GIT dc87c3985e9b442c60994308a96f887579addc39 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git commit Author: Linus Torvalds Date: Mon Apr 30 17:43:48 2007 -0700 libata: honour host controllers that want just one host The Marvell IDE interface on my machine would hit a BUG_ON() in lib/iomem.c because it was calling ata_pci_init_one() specifying just a single port on the host, but that would actually end up trying to initialize two ports, the second one with bogus information. This fixes "ata_pci_init_one()" so that it actually passes down the n_ports variable that it got from the low-level driver to the host allocation routine ("ata_host_alloc_pinfo()"), which results in the ATA layer actually having the correct port number information. And in order to make it all work, I also needed to fix a few places that had incorrectly hard-coded the fact that a host always had exactly two ports (both ata_pci_init_bmdma() and ata_request_legacy_irqs() would just always iterate over both ports). Acked-by: Jeff Garzik Signed-off-by: Linus Torvalds commit 14e38ac823b7b25e3f4e563c182f93fde78167d6 Author: David Rientjes Date: Mon Apr 30 15:09:56 2007 -0700 pm: include EIO from errno-base.h For backwards compatibility, call_platform_enable_wakeup() can return 0 instead of -EIO since we aren't guaranteed to have errno defined. Cc: David Brownell Signed-off-by: David Rientjes Cc: "Randy.Dunlap" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit 11443ec7d9286dd25663516436a14edfb5f43857 Author: Jeremy Fitzhardinge Date: Mon Apr 30 15:09:56 2007 -0700 Add kvasprintf() Add a kvasprintf() function to complement kasprintf(). No in-tree users yet, but I have some coming up. [akpm@linux-foundation.org: EXPORT it] Signed-off-by: Jeremy Fitzhardinge Cc: Andrew Morton Cc: Keir Fraser Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit 9684e51cd157607f0727c1550e7df6e31de40808 Author: Johannes Berg Date: Mon Apr 30 15:09:55 2007 -0700 power management: force pm_ops.valid callback to be assigned This patch changes the docs and behaviour from "all states valid" to "no states valid" if no .valid callback is assigned. Users of pm_ops that only need mem sleep can assign pm_valid_only_mem without any overhead, others will require more elaborate callbacks. Now that all users of pm_ops have a .valid callback this is a safe thing to do and prevents things from getting messy again as they were before. Signed-off-by: Johannes Berg Acked-by: Pavel Machek Looks-okay-to: Rafael J. Wysocki Cc: Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit e8c9c502690efd24b7055bf608e7a3c34216848b Author: Johannes Berg Date: Mon Apr 30 15:09:54 2007 -0700 power management: implement pm_ops.valid for everybody Almost all users of pm_ops only support mem sleep, don't check in .valid and don't reject any others in .prepare so users can be confused if they check /sys/power/state, especially when new states are added (these would then result in s-t-r although they're supposed to be something different). This patch implements a generic pm_valid_only_mem function that is then exported for users and puts it to use in almost all existing pm_ops. Signed-off-by: Johannes Berg Cc: David Brownell Acked-by: Pavel Machek Cc: linux-pm@lists.linux-foundation.org Cc: Len Brown Acked-by: Russell King Cc: Greg KH Cc: "Rafael J. Wysocki" Cc: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit 11d77d0c01b80e44c7aceb21928508dafce774f9 Author: Johannes Berg Date: Mon Apr 30 15:09:53 2007 -0700 power management: remove firmware disk mode This patch removes the firmware disk suspend mode which is the wrong approach, it is supposed to be used for implementing firmware-based disk suspend but cannot actually be used for that. Signed-off-by: Johannes Berg Acked-by: Pavel Machek Cc: Cc: David Brownell Cc: Len Brown Acked-by: Russell King Cc: Greg KH Cc: "Rafael J. Wysocki" Cc: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit fe0c935a6cbf25d72a27c7a345df8a2151de0b74 Author: Johannes Berg Date: Mon Apr 30 15:09:51 2007 -0700 rework pm_ops pm_disk_mode, kill misuse This patch series cleans up some misconceptions about pm_ops. Some users of the pm_ops structure attempt to use it to stop the user from entering suspend to disk, this, however, is not possible since the user can always use "shutdown" in /sys/power/disk and then the pm_ops are never invoked. Also, platforms that don't support suspend to disk simply should not allow configuring SOFTWARE_SUSPEND (read the help text on it, it only selects suspend to disk and nothing else, all the other stuff depends on PM). The pm_ops structure is actually intended to provide a way to enter platform-defined sleep states (currently supported states are "standby" and "mem" (suspend to ram)) and additionally (if SOFTWARE_SUSPEND is configured) allows a platform to support a platform specific way to enter low-power mode once everything has been saved to disk. This is currently only used by ACPI (S4). This patch: The pm_ops.pm_disk_mode is used in totally bogus ways since nobody really seems to understand what it actually does. This patch clarifies the pm_disk_mode description. It also removes all the arm and sh users that think they can veto suspend to disk via pm_ops; not so since the user can always do echo shutdown > /sys/power/disk, they need to find a better way involving Kconfig or such. ACPI is the only user left with a non-zero pm_disk_mode. The patch also sets the default mode to shutdown again, but when a new pm_ops is registered its pm_disk_mode is selected as default, that way the default stays for ACPI where it is apparently required. Signed-off-by: Johannes Berg Cc: David Brownell Acked-by: Pavel Machek Cc: Cc: Len Brown Acked-by: Russell King Cc: Greg KH Cc: "Rafael J. Wysocki" Acked-by: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit 1173a729fc3ce2fa0d698bd39be8ff7bf6c70bf1 Author: Jeff Mahoney Date: Mon Apr 30 15:09:50 2007 -0700 reiserfs: suppress lockdep warning We're getting lockdep warnings due to a post-2.6.21-rc7 bugfix. The xattr_sem can never be taken in the manner described. Internal inodes are protected by I_PRIVATE. Add the appropriate annotation. Cc: Cc: "Antonino A. Daplas" Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit 42e380832a6911c8a3173ee0172fbc0e4864d80b Author: Robert Peterson Date: Mon Apr 30 15:09:48 2007 -0700 Extend print_symbol capability Today's print_symbol function dumps a kernel symbol with printk. This patch extends the functionality of kallsyms.c so that the symbol lookup function may be used without the printk. This is useful for modules that want to dump symbols elsewhere, for example, to debugfs. I intend to use the new function call in the GFS2 file system (which will be a separate patch). [akpm@linux-foundation.org: build fix] [clameter@sgi.com: sprint_symbol should return length of string like sprintf] Signed-off-by: Robert Peterson Cc: Rusty Russell Cc: Roman Zippel Cc: "Randy.Dunlap" Cc: Sam Ravnborg Acked-by: Paulo Marques Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit de34ed91c4ffa4727964a832c46e624dd1495cf5 Author: David S. Miller Date: Mon Apr 30 14:51:58 2007 -0700 [UDP]: Do not allow specific bind when wildcard bind exists. When allocating local ports, do not allow a bind to a port with a specific local address when a bind to that port with a wildcard local address already exists. Noticed by Linus. Signed-off-by: David S. Miller commit b7b5f487ab39bc10ed0694af35651a03d9cb97ff Author: David S. Miller Date: Mon Apr 30 13:35:29 2007 -0700 [IPV4] UDP: Fix endianness bugs in hashing changes. I accidently applied an earlier version of Eric Dumazet's patch, from March 21st. His version from March 30th didn't have these bugs, so this just interdiffs to the correct patch. Signed-off-by: David S. Miller commit 80787ebc2bbd8e675d8b9ff8cfa40f15134feebe Author: Mitsuru Chinen Date: Mon Apr 30 00:48:20 2007 -0700 [IPV4] SNMP: Support OutMcastPkts and OutBcastPkts A transmitted IP multicast datagram should be counted as OutMcastPkts. By the same token, a transmitted IP broadcast datagram should be counted as OutBcastPkts. Signed-off-by: Mitsuru Chinen Signed-off-by: David S. Miller commit 5506b54b36f067b9776935085c9f8e607b026b23 Author: Mitsuru Chinen Date: Mon Apr 30 00:48:10 2007 -0700 [IPV4] SNMP: Support InMcastPkts and InBcastPkts A received IP multicast datagram should be counted as InMcastPkts. By the same token, a received IP broadcast datagram should be counted as InBcastPkts. Signed-off-by: Mitsuru Chinen Signed-off-by: David S. Miller commit 704aed53b4e43bebfbd425cf95b66794a9cfa2c2 Author: Mitsuru Chinen Date: Mon Apr 30 00:46:30 2007 -0700 [IPV4] SNMP: Support InTruncatedPkts An IP datagram which is being discarded because the datagram frame didn't carry enough data should be counted as InTruncatedPkts. Signed-off-by: Mitsuru Chinen Signed-off-by: David S. Miller commit e91a47ebb130b90790c7a8c625ade4dcea246842 Author: Mitsuru Chinen Date: Mon Apr 30 00:45:49 2007 -0700 [IPV4] SNMP: Support InNoRoutes An IP datagram which is being discarded because of no routes in the forwarding path should be counted as InNoRoutes. Signed-off-by: Mitsuru Chinen Signed-off-by: David S. Miller commit 71ff6c0a857d11e70aec0c8f1e0d4ae9a45dd468 Author: Mitsuru Chinen Date: Mon Apr 30 00:45:02 2007 -0700 [SNMP]: Add definitions for {In,Out}BcastPkts The updated IP-MIB RFC (RFC4293) specifys new objects, InBcastPkts and OutBcastPkts. This adds definitions for them. Signed-off-by: Mitsuru Chinen Signed-off-by: David S. Miller commit d551e4541dd60ae53459f77a971f2d6043431f5f Author: Ilpo Järvinen Date: Mon Apr 30 00:42:20 2007 -0700 [TCP] FRTO: RFC4138 allows Nagle override when new data must be sent This is a corner case where less than MSS sized new data thingie is awaiting in the send queue. For F-RTO to work correctly, a new data segment must be sent at certain point or F-RTO cannot be used at all. RFC4138 allows overriding of Nagle at that point. Implementation uses frto_counter states 2 and 3 to distinguish when Nagle override is needed. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 575ee7140dabe9b9c4f66f4f867039b97e548867 Author: Ilpo Järvinen Date: Mon Apr 30 00:39:55 2007 -0700 [TCP] FRTO: Delay skb available check until it's mandatory No new data is needed until the first ACK comes, so no need to check for application limitedness until then. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 157bfc25020f7eb731f94140e099307ade47299e Author: Masahide NAKAMURA Date: Mon Apr 30 00:33:35 2007 -0700 [XFRM]: Restrict upper layer information by bundle. On MIPv6 usage, XFRM sub policy is enabled. When main (IPsec) and sub (MIPv6) policy selectors have the same address set but different upper layer information (i.e. protocol number and its ports or type/code), multiple bundle should be created. However, currently we have issue to use the same bundle created for the first time with all flows covered by the case. It is useful for the bundle to have the upper layer information to be restructured correctly if it does not match with the flow. 1. Bundle was created by two policies Selector from another policy is added to xfrm_dst. If the flow does not match the selector, it goes to slow path to restructure new bundle by single policy. 2. Bundle was created by one policy Flow cache is added to xfrm_dst as originated one. If the flow does not match the cache, it goes to slow path to try searching another policy. Signed-off-by: Masahide NAKAMURA Signed-off-by: David S. Miller commit 34588b4c046c34773e5a1a962da7b78b05c4d1bd Author: Ilpo Järvinen Date: Mon Apr 30 00:57:33 2007 -0700 [TCP]: Catch skb with S+L bugs earlier SACKED_ACKED and LOST are mutually exclusive with SACK, thus having their sum larger than packets_out is bug with SACK. Eventually these bugs trigger traps in the tcp_clean_rtx_queue with SACK but it's much more informative to do this here. Non-SACK TCP, however, could get more than packets_out duplicate ACKs which each increment sacked_out, so it makes sense to do this kind of limitting for non-SACK TCP but not for SACK enabled one. Perhaps the author had the opposite in mind but did the logic accidently wrong way around? Anyway, the sacked_out incrementer code for non-SACK already deals this issue before calling sync_left_out so this trapping can be done unconditionally. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 6aaf47fa48d3c44280810b1b470261d340e4ed87 Author: Eric Dumazet Date: Mon Apr 30 00:26:00 2007 -0700 [PATCH] INET : IPV4 UDP lookups converted to a 2 pass algo Some people want to have many UDP sockets, binded to a single port but many different addresses. We currently hash all those sockets into a single chain. Processing of incoming packets is very expensive, because the whole chain must be examined to find the best match. I chose in this patch to hash UDP sockets with a hash function that take into account both their port number and address : This has a drawback because we need two lookups : one with a given address, one with a wildcard (null) address. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit 65def812ab25d7565756e5748d91e22e302197ee Author: James Chapman Date: Mon Apr 30 00:21:02 2007 -0700 [L2TP]: Add the ability to autoload a pppox protocol module. This patch allows a name "pppox-proto-nnn" to be used in modprobe.conf to autoload a PPPoX protocol nnn. Signed-off-by: James Chapman Signed-off-by: David S. Miller commit 2a12dcd71a5e0667b33f7b47bcac95c71d551840 Author: Jens Axboe Date: Thu Apr 26 14:41:53 2007 +0200 [PATCH] elevator: elv_list_lock does not need irq disabling It's never grabbed from irq context, so just make it plain spin_lock(). Signed-off-by: Jens Axboe commit 5972511b77809cb7c9ccdb79b825c54921c5c546 Author: Jens Axboe Date: Mon Apr 2 10:06:42 2007 +0200 [BLOCK] Don't pin lots of memory in mempools Currently we scale the mempool sizes depending on memory installed in the machine, except for the bio pool itself which sits at a fixed 256 entry pre-allocation. There's really no point in "optimizing" this OOM path, we just need enough preallocated to make progress. A single unit is enough, lets scale it down to 2 just to be on the safe side. This patch saves ~150kb of pinned kernel memory on a 32-bit box. Signed-off-by: Jens Axboe commit 46f8914e53c28d0716c586e08a7c819d8ebb9d54 Author: James Chapman Date: Mon Apr 30 00:07:31 2007 -0700 [SKB]: Introduce skb_queue_walk_safe() This patch provides a method for walking skb lists while inserting or removing skbs from the list. Signed-off-by: James Chapman Signed-off-by: David S. Miller commit 597bc485d6906359ad667fc8ead5e5f0ede03a0a Author: Jens Axboe Date: Tue Apr 24 21:23:53 2007 +0200 cfq-iosched: speedup cic rb lookup We often lookup the same queue many times in succession, so cache the last looked up queue to avoid browsing the rbtree. Signed-off-by: Jens Axboe commit 4e521c27eee33cebd618c26649e2c93803004647 Author: Jens Axboe Date: Tue Apr 24 21:17:33 2007 +0200 ll_rw_blk: add io_context private pointer To be used by as/cfq as they see fit. Signed-off-by: Jens Axboe commit 91fac317a34859986a2359a5a5c0e37dc17a9c3d Author: Vasily Tarasov Date: Wed Apr 25 12:29:51 2007 +0200 cfq-iosched: get rid of cfqq hash cfq hash is no more necessary. We always can get cfqq from io context. cfq_get_io_context_noalloc() function is introduced, because we don't want to allocate cic on merging and checking may_queue. In order to identify sync queue we've used hash key = CFQ_KEY_ASYNC. Since hash is eliminated we need to use other criterion: sync flag for queue is added. In all places where we dig in rb_tree we're in current context, so no additional locking is required. Advantages of this patch: no additional memory for hash, no seeking in hash, code is cleaner. But it is necessary now to seek cic in per-ioc rbtree, but it is faster: - most processes work only with few devices - most systems have only few block devices - it is a rb-tree Signed-off-by: Vasily Tarasov Changes by me: - Merge into CFQ devel branch - Get rid of cfq_get_io_context_noalloc() - Fix various bugs with dereferencing cic->cfqq[] with offset other than 0 or 1. - Fix bug in cfqq setup, is_sync condition was reversed. - Fix bug where only bio_sync() is used, we need to check for a READ too Signed-off-by: Jens Axboe commit cc19747977824ece6aa1c56a29e974fef5ec2b32 Author: Jens Axboe Date: Fri Apr 20 20:45:39 2007 +0200 cfq-iosched: tighten queue request overlap condition For tagged devices, allow overlap of requests if the idle window isn't enabled on the current active queue. Signed-off-by: Jens Axboe commit 3ed9a2965c47636bc0ebafab31a39f1c105492ca Author: Jens Axboe Date: Mon Apr 23 08:33:33 2007 +0200 cfq-iosched: improve sync vs async workloads Signed-off-by: Jens Axboe commit 1be92f2fc7b563db3a8909d2d1c6a6520aeca323 Author: Jens Axboe Date: Thu Apr 19 14:32:26 2007 +0200 cfq-iosched: never allow an async queue idling We don't enable it by default, don't let it get enabled during runtime. Signed-off-by: Jens Axboe commit 20e493a8d03b3b2f51b619a453f7bbbebedd6bda Author: Jens Axboe Date: Mon Apr 23 08:26:36 2007 +0200 cfq-iosched: get rid of ->dispatch_slice We can track it fairly accurately locally, let the slice handling take care of the rest. Signed-off-by: Jens Axboe commit 6084cdda0ea4561feb68e00a8c50068bba98006d Author: Jens Axboe Date: Mon Apr 23 08:25:00 2007 +0200 cfq-iosched: don't pass unused preemption variable around We don't use it anymore in the slice expiry handling. Signed-off-by: Jens Axboe commit edd75ffd92a5b7f6244431e8ff6c32b846f9ba86 Author: Jens Axboe Date: Thu Apr 19 12:03:34 2007 +0200 cfq-iosched: get rid of ->cur_rr and ->cfq_list It's only used for preemption now that the IDLE and RT queues also use the rbtree. If we pass an 'add_front' variable to cfq_service_tree_add(), we can set ->rb_key to 0 to force insertion at the front of the tree. Signed-off-by: Jens Axboe commit 67e6b49e39e9b9bf5ce1351ef21dad391856183f Author: Jens Axboe Date: Fri Apr 20 14:18:00 2007 +0200 cfq-iosched: slice offset should take ioprio into account Use the max_slice-cur_slice as the multipler for the insertion offset. Signed-off-by: Jens Axboe commit 498d3aa2b4f791059acd8c942ee8fa15c2ce36c2 Author: Jens Axboe Date: Thu Apr 26 12:54:48 2007 +0200 [PATCH] cfq-iosched: style cleanups and comments Signed-off-by: Jens Axboe commit 67060e37994444ee9c0bd2413c8baa6cc58e7adb Author: Jens Axboe Date: Wed Apr 18 20:13:32 2007 +0200 cfq-iosched: sort IDLE queues into the rbtree Same treatment as the RT conversion, just put the sorted idle branch at the end of the tree. Signed-off-by: Jens Axboe commit 0c534e0a463e2eeafc97ba25ab23c14f3cdf2bdb Author: Jens Axboe Date: Wed Apr 18 20:01:57 2007 +0200 cfq-iosched: sort RT queues into the rbtree Currently CFQ does a linked insert into the current list for RT queues. We can just factor the class into the rb insertion, and then we don't have to treat RT queues in a special way. It's faster, too. Signed-off-by: Jens Axboe commit cc09e2990fdd96d25fdbb9db6bc9b4c82d9e4a3c Author: Jens Axboe Date: Thu Apr 26 12:53:50 2007 +0200 [PATCH] cfq-iosched: speed up rbtree handling For cases where the rbtree is mainly used for sorting and min retrieval, a nice speedup of the rbtree code is to maintain a cache of the leftmost node in the tree. Also spotted in the CFS CPU scheduler code. Improved by Alan D. Brunelle by updating the leftmost hint in cfq_rb_first() if it isn't set, instead of only updating it on insert. Signed-off-by: Jens Axboe commit d9e7620e60bc6648c3dcabbc8d1a320b69c846f9 Author: Jens Axboe Date: Fri Apr 20 14:27:50 2007 +0200 cfq-iosched: rework the whole round-robin list concept Drawing on some inspiration from the CFS CPU scheduler design, overhaul the pending cfq_queue concept list management. Currently CFQ uses a doubly linked list per priority level for sorting and service uses. Kill those lists and maintain an rbtree of cfq_queue's, sorted by when to service them. This unfortunately means that the ionice levels aren't as strong anymore, will work on improving those later. We only scale the slice time now, not the number of times we service. This means that latency is better (for all priority levels), but that the distinction between the highest and lower levels aren't as big. The diffstat speaks for itself. cfq-iosched.c | 363 +++++++++++++++++--------------------------------- 1 file changed, 125 insertions(+), 238 deletions(-) Signed-off-by: Jens Axboe commit 1afba0451c83cbff622a08f2d86fbb2e680dfd5f Author: Jens Axboe Date: Tue Apr 17 12:47:55 2007 +0200 cfq-iosched: minor updates - Move the queue_new flag clear to when the queue is selected - Only select the non-first queue in cfq_get_best_queue(), if there's a substantial difference between the best and first. - Get rid of ->busy_rr - Only select a close cooperator, if the current queue is known to take a while to "think". Signed-off-by: Jens Axboe commit 6d048f5310aa2dda2b5acd947eab3598c25e269f Author: Jens Axboe Date: Wed Apr 25 12:44:27 2007 +0200 cfq-iosched: development update - Implement logic for detecting cooperating processes, so we choose the best available queue whenever possible. - Improve residual slice time accounting. - Remove dead code: we no longer see async requests coming in on sync queues. That part was removed a long time ago. That means that we can also remove the difference between cfq_cfqq_sync() and cfq_cfqq_class_sync(), they are now indentical. And we can kill the on_dispatch array, just make it a counter. - Allow a process to go into the current list, if it hasn't been serviced in this scheduler tick yet. Possible future improvements including caching the cfqq lookup in cfq_close_cooperator(), so we don't have to look it up twice. cfq_get_best_queue() should just use that last decision instead of doing it again. Signed-off-by: Jens Axboe commit 1e3335de05da3dfbe48b8caa03db1834a2133256 Author: Jens Axboe Date: Wed Feb 14 19:59:49 2007 +0100 cfq-iosched: improve preemption for cooperating tasks When testing the syslet async io approach, I discovered that CFQ sometimes didn't perform as well as expected. cfq_should_preempt() needs to better check for cooperating tasks, so fix that by allowing preemption of an equal priority queue if the recently queued request is as good a candidate for IO as the one we are currently waiting for. Signed-off-by: Jens Axboe commit f900e9777fc9b65140cb9570438597bc8fae56ab Author: Paul Mackerras Date: Mon Apr 30 13:03:39 2007 +1000 [POWERPC] Remove dev_dbg redefinition in drivers/ps3/vuart.c Commit 404d5b185b4eb56d6fa2f7bd27833f8df1c38ce4 changed the definition of dev_dbg in the !DEBUG case from being a #define to being a static inline. There was code in drivers/ps3/vuart.c to do exactly that, which fails to compile now. This fixes it by removing the redefinition, as the redefinition is now superfluous. Signed-off-by: Paul Mackerras commit 34f6d749c0a328817d5e36274e53121c1db734dc Author: Dave Jiang Date: Sat Apr 14 08:25:24 2007 +1000 [POWERPC] remove kernel module option for booke wdt Remove option of making booke_wdt into a kernel module. This watchdog cannot be disabled. No point being a kernel module. Signed-off-by: Dave Jiang Signed-off-by: Paul Mackerras commit 6ec367091a418dd6119e381758940a38b180089c Author: John Rigby Date: Sat Apr 7 08:57:37 2007 +1000 [POWERPC] Avoid putting cpu node twice Call of_find_node_by_type with NULL instead of np so the cpu node does not get put twice. This was causing kref_put warnings. Signed-off-by: John Rigby Acked-by: Sylvain Munaut Signed-off-by: Paul Mackerras commit 057b184a007376562e905aa39f1ba352fb8d78b1 Author: Thomas Gleixner Date: Sun Apr 29 16:10:39 2007 +0000 [POWERPC] Spinlock initializer cleanup Use DEFINE_SPINLOCK instead of initializing spinlocks to SPIN_LOCK_UNLOCKED, since DEFINE_SPINLOCK is better for lockdep. Signed-off-by: Thomas Gleixner Signed-off-by: Paul Mackerras commit f139efedb8d34904cf8ea30b174c3ee57204d114 Author: Andrew Morton Date: Thu Apr 26 00:07:05 2007 -0700 [POWERPC] ppc4xx_sgdma needs dma-mapping.h For dma_alloc_*() Cc: Mathieu Desnoyers Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras commit 738925b6855f989d3f90a3330eeff34010309be3 Author: Srinivasa Ds Date: Thu Apr 26 00:07:04 2007 -0700 [POWERPC] arch/powerpc/sysdev/timer.c build fix arch/powerpc/sysdev/timer.c:51: error: variable `timer_sysclass' has initializer but incomplete type arch/powerpc/sysdev/timer.c:52: error: unknown field `resume' specified in initializer Signed-off-by: Srinivasa Ds Acked-by: Johannes Berg Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras commit 12d371a69e6df96cd949af6bcb569e978e8f9d41 Author: Stephen Rothwell Date: Sun Apr 29 16:29:08 2007 +1000 [POWERPC] get_property cleanups Just another pass through arch/powerpc for old usages. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 8d1cea6e1439a113b1f17eab9b4e2f0cfc24bbb0 Author: Adrian Bunk Date: Sun Apr 29 05:19:56 2007 +1000 [POWERPC] Remove the unused HTDMSOUND driver Recently, someone fixed a syntax error in the HTDMSOUND driver introduced 4 years ago. Unfortunately not by trying to compile this driver for his hardware but by code inspection - which seems to be a strong indication that there are no users left for this OSS sound driver. This patch therefore removes it. Signed-off-by: Adrian Bunk Acked-by: Dan Malek Acked-by: Marcelo Tosatti Signed-off-by: Paul Mackerras commit 4bd4aa1967628fa85a40936410acab6b8bbae6f5 Author: Olof Johansson Date: Sat Apr 28 12:49:03 2007 +1000 [POWERPC] cell: cbe_cpufreq cleanup and crash fix cbe_cpufreq cleanups: * comment format * whitespace * don't init on non-cell platforms Signed-off-by: Olof Johansson Acked-by: Christian Krafft Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit d169d140944a67edd6f88f78b65b3117059f4380 Author: Johannes Berg Date: Sat Apr 28 08:00:03 2007 +1000 [POWERPC] Declare enable_kernel_spe in a header This patch puts enable_kernel_spe into along with enable_kernel_altivec etc. Signed-off-by: Johannes Berg Signed-off-by: Paul Mackerras commit 8895ea483e144f8acca16adfff7c60a993e77b7d Author: Mark A. Greer Date: Sat Apr 28 06:48:24 2007 +1000 [POWERPC] Add dt_xlate_addr() to bootwrapper dt_xlate_reg() looks up the 'reg' property in the specified node to get the address and size to translate. Add dt_xlate_addr() which is passed in the address and size to translate. Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras commit d818d7ec8bb06c1bc10270962e28fb0cbd93b64a Author: Scott Wood Date: Sat Apr 28 06:32:15 2007 +1000 [POWERPC] bootwrapper: CONFIG_ -> CONFIG_DEVICE_TREE A usage of CONFIG_DEVICE_TREE got accidentally truncated; this fix allows out-of-tree dts files to work. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit 9be4dcb606e647854760fafd0bb1cb3e1a804d16 Author: Grant Likely Date: Sat Apr 28 05:50:05 2007 +1000 [POWERPC] Don't define a custom bd_t for Xilixn Virtex based boards. Why create a platform specific board_info structure that is hacked together, ugly, and dangerous, when we've got a perfectly fine common board_info structure that is hacked-together, ugly and dangerous. Signed-off-by: Grant Likely Signed-off-by: Paul Mackerras commit 30fea61fd01955cc35c6a63260b374d7a6d12c8b Author: Grant Likely Date: Sat Apr 28 05:50:04 2007 +1000 [POWERPC] Add sane defaults for Xilinx EDK generated xparameters files Signed-off-by: Grant Likely Signed-off-by: Paul Mackerras commit 8b01653ab01eebc781c5a375ff336d0837f30c4e Author: Grant Likely Date: Sat Apr 28 05:50:03 2007 +1000 [POWERPC] Add uartlite boot console driver for the zImage wrapper Signed-off-by: Grant Likely Acked-by: Peter Korsgaard Signed-off-by: Paul Mackerras commit 8c38fc2b7429b26105fe244890c8d5ab3043f099 Author: Grant Likely Date: Sat Apr 28 05:50:02 2007 +1000 [POWERPC] Stop using ppc_sys for Xilinx Virtex boards The arch/ppc/syslib/ppc_sys.c infrastructure does not work well for the virtex ports. Move the ml300 and ml403 board ports over to use the new virtex_devices infrastructure. Signed-off-by: Grant Likely Acked-by: Peter Korsgaard Signed-off-by: Paul Mackerras commit d26cd57071b4358bcf3a9140a90b38514c7f345e Author: Grant Likely Date: Sat Apr 28 05:50:01 2007 +1000 [POWERPC] New registration for common Xilinx Virtex ppc405 platform devices Currently virtex support in mainline make use of the infrastructure in arch/ppc/syslib/ppc_sys.c for registering common devices on virtex ppc405 platforms. The ppc_sys.c code is not well suited to the dynamic nature of FPGA designs and makes adding new board ports more complex. This patch adds a new listing of common devices which does not depend on the ppc_sys.c infrastructure. Signed-off-by: Grant Likely Signed-off-by: Paul Mackerras commit 5ff084f21da25ffcc5e0cb0293a0ea588cb46272 Author: Grant Likely Date: Sat Apr 28 05:50:00 2007 +1000 [POWERPC] Merge common virtex header files The header files for the ml403 and ml300 are virtually identical, merge them into a single file. Signed-off-by: Grant Likely Signed-off-by: Paul Mackerras commit 2b10caf380b0dbe3923b0ceefed17e3eaa7f2d4b Author: Grant Likely Date: Sat Apr 28 05:49:59 2007 +1000 [POWERPC] Rework Kconfig dependancies for Xilinx Virtex ppc405 platform Reverse dependency order for Xilinx Virtex parts. For these parts, It makes more sense for boards/chips to specify which features they provide instead of the features listing the parts they are implemented in. I think it also makes adding new board ports simpler. Signed-off-by: Grant Likely Acked-by: Peter Korsgaard Signed-off-by: Paul Mackerras commit c146c958dc953da172231577d7ee81cf06d7eeb4 Author: Olof Johansson Date: Fri Apr 27 15:52:43 2007 +1000 [POWERPC] Clean up cpufreq Kconfig dependencies Shuffle Kconfig order, making the platform drivers menu depend on the global option instead of each driver being dependent on it. Also fix dependency of PPC_PMAC on the G5 one. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit e089ad46dbede9eed650f12d039d1addc05adf43 Author: Paul Mackerras Date: Mon Apr 30 10:24:24 2007 +1000 Revert "[POWERPC] Autodetect serial console on efika" This reverts commit 9414715a7bbb45450015e9bc2676d85d919d08d4, at Olaf Hering's request: > Paul, please discard this patch. The optional graphics card may have > also device_type 'serial' if it is in VGA mode. > I will send an updated patch later. commit bcfd09ee48f77a4fe903dbc3757e7af931998ce1 Author: Stefan Richter Date: Sat Apr 21 21:02:52 2007 +0200 ieee1394: remove garbage from Kconfig Signed-off-by: Stefan Richter commit 3f94aa4d69bb9837857bac2755090a3cd28bfdc1 Author: Stefan Richter Date: Sat Apr 21 20:54:37 2007 +0200 ieee1394: more help in Kconfig - s/Device Drivers/Controllers/ - clarify who needs pcilynx - don't recommend Y for raw1394; M is typically used Signed-off-by: Stefan Richter commit 749cf76620a8f0d1ab4ff83c8e8f18028045a094 Author: Simon Arlott Date: Tue Apr 24 23:44:57 2007 +0100 ieee1394: ohci1394: Fix mistake in printk message. Fix the "attempting to setting" message in ohci1394. Signed-off-by: Simon Arlott Signed-off-by: Andrew Morton Signed-off-by: Stefan Richter commit 2ab77524693ab855fc756faff0d1d26cb11a89e8 Author: Bernhard Kauer Date: Fri Apr 20 13:59:54 2007 +0200 ieee1394: ohci1394: remove unnecessary rcvPhyPkt bit flipping in LinkControl register Remove the unneeded code that clears, sets and again clears the rcvPhyPkt bit in the ohci1394 LinkControl register in ohci_initialize(). Signed-off-by: Bernhard Kauer Signed-off-by: Stefan Richter commit c13596b0e5db1135ca22d068eca16ffe1e0ea912 Author: Stefan Richter Date: Thu Apr 12 22:21:55 2007 +0200 ieee1394: ohci1394: fix cosmetic problem in error logging If posted write failed, an "Unhandled interrupt(s) 0x00000100" message was logged by mistake. Signed-off-by: Stefan Richter commit 21b2c5647b057624628888857f0e2246538a80b1 Author: Stefan Richter Date: Mon Apr 23 21:28:47 2007 +0200 ieee1394: eth1394: send async streams at S100 on 1394b buses eth1394 did not work on buses consisting of S100B...S400B hardware because it attempted to send GASP packets at S800. Signed-off-by: Stefan Richter commit 809e905ce73eaa13972c2617959f8ec16e7d0d6f Author: Akinobu Mita Date: Sat Apr 21 18:36:26 2007 +0900 ieee1394: eth1394: fix error path in module_init This patch fixes some error handlings in eth1394: - check return value of kmem_cache_create() - cleanup resources if hpsb_register_protocol() fails Signed-off-by: Akinobu Mita Signed-off-by: Stefan Richter (whitespace) commit fdc0092bfd68cedfb9929256957f64c2c2760b5c Author: Stefan Richter Date: Mon Apr 2 02:24:27 2007 +0200 ieee1394: eth1394: correct return codes in hard_start_xmit This patch actually doesn't change anything because there was always 0 == NETDEV_TX_OK returned before. TODO: Return NETDEV_TX_BUSY in error case and test in different error conditions. Signed-off-by: Stefan Richter commit 53f374e76c2b37835966382b27efb6bb3715f9d8 Author: Stefan Richter Date: Mon Apr 2 02:23:19 2007 +0200 ieee1394: eth1394: hard_start_xmit is called in atomic context Signed-off-by: Stefan Richter commit 099398719bb53119734354bc079840bebf1c7386 Author: Stefan Richter Date: Mon Apr 2 02:22:21 2007 +0200 ieee1394: eth1394: some conditions are unlikely Signed-off-by: Stefan Richter commit 2e2173df68f419aa41558e1fa90d7263b2d0211f Author: Stefan Richter Date: Mon Apr 2 02:21:46 2007 +0200 ieee1394: eth1394: clean up fragment_overlap offset > fi->offset + fi->len - 1 == !(offset < fi->offset + fi->len) offset + len - 1 < fi->offset == !(offset + len > fi->offset) !(A || B) == (!A && !B) Signed-off-by: Stefan Richter commit 01590d20b42400be46cf4e565b39764e38ca87fe Author: Stefan Richter Date: Mon Apr 2 02:20:37 2007 +0200 ieee1394: eth1394: don't use alloc_etherdev Signed-off-by: Stefan Richter commit 8a62bf7978eaa428e400677d5e5f5441262f79b1 Author: Stefan Richter Date: Mon Apr 2 02:19:48 2007 +0200 ieee1394: eth1394: omit useless set_mac_address callback We can't reconfigure the MAC address, hence we don't need the callback. Signed-off-by: Stefan Richter commit 599bba9647f7813c09bf921c72351609430c8a33 Author: Stefan Richter Date: Mon Apr 2 02:19:02 2007 +0200 ieee1394: eth1394: CONFIG_INET is always defined because CONFIG_IEEE1394_ETH1394 depends on it. Signed-off-by: Stefan Richter commit 17bab407d54ba1320d71a45641ecffc33bd331c1 Author: Stefan Richter Date: Tue Apr 3 23:55:40 2007 +0200 ieee1394: eth1394: allow MTU bigger than 1500 RFC 2734 says: "IP-capable nodes may operate with an MTU size larger than the default [1500 octets], but the means by which a larger MTU is configured are beyond the scope of this document." Allow users to set an MTU bigger than 1500. Signed-off-by: Stefan Richter commit f982e5ffcfa9d0a2480d0b8261bd11521f3a1994 Author: Adrian Bunk Date: Fri Apr 27 01:47:32 2007 +0200 ieee1394: unexport highlevel_host_reset highlevel_host_reset no longer has any modular users. Signed-off-by: Adrian Bunk Signed-off-by: Stefan Richter commit 246a5fdade88cbeba09d07c69f67444a24a57d79 Author: Stefan Richter Date: Mon Apr 2 02:16:40 2007 +0200 ieee1394: eth1394: contain host reset Call only eth1394's own host reset handler from .tx_timeout, not the reset hooks of all other IEEE 1394 drivers. A minor drawback of this patch is that ether1394_host_reset by timeout is not serialized against ether1394_host_reset by bus reset. Signed-off-by: Stefan Richter commit 5009d269610b4c89761dcae296d9717f2f48234b Author: Stefan Richter Date: Mon Apr 2 02:15:53 2007 +0200 ieee1394: eth1394: shorter error messages Signed-off-by: Stefan Richter commit 027611b84260cf3adf14e30d2480007795829e6e Author: Stefan Richter Date: Mon Apr 2 02:15:21 2007 +0200 ieee1394: eth1394: correct a memset argument The old argument calculated the correct value in a wrong way. Signed-off-by: Stefan Richter commit d06c1ddad9055eef55456abbae795279d6b1bbcf Author: Stefan Richter Date: Mon Apr 2 02:14:45 2007 +0200 ieee1394: eth1394: refactor .probe and .update Move common code into an extra function. This implicitly adds a missing node_info->fifo = CSR1212_INVALID_ADDR_SPACE; to .update. Signed-off-by: Stefan Richter commit 5e7abccd38f7f2ce838eb49a657eea70b22f0803 Author: Stefan Richter Date: Mon Apr 2 02:13:51 2007 +0200 ieee1394: eth1394: .probe and .update may sleep Signed-off-by: Stefan Richter commit efbeccf174bac803421a5f35076a17af47c9ce00 Author: Stefan Richter Date: Mon Apr 2 02:12:32 2007 +0200 ieee1394: eth1394: coding style Adjust white space and line wraps. Remove unnecessary parentheses and braces, unused macros, and some of the more redundant comments. Signed-off-by: Stefan Richter commit 09d7a96f5ad1019386594e2795c1f0229dd43305 Author: Jean Delvare Date: Sun Apr 1 10:06:33 2007 +0200 ieee1394: eth1394: Move common recv_init code to helper function There is some common code between ether1394_open and ether1394_add_host which can be moved to a separate helper function for a slightly smaller eth1394 driver (-160 bytes on i386.) Signed-off-by: Jean Delvare Signed-off-by: Stefan Richter commit 70093cfde8af52b0b9030d90f9004cbde38f2ff8 Author: Stefan Richter Date: Tue Mar 27 01:36:50 2007 +0200 ieee1394: eth1394: don't autoload by hotplug when ohci1394 starts Until now, ieee1394 put an IP-over-1394 capability entry into each new host's config ROM. As soon as the controller was initialized --- i.e. right after modprobe ohci1394 --- this entry triggered a hotplug event which typically caused auto-loading of eth1394. This irritated or annoyed many users and distributors. Of course they could blacklist eth1394, but then ieee1394 wrongly advertized IP-over- 1394 capability to the FireWire bus. Therefore - remove the offending kernel config option IEEE1394_CONFIG_ROM_IP1394, - let eth1394 add the ROM entry by itself, i.e. only after eth1394 was loaded. This fixes http://bugzilla.kernel.org/show_bug.cgi?id=7793 . To emulate the behaviour of older kernels, simply add the following to to /etc/modprobe.conf: install ohci1394 /sbin/modprobe eth1394; \ /sbin/modprobe --ignore-install ohci1394 Note, autoloading of eth1394 when an _external_ IP-over-1394 capable device is discovered is _not_ affected by this patch. Signed-off-by: Stefan Richter commit e00f04a70fa387b3accc81b5c346200f836e2a52 Author: Stefan Richter Date: Sun Mar 18 12:23:11 2007 +0100 ieee1394: eth1394: reduce excessive function inlining Shrinks eth1394.ko by about 5%. Many of these functions have only one caller and are therefore auto- inlined anyway. Signed-off-by: Stefan Richter commit 2cd556ae61c862f4d00bb63863c6e5c67fd55bd4 Author: Stefan Richter Date: Sat Feb 10 23:57:57 2007 +0100 ieee1394: eth1394: clean up host removal ether1394_add_host() guarantees that hi->dev != NULL if hi != NULL. Signed-off-by: Stefan Richter commit 157188cb54b22e5c0c6439ef0500ba97b068097a Author: Stefan Richter Date: Sat Feb 10 23:56:38 2007 +0100 ieee1394: eth1394: unregister address space in failure case Warn if hpsb_allocate_and_register_addrspace() failed. Unregister the address space if something else failed. Signed-off-by: Stefan Richter commit ea9057ad622db41745be416e29c5760d141a6514 Author: Stefan Richter Date: Mon Apr 23 21:27:13 2007 +0200 ieee1394: send async streams at S100 The comment says it all. This affects only asynchronous streams sent via raw1394; the eth1394 driver has own code and needs an own fix. Signed-off-by: Stefan Richter commit df18ce85de3deeaf311f96eb3d47e45fc7050f87 Author: Milind Arun Choudhary Date: Wed Apr 11 23:24:34 2007 +0530 ieee1394: SPIN_LOCK_UNLOCKED cleanup SPIN_LOCK_UNLOCKED cleanup,use DEFINE_SPINLOCK instead Signed-off-by: Milind Arun Choudhary Signed-off-by: Stefan Richter commit 9be51c5d789a4864a820662460b8896b12a34c9d Author: Stefan Richter Date: Fri Mar 30 19:21:05 2007 +0200 ieee1394: nodemgr: unify some error messages Shrinks object file size a little bit. Signed-off-by: Stefan Richter commit 9324547235f63b7ebc905feb606291fce5d85ef5 Author: Stefan Richter Date: Fri Mar 30 19:19:55 2007 +0200 ieee1394: nodemgr: less noise in dmesg Everytime when eth1394 or a libraw1394 client updates the configuration ROM, a certain sysfs attribute cannot be added since it already exists. Signed-off-by: Stefan Richter commit d4c60085a97549ad3bb648e0652b9b48b7e42fa8 Author: Stefan Richter Date: Sun Mar 18 00:55:15 2007 +0100 ieee1394: unroll a weird macro This is a coding style touch-up for ieee1394's handle_incoming_packet(). A preprocessor macro contained hardwired variable names and, even worse, the 'break' keyword. This macro is now unrolled and removed. Also, all 'break's which had the effect of a return are replaced by return. And a FIXME comment is brought up to date. Signed-off-by: Stefan Richter commit b9e5eb067b6882f564e1daa26e37ad6145f01da4 Author: Andrew Morton Date: Thu Apr 26 00:16:04 2007 -0700 ieee1394: iso.c needs sched.h alpha: drivers/ieee1394/iso.c: In function 'hpsb_iso_xmit_sync': drivers/ieee1394/iso.c:440: error: invalid use of undefined type 'struct task_struct' drivers/ieee1394/iso.c:440: error: 'TASK_INTERRUPTIBLE' undeclared (first use in this function) drivers/ieee1394/iso.c:440: error: (Each undeclared identifier is reported only once drivers/ieee1394/iso.c:440: error: for each function it appears in.) drivers/ieee1394/iso.c:440: warning: implicit declaration of function 'signal_pending' drivers/ieee1394/iso.c:440: error: invalid use of undefined type 'struct task_struct' drivers/ieee1394/iso.c:440: warning: implicit declaration of function 'schedule' drivers/ieee1394/iso.c: In function 'hpsb_iso_wake': drivers/ieee1394/iso.c:562: error: 'TASK_INTERRUPTIBLE' undeclared (first use in this function) Signed-off-by: Andrew Morton Signed-off-by: Stefan Richter (brought into alphabetic order) commit 9543a931dcd82bfc5143807440ff63c7721a2e2a Author: Stefan Richter Date: Tue Apr 10 02:39:07 2007 +0200 ieee1394: some more includes Signed-off-by: Stefan Richter commit 3a23a81e83fddb40ec0242c74acb4b1829676bfc Author: Torsten Kaiser Date: Mon Apr 9 21:03:15 2007 +0200 ieee1394: ieee1394_transactions needs sched.h drivers/ieee1394/ieee1394_transactions.c fails for me if CONFIG_SMP=n gcc complains: CC drivers/ieee1394/ieee1394_transactions.o drivers/ieee1394/ieee1394_transactions.c: In function 'hpsb_get_tlabel': drivers/ieee1394/ieee1394_transactions.c:183: error: 'TASK_INTERRUPTIBLE' undeclared (first use in this function) drivers/ieee1394/ieee1394_transactions.c:183: error: (Each undeclared identifier is reported only once Signed-off-by: Andrew Morton Signed-off-by: Stefan Richter (added comment) commit 504945c9c6954b83758272d04797f31437dfce9e Author: Randy Dunlap Date: Tue Apr 3 13:00:47 2007 -0700 ieee1394: ieee1394_core printk format Fix printk format string: drivers/ieee1394/ieee1394_core.c:702: warning: format '%d' expects type 'int', but argument 2 has type 'size_t' Signed-off-by: Randy Dunlap Signed-off-by: Stefan Richter commit 7542e0e696d1b6e71e6eb3183cbf2c63ec6b5acb Author: Stefan Richter Date: Sun Mar 25 22:22:40 2007 +0200 ieee1394: remove usage of skb_queue as packet queue This considerably reduces the memory requirements for a packet and eliminates ieee1394's dependency on CONFIG_NET. Signed-off-by: Stefan Richter commit d265250341f83fa904d4fecdfadb46d7ab50765f Author: Stefan Richter Date: Wed Mar 14 00:29:20 2007 +0100 ieee1394: csr1212: log if devices have CRC errors in their ROM This will point out firmware bugs. I tested with 11 SBP-2 devices and one OS X PC and got these errors from two old CD-RWs only. Signed-off-by: Stefan Richter commit 511f7b3227eef52b56cf336a5313d8ff766c3050 Author: Stefan Richter Date: Wed Mar 14 00:28:36 2007 +0100 ieee1394: csr1212: more sensible names for jump targets Code beneath two labels called "fail" is actually also reached in case of success. Signed-off-by: Stefan Richter commit c94ccf9e3389ff55078a049bfe59b82f854436e8 Author: Stefan Richter Date: Wed Mar 14 00:27:46 2007 +0100 ieee1394: csr1212: warn on unreachable code We want bugs to show themselves. Signed-off-by: Stefan Richter commit a1c6250cb60a52a7f799610f9a4b1f4e8671175f Author: Stefan Richter Date: Wed Mar 14 00:27:18 2007 +0100 ieee1394: shrink csr1212_new_string_descriptor_leaf Make unnecessarily generic code specific and thus simpler. Shrink a lookup table from 128 to 16 bytes. Signed-off-by: Stefan Richter commit c868ae2a1d1ad3a474d2a17295ac1ab190b30061 Author: Stefan Richter Date: Wed Mar 14 00:26:38 2007 +0100 ieee1394: csr1212: coding style Whitespace, line breaks, braces... Signed-off-by: Stefan Richter commit fd2f3bddaeb20564f32e59f64e5063fbe0c8f4cc Author: Stefan Richter Date: Sun Mar 11 22:51:24 2007 +0100 ieee1394: replace vmalloc by kmalloc in csr1212 The biggest chunk ever allocated by CSR1212_MALLOC is 1024 Bytes + sizeof(struct csr1212_csr_rom_cache) big. Most of the time much smaller data structures are allocated. Therefore vmalloc is a waste. The one exception is csr1212_append_new_cache() which is called to append a chunk of CSR1212_EXTENDED_ROM_SIZE + sizeof(struct csr1212_csr_rom_cache) if the currently allocated ROM cache is too small. CSR1212_EXTENDED_ROM_SIZE is generously defined as 256 kBytes. In SVN commit 1220, Steve Kinneberg lowered this to 2 kBytes in the config_rom_2.4 branch. This same commit also switched CSR1212_MALLOC from kmalloc to vmalloc in the SVN trunk branch: > r1220 | kberg | 2004-05-31 01:51:44 +0200 (Mon, 31 May 2004) | 13 lines > > CSR1212 Extended ROM bug fixes: > trunk line changes: > - Use vmalloc instead of kmalloc > - Change delayed_reset_bus() to operate in a work_queue instead of a > timer interrupt. > - Fix hpsb_allocate_and_register_addrspace() to not allocate space > on top of already allocated space. > - Fix problems in csr1212.c filling ConfigROM images when extend > ROMs are present. > config-rom-2.4 changes: > - Changed extended rom allocation from 256K to 8K. (It was actually 2 kB, not 8 kB.) > - Fix hpsb_allocate_and_register_addrspace() to not allocate space > on top of already allocated space. > - Fix problems in csr1212.c filling ConfigROM images when extend > ROMs are present. I am now setting CSR1212_EXTENDED_ROM_SIZE to 2 kB minus the overhead of struct csr1212_csr_rom_cache. Note, this code path is not used by the in-kernel drivers though. raw1394 could trigger it, but the respective libraw1394 functions don't exist yet. Furthermore, userspace programs can replace the entire local ROM via raw1394. If kmalloc does not fulfill their needs --- well, tough luck. I decree that nobody needs such huge extended ROMs. (Extended ROMs are defined by IEEE 1212 clause 7.7.18. The spec does not impose practically relevant restrictions on the size of extended ROM chunks.) Another potentially demanding use of CSR1212_MALLOC is if external FireWire devices come with Extended ROM entries. If they are too big for kmalloc (or have been too big for vmalloc) we just fail to read their ROM. This is quite unlikely though, to my knowledge. Signed-off-by: Stefan Richter commit c1a37f2c6572031203243dd083585aa4a1c138d5 Author: Stefan Richter Date: Wed Mar 14 00:20:53 2007 +0100 ieee1394: de-inline some functions This small reorganization of public csr1212 functions saves one exported symbol and a few bytes in the driver modules. Signed-off-by: Stefan Richter commit 64ff712321875c2457d3a77d3fc4ab4989f7a8c0 Author: Stefan Richter Date: Sun Mar 11 22:50:13 2007 +0100 ieee1394: stricter error checks in csr1212 return -EINVAL becomes BUG_ON in checks of function call parameters. Signed-off-by: Stefan Richter commit 982610bd0d8e64baff36099f6dc456ea52d22257 Author: Stefan Richter Date: Sun Mar 11 22:49:34 2007 +0100 ieee1394: csr1212: rename some types Use u8, u32 etc. instead of u_int8_t, csr1212_quad_t etc. Signed-off-by: Stefan Richter commit 7fb9addba8ebd67306099e7fa629ff76c1be2105 Author: Stefan Richter Date: Sun Mar 11 22:49:05 2007 +0100 ieee1394: drop csr1212's support for external compilation csr1212 was written to be compiled either as part of the ieee1394 kernel driver or of an anticipated IEEE 1212 userspace library. We now drop support for the latter. The costs in terms of code footprint and depth of abstraction are not countered by any actual benefit. Also remove some obsolete #includes. Signed-off-by: Stefan Richter commit 6c88e475660edcd5571a5aab39ce8062183af951 Author: Stefan Richter Date: Sun Mar 11 22:47:34 2007 +0100 ieee1394: remove unused csr1212 code Delete unused code. Make some extern functions static. Remove superfluous inline keywords. Move private definitions from csr1212.h to csr1212.c. Signed-off-by: Stefan Richter commit e167c88ebb2fcc2d98bd9a9970ae29e4fda4bdf9 Author: Stefan Richter Date: Mon Mar 5 03:07:38 2007 +0100 ieee1394: small header cleanup Signed-off-by: Stefan Richter commit afd6546d8d2d8ba1dbe1d2508baf81eebdca3d79 Author: Stefan Richter Date: Mon Mar 5 03:06:23 2007 +0100 ieee1394: move some comments from declaration to definition Signed-off-by: Stefan Richter commit ef8153348f82688af87e19d594162ca81741fe6a Author: Stefan Richter Date: Mon Mar 5 03:05:32 2007 +0100 ieee1394: remove declarations of nonexisting functions Signed-off-by: Stefan Richter commit f84c922ba16fdadb9e4ebf9dc177e3dbd3282f7c Author: Andrew Morton Date: Mon Apr 23 11:50:56 2007 -0700 ieee1394: sbp2: include fixes drivers/ieee1394/sbp2.c: In function 'sbp2util_access_timeout': drivers/ieee1394/sbp2.c:399: error: 'TASK_INTERRUPTIBLE' undeclared (first use in this function) drivers/ieee1394/sbp2.c:399: error: (Each undeclared identifier is reported only once drivers/ieee1394/sbp2.c:399: error: for each function it appears in.) drivers/ieee1394/sbp2.c:399: warning: implicit declaration of function 'signal_pending' drivers/ieee1394/sbp2.c:399: warning: implicit declaration of function 'schedule_timeout' drivers/ieee1394/sbp2.c: In function 'sbp2_prep_command_orb_sg': drivers/ieee1394/sbp2.c:1438: warning: implicit declaration of function 'page_address' drivers/ieee1394/sbp2.c:1438: warning: passing argument 2 of 'dma_map_single' makes pointer from integer without a cast drivers/ieee1394/sbp2.c: In function 'sbp2_handle_status_write': drivers/ieee1394/sbp2.c:1842: error: 'TASK_INTERRUPTIBLE' undeclared (first use in this function) Possibly due to changes in -mm, but this file should explicitly include the headers for the stuff it uses. Signed-off-by: Andrew Morton Signed-off-by: Stefan Richter (brought into alphabetic order) commit 3d269cb50c81d3bb01d5856d5157d4db346bab4b Author: Stefan Richter Date: Sun Feb 4 20:57:38 2007 +0100 ieee1394: sbp2: move some memory allocations into non-atomic context When the command ORB pool is created, the ORB list won't be accessed concurrently. Therefore we don't have to take the spinlock there. Signed-off-by: Stefan Richter commit 2446a79f4f0a5e88e5d8316dac407d66ac10f70d Author: Stefan Richter Date: Sun Feb 4 20:54:57 2007 +0100 ieee1394: sbp2: optimize DMA direction of s/g tables Unlike the name suggests, "cmd->scatter_gather_element" holds only the s/g table, not the actual s/g elements. Since the table is only read but never written by the device, DMA_BIDIRECTIONAL can be replaced by DMA_TO_DEVICE which may be cheaper on some architectures. Signed-off-by: Stefan Richter commit 0555659d63c285ceb7ead3115532e1b71b0f27a7 Author: Stefan Richter Date: Sun Feb 4 20:25:43 2007 +0100 ieee1394: sbp2: enforce 32bit DMA mapping In order to use OHCI-1394 physical DMA, all s/g elements, s/g tables, ORBs, and response buffers have to reside within the first 4 GB of the FireWire controller's physical address space. Set the correct mask for DMA mappings. Signed-off-by: Stefan Richter commit f412bf440be2dfc0f0ded917410c599c002549e0 Author: Stefan Richter Date: Sun Feb 4 13:04:32 2007 +0100 ieee1394: sbp2: remove unnecessary alignments of struct members The members "dma_addr_t command_orb_dma" and "dma_addr_t sge_dma" of sbp2.h::sbp2_command_info do not have to be aligned themselves --- only the memory which they point to has to be. The member "struct sbp2_command_orb command_orb" has to be aligned on 4 bytes boundary which is guaranteed because it contains u32 members. The member "struct sbp2_unrestricted_page_table scatter_gather_element", i.e. the SBP-2 s/g table, has to be aligned on 8 bytes boundary according to the SBP-2 spec. This is not a requirement for FireWire controllers but could be expected by SBP-2 targets. I see no need to align the members command_orb and scatter_gather_element on CPU cacheline boundaries. It could have performance benefits, but on the other hand sbp2 has a somewhat wasteful allocation scheme which should be optimized first before further tweaks like cacheline alignments. (E.g. don't always allocate SG_ALL s/g table elements.) Note, before as well as after the patch, the code relies on the assumption that memory alignment in the virtual address space is preserved in the physical address space after DMA mapping. Signed-off-by: Stefan Richter commit f61c9127b9840661244b1b6571e4304a7f0b5c73 Author: Jiri Kosina Date: Sun Apr 29 13:14:56 2007 +0200 USB HID: don't warn on idVendor == 0 It turns out that there are broken devices out there that incorrectly report VID/PID as 0x000, see http://lkml.org/lkml/2007/4/27/496 Therefore we should not confuse users by dumping warnings and stacktraces in such situation. It is not possible to add quirks for such horribly broken devices, but currently that's not needed. Signed-off-by: Jiri Kosina commit 04b090d50c88ac8e5ec9c2e985bb65bd153893aa Author: Martin Schwidefsky Date: Sat Apr 28 23:03:59 2007 -0700 [AF_IUCV/IUCV]: smp_call_function deadlock Calling smp_call_function can lead to a deadlock if it is called from tasklet context. Fixing this deadlock requires to move the smp_call_function from the tasklet context to a work queue. To do that queue the path pending interrupts to a separate list and move the path cleanup out of iucv_path_sever to iucv_path_connect and iucv_path_pending. This creates a new requirement for iucv_path_connect: it may not be called from tasklet context anymore. Also fixed compile problem for CONFIG_HOTPLUG_CPU=n and another one when walking the cpu_online mask. When doing this, we must disable cpu hotplug. Signed-off-by: Frank Pavlic Signed-off-by: Martin Schwidefsky Signed-off-by: David S. Miller commit d0772b70faaf8e9f2013b6c4273d94d5eac8047a Author: Eric Sesterhenn Date: Sat Apr 28 21:26:23 2007 -0700 [IPV6]: Fix slab corruption running ip6sic From: Eric Sesterhenn Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 65bb723c9502b7ba0a3aad13bdac8832e213ba74 Author: Gerrit Renker Date: Sat Apr 28 21:21:46 2007 -0700 [TCP]: Update references in two old comments This updates references to drafts in comments which must be about 10 years old. Internet draft draft-ietf-tcpimpl-prob-03.txt expired in 1998 and was replaced by RFC 2525 in March 1999. Section 3.10 of the draft maps almost identically into section 2.17 of RFC 2525: both are entitled "Failure to RST on close with data pending", the differences in text body amount to a typo and minor sentence change. Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller commit ecfd6b183780c6d9e85873693b3ce6c5f4d08b58 Author: Jamal Hadi Salim Date: Sat Apr 28 21:20:32 2007 -0700 [XFRM]: Export SPD info With this patch you can use iproute2 in user space to efficiently see how many policies exist in different directions. Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller commit 5632c5152aa621885d87ea0b8fdd5a6bb9f69c6f Author: Stephen Hemminger Date: Sat Apr 28 21:16:39 2007 -0700 [IPV6]: Track device renames in snmp6. When network device's are renamed, the IPV6 snmp6 code gets confused. It doesn't track name changes so it will OOPS when network device's are removed. The fix is trivial, just unregister/re-register in notify handler. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit aad97f38b71dd2ecd730b3a3dce8264d13fbcd56 Author: Vlad Yasevich Date: Sat Apr 28 21:09:04 2007 -0700 [SCTP]: Fix sctp_getsockopt_local_addrs_old() to use local storage. sctp_getsockopt_local_addrs_old() in net/sctp/socket.c calls copy_to_user() while the spinlock addr_lock is held. this should not be done as copy_to_user() might sleep. the call to sctp_copy_laddrs_to_user() while holding the lock is also problematic as it calls copy_to_user() Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller commit 5a1b5898ee9e0bf68a86609ecb9775457b1857a5 Author: Rusty Russell Date: Sat Apr 28 21:04:03 2007 -0700 [NET]: Remove NETIF_F_INTERNAL_STATS, default to internal stats. Herbert Xu conviced me that a new flag was overkill; every driver currently overrides get_stats, so we might as well make the internal one the default. If someone did fail to set get_stats, they would now get all 0 stats instead of "No statistics available". Signed-off-by: Rusty Russell Acked-by: Herbert Xu Signed-off-by: David S. Miller commit d16bfd0c77b35a0a0f0e159b94b9921510bf7934 Author: Sergei Shtylyov Date: Sat Apr 28 20:58:22 2007 -0700 [NETPOLL]: Remove CONFIG_NETPOLL_RX Get rid of the CONFIG_NETPOLL_RX option completely since all the dependencies have been removed long ago... Signed-off-by: Sergei Shtylyov Acked-by: Jeff Garzik Acked-by: Matt Mackall Signed-off-by: David S. Miller commit 5f286e113fa377e50bd18fc45e5a0d4d83f6950c Author: Sergei Shtylyov Date: Sat Apr 28 20:57:37 2007 -0700 [NETPOLL]: Fix TX queue overflow in trapped mode. CONFIG_NETPOLL_TRAP causes the TX queue controls to be completely bypassed in the netpoll's "trapped" mode which easily causes overflows in the drivers with short TX queues (most notably, in 8139too with its 4-deep queue). So, make this option more sensible by making it only bypass the TX softirq wakeup. Signed-off-by: Sergei Shtylyov Acked-by: Jeff Garzik Acked-by: Tom Rini Acked-by: Matt Mackall Signed-off-by: David S. Miller commit c6c60106b9584f17c55e4c5e0ce9b905a1a6cdb6 Author: Matthias Kaehlcke Date: Tue Apr 24 22:02:35 2007 +0200 sonypi: use mutex instead of semaphore the Sony Programmable I/O Control driver uses a semaphore as mutex. use the mutex API instead of the (binary) semaphore Signed-off-by: Matthias Kaehlcke Acked-by: Mattia Dongili Signed-off-by: Len Brown commit 1b20d34406775369d50fc2ffe27a64a0d6fd313e Author: malattia@linux.it Date: Sat Apr 28 23:36:40 2007 +0900 sony-laptop: remove user visible camera controls as platform attributes Avoid giving the user the possibility to shoot his own foot and let the meye driver enable/disable the camera wisely (PCI_ID based). Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit cbefb762b67fa6d3eb2a48ae3380358a940e8c9d Author: malattia@linux.it Date: Sat Apr 28 23:36:26 2007 +0900 meye: make meye use sony-laptop instead of sonypi Change sonypi_camera_command() calls to sony_pic_camera_command() and use the renamed macros. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 1ce82c14d06ff68380d7c647f768858e077930c5 Author: malattia@linux.it Date: Sat Apr 28 23:34:36 2007 +0900 sony-laptop: add a meye-usable include file for camera ops Copy and rename (for easier co-existence) the MEYE-wise exported interface. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit e364632e740fe9fcb401e5ece3be69e4d81c5a80 Author: malattia@linux.it Date: Sat Apr 28 23:34:22 2007 +0900 sony-laptop: complete the motion eye camera support in sony-laptop Add the exported sony_pic_camera_command() function to make the MEYE driver happy. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 1a3e323907dc5991cba2d715d5db3ae2eac78280 Author: malattia@linux.it Date: Sat Apr 28 23:34:10 2007 +0900 sonypi: try to detect if sony-laptop has already taken one of the known ioports Get the IO resources list in sony-laptop in the same order as listed in sonypi and make sonypi check if one of those is already busy. The sonypi check can be disabled by a module parameter in case the user thinks we are plainly wrong (check_ioport=0). Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 74a882e4857414a98ca5904b3be90fb6aba2f25e Author: malattia@linux.it Date: Sat Apr 28 23:22:11 2007 +0900 sonypi: suggest sonypi users to try sony-laptop instead Try to migrate sonypi users to sony-laptop gracefully. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 9476cdfae61a3c3fa61d06c18dd002b03671ca9f Author: malattia@linux.it Date: Sat Apr 28 23:21:42 2007 +0900 sony-laptop: add edge modem support (also called WWAN) Some SZ Vaios have a gsm built-in modem. Allow powering on/off this device. Thanks to Joshua Wise for the base code. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 9f9f0761712928768198278c6cbc5cafe5502d38 Author: malattia@linux.it Date: Sat Apr 28 23:19:36 2007 +0900 sony-laptop: add locking on accesses to the ioport and global vars Better avoid having ioport commands mixing and global variables reading/writing. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 5f3d2898d79520bc8d8706ed3859060f9cbb969e Author: malattia@linux.it Date: Sat Apr 28 23:18:45 2007 +0900 sony-laptop: add camera enable/disable parameter, better handle possible infinite loop Use a parameter to enable/disable motion eye camera (for C1VE/C1VN models) controls and avoid entering an infinite loop if the camera is not present and the HW doesn't answer as we expect on io commands. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 836a53f42f3b5d5cb3a0751587ea33801e4b120d Author: Adrian Bunk Date: Sat Apr 28 21:19:38 2007 +0200 thinkpad-acpi: make drivers/misc/thinkpad_acpi:fan_mutex static This patch makes the needlessly global fan_mutex static. Signed-off-by: Adrian Bunk Acked-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit d3a6ade4f84416d774c3e5db5faae1840d55bd97 Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:17 2007 -0300 ACPI: thinkpad-acpi: add sysfs support to wan and bluetooth subdrivers Add support to sysfs to the wan and bluetooth subdrivers. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit a0416420e2c6244792d6f308183ad57c40532078 Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:16 2007 -0300 ACPI: thinkpad-acpi: add sysfs support to hotkey subdriver Add the hotkey sysfs support. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit d94a7f16cad7700f8d2b142cc13cfba5387af3db Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:15 2007 -0300 ACPI: thinkpad-acpi: improve dock subdriver initialization The dock sub-driver has split-personality (two subdrivers), and it was doing some unoptimal things on init because of that. Fix it so that the second half of it will only init when necessary, and only if the first half initialized sucessfully in the first place. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 5ae930e685018e2dc6d4139362213e4b283e5700 Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:14 2007 -0300 ACPI: thinkpad-acpi: improve debugging for acpi helpers Some issues with the dock subdriver proved that a slightly improved debugging setup for ACPI notifiers and handler helpers would be useful. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit b39fe582eb9252dca9a62f7135bcad2e486083e5 Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:13 2007 -0300 ACPI: thinkpad-acpi: improve fan control documentation Improve fan control documentation and fix one mistake. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit c573ddb998456a89a5ccb83a922d2c8ba18484a6 Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:12 2007 -0300 ACPI: thinkpad-acpi: map ENXIO to EINVAL for fan sysfs Currently, all fan control operations return ENXIO if unsupported operations are requested, but return EINVAL if invalid fan modes are requested on a given ThinkPad. This is not strictly correct for sysfs, so map ENXIO to EINVAL in the sysfs attribute store handlers, as we do benefit from the ENXIO in other parts of the driver code. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit ca4ac2f48a4502bbbfcb47b86312273c28194f53 Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:11 2007 -0300 ACPI: thinkpad-acpi: fix a fan watchdog invocation The fan control watchdog was being called in one place even when the fan control operation had failed. Fix it. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 4985cd0a63b0713b6469ef01aae6a0e63ea72f83 Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:10 2007 -0300 ACPI: thinkpad-acpi: do not arm fan watchdog if it would not work Do not enable/rearm the fan control safety watchdog if we would not be able to do anything to the fan anyway. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit ecf2a80a97b3d38ae008fa8a3cb98cd540ac1eae Author: Henrique de Moraes Holschuh Date: Fri Apr 27 22:00:09 2007 -0300 ACPI: thinkpad-acpi: add a fan-control feature master toggle Len Brown considers that an active by default fan control interface in laptops may be too close to giving users enough rope. There is a good chance he is quite correct on this, especially if someone decides to use that interface in applets and users are not aware of its risks. This patch adds a master switch to thinkpad-acpi that enables or disables the entire fan-control feature as a module parameter: "fan_control". It defaults to disabled. Set it to non-zero to enable fan control. Also, the patch removes the expermiental status from fan control, since it is stable enough to not be called experimental, and the master switch makes it safe enough to do so. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 79fff270026dc46634563a29b99e4034028ee919 Author: Bob Moore Date: Sat Apr 28 20:53:50 2007 -0400 ACPICA: clear fields reserved before FADT r3 Linux-2.6.21 stopped booting on a P4/HT because Linux wrote the FADT.CST_CNT value to the SMI_CMD. Apparently this stumbled over some SMM instability, such as confusing SMM when invoking it from cpu1. Linux did this because even though the r2 FADT reserves the CST_CNT field, this BIOS set that field and Linux used it. Turns out that up through 2.6.20 we explicitly cleared cst_control for r2 FADTs. So here we go back to doing that, plus also clear some additional fields that are reserved until FADT r3. http://bugzilla.kernel.org/show_bug.cgi?id=8346 Signed-off-by: Bob Moore Signed-off-by: Len Brown commit 225036314e768bbfe8331ea9b0e91ca101afe427 Author: Milind Arun Choudhary Date: Thu Apr 26 00:19:27 2007 -0700 SPIN_LOCK_UNLOCKED cleanup in drivers/ata/pata_winbond.c remove SPIN_LOCK_UNLOCKED, use DEFINE_SPINLOCK instead Signed-off-by: Milind Arun Choudhary Acked-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 4b22afd743a3882123a16135fa863558c0ac2d94 Author: Andrew Morton Date: Thu Apr 26 00:19:26 2007 -0700 drivers/ata/pata_cmd640.c: fix build with CONFIG_PM=n This is grubby, but all the ata drivers do it this way. Would it not be better to do #define ata_scsi_device_resume NULL in libata.h, remove all those ifdefs? (updated version, ug, ug) Cc: Jeff Garzik Cc: Alan Cox Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit a473446856374668c4296db5256ca708ce8f7095 Author: Alan Cox Date: Thu Apr 26 00:19:25 2007 -0700 pata_hpt37x: Further small fixes Further HPT37x changes - No 66MHz 370/370A - Remove dead special case check now we use the DPLL (as per the IDE driver) Pointed out by Sergei Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 28e21c8c0d44cd63bad4c62f94ef0c5a1cb8402c Author: Alan Cox Date: Thu Apr 26 00:19:25 2007 -0700 pata_hpt3x2n: Add HPT371N support and other bits Yes its no longer 3x2n but 3xxn, I can rename it if you want Jeff - Don't reset both ports each time (Sergei) - If we can't get a DPLL then abort entirely - Use ioport access for clock (from drivers/ide) - Add HPT371N support (from drivers/ide) Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit bd1d5ec64fff579e624b7c50c8a737da112efe5f Author: Andrew Morton Date: Thu Apr 26 00:19:21 2007 -0700 ata: printk warning fixes drivers/ata/libata-core.c: In function 'ata_hpa_resize': drivers/ata/libata-core.c:986: warning: format '%lld' expects type 'long long int', but argument 5 has type 'u64' drivers/ata/libata-core.c:986: warning: format '%lld' expects type 'long long int', but argument 6 has type 'u64' drivers/ata/libata-core.c:990: warning: format '%lld' expects type 'long long int', but argument 4 has type 'u64' drivers/ata/libata-core.c:990: warning: format '%lld' expects type 'long long int', but argument 5 has type 'u64' drivers/ata/libata-core.c:1003: warning: format '%lld' expects type 'long long int', but argument 4 has type 'u64' Also fix various 80-col bustage. Cc: Jeff Garzik Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 0d64a233fe6a8fd45a062fe125159854ffed60c7 Author: Tejun Heo Date: Mon Apr 23 02:41:05 2007 +0900 libata: separate ATA_EHI_DID_RESET into DID_SOFTRESET and DID_HARDRESET Separate ATA_EHI_DID_RESET into ATA_EHI_DID_SOFTRESET and ATA_EHI_DID_HARDRESET. ATA_EHI_DID_RESET is redefined as OR of the two flags. This patch doesn't introduce any behavior change. This will be used later to determine whether _SDD is necessary or not. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 1188c0d83ca010c3799711e85e63dbde122e6a90 Author: Tejun Heo Date: Mon Apr 23 02:41:05 2007 +0900 ahci: consolidate common port flags Consolidate common port flags into AHCI_FLAG_COMMON. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 4f701d1e9a796a3d6657e1129bee0566d7cda916 Author: Alan Cox Date: Mon Apr 23 11:55:36 2007 +0100 ata_timing: ensure t->cycle is always correct Russell King hit a case where quantisation errors accumulated such that the cycle time was shorter than rather than equal to the active/recovery time. The code already knows how to stretch times to fit the cycle time but does not know about the reverse. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit c1c4e8d55757f8aec5f95eb80860e340d717c217 Author: Tejun Heo Date: Mon Apr 23 02:05:53 2007 +0900 libata: add missing call to ->cable_detect() in new EH path ->cable_detect() used to be called on by the old ata_bus_probe() path. Add invocation to ata_eh_revalidate_and_attach() right after IDENTIFYs are done. Signed-off-by: Tejun Heo Cc: Alan Cox Signed-off-by: Jeff Garzik commit abcfa88bd47d433c796cf724a8a8b321a7190bdd Author: Tejun Heo Date: Mon Apr 23 02:04:38 2007 +0900 pata_amd: remove contamination added during cable_detect conversion This is added by added by cff63dfceb52c564fe1ba5394d50ab7d599a11b9 - pata: cable methods. Signed-off-by: Tejun Heo Cc: Alan Cox Signed-off-by: Jeff Garzik commit 169439c2e35f01e7832a9b4fc8a7446980c3d593 Author: Mark Lord Date: Tue Apr 17 18:26:07 2007 -0400 libata: Handle drives that require a spin-up command before first access (S)ATA drives can be configured for "power-up in standby", a mode whereby a specific "spin up now!" command is required before the first media access. Currently, a drive with this feature enabled can not be used at all with libata, and once in this mode, the drive becomes a doorstop. The older drivers/ide subsystem at least enumerates the drive, so that it can be woken up after the fact from a userspace HDIO_* command, but not libata. This patch adds support to libata for the "power-up in standby" mode where a "spin up now!" command (SET_FEATURES) is needed. With this, libata will recognize such drives, spin them up, and then re-IDENTIFY them if necessary to get a full/complete set of drive features data. Drives in this state are determined by looking for special values in id[2], as documented in the current ATA specs. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik commit 1e999736cafdffc374f22eed37b291129ef82e4e Author: Alan Cox Date: Wed Apr 11 00:23:13 2007 +0100 libata: HPA support Signed-off-by: Alan Cox Add support for ignoring the BIOS HPA result (off by default) and setting the disk to the full available size unless already frozen. Tested with various platforms/disks and confirmed to work with the Macintosh (which broke earlier) and ata_piix (breakage due to the LBA48 readback that Tejun fixed). For normal users this brings us, I believe, to feature parity with old IDE (and of course more featured in some areas too). Signed-off-by: Jeff Garzik commit 6bfff31e77cfa1b13490337e5a4dbaa3407e83ac Author: Tejun Heo Date: Tue Apr 17 23:44:08 2007 +0900 libata: kill probe_ent and related helpers All drivers are converted to new init model. Kill probe_ent, ata_device_add() and ata_pci_init_native_mode(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 5d728824efeda61d304153bfcf1378a3c18b7d70 Author: Tejun Heo Date: Tue Apr 17 23:44:08 2007 +0900 libata: convert the remaining PATA drivers to new init model Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf, pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x, pata_platform, pata_qdi, pata_scc and pata_winbond to new init model. * init_one()'s now follow more consistent init order * cs5520 now registers one host with two ports, not two hosts. If any of the two ports are disabled, it's made dummy as other drivers do. Tested pdc_adma and pata_legacy. Both are as broken as before. The rest are compile tested only. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 4447d35156169cf136e829eb6b5cac2d6370f2d9 Author: Tejun Heo Date: Tue Apr 17 23:44:08 2007 +0900 libata: convert the remaining SATA drivers to new init model Convert ahci, sata_sil, sata_sil24, sata_svw, sata_qstor, sata_mv, sata_sx4, sata_vsc and sata_inic162x to new init model. Now that host and ap are available during intialization, functions are converted to take either host or ap instead of low level parameters which were inevitable for functions shared between init and other paths. This simplifies code quite a bit. * init_one()'s now follow more consistent init order * ahci_setup_port() and ahci_host_init() collapsed into ahci_init_one() for init order consistency * sata_vsc uses port_info instead of setting fields manually * in sata_svw, k2_board_info converted to port_info (info is now in port flags). port number is honored now. Tested on ICH7/8 AHCI, jmb360, sil3112, 3114, 3124 and 3132. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 9a829ccfc833269bdb85751f5048288ab93678ac Author: Tejun Heo Date: Tue Apr 17 23:44:08 2007 +0900 libata: convert ata_pci_init_native_mode() users to new init model Convert drivers which use ata_pci_init_native_mode() to new init model. ata_pci_init_native_host() is used instead. sata_nv, sata_uli and sata_sis are in this category. Tested on nVidia Corporation CK804 Serial ATA Controller [10de:0054] in both BMDMA and ADMA mode. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit eca25dca17630ae354f4b1df559ed90578b794fe Author: Tejun Heo Date: Tue Apr 17 23:44:07 2007 +0900 libata: convert drivers with combined SATA/PATA ports to new init model Convert sata_via and sata_promise to new init model. Both controllers can have combined configuration (SATA + PATA) and used twisted initialization method (modifying port in ->port_start) to overcome probe_ent limitations. This patch converts both drivers to new init model in which such configuration is natively supported. * promise: Combined pata port now uses separate port_info entry right after the sata counterpart entry. * promise: Controller configuration is discerned using ap->flags. This simplifies init path and makes it look more like other LLDs. * via: Both SATA and PATA ports in vt6421 are represented in their own port_info structure. Tested on PDC20375 (SATA150 TX2plus) [105a:3375] and PDC40775 (SATA 300 TX2plus) [105a:3d73]. Couldn't test via cuz my c3 won't boot the current kernel. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 21b0ad4fb8306ac2bf5a249ffc978b1b8924c7d0 Author: Tejun Heo Date: Tue Apr 17 23:44:07 2007 +0900 libata: add init helpers including ata_pci_prepare_native_host() These will be used to convert LLDs to new init model. * Add irq_handler field to port_info. In new init model, requesting IRQ is LLD's responsibility and libata doesn't need to know about irq_handler. Most LLDs can simply register their irq_handler but some need different irq_handler depending on specific chip. The added port_info->irq_handler field can be used by LLDs to select the matching IRQ handler in such cases. * Add ata_dummy_port_info. * Implement ata_pci_prepare_native_host(), a helper to alloc ATA host, acquire all resources and init the host in one go. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit d491b27b1959565671e2c05dff09b5f535a854ce Author: Tejun Heo Date: Tue Apr 17 23:44:07 2007 +0900 libata: convert native PCI host handling to new init model Convert native PCI host handling to alloc-init-register model. New function ata_pci_init_native_host() follows the new init model and replaces ata_pci_init_native_mode(). As there are remaining LLD users, the old function isn't removed yet. ata_pci_init_one() is reimplemented using the new function and now fully converted to new init model. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 0f834de3ea61aacacf1fac59ba9e82680f83c846 Author: Tejun Heo Date: Tue Apr 17 23:44:07 2007 +0900 libata: convert legacy PCI host handling to new init model Convert legacy PCI host handling to alloc-init-register model. ata_init_legacy_host(), ata_request_legacy_irqs() and ata_pci_init_bmdma() are separated out and follow the new init model. The two legacy handling functions use separate ata_legacy_devres instead of generic devm_* resources. This reduces devres overhead for legacy hosts which was a bit high because it didn't use PCI/iomap merged resoruces. ata_pci_init_one() is rewritten in terms of the aboved functions but native mode handling is still using the old method. Conversion will be completed when native mode handling is updated. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit f5cda257296fbd3683b1f568f2d94d3caaacf74d Author: Tejun Heo Date: Tue Apr 17 23:44:07 2007 +0900 libata: implement ata_host_alloc_pinfo() and ata_host_register() Implement ata_host_alloc_pinfo() and ata_host_register(). These helpers will be used in the following patches to adopt new init model. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit f31871951b38daf2d7ca17daad59fdb735062da3 Author: Tejun Heo Date: Tue Apr 17 23:44:07 2007 +0900 libata: separate out ata_host_alloc() and ata_host_register() Reorganize ata_host_alloc() and its subroutines into the following three functions. * ata_host_alloc() : allocates host and its ports. shost is not registered automatically. * ata_scsi_add_hosts() : allocates and adds shosts associated with an ATA host. Used by ata_host_register(). * ata_host_register() : takes a fully initialized ata_host structure and registers it to libata layer and probes it. Only ata_host_alloc() and ata_host_register() are exported. ata_device_add() is rewritten using the above functions. This patch does not introduce any observable behavior change. Things worth mentioning. * print_id is assigned at registration time and LLDs are allowed to overallocate ports and reduce host->n_ports during initialization. ata_host_register() will throw away unused ports automatically. * All SCSI host initialization stuff now resides in ata_scsi_add_hosts() in libata-scsi.c, where it should be. * ipr is now the only user of ata_host_init(). Either kill it by converting ipr to use ata_host_alloc() and friends or rename and move it to libata-scsi.c Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit ecef7253235e7a9365afe08a508e11bed91c1c11 Author: Tejun Heo Date: Tue Apr 17 23:44:06 2007 +0900 libata: separate out ata_host_start() Separate out ata_host_start() from ata_device_add(). ata_host_start() calls ->port_start on each port if available and freezes the port. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 4911487a34baa89ec5b5f09a661761b73091fbec Author: Tejun Heo Date: Tue Apr 17 23:44:06 2007 +0900 libata: allocate ap separately from shost Don't embed ap inside shost. Allocate it separately and point it back from shosts's hostdata. This makes port allocation more flexible and allows regular ATA and SAS share host alloc/init paths. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit bf33554290bb6a6b2bd8827076f89fb17fb19e3d Author: Tejun Heo Date: Wed Apr 11 17:27:14 2007 +0900 ahci: add PCI ID for new VIA chip Add PCI ID for new VIA chip. Original patch is from Maarten Vanraes. Signed-off-by: Tejun Heo Cc: Maarten Vanraes Signed-off-by: Jeff Garzik commit 03ec52dea0f3c615b1b502672c189f296842f7dd Author: Tejun Heo Date: Thu Apr 12 13:38:11 2007 +0900 libata: kill type mismatch compile warning kill the following compile warning. drivers/ata/libata-core.c:1786: warning: comparison of distinct pointer types lacks a cast Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit c65ec1c25dc23123040efdc4ada46071431723e3 Author: Conke Hu Date: Wed Apr 11 18:23:14 2007 +0800 ahci.c: remove non-existing SB600 raid id (re-send) SB600 RAID and SB600 SATA is the same controller and share the same PCI ID 0x4380. There is no such PCI ID 0x4381. Signed-off-by: Conke Hu --------- Signed-off-by: Jeff Garzik commit a51d644af6eb0a93bc383e5f005faa445c87f335 Author: Tejun Heo Date: Tue Mar 20 15:24:11 2007 +0900 libata: improve AC_ERR_DEV handling for ->post_internal_cmd ->post_internal_cmd is simplified EH for internal commands. Its primary mission is to stop the controller such that no rogue memory access or other activities occur after the internal command is released. It may provide error diagnostics by setting qc->err_mask but this hasn't been a requirement. To ignore SETXFER failure for CFA devices, libata needs to know whether a command was failed by the device or for any other reason. ie. internal command needs to get AC_ERR_DEV right. This patch makes the following changes to AC_ERR_DEV handling and ->post_internal_cmd semantics to accomodate this need and simplify callback implementation. 1. As long as the correct bits in the result TF registers are set, there is no need to set AC_ERR_DEV explicitly. libata EH core takes care of that for both normal and internal commands. 2. The only requirement for ->post_internal_cmd() is to put the controller into quiescent state. It needs not to set any err_mask. 3. ata_exec_internal_sg() performs minimal error analysis such that AC_ERR_DEV is automatically set as long as result_tf is filled correctly. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 55a6adeea4077521b4bba1dfe674f5835157a00b Author: Tejun Heo Date: Fri Mar 9 19:43:35 2007 +0900 libata: fix native mode disabled port handling Disabled port handling in ata_pci_init_native_mode() is slightly broken in that it may end up using the wrong port_info. This patch updates it such that disables ports are made dummy as done in the legacy and other cases. While at it, fix indentation in ata_resources_present(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 17199b187b5d9a22f2ec835c9fdb38302501b918 Author: Tejun Heo Date: Sun Mar 18 22:26:53 2007 +0900 ahci: move port_map handling to ahci_save_initial_config() Move cross checking between port_map and cap.n_ports into ahci_save_initial_config(). After save_initial_config is done, hpriv->port_map is always setup properly. Tested on JMB363, ICH7 and ICH8 (with dummy ports). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit d447df140d0f07a02bd221cb42eb0b61bce16042 Author: Tejun Heo Date: Sun Mar 18 22:15:33 2007 +0900 ahci: implement ata_save/restore_initial_config() There are several registers which describe how the controller is configured. These registers are sometimes implemented as r/w registers which are configured by firmware and get cleared on controller reset or after suspend/resume cycle. ahci saved and restored those values inside ahci_reset_controller() which is a bit messy and doesn't work over suspend/resume cycle. This patch implements ahci_save/restore_initial_config(). The save function is called during driver initialization and saves cap and port_map to hpriv. The restore function is called after the controller is reset to restore the initial values. Sometimes the initial firmware values are inconsistent and need to be fixed up. This is handled by ahci_save_initial_config(). For this, there are two versions of saved registers. One to write back to the hardware register, the other to use during driver operation. This is necessary to keep ahci's behavior unchanged (write back fixed up port_map while keeping cap as-is). This patch makes ahci save the register values once before the first controller reset, not after it's been reset. Also, the same stored values are used written back after each reset, so the register values are properly recovered after suspend/resume cycle. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit ce2d3abc292c1eecd9ddc6f03391a0a46c6561dc Author: Mikael Pettersson Date: Sat Apr 7 14:29:51 2007 +0200 sata_promise: fix error decode regression Promise ATA ports should always be reset by pdc_reset_port() when errors are detected, but the recent error reason decoding update to sata_promise replaced that reset with a freeze. This patch changes the error detection to do a reset again. This makes the error decoding update safer, as it now only adds error decoding without changing any other behaviour. Signed-off-by: Mikael Pettersson Signed-off-by: Jeff Garzik commit 9bedb799f2d270dc6434473fcab745adc930a85b Author: Alan Cox Date: Wed Apr 11 00:19:00 2007 +0100 pata_pdc2027x: Updates Signed-off-by: Alan Cox Correct missing modefilter (crash if BAR4 unassigned) Use Cable Detect method Wrap ->set_mode instead ready for ->post_set_mode removal Maxtor errata as per Jeff Garzik report Remove duplicated private udma_mask hacking Signed-off-by: Jeff Garzik commit ad648c08835851b1b447b08144013e86aceafe5f Author: Alan Cox Date: Wed Apr 11 00:14:09 2007 +0100 pata_winbond Not a PCI device so doesn't need PCI includes Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit eb4a2c7f03db06dda0370591c958fa5a62ff2ec3 Author: Alan Cox Date: Wed Apr 11 00:04:20 2007 +0100 pata: cable methods Versus upstream as requested Last of the trivial switches to cable_detect methods. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 5a5dbd18a7496ed403f6f54bb20c955c65482fa5 Author: Mark Lord Date: Fri Mar 16 10:22:26 2007 -0400 libata: add support for READ/WRITE LONG The READ/WRITE LONG commands are theoretically obsolete, but the majority of drives in existance still implement them. The WRITE_LONG and WRITE_LONG_ONCE commands are of particular interest for fault injection testing -- eg. creating "media errors" at specific locations on a disk. The fussy bit is that these commands require a non-standard sector size, usually 520 bytes instead of 512. This patch adds support to libata for READ/WRITE LONG commands issued via SG_IO/ATA_16. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik commit 1234010684bb9cde51125ec3d1c71054a9f24f47 Author: Robin H\. Johnson Date: Wed Mar 28 18:02:07 2007 -0700 Add notation that the Asus W5F laptop has a short cable instead of 80-wire. The Asus W5F laptop uses a short cable instead of the 80-wire style, and thus needs to be in the ich_laptop special cases for correct detection and support of UDMA/100 for the hard drive. I noticed this because I have the W5F laptop, and was tracing apparent slowness. Signed-off-by: Robin H. Johnson Signed-off-by: Jeff Garzik commit 5c25bf0d2893ae03287693b958e9ef12e4893e04 Author: Alan Cox Date: Mon Mar 26 21:43:43 2007 -0800 pata_optidma: rework for cable detect and to remove post_set_mode() A lot of noise because I had to rename the optidma_set_mode() method to avoid confusion with the new ->set_mode() method that was added. Cable detect side is pretty trivial. Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 2f5344b1be57e63b6a3bfa19e69c5cd84f031edb Author: Alan Cox Date: Mon Mar 26 21:43:42 2007 -0800 pata_winbond: restore cable method Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit c45a6328742be39eee351a4ac7a5e60f7d048846 Author: Alan Cox Date: Mon Mar 26 21:43:41 2007 -0800 pata_sl82c105: restore cable detect method Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 3b4ba5910b150c5db019642899d7b6ba9bec27b4 Author: Alan Cox Date: Mon Mar 26 21:43:40 2007 -0800 pata_netcell: re-remove all the crud Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 3be40d7665e733c2b53bac38fcb148433718f555 Author: Alan Cox Date: Mon Mar 26 21:43:40 2007 -0800 pata_qdI: restore cable detect Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit b723d1448cbefc9fd591798d229d1dafb358bc66 Author: Alan Cox Date: Mon Mar 26 21:43:37 2007 -0800 pata_ali: remove all the crap again and switch to cable_detect method Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit f2fb344beadf79f9d265c3fc60e8399bbf917a4d Author: Robert Hancock Date: Mon Mar 26 21:43:36 2007 -0800 sata_nv: don't read shadow registers when in ADMA mode Reading from the ATA shadow registers while we are in ADMA mode may cause undefined behavior. Don't read the ATA status register when completing commands for this reason, it shouldn't be needed as the controller will notify us if the command failed. Also, don't allow commands with result taskfile requested to execute in ADMA mode, since that requires accessing the shadow registers. We also still need to override tf_read since libata will read the result taskfile on a command failure, and we need to go into port register mode before allowing this. Signed-off-by: Robert Hancock Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 4dc5200d70a3e81510956c35fbb9fa3c15b440a5 Author: Alan Date: Mon Mar 26 21:43:34 2007 -0800 pcmcia - spot slave decode flaws (for testing) It tries to spot when the slave is a mirror of the master and to fix up problems that causes. I've got two confirmations so far that this plus the "can fail set_xfer" patch work for folks who had problems before. Also if you are unfortunate enough to be running something like HAL then it'll automount the same disk twice for you and corrupt it without the fix (aint that nice...) Tested (successfully) by Komuro . Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 120bda35ff8514c937dac6d4e5c7dc6c01c699ac Author: Andrew Morton Date: Mon Mar 26 02:17:43 2007 -0700 git-libata-all-ipr-fix drivers/scsi/ipr.c: In function '__ipr_eh_dev_reset': drivers/scsi/ipr.c:3865: warning: passing argument 4 of 'ata_do_eh' from incompatible pointer type Cc: Tejun Heo Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 771b8dad9653d2659e0ffcc237184cb16c317788 Author: Tejun Heo Date: Wed Mar 14 01:20:51 2007 +0900 libata: hardreset on SERR_INTERNAL There was a rare report where SB600 reported SERR_INTERNAL and SRST couldn't get it out of the failure mode. Hardreset on SERR_INTERNAL. As the problem is intermittent, whether this fixes the problem or not hasn't been verified yet, but hardresetting the channel on internal error is a good idea anyway. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik commit 176efb054422bffe6b5a34194ffad134366c7f7e Author: Mikael Pettersson Date: Wed Mar 14 09:51:35 2007 +0100 sata_promise: decode and report error reasons This patch adds much needed error reason decoding and reporting to sata_promise. It's simplistic but should log all relevant error info the controller provides. Signed-off-by: Mikael Pettersson Signed-off-by: Jeff Garzik commit 724114a5738131997af9272f3c716dbe457c1690 Author: Mikael Pettersson Date: Sun Mar 11 21:20:43 2007 +0100 sata_promise: separate SATA and PATA ops This patch changes sata_promise so that the PATA ports on TX2plus chips are bound to the pdc_pata_ops structure. This means that operations called from the SATA ops structures don't need any SATA-vs-PATA tests any more. Instead, operations that depend on a port being SATA or PATA are separated into different procedures. * pdc_cable_type() is split into a PATA version and a SATA version * pdc_error_handler() is split into a PATA version and a SATA version, that both call a common version after setting up the `hardreset' function pointer * pdc_old_check_atapi_dma() is now only used for SATAI ports, so is renamed to pdc_old_sata_check_atapi_dma() and simplified * pdc_sata_scr_{read,write}() are now only used for SATA ports, so their is-not-SATA tests are removed * pdc_port_start() is split into three procedures: a wrapper which performs the ->ops adjustment on TX2plus PATA ports, a procedure with the common code, and a procedure with the SATA-specific code (this bit might be cleaned up by Tejun's new init model) Tested on 20619, 20575, and 20775 chips. Signed-off-by: Mikael Pettersson Signed-off-by: Jeff Garzik commit 799331fda03f969b781553b786f38b83ec3bb608 Author: Mikael Pettersson Date: Sun Mar 11 21:19:27 2007 +0100 sata_promise: add missing cable_detect hooks The recent change which moved cable detection from pdc_pre_reset() to the new ->cable_detect hook only added the hook for SATAII chips, leaving SATAI chips and the 20619 without the hook. Fixed by this patch. Signed-off-by: Mikael Pettersson Signed-off-by: Jeff Garzik commit 8cdfb29c0cd8018f92214c11c631d8926f4cb032 Author: Jeff Garzik Date: Fri Mar 9 10:54:42 2007 -0500 libata/IDE: remove combined mode quirk Both old-IDE and libata should be able handle all controllers and devices found using normal resource reservation methods. This eliminates the awful, low-performing split-driver configuration where old-IDE drove the PATA portion of a PCI device, in PIO-only mode, and libata drove the SATA portion of the /same/ PCI device, in DMA mode. Typically vendors would ship SATA hard drive / PATA optical configuration, which would lend itself to slow (PIO-only) CD-ROM performance. For Intel users running in combined mode, it is now wholly dependent on your driver choice (potentially link order, if you compile both drivers in) whether old-IDE or libata will drive your hardware. In either case, you will get full performance from both SATA and PATA ports now, without having to pass a kernel command line parameter. Signed-off-by: Jeff Garzik commit e424675f152572d8d2365e351b90bfd81686a150 Author: Jeff Garzik Date: Fri Mar 9 09:56:46 2007 -0500 [libata] turn on !IORDY filter The previous commit erroneously noted that the !IORDY filter was turned on. No true, that change was split out into this commit. Originally authored and signed-off-by Alan Cox. Signed-off-by: Jeff Garzik commit cffacd85bcf6fc652292001873119333555fe260 Author: Jeff Garzik Date: Fri Mar 9 09:46:47 2007 -0500 [libata] sata_mv: support ->cable_detect Signed-off-by: Jeff Garzik commit a76b62ca70662cd0ca98edf366c6637009a95f7d Author: Alan Cox Date: Fri Mar 9 09:34:07 2007 -0500 libata: Change prototype of mode_filter to remove ata_port* With Tejun having added adev->ap some time ago we can get rid of the almost unused port being passed to mode filters. And while we are doing filters, lets turn on the !IORDY filter as well. Signed-off-by: Alan Cox With some hand massaging from Signed-off-by: Jeff Garzik commit 9d2c7c75f889a3eefad016c71f651b0796e0a6e9 Author: Alan Cox Date: Thu Mar 8 23:09:12 2007 +0000 sata_sil: First step to removing ->post_set_mode Now that we have ata_do_set_mode() available for drivers to use we don't actually need ->post_set_mode() as the driver can wrap set_mode nicely and do stuff before or after (eg PCMCIA needs before), so we can kill off a method in all the structs While I was at it I added kernel-doc to the function involved. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 432729f0b0c299ae9731aaa31beaa0dd3a9751eb Author: Alan Cox Date: Thu Mar 8 23:22:59 2007 +0000 libata-core: Fix the iordy methods This alone isn't sufficient to save the universe from prehistoric disks and controllers but it is a first important step. Split off a separate function to provide a mode filter when controller iordy is not available. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 04351821b43e6c0c91ad50d7e4be54a935f749e1 Author: Alan Date: Tue Mar 6 02:37:52 2007 -0800 pata: expose set_mode method so it can be wrapped This splits set_mode into do_set_mode and the wrapper so that a driver can call the standard method inside its own. This in theory also obsoletes ->post_set_mode(). Signed-off-by: Alan Cox Cc: Jeff Garzik Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit fcc2f69a6fad1543b466db9c35aa5a2f364eb3d4 Author: Alan Cox Date: Thu Mar 8 23:28:52 2007 +0000 pata_hpt37x: Updates from drivers/ide work Drag pata_hpt37x kicking and screaming in the direction of drivers/ide/pci/hpt366.c and all the work that Sergei has been doing there. Plenty left to be done but this is a good snapshot for folks to work on and to review Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit ec04b075843d12b5115267415d0426b48e672136 Author: Tejun Heo Date: Fri Mar 9 19:45:58 2007 +0900 iomap: implement pcim_iounmap_regions() Implement pcim_iounmap_regions() - the opposite of pcim_iomap_regions(). Signed-off-by: Tejun heo Signed-off-by: Jeff Garzik commit a73984a0d5664fa1bfdd9f0a475b8d74af7f44a6 Author: Jeff Garzik Date: Fri Mar 9 08:37:46 2007 -0500 [libata] More PATA driver ->cable_detect support Roll-up of ->cable_detect feature addition patches, authored and signed-off-by Alan Cox. Signed-off-by: Jeff Garzik commit fecfda5d88dcc3775f72d6f3a55d11b77c67f878 Author: Alan Cox Date: Thu Mar 8 19:34:28 2007 +0000 pata_hpt366: support ->cable_detect Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 6bfed3fb035b4eff59160c2da5e3fbb69d6b0531 Author: Alan Cox Date: Thu Mar 8 19:33:29 2007 +0000 pata_efar: support ->cable_detect Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 847086069cdb5eac14f70cd0aea8545d499c80b4 Author: Alan Cox Date: Thu Mar 8 19:27:31 2007 +0000 pata_atiixp: support ->cable_detect Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit d36a76482c19c186aa638446def39ec643993955 Author: Alan Cox Date: Thu Mar 8 22:56:07 2007 +0000 pata_radisys: support cable_detect Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 745975c052cc8e808860c8634a2efdcc2da6dd01 Author: Alan Cox Date: Thu Mar 8 22:58:03 2007 +0000 pata_sc1200: restore cable type Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 2a25dfe4f43b2199376424dce67ed11e3e276467 Author: Alan Cox Date: Thu Mar 8 22:57:03 2007 +0000 pata_rz1000: support cable_detect Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 570cb62deef574db10bcf1b4e9a8a0cbf7b101ee Author: Alan Cox Date: Thu Mar 8 22:54:04 2007 +0000 pata_platform: Add cable_detect method Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit e2a9752a21df4407a2094fb3345878d18a6c6d68 Author: Alan Cox Date: Thu Mar 8 23:06:47 2007 +0000 sata_promise: Switch to cable method, clean up some bits as a result Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 2e41e8e67af4cb0917793922fc8f55d3eeb6fa43 Author: Alan Cox Date: Thu Mar 8 23:19:19 2007 +0000 libata-core: fix comments on cable type Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 942d09470c3fb3254905c75f0cc9cd2d0dff24dd Author: Alan Cox Date: Thu Mar 8 23:24:49 2007 +0000 libata: Restore Kconfig updated experimental levels and correct Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 7938a72db4d82f3caf3ae22aafa1ea18793a3149 Author: Alan Cox Date: Wed Mar 7 16:43:29 2007 +0000 pata_cmd640: Multiple updates Fix suspend/resume support Write 0x5B to 0 not 0x5C The former is important as we must kill the FIFO on a resume Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 97cb81c335565f3ce27cd26d71480dff0211797a Author: Alan Cox Date: Wed Mar 7 16:56:54 2007 +0000 pata_via: Use cable_detect method We end up shifting a few bits of logic around in this driver but the basic change is the same. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 2e413f510f1b77846e0e7728b991849e697d7f8b Author: Alan Cox Date: Wed Mar 7 16:54:24 2007 +0000 pata_sis: Clean up using cable_detect methods This changeset revolves around the fact that all the SiS controllers have the same enable bits, but differing cable detection methods. Previously that meant each type had its own error_handler methods. Instead we can now implement different ->cable_detect methods and share a single error_handler which does the filtering by enable bits. In addition we had some auto const arrays that should be static const. I'm not sure if gcc already treats them intelligently but adding the static will make sure. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 307c6054ad67428ad4e2bd4e5faae54fc4f8bcd2 Author: Alan Cox Date: Wed Mar 7 16:48:09 2007 +0000 pata_marvell: Cable and reset fixes There are two changes here. Firstly we switch to a cable detect method, secondly the old code forgot to call ata_std_prereset() but somehow managed to work anyway. Fix the missing call. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit 5816fbbf22470b687964462e6c1f18165b291f22 Author: Alan Cox Date: Wed Mar 7 16:46:20 2007 +0000 pata_it8213: Cable detect Another not-quite PIIX, another cable type conversion Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit a0fcdc0259e98d1c16d96baea9ba8a8603e41791 Author: Jeff Garzik Date: Fri Mar 9 07:24:15 2007 -0500 [libata] Update several PATA drivers for new ->cable_detect hook All patches authored and signed-off-by Alan Cox, sent on Mar 7, 2007. I merely combined them all into a single patch. Signed-off-by: Jeff Garzik commit 6a3d586d8e8a50e4cfd7f8c36d82a53c5614e05b Author: Morrison, Tom Date: Tue Mar 6 02:38:10 2007 -0800 Support for Marvell 7042 Chip Added Support for Marvell 7042 Chip - 7042 has same capabilities & behavior as 6042. Signed-off-by: Thomas A. Morrison Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit b2248dac07cf057e4af3ec970e7d36a09f44fac8 Author: Alan Cox Date: Tue Mar 6 02:38:11 2007 -0800 pata_cmd640: CMD640 PCI support Support for the PCI CMD640 (not VLB) Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit be0d18dff5fae83845801929f297977c10ab99ad Author: Alan Cox Date: Tue Mar 6 02:37:56 2007 -0800 libata: cable detection fixes 2.6.21-rc has horrible problems with libata and PATA cable types (and thus speeds). This occurs because Tejun fixed a pile of other bugs and we now do cable detect enforcement for drive side detection properly. Unfortunately we don't do the process around cable detection right. Tejun identified the problem and pointed to the right Annex in the spec, this patch implements the rest of the needed changes. We add a ->cable_detect() method called after the identify sequence which allows a host to do host side detection at this point should it wish, or to modify the results of the drive side identify. This separate ->cable_detect method also cleans up a lot of code because many drivers have their own error_handler methods which really just set the cable type. If there is no ->cable_detect method the cable type is left alone so a driver setting it earlier (eg because it has the SATA flags set or because it uses the old error_handler approach) will still do the right thing (or at least the same thing) as before. This patch simply adds the cable_detect method and helpers it doesn't use them but other follow up patches will (ie Adrian please don't submit patches to unexport them ;)) Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 5aea408df5ae459cb29b91d45fa0f8bc1bee924e Author: Dmitriy Monakhov Date: Tue Mar 6 02:37:54 2007 -0800 libata: handle ata_pci_device_do_resume() failure while resuming Since commit:553c4aa630af7bc885e056d0436e4eb7f238579b ata_pci_device_do_resume() can return error code, all callers was updated except this one. Signed-off-by: Monakhov Dmitriy Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 8343f88999bfcf4c4169e964c8a5c040f40c5a77 Author: Robert Hancock Date: Tue Mar 6 02:37:51 2007 -0800 libata: warn if speed limited due to 40-wire cable Warn the user if a drive's transfer rate is limited because of a 40-wire cable detection. Signed-off-by: Robert Hancock Cc: Jeff Garzik Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit f0ef88ed413232daebfe675c7b1bbf100d604d4b Author: Mark Lord Date: Fri Feb 2 12:36:25 2007 -0500 RESEND: libata: check cdb len per dev instead of per host Resending, with s/printk/DPRINTK/ as pointed out by Alan. Fix libata to perform CDB len validation per device rather than per host. This way, validation still works when we have a mix of 12-byte and 16-byte devices on a common host interface. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik commit cd0d3bbcdd650651b7ccfaf55d107e3fc237d95a Author: Alan Date: Fri Mar 2 00:56:15 2007 +0000 libata: dev_config does not need ap and adev passing It used to be impossible to get from ata_device to ata_port but that is no longer true. Various methods have been cleaned up over time but dev_config still takes both and most users don't need both anyway. Tidy this one up Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik commit d88184fb2348a50f7c34f5d49a901c875b2e0114 Author: Jeff Garzik Date: Mon Feb 26 01:26:06 2007 -0500 [libata] sata_mv: clean up DMA boundary issues, turn on 64-bit DMA The chips covered by sata_mv have a 32-bit DMA boundary they must not cross, not a 64K boundary. We are merely limited to a 64K maximum segment size. Therefore, the DMA scatter/gather table fill code can be greatly simplified, and we need not cut in half the S/G table size as reported to the SCSI layer. Also, the driver forget to turn on 64-bit DMA at the PCI layer. All other data structures (both hardware and software) have been prepped for 64-bit PCI DMA. It was simply never turned on. let's see if it still works... Signed-off-by: Jeff Garzik commit 43727fbc753c63f9d2764c56467303698cc52c14 Author: Jeff Garzik Date: Sun Feb 25 16:50:52 2007 -0500 [libata] export sata_print_link_status() To be used in sata_mv's exception handling code, and overall is a generally useful function. Signed-off-by: Jeff Garzik commit 351772658a4d1acc0221a6e30676bb0594e74812 Author: Jeff Garzik Date: Sat Feb 24 21:26:42 2007 -0500 [libata] sata_mv: remove extra braces Signed-off-by: Jeff Garzik commit b4cf205846463a0a23a917bb18ad833bc9a8c0bb Author: Vitaly Wool Date: Fri Apr 27 14:42:09 2007 +0400 smc911x: fix compilation breakage wjen debug is on the patch below fixes compilation breakage of smc911x driver when ENABLE_SMC_DEBUG_PKTS equals to 1. Signed-off-by: Vitaly Wool Signed-off-by: Jeff Garzik commit 1c0d6dcf885badc72f72bfba9bc5b4f1469b8501 Author: Shani Moideen Date: Sat Apr 28 11:05:43 2007 -0400 [netdrvr] eexpress: minor corrections A few corrections related to time_after and time_before in drivers/net/eexpress.c as suggested by Marcin slusarz. Signed-off-by: Shani Moideen Signed-off-by: Jeff Garzik commit 693aa9470d8273a0ded8b211a8f5f7c0835adf30 Author: Mark Mason Date: Thu Apr 26 00:23:22 2007 -0700 add NAPI support to sb1250-mac.c Patch to add NAPI support to sb1250-mac.c (rev 2). This patch differs from the last in that the NAPI support isn't marked as experimental, nor is it configurable (ie. once applied - NAPI is enabled all the time). This was based on feedback from Ralf and others. Signed-off-by: Mark Mason Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 55e924cf5772cbcf00549e448be35b392ff3084c Author: Milind Arun Choudhary Date: Fri Apr 27 13:55:31 2007 -0700 ixgb: ROUND_UP macro cleanup in drivers/net/ixgb IXGB_ROUNDUP macro cleanup ,use ALIGN Signed-off-by: Milind Arun Choudhary Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit 9099cfb9170f352f08207dfa099717a904cff71f Author: Milind Arun Choudhary Date: Fri Apr 27 13:55:29 2007 -0700 e1000: ROUND_UP macro cleanup in drivers/net/e1000 E1000_ROUNDUP macro cleanup, use ALIGN Signed-off-by: Milind Arun Choudhary Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit abf17ffda7b7b6c83a29d7ccea91d46065c6ca3e Author: Krzysztof Halasa Date: Fri Apr 27 13:13:33 2007 +0200 Generic HDLC sparse annotations Sparse annotations, including two minor bugfixes. Signed-off-by: Krzysztof Halasa Signed-off-by: Jeff Garzik commit 27345bb684140f5f306963e0d6e25a60c7857dfe Author: Jesse Brandeburg Date: Thu Apr 26 09:43:20 2007 -0700 e100: Optionally use I/O mode only to access register space It appears that some systems still like e100 better if it uses I/O access mode. Setting the new parameter use_io=1 will cause all driver instances to use io mapping to access the register space on the e100 device. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit 948cd43fed7c7d919fa30e0609b2b5852c4503ef Author: Jesse Brandeburg Date: Thu Apr 26 09:43:11 2007 -0700 e100: allow bad MAC address when running with invalid eeprom csum Seved Torstendahl suggested to let the module parameter for invalid eeprom checksum control the valid mac address test. If this bypass happens we should print a different message, or at least one that is correct, maybe something like below Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit 1211bb6dcd935c48e864d4eecbf8a684e982419a Author: Thomas Klein Date: Thu Apr 26 11:56:43 2007 +0200 ehea: fix for dlpar support Certain resources may only be allocated when first logical port is available, and must be removed when last logical port has been removed. Signed-off-by: Thomas Klein Signed-off-by: Jeff Garzik commit d1dea38d54311f6b3dd37ce485e794bd133e3593 Author: Thomas Klein Date: Thu Apr 26 11:56:13 2007 +0200 ehea: fix for sysfs entries Create symbolic link from each logical port to ehea driver Signed-off-by: Thomas Klein Signed-off-by: Jeff Garzik commit 3b0dc5c1cfcdff806fe860a28d62fbf70af112e9 Author: Robert P. J. Day Date: Thu Apr 26 00:23:25 2007 -0700 3C509: Remove unnecessary include of Remove the apparently redundant include of . Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 78403a929a176813d7911bebccad303a1fdfe281 Author: Mithlesh Thukral Date: Fri Apr 20 07:57:26 2007 -0700 NetXen: Fix for vmalloc issues NetXen: Fix vmalloc errors on seen on some X86 high end machines. Signed-off by: Milan Bag Acked-by: Mithlesh Thukral Signed-off-by: Jeff Garzik commit 6d1495f29a59aad464024693a413a945e274c1ed Author: Mithlesh Thukral Date: Fri Apr 20 07:56:42 2007 -0700 NetXen: Fixes for Power PC architecture NetXen: Fix PPC architecture specific bugs Fixes some issues seen on Big endian machines. Signed-off by: Milan Bag Signed-off by: Mithlesh Thukral Signed-off-by: Jeff Garzik commit 6c80b18df3537d1221ab34555c150bccbfd90260 Author: Mithlesh Thukral Date: Fri Apr 20 07:55:26 2007 -0700 NetXen: Port swap feature for multi port cards NetXen: Port Swap feature This patch will allow a port numbers on the card to be swapped in host driver. This feature is applicable to cards having more than 1 port. Signed-off by: Milan Bag Signed-off by: Mithlesh Thukral Signed-off-by: Jeff Garzik commit 5d512f5594f9f4829b099c87f7bc6f683ef146ca Author: Mithlesh Thukral Date: Fri Apr 20 07:54:28 2007 -0700 NetXen: Removal of redundant macros NetXen: Remove 2 redundant macro definitions from header file. Signed-off by: Mithlesh Thukral Signed-off-by: Jeff Garzik commit 595e3fb8cf0c3c1c6d63dc3d7fd166e50bf150ff Author: Mithlesh Thukral Date: Fri Apr 20 07:53:52 2007 -0700 NetXen: Multi PCI support for Quad cards NetXen: Fix the multi PCI function for cards with more than 2 ports. This patch fixes the working of multi PCI capable driver on cards with more than 2 ports by adding the addresses for their rings and sizes. Signed-off by: Mithlesh Thukral Signed-off-by: Jeff Garzik commit 13ba9c7714cfa40821d29090442de14d01d6eb33 Author: Mithlesh Thukral Date: Fri Apr 20 07:53:05 2007 -0700 NetXen: Removal of redundant argument passing NetXen: Removal of redundant function call parameters and bug fixes. This patch will remove the redundant paramters which were being passed to many functions since now adapter->portnum can be used. Signed-off-by: Mithlesh Thukral Signed-off-by: Jeff Garzik commit 3176ff3ee71bddbd1d68e6a9e28dbcf0a2960c95 Author: Mithlesh Thukral Date: Fri Apr 20 07:52:37 2007 -0700 NetXen: Use multiple PCI functions NetXen: Make driver use multiple PCI functions. This patch will make NetXen driver work with multiple PCI functions. This will make the usage of memory resources as well as interrupts more independent among different functions which results in better throughput. This change has been done after the multiport support is added in firmware. Signed-off by: Mithlesh Thukral Signed-off-by: Jeff Garzik commit d52df4a35af569071fda3f4eb08e47cc7023f094 Author: Scott Feldman Date: Wed Nov 9 02:18:52 2005 -0500 [netdrvr e100] experiment with doing RX in a similar manner to eepro100 I was going to say that eepro100's speedo_rx_link() does the same DMA abuse as e100, but then I noticed one little detail: eepro100 sets both EL (end of list) and S (suspend) bits in the RFD as it chains it to the RFD list. e100 was only setting the EL bit. Hmmm, that's interesting. That means that if HW reads a RFD with the S-bit set, it'll process that RFD and then suspend the receive unit. The receive unit will resume when SW clears the S-bit. There is no need for SW to restart the receive unit. Which means a lot of the receive unit state tracking code in the driver goes away. So here's a patch against 2.6.14. (Sorry for inlining it; the mailer I'm using now will mess with the word wrap). I can't test this on XScale (unless someone has an e100 module for Gumstix :) . It should be doing exactly what eepro100 does with RFDs. I don't believe this change will introduce a performance hit because the S-bit and EL-bit go hand-in-hand meaning if we're going to suspend because of the S- bit, we're on the last resource anyway, so we'll have to wait for SW to replenish. (cherry picked from 29e79da9495261119e3b2e4e7c72507348e75976 commit) commit 2933d42cb7b0f14e0f83f6f231c966e97c79cdc9 Author: Larry Finger Date: Tue Apr 17 10:28:47 2007 -0500 [PATCH] ieee80211: add missing global needed by IEEE80211_DEBUG_XXXX Using any of the IEEE80211_DEBUG_XXXX macros in any ieee80211_crypt routine built as a module results in a missing global for 'ieee80211_debug_level'. The fix is to export the symbol in ieee80211. Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit 32684ec61678c61c6ceebfb29901b7723410ed83 Author: Randy Dunlap Date: Fri Apr 6 11:08:24 2007 -0700 mii: add kernel-doc notation Add kernel-doc notation to drivers/net/mii.c functions. Signed-off-by: Randy Dunlap Signed-off-by: Jeff Garzik commit 493a684ab8af36173e54ee9a102ed1768991f78a Author: Michael Ellerman Date: Tue Apr 17 13:12:55 2007 +1000 Fix sparse errors in drivers/net/ibmveth.c drivers/net/ibmveth.c:96:46: error: marked inline, but without a definition drivers/net/ibmveth.c:96: warning: 'ibmveth_rxq_harvest_buffer' declared inline after being called drivers/net/ibmveth.c:96: warning: previous declaration of 'ibmveth_rxq_harvest_buffer' was here Just let the compiler decide, as it happens gcc 4.~ inlines it anyway. drivers/net/ibmveth.c:957:71: warning: Using plain integer as NULL pointer drivers/net/ibmveth.c:964:85: warning: Using plain integer as NULL pointer Split the long lines as well, ugly, but < 80 columns. Signed-off-by: Michael Ellerman Signed-off-by: Jeff Garzik commit c2bcf00b437805b3c13374a4c4c8b6af646bdc2f Author: Kim Phillips Date: Fri Apr 13 01:26:36 2007 -0500 ucc_geth: version 1.1 Signed-off-by: Kim Phillips Signed-off-by: Jeff Garzik commit 3a8205ead4dc4b05fbc164eeb852b8f8921d11d3 Author: Nicu Ioan Petru Date: Fri Apr 13 01:26:29 2007 -0500 ucc_geth: fixes for ucc_geth_memclean The ucc_geth_memclean function can be called before the Tx BD rings, Rx BD rings and associated socket buffers are allocated (for example if ucc_fast_init fails). The current code doesn't check if p_tx_bd_ring[i] is null, generating a kernel panic when trying to free the associated socket buffers. The function can also fail when accessing the uninitialized list_head structures ugeth->group_hash_q and ugeth->ind_hash_q. In the current implementation the list heads are initialized only when maxGroupAddrInHash and maxIndAddrInHash are positive values, although I think it's better to always initialize them. Signed-off-by: Ionut Nicu Signed-off-by: Kim Phillips Signed-off-by: Jeff Garzik commit d5b9049df25f38b3eaf3af48d494b0747aae4349 Author: Michael Reiss Date: Fri Apr 13 01:26:19 2007 -0500 ucc_geth: Implement Transmit on Demand support Transmit on Demand: Fix spelling in config option, and make it actually enable TOD. Signed-off-by: Michael Reiss Signed-off-by: Michael Barkowski Signed-off-by: Kim Phillips Signed-off-by: Jeff Garzik commit 7563907eb8aad8f15b6a39fa6d863c091d83ef48 Author: Michael Barkowski Date: Fri Apr 13 01:26:15 2007 -0500 ucc_geth: Fix interrupt coalescing size and alignment The rx interrupt coalescing table alignment was "guessed" to be 4, but should be 64. The size should be 8 * number of queues + 4. Verified in the MPC8323E manual. Signed-off-by: Michael Barkowski Signed-off-by: Kim Phillips Signed-off-by: Jeff Garzik commit 702ff12ce7e9643084232a8d50b0b1eec26026ae Author: Michael Reiss Date: Fri Apr 13 01:26:11 2007 -0500 ucc_geth: NAPI-related bug fixes Based partly on the gianfar driver, this patch fixes several bugs which were causing NAPI to be completely unusable. * An IRQ is still needed in NAPI, to kick off NAPI task, and for Tx processing. Request the IRQ. * If rx_work_limit = 0 we are not complete. * While running Rx NAPI processing we must mask Rx events, including Rx busy. * ucc_geth_rx function does not need a lock. Could lead to deadlock in NAPI case. * There's no need to loop reading ucce multiple times in the ISR, so while adding the call to schedule NAPI which was not there, simplify the event processing into if-else format. * Rx Busy now kicks off NAPI processing, while still being counted as an error. Signed-off-by: Michael Reiss Signed-off-by: Michael Barkowski Signed-off-by: Kim Phillips Signed-off-by: Jeff Garzik commit 728de4c927a3544b6d3da331b634035d4c75ca17 Author: Kim Phillips Date: Fri Apr 13 01:26:03 2007 -0500 ucc_geth: migrate ucc_geth to phylib migrate ucc_geth to use the common phylib code. There are several side effects from doing this: o deprecate 'interface' property specification present in some old device tree source files in favour of a split 'max-speed' and 'interface-type' description to appropriately match definitions in include/linux/phy.h. Note that 'interface' property is still honoured if max-speed or interface-type are not present (backward compatible). o compile-time CONFIG_UGETH_HAS_GIGA is eliminated in favour of probe time speed derivation logic. o adjust_link streamlined to only operate on maccfg2 and upsmr.r10m, instead of reapplying static initial values related to the interface-type. o Addition of UEC MDIO of_platform driver requires platform code add 'mdio' type to id list prior to calling of_platform_bus_probe (separate patch). o ucc_struct_init introduced to reduce ucc_geth_startup complexity. Signed-off-by: Li Yang Signed-off-by: Kim Phillips Signed-off-by: Jeff Garzik commit a999589ccaae16472531e0616f23826ad097aa40 Author: Kim Phillips Date: Fri Apr 13 01:25:57 2007 -0500 phylib: add RGMII-ID interface mode definition The RGMII spec allows compliance for devices that implement an internal delay on TXC or RXC inside the transmitter. This patch adds an RGMII_ID definition to support RGMII-ID devices in the phylib. Signed-off-by: Kim Phillips Signed-off-by: Jeff Garzik commit aeb998c124786e8c7fbd6eab347230469e911872 Author: Larry Finger Date: Mon Apr 9 11:24:41 2007 -0500 [PATCH] ieee80211-crypt: Make some TKIP and CCMP error logging conditional There are messages arising from ieee80211_crypt that spam the logs of casual users. These are changed to be logged only if the user specifically requests the IEEE80211_DEBUG_DROP messages. In either case, the error/drop count is incremented. Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit 5d082fe85bf77f11aebb98d3bd29d66284d46851 Author: Daniel Drake Date: Sat Apr 7 16:00:43 2007 +0100 [PATCH] zd1211rw: Add ID for ZyXEL AG-220 Tested by Christoph Sager and Tomas Klas zd1211b chip 0586:3412 v4810 high 00-13-49 AL7230B_RF pa0 g---- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit ba528c4587005a004d136ed2e69705401ee4b878 Author: Daniel Drake Date: Sat Apr 7 16:00:30 2007 +0100 [PATCH] zd1211rw: Add AL7230B RF support for ZD1211B This patch adds support for some new ZD1211B devices which ship with the AL7230B RF. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 72018b223dcacb631f140d4c8fada3790bd1b03c Author: Daniel Drake Date: Sat Apr 7 16:00:15 2007 +0100 [PATCH] zd1211rw: rework band edge patching This change allows RF drivers to provide their own 6M band edge patching implementation, while providing a generic implementation shared by most currently supported RF's. The upcoming ZD1211B/AL7230B code will use this to define its own patching function, which is different from the other RF configurations. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit dc536a706951e23d3c81d0aa81414dc9f2c34ce5 Author: Daniel Drake Date: Tue Apr 3 23:17:10 2007 +0100 [PATCH] zd1211rw: Remove invalid CR write during ZD1211 phy reset The vendor driver only does the CR123 write for non-USB devices (which don't exist on the consumer market) Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 25ac6c2627acd68bd50307e61a3b581b2f82552e Author: Marcin Juszkiewicz Date: Fri Mar 30 15:34:14 2007 +0200 [PATCH] hostap_cs: support ADLINK 345 CF Add ADLINK 345 CF card into hostap_cs product info: "ADLINK 345 CF" manfid 0xd601, 0x0005 Signed-off-by: Marcin Juszkiewicz Signed-off-by: John W. Linville commit 6693228da9cc8cd6a8ede97989cf2e46b99d6934 Author: Pavel Roskin Date: Wed Mar 28 17:08:51 2007 -0400 [PATCH] Remove comment about IEEE80211_RADIOTAP_FCS IEEE80211_RADIOTAP_FCS is obsolete and should not be used. It's no longer defined. Remove it from the comment too. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 3a5c393e6f3e9f537e790016f3214447f2b50e51 Author: Larry Finger Date: Wed Mar 28 11:07:49 2007 -0500 [PATCH] bcm43xx: Change initialization for 2050 radios This patch implements the changes in the specifications for 2050radio_init that were recently posted. Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit b1fc1fa9b36512e928df95aead1a85381380ad1f Author: Adrian Bunk Date: Sat Mar 31 22:55:40 2007 +0200 make drivers/net/qla3xxx.c:PHY_DEVICES[] static On Fri, Mar 30, 2007 at 01:05:59AM -0700, Andrew Morton wrote: >... > Changes since 2.6.21-rc5-mm2: >... > git-netdev-all.patch >... > git trees >... This patch makes the needlessly global PHY_DEVICES[] static. Signed-off-by: Adrian Bunk Signed-off-by: Jeff Garzik commit 3ef34b807d35f81ee686e90e28084ee572a66e01 Author: Ramkrishna Vepa Date: Fri Mar 30 18:42:27 2007 -0700 S2io: Change of driver maintainers - Changed the maintainers for the S2io driver. Signed-off-by: Ramkrishna Vepa Signed-off-by: Jeff Garzik commit d7ef45b04905468b08e36b43dbce5b1de496e682 Author: Shani Date: Wed Mar 28 10:44:31 2007 +0530 Patch:replace with time_after in drivers/net/eexpress.c Replacing with time_after in drivers/net/eexpress.c Applies and compiles clean on latest tree.Not tested. Signed-off-by: Shani Moideen Signed-off-by: Jeff Garzik commit acbddb591ba76bb20204fd6a407cb87d3f5f751e Author: Jan-Bernd Themann Date: Fri Mar 23 17:18:53 2007 +0100 ehea: removing unused functionality This patch includes: - removal of unused fields in structs - ethtool statistics cleanup - removes unsed functionality from send path Signed-off-by: Jan-Bernd Themann Signed-off-by: Jeff Garzik commit 144213d71ce8b2a1e0740dd25808143e9ace655a Author: Gabriel Paubert Date: Fri Mar 23 12:07:26 2007 -0700 mv643xx_eth: make eth_port_uc_addr_{get,set}() calls symmetric There is no good reason for the asymmetry in the parameters of eth_port_uc_addr_get() and eth_port_uc_addr_set(). Make them symmetric. Remove some gratuitous block comments while we're here. Signed-off-by: Gabriel Paubert Signed-off-by: Dale Farnsworth Signed-off-by: Jeff Garzik commit 201f27e625cb2fe150e92d81d9725fe28c279792 Author: Ron Mercer Date: Mon Mar 26 13:43:03 2007 -0700 qla3xxx: Bumping driver version number to v2.03.00-k4 Signed-off-by: Ron Mercer Signed-off-by: Jeff Garzik commit 3efedf2e5b814f3edd99e4f4ca47a604871ebe0e Author: Ron Mercer Date: Mon Mar 26 12:43:52 2007 -0700 qla3xxx: Adding support for the Agere PHY (ET1011C) This PHY support patch was written by Benjamin Li. Signed-off-by: Benjamin Li Signed-off-by: Ron Mercer Signed-off-by: Jeff Garzik commit ec8263839aa8bc6eeee608a045e8f51738d7e436 Author: Ron Mercer Date: Mon Mar 26 13:43:01 2007 -0700 qla3xxx: Add ethtool get_pauseparam for improved bonding support. Signed-off-by: Ron Mercer Signed-off-by: Jeff Garzik commit f930e46a85a61895ef5c7c56c66c28272bec1ae1 Author: Robert P. J. Day Date: Mon Mar 26 21:47:24 2007 -0800 remove unused header file: drivers/net/skfp/h/lnkstat.h Signed-off-by: Robert P. J. Day Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 939456ac51dd0b6b2b5653823b5655a790e72769 Author: Folkert van Heusden Date: Mon Mar 26 21:47:23 2007 -0800 baycom_ser_fdx: also allow i/o ports >= 0x1000 and enhanced failure logging The baycom_ser_fdx driver did not allow i/o ports >= 0x1000. Now that there are pci cards (with rs232 ports) which use for example 0xb800 this limit should not exists. Also, for non kernel coders find the cause of problems was challenging so I added extra logging. Signed-off-by: Folkert van Heusden Cc: Jeff Garzik Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 4eccccb36223be5271b550a9ba4722a1e72c9312 Author: Jean Delvare Date: Mon Mar 26 21:47:22 2007 -0800 strlcpy is smart enough strlcpy already accounts for the trailing zero in its length computation, so there is no need to substract one to the buffer size. Signed-off-by: Jean Delvare Cc: Jan-Bernd Themann Cc: Thomas Klein Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit a3fa3aba7a0e00aa1cbb13c9e9bed162fa057973 Author: Daniel Drake Date: Mon Mar 26 01:00:12 2007 +0100 [PATCH] zd1211rw: Add another ID for Linksys WUSBF54G Tested by TiCPU on irc zd1211 chip 13b1:001e v4802 high 00-14-bf AL2230_RF pa0 g--- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 44713b1da5a78d722dc0c03a87dc9ddf46668f80 Author: Daniel Drake Date: Mon Mar 26 00:59:47 2007 +0100 [PATCH] zd1211rw: Don't handle broken frames in monitor mode Using monitor mode, Johannes Berg observed out that lots of corrupted and otherwise invalid frames were being passed to the host. When in monitor mode we were disabling the hardware filtering here, but this is not how monitor mode should work. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit d2d7c052106ebee75952da4dd547f40a1673d9e0 Author: Daniel Drake Date: Mon Mar 26 00:59:18 2007 +0100 [PATCH] zd1211rw: Mark some data const This is a backport of Helge Deller's recent patch for wireless-dev.git Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 85d32e7b0ea53a11d2a4018d8ad2605052778df7 Author: Jouni Malinen Date: Sat Mar 24 17:15:30 2007 -0700 [PATCH] Update my email address from jkmaline@cc.hut.fi to j@w1.fi After 13 years of use, it looks like my email address is finally going to disappear. While this is likely to drop the amount of incoming spam greatly ;-), it may also affect more appropriate messages, so let's update my email address in various places. In addition, Host AP mailing list is subscribers-only and linux-wireless can also be used for discussing issues related to this driver which is now shown in MAINTAINERS. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit e542aa6bd50ba163253e60ba8e7e51c0d56162a7 Author: Jan-Bernd Themann Date: Thu Mar 22 17:50:24 2007 +0100 ehea: code cleanup This patch includes: - code cleanup related to resource management - extended error data gathering for resource management - removing trailing whitespaces Signed-off-by: Jan-Bernd Themann Signed-off-by: Jeff Garzik commit 1eef4e04c95fb52a1a3885c8f53a822206fc9aa5 Author: Jan-Bernd Themann Date: Thu Mar 22 17:49:42 2007 +0100 ehea: fix for dynamic lpar support The patch fixes bugs related to the probe / remove adapter functionality (handling of OFDT nodes) Signed-off-by: Jan-Bernd Themann Signed-off-by: Jeff Garzik commit d68300182828596016d7a6c0f23a912f07d9d0df Author: Deepak Saxena Date: Mon Mar 19 15:43:11 2007 -0700 Netpoll support for Sibyte MAC NETPOLL support for Sibyte MAC Signed-off-by: Manish Lachwani Signed-off-by: Deepak Saxena Signed-off-by: Jeff Garzik commit 0c61ed5fe2c36b502a716a2ea3dd24e846029287 Author: Ramkrishna Vepa Date: Fri Mar 9 18:28:32 2007 -0800 S2io: Remove unused variables - Remove unused variables from s2io_nic structure - Changed the memory failure printk messages to print only in debug mode - Updated the copyright messages (Resending; due to patch being corrupted) Signed-off-by: Santosh Rastapur Signed-off-by: Jeff Garzik commit 1e2b980fdfb6189baa779ba7de93f7dba70aa3fd Author: Ralf Baechle Date: Sun Mar 18 23:21:22 2007 +0000 MIPSnet: Modernize use platform_device API. Signed-off-by: Ralf Baechle Signed-off-by: Jeff Garzik commit a5f8f3b6c91b07ee9aa2445a92a1d6c00a71a3cd Author: Stephen Hemminger Date: Fri Mar 16 14:01:32 2007 -0700 skge: version 1.11 New version to track changes. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 3f0dec7f6069c308ee7110e29e2b2d63bc5baeea Author: Stephen Hemminger Date: Fri Mar 16 14:01:31 2007 -0700 skge: rearrange fields Do some minor rearrangement of data structures to try and optimize cache usage. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 992c9623b148ba939e9cdba0d668eedd3af1a5d2 Author: Stephen Hemminger Date: Fri Mar 16 14:01:30 2007 -0700 skge: transmit locking improvements Don't need to lock when processing transmit complete unless queue fills. Modeled after tg3. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 7e0038a414c38085dfbf352f21006bcca4cd308b Author: Stephen Hemminger Date: Fri Mar 16 14:01:29 2007 -0700 skge: ignore unused error interrupts The following hardware error bits only show up on Genesis chipset and are handled elsewhere, so they can be masked off. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit a0d69f229fedea3f2b510ee8ece985c974860d41 Author: Pavel Roskin Date: Sat Mar 10 22:54:22 2007 -0500 [PATCH] sparse-annotate radiotap header Document that all fields must be little endian. Use annotated types even in the comments. Consistently use shorter type names (u8, s8). Realign the comments. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit d2f11e0991e3794f11d851b7ee0d98e064b10333 Author: Tony Breeds Date: Fri Mar 9 13:11:46 2007 +1100 [PATCH] libertas: use standard kernel macros Cleanup drivers/net/wireless/libertas/debugfs.c to use standard kernel macros and functions. Signed-off-by: Tony Breeds Signed-off-by: John W. Linville commit af5b5c9aa92ced95fca509e775aec90933f8959d Author: Michal Schmidt Date: Fri Mar 16 12:44:40 2007 +0100 [PATCH] airo: Make /sys/bus/pci/drivers/airo/{,un}bind work The way airo.c keeps track of all its devices is complicated and buggy as well (del_airo_dev forgets to free the memory add_airo_dev allocates). It's cleaner to use the standard list primitives. While we're at it, it's not necessary to put PCI cards in the list, because the kernel already keeps track of them. We can take advantage of it and use the .remove callback as it was meant to be. This makes /sys/bus/pci/drivers/airo/{,un}bind work. Signed-off-by: Michal Schmidt Signed-off-by: John W. Linville commit fd48f8d3a3e147e666d838194d5f9de64403ecb1 Author: Michal Schmidt Date: Fri Mar 16 12:40:00 2007 +0100 [PATCH] airo: Don't check for NULL before kfree() It's unnecessary to check for NULL before calling kfree(). Signed-off-by: Michal Schmidt Signed-off-by: John W. Linville commit aa93c85d09295dcb269fc1a0690d9ddfb58f46cc Author: Larry Finger Date: Wed Mar 14 15:06:22 2007 -0500 [PATCH] bcm43xx:Eliminate some 'G Mode Enable' magic numbers In code manipulating the TM State Low register of 802.11 cores, two different magic numbers are used to reference the 'G Mode Enable' bit. One of these, 0x20000000, is clear, but the other, (0x800 << 18), is not. This patch replaces both types with a defined constant. In addition, two bits in the TM State High registers are given definitions to help in following the code. Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit aec91028db71cae7efa1101cf2e38c407096f023 Author: Daniel Drake Date: Sun Mar 11 19:54:39 2007 +0000 [PATCH] zd1211rw: More device IDs ASUS A9Rp Tested by Serge zd1211b chip 0b05:171b v4802 high 00-17-31 AL2230_RF pa0 g-- ZyXEL G-202 Tested by Marcus D. Hanwell zd1211b chip 0586:3410 v4810 high 00-13-49 AL2230_RF pa0 g--- US Robotics USR805423 Tested by Pascal S. de Kloe FCC ID: RAXWN4501H zd1211b chip 0baf:0121 v4810 high 00-14-c1 AL2230_RF pa0 g--N Julien Pinon reports this also comes in AL2230S form Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit f2a81a161d5089fe838ec6c4c7b6357f25aeacbe Author: Daniel Drake Date: Sun Mar 11 19:54:28 2007 +0000 [PATCH] zd1211rw: Add AL2230S RF support ZD1211 appears to be back in production: a number of new devices have been appearing! Some of them are using new radios. This patch adds support for the next generation AL2230 RF chip which has been spotted in a few new devices. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 832855dc5a9beb78644d6163f94eccf2094bf30d Author: Daniel Drake Date: Sun Mar 11 19:53:40 2007 +0000 [PATCH] zd1211rw: Use compare_ether_addr() Suggested by Maxime Austruy, based on mac80211 changes from Stephen Hemminger Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 6bab99be917054736097b758489a9ea27fd44245 Author: Valerie Henson Date: Mon Mar 12 02:31:34 2007 -0700 Rev tulip version Rev tulip version... things have changed since 2002! Signed-off-by: Valerie Henson Cc: Jeff Garzik Signed-off-by: Jeff Garzik commit 1ddb98618d5b797cb5300f093c1df1f38f79d9ba Author: Valerie Henson Date: Mon Mar 12 02:31:33 2007 -0700 Fix tulip SytemError typo Fix an annoying typo - SytemError -> SystemError Signed-off-by: Valerie Henson Cc: Jeff Garzik Signed-off-by: Jeff Garzik commit eb117b1786804f2e128b871879ffe8f6a2701378 Author: Thibaut VARENE Date: Mon Mar 12 02:31:30 2007 -0700 TULIP: Natsemi dp83840a PHY fix Fix a problem with Tulip 21142 HP branded PCI cards (PN#: B5509-66001), which feature a NatSemi DP83840A PHY. Without that patch, it is impossible to properly initialize the card's PHY, and it's thus impossible to monitor/configure it. [VAL: I'm happy with the 1.5 ms max delay; it doesn't seem excessive.] Signed-off-by: Thibaut VARENE Cc: Jeff Garzik Acked-by: Grant Grundler Signed-off-by: Valerie Henson Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit b3bff39a2b3574542f5007e19038393dfdd64c85 Author: Valerie Henson Date: Mon Mar 12 02:31:29 2007 -0700 TULIP: Fix for 64-bit MIPS From: Jim Gifford , Grant Grundler , Peter Horton With Grant's help I was able to get the tulip driver to work with 64 bit MIPS. [VAL: I'm happy with the 1.5 ms max delay; it doesn't seem excessive.] Signed-off-by: Valerie Henson Signed-off-by: Andrew Morton Cc: Jeff Garzik Signed-off-by: Jeff Garzik commit c28896a4244e2fbe28c94e3a6048625f5c15cab4 Author: Atsushi Nemoto Date: Thu Mar 15 00:10:37 2007 +0900 tc35815: Zap changelog from source code Signed-off-by: Atsushi Nemoto Signed-off-by: Jeff Garzik commit 793bc0afbdccd97e66b2bdf43c2ce5653140ee5e Author: Atsushi Nemoto Date: Wed Mar 14 01:02:20 2007 +0900 tc35815: Fix an usage of streaming DMA API. The tc35815 driver lacks a call to pci_dma_sync_single_for_device() on receiving. Recent fix of MIPS dma_sync_single_for_cpu() reveal this bug. Signed-off-by: Atsushi Nemoto Signed-off-by: Jeff Garzik commit 1e56a4b4029dd1cabf73f28970b5c11bcbae6bb7 Author: Don Fry Date: Tue Mar 6 10:55:00 2007 -0800 pcnet32: change to use netdev_priv use netdev_priv() instead of dev->priv Signed-off-by: Thomas Bogendoerfer Signed-off-by: Don Fry Signed-off-by: Jeff Garzik commit 6ecb766710e5128e3b8f3c775f907dcb8fead8d1 Author: Don Fry Date: Tue Mar 6 10:45:23 2007 -0800 pcnet32: only allocate init_block dma consistent The patch below moves the init_block out of the private struct and only allocates init block with pci_alloc_consistent. This has two effects: 1. Performance increase for non cache coherent machines, because the CPU only data in the private struct are now cached 2. locks are working now for platforms, which need to have locks in cached memory Signed-off-by: Thomas Bogendoerfer Acked-by: Don Fry Signed-off-by: Jeff Garzik commit 1c8816c6fe375ed3a1e342fcf7f403cd4f0d5041 Author: Ahmed S. Darwish Date: Tue Mar 6 11:08:28 2007 -0800 ixgb: Use ARRAY_SIZE macro when appropriate. Signed-off-by: Ahmed S. Darwish Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit 363dc36733b06a94d3dce7c43b56c888f6d185de Author: Ramkrishna Vepa Date: Tue Mar 6 17:01:00 2007 -0800 S2IO: Save/Restore unused buffer mappings in 2/3 buffer mode - Save/Restore unused buffer mappings in 2/3 buffer mode to avoid frequent mapping - Save/Restore adapter reset count during adapter reset (Resending; forgot to cc netdev) Signed-off-by: Santosh Rastapur Signed-off-by: Jeff Garzik commit dcd01faf66372f1806451422ad67b06adcc68f82 Author: Auke Kok Date: Tue Mar 6 08:58:06 2007 -0800 e1000: list e1000-devel mailing list in MAINTAINERS Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit 1c7e5b125a0d31efd994326ed4c6d60c5760c43b Author: Yan Burman Date: Tue Mar 6 08:58:04 2007 -0800 e1000: Use kcalloc() Replace kmalloc+memsetout the driver. Slightly modified by Auke Kok. Signed-off-by: Yan Burman Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit c38db30b22913394f4634dc09f32d7838eb52ca1 Author: Ahmed S. Darwish Date: Tue Mar 6 08:58:02 2007 -0800 e1000: Use ARRAY_SIZE macro when appropriate A patch to use ARRAY_SIZE macro already defined in kernel.h. Signed-off-by: Ahmed S. Darwish Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit 024a0a3cfb4c98cb3c6c81ec70672c6a925cf164 Author: Shan Lu Date: Tue Mar 6 02:42:03 2007 -0800 network: add the missing phy_device speed information to phy_mii_ioctl Function `phy_mii_ioctl' returns physical device's information based on user requests. When requested to return the basic mode control register information (BMCR), the original implementation only returns the physical device's duplex information and forgets to return speed information, which should not be because BMCR register is used to hold both duplex and speed information. The patch checks the BMCR value against speed-related flags and fills the return structure's speed field accordingly. Signed-off-by: Shan Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 2b272221ad3d5c686f1d5ffb3b3c8a45120ad765 Author: Dmitriy Monakhov Date: Tue Mar 6 02:42:01 2007 -0800 sk98lin: handle pci_enable_device() return value in skge_resume() Signed-off-by: Monakhov Dmitriy Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit f1069046b4c7a5750611305433646c1bff3686fd Author: Maxim Levitsky Date: Tue Mar 6 02:41:54 2007 -0800 dmfe: add support for Wake on lan Add support for WOL on Magic Packet and on link change Signed-off-by: Maxim Levitsky Cc: Valerie Henson Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit bc8a8387ba57db2275e717c19838a8d5a404c270 Author: Maxim Levitsky Date: Tue Mar 6 02:41:53 2007 -0800 dmfe: add support for suspend/resume This adds support for suspend resume [akpm@linux-foundation.org: fix CONFIG_PM=n, coding style] Signed-off-by: Maxim Levitsky Cc: Valerie Henson Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit b3df0da886ffdb3e70c3197f589e959e5f8c9c04 Author: Randy Dunlap Date: Tue Mar 6 02:41:48 2007 -0800 phy layer: add kernel-doc + DocBook Convert function documentation in drivers/net/phy/ to kernel-doc and add it to DocBook. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 56e1393f82349d8206fe7feb94db96b065c55e51 Author: Arjan van de Ven Date: Tue Mar 6 02:41:48 2007 -0800 user of the jiffies rounding code: e1000 Use the round_jiffies() function in e1000. These timers all were of the "about once a second" or "about once every X seconds" variety and several showed up in the "what wakes the cpu up" profiles that the tickless patches provide. Some timers are highly dynamic based on network load; but even on low activity systems they still show up so the rounding is done only in cases of low activity, allowing higher frequency timers in the high activity case. The various hardware watchdogs are an obvious case; they run every 2 seconds but aren't otherwise specific of exactly when they need to run. Signed-off-by: Arjan van de Ven Acked-by: Auke Kok Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit eea221ce48803a92e2319270b2b7b8e21cd470ca Author: Atsushi Nemoto Date: Sat Mar 3 23:54:59 2007 +0900 tc35815 driver update (take 2) Current tc35815 driver is very obsolete and less maintained for a long time. Replace it with a new driver based on one from CELF patch archive. Major advantages of CELF version (version 1.23, for kernel 2.6.10) are: * Independent of JMR3927. (Actually independent of MIPS, but AFAIK the chip is used only on MIPS platforms) * TX4938 support. * 64-bit proof. * Asynchronous and on-demand auto negotiation. * High performance on non-coherent architecture. * ethtool support. * Many bugfixes and cleanups. And improvoments since version 1.23 are: * TX4939 support. * NETPOLL support. * NAPI support. (disabled by default) * Reduce memcpy on receiving. * PM support. * Many cleanups and bugfixes. Signed-off-by: Atsushi Nemoto Signed-off-by: Jeff Garzik commit 3664006ab9d90caca5c195e11a2b2e400d454265 Author: Adrian Bunk Date: Mon Mar 5 02:49:27 2007 +0100 drivers/net/qla3xxx.c: make 2 functions static This patch makes two needlessly global functions static. Signed-off-by: Adrian Bunk Signed-off-by: Jeff Garzik commit 7b49034399bfbf4eaaa3e0447d325e8f3a132209 Author: Adrian Bunk Date: Mon Mar 5 02:49:25 2007 +0100 make drivers/net/s2io.c:vlan_strip_flag static This patch makes the needlessly global vlan_strip_flag static. Signed-off-by: Adrian Bunk Signed-off-by: Jeff Garzik commit 78ee5b3cc88297bb98843c24b231e99f3f2886a0 Author: Ladislav Michl Date: Wed Feb 28 01:18:35 2007 +0000 Add support for Seeq 8003 on Challenge S Mezz board. Thanks to Jö Fahlke for donating hardware. Signed-off-by: Ladislav Michl Forward porting of Ladis' 2.4 patch. Signed-off-by: Ralf Baechle Signed-off-by: Jeff Garzik commit 18604c54854549ee0ad65e27ca9cb91c96af784c Author: Jan-Bernd Themann Date: Wed Feb 28 18:34:10 2007 +0100 ehea: NAPI multi queue TX/RX path for SMP This patch provides a functionality that allows parallel RX processing on multiple RX queues by using dummy netdevices. Signed-off-by: Jan-Bernd Themann Signed-off-by: Jeff Garzik commit 1acf2318dd136edfbfa30f1f33b43f69f2e2ec6c Author: Jan-Bernd Themann Date: Wed Feb 28 18:34:02 2007 +0100 ehea: dynamic add / remove port This patch introduces functionality to dynamically add / remove ehea ports via an userspace DLPAR tool. It creates a subnode for each logical port in the sysfs. Signed-off-by: Jan-Bernd Themann Signed-off-by: Jeff Garzik commit 459e536b1ddfd217ec8a3437a3214968a98223c7 Author: Stephen Hemminger Date: Tue Feb 20 15:58:02 2007 -0800 chelsio: use const for virtual functions There are several uses of _ops structure in this driver that can be converted to const. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 4c247db114d95fb42528afe4c16db522dd050d7b Author: Stephen Hemminger Date: Tue Feb 20 15:58:01 2007 -0800 chelsio: use C99 style initialization Convert some initialized structures to C99 style. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 4d2b8f66b89dd74d76d2b40cb45dffaa5567bb8f Author: Stephen Hemminger Date: Tue Feb 20 15:58:00 2007 -0800 chelsio: remove unused code for 1G boards Some code for Chelsio 1G boards was put in the driver based on the vendor version (minus TOE). Well some of those board versions are only supported with TOE on the vendor driver, so additional dead code was added. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 0ae08183d89fa48c0e4be019f45b93ad638c3890 Author: Sam Ravnborg Date: Sat Mar 3 23:55:08 2007 -0600 [PATCH] bcm43xx: do not rebuild when kernel version changes In bcm43xx_ethtool, UTS_RELEASE is used. Replacing this with utsname()->release avoids rebuilding this module each time the kernel version changes. Signed-off-by: Sam Ravnborg Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit 99d3184ac7b4e91c88073f474ebe758540b3ad82 Author: Jouni Malinen Date: Tue Feb 27 19:46:46 2007 -0800 [PATCH] hostap: Add D-Link DWL-650 Rev. P1 product id It looks like some of the PC Card manfid/product strings were lost when Host AP driver was converted to use PCMCIA_DEVICE_* helpers. This patch adds back D-Link DWL-650 Rev. P1 using the same product ID string match as the pcmcia-cs/cardmgr configuration used before. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit 742e9910d68a338189de8b5be77ae1d141f3851f Author: Andrew Morton Date: Thu Mar 1 08:19:29 2007 -0500 [PATCH] ipw2200: fix ieee80211_get_geo typo testing much? Cc: Zhu Yi Signed-off-by: Andrew Morton Signed-off-by: John W. Linville commit b3f1b8cf11e412367a35045a60abbbd2ada5f75d Author: John W. Linville Date: Tue Feb 27 14:39:04 2007 -0500 [PATCH] libertas: fix build breakage from netdev class_device -> device Signed-off-by: John W. Linville commit ebb4e07e351e456216681f9bcf6bc4a5f0f7f55b Author: John W. Linville Date: Tue Feb 27 13:41:26 2007 -0500 [PATCH] wireless: remove obsolete text files Signed-off-by: John W. Linville commit 70a734391a7e12ae177f703b8caf37c7f2014682 Author: Larry Finger Date: Tue Feb 20 10:33:13 2007 -0600 [PATCH] bcm43xx: Update Documentation/bcm43xx.txt The in-kernel documentation of the bcm43xx driver is out of date. Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit c6710e50f6b976dead878e143c568d4f8b068c7c Author: Pavel Roskin Date: Sun Feb 18 20:44:06 2007 -0500 [PATCH] hostap: use offsetof() instead of own equivalent The original macros result in gcc 4.2 warning about "cast from pointer to integer of different size" on 64-bit systems. Use of offsetof() on fields in substructures is widespread throughout the kernel code and should work whether offsetof() is defined using __compiler_offsetof() or a cast. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 876c9d3aeb989cf1961f2c228d309ba5dcfb1172 Author: Marcelo Tosatti Date: Sat Feb 10 12:25:27 2007 -0200 [PATCH] Marvell Libertas 8388 802.11b/g USB driver Add the Marvell Libertas 8388 802.11 USB driver. Signed-off-by: Marcelo Tosatti Signed-off-by: John W. Linville commit 35c3404efa7407811b706453f83d39b2539dcbd0 Author: Ulrich Kunitz Date: Sun Feb 18 20:28:23 2007 +0000 [PATCH] zd1211rw: changed GFP_NOFS to GFP_KERNEL Michael Buesch commented that GFP_NOFS should not be used in a network driver. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 375dd24428cfe900f4ed95d301fe604f8e0d459b Author: Zhu Yi Date: Wed Feb 14 16:04:24 2007 +0800 [PATCH] ipw2200: add channels sysfs entry Add 'channels' sysfs entry for ipw2200. The entry exports channels information for the user space. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville commit dc5a144991ba803bc8afded105c9db1dea0e57ab Author: Neil Horman Date: Thu Apr 26 13:47:36 2007 -0400 sis900: Allocate rx replacement buffer before rx operation Just found a hole in my last patch. It was reported to me that shortly after we integrated this patch. The report was of an oops that took place inside of netif_rx when using the sis900 driver. Looking at my origional patch I noted that there was a spot between the new skb_alloc and the refill_rx_ring label where skb got reassigned to the pointer currently held in the rx_ring for the purposes of receiveing the frame. The result of this is however that the buffer that gets passed to netif_rx (if it is called), then gets placed right back into the rx_ring. So if you receive frames fast enough the skb being processed by the network stack can get corrupted. The reporter is testing out the fix I've written for this below (I'm not near my hardware at the moment to test myself), but I wanted to post it for review ASAP. I'll post test results when I hear them, but I think this is a pretty straightforward fix. It just uses a separate pointer to do the rx operation, so that we don't improperly reassign the pointer that we use to refill the rx ring. Signed-off-by: Neil Horman Signed-off-by: Jeff Garzik commit 1764f15016fea54db723a96234a82646dac9a036 Author: Dan Williams Date: Wed Apr 25 21:30:29 2007 -0400 usb-net/pegasus: simplify carrier detection Simplify pegasus carrier detection; rely only on the periodic MII polling. Reverts pieces of c43c49bd61fdb9bb085ddafcaadb17d06f95ec43. Signed-off-by: Dan Williams Signed-off-by: Jeff Garzik commit 28b57cddb3ed4f7999e4b76ef36ebaaf6e2e0c37 Author: Adrian Bunk Date: Fri Apr 27 01:48:01 2007 +0200 [MTD] [MAPS] drivers/mtd/maps/ck804xrom.c: convert pci_module_init() This patch converts the pci_module_init() usage to pci_register_driver(). It's currently #if 0'ed, but still not a bad idea to change it. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse commit b8b8fd2dc23725fba77f66b3fef11b11f983fc08 Author: David Howells Date: Fri Apr 27 15:31:24 2007 -0700 [NET]: Fix networking compilation errors Fix miscellaneous networking compilation errors. (*) Export ktime_add_ns() for modules. (*) wext_proc_init() should have an ANSI declaration. Signed-off-by: David Howells Signed-off-by: David S. Miller commit b1bdb691c3c38b4fbaf99fa8474f5cfa99b2d774 Author: David Howells Date: Fri Apr 27 15:28:45 2007 -0700 [AF_RXRPC/AFS]: Arch-specific fixes. Fixes for various arch compilation problems: (*) Missing module exports. (*) Variable name collision when rxkad and af_rxrpc both built in (rxrpc_debug). (*) Large constant representation problem (AFS_UUID_TO_UNIX_TIME). (*) Configuration dependencies. (*) printk() format warnings. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 47051a2152f8b2355ee70249a0faaf7b682e8ce5 Author: David Howells Date: Fri Apr 27 15:26:30 2007 -0700 [AFS]: Fix VLocation record update wakeup Fix the wakeup transitions after a VLocation record update completes one way or another. This builds on Dave Miller's partial fix. Also move wakeups outside the spinlocked sections. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 1a028e50729b85d0a038fad13daf0ee201a37454 Author: David S. Miller Date: Fri Apr 27 15:21:23 2007 -0700 [NET]: Revert sk_buff walker cleanups. This reverts eefa3906283a2b60a6d02a2cda593a7d7d7946c5 The simplification made in that change works with the assumption that the 'offset' parameter to these functions is always positive or zero, which is not true. It can be and often is negative in order to access SKB header values in front of skb->data. Signed-off-by: David S. Miller commit e1f2a094bdfa8e7ecc31f048e6c6dbea2b4f5f74 Author: Alexey Dobriyan Date: Fri Apr 27 15:19:27 2007 -0700 [SCSI] esp_scsi.c: Fix compilation. irqreturn.h for irqreturn_t and dma_addr_t being u128 warnings ;-) Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller commit 54d33c4c715b80cc022b8e4974a4de693c96fc99 Author: Mike Rapoport Date: Sun Apr 22 08:53:21 2007 +0300 [MTD] [NAND] CM-x270 MTD driver This patch provides MTD support for NAND flash devices on CM-x270 modules. Signed-off-by: Mike Rapoport Signed-off-by: David Woodhouse commit a7205b30106a2d4ee268132644cdb292da2d9b41 Author: Li Yang Date: Mon Apr 23 10:38:18 2007 -0700 USB: update MAINAINERS and CREDITS for Freescale USB driver Add MAINAINERS and CREDITS entry for Freescale Highspeed USB device driver. Signed-off-by: Li Yang Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit d2eef1fc99640f7de302dcdbcfbdfcec2eef13f6 Author: Li Yang Date: Mon Apr 23 10:37:36 2007 -0700 USB: update gadget files for fsl_usb2_udc driver Update gadget_chip.c, ether.c for newly added Freescale Highspeed USB device driver. Signed-off-by: Li Yang Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit b504882da539c17ce6fee9da2a97f2fafabd495d Author: Li Yang Date: Mon Apr 23 10:54:25 2007 -0700 USB: add Freescale high-speed USB SOC device controller driver Freescale high-speed USB SOC can be found on some Freescale processors among different architectures. It supports both host and device functions. This driver adds its device support for Linux USB Gadget layer. It is tested on MPC8349 and MPC8313, but should work on other platforms with minor tweaks. The driver passed USBCV 1.3 compliance tests. Note that this driver doesn't yet include OTG support. Signed-off-by: Li Yang Signed-off-by: Jiang Bo Signed-off-by: Bruce Schmid Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit 33f73e56198457c38789e08c47d2af47174c1d8f Author: Raphael Assenat Date: Tue Apr 17 13:09:18 2007 -0700 USB: quirk for broken suspend of IT8152F/G Here's a patch which adds my device to the list. This patch enables the broken suspend quirk for the PCI OHCI controller present in the IT8152F/G RISC-to-PCI Companion Chip. Signed-off-by: Raphael Assenat Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit 147c5a17338fc72a89452f0a6c14ae6fcf853919 Author: Eberhard Fahle Date: Thu Apr 5 11:13:21 2007 +0200 USB: iowarrior.c: timeouts too small in usb_control_msg calls The driver uses usb_control_msg() for exchanging data with the device. When the driver lived freeley _outside_ the kernel tree (pre 2.6.21) the timeouts for these calls where set to 5*HZ for reading, 1HZ for writing. (These timeouts seemed to work fine for all users of the driver, at least nobody complained in the last 2 years. The current code (2.6.21-rc5) removed the 'HZ' from the timeouts and left the driver with 5 jiffies for reading and 1 jiffy for writing. My new machine is fast, but not that fast. The patch also removes a useless debug statement, which was left over from testing a broken firmware version From: Eberhard Fahle Signed-off-by: Greg Kroah-Hartman commit a03c6facf84a51a04958d75ceef443108c44872d Author: Hans Engelen Date: Thu Apr 12 14:40:26 2007 +0200 USB: dell device id for option.c Signed-off-by: Greg Kroah-Hartman commit 862e9043776578d575494db79d9189795350ed67 Author: Phil Dibowitz Date: Sun Apr 15 23:42:40 2007 -0700 USB: Remove Huawei unusual_devs entry Per the Rui Santos and the hardware manufacturers, this actually inhibits useful parts of the hardware. The correct way to use this hardware is with the software at http://www.kanoistika.sk/bobovsky/archiv/umts/ and the manufacturers are also planning on including Linux drivers/material in future revisions. CC: Rui Santos CC: CC: CC: CC: Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman commit 92f5cae2e8914d2d762728dd7629e5121ba070f3 Author: Craig Shelley Date: Fri Apr 20 14:37:17 2007 +0100 USB: CP2101 New Device IDs Two new device IDs for CP2101 driver. Signed-off-by: Craig Shelley Signed-off-by: Greg Kroah-Hartman commit 79dcdbf67d8885375711c4f2261168fa87389a31 Author: Joey Goncalves Date: Fri Apr 20 11:05:54 2007 -0700 USB: add picdem device to ldusb Hi Greg: I have found that /drivers/usb/misc/ldusb.c works with the "PICDEM Full Speed USB" http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en021940 Signed-off-by: Joey S Goncalves Cc: Michael Hund Signed-off-by: Greg Kroah-Hartman commit 527660a819f513edc52fea6bcb4068ce1d4d966b Author: Oliver Neukum Date: Fri Apr 20 20:50:48 2007 +0200 usbfs micro optimitation the memory barrier is needed only with smp. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 1b101ceb9162086035ce0c9cbe83ca7b4845179a Author: David Brownell Date: Sun Apr 22 11:05:52 2007 -0700 USB: remove ancient/broken CRIS hcd Remove the old crisv10 HCD ... it can't have built for some time, doesn't even have a Kconfig entry, was the last driver not to have been converted to the "hcd" framework, and considering the usbcore changes since its last patch was merged, has just got to buggy as all get-out. I'm told Axis has a new driver, and will be submitting it soon. Signed-off-by: David Brownell Cc: Mikael Starvik Signed-off-by: Greg Kroah-Hartman commit b8d297c93a888fcd4d74ba0bbeeabe9b84caf514 Author: Erik Hovland Date: Mon Apr 23 10:50:15 2007 -0700 usb ethernet gadget, workaround network stack API glitch Another workaround for the glitch in the network layer, whereby one call ignores the (otherwise kernel-wide) convention that free() calls should not oops when passed nulls. This code already handles that API glitch in most other paths. From: Erik Hovland Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit 83f7d958eab2fbc6b159ee92bf1493924e1d0f72 Author: Alan Stern Date: Wed Apr 25 15:15:43 2007 -0400 USB: add "busnum" attribute for USB devices This patch (as903) adds a "busnum" sysfs attribute for USB devices. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 6a02c996bce297a782432e29c69268356e97fadd Author: Simon Arlott Date: Thu Apr 26 00:38:05 2007 -0700 USB: cxacru: ADSL state management The device has commands to start/stop the ADSL function, so this adds a sysfs attribute to allow it to be started/stopped/restarted. It also stops polling the device for status when the ADSL function is disabled. There are no problems with sending multiple start or stop commands, even with a fast loop of them the device still works. There is no need to protect the restart process from further user actions while it's waiting for 1.5s. Signed-off-by: Simon Arlott Cc: Duncan Sands Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 7d5e1dd40bd5ced457be178e4f0b1267a3df2142 Author: Simon Arlott Date: Thu Apr 26 00:38:04 2007 -0700 usbatm: Detect usb device shutdown and ignore failed urbs Detect usb device shutdown and ignore failed urbs. This happens when the driver is unloaded or the device is unplugged. I'm not sure what other urb statuses should be ignored, and the warning message doesn't need to be shown when the module is unloaded or the device is removed. Signed-off-by: Simon Arlott Cc: Duncan Sands Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 5c975acb99168827a8ce5453be0e44ad2e4b70b2 Author: S.Caglar Onur Date: Thu Apr 26 00:38:03 2007 -0700 USB: Remove duplicate define of OHCI_QUIRK_ZFMICRO Remove duplicate define of OHCI_QUIRK_ZFMICRO from ftdi-elan.c, its already defined in drivers/ush/host/ohci.c Signed-off-by: "S.Caglar Onur" Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 3f6e58467c635ebbb4a139cbe5e0c1f46792e18c Author: Leon Leong Date: Thu Apr 26 00:38:02 2007 -0700 USB: BandRich BandLuxe HSDPA Data Card Driver Add the detection for the BandRich BandLuxe C100/C100S/C120 HSDPA Data Card. With the vendor and product IDs are set properly, the data card can be detected and works fine. Signed-off-by: Leon Leong Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 178398dca00b276aaa1aded7cded89921f710365 Author: Wu, Bryan Date: Thu Apr 26 00:38:01 2007 -0700 USB gadget rndis: fix struct rndis_packet_msg_type unaligned bug skb_push function may return a pointer which is not aligned as required by struct rndis_packet_msg_type. Using attribute trick to fix this bug. Signed-off-by: Roy Huang Signed-off-by: Jie Zhang Signed-off-by: Bryan Wu Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 893a342a686e6ce36ef24d322f3f52420a041313 Author: Cyrill Gorcunov Date: Thu Apr 26 00:38:00 2007 -0700 USB Elan FTDI: check for driver registration status Add checking of driver registration status and release allocated resources if it failed. Signed-off-by: Cyrill Gorcunov Cc: Pete Zaitcev Cc: "Luiz Fernando N. Capitulino" Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit f094e4f358c2f9f0a46dc777f64ed7794f73d283 Author: Greg Kroah-Hartman Date: Thu Apr 26 00:12:01 2007 -0700 USB: sierra: add more checks on shutdown This should help with any potential NULL pointer usages as reported by a few users. Signed-off-by: Greg Kroah-Hartman commit 931384fb0ec99a7459b3052f5d4db15bcb5037ea Author: David Brownell Date: Tue Apr 17 13:06:29 2007 -0700 USB: add an ohci board-specific quirk Use the new ohci-pci quirk infrastructure to address the problem it was created to address: a quirk specific to the Portege 4000, in buzilla as http://bugzilla.kernel.org/show_bug.cgi?id=6723 Also fix a misuse of "__devinit" for the quirk functions. It must not be used without first ensuring that the references from the quirk tables are gone, and that the function using those quirk tables is also gone. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit 296c02429271e1b9525b52fed76daa3b1cafefc4 Author: David Brownell Date: Tue Apr 17 16:10:10 2007 -0700 USB: usbnet reports minidriver name through ethtool Update "usbnet" so that ethtool reports the name of the minidriver in use (e.g. asix, cdc_ether, dm9601, rndis_host) instead of "usbnet". This is a better match to how other network drivers work, resolving a minor open issue. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit ddda08624013e8435e9f7cfc34a35bd7b3520b6d Author: David Brownell Date: Tue Apr 17 17:53:20 2007 -0700 USB: rndis_host, various cleanups Cleanups to the rndis_host code, and a tweak that helps talking to PXA hardware. Mostly from Ole André Vadla Ravnås - Prevent SET_INTERFACE requests, they give PXA hardware bad indigestion - For paranoia, null a pointer after freeing its data - Wrap up ActiveSync oddities for RNDIS_QUERY in one routine - Use that wrapper when getting the Ethernet address - Whitespace fixes Plus add a comment noting the open issues about some RNDIS clients still needing TBD kinds of browbeating to accept non-jumbogram packets. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit aa2ce5ca6be480cb139e21258671c2c27826f8ff Author: David Brownell Date: Tue Apr 17 17:51:38 2007 -0700 USB: minor doc update Minor doc update to ... say where USB_DT_CS_* came from and update the definitions to match how they're derived there. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit 8c9862e512f59ae3f41f83c109be12f93e37bb2d Author: Alan Stern Date: Wed Apr 11 12:06:16 2007 -0400 USB: fix signed jiffies issue in autosuspend logic This patch (as897) changes the autosuspend timer code to use the standard types and macros in dealing with jiffies values. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit ecb658d387dc09f344b3d755e8674076072032c7 Author: Pete Zaitcev Date: Wed Apr 11 13:47:26 2007 -0700 usbmon: bus zero Add the "bus zero" feature to the usbmon. If a user process specifies bus with number zero, it receives events from all buses. This is useful when we wish to see initial enumeration when a bus is created, typically after a modprobe. Until now, an application had to loop until a new bus could be open, then start capturing on it. This procedure was cumbersome and could lose initial events. Also, often it's too bothersome to find exactly to which bus a specific device is attached. Paolo Albeni provided the original concept implementation. I added the handling of "bus->monitored" flag and generally fixed it up. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman commit 35d07fd58f47284adecf219d3b73e4ea197cf29f Author: Tony Lindgren Date: Sat Mar 31 18:15:43 2007 -0700 USB: Allow transfer_buffer with transfer_dma Some host controller drivers may need a PIO fallback when a DMA channel is temporarily unavailable. This patch provides an address that such drivers can use for PIO in those cases, and nulls that field out when no such address is available (highmem) which should help usbmon. Signed-off-by: Tony Lindgren Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit ebc3ac149bf3a20c235625f1c07e0f997b3e08ba Author: Oliver Neukum Date: Mon Apr 2 15:16:36 2007 +0200 USB: cleanup ofd adutux this driver does - ignore errors during open - submit a running urb - use down_interruptible not handling signals - GFP_KERNEL with a spinlock held Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 1941044aa9632aa8debbb94a3c8a5ed0ebddade8 Author: Alan Stern Date: Tue Mar 27 13:33:59 2007 -0400 USB: add "last_busy" field for use in autosuspend This patch (as877) adds a "last_busy" field to struct usb_device, for use by the autosuspend framework. Now if an autosuspend call comes at a time when the device isn't busy but hasn't yet been idle for long enough, the timer can be set to exactly the desired value. And we will be ready to handle things like HID drivers, which can't maintain a useful usage count and must rely on the time-of-last-use to decide when to autosuspend. The patch also makes some related minor improvements: Move the calls to the autosuspend condition-checking routine into usb_suspend_both(), which is the only place where it really matters. If the autosuspend timer is already running, don't stop and restart it. Replace immediate returns with gotos so that the optional debugging ouput won't be bypassed. If autoresume is disabled but the device is already awake, don't return an error for an autoresume call. Don't try to autoresume a device if it isn't suspended. (Yes, this undercuts the previous change -- so sue me.) Don't duplicate existing code in the autosuspend work routine. Fix the kerneldoc in usb_autopm_put_interface(): If an autoresume call fails, the usage counter is left unchanged. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 38c3cb5b677cf551cd93a494dc5c551271d90ac1 Author: Richard Knutsson Date: Mon Mar 26 22:00:28 2007 -0800 USB: whiteheat: Convert to generic boolean Signed-off-by: Richard Knutsson Acked-by: Stuart MacDonald Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 5ec1862e7b612d804ca10a0475dccf98c857efec Author: Oliver Neukum Date: Tue Mar 27 16:02:34 2007 +0200 USB: fix omninet write vs. close race omninet kills all URBs in close. However write() returns as soon as the URB has been submitted. Killing the last URB means a race that can lose that date written in the last call to write(). As a fix this is moved to shutdown(). Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 2f007de2f4296e4dafae6ab0b3cc1bc49443137a Author: Oliver Neukum Date: Thu Mar 29 10:45:17 2007 +0200 USB: fix error handling for mct_u232 we report errors to the caller. THis patch adds error handling to the driver. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 9306fff17d3852e088dfc512e6f6673f3d80e71e Author: Oliver Neukum Date: Thu Mar 29 11:23:54 2007 +0200 USB: fix error handling in kl5kusb - report errors - cleanup in error case - use of endianness macros Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 5a9f4e338179e75e7e9b56293acb17023e342d81 Author: Oliver Neukum Date: Fri Mar 30 13:11:00 2007 +0200 USB: fix catc error handling this driver ignores errors while starting the transmit queue. It will never be reported stopped as the completion handler won't run and it will never be started again as it will be considered started. This patch adds error handling. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 949ce47168a34618c3e24cb7dd4ab52233a4653c Author: Oliver Neukum Date: Fri Mar 30 10:52:16 2007 +0200 USB: cypress_cy7c63: race disconnect/sysfs read-write leading to following NULL pointer this driver sets intfdata to NULL while it still can be read and happily followed. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 4edf2c83637b9e9db771cc5629de036fe4488564 Author: Oliver Neukum Date: Mon Mar 26 18:12:44 2007 +0200 USB: fix ark3116 memory leak this driver has a memory leak in an error case. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 868e440d2f2b55f790d106100a46034b6aa12577 Author: Jelle Foks Date: Sun Mar 25 21:08:35 2007 -0400 USB: ftdi_sio: Add USB ID of ADSTech USBX-707 This patch adds the USB ID of the ADS Tech USBX-707 USB IR blaster (that comes with the ADS Tech PTV-305 grabber card), which has a ftdi232bm inside hooked up to a pic. With this it should be fairly straightforward to make at least lirc receiving work with this device. I will submit a patch to lirc for that as soon as I have one ready, I'm getting data with minicom with this patch, but need to figure out some more details such as best/correct baudrate. Signed-off-by: Jelle Foks Signed-off-by: Greg Kroah-Hartman commit 62127a585cc72509a0bfae502cc16f9935a4cda6 Author: Oliver Neukum Date: Fri Mar 23 14:30:16 2007 +0100 USB: fix race in ftdio_write this has the same race as the visor driver. The counter must be incremented under the lock it is checked under. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit b80349b17c6e1236a24616f71e59ed31279de25a Author: Oliver Neukum Date: Fri Mar 23 11:58:03 2007 +0100 USB: fix race in visor_write this fixes a small race in visor_write. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit b19d402a3450bb2597d0c1b54f6139462a5f35ae Author: Oliver Neukum Date: Mon Mar 26 16:55:16 2007 +0200 USB: aircable cleanup - proper endianness macros - scheduling in interrupt in error case Signed-off-by: Oliver Neukum Signed-off-by: Naranjo Manuel Francisco commit 9f8b17e643fe6aa505629658445849397bda4e4f Author: Kay Sievers Date: Tue Mar 13 15:59:31 2007 +0100 USB: make usbdevices export their device nodes instead of using a separate class o The "real" usb-devices export now a device node which can populate /dev/bus/usb. o The usb_device class is optional now and can be disabled in the kernel config. Major/minor of the "real" devices and class devices are the same. o The environment of the usb-device event contains DEVNUM and BUSNUM to help udev and get rid of the ugly udev rule we need for the class devices. o The usb-devices and usb-interfaces share the same bus, so I used the new "struct device_type" to let these devices identify themselves. This also removes the current logic of using a magic platform-pointer. The name of the device_type is also added to the environment which makes it easier to distinguish the different kinds of devices on the same subsystem. It looks like this: add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1 ACTION=add DEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2-1 SUBSYSTEM=usb SEQNUM=1533 MAJOR=189 MINOR=131 DEVTYPE=usb_device PRODUCT=46d/c03e/2000 TYPE=0/0/0 BUSNUM=002 DEVNUM=004 This udev rule works as a replacement for usb_device class devices: SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", \ NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0644" Updated patch, which needs the device_type patches in Greg's tree. I also got a bugzilla assigned for this. :) https://bugzilla.novell.com/show_bug.cgi?id=250659 Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 87840289637e9ea95118ebd76e2e335fdcddd725 Author: Randy Dunlap Date: Wed Mar 21 13:57:51 2007 -0700 USB: gadget-storage needs BLOCK With CONFIG_BLOCK=n, this build error happens: WARNING: "bdev_read_only" [drivers/usb/gadget/g_file_storage.ko] undefined! Signed-off-by: Randy Dunlap Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 2add5229d77a3de08015feef437653e02372162f Author: Alan Stern Date: Tue Mar 20 14:59:39 2007 -0400 USB: add power/level sysfs attribute This patch (as874) adds another piece to the user-visible part of the USB autosuspend interface. The new power/level sysfs attribute allows users to force the device on (with autosuspend off), force the device to sleep (with autoresume off), or return to normal automatic operation. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 13f6be01db9ada144f28241f939f4f3f8ec8e40b Author: Peter Stokes Date: Sat Mar 17 16:14:12 2007 +0200 USB: ati_remote2: Add channel support Add logical channel support for ATI Remote Wonder II The ATI Remote Wonder II can be configured with one of 16 unique logical channels. Allowing up to 16 remotes to be used independently within range of each other. This change adds functionality to configure the receiver and filter the input data to respond or exclude remotes configured with different logical channels. Signed-off-by: Peter Stokes Acked-by: Ville Syrjala Signed-off-by: Greg Kroah-Hartman commit ebcf3ede6d84bf8aeff4378035d3fb312ede8dc9 Author: Oliver Neukum Date: Tue Mar 20 13:54:05 2007 +0100 USB: sierra close race the sierra driver does not directly use usb_kill_urb(). It uses a wrapper. This wrapper means that callbacks which are running are not killed during close, resubmitting and illicitly pushing data into the tty layer. The whole purpose of usb_kill_urb() is subverted. The wrapper must be removed. The same problem as the option driver. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 7d28e74b97c8eb859fd9f5eb018bb1c75627bd55 Author: Oliver Neukum Date: Tue Mar 20 13:41:21 2007 +0100 USB: option close race the option driver does not directly use usb_kill_urb(). It uses a wrapper. This wrapper means that callbacks which are running are not killed during close, resubmitting and illicitly pushing data into the tty layer. The whole purpose of usb_kill_urb() is subverted. The wrapper must be removed. Signed-off-by: Oliver Neukum Signed-off-by: Matthias Urlichs Signed-off-by: Greg Kroah-Hartman commit 4f93b3e8212df43ff380e118aebb68f6d1e5b060 Author: Oliver Neukum Date: Tue Mar 20 13:15:05 2007 +0100 USB: omninet memory leak in error case memory allocated must be freed in the error case. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 0cef7727c953056bb3ef9852a79d9658a1d8df4e Author: Alan Stern Date: Mon Mar 19 15:33:11 2007 -0400 UHCI: Add some WARN_ON()s This patch (as872) adds some WARN_ON()s to various error checks which are never supposed to fail. Unsettlingly, one of them has shown up in a user's log! Maybe making the warning more visible and having the call-stack information available will help pinpoint the source of the problem. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 325b70c233396f0cfe15012682a5080bf8040901 Author: Oliver Neukum Date: Mon Mar 19 13:58:29 2007 +0100 USB: fix endianness in mos7720 there's code unclean w.r.t. endianness in the mos7720 driver. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 1b55fa2d430d6c708d3e5efbc6134be4a0c32b15 Author: Adrian Bunk Date: Mon Mar 19 10:26:32 2007 +0100 USB: gtco.c: fix a use-before-check NULL checks should be before the first dereference. Spotted by the Coverity checker. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman commit f81ee4d52880b08c213982df5041217212689960 Author: Oliver Neukum Date: Mon Mar 19 11:39:13 2007 +0100 USB: iowarrior.c: fix NULL dereference Am Montag, 19. 2007 10:25 schrieb Adrian Bunk: > The Coverity checker spotted the following NULL dereference: And this fixes an oops upon allocation failures. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 686149f488e1912f29bcfc593fad4c18f0f75397 Author: Randy Dunlap Date: Fri Mar 16 18:33:13 2007 -0700 USB: dm9601: fix sparse NULL warnings Fix sparse NULL warnings: drivers/usb/net/dm9601.c:88:23: warning: Using plain integer as NULL pointer drivers/usb/net/dm9601.c:174:22: warning: Using plain integer as NULL pointer Signed-off-by: Randy Dunlap Acked-by: Peter Korsgaard Signed-off-by: Greg Kroah-Hartman commit cb8eaa8b2b913387a9a1d3d8fe48edfc1595ba3e Author: Richard Knutsson Date: Sat Mar 17 01:35:53 2007 +0100 USB: io_edgeport: Convert to generic boolean Signed-off-by: Richard Knutsson Signed-off-by: Greg Kroah-Hartman commit 0de9a7024e7ae62512d080c7e2beb59d82958cd5 Author: Oliver Neukum Date: Fri Mar 16 20:28:28 2007 +0100 USB: overhaul of mos7840 driver This fixes: - breaking DMA rules about buffers - usage of _global_ variables to save a single device's attributes - racy access to urb->status - smp monotonity issue with statistics - use of one buffer for many simultaneous URBs - error handling introduced - several instances of following NULL pointers - use after free - unnecessary GFP_ATOMIC - GFP_KERNEL in interrupt - various cleanups - write room granularity issue that bit cdc-acm - race in shutdown Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 96c706ed1c46470598d785124b2a7fb233b27dab Author: Oliver Neukum Date: Thu Mar 15 15:27:17 2007 +0100 USB: io_edgeport race condition in counters io_edgeport is using a global variable without locking. This is _the_ classical race condition. This patch switches to atomic_t. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit e198a31489146bb723fef179e5d1d18c8225f246 Author: Alan Stern Date: Thu Mar 15 15:54:30 2007 -0400 EHCI: add delay to bus_resume before accessing ports This patch (as870) adds a delay to ehci-hcd's bus_resume routine. Apparently there are controllers and/or BIOSes out there which need such a delay to get the ports back into their correct state. This fixes Bugzilla #8190. Signed-off-by: Alan Stern Cc: David Brownell Signed-off-by: Greg Kroah-Hartman commit eaafbc3a8adab16babe2c20e54ad3ba40d1fbbc9 Author: Alan Stern Date: Tue Mar 13 16:39:15 2007 -0400 USB: Allow autosuspend delay to equal 0 This patch (as867) adds an entry for the new power/autosuspend attribute in Documentation/ABI/testing, and it changes the behavior of the delay value. Now a delay of 0 means to autosuspend as soon as possible, and negative values will prevent autosuspend. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 6b157c9bf3bace6eeb4a973da63923ef24995cce Author: Alan Stern Date: Tue Mar 13 16:37:30 2007 -0400 USB: separate autosuspend from external suspend This patch (as866) adds new entry points for external USB device suspend and resume requests, as opposed to internally-generated autosuspend or autoresume. It also changes the existing remote-wakeup code paths to use the new routines, since remote wakeup is not the same as autoresume. As part of the change, it turns out to be necessary to do remote wakeup of root hubs from a workqueue. We had been using khubd, but it does autoresume rather than an external resume. Using the ksuspend_usb_wq workqueue for this purpose seemed a logical choice. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 378465396e74aa6ef36271cecaea49eb742025a8 Author: Oliver Neukum Date: Wed Mar 14 15:23:56 2007 +0100 USB: help text for mos 7720 driver this driver's help text incorrectly claims to support only single port devices. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit fe4b65ec9127a336eeaa503f878062d9e6f44591 Author: Oliver Neukum Date: Wed Mar 14 15:22:25 2007 +0100 mos7720 update this driver has an interesting way of handling ENOMEM: complain and ignore. If you decide to live with allocation failures, you must 1. guard against URBs without corresponding buffers 2. complete allocation failures 3. always test entries for NULL before you follow the pointers This patch does so. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 7378c57a8d4cf36e2f2b389d96d0d85043bd1c17 Author: Christian Engelmayer Date: Mon Mar 12 09:08:36 2007 +0100 ehci-fsl: change SI_CTRL, PRI_CTRL register offsets according to errata Correct the offsets of the SI_CTRL, PRI_CTRL registers according to the Reference Manual errata sheet in order to prevent unwanted settings regarding burst transactions and priority states. Signed-off-by: Christian Engelmayer Signed-off-by: Greg Kroah-Hartman commit 1b42ae6d4355328dc4406b6f0188adcf8c566435 Author: Alan Stern Date: Tue Mar 13 11:10:52 2007 -0400 USB: fix race in HCD removal This patch (as865) fixes a race in the HCD removal code discovered by Milan Plzik. Arrival of an interrupt after the root hub was unregistered could cause the root-hub status timer to start up, even after it was supposed to have been shut down. The problem is fixed by moving the del_timer_sync() call to after the HCD's stop() method, at which time IRQ generation should be disabled. Cc: Milan Plzik Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 949be0f7be8de0c5a6a46626bd983f7a03a4b26e Author: Simon Arlott Date: Tue Mar 6 02:47:46 2007 -0800 USB: MAINTAINERS: cxacru I've acquired a second device for testing and plan to make some changes in the near future to export all the device stats to sysfs (based on my proposed patch to add them to the proc file ~2007-01-30). Signed-off-by: Simon Arlott Acked-by: Duncan Sands Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit fa70fe44aba95ce373d7bcd27df4a594b53dcbdc Author: Simon Arlott Date: Tue Mar 6 02:47:45 2007 -0800 USB: cxacru: export detailed device info through sysfs When the device is polled for status there is a lot of useful status information available that is ignored. This patch stores the device info array when the status is polled and adds sysfs files to the usb device to allow userspace to query it. Since the device updates its status internally once a second the poll time is changed to this, and round_jiffies_relative is used to avoid waking the cpu unnecessarily. Signed-off-by: Simon Arlott Cc: Duncan Sands Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit e9b8daf31b459acb440647a651b1bda3b30e6188 Author: Simon Arlott Date: Tue Mar 6 02:47:43 2007 -0800 usbatm: create sysfs link "device" from atm class device to usb interface There is currently no path from the ATM device in /sys to the USB device's interface that the driver is using; this patch creates a "device" symlink. It is then possible to get to the cxacru ADSL statistics (http://lkml.org/lkml/2007/2/23/328): /sys/class/atm/cxacru0/device $ ls *_rate *_margin *_attenuation downstream_attenuation downstream_snr_margin upstream_rate downstream_rate upstream_attenuation upstream_snr_margin If this link is not appropriate I'd have to create device files in /sys/class/atm/cxacru0 instead - which seems less appropriate since the ADSL statistics are for the USB device not ATM (which is running over the ADSL). [akpm@linux-foundation.org: cleanups] Signed-off-by: Simon Arlott Cc: Duncan Sands Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit c2cf3f6ec58cf1d9e2a92cd74506dcf2265b31a1 Author: Oliver Neukum Date: Tue Mar 6 16:21:22 2007 +0100 USB: unnecessary GFP_ATOMIC in mos7720 driver GFP_KERNEL will do. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 718efa64e30a5e9db0351d70c5a91969306a12d1 Author: Alan Stern Date: Fri Mar 9 15:41:13 2007 -0500 usbcore: move usb_autosuspend_work This patch (as864) moves the work routine for USB autosuspend from one source file to another. This permits the removal of one whole global symbol (!) and should smooth the way for more changes in the future. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 64e35d92367d8cd376946eb924838de1dd3287c7 Author: Pete Zaitcev Date: Thu Mar 8 20:02:26 2007 -0800 libusual: change block scope variable to function scope Someone changed the code to kthread and used his style instead of mine. The problem with the block variables is that they provoke shadowing, which is actually exactly what has happened in my other tree which has the class patch. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman commit 643616e6780b26dd8c9cea0b9344bb5d7aeae29d Author: Pete Zaitcev Date: Thu Mar 8 19:56:23 2007 -0800 ub: Bind to first endpoint, not to last The usb-storage switched to binding to first endpoint recently. Apparently, there are devices out there with extra endpoints. It is perfectly legal. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman commit c4cabd28c73116716dcfd0d5f91414b48c0cf5ce Author: Oliver Neukum Date: Tue Feb 27 15:28:55 2007 +0100 USB: cdc-acm: export parsed capabilities through sysfs this patch exports the attributes cdc-acm knows about a device through sysfs. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit bb74782e621e988555354abba03812982236a3af Author: Oliver Neukum Date: Tue Feb 27 11:30:24 2007 +0100 USB: additional structure from cdc spec this adds another structure for CDC devices to cdc.h. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit f1c9e30b5e4cdd8aae5f0ea87004b1b61ec41881 Author: Pete Zaitcev Date: Sat Feb 24 19:27:33 2007 -0800 usbmon: Extended text API This patch adds a new text API, codenamed '1u', which captures more URB fields than old '1t' interface did. Also the '1u' text API is compatible with the future "bus zero" extension. Signed-off-by: Pete Zaitcev Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman commit 2e85c91e2e0f4e58fb54354bb7f6cc7dd3466dd9 Author: Oliver Neukum Date: Mon Mar 5 15:11:14 2007 +0100 USB: kill BKL in usblcd this patch removes usage of BKL from usblcd, which got it from the old skeleton driver. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 5d9b89b33f3ed19479dc5240986b0fedda08b82c Author: Oliver Neukum Date: Thu Mar 1 23:07:32 2007 +0100 USB: kill BKL in skeleton driver Iet's kill BKL where we can. This is relative to the last patch to the skeleton driver. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit ba35e02bdcbd3a25238421a7e20efdb69436d3cf Author: Oliver Neukum Date: Thu Mar 1 14:31:02 2007 +0100 USB: fix skeleton driver compilation of the skeleton driver is currently broken. It doesn't compile. So while I am it: - fix typo - add comments to answer common questions - actually allow autosuspend in the driver struct - increase paralellism by restricting code under locks Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 08a78cbb410032a025e0323bcfb12b348fc7a389 Author: Milan Svoboda Date: Tue Feb 27 09:20:09 2007 +0000 USB: pxa2xx_udc: fix hardcoded irq number This patch changes last use of hardcoded number of irq to use platfrom_get_irq. Signed-off-by: Milan Svoboda Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit 34ebcd28235dc58633eeb8f7ca603838af23df0d Author: David Brownell Date: Sat Feb 24 12:23:52 2007 -0800 pxa2xx_udc: cleanups, use platform_get_irq Make the pxa2xx_udc driver fetch its IRQ from platform resources rather than using compile-time constants, so that it works properly on IXP4xx systems not just PXA21x/25x/26x. Other updates: - Do that using platform_get_irq() - Switch to platform_driver_probe() - Handle device_add() errors - Remove "function" sysfs attribute and its potential errors - Whitespace cleanups Signed-off-by: Milan Svoboda Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman commit c51e9749ab49c5fa6c9cc1cb96bb5f36a571f553 Author: Norihiko Tomiyama Date: Thu Apr 5 10:05:40 2007 +0900 Adding PID of SHARP S01SH for ipaq.c I write a patch adding support "SHARP EMONE(S01SH)" device for ipaq.c. EMONE is a PDA with built-in HSDPA function. From: Norihiko Tomiyama Signed-off-by: Greg Kroah-Hartman commit d455cf5d0db9e3eb1b204cd4a61d8c5ccfe4305f Author: Douglas Landgraf Date: Thu Apr 26 16:44:55 2007 -0300 V4L/DVB (5563): Radio-maestro.c Replace radio_ioctl to use video_ioctl2 Convert radio-maestro to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 6f66446c5465c0d29f8805ef47810f4a72842e5c Author: Douglas Landgraf Date: Thu Apr 26 10:42:12 2007 -0300 V4L/DVB (5562): Radio-gemtek-pci.c Replace gemtek_pci_ioctl to use video_ioctl2 Convert radio-gemtek-pci to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 018ba85bf6d77eefc67796d707c81e8531b74d2f Author: Hans Verkuil Date: Tue Apr 10 18:59:09 2007 -0300 V4L/DVB (5560): Ivtv: fix incorrect bitwise-and for command flags. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 86534e5e8eaeab2f5d2e92cff5e16980750095c6 Author: Marco Gittler Date: Mon Apr 23 17:52:58 2007 -0300 V4L/DVB (5558): Opera: use 7-bit i2c addresses - the tuner i2c addr now without define (as wanted). - now 7 bit addr are used (the power_ctrl fkt ist ok so, because this is a raw write) - the addr >> 1 , addr << 1 is ok so, i think beause the read write is now taken from the last bit. - now i have no datasheet for the device, all taken from usb-logs Signed-off-by: Marco Gittler Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 12df2f54e5522d89b3de6df62a800a0edeb10dcc Author: Trent Piepho Date: Wed Apr 25 00:20:13 2007 -0300 V4L/DVB (5557): Cafe_ccic: check return value of pci_enable_device Remove warnings Signed-off-by: Trent Piepho Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit e9bb9c643924aee08f2a6b353174361ab567e436 Author: Douglas Landgraf Date: Wed Apr 25 00:15:46 2007 -0300 V4L/DVB (5556): Radio-gemtek.c Replace gemtek_ioctl to use video_ioctl2 Convert radio-gemtek to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 385e8d8fc6a3b5d0a656d69fc11cc6e26d9512cf Author: Douglas Landgraf Date: Wed Apr 25 00:14:36 2007 -0300 V4L/DVB (5555): Radio-aimslab.c Replace rt_ioctl to use video_ioctl2 Convert radio-aimslab to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 2aa2342f2ba7caac0c5ed08331e883b7a72da2dd Author: Mauro Carvalho Chehab Date: Tue Apr 24 13:40:07 2007 -0300 V4L/DVB (5554): Fix: vidioc_g_parm were not zeroing the memory Signed-off-by: Mauro Carvalho Chehab commit 3f6892aca5d23730a747ce88eb0b6848e7a52a2f Author: Douglas Landgraf Date: Tue Apr 24 08:40:06 2007 -0300 V4L/DVB (5553): Replace typhoon_do_ioctl to use video_ioctl2 Convert radio-typhoon to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 74ea1c7a7e7b5dfe961f3ed08365f9deb899b163 Author: Alan Cox Date: Mon Apr 23 17:57:28 2007 -0300 V4L/DVB (5552): Plan-b: Switch to refcounting PCI API Signed-off-by: Alan Cox Signed-off-by: Mauro Carvalho Chehab commit 8ca966d7fdfa44f5ffbc2aac6b65e5aa5482123b Author: Alan Cox Date: Mon Apr 23 17:57:22 2007 -0300 V4L/DVB (5551): Plan-b: header change Signed-off-by: Alan Cox Signed-off-by: Mauro Carvalho Chehab commit c123b867713425f8db060b5e29201a1056c45d90 Author: Douglas Landgraf Date: Mon Apr 23 17:52:12 2007 -0300 V4L/DVB (5550): Radio-sf16fmi.c Replace fmi_do_ioctl to use video_ioctl2 Convert radio-sf16fmi to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 34ab962db8765a494072908e2dd5bf1478ac070d Author: Douglas Landgraf Date: Mon Apr 23 17:51:37 2007 -0300 V4L/DVB (5549): Radio-sf16fmr2.c Replace fmr2_do_ioctl to use video_ioctl2 Convert radio-sf16fmr2 to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 288bb0e79015d4121d4ccf77a4db2bde27a49492 Author: Luc Saillard Date: Sun Apr 22 23:55:10 2007 -0300 V4L/DVB (5548): Fix v4l2 buffer to the length Set the length of the v4l2 buffer to the length of the mapped memory. This should fix the problem with amsn. Signed-off-by: Mauro Carvalho Chehab commit 9ee6d78cd4112c0f5a257a01383c64dadbf66da9 Author: Luc Saillard Date: Sun Apr 22 23:54:36 2007 -0300 V4L/DVB (5547): Add ENUM_FRAMESIZES and ENUM_FRAMEINTERVALS ioctls This patch add support for the VIDIOC_ENUM_FRAMESIZES and VIDIOC_ENUM_FRAMEINTERVALS ioctl. * check if the maximum native framesize for raw mode is correct * raw mode framerates for all three chipset types Signed-off-by: Gregor Jasny Signed-off-by: Luc Saillard Signed-off-by: Mauro Carvalho Chehab commit 1de69238111a65283a4548d8fd4727397873a02f Author: Douglas Landgraf Date: Sun Apr 22 23:15:47 2007 -0300 V4L/DVB (5546): Radio-terratec.c Replace tt_do_ioctl to use video_ioctl2 Convert radio-terratec to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 2970c49270be2a62b1115f73bbfc0b8d3b3f48ba Author: Hartmut Birr Date: Sun Apr 22 06:57:26 2007 -0300 V4L/DVB (5545): Saa7146: Release capture buffers on device close If the video device (saa7146 on a FF card) was open for capturing, the close call didn't release the capture buffers. Signed-off-by: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit dc120b0734fcc6a4c42c73790a02f2fa74a7a326 Author: Hartmut Birr Date: Sat Apr 21 19:44:10 2007 -0300 V4L/DVB (5544): Budget-av: Make inversion setting configurable, add KNC ONE V1.0 card Make the inversion setting configurable. The KNC ONE V1.0 uses non inverted setting for the inversion and add the KNC ONE V1.0 card. Signed-off-by: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit aa323ac89c5724de89656fcf31590d19e74594ec Author: Hartmut Birr Date: Sat Apr 21 19:37:17 2007 -0300 V4L/DVB (5543): Tda10023: Add support for frontend TDA10023 Add support for the frontend TDA10023 and add cards that need the tda10023. Signed-off-by: Hartmut Birr Signed-off-by: Georg Acher Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit fd9c66e269a44bd3c6c615957c79b21f3dde69af Author: Hartmut Birr Date: Sat Apr 21 19:17:49 2007 -0300 V4L/DVB (5542): Budget-av: Remove polarity switching of the clock for DVB-C Remove the polarity switching of the clock for the DVB-C cards in budget-av.c. This hack is no longer necessary because the saa7146 can do the job to remove the additional FEC bytes from the TS. Signed-off-by: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit a1314b1ab61027ac803b0899ead1ce1229d46a37 Author: Douglas Landgraf Date: Fri Apr 20 18:23:38 2007 -0300 V4L/DVB (5541): Radio-zoltrix.c Replace zol_do_ioctl to use video_ioctl2 Convert radio-zoltrix to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit c5f822bf60b174c52d6d491d631d205384a2d1ca Author: Douglas Landgraf Date: Fri Apr 20 18:22:19 2007 -0300 V4L/DVB (5540): Radio-trust.c Replace tr_do_ioctl to use video_ioctl2 Convert radio-trust to use video ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 8b811cf0c618915b286b5f829a77349a200b814c Author: Douglas Landgraf Date: Fri Apr 20 06:37:36 2007 -0300 V4L/DVB (5539): Radio-rtrack2.c Added VIDIOC_[GS]_AUDIO and VIDIOC_[S|G]_INPUT Implements VIDIOC_[GS]_AUDIO and VIDIOC_[S|G]_INPUT on radio-rtrack2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit ec709bb801a98dcac0a95c060c431eda73e31587 Author: Thierry MERLE Date: Tue Apr 17 02:28:32 2007 -0300 V4L/DVB (5530): Usbvision: remove CustomDevice facility usbvision has a module parameter that ables the user to add a new USB entry at driver load. This functionality is useless by experience (adding statically the entry is easy). Furthermore, the USB_DEVICE(0xfff0, 0xfff0) USB entry caused usbvision_probe to be called for all unclaimed devices. Signed-off-by: Thierry MERLE Acked-by: Dwaine Garden Signed-off-by: Mauro Carvalho Chehab commit 25f30389dd84dcaf54d7d76fdd84e85bec206db6 Author: Douglas Landgraf Date: Thu Apr 19 16:42:25 2007 -0300 V4L/DVB (5534): Radio-rtrack2.c Replace rt_ioctl to use video_ioctl2 Convert radio-rtrack2 to use video_ioctl2 Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab commit 941491f3a52c34506137060716ce73e642ee326e Author: Marco Gittler Date: Thu Apr 19 11:26:47 2007 -0300 V4L/DVB (5532): Add support for Opera S1- DVB-USB This patch adds support for DVB-Opera S1 USB 2.0 BOX. Signed-off-by: Marco Gittler Signed-off-by: Mauro Carvalho Chehab commit 6284feafcf589103f4a85d98d305e7a9d98970d3 Author: Thierry MERLE Date: Mon Apr 16 17:47:08 2007 -0300 V4L/DVB (5529): Usbvision: remove not needed TUNER_SET_TYPE_ADDR call usbvision_i2c: remove TUNER_SET_TYPE_ADDR call in attach_inform since it is done by tuner_core. Acked-by: Dwaine Garden Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit bf8b20e190f9878978117e39e9a4e493a2e9d6bd Author: Thierry MERLE Date: Mon Apr 16 18:00:49 2007 -0300 V4L/DVB (5528): Usbvision: reverting some i2c cleanups usbvision-i2c.c: reverting some i2c cleanups in order to recover a safe state. Acked-by: Dwaine Garden Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit c42cabefce5a333d8158434f902117d8c8dfd3b0 Author: vignesh.babu@wipro.com Date: Mon Apr 16 10:34:33 2007 -0300 V4L/DVB (5526): Cx88-alsa.c: Use kzalloc Replacing kmalloc/memset combination with kzalloc. Signed-off-by: vignesh babu Signed-off-by: Mauro Carvalho Chehab commit 3153bd91bfe14b6b93aef5b6b7c9fc279eec60d9 Author: Trent Piepho Date: Sun Apr 15 10:09:56 2007 -0300 V4L/DVB (5525): Usbvision: fix confusion over 7-bit vs 8-bit TDDA9887 addresses The code was testing an 8-bit address against a 7-bit address. Will the confusion of the two never cease? Biggest flaw of the I2C protocol: the R/W bit is the LSB instead of the MSB. No one can ever agree if addresses are 7-bits and the R/W bit follows them, or if they are 8-bit and the R/W bit is OR-ed into the address byte. If the R/W bit was first, it wouldn't make any difference! Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 66ab6e023ebbb362b7cf42c8f56cc54a85fb7bb0 Author: Akinobu Mita Date: Sat Apr 14 10:24:15 2007 -0300 V4L/DVB (5513): Bt878: prevent probing wrong card entry I got strange message when I did modprobe bt878: bt878_probe: card id=[0x0],[ ] has DVB functions. The card_list array is terminated by sentinel entry. But this list is traversed by: for (i = 0, dvb_cards = card_list; i < ARRAY_SIZE(card_list); i++, dvb_cards++) { ... } in bt878_probe(). So this loop checks dummy sentinel entry, too. This patch removes unnecessary sentinel entry. Signed-off-by: Akinobu Mita Signed-off-by: Mauro Carvalho Chehab commit f2c340583c27f2f93ed1c720ae863ea601e1d74a Author: Thierry MERLE Date: Sun Apr 15 04:29:13 2007 -0300 V4L/DVB (5524): Usbvision: fix TDA9887 detection - Adding 0x86 as possible I2C addresses for TDA9887 to call TUNER_SET_TYPE_ADDR. Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit 672d013bed5e144ac641db343251e35d3212c9ad Author: Thierry MERLE Date: Sat Apr 14 17:53:55 2007 -0300 V4L/DVB (5523): Usbvision: fix a debug message in usb probe function - change a printk to PDEBUG when USB probe detects an unknown device. This will avoid a message log from usbvision when an unclaimed device is inserted. Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit 1ff16c2091a557f8080eb62c087465a87e4330e9 Author: Thierry MERLE Date: Sat Apr 14 16:23:49 2007 -0300 V4L/DVB (5522): Usbvision: i2c function cleanups usbvision-i2c function renamings, code cleanup Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit d3df9c4fa13db14cb9f6cd4cf31bd2a61c0e9911 Author: Mauro Carvalho Chehab Date: Sat Apr 14 16:19:13 2007 -0300 V4L/DVB (5521): Usb_get_dev were called twice. Removing the extra call. Signed-off-by: Mauro Carvalho Chehab commit 28aedb8fd95b9a11cb9dc75d3be1f30227cb4385 Author: Ville-Pekka Vainio Date: Sat Apr 14 15:38:23 2007 -0300 V4L/DVB (5520): Add support for the extra keys in the black Technotrend 1500 IR It has come to my knowledge that the Technotrend 1500 DVB cards have been sold bundled with at least two different kinds of remotes, a grey one and a black one. This patch adds support for the extra keys in the black remote to ir-keymaps.c. Signed-off-by: Ville-Pekka Vainio Signed-off-by: Mauro Carvalho Chehab commit f8a389db502f7b287903b209f925df5570ff5478 Author: Mauro Carvalho Chehab Date: Sat Apr 14 15:17:35 2007 -0300 V4L/DVB (5518): Fix a bug on device detection Thanks to: Thierry MERLE for pointing this Signed-off-by: Mauro Carvalho Chehab commit c682b3a7fb8ec69ac73511bbb6a378e40aa35f35 Author: Trent Piepho Date: Sat Apr 14 15:16:26 2007 -0300 V4L/DVB (5517): Usbvision: store the device database more efficiently One bit wide bitfields need to declared unsigned to have the range 0 to 1, or they have the range -1 to 0. A few techniques to reduce the driver's size by about 1700 bytes on ia32, probably more on x86-64. Put the biggest fields first, less padding is necessary that way. Put fields with a limited range into a smaller type. For example VideoChannels will fit in 3 bits, and TunerType can use 8 bits. Vin_Reg1, Vin_Reg2, and Dvi_yuv define values for 8-bit registers, but they can't just go into an 8-bit field with no changes, since -1 was used as a flag to indicate a value was not present. So what we do is create a one-bit flag for each one to indicate if a value is or is not present. This only takes 9 bits and has the added advantage that when the register isn't overridden (Vin_Reg[12] never are) it doesn't need to appear in the structure definition since the default value for the flag will be zero. Signed-off-by: Trent Piepho Acked-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit f7ca6256bc1db4fb44adda99e082f8c80ada8957 Author: Mauro Carvalho Chehab Date: Sat Apr 14 15:15:43 2007 -0300 V4L/DVB (5516): Reduce usbvision data size This patch reduces usbvision driver on about 1Kb on i386 over the original version with the old struct: text data bss dec hex filename 52312 11848 60 64220 fadc old/usbvision.ko 52474 10708 60 63242 f70a new/usbvision.ko Acked-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit 659ae56dcd5a50e4560cb526a0e0dc881418dad4 Author: Mauro Carvalho Chehab Date: Sat Apr 14 15:09:59 2007 -0300 V4L/DVB (5515): Use a better format to represent usbvision supported boards Changed usbvision cards table to allow: 1) Not repeat USB ID on two structs; 2) Not need to specify both usb and card description tables at the same order, removing some magic; Some cards had duplicated names. Fixed. A test for an specific board were doing by using a string comparation. The comparation were wrong. Also, it is not a good practice to recognize a board based on his string name. Acked-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit 2c4d336468b400f9a47d6c1785d230548b89ca24 Author: Markus Rechberger Date: Sat Apr 14 10:19:36 2007 -0300 V4L/DVB (5512): Fix 3/3 for bug 7819: fixed hotplugging for dvbnet fixed hotplugging for dvbnet Signed-off-by: Michal CIJOML Semler Signed-off-by: Markus Rechberger Signed-off-by: Mauro Carvalho Chehab commit 57861b432bda77f8bfafda2fb6f5a922d5f3aef1 Author: Markus Rechberger Date: Sat Apr 14 10:19:18 2007 -0300 V4L/DVB (5511): Fix 2/3 for bug 7819: demux and dvr fixing hotplug issue for demux[n] and dvr[n] Signed-off-by: Michal CIJOML Semler Signed-off-by: Markus Rechberger Signed-off-by: Mauro Carvalho Chehab commit ca5be9cd0516629cb8ee335b7dad076e66d72a22 Author: Markus Rechberger Date: Sat Apr 14 10:18:58 2007 -0300 V4L/DVB (5510): Fix 1/3 for bug 7819: fixed frontend hotplug issue fixed frontend hotplug issue Signed-off-by: Michal CIJOML Semler Signed-off-by: Markus Rechberger Signed-off-by: Mauro Carvalho Chehab commit 31a1854706707dc3b67eb0d3bf0f51c67d91c82e Author: Mike Isely Date: Sun Apr 8 01:11:47 2007 -0300 V4L/DVB (5507): Pvrusb2: Gather USB bus address info and report it The V4L2 API requires a unique bus_info string returned as part of the v4l2_capability structure. These changes gather up the USB address information, from the underlying device, into a string and report that out through v4l2 and via sysfs (for completeness). Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab commit 4f210e072235c3c123b068d348a1a02e624ff5be Author: Hendrik Borghorst Date: Thu Apr 5 14:28:11 2007 -0300 V4L/DVB (5505): Fix Kernel Bugzilla #8301: spinlock fix for flexcop-pci If you modprobe the b2c2-flexcop-pci module you got a hardlock of your system. This is due the usage of spin_lock before spin_lock_init is called. Signed-off-by: Mauro Carvalho Chehab commit 990e3743b505a0bb08c04a381d5477e19d31ef5e Author: Trent Piepho Date: Wed Apr 4 17:11:06 2007 -0300 V4L/DVB (5504): Sn9c102: Make driver V4L2 not V4L1 sn9c102 is a v4l2 driver, except it used a couple v4l1 helper functions. Stop using those functions and depend on V4L2 in Kconfig. Acked-by: Luca Risolia Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 7e81d8254d4f00817b98588fce1afb448ccc14a9 Author: Trent Piepho Date: Wed Apr 4 17:11:05 2007 -0300 V4L/DVB (5503): Sn9c102: declare constant byte sequences as static const Makes sure they don't get copied onto the stack. Signed-off-by: Trent Piepho Acked-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab commit c680dd603857d7218b84751e9f6f0654bbfbefa2 Author: Trent Piepho Date: Wed Apr 4 17:11:04 2007 -0300 V4L/DVB (5502): Sn9c102: more efficient register writing code There were many places in the driver which had long sequences of constant register initializations. These were done with one function call per register. The register address and value were immediate values in the function calls. This is very inefficient, as each register and value take twice the space when they are code, as each includes a push instruction to put it on the stack. There there is the overhead, both size and time, for a function call for each register. It's also quite a few lines of C code to do this. The patch creates a function that writes multiple registers from a list, and a macro that makes it easy to construct a such a list as a const static local to send to the function. This gets rid of quite a bit of C code, and shrinks the driver by around 8k, while at the same time being more efficient. Acked-by: Luca Risolia Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 0ee32871c18a3662d8958a8e9998eb4d2ae94159 Author: Mauro Carvalho Chehab Date: Tue Apr 3 18:08:19 2007 -0300 V4L/DVB (5500): Add a CARDLIST for the supported devices by usbvision driver Signed-off-by: Mauro Carvalho Chehab commit 1ebba670edac28d4ea37579453417ced71fd9128 Author: Scott Alfter Date: Mon Apr 2 14:22:39 2007 -0300 V4L/DVB (5497): Additional card support for bttv driver SSAI (www.ssai.us) makes several Bt878-based capture cards that get used in our surveillance, conferencing, and medical imaging systems. The attached relatively small patch adds support for these cards, which fall into two broad * boards with one or more Bt878s, one or more composite inputs, and no S-video or tuner inputs * boards with one Bt878, one composite input, one S-video input, and no tuner input Signed-off-by: Scott Alfter Signed-off-by: Mauro Carvalho Chehab commit 4b9d4e7dba102c8ab5ef651e538e9d0c79c80cab Author: Trent Piepho Date: Sun Apr 1 18:29:04 2007 -0300 V4L/DVB (5494): Lgdt330x: Fix some warnings It's KERN_WARNING "lgdt....", not "KERN_WARNING lgdt...." Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 82c2e4617c28f1e64ecbbeb7a2325f79c80a5aa9 Author: Jean Delvare Date: Sat Mar 31 10:35:24 2007 -0300 V4L/DVB (5492): Remove useless includes of i2c-algo-bit.h The tda7432, tda9875 and tvaudio media drivers don't need to include the linux/i2c-algo-bit.h header file. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab commit e8f4e7525c6ba52d97bb057420163b4c704c6d10 Author: Jean Delvare Date: Sat Mar 31 10:34:59 2007 -0300 V4L/DVB (5491): Cx88: Support the DTV1000 T analog inputs Add support for the S-Video and CVBS (composite) analog video inputs of the Leadtek WinFast DTV1000 T adapter. Signed-off-by: Jean Delvare Acked-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit fd51c697dd6111ee4260d8c752ba4d09dc614c3f Author: Amit Choudhary Date: Fri Mar 30 17:48:59 2007 -0300 V4L/DVB (5490): Drivers/media/video/se401.c: check kmalloc() return value. Check the return value of kmalloc() in function se401_start_stream(), in file drivers/media/video/se401.c. Signed-off-by: Amit Choudhary Signed-off-by: Mauro Carvalho Chehab commit 20ec811eddb362f821c6fd57e5449f3ddb80b466 Author: Amit Choudhary Date: Fri Mar 30 17:34:14 2007 -0300 V4L/DVB (5489): Codec.c: check kmalloc() return value. Signed-off-by: Amit Choudhary Signed-off-by: Mauro Carvalho Chehab commit aaa40cb8b7056b597fa77c9f61a1b271c0e341cf Author: Mauro Carvalho Chehab Date: Fri Mar 30 10:58:01 2007 -0300 V4L/DVB (5488): Replace DMA magic mask for its aliases Signed-off-by: Mauro Carvalho Chehab commit 904ab884a25fbaebe5d76d633d1c30c9f2a7c0aa Author: Ed Vipas Date: Thu Mar 29 18:32:49 2007 -0300 V4L/DVB (5486): Add support for remote of Asustech P7131 Hybrid LNA This patch just defines the remote control type. Signed-off-by: Ed Vipas Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 9971f4f1d3d71a5b6654ba226976ba82d2e047a4 Author: Hartmut Hackmann Date: Fri Mar 23 21:00:07 2007 -0300 V4L/DVB (5485): Tda827x: delayed probing of tuner version When the tuner is attached, the tda10046 is not initilized yet, so it is searching for its firmware. If the tuner is attached to the tda10046 silent i2c port, a bus collision can occur. Now the version is probed during the first init or sleep call. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit e65ec752ced103eb86e5f9c1f747f06d3e266780 Author: Hartmut Hackmann Date: Thu Mar 22 20:58:43 2007 -0300 V4L/DVB (5484): Set tda8290 to analog mode after init Set tda8290 to analog mode after init, otherwise the tuner driver will not accept i.e. the standby command. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit f992a497c71981e215b1415759fc13593ed2919f Author: Jarod Wilson Date: Sat Mar 24 15:23:50 2007 -0300 V4L/DVB (5482): Bttv: automatically load dvb-bt8xx for bttv cards with dvb This patch causes the bttv driver to automatically load the dvb-bt8xx module for bttv/dvb hybrid cards. Successfully tested with a pcHDTV HD-2000 card. This patch is based on the recent patches to enable autoloading of cx88-dvb, cx88-blackbird and saa7134-dvb. Signed-off-by: Jarod Wilson Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit fbc8408a0d9deeba8926e041db0bb1ef7f1b2cd6 Author: Damian Minkov Date: Thu Mar 29 08:47:39 2007 -0300 V4L/DVB (5481): Fix audio input for AverTv Go 007 Fix audio input source for capturing(playing) audio on AverTv Go 007 cards. Signed-off-by: Damian Minkov Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab commit 66623a0419da2bae2efab40a46018faacce2e3aa Author: Mauro Carvalho Chehab Date: Thu Mar 29 08:47:04 2007 -0300 V4L/DVB (5480): Fix cx88_print_irqbits calls to use ARRAY_SIZE cx88_print_irqbits were expecting a string pointer with 32 bytes. Better to pass the string size and use ARRAY_SIZE on its calls. Signed-off-by: Mauro Carvalho Chehab commit 53c4e9551c2930767fcdaa54323616c32ed6e9c6 Author: Mauro Carvalho Chehab Date: Thu Mar 29 08:42:30 2007 -0300 V4L/DVB (5479): Use ARRAY_SIZE instead of a magic number Signed-off-by: Mauro Carvalho Chehab commit 5b9c4e6dbb3204568d4c058af6e34772393ada19 Author: Mauro Carvalho Chehab Date: Wed Mar 28 22:37:26 2007 -0300 V4L/DVB (5478): Use ARRAY_SIZE and a cleaner logic for initializing tuner ATI HDTV Wonder needs to initialize some registers before allowing the tuner to start working. The current logic have lots of magic. This patch makes the code cleaner, using ARRAY_SIZE() for the initialization array and using a bidimensional array, instead of doing some stuff like: &buffer[i+2] Signed-off-by: Mauro Carvalho Chehab commit c1d570385bd6dd5fe4c0cab09b1c390331111b35 Author: Mauro Carvalho Chehab Date: Wed Mar 28 22:37:23 2007 -0300 V4L/DVB (5477): CodingStyle cleanups on for loops at bttv-cards.c Signed-off-by: Mauro Carvalho Chehab commit 57747b7f2517524aed5f0c5b744badf9da94a91b Author: Mauro Carvalho Chehab Date: Wed Mar 28 22:37:20 2007 -0300 V4L/DVB (5476): Fix gpiomux array size there were several "magic" for loops, addressing gpiomux array size (4). Adrian Bunk showed that one of the loops were wrong, going from 0 to 4. This patch provides the right fix for this trouble, by using ARRAY_SIZE on all places where we have a for loop using gpiomux. Thanks to: Adrian Bunk for pointing me about this trouble. Signed-off-by: Mauro Carvalho Chehab commit f423b9a86a6dd3d2bc08d78f4d21525a14c40a6b Author: Luca Risolia Date: Mon Mar 26 16:12:04 2007 -0300 V4L/DVB (5474): SN9C1xx driver updates @ Don't assume that SOF headers can't cross packets boundaries @ Fix compression quality selection + Add support for MI-0360 image sensor * Documentation updates @ Fix sysfs @ MI0343 rewritten * HV7131R color fixes and add new ABLC control * Rename the archive from "sn9c102" to "sn9c1xx" * fix typos * better support for TAS5110D @ fix OV7630 wrong colors @ Don't return an error if no input buffers are enqueued yet on VIDIOC_STREAMON * Add informations about colorspaces * More appropriate error codes in case of failure of some system calls * More precise hardware detection * Add more informations about supported hardware in the documentation + More supported devices + Add support for HV7131R image sensor Signed-off-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab commit 9ab7e323af9f9efad3e20a14faa4d947adfac381 Author: Adrian Bunk Date: Sun Mar 25 12:14:38 2007 -0300 V4L/DVB (5471): Cpia_pp.c: convert to module_{init,exit} After looking at a section bug (in the non-modular case, clearly non-init code referenced the __initdata parport_nr[]), I thought it was time to convert this driver to module_{init,exit}. Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab commit 585553ecedabf434e5cdc59d05bf64596ceab7bc Author: Jonathan Corbet Date: Sun Mar 25 11:38:21 2007 -0300 V4L/DVB (5469): Add raw bayer support to the ov7670 driver Add raw bayer support to the ov7670 driver Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 77d5140fe78ca628bfc9d2567c35457481f9c63d Author: Jonathan Corbet Date: Thu Mar 22 19:44:17 2007 -0300 V4L/DVB (5469a): Copyright and maintainer tweaks Fix up Cafe/ov7670 copyrights and maintainer entries Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 7f7b12f09be8e396d159800baeaf922df46e25cf Author: Jonathan Corbet Date: Sun Mar 25 11:36:42 2007 -0300 V4L/DVB (5468): Don't mirror ov7670 images by default Don't mirror ov7670 images by default. The ov7670 sensor driver sets the mirror bit by default, which is not the desired mode. OLPC has been running with this patch for a while. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit ff68defa08376b6c0482b249b7dfd35241861bce Author: Jonathan Corbet Date: Sun Mar 25 11:36:28 2007 -0300 V4L/DVB (5467): Add suspend/resume support to the Cafe CCIC Add suspend/resume support to the Cafe CCIC driver. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 5b50ed7ca2432e7ea3caf8cc2ef5ac805c082519 Author: Jonathan Corbet Date: Fri Apr 27 12:32:28 2007 -0300 V4L/DVB (5466): Fix up some Cafe CCIC delay issues Fix up unsociable Cafe CCIC delays. The Cafe CCIC driver contains some lengthy delays, some of which are unnecessary and some of which are done under lock. Some were marked with comments, but the comments somehow failed to make the issue go away. So fix it for real. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 76f4a12000254d460e947d2a95ab422ee6e598bf Author: Jonathan Corbet Date: Sun Mar 25 11:36:02 2007 -0300 V4L/DVB (5465): Remove an obsolete PCI ID Remove an obsolete PCI ID. The CAFE driver includes three PCI IDs, one of which corresponds to a development board which is no longer in use. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 018cfd51f682876fec682a33819964c1d6ed6794 Author: Jonathan Corbet Date: Sun Mar 25 11:35:56 2007 -0300 V4L/DVB (5464): Set the PCI device in the V4L2 device Set the PCI dev in the V4L2 dev so that the proper sysfs link gets made Signed-off-by: Jonathan Corbet Signed-off-by: Dan Williams Signed-off-by: Mauro Carvalho Chehab commit 6b1ce3c1017adce52675ec72825f0b052a6af5d4 Author: Mauro Carvalho Chehab Date: Wed Mar 21 16:35:28 2007 -0300 V4L/DVB (5462): Add Logitech ViewPort AV 100 Logitech ViewPort AV 100 has the same internals as Cisco VT Camera. Fixing Pwc driver to handle it properly. Also, fixed the comments for both cameras. Thanks to Martin Rubli for pointing me this. Signed-off-by: Mauro Carvalho Chehab commit a63e157fc6147ae9792325cb55fa0c6d1d0f9905 Author: Jean Tourrilhes Date: Wed Mar 21 16:29:16 2007 -0300 V4L/DVB (5461): Pwc: cisco VT Camera support I have a Cisco VT Camera, and it was just collecting dust. I decided to try connecting it to my Linux box at home. Just a disgression about the product. The Cisco VT Camera is a webcam Cisco sold to work with their IP phone hardware and software. It's mostly useless on Windows, as it interfaces only to Cisco software. You can find some for cheap on eBay... Physically, it's just a Logitech Pro 4000. The only difference with the Pro 4000 is the Cisco logo and that it's grey like the Pro 3000. I believe Cisco is now selling the Cisco VT Camera II, which look to be something else... So, assuming that it was a Pro 4000 inside, I created the little patch attached. I'm new to webcam under Linux, but I managed to get an image from it using xawtv, and the image looked all right, so I consider that a success. The imaged seemed a bit small and I could not get the microphone driver loaded, but I assume it's my lack of experience. Note that I did not try any other type_id, but this one works great. Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab commit d4ca23b188d458ef508649a0c6866937b48d4bf8 Author: Pierre Willenbrock Date: Sun Mar 18 19:54:07 2007 -0300 V4L/DVB (5459): M920x: add support for Anubis Electronics / MSI Digi Vox Mini II Add support for Anubis Electronics "Lifeview" (USB-ID: 0x10fd:0x1513) Signed-off-by: Pierre Willenbrock Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 08cdf94c076121cd0214ef9ea18ae3fbb9ace684 Author: Hartmut Hackmann Date: Sun Mar 18 19:23:20 2007 -0300 V4L/DVB (5458): Tda1004x: add ts_mode option to config struct The struct tda1004x_config has a new entry: .ts_mode Possible values are TDA10046_TS_PARALLEL or TDA10046_TS_SERIAL There always is only one interface active, default is parallel. Signed-off-by: Hartmut Hackmann Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit d519dcf61ed55bcfb947a31122cea068a73ad974 Author: Trent Piepho Date: Mon Mar 19 02:24:09 2007 -0300 V4L/DVB (5457): Dvb-pll: Replace sleep function with a more capable one The dvb-pll sleep function could only send a 2-byte sequence to the PLL. This isn't enough in some cases, for example fmd1216me will need to send a 4-byte command to set both BB and AB to the correct values. Instead of using a fake band with a frequency of 0 to store the sleep data (which has room for only two bytes), the new sleep function works like the init function. A new pointer is added to the pll description, and when non-NULL points to a buffer with the length and data to send. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit df78cb0a1870703b7622fe8c63e5bcb6563197fd Author: Trent Piepho Date: Mon Mar 19 02:24:04 2007 -0300 V4L/DVB (5456): Dvb-pll: Move IF frequency from per-band data to per-tuner data The IF frequency was specified for each band, but it's not something that changes from band to band. None of the tuner definitions had a different IF frequency (called offset) from one band to another. Acked-by: Michael Krufky Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 0fd17d6da8154e1a6391440a268b0620ab339122 Author: Trent Piepho Date: Mon Mar 19 02:23:59 2007 -0300 V4L/DVB (5455): Dvb-pll: Adjust rounding to be consistent Some PLLs had one half the step size added to the offset, so that the divisor would be rounded to the nearest integer. Some didn't and so would always be rounded down. This makes dvb-pll round to the nearest when calculating the divisor, without the offset needing to be fudged. PLLs that had a fudged offset have the offset changed to be just the IF frequency. The satellite PLL dvb_pll_philips_sd1878_tda8261 was rounding up for some reason, and I've kept it that way. In addition, frequencies that were rounded to the nearest kHz are extended to full Hz resolution. One sixth MHz step sizes that were listed as 166,666 Hz are changed to 166,667 Hz, which is slightly closer. PLLs that were already rounding: dvb_pll_tda665x, offset was 36 1/6 (to nearest kHz) + step/2 dvb_pll_fmd1216me, offset was 36 1/8 (to two digits) + step/2 dvb_pll_thomson_fe6600, offset was 36 1/8 (to two digits) + step/2 dvb_pll_env57h1xd5, offset was 36 1/8 + step Note that the last PLL, dvb_pll_env57h1xd5, appears to have had a bug in the offset. Rather than adding stepsize/2, it was adding a full stepsize. The PLL definition originally came from the dibusb driver, which used 36 1/8 + step/2. The change to 36 1/8 + step was probably a mistake added when the tuner was converted to dvb-pll. Signed-off-by: Trent Piepho Acked-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit a5a2ecfc3b9b1f97a7f141bd95ea75097ad49b7c Author: Trent Piepho Date: Fri Mar 9 15:07:07 2007 -0300 V4L/DVB (5452): Cx88: merge identical boards The attach code for HAUPPAUGE_HVR3000 and HAUPPAUGE_HVR1300 is exactly the same as the code used by HAUPPAUGE_HVR1100, HAUPPAUGE_HVR1100LP, and WINFAST_DTV2000H. So, those first two cards are added to the case block used by the last three. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 59069e53a27605d14a1a693683a45de837ee7521 Author: Pierre Willenbrock Date: Thu Mar 15 13:24:29 2007 -0300 V4L/DVB (5449): M920x: add error messages for debugging purposes Signed-off-by: Pierre Willenbrock Signed-off-by: Aapo Tahkola Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 7d1d4e6c58243614c38ad516a5acfd1af096ba9b Author: Aapo Tahkola Date: Thu Mar 15 13:01:46 2007 -0300 V4L/DVB (5448): M920x: rename megasky_identify_state to m920x_identify_state This function should work for all m920x-based devices. Signed-off-by: Aapo Tahkola Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit f3eec0c001a6781224d39912d41b58eaf2126586 Author: Hartmut Hackmann Date: Wed Mar 14 20:33:55 2007 -0300 V4L/DVB (5446): Renamed ASUStek P7131 card [1043:4876] The new name fits to what it is and what is on the box. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit e06cea4cb4a16076dfca4863b12e1c4aeb781c8d Author: Hartmut Hackmann Date: Tue Mar 13 20:58:29 2007 -0300 V4L/DVB (5445): Added / corrected support for some ASUS hybrid boards There are 2 new entries for p7131 boards and one correction for a board with LNA. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit cf83ac433c275b8a652492cabdb39b88a7f8d45c Author: Hartmut Hackmann Date: Tue Mar 13 20:52:35 2007 -0300 V4L/DVB (5444): Saa7134-dvb fix sleep function of the fmd1216 tuner. Static locals should not be changed - the original contents gets lost. Thanks to Trent Piepho for pointing me to this. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 80d352374be7ac88a23fb427d146ac9a71beff90 Author: Hartmut Hackmann Date: Tue Mar 13 20:44:22 2007 -0300 V4L/DVB (5443): Saa7134: put tuner to sleep mode after board initialization Besides power saving, this puts the AGC output of the tda8290 to tristate. This is necessary for some hybrid boards which don't use a multiplexer for the AGC Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 1b2457680f237f858d5f6bcf933cd173a7b27a81 Author: Oliver Endriss Date: Mon Mar 12 21:45:46 2007 -0300 V4L/DVB (5440): Dvb-ttpci: Infrared remote initialization fix Fix bug introduced during infrared refactoring. Thanks to Johann Friedrichs for spotting this. Thanks-to: Johann Friedrichs Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit 9e0df402e695e638dfee94a6e05fca48b15404e6 Author: Ian Armstrong Date: Fri Mar 16 07:44:42 2007 -0300 V4L/DVB (5438): Fix ivtv yuv threshold handling Modifies automatic mode selection for yuv playback. Behaviour is now that source video with a vertical resolution below that of the currently set broadcast mode will be treated as progressive. Video with a vertical resolution greater or equal to the current broadcast mode (up to 576 lines) will be treated as interlaced. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 59fc7f52898fb35eb053bc7b54430d81629b5966 Author: Ian Armstrong Date: Fri Mar 16 07:40:48 2007 -0300 V4L/DVB (5437): Update cx23415 documentation Adds more osd mode switching information. Corrects some information regarding mode selection & local alpha operation for 16 bit modes. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 31a7c549eda6bf0a38d8fc12c903a8535a9f27ca Author: Hans Verkuil Date: Tue Mar 13 19:22:40 2007 -0300 V4L/DVB (5436): Fix TV output initialization The TV standard should be set AFTER the TV output is fully initialized. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit ddc285c7632b64fb1ac3ec8f25bcc8abd4a1f727 Author: Steven Toth Date: Mon Mar 12 22:26:40 2007 -0300 V4L/DVB (5434): Updates to the tveeprom tuner, video decoder and audio chip Some of the new tuner entries may need to be mapped to compatible tuners already defined. I don't know for certain which tuners are compatible between manufacturers. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab commit 923da8a750fe008da308d0698f66052dfccb037d Author: Michael Krufky Date: Sun Mar 11 12:52:48 2007 -0300 V4L/DVB (5432): Cx88: whitespace cleanup replace leading spaces with tabs Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 733aeaf4c006353b1ed8c54b707c03a6385c30ed Author: Michael Krufky Date: Sun Mar 11 12:48:04 2007 -0300 V4L/DVB (5431): Cx88: autodetect ADS Tech InstantTV DVB-S The ADS Tech InstantTV DVB-S is a clone of the KWorld DVB-S 100. This patch adds autodetection support for this card based on pci subsystem id. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 57f8dcf90bdf4c6b23ea2af3d307d1d137b696b9 Author: Trent Piepho Date: Mon Mar 12 12:13:12 2007 -0300 V4L/DVB (5428): M920x: Detect zero-length I2C messages and fix a typo Change a 00 to just 0 Detect zero-length I2C messages and return not supported. I think I know how to send one, but the problem is getting the slave's ack. The only point of a zero-length message is for probing; too see if the slave will ack its address. Since we don't know how to get the ack, we can't support zero-length messages in a useful way, so it's probably best to just return not supported for them. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit d40860f8e2edb31196f4233d3691704d313dbdd6 Author: Trent Piepho Date: Mon Mar 5 23:55:00 2007 -0300 V4L/DVB (5427): M920x: Improve I2C operations Write some better documentation about what might be known about how the m920x I2C works, since a datasheet is lacking. The I2C xfer function should now handle more types of I2C transactions than it could before. Those it can't, will return error codes instead of being executed incorrectly. Multi-byte reads were not being done correctly, which should be fixed. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 634bc48da766a9817e55e7dbd9909a6b26567e92 Author: Aapo Tahkola Date: Mon Mar 5 18:57:27 2007 -0300 V4L/DVB (5426): M920x: remove unneeded code Signed-off-by: Aapo Tahkola Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 480ac761729349f9005d2c6dce6076f7e33de3bc Author: Aapo Tahkola Date: Mon Mar 5 18:54:27 2007 -0300 V4L/DVB (5425): M920x: rework driver code to allow for different devices Signed-off-by: Aapo Tahkola Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 26a154c355cceba66ff13b383aa2019c28dbc685 Author: Aapo Tahkola Date: Mon Mar 5 18:25:36 2007 -0300 V4L/DVB (5424): Fix i2c implementation for gl861 and au6610 - r/w bit is not part of the i2c address Signed-off-by: Aapo Tahkola Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 26247018be1ca7ca464f5f32e8ca7c897ded5393 Author: Aapo Tahkola Date: Mon Mar 5 18:23:19 2007 -0300 V4L/DVB (5423): M920x: i2c cleanups - Implement m920x i2c as suggested by Pierre Willenbrock - remove "magic" hack - r/w bit is not part of the i2c address - move hardware remarks to header file Signed-off-by: Aapo Tahkola Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit fee73165ee0ff7cfbffad4233a70f56b821f434e Author: Hans Verkuil Date: Sun Mar 11 14:16:42 2007 -0300 V4L/DVB (5420): Initialize the inputs before registering the devices. Once the devices have been registered anyone can start changing the inputs or TV standard before they have been initialized by the driver. This leads to cases were the input is changed in an udev rule, but after that rule is triggered the tail-end of the ivtv driver initialization can override that by selecting the tuner input. The correct sequence is to first setup the input, initial frequency and TV standard before finally registering the video devices. This prevents any udev rules from being triggered prematurely. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 6816b1991fd4bcb457c9534e8136476e45bfee0a Author: Hans Verkuil Date: Sun Mar 11 10:54:11 2007 -0300 V4L/DVB (5419): Add comment how the speed field is interpreted. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 3700a90f05f316948328e8d0e6a9955338a96565 Author: Hans Verkuil Date: Sun Mar 11 10:50:03 2007 -0300 V4L/DVB (5418): Speed is a signed 32-bit integer, not unsigned. Negative speed values have to be allowed for reverse playback. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit f40a29168475d64d854ef16a9263b24b0b2a9c6e Author: Hans Verkuil Date: Sat Mar 10 20:50:51 2007 -0300 V4L/DVB (5417): First unregister the driver, and then free the memory. ivtv_remove which is called by pci_unregister_driver was still using memory that was already freed. Ouch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 6773c1c24d48f0aa667ba2aff573904a77050f09 Author: Hans Verkuil Date: Sat Mar 10 20:34:54 2007 -0300 V4L/DVB (5416): Use pci_register_driver instead of pci_module_init in ivtv. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 1e5e9aab31d3f73977d2b14189a0bb08b582cbb9 Author: Hans Verkuil Date: Sat Mar 10 20:11:23 2007 -0300 V4L/DVB (5414): Add missing kfree in early exit of saa7115. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit d6102900e7e06e1c0c93889d38848a5b2d44e41d Author: Hans Verkuil Date: Sat Mar 10 20:09:07 2007 -0300 V4L/DVB (5413): Use spin_lock_init to fix lockdep warnings. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 25415cf3b8b3982b065e2227f079ea9cf7a97ef7 Author: Hans Verkuil Date: Sat Mar 10 18:29:48 2007 -0300 V4L/DVB (5412): Fix VIDIOC_TRY_ENCODER_CMD and VIDEO_TRY_COMMAND VIDIOC_TRY_ENCODER_CMD did the same as VIDIOC_ENCODER_CMD, now it no longer touches the encoder. Both the encoder and decoder commands did not clear the flags field of unknown flags. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit d4e7ee36f988d2757c218d61c2f334df0a1acd45 Author: Hans Verkuil Date: Sat Mar 10 18:19:12 2007 -0300 V4L/DVB (5411): Use v4l_printk_ioctl for debug Using v4l_printk_ioctl saves a lot of code duplication. Also moved a few ioctl cases to another function, improving the ioctl grouping. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit d46c17d7aa12e30b612acae35535fcd64f2db3d6 Author: Hans Verkuil Date: Sat Mar 10 17:59:15 2007 -0300 V4L/DVB (5410): Add VIDIOC_G/S_PRIORITY support to ivtv. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit a542fe47fe3f758e47d71640e7ca328c032a13e1 Author: Hans Verkuil Date: Sat Mar 10 13:56:56 2007 -0300 V4L/DVB (5409): Add CARDLIST.ivtv and README.ivtv Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 3f3f6a5958ffc651227df671704467654b71c09d Author: Hans Verkuil Date: Sat Mar 10 08:10:11 2007 -0300 V4L/DVB (5407a): Update feature-removal-schedule.txt: remove VIDIOC_S/G_MPEGCOMP Those two experimental APIs never worked fine nor, afaik, were implemented at the apps. Their functionalities were implemented by other means. So, let's remove those obsolete stuff. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit b6735ac24ef735532d8f6d71eb30bca365d9307a Author: Hans Verkuil Date: Sat Mar 10 06:59:44 2007 -0300 V4L/DVB (5406): Add comment why the symbols are exported. It is not immediately obvious why the ivtv symbols are exported in ivtv-driver.c since both ivtv-fb and the IR-blaster module are still out-of-tree, currently being ported to be in kernel. Added a comment so people are aware of these issues. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 83df8e7b0d7b319f9ce9773eaf4b1da324ae17d7 Author: Hans Verkuil Date: Sat Mar 10 06:54:58 2007 -0300 V4L/DVB (5405): Add missing includes. Every file should include the headers containing the prototypes for its global functions. Signed-off-by: Adrian Bunk Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 1e13f9e3f1501cc167e40a2adf07e6e4705cb331 Author: Hans Verkuil Date: Sat Mar 10 06:52:02 2007 -0300 V4L/DVB (5404): Merges VBI & YUV handling into a single work queue. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 037c86c53362b0b3dda6201c9f62f64c9d17abb6 Author: Hans Verkuil Date: Sat Mar 10 06:30:19 2007 -0300 V4L/DVB (5403): Set vsync_field correctly in ivtv. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 43d0dfcfc654fa18b6dd91b9483273b44112997f Author: Hans Verkuil Date: Sat Mar 10 06:24:30 2007 -0300 V4L/DVB (5402): Add vsync_field to the union in video_event for VIDEO_EVENT_VSYNC VIDEO_EVENT_VSYNC needs to tell the application which field it was that received a VSYNC (odd/even/progressive). The vsync_field was added to the union in video_event for this purpose. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 5332bdbe9aae9e1a8fc5daaca6c75f05bc37d310 Author: Oliver Neukum Date: Fri Mar 9 18:05:43 2007 -0300 V4L/DVB (5399): Usbvideo module handling Signed-off-by: Oliver Neukum Signed-off-by: Mauro Carvalho Chehab commit 5cdc178d1c7e677f0344b7b933d71c900ae73c85 Author: Oleg Nesterov Date: Fri Mar 9 17:43:39 2007 -0300 V4L/DVB (5398): Cpia_pp.c: don't use _WORK_NAR pp_cam_entry->cb_task need not to be _NOAUTOREL ... because in fact it is never used ??? Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab commit 15f189e9780d2859ee2bd5ee6b7180a010a9ebf5 Author: Adrian Bunk Date: Fri Mar 9 17:39:52 2007 -0300 V4L/DVB (5397): Saa7134: fix MODULES=n compilation This patch fixes the following compile error with CONFIG_MODULES=n: CC drivers/media/video/saa7134/saa7134-core.o /home/bunk/linux/kernel-2.6/linux-2.6.21-rc2-mm1/drivers/media/video/saa7134/saa7134-core.c:979:24: error: macro "request_submodules" passed 1 arguments, but takes just 0 /home/bunk/linux/kernel-2.6/linux-2.6.21-rc2-mm1/drivers/media/video/saa7134/saa7134-core.c: In function 'saa7134_initdev': /home/bunk/linux/kernel-2.6/linux-2.6.21-rc2-mm1/drivers/media/video/saa7134/saa7134-core.c:979: error: 'request_submodules' undeclared (first use in this function) /home/bunk/linux/kernel-2.6/linux-2.6.21-rc2-mm1/drivers/media/video/saa7134/saa7134-core.c:979: error: (Each undeclared identifier is reported only once /home/bunk/linux/kernel-2.6/linux-2.6.21-rc2-mm1/drivers/media/video/saa7134/saa7134-core.c:979: error: for each function it appears in.) make[5]: *** [drivers/media/video/saa7134/saa7134-core.o] Error 1 Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab commit 93566ad8068a968c3e72951b4539eb9da661446d Author: Trent Piepho Date: Wed Mar 7 18:19:49 2007 -0300 V4L/DVB (5392): Zr364xx: Use kernel's byte-swapping function Some code to swap bytes wasn't using the swab16() function that the kernel provides for this. Make use of it, which results in more efficient code. Signed-off-by: Trent Piepho Acked-by: Antoine Jacquet Signed-off-by: Mauro Carvalho Chehab commit cf3c34c87f921c5c63d47285c9860345cdaf5170 Author: Trent Piepho Date: Wed Mar 7 18:19:48 2007 -0300 V4L/DVB (5391): Saa7134: Clean up printk()s Change some debug messages from printk() to dprintk(). Add KERN_WARNING and KERN_ERR level indicators to other printk()s that lacked them. Format printk lines with consistent ("%s/dvb: ", dev->name) prefix. Fixed dprintk macro, which had an if with no else that wasn't protected with a do {} while(0) block. That leads to "if(...) dprintk(); else" not doing what one would expect. Signed-off-by: Trent Piepho Acked-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 3b35b4b38049356b1e0b8e64ee27d1aca606b766 Author: Hans Verkuil Date: Wed Mar 7 17:10:07 2007 -0300 V4L/DVB (5389): Add tveeprom entry for tuner LG S701D MK3 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit a415783bbb9fba7be2eeaeb5e3e08262bd3d64a1 Author: Andrew Morton Date: Wed Mar 7 11:28:33 2007 -0300 V4L/DVB (5388): Ivtv warning fix drivers/media/video/ivtv/ivtv-i2c.c:547: warning: initializer-string for array of chars is too long drivers/media/video/ivtv/ivtv-i2c.c:547: warning: (near initialization for 'ivtv_i2c_client_template.name') Signed-off-by: Andrew Morton Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit e6c1df5581320720b8212a543e055e19d46f32bb Author: Dwaine P. Garden Date: Tue Mar 6 15:15:19 2007 -0300 V4L/DVB (5386): Add some missing Hauppauge and Belkin devices to the driver -Add some missing Hauppauge and Belkin devices to the driver. -Fixed up some device descriptions. Signed-off-by: Dwaine P. Garden Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab commit 2575f84a50f71b867d8d2ff2994eefc92d60ac79 Author: Antoine Jacquet Date: Mon Mar 5 06:32:29 2007 -0300 V4L/DVB (5385): Fix compilation issue with zr364xx when V4L1 is disabled Add a missing header to fix compilation issue in the zr364xx driver when CONFIG_VIDEO_V4L1 and CONFIG_VIDEO_V4L1_COMPAT are not set. Signed-off-by: Antoine Jacquet Signed-off-by: Mauro Carvalho Chehab commit a6210b7bea86305871bdca32f38b115b3ea67edf Author: Emil Georgiev Date: Sun Mar 4 07:03:17 2007 -0300 V4L/DVB (5383): Fix duplicated codes in Pinnacle Grey remote The keymap for this remote had duplicated labels for different keys, which resulted in those key pairs being unable to function as different inputs. Signed-off-by: Emil Georgiev Signed-off-by: Ricardo Cerqueira Signed-off-by: Mauro Carvalho Chehab commit 14500d4a5140252f2524d8d89a22166ef2313614 Author: Oliver Endriss Date: Sat Mar 3 14:45:48 2007 -0300 V4L/DVB (5381): Dvb-ttpci: Update frontend lock status in transfer mode (bugfix) In transfer/replay mode the frontend lock status was never updated. This caused a 'black screen' if VDR switched from transfer mode to live mode on the same transponder. Thanks to Marco Schluessler for spotting the problem. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit bfce1747e8c5a7b4b0d1331328a08f26fa5b2f2e Author: Jean Delvare Date: Sat Mar 3 13:34:34 2007 -0300 V4L/DVB (5380): Cx25840-firmware include cleanup There is no reason why cx25840-firmware.c would need to include . Signed-off-by: Jean Delvare Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 31ec13561060b748221f4e0404bcc5bf8078ccd0 Author: Hans Verkuil Date: Sat Mar 3 08:50:42 2007 -0300 V4L/DVB (5379): If possible make vars/functions static. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 43053c07fa2935038bfc0e5dbaf417d1d66cd95d Author: Hans Verkuil Date: Sat Mar 3 08:40:36 2007 -0300 V4L/DVB (5378): Add missing IVTV_FB_WARN #define This is needed for ivtv-fb. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit a51a50bd01e9f45245edd67dad7cef3ddb8452c5 Author: Hans Verkuil Date: Sat Mar 3 08:28:54 2007 -0300 V4L/DVB (5377): Replace SA_* with IRQF_* SA_* interrupt flags are being phased out, update to newer flags. Thanks to Maarten Maathuis for pointing this out to me. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 4b0e51dd6d7998a7acaca880392979a4a7072d21 Author: Hans Verkuil Date: Sat Mar 3 08:15:36 2007 -0300 V4L/DVB (5376): Add dependency on VIDEO_V4L1 VIDEO_V4L1 is needed to get tvaudio to be built. Stupid really as ivtv is only using the v4l2 API to communicate with tvaudio. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 82dcab2d628ec680d701f6de66441ddc2c367227 Author: Hans Verkuil Date: Sat Mar 3 08:01:54 2007 -0300 V4L/DVB (5375): Add missing VIDEO_CX25840 dep, remove unused VIDEO_TLV320AIC23B dep VIDEO_CX25840 was missing in the ivtv dependencies. VIDEO_TLV320AIC23B was removed since it isn't used by ivtv. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit d9e54324d03add27990ac27fcb34a1ae7a0f97ef Author: Trent Piepho Date: Fri Mar 2 19:42:21 2007 -0300 V4L/DVB (5374): Or51132: refactor i2c code, improve error resilience The code the i2c transactions was leftover from the old V4L-based ATSC driver. It did too little with too much code. It is re-written to remove unnecessary parameters and be more efficient. A demod register can now be read with one function call, instead of repeating a dozen line block of code each time. There were msleep()'s, which appear to be unnecessary, spread around all the I2C transactions. These have been removed. Reading SNR used to take about 130 ms, now it's down to 1.8 ms. Reads from the demodulator's registers do not return correct results sometimes. Adding or removing the delays in the I2C transactions did not appear to effect the probability of failure. If anything, the transactions without delays were less likely to fail, but since far more transactions could be made per second the number of failures per hour was greater. To increase reliability, the SNR and get_params functions will now retry once if they get bad data back. This appears to have reduced the probability of failure to effectively zero. Some error messages are cleaned up or given KERN_* levels when they were missing. or51132_setmode() wasn't returning correct error codes, which is fixed as well. CC: Rusty Scott Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 387e69adff3d441616b45002ff85c5b36001be9e Author: Dennis Ranke Date: Fri Mar 2 06:27:28 2007 -0300 V4L/DVB (5373): [PATCH] Hauppauge Nova-T endianess problem on powerpc When trying to use a Hauppauge Nova-T Stick on a big-endian architecture (such as powerpc) no frontend can be attached. The attached patch fixes this problem by removing two lines in dib0700_ctrl_rd() that try to correct the endianess on two values that already are correct: - /* think about swapping here */ - value = le16_to_cpu(value); - index = le16_to_cpu(index); With this simple patch this dvb hardware works great, thanks to anyone involved for the good work. :) Signed-off-by: Dennis Ranke Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit ddc9ece89dbeb374e34772232f5e26f64ce63390 Author: Christophe Cattelain Date: Fri Apr 27 12:31:32 2007 -0300 V4L/DVB (5371): [PATCH] Pinnacle PCTV Sat Pro USB (450e) support by ttusb2.c (400e driver) Added USB_PID_PCTV_450E to the 'usb_device_id ttusb2_table' and an entry in the .devices table. The 400e driver now supports the 'Pinnacle PCTV Sat Pro USB (450e)' with USB_ID 2304:0222. Signed-off-by: Christophe Cattelain Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit ede2200d79777d461cf2f0fd19cf7a17f633d3a4 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:32 2007 -0300 V4L/DVB (5369): Fixed 1 byte too short buffer in tda827x.c - The i2c data buffer in tda827xa_set_params was 1 byte too short - saa7134-dvb now gives an error mesage if tda827x could not be attached - coding style fix in tda1004x.c Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 8481a7506bc71622bb5bbd89bc89b2f38bff5ead Author: Mike Isely Date: Fri Apr 27 12:31:31 2007 -0300 V4L/DVB (5367): Pvrusb2: (trivial) Fix too-wide source line Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab commit 6bdcc6e6dbab8daffd05e5026486f34ba41a6c72 Author: Trent Piepho Date: Fri Apr 27 12:31:30 2007 -0300 V4L/DVB (5363): Dvb: Remove lgh06xf driver The code of the dvb-pll driver and the lgh06xf driver is nearly identical. The main difference is that the lgh06xf driver would set the AGC TOP value on every tune call. The dvb-pll driver now has the ability to set the AGC TOP when the front-end device is opened, which is a better way to go about it. By using this ability of dvb-pll, the lgh06xf driver is made unnecessary. There is one other difference. dvb-pll will probe for the presence of an I2C pll chip by doing a one byte read, the lgh06xf driver did not do this. In some devices the PLL is not reachable over I2C at the timer the tuner is attached. Some more initialization, such as firmware loading, must take place first. None of the devices using a LG-H06xF should have this problem. Signed-off-by: Trent Piepho Acked-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 26aed92289ae8caffeedebb3f841a9e0371119a9 Author: Trent Piepho Date: Fri Apr 27 12:31:29 2007 -0300 V4L/DVB (5362): Dvb-pll: add code for doing tuner initialization Some tuners need or benefit from initialization, to change certain settings from their power on default values. Typically, tuners with TUA603x PLLs can benefit from setting the AGC TOP value to something else. This patch includes code to set the AGC TOP to 103 dBuV for the Thomson DTT-761x tuners, which I have experimentally verified gives the best SNR readings, increasing SNR by about 0.19 dB over the default value. Other tuners can make use of this as well. For example, the separate LG TDVS-H06xF driver's only difference from dvb-pll is this same setting of AGC TOP value. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 9ab1ba38e6a25a480234f196aa26239e28ac2407 Author: Trent Piepho Date: Fri Apr 27 12:31:28 2007 -0300 V4L/DVB (5361): Dvb-pll: Fix Kconfig files and allow dvb-pll to be optional A number of drivers selected DVB_PLL when they did not need it, and some that did need it did not select it. The DVB_PLL option is given a name and help text, so that it will show up in the config menu. DVB_PLL support can be turned on if an out-of-tree driver needs it. The standard dvb fe customization support is added to dvb-pll.h. Since all modules which select DVB_PLL do so unconditionally, it is not possible to turn dvb-pll off when an enabled module selects it, unlike most of the other frontend/tuner drivers. This is because the users of dvb-pll have static references to dvb-pll symbols other than the attach function. If these references are removed, then dvb-pll will be disablable as the other frontend/tuner drivers are. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 982dd1bdaab39185ccd55cafd3c099b7fee5f2dd Author: Trent Piepho Date: Fri Apr 27 12:31:27 2007 -0300 V4L/DVB (5360): Dvb-pll: Use sizeof() to get name length Better to use sizeof() to get the size of the output buffer for the tuner name, instead of just hard coding 128. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 51dec1f1abef9771d9085c2336234b28b3e78821 Author: Hans Verkuil Date: Fri Apr 27 12:31:27 2007 -0300 V4L/DVB (5356): Fix bogus error messages in ivtv for VIDIOC_G_CHIP_IDENT Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 74cab31c413c8615efe818d44ff4ac83e2a138be Author: Hans Verkuil Date: Fri Apr 27 12:31:26 2007 -0300 V4L/DVB (5355): Add VIDIOC_G_CHIP_IDENT to various i2c modules Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 1a0adaf37c30e89e44d1470ef604a930999a5826 Author: Hans Verkuil Date: Fri Apr 27 12:31:25 2007 -0300 V4L/DVB (5345): ivtv driver for Conexant cx23416/cx23415 MPEG encoder/decoder It took three core maintainers, over four years of work, eight new i2c modules, eleven new V4L2 ioctls, three new DVB video ioctls, a Sliced VBI API, a new MPEG encoder API, an enhanced DVB video MPEG decoding API, major YUV/OSD contributions from Ian and John, web/wiki/svn/trac support from Axel Thimm, (hardware) support from Hauppauge, support and assistance from the v4l-dvb people and the many, many users of ivtv to finally make it possible to merge this driver into the kernel. Thank you all! Signed-off-by: Kevin Thayer Signed-off-by: Chris Kennedy Signed-off-by: Hans Verkuil Signed-off-by: John P Harvey Signed-off-by: Ian Armstrong Signed-off-by: Mauro Carvalho Chehab commit ac52ea3c3c04403d10acf0253180ec6f51977142 Author: Oliver Endriss Date: Fri Apr 27 12:31:24 2007 -0300 V4L/DVB (5344): Dvb-ttpci: Support for MSC_RAW and MSC_SCAN RC events Support for MSC_RAW and MSC_SCAN remote control events. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit 1b5888cea1d371239a130150222e63d476298d89 Author: Hans Verkuil Date: Fri Apr 27 12:31:23 2007 -0300 V4L/DVB (5341): Add cx23415/6 chip idents. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 0b20060f6c2cc69c5394cf9782513e7b526e87b9 Author: Hans Verkuil Date: Fri Apr 27 12:31:22 2007 -0300 V4L/DVB (5336): Cx23416 doc updates + rename CX2341X_ENC_UNKNOWN The documentation of Several miscellaneous commands was updated. As a result of which the CX2341X_ENC_UNKNOWN command was renamed to CX2341X_ENC_SET_VERT_CROP_LINE. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 145859cb8fafc1d75e780ea182413b13f5913a7f Author: David Hrdeman Date: Fri Apr 27 12:31:22 2007 -0300 V4L/DVB (5335): Budget-ci: Use the repeat handling of the input subsystem The attached patch contains the last set of changes to the budget-ci IR handling which makes it use the repeat handling of the input subsystem. This allows some code simplification, makes sure that repeat key presses are reported as such and also allows the "debounce" hack to be removed altogether. In addition a couple of static variables were removed which would have confused the IR code if more than one card is used. Signed-off-by: David Hardeman Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit ee820a648fb333034aa455e8e1479b89798a1281 Author: Oliver Endriss Date: Fri Apr 27 12:31:21 2007 -0300 V4L/DVB (5334): Dvb-ttpci: Infrared remote control refactoring Infrared remote control support rewritten. Now each device provides its own event device, keymap, protocol, inversion and address setting. EVIOCGKEYCODE and EVIOCSKEYCODE ioctls are supported to read/modify a keymap. Keymaps may be loaded using - input tools (keyb etc.) - av7110_loadkeys (obsolete, for backward compatibility) New command line parameters: - ir_protocol: select infrared protocol: 0 RC5, 1 RCMM (default) - ir_inversion: signal inversion: 0 not inverted (default), 1 inverted - ir_device_mask: bitmask of infrared devices (default: accept all) Those parameters may be set anytime. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit db4836791dc578f0f6e9573468cffeee00fa7ebc Author: Peter Missel Date: Fri Apr 27 12:31:20 2007 -0300 V4L/DVB (5331): Identify MSI TV@nywhere Duo It is a Lifeview Duo with a different ID Signed-off-by: Peter Missel Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit e95d317da2de7116ef66fa16bd9664cd39f1c11c Author: Markus Rechberger Date: Fri Apr 27 12:31:19 2007 -0300 V4L/DVB (5330): Added card definition for AverMedia M102 miniPCI Signed-off-by: Markus Rechberger Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 9040b32ea34eff58c59b46abf440c0dd1614c8a2 Author: Heikki Orsila Date: Fri Apr 27 12:31:18 2007 -0300 V4L/DVB (5329): Some saa7134 cleanups - use generic sort instead of bubblesort - removed useless saa7134_video_fini function - small coding style changes Signed-off-by: Heikki Orsila Signed-off-by: Andrew Morton Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 80f90fba3ed8c8826d968125004e4462d2de172e Author: Hartmut Hackmann Date: Fri Apr 27 12:31:18 2007 -0300 V4L/DVB (5326): Allow to set tuner_config in attach inform This patch move the assignment of the tuner config and the callback before the check whether it is called in the attach inform. This solves a module load order issue Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 4217e25275eeb1973ca5bed65645f70b8f6c199d Author: Markus Rechberger Date: Fri Apr 27 12:31:17 2007 -0300 V4L/DVB (5324): This patch fixes request_module_depend() this patch fixes request_module_depend() Signed-off-by: Markus Rechberger Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit cfeb88398f004a0e85ee011fd89a01f5d3bf3c81 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:17 2007 -0300 V4L/DVB (5323): Updated support for tuner callbacks This change supplies a more generic version of the tuner callback. The tuner struct now has a function pointer int (*tuner_callback) (void *dev, int command, int arg) additionally to a int config parameter. both can be set through the TUNER_SET_TYPE_ADDR client call. Note that the meaning of the parameters depend on the tuner type. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit b8bc76d88fa7a1e4cd679fac3adfc5afeb2b3427 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:16 2007 -0300 V4L/DVB (5322): Removed board naming code in saa7134-dvb This is for better consistency with other drivers Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 1c4f76abb85918646eed5dee0b26744cc49fd1da Author: Hartmut Hackmann Date: Fri Apr 27 12:31:16 2007 -0300 V4L/DVB (5321): Saa7134-dvb: initialize the dvb frontend in dvb_init The hardware is completely initialized afterwards, especially the tda10046 has its firmware - which is also necessary in analog mode of some hybrid boards. Calling the sleep function afterwards saves power and definitely puts hybrid boards into analog mode without additional code elsewere. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 11f65106adb25a9ef5b6d8911267b2365c97a759 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:15 2007 -0300 V4L/DVB (5320): Filled in limiting values in tda827x.c The parameters for minimum and maximum frequency were missing. Also added mail addresses of the module authors. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 68717583557341874b2eea4dea36635256e932b6 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:15 2007 -0300 V4L/DVB (5319): Set tda827x to sleep mode after attach This change sets the tda827x to sleep mode right after attach in dvb mode. It is just to save power. For the same reason, the ADC of the tda10046 gets turned off in sleep mode. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 06be3035f96d73cf64dc20a8ee37c902d7a2ff2d Author: Hartmut Hackmann Date: Fri Apr 27 12:31:15 2007 -0300 V4L/DVB (5318): Fix tda8290 code for tda827x module During tuner attach, the pointers to host dev structure are not set yet, so the I2c adapter needs to be accessed differently. This patch also does some minor cleanup in the saa7134-dvb module. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 8ce47dad8e2b9fcb899a67e6537337fa9c18c1f5 Author: Michael Krufky Date: Fri Apr 27 12:31:14 2007 -0300 V4L/DVB (5317): Create tda827x dvb tuner module The patch moves the tda827x dvb tuning code to a separate module Signed-off-by: Michael Krufky Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 7c7fea669d77048b3013567dfb4c9171d536da05 Author: Peter Missel Date: Fri Apr 27 12:31:14 2007 -0300 V4L/DVB (5316): Add radio support for the Lifeview FlyDVB-T Duo There are card variants supporting FM radio through tda8275. Signed-off-by: Peter Missel Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit f4546e702a89d2e483570f0f16c5155bb781cc38 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:13 2007 -0300 V4L/DVB (5315): Tda1004x: check request firmware for NULL ponter again In older versions, this was used to decide whether to boot from eeprom or file. This is no longer necessary but the check helps to avoid an oops with misconfigured cards. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 58ef4f924cf2824ae198b1fec3eea1e4059a021c Author: Hartmut Hackmann Date: Fri Apr 27 12:31:12 2007 -0300 V4L/DVB (5314): Added support for tda827x tuners with preamlifiers This patch contains - new tuning code for the tda827xa silicon tuner. - controls the preamplifier of some boards with this tuner. - support for the Philips Tiger S hybrid DVB-T reference design. - reworked the saa7134-dvb modulue to get rid of most of the small board specific functions. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit de956c1e0f89413a3837b642d592e2dff3e3eb78 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:12 2007 -0300 V4L/DVB (5313): Added a config entry and a gpio function pointer to tuner struct These entries mainly are to support configurations of the tda827x silicon tuner with a preamplifier. The values can be set throgh the attach inform or through the extended TUNER_SET_TYPE_ADDR client call. The function pointer will only be updated if the parameter is not NULL. Since a typecast is necessary to set the pointer, i added a typedef for this pointer (tuner_gpio_func_t) in tuner.h Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit b8195946228c749702dfe5995b98516ea4b04cdc Author: Hartmut Hackmann Date: Fri Apr 27 12:31:11 2007 -0300 V4L/DVB (5312): Saa713x: added a GPIO handler function This function allows to set, clear and tristate the GPIO ports. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 1bb0e8667fab773d6c5a3d7caf506001deaeb7f5 Author: Hartmut Hackmann Date: Fri Apr 27 12:31:10 2007 -0300 V4L/DVB (5311): Tda1004x driver updates There are the following changes: - separate configuration of IF and GPIOs. - set GPIOs before firmware load. This helps to avoid I2C address collisions. - if desired invert GPIOs at sleep (automatic return to analog mode of card). - added 3 tuner configuration bytes to config stuct. - added i2c gate address to config struct. - moved _state struct declaration to header file to make it accessible on board layer. - added "conf_probed" to the state struct to allow i.e. probing for correct tuner version. - changed firmware load mechanism to always: + check if already loaded + try to boot from eeprom + try downlad from host - corrected name of tda10046 firmware image (backward compatible). Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 2435be11ae1afb64ac7dfb25e10b6e3037ab0522 Author: Hans Verkuil Date: Fri Apr 27 12:31:09 2007 -0300 V4L/DVB (5307): Add support for the cx23415 MPEG decoding features. The cx23415 adds some extra features that this DVB decoding API did not support. This API has been expanded to support the required features. Both source and binary backwards compatibility is kept intact by these changes. So existing applications are not affected. Signed-off-by: Hans Verkuil Signed-off-by: Ralph Metzler Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit 3434eb7e14d9587ee56f3462bcfa5726b62dadb9 Author: Hans Verkuil Date: Fri Apr 27 12:31:08 2007 -0300 V4L/DVB (5306): Add support for VIDIOC_G_CHIP_IDENT VIDIOC_G_CHIP_IDENT improves debugging of card problems: it can be used to detect which chips are on the board and based on that information selected register dumps can be made, making it easy to debug complicated media chips containing tens or hundreds of registers. This ioctl replaces the internal VIDIOC_INT_G_CHIP_IDENT ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit ced80c67cd1ed503c6fb72f02ac7342ab4ebf67a Author: Markus Rechberger Date: Fri Apr 27 12:31:07 2007 -0300 V4L/DVB (5299): Added support for loading cx88-dvb and cx88-blackbird Added support for loading cx88-dvb and cx88-blackbird Signed-off-by: Markus Rechberger Signed-off-by: Mauro Carvalho Chehab commit 77b7477467824098741351b6253a4ad292e28df9 Author: Markus Rechberger Date: Fri Apr 27 12:31:06 2007 -0300 V4L/DVB (5298): Added support for deferred module requesting to cx88 added support for deferred module requesting to cx88 Signed-off-by: Markus Rechberger Signed-off-by: Mauro Carvalho Chehab commit acf2821282bdd912f9ca46780879a21679429f85 Author: Mauro Carvalho Chehab Date: Fri Apr 27 12:31:06 2007 -0300 V4L/DVB (5297): Fix identation on tda10021.c Signed-off-by: Mauro Carvalho Chehab commit 5948e52cee0f2e72f8aaf0a78fec257f217efd88 Author: Adrian Bunk Date: Fri Apr 27 12:31:05 2007 -0300 V4L/DVB (5294): Make pvr2_encoder_prep_config() static Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab commit 3721929ed327ac2a6a879ad0b7dcbfc8ba2ae93f Author: Adrian Bunk Date: Fri Apr 27 12:31:05 2007 -0300 V4L/DVB (5293): Make dvb_usb_gl861_debug static Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Andrew Morton commit 045290b2a90ff1be60196a061aadecf70eb6bcc3 Author: Hans Verkuil Date: Fri Apr 27 12:31:04 2007 -0300 V4L/DVB (5290): Add support for VIDIOC_INT_G/S_STD_OUTPUT Added VIDIOC_INT_G_STD_OUTPUT and VIDIOC_INT_S_STD_OUTPUT to allow drivers to set the TV standard for video output separately from the video capture. This is needed for cx23415 support where the decoder is separate from the encoder and can have a different TV standard. Modified the saa7127 module to listen to VIDIOC_INT_G/S_STD_OUTPUT instead of VIDIOC_G/S_STD. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit b2787845fb91da18ebb079dc9297f92d990e9fe1 Author: Hans Verkuil Date: Fri Apr 27 12:31:02 2007 -0300 V4L/DVB (5289): Add support for video output overlays. Add V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY support. Also add support for local and global alpha overlays. Add new field enums V4L2_FIELD_INTERLACED_TB and V4L2_FIELD_INTERLACED_BT. These changes are needed to support the ivtv On Screen Display features. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 3bfb7398e2554fb54acb2900b81de144eb41c3ac Author: Adrian Bunk Date: Fri Apr 27 12:31:01 2007 -0300 V4L/DVB (5278): Bt8xx/: possible cleanups This patch contains the following possible cleanups: - remove the following unused global functions: - bttv-if.c: bttv_get_cardinfo() - bttv-if.c: bttv_get_id() - bttv-if.c: bttv_get_gpio_queue() - bttv-if.c: bttv_i2c_call() - remove the following unused EXPORT_SYMBOL's: - bttv-gpio.c: bttv_sub_bus_type - bttv-gpio.c: bttv_gpio_inout - bttv-gpio.c: bttv_gpio_read - bttv-gpio.c: bttv_gpio_write - bttv-gpio.c: bttv_gpio_bits Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab commit 206ebaf32795cf1582b1e2ff2ec6a560c9e986b8 Author: Hans Verkuil Date: Fri Apr 27 12:31:01 2007 -0300 V4L/DVB (5272): Add V4L2_CAP_VIDEO_OUTPUT_POS capability Add V4L2_CAP_VIDEO_OUTPUT_POS capability and x, y position coordinates to struct v4l2_pix_format. This is needed to support positioning the MPEG/YUV output of the cx23415. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 5eee72e88416ef11f55791626440ac3c9018c4c0 Author: Hans Verkuil Date: Fri Apr 27 12:31:00 2007 -0300 V4L/DVB (5268): Add support for three new MPEG controls. Added V4L2_CID_MPEG_AUDIO_MUTE, V4L2_CID_MPEG_VIDEO_MUTE and V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS controls together with their implementation in the cx2341x module. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit b7eee616ad8db5db5441a7d82083003df3ab6d3b Author: Antoine Jacquet Date: Fri Apr 27 12:30:59 2007 -0300 V4L/DVB (5257): USB: add zr364xx V4L2 driver This patch adds a V4L2 driver giving support for USB webcams based on the zr364xx chipsets. Signed-off-by: Antoine Jacquet Signed-off-by: Mauro Carvalho Chehab commit 404d5b185b4eb56d6fa2f7bd27833f8df1c38ce4 Author: Dan Williams Date: Thu Apr 26 00:12:10 2007 -0700 dev_dbg: check dev_dbg() arguments Duplicate what Zach Brown did for pr_debug in commit 8b2a1fd1b394c60eaa2587716102dd5e9b4e5990 [akpm@linux-foundation.org: fix a couple of things which broke] Signed-off-by: Dan Williams Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 61a2f59af6c8ffd9d6dd53f0da32563d4434e790 Author: Matthias Kaehlcke Date: Thu Apr 26 00:12:09 2007 -0700 drivers/base/attribute_container.c: use mutex instead of binary semaphore use mutex instead of binary semaphore in drivers/base/attribute_container.c Signed-off-by: Matthias Kaehlcke Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 240936e18b75937e7866934df723c2db0011d24f Author: Akinobu Mita Date: Thu Apr 26 00:12:09 2007 -0700 mod_sysfs_setup() doesn't return errno when kobject_add_dir() failure occurs mod_sysfs_setup() doesn't return an errno when kobject_add_dir() for module "holders" directory fails. So caller of mod_sysfs_setup() will keep going and get oops. Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit a53c46dc8253cc613ad66a2ca7aad6de8b7e61b9 Author: Johannes Berg Date: Thu Apr 26 11:43:58 2007 +0200 s2ram: add arch irq disable/enable hooks After some more discussion this patch replaces it: From: Johannes Berg Subject: suspend: add arch irq disable/enable hooks For powermac, we need to do some things between suspending devices and device_power_off, for example setting the decrementer. This patch allows architectures to define arch_s2ram_{en,dis}able_irqs in their asm/suspend.h to have control over this step. Signed-off-by: Johannes Berg Acked-by: Pavel Machek Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 075c1771526c85849ed22298d048bc07e400aee5 Author: David Brownell Date: Thu Apr 26 00:12:06 2007 -0700 define platform wakeup hook, use in pci_enable_wake() This defines a platform hook to enable/disable a device as a wakeup event source. It's initially for use with ACPI, but more generally it could be used whenever enable_irq_wake()/disable_irq_wake() don't suffice. The hook is called -- if available -- inside pci_enable_wake(); and the semantics of that call are enhanced so that support for PCI PME# is no longer needed. It can now work for devices with "legacy PCI PM", when platform support allows it. (That support would use some board-specific signal for for the same purpose as PME#.) [akpm@linux-foundation.org: Make it compile with CONFIG_PM=n] Signed-off-by: David Brownell Signed-off-by: Zhang Rui Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 057f6c019fff9ee290641d50647359bb8898918e Author: James Morris Date: Thu Apr 26 00:12:05 2007 -0700 security: prevent permission checking of file removal via sysfs_remove_group() Prevent permission checking from being performed when the kernel wants to unconditionally remove a sysfs group, by introducing an kernel-only variant of lookup_one_len(), lookup_one_len_kern(). Additionally, as sysfs_remove_group() does not check the return value of the lookup before using it, a BUG_ON has been added to pinpoint the cause of any problems potentially caused by this (and as a form of annotation). Signed-off-by: James Morris Cc: Nagendra Singh Tomar Cc: Tejun Heo Cc: Stephen Smalley Cc: Eric Paris Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 523ded71de0c5e66973335bf99a80edfda9f401b Author: Alan Stern Date: Thu Apr 26 00:12:04 2007 -0700 device_schedule_callback() needs a module reference This patch (as896b) fixes an oversight in the design of device_schedule_callback(). It is necessary to acquire a reference to the module owning the callback routine, to prevent the module from being unloaded before the callback can run. Signed-off-by: Alan Stern Cc: Satyam Sharma Cc: Neil Brown Cc: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit fa1a8c23eb7d3ded8a3c6d0e653339a2bc7fca9e Author: Cornelia Huck Date: Thu Apr 26 00:12:03 2007 -0700 s390: cio: Delay uevents for subchannels We often have the situation that we register a subchannel and start device recognition, only to find out that the device is not usable after all, which triggers an unregister of the subchannel. This often happens on hundreds of subchannels on a LPAR, leading to a storm of events which aren't of any use. Therefore, use uevent_suppress to delay the KOBJ_ADD uevent for a subchannel until we know that its ccw_device is to be registered. Signed-off-by: Cornelia Huck Cc: Kay Sievers Cc: Eric Rannaud Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 45cd8d8e1e56fba41b1e4fae988f40fe938045c2 Author: Andrew Morton Date: Thu Apr 26 00:12:10 2007 -0700 sysfs: bin.c printk fix fs/sysfs/bin.c: In function 'read': fs/sysfs/bin.c:77: warning: format '%zd' expects type 'signed size_t', but argument 4 has type 'int' Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit b2366d68d9ec3498b342507facf526c1f66ffa41 Author: Matthias Kaehlcke Date: Tue Apr 24 22:45:25 2007 +0200 Driver core: use mutex instead of semaphore in DMA pool handler the DMA pool handler uses a semaphore as mutex. use the mutex API instead of the (binary) semaphore Signed-off-by: Matthias Kaehlcke Signed-off-by: Greg Kroah-Hartman commit 4f6e1945fecad6ac8134e9fa68b7708e55690e9e Author: Greg Kroah-Hartman Date: Fri Apr 20 11:29:52 2007 -0700 driver core: bus_add_driver should return an error if no bus As pointed out by Dave Jones. Cc: Dave Jones Signed-off-by: Greg Kroah-Hartman commit 8447891fe845851738439788c74b3c811578e3f9 Author: Michael Ellerman Date: Tue Apr 17 15:59:36 2007 +1000 debugfs: Add debugfs_create_u64() I went to use this the other day, only to find it didn't exist. It's a straight copy of the debugfs u32 code, then s/u32/u64/. A quick test shows it seems to be working. Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman commit 3106d46f51a1a72fdbf071ebc0800a9bcfcbc544 Author: Adrian Bunk Date: Fri Apr 6 12:21:45 2007 +0200 the overdue removal of the mount/umount uevents This patch contains the overdue removal of the mount/umount uevents. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman commit 2753133eb3321feb81dcb9c88141dd2aa5973ee1 Author: Eric W. Biederman Date: Fri Apr 6 10:47:11 2007 -0600 kobject: Comment and warning fixes to kobject.c This dots some i's and crosses some t's after left over from when kobject_kset_add_dir was built from kobject_add_dir. Signed-off-by: Eric W. Biederman Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 22af74f3b221f1436a37554501431e51c06ee77e Author: Kay Sievers Date: Fri Apr 6 01:40:38 2007 +0200 Driver core: warn when userspace writes to the uevent file in a non-supported way In the future we will allow the uevent type to be written to the uevent file to trigger the different types of uevents. But for now, as we only support the ADD event, warn if userspace tries to write anything else to this file. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 16574dccd8f62dc1b585325f8a6a0aab10047ed8 Author: Kay Sievers Date: Fri Apr 6 01:40:38 2007 +0200 Driver core: make uevent-environment available in uevent-file This allows sysfs to show the environment variables that are available if the uevent happens. This lets userspace not have to cache all of this information as the kernel already knows it. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 4628803062d93dadc6ba8e801fd075526904a38c Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 kobject core: remove rwsem from struct subsystem It isn't used at all by the driver core anymore, and the few usages of it within the kernel have now all been fixed as most of them were using it incorrectly. So remove it. Now the whole struct subsys can be removed from the system, but that's for a later patch... Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 2f66858a0ae48faf4c8dc19042f3aff52208dc57 Author: Cornelia Huck Date: Tue Apr 17 13:01:38 2007 +0200 qeth: Remove usage of subsys.rwsem the current driver tree contains the removal of subsys.rwsem. Unfortunately, this breaks qeth. However, it should be no problem to fix the walking of the devices for /proc/qeth: No need to take subsys.rwsem during walking the devices, driver_find_devices() should already suffice. Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman commit 87aebe078e450795d336d20304d01095251ff9fa Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 PHY: remove rwsem use from phy core The subsystem rwsem is not used by the driver core at all, so the use of it in the phy code doesn't make any sense. They might possibly want to use a local lock, but I am unsure about that. Cc: netdev Signed-off-by: Greg Kroah-Hartman commit a2a0f74dc1e7d588b0ba6a1d177f42bef18117d0 Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 IEEE1394: remove rwsem use from ieee1394 core The subsystem rwsem is not used by the driver core at all, so the use of it in the ieee1394 code doesn't make any sense. They might possibly want to use a local lock, but as most of these operations are already protected by a local lock, it really doesn't look like it would be needed. Cc: Ben Collins Cc: Stefan Richter Cc: linux1394-devel Signed-off-by: Greg Kroah-Hartman commit c401110186cc0ce2f0e4a03695af115e6c177d95 Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 IDE: remove rwsem use from ide-proc core The subsystem rwsem is not used by the driver core at all, so the use of it in the ide-proc code of it doesn't make any sense. Perhaps a local lock might be needed, but I do not really think so. Cc: Bartlomiej Zolnierkiewicz Cc: linux ide Signed-off-by: Greg Kroah-Hartman commit 79580057de60867f535b7e7ec17c85709853d6bf Author: Dmitry Torokhov Date: Tue Apr 10 00:40:48 2007 -0400 Input: gameport - do not touch bus's rwsem The subsystem rwsem is not used by the driver core at all, so there is no point in trying to access it. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman commit 70f256fda1649fbb3deea37e86342f6139a0a82c Author: Dmitry Torokhov Date: Tue Apr 10 00:40:27 2007 -0400 Input: serio - do not touch bus's rwsem The subsystem rwsem is not used by the driver core at all, so there is no point in trying to access it. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman commit 75f1115c9b1a0c24d9025865285870122ec6f811 Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 PNP: stop using the subsystem rwsem The rwsem is not used to protect anything, so the use of it by the PNP subsystem isn't really useful, and it's doubtful if it really did anything or not. So I've removed it. Cc: Adam Belay Signed-off-by: Greg Kroah-Hartman commit 341487a837c02cbd674d4751061e7d098b0b8e98 Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 USB: remove use of the bus rwsem, as it doesn't really protect anything. The driver core stopped using the rwsem a long time ago, yet the USB core still grabbed the lock, thinking it protected something. This patch removes that useless use. Cc: Alan Stern Cc: Oliver Neukum Cc: David Brownell Cc: linux-usb-devel Signed-off-by: Greg Kroah-Hartman commit b7bb125dc3d0ca3f72c9116525ce0f018a9844e1 Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 SCSI: use the proper semaphore to protect the class lists SCSI was using the incorrect lock to protect walking the list of all devices in the class. This patch fixes this. Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman commit 49f019d66d056ebb261d261d7c89cb698f5eec18 Author: Greg Kroah-Hartman Date: Mon Apr 9 11:52:31 2007 -0400 Driver core: remove use of rwsem This lock is never used by the rest of the driver core, so the fact that we are grabbing it here means it isn't correct... Signed-off-by: Greg Kroah-Hartman commit 88db4721d47bc59b92d46de04e21c7975ea983f2 Author: Cornelia Huck Date: Tue Apr 10 14:35:27 2007 +0200 kobject: kobject_add() reference leak We leak a reference if we attempt to add a kobject with no name. Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman commit 14193fb91a7d88d3fe55d3160892edeb2b02e0c2 Author: John Anthony Kazos Jr Date: Wed Apr 4 07:39:17 2007 -0400 Kobject: kobject_uevent.c: Collapse unnecessary loop nesting (top_kobj) Collapses a do..while() loop within an if() to a simple while() loop for simplicity and readability. Signed-off-by: John Anthony Kazos Jr. Signed-off-by: Greg Kroah-Hartman commit f89cbc399ecd924c4bd879344e662aace2274b4f Author: Dmitry Torokhov Date: Tue Apr 3 01:08:40 2007 -0400 Driver core: add suspend() and resume() to struct device_type Driver core: add suspend() and resume() to struct device_type In cases when there are devices of different types in the same class we can't use class's implementation of suspend and resume methods and we need to add them to struct device_type instead. Also fix error handling in resume code (we should not try to call class's resume method iof bus's resume method for the device failed. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman commit bf62456eb91f3d2ef0736081583d09b0b3c8b7ea Author: Eric Rannaud Date: Fri Mar 30 22:23:12 2007 -0700 uevent: use add_uevent_var() instead of open coding it Make use of add_uevent_var() instead of (often incorrectly) open coding it. Signed-off-by: Michael Ellerman Signed-off-by: Eric Rannaud Cc: Kay Sievers Cc: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit bdc4960a0b4831a24276b65f1f7afdfc57f2f5cf Author: Cornelia Huck Date: Thu Mar 29 11:12:14 2007 +0200 Driver core: switch firmware_class to uevent_suppress. Use uevent_suppress instead of returning an error code in firmware_uevent(). Get rid of the now unneeded FW_STATUS_READY and FW_STATUS_READY_NOHOTPLUG. Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman commit 83b5fb4cce13b9f7b218ed88ee05387c3f3bdda3 Author: Cornelia Huck Date: Thu Mar 29 11:12:11 2007 +0200 Driver core: suppress uevents via filter Suppress uevents for devices if uevent_suppress is set via dev_uevent_filter(). This makes the driver core suppress all device uevents, not just the add event in device_add(). Signed-off-by: Cornelia Huck Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit ca2f37dbc5324c7278577731033a358f1f86050a Author: Jean Tourrilhes Date: Wed Mar 7 10:49:30 2007 -0800 Driver core: notify userspace of network device renames Provide rename event for when we rename network devices. Signed-off-by: Jean Tourrilhes Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 1b0b3b9980e482ab7c603430462538334f69f14a Author: Oliver Neukum Date: Mon Apr 2 14:47:59 2007 +0200 kref: fix CPU ordering with respect to krefs some atomic operations are only atomic, not ordered. Thus a CPU is allowed to reorder memory references to an object to before the reference is obtained. This fixes it. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman commit 74e9f5fa1570f956c96dd5d3f1053daedbbf01a0 Author: Greg Kroah-Hartman Date: Tue Apr 9 12:14:34 2002 -0700 Driver core: remove unneeded completion from driver release path The completion in the driver release path is due to ancient history in the _very_ early 2.5 days when we were not tracking the module reference count of attributes. It is not needed at all and can be removed. Note, we now have an empty release function for the driver structure. This is due to the fact that drivers are statically allocated in the system at this point in time, something which I want to change in the future. But remember, drivers are really code, which is reference counted by the module, unlike devices, which are data and _must_ be reference counted properly in order to work correctly. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit c6a46696f97ff260a4ecce5e287f8de4b9d7fe14 Author: Cornelia Huck Date: Mon Feb 5 16:15:26 2007 -0800 driver core: don't fail attaching the device if it cannot be bound Don't fail bus_attach_device() if the device cannot be bound. If dev->driver has been specified, reset it to NULL if device_bind_driver() failed and add the device as an unbound device. As a result, bus_attach_device() now cannot fail, and we can remove some checking from device_add(). Also remove an unneeded check in bus_rescan_devices_helper(). Signed-off-by: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit eed40d3ad2ba652c08422d62a5ff6f62ac0be16d Author: Andrew Morton Date: Mon Feb 5 16:15:25 2007 -0800 powerpc: make it compile for multithread change arch/powerpc/kernel/of_platform.c:479: error: unknown field `multithread_probe' specified in initializer Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 21c7f30b1d3f8a3de3128478daca3ce203fc8733 Author: Cornelia Huck Date: Mon Feb 5 16:15:25 2007 -0800 driver core: per-subsystem multithreaded probing Make multithreaded probing work per subsystem instead of per driver. It doesn't make much sense to probe the same device for multiple drivers in parallel (after all, only one driver can bind to the device). Instead, create a probing thread for each device that probes the drivers one after another. Also make the decision to use multi-threaded probe per bus instead of per device and adapt the pci code. Signed-off-by: Cornelia Huck Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 460f7e9a1bde2c74f060f7ce0a308dab4be6a56b Author: Dmitriy Monakhov Date: Sat Mar 10 14:00:10 2007 +0300 kobject: kobject_shadow_add cleanup - correct function name in comments - parrent assignment does metter only inside "if" block, so move it inside this block. Signed-off-by: Monakhov Dmitriy Signed-off-by: Greg Kroah-Hartman commit 414264f959cf46f49f974b3510400e12ac3624a6 Author: Kay Sievers Date: Mon Mar 12 21:08:57 2007 +0100 Driver core: add name to device_type If "name" of a device_type is specified, the uevent will contain the device_type name in the DEVTYPE variable. This helps userspace to distingiush between different types of devices, belonging to the same subsystem. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 621a1672f7377e08a942f205d6742d8af1292aab Author: Dmitry Torokhov Date: Sat Mar 10 01:37:34 2007 -0500 driver core: Use attribute groups in struct device_type Driver core: use attribute groups in struct device_type Attribute groups are more flexible than attribute lists (an attribute list can be represented by anonymous group) so switch struct device_type to use them. Also rework attribute creation for devices so that they all cleaned up properly in case of errors. Signed-off-by: Dmitry Torokhov Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit b8c5cec23d5c33b767a1cddebd4f8813a9563e3c Author: Kay Sievers Date: Fri Feb 16 17:33:36 2007 +0100 Driver core: udev triggered device-<>driver binding We get two per-bus sysfs files: ls-l /sys/subsystem/usb drwxr-xr-x 2 root root 0 2007-02-16 16:42 devices drwxr-xr-x 7 root root 0 2007-02-16 14:55 drivers -rw-r--r-- 1 root root 4096 2007-02-16 16:42 drivers_autoprobe --w------- 1 root root 4096 2007-02-16 16:42 drivers_probe The flag "drivers_autoprobe" controls the behavior of the bus to bind devices by default, or just initialize the device and leave it alone. The command "drivers_probe" accepts a bus_id and the bus tries to bind a driver to this device. Systems who want to control the driver binding with udev, switch off the bus initiated probing: echo 0 > /sys/subsystem/usb/drivers_autoprobe echo 0 > /sys/subsystem/pcmcia/drivers_autoprobe ... and initiate the probing with udev rules like: ACTION=="add", SUBSYSTEM=="usb", ATTR{subsystem/drivers_probe}="$kernel" ACTION=="add", SUBSYSTEM=="pcmcia", ATTR{subsystem/drivers_probe}="$kernel" ... Custom driver binding can happen in earlier rules by something like: ACTION=="add", SUBSYSTEM=="usb", \ ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678" \ ATTR{subsystem/drivers//bind}="$kernel" This is intended to solve the modprobe.conf mess with "install-rules", custom bind/unbind-scripts and all the weird things people invented over the years. It should also provide the functionality "libusual" was supposed to do. With udev, one can just write a udev rule to drive all USB-disks at the third port of USB-hub by the "ub" driver, and everything else by usb-storage. One can also instruct udev to bind different wireless drivers to identical cards - just selected by the pcmcia slot-number, and whatever ... To use the mentioned rules, it needs udev version 106, to be able to write ATTR{}="$kernel" to sysfs files. Signed-off-by: Greg Kroah-Hartman commit a456b7023e0abf80bb03b0bdf5471b48878e5c49 Author: Jean Delvare Date: Fri Mar 9 16:33:10 2007 +0100 dev_printk and new-style class devices As the new-style class devices (as opposed to old-style struct class_device) are becoming more widely used, I noticed that the dev_printk-based functions are not working properly with these. New-style class devices have no driver nor bus, almost by definition, and as a result dev_driver_string(), which is used as the first parameter of dev_printk, resolves to an empty string. This causes entries like the following to show in my logs: i2c-2: adapter [SMBus stub driver] registered Notice the unaesthetical leading whitespace. In order to fix this problem, I suggest that we extend dev_driver_string to deal with new-style class devices: Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman commit 864062457a2e444227bd368ca5f2a2b740de604f Author: Kay Sievers Date: Wed Mar 14 03:25:56 2007 +0100 driver core: fix namespace issue with devices assigned to classes - uses a kset in "struct class" to keep track of all directories belonging to this class - merges with the /sys/devices/virtual logic. - removes the namespace-dir if the last member of that class leaves the directory. There may be locking or refcounting fixes left, I stopped when it seemed to work with network and sound modules. :) From: Kay Sievers Signed-off-by: Greg Kroah-Hartman commit 00ed8e3dda47f8421b11da17e353d7db8c878121 Author: Dmitriy Monakhov Date: Sun Mar 11 15:36:19 2007 +0300 driver core: fix device_add error path - At the moment we jump here device was't added to dev->class->devices list yet. Signed-off-by: Monakhov Dmitriy Signed-off-by: Greg Kroah-Hartman commit 39bc89fd4019b164002adaacef92c4140e37955a Author: Ingo Molnar Date: Wed Apr 25 20:50:03 2007 -0700 make SysRq-T show all tasks again show_state() (SysRq-T) developed the buggy habbit of not showing TASK_RUNNING tasks. This was due to the mistaken belief that state_filter == -1 would be a pass-through filter - while in reality it did not let TASK_RUNNING == 0 p->state values through. Fix this by restoring the original '!state_filter means all tasks' special-case i had in the original version. Test-built and test-booted on i686, SysRq-T now works as intended. Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit 20f09390b2da2432309afe8aaa0bd64ec64c4584 Author: Daniel Walker Date: Thu Apr 26 09:46:05 2007 -0700 seqlocks: trivial remove weird whitespace Signed-off-by: Daniel Walker Signed-off-by: Linus Torvalds commit 07db59bd6b0f279c31044cba6787344f63be87ea Author: Linus Torvalds Date: Fri Apr 27 09:10:47 2007 -0700 Change default dirty-writeback limits Do this really early in the 2.6.22-rc series, so that we'll get feedback. And don't change by half measures. Just cut the default dirty limit to a quarter of what it was, and see if anybody even notices. Signed-off-by: Linus Torvalds commit 14cf232ab161ce87ca538af3daad5f717c20d487 Author: Franck Bui-Huu Date: Thu Apr 26 00:20:15 2007 -0700 [MIPS] Don't force frame pointers for lockdep on MIPS Stacktrace support on MIPS doesn't use frame pointers. Since this option considerably increases the size of the kernel code, force lockdep to not use it. Signed-off-by: Franck Bui-Huu Signed-off-by: Ralf Baechle commit c4be17370b76bc6c23a239ca512fe360785d2369 Author: Yoichi Yuasa Date: Thu Apr 26 19:53:59 2007 +0900 [MIPS] update vr41xx Kconfig This patch has updated vr41xx/Kconfig. Signed-off-by: Yoichi Yuasa Signed-off-by: Ralf Baechle commit 678f4e34a66eb364f4f8e7dc82746c3df14dc1e8 Author: Yoichi Yuasa Date: Thu Apr 26 19:51:31 2007 +0900 [MIPS] remove 2 select entries for VR41xx This patch has removed 2 select entries for VR41xx. These entries are selected in arch/mips/vr41xx/Kconfig. Signed-off-by: Yoichi Yuasa Signed-off-by: Ralf Baechle commit 74142d65b23b46587ea329202e957c901d9a57a1 Author: Yoichi Yuasa Date: Thu Apr 26 19:45:09 2007 +0900 [MIPS] rename VR41XX to VR4100 series This patch has renamed VR41XX to VR4100 series. That's better. Signed-off-by: Yoichi Yuasa Signed-off-by: Ralf Baechle commit 820c229f6c82bb91c0dbfbce90f7e6eb9639c7ab Author: Milind Arun Choudhary Date: Thu Apr 19 15:05:16 2007 +0530 [MIPS] Use DEFINE_SPINLOCK instead of SPIN_LOCK_UNLOCKED. Signed-off-by: Milind Arun Choudhary Signed-off-by: Ralf Baechle commit a4c9bb7d22aa61ec585984f46df185669e557e3d Author: Robert P. J. Day Date: Tue Apr 10 06:23:27 2007 -0400 [MIPS] Replace old fashioned "__typeof" with "__typeof__". [Robert's original log message said this was a bug but it isn't, it's just very old fashioned syntax that is not (no longer?) documented in the gcc documentation. So for the sake of uniformity I'm applying his patch but with a modified log message. -- Ralf] Signed-off-by: Robert P. J. Day Signed-off-by: Ralf Baechle commit 05bc284a719b778243f51e23c88fe6cefe6b219b Author: Ralf Baechle Date: Thu Apr 26 15:46:28 2007 +0100 [MIPS] Remove unused _THREAD_SIZE_ORDER from asm-offset.c. Signed-off-by: Ralf Baechle commit bea771751c116a690054581902b4144fe5a4520e Author: Thomas Bogendoerfer Date: Sun Apr 8 13:34:57 2007 +0200 [MIPS] Change PCI host bridge setup/resources PCI host bridge setup for SNI RM machines with PCI is quite broken, now that Linux does it's resource setup own its own. It will use IO addresses, which are needed by the EISA config detection and assigns PCI memory addresses, which overlap with ISA legacy addresses (video ram). Below is a patch, which changes the way how the PCI memory addresses are used and sets the minimum IO address to give enough IO space for 8 EISA slots). This patch needs the other PCI resource change, I've posted. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Ralf Baechle commit 639702bd725b3cc1a9bd442a7822c83849d66e91 Author: Thomas Bogendoerfer Date: Sun Apr 8 13:28:44 2007 +0200 [MIPS] Register PCI host bridge resource earlier PCI based SNI RM machines have their EISA bus behind an Intel PCI/EISA bridge. So the PCI IO range must start at 0x0000. Changing that will break the PCI bus, because i8259.c already has registered it's IO addresses before the PCI bus gets initialized. Below is a patch, which will register the PCI host bridge resources inside register_pci_controller(). It also changes i8259.c to use insert_region(), because request_resource() will fail, if the IO space of the PIT hanging of the PCI host bridge (maybe passing the resource parent to init_i8259_irqs() is a cleaner fix for that). Signed-off-by: Thomas Bogendoerfer Signed-off-by: Ralf Baechle commit 3c5e370600c2dda8a4f59f841f323df04e6ce7b2 Author: Yoichi Yuasa Date: Wed Apr 4 16:40:52 2007 +0900 [MIPS] Remove pnx8550-v2pci_defconfig Signed-off-by: Yoichi Yuasa Signed-off-by: Ralf Baechle commit d619f38fdacb5cec0c841798bbadeaf903868852 Author: Mark Mason Date: Thu Mar 29 11:39:56 2007 -0700 [MIPS] Add bcm1480 ZBus trace support, fix wait related bugs Make ZBus tracing generic - moving it to a common direcotry under arch/mips/sibyte, add bcm1480 support and fix some wait related bugs (thanks to Ralf for assistance on that). Signed-off-by: Mark Mason Signed-off-by: Ralf Baechle commit 8deab1144b553548fb2f1b51affdd36dcd652aaa Author: Mark Mason Date: Wed Mar 28 14:40:25 2007 -0700 [MIPS] Updated Sibyte headers This is an update to the earlier patch for the sibyte headers, and superceeds the previous patch. Changes were necessary to get the tbprof driver working on the bcm1480. Patch to update Sibyte header files to match master versions maintained at Broadcom. This patch also corrects some whitespace problems, and (hopefully) shouldn't introduce any new ones. Signed-off-by: Mark Mason Signed-off-by: Ralf Baechle commit eacb9d61919db56482dcea7ec943c9508175dc16 Author: Ralf Baechle Date: Thu Apr 26 15:46:25 2007 +0100 [MIPS] Remove unused argument from kunmap_coherent(). Signed-off-by: Ralf Baechle commit e3cf10e93f42171224e88b06c226d450f3b3a55f Author: Ralf Baechle Date: Thu Apr 26 15:46:25 2007 +0100 [MIPS] Malta: Delete unused prototype of mips_timer_interrupt. Signed-off-by: Ralf Baechle commit 05502339332564ffd545be9ca37b208296a2eaad Author: Atsushi Nemoto Date: Wed Mar 21 00:36:02 2007 +0900 [MIPS] Select ZONE_DMA only if GENERIC_ISA_DMA selected Signed-off-by: Atsushi Nemoto Signed-off-by: Ralf Baechle commit f197465384bf7ef1af184c2ed1a4e268911a91e3 Author: Ralf Baechle Date: Thu Apr 26 15:46:24 2007 +0100 [MIPS] MIPS Tech: Get rid of volatile in core code. Signed-off-by: Ralf Baechle commit 78709b9df35346965b214e0e548412748d147776 Author: Ralf Baechle Date: Thu Apr 26 15:46:24 2007 +0100 [MIPS] IP22: Get rid of volatile in IP22 core code. Signed-off-by: Ralf Baechle commit 2127435e57a15f1fea8d6969e264eeb05b28ba4b Author: Atsushi Nemoto Date: Thu Mar 15 00:58:28 2007 +0900 [MIPS] JMR3927 cleanup * Kill dead codes * Rearrange irq chip handlers * Minimize defconfig Signed-off-by: Atsushi Nemoto Signed-off-by: Ralf Baechle commit 252161eccd1a44f32a506d0fedb424d4ff84e4dc Author: Yoichi Yuasa Date: Wed Mar 14 21:51:26 2007 +0900 [MIPS] merge GT64111 PCI routines and GT64120 PCI_0 routines This patch has merged GT64111 PCI routines and GT64120 PCI_0 routines. GT64111 PCI is almost the same as GT64120's PCI_0. This patch don't change GT64120 PCI routines. Signed-off-by: Yoichi Yuasa Signed-off-by: Ralf Baechle commit 2a9effc67804102d6d5182eb0116520588ae2256 Author: Yoichi Yuasa Date: Mon Mar 5 19:10:03 2007 +0900 [MIPS] Cobalt: Split PCI codes from setup.c It's removed #ifdef CONFIG_PCI/#endif from cobalt setup.c . Signed-off-by: Yoichi Yuasa Signed-off-by: Ralf Baechle commit cc50b67dcd84c6215232c0e1c95e24786e555782 Author: Yoichi Yuasa Date: Tue Mar 6 21:34:44 2007 +0900 [MIPS] Cobalt: clean up include files Signed-off-by: Yoichi Yuasa commit 7f5a7716dc0b380fd3c85ca5a5841969555feaa7 Author: Ralf Baechle Date: Wed Apr 25 15:08:57 2007 +0100 [MIPS] Fix AP/SP to work in the reality of modern kernels. Signed-off-by: Ralf Baechle commit 78ab67da1002d954ea4c3e2b441e2483c41f94e8 Author: Knobloch, Thomas Date: Fri Apr 27 13:19:36 2007 +0200 [MTD] [NAND] Wrong calculation of page number in nand_block_bad() In case that there is no memory based bad block table available the function nand_block_checkbad() in drivers/mtd/nand/nand_base.c will call nand_block_bad() directly. When parameter 'getchip' is set to zero, nand_block_bad() will not right shift the offset to calculate the correct page number. Signed-off-by: Thomas Knobloch Signed-off-by: David Woodhouse commit cb629a01bb5bca951287e761c590a5686c6ca416 Author: Milind Arun Choudhary Date: Fri Apr 27 16:02:01 2007 +0200 [S390] SPIN_LOCK_UNLOCKED cleanup in drivers/s390 SPIN_LOCK_UNLOCKED cleanup,use __SPIN_LOCK_UNLOCKED instead. Signed-off-by: Milind Arun Choudhary Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky commit 39ce010d38bf6703b49f59eb73bef030b1d659f2 Author: Heiko Carstens Date: Fri Apr 27 16:02:00 2007 +0200 [S390] Clean up smp code in preparation for some larger changes. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 9ff6f4577e69801a43c0d58606a80040aecbc4bc Author: Martin Schwidefsky Date: Fri Apr 27 16:01:59 2007 +0200 [S390] Remove debugging junk. arch/s390/appldata/appldata_base.c has some confusing debugging code left over to allow compiling it as a module. In practice, it cannot be configured as module and there is no need to keep that code. Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky commit ecdcc0234b27472b561378ac59e2beeea06ec6ff Author: Martin Schwidefsky Date: Fri Apr 27 16:01:58 2007 +0200 [S390] Switch etr from tasklet to workqueue. The clock synchronization of the ETR code requires an smp_call_function to synchronize all cpus. Calling smp_call_function from a tasklet is illegal. Replace the tasklet with a job on the global workqueue. ETR work is rare and can be postponed to a be done by a kernel thread. Signed-off-by: Martin Schwidefsky commit 6c210482ae4a9a5bb9377ad250feaacec3faa3cd Author: Martin Schwidefsky Date: Fri Apr 27 16:01:57 2007 +0200 [S390] split page_test_and_clear_dirty. The page_test_and_clear_dirty primitive really consists of two operations, page_test_dirty and the page_clear_dirty. The combination of the two is not an atomic operation, so it makes more sense to have two separate operations instead of one. In addition to the improved readability of the s390 version of SetPageUptodate, it now avoids the page_test_dirty operation which is an insert-storage-key-extended (iske) instruction which is an expensive operation. Signed-off-by: Martin Schwidefsky commit 2fc2d1e9ffcde78af7ab63ed640d9a4901797de2 Author: Heiko Carstens Date: Fri Apr 27 16:01:56 2007 +0200 [S390] Processor degradation notification. Generate uevents for all cpus if cpu capability changes. This can happen e.g. because the cpus are overheating. The cpu capability can be read via /sys/devices/system/cpu/cpuN/capability. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit db77aa5f3d01fe6a6cc629dbd37936b1fdd129ba Author: Jan Glauber Date: Fri Apr 27 16:01:55 2007 +0200 [S390] vtime: cleanup per_cpu usage. Replace per_cpu(... , smp_processor_id()) with __get_cpu_var() Signed-off-by: Jan Glauber Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit 131a395c18af43d824841642038e5cc0d48f0bd2 Author: Jan Glauber Date: Fri Apr 27 16:01:54 2007 +0200 [S390] crypto: cleanup. Cleanup code and remove obsolete documentation. Signed-off-by: Jan Glauber Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit 6d4740c89c187ee8f5ac7355c4eeffda26493d1f Author: Stefan Haberland Date: Fri Apr 27 16:01:53 2007 +0200 [S390] sclp: fix coding style. Use only capital letters for defines. Cc: Peter Oberparleiter Signed-off-by: Stefan Haberland Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit 66b494a7178cbd84d8fc0e5f1e92d81fb6ec9f6e Author: Ursula Braun Date: Fri Apr 27 16:01:52 2007 +0200 [S390] vmlogrdr: stop IUCV connection in vmlogrdr_release. Reopen of /dev/account failed. The IUCV path has to be terminated in vmlogrdr_release. Signed-off-by: Ursula Braun Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit b3d00c3b9278876b84a808bc513048b145fdef90 Author: Peter Oberparleiter Date: Fri Apr 27 16:01:51 2007 +0200 [S390] sclp: initialize early. Add explicit sclp initialization for those sclp users that do not register with the interface. Signed-off-by: Peter Oberparleiter Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit b7127dfeed3252a76aa31f016aac5fba53d99711 Author: Ahmed S. Darwish Date: Fri Apr 27 16:01:50 2007 +0200 [S390] ctc: kmalloc->kzalloc/casting cleanups. A patch for the CTC / ESCON network driver. Switch from kmalloc to kzalloc when appropriate, remove some unnecessary kmalloc casts too. Since I have no s390 machine, I didn't compile it but I examined it carefully. Signed-off-by: "Ahmed S. Darwish" Cc: Frank Pavlic Cc: Ursula Braun Signed-off-by: Andrew Morton Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit 411ed3225733dbd83b4cbaaa992ef80d6ec1534e Author: Michael Holzheu Date: Fri Apr 27 16:01:49 2007 +0200 [S390] zfcpdump support. s390 machines provide hardware support for creating Linux dumps on SCSI disks. For creating a dump a special purpose dump Linux is used. The first 32 MB of memory are saved by the hardware before the dump Linux is booted. Via an SCLP interface, the saved memory can be accessed from Linux. This patch exports memory and registers of the crashed Linux to userspace via a debugfs file. For more information refer to Documentation/s390/zfcpdump.txt, which is included in this patch. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 7039d3a11c4b4b59f9ef933b4b0a28304bdd07d1 Author: Peter Oberparleiter Date: Fri Apr 27 16:01:48 2007 +0200 [S390] dasd: Add ipldev parameter. Specifying 'ipldev' in the dasd= kernel parameter will automatically activate the boot device for use by the dasd driver. Signed-off-by: Peter Oberparleiter Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit 4dfd5c4593e69e9d399dd9e01d184dc534408f7e Author: Horst Hummel Date: Fri Apr 27 16:01:47 2007 +0200 [S390] dasd: Add sysfs attribute status and generate uevents. This patch adds a sysfs-attribute 'status' to make the DASD device-status accessible from user-space. In addition, the DASD driver generates an uevent(CHANGE) for the ccw-device on each device-status change. This enables user-space applications (e.g. udev) to do related processing. Signed-off-by: Horst Hummel Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit be7962856d299a0f231ac36f89f4a89cbecfe0ff Author: Martin Schwidefsky Date: Fri Apr 27 16:01:46 2007 +0200 [S390] Improved kernel stack overflow checking. Recent cvs versions of gcc have support for an improved stack overflow checking that calculates the size of the guard size for each function. If the compiler accepts -mstack-size without -mstack-guard then the new stack check is available. We always want to use the new stack checker. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 60691d3c2c0fe9ecc264741ff41f283fef579b8a Author: Heiko Carstens Date: Fri Apr 27 16:01:45 2007 +0200 [S390] Get rid of console setup functions. We get this: Section mismatch: reference to .init.text:con3270_consetup from .data between 'con3270' (at offset 0x45c8) and 'con3270_fn' Section mismatch: reference to .init.text:con3215_consetup from .data between 'con3215' (at offset 0x4678) and 'raw3215_ccw_driver' Since there is no difference between a non present console setup function and one that returns only 0 remove them. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit be5ec363e958982454ac9b3138b0e78c032e758d Author: Martin Schwidefsky Date: Fri Apr 27 16:01:44 2007 +0200 [S390] No execute support cleanup. Simplify the signal_return function that checks for the two special system calls sigreturn and rt_sigreturn. No need to do a page table walk, a call to copy_from_user while disabled page faults will work as well. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 10c1031f706bbe0690d84cdbccad15b11c6dc661 Author: Martin Schwidefsky Date: Fri Apr 27 16:01:43 2007 +0200 [S390] Minor fault path optimization. The minor fault path has grown a lot in terms of cycles. In particular the kprobes hook is very costly. Optimize the path to save a couple of cycles. If kprobes is enabled more than 300 cycles can be avoided if kprobes_running() is false. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit c0007f1a65762eaf55633d403b380130ec60adad Author: Heiko Carstens Date: Fri Apr 27 16:01:42 2007 +0200 [S390] Use generic bug. Generic bug implementation for s390. Will increase the value of the console output on BUG() statements since registers r0-r5,r14 will not be clobbered by a printk() call that was previously done before the illegal instruction of BUG() was hit. Also implements an architecture specific WARN_ON(). Output of that could be increased but requires common code change. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit bb11e3bdbac08f773a89f3ca287024a956ee8a12 Author: Martin Schwidefsky Date: Fri Apr 27 16:01:41 2007 +0200 [S390] Improved oops output. This patch adds two improvements to the oops output. First it adds an additional line after the PSW which decodes the different fields of it. Second a disassembler is added that decodes the instructions surrounding the faulting PSW. The output of a test oops now looks like this: kernel BUG at init/main.c:419 illegal operation: 0001 [#1] CPU: 0 Not tainted Process swapper (pid: 0, task: 0000000000464968, ksp: 00000000004be000) Krnl PSW : 0700000180000000 00000000000120b6 (rest_init+0x36/0x38) R:0 T:1 IO:1 EX:1 Key:0 M:0 W:0 P:0 AS:0 CC:0 PM:0 EA:3 Krnl GPRS: 0000000000000003 00000000004ba017 0000000000000022 0000000000000001 000000000003a5f6 0000000000000000 00000000004be6a8 0000000000000000 0000000000000000 00000000004b8200 0000000000003a50 0000000000008000 0000000000516368 000000000033d008 00000000000120b2 00000000004bdee0 Krnl Code: 00000000000120a6: e3e0f0980024 stg %r14,152(%r15) 00000000000120ac: c0e500014296 brasl %r14,3a5d8 00000000000120b2: a7f40001 brc 15,120b4 >00000000000120b6: 0707 bcr 0,%r7 00000000000120b8: eb7ff0500024 stmg %r7,%r15,80(%r15) 00000000000120be: c0d000195825 larl %r13,33d108 00000000000120c4: a7f13f00 tmll %r15,16128 00000000000120c8: a7840001 brc 8,120ca Call Trace: ([<00000000000120b2>] rest_init+0x32/0x38) [<00000000004be614>] start_kernel+0x37c/0x410 [<0000000000012020>] _ehead+0x20/0x80 Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 03ff9a235a0602724fc54916469b6e0939c62c9b Author: Martin Schwidefsky Date: Fri Apr 27 16:01:40 2007 +0200 [S390] System call cleanup. Remove system call glue for sys_clone, sys_fork, sys_vfork, sys_execve, sys_sigreturn, sys_rt_sigreturn and sys_sigaltstack. Call do_execve from kernel_execve directly, move pt_regs to the right place and branch to sysc_return to start the user space program. This removes the last in-kernel system call. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit ef99516c9646802c3d38c3eb83de302e05b3c1b5 Author: Cornelia Huck Date: Fri Apr 27 16:01:39 2007 +0200 [S390] cio: Unregister ccw devices directly. We used to unregister ccw devices not directly from the I/O subchannel remove function in order to avoid lifelocks on the css bus semaphore. This semaphore is gone, and there is no reason to not unregister the ccw device directly (it is even better since it is more in keeping with the goal of immediate disconnect). Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky commit 8c4941c53b14e5a08ed2f270e9f087b410a9abcc Author: Cornelia Huck Date: Fri Apr 27 16:01:38 2007 +0200 [S390] cio: cm_enable memory leak. We allocage two pages when channel path measurements are enabled via cm_enable. We must not forget to free them again when channel path measurements are disabled again. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky commit d76123eb357a4baa653714183df286c1bb99f707 Author: Cornelia Huck Date: Fri Apr 27 16:01:37 2007 +0200 [S390] cio: ccwgroup register vs. unregister. Introduce a mutex for struct ccwgroup to prevent simuntaneous register/unregister on the same ccwgroup device. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky commit 82b7ac058f60e0c92f9237fbaf440671f437ecdf Author: Cornelia Huck Date: Fri Apr 27 16:01:36 2007 +0200 [S390] cio: Dont call css_update_ssd_info from interrupt context. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky commit 7ad6a24970325294a22a08446d473384c15b928e Author: Peter Oberparleiter Date: Fri Apr 27 16:01:35 2007 +0200 [S390] cio: fix subchannel channel-path data usage Ensure that channel-path related subchannel data is only retrieved and used when it is valid and that it is updated when it may have changed. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky commit 83b3370c79b91b9be3f6540c3c914e689134b45f Author: Peter Oberparleiter Date: Fri Apr 27 16:01:34 2007 +0200 [S390] cio: replace subchannel evaluation queue with bitmap Use a bitmap for indicating which subchannels require evaluation instead of allocating memory for each evaluation request. This approach reduces memory consumption during recovery in case of massive evaluation request occurrence and removes the need for memory allocation failure handling. Cc: Heiko Carstens Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky commit 387b734fc2b55f776b192c7afdfd892ba42347d4 Author: Stefan Bader Date: Fri Apr 27 16:01:33 2007 +0200 [S390] cio: Re-start path verification after aborting internal I/O. Path verification triggered by changes to the available CHPIDs will be interrupted by another change but not re-started. This results in an invalid path mask. To solve this make sure to completely re-start path verification when changing the available paths. Signed-off-by: Stefan Bader Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit cfbe9bb2fb5de1da58d351432a9465c22d6d3ee5 Author: Cornelia Huck Date: Fri Apr 27 16:01:32 2007 +0200 [S390] cio: Use add_uevent_var. Convert ccw_uevent to use add_uevent_var and adapt snprint_alias. Signed-off-by: Cornelia Huck Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit e5854a5839fa426a7873f038080f63587de5f1f1 Author: Peter Oberparleiter Date: Fri Apr 27 16:01:31 2007 +0200 [S390] cio: Channel-path configure function. Add a new attribute to the channel-path sysfs directory through which channel-path configure operations can be triggered. Also listen for hardware events requesting channel-path configure operations and process them accordingly. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit f5ba6c863617c15d22cce5f8666ff4c832773025 Author: Cornelia Huck Date: Fri Apr 27 16:01:30 2007 +0200 [S390] cio: Clean up online_store. Detangle the online_store code and make it more readable. Signed-off-by: Cornelia Huck Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky commit c9182e0f42c5646e670c2166b6d6638052d574af Author: Peter Oberparleiter Date: Fri Apr 27 16:01:29 2007 +0200 [S390] cio: observe chpid valid flag Check validity flag of CHPID description data before continuing with channel-path initialization. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky commit e6b6e10ac1de116fc6d2288f185393014851cccf Author: Peter Oberparleiter Date: Fri Apr 27 16:01:28 2007 +0200 [S390] cio: Introduce separate files for channel-path related code. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit d120b2a4e60cc9e62e7cc5dcf049100af3745cc4 Author: Peter Oberparleiter Date: Fri Apr 27 16:01:27 2007 +0200 [S390] cio: Allow 0 and 1 as input for channel path status attribute. Channel path status can now be modified by writing '0' and '1' to the sysfs status attribute in addition to 'offline' and 'online' respectively. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit f86635fad14c4a6810cf0e08488fc9129a3b3b32 Author: Peter Oberparleiter Date: Fri Apr 27 16:01:26 2007 +0200 [S390] cio: Introduce struct chp_id. Introduce data type for channel-path IDs. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 6fc321fd7dd91f0592f37503219196835314fbb7 Author: Heiko Carstens Date: Fri Apr 27 16:01:25 2007 +0200 [S390] cio/ipl: Clean interface between cio and ipl code. Clean interface between cio and ipl code, so Peter stops complaining. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 29c380f5f06d0c5a320b9bb6f8987065e7b81c91 Author: Heiko Carstens Date: Fri Apr 27 16:01:04 2007 +0200 [S390] memory detection: stop at first memory hole. If both sclp and diag memory detection don't work stop at the first memory hole. Otherwise the code might loop forever... Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens commit 8224ca195874525533665bbcd23b6da1e575aa4d Author: Haavard Skinnemoen Date: Fri Apr 27 14:21:47 2007 +0200 [AVR32] Fix compile error with gcc 4.1 gcc 4.1 doesn't seem to like const variables as inline assembly outputs. Drop support for reading 64-bit values using get_user() so that we can use an unsigned long to hold the result regardless of the actual size. This should be safe since many architectures, including i386, doesn't support reading 64-bit values with get_user(). Signed-off-by: Haavard Skinnemoen commit d468a030026017008286919aa6127b1190efb2c2 Author: Artem Bityutskiy Date: Fri Apr 27 15:11:44 2007 +0300 UBI: remove unused variable Signed-off-by: Artem Bityutskiy commit a4022b0d6005b117a985cec64559e048981a4244 Author: Mathieu Desnoyers Date: Tue Apr 10 18:23:09 2007 -0400 avr32: remove unneeded cast in atomic.h This int cast is superfluous since system.h cmpxchg already casts it in (typeof(*(ptr))). Signed-off-by: Mathieu Desnoyers Signed-off-by: Andrew Morton Signed-off-by: Haavard Skinnemoen commit 0277b378c3779e3c8a413afb7d4ee00fa24a5a26 Author: Robert P. J. Day Date: Thu Apr 26 08:53:38 2007 -0400 AVR32: Remove useless config option "GENERIC_BUST_SPINLOCK". Remove the clearly useless config option GENERIC_BUST_SPINLOCK, which is not used anywhere in the tree. Signed-off-by: Robert P. J. Day Signed-off-by: Haavard Skinnemoen commit c0c3e81608fc300027f2131e351e67ab118cf24c Author: Haavard Skinnemoen Date: Wed Mar 14 13:59:13 2007 +0100 [AVR32] Optimize the TLB miss handler Reorder some instructions and change the register usage to reduce the number of pipeline stalls. Also use the bfextu and bfins instructions for bitfield manipulations instead of shifting and masking. This makes gzipping a 80MB file approximately 2% faster. Signed-off-by: Haavard Skinnemoen commit 9ca20a8366462c553c27216161c735937f9de108 Author: Haavard Skinnemoen Date: Thu Apr 12 17:26:57 2007 +0200 [AVR32] Board code for ATNGW100 Add board code and defconfig for the ATNGW100 Network Gateway kit. For more information about this board, see http://www.atmel.com/dyn/products/tools_card.asp?tool_id=4102 Signed-off-by: Haavard Skinnemoen commit 2c1a2a3441a754a9b5a8e7184071154f8a9bd61b Author: Haavard Skinnemoen Date: Wed Mar 7 10:40:44 2007 +0100 [AVR32] Use memcpy/memset in memcpy_{from,to}_io and memset_io Using readb/writeb to implement these breaks NOR flash support. I can't see any reason why regular memcpy and memset shouldn't work. Signed-off-by: Haavard Skinnemoen commit d80e2bb12606906fd0b5b5592f519852de8b0113 Author: Haavard Skinnemoen Date: Wed Mar 21 16:23:41 2007 +0100 [AVR32] Get rid of board_setup_fbmem() Since the core setup code takes care of both allocation and reservation of framebuffer memory, there's no need for this board- specific hook anymore. Replace it with two global variables, fbmem_start and fbmem_size, which can be used directly. Signed-off-by: Haavard Skinnemoen commit f9692b9501c339ec90647d8cd6ee5c106f072f9f Author: Haavard Skinnemoen Date: Wed Mar 21 16:16:50 2007 +0100 [AVR32] Reserve framebuffer memory in early_parse_fbmem() With the current strategy of using the bootmem allocator to allocate or reserve framebuffer memory, there's a slight chance that the requested area has been taken by the boot allocator bitmap before we get around to reserving it. By inserting the framebuffer region as a reserved region as early as possible, we improve our chances for success and we make the region visible as a reserved region in dmesg and /proc/iomem without any extra work. Signed-off-by: Haavard Skinnemoen commit d8011768e6bdd0d9de5cc7bdbd3077b4b4fab8c7 Author: Haavard Skinnemoen Date: Wed Mar 21 16:02:57 2007 +0100 [AVR32] Simplify early handling of memory regions Use struct resource to specify both physical memory regions and reserved regions and push everything into the same framework, including kernel code/data and initrd memory. This allows us to get rid of many special cases in the bootmem initialization and will also make it easier to implement more robust handling of framebuffer memory later. Signed-off-by: Haavard Skinnemoen commit 5539f59ac40473730806580f212c4eac6e769f01 Author: Haavard Skinnemoen Date: Wed Mar 21 15:39:18 2007 +0100 [AVR32] Move setup_bootmem() from mm/init.c to kernel/setup.c Signed-off-by: Haavard Skinnemoen commit e3e7d8d4ea37b8372ee417452d03171c5dc55125 Author: Haavard Skinnemoen Date: Mon Feb 12 16:28:56 2007 +0100 [AVR32] Make I/O access macros work with external devices Fix the I/O access macros so that they work with externally connected devices accessed in little-endian mode over any bus width: * Use a set of macros to define I/O port- and memory operations borrowed from MIPS. * Allow subarchitecture to specify address- and data-mangling * Implement at32ap-specific port mangling (with build-time configurable bus width. Only one bus width at a time supported for now.) * Rewrite iowriteN and friends to use write[bwl] and friends (not the __raw counterparts.) This has been tested using pata_pcmcia to access a CompactFlash card connected to the EBI (16-bit bus width.) Signed-off-by: Haavard Skinnemoen commit 92b728c147adb8c690b520304f4c9ee3eee43c21 Author: Haavard Skinnemoen Date: Tue Mar 13 10:06:37 2007 +0100 [AVR32] Fix NMI handler Fix a problem with the NMI handler entry code related to the NMI handler sharing some code with the exception handlers. This is not a good idea because the RSR and RAR registers are not the same, and the NMI handler runs with interrupts masked the whole time so there's no need to check for pending work. Open-code the low-level NMI handling logic instead so that the pt_regs layout is actually correct when the higher-level handler is called. Signed-off-by: Haavard Skinnemoen commit 623b0355d5b1f9c6d05005b649a2f3a7b9fd7816 Author: Haavard Skinnemoen Date: Tue Mar 13 17:59:11 2007 +0100 [AVR32] Clean up exception handling code * Use generic BUG() handling * Remove some useless debug statements * Use a common function _exception() to send signals or oops when an exception can't be handled. This makes sure init doesn't enter an infinite exception loop as well. Borrowed from powerpc. * Add some basic exception tracing support to the page fault code. * Rework dump_stack(), show_regs() and friends and move everything into process.c * Print information about configuration options and chip type when oopsing Signed-off-by: Haavard Skinnemoen commit 3b328c98093702c584692bffabd440800b383d73 Author: Haavard Skinnemoen Date: Tue Mar 13 15:30:38 2007 +0100 [AVR32] Clean up cpu identification and add features bitmap Clean up the cpu identification code, using definitions from instead of hardcoded constants. Also, add a features bitmap to struct avr32_cpuinfo to allow other code to make decisions based upon what the running cpu is actually capable of. Signed-off-by: Haavard Skinnemoen commit 535c806c26ef602d578792083df52b31803b961e Author: Haavard Skinnemoen Date: Tue Mar 13 14:17:14 2007 +0100 [AVR32] Clean up asm/sysreg.h Fix indentation and remove spurious comments in asm-avr32/sysreg.h Signed-off-by: Haavard Skinnemoen commit 188ff65d49dadf7b0e9b6718abc3fe98a5098711 Author: Haavard Skinnemoen Date: Wed Mar 14 13:23:44 2007 +0100 [AVR32] Don't enable clocks with no users Bring the code that sets the initial PM clock masks in line with the comment preceding it by only enabling clocks that have users != 0. Fix SM clock definition and avr32_hpt_init() so that the SM and TC0 clocks keep ticking. Signed-off-by: Haavard Skinnemoen commit 19b7ce8bad718a2850ea19aeb7383f1728596c24 Author: Hans-Christian Egtvedt Date: Mon Feb 26 13:50:43 2007 +0100 [AVR32] Put cpu in sleep 0 when idle. This patch puts the CPU in sleep 0 when doing nothing, idle. This will turn of the CPU clock and thus save power. The CPU is waken again when an interrupt occurs. Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Haavard Skinnemoen commit 7760989e5e2900e484e9115e6e690c6ce0b0221c Author: Hans-Christian Egtvedt Date: Mon Mar 12 18:15:16 2007 +0100 [AVR32] Change system timer from count-compare to Timer/Counter 0 Due to limitation of the count-compare system timer (not able to count when CPU is in sleep), the system timer had to be changed to use a peripheral timer/counter. The old COUNT-COMPARE code is still present in time.c as weak functions. The new timer is added to the architecture directory. This patch sets up TC0 as system timer The new timer has been tested on AT32AP7000/ATSTK1000 at 100 Hz, 250 Hz, 300 Hz and 1000 Hz. For more details about the timer/counter see the datasheet for AT32AP700x available at http://www.atmel.com/dyn/products/product_card.asp?part_id=3903 Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Haavard Skinnemoen commit 228e845fd243bf42033998afab792357444e9e4a Author: Haavard Skinnemoen Date: Wed Mar 7 15:24:34 2007 +0100 [AVR32] Add mach-specific Kconfig Include at32ap-specific Kconfig file from top-level Kconfig file. The at32ap Kconfig is currently empty, but it will grow some machine- specific options soon. Signed-off-by: Haavard Skinnemoen commit 068d9f6eb9369a00eb45be91c07653cfef65f4a0 Author: Hans-Christian Egtvedt Date: Wed Jan 31 18:01:45 2007 +0100 [AVR32] Add nwait and tdf parameters to SMC configuration Complete the SMC configuration code by adding nwait and tdf parameter. After this change, we support the same parameters as the hardware. Signed-off-by: Haavard Skinnemoen commit 485764016d5accb813e8bdd076802a7e3318bb64 Author: Artem Bityutskiy Date: Tue Feb 13 17:11:10 2007 +0200 UBI: add me to MAINTAINERS Signed-off-by: Artem Bityutskiy commit 0029da3bf430eea498eee8cef5933f9214534b8a Author: Artem Bityutskiy Date: Wed Oct 4 19:15:21 2006 +0300 JFFS2: add UBI support This patch make JFFS2 able to work with UBI volumes via the emulated MTD devices which are directly mapped to these volumes. Signed-off-by: Artem Bityutskiy commit 801c135ce73d5df1caf3eca35b66a10824ae0707 Author: Artem B. Bityutskiy Date: Tue Jun 27 12:22:22 2006 +0400 UBI: Unsorted Block Images UBI (Latin: "where?") manages multiple logical volumes on a single flash device, specifically supporting NAND flash devices. UBI provides a flexible partitioning concept which still allows for wear-levelling across the whole flash device. In a sense, UBI may be compared to the Logical Volume Manager (LVM). Whereas LVM maps logical sector numbers to physical HDD sector numbers, UBI maps logical eraseblocks to physical eraseblocks. More information may be found at http://www.linux-mtd.infradead.org/doc/ubi.html Partitioning/Re-partitioning An UBI volume occupies a certain number of erase blocks. This is limited by a configured maximum volume size, which could also be viewed as the partition size. Each individual UBI volume's size can be changed independently of the other UBI volumes, provided that the sum of all volume sizes doesn't exceed a certain limit. UBI supports dynamic volumes and static volumes. Static volumes are read-only and their contents are protected by CRC check sums. Bad eraseblocks handling UBI transparently handles bad eraseblocks. When a physical eraseblock becomes bad, it is substituted by a good physical eraseblock, and the user does not even notice this. Scrubbing On a NAND flash bit flips can occur on any write operation, sometimes also on read. If bit flips persist on the device, at first they can still be corrected by ECC, but once they accumulate, correction will become impossible. Thus it is best to actively scrub the affected eraseblock, by first copying it to a free eraseblock and then erasing the original. The UBI layer performs this type of scrubbing under the covers, transparently to the UBI volume users. Erase Counts UBI maintains an erase count header per eraseblock. This frees higher-level layers (like file systems) from doing this and allows for centralized erase count management instead. The erase counts are used by the wear-levelling algorithm in the UBI layer. The algorithm itself is exchangeable. Booting from NAND For booting directly from NAND flash the hardware must at least be capable of fetching and executing a small portion of the NAND flash. Some NAND flash controllers have this kind of support. They usually limit the window to a few kilobytes in erase block 0. This "initial program loader" (IPL) must then contain sufficient logic to load and execute the next boot phase. Due to bad eraseblocks, which may be randomly scattered over the flash device, it is problematic to store the "secondary program loader" (SPL) statically. Also, due to bit-flips it may become corrupted over time. UBI allows to solve this problem gracefully by storing the SPL in a small static UBI volume. UBI volumes vs. static partitions UBI volumes are still very similar to static MTD partitions: * both consist of eraseblocks (logical eraseblocks in case of UBI volumes, and physical eraseblocks in case of static partitions; * both support three basic operations - read, write, erase. But UBI volumes have the following advantages over traditional static MTD partitions: * there are no eraseblock wear-leveling constraints in case of UBI volumes, so the user should not care about this; * there are no bit-flips and bad eraseblocks in case of UBI volumes. So, UBI volumes may be considered as flash devices with relaxed restrictions. Where can it be found? Documentation, kernel code and applications can be found in the MTD gits. What are the applications for? The applications help to create binary flash images for two purposes: pfi files (partial flash images) for in-system update of UBI volumes, and plain binary images, with or without OOB data in case of NAND, for a manufacturing step. Furthermore some tools are/and will be created that allow flash content analysis after a system has crashed.. Who did UBI? The original ideas, where UBI is based on, were developed by Andreas Arnez, Frank Haverkamp and Thomas Gleixner. Josh W. Boyer and some others were involved too. The implementation of the kernel layer was done by Artem B. Bityutskiy. The user-space applications and tools were written by Oliver Lohmann with contributions from Frank Haverkamp, Andreas Arnez, and Artem. Joern Engel contributed a patch which modifies JFFS2 so that it can be run on a UBI volume. Thomas Gleixner did modifications to the NAND layer. Alexander Schmidt made some testing work as well as core functionality improvements. Signed-off-by: Artem B. Bityutskiy Signed-off-by: Frank Haverkamp commit 8d8a0241eb019ce9648a77b55f9f76a834207cbb Author: Olaf Hering Date: Thu Apr 26 06:36:56 2007 +1000 [POWERPC] Generic check_legacy_ioport check_legacy_ioport makes only sense on PREP, CHRP and pSeries. They may have an isa node with PS/2, parport, floppy and serial ports. Remove the check_legacy_ioport call from ppc_md, it's not needed anymore. Hardware capabilities come from the device-tree. Signed-off-by: Olaf Hering Signed-off-by: Paul Mackerras commit 8d2169e8d6b8a91413df33bc402e0f602ceaabcc Author: David Gibson Date: Fri Apr 27 11:53:52 2007 +1000 [POWERPC] Prepare for splitting up mmu.h by MMU type Currently asm-powerpc/mmu.h has definitions for the 64-bit hash based MMU. If CONFIG_PPC64 is not set, it instead includes asm-ppc/mmu.h which contains a particularly horrible mess of #ifdefs giving the definitions for all the various 32-bit MMUs. It would be nice to have the low level definitions for each MMU type neatly in their own separate files. It would also be good to wean arch/powerpc off dependence on the old asm-ppc/mmu.h. This patch makes a start on such a cleanup by moving the definitions for the 64-bit hash MMU to their own file, asm-powerpc/mmu_hash64.h. Definitions for the other MMUs still all come from asm-ppc/mmu.h, however each MMU type can now be one-by-one moved over to their own file, in the process cleaning them up stripping them of cruft no longer necessary in arch/powerpc. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 173ba87b9584e4cba41ce9a06916eba80baa1bf4 Author: Scott Wood Date: Fri Apr 27 03:19:31 2007 +1000 [POWERPC] bootwrapper: cuboot-83xx: Exclude upper 1MB from heap. The uppermost part of memory is where u-boot puts the stack, so don't include that in the heap. It's not currently causing problems, as the current code allocates from the bottom of the heap, but this will keep things from potentially breaking if a future implementation were to allocate from the top. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit 4536b937466ff6784d2a9a4bebde924a0d6e01cb Author: Scott Wood Date: Fri Apr 27 03:09:01 2007 +1000 [POWERPC] bootwrapper: cuImage for 85xx Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit e4bb688d9f11d7fee927312cc97d443472c7c212 Author: Scott Wood Date: Fri Apr 27 03:08:13 2007 +1000 [POWERPC] bootwrapper: Fix array handling in dt_xlate_reg(). This fixes a few bugs in how dt_xlate_reg() handles address arrays: 1. copy_val() was copying into the wrong end of the array, resulting in random stack garbage at the other end. 2. dt_xlate_reg() was getting the result from the wrong end of the array. 3. add_reg() and sub_reg() were treating the arrays as little-endian rather than big-endian. 4. add_reg() only returned an error on a carry out of the entire array, rather than out of the naddr portion. 5. The requested reg resource was checked to see if it exceeded the size of the reg property, but not to see if it exceeded the size of the buffer. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit 3c5f6162549b9045a2925dff64c140c7f49ea344 Author: Milton Miller Date: Wed Apr 11 18:32:36 2007 +1000 [POWERPC] boot: More verbose gunzip error message Change the error message in gunzip_exactly to be more verbose. Besides the identifier being unrelated to the current function name, the user had no indication if the corruption was near the beginning or the end. Signed-off-by: Milton Miller Acked-by: David Gibson Signed-off-by: Paul Mackerras commit 928370c676729df118f15939ef69b540a7b946b1 Author: Akinobu Mita Date: Tue Apr 10 21:05:31 2007 +1000 [POWERPC] Enable make install make help on powerpc says make install is available. But it failed due to no rule to make install. This patch enables make install to work. Signed-off-by: Akinobu Mita Signed-off-by: Paul Mackerras commit 57647a4dada06bb4d4056e57b4dcb79c4c8bb191 Author: Benjamin Herrenschmidt Date: Thu Mar 22 17:23:44 2007 +1100 [POWERPC] Always use -mno-string The string load/store instructions are unimplemented on some processors and slow (microcoded) on some others. It's simplest to just not use them at all. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit f13659e0b3907548402ce1f47bf866544b804260 Author: Anton Blanchard Date: Wed Mar 21 01:48:34 2007 +1100 [POWERPC] Fix WARN_ON when entering xmon Whenever we enter xmon we get a WARN_ON out of the rtas code since it thinks interrupts are still on: Unable to handle kernel paging request for data at address 0x00000000 Faulting instruction address: 0xd000000000080008 cpu 0x3: Vector: 300 (Data Access) at [c0000000075dba00] pc: d000000000080008: .doit+0x8/0x40 [oopser] lr: c000000000077704: .sys_init_module+0x1664/0x1824 sp: c0000000075dbc80 msr: 9000000000009032 dar: 0 dsisr: 42000000 current = 0xc000000003fa64b0 paca = 0xc000000000694280 pid = 2260, comm = insmod ------------[ cut here ]------------ Badness at arch/powerpc/kernel/entry_64.S:651 Call Trace: [C0000000075DAE70] [C00000000000EB64] .show_stack+0x68/0x1b0 (unreliable) [C0000000075DAF10] [C000000000216254] .report_bug+0x94/0xe8 [C0000000075DAFA0] [C00000000047B140] __kprobes_text_start+0x178/0x584 [C0000000075DB040] [C0000000000044F4] program_check_common+0xf4/0x100 Signed-off-by: Paul Mackerras commit e7273d2a0841612794dff43328d461beee95c986 Author: Will Schmidt Date: Tue Mar 13 06:21:16 2007 +1100 [POWERPC] Replace if-then-else with a switch statement Convert a compound if-else blob to a switch statement. This better fits the kernel coding style. Signed-off-by: Will Schmidt Signed-off-by: Paul Mackerras commit 5dd60166c22dbbb7a8684c119d28cb88b6087784 Author: Domen Puncer Date: Fri Mar 2 21:44:45 2007 +1100 [POWERPC] Fix typos in booting-without-of.txt Fix typos + some cosmetic changes. Signed-off-by: Domen Puncer Signed-off-by: Paul Mackerras commit 9c8f8e752431f3f7ed6ea6ea6e491ce12057f572 Author: Haavard Skinnemoen Date: Thu Feb 1 16:34:10 2007 +0100 [AVR32] Add basic HMATRIX support This adds register and clock definitions for the High-speed bus Matrix (HMATRIX) as well as a function that can be used to configure special EBI functionality like CompactFlash and NAND flash support. Signed-off-by: Haavard Skinnemoen commit 912a41a4ab935ce8c4308428ec13fc7f8b1f18f4 Author: Sergey Vlasov Date: Fri Apr 27 02:17:19 2007 -0700 [IPV4] nl_fib_lookup: Initialise res.r before fib_res_put(&res) When CONFIG_IP_MULTIPLE_TABLES is enabled, the code in nl_fib_lookup() needs to initialize the res.r field before fib_res_put(&res) - unlike fib_lookup(), a direct call to ->tb_lookup does not set this field. Signed-off-by: Sergey Vlasov Signed-off-by: David S. Miller commit ebbd90a730711280142017e482f27ec3fbb4f227 Author: YOSHIFUJI Hideaki Date: Fri Apr 27 02:13:39 2007 -0700 [IPV6]: Fix thinko in ipv6_rthdr_rcv() changes. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 49e9f70f8e7a4df00a5185e7f5c91e3c583847db Author: David S. Miller Date: Fri Apr 27 01:04:23 2007 -0700 [IPV4]: Add multipath cached to feature-removal-schedule.txt Signed-off-by: David S. Miller commit cd9ad58d4061494e7fdd70ded7bcf2418daf356a Author: David S. Miller Date: Thu Apr 26 21:19:23 2007 -0700 [SCSI] SUNESP: Complete driver rewrite to version 2.0 Major features: 1) Tagged queuing support. 2) Will properly negotiate for synchronous transfers even on devices that reject the wide negotiation message, such as CDROMs 3) Significantly lower kernel stack usage in interrupt handler path by elimination of function vector arrays, replaced by a top-level switch statement state machine. 4) Uses generic scsi infrastructure as much as possible to avoid code duplication. 5) Automatic request of sense data in response to CHECK_CONDITION 6) Portable to other platforms using ESP such as DEC and Sun3 systems. Signed-off-by: David S. Miller commit 2e0c3370b3ecf831ef209f1094df36a808865845 Author: Olof Johansson Date: Fri Apr 27 15:46:01 2007 +1000 [POWERPC] pasemi: Cpufreq driver Cpufreq driver for PA Semi PWRficient processors. Signed-off-by: Egor Martovetsky Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 116ab40df8decf8c519368e31825e9584c86adf2 Author: Olof Johansson Date: Wed Apr 25 09:17:33 2007 +1000 [POWERPC] pasemi: Kconfig for gpio_mdio arch/powerpc/platforms/pasemi/gpio_mdio.c really depends on CONFIG_PHYLIB. Add a config option for it, allow for it to be disabled if needed and fix the dependency. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 952418cd3894cf370cf2c9acdfd498c84c3b7cf9 Author: Olof Johansson Date: Wed Apr 25 04:01:34 2007 +1000 [POWERPC] pasemi: Remove build warning arch/powerpc/platforms/pasemi/setup.c: In function 'pasemi_publish_devices': arch/powerpc/platforms/pasemi/setup.c:220: warning: implicit declaration of function 'of_platform_bus_probe' Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 5bcec54f026b13617ec8aa8fa229970f9acf8dd5 Author: Stephen Rothwell Date: Fri Apr 27 14:32:37 2007 +1000 [POWERPC] Partially revert a7edd0e676d51145ae634a2acf7a447e319200fa Due to conflicts with the network drivers tree. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 01b2726dd11ef198ac6cf8f88974b4427d40ffdb Author: Stephen Rothwell Date: Fri Apr 27 13:41:15 2007 +1000 [POWERPC] Rename get_property to of_get_property: partial drivers This does drivers/machintosh and the hvc code. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 16ce82d846f2e6b652a064f91c5019cfe8682be4 Author: David S. Miller Date: Thu Apr 26 21:08:21 2007 -0700 [SPARC64]: Convert PCI over to generic struct iommu/strbuf. Signed-off-by: David S. Miller commit f16bfc1c0958ff340a02779ab139b03fb5ba6e82 Author: Johannes Berg Date: Thu Apr 26 20:51:12 2007 -0700 [WIRELESS] cfg80211: Clarify locking comment. This patch clarifies the comment about locking in wiphy_unregister. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit a4d73ee168eeaed3baea86542ad42e1fd7e192d3 Author: Johannes Berg Date: Thu Apr 26 20:50:35 2007 -0700 [WIRELESS] cfg80211: Fix locking in wiphy_new. This patch fixes the locking in wiphy new. Ingo Oeser noticed that locking in the error case was wrong and also suggested this fix. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit b86e0280bb5585a610783ff5392d9d439dee7ddd Author: Johannes Berg Date: Thu Apr 26 20:48:23 2007 -0700 [WEXT] net_device: Don't include wext bits if not required. This patch makes the wext bits in struct net_device depend on CONFIG_WIRELESS_EXT. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 4d44e0dfe961e02489d40d32334454ebe0e784e8 Author: Johannes Berg Date: Thu Apr 26 20:47:25 2007 -0700 [WEXT]: Misc code cleanups. Just a few things that didn't fit in with the other patches. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit bdf51894c1d7ce3fba8c8fdf485e85173ac60c6c Author: Johannes Berg Date: Thu Apr 26 20:46:55 2007 -0700 [WEXT]: Reduce inline abuse. This patch removes a bunch of inline abuse from wext. Most functions that were marked inline are only used once so the compiler will inline them anyway, others are used multiple times but there's no requirement for them to be inline since they aren't in any fast paths. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 7a9df167db0f200d5f8e393376dba8ceeae0fd53 Author: Johannes Berg Date: Thu Apr 26 20:46:23 2007 -0700 [WEXT]: Move EXPORT_SYMBOL statements where they belong. EXPORT_SYMBOL statements are supposed to go together with the symbol they're exporting. This patch moves them accordingly. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit dd8ceabcd10d47f6f28ecfaf2eac7beffca11b3c Author: Johannes Berg Date: Thu Apr 26 20:45:47 2007 -0700 [WEXT]: Cleanup early ioctl call path. This patch makes the code in wireless_process_ioctl somewhat more readable. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 4b1e255384570138c2a823904796d46f628e8350 Author: Johannes Berg Date: Thu Apr 26 20:45:14 2007 -0700 [WEXT]: Remove options. This patch kills the two options in wext that are required to be enabled anyway because they influence the userspace API. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 235c107ba08becb3ae6c3d3449c8b1053a5a9d75 Author: Johannes Berg Date: Thu Apr 26 20:44:35 2007 -0700 [WEXT]: Remove dead debug code. This patch kills a whole bunch of code that can only ever be used by defining some things in wext.c. Also, the things that are printed are mostly useless since the API is fairly well-tested. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 295f4a1fa3ecdf816b18393ef7bcd37c032df2fa Author: Johannes Berg Date: Thu Apr 26 20:43:56 2007 -0700 [WEXT]: Clean up how wext is called. This patch cleans up the call paths from the core code into wext. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 11433ee450eb4a320f46ce5ed51410b52803ffcc Author: Johannes Berg Date: Thu Apr 26 20:42:51 2007 -0700 [WEXT]: Move to net/wireless This patch moves dev/core/wireless.c to net/wireless/wext.c. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 39bf09493042200b967cdf2ee6e3f670b7963903 Author: David S. Miller Date: Thu Apr 26 20:39:14 2007 -0700 [AFS]: Eliminate cmpxchg() usage in vlocation code. cmpxchg() is not available on every processor so can't be used in generic code. Replace with spinlock protection on the ->state changes, wakeups, and wait loops. Add what appears to be a missing wakeup on transition to AFS_VL_VALID state in afs_vlocation_updater(). Signed-off-by: David S. Miller commit 68c708fd5e90f6d178c84bb7e641589eb2842319 Author: David S. Miller Date: Thu Apr 26 20:20:21 2007 -0700 [RXRPC]: Fix pointers passed to bitops. CC [M] net/rxrpc/ar-input.o net/rxrpc/ar-input.c: In function ‘rxrpc_fast_process_data’: net/rxrpc/ar-input.c:171: warning: passing argument 2 of ‘__test_and_set_bit’ from incompatible pointer type net/rxrpc/ar-input.c:180: warning: passing argument 2 of ‘__clear_bit’ from incompatible pointer type net/rxrpc/ar-input.c:218: warning: passing argument 2 of ‘__clear_bit’ from incompatible pointer type Signed-off-by: David S. Miller commit 411faf5810cdd0e4f5071a3805d8adb49d120a07 Author: David S. Miller Date: Thu Apr 26 20:18:17 2007 -0700 [RXRPC]: Remove bogus atomic_* overrides. These are done with CPP defines which several platforms use for their atomic.h implementation, which floods the build with warnings and breaks the build. Signed-off-by: David S. Miller commit ba3e0e1accd8d5bb12eaeb0977429d8dc04f6d1e Author: David S. Miller Date: Thu Apr 26 16:06:22 2007 -0700 [AFS]: Fix u64 printing in debug logging. Need 'unsigned long long' casts to quiet warnings on 64-bit platforms when using %ll on a u64. Signed-off-by: David S. Miller commit 260a980317dac80182dd76140cf67c6e81d6d3dd Author: David Howells Date: Thu Apr 26 15:59:35 2007 -0700 [AFS]: Add "directory write" support. Add support for the create, link, symlink, unlink, mkdir, rmdir and rename VFS operations to the in-kernel AFS filesystem. Also: (1) Fix dentry and inode revalidation. d_revalidate should only look at state of the dentry. Revalidation of the contents of an inode pointed to by a dentry is now separate. (2) Fix afs_lookup() to hash negative dentries as well as positive ones. Signed-off-by: David Howells Signed-off-by: David S. Miller commit c35eccb1f614954b10cba3f74b7c301993b2f42e Author: David Howells Date: Thu Apr 26 15:58:49 2007 -0700 [AFS]: Implement the CB.InitCallBackState3 operation. Implement the CB.InitCallBackState3 operation for the fileserver to call. This reduces the amount of network traffic because if this op is aborted, the fileserver will then attempt an CB.InitCallBackState operation. Signed-off-by: David Howells Signed-off-by: David S. Miller commit b908fe6b2d1294d93b0d0badf6bf4f9a2cd7d729 Author: David Howells Date: Thu Apr 26 15:58:17 2007 -0700 [AFS]: Add support for the CB.GetCapabilities operation. Add support for the CB.GetCapabilities operation with which the fileserver can ask the client for the following information: (1) The list of network interfaces it has available as IPv4 address + netmask plus the MTUs. (2) The client's UUID. (3) The extended capabilities of the client, for which the only current one is unified error mapping (abort code interpretation). To support this, the patch adds the following routines to AFS: (1) A function to iterate through all the network interfaces using RTNETLINK to extract IPv4 addresses and MTUs. (2) A function to iterate through all the network interfaces using RTNETLINK to pull out the MAC address of the lowest index interface to use in UUID construction. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 0795e7c031c4bda46fbdde678adf29de19bef7f4 Author: David Howells Date: Thu Apr 26 15:57:43 2007 -0700 [AFS]: Update the AFS fs documentation. Update the AFS fs documentation. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 00d3b7a4533e367b0dc2812a706db8f9f071c27f Author: David Howells Date: Thu Apr 26 15:57:07 2007 -0700 [AFS]: Add security support. Add security support to the AFS filesystem. Kerberos IV tickets are added as RxRPC keys are added to the session keyring with the klog program. open() and other VFS operations then find this ticket with request_key() and either use it immediately (eg: mkdir, unlink) or attach it to a file descriptor (open). Signed-off-by: David Howells Signed-off-by: David S. Miller commit 436058a49e0fb91c74454dbee9cfee6fb53b4336 Author: David Howells Date: Thu Apr 26 15:56:24 2007 -0700 [AFS]: Handle multiple mounts of an AFS superblock correctly. Handle multiple mounts of an AFS superblock correctly, checking to see whether the superblock is already initialised after calling sget() rather than just unconditionally stamping all over it. Also delete the "silent" parameter to afs_fill_super() as it's not used and can, in any case, be obtained from sb->s_flags. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 63b6be55e8b51cb718468794d343058e96c7462c Author: David Howells Date: Thu Apr 26 15:55:48 2007 -0700 [AF_RXRPC]: Delete the old RxRPC code. Delete the old RxRPC code as it's now no longer used. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 08e0e7c82eeadec6f4871a386b86bf0f0fbcb4eb Author: David Howells Date: Thu Apr 26 15:55:03 2007 -0700 [AF_RXRPC]: Make the in-kernel AFS filesystem use AF_RXRPC. Make the in-kernel AFS filesystem use AF_RXRPC instead of the old RxRPC code. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 651350d10f93bed7003c9a66e24cf25e0f8eed3d Author: David Howells Date: Thu Apr 26 15:50:17 2007 -0700 [AF_RXRPC]: Add an interface to the AF_RXRPC module for the AFS filesystem to use Add an interface to the AF_RXRPC module so that the AFS filesystem module can more easily make use of the services available. AFS still opens a socket but then uses the action functions in lieu of sendmsg() and registers an intercept functions to grab messages before they're queued on the socket Rx queue. This permits AFS (or whatever) to: (1) Avoid the overhead of using the recvmsg() call. (2) Use different keys directly on individual client calls on one socket rather than having to open a whole slew of sockets, one for each key it might want to use. (3) Avoid calling request_key() at the point of issue of a call or opening of a socket. This is done instead by AFS at the point of open(), unlink() or other VFS operation and the key handed through. (4) Request the use of something other than GFP_KERNEL to allocate memory. Furthermore: (*) The socket buffer markings used by RxRPC are made available for AFS so that it can interpret the cooked RxRPC messages itself. (*) rxgen (un)marshalling abort codes are made available. The following documentation for the kernel interface is added to Documentation/networking/rxrpc.txt: ========================= AF_RXRPC KERNEL INTERFACE ========================= The AF_RXRPC module also provides an interface for use by in-kernel utilities such as the AFS filesystem. This permits such a utility to: (1) Use different keys directly on individual client calls on one socket rather than having to open a whole slew of sockets, one for each key it might want to use. (2) Avoid having RxRPC call request_key() at the point of issue of a call or opening of a socket. Instead the utility is responsible for requesting a key at the appropriate point. AFS, for instance, would do this during VFS operations such as open() or unlink(). The key is then handed through when the call is initiated. (3) Request the use of something other than GFP_KERNEL to allocate memory. (4) Avoid the overhead of using the recvmsg() call. RxRPC messages can be intercepted before they get put into the socket Rx queue and the socket buffers manipulated directly. To use the RxRPC facility, a kernel utility must still open an AF_RXRPC socket, bind an addess as appropriate and listen if it's to be a server socket, but then it passes this to the kernel interface functions. The kernel interface functions are as follows: (*) Begin a new client call. struct rxrpc_call * rxrpc_kernel_begin_call(struct socket *sock, struct sockaddr_rxrpc *srx, struct key *key, unsigned long user_call_ID, gfp_t gfp); This allocates the infrastructure to make a new RxRPC call and assigns call and connection numbers. The call will be made on the UDP port that the socket is bound to. The call will go to the destination address of a connected client socket unless an alternative is supplied (srx is non-NULL). If a key is supplied then this will be used to secure the call instead of the key bound to the socket with the RXRPC_SECURITY_KEY sockopt. Calls secured in this way will still share connections if at all possible. The user_call_ID is equivalent to that supplied to sendmsg() in the control data buffer. It is entirely feasible to use this to point to a kernel data structure. If this function is successful, an opaque reference to the RxRPC call is returned. The caller now holds a reference on this and it must be properly ended. (*) End a client call. void rxrpc_kernel_end_call(struct rxrpc_call *call); This is used to end a previously begun call. The user_call_ID is expunged from AF_RXRPC's knowledge and will not be seen again in association with the specified call. (*) Send data through a call. int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg, size_t len); This is used to supply either the request part of a client call or the reply part of a server call. msg.msg_iovlen and msg.msg_iov specify the data buffers to be used. msg_iov may not be NULL and must point exclusively to in-kernel virtual addresses. msg.msg_flags may be given MSG_MORE if there will be subsequent data sends for this call. The msg must not specify a destination address, control data or any flags other than MSG_MORE. len is the total amount of data to transmit. (*) Abort a call. void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code); This is used to abort a call if it's still in an abortable state. The abort code specified will be placed in the ABORT message sent. (*) Intercept received RxRPC messages. typedef void (*rxrpc_interceptor_t)(struct sock *sk, unsigned long user_call_ID, struct sk_buff *skb); void rxrpc_kernel_intercept_rx_messages(struct socket *sock, rxrpc_interceptor_t interceptor); This installs an interceptor function on the specified AF_RXRPC socket. All messages that would otherwise wind up in the socket's Rx queue are then diverted to this function. Note that care must be taken to process the messages in the right order to maintain DATA message sequentiality. The interceptor function itself is provided with the address of the socket and handling the incoming message, the ID assigned by the kernel utility to the call and the socket buffer containing the message. The skb->mark field indicates the type of message: MARK MEANING =============================== ======================================= RXRPC_SKB_MARK_DATA Data message RXRPC_SKB_MARK_FINAL_ACK Final ACK received for an incoming call RXRPC_SKB_MARK_BUSY Client call rejected as server busy RXRPC_SKB_MARK_REMOTE_ABORT Call aborted by peer RXRPC_SKB_MARK_NET_ERROR Network error detected RXRPC_SKB_MARK_LOCAL_ERROR Local error encountered RXRPC_SKB_MARK_NEW_CALL New incoming call awaiting acceptance The remote abort message can be probed with rxrpc_kernel_get_abort_code(). The two error messages can be probed with rxrpc_kernel_get_error_number(). A new call can be accepted with rxrpc_kernel_accept_call(). Data messages can have their contents extracted with the usual bunch of socket buffer manipulation functions. A data message can be determined to be the last one in a sequence with rxrpc_kernel_is_data_last(). When a data message has been used up, rxrpc_kernel_data_delivered() should be called on it.. Non-data messages should be handled to rxrpc_kernel_free_skb() to dispose of. It is possible to get extra refs on all types of message for later freeing, but this may pin the state of a call until the message is finally freed. (*) Accept an incoming call. struct rxrpc_call * rxrpc_kernel_accept_call(struct socket *sock, unsigned long user_call_ID); This is used to accept an incoming call and to assign it a call ID. This function is similar to rxrpc_kernel_begin_call() and calls accepted must be ended in the same way. If this function is successful, an opaque reference to the RxRPC call is returned. The caller now holds a reference on this and it must be properly ended. (*) Reject an incoming call. int rxrpc_kernel_reject_call(struct socket *sock); This is used to reject the first incoming call on the socket's queue with a BUSY message. -ENODATA is returned if there were no incoming calls. Other errors may be returned if the call had been aborted (-ECONNABORTED) or had timed out (-ETIME). (*) Record the delivery of a data message and free it. void rxrpc_kernel_data_delivered(struct sk_buff *skb); This is used to record a data message as having been delivered and to update the ACK state for the call. The socket buffer will be freed. (*) Free a message. void rxrpc_kernel_free_skb(struct sk_buff *skb); This is used to free a non-DATA socket buffer intercepted from an AF_RXRPC socket. (*) Determine if a data message is the last one on a call. bool rxrpc_kernel_is_data_last(struct sk_buff *skb); This is used to determine if a socket buffer holds the last data message to be received for a call (true will be returned if it does, false if not). The data message will be part of the reply on a client call and the request on an incoming call. In the latter case there will be more messages, but in the former case there will not. (*) Get the abort code from an abort message. u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb); This is used to extract the abort code from a remote abort message. (*) Get the error number from a local or network error message. int rxrpc_kernel_get_error_number(struct sk_buff *skb); This is used to extract the error number from a message indicating either a local error occurred or a network error occurred. Signed-off-by: David Howells Signed-off-by: David S. Miller commit ec26815ad847dbf74a1e27aa5515fb7d5dc6ee6f Author: David Howells Date: Thu Apr 26 15:49:28 2007 -0700 [AFS]: Clean up the AFS sources Clean up the AFS sources. Also remove references to AFS keys. RxRPC keys are used instead. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 17926a79320afa9b95df6b977b40cca6d8713cea Author: David Howells Date: Thu Apr 26 15:48:28 2007 -0700 [AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both Provide AF_RXRPC sockets that can be used to talk to AFS servers, or serve answers to AFS clients. KerberosIV security is fully supported. The patches and some example test programs can be found in: http://people.redhat.com/~dhowells/rxrpc/ This will eventually replace the old implementation of kernel-only RxRPC currently resident in net/rxrpc/. Signed-off-by: David Howells Signed-off-by: David S. Miller commit e19dff1fdd99a25819af74cf0710e147fff4fd3a Author: David Howells Date: Thu Apr 26 15:46:56 2007 -0700 [AF_RXRPC]: Make it possible to merely try to cancel timers from a module Export try_to_del_timer_sync() for use by the AF_RXRPC module. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 7318226ea2931a627f3572e5f4804c91ca19ecbc Author: David Howells Date: Thu Apr 26 15:46:23 2007 -0700 [AF_RXRPC]: Key facility changes for AF_RXRPC Export the keyring key type definition and document its availability. Add alternative types into the key's type_data union to make it more useful. Not all users necessarily want to use it as a list_head (AF_RXRPC doesn't, for example), so make it clear that it can be used in other ways. Signed-off-by: David Howells Signed-off-by: David S. Miller commit 071b638689464c6b39407025eedd810d5b5e6f5d Author: Oleg Nesterov Date: Thu Apr 26 15:45:32 2007 -0700 [WORKQUEUE]: cancel_delayed_work: use del_timer() instead of del_timer_sync() del_timer_sync() buys nothing for cancel_delayed_work(), but it is less efficient since it locks the timer unconditionally, and may wait for the completion of the delayed_work_timer_fn(). cancel_delayed_work() == 0 means: before this patch: work->func may still be running or queued after this patch: work->func may still be running or queued, or delayed_work_timer_fn->__queue_work() in progress. The latter doesn't differ from the caller's POV, delayed_work_timer_fn() is called with _PENDING bit set. cancel_delayed_work() == 1 with this patch adds a new possibility: delayed_work->work was cancelled, but delayed_work_timer_fn is still running (this is only possible for the re-arming works on single-threaded workqueue). In this case the timer was re-started by work->func(), nobody else can do this. This in turn means that delayed_work_timer_fn has already passed __queue_work() (and wont't touch delayed_work) because nobody else can queue delayed_work->work. Signed-off-by: Oleg Nesterov Signed-Off-By: David Howells Signed-off-by: David S. Miller commit 83418978827324918a8cd25ce5227312de1d4468 Author: Mark Fasheh Date: Mon Apr 23 18:53:12 2007 -0700 ocfs2: Cache extent records The extent map code was ripped out earlier because of an inability to deal with holes. This patch adds back a simpler caching scheme requiring far less code. Our old extent map caching was designed back when meta data block caching in Ocfs2 didn't work very well, resulting in many disk reads. These days our metadata caching is much better, resulting in no un-necessary disk reads. As a result, extent caching doesn't have to be as fancy, nor does it have to cache as many extents. Keeping the last 3 extents seen should be sufficient to give us a small performance boost on some streaming workloads. Signed-off-by: Mark Fasheh commit 7cdfc3a1c3971c9125c317cb8c2525745851798e Author: Mark Fasheh Date: Mon Apr 16 17:28:51 2007 -0700 ocfs2: Remember rw lock level during direct io Cluster locking might have been redone because a direct write won't complete, so this needs to be reflected in the iocb. Signed-off-by: Mark Fasheh commit 8110b073a9135acf0a71bccfc20c0d1023f179c6 Author: Mark Fasheh Date: Thu Mar 22 16:53:23 2007 -0700 ocfs2: Fix up i_blocks calculation to know about holes Older file systems which didn't support holes did a dumb calculation of i_blocks based on i_size. This is no longer accurate, so fix things up to take actual allocation into account. Signed-off-by: Mark Fasheh commit 4f902c37727bbedbc0508a1477874c58ddcc9af8 Author: Mark Fasheh Date: Fri Mar 9 16:26:50 2007 -0800 ocfs2: Fix extent lookup to return true size of holes Initially, we had wired things to return a size '1' of holes. Cook up a small amount of code to find the next extent and calculate the number of clusters between the virtual offset and the next allocated extent. Signed-off-by: Mark Fasheh commit 49cb8d2d496ce06869ccca2ab368ed6b0b5b979d Author: Mark Fasheh Date: Fri Mar 9 16:21:46 2007 -0800 ocfs2: Read from an unwritten extent returns zeros Return an optional extent flags field from our lookup functions and wire up callers to treat unwritten regions as holes for the purpose of returning zeros to the user. Signed-off-by: Mark Fasheh commit e48edee2d8eab812f31f0ff62c6ba635ca2e1e21 Author: Mark Fasheh Date: Wed Mar 7 16:46:57 2007 -0800 ocfs2: make room for unwritten extents flag Due to the size of our group bitmaps, we'll never have a leaf node extent record with more than 16 bits worth of clusters. Split e_clusters up so that leaf nodes can get a flags field where we can mark unwritten extents. Interior nodes whose length references all the child nodes beneath it can't split their e_clusters field, so we use a union to preserve sizing there. Signed-off-by: Mark Fasheh commit 6af67d8205cf65fbaaa743edc7ebb46e486e34ff Author: Mark Fasheh Date: Tue Mar 6 17:24:46 2007 -0800 ocfs2: Use own splice write actor We need to fill holes during a splice write. Provide our own splice write actor which can call ocfs2_file_buffered_write() with a splice-specific callback. Signed-off-by: Mark Fasheh commit fa41045fcbf78269991d5aebb1820fc51534f05d Author: Mark Fasheh Date: Thu Mar 1 11:22:19 2007 -0800 ocfs2: Use do_sync_mapping_range() in ocfs2_zero_tail_for_truncate() Do this instead of filemap_fdatawrite() - this way we sync only the range between i_size and the cluster boundary. Signed-off-by: Mark Fasheh commit 5b04aa3a64f854244bc40a6f528176ed50b5c4f6 Author: Mark Fasheh Date: Thu Mar 1 11:01:55 2007 -0800 [PATCH] Turn do_sync_file_range() into do_sync_mapping_range() do_sync_file_range() accepts a file * from which it takes an address_space to sync. Abstract out the bulk of the function into do_sync_mapping_range() which takes the address_space directly. This way callers who want to sync an address_space directly can take advantage of the functionality provided. do_sync_file_range() is preserved as a small wrapper around do_sync_mapping_range(). Ocfs2 in particular would like to use this to initiate a sync of a specific inode range during truncate, where a file * may not be available. Signed-off-by: Mark Fasheh Cc: Christoph Hellwig Signed-off-by: Andrew Morton commit 60b11392f1a09433740bda3048202213daa27736 Author: Mark Fasheh Date: Fri Feb 16 11:46:50 2007 -0800 ocfs2: zero tail of sparse files on truncate Since we don't zero on extend anymore, truncate needs to be fixed up to zero the part of a file between i_size and and end of it's cluster. Otherwise a subsequent extend could expose bad data. This introduced a new helper, which can be used in ocfs2_write(). Signed-off-by: Mark Fasheh commit 25baf2da1473d9dcde1a4c7b0ab26e7d67d9bf62 Author: Mark Fasheh Date: Wed Feb 14 15:30:30 2007 -0800 ocfs2: Teach ocfs2_get_block() about holes ocfs2_get_block() didn't understand sparse files, fix that. Also remove some code that isn't really useful anymore. We can fix up ocfs2_direct_IO_get_blocks() at the same time. Signed-off-by: Mark Fasheh commit 5069120b7227fd323152a3755a0aa6bdeb361310 Author: Mark Fasheh Date: Fri Feb 9 20:52:53 2007 -0800 ocfs2: remove ocfs2_prepare_write() and ocfs2_commit_write() These are no longer used, and can't handle file systems with sparse file allocation. Signed-off-by: Mark Fasheh commit 9517bac6cc7a7aa4fee63cb38a32cb6014e264c7 Author: Mark Fasheh Date: Fri Feb 9 20:24:12 2007 -0800 ocfs2: teach ocfs2_file_aio_write() about sparse files Unfortunately, ocfs2 can no longer make use of generic_file_aio_write_nlock() because allocating writes will require zeroing of pages adjacent to the I/O for cluster sizes greater than page size. Implement a custom file write here, which can order page locks for zeroing. This also has the advantage that cluster locks can easily be ordered outside of the page locks. Signed-off-by: Mark Fasheh commit 89488984ac23b0580f959b9ee549f2fcb1c2f194 Author: Mark Fasheh Date: Wed Jan 17 13:10:55 2007 -0800 ocfs2: Turn off shared writeable mmap for local files systems with holes. This will be turned back on once we can do allocation in ->page_mkwrite(). Signed-off-by: Mark Fasheh commit abf8b1569415bb4a8915a4884943ecd39c510957 Author: Mark Fasheh Date: Wed Jan 17 13:07:24 2007 -0800 ocfs2: abstract out allocation locking Right now, file allocation for ocfs2 is done within ocfs2_extend_file(), which is either called from ->setattr() (for an i_size change), or at the top of ocfs2_file_aio_write(). Inodes on file systems with sparse file support will want to do their allocation during the actual write call. In either case the cluster locking decisions are the same. We abstract out that code into a new function, ocfs2_lock_allocators() which will be used by a later patch to enable writing to sparse files. This also provides a nice cleanup of ocfs2_extend_allocation(). Signed-off-by: Mark Fasheh commit 3a0782d09c07aa3ec767ba6089cd15cfbfbfc508 Author: Mark Fasheh Date: Wed Jan 17 12:53:31 2007 -0800 ocfs2: teach extend/truncate about sparse files For ocfs2_truncate_file(), we eliminate the "simple" truncate case which no longer exists since i_size is not tied to i_clusters. In ocfs2_extend_file(), we skip the allocation / page zeroing code for file systems which understand sparse files. The core truncate code is changed to do a bottom up tree traversal. This gets abstracted out into it's own function. To make things more readable, most of the special case handling for in-inode extents from ocfs2_do_truncate() is also removed. Though write support for sparse files comes in a later patch, we at least update ocfs2_prepare_inode_for_write() to skip allocation for sparse files. Signed-off-by: Mark Fasheh commit 363041a5f74b953ab6b705ac9c88e5eda218a24b Author: Mark Fasheh Date: Wed Jan 17 12:31:35 2007 -0800 ocfs2: temporarily remove extent map caching The code in extent_map.c is not prepared to deal with a subtree being rotated between lookups. This can happen when filling holes in sparse files. Instead of a lengthy patch to update the code (which would likely lose the benefit of caching subtree roots), we remove most of the algorithms and implement a simple path based lookup. A less ambitious extent caching scheme will be added in a later patch. Signed-off-by: Mark Fasheh commit dcd0538ff4e854fa9d7f4630b359ca8fdb5cb5a8 Author: Mark Fasheh Date: Tue Jan 16 11:32:23 2007 -0800 ocfs2: sparse b-tree support Introduce tree rotations into the b-tree code. This will allow ocfs2 to support sparse files. Much of the added code is designed to be generic (in the ocfs2 sense) so that it can later be re-used to implement large extended attributes. This patch only adds the rotation code and does minimal updates to callers of the extent api. Signed-off-by: Mark Fasheh commit 6f16bf655c5795586dd2ac96a7c70e0b9a378746 Author: Mark Fasheh Date: Tue Mar 20 17:17:54 2007 -0700 ocfs2: small cleanup of ocfs2_request_delete() There are two checks in there (one for inode newness, one for other mounted nodes) which are unnecessary, so remove them. The DLM will allow the trylock in either case without any messaging overhead. Removing these makes ocfs2_request_delete() a one liner function, so just move the trylock out one level into ocfs2_query_inode_wipe(). Signed-off-by: Mark Fasheh commit 68e2b740c4b5394680cfefccddbdb486c5866a4c Author: Tiger Yang Date: Tue Mar 20 16:42:10 2007 -0700 ocfs2: remove unused code Remove node messaging code that becomes unused with the delete inode vote removal. [Removed even more cruft which I spotted during review --Mark] Signed-off-by: Tiger Yang Signed-off-by: Mark Fasheh commit 500086300e6dc5308a7328990bd50d17e075162b Author: Tiger Yang Date: Tue Mar 20 16:01:38 2007 -0700 ocfs2: Remove delete inode vote Ocfs2 currently does cluster-wide node messaging to check the open state of an inode during delete. This patch removes that mechanism in favor of an inode cluster lock which is taken at shared read when an inode is first read and dropped in clear_inode(). This allows a deleting node to test the liveness of an inode by attempting to take an exclusive lock. Signed-off-by: Tiger Yang Signed-off-by: Mark Fasheh commit 566ec03448052c096dc3982fbe573522dc0ba479 Author: Jamal Hadi Salim Date: Thu Apr 26 14:12:15 2007 -0700 [XFRM]: Missing bits to SAD info. This brings the SAD info in sync with net-2.6.22/net-2.6 Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller commit a9f5f70739363ccca2e771c274c4f015c5fb7a88 Author: Mark Fasheh Date: Thu Apr 26 11:43:43 2007 -0700 ocfs2: filter more error prints We don't want to print anything at all in ocfs2_lookup() when getting an error from ocfs2_iget() - it could be something as innocuous as a signal being detected in the dlm. ocfs2_permission() should filter on -ENOENT which ocfs2_meta_lock() can return if the inode was deleted on another node. Signed-off-by: Mark Fasheh commit bebe6f120b036349f7212205eeaf8248d4820c4b Author: Sunil Mushran Date: Tue Apr 17 13:53:38 2007 -0700 ocfs2: Replace panic() with emergency_restart() when fencing We have noticed panic() hanging leading us to a situation in which the node, while otherwise dead, is still disk heartbeating. This leads to a hung cluster as the other nodes are waiting for this node to stop disk heartbeating. This situation is only resolved by power resetting the box. Signed-off-by: Sunil Mushran Signed-off-by: Mark Fasheh commit 5d262cc7dd3d47784f8233ad4ec2cc5a08059b71 Author: Sunil Mushran Date: Tue Apr 17 13:49:19 2007 -0700 ocfs2: Silence compiler warnings Signed-off-by: Sunil Mushran Signed-off-by: Mark Fasheh commit be9e986b824b41c9d5cc5eca34ee3424c35fd162 Author: Mark Fasheh Date: Wed Apr 18 15:22:08 2007 -0700 ocfs2: Local mounts should skip inode updates We don't want the extent map and uptodate cache destruction in ocfs2_meta_lock_update() on a local mount, so skip that. This fixes several bugs with uptodate being cleared on buffers and extent maps being corrupted. Signed-off-by: Mark Fasheh commit 0d01af6e5dd6bc7abbcb6331021f8fee18005540 Author: Sunil Mushran Date: Tue Apr 17 13:32:20 2007 -0700 ocfs2_dlm: Call cond_resched_lock() once per hash bucket scan In dlm_migrate_all_locks(), we currently call cond_resched_lock() after processing each lockres in a hash bucket. Move it outside the loop so as to call it only after the entire hash bucket has been processed. Signed-off-by: Sunil Mushran Signed-off-by: Mark Fasheh commit 756a1501ddbbe73098aa031939460930f6edc9cd Author: Srinivas Eeda Date: Tue Apr 17 13:26:33 2007 -0700 ocfs2_dlm: fix race in dlm_remaster_locks There is a possibility that dlm_remaster_locks could overwride node->state with DLM_RECO_NODE_DATA_REQUESTED after dlm_reco_data_done_handler sets the node->state to DLM_RECO_NODE_DATA_DONE. This could lead to recovery getting stuck and requires a cluster reboot. Synchronize with dlm_reco_state_lock spinlock. Signed-off-by: Srinivas Eeda Signed-off-by: Mark Fasheh commit a48141db68e4b9143759435badcc1a49d9022db4 Author: Paul Mackerras Date: Thu Apr 26 22:24:31 2007 +1000 Revert "[POWERPC] Rename get_property to of_get_property: drivers" This reverts commit d05c7a80cf39ae7d0f8d0c3e47c93d51fcd393d3, which included changes which should go via other subsystem maintainers. commit 0999ed7f57728c1919b131207e47d9b311cfbd74 Author: Paul Mackerras Date: Thu Apr 26 17:01:04 2007 +1000 Revert "[POWERPC] DMA 4GB boundary protection" This reverts commit 618d3adc351a24c4c48437c767befb88ca2d199d, because it is superseded by 569975591c5530fdc9c7a3c45122e5e46f075a74. commit ee5ac9ddf2ea13be2418ac7d0ce5a930e78af013 Author: Stephen Rothwell Date: Thu Apr 26 00:03:53 2007 -0700 [SPARC]: device_node name constification fallout A couple of routines need their arguments to be const. Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 3e4d26508af6d03034a97583c895f33bef671d06 Author: David S. Miller Date: Wed Apr 25 15:58:22 2007 -0700 [SPARC64]: Convert SBUS over to generic iommu/strbuf structs. Signed-off-by: David S. Miller commit 66875088098f314af1a4d9e0cc47e617d643bffd Author: David S. Miller Date: Wed Apr 25 00:12:09 2007 -0700 [SPARC64]: Add generic iommu and strbuf structs to iommu.h Signed-off-by: David S. Miller commit 9b3627f389c07c5be9c86ac4d472a0d4fd47feac Author: David S. Miller Date: Tue Apr 24 23:51:18 2007 -0700 [SPARC64]: Consolidate {sbus,pci}_iommu_arena. Move to asm-sparc64/iommu.h and rename to plain "iommu_arena". Signed-off-by: David S. Miller commit 711b360d64418e88ed45f812e0ebd202073d888d Author: Stephen Rothwell Date: Thu Apr 12 14:38:34 2007 -0700 [SPARC]: Make device_node name and type const Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 3dfe10ee7caae9802d84a06fe7724274dea24020 Author: Stephen Rothwell Date: Thu Mar 29 11:22:57 2007 -0700 [SPARC64]: constify some paramaters of OF routines This starts bringing the PowerPC and Sparc64 implemetations back closer together. Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 374d4cac6283469f101282ca83ee008368bd8350 Author: David S. Miller Date: Thu Mar 29 01:57:57 2007 -0700 [TIGON3]: of_get_property() returns const. Signed-off-by: David S. Miller commit a165b4205e0097c7544ec3c59522a3b20ec14eb1 Author: David S. Miller Date: Thu Mar 29 01:50:16 2007 -0700 [SPARC64]: Fix PCI rework to adhere to of_get_property() const return. Signed-off-by: David S. Miller commit f1cfdb55f16596752e8a61a8570a90ee26af183a Author: David S. Miller Date: Thu Mar 15 22:52:18 2007 -0700 [SPARC64]: Document and fix calculation of pages_avail. It should be set to the total number of pages that the system will really have available after things like initmem, the bootmem map, and initrd are freed up. Signed-off-by: David S. Miller commit 0f3e25049e0a54916d0991c1eaa5f8df926c7f92 Author: David S. Miller Date: Thu Mar 15 21:44:03 2007 -0700 [SPARC64]: Make sure pbm->prom_node is setup easly enough in psycho.c It needs to be ready before we invoke pci_determine_mem_io_space(). Signed-off-by: David S. Miller commit 3996465392fd1632b671707d16bbc96a9481cfe2 Author: David S. Miller Date: Thu Mar 15 19:36:53 2007 -0700 [SPARC64]: Use bootmem_bootmap_pages() in choose_bootmap_pfn(). Signed-off-by: David S. Miller commit b93f2620231d4641bdbaaa952d3e8890687124bb Author: David S. Miller Date: Thu Mar 15 18:29:13 2007 -0700 [SPARC64]: Add proper header file extern for cmdline_memory_size. Signed-off-by: David S. Miller commit 9753f0d6502acd65761ff15244d26d0e88f0820a Author: David S. Miller Date: Thu Mar 15 18:26:00 2007 -0700 [SPARC64]: Kill sparc_ultra_dump_{i,d}tlb() While useful in odd circumstances to debug something, they are normally totally unused and anyone can fetch this code out of the history if they really need it. And in any event, the person who needs this kind of code is usually me :-) Signed-off-by: David S. Miller commit 85f1e1f66011e67e68065f2db4cde499decb9c84 Author: David S. Miller Date: Thu Mar 15 17:51:26 2007 -0700 [SPARC64]: Use DECLARE_BITMAP and BITS_TO_LONGS in mm/init.c Signed-off-by: David S. Miller commit 5be4a963675d3270fab7f55e8c4a2e56afd408f6 Author: David S. Miller Date: Thu Mar 15 16:00:29 2007 -0700 [SPARC64]: Give move verbose show_mem() output just like i386. We now report everything i386 does except for highmem which doesn't apply. Signed-off-by: David S. Miller commit 28256ca2e04c72eee1e83524d7f78ce5646030e2 Author: David S. Miller Date: Thu Mar 15 15:56:07 2007 -0700 [SPARC64]: Mark show_mem() printk's with KERN_INFO. Signed-off-by: David S. Miller commit a94aa2530643f02a4b243f81b5f6354b9b958d7e Author: David S. Miller Date: Thu Mar 15 15:50:11 2007 -0700 [SPARC64]: Kill kvaddr_to_phys() and friends. Just inline it into flush_icache_range() which is the only user. Signed-off-by: David S. Miller commit 4be5c34dc47b5a9e6f91c8f5937a93c464870b8e Author: David S. Miller Date: Thu Mar 15 15:44:05 2007 -0700 [SPARC64]: Privatize sun4u_get_pte() and fix name. __get_phys is only called from init.c as is prom_virt_to_phys(), __get_iospace() is not called at all, and sun4u_get_pte() is largely misnamed. Privatize the implementation and helper functions of sun4u_get_phys() to mm/init.c, and rename to kvaddr_to_paddr(). The only used of this thing is flush_icache_range(), and thus things can be considerably further simplified. For example, we should only see module or PAGE_OFFSET kernel addresses here, so we don't need the OBP firmware range handling at all. Signed-off-by: David S. Miller commit a0963bdfb91ca97c2b0b6d4ca81ff557fac66901 Author: David S. Miller Date: Thu Mar 15 15:09:06 2007 -0700 [SPARC64]: Kill _start[]/_end[] declarations in mm/init.c We already get those from asm/sections.h Signed-off-by: David S. Miller commit 4e286d5be63c93b17f8a82d6f3618faa9c1b025c Author: David S. Miller Date: Thu Mar 15 00:21:45 2007 -0700 [SPARC64]: MAX_PHYSADDR_BITS et al. really need to be 42 bits not 41. Signed-off-by: David S. Miller commit 0015d3d68c84eb33e6b380802ad61b23f7eb6523 Author: David S. Miller Date: Thu Mar 15 00:06:34 2007 -0700 [SPARC64]: Simplify read_obp_memory(). Kick out empty entries as soon as we spot them, and use memmove() instead of a silly loop to make the operation more clear. Signed-off-by: David S. Miller commit d78d0891d3dd976a2fb707c6c691d9cd5ed60727 Author: David S. Miller Date: Wed Mar 14 22:47:01 2007 -0700 [SPARC64]: Use SPARSEMEM_STATIC Decrease the SECTION_SIZE_BITS --> MAX_PHYSADDR_BITS range a little bit. The cost of going to SPARSEMEM_STATIC becomes 8K of BSS space, and in return we save a pointer dereferences on every page struct lookup. Even better we hit the main kernel image for the base address which is in a hugepage locked TLB entry. Signed-off-by: David S. Miller commit 43bed127376ff2ef9c268cf6688a43d0fbed2ff4 Author: David S. Miller Date: Wed Mar 14 18:33:49 2007 -0700 [SPARC64]: Use DECLARE_BITMAP in struct pci_iommu. Signed-off-by: David S. Miller commit 28f57e774d91ce01e03ff65caa2313bc8786b66f Author: David S. Miller Date: Mon Mar 12 19:40:26 2007 -0700 [SPARC64]: Force dummy host controller onto bus zero. This helps deal with the invisible bridge that sits between the host controller and the top-most visisble PCI devices on hypervisor systems. For example, on T1000 the bus-range property says 2 --> 4 and so there is a PCI express bridge at bus 2, devfn 0, etc. So if we don't force the dummy host controller to bus zero, we'll try to create two devices with the same domain/bus/devfn triplet. Also, add some more log diagnostics to make debugging stuff like this easyer. Signed-off-by: David S. Miller commit 97b3cf050b467dda571943ceadff5452bed04549 Author: David S. Miller Date: Sun Mar 11 16:42:53 2007 -0700 [SPARC64]: Add dummy host controller to root of all PCI domains. We fake up a dummy one in all cases because that is the simplest thing to do and it happens to be necessary for hypervisor systems. Signed-off-by: David S. Miller commit c6e87566ea080bbbe926c0e429fed48e6f680d93 Author: David S. Miller Date: Fri Mar 9 16:58:43 2007 -0800 [SPARC64]: Const'ify pci_iommu_ops. Based upon a similar patch for x86_64 written by Stephen Hemminger. Signed-off-by: David S. Miller commit 0bba2dd823fd995ed805ae5cbd5a1c1381257a12 Author: David S. Miller Date: Thu Mar 8 23:06:39 2007 -0800 [SPARC64]: Kill pbm->pci_first_slot. Set but never used. Signed-off-by: David S. Miller commit 3875c5c02d7112aa85f815d65d8add2e39ae9e34 Author: David S. Miller Date: Thu Mar 8 22:52:11 2007 -0800 [SPARC64]: Kill pci_controller->pbms_same_domain We don't do the "Simba APB is a PBM" bogosity for Sabre controllers any longer, so this pbms_same_domain thing is no longer necessary. Signed-off-by: David S. Miller commit 8d3aee937596d2ca6676c2c27789751445bf0bc9 Author: David S. Miller Date: Thu Mar 8 22:46:02 2007 -0800 [SPARC64]: Kill pci_controller->base_address_update(). Implemented but never actually used. Signed-off-by: David S. Miller commit 0bae5f81b6f8130f5197e59b0e2ad6820c766b2b Author: David S. Miller Date: Thu Mar 8 22:42:19 2007 -0800 [SPARC64]: Kill pci_controller->resource_adjust() All the implementations can be identical and generic, so no need for controller specific methods. Signed-off-by: David S. Miller commit 3487a1f9e719d36c9b2d4d492994b2dd815a58b7 Author: David S. Miller Date: Thu Mar 8 22:28:17 2007 -0800 [SPARC64]: Kill PBM ranges software state. It is only used in one spot and we can just fetch the OF property right there. Signed-off-by: David S. Miller commit 229177c7f38d6a2b1285b42da4b19d76346b4bac Author: David S. Miller Date: Thu Mar 8 22:11:00 2007 -0800 [SPARC64]: Kill PBM intmap software state. Set but never used. Signed-off-by: David S. Miller commit 9fd8b64761d3fe7e4ef567161be57e4234af5c1c Author: David S. Miller Date: Thu Mar 8 21:55:49 2007 -0800 [SPARC64]: Consolidate PCI mem/io resource determination. It can be done for every PCI configuration using OF properties. Signed-off-by: David S. Miller commit 01f94c4a6ced476ce69b895426fc29bfc48c69bd Author: David S. Miller Date: Sun Mar 4 12:53:19 2007 -0800 [SPARC64]: Fix sabre pci controllers with new probing scheme. The SIMBA APB bridge is strange, it is a PCI bridge but it lacks some standard OF properties, in particular it lacks a 'ranges' property. What you have to do is read the IO and MEM range registers in the APB bridge to determine the ranges handled by each bridge. So fill in the bus resources by doing that. Since we now handle this quirk in the generic PCI and OF device probing layers, we can flat out eliminate all of that code from the sabre pci controller driver. In fact we can thus eliminate completely another quirk of the sabre driver. It tried to make the two APB bridges look like PBMs but that makes zero sense now (and it's questionable whether it ever made sense). So now just use pbm_A and probe the whole PCI hierarchy using that as the root. This simplification allows many future cleanups to occur. Also, I've found yet another quirk that needs to be worked around while testing this. You can't use the 'class-code' OF firmware property, especially for IDE controllers. We have to read the value out of PCI config space or else we'll see the value the device was showing before it was programmed into native mode. I'm starting to think it might be wise to just read all of the values out of PCI config space instead of using the OF properties. :-/ Signed-off-by: David S. Miller commit a378fd0ee8ea6af5dafd0ab3d634f22b926b5ac4 Author: David S. Miller Date: Thu Mar 1 11:46:13 2007 -0800 [SPARC64]: Fix obppath pci device sysfs creation. Need to traverse recursively down child busses else we only get the file created under devices at the top-level. Signed-off-by: David S. Miller commit bc606f3c917aa453fca62b76c8e9998b4171f4fa Author: David S. Miller Date: Thu Mar 1 11:20:37 2007 -0800 [SPARC64]: Minor cleanups to schizo pci controller driver. Signed-off-by: David S. Miller commit 1e8a8cc52daa95e702303ca3ce67955a4c051d7d Author: David S. Miller Date: Wed Feb 28 23:38:38 2007 -0800 [SPARC64]: Internalize pci_memspace_mask. The only user was bus_dvma_to_mem() which is no longer used by any driver, so kill that, and the export of pci_memspace_mask. The only user now is the PCI mmap support code. Signed-off-by: David S. Miller commit a2fb23af1c31ad6e0c281e56d385f803229d57fa Author: David S. Miller Date: Wed Feb 28 23:35:04 2007 -0800 [SPARC64]: Probe PCI bus using OF device tree. Almost entirely taken from the 64-bit PowerPC PCI code. This allowed to eliminate a ton of cruft from the sparc64 PCI layer. Signed-off-by: David S. Miller commit deb66c4521e119442aa266553e8cbfc86eb71232 Author: David S. Miller Date: Wed Feb 28 18:01:38 2007 -0800 [SPARC64] isa: Convert to use pci_device_to_OF_node(). Also, do not try to compute resources by hand, instead use the pre-computed ones in the of_device. Signed-off-by: David S. Miller commit 1327e9b62fc88e64ffbbd42d61fccd34e521bb86 Author: David S. Miller Date: Wed Feb 28 17:55:46 2007 -0800 [SPARC64] ebus: Convert to use pci_device_to_OF_node(). Also, we don't need to store or use the PBM so kill that from the linux_ebus. Signed-off-by: David S. Miller commit 9b1caafe09ccec8e0103e9375b711e3a0c838260 Author: David S. Miller Date: Wed Feb 28 17:05:06 2007 -0800 [IGAFB]: Use pci_device_to_OF_node() on sparc. Also __sparc__ --> CONFIG_SPARC Signed-off-by: David S. Miller commit a02079cdb74dde27391d019abca4a37988504b4e Author: David S. Miller Date: Wed Feb 28 17:02:45 2007 -0800 [ATYFB]: Use pci_device_to_OF_node() in sparc. Signed-off-by: David S. Miller commit fa449bd602c8871da48e6dbadfa0faaf4d33d32e Author: David S. Miller Date: Wed Apr 25 16:01:51 2007 -0700 [OPENPROM]: Use pci_device_to_OF_node(). Signed-off-by: David S. Miller commit d297c31fd101473983c17734a7e8a3752da1880f Author: David S. Miller Date: Thu Mar 29 01:41:28 2007 -0700 [TULIP]: Use pci_device_to_OF_node() on sparc. Signed-off-by: David S. Miller commit 49345103fef36617abc9a649dfc34f7e921c6878 Author: David S. Miller Date: Thu Mar 29 01:39:44 2007 -0700 [TULIP]: Use CONFIG_SPARC consistently in ifdef tests. Signed-off-by: David S. Miller commit 49b6e95ff6d05722bcf7a52b00454566ce0c44eb Author: David S. Miller Date: Thu Mar 29 01:38:42 2007 -0700 [TG3]: Use pci_device_to_OF_node() on sparc. And use CONFIG_SPARC instead of CONFIG_SPARC64 as the test. Signed-off-by: David S. Miller commit 6f85a8597d1d0d8ceeec5a82881c6ddf5cfb45e5 Author: David S. Miller Date: Wed Feb 28 16:40:57 2007 -0800 [SUNHME]: Use pci_device_to_OF_node(). Signed-off-by: David S. Miller commit 457e1a8afbcf5deffa501f2e9829526c18ed55b5 Author: David S. Miller Date: Thu Mar 29 01:36:44 2007 -0700 [SUNGEM]: Consolidate powerpc and sparc MAC probing code. Signed-off-by: David S. Miller commit dadb830dac401c4b1420ee2fd6c7559871b43319 Author: David S. Miller Date: Wed Feb 28 15:42:50 2007 -0800 [SUNGEM]: __sparc__ --> CONFIG_SPARC Signed-off-by: David S. Miller commit 9f47df264fa53e562cafa0de4a405d0846a81fbd Author: David S. Miller Date: Thu Mar 29 01:33:46 2007 -0700 [RADEON]: Probe clocks and monitor using OF properties on sparc. Just like powerpc does. Signed-off-by: David S. Miller commit a8b8814bdfe3bb2bdfa23722de947bad8283037c Author: David S. Miller Date: Thu Mar 29 01:28:51 2007 -0700 [SPARC]: Use strcasecmp for OFW property name comparisons. This allows us to simplify sharing code with powerpc which has properties that have various forms of capitalization when on the sparc64 side the property is all lower-case. Signed-off-by: David S. Miller commit ded220bd8f0823771fc0a9bdf7f5bcbe543197b6 Author: David S. Miller Date: Thu Mar 29 01:18:42 2007 -0700 [STRING]: Move strcasecmp/strncasecmp to lib/string.c We have several platforms using local copies of identical code. Signed-off-by: David S. Miller commit 357418e7cac16fed4ca558c6037d189d2109c9c2 Author: Stephen Rothwell Date: Thu Mar 29 00:54:04 2007 -0700 [SPARC]: constify some paramaters of OF routines This starts bringing the PowerPC and Sparc implemetations back closer together. Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 64b94701c0714f814e640ff351d5f784fdc0381e Author: Stephen Rothwell Date: Thu Mar 29 00:53:28 2007 -0700 [SPARC/64]: constify of_get_property return Finally, we actually change the functions themselves. Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 3198514d2d10fb3ce5e49ba0c611764ad8a214d0 Author: Stephen Rothwell Date: Thu Mar 29 00:50:57 2007 -0700 [SPARC/64] constify of_get_property return: sound Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 66f3cb7ccfe6d735bd1fa435aebc9b985ac74e07 Author: Stephen Rothwell Date: Thu Mar 29 00:50:29 2007 -0700 [SPARC64] constify of_get_property return: include Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit ccf0dec6fcadb4e1c877b9bafb031a6bdb7112b9 Author: Stephen Rothwell Date: Thu Mar 29 00:49:54 2007 -0700 [SPARC/64] constify of_get_property return: drivers The only unfortunate bit here is that the name field of struct map_info is not const, so for now we put a cast on the assignment of it. Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 6a23acf3905287eb952a6f1dbbc8fb3e4eeae2f6 Author: Stephen Rothwell Date: Mon Apr 23 15:53:27 2007 -0700 [SPARC64]: constify of_get_property return: arch/sparc64 Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 8271f04242af8ddf8390f289cd6ef78fb3e3c6d9 Author: Stephen Rothwell Date: Thu Mar 29 00:47:23 2007 -0700 [SPARC]: constify of_get_property return: arch/sparc Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller commit 644923d4a5f117d437aefd47688d1141cc8361ed Author: Tony Breeds Date: Wed Mar 28 19:10:12 2007 -0700 [SPARC64]: Small cleanups time.c - Removes days_in_mo[], as it's almost identical to month_days[] - Use the leapyear() macro - Line length wrapping. Signed-off-by: Tony Breeds Signed-off-by: David S. Miller commit d62c6f093a1ef8fa5f8951e8da93c8ddd3ce193a Author: David S. Miller Date: Tue Mar 27 01:20:14 2007 -0700 [SPARC64]: Fix sparc64_next_event() error return. It should return an error code not a boolean. Based upon an hpet timer fix by Thomas Gleixner. Signed-off-by: David S. Miller commit 112f48716d9f292c92a033cff9e3ce7405ed4280 Author: David S. Miller Date: Mon Mar 5 15:28:37 2007 -0800 [SPARC64]: Add clocksource/clockevents support. I'd like to thank John Stul and others for helping me along the way. A lot of cleanups fell out of this. For example, the get_compare() tick_op was totally unused, so was deleted. And the most often used tick_op members were grouped together for cache-friendlyness. The sparc64 TSC is given to the kernel as a one-shot timer. tick_ops->init_timer() simply turns off the privileged bit in the tick register (when possible), and disables the interrupt by setting bit 63 in the compare register. The ->disable_irq() op also sets this bit. tick_ops->add_compare() is changed to: 1) Add the given delta to "tick" not to "compare" 2) Return a boolean which, if true, means that the tick value read after writing the compare value was found to have incremented past the initial tick value. This mirrors logic used in the HPET driver's ->next_event() method. Each tick_ops implementation also now provides a name string. And we feed this into the clocksource and clockevents layers. Signed-off-by: David S. Miller commit 038cb01ea69cb24ecf30e3ec882e429c84badbeb Author: David S. Miller Date: Thu Feb 22 06:24:45 2007 -0800 [SPARC64]: Add tick_nohz_{stop,restart}_sched_tick() calls to cpu_idle(). Signed-off-by: David S. Miller commit 777a447529ad138f5fceb9c9ad28bab19848f277 Author: David S. Miller Date: Thu Feb 22 06:24:10 2007 -0800 [SPARC64]: Unify timer interrupt handler. Things were scattered all over the place, split between SMP and non-SMP. Unify it all so that dyntick support is easier to add. Signed-off-by: David S. Miller commit a58c9f3c1e929c3c323c26dbdafef46373a719d4 Author: David S. Miller Date: Thu Feb 22 04:16:21 2007 -0800 [SPARC64]: Synchronize RTC clock via timer just like x86. Signed-off-by: David S. Miller commit bfbf3c0968498f5232c02965cf41695edae1bc4d Author: Matthias Kaehlcke Date: Thu Apr 26 01:41:49 2007 -0700 [ATM]: Use mutex instead of binary semaphore in FORE Systems 200E-series driver (akpm: remove CVS control string too) Signed-off-by: Matthias Kaehlcke Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 74da9d88bf5ffd31aed61a0b19519684ad744ded Author: Andrew Morton Date: Thu Apr 26 01:41:01 2007 -0700 [BLUETOOTH] rfcomm_worker(): fix wakeup race Set TASK_INTERRUPTIBLE prior to testing the flag to avoid missed wakeups. Signed-off-by: Andrew Morton Acked-by: Marcel Holtmann Signed-off-by: David S. Miller commit 9198d2220d29b87ac3a05a3b791c50bb8a014d63 Author: Alexandra N. Kossovsky Date: Thu Apr 26 01:40:13 2007 -0700 [NET]: bonding documentation fix for multiple bonding interfaces Fix bonding driver documentation for the case of multiple bonding interfaces. Signed-off-by: "Alexandra N. Kossovsky" Acked-by: Jay Vosburgh Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 4ef8d0aeafda8388dd51f2671b7059192b1e5a5f Author: Milind Arun Choudhary Date: Thu Apr 26 01:37:44 2007 -0700 [NET]: SPIN_LOCK_UNLOCKED cleanup in drivers/atm, net SPIN_LOCK_UNLOCKED cleanup,use __SPIN_LOCK_UNLOCKED instead Signed-off-by: Milind Arun Choudhary Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 1c8ea5aee0b16409295d96a5e8984bd902f06a77 Author: Andrew Morton Date: Thu Apr 26 01:36:49 2007 -0700 [IRDA] irda_device_dongle_init: fix kzalloc(GFP_KERNEL) in spinlock Fix http://bugzilla.kernel.org/show_bug.cgi?id=8343 Signed-off-by: Andrew Morton Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit 14690fc649f4c59712f497135f7323eb8ceceaaf Author: Martin Peschke Date: Thu Apr 26 01:03:43 2007 -0700 [SUNRPC]: cleanup: use seq_release_private() where appropriate We can save some lines of code by using seq_release_private(). Signed-off-by: Martin Peschke Acked-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit f8a6d97043f9adc25889876b681998b77f543bfa Author: Alexey Dobriyan Date: Thu Apr 26 01:02:51 2007 -0700 [AF_IUCV]: Fix compilation on s390-up CC [M] net/iucv/iucv.o net/iucv/iucv.c: In function 'iucv_init': net/iucv/iucv.c:1556: error: 'iucv_cpu_notifier' undeclared (first use in this function) Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 57cd5f754e04240ee587c51b7be8d3b7793542ae Author: Milind Arun Choudhary Date: Thu Apr 26 01:01:53 2007 -0700 [NET]: ROUND_UP macro cleanup in drivers/net/ppp_generic.c ROUND_UP macro cleanup use DIV_ROUND_UP Signed-off-by: Milind Arun Choudhary Acked-by: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 36226a8ded46b89a94f9de5976f554bb5e02d84c Author: Brian Braunstein Date: Thu Apr 26 01:00:55 2007 -0700 [NET] tun/tap: fixed hw address handling Fixed tun/tap driver's handling of hw addresses. The hw address is stored in both the net_device.dev_addr and tun.dev_addr fields. These fields were not kept synchronized, and in fact weren't even initialized to the same value. Now during both init and when performing SIOCSIFHWADDR on the tun device these values are both updated. However, if SIOCSIFHWADDR is performed on the net device directly (for instance, setting the hw address using ifconfig), the tun device does not get updated. Perhaps the tun.dev_addr field should be removed completely at some point, as it is redundant and net_device.dev_addr can be used anywhere it is used. Signed-off-by: Brian Braunstein Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 48491e6bdb8fa73751cc95f740175ec799db5d55 Author: Robert P. J. Day Date: Thu Apr 26 00:59:27 2007 -0700 [NET]: Delete unused header file linux/if_wanpipe_common.h Delete the unreferenced header file include/linux/if_wanpipe_common.h, as well as the reference to it in the Doc file. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit c1a068f6b0c38665c079e8d4ca241e24020eff36 Author: Robert P. J. Day Date: Thu Apr 26 00:58:39 2007 -0700 [NET]: Delete unused header file linux/sdla_fr.h. Delete the unreferenced header file include/linux/sdla_fr.h. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton commit 42bad1da506cafa7041a02ab84033a724afe88ac Author: Adrian Bunk Date: Thu Apr 26 00:57:41 2007 -0700 [NETLINK]: Possible cleanups. - make the following needlessly global variables static: - core/rtnetlink.c: struct rtnl_msg_handlers[] - netfilter/nf_conntrack_proto.c: struct nf_ct_protos[] - make the following needlessly global functions static: - core/rtnetlink.c: rtnl_dump_all() - netlink/af_netlink.c: netlink_queue_skip() Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 55404bca6c45595fee1a546f1a0cc616aeef0b00 Author: Andrew Morton Date: Thu Apr 26 00:55:53 2007 -0700 [NET]: Fix yam.c drivers/net/hamradio/yam.c: In function `yam_tx_byte': drivers/net/hamradio/yam.c:643: warning: passing arg 1 of `skb_copy_from_linear_data_offset' from incompatible pointer type Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit eefa3906283a2b60a6d02a2cda593a7d7d7946c5 Author: Jean Delvare Date: Thu Apr 26 00:44:22 2007 -0700 [NET]: Clean up sk_buff walkers. I noticed recently that, in skb_checksum(), "offset" and "start" are essentially the same thing and have the same value throughout the function, despite being computed differently. Using a single variable allows some cleanups and makes the skb_checksum() function smaller, more readable, and presumably marginally faster. We appear to have many other "sk_buff walker" functions built on the exact same model, so the cleanup applies to them, too. Here is a list of the functions I found to be affected: net/appletalk/ddp.c:atalk_sum_skb() net/core/datagram.c:skb_copy_datagram_iovec() net/core/datagram.c:skb_copy_and_csum_datagram() net/core/skbuff.c:skb_copy_bits() net/core/skbuff.c:skb_store_bits() net/core/skbuff.c:skb_checksum() net/core/skbuff.c:skb_copy_and_csum_bit() net/core/user_dma.c:dma_skb_copy_datagram_iovec() net/xfrm/xfrm_algo.c:skb_icv_walk() net/xfrm/xfrm_algo.c:skb_to_sgvec() OTOH, I admit I'm a bit surprised, the cleanup is rather obvious so I'm really wondering if I am missing something. Can anyone please comment on this? Signed-off-by: Jean Delvare Signed-off-by: David S. Miller commit 28d8909bc790d936ce33f4402adf7577533bbd4b Author: Jamal Hadi Salim Date: Thu Apr 26 00:10:29 2007 -0700 [XFRM]: Export SAD info. On a system with a lot of SAs, counting SAD entries chews useful CPU time since you need to dump the whole SAD to user space; i.e something like ip xfrm state ls | grep -i src | wc -l I have seen taking literally minutes on a 40K SAs when the system is swapping. With this patch, some of the SAD info (that was already being tracked) is exposed to user space. i.e you do: ip xfrm state count And you get the count; you can also pass -s to the command line and get the hash info. Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller commit 06d63cc51d47f572009138a7f3ac34d95773405d Author: Randy Dunlap Date: Wed Apr 25 22:41:34 2007 -0700 [MTD] [MAPS] fix plat-ram printk format drivers/mtd/maps/plat-ram.c:172: warning: format '%lx' expects type 'long unsigned int', but argument 4 has type 'resource_size_t' Signed-off-by: Randy Dunlap Signed-off-by: David Woodhouse commit f6449f4ece2bf283500bda73edcbea82f2cb3a1b Author: Andrew Morton Date: Thu Apr 26 07:27:04 2007 +0100 [JFFS2] Fix compr_rubin.c build after include file elimination. It seems to be silly season lately. (Oops, test builds are more useful if the file in question is actually configured on. dwmw2). Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit f50393fe869ba457cd75569c74c0f9bd2e7f7a0f Author: Mark Huth Date: Tue Mar 6 08:57:26 2007 -0800 e1000: FIX: Stop raw interrupts disabled nag from RT Current e1000_xmit_frame spews raw interrupt disabled nag messages when used with RT kernel patches. This patch uses spin_trylock_irqsave, which allows RT patches to properly manage the irq semantics. Signed-off-by: Mark Huth Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit 31d76442f719af834718cbf5bf866370acc36093 Author: Bruce Allan Date: Tue Mar 6 08:57:24 2007 -0800 e1000: FIX: firmware handover bits Upon code inspection it was spotted that the firmware handover bit get/set mismatched, which may have resulted in management issues on PCI-E adapters. Setting them correctly may fix some management issues such as arp routing etc. Signed-off-by: Auke Kok Signed-off-by: Bruce Allan Signed-off-by: Jeff Garzik commit e0aac5a289b1dacbc94bd9ae8c449bcdf9ab508c Author: Auke Kok Date: Tue Mar 6 08:57:21 2007 -0800 e1000: FIX: be ready for incoming irq at pci_request_irq DEBUG_SHIRQ code exposed that e1000 was not ready for incoming interrupts after having called pci_request_irq. This obviously requires us to finish our software setup which assigns the irq handler before we request the irq. Signed-off-by: Auke Kok Signed-off-by: Jeff Garzik commit e900a7d90ae1486ac95c10e0b7337fc2c2eda529 Author: Stephen Smalley Date: Thu Apr 19 14:16:19 2007 -0400 selinux: preserve boolean values across policy reloads At present, the userland policy loading code has to go through contortions to preserve boolean values across policy reloads, and cannot do so atomically. As this is what we always want to do for reloads, let the kernel preserve them instead. Signed-off-by: Stephen Smalley Acked-by: Karl MacMillan Signed-off-by: James Morris commit bce34bc0eef03c68b5c49a3cc5bc77c84760cfe2 Author: James Carter Date: Wed Apr 4 16:18:50 2007 -0400 selinux: change numbering of boolean directory inodes in selinuxfs Change the numbering of the booleans directory inodes in selinuxfs to provide more room for new inodes without a conflict in inode numbers and to be consistent with how inode numbering is done in the initial_contexts directory. Signed-off-by: James Carter Acked-by: Eric Paris Acked-by: Stephen Smalley Signed-off-by: James Morris commit 68b00df9bb5f38e87c102b3179a18eba9c9937a8 Author: James Carter Date: Wed Apr 4 16:18:43 2007 -0400 selinux: remove unused enumeration constant from selinuxfs Remove the unused enumeration constant, SEL_AVC, from the sel_inos enumeration in selinuxfs. Signed-off-by: James Carter Acked-by: Eric Paris Acked-by: Stephen Smalley Signed-off-by: James Morris commit 6174eafce3a38114adc6058e2872434c53feae87 Author: James Carter Date: Wed Apr 4 16:18:39 2007 -0400 selinux: explicitly number all selinuxfs inodes Explicitly number all selinuxfs inodes to prevent a conflict between inodes numbered using last_ino when created with new_inode() and those labeled explicitly. Signed-off-by: James Carter Acked-by: Eric Paris Acked-by: Stephen Smalley Signed-off-by: James Morris commit f0ee2e467ffa68c3122128b704c1540ee294b748 Author: James Carter Date: Wed Apr 4 10:11:29 2007 -0400 selinux: export initial SID contexts via selinuxfs Make the initial SID contexts accessible to userspace via selinuxfs. An initial use of this support will be to make the unlabeled context available to libselinux for use for invalidated userspace SIDs. Signed-off-by: James Carter Acked-by: Stephen Smalley Signed-off-by: James Morris commit a764ae4b0781fac75f9657bc737c37ae59888389 Author: Stephen Smalley Date: Mon Mar 26 13:36:26 2007 -0400 selinux: remove userland security class and permission definitions Remove userland security class and permission definitions from the kernel as the kernel only needs to use and validate its own class and permission definitions and userland definitions may change. Signed-off-by: Stephen Smalley Signed-off-by: James Morris commit 4f6a993f96a256e83b9be7612f958c7bc4ca9f00 Author: Paul Moore Date: Thu Mar 1 14:35:22 2007 -0500 SELinux: move security_skb_extlbl_sid() out of the security server As suggested, move the security_skb_extlbl_sid() function out of the security server and into the SELinux hooks file. Signed-off-by: Paul Moore Acked-by: Stephen Smalley Signed-off-by: James Morris commit 588a31577f86a5cd8b0bcde6026e4e6dcac8c383 Author: Stephen Smalley Date: Fri Feb 23 09:20:09 2007 -0500 MAINTAINERS: update selinux entry Add Eric Paris as an SELinux maintainer. Signed-off-by: James Morris commit c60475bf35fc5fa10198df89187ab148527e72f7 Author: Paul Moore Date: Wed Feb 28 15:14:23 2007 -0500 SELinux: rename selinux_netlabel.h to netlabel.h In the beginning I named the file selinux_netlabel.h to avoid potential namespace colisions. However, over time I have realized that there are several other similar cases of multiple header files with the same name so I'm changing the name to something which better fits with existing naming conventions. Signed-off-by: Paul Moore Signed-off-by: James Morris commit 5778eabd9cdbf16ea3e40248c452b4fd25554d11 Author: Paul Moore Date: Wed Feb 28 15:14:22 2007 -0500 SELinux: extract the NetLabel SELinux support from the security server Up until this patch the functions which have provided NetLabel support to SELinux have been integrated into the SELinux security server, which for various reasons is not really ideal. This patch makes an effort to extract as much of the NetLabel support from the security server as possibile and move it into it's own file within the SELinux directory structure. Signed-off-by: Paul Moore Signed-off-by: James Morris commit 128c6b6cbffc8203e13ea5712a8aa65d2ed82e4e Author: Paul Moore Date: Wed Feb 28 15:14:21 2007 -0500 NetLabel: convert a BUG_ON in the CIPSO code to a runtime check This patch changes a BUG_ON in the CIPSO code to a runtime check. It should also increase the readability of the code as it replaces an unexplained constant with a well defined macro. Signed-off-by: Paul Moore Signed-off-by: James Morris commit f998e8cb52396c6a197d14f6afb07144324aea6d Author: Paul Moore Date: Wed Feb 28 15:14:20 2007 -0500 NetLabel: cleanup and document CIPSO constants This patch collects all of the CIPSO constants and puts them in one place; it also documents each value explaining how the value is derived. Signed-off-by: Paul Moore Signed-off-by: James Morris commit 98486fa2f4894e2b01e325c659635596bdec1614 Author: Stephen Hemminger Date: Wed Apr 25 22:08:46 2007 -0700 [BRIDGE]: Missing rtnl. Writing to /sys/class/net/brX/bridge/stp_state causes a warning because RTNL is not held when call br_stp_if.c Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit c2886d6259b8faac4c05ffd9c3c401ac84478de0 Author: Stephen Hemminger Date: Wed Apr 25 22:07:58 2007 -0700 [BRIDGE]: if no STP then forward all BPDUs If a bridge is not running STP, then it has no way to detect a cycle in the network. But if it is not running STP and some other machine or device is running STP, then if STP BPDU's get forwarded to it can detect the cycle. This is how the old 2.4 and early 2.6 code worked. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 2111f8b9e58fd04b87b8b07d66485f255a57b0bb Author: Stephen Hemminger Date: Wed Apr 25 22:05:55 2007 -0700 [BRIDGE]: drop PAUSE frames Pause frames should never make it out of the network device into the stack. But if a device was misconfigured, it might happen. So drop pause frames in bridge. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 83aa0938ff59e8ef6d0b99260063ebe84fc84a16 Author: Stephen Hemminger Date: Wed Apr 25 22:03:10 2007 -0700 [BRIDGE]: don't change packet type The change to forward STP bpdu's (for usermode STP) through normal path, changed the packet type in the process. Since link local stuff is multicast, it should stay pkt_type = PACKET_MULTICAST. The code was probably copy/pasted incorrectly from the bridge pseudo-device receive path. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit e1ec7842df5db897516d73c76bd2a568b4abc33b Author: YOSHIFUJI Hideaki Date: Tue Apr 24 20:44:52 2007 +0900 [IPV6] NDISC: Unify main process of sending ND messages. Because ndisc_send_na(), ndisc_send_ns() and ndisc_send_rs() are almost identical, so let's unify their common part. With gcc (GCC) 3.3.5 (Debian 1:3.3.5-13) on i386, Before: text data bss dec hex filename 14689 364 24 15077 3ae5 net/ipv6/ndisc.o After: text data bss dec hex filename 12317 364 24 12705 31a1 net/ipv6/ndisc.o Signed-off-by: YOSHIFUJI Hideaki commit c53b3590bb294a42121b640e8309379752482b38 Author: YOSHIFUJI Hideaki Date: Tue Apr 24 20:44:50 2007 +0900 [IPV6] XFRM: Use ip6addr_any where applicable. Signed-off-by: YOSHIFUJI Hideaki commit df8981dc1928f3a231d91f27c2b3dc373fb4d410 Author: YOSHIFUJI Hideaki Date: Tue Apr 24 20:44:49 2007 +0900 [IPV6]: Export in6addr_any for future use. Signed-off-by: YOSHIFUJI Hideaki commit 5056a1ef9e2597cff7b15904fbc74193f316fc40 Author: YOSHIFUJI Hideaki Date: Tue Apr 24 20:44:48 2007 +0900 [IPV4] IP_GRE: Unify code path to get hash array index. Signed-off-by: YOSHIFUJI Hideaki commit 87d1a164df0b5e297cda698724ea7984d8392b06 Author: YOSHIFUJI Hideaki Date: Tue Apr 24 20:44:47 2007 +0900 [IPV4] IPIP: Unify code path to get hash array index. Signed-off-by: YOSHIFUJI Hideaki commit 420fe234ad7adaa5a5445e5fab83b1485ed9e0f3 Author: YOSHIFUJI Hideaki Date: Tue Apr 24 20:44:47 2007 +0900 [IPV6] SIT: Unify code path to get hash array index. Signed-off-by: YOSHIFUJI Hideaki commit 30041e4af426bc9ab7a73440ce4a7c78881b6001 Author: David S. Miller Date: Tue Apr 24 22:15:40 2007 -0700 [IPV6]: Fix Makefile thinko. obj-$(CONFIG_PROC_FS) --> ipv6-$(CONFIG_PROC_FS) Signed-off-by: David S. Miller commit 7f7d9a6b96c5708c5184cbed61bbc15b163a0f08 Author: Herbert Xu Date: Tue Apr 24 21:54:09 2007 -0700 [IPV6]: Consolidate common SNMP code This patch moves the non-proc SNMP code into addrconf.c and reuses IPv4 SNMP code where applicable. As a result we can skip proc.o if /proc is disabled. Note that I've made a number of functions static since they're only used by addrconf.c for now. If they ever get used elsewhere we can always remove the static. Signed-off-by: Herbert Xu Acked-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 5e0f04351d11e07a23b5ab4914282cbb78027e50 Author: Herbert Xu Date: Tue Apr 24 21:53:35 2007 -0700 [IPV4]: Consolidate common SNMP code This patch moves the SNMP code shared between IPv4/IPv6 from proc.c into net/ipv4/af_inet.c. This makes sense because these functions aren't specific to /proc. As a result we can again skip proc.o if /proc is disabled. Signed-off-by: Herbert Xu Acked-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit bb7ec6dfb5aa32b5b4d7d6388b4098b33cd01e8c Author: YOSHIFUJI Hideaki Date: Tue Apr 24 16:22:42 2007 -0700 [IPV4]: Fix build without procfs. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 84299b3bc4eaedc0734fcc9052b01291e44445fc Author: YOSHIFUJI Hideaki Date: Tue Apr 24 16:21:38 2007 -0700 [TCP]: Fix linkage errors on i386. To avoid raw division, use ktime_to_timeval() to get usec. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 1f9eda7e2b67898fb8e79b3aa3880211b51235e6 Author: Allan Stephens Date: Tue Apr 24 14:51:55 2007 -0700 [TIPC]: Enhancements to msg_set_bits() routine This patch makes two enhancements to msg_set_bits(): 1) It now ignores any bits of the new field value that are not covered by the mask being used. (Previously, if the new value exceeded the size of the mask the extra bits could corrupt other fields in the message header word being updated.) 2) The code has been optimized to minimize the number of run-time endianness conversion operations by leveraging the fact that the mask (and, in some cases, the value as well) is constant and the necessary conversion can be performed by the compiler. Signed-off-by: Allan Stephens Signed-off-by: Jon Paul Maloy Signed-off-by: David S. Miller commit 43fb45cb79e9441a79ece206cf741774500dd627 Author: Johannes Berg Date: Tue Apr 24 14:07:27 2007 -0700 [WIRELESS] cfg80211: Update comment for locking. This patch adds a comment that was part of my rtnl locking patch for cfg80211 but which I forgot for the merge. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit f9d106a6d53b57b78eae5544f9582c643343a764 Author: Herbert Xu Date: Mon Apr 23 22:36:13 2007 -0700 [NET]: Warn about GSO/checksum abuse Now that Patrick has added the code to deal with GSO in netfilter, we no longer need the crutch that computes partial checksums just before transmission. This patch turns this into a warning again. If this goes OK, we can then turn it into a BUG_ON and remove the gso_send_check cruft. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller commit 7752237e9f07b316f81aebdc43f0d7c9a4ba0acf Author: Stephen Hemminger Date: Mon Apr 23 22:28:23 2007 -0700 [TCP] TCP YEAH: Use vegas dont copy it. Rather than using a copy of vegas code, the YEAH code should just have it exported so there is common code. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 164891aadf1721fca4dce473bb0e0998181537c6 Author: Stephen Hemminger Date: Mon Apr 23 22:26:16 2007 -0700 [TCP]: Congestion control API update. Do some simple changes to make congestion control API faster/cleaner. * use ktime_t rather than timeval * merge rtt sampling into existing ack callback this means one indirect call versus two per ack. * use flags bits to store options/settings Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 65d1b4a7e73fe0e1f5275ad7d2d3547981480886 Author: Stephen Hemminger Date: Mon Apr 23 22:24:32 2007 -0700 [TCP]: TCP Illinois update. This version more closely matches the paper, and fixes several math errors. The biggest difference is that it updates alpha/beta once per RTT Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 42431592e74a968d919a46baf0515a2ee6978dac Author: John W. Linville Date: Mon Apr 23 13:28:49 2007 -0700 [WIRELESS] drivers/net/wireless/Kconfig: correct minor typo Correct minor typo in drivers/net/wireless/Kconfig identified by Stefano Brivio . Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 9e101eab153073d8a1fc7ea22b20af65de8ab44b Author: Johannes Berg Date: Mon Apr 23 12:20:55 2007 -0700 [WIRELESS]: Remove wext over netlink. As scheduled, this patch removes the pointless wext over netlink code. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 704232c2718c9d4b3375ec15a14fc0397970c449 Author: Johannes Berg Date: Mon Apr 23 12:20:05 2007 -0700 [WIRELESS] cfg80211: New wireless config infrastructure. This patch creates the core cfg80211 code along with some sysfs bits. This is a stripped down version to allow mac80211 to function, but doesn't include any configuration yet except for creating and removing virtual interfaces. This patch includes the nl80211 header file but it only contains the interface types which the cfg80211 interface for creating virtual interfaces relies on. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 2a5e1c0eb9efe26eed1dd072fe08de5797a7efd5 Author: Johannes Berg Date: Mon Apr 23 12:19:12 2007 -0700 [WIRELESS]: Refactor wireless Kconfig. This patch refactors the wireless Kconfig all over and already introduces net/wireless/Kconfig with just the WEXT bit for now, the cfg80211 patch will add to that as well. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 724c6b35ecff0fb68bbb315a34b2f9cb694865d3 Author: Johannes Berg Date: Mon Apr 23 12:18:20 2007 -0700 [WIRELESS]: Update MAINTAINERS for wireless mailing list. This patch adds the linux-wireless mailing list to all appropriate entries in the MAINTAINERS file. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller commit 372cc74c8b41d808af0a3fa8b11795cba79e7299 Author: Andrew Morton Date: Sun Apr 22 23:22:24 2007 -0700 [NET]: Prevent much sadness in qdisc_lock_tree(). Signed-off-by: Andrew Morton Signed-off-by: David S. Miller commit 97fc8d0bc58cd09e62dc06ea5a64b58841738934 Author: YOSHIFUJI Hideaki Date: Sat Apr 21 19:52:04 2007 -0700 [IPV6] SNMP: Use put_unaligned() instead of memcpy(). Hint from David Miller . Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 952a10be3272c4b5b7839b09cb0483dc72137101 Author: YOSHIFUJI Hideaki Date: Sat Apr 21 20:13:44 2007 +0900 [IPV6] SNMP: Fix several warnings without procfs. Signed-off-by: YOSHIFUJI Hideaki commit 2334e973559e119fa4161047035f03ad97a8676a Author: YOSHIFUJI Hideaki Date: Sat Apr 21 20:12:43 2007 +0900 [IPV6] SNMP: Avoid unaligned accesses. Because stats pointer may not be aligned for u64, use memcpy to fill u64 values. Issue reported by David Miller . Signed-off-by: YOSHIFUJI Hideaki commit 9e412ba7632f71259a53085665d4983b78257b7c Author: Ilpo Järvinen Date: Fri Apr 20 22:18:02 2007 -0700 [TCP]: Sed magic converts func(sk, tp, ...) -> func(sk, ...) This is (mostly) automated change using magic: sed -e '/struct sock \*sk/ N' -e '/struct sock \*sk/ N' -e '/struct sock \*sk/ N' -e '/struct sock \*sk/ N' -e 's|struct sock \*sk,[\n\t ]*struct tcp_sock \*tp\([^{]*\n{\n\)| struct sock \*sk\1\tstruct tcp_sock *tp = tcp_sk(sk);\n|g' -e 's|struct sock \*sk, struct tcp_sock \*tp| struct sock \*sk|g' -e 's|sk, tp\([^-]\)|sk\1|g' Fixed four unused variable (tp) warnings that were introduced. In addition, manually added newlines after local variables and tweaked function arguments positioning. $ gcc --version gcc (GCC) 4.1.1 20060525 (Red Hat 4.1.1-1) ... $ codiff -fV built-in.o.old built-in.o.new net/ipv4/route.c: rt_cache_flush | +14 1 function changed, 14 bytes added net/ipv4/tcp.c: tcp_setsockopt | -5 tcp_sendpage | -25 tcp_sendmsg | -16 3 functions changed, 46 bytes removed net/ipv4/tcp_input.c: tcp_try_undo_recovery | +3 tcp_try_undo_dsack | +2 tcp_mark_head_lost | -12 tcp_ack | -15 tcp_event_data_recv | -32 tcp_rcv_state_process | -10 tcp_rcv_established | +1 7 functions changed, 6 bytes added, 69 bytes removed, diff: -63 net/ipv4/tcp_output.c: update_send_head | -9 tcp_transmit_skb | +19 tcp_cwnd_validate | +1 tcp_write_wakeup | -17 __tcp_push_pending_frames | -25 tcp_push_one | -8 tcp_send_fin | -4 7 functions changed, 20 bytes added, 63 bytes removed, diff: -43 built-in.o.new: 18 functions changed, 40 bytes added, 178 bytes removed, diff: -138 Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 38b4da383705394788aa09208917ba200792de4b Author: Borislav Petkov Date: Fri Apr 20 22:14:10 2007 -0700 [NET]: Fix comments for register_netdev(). Correct the function name in the comments supplied with register_netdev() Signed-off-by: Borislav Petkov Signed-off-by: David S. Miller commit b450777a572d68975c8748b0d48d517dd3468ea6 Author: G. Liakhovetski Date: Fri Apr 20 22:12:48 2007 -0700 [IrDA]: Misc spelling corrections. Spelling corrections, from "to" to "too". Signed-off-by: G. Liakhovetski Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit 599b1fa91439cff8605a71f1a2b5bb42c177b667 Author: Samuel Ortiz Date: Fri Apr 20 22:12:07 2007 -0700 [IrDA]: Adding carriage returns to mcs7780 debug statements Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit c3ea9fa2741320f9cade15efe10559b549af4ebf Author: Samuel Ortiz Date: Fri Apr 20 22:10:13 2007 -0700 [IrDA] af_irda: IRDA_ASSERT cleanups In af_irda.c, the multiple IRDA_ASSERT() are either hiding bugs, useless, or returning the wrong value. Let's clean that up. Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit d7f48d1a9398a3bd7bb6f4774640b24a0294cda3 Author: Samuel Ortiz Date: Fri Apr 20 22:09:33 2007 -0700 [IrDA] af_irda: irda_accept cleanup This patch removes a cut'n'paste copy of wait_event_interruptible from irda_accept. Signed-off-by: Samuel Ortiz Acked-by: Olaf Kirch Signed-off-by: David S. Miller commit 6e66aa15d8873ae7418d5afc6476daec466ff93b Author: Olaf Kirch Date: Fri Apr 20 22:08:15 2007 -0700 [IrDA] af_irda: Silence kernel message in irda_recvmsg_stream This patch silences an IRDA_ASSERT in irda_recvmsg_stream, as described in http://bugzilla.kernel.org/show_bug.cgi?id=7512 irda_disconnect_indication would set sk->sk_err to ECONNRESET, and a subsequent call to recvmsg would print an irritating kernel message and return -1. When a connected socket is closed by the peer, recvmsg should return 0 rather than an error. This patch fixes this. Signed-off-by: Olaf Kirch Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit 305f2aa18214555e611ad05e586dd385e64ab665 Author: Olaf Kirch Date: Fri Apr 20 22:05:27 2007 -0700 [IrDA] af_irda: irda_recvmsg_stream cleanup This patch cleans up some code in irda_recvmsg_stream, replacing some homebrew code with prepare_to_wait/finish_wait, and by making the code honor sock_rcvtimeo. Signed-off-by: Olaf Kirch Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit 9958089a43ae8a9af07402461c0b2b7548c7341e Author: Andi Kleen Date: Fri Apr 20 17:12:43 2007 -0700 [NET]: Move sk_setup_caps() out of line. It is far too large to be an inline and not in any hot paths. Signed-off-by: Andi Kleen Signed-off-by: David S. Miller commit 4ac02bab77438b484a5cf855a002fb6a1d592894 Author: Andi Kleen Date: Fri Apr 20 17:11:46 2007 -0700 [TCP]: Uninline tcp_done(). The function is quite big and has several call sites and nothing to collapse by compiler optimization on inlining. Besides it's nicer to read in a in .c file. Signed-off-by: Andi Kleen Signed-off-by: David S. Miller commit 3ff50b7997fe06cd5d276b229967bb52d6b3b6c1 Author: Stephen Hemminger Date: Fri Apr 20 17:09:22 2007 -0700 [NET]: cleanup extra semicolons Spring cleaning time... There seems to be a lot of places in the network code that have extra bogus semicolons after conditionals. Most commonly is a bogus semicolon after: switch() { } Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit c462238d6a6d8ee855bda10f9fff442971540ed2 Author: Stephen Hemminger Date: Fri Apr 20 17:07:51 2007 -0700 [TCP]: TCP Illinois congestion control (rev3) This is an implementation of TCP Illinois invented by Shao Liu at University of Illinois. It is a another variant of Reno which adapts the alpha and beta parameters based on RTT. The basic idea is to increase window less rapidly as delay approaches the maximum. See the papers and talks to get a more complete description. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 9be9a6b983314dd57e2c5ba548dee8b53d338ac3 Author: Stephen Hemminger Date: Fri Apr 20 17:02:45 2007 -0700 [NET]: Get rid of netdev_nit It isn't any faster to test a boolean global variable than do a simple check for empty list. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 42dc9cd54b7290f862874a2544e50395e5719985 Author: Michal Ostrowski Date: Fri Apr 20 16:59:24 2007 -0700 [PPPOE]: Fix device tear-down notification. pppoe_flush_dev() kicks all sockets bound to a device that is going down. In doing so, locks must be taken in the right order consistently (sock lock, followed by the pppoe_hash_lock). However, the scan process is based on us holding the sock lock. So, when something is found in the scan we must release the lock we're holding and grab the sock lock. This patch fixes race conditions between this code and pppoe_release(), both of which perform similar functions but would naturally prefer to grab locks in opposing orders. Both code paths are now going after these locks in a consistent manner. pppoe_hash_lock protects the contents of the "pppox_sock" objects that reside inside the hash. Thus, NULL'ing out the pppoe_dev field should be done under the protection of this lock. Signed-off-by: Michal Ostrowski Signed-off-by: David S. Miller commit 202a03acf9994076055df40ae093a5c5474ad0bd Author: Florian Zumbiehl Date: Fri Apr 20 16:58:14 2007 -0700 [PPPOE]: memory leak when socket is release()d before PPPIOCGCHAN has been called on it below you find a patch that fixes a memory leak when a PPPoE socket is release()d after it has been connect()ed, but before the PPPIOCGCHAN ioctl ever has been called on it. This is somewhat of a security problem, too, since PPPoE sockets can be created by any user, so any user can easily allocate all the machine's RAM to non-swappable address space and thus DoS the system. Is there any specific reason for PPPoE sockets being available to any unprivileged process, BTW? After all, you need a packet socket for the discovery stage anyway, so it's unlikely that any unprivileged process will ever need to create a PPPoE socket, no? Allocating all session IDs for a known AC is a kind of DoS, too, after all - with Juniper ERXes, this is really easy, actually, since they don't ever assign session ids above 8000 ... Signed-off-by: Florian Zumbiehl Acked-by: Michal Ostrowski Signed-off-by: David S. Miller commit 74b885cf86def9bc836772e3c1788c00b72a35c9 Author: Florian Zumbiehl Date: Fri Apr 20 16:57:27 2007 -0700 [PPPOE]: race between interface going down and connect() below you find a patch that (hopefully) fixes a race between an interface going down and a connect() to a peer on that interface. Before, connect() would determine that an interface is up, then the interface could go down and all entries referring to that interface in the item_hash_table would be marked as ZOMBIEs and their references to the device would be freed, and after that, connect() would put a new entry into the hash table referring to the device that meanwhile is down already - which also would cause unregister_netdevice() to wait until the socket has been release()d. This patch does not suffice if we are not allowed to accept connect()s referring to a device that we already acked a NETDEV_GOING_DOWN for (that is: all references are only guaranteed to be freed after NETDEV_DOWN has been acknowledged, not necessarily after the NETDEV_GOING_DOWN already). And if we are allowed to, we could avoid looking through the hash table upon NETDEV_GOING_DOWN completely and only do that once we get the NETDEV_DOWN ... mostrows: pppoe_flush_dev is called on NETDEV_GOING_DOWN and NETDEV_DOWN to deal with this "late connect" issue. Ideally one would hope to notify users at the "NETDEV_GOING_DOWN" phase (just to pretend to be nice). However, it is the NETDEV_DOWN scan that takes all the responsibility for ensuring nobody is hanging around at that time. Signed-off-by: Florian Zumbiehl Acked-by: Michal Ostrowski Signed-off-by: David S. Miller commit bfafb26e11849fe99e03cc1902a91f7f65354e47 Author: Florian Zumbiehl Date: Fri Apr 20 16:56:31 2007 -0700 [PPPoE]: miscellaneous smaller cleanups below is a patch that just removes dead code/initializers without any effect (first access is an assignment) that I stumbled accross while reading the source. Signed-off-by: Florian Zumbiehl Acked-by: Michal Ostrowski Signed-off-by: David S. Miller commit 0c6fcc8a8cfcc737d05b6be8b2c3e931ef99cfc2 Author: Stephen Hemminger Date: Fri Apr 20 16:40:01 2007 -0700 [NET] skbuff: skb_store_bits const is backwards Getting warnings becuase skb_store_bits has skb as constant, but the function overwrites it. Looks like const was on the wrong side. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 3e6cf558b0098a15d8c360c4eaad3e4d719a555a Author: Stephen Hemminger Date: Fri Apr 20 16:39:17 2007 -0700 [BRIDGE]: Fix warning in net-2.6.22 The following is leftover from earlier change in net-2.6.22. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 75606dc69adcfff433bca0ff747538d8495da0ab Author: Ralf Baechle Date: Fri Apr 20 16:06:45 2007 -0700 [AX25/NETROM/ROSE]: Convert to use modern wait queue API Signed-off-by: Ralf Baechle Signed-off-by: David S. Miller commit 80feaacb8a6400a9540a961b6743c69a5896b937 Author: Peter P. Waskiewicz Jr Date: Fri Apr 20 16:05:39 2007 -0700 [AF_PACKET]: Add option to return orig_dev to userspace. Add a packet socket option to allow the orig_dev index to be returned to userspace when passing traffic through a decapsulated device, such as the bonding driver. This is very useful for layer 2 traffic being able to report which physical device actually received the traffic, instead of having the encapsulating device hide that information. The new option is called PACKET_ORIGDEV. Signed-off-by: Peter P. Waskiewicz Jr. Signed-off-by: David S. Miller commit 1370b5a59b941ac3873b5e8614d496e9f481d670 Author: YOSHIFUJI Hideaki Date: Fri Apr 20 15:57:45 2007 -0700 [IPV6] SNMP: Export statistics via netlink without CONFIG_PROC_FS. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 334901700f9f58993ebd7f6136d3f9062460d34d Author: YOSHIFUJI Hideaki Date: Fri Apr 20 15:57:15 2007 -0700 [IPV4] SNMP: Move some statistic bits to net/ipv4/proc.c. This also fixes memory leak in error path. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 49ed67a9eee3c756263feed4474e4fcf5c8eaed2 Author: YOSHIFUJI Hideaki Date: Fri Apr 20 15:56:48 2007 -0700 [IPV6] SNMP: Move some statistic bits to net/ipv6/proc.c. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit bf99f1bde3b3009af74874f3465f6861431fbb66 Author: YOSHIFUJI Hideaki Date: Fri Apr 20 15:56:20 2007 -0700 [IPV6] SNMP: Netlink interface. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 628a5c561890a9a9a74dea017873530584aab06e Author: John Heffner Date: Fri Apr 20 15:53:27 2007 -0700 [INET]: Add IP(V6)_PMTUDISC_RPOBE Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath. Signed-off-by: John Heffner Signed-off-by: David S. Miller commit b881ef7603230550aa0150b22af94089f07ab00d Author: John Heffner Date: Fri Apr 20 15:52:39 2007 -0700 [IPV6]: MTU discovery check in ip6_fragment() Adds a check in ip6_fragment() mirroring ip_fragment() for packets that we can't fragment, and sends an ICMP Packet Too Big message in response. Signed-off-by: John Heffner Signed-off-by: David S. Miller commit fd44de7cc1d430caef91ad9aecec9ff000fe86f8 Author: Patrick McHardy Date: Mon Apr 16 17:07:08 2007 -0700 [NET_SCHED]: ingress: switch back to using ingress_lock Switch ingress queueing back to use ingress_lock. qdisc_lock_tree now locks both the ingress and egress qdiscs on the device. All changes to data that might be used on both ingress and egress needs to be protected by using qdisc_lock_tree instead of manually taking dev->queue_lock. Additionally the qdisc stats_lock needs to be initialized to ingress_lock for ingress qdiscs. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 0463d4ae25771aaf3379bb6b2392f6edf23c2828 Author: Patrick McHardy Date: Mon Apr 16 17:02:10 2007 -0700 [NET_SCHED]: Eliminate qdisc_tree_lock Since we're now holding the rtnl during the entire dump operation, we can remove qdisc_tree_lock, whose only purpose is to protect dump callbacks from concurrent changes to the qdisc tree. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit ffa4d7216e848fbfdcb8e6f0bb66abeaa1888964 Author: Patrick McHardy Date: Wed Apr 25 14:01:17 2007 -0700 [NETLINK]: don't reinitialize callback mutex Don't reinitialize the callback mutex the netlink_kernel_create caller handed in, it is supposed to already be initialized and could already be held by someone. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 6313c1e0992feaee56bc09b85042b3186041fa3c Author: Patrick McHardy Date: Mon Apr 16 17:00:53 2007 -0700 [RTNETLINK]: Remove unnecessary locking in dump callbacks Since we're now holding the rtnl during the entire dump operation, we can remove additional locking for rtnl protected data. This patch does that for all simple cases (dev_base_lock for dev_base walking, RCU protection for FIB rule dumping). Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 1c2d670f3660e9103fdcdca702f6dbf8ea7d6afb Author: Patrick McHardy Date: Mon Apr 16 16:59:10 2007 -0700 [RTNETLINK]: Hold rtnl_mutex during netlink dump callbacks Hold rtnl_mutex during the entire netlink dump operation. This allows to simplify locking in the dump callbacks, since they can now rely on that no concurrent changes happen. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit af65bdfce98d7965fbe93a48b8128444a2eea024 Author: Patrick McHardy Date: Fri Apr 20 14:14:21 2007 -0700 [NETLINK]: Switch cb_lock spinlock to mutex and allow to override it Switch cb_lock to mutex and allow netlink kernel users to override it with a subsystem specific mutex for consistent locking in dump callbacks. All netlink_dump_start users have been audited not to rely on any side-effects of the previously used spinlock. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit b076deb8498e26c9aa2f44046fe5e9936ae2fb5a Author: Patrick McHardy Date: Thu Apr 12 22:17:05 2007 -0700 [NETFILTER]: ipt_ULOG: add compat conversion functions Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit d3a2c3ca8e7d908824701db978b936d115aea506 Author: Patrick McHardy Date: Thu Apr 12 22:16:38 2007 -0700 [NETFILTER]: nfnetlink_log: remove fallback to group 0 Don't fallback to group 0 if no instance can be found for the given group. This potentially confuses the listener and is not what the user configured. Also remove the ring buffer spamming that happens when rules are set up before the logging daemon is started. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 3b5018d6766186474366f26cc87fba81407b9089 Author: Patrick McHardy Date: Thu Apr 12 22:16:18 2007 -0700 [NETFILTER]: {eb,ip6,ip}t_LOG: remove remains of LOG target overloading All LOG targets always use their internal logging function nowadays, so remove the incorrect error message and handle real errors (!= -EEXIST) by failing to load. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit fe6092ea0019cbba5263a915c9ce9f2bf383209e Author: Patrick McHardy Date: Thu Apr 12 22:15:50 2007 -0700 [NETFILTER]: nf_nat: use HW checksumming when possible When mangling packets forwarded to a HW checksumming capable device, offload recalculation of the checksum instead of doing it in software. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit c15bf6e699f4c366f2d1e19ac5d7add21c6b5a19 Author: Bart De Schuymer Date: Thu Apr 12 22:15:06 2007 -0700 [NETFILTER]: ebt_arp: add gratuitous arp filtering The attached patch adds gratuitous arp filtering, more precisely: it allows checking that the IPv4 source address matches the IPv4 destination address inside the ARP header. It also adds a check for the hardware address type when matching MAC addresses (nothing critical, just for better consistency). Signed-off-by: Bart De Schuymer Acked-by: Carl-Daniel Hailfinger Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 516299d2f5b6f9703b9b388faf91898dc636a678 Author: Michael Milner Date: Thu Apr 12 22:14:23 2007 -0700 [NETFILTER]: bridge-nf: filter bridged IPv4/IPv6 encapsulated in pppoe traffic The attached patch by Michael Milner adds support for using iptables and ip6tables on bridged traffic encapsulated in ppoe frames, similar to what's already supported for vlan. Signed-off-by: Michael Milner Signed-off-by: Bart De Schuymer Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 91d73c15cb165195bc8c3d6a35e30df454b1485b Author: Gerrit Renker Date: Fri Apr 20 13:57:21 2007 -0700 [DCCP]: Complete documentation of dccp_sock This fills in missing documentation for dccp_sock fields. Signed-off-by: Gerrit Renker Signed-off-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit f73f7097c986aab159491dcded7fc918e76e9ec3 Author: Gerrit Renker Date: Fri Apr 20 13:56:47 2007 -0700 [DCCP]: Debug statements for Elapsed Time option This prints the value of the parsed Elapsed Time when received via a Timestamp Echo option [RFC 4342, 13.3]. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit b2449fdc30ccac550344df5e60d38bb8427b109c Author: Gerrit Renker Date: Fri Apr 20 13:02:55 2007 -0700 [DCCP]: Fix bug in the calculation of very low sending rates This fixes an error in the calculation of t_ipi when X converges towards very low sending rates (between 1 and 64 bytes per second). Although this case may not sound likely, it can be reproduced by connecting, hitting enter (1 byte sent) and waiting for some time, during which the nofeedback timer halves the sending rate until finally it reaches the region 1..64 bytes/sec. Computing X is handled correctly (tested separately); but by dividing X _before_ entering the calculation of t_ipi, X becomes zero as a result. This in turn triggers a BUG condition caught in scaled_div(). Fixed by replacing with equivalent statement and explicit typecast for good measure. Calculation verified and effect of patch tested - reduced never below 1 byte per 64 seconds afterwards, i.e. not allowing divide-by-zero. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit cb8c181f288a1157bc717cc7a02412d4d1dc19bc Author: David S. Miller Date: Tue Apr 10 22:10:39 2007 -0700 [S390]: Fix build on 31-bit. Allow s390 to properly override the generic __div64_32() implementation by: 1) Using obj-y for div64.o in s390's makefile instead of lib-y 2) Adding the weak attribute to the generic implementation. Signed-off-by: David S. Miller commit efd1e8d569b3d35a3a636683c2a9ebffec9c1fcf Author: Patrick McHardy Date: Tue Apr 10 18:30:09 2007 -0700 [SK_BUFF]: Fix missing offset adjustment in skb_copy_expand skb_copy_expand changes the headroom, so it needs to adjust the header offsets by the difference between the old and the new value. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 33036807b32d5ed1f4fdfe2d5e6bab2bb260b9f7 Author: Eric Dumazet Date: Tue Apr 10 13:25:40 2007 -0700 [NET]: loopback driver can use loopback_dev integrated net_device_stats Rusty added a new 'stats' field to struct net_device. loopback driver can use it instead of declaring another struct net_device_stats This saves some memory. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit 87a596e0b8bc344bd6bfebe83b56d11fb79ee23a Author: Akinobu Mita Date: Sat Apr 7 18:57:07 2007 +0900 bridge: check kmem_cache_create() error This patch checks kmem_cache_create() error and aborts loading module on failure. Signed-off-by: Akinobu Mita Signed-off-by: Stephen Hemminger commit ffe1d49cc300f3dff990093aa952a2fbb371c1b6 Author: Stephen Hemminger Date: Mon Apr 9 11:49:58 2007 -0700 bridge: allow changing hardware address to any valid address For case of bridging pseudo devices, the get created/destroyed (Xen) need to allow setting address to any valid value. Signed-off-by: Stephen Hemminger commit b86c45035c439cfa6ef5b2e4bf080b24bd8765f1 Author: Stephen Hemminger Date: Thu Mar 22 14:08:46 2007 -0700 bridge: change when netlink events go to STP Need to tell STP daemon about more events, like any time a device is added even when it is down. Signed-off-by: Stephen Hemminger commit 9cde070874b822d4677f4f01fe146991785813b1 Author: Stephen Hemminger Date: Wed Mar 21 14:22:44 2007 -0700 bridge: add support for user mode STP This patchset based on work by Aji_Srinivas@emc.com provides allows spanning tree to be controled from userspace. Like hotplug, it uses call_usermodehelper when spanning tree is enabled so there is no visible API change. If call to start usermode STP fails it falls back to existing kernel STP. Signed-off-by: Stephen Hemminger commit 9cf637473c8535b5abe27fee79254c2d552e042a Author: Stephen Hemminger Date: Mon Apr 9 12:57:54 2007 -0700 bridge: add sysfs hook to flush forwarding table The RSTP daemon needs to be able to flush all dynamic forwarding entries in the case of topology change. This is a temporary interface. It will change to a netlink interface before RSTP daemon is officially released. Signed-off-by: Stephen Hemminger commit 3f890923182aeebc572f3818dd51c9014827e0ec Author: Stephen Hemminger Date: Wed Mar 21 13:42:33 2007 -0700 bridge: simpler hash with salt Instead of hashing the whole Ethernet address, it should be faster to just use the last 4 bytes. Add a random salt value to the hash to make it more difficult to construct worst case DoS hash chains. Signed-off-by: Stephen Hemminger commit 467aea0ddfd1f0f1158c57cbef0e8941dd63374c Author: Stephen Hemminger Date: Wed Mar 21 13:42:06 2007 -0700 bridge: don't route packets while learning While in the STP learning state, don't route packets; wait until forwarding delay has expired. The purpose of the forwarding delay is to detect loops in the network, and if a brouter started up and started forwarding, it could cause a flood. Signed-off-by: Stephen Hemminger commit 6229e362dd49b9e8387126bd4483ab0574d23e9c Author: Stephen Hemminger Date: Wed Mar 21 13:38:47 2007 -0700 bridge: eliminate call by reference Change the bridging hook to be simple function with return value rather than modifying the skb argument. This could generate better code and is cleaner. Signed-off-by: Stephen Hemminger commit 604763722c655c7e3f31ecf6f7b4dafcd26a7a15 Author: Herbert Xu Date: Mon Apr 9 11:59:39 2007 -0700 [NET]: Treat CHECKSUM_PARTIAL as CHECKSUM_UNNECESSARY When a transmitted packet is looped back directly, CHECKSUM_PARTIAL maps to the semantics of CHECKSUM_UNNECESSARY. Therefore we should treat it as such in the stack. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller commit 628592ccdbbb5bb751217cf02e2e7abb500d7ffe Author: Herbert Xu Date: Mon Apr 23 17:06:40 2007 -0700 [NETDRV]: Perform missing csum_offset conversions When csum_offset was introduced we did a conversion from csum to csum_offset where applicable. A couple of drivers were missed in this process. It was harmless to begin with since the two fields coincided. Now that we've made them different with the addition of csum_start, the missed drivers must be converted or they can't send packets out at all that require checksum offload. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller commit 663ead3bb8d5b561e70fc3bb3861c9220b5a77eb Author: Herbert Xu Date: Mon Apr 9 11:59:07 2007 -0700 [NET]: Use csum_start offset instead of skb_transport_header The skb transport pointer is currently used to specify the start of the checksum region for transmit checksum offload. Unfortunately, the same pointer is also used during receive side processing. This creates a problem when we want to retransmit a received packet with partial checksums since the skb transport pointer would be overwritten. This patch solves this problem by creating a new 16-bit csum_start offset value to replace the skb transport header for the purpose of checksums. This offset is calculated from skb->head so that it does not have to change when skb->data changes. No extra space is required since csum_offset itself fits within a 16-bit word so we can use the other 16 bits for csum_start. For backwards compatibility, just before we push a packet with partial checksums off into the device driver, we set the skb transport header to what it would have been under the old scheme. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller commit ac758e3c55c529714354fc268892ca4d23ca1e99 Author: Patrick McHardy Date: Mon Apr 9 11:47:58 2007 -0700 [XFRM]: beet: fix worst case header_len calculation esp_init_state doesn't account for the beet pseudo header in the header_len calculation, which may result in undersized skbs hitting xfrm4_beet_output, causing unnecessary reallocations in ip_finish_output2. The skbs should still always have enough room to avoid causing skb_under_panic in skb_push since we have at least 16 bytes available from LL_RESERVED_SPACE in xfrm_state_check_space. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit c5c2523893747f88a83376abad310c8ad13f7197 Author: Patrick McHardy Date: Mon Apr 9 11:47:18 2007 -0700 [XFRM]: Optimize MTU calculation Replace the probing based MTU estimation, which usually takes 2-3 iterations to find a fitting value and may underestimate the MTU, by an exact calculation. Also fix underestimation of the XFRM trailer_len, which causes unnecessary reallocations. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 557922584d9c5b6b990bcfb2fec3134f0e73a05d Author: Patrick McHardy Date: Mon Apr 9 11:46:17 2007 -0700 [XFRM]: esp: fix skb_tail_pointer conversion bug Fix incorrect switch of "trailer" skb by "skb" during skb_tail_pointer conversion: - *(u8*)(trailer->tail - 1) = top_iph->protocol; + *(skb_tail_pointer(skb) - 1) = top_iph->protocol; - *(u8 *)(trailer->tail - 1) = *skb_network_header(skb); + *(skb_tail_pointer(skb) - 1) = *skb_network_header(skb); Signed-off-by: Patrick McHardy Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 56eb88828b78f6f3b11a2996350092a40745301f Author: Patrick McHardy Date: Mon Apr 9 11:45:04 2007 -0700 [SK_BUFF]: Fix missing offset adjustment in pskb_expand_head Since we're increasing the headroom, the header offsets need to be increased by the same amount as well. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 29f6af7712c40045e7886d0fa356d97a6f9aba49 Author: YOSHIFUJI Hideaki Date: Fri Apr 6 11:45:39 2007 -0700 [IPV6] FIB6RULE: Find source address during looking up route. When looking up route for destination with rules with source address restrictions, we may need to find a source address for the traffic if not given. Based on patch from Noriaki TAKAMIYA . Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit ea2f10a3c81724701fe6a754789eafd50b33909f Author: Patrick McHardy Date: Thu Apr 5 16:04:04 2007 -0700 [XFRM]: beet: minor cleanups Remove unnecessary initialization/variable. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 038890fed8d1fa95bbbdeb517f5710eb75fa9e2e Author: Thomas Graf Date: Thu Apr 5 14:35:52 2007 -0700 [RTNL]: Improve error codes for unsupported operations The most common trigger of these errors is that the config option hasn't been enable wich would make the functionality available. Therefore returning EOPNOTSUPP gives a better idea on what is going wrong. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 716ea3a7aae3a2bfc44cb97b5419c1c9868c7bc9 Author: David Howells Date: Mon Apr 2 20:19:53 2007 -0700 [NET]: Move generic skbuff stuff from XFRM code to generic code Move generic skbuff stuff from XFRM code to generic code so that AF_RXRPC can use it too. The kdoc comments I've attached to the functions needs to be checked by whoever wrote them as I had to make some guesses about the workings of these functions. Signed-off-By: David Howells Signed-off-by: David S. Miller commit 926554c4b74e53d5da4cefdc3bbd7e92427fb1a9 Author: Arnaldo Carvalho de Melo Date: Sat Mar 31 12:05:49 2007 -0300 [CREDITS]: Update Arnaldo entry Signed-off-by: Arnaldo Carvalho de Melo commit 1a4e2d093fd5f3eaf8cffc04a1b803f8b0ddef6d Author: Arnaldo Carvalho de Melo Date: Sat Mar 31 11:55:45 2007 -0300 [SK_BUFF]: Some more conversions to skb_copy_from_linear_data Signed-off-by: Arnaldo Carvalho de Melo commit 27d7ff46a3498d3debc6ba68fb8014c702b81170 Author: Arnaldo Carvalho de Melo Date: Sat Mar 31 11:55:19 2007 -0300 [SK_BUFF]: Introduce skb_copy_to_linear_data{_offset} To clearly state the intent of copying to linear sk_buffs, _offset being a overly long variant but interesting for the sake of saving some bytes. Signed-off-by: Arnaldo Carvalho de Melo commit 3dbad80ac7632f243b824d469301abb97ec634a1 Author: David S. Miller Date: Thu Mar 29 19:16:03 2007 -0700 [NET]: Fix warnings in 3c523.c and ni52.c We have to put back the cast to "char *" because these pointers are volatile. Reported by Andrew Morton. Signed-off-by: David S. Miller commit c45d286e72dd72c0229dc9e2849743ba427fee84 Author: Rusty Russell Date: Wed Mar 28 14:29:08 2007 -0700 [NET]: Inline net_device_stats Network drivers which keep stats allocate their own stats structure then write a get_stats() function to return them. It would be nice if this were done by default. 1) Add a new "stats" field to "struct net_device". 2) Add a new feature field to say "this driver uses the internal one" 3) Have a default "get_stats" which returns NULL if that feature not set. 4) Change callers to check result of get_stats call for NULL, not if ->get_stats is set. This should not break backwards compatibility with older drivers, yet allow modern drivers to shed some boilerplate code. Lightly tested: works for a modified lguest network driver. Signed-off-by: Rusty Russell Signed-off-by: David S. Miller commit f85958151900f9d30fa5ff941b0ce71eaa45a7de Author: Eric Dumazet Date: Wed Mar 28 14:22:33 2007 -0700 [NET]: random functions can use nsec resolution instead of usec In order to get more randomness for secure_tcpv6_sequence_number(), secure_tcp_sequence_number(), secure_dccp_sequence_number() functions, we can use the high resolution time services, providing nanosec resolution. I've also done two kmalloc()/kzalloc() conversions. Signed-off-by: Eric Dumazet Acked-by: James Morris Signed-off-by: David S. Miller commit 4b19ca44cbafabfe0b7b98e2e24b21a96198f509 Author: Thomas Graf Date: Wed Mar 28 14:18:52 2007 -0700 [NET] fib_rules: delay route cache flush by ip_rt_min_delay Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit d626f62b11e00c16e81e4308ab93d3f13551812a Author: Arnaldo Carvalho de Melo Date: Tue Mar 27 18:55:52 2007 -0300 [SK_BUFF]: Introduce skb_copy_from_linear_data{_offset} To clearly state the intent of copying from linear sk_buffs, _offset being a overly long variant but interesting for the sake of saving some bytes. Signed-off-by: Arnaldo Carvalho de Melo commit 2a123b86e2b242a4a6db990d2851d45e192f88e5 Author: Arnaldo Carvalho de Melo Date: Tue Mar 27 18:38:07 2007 -0300 [BLUETOOTH]: Introduce skb->data accessor methods for hci_{acl,event,sco}_hdr For consistency with other skb data accessors, reducing the number of direct accesses to skb->data. Signed-off-by: Arnaldo Carvalho de Melo commit 03d4f879b9ddf7d5c1f788792247e62450342eed Author: Eric Dumazet Date: Tue Mar 27 14:18:34 2007 -0700 [IPV4]: align inet_protos[] on SMP As IPPROTO_TCP is 6, it makes sense to make sure inet_protos[] array is properly cache line aligned to avoid false sharing on SMP. c0680540 b peer_total c0680544 b inet_peer_unused_head c0680560 B inet_protos On i386 this example, we can see that inet_protos[IPPROTO_TCP] shares a potentially hot (and modified) cache line. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit 4103f8cd5c1f260d674a7b426ed221812de54d47 Author: Eric Dumazet Date: Tue Mar 27 13:58:31 2007 -0700 [TCP]: tcp_memory_pressure and tcp_socket are__read_mostly candidates tcp_memory_pressure and tcp_socket currently share a cache line with tcp_memory_allocated, tcp_sockets_allocated. (Very hot cache line) It makes sense to declare these variables as __read_mostly, to avoid false sharing on SMP. ffffffff8081d9c0 B tcp_orphan_count ffffffff8081d9c4 B tcp_memory_allocated ffffffff8081d9c8 B tcp_sockets_allocated ffffffff8081d9cc B tcp_memory_pressure ffffffff8081d9d0 b tcp_md5sig_users ffffffff8081d9d8 b tcp_md5sig_pool ffffffff8081d9e0 b warntime.31570 ffffffff8081d9e8 b tcp_socket Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit 73417f617a93cf30342c3ea41abc38927bd467aa Author: Thomas Graf Date: Tue Mar 27 13:56:52 2007 -0700 [NET] fib_rules: Flush route cache after rule modifications The results of FIB rules lookups are cached in the routing cache except for IPv6 as no such cache exists. So far, it was the responsibility of the user to flush the cache after modifying any rules. This lead to many false bug reports due to misunderstanding of this concept. This patch automatically flushes the route cache after inserting or deleting a rule. Thanks to Muli Ben-Yehuda for catching a bug in the previous patch. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit be776281aee54626a474ba06f91926b98bdd180d Author: Eric Dumazet Date: Tue Mar 27 13:53:04 2007 -0700 [NET]: inet_ehash_secret should be __read_mostly and set only once There is a very tiny probability that build_ehash_secret() is called at the same time by different CPUS. Also, using __read_mostly is a must for inet_ehash_secret Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit 35fc92a9deee0da6e35fdc3150bb134e58f2fd63 Author: Herbert Xu Date: Mon Mar 26 23:22:20 2007 -0700 [NET]: Allow forwarding of ip_summed except CHECKSUM_COMPLETE Right now Xen has a horrible hack that lets it forward packets with partial checksums. One of the reasons that CHECKSUM_PARTIAL and CHECKSUM_COMPLETE were added is so that we can get rid of this hack (where it creates two extra bits in the skbuff to essentially mirror ip_summed without being destroyed by the forwarding code). I had forgotten that I've already gone through all the deivce drivers last time around to make sure that they're looking at ip_summed == CHECKSUM_PARTIAL rather than ip_summed != 0 on transmit. In any case, I've now done that again so it should definitely be safe. Unfortunately nobody has yet added any code to update CHECKSUM_COMPLETE values on forward so we I'm setting that to CHECKSUM_NONE. This should be safe to remove for bridging but I'd like to check that code path first. So here is the patch that lets us get rid of the hack by preserving ip_summed (mostly) on forwarded packets. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller commit 2d771cd86d4c3af26f34a7bcdc1b87696824cad9 Author: Janusz Krzysztofik Date: Mon Mar 26 18:03:44 2007 -0700 [IPV4] LVS: Allow to send ICMP unreachable responses when real-servers are removed this is a small patch by Janusz Krzysztofik to ip_route_output_slow() that allows VIP-less LVS linux director to generate packets originating >From VIP if sysctl_ip_nonlocal_bind is set. In a nutshell, the intention is for an LVS linux director to be able to send ICMP unreachable responses to end-users when real-servers are removed. http://archive.linuxvirtualserver.org/html/lvs-users/2007-01/msg00106.html Signed-off-by: Simon Horman Signed-off-by: David S. Miller commit fa0b2d1d2196dd46527a8d028797e2bca5930a92 Author: Thomas Graf Date: Mon Mar 26 17:38:53 2007 -0700 [NET] fib_rules: Add no-operation action The use of nop rules simplifies the usage of goto rules and adds more flexibility as they allow targets to remain while the actual content of the branches can change easly. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 2b44368307cd06c5614d7b53801f516c0654020b Author: Thomas Graf Date: Mon Mar 26 17:37:59 2007 -0700 [NET] fib_rules: Mark rules detached from the device Rules which match against device names in their selector can remain while the device itself disappears, in fact the device doesn't have to present when the rule is added in the first place. The device name is resolved by trying when the rule is added and later by listening to NETDEV_REGISTER/UNREGISTER notifications. This patch adds the flag FIB_RULE_DEV_DETACHED which is set towards userspace when a rule contains a device match which is unresolved at the moment. This eases spotting the reason why certain rules seem not to function properly. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 0947c9fe56d9cf7ad0bc3a03ccd30446cde698e4 Author: Thomas Graf Date: Mon Mar 26 17:14:15 2007 -0700 [NET] fib_rules: goto rule action This patch adds a new rule action FR_ACT_GOTO which allows to skip a set of rules by jumping to another rule. The rule to jump to is specified via the FRA_GOTO attribute which carries a rule preference. Referring to a rule which doesn't exists is explicitely allowed. Such goto rules are marked with the flag FIB_RULE_UNRESOLVED and will act like a rule with a non-matching selector. The rule will become functional as soon as its target is present. The goto action enables performance optimizations by reducing the average number of rules that have to be passed per lookup. Example: 0: from all lookup local 40: not from all to 192.168.23.128 goto 32766 41: from all fwmark 0xa blackhole 42: from all fwmark 0xff blackhole 32766: from all lookup main Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 2f7826c02447480c7c1b5500b34fc783f1ed8145 Author: David S. Miller Date: Mon Mar 26 02:00:58 2007 -0700 [WAN] cosa.c: Build fix. Caused by skb_reset_mac_header() changes, missing semicolon. Signed-off-by: David S. Miller commit 85795d64eddd4546375f5ee37bedd88cb5bc4ece Author: Stephen Hemminger Date: Sat Mar 24 21:35:33 2007 -0700 [TCP] tcp_probe: improvements for net-2.6.22 Change tcp_probe to use ktime (needed to add one export). Add option to only get events when cwnd changes - from Doug Leith Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit e1c3e7ab6de9711d2e0e9daf369c6638582eb7e7 Author: Stephen Hemminger Date: Sat Mar 24 21:34:38 2007 -0700 [TCP]: cubic update for net-2.6.22 The following update received from Injong updates TCP cubic to the latest version. I am running more complete tests and will have results after 4/1. According to Injong: the new version improves on its scalability, fairness and stability. So in all properties, we confirmed it shows better performance. NCSU results (for 2.6.18 and 2.6.20) available: http://netsrv.csc.ncsu.edu/wiki/index.php/TCP_Testing This version is described in a new Internet draft for CUBIC. http://www.ietf.org/internet-drafts/draft-rhee-tcp-cubic-00.txt Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 9af3912ec9e30509b76cb376abb65a4d8af27df3 Author: John Heffner Date: Sun Mar 25 23:32:29 2007 -0700 [NET] Move DF check to ip_forward Do fragmentation check in ip_forward, similar to ipv6 forwarding. Signed-off-by: John Heffner Signed-off-by: David S. Miller commit b3da2cf37c5c6e47698957a25ab43a7223dbb90f Author: David S. Miller Date: Fri Mar 23 11:40:27 2007 -0700 [INET]: Use jhash + random secret for ehash. The days are gone when this was not an issue, there are folks out there with huge bot networks that can be used to attack the established hash tables on remote systems. So just like the routing cache and connection tracking hash, use Jenkins hash with random secret input. Signed-off-by: David S. Miller commit d30045a0bcf144753869175dd9d840f7ceaf4aba Author: Johannes Berg Date: Fri Mar 23 11:37:48 2007 -0700 [NETLINK]: introduce NLA_BINARY type This patch introduces a new NLA_BINARY attribute policy type with the verification of simply checking the maximum length of the payload. It also fixes a small typo in the example. Signed-off-by: Johannes Berg Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 703315712cfccfe0b45ef4aa6994527d8ee95e33 Author: Vlad Yasevich Date: Fri Mar 23 11:34:36 2007 -0700 [SCTP]: Implement SCTP_MAX_BURST socket option. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller commit a5a35e76753d27e782028843a5186f176b50dd16 Author: Vlad Yasevich Date: Fri Mar 23 11:34:08 2007 -0700 [SCTP]: Implement sac_info field in SCTP_ASSOC_CHANGE notification. As stated in the sctp socket api draft: sac_info: variable If the sac_state is SCTP_COMM_LOST and an ABORT chunk was received for this association, sac_info[] contains the complete ABORT chunk as defined in the SCTP specification RFC2960 [RFC2960] section 3.3.7. We now save received ABORT chunks into the sac_info field and pass that to the user. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller commit bdf3092af601ccad765974652ab103162fbe14f4 Author: Vlad Yasevich Date: Fri Mar 23 11:33:12 2007 -0700 [SCTP]: Honor flags when setting peer address parameters Parameters only take effect when a corresponding flag bit is set and a value is specified. This means we need to check the flags in addition to checking for non-zero value. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller commit 1ae4114dce35dd1d32ed847f60b599dbbdfd5829 Author: Vlad Yasevich Date: Fri Mar 23 11:32:26 2007 -0700 [SCTP]: Implement SCTP_ADDR_CONFIRMED state for ADDR_CHNAGE event Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller commit d49d91d79a8dc5e85108a5ae1c8eef23dec135c1 Author: Vlad Yasevich Date: Fri Mar 23 11:32:00 2007 -0700 [SCTP]: Implement SCTP_PARTIAL_DELIVERY_POINT option. This option induces partial delivery to run as soon as the specified amount of data has been accumulated on the association. However, we give preference to fully reassembled messages over PD messages. In any case, window and buffer is freed up. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller commit b6e1331f3ce25a56edb956054eaf8011654686cb Author: Vlad Yasevich Date: Fri Apr 20 12:23:15 2007 -0700 [SCTP]: Implement SCTP_FRAGMENT_INTERLEAVE socket option This option was introduced in draft-ietf-tsvwg-sctpsocket-13. It prevents head-of-line blocking in the case of one-to-many endpoint. Applications enabling this option really must enable SCTP_SNDRCV event so that they would know where the data belongs. Based on an earlier patch by Ivan Skytte Jørgensen. Additionally, this functionality now permits multiple associations on the same endpoint to enter Partial Delivery. Applications should be extra careful, when using this functionality, to track EOR indicators. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller commit c95e939508e64863a1c5c73a9e1a908784e06820 Author: Patrick McHardy Date: Fri Mar 23 11:30:04 2007 -0700 [NET_SCHED]: qdisc: remove unnecessary memory barriers We're holding dev->queue_lock in qdisc_watchdog_schedule and qdisc_watchdog_cancel, no need for the barriers. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit a48b5a61448899040dfbd2e0cd55b06a2bd2466c Author: Patrick McHardy Date: Fri Mar 23 11:29:43 2007 -0700 [NET_SCHED]: Unline tcf_destroy Uninline tcf_destroy and add a helper function to destroy an entire filter chain. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 3bebcda28077375470dd60545b71bba2f83335fd Author: Patrick McHardy Date: Fri Mar 23 11:29:25 2007 -0700 [NET_SCHED]: turn PSCHED_GET_TIME into inline function Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 03cc45c0a5b9b7f74768feb43b9a2525d203bbdb Author: Patrick McHardy Date: Fri Mar 23 11:29:11 2007 -0700 [NET_SCHED]: turn PSCHED_TDIFF_SAFE into inline function Also rename to psched_tdiff_bounded. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 8edc0c31d6b7849b0fb50db86824830769241939 Author: Patrick McHardy Date: Fri Mar 23 11:28:55 2007 -0700 [NET_SCHED]: kill PSCHED_TDIFF Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit a084980dcbf56c896e4b6c19aff2b082d5db7006 Author: Patrick McHardy Date: Fri Mar 23 11:28:30 2007 -0700 [NET_SCHED]: kill PSCHED_SET_PASTPERFECT/PSCHED_IS_PASTPERFECT Use direct assignment and comparison instead. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 104e0878984bb467e3f54d61105d8903babb4ec1 Author: Patrick McHardy Date: Fri Mar 23 11:28:07 2007 -0700 [NET_SCHED]: kill PSCHED_TLESS Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 7c59e25f3186f26e85b13a318dbc4482d1d363e9 Author: Patrick McHardy Date: Fri Mar 23 11:27:45 2007 -0700 [NET_SCHED]: kill PSCHED_TADD/PSCHED_TADD2 Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 26e252df1e6e5b68eb790e4a4baf745aa3870038 Author: Patrick McHardy Date: Fri Mar 23 11:27:29 2007 -0700 [NET_SCHED]: kill PSCHED_AUDIT_TDIFF Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 76d643cd3bd2b4a1e27e3eafee8e37be9c681792 Author: Patrick McHardy Date: Fri Mar 23 11:27:04 2007 -0700 [NET_SCHED]: sch_netem: fix off-by-one in send time comparison netem checks PSCHED_TLESS(cb->time_to_send, now) to find out whether it is allowed to send a packet, which is equivalent to cb->time_to_send < now. Use !PSCHED_TLESS(now, cb->time_to_send) instead to properly handle cb->time_to_send == now. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit c7bf5f9dc2f78ae8ebbfffc5f17becd0d9e6ba9e Author: Thomas Graf Date: Fri Mar 23 11:17:57 2007 -0700 [NETFILTER] nfnetlink: netlink_run_queue() already checks for NLM_F_REQUEST Patrick has made use of netlink_run_queue() in nfnetlink while my patches have been waiting for net-2.6.22 to open. So this check for NLM_F_REQUEST can go as well. Signed-off-by: Thomas Graf Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit de6e05c49f8b4ed63224c5d38891f531ecc4eabb Author: Yasuyuki Kozakai Date: Fri Mar 23 11:17:27 2007 -0700 [NETFILTER]: nf_conntrack: kill destroy() in struct nf_conntrack for diet The destructor per conntrack is unnecessary, then this replaces it with system wide destructor. Signed-off-by: Yasuyuki Kozakai Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 5f79e0f916a3bdeccc910fdf466bca582a9b2cca Author: Yasuyuki Kozakai Date: Fri Mar 23 11:17:07 2007 -0700 [NETFILTER]: nf_conntrack: don't use nfct in skb if conntrack is disabled Signed-off-by: Yasuyuki Kozakai Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit e6f689db51a789807edede411b32eb7c9e457948 Author: Patrick McHardy Date: Fri Mar 23 11:16:30 2007 -0700 [NETFILTER]: Use setup_timer Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 9afdb00c80b0b9c20435ce690b5287fa2434ef44 Author: Patrick McHardy Date: Fri Mar 23 11:12:50 2007 -0700 [NETFILTER]: nfnetlink_log: remove conditional locking This is gross, have the wrapper function take the lock. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 370e6a878962cad614eb8c7c5a22240e5cd316bb Author: Michal Miroslaw Date: Fri Mar 23 11:12:21 2007 -0700 [NETFILTER]: nfnetlink_log: micro-optimization: inst->skb != NULL in __nfulnl_send() No other function calls __nfulnl_send() with inst->skb == NULL than nfulnl_timer(). Signed-off-by: Michal Miroslaw Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit f76cdcee5ba4a3fb41de93d5f1c17fb6ab4d0820 Author: Michal Miroslaw Date: Fri Mar 23 11:12:03 2007 -0700 [NETFILTER]: nfnetlink_log: iterator functions need iter_state * only get_*() don't need access to seq_file - iter_state is enough for them. Signed-off-by: Michal Miroslaw Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 9a36e8c2b337c424ed77f5dea0a67dc8039d351b Author: Michal Miroslaw Date: Fri Mar 23 11:11:48 2007 -0700 [NETFILTER]: nfnetlink_log: micro-optimization: don't modify destroyed instance Simple micro-optimization: Don't change any options if the instance is being destroyed. Signed-off-by: Michal Miroslaw Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit f414c16c04b1c998e90370791f9a728e292146ea Author: Michal Miroslaw Date: Fri Mar 23 11:11:31 2007 -0700 [NETFILTER]: nfnetlink_log: micro-optimization for inst==NULL in nfulnl_recv_config() Simple micro-optimization: don't call instance_put() on known NULL pointers. Signed-off-by: Michal Miroslaw Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 55b5a91e1723280570957990a0c5ab8c3ec4090a Author: Michal Miroslaw Date: Fri Mar 23 11:11:05 2007 -0700 [NETFILTER]: nfnetlink_log: kill duplicate code Kill some duplicate code in nfulnl_log_packet(). Signed-off-by: Michal Miroslaw Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 09972d6f968d67dd82cbd403d5aa42c241a8d0cb Author: Michal Miroslaw Date: Fri Mar 23 11:10:47 2007 -0700 [NETFILTER]: nfnetlink_log: don't count max(a,b) twice We don't need local nlbufsiz (skb size) as nfulnl_alloc_skb() takes the maximum anyway. Signed-off-by: Michal Miroslaw Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 1b53d9042c04b8eb875d02e65792e9884efc3784 Author: Patrick McHardy Date: Fri Mar 23 11:10:13 2007 -0700 [NETFILTER]: Remove changelogs and CVS IDs Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit bb2f8cc0ecf025d6d3947e0389434650023f432e Author: Stephen Hemminger Date: Fri Mar 23 00:12:09 2007 -0700 [NETEM]: spelling errors Get rid of some of my creative spelling. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit c702e8047fe74648f7852a9c1de781b0d5a98402 Author: Thomas Graf Date: Thu Mar 22 23:30:55 2007 -0700 [NETLINK]: Directly return -EINTR from netlink_dump_start() Now that all users of netlink_dump_start() use netlink_run_queue() to process the receive queue, it is possible to return -EINTR from netlink_dump_start() directly, therefore simplying the callers. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit ead592ba246dfcc643b3f0f0c8c03f7bc898a59f Author: Thomas Graf Date: Thu Mar 22 23:30:35 2007 -0700 [IPv4] diag: Use netlink_run_queue() to process the receive queue Makes use of netlink_run_queue() to process the receive queue and converts inet_diag_rcv_msg() to use the type safe netlink interface. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 1d00a4eb42bdade33a6ec0961cada93577a66ae6 Author: Thomas Graf Date: Thu Mar 22 23:30:12 2007 -0700 [NETLINK]: Remove error pointer from netlink message handler The error pointer argument in netlink message handlers is used to signal the special case where processing has to be interrupted because a dump was started but no error happened. Instead it is simpler and more clear to return -EINTR and have netlink_run_queue() deal with getting the queue right. nfnetlink passed on this error pointer to its subsystem handlers but only uses it to signal the start of a netlink dump. Therefore it can be removed there as well. This patch also cleans up the error handling in the affected message handlers to be consistent since it had to be touched anyway. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 45e7ae7f716086994e4e747226881f901c67b031 Author: Thomas Graf Date: Thu Mar 22 23:29:10 2007 -0700 [NETLINK]: Ignore control messages directly in netlink_run_queue() Changes netlink_rcv_skb() to skip netlink controll messages and don't pass them on to the message handler. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit d35b685640aeb39eb4f5e98c75e8e001e406f9a3 Author: Thomas Graf Date: Thu Mar 22 23:28:46 2007 -0700 [NETLINK]: Ignore !NLM_F_REQUEST messages directly in netlink_run_queue() netlink_rcv_skb() is changed to skip messages which don't have the NLM_F_REQUEST bit to avoid every netlink family having to perform this check on their own. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 33a0543cd9e090d2c6759e0ed85c3049c6efcc06 Author: Thomas Graf Date: Thu Mar 22 23:27:39 2007 -0700 [NETLINK]: Remove unused groups variable Leftover from dynamic multicast groups allocation work. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 267281058c4cfd6a9a173aa957bffa58239f9656 Author: Thomas Graf Date: Thu Mar 22 23:27:19 2007 -0700 [TCP] westwood: Use type safe netlink interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit e9195d677d6f06730edd5c2a3fe3283564e39c51 Author: Thomas Graf Date: Thu Mar 22 23:27:01 2007 -0700 [TCP] vegas: Use type safe netlink interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 51057f2fecff1c520b083c5ac9229e7aebce9e01 Author: Thomas Graf Date: Thu Mar 22 21:41:06 2007 -0700 [RTNL]: Properly return rntl message handler Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 1936502d00ae6c2aa3931c42f6cf54afaba094f2 Author: Stephen Hemminger Date: Thu Mar 22 12:18:35 2007 -0700 [NET_SCHED] qdisc: avoid transmit softirq on watchdog wakeup If possible, avoid having to do a transmit softirq when a qdisc watchdog decides to re-enable. The watchdog routine runs off a timer, so it is already in the same effective context as the softirq. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 11274e5a43266d531140530adebead6903380caf Author: Stephen Hemminger Date: Thu Mar 22 12:17:42 2007 -0700 [NETEM]: avoid excessive requeues The netem code would call getnstimeofday() and dequeue/requeue after every packet, even if it was waiting. Avoid this overhead by using the throttled flag. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 075aa573b74a732aeff487ab77d3fbd627c10856 Author: Stephen Hemminger Date: Thu Mar 22 12:17:05 2007 -0700 [NETEM]: Optimize tfifo In most cases, the next packet will be sent after the last one. So optimize that case. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit b407621c35ed5f9a0734e57472e9539117963768 Author: Stephen Hemminger Date: Thu Mar 22 12:16:21 2007 -0700 [NETEM]: use better types for time values The random number generator always generates 32 bit values. The time values are limited by psched_tdiff_t Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit a362e0a7890c735a3ef63aab12d71ecfc6e6f4a5 Author: Stephen Hemminger Date: Thu Mar 22 12:15:45 2007 -0700 [NETEM]: report reorder percent correctly. If you setup netem to just delay packets; "tc qdisc ls" will report the reordering as 100%. Well it's a lie, reorder isn't used unless gap is set, so just set value to 0 so the output of utility is correct. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 7e58886b45bc4a309aeaa8178ef89ff767daaf7f Author: Stephen Hemminger Date: Thu Mar 22 12:10:58 2007 -0700 [TCP]: cubic optimization Use willy's work in optimizing cube root by having table for small values. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 22b9a0a3a49ab1a856e0853b3f3dd2abd156bd7c Author: Stephen Hemminger Date: Thu Mar 22 12:10:18 2007 -0700 [LIB]: div64_64 optimization Minor optimization of div64_64. do_div() already does optimization for the case of 32 by 32 divide, so no need to do it here. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit c454673da7c1d6533f40ec2f788023df9af56ebf Author: Thomas Graf Date: Sun Mar 25 23:24:24 2007 -0700 [NET] rules: Unified rules dumping Implements a unified, protocol independant rules dumping function which is capable of both, dumping a specific protocol family or all of them. This speeds up dumping as less lookups are required. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 687ad8cc640fd1f1619cc44a9ab274dabd48c758 Author: Thomas Graf Date: Thu Mar 22 11:59:42 2007 -0700 [RTNL]: Use rtnl registration interface for dump-all aliases Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 32fe21c0c0a3091552fea8f2f7e4905f547a3433 Author: Thomas Graf Date: Thu Mar 22 11:59:03 2007 -0700 [BRIDGE]: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit c127ea2c45d1b13a672fde254679721bb282e90a Author: Thomas Graf Date: Thu Mar 22 11:58:32 2007 -0700 [IPv6]: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit fa34ddd739cecf3999ec0b7562618e8321829d41 Author: Thomas Graf Date: Thu Mar 22 11:57:46 2007 -0700 [DECNet]: Use rtnl registration interface Signed-off-by: Thomas Graf Acked-by: Steven Whitehouse Signed-off-by: David S. Miller commit 708914cc5e1657eb1a1f9eefc6333dfd2df8c73a Author: Thomas Graf Date: Thu Mar 22 11:56:59 2007 -0700 [PKT_SCHED] act: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 82623c0d73bd111cad26e501e509966b2455b0e0 Author: Thomas Graf Date: Thu Mar 22 11:56:22 2007 -0700 [PKT_SCHED] cls: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit be577ddc2b4aca0849f701222f5bc13cf1b79c9a Author: Thomas Graf Date: Thu Mar 22 11:55:50 2007 -0700 [PKT_SCHED] qdisc: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 63f3444fb9a54c024d55f1205f8b94e7d2786595 Author: Thomas Graf Date: Thu Mar 22 11:55:17 2007 -0700 [IPv4]: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 9d9e6a5819230b5a5cc036f213135cb123ab1e50 Author: Thomas Graf Date: Sun Mar 25 23:20:05 2007 -0700 [NET] rules: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit c8822a4e00442e65d42d50db8e529d75c2025630 Author: Thomas Graf Date: Thu Mar 22 11:50:06 2007 -0700 [NEIGH]: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 340d17fc9d577c93678850e46963e9b19b92db7e Author: Thomas Graf Date: Thu Mar 22 11:49:22 2007 -0700 [NET] link: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit e284986385b6420a5f30f2dcd743512bbe1a3202 Author: Thomas Graf Date: Thu Mar 22 11:48:11 2007 -0700 [RTNL]: Message handler registration interface This patch adds a new interface to register rtnetlink message handlers replacing the exported rtnl_links[] array which required many message handlers to be exported unnecessarly. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller commit 30833ffead66e1f0052150a51db0b45151189ac1 Author: Gerrit Renker Date: Tue Mar 20 15:31:56 2007 -0300 [CCID3]: Use initial RTT sample from SYN exchange The patch follows the following recommendation made in an erratum to RFC 4342: "Senders MAY additionally make use of other available RTT measurements, including those from the initial Request-Response packet exchange." It implements larger initial windows with regard to this inital RTT measurement, using the mechanism suggested in draft-ietf-dccp-rfc3448bis, section 4.2. Signed-off-by: Gerrit Renker Signed-off-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 89560b53b92a07c529e13a462aa7fd87a844f1f5 Author: Gerrit Renker Date: Tue Mar 20 15:27:17 2007 -0300 [DCCP]: Sample RTT from SYN exchange Function: commit 7dfee1a9c07f80a82aa5fbad340146f2b5c794b4 Author: Gerrit Renker Date: Tue Mar 20 15:24:37 2007 -0300 [CCID3]: Use function for RTT sampling This replaces the existing occurrences of RTT sampling with the use of the new function dccp_sample_rtt. Signed-off-by: Gerrit Renker Signed-off-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 4712a792ee661921374c163eb6a4d06e33fd305f Author: Gerrit Renker Date: Tue Mar 20 15:23:18 2007 -0300 [DCCP]: Provide function for RTT sampling A recurring problem, in particular in the CCID code, is that RTT samples from packets with timestamp echo and elapsed time options need to be taken. This service is provided via a new function dccp_sample_rtt in this patch. Furthermore, to protect against `insane' RTT samples, the sampled value is bounded between 100 microseconds and 4 seconds - for which u32 is sufficient. Signed-off-by: Gerrit Renker Signed-off-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 0c150efb280986db7958cf2a559b91d826241e59 Author: Gerrit Renker Date: Tue Mar 20 15:19:07 2007 -0300 [CCID3]: Handle Idle and Application-Limited periods This updates the code with regard to handling idle and application-limited periods as specified in [RFC 4342, 5.1]. Background: commit a21f9f96cd035b0d9aec32d80ea0152672fbed42 Author: Gerrit Renker Date: Tue Mar 20 15:12:10 2007 -0300 [CCID3]: Wrap computation of RFC3390-initial rate into separate function The CCID 3 and TFRC specs (RFC 4342, RFC 3448, draft-3448bis) make frequent reference to the computation of the RFC-3390 initial sending rate: 1. Initial sending rate when RTT is known (RFC 4342, p. 6) 2. Response to Idle/Application-Limited periods (RFC 4342, 5.1) This warrants putting the code into its own function, for later code reuse. Signed-off-by: Gerrit Renker Signed-off-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 1761f7d7fea32c2290710f5c0afa0c3d93220593 Author: Gerrit Renker Date: Tue Mar 20 15:04:30 2007 -0300 [CCID3]: Remove build warnings for 64bit This clears the following sparc64 build warnings: 1) warning: format "%ld" expects type "long int", but argument 3 has type "suseconds_t" 2) warning: format "%llu" expects type "long long unsigned int", but argument 3 has type "__u64" Fixed by using typecast to unsigned. This is argued to be safe, since the quantities, after de-scaling (factor 2^6) fit all in u32. Signed-off-by: Gerrit Renker Signed-off-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit fddc2feb94c1f734dc27d44d166e97ab2e005ec1 Author: Gerrit Renker Date: Tue Mar 20 15:02:10 2007 -0300 [CCID3]: More to see in dccp_probe This adds a few more fields of interest to /proc/net/dccpprobe, the following output ensues: 1 2 3 4 5 6 7 8 9 10 11 sec.usec src:sport dst:dport size s rtt p X_calc X_recv X t_ipi Also made the formatting consistent. Scripts that go with this can be downloaded from http://139.133.210.30/users/gerrit/dccp/dccp_probe/ Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit f2645101350c6db66f0a1e72648909cc411f2b38 Author: Gerrit Renker Date: Tue Mar 20 15:01:14 2007 -0300 [CCID3]: Add documentation for socket options This updates the documentation on CCID3-specific options. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 6626e3628fe42837f733d103e194c6b4473d8669 Author: Gerrit Renker Date: Tue Mar 20 15:00:28 2007 -0300 [DCCP]: More debug information for dccp_wait_for_ccid This adds more detail in the wait_for_ccid packet scheduling loop. In particular, it informs about (i) when delay is used and (ii) why a packet is discarded. Signed-off-by: Gerrit Renker Signed-off-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit ac12b0c49571fe4c3a2f4957ed494da316d558be Author: Gerrit Renker Date: Tue Mar 20 14:59:23 2007 -0300 [DCCP]: Always use debug-toggle parameters Currently debugging output (when configured) is automatically enabled when DCCP modules are compiled into the kernel rather than built as loadable modules. This is not necessary, since the module parameters in this case become kernel commandline parameters, e.g. DCCP or CCID3 debug output can be enabled for a static build by appending the following at the boot prompt: dccp.dccp_debug=1 dccp_ccid3.ccid3_debug=1 This patch therefore does away with the more complicated way of always enabling debug output for static builds Signed-off-by: Gerrit Renker Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 1266adee12d25385a25e1c57b1e3ff05a90bb4d7 Author: Gerrit Renker Date: Tue Mar 20 14:56:11 2007 -0300 [CCID3]: Remove race condition and update t_ipi when `s' changes This: 1. removes a race condition in the access to the scheduled send time t_nom which results from allowing asynchronous r/w access to t_nom without locks; 2. updates the inter-packet interval t_ipi = s/X when `s' changes, following a suggestion by Ian McDonald. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 8699be7d240e37c91a84bdf32e79941d72bc7bd5 Author: Ian McDonald Date: Tue Mar 20 14:49:20 2007 -0300 [CCID3]: More verbose debugging This adds a few debugging statements to ccid3.c Signed-off-by: Ian McDonald Signed-off-by: Gerrit Renker Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 551dc5f7a11cfb66685bfd36cbbdb209c5a11d14 Author: Ian McDonald Date: Tue Mar 20 14:46:52 2007 -0300 [CCID3]: Fix use of invalid loss intervals This fixes a bug which uses an invalid comparison. The bug resulted in the use of invalid loss intervals. Signed-off-by: Ian McDonald Acked-by: Gerrit Renker Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 371fe7779cad6557a58df9a1b5543652e067400f Author: Gerrit Renker Date: Tue Mar 20 14:28:44 2007 -0300 [CCID3]: Use MSS for larger initial windows This improves the slow-start phase by using the MSS (as suggested in RFC 4342, sec. 5) instead of the packet size s. Also figured out that __u32 is ample resource enough. After applying, I got the following in the logs: ccid3_hc_tx_packet_recv: client(f7421700), s=6, MSS=1424, w_init=4380, R_sample=176us, X=24886363 Had the previous variant been used, w_init would have been as low as 24. Committer note: removed unneeded cast to unsigned long long that was causing a compiler warning on 64bit architectures. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 9bf17475eb658a920125bd8f05edf9c57c2dd950 Author: Gerrit Renker Date: Tue Mar 20 13:11:24 2007 -0300 [CCID3]: Re-order CCID 3 source file No code change at all. This splits ccid3.c into a RX and a TX section, so that the file has an organisation similar to the other ones (e.g. packet_history.{h,c}). Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 353b13e10a3f1a18c6b33858fb3337bcd2692eb5 Author: Gerrit Renker Date: Tue Mar 20 13:10:15 2007 -0300 [CCID3]: Remove redundant `len' test Since CCID3 avoids sending 0-byte data packets (cf. ccid3_hc_tx_send_packet), testing for zero-payload length, as performed by ccid3_hc_tx_update_s, is redundant - hence removed by this patch. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 8d13bf9a0bd4984756e234ce54299b92acefab99 Author: Gerrit Renker Date: Tue Mar 20 13:08:19 2007 -0300 [DCCP]: Remove ambiguity in the way before48 is used This removes two ambiguities in employing the new definition of before48, following the analysis on http://www.mail-archive.com/dccp@vger.kernel.org/msg01295.html (1) Updating GSR when P.seqno >= S.SWL With the old definition we did not update when P.seqno and S.SWL are 2^47 apart. To ensure the same behaviour as with the old definition, this is replaced with the equivalent condition dccp_delta_seqno(S.SWL, P.seqno) >= 0 (2) Sending SYNC when P.seqno >= S.OSR Here it is debatable whether the new definition causes an ambiguity: the case is similar to (1); and to have consistency with the case (1), we use the equivalent condition dccp_delta_seqno(S.OSR, P.seqno) >= 0 Detailed Justification commit b16be51b5e5d75cec71b18ebc75f15a4734c62ad Author: Gerrit Renker Date: Tue Mar 20 13:03:47 2007 -0300 [DCCP]: Fix for follows48 The follows48 relation identifies whether 48-bit sequence number x is the direct successor of y. Currently, it does not handle cases of the following type correctly: follows48(0x(prefix)10000LL, 0x(prefix)0FFFFLL) where prefix is an arbitrary hex sequence of up to 7 digits. This is fixed by reusing the new dccp_delta_seqno function. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit d52de17b8cf36d43a9d6977e7861a9f415541c6b Author: Gerrit Renker Date: Tue Mar 20 13:00:26 2007 -0300 [DCCP]: Make `before' relation unambiguous Problem: commit 0aec51c86986f61de26dd04913667af544a8b8eb Author: Gerrit Renker Date: Tue Mar 20 12:45:59 2007 -0300 [DCCP]: Make dccp_delta_seqno return signed numbers Problem: commit 6b811d43f6cc9eccdfc011a99f8571df2abc46d1 Author: Gerrit Renker Date: Tue Mar 20 12:26:51 2007 -0300 [DCCP]: 48-bit sequence number arithmetic This patch * organizes the sequence arithmetic functions into one corner of dccp.h * performs a small modification of dccp_set_seqno to make it more widely reusable (now it is safe to use any number, since it performs modulo-2^48 assignment) * adds functions and generic macros for 48-bit sequence arithmetic: --48 bit complement --modulo-48 addition and modulo-48 subtraction --dccp_inc_seqno now a special case of add48 Constants renamed following a suggestion by Arnaldo. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 8b5be26831b973d8013e8b4c9860d9694310cdc6 Author: Arnaldo Carvalho de Melo Date: Tue Mar 20 12:08:20 2007 -0300 [FORCEDETH]: Use skb_tailroom where appropriate Reducing the number of skb->data direct accesses. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit d004b8d4903180c111e114726982c194adf2a04f Author: Arnaldo Carvalho de Melo Date: Tue Mar 20 12:00:44 2007 -0300 [LMC]: lmc_main wants to use skb_tailroom At that point it is equivalent to what was being used, skb->end - skb->data, and the need is clearly the one skb_tailroom satisfies. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit f2adc9866742e7904f0268824edc53c948741415 Author: Arnaldo Carvalho de Melo Date: Tue Mar 20 11:52:34 2007 -0300 [ATM] idt77252: Fix double kfree_skb on failure in push_rx_skb Signed-off-by: Arnaldo Carvalho de Melo commit 6b88dd966b42e374dc783c397efc15f5c1458265 Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 22:29:03 2007 -0300 [SK_BUFF] ipv6: Use skb_network_offset in some more places So that we reduce the number of direct accesses to skb->data. Signed-off-by: Arnaldo Carvalho de Melo commit dc5fc579b90ed0a9a4e55b0218cdbaf0a8cf2e67 Author: Arnaldo Carvalho de Melo Date: Sun Mar 25 23:06:12 2007 -0700 [NETLINK]: Use nlmsg_trim() where appropriate Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit a36ca733375860b389c15ffdf6a5f92df64a33b6 Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 22:28:08 2007 -0300 [NETLINK]: Remove NLMSG_{NEW_ANSWER,CANCEL,END} Not used anywhere and defined inside __KERNEL__, Thomas acked this on irc. Signed-off-by: Arnaldo Carvalho de Melo commit 897933bcdf31c372e029dd4e2ecd573ebe6cfd9c Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 22:27:36 2007 -0300 [SK_BUFF]: Remove skb_add_mtu() leftovers Signed-off-by: Arnaldo Carvalho de Melo commit b529ccf2799c14346d1518e9bdf1f88f03643e99 Author: Arnaldo Carvalho de Melo Date: Wed Apr 25 19:08:35 2007 -0700 [NETLINK]: Introduce nlmsg_hdr() helper For the common "(struct nlmsghdr *)skb->data" sequence, so that we reduce the number of direct accesses to skb->data and for consistency with all the other cast skb member helpers. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 965ffea43d4ebe8cd7b9fee78d651268dd7d23c5 Author: Robert Olsson Date: Mon Mar 19 16:29:58 2007 -0700 [IPV4]: fib_trie root node settings The threshold for root node can be more aggressive set to get better tree compression. The new setting mekes the root grow from 16 to 19 bits and substansial improvemnt in Aver depth this with the current table of 214393 prefixes But really the dynamic resize should need more investigation both in terms convergence and performance and maybe it should be possible to change... Maybe just for the brave to start with or we may have to back this out. commit 05eee48c5af8213a71bd908ce17f577b2b776f79 Author: Robert Olsson Date: Mon Mar 19 16:27:37 2007 -0700 [IPV4]: fib_trie resize break The patch below adds break condition for the resize operations. If we don't achieve the desired fill factor a warning is printed. Trie should still be operational but new thresholds should be considered. Signed-off-by: Robert Olsson Signed-off-by: David S. Miller commit ca0605a7c8a42379c695308944b3ae82a85479f1 Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 10:48:59 2007 -0300 [SK_BUFF]: Adjust the zeroing up to tail in __alloc_skb too I did it just in alloc_skb_from_cache, forgot __alloc_skb, fixed now. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 4305b541357ddbd205aa145dc378926b7cb12283 Author: Arnaldo Carvalho de Melo Date: Thu Apr 19 20:43:29 2007 -0700 [SK_BUFF]: Convert skb->end to sk_buff_data_t Now to convert the last one, skb->data, that will allow many simplifications and removal of some of the offset helpers. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 27a884dc3cb63b93c2b3b643f5b31eed5f8a4d26 Author: Arnaldo Carvalho de Melo Date: Thu Apr 19 20:29:13 2007 -0700 [SK_BUFF]: Convert skb->tail to sk_buff_data_t So that it is also an offset from skb->head, reduces its size from 8 to 4 bytes on 64bit architectures, allowing us to combine the 4 bytes hole left by the layer headers conversion, reducing struct sk_buff size to 256 bytes, i.e. 4 64byte cachelines, and since the sk_buff slab cache is SLAB_HWCACHE_ALIGN... :-) Many calculations that previously required that skb->{transport,network, mac}_header be first converted to a pointer now can be done directly, being meaningful as offsets or pointers. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit be8bd86321fa7f06359d866ef61fb4d2f3e9dce9 Author: David S. Miller Date: Thu Apr 19 20:34:51 2007 -0700 [VLAN] vlan_dev: Use skb_reset_network_header(). Signed-off-by: David S. Miller commit afdf27c95629634ea40703197b6788e454d31609 Author: Peter Kovar Date: Fri Mar 16 20:39:25 2007 -0700 [IrDA]: SMC SuperIO Chip LPC47N227 not identified properly SMC SuperIO Chip LPC47N227 used for IrDA is not detected because its device identification byte can be 0x7A instead of 0x5A. Patch from Peter Kovar Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit c7630a4b932af254d61947a3a7e3831de92c7fb5 Author: Samuel Ortiz Date: Fri Mar 16 20:38:23 2007 -0700 [IrDA]: irda lockdep annotation Rmmoding irda triggers a lockdep false positive. Reported-by: Dave Jones Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit 5c81cd75fa63eaf2df0b8904508e53e953f316cf Author: Samuel Ortiz Date: Fri Mar 16 20:35:25 2007 -0700 [IrDA]: removing stir4200 useless include stir4200 doesn't need to include irlap.h Signed-off-by: Samuel Ortiz Signed-off-by: David S. Miller commit 2e07fa9cd3bac1e28cfe3131ed86b053afb02fc9 Author: Arnaldo Carvalho de Melo Date: Tue Apr 10 21:22:35 2007 -0700 [SK_BUFF]: Use offsets for skb->{mac,network,transport}_header on 64bit architectures With this we save 8 bytes per network packet, leaving a 4 bytes hole to be used in further shrinking work, likely with the offsetization of other pointers, such as ->{data,tail,end}, at the cost of adds, that were minimized by the usual practice of setting skb->{mac,nh,n}.raw to a local variable that is then accessed multiple times in each function, it also is not more expensive than before with regards to most of the handling of such headers, like setting one of these headers to another (transport to network, etc), or subtracting, adding to/from it, comparing them, etc. Now we have this layout for sk_buff on a x86_64 machine: [acme@mica net-2.6.22]$ pahole vmlinux sk_buff struct sk_buff { struct sk_buff * next; /* 0 8 */ struct sk_buff * prev; /* 8 8 */ struct rb_node rb; /* 16 24 */ struct sock * sk; /* 40 8 */ ktime_t tstamp; /* 48 8 */ struct net_device * dev; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ struct net_device * input_dev; /* 64 8 */ sk_buff_data_t transport_header; /* 72 4 */ sk_buff_data_t network_header; /* 76 4 */ sk_buff_data_t mac_header; /* 80 4 */ /* XXX 4 bytes hole, try to pack */ struct dst_entry * dst; /* 88 8 */ struct sec_path * sp; /* 96 8 */ char cb[48]; /* 104 48 */ /* cacheline 2 boundary (128 bytes) was 24 bytes ago*/ unsigned int len; /* 152 4 */ unsigned int data_len; /* 156 4 */ unsigned int mac_len; /* 160 4 */ union { __wsum csum; /* 4 */ __u32 csum_offset; /* 4 */ }; /* 164 4 */ __u32 priority; /* 168 4 */ __u8 local_df:1; /* 172 1 */ __u8 cloned:1; /* 172 1 */ __u8 ip_summed:2; /* 172 1 */ __u8 nohdr:1; /* 172 1 */ __u8 nfctinfo:3; /* 172 1 */ __u8 pkt_type:3; /* 173 1 */ __u8 fclone:2; /* 173 1 */ __u8 ipvs_property:1; /* 173 1 */ /* XXX 2 bits hole, try to pack */ __be16 protocol; /* 174 2 */ void (*destructor)(struct sk_buff *); /* 176 8 */ struct nf_conntrack * nfct; /* 184 8 */ /* --- cacheline 3 boundary (192 bytes) --- */ struct sk_buff * nfct_reasm; /* 192 8 */ struct nf_bridge_info *nf_bridge; /* 200 8 */ __u16 tc_index; /* 208 2 */ __u16 tc_verd; /* 210 2 */ dma_cookie_t dma_cookie; /* 212 4 */ __u32 secmark; /* 216 4 */ __u32 mark; /* 220 4 */ unsigned int truesize; /* 224 4 */ atomic_t users; /* 228 4 */ unsigned char * head; /* 232 8 */ unsigned char * data; /* 240 8 */ unsigned char * tail; /* 248 8 */ /* --- cacheline 4 boundary (256 bytes) --- */ unsigned char * end; /* 256 8 */ }; /* size: 264, cachelines: 5 */ /* sum members: 260, holes: 1, sum holes: 4 */ /* bit holes: 1, sum bit holes: 2 bits */ /* last cacheline: 8 bytes */ On 32 bits nothing changes, and pointers continue to be used with the compiler turning all this abstraction layer into dust. But there are some sk_buff validation tricks that are now possible, humm... :-) Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit b0e380b1d8a8e0aca215df97702f99815f05c094 Author: Arnaldo Carvalho de Melo Date: Tue Apr 10 21:21:55 2007 -0700 [SK_BUFF]: unions of just one member don't get anything done, kill them Renaming skb->h to skb->transport_header, skb->nh to skb->network_header and skb->mac to skb->mac_header, to match the names of the associated helpers (skb[_[re]set]_{transport,network,mac}_header). Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit cfe1fc7759fdacb0c650b575daed1692bf3eaece Author: Arnaldo Carvalho de Melo Date: Fri Mar 16 17:26:39 2007 -0300 [SK_BUFF]: Introduce skb_network_header_len For the common sequence "skb->h.raw - skb->nh.raw", similar to skb->mac_len, that is precalculated tho, don't think we need to bloat skb with one more member, so just use this new helper, reducing the number of non-skbuff.h references to the layer headers even more. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit bff9b61ce330df04c6830d823c30c04203543f01 Author: Arnaldo Carvalho de Melo Date: Fri Mar 16 17:19:57 2007 -0300 [SK_BUFF]: Use the helpers to get the layer header pointer Some more cases... Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 514bca322cb9220308d22691ac1e74038bfabac3 Author: Patrick McHardy Date: Fri Mar 16 12:34:52 2007 -0700 [NET_SCHED]: Fix warning net/sched/sch_api.c: In function 'psched_show': net/sched/sch_api.c:1219: warning: format '%08x' expects type 'unsigned int', but argument 6 has type 's64' Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit bb239acf5679ee1936f6b1b034ad260c4fec89c8 Author: Patrick McHardy Date: Fri Mar 16 12:31:28 2007 -0700 [NET_SCHED]: sch_cbq: fix watchdog scheduled too late q->now is increased during dequeue and doesn't contain the current time afterwards, resulting in a too large timeout value for the qdisc watchdog. Use "now" instead, which still contains the current time. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 4361cb17f0df5491fe6e2c3ae1defc98e9a64a79 Author: Patrick McHardy Date: Fri Mar 16 01:23:28 2007 -0700 [NET_SCHED]: Export real timer resolution in /proc/net/psched The timer resolution exported in /proc/net/psched is used by userspace to calculate HTB's burst values. Currently it is set to HZ, since we're now using hrtimers, use KTIME_MONOTONIC_RES, which makes HTB use smaller burst values. This patch also affects libnl, which incorrectly uses this value for the SFQ perturbation parameter, which is always in seconds, and some routing cache values, which are in USER_HZ, so both cases are broken anyway. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 00c04af9df3d26e5a8093da850e982a7b6aeada7 Author: Patrick McHardy Date: Fri Mar 16 01:23:02 2007 -0700 [NET_SCHED]: kill jiffie conversion macros Now that all packet schedulers have been converted to hrtimers most users of PSCHED_JIFFIE2US and PSCHED_US2JIFFIE are gone. The remaining users use it to convert external time units to packet scheduler clock ticks, so use PSCHED_TICKS_PER_SEC instead. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit fb983d4578e238b7f483b4f8f39f3a0f35d34d16 Author: Patrick McHardy Date: Fri Mar 16 01:22:39 2007 -0700 [NET_SCHED]: sch_htb: use hrtimer based watchdog Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 1a13cb63d679da328cfa339c89b8b2d0eba3b81e Author: Patrick McHardy Date: Fri Mar 16 01:22:20 2007 -0700 [NET_SCHED]: sch_cbq: use hrtimer for delay_timer Switch delay_timer to hrtimer. The class penalty parameter is changed to use psched ticks as units. Since iproute never supported using this and the only existing user (libnl) incorrectly assumes psched ticks as units anyway, this shouldn't break anything. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit e9054a339eb275c756efeeaee42af484ac72a3f4 Author: Patrick McHardy Date: Fri Mar 16 01:21:40 2007 -0700 [NET_SCHED]: sch_cbq: fix cbq_undelay_prio for non-active priorites cbq_undelay_prio is supposed to return a time delta, but returns the current time for non-active priorities, causing cbq_undelay to mark the priority as active and schedule a timer for twice the current time. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 88a993540a65c38865f83961520494b4ad5d0363 Author: Patrick McHardy Date: Fri Mar 16 01:21:11 2007 -0700 [NET_SCHED]: sch_cbq: use hrtimer based watchdog Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 59cb5c6734021acc68590c7c2e0e92ad9a4952c6 Author: Patrick McHardy Date: Fri Mar 16 01:20:31 2007 -0700 [NET_SCHED]: sch_netem: use hrtimer based watchdog Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit f7f593e383145931cb2a65df62c31ce1bcc0cffc Author: Patrick McHardy Date: Fri Mar 16 01:20:07 2007 -0700 [NET_SCHED]: sch_tbf: use hrtimer based watchdog Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit ed2b229a97fd537857ad8441ab8b5996b15eadfd Author: Patrick McHardy Date: Fri Mar 16 01:19:33 2007 -0700 [NET_SCHED]: sch_hfsc: use hrtimer based watchdog Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 4179477f637caa730626bd597fdf28c5bad73565 Author: Patrick McHardy Date: Fri Mar 16 01:19:15 2007 -0700 [NET_SCHED]: Add hrtimer based qdisc watchdog Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 641b9e0e8b7f96425da6ce98f3361e3af0baee29 Author: Patrick McHardy Date: Fri Mar 16 01:18:42 2007 -0700 [NET_SCHED]: Use ktime as clocksource Get rid of the manual clock source selection mess and use ktime. Also use a scalar representation, which allows to clean up pkt_sched.h a bit more and results in less ktime_to_ns() calls in most cases. The PSCHED_US2JIFFIE/PSCHED_JIFFIE2US macros are implemented quite inefficient by this patch, following patches will convert all qdiscs to hrtimers and get rid of them entirely. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit ddc7b8e32b22fe8b45d306b7d99472d4b560add6 Author: Arnaldo Carvalho de Melo Date: Thu Mar 15 21:42:27 2007 -0300 [SK_BUFF]: Some more layer header conversions Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 0a6114d94b6d6f82e81cb8e0d8b0d4cf50739fec Author: Arnaldo Carvalho de Melo Date: Thu Mar 15 21:08:55 2007 -0300 [KBUILD]: Unifdef headers changed by the skb layer header refactorings Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit d10ba34b001944a8d1c8adb5646140ef089c432b Author: Arnaldo Carvalho de Melo Date: Wed Mar 14 21:05:37 2007 -0300 [SK_BUFF]: More skb_put related skb_reset_transport_header This time we have to set it to skb->tail that is not anymore equal to skb->data, so we either add a new helper or just add the skb->tail - skb->data offset, for now do the later. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 55f79cc0c02f9ce8f85e965e9679796f62b790f5 Author: Arnaldo Carvalho de Melo Date: Wed Mar 14 21:05:03 2007 -0300 [IPV6]: Reset the network header in ip6_nd_hdr ip6_nd_hdr is always called immediately after a alloc_skb + skb_reserve sequence, i.e. when skb->tail is equal to skb->data, making it correct to use skb_reset_network_header(). Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit eeeb03745bf9ea352df2373b9cb5fa14e60a2de0 Author: Arnaldo Carvalho de Melo Date: Wed Mar 14 21:04:34 2007 -0300 [SK_BUFF]: More skb_put related conversions to skb_reset_transport_header This is similar to the skb_reset_network_header(), i.e. at the point we reset the transport header pointer/offset skb->tail is equal to skb->data. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit ac6d141dc7d1d0eeec850d1b451dca83ce649684 Author: Pablo Neira Ayuso Date: Wed Mar 14 16:45:39 2007 -0700 [NETFILTER]: nfnetlink: parse attributes with nfattr_parse in nfnetlink_check_attribute Use nfattr_parse to parse attributes, this patch also modifies the default behaviour since unknown attributes will be ignored instead of returning EINVAL. This ensure backward compatibility: new libraries with new attributes and old kernels can work. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit c8e2078cfe414a99cf6f2f2f1d78c7e75392e9d4 Author: Pablo Neira Ayuso Date: Wed Mar 14 16:45:19 2007 -0700 [NETFILTER]: ctnetlink: add support for internal tcp connection tracking flags handling This patch let userspace programs set the IP_CT_TCP_BE_LIBERAL flag to force the pickup of established connections. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 5c8ce7c92106434d2bdc9d5dfa5f62bf4546b296 Author: Willy Tarreau Date: Wed Mar 14 16:44:53 2007 -0700 [NETFILTER]: TCP conntrack: factorize out the PUSH flag The PUSH flag is accepted with every other valid combination. Let's get it out of the tcp_valid_flags table and reduce the number of combinations we have to handle. This does not significantly reduce the table size however (8 bytes). Signed-off-by: Willy Tarreau Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 8f5bd99071212cd16b3449d16639971a44540d51 Author: Willy Tarreau Date: Wed Mar 14 16:44:31 2007 -0700 [NETFILTER]: TCP conntrack: accept RST|PSH as valid This combination has been encountered on an IBM AS/400 in response to packets sent to a closed session. There is no particular reason to mark it invalid. Signed-off-by: Willy Tarreau Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit e7ac05f3407a3fb5a1b2ff5d5554899eaa0a10a3 Author: Yasuyuki Kozakai Date: Wed Mar 14 16:44:01 2007 -0700 [NETFILTER]: nf_conntrack: add nf_copy() to safely copy members in skb This unifies the codes to copy netfilter related datas. Before copying, nf_copy() puts original members in destination skb. Signed-off-by: Yasuyuki Kozakai Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit edda553c324bdc5bb5c2d553b524cab37058a855 Author: Yasuyuki Kozakai Date: Wed Mar 14 16:43:37 2007 -0700 [NETFILTER]: nf_conntrack: add __nf_copy() to copy members in skb This unifies the codes to copy netfilter related datas. Note that __nf_copy() assumes destination skb doesn't have any netfilter related members. Signed-off-by: Yasuyuki Kozakai Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 9b88790972498d235a2a4d2b66640c3c5b70bb7c Author: Sami Farin Date: Wed Mar 14 16:43:00 2007 -0700 [NETFILTER]: nf_conntrack: use jhash2 in __hash_conntrack Now it uses jhash, but using jhash2 would be around 3-4 times faster (on P4). Signed-off-by: Sami Farin Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 8e87e014ec881ce353e1f43340157f519b5d9f30 Author: Patrick McHardy Date: Wed Mar 14 16:42:29 2007 -0700 [JHASH]: Use const in jhash2 Use const to avoid forcing users to cast const data. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit f4bc177f0ff0bf41b178452877762a9f0184d1a1 Author: Pablo Neira Ayuso Date: Wed Mar 14 16:42:11 2007 -0700 [NETFILTER]: nfnetlink: move EXPORT_SYMBOL declarations next to the exported symbol Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 8a2e89533a9b06bc960445dd6034eeab76117424 Author: Pablo Neira Ayuso Date: Wed Mar 14 16:41:47 2007 -0700 [NETFILTER]: nfnetlink: remove unused includes in nfnetlink.c Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit ac0f1d9894650d900af99bdaed83e110d9dce025 Author: Pablo Neira Ayuso Date: Wed Mar 14 16:41:28 2007 -0700 [NETFILTER]: nfnetlink: remove unrequired check in nfnetlink_get_subsys subsys_table is initialized to NULL, therefore just returns NULL in case that it is not set. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit d9e6d029498ab9e943c70f24c027aeda5602196d Author: Pablo Neira Ayuso Date: Wed Mar 14 16:41:03 2007 -0700 [NETFILTER]: nfnetlink: remove duplicate checks in nfnetlink_check_attributes Remove nfnetlink_check_attributes duplicates message size and callback id checks. nfnetlink_find_client and nfnetlink_rcv_msg already do such checks. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 67ca396606432aae3b747d5e6bb61d0c297eb782 Author: Pablo Neira Ayuso Date: Wed Mar 14 16:40:38 2007 -0700 [NETFILTER]: nfnetlink: remove early debugging messages from nfnetlink Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 010c7d6f867e98c86723f420d485583464fbab45 Author: Patrick McHardy Date: Wed Mar 14 16:40:10 2007 -0700 [NETFILTER]: nf_conntrack: uninline notifier registration functions Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 73c361862c2be2e4ed6019da283fe1b422107f16 Author: Patrick McHardy Date: Wed Mar 14 16:39:45 2007 -0700 [NETFILTER]: nfnetlink: use netlink_run_queue() Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit a3c5029cf7a96da3acdf6884a21581b5bef310c3 Author: Patrick McHardy Date: Wed Mar 14 16:39:25 2007 -0700 [NETFILTER]: nfnetlink: use mutex instead of semaphore Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit c6a1e615d1ba942b9e783079d53f741e4a8e1c89 Author: Patrick McHardy Date: Wed Mar 14 16:39:07 2007 -0700 [NETFILTER]: nf_conntrack: simplify l4 protocol array allocation The retrying after an allocation failure is not necessary anymore since we're holding the mutex the entire time, for the same reason the double allocation race can't happen anymore. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 0661cca9c216322e77dca7f47df107c02ce4e70c Author: Patrick McHardy Date: Wed Mar 14 16:38:48 2007 -0700 [NETFILTER]: nf_conntrack: simplify protocol locking Now that we don't use nf_conntrack_lock anymore but a single mutex for all protocol handling, no need to release and grab it again for sysctl registration. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit ac5357ebac43e191003c2cd0722377dccfa01a84 Author: Patrick McHardy Date: Wed Mar 14 16:38:25 2007 -0700 [NETFILTER]: nf_conntrack: remove ugly hack in l4proto registration Remove ugly special-casing of nf_conntrack_l4proto_generic, all it wants is its sysctl tables registered, so do that explicitly in an init function and move the remaining protocol initialization and cleanup code to nf_conntrack_proto.c as well. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit b19caa0ca071dce76b0e81e957e7eb7c03d72cf5 Author: Patrick McHardy Date: Wed Mar 14 16:37:52 2007 -0700 [NETFILTER]: nf_conntrack: switch protocol registration/unregistration to mutex The protocol lookups done by nf_conntrack are already protected by RCU, there is no need to keep taking nf_conntrack_lock for registration and unregistration. Switch to a mutex. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 587aa64163bb14f70098f450abab9410787fce9d Author: Patrick McHardy Date: Wed Mar 14 16:37:25 2007 -0700 [NETFILTER]: Remove IPv4 only connection tracking/NAT Remove the obsolete IPv4 only connection tracking/NAT as scheduled in feature-removal-schedule. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit ce18afe57bf53477f133208856dd2b7e6b5db5e3 Author: Tobias Klauser Date: Wed Mar 14 16:36:16 2007 -0700 [NETFILTER]: x_tables: remove duplicate of xt_prefix Remove xt_proto_prefix array which duplicates xt_prefix and change all users of xt_proto_prefix to xt_prefix. Signed-off-by: Tobias Klauser Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller commit 239254fedcbc6ff79bcf5696fe94723f7a5d0782 Author: David S. Miller Date: Thu Apr 19 19:55:44 2007 -0700 [IPV4] xfrm4_mode_beet: Use skb_transport_header(). Signed-off-by: David S. Miller commit 9c70220b73908f64792422a2c39c593c4792f2c5 Author: Arnaldo Carvalho de Melo Date: Wed Apr 25 18:04:18 2007 -0700 [SK_BUFF]: Introduce skb_transport_header(skb) For the places where we need a pointer to the transport header, it is still legal to touch skb->h.raw directly if just adding to, subtracting from or setting it to another layer header. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit a27ef749e7be3b06fb58df53d94eb97a21f18707 Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 17:17:10 2007 -0300 [SCTP]: Eliminate some pointer attributions to the skb layer headers Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit bd82393ca23324d103b21aae43160728da6e6c9c Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 17:10:43 2007 -0300 [SK_BUFF]: More skb_reset_transport_header conversions These are a bit more subtle, they are of this type: - skb->h.raw = payload; __skb_pull(skb, payload - skb->data); + skb_reset_transport_header(skb); __skb_pull results in: skb->data = skb->data + payload - skb->data; skb->data = payload; So after __skb_pull we have skb->data pointing to payload and we can just call skb_reset_transport_header(skb), that will do: skb->h.raw = payload; The others are similar, allowing us to get rid of some more cases where a pointer was being attributed to the layer headers. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 39b89160df691045d1449cbaef43c02084c7543a Author: Arnaldo Carvalho de Melo Date: Tue Apr 10 21:06:25 2007 -0700 [SK_BUFF]: Introduce ipipv6_hdr(), remove skb->h.ipv6h Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit b0061ce49c83657563b64ffcf1ec137110230d93 Author: Arnaldo Carvalho de Melo Date: Wed Apr 25 18:02:22 2007 -0700 [SK_BUFF]: Introduce ipip_hdr(), remove skb->h.ipiph Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit aa8223c7bb0b05183e1737881ed21827aa5b9e73 Author: Arnaldo Carvalho de Melo Date: Tue Apr 10 21:04:22 2007 -0700 [SK_BUFF]: Introduce tcp_hdr(), remove skb->h.th Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit ab6a5bb6b28a970104a34f0f6959b73cf61bdc72 Author: Arnaldo Carvalho de Melo Date: Sun Mar 18 17:43:48 2007 -0700 [TCP]: Introduce tcp_hdrlen() and tcp_optlen() The ip_hdrlen() buddy, created to reduce the number of skb->h.th-> uses and to avoid the longer, open coded equivalent. Ditched a no-op in bnx2 in the process. I wonder if we should have a BUG_ON(skb->h.th->doff < 5) in tcp_optlen()... Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 88c7664f13bd1a36acb8566b93892a4c58759ac6 Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 14:43:18 2007 -0300 [SK_BUFF]: Introduce icmp_hdr(), remove skb->h.icmph Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 4bedb45203eab92a87b4c863fe2d0cded633427f Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 14:28:48 2007 -0300 [SK_BUFF]: Introduce udp_hdr(), remove skb->h.uh Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit d9edf9e2be0f7661558984c32bd53867a7037fd3 Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 14:19:23 2007 -0300 [SK_BUFF]: Introduce igmp_hdr() & friends, remove skb->h.igmph Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit cc70ab261c9f997589546100ddec5da6bfd89c4e Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 14:03:22 2007 -0300 [ICMP6]: Introduce icmp6_hdr() For consistency with all the other skb->h.raw accessors. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 2c0fd387b00a6758550b5ca1aae4408374483fe7 Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 13:59:32 2007 -0300 [SCTP]: Introduce sctp_hdr() For consistency with all the other skb->h.raw accessors. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 967b05f64e27d04a4c8879addd0e1c52137e2c9e Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 13:51:52 2007 -0300 [SK_BUFF]: Introduce skb_set_transport_header For the cases where the transport header is being set to a offset from skb->data. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit ea2ae17d6443abddc79480dc9f7af8feacabddc4 Author: Arnaldo Carvalho de Melo Date: Wed Apr 25 17:55:53 2007 -0700 [SK_BUFF]: Introduce skb_transport_offset() For the quite common 'skb->h.raw - skb->data' sequence. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit badff6d01a8589a1c828b0bf118903ca38627f4e Author: Arnaldo Carvalho de Melo Date: Tue Mar 13 13:06:52 2007 -0300 [SK_BUFF]: Introduce skb_reset_transport_header(skb) For the common, open coded 'skb->h.raw = skb->data' operation, so that we can later turn skb->h.raw into a offset, reducing the size of struct sk_buff in 64bit land while possibly keeping it as a pointer on 32bit. This one touches just the most simple cases: skb->h.raw = skb->data; skb->h.raw = {skb_push|[__]skb_pull}() The next ones will handle the slightly more "complex" cases. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 0660e03f6b18f19b6bbafe7583265a51b90daf36 Author: Arnaldo Carvalho de Melo Date: Wed Apr 25 17:54:47 2007 -0700 [SK_BUFF]: Introduce ipv6_hdr(), remove skb->nh.ipv6h Now the skb->nh union has just one member, .raw, i.e. it is just like the skb->mac union, strange, no? I'm just leaving it like that till the transport layer is done with, when we'll rename skb->mac.raw to skb->mac_header (or ->mac_header_offset?), ditto for ->{h,nh}. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit d0a92be05ed4aea7d35c2b257e3f9173565fe4eb Author: Arnaldo Carvalho de Melo Date: Mon Mar 12 20:56:31 2007 -0300 [SK_BUFF]: Introduce arp_hdr(), remove skb->nh.arph Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit fd74e6ccd522e2f26163eb5ac1abebcab2bd017c Author: Stephen Hemminger Date: Mon Mar 12 16:25:32 2007 -0700 [BRIDGE]: faster compare for link local addresses Use logic operations rather than memcmp() to compare destination address with link local multicast addresses. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit eddc9ec53be2ecdbf4efe0efd4a83052594f0ac0 Author: Arnaldo Carvalho de Melo Date: Fri Apr 20 22:47:35 2007 -0700 [SK_BUFF]: Introduce ip_hdr(), remove skb->nh.iph Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit e023dd643798c4f06c16466af90b4d250e4b8bd7 Author: Arnaldo Carvalho de Melo Date: Mon Mar 12 20:09:36 2007 -0300 [IPMR]: Fix bug introduced when converting to skb_network_reset_header Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit c9bdd4b5257406b0608385d19c40b5511decf4f6 Author: Arnaldo Carvalho de Melo Date: Mon Mar 12 20:09:15 2007 -0300 [IP]: Introduce ip_hdrlen() For the common sequence "skb->nh.iph->ihl * 4", removing a good number of open coded skb->nh.iph uses, now to go after the rest... Just out of curiosity, here are the idioms found to get the same result: skb->nh.iph->ihl << 2 skb->nh.iph->ihl<<2 skb->nh.iph->ihl * 4 skb->nh.iph->ihl*4 (skb->nh.iph)->ihl * sizeof(u32) Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 0272ffc46f81a4bbbf302ba093c737e969c5bb55 Author: Arnaldo Carvalho de Melo Date: Mon Mar 12 20:05:39 2007 -0300 [SK_BUFF] ipmr: Missed one conversion to skb_network_header() We can't access skb->nh.raw directly anymore, it will become an offset. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 0e1256ffd1ec654b35e023c66f6b262d4cba91e9 Author: Stephen Hemminger Date: Mon Mar 12 14:35:37 2007 -0700 [NET]: show bound packet types Show what protocols are bound to what packet types in /proc/net/ptype Uses kallsyms to decode function pointers if possible. Example: Type Device Function ALL eth1 packet_rcv_spkt+0x0 0800 ip_rcv+0x0 0806 arp_rcv+0x0 86dd :ipv6:ipv6_rcv+0x0 Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit f690808e17925fc45217eb22e8670902ecee5c1b Author: Stephen Hemminger Date: Mon Mar 12 14:34:29 2007 -0700 [NET]: make seq_operations const The seq_file operations stuff can be marked constant to get it out of dirty cache. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 6b2bedc3a659ba228a93afc8e3f008e152abf18a Author: Stephen Hemminger Date: Mon Mar 12 14:33:50 2007 -0700 [NET]: network dev read_mostly For Eric, mark packet type and network device watermarks as read mostly. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit c14d2450cb7fe1786e2ec325172baf66922bf597 Author: Arnaldo Carvalho de Melo Date: Sun Mar 11 22:39:41 2007 -0300 [SK_BUFF]: Introduce skb_set_network_header For the cases where the network header is being set to a offset from skb->data. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 878c814500b123dd61a5e211879a32e5fd932713 Author: Arnaldo Carvalho de Melo Date: Sun Mar 11 22:38:29 2007 -0300 [SK_BUFF] ipmr: Another skb_push related conversion to skb_reset_network_header Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit d56f90a7c96da5187f0cdf07ee7434fe6aa78bbc Author: Arnaldo Carvalho de Melo Date: Tue Apr 10 20:50:43 2007 -0700 [SK_BUFF]: Introduce skb_network_header() For the places where we need a pointer to the network header, it is still legal to touch skb->nh.raw directly if just adding to, subtracting from or setting it to another layer header. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit bbe735e4247dba32568a305553b010081c8dea99 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 22:16:10 2007 -0300 [SK_BUFF]: Introduce skb_network_offset() For the quite common 'skb->nh.raw - skb->data' sequence. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit e7dd65dafda5737a983c04d652a69ab8da78ee3f Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 20:09:45 2007 -0300 [SK_BUFF] bonding: Set skb->nh.raw relative to skb->mac.raw Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 7f5c0cb05f158ee91414e1f99d3fe18349a80371 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 19:59:16 2007 -0300 [SK_BUFF] xfrm4: use skb_reset_network_header Setting it to skb->h.raw, which is valid, in the (to become) old pointer based world order and in the new world of offset based layer headers. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 1ced98e81d1c2f1ce965ecf8d0032e02ffa07bf0 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 19:57:15 2007 -0300 [SK_BUFF] ipv6: More skb_reset_network_header conversions related to skb_pull Now related to this form: skb->nh.ipv6h = (struct ipv6hdr *)skb_put(skb, length); That, as the others, is done when skb->tail is still equal to skb->data, making the conversion to skb_reset_network_header possible. Also one more case equivalent to skb->nh.raw = skb->data, of this form: iph = (struct ipv6hdr *)skb->data; skb->nh.ipv6h = iph; Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 8856dfa3e9b71ac2177016f66ace3a8978afecc1 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 19:40:39 2007 -0300 [SK_BUFF]: Use skb_reset_network_header after skb_push Some more cases where skb->nh.iph was being set that were converted to using skb_reset_network_header. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 04b964dbad25cbd6edd8ecbeca2efb40c9860865 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 19:27:27 2007 -0300 [SK_BUFF] ipconfig: Another conversion to skb_reset_network_header related to skb_put boot_pkt->iph is the first member, that is at skb->data, so just use skb_reset_network_header(). Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 2ca9e6f2c2a4117d21947e911ae1f5e5306b0df0 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 19:15:25 2007 -0300 [SK_BUFF]: Some more skb_put cases converted to skb_reset_network_header Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 31c7711b509d470ab1e175e7bb98ea66a82aa916 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 19:04:55 2007 -0300 [SK_BUFF]: Some more simple skb_reset_network_header conversions This time of the type: skb->nh.iph = (struct iphdr *)skb->data; That is completely equivalent to: skb->nh.raw = skb->data; Wonder why people love casts... :-) Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 4209fb601c0a0e0a9d90c0008f350dd345c8b7de Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 18:42:03 2007 -0300 [SK_BUFF]: Use skb_reset_network_header where the return of __pskb_pull was being used It returns skb->data, so we can just use skb_reset_network_header after it. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 7e28ecc282574a7d72ace365fc9bc86e27ba880f Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 18:40:59 2007 -0300 [SK_BUFF]: Use skb_reset_network_header where the skb_pull return was being used But only in the cases where its a newly allocated skb, i.e. one where skb->tail is equal to skb->data, or just after skb_reserve, where this requirement is maintained. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit e2d1bca7e6134671bcb19810d004a252aa6a644d Author: Arnaldo Carvalho de Melo Date: Tue Apr 10 20:46:21 2007 -0700 [SK_BUFF]: Use skb_reset_network_header in skb_push cases skb_push updates and returns skb->data, so we can just call skb_reset_network_header after the call to skb_push. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit c1d2bbe1cd6c7bbdc6d532cefebb66c7efb789ce Author: Arnaldo Carvalho de Melo Date: Tue Apr 10 20:45:18 2007 -0700 [SK_BUFF]: Introduce skb_reset_network_header(skb) For the common, open coded 'skb->nh.raw = skb->data' operation, so that we can later turn skb->nh.raw into a offset, reducing the size of struct sk_buff in 64bit land while possibly keeping it as a pointer on 32bit. This one touches just the most simple case, next will handle the slightly more "complex" cases. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 57effc70a5be9f7804e9a99964eb7265367effca Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 16:21:45 2007 -0300 [IPV6]: Use skb->nh.ipv6h instead of casting skb->nh.raw nh.ipv6h is there exactly for this reason! Use it while it exists ;-) Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit a16aeb36239ce612699ed64a75a03c88cbc657e8 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 16:07:19 2007 -0300 [BONDING]: Introduce arp_pkt() For consistency with all the other skb->nh.raw accessors. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 797659fb4a4a511649cd71028141c32ad1698a12 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 15:56:08 2007 -0300 [PPPOE]: Introduce pppoe_hdr() For consistency with all the other skb->nh.raw accessors. Also do some really obvious simplifications in pppoe_recvmsg, well the kfree_skb one is not so obvious, but free() and kfree() have the same behaviour (hint :-) ). Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 37e6636669b0b996681586facee8034f7f674f6a Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 15:34:36 2007 -0300 [LLC]: Kill llc_set_pdu_hdr We'll have skb_reset_network_header soon. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 98e399f82ab3a6d863d1d4a7ea48925cc91c830e Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 15:33:04 2007 -0700 [SK_BUFF]: Introduce skb_mac_header() For the places where we need a pointer to the mac header, it is still legal to touch skb->mac.raw directly if just adding to, subtracting from or setting it to another layer header. This one also converts some more cases to skb_reset_mac_header() that my regex missed as it had no spaces before nor after '=', ugh. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 31713c333ddbb66d694829082620b69b71c4b09a Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 12:48:37 2007 -0300 [TCP]: Use skb_set_mac_header in tcp_collapse Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit c51957dafa6f960c5c6372aa3da6c8fa71c13730 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 12:47:22 2007 -0300 [TCP]: Do the layer header setting in tcp_collapse relative to skb->data That is equal to skb->head before skb_reserve, to help in the layer header changes. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 39f69c6f922fbfb51e1ff24c9e196584a79f1484 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 12:40:27 2007 -0300 [SK_BUFF] xfrm: Use skb_set_mac_header in the memmove cases Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 48d49d0ccdaa9caff4636ef9c3410973d28131b5 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 12:30:58 2007 -0300 [SK_BUFF]: Introduce skb_set_mac_header() For the cases where we want to set skb->mac.raw to an offset from skb->data. Simple cases first, the memmove ones and specially pktgen will be left for later. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit f64955eb117ad62480b858fd69a11e6f9e74f60b Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 12:17:29 2007 -0300 [LLC]: Use skb_reset_mac_header in llc_mac_hdr_init skb_push updates and returns skb->data, so we can just call skb_reset_mac_header after the call to skb_push. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 0a1b0ad9ae27f918fd935c6da101083e11446f09 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 12:14:56 2007 -0300 [LLC]: Use skb_reset_mac_header in llc_alloc_frame skb->head is equal to skb->data after alloc_skb, so reset the mac header while this is true, i.e. before skb_reserve. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 459a98ed881802dee55897441bc7f77af614368e Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 15:30:44 2007 -0700 [SK_BUFF]: Introduce skb_reset_mac_header(skb) For the common, open coded 'skb->mac.raw = skb->data' operation, so that we can later turn skb->mac.raw into a offset, reducing the size of struct sk_buff in 64bit land while possibly keeping it as a pointer on 32bit. This one touches just the most simple case, next will handle the slightly more "complex" cases. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 4c13eb6657fe9ef7b4dc8f1a405c902e9e5234e0 Author: Arnaldo Carvalho de Melo Date: Wed Apr 25 17:40:23 2007 -0700 [ETH]: Make eth_type_trans set skb->dev like the other *_type_trans One less thing for drivers writers to worry about. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 029720f15dcd3c6c16824177cfc486083b229411 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 11:20:07 2007 -0300 [AOE]: Introduce aoe_hdr() For consistency with other skb->mac.raw users. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 4839fccea04b5f4d2b3ce01585d6bdbcbc24002c Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 11:13:59 2007 -0300 [QETH]: Use eth_hdr() Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 0a4f23fbbff70c268b0f2f5e0b87301c132fb305 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 10:57:13 2007 -0300 [HIPPI/FDDI]: Make {hippi,fddi}_type_trans set skb->dev Now all the _type_trans routines are consistent in this regard. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit c8fb7948dc1aeff0515b2912b564d4236f6c0ebd Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 15:29:16 2007 -0700 [TR]: Make tr_type_trans set skb->dev Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit c1a4b86e396b6870b420d23e4d49c7b685aef0a4 Author: Arnaldo Carvalho de Melo Date: Mon Mar 19 15:27:07 2007 -0700 [TR]: Use tr_hdr() were appropriate Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 7c81fd8bfbaa9732eca142350de5154da6919411 Author: Arnaldo Carvalho de Melo Date: Sat Mar 10 00:39:35 2007 -0300 [SOCKET]: Export __sock_recv_timestamp Kernel: arch/x86_64/boot/bzImage is ready (#2) MODPOST 1816 modules WARNING: "__sock_recv_timestamp" [net/sctp/sctp.ko] undefined! WARNING: "__sock_recv_timestamp" [net/packet/af_packet.ko] undefined! WARNING: "__sock_recv_timestamp" [net/key/af_key.ko] undefined! WARNING: "__sock_recv_timestamp" [net/ipv6/ipv6.ko] undefined! WARNING: "__sock_recv_timestamp" [net/atm/atm.ko] undefined! make[2]: *** [__modpost] Error 1 make[1]: *** [modules] Error 2 make: *** [_all] Error 2 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 92f37fd2ee805aa77925c1e64fd56088b46094fc Author: Eric Dumazet Date: Sun Mar 25 22:14:49 2007 -0700 [NET]: Adding SO_TIMESTAMPNS / SCM_TIMESTAMPNS support Now that network timestamps use ktime_t infrastructure, we can add a new SOL_SOCKET sockopt SO_TIMESTAMPNS. This command is similar to SO_TIMESTAMP, but permits transmission of a 'timespec struct' instead of a 'timeval struct' control message. (nanosecond resolution instead of microsecond) Control message is labelled SCM_TIMESTAMPNS instead of SCM_TIMESTAMP A socket cannot mix SO_TIMESTAMP and SO_TIMESTAMPNS : the two modes are mutually exclusive. sock_recv_timestamp() became too big to be fully inlined so I added a __sock_recv_timestamp() helper function. Signed-off-by: Eric Dumazet CC: linux-arch@vger.kernel.org Signed-off-by: David S. Miller commit c7a3c5da35055e2fa97ed4f0da3eec4bd0ef4c38 Author: Arnaldo Carvalho de Melo Date: Fri Mar 9 13:51:54 2007 -0800 [UDP]: Use __skb_pull since we have checked it won't fail with pskb_may_pull Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller commit 6dea649a8a4c4b086227018c919298f988c34b30 Author: Eric Dumazet Date: Thu Mar 8 22:36:37 2007 -0800 [NET]: New sysctls should use __read_mostly tags net_msg_warn should be placed in the read_mostly section, to avoid performance problems on SMP Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit e5268f12f26f1f51590cd1ed26547e21c46b08f2 Author: YOSHIFUJI Hideaki Date: Thu Mar 8 20:48:23 2007 -0800 [IPV6]: Ensure to truncate result and return full length for sticky options. Bug noticed by Chris Wright . Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 4c6510a738c71ca6b4b7b624a7d0a00acebfd7fb Author: YOSHIFUJI Hideaki Date: Sun Mar 18 17:35:57 2007 -0700 [IPV6]: Return correct result for sticky options. We returned incorrect result with IPV6_RTHDRDSTOPTS, IPV6_RTHDR and IPV6_DSTOPTS. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 3fbe070a4293e8ab2d2edb1bc23f1e5220ce61af Author: Stephen Hemminger Date: Thu Mar 8 20:46:41 2007 -0800 [UDP]: deinline A couple of functions are exported or used indirectly so it is pointless to mark them as inline. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 6f05f629716a71d4c9c82813f45d3e9a6e90d146 Author: Stephen Hemminger Date: Thu Mar 8 20:46:03 2007 -0800 [NET]: deinline some functions Several functions are marked inline or forced inline, but it would be better to let the compiler decide. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 2de979bd7da9c8b39cc0aabb0ab5aa1516d929eb Author: Stephen Hemminger Date: Thu Mar 8 20:45:19 2007 -0800 [TCP]: whitespace cleanup Add whitespace around keywords. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 132adf54639cf7dd9315e8df89c2faa59f6e46d9 Author: Stephen Hemminger Date: Thu Mar 8 20:44:43 2007 -0800 [IPV4]: cleanup Add whitespace around keywords. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 1ac58ee37f439044eb09381f33c97ce0e7f2643b Author: Stephen Hemminger Date: Thu Mar 8 20:43:49 2007 -0800 [WIRELESS]: use ARRAY_SIZE() Use ARRAY_SIZE() macro now. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit e71a4783aae059931f63b2d4e7013e36529badef Author: Stephen Hemminger Date: Tue Apr 10 20:10:33 2007 -0700 [NET] core: whitespace cleanup Fix whitespace around keywords. Fix indentation especially of switch statements. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit add459aa1afe05472abc96f6a29aefd0c84e73d6 Author: Stephen Hemminger Date: Thu Mar 8 20:42:35 2007 -0800 [UDP]: ipv6 style cleanup Fix whitespace around keywords. Eliminate unnecessary ()'s on return statements. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 6516c65573fde5e421c6c92c4b180bbe2245b23b Author: Stephen Hemminger Date: Thu Mar 8 20:41:55 2007 -0800 [UDP]: ipv4 whitespace cleanup Fix whitespace around keywords. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit a2a316fd068c455c609ecc155dcfaa7e208d29fe Author: Stephen Hemminger Date: Thu Mar 8 20:41:08 2007 -0800 [NET]: Replace CONFIG_NET_DEBUG with sysctl. Covert network warning messages from a compile time to runtime choice. Removes kernel config option and replaces it with new /proc/sys/net/core/warnings. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit ae40eb1ef30ab4120bd3c8b7e3da99ee53d27a23 Author: Eric Dumazet Date: Sun Mar 18 17:33:16 2007 -0700 [NET]: Introduce SIOCGSTAMPNS ioctl to get timestamps with nanosec resolution Now network timestamps use ktime_t infrastructure, we can add a new ioctl() SIOCGSTAMPNS command to get timestamps in 'struct timespec'. User programs can thus access to nanosecond resolution. Signed-off-by: Eric Dumazet CC: Stephen Hemminger Signed-off-by: David S. Miller commit cb69cc52364690d7789940c480b3a9490784b680 Author: Adrian Bunk Date: Wed Mar 7 19:33:52 2007 -0800 [TCP/DCCP/RANDOM]: Remove unused exports. This patch removes the following not or no longer used exports: - drivers/char/random.c: secure_tcp_sequence_number - net/dccp/options.c: sysctl_dccp_feat_sequence_window - net/netlink/af_netlink.c: netlink_set_err Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller commit fe067e8ab5e0dc5ca3c54634924c628da92090b4 Author: David S. Miller Date: Wed Mar 7 12:12:44 2007 -0800 [TCP]: Abstract out all write queue operations. This allows the write queue implementation to be changed, for example, to one which allows fast interval searching. Signed-off-by: David S. Miller commit 02ea4923b4997d7e1310c027081f46d584b9d714 Author: YOSHIFUJI Hideaki Date: Wed Mar 7 14:21:31 2007 +0900 [NET] TIPC: Use htons() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit b6d9bcb0697e60d5424e2f395fe950f0e22f4418 Author: YOSHIFUJI Hideaki Date: Wed Mar 7 14:21:20 2007 +0900 [NET] SCHED: Use htons() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 8f05ce91c8b801af106611ad83b1d8d7429b9b46 Author: YOSHIFUJI Hideaki Date: Wed Mar 7 14:21:00 2007 +0900 [NET] NETFILTER: Use htonl() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 4412ec494868160d57da6e436a92b0696f40b19d Author: YOSHIFUJI Hideaki Date: Wed Mar 7 14:19:10 2007 +0900 [NET] IPV4: Use hton{s,l}() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 1c9e8ef7f731c2548414644e5bf540c38c85aff0 Author: YOSHIFUJI Hideaki Date: Wed Mar 7 14:19:05 2007 +0900 [NET] IEEE80211: Use htons() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit f576e24ffaf2c6b01af389e3bad3342681a8b84f Author: YOSHIFUJI Hideaki Date: Wed Mar 7 14:19:03 2007 +0900 [NET] ETHERNET: Use htons() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 724800d61b8bc574a364707b6a6c6a6252e8cdb4 Author: YOSHIFUJI Hideaki Date: Sun Mar 25 20:13:04 2007 -0700 [NET] CORE: Use htons() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit aca3192cc60d2bf193c2252e45563c32e3117289 Author: YOSHIFUJI Hideaki Date: Sun Mar 25 20:12:50 2007 -0700 [NET] BLUETOOTH: Use cpu_to_le{16,32}() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit acde4855bb8f5fba8bb065d35ff6ac8a94b3dfa8 Author: YOSHIFUJI Hideaki Date: Sun Mar 25 20:12:32 2007 -0700 [NET] ATM: Use htons() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit b93b7eebd328d5c1d171896fb823267539d4a0f6 Author: YOSHIFUJI Hideaki Date: Sun Mar 25 20:12:18 2007 -0700 [NET] 8021Q: Use htons() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 2953fd246845f4d00af3717163f37b2ff4c5ce29 Author: YOSHIFUJI Hideaki Date: Sun Mar 25 20:11:55 2007 -0700 [NET] 802: Use hton{s,l}() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 759e5d006462d53fb708daa8284b4ad909415da1 Author: Herbert Xu Date: Sun Mar 25 20:10:56 2007 -0700 [UDP]: Clean up UDP-Lite receive checksum This patch eliminates some duplicate code for the verification of receive checksums between UDP-Lite and UDP. It does this by introducing __skb_checksum_complete_head which is identical to __skb_checksum_complete_head apart from the fact that it takes a length parameter rather than computing the first skb->len bytes. As a result UDP-Lite will be able to use hardware checksum offload for packets which do not use partial coverage checksums. It also means that UDP-Lite loopback no longer does unnecessary checksum verification. If any NICs start support UDP-Lite this would also start working automatically. This patch removes the assumption that msg_flags has MSG_TRUNC clear upon entry in recvmsg. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller commit 1ab6eb62b02e0949a392fb19bf31ba59ae1022b1 Author: Herbert Xu Date: Tue Mar 6 20:29:58 2007 -0800 [UDP6]: Restore sk_filter optimisation This reverts the changeset [IPV6]: UDPv6 checksum. We always need to check UDPv6 checksum because it is mandatory. The sk_filter optimisation has nothing to do whether we verify the checksum. It simply postpones it to the point when the user calls recv or poll. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller commit 243bbcaa09e8482aa28065cbc2eb99f0ca2fc8d6 Author: Eric Dumazet Date: Tue Mar 6 20:23:10 2007 -0800 [IPV4]: Optimize inet_getpeer() 1) Some sysctl vars are declared __read_mostly 2) We can avoid updating stack[] when doing an AVL lookup only. lookup() macro is extended to receive a second parameter, that may be NULL in case of a pure lookup (no need to save the AVL path). This removes unnecessary instructions, because compiler knows if this _stack parameter is NULL or not. text size of net/ipv4/inetpeer.o is 2063 bytes instead of 2107 on x86_64 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit 43e683926f808cec9802466c27cee7499eda3d11 Author: Stephen Hemminger Date: Tue Mar 6 20:21:20 2007 -0800 [TCP] TCP Yeah: cleanup Eliminate need for full 6/4/64 divide to compute queue. Variable maxqueue was really a constant. Fix indentation. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit c5f5877c043ca471c3a607fa2c864848b19bc49a Author: Stephen Hemminger Date: Sun Mar 25 20:21:15 2007 -0700 [TCP] tcp_cubic: faster cube root The Newton-Raphson method is quadratically convergent so only a small fixed number of steps are necessary. Therefore it is faster to unroll the loop. Since div64_64 is no longer inline it won't cause code explosion. Also fixes a bug that can occur if x^2 was bigger than 32 bits. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 8570419fb7be0af84085ac8f13307392a748482c Author: YOSHIFUJI Hideaki Date: Tue Mar 6 20:19:26 2007 -0800 [ATM] ENI: Convert to struct timeval to ktime_t. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit fc910a27839584209726537698b596576940add4 Author: David S. Miller Date: Sun Mar 25 20:27:59 2007 -0700 [NETLINK]: Limit NLMSG_GOODSIZE to 8K. Signed-off-by: David S. Miller commit ca043569390c528de4cd5ec9e07502f2bf4ecd1f Author: YOSHIFUJI Hideaki Date: Wed Feb 28 23:13:20 2007 +0900 [IPV6] ADDRCONF: Fix possible inet6_ifaddr leakage with CONFIG_OPTIMISTIC_DAD. The inet6_ifaddr for source address of RS is leaked if the address is not an optimistic address. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 95c385b4d5a71b8ad552aecaa968ea46d7da2f6a Author: Neil Horman Date: Wed Apr 25 17:08:10 2007 -0700 [IPV6] ADDRCONF: Optimistic Duplicate Address Detection (RFC 4429) Support. Nominally an autoconfigured IPv6 address is added to an interface in the Tentative state (as per RFC 2462). Addresses in this state remain in this state while the Duplicate Address Detection process operates on them to determine their uniqueness on the network. During this period, these tentative addresses may not be used for communication, increasing the time before a node may be able to communicate on a network. Using Optimistic Duplicate Address Detection, autoconfigured addresses may be used immediately for communication on the network, as long as certain rules are followed to avoid conflicts with other nodes during the Duplicate Address Detection process. Signed-off-by: Neil Horman Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 502b093569e48db264831be7966e1c447de2f52f Author: Yasuyuki Kozakai Date: Thu Nov 30 14:43:28 2006 +0900 [IPV6] IP6TUNNEL: Enable to control the handled inner protocol. ip6_tunnel before supporting IPv4/IPv6 tunnel allows only IPPROTO_IPV6 in configurations from userland. This allows userland to set IPPROTO_IPIP and 0(wildcard). ip6_tunnel only handles allowed inner protocols. Signed-off-by: Yasuyuki Kozakai Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 3144581cb0b4b1ef897470195128cc1c8dc037b6 Author: Yasuyuki Kozakai Date: Sat Feb 10 00:30:33 2007 +0900 [IPV6] IP6TUNNEL: Rename functions ip6ip6_* to ip6_tnl_*. Signed-off-by: Yasuyuki Kozakai Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit c4d3efafcc933fd2ffd169d7dc4f980393a13796 Author: Yasuyuki Kozakai Date: Thu Feb 15 00:43:16 2007 +0900 [IPV6] IP6TUNNEL: Add support to IPv4 over IPv6 tunnel. Some notes - Protocol number IPPROTO_IPIP is used for IPv4 over IPv6 packets. - If IP6_TNL_F_USE_ORIG_TCLASS is set, TOS in IPv4 header is copied to Traffic Class in outer IPv6 header on xmit. - IP6_TNL_F_USE_ORIG_FLOWLABEL is ignored on xmit of IPv4 packets, because IPv4 header does not have flow label. - Kernel sends ICMP error if IPv4 packet is too big on xmit, even if DF flag is not set. Signed-off-by: Yasuyuki Kozakai Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 61ec2aec28ba8de09f76a558a5d6d3893b1d2e47 Author: Yasuyuki Kozakai Date: Sun Nov 5 22:56:45 2006 +0900 [IPV6] IP6TUNNEL: Split out generic routine in ip6ip6_xmit(). This enables to add IPv4/IPv6 specific handling later, Signed-off-by: Yasuyuki Kozakai Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 8359925be8bb5960f614e3f25454f3ef7cc9df65 Author: Yasuyuki Kozakai Date: Fri Nov 3 09:39:14 2006 +0900 [IPV6] IP6TUNNEL: Split out generic routine in ip6ip6_rcv(). This enables to add IPv4/IPv6 specific handling later, Signed-off-by: Yasuyuki Kozakai Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit e490d1d85cf5e191791979e5f260d32eb4f703a8 Author: Yasuyuki Kozakai Date: Tue Oct 31 23:11:25 2006 +0900 [IPV6] IP6TUNNEL: Split out generic routine in ip6ip6_err(). This enables to add IPv4/IPv6 specific error handling later, Signed-off-by: Yasuyuki Kozakai Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller commit 7159039a128fa0a73ca7b532f6e1d30d9885277f Author: YOSHIFUJI Hideaki Date: Thu Feb 22 22:05:40 2007 +0900 [IPV6]: Decentralize EXPORT_SYMBOLs. Signed-off-by: YOSHIFUJI Hideaki commit b558ff799977a4eda8b3823d1cf6c1c33becb671 Author: David S. Miller Date: Tue Mar 6 17:02:35 2007 -0800 [NETLINK]: Mirror UDP MSG_TRUNC semantics. If the user passes MSG_TRUNC in via msg_flags, return the full packet size not the truncated size. Idea from Herbert Xu and Thomas Graf. Signed-off-by: David S. Miller commit b7aa0bf70c4afb9e38be25f5c0922498d0f8684c Author: Eric Dumazet Date: Thu Apr 19 16:16:32 2007 -0700 [NET]: convert network timestamps to ktime_t We currently use a special structure (struct skb_timeval) and plain 'struct timeval' to store packet timestamps in sk_buffs and struct sock. This has some drawbacks : - Fixed resolution of micro second. - Waste of space on 64bit platforms where sizeof(struct timeval)=16 I suggest using ktime_t that is a nice abstraction of high resolution time services, currently capable of nanosecond resolution. As sizeof(ktime_t) is 8 bytes, using ktime_t in 'struct sock' permits a 8 byte shrink of this structure on 64bit architectures. Some other structures also benefit from this size reduction (struct ipq in ipv4/ip_fragment.c, struct frag_queue in ipv6/reassembly.c, ...) Once this ktime infrastructure adopted, we can more easily provide nanosecond resolution on top of it. (ioctl SIOCGSTAMPNS and/or SO_TIMESTAMPNS/SCM_TIMESTAMPNS) Note : this patch includes a bug correction in compat_sock_get_timestamp() where a "err = 0;" was missing (so this syscall returned -ENOENT instead of 0) Signed-off-by: Eric Dumazet CC: Stephen Hemminger CC: John find Signed-off-by: David S. Miller commit 3927f2e8f9afa3424bb51ca81f7abac01ffd0005 Author: Stephen Hemminger Date: Sun Mar 25 19:54:23 2007 -0700 [NET]: div64_64 consolidate (rev3) Here is the current version of the 64 bit divide common code. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller commit 9d729f72dca9406025bcfa9c1f660d71d9ef0ff5 Author: James Morris Date: Sun Mar 4 16:12:44 2007 -0800 [NET]: Convert xtime.tv_sec to get_seconds() Where appropriate, convert references to xtime.tv_sec to the get_seconds() helper function. Signed-off-by: James Morris Signed-off-by: David S. Miller commit 39df232f1a9ba48d41c68ee7d4046756e709cf91 Author: Stephen Hemminger Date: Sun Mar 4 16:11:51 2007 -0800 [PKTGEN]: fix device name handling Since devices can change name and other wierdness, don't hold onto a copy of device name, instead use pointer to output device. Fix a couple of leaks in error handling path as well. Signed-off-by: Stephen Hemminger Signed-off-by: Robert Olsson Signed-off-by: David S. Miller commit d5f1ce9a5e80fb315c86b036a89b1237fdf11938 Author: Stephen Hemminger Date: Sun Mar 4 16:08:08 2007 -0800 [PKTGEN]: don't use __constant_htonl() The existing htonl() macro is smart enough to do the same code as using __constant_htonl() and it looks cleaner. Signed-off-by: Stephen Hemminger Signed-off-by: Robert Olsson Signed-off-by: David S. Miller commit 5fa6fc76f55c5c42fff52ae1d57a685b9373fcdc Author: Stephen Hemminger Date: Sun Mar 4 16:07:28 2007 -0800 [PKTGEN]: use random32 Can use random32() now. Signed-off-by: Stephen Hemminger Signed-off-by: Robert Olsson Signed-off-by: David S. Miller commit 25c4e53a4c9bfe45be52821f54ec5ce957519db2 Author: Stephen Hemminger Date: Sun Mar 4 16:06:47 2007 -0800 [PKTGEN]: use pr_debug Remove private debug macro and replace with standard version Signed-off-by: Stephen Hemminger Signed-off-by: Robert Olsson Signed-off-by: David S. Miller commit fa438ccfdfd3f6db02c13b61b21454eb81cd6a13 Author: Eric Dumazet Date: Sun Mar 4 16:05:44 2007 -0800 [NET]: Keep sk_backlog near sk_lock sk_backlog is a critical field of struct sock. (known famous words) It is (ab)used in hot paths, in particular in release_sock(), tcp_recvmsg(), tcp_v4_rcv(), sk_receive_skb(). It really makes sense to place it next to sk_lock, because sk_backlog is only used after sk_lock locked (and thus memory cache line in L1 cache). This should reduce cache misses and sk_lock acquisition time. (In theory, we could only move the head pointer near sk_lock, and leaving tail far away, because 'tail' is normally not so hot, but keep it simple :) ) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit e317f6f69cb95527799d308a9421b7dc1252989a Author: Ilpo Järvinen Date: Fri Mar 2 13:34:19 2007 -0800 [TCP]: FRTO undo response falls back to ratehalving one if ECEd Undoing ssthresh is disabled in fastretrans_alert whenever FLAG_ECE is set by clearing prior_ssthresh. The clearing does not protect FRTO because FRTO operates before fastretrans_alert. Moving the clearing of prior_ssthresh earlier seems to be a suboptimal solution to the FRTO case because then FLAG_ECE will cause a second ssthresh reduction in try_to_open (the first occurred when FRTO was entered). So instead, FRTO falls back immediately to the rate halving response, which switches TCP to CA_CWR state preventing the latter reduction of ssthresh. If the first ECE arrived before the ACK after which FRTO is able to decide RTO as spurious, prior_ssthresh is already cleared. Thus no undoing for ssthresh occurs. Besides, FLAG_ECE should be set also in the following ACKs resulting in rate halving response that sees TCP is already in CA_CWR, which again prevents an extra ssthresh reduction on that round-trip. If the first ECE arrived before RTO, ssthresh has already been adapted and prior_ssthresh remains cleared on entry because TCP is in CA_CWR (the same applies also to a case where FRTO is entered more than once and ECE comes in the middle). High_seq must not be touched after tcp_enter_cwr because CWR round-trip calculation depends on it. I believe that after this patch, FRTO should be ECN-safe and even able to take advantage of synergy benefits. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit e01f9d7793be82e6c252efbd52c399d3eb65abe4 Author: Ilpo Järvinen Date: Fri Mar 2 13:27:25 2007 -0800 [TCP]: Complete icsk-to-local-variable change (in tcp_enter_cwr) A local variable for icsk was created but this change was missing. Spotted by Jarek Poplawski. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 89808060b7a71376cc2ba8092d43b2010da465b6 Author: Ilpo Järvinen Date: Tue Feb 27 10:10:55 2007 -0800 [TCP] Sysctl documentation: tcp_frto_response In addition, fixed minor things in tcp_frto sysctl. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 3cfe3baaf07c9e40a75f9a70662de56df1c246a8 Author: Ilpo Järvinen Date: Tue Feb 27 10:09:49 2007 -0800 [TCP]: Add two new spurious RTO responses to FRTO New sysctl tcp_frto_response is added to select amongst these responses: - Rate halving based; reuses CA_CWR state (default) - Very conservative; used to be the only one available (=1) - Undo cwr; undoes ssthresh and cwnd reductions (=2) The response with rate halving requires a new parameter to tcp_enter_cwr because FRTO has already reduced ssthresh and doing a second reduction there has to be prevented. In addition, to keep things nice on 80 cols screen, a local variable was added. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit c5e7af0df5d7234afd8596560d9f570cfc6c18bf Author: Ilpo Järvinen Date: Fri Feb 23 16:22:06 2007 -0800 [TCP]: Correct reordering detection change (no FRTO case) The reordering detection must work also when FRTO has not been used at all which was the original intention of mine, just the expression of the idea was flawed. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit e0ef57cc56c3c96493f9b0d6c77bb9608eeaa173 Author: David S. Miller Date: Thu Feb 22 22:52:59 2007 -0800 [TCP]: Make snd_cwnd_clamp a u32. Signed-off-by: David S. Miller commit 54287cc178cf85dbae0decec8b4dc190bff757ad Author: Eric Dumazet Date: Thu Feb 22 03:20:44 2007 -0800 [TCP]: Keep copied_seq, rcv_wup and rcv_next together. I noticed in oprofile study a cache miss in tcp_rcv_established() to read copied_seq. ffffffff80400a80 : /* tcp_rcv_established total: 4034293   2.0400 */  55493  0.0281 :ffffffff80400bc9:   mov    0x4c8(%r12),%eax copied_seq 543103  0.2746 :ffffffff80400bd1:   cmp    0x3e0(%r12),%eax   rcv_nxt     if (tp->copied_seq == tp->rcv_nxt &&         len - tcp_header_len <= tp->ucopy.len) { In this function, the cache line 0x4c0 -> 0x500 is used only for this reading 'copied_seq' field. rcv_wup and copied_seq should be next to rcv_nxt field, to lower number of active cache lines in hot paths. (tcp_rcv_established(), tcp_poll(), ...) As you suggested, I changed tcp_create_openreq_child() so that these fields are changed together, to avoid adding a new store buffer stall. Patch is 64bit friendly (no new hole because of alignment constraints) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller commit cf4c6bf83d0fa070f60b1ba8124dfe0e65fbfbcc Author: Ilpo Järvinen Date: Thu Feb 22 01:13:58 2007 -0800 [TCP]: struct *sock argument renamed: sp -> sk In general, TCP code uses "sk" for struct sock pointer. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 886236c1247ab5e2ad9c73f6e9a652e3ae3c8b07 Author: John Heffner Date: Sun Mar 25 19:21:45 2007 -0700 [TCP]: Add RFC3742 Limited Slow-Start, controlled by variable sysctl_tcp_max_ssthresh. Signed-off-by: John Heffner Signed-off-by: David S. Miller commit 5ef814753eb810d900fbd77af7c87f6d04f0e551 Author: Angelo P. Castellani Date: Thu Feb 22 00:23:05 2007 -0800 [TCP] YeAH-TCP: algorithm implementation YeAH-TCP is a sender-side high-speed enabled TCP congestion control algorithm, which uses a mixed loss/delay approach to compute the congestion window. It's design goals target high efficiency, internal, RTT and Reno fairness, resilience to link loss while keeping network elements load as low as possible. For further details look here: http://wil.cs.caltech.edu/pfldnet2007/paper/YeAH_TCP.pdf Signed-off-by: Angelo P. Castellani Signed-off-by: David S. Miller commit 127af0c44fc916908abd145914d65b9fe598bcd7 Author: Ilpo Järvinen Date: Wed Feb 21 23:16:38 2007 -0800 [TCP] FRTO: Sysctl documentation for SACK enhanced version The description is overly verbose to avoid ambiguity between "SACK enabled" and "SACK enhanced FRTO" Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 4dc2665e3634d720a62bd27128fc8781fcdad2dc Author: Ilpo Järvinen Date: Wed Feb 21 23:16:11 2007 -0800 [TCP]: SACK enhanced FRTO Implements the SACK-enhanced FRTO given in RFC4138 using the variant given in Appendix B. RFC4138, Appendix B: "This means that in order to declare timeout spurious, the TCP sender must receive an acknowledgment for non-retransmitted segment between SND.UNA and RecoveryPoint in algorithm step 3. RecoveryPoint is defined in conservative SACK-recovery algorithm [RFC3517]" The basic version of the FRTO algorithm can still be used also when SACK is enabled. To enabled SACK-enhanced version, tcp_frto sysctl is set to 2. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 288035f915686a9a9e85e0358c5392bb5d7ae58d Author: Ilpo Järvinen Date: Wed Feb 21 23:14:42 2007 -0800 [TCP]: Prevent reordering adjustments during FRTO To be honest, I'm not too sure how the reord stuff works in the first place but this seems necessary. When FRTO has been active, the one and only retransmission could be unnecessary but the state and sending order might not be what the sacktag code expects it to be (to work correctly). Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 66e93e45c09affa407750cc06398492e8b897848 Author: Ilpo Järvinen Date: Wed Feb 21 23:13:47 2007 -0800 [TCP] FRTO: Fake cwnd for ssthresh callback TCP without FRTO would be in Loss state with small cwnd. FRTO, however, leaves cwnd (typically) to a larger value which causes ssthresh to become too large in case RTO is triggered again compared to what conventional recovery would do. Because consecutive RTOs result in only a single ssthresh reduction, RTO+cumulative ACK+RTO pattern is required to trigger this event. A large comment is included for congestion control module writers trying to figure out what CA_EVENT_FRTO handler should do because there exists a remote possibility of incompatibility between FRTO and module defined ssthresh functions. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit d1a54c6a0a3f9c2c4ef71982d89b8571bd9eaa51 Author: Ilpo Järvinen Date: Wed Feb 21 23:11:57 2007 -0800 [TCP] FRTO: Reverse RETRANS bit clearing logic Previously RETRANS bits were cleared on the entry to FRTO. We postpone that into tcp_enter_frto_loss, which is really the place were the clearing should be done anyway. This allows simplification of the logic from a clearing loop to the head skb clearing only. Besides, the other changes made in the previous patches to tcp_use_frto made it impossible for the non-SACKed FRTO to be entered if other than the head has been rexmitted. With SACK-enhanced FRTO (and Appendix B), however, there can be a number retransmissions in flight when RTO expires (same thing could happen before this patchset also with non-SACK FRTO). To not introduce any jumpiness into the packet counting during FRTO, instead of clearing RETRANS bits from skbs during entry, do it later on. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 46d0de4ed92650b95f27acae09914996bbe624e7 Author: Ilpo Järvinen Date: Wed Feb 21 23:10:39 2007 -0800 [TCP] FRTO: Entry is allowed only during (New)Reno like recovery This interpretation comes from RFC4138: "If the sender implements some loss recovery algorithm other than Reno or NewReno [FHG04], the F-RTO algorithm SHOULD NOT be entered when earlier fast recovery is underway." I think the RFC means to say (especially in the light of Appendix B) that ...recovery is underway (not just fast recovery) or was underway when it was interrupted by an earlier (F-)RTO that hasn't yet been resolved (snd_una has not advanced enough). Thus, my interpretation is that whenever TCP has ever retransmitted other than head, basic version cannot be used because then the order assumptions which are used as FRTO basis do not hold. NewReno has only the head segment retransmitted at a time. Therefore, walk up to the segment that has not been SACKed, if that segment is not retransmitted nor anything before it, we know for sure, that nothing after the non-SACKed segment should be either. This assumption is valid because TCPCB_EVER_RETRANS does not leave holes but each non-SACKed segment is rexmitted in-order. Check for retrans_out > 1 avoids more expensive walk through the skb list, as we can know the result beforehand: F-RTO will not be allowed. SACKed skb can turn into non-SACked only in the extremely rare case of SACK reneging, in this case we might fail to detect retransmissions if there were them for any other than head. To get rid of that feature, whole rexmit queue would have to be walked (always) or FRTO should be prevented when SACK reneging happens. Of course RTO should still trigger after reneging which makes this issue even less likely to show up. And as long as the response is as conservative as it's now, nothing bad happens even then. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 7c9a4a5b67926dd186d427bc5b9fce6ccbde154c Author: Ilpo Järvinen Date: Wed Feb 21 23:08:34 2007 -0800 [TCP]: Prevent unrelated cwnd adjustment while using FRTO FRTO controls cwnd when it still processes the ACK input or it has just reverted back to conventional RTO recovery; the normal rules apply when FRTO has reverted to standard congestion control. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 94d0ea7786714d78d7cb73144bb850254dd0bb78 Author: Ilpo Järvinen Date: Wed Feb 21 23:07:27 2007 -0800 [TCP] FRTO: frto_counter modulo-op converted to two assignments Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 52c63f1e86ebb18ef4b710b5b647e552a041e5ca Author: Ilpo Järvinen Date: Wed Feb 21 23:06:52 2007 -0800 [TCP]: Don't enter to fast recovery while using FRTO Because TCP is not in Loss state during FRTO recovery, fast recovery could be triggered by accident. Non-SACK FRTO is more robust than not yet included SACK-enhanced version (that can receiver high number of duplicate ACKs with SACK blocks during FRTO), at least with unidirectional transfers, but under extraordinary patterns fast recovery can be incorrectly triggered, e.g., Data loss+ACK losses => cumulative ACK with enough SACK blocks to meet sacked_out >= dupthresh condition). Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit aa8b6a7ad147dfbaaf10368ff15df9418b670d8b Author: Ilpo Järvinen Date: Wed Feb 21 23:06:03 2007 -0800 [TCP] FRTO: Response should reset also snd_cwnd_cnt Since purpose is to reduce CWND, we prevent immediate growth. This is not a major issue nor is "the correct way" specified anywhere. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 95c4922bf9330eb2c71b752359dd89c4e166f3c5 Author: Ilpo Järvinen Date: Wed Feb 21 23:05:18 2007 -0800 [TCP] FRTO: fixes fallback to conventional recovery The FRTO detection did not care how ACK pattern affects to cwnd calculation of the conventional recovery. This caused incorrect setting of cwnd when the fallback becames necessary. The knowledge tcp_process_frto() has about the incoming ACK is now passed on to tcp_enter_frto_loss() in allowed_segments parameter that gives the number of segments that must be added to packets-in-flight while calculating the new cwnd. Instead of snd_una we use FLAG_DATA_ACKED in duplicate ACK detection because RFC4138 states (in Section 2.2): If the first acknowledgment after the RTO retransmission does not acknowledge all of the data that was retransmitted in step 1, the TCP sender reverts to the conventional RTO recovery. Otherwise, a malicious receiver acknowledging partial segments could cause the sender to declare the timeout spurious in a case where data was lost. If the next ACK after RTO is duplicate, we do not retransmit anything, which is equal to what conservative conventional recovery does in such case. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 6408d206c7484615ecae54bf6474a02c94e9e862 Author: Ilpo Järvinen Date: Wed Feb 21 23:04:11 2007 -0800 [TCP] FRTO: Ignore some uninteresting ACKs Handles RFC4138 shortcoming (in step 2); it should also have case c) which ignores ACKs that are not duplicates nor advance window (opposite dir data, winupdate). Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 7b0eb22b1d3b049306813a4aaa52966650f7491c Author: Ilpo Järvinen Date: Wed Feb 21 23:03:35 2007 -0800 [TCP] FRTO: Use Disorder state during operation instead of Open Retransmission counter assumptions are to be changed. Forcing reason to do this exist: Using sysctl in check would be racy as soon as FRTO starts to ignore some ACKs (doing that in the following patches). Userspace may disable it at any moment giving nice oops if timing is right. frto_counter would be inaccessible from userspace, but with SACK enhanced FRTO retrans_out can include other than head, and possibly leaving it non-zero after spurious RTO, boom again. Luckily, solution seems rather simple: never go directly to Open state but use Disorder instead. This does not really change much, since TCP could anyway change its state to Disorder during FRTO using path tcp_fastretrans_alert -> tcp_try_to_open (e.g., when a SACK block makes ACK dubious). Besides, Disorder seems to be the state where TCP should be if not recovering (in Recovery or Loss state) while having some retransmissions in-flight (see tcp_try_to_open), which is exactly what happens with FRTO. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 7487c48c4fd15d1e2542be1183b783562cfe10bc Author: Ilpo Järvinen Date: Wed Feb 21 23:02:30 2007 -0800 [TCP] FRTO: Consecutive RTOs keep prior_ssthresh and ssthresh In case a latency spike causes more than one RTO, the later should not cause the already reduced ssthresh to propagate into the prior_ssthresh since FRTO declares all such RTOs spurious at once or none of them. In treating of ssthresh, we mimic what tcp_enter_loss() does. The previous state (in frto_counter) must be available until we have checked it in tcp_enter_frto(), and also ACK information flag in process_frto(). Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 30935cf4f915c3178ce63331d6ff4c82163e26af Author: Ilpo Järvinen Date: Wed Feb 21 23:01:36 2007 -0800 [TCP] FRTO: Comment cleanup & improvement Moved comments out from the body of process_frto() to the head (preferred way; see Documentation/CodingStyle). Bonus: it's much easier to read in this compacted form. FRTO algorithm and implementation is described in greater detail. For interested reader, more information is available in RFC4138. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit bdaae17da81db79b9aa4dfbf43305cfeef64f6a8 Author: Ilpo Järvinen Date: Wed Feb 21 22:59:58 2007 -0800 [TCP] FRTO: Moved tcp_use_frto from tcp.h to tcp_input.c In addition, removed inline. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 9ead9a1d385ae2c52a6dcf2828d84ce66be04fc2 Author: Ilpo Järvinen Date: Wed Feb 21 22:56:19 2007 -0800 [TCP] FRTO: Separated response from FRTO detection algorithm FRTO spurious RTO detection algorithm (RFC4138) does not include response to a detected spurious RTO but can use different response algorithms. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit 522e7548a9bd40305df41c0beae69448b7620d6b Author: Ilpo Järvinen Date: Wed Feb 21 22:54:52 2007 -0800 [TCP] FRTO: Incorrectly clears TCPCB_EVER_RETRANS bit FRTO was slightly too brave... Should only clear TCPCB_SACKED_RETRANS bit. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller commit afd3810d9b6b0d446a34e1d4e94f0cc020b00a14 Author: Zachary Amsden Date: Wed Apr 25 15:32:23 2007 -0400 ACPI: Remove a warning about unused variable in !CONFIG_ACPI compilation. Signed-off-by: Zachary Amsden Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit b2983f10f87423fab92326bbe1e92e2256573d4f Author: Thierry Vignaud Date: Wed Apr 25 15:31:30 2007 -0400 ACPI: prevent ACPI quirk warning mass spamming in logs The following patch prevent this warning to be displayed again & again (eg: nine times on my NForce2 motherboard) and thus improve signal to noise ratio in logs. The ATI quirk below probably needs a similar "fix" but I don't have the hardware to test. Btw arch/x86_64/kernel/early-quirks.c::nvidia_bugs() would probably need to be synced (but I don't have an x86_64 NVidia motherboard to boot test it). Still it shows the usefullity of the recent x86 merge thread. [akpm@linux-foundation.org: cleanup] Signed-off-by: Thierry Vignaud Signed-off-by: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 8aa55591bfea25c441117e82711cbfd7c274250a Author: David Brownell Date: Wed Apr 25 15:20:10 2007 -0400 ACPI: make /proc/acpi/wakeup more useful This updates /proc/acpi/wakeup to be more informative, primarily by showing the sysfs node associated with each wakeup-enabled device. Example: Device S-state Status Sysfs node PCI0 S4 disabled no-bus:pci0000:00 PS2M S4 disabled pnp:00:05 PS2K S4 disabled pnp:00:06 UAR1 S4 disabled pnp:00:08 USB1 S3 disabled pci:0000:00:03.0 USB2 S3 disabled pci:0000:00:03.1 USB3 S3 disabled USB4 S3 disabled pci:0000:00:03.3 S139 S4 disabled LAN S4 disabled pci:0000:00:04.0 MDM S4 disabled AUD S4 disabled pci:0000:00:02.7 SLPB S4 *enabled Eventually this file should be removed, but until then it's almost the only way we have to tell how the relevant ACPI tables are broken (and cope). In that example, two devices don't actually exist (USB3, S139), one can't issue wakeup events (PCI0), and two seem harmlessly (?) confused (MDM and AUD are the same PCI device, but it's the _modem_ that does wake-on-ring). In particular, we need to be sure driver model nodes are properly hooked up before we can get rid of this ACPI-only interface for wakeup events. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit cf6c6045a06aed2ccd8ebd0a3128ce0f2f8a11aa Author: Borislav Petkov Date: Wed Apr 25 14:29:50 2007 -0400 ACPI: word-smith kconfig help Signed-off-by: Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 8ce8e2f99a973c39c4aeddbe0966038196a8e71a Author: Daniel Walker Date: Wed Apr 25 14:27:06 2007 -0400 ACPI: correct pathname in comment Signed-off-by: Daniel Walker Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 0c0e8921018dbb4fe189a1034f80ac32553bc7bc Author: Bjorn Helgaas Date: Wed Apr 25 14:20:58 2007 -0400 ACPI: use _STA bit names rather than 0x0F Be explicit about what "device->status = 0x0F" really means. syntax only. Signed-off-by: Bjorn Helgaas Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit a0bd4ac498acfe60f7533d15ba60d5efdd4e9ca5 Author: Bjorn Helgaas Date: Wed Apr 25 14:17:39 2007 -0400 ACPI: Remove duplicate definitions for _STA bits No need to duplicate the existing definitions in include/acpi/actypes.h. syntax only -- no functional change. Signed-off-by: Bjorn Helgaas Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit d8938801d10945ac2fbe0f41ded43f6276660a17 Author: Ray Lee Date: Wed Apr 25 14:12:00 2007 -0400 ACPI: remove duplicate include Thomas's patch for including for x86 UP builds came into Linus's tree from two different directions, both of which were merged. This reverts the latter, yanking out the duplicate #include and comment. Signed-off-by: Ray Lee Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 61c4b23770d1b0cef7c06a23378ab544eb0c64b4 Author: David Woodhouse Date: Wed Apr 25 17:04:23 2007 +0100 [JFFS2] Handle inodes with only a single metadata node with non-zero isize This should never happen unless there's corruption on the medium and the actual data nodes go missing. But the failure mode (an oops when we assume the fragtree isn't empty and go looking for its last node) isn't useful. Signed-off-by: David Woodhouse commit c00c310eac04a28d2143368ae988716792ed53ce Author: David Woodhouse Date: Wed Apr 25 14:16:47 2007 +0100 [JFFS2] Tidy up licensing/copyright boilerplate. In particular, remove the bit in the LICENCE file about contacting Red Hat for alternative arrangements. Their errant IS department broke that arrangement a long time ago -- the policy of collecting copyright assignments from contributors came to an end when the plug was pulled on the servers hosting the project, without notice or reason. We do still dual-license it for use with eCos, with the GPL+exception licence approved by the FSF as being GPL-compatible. It's just that nobody has the right to license it differently. Signed-off-by: David Woodhouse commit c36c46d53b2f95bfcbe992cfb541a78ab92310a4 Author: Adrian Hunter Date: Fri Mar 23 17:16:22 2007 +0900 [MTD] [OneNAND] Exit loop only when column start with 0 The JFFS2 requests OOB function from column 0. But the oobtest in nand-tests doesn't. So we only exit loop only when column start with 0. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse commit ad286343665cad2135792bcf53117d8344f64b03 Author: Kyungmin Park Date: Fri Mar 23 10:19:52 2007 +0900 [MTD] [OneNAND] Fix access the past of the real oobfree array Here it's not the case: all the entries are occupied by OOB chunks. Therefore, once we get into a loop like for (free = this->ecclayout->oobfree; free->length; ++free) { } we might end up scanning past the real oobfree array. Probably the best way out, as the same thing might happen for common NAND as well, is to check index against MTD_MAX_OOBFREE_ENTRIES. Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse commit c19df27ec7f8b184db867c4490d87f997fdc6e4e Author: Kyungmin Park Date: Wed Apr 25 11:05:48 2007 +0100 [MTD] [OneNAND] Update Samsung OneNAND official URL Update Samsung OneNAND official URL. Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse commit 7d5a015eece8be9186d3613d595643a520555e33 Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:20 2007 -0300 ACPI: thinkpad-acpi: update brightness sysfs interface support Update the brightness sysfs interface (done through the backlight class) to be in line with the rest of the thinkpad-acpi driver. This renames the incorrect, un-obvious, and clash-prone name of "ibm" for the backlight device to a much more fitting and descriptive "thinkpad_screen". This is something I wanted to do for quite a while... Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit b616004c70dd7f60a1477c3e9d6fddd00ee1fa37 Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:19 2007 -0300 ACPI: thinkpad-acpi: add sysfs support to the cmos command subdriver Add sysfs attributes to send ThinkPad CMOS commands. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit eaa7571b2d1a08873e4bdd8e6db3431df61cd9ad Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:18 2007 -0300 ACPI: thinkpad-acpi: add a safety net for TPEC fan control mode The Linux ThinkPad community is not positive that all ThinkPads that do HFSP EC fan control do implement full-speed and auto modes, some of the earlier ones supporting HFSP might not. If the EC ignores the AUTO or FULL-SPEED bits, it will pay attention to the lower three bits that set the fan level. And as thinkpad-acpi was leaving these set to zero, it would stop(!) the fan, which is Not A Good Thing. So, as a safety net, we now make sure to also set the fan level part of the HFSP register to speed 7 for full-speed, and a minimum of speed 4 for auto mode. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit fe98a52ce7540fb3a19d57488a08864110cf4d5c Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:17 2007 -0300 ACPI: thinkpad-acpi: add sysfs support to fan subdriver Export sysfs attributes to monitor and control the internal thinkpad fan (some thinkpads have more than one fan, but thinkpad-acpi doesn't support the second fan yet). The sysfs interface follows the hwmon design guide for fan devices. Also, fix some stray "thermal" files in the fan procfs description that have been there forever, and officially support "full-speed" as the name for the PWM-disabled state of the fan controller to keep it in line with the hwmon interface. It is much better a name for that mode than the unobvious "disengaged" anyway. Change the procfs interface to also accept full-speed as a fan level, but still report it as disengaged for backwards compatibility. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 2c37aa4e22dd55070c608290c5031f2ee93e69ce Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:16 2007 -0300 ACPI: thinkpad-acpi: add sysfs support to the thermal subdriver Export thinkpad thermal sensors to sysfs, following the hwmon specification for thermal monitoring sensors. ThinkPad thermal monitoring is done by the EC. Sensors can show up or disappear at runtime when they are inside hotswappable hardware, such as batteries. Sensors that are not available return -ENXIO when accessed. Up to 16 thermal sensors are supported on new firmware (but nobody has reported a ThinkPad with more than 12 sensors so far), and 8 sensors are supported on older firmware. Thermal sensor mapping is model-specific. Precision varies, it is 1 degree Celcius on new ThinkPads, but higher on some older models. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 40ca9fdf8aa7d929e2b8939be1e6380d107381e1 Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:15 2007 -0300 ACPI: thinkpad-acpi: protect fan and hotkey data structures Add proper mutex locking to some data structures access subject to races due to concurrent access of driver functions on the hotkey and fan subdrivers. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 7252374a39d794879f5e47bcfa0a16e7599b27b5 Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:14 2007 -0300 ACPI: thinkpad-acpi: add infrastructure for the sysfs device attributes Add infrastructure to deal with sysfs attributes and grouping, and helpers for common sysfs parsing. Switch driver attributes to use them. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 176750d68801bfa4a88d1cf54174aa0347d7e5d8 Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:13 2007 -0300 ACPI: thinkpad-acpi: driver sysfs conversion Add the sysfs attributes for the platform driver. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 54ae15014c306b3d7ad32c996fea9a5ac8560b60 Author: Henrique de Moraes Holschuh Date: Tue Apr 24 11:48:12 2007 -0300 ACPI: thinkpad-acpi: register with the device model Register thinkpad-acpi platform driver and platform device for the device model. Also register the platform device with the hwmon class. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit f989106cac719f8fe91da7734e73b3ca09146ecc Author: Zhang Rui Date: Tue Apr 24 13:53:22 2007 +0800 ACPI: Improve acpi debug documentation Now we use acpi.debug_level and acpi.debug_layer as kernel boot parameters instead of acpi_dbg_level and acpi_dbg_layer. Thanks to Andi Kleen for pointing it out. Signed-off-by: Zhang Rui Signed-off-by: Len Brown commit f8993aff8b4de0317c6e081802ca5c86c449fef2 Author: Shaohua Li Date: Wed Apr 25 11:05:12 2007 +0800 ACPI: Disable MSI on request of FADT The ACPI spec defines the bit and Microsoft uses it, so Linux must use it too. Signed-off-by: Shaohua Li Signed-off-by: Len Brown commit 1912ffbb88efe872eb8fa8113dfb3cb0b7238764 Author: Joachim Fenkes Date: Mon Apr 23 18:20:27 2007 +0200 IB: Set class_dev->dev in core for nice device symlink All RDMA drivers except ehca set class_dev->dev to their dma_device value (ehca leaves this unset). dma_device is the only value that makes any sense, so move this assignment to core/sysfs.c. This reduce the duplicated code in the rest of the drivers and gives ehca a nice /sys/class/infiniband/ehcaX/device symlink. Signed-off-by: Joachim Fenkes Signed-off-by: Roland Dreier commit c4ed790dfd4b2182c76e0fcd79d4aa85ab02eccf Author: Joachim Fenkes Date: Tue Apr 24 17:44:31 2007 +0200 IB/ehca: Implement modify_port Add "Modify Port" verb support to eHCA driver. The IB communication manager needs this to set the IsCM port capability bit when initializing. Signed-off-by: Joachim Fenkes Signed-off-by: Roland Dreier commit bd8031b49a9b05933fb1ec1c36620ed4e1e67793 Author: Hal Rosenstock Date: Tue Apr 24 21:30:38 2007 -0700 IB/umad: Clarify documentation of transaction ID Signed-off-by: Hal Rosenstock Signed-off-by: Roland Dreier commit 37aebbde7023d75bf09fbadb6796276d0a65a068 Author: Roland Dreier Date: Tue Apr 24 21:30:37 2007 -0700 IPoIB/cm: spin_lock_irqsave() -> spin_lock_irq() replacements There are quite a few places in ipoib_cm.c where we know IRQs are enabled because we do something that sleeps in the same function, so we can convert several occurrences of spin_lock_irqsave() to a plain spin_lock_irq(). This cleans up the source a little and makes the code smaller too: add/remove: 0/0 grow/shrink: 1/5 up/down: 3/-51 (-48) function old new delta ipoib_cm_tx_reap 403 406 +3 ipoib_cm_stale_task 146 145 -1 ipoib_cm_dev_stop 173 172 -1 ipoib_cm_tx_handler 964 956 -8 ipoib_cm_rx_handler 956 937 -19 ipoib_cm_skb_reap 212 190 -22 Signed-off-by: Roland Dreier commit 0dec4c8bc6ed62a65b61594aa754e21270423796 Author: Joakim Tjernlund Date: Sat Mar 10 17:08:44 2007 +0100 [JFFS2] Better fix for all-zero node headers No need to check for all-zero header since the header cannot be zero due to other checks. Replace the all-zero header check in readinode.c with a check for the magic word. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse commit df8e96f39103adf5a13332d784040a2c62667243 Author: David Woodhouse Date: Wed Apr 25 03:23:42 2007 +0100 [JFFS2] Improve read_inode memory usage, v2. We originally used to read every node and allocate a jffs2_tmp_dnode_info structure for each, before processing them in (reverse) version order and discarding the ones which are obsoleted by later nodes. With huge logfiles, this behaviour caused memory problems. For example, a file involved in OLPC trac #1292 has 1822391 nodes, and would cause the XO machine to run out of memory during the first stage of read_inode(). Instead of just inserting nodes into a tree in version order as we find them, we now put them into a tree in order of their offset within the file, which allows us to immediately discard nodes which are completely obsoleted. We don't use a full tree with 'fragments' pointing to the real data structure, as we do in the normal fragtree. We sort only on the start address, and add an 'overlapped' flag to the tmp_dnode_info to indicate that the node in question is (partially) overlapped by another. When the scan is complete, we start at the end of the file, adding each node to a real fragtree as before. Where the node is non-overlapped, we just add it (it doesn't matter that it's not the latest version; there is no overlap). When the node at the end of the tree _is_ overlapped, we sort it and all its overlapping nodes into version order and then add them to the fragtree in that order. This 'early discard' reduces the peak allocation of tmp_dnode_info structures from 1.8M to a mere 62872 (3.5%) in the degenerate case referenced above. This version of the patch also correctly rememembers the highest node version# seen for an inode when it's scanned. Signed-off-by: David Woodhouse commit de493d47d8b4738827d8914a4dc94058c58f4249 Author: Hal Rosenstock Date: Mon Apr 2 11:24:07 2007 -0400 IB/mad: Change SMI to use enums rather than magic return codes Clarify code by changing return values from SMI functions to named enum values instead of magic 0/1 values. Signed-off-by: Hal Rosenstock Signed-off-by: Roland Dreier commit aeba84a9251968a51fc6faae846518aac4e77565 Author: Sean Hefty Date: Thu Apr 5 11:49:21 2007 -0700 IB/umad: Implement GRH handling for sent/received MADs We need to set the SGID index for routed MADs and pass received GRH information to userspace when a MAD is received. Signed-off-by: Sean Hefty commit 46f1b3d7aff99ef4c1e729e023b9c8ee51de5973 Author: Sean Hefty Date: Thu Apr 5 11:50:11 2007 -0700 IB/ipoib: Use ib_init_ah_from_path to initialize ah_attr To support destinations that are not on the local IB subnet, IPoIB should include the GRH information when constructing an address handle. Using the existing ib_init_ah_from_path() call will do this for us. Signed-off-by: Sean Hefty commit d0e7bb141837db620f24406ca8b4667424138d42 Author: Sean Hefty Date: Thu Apr 5 10:51:10 2007 -0700 IB/sa: Set src_path_bits correctly in ib_init_ah_from_path() src_path_bits needs to mask off the base LID value. Signed-off-by: Sean Hefty commit 9d41b7fdeadb76bd4d06c16803daffd9fcf8dc7f Author: Sean Hefty Date: Thu Apr 5 10:51:05 2007 -0700 IB/ucm: Simplify ib_ucm_event() Use wait_event_interruptible() instead of a more complicated open-coded equivalent. Signed-off-by: Sean Hefty commit d92f76448c1a3e40ff3df96a653ecd83aeac6ee7 Author: Sean Hefty Date: Thu Apr 5 10:49:51 2007 -0700 RDMA/ucma: Simplify ucma_get_event() Use wait_event_interruptible() instead of a more complicated open-coded equivalent. Signed-off-by: Sean Hefty commit 30c00986f3a610cdcee2602b8254c3ffa6cddc04 Author: Roland Dreier Date: Tue Apr 24 16:31:11 2007 -0700 IB/mthca: Simplify CQ cleaning in mthca_free_qp() mthca_free_qp() already has local variables to hold the QP's send_cq and recv_cq, so we can slightly clean up the calls to mthca_cq_clean() by using those local variables instead of expressions like to_mcq(qp->ibqp.send_cq). Also, by cleaning the recv_cq first, we can avoid worrying about whether the QP is attached to an SRQ for the second call, because we would only clean send_cq if send_cq is not equal to recv_cq, and that means send_cq cannot have any receive completions from the QP being destroyed. All this work even improves the generated code a bit: add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-5 (-5) function old new delta mthca_free_qp 510 505 -5 Signed-off-by: Roland Dreier commit 532c3b581725e2c6480a20c845fff920690286f1 Author: Roland Dreier Date: Tue Apr 24 16:31:04 2007 -0700 IB/mthca: Fix mthca_write_mtt() on HCAs with hidden memory Commit b2875d4c ("IB/mthca: Always fill MTTs from CPU") causes a crash in mthca_write_mtt() with non-memfree HCAs that have their memory hidden (that is, have only two PCI BARs instead of having a third BAR that allows access to the RAM attached to the HCA) on 64-bit architectures. This is because the commit just before, c20e20ab ("IB/mthca: Merge MR and FMR space on 64-bit systems") makes dev->mr_table.fmr_mtt_buddy equal to &dev->mr_table.mtt_buddy and hence mthca_write_mtt() tries to write directly into the HCA's MTT table. However, since that table is in the HCA's memory, this is impossible without the PCI BAR that gives access to that memory. This causes a crash because mthca_tavor_write_mtt_seg() basically tries to dereference some offset of a NULL pointer. Fix this by adding a test of MTHCA_FLAG_FMR in mthca_write_mtt() so that we always use the WRITE_MTT firmware command rather than writing directly if FMRs are not enabled. Signed-off-by: Roland Dreier commit c78f830547087aa4143affd3404a854995603544 Author: Alan Cox Date: Mon Apr 23 14:56:01 2007 +0100 [POWERPC] via-pmu: Switch to ref counting PCI API Signed-off-by: Alan Cox Signed-off-by: Paul Mackerras commit ab4627683a0e4430cb5acb1cd896f08ec5dad90c Author: Alan Cox Date: Mon Apr 23 14:47:59 2007 +0100 [POWERPC] pci_32.c: Switch to ref counting PCI API pci_find_slot isn't hot-plug safe. Move this code to the pci hotplug safe equivalent and hold a refcount properly while doing make_one_node_map. Signed-off-by: Alan Cox Signed-off-by: Paul Mackerras commit 37f01d64d83705f82bb06eac8134acc8ef665565 Author: David Gibson Date: Tue Apr 24 15:05:18 2007 +1000 [POWERPC] Abolish PHYS_FMT macro from arch/powerpc 32-bit powerpc systems define a macro, PHYS_FMT, giving a printf format string fragment for displaying physical addresses, since most 32-bit powerpc platforms use 32-bit physical addresses but a few use 64-bit physical addresses. This macro is used in exactly one place, a rare error message, where we can solve the problem more simply by just unconditionally casting the address up to 64-bit quantity before formatting it. This patch does so, meaning that as we bring MMU definitions from asm-ppc over to asm-powerpc, cleaning them up in the process, we don't need to implement this ugly macro (which additionally has a very bad name for something global). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 30686ba6d56858657829d3eb524ed73e5dc98d2b Author: Stephen Rothwell Date: Tue Apr 24 13:53:04 2007 +1000 [POWERPC] Remove old interface find_devices Replace uses with of_find_node_by_name and for_each_node_by_name. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 1658ab66781d918f604c6069c5cf9a94b6f52f84 Author: Stephen Rothwell Date: Tue Apr 24 13:51:59 2007 +1000 [POWERPC] Remove old interface find_type_devices Replaced by of_find_node_by_type. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 8c8dc322486d5394dc981bef9276dd0ce6c8d1ce Author: Stephen Rothwell Date: Tue Apr 24 13:50:55 2007 +1000 [POWERPC] Remove old interface find_path_device Replaced by of_find_node_by_path. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 112466b4d0036b3244509d01dbbf3c8caec52a23 Author: Stephen Rothwell Date: Tue Apr 24 13:49:47 2007 +1000 [POWERPC] Remove find_all_nodes This old interface has no more users. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 4bf56e1725a298fb430977cf143ad3a36c91b46a Author: Stephen Rothwell Date: Tue Apr 24 13:48:41 2007 +1000 [POWERPC] Remove find_compatible_devices This is an old interface and is replaced by of_find_compatible_node. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 621023072524fc0155ed16490255e1ea3aa11585 Author: David Gibson Date: Tue Apr 24 13:09:12 2007 +1000 [POWERPC] Cleanup and fix breakage in tlbflush.h BenH's commit a741e67969577163a4cfc78d7fd2753219087ef1 in powerpc.git, although (AFAICT) only intended to affect ppc64, also has side-effects which break 44x. I think 40x, 8xx and Freescale Book E are also affected, though I haven't tested them. The problem lies in unconditionally removing flush_tlb_pending() from the versions of flush_tlb_mm(), flush_tlb_range() and flush_tlb_kernel_range() used on ppc64 - which are also used the embedded platforms mentioned above. The patch below cleans up the convoluted #ifdef logic in tlbflush.h, in the process restoring the necessary flushes for the software TLB platforms. There are three sets of definitions for the flushing hooks: the software TLB versions (revised to avoid using names which appear to related to TLB batching), the 32-bit hash based versions (external functions) amd the 64-bit hash based versions (which implement batching). It also moves the declaration of update_mmu_cache() to always be in tlbflush.h (previously it was in tlbflush.h except for PPC64, where it was in pgtable.h). Booted on Ebony (440GP) and compiled for 64-bit and 32-bit multiplatform. Signed-off-by: David Gibson Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 687304014f7ca8e2fbb3feaefef356b4a0da65ad Author: Olof Johansson Date: Tue Apr 24 01:11:55 2007 +1000 [POWERPC] Save trap number in bad_stack Save the trap number in the case of getting a bad stack in an exception handler. It is sometimes useful to know what exception it was that caused this to happen. Without this, no trap number is reported. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 6cfef5b27e49e826125f12637ee0d7210a896044 Author: Michael Ellerman Date: Mon Apr 23 18:47:08 2007 +1000 [POWERPC] Rename MPIC_BROKEN_U3 to MPIC_U3_HT_IRQS Rename MPIC_BROKEN_U3 to something a little more descriptive. Its effect is to enable support for HT irqs behind the PCI-X/HT bridge on U3/U4 (aka. CPC9x5) parts. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras commit e3f64788d32f97b31709a45911dabaa37f933359 Author: Grant Likely Date: Sun Apr 22 07:24:04 2007 +1000 [POWERPC] Fix comment typo in Kurobox device tree Signed-off-by: Grant Likely Signed-off-by: Paul Mackerras commit 4ca478e6066ce57f7cc856af36aaf1a2d64417cb Author: Geert Uytterhoeven Date: Wed Apr 18 19:24:12 2007 +1000 [POWERPC] bootwrapper: Use `unsigned long' for malloc sizes Use `unsigned long' for malloc sizes, to match common practice and types used by most callers and callees. Also use `unsigned long' for integers representing pointers in simple_alloc. Signed-off-by: Geert Uytterhoeven Signed-off-by: Paul Mackerras commit e58923ed14370e0facc5eb2c3923216adc3bf260 Author: David Gibson Date: Wed Apr 18 16:36:26 2007 +1000 [POWERPC] Add arch/powerpc driver for UIC, PPC4xx interrupt controller This patch adds a driver to arch/powerpc/sysdev for the UIC, the on-chip interrupt controller from IBM/AMCC 4xx chips. It uses the new irq host mapping infrastructure. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit f65573314e4e2d7deacd7ed101c004148e156296 Author: David Gibson Date: Wed Apr 18 16:36:26 2007 +1000 [POWERPC] Re-organize Kconfig code for 4xx in arch/powerpc Now that we always take a device tree in arch/powerpc, there's no good reason not to allow a single kernel to support multiple embedded 4xx boards - the correct platform code can be selected based on the device tree information. Therefore, this patch re-arranges the 4xx Kconfig code to allow this. In addition we: - use "select" instead of depends to configure the correct config options for specific 4xx CPUs and workarounds, which makes the information about specific boards and CPUs less scattered. - Some old, unused (in arch/powerpc) config options are removed: WANT_EARLY_SERIAL, IBM_OCP, etc. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit e6349a958b3577da6e5c5eacda85c07f9a364cb5 Author: Ananth N Mavinakayanahalli Date: Wed Apr 18 15:57:51 2007 +1000 [POWERPC] kprobes: Eliminate sstep exception if instruction can be emulated For cases when probes are placed on instructions that can be emulated, don't take the single-step exception. Signed-off-by: Ananth N Mavinakayanahalli Signed-off-by: Paul Mackerras commit 6888199f7fe5ea496f48bb6de67b9b7f05b8071b Author: Ananth N Mavinakayanahalli Date: Wed Apr 18 15:56:38 2007 +1000 [POWERPC] Emulate more instructions in software Emulate a few more instructions in software - especially useful during singlestepping (xmon/kprobes). Instructions emulated with this patch are mfcr/mtcr rX, mfxer/mtxer rX, mflr/mtlr rX, mfctr/mtctr rX and mr rA,rB. Signed-off-by: Ananth N Mavinakayanahalli Signed-off-by: Paul Mackerras commit 5cc5133a29fab993f0b40c03e975bc5458ece507 Author: Scott Wood Date: Tue Apr 17 09:25:55 2007 +1000 [POWERPC] bootwrapper: cuboot for 83xx This adds cuboot support for MPC83xx platforms. A device tree used with this must have linux,stdout-path in /chosen and linux,network-index in any network device nodes that need mac addresses assigned. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit 9b1c59e256e91aaf2a6543b32afc3658d8f96d4b Author: Scott Wood Date: Tue Apr 17 09:25:53 2007 +1000 [POWERPC] bootwrapper: Add ppcboot.h This file describes the bd_t struct, which is used by old versions of U-boot to pass information to the kernel. Platform code that needs to interoperate with such firmware can use this; it should not be used for anything new. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit 0fdd717ed4d9c82f3f766007b3f5da656649f3aa Author: Scott Wood Date: Tue Apr 17 09:25:50 2007 +1000 [POWERPC] bootwrapper: Add a cuboot platform and a cuImage target The cuImage target will build a uImage with bootwrapper code and a device tree. The default device tree and platform file are determined by the kernel configuration. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit 270429baa928fa6beff4042a7701944e23396417 Author: Scott Wood Date: Tue Apr 17 09:24:51 2007 +1000 [POWERPC] bootwrapper: Add CONFIG_DEVICE_TREE This provides a way to tell the bootwrapper makefile which device tree to include by default. The wrapper can still be invoked standalone to wrap with a different device tree without reconfiguring the kernel, if that is desired. The user will only be asked to provide a device tree if the platform selects CONFIG_WANT_DEVICE_TREE. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit b97d27914354619ec6883ffe08dbd9c8e4b826b7 Author: Olof Johansson Date: Wed Apr 18 16:39:54 2007 +1000 [POWERPC] pasemi: GPIO MDIO of_platform driver MDIO driver for PHY's connected via GPIO as on the PA Semi Electra eval board. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 25fc530eed1ca9ccde2a1e96d0b2060867f76bb2 Author: Olof Johansson Date: Wed Apr 18 16:38:21 2007 +1000 [POWERPC] pasemi: PA6T oprofile support Oprofile support for PA6T, kernel side. Also rename the PA6T_SPRN.* defines to SPRN_PA6T.*. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 7e8bddf56661a16b7d5945390a37cd2e9c5e45fe Author: Olof Johansson Date: Mon Apr 16 16:28:38 2007 +1000 [POWERPC] pasemi: Reset mpic on boot Reset MPIC on boot to clear some timer state that firmware might leave configured. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 62357d821550e253b53a6106e3e5dc946647fec4 Author: Olof Johansson Date: Mon Apr 16 16:27:45 2007 +1000 [POWERPC] pasemi: Enable one more hid bit Minor HID change. Firmware can't know that we want this set so we have to set it in the kernel. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit df7e70a2b0899845bb9a53548beeed05b9947705 Author: Olof Johansson Date: Mon Apr 16 16:26:34 2007 +1000 [POWERPC] pasemi: Allow 8KB config space for I/O bridge Device 0 function 0 on the root bus is really a two-function bus agent, but only the first function is visible. Because of this, we need to allow config accesses into the second range. Modify the check for valid offsets accordingly. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit a14c4508f4bb1bb7772b1976a82646be8d8b515a Author: Josh Boyer Date: Fri Apr 13 04:33:25 2007 +1000 [POWERPC] Fix PowerPC 750CL and 750GX CPU features PowerPC 750CL has high BATs. The patch below adds a CPU_FTRS_750CL that includes that. Without it, the original firmware mappings in the high BATs aren't cleared which continue to override the linux translations. It also adds CPU_FTR_COMMON to CPU_FTRS_750GX for completeness. Signed-off-by: Josh Boyer Signed-off-by: Paul Mackerras commit c3ea6921a68ed4bdcfc782676c52707cbe347952 Author: will schmidt Date: Wed Apr 18 00:54:51 2007 +1000 [POWERPC] hvc_console: Typo fixes Fix a handful of comment typos for hvc_console. Signed-off-by: Will Schmidt Signed-off-by: Paul Mackerras commit b791072ba3c3b29bab1890963bde52eb944a8451 Author: Will Schmidt Date: Wed Apr 18 00:44:46 2007 +1000 [POWERPC] hvc_console: Polling mode timer backoff Add a back-off mechanism to hvc_console's polling logic. This change drops the timers/second ratio from ~90 to ~1/2 while the console is idle. This change is most noticeable when watching /proc/timer_stats output. This only affects when the hvc_console is running in poll mode, i.e. power4 and cell systems. I've tested on Power4, Michael Ellerman has both contributed to the patch and tested on cell. Signed-off-by: Will Schmidt Signed-off-by: Michael Ellerman Acked-by: Linas Vepstas Signed-off-by: Paul Mackerras commit b3a6d2a54b8be331b36d849577a716867895ca75 Author: Stephen Rothwell Date: Fri Apr 13 17:14:22 2007 +1000 [POWERPC] Rename last get_property calls These got added recently. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit edfedbf0cd0a57b16b638275e8fe8c369580b726 Author: Paul Mackerras Date: Tue Apr 24 13:57:12 2007 +1000 [POWERPC] Partially revert "Add correct interrupt property for pegasos ide" It turns out that commit e48059225c2edc6f1e5a2008261f1efdf606f247 breaks some existing systems that use the via82cxxx driver. This reverts the change to via82cxxx.c. Signed-off-by: Paul Mackerras commit c6d344819ea26c4df1cf2572232706667e1d99ea Author: Arnd Bergmann Date: Mon Apr 23 21:35:48 2007 +0200 [POWERPC] update cell_defconfig Sync with the Kconfig changes, and enable some options for celleb Cc: Ishizaki Kou Signed-off-by: Jens Osterkamp Signed-off-by: Arnd Bergmann commit 150f7e3cfec42a7d96ffda6f83881a7cee101c87 Author: Jeremy Kerr Date: Mon Apr 23 21:35:47 2007 +0200 [POWERPC] cell: enable RTAS-based PTCAL for Cell XDR memory Enable Periodic Recalibration (PTCAL) support for Cell XDR memory, using the new ibm,cbe-start-ptcal and ibm,cbe-stop-ptcal RTAS calls. Tested on QS20 and QS21 (by Thomas Huth). It seems that SLOF has problems disabling, at least on QS20; this patch should only be used once these problems have been addressed. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann commit 9dd855a729abb4670522d5eae6854db48d624498 Author: Christian Krafft Date: Mon Apr 23 21:35:46 2007 +0200 [POWERPC] cell: add support for proper device-tree This patch adds support for a proper device-tree. A porper device-tree on cell contains be nodes for each CBE containg nodes for SPEs and all the other special devices on it. Ofcourse oldschool devicetree is still supported. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit 6bf05fd776e38a0a9c17e17c2345b59b1b9aa2cb Author: Christian Krafft Date: Mon Apr 23 21:35:45 2007 +0200 [POWERPC] add of_iomap function The of_iomap function maps memory for a given device_node and returns a pointer to that memory. This is used at some places, so it makes sense to a seperate function. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit 4a065f9418274f2d73066f1638b3612341e4f030 Author: Christian Krafft Date: Mon Apr 23 21:35:44 2007 +0200 [POWERPC] pmi probe device by device-type At the moment the pmi device driver is probing for devices with a given type and a given name. As there may be devices of the same type but with a different name, probing should be done also for device type only. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit 79baf4a60e8aceb2b8a5bed8575885499cb21ce4 Author: Christian Krafft Date: Mon Apr 23 21:35:43 2007 +0200 [POWERPC] add check for initialized driver data to pmi driver This patch adds a check for the private driver data to be initialized. The bug showed up, as the caller found a pmi device by it's type. Whereas the pmi driver probes for the type and the name. Since the name was not as the driver expected, it did not initialize. A more relaxed probing will be supplied with an extra patch, too. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit 5050063c0464663a0b0c3dc9fc5bc822aa74a1dd Author: Christian Krafft Date: Mon Apr 23 21:35:42 2007 +0200 [POWERPC] cell: use pmi in cpufreq driver The new PMI driver was added in order to support cpufreq on blades that require the frequency to be controlled by the service processor, so use it on those. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit 5f7bdaee2aaf7dfdf18c735dee62401fd26c8d72 Author: Christian Krafft Date: Mon Apr 23 21:35:41 2007 +0200 [POWERPC] cbe_thermal: add throttling attributes to cpu and spu nodes This patch adds some attributes the cpu and spu nodes: /sys/devices/system/[c|s]pu/[c|s]pu*/thermal/throttle_begin /sys/devices/system/[c|s]pu/[c|s]pu*/thermal/throttle_end /sys/devices/system/[c|s]pu/[c|s]pu*/thermal/throttle_full_stop Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit 24d560d7b9cf90451c6ef6248c09fb4cee1c76e6 Author: Christian Krafft Date: Mon Apr 23 21:35:40 2007 +0200 [POWERPC] cbe_thermal: clean up computation of temperature This patch introduces a little function for transforming register values into temperature. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit 91a69c9646a5b709381d99a171890e77377b1b9c Author: Christian Krafft Date: Mon Apr 23 21:35:39 2007 +0200 [POWERPC] cell: add cbe_node_to_cpu function This patch adds code to deal with conversion of logical cpu to cbe nodes. It removes code that assummed there were two logical CPUs per CBE. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann commit ccf17e9d008dfebbf90dfa4ee1a56e81c784c73e Author: Jeremy Kerr Date: Mon Apr 23 21:08:29 2007 +0200 [POWERPC] spu_base: fix initialisation on systems with no SPEs This change fixes the case where spu_base and spufs are initialised on a system with no SPEs - unconditionally create the spu_lists so spu_alloc doesn't explode, and check for spu_management ops before starting spufs. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann arch/powerpc/platforms/cell/spu_base.c | 7 ++++--- arch/powerpc/platforms/cell/spufs/inode.c | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) commit befdc746ee027d686a06be29cb1391f9d2c45cf6 Author: Christoph Hellwig Date: Mon Apr 23 21:08:28 2007 +0200 [POWERPC] spu_base: remove cleanup_spu_base spu_base.c is always built into the kernel image, so there is no need for a cleanup function. And some of the things it does are in the way for my following patches, so I'd rather get rid of it ASAP. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit aa45e2569ffe963dfbbbfddfdccd12afe69b2d65 Author: Christoph Hellwig Date: Mon Apr 23 21:08:27 2007 +0200 [POWERPC] spufs: various run.c cleanups - remove the spu_acquire_runnable from spu_run_init. I need to opencode it in spufs_run_spu in the next patch - remove various inline attributes, we don't really want to inline long functions with multiple callsites - cleanup return values and runcntl_write calls in spu_run_init - use normal kernel codingstyle in spu_reacquire_runnable Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit fe8a29db5bce1b5bd1ceb85fd153fac52cdab7b2 Author: Akinobu Mita Date: Mon Apr 23 21:08:26 2007 +0200 [POWERPC] spufs: enable SPU coredump for kernel-builtin spufs spu_coredump_calls.owner is NULL in case of a builtin spufs, so the checks in here break. Check for the availability of the spu_coredump_calls variable instead. Signed-off-by: Arnd Bergmann commit 6cf2179202cf706471777ad6ee5d0377d5990ab7 Author: Arnd Bergmann Date: Mon Apr 23 21:08:25 2007 +0200 [POWERPC] spufs: fix memory leak on coredump Dynamically allocated read/write buffer in spufs_arch_write_note() will not be freed. Convert it to get_free_page at the same time. Cc: Akinobu Mita Signed-off-by: Arnd Bergmann commit d3764397d07b1e03943edfdcc3fb77af7bdac02b Author: Jeremy Kerr Date: Mon Apr 23 21:08:24 2007 +0200 [POWERPC] spufs: Minor cleanup of spu_wait Change the loop in spu_wait to be a little more straightforward. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann commit f11f5ee70f48899506514e5e0d10ee2c8ddd359a Author: Jeremy Kerr Date: Mon Apr 23 21:08:23 2007 +0200 [POWERPC] spufs: add mode= mount option Add a 'mode=' option to spufs mount arguments. This allows more control over access to the top-level spufs directory. Tested on Cell. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann commit 9e2fe2ce4e957a79d3dc5d813e0cfb10d79b79b3 Author: Akinobu Mita Date: Mon Apr 23 21:08:22 2007 +0200 [POWERPC] spufs: use memcpy_fromio() to copy from local store GCC may generates inline copy loop to handle memcpy() function instead of kernel defined memcpy(). But this inlined version of memcpy() causes an alignment interrupt when copying from local store. This patch uses memcpy_fromio() and memcpy_toio to copy local store to prevent memcpy() being inlined. Signed-off-by: Akinobu Mita Signed-off-by: Arnd Bergmann commit 8a7d86bdb22678b17928eef0c8fa356d8b21cc76 Author: Christoph Hellwig Date: Mon Apr 23 21:08:21 2007 +0200 [POWERPC] spufs: avoid spurious memory barriers We now have proper locking around assignets of the mapping pointers, and the spin_unlock implies enough of a barrier to get rid of the explicit one. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit db1384b40d12eda6910513ff429ad90453ca49e1 Author: Akinobu Mita Date: Mon Apr 23 21:08:20 2007 +0200 [POWERPC] spufs: fix memory leak on spufs reloading When SPU isolation mode enabled, isolated_loader would be allocated by spufs_init_isolated_loader() on module_init(). But anyone do not free it. This patch introduces spufs_exit_isolated_loader() which is the opposite of spufs_init_isolated_loader() and called on module_exit(). Cc: Arnd Bergmann Signed-off-by: Akinobu Mita Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit c99c1994a2bb9493b4ac372b2b6ee2606d291171 Author: Akinobu Mita Date: Mon Apr 23 21:08:19 2007 +0200 [POWERPC] spufs: fix missing error handling in module_init() spufs module_init forgot to call a few cleanup functions on error path. This patch also includes cosmetic changes in spu_sched_init() (identation fix and return error code). [modified by hch to apply ontop of the latest schedule changes] Cc: Arnd Bergmann Signed-off-by: Akinobu Mita Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit 577f8f1021f9ee6ef2a98a142652759ec122d27f Author: Akinobu Mita Date: Mon Apr 23 21:08:18 2007 +0200 [POWERPC] spufs: check spu_acquire_runnable() return value This patch checks return value of spu_acquire_runnable() in spufs_mfc_write(). Signed-off-by: Akinobu Mita Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit e45d48a34d4d1862d28d22c2533b8c6bb83b8c1f Author: Christoph Hellwig Date: Mon Apr 23 21:08:17 2007 +0200 [POWERPC] spufs: turn run_sema into run_mutex There is no reason for run_sema to be a struct semaphore. Changing it to a mutex and rename it accordingly. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit c8a1e9393a86f862ab9c8bc0db9b8a1822226f84 Author: Jeremy Kerr Date: Mon Apr 23 21:08:16 2007 +0200 [POWERPC] spufs: provide siginfo for SPE faults This change populates a siginfo struct for SPE application exceptions (ie, invalid DMAs and illegal instructions). Tested on an IBM Cell Blade. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann commit 57dace2391ba10135e38457904121e7ef34d0c83 Author: Arnd Bergmann Date: Mon Apr 23 21:08:15 2007 +0200 [POWERPC] spufs: make spu page faults not block scheduling Until now, we have always entered the spu page fault handler with a mutex for the spu context held. This has multiple bad side-effects: - it becomes impossible to suspend the context during page faults - if an spu program attempts to access its own mmio areas through DMA, we get an immediate livelock when the nopage function tries to acquire the same mutex This patch makes the page fault logic operate on a struct spu_context instead of a struct spu, and moves it from spu_base.c to a new file fault.c inside of spufs. We now also need to copy the dar and dsisr contents of the last fault into the saved context to have it accessible in case we schedule out the context before activating the page fault handler. Signed-off-by: Arnd Bergmann commit 62c05d583ec016c40011462d5f03b072bfbd3dc7 Author: Christoph Hellwig Date: Mon Apr 23 21:08:14 2007 +0200 [POWERPC] spu_base: move spu_init_channels out of spu_mutex There is no reason to execute spu_init_channels under spu_mutex after the spu has been taken off the freelist it's ours. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit 4e0f4ed0df71013290cd2a01f7b84264f7b99678 Author: Luke Browning Date: Mon Apr 23 21:08:13 2007 +0200 [POWERPC] spu sched: make addition to stop_wq and runque atomic vs wakeup Addition to stop_wq needs to happen before adding to the runqeueue and under the same lock so that we don't have a race window for a lost wake up in the spu scheduler. Signed-off-by: Luke Browning Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit 7ec18ab923a2e377ecb05c74a2d38f457f79950f Author: Christoph Hellwig Date: Mon Apr 23 21:08:12 2007 +0200 [POWERPC] spufs: streamline locking for isolated spu setup For quite a while now spu state is protected by a simple mutex instead of the old rw_semaphore, and this means we can simplify the locking around spu_setup_isolated a lot. Instead of doing an spu_release before entering spu_setup_isolated and then calling the complicated spu_acquire_exclusive we can now simply enter the function locked an in guaranteed runnable state, so that the only bit of spu_acquire_exclusive that's left is the call to spu_unmap_mappings. Similarly there's no more need to unlock and reacquire the state_mutex when spu_setup_isolated is done, but we can always return with the lock held and only drop it in spu_run_init in the failure case. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit a475c2f43520cb095452201da57395000cfeb94c Author: Christoph Hellwig Date: Mon Apr 23 21:08:11 2007 +0200 [POWERPC] spufs: remove woken threads from the runqueue early A single context should only be woken once, and we should not have more wakeups for a given priority than the number of contexts on that runqueue position. Also add some asserts to trap future problems in this area more easily. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit 390c53430498c9973e015432806edd53b2efe6c6 Author: Arnd Bergmann Date: Mon Apr 23 21:08:10 2007 +0200 [POWERPC] spufs: add memory barriers after set_bit set_bit does not guarantee ordering on powerpc, so using it for communication between threads requires explicit mb() calls. Signed-off-by: Arnd Bergmann commit e097b513285e616215b23af234d127298bb8d89a Author: Christoph Hellwig Date: Mon Apr 23 21:08:09 2007 +0200 [POWERPC] spu sched: ensure preempted threads are put back on the runqueue, part2 To not lose a spu thread we need to make sure it always gets put back on the runqueue. In find_victim aswell as in the scheduler tick as done in the previous patch. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit b3e76cc3244ac139fc75750c5af9edbb9f191a10 Author: Christoph Hellwig Date: Mon Apr 23 21:08:08 2007 +0200 [POWERPC] spu sched: ensure preempted threads are put back on the runqueue To not lose a spu thread we need to make sure it always gets put back on the runqueue. Signed-off-by: Christoph Hellwig Acked-by: Jeremy Kerr Signed-off-by: Arnd Bergmann commit 43c2bbd932b66403688f3d812065d82f8fb8f4b3 Author: Christoph Hellwig Date: Mon Apr 23 21:08:07 2007 +0200 [POWERPC] spufs: clear mapping pointers after last close Make sure the pointers to various mappings are cleared once the last user stopped using them. This avoids accessing freed memory when tearing down the gang directory aswell as optimizing away pte invalidations if no one uses these. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit 0887309589824fb1c3744c69a330c99c369124a0 Author: Christoph Hellwig Date: Mon Apr 23 21:08:06 2007 +0200 [POWERPC] spufs: use cancel_rearming_delayed_workqueue when stopping spu contexts The scheduler workqueue may rearm itself and deadlock when we try to stop it. Put a flag in place to avoid skip the work if we're tearing down the context. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann commit 44b998e1eb254edc87177819ee693690fac68b7f Author: David Woodhouse Date: Mon Apr 23 12:11:46 2007 +0100 [JFFS2] Improve failure mode if inode checking leaves unchecked space. We should never find the unchecked size is non-zero after we've finished checking all inodes. If it happens, used to BUG(), leaving the alloc_sem held and deadlocking. Instead, just return -ENOSPC after complaining. The GC thread will die, but read-only operation should be able to continue and the file system should be unmountable. Signed-off-by: David Woodhouse commit 566865a2a4791c9290155f651ee0c2c606db0b1d Author: David Woodhouse Date: Mon Apr 23 12:07:17 2007 +0100 [JFFS2] Fix cross-endian build. When compiling a LE-capable JFFS2 on PowerPC, wbuf.c fails to compile: fs/jffs2/wbuf.c:973: error: braced-group within expression allowed only inside a function fs/jffs2/wbuf.c:973: error: initializer element is not constant fs/jffs2/wbuf.c:973: error: (near initialization for ‘oob_cleanmarker.magic’) fs/jffs2/wbuf.c:974: error: braced-group within expression allowed only inside a function fs/jffs2/wbuf.c:974: error: initializer element is not constant fs/jffs2/wbuf.c:974: error: (near initialization for ‘oob_cleanmarker.nodetype’) fs/jffs2/wbuf.c:975: error: braced-group within expression allowed only inside a function fs/jffs2/wbuf.c:976: error: initializer element is not constant fs/jffs2/wbuf.c:976: error: (near initialization for ‘oob_cleanmarker.totlen’) Provide constant_cpu_to_je{16,32} functions, and use them for initialising the offending structure. Signed-off-by: David Woodhouse commit 3e67fe4543333048e486d7f360a0e2ae5d76c053 Author: Christoph Hellwig Date: Sun Apr 22 20:40:57 2007 +0100 [MTD] Finish conversion mtd_blkdevs to use the kthread API Remove waitqueue, 'exiting' flag and completion; use kthread APIs instead. Signed-off-by: Christoph Hellwig Signed-off-by: David Woodhouse commit 99fba3f8177956170f3d86f83c2cf2f70747105f Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:44 2007 -0300 ACPI: thinkpad-acpi: improve fan watchdog messages Improve some of the fan watchdog error messages to be a little more helpful. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 04cc862c1893a055ab1117fa6f3aa0886c0ba032 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:43 2007 -0300 ACPI: thinkpad-acpi: cleanup thermal subdriver for sysfs conversion Clean-up the thermal subdriver for sysfs conversion. Make thermal_get_* reentrancy-safe while at it, and add the missing thermal_read_mode variable to the header file. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit c9bea99c1a712548db3437cbca52b0da8f30069c Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:42 2007 -0300 ACPI: thinkpad-acpi: clean up CMOS commands subdriver Some ThinkPad CMOS commands subdriver cleanups, and also rename/promote cmos_eval to a ACPI helper function, as it is used by many other subdrivers. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 83f34724643a3b0ec9322490b9ad9f1b60170a6c Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:41 2007 -0300 ACPI: thinkpad-acpi: cleanup video subdriver Cleanup video subdriver for sysfs conversion, and properly check result status of acpi_evalf. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit d6fdd1e91a8a4cd852dc1d945165e3a69ac9e257 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:40 2007 -0300 ACPI: thinkpad-acpi: cleanup bluetooth and wan for sysfs conversion Prepare bluetooth and wan driver code to be more easily hooked into sysfs helpers, by separating the procfs logic from the device attribute handling. These changes also remove the entries from procfs on notebooks without the bluetooth/wan hardware installed. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit b86c4722de62f336b82dff3c47ef59ba2a587ec1 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:39 2007 -0300 ACPI: thinkpad-acpi: clean up hotkey subdriver Cleanup hotkey subdriver code. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit d01320e606d334a0cd35d781a58f9f3c254829ab Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:38 2007 -0300 ACPI: thinkpad-acpi: mark acpi helper functions __must_check Mark acpi_evalf and friends __must_check. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 8d376cd6543d57ef10799be02ba5f19aa6678032 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:37 2007 -0300 ACPI: thinkpad-acpi: prepare for device model conversion Prepare the thinkpad-acpi driver for the conversion to the device model, by renaming variables and doing other glue work that shall make the later patches much cleaner. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit d8fd94d9f08237ffda7e44e6825b057bf20a90e3 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:36 2007 -0300 ACPI: thinkpad-acpi: use bitfields for module flags Use a bitfield to hold boolean module-wide flags, to conserve some memory. It is easy and it is clean, so we do it just for the heck of it even if it saves very little space. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 926411779287ad4f7013c9d80aa44fd131b70cd9 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:35 2007 -0300 ACPI: thinkpad-acpi: use bitfields to hold subdriver flags Save some memory by using bitfields to hold boolean flags for the subdrivers. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 0dcef77c5b889338811d35e786b42046259fe433 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:34 2007 -0300 ACPI: thinkpad-acpi: improve thinkpad detection Improve the detection of ThinkPads, so as to reduce the chances of false positives. Since this could potentially add false negatives on the very old models, add a module parameter to force the detection of a thinkpad. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit a5763f2223ce3fdbc75923f8c948fc7b59ed2f96 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:33 2007 -0300 ACPI: thinkpad-acpi: uncouple subdriver init from ibms struct Move the .init method from ibms struct to another struct, and use a list head to control which subdrivers have been activated. This allows us to have the subdriver init methods marked __init, saving quite a lot of .text size, and even a bit of .data size as some data can now be made __initdata. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit fe08bc4b4fd1371fad111675a564e4d2ebbf39ea Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:32 2007 -0300 ACPI: thinkpad-acpi: add subdriver debug statements Add debug messages to the subdriver initialization and exit code. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 5fba344cfdbaa79e6320da26c3db34dfb219a845 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:31 2007 -0300 ACPI: thinkpad-acpi: clean up probing and move init to subdrivers Move most of the probing code to its own function, and most of the subdriver-specific init code into subdriver init functions. This allows us to not define pci_handle unless the dock subdriver is enabled, as well. This patch causes a minor userland interface change: if a subdriver doesn't detect a capability, /proc entries for it are not created anymore (as opposed to a /proc entry that just returned "unsupported"). Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 132ce09123755ec5e3d3a8ae22f4f753c3baac97 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:30 2007 -0300 ACPI: thinkpad-acpi: add debug mode Add a debug mode parameter and verbose debug mode Kconfig option. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit f51d1a39840ae5e8678d702ab57377c611fc3826 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:29 2007 -0300 ACPI: thinkpad-acpi: update fan firmware documentation Update some stuff in the in-code text describing the ThinkPad fan firmware. This patch has no code changes. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit efa27145df34eacf2569bd45f68dbe00003d3616 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:28 2007 -0300 ACPI: thinkpad-acpi: rename thinkpad constants Rename all IBMACPI_ constants, now that we are not called ibm-acpi anymore. Driver-specific constants are now prefixed TPACPI_, ThinkPad firmware specific ones are now prefixed TP_CMOS_, TP_ACPI_, or TP_EC_. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 1def7115f0277ce9d2a54efd0ae187aa88d5c7fa Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:27 2007 -0300 ACPI: thinkpad-acpi: rename module glue Rename module init and exit functions, now that we are not called ibm-acpi anymore. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 142cfc90f026b0b8fd1a14ba11ae29eb7b1b6ca1 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:26 2007 -0300 ACPI: thinkpad-acpi: rename one stray use of ibm-acpi in a comment Rename a stray use of ibm-acpi on a comment, no functional changes. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 6700121b535fa16fe1c8aaac03559b2f12909726 Author: Henrique de Moraes Holschuh Date: Sat Apr 21 11:08:25 2007 -0300 ACPI: thinkpad-acpi: rename register_ibmacpi_subdriver Rename register_ibmacpi_subdriver to register_tpacpi_subdriver, as we are not called ibmacpi anymore. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit a491486a2087ac3dfc00efb4f838c8d684afaf54 Author: Joakim Tjernlund Date: Fri Mar 16 16:15:45 2007 +0100 [JFFS2] Obsolete dirent nodes immediately on unlink, where possible. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse commit ec98c681a5355469eee70227b7e0a88f0d688483 Author: Jan Engelhardt Date: Thu Apr 19 16:21:41 2007 -0500 Use menuconfig objects: MTD Use menuconfigs instead of menus, so the whole menu can be disabled at once instead of going through all options. Signed-off-by: Jan Engelhardt Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 99f9b2431ed3da4a66cf1cfe74132a53a9569bba Author: Eric W. Biederman Date: Thu Apr 19 01:58:33 2007 -0600 [MTD] mtd_blkdevs: Convert to use the kthread API thread_run is used intead of kernel_thread, daemonize, and mucking around blocking signals directly. Signed-off-by: Eric W. Biederman Signed-off-by: David Woodhouse commit 876b9276b993723f7a74d55b3b49b9186f05d09d Author: Paul Walmsley Date: Thu Apr 19 14:56:12 2007 +0200 USB HID: add 'quirks' module parameter Add a 'quirks' module parameter for the usbhid module, so users can add or modify quirks at module load time. Signed-off-by: Paul Walmsley Signed-off-by: Jiri Kosina commit 8cef908235bcac898a4f4ccc50c781e08022a579 Author: Paul Walmsley Date: Thu Apr 19 14:37:44 2007 +0200 USB HID: add support for dynamically-created quirks Add internal support for dynamically-allocated HID quirks, "dquirks" (for "dynamic quirks"). Includes several functions to add/modify quirks from the list. This code is used by the next patch to implement quirk modification upon module load. Signed-off-by: Paul Walmsley Signed-off-by: Jiri Kosina commit 8222fbe67cc6c83bb6d8d9d913aee17957fc0654 Author: Paul Walmsley Date: Thu Apr 19 13:45:57 2007 +0200 USB HID: clarify static quirk handling as squirks Rename existing quirks handling code that operates over a static array to "squirks" (short for static quirks) to differentiate it from the dynamically-allocated quirks that will be introduced in the next patch. Add an accessor function specifically for static quirks, usbhid_exists_squirk(). Signed-off-by: Paul Walmsley Signed-off-by: Jiri Kosina commit 2eb5dc30eb87aa30f67e3dff39d5c9f3fb643260 Author: Paul Walmsley Date: Thu Apr 19 13:27:04 2007 +0200 USB HID: encapsulate quirk handling into hid-quirks.c Move the USB_VENDOR* and USB_DEVICE* defines and the hid_blacklist[] array there from hid-core.c. Add hid-quirks.c:usbhid_lookup_any_quirks() to return quirk information to hid-core.c. Convert __u32, __u16 types to u32, u16. Signed-off-by: Paul Walmsley Signed-off-by: Jiri Kosina commit 3f114853d4f7c1746389f26e1d500887294da8fd Author: Roland Dreier Date: Wed Apr 18 20:21:02 2007 -0700 IB/mthca: Update HCA firmware revisions Update the driver's list of current firmware versions with Mellanox's latest releases. Signed-off-by: Roland Dreier commit 40b90430ecac40cc9adb26b808cc12a3d569da5d Author: Robert Walsh Date: Thu Mar 15 14:45:17 2007 -0700 IB/ipath: Fix WC format drift between user and kernel space The kernel ib_wc structure now uses a QP pointer, but the user space equivalent uses a QP number instead. This means we can no longer use a simple structure copy to copy stuff into user space. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 6ce73b07db7aa05d4a30716d6a99c832b6d9db4a Author: Robert Walsh Date: Thu Mar 15 14:45:16 2007 -0700 IB/ipath: Check that a UD work request's address handle is valid Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 0d6172a4284b21e4762e8638a4d693ef52f63bfe Author: Robert Walsh Date: Thu Mar 15 14:45:15 2007 -0700 IB/ipath: Remove duplicate stuff from ipath_verbs.h Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 253fb3902008353831525ab711909abdd5ee191f Author: Robert Walsh Date: Thu Mar 15 14:45:14 2007 -0700 IB/ipath: Check reserved memory keys Don't let userspace use the direct-physical-map L_key or R_key. Signed-off-by: Ralph Campbell Signed-off-by: Roland Dreier commit f0810daf74c564a3615eba5002cc11c21a0949ba Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:13 2007 -0700 IB/ipath: Fix unit selection when all CPU affinity bits set At some point things changed so that all the affinity bits can be set, but cpus_full() macro is not true. This caused problems with the unit selection logic on multi-unit (board) configurations. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 662af5813be9aadf95ca310b7b6d1d37070c9922 Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:12 2007 -0700 IB/ipath: Don't allow QPs 0 and 1 to be opened multiple times Signed-off-by: Robert Walsh Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 53c1d2c943a67fb129ed2797182305a4633531fb Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:11 2007 -0700 IB/ipath: Disable IB link earlier in shutdown sequence Move the code that shuts down the IB link earlier in the unload process, to be sure no new packets can arrive while we are unloading. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 490462c2686df6e35c21d1efe935e0b4a3bddb39 Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:10 2007 -0700 IB/ipath: Prevent random program use of diags interface To prevent random utility reads and writes of the diag interface to the chip, we first require a handshake of reading from offset 0 and writing to offset 0 before any other reads or writes can be done through the diags device. Otherwise chip errors can be triggered. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit f5408ac7ccec0a7edd2b6add0da82735375a37a0 Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:09 2007 -0700 IB/ipath: On unrecoverable errors, force link down, LEDs off If the chip is no longer usable, LEDs should be turned off so system can be found easily in the cluster. Also some minor reorganizing so both chips print hardware error message at same point and only if there were unrecovered errors Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 27b044a815df7d4530bc68560796680ed588070c Author: Michael Albaugh Date: Thu Mar 15 14:45:08 2007 -0700 IB/ipath: Fix driver crash (in interrupt or during unload) after chip reset Re-init of the kernel structures after a chip reset was leaving the portdata structure for port zero in an inconsistent state, and a pointer to it either stale (in re-init code) or NULL (in devdata) Fixing the order of operations on this struct, and the condition for interrupt access, prevents the crashes. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 9783ab405844202b452ac673677e6c8f8c9a6a99 Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:07 2007 -0700 IB/ipath: Improve handling and reporting of parity errors Mostly cleanup. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 820054b7ca7a54ba94d89db4b3c53a24d2d66633 Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:06 2007 -0700 IB/ipath: Print better error messages if kernel is misconfigured Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 569b87b47f906d65ee35d6ecc4767f20a6390b9b Author: Arthur Jones Date: Thu Mar 15 14:45:05 2007 -0700 IB/ipath: Force PIOAvail update entry point Due to a chip bug, the PIOAvail register is not always updated to memory. This patch allows userspace to force an update. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 7b196e2ff3953063b656212ff517f6115a1477b2 Author: Arthur Jones Date: Thu Mar 15 14:45:04 2007 -0700 IB/ipath: Call free_irq() on chip specific initialization failure In initialization, if we bailed at chip specific initialization, we forgot to clean up the irq we had requested. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 5a7d4eea9185c20275307fcd1077d6f9dfdab48a Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:03 2007 -0700 IB/ipath: Discard multicast packets without a GRH This patch fixes a bug where multicast packets without a GRH were not being dropped as per the IB spec. Signed-off-by: Ralph Campbell Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 0ed3c594e3878274787810422760dc7c51e0ee72 Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:02 2007 -0700 IB/ipath: Fix calculation for number of kernel PIO buffers If the module parameter "kpiobufs" is set too high, the calculation to reset it to a sane value was incorrect. Signed-off-by: Ralph Campbell Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit c8c6f5d496fe794cbb52fe5a08c2bd839eecaa07 Author: Bryan O'Sullivan Date: Thu Mar 15 14:45:01 2007 -0700 IB/ipath: Remove unused ipath_read_kreg64_port() Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit dd5190b6be0f3e27b6a4933a6a6d2d59957fc748 Author: Ralph Campbell Date: Thu Mar 15 14:45:00 2007 -0700 IB/ipath: Fix RDMA reads of length zero and error handling Fix RDMA read response length checking for RDMA_READ_RESPONSE_ONLY to allow a zero length response. RDMA read responses which don't match the expected length or occur in response to some other operation should generate a completion queue error (see table 56, ch. 9.9.2.3 in the IB spec). Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit c7e29ff11f23ec78b3caf691789c2b791bb596bf Author: Mark Debbage Date: Thu Mar 15 14:44:59 2007 -0700 IB/ipath: Allow receive ports mapped into userspace to be shared Improve port-sharing performance by allowing any process to receive packets from the shared hardware port under a spin lock for mutual exclusion. Previously, one process was nominated as the master and that process was responsible for receiving all packets from the shared hardware port and either consuming them or forwarding them to their destination. This led to starvation problems for other processes when the master process was busy in computation phases. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 0a5a83cffc03592c2102ad07b7532b596a16f8cd Author: Ralph Campbell Date: Thu Mar 15 14:44:58 2007 -0700 IB/ipath: Fix port sharing on powerpc The port sharing feature mixed kernel virtual addresses as well as physical addresses for the offset used to describe the mmap address to map the InfiniPath hardware into user space. This had a conflict on powerpc. The new scheme converts it to a physical address so it doesn't conflict with chip addresses and yet still fits in 40/44 bits so it isn't truncated by 32-bit applications calling mmap64(). Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 041eab9136d8325c332429df71d05ba3e0ea8ebc Author: Bryan O'Sullivan Date: Thu Mar 15 14:44:57 2007 -0700 IB/ipath: Fix CQ flushing when QP is modified to error state If a receive work request has been removed from the queue but has not had a CQ entry generated for it and the QP is modified to the error state, the completion entry generated is incorrect. This patch fixes the problem. Signed-off-by: Ralph Campbell Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 614d49a21e96737f84b13f644ac813f8eb6d297a Author: Bryan O'Sullivan Date: Thu Mar 15 14:44:56 2007 -0700 IB/ipath: Fix bad argument to clear_bit() Code was converted from a &= ~mask to clear_bit, but the bit was left shifted instead of being used directly, so we were either trashing memory several pages away, or sometimes taking a kernel page fault on an invalid page. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 8ec1077b35359c973f4b1de7c516be570a6df495 Author: Bryan O'Sullivan Date: Thu Mar 15 14:44:55 2007 -0700 IB/ipath: Change packet problems vs chip errors handling and reporting Some types of packet errors are moderately common with longer IB cables and large clusters, and are not reported with prints by other IB HCA drivers. This suppresses those messages unless the new __IPATH_ERRPKTDBG bit is set in ipath_debug. Reporting of temporarily disabled frequent error interrupts was also made clearer We also distinguish between chip errors, and bad packets sent or received in the wording of the messages. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 6f5c407460bba332d6bee52e19f2305539395511 Author: Ralph Campbell Date: Thu Mar 15 14:44:54 2007 -0700 IB/ipath: Fix PSN update for RC retries This patch fixes a number of bugs with updating the PSN for retries of RC requests. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 0434d271fddaabd65aaa4dbd0145112d6e8aa388 Author: Ralph Campbell Date: Thu Mar 15 14:44:53 2007 -0700 IB/ipath: Fix QP error completion queue entries When switching to the QP error state, the completion queue entries (error or flush) were not being generated correctly. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 39c0d0b919ae5080163bd2d41c0271cda250d382 Author: Bryan O'Sullivan Date: Thu Mar 15 14:44:52 2007 -0700 IB/ipath: Fix up some debug messages ipath_dbg doesn't need the same prefixes that printk does. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 3859e39d75b72f35f7d38c618fbbacb39a440c22 Author: Ralph Campbell Date: Thu Mar 15 14:44:51 2007 -0700 IB/ipath: Support larger IB_QP_MAX_DEST_RD_ATOMIC and IB_QP_MAX_QP_RD_ATOMIC This patch adds support for multiple RDMA reads and atomics to be sent before an ACK is required to be seen by the requester. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 7b21d26ddad6912bf345e8e88a51a5ce98a036ad Author: Ralph Campbell Date: Thu Mar 15 14:44:50 2007 -0700 IB/ipath: NMI cpu lockup if local loopback used If a post send is done in loopback and there is no receive queue entry, the sending QP is put on a timeout list for a while so the receiver has a chance to post a receive buffer. If the another post send is done, the code incorrectly tried to put the QP on the timeout list again an corrupted the timeout list. This eventually leads to a spin lock deadlock NMI due to the timer function looping forever with the lock held. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 9f9630d5e12a51f38513de0d64320a55ab6f02d5 Author: Ralph Campbell Date: Thu Mar 15 14:44:49 2007 -0700 IB/ipath: Fix SRQ limit event causing dropped CQ entry A silly programming error causes a CQ entry to not be generated if a SRQ limit event is generated. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 947d7617a1d876c2c93f73017a734e070c64d43b Author: Ralph Campbell Date: Thu Mar 15 14:44:48 2007 -0700 IB/ipath: Don't initialize port memory for subports A recent change was made to allocate memory for a port after CPU affinity is set. That change didn't account for subports and was trying to allocate memory for the port twice. Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 19085745598ec254fd814411b675b52380c3bac0 Author: Bryan O'Sullivan Date: Thu Mar 15 14:44:47 2007 -0700 IB/ipath: Definitions of two RXE parity err bits were reversed The chip documentation on the expected TID vs eager TID parity error bits was reversed from what was implemented in the RTL, for both chips. This corrects the definitions. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 165c552c35052284e8ec4f7e9c027dfd33490e2c Author: Bryan O'Sullivan Date: Thu Mar 15 14:44:46 2007 -0700 IB/ipath: Fix user memory region creation when IOMMU present The loop which initializes the user memory region from an array of pages was using the wrong limit for the array. This worked OK when dma_map_sg() returned the same number as the number of pages. This patch fixes the problem. Signed-off-by: Ralph Campbell Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit 946db67fbf836af30835d610b914cdde0cf467f8 Author: Bryan O'Sullivan Date: Thu Mar 15 14:44:45 2007 -0700 IB/ipath: Add ability to set and clear IB local loopback This is a sticky state. It is useful for diagnosing problems with boards versus cable/switch problems. Signed-off-by: Dave Olson Signed-off-by: Bryan O'Sullivan Signed-off-by: Roland Dreier commit a89875fc7e23ec91561bc3742df3bd5d12b376b4 Author: Roland Dreier Date: Wed Apr 18 20:20:53 2007 -0700 IPoIB: Remove pointless opcode field from debugging output There's no point in printing the opcode field in the completion handling debugging output, since the type of completion is already printed at the beginning of the line. In fact the opcode field is not even defined for completions with a status other than success. Signed-off-by: Roland Dreier commit 9a4b65e35714516980c863bfb7edc5f232b8b458 Author: Hal Rosenstock Date: Mon Apr 2 12:45:16 2007 -0400 IB/umad: Fix declaration of dev_map[] The current ib_umad code never accesses bits past IB_UMAD_MAX_PORTS in dev_map[]. We shouldn't declare it to be twice as big. Pointed-out-by: Roland Dreier Signed-off-by: Hal Rosenstock commit 9b620d2a16814e5f2a063359c953c41f804e091a Author: Roland Dreier Date: Wed Apr 18 20:20:53 2007 -0700 IB: Remove reference to obsolete CONFIG_IPATH_CORE Since commit b1c1b6a3 ("IB/ipath: merge ipath_core and ib_ipath drivers"), CONFIG_IPATH_CORE no longer exists, so there's no reason to have a line for it in drivers/Makefile. Pointed out by Robert P. J. Day . Signed-off-by: Roland Dreier commit 4d5a1be98d8d40a7c558005d423b3daed8ba1ddb Author: Paul Zaremba Date: Wed Apr 18 15:29:47 2007 +0200 USB HID: EMS USBII device needs HID_QUIRK_MULTI_INPUT Add HID_QUIRK_MULTI_INPUT to the EMS USBII (0x0b43/0003) so the kernel detects both joystick ports properly. Without it you end up with a single joystick node (js0) that combines the two physical port signals. Signed-off-by: Paul Zaremba Signed-off-by: Jiri Kosina commit e6be133b68ae2c8f89d46da25ed7b31b84793e7e Author: Shashi Rao Date: Wed Mar 28 15:56:28 2007 -0700 [MTD] Fix fwh_lock locking This is on a custom board with a mapping driver access to an ST M50LPW080 chip. This chip is probed successfully with do_map_probe("jedec_probe",...). If I use the mtdchar interface to perform unlock->erase->program->lock on any of the 16 eraseblocks in the chip, the chip is left in FL_STATUS mode while the data structures believe that the chip is in FL_READY mode. Hence, any subsequent reads to any flash byte results in 0x80 being read. Signed-off-by: Shashi Rao Signed-off-by: David Woodhouse commit c2aecda79cd872679b9b11f9e59d797fb4c7d677 Author: Joakim Tjernlund Date: Tue Mar 27 13:32:09 2007 +0200 [JFFS2] Speed up mount for directly-mapped NOR flash Remove excessive scanning of empty flash after a clean marker for users of the point/unpoint method. cfi_cmdset_0001 uses point/unpoint by default iff flash mapping is linear. The speedup is several orders of magnitude if FS is less than half full. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse commit 10731f83009e2556f98ffa5c7c2cbffe66dacfb3 Author: Artem Bityutskiy Date: Wed Apr 4 13:59:11 2007 +0300 [JFFS2] fix buffer sise calculations in jffs2_get_inode_nodes() In read inode we have an optimization which prevents one min. I/O unit (e.g. NAND page) to be read more then once. Namely, at the beginning we do not know which node type we read, so we read so we assume we read the directory entry, because it has the smallest node header. When we read it, we read up to the next min. I/O unit, just because if later we'll need to read more, we already have this data. If it turns out to be that the node is not directory entry, and we need more data, and we did not read it because it sits in the next min. I/O unit, we read the whole next (or several next) min. I/O unit(s). And if it happens to be that we read a data node, and we've read part of its data, we calculate partial CRC. So if later we need to check data CRC, we'll only read the rest of the data from further min. I/O units and continue CRC checking. This code was a bit messy and buggy. The bug was that it assumed relatively large min. I/O unit, so that the largest node header could overlap only one min. I/O unit boundary. This parch clean-ups the code a bit and fixes this bug. The patch was not tested on flash with small min. I/O unit, like NOR-ECC, nut it was tested on NAND with 512 bytes NAND page, so it at least does not break NAND. It was also tested with mtdram so it should not break NOR. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit 7f762ab24ca2215b69a1395b5b58877f8282a089 Author: Adrian Hunter Date: Wed Apr 4 13:47:53 2007 +0300 [JFFS2] Disable summary after wbuf recovery After a write error, any data in the write buffer must be relocated. This is handled by the jffs2_wbuf_recover function. This function does not fix up the erase block summary information that is collected for writing at the end of the block, which results in an incorrect summary (or BUG if the summary was found to be empty). As the summary is not essential (it is an optimisation), it may be disabled for the current erase block when this situation arises. This patch does that. Signed-off-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit 99c2594f0e13de1ca84f97efc3f9e7bc49f91e11 Author: Adrian Hunter Date: Thu Mar 29 11:00:47 2007 +0300 [JFFS2] Prevent list corruption when handling write errors If a write error occurs, the affected block is placed on the bad_used_list. In the case that the write error occured when writing summary data the block was also being placed on the dirty_list, which caused list corruption and ultimately a soft lockup in jffs2_mark_node_obsolete. This fixes that. Signed-off-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit a5ac8aeb29000fcab8d91848273a6616fcd039ee Author: Adrian Hunter Date: Mon Mar 19 12:49:11 2007 +0200 [MTD] nandsim: enhance nandsim to allow arbitrary NAND size A new module parameter has been added called 'overridesize', which overrides the size that would be determined by the ID bytes. 'overridesize' is specified in erase blocks and as the exponent of a power of two e.g. 5 means a size of 32 erase blocks. Signed-off-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit 57aa6b545f6f772dd317ccd29bdada999b16a13d Author: Adrian Hunter Date: Mon Mar 19 12:40:41 2007 +0200 [MTD] nandsim: Enhance nandsim optionally to report wear information A new module parameter 'rptwear' specifies how many erases between reporting wear information. Zero means never. Signed-off-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit 514087e74fb401a6621e8c836f4eaab87c269f24 Author: Adrian Hunter Date: Mon Mar 19 12:47:45 2007 +0200 [MTD] nandsim: enhance nandsim to simulate flash errors New module parameters have been added to nandsim to simulate: bitflips random bit flips badblocks blocks that are initially marked bad weakblocks blocks that fail to erase after a small number of erase cycles weakpages pages that fail to write after a small number of successful writes gravepages pages that fail to read after a small number of successful reads Signed-off-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit 2b77a0ed54eeea61937e7f71b0487b815edfbcdf Author: Adrian Hunter Date: Mon Mar 19 12:46:43 2007 +0200 [MTD] nandsim: add partition capability to nandsim Enhance nandsim to be able to create more than 1 partition. A new module parameter 'parts' may be used to specify partition sizes. Signed-off-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit b0afbbec4981417f79e05865a36e57abfc289002 Author: Artem Bityutskiy Date: Wed Mar 21 11:07:05 2007 +0200 [JFFS2] fix deadlock on error path When the MTD driver returns write failure, the following deadlock occurs: We are in __jffs2_flush_wbuf(), we hold &c->wbuf_sem. Write failure. jffs2_wbuf_recover()->jffs2_reserve_space_gc()->jffs2_do_reserve_space() ->jffs2_erase_pending_blocks()->jffs2_flash_read() and it tries to lock &c->wbuf_sem again. Deadlock. Reported-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse commit 4226b510371efd9cdc628663527d36aee36054a9 Author: Andre Renaud Date: Tue Apr 17 13:50:59 2007 -0400 [MTD] [NAND] Casting bug in nand_default_block_markbad There is a slight bug in nand_default_block_markbad, where the offset is cast to an integer, prior to being shifted. This means that on large offsets, it is incorrectly doing a signed shift & losing bits. Fixed this by doing the cast after the shift (as is done elsewhere in the code). Signed-off-by: Andre Renaud Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 340ea370c2ce89d1c15fbf785460f2f74314ce58 Author: Hans-Jürgen Koch Date: Tue Apr 17 13:42:56 2007 -0400 [MTD] Driver for AT26Fxxx dataflash devices Add support for AT26Fxxx dataflash devices. These devices have a quite different commandset than the AT45xxx chips, which are handled by at91_dataflash.c, so a combined driver turned out to be more ugly than useful. Tested only on AT26F004. Signed-off-by: Hans-Jürgen Koch Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 408b483d9cc2d839ecbc9134958c42814865081c Author: Thomas Gleixner Date: Fri Apr 13 19:50:48 2007 +0200 [MTD] Fix length comparison in MEMREADOOB The ops.len member is not initialized, because it is unused for this operation. The length check needs to use ops.ooblen instead Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 1cf9827b6852d5d81130efbf2e777e50b7126d23 Author: Thomas Gleixner Date: Tue Apr 17 18:30:57 2007 +0100 [MTD] [NAND] Move ancient NAND chip support into a config option The support for obsolete ancient NAND chips adds .data size and one of the old ids conflicts with a modern one. Make the support for such chips depending on a config option. Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 90424de8d0646eaf7cddbdb111edaf429dea6042 Author: Thomas Gleixner Date: Thu Apr 5 11:44:05 2007 +0200 [MTD] [NAND] Use ecc.read/write_page_raw consequently Use the functions in the ecc structure instead of the default ones, so the override by the board driver is effective also for software ecc code paths. Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 8c60e5475d8ca614d712cd3e2fe7330480709e02 Author: sshahrom@micron.com Date: Wed Mar 21 18:48:02 2007 -0700 [MTD][NAND] Add Micron Manufacturer ID Add Micron Manufacturer ID. Signed-off-by: Shahrom Sharif Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 53043002ef6cc0369fd5c5fa0a257f290ba6a3a6 Author: Thomas Gleixner Date: Thu Apr 5 11:09:01 2007 +0200 [JFFS2] check node crc before doing anything else Check the node CRC on scan before doing anything else with the node. Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 873b6a230652803d1de480f5d3b802e4ffd0bcad Author: Ralf Baechle Date: Sat Mar 10 23:10:50 2007 +0000 [MTD] Fix dependencies for MIPS MTD drivers o A dependency on the processor architecture does not make sense; delete it. o The Alchemy and MTX drivers requires MTD_PARTITIONS and MTD_CFI to work, make those dependencies. Signed-off-by: Ralf Baechle Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit 19da63d1d2df393f8bf891d02e9960430f9178f8 Author: Ralf Baechle Date: Sat Mar 10 23:14:12 2007 +0000 [MTD] Alchemy cleanups Delete RCS $Id string and unused debug code. Signed-off-by: Ralf Baechle Signed-off-by: Thomas Gleixner Signed-off-by: David Woodhouse commit f142b3a4f64eca235f5890d9cecb68482f62ed26 Author: Jiri Kosina Date: Mon Apr 16 11:29:28 2007 +0200 HID: update copyright and authorship macro Updates Copyright and DRIVER_AUTHOR in HID and USB HID sources. Signed-off-by: Jiri Kosina commit 390cbb56a731546edc0f35fbc4c5045676467581 Author: Paul Mackerras Date: Fri Apr 13 10:46:21 2007 +1000 [POWERPC] Fix detection of loader-supplied initrd on OF platforms Commit 79c8541924a220964f9f2cbed31eaa9fdb042eab introduced code to move the initrd if it was in a place where it would get overwritten by the kernel image. Unfortunately this exposed the fact that the code that checks whether the values passed in r3 and r4 are intended to indicate the start address and size of an initrd image was not as thorough as the kernel's checks. The symptom is that on OF-based platforms, the bootwrapper can cause an exception which causes the system to drop back into OF. Previously it didn't matter so much if the code incorrectly thought that there was an initrd, since the values for start and size were just passed through to the kernel. Now the bootwrapper needs to apply the same checks as the kernel since it is now using the initrd data itself (in the process of copying it if necessary). This adds the code to do that. Signed-off-by: Paul Mackerras commit 98750261fbf6348517ef517f617014de8238475f Author: Kumar Gala Date: Thu Apr 12 18:01:34 2007 -0500 [POWERPC] Miscellaneous arch/powerpc Kconfig and platform/Kconfig cleanup * Cleaned up some whitespace in arch/powerpc/Kconfig * Moved sourcing of platforms/embedded6xx/Kconfig into platform/Kconfig * Moved sourcing of platforms/4xx/Kconfig into platform/Kconfig and disabled it * Removed EMBEDDEDBOOT since its not supported in arch/powerpc * Removed PC_KEYBOARD since its not used anywhere * Moved a few CONFIG options around in platform/Kconfig * Moved interrupt controllers into platform/Kconfig out of bus section Signed-off-by: Kumar Gala commit db9478086dc9c8b46b19f5308c49caff773110ff Author: Kumar Gala Date: Thu Apr 12 17:44:07 2007 -0500 [POWERPC] Convert 85xx platform to unified platform Kconfig Moved 85xx platform Kconfig over to being sourced by the unified arch/powerpc/platforms/Kconfig. Signed-off-by: Kumar Gala commit c8a55f3ddaa725a171385d9ffb416b183243856e Author: Kumar Gala Date: Thu Apr 12 17:35:54 2007 -0500 [POWERPC] Convert 8xx platform to unified platform Kconfig Moved 8xx platform Kconfig over to being sourced by the unified arch/powerpc/platforms/Kconfig. Also, cleaned up whitespace issues in 8xx Kconfig. Signed-off-by: Kumar Gala commit d6071f881fcd3c45c00ea1c2d13a5b01bbb6c412 Author: Kumar Gala Date: Thu Apr 12 16:53:32 2007 -0500 [POWERPC] Convert 82xx platform to unified platform Kconfig Moved 82xx platform Kconfig over to being sourced by the unified arch/powerpc/platforms/Kconfig. Also, cleaned up whitespace issues in 82xx Kconfig. Signed-off-by: Kumar Gala commit b5a4834692ab799a7a6b75a7f1e20e9f41a3126e Author: Kumar Gala Date: Thu Apr 12 15:46:21 2007 -0500 [POWERPC] Convert 83xx platform to unified platform Kconfig Moved 83xx platform Kconfig over to being sourced by the unified arch/powerpc/platforms/Kconfig. Signed-off-by: Kumar Gala commit 4a89f7fa7a32e35d3b5d956846cd2ae274db5ed2 Author: Kumar Gala Date: Thu Apr 12 15:41:26 2007 -0500 [POWERPC] Convert 86xx platform to unified platform Kconfig Moved 86xx platform Kconfig over to being sourced by the unified arch/powerpc/platforms/Kconfig. Signed-off-by: Kumar Gala commit 164a460d467a43e9f403cd4b76cec626dff99779 Author: Kumar Gala Date: Thu Apr 12 15:35:50 2007 -0500 [POWERPC] Ensure platform CONFIG options have correct dependencies We currently support TAU and CPU frequency scaling only on discrete (non-SOC) processors. Signed-off-by: Kumar Gala commit 0727702a3a6a9e33db4a4f9a038327d014753b6e Author: Joachim Fenkes Date: Thu Mar 22 17:00:46 2007 +0100 [POWERPC] ibmebus: change probe/remove interface from using loc-code to DT path In some cases, multiple OFDT nodes might share the same location code, so the location code is not a unique identifier for an OFDT node. Changed the ibmebus probe/remove interface to use the DT path of the device node instead of the location code. The DT path must be written into probe/remove right as it would appear in the "devspec" attribute of the ebus device: relative to the DT root, with a leading slash and without a trailing slash. One trailing newline will not hurt; multiple newlines will (like perl's chomp()). Example: Add a device "/proc/device-tree/foo@12345678" to ibmebus like this: echo /foo@12345678 > /sys/bus/ibmebus/probe Remove the device like this: echo /foo@12345678 > /sys/bus/ibmebus/remove Signed-off-by: Joachim Fenkes Signed-off-by: Paul Mackerras commit 370a908db154f51008cea41e67e7409efa251c7b Author: Benjamin Herrenschmidt Date: Thu Apr 12 15:30:23 2007 +1000 [POWERPC] DEBUG_PAGEALLOC for 64-bit Here's an implementation of DEBUG_PAGEALLOC for 64 bits powerpc. It applies on top of the 32 bits patch. Unlike Anton's previous attempt, I'm not using updatepp. I'm removing the hash entries from the bolted mapping (using a map in RAM of all the slots). Expensive but it doesn't really matter, does it ? :-) Memory hot-added doesn't benefit from this unless it's added at an address that is below end_of_DRAM() as calculated at boot time. Signed-off-by: Benjamin Herrenschmidt arch/powerpc/Kconfig.debug | 2 arch/powerpc/mm/hash_utils_64.c | 84 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 4 deletions(-) Signed-off-by: Paul Mackerras commit 88df6e90fa9782dbf44d936e44649afe271e4790 Author: Benjamin Herrenschmidt Date: Thu Apr 12 15:30:22 2007 +1000 [POWERPC] DEBUG_PAGEALLOC for 32-bit Here's an implementation of DEBUG_PAGEALLOC for ppc32. It disables BAT mapping and is only tested with Hash table based processor though it shouldn't be too hard to adapt it to others. Signed-off-by: Benjamin Herrenschmidt arch/powerpc/Kconfig.debug | 9 ++++++ arch/powerpc/mm/init_32.c | 4 +++ arch/powerpc/mm/pgtable_32.c | 52 +++++++++++++++++++++++++++++++++++++++ arch/powerpc/mm/ppc_mmu_32.c | 4 ++- include/asm-powerpc/cacheflush.h | 6 ++++ 5 files changed, 74 insertions(+), 1 deletion(-) Signed-off-by: Paul Mackerras commit ee4f2ea48674b6c9d91bc854edc51a3e6a7168c4 Author: Benjamin Herrenschmidt Date: Thu Apr 12 15:30:22 2007 +1000 [POWERPC] Fix 32-bit mm operations when not using BATs On hash table based 32 bits powerpc's, the hash management code runs with a big spinlock. It's thus important that it never causes itself a hash fault. That code is generally safe (it does memory accesses in real mode among other things) with the exception of the actual access to the code itself. That is, the kernel text needs to be accessible without taking a hash miss exceptions. This is currently guaranteed by having a BAT register mapping part of the linear mapping permanently, which includes the kernel text. But this is not true if using the "nobats" kernel command line option (which can be useful for debugging) and will not be true when using DEBUG_PAGEALLOC implemented in a subsequent patch. This patch fixes this by pre-faulting in the hash table pages that hit the kernel text, and making sure we never evict such a page under hash pressure. Signed-off-by: Benjamin Herrenchmidt arch/powerpc/mm/hash_low_32.S | 22 ++++++++++++++++++++-- arch/powerpc/mm/mem.c | 3 --- arch/powerpc/mm/mmu_decl.h | 4 ++++ arch/powerpc/mm/pgtable_32.c | 11 +++++++---- 4 files changed, 31 insertions(+), 9 deletions(-) Signed-off-by: Paul Mackerras commit 3be4e6990edf65624cfcbf8f7e33810626b2eefa Author: Benjamin Herrenschmidt Date: Thu Apr 12 15:30:21 2007 +1000 [POWERPC] Cleanup 32-bit map_page The 32 bits map_page() function is used internally by the mm code for early mmu mappings and for ioremap. It should never be called for an address that already has a valid PTE or hash entry, so we add a BUG_ON for that and remove the useless flush_HPTE call. Signed-off-by: Benjamin Herrenschmidt arch/powerpc/mm/pgtable_32.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) Signed-off-by: Paul Mackerras commit a741e67969577163a4cfc78d7fd2753219087ef1 Author: Benjamin Herrenschmidt Date: Tue Apr 10 17:09:37 2007 +1000 [POWERPC] Make tlb flush batch use lazy MMU mode The current tlb flush code on powerpc 64 bits has a subtle race since we lost the page table lock due to the possible faulting in of new PTEs after a previous one has been removed but before the corresponding hash entry has been evicted, which can leads to all sort of fatal problems. This patch reworks the batch code completely. It doesn't use the mmu_gather stuff anymore. Instead, we use the lazy mmu hooks that were added by the paravirt code. They have the nice property that the enter/leave lazy mmu mode pair is always fully contained by the PTE lock for a given range of PTEs. Thus we can guarantee that all batches are flushed on a given CPU before it drops that lock. We also generalize batching for any PTE update that require a flush. Batching is now enabled on a CPU by arch_enter_lazy_mmu_mode() and disabled by arch_leave_lazy_mmu_mode(). The code epects that this is always contained within a PTE lock section so no preemption can happen and no PTE insertion in that range from another CPU. When batching is enabled on a CPU, every PTE updates that need a hash flush will use the batch for that flush. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit e4ee3891db35aa9a069bb403c2a66a8fbfa274d6 Author: Benjamin Herrenschmidt Date: Wed Apr 11 16:13:19 2007 +1000 [POWERPC] Alignment exception uses __get/put_user_inatomic Make the alignment exception handler use the new _inatomic variants of __get/put_user. This fixes erroneous warnings in the very rare cases where we manage to have copy_tofrom_user_inatomic() trigger an alignment exception. Signed-off-by: Benjamin Herrenschmidt arch/powerpc/kernel/align.c | 56 ++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 25 deletions(-) Signed-off-by: Paul Mackerras commit e68c825bb016703eda94aac99be96de73b482d61 Author: Benjamin Herrenschmidt Date: Wed Apr 11 16:13:19 2007 +1000 [POWERPC] Add inatomic versions of __get_user and __put_user Those are needed by things like alignment exception fixup handlers since those can now be triggered by copy_tofrom_user_inatomic. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 06533e28c9e8b252fbfb6858647afe48b5147e16 Author: Milind Arun Choudhary Date: Wed Apr 4 22:07:40 2007 +0530 [POWERPC] Clean up unused ROUND_UP, NAME_OFFSET macros in arch/powerpc Unused ROUND_UP, NAME_OFFSET macro cleanup Signed-off-by: Milind Arun Choudhary Signed-off-by: Paul Mackerras commit e48059225c2edc6f1e5a2008261f1efdf606f247 Author: Olaf Hering Date: Wed Apr 4 18:20:04 2007 +0200 [POWERPC] Add correct interrupt property for pegasos ide The firmware assigns irq 20/21 to the VIA IDE device on Pegasos. But the required interrupt is 14/15. Maybe someone confused decimal vs. hexadecimal values. Signed-off-by: Olaf Hering Signed-off-by: Paul Mackerras commit 9c1a2bae0cc52b21121ea2380a2db0294ad0d8e7 Author: Stephen Rothwell Date: Tue Apr 3 22:50:59 2007 +1000 [POWERPC] Rename get_property to of_get_property: the last one This also fixes a bug where a property value was being modified in place. Signed-off-by: Stephen Rothwell Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit d05c7a80cf39ae7d0f8d0c3e47c93d51fcd393d3 Author: Stephen Rothwell Date: Tue Apr 3 22:40:39 2007 +1000 [POWERPC] Rename get_property to of_get_property: drivers Signed-off-by: Stephen Rothwell Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit c4f55b394505fff6ad831d17e36e02dde1c3a8d7 Author: Stephen Rothwell Date: Tue Apr 3 22:39:14 2007 +1000 [POWERPC] Rename get_property to of_get_property: sound Signed-off-by: Stephen Rothwell Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit e2eb63927bfcb54232163bfec32440246fd44457 Author: Stephen Rothwell Date: Tue Apr 3 22:26:41 2007 +1000 [POWERPC] Rename get_property to of_get_property: arch/powerpc Signed-off-by: Stephen Rothwell Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit ceef87782a9452eeeca774e65d7f4e06455780a3 Author: Stephen Rothwell Date: Tue Apr 3 22:24:06 2007 +1000 [POWERPC] Rename get_property to of_get_property: include Signed-off-by: Stephen Rothwell Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 721151d004dcf01a71b12bb6b893f9160284cf6e Author: Paul Mackerras Date: Tue Apr 3 21:24:02 2007 +1000 [POWERPC] Allow drivers to map individual 4k pages to userspace Some drivers have resources that they want to be able to map into userspace that are 4k in size. On a kernel configured with 64k pages we currently end up mapping the 4k we want plus another 60k of physical address space, which could contain anything. This can introduce security problems, for example in the case of an infiniband adaptor where the other 60k could contain registers that some other program is using for its communications. This patch adds a new function, remap_4k_pfn, which drivers can use to map a single 4k page to userspace regardless of whether the kernel is using a 4k or a 64k page size. Like remap_pfn_range, it would typically be called in a driver's mmap function. It only maps a single 4k page, which on a 64k page kernel appears replicated 16 times throughout a 64k page. On a 4k page kernel it reduces to a call to remap_pfn_range. The way this works on a 64k kernel is that a new bit, _PAGE_4K_PFN, gets set on the linux PTE. This alters the way that __hash_page_4K computes the real address to put in the HPTE. The RPN field of the linux PTE becomes the 4k RPN directly rather than being interpreted as a 64k RPN. Since the RPN field is 32 bits, this means that physical addresses being mapped with remap_4k_pfn have to be below 2^44, i.e. 0x100000000000. The patch also factors out the code in arch/powerpc/mm/hash_utils_64.c that deals with demoting a process to use 4k pages into one function that gets called in the various different places where we need to do that. There were some discrepancies between exactly what was done in the various places, such as a call to spu_flush_all_slbs in one case but not in others. Signed-off-by: Paul Mackerras commit 1a38147ed0737a9c01dbf5f2ca47fd2a0aa5cb55 Author: Stephen Rothwell Date: Tue Apr 3 10:58:52 2007 +1000 [POWERPC] Make struct property's value a void * Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 9213feea6e197f8507ec855337798cc3388f5570 Author: Stephen Rothwell Date: Tue Apr 3 10:57:48 2007 +1000 [POWERPC] Rename prom_n_size_cells to of_n_size_cells This is more consistent and gets us closer to the Sparc code. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit a8bda5dd4f99d6469f3c0dc362db3cce8a4d6416 Author: Stephen Rothwell Date: Tue Apr 3 10:56:50 2007 +1000 [POWERPC] Rename prom_n_addr_cells to of_n_addr_cells This is more consistent and gets us closer to the Sparc code. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 7a92f74f98bde8498c98aad6cac5da5a87dd0bf4 Author: Stephen Rothwell Date: Tue Apr 3 10:55:39 2007 +1000 [POWERPC] Rename device_is_compatible to of_device_is_compatible This is more consistent and gets us closer to the Sparc code. We add a device_is_compatible define for compatibility during the change over. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 0e56efc7dcd1eb5004363e52bdbe801783245638 Author: Stephen Rothwell Date: Tue Apr 3 10:54:01 2007 +1000 [POWERPC] Rename get_property to of_get_property This is more consistent and gets us closer to the Sparc code. We add a get_property define for compatibility during the change over. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit a7edd0e676d51145ae634a2acf7a447e319200fa Author: Stephen Rothwell Date: Tue Apr 3 10:52:17 2007 +1000 [POWERPC] get_property returns const This just tidies up some of the remains. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 6c2d046980299d52d78b2738ad7f11fc593dea75 Author: Olaf Hering Date: Mon Apr 2 14:33:27 2007 +0200 [POWERPC] Fix modalias content in sysfs for macio devices Currently the buf pointer is advanced too far during each iteration. Also terminate the string with a newline. Signed-off-by: Olaf Hering Signed-off-by: Paul Mackerras commit 9414715a7bbb45450015e9bc2676d85d919d08d4 Author: Olaf Hering Date: Sat Mar 31 17:08:23 2007 +0200 [POWERPC] Autodetect serial console on efika Efika boards have to be booted with console=ttyPSC0 unless there is a graphics card plugged in. Detect if the firmware stdout is the serial connector. Signed-off-by: Olaf Hering Acked-by: Grant Likely Signed-off-by: Paul Mackerras commit 02cc51149f99e3c6c106e1e16dcc2e016b1bc3b5 Author: Milton Miller Date: Thu Mar 29 07:31:41 2007 -0600 [POWERPC] bootwrapper: Decompress less, check more Our kernels put everything in the first load segment, and we read that. Instead of decompressing to the end of the gzip stream or supplied image and hoping we get it all, decompress the expected size and complain if it is not available. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras commit 1383a34fafeb9f49975fc33783960729fbd2cddf Author: Milton Miller Date: Wed Mar 28 02:21:04 2007 -0600 [POWERPC] bootwrapper: no-gzip fixes Commit a9903811bf8d130a26004f9cb27b66513a267908 missed two uses of the the .gz suffix in the wrapper script and didn't clean the additonal possibly cached files. Signed-off-by: Milton Miller Acked-by: Scott Wood Signed-off-by: Paul Mackerras commit f4bdbfc41b3cad813745f64fb849c298770da517 Author: Milton Miller Date: Wed Mar 28 02:21:02 2007 -0600 [POWERPC] bootwrapper: missing relocation in crt0.S crt0.S had provisions to provide run address relocaton to got2 and cache flush, but not on the bss clear or stack pointer load. Apply the same fixup for them. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras commit d6f1d2a9a83f447af6c210c8268ce117772da77f Author: Mark A. Greer Date: Tue Mar 27 15:31:41 2007 -0700 [POWERPC] Routine to find the devtree node of a linux,phandle Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras commit 88e687313e683ee006152d611b95f40900e3bce0 Author: Mark A. Greer Date: Tue Mar 27 15:29:50 2007 -0700 [POWERPC] Move bootwrapper ELF parsing routines to a file The ELF parsing routines local to arch/powerpc/boot/main.c are useful to other callers therefore move them to their own file. Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras commit 5e41763ae9b4b6335fab88da85600f16d7a5a7b5 Author: Giuliano Pochini Date: Mon Mar 26 21:40:28 2007 -0800 [POWERPC] Fix breakage caused by 72486f1f8f0a2bc828b9d30cf4690cf2dd6807fc 72486f1f8f0a2bc828b9d30cf4690cf2dd6807fc inverted the sense for enabling hotplug CPU controls without reference to any other architecture other than i386, ia64 and PowerPC. This left everyone else without hotplug CPU control. Fix powerpc for this brain damage. (akpm: patch adapted from rmk's ARM fix. Changelog stolen from rmk) Signed-off-by: Giuliano Pochini Cc: Benjamin Herrenschmidt Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras commit 6590a0197cc89e663d884503073dbe235b3bd860 Author: Robert P. J. Day Date: Mon Mar 26 21:40:27 2007 -0800 [POWERPC] Remove unused header file: arch/ppc/syslib/cpc710.h. Signed-off-by: Robert P. J. Day Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras commit 0fbbeba2427a842a1a4ac9f379ca2ca37ea907eb Author: Bart Van Assche Date: Mon Mar 26 21:40:29 2007 -0800 [POWERPC] Ocotea board: ntpd complains that the frequency error exceeds the tolerance Lifted from http://bugzilla.kernel.org/show_bug.cgi?id=8182 Steps to reproduce: - Boot an Ocotea board with the mainline 2.6.20.1 kernel. - Create an /etc/ntp.conf file with at least one NTP server and iburst mode set. - Issue the command "ntpd -g". - Wait about two minutes. - Verify ntpd's status via "ntpq -pn" and by looking in /var/log/ntp. This fixes this problem by adjusting the expected clock frequency. Cc: Kumar Gala Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras commit 6e1af384f1c1742ae6d86bbf779d4fa020c509bc Author: Scott Wood Date: Mon Mar 26 15:52:24 2007 -0500 [POWERPC] bootwrapper: Add dt_xlate_reg(), and use it to find serial registers. dt_xlate_reg() uses the ranges properties of a node's parentage to find the absolute physical address of the node's registers. The ns16550 driver uses this when no virtual-reg property is found. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit dbf8eefa2b814d6922492120bfa46d4bc42ceb20 Author: Christoph Hellwig Date: Fri Mar 23 15:12:26 2007 +0100 [POWERPC] spufs: don't yield CPU in spu_yield There is no reason to yield the CPU in spu_yield - if the backing thread reenters spu_run it gets added to the end of the runqueue for it's priority. So the yield is just a slowdown for the case where we have higher priority contexts waiting. Signed-off-by: Christoph Hellwig Acked-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 28066ae91b2050d79531a6f2d5e35dd8f666bafd Author: Geert Uytterhoeven Date: Fri Mar 23 14:06:43 2007 +0100 [POWERPC] CBE thermal support on PS3 I wanted to enable CBE_THERM on PS3. So I had to enable CBE_RAS first. But the resulting kernel doesn't link, as cbe_regs.c isn't compiled for non-PPC_CELL_NATIVE. CBE_RAS should depend on PPC_CELL_NATIVE; this makes it so. Signed-off-by: Geert Uytterhoeven Acked-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit a5bcbcaff24185695b296d4aa2278c5f890a7987 Author: Sonny Rao Date: Thu Mar 22 22:44:31 2007 -0400 [POWERPC] Remove stale comment from head_64.S This is now inaccurate because we may not have entered prom_init() and r3 is overwritten immediately anyway. Signed-off-by: Sonny Rao Signed-off-by: Paul Mackerras commit 8545cd201134860b1eb72578419f5cbd4c0789c0 Author: Olaf Hering Date: Fri Mar 23 01:11:59 2007 +0100 [POWERPC] Remove unused inclusion of linux/ide.h Remove unneeded inclusion of linux/ide.h It does not compile with CONFIG_BLOCK=n. Remove asm/ide.h from ksyms file, it gets included earlier via linux/ide.h. Compile tested with all defconfig files. Signed-off-by: Olaf Hering Signed-off-by: Paul Mackerras commit e51b85dcf56c87772b47a0781e6cfa88848b50b8 Author: Tony Vroon Date: Thu Mar 22 23:31:08 2007 +0000 [POWERPC] PMU LED whitelisting of PowerMac 7,2 and 7,3 This allows the PMU LED on both a PowerMac 7,2 (Dual G5 2.0GHz, June 2003) and a PowerMac 7,3 (Dual G5 2.0GHz, June 2004) to be controlled. The physical LED is never off, unlike an iBook/PowerBook LED. It is rather dim ("off") or very bright ("on"). Signed-off-by: Tony Vroon Signed-off-by: Paul Mackerras commit c3b9d9ab96becbd901bd31db0ea8174fcc02e83f Author: Olaf Hering Date: Thu Mar 22 23:14:07 2007 +0100 [POWERPC] Fix link errors when EEH is disabled Fix link errors with CONFIG_EEH=n: arch/powerpc/platforms/built-in.o: In function `.pcibios_fixup_new_pci_devices': (.text+0x41c8): undefined reference to `.eeh_add_device_tree_late' arch/powerpc/platforms/built-in.o: In function `.init_phb_dynamic': (.text+0x4280): undefined reference to `.eeh_add_device_tree_early' arch/powerpc/platforms/built-in.o: In function `.pcibios_remove_pci_devices': (.text+0x42fc): undefined reference to `.eeh_remove_bus_device' arch/powerpc/platforms/built-in.o: In function `.pcibios_add_pci_devices': (.text+0x43c0): undefined reference to `.eeh_add_device_tree_early' arch/powerpc/platforms/built-in.o: In function `.pSeries_final_fixup': (.init.text+0xb4): undefined reference to `.pci_addr_cache_build' make[1]: *** [.tmp_vmlinux1] Error 1 Signed-off-by: Olaf Hering Signed-off-by: Paul Mackerras commit 3af82a8b00f98ca54e4c860eeb2b9ede6d8cadf4 Author: David Gibson Date: Thu Mar 22 17:02:21 2007 +1100 [POWERPC] Clean up zImage handling of the command line This cleans up how the zImage code manipulates the kernel command line. Notable improvements from the old handling: - Command line manipulation is consolidated into a new prep_cmdline() function, rather than being scattered across start() and some helper functions - Less stack space use: we use just a single global command line buffer, which can be initialized by an external tool as before, we no longer need another command line sized buffer on the stack. - Easier to support platforms whose firmware passes a commandline, but not a device tree. Platform code can now point new loader_info fields to the firmware's command line, rather than having to do early manipulation of the /chosen bootargs property which may then be rewritten again by the core. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 27fbaa9702e548e74dffd21855769f6cedad42bd Author: David Gibson Date: Thu Mar 22 17:02:21 2007 +1100 [POWERPC] Add device tree utility functions to zImage This patch adds a library of useful device tree manipulation functions to the zImage library, for use by platform code. These functions are based on the hooks already in dt_ops, so they're not dependent on a particular device tree implementation. This patch also slightly streamlines the code in main.c using these new functions. This is a consolidation of my work in this area with Scott Wood's patches to a very similar end. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 3467bfd340f9ad48f3732415533a2e9c18240b62 Author: Olof Johansson Date: Thu Mar 22 09:34:13 2007 -0500 [POWERPC] Use mtocrf instruction in asm when CONFIG_POWER4_ONLY=y mtocrf is a faster single-field mtcrf (move to condition register fields) instruction available in POWER4 and later processors. It can make quite a difference in performance on some implementations, so use it for CONFIG_POWER4_ONLY builds. Signed-off-by: Olof Johansson Signed-off-by: Paul Mackerras commit 569975591c5530fdc9c7a3c45122e5e46f075a74 Author: Jake Moilanen Date: Thu Mar 29 08:44:02 2007 -0500 [POWERPC] DMA 4GB boundary protection There are many adapters which cannot handle DMAing across any 4 GB boundary. For instance, the latest Emulex adapters. This normally is not an issue as firmware gives dma-windows under 4gigs. However, some of the new System-P boxes have dma-windows above 4gigs, and this present a problem. During initialization of the IOMMU tables, the last entry at each 4GB boundary is marked as used. Thus no mappings can cross the boundary. If a table ends at a 4GB boundary, the entry is not marked as used. A boot option to remove this 4GB protection is given w/ protect4gb=off. This exposes the potential issue for driver and hardware development purposes. Signed-off-by: Jake Moilanen Acked-by: Olof Johansson Signed-off-by: Paul Mackerras commit 1f9209cfe06be715b82075e79c9aab3c5b714010 Author: Sylvain Munaut Date: Mon Feb 12 23:13:27 2007 +0100 [POWERPC] Add uevent handler for of_platform_bus Adding this handler allow userspace to properly handle the module autoloading. The generation of the uevent itself is now common to all bus using of_device, so not much code here. Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras commit cc11645b9d04c3df5180620bded522e14e619604 Author: Sylvain Munaut Date: Mon Feb 12 23:13:26 2007 +0100 [POWERPC] powermac: Use the new of_device common uevent handler The generation of the uevent is now common to all bus using of_device. Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras commit eb0cb8a07e320ed3237789cc4f29858338d14d8e Author: Sylvain Munaut Date: Mon Feb 12 23:13:25 2007 +0100 [POWERPC] Add a unified uevent handler for bus based on of_device This common uevent handler allow the several bus types based on of_device to generate the uevent properly and avoiding code duplication. This handlers take a struct device as argument and can therefore be used as the uevent call directly if no special treatment is needed for the bus. Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras commit 46386b587086c8d2698222a031bf749688464032 Author: Simon Budig Date: Mon Mar 12 13:52:04 2007 +0100 HID: introduce proper zeroing of unused bits in output reports Some HID devices are looking on the unused bits in the HID reports they receive. This is violating the specification, but we want to make those devices work. Well-behaving devices are unaffected, as they don't care about the unused bits. If bitsused % 8 is 0 all bits in data[] get used and we don't need to clear anything. Otherwise (bitsused % 8) bits of the last byte get used. By shifting 1 for (bitsused % 8) bits and subtracting 1 we create a mask consisting of (bitsused % 8) ones and remaining zeroes. By ANDing we clear the upper unused bits. Signed-off-by: Simon Budig Signed-off-by: Jiri Kosina commit 42cfb632f0210caa7d979666058075b06fc4680c Author: Sam Liddicott Date: Tue Apr 10 15:50:45 2007 +0200 USB HID: add support for WiseGroup MP-8800 Quad Joypad This adds support for WiseGroup Quad Joypad (0x0925/0x8800). The same quirks as for Dual Joypad (0x0925/0x8866) are needed. Signed-off-by: Sam Liddicott Signed-off-by: Jiri Kosina commit 320c01500c6ecb189c577b921a2357c7a56aaebc Author: Jiri Kosina Date: Tue Apr 10 16:08:34 2007 +0200 USB HID: add FF support for Logitech Force 3D Pro Joystick This patch adds support for Logitech Force 3D Pro Joystick (0x046d/0xc286) to hid-lgff driver. Device ID reported by Richard Bolkey Signed-off-by: Jiri Kosina commit 713c8aad6b7202671ce1ac6109f6b48d8223e938 Author: Pete Zaitcev Date: Fri Apr 6 14:33:18 2007 +0200 USB HID: numlock quirk for dell W7658 keyboard On Dell W7658 keyboard, when BIOS sets NumLock LED on, it survives the takeover by kernel and thus confuses users. Eating of an increasibly scarce quirk bit is unfortunate. We do it for safety, given the history of nervous input devices which crash if anything unusual happens. Signed-off-by: Pete Zaitcev Signed-off-by: Jiri Kosina commit 85cbea3952135ecad4b47cb6cc57e25279129e2d Author: Jiri Kosina Date: Thu Apr 5 12:23:09 2007 +0200 USB HID: Logitech MX3000 keyboard needs report descriptor quirk Logitech MX3000 contains report descriptor which doesn't cover usages above 0x28c, but emits such usages. Report descriptor needs fixing in the very same way as with receivers shipped with S510 keyboards. This patch also adds a few mappings for multimedia keys that S510 didn't emit. Signed-off-by: Jiri Kosina commit bf892e60d0b32989bc55ef64cd6557a49806f0cf Author: Jiri Kosina Date: Wed Apr 4 10:56:12 2007 +0200 USB HID: extend quirk for Logitech S510 keyboard Logitech S510 keyboard is shipped with USB receivers with various product ids, all need their report descriptor to be fixed. This adds PID 0xc50c. Reported by Christophe Colombier in kernel.org bugzilla #7352 Signed-off-by: Jiri Kosina commit 5d6341c606b9eb62fbaa7b2a0da82ac851bf0fc4 Author: Dmitry Torokhov Date: Wed Apr 4 10:40:57 2007 +0200 USB HID: usbkbd/usbmouse - handle errors when registering devices Handle errors when registering input devices in usbkbd/usbmouse. Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina commit 66df514b1dbf51a0a02a8abe1219e46e49710aea Author: Daniel P. Engel Date: Tue Apr 3 23:43:46 2007 +0200 USB HID: add QUIRK_HIDDEV for Belkin Flip KVM Add HID_QUIRK_HIDDEV for the Belkin Flip USB KVM, which provides for software control of switching via a HID class interface. It overloads three HID LED usages, two of which aren't mapped in the ev_dev input subsection, and which it doesn't make sense to map. In order to force the creation of a hiddev device for controlling the Flip, this quirk flag is needed. Signed-off-by: Daniel P. Engel Signed-off-by: Jiri Kosina commit daa0bc902c8e9476673b47d3b59c9bb922843563 Author: Chris Clayton Date: Sun Apr 1 23:07:55 2007 +0200 HID: enable dead keys on a belkin wireless keyboard Belkin Wireless keyboard, model number F8E849KYBD, USB ID 1020:0006, FCCID: K7SF8E849KYBD emits usages 0x03a-0x03c from Consumer usage page. As of HUT v1.12, these are marked as reserved. If any conflict arises later, the mapping could be made conditional on VID/PID. Signed-off-by: Chris Clayton Signed-off-by: Jiri Kosina commit 38d4b89e274c7eed99fcf6c3f8686f70edd6ab7c Author: Ronny Peine Date: Tue Mar 27 14:37:44 2007 +0200 USB HID: Thustmaster firestorm dual power v1 support This patch adds support for version 1 of Thustmaster firestorm dual power (0x44f/0xb300). Signed-off-by: Ronny Peine Signed-off-by: Jiri Kosina commit 4cbe7d28f2c63e801d651b3b56f953a5ff5ae70f Author: Paul Walmsley Date: Tue Mar 20 19:23:16 2007 +0100 USB HID: specify explicit size for hid_blacklist.quirks Explicitly specify the size of the hid_blacklist quirks member, to guard against surprises on architectures where unsigned ints aren't 32 bits long. Signed-off-by: Paul Walmsley Signed-off-by: Jiri Kosina commit 5e2a55f25d255a356bdaf2cb04c71b8d76c307a8 Author: Alan Stern Date: Tue Mar 20 19:03:31 2007 +0100 USB HID: fix retry & reset logic The USB HID driver fails to reset its error-retry timeout when there has been a long time interval between I/O errors with no successful URB completions in the meantime. As a result, the very next error would trigger an immediate reset, even if it was a chance event occurring long after the previous error. More USB keyboards and mice than one might expect end up getting I/O errors. Almost always this results from hardware problems of one sort of another. For example, people attach the device to a USB extension cable, which degrades the signal. Or they simply have poor quality cables to begin with. Or they use a KVM switch which doesn't handle USB messages correctly. Etc... There have been reports from several users in which these I/O errors would occur more or less randomly, at intervals ranging from seconds to minutes. The error-handling code in hid-core.c was originally meant for situations where a single outage would persist for a few hundred ms (electromagnetic interference, for example). It didn't work right when these more sporadic errors occurred, because of a flaw in the logic which this patch fixes. This patch (as873) fixes that oversight. Signed-off-by: Alan Stern Signed-off-by: Jiri Kosina commit 48b4554aca28f721d3dfbab077c05032b3d1cd31 Author: Jiri Kosina Date: Tue Apr 3 23:39:37 2007 +0200 USB HID: consolidate vendor/product ids The vendor/product IDs for the purposes of hid_blacklist got scathered around the hid-core.c in a rather random way over the time. Move all the related definitions at the beginning of the file, and make them sorted again. Sort also hid_blacklist properly. Signed-off-by: Jiri Kosina commit 6db3dfefa28739e7c9c60809c3a5aef7cc088b97 Author: Jiri Kosina Date: Thu Mar 8 16:47:49 2007 +0100 USB HID: move usbhid code from drivers/usb/input to drivers/hid/usbhid Separate usbhid code into dedicated drivers/hid/usbhid directory as discussed previously with Greg, so that it eases maintaineance process. Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman commit 7b153f366867a3b70daeaf3c6074e4a0594057a7 Author: malattia@linux.it Date: Mon Apr 9 19:31:25 2007 +0200 sony-laptop: sonypi backward compatibility code Compatibility code to allow old sonypi bound userspace apps to still work. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 3d2b8a9f2c26bc0fe03b3545d07245798b1b81b9 Author: malattia@linux.it Date: Mon Apr 9 19:31:16 2007 +0200 sony-laptop: update documentation and Kconfig help Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit f6119b027578c21b544a98fd67e5f0b7e4fbea7d Author: malattia@linux.it Date: Mon Apr 9 19:31:06 2007 +0200 sony-laptop: sanitize printks Unify printks to resemble a unique driver. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 49a11deade3c1d9e2d7c88d25899b3a9174d048e Author: malattia@linux.it Date: Mon Apr 9 19:28:56 2007 +0200 sony-laptop: additional platform attributes coming from SNY6001 Register additional platform attributes coming from the SPIC (sonypi) driver. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 1549ee6fb122400c0767b5f3da2c42abbc4f750a Author: malattia@linux.it Date: Mon Apr 9 10:19:08 2007 +0200 sony-laptop: Unify the input subsystem event forwarding SNC and SPIC events are forwarded to the same input devices and are thus handled together. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 33a04454527edd33d4a6332a2944d2b4f46fbb18 Author: malattia@linux.it Date: Mon Apr 9 19:26:03 2007 +0200 sony-laptop: Add SNY6001 device handling (sonypi reimplementation) Reimplement sonypi using ACPI only functions. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit b9a218b738c5c2387f666731b81a4376021d681e Author: malattia@linux.it Date: Mon Apr 9 10:19:06 2007 +0200 sony-laptop: Add debug macros also used by the sonypi reimplementation Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 56b8756b3bc8812837a21f3e066dba1b489e071e Author: malattia@linux.it Date: Mon Apr 9 10:19:05 2007 +0200 sony-laptop: Prepare the platform driver for multiple users. Both the SNC and SPIC device drivers will create attributes and thus there's the need to have an internal usage count to avoid re-registering or de-registering at the wrong time. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 59b19106f3f20487093ea06c8220426147cf7835 Author: malattia@linux.it Date: Mon Apr 9 10:19:04 2007 +0200 sony-laptop: Remove ACPI references from variable and function names. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown commit 0ecbc81adfcb9f15f86b05ff576b342ce81bbef8 Author: Rodolfo Giometti Date: Mon Mar 26 21:45:43 2007 -0800 [MTD] [NOR] Support for auto locking flash on power up Auto unlock sectors on resume for auto locking flash on power up. Signed-off-by: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 8dc64fca75b631142f282047d7f6ae9e8af82543 Author: Robert P. J. Day Date: Mon Mar 26 21:45:41 2007 -0800 [JFFS2] Delete everything related to obsolete JFFS2_PROC option Delete everything related to the apparently non-existent kernel config option JFFS2_PROC. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 68aa0fa87f6d4b2f5e8ad39ecaec8bba9137bb3d Author: Marc St-Jean Date: Mon Mar 26 21:45:41 2007 -0800 [MTD] PMC MSP71xx flash/rootfs mappings Add flash and rootfs mappings for the PMC-Sierra MSP71xx devices. This patch references some platform support files previously submitted to the linux-mips@linux-mips.org list. Signed-off-by: Marc St-Jean Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse commit 756970ad4bb93027a60da2de9b43d094b7f387a2 Author: Henrique de Moraes Holschuh Date: Thu Mar 29 01:58:44 2007 -0300 ACPI: thinkpad-acpi: update MAINTAINERS Update MAINTAINERS file for the ibm-acpi -> thinkpad-acpi renaming. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 643f12dbb660e139fbaea268f3e3ce4d7d594b8f Author: Henrique de Moraes Holschuh Date: Thu Mar 29 01:58:43 2007 -0300 ACPI: thinkpad-acpi: cleanup after rename Cleanup documentation, driver strings and other misc stuff, now that the driver is named "thinkpad-acpi". Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit d903ac5455102b13d0e28d6a39f640175fb4cd4d Author: Henrique de Moraes Holschuh Date: Thu Mar 29 01:58:42 2007 -0300 ACPI: thinkpad-acpi: add compatibility MODULE_ALIAS entry Add a ibm_acpi module alias for userpace, so that modprobe ibm_acpi will still load the correct driver. This alias can be removed in the future, probably two years from now if nothing warrants removing it sooner. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 85998248b2e8c6ae7d3ad1fa7b059aed22205ec4 Author: Henrique de Moraes Holschuh Date: Thu Mar 29 01:58:41 2007 -0300 ACPI: thinkpad-acpi: cleanup Kconfig for thinkpad-acpi Since ibm-acpi was renamed to thinkpad-acpi, rename and update its Kconfig entries and Kconfig-related symbols accordingly. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit f21f85de4b3b9ad4a671fb19a889c16db2ea38b2 Author: Henrique de Moraes Holschuh Date: Thu Mar 29 01:58:40 2007 -0300 ACPI: ibm-acpi: rename driver to thinkpad-acpi Rename the ibm-acpi driver to thinkpad-acpi. ThinkPads are not even made by IBM anymore, so it is high time to rename the driver... The name thinkpad-acpi was used sometime ago by a thinkpad-specific hotkey driver by Erik Rigtorp, around the 2.6.8-2.6.10 time frame. The driver apparently never got merged into mainline (it did make some trips through -mm). ibm-acpi was merged soon after, making its debut in 2.6.10. The reuse of the thinkpad-acpi name shouldn't be a problem as far as user confusion goes, as Erik's thinkpad-acpi apparently didn't get widespread use in the Linux ThinkPad community and most hits for thinkpad-acpi in google point to ibm-acpi anyway. Erik, if you read this, please consider the reuse of the thinkpad-acpi name as a compliment to your effort to make ThinkPads more useful to all of us. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit edfac96a92b88d3b0b53e3f8231b74beee9ecd1d Author: Jon Loeliger Date: Tue Mar 20 11:18:46 2007 -0500 [POWERPC] 85xx: Add initial MPC8544DS basic port defconfig. Signed-off-by: Xianghua Xiao Signed-off-by: Roy Zang Signed-off-by: York Sun Signed-off-by: Andy Fleming Signed-off-by: Jon Loeliger Signed-off-by: Kumar Gala commit d93daf848161043c06b665bbbd4bc22ef0247065 Author: Jon Loeliger Date: Tue Mar 20 11:19:10 2007 -0500 [POWERPC] 85xx: Add initial MPC8544 DS platform files. This patch provides the basic MPC8544 DS platform code and config. Follow-up patches will add peripherals such as PCI and SATA. Signed-off-by: Xianghua Xiao Signed-off-by: Roy Zang Signed-off-by: York Sun Signed-off-by: Andy Fleming Signed-off-by: Jon Loeliger Signed-off-by: Kumar Gala commit 23308c54d559a210019a576c5741cfb762af69d6 Author: Michael Barkowski Date: Mon Mar 19 09:15:28 2007 -0500 [POWERPC] 83xx: Add MPC832x RDB board support. Add support for the MPC8323E Reference Development Board (RDB). The board is a mini-ITX reference board with 64M DDR2, 16M flash, USB, PCI, 10/100 ethernet, serial, and phone ports. Signed-off-by: Michael Barkowski Signed-off-by: Kim Phillips Signed-off-by: Kumar Gala commit 62a6d7fd9bc1d85f9aae734c46234e88fa839db0 Author: Randy Dunlap Date: Mon Mar 26 21:38:49 2007 -0800 ACPI: dock: use NULL for pointer Use NULL instead of 0 for pointers: drivers/acpi/dock.c:677:75: warning: Using plain integer as NULL pointer Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Len Brown commit 1a4d9399a921c287eb175d29f288d1d92d02b302 Author: Kumar Gala Date: Thu Mar 15 10:01:14 2007 -0500 [POWERPC] 83xx: Removed dead header mpc832x_mds.h was exporting a function that didn't exist anymore. Once removed the header had no purpose. Signed-off-by: Kumar Gala commit de74f70360353929009f8611f3bb8fbb4f030527 Author: Timur Tabi Date: Thu Mar 15 09:48:53 2007 -0500 [POWERPC] QE: fix invalid pointer usage in ucc_slow_init() In two places, ucc_slow_init() passes a physical address instead of the virtual address to functions that were expecting the latter, causing a kernel panic. us_info->regs contains the physical address of the UCC register set. The registers are ioremap'd to kernel space, and the virtual pointers are stored in us_regs. The code was using us_info->regs when it should have been using us_regs. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala commit 8b03336752e664880182a8f50e1807edc7fe93d4 Author: Robert P. J. Day Date: Sat Mar 10 16:17:28 2007 -0500 [POWERPC] 83xx: Delete unused header file. Delete apparently unused header file arch/powerpc/platforms/83xx/mpc834x_itx.h. Signed-off-by: Robert P. J. Day Signed-off-by: Kumar Gala commit 7d776cb596994219584257eb5956b87628e5deaf Author: Timur Tabi Date: Mon Mar 12 15:40:27 2007 -0500 [POWERPC] QE: automatically select QE options Change the Kconfig files so that the Freescale QE options are automatically selected if a QE device is selected. Previously, you'd need to manually select UCC_FAST if you want any "fast" UCC devices, such as Gigabit Ethernet. Now, the QE Gigabit Ethernet option is always available if the device has a QE, and UCC_FAST is automatically enabled. A side-effect is that the "QE Options" menu no longer exists. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala commit 9eb90a0c3b333e27db74412833a36da3f27da6a3 Author: Zang Roy-r61911 Date: Fri Mar 9 13:27:28 2007 +0800 [POWERPC] 86xx/85xx: Unify Freescale PCI Express memory map registers structure Unify PCI Express memory map registers structure define to arch/pwoerpc/sysdev/fsl_pcie.h for Freescale 85xx/86xx processor family. Signed-off-by: Roy Zang Signed-off-by: Kumar Gala commit 3e4e97f42e134e1fe46bdf36bd5d874f5b4f8755 Author: Jon Loeliger Date: Wed Mar 7 14:48:45 2007 -0600 [POWERPC] 86xx/85xx: Move 8641 PCI-Express to arch/powerpc/sysdev/fsl_pcie.c. This move sets the stage for the use of generic PCI Express code in 85xx and 86xx parts from FSL. Subsequent patches for 8548 and 8544 will be able to use this shared code. Signed-off-by: Jon Loeliger Acked-by: Andy Fleming Signed-off-by: Kumar Gala commit e0e3c8d432ab9503b167e53d60b145f0e26bb1e2 Author: Zhang Wei Date: Wed Mar 7 11:47:41 2007 -0600 [POWERPC] 86xx: Added 2nd PCI-Ex controller support for MPC8641 HPCN to DTS. Signed-off-by: Zhang Wei Signed-off-by: Jon Loeliger Signed-off-by: Kumar Gala commit e5a2072bd48eb4a35c57a8ec45897ac2db3a3f82 Author: David Gibson Date: Thu Mar 22 17:02:21 2007 +1100 [POWERPC] New reg.h for the zImage This patch adds a reg.h to the zImage code, with common definitions for accessing system registers. For now, this includes functions for retrieving the PVR and the stack pointer. This patch then uses the new reg.h to let start() display the running stack address without having to explicitly pass the stack as a parameter from the asm code. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit fae59c39e885148acf42320fe0d4ebf4cb3e9231 Author: David Gibson Date: Thu Mar 22 16:59:13 2007 +1100 [POWERPC] Add gcc format warnings to zImage printf() This patch adds the correct attributes to the zImage's versions of printf to make gcc generate format string mismatch warnings. It also corrects several minor problems with format strings in the zImage thus discovered. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 9da82a6dee9db4cd5ae7a74ab4f51afb52b6efb9 Author: Milton Miller Date: Wed Mar 21 09:03:23 2007 -0600 [POWERPC] boot: Use a common zImage rule Before the plethora of platforms gets any worse, establish a common rule to invoke the wrapper for any platform. Add arguments to the rule for initrd, dts, dtb, etc. Show example usage with initrd. Create default rules for zImage, and zImage.initrd. initrd targets depend on the ramdisk file. Don't consider targets for zImage.initrd that are targets for zImage. This means uImage is no longer considered a target for zImage.initrd. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras commit 5d7960ffb8157acdf92223e32d34da504578aca0 Author: Milton Miller Date: Wed Mar 21 09:03:10 2007 -0600 [POWERPC] boot: clean rule fixes Now that obj-boot is in targets, we can remove (twice) it from clean-files. zImage.initrd was missing, move zImage nearer where its used, and place zImage.initrd next to it. Remove non-ported zImage.sandpoint, and add auto-generation of unconfigured zImage.initrd* like image-. Signed-off-by: Milton Miller -- Testing: did a few builds and cleans Signed-off-by: Paul Mackerras commit 235fd8354528a7dabb3b6050ca4d201549a6f858 Author: Milton Miller Date: Wed Mar 21 09:02:37 2007 -0600 [POWERPC] boot: Use FORCE Kbuild if_changed and if_changed_dep require the use of the dummy FORCE to get the dependencies right. Also add to targets to get correct behavior. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras commit 160cc3ece727c5158facec9937c2a5b10013bae1 Author: Milton Miller Date: Wed Mar 21 09:02:53 2007 -0600 [POWERPC] bootwrapper: Allow platforms to call library zImage_start Some platforms might need to run some code before the zImage start, but could otherwise use the bss clear and relocation code. Export the start address strongly as zImage_start_lib. Signed-off-by: Milton Miller Acked-by: David Gibson Signed-off-by: Paul Mackerras commit 6a923216aac01d0f3eeea606377b81541f1a2773 Author: Milton Miller Date: Wed Mar 21 09:02:44 2007 -0600 [POWERPC] bootwrapper: Add a fatal error helper Add a macro fatal that calls printf then exit. User must include stdio.h. Typically replaces 3 lines with 1, although I added back some whitespace. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras commit 3ede41c718c7845905231019e42d05a3ed329515 Author: Henrique de Moraes Holschuh Date: Fri Mar 23 17:34:00 2007 -0300 ACPI: ibm-acpi: move driver to drivers/misc hierarchy ibm-acpi is not an ACPICA driver, so move it to drivers/misc as per Len Brown's request. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 38f996ed21089fa4ae40526a5f428e3c792ea561 Author: Henrique de Moraes Holschuh Date: Fri Mar 23 17:33:59 2007 -0300 ACPI: ibm-acpi: update documentation Update documentation header, and relocate a hunk of text that was missplaced. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit a62bc916cf48caaf9efa2fed20440fd617647c6c Author: Henrique de Moraes Holschuh Date: Fri Mar 23 17:33:58 2007 -0300 ACPI: ibm-acpi: update copyright notice Update copyright and license info on the source code comments. No functional changes. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 56b6aeb05890f219895197f5166637b3d7a6f679 Author: Henrique de Moraes Holschuh Date: Fri Mar 23 17:33:57 2007 -0300 ACPI: ibm-acpi: organize code Shuffle code around to better organize the driver code inside the ibm-acpi.c file. This patch adds no functional changes. It is pure fluff that will make me a bit more productive. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 1406cdd1760743106278c1f02a0f445159c8f400 Author: Henrique de Moraes Holschuh Date: Fri Mar 23 17:33:56 2007 -0300 ACPI: ibm-acpi: add header file Add a (private) header file for ibm-acpi, and move type definitions and ThinkPad driver constants to the new header file. This patch has no functional changes. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit e062e0343871a41e8ec408f1c1e8ac3b0310da9d Author: Henrique de Moraes Holschuh Date: Fri Mar 23 17:33:55 2007 -0300 ACPI: ibm-acpi: rename some identifiers Rename some identifiers so that they are more in tune with the rest of the driver code, or less generic. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 837ca6ddb440c186eaa8e01b69486581d3457f2c Author: Henrique de Moraes Holschuh Date: Fri Mar 23 17:33:54 2007 -0300 ACPI: ibm-acpi: kill trailing whitespace I shall protect the ibm-acpi city against the invasion of the barbarian blanks! To the unforgiving jaws of sed s/[[:blank:]]\+$// they go! Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 70b52b3869a31aab85241a1f998f9943a3905637 Author: Johannes Berg Date: Mon Mar 19 11:53:55 2007 +0100 [POWERPC] powermac: disallow pmu sleep notifiers from aborting sleep Tracing through the code, no current PMU sleep notifier can abort sleep. Since no new PMU sleep notifiers should be added, this patch simplifies the code and removes the ability to abort sleep. Signed-off-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 17e638bc28f2fdc9c0d3eebfb80fce43827b8d12 Author: Johannes Berg Date: Mon Mar 19 11:53:53 2007 +0100 [POWERPC] Generic time suspend/resume code This removes the time suspend/restore code that was done through a PMU notifier in arch/platforms/powermac/time.c. Instead, introduce arch/powerpc/sysdev/timer.c which creates a sys device and handles time of day suspend/resume through that. This should probably be replaced by using the generic RTC framework but for now it gets rid of the arcane powermac specific hack. Signed-off-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit ec5f77e789a02adf7c45f03a76455b4e71ae1c5b Author: Stefan Roese Date: Fri Mar 16 21:06:00 2007 +0100 [POWERPC] ppc: Fix PCIX configuration of Ocotea & Taishan for > 512MB DDR Change the configuration of the PCIX PCI->PLB inbound memory window to be 2GB instead of 512kB. The comment already mentioned 2GB, but the code unfortunately didn't reflect this. Signed-off-by: Stefan Roese Signed-off-by: Paul Mackerras commit f6f7dde3f778b318aca71220834482d4ea2d7738 Author: anton@samba.org Date: Tue Mar 20 20:38:19 2007 -0500 [POWERPC] Use lowercase for hex printouts in oops messages. Use lowercase for hex printouts in oops messages. The number of times I have tried to copy and paste from an oops into an objdump search... Signed-off-by: Anton Blanchard Acked-by: Olof Johansson Signed-off-by: Paul Mackerras commit ae7f4463773dafac52d70c9803f283afe27ab1e3 Author: anton@samba.org Date: Tue Mar 20 20:38:14 2007 -0500 [POWERPC] Fix backwards ? : when printing machine type Looks like someone got this backwards, highlighting the perils of the ? : !!! :) Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras commit 34c2a14fc20e4ab878fbf87e5f7fe1cff6afb3d4 Author: anton@samba.org Date: Tue Mar 20 20:38:13 2007 -0500 [POWERPC] Handle recursive oopses Handle recursive oopses, like on x86. We had a few cases recently where we locked up in oops printing and didnt make it into crashdump. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras commit 6031d9d9ad905b514bf45572bd1877fe6b5145ab Author: anton@samba.org Date: Tue Mar 20 20:38:12 2007 -0500 [POWERPC] Clean up pmac_backlight_unblank in oops path Move pmac_backlight_unblank into its own function and only take the pmac_backlight_mutex when we are on a pmac for that added bit of paranoia. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras commit 293e4688fe2fec87fccf84a3b1100b27191424e9 Author: anton@samba.org Date: Tue Mar 20 20:38:11 2007 -0500 [POWERPC] Add missing oops_enter/oops_exit Add missing oops_enter/oops_exit, makes pause_on_oops boot parameter work. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras commit 118af321b24529d546cad1c4b6fccf02cd838384 Author: Robert P. J. Day Date: Fri Mar 23 11:27:01 2007 -0400 [MTD] Delete unused header file linux/mtd/iflash.h. Delete the unreferenced header file include/linux/mtd/iflash.h. Signed-off-by: Robert P. J. Day Signed-off-by: David Woodhouse commit 8e5368a1e230a87220ef0d238584002e4a429ce3 Author: David Woodhouse Date: Fri Mar 23 10:40:04 2007 +0000 [MTD] [NAND] Remember timing settings for CAFÉ NAND controller. We'll need them for suspend/resume. Signed-off-by: David Woodhouse commit 4330f5da98eb91392c7a7b00c22a24c57079c0fc Author: Kumar Gala Date: Fri Mar 16 09:32:17 2007 -0500 [POWERPC] Created arch/powerpc/platforms/Kconfig for "Platform support" Split "Platform support" menu out from arch/powerpc/Kconfig into arch/powerpc/platforms/Kconfig in prep for allowing other sub-arches to be configured via a single "Platform support" menu. Signed-off-by: Kumar Gala commit 35a1245ad09412ffba2f17631a9fb3cae3d5a5ac Author: Kumar Gala Date: Fri Mar 16 09:14:08 2007 -0500 [POWERPC] Split several platforms into their respective Kconfig file Moved pseries, iseries, chrp, prep, maple and pasemi into their respective arch/powerpc/platform/*/Kconfig files out of arch/powerpc/Kconfig Signed-off-by: Kumar Gala commit fd42c717510cd65529ccb46ccfa71fe4dde9fbd9 Author: Kumar Gala Date: Fri Mar 16 08:46:55 2007 -0500 [POWERPC] Removed config options that we don't support in embedded6xx When we started arch/powerpc we duplicated a number of config options from arch/ppc for various platforms that are supported. Now that we actually support a few platforms, remove all the ones that haven't been moved over. Additionally, this cleanup moved the 82xx/PQ2 options over into arch/powerpc/platforms/82xx/Kconfig where they belong. It also killed GEN550 which doesn't exist in arch/powerpc. Signed-off-by: Kumar Gala commit 72e77a1b941e24e67f396246310438afbad9e6b3 Author: Kumar Gala Date: Fri Mar 16 08:13:18 2007 -0500 [POWERPC] Split cell platforms into their respective Kconfig file Cleaning up arch/powerpc/Kconfig platform support. Signed-off-by: Kumar Gala commit 9b8babf4a9cc0ba3a8f00b84419ab44bb4d22e05 Author: Kumar Gala Date: Thu Mar 15 18:15:07 2007 -0500 [POWERPC] Split powermac platforms into their own Kconfig file Cleaning up arch/powerpc/Kconfig platform support. Signed-off-by: Kumar Gala commit 5396132cf90423d988bde340987ba21a27b787d7 Author: Kumar Gala Date: Thu Mar 15 18:13:20 2007 -0500 [POWERPC] Split 52xx platforms into their own Kconfig file Cleaning up arch/powerpc/Kconfig platform support. Signed-off-by: Kumar Gala commit 4002aca771a2aa2848e94a98cf51a2cae4e77ae0 Author: Anton Blanchard Date: Tue Mar 20 10:08:33 2007 -0500 [POWERPC] Remove last_syscall Remove last_syscall from 32bit powerpc, its been gone in 64bit for years. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras commit f4db196717c615db68100dee2de8f47d2dc19372 Author: Anton Blanchard Date: Tue Mar 20 10:07:12 2007 -0500 [POWERPC] Remove _get_SP We already have an inline __get_SP, no need for yet another one. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras commit 4980d5eb750288ffc0bb9daff3feb947e1bac61e Author: Linas Vepstas Date: Mon Mar 19 15:01:31 2007 -0500 [POWERPC] EEH: restructure multi-function support Rework how multi-function PCI devices are identified and traversed. This fixes a bug with multi-function recovery on Power4 that was introduced by a recent Power4 EEH patch. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit fa1be476a2baa0961f63161caee6733cdc353adb Author: Linas Vepstas Date: Mon Mar 19 14:59:59 2007 -0500 [POWERPC] EEH: verify state change After requesting a state change, verify that the state change actually ocurred, and the system ends up in the expected state. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit d0ab95ca9854174029cef2f08acf1859441cb547 Author: Linas Vepstas Date: Mon Mar 19 14:59:10 2007 -0500 [POWERPC] EEH: rm un-needed data The EEH event notification system passes around data that is not needed or at least, not used properly. Stop passing this data; get it in a more reliable fashion. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 9c547768e7d9f456f1b145102e75f79e30f7b709 Author: Linas Vepstas Date: Mon Mar 19 14:58:07 2007 -0500 [POWERPC] EEH: wait for slot status Modify routine that returns PCI slot status to wait for slot status to become available. This is needed, as slots that are in some remote card cage may go offline for extended periods of time. New users for this routine in following patches. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 90375f53960f2b1e8d2a6af3324d440e3a977bf3 Author: Linas Vepstas Date: Mon Mar 19 14:56:43 2007 -0500 [POWERPC] EEH: handle reset state high Some firmware versions will return a slot reset state of "1" when a slot is EEH frozen. Recognize this as a state that can be handled. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 5794dbcbab862e416c4ea4f10fda5e67f5565fd7 Author: Linas Vepstas Date: Mon Mar 19 14:55:51 2007 -0500 [POWERPC] EEH: multifunction recovery bugfix If the second or higher function of a multi-function device fails to recover, this failure is not reported upwards. Fix this. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 90fdd6130f5c0053c48e8c8e247091739b6e4092 Author: Linas Vepstas Date: Mon Mar 19 14:55:10 2007 -0500 [POWERPC] EEH: hotplug recovery bugfix If a device driver does not have native PCI error recovery, a hotplug error recovery will be attemped. In this case, the device driver will not report back whether its healthy or not; simply assume that it is. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 147d6a37500348b6bda5738453d84c46678209cf Author: Linas Vepstas Date: Mon Mar 19 14:54:21 2007 -0500 [POWERPC] EEH: support ibm,get-config-addr-info2 RTAS call Provide support for the new ibm,get-config-addr-info2 RTAS token, whenever it is actually available. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 2fd30be8dae25386fc5167c34c6d73201334a8d4 Author: Linas Vepstas Date: Mon Mar 19 14:53:22 2007 -0500 [POWERPC] EEH: Tolerate high mmio Some drivers will attempt to perform a lot of mmio even after an EEH event was detected. This is especially the case for fast cpu's and PCI-E slots. Be a bit more lenient in allowing this. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit e0f90b64181d668ce5995a40f312ed21085bfa98 Author: Linas Vepstas Date: Mon Mar 19 14:52:04 2007 -0500 [POWERPC] EEH: Add clarifying messages. There are multiple code patchs tht resuls in a "permanent failure"; when examining rare events, it can be hard to see which was taken. This patch adds printk's to assist. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 39d16e295966a1f0025a65eaab4cb59fe5ba8c17 Author: Linas Vepstas Date: Mon Mar 19 14:51:00 2007 -0500 [POWERPC] EEH: modify order of EEH state checking Change the order in which pci error state is examined; the "capabilites" is not valid if "reset state" is 5. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 439a888885c584f7ac8536a43be80475f9eaed71 Author: Len Brown Date: Thu Mar 22 01:21:05 2007 -0400 ACPI: sbs: remove i2c_ec.[ch] Signed-off-by: Len Brown commit 722062334b972c31a3b83dbf7e9b5a58bb2707dd Author: Vladimir Lebedev Date: Mon Mar 19 17:45:50 2007 +0300 ACPI: sbs: Common interface with CM battery The SBS driver has tne features as CM battery: SBS update_time variable has tne same definition as CM battery 'update_time' variable. Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit 6845118b3b7a9cc2ba14dc665370217bc3ba8057 Author: Vladimir Lebedev Date: Mon Mar 19 17:45:50 2007 +0300 ACPI: sbs: Debug messages correction/improvement Debug messages correction/improvement: Use ACPI_EXCEPTION instead of ACPI_DEBUG_PRINT. Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit 84cb55987236ffea062a35fbe1441768b6bb2722 Author: Vladimir Lebedev Date: Mon Mar 19 17:45:50 2007 +0300 ACPI: sbs: remove I2C Makefile hooks SBS does not depend on I2C. i2c_ec.h and i2c_ec.c are not needed Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit b4150fc4ae20621edf2f8e1ea5ce13eb2c803e7a Author: Vladimir Lebedev Date: Mon Mar 19 17:45:50 2007 +0300 ACPI: sbs: remove I2C Kconfig dependency SBS does not depend on I2C. Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit 6d15702cc07503b74494dc4f1ddb15f354987b14 Author: Vladimir Lebedev Date: Mon Mar 19 17:45:50 2007 +0300 ACPI: sbs: use EC rather than I2C SBS is based on EC function(ec_read/ec_write). Not needed using of I2C structures/functions ... is removed. SBS does not depend on I2C now. Signed-off-by: Vladimir Lebedev Signed-off-by: Len Brown commit e91948fd84086020072e022d5463036033d449c1 Author: Stephen Rothwell Date: Fri Mar 16 17:47:07 2007 +1100 [POWERPC] Minor paca optimisation Move the slb_shadow_ptr field into the first cache line since it is (like everything there) read-only after boot. It is in fact statically initialised and thereafter only read. Signed-off-by: Stephen Rothwell Acked-by: Michael Neuling Signed-off-by: Paul Mackerras commit 0e6806734fd861c360ecbb4262d3d5678cea7faf Author: Milton Miller Date: Mon Mar 19 14:58:06 2007 -0600 [POWERPC] boot: export flush_cache Move the declaration of flush_cache to ops.h for use by platform code. Signed-off-by: Milton Miller Acked-by: David Gibson Signed-off-by: Paul Mackerras commit 65b580395d234350b53a03285b98c9a271eb5eb7 Author: Milton Miller Date: Mon Mar 19 14:58:04 2007 -0600 [POWERPC] boot: rebuild when wrapper changes Since there is magic defined per platform in the wrapper script, the zImage targets should depend on it. Signed-off-by: Milton Miller Acked-by: David Gibson Acked-by: Segher Boessenkool Signed-off-by: Paul Mackerras commit f61e7cd21b47b07002aa39d2f8f0db14b4a51719 Author: Scott Wood Date: Fri Mar 16 12:28:49 2007 -0500 [POWERPC] bootwrapper: Make setprop accept a const buffer. Signed-off-by: Scott Wood Acked-by: David Gibson Signed-off-by: Paul Mackerras commit e0a2f28b4dee2a1e4c62dc8389f25defb284e387 Author: Scott Wood Date: Fri Mar 16 12:28:46 2007 -0500 [POWERPC] Document the linux,network-index property. To allow more robust association of each network device node with an index (such as is used by the firmware or an EEPROM to indicate MAC addresses), a network device's node may specify the index explicitly. Signed-off-by: Scott Wood Acked-by: David Gibson david@gibson.dropbear.id.au> Signed-off-by: Paul Mackerras commit a9903811bf8d130a26004f9cb27b66513a267908 Author: Scott Wood Date: Fri Mar 16 12:27:59 2007 -0500 [POWERPC] bootwrapper: Make compression of the kernel image optional. The --no-gzip option can be passed to the wrapper so that the kernel image is included uncompressed into the zImage. This is intended for bootloaders where the zImage itself can be compressed, or where boot time is considered more important than kernel image size. Signed-off-by: Scott Wood Acked-by: David Gibson Signed-off-by: Paul Mackerras commit a07940ba00218267493798e89e3f4a86fea53125 Author: Scott Wood Date: Fri Mar 16 12:27:54 2007 -0500 [POWERPC] bootwrapper: Add dt_ops methods. Add get_parent, create_node, and find_node_by_prop_value to dt_ops. Currently only implemented by flatdevtree_misc. Also, add a _str convenience wrapper for setprop. Signed-off-by: Scott Wood Acked-by: David Gibson Signed-off-by: Paul Mackerras commit e85f008d016d2de59ee5b01dba18ea3dea41545b Author: Robert P. J. Day Date: Thu Mar 8 11:19:39 2007 -0500 [POWERPC] Correct apparently misspelled "XMON" preprocessor symbol. Correct the apparent misspelling of "XMON" to "CONFIG_XMON". Signed-off-by: Robert P. J. Day Signed-off-by: Paul Mackerras commit 7850ad5c39a40ae14ab37e030357e2ae8252af2b Author: David Gibson Date: Wed Mar 14 16:32:17 2007 +1100 [POWERPC] Add documentation for the zImage's gunzip convenience functions This patch adds documenting comments to the gunzip convenience functions added in commit ad9d2716cfc1cda5a7e0d7bc0db45e3af8a4adbb. It also removes a stray newline, and an unused global variable. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 0e0293c898c424c52e5d4e7f6923a203d06b9c4b Author: David Gibson Date: Wed Mar 14 11:50:40 2007 +1100 [POWERPC] Update documentation for flat device tree format v17 This patch updates booting-without-of.txt to describe version 17 of the flattened device tree format. Version 17 is a small, backwards compatible change from version 16, adding an extra field giving the size of the device tree's structure block. At this time, the kernel has no use for the extra information, however its presence can make life easier for bootloaders or other software manipulating the tree. In addition this patch adds information on the size_dt_strings field of the device tree header, present since version 3 of the flattened tree format, but omitted from the documentation. It also makes changes to consistently refer to versions 16 and 17 as versions 16 and 17 in decimal, rather than version 0x10 which was occasionally used for version 16 previously. Finally, we also add the new field to the definition of the device tree header structure in prom.h Signed-off-by: David Gibson Acked-by: Jon Loeliger Signed-off-by: Paul Mackerras commit a782a9e3a12bcea4916117b0832016c24fffeb85 Author: Mariusz Kozlowski Date: Thu Mar 15 23:20:44 2007 +0100 [POWERPC] 8xx parenthesis balance This balances parenthesis in powerpc 8xx header files. Signed-off-by: Mariusz Kozlowski Signed-off-by: Paul Mackerras commit 6bccf755ff53241d46c01c229b3c2452b9029ec4 Author: Joachim Fenkes Date: Fri Mar 9 19:00:32 2007 +0100 [POWERPC] ibmebus: dynamic addition/removal of adapters, some code cleanup This adds two sysfs attributes to /sys/bus/ibmebus which can be used to notify the ebus driver of added / removed ebus devices in the OF device tree. Echoing the device's location code (as found in the OFDT "ibm,loc-code" property) into the "probe" attribute will notify ebus of addition of the device and cause the appropriate device driver's probe function to be called on the device. Likewise, echoing the location code into the "remove" attribute will cause the device to be removed from the system. The writes will block until the respective operation has finished and return an error code if the operation failed. In addition, two minor tidbits are fixed: - The fake root device used to provide a common parent for all ebus devices is now based on device instead of of_device - it had no associated devtree node. This saves several checks throughout the ebus driver. - The sysfs attributes are now generated automagically by device_register() instead of by the ibmebus code, which saves a few compiler warnings about unused return codes. Signed-off-by: Joachim Fenkes Signed-off-by: Paul Mackerras commit a83088003cd53f3cd8d550ab5d7778866568d204 Author: Joachim Fenkes Date: Fri Mar 9 18:56:46 2007 +0100 [POWERPC] ibmebus: whitespace fixes This fixes a lot of whitespace in ibmebus.[ch] Signed-off-by: Joachim Fenkes Signed-off-by: Paul Mackerras commit 556b09c8189b9b3f5626ca73196009e397503f85 Author: Mark A. Greer Date: Wed Oct 25 16:36:49 2006 -0700 [POWERPC] 32-bit early_init() should zero from __bss_start to __bss_stop only Currently, early_init() in setup_32.c zeroes from '_bss_start' to '_end'. It should only zero from '__bss_start' to '__bss_stop'. This patch does that. Signed-off-by: Mark A. Greer Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 86a1b63349bb2cbed6c2cbf8f9e9de9259a404df Author: Scott Wood Date: Mon Mar 12 14:41:59 2007 -0600 [POWERPC] bootwrapper: Make ft_create_node() pay attention to the parent parameter. Signed-off-by: Scott Wood Acked-by: David Gibson Signed-off-by: Paul Mackerras commit 8941c0c495e8765206ec1017b1e069ce41bf6e8f Author: Scott Wood Date: Mon Mar 12 14:41:58 2007 -0600 [POWERPC] bootwrapper: Add ft_find_node_by_prop_value(). ft_find_node_by_prop_value() finds nodes with the specified property/value pair. Signed-off-by: Scott Wood Acked-by: Mark A. Greer Signed-off-by: Paul Mackerras commit a9ec7669fc07f80f6e39807f1ac319764a304319 Author: Scott Wood Date: Mon Mar 12 14:41:56 2007 -0600 [POWERPC] bootwrapper: Make ft_get_parent() return a phandle, and NULL if already top-level. Most of ft_get_parent() is factored out into __ft_get_parent(), which deals only in internal node pointers. The ft_get_parent() wrapper handles phandle conversion in both directions (previously, ft_get_parent() did not convert its return value). It also now returns NULL as the parent of the toplevel node, rather than just returning the toplevel node again (which made it rather useless in loops). Signed-off-by: Scott Wood Acked-by: Mark A. Greer Signed-off-by: Paul Mackerras commit c350038b2bdabb07611dcc8116b55f917ada09fa Author: Scott Wood Date: Mon Mar 12 14:41:54 2007 -0600 [POWERPC] bootwrapper: Refactor ft_get_prop() into internal and external functions. The property searching part of ft_get_prop is factored out into an internal __ft_get_prop() which does not deal with phandles and does not copy the property data. ft_get_prop() is then a wrapper that does the phandle translation and copying. Signed-off-by: Scott Wood Acked-by: Mark A. Greer Signed-off-by: Paul Mackerras commit fc583411617bf8a466c68350697a806704e88fc3 Author: Scott Wood Date: Mon Mar 12 14:41:53 2007 -0600 [POWERPC] bootwrapper: Add ft_find_device_rel(). Add a function to look up a relative, rather than absolute, path name. Signed-off-by: Scott Wood Acked-by: Mark A. Greer Signed-off-by: Paul Mackerras commit 5adeef52ccc0229e06a6e0b2fefe442d8779f025 Author: Scott Wood Date: Mon Mar 12 14:41:51 2007 -0600 [POWERPC] bootwrapper: Use map_string() instead of lookup_string() in ft_prop(). When adding a property, the property name should be added to the string table if it doesn't already exist. map_string() does that; lookup_string() will fail instead. Signed-off-by: Scott Wood Acked-by: David Gibson Signed-off-by: Paul Mackerras commit 9dd2c31ab89a12571db89648bcc9992cf71b63d8 Author: Scott Wood Date: Mon Mar 12 14:41:48 2007 -0600 [POWERPC] bootwrapper: Modify *pp, not *p, in ft_shuffle(). Move the caller's pointer back to match the change in the region's start, rather than alter a byte of the device tree's content. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit 78438b36011d8ef7d28ef63a30b11801be1eea71 Author: Scott Wood Date: Mon Mar 12 14:41:47 2007 -0600 [POWERPC] bootwrapper: Preserve the pp pointer in ft_make_space() when calling ft_reorder(). The ft_reorder() function may change the start of the region of interest, so the pointer provided by the caller into that region must be fixed up to still point to the same datum. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras commit c8aa72633e65c5c215b0cdd9970642e2a4f9a9a3 Author: Scott Wood Date: Mon Mar 12 14:41:45 2007 -0600 [POWERPC] bootwrapper: Make ft_get_phandle() accept and return NULL. Currently, if ft_get_phandle() is passed NULL it will allocate an entry for it and return a non-NULL phandle. This patch makes it simply pass the NULL through. Signed-off-by: Scott Wood Acked-by: Mark A. Greer Signed-off-by: Paul Mackerras commit 1c53a496ba6132a37f052aa907e23445b3fe928c Author: Scott Wood Date: Mon Mar 12 14:41:44 2007 -0600 [POWERPC] bootwrapper: Rename ft_node_add() to ft_get_phandle(). This name better reflects what the function does, which is to look up the phandle for an internal node pointer, and add it to the internal pointer to phandle table if not found. Signed-off-by: Scott Wood Acked-by: Mark A. Greer Acked-by: David Gibson Signed-off-by: Paul Mackerras commit 7c71c04625483c1da91846fd6746057d24ad6be8 Author: Scott Wood Date: Mon Mar 12 14:41:42 2007 -0600 [POWERPC] bootwrapper: Add ft_root_node(). Clean up some of the open-coded data structure references by providing a function to return a pointer to the tree's root node. This is only used in high-level functions trying to access the root of the tree, not in low-level code that is actually manipulating the data structure. Signed-off-by: Scott Wood Acked-by: Mark A. Greer Acked-by: David Gibson Signed-off-by: Paul Mackerras commit ce3edb30ec2b6350c4258e3d52e73b410e2ea12d Author: Scott Wood Date: Mon Mar 12 14:41:38 2007 -0600 [POWERPC] bootwrapper: Add stddef.h to ops.h ops.h references NULL, so include stddef.h, so files including ops.h don't have to. Signed-off-by: Scott Wood Acked-by: Mark A. Greer Acked-by: David Gibson Signed-off-by: Paul Mackerras commit cd197ffcf10bcc1a260efe5c09a3188fd9228c83 Author: David Gibson Date: Mon Mar 5 14:24:52 2007 +1100 [POWERPC] zImage: Cleanup and improve zImage entry point This patch re-organises the way the zImage wrapper code is entered, to allow more flexibility on platforms with unusual entry conditions. After this patch, a platform .o file has two options: 1) It can define a _zimage_start, in which case the platform code gets control from the very beginning of execution. In this case the platform code is responsible for relocating the zImage if necessary, clearing the BSS, performing any platform specific initialization, and finally calling start() to load and enter the kernel. 2) It can define platform_init(). In this case the generic crt0.S handles initial entry, and calls platform_init() before calling start(). The signature of platform_init() is changed, however, to take up to 5 parameters (in r3..r7) as they come from the platform's initial loader, instead of a fixed set of parameters based on OF's usage. When using the generic crt0.S, the platform .o can optionally supply a custom stack to use, using the BSS_STACK() macro. If this is not supplied, the crt0.S will assume that the loader has supplied a usable stack. In either case, the platform code communicates information to the generic code (specifically, a PROM pointer for OF systems, and/or an initrd image address supplied by the bootloader) via a global structure "loader_info". In addition the wrapper script is rearranged to ensure that the platform .o is always linked first. This means that platforms where the zImage entry point is at a fixed address or offset, rather than being encoded in the binary header can be supported using option (1). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 79c8541924a220964f9f2cbed31eaa9fdb042eab Author: David Gibson Date: Mon Mar 5 14:24:52 2007 +1100 [POWERPC] zImage: Cleanup and improve prep_kernel() This patch rewrites prep_kernel() in the zImage wrapper code to be clearer and more flexible. Notable changes: - Handling of the initrd image from prep_kernel() has moved into a new prep_initrd() function. - The address of the initrd image is now added as device tree properties, as the kernel expects. - We only copy a packaged initrd image to a new location if it is in danger of being clobbered when the kernel moves to its final location, instead of always. - By default we decompress the kernel directly to address 0, instead of requiring it to relocate itself. Platforms (such as OF) where doing this could clobber still-live firmware data structures can override the vmlinux_alloc hook to provide an alternate place to decompress the kernel. - We no longer pass lots of information between functions in global variables. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit ad9d2716cfc1cda5a7e0d7bc0db45e3af8a4adbb Author: David Gibson Date: Mon Mar 5 14:24:52 2007 +1100 [POWERPC] zImage: Add more flexible gunzip convenience functions At present, arch/powerpc/boot/main.c includes a gunzip() function which is a convenient wrapper around zlib. However, it doesn't conveniently allow decompressing part of an image to one location, then the remainder to a different address. This patch adds a new set of more flexible convenience wrappers around zlib, moving them to their own file, gunzip_util.c, in the process. These wrappers allow decompressing sections of the compressed image to different locations. In addition, they transparently handle uncompressed data, avoiding special case code to handle uncompressed vmlinux images. The patch also converts main.c to use the new wrappers, using the new flexibility to avoid decompressing the vmlinux's ELF header twice as we did previously. That in turn means we avoid extending our allocations for the vmlinux to allow space for the extra copy of the ELF header. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 8ec555c2c4c6c33759a1dbb13fa8f3b14fc77e10 Author: Corentin Chary Date: Sun Mar 11 10:28:03 2007 +0100 asus-laptop: version bump Version and copyright bump. Signed-off-by: Corentin Chary Signed-off-by: Len Brown commit 185e5af98b1e09b1e0f859332243223776b2ad57 Author: Corentin Chary Date: Sun Mar 11 10:27:33 2007 +0100 asus-laptop: add wapf param Add the "wapf" param. This param allows to define the behavior of the Fn F2 key (wlan switch). Signed-off-by: Corentin Chary Signed-off-by: Len Brown commit fdd8d08084663242b42e27f7d71739f3f9009286 Author: Corentin Chary Date: Sun Mar 11 10:26:48 2007 +0100 asus-laptop: add GLED Add support for "gaming" led. Signed-off-by: Corentin Chary Signed-off-by: Len Brown commit 935ffeeca84fe5d48d0cc9f35c58db42b384229f Author: Corentin Chary Date: Sun Mar 11 10:26:12 2007 +0100 asus-laptop: clean write_status Clean the write_status function, and implement special case with a switch inside write_status. It also make sure bt and wl status are right when booting with the hardware switch off. Signed-off-by: Corentin Chary Signed-off-by: Len Brown commit 9a8168500674b1062afe438d34d0c8216d38dc31 Author: Corentin Chary Date: Sun Mar 11 10:25:38 2007 +0100 asus-laptop: use acpi_evaluate_integer instead of read_acpi_int Use acpi_evaluate_integer() instead of read_acpi_int() Signed-off-by: Corentin Chary Signed-off-by: Len Brown commit 3a6effe81fa0bd2fb9c6c5ecde665492536733e3 Author: Robert P. J. Day Date: Sat Mar 10 03:57:25 2007 -0500 [JFFS2] Remove superfluous source file fs/jffs2/comprtest.c Delete the obsolete source file fs/jffs2/comprtest.c. Signed-off-by: Robert P. J. Day Signed-off-by: David Woodhouse commit 9fd9f8e8bdcfc9aa309dae5bccc55d02804337d0 Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: Block queries until EC is fully initialized Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 01f2246269639f6aa93086719a8dbec26cb68e98 Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: Cleanup of EC initialization Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit d033879c9838b960014e861d0eb3bdf11d3b9d9d Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: first_ec is better to be acpi_ec than acpi_device. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit d66d969df88c742494736ed06eeaf3229d3a7259 Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: Rename ec_ecdt to more informative boot_ec Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit c0900c3512dc8fd0b37f8fbcebc7853ed9efff10 Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: Clean ECDT and namespace parsing. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit e8284321048aac7be307b3ec5e0631f5c514935a Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: Put install handlers into separate function. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 3d02b90be2c7bc7ffbc5e502a135c13838d23ef4 Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: Remove casts to/from void* from ec.c Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit c45aac43fec2d6ca8d0be8408f94e8176c8110ef Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: enable burst functionality in EC. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 33c7a0738c5f753a7d94fd3b2ec7d84e79a141a8 Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: "Fake ECDT" workaround is not needed any longer. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 33d20b6100d05a0b14883e7dc8ab41e4531fcf59 Author: Alexey Starikovskiy Date: Wed Mar 7 22:28:00 2007 +0300 ACPI: EC: Make EC to initialize first in ACPI Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit a5f8dee2d367e69fd57f5ea107072bb72eb15327 Author: Alexey Starikovskiy Date: Wed Mar 7 22:27:59 2007 +0300 ACPI: EC: Don't use Global Lock if not asked to do so Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit ac122bb64b0d51f0512185d3522a75f3f3a80bc9 Author: Ilya A. Volynets-Evenbakh Date: Mon Feb 19 15:19:31 2007 -0800 ACPI: dock: add access to ACPI docking station UID It is useful to know whether your laptop is docked or not, but it is even more useful to know which docking station it's docked to. Attached patch adds "uid" file to sysfs. Tested on Dell Latitude D600 with D/Dock. Patch is against official 2.6.20 release. Signed-off-by: Len Brown commit cfbff8a3802542c4d8b2290c49b1a59128c4a380 Author: Jake Moilanen Date: Tue Oct 3 14:29:34 2006 -0500 [POWERPC] 750CL cputable entry 750CL cputable entry from Steve Winiecki. Signed-off-by: Jake Moilanen Signed-off-by: Paul Mackerras commit 6406063899cc6f558df1e751d46de2e3c45c2189 Author: Zang Roy-r61911 Date: Tue Mar 6 14:10:36 2007 +0800 [POWERPC] Remove fixed setting of ROOT_DEV for 7448HPC2 platforms Remove fixed setting of ROOT_DEV for 7448HPC2 platforms. Signed-off-by: Roy Zang Signed-off-by: Paul Mackerras commit 36241ce695f16193d0f76ea010f212119da37071 Author: Stephen Rothwell Date: Sun Mar 4 17:07:38 2007 +1100 [POWERPC] Make find_and_init_pbs() a void function It always returned 0 and noone checked. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 0a7c7efccc08f00ae6fc8e1f2de0ee61f07357fd Author: Stephen Rothwell Date: Sun Mar 4 17:05:34 2007 +1100 [POWERPC] Allow xmon to build without CONFIG_DEBUG_BUGVERBOSE Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit bed59275810a55500e885cbdc5c2a0507f13c00e Author: Stephen Rothwell Date: Sun Mar 4 17:04:44 2007 +1100 [POWERPC] Allow pSeries to build without CONFIG_PCI Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit be9e95b17e150c7b6933cb2f1e1c46a501976080 Author: Stephen Rothwell Date: Sun Mar 4 17:03:48 2007 +1100 [POWERPC] Make iSeries build without CONFIG_PCI Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 57190708f1f52d732d94fa21a8e576302d384d33 Author: Stephen Rothwell Date: Sun Mar 4 17:02:41 2007 +1100 [POWERPC] Create and use get_pci_dma_ops() This allows us to hide pci_dma_ops. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 9874777016e06ad2df420237963e81389776cb6d Author: Stephen Rothwell Date: Sun Mar 4 16:58:39 2007 +1100 [POWERPC] Create and use set_pci_dma_ops This will allow us to build without PCI easier. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 618d3adc351a24c4c48437c767befb88ca2d199d Author: Jake Moilanen Date: Fri Mar 2 15:49:43 2007 -0600 [POWERPC] DMA 4GB boundary protection There are many adapters which can not handle DMAing acrosss any 4 GB boundary. For instance the latest Emulex adapters. This normally is not an issue as firmware gives us dma-windows under 4gigs. However, some of the new System-P boxes have dma-windows above 4gigs, and this present a problem. I propose fixing it in the IOMMU allocation instead of making each driver protect against it as it is more efficient, and won't require changing every driver which has not considered this issue. This patch checks to see if the mapping spans a 4 gig boundary, and if it does, retries the allocation. It tries the next allocation at the start of the crossed 4 gig boundary. Signed-off-by: Jake Moilanen Signed-off-by: Paul Mackerras commit 723ec731de880a76a004a304b62bf8d0f96435d8 Author: Dave Jiang Date: Fri Mar 2 13:36:21 2007 -0700 [POWERPC] EDAC ECC software scrubber Implements the per arch atomic_scrub() that EDAC uses for software ECC scrubbing. It reads memory and then writes back the original value, allowing the hardware to detect and correct memory errors. Signed-off-by: Dave Jiang Signed-off-by: Paul Mackerras commit 27565903e94d548256bf5923653ab2a9668c9b9f Author: Stuart Yoder Date: Fri Mar 2 13:42:33 2007 -0600 [POWERPC] Update interrupt info in booting-without-of.txt Create a new section descrbing how interrupts are represented in the device tree. Added more detail. Clarified some things. Signed-off-by: Stuart Yoder Signed-off-by: Paul Mackerras commit 500798d48fdcffbbc7f619bd3e6b5b5cea6869d1 Author: Stuart Yoder Date: Tue Feb 27 10:14:14 2007 -0600 [POWERPC] Remove unused, undocumented #cpus property from cpus node The #cpus property is unused and undocumented and is therefore being removed. Signed-off-by: Stuart Yoder Acked-by: David Gibson Signed-off-by: Paul Mackerras commit fdc0a9be3a63a71c12de86cc97d4cd8cf46239c0 Author: MOKUNO Masakazu Date: Mon Feb 26 17:21:05 2007 +0900 [POWERPC] Remove some redundant isync instructions Remove some redundant isync instructions. enable_64b_mode() already does an isync, so there is no need to do it again. Signed-off-by: MOKUNO, Masakazu Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit dbc11f539df7c9a32424b78afb0314c68d5e7d0b Author: Olaf Hering Date: Sun Feb 25 20:04:18 2007 +0100 [POWERPC] Include stddef.h in asm-powerpc/current.h to get offsetof On Tue, Oct 31, Hugh Dickins wrote: > +++ linux/include/asm-powerpc/current.h 2006-10-30 19:27:05.000000000 +0000 > +static inline struct task_struct *get_current(void) > +{ > + struct task_struct *task; > + > + __asm__ __volatile__("ld %0,%1(13)" > + : "=r" (task) > + : "i" (offsetof(struct paca_struct, __current))); This breaks compile of 2.6.18.8: CC [M] drivers/media/video/pwc/pwc-uncompress.o In file included from /home/olaf/kernel/linux-2.6.18.8/drivers/media/video/pwc/pwc-uncompress.c:29: include2/asm/current.h: In function 'get_current': include2/asm/current.h:23: warning: implicit declaration of function 'offsetof' include2/asm/current.h:23: error: expected expression before 'struct' make[5]: *** [drivers/media/video/pwc/pwc-uncompress.o] Error 1 Signed-off-by: Olaf Hering Signed-off-by: Paul Mackerras commit 1c56f838a9d0b25d68363bca133722c5330707b3 Author: Stephen Rothwell Date: Fri Feb 23 14:41:41 2007 +1100 [POWERPC] Make ppc64_defconfig without CONFIG_PPC_PSERIES build Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 44d7631bdb16211a492921dbc0d14fce66c20501 Author: Segher Boessenkool Date: Thu Feb 22 23:52:02 2007 +0100 [POWERPC] PowerPC: select default image for Linkstation Signed-off-by: Segher Boessenkool Acked-by: G. Liakhovetski Signed-off-by: Paul Mackerras commit 8170f524894b17a3f36351fe0e1a3fb6aca733ee Author: Geert Uytterhoeven Date: Thu Feb 22 16:43:12 2007 +0100 [POWERPC] ps3: always make sure we're running on a PS3 Add missing checks to PS3 specific drivers ps3av and sys-manager to verify that we are actually running on a PS3 (pointed out by Arnd). Correct existing checks in other subsystems/drivers to return -ENODEV instead of zero. Signed-off-by: Geert Uytterhoeven Acked-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 02567c6cdad4d6254052f25f3b93aa2771f48d25 Author: Stephen Rothwell Date: Wed Feb 21 14:53:50 2007 +1100 [POWERPC] Allocate syscall number for sys_getcpu I forgot to do this when wiring up the syscall. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 30437b3e743f33e9b68f813ca24e547aa9fcf7d7 Author: David Gibson Date: Wed Feb 28 14:12:29 2007 +1100 [POWERPC] Automatically lmb_reserve() initrd At present, when an initrd is passed to the kernel used flat device tree properties, the memory the initrd occupies must also be reserved in the flat tree's reserve map, or the kernel may overwrite it. That makes life more complicated than it could be for the bootwrapper. This patch makes the kernel automatically reserve the initrd's space. That in turn requires parsing the initrd parameters earlier than they are currently, in early_init_dt_scan_chosen() instead of check_for_initrd(). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit eb6de2863750e696201780283e4c9ada19b4728e Author: David Gibson Date: Wed Feb 28 14:12:29 2007 +1100 [POWERPC] Allow duplicate lmb_reserve() calls At present calling lmb_reserve() (and hence lmb_add_region()) twice for exactly the same memory region will cause strange behaviour. This makes life difficult when booting from a flat device tree with memory reserve map. Which regions are automatically reserved by the kernel has changed over time, so it's quite possible a newer kernel could attempt to auto-reserve a region which is also explicitly listed in the device tree's reserve map, leading to trouble. This patch avoids the problem by making lmb_reserve() ignore a call to reserve a previously reserved region. It also removes a now redundant test designed to avoid one specific case of the problem noted above. At present, this patch deals only with duplicate reservations of an identical region. Attempting to reserve two different, but overlapping regions will still cause problems. I might post another patch later dealing with this case, but I'm avoiding it now since it is substantially more complicated to deal with, less likely to occur and more likely to indicate a genuine bug elsewhere if it does occur. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 83ddcf5d364be7b1d8c214d2dd97753e1df589cd Author: Adrian Bunk Date: Tue Feb 20 01:08:12 2007 +0100 [POWERPC] Unexport mac_hid_mouse_emulate_buttons This patch removes the unused EXPORT_SYMBOL(mac_hid_mouse_emulate_buttons). Signed-off-by: Adrian Bunk Signed-off-by: Paul Mackerras commit d1bff9ed3c05859fe4a8d00e51f331f5d45350ed Author: Stuart Yoder Date: Mon Feb 19 11:25:05 2007 -0600 [POWERPC] Remove interrupt-controller as a property under /chosen Remove interrupt-controller as a valid property under /chosen in the documentation. There is a consensus that an interrupt-controller property does not belong under /chosen. /chosen is specifically for dynamic properties set at runtime. Signed-off-by: Stuart Yoder Signed-off-by: Paul Mackerras commit bb72c481e970dc1b4034ddccbe8302ff39e0d948 Author: Paul Mackerras Date: Mon Feb 19 11:42:42 2007 +1100 [POWERPC] Harden validate_sp against stack corruption If something has overflowed or corrupted the stack and causes an oops, and we try to print a stack trace, that will call validate_sp, which can itself cause an oops if the cpu field of the thread_info struct at the bottom of the stack has been corrupted (if CONFIG_IRQSTACKS is set). This makes debugging harder. To avoid the second oops, this adds a check to make sure that the cpu number is reasonable before using it to check whether the stack is on the softirq or hardirq stack. Signed-off-by: Paul Mackerras commit 99ddef9bfe714c3273e3fce4c6b6a2a99e7d0bf8 Author: Andrew Morton Date: Sat Feb 17 18:17:16 2007 -0700 [POWERPC] Fix compile error in prom.h In file included from include/asm/pci.h:20, from include/linux/pci.h:751, from arch/powerpc/sysdev/dart_iommu.c:36: include/asm/prom.h: In function `of_irq_to_resource': include/asm/prom.h:341: warning: implicit declaration of function `irq_of_parse_and_map' include/asm/prom.h:345: error: `NO_IRQ' undeclared (first use in this function) include/asm/prom.h:345: error: (Each undeclared identifier is reported only once include/asm/prom.h:345: error: for each function it appears in.) Seems that prom.h has always wanted irq.h. Cc: Mathieu Desnoyers Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras commit 21d31f1f7c8f832324fb55eb4b1397b16258904e Author: Joern Engel Date: Tue Feb 20 20:22:22 2007 +0100 [PATCH] [MTD] block2mtd: remove readahead code Over the years there was a slow trickle of complaints against the readahead code. Most of them concerned performance, Peter Zijlstra stumbled over it when working unrelated changes and I believe there was an actual bug report. Oh, Andrew Morton also complained about duplicating code from mm/readahead.c. It is just not worth it. On flash media like usb sticks, readahead will make things go slow - very slow. On spinning disks, readahead may be a win, but this is definitely not the place to add it. Signed-off-by: Jörn Engel commit 8870530a4053add56a2c0eb90a3669facb9f7117 Author: Joern Engel Date: Tue Feb 20 20:21:41 2007 +0100 [PATCH] [MTD] block2mtd: remove warning drivers/mtd/devices/block2mtd.c:311:9: warning: symbol 'dev' shadows an earlier one drivers/mtd/devices/block2mtd.c:294:23: originally declared here Signed-off-by: Jörn Engel commit 0ffb74ccc06a112042adfaf8229684b78202bcae Author: Joern Engel Date: Tue Feb 20 20:20:58 2007 +0100 [PATCH] [MTD] block2mtd: remove casts Remove two casts - they were not only pointless, but outright harmful. Spotted by Felix Fietkau Signed-off-by: Jörn Engel CREDITS | 22 Documentation/ABI/testing/sysfs-bus-usb | 41 Documentation/DocBook/kernel-api.tmpl | 6 Documentation/feature-removal-schedule.txt | 61 Documentation/filesystems/afs.txt | 214 + Documentation/filesystems/proc.txt | 9 Documentation/ibm-acpi.txt | 728 --- Documentation/infiniband/user_mad.txt | 8 Documentation/kernel-parameters.txt | 34 Documentation/keys.txt | 12 Documentation/networking/bcm43xx.txt | 97 Documentation/networking/bonding.txt | 35 Documentation/networking/dccp.txt | 10 Documentation/networking/ip-sysctl.txt | 31 Documentation/networking/rxrpc.txt | 859 ++++ Documentation/networking/wan-router.txt | 1 Documentation/power/interface.txt | 21 Documentation/power/states.txt | 13 Documentation/power/swsusp.txt | 14 Documentation/powerpc/booting-without-of.txt | 256 + Documentation/s390/crypto/crypto-API.txt | 83 Documentation/s390/zfcpdump.txt | 87 Documentation/sony-laptop.txt | 25 Documentation/thinkpad-acpi.txt | 981 ++++ Documentation/usb/usbmon.txt | 80 Documentation/video4linux/CARDLIST.bttv | 2 Documentation/video4linux/CARDLIST.cx88 | 2 Documentation/video4linux/CARDLIST.ivtv | 18 Documentation/video4linux/CARDLIST.saa7134 | 8 Documentation/video4linux/CARDLIST.usbvision | 64 Documentation/video4linux/README.ivtv | 187 + .../video4linux/cx2341x/fw-decoder-regs.txt | 12 .../video4linux/cx2341x/fw-encoder-api.txt | 19 Documentation/video4linux/cx2341x/fw-osd-api.txt | 12 Documentation/video4linux/meye.txt | 7 Documentation/video4linux/sn9c102.txt | 64 Documentation/video4linux/zr364xx.txt | 65 MAINTAINERS | 146 - arch/alpha/lib/Makefile | 1 arch/alpha/lib/strcasecmp.c | 26 arch/arm/common/sharpsl_pm.c | 2 arch/arm/mach-at91/pm.c | 1 arch/arm/mach-omap1/pm.c | 2 arch/arm/mach-omap2/pm.c | 2 arch/arm/mach-pnx4008/pm.c | 39 arch/arm/mach-pxa/pm.c | 5 arch/arm/mach-sa1100/pm.c | 8 arch/arm/plat-s3c24xx/pm.c | 28 arch/avr32/Kconfig | 13 arch/avr32/Makefile | 1 arch/avr32/boards/atngw100/Makefile | 1 arch/avr32/boards/atngw100/flash.c | 95 arch/avr32/boards/atngw100/setup.c | 124 + arch/avr32/boards/atstk1000/atstk1002.c | 4 arch/avr32/boards/atstk1000/setup.c | 30 arch/avr32/configs/atngw100_defconfig | 1085 +++++ arch/avr32/kernel/cpu.c | 64 arch/avr32/kernel/entry-avr32b.S | 124 - arch/avr32/kernel/module.c | 11 arch/avr32/kernel/process.c | 193 + arch/avr32/kernel/setup.c | 484 ++ arch/avr32/kernel/time.c | 150 - arch/avr32/kernel/traps.c | 421 +- arch/avr32/kernel/vmlinux.lds.c | 9 arch/avr32/mach-at32ap/Kconfig | 31 arch/avr32/mach-at32ap/Makefile | 1 arch/avr32/mach-at32ap/at32ap7000.c | 70 arch/avr32/mach-at32ap/hmatrix.h | 182 + arch/avr32/mach-at32ap/hsmc.c | 23 arch/avr32/mach-at32ap/time-tc.c | 218 + arch/avr32/mm/fault.c | 116 - arch/avr32/mm/init.c | 238 - arch/i386/defconfig | 1 arch/i386/kernel/acpi/earlyquirk.c | 5 arch/ia64/hp/sim/simeth.c | 3 arch/ia64/lib/csum_partial_copy.c | 2 arch/ia64/sn/kernel/xpnet.c | 18 arch/mips/Kconfig | 27 arch/mips/Makefile | 6 arch/mips/basler/excite/excite_setup.c | 2 arch/mips/cobalt/Makefile | 1 arch/mips/cobalt/console.c | 8 arch/mips/cobalt/irq.c | 2 arch/mips/cobalt/pci.c | 47 arch/mips/cobalt/reset.c | 11 arch/mips/cobalt/setup.c | 32 arch/mips/configs/jmr3927_defconfig | 254 - arch/mips/configs/pnx8550-v2pci_defconfig | 1540 ------- arch/mips/gt64120/wrppmc/pci.c | 4 arch/mips/jmr3927/common/prom.c | 12 arch/mips/jmr3927/common/puts.c | 122 - arch/mips/jmr3927/rbhma3100/Makefile | 1 arch/mips/jmr3927/rbhma3100/init.c | 16 arch/mips/jmr3927/rbhma3100/irq.c | 312 - arch/mips/jmr3927/rbhma3100/kgdb_io.c | 54 arch/mips/jmr3927/rbhma3100/setup.c | 157 - arch/mips/kernel/asm-offsets.c | 1 arch/mips/kernel/i8259.c | 4 arch/mips/kernel/kspd.c | 5 arch/mips/kernel/rtlx.c | 7 arch/mips/mips-boards/generic/display.c | 8 arch/mips/mips-boards/generic/pci.c | 4 arch/mips/mips-boards/generic/reset.c | 12 arch/mips/mips-boards/malta/malta_int.c | 4 arch/mips/mips-boards/malta/malta_setup.c | 4 arch/mips/mips-boards/sim/Makefile | 3 arch/mips/mips-boards/sim/sim_platform.c | 35 arch/mips/mm/cache.c | 2 arch/mips/mm/init.c | 25 arch/mips/pci/Makefile | 3 arch/mips/pci/fixup-jmr3927.c | 11 arch/mips/pci/ops-gt64111.c | 100 arch/mips/pci/ops-gt64120.c | 152 - arch/mips/pci/ops-gt64xxx_pci0.c | 152 + arch/mips/pci/ops-tx3927.c | 232 - arch/mips/pci/pci-lasat.c | 4 arch/mips/pci/pci-ocelot.c | 2 arch/mips/pci/pci.c | 25 arch/mips/sgi-ip22/ip22-nvram.c | 24 arch/mips/sgi-ip22/ip22-time.c | 14 arch/mips/sibyte/Kconfig | 2 arch/mips/sibyte/common/Makefile | 5 arch/mips/sibyte/common/sb_tbprof.c | 601 +++ arch/mips/sibyte/sb1250/Makefile | 1 arch/mips/sibyte/sb1250/bcm1250_tbprof.c | 571 --- arch/mips/sni/pcimt.c | 105 arch/mips/sni/pcit.c | 122 - arch/mips/vr41xx/Kconfig | 92 arch/parisc/configs/c3000_defconfig | 1 arch/powerpc/Kconfig | 472 -- arch/powerpc/Kconfig.debug | 12 arch/powerpc/Makefile | 11 arch/powerpc/boot/.gitignore | 3 arch/powerpc/boot/Makefile | 122 - arch/powerpc/boot/crt0.S | 37 arch/powerpc/boot/cuboot-83xx.c | 68 arch/powerpc/boot/cuboot-85xx.c | 69 arch/powerpc/boot/devtree.c | 307 + arch/powerpc/boot/dts/kuroboxHD.dts | 7 arch/powerpc/boot/dts/kuroboxHG.dts | 7 arch/powerpc/boot/dts/lite5200.dts | 1 arch/powerpc/boot/dts/lite5200b.dts | 1 arch/powerpc/boot/dts/mpc7448hpc2.dts | 1 arch/powerpc/boot/dts/mpc8272ads.dts | 1 arch/powerpc/boot/dts/mpc8313erdb.dts | 1 arch/powerpc/boot/dts/mpc832x_mds.dts | 1 arch/powerpc/boot/dts/mpc832x_rdb.dts | 291 + arch/powerpc/boot/dts/mpc8349emitx.dts | 1 arch/powerpc/boot/dts/mpc8349emitxgp.dts | 1 arch/powerpc/boot/dts/mpc834x_mds.dts | 1 arch/powerpc/boot/dts/mpc836x_mds.dts | 1 arch/powerpc/boot/dts/mpc8540ads.dts | 1 arch/powerpc/boot/dts/mpc8541cds.dts | 1 arch/powerpc/boot/dts/mpc8544ds.dts | 136 + arch/powerpc/boot/dts/mpc8548cds.dts | 1 arch/powerpc/boot/dts/mpc8555cds.dts | 1 arch/powerpc/boot/dts/mpc8560ads.dts | 1 arch/powerpc/boot/dts/mpc8568mds.dts | 1 arch/powerpc/boot/dts/mpc8641_hpcn.dts | 25 arch/powerpc/boot/dts/mpc866ads.dts | 1 arch/powerpc/boot/dts/mpc885ads.dts | 1 arch/powerpc/boot/elf.h | 8 arch/powerpc/boot/elf_util.c | 76 arch/powerpc/boot/flatdevtree.c | 199 + arch/powerpc/boot/flatdevtree.h | 7 arch/powerpc/boot/flatdevtree_misc.c | 42 arch/powerpc/boot/gunzip_util.c | 206 + arch/powerpc/boot/gunzip_util.h | 45 arch/powerpc/boot/main.c | 369 +- arch/powerpc/boot/ns16550.c | 9 arch/powerpc/boot/of.c | 21 arch/powerpc/boot/ops.h | 103 arch/powerpc/boot/ppcboot.h | 108 arch/powerpc/boot/reg.h | 22 arch/powerpc/boot/simple_alloc.c | 31 arch/powerpc/boot/stdio.h | 5 arch/powerpc/boot/wrapper | 53 arch/powerpc/boot/zImage.coff.lds.S | 3 arch/powerpc/boot/zImage.lds.S | 1 arch/powerpc/configs/cell_defconfig | 57 arch/powerpc/configs/g5_defconfig | 2 arch/powerpc/configs/maple_defconfig | 2 arch/powerpc/configs/mpc832x_rdb_defconfig | 1292 ++++++ arch/powerpc/configs/mpc8544_ds_defconfig | 1077 +++++ arch/powerpc/configs/ppc64_defconfig | 2 arch/powerpc/kernel/Makefile | 4 arch/powerpc/kernel/align.c | 56 arch/powerpc/kernel/asm-offsets.c | 2 arch/powerpc/kernel/btext.c | 22 arch/powerpc/kernel/cpu_setup_pa6t.S | 2 arch/powerpc/kernel/cputable.c | 14 arch/powerpc/kernel/entry_32.S | 1 arch/powerpc/kernel/head_64.S | 16 arch/powerpc/kernel/ibmebus.c | 288 + arch/powerpc/kernel/iommu.c | 35 arch/powerpc/kernel/irq.c | 2 arch/powerpc/kernel/kprobes.c | 36 arch/powerpc/kernel/legacy_serial.c | 24 arch/powerpc/kernel/lparcfg.c | 58 arch/powerpc/kernel/machine_kexec_64.c | 14 arch/powerpc/kernel/misc_32.S | 4 arch/powerpc/kernel/of_device.c | 112 arch/powerpc/kernel/of_platform.c | 8 arch/powerpc/kernel/pci_32.c | 42 arch/powerpc/kernel/pci_64.c | 32 arch/powerpc/kernel/pci_dn.c | 9 arch/powerpc/kernel/ppc_ksyms.c | 3 arch/powerpc/kernel/process.c | 54 arch/powerpc/kernel/prom.c | 167 - arch/powerpc/kernel/prom_init.c | 55 arch/powerpc/kernel/prom_parse.c | 51 arch/powerpc/kernel/rtas-proc.c | 4 arch/powerpc/kernel/rtas.c | 19 arch/powerpc/kernel/rtas_pci.c | 14 arch/powerpc/kernel/setup-common.c | 68 arch/powerpc/kernel/setup_32.c | 17 arch/powerpc/kernel/setup_64.c | 11 arch/powerpc/kernel/smp.c | 4 arch/powerpc/kernel/sys_ppc32.c | 4 arch/powerpc/kernel/sysfs.c | 17 arch/powerpc/kernel/time.c | 2 arch/powerpc/kernel/traps.c | 76 arch/powerpc/kernel/vio.c | 17 arch/powerpc/lib/Makefile | 5 arch/powerpc/lib/copyuser_64.S | 6 arch/powerpc/lib/locks.c | 4 arch/powerpc/lib/mem_64.S | 6 arch/powerpc/lib/memcpy_64.S | 6 arch/powerpc/lib/sstep.c | 45 arch/powerpc/lib/strcase.c | 25 arch/powerpc/mm/hash_low_32.S | 22 arch/powerpc/mm/hash_low_64.S | 5 arch/powerpc/mm/hash_native_64.c | 2 arch/powerpc/mm/hash_utils_64.c | 127 - arch/powerpc/mm/hugetlbpage.c | 16 arch/powerpc/mm/init_32.c | 4 arch/powerpc/mm/lmb.c | 4 arch/powerpc/mm/mem.c | 3 arch/powerpc/mm/mmu_decl.h | 5 arch/powerpc/mm/numa.c | 24 arch/powerpc/mm/pgtable_32.c | 76 arch/powerpc/mm/ppc_mmu_32.c | 4 arch/powerpc/mm/tlb_64.c | 68 arch/powerpc/oprofile/Makefile | 2 arch/powerpc/oprofile/common.c | 3 arch/powerpc/oprofile/op_model_cell.c | 3 arch/powerpc/oprofile/op_model_pa6t.c | 234 + arch/powerpc/platforms/4xx/Kconfig | 372 +- arch/powerpc/platforms/52xx/Kconfig | 35 arch/powerpc/platforms/52xx/efika.c | 15 arch/powerpc/platforms/52xx/lite5200.c | 12 arch/powerpc/platforms/52xx/mpc52xx_common.c | 2 arch/powerpc/platforms/52xx/mpc52xx_pci.c | 2 arch/powerpc/platforms/82xx/Kconfig | 45 arch/powerpc/platforms/82xx/mpc82xx.c | 6 arch/powerpc/platforms/82xx/mpc82xx_ads.c | 12 arch/powerpc/platforms/83xx/Kconfig | 15 arch/powerpc/platforms/83xx/Makefile | 1 arch/powerpc/platforms/83xx/mpc832x_mds.c | 1 arch/powerpc/platforms/83xx/mpc832x_mds.h | 19 arch/powerpc/platforms/83xx/mpc832x_rdb.c | 138 + arch/powerpc/platforms/83xx/mpc834x_itx.h | 23 arch/powerpc/platforms/83xx/pci.c | 2 arch/powerpc/platforms/85xx/Kconfig | 38 arch/powerpc/platforms/85xx/Makefile | 1 arch/powerpc/platforms/85xx/mpc8544_ds.c | 144 + arch/powerpc/platforms/85xx/mpc85xx_ads.c | 2 arch/powerpc/platforms/85xx/mpc85xx_cds.c | 2 arch/powerpc/platforms/85xx/mpc85xx_mds.c | 2 arch/powerpc/platforms/85xx/pci.c | 2 arch/powerpc/platforms/86xx/Kconfig | 18 arch/powerpc/platforms/86xx/Makefile | 2 arch/powerpc/platforms/86xx/mpc86xx_hpcn.c | 4 arch/powerpc/platforms/86xx/mpc86xx_pcie.c | 173 - arch/powerpc/platforms/86xx/pci.c | 4 arch/powerpc/platforms/8xx/Kconfig | 67 arch/powerpc/platforms/8xx/m8xx_setup.c | 8 arch/powerpc/platforms/8xx/mpc86xads.h | 2 arch/powerpc/platforms/8xx/mpc86xads_setup.c | 4 arch/powerpc/platforms/8xx/mpc885ads.h | 2 arch/powerpc/platforms/8xx/mpc885ads_setup.c | 4 arch/powerpc/platforms/Kconfig | 259 + arch/powerpc/platforms/cell/Kconfig | 24 arch/powerpc/platforms/cell/cbe_cpufreq.c | 112 arch/powerpc/platforms/cell/cbe_regs.c | 168 + arch/powerpc/platforms/cell/cbe_regs.h | 5 arch/powerpc/platforms/cell/cbe_thermal.c | 181 + arch/powerpc/platforms/cell/interrupt.c | 4 arch/powerpc/platforms/cell/io-workarounds.c | 2 arch/powerpc/platforms/cell/iommu.c | 16 arch/powerpc/platforms/cell/ras.c | 160 + arch/powerpc/platforms/cell/setup.c | 12 arch/powerpc/platforms/cell/spider-pic.c | 8 arch/powerpc/platforms/cell/spu_base.c | 163 - arch/powerpc/platforms/cell/spu_coredump.c | 34 arch/powerpc/platforms/cell/spu_manage.c | 18 arch/powerpc/platforms/cell/spufs/Makefile | 2 arch/powerpc/platforms/cell/spufs/backing_ops.c | 6 arch/powerpc/platforms/cell/spufs/context.c | 45 arch/powerpc/platforms/cell/spufs/coredump.c | 19 arch/powerpc/platforms/cell/spufs/fault.c | 211 + arch/powerpc/platforms/cell/spufs/file.c | 152 + arch/powerpc/platforms/cell/spufs/hw_ops.c | 9 arch/powerpc/platforms/cell/spufs/inode.c | 44 arch/powerpc/platforms/cell/spufs/run.c | 123 - arch/powerpc/platforms/cell/spufs/sched.c | 113 arch/powerpc/platforms/cell/spufs/spufs.h | 34 arch/powerpc/platforms/cell/spufs/switch.c | 8 arch/powerpc/platforms/celleb/Kconfig | 9 arch/powerpc/platforms/celleb/iommu.c | 6 arch/powerpc/platforms/celleb/pci.c | 18 arch/powerpc/platforms/celleb/setup.c | 12 arch/powerpc/platforms/chrp/Kconfig | 11 arch/powerpc/platforms/chrp/nvram.c | 2 arch/powerpc/platforms/chrp/pci.c | 25 arch/powerpc/platforms/chrp/setup.c | 52 arch/powerpc/platforms/chrp/time.c | 13 arch/powerpc/platforms/embedded6xx/Kconfig | 264 - arch/powerpc/platforms/embedded6xx/linkstation.c | 8 arch/powerpc/platforms/embedded6xx/ls_uart.c | 4 arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c | 15 arch/powerpc/platforms/iseries/Kconfig | 4 arch/powerpc/platforms/iseries/iommu.c | 4 arch/powerpc/platforms/iseries/irq.c | 4 arch/powerpc/platforms/iseries/pci.c | 5 arch/powerpc/platforms/iseries/setup.c | 10 arch/powerpc/platforms/iseries/viopath.c | 2 arch/powerpc/platforms/maple/Kconfig | 17 arch/powerpc/platforms/maple/pci.c | 8 arch/powerpc/platforms/maple/setup.c | 17 arch/powerpc/platforms/pasemi/Kconfig | 19 arch/powerpc/platforms/pasemi/Makefile | 3 arch/powerpc/platforms/pasemi/cpufreq.c | 308 + arch/powerpc/platforms/pasemi/gpio_mdio.c | 339 + arch/powerpc/platforms/pasemi/idle.c | 9 arch/powerpc/platforms/pasemi/iommu.c | 6 arch/powerpc/platforms/pasemi/pasemi.h | 8 arch/powerpc/platforms/pasemi/pci.c | 16 arch/powerpc/platforms/pasemi/setup.c | 28 arch/powerpc/platforms/powermac/Kconfig | 20 arch/powerpc/platforms/powermac/backlight.c | 9 arch/powerpc/platforms/powermac/cpufreq_32.c | 24 arch/powerpc/platforms/powermac/cpufreq_64.c | 16 arch/powerpc/platforms/powermac/feature.c | 101 arch/powerpc/platforms/powermac/low_i2c.c | 15 arch/powerpc/platforms/powermac/pci.c | 41 arch/powerpc/platforms/powermac/pfunc_base.c | 2 arch/powerpc/platforms/powermac/pfunc_core.c | 5 arch/powerpc/platforms/powermac/pic.c | 8 arch/powerpc/platforms/powermac/setup.c | 61 arch/powerpc/platforms/powermac/smp.c | 11 arch/powerpc/platforms/powermac/time.c | 38 arch/powerpc/platforms/powermac/udbg_scc.c | 6 arch/powerpc/platforms/prep/Kconfig | 9 arch/powerpc/platforms/ps3/Kconfig | 16 arch/powerpc/platforms/ps3/htab.c | 2 arch/powerpc/platforms/pseries/Kconfig | 10 arch/powerpc/platforms/pseries/Makefile | 5 arch/powerpc/platforms/pseries/eeh.c | 204 + arch/powerpc/platforms/pseries/eeh_driver.c | 78 arch/powerpc/platforms/pseries/eeh_event.c | 8 arch/powerpc/platforms/pseries/firmware.c | 2 arch/powerpc/platforms/pseries/hotplug-cpu.c | 4 arch/powerpc/platforms/pseries/iommu.c | 67 arch/powerpc/platforms/pseries/lpar.c | 6 arch/powerpc/platforms/pseries/nvram.c | 2 arch/powerpc/platforms/pseries/pci.c | 3 arch/powerpc/platforms/pseries/pci_dlpar.c | 1 arch/powerpc/platforms/pseries/ras.c | 2 arch/powerpc/platforms/pseries/rtasd.c | 2 arch/powerpc/platforms/pseries/setup.c | 44 arch/powerpc/platforms/pseries/xics.c | 17 arch/powerpc/sysdev/Makefile | 5 arch/powerpc/sysdev/dart_iommu.c | 4 arch/powerpc/sysdev/dcr.c | 14 arch/powerpc/sysdev/fsl_pcie.c | 171 + arch/powerpc/sysdev/fsl_pcie.h | 94 arch/powerpc/sysdev/fsl_soc.c | 90 arch/powerpc/sysdev/mpic.c | 32 arch/powerpc/sysdev/pmi.c | 29 arch/powerpc/sysdev/qe_lib/Kconfig | 10 arch/powerpc/sysdev/qe_lib/qe.c | 4 arch/powerpc/sysdev/qe_lib/qe_io.c | 6 arch/powerpc/sysdev/qe_lib/ucc_fast.c | 3 arch/powerpc/sysdev/qe_lib/ucc_slow.c | 4 arch/powerpc/sysdev/timer.c | 71 arch/powerpc/sysdev/tsi108_dev.c | 16 arch/powerpc/sysdev/tsi108_pci.c | 2 arch/powerpc/sysdev/uic.c | 342 ++ arch/powerpc/xmon/xmon.c | 11 arch/ppc/8260_io/enet.c | 1 arch/ppc/8260_io/fcc_enet.c | 1 arch/ppc/8xx_io/Kconfig | 4 arch/ppc/8xx_io/Makefile | 1 arch/ppc/8xx_io/cs4218.h | 166 - arch/ppc/8xx_io/cs4218_tdm.c | 2833 ------------ arch/ppc/8xx_io/enet.c | 1 arch/ppc/8xx_io/fec.c | 1 arch/ppc/boot/common/misc-common.c | 15 arch/ppc/boot/simple/Makefile | 1 arch/ppc/boot/simple/uartlite_tty.c | 37 arch/ppc/kernel/asm-offsets.c | 1 arch/ppc/kernel/entry.S | 1 arch/ppc/kernel/ppc_ksyms.c | 2 arch/ppc/lib/Makefile | 2 arch/ppc/lib/strcase.c | 24 arch/ppc/platforms/4xx/Kconfig | 15 arch/ppc/platforms/4xx/Makefile | 1 arch/ppc/platforms/4xx/ocotea.c | 4 arch/ppc/platforms/4xx/taishan.c | 2 arch/ppc/platforms/4xx/virtex.c | 56 arch/ppc/platforms/4xx/virtex.h | 34 arch/ppc/platforms/4xx/xilinx_ml300.c | 65 arch/ppc/platforms/4xx/xilinx_ml300.h | 45 arch/ppc/platforms/4xx/xilinx_ml403.c | 66 arch/ppc/platforms/4xx/xilinx_ml403.h | 49 arch/ppc/platforms/4xx/xparameters/xparameters.h | 60 arch/ppc/platforms/rpxclassic.h | 4 arch/ppc/platforms/rpxhiox.h | 41 arch/ppc/platforms/rpxlite.h | 4 arch/ppc/syslib/Makefile | 3 arch/ppc/syslib/cpc710.h | 81 arch/ppc/syslib/m8xx_setup.c | 2 arch/ppc/syslib/ppc4xx_sgdma.c | 1 arch/ppc/syslib/virtex_devices.c | 233 + arch/ppc/syslib/virtex_devices.h | 27 arch/s390/Kconfig | 13 arch/s390/Makefile | 5 arch/s390/appldata/appldata_base.c | 38 arch/s390/appldata/appldata_net_sum.c | 3 arch/s390/crypto/sha1_s390.c | 129 - arch/s390/crypto/sha256_s390.c | 38 arch/s390/defconfig | 3 arch/s390/kernel/Makefile | 2 arch/s390/kernel/compat_linux.c | 60 arch/s390/kernel/compat_signal.c | 14 arch/s390/kernel/dis.c | 1278 ++++++ arch/s390/kernel/early.c | 7 arch/s390/kernel/entry.S | 87 arch/s390/kernel/entry64.S | 100 arch/s390/kernel/head64.S | 72 arch/s390/kernel/ipl.c | 253 + arch/s390/kernel/module.c | 4 arch/s390/kernel/process.c | 82 arch/s390/kernel/setup.c | 38 arch/s390/kernel/signal.c | 10 arch/s390/kernel/smp.c | 369 +- arch/s390/kernel/sys_s390.c | 20 arch/s390/kernel/syscalls.S | 14 arch/s390/kernel/time.c | 34 arch/s390/kernel/traps.c | 72 arch/s390/kernel/vmlinux.lds.S | 10 arch/s390/kernel/vtime.c | 16 arch/s390/lib/Makefile | 2 arch/s390/lib/div64.c | 2 arch/s390/mm/fault.c | 331 + arch/sh/boards/hp6xx/pm.c | 8 arch/sh/lib/Makefile | 2 arch/sh/lib/strcasecmp.c | 26 arch/sparc/kernel/ebus.c | 14 arch/sparc/kernel/of_device.c | 21 arch/sparc/kernel/pcic.c | 4 arch/sparc/kernel/prom.c | 19 arch/sparc/kernel/time.c | 2 arch/sparc64/Kconfig | 15 arch/sparc64/kernel/central.c | 12 arch/sparc64/kernel/chmc.c | 4 arch/sparc64/kernel/ebus.c | 14 arch/sparc64/kernel/irq.c | 28 arch/sparc64/kernel/isa.c | 36 arch/sparc64/kernel/of_device.c | 59 arch/sparc64/kernel/pci.c | 599 ++- arch/sparc64/kernel/pci_common.c | 806 ---- arch/sparc64/kernel/pci_impl.h | 33 arch/sparc64/kernel/pci_iommu.c | 104 arch/sparc64/kernel/pci_psycho.c | 154 - arch/sparc64/kernel/pci_sabre.c | 465 -- arch/sparc64/kernel/pci_schizo.c | 295 - arch/sparc64/kernel/pci_sun4v.c | 402 -- arch/sparc64/kernel/process.c | 15 arch/sparc64/kernel/prom.c | 39 arch/sparc64/kernel/sbus.c | 243 + arch/sparc64/kernel/smp.c | 116 - arch/sparc64/kernel/sparc64_ksyms.c | 1 arch/sparc64/kernel/time.c | 484 +- arch/sparc64/kernel/ttable.S | 6 arch/sparc64/mm/init.c | 280 - arch/sparc64/solaris/misc.c | 3 arch/um/drivers/daemon_kern.c | 2 arch/um/drivers/mcast_kern.c | 2 arch/um/drivers/net_kern.c | 2 arch/um/drivers/pcap_kern.c | 2 arch/um/drivers/slip_kern.c | 2 arch/um/drivers/slirp_kern.c | 2 arch/um/os-Linux/drivers/ethertap_kern.c | 2 arch/um/os-Linux/drivers/tuntap_kern.c | 2 arch/x86_64/defconfig | 1 arch/xtensa/lib/Makefile | 2 arch/xtensa/lib/strcasecmp.c | 32 arch/xtensa/platform-iss/network.c | 2 block/cfq-iosched.c | 853 ++-- block/elevator.c | 17 block/ll_rw_blk.c | 1 crypto/michael_mic.c | 4 drivers/Makefile | 1 drivers/acpi/Kconfig | 44 drivers/acpi/Makefile | 10 drivers/acpi/acpi_memhotplug.c | 13 drivers/acpi/bus.c | 4 drivers/acpi/container.c | 6 drivers/acpi/dock.c | 25 drivers/acpi/ec.c | 495 +- drivers/acpi/i2c_ec.c | 403 -- drivers/acpi/i2c_ec.h | 23 drivers/acpi/ibm_acpi.c | 2798 ------------ drivers/acpi/processor_core.c | 4 drivers/acpi/processor_idle.c | 8 drivers/acpi/sbs.c | 1291 +++--- drivers/acpi/scan.c | 4 drivers/acpi/sleep/main.c | 13 drivers/acpi/sleep/proc.c | 16 drivers/acpi/tables/tbfadt.c | 12 drivers/amba/bus.c | 13 drivers/ata/Kconfig | 43 drivers/ata/Makefile | 1 drivers/ata/ahci.c | 473 +- drivers/ata/ata_generic.c | 32 drivers/ata/ata_piix.c | 56 drivers/ata/libata-core.c | 1015 +++- drivers/ata/libata-eh.c | 15 drivers/ata/libata-scsi.c | 85 drivers/ata/libata-sff.c | 636 ++- drivers/ata/libata.h | 10 drivers/ata/pata_ali.c | 74 drivers/ata/pata_amd.c | 89 drivers/ata/pata_artop.c | 26 drivers/ata/pata_atiixp.c | 26 drivers/ata/pata_cmd640.c | 312 + drivers/ata/pata_cmd64x.c | 34 drivers/ata/pata_cs5520.c | 145 - drivers/ata/pata_cs5530.c | 15 drivers/ata/pata_cs5535.c | 24 drivers/ata/pata_cypress.c | 14 drivers/ata/pata_efar.c | 31 drivers/ata/pata_hpt366.c | 26 drivers/ata/pata_hpt37x.c | 381 +- drivers/ata/pata_hpt3x2n.c | 74 drivers/ata/pata_hpt3x3.c | 22 drivers/ata/pata_isapnp.c | 44 drivers/ata/pata_it8213.c | 36 drivers/ata/pata_it821x.c | 36 drivers/ata/pata_ixp4xx_cf.c | 40 drivers/ata/pata_legacy.c | 45 drivers/ata/pata_marvell.c | 20 drivers/ata/pata_mpc52xx.c | 49 drivers/ata/pata_mpiix.c | 40 drivers/ata/pata_netcell.c | 31 drivers/ata/pata_ns87410.c | 6 drivers/ata/pata_oldpiix.c | 6 drivers/ata/pata_opti.c | 5 drivers/ata/pata_optidma.c | 54 drivers/ata/pata_pcmcia.c | 77 drivers/ata/pata_pdc2027x.c | 158 - drivers/ata/pata_pdc202xx_old.c | 45 drivers/ata/pata_platform.c | 44 drivers/ata/pata_qdi.c | 47 drivers/ata/pata_radisys.c | 37 drivers/ata/pata_rz1000.c | 28 drivers/ata/pata_sc1200.c | 1 drivers/ata/pata_scc.c | 51 drivers/ata/pata_serverworks.c | 34 drivers/ata/pata_sil680.c | 11 drivers/ata/pata_sis.c | 109 drivers/ata/pata_sl82c105.c | 2 drivers/ata/pata_triflex.c | 4 drivers/ata/pata_via.c | 27 drivers/ata/pata_winbond.c | 101 drivers/ata/pdc_adma.c | 85 drivers/ata/sata_inic162x.c | 86 drivers/ata/sata_mv.c | 213 - drivers/ata/sata_nv.c | 130 - drivers/ata/sata_promise.c | 398 +- drivers/ata/sata_qstor.c | 62 drivers/ata/sata_sil.c | 118 - drivers/ata/sata_sil24.c | 122 - drivers/ata/sata_sis.c | 50 drivers/ata/sata_svw.c | 107 drivers/ata/sata_sx4.c | 150 - drivers/ata/sata_uli.c | 65 drivers/ata/sata_via.c | 217 - drivers/ata/sata_vsc.c | 72 drivers/atm/ambassador.c | 2 drivers/atm/atmtcp.c | 6 drivers/atm/eni.c | 4 drivers/atm/eni.h | 2 drivers/atm/fore200e.c | 20 drivers/atm/fore200e.h | 2 drivers/atm/he.c | 4 drivers/atm/idt77252.c | 28 drivers/atm/nicstar.c | 14 drivers/base/attribute_container.c | 26 drivers/base/base.h | 2 drivers/base/bus.c | 112 drivers/base/class.c | 2 drivers/base/core.c | 293 + drivers/base/dd.c | 66 drivers/base/dmapool.c | 14 drivers/base/driver.c | 20 drivers/base/firmware_class.c | 10 drivers/base/power/main.c | 3 drivers/base/power/resume.c | 13 drivers/base/power/shutdown.c | 2 drivers/base/power/suspend.c | 12 drivers/block/aoe/aoe.h | 9 drivers/block/aoe/aoecmd.c | 17 drivers/block/aoe/aoenet.c | 2 drivers/block/ub.c | 11 drivers/bluetooth/bfusb.c | 2 drivers/bluetooth/bluecard_cs.c | 6 drivers/bluetooth/bpa10x.c | 4 drivers/bluetooth/bt3c_cs.c | 6 drivers/bluetooth/btuart_cs.c | 6 drivers/bluetooth/dtl1_cs.c | 2 drivers/bluetooth/hci_h4.c | 6 drivers/char/briq_panel.c | 7 drivers/char/hvc_console.c | 49 drivers/char/hvc_iseries.c | 2 drivers/char/hvc_vio.c | 2 drivers/char/hvsi.c | 4 drivers/char/pcmcia/synclink_cs.c | 2 drivers/char/random.c | 38 drivers/char/sonypi.c | 53 drivers/char/tpm/tpm_atmel.h | 4 drivers/char/watchdog/Kconfig | 2 drivers/clocksource/acpi_pm.c | 2 drivers/connector/connector.c | 4 drivers/hid/Kconfig | 2 drivers/hid/Makefile | 4 drivers/hid/hid-core.c | 9 drivers/hid/hid-input.c | 27 drivers/hid/usbhid/Kconfig | 149 + drivers/hid/usbhid/Makefile | 35 drivers/hid/usbhid/hid-core.c | 1114 +++++ drivers/hid/usbhid/hid-ff.c | 91 drivers/hid/usbhid/hid-lgff.c | 151 + drivers/hid/usbhid/hid-pidff.c | 1331 ++++++ drivers/hid/usbhid/hid-plff.c | 129 + drivers/hid/usbhid/hid-quirks.c | 681 +++ drivers/hid/usbhid/hid-tmff.c | 147 + drivers/hid/usbhid/hid-zpff.c | 111 drivers/hid/usbhid/hiddev.c | 847 ++++ drivers/hid/usbhid/usbhid.h | 87 drivers/hid/usbhid/usbkbd.c | 367 ++ drivers/hid/usbhid/usbmouse.c | 252 + drivers/hwmon/ams/ams-core.c | 4 drivers/hwmon/ams/ams-i2c.c | 4 drivers/hwmon/ams/ams-pmu.c | 4 drivers/ide/ide-proc.c | 4 drivers/ieee1394/Kconfig | 52 drivers/ieee1394/config_roms.c | 93 drivers/ieee1394/config_roms.h | 20 drivers/ieee1394/csr1212.c | 870 ++-- drivers/ieee1394/csr1212.h | 483 -- drivers/ieee1394/dma.c | 24 drivers/ieee1394/dma.h | 22 drivers/ieee1394/eth1394.c | 802 ++-- drivers/ieee1394/eth1394.h | 27 drivers/ieee1394/highlevel.c | 89 drivers/ieee1394/highlevel.h | 55 drivers/ieee1394/hosts.c | 23 drivers/ieee1394/hosts.h | 10 drivers/ieee1394/ieee1394_core.c | 461 +- drivers/ieee1394/ieee1394_core.h | 100 drivers/ieee1394/ieee1394_transactions.c | 43 drivers/ieee1394/ieee1394_transactions.h | 20 drivers/ieee1394/iso.c | 85 drivers/ieee1394/iso.h | 35 drivers/ieee1394/nodemgr.c | 83 drivers/ieee1394/nodemgr.h | 24 drivers/ieee1394/ohci1394.c | 12 drivers/ieee1394/ohci1394.h | 4 drivers/ieee1394/raw1394.c | 3 drivers/ieee1394/sbp2.c | 39 drivers/ieee1394/sbp2.h | 8 drivers/infiniband/core/mad.c | 34 drivers/infiniband/core/sa_query.c | 24 drivers/infiniband/core/smi.c | 86 drivers/infiniband/core/smi.h | 34 drivers/infiniband/core/sysfs.c | 1 drivers/infiniband/core/ucm.c | 23 drivers/infiniband/core/ucma.c | 22 drivers/infiniband/core/user_mad.c | 20 drivers/infiniband/hw/amso1100/c2.c | 6 drivers/infiniband/hw/amso1100/c2_provider.c | 1 drivers/infiniband/hw/cxgb3/iwch_cm.c | 17 drivers/infiniband/hw/cxgb3/iwch_provider.c | 1 drivers/infiniband/hw/ehca/ehca_classes.h | 1 drivers/infiniband/hw/ehca/ehca_hca.c | 55 drivers/infiniband/hw/ehca/ehca_main.c | 5 drivers/infiniband/hw/ehca/hcp_if.c | 24 drivers/infiniband/hw/ehca/hcp_if.h | 4 drivers/infiniband/hw/ipath/ipath_common.h | 23 drivers/infiniband/hw/ipath/ipath_cq.c | 38 drivers/infiniband/hw/ipath/ipath_debug.h | 1 drivers/infiniband/hw/ipath/ipath_diag.c | 11 drivers/infiniband/hw/ipath/ipath_driver.c | 123 - drivers/infiniband/hw/ipath/ipath_eeprom.c | 4 drivers/infiniband/hw/ipath/ipath_file_ops.c | 287 + drivers/infiniband/hw/ipath/ipath_iba6110.c | 152 - drivers/infiniband/hw/ipath/ipath_iba6120.c | 73 drivers/infiniband/hw/ipath/ipath_init_chip.c | 86 drivers/infiniband/hw/ipath/ipath_intr.c | 100 drivers/infiniband/hw/ipath/ipath_kernel.h | 10 drivers/infiniband/hw/ipath/ipath_keys.c | 14 drivers/infiniband/hw/ipath/ipath_mr.c | 12 drivers/infiniband/hw/ipath/ipath_qp.c | 133 - drivers/infiniband/hw/ipath/ipath_rc.c | 920 ++-- drivers/infiniband/hw/ipath/ipath_registers.h | 22 drivers/infiniband/hw/ipath/ipath_ruc.c | 63 drivers/infiniband/hw/ipath/ipath_stats.c | 16 drivers/infiniband/hw/ipath/ipath_uc.c | 6 drivers/infiniband/hw/ipath/ipath_ud.c | 8 drivers/infiniband/hw/ipath/ipath_verbs.c | 15 drivers/infiniband/hw/ipath/ipath_verbs.h | 57 drivers/infiniband/hw/mthca/mthca_main.c | 10 drivers/infiniband/hw/mthca/mthca_mr.c | 6 drivers/infiniband/hw/mthca/mthca_provider.c | 1 drivers/infiniband/hw/mthca/mthca_qp.c | 7 drivers/infiniband/ulp/ipoib/ipoib_cm.c | 66 drivers/infiniband/ulp/ipoib/ipoib_ib.c | 10 drivers/infiniband/ulp/ipoib/ipoib_main.c | 12 drivers/input/gameport/gameport.c | 39 drivers/input/serio/serio.c | 41 drivers/isdn/act2000/module.c | 2 drivers/isdn/gigaset/usb-gigaset.c | 2 drivers/isdn/hardware/avm/b1dma.c | 3 drivers/isdn/hardware/avm/c4.c | 3 drivers/isdn/hisax/elsa_ser.c | 6 drivers/isdn/hisax/isdnl2.c | 3 drivers/isdn/hysdn/hycapi.c | 5 drivers/isdn/hysdn/hysdn_net.c | 2 drivers/isdn/hysdn/hysdn_sched.c | 5 drivers/isdn/i4l/isdn_common.c | 2 drivers/isdn/i4l/isdn_net.c | 11 drivers/isdn/i4l/isdn_ppp.c | 9 drivers/isdn/isdnloop/isdnloop.c | 3 drivers/isdn/pcbit/capi.c | 12 drivers/macintosh/adb.c | 42 drivers/macintosh/ans-lcd.c | 9 drivers/macintosh/apm_emu.c | 13 drivers/macintosh/mac_hid.c | 2 drivers/macintosh/macio-adb.c | 16 drivers/macintosh/macio_asic.c | 100 drivers/macintosh/macio_sysfs.c | 10 drivers/macintosh/rack-meter.c | 2 drivers/macintosh/smu.c | 8 drivers/macintosh/therm_adt746x.c | 8 drivers/macintosh/therm_pm72.c | 8 drivers/macintosh/therm_windtunnel.c | 2 drivers/macintosh/via-cuda.c | 2 drivers/macintosh/via-pmu-led.c | 10 drivers/macintosh/via-pmu.c | 55 drivers/macintosh/windfarm_lm75_sensor.c | 2 drivers/macintosh/windfarm_max6690_sensor.c | 2 drivers/macintosh/windfarm_smu_controls.c | 8 drivers/macintosh/windfarm_smu_sat.c | 8 drivers/macintosh/windfarm_smu_sensors.c | 6 drivers/md/dm-crypt.c | 2 drivers/md/dm-io.c | 2 drivers/md/dm.c | 2 drivers/media/common/ir-keymaps.c | 18 drivers/media/common/saa7146_video.c | 6 drivers/media/dvb/b2c2/Kconfig | 1 drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 3 drivers/media/dvb/b2c2/flexcop-pci.c | 9 drivers/media/dvb/bt8xx/Kconfig | 2 drivers/media/dvb/bt8xx/bt878.c | 4 drivers/media/dvb/bt8xx/dvb-bt8xx.c | 3 drivers/media/dvb/bt8xx/dvb-bt8xx.h | 2 drivers/media/dvb/dvb-core/dmxdev.c | 56 drivers/media/dvb/dvb-core/dmxdev.h | 2 drivers/media/dvb/dvb-core/dvb_frontend.c | 20 drivers/media/dvb/dvb-core/dvb_net.c | 48 drivers/media/dvb/dvb-core/dvb_net.h | 1 drivers/media/dvb/dvb-core/dvbdev.c | 1 drivers/media/dvb/dvb-core/dvbdev.h | 1 drivers/media/dvb/dvb-usb/Kconfig | 12 drivers/media/dvb/dvb-usb/Makefile | 4 drivers/media/dvb/dvb-usb/au6610.c | 6 drivers/media/dvb/dvb-usb/cxusb.c | 4 drivers/media/dvb/dvb-usb/dib0700_core.c | 4 drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 6 drivers/media/dvb/dvb-usb/gl861.c | 8 drivers/media/dvb/dvb-usb/m920x.c | 235 + drivers/media/dvb/dvb-usb/m920x.h | 46 drivers/media/dvb/dvb-usb/opera1.c | 581 +++ drivers/media/dvb/dvb-usb/opera1.h | 9 drivers/media/dvb/dvb-usb/ttusb2.c | 7 drivers/media/dvb/frontends/Kconfig | 28 drivers/media/dvb/frontends/Makefile | 3 drivers/media/dvb/frontends/dvb-pll.c | 340 + drivers/media/dvb/frontends/dvb-pll.h | 16 drivers/media/dvb/frontends/lgdt330x.c | 4 drivers/media/dvb/frontends/lgh06xf.c | 134 - drivers/media/dvb/frontends/lgh06xf.h | 35 drivers/media/dvb/frontends/or51132.c | 305 + drivers/media/dvb/frontends/tda10021.c | 47 drivers/media/dvb/frontends/tda10021.h | 55 drivers/media/dvb/frontends/tda10023.c | 540 ++ drivers/media/dvb/frontends/tda1002x.h | 60 drivers/media/dvb/frontends/tda1004x.c | 98 drivers/media/dvb/frontends/tda1004x.h | 54 drivers/media/dvb/frontends/tda827x.c | 512 ++ drivers/media/dvb/frontends/tda827x.h | 62 drivers/media/dvb/pluto2/Kconfig | 1 drivers/media/dvb/ttpci/Kconfig | 6 drivers/media/dvb/ttpci/av7110.c | 17 drivers/media/dvb/ttpci/av7110.h | 28 drivers/media/dvb/ttpci/av7110_av.c | 24 drivers/media/dvb/ttpci/av7110_hw.h | 10 drivers/media/dvb/ttpci/av7110_ir.c | 365 +- drivers/media/dvb/ttpci/budget-av.c | 147 - drivers/media/dvb/ttpci/budget-ci.c | 96 drivers/media/dvb/ttpci/budget-core.c | 58 drivers/media/dvb/ttpci/budget.h | 3 drivers/media/dvb/ttusb-budget/Kconfig | 1 drivers/media/radio/radio-aimslab.c | 240 + drivers/media/radio/radio-gemtek-pci.c | 253 + drivers/media/radio/radio-gemtek.c | 260 + drivers/media/radio/radio-maestro.c | 266 + drivers/media/radio/radio-rtrack2.c | 255 + drivers/media/radio/radio-sf16fmi.c | 262 + drivers/media/radio/radio-sf16fmr2.c | 350 +- drivers/media/radio/radio-terratec.c | 247 + drivers/media/radio/radio-trust.c | 256 + drivers/media/radio/radio-typhoon.c | 239 + drivers/media/radio/radio-zoltrix.c | 256 + drivers/media/video/Kconfig | 20 drivers/media/video/Makefile | 2 drivers/media/video/bt8xx/bttv-cards.c | 53 drivers/media/video/bt8xx/bttv-driver.c | 24 drivers/media/video/bt8xx/bttv-gpio.c | 5 drivers/media/video/bt8xx/bttv-i2c.c | 2 drivers/media/video/bt8xx/bttv-if.c | 48 drivers/media/video/bt8xx/bttv.h | 25 drivers/media/video/bt8xx/bttvp.h | 3 drivers/media/video/cafe_ccic.c | 79 drivers/media/video/cpia_pp.c | 49 drivers/media/video/cs53l32a.c | 4 drivers/media/video/cx2341x.c | 72 drivers/media/video/cx25840/cx25840-core.c | 9 drivers/media/video/cx25840/cx25840-core.h | 3 drivers/media/video/cx25840/cx25840-firmware.c | 1 drivers/media/video/cx88/Kconfig | 1 drivers/media/video/cx88/cx88-alsa.c | 9 drivers/media/video/cx88/cx88-cards.c | 37 drivers/media/video/cx88/cx88-core.c | 8 drivers/media/video/cx88/cx88-dvb.c | 31 drivers/media/video/cx88/cx88-i2c.c | 3 drivers/media/video/cx88/cx88-mpeg.c | 29 drivers/media/video/cx88/cx88-video.c | 5 drivers/media/video/cx88/cx88.h | 4 drivers/media/video/em28xx/em28xx-i2c.c | 2 drivers/media/video/ir-kbd-i2c.c | 2 drivers/media/video/ivtv/Kconfig | 26 drivers/media/video/ivtv/Makefile | 7 drivers/media/video/ivtv/ivtv-audio.c | 74 drivers/media/video/ivtv/ivtv-audio.h | 23 drivers/media/video/ivtv/ivtv-cards.c | 964 ++++ drivers/media/video/ivtv/ivtv-cards.h | 207 + drivers/media/video/ivtv/ivtv-controls.c | 303 + drivers/media/video/ivtv/ivtv-controls.h | 21 drivers/media/video/ivtv/ivtv-driver.c | 1374 ++++++ drivers/media/video/ivtv/ivtv-driver.h | 868 ++++ drivers/media/video/ivtv/ivtv-fileops.c | 921 ++++ drivers/media/video/ivtv/ivtv-fileops.h | 44 drivers/media/video/ivtv/ivtv-firmware.c | 272 + drivers/media/video/ivtv/ivtv-firmware.h | 25 drivers/media/video/ivtv/ivtv-gpio.c | 307 + drivers/media/video/ivtv/ivtv-gpio.h | 25 drivers/media/video/ivtv/ivtv-i2c.c | 748 +++ drivers/media/video/ivtv/ivtv-i2c.h | 36 drivers/media/video/ivtv/ivtv-ioctl.c | 1567 +++++++ drivers/media/video/ivtv/ivtv-ioctl.h | 28 drivers/media/video/ivtv/ivtv-irq.c | 838 ++++ drivers/media/video/ivtv/ivtv-irq.h | 26 drivers/media/video/ivtv/ivtv-mailbox.c | 360 ++ drivers/media/video/ivtv/ivtv-mailbox.h | 25 drivers/media/video/ivtv/ivtv-queue.c | 262 + drivers/media/video/ivtv/ivtv-queue.h | 64 drivers/media/video/ivtv/ivtv-streams.c | 977 ++++ drivers/media/video/ivtv/ivtv-streams.h | 31 drivers/media/video/ivtv/ivtv-udma.c | 200 + drivers/media/video/ivtv/ivtv-udma.h | 43 drivers/media/video/ivtv/ivtv-vbi.c | 538 ++ drivers/media/video/ivtv/ivtv-vbi.h | 26 drivers/media/video/ivtv/ivtv-version.h | 26 drivers/media/video/ivtv/ivtv-video.c | 142 + drivers/media/video/ivtv/ivtv-video.h | 24 drivers/media/video/ivtv/ivtv-yuv.c | 1129 +++++ drivers/media/video/ivtv/ivtv-yuv.h | 24 drivers/media/video/meye.c | 62 drivers/media/video/meye.h | 2 drivers/media/video/msp3400-driver.c | 5 drivers/media/video/msp3400-driver.h | 1 drivers/media/video/ov7670.c | 40 drivers/media/video/planb.c | 11 drivers/media/video/planb.h | 1 drivers/media/video/pvrusb2/pvrusb2-encoder.c | 2 drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 2 drivers/media/video/pvrusb2/pvrusb2-hdw.c | 16 drivers/media/video/pvrusb2/pvrusb2-hdw.h | 3 drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 30 drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 2 drivers/media/video/pwc/pwc-ctrl.c | 61 drivers/media/video/pwc/pwc-if.c | 16 drivers/media/video/pwc/pwc-ioctl.h | 36 drivers/media/video/pwc/pwc-kiara.c | 2 drivers/media/video/pwc/pwc-kiara.h | 5 drivers/media/video/pwc/pwc-timon.c | 4 drivers/media/video/pwc/pwc-timon.h | 6 drivers/media/video/pwc/pwc-v4l.c | 60 drivers/media/video/pwc/pwc.h | 5 drivers/media/video/saa7115.c | 10 drivers/media/video/saa7127.c | 12 drivers/media/video/saa7134/Kconfig | 1 drivers/media/video/saa7134/saa7134-cards.c | 204 + drivers/media/video/saa7134/saa7134-core.c | 125 - drivers/media/video/saa7134/saa7134-dvb.c | 1027 ++--- drivers/media/video/saa7134/saa7134-i2c.c | 4 drivers/media/video/saa7134/saa7134-input.c | 1 drivers/media/video/saa7134/saa7134-video.c | 85 drivers/media/video/saa7134/saa7134.h | 10 drivers/media/video/se401.c | 36 drivers/media/video/sn9c102/Kconfig | 2 drivers/media/video/sn9c102/Makefile | 17 drivers/media/video/sn9c102/sn9c102.h | 17 drivers/media/video/sn9c102/sn9c102_core.c | 274 + drivers/media/video/sn9c102/sn9c102_devtable.h | 14 drivers/media/video/sn9c102/sn9c102_hv7131d.c | 25 drivers/media/video/sn9c102/sn9c102_hv7131r.c | 366 ++ drivers/media/video/sn9c102/sn9c102_mi0343.c | 130 - drivers/media/video/sn9c102/sn9c102_mi0360.c | 338 + drivers/media/video/sn9c102/sn9c102_ov7630.c | 121 - drivers/media/video/sn9c102/sn9c102_ov7660.c | 234 - drivers/media/video/sn9c102/sn9c102_pas106b.c | 23 drivers/media/video/sn9c102/sn9c102_pas202bcb.c | 77 drivers/media/video/sn9c102/sn9c102_sensor.h | 12 drivers/media/video/sn9c102/sn9c102_tas5110c1b.c | 18 drivers/media/video/sn9c102/sn9c102_tas5110d.c | 118 + drivers/media/video/sn9c102/sn9c102_tas5130d1b.c | 19 drivers/media/video/tda7432.c | 1 drivers/media/video/tda8290.c | 144 - drivers/media/video/tda9875.c | 1 drivers/media/video/tuner-core.c | 28 drivers/media/video/tvaudio.c | 5 drivers/media/video/tveeprom.c | 45 drivers/media/video/upd64031a.c | 4 drivers/media/video/upd64083.c | 5 drivers/media/video/usbvideo/usbvideo.c | 18 drivers/media/video/usbvision/usbvision-cards.c | 1165 +++++ drivers/media/video/usbvision/usbvision-cards.h | 66 drivers/media/video/usbvision/usbvision-core.c | 10 drivers/media/video/usbvision/usbvision-i2c.c | 104 drivers/media/video/usbvision/usbvision-video.c | 173 - drivers/media/video/usbvision/usbvision.h | 39 drivers/media/video/v4l2-common.c | 41 drivers/media/video/videocodec.c | 3 drivers/media/video/videodev.c | 40 drivers/media/video/wm8739.c | 4 drivers/media/video/wm8775.c | 4 drivers/media/video/zr364xx.c | 929 ++++ drivers/message/fusion/mptlan.c | 36 drivers/misc/Kconfig | 66 drivers/misc/Makefile | 1 drivers/misc/asus-laptop.c | 151 - drivers/misc/sony-laptop.c | 2131 ++++++++- drivers/misc/thinkpad_acpi.c | 4312 +++++++++++++++++++ drivers/misc/thinkpad_acpi.h | 572 +++ drivers/mmc/mmc_sysfs.c | 27 drivers/mtd/Kconfig | 30 drivers/mtd/Makefile | 2 drivers/mtd/chips/Kconfig | 12 drivers/mtd/chips/cfi_cmdset_0001.c | 93 drivers/mtd/chips/fwh_lock.h | 3 drivers/mtd/devices/Kconfig | 26 drivers/mtd/devices/Makefile | 1 drivers/mtd/devices/at91_dataflash26.c | 485 ++ drivers/mtd/devices/block2mtd.c | 67 drivers/mtd/maps/Kconfig | 47 drivers/mtd/maps/Makefile | 2 drivers/mtd/maps/alchemy-flash.c | 9 drivers/mtd/maps/ck804xrom.c | 2 drivers/mtd/maps/plat-ram.c | 3 drivers/mtd/maps/pmcmsp-flash.c | 184 + drivers/mtd/maps/pmcmsp-ramroot.c | 105 drivers/mtd/maps/sun_uflash.c | 4 drivers/mtd/mtd_blkdevs.c | 43 drivers/mtd/mtdchar.c | 2 drivers/mtd/nand/Kconfig | 63 drivers/mtd/nand/Makefile | 1 drivers/mtd/nand/cafe.c | 25 drivers/mtd/nand/cmx270_nand.c | 267 + drivers/mtd/nand/nand_base.c | 8 drivers/mtd/nand/nand_ids.c | 4 drivers/mtd/nand/nandsim.c | 518 ++ drivers/mtd/onenand/Kconfig | 15 drivers/mtd/onenand/onenand_base.c | 21 drivers/mtd/ubi/Kconfig | 58 drivers/mtd/ubi/Kconfig.debug | 104 drivers/mtd/ubi/Makefile | 7 drivers/mtd/ubi/build.c | 848 ++++ drivers/mtd/ubi/cdev.c | 722 +++ drivers/mtd/ubi/debug.c | 224 + drivers/mtd/ubi/debug.h | 161 + drivers/mtd/ubi/eba.c | 1241 +++++ drivers/mtd/ubi/gluebi.c | 323 + drivers/mtd/ubi/io.c | 1259 ++++++ drivers/mtd/ubi/kapi.c | 575 +++ drivers/mtd/ubi/misc.c | 105 drivers/mtd/ubi/scan.c | 1368 ++++++ drivers/mtd/ubi/scan.h | 167 + drivers/mtd/ubi/ubi.h | 535 ++ drivers/mtd/ubi/upd.c | 348 ++ drivers/mtd/ubi/vmt.c | 809 ++++ drivers/mtd/ubi/vtbl.c | 809 ++++ drivers/mtd/ubi/wl.c | 1671 +++++++ drivers/net/3c501.c | 1 drivers/net/3c505.c | 3 drivers/net/3c507.c | 1 drivers/net/3c509.c | 2 drivers/net/3c515.c | 2 drivers/net/3c523.c | 3 drivers/net/3c527.c | 1 drivers/net/3c59x.c | 2 drivers/net/7990.c | 3 drivers/net/8139cp.c | 6 drivers/net/8139too.c | 7 drivers/net/82596.c | 1 drivers/net/Kconfig | 29 drivers/net/Makefile | 4 drivers/net/a2065.c | 3 drivers/net/acenic.c | 1 drivers/net/amd8111e.c | 4 drivers/net/appletalk/cops.c | 4 drivers/net/appletalk/ltpc.c | 15 drivers/net/arcnet/arc-rawmode.c | 2 drivers/net/arcnet/arcnet.c | 17 drivers/net/arcnet/capmode.c | 14 drivers/net/arcnet/rfc1051.c | 2 drivers/net/arcnet/rfc1201.c | 2 drivers/net/ariadne.c | 1 drivers/net/arm/am79c961a.c | 1 drivers/net/arm/at91_ether.c | 1 drivers/net/arm/ep93xx_eth.c | 1 drivers/net/arm/ether1.c | 1 drivers/net/arm/ether3.c | 1 drivers/net/at1700.c | 1 drivers/net/atari_bionet.c | 7 drivers/net/atari_pamsnet.c | 6 drivers/net/atarilance.c | 1 drivers/net/atl1/atl1_main.c | 33 drivers/net/atp.c | 1 drivers/net/au1000_eth.c | 3 drivers/net/b44.c | 8 drivers/net/bmac.c | 1 drivers/net/bnx2.c | 37 drivers/net/bonding/bond_3ad.c | 8 drivers/net/bonding/bond_alb.c | 36 drivers/net/bonding/bond_main.c | 66 drivers/net/cassini.c | 11 drivers/net/chelsio/Makefile | 4 drivers/net/chelsio/common.h | 6 drivers/net/chelsio/cphy.h | 16 drivers/net/chelsio/gmac.h | 10 drivers/net/chelsio/ixf1010.c | 505 -- drivers/net/chelsio/mac.c | 2 drivers/net/chelsio/mv88e1xxx.c | 8 drivers/net/chelsio/mv88x201x.c | 8 drivers/net/chelsio/my3126.c | 8 drivers/net/chelsio/pm3393.c | 8 drivers/net/chelsio/sge.c | 39 drivers/net/chelsio/subr.c | 199 + drivers/net/chelsio/vsc7326.c | 2 drivers/net/chelsio/vsc8244.c | 367 -- drivers/net/chelsio/vsc8244_reg.h | 172 - drivers/net/cris/eth_v10.c | 4 drivers/net/cs89x0.c | 2 drivers/net/cxgb3/cxgb3_offload.c | 2 drivers/net/cxgb3/sge.c | 39 drivers/net/de600.c | 1 drivers/net/de620.c | 1 drivers/net/declance.c | 1 drivers/net/defxx.c | 6 drivers/net/depca.c | 1 drivers/net/dgrs.c | 3 drivers/net/dl2k.c | 4 drivers/net/dm9000.c | 1 drivers/net/e100.c | 161 - drivers/net/e1000/e1000.h | 3 drivers/net/e1000/e1000_ethtool.c | 34 drivers/net/e1000/e1000_main.c | 208 - drivers/net/e1000/e1000_param.c | 4 drivers/net/eepro.c | 1 drivers/net/eepro100.c | 6 drivers/net/eexpress.c | 10 drivers/net/ehea/ehea.h | 42 drivers/net/ehea/ehea_ethtool.c | 115 - drivers/net/ehea/ehea_main.c | 979 +++- drivers/net/ehea/ehea_phyp.c | 6 drivers/net/ehea/ehea_phyp.h | 6 drivers/net/ehea/ehea_qmr.c | 184 + drivers/net/ehea/ehea_qmr.h | 16 drivers/net/epic100.c | 3 drivers/net/eth16i.c | 1 drivers/net/ewrk3.c | 1 drivers/net/fealnx.c | 1 drivers/net/fec.c | 1 drivers/net/fec_8xx/fec_main.c | 5 drivers/net/forcedeth.c | 30 drivers/net/fs_enet/fs_enet-main.c | 9 drivers/net/gianfar.c | 12 drivers/net/hamachi.c | 1 drivers/net/hamradio/baycom_ser_fdx.c | 13 drivers/net/hamradio/bpqether.c | 2 drivers/net/hamradio/dmascc.c | 2 drivers/net/hamradio/hdlcdrv.c | 4 drivers/net/hamradio/yam.c | 4 drivers/net/hp100.c | 1 drivers/net/ibm_emac/ibm_emac_core.c | 3 drivers/net/ibmlana.c | 1 drivers/net/ibmveth.c | 11 drivers/net/ioc3-eth.c | 13 drivers/net/irda/ali-ircc.c | 9 drivers/net/irda/au1k_ir.c | 6 drivers/net/irda/donauboe.c | 8 drivers/net/irda/irda-usb.c | 6 drivers/net/irda/mcs7780.c | 38 drivers/net/irda/nsc-ircc.c | 15 drivers/net/irda/pxaficp_ir.c | 6 drivers/net/irda/sa1100_ir.c | 2 drivers/net/irda/smsc-ircc2.c | 5 drivers/net/irda/stir4200.c | 5 drivers/net/irda/via-ircc.c | 18 drivers/net/irda/vlsi_ir.c | 4 drivers/net/irda/w83977af_ir.c | 12 drivers/net/iseries_veth.c | 1 drivers/net/ixgb/ixgb.h | 3 drivers/net/ixgb/ixgb_ethtool.c | 4 drivers/net/ixgb/ixgb_main.c | 40 drivers/net/ixgb/ixgb_param.c | 8 drivers/net/ixp2000/ixpdev.c | 3 drivers/net/lance.c | 3 drivers/net/lasi_82596.c | 1 drivers/net/lib8390.c | 1 drivers/net/loopback.c | 24 drivers/net/lp486e.c | 1 drivers/net/mac89x0.c | 1 drivers/net/macb.c | 11 drivers/net/mace.c | 1 drivers/net/macmace.c | 4 drivers/net/meth.c | 11 drivers/net/mii.c | 57 drivers/net/mipsnet.c | 54 drivers/net/mv643xx_eth.c | 68 drivers/net/mv643xx_eth.h | 4 drivers/net/myri10ge/myri10ge.c | 7 drivers/net/myri_sbus.c | 4 drivers/net/natsemi.c | 1 drivers/net/netx-eth.c | 1 drivers/net/netxen/netxen_nic.h | 189 - drivers/net/netxen/netxen_nic_ethtool.c | 212 + drivers/net/netxen/netxen_nic_hdr.h | 12 drivers/net/netxen/netxen_nic_hw.c | 416 +- drivers/net/netxen/netxen_nic_hw.h | 85 drivers/net/netxen/netxen_nic_init.c | 131 - drivers/net/netxen/netxen_nic_isr.c | 101 drivers/net/netxen/netxen_nic_main.c | 781 ++- drivers/net/netxen/netxen_nic_niu.c | 168 - drivers/net/netxen/netxen_nic_phan_reg.h | 134 - drivers/net/ni5010.c | 1 drivers/net/ni52.c | 3 drivers/net/ni65.c | 7 drivers/net/ns83820.c | 5 drivers/net/pasemi_mac.c | 14 drivers/net/pci-skeleton.c | 3 drivers/net/pcmcia/3c574_cs.c | 1 drivers/net/pcmcia/3c589_cs.c | 1 drivers/net/pcmcia/axnet_cs.c | 3 drivers/net/pcmcia/fmvj18x_cs.c | 1 drivers/net/pcmcia/nmclan_cs.c | 4 drivers/net/pcmcia/smc91c92_cs.c | 1 drivers/net/pcmcia/xirc2ps_cs.c | 1 drivers/net/pcnet32.c | 160 - drivers/net/phy/fixed.c | 6 drivers/net/phy/mdio_bus.c | 19 drivers/net/phy/phy.c | 194 + drivers/net/phy/phy_device.c | 123 - drivers/net/plip.c | 2 drivers/net/ppp_generic.c | 6 drivers/net/ppp_synctty.c | 3 drivers/net/pppoe.c | 156 - drivers/net/pppox.c | 10 drivers/net/qla3xxx.c | 376 +- drivers/net/qla3xxx.h | 33 drivers/net/r8169.c | 3 drivers/net/rionet.c | 1 drivers/net/rrunner.c | 3 drivers/net/s2io-regs.h | 2 drivers/net/s2io.c | 82 drivers/net/s2io.h | 8 drivers/net/saa9730.c | 1 drivers/net/sb1000.c | 2 drivers/net/sb1250-mac.c | 297 + drivers/net/sc92031.c | 1 drivers/net/seeq8005.c | 1 drivers/net/sgiseeq.c | 31 drivers/net/sis190.c | 1 drivers/net/sis900.c | 12 drivers/net/sk98lin/skge.c | 32 drivers/net/skfp/h/lnkstat.h | 84 drivers/net/skfp/skfddi.c | 3 drivers/net/skge.c | 36 drivers/net/skge.h | 10 drivers/net/sky2.c | 11 drivers/net/slip.c | 2 drivers/net/smc911x.c | 4 drivers/net/smc9194.c | 1 drivers/net/smc91x.c | 1 drivers/net/sonic.c | 2 drivers/net/spider_net.c | 3 drivers/net/starfire.c | 1 drivers/net/sun3_82586.c | 3 drivers/net/sun3lance.c | 5 drivers/net/sunbmac.c | 1 drivers/net/sundance.c | 1 drivers/net/sungem.c | 46 drivers/net/sungem.h | 2 drivers/net/sunhme.c | 33 drivers/net/sunlance.c | 4 drivers/net/sunqe.c | 3 drivers/net/tc35815.c | 2555 ++++++++--- drivers/net/tg3.c | 82 drivers/net/tlan.c | 4 drivers/net/tokenring/3c359.c | 11 drivers/net/tokenring/ibmtr.c | 1 drivers/net/tokenring/lanstreamer.c | 7 drivers/net/tokenring/olympic.c | 18 drivers/net/tokenring/smctr.c | 6 drivers/net/tokenring/tms380tr.c | 6 drivers/net/tsi108_eth.c | 1 drivers/net/tulip/de2104x.c | 7 drivers/net/tulip/de4x5.c | 12 drivers/net/tulip/dmfe.c | 130 + drivers/net/tulip/interrupt.c | 6 drivers/net/tulip/media.c | 40 drivers/net/tulip/tulip.h | 9 drivers/net/tulip/tulip_core.c | 36 drivers/net/tulip/uli526x.c | 22 drivers/net/tulip/winbond-840.c | 7 drivers/net/tulip/xircom_cb.c | 7 drivers/net/tulip/xircom_tulip_cb.c | 7 drivers/net/tun.c | 46 drivers/net/typhoon.c | 1 drivers/net/ucc_geth.c | 946 ++-- drivers/net/ucc_geth.h | 114 - drivers/net/ucc_geth_mii.c | 279 + drivers/net/ucc_geth_mii.h | 100 drivers/net/ucc_geth_phy.c | 785 --- drivers/net/ucc_geth_phy.h | 217 - drivers/net/via-rhine.c | 1 drivers/net/via-velocity.c | 12 drivers/net/wan/cosa.c | 2 drivers/net/wan/cycx_x25.c | 2 drivers/net/wan/dlci.c | 2 drivers/net/wan/dscc4.c | 3 drivers/net/wan/farsync.c | 2 drivers/net/wan/hdlc_cisco.c | 31 drivers/net/wan/hdlc_fr.c | 23 drivers/net/wan/hostess_sv11.c | 2 drivers/net/wan/lmc/lmc_main.c | 16 drivers/net/wan/pc300_drv.c | 6 drivers/net/wan/pc300_tty.c | 6 drivers/net/wan/sbni.c | 5 drivers/net/wan/sealevel.c | 2 drivers/net/wan/syncppp.c | 2 drivers/net/wan/z85230.c | 4 drivers/net/wireless/Kconfig | 133 - drivers/net/wireless/Makefile | 1 drivers/net/wireless/README | 25 drivers/net/wireless/airo.c | 81 drivers/net/wireless/arlan-main.c | 1 drivers/net/wireless/atmel.c | 6 drivers/net/wireless/bcm43xx/Kconfig | 3 drivers/net/wireless/bcm43xx/bcm43xx.h | 3 drivers/net/wireless/bcm43xx/bcm43xx_dma.c | 3 drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c | 4 drivers/net/wireless/bcm43xx/bcm43xx_main.c | 4 drivers/net/wireless/bcm43xx/bcm43xx_phy.c | 23 drivers/net/wireless/bcm43xx/bcm43xx_phy.h | 4 drivers/net/wireless/bcm43xx/bcm43xx_radio.c | 196 + drivers/net/wireless/hostap/Kconfig | 3 drivers/net/wireless/hostap/hostap_80211_rx.c | 23 drivers/net/wireless/hostap/hostap_80211_tx.c | 25 drivers/net/wireless/hostap/hostap_ap.c | 11 drivers/net/wireless/hostap/hostap_common.h | 4 drivers/net/wireless/hostap/hostap_cs.c | 9 drivers/net/wireless/hostap/hostap_hw.c | 11 drivers/net/wireless/hostap/hostap_main.c | 21 drivers/net/wireless/hostap/hostap_pci.c | 2 drivers/net/wireless/hostap/hostap_plx.c | 2 drivers/net/wireless/ipw2100.c | 9 drivers/net/wireless/ipw2200.c | 51 drivers/net/wireless/libertas/11d.c | 754 +++ drivers/net/wireless/libertas/11d.h | 105 drivers/net/wireless/libertas/LICENSE | 16 drivers/net/wireless/libertas/Makefile | 21 drivers/net/wireless/libertas/README | 1044 +++++ drivers/net/wireless/libertas/assoc.c | 588 +++ drivers/net/wireless/libertas/assoc.h | 30 drivers/net/wireless/libertas/cmd.c | 1958 +++++++++ drivers/net/wireless/libertas/cmdresp.c | 1031 +++++ drivers/net/wireless/libertas/debugfs.c | 1935 +++++++++ drivers/net/wireless/libertas/debugfs.h | 6 drivers/net/wireless/libertas/decl.h | 83 drivers/net/wireless/libertas/defs.h | 369 ++ drivers/net/wireless/libertas/dev.h | 403 ++ drivers/net/wireless/libertas/ethtool.c | 184 + drivers/net/wireless/libertas/fw.c | 361 ++ drivers/net/wireless/libertas/fw.h | 13 drivers/net/wireless/libertas/host.h | 338 + drivers/net/wireless/libertas/hostcmd.h | 693 +++ drivers/net/wireless/libertas/if_bootcmd.c | 38 drivers/net/wireless/libertas/if_usb.c | 952 ++++ drivers/net/wireless/libertas/if_usb.h | 109 drivers/net/wireless/libertas/ioctl.c | 2500 +++++++++++ drivers/net/wireless/libertas/join.c | 1055 +++++ drivers/net/wireless/libertas/join.h | 64 drivers/net/wireless/libertas/main.c | 1258 ++++++ drivers/net/wireless/libertas/radiotap.h | 57 drivers/net/wireless/libertas/rx.c | 459 ++ drivers/net/wireless/libertas/sbi.h | 40 drivers/net/wireless/libertas/scan.c | 2044 +++++++++ drivers/net/wireless/libertas/scan.h | 216 + drivers/net/wireless/libertas/thread.h | 52 drivers/net/wireless/libertas/tx.c | 285 + drivers/net/wireless/libertas/types.h | 289 + drivers/net/wireless/libertas/version.h | 8 drivers/net/wireless/libertas/wext.c | 2769 ++++++++++++ drivers/net/wireless/libertas/wext.h | 147 + drivers/net/wireless/netwave_cs.c | 1 drivers/net/wireless/orinoco.c | 5 drivers/net/wireless/prism54/islpci_eth.c | 23 drivers/net/wireless/ray_cs.c | 4 drivers/net/wireless/strip.c | 2 drivers/net/wireless/todo.txt | 15 drivers/net/wireless/wavelan.c | 9 drivers/net/wireless/wavelan_cs.c | 6 drivers/net/wireless/zd1201.c | 6 drivers/net/wireless/zd1211rw/Kconfig | 3 drivers/net/wireless/zd1211rw/zd_chip.c | 47 drivers/net/wireless/zd1211rw/zd_chip.h | 1 drivers/net/wireless/zd1211rw/zd_mac.c | 23 drivers/net/wireless/zd1211rw/zd_rf.c | 18 drivers/net/wireless/zd1211rw/zd_rf.h | 11 drivers/net/wireless/zd1211rw/zd_rf_al2230.c | 91 drivers/net/wireless/zd1211rw/zd_rf_al7230b.c | 317 + drivers/net/wireless/zd1211rw/zd_rf_rf2959.c | 4 drivers/net/wireless/zd1211rw/zd_usb.c | 24 drivers/net/yellowfin.c | 1 drivers/net/znet.c | 1 drivers/parisc/led.c | 2 drivers/pci/pci-acpi.c | 4 drivers/pci/pci-driver.c | 6 drivers/pci/pci.c | 58 drivers/pci/quirks.c | 113 drivers/pnp/card.c | 6 drivers/ps3/vuart.c | 8 drivers/s390/block/dasd.c | 3 drivers/s390/block/dasd_devmap.c | 58 drivers/s390/char/Makefile | 5 drivers/s390/char/con3215.c | 7 drivers/s390/char/con3270.c | 7 drivers/s390/char/sclp.c | 10 drivers/s390/char/sclp.h | 72 drivers/s390/char/sclp_chp.c | 196 + drivers/s390/char/sclp_config.c | 75 drivers/s390/char/sclp_cpi.c | 4 drivers/s390/char/sclp_quiesce.c | 2 drivers/s390/char/sclp_rw.c | 16 drivers/s390/char/sclp_sdias.c | 255 + drivers/s390/char/sclp_tty.c | 6 drivers/s390/char/sclp_vt220.c | 8 drivers/s390/char/vmlogrdr.c | 9 drivers/s390/char/zcore.c | 651 +++ drivers/s390/cio/Makefile | 2 drivers/s390/cio/ccwgroup.c | 33 drivers/s390/cio/chp.c | 683 +++ drivers/s390/cio/chp.h | 53 drivers/s390/cio/chsc.c | 1024 +---- drivers/s390/cio/chsc.h | 42 drivers/s390/cio/cio.c | 52 drivers/s390/cio/cio.h | 17 drivers/s390/cio/cmf.c | 2 drivers/s390/cio/css.c | 201 - drivers/s390/cio/css.h | 16 drivers/s390/cio/device.c | 252 + drivers/s390/cio/device_fsm.c | 8 drivers/s390/cio/device_ops.c | 7 drivers/s390/cio/idset.c | 112 drivers/s390/cio/idset.h | 25 drivers/s390/cio/ioasm.h | 5 drivers/s390/crypto/ap_bus.c | 28 drivers/s390/net/claw.c | 2 drivers/s390/net/ctcmain.c | 51 drivers/s390/net/lcs.c | 3 drivers/s390/net/netiucv.c | 21 drivers/s390/net/qeth_eddp.c | 30 drivers/s390/net/qeth_main.c | 45 drivers/s390/net/qeth_proc.c | 2 drivers/s390/net/qeth_tso.h | 14 drivers/s390/s390mach.c | 25 drivers/s390/sysinfo.c | 18 drivers/sbus/char/envctrl.c | 8 drivers/sbus/char/flash.c | 2 drivers/sbus/char/openprom.c | 22 drivers/sbus/sbus.c | 4 drivers/scsi/Kconfig | 6 drivers/scsi/Makefile | 3 drivers/scsi/esp.c | 4394 ------------------- drivers/scsi/esp.h | 406 -- drivers/scsi/esp_scsi.c | 2711 ++++++++++++ drivers/scsi/esp_scsi.h | 560 ++ drivers/scsi/hosts.c | 4 drivers/scsi/ibmvscsi/ibmvstgt.c | 7 drivers/scsi/ibmvscsi/rpa_vscsi.c | 3 drivers/scsi/ipr.c | 3 drivers/scsi/qlogicpti.c | 2 drivers/scsi/scsi_lib.c | 2 drivers/scsi/scsi_netlink.c | 5 drivers/scsi/scsi_transport_iscsi.c | 4 drivers/scsi/sun_esp.c | 634 +++ drivers/serial/pmac_zilog.c | 4 drivers/serial/sunsu.c | 4 drivers/usb/Makefile | 4 drivers/usb/atm/cxacru.c | 411 ++ drivers/usb/atm/usbatm.c | 27 drivers/usb/class/cdc-acm.c | 81 drivers/usb/class/cdc-acm.h | 3 drivers/usb/core/Kconfig | 25 drivers/usb/core/devices.c | 2 drivers/usb/core/devio.c | 109 drivers/usb/core/driver.c | 267 + drivers/usb/core/hcd.c | 34 drivers/usb/core/hcd.h | 3 drivers/usb/core/hub.c | 29 drivers/usb/core/inode.c | 2 drivers/usb/core/message.c | 83 drivers/usb/core/quirks.c | 2 drivers/usb/core/sysfs.c | 102 drivers/usb/core/usb.c | 46 drivers/usb/core/usb.h | 26 drivers/usb/gadget/Kconfig | 22 drivers/usb/gadget/Makefile | 1 drivers/usb/gadget/ether.c | 7 drivers/usb/gadget/fsl_usb2_udc.c | 2500 +++++++++++ drivers/usb/gadget/fsl_usb2_udc.h | 579 +++ drivers/usb/gadget/gadget_chips.h | 8 drivers/usb/gadget/pxa2xx_udc.c | 98 drivers/usb/gadget/rndis.h | 2 drivers/usb/host/Makefile | 1 drivers/usb/host/ehci-fsl.h | 4 drivers/usb/host/ehci-hub.c | 4 drivers/usb/host/hc_crisv10.c | 4550 -------------------- drivers/usb/host/hc_crisv10.h | 289 - drivers/usb/host/ohci-hcd.c | 6 drivers/usb/host/ohci-pci.c | 32 drivers/usb/host/uhci-q.c | 16 drivers/usb/input/Kconfig | 145 - drivers/usb/input/Makefile | 28 drivers/usb/input/ati_remote2.c | 89 drivers/usb/input/gtco.c | 5 drivers/usb/input/hid-core.c | 1477 ------ drivers/usb/input/hid-ff.c | 89 drivers/usb/input/hid-lgff.c | 150 - drivers/usb/input/hid-pidff.c | 1331 ------ drivers/usb/input/hid-plff.c | 129 - drivers/usb/input/hid-tmff.c | 147 - drivers/usb/input/hid-zpff.c | 111 drivers/usb/input/hiddev.c | 847 ---- drivers/usb/input/usbhid.h | 87 drivers/usb/input/usbkbd.c | 362 -- drivers/usb/input/usbmouse.c | 245 - drivers/usb/misc/adutux.c | 48 drivers/usb/misc/cypress_cy7c63.c | 4 drivers/usb/misc/ftdi-elan.c | 19 drivers/usb/misc/iowarrior.c | 20 drivers/usb/misc/ldusb.c | 3 drivers/usb/misc/usblcd.c | 7 drivers/usb/mon/mon_bin.c | 14 drivers/usb/mon/mon_main.c | 158 - drivers/usb/mon/mon_text.c | 315 + drivers/usb/mon/usb_mon.h | 6 drivers/usb/net/asix.c | 8 drivers/usb/net/catc.c | 30 drivers/usb/net/dm9601.c | 5 drivers/usb/net/gl620a.c | 2 drivers/usb/net/kaweth.c | 2 drivers/usb/net/net1080.c | 2 drivers/usb/net/pegasus.c | 17 drivers/usb/net/pegasus.h | 3 drivers/usb/net/rndis_host.c | 114 - drivers/usb/net/rtl8150.c | 1 drivers/usb/net/usbnet.c | 9 drivers/usb/net/usbnet.h | 1 drivers/usb/serial/Kconfig | 6 drivers/usb/serial/aircable.c | 7 drivers/usb/serial/ark3116.c | 3 drivers/usb/serial/cp2101.c | 2 drivers/usb/serial/ftdi_sio.c | 23 drivers/usb/serial/ftdi_sio.h | 1 drivers/usb/serial/io_edgeport.c | 139 - drivers/usb/serial/io_edgeport.h | 6 drivers/usb/serial/ipaq.c | 1 drivers/usb/serial/kl5kusb105.c | 28 drivers/usb/serial/mct_u232.c | 12 drivers/usb/serial/mos7720.c | 34 drivers/usb/serial/mos7840.c | 233 + drivers/usb/serial/omninet.c | 40 drivers/usb/serial/option.c | 23 drivers/usb/serial/sierra.c | 25 drivers/usb/serial/visor.c | 22 drivers/usb/serial/whiteheat.c | 8 drivers/usb/serial/whiteheat.h | 4 drivers/usb/storage/libusual.c | 3 drivers/usb/storage/unusual_devs.h | 9 drivers/usb/usb-skeleton.c | 41 drivers/video/Kconfig | 2 drivers/video/aty/atyfb_base.c | 6 drivers/video/aty/radeon_base.c | 12 drivers/video/aty/radeon_monitor.c | 16 drivers/video/aty/radeon_pm.c | 2 drivers/video/aty/radeonfb.h | 4 drivers/video/cg3.c | 2 drivers/video/controlfb.c | 16 drivers/video/igafb.c | 24 fs/Kconfig | 14 fs/afs/Makefile | 7 fs/afs/afs.h | 146 + fs/afs/afs_cm.h | 32 fs/afs/afs_fs.h | 48 fs/afs/afs_vl.h | 84 fs/afs/cache.c | 256 + fs/afs/cache.h | 12 fs/afs/callback.c | 509 ++ fs/afs/cell.c | 471 +- fs/afs/cell.h | 78 fs/afs/cmservice.c | 926 ++-- fs/afs/cmservice.h | 29 fs/afs/dir.c | 852 +++- fs/afs/errors.h | 34 fs/afs/file.c | 124 - fs/afs/fsclient.c | 1528 ++++--- fs/afs/fsclient.h | 54 fs/afs/inode.c | 248 + fs/afs/internal.h | 755 +++ fs/afs/kafsasyncd.c | 255 - fs/afs/kafsasyncd.h | 52 fs/afs/kafstimod.c | 205 - fs/afs/kafstimod.h | 49 fs/afs/main.c | 262 - fs/afs/misc.c | 38 fs/afs/mntpt.c | 141 - fs/afs/mount.h | 23 fs/afs/proc.c | 230 - fs/afs/rxrpc.c | 782 +++ fs/afs/security.c | 356 ++ fs/afs/server.c | 647 +-- fs/afs/server.h | 102 fs/afs/super.c | 326 + fs/afs/super.h | 45 fs/afs/transport.h | 21 fs/afs/types.h | 125 - fs/afs/use-rtnetlink.c | 473 ++ fs/afs/vlclient.c | 737 +-- fs/afs/vlclient.h | 93 fs/afs/vlocation.c | 1224 ++--- fs/afs/vnode.c | 731 ++- fs/afs/vnode.h | 94 fs/afs/volume.c | 290 - fs/afs/volume.h | 140 - fs/bio.c | 41 fs/compat_ioctl.c | 18 fs/debugfs/file.c | 42 fs/ecryptfs/netlink.c | 6 fs/jffs2/LICENCE | 7 fs/jffs2/Makefile | 1 fs/jffs2/README.Locking | 1 fs/jffs2/TODO | 3 fs/jffs2/acl.c | 3 fs/jffs2/acl.h | 3 fs/jffs2/background.c | 4 fs/jffs2/build.c | 4 fs/jffs2/compr.c | 144 - fs/jffs2/compr.h | 17 fs/jffs2/compr_rtime.c | 3 fs/jffs2/compr_rubin.c | 81 fs/jffs2/compr_rubin.h | 21 fs/jffs2/compr_zlib.c | 4 fs/jffs2/comprtest.c | 307 - fs/jffs2/debug.c | 5 fs/jffs2/debug.h | 5 fs/jffs2/dir.c | 4 fs/jffs2/erase.c | 6 fs/jffs2/file.c | 4 fs/jffs2/fs.c | 16 fs/jffs2/gc.c | 7 fs/jffs2/ioctl.c | 4 fs/jffs2/jffs2_fs_i.h | 11 fs/jffs2/jffs2_fs_sb.h | 11 fs/jffs2/malloc.c | 4 fs/jffs2/nodelist.c | 482 -- fs/jffs2/nodelist.h | 40 fs/jffs2/nodemgmt.c | 9 fs/jffs2/os-linux.h | 10 fs/jffs2/pushpull.h | 72 fs/jffs2/read.c | 4 fs/jffs2/readinode.c | 851 +++- fs/jffs2/scan.c | 62 fs/jffs2/security.c | 3 fs/jffs2/summary.c | 12 fs/jffs2/summary.h | 10 fs/jffs2/super.c | 6 fs/jffs2/symlink.c | 5 fs/jffs2/wbuf.c | 39 fs/jffs2/write.c | 7 fs/jffs2/writev.c | 4 fs/jffs2/xattr.c | 3 fs/jffs2/xattr.h | 3 fs/jffs2/xattr_trusted.c | 3 fs/jffs2/xattr_user.c | 3 fs/namei.c | 72 fs/ocfs2/alloc.c | 3037 +++++++++++-- fs/ocfs2/alloc.h | 27 fs/ocfs2/aops.c | 1011 ++++ fs/ocfs2/aops.h | 77 fs/ocfs2/cluster/quorum.c | 5 fs/ocfs2/cluster/tcp_internal.h | 5 fs/ocfs2/dir.c | 15 fs/ocfs2/dlm/dlmdomain.c | 5 fs/ocfs2/dlm/dlmrecovery.c | 2 fs/ocfs2/dlmglue.c | 143 + fs/ocfs2/dlmglue.h | 3 fs/ocfs2/extent_map.c | 1233 ++--- fs/ocfs2/extent_map.h | 39 fs/ocfs2/file.c | 637 ++- fs/ocfs2/file.h | 5 fs/ocfs2/inode.c | 199 - fs/ocfs2/inode.h | 23 fs/ocfs2/journal.c | 24 fs/ocfs2/journal.h | 2 fs/ocfs2/mmap.c | 7 fs/ocfs2/namei.c | 23 fs/ocfs2/ocfs2.h | 55 fs/ocfs2/ocfs2_fs.h | 31 fs/ocfs2/ocfs2_lockid.h | 5 fs/ocfs2/slot_map.c | 2 fs/ocfs2/suballoc.c | 3 fs/ocfs2/super.c | 7 fs/ocfs2/vote.c | 289 - fs/ocfs2/vote.h | 3 fs/proc/proc_devtree.c | 2 fs/reiserfs/xattr.c | 2 fs/super.c | 12 fs/sync.c | 8 fs/sysfs/bin.c | 2 fs/sysfs/file.c | 14 fs/sysfs/group.c | 6 include/acpi/actbl.h | 1 include/asm-alpha/socket.h | 2 include/asm-alpha/sockios.h | 3 include/asm-alpha/string.h | 2 include/asm-arm/div64.h | 3 include/asm-arm/socket.h | 2 include/asm-arm/sockios.h | 3 include/asm-arm26/socket.h | 2 include/asm-arm26/sockios.h | 3 include/asm-avr32/arch-at32ap/io.h | 39 include/asm-avr32/arch-at32ap/smc.h | 22 include/asm-avr32/arch-at32ap/time.h | 112 include/asm-avr32/atomic.h | 2 include/asm-avr32/bug.h | 50 include/asm-avr32/io.h | 326 + include/asm-avr32/processor.h | 15 include/asm-avr32/setup.h | 13 include/asm-avr32/socket.h | 2 include/asm-avr32/sockios.h | 3 include/asm-avr32/sysreg.h | 543 +- include/asm-avr32/system.h | 13 include/asm-avr32/thread_info.h | 2 include/asm-avr32/uaccess.h | 13 include/asm-cris/socket.h | 2 include/asm-cris/sockios.h | 3 include/asm-frv/socket.h | 2 include/asm-frv/sockios.h | 3 include/asm-generic/div64.h | 7 include/asm-generic/pgtable.h | 11 include/asm-h8300/socket.h | 2 include/asm-h8300/sockios.h | 3 include/asm-i386/div64.h | 4 include/asm-i386/socket.h | 2 include/asm-i386/sockios.h | 3 include/asm-ia64/socket.h | 2 include/asm-ia64/sockios.h | 3 include/asm-m32r/socket.h | 2 include/asm-m32r/sockios.h | 3 include/asm-m68k/div64.h | 3 include/asm-m68k/socket.h | 2 include/asm-m68k/sockios.h | 3 include/asm-mips/cacheflush.h | 2 include/asm-mips/div64.h | 11 include/asm-mips/jmr3927/irq.h | 57 include/asm-mips/jmr3927/jmr3927.h | 130 - include/asm-mips/jmr3927/tx3927.h | 8 include/asm-mips/jmr3927/txx927.h | 5 include/asm-mips/paccess.h | 2 include/asm-mips/sgi/hpc3.h | 2 include/asm-mips/sgi/ip22.h | 2 include/asm-mips/sgi/mc.h | 2 include/asm-mips/sibyte/bcm1480_int.h | 2 include/asm-mips/sibyte/bcm1480_mc.h | 32 include/asm-mips/sibyte/bcm1480_regs.h | 20 include/asm-mips/sibyte/bcm1480_scd.h | 94 include/asm-mips/sibyte/board.h | 14 include/asm-mips/sibyte/carmel.h | 1 include/asm-mips/sibyte/sb1250_int.h | 5 include/asm-mips/sibyte/sb1250_mac.h | 24 include/asm-mips/sibyte/sb1250_mc.h | 2 include/asm-mips/sibyte/sb1250_regs.h | 46 include/asm-mips/sibyte/sb1250_scd.h | 30 include/asm-mips/sibyte/swarm.h | 12 include/asm-mips/socket.h | 2 include/asm-mips/sockios.h | 3 include/asm-parisc/socket.h | 2 include/asm-parisc/sockios.h | 3 include/asm-powerpc/asm-compat.h | 10 include/asm-powerpc/cacheflush.h | 6 include/asm-powerpc/cell-pmu.h | 5 include/asm-powerpc/cputable.h | 11 include/asm-powerpc/current.h | 1 include/asm-powerpc/edac.h | 40 include/asm-powerpc/eeh_event.h | 6 include/asm-powerpc/ibmebus.h | 44 include/asm-powerpc/immap_86xx.h | 75 include/asm-powerpc/io.h | 7 include/asm-powerpc/kprobes.h | 5 include/asm-powerpc/machdep.h | 3 include/asm-powerpc/mmu-hash64.h | 400 ++ include/asm-powerpc/mmu.h | 405 -- include/asm-powerpc/mpic.h | 12 include/asm-powerpc/of_device.h | 3 include/asm-powerpc/oprofile_impl.h | 2 include/asm-powerpc/paca.h | 4 include/asm-powerpc/parport.h | 6 include/asm-powerpc/pci.h | 14 include/asm-powerpc/pgtable-4k.h | 3 include/asm-powerpc/pgtable-64k.h | 5 include/asm-powerpc/pgtable.h | 60 include/asm-powerpc/pmc.h | 1 include/asm-powerpc/ppc-pci.h | 12 include/asm-powerpc/processor.h | 1 include/asm-powerpc/prom.h | 34 include/asm-powerpc/reg.h | 68 include/asm-powerpc/socket.h | 2 include/asm-powerpc/sockios.h | 3 include/asm-powerpc/spu_csa.h | 1 include/asm-powerpc/string.h | 2 include/asm-powerpc/system.h | 1 include/asm-powerpc/tlb.h | 1 include/asm-powerpc/tlbflush.h | 172 - include/asm-powerpc/uaccess.h | 28 include/asm-powerpc/ucc_fast.h | 3 include/asm-powerpc/uic.h | 23 include/asm-ppc/ibm4xx.h | 8 include/asm-ppc/ppc_sys.h | 2 include/asm-ppc/prom.h | 3 include/asm-s390/bug.h | 69 include/asm-s390/ccwgroup.h | 1 include/asm-s390/chpid.h | 53 include/asm-s390/cio.h | 8 include/asm-s390/ipl.h | 35 include/asm-s390/lowcore.h | 46 include/asm-s390/pgtable.h | 15 include/asm-s390/processor.h | 2 include/asm-s390/sclp.h | 14 include/asm-s390/setup.h | 2 include/asm-s390/smp.h | 6 include/asm-s390/socket.h | 2 include/asm-s390/sockios.h | 3 include/asm-sh/socket.h | 2 include/asm-sh/sockios.h | 3 include/asm-sh/string.h | 3 include/asm-sh64/sockios.h | 3 include/asm-sparc/prom.h | 14 include/asm-sparc/socket.h | 2 include/asm-sparc/sockios.h | 3 include/asm-sparc64/cpudata.h | 4 include/asm-sparc64/device.h | 18 include/asm-sparc64/ebus.h | 2 include/asm-sparc64/floppy.h | 4 include/asm-sparc64/io.h | 8 include/asm-sparc64/iommu.h | 51 include/asm-sparc64/isa.h | 2 include/asm-sparc64/parport.h | 2 include/asm-sparc64/pbm.h | 132 - include/asm-sparc64/pci.h | 2 include/asm-sparc64/pgtable.h | 16 include/asm-sparc64/prom.h | 12 include/asm-sparc64/smp.h | 4 include/asm-sparc64/socket.h | 2 include/asm-sparc64/sockios.h | 3 include/asm-sparc64/sparsemem.h | 2 include/asm-sparc64/timer.h | 17 include/asm-sparc64/ttable.h | 27 include/asm-um/div64.h | 1 include/asm-v850/socket.h | 2 include/asm-v850/sockios.h | 3 include/asm-x86_64/socket.h | 2 include/asm-x86_64/sockios.h | 3 include/asm-xtensa/div64.h | 6 include/asm-xtensa/socket.h | 2 include/asm-xtensa/sockios.h | 3 include/keys/rxrpc-type.h | 22 include/linux/Kbuild | 7 include/linux/ata.h | 10 include/linux/atalk.h | 4 include/linux/bio.h | 2 include/linux/blkdev.h | 1 include/linux/dccp.h | 46 include/linux/debugfs.h | 9 include/linux/device.h | 69 include/linux/dvb/audio.h | 5 include/linux/dvb/version.h | 2 include/linux/dvb/video.h | 62 include/linux/fib_rules.h | 15 include/linux/fs.h | 9 include/linux/fsl_devices.h | 39 include/linux/hdlc.h | 7 include/linux/hid.h | 14 include/linux/icmp.h | 9 include/linux/icmpv6.h | 9 include/linux/if_addr.h | 1 include/linux/if_arp.h | 9 include/linux/if_bridge.h | 3 include/linux/if_ether.h | 3 include/linux/if_link.h | 1 include/linux/if_packet.h | 1 include/linux/if_pppox.h | 10 include/linux/if_tr.h | 2 include/linux/if_vlan.h | 6 include/linux/if_wanpipe_common.h | 58 include/linux/igmp.h | 21 include/linux/in.h | 1 include/linux/in6.h | 3 include/linux/ioport.h | 1 include/linux/ip.h | 14 include/linux/ipv6.h | 14 include/linux/jhash.h | 2 include/linux/kallsyms.h | 13 include/linux/kernel.h | 1 include/linux/key.h | 2 include/linux/kobject.h | 12 include/linux/ktime.h | 6 include/linux/libata.h | 78 include/linux/mtd/iflash.h | 98 include/linux/mtd/mtd.h | 1 include/linux/mtd/nand.h | 1 include/linux/mtd/ubi.h | 202 + include/linux/namei.h | 1 include/linux/net.h | 2 include/linux/netdevice.h | 16 include/linux/netfilter.h | 12 include/linux/netfilter/nf_conntrack_tcp.h | 5 include/linux/netfilter/nfnetlink.h | 19 include/linux/netfilter/nfnetlink_conntrack.h | 4 include/linux/netfilter_bridge.h | 11 include/linux/netfilter_bridge/ebt_802_3.h | 2 include/linux/netfilter_bridge/ebt_arp.h | 4 include/linux/netfilter_ipv4/Kbuild | 14 include/linux/netfilter_ipv4/ip_conntrack.h | 402 -- include/linux/netfilter_ipv4/ip_conntrack_amanda.h | 11 include/linux/netfilter_ipv4/ip_conntrack_core.h | 61 include/linux/netfilter_ipv4/ip_conntrack_ftp.h | 44 include/linux/netfilter_ipv4/ip_conntrack_h323.h | 89 include/linux/netfilter_ipv4/ip_conntrack_helper.h | 46 include/linux/netfilter_ipv4/ip_conntrack_icmp.h | 6 include/linux/netfilter_ipv4/ip_conntrack_irc.h | 32 include/linux/netfilter_ipv4/ip_conntrack_pptp.h | 326 - .../linux/netfilter_ipv4/ip_conntrack_proto_gre.h | 114 - .../linux/netfilter_ipv4/ip_conntrack_protocol.h | 98 include/linux/netfilter_ipv4/ip_conntrack_sctp.h | 6 include/linux/netfilter_ipv4/ip_conntrack_sip.h | 40 include/linux/netfilter_ipv4/ip_conntrack_tcp.h | 6 include/linux/netfilter_ipv4/ip_conntrack_tftp.h | 20 include/linux/netfilter_ipv4/ip_conntrack_tuple.h | 146 - include/linux/netfilter_ipv4/ip_nat.h | 79 include/linux/netfilter_ipv4/ip_nat_core.h | 18 include/linux/netfilter_ipv4/ip_nat_helper.h | 33 include/linux/netfilter_ipv4/ip_nat_pptp.h | 11 include/linux/netfilter_ipv4/ip_nat_protocol.h | 74 include/linux/netfilter_ipv4/ip_nat_rule.h | 28 include/linux/netfilter_ipv4/ipt_SAME.h | 2 include/linux/netlink.h | 33 include/linux/nl80211.h | 38 include/linux/page-flags.h | 2 include/linux/pci.h | 3 include/linux/pci_ids.h | 3 include/linux/phy.h | 1 include/linux/pm.h | 83 include/linux/pmu.h | 12 include/linux/rtnetlink.h | 13 include/linux/rxrpc.h | 62 include/linux/sched.h | 4 include/linux/sctp.h | 9 include/linux/sdla_fr.h | 638 --- include/linux/seqlock.h | 8 include/linux/skbuff.h | 397 +- include/linux/snmp.h | 2 include/linux/socket.h | 5 include/linux/sony-laptop.h | 34 include/linux/string.h | 6 include/linux/sysctl.h | 4 include/linux/sysfs.h | 4 include/linux/tcp.h | 21 include/linux/udp.h | 9 include/linux/usb.h | 22 include/linux/usb/cdc.h | 11 include/linux/usb/ch9.h | 15 include/linux/videodev2.h | 83 include/linux/wireless.h | 2 include/linux/workqueue.h | 7 include/linux/xfrm.h | 60 include/media/cx2341x.h | 6 include/media/ivtv.h | 65 include/media/tuner.h | 5 include/media/v4l2-chip-ident.h | 149 + include/media/v4l2-common.h | 47 include/media/v4l2-dev.h | 10 include/mtd/Kbuild | 2 include/mtd/mtd-abi.h | 1 include/mtd/ubi-header.h | 360 ++ include/mtd/ubi-user.h | 161 + include/net/addrconf.h | 4 include/net/af_rxrpc.h | 57 include/net/ax25.h | 2 include/net/bluetooth/hci.h | 18 include/net/cfg80211.h | 40 include/net/cipso_ipv4.h | 2 include/net/compat.h | 1 include/net/dn_fib.h | 9 include/net/dn_route.h | 1 include/net/esp.h | 2 include/net/fib_rules.h | 20 include/net/flow.h | 6 include/net/ieee80211.h | 4 include/net/ieee80211_crypt.h | 4 include/net/ieee80211_radiotap.h | 77 include/net/inet6_hashtables.h | 12 include/net/inet_ecn.h | 8 include/net/inet_sock.h | 11 include/net/ip.h | 11 include/net/ip6_fib.h | 2 include/net/ip6_route.h | 5 include/net/ip_fib.h | 6 include/net/ipv6.h | 19 include/net/ipx.h | 2 include/net/iucv/iucv.h | 2 include/net/iw_handler.h | 21 include/net/llc_pdu.h | 15 include/net/neighbour.h | 10 include/net/netfilter/nf_conntrack.h | 5 include/net/netfilter/nf_conntrack_compat.h | 145 - include/net/netfilter/nf_conntrack_core.h | 3 include/net/netfilter/nf_conntrack_ecache.h | 30 include/net/netfilter/nf_conntrack_l3proto.h | 5 include/net/netfilter/nf_conntrack_l4proto.h | 1 include/net/netfilter/nf_nat_rule.h | 10 include/net/netlink.h | 18 include/net/pkt_cls.h | 10 include/net/pkt_sched.h | 182 - include/net/red.h | 10 include/net/rtnetlink.h | 25 include/net/sch_generic.h | 12 include/net/sctp/constants.h | 2 include/net/sctp/structs.h | 5 include/net/sctp/ulpevent.h | 1 include/net/sctp/ulpqueue.h | 2 include/net/sctp/user.h | 25 include/net/sock.h | 89 include/net/tcp.h | 199 + include/net/tcp_ecn.h | 17 include/net/udp.h | 11 include/net/udplite.h | 45 include/net/wext.h | 24 include/net/wireless.h | 139 + include/net/x25device.h | 2 include/net/xfrm.h | 33 include/rxrpc/call.h | 212 - include/rxrpc/connection.h | 83 include/rxrpc/krxiod.h | 27 include/rxrpc/krxsecd.h | 22 include/rxrpc/krxtimod.h | 45 include/rxrpc/message.h | 71 include/rxrpc/packet.h | 119 - include/rxrpc/peer.h | 82 include/rxrpc/rxrpc.h | 36 include/rxrpc/transport.h | 106 kernel/audit.c | 16 kernel/hrtimer.c | 3 kernel/kallsyms.c | 23 kernel/module.c | 4 kernel/power/disk.c | 87 kernel/power/main.c | 41 kernel/resource.c | 21 kernel/sched.c | 2 kernel/taskstats.c | 4 kernel/time.c | 2 kernel/timer.c | 2 lib/Kconfig.debug | 2 lib/Makefile | 5 lib/devres.c | 26 lib/div64.c | 22 lib/kobject.c | 62 lib/kobject_uevent.c | 28 lib/kref.c | 2 lib/string.c | 28 lib/vsprintf.c | 26 mm/page-writeback.c | 4 mm/rmap.c | 8 net/802/fddi.c | 7 net/802/hippi.c | 12 net/802/psnap.c | 4 net/802/tr.c | 9 net/8021q/vlan.c | 6 net/8021q/vlan_dev.c | 14 net/Kconfig | 19 net/Makefile | 3 net/appletalk/aarp.c | 14 net/appletalk/ddp.c | 21 net/atm/br2684.c | 8 net/atm/clip.c | 4 net/atm/ioctl.c | 3 net/atm/lec.c | 15 net/atm/mpc.c | 17 net/atm/signaling.c | 2 net/ax25/af_ax25.c | 113 net/ax25/ax25_ds_subr.c | 2 net/ax25/ax25_in.c | 24 net/ax25/ax25_ip.c | 4 net/ax25/ax25_out.c | 12 net/ax25/ax25_subr.c | 4 net/bluetooth/af_bluetooth.c | 2 net/bluetooth/bnep/core.c | 16 net/bluetooth/cmtp/core.c | 4 net/bluetooth/hci_conn.c | 36 net/bluetooth/hci_core.c | 35 net/bluetooth/hci_event.c | 8 net/bluetooth/hci_sock.c | 2 net/bluetooth/l2cap.c | 76 net/bluetooth/rfcomm/core.c | 6 net/bluetooth/sco.c | 2 net/bridge/br.c | 12 net/bridge/br_device.c | 22 net/bridge/br_fdb.c | 42 net/bridge/br_forward.c | 2 net/bridge/br_if.c | 6 net/bridge/br_input.c | 51 net/bridge/br_ioctl.c | 5 net/bridge/br_netfilter.c | 142 - net/bridge/br_netlink.c | 24 net/bridge/br_notify.c | 13 net/bridge/br_private.h | 23 net/bridge/br_stp.c | 10 net/bridge/br_stp_bpdu.c | 19 net/bridge/br_stp_if.c | 59 net/bridge/br_sysfs_br.c | 20 net/bridge/br_sysfs_if.c | 8 net/bridge/netfilter/ebt_arp.c | 48 net/bridge/netfilter/ebt_log.c | 12 net/bridge/netfilter/ebt_ulog.c | 12 net/compat.c | 79 net/core/Makefile | 1 net/core/datagram.c | 10 net/core/dev.c | 363 +- net/core/dev_mcast.c | 2 net/core/ethtool.c | 4 net/core/fib_rules.c | 161 + net/core/filter.c | 6 net/core/gen_stats.c | 4 net/core/link_watch.c | 2 net/core/neighbour.c | 34 net/core/net-sysfs.c | 31 net/core/netpoll.c | 23 net/core/pktgen.c | 299 + net/core/rtnetlink.c | 298 + net/core/skbuff.c | 373 +- net/core/sock.c | 775 ++- net/core/sysctl_net_core.c | 8 net/core/utils.c | 6 net/core/wireless.c | 2371 ---------- net/dccp/ackvec.c | 2 net/dccp/ccids/ccid3.c | 322 + net/dccp/ccids/ccid3.h | 10 net/dccp/ccids/lib/loss_interval.c | 2 net/dccp/dccp.h | 75 net/dccp/input.c | 54 net/dccp/ipv4.c | 43 net/dccp/ipv6.c | 40 net/dccp/minisocks.c | 2 net/dccp/options.c | 18 net/dccp/output.c | 3 net/dccp/probe.c | 17 net/decnet/af_decnet.c | 1 net/decnet/dn_dev.c | 31 net/decnet/dn_fib.c | 8 net/decnet/dn_neigh.c | 6 net/decnet/dn_nsp_in.c | 7 net/decnet/dn_nsp_out.c | 8 net/decnet/dn_route.c | 28 net/decnet/dn_rules.c | 6 net/decnet/dn_table.c | 11 net/decnet/netfilter/dn_rtmsg.c | 8 net/econet/af_econet.c | 15 net/ethernet/eth.c | 5 net/ieee80211/Kconfig | 3 net/ieee80211/ieee80211_crypt.c | 2 net/ieee80211/ieee80211_crypt_ccmp.c | 4 net/ieee80211/ieee80211_crypt_tkip.c | 6 net/ieee80211/ieee80211_crypt_wep.c | 4 net/ieee80211/ieee80211_module.c | 5 net/ieee80211/ieee80211_rx.c | 25 net/ieee80211/ieee80211_tx.c | 12 net/ieee80211/ieee80211_wx.c | 4 net/ipv4/Kconfig | 27 net/ipv4/Makefile | 2 net/ipv4/af_inet.c | 148 + net/ipv4/ah4.c | 14 net/ipv4/arp.c | 16 net/ipv4/cipso_ipv4.c | 45 net/ipv4/devinet.c | 37 net/ipv4/esp4.c | 59 net/ipv4/fib_frontend.c | 21 net/ipv4/fib_hash.c | 2 net/ipv4/fib_rules.c | 11 net/ipv4/fib_semantics.c | 2 net/ipv4/fib_trie.c | 51 net/ipv4/icmp.c | 31 net/ipv4/igmp.c | 43 net/ipv4/inet_diag.c | 90 net/ipv4/inetpeer.c | 38 net/ipv4/ip_forward.c | 14 net/ipv4/ip_fragment.c | 47 net/ipv4/ip_gre.c | 63 net/ipv4/ip_input.c | 38 net/ipv4/ip_options.c | 26 net/ipv4/ip_output.c | 129 - net/ipv4/ip_sockglue.c | 1169 +++-- net/ipv4/ipcomp.c | 58 net/ipv4/ipconfig.c | 19 net/ipv4/ipip.c | 60 net/ipv4/ipmr.c | 418 +- net/ipv4/ipvs/ip_vs_app.c | 14 net/ipv4/ipvs/ip_vs_core.c | 56 net/ipv4/ipvs/ip_vs_dh.c | 2 net/ipv4/ipvs/ip_vs_ftp.c | 8 net/ipv4/ipvs/ip_vs_lblc.c | 2 net/ipv4/ipvs/ip_vs_lblcr.c | 2 net/ipv4/ipvs/ip_vs_proto_ah.c | 16 net/ipv4/ipvs/ip_vs_proto_tcp.c | 24 net/ipv4/ipvs/ip_vs_proto_udp.c | 26 net/ipv4/ipvs/ip_vs_sh.c | 2 net/ipv4/ipvs/ip_vs_xmit.c | 44 net/ipv4/multipath_drr.c | 2 net/ipv4/netfilter.c | 8 net/ipv4/netfilter/Kconfig | 267 - net/ipv4/netfilter/Makefile | 45 net/ipv4/netfilter/arp_tables.c | 4 net/ipv4/netfilter/arpt_mangle.c | 12 net/ipv4/netfilter/ip_conntrack_amanda.c | 229 - net/ipv4/netfilter/ip_conntrack_core.c | 1550 ------- net/ipv4/netfilter/ip_conntrack_ftp.c | 520 -- net/ipv4/netfilter/ip_conntrack_helper_h323.c | 1841 -------- net/ipv4/netfilter/ip_conntrack_helper_pptp.c | 684 --- net/ipv4/netfilter/ip_conntrack_irc.c | 314 - net/ipv4/netfilter/ip_conntrack_netbios_ns.c | 143 - net/ipv4/netfilter/ip_conntrack_netlink.c | 1577 ------- net/ipv4/netfilter/ip_conntrack_proto_generic.c | 74 net/ipv4/netfilter/ip_conntrack_proto_gre.c | 328 - net/ipv4/netfilter/ip_conntrack_proto_icmp.c | 315 - net/ipv4/netfilter/ip_conntrack_proto_sctp.c | 659 --- net/ipv4/netfilter/ip_conntrack_proto_tcp.c | 1164 ----- net/ipv4/netfilter/ip_conntrack_proto_udp.c | 148 - net/ipv4/netfilter/ip_conntrack_sip.c | 520 -- net/ipv4/netfilter/ip_conntrack_standalone.c | 962 ---- net/ipv4/netfilter/ip_conntrack_tftp.c | 161 - net/ipv4/netfilter/ip_nat_amanda.c | 85 net/ipv4/netfilter/ip_nat_core.c | 634 --- net/ipv4/netfilter/ip_nat_ftp.c | 180 - net/ipv4/netfilter/ip_nat_helper.c | 436 -- net/ipv4/netfilter/ip_nat_helper_h323.c | 611 --- net/ipv4/netfilter/ip_nat_helper_pptp.c | 350 -- net/ipv4/netfilter/ip_nat_irc.c | 122 - net/ipv4/netfilter/ip_nat_proto_gre.c | 174 - net/ipv4/netfilter/ip_nat_proto_icmp.c | 87 net/ipv4/netfilter/ip_nat_proto_tcp.c | 154 - net/ipv4/netfilter/ip_nat_proto_udp.c | 144 - net/ipv4/netfilter/ip_nat_proto_unknown.c | 55 net/ipv4/netfilter/ip_nat_rule.c | 314 - net/ipv4/netfilter/ip_nat_sip.c | 282 - net/ipv4/netfilter/ip_nat_snmp_basic.c | 1333 ------ net/ipv4/netfilter/ip_nat_standalone.c | 388 -- net/ipv4/netfilter/ip_nat_tftp.c | 70 net/ipv4/netfilter/ip_queue.c | 28 net/ipv4/netfilter/ip_tables.c | 12 net/ipv4/netfilter/ipt_CLUSTERIP.c | 24 net/ipv4/netfilter/ipt_ECN.c | 15 net/ipv4/netfilter/ipt_LOG.c | 16 net/ipv4/netfilter/ipt_MASQUERADE.c | 57 net/ipv4/netfilter/ipt_NETMAP.c | 26 net/ipv4/netfilter/ipt_REDIRECT.c | 24 net/ipv4/netfilter/ipt_REJECT.c | 45 net/ipv4/netfilter/ipt_SAME.c | 40 net/ipv4/netfilter/ipt_TOS.c | 4 net/ipv4/netfilter/ipt_TTL.c | 2 net/ipv4/netfilter/ipt_ULOG.c | 77 net/ipv4/netfilter/ipt_addrtype.c | 2 net/ipv4/netfilter/ipt_ecn.c | 10 net/ipv4/netfilter/ipt_iprange.c | 2 net/ipv4/netfilter/ipt_recent.c | 6 net/ipv4/netfilter/ipt_tos.c | 2 net/ipv4/netfilter/ipt_ttl.c | 11 net/ipv4/netfilter/iptable_filter.c | 3 net/ipv4/netfilter/iptable_mangle.c | 30 net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 27 net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 11 net/ipv4/netfilter/nf_nat_core.c | 14 net/ipv4/netfilter/nf_nat_h323.c | 14 net/ipv4/netfilter/nf_nat_helper.c | 76 net/ipv4/netfilter/nf_nat_pptp.c | 2 net/ipv4/netfilter/nf_nat_rule.c | 2 net/ipv4/netfilter/nf_nat_sip.c | 11 net/ipv4/netfilter/nf_nat_snmp_basic.c | 8 net/ipv4/netfilter/nf_nat_standalone.c | 18 net/ipv4/proc.c | 41 net/ipv4/protocol.c | 2 net/ipv4/raw.c | 18 net/ipv4/route.c | 29 net/ipv4/syncookies.c | 40 net/ipv4/sysctl_net_ipv4.c | 16 net/ipv4/tcp.c | 145 - net/ipv4/tcp_bic.c | 2 net/ipv4/tcp_cong.c | 45 net/ipv4/tcp_cubic.c | 81 net/ipv4/tcp_htcp.c | 2 net/ipv4/tcp_hybla.c | 2 net/ipv4/tcp_illinois.c | 356 ++ net/ipv4/tcp_input.c | 646 ++- net/ipv4/tcp_ipv4.c | 143 - net/ipv4/tcp_lp.c | 8 net/ipv4/tcp_minisocks.c | 29 net/ipv4/tcp_output.c | 206 - net/ipv4/tcp_probe.c | 68 net/ipv4/tcp_timer.c | 10 net/ipv4/tcp_vegas.c | 57 net/ipv4/tcp_vegas.h | 24 net/ipv4/tcp_veno.c | 10 net/ipv4/tcp_westwood.c | 21 net/ipv4/tcp_yeah.c | 268 + net/ipv4/tcp_yeah.h | 7 net/ipv4/udp.c | 438 +- net/ipv4/udplite.c | 2 net/ipv4/xfrm4_input.c | 23 net/ipv4/xfrm4_mode_beet.c | 37 net/ipv4/xfrm4_mode_transport.c | 28 net/ipv4/xfrm4_mode_tunnel.c | 31 net/ipv4/xfrm4_output.c | 3 net/ipv4/xfrm4_policy.c | 8 net/ipv4/xfrm4_tunnel.c | 3 net/ipv6/Kconfig | 10 net/ipv6/Makefile | 5 net/ipv6/addrconf.c | 246 + net/ipv6/af_inet6.c | 88 net/ipv6/ah6.c | 34 net/ipv6/datagram.c | 63 net/ipv6/esp6.c | 52 net/ipv6/exthdrs.c | 118 - net/ipv6/fib6_rules.c | 39 net/ipv6/icmp.c | 48 net/ipv6/ip6_fib.c | 4 net/ipv6/ip6_input.c | 18 net/ipv6/ip6_output.c | 187 + net/ipv6/ip6_tunnel.c | 643 ++- net/ipv6/ipcomp6.c | 16 net/ipv6/ipv6_sockglue.c | 46 net/ipv6/ipv6_syms.c | 36 net/ipv6/mcast.c | 46 net/ipv6/mip6.c | 62 net/ipv6/ndisc.c | 435 +- net/ipv6/netfilter.c | 8 net/ipv6/netfilter/ip6_queue.c | 28 net/ipv6/netfilter/ip6_tables.c | 17 net/ipv6/netfilter/ip6t_HL.c | 2 net/ipv6/netfilter/ip6t_LOG.c | 21 net/ipv6/netfilter/ip6t_REJECT.c | 11 net/ipv6/netfilter/ip6t_eui64.c | 8 net/ipv6/netfilter/ip6t_hl.c | 2 net/ipv6/netfilter/ip6t_ipv6header.c | 2 net/ipv6/netfilter/ip6table_filter.c | 2 net/ipv6/netfilter/ip6table_mangle.c | 18 net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 30 net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 7 net/ipv6/netfilter/nf_conntrack_reasm.c | 59 net/ipv6/proc.c | 62 net/ipv6/protocol.c | 4 net/ipv6/raw.c | 52 net/ipv6/reassembly.c | 62 net/ipv6/route.c | 24 net/ipv6/sit.c | 58 net/ipv6/tcp_ipv6.c | 120 - net/ipv6/udp.c | 123 - net/ipv6/udplite.c | 2 net/ipv6/xfrm6_input.c | 18 net/ipv6/xfrm6_mode_beet.c | 27 net/ipv6/xfrm6_mode_ro.c | 7 net/ipv6/xfrm6_mode_transport.c | 20 net/ipv6/xfrm6_mode_tunnel.c | 36 net/ipv6/xfrm6_output.c | 6 net/ipv6/xfrm6_policy.c | 25 net/ipv6/xfrm6_tunnel.c | 4 net/ipx/af_ipx.c | 8 net/ipx/ipx_route.c | 4 net/irda/af_irda.c | 136 - net/irda/ircomm/ircomm_param.c | 4 net/irda/irda_device.c | 21 net/irda/irlan/irlan_common.c | 2 net/irda/irlan/irlan_eth.c | 3 net/irda/irlap_event.c | 2 net/irda/irlap_frame.c | 18 net/irda/irqueue.c | 9 net/irda/irttp.c | 10 net/irda/parameters.c | 8 net/irda/qos.c | 14 net/irda/wrapper.c | 5 net/iucv/af_iucv.c | 6 net/iucv/iucv.c | 207 + net/key/af_key.c | 4 net/llc/llc_input.c | 2 net/llc/llc_output.c | 8 net/llc/llc_sap.c | 5 net/netfilter/Kconfig | 63 net/netfilter/core.c | 21 net/netfilter/nf_conntrack_core.c | 58 net/netfilter/nf_conntrack_ecache.c | 23 net/netfilter/nf_conntrack_expect.c | 4 net/netfilter/nf_conntrack_ftp.c | 6 net/netfilter/nf_conntrack_netbios_ns.c | 2 net/netfilter/nf_conntrack_netlink.c | 66 net/netfilter/nf_conntrack_proto.c | 144 - net/netfilter/nf_conntrack_proto_generic.c | 5 net/netfilter/nf_conntrack_proto_sctp.c | 9 net/netfilter/nf_conntrack_proto_tcp.c | 88 net/netfilter/nf_conntrack_proto_udp.c | 5 net/netfilter/nf_conntrack_standalone.c | 11 net/netfilter/nfnetlink.c | 197 - net/netfilter/nfnetlink_log.c | 108 net/netfilter/nfnetlink_queue.c | 20 net/netfilter/x_tables.c | 26 net/netfilter/xt_CONNMARK.c | 32 net/netfilter/xt_CONNSECMARK.c | 18 net/netfilter/xt_DSCP.c | 10 net/netfilter/xt_NOTRACK.c | 4 net/netfilter/xt_TCPMSS.c | 12 net/netfilter/xt_connbytes.c | 35 net/netfilter/xt_connmark.c | 17 net/netfilter/xt_conntrack.c | 110 net/netfilter/xt_dscp.c | 6 net/netfilter/xt_hashlimit.c | 14 net/netfilter/xt_helper.c | 60 net/netfilter/xt_length.c | 5 net/netfilter/xt_limit.c | 7 net/netfilter/xt_mac.c | 4 net/netfilter/xt_pkttype.c | 2 net/netfilter/xt_realm.c | 2 net/netfilter/xt_state.c | 4 net/netlabel/netlabel_kapi.c | 3 net/netlink/af_netlink.c | 98 net/netlink/attr.c | 5 net/netlink/genetlink.c | 66 net/netrom/af_netrom.c | 115 - net/netrom/nr_dev.c | 4 net/netrom/nr_in.c | 6 net/netrom/nr_loopback.c | 4 net/netrom/nr_out.c | 8 net/netrom/nr_subr.c | 4 net/packet/af_packet.c | 94 net/rose/af_rose.c | 70 net/rose/rose_loopback.c | 2 net/rose/rose_route.c | 2 net/rxrpc/Kconfig | 42 net/rxrpc/Makefile | 40 net/rxrpc/af_rxrpc.c | 879 ++++ net/rxrpc/ar-accept.c | 504 ++ net/rxrpc/ar-ack.c | 1250 +++++ net/rxrpc/ar-call.c | 804 ++++ net/rxrpc/ar-connection.c | 911 ++++ net/rxrpc/ar-connevent.c | 403 ++ net/rxrpc/ar-error.c | 253 + net/rxrpc/ar-input.c | 797 ++++ net/rxrpc/ar-internal.h | 808 ++++ net/rxrpc/ar-key.c | 334 + net/rxrpc/ar-local.c | 309 + net/rxrpc/ar-output.c | 734 +++ net/rxrpc/ar-peer.c | 273 + net/rxrpc/ar-proc.c | 247 + net/rxrpc/ar-recvmsg.c | 437 ++ net/rxrpc/ar-security.c | 258 + net/rxrpc/ar-skbuff.c | 132 + net/rxrpc/ar-transport.c | 276 + net/rxrpc/call.c | 2277 ---------- net/rxrpc/connection.c | 777 --- net/rxrpc/internal.h | 106 net/rxrpc/krxiod.c | 262 - net/rxrpc/krxsecd.c | 270 - net/rxrpc/krxtimod.c | 204 - net/rxrpc/main.c | 180 - net/rxrpc/peer.c | 398 -- net/rxrpc/proc.c | 617 --- net/rxrpc/rxkad.c | 1154 +++++ net/rxrpc/rxrpc_syms.c | 34 net/rxrpc/sysctl.c | 121 - net/rxrpc/transport.c | 846 ---- net/sched/Kconfig | 56 net/sched/act_api.c | 81 net/sched/act_gact.c | 5 net/sched/act_ipt.c | 5 net/sched/act_mirred.c | 5 net/sched/act_pedit.c | 7 net/sched/act_police.c | 34 net/sched/act_simple.c | 5 net/sched/cls_api.c | 36 net/sched/cls_basic.c | 7 net/sched/cls_fw.c | 7 net/sched/cls_route.c | 11 net/sched/cls_rsvp.c | 1 net/sched/cls_rsvp.h | 12 net/sched/cls_rsvp6.c | 1 net/sched/cls_tcindex.c | 9 net/sched/cls_u32.c | 13 net/sched/em_u32.c | 2 net/sched/ematch.c | 17 net/sched/sch_api.c | 227 - net/sched/sch_atm.c | 28 net/sched/sch_cbq.c | 207 - net/sched/sch_dsmark.c | 22 net/sched/sch_generic.c | 35 net/sched/sch_hfsc.c | 109 net/sched/sch_htb.c | 130 - net/sched/sch_ingress.c | 27 net/sched/sch_netem.c | 108 net/sched/sch_prio.c | 14 net/sched/sch_sfq.c | 9 net/sched/sch_tbf.c | 47 net/sched/sch_teql.c | 2 net/sctp/associola.c | 14 net/sctp/debug.c | 5 net/sctp/input.c | 51 net/sctp/inqueue.c | 8 net/sctp/ipv6.c | 36 net/sctp/output.c | 2 net/sctp/outqueue.c | 12 net/sctp/protocol.c | 20 net/sctp/sm_make_chunk.c | 12 net/sctp/sm_sideeffect.c | 16 net/sctp/sm_statefuns.c | 30 net/sctp/sm_statetable.c | 2 net/sctp/socket.c | 363 +- net/sctp/transport.c | 2 net/sctp/ulpevent.c | 49 net/sctp/ulpqueue.c | 173 + net/socket.c | 33 net/sunrpc/cache.c | 10 net/sunrpc/socklib.c | 2 net/sunrpc/svcsock.c | 10 net/tipc/config.c | 2 net/tipc/eth_media.c | 8 net/tipc/link.c | 48 net/tipc/msg.h | 18 net/tipc/netlink.c | 2 net/tipc/port.c | 8 net/tipc/socket.c | 2 net/unix/af_unix.c | 2 net/wanrouter/wanmain.c | 6 net/wireless/Kconfig | 16 net/wireless/Makefile | 4 net/wireless/core.c | 224 + net/wireless/core.h | 49 net/wireless/sysfs.c | 80 net/wireless/sysfs.h | 9 net/wireless/wext.c | 1509 +++++++ net/x25/af_x25.c | 22 net/x25/x25_dev.c | 4 net/x25/x25_in.c | 14 net/x25/x25_out.c | 6 net/xfrm/xfrm_algo.c | 169 - net/xfrm/xfrm_input.c | 6 net/xfrm/xfrm_policy.c | 85 net/xfrm/xfrm_state.c | 60 net/xfrm/xfrm_user.c | 256 + security/keys/keyring.c | 2 security/selinux/Makefile | 2 security/selinux/avc.c | 2 security/selinux/hooks.c | 44 security/selinux/include/av_perm_to_string.h | 102 security/selinux/include/av_permissions.h | 179 - security/selinux/include/class_to_string.h | 34 security/selinux/include/flask.h | 16 security/selinux/include/netlabel.h | 121 + security/selinux/include/security.h | 29 security/selinux/include/selinux_netlabel.h | 124 - security/selinux/netlabel.c | 363 ++ security/selinux/netlink.c | 4 security/selinux/selinuxfs.c | 85 security/selinux/ss/services.c | 499 -- sound/aoa/codecs/snd-aoa-codec-onyx.c | 4 sound/aoa/codecs/snd-aoa-codec-tas.c | 9 sound/aoa/core/snd-aoa-gpio-feature.c | 8 sound/aoa/fabrics/snd-aoa-fabric-layout.c | 8 sound/aoa/soundbus/core.c | 82 sound/aoa/soundbus/i2sbus/i2sbus-core.c | 8 sound/oss/dmasound/dmasound_awacs.c | 121 - sound/oss/dmasound/tas_common.c | 9 sound/ppc/pmac.c | 41 sound/ppc/tumbler.c | 55 sound/sparc/amd7930.c | 4 sound/sparc/cs4231.c | 2 2545 files changed, 163248 insertions(+), 107981 deletions(-) diff --git a/CREDITS b/CREDITS index 6bd8ab8..d714030 100644 --- a/CREDITS +++ b/CREDITS @@ -317,6 +317,12 @@ S: 2322 37th Ave SW S: Seattle, Washington 98126-2010 S: USA +N: Johannes Berg +E: johannes@sipsolutions.net +W: http://johannes.sipsolutions.net/ +P: 1024D/9AB78CA5 AD02 0176 4E29 C137 1DF6 08D2 FC44 CF86 9AB7 8CA5 +D: powerpc & 802.11 hacker + N: Stephen R. van den Berg (AKA BuGless) E: berg@pool.informatik.rwth-aachen.de D: General kernel, gcc, and libc hacker @@ -2286,14 +2292,14 @@ S: D-90453 Nuernberg S: Germany N: Arnaldo Carvalho de Melo -E: acme@mandriva.com E: acme@ghostprotocols.net +E: arnaldo.melo@gmail.com +E: acme@redhat.com W: http://oops.ghostprotocols.net:81/blog/ P: 1024D/9224DF01 D5DF E3BB E3C8 BCBB F8AD 841A B6AB 4681 9224 DF01 D: IPX, LLC, DCCP, cyc2x, wl3501_cs, net/ hacks -S: Mandriva -S: R. Tocantins, 89 - Cristo Rei -S: 80050-430 - Curitiba - Paran +S: R. Braslio Itiber, 4270/1010 - gua Verde +S: 80240-060 - Curitiba - Paran S: Brazil N: Karsten Merker @@ -3295,6 +3301,14 @@ S: 12725 SW Millikan Way, Suite 400 S: Beaverton, Oregon 97005 S: USA +N: Li Yang +E: leoli@freescale.com +D: Freescale Highspeed USB device driver +D: Freescale QE SoC support and Ethernet driver +S: B-1206 Jingmao Guojigongyu +S: 16 Baliqiao Nanjie, Beijing 101100 +S: People's Repulic of China + N: Marcelo Tosatti E: marcelo@kvack.org D: v2.4 kernel maintainer diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb new file mode 100644 index 0000000..f9937ad --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -0,0 +1,41 @@ +What: /sys/bus/usb/devices/.../power/autosuspend +Date: March 2007 +KernelVersion: 2.6.21 +Contact: Alan Stern +Description: + Each USB device directory will contain a file named + power/autosuspend. This file holds the time (in seconds) + the device must be idle before it will be autosuspended. + 0 means the device will be autosuspended as soon as + possible. Negative values will prevent the device from + being autosuspended at all, and writing a negative value + will resume the device if it is already suspended. + + The autosuspend delay for newly-created devices is set to + the value of the usbcore.autosuspend module parameter. + +What: /sys/bus/usb/devices/.../power/level +Date: March 2007 +KernelVersion: 2.6.21 +Contact: Alan Stern +Description: + Each USB device directory will contain a file named + power/level. This file holds a power-level setting for + the device, one of "on", "auto", or "suspend". + + "on" means that the device is not allowed to autosuspend, + although normal suspends for system sleep will still + be honored. "auto" means the device will autosuspend + and autoresume in the usual manner, according to the + capabilities of its driver. "suspend" means the device + is forced into a suspended state and it will not autoresume + in response to I/O requests. However remote-wakeup requests + from the device may still be enabled (the remote-wakeup + setting is controlled separately by the power/wakeup + attribute). + + During normal use, devices should be left in the "auto" + level. The other levels are meant for administrative uses. + If you want to suspend a device immediately but leave it + free to wake up in response to I/O requests, you should + write "0" to power/autosuspend. diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 0bb9023..b61dfc7 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -236,6 +236,12 @@ X!Ilib/string.c !Enet/core/dev.c !Enet/ethernet/eth.c !Iinclude/linux/etherdevice.h +!Edrivers/net/phy/phy.c +!Idrivers/net/phy/phy.c +!Edrivers/net/phy/phy_device.c +!Idrivers/net/phy/phy_device.c +!Edrivers/net/phy/mdio_bus.c +!Idrivers/net/phy/mdio_bus.c diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 19b4c96..5c88ba1 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,6 +6,18 @@ be removed from this file. --------------------------- +What: V4L2 VIDIOC_G_MPEGCOMP and VIDIOC_S_MPEGCOMP +When: October 2007 +Why: Broken attempt to set MPEG compression parameters. These ioctls are + not able to implement the wide variety of parameters that can be set + by hardware MPEG encoders. A new MPEG control mechanism was created + in kernel 2.6.18 that replaces these ioctls. See the V4L2 specification + (section 1.9: Extended controls) for more information on this topic. +Who: Hans Verkuil and + Mauro Carvalho Chehab + +--------------------------- + What: /sys/devices/.../power/state dev->power.power_state dpm_runtime_{suspend,resume)() @@ -134,15 +146,6 @@ Who: Arjan van de Ven - ---------------------------- - What: USB driver API moves to EXPORT_SYMBOL_GPL When: February 2008 Files: include/linux/usb.h, drivers/usb/core/driver.c @@ -211,15 +214,6 @@ Who: Adrian Bunk --------------------------- -What: IPv4 only connection tracking/NAT/helpers -When: 2.6.22 -Why: The new layer 3 independant connection tracking replaces the old - IPv4 only version. After some stabilization of the new code the - old one will be removed. -Who: Patrick McHardy - ---------------------------- - What: ACPI hooks (X86_SPEEDSTEP_CENTRINO_ACPI) in speedstep-centrino driver When: December 2006 Why: Speedstep-centrino driver with ACPI hooks and acpi-cpufreq driver are @@ -294,18 +288,6 @@ Who: Richard Purdie --------------------------- -What: Wireless extensions over netlink (CONFIG_NET_WIRELESS_RTNETLINK) -When: with the merge of wireless-dev, 2.6.22 or later -Why: The option/code is - * not enabled on most kernels - * not required by any userspace tools (except an experimental one, - and even there only for some parts, others use ioctl) - * pointless since wext is no longer evolving and the ioctl - interface needs to be kept -Who: Johannes Berg - ---------------------------- - What: i8xx_tco watchdog driver When: in 2.6.22 Why: the i8xx_tco watchdog driver has been replaced by the iTCO_wdt @@ -313,3 +295,22 @@ Why: the i8xx_tco watchdog driver has be Who: Wim Van Sebroeck --------------------------- + +What: Multipath cached routing support in ipv4 +When: in 2.6.23 +Why: Code was merged, then submitter immediately disappeared leaving + us with no maintainer and lots of bugs. The code should not have + been merged in the first place, and many aspects of it's + implementation are blocking more critical core networking + development. It's marked EXPERIMENTAL and no distribution + enables it because it cause obscure crashes due to unfixable bugs + (interfaces don't return errors so memory allocation can't be + handled, calling contexts of these interfaces make handling + errors impossible too because they get called after we've + totally commited to creating a route object, for example). + This problem has existed for years and no forward progress + has ever been made, and nobody steps up to try and salvage + this code, so we're going to finally just get rid of it. +Who: David S. Miller + +--------------------------- diff --git a/Documentation/filesystems/afs.txt b/Documentation/filesystems/afs.txt index 2f4237d..12ad6c7 100644 --- a/Documentation/filesystems/afs.txt +++ b/Documentation/filesystems/afs.txt @@ -1,31 +1,82 @@ + ==================== kAFS: AFS FILESYSTEM ==================== -ABOUT -===== +Contents: + + - Overview. + - Usage. + - Mountpoints. + - Proc filesystem. + - The cell database. + - Security. + - Examples. + + +======== +OVERVIEW +======== -This filesystem provides a fairly simple AFS filesystem driver. It is under -development and only provides very basic facilities. It does not yet support -the following AFS features: +This filesystem provides a fairly simple secure AFS filesystem driver. It is +under development and does not yet provide the full feature set. The features +it does support include: - (*) Write support. - (*) Communications security. - (*) Local caching. - (*) pioctl() system call. - (*) Automatic mounting of embedded mountpoints. + (*) Security (currently only AFS kaserver and KerberosIV tickets). + (*) File reading. + (*) Automounting. + +It does not yet support the following AFS features: + + (*) Write support. + + (*) Local caching. + + (*) pioctl() system call. + + +=========== +COMPILATION +=========== + +The filesystem should be enabled by turning on the kernel configuration +options: + + CONFIG_AF_RXRPC - The RxRPC protocol transport + CONFIG_RXKAD - The RxRPC Kerberos security handler + CONFIG_AFS - The AFS filesystem + +Additionally, the following can be turned on to aid debugging: + + CONFIG_AF_RXRPC_DEBUG - Permit AF_RXRPC debugging to be enabled + CONFIG_AFS_DEBUG - Permit AFS debugging to be enabled + +They permit the debugging messages to be turned on dynamically by manipulating +the masks in the following files: + + /sys/module/af_rxrpc/parameters/debug + /sys/module/afs/parameters/debug + + +===== USAGE ===== When inserting the driver modules the root cell must be specified along with a list of volume location server IP addresses: - insmod rxrpc.o + insmod af_rxrpc.o + insmod rxkad.o insmod kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91 -The first module is a driver for the RxRPC remote operation protocol, and the -second is the actual filesystem driver for the AFS filesystem. +The first module is the AF_RXRPC network protocol driver. This provides the +RxRPC remote operation protocol and may also be accessed from userspace. See: + + Documentation/networking/rxrpc.txt + +The second module is the kerberos RxRPC security driver, and the third module +is the actual filesystem driver for the AFS filesystem. Once the module has been loaded, more modules can be added by the following procedure: @@ -33,7 +84,7 @@ procedure: echo add grand.central.org 18.7.14.88:128.2.191.224 >/proc/fs/afs/cells Where the parameters to the "add" command are the name of a cell and a list of -volume location servers within that cell. +volume location servers within that cell, with the latter separated by colons. Filesystems can be mounted anywhere by commands similar to the following: @@ -42,11 +93,6 @@ Filesystems can be mounted anywhere by c mount -t afs "#root.afs." /afs mount -t afs "#root.cell." /afs/cambridge - NB: When using this on Linux 2.4, the mount command has to be different, - since the filesystem doesn't have access to the device name argument: - - mount -t afs none /afs -ovol="#root.afs." - Where the initial character is either a hash or a percent symbol depending on whether you definitely want a R/W volume (hash) or whether you'd prefer a R/O volume, but are willing to use a R/W volume instead (percent). @@ -60,55 +106,66 @@ named volume will be looked up in the ce Additional cells can be added through /proc (see later section). +=========== MOUNTPOINTS =========== -AFS has a concept of mountpoints. These are specially formatted symbolic links -(of the same form as the "device name" passed to mount). kAFS presents these -to the user as directories that have special properties: +AFS has a concept of mountpoints. In AFS terms, these are specially formatted +symbolic links (of the same form as the "device name" passed to mount). kAFS +presents these to the user as directories that have a follow-link capability +(ie: symbolic link semantics). If anyone attempts to access them, they will +automatically cause the target volume to be mounted (if possible) on that site. - (*) They cannot be listed. Running a program like "ls" on them will incur an - EREMOTE error (Object is remote). +Automatically mounted filesystems will be automatically unmounted approximately +twenty minutes after they were last used. Alternatively they can be unmounted +directly with the umount() system call. - (*) Other objects can't be looked up inside of them. This also incurs an - EREMOTE error. +Manually unmounting an AFS volume will cause any idle submounts upon it to be +culled first. If all are culled, then the requested volume will also be +unmounted, otherwise error EBUSY will be returned. - (*) They can be queried with the readlink() system call, which will return - the name of the mountpoint to which they point. The "readlink" program - will also work. +This can be used by the administrator to attempt to unmount the whole AFS tree +mounted on /afs in one go by doing: - (*) They can be mounted on (which symbolic links can't). + umount /afs +=============== PROC FILESYSTEM =============== -The rxrpc module creates a number of files in various places in the /proc -filesystem: - - (*) Firstly, some information files are made available in a directory called - "/proc/net/rxrpc/". These list the extant transport endpoint, peer, - connection and call records. - - (*) Secondly, some control files are made available in a directory called - "/proc/sys/rxrpc/". Currently, all these files can be used for is to - turn on various levels of tracing. - The AFS modules creates a "/proc/fs/afs/" directory and populates it: - (*) A "cells" file that lists cells currently known to the afs module. + (*) A "cells" file that lists cells currently known to the afs module and + their usage counts: + + [root@andromeda ~]# cat /proc/fs/afs/cells + USE NAME + 3 cambridge.redhat.com (*) A directory per cell that contains files that list volume location servers, volumes, and active servers known within that cell. + [root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/servers + USE ADDR STATE + 4 172.16.18.91 0 + [root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/vlservers + ADDRESS + 172.16.18.91 + [root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/volumes + USE STT VLID[0] VLID[1] VLID[2] NAME + 1 Val 20000000 20000001 20000002 root.afs + +================= THE CELL DATABASE ================= -The filesystem maintains an internal database of all the cells it knows and -the IP addresses of the volume location servers for those cells. The cell to -which the computer belongs is added to the database when insmod is performed -by the "rootcell=" argument. +The filesystem maintains an internal database of all the cells it knows and the +IP addresses of the volume location servers for those cells. The cell to which +the system belongs is added to the database when insmod is performed by the +"rootcell=" argument or, if compiled in, using a "kafs.rootcell=" argument on +the kernel command line. Further cells can be added by commands similar to the following: @@ -118,20 +175,65 @@ Further cells can be added by commands s No other cell database operations are available at this time. +======== +SECURITY +======== + +Secure operations are initiated by acquiring a key using the klog program. A +very primitive klog program is available at: + + http://people.redhat.com/~dhowells/rxrpc/klog.c + +This should be compiled by: + + make klog LDLIBS="-lcrypto -lcrypt -lkrb4 -lkeyutils" + +And then run as: + + ./klog + +Assuming it's successful, this adds a key of type RxRPC, named for the service +and cell, eg: "afs@". This can be viewed with the keyctl program or +by cat'ing /proc/keys: + + [root@andromeda ~]# keyctl show + Session Keyring + -3 --alswrv 0 0 keyring: _ses.3268 + 2 --alswrv 0 0 \_ keyring: _uid.0 + 111416553 --als--v 0 0 \_ rxrpc: afs@CAMBRIDGE.REDHAT.COM + +Currently the username, realm, password and proposed ticket lifetime are +compiled in to the program. + +It is not required to acquire a key before using AFS facilities, but if one is +not acquired then all operations will be governed by the anonymous user parts +of the ACLs. + +If a key is acquired, then all AFS operations, including mounts and automounts, +made by a possessor of that key will be secured with that key. + +If a file is opened with a particular key and then the file descriptor is +passed to a process that doesn't have that key (perhaps over an AF_UNIX +socket), then the operations on the file will be made with key that was used to +open the file. + + +======== EXAMPLES ======== -Here's what I use to test this. Some of the names and IP addresses are local -to my internal DNS. My "root.afs" partition has a mount point within it for +Here's what I use to test this. Some of the names and IP addresses are local +to my internal DNS. My "root.afs" partition has a mount point within it for some public volumes volumes. -insmod -S /tmp/rxrpc.o -insmod -S /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91 +insmod /tmp/rxrpc.o +insmod /tmp/rxkad.o +insmod /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.91 mount -t afs \%root.afs. /afs mount -t afs \%cambridge.redhat.com:root.cell. /afs/cambridge.redhat.com/ -echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells +echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells mount -t afs "#grand.central.org:root.cell." /afs/grand.central.org/ mount -t afs "#grand.central.org:root.archive." /afs/grand.central.org/archive mount -t afs "#grand.central.org:root.contrib." /afs/grand.central.org/contrib @@ -141,15 +243,7 @@ mount -t afs "#grand.central.org:root.se mount -t afs "#grand.central.org:root.software." /afs/grand.central.org/software mount -t afs "#grand.central.org:root.user." /afs/grand.central.org/user -umount /afs/grand.central.org/user -umount /afs/grand.central.org/software -umount /afs/grand.central.org/service -umount /afs/grand.central.org/project -umount /afs/grand.central.org/doc -umount /afs/grand.central.org/contrib -umount /afs/grand.central.org/archive -umount /afs/grand.central.org -umount /afs/cambridge.redhat.com umount /afs rmmod kafs +rmmod rxkad rmmod rxrpc diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 5484ab5..7aaf09b 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1421,6 +1421,15 @@ fewer messages that will be written. Mes be dropped. The default settings limit warning messages to one every five seconds. +warnings +-------- + +This controls console messages from the networking stack that can occur because +of problems on the network like duplicate address or bad checksums. Normally, +this should be enabled, but if the problem persists the messages can be +disabled. + + netdev_max_backlog ------------------ diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt deleted file mode 100644 index 0132d36..0000000 --- a/Documentation/ibm-acpi.txt +++ /dev/null @@ -1,728 +0,0 @@ - IBM ThinkPad ACPI Extras Driver - - Version 0.12 - 17 August 2005 - - Borislav Deianov - http://ibm-acpi.sf.net/ - - -This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports -various features of these laptops which are accessible through the -ACPI framework but not otherwise supported by the generic Linux ACPI -drivers. - - -Status ------- - -The features currently supported are the following (see below for -detailed description): - - - Fn key combinations - - Bluetooth enable and disable - - video output switching, expansion control - - ThinkLight on and off - - limited docking and undocking - - UltraBay eject - - CMOS control - - LED control - - ACPI sounds - - temperature sensors - - Experimental: embedded controller register dump - - LCD brightness control - - Volume control - - Experimental: fan speed, fan enable/disable - - Experimental: WAN enable and disable - -A compatibility table by model and feature is maintained on the web -site, http://ibm-acpi.sf.net/. I appreciate any success or failure -reports, especially if they add to or correct the compatibility table. -Please include the following information in your report: - - - ThinkPad model name - - a copy of your DSDT, from /proc/acpi/dsdt - - which driver features work and which don't - - the observed behavior of non-working features - -Any other comments or patches are also more than welcome. - - -Installation ------------- - -If you are compiling this driver as included in the Linux kernel -sources, simply enable the CONFIG_ACPI_IBM option (Power Management / -ACPI / IBM ThinkPad Laptop Extras). - -Features --------- - -The driver creates the /proc/acpi/ibm directory. There is a file under -that directory for each feature described below. Note that while the -driver is still in the alpha stage, the exact proc file format and -commands supported by the various features is guaranteed to change -frequently. - -Driver version -- /proc/acpi/ibm/driver ---------------------------------------- - -The driver name and version. No commands can be written to this file. - -Hot keys -- /proc/acpi/ibm/hotkey ---------------------------------- - -Without this driver, only the Fn-F4 key (sleep button) generates an -ACPI event. With the driver loaded, the hotkey feature enabled and the -mask set (see below), the various hot keys generate ACPI events in the -following format: - - ibm/hotkey HKEY 00000080 0000xxxx - -The last four digits vary depending on the key combination pressed. -All labeled Fn-Fx key combinations generate distinct events. In -addition, the lid microswitch and some docking station buttons may -also generate such events. - -The following commands can be written to this file: - - echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature - echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature - echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys - echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys - ... any other 4-hex-digit mask ... - echo reset > /proc/acpi/ibm/hotkey -- restore the original mask - -The bit mask allows some control over which hot keys generate ACPI -events. Not all bits in the mask can be modified. Not all bits that -can be modified do anything. Not all hot keys can be individually -controlled by the mask. Most recent ThinkPad models honor the -following bits (assuming the hot keys feature has been enabled): - - key bit behavior when set behavior when unset - - Fn-F3 always generates ACPI event - Fn-F4 always generates ACPI event - Fn-F5 0010 generate ACPI event enable/disable Bluetooth - Fn-F7 0040 generate ACPI event switch LCD and external display - Fn-F8 0080 generate ACPI event expand screen or none - Fn-F9 0100 generate ACPI event none - Fn-F12 always generates ACPI event - -Some models do not support all of the above. For example, the T30 does -not support Fn-F5 and Fn-F9. Other models do not support the mask at -all. On those models, hot keys cannot be controlled individually. - -Note that enabling ACPI events for some keys prevents their default -behavior. For example, if events for Fn-F5 are enabled, that key will -no longer enable/disable Bluetooth by itself. This can still be done -from an acpid handler for the ibm/hotkey event. - -Note also that not all Fn key combinations are supported through -ACPI. For example, on the X40, the brightness, volume and "Access IBM" -buttons do not generate ACPI events even with this driver. They *can* -be used through the "ThinkPad Buttons" utility, see -http://www.nongnu.org/tpb/ - -Bluetooth -- /proc/acpi/ibm/bluetooth -------------------------------------- - -This feature shows the presence and current state of a Bluetooth -device. If Bluetooth is installed, the following commands can be used: - - echo enable > /proc/acpi/ibm/bluetooth - echo disable > /proc/acpi/ibm/bluetooth - -Video output control -- /proc/acpi/ibm/video --------------------------------------------- - -This feature allows control over the devices used for video output - -LCD, CRT or DVI (if available). The following commands are available: - - echo lcd_enable > /proc/acpi/ibm/video - echo lcd_disable > /proc/acpi/ibm/video - echo crt_enable > /proc/acpi/ibm/video - echo crt_disable > /proc/acpi/ibm/video - echo dvi_enable > /proc/acpi/ibm/video - echo dvi_disable > /proc/acpi/ibm/video - echo auto_enable > /proc/acpi/ibm/video - echo auto_disable > /proc/acpi/ibm/video - echo expand_toggle > /proc/acpi/ibm/video - echo video_switch > /proc/acpi/ibm/video - -Each video output device can be enabled or disabled individually. -Reading /proc/acpi/ibm/video shows the status of each device. - -Automatic video switching can be enabled or disabled. When automatic -video switching is enabled, certain events (e.g. opening the lid, -docking or undocking) cause the video output device to change -automatically. While this can be useful, it also causes flickering -and, on the X40, video corruption. By disabling automatic switching, -the flickering or video corruption can be avoided. - -The video_switch command cycles through the available video outputs -(it simulates the behavior of Fn-F7). - -Video expansion can be toggled through this feature. This controls -whether the display is expanded to fill the entire LCD screen when a -mode with less than full resolution is used. Note that the current -video expansion status cannot be determined through this feature. - -Note that on many models (particularly those using Radeon graphics -chips) the X driver configures the video card in a way which prevents -Fn-F7 from working. This also disables the video output switching -features of this driver, as it uses the same ACPI methods as -Fn-F7. Video switching on the console should still work. - -UPDATE: There's now a patch for the X.org Radeon driver which -addresses this issue. Some people are reporting success with the patch -while others are still having problems. For more information: - -https://bugs.freedesktop.org/show_bug.cgi?id=2000 - -ThinkLight control -- /proc/acpi/ibm/light ------------------------------------------- - -The current status of the ThinkLight can be found in this file. A few -models which do not make the status available will show it as -"unknown". The available commands are: - - echo on > /proc/acpi/ibm/light - echo off > /proc/acpi/ibm/light - -Docking / undocking -- /proc/acpi/ibm/dock ------------------------------------------- - -Docking and undocking (e.g. with the X4 UltraBase) requires some -actions to be taken by the operating system to safely make or break -the electrical connections with the dock. - -The docking feature of this driver generates the following ACPI events: - - ibm/dock GDCK 00000003 00000001 -- eject request - ibm/dock GDCK 00000003 00000002 -- undocked - ibm/dock GDCK 00000000 00000003 -- docked - -NOTE: These events will only be generated if the laptop was docked -when originally booted. This is due to the current lack of support for -hot plugging of devices in the Linux ACPI framework. If the laptop was -booted while not in the dock, the following message is shown in the -logs: - - Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present - -In this case, no dock-related events are generated but the dock and -undock commands described below still work. They can be executed -manually or triggered by Fn key combinations (see the example acpid -configuration files included in the driver tarball package available -on the web site). - -When the eject request button on the dock is pressed, the first event -above is generated. The handler for this event should issue the -following command: - - echo undock > /proc/acpi/ibm/dock - -After the LED on the dock goes off, it is safe to eject the laptop. -Note: if you pressed this key by mistake, go ahead and eject the -laptop, then dock it back in. Otherwise, the dock may not function as -expected. - -When the laptop is docked, the third event above is generated. The -handler for this event should issue the following command to fully -enable the dock: - - echo dock > /proc/acpi/ibm/dock - -The contents of the /proc/acpi/ibm/dock file shows the current status -of the dock, as provided by the ACPI framework. - -The docking support in this driver does not take care of enabling or -disabling any other devices you may have attached to the dock. For -example, a CD drive plugged into the UltraBase needs to be disabled or -enabled separately. See the provided example acpid configuration files -for how this can be accomplished. - -There is no support yet for PCI devices that may be attached to a -docking station, e.g. in the ThinkPad Dock II. The driver currently -does not recognize, enable or disable such devices. This means that -the only docking stations currently supported are the X-series -UltraBase docks and "dumb" port replicators like the Mini Dock (the -latter don't need any ACPI support, actually). - -UltraBay eject -- /proc/acpi/ibm/bay ------------------------------------- - -Inserting or ejecting an UltraBay device requires some actions to be -taken by the operating system to safely make or break the electrical -connections with the device. - -This feature generates the following ACPI events: - - ibm/bay MSTR 00000003 00000000 -- eject request - ibm/bay MSTR 00000001 00000000 -- eject lever inserted - -NOTE: These events will only be generated if the UltraBay was present -when the laptop was originally booted (on the X series, the UltraBay -is in the dock, so it may not be present if the laptop was undocked). -This is due to the current lack of support for hot plugging of devices -in the Linux ACPI framework. If the laptop was booted without the -UltraBay, the following message is shown in the logs: - - Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present - -In this case, no bay-related events are generated but the eject -command described below still works. It can be executed manually or -triggered by a hot key combination. - -Sliding the eject lever generates the first event shown above. The -handler for this event should take whatever actions are necessary to -shut down the device in the UltraBay (e.g. call idectl), then issue -the following command: - - echo eject > /proc/acpi/ibm/bay - -After the LED on the UltraBay goes off, it is safe to pull out the -device. - -When the eject lever is inserted, the second event above is -generated. The handler for this event should take whatever actions are -necessary to enable the UltraBay device (e.g. call idectl). - -The contents of the /proc/acpi/ibm/bay file shows the current status -of the UltraBay, as provided by the ACPI framework. - -EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use -this feature, you need to supply the experimental=1 parameter when -loading the module): - -These models do not have a button near the UltraBay device to request -a hot eject but rather require the laptop to be put to sleep -(suspend-to-ram) before the bay device is ejected or inserted). -The sequence of steps to eject the device is as follows: - - echo eject > /proc/acpi/ibm/bay - put the ThinkPad to sleep - remove the drive - resume from sleep - cat /proc/acpi/ibm/bay should show that the drive was removed - -On the A3x, both the UltraBay 2000 and UltraBay Plus devices are -supported. Use "eject2" instead of "eject" for the second bay. - -Note: the UltraBay eject support on the 600e/x, A22p and A3x is -EXPERIMENTAL and may not work as expected. USE WITH CAUTION! - -CMOS control -- /proc/acpi/ibm/cmos ------------------------------------ - -This feature is used internally by the ACPI firmware to control the -ThinkLight on most newer ThinkPad models. It may also control LCD -brightness, sounds volume and more, but only on some models. - -The commands are non-negative integer numbers: - - echo 0 >/proc/acpi/ibm/cmos - echo 1 >/proc/acpi/ibm/cmos - echo 2 >/proc/acpi/ibm/cmos - ... - -The range of valid numbers is 0 to 21, but not all have an effect and -the behavior varies from model to model. Here is the behavior on the -X40 (tpb is the ThinkPad Buttons utility): - - 0 - no effect but tpb reports "Volume down" - 1 - no effect but tpb reports "Volume up" - 2 - no effect but tpb reports "Mute on" - 3 - simulate pressing the "Access IBM" button - 4 - LCD brightness up - 5 - LCD brightness down - 11 - toggle screen expansion - 12 - ThinkLight on - 13 - ThinkLight off - 14 - no effect but tpb reports ThinkLight status change - -LED control -- /proc/acpi/ibm/led ---------------------------------- - -Some of the LED indicators can be controlled through this feature. The -available commands are: - - echo ' on' >/proc/acpi/ibm/led - echo ' off' >/proc/acpi/ibm/led - echo ' blink' >/proc/acpi/ibm/led - -The range is 0 to 7. The set of LEDs that can be -controlled varies from model to model. Here is the mapping on the X40: - - 0 - power - 1 - battery (orange) - 2 - battery (green) - 3 - UltraBase - 4 - UltraBay - 7 - standby - -All of the above can be turned on and off and can be made to blink. - -ACPI sounds -- /proc/acpi/ibm/beep ----------------------------------- - -The BEEP method is used internally by the ACPI firmware to provide -audible alerts in various situations. This feature allows the same -sounds to be triggered manually. - -The commands are non-negative integer numbers: - - echo >/proc/acpi/ibm/beep - -The valid range is 0 to 17. Not all numbers trigger sounds -and the sounds vary from model to model. Here is the behavior on the -X40: - - 0 - stop a sound in progress (but use 17 to stop 16) - 2 - two beeps, pause, third beep ("low battery") - 3 - single beep - 4 - high, followed by low-pitched beep ("unable") - 5 - single beep - 6 - very high, followed by high-pitched beep ("AC/DC") - 7 - high-pitched beep - 9 - three short beeps - 10 - very long beep - 12 - low-pitched beep - 15 - three high-pitched beeps repeating constantly, stop with 0 - 16 - one medium-pitched beep repeating constantly, stop with 17 - 17 - stop 16 - -Temperature sensors -- /proc/acpi/ibm/thermal ---------------------------------------------- - -Most ThinkPads include six or more separate temperature sensors but -only expose the CPU temperature through the standard ACPI methods. -This feature shows readings from up to eight different sensors on older -ThinkPads, and it has experimental support for up to sixteen different -sensors on newer ThinkPads. Readings from sensors that are not available -return -128. - -No commands can be written to this file. - -EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the -implementation directly accesses hardware registers and may not work as -expected. USE WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. When EXPERIMENTAL -mode is enabled, reading the first 8 sensors on newer ThinkPads will -also use an new experimental thermal sensor access mode. - -For example, on the X40, a typical output may be: -temperatures: 42 42 45 41 36 -128 33 -128 - -EXPERIMENTAL: On the T43/p, a typical output may be: -temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128 - -The mapping of thermal sensors to physical locations varies depending on -system-board model (and thus, on ThinkPad model). - -http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that -tries to track down these locations for various models. - -Most (newer?) models seem to follow this pattern: - -1: CPU -2: (depends on model) -3: (depends on model) -4: GPU -5: Main battery: main sensor -6: Bay battery: main sensor -7: Main battery: secondary sensor -8: Bay battery: secondary sensor -9-15: (depends on model) - -For the R51 (source: Thomas Gruber): -2: Mini-PCI -3: Internal HDD - -For the T43, T43/p (source: Shmidoax/Thinkwiki.org) -http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p -2: System board, left side (near PCMCIA slot), reported as HDAPS temp -3: PCMCIA slot -9: MCH (northbridge) to DRAM Bus -10: ICH (southbridge), under Mini-PCI card, under touchpad -11: Power regulator, underside of system board, below F2 key - -The A31 has a very atypical layout for the thermal sensors -(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31) -1: CPU -2: Main Battery: main sensor -3: Power Converter -4: Bay Battery: main sensor -5: MCH (northbridge) -6: PCMCIA/ambient -7: Main Battery: secondary sensor -8: Bay Battery: secondary sensor - - -EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump ------------------------------------------------------------------------- - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. - -This feature dumps the values of 256 embedded controller -registers. Values which have changed since the last time the registers -were dumped are marked with a star: - -[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump -EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f -EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 -EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 -EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 -EC 0x30: 01 07 1a 00 30 04 00 00 *85 00 00 10 00 50 00 00 -EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 -EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 *bc *02 *bc -EC 0x60: *02 *bc *02 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0x70: 00 00 00 00 00 12 30 40 *24 *26 *2c *27 *20 80 *1f 80 -EC 0x80: 00 00 00 06 *37 *0e 03 00 00 00 0e 07 00 00 00 00 -EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xa0: *ff 09 ff 09 ff ff *64 00 *00 *00 *a2 41 *ff *ff *e0 00 -EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 -EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a - -This feature can be used to determine the register holding the fan -speed on some models. To do that, do the following: - - - make sure the battery is fully charged - - make sure the fan is running - - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so - -The first step makes sure various charging-related values don't -vary. The second ensures that the fan-related values do vary, since -the fan speed fluctuates a bit. The third will (hopefully) mark the -fan register with a star: - -[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump -EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f -EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 -EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 -EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 -EC 0x30: 01 07 1a 00 30 04 00 00 85 00 00 10 00 50 00 00 -EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 -EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 bc 02 bc -EC 0x60: 02 bc 02 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0x70: 00 00 00 00 00 12 30 40 24 27 2c 27 21 80 1f 80 -EC 0x80: 00 00 00 06 *be 0d 03 00 00 00 0e 07 00 00 00 00 -EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xa0: ff 09 ff 09 ff ff 64 00 00 00 a2 41 ff ff e0 00 -EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 -EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a - -Another set of values that varies often is the temperature -readings. Since temperatures don't change vary fast, you can take -several quick dumps to eliminate them. - -You can use a similar method to figure out the meaning of other -embedded controller registers - e.g. make sure nothing else changes -except the charging or discharging battery to determine which -registers contain the current battery capacity, etc. If you experiment -with this, do send me your results (including some complete dumps with -a description of the conditions when they were taken.) - -LCD brightness control -- /proc/acpi/ibm/brightness ---------------------------------------------------- - -This feature allows software control of the LCD brightness on ThinkPad -models which don't have a hardware brightness slider. The available -commands are: - - echo up >/proc/acpi/ibm/brightness - echo down >/proc/acpi/ibm/brightness - echo 'level ' >/proc/acpi/ibm/brightness - -The number range is 0 to 7, although not all of them may be -distinct. The current brightness level is shown in the file. - -Volume control -- /proc/acpi/ibm/volume ---------------------------------------- - -This feature allows volume control on ThinkPad models which don't have -a hardware volume knob. The available commands are: - - echo up >/proc/acpi/ibm/volume - echo down >/proc/acpi/ibm/volume - echo mute >/proc/acpi/ibm/volume - echo 'level ' >/proc/acpi/ibm/volume - -The number range is 0 to 15 although not all of them may be -distinct. The unmute the volume after the mute command, use either the -up or down command (the level command will not unmute the volume). -The current volume level and mute state is shown in the file. - -EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan ------------------------------------------------------------------ - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. - -This feature attempts to show the current fan speed, control mode and -other fan data that might be available. The speed is read directly -from the hardware registers of the embedded controller. This is known -to work on later R, T and X series ThinkPads but may show a bogus -value on other models. - -Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher -the level, the higher the fan speed, although adjacent levels often map -to the same fan speed. 7 is the highest level, where the fan reaches -the maximum recommended speed. Level "auto" means the EC changes the -fan level according to some internal algorithm, usually based on -readings from the thermal sensors. Level "disengaged" means the EC -disables the speed-locked closed-loop fan control, and drives the fan as -fast as it can go, which might exceed hardware limits, so use this level -with caution. - -The fan usually ramps up or down slowly from one speed to another, -and it is normal for the EC to take several seconds to react to fan -commands. - -The fan may be enabled or disabled with the following commands: - - echo enable >/proc/acpi/ibm/fan - echo disable >/proc/acpi/ibm/fan - -Placing a fan on level 0 is the same as disabling it. Enabling a fan -will try to place it in a safe level if it is too slow or disabled. - -WARNING WARNING WARNING: do not leave the fan disabled unless you are -monitoring all of the temperature sensor readings and you are ready to -enable it if necessary to avoid overheating. - -An enabled fan in level "auto" may stop spinning if the EC decides the -ThinkPad is cool enough and doesn't need the extra airflow. This is -normal, and the EC will spin the fan up if the varios thermal readings -rise too much. - -On the X40, this seems to depend on the CPU and HDD temperatures. -Specifically, the fan is turned on when either the CPU temperature -climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The -fan is turned off when the CPU temperature drops to 49 degrees and the -HDD temperature drops to 41 degrees. These thresholds cannot -currently be controlled. - -The fan level can be controlled with the command: - - echo 'level ' > /proc/acpi/ibm/thermal - -Where is an integer from 0 to 7, or one of the words "auto" -or "disengaged" (without the quotes). Not all ThinkPads support the -"auto" and "disengaged" levels. - -On the X31 and X40 (and ONLY on those models), the fan speed can be -controlled to a certain degree. Once the fan is running, it can be -forced to run faster or slower with the following command: - - echo 'speed ' > /proc/acpi/ibm/thermal - -The sustainable range of fan speeds on the X40 appears to be from -about 3700 to about 7350. Values outside this range either do not have -any effect or the fan speed eventually settles somewhere in that -range. The fan cannot be stopped or started with this command. - -The ThinkPad's ACPI DSDT code will reprogram the fan on its own when -certain conditions are met. It will override any fan programming done -through ibm-acpi. - -EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan ---------------------------------------- - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. - -This feature shows the presence and current state of a WAN (Sierra -Wireless EV-DO) device. If WAN is installed, the following commands can -be used: - - echo enable > /proc/acpi/ibm/wan - echo disable > /proc/acpi/ibm/wan - -It was tested on a Lenovo Thinkpad X60. It should probably work on other -Thinkpad models which come with this module installed. - -Multiple Commands, Module Parameters ------------------------------------- - -Multiple commands can be written to the proc files in one shot by -separating them with commas, for example: - - echo enable,0xffff > /proc/acpi/ibm/hotkey - echo lcd_disable,crt_enable > /proc/acpi/ibm/video - -Commands can also be specified when loading the ibm_acpi module, for -example: - - modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable - -The ibm-acpi kernel driver can be programmed to revert the fan level -to a safe setting if userspace does not issue one of the fan commands: -"enable", "disable", "level" or "watchdog" within a configurable -ammount of time. To do this, use the "watchdog" command. - - echo 'watchdog ' > /proc/acpi/ibm/fan - -Interval is the ammount of time in seconds to wait for one of the -above mentioned fan commands before reseting the fan level to a safe -one. If set to zero, the watchdog is disabled (default). When the -watchdog timer runs out, it does the exact equivalent of the "enable" -fan command. - -Note that the watchdog timer stops after it enables the fan. It will -be rearmed again automatically (using the same interval) when one of -the above mentioned fan commands is received. The fan watchdog is, -therefore, not suitable to protect against fan mode changes made -through means other than the "enable", "disable", and "level" fan -commands. - - -Example Configuration ---------------------- - -The ACPI support in the kernel is intended to be used in conjunction -with a user-space daemon, acpid. The configuration files for this -daemon control what actions are taken in response to various ACPI -events. An example set of configuration files are included in the -config/ directory of the tarball package available on the web -site. Note that these are provided for illustration purposes only and -may need to be adapted to your particular setup. - -The following utility scripts are used by the example action -scripts (included with ibm-acpi for completeness): - - /usr/local/sbin/idectl -- from the hdparm source distribution, - see http://www.ibiblio.org/pub/Linux/system/hardware - /usr/local/sbin/laptop_mode -- from the Linux kernel source - distribution, see Documentation/laptop-mode.txt - /sbin/service -- comes with Redhat/Fedora distributions - /usr/sbin/hibernate -- from the Software Suspend 2 distribution, - see http://softwaresuspend.berlios.de/ - -Toan T Nguyen notes that Suse uses the -powersave program to suspend ('powersave --suspend-to-ram') or -hibernate ('powersave --suspend-to-disk'). This means that the -hibernate script is not needed on that distribution. - -Henrik Brix Andersen has written a Gentoo ACPI event -handler script for the X31. You can get the latest version from -http://dev.gentoo.org/~brix/files/x31.sh - -David Schweikert has written an alternative blank.sh -script which works on Debian systems. This scripts has now been -extended to also work on Fedora systems and included as the default -blank.sh in the distribution. diff --git a/Documentation/infiniband/user_mad.txt b/Documentation/infiniband/user_mad.txt index 750fe5e..8ec54b9 100644 --- a/Documentation/infiniband/user_mad.txt +++ b/Documentation/infiniband/user_mad.txt @@ -91,6 +91,14 @@ Sending MADs if (ret != sizeof *mad + mad_length) perror("write"); +Transaction IDs + + Users of the umad devices can use the lower 32 bits of the + transaction ID field (that is, the least significant half of the + field in network byte order) in MADs being sent to match + request/response pairs. The upper 32 bits are reserved for use by + the kernel and will be overwritten before a MAD is sent. + Setting IsSM Capability Bit To set the IsSM capability bit for a port, simply open the diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 12533a9..84c3bd0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -181,19 +181,41 @@ and is between 256 and 4096 characters. that require a timer override, but don't have HPET - acpi_dbg_layer= [HW,ACPI] + acpi.debug_layer= [HW,ACPI] Format: Each bit of the indicates an ACPI debug layer, 1: enable, 0: disable. It is useful for boot time debugging. After system has booted up, it can be set - via /proc/acpi/debug_layer. - - acpi_dbg_level= [HW,ACPI] + via /sys/module/acpi/parameters/debug_layer. + CONFIG_ACPI_DEBUG must be enabled for this to produce any output. + Available bits (add the numbers together) to enable debug output + for specific parts of the ACPI subsystem: + 0x01 utilities 0x02 hardware 0x04 events 0x08 tables + 0x10 namespace 0x20 parser 0x40 dispatcher + 0x80 executer 0x100 resources 0x200 acpica debugger + 0x400 os services 0x800 acpica disassembler. + The number can be in decimal or prefixed with 0x in hex. + Warning: Many of these options can produce a lot of + output and make your system unusable. Be very careful. + + acpi.debug_level= [HW,ACPI] Format: Each bit of the indicates an ACPI debug level, 1: enable, 0: disable. It is useful for boot time debugging. After system has booted up, it can be set - via /proc/acpi/debug_level. + via /sys/module/acpi/parameters/debug_level. + CONFIG_ACPI_DEBUG must be enabled for this to produce any output. + Available bits (add the numbers together) to enable different + debug output levels of the ACPI subsystem: + 0x01 error 0x02 warn 0x04 init 0x08 debug object + 0x10 info 0x20 init names 0x40 parse 0x80 load + 0x100 dispatch 0x200 execute 0x400 names 0x800 operation region + 0x1000 bfield 0x2000 tables 0x4000 values 0x8000 objects + 0x10000 resources 0x20000 user requests 0x40000 package. + The number can be in decimal or prefixed with 0x in hex. + Warning: Many of these options can produce a lot of + output and make your system unusable. Be very careful. + acpi_fake_ecdt [HW,ACPI] Workaround failure due to BIOS lacking ECDT @@ -1792,7 +1814,7 @@ and is between 256 and 4096 characters. for newly-detected USB devices (default 2). This is the time required before an idle device will be autosuspended. Devices for which the delay is set - to 0 won't be autosuspended at all. + to a negative value won't be autosuspended at all. usbhid.mousepoll= [USBHID] The interval which mice are to be polled at. diff --git a/Documentation/keys.txt b/Documentation/keys.txt index 60c665d..81d9aa0 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -859,6 +859,18 @@ (*) To unregister a key type, call: void unregister_key_type(struct key_type *type); +Under some circumstances, it may be desirable to desirable to deal with a +bundle of keys. The facility provides access to the keyring type for managing +such a bundle: + + struct key_type key_type_keyring; + +This can be used with a function such as request_key() to find a specific +keyring in a process's keyrings. A keyring thus found can then be searched +with keyring_search(). Note that it is not possible to use request_key() to +search a specific keyring, so using keyrings in this way is of limited utility. + + =================================== NOTES ON ACCESSING PAYLOAD CONTENTS =================================== diff --git a/Documentation/networking/bcm43xx.txt b/Documentation/networking/bcm43xx.txt index 28541d2..a136721 100644 --- a/Documentation/networking/bcm43xx.txt +++ b/Documentation/networking/bcm43xx.txt @@ -2,35 +2,88 @@ BCM43xx Linux Driver Project ============================ -About this software -------------------- +Introduction +------------ -The goal of this project is to develop a linux driver for Broadcom -BCM43xx chips, based on the specification at -http://bcm-specs.sipsolutions.net/ +Many of the wireless devices found in modern notebook computers are +based on the wireless chips produced by Broadcom. These devices have +been a problem for Linux users as there is no open-source driver +available. In addition, Broadcom has not released specifications +for the device, and driver availability has been limited to the +binary-only form used in the GPL versions of AP hardware such as the +Linksys WRT54G, and the Windows and OS X drivers. Before this project +began, the only way to use these devices were to use the Windows or +OS X drivers with either the Linuxant or ndiswrapper modules. There +is a strong penalty if this method is used as loading the binary-only +module "taints" the kernel, and no kernel developer will help diagnose +any kernel problems. -The project page is http://bcm43xx.berlios.de/ +Development +----------- +This driver has been developed using +a clean-room technique that is described at +http://bcm-specs.sipsolutions.net/ReverseEngineeringProcess. For legal +reasons, none of the clean-room crew works on the on the Linux driver, +and none of the Linux developers sees anything but the specifications, +which are the ultimate product of the reverse-engineering group. -Requirements ------------- +Software +-------- + +Since the release of the 2.6.17 kernel, the bcm43xx driver has been +distributed with the kernel source, and is prebuilt in most, if not +all, distributions. There is, however, additional software that is +required. The firmware used by the chip is the intellectual property +of Broadcom and they have not given the bcm43xx team redistribution +rights to this firmware. Since we cannot legally redistribute +the firwmare we cannot include it with the driver. Furthermore, it +cannot be placed in the downloadable archives of any distributing +organization; therefore, the user is responsible for obtaining the +firmware and placing it in the appropriate location so that the driver +can find it when initializing. + +To help with this process, the bcm43xx developers provide a separate +program named bcm43xx-fwcutter to "cut" the firmware out of a +Windows or OS X driver and write the extracted files to the proper +location. This program is usually provided with the distribution; +however, it may be downloaded from + +http://developer.berlios.de/project/showfiles.php?group_id=4547 -1) Linux Kernel 2.6.16 or later - http://www.kernel.org/ +The firmware is available in two versions. V3 firmware is used with +the in-kernel bcm43xx driver that uses a software MAC layer called +SoftMAC, and will have a microcode revision of 0x127 or smaller. The +V4 firmware is used by an out-of-kernel driver employing a variation of +the Devicescape MAC layer known as d80211. Once bcm43xx-d80211 reaches +a satisfactory level of development, it will replace bcm43xx-softmac +in the kernel as it is much more flexible and powerful. - You may want to configure your kernel with: +A source for the latest V3 firmware is - CONFIG_DEBUG_FS (optional): - -> Kernel hacking - -> Debug Filesystem +http://downloads.openwrt.org/sources/wl_apsta-3.130.20.0.o -2) SoftMAC IEEE 802.11 Networking Stack extension and patched ieee80211 - modules: - http://softmac.sipsolutions.net/ +Once this file is downloaded, the command +'bcm43xx-fwcutter -w ' +will extract the microcode and write it to directory +. The correct directory will depend on your distribution; +however, most use '/lib/firmware'. Once this step is completed, +the bcm3xx driver should load when the system is booted. To see +any messages relating to the driver, issue the command 'dmesg | +grep bcm43xx' from a terminal window. If there are any problems, +please send that output to Bcm43xx-dev@lists.berlios.de. -3) Firmware Files +Although the driver has been in-kernel since 2.6.17, the earliest +version is quite limited in its capability. Patches that include +all features of later versions are available for the stable kernel +versions from 2.6.18. These will be needed if you use a BCM4318, +or a PCI Express version (BCM4311 and BCM4312). In addition, if you +have an early BCM4306 and more than 1 GB RAM, your kernel will need +to be patched. These patches, which are being updated regularly, +are available at ftp://lwfinger.dynalias.org/patches. Look for +combined_2.6.YY.patch. Of course you will need kernel source downloaded +from kernel.org, or the source from your distribution. - Please try fwcutter. Fwcutter can extract the firmware from various - binary driver files. It supports driver files from Windows, MacOS and - Linux. You can get fwcutter from http://bcm43xx.berlios.de/. - Also, fwcutter comes with a README file for further instructions. +If you build your own kernel, please enable CONFIG_BCM43XX_DEBUG +and CONFIG_IEEE80211_SOFTMAC_DEBUG. The log information provided is +essential for solving any problems. diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index de809e5..1da5666 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -920,40 +920,9 @@ options, you may wish to use the "max_bo documented above. To create multiple bonding devices with differing options, it -is necessary to load the bonding driver multiple times. Note that -current versions of the sysconfig network initialization scripts -handle this automatically; if your distro uses these scripts, no -special action is needed. See the section Configuring Bonding -Devices, above, if you're not sure about your network initialization -scripts. - - To load multiple instances of the module, it is necessary to -specify a different name for each instance (the module loading system -requires that every loaded module, even multiple instances of the same -module, have a unique name). This is accomplished by supplying -multiple sets of bonding options in /etc/modprobe.conf, for example: - -alias bond0 bonding -options bond0 -o bond0 mode=balance-rr miimon=100 - -alias bond1 bonding -options bond1 -o bond1 mode=balance-alb miimon=50 - - will load the bonding module two times. The first instance is -named "bond0" and creates the bond0 device in balance-rr mode with an -miimon of 100. The second instance is named "bond1" and creates the -bond1 device in balance-alb mode with an miimon of 50. - - In some circumstances (typically with older distributions), -the above does not work, and the second bonding instance never sees -its options. In that case, the second options line can be substituted -as follows: - -install bond1 /sbin/modprobe --ignore-install bonding -o bond1 \ - mode=balance-alb miimon=50 +is necessary to use bonding parameters exported by sysfs, documented +in the section below. - This may be repeated any number of times, specifying a new and -unique name in place of bond1 for each subsequent instance. 3.4 Configuring Bonding Manually via Sysfs ------------------------------------------ diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 387482e..4504cc5 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -57,6 +57,16 @@ DCCP_SOCKOPT_SEND_CSCOV is for the recei coverage value are also acceptable. The higher the number, the more restrictive this setting (see [RFC 4340, sec. 9.2.1]). +The following two options apply to CCID 3 exclusively and are getsockopt()-only. +In either case, a TFRC info struct (defined in ) is returned. +DCCP_SOCKOPT_CCID_RX_INFO + Returns a `struct tfrc_rx_info' in optval; the buffer for optval and + optlen must be set to at least sizeof(struct tfrc_rx_info). +DCCP_SOCKOPT_CCID_TX_INFO + Returns a `struct tfrc_tx_info' in optval; the buffer for optval and + optlen must be set to at least sizeof(struct tfrc_tx_info). + + Sysctl variables ================ Several DCCP default parameters can be managed by the following sysctls diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 702d1d8..af6a63a 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -179,11 +179,31 @@ tcp_fin_timeout - INTEGER because they eat maximum 1.5K of memory, but they tend to live longer. Cf. tcp_max_orphans. -tcp_frto - BOOLEAN +tcp_frto - INTEGER Enables F-RTO, an enhanced recovery algorithm for TCP retransmission timeouts. It is particularly beneficial in wireless environments where packet loss is typically due to random radio interference - rather than intermediate router congestion. + rather than intermediate router congestion. If set to 1, basic + version is enabled. 2 enables SACK enhanced F-RTO, which is + EXPERIMENTAL. The basic version can be used also when SACK is + enabled for a flow through tcp_sack sysctl. + +tcp_frto_response - INTEGER + When F-RTO has detected that a TCP retransmission timeout was + spurious (i.e, the timeout would have been avoided had TCP set a + longer retransmission timeout), TCP has several options what to do + next. Possible values are: + 0 Rate halving based; a smooth and conservative response, + results in halved cwnd and ssthresh after one RTT + 1 Very conservative response; not recommended because even + though being valid, it interacts poorly with the rest of + Linux TCP, halves cwnd and ssthresh immediately + 2 Aggressive response; undoes congestion control measures + that are now known to be unnecessary (ignoring the + possibility of a lost retransmission that would require + TCP to be more cautious), cwnd and ssthresh are restored + to the values prior timeout + Default: 0 (rate halving based) tcp_keepalive_time - INTEGER How often TCP sends out keepalive messages when keepalive is enabled. @@ -995,7 +1015,12 @@ bridge-nf-call-ip6tables - BOOLEAN Default: 1 bridge-nf-filter-vlan-tagged - BOOLEAN - 1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables. + 1 : pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables. + 0 : disable this. + Default: 1 + +bridge-nf-filter-pppoe-tagged - BOOLEAN + 1 : pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables. 0 : disable this. Default: 1 diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt new file mode 100644 index 0000000..cae231b --- /dev/null +++ b/Documentation/networking/rxrpc.txt @@ -0,0 +1,859 @@ + ====================== + RxRPC NETWORK PROTOCOL + ====================== + +The RxRPC protocol driver provides a reliable two-phase transport on top of UDP +that can be used to perform RxRPC remote operations. This is done over sockets +of AF_RXRPC family, using sendmsg() and recvmsg() with control data to send and +receive data, aborts and errors. + +Contents of this document: + + (*) Overview. + + (*) RxRPC protocol summary. + + (*) AF_RXRPC driver model. + + (*) Control messages. + + (*) Socket options. + + (*) Security. + + (*) Example client usage. + + (*) Example server usage. + + (*) AF_RXRPC kernel interface. + + +======== +OVERVIEW +======== + +RxRPC is a two-layer protocol. There is a session layer which provides +reliable virtual connections using UDP over IPv4 (or IPv6) as the transport +layer, but implements a real network protocol; and there's the presentation +layer which renders structured data to binary blobs and back again using XDR +(as does SunRPC): + + +-------------+ + | Application | + +-------------+ + | XDR | Presentation + +-------------+ + | RxRPC | Session + +-------------+ + | UDP | Transport + +-------------+ + + +AF_RXRPC provides: + + (1) Part of an RxRPC facility for both kernel and userspace applications by + making the session part of it a Linux network protocol (AF_RXRPC). + + (2) A two-phase protocol. The client transmits a blob (the request) and then + receives a blob (the reply), and the server receives the request and then + transmits the reply. + + (3) Retention of the reusable bits of the transport system set up for one call + to speed up subsequent calls. + + (4) A secure protocol, using the Linux kernel's key retention facility to + manage security on the client end. The server end must of necessity be + more active in security negotiations. + +AF_RXRPC does not provide XDR marshalling/presentation facilities. That is +left to the application. AF_RXRPC only deals in blobs. Even the operation ID +is just the first four bytes of the request blob, and as such is beyond the +kernel's interest. + + +Sockets of AF_RXRPC family are: + + (1) created as type SOCK_DGRAM; + + (2) provided with a protocol of the type of underlying transport they're going + to use - currently only PF_INET is supported. + + +The Andrew File System (AFS) is an example of an application that uses this and +that has both kernel (filesystem) and userspace (utility) components. + + +====================== +RXRPC PROTOCOL SUMMARY +====================== + +An overview of the RxRPC protocol: + + (*) RxRPC sits on top of another networking protocol (UDP is the only option + currently), and uses this to provide network transport. UDP ports, for + example, provide transport endpoints. + + (*) RxRPC supports multiple virtual "connections" from any given transport + endpoint, thus allowing the endpoints to be shared, even to the same + remote endpoint. + + (*) Each connection goes to a particular "service". A connection may not go + to multiple services. A service may be considered the RxRPC equivalent of + a port number. AF_RXRPC permits multiple services to share an endpoint. + + (*) Client-originating packets are marked, thus a transport endpoint can be + shared between client and server connections (connections have a + direction). + + (*) Up to a billion connections may be supported concurrently between one + local transport endpoint and one service on one remote endpoint. An RxRPC + connection is described by seven numbers: + + Local address } + Local port } Transport (UDP) address + Remote address } + Remote port } + Direction + Connection ID + Service ID + + (*) Each RxRPC operation is a "call". A connection may make up to four + billion calls, but only up to four calls may be in progress on a + connection at any one time. + + (*) Calls are two-phase and asymmetric: the client sends its request data, + which the service receives; then the service transmits the reply data + which the client receives. + + (*) The data blobs are of indefinite size, the end of a phase is marked with a + flag in the packet. The number of packets of data making up one blob may + not exceed 4 billion, however, as this would cause the sequence number to + wrap. + + (*) The first four bytes of the request data are the service operation ID. + + (*) Security is negotiated on a per-connection basis. The connection is + initiated by the first data packet on it arriving. If security is + requested, the server then issues a "challenge" and then the client + replies with a "response". If the response is successful, the security is + set for the lifetime of that connection, and all subsequent calls made + upon it use that same security. In the event that the server lets a + connection lapse before the client, the security will be renegotiated if + the client uses the connection again. + + (*) Calls use ACK packets to handle reliability. Data packets are also + explicitly sequenced per call. + + (*) There are two types of positive acknowledgement: hard-ACKs and soft-ACKs. + A hard-ACK indicates to the far side that all the data received to a point + has been received and processed; a soft-ACK indicates that the data has + been received but may yet be discarded and re-requested. The sender may + not discard any transmittable packets until they've been hard-ACK'd. + + (*) Reception of a reply data packet implicitly hard-ACK's all the data + packets that make up the request. + + (*) An call is complete when the request has been sent, the reply has been + received and the final hard-ACK on the last packet of the reply has + reached the server. + + (*) An call may be aborted by either end at any time up to its completion. + + +===================== +AF_RXRPC DRIVER MODEL +===================== + +About the AF_RXRPC driver: + + (*) The AF_RXRPC protocol transparently uses internal sockets of the transport + protocol to represent transport endpoints. + + (*) AF_RXRPC sockets map onto RxRPC connection bundles. Actual RxRPC + connections are handled transparently. One client socket may be used to + make multiple simultaneous calls to the same service. One server socket + may handle calls from many clients. + + (*) Additional parallel client connections will be initiated to support extra + concurrent calls, up to a tunable limit. + + (*) Each connection is retained for a certain amount of time [tunable] after + the last call currently using it has completed in case a new call is made + that could reuse it. + + (*) Each internal UDP socket is retained [tunable] for a certain amount of + time [tunable] after the last connection using it discarded, in case a new + connection is made that could use it. + + (*) A client-side connection is only shared between calls if they have have + the same key struct describing their security (and assuming the calls + would otherwise share the connection). Non-secured calls would also be + able to share connections with each other. + + (*) A server-side connection is shared if the client says it is. + + (*) ACK'ing is handled by the protocol driver automatically, including ping + replying. + + (*) SO_KEEPALIVE automatically pings the other side to keep the connection + alive [TODO]. + + (*) If an ICMP error is received, all calls affected by that error will be + aborted with an appropriate network error passed through recvmsg(). + + +Interaction with the user of the RxRPC socket: + + (*) A socket is made into a server socket by binding an address with a + non-zero service ID. + + (*) In the client, sending a request is achieved with one or more sendmsgs, + followed by the reply being received with one or more recvmsgs. + + (*) The first sendmsg for a request to be sent from a client contains a tag to + be used in all other sendmsgs or recvmsgs associated with that call. The + tag is carried in the control data. + + (*) connect() is used to supply a default destination address for a client + socket. This may be overridden by supplying an alternate address to the + first sendmsg() of a call (struct msghdr::msg_name). + + (*) If connect() is called on an unbound client, a random local port will + bound before the operation takes place. + + (*) A server socket may also be used to make client calls. To do this, the + first sendmsg() of the call must specify the target address. The server's + transport endpoint is used to send the packets. + + (*) Once the application has received the last message associated with a call, + the tag is guaranteed not to be seen again, and so it can be used to pin + client resources. A new call can then be initiated with the same tag + without fear of interference. + + (*) In the server, a request is received with one or more recvmsgs, then the + the reply is transmitted with one or more sendmsgs, and then the final ACK + is received with a last recvmsg. + + (*) When sending data for a call, sendmsg is given MSG_MORE if there's more + data to come on that call. + + (*) When receiving data for a call, recvmsg flags MSG_MORE if there's more + data to come for that call. + + (*) When receiving data or messages for a call, MSG_EOR is flagged by recvmsg + to indicate the terminal message for that call. + + (*) A call may be aborted by adding an abort control message to the control + data. Issuing an abort terminates the kernel's use of that call's tag. + Any messages waiting in the receive queue for that call will be discarded. + + (*) Aborts, busy notifications and challenge packets are delivered by recvmsg, + and control data messages will be set to indicate the context. Receiving + an abort or a busy message terminates the kernel's use of that call's tag. + + (*) The control data part of the msghdr struct is used for a number of things: + + (*) The tag of the intended or affected call. + + (*) Sending or receiving errors, aborts and busy notifications. + + (*) Notifications of incoming calls. + + (*) Sending debug requests and receiving debug replies [TODO]. + + (*) When the kernel has received and set up an incoming call, it sends a + message to server application to let it know there's a new call awaiting + its acceptance [recvmsg reports a special control message]. The server + application then uses sendmsg to assign a tag to the new call. Once that + is done, the first part of the request data will be delivered by recvmsg. + + (*) The server application has to provide the server socket with a keyring of + secret keys corresponding to the security types it permits. When a secure + connection is being set up, the kernel looks up the appropriate secret key + in the keyring and then sends a challenge packet to the client and + receives a response packet. The kernel then checks the authorisation of + the packet and either aborts the connection or sets up the security. + + (*) The name of the key a client will use to secure its communications is + nominated by a socket option. + + +Notes on recvmsg: + + (*) If there's a sequence of data messages belonging to a particular call on + the receive queue, then recvmsg will keep working through them until: + + (a) it meets the end of that call's received data, + + (b) it meets a non-data message, + + (c) it meets a message belonging to a different call, or + + (d) it fills the user buffer. + + If recvmsg is called in blocking mode, it will keep sleeping, awaiting the + reception of further data, until one of the above four conditions is met. + + (2) MSG_PEEK operates similarly, but will return immediately if it has put any + data in the buffer rather than sleeping until it can fill the buffer. + + (3) If a data message is only partially consumed in filling a user buffer, + then the remainder of that message will be left on the front of the queue + for the next taker. MSG_TRUNC will never be flagged. + + (4) If there is more data to be had on a call (it hasn't copied the last byte + of the last data message in that phase yet), then MSG_MORE will be + flagged. + + +================ +CONTROL MESSAGES +================ + +AF_RXRPC makes use of control messages in sendmsg() and recvmsg() to multiplex +calls, to invoke certain actions and to report certain conditions. These are: + + MESSAGE ID SRT DATA MEANING + ======================= === =========== =============================== + RXRPC_USER_CALL_ID sr- User ID App's call specifier + RXRPC_ABORT srt Abort code Abort code to issue/received + RXRPC_ACK -rt n/a Final ACK received + RXRPC_NET_ERROR -rt error num Network error on call + RXRPC_BUSY -rt n/a Call rejected (server busy) + RXRPC_LOCAL_ERROR -rt error num Local error encountered + RXRPC_NEW_CALL -r- n/a New call received + RXRPC_ACCEPT s-- n/a Accept new call + + (SRT = usable in Sendmsg / delivered by Recvmsg / Terminal message) + + (*) RXRPC_USER_CALL_ID + + This is used to indicate the application's call ID. It's an unsigned long + that the app specifies in the client by attaching it to the first data + message or in the server by passing it in association with an RXRPC_ACCEPT + message. recvmsg() passes it in conjunction with all messages except + those of the RXRPC_NEW_CALL message. + + (*) RXRPC_ABORT + + This is can be used by an application to abort a call by passing it to + sendmsg, or it can be delivered by recvmsg to indicate a remote abort was + received. Either way, it must be associated with an RXRPC_USER_CALL_ID to + specify the call affected. If an abort is being sent, then error EBADSLT + will be returned if there is no call with that user ID. + + (*) RXRPC_ACK + + This is delivered to a server application to indicate that the final ACK + of a call was received from the client. It will be associated with an + RXRPC_USER_CALL_ID to indicate the call that's now complete. + + (*) RXRPC_NET_ERROR + + This is delivered to an application to indicate that an ICMP error message + was encountered in the process of trying to talk to the peer. An + errno-class integer value will be included in the control message data + indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call + affected. + + (*) RXRPC_BUSY + + This is delivered to a client application to indicate that a call was + rejected by the server due to the server being busy. It will be + associated with an RXRPC_USER_CALL_ID to indicate the rejected call. + + (*) RXRPC_LOCAL_ERROR + + This is delivered to an application to indicate that a local error was + encountered and that a call has been aborted because of it. An + errno-class integer value will be included in the control message data + indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call + affected. + + (*) RXRPC_NEW_CALL + + This is delivered to indicate to a server application that a new call has + arrived and is awaiting acceptance. No user ID is associated with this, + as a user ID must subsequently be assigned by doing an RXRPC_ACCEPT. + + (*) RXRPC_ACCEPT + + This is used by a server application to attempt to accept a call and + assign it a user ID. It should be associated with an RXRPC_USER_CALL_ID + to indicate the user ID to be assigned. If there is no call to be + accepted (it may have timed out, been aborted, etc.), then sendmsg will + return error ENODATA. If the user ID is already in use by another call, + then error EBADSLT will be returned. + + +============== +SOCKET OPTIONS +============== + +AF_RXRPC sockets support a few socket options at the SOL_RXRPC level: + + (*) RXRPC_SECURITY_KEY + + This is used to specify the description of the key to be used. The key is + extracted from the calling process's keyrings with request_key() and + should be of "rxrpc" type. + + The optval pointer points to the description string, and optlen indicates + how long the string is, without the NUL terminator. + + (*) RXRPC_SECURITY_KEYRING + + Similar to above but specifies a keyring of server secret keys to use (key + type "keyring"). See the "Security" section. + + (*) RXRPC_EXCLUSIVE_CONNECTION + + This is used to request that new connections should be used for each call + made subsequently on this socket. optval should be NULL and optlen 0. + + (*) RXRPC_MIN_SECURITY_LEVEL + + This is used to specify the minimum security level required for calls on + this socket. optval must point to an int containing one of the following + values: + + (a) RXRPC_SECURITY_PLAIN + + Encrypted checksum only. + + (b) RXRPC_SECURITY_AUTH + + Encrypted checksum plus packet padded and first eight bytes of packet + encrypted - which includes the actual packet length. + + (c) RXRPC_SECURITY_ENCRYPTED + + Encrypted checksum plus entire packet padded and encrypted, including + actual packet length. + + +======== +SECURITY +======== + +Currently, only the kerberos 4 equivalent protocol has been implemented +(security index 2 - rxkad). This requires the rxkad module to be loaded and, +on the client, tickets of the appropriate type to be obtained from the AFS +kaserver or the kerberos server and installed as "rxrpc" type keys. This is +normally done using the klog program. An example simple klog program can be +found at: + + http://people.redhat.com/~dhowells/rxrpc/klog.c + +The payload provided to add_key() on the client should be of the following +form: + + struct rxrpc_key_sec2_v1 { + uint16_t security_index; /* 2 */ + uint16_t ticket_length; /* length of ticket[] */ + uint32_t expiry; /* time at which expires */ + uint8_t kvno; /* key version number */ + uint8_t __pad[3]; + uint8_t session_key[8]; /* DES session key */ + uint8_t ticket[0]; /* the encrypted ticket */ + }; + +Where the ticket blob is just appended to the above structure. + + +For the server, keys of type "rxrpc_s" must be made available to the server. +They have a description of ":" (eg: "52:2" for an +rxkad key for the AFS VL service). When such a key is created, it should be +given the server's secret key as the instantiation data (see the example +below). + + add_key("rxrpc_s", "52:2", secret_key, 8, keyring); + +A keyring is passed to the server socket by naming it in a sockopt. The server +socket then looks the server secret keys up in this keyring when secure +incoming connections are made. This can be seen in an example program that can +be found at: + + http://people.redhat.com/~dhowells/rxrpc/listen.c + + +==================== +EXAMPLE CLIENT USAGE +==================== + +A client would issue an operation by: + + (1) An RxRPC socket is set up by: + + client = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); + + Where the third parameter indicates the protocol family of the transport + socket used - usually IPv4 but it can also be IPv6 [TODO]. + + (2) A local address can optionally be bound: + + struct sockaddr_rxrpc srx = { + .srx_family = AF_RXRPC, + .srx_service = 0, /* we're a client */ + .transport_type = SOCK_DGRAM, /* type of transport socket */ + .transport.sin_family = AF_INET, + .transport.sin_port = htons(7000), /* AFS callback */ + .transport.sin_address = 0, /* all local interfaces */ + }; + bind(client, &srx, sizeof(srx)); + + This specifies the local UDP port to be used. If not given, a random + non-privileged port will be used. A UDP port may be shared between + several unrelated RxRPC sockets. Security is handled on a basis of + per-RxRPC virtual connection. + + (3) The security is set: + + const char *key = "AFS:cambridge.redhat.com"; + setsockopt(client, SOL_RXRPC, RXRPC_SECURITY_KEY, key, strlen(key)); + + This issues a request_key() to get the key representing the security + context. The minimum security level can be set: + + unsigned int sec = RXRPC_SECURITY_ENCRYPTED; + setsockopt(client, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL, + &sec, sizeof(sec)); + + (4) The server to be contacted can then be specified (alternatively this can + be done through sendmsg): + + struct sockaddr_rxrpc srx = { + .srx_family = AF_RXRPC, + .srx_service = VL_SERVICE_ID, + .transport_type = SOCK_DGRAM, /* type of transport socket */ + .transport.sin_family = AF_INET, + .transport.sin_port = htons(7005), /* AFS volume manager */ + .transport.sin_address = ..., + }; + connect(client, &srx, sizeof(srx)); + + (5) The request data should then be posted to the server socket using a series + of sendmsg() calls, each with the following control message attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + MSG_MORE should be set in msghdr::msg_flags on all but the last part of + the request. Multiple requests may be made simultaneously. + + If a call is intended to go to a destination other then the default + specified through connect(), then msghdr::msg_name should be set on the + first request message of that call. + + (6) The reply data will then be posted to the server socket for recvmsg() to + pick up. MSG_MORE will be flagged by recvmsg() if there's more reply data + for a particular call to be read. MSG_EOR will be set on the terminal + read for a call. + + All data will be delivered with the following control message attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + If an abort or error occurred, this will be returned in the control data + buffer instead, and MSG_EOR will be flagged to indicate the end of that + call. + + +==================== +EXAMPLE SERVER USAGE +==================== + +A server would be set up to accept operations in the following manner: + + (1) An RxRPC socket is created by: + + server = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); + + Where the third parameter indicates the address type of the transport + socket used - usually IPv4. + + (2) Security is set up if desired by giving the socket a keyring with server + secret keys in it: + + keyring = add_key("keyring", "AFSkeys", NULL, 0, + KEY_SPEC_PROCESS_KEYRING); + + const char secret_key[8] = { + 0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 }; + add_key("rxrpc_s", "52:2", secret_key, 8, keyring); + + setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING, "AFSkeys", 7); + + The keyring can be manipulated after it has been given to the socket. This + permits the server to add more keys, replace keys, etc. whilst it is live. + + (2) A local address must then be bound: + + struct sockaddr_rxrpc srx = { + .srx_family = AF_RXRPC, + .srx_service = VL_SERVICE_ID, /* RxRPC service ID */ + .transport_type = SOCK_DGRAM, /* type of transport socket */ + .transport.sin_family = AF_INET, + .transport.sin_port = htons(7000), /* AFS callback */ + .transport.sin_address = 0, /* all local interfaces */ + }; + bind(server, &srx, sizeof(srx)); + + (3) The server is then set to listen out for incoming calls: + + listen(server, 100); + + (4) The kernel notifies the server of pending incoming connections by sending + it a message for each. This is received with recvmsg() on the server + socket. It has no data, and has a single dataless control message + attached: + + RXRPC_NEW_CALL + + The address that can be passed back by recvmsg() at this point should be + ignored since the call for which the message was posted may have gone by + the time it is accepted - in which case the first call still on the queue + will be accepted. + + (5) The server then accepts the new call by issuing a sendmsg() with two + pieces of control data and no actual data: + + RXRPC_ACCEPT - indicate connection acceptance + RXRPC_USER_CALL_ID - specify user ID for this call + + (6) The first request data packet will then be posted to the server socket for + recvmsg() to pick up. At that point, the RxRPC address for the call can + be read from the address fields in the msghdr struct. + + Subsequent request data will be posted to the server socket for recvmsg() + to collect as it arrives. All but the last piece of the request data will + be delivered with MSG_MORE flagged. + + All data will be delivered with the following control message attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + (8) The reply data should then be posted to the server socket using a series + of sendmsg() calls, each with the following control messages attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + MSG_MORE should be set in msghdr::msg_flags on all but the last message + for a particular call. + + (9) The final ACK from the client will be posted for retrieval by recvmsg() + when it is received. It will take the form of a dataless message with two + control messages attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + RXRPC_ACK - indicates final ACK (no data) + + MSG_EOR will be flagged to indicate that this is the final message for + this call. + +(10) Up to the point the final packet of reply data is sent, the call can be + aborted by calling sendmsg() with a dataless message with the following + control messages attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + RXRPC_ABORT - indicates abort code (4 byte data) + + Any packets waiting in the socket's receive queue will be discarded if + this is issued. + +Note that all the communications for a particular service take place through +the one server socket, using control messages on sendmsg() and recvmsg() to +determine the call affected. + + +========================= +AF_RXRPC KERNEL INTERFACE +========================= + +The AF_RXRPC module also provides an interface for use by in-kernel utilities +such as the AFS filesystem. This permits such a utility to: + + (1) Use different keys directly on individual client calls on one socket + rather than having to open a whole slew of sockets, one for each key it + might want to use. + + (2) Avoid having RxRPC call request_key() at the point of issue of a call or + opening of a socket. Instead the utility is responsible for requesting a + key at the appropriate point. AFS, for instance, would do this during VFS + operations such as open() or unlink(). The key is then handed through + when the call is initiated. + + (3) Request the use of something other than GFP_KERNEL to allocate memory. + + (4) Avoid the overhead of using the recvmsg() call. RxRPC messages can be + intercepted before they get put into the socket Rx queue and the socket + buffers manipulated directly. + +To use the RxRPC facility, a kernel utility must still open an AF_RXRPC socket, +bind an addess as appropriate and listen if it's to be a server socket, but +then it passes this to the kernel interface functions. + +The kernel interface functions are as follows: + + (*) Begin a new client call. + + struct rxrpc_call * + rxrpc_kernel_begin_call(struct socket *sock, + struct sockaddr_rxrpc *srx, + struct key *key, + unsigned long user_call_ID, + gfp_t gfp); + + This allocates the infrastructure to make a new RxRPC call and assigns + call and connection numbers. The call will be made on the UDP port that + the socket is bound to. The call will go to the destination address of a + connected client socket unless an alternative is supplied (srx is + non-NULL). + + If a key is supplied then this will be used to secure the call instead of + the key bound to the socket with the RXRPC_SECURITY_KEY sockopt. Calls + secured in this way will still share connections if at all possible. + + The user_call_ID is equivalent to that supplied to sendmsg() in the + control data buffer. It is entirely feasible to use this to point to a + kernel data structure. + + If this function is successful, an opaque reference to the RxRPC call is + returned. The caller now holds a reference on this and it must be + properly ended. + + (*) End a client call. + + void rxrpc_kernel_end_call(struct rxrpc_call *call); + + This is used to end a previously begun call. The user_call_ID is expunged + from AF_RXRPC's knowledge and will not be seen again in association with + the specified call. + + (*) Send data through a call. + + int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg, + size_t len); + + This is used to supply either the request part of a client call or the + reply part of a server call. msg.msg_iovlen and msg.msg_iov specify the + data buffers to be used. msg_iov may not be NULL and must point + exclusively to in-kernel virtual addresses. msg.msg_flags may be given + MSG_MORE if there will be subsequent data sends for this call. + + The msg must not specify a destination address, control data or any flags + other than MSG_MORE. len is the total amount of data to transmit. + + (*) Abort a call. + + void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code); + + This is used to abort a call if it's still in an abortable state. The + abort code specified will be placed in the ABORT message sent. + + (*) Intercept received RxRPC messages. + + typedef void (*rxrpc_interceptor_t)(struct sock *sk, + unsigned long user_call_ID, + struct sk_buff *skb); + + void + rxrpc_kernel_intercept_rx_messages(struct socket *sock, + rxrpc_interceptor_t interceptor); + + This installs an interceptor function on the specified AF_RXRPC socket. + All messages that would otherwise wind up in the socket's Rx queue are + then diverted to this function. Note that care must be taken to process + the messages in the right order to maintain DATA message sequentiality. + + The interceptor function itself is provided with the address of the socket + and handling the incoming message, the ID assigned by the kernel utility + to the call and the socket buffer containing the message. + + The skb->mark field indicates the type of message: + + MARK MEANING + =============================== ======================================= + RXRPC_SKB_MARK_DATA Data message + RXRPC_SKB_MARK_FINAL_ACK Final ACK received for an incoming call + RXRPC_SKB_MARK_BUSY Client call rejected as server busy + RXRPC_SKB_MARK_REMOTE_ABORT Call aborted by peer + RXRPC_SKB_MARK_NET_ERROR Network error detected + RXRPC_SKB_MARK_LOCAL_ERROR Local error encountered + RXRPC_SKB_MARK_NEW_CALL New incoming call awaiting acceptance + + The remote abort message can be probed with rxrpc_kernel_get_abort_code(). + The two error messages can be probed with rxrpc_kernel_get_error_number(). + A new call can be accepted with rxrpc_kernel_accept_call(). + + Data messages can have their contents extracted with the usual bunch of + socket buffer manipulation functions. A data message can be determined to + be the last one in a sequence with rxrpc_kernel_is_data_last(). When a + data message has been used up, rxrpc_kernel_data_delivered() should be + called on it.. + + Non-data messages should be handled to rxrpc_kernel_free_skb() to dispose + of. It is possible to get extra refs on all types of message for later + freeing, but this may pin the state of a call until the message is finally + freed. + + (*) Accept an incoming call. + + struct rxrpc_call * + rxrpc_kernel_accept_call(struct socket *sock, + unsigned long user_call_ID); + + This is used to accept an incoming call and to assign it a call ID. This + function is similar to rxrpc_kernel_begin_call() and calls accepted must + be ended in the same way. + + If this function is successful, an opaque reference to the RxRPC call is + returned. The caller now holds a reference on this and it must be + properly ended. + + (*) Reject an incoming call. + + int rxrpc_kernel_reject_call(struct socket *sock); + + This is used to reject the first incoming call on the socket's queue with + a BUSY message. -ENODATA is returned if there were no incoming calls. + Other errors may be returned if the call had been aborted (-ECONNABORTED) + or had timed out (-ETIME). + + (*) Record the delivery of a data message and free it. + + void rxrpc_kernel_data_delivered(struct sk_buff *skb); + + This is used to record a data message as having been delivered and to + update the ACK state for the call. The socket buffer will be freed. + + (*) Free a message. + + void rxrpc_kernel_free_skb(struct sk_buff *skb); + + This is used to free a non-DATA socket buffer intercepted from an AF_RXRPC + socket. + + (*) Determine if a data message is the last one on a call. + + bool rxrpc_kernel_is_data_last(struct sk_buff *skb); + + This is used to determine if a socket buffer holds the last data message + to be received for a call (true will be returned if it does, false + if not). + + The data message will be part of the reply on a client call and the + request on an incoming call. In the latter case there will be more + messages, but in the former case there will not. + + (*) Get the abort code from an abort message. + + u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb); + + This is used to extract the abort code from a remote abort message. + + (*) Get the error number from a local or network error message. + + int rxrpc_kernel_get_error_number(struct sk_buff *skb); + + This is used to extract the error number from a message indicating either + a local error occurred or a network error occurred. diff --git a/Documentation/networking/wan-router.txt b/Documentation/networking/wan-router.txt index 653978d..07dd6d9 100644 --- a/Documentation/networking/wan-router.txt +++ b/Documentation/networking/wan-router.txt @@ -250,7 +250,6 @@ PRODUCT COMPONENTS AND RELATED FILES sdladrv.h SDLA support module API definitions sdlasfm.h SDLA firmware module definitions if_wanpipe.h WANPIPE Socket definitions - if_wanpipe_common.h WANPIPE Socket/Driver common definitions. sdlapci.h WANPIPE PCI definitions diff --git a/Documentation/power/interface.txt b/Documentation/power/interface.txt index 74311d7..8c5b41b 100644 --- a/Documentation/power/interface.txt +++ b/Documentation/power/interface.txt @@ -18,17 +18,10 @@ states. /sys/power/disk controls the operating mode of the suspend-to-disk -mechanism. Suspend-to-disk can be handled in several ways. The -greatest distinction is who writes memory to disk - the firmware or -the kernel. If the firmware does it, we assume that it also handles -suspending the system. - -If the kernel does it, then we have three options for putting the system -to sleep - using the platform driver (e.g. ACPI or other PM -registers), powering off the system or rebooting the system (for -testing). The system will support either 'firmware' or 'platform', and -that is known a priori. But, the user may choose 'shutdown' or -'reboot' as alternatives. +mechanism. Suspend-to-disk can be handled in several ways. We have a +few options for putting the system to sleep - using the platform driver +(e.g. ACPI or other pm_ops), powering off the system or rebooting the +system (for testing). Additionally, /sys/power/disk can be used to turn on one of the two testing modes of the suspend-to-disk mechanism: 'testproc' or 'test'. If the @@ -44,16 +37,12 @@ is being slow and which device drivers a Reading from this file will display what the mode is currently set to. Writing to this file will accept one of - 'firmware' - 'platform' + 'platform' (only if the platform supports it) 'shutdown' 'reboot' 'testproc' 'test' -It will only change to 'firmware' or 'platform' if the system supports -it. - /sys/power/image_size controls the size of the image created by the suspend-to-disk mechanism. It can be written a string representing a non-negative integer that will be used as an upper diff --git a/Documentation/power/states.txt b/Documentation/power/states.txt index 0931a33..34800cc 100644 --- a/Documentation/power/states.txt +++ b/Documentation/power/states.txt @@ -62,17 +62,18 @@ setup via another operating system for i inconvenience, this method requires minimal work by the kernel, since the firmware will also handle restoring memory contents on resume. -If the kernel is responsible for persistently saving state, a mechanism -called 'swsusp' (Swap Suspend) is used to write memory contents to -free swap space. swsusp has some restrictive requirements, but should -work in most cases. Some, albeit outdated, documentation can be found -in Documentation/power/swsusp.txt. +For suspend-to-disk, a mechanism called swsusp called 'swsusp' (Swap +Suspend) is used to write memory contents to free swap space. +swsusp has some restrictive requirements, but should work in most +cases. Some, albeit outdated, documentation can be found in +Documentation/power/swsusp.txt. Alternatively, userspace can do most +of the actual suspend to disk work, see userland-swsusp.txt. Once memory state is written to disk, the system may either enter a low-power state (like ACPI S4), or it may simply power down. Powering down offers greater savings, and allows this mechanism to work on any system. However, entering a real low-power state allows the user to -trigger wake up events (e.g. pressing a key or opening a laptop lid). +trigger wake up events (e.g. pressing a key or opening a laptop lid). A transition from Suspend-to-Disk to the On state should take about 30 seconds, though it's typically a bit more with the current diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index 0761ff6..c55bd50 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -156,8 +156,7 @@ instead set the PF_NOFREEZE process flag be very careful). -Q: What is the difference between "platform", "shutdown" and -"firmware" in /sys/power/disk? +Q: What is the difference between "platform" and "shutdown"? A: @@ -166,11 +165,8 @@ shutdown: save state in linux, then tell platform: save state in linux, then tell bios to powerdown and blink "suspended led" -firmware: tell bios to save state itself [needs BIOS-specific suspend - partition, and has very little to do with swsusp] - -"platform" is actually right thing to do, but "shutdown" is most -reliable. +"platform" is actually right thing to do where supported, but +"shutdown" is most reliable (except on ACPI systems). Q: I do not understand why you have such strong objections to idea of selective suspend. @@ -388,8 +384,8 @@ while the system is asleep, maintaining modes like "suspend-to-RAM" or "standby". (Don't write "disk" to the /sys/power/state file; write "standby" or "mem".) We've not seen any hardware that can use these modes through software suspend, although in -theory some systems might support "platform" or "firmware" modes that -won't break the USB connections. +theory some systems might support "platform" modes that won't break the +USB connections. Remember that it's always a bad idea to unplug a disk drive containing a mounted filesystem. That's true even when your system is asleep! The diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index b41397d..033a3f3 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -39,7 +39,7 @@ (c) 2006 MontaVista Software, Inc. and property data. The old style variable alignment would make it impossible to do "simple" insertion of properties using - memove (thanks Milton for + memmove (thanks Milton for noticing). Updated kernel patch as well - Correct a few more alignment constraints - Add a chapter about the device-tree @@ -55,7 +55,7 @@ (c) 2006 MontaVista Software, Inc. ToDo: - Add some definitions of interrupt tree (simple/complex) - - Add some definitions for pci host bridges + - Add some definitions for PCI host bridges - Add some common address format examples - Add definitions for standard properties and "compatible" names for cells that are not already defined by the existing @@ -114,7 +114,7 @@ it with special cases. forth words isn't required), you can enter the kernel with: r5 : OF callback pointer as defined by IEEE 1275 - bindings to powerpc. Only the 32 bit client interface + bindings to powerpc. Only the 32-bit client interface is currently supported r3, r4 : address & length of an initrd if any or 0 @@ -194,7 +194,7 @@ it with special cases. for this is to keep kernels on embedded systems small and efficient; part of this is due to the fact the code is already that way. In the future, a kernel may support multiple platforms, but only if the - platforms feature the same core architectire. A single kernel build + platforms feature the same core architecture. A single kernel build cannot support both configurations with Book E and configurations with classic Powerpc architectures. @@ -215,7 +215,7 @@ of the boot sequences.... someone speak enable another config option to select the specific board supported. -NOTE: If ben doesn't merge the setup files, may need to change this to +NOTE: If Ben doesn't merge the setup files, may need to change this to point to setup_32.c @@ -256,7 +256,7 @@ struct boot_param_header { u32 off_dt_struct; /* offset to structure */ u32 off_dt_strings; /* offset to strings */ u32 off_mem_rsvmap; /* offset to memory reserve map -*/ + */ u32 version; /* format version */ u32 last_comp_version; /* last compatible version */ @@ -265,6 +265,9 @@ struct boot_param_header { booting on */ /* version 3 fields below */ u32 size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + u32 size_dt_struct; /* size of the DT structure block */ }; Along with the constants: @@ -273,7 +276,7 @@ struct boot_param_header { #define OF_DT_HEADER 0xd00dfeed /* 4: version, 4: total size */ #define OF_DT_BEGIN_NODE 0x1 /* Start node: full name -*/ + */ #define OF_DT_END_NODE 0x2 /* End node */ #define OF_DT_PROP 0x3 /* Property: name off, size, content */ @@ -310,9 +313,8 @@ #define OF_DT_END 0x9 - off_mem_rsvmap This is an offset from the beginning of the header to the start - of the reserved memory map. This map is a list of pairs of 64 + of the reserved memory map. This map is a list of pairs of 64- bit integers. Each pair is a physical address and a size. The - list is terminated by an entry of size 0. This map provides the kernel with a list of physical memory areas that are "reserved" and thus not to be used for memory allocations, especially during @@ -325,7 +327,7 @@ #define OF_DT_END 0x9 contain _at least_ this DT block itself (header,total_size). If you are passing an initrd to the kernel, you should reserve it as well. You do not need to reserve the kernel image itself. The map - should be 64 bit aligned. + should be 64-bit aligned. - version @@ -335,10 +337,13 @@ #define OF_DT_END 0x9 to reallocate it easily at boot and free up the unused flattened structure after expansion. Version 16 introduces a new more "compact" format for the tree itself that is however not backward - compatible. You should always generate a structure of the highest - version defined at the time of your implementation. Currently - that is version 16, unless you explicitly aim at being backward - compatible. + compatible. Version 17 adds an additional field, size_dt_struct, + allowing it to be reallocated or moved more easily (this is + particularly useful for bootloaders which need to make + adjustments to a device tree based on probed information). You + should always generate a structure of the highest version defined + at the time of your implementation. Currently that is version 17, + unless you explicitly aim at being backward compatible. - last_comp_version @@ -347,7 +352,7 @@ #define OF_DT_END 0x9 is backward compatible with version 1 (that is, a kernel build for version 1 will be able to boot with a version 2 format). You should put a 1 in this field if you generate a device tree of - version 1 to 3, or 0x10 if you generate a tree of version 0x10 + version 1 to 3, or 16 if you generate a tree of version 16 or 17 using the new unit name format. - boot_cpuid_phys @@ -360,6 +365,17 @@ #define OF_DT_END 0x9 point (see further chapters for more informations on the required device-tree contents) + - size_dt_strings + + This field only exists on version 3 and later headers. It + gives the size of the "strings" section of the device tree (which + starts at the offset given by off_dt_strings). + + - size_dt_struct + + This field only exists on version 17 and later headers. It gives + the size of the "structure" section of the device tree (which + starts at the offset given by off_dt_struct). So the typical layout of a DT block (though the various parts don't need to be in that order) looks like this (addresses go from top to @@ -417,7 +433,7 @@ root node who has no parent. A node has 2 names. The actual node name is generally contained in a property of type "name" in the node property list whose value is a zero terminated string and is mandatory for version 1 to 3 of the -format definition (as it is in Open Firmware). Version 0x10 makes it +format definition (as it is in Open Firmware). Version 16 makes it optional as it can generate it from the unit name defined below. There is also a "unit name" that is used to differentiate nodes with @@ -461,7 +477,7 @@ referencing another node via "phandle" i interrupt tree which will be described in a further version of this document. -This "linux, phandle" property is a 32 bit value that uniquely +This "linux, phandle" property is a 32-bit value that uniquely identifies a node. You are free to use whatever values or system of values, internal pointers, or whatever to generate these, the only requirement is that every node for which you provide that property has @@ -471,7 +487,7 @@ Here is an example of a simple device-tr designates a node followed by the node unit name. Properties are presented with their name followed by their content. "content" represents an ASCII string (zero terminated) value, while -represents a 32 bit hexadecimal value. The various nodes in this +represents a 32-bit hexadecimal value. The various nodes in this example will be discussed in a later chapter. At this point, it is only meant to give you a idea of what a device-tree looks like. I have purposefully kept the "name" and "linux,phandle" properties which @@ -543,15 +559,15 @@ Here's the basic structure of a single n * [align gap to next 4 bytes boundary] * for each property: * token OF_DT_PROP (that is 0x00000003) - * 32 bit value of property value size in bytes (or 0 of no - * value) - * 32 bit value of offset in string block of property name + * 32-bit value of property value size in bytes (or 0 if no + value) + * 32-bit value of offset in string block of property name * property value data if any * [align gap to next 4 bytes boundary] * [child nodes if any] * token OF_DT_END_NODE (that is 0x00000002) -So the node content can be summarised as a start token, a full path, +So the node content can be summarized as a start token, a full path, a list of properties, a list of child nodes, and an end token. Every child node is a full node structure itself as defined above. @@ -583,7 +599,7 @@ provide those properties yourself. ---------------------------------------------- The general rule is documented in the various Open Firmware -documentations. If you chose to describe a bus with the device-tree +documentations. If you choose to describe a bus with the device-tree and there exist an OF bus binding, then you should follow the specification. However, the kernel does not require every single device or bus to be described by the device tree. @@ -596,9 +612,9 @@ those properties defining addresses form on the processor bus. Those 2 properties define 'cells' for representing an address and a -size. A "cell" is a 32 bit number. For example, if both contain 2 +size. A "cell" is a 32-bit number. For example, if both contain 2 like the example tree given above, then an address and a size are both -composed of 2 cells, and each is a 64 bit number (cells are +composed of 2 cells, and each is a 64-bit number (cells are concatenated and expected to be in big endian format). Another example is the way Apple firmware defines them, with 2 cells for an address and one cell for a size. Most 32-bit implementations should define @@ -632,7 +648,7 @@ prom_parse.c file of the recent kernels The "reg" property only defines addresses and sizes (if #size-cells is non-0) within a given bus. In order to translate addresses upward -(that is into parent bus addresses, and possibly into cpu physical +(that is into parent bus addresses, and possibly into CPU physical addresses), all busses must contain a "ranges" property. If the "ranges" property is missing at a given level, it's assumed that translation isn't possible. The format of the "ranges" property for a @@ -648,9 +664,9 @@ example, for a PCI host controller, that PCI<->ISA bridge, that would be a PCI address. It defines the base address in the parent bus where the beginning of that range is mapped. -For a new 64 bit powerpc board, I recommend either the 2/2 format or +For a new 64-bit powerpc board, I recommend either the 2/2 format or Apple's 2/1 format which is slightly more compact since sizes usually -fit in a single 32 bit word. New 32 bit powerpc boards should use a +fit in a single 32-bit word. New 32-bit powerpc boards should use a 1/1 format, unless the processor supports physical addresses greater than 32-bits, in which case a 2/1 format is recommended. @@ -764,7 +780,7 @@ address which can extend beyond that lim Required properties: - device_type : has to be "cpu" - - reg : This is the physical cpu number, it's a single 32 bit cell + - reg : This is the physical CPU number, it's a single 32-bit cell and is also used as-is as the unit number for constructing the unit name in the full path. For example, with 2 CPUs, you would have the full path: @@ -785,7 +801,7 @@ address which can extend beyond that lim the kernel timebase/decrementer calibration based on this value. - clock-frequency : a cell indicating the CPU core clock frequency - in Hz. A new property will be defined for 64 bit values, but if + in Hz. A new property will be defined for 64-bit values, but if your frequency is < 4Ghz, one cell is enough. Here as well as for the above, the common code doesn't use that property, but you are welcome to re-use the pSeries or Maple one. A future @@ -832,8 +848,7 @@ address which can extend beyond that lim This node is a bit "special". Normally, that's where open firmware puts some variable environment information, like the arguments, or - phandle pointers to nodes like the main interrupt controller, or the - default input/output devices. + the default input/output devices. This specification makes a few of these mandatory, but also defines some linux-specific properties that would be normally constructed by @@ -853,14 +868,14 @@ address which can extend beyond that lim that the kernel tries to find out the default console and has knowledge of various types like 8250 serial ports. You may want to extend this function to add your own. - - interrupt-controller : This is one cell containing a phandle - value that matches the "linux,phandle" property of your main - interrupt controller node. May be used for interrupt routing. - Note that u-boot creates and fills in the chosen node for platforms that use it. + (Note: a practice that is now obsolete was to include a property + under /chosen called interrupt-controller which had a phandle value + that pointed to the main interrupt controller) + f) the /soc node This node is used to represent a system-on-a-chip (SOC) and must be @@ -908,8 +923,7 @@ address which can extend beyond that lim The SOC node may contain child nodes for each SOC device that the platform uses. Nodes should not be created for devices which exist on the SOC but are not used by a particular platform. See chapter VI - for more information on how to specify devices that are part of an -SOC. + for more information on how to specify devices that are part of a SOC. Example SOC node for the MPC8540: @@ -972,7 +986,7 @@ The syntax of the dtc tool is [-o output-filename] [-V output_version] input_filename -The "output_version" defines what versio of the "blob" format will be +The "output_version" defines what version of the "blob" format will be generated. Supported versions are 1,2,3 and 16. The default is currently version 3 but that may change in the future to version 16. @@ -994,12 +1008,12 @@ supported currently at the toplevel. */ property2 = <1234abcd>; /* define a property containing a - * numerical 32 bits value (hexadecimal) + * numerical 32-bit value (hexadecimal) */ property3 = <12345678 12345678 deadbeef>; /* define a property containing 3 - * numerical 32 bits values (cells) in + * numerical 32-bit values (cells) in * hexadecimal */ property4 = [0a 0b 0c 0d de ea ad be ef]; @@ -1068,7 +1082,7 @@ while all this has been defined and impl its usage in early_init_devtree(), and the corresponding various early_init_dt_scan_*() callbacks. That code can be re-used in a GPL bootloader, and as the author of that code, I would be happy - to discuss possible free licencing to any vendor who wishes to + to discuss possible free licensing to any vendor who wishes to integrate all or part of this code into a non-GPL bootloader. @@ -1077,7 +1091,7 @@ VI - System-on-a-chip devices and nodes ======================================= Many companies are now starting to develop system-on-a-chip -processors, where the processor core (cpu) and many peripheral devices +processors, where the processor core (CPU) and many peripheral devices exist on a single piece of silicon. For these SOCs, an SOC node should be used that defines child nodes for the devices that make up the SOC. While platforms are not required to use this model in @@ -1109,42 +1123,7 @@ See appendix A for an example partial SO MPC8540. -2) Specifying interrupt information for SOC devices ---------------------------------------------------- - -Each device that is part of an SOC and which generates interrupts -should have the following properties: - - - interrupt-parent : contains the phandle of the interrupt - controller which handles interrupts for this device - - interrupts : a list of tuples representing the interrupt - number and the interrupt sense and level for each interrupt - for this device. - -This information is used by the kernel to build the interrupt table -for the interrupt controllers in the system. - -Sense and level information should be encoded as follows: - - Devices connected to openPIC-compatible controllers should encode - sense and polarity as follows: - - 0 = low to high edge sensitive type enabled - 1 = active low level sensitive type enabled - 2 = active high level sensitive type enabled - 3 = high to low edge sensitive type enabled - - ISA PIC interrupt controllers should adhere to the ISA PIC - encodings listed below: - - 0 = active low level sensitive type enabled - 1 = active high level sensitive type enabled - 2 = high to low edge sensitive type enabled - 3 = low to high edge sensitive type enabled - - - -3) Representing devices without a current OF specification +2) Representing devices without a current OF specification ---------------------------------------------------------- Currently, there are many devices on SOCs that do not have a standard @@ -1201,6 +1180,13 @@ platforms are moved over to use the flat - phy-handle : The phandle for the PHY connected to this ethernet controller. + Recommended properties: + + - linux,network-index : This is the intended "index" of this + network device. This is used by the bootwrapper to interpret + MAC addresses passed by the firmware when no information other + than indices is available to associate an address with a device. + Example: ethernet@24000 { @@ -1312,10 +1298,10 @@ platforms are moved over to use the flat and additions : Required properties : - - compatible : Should be "fsl-usb2-mph" for multi port host usb - controllers, or "fsl-usb2-dr" for dual role usb controllers - - phy_type : For multi port host usb controllers, should be one of - "ulpi", or "serial". For dual role usb controllers, should be + - compatible : Should be "fsl-usb2-mph" for multi port host USB + controllers, or "fsl-usb2-dr" for dual role USB controllers + - phy_type : For multi port host USB controllers, should be one of + "ulpi", or "serial". For dual role USB controllers, should be one of "ulpi", "utmi", "utmi_wide", or "serial". - reg : Offset and length of the register set for the device - port0 : boolean; if defined, indicates port0 is connected for @@ -1339,7 +1325,7 @@ platforms are moved over to use the flat - interrupt-parent : the phandle for the interrupt controller that services interrupts for this device. - Example multi port host usb controller device node : + Example multi port host USB controller device node : usb@22000 { device_type = "usb"; compatible = "fsl-usb2-mph"; @@ -1353,7 +1339,7 @@ platforms are moved over to use the flat port1; }; - Example dual role usb controller device node : + Example dual role USB controller device node : usb@23000 { device_type = "usb"; compatible = "fsl-usb2-dr"; @@ -1387,7 +1373,7 @@ platforms are moved over to use the flat - channel-fifo-len : An integer representing the number of descriptor pointers each channel fetch fifo can hold. - exec-units-mask : The bitmask representing what execution units - (EUs) are available. It's a single 32 bit cell. EU information + (EUs) are available. It's a single 32-bit cell. EU information should be encoded following the SEC's Descriptor Header Dword EU_SEL0 field documentation, i.e. as follows: @@ -1403,7 +1389,7 @@ platforms are moved over to use the flat bits 8 through 31 are reserved for future SEC EUs. - descriptor-types-mask : The bitmask representing what descriptors - are available. It's a single 32 bit cell. Descriptor type + are available. It's a single 32-bit cell. Descriptor type information should be encoded following the SEC's Descriptor Header Dword DESC_TYPE field documentation, i.e. as follows: @@ -1492,7 +1478,7 @@ platforms are moved over to use the flat Required properties: - device_type : should be "spi". - compatible : should be "fsl_spi". - - mode : the spi operation mode, it can be "cpu" or "qe". + - mode : the SPI operation mode, it can be "cpu" or "qe". - reg : Offset and length of the register set for the device - interrupts : where a is the interrupt number and b is a field that represents an encoding of the sense and level @@ -1569,6 +1555,12 @@ platforms are moved over to use the flat - mac-address : list of bytes representing the ethernet address. - phy-handle : The phandle for the PHY connected to this controller. + Recommended properties: + - linux,network-index : This is the intended "index" of this + network device. This is used by the bootwrapper to interpret + MAC addresses passed by the firmware when no information other + than indices is available to associate an address with a device. + Example: ucc@2000 { device_type = "network"; @@ -1712,7 +1704,7 @@ platforms are moved over to use the flat - partitions : Several pairs of 32-bit values where the first value is partition's offset from the start of the device and the second one is partition size in bytes with LSB used to signify a read only - partition (so, the parition size should always be an even number). + partition (so, the partition size should always be an even number). - partition-names : The list of concatenated zero terminated strings representing the partition names. - probe-type : The type of probe which should be done for the chip @@ -1733,6 +1725,92 @@ platforms are moved over to use the flat More devices will be defined as this spec matures. +VII - Specifying interrupt information for devices +=================================================== + +The device tree represents the busses and devices of a hardware +system in a form similar to the physical bus topology of the +hardware. + +In addition, a logical 'interrupt tree' exists which represents the +hierarchy and routing of interrupts in the hardware. + +The interrupt tree model is fully described in the +document "Open Firmware Recommended Practice: Interrupt +Mapping Version 0.9". The document is available at: +. + +1) interrupts property +---------------------- + +Devices that generate interrupts to a single interrupt controller +should use the conventional OF representation described in the +OF interrupt mapping documentation. + +Each device which generates interrupts must have an 'interrupt' +property. The interrupt property value is an arbitrary number of +of 'interrupt specifier' values which describe the interrupt or +interrupts for the device. + +The encoding of an interrupt specifier is determined by the +interrupt domain in which the device is located in the +interrupt tree. The root of an interrupt domain specifies in +its #interrupt-cells property the number of 32-bit cells +required to encode an interrupt specifier. See the OF interrupt +mapping documentation for a detailed description of domains. + +For example, the binding for the OpenPIC interrupt controller +specifies an #interrupt-cells value of 2 to encode the interrupt +number and level/sense information. All interrupt children in an +OpenPIC interrupt domain use 2 cells per interrupt in their interrupts +property. + +The PCI bus binding specifies a #interrupt-cell value of 1 to encode +which interrupt pin (INTA,INTB,INTC,INTD) is used. + +2) interrupt-parent property +---------------------------- + +The interrupt-parent property is specified to define an explicit +link between a device node and its interrupt parent in +the interrupt tree. The value of interrupt-parent is the +phandle of the parent node. + +If the interrupt-parent property is not defined for a node, it's +interrupt parent is assumed to be an ancestor in the node's +_device tree_ hierarchy. + +3) OpenPIC Interrupt Controllers +-------------------------------- + +OpenPIC interrupt controllers require 2 cells to encode +interrupt information. The first cell defines the interrupt +number. The second cell defines the sense and level +information. + +Sense and level information should be encoded as follows: + + 0 = low to high edge sensitive type enabled + 1 = active low level sensitive type enabled + 2 = active high level sensitive type enabled + 3 = high to low edge sensitive type enabled + +4) ISA Interrupt Controllers +---------------------------- + +ISA PIC interrupt controllers require 2 cells to encode +interrupt information. The first cell defines the interrupt +number. The second cell defines the sense and level +information. + +ISA PIC interrupt controllers should adhere to the ISA PIC +encodings listed below: + + 0 = active low level sensitive type enabled + 1 = active high level sensitive type enabled + 2 = high to low edge sensitive type enabled + 3 = low to high edge sensitive type enabled + Appendix A - Sample SOC node for MPC8540 ======================================== diff --git a/Documentation/s390/crypto/crypto-API.txt b/Documentation/s390/crypto/crypto-API.txt deleted file mode 100644 index 71ae6ca..0000000 --- a/Documentation/s390/crypto/crypto-API.txt +++ /dev/null @@ -1,83 +0,0 @@ -crypto-API support for z990 Message Security Assist (MSA) instructions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AUTHOR: Thomas Spatzier (tspat@de.ibm.com) - - -1. Introduction crypto-API -~~~~~~~~~~~~~~~~~~~~~~~~~~ -See Documentation/crypto/api-intro.txt for an introduction/description of the -kernel crypto API. -According to api-intro.txt support for z990 crypto instructions has been added -in the algorithm api layer of the crypto API. Several files containing z990 -optimized implementations of crypto algorithms are placed in the -arch/s390/crypto directory. - - -2. Probing for availability of MSA -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It should be possible to use Kernels with the z990 crypto implementations both -on machines with MSA available and on those without MSA (pre z990 or z990 -without MSA). Therefore a simple probing mechanism has been implemented: -In the init function of each crypto module the availability of MSA and of the -respective crypto algorithm in particular will be tested. If the algorithm is -available the module will load and register its algorithm with the crypto API. - -If the respective crypto algorithm is not available, the init function will -return -ENOSYS. In that case a fallback to the standard software implementation -of the crypto algorithm must be taken ( -> the standard crypto modules are -also built when compiling the kernel). - - -3. Ensuring z990 crypto module preference -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If z990 crypto instructions are available the optimized modules should be -preferred instead of standard modules. - -3.1. compiled-in modules -~~~~~~~~~~~~~~~~~~~~~~~~ -For compiled-in modules it has to be ensured that the z990 modules are linked -before the standard crypto modules. Then, on system startup the init functions -of z990 crypto modules will be called first and query for availability of z990 -crypto instructions. If instruction is available, the z990 module will register -its crypto algorithm implementation -> the load of the standard module will fail -since the algorithm is already registered. -If z990 crypto instruction is not available the load of the z990 module will -fail -> the standard module will load and register its algorithm. - -3.2. dynamic modules -~~~~~~~~~~~~~~~~~~~~ -A system administrator has to take care of giving preference to z990 crypto -modules. If MSA is available appropriate lines have to be added to -/etc/modprobe.conf. - -Example: z990 crypto instruction for SHA1 algorithm is available - - add the following line to /etc/modprobe.conf (assuming the - z990 crypto modules for SHA1 is called sha1_z990): - - alias sha1 sha1_z990 - - -> when the sha1 algorithm is requested through the crypto API - (which has a module autoloader) the z990 module will be loaded. - -TBD: a userspace module probing mechanism - something like 'probe sha1 sha1_z990 sha1' in modprobe.conf - -> try module sha1_z990, if it fails to load standard module sha1 - the 'probe' statement is currently not supported in modprobe.conf - - -4. Currently implemented z990 crypto algorithms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following crypto algorithms with z990 MSA support are currently implemented. -The name of each algorithm under which it is registered in crypto API and the -name of the respective module is given in square brackets. - -- SHA1 Digest Algorithm [sha1 -> sha1_z990] -- DES Encrypt/Decrypt Algorithm (64bit key) [des -> des_z990] -- Triple DES Encrypt/Decrypt Algorithm (128bit key) [des3_ede128 -> des_z990] -- Triple DES Encrypt/Decrypt Algorithm (192bit key) [des3_ede -> des_z990] - -In order to load, for example, the sha1_z990 module when the sha1 algorithm is -requested (see 3.2.) add 'alias sha1 sha1_z990' to /etc/modprobe.conf. - diff --git a/Documentation/s390/zfcpdump.txt b/Documentation/s390/zfcpdump.txt new file mode 100644 index 0000000..cf45d27 --- /dev/null +++ b/Documentation/s390/zfcpdump.txt @@ -0,0 +1,87 @@ +s390 SCSI dump tool (zfcpdump) + +System z machines (z900 or higher) provide hardware support for creating system +dumps on SCSI disks. The dump process is initiated by booting a dump tool, which +has to create a dump of the current (probably crashed) Linux image. In order to +not overwrite memory of the crashed Linux with data of the dump tool, the +hardware saves some memory plus the register sets of the boot cpu before the +dump tool is loaded. There exists an SCLP hardware interface to obtain the saved +memory afterwards. Currently 32 MB are saved. + +This zfcpdump implementation consists of a Linux dump kernel together with +a userspace dump tool, which are loaded together into the saved memory region +below 32 MB. zfcpdump is installed on a SCSI disk using zipl (as contained in +the s390-tools package) to make the device bootable. The operator of a Linux +system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump +resides on. + +The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem", +which exports memory and registers of the crashed Linux in an s390 +standalone dump format. It can be used in the same way as e.g. /dev/mem. The +dump format defines a 4K header followed by plain uncompressed memory. The +register sets are stored in the prefix pages of the respective cpus. To build a +dump enabled kernel with the zcore driver, the kernel config option +CONFIG_ZFCPDUMP has to be set. When reading from "zcore/mem", the part of +memory, which has been saved by hardware is read by the driver via the SCLP +hardware interface. The second part is just copied from the non overwritten real +memory. + +The userspace application of zfcpdump can reside e.g. in an intitramfs or an +initrd. It reads from zcore/mem and writes the system dump to a file on a +SCSI disk. + +To build a zfcpdump kernel use the following settings in your kernel +configuration: + * CONFIG_ZFCPDUMP=y + * Enable ZFCP driver + * Enable SCSI driver + * Enable ext2 and ext3 filesystems + * Disable as many features as possible to keep the kernel small. + E.g. network support is not needed at all. + +To use the zfcpdump userspace application in an initramfs you have to do the +following: + + * Copy the zfcpdump executable somewhere into your Linux tree. + E.g. to "arch/s390/boot/zfcpdump. If you do not want to include + shared libraries, compile the tool with the "-static" gcc option. + * If you want to include e2fsck, add it to your source tree, too. The zfcpdump + application attempts to start /sbin/e2fsck from the ramdisk. + * Use an initramfs config file like the following: + + dir /dev 755 0 0 + nod /dev/console 644 0 0 c 5 1 + nod /dev/null 644 0 0 c 1 3 + nod /dev/sda1 644 0 0 b 8 1 + nod /dev/sda2 644 0 0 b 8 2 + nod /dev/sda3 644 0 0 b 8 3 + nod /dev/sda4 644 0 0 b 8 4 + nod /dev/sda5 644 0 0 b 8 5 + nod /dev/sda6 644 0 0 b 8 6 + nod /dev/sda7 644 0 0 b 8 7 + nod /dev/sda8 644 0 0 b 8 8 + nod /dev/sda9 644 0 0 b 8 9 + nod /dev/sda10 644 0 0 b 8 10 + nod /dev/sda11 644 0 0 b 8 11 + nod /dev/sda12 644 0 0 b 8 12 + nod /dev/sda13 644 0 0 b 8 13 + nod /dev/sda14 644 0 0 b 8 14 + nod /dev/sda15 644 0 0 b 8 15 + file /init arch/s390/boot/zfcpdump 755 0 0 + file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0 + dir /proc 755 0 0 + dir /sys 755 0 0 + dir /mnt 755 0 0 + dir /sbin 755 0 0 + + * Issue "make image" to build the zfcpdump image with initramfs. + +In a Linux distribution the zfcpdump enabled kernel image must be copied to +/usr/share/zfcpdump/zfcpdump.image, where the s390 zipl tool is looking for the +dump kernel when preparing a SCSI dump disk. + +If you use a ramdisk copy it to "/usr/share/zfcpdump/zfcpdump.rd". + +For more information on how to use zfcpdump refer to the s390 'Using the Dump +Tools book', which is available from +http://www.ibm.com/developerworks/linux/linux390. diff --git a/Documentation/sony-laptop.txt b/Documentation/sony-laptop.txt index dfd26df..7a5c1a8 100644 --- a/Documentation/sony-laptop.txt +++ b/Documentation/sony-laptop.txt @@ -3,12 +3,18 @@ Sony Notebook Control Driver (SNC) Readm Copyright (C) 2004- 2005 Stelian Pop Copyright (C) 2007 Mattia Dongili -This mini-driver drives the SNC device present in the ACPI BIOS of -the Sony Vaio laptops. +This mini-driver drives the SNC and SPIC device present in the ACPI BIOS of the +Sony Vaio laptops. This driver mixes both devices functions under the same +(hopefully consistent) interface. This also means that the sonypi driver is +obsoleted by sony-laptop now. -It gives access to some extra laptop functionalities. In its current -form, this driver let the user set or query the screen brightness -through the backlight subsystem and remove/apply power to some devices. +Fn keys (hotkeys): +------------------ +Some models report hotkeys through the SNC or SPIC devices, such events are +reported both through the ACPI subsystem as acpi events and through the INPUT +subsystem. See the logs of acpid or /proc/acpi/event and +/proc/bus/input/devices to find out what those events are and which input +devices are created by the driver. Backlight control: ------------------ @@ -39,6 +45,8 @@ The files are: audiopower power on/off the internal sound card lanpower power on/off the internal ethernet card (only in debug mode) + bluetoothpower power on/off the internal bluetooth device + fanspeed get/set the fan speed Note that some files may be missing if they are not supported by your particular laptop model. @@ -76,9 +84,9 @@ The sony-laptop driver creates, for some current ones found on several Vaio models), an entry under /sys/devices/platform/sony-laptop, just like the 'cdpower' one. You can create other entries corresponding to your own laptop methods by -further editing the source (see the 'sony_acpi_values' table, and add a new +further editing the source (see the 'sony_nc_values' table, and add a new entry to this table with your get/set method names using the -HANDLE_NAMES macro). +SNC_HANDLE_NAMES macro). Your mission, should you accept it, is to try finding out what those entries are for, by reading/writing random values from/to those @@ -87,6 +95,9 @@ files and find out what is the impact on Should you find anything interesting, please report it back to me, I will not disavow all knowledge of your actions :) +See also http://www.linux.it/~malattia/wiki/index.php/Sony_drivers for other +useful info. + Bugs/Limitations: ----------------- diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt new file mode 100644 index 0000000..2d48033 --- /dev/null +++ b/Documentation/thinkpad-acpi.txt @@ -0,0 +1,981 @@ + ThinkPad ACPI Extras Driver + + Version 0.14 + April 21st, 2007 + + Borislav Deianov + Henrique de Moraes Holschuh + http://ibm-acpi.sf.net/ + + +This is a Linux driver for the IBM and Lenovo ThinkPad laptops. It +supports various features of these laptops which are accessible +through the ACPI and ACPI EC framework, but not otherwise fully +supported by the generic Linux ACPI drivers. + +This driver used to be named ibm-acpi until kernel 2.6.21 and release +0.13-20070314. It used to be in the drivers/acpi tree, but it was +moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel +2.6.22, and release 0.14. + + +Status +------ + +The features currently supported are the following (see below for +detailed description): + + - Fn key combinations + - Bluetooth enable and disable + - video output switching, expansion control + - ThinkLight on and off + - limited docking and undocking + - UltraBay eject + - CMOS control + - LED control + - ACPI sounds + - temperature sensors + - Experimental: embedded controller register dump + - LCD brightness control + - Volume control + - Fan control and monitoring: fan speed, fan enable/disable + - Experimental: WAN enable and disable + +A compatibility table by model and feature is maintained on the web +site, http://ibm-acpi.sf.net/. I appreciate any success or failure +reports, especially if they add to or correct the compatibility table. +Please include the following information in your report: + + - ThinkPad model name + - a copy of your DSDT, from /proc/acpi/dsdt + - a copy of the output of dmidecode, with serial numbers + and UUIDs masked off + - which driver features work and which don't + - the observed behavior of non-working features + +Any other comments or patches are also more than welcome. + + +Installation +------------ + +If you are compiling this driver as included in the Linux kernel +sources, simply enable the CONFIG_THINKPAD_ACPI option, and optionally +enable the CONFIG_THINKPAD_ACPI_BAY option if you want the +thinkpad-specific bay functionality. + +Features +-------- + +The driver exports two different interfaces to userspace, which can be +used to access the features it provides. One is a legacy procfs-based +interface, which will be removed at some time in the distant future. +The other is a new sysfs-based interface which is not complete yet. + +The procfs interface creates the /proc/acpi/ibm directory. There is a +file under that directory for each feature it supports. The procfs +interface is mostly frozen, and will change very little if at all: it +will not be extended to add any new functionality in the driver, instead +all new functionality will be implemented on the sysfs interface. + +The sysfs interface tries to blend in the generic Linux sysfs subsystems +and classes as much as possible. Since some of these subsystems are not +yet ready or stabilized, it is expected that this interface will change, +and any and all userspace programs must deal with it. + + +Notes about the sysfs interface: + +Unlike what was done with the procfs interface, correctness when talking +to the sysfs interfaces will be enforced, as will correctness in the +thinkpad-acpi's implementation of sysfs interfaces. + +Also, any bugs in the thinkpad-acpi sysfs driver code or in the +thinkpad-acpi's implementation of the sysfs interfaces will be fixed for +maximum correctness, even if that means changing an interface in +non-compatible ways. As these interfaces mature both in the kernel and +in thinkpad-acpi, such changes should become quite rare. + +Applications interfacing to the thinkpad-acpi sysfs interfaces must +follow all sysfs guidelines and correctly process all errors (the sysfs +interface makes extensive use of errors). File descriptors and open / +close operations to the sysfs inodes must also be properly implemented. + +The version of thinkpad-acpi's sysfs interface is exported by the driver +as a driver attribute (see below). + +Sysfs driver attributes are on the driver's sysfs attribute space, +for 2.6.20 this is /sys/bus/platform/drivers/thinkpad-acpi/. + +Sysfs device attributes are on the driver's sysfs attribute space, +for 2.6.20 this is /sys/devices/platform/thinkpad-acpi/. + +Driver version +-------------- + +procfs: /proc/acpi/ibm/driver +sysfs driver attribute: version + +The driver name and version. No commands can be written to this file. + +Sysfs interface version +----------------------- + +sysfs driver attribute: interface_version + +Version of the thinkpad-acpi sysfs interface, as an unsigned long +(output in hex format: 0xAAAABBCC), where: + AAAA - major revision + BB - minor revision + CC - bugfix revision + +The sysfs interface version changelog for the driver can be found at the +end of this document. Changes to the sysfs interface done by the kernel +subsystems are not documented here, nor are they tracked by this +attribute. + +Hot keys +-------- + +procfs: /proc/acpi/ibm/hotkey +sysfs device attribute: hotkey/* + +Without this driver, only the Fn-F4 key (sleep button) generates an +ACPI event. With the driver loaded, the hotkey feature enabled and the +mask set (see below), the various hot keys generate ACPI events in the +following format: + + ibm/hotkey HKEY 00000080 0000xxxx + +The last four digits vary depending on the key combination pressed. +All labeled Fn-Fx key combinations generate distinct events. In +addition, the lid microswitch and some docking station buttons may +also generate such events. + +The bit mask allows some control over which hot keys generate ACPI +events. Not all bits in the mask can be modified. Not all bits that +can be modified do anything. Not all hot keys can be individually +controlled by the mask. Most recent ThinkPad models honor the +following bits (assuming the hot keys feature has been enabled): + + key bit behavior when set behavior when unset + + Fn-F3 always generates ACPI event + Fn-F4 always generates ACPI event + Fn-F5 0010 generate ACPI event enable/disable Bluetooth + Fn-F7 0040 generate ACPI event switch LCD and external display + Fn-F8 0080 generate ACPI event expand screen or none + Fn-F9 0100 generate ACPI event none + Fn-F12 always generates ACPI event + +Some models do not support all of the above. For example, the T30 does +not support Fn-F5 and Fn-F9. Other models do not support the mask at +all. On those models, hot keys cannot be controlled individually. + +Note that enabling ACPI events for some keys prevents their default +behavior. For example, if events for Fn-F5 are enabled, that key will +no longer enable/disable Bluetooth by itself. This can still be done +from an acpid handler for the ibm/hotkey event. + +Note also that not all Fn key combinations are supported through +ACPI. For example, on the X40, the brightness, volume and "Access IBM" +buttons do not generate ACPI events even with this driver. They *can* +be used through the "ThinkPad Buttons" utility, see +http://www.nongnu.org/tpb/ + +procfs notes: + +The following commands can be written to the /proc/acpi/ibm/hotkey file: + + echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature + echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature + echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys + echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys + ... any other 4-hex-digit mask ... + echo reset > /proc/acpi/ibm/hotkey -- restore the original mask + +sysfs notes: + + The hot keys attributes are in a hotkey/ subdirectory off the + thinkpad device. + + bios_enabled: + Returns the status of the hot keys feature when + thinkpad-acpi was loaded. Upon module unload, the hot + key feature status will be restored to this value. + + 0: hot keys were disabled + 1: hot keys were enabled + + bios_mask: + Returns the hot keys mask when thinkpad-acpi was loaded. + Upon module unload, the hot keys mask will be restored + to this value. + + enable: + Enables/disables the hot keys feature, and reports + current status of the hot keys feature. + + 0: disables the hot keys feature / feature disabled + 1: enables the hot keys feature / feature enabled + + mask: + bit mask to enable ACPI event generation for each hot + key (see above). Returns the current status of the hot + keys mask, and allows one to modify it. + + +Bluetooth +--------- + +procfs: /proc/acpi/ibm/bluetooth +sysfs device attribute: bluetooth/enable + +This feature shows the presence and current state of a ThinkPad +Bluetooth device in the internal ThinkPad CDC slot. + +Procfs notes: + +If Bluetooth is installed, the following commands can be used: + + echo enable > /proc/acpi/ibm/bluetooth + echo disable > /proc/acpi/ibm/bluetooth + +Sysfs notes: + + If the Bluetooth CDC card is installed, it can be enabled / + disabled through the "bluetooth/enable" thinkpad-acpi device + attribute, and its current status can also be queried. + + enable: + 0: disables Bluetooth / Bluetooth is disabled + 1: enables Bluetooth / Bluetooth is enabled. + + Note: this interface will be probably be superseeded by the + generic rfkill class. + +Video output control -- /proc/acpi/ibm/video +-------------------------------------------- + +This feature allows control over the devices used for video output - +LCD, CRT or DVI (if available). The following commands are available: + + echo lcd_enable > /proc/acpi/ibm/video + echo lcd_disable > /proc/acpi/ibm/video + echo crt_enable > /proc/acpi/ibm/video + echo crt_disable > /proc/acpi/ibm/video + echo dvi_enable > /proc/acpi/ibm/video + echo dvi_disable > /proc/acpi/ibm/video + echo auto_enable > /proc/acpi/ibm/video + echo auto_disable > /proc/acpi/ibm/video + echo expand_toggle > /proc/acpi/ibm/video + echo video_switch > /proc/acpi/ibm/video + +Each video output device can be enabled or disabled individually. +Reading /proc/acpi/ibm/video shows the status of each device. + +Automatic video switching can be enabled or disabled. When automatic +video switching is enabled, certain events (e.g. opening the lid, +docking or undocking) cause the video output device to change +automatically. While this can be useful, it also causes flickering +and, on the X40, video corruption. By disabling automatic switching, +the flickering or video corruption can be avoided. + +The video_switch command cycles through the available video outputs +(it simulates the behavior of Fn-F7). + +Video expansion can be toggled through this feature. This controls +whether the display is expanded to fill the entire LCD screen when a +mode with less than full resolution is used. Note that the current +video expansion status cannot be determined through this feature. + +Note that on many models (particularly those using Radeon graphics +chips) the X driver configures the video card in a way which prevents +Fn-F7 from working. This also disables the video output switching +features of this driver, as it uses the same ACPI methods as +Fn-F7. Video switching on the console should still work. + +UPDATE: There's now a patch for the X.org Radeon driver which +addresses this issue. Some people are reporting success with the patch +while others are still having problems. For more information: + +https://bugs.freedesktop.org/show_bug.cgi?id=2000 + +ThinkLight control -- /proc/acpi/ibm/light +------------------------------------------ + +The current status of the ThinkLight can be found in this file. A few +models which do not make the status available will show it as +"unknown". The available commands are: + + echo on > /proc/acpi/ibm/light + echo off > /proc/acpi/ibm/light + +Docking / undocking -- /proc/acpi/ibm/dock +------------------------------------------ + +Docking and undocking (e.g. with the X4 UltraBase) requires some +actions to be taken by the operating system to safely make or break +the electrical connections with the dock. + +The docking feature of this driver generates the following ACPI events: + + ibm/dock GDCK 00000003 00000001 -- eject request + ibm/dock GDCK 00000003 00000002 -- undocked + ibm/dock GDCK 00000000 00000003 -- docked + +NOTE: These events will only be generated if the laptop was docked +when originally booted. This is due to the current lack of support for +hot plugging of devices in the Linux ACPI framework. If the laptop was +booted while not in the dock, the following message is shown in the +logs: + + Mar 17 01:42:34 aero kernel: thinkpad_acpi: dock device not present + +In this case, no dock-related events are generated but the dock and +undock commands described below still work. They can be executed +manually or triggered by Fn key combinations (see the example acpid +configuration files included in the driver tarball package available +on the web site). + +When the eject request button on the dock is pressed, the first event +above is generated. The handler for this event should issue the +following command: + + echo undock > /proc/acpi/ibm/dock + +After the LED on the dock goes off, it is safe to eject the laptop. +Note: if you pressed this key by mistake, go ahead and eject the +laptop, then dock it back in. Otherwise, the dock may not function as +expected. + +When the laptop is docked, the third event above is generated. The +handler for this event should issue the following command to fully +enable the dock: + + echo dock > /proc/acpi/ibm/dock + +The contents of the /proc/acpi/ibm/dock file shows the current status +of the dock, as provided by the ACPI framework. + +The docking support in this driver does not take care of enabling or +disabling any other devices you may have attached to the dock. For +example, a CD drive plugged into the UltraBase needs to be disabled or +enabled separately. See the provided example acpid configuration files +for how this can be accomplished. + +There is no support yet for PCI devices that may be attached to a +docking station, e.g. in the ThinkPad Dock II. The driver currently +does not recognize, enable or disable such devices. This means that +the only docking stations currently supported are the X-series +UltraBase docks and "dumb" port replicators like the Mini Dock (the +latter don't need any ACPI support, actually). + +UltraBay eject -- /proc/acpi/ibm/bay +------------------------------------ + +Inserting or ejecting an UltraBay device requires some actions to be +taken by the operating system to safely make or break the electrical +connections with the device. + +This feature generates the following ACPI events: + + ibm/bay MSTR 00000003 00000000 -- eject request + ibm/bay MSTR 00000001 00000000 -- eject lever inserted + +NOTE: These events will only be generated if the UltraBay was present +when the laptop was originally booted (on the X series, the UltraBay +is in the dock, so it may not be present if the laptop was undocked). +This is due to the current lack of support for hot plugging of devices +in the Linux ACPI framework. If the laptop was booted without the +UltraBay, the following message is shown in the logs: + + Mar 17 01:42:34 aero kernel: thinkpad_acpi: bay device not present + +In this case, no bay-related events are generated but the eject +command described below still works. It can be executed manually or +triggered by a hot key combination. + +Sliding the eject lever generates the first event shown above. The +handler for this event should take whatever actions are necessary to +shut down the device in the UltraBay (e.g. call idectl), then issue +the following command: + + echo eject > /proc/acpi/ibm/bay + +After the LED on the UltraBay goes off, it is safe to pull out the +device. + +When the eject lever is inserted, the second event above is +generated. The handler for this event should take whatever actions are +necessary to enable the UltraBay device (e.g. call idectl). + +The contents of the /proc/acpi/ibm/bay file shows the current status +of the UltraBay, as provided by the ACPI framework. + +EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use +this feature, you need to supply the experimental=1 parameter when +loading the module): + +These models do not have a button near the UltraBay device to request +a hot eject but rather require the laptop to be put to sleep +(suspend-to-ram) before the bay device is ejected or inserted). +The sequence of steps to eject the device is as follows: + + echo eject > /proc/acpi/ibm/bay + put the ThinkPad to sleep + remove the drive + resume from sleep + cat /proc/acpi/ibm/bay should show that the drive was removed + +On the A3x, both the UltraBay 2000 and UltraBay Plus devices are +supported. Use "eject2" instead of "eject" for the second bay. + +Note: the UltraBay eject support on the 600e/x, A22p and A3x is +EXPERIMENTAL and may not work as expected. USE WITH CAUTION! + +CMOS control +------------ + +procfs: /proc/acpi/ibm/cmos +sysfs device attribute: cmos_command + +This feature is used internally by the ACPI firmware to control the +ThinkLight on most newer ThinkPad models. It may also control LCD +brightness, sounds volume and more, but only on some models. + +The range of valid cmos command numbers is 0 to 21, but not all have an +effect and the behavior varies from model to model. Here is the behavior +on the X40 (tpb is the ThinkPad Buttons utility): + + 0 - no effect but tpb reports "Volume down" + 1 - no effect but tpb reports "Volume up" + 2 - no effect but tpb reports "Mute on" + 3 - simulate pressing the "Access IBM" button + 4 - LCD brightness up + 5 - LCD brightness down + 11 - toggle screen expansion + 12 - ThinkLight on + 13 - ThinkLight off + 14 - no effect but tpb reports ThinkLight status change + +The cmos command interface is prone to firmware split-brain problems, as +in newer ThinkPads it is just a compatibility layer. + +LED control -- /proc/acpi/ibm/led +--------------------------------- + +Some of the LED indicators can be controlled through this feature. The +available commands are: + + echo ' on' >/proc/acpi/ibm/led + echo ' off' >/proc/acpi/ibm/led + echo ' blink' >/proc/acpi/ibm/led + +The range is 0 to 7. The set of LEDs that can be +controlled varies from model to model. Here is the mapping on the X40: + + 0 - power + 1 - battery (orange) + 2 - battery (green) + 3 - UltraBase + 4 - UltraBay + 7 - standby + +All of the above can be turned on and off and can be made to blink. + +ACPI sounds -- /proc/acpi/ibm/beep +---------------------------------- + +The BEEP method is used internally by the ACPI firmware to provide +audible alerts in various situations. This feature allows the same +sounds to be triggered manually. + +The commands are non-negative integer numbers: + + echo >/proc/acpi/ibm/beep + +The valid range is 0 to 17. Not all numbers trigger sounds +and the sounds vary from model to model. Here is the behavior on the +X40: + + 0 - stop a sound in progress (but use 17 to stop 16) + 2 - two beeps, pause, third beep ("low battery") + 3 - single beep + 4 - high, followed by low-pitched beep ("unable") + 5 - single beep + 6 - very high, followed by high-pitched beep ("AC/DC") + 7 - high-pitched beep + 9 - three short beeps + 10 - very long beep + 12 - low-pitched beep + 15 - three high-pitched beeps repeating constantly, stop with 0 + 16 - one medium-pitched beep repeating constantly, stop with 17 + 17 - stop 16 + +Temperature sensors +------------------- + +procfs: /proc/acpi/ibm/thermal +sysfs device attributes: (hwmon) temp*_input + +Most ThinkPads include six or more separate temperature sensors but +only expose the CPU temperature through the standard ACPI methods. +This feature shows readings from up to eight different sensors on older +ThinkPads, and it has experimental support for up to sixteen different +sensors on newer ThinkPads. + +EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the +implementation directly accesses hardware registers and may not work as +expected. USE WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. When EXPERIMENTAL +mode is enabled, reading the first 8 sensors on newer ThinkPads will +also use an new experimental thermal sensor access mode. + +For example, on the X40, a typical output may be: +temperatures: 42 42 45 41 36 -128 33 -128 + +EXPERIMENTAL: On the T43/p, a typical output may be: +temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128 + +The mapping of thermal sensors to physical locations varies depending on +system-board model (and thus, on ThinkPad model). + +http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that +tries to track down these locations for various models. + +Most (newer?) models seem to follow this pattern: + +1: CPU +2: (depends on model) +3: (depends on model) +4: GPU +5: Main battery: main sensor +6: Bay battery: main sensor +7: Main battery: secondary sensor +8: Bay battery: secondary sensor +9-15: (depends on model) + +For the R51 (source: Thomas Gruber): +2: Mini-PCI +3: Internal HDD + +For the T43, T43/p (source: Shmidoax/Thinkwiki.org) +http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p +2: System board, left side (near PCMCIA slot), reported as HDAPS temp +3: PCMCIA slot +9: MCH (northbridge) to DRAM Bus +10: ICH (southbridge), under Mini-PCI card, under touchpad +11: Power regulator, underside of system board, below F2 key + +The A31 has a very atypical layout for the thermal sensors +(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31) +1: CPU +2: Main Battery: main sensor +3: Power Converter +4: Bay Battery: main sensor +5: MCH (northbridge) +6: PCMCIA/ambient +7: Main Battery: secondary sensor +8: Bay Battery: secondary sensor + + +Procfs notes: + Readings from sensors that are not available return -128. + No commands can be written to this file. + +Sysfs notes: + Sensors that are not available return the ENXIO error. This + status may change at runtime, as there are hotplug thermal + sensors, like those inside the batteries and docks. + + thinkpad-acpi thermal sensors are reported through the hwmon + subsystem, and follow all of the hwmon guidelines at + Documentation/hwmon. + + +EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump +------------------------------------------------------------------------ + +This feature is marked EXPERIMENTAL because the implementation +directly accesses hardware registers and may not work as expected. USE +WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. + +This feature dumps the values of 256 embedded controller +registers. Values which have changed since the last time the registers +were dumped are marked with a star: + +[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump +EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f +EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 +EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 +EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 +EC 0x30: 01 07 1a 00 30 04 00 00 *85 00 00 10 00 50 00 00 +EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 +EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 *bc *02 *bc +EC 0x60: *02 *bc *02 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0x70: 00 00 00 00 00 12 30 40 *24 *26 *2c *27 *20 80 *1f 80 +EC 0x80: 00 00 00 06 *37 *0e 03 00 00 00 0e 07 00 00 00 00 +EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xa0: *ff 09 ff 09 ff ff *64 00 *00 *00 *a2 41 *ff *ff *e0 00 +EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 +EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a + +This feature can be used to determine the register holding the fan +speed on some models. To do that, do the following: + + - make sure the battery is fully charged + - make sure the fan is running + - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so + +The first step makes sure various charging-related values don't +vary. The second ensures that the fan-related values do vary, since +the fan speed fluctuates a bit. The third will (hopefully) mark the +fan register with a star: + +[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump +EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f +EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 +EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 +EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 +EC 0x30: 01 07 1a 00 30 04 00 00 85 00 00 10 00 50 00 00 +EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 +EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 bc 02 bc +EC 0x60: 02 bc 02 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0x70: 00 00 00 00 00 12 30 40 24 27 2c 27 21 80 1f 80 +EC 0x80: 00 00 00 06 *be 0d 03 00 00 00 0e 07 00 00 00 00 +EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xa0: ff 09 ff 09 ff ff 64 00 00 00 a2 41 ff ff e0 00 +EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 +EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a + +Another set of values that varies often is the temperature +readings. Since temperatures don't change vary fast, you can take +several quick dumps to eliminate them. + +You can use a similar method to figure out the meaning of other +embedded controller registers - e.g. make sure nothing else changes +except the charging or discharging battery to determine which +registers contain the current battery capacity, etc. If you experiment +with this, do send me your results (including some complete dumps with +a description of the conditions when they were taken.) + +LCD brightness control +---------------------- + +procfs: /proc/acpi/ibm/brightness +sysfs backlight device "thinkpad_screen" + +This feature allows software control of the LCD brightness on ThinkPad +models which don't have a hardware brightness slider. + +It has some limitations: the LCD backlight cannot be actually turned on or off +by this interface, and in many ThinkPad models, the "dim while on battery" +functionality will be enabled by the BIOS when this interface is used, and +cannot be controlled. + +The backlight control has eight levels, ranging from 0 to 7. Some of the +levels may not be distinct. + +Procfs notes: + + The available commands are: + + echo up >/proc/acpi/ibm/brightness + echo down >/proc/acpi/ibm/brightness + echo 'level ' >/proc/acpi/ibm/brightness + +Sysfs notes: + +The interface is implemented through the backlight sysfs class, which is poorly +documented at this time. + +Locate the thinkpad_screen device under /sys/class/backlight, and inside it +there will be the following attributes: + + max_brightness: + Reads the maximum brightness the hardware can be set to. + The minimum is always zero. + + actual_brightness: + Reads what brightness the screen is set to at this instant. + + brightness: + Writes request the driver to change brightness to the given + value. Reads will tell you what brightness the driver is trying + to set the display to when "power" is set to zero and the display + has not been dimmed by a kernel power management event. + + power: + power management mode, where 0 is "display on", and 1 to 3 will + dim the display backlight to brightness level 0 because + thinkpad-acpi cannot really turn the backlight off. Kernel + power management events can temporarily increase the current + power management level, i.e. they can dim the display. + + +Volume control -- /proc/acpi/ibm/volume +--------------------------------------- + +This feature allows volume control on ThinkPad models which don't have +a hardware volume knob. The available commands are: + + echo up >/proc/acpi/ibm/volume + echo down >/proc/acpi/ibm/volume + echo mute >/proc/acpi/ibm/volume + echo 'level ' >/proc/acpi/ibm/volume + +The number range is 0 to 15 although not all of them may be +distinct. The unmute the volume after the mute command, use either the +up or down command (the level command will not unmute the volume). +The current volume level and mute state is shown in the file. + +Fan control and monitoring: fan speed, fan enable/disable +--------------------------------------------------------- + +procfs: /proc/acpi/ibm/fan +sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable + +NOTE NOTE NOTE: fan control operations are disabled by default for +safety reasons. To enable them, the module parameter "fan_control=1" +must be given to thinkpad-acpi. + +This feature attempts to show the current fan speed, control mode and +other fan data that might be available. The speed is read directly +from the hardware registers of the embedded controller. This is known +to work on later R, T, X and Z series ThinkPads but may show a bogus +value on other models. + +Fan levels: + +Most ThinkPad fans work in "levels" at the firmware interface. Level 0 +stops the fan. The higher the level, the higher the fan speed, although +adjacent levels often map to the same fan speed. 7 is the highest +level, where the fan reaches the maximum recommended speed. + +Level "auto" means the EC changes the fan level according to some +internal algorithm, usually based on readings from the thermal sensors. + +There is also a "full-speed" level, also known as "disengaged" level. +In this level, the EC disables the speed-locked closed-loop fan control, +and drives the fan as fast as it can go, which might exceed hardware +limits, so use this level with caution. + +The fan usually ramps up or down slowly from one speed to another, and +it is normal for the EC to take several seconds to react to fan +commands. The full-speed level may take up to two minutes to ramp up to +maximum speed, and in some ThinkPads, the tachometer readings go stale +while the EC is transitioning to the full-speed level. + +WARNING WARNING WARNING: do not leave the fan disabled unless you are +monitoring all of the temperature sensor readings and you are ready to +enable it if necessary to avoid overheating. + +An enabled fan in level "auto" may stop spinning if the EC decides the +ThinkPad is cool enough and doesn't need the extra airflow. This is +normal, and the EC will spin the fan up if the varios thermal readings +rise too much. + +On the X40, this seems to depend on the CPU and HDD temperatures. +Specifically, the fan is turned on when either the CPU temperature +climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The +fan is turned off when the CPU temperature drops to 49 degrees and the +HDD temperature drops to 41 degrees. These thresholds cannot +currently be controlled. + +The ThinkPad's ACPI DSDT code will reprogram the fan on its own when +certain conditions are met. It will override any fan programming done +through thinkpad-acpi. + +The thinkpad-acpi kernel driver can be programmed to revert the fan +level to a safe setting if userspace does not issue one of the procfs +fan commands: "enable", "disable", "level" or "watchdog", or if there +are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is +set to 1, manual mode) within a configurable amount of time of up to +120 seconds. This functionality is called fan safety watchdog. + +Note that the watchdog timer stops after it enables the fan. It will be +rearmed again automatically (using the same interval) when one of the +above mentioned fan commands is received. The fan watchdog is, +therefore, not suitable to protect against fan mode changes made through +means other than the "enable", "disable", and "level" procfs fan +commands, or the hwmon fan control sysfs interface. + +Procfs notes: + +The fan may be enabled or disabled with the following commands: + + echo enable >/proc/acpi/ibm/fan + echo disable >/proc/acpi/ibm/fan + +Placing a fan on level 0 is the same as disabling it. Enabling a fan +will try to place it in a safe level if it is too slow or disabled. + +The fan level can be controlled with the command: + + echo 'level ' > /proc/acpi/ibm/fan + +Where is an integer from 0 to 7, or one of the words "auto" or +"full-speed" (without the quotes). Not all ThinkPads support the "auto" +and "full-speed" levels. The driver accepts "disengaged" as an alias for +"full-speed", and reports it as "disengaged" for backwards +compatibility. + +On the X31 and X40 (and ONLY on those models), the fan speed can be +controlled to a certain degree. Once the fan is running, it can be +forced to run faster or slower with the following command: + + echo 'speed ' > /proc/acpi/ibm/fan + +The sustainable range of fan speeds on the X40 appears to be from about +3700 to about 7350. Values outside this range either do not have any +effect or the fan speed eventually settles somewhere in that range. The +fan cannot be stopped or started with this command. This functionality +is incomplete, and not available through the sysfs interface. + +To program the safety watchdog, use the "watchdog" command. + + echo 'watchdog ' > /proc/acpi/ibm/fan + +If you want to disable the watchdog, use 0 as the interval. + +Sysfs notes: + +The sysfs interface follows the hwmon subsystem guidelines for the most +part, and the exception is the fan safety watchdog. + +Writes to any of the sysfs attributes may return the EINVAL error if +that operation is not supported in a given ThinkPad or if the parameter +is out-of-bounds, and EPERM if it is forbidden. They may also return +EINTR (interrupted system call), and EIO (I/O error while trying to talk +to the firmware). + +Features not yet implemented by the driver return ENOSYS. + +hwmon device attribute pwm1_enable: + 0: PWM offline (fan is set to full-speed mode) + 1: Manual PWM control (use pwm1 to set fan level) + 2: Hardware PWM control (EC "auto" mode) + 3: reserved (Software PWM control, not implemented yet) + + Modes 0 and 2 are not supported by all ThinkPads, and the + driver is not always able to detect this. If it does know a + mode is unsupported, it will return -EINVAL. + +hwmon device attribute pwm1: + Fan level, scaled from the firmware values of 0-7 to the hwmon + scale of 0-255. 0 means fan stopped, 255 means highest normal + speed (level 7). + + This attribute only commands the fan if pmw1_enable is set to 1 + (manual PWM control). + +hwmon device attribute fan1_input: + Fan tachometer reading, in RPM. May go stale on certain + ThinkPads while the EC transitions the PWM to offline mode, + which can take up to two minutes. May return rubbish on older + ThinkPads. + +driver attribute fan_watchdog: + Fan safety watchdog timer interval, in seconds. Minimum is + 1 second, maximum is 120 seconds. 0 disables the watchdog. + +To stop the fan: set pwm1 to zero, and pwm1_enable to 1. + +To start the fan in a safe mode: set pwm1_enable to 2. If that fails +with EINVAL, try to set pwm1_enable to 1 and pwm1 to at least 128 (255 +would be the safest choice, though). + + +EXPERIMENTAL: WAN +----------------- + +procfs: /proc/acpi/ibm/wan +sysfs device attribute: wwan/enable + +This feature is marked EXPERIMENTAL because the implementation +directly accesses hardware registers and may not work as expected. USE +WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. + +This feature shows the presence and current state of a W-WAN (Sierra +Wireless EV-DO) device. + +It was tested on a Lenovo Thinkpad X60. It should probably work on other +Thinkpad models which come with this module installed. + +Procfs notes: + +If the W-WAN card is installed, the following commands can be used: + + echo enable > /proc/acpi/ibm/wan + echo disable > /proc/acpi/ibm/wan + +Sysfs notes: + + If the W-WAN card is installed, it can be enabled / + disabled through the "wwan/enable" thinkpad-acpi device + attribute, and its current status can also be queried. + + enable: + 0: disables WWAN card / WWAN card is disabled + 1: enables WWAN card / WWAN card is enabled. + + Note: this interface will be probably be superseeded by the + generic rfkill class. + +Multiple Commands, Module Parameters +------------------------------------ + +Multiple commands can be written to the proc files in one shot by +separating them with commas, for example: + + echo enable,0xffff > /proc/acpi/ibm/hotkey + echo lcd_disable,crt_enable > /proc/acpi/ibm/video + +Commands can also be specified when loading the thinkpad-acpi module, +for example: + + modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable + +Enabling debugging output +------------------------- + +The module takes a debug paramater which can be used to selectively +enable various classes of debugging output, for example: + + modprobe ibm_acpi debug=0xffff + +will enable all debugging output classes. It takes a bitmask, so +to enable more than one output class, just add their values. + + Debug bitmask Description + 0x0001 Initialization and probing + 0x0002 Removal + +There is also a kernel build option to enable more debugging +information, which may be necessary to debug driver problems. + +The level of debugging information output by the driver can be changed +at runtime through sysfs, using the driver attribute debug_level. The +attribute takes the same bitmask as the debug module parameter above. + +Force loading of module +----------------------- + +If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify +the module parameter force_load=1. Regardless of whether this works or +not, please contact ibm-acpi-devel@lists.sourceforge.net with a report. + + +Sysfs interface changelog: + +0x000100: Initial sysfs support, as a single platform driver and + device. diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index 0f6808a..53ae866 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -16,7 +16,7 @@ situation as with tcpdump. Unlike the packet socket, usbmon has an interface which provides traces in a text format. This is used for two purposes. First, it serves as a -common trace exchange format for tools while most sophisticated formats +common trace exchange format for tools while more sophisticated formats are finalized. Second, humans can read it in case tools are not available. To collect a raw text trace, execute following steps. @@ -34,7 +34,7 @@ # Verify that bus sockets are present. # ls /sys/kernel/debug/usbmon -1s 1t 2s 2t 3s 3t 4s 4t +1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u # 2. Find which bus connects to the desired device @@ -54,7 +54,7 @@ Bus=03 means it's bus 3. 3. Start 'cat' -# cat /sys/kernel/debug/usbmon/3t > /tmp/1.mon.out +# cat /sys/kernel/debug/usbmon/3u > /tmp/1.mon.out This process will be reading until killed. Naturally, the output can be redirected to a desirable location. This is preferred, because it is going @@ -75,46 +75,80 @@ that the file size is not excessive for * Raw text data format -The '1t' type data consists of a stream of events, such as URB submission, +Two formats are supported currently: the original, or '1t' format, and +the '1u' format. The '1t' format is deprecated in kernel 2.6.21. The '1u' +format adds a few fields, such as ISO frame descriptors, interval, etc. +It produces slightly longer lines, but otherwise is a perfect superset +of '1t' format. + +If it is desired to recognize one from the other in a program, look at the +"address" word (see below), where '1u' format adds a bus number. If 2 colons +are present, it's the '1t' format, otherwise '1u'. + +Any text format data consists of a stream of events, such as URB submission, URB callback, submission error. Every event is a text line, which consists of whitespace separated words. The number or position of words may depend on the event type, but there is a set of words, common for all types. Here is the list of words, from left to right: + - URB Tag. This is used to identify URBs is normally a kernel mode address of the URB structure in hexadecimal. + - Timestamp in microseconds, a decimal number. The timestamp's resolution depends on available clock, and so it can be much worse than a microsecond (if the implementation uses jiffies, for example). + - Event Type. This type refers to the format of the event, not URB type. Available types are: S - submission, C - callback, E - submission error. -- "Pipe". The pipe concept is deprecated. This is a composite word, used to - be derived from information in pipes. It consists of three fields, separated - by colons: URB type and direction, Device address, Endpoint number. + +- "Address" word (formerly a "pipe"). It consists of four fields, separated by + colons: URB type and direction, Bus number, Device address, Endpoint number. Type and direction are encoded with two bytes in the following manner: Ci Co Control input and output Zi Zo Isochronous input and output Ii Io Interrupt input and output Bi Bo Bulk input and output - Device address and Endpoint number are 3-digit and 2-digit (respectively) - decimal numbers, with leading zeroes. -- URB Status. In most cases, this field contains a number, sometimes negative, - which represents a "status" field of the URB. This field makes no sense for - submissions, but is present anyway to help scripts with parsing. When an - error occurs, the field contains the error code. In case of a submission of - a Control packet, this field contains a Setup Tag instead of an error code. - It is easy to tell whether the Setup Tag is present because it is never a - number. Thus if scripts find a number in this field, they proceed to read - Data Length. If they find something else, like a letter, they read the setup - packet before reading the Data Length. + Bus number, Device address, and Endpoint are decimal numbers, but they may + have leading zeros, for the sake of human readers. + +- URB Status word. This is either a letter, or several numbers separated + by colons: URB status, interval, start frame, and error count. Unlike the + "address" word, all fields save the status are optional. Interval is printed + only for interrupt and isochronous URBs. Start frame is printed only for + isochronous URBs. Error count is printed only for isochronous callback + events. + + The status field is a decimal number, sometimes negative, which represents + a "status" field of the URB. This field makes no sense for submissions, but + is present anyway to help scripts with parsing. When an error occurs, the + field contains the error code. + + In case of a submission of a Control packet, this field contains a Setup Tag + instead of an group of numbers. It is easy to tell whether the Setup Tag is + present because it is never a number. Thus if scripts find a set of numbers + in this word, they proceed to read Data Length (except for isochronous URBs). + If they find something else, like a letter, they read the setup packet before + reading the Data Length or isochronous descriptors. + - Setup packet, if present, consists of 5 words: one of each for bmRequestType, bRequest, wValue, wIndex, wLength, as specified by the USB Specification 2.0. These words are safe to decode if Setup Tag was 's'. Otherwise, the setup packet was present, but not captured, and the fields contain filler. + +- Number of isochronous frame descriptors and descriptors themselves. + If an Isochronous transfer event has a set of descriptors, a total number + of them in an URB is printed first, then a word per descriptor, up to a + total of 5. The word consists of 3 colon-separated decimal numbers for + status, offset, and length respectively. For submissions, initial length + is reported. For callbacks, actual length is reported. + - Data Length. For submissions, this is the requested length. For callbacks, this is the actual length. + - Data tag. The usbmon may not always capture data, even if length is nonzero. The data words are present only if this tag is '='. + - Data words follow, in big endian hexadecimal format. Notice that they are not machine words, but really just a byte stream split into words to make it easier to read. Thus, the last word may contain from one to four bytes. @@ -153,20 +187,18 @@ class ParsedLine { } } -This format may be changed in the future. - Examples: An input control transfer to get a port status. -d5ea89a0 3575914555 S Ci:001:00 s a3 00 0000 0003 0004 4 < -d5ea89a0 3575914560 C Ci:001:00 0 4 = 01050000 +d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 < +d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000 An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper to a storage device at address 5: -dd65f0e8 4128379752 S Bo:005:02 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000 -dd65f0e8 4128379808 C Bo:005:02 0 31 > +dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000 +dd65f0e8 4128379808 C Bo:1:005:2 0 31 > * Raw binary format and API diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index fc2fe9b..b606391 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -143,3 +143,5 @@ 142 -> Sabrent TV-FM (bttv version) 143 -> Hauppauge ImpactVCB (bt878) [0070:13eb] 144 -> MagicTV +145 -> SSAI Security Video Interface [4149:5353] +146 -> SSAI Ultrasound Video Interface [414a:5353] diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index 62e32b4..60f838b 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -37,7 +37,7 @@ 36 -> AVerTV 303 (M126) [1461:000a] 37 -> Hauppauge Nova-S-Plus DVB-S [0070:9201,0070:9202] 38 -> Hauppauge Nova-SE2 DVB-S [0070:9200] - 39 -> KWorld DVB-S 100 [17de:08b2] + 39 -> KWorld DVB-S 100 [17de:08b2,1421:0341] 40 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid [0070:9400,0070:9402] 41 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile) [0070:9800,0070:9802] 42 -> digitalnow DNTV Live! DVB-T Pro [1822:0025,1822:0019] diff --git a/Documentation/video4linux/CARDLIST.ivtv b/Documentation/video4linux/CARDLIST.ivtv new file mode 100644 index 0000000..ddd76a0 --- /dev/null +++ b/Documentation/video4linux/CARDLIST.ivtv @@ -0,0 +1,18 @@ + 1 -> Hauppauge WinTV PVR-250 + 2 -> Hauppauge WinTV PVR-350 + 3 -> Hauppauge WinTV PVR-150 or PVR-500 + 4 -> AVerMedia M179 [1461:a3ce,1461:a3cf] + 5 -> Yuan MPG600/Kuroutoshikou iTVC16-STVLP [12ab:fff3,12ab:ffff] + 6 -> Yuan MPG160/Kuroutoshikou iTVC15-STVLP [12ab:0000,10fc:40a0] + 7 -> Yuan PG600/DiamondMM PVR-550 [ff92:0070,ffab:0600] + 8 -> Adaptec AVC-2410 [9005:0093] + 9 -> Adaptec AVC-2010 [9005:0092] +10 -> NAGASE TRANSGEAR 5000TV [1461:bfff] +11 -> AOpen VA2000MAX-STN6 [0000:ff5f] +12 -> YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP [12ab:0600,fbab:0600,1154:0523] +13 -> I/O Data GV-MVP/RX [10fc:d01e,10fc:d038,10fc:d039] +14 -> I/O Data GV-MVP/RX2E [10fc:d025] +15 -> GOTVIEW PCI DVD (partial support only) [12ab:0600] +16 -> GOTVIEW PCI DVD2 Deluxe [ffac:0600] +17 -> Yuan MPC622 [ff01:d998] +18 -> Digital Cowboy DCT-MTVP1 [1461:bfff] diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index a12246a..d7bb2e2 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -53,7 +53,7 @@ 52 -> AverMedia AverTV/305 [1461:2108] 53 -> ASUS TV-FM 7135 [1043:4845] 54 -> LifeView FlyTV Platinum FM / Gold [5168:0214,1489:0214,5168:0304] - 55 -> LifeView FlyDVB-T DUO [5168:0306] + 55 -> LifeView FlyDVB-T DUO / MSI TV@nywhere Duo [5168:0306,4E42:0306] 56 -> Avermedia AVerTV 307 [1461:a70a] 57 -> Avermedia AVerTV GO 007 FM [1461:f31f] 58 -> ADS Tech Instant TV (saa7135) [1421:0350,1421:0351,1421:0370,1421:1370] @@ -76,7 +76,7 @@ 75 -> AVerMedia AVerTVHD MCE A180 [1461:1044] 76 -> SKNet MonsterTV Mobile [1131:4ee9] 77 -> Pinnacle PCTV 40i/50i/110i (saa7133) [11bd:002e] - 78 -> ASUSTeK P7131 Dual [1043:4862,1043:4876] + 78 -> ASUSTeK P7131 Dual [1043:4862,1043:4857] 79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B) 80 -> ASUS Digimatrix TV [1043:0210] 81 -> Philips Tiger reference design [1131:2018] @@ -107,3 +107,7 @@ 106 -> Encore ENLTV [1131:2342,1131:2341,3016:2344] 107 -> Encore ENLTV-FM [1131:230f] 108 -> Terratec Cinergy HT PCI [153b:1175] +109 -> Philips Tiger - S Reference design +110 -> Avermedia M102 [1461:f31e] +111 -> ASUS P7131 4871 [1043:4871] +112 -> ASUSTeK P7131 Hybrid [1043:4876] diff --git a/Documentation/video4linux/CARDLIST.usbvision b/Documentation/video4linux/CARDLIST.usbvision new file mode 100644 index 0000000..3d6850e --- /dev/null +++ b/Documentation/video4linux/CARDLIST.usbvision @@ -0,0 +1,64 @@ + 0 -> Xanboo [0a6f:0400] + 1 -> Belkin USB VideoBus II Adapter [050d:0106] + 2 -> Belkin Components USB VideoBus [050d:0207] + 3 -> Belkin USB VideoBus II [050d:0208] + 4 -> echoFX InterView Lite [0571:0002] + 5 -> USBGear USBG-V1 resp. HAMA USB [0573:0003] + 6 -> D-Link V100 [0573:0400] + 7 -> X10 USB Camera [0573:2000] + 8 -> Hauppauge WinTV USB Live (PAL B/G) [0573:2d00] + 9 -> Hauppauge WinTV USB Live Pro (NTSC M/N) [0573:2d01] + 10 -> Zoran Co. PMD (Nogatech) AV-grabber Manhattan [0573:2101] + 11 -> Nogatech USB-TV (NTSC) FM [0573:4100] + 12 -> PNY USB-TV (NTSC) FM [0573:4110] + 13 -> PixelView PlayTv-USB PRO (PAL) FM [0573:4450] + 14 -> ZTV ZT-721 2.4GHz USB A/V Receiver [0573:4550] + 15 -> Hauppauge WinTV USB (NTSC M/N) [0573:4d00] + 16 -> Hauppauge WinTV USB (PAL B/G) [0573:4d01] + 17 -> Hauppauge WinTV USB (PAL I) [0573:4d02] + 18 -> Hauppauge WinTV USB (PAL/SECAM L) [0573:4d03] + 19 -> Hauppauge WinTV USB (PAL D/K) [0573:4d04] + 20 -> Hauppauge WinTV USB (NTSC FM) [0573:4d10] + 21 -> Hauppauge WinTV USB (PAL B/G FM) [0573:4d11] + 22 -> Hauppauge WinTV USB (PAL I FM) [0573:4d12] + 23 -> Hauppauge WinTV USB (PAL D/K FM) [0573:4d14] + 24 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d2a] + 25 -> Hauppauge WinTV USB Pro (NTSC M/N) V2 [0573:4d2b] + 26 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L) [0573:4d2c] + 27 -> Hauppauge WinTV USB Pro (NTSC M/N) V3 [0573:4d20] + 28 -> Hauppauge WinTV USB Pro (PAL B/G) [0573:4d21] + 29 -> Hauppauge WinTV USB Pro (PAL I) [0573:4d22] + 30 -> Hauppauge WinTV USB Pro (PAL/SECAM L) [0573:4d23] + 31 -> Hauppauge WinTV USB Pro (PAL D/K) [0573:4d24] + 32 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) [0573:4d25] + 33 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2 [0573:4d26] + 34 -> Hauppauge WinTV USB Pro (PAL B/G) V2 [0573:4d27] + 35 -> Hauppauge WinTV USB Pro (PAL B/G,D/K) [0573:4d28] + 36 -> Hauppauge WinTV USB Pro (PAL I,D/K) [0573:4d29] + 37 -> Hauppauge WinTV USB Pro (NTSC M/N FM) [0573:4d30] + 38 -> Hauppauge WinTV USB Pro (PAL B/G FM) [0573:4d31] + 39 -> Hauppauge WinTV USB Pro (PAL I FM) [0573:4d32] + 40 -> Hauppauge WinTV USB Pro (PAL D/K FM) [0573:4d34] + 41 -> Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM) [0573:4d35] + 42 -> Hauppauge WinTV USB Pro (Temic PAL B/G FM) [0573:4d36] + 43 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM) [0573:4d37] + 44 -> Hauppauge WinTV USB Pro (NTSC M/N FM) V2 [0573:4d38] + 45 -> Camtel Technology USB TV Genie Pro FM Model TVB330 [0768:0006] + 46 -> Digital Video Creator I [07d0:0001] + 47 -> Global Village GV-007 (NTSC) [07d0:0002] + 48 -> Dazzle Fusion Model DVC-50 Rev 1 (NTSC) [07d0:0003] + 49 -> Dazzle Fusion Model DVC-80 Rev 1 (PAL) [07d0:0004] + 50 -> Dazzle Fusion Model DVC-90 Rev 1 (SECAM) [07d0:0005] + 51 -> Eskape Labs MyTV2Go [07f8:9104] + 52 -> Pinnacle Studio PCTV USB (PAL) [2304:010d] + 53 -> Pinnacle Studio PCTV USB (SECAM) [2304:0109] + 54 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0110] + 55 -> Miro PCTV USB [2304:0111] + 56 -> Pinnacle Studio PCTV USB (NTSC) FM [2304:0112] + 57 -> Pinnacle Studio PCTV USB (PAL) FM V2 [2304:0210] + 58 -> Pinnacle Studio PCTV USB (NTSC) FM V2 [2304:0212] + 59 -> Pinnacle Studio PCTV USB (PAL) FM V3 [2304:0214] + 60 -> Pinnacle Studio Linx Video input cable (NTSC) [2304:0300] + 61 -> Pinnacle Studio Linx Video input cable (PAL) [2304:0301] + 62 -> Pinnacle PCTV Bungee USB (PAL) FM [2304:0419] + 63 -> Hauppauge WinTv-USB [2400:4200] diff --git a/Documentation/video4linux/README.ivtv b/Documentation/video4linux/README.ivtv new file mode 100644 index 0000000..73df22c --- /dev/null +++ b/Documentation/video4linux/README.ivtv @@ -0,0 +1,187 @@ + +ivtv release notes +================== + +This is a v4l2 device driver for the Conexant cx23415/6 MPEG encoder/decoder. +The cx23415 can do both encoding and decoding, the cx23416 can only do MPEG +encoding. Currently the only card featuring full decoding support is the +Hauppauge PVR-350. + +NOTE: this driver requires the latest encoder firmware (version 2.06.039, size +376836 bytes). Get the firmware from here: + +http://dl.ivtvdriver.org/ivtv/firmware/firmware.tar.gz + +NOTE: 'normal' TV applications do not work with this driver, you need +an application that can handle MPEG input such as mplayer, xine, MythTV, +etc. + +The primary goal of the IVTV project is to provide a "clean room" Linux +Open Source driver implementation for video capture cards based on the +iCompression iTVC15 or Conexant CX23415/CX23416 MPEG Codec. + +Features: + * Hardware mpeg2 capture of broadcast video (and sound) via the tuner or + S-Video/Composite and audio line-in. + * Hardware mpeg2 capture of FM radio where hardware support exists + * Supports NTSC, PAL, SECAM with stereo sound + * Supports SAP and bilingual transmissions. + * Supports raw VBI (closed captions and teletext). + * Supports sliced VBI (closed captions and teletext) and is able to insert + this into the captured MPEG stream. + * Supports raw YUV and PCM input. + +Additional features for the PVR-350 (CX23415 based): + * Provides hardware mpeg2 playback + * Provides comprehensive OSD (On Screen Display: ie. graphics overlaying the + video signal) + * Provides a framebuffer (allowing X applications to appear on the video + device) (this framebuffer is not yet part of the kernel. In the meantime it + is available from www.ivtvdriver.org). + * Supports raw YUV output. + +IMPORTANT: In case of problems first read this page: + http://www.ivtvdriver.org/index.php/Troubleshooting + +See also: + +Homepage + Wiki +http://www.ivtvdriver.org + +IRC +irc://irc.freenode.net/ivtv-dev + +---------------------------------------------------------- + +Devices +======= + +A maximum of 12 ivtv boards are allowed at the moment. + +Cards that don't have a video output capability (i.e. non PVR350 cards) +lack the vbi8, vbi16, video16 and video48 devices. They also do not +support the framebuffer device /dev/fbx for OSD. + +The radio0 device may or may not be present, depending on whether the +card has a radio tuner or not. + +Here is a list of the base v4l devices: +crw-rw---- 1 root video 81, 0 Jun 19 22:22 /dev/video0 +crw-rw---- 1 root video 81, 16 Jun 19 22:22 /dev/video16 +crw-rw---- 1 root video 81, 24 Jun 19 22:22 /dev/video24 +crw-rw---- 1 root video 81, 32 Jun 19 22:22 /dev/video32 +crw-rw---- 1 root video 81, 48 Jun 19 22:22 /dev/video48 +crw-rw---- 1 root video 81, 64 Jun 19 22:22 /dev/radio0 +crw-rw---- 1 root video 81, 224 Jun 19 22:22 /dev/vbi0 +crw-rw---- 1 root video 81, 228 Jun 19 22:22 /dev/vbi8 +crw-rw---- 1 root video 81, 232 Jun 19 22:22 /dev/vbi16 + +Base devices +============ + +For every extra card you have the numbers increased by one. For example, +/dev/video0 is listed as the 'base' encoding capture device so we have: + + /dev/video0 is the encoding capture device for the first card (card 0) + /dev/video1 is the encoding capture device for the second card (card 1) + /dev/video2 is the encoding capture device for the third card (card 2) + +Note that if the first card doesn't have a feature (eg no decoder, so no +video16, the second card will still use video17. The simple rule is 'add +the card number to the base device number'. If you have other capture +cards (e.g. WinTV PCI) that are detected first, then you have to tell +the ivtv module about it so that it will start counting at 1 (or 2, or +whatever). Otherwise the device numbers can get confusing. The ivtv +'ivtv_first_minor' module option can be used for that. + + +/dev/video0 +The encoding capture device(s). +Read-only. + +Reading from this device gets you the MPEG1/2 program stream. +Example: + +cat /dev/video0 > my.mpg (you need to hit ctrl-c to exit) + + +/dev/video16 +The decoder output device(s) +Write-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +An mpeg2 stream sent to this device will appear on the selected video +display, audio will appear on the line-out/audio out. It is only +available for cards that support video out. Example: + +cat my.mpg >/dev/video16 + + +/dev/video24 +The raw audio capture device(s). +Read-only + +The raw audio PCM stereo stream from the currently selected +tuner or audio line-in. Reading from this device results in a raw +(signed 16 bit Little Endian, 48000 Hz, stereo pcm) capture. +This device only captures audio. This should be replaced by an ALSA +device in the future. +Note that there is no corresponding raw audio output device, this is +not supported in the decoder firmware. + + +/dev/video32 +The raw video capture device(s) +Read-only + +The raw YUV video output from the current video input. The YUV format +is non-standard (V4L2_PIX_FMT_HM12). + +Note that the YUV and PCM streams are not synchronized, so they are of +limited use. + + +/dev/video48 +The raw video display device(s) +Write-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +Writes a YUV stream to the decoder of the card. + + +/dev/radio0 +The radio tuner device(s) +Cannot be read or written. + +Used to enable the radio tuner and tune to a frequency. You cannot +read or write audio streams with this device. Once you use this +device to tune the radio, use /dev/video24 to read the raw pcm stream +or /dev/video0 to get an mpeg2 stream with black video. + + +/dev/vbi0 +The 'vertical blank interval' (Teletext, CC, WSS etc) capture device(s) +Read-only + +Captures the raw (or sliced) video data sent during the Vertical Blank +Interval. This data is used to encode teletext, closed captions, VPS, +widescreen signalling, electronic program guide information, and other +services. + + +/dev/vbi8 +Processed vbi feedback device(s) +Read-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +The sliced VBI data embedded in an MPEG stream is reproduced on this +device. So while playing back a recording on /dev/video16, you can +read the embedded VBI data from /dev/vbi8. + + +/dev/vbi16 +The vbi 'display' device(s) +Write-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +Can be used to send sliced VBI data to the video-out connector. + +--------------------------------- + +Hans Verkuil diff --git a/Documentation/video4linux/cx2341x/fw-decoder-regs.txt b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt index db2366c..cf52c8f 100644 --- a/Documentation/video4linux/cx2341x/fw-decoder-regs.txt +++ b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt @@ -624,11 +624,11 @@ out what values are bad when it hangs. 2A00 bits 0:2 osd colour mode + 000 = 8 bit indexed 001 = 16 bit (565) 010 = 15 bit (555) 011 = 12 bit (444) 100 = 32 bit (8888) - 101 = 8 bit indexed bits 4:5 osd display bpp @@ -676,9 +676,11 @@ out what values are bad when it hangs. completely transparent. When using 565, 555 or 444 colour modes, the colour key is always 16 bits wide. The colour to key on is set in Reg 2A18. - Local alpha is a per-pixel 256 step transparency, with 0 being transparent - and 255 being solid. This is only available in 32 bit & 8 bit indexed - colour modes. + Local alpha works differently depending on the colour mode. For 32bpp & 8 + bit indexed, local alpha is a per-pixel 256 step transparency, with 0 being + transparent and 255 being solid. For the 16bpp modes 555 & 444, the unused + bit(s) act as a simple transparency switch, with 0 being solid & 1 being + fully transparent. There is no local alpha support for 16bit 565. Global alpha is a 256 step transparency that applies to the entire osd, with 0 being transparent & 255 being solid. @@ -811,5 +813,5 @@ out what values are bad when it hangs. -------------------------------------------------------------------------------- -v0.3 - 2 February 2007 - Ian Armstrong (ian@iarmst.demon.co.uk) +v0.4 - 12 March 2007 - Ian Armstrong (ian@iarmst.demon.co.uk) diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index 242104c..5dd3109 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -663,12 +663,13 @@ Param[0] ------------------------------------------------------------------------------- -Name CX2341X_ENC_UNKNOWN +Name CX2341X_ENC_SET_VERT_CROP_LINE Enum 219/0xDB Description - Unknown API, it's used by Hauppauge though. + Something to do with 'Vertical Crop Line' Param[0] - 0 This is the value Hauppauge uses, Unknown what it means. + If saa7114 and raw VBI capture and 60 Hz, then set to 10001. + Else 0. ------------------------------------------------------------------------------- @@ -682,11 +683,9 @@ Param[0] Command number: 1=set initial SCR value when starting encoding (works). 2=set quality mode (apparently some test setting). - 3=setup advanced VIM protection handling (supposedly only for the cx23416 - for raw YUV). - Actually it looks like this should be 0 for saa7114/5 based card and 1 - for cx25840 based cards. - 4=generate artificial PTS timestamps + 3=setup advanced VIM protection handling. + Always 1 for the cx23416 and 0 for cx23415. + 4=generate DVD compatible PTS timestamps 5=USB flush mode 6=something to do with the quantization matrix 7=set navigation pack insertion for DVD: adds 0xbf (private stream 2) @@ -698,7 +697,9 @@ Param[0] 9=set history parameters of the video input module 10=set input field order of VIM 11=set quantization matrix - 12=reset audio interface + 12=reset audio interface after channel change or input switch (has no argument). + Needed for the cx2584x, not needed for the mspx4xx, but it doesn't seem to + do any harm calling it regardless. 13=set audio volume delay 14=set audio delay diff --git a/Documentation/video4linux/cx2341x/fw-osd-api.txt b/Documentation/video4linux/cx2341x/fw-osd-api.txt index 0a602f3..89c4601 100644 --- a/Documentation/video4linux/cx2341x/fw-osd-api.txt +++ b/Documentation/video4linux/cx2341x/fw-osd-api.txt @@ -21,7 +21,11 @@ Enum 66/0x42 Description Query OSD format Result[0] - 0=8bit index, 4=AlphaRGB 8:8:8:8 + 0=8bit index + 1=16bit RGB 5:6:5 + 2=16bit ARGB 1:5:5:5 + 3=16bit ARGB 1:4:4:4 + 4=32bit ARGB 8:8:8:8 ------------------------------------------------------------------------------- @@ -30,7 +34,11 @@ Enum 67/0x43 Description Assign pixel format Param[0] - 0=8bit index, 4=AlphaRGB 8:8:8:8 + 0=8bit index + 1=16bit RGB 5:6:5 + 2=16bit ARGB 1:5:5:5 + 3=16bit ARGB 1:4:4:4 + 4=32bit ARGB 8:8:8:8 ------------------------------------------------------------------------------- diff --git a/Documentation/video4linux/meye.txt b/Documentation/video4linux/meye.txt index ecb3416..5e51c59 100644 --- a/Documentation/video4linux/meye.txt +++ b/Documentation/video4linux/meye.txt @@ -5,10 +5,9 @@ Vaio Picturebook Motion Eye Camera Drive Copyright (C) 2000 Andrew Tridgell This driver enable the use of video4linux compatible applications with the -Motion Eye camera. This driver requires the "Sony Vaio Programmable I/O -Control Device" driver (which can be found in the "Character drivers" -section of the kernel configuration utility) to be compiled and installed -(using its "camera=1" parameter). +Motion Eye camera. This driver requires the "Sony Laptop Extras" driver (which +can be found in the "Misc devices" section of the kernel configuration utility) +to be compiled and installed (using its "camera=1" parameter). It can do at maximum 30 fps @ 320x240 or 15 fps @ 640x480. diff --git a/Documentation/video4linux/sn9c102.txt b/Documentation/video4linux/sn9c102.txt index 2913da3..5fe0ad7 100644 --- a/Documentation/video4linux/sn9c102.txt +++ b/Documentation/video4linux/sn9c102.txt @@ -25,7 +25,7 @@ Index 1. Copyright ============ -Copyright (C) 2004-2006 by Luca Risolia +Copyright (C) 2004-2007 by Luca Risolia 2. Disclaimer @@ -216,10 +216,10 @@ Description: Debugging information le 1 = critical errors 2 = significant informations 3 = more verbose messages - Level 3 is useful for testing only, when only one device - is used. It also shows some more informations about the - hardware being detected. This parameter can be changed at - runtime thanks to the /sys filesystem interface. + Level 3 is useful for testing only. It also shows some more + informations about the hardware being detected. + This parameter can be changed at runtime thanks to the /sys + filesystem interface. Default: 2 ------------------------------------------------------------------------------- @@ -235,7 +235,7 @@ created in the /sys/class/video4linux/vi channel's gain by writing the desired value to it. The value may range from 0 to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, SN9C105 and SN9C120 bridges. -Similarly, only for the SN9C103, SN9C105 and SN9120 controllers, blue and red +Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red gain control files are available in the same directory, for which accepted values may range from 0 to 127. @@ -402,38 +402,49 @@ Vendor ID Product ID 0x0c45 0x60bc 0x0c45 0x60be 0x0c45 0x60c0 +0x0c45 0x60c2 0x0c45 0x60c8 0x0c45 0x60cc 0x0c45 0x60ea 0x0c45 0x60ec +0x0c45 0x60ef 0x0c45 0x60fa 0x0c45 0x60fb 0x0c45 0x60fc 0x0c45 0x60fe +0x0c45 0x6102 +0x0c45 0x6108 +0x0c45 0x610f 0x0c45 0x6130 +0x0c45 0x6138 0x0c45 0x613a 0x0c45 0x613b 0x0c45 0x613c 0x0c45 0x613e The list above does not imply that all those devices work with this driver: up -until now only the ones that assemble the following image sensors are -supported; kernel messages will always tell you whether this is the case (see -"Module loading" paragraph): - -Model Manufacturer ------ ------------ -HV7131D Hynix Semiconductor, Inc. -MI-0343 Micron Technology, Inc. -OV7630 OmniVision Technologies, Inc. -OV7660 OmniVision Technologies, Inc. -PAS106B PixArt Imaging, Inc. -PAS202BCA PixArt Imaging, Inc. -PAS202BCB PixArt Imaging, Inc. -TAS5110C1B Taiwan Advanced Sensor Corporation -TAS5130D1B Taiwan Advanced Sensor Corporation - -Some of the available control settings of each image sensor are supported +until now only the ones that assemble the following pairs of SN9C1xx bridges +and image sensors are supported; kernel messages will always tell you whether +this is the case (see "Module loading" paragraph): + +Image sensor / SN9C1xx bridge | SN9C10[12] SN9C103 SN9C105 SN9C120 +------------------------------------------------------------------------------- +HV7131D Hynix Semiconductor | Yes No No No +HV7131R Hynix Semiconductor | No Yes Yes Yes +MI-0343 Micron Technology | Yes No No No +MI-0360 Micron Technology | No Yes No No +OV7630 OmniVision Technologies | Yes Yes No No +OV7660 OmniVision Technologies | No No Yes Yes +PAS106B PixArt Imaging | Yes No No No +PAS202B PixArt Imaging | Yes Yes No No +TAS5110C1B Taiwan Advanced Sensor | Yes No No No +TAS5110D Taiwan Advanced Sensor | Yes No No No +TAS5130D1B Taiwan Advanced Sensor | Yes No No No + +"Yes" means that the pair is supported by the driver, while "No" means that the +pair does not exist or is not supported by the driver. + +Only some of the available control settings of each image sensor are supported through the V4L2 interface. Donations of new models for further testing and support would be much @@ -482,8 +493,8 @@ The SN9C1xx PC Camera Controllers can se formats over the USB: either native "Sequential RGB Bayer" or compressed. The compression is used to achieve high frame rates. With regard to the SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding -algorithm described below, while the SN9C105 and SN9C120 the compression is -based on the JPEG standard. +algorithm described below, while with regard to the SN9C105 and SN9C120 the +compression is based on the JPEG standard. The current video format may be selected or queried from the user application by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 API specifications. @@ -573,4 +584,5 @@ order): - Mizuno Takafumi for the donation of a webcam; - an "anonymous" donator (who didn't want his name to be revealed) for the donation of a webcam. -- an anonymous donator for the donation of four webcams. +- an anonymous donator for the donation of four webcams and two boards with ten + image sensors. diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt new file mode 100644 index 0000000..c76992d --- /dev/null +++ b/Documentation/video4linux/zr364xx.txt @@ -0,0 +1,65 @@ +Zoran 364xx based USB webcam module version 0.72 +site: http://royale.zerezo.com/zr364xx/ +mail: royale@zerezo.com + +introduction: +This brings support under Linux for the Aiptek PocketDV 3300 in webcam mode. +If you just want to get on your PC the pictures and movies on the camera, you should use the usb-storage module instead. +The driver works with several other cameras in webcam mode (see the list below). +Maybe this code can work for other JPEG/USB cams based on the Coach chips from Zoran? +Possible chipsets are : ZR36430 (ZR36430BGC) and maybe ZR36431, ZR36440, ZR36442... +You can try the experience changing the vendor/product ID values (look at the source code). +You can get these values by looking at /var/log/messages when you plug your camera, or by typing : cat /proc/bus/usb/devices. +If you manage to use your cam with this code, you can send me a mail (royale@zerezo.com) with the name of your cam and a patch if needed. +This is a beta release of the driver. +Since version 0.70, this driver is only compatible with V4L2 API and 2.6.x kernels. +If you need V4L1 or 2.4x kernels support, please use an older version, but the code is not maintained anymore. +Good luck! + +install: +In order to use this driver, you must compile it with your kernel. +Location: Device Drivers -> Multimedia devices -> Video For Linux -> Video Capture Adapters -> V4L USB devices + +usage: +modprobe zr364xx debug=X mode=Y + - debug : set to 1 to enable verbose debug messages + - mode : 0 = 320x240, 1 = 160x120, 2 = 640x480 +You can then use the camera with V4L2 compatible applications, for example Ekiga. +To capture a single image, try this: dd if=/dev/video0 of=test.jpg bs=1 count=1 + +links : +http://mxhaard.free.fr/ (support for many others cams including some Aiptek PocketDV) +http://www.harmwal.nl/pccam880/ (this project also supports cameras based on this chipset) + +supported devices: +------ ------- ----------- ----- +Vendor Product Distributor Model +------ ------- ----------- ----- +0x08ca 0x0109 Aiptek PocketDV 3300 +0x08ca 0x0109 Maxell Maxcam PRO DV3 +0x041e 0x4024 Creative PC-CAM 880 +0x0d64 0x0108 Aiptek Fidelity 3200 +0x0d64 0x0108 Praktica DCZ 1.3 S +0x0d64 0x0108 Genius Digital Camera (?) +0x0d64 0x0108 DXG Technology Fashion Cam +0x0546 0x3187 Polaroid iON 230 +0x0d64 0x3108 Praktica Exakta DC 2200 +0x0d64 0x3108 Genius G-Shot D211 +0x0595 0x4343 Concord Eye-Q Duo 1300 +0x0595 0x4343 Concord Eye-Q Duo 2000 +0x0595 0x4343 Fujifilm EX-10 +0x0595 0x4343 Ricoh RDC-6000 +0x0595 0x4343 Digitrex DSC 1300 +0x0595 0x4343 Firstline FDC 2000 +0x0bb0 0x500d Concord EyeQ Go Wireless +0x0feb 0x2004 CRS Electronic 3.3 Digital Camera +0x0feb 0x2004 Packard Bell DSC-300 +0x055f 0xb500 Mustek MDC 3000 +0x08ca 0x2062 Aiptek PocketDV 5700 +0x052b 0x1a18 Chiphead Megapix V12 +0x04c8 0x0729 Konica Revio 2 +0x04f2 0xa208 Creative PC-CAM 850 +0x0784 0x0040 Traveler Slimline X5 +0x06d6 0x0034 Trust Powerc@m 750 +0x0a17 0x0062 Pentax Optio 50L + diff --git a/MAINTAINERS b/MAINTAINERS index 277877a..af1c792 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -55,7 +55,7 @@ trivial patch so apply some common sense 8. Happy hacking. - ----------------------------------- + ----------------------------------- Maintainers List (try to look for most precise areas first) @@ -384,7 +384,7 @@ S: Supported APPLETALK NETWORK LAYER P: Arnaldo Carvalho de Melo -M: acme@conectiva.com.br +M: acme@ghostprotocols.net S: Maintained ARC FRAMEBUFFER DRIVER @@ -656,6 +656,7 @@ S: Supported ATMEL WIRELESS DRIVER P: Simon Kelley M: simon@thekelleys.org.uk +L: linux-wireless@vger.kernel.org W: http://www.thekelleys.org.uk/atmel W: http://atmelwlandriver.sourceforge.net/ S: Maintained @@ -711,6 +712,7 @@ P: Larry Finger M: Larry.Finger@lwfinger.net P: Stefano Brivio M: st3@riseup.net +L: linux-wireless@vger.kernel.org W: http://bcm43xx.berlios.de/ S: Maintained @@ -871,6 +873,12 @@ W: http://linuxtv.org T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git S: Maintained +CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER +P: Jonathan Corbet +M: corbet@lwn.net +L: video4linux-list@redhat.com +S: Maintained + CALGARY x86-64 IOMMU P: Muli Ben-Yehuda M: muli@il.ibm.com @@ -892,6 +900,12 @@ M: maxextreme@gmail.com L: linux-kernel@vger.kernel.org S: Maintained +CFG80211 and NL80211 +P: Johannes Berg +M: johannes@sipsolutions.net +L: linux-wireless@vger.kernel.org +S: Maintained + COMMON INTERNET FILE SYSTEM (CIFS) P: Steve French M: sfrench@samba.org @@ -899,7 +913,7 @@ L: linux-cifs-client@lists.samba.org L: samba-technical@lists.samba.org W: http://us1.samba.org/samba/Linux_CIFS_client.html T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git -S: Supported +S: Supported CONFIGFS P: Joel Becker @@ -967,6 +981,11 @@ M: mhw@wittsend.com W: http://www.wittsend.com/computone.html S: Maintained +CONEXANT ACCESSRUNNER USB DRIVER +P: Simon Arlott +M: cxacru@fire.lp0.eu +S: Maintained + COSA/SRP SYNC SERIAL DRIVER P: Jan "Yenya" Kasprzak M: kas@fi.muni.cz @@ -1034,9 +1053,8 @@ S: Maintained CYCLADES 2X SYNC CARD DRIVER P: Arnaldo Carvalho de Melo -M: acme@conectiva.com.br -W: http://advogato.org/person/acme -L: cycsyn-devel@bazar.conectiva.com.br +M: acme@ghostprotocols.net +W: http://oops.ghostprotocols.net:81/blog S: Maintained CYCLADES ASYNC MUX DRIVER @@ -1077,7 +1095,7 @@ S: Maintained DCCP PROTOCOL P: Arnaldo Carvalho de Melo -M: acme@mandriva.com +M: acme@ghostprotocols.net L: dccp@vger.kernel.org W: http://linux-net.osdl.org/index.php/DCCP S: Maintained @@ -1376,6 +1394,13 @@ L: linuxppc-embedded@ozlabs.org L: netdev@vger.kernel.org S: Maintained +FREESCALE HIGHSPEED USB DEVICE DRIVER +P: Li Yang +M: leoli@freescale.com +L: linux-usb-devel@lists.sourceforge.net +L: linuxppc-embedded@ozlabs.org +S: Maintained + FILE LOCKING (flock() and fcntl()/lockf()) P: Matthew Wilcox M: matthew@wil.cx @@ -1542,23 +1567,24 @@ P: Chirag Kantharia M: chirag.kantharia@hp.com L: iss_storagedev@hp.com S: Maintained - + HEWLETT-PACKARD SMART2 RAID DRIVER P: Chirag Kantharia M: chirag.kantharia@hp.com L: iss_storagedev@hp.com S: Maintained - + HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss) P: Mike Miller M: mike.miller@hp.com L: iss_storagedev@hp.com S: Supported - + HOST AP DRIVER P: Jouni Malinen -M: jkmaline@cc.hut.fi -L: hostap@shmoo.com +M: j@w1.fi +L: hostap@shmoo.com (subscribers-only) +L: linux-wireless@vger.kernel.org W: http://hostap.epitest.fi/ S: Maintained @@ -1632,15 +1658,6 @@ W: http://www.ia64-linux.org/ T: git kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git S: Maintained -IBM ACPI EXTRAS DRIVER -P: Henrique de Moraes Holschuh -M: ibm-acpi@hmh.eng.br -L: ibm-acpi-devel@lists.sourceforge.net -W: http://ibm-acpi.sourceforge.net -W: http://thinkwiki.org/wiki/Ibm-acpi -T: git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git -S: Maintained - SN-IA64 (Itanium) SUB-PLATFORM P: Jes Sorensen M: jes@sgi.com @@ -1665,7 +1682,7 @@ P: Jack Hammer P: Dave Jeffery M: ipslinux@adaptec.com W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html -S: Supported +S: Supported IDE SUBSYSTEM P: Bartlomiej Zolnierkiewicz @@ -1794,6 +1811,7 @@ P: Jeff Kirsher M: jeffrey.t.kirsher@intel.com P: Auke Kok M: auke-jan.h.kok@intel.com +L: e1000-devel@lists.sourceforge.net W: http://sourceforge.net/projects/e1000/ S: Supported @@ -1808,6 +1826,7 @@ P: Jeff Kirsher M: jeffrey.t.kirsher@intel.com P: Auke Kok M: auke-jan.h.kok@intel.com +L: e1000-devel@lists.sourceforge.net W: http://sourceforge.net/projects/e1000/ S: Supported @@ -1822,6 +1841,7 @@ P: Jesse Brandeburg M: jesse.brandeburg@intel.com P: Auke Kok M: auke-jan.h.kok@intel.com +L: e1000-devel@lists.sourceforge.net W: http://sourceforge.net/projects/e1000/ S: Supported @@ -1830,6 +1850,7 @@ P: Yi Zhu M: yi.zhu@intel.com P: James Ketrenos M: jketreno@linux.intel.com +L: linux-wireless@vger.kernel.org L: ipw2100-devel@lists.sourceforge.net L: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel W: http://ipw2100.sourceforge.net @@ -1840,6 +1861,7 @@ P: Yi Zhu M: yi.zhu@intel.com P: James Ketrenos M: jketreno@linux.intel.com +L: linux-wireless@vger.kernel.org L: ipw2100-devel@lists.sourceforge.net L: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel W: http://ipw2200.sourceforge.net @@ -1871,7 +1893,7 @@ S: Supported IPX NETWORK LAYER P: Arnaldo Carvalho de Melo -M: acme@conectiva.com.br +M: acme@ghostprotocols.net L: netdev@vger.kernel.org S: Maintained @@ -1965,7 +1987,7 @@ M: kai@germaschewski.name P: Sam Ravnborg M: sam@ravnborg.org T: git kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git -S: Maintained +S: Maintained KERNEL JANITORS P: Several @@ -2108,7 +2130,7 @@ S: Supported LLC (802.2) P: Arnaldo Carvalho de Melo -M: acme@conectiva.com.br +M: acme@ghostprotocols.net S: Maintained LINUX FOR 64BIT POWERPC @@ -2145,7 +2167,7 @@ S: Maintained LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks) P: Richard Russon (FlatCap) M: ldm@flatcap.org -L: ldm-devel@lists.sourceforge.net +L: ldm-devel@lists.sourceforge.net W: http://ldm.sourceforge.net S: Maintained @@ -2236,6 +2258,14 @@ L: linux-mtd@lists.infradead.org T: git git://git.infradead.org/mtd-2.6.git S: Maintained +UNSORTED BLOCK IMAGES (UBI) +P: Artem Bityutskiy +M: dedekind@infradead.org +W: http://www.linux-mtd.infradead.org/ +L: linux-mtd@lists.infradead.org +T: git git://git.infradead.org/ubi-2.6.git +S: Maintained + MICROTEK X6 SCANNER P: Oliver Neukum M: oliver@neukum.name @@ -2473,6 +2503,19 @@ M: adaplas@gmail.com L: linux-fbdev-devel@lists.sourceforge.net (subscribers-only) S: Maintained +NETERION (S2IO) Xframe 10GbE DRIVER +P: Ramkrishna Vepa +M: ram.vepa@neterion.com +P: Rastapur Santosh +M: santosh.rastapur@neterion.com +P: Sivakumar Subramani +M: sivakumar.subramani@neterion.com +P: Sreenivasa Honnur +M: sreenivasa.honnur@neterion.com +L: netdev@vger.kernel.org +W: http://trac.neterion.com/cgi-bin/trac.cgi/wiki/TitleIndex?anonymous +S: Supported + OPENCORES I2C BUS DRIVER P: Peter Korsgaard M: jacmet@sunsite.dk @@ -2486,13 +2529,13 @@ P: Kurt Hackel M: kurt.hackel@oracle.com L: ocfs2-devel@oss.oracle.com W: http://oss.oracle.com/projects/ocfs2/ -S: Supported +S: Supported OLYMPIC NETWORK DRIVER P: Peter De Shrijver M: p2@ace.ulyssis.student.kuleuven.ac.be P: Mike Phillips -M: mikep@linuxtr.net +M: mikep@linuxtr.net L: netdev@vger.kernel.org L: linux-tr@linuxtr.net W: http://www.linuxtr.net @@ -2508,6 +2551,12 @@ P: Harald Welte M: laforge@gnumonks.org S: Maintained +OMNIVISION OV7670 SENSOR DRIVER +P: Jonathan Corbet +M: corbet@lwn.net +L: video4linux-list@redhat.com +S: Maintained + ONSTREAM SCSI TAPE DRIVER P: Willem Riede M: osst@riede.org @@ -2532,6 +2581,7 @@ P: Pavel Roskin M: proski@gnu.org P: David Gibson M: hermes@gibson.dropbear.id.au +L: linux-wireless@vger.kernel.org L: orinoco-users@lists.sourceforge.net L: orinoco-devel@lists.sourceforge.net W: http://www.nongnu.org/orinoco/ @@ -2711,7 +2761,7 @@ S: Supported PRISM54 WIRELESS DRIVER P: Prism54 Development Team M: developers@islsm.org -L: netdev@vger.kernel.org +L: linux-wireless@vger.kernel.org W: http://prism54.org S: Maintained @@ -2782,7 +2832,7 @@ S: Maintained RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER P: Corey Thomas M: corey@world.std.com -L: linux-kernel@vger.kernel.org +L: linux-wireless@vger.kernel.org S: Maintained RANDOM NUMBER DRIVER @@ -2961,8 +3011,10 @@ P: Stephen Smalley M: sds@tycho.nsa.gov P: James Morris M: jmorris@namei.org +P: Eric Paris +M: eparis@parisplace.org L: linux-kernel@vger.kernel.org (kernel issues) -L: selinux@tycho.nsa.gov (general discussion) +L: selinux@tycho.nsa.gov (subscribers-only, general discussion) W: http://www.nsa.gov/selinux S: Supported @@ -3024,7 +3076,7 @@ SIS FRAMEBUFFER DRIVER P: Thomas Winischhofer M: thomas@winischhofer.net W: http://www.winischhofer.net/linuxsisvga.shtml -S: Maintained +S: Maintained SIS USB2VGA DRIVER P: Thomas Winischhofer @@ -3045,7 +3097,7 @@ M: josejx@gentoo.org P: Daniel Drake M: dsd@gentoo.org W: http://softmac.sipsolutions.net/ -L: netdev@vger.kernel.org +L: linux-wireless@vger.kernel.org S: Maintained SOFTWARE RAID (Multiple Disks) SUPPORT @@ -3121,6 +3173,15 @@ P: Chris Zankel M: chris@zankel.net S: Maintained +THINKPAD ACPI EXTRAS DRIVER +P: Henrique de Moraes Holschuh +M: ibm-acpi@hmh.eng.br +L: ibm-acpi-devel@lists.sourceforge.net +W: http://ibm-acpi.sourceforge.net +W: http://thinkwiki.org/wiki/Ibm-acpi +T: git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git +S: Maintained + UltraSPARC (sparc64): P: David S. Miller M: davem@davemloft.net @@ -3573,7 +3634,7 @@ L: linux-usb-devel@lists.sourceforge.net W: http://www.connecttech.com S: Supported -USB SN9C10x DRIVER +USB SN9C1xx DRIVER P: Luca Risolia M: luca.risolia@studio.unibo.it L: linux-usb-devel@lists.sourceforge.net @@ -3628,6 +3689,14 @@ L: linux-usb-devel@lists.sourceforge.net W: http://linux-lc100020.sourceforge.net S: Maintained +USB ZR364XX DRIVER +P: Antoine Jacquet +M: royale@zerezo.com +L: linux-usb-devel@lists.sourceforge.net +L: video4linux-list@redhat.com +W: http://royale.zerezo.com/zr364xx/ +S: Maintained + USER-MODE LINUX P: Jeff Dike M: jdike@karaya.com @@ -3635,7 +3704,7 @@ L: user-mode-linux-devel@lists.sourcefor L: user-mode-linux-user@lists.sourceforge.net W: http://user-mode-linux.sourceforge.net S: Maintained - + FAT/VFAT/MSDOS FILESYSTEM: P: OGAWA Hirofumi M: hirofumi@mail.parknet.co.jp @@ -3750,6 +3819,7 @@ S: Maintained WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS P: Jean Tourrilhes M: jt@hpl.hp.com +L: linux-wireless@vger.kernel.org W: http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/ S: Maintained @@ -3766,8 +3836,9 @@ S: Maintained WL3501 WIRELESS PCMCIA CARD DRIVER P: Arnaldo Carvalho de Melo -M: acme@conectiva.com.br -W: http://advogato.org/person/acme +M: acme@ghostprotocols.net +L: linux-wireless@vger.kernel.org +W: http://oops.ghostprotocols.net:81/blog S: Maintained X.25 NETWORK LAYER @@ -3830,6 +3901,7 @@ M: dsd@gentoo.org P: Ulrich Kunitz M: kune@deine-taler.de W: http://zd1211.ath.cx/wiki/DriverRewrite +L: linux-wireless@vger.kernel.org L: zd1211-devs@lists.sourceforge.net (subscribers-only) S: Maintained diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile index 21cf624..ea098f3 100644 --- a/arch/alpha/lib/Makefile +++ b/arch/alpha/lib/Makefile @@ -36,7 +36,6 @@ lib-y = __divqu.o __remqu.o __divlu.o __ $(ev6-y)csum_ipv6_magic.o \ $(ev6-y)clear_page.o \ $(ev6-y)copy_page.o \ - strcasecmp.o \ fpreg.o \ callback_srm.o srm_puts.o srm_printk.o diff --git a/arch/alpha/lib/strcasecmp.c b/arch/alpha/lib/strcasecmp.c deleted file mode 100644 index 4e57a21..0000000 --- a/arch/alpha/lib/strcasecmp.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * linux/arch/alpha/lib/strcasecmp.c - */ - -#include - - -/* We handle nothing here except the C locale. Since this is used in - only one place, on strings known to contain only 7 bit ASCII, this - is ok. */ - -int strcasecmp(const char *a, const char *b) -{ - int ca, cb; - - do { - ca = *a++ & 0xff; - cb = *b++ & 0xff; - if (ca >= 'A' && ca <= 'Z') - ca += 'a' - 'A'; - if (cb >= 'A' && cb <= 'Z') - cb += 'a' - 'A'; - } while (ca == cb && ca != '\0'); - - return ca - cb; -} diff --git a/arch/arm/common/sharpsl_pm.c b/arch/arm/common/sharpsl_pm.c index a9bc5b5..5972df2 100644 --- a/arch/arm/common/sharpsl_pm.c +++ b/arch/arm/common/sharpsl_pm.c @@ -766,10 +766,10 @@ static void sharpsl_apm_get_power_status } static struct pm_ops sharpsl_pm_ops = { - .pm_disk_mode = PM_DISK_FIRMWARE, .prepare = pxa_pm_prepare, .enter = corgi_pxa_pm_enter, .finish = pxa_pm_finish, + .valid = pm_valid_only_mem, }; static int __init sharpsl_pm_probe(struct platform_device *pdev) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index b49bfda..ff8db29 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -201,7 +201,6 @@ error: static struct pm_ops at91_pm_ops ={ - .pm_disk_mode = 0, .valid = at91_pm_valid_state, .prepare = at91_pm_prepare, .enter = at91_pm_enter, diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c index 49efe90..0383ab3 100644 --- a/arch/arm/mach-omap1/pm.c +++ b/arch/arm/mach-omap1/pm.c @@ -698,10 +698,10 @@ static struct irqaction omap_wakeup_irq static struct pm_ops omap_pm_ops ={ - .pm_disk_mode = 0, .prepare = omap_pm_prepare, .enter = omap_pm_enter, .finish = omap_pm_finish, + .valid = pm_valid_only_mem, }; static int __init omap_pm_init(void) diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index d7eee99..6f4a543 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -370,10 +370,10 @@ static int omap2_pm_finish(suspend_state } static struct pm_ops omap_pm_ops = { - .pm_disk_mode = 0, .prepare = omap2_pm_prepare, .enter = omap2_pm_enter, .finish = omap2_pm_finish, + .valid = pm_valid_only_mem, }; int __init omap2_pm_init(void) diff --git a/arch/arm/mach-pnx4008/pm.c b/arch/arm/mach-pnx4008/pm.c index 3649cd3..2a137f3 100644 --- a/arch/arm/mach-pnx4008/pm.c +++ b/arch/arm/mach-pnx4008/pm.c @@ -107,50 +107,19 @@ static int pnx4008_pm_enter(suspend_stat case PM_SUSPEND_MEM: pnx4008_suspend(); break; - case PM_SUSPEND_DISK: - return -ENOTSUPP; - default: - return -EINVAL; } return 0; } -/* - * Called after processes are frozen, but before we shut down devices. - */ -static int pnx4008_pm_prepare(suspend_state_t state) -{ - switch (state) { - case PM_SUSPEND_STANDBY: - case PM_SUSPEND_MEM: - break; - - case PM_SUSPEND_DISK: - return -ENOTSUPP; - break; - - default: - return -EINVAL; - break; - } - return 0; -} - -/* - * Called after devices are re-setup, but before processes are thawed. - */ -static int pnx4008_pm_finish(suspend_state_t state) +static int pnx4008_pm_valid(suspend_state_t state) { - return 0; + return (state == PM_SUSPEND_STANDBY) || + (state == PM_SUSPEND_MEM); } -/* - * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. - */ static struct pm_ops pnx4008_pm_ops = { - .prepare = pnx4008_pm_prepare, .enter = pnx4008_pm_enter, - .finish = pnx4008_pm_finish, + .valid = pnx4008_pm_valid, }; static int __init pnx4008_pm_init(void) diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c index b4d8276..6bf15ae 100644 --- a/arch/arm/mach-pxa/pm.c +++ b/arch/arm/mach-pxa/pm.c @@ -223,14 +223,11 @@ int pxa_pm_finish(suspend_state_t state) EXPORT_SYMBOL_GPL(pxa_pm_finish); -/* - * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. - */ static struct pm_ops pxa_pm_ops = { - .pm_disk_mode = PM_DISK_FIRMWARE, .prepare = pxa_pm_prepare, .enter = pxa_pm_enter, .finish = pxa_pm_finish, + .valid = pm_valid_only_mem, }; static int __init pxa_pm_init(void) diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c index 786c853..d674cf3 100644 --- a/arch/arm/mach-sa1100/pm.c +++ b/arch/arm/mach-sa1100/pm.c @@ -59,9 +59,6 @@ static int sa11x0_pm_enter(suspend_state unsigned long gpio, sleep_save[SLEEP_SAVE_SIZE]; struct timespec delta, rtc; - if (state != PM_SUSPEND_MEM) - return -EINVAL; - /* preserve current time */ rtc.tv_sec = RCNR; rtc.tv_nsec = 0; @@ -134,12 +131,9 @@ unsigned long sleep_phys_sp(void *sp) return virt_to_phys(sp); } -/* - * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. - */ static struct pm_ops sa11x0_pm_ops = { - .pm_disk_mode = PM_DISK_FIRMWARE, .enter = sa11x0_pm_enter, + .valid = pm_valid_only_mem, }; static int __init sa11x0_pm_init(void) diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c index ecf68d6..c6b03f8 100644 --- a/arch/arm/plat-s3c24xx/pm.c +++ b/arch/arm/plat-s3c24xx/pm.c @@ -511,11 +511,6 @@ static int s3c2410_pm_enter(suspend_stat return -EINVAL; } - if (state != PM_SUSPEND_MEM) { - printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n"); - return -EINVAL; - } - /* check if we have anything to wake-up with... bad things seem * to happen if you suspend with no wakeup (system will often * require a full power-cycle) @@ -617,30 +612,9 @@ static int s3c2410_pm_enter(suspend_stat return 0; } -/* - * Called after processes are frozen, but before we shut down devices. - */ -static int s3c2410_pm_prepare(suspend_state_t state) -{ - return 0; -} - -/* - * Called after devices are re-setup, but before processes are thawed. - */ -static int s3c2410_pm_finish(suspend_state_t state) -{ - return 0; -} - -/* - * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. - */ static struct pm_ops s3c2410_pm_ops = { - .pm_disk_mode = PM_DISK_FIRMWARE, - .prepare = s3c2410_pm_prepare, .enter = s3c2410_pm_enter, - .finish = s3c2410_pm_finish, + .valid = pm_valid_only_mem, }; /* s3c2410_pm_init diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index ce4013a..3ec7658 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -57,9 +57,6 @@ config ARCH_HAS_ILOG2_U64 bool default n -config GENERIC_BUST_SPINLOCK - bool - config GENERIC_HWEIGHT bool default y @@ -68,6 +65,11 @@ config GENERIC_CALIBRATE_DELAY bool default y +config GENERIC_BUG + bool + default y + depends on BUG + source "init/Kconfig" menu "System Type and features" @@ -106,6 +108,9 @@ choice config BOARD_ATSTK1000 bool "ATSTK1000 evaluation board" select BOARD_ATSTK1002 if CPU_AT32AP7000 + +config BOARD_ATNGW100 + bool "ATNGW100 Network Gateway" endchoice choice @@ -116,6 +121,8 @@ config LOADER_U_BOOT bool "U-Boot (or similar) bootloader" endchoice +source "arch/avr32/mach-at32ap/Kconfig" + config LOAD_ADDRESS hex default 0x10000000 if LOADER_U_BOOT=y && CPU_AT32AP7000=y diff --git a/arch/avr32/Makefile b/arch/avr32/Makefile index 7b842e9..6115fc1 100644 --- a/arch/avr32/Makefile +++ b/arch/avr32/Makefile @@ -27,6 +27,7 @@ head-$(CONFIG_LOADER_U_BOOT) += arch/av head-y += arch/avr32/kernel/head.o core-$(CONFIG_PLATFORM_AT32AP) += arch/avr32/mach-at32ap/ core-$(CONFIG_BOARD_ATSTK1000) += arch/avr32/boards/atstk1000/ +core-$(CONFIG_BOARD_ATNGW100) += arch/avr32/boards/atngw100/ core-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/ core-y += arch/avr32/kernel/ core-y += arch/avr32/mm/ diff --git a/arch/avr32/boards/atngw100/Makefile b/arch/avr32/boards/atngw100/Makefile new file mode 100644 index 0000000..c740aa1 --- /dev/null +++ b/arch/avr32/boards/atngw100/Makefile @@ -0,0 +1 @@ +obj-y += setup.o flash.o diff --git a/arch/avr32/boards/atngw100/flash.c b/arch/avr32/boards/atngw100/flash.c new file mode 100644 index 0000000..f9b32a8 --- /dev/null +++ b/arch/avr32/boards/atngw100/flash.c @@ -0,0 +1,95 @@ +/* + * ATNGW100 board-specific flash initialization + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include + +static struct smc_config flash_config __initdata = { + .ncs_read_setup = 0, + .nrd_setup = 40, + .ncs_write_setup = 0, + .nwe_setup = 10, + + .ncs_read_pulse = 80, + .nrd_pulse = 40, + .ncs_write_pulse = 65, + .nwe_pulse = 55, + + .read_cycle = 120, + .write_cycle = 120, + + .bus_width = 2, + .nrd_controlled = 1, + .nwe_controlled = 1, + .byte_write = 1, +}; + +static struct mtd_partition flash_parts[] = { + { + .name = "u-boot", + .offset = 0x00000000, + .size = 0x00020000, /* 128 KiB */ + .mask_flags = MTD_WRITEABLE, + }, + { + .name = "root", + .offset = 0x00020000, + .size = 0x007d0000, + }, + { + .name = "env", + .offset = 0x007f0000, + .size = 0x00010000, + .mask_flags = MTD_WRITEABLE, + }, +}; + +static struct physmap_flash_data flash_data = { + .width = 2, + .nr_parts = ARRAY_SIZE(flash_parts), + .parts = flash_parts, +}; + +static struct resource flash_resource = { + .start = 0x00000000, + .end = 0x007fffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device flash_device = { + .name = "physmap-flash", + .id = 0, + .resource = &flash_resource, + .num_resources = 1, + .dev = { + .platform_data = &flash_data, + }, +}; + +/* This needs to be called after the SMC has been initialized */ +static int __init atngw100_flash_init(void) +{ + int ret; + + ret = smc_set_configuration(0, &flash_config); + if (ret < 0) { + printk(KERN_ERR "atngw100: failed to set NOR flash timing\n"); + return ret; + } + + platform_device_register(&flash_device); + + return 0; +} +device_initcall(atngw100_flash_init); diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c new file mode 100644 index 0000000..9bc37d4 --- /dev/null +++ b/arch/avr32/boards/atngw100/setup.c @@ -0,0 +1,124 @@ +/* + * Board-specific setup code for the ATNGW100 Network Gateway + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* Initialized by bootloader-specific startup code. */ +struct tag *bootloader_tags __initdata; + +struct eth_addr { + u8 addr[6]; +}; +static struct eth_addr __initdata hw_addr[2]; +static struct eth_platform_data __initdata eth_data[2]; + +static struct spi_board_info spi0_board_info[] __initdata = { + { + .modalias = "mtd_dataflash", + .max_speed_hz = 10000000, + .chip_select = 0, + }, +}; + +/* + * The next two functions should go away as the boot loader is + * supposed to initialize the macb address registers with a valid + * ethernet address. But we need to keep it around for a while until + * we can be reasonably sure the boot loader does this. + * + * The phy_id is ignored as the driver will probe for it. + */ +static int __init parse_tag_ethernet(struct tag *tag) +{ + int i; + + i = tag->u.ethernet.mac_index; + if (i < ARRAY_SIZE(hw_addr)) + memcpy(hw_addr[i].addr, tag->u.ethernet.hw_address, + sizeof(hw_addr[i].addr)); + + return 0; +} +__tagtable(ATAG_ETHERNET, parse_tag_ethernet); + +static void __init set_hw_addr(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + const u8 *addr; + void __iomem *regs; + struct clk *pclk; + + if (!res) + return; + if (pdev->id >= ARRAY_SIZE(hw_addr)) + return; + + addr = hw_addr[pdev->id].addr; + if (!is_valid_ether_addr(addr)) + return; + + /* + * Since this is board-specific code, we'll cheat and use the + * physical address directly as we happen to know that it's + * the same as the virtual address. + */ + regs = (void __iomem __force *)res->start; + pclk = clk_get(&pdev->dev, "pclk"); + if (!pclk) + return; + + clk_enable(pclk); + __raw_writel((addr[3] << 24) | (addr[2] << 16) + | (addr[1] << 8) | addr[0], regs + 0x98); + __raw_writel((addr[5] << 8) | addr[4], regs + 0x9c); + clk_disable(pclk); + clk_put(pclk); +} + +struct platform_device *at32_usart_map[1]; +unsigned int at32_nr_usarts = 1; + +void __init setup_board(void) +{ + at32_map_usart(1, 0); /* USART 1: /dev/ttyS0, DB9 */ + at32_setup_serial_console(0); +} + +static int __init atngw100_init(void) +{ + /* + * ATNGW100 uses 16-bit SDRAM interface, so we don't need to + * reserve any pins for it. + */ + + at32_add_system_devices(); + + at32_add_device_usart(0); + + set_hw_addr(at32_add_device_eth(0, ð_data[0])); + set_hw_addr(at32_add_device_eth(1, ð_data[1])); + + at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); + + return 0; +} +postcore_initcall(atngw100_init); diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index 5974768..abe6ca2 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -33,7 +33,7 @@ struct eth_addr { static struct eth_addr __initdata hw_addr[2]; static struct eth_platform_data __initdata eth_data[2]; -extern struct lcdc_platform_data atstk1000_fb0_data; +static struct lcdc_platform_data atstk1000_fb0_data; static struct spi_board_info spi0_board_info[] __initdata = { { @@ -148,6 +148,8 @@ #endif set_hw_addr(at32_add_device_eth(0, ð_data[0])); at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); + atstk1000_fb0_data.fbmem_start = fbmem_start; + atstk1000_fb0_data.fbmem_size = fbmem_size; at32_add_device_lcdc(0, &atstk1000_fb0_data); return 0; diff --git a/arch/avr32/boards/atstk1000/setup.c b/arch/avr32/boards/atstk1000/setup.c index 272c011..2bc4b88 100644 --- a/arch/avr32/boards/atstk1000/setup.c +++ b/arch/avr32/boards/atstk1000/setup.c @@ -18,33 +18,3 @@ #include /* Initialized by bootloader-specific startup code. */ struct tag *bootloader_tags __initdata; - -struct lcdc_platform_data __initdata atstk1000_fb0_data; - -void __init board_setup_fbmem(unsigned long fbmem_start, - unsigned long fbmem_size) -{ - if (!fbmem_size) - return; - - if (!fbmem_start) { - void *fbmem; - - fbmem = alloc_bootmem_low_pages(fbmem_size); - fbmem_start = __pa(fbmem); - } else { - pg_data_t *pgdat; - - for_each_online_pgdat(pgdat) { - if (fbmem_start >= pgdat->bdata->node_boot_start - && fbmem_start <= pgdat->bdata->node_low_pfn) - reserve_bootmem_node(pgdat, fbmem_start, - fbmem_size); - } - } - - printk("%luKiB framebuffer memory at address 0x%08lx\n", - fbmem_size >> 10, fbmem_start); - atstk1000_fb0_data.fbmem_start = fbmem_start; - atstk1000_fb0_data.fbmem_size = fbmem_size; -} diff --git a/arch/avr32/configs/atngw100_defconfig b/arch/avr32/configs/atngw100_defconfig new file mode 100644 index 0000000..c254ffc --- /dev/null +++ b/arch/avr32/configs/atngw100_defconfig @@ -0,0 +1,1085 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.21-rc6 +# Thu Apr 12 16:35:07 2007 +# +CONFIG_AVR32=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_TIME=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_IPC_NS is not set +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +# CONFIG_TASKSTATS is not set +# CONFIG_UTS_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +# CONFIG_BASE_FULL is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SHMEM=y +CONFIG_SLAB=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=1 +# CONFIG_SLOB is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# Block layer +# +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" + +# +# System Type and features +# +CONFIG_SUBARCH_AVR32B=y +CONFIG_MMU=y +CONFIG_PERFORMANCE_COUNTERS=y +CONFIG_PLATFORM_AT32AP=y +CONFIG_CPU_AT32AP7000=y +# CONFIG_BOARD_ATSTK1000 is not set +CONFIG_BOARD_ATNGW100=y +CONFIG_LOADER_U_BOOT=y + +# +# Atmel AVR32 AP options +# +# CONFIG_AP7000_32_BIT_SMC is not set +CONFIG_AP7000_16_BIT_SMC=y +# CONFIG_AP7000_8_BIT_SMC is not set +CONFIG_LOAD_ADDRESS=0x10000000 +CONFIG_ENTRY_ADDRESS=0x90000000 +CONFIG_PHYS_OFFSET=0x10000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +# CONFIG_HAVE_ARCH_BOOTMEM_NODE is not set +# CONFIG_ARCH_HAVE_MEMORY_PRESENT is not set +# CONFIG_NEED_NODE_MEMMAP_SIZE is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +# CONFIG_ARCH_SPARSEMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +# CONFIG_OWNERSHIP_TRACE is not set +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +CONFIG_CMDLINE="" + +# +# Bus options +# + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_NETDEBUG is not set +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_MULTIPLE_TABLES is not set +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +# CONFIG_IP_PIMSM_V2 is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +CONFIG_IPV6=y +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +# CONFIG_IPV6_MIP6 is not set +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +CONFIG_NF_CONNTRACK_ENABLED=m +CONFIG_NF_CONNTRACK_SUPPORT=y +# CONFIG_IP_NF_CONNTRACK_SUPPORT is not set +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CT_ACCT=y +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_EVENTS is not set +CONFIG_NF_CT_PROTO_GRE=m +# CONFIG_NF_CT_PROTO_SCTP is not set +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NETFILTER_XTABLES=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m + +# +# IP: Netfilter Configuration +# +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_IPRANGE=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_RECENT=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_NF_NAT_SIP=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_TOS=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m + +# +# IPv6: Netfilter Configuration (EXPERIMENTAL) +# +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_IP6_NF_QUEUE=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_OWNER=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_RAW=m + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +CONFIG_NET_CLS_ROUTE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0x80000000 +CONFIG_MTD_PHYSMAP_LEN=0x0 +CONFIG_MTD_PHYSMAP_BANKWIDTH=2 +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_DATAFLASH=y +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# OneNAND Flash Device Drivers +# +# CONFIG_MTD_ONENAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +# CONFIG_PNPACPI is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# Misc devices +# + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_NETLINK is not set + +# +# Serial ATA (prod) and Parallel ATA (experimental) drivers +# +# CONFIG_ATA is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=m + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_MACB=y + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_MPPE=m +CONFIG_PPPOE=m +# CONFIG_SLIP is not set +CONFIG_SLHC=m +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_ATMEL=y +CONFIG_SERIAL_ATMEL_CONSOLE=y +# CONFIG_SERIAL_ATMEL_TTYAT is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# SPI support +# +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_ATMEL=y +# CONFIG_SPI_BITBANG is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_AT25 is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Hardware Monitoring support +# +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB_ARCH_HAS_HCD is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# LED devices +# +# CONFIG_NEW_LEDS is not set + +# +# LED drivers +# + +# +# LED Triggers +# + +# +# InfiniBand support +# + +# +# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) +# + +# +# Real Time Clock +# +# CONFIG_RTC_CLASS is not set + +# +# DMA Engine support +# +# CONFIG_DMA_ENGINE is not set + +# +# DMA Clients +# + +# +# DMA Devices +# + +# +# Auxiliary Display support +# + +# +# Virtualization +# + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_INOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_DNOTIFY is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=m + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=850 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +CONFIG_CONFIGFS_FS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=y +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Distributed Lock Manager +# +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_LIST is not set +CONFIG_FRAME_POINTER=y +# CONFIG_FORCED_INLINING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_KPROBES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_CRC_CCITT=m +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=m +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y diff --git a/arch/avr32/kernel/cpu.c b/arch/avr32/kernel/cpu.c index 2e72fd2..2714cf6 100644 --- a/arch/avr32/kernel/cpu.c +++ b/arch/avr32/kernel/cpu.c @@ -209,16 +209,17 @@ static const char *mmu_types[] = { void __init setup_processor(void) { unsigned long config0, config1; + unsigned long features; unsigned cpu_id, cpu_rev, arch_id, arch_rev, mmu_type; unsigned tmp; - config0 = sysreg_read(CONFIG0); /* 0x0000013e; */ - config1 = sysreg_read(CONFIG1); /* 0x01f689a2; */ - cpu_id = config0 >> 24; - cpu_rev = (config0 >> 16) & 0xff; - arch_id = (config0 >> 13) & 0x07; - arch_rev = (config0 >> 10) & 0x07; - mmu_type = (config0 >> 7) & 0x03; + config0 = sysreg_read(CONFIG0); + config1 = sysreg_read(CONFIG1); + cpu_id = SYSREG_BFEXT(PROCESSORID, config0); + cpu_rev = SYSREG_BFEXT(PROCESSORREVISION, config0); + arch_id = SYSREG_BFEXT(AT, config0); + arch_rev = SYSREG_BFEXT(AR, config0); + mmu_type = SYSREG_BFEXT(MMUT, config0); boot_cpu_data.arch_type = arch_id; boot_cpu_data.cpu_type = cpu_id; @@ -226,16 +227,16 @@ void __init setup_processor(void) boot_cpu_data.cpu_revision = cpu_rev; boot_cpu_data.tlb_config = mmu_type; - tmp = (config1 >> 13) & 0x07; + tmp = SYSREG_BFEXT(ILSZ, config1); if (tmp) { - boot_cpu_data.icache.ways = 1 << ((config1 >> 10) & 0x07); - boot_cpu_data.icache.sets = 1 << ((config1 >> 16) & 0x0f); + boot_cpu_data.icache.ways = 1 << SYSREG_BFEXT(IASS, config1); + boot_cpu_data.icache.sets = 1 << SYSREG_BFEXT(ISET, config1); boot_cpu_data.icache.linesz = 1 << (tmp + 1); } - tmp = (config1 >> 3) & 0x07; + tmp = SYSREG_BFEXT(DLSZ, config1); if (tmp) { - boot_cpu_data.dcache.ways = 1 << (config1 & 0x07); - boot_cpu_data.dcache.sets = 1 << ((config1 >> 6) & 0x0f); + boot_cpu_data.dcache.ways = 1 << SYSREG_BFEXT(DASS, config1); + boot_cpu_data.dcache.sets = 1 << SYSREG_BFEXT(DSET, config1); boot_cpu_data.dcache.linesz = 1 << (tmp + 1); } @@ -250,16 +251,39 @@ void __init setup_processor(void) cpu_names[cpu_id], cpu_id, cpu_rev, arch_names[arch_id], arch_rev); printk ("CPU: MMU configuration: %s\n", mmu_types[mmu_type]); + printk ("CPU: features:"); - if (config0 & (1 << 6)) - printk(" fpu"); - if (config0 & (1 << 5)) - printk(" java"); - if (config0 & (1 << 4)) - printk(" perfctr"); - if (config0 & (1 << 3)) + features = 0; + if (config0 & SYSREG_BIT(CONFIG0_R)) { + features |= AVR32_FEATURE_RMW; + printk(" rmw"); + } + if (config0 & SYSREG_BIT(CONFIG0_D)) { + features |= AVR32_FEATURE_DSP; + printk(" dsp"); + } + if (config0 & SYSREG_BIT(CONFIG0_S)) { + features |= AVR32_FEATURE_SIMD; + printk(" simd"); + } + if (config0 & SYSREG_BIT(CONFIG0_O)) { + features |= AVR32_FEATURE_OCD; printk(" ocd"); + } + if (config0 & SYSREG_BIT(CONFIG0_P)) { + features |= AVR32_FEATURE_PCTR; + printk(" perfctr"); + } + if (config0 & SYSREG_BIT(CONFIG0_J)) { + features |= AVR32_FEATURE_JAVA; + printk(" java"); + } + if (config0 & SYSREG_BIT(CONFIG0_F)) { + features |= AVR32_FEATURE_FPU; + printk(" fpu"); + } printk("\n"); + boot_cpu_data.features = features; } #ifdef CONFIG_PROC_FS diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S index eeb6679..42657f1 100644 --- a/arch/avr32/kernel/entry-avr32b.S +++ b/arch/avr32/kernel/entry-avr32b.S @@ -100,55 +100,49 @@ dtlb_miss_write: .global tlb_miss_common tlb_miss_common: - mfsr r0, SYSREG_PTBR - mfsr r1, SYSREG_TLBEAR + mfsr r0, SYSREG_TLBEAR + mfsr r1, SYSREG_PTBR /* Is it the vmalloc space? */ - bld r1, 31 + bld r0, 31 brcs handle_vmalloc_miss /* First level lookup */ pgtbl_lookup: - lsr r2, r1, PGDIR_SHIFT - ld.w r0, r0[r2 << 2] - bld r0, _PAGE_BIT_PRESENT + lsr r2, r0, PGDIR_SHIFT + ld.w r3, r1[r2 << 2] + bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT + bld r3, _PAGE_BIT_PRESENT brcc page_table_not_present - /* TODO: Check access rights on page table if necessary */ - /* Translate to virtual address in P1. */ - andl r0, 0xf000 - sbr r0, 31 + andl r3, 0xf000 + sbr r3, 31 /* Second level lookup */ - lsl r1, (32 - PGDIR_SHIFT) - lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT - add r2, r0, r1 << 2 - ld.w r1, r2[0] - bld r1, _PAGE_BIT_PRESENT + ld.w r2, r3[r1 << 2] + mfsr r0, SYSREG_TLBARLO + bld r2, _PAGE_BIT_PRESENT brcc page_not_present /* Mark the page as accessed */ - sbr r1, _PAGE_BIT_ACCESSED - st.w r2[0], r1 + sbr r2, _PAGE_BIT_ACCESSED + st.w r3[r1 << 2], r2 /* Drop software flags */ - andl r1, _PAGE_FLAGS_HARDWARE_MASK & 0xffff - mtsr SYSREG_TLBELO, r1 + andl r2, _PAGE_FLAGS_HARDWARE_MASK & 0xffff + mtsr SYSREG_TLBELO, r2 /* Figure out which entry we want to replace */ - mfsr r0, SYSREG_TLBARLO + mfsr r1, SYSREG_MMUCR clz r2, r0 brcc 1f - mov r1, -1 /* All entries have been accessed, */ - mtsr SYSREG_TLBARLO, r1 /* so reset TLBAR */ - mov r2, 0 /* and start at 0 */ -1: mfsr r1, SYSREG_MMUCR - lsl r2, 14 - andl r1, 0x3fff, COH - or r1, r2 - mtsr SYSREG_MMUCR, r1 + mov r3, -1 /* All entries have been accessed, */ + mov r2, 0 /* so start at 0 */ + mtsr SYSREG_TLBARLO, r3 /* and reset TLBAR */ +1: bfins r1, r2, SYSREG_DRP_OFFSET, SYSREG_DRP_SIZE + mtsr SYSREG_MMUCR, r1 tlbw tlbmiss_restore @@ -156,8 +150,8 @@ pgtbl_lookup: handle_vmalloc_miss: /* Simply do the lookup in init's page table */ - mov r0, lo(swapper_pg_dir) - orh r0, hi(swapper_pg_dir) + mov r1, lo(swapper_pg_dir) + orh r1, hi(swapper_pg_dir) rjmp pgtbl_lookup @@ -340,12 +334,34 @@ do_bus_error_read: do_nmi_ll: sub sp, 4 stmts --sp, r0-lr - /* FIXME: Make sure RAR_NMI and RSR_NMI are pushed instead of *_EX */ - rcall save_full_context_ex + mfsr r9, SYSREG_RSR_NMI + mfsr r8, SYSREG_RAR_NMI + bfextu r0, r9, MODE_SHIFT, 3 + brne 2f + +1: pushm r8, r9 /* PC and SR */ mfsr r12, SYSREG_ECR mov r11, sp rcall do_nmi - rjmp bad_return + popm r8-r9 + mtsr SYSREG_RAR_NMI, r8 + tst r0, r0 + mtsr SYSREG_RSR_NMI, r9 + brne 3f + + ldmts sp++, r0-lr + sub sp, -4 /* skip r12_orig */ + rete + +2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR) + stdsp sp[4], r10 /* replace saved SP */ + rjmp 1b + +3: popm lr + sub sp, -4 /* skip sp */ + popm r0-r12 + sub sp, -4 /* skip r12_orig */ + rete handle_address_fault: sub sp, 4 @@ -630,9 +646,12 @@ irq_level\level: rcall do_IRQ lddsp r4, sp[REG_SR] - andh r4, (MODE_MASK >> 16), COH + bfextu r4, r4, SYSREG_M0_OFFSET, 3 + cp.w r4, MODE_SUPERVISOR >> SYSREG_M0_OFFSET + breq 2f + cp.w r4, MODE_USER >> SYSREG_M0_OFFSET #ifdef CONFIG_PREEMPT - brne 2f + brne 3f #else brne 1f #endif @@ -649,9 +668,18 @@ #endif sub sp, -4 /* ignore r12_orig */ rete +2: get_thread_info r0 + ld.w r1, r0[TI_flags] + bld r1, TIF_CPU_GOING_TO_SLEEP #ifdef CONFIG_PREEMPT -2: - get_thread_info r0 + brcc 3f +#else + brcc 1b +#endif + sub r1, pc, . - cpu_idle_skip_sleep + stdsp sp[REG_PC], r1 +#ifdef CONFIG_PREEMPT +3: get_thread_info r0 ld.w r2, r0[TI_preempt_count] cp.w r2, 0 brne 1b @@ -662,12 +690,32 @@ #ifdef CONFIG_PREEMPT bld r4, SYSREG_GM_OFFSET brcs 1b rcall preempt_schedule_irq - rjmp 1b #endif + rjmp 1b .endm .section .irq.text,"ax",@progbits +.global cpu_idle_sleep +cpu_idle_sleep: + mask_interrupts + get_thread_info r8 + ld.w r9, r8[TI_flags] + bld r9, TIF_NEED_RESCHED + brcs cpu_idle_enable_int_and_exit + sbr r9, TIF_CPU_GOING_TO_SLEEP + st.w r8[TI_flags], r9 + unmask_interrupts + sleep 0 +cpu_idle_skip_sleep: + mask_interrupts + ld.w r9, r8[TI_flags] + cbr r9, TIF_CPU_GOING_TO_SLEEP + st.w r8[TI_flags], r9 +cpu_idle_enable_int_and_exit: + unmask_interrupts + retal r12 + .global irq_level0 .global irq_level1 .global irq_level2 diff --git a/arch/avr32/kernel/module.c b/arch/avr32/kernel/module.c index b599eae..1167fe9 100644 --- a/arch/avr32/kernel/module.c +++ b/arch/avr32/kernel/module.c @@ -12,10 +12,11 @@ * published by the Free Software Foundation. */ -#include -#include -#include +#include #include +#include +#include +#include #include void *module_alloc(unsigned long size) @@ -315,10 +316,10 @@ int module_finalize(const Elf_Ehdr *hdr, vfree(module->arch.syminfo); module->arch.syminfo = NULL; - return 0; + return module_bug_finalize(hdr, sechdrs, module); } void module_arch_cleanup(struct module *module) { - + module_bug_cleanup(module); } diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 0b43259..4e4181e 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -11,6 +11,7 @@ #include #include #include #include +#include #include #include @@ -19,6 +20,8 @@ #include void (*pm_power_off)(void) = NULL; EXPORT_SYMBOL(pm_power_off); +extern void cpu_idle_sleep(void); + /* * This file handles the architecture-dependent parts of process handling.. */ @@ -27,9 +30,8 @@ void cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { - /* TODO: Enter sleep mode */ while (!need_resched()) - cpu_relax(); + cpu_idle_sleep(); preempt_enable_no_resched(); schedule(); preempt_disable(); @@ -114,39 +116,178 @@ void release_thread(struct task_struct * /* do nothing */ } +static void dump_mem(const char *str, const char *log_lvl, + unsigned long bottom, unsigned long top) +{ + unsigned long p; + int i; + + printk("%s%s(0x%08lx to 0x%08lx)\n", log_lvl, str, bottom, top); + + for (p = bottom & ~31; p < top; ) { + printk("%s%04lx: ", log_lvl, p & 0xffff); + + for (i = 0; i < 8; i++, p += 4) { + unsigned int val; + + if (p < bottom || p >= top) + printk(" "); + else { + if (__get_user(val, (unsigned int __user *)p)) { + printk("\n"); + goto out; + } + printk("%08x ", val); + } + } + printk("\n"); + } + +out: + return; +} + +static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p) +{ + return (p > (unsigned long)tinfo) + && (p < (unsigned long)tinfo + THREAD_SIZE - 3); +} + +#ifdef CONFIG_FRAME_POINTER +static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp, + struct pt_regs *regs, const char *log_lvl) +{ + unsigned long lr, fp; + struct thread_info *tinfo; + + if (regs) + fp = regs->r7; + else if (tsk == current) + asm("mov %0, r7" : "=r"(fp)); + else + fp = tsk->thread.cpu_context.r7; + + /* + * Walk the stack as long as the frame pointer (a) is within + * the kernel stack of the task, and (b) it doesn't move + * downwards. + */ + tinfo = task_thread_info(tsk); + printk("%sCall trace:\n", log_lvl); + while (valid_stack_ptr(tinfo, fp)) { + unsigned long new_fp; + + lr = *(unsigned long *)fp; +#ifdef CONFIG_KALLSYMS + printk("%s [<%08lx>] ", log_lvl, lr); +#else + printk(" [<%08lx>] ", lr); +#endif + print_symbol("%s\n", lr); + + new_fp = *(unsigned long *)(fp + 4); + if (new_fp <= fp) + break; + fp = new_fp; + } + printk("\n"); +} +#else +static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp, + struct pt_regs *regs, const char *log_lvl) +{ + unsigned long addr; + + printk("%sCall trace:\n", log_lvl); + + while (!kstack_end(sp)) { + addr = *sp++; + if (kernel_text_address(addr)) { +#ifdef CONFIG_KALLSYMS + printk("%s [<%08lx>] ", log_lvl, addr); +#else + printk(" [<%08lx>] ", addr); +#endif + print_symbol("%s\n", addr); + } + } + printk("\n"); +} +#endif + +void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp, + struct pt_regs *regs, const char *log_lvl) +{ + struct thread_info *tinfo; + + if (sp == 0) { + if (tsk) + sp = tsk->thread.cpu_context.ksp; + else + sp = (unsigned long)&tinfo; + } + if (!tsk) + tsk = current; + + tinfo = task_thread_info(tsk); + + if (valid_stack_ptr(tinfo, sp)) { + dump_mem("Stack: ", log_lvl, sp, + THREAD_SIZE + (unsigned long)tinfo); + show_trace_log_lvl(tsk, (unsigned long *)sp, regs, log_lvl); + } +} + +void show_stack(struct task_struct *tsk, unsigned long *stack) +{ + show_stack_log_lvl(tsk, (unsigned long)stack, NULL, ""); +} + +void dump_stack(void) +{ + unsigned long stack; + + show_trace_log_lvl(current, &stack, NULL, ""); +} +EXPORT_SYMBOL(dump_stack); + static const char *cpu_modes[] = { "Application", "Supervisor", "Interrupt level 0", "Interrupt level 1", "Interrupt level 2", "Interrupt level 3", "Exception", "NMI" }; -void show_regs(struct pt_regs *regs) +void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl) { unsigned long sp = regs->sp; unsigned long lr = regs->lr; unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT; - if (!user_mode(regs)) + if (!user_mode(regs)) { sp = (unsigned long)regs + FRAME_SIZE_FULL; - print_symbol("PC is at %s\n", instruction_pointer(regs)); - print_symbol("LR is at %s\n", lr); - printk("pc : [<%08lx>] lr : [<%08lx>] %s\n" - "sp : %08lx r12: %08lx r11: %08lx\n", - instruction_pointer(regs), - lr, print_tainted(), sp, regs->r12, regs->r11); - printk("r10: %08lx r9 : %08lx r8 : %08lx\n", - regs->r10, regs->r9, regs->r8); - printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", - regs->r7, regs->r6, regs->r5, regs->r4); - printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", - regs->r3, regs->r2, regs->r1, regs->r0); - printk("Flags: %c%c%c%c%c\n", + printk("%s", log_lvl); + print_symbol("PC is at %s\n", instruction_pointer(regs)); + printk("%s", log_lvl); + print_symbol("LR is at %s\n", lr); + } + + printk("%spc : [<%08lx>] lr : [<%08lx>] %s\n" + "%ssp : %08lx r12: %08lx r11: %08lx\n", + log_lvl, instruction_pointer(regs), lr, print_tainted(), + log_lvl, sp, regs->r12, regs->r11); + printk("%sr10: %08lx r9 : %08lx r8 : %08lx\n", + log_lvl, regs->r10, regs->r9, regs->r8); + printk("%sr7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", + log_lvl, regs->r7, regs->r6, regs->r5, regs->r4); + printk("%sr3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", + log_lvl, regs->r3, regs->r2, regs->r1, regs->r0); + printk("%sFlags: %c%c%c%c%c\n", log_lvl, regs->sr & SR_Q ? 'Q' : 'q', regs->sr & SR_V ? 'V' : 'v', regs->sr & SR_N ? 'N' : 'n', regs->sr & SR_Z ? 'Z' : 'z', regs->sr & SR_C ? 'C' : 'c'); - printk("Mode bits: %c%c%c%c%c%c%c%c%c\n", + printk("%sMode bits: %c%c%c%c%c%c%c%c%c\n", log_lvl, regs->sr & SR_H ? 'H' : 'h', regs->sr & SR_R ? 'R' : 'r', regs->sr & SR_J ? 'J' : 'j', @@ -156,9 +297,21 @@ void show_regs(struct pt_regs *regs) regs->sr & SR_I1M ? '1' : '.', regs->sr & SR_I0M ? '0' : '.', regs->sr & SR_GM ? 'G' : 'g'); - printk("CPU Mode: %s\n", cpu_modes[mode]); + printk("%sCPU Mode: %s\n", log_lvl, cpu_modes[mode]); + printk("%sProcess: %s [%d] (task: %p thread: %p)\n", + log_lvl, current->comm, current->pid, current, + task_thread_info(current)); +} + +void show_regs(struct pt_regs *regs) +{ + unsigned long sp = regs->sp; + + if (!user_mode(regs)) + sp = (unsigned long)regs + FRAME_SIZE_FULL; - show_trace(NULL, (unsigned long *)sp, regs); + show_regs_log_lvl(regs, ""); + show_trace_log_lvl(current, (unsigned long *)sp, regs, ""); } EXPORT_SYMBOL(show_regs); diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index a1a7c3c..b279d66 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -8,12 +8,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -30,13 +32,6 @@ #include extern int root_mountflags; /* - * Bootloader-provided information about physical memory - */ -struct tag_mem_range *mem_phys; -struct tag_mem_range *mem_reserved; -struct tag_mem_range *mem_ramdisk; - -/* * Initialize loops_per_jiffy as 5000000 (500MIPS). * Better make it too large than too small... */ @@ -48,48 +43,193 @@ EXPORT_SYMBOL(boot_cpu_data); static char __initdata command_line[COMMAND_LINE_SIZE]; /* - * Should be more than enough, but if you have a _really_ complex - * setup, you might need to increase the size of this... + * Standard memory resources */ -static struct tag_mem_range __initdata mem_range_cache[32]; -static unsigned mem_range_next_free; +static struct resource __initdata kernel_data = { + .name = "Kernel data", + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM, +}; +static struct resource __initdata kernel_code = { + .name = "Kernel code", + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM, + .sibling = &kernel_data, +}; /* - * Standard memory resources + * Available system RAM and reserved regions as singly linked + * lists. These lists are traversed using the sibling pointer in + * struct resource and are kept sorted at all times. */ -static struct resource mem_res[] = { - { - .name = "Kernel code", - .start = 0, - .end = 0, - .flags = IORESOURCE_MEM - }, - { - .name = "Kernel data", - .start = 0, - .end = 0, - .flags = IORESOURCE_MEM, - }, -}; +static struct resource *__initdata system_ram; +static struct resource *__initdata reserved = &kernel_code; + +/* + * We need to allocate these before the bootmem allocator is up and + * running, so we need this "cache". 32 entries are probably enough + * for all but the most insanely complex systems. + */ +static struct resource __initdata res_cache[32]; +static unsigned int __initdata res_cache_next_free; + +static void __init resource_init(void) +{ + struct resource *mem, *res; + struct resource *new; + + kernel_code.start = __pa(init_mm.start_code); + + for (mem = system_ram; mem; mem = mem->sibling) { + new = alloc_bootmem_low(sizeof(struct resource)); + memcpy(new, mem, sizeof(struct resource)); + + new->sibling = NULL; + if (request_resource(&iomem_resource, new)) + printk(KERN_WARNING "Bad RAM resource %08x-%08x\n", + mem->start, mem->end); + } + + for (res = reserved; res; res = res->sibling) { + new = alloc_bootmem_low(sizeof(struct resource)); + memcpy(new, res, sizeof(struct resource)); + + new->sibling = NULL; + if (insert_resource(&iomem_resource, new)) + printk(KERN_WARNING + "Bad reserved resource %s (%08x-%08x)\n", + res->name, res->start, res->end); + } +} + +static void __init +add_physical_memory(resource_size_t start, resource_size_t end) +{ + struct resource *new, *next, **pprev; + + for (pprev = &system_ram, next = system_ram; next; + pprev = &next->sibling, next = next->sibling) { + if (end < next->start) + break; + if (start <= next->end) { + printk(KERN_WARNING + "Warning: Physical memory map is broken\n"); + printk(KERN_WARNING + "Warning: %08x-%08x overlaps %08x-%08x\n", + start, end, next->start, next->end); + return; + } + } + + if (res_cache_next_free >= ARRAY_SIZE(res_cache)) { + printk(KERN_WARNING + "Warning: Failed to add physical memory %08x-%08x\n", + start, end); + return; + } + + new = &res_cache[res_cache_next_free++]; + new->start = start; + new->end = end; + new->name = "System RAM"; + new->flags = IORESOURCE_MEM; + + *pprev = new; +} + +static int __init +add_reserved_region(resource_size_t start, resource_size_t end, + const char *name) +{ + struct resource *new, *next, **pprev; + + if (end < start) + return -EINVAL; + + if (res_cache_next_free >= ARRAY_SIZE(res_cache)) + return -ENOMEM; + + for (pprev = &reserved, next = reserved; next; + pprev = &next->sibling, next = next->sibling) { + if (end < next->start) + break; + if (start <= next->end) + return -EBUSY; + } + + new = &res_cache[res_cache_next_free++]; + new->start = start; + new->end = end; + new->name = name; + new->flags = IORESOURCE_MEM; + + *pprev = new; + + return 0; +} + +static unsigned long __init +find_free_region(const struct resource *mem, resource_size_t size, + resource_size_t align) +{ + struct resource *res; + unsigned long target; + + target = ALIGN(mem->start, align); + for (res = reserved; res; res = res->sibling) { + if ((target + size) <= res->start) + break; + if (target <= res->end) + target = ALIGN(res->end + 1, align); + } + + if ((target + size) > (mem->end + 1)) + return mem->end + 1; + + return target; +} + +static int __init +alloc_reserved_region(resource_size_t *start, resource_size_t size, + resource_size_t align, const char *name) +{ + struct resource *mem; + resource_size_t target; + int ret; + + for (mem = system_ram; mem; mem = mem->sibling) { + target = find_free_region(mem, size, align); + if (target <= mem->end) { + ret = add_reserved_region(target, target + size - 1, + name); + if (!ret) + *start = target; + return ret; + } + } -#define kernel_code mem_res[0] -#define kernel_data mem_res[1] + return -ENOMEM; +} /* * Early framebuffer allocation. Works as follows: * - If fbmem_size is zero, nothing will be allocated or reserved. * - If fbmem_start is zero when setup_bootmem() is called, - * fbmem_size bytes will be allocated from the bootmem allocator. + * a block of fbmem_size bytes will be reserved before bootmem + * initialization. It will be aligned to the largest page size + * that fbmem_size is a multiple of. * - If fbmem_start is nonzero, an area of size fbmem_size will be - * reserved at the physical address fbmem_start if necessary. If - * the area isn't in a memory region known to the kernel, it will - * be left alone. + * reserved at the physical address fbmem_start if possible. If + * it collides with other reserved memory, a different block of + * same size will be allocated, just as if fbmem_start was zero. * * Board-specific code may use these variables to set up platform data * for the framebuffer driver if fbmem_size is nonzero. */ -static unsigned long __initdata fbmem_start; -static unsigned long __initdata fbmem_size; +resource_size_t __initdata fbmem_start; +resource_size_t __initdata fbmem_size; /* * "fbmem=xxx[kKmM]" allocates the specified amount of boot memory for @@ -103,48 +243,42 @@ static unsigned long __initdata fbmem_si */ static int __init early_parse_fbmem(char *p) { + int ret; + unsigned long align; + fbmem_size = memparse(p, &p); - if (*p == '@') + if (*p == '@') { fbmem_start = memparse(p, &p); - return 0; -} -early_param("fbmem", early_parse_fbmem); - -static inline void __init resource_init(void) -{ - struct tag_mem_range *region; - - kernel_code.start = __pa(init_mm.start_code); - kernel_code.end = __pa(init_mm.end_code - 1); - kernel_data.start = __pa(init_mm.end_code); - kernel_data.end = __pa(init_mm.brk - 1); - - for (region = mem_phys; region; region = region->next) { - struct resource *res; - unsigned long phys_start, phys_end; - - if (region->size == 0) - continue; - - phys_start = region->addr; - phys_end = phys_start + region->size - 1; - - res = alloc_bootmem_low(sizeof(*res)); - res->name = "System RAM"; - res->start = phys_start; - res->end = phys_end; - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - - request_resource (&iomem_resource, res); + ret = add_reserved_region(fbmem_start, + fbmem_start + fbmem_size - 1, + "Framebuffer"); + if (ret) { + printk(KERN_WARNING + "Failed to reserve framebuffer memory\n"); + fbmem_start = 0; + } + } - if (kernel_code.start >= res->start && - kernel_code.end <= res->end) - request_resource (res, &kernel_code); - if (kernel_data.start >= res->start && - kernel_data.end <= res->end) - request_resource (res, &kernel_data); + if (!fbmem_start) { + if ((fbmem_size & 0x000fffffUL) == 0) + align = 0x100000; /* 1 MiB */ + else if ((fbmem_size & 0x0000ffffUL) == 0) + align = 0x10000; /* 64 KiB */ + else + align = 0x1000; /* 4 KiB */ + + ret = alloc_reserved_region(&fbmem_start, fbmem_size, + align, "Framebuffer"); + if (ret) { + printk(KERN_WARNING + "Failed to allocate framebuffer memory\n"); + fbmem_size = 0; + } } + + return 0; } +early_param("fbmem", early_parse_fbmem); static int __init parse_tag_core(struct tag *tag) { @@ -157,11 +291,9 @@ static int __init parse_tag_core(struct } __tagtable(ATAG_CORE, parse_tag_core); -static int __init parse_tag_mem_range(struct tag *tag, - struct tag_mem_range **root) +static int __init parse_tag_mem(struct tag *tag) { - struct tag_mem_range *cur, **pprev; - struct tag_mem_range *new; + unsigned long start, end; /* * Ignore zero-sized entries. If we're running standalone, the @@ -171,34 +303,53 @@ static int __init parse_tag_mem_range(st if (tag->u.mem_range.size == 0) return 0; - /* - * Copy the data so the bootmem init code doesn't need to care - * about it. - */ - if (mem_range_next_free >= ARRAY_SIZE(mem_range_cache)) - panic("Physical memory map too complex!\n"); + start = tag->u.mem_range.addr; + end = tag->u.mem_range.addr + tag->u.mem_range.size - 1; + + add_physical_memory(start, end); + return 0; +} +__tagtable(ATAG_MEM, parse_tag_mem); + +static int __init parse_tag_rdimg(struct tag *tag) +{ +#ifdef CONFIG_INITRD + struct tag_mem_range *mem = &tag->u.mem_range; + int ret; - new = &mem_range_cache[mem_range_next_free++]; - *new = tag->u.mem_range; + if (initrd_start) { + printk(KERN_WARNING + "Warning: Only the first initrd image will be used\n"); + return 0; + } - pprev = root; - cur = *root; - while (cur) { - pprev = &cur->next; - cur = cur->next; + ret = add_reserved_region(mem->start, mem->start + mem->size - 1, + "initrd"); + if (ret) { + printk(KERN_WARNING + "Warning: Failed to reserve initrd memory\n"); + return ret; } - *pprev = new; - new->next = NULL; + initrd_start = (unsigned long)__va(mem->addr); + initrd_end = initrd_start + mem->size; +#else + printk(KERN_WARNING "RAM disk image present, but " + "no initrd support in kernel, ignoring\n"); +#endif return 0; } +__tagtable(ATAG_RDIMG, parse_tag_rdimg); -static int __init parse_tag_mem(struct tag *tag) +static int __init parse_tag_rsvd_mem(struct tag *tag) { - return parse_tag_mem_range(tag, &mem_phys); + struct tag_mem_range *mem = &tag->u.mem_range; + + return add_reserved_region(mem->addr, mem->addr + mem->size - 1, + "Reserved"); } -__tagtable(ATAG_MEM, parse_tag_mem); +__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem); static int __init parse_tag_cmdline(struct tag *tag) { @@ -207,12 +358,6 @@ static int __init parse_tag_cmdline(stru } __tagtable(ATAG_CMDLINE, parse_tag_cmdline); -static int __init parse_tag_rdimg(struct tag *tag) -{ - return parse_tag_mem_range(tag, &mem_ramdisk); -} -__tagtable(ATAG_RDIMG, parse_tag_rdimg); - static int __init parse_tag_clock(struct tag *tag) { /* @@ -223,12 +368,6 @@ static int __init parse_tag_clock(struct } __tagtable(ATAG_CLOCK, parse_tag_clock); -static int __init parse_tag_rsvd_mem(struct tag *tag) -{ - return parse_tag_mem_range(tag, &mem_reserved); -} -__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem); - /* * Scan the tag table for this tag, and call its parse function. The * tag table is built by the linker from all the __tagtable @@ -260,10 +399,137 @@ static void __init parse_tags(struct tag t->hdr.tag); } +/* + * Find a free memory region large enough for storing the + * bootmem bitmap. + */ +static unsigned long __init +find_bootmap_pfn(const struct resource *mem) +{ + unsigned long bootmap_pages, bootmap_len; + unsigned long node_pages = PFN_UP(mem->end - mem->start + 1); + unsigned long bootmap_start; + + bootmap_pages = bootmem_bootmap_pages(node_pages); + bootmap_len = bootmap_pages << PAGE_SHIFT; + + /* + * Find a large enough region without reserved pages for + * storing the bootmem bitmap. We can take advantage of the + * fact that all lists have been sorted. + * + * We have to check that we don't collide with any reserved + * regions, which includes the kernel image and any RAMDISK + * images. + */ + bootmap_start = find_free_region(mem, bootmap_len, PAGE_SIZE); + + return bootmap_start >> PAGE_SHIFT; +} + +#define MAX_LOWMEM HIGHMEM_START +#define MAX_LOWMEM_PFN PFN_DOWN(MAX_LOWMEM) + +static void __init setup_bootmem(void) +{ + unsigned bootmap_size; + unsigned long first_pfn, bootmap_pfn, pages; + unsigned long max_pfn, max_low_pfn; + unsigned node = 0; + struct resource *res; + + printk(KERN_INFO "Physical memory:\n"); + for (res = system_ram; res; res = res->sibling) + printk(" %08x-%08x\n", res->start, res->end); + printk(KERN_INFO "Reserved memory:\n"); + for (res = reserved; res; res = res->sibling) + printk(" %08x-%08x: %s\n", + res->start, res->end, res->name); + + nodes_clear(node_online_map); + + if (system_ram->sibling) + printk(KERN_WARNING "Only using first memory bank\n"); + + for (res = system_ram; res; res = NULL) { + first_pfn = PFN_UP(res->start); + max_low_pfn = max_pfn = PFN_DOWN(res->end + 1); + bootmap_pfn = find_bootmap_pfn(res); + if (bootmap_pfn > max_pfn) + panic("No space for bootmem bitmap!\n"); + + if (max_low_pfn > MAX_LOWMEM_PFN) { + max_low_pfn = MAX_LOWMEM_PFN; +#ifndef CONFIG_HIGHMEM + /* + * Lowmem is memory that can be addressed + * directly through P1/P2 + */ + printk(KERN_WARNING + "Node %u: Only %ld MiB of memory will be used.\n", + node, MAX_LOWMEM >> 20); + printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); +#else +#error HIGHMEM is not supported by AVR32 yet +#endif + } + + /* Initialize the boot-time allocator with low memory only. */ + bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn, + first_pfn, max_low_pfn); + + /* + * Register fully available RAM pages with the bootmem + * allocator. + */ + pages = max_low_pfn - first_pfn; + free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn), + PFN_PHYS(pages)); + + /* Reserve space for the bootmem bitmap... */ + reserve_bootmem_node(NODE_DATA(node), + PFN_PHYS(bootmap_pfn), + bootmap_size); + + /* ...and any other reserved regions. */ + for (res = reserved; res; res = res->sibling) { + if (res->start > PFN_PHYS(max_pfn)) + break; + + /* + * resource_init will complain about partial + * overlaps, so we'll just ignore such + * resources for now. + */ + if (res->start >= PFN_PHYS(first_pfn) + && res->end < PFN_PHYS(max_pfn)) + reserve_bootmem_node( + NODE_DATA(node), res->start, + res->end - res->start + 1); + } + + node_set_online(node); + } +} + void __init setup_arch (char **cmdline_p) { struct clk *cpu_clk; + init_mm.start_code = (unsigned long)_text; + init_mm.end_code = (unsigned long)_etext; + init_mm.end_data = (unsigned long)_edata; + init_mm.brk = (unsigned long)_end; + + /* + * Include .init section to make allocations easier. It will + * be removed before the resource is actually requested. + */ + kernel_code.start = __pa(__init_begin); + kernel_code.end = __pa(init_mm.end_code - 1); + kernel_data.start = __pa(init_mm.end_code); + kernel_data.end = __pa(init_mm.brk - 1); + parse_tags(bootloader_tags); setup_processor(); @@ -289,24 +555,16 @@ void __init setup_arch (char **cmdline_p ((cpu_hz + 500) / 1000) % 1000); } - init_mm.start_code = (unsigned long) &_text; - init_mm.end_code = (unsigned long) &_etext; - init_mm.end_data = (unsigned long) &_edata; - init_mm.brk = (unsigned long) &_end; - strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; parse_early_param(); setup_bootmem(); - board_setup_fbmem(fbmem_start, fbmem_size); - #ifdef CONFIG_VT conswitchp = &dummy_con; #endif paging_init(); - resource_init(); } diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c index c10833f..7014a35 100644 --- a/arch/avr32/kernel/time.c +++ b/arch/avr32/kernel/time.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2006 Atmel Corporation + * Copyright (C) 2004-2007 Atmel Corporation * * Based on MIPS implementation arch/mips/kernel/time.c * Copyright 2001 MontaVista Software Inc. @@ -20,18 +20,25 @@ #include #include #include #include +#include #include #include #include #include -static cycle_t read_cycle_count(void) +/* how many counter cycles in a jiffy? */ +static u32 cycles_per_jiffy; + +/* the count value for the next timer interrupt */ +static u32 expirelo; + +cycle_t __weak read_cycle_count(void) { return (cycle_t)sysreg_read(COUNT); } -static struct clocksource clocksource_avr32 = { +struct clocksource __weak clocksource_avr32 = { .name = "avr32", .rating = 350, .read = read_cycle_count, @@ -40,12 +47,20 @@ static struct clocksource clocksource_av .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; +irqreturn_t __weak timer_interrupt(int irq, void *dev_id); + +struct irqaction timer_irqaction = { + .handler = timer_interrupt, + .flags = IRQF_DISABLED, + .name = "timer", +}; + /* * By default we provide the null RTC ops */ static unsigned long null_rtc_get_time(void) { - return mktime(2004, 1, 1, 0, 0, 0); + return mktime(2007, 1, 1, 0, 0, 0); } static int null_rtc_set_time(unsigned long sec) @@ -56,23 +71,14 @@ static int null_rtc_set_time(unsigned lo static unsigned long (*rtc_get_time)(void) = null_rtc_get_time; static int (*rtc_set_time)(unsigned long) = null_rtc_set_time; -/* how many counter cycles in a jiffy? */ -static unsigned long cycles_per_jiffy; - -/* cycle counter value at the previous timer interrupt */ -static unsigned int timerhi, timerlo; - -/* the count value for the next timer interrupt */ -static unsigned int expirelo; - static void avr32_timer_ack(void) { - unsigned int count; + u32 count; /* Ack this timer interrupt and set the next one */ expirelo += cycles_per_jiffy; + /* setting COMPARE to 0 stops the COUNT-COMPARE */ if (expirelo == 0) { - printk(KERN_DEBUG "expirelo == 0\n"); sysreg_write(COMPARE, expirelo + 1); } else { sysreg_write(COMPARE, expirelo); @@ -86,27 +92,56 @@ static void avr32_timer_ack(void) } } -static unsigned int avr32_hpt_read(void) +int __weak avr32_hpt_init(void) { - return sysreg_read(COUNT); + int ret; + unsigned long mult, shift, count_hz; + + count_hz = clk_get_rate(boot_cpu_data.clk); + shift = clocksource_avr32.shift; + mult = clocksource_hz2mult(count_hz, shift); + clocksource_avr32.mult = mult; + + { + u64 tmp; + + tmp = TICK_NSEC; + tmp <<= shift; + tmp += mult / 2; + do_div(tmp, mult); + + cycles_per_jiffy = tmp; + } + + ret = setup_irq(0, &timer_irqaction); + if (ret) { + pr_debug("timer: could not request IRQ 0: %d\n", ret); + return -ENODEV; + } + + printk(KERN_INFO "timer: AT32AP COUNT-COMPARE at irq 0, " + "%lu.%03lu MHz\n", + ((count_hz + 500) / 1000) / 1000, + ((count_hz + 500) / 1000) % 1000); + + return 0; } /* * Taken from MIPS c0_hpt_timer_init(). * - * Why is it so complicated, and what is "count"? My assumption is - * that `count' specifies the "reference cycle", i.e. the cycle since - * reset that should mean "zero". The reason COUNT is written twice is - * probably to make sure we don't get any timer interrupts while we - * are messing with the counter. + * The reason COUNT is written twice is probably to make sure we don't get any + * timer interrupts while we are messing with the counter. */ -static void avr32_hpt_init(unsigned int count) +int __weak avr32_hpt_start(void) { - count = sysreg_read(COUNT) - count; + u32 count = sysreg_read(COUNT); expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy; sysreg_write(COUNT, expirelo - cycles_per_jiffy); sysreg_write(COMPARE, expirelo); sysreg_write(COUNT, count); + + return 0; } /* @@ -115,26 +150,18 @@ static void avr32_hpt_init(unsigned int * * In UP mode, it is invoked from the (global) timer_interrupt. */ -static void local_timer_interrupt(int irq, void *dev_id) +void local_timer_interrupt(int irq, void *dev_id) { if (current->pid) profile_tick(CPU_PROFILING); update_process_times(user_mode(get_irq_regs())); } -static irqreturn_t -timer_interrupt(int irq, void *dev_id) +irqreturn_t __weak timer_interrupt(int irq, void *dev_id) { - unsigned int count; - /* ack timer interrupt and try to set next interrupt */ - count = avr32_hpt_read(); avr32_timer_ack(); - /* Update timerhi/timerlo for intra-jiffy calibration */ - timerhi += count < timerlo; /* Wrap around */ - timerlo = count; - /* * Call the generic timer interrupt handler */ @@ -153,60 +180,37 @@ timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction timer_irqaction = { - .handler = timer_interrupt, - .flags = IRQF_DISABLED, - .name = "timer", -}; - void __init time_init(void) { - unsigned long mult, shift, count_hz; int ret; + /* + * Make sure we don't get any COMPARE interrupts before we can + * handle them. + */ + sysreg_write(COMPARE, 0); + xtime.tv_sec = rtc_get_time(); xtime.tv_nsec = 0; set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); - printk("Before time_init: count=%08lx, compare=%08lx\n", - (unsigned long)sysreg_read(COUNT), - (unsigned long)sysreg_read(COMPARE)); - - count_hz = clk_get_rate(boot_cpu_data.clk); - shift = clocksource_avr32.shift; - mult = clocksource_hz2mult(count_hz, shift); - clocksource_avr32.mult = mult; - - printk("Cycle counter: mult=%lu, shift=%lu\n", mult, shift); - - { - u64 tmp; - - tmp = TICK_NSEC; - tmp <<= shift; - tmp += mult / 2; - do_div(tmp, mult); - - cycles_per_jiffy = tmp; + ret = avr32_hpt_init(); + if (ret) { + pr_debug("timer: failed setup: %d\n", ret); + return; } - /* This sets up the high precision timer for the first interrupt. */ - avr32_hpt_init(avr32_hpt_read()); - - printk("After time_init: count=%08lx, compare=%08lx\n", - (unsigned long)sysreg_read(COUNT), - (unsigned long)sysreg_read(COMPARE)); - ret = clocksource_register(&clocksource_avr32); if (ret) - printk(KERN_ERR - "timer: could not register clocksource: %d\n", ret); + pr_debug("timer: could not register clocksource: %d\n", ret); - ret = setup_irq(0, &timer_irqaction); - if (ret) - printk("timer: could not request IRQ 0: %d\n", ret); + ret = avr32_hpt_start(); + if (ret) { + pr_debug("timer: failed starting: %d\n", ret); + return; + } } static struct sysdev_class timer_class = { diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c index adc01a1..4f0382d 100644 --- a/arch/avr32/kernel/traps.c +++ b/arch/avr32/kernel/traps.c @@ -5,158 +5,25 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#undef DEBUG -#include + +#include #include -#include #include +#include #include +#include +#include -#include -#include #include -#include #include -#include - -static void dump_mem(const char *str, unsigned long bottom, unsigned long top) -{ - unsigned long p; - int i; - - printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); - - for (p = bottom & ~31; p < top; ) { - printk("%04lx: ", p & 0xffff); - - for (i = 0; i < 8; i++, p += 4) { - unsigned int val; - - if (p < bottom || p >= top) - printk(" "); - else { - if (__get_user(val, (unsigned int __user *)p)) { - printk("\n"); - goto out; - } - printk("%08x ", val); - } - } - printk("\n"); - } - -out: - return; -} - -static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p) -{ - return (p > (unsigned long)tinfo) - && (p < (unsigned long)tinfo + THREAD_SIZE - 3); -} - -#ifdef CONFIG_FRAME_POINTER -static inline void __show_trace(struct task_struct *tsk, unsigned long *sp, - struct pt_regs *regs) -{ - unsigned long lr, fp; - struct thread_info *tinfo; - - tinfo = (struct thread_info *) - ((unsigned long)sp & ~(THREAD_SIZE - 1)); - - if (regs) - fp = regs->r7; - else if (tsk == current) - asm("mov %0, r7" : "=r"(fp)); - else - fp = tsk->thread.cpu_context.r7; - - /* - * Walk the stack as long as the frame pointer (a) is within - * the kernel stack of the task, and (b) it doesn't move - * downwards. - */ - while (valid_stack_ptr(tinfo, fp)) { - unsigned long new_fp; - - lr = *(unsigned long *)fp; - printk(" [<%08lx>] ", lr); - print_symbol("%s\n", lr); - - new_fp = *(unsigned long *)(fp + 4); - if (new_fp <= fp) - break; - fp = new_fp; - } - printk("\n"); -} -#else -static inline void __show_trace(struct task_struct *tsk, unsigned long *sp, - struct pt_regs *regs) -{ - unsigned long addr; - - while (!kstack_end(sp)) { - addr = *sp++; - if (kernel_text_address(addr)) { - printk(" [<%08lx>] ", addr); - print_symbol("%s\n", addr); - } - } -} -#endif - -void show_trace(struct task_struct *tsk, unsigned long *sp, - struct pt_regs *regs) -{ - if (regs && - (((regs->sr & MODE_MASK) == MODE_EXCEPTION) || - ((regs->sr & MODE_MASK) == MODE_USER))) - return; - - printk ("Call trace:"); -#ifdef CONFIG_KALLSYMS - printk("\n"); -#endif - - __show_trace(tsk, sp, regs); - printk("\n"); -} - -void show_stack(struct task_struct *tsk, unsigned long *sp) -{ - unsigned long stack; - - if (!tsk) - tsk = current; - if (sp == 0) { - if (tsk == current) { - register unsigned long *real_sp __asm__("sp"); - sp = real_sp; - } else { - sp = (unsigned long *)tsk->thread.cpu_context.ksp; - } - } - - stack = (unsigned long)sp; - dump_mem("Stack: ", stack, - THREAD_SIZE + (unsigned long)tsk->thread_info); - show_trace(tsk, sp, NULL); -} - -void dump_stack(void) -{ - show_stack(NULL, NULL); -} -EXPORT_SYMBOL(dump_stack); +#include +#include +#include ATOMIC_NOTIFIER_HEAD(avr32_die_chain); int register_die_notifier(struct notifier_block *nb) { - pr_debug("register_die_notifier: %p\n", nb); - return atomic_notifier_chain_register(&avr32_die_chain, nb); } EXPORT_SYMBOL(register_die_notifier); @@ -169,93 +36,103 @@ EXPORT_SYMBOL(unregister_die_notifier); static DEFINE_SPINLOCK(die_lock); -void __die(const char *str, struct pt_regs *regs, unsigned long err, - const char *file, const char *func, unsigned long line) +void NORET_TYPE die(const char *str, struct pt_regs *regs, long err) { - struct task_struct *tsk = current; static int die_counter; console_verbose(); spin_lock_irq(&die_lock); bust_spinlocks(1); - printk(KERN_ALERT "%s", str); - if (file && func) - printk(" in %s:%s, line %ld", file, func, line); - printk("[#%d]:\n", ++die_counter); - print_modules(); - show_regs(regs); - printk("Process %s (pid: %d, stack limit = 0x%p)\n", - tsk->comm, tsk->pid, tsk->thread_info + 1); - - if (!user_mode(regs) || in_interrupt()) { - dump_mem("Stack: ", regs->sp, - THREAD_SIZE + (unsigned long)tsk->thread_info); + printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n" KERN_EMERG, + str, err, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); +#endif +#ifdef CONFIG_FRAME_POINTER + printk("FRAME_POINTER "); +#endif + if (current_cpu_data.features & AVR32_FEATURE_OCD) { + unsigned long did = __mfdr(DBGREG_DID); + printk("chip: 0x%03lx:0x%04lx rev %lu\n", + (did >> 1) & 0x7ff, + (did >> 12) & 0x7fff, + (did >> 28) & 0xf); + } else { + printk("cpu: arch %u r%u / core %u r%u\n", + current_cpu_data.arch_type, + current_cpu_data.arch_revision, + current_cpu_data.cpu_type, + current_cpu_data.cpu_revision); } + print_modules(); + show_regs_log_lvl(regs, KERN_EMERG); + show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG); bust_spinlocks(0); spin_unlock_irq(&die_lock); - do_exit(SIGSEGV); + + if (in_interrupt()) + panic("Fatal exception in interrupt"); + + if (panic_on_oops) + panic("Fatal exception"); + + do_exit(err); } -void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err, - const char *file, const char *func, unsigned long line) +void _exception(long signr, struct pt_regs *regs, int code, + unsigned long addr) { + siginfo_t info; + if (!user_mode(regs)) - __die(str, regs, err, file, func, line); -} + die("Unhandled exception in kernel mode", regs, signr); + + memset(&info, 0, sizeof(info)); + info.si_signo = signr; + info.si_code = code; + info.si_addr = (void __user *)addr; + force_sig_info(signr, &info, current); -asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs) -{ -#ifdef CONFIG_SUBARCH_AVR32B /* - * The exception entry always saves RSR_EX. For NMI, this is - * wrong; it should be RSR_NMI + * Init gets no signals that it doesn't have a handler for. + * That's all very well, but if it has caused a synchronous + * exception and we ignore the resulting signal, it will just + * generate the same exception over and over again and we get + * nowhere. Better to kill it and let the kernel panic. */ - regs->sr = sysreg_read(RSR_NMI); -#endif + if (is_init(current)) { + __sighandler_t handler; + + spin_lock_irq(¤t->sighand->siglock); + handler = current->sighand->action[signr-1].sa.sa_handler; + spin_unlock_irq(¤t->sighand->siglock); + if (handler == SIG_DFL) { + /* init has generated a synchronous exception + and it doesn't have a handler for the signal */ + printk(KERN_CRIT "init has generated signal %ld " + "but has no handler for it\n", signr); + do_exit(signr); + } + } +} - printk("NMI taken!!!!\n"); - die("NMI", regs, ecr); - BUG(); +asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs) +{ + printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n"); + show_regs_log_lvl(regs, KERN_ALERT); + show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT); } asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs) { - printk("Unable to handle critical exception %lu at pc = %08lx!\n", - ecr, regs->pc); - die("Oops", regs, ecr); - BUG(); + die("Critical exception", regs, SIGKILL); } asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs) { - siginfo_t info; - - die_if_kernel("Oops: Address exception in kernel mode", regs, ecr); - -#ifdef DEBUG - if (ecr == ECR_ADDR_ALIGN_X) - pr_debug("Instruction Address Exception at pc = %08lx\n", - regs->pc); - else if (ecr == ECR_ADDR_ALIGN_R) - pr_debug("Data Address Exception (Read) at pc = %08lx\n", - regs->pc); - else if (ecr == ECR_ADDR_ALIGN_W) - pr_debug("Data Address Exception (Write) at pc = %08lx\n", - regs->pc); - else - BUG(); - - show_regs(regs); -#endif - - info.si_signo = SIGBUS; - info.si_errno = 0; - info.si_code = BUS_ADRALN; - info.si_addr = (void __user *)regs->pc; - - force_sig_info(SIGBUS, &info, current); + _exception(SIGBUS, regs, BUS_ADRALN, regs->pc); } /* This way of handling undefined instructions is stolen from ARM */ @@ -280,7 +157,8 @@ static int do_cop_absent(u32 insn) { int cop_nr; u32 cpucr; - if ( (insn & 0xfdf00000) == 0xf1900000 ) + + if ((insn & 0xfdf00000) == 0xf1900000) /* LDC0 */ cop_nr = 0; else @@ -292,136 +170,91 @@ static int do_cop_absent(u32 insn) sysreg_write(CPUCR, cpucr); cpucr = sysreg_read(CPUCR); - if ( !(cpucr & (1 << (24 + cop_nr))) ){ - printk("Coprocessor #%i not found!\n", cop_nr); - return -1; - } + if (!(cpucr & (1 << (24 + cop_nr)))) + return -ENODEV; return 0; } -#ifdef CONFIG_BUG -#ifdef CONFIG_DEBUG_BUGVERBOSE -static inline void do_bug_verbose(struct pt_regs *regs, u32 insn) -{ - char *file; - u16 line; - char c; - - if (__get_user(line, (u16 __user *)(regs->pc + 2))) - return; - if (__get_user(file, (char * __user *)(regs->pc + 4)) - || (unsigned long)file < PAGE_OFFSET - || __get_user(c, file)) - file = ""; - - printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line); -} -#else -static inline void do_bug_verbose(struct pt_regs *regs, u32 insn) +int is_valid_bugaddr(unsigned long pc) { + unsigned short opcode; + + if (pc < PAGE_OFFSET) + return 0; + if (probe_kernel_address((u16 *)pc, opcode)) + return 0; + return opcode == AVR32_BUG_OPCODE; } -#endif -#endif asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs) { u32 insn; struct undef_hook *hook; - siginfo_t info; void __user *pc; + long code; - if (!user_mode(regs)) - goto kernel_trap; + if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) { + enum bug_trap_type type; + + type = report_bug(regs->pc); + switch (type) { + case BUG_TRAP_TYPE_NONE: + break; + case BUG_TRAP_TYPE_WARN: + regs->pc += 2; + return; + case BUG_TRAP_TYPE_BUG: + die("Kernel BUG", regs, SIGKILL); + } + } local_irq_enable(); - pc = (void __user *)instruction_pointer(regs); - if (__get_user(insn, (u32 __user *)pc)) - goto invalid_area; + if (user_mode(regs)) { + pc = (void __user *)instruction_pointer(regs); + if (get_user(insn, (u32 __user *)pc)) + goto invalid_area; - if (ecr == ECR_COPROC_ABSENT) { - if (do_cop_absent(insn) == 0) + if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn)) return; - } - spin_lock_irq(&undef_lock); - list_for_each_entry(hook, &undef_hook, node) { - if ((insn & hook->insn_mask) == hook->insn_val) { - if (hook->fn(regs, insn) == 0) { - spin_unlock_irq(&undef_lock); - return; + spin_lock_irq(&undef_lock); + list_for_each_entry(hook, &undef_hook, node) { + if ((insn & hook->insn_mask) == hook->insn_val) { + if (hook->fn(regs, insn) == 0) { + spin_unlock_irq(&undef_lock); + return; + } } } + spin_unlock_irq(&undef_lock); } - spin_unlock_irq(&undef_lock); - -invalid_area: -#ifdef DEBUG - printk("Illegal instruction at pc = %08lx\n", regs->pc); - if (regs->pc < TASK_SIZE) { - unsigned long ptbr, pgd, pte, *p; - - ptbr = sysreg_read(PTBR); - p = (unsigned long *)ptbr; - pgd = p[regs->pc >> 22]; - p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000); - pte = p[(regs->pc >> 12) & 0x3ff]; - printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte); - } -#endif - - info.si_signo = SIGILL; - info.si_errno = 0; - info.si_addr = (void __user *)regs->pc; switch (ecr) { - case ECR_ILLEGAL_OPCODE: - case ECR_UNIMPL_INSTRUCTION: - info.si_code = ILL_ILLOPC; - break; case ECR_PRIVILEGE_VIOLATION: - info.si_code = ILL_PRVOPC; + code = ILL_PRVOPC; break; case ECR_COPROC_ABSENT: - info.si_code = ILL_COPROC; + code = ILL_COPROC; break; default: - BUG(); + code = ILL_ILLOPC; + break; } - force_sig_info(SIGILL, &info, current); + _exception(SIGILL, regs, code, regs->pc); return; -kernel_trap: -#ifdef CONFIG_BUG - if (__kernel_text_address(instruction_pointer(regs))) { - insn = *(u16 *)instruction_pointer(regs); - if (insn == AVR32_BUG_OPCODE) { - do_bug_verbose(regs, insn); - die("Kernel BUG", regs, 0); - return; - } - } -#endif - - die("Oops: Illegal instruction in kernel code", regs, ecr); +invalid_area: + _exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc); } asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs) { - siginfo_t info; - - printk("Floating-point exception at pc = %08lx\n", regs->pc); - - /* We have no FPU... */ - info.si_signo = SIGILL; - info.si_errno = 0; - info.si_addr = (void __user *)regs->pc; - info.si_code = ILL_COPROC; - - force_sig_info(SIGILL, &info, current); + /* We have no FPU yet */ + _exception(SIGILL, regs, ILL_COPROC, regs->pc); } diff --git a/arch/avr32/kernel/vmlinux.lds.c b/arch/avr32/kernel/vmlinux.lds.c index ef13b7c..7ad20cf 100644 --- a/arch/avr32/kernel/vmlinux.lds.c +++ b/arch/avr32/kernel/vmlinux.lds.c @@ -26,6 +26,12 @@ SECTIONS _sinittext = .; *(.text.reset) *(.init.text) + /* + * .exit.text is discarded at runtime, not + * link time, to deal with references from + * __bug_table + */ + *(.exit.text) _einittext = .; . = ALIGN(4); __tagtable_begin = .; @@ -86,6 +92,8 @@ #endif __stop___ex_table = .; } + BUG_TABLE + RODATA . = ALIGN(8192); @@ -126,7 +134,6 @@ #endif * thrown away, as cleanup code is never called unless it's a module. */ /DISCARD/ : { - *(.exit.text) *(.exit.data) *(.exitcall.exit) } diff --git a/arch/avr32/mach-at32ap/Kconfig b/arch/avr32/mach-at32ap/Kconfig new file mode 100644 index 0000000..eb30783 --- /dev/null +++ b/arch/avr32/mach-at32ap/Kconfig @@ -0,0 +1,31 @@ +if PLATFORM_AT32AP + +menu "Atmel AVR32 AP options" + +choice + prompt "AT32AP7000 static memory bus width" + depends on CPU_AT32AP7000 + default AP7000_16_BIT_SMC + help + Define the width of the AP7000 external static memory interface. + This is used to determine how to mangle the address and/or data + when doing little-endian port access. + + The current code can only support a single external memory bus + width for all chip selects, excluding the flash (which is using + raw access and is thus not affected by any of this.) + +config AP7000_32_BIT_SMC + bool "32 bit" + +config AP7000_16_BIT_SMC + bool "16 bit" + +config AP7000_8_BIT_SMC + bool "8 bit" + +endchoice + +endmenu + +endif # PLATFORM_AT32AP diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index b21bea9..f1d3957 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile @@ -1,2 +1,3 @@ obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o +obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c index 472703f..56db45b 100644 --- a/arch/avr32/mach-at32ap/at32ap7000.c +++ b/arch/avr32/mach-at32ap/at32ap7000.c @@ -18,6 +18,7 @@ #include #include #include "clock.h" +#include "hmatrix.h" #include "pio.h" #include "sm.h" @@ -416,7 +417,15 @@ struct platform_device at32_sm_device = .resource = sm_resource, .num_resources = ARRAY_SIZE(sm_resource), }; -DEV_CLK(pclk, at32_sm, pbb, 0); +static struct clk at32_sm_pclk = { + .name = "pclk", + .dev = &at32_sm_device.dev, + .parent = &pbb_clk, + .mode = pbb_clk_mode, + .get_rate = pbb_clk_get_rate, + .users = 1, + .index = 0, +}; static struct resource intc0_resource[] = { PBMEM(0xfff00400), @@ -442,6 +451,7 @@ static struct clk hramc_clk = { .mode = hsb_clk_mode, .get_rate = hsb_clk_get_rate, .users = 1, + .index = 3, }; static struct resource smc0_resource[] = { @@ -467,6 +477,57 @@ static struct clk pico_clk = { }; /* -------------------------------------------------------------------- + * HMATRIX + * -------------------------------------------------------------------- */ + +static struct clk hmatrix_clk = { + .name = "hmatrix_clk", + .parent = &pbb_clk, + .mode = pbb_clk_mode, + .get_rate = pbb_clk_get_rate, + .index = 2, + .users = 1, +}; +#define HMATRIX_BASE ((void __iomem *)0xfff00800) + +#define hmatrix_readl(reg) \ + __raw_readl((HMATRIX_BASE) + HMATRIX_##reg) +#define hmatrix_writel(reg,value) \ + __raw_writel((value), (HMATRIX_BASE) + HMATRIX_##reg) + +/* + * Set bits in the HMATRIX Special Function Register (SFR) used by the + * External Bus Interface (EBI). This can be used to enable special + * features like CompactFlash support, NAND Flash support, etc. on + * certain chipselects. + */ +static inline void set_ebi_sfr_bits(u32 mask) +{ + u32 sfr; + + clk_enable(&hmatrix_clk); + sfr = hmatrix_readl(SFR4); + sfr |= mask; + hmatrix_writel(SFR4, sfr); + clk_disable(&hmatrix_clk); +} + +/* -------------------------------------------------------------------- + * System Timer/Counter (TC) + * -------------------------------------------------------------------- */ +static struct resource at32_systc0_resource[] = { + PBMEM(0xfff00c00), + IRQ(22), +}; +struct platform_device at32_systc0_device = { + .name = "systc", + .id = 0, + .resource = at32_systc0_resource, + .num_resources = ARRAY_SIZE(at32_systc0_resource), +}; +DEV_CLK(pclk, at32_systc0, pbb, 3); + +/* -------------------------------------------------------------------- * PIO * -------------------------------------------------------------------- */ @@ -514,6 +575,8 @@ void __init at32_add_system_devices(void platform_device_register(&smc0_device); platform_device_register(&pdc_device); + platform_device_register(&at32_systc0_device); + platform_device_register(&pio0_device); platform_device_register(&pio1_device); platform_device_register(&pio2_device); @@ -950,6 +1013,7 @@ struct clk *at32_clock_list[] = { &pbb_clk, &at32_sm_pclk, &at32_intc0_pclk, + &hmatrix_clk, &ebi_clk, &hramc_clk, &smc0_pclk, @@ -962,6 +1026,7 @@ struct clk *at32_clock_list[] = { &pio2_mck, &pio3_mck, &pio4_mck, + &at32_systc0_pclk, &atmel_usart0_usart, &atmel_usart1_usart, &atmel_usart2_usart, @@ -1024,6 +1089,9 @@ void __init at32_clock_init(void) for (i = 0; i < ARRAY_SIZE(at32_clock_list); i++) { struct clk *clk = at32_clock_list[i]; + if (clk->users == 0) + continue; + if (clk->mode == &cpu_clk_mode) cpu_mask |= 1 << clk->index; else if (clk->mode == &hsb_clk_mode) diff --git a/arch/avr32/mach-at32ap/hmatrix.h b/arch/avr32/mach-at32ap/hmatrix.h new file mode 100644 index 0000000..d10bfb6 --- /dev/null +++ b/arch/avr32/mach-at32ap/hmatrix.h @@ -0,0 +1,182 @@ +/* + * Register definitions for High-Speed Bus Matrix + */ +#ifndef __HMATRIX_H +#define __HMATRIX_H + +/* HMATRIX register offsets */ +#define HMATRIX_MCFG0 0x0000 +#define HMATRIX_MCFG1 0x0004 +#define HMATRIX_MCFG2 0x0008 +#define HMATRIX_MCFG3 0x000c +#define HMATRIX_MCFG4 0x0010 +#define HMATRIX_MCFG5 0x0014 +#define HMATRIX_MCFG6 0x0018 +#define HMATRIX_MCFG7 0x001c +#define HMATRIX_MCFG8 0x0020 +#define HMATRIX_MCFG9 0x0024 +#define HMATRIX_MCFG10 0x0028 +#define HMATRIX_MCFG11 0x002c +#define HMATRIX_MCFG12 0x0030 +#define HMATRIX_MCFG13 0x0034 +#define HMATRIX_MCFG14 0x0038 +#define HMATRIX_MCFG15 0x003c +#define HMATRIX_SCFG0 0x0040 +#define HMATRIX_SCFG1 0x0044 +#define HMATRIX_SCFG2 0x0048 +#define HMATRIX_SCFG3 0x004c +#define HMATRIX_SCFG4 0x0050 +#define HMATRIX_SCFG5 0x0054 +#define HMATRIX_SCFG6 0x0058 +#define HMATRIX_SCFG7 0x005c +#define HMATRIX_SCFG8 0x0060 +#define HMATRIX_SCFG9 0x0064 +#define HMATRIX_SCFG10 0x0068 +#define HMATRIX_SCFG11 0x006c +#define HMATRIX_SCFG12 0x0070 +#define HMATRIX_SCFG13 0x0074 +#define HMATRIX_SCFG14 0x0078 +#define HMATRIX_SCFG15 0x007c +#define HMATRIX_PRAS0 0x0080 +#define HMATRIX_PRBS0 0x0084 +#define HMATRIX_PRAS1 0x0088 +#define HMATRIX_PRBS1 0x008c +#define HMATRIX_PRAS2 0x0090 +#define HMATRIX_PRBS2 0x0094 +#define HMATRIX_PRAS3 0x0098 +#define HMATRIX_PRBS3 0x009c +#define HMATRIX_PRAS4 0x00a0 +#define HMATRIX_PRBS4 0x00a4 +#define HMATRIX_PRAS5 0x00a8 +#define HMATRIX_PRBS5 0x00ac +#define HMATRIX_PRAS6 0x00b0 +#define HMATRIX_PRBS6 0x00b4 +#define HMATRIX_PRAS7 0x00b8 +#define HMATRIX_PRBS7 0x00bc +#define HMATRIX_PRAS8 0x00c0 +#define HMATRIX_PRBS8 0x00c4 +#define HMATRIX_PRAS9 0x00c8 +#define HMATRIX_PRBS9 0x00cc +#define HMATRIX_PRAS10 0x00d0 +#define HMATRIX_PRBS10 0x00d4 +#define HMATRIX_PRAS11 0x00d8 +#define HMATRIX_PRBS11 0x00dc +#define HMATRIX_PRAS12 0x00e0 +#define HMATRIX_PRBS12 0x00e4 +#define HMATRIX_PRAS13 0x00e8 +#define HMATRIX_PRBS13 0x00ec +#define HMATRIX_PRAS14 0x00f0 +#define HMATRIX_PRBS14 0x00f4 +#define HMATRIX_PRAS15 0x00f8 +#define HMATRIX_PRBS15 0x00fc +#define HMATRIX_MRCR 0x0100 +#define HMATRIX_SFR0 0x0110 +#define HMATRIX_SFR1 0x0114 +#define HMATRIX_SFR2 0x0118 +#define HMATRIX_SFR3 0x011c +#define HMATRIX_SFR4 0x0120 +#define HMATRIX_SFR5 0x0124 +#define HMATRIX_SFR6 0x0128 +#define HMATRIX_SFR7 0x012c +#define HMATRIX_SFR8 0x0130 +#define HMATRIX_SFR9 0x0134 +#define HMATRIX_SFR10 0x0138 +#define HMATRIX_SFR11 0x013c +#define HMATRIX_SFR12 0x0140 +#define HMATRIX_SFR13 0x0144 +#define HMATRIX_SFR14 0x0148 +#define HMATRIX_SFR15 0x014c + +/* Bitfields in MCFGx */ +#define HMATRIX_ULBT_OFFSET 0 +#define HMATRIX_ULBT_SIZE 3 + +/* Bitfields in SCFGx */ +#define HMATRIX_SLOT_CYCLE_OFFSET 0 +#define HMATRIX_SLOT_CYCLE_SIZE 8 +#define HMATRIX_DEFMSTR_TYPE_OFFSET 16 +#define HMATRIX_DEFMSTR_TYPE_SIZE 2 +#define HMATRIX_FIXED_DEFMSTR_OFFSET 18 +#define HMATRIX_FIXED_DEFMSTR_SIZE 4 +#define HMATRIX_ARBT_OFFSET 24 +#define HMATRIX_ARBT_SIZE 2 + +/* Bitfields in PRASx */ +#define HMATRIX_M0PR_OFFSET 0 +#define HMATRIX_M0PR_SIZE 4 +#define HMATRIX_M1PR_OFFSET 4 +#define HMATRIX_M1PR_SIZE 4 +#define HMATRIX_M2PR_OFFSET 8 +#define HMATRIX_M2PR_SIZE 4 +#define HMATRIX_M3PR_OFFSET 12 +#define HMATRIX_M3PR_SIZE 4 +#define HMATRIX_M4PR_OFFSET 16 +#define HMATRIX_M4PR_SIZE 4 +#define HMATRIX_M5PR_OFFSET 20 +#define HMATRIX_M5PR_SIZE 4 +#define HMATRIX_M6PR_OFFSET 24 +#define HMATRIX_M6PR_SIZE 4 +#define HMATRIX_M7PR_OFFSET 28 +#define HMATRIX_M7PR_SIZE 4 + +/* Bitfields in PRBSx */ +#define HMATRIX_M8PR_OFFSET 0 +#define HMATRIX_M8PR_SIZE 4 +#define HMATRIX_M9PR_OFFSET 4 +#define HMATRIX_M9PR_SIZE 4 +#define HMATRIX_M10PR_OFFSET 8 +#define HMATRIX_M10PR_SIZE 4 +#define HMATRIX_M11PR_OFFSET 12 +#define HMATRIX_M11PR_SIZE 4 +#define HMATRIX_M12PR_OFFSET 16 +#define HMATRIX_M12PR_SIZE 4 +#define HMATRIX_M13PR_OFFSET 20 +#define HMATRIX_M13PR_SIZE 4 +#define HMATRIX_M14PR_OFFSET 24 +#define HMATRIX_M14PR_SIZE 4 +#define HMATRIX_M15PR_OFFSET 28 +#define HMATRIX_M15PR_SIZE 4 + +/* Bitfields in SFR4 */ +#define HMATRIX_CS1A_OFFSET 1 +#define HMATRIX_CS1A_SIZE 1 +#define HMATRIX_CS3A_OFFSET 3 +#define HMATRIX_CS3A_SIZE 1 +#define HMATRIX_CS4A_OFFSET 4 +#define HMATRIX_CS4A_SIZE 1 +#define HMATRIX_CS5A_OFFSET 5 +#define HMATRIX_CS5A_SIZE 1 +#define HMATRIX_DBPUC_OFFSET 8 +#define HMATRIX_DBPUC_SIZE 1 + +/* Constants for ULBT */ +#define HMATRIX_ULBT_INFINITE 0 +#define HMATRIX_ULBT_SINGLE 1 +#define HMATRIX_ULBT_FOUR_BEAT 2 +#define HMATRIX_ULBT_EIGHT_BEAT 3 +#define HMATRIX_ULBT_SIXTEEN_BEAT 4 + +/* Constants for DEFMSTR_TYPE */ +#define HMATRIX_DEFMSTR_TYPE_NO_DEFAULT 0 +#define HMATRIX_DEFMSTR_TYPE_LAST_DEFAULT 1 +#define HMATRIX_DEFMSTR_TYPE_FIXED_DEFAULT 2 + +/* Constants for ARBT */ +#define HMATRIX_ARBT_ROUND_ROBIN 0 +#define HMATRIX_ARBT_FIXED_PRIORITY 1 + +/* Bit manipulation macros */ +#define HMATRIX_BIT(name) \ + (1 << HMATRIX_##name##_OFFSET) +#define HMATRIX_BF(name,value) \ + (((value) & ((1 << HMATRIX_##name##_SIZE) - 1)) \ + << HMATRIX_##name##_OFFSET) +#define HMATRIX_BFEXT(name,value) \ + (((value) >> HMATRIX_##name##_OFFSET) \ + & ((1 << HMATRIX_##name##_SIZE) - 1)) +#define HMATRIX_BFINS(name,value,old) \ + (((old) & ~(((1 << HMATRIX_##name##_SIZE) - 1) \ + << HMATRIX_##name##_OFFSET)) \ + | HMATRIX_BF(name,value)) + +#endif /* __HMATRIX_H */ diff --git a/arch/avr32/mach-at32ap/hsmc.c b/arch/avr32/mach-at32ap/hsmc.c index 7691721..5e22a75 100644 --- a/arch/avr32/mach-at32ap/hsmc.c +++ b/arch/avr32/mach-at32ap/hsmc.c @@ -75,12 +75,35 @@ #define ns2cyc(x) ((((x) * mul) + 65535) return -EINVAL; } + switch (config->nwait_mode) { + case 0: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_DISABLED); + break; + case 1: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_RESERVED); + break; + case 2: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_FROZEN); + break; + case 3: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_READY); + break; + default: + return -EINVAL; + } + + if (config->tdf_cycles) { + mode |= HSMC_BF(TDF_CYCLES, config->tdf_cycles); + } + if (config->nrd_controlled) mode |= HSMC_BIT(READ_MODE); if (config->nwe_controlled) mode |= HSMC_BIT(WRITE_MODE); if (config->byte_write) mode |= HSMC_BIT(BAT); + if (config->tdf_mode) + mode |= HSMC_BIT(TDF_MODE); pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n", cs, setup, pulse, cycle, mode); diff --git a/arch/avr32/mach-at32ap/time-tc.c b/arch/avr32/mach-at32ap/time-tc.c new file mode 100644 index 0000000..e3070bd --- /dev/null +++ b/arch/avr32/mach-at32ap/time-tc.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2004-2007 Atmel Corporation + * + * Based on MIPS implementation arch/mips/kernel/time.c + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* how many counter cycles in a jiffy? */ +static u32 cycles_per_jiffy; + +/* the count value for the next timer interrupt */ +static u32 expirelo; + +/* the I/O registers of the TC module */ +static void __iomem *ioregs; + +cycle_t read_cycle_count(void) +{ + return (cycle_t)timer_read(ioregs, 0, CV); +} + +struct clocksource clocksource_avr32 = { + .name = "avr32", + .rating = 342, + .read = read_cycle_count, + .mask = CLOCKSOURCE_MASK(16), + .shift = 16, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void avr32_timer_ack(void) +{ + u16 count = expirelo; + + /* Ack this timer interrupt and set the next one, use a u16 + * variable so it will wrap around correctly */ + count += cycles_per_jiffy; + expirelo = count; + timer_write(ioregs, 0, RC, expirelo); + + /* Check to see if we have missed any timer interrupts */ + count = timer_read(ioregs, 0, CV); + if ((count - expirelo) < 0x7fff) { + expirelo = count + cycles_per_jiffy; + timer_write(ioregs, 0, RC, expirelo); + } +} + +u32 avr32_hpt_read(void) +{ + return timer_read(ioregs, 0, CV); +} + +static int avr32_timer_calc_div_and_set_jiffies(struct clk *pclk) +{ + unsigned int cycles_max = (clocksource_avr32.mask + 1) / 2; + unsigned int divs[] = { 4, 8, 16, 32 }; + int divs_size = sizeof(divs) / sizeof(*divs); + int i = 0; + unsigned long count_hz; + unsigned long shift; + unsigned long mult; + int clock_div = -1; + u64 tmp; + + shift = clocksource_avr32.shift; + + do { + count_hz = clk_get_rate(pclk) / divs[i]; + mult = clocksource_hz2mult(count_hz, shift); + clocksource_avr32.mult = mult; + + tmp = TICK_NSEC; + tmp <<= shift; + tmp += mult / 2; + do_div(tmp, mult); + + cycles_per_jiffy = tmp; + } while (cycles_per_jiffy > cycles_max && ++i < divs_size); + + clock_div = i + 1; + + if (clock_div > divs_size) { + pr_debug("timer: could not calculate clock divider\n"); + return -EFAULT; + } + + /* Set the clock divider */ + timer_write(ioregs, 0, CMR, TIMER_BF(CMR_TCCLKS, clock_div)); + + return 0; +} + +int avr32_hpt_init(unsigned int count) +{ + struct resource *regs; + struct clk *pclk; + int irq = -1; + int ret = 0; + + ret = -ENXIO; + + irq = platform_get_irq(&at32_systc0_device, 0); + if (irq < 0) { + pr_debug("timer: could not get irq\n"); + goto out_error; + } + + pclk = clk_get(&at32_systc0_device.dev, "pclk"); + if (IS_ERR(pclk)) { + pr_debug("timer: could not get clk: %ld\n", PTR_ERR(pclk)); + goto out_error; + } + clk_enable(pclk); + + regs = platform_get_resource(&at32_systc0_device, IORESOURCE_MEM, 0); + if (!regs) { + pr_debug("timer: could not get resource\n"); + goto out_error_clk; + } + + ioregs = ioremap(regs->start, regs->end - regs->start + 1); + if (!ioregs) { + pr_debug("timer: could not get ioregs\n"); + goto out_error_clk; + } + + ret = avr32_timer_calc_div_and_set_jiffies(pclk); + if (ret) + goto out_error_io; + + ret = setup_irq(irq, &timer_irqaction); + if (ret) { + pr_debug("timer: could not request irq %d: %d\n", + irq, ret); + goto out_error_io; + } + + expirelo = (timer_read(ioregs, 0, CV) / cycles_per_jiffy + 1) + * cycles_per_jiffy; + + /* Enable clock and interrupts on RC compare */ + timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_CLKEN)); + timer_write(ioregs, 0, IER, TIMER_BIT(IER_CPCS)); + /* Set cycles to first interrupt */ + timer_write(ioregs, 0, RC, expirelo); + + printk(KERN_INFO "timer: AT32AP system timer/counter at 0x%p irq %d\n", + ioregs, irq); + + return 0; + +out_error_io: + iounmap(ioregs); +out_error_clk: + clk_put(pclk); +out_error: + return ret; +} + +int avr32_hpt_start(void) +{ + timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_SWTRG)); + return 0; +} + +irqreturn_t timer_interrupt(int irq, void *dev_id) +{ + unsigned int sr = timer_read(ioregs, 0, SR); + + if (sr & TIMER_BIT(SR_CPCS)) { + /* ack timer interrupt and try to set next interrupt */ + avr32_timer_ack(); + + /* + * Call the generic timer interrupt handler + */ + write_seqlock(&xtime_lock); + do_timer(1); + write_sequnlock(&xtime_lock); + + /* + * In UP mode, we call local_timer_interrupt() to do profiling + * and process accounting. + * + * SMP is not supported yet. + */ + local_timer_interrupt(irq, dev_id); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} diff --git a/arch/avr32/mm/fault.c b/arch/avr32/mm/fault.c index 6785572..146ebdb 100644 --- a/arch/avr32/mm/fault.c +++ b/arch/avr32/mm/fault.c @@ -16,26 +16,8 @@ #include #include #include #include -#include #include - -#ifdef DEBUG -static void dump_code(unsigned long pc) -{ - char *p = (char *)pc; - char val; - int i; - - - printk(KERN_DEBUG "Code:"); - for (i = 0; i < 16; i++) { - if (__get_user(val, p + i)) - break; - printk(" %02x", val); - } - printk("\n"); -} -#endif +#include #ifdef CONFIG_KPROBES ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); @@ -68,17 +50,19 @@ static inline int notify_page_fault(enum } #endif +int exception_trace = 1; + /* * This routine handles page faults. It determines the address and the * problem, and then passes it off to one of the appropriate routines. * * ecr is the Exception Cause Register. Possible values are: - * 5: Page not found (instruction access) * 6: Protection fault (instruction access) - * 12: Page not found (read access) - * 13: Page not found (write access) - * 14: Protection fault (read access) - * 15: Protection fault (write access) + * 15: Protection fault (read access) + * 16: Protection fault (write access) + * 20: Page not found (instruction access) + * 24: Page not found (read access) + * 28: Page not found (write access) */ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs) { @@ -88,7 +72,9 @@ asmlinkage void do_page_fault(unsigned l const struct exception_table_entry *fixup; unsigned long address; unsigned long page; - int writeaccess = 0; + int writeaccess; + long signr; + int code; if (notify_page_fault(DIE_PAGE_FAULT, regs, ecr, SIGSEGV) == NOTIFY_STOP) @@ -99,6 +85,9 @@ asmlinkage void do_page_fault(unsigned l tsk = current; mm = tsk->mm; + signr = SIGSEGV; + code = SEGV_MAPERR; + /* * If we're in an interrupt or have no user context, we must * not take the fault... @@ -125,7 +114,9 @@ asmlinkage void do_page_fault(unsigned l * can handle it... */ good_area: - //pr_debug("good area: vm_flags = 0x%lx\n", vma->vm_flags); + code = SEGV_ACCERR; + writeaccess = 0; + switch (ecr) { case ECR_PROTECTION_X: case ECR_TLB_MISS_X: @@ -176,46 +167,24 @@ survive: * map. Fix it, but check if it's kernel or user first... */ bad_area: - pr_debug("Bad area [%s:%u]: addr %08lx, ecr %lu\n", - tsk->comm, tsk->pid, address, ecr); - up_read(&mm->mmap_sem); if (user_mode(regs)) { - /* Hmm...we have to pass address and ecr somehow... */ - /* tsk->thread.address = address; - tsk->thread.error_code = ecr; */ -#ifdef DEBUG - show_regs(regs); - dump_code(regs->pc); - - page = sysreg_read(PTBR); - printk("ptbr = %08lx", page); - if (page) { - page = ((unsigned long *)page)[address >> 22]; - printk(" pgd = %08lx", page); - if (page & _PAGE_PRESENT) { - page &= PAGE_MASK; - address &= 0x003ff000; - page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT]; - printk(" pte = %08lx\n", page); - } - } -#endif - pr_debug("Sending SIGSEGV to PID %d...\n", - tsk->pid); - force_sig(SIGSEGV, tsk); + if (exception_trace) + printk("%s%s[%d]: segfault at %08lx pc %08lx " + "sp %08lx ecr %lu\n", + is_init(tsk) ? KERN_EMERG : KERN_INFO, + tsk->comm, tsk->pid, address, regs->pc, + regs->sp, ecr); + _exception(SIGSEGV, regs, code, address); return; } no_context: - pr_debug("No context\n"); - /* Are we prepared to handle this kernel fault? */ fixup = search_exception_tables(regs->pc); if (fixup) { regs->pc = fixup->fixup; - pr_debug("Found fixup at %08lx\n", fixup->fixup); return; } @@ -230,7 +199,6 @@ no_context: printk(KERN_ALERT "Unable to handle kernel paging request"); printk(" at virtual address %08lx\n", address); - printk(KERN_ALERT "pc = %08lx\n", regs->pc); page = sysreg_read(PTBR); printk(KERN_ALERT "ptbr = %08lx", page); @@ -241,20 +209,20 @@ no_context: page &= PAGE_MASK; address &= 0x003ff000; page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT]; - printk(" pte = %08lx\n", page); + printk(" pte = %08lx", page); } } - die("\nOops", regs, ecr); - do_exit(SIGKILL); + printk("\n"); + die("Kernel access of bad area", regs, signr); + return; /* * We ran out of memory, or some other thing happened to us * that made us unable to handle the page fault gracefully. */ out_of_memory: - printk("Out of memory\n"); up_read(&mm->mmap_sem); - if (current->pid == 1) { + if (is_init(current)) { yield(); down_read(&mm->mmap_sem); goto survive; @@ -267,21 +235,20 @@ out_of_memory: do_sigbus: up_read(&mm->mmap_sem); - /* - * Send a sigbus, regardless of whether we were in kernel or - * user mode. - */ - /* address, error_code, trap_no, ... */ -#ifdef DEBUG - show_regs(regs); - dump_code(regs->pc); -#endif - pr_debug("Sending SIGBUS to PID %d...\n", tsk->pid); - force_sig(SIGBUS, tsk); - /* Kernel mode? Handle exceptions or die */ + signr = SIGBUS; + code = BUS_ADRERR; if (!user_mode(regs)) goto no_context; + + if (exception_trace) + printk("%s%s[%d]: bus error at %08lx pc %08lx " + "sp %08lx ecr %lu\n", + is_init(tsk) ? KERN_EMERG : KERN_INFO, + tsk->comm, tsk->pid, address, regs->pc, + regs->sp, ecr); + + _exception(SIGBUS, regs, BUS_ADRERR, address); } asmlinkage void do_bus_error(unsigned long addr, int write_access, @@ -292,8 +259,7 @@ asmlinkage void do_bus_error(unsigned lo addr, write_access ? "write" : "read"); printk(KERN_INFO "DTLB dump:\n"); dump_dtlb(); - die("Bus Error", regs, write_access); - do_exit(SIGKILL); + die("Bus Error", regs, SIGKILL); } /* diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 70da689..82cf708 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -10,11 +10,9 @@ #include #include #include #include -#include #include #include #include -#include #include #include @@ -78,242 +76,6 @@ void show_mem(void) printk ("%d pages swap cached\n", cached); } -static void __init print_memory_map(const char *what, - struct tag_mem_range *mem) -{ - printk ("%s:\n", what); - for (; mem; mem = mem->next) { - printk (" %08lx - %08lx\n", - (unsigned long)mem->addr, - (unsigned long)(mem->addr + mem->size)); - } -} - -#define MAX_LOWMEM HIGHMEM_START -#define MAX_LOWMEM_PFN PFN_DOWN(MAX_LOWMEM) - -/* - * Sort a list of memory regions in-place by ascending address. - * - * We're using bubble sort because we only have singly linked lists - * with few elements. - */ -static void __init sort_mem_list(struct tag_mem_range **pmem) -{ - int done; - struct tag_mem_range **a, **b; - - if (!*pmem) - return; - - do { - done = 1; - a = pmem, b = &(*pmem)->next; - while (*b) { - if ((*a)->addr > (*b)->addr) { - struct tag_mem_range *tmp; - tmp = (*b)->next; - (*b)->next = *a; - *a = *b; - *b = tmp; - done = 0; - } - a = &(*a)->next; - b = &(*a)->next; - } - } while (!done); -} - -/* - * Find a free memory region large enough for storing the - * bootmem bitmap. - */ -static unsigned long __init -find_bootmap_pfn(const struct tag_mem_range *mem) -{ - unsigned long bootmap_pages, bootmap_len; - unsigned long node_pages = PFN_UP(mem->size); - unsigned long bootmap_addr = mem->addr; - struct tag_mem_range *reserved = mem_reserved; - struct tag_mem_range *ramdisk = mem_ramdisk; - unsigned long kern_start = virt_to_phys(_stext); - unsigned long kern_end = virt_to_phys(_end); - - bootmap_pages = bootmem_bootmap_pages(node_pages); - bootmap_len = bootmap_pages << PAGE_SHIFT; - - /* - * Find a large enough region without reserved pages for - * storing the bootmem bitmap. We can take advantage of the - * fact that all lists have been sorted. - * - * We have to check explicitly reserved regions as well as the - * kernel image and any RAMDISK images... - * - * Oh, and we have to make sure we don't overwrite the taglist - * since we're going to use it until the bootmem allocator is - * fully up and running. - */ - while (1) { - if ((bootmap_addr < kern_end) && - ((bootmap_addr + bootmap_len) > kern_start)) - bootmap_addr = kern_end; - - while (reserved && - (bootmap_addr >= (reserved->addr + reserved->size))) - reserved = reserved->next; - - if (reserved && - ((bootmap_addr + bootmap_len) >= reserved->addr)) { - bootmap_addr = reserved->addr + reserved->size; - continue; - } - - while (ramdisk && - (bootmap_addr >= (ramdisk->addr + ramdisk->size))) - ramdisk = ramdisk->next; - - if (!ramdisk || - ((bootmap_addr + bootmap_len) < ramdisk->addr)) - break; - - bootmap_addr = ramdisk->addr + ramdisk->size; - } - - if ((PFN_UP(bootmap_addr) + bootmap_len) >= (mem->addr + mem->size)) - return ~0UL; - - return PFN_UP(bootmap_addr); -} - -void __init setup_bootmem(void) -{ - unsigned bootmap_size; - unsigned long first_pfn, bootmap_pfn, pages; - unsigned long max_pfn, max_low_pfn; - unsigned long kern_start = virt_to_phys(_stext); - unsigned long kern_end = virt_to_phys(_end); - unsigned node = 0; - struct tag_mem_range *bank, *res; - - sort_mem_list(&mem_phys); - sort_mem_list(&mem_reserved); - - print_memory_map("Physical memory", mem_phys); - print_memory_map("Reserved memory", mem_reserved); - - nodes_clear(node_online_map); - - if (mem_ramdisk) { -#ifdef CONFIG_BLK_DEV_INITRD - initrd_start = (unsigned long)__va(mem_ramdisk->addr); - initrd_end = initrd_start + mem_ramdisk->size; - - print_memory_map("RAMDISK images", mem_ramdisk); - if (mem_ramdisk->next) - printk(KERN_WARNING - "Warning: Only the first RAMDISK image " - "will be used\n"); - sort_mem_list(&mem_ramdisk); -#else - printk(KERN_WARNING "RAM disk image present, but " - "no initrd support in kernel!\n"); -#endif - } - - if (mem_phys->next) - printk(KERN_WARNING "Only using first memory bank\n"); - - for (bank = mem_phys; bank; bank = NULL) { - first_pfn = PFN_UP(bank->addr); - max_low_pfn = max_pfn = PFN_DOWN(bank->addr + bank->size); - bootmap_pfn = find_bootmap_pfn(bank); - if (bootmap_pfn > max_pfn) - panic("No space for bootmem bitmap!\n"); - - if (max_low_pfn > MAX_LOWMEM_PFN) { - max_low_pfn = MAX_LOWMEM_PFN; -#ifndef CONFIG_HIGHMEM - /* - * Lowmem is memory that can be addressed - * directly through P1/P2 - */ - printk(KERN_WARNING - "Node %u: Only %ld MiB of memory will be used.\n", - node, MAX_LOWMEM >> 20); - printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); -#else -#error HIGHMEM is not supported by AVR32 yet -#endif - } - - /* Initialize the boot-time allocator with low memory only. */ - bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn, - first_pfn, max_low_pfn); - - printk("Node %u: bdata = %p, bdata->node_bootmem_map = %p\n", - node, NODE_DATA(node)->bdata, - NODE_DATA(node)->bdata->node_bootmem_map); - - /* - * Register fully available RAM pages with the bootmem - * allocator. - */ - pages = max_low_pfn - first_pfn; - free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn), - PFN_PHYS(pages)); - - /* - * Reserve space for the kernel image (if present in - * this node)... - */ - if ((kern_start >= PFN_PHYS(first_pfn)) && - (kern_start < PFN_PHYS(max_pfn))) { - printk("Node %u: Kernel image %08lx - %08lx\n", - node, kern_start, kern_end); - reserve_bootmem_node(NODE_DATA(node), kern_start, - kern_end - kern_start); - } - - /* ...the bootmem bitmap... */ - reserve_bootmem_node(NODE_DATA(node), - PFN_PHYS(bootmap_pfn), - bootmap_size); - - /* ...any RAMDISK images... */ - for (res = mem_ramdisk; res; res = res->next) { - if (res->addr > PFN_PHYS(max_pfn)) - break; - - if (res->addr >= PFN_PHYS(first_pfn)) { - printk("Node %u: RAMDISK %08lx - %08lx\n", - node, - (unsigned long)res->addr, - (unsigned long)(res->addr + res->size)); - reserve_bootmem_node(NODE_DATA(node), - res->addr, res->size); - } - } - - /* ...and any other reserved regions. */ - for (res = mem_reserved; res; res = res->next) { - if (res->addr > PFN_PHYS(max_pfn)) - break; - - if (res->addr >= PFN_PHYS(first_pfn)) { - printk("Node %u: Reserved %08lx - %08lx\n", - node, - (unsigned long)res->addr, - (unsigned long)(res->addr + res->size)); - reserve_bootmem_node(NODE_DATA(node), - res->addr, res->size); - } - } - - node_set_online(node); - } -} - /* * paging_init() sets up the page tables * diff --git a/arch/i386/defconfig b/arch/i386/defconfig index f4efd66..c96911c 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -692,7 +692,6 @@ # CONFIG_SATA_ULI is not set CONFIG_SATA_VIA=y # CONFIG_SATA_VITESSE is not set # CONFIG_SATA_INIC162X is not set -CONFIG_SATA_INTEL_COMBINED=y CONFIG_SATA_ACPI=y # CONFIG_PATA_ALI is not set # CONFIG_PATA_AMD is not set diff --git a/arch/i386/kernel/acpi/earlyquirk.c b/arch/i386/kernel/acpi/earlyquirk.c index a7d22d9..8f7efd3 100644 --- a/arch/i386/kernel/acpi/earlyquirk.c +++ b/arch/i386/kernel/acpi/earlyquirk.c @@ -23,10 +23,13 @@ #endif static int __init check_bridge(int vendor, int device) { #ifdef CONFIG_ACPI + static int warned; /* According to Nvidia all timer overrides are bogus unless HPET is enabled. */ if (!acpi_use_timer_override && vendor == PCI_VENDOR_ID_NVIDIA) { - if (acpi_table_parse(ACPI_SIG_HPET, nvidia_hpet_check)) { + if (!warned && acpi_table_parse(ACPI_SIG_HPET, + nvidia_hpet_check)) { + warned = 1; acpi_skip_timer_override = 1; printk(KERN_INFO "Nvidia board " "detected. Ignoring ACPI " diff --git a/arch/ia64/hp/sim/simeth.c b/arch/ia64/hp/sim/simeth.c index 424e925..f26077a 100644 --- a/arch/ia64/hp/sim/simeth.c +++ b/arch/ia64/hp/sim/simeth.c @@ -427,7 +427,6 @@ make_new_skb(struct net_device *dev) printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name); return NULL; } - nskb->dev = dev; skb_reserve(nskb, 2); /* Align IP on 16 byte boundaries */ @@ -474,7 +473,7 @@ #if 0 * XXX Fix me * Should really do a csum+copy here */ - memcpy(skb->data, frame, len); + skb_copy_to_linear_data(skb, frame, len); #endif skb->protocol = eth_type_trans(skb, dev); diff --git a/arch/ia64/lib/csum_partial_copy.c b/arch/ia64/lib/csum_partial_copy.c index 503dfe6..118daf5 100644 --- a/arch/ia64/lib/csum_partial_copy.c +++ b/arch/ia64/lib/csum_partial_copy.c @@ -128,6 +128,8 @@ csum_partial_copy_from_user(const void _ return (__force __wsum)result; } +EXPORT_SYMBOL(csum_partial_copy_from_user); + __wsum csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum) { diff --git a/arch/ia64/sn/kernel/xpnet.c b/arch/ia64/sn/kernel/xpnet.c index c8173db..5419acb 100644 --- a/arch/ia64/sn/kernel/xpnet.c +++ b/arch/ia64/sn/kernel/xpnet.c @@ -233,7 +233,7 @@ xpnet_receive(partid_t partid, int chann "%lu)\n", skb->data, &msg->data, (size_t) msg->embedded_bytes); - memcpy(skb->data, &msg->data, (size_t) msg->embedded_bytes); + skb_copy_to_linear_data(skb, &msg->data, (size_t)msg->embedded_bytes); } else { dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t" "bte_copy(0x%p, 0x%p, %hu)\n", (void *)msg->buf_pa, @@ -264,17 +264,16 @@ xpnet_receive(partid_t partid, int chann dev_dbg(xpnet, "head=0x%p skb->data=0x%p skb->tail=0x%p " "skb->end=0x%p skb->len=%d\n", (void *) skb->head, - (void *) skb->data, (void *) skb->tail, (void *) skb->end, + (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), skb->len); - skb->dev = xpnet_device; skb->protocol = eth_type_trans(skb, xpnet_device); skb->ip_summed = CHECKSUM_UNNECESSARY; dev_dbg(xpnet, "passing skb to network layer; \n\tskb->head=0x%p " "skb->data=0x%p skb->tail=0x%p skb->end=0x%p skb->len=%d\n", - (void *) skb->head, (void *) skb->data, (void *) skb->tail, - (void *) skb->end, skb->len); + (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb), skb->len); xpnet_device->last_rx = jiffies; @@ -476,7 +475,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p " "skb->end=0x%p skb->len=%d\n", (void *) skb->head, - (void *) skb->data, (void *) skb->tail, (void *) skb->end, + (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), skb->len); @@ -498,7 +497,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff /* get the beginning of the first cacheline and end of last */ start_addr = ((u64) skb->data & ~(L1_CACHE_BYTES - 1)); - end_addr = L1_CACHE_ALIGN((u64) skb->tail); + end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb)); /* calculate how many bytes to embed in the XPC message */ embedded_bytes = 0; @@ -567,14 +566,15 @@ xpnet_dev_hard_start_xmit(struct sk_buff msg->version = XPNET_VERSION_EMBED; dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n", &msg->data, skb->data, (size_t) embedded_bytes); - memcpy(&msg->data, skb->data, (size_t) embedded_bytes); + skb_copy_from_linear_data(skb, &msg->data, + (size_t)embedded_bytes); } else { msg->version = XPNET_VERSION; } msg->magic = XPNET_MAGIC; msg->size = end_addr - start_addr; msg->leadin_ignore = (u64) skb->data - start_addr; - msg->tailout_ignore = end_addr - (u64) skb->tail; + msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb); msg->buf_pa = __pa(start_addr); dev_dbg(xpnet, "sending XPC message to %d:%d\nmsg->buf_pa=" diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index c78b143..130d825 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -10,7 +10,6 @@ menu "Machine selection" config ZONE_DMA bool - default y choice prompt "System type" @@ -165,7 +164,7 @@ config MIPS_COBALT select HW_HAS_PCI select I8259 select IRQ_CPU - select MIPS_GT64111 + select PCI_GT64XXX_PCI0 select SYS_HAS_CPU_NEVADA select SYS_HAS_EARLY_PRINTK select SYS_SUPPORTS_32BIT_KERNEL @@ -207,7 +206,7 @@ config MIPS_EV64120 depends on EXPERIMENTAL select DMA_NONCOHERENT select HW_HAS_PCI - select MIPS_GT64120 + select PCI_GT64XXX_PCI0 select SYS_HAS_CPU_R5000 select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_64BIT_KERNEL @@ -245,7 +244,7 @@ config LASAT select DMA_NONCOHERENT select SYS_HAS_EARLY_PRINTK select HW_HAS_PCI - select MIPS_GT64120 + select PCI_GT64XXX_PCI0 select MIPS_NILE4 select R5000_CPU_SCACHE select SYS_HAS_CPU_R5000 @@ -263,7 +262,7 @@ config MIPS_ATLAS select HW_HAS_PCI select MIPS_BOARDS_GEN select MIPS_BONITO64 - select MIPS_GT64120 + select PCI_GT64XXX_PCI0 select MIPS_MSC select RM7000_CPU_SCACHE select SWAP_IO_SPACE @@ -296,7 +295,7 @@ config MIPS_MALTA select MIPS_BOARDS_GEN select MIPS_BONITO64 select MIPS_CPU_SCACHE - select MIPS_GT64120 + select PCI_GT64XXX_PCI0 select MIPS_MSC select SWAP_IO_SPACE select SYS_HAS_CPU_MIPS32_R1 @@ -340,7 +339,7 @@ config WR_PPMC select BOOT_ELF32 select DMA_NONCOHERENT select HW_HAS_PCI - select MIPS_GT64120 + select PCI_GT64XXX_PCI0 select SWAP_IO_SPACE select SYS_HAS_CPU_MIPS32_R1 select SYS_HAS_CPU_MIPS32_R2 @@ -398,7 +397,7 @@ config MOMENCO_OCELOT select HW_HAS_PCI select IRQ_CPU select IRQ_CPU_RM7K - select MIPS_GT64120 + select PCI_GT64XXX_PCI0 select RM7000_CPU_SCACHE select SWAP_IO_SPACE select SYS_HAS_CPU_RM7000 @@ -501,10 +500,8 @@ config DDB5477 ether port USB, AC97, PCI, etc. config MACH_VR41XX - bool "NEC VR41XX-based machines" + bool "NEC VR4100 series based machines" select SYS_HAS_CPU_VR41XX - select SYS_SUPPORTS_32BIT_KERNEL - select SYS_SUPPORTS_64BIT_KERNEL if EXPERIMENTAL select GENERIC_HARDIRQS_NO__DO_IRQ config PMC_YOSEMITE @@ -779,6 +776,7 @@ config TOSHIBA_JMR3927 select SYS_SUPPORTS_LITTLE_ENDIAN select SYS_SUPPORTS_BIG_ENDIAN select TOSHIBA_BOARDS + select GENERIC_HARDIRQS_NO__DO_IRQ config TOSHIBA_RBTX4927 bool "Toshiba TBTX49[23]7 board" @@ -922,6 +920,7 @@ config SYS_HAS_EARLY_PRINTK config GENERIC_ISA_DMA bool + select ZONE_DMA config I8259 bool @@ -945,6 +944,7 @@ config MIPS_DISABLE_OBSOLETE_IDE config GENERIC_ISA_DMA_SUPPORT_BROKEN bool + select ZONE_DMA # # Endianess selection. Sufficiently obscure so many users don't know what to @@ -999,10 +999,7 @@ config DDB5XXX_COMMON config MIPS_BOARDS_GEN bool -config MIPS_GT64111 - bool - -config MIPS_GT64120 +config PCI_GT64XXX_PCI0 bool config MIPS_TX3927 diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 92bca6a..f2f742d 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -530,25 +530,29 @@ cflags-$(CONFIG_SGI_IP32) += -Iinclude/a load-$(CONFIG_SGI_IP32) += 0xffffffff80004000 # -# Sibyte SB1250 SOC +# Sibyte SB1250/BCM1480 SOC # # This is a LIB so that it links at the end, and initcalls are later # the sequence; but it is built as an object so that modules don't get # removed (as happens, even if they have __initcall/module_init) # core-$(CONFIG_SIBYTE_BCM112X) += arch/mips/sibyte/sb1250/ +core-$(CONFIG_SIBYTE_BCM112X) += arch/mips/sibyte/common/ cflags-$(CONFIG_SIBYTE_BCM112X) += -Iinclude/asm-mips/mach-sibyte \ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL core-$(CONFIG_SIBYTE_SB1250) += arch/mips/sibyte/sb1250/ +core-$(CONFIG_SIBYTE_SB1250) += arch/mips/sibyte/common/ cflags-$(CONFIG_SIBYTE_SB1250) += -Iinclude/asm-mips/mach-sibyte \ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL core-$(CONFIG_SIBYTE_BCM1x55) += arch/mips/sibyte/bcm1480/ +core-$(CONFIG_SIBYTE_BCM1x55) += arch/mips/sibyte/common/ cflags-$(CONFIG_SIBYTE_BCM1x55) += -Iinclude/asm-mips/mach-sibyte \ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL core-$(CONFIG_SIBYTE_BCM1x80) += arch/mips/sibyte/bcm1480/ +core-$(CONFIG_SIBYTE_BCM1x80) += arch/mips/sibyte/common/ cflags-$(CONFIG_SIBYTE_BCM1x80) += -Iinclude/asm-mips/mach-sibyte \ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL diff --git a/arch/mips/basler/excite/excite_setup.c b/arch/mips/basler/excite/excite_setup.c index 42f0eda..2f0e4c0 100644 --- a/arch/mips/basler/excite/excite_setup.c +++ b/arch/mips/basler/excite/excite_setup.c @@ -63,7 +63,7 @@ volatile void __iomem * const ocd_base = volatile void __iomem * const titan_base = (void *) (EXCITE_ADDR_TITAN); /* Protect access to shared GPI registers */ -spinlock_t titan_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(titan_lock); int titan_irqflags; diff --git a/arch/mips/cobalt/Makefile b/arch/mips/cobalt/Makefile index b36dd8f..de017c1 100644 --- a/arch/mips/cobalt/Makefile +++ b/arch/mips/cobalt/Makefile @@ -4,5 +4,6 @@ # obj-y := irq.o reset.o setup.o +obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_EARLY_PRINTK) += console.o obj-$(CONFIG_MTD_PHYSMAP) += mtd.o diff --git a/arch/mips/cobalt/console.c b/arch/mips/cobalt/console.c index ca56b41..0485d51 100644 --- a/arch/mips/cobalt/console.c +++ b/arch/mips/cobalt/console.c @@ -1,13 +1,11 @@ /* * (C) P. Horton 2006 */ - -#include -#include -#include #include + #include -#include + +#include void prom_putchar(char c) { diff --git a/arch/mips/cobalt/irq.c b/arch/mips/cobalt/irq.c index fe93b84..950ad1e 100644 --- a/arch/mips/cobalt/irq.c +++ b/arch/mips/cobalt/irq.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include /* * We have two types of interrupts that we handle, ones that come in through diff --git a/arch/mips/cobalt/pci.c b/arch/mips/cobalt/pci.c new file mode 100644 index 0000000..d91027f --- /dev/null +++ b/arch/mips/cobalt/pci.c @@ -0,0 +1,47 @@ +/* + * Register PCI controller. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 1997, 2004, 05 by Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2001, 2002, 2003 by Liam Davies (ldavies@agile.tv) + * + */ +#include +#include + +#include + +extern struct pci_ops gt64xxx_pci0_ops; + +static struct resource cobalt_mem_resource = { + .start = GT_DEF_PCI0_MEM0_BASE, + .end = GT_DEF_PCI0_MEM0_BASE + GT_DEF_PCI0_MEM0_SIZE - 1, + .name = "PCI memory", + .flags = IORESOURCE_MEM, +}; + +static struct resource cobalt_io_resource = { + .start = 0x1000, + .end = GT_DEF_PCI0_IO_SIZE - 1, + .name = "PCI I/O", + .flags = IORESOURCE_IO, +}; + +static struct pci_controller cobalt_pci_controller = { + .pci_ops = >64xxx_pci0_ops, + .mem_resource = &cobalt_mem_resource, + .io_resource = &cobalt_io_resource, + .io_offset = 0 - GT_DEF_PCI0_IO_BASE, +}; + +static int __init cobalt_pci_init(void) +{ + register_pci_controller(&cobalt_pci_controller); + + return 0; +} + +arch_initcall(cobalt_pci_init); diff --git a/arch/mips/cobalt/reset.c b/arch/mips/cobalt/reset.c index 753dfcc..43cca21 100644 --- a/arch/mips/cobalt/reset.c +++ b/arch/mips/cobalt/reset.c @@ -8,15 +8,12 @@ * Copyright (C) 1995, 1996, 1997 by Ralf Baechle * Copyright (C) 2001 by Liam Davies (ldavies@agile.tv) */ -#include -#include -#include +#include + #include -#include #include -#include -#include -#include + +#include void cobalt_machine_halt(void) { diff --git a/arch/mips/cobalt/setup.c b/arch/mips/cobalt/setup.c index 88d34f1..d0dd817 100644 --- a/arch/mips/cobalt/setup.c +++ b/arch/mips/cobalt/setup.c @@ -19,12 +19,10 @@ #include #include #include #include -#include -#include #include #include -#include +#include extern void cobalt_machine_restart(char *command); extern void cobalt_machine_halt(void); @@ -63,22 +61,6 @@ void __init plat_timer_setup(struct irqa GT_WRITE(GT_INTRMASK_OFS, GT_INTR_T0EXP_MSK | GT_READ(GT_INTRMASK_OFS)); } -extern struct pci_ops gt64111_pci_ops; - -static struct resource cobalt_mem_resource = { - .start = GT_DEF_PCI0_MEM0_BASE, - .end = GT_DEF_PCI0_MEM0_BASE + GT_DEF_PCI0_MEM0_SIZE - 1, - .name = "PCI memory", - .flags = IORESOURCE_MEM -}; - -static struct resource cobalt_io_resource = { - .start = 0x1000, - .end = 0xffff, - .name = "PCI I/O", - .flags = IORESOURCE_IO -}; - /* * Cobalt doesn't have PS/2 keyboard/mouse interfaces, * keyboard conntroller is never used. @@ -111,14 +93,6 @@ static struct resource cobalt_reserved_r }, }; -static struct pci_controller cobalt_pci_controller = { - .pci_ops = >64111_pci_ops, - .mem_resource = &cobalt_mem_resource, - .mem_offset = 0, - .io_resource = &cobalt_io_resource, - .io_offset = 0 - GT_DEF_PCI0_IO_BASE, -}; - void __init plat_mem_setup(void) { static struct uart_port uart; @@ -146,10 +120,6 @@ void __init plat_mem_setup(void) printk("Cobalt board ID: %d\n", cobalt_board_id); -#ifdef CONFIG_PCI - register_pci_controller(&cobalt_pci_controller); -#endif - if (cobalt_board_id > COBALT_BRD_ID_RAQ1) { #ifdef CONFIG_SERIAL_8250 uart.line = 0; diff --git a/arch/mips/configs/jmr3927_defconfig b/arch/mips/configs/jmr3927_defconfig index 21a0947..068e48e 100644 --- a/arch/mips/configs/jmr3927_defconfig +++ b/arch/mips/configs/jmr3927_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.20 -# Tue Feb 20 21:47:34 2007 +# Linux kernel version: 2.6.21-rc3 +# Thu Mar 15 00:40:40 2007 # CONFIG_MIPS=y @@ -70,7 +70,7 @@ CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_GENERIC_TIME=y CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y -# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y CONFIG_DMA_NONCOHERENT=y CONFIG_DMA_NEED_PCI_MAP_STATE=y CONFIG_CPU_BIG_ENDIAN=y @@ -138,12 +138,12 @@ CONFIG_ZONE_DMA_FLAG=1 # CONFIG_HZ_48 is not set # CONFIG_HZ_100 is not set # CONFIG_HZ_128 is not set -# CONFIG_HZ_250 is not set +CONFIG_HZ_250=y # CONFIG_HZ_256 is not set -CONFIG_HZ_1000=y +# CONFIG_HZ_1000 is not set # CONFIG_HZ_1024 is not set CONFIG_SYS_SUPPORTS_ARBIT_HZ=y -CONFIG_HZ=1000 +CONFIG_HZ=250 CONFIG_PREEMPT_NONE=y # CONFIG_PREEMPT_VOLUNTARY is not set # CONFIG_PREEMPT is not set @@ -175,14 +175,15 @@ # CONFIG_UTS_NS is not set # CONFIG_AUDIT is not set # CONFIG_IKCONFIG is not set CONFIG_SYSFS_DEPRECATED=y -CONFIG_RELAY=y +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y CONFIG_EMBEDDED=y CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set -CONFIG_HOTPLUG=y +# CONFIG_HOTPLUG is not set CONFIG_PRINTK=y CONFIG_BUG=y CONFIG_ELF_CORE=y @@ -217,11 +218,11 @@ CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y -CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_AS is not set # CONFIG_DEFAULT_DEADLINE is not set -# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="anticipatory" +CONFIG_DEFAULT_IOSCHED="cfq" # # Bus options (PCI, PCMCIA, EISA, ISA, TC) @@ -233,12 +234,10 @@ CONFIG_MMU=y # # PCCARD (PCMCIA/CardBus) support # -# CONFIG_PCCARD is not set # # PCI Hotplug Support # -# CONFIG_HOTPLUG_PCI is not set # # Executable file formats @@ -250,10 +249,7 @@ CONFIG_TRAD_SIGNALS=y # # Power management options # -CONFIG_PM=y -# CONFIG_PM_LEGACY is not set -# CONFIG_PM_DEBUG is not set -# CONFIG_PM_SYSFS_DEPRECATED is not set +# CONFIG_PM is not set # # Networking @@ -267,12 +263,7 @@ # CONFIG_NETDEBUG is not set CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set CONFIG_UNIX=y -CONFIG_XFRM=y -CONFIG_XFRM_USER=y -# CONFIG_XFRM_SUB_POLICY is not set -CONFIG_XFRM_MIGRATE=y -CONFIG_NET_KEY=y -CONFIG_NET_KEY_MIGRATE=y +# CONFIG_NET_KEY is not set CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set @@ -290,19 +281,18 @@ # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set # CONFIG_INET_XFRM_TUNNEL is not set # CONFIG_INET_TUNNEL is not set -CONFIG_INET_XFRM_MODE_TRANSPORT=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_INET_XFRM_MODE_BEET=y -CONFIG_INET_DIAG=y -CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_DIAG is not set # CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_CUBIC=y CONFIG_DEFAULT_TCP_CONG="cubic" -CONFIG_TCP_MD5SIG=y +# CONFIG_TCP_MD5SIG is not set # CONFIG_IPV6 is not set # CONFIG_INET6_XFRM_TUNNEL is not set # CONFIG_INET6_TUNNEL is not set -CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_SECMARK is not set # CONFIG_NETFILTER is not set # @@ -343,13 +333,7 @@ # CONFIG_NET_PKTGEN is not set # CONFIG_HAMRADIO is not set # CONFIG_IRDA is not set # CONFIG_BT is not set -CONFIG_IEEE80211=y -# CONFIG_IEEE80211_DEBUG is not set -CONFIG_IEEE80211_CRYPT_WEP=y -CONFIG_IEEE80211_CRYPT_CCMP=y -CONFIG_IEEE80211_SOFTMAC=y -# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set -CONFIG_WIRELESS_EXT=y +# CONFIG_IEEE80211 is not set # # Device Drivers @@ -360,14 +344,12 @@ # Generic Driver Options # CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y -CONFIG_FW_LOADER=y # CONFIG_SYS_HYPERVISOR is not set # # Connector - unified userspace <-> kernelspace linker # -CONFIG_CONNECTOR=y -CONFIG_PROC_EVENTS=y +# CONFIG_CONNECTOR is not set # # Memory Technology Devices (MTD) @@ -396,16 +378,13 @@ # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set # CONFIG_BLK_DEV_RAM is not set -# CONFIG_BLK_DEV_INITRD is not set -CONFIG_CDROM_PKTCDVD=y -CONFIG_CDROM_PKTCDVD_BUFFERS=8 -# CONFIG_CDROM_PKTCDVD_WCACHE is not set -CONFIG_ATA_OVER_ETH=y +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set # # Misc devices # -CONFIG_SGI_IOC4=y +# CONFIG_SGI_IOC4 is not set # CONFIG_TIFM_CORE is not set # @@ -416,7 +395,7 @@ # CONFIG_IDE is not set # # SCSI device support # -CONFIG_RAID_ATTRS=y +# CONFIG_RAID_ATTRS is not set # CONFIG_SCSI is not set # CONFIG_SCSI_NETLINK is not set @@ -462,26 +441,13 @@ # CONFIG_ARCNET is not set # # PHY device support # -CONFIG_PHYLIB=y - -# -# MII PHY device drivers -# -CONFIG_MARVELL_PHY=y -CONFIG_DAVICOM_PHY=y -CONFIG_QSEMI_PHY=y -CONFIG_LXT_PHY=y -CONFIG_CICADA_PHY=y -CONFIG_VITESSE_PHY=y -CONFIG_SMSC_PHY=y -# CONFIG_BROADCOM_PHY is not set -# CONFIG_FIXED_PHY is not set +# CONFIG_PHYLIB is not set # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_MII is not set +CONFIG_MII=y # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set # CONFIG_CASSINI is not set @@ -493,7 +459,27 @@ # Tulip family network device support # # CONFIG_NET_TULIP is not set # CONFIG_HP100 is not set -# CONFIG_NET_PCI is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +CONFIG_TC35815=y +# CONFIG_DGRS is not set +# CONFIG_EEPRO100 is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_SC92031 is not set # # Ethernet (1000 Mbit) @@ -509,20 +495,21 @@ # CONFIG_SIS190 is not set # CONFIG_SKGE is not set # CONFIG_SKY2 is not set # CONFIG_SK98LIN is not set +# CONFIG_VIA_VELOCITY is not set # CONFIG_TIGON3 is not set # CONFIG_BNX2 is not set -CONFIG_QLA3XXX=y +# CONFIG_QLA3XXX is not set # CONFIG_ATL1 is not set # # Ethernet (10000 Mbit) # # CONFIG_CHELSIO_T1 is not set -CONFIG_CHELSIO_T3=y +# CONFIG_CHELSIO_T3 is not set # CONFIG_IXGB is not set # CONFIG_S2IO is not set # CONFIG_MYRI10GE is not set -CONFIG_NETXEN_NIC=y +# CONFIG_NETXEN_NIC is not set # # Token Ring devices @@ -566,10 +553,7 @@ # CONFIG_INPUT_FF_MEMLESS is not set # # Userland interfaces # -CONFIG_INPUT_MOUSEDEV=y -CONFIG_INPUT_MOUSEDEV_PSAUX=y -CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 -CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_MOUSEDEV is not set # CONFIG_INPUT_JOYDEV is not set # CONFIG_INPUT_TSDEV is not set # CONFIG_INPUT_EVDEV is not set @@ -587,21 +571,13 @@ # CONFIG_INPUT_MISC is not set # # Hardware I/O ports # -CONFIG_SERIO=y -# CONFIG_SERIO_I8042 is not set -CONFIG_SERIO_SERPORT=y -# CONFIG_SERIO_PCIPS2 is not set -# CONFIG_SERIO_LIBPS2 is not set -CONFIG_SERIO_RAW=y +# CONFIG_SERIO is not set # CONFIG_GAMEPORT is not set # # Character devices # -CONFIG_VT=y -CONFIG_VT_CONSOLE=y -CONFIG_HW_CONSOLE=y -CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_VT is not set CONFIG_SERIAL_NONSTANDARD=y # CONFIG_COMPUTONE is not set # CONFIG_ROCKETPORT is not set @@ -609,7 +585,7 @@ # CONFIG_CYCLADES is not set # CONFIG_DIGIEPCA is not set # CONFIG_MOXA_INTELLIO is not set # CONFIG_MOXA_SMARTIO is not set -CONFIG_MOXA_SMARTIO_NEW=y +# CONFIG_MOXA_SMARTIO_NEW is not set # CONFIG_ISI is not set # CONFIG_SYNCLINKMP is not set # CONFIG_SYNCLINK_GT is not set @@ -629,11 +605,12 @@ # # Non-8250 serial port support # CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_SERIAL_TXX9=y CONFIG_HAS_TXX9_SERIAL=y CONFIG_SERIAL_TXX9_NR_UARTS=6 -# CONFIG_SERIAL_TXX9_CONSOLE is not set -# CONFIG_SERIAL_TXX9_STDSERIAL is not set +CONFIG_SERIAL_TXX9_CONSOLE=y +CONFIG_SERIAL_TXX9_STDSERIAL=y # CONFIG_SERIAL_JSM is not set # CONFIG_UNIX98_PTYS is not set CONFIG_LEGACY_PTYS=y @@ -685,6 +662,11 @@ # CONFIG_HWMON is not set # CONFIG_HWMON_VID is not set # +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set + +# # Multimedia devices # # CONFIG_VIDEO_DEV is not set @@ -697,51 +679,8 @@ # CONFIG_DVB is not set # # Graphics support # -# CONFIG_FIRMWARE_EDID is not set -CONFIG_FB=y -# CONFIG_FB_CFB_FILLRECT is not set -# CONFIG_FB_CFB_COPYAREA is not set -# CONFIG_FB_CFB_IMAGEBLIT is not set -# CONFIG_FB_SVGALIB is not set -# CONFIG_FB_MACMODES is not set -# CONFIG_FB_BACKLIGHT is not set -# CONFIG_FB_MODE_HELPERS is not set -# CONFIG_FB_TILEBLITTING is not set -# CONFIG_FB_CIRRUS is not set -# CONFIG_FB_PM2 is not set -# CONFIG_FB_CYBER2000 is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_IMSTT is not set -# CONFIG_FB_S1D13XXX is not set -# CONFIG_FB_NVIDIA is not set -# CONFIG_FB_RIVA is not set -# CONFIG_FB_MATROX is not set -# CONFIG_FB_RADEON is not set -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_S3 is not set -# CONFIG_FB_SAVAGE is not set -# CONFIG_FB_SIS is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_FB_KYRO is not set -# CONFIG_FB_3DFX is not set -# CONFIG_FB_VOODOO1 is not set -# CONFIG_FB_SMIVGX is not set -# CONFIG_FB_TRIDENT is not set -# CONFIG_FB_VIRTUAL is not set - -# -# Console display driver support -# -# CONFIG_VGA_CONSOLE is not set -CONFIG_DUMMY_CONSOLE=y -# CONFIG_FRAMEBUFFER_CONSOLE is not set - -# -# Logo configuration -# -# CONFIG_LOGO is not set # CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_FB is not set # # Sound @@ -864,7 +803,7 @@ # CONFIG_QUOTA is not set CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set -CONFIG_FUSE_FS=y +# CONFIG_FUSE_FS is not set # # CD-ROM/DVD Filesystems @@ -889,14 +828,13 @@ CONFIG_SYSFS=y # CONFIG_TMPFS is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y -CONFIG_CONFIGFS_FS=y +# CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems # # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set -# CONFIG_ECRYPT_FS is not set # CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set @@ -944,10 +882,7 @@ # CONFIG_NLS is not set # # Distributed Lock Manager # -CONFIG_DLM=y -CONFIG_DLM_TCP=y -# CONFIG_DLM_SCTP is not set -# CONFIG_DLM_DEBUG is not set +# CONFIG_DLM is not set # # Profiling support @@ -972,65 +907,22 @@ CONFIG_CMDLINE="" # # Security options # -CONFIG_KEYS=y -CONFIG_KEYS_DEBUG_PROC_KEYS=y +# CONFIG_KEYS is not set # CONFIG_SECURITY is not set # # Cryptographic options # -CONFIG_CRYPTO=y -CONFIG_CRYPTO_ALGAPI=y -CONFIG_CRYPTO_BLKCIPHER=y -CONFIG_CRYPTO_HASH=y -CONFIG_CRYPTO_MANAGER=y -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_XCBC=y -CONFIG_CRYPTO_NULL=y -CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA256=y -CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_WP512=y -CONFIG_CRYPTO_TGR192=y -CONFIG_CRYPTO_GF128MUL=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_PCBC=y -CONFIG_CRYPTO_LRW=y -CONFIG_CRYPTO_DES=y -CONFIG_CRYPTO_FCRYPT=y -CONFIG_CRYPTO_BLOWFISH=y -CONFIG_CRYPTO_TWOFISH=y -CONFIG_CRYPTO_TWOFISH_COMMON=y -CONFIG_CRYPTO_SERPENT=y -CONFIG_CRYPTO_AES=y -CONFIG_CRYPTO_CAST5=y -CONFIG_CRYPTO_CAST6=y -CONFIG_CRYPTO_TEA=y -CONFIG_CRYPTO_ARC4=y -CONFIG_CRYPTO_KHAZAD=y -CONFIG_CRYPTO_ANUBIS=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_MICHAEL_MIC=y -CONFIG_CRYPTO_CRC32C=y -CONFIG_CRYPTO_CAMELLIA=y - -# -# Hardware crypto devices -# +# CONFIG_CRYPTO is not set # # Library routines # CONFIG_BITREVERSE=y # CONFIG_CRC_CCITT is not set -CONFIG_CRC16=y +# CONFIG_CRC16 is not set CONFIG_CRC32=y -CONFIG_LIBCRC32C=y -CONFIG_ZLIB_INFLATE=y -CONFIG_ZLIB_DEFLATE=y +# CONFIG_LIBCRC32C is not set CONFIG_PLIST=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT=y diff --git a/arch/mips/configs/pnx8550-v2pci_defconfig b/arch/mips/configs/pnx8550-v2pci_defconfig deleted file mode 100644 index 3d6c2d7..0000000 --- a/arch/mips/configs/pnx8550-v2pci_defconfig +++ /dev/null @@ -1,1540 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.20 -# Tue Feb 20 21:47:39 2007 -# -CONFIG_MIPS=y - -# -# Machine selection -# -CONFIG_ZONE_DMA=y -# CONFIG_MIPS_MTX1 is not set -# CONFIG_MIPS_BOSPORUS is not set -# CONFIG_MIPS_PB1000 is not set -# CONFIG_MIPS_PB1100 is not set -# CONFIG_MIPS_PB1500 is not set -# CONFIG_MIPS_PB1550 is not set -# CONFIG_MIPS_PB1200 is not set -# CONFIG_MIPS_DB1000 is not set -# CONFIG_MIPS_DB1100 is not set -# CONFIG_MIPS_DB1500 is not set -# CONFIG_MIPS_DB1550 is not set -# CONFIG_MIPS_DB1200 is not set -# CONFIG_MIPS_MIRAGE is not set -# CONFIG_BASLER_EXCITE is not set -# CONFIG_MIPS_COBALT is not set -# CONFIG_MACH_DECSTATION is not set -# CONFIG_MIPS_EV64120 is not set -# CONFIG_MACH_JAZZ is not set -# CONFIG_LASAT is not set -# CONFIG_MIPS_ATLAS is not set -# CONFIG_MIPS_MALTA is not set -# CONFIG_MIPS_SEAD is not set -# CONFIG_WR_PPMC is not set -# CONFIG_MIPS_SIM is not set -# CONFIG_MOMENCO_JAGUAR_ATX is not set -# CONFIG_MOMENCO_OCELOT is not set -# CONFIG_MOMENCO_OCELOT_3 is not set -# CONFIG_MOMENCO_OCELOT_C is not set -# CONFIG_MOMENCO_OCELOT_G is not set -# CONFIG_MIPS_XXS1500 is not set -# CONFIG_PNX8550_JBS is not set -# CONFIG_PNX8550_STB810 is not set -# CONFIG_DDB5477 is not set -# CONFIG_MACH_VR41XX is not set -# CONFIG_PMC_YOSEMITE is not set -# CONFIG_QEMU is not set -# CONFIG_MARKEINS is not set -# CONFIG_SGI_IP22 is not set -# CONFIG_SGI_IP27 is not set -# CONFIG_SGI_IP32 is not set -# CONFIG_SIBYTE_BIGSUR is not set -# CONFIG_SIBYTE_SWARM is not set -# CONFIG_SIBYTE_SENTOSA is not set -# CONFIG_SIBYTE_RHONE is not set -# CONFIG_SIBYTE_CARMEL is not set -# CONFIG_SIBYTE_PTSWARM is not set -# CONFIG_SIBYTE_LITTLESUR is not set -# CONFIG_SIBYTE_CRHINE is not set -# CONFIG_SIBYTE_CRHONE is not set -# CONFIG_SNI_RM is not set -# CONFIG_TOSHIBA_JMR3927 is not set -# CONFIG_TOSHIBA_RBTX4927 is not set -# CONFIG_TOSHIBA_RBTX4938 is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_ARCH_HAS_ILOG2_U32 is not set -# CONFIG_ARCH_HAS_ILOG2_U64 is not set -CONFIG_GENERIC_FIND_NEXT_BIT=y -CONFIG_GENERIC_HWEIGHT=y -CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_GENERIC_TIME=y -CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y -CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y -CONFIG_DMA_NONCOHERENT=y -CONFIG_DMA_NEED_PCI_MAP_STATE=y -# CONFIG_CPU_BIG_ENDIAN is not set -CONFIG_CPU_LITTLE_ENDIAN=y -CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y -CONFIG_PNX8550=y -CONFIG_SOC_PNX8550=y -CONFIG_MIPS_L1_CACHE_SHIFT=5 - -# -# CPU selection -# -CONFIG_CPU_MIPS32_R1=y -# CONFIG_CPU_MIPS32_R2 is not set -# CONFIG_CPU_MIPS64_R1 is not set -# CONFIG_CPU_MIPS64_R2 is not set -# CONFIG_CPU_R3000 is not set -# CONFIG_CPU_TX39XX is not set -# CONFIG_CPU_VR41XX is not set -# CONFIG_CPU_R4300 is not set -# CONFIG_CPU_R4X00 is not set -# CONFIG_CPU_TX49XX is not set -# CONFIG_CPU_R5000 is not set -# CONFIG_CPU_R5432 is not set -# CONFIG_CPU_R6000 is not set -# CONFIG_CPU_NEVADA is not set -# CONFIG_CPU_R8000 is not set -# CONFIG_CPU_R10000 is not set -# CONFIG_CPU_RM7000 is not set -# CONFIG_CPU_RM9000 is not set -# CONFIG_CPU_SB1 is not set -CONFIG_SYS_HAS_CPU_MIPS32_R1=y -CONFIG_CPU_MIPS32=y -CONFIG_CPU_MIPSR1=y -CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y -CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y - -# -# Kernel type -# -CONFIG_32BIT=y -# CONFIG_64BIT is not set -CONFIG_PAGE_SIZE_4KB=y -# CONFIG_PAGE_SIZE_8KB is not set -# CONFIG_PAGE_SIZE_16KB is not set -# CONFIG_PAGE_SIZE_64KB is not set -CONFIG_CPU_HAS_PREFETCH=y -CONFIG_MIPS_MT_DISABLED=y -# CONFIG_MIPS_MT_SMP is not set -# CONFIG_MIPS_MT_SMTC is not set -# CONFIG_MIPS_VPE_LOADER is not set -# CONFIG_64BIT_PHYS_ADDR is not set -CONFIG_CPU_HAS_LLSC=y -CONFIG_CPU_HAS_SYNC=y -CONFIG_GENERIC_HARDIRQS=y -CONFIG_GENERIC_IRQ_PROBE=y -CONFIG_CPU_SUPPORTS_HIGHMEM=y -CONFIG_ARCH_FLATMEM_ENABLE=y -CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_FLATMEM_MANUAL=y -# CONFIG_DISCONTIGMEM_MANUAL is not set -# CONFIG_SPARSEMEM_MANUAL is not set -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y -# CONFIG_SPARSEMEM_STATIC is not set -CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_RESOURCES_64BIT is not set -CONFIG_ZONE_DMA_FLAG=1 -# CONFIG_HZ_48 is not set -# CONFIG_HZ_100 is not set -# CONFIG_HZ_128 is not set -CONFIG_HZ_250=y -# CONFIG_HZ_256 is not set -# CONFIG_HZ_1000 is not set -# CONFIG_HZ_1024 is not set -CONFIG_SYS_SUPPORTS_ARBIT_HZ=y -CONFIG_HZ=250 -CONFIG_PREEMPT_NONE=y -# CONFIG_PREEMPT_VOLUNTARY is not set -# CONFIG_PREEMPT is not set -# CONFIG_KEXEC is not set -CONFIG_LOCKDEP_SUPPORT=y -CONFIG_STACKTRACE_SUPPORT=y -CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" - -# -# Code maturity level options -# -CONFIG_EXPERIMENTAL=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -CONFIG_LOCALVERSION_AUTO=y -CONFIG_SWAP=y -CONFIG_SYSVIPC=y -# CONFIG_IPC_NS is not set -CONFIG_SYSVIPC_SYSCTL=y -# CONFIG_POSIX_MQUEUE is not set -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_TASKSTATS is not set -# CONFIG_UTS_NS is not set -# CONFIG_AUDIT is not set -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_SYSFS_DEPRECATED=y -# CONFIG_RELAY is not set -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_SYSCTL=y -CONFIG_EMBEDDED=y -# CONFIG_SYSCTL_SYSCALL is not set -CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_EXTRA_PASS is not set -CONFIG_HOTPLUG=y -CONFIG_PRINTK=y -CONFIG_BUG=y -CONFIG_ELF_CORE=y -CONFIG_BASE_FULL=y -CONFIG_FUTEX=y -CONFIG_EPOLL=y -CONFIG_SHMEM=y -CONFIG_SLAB=y -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_RT_MUTEXES=y -# CONFIG_TINY_SHMEM is not set -CONFIG_BASE_SMALL=0 -# CONFIG_SLOB is not set - -# -# Loadable module support -# -CONFIG_MODULES=y -# CONFIG_MODULE_UNLOAD is not set -# CONFIG_MODVERSIONS is not set -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# Block layer -# -CONFIG_BLOCK=y -# CONFIG_LBD is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_LSF is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y -CONFIG_DEFAULT_AS=y -# CONFIG_DEFAULT_DEADLINE is not set -# CONFIG_DEFAULT_CFQ is not set -# CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="anticipatory" - -# -# Bus options (PCI, PCMCIA, EISA, ISA, TC) -# -CONFIG_HW_HAS_PCI=y -CONFIG_PCI=y -CONFIG_MMU=y - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# -# CONFIG_HOTPLUG_PCI is not set - -# -# Executable file formats -# -CONFIG_BINFMT_ELF=y -# CONFIG_BINFMT_MISC is not set -CONFIG_TRAD_SIGNALS=y - -# -# Power management options -# -CONFIG_PM=y -# CONFIG_PM_LEGACY is not set -# CONFIG_PM_DEBUG is not set -# CONFIG_PM_SYSFS_DEPRECATED is not set - -# -# Networking -# -CONFIG_NET=y - -# -# Networking options -# -# CONFIG_NETDEBUG is not set -CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set -CONFIG_UNIX=y -CONFIG_XFRM=y -# CONFIG_XFRM_USER is not set -# CONFIG_XFRM_SUB_POLICY is not set -CONFIG_XFRM_MIGRATE=y -# CONFIG_NET_KEY is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ADVANCED_ROUTER is not set -CONFIG_IP_FIB_HASH=y -CONFIG_IP_PNP=y -# CONFIG_IP_PNP_DHCP is not set -# CONFIG_IP_PNP_BOOTP is not set -# CONFIG_IP_PNP_RARP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_ARPD is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_XFRM_TUNNEL is not set -CONFIG_INET_TUNNEL=m -CONFIG_INET_XFRM_MODE_TRANSPORT=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_INET_XFRM_MODE_BEET=y -CONFIG_INET_DIAG=y -CONFIG_INET_TCP_DIAG=y -# CONFIG_TCP_CONG_ADVANCED is not set -CONFIG_TCP_CONG_CUBIC=y -CONFIG_DEFAULT_TCP_CONG="cubic" -CONFIG_TCP_MD5SIG=y - -# -# IP: Virtual Server Configuration -# -# CONFIG_IP_VS is not set -CONFIG_IPV6=m -# CONFIG_IPV6_PRIVACY is not set -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -# CONFIG_INET6_AH is not set -# CONFIG_INET6_ESP is not set -# CONFIG_INET6_IPCOMP is not set -# CONFIG_IPV6_MIP6 is not set -# CONFIG_INET6_XFRM_TUNNEL is not set -# CONFIG_INET6_TUNNEL is not set -CONFIG_INET6_XFRM_MODE_TRANSPORT=m -CONFIG_INET6_XFRM_MODE_TUNNEL=m -CONFIG_INET6_XFRM_MODE_BEET=m -# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set -CONFIG_IPV6_SIT=m -# CONFIG_IPV6_TUNNEL is not set -# CONFIG_IPV6_MULTIPLE_TABLES is not set -# CONFIG_NETWORK_SECMARK is not set -CONFIG_NETFILTER=y -# CONFIG_NETFILTER_DEBUG is not set - -# -# Core Netfilter Configuration -# -# CONFIG_NETFILTER_NETLINK is not set -CONFIG_NF_CONNTRACK_ENABLED=m -CONFIG_NF_CONNTRACK_SUPPORT=y -# CONFIG_IP_NF_CONNTRACK_SUPPORT is not set -CONFIG_NF_CONNTRACK=m -CONFIG_NF_CT_ACCT=y -CONFIG_NF_CONNTRACK_MARK=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_GRE=m -CONFIG_NF_CT_PROTO_SCTP=m -CONFIG_NF_CONNTRACK_AMANDA=m -CONFIG_NF_CONNTRACK_FTP=m -CONFIG_NF_CONNTRACK_H323=m -CONFIG_NF_CONNTRACK_IRC=m -# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set -CONFIG_NF_CONNTRACK_PPTP=m -CONFIG_NF_CONNTRACK_SANE=m -CONFIG_NF_CONNTRACK_SIP=m -CONFIG_NF_CONNTRACK_TFTP=m -CONFIG_NETFILTER_XTABLES=m -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m -CONFIG_NETFILTER_XT_TARGET_MARK=m -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m -CONFIG_NETFILTER_XT_TARGET_NFLOG=m -CONFIG_NETFILTER_XT_TARGET_TCPMSS=m -CONFIG_NETFILTER_XT_MATCH_COMMENT=m -CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m -CONFIG_NETFILTER_XT_MATCH_CONNMARK=m -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DCCP=m -# CONFIG_NETFILTER_XT_MATCH_DSCP is not set -CONFIG_NETFILTER_XT_MATCH_ESP=m -CONFIG_NETFILTER_XT_MATCH_HELPER=m -CONFIG_NETFILTER_XT_MATCH_LENGTH=m -CONFIG_NETFILTER_XT_MATCH_LIMIT=m -CONFIG_NETFILTER_XT_MATCH_MAC=m -CONFIG_NETFILTER_XT_MATCH_MARK=m -# CONFIG_NETFILTER_XT_MATCH_POLICY is not set -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m -# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set -CONFIG_NETFILTER_XT_MATCH_REALM=m -CONFIG_NETFILTER_XT_MATCH_SCTP=m -CONFIG_NETFILTER_XT_MATCH_STATE=m -# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set -CONFIG_NETFILTER_XT_MATCH_STRING=m -CONFIG_NETFILTER_XT_MATCH_TCPMSS=m -CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m - -# -# IP: Netfilter Configuration -# -CONFIG_NF_CONNTRACK_IPV4=m -CONFIG_NF_CONNTRACK_PROC_COMPAT=y -# CONFIG_IP_NF_QUEUE is not set -# CONFIG_IP_NF_IPTABLES is not set -# CONFIG_IP_NF_ARPTABLES is not set - -# -# IPv6: Netfilter Configuration (EXPERIMENTAL) -# -CONFIG_NF_CONNTRACK_IPV6=m -# CONFIG_IP6_NF_QUEUE is not set -# CONFIG_IP6_NF_IPTABLES is not set - -# -# DCCP Configuration (EXPERIMENTAL) -# -# CONFIG_IP_DCCP is not set - -# -# SCTP Configuration (EXPERIMENTAL) -# -# CONFIG_IP_SCTP is not set - -# -# TIPC Configuration (EXPERIMENTAL) -# -# CONFIG_TIPC is not set -# CONFIG_ATM is not set -# CONFIG_BRIDGE is not set -# CONFIG_VLAN_8021Q is not set -# CONFIG_DECNET is not set -# CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_ECONET is not set -# CONFIG_WAN_ROUTER is not set - -# -# QoS and/or fair queueing -# -# CONFIG_NET_SCHED is not set -CONFIG_NET_CLS_ROUTE=y - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_BT is not set -# CONFIG_IEEE80211 is not set - -# -# Device Drivers -# - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -CONFIG_FW_LOADER=y -# CONFIG_SYS_HYPERVISOR is not set - -# -# Connector - unified userspace <-> kernelspace linker -# -# CONFIG_CONNECTOR is not set - -# -# Memory Technology Devices (MTD) -# -# CONFIG_MTD is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Plug and Play support -# -# CONFIG_PNPACPI is not set - -# -# Block devices -# -# CONFIG_BLK_CPQ_DA is not set -# CONFIG_BLK_CPQ_CISS_DA is not set -# CONFIG_BLK_DEV_DAC960 is not set -# CONFIG_BLK_DEV_UMEM is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -CONFIG_BLK_DEV_LOOP=y -# CONFIG_BLK_DEV_CRYPTOLOOP is not set -# CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_SX8 is not set -# CONFIG_BLK_DEV_UB is not set -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 -CONFIG_BLK_DEV_INITRD=y -# CONFIG_CDROM_PKTCDVD is not set -# CONFIG_ATA_OVER_ETH is not set - -# -# Misc devices -# -CONFIG_SGI_IOC4=m -# CONFIG_TIFM_CORE is not set - -# -# ATA/ATAPI/MFM/RLL support -# -CONFIG_IDE=y -CONFIG_IDE_MAX_HWIFS=4 -CONFIG_BLK_DEV_IDE=y - -# -# Please see Documentation/ide.txt for help/info on IDE drives -# -# CONFIG_BLK_DEV_IDE_SATA is not set -CONFIG_BLK_DEV_IDEDISK=y -CONFIG_IDEDISK_MULTI_MODE=y -# CONFIG_BLK_DEV_IDECD is not set -# CONFIG_BLK_DEV_IDETAPE is not set -# CONFIG_BLK_DEV_IDEFLOPPY is not set -# CONFIG_BLK_DEV_IDESCSI is not set -# CONFIG_IDE_TASK_IOCTL is not set - -# -# IDE chipset support/bugfixes -# -CONFIG_IDE_GENERIC=y -CONFIG_BLK_DEV_IDEPCI=y -CONFIG_IDEPCI_SHARE_IRQ=y -# CONFIG_BLK_DEV_OFFBOARD is not set -# CONFIG_BLK_DEV_GENERIC is not set -# CONFIG_BLK_DEV_OPTI621 is not set -CONFIG_BLK_DEV_IDEDMA_PCI=y -# CONFIG_BLK_DEV_IDEDMA_FORCED is not set -CONFIG_IDEDMA_PCI_AUTO=y -# CONFIG_IDEDMA_ONLYDISK is not set -# CONFIG_BLK_DEV_AEC62XX is not set -# CONFIG_BLK_DEV_ALI15X3 is not set -# CONFIG_BLK_DEV_AMD74XX is not set -CONFIG_BLK_DEV_CMD64X=y -# CONFIG_BLK_DEV_TRIFLEX is not set -# CONFIG_BLK_DEV_CY82C693 is not set -# CONFIG_BLK_DEV_CS5520 is not set -# CONFIG_BLK_DEV_CS5530 is not set -# CONFIG_BLK_DEV_HPT34X is not set -# CONFIG_BLK_DEV_HPT366 is not set -# CONFIG_BLK_DEV_JMICRON is not set -# CONFIG_BLK_DEV_SC1200 is not set -# CONFIG_BLK_DEV_PIIX is not set -CONFIG_BLK_DEV_IT8213=m -# CONFIG_BLK_DEV_IT821X is not set -# CONFIG_BLK_DEV_NS87415 is not set -# CONFIG_BLK_DEV_PDC202XX_OLD is not set -# CONFIG_BLK_DEV_PDC202XX_NEW is not set -# CONFIG_BLK_DEV_SVWKS is not set -# CONFIG_BLK_DEV_SIIMAGE is not set -# CONFIG_BLK_DEV_SLC90E66 is not set -# CONFIG_BLK_DEV_TRM290 is not set -# CONFIG_BLK_DEV_VIA82CXXX is not set -CONFIG_BLK_DEV_TC86C001=m -# CONFIG_IDE_ARM is not set -CONFIG_BLK_DEV_IDEDMA=y -# CONFIG_IDEDMA_IVB is not set -CONFIG_IDEDMA_AUTO=y -# CONFIG_BLK_DEV_HD is not set - -# -# SCSI device support -# -# CONFIG_RAID_ATTRS is not set -CONFIG_SCSI=y -CONFIG_SCSI_TGT=m -CONFIG_SCSI_NETLINK=y -CONFIG_SCSI_PROC_FS=y - -# -# SCSI support type (disk, tape, CD-ROM) -# -CONFIG_BLK_DEV_SD=y -# CONFIG_CHR_DEV_ST is not set -# CONFIG_CHR_DEV_OSST is not set -# CONFIG_BLK_DEV_SR is not set -# CONFIG_CHR_DEV_SG is not set -# CONFIG_CHR_DEV_SCH is not set - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# -# CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_CONSTANTS is not set -# CONFIG_SCSI_LOGGING is not set -CONFIG_SCSI_SCAN_ASYNC=y - -# -# SCSI Transports -# -CONFIG_SCSI_SPI_ATTRS=m -CONFIG_SCSI_FC_ATTRS=y -CONFIG_SCSI_ISCSI_ATTRS=m -# CONFIG_SCSI_SAS_ATTRS is not set -# CONFIG_SCSI_SAS_LIBSAS is not set - -# -# SCSI low-level drivers -# -CONFIG_ISCSI_TCP=m -# CONFIG_BLK_DEV_3W_XXXX_RAID is not set -# CONFIG_SCSI_3W_9XXX is not set -# CONFIG_SCSI_ACARD is not set -# CONFIG_SCSI_AACRAID is not set -CONFIG_SCSI_AIC7XXX=m -CONFIG_AIC7XXX_CMDS_PER_DEVICE=32 -CONFIG_AIC7XXX_RESET_DELAY_MS=15000 -# CONFIG_AIC7XXX_DEBUG_ENABLE is not set -CONFIG_AIC7XXX_DEBUG_MASK=0 -# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set -# CONFIG_SCSI_AIC7XXX_OLD is not set -# CONFIG_SCSI_AIC79XX is not set -# CONFIG_SCSI_AIC94XX is not set -# CONFIG_SCSI_DPT_I2O is not set -# CONFIG_SCSI_ARCMSR is not set -# CONFIG_MEGARAID_NEWGEN is not set -# CONFIG_MEGARAID_LEGACY is not set -# CONFIG_MEGARAID_SAS is not set -# CONFIG_SCSI_HPTIOP is not set -# CONFIG_SCSI_DMX3191D is not set -# CONFIG_SCSI_FUTURE_DOMAIN is not set -# CONFIG_SCSI_IPS is not set -# CONFIG_SCSI_INITIO is not set -# CONFIG_SCSI_INIA100 is not set -# CONFIG_SCSI_STEX is not set -# CONFIG_SCSI_SYM53C8XX_2 is not set -# CONFIG_SCSI_QLOGIC_1280 is not set -# CONFIG_SCSI_QLA_FC is not set -# CONFIG_SCSI_QLA_ISCSI is not set -# CONFIG_SCSI_LPFC is not set -# CONFIG_SCSI_DC395x is not set -# CONFIG_SCSI_DC390T is not set -# CONFIG_SCSI_NSP32 is not set -# CONFIG_SCSI_DEBUG is not set -# CONFIG_SCSI_SRP is not set - -# -# Serial ATA (prod) and Parallel ATA (experimental) drivers -# -# CONFIG_ATA is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set -# CONFIG_FUSION_SPI is not set -# CONFIG_FUSION_FC is not set -# CONFIG_FUSION_SAS is not set - -# -# IEEE 1394 (FireWire) support -# -# CONFIG_IEEE1394 is not set - -# -# I2O device support -# -# CONFIG_I2O is not set - -# -# Network device support -# -CONFIG_NETDEVICES=y -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -CONFIG_TUN=m - -# -# ARCnet devices -# -# CONFIG_ARCNET is not set - -# -# PHY device support -# -# CONFIG_PHYLIB is not set - -# -# Ethernet (10 or 100Mbit) -# -CONFIG_NET_ETHERNET=y -CONFIG_MII=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNGEM is not set -# CONFIG_CASSINI is not set -# CONFIG_NET_VENDOR_3COM is not set -# CONFIG_DM9000 is not set - -# -# Tulip family network device support -# -# CONFIG_NET_TULIP is not set -# CONFIG_HP100 is not set -CONFIG_NET_PCI=y -# CONFIG_PCNET32 is not set -# CONFIG_AMD8111_ETH is not set -# CONFIG_ADAPTEC_STARFIRE is not set -# CONFIG_B44 is not set -# CONFIG_FORCEDETH is not set -# CONFIG_DGRS is not set -# CONFIG_EEPRO100 is not set -# CONFIG_E100 is not set -# CONFIG_FEALNX is not set -CONFIG_NATSEMI=y -# CONFIG_NE2K_PCI is not set -# CONFIG_8139CP is not set -CONFIG_8139TOO=y -# CONFIG_8139TOO_PIO is not set -# CONFIG_8139TOO_TUNE_TWISTER is not set -# CONFIG_8139TOO_8129 is not set -# CONFIG_8139_OLD_RX_RESET is not set -# CONFIG_SIS900 is not set -# CONFIG_EPIC100 is not set -# CONFIG_SUNDANCE is not set -# CONFIG_TLAN is not set -# CONFIG_VIA_RHINE is not set -# CONFIG_SC92031 is not set - -# -# Ethernet (1000 Mbit) -# -# CONFIG_ACENIC is not set -# CONFIG_DL2K is not set -# CONFIG_E1000 is not set -# CONFIG_NS83820 is not set -# CONFIG_HAMACHI is not set -# CONFIG_YELLOWFIN is not set -# CONFIG_R8169 is not set -# CONFIG_SIS190 is not set -# CONFIG_SKGE is not set -# CONFIG_SKY2 is not set -# CONFIG_SK98LIN is not set -# CONFIG_VIA_VELOCITY is not set -# CONFIG_TIGON3 is not set -# CONFIG_BNX2 is not set -# CONFIG_QLA3XXX is not set -# CONFIG_ATL1 is not set - -# -# Ethernet (10000 Mbit) -# -# CONFIG_CHELSIO_T1 is not set -CONFIG_CHELSIO_T3=m -# CONFIG_IXGB is not set -# CONFIG_S2IO is not set -# CONFIG_MYRI10GE is not set -CONFIG_NETXEN_NIC=m - -# -# Token Ring devices -# -# CONFIG_TR is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set - -# -# Wan interfaces -# -# CONFIG_WAN is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -CONFIG_PPP=m -# CONFIG_PPP_MULTILINK is not set -# CONFIG_PPP_FILTER is not set -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -# CONFIG_PPP_BSDCOMP is not set -CONFIG_PPP_MPPE=m -# CONFIG_PPPOE is not set -# CONFIG_SLIP is not set -CONFIG_SLHC=m -# CONFIG_NET_FC is not set -# CONFIG_SHAPER is not set -# CONFIG_NETCONSOLE is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# -# CONFIG_ISDN is not set - -# -# Telephony Support -# -# CONFIG_PHONE is not set - -# -# Input device support -# -CONFIG_INPUT=y -# CONFIG_INPUT_FF_MEMLESS is not set - -# -# Userland interfaces -# -CONFIG_INPUT_MOUSEDEV=y -CONFIG_INPUT_MOUSEDEV_PSAUX=y -CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 -CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -CONFIG_INPUT_EVDEV=m -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -CONFIG_INPUT_KEYBOARD=y -CONFIG_KEYBOARD_ATKBD=y -# CONFIG_KEYBOARD_SUNKBD is not set -# CONFIG_KEYBOARD_LKKBD is not set -# CONFIG_KEYBOARD_XTKBD is not set -# CONFIG_KEYBOARD_NEWTON is not set -# CONFIG_KEYBOARD_STOWAWAY is not set -CONFIG_INPUT_MOUSE=y -CONFIG_MOUSE_PS2=y -# CONFIG_MOUSE_SERIAL is not set -# CONFIG_MOUSE_VSXXXAA is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -CONFIG_SERIO=y -CONFIG_SERIO_I8042=y -CONFIG_SERIO_SERPORT=y -# CONFIG_SERIO_PCIPS2 is not set -CONFIG_SERIO_LIBPS2=y -# CONFIG_SERIO_RAW is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -CONFIG_VT=y -# CONFIG_VT_CONSOLE is not set -CONFIG_HW_CONSOLE=y -# CONFIG_VT_HW_CONSOLE_BINDING is not set -CONFIG_SERIAL_NONSTANDARD=y -# CONFIG_COMPUTONE is not set -# CONFIG_ROCKETPORT is not set -# CONFIG_CYCLADES is not set -# CONFIG_DIGIEPCA is not set -# CONFIG_MOXA_INTELLIO is not set -# CONFIG_MOXA_SMARTIO is not set -CONFIG_MOXA_SMARTIO_NEW=m -# CONFIG_ISI is not set -# CONFIG_SYNCLINKMP is not set -# CONFIG_SYNCLINK_GT is not set -# CONFIG_N_HDLC is not set -# CONFIG_RISCOM8 is not set -# CONFIG_SPECIALIX is not set -# CONFIG_SX is not set -# CONFIG_RIO is not set -# CONFIG_STALDRV is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -CONFIG_SERIAL_PNX8XXX=y -CONFIG_SERIAL_PNX8XXX_CONSOLE=y -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -# CONFIG_SERIAL_JSM is not set -CONFIG_UNIX98_PTYS=y -CONFIG_LEGACY_PTYS=y -CONFIG_LEGACY_PTY_COUNT=256 - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -CONFIG_HW_RANDOM=y -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set -# CONFIG_DRM is not set -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# -# CONFIG_TCG_TPM is not set - -# -# I2C support -# -CONFIG_I2C=m -CONFIG_I2C_CHARDEV=m - -# -# I2C Algorithms -# -CONFIG_I2C_ALGOBIT=m -# CONFIG_I2C_ALGOPCF is not set -# CONFIG_I2C_ALGOPCA is not set - -# -# I2C Hardware Bus support -# -# CONFIG_I2C_ALI1535 is not set -# CONFIG_I2C_ALI1563 is not set -# CONFIG_I2C_ALI15X3 is not set -# CONFIG_I2C_AMD756 is not set -# CONFIG_I2C_AMD8111 is not set -# CONFIG_I2C_I801 is not set -# CONFIG_I2C_I810 is not set -# CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_NFORCE2 is not set -# CONFIG_I2C_OCORES is not set -# CONFIG_I2C_PARPORT_LIGHT is not set -# CONFIG_I2C_PASEMI is not set -# CONFIG_I2C_PROSAVAGE is not set -# CONFIG_I2C_SAVAGE4 is not set -# CONFIG_I2C_SIS5595 is not set -# CONFIG_I2C_SIS630 is not set -# CONFIG_I2C_SIS96X is not set -# CONFIG_I2C_STUB is not set -# CONFIG_I2C_VIA is not set -# CONFIG_I2C_VIAPRO is not set -# CONFIG_I2C_VOODOO3 is not set -# CONFIG_I2C_PCA_ISA is not set - -# -# Miscellaneous I2C Chip support -# -# CONFIG_SENSORS_DS1337 is not set -# CONFIG_SENSORS_DS1374 is not set -# CONFIG_SENSORS_EEPROM is not set -# CONFIG_SENSORS_PCF8574 is not set -# CONFIG_SENSORS_PCA9539 is not set -# CONFIG_SENSORS_PCF8591 is not set -# CONFIG_SENSORS_MAX6875 is not set -# CONFIG_I2C_DEBUG_CORE is not set -# CONFIG_I2C_DEBUG_ALGO is not set -# CONFIG_I2C_DEBUG_BUS is not set -# CONFIG_I2C_DEBUG_CHIP is not set - -# -# SPI support -# -# CONFIG_SPI is not set -# CONFIG_SPI_MASTER is not set - -# -# Dallas's 1-wire bus -# -# CONFIG_W1 is not set - -# -# Hardware Monitoring support -# -CONFIG_HWMON=y -# CONFIG_HWMON_VID is not set -# CONFIG_SENSORS_ABITUGURU is not set -# CONFIG_SENSORS_ADM1021 is not set -# CONFIG_SENSORS_ADM1025 is not set -# CONFIG_SENSORS_ADM1026 is not set -# CONFIG_SENSORS_ADM1029 is not set -# CONFIG_SENSORS_ADM1031 is not set -# CONFIG_SENSORS_ADM9240 is not set -# CONFIG_SENSORS_ASB100 is not set -# CONFIG_SENSORS_ATXP1 is not set -# CONFIG_SENSORS_DS1621 is not set -# CONFIG_SENSORS_F71805F is not set -# CONFIG_SENSORS_FSCHER is not set -# CONFIG_SENSORS_FSCPOS is not set -# CONFIG_SENSORS_GL518SM is not set -# CONFIG_SENSORS_GL520SM is not set -# CONFIG_SENSORS_IT87 is not set -# CONFIG_SENSORS_LM63 is not set -# CONFIG_SENSORS_LM75 is not set -# CONFIG_SENSORS_LM77 is not set -# CONFIG_SENSORS_LM78 is not set -# CONFIG_SENSORS_LM80 is not set -# CONFIG_SENSORS_LM83 is not set -# CONFIG_SENSORS_LM85 is not set -# CONFIG_SENSORS_LM87 is not set -# CONFIG_SENSORS_LM90 is not set -# CONFIG_SENSORS_LM92 is not set -# CONFIG_SENSORS_MAX1619 is not set -# CONFIG_SENSORS_PC87360 is not set -# CONFIG_SENSORS_PC87427 is not set -# CONFIG_SENSORS_SIS5595 is not set -# CONFIG_SENSORS_SMSC47M1 is not set -# CONFIG_SENSORS_SMSC47M192 is not set -# CONFIG_SENSORS_SMSC47B397 is not set -# CONFIG_SENSORS_VIA686A is not set -# CONFIG_SENSORS_VT1211 is not set -# CONFIG_SENSORS_VT8231 is not set -# CONFIG_SENSORS_W83781D is not set -# CONFIG_SENSORS_W83791D is not set -# CONFIG_SENSORS_W83792D is not set -# CONFIG_SENSORS_W83793 is not set -# CONFIG_SENSORS_W83L785TS is not set -# CONFIG_SENSORS_W83627HF is not set -# CONFIG_SENSORS_W83627EHF is not set -# CONFIG_HWMON_DEBUG_CHIP is not set - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# -# CONFIG_DVB is not set -# CONFIG_USB_DABUSB is not set - -# -# Graphics support -# -CONFIG_FIRMWARE_EDID=y -CONFIG_FB=y -# CONFIG_FB_DDC is not set -# CONFIG_FB_CFB_FILLRECT is not set -# CONFIG_FB_CFB_COPYAREA is not set -# CONFIG_FB_CFB_IMAGEBLIT is not set -# CONFIG_FB_SVGALIB is not set -# CONFIG_FB_MACMODES is not set -# CONFIG_FB_BACKLIGHT is not set -# CONFIG_FB_MODE_HELPERS is not set -# CONFIG_FB_TILEBLITTING is not set -# CONFIG_FB_CIRRUS is not set -# CONFIG_FB_PM2 is not set -# CONFIG_FB_CYBER2000 is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_IMSTT is not set -# CONFIG_FB_S1D13XXX is not set -# CONFIG_FB_NVIDIA is not set -# CONFIG_FB_RIVA is not set -# CONFIG_FB_MATROX is not set -# CONFIG_FB_RADEON is not set -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_S3 is not set -# CONFIG_FB_SAVAGE is not set -# CONFIG_FB_SIS is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_FB_KYRO is not set -# CONFIG_FB_3DFX is not set -# CONFIG_FB_VOODOO1 is not set -# CONFIG_FB_SMIVGX is not set -# CONFIG_FB_TRIDENT is not set -# CONFIG_FB_VIRTUAL is not set - -# -# Console display driver support -# -# CONFIG_VGA_CONSOLE is not set -CONFIG_DUMMY_CONSOLE=y -# CONFIG_FRAMEBUFFER_CONSOLE is not set - -# -# Logo configuration -# -# CONFIG_LOGO is not set -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# HID Devices -# -CONFIG_HID=y -# CONFIG_HID_DEBUG is not set - -# -# USB support -# -CONFIG_USB_ARCH_HAS_HCD=y -CONFIG_USB_ARCH_HAS_OHCI=y -CONFIG_USB_ARCH_HAS_EHCI=y -CONFIG_USB=y -# CONFIG_USB_DEBUG is not set - -# -# Miscellaneous USB options -# -CONFIG_USB_DEVICEFS=y -# CONFIG_USB_DYNAMIC_MINORS is not set -# CONFIG_USB_SUSPEND is not set -# CONFIG_USB_OTG is not set - -# -# USB Host Controller Drivers -# -# CONFIG_USB_EHCI_HCD is not set -# CONFIG_USB_ISP116X_HCD is not set -# CONFIG_USB_OHCI_HCD is not set -# CONFIG_USB_UHCI_HCD is not set -# CONFIG_USB_SL811_HCD is not set - -# -# USB Device Class drivers -# -# CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set - -# -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' -# - -# -# may also be needed; see USB_STORAGE Help for more information -# -CONFIG_USB_STORAGE=y -# CONFIG_USB_STORAGE_DEBUG is not set -# CONFIG_USB_STORAGE_DATAFAB is not set -# CONFIG_USB_STORAGE_FREECOM is not set -# CONFIG_USB_STORAGE_ISD200 is not set -# CONFIG_USB_STORAGE_DPCM is not set -# CONFIG_USB_STORAGE_USBAT is not set -# CONFIG_USB_STORAGE_SDDR09 is not set -# CONFIG_USB_STORAGE_SDDR55 is not set -# CONFIG_USB_STORAGE_JUMPSHOT is not set -# CONFIG_USB_STORAGE_ALAUDA is not set -# CONFIG_USB_STORAGE_KARMA is not set -# CONFIG_USB_LIBUSUAL is not set - -# -# USB Input Devices -# -CONFIG_USB_HID=y -# CONFIG_USB_HIDINPUT_POWERBOOK is not set -# CONFIG_HID_FF is not set -CONFIG_USB_HIDDEV=y -# CONFIG_USB_AIPTEK is not set -# CONFIG_USB_WACOM is not set -# CONFIG_USB_ACECAD is not set -# CONFIG_USB_KBTAB is not set -# CONFIG_USB_POWERMATE is not set -# CONFIG_USB_TOUCHSCREEN is not set -# CONFIG_USB_YEALINK is not set -# CONFIG_USB_XPAD is not set -# CONFIG_USB_ATI_REMOTE is not set -# CONFIG_USB_ATI_REMOTE2 is not set -# CONFIG_USB_KEYSPAN_REMOTE is not set -# CONFIG_USB_APPLETOUCH is not set -# CONFIG_USB_GTCO is not set - -# -# USB Imaging devices -# -# CONFIG_USB_MDC800 is not set -# CONFIG_USB_MICROTEK is not set - -# -# USB Network Adapters -# -# CONFIG_USB_CATC is not set -# CONFIG_USB_KAWETH is not set -# CONFIG_USB_PEGASUS is not set -# CONFIG_USB_RTL8150 is not set -# CONFIG_USB_USBNET_MII is not set -# CONFIG_USB_USBNET is not set -CONFIG_USB_MON=y - -# -# USB port drivers -# - -# -# USB Serial Converter support -# -# CONFIG_USB_SERIAL is not set - -# -# USB Miscellaneous drivers -# -# CONFIG_USB_EMI62 is not set -# CONFIG_USB_EMI26 is not set -# CONFIG_USB_ADUTUX is not set -# CONFIG_USB_AUERSWALD is not set -# CONFIG_USB_RIO500 is not set -# CONFIG_USB_LEGOTOWER is not set -# CONFIG_USB_LCD is not set -# CONFIG_USB_BERRY_CHARGE is not set -# CONFIG_USB_LED is not set -# CONFIG_USB_CYPRESS_CY7C63 is not set -# CONFIG_USB_CYTHERM is not set -# CONFIG_USB_PHIDGET is not set -# CONFIG_USB_IDMOUSE is not set -# CONFIG_USB_FTDI_ELAN is not set -# CONFIG_USB_APPLEDISPLAY is not set -# CONFIG_USB_LD is not set -# CONFIG_USB_TRANCEVIBRATOR is not set -# CONFIG_USB_TEST is not set - -# -# USB DSL modem support -# - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# MMC/SD Card support -# -# CONFIG_MMC is not set - -# -# LED devices -# -# CONFIG_NEW_LEDS is not set - -# -# LED drivers -# - -# -# LED Triggers -# - -# -# InfiniBand support -# -# CONFIG_INFINIBAND is not set - -# -# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) -# - -# -# Real Time Clock -# -# CONFIG_RTC_CLASS is not set - -# -# DMA Engine support -# -# CONFIG_DMA_ENGINE is not set - -# -# DMA Clients -# - -# -# DMA Devices -# - -# -# Auxiliary Display support -# - -# -# Virtualization -# - -# -# File systems -# -CONFIG_EXT2_FS=y -# CONFIG_EXT2_FS_XATTR is not set -# CONFIG_EXT2_FS_XIP is not set -CONFIG_EXT3_FS=y -CONFIG_EXT3_FS_XATTR=y -# CONFIG_EXT3_FS_POSIX_ACL is not set -# CONFIG_EXT3_FS_SECURITY is not set -# CONFIG_EXT4DEV_FS is not set -CONFIG_JBD=y -# CONFIG_JBD_DEBUG is not set -CONFIG_FS_MBCACHE=y -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set -CONFIG_XFS_FS=m -# CONFIG_XFS_QUOTA is not set -# CONFIG_XFS_SECURITY is not set -# CONFIG_XFS_POSIX_ACL is not set -# CONFIG_XFS_RT is not set -# CONFIG_GFS2_FS is not set -# CONFIG_OCFS2_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_ROMFS_FS is not set -CONFIG_INOTIFY=y -CONFIG_INOTIFY_USER=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -CONFIG_AUTOFS_FS=y -CONFIG_AUTOFS4_FS=y -# CONFIG_FUSE_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y -CONFIG_FAT_DEFAULT_CODEPAGE=437 -CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -# CONFIG_PROC_KCORE is not set -CONFIG_PROC_SYSCTL=y -CONFIG_SYSFS=y -CONFIG_TMPFS=y -# CONFIG_TMPFS_POSIX_ACL is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y -CONFIG_CONFIGFS_FS=m - -# -# Miscellaneous filesystems -# -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_HFSPLUS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_EFS_FS is not set -CONFIG_CRAMFS=y -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Network File Systems -# -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -# CONFIG_NFS_V3_ACL is not set -# CONFIG_NFS_V4 is not set -# CONFIG_NFS_DIRECTIO is not set -CONFIG_NFSD=m -# CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set -CONFIG_ROOT_NFS=y -CONFIG_LOCKD=y -CONFIG_LOCKD_V4=y -CONFIG_EXPORTFS=m -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=y -# CONFIG_RPCSEC_GSS_KRB5 is not set -# CONFIG_RPCSEC_GSS_SPKM3 is not set -CONFIG_SMB_FS=m -# CONFIG_SMB_NLS_DEFAULT is not set -# CONFIG_CIFS is not set -# CONFIG_NCP_FS is not set -# CONFIG_CODA_FS is not set -# CONFIG_AFS_FS is not set -# CONFIG_9P_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -CONFIG_NLS=y -CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set -# CONFIG_NLS_CODEPAGE_737 is not set -# CONFIG_NLS_CODEPAGE_775 is not set -# CONFIG_NLS_CODEPAGE_850 is not set -# CONFIG_NLS_CODEPAGE_852 is not set -# CONFIG_NLS_CODEPAGE_855 is not set -# CONFIG_NLS_CODEPAGE_857 is not set -# CONFIG_NLS_CODEPAGE_860 is not set -# CONFIG_NLS_CODEPAGE_861 is not set -# CONFIG_NLS_CODEPAGE_862 is not set -# CONFIG_NLS_CODEPAGE_863 is not set -# CONFIG_NLS_CODEPAGE_864 is not set -# CONFIG_NLS_CODEPAGE_865 is not set -# CONFIG_NLS_CODEPAGE_866 is not set -# CONFIG_NLS_CODEPAGE_869 is not set -# CONFIG_NLS_CODEPAGE_936 is not set -# CONFIG_NLS_CODEPAGE_950 is not set -# CONFIG_NLS_CODEPAGE_932 is not set -# CONFIG_NLS_CODEPAGE_949 is not set -# CONFIG_NLS_CODEPAGE_874 is not set -# CONFIG_NLS_ISO8859_8 is not set -# CONFIG_NLS_CODEPAGE_1250 is not set -# CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ASCII is not set -# CONFIG_NLS_ISO8859_1 is not set -# CONFIG_NLS_ISO8859_2 is not set -# CONFIG_NLS_ISO8859_3 is not set -# CONFIG_NLS_ISO8859_4 is not set -# CONFIG_NLS_ISO8859_5 is not set -# CONFIG_NLS_ISO8859_6 is not set -# CONFIG_NLS_ISO8859_7 is not set -# CONFIG_NLS_ISO8859_9 is not set -# CONFIG_NLS_ISO8859_13 is not set -# CONFIG_NLS_ISO8859_14 is not set -# CONFIG_NLS_ISO8859_15 is not set -# CONFIG_NLS_KOI8_R is not set -# CONFIG_NLS_KOI8_U is not set -# CONFIG_NLS_UTF8 is not set - -# -# Distributed Lock Manager -# -CONFIG_DLM=m -CONFIG_DLM_TCP=y -# CONFIG_DLM_SCTP is not set -# CONFIG_DLM_DEBUG is not set - -# -# Profiling support -# -# CONFIG_PROFILING is not set - -# -# Kernel hacking -# -CONFIG_TRACE_IRQFLAGS_SUPPORT=y -# CONFIG_PRINTK_TIME is not set -CONFIG_ENABLE_MUST_CHECK=y -# CONFIG_MAGIC_SYSRQ is not set -# CONFIG_UNUSED_SYMBOLS is not set -# CONFIG_DEBUG_FS is not set -# CONFIG_HEADERS_CHECK is not set -# CONFIG_DEBUG_KERNEL is not set -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_CROSSCOMPILE=y -CONFIG_CMDLINE="" -CONFIG_SYS_SUPPORTS_KGDB=y - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -CONFIG_CRYPTO=y -CONFIG_CRYPTO_ALGAPI=y -CONFIG_CRYPTO_BLKCIPHER=m -CONFIG_CRYPTO_HASH=m -CONFIG_CRYPTO_MANAGER=m -# CONFIG_CRYPTO_HMAC is not set -CONFIG_CRYPTO_XCBC=m -# CONFIG_CRYPTO_NULL is not set -# CONFIG_CRYPTO_MD4 is not set -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_SHA1=m -# CONFIG_CRYPTO_SHA256 is not set -# CONFIG_CRYPTO_SHA512 is not set -# CONFIG_CRYPTO_WP512 is not set -# CONFIG_CRYPTO_TGR192 is not set -CONFIG_CRYPTO_GF128MUL=m -CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_CBC=m -CONFIG_CRYPTO_PCBC=m -CONFIG_CRYPTO_LRW=m -# CONFIG_CRYPTO_DES is not set -CONFIG_CRYPTO_FCRYPT=m -# CONFIG_CRYPTO_BLOWFISH is not set -# CONFIG_CRYPTO_TWOFISH is not set -# CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_AES is not set -# CONFIG_CRYPTO_CAST5 is not set -# CONFIG_CRYPTO_CAST6 is not set -# CONFIG_CRYPTO_TEA is not set -CONFIG_CRYPTO_ARC4=m -# CONFIG_CRYPTO_KHAZAD is not set -# CONFIG_CRYPTO_ANUBIS is not set -# CONFIG_CRYPTO_DEFLATE is not set -# CONFIG_CRYPTO_MICHAEL_MIC is not set -CONFIG_CRYPTO_CRC32C=m -CONFIG_CRYPTO_CAMELLIA=m -# CONFIG_CRYPTO_TEST is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -CONFIG_BITREVERSE=y -CONFIG_CRC_CCITT=m -# CONFIG_CRC16 is not set -CONFIG_CRC32=y -CONFIG_LIBCRC32C=m -CONFIG_ZLIB_INFLATE=y -CONFIG_ZLIB_DEFLATE=m -CONFIG_TEXTSEARCH=y -CONFIG_TEXTSEARCH_KMP=m -CONFIG_TEXTSEARCH_BM=m -CONFIG_TEXTSEARCH_FSM=m -CONFIG_PLIST=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT=y diff --git a/arch/mips/gt64120/wrppmc/pci.c b/arch/mips/gt64120/wrppmc/pci.c index 2fbe934..0d5289b 100644 --- a/arch/mips/gt64120/wrppmc/pci.c +++ b/arch/mips/gt64120/wrppmc/pci.c @@ -13,7 +13,7 @@ #include #include #include -extern struct pci_ops gt64120_pci_ops; +extern struct pci_ops gt64xxx_pci0_ops; static struct resource pci0_io_resource = { .name = "pci_0 io", @@ -30,7 +30,7 @@ static struct resource pci0_mem_resource }; static struct pci_controller hose_0 = { - .pci_ops = >64120_pci_ops, + .pci_ops = >64xxx_pci0_ops, .io_resource = &pci0_io_resource, .mem_resource = &pci0_mem_resource, }; diff --git a/arch/mips/jmr3927/common/prom.c b/arch/mips/jmr3927/common/prom.c index aa481b7..5398813 100644 --- a/arch/mips/jmr3927/common/prom.c +++ b/arch/mips/jmr3927/common/prom.c @@ -41,16 +41,6 @@ #include #include -extern int prom_argc; -extern char **prom_argv, **prom_envp; - -typedef struct -{ - char *name; -/* char *val; */ -}t_env_var; - - char * __init prom_getcmdline(void) { return &(arcs_cmdline[0]); @@ -60,6 +50,8 @@ void __init prom_init_cmdline(void) { char *cp; int actr; + int prom_argc = fw_arg0; + char **prom_argv = (char **) fw_arg1; actr = 1; /* Always ignore argv[0] */ diff --git a/arch/mips/jmr3927/common/puts.c b/arch/mips/jmr3927/common/puts.c index 1c1cad9..c611ab4 100644 --- a/arch/mips/jmr3927/common/puts.c +++ b/arch/mips/jmr3927/common/puts.c @@ -32,137 +32,29 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include -#include #include -#include #define TIMEOUT 0xffffff -#define SLOW_DOWN - -static const char digits[16] = "0123456789abcdef"; - -#ifdef SLOW_DOWN -#define slow_down() { int k; for (k=0; k<10000; k++); } -#else -#define slow_down() -#endif void -putch(const unsigned char c) +prom_putchar(char c) { int i = 0; do { - slow_down(); i++; - if (i>TIMEOUT) { + if (i>TIMEOUT) break; - } } while (!(tx3927_sioptr(1)->cisr & TXx927_SICISR_TXALS)); tx3927_sioptr(1)->tfifo = c; return; } -unsigned char getch(void) -{ - int i = 0; - int dicr; - char c; - - /* diable RX int. */ - dicr = tx3927_sioptr(1)->dicr; - tx3927_sioptr(1)->dicr = 0; - - do { - slow_down(); - i++; - if (i>TIMEOUT) { - break; - } - } while (tx3927_sioptr(1)->disr & TXx927_SIDISR_UVALID) - ; - c = tx3927_sioptr(1)->rfifo; - - /* clear RX int. status */ - tx3927_sioptr(1)->disr &= ~TXx927_SIDISR_RDIS; - /* enable RX int. */ - tx3927_sioptr(1)->dicr = dicr; - - return c; -} -void -do_jmr3927_led_set(char n) -{ - /* and with current leds */ - jmr3927_led_and_set(n); -} - void -puts(unsigned char *cp) +puts(const char *cp) { - int i = 0; - - while (*cp) { - do { - slow_down(); - i++; - if (i>TIMEOUT) { - break; - } - } while (!(tx3927_sioptr(1)->cisr & TXx927_SICISR_TXALS)); - tx3927_sioptr(1)->tfifo = *cp++; - } - putch('\r'); - putch('\n'); -} - -void -fputs(unsigned char *cp) -{ - int i = 0; - - while (*cp) { - do { - slow_down(); - i++; - if (i>TIMEOUT) { - break; - } - } while (!(tx3927_sioptr(1)->cisr & TXx927_SICISR_TXALS)); - tx3927_sioptr(1)->tfifo = *cp++; - } -} - - -void -put64(uint64_t ul) -{ - int cnt; - unsigned ch; - - cnt = 16; /* 16 nibbles in a 64 bit long */ - putch('0'); - putch('x'); - do { - cnt--; - ch = (unsigned char)(ul >> cnt * 4) & 0x0F; - putch(digits[ch]); - } while (cnt > 0); -} - -void -put32(unsigned u) -{ - int cnt; - unsigned ch; - - cnt = 8; /* 8 nibbles in a 32 bit long */ - putch('0'); - putch('x'); - do { - cnt--; - ch = (unsigned char)(u >> cnt * 4) & 0x0F; - putch(digits[ch]); - } while (cnt > 0); + while (*cp) + prom_putchar(*cp++); + prom_putchar('\r'); + prom_putchar('\n'); } diff --git a/arch/mips/jmr3927/rbhma3100/Makefile b/arch/mips/jmr3927/rbhma3100/Makefile index 18fe9a8..8d00ba4 100644 --- a/arch/mips/jmr3927/rbhma3100/Makefile +++ b/arch/mips/jmr3927/rbhma3100/Makefile @@ -3,5 +3,4 @@ # Makefile for TOSHIBA JMR-TX3927 board # obj-y += init.o irq.o setup.o -obj-$(CONFIG_RUNTIME_DEBUG) += debug.o obj-$(CONFIG_KGDB) += kgdb_io.o diff --git a/arch/mips/jmr3927/rbhma3100/init.c b/arch/mips/jmr3927/rbhma3100/init.c index a55cb45..9169fab 100644 --- a/arch/mips/jmr3927/rbhma3100/init.c +++ b/arch/mips/jmr3927/rbhma3100/init.c @@ -28,20 +28,10 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include -#include -#include -#include - -#include #include -#include #include -int prom_argc; -char **prom_argv, **prom_envp; extern void __init prom_init_cmdline(void); -extern char *prom_getenv(char *envname); -unsigned long mips_nofpu = 0; const char *get_system_type(void) { @@ -52,7 +42,7 @@ #endif ; } -extern void puts(unsigned char *cp); +extern void puts(const char *cp); void __init prom_init(void) { @@ -61,10 +51,6 @@ #ifdef CONFIG_TOSHIBA_JMR3927 if ((tx3927_ccfgptr->ccfg & TX3927_CCFG_TLBOFF) == 0) puts("Warning: TX3927 TLB off\n"); #endif - prom_argc = fw_arg0; - prom_argv = (char **) fw_arg1; - prom_envp = (char **) fw_arg2; - mips_machgroup = MACH_GROUP_TOSHIBA; #ifdef CONFIG_TOSHIBA_JMR3927 diff --git a/arch/mips/jmr3927/rbhma3100/irq.c b/arch/mips/jmr3927/rbhma3100/irq.c index 7d2c203..1187b44 100644 --- a/arch/mips/jmr3927/rbhma3100/irq.c +++ b/arch/mips/jmr3927/rbhma3100/irq.c @@ -30,53 +30,21 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include - -#include -#include -#include -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include -#include #include -#include -#include #include #if JMR3927_IRQ_END > NR_IRQS #error JMR3927_IRQ_END > NR_IRQS #endif -struct tb_irq_space* tb_irq_spaces; - -static int jmr3927_irq_base = -1; - -#ifdef CONFIG_PCI -static int jmr3927_gen_iack(void) -{ - /* generate ACK cycle */ -#ifdef __BIG_ENDIAN - return (tx3927_pcicptr->iiadp >> 24) & 0xff; -#else - return tx3927_pcicptr->iiadp & 0xff; -#endif -} -#endif - #define irc_dlevel 0 #define irc_elevel 1 @@ -87,89 +55,24 @@ static unsigned char irc_level[TX3927_NU 6, 6, 6 /* TMR */ }; -static void jmr3927_irq_disable(unsigned int irq_nr); -static void jmr3927_irq_enable(unsigned int irq_nr); - -static void jmr3927_irq_ack(unsigned int irq) -{ - if (irq == JMR3927_IRQ_IRC_TMR0) - jmr3927_tmrptr->tisr = 0; /* ack interrupt */ - - jmr3927_irq_disable(irq); -} - -static void jmr3927_irq_end(unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) - jmr3927_irq_enable(irq); -} - -static void jmr3927_irq_disable(unsigned int irq_nr) -{ - struct tb_irq_space* sp; - - for (sp = tb_irq_spaces; sp; sp = sp->next) { - if (sp->start_irqno <= irq_nr && - irq_nr < sp->start_irqno + sp->nr_irqs) { - if (sp->mask_func) - sp->mask_func(irq_nr - sp->start_irqno, - sp->space_id); - break; - } - } -} - -static void jmr3927_irq_enable(unsigned int irq_nr) -{ - struct tb_irq_space* sp; - - for (sp = tb_irq_spaces; sp; sp = sp->next) { - if (sp->start_irqno <= irq_nr && - irq_nr < sp->start_irqno + sp->nr_irqs) { - if (sp->unmask_func) - sp->unmask_func(irq_nr - sp->start_irqno, - sp->space_id); - break; - } - } -} - /* * CP0_STATUS is a thread's resource (saved/restored on context switch). - * So disable_irq/enable_irq MUST handle IOC/ISAC/IRC registers. + * So disable_irq/enable_irq MUST handle IOC/IRC registers. */ -static void mask_irq_isac(int irq_nr, int space_id) -{ - /* 0: mask */ - unsigned char imask = - jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR); - unsigned int bit = 1 << irq_nr; - jmr3927_isac_reg_out(imask & ~bit, JMR3927_ISAC_INTM_ADDR); - /* flush write buffer */ - (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); -} -static void unmask_irq_isac(int irq_nr, int space_id) -{ - /* 0: mask */ - unsigned char imask = jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR); - unsigned int bit = 1 << irq_nr; - jmr3927_isac_reg_out(imask | bit, JMR3927_ISAC_INTM_ADDR); - /* flush write buffer */ - (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); -} - -static void mask_irq_ioc(int irq_nr, int space_id) +static void mask_irq_ioc(unsigned int irq) { /* 0: mask */ + unsigned int irq_nr = irq - JMR3927_IRQ_IOC; unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR); unsigned int bit = 1 << irq_nr; jmr3927_ioc_reg_out(imask & ~bit, JMR3927_IOC_INTM_ADDR); /* flush write buffer */ (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); } -static void unmask_irq_ioc(int irq_nr, int space_id) +static void unmask_irq_ioc(unsigned int irq) { /* 0: mask */ + unsigned int irq_nr = irq - JMR3927_IRQ_IOC; unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR); unsigned int bit = 1 << irq_nr; jmr3927_ioc_reg_out(imask | bit, JMR3927_IOC_INTM_ADDR); @@ -177,8 +80,9 @@ static void unmask_irq_ioc(int irq_nr, i (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); } -static void mask_irq_irc(int irq_nr, int space_id) +static void mask_irq_irc(unsigned int irq) { + unsigned int irq_nr = irq - JMR3927_IRQ_IRC; volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2]; if (irq_nr & 1) *ilrp = (*ilrp & 0x00ff) | (irc_dlevel << 8); @@ -191,8 +95,9 @@ static void mask_irq_irc(int irq_nr, int (void)tx3927_ircptr->ssr; } -static void unmask_irq_irc(int irq_nr, int space_id) +static void unmask_irq_irc(unsigned int irq) { + unsigned int irq_nr = irq - JMR3927_IRQ_IRC; volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2]; if (irq_nr & 1) *ilrp = (*ilrp & 0x00ff) | (irc_level[irq_nr] << 8); @@ -203,98 +108,14 @@ static void unmask_irq_irc(int irq_nr, i tx3927_ircptr->imr = irc_elevel; } -struct tb_irq_space jmr3927_isac_irqspace = { - .next = NULL, - .start_irqno = JMR3927_IRQ_ISAC, - nr_irqs : JMR3927_NR_IRQ_ISAC, - .mask_func = mask_irq_isac, - .unmask_func = unmask_irq_isac, - .name = "ISAC", - .space_id = 0, - can_share : 0 -}; -struct tb_irq_space jmr3927_ioc_irqspace = { - .next = NULL, - .start_irqno = JMR3927_IRQ_IOC, - nr_irqs : JMR3927_NR_IRQ_IOC, - .mask_func = mask_irq_ioc, - .unmask_func = unmask_irq_ioc, - .name = "IOC", - .space_id = 0, - can_share : 1 -}; - -struct tb_irq_space jmr3927_irc_irqspace = { - .next = NULL, - .start_irqno = JMR3927_IRQ_IRC, - .nr_irqs = JMR3927_NR_IRQ_IRC, - .mask_func = mask_irq_irc, - .unmask_func = unmask_irq_irc, - .name = "on-chip", - .space_id = 0, - .can_share = 0 -}; - - -#ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND -static int tx_branch_likely_bug_count = 0; -static int have_tx_branch_likely_bug = 0; - -static void tx_branch_likely_bug_fixup(void) -{ - struct pt_regs *regs = get_irq_regs(); - - /* TX39/49-BUG: Under this condition, the insn in delay slot - of the branch likely insn is executed (not nullified) even - the branch condition is false. */ - if (!have_tx_branch_likely_bug) - return; - if ((regs->cp0_epc & 0xfff) == 0xffc && - KSEGX(regs->cp0_epc) != KSEG0 && - KSEGX(regs->cp0_epc) != KSEG1) { - unsigned int insn = *(unsigned int*)(regs->cp0_epc - 4); - /* beql,bnel,blezl,bgtzl */ - /* bltzl,bgezl,blezall,bgezall */ - /* bczfl, bcztl */ - if ((insn & 0xf0000000) == 0x50000000 || - (insn & 0xfc0e0000) == 0x04020000 || - (insn & 0xf3fe0000) == 0x41020000) { - regs->cp0_epc -= 4; - tx_branch_likely_bug_count++; - printk(KERN_INFO - "fix branch-likery bug in %s (insn %08x)\n", - current->comm, insn); - } - } -} -#endif - -static void jmr3927_spurious(void) -{ - struct pt_regs * regs = get_irq_regs(); - -#ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND - tx_branch_likely_bug_fixup(); -#endif - printk(KERN_WARNING "spurious interrupt (cause 0x%lx, pc 0x%lx, ra 0x%lx).\n", - regs->cp0_cause, regs->cp0_epc, regs->regs[31]); -} - asmlinkage void plat_irq_dispatch(void) { - struct pt_regs * regs = get_irq_regs(); + unsigned long cp0_cause = read_c0_cause(); int irq; -#ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND - tx_branch_likely_bug_fixup(); -#endif - if ((regs->cp0_cause & CAUSEF_IP7) == 0) { -#if 0 - jmr3927_spurious(); -#endif + if ((cp0_cause & CAUSEF_IP7) == 0) return; - } - irq = (regs->cp0_cause >> CAUSEB_IP2) & 0x0f; + irq = (cp0_cause >> CAUSEB_IP2) & 0x0f; do_IRQ(irq + JMR3927_IRQ_IRC); } @@ -317,35 +138,6 @@ static struct irqaction ioc_action = { jmr3927_ioc_interrupt, 0, CPU_MASK_NONE, "IOC", NULL, NULL, }; -static irqreturn_t jmr3927_isac_interrupt(int irq, void *dev_id) -{ - unsigned char istat = jmr3927_isac_reg_in(JMR3927_ISAC_INTS2_ADDR); - int i; - - for (i = 0; i < JMR3927_NR_IRQ_ISAC; i++) { - if (istat & (1 << i)) { - irq = JMR3927_IRQ_ISAC + i; - do_IRQ(irq); - } - } - return IRQ_HANDLED; -} - -static struct irqaction isac_action = { - jmr3927_isac_interrupt, 0, CPU_MASK_NONE, "ISAC", NULL, NULL, -}; - - -static irqreturn_t jmr3927_isaerr_interrupt(int irq, void *dev_id) -{ - printk(KERN_WARNING "ISA error interrupt (irq 0x%x).\n", irq); - - return IRQ_HANDLED; -} -static struct irqaction isaerr_action = { - jmr3927_isaerr_interrupt, 0, CPU_MASK_NONE, "ISA error", NULL, NULL, -}; - static irqreturn_t jmr3927_pcierr_interrupt(int irq, void *dev_id) { printk(KERN_WARNING "PCI error interrupt (irq 0x%x).\n", irq); @@ -358,54 +150,19 @@ static struct irqaction pcierr_action = jmr3927_pcierr_interrupt, 0, CPU_MASK_NONE, "PCI error", NULL, NULL, }; -int jmr3927_ether1_irq = 0; - -void jmr3927_irq_init(u32 irq_base); +static void __init jmr3927_irq_init(void); void __init arch_init_irq(void) { - /* look for io board's presence */ - int have_isac = jmr3927_have_isac(); - /* Now, interrupt control disabled, */ /* all IRC interrupts are masked, */ /* all IRC interrupt mode are Low Active. */ - if (have_isac) { - - /* ETHER1 (NE2000 compatible 10M-Ether) parameter setup */ - /* temporary enable interrupt control */ - tx3927_ircptr->cer = 1; - /* ETHER1 Int. Is High-Active. */ - if (tx3927_ircptr->ssr & (1 << 0)) - jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT0; -#if 0 /* INT3 may be asserted by ether0 (even after reboot...) */ - else if (tx3927_ircptr->ssr & (1 << 3)) - jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT3; -#endif - /* disable interrupt control */ - tx3927_ircptr->cer = 0; - - /* Ether1: High Active */ - if (jmr3927_ether1_irq) { - int ether1_irc = jmr3927_ether1_irq - JMR3927_IRQ_IRC; - tx3927_ircptr->cr[ether1_irc / 8] |= - TX3927_IRCR_HIGH << ((ether1_irc % 8) * 2); - } - } - /* mask all IOC interrupts */ jmr3927_ioc_reg_out(0, JMR3927_IOC_INTM_ADDR); /* setup IOC interrupt mode (SOFT:High Active, Others:Low Active) */ jmr3927_ioc_reg_out(JMR3927_IOC_INTF_SOFT, JMR3927_IOC_INTP_ADDR); - if (have_isac) { - /* mask all ISAC interrupts */ - jmr3927_isac_reg_out(0, JMR3927_ISAC_INTM_ADDR); - /* setup ISAC interrupt mode (ISAIRQ3,ISAIRQ5:Low Active ???) */ - jmr3927_isac_reg_out(JMR3927_ISAC_INTF_IRQ3|JMR3927_ISAC_INTF_IRQ5, JMR3927_ISAC_INTP_ADDR); - } - /* clear PCI Soft interrupts */ jmr3927_ioc_reg_out(0, JMR3927_IOC_INTS1_ADDR); /* clear PCI Reset interrupts */ @@ -415,21 +172,11 @@ #endif tx3927_ircptr->cer = TX3927_IRCER_ICE; tx3927_ircptr->imr = irc_elevel; - jmr3927_irq_init(NR_ISA_IRQS); - - /* setup irq space */ - add_tb_irq_space(&jmr3927_isac_irqspace); - add_tb_irq_space(&jmr3927_ioc_irqspace); - add_tb_irq_space(&jmr3927_irc_irqspace); + jmr3927_irq_init(); /* setup IOC interrupt 1 (PCI, MODEM) */ setup_irq(JMR3927_IRQ_IOCINT, &ioc_action); - if (have_isac) { - setup_irq(JMR3927_IRQ_ISACINT, &isac_action); - setup_irq(JMR3927_IRQ_ISAC_ISAER, &isaerr_action); - } - #ifdef CONFIG_PCI setup_irq(JMR3927_IRQ_IRC_PCI, &pcierr_action); #endif @@ -438,21 +185,28 @@ #endif set_c0_status(ST0_IM); /* IE bit is still 0. */ } -static struct irq_chip jmr3927_irq_controller = { - .name = "jmr3927_irq", - .ack = jmr3927_irq_ack, - .mask = jmr3927_irq_disable, - .mask_ack = jmr3927_irq_ack, - .unmask = jmr3927_irq_enable, - .end = jmr3927_irq_end, +static struct irq_chip jmr3927_irq_ioc = { + .name = "jmr3927_ioc", + .ack = mask_irq_ioc, + .mask = mask_irq_ioc, + .mask_ack = mask_irq_ioc, + .unmask = unmask_irq_ioc, }; -void jmr3927_irq_init(u32 irq_base) +static struct irq_chip jmr3927_irq_irc = { + .name = "jmr3927_irc", + .ack = mask_irq_irc, + .mask = mask_irq_irc, + .mask_ack = mask_irq_irc, + .unmask = unmask_irq_irc, +}; + +static void __init jmr3927_irq_init(void) { u32 i; - for (i= irq_base; i< irq_base + JMR3927_NR_IRQ_IRC + JMR3927_NR_IRQ_IOC; i++) - set_irq_chip(i, &jmr3927_irq_controller); - - jmr3927_irq_base = irq_base; + for (i = JMR3927_IRQ_IRC; i < JMR3927_IRQ_IRC + JMR3927_NR_IRQ_IRC; i++) + set_irq_chip_and_handler(i, &jmr3927_irq_irc, handle_level_irq); + for (i = JMR3927_IRQ_IOC; i < JMR3927_IRQ_IOC + JMR3927_NR_IRQ_IOC; i++) + set_irq_chip_and_handler(i, &jmr3927_irq_ioc, handle_level_irq); } diff --git a/arch/mips/jmr3927/rbhma3100/kgdb_io.c b/arch/mips/jmr3927/rbhma3100/kgdb_io.c index 269a42d..2604f2c 100644 --- a/arch/mips/jmr3927/rbhma3100/kgdb_io.c +++ b/arch/mips/jmr3927/rbhma3100/kgdb_io.c @@ -31,23 +31,12 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include -#include -#include #include #define TIMEOUT 0xffffff -#define SLOW_DOWN - -static const char digits[16] = "0123456789abcdef"; - -#ifdef SLOW_DOWN -#define slow_down() { int k; for (k=0; k<10000; k++); } -#else -#define slow_down() -#endif static int remoteDebugInitialized = 0; +static void debugInit(int baud) int putDebugChar(unsigned char c) { @@ -103,20 +92,8 @@ unsigned char getDebugChar(void) return c; } -void debugInit(int baud) +static void debugInit(int baud) { - /* - volatile unsigned long lcr; - volatile unsigned long dicr; - volatile unsigned long disr; - volatile unsigned long cisr; - volatile unsigned long fcr; - volatile unsigned long flcr; - volatile unsigned long bgr; - volatile unsigned long tfifo; - volatile unsigned long rfifo; - */ - tx3927_sioptr(0)->lcr = 0x020; tx3927_sioptr(0)->dicr = 0; tx3927_sioptr(0)->disr = 0x4100; @@ -125,31 +102,4 @@ void debugInit(int baud) tx3927_sioptr(0)->flcr = 0x02; tx3927_sioptr(0)->bgr = ((JMR3927_BASE_BAUD + baud / 2) / baud) | TXx927_SIBGR_BCLK_T0; -#if 0 - /* - * Reset the UART. - */ - tx3927_sioptr(0)->fcr = TXx927_SIFCR_SWRST; - while (tx3927_sioptr(0)->fcr & TXx927_SIFCR_SWRST) - ; - - /* - * and set the speed of the serial port - * (currently hardwired to 9600 8N1 - */ - - tx3927_sioptr(0)->lcr = TXx927_SILCR_UMODE_8BIT | - TXx927_SILCR_USBL_1BIT | - TXx927_SILCR_SCS_IMCLK_BG; - tx3927_sioptr(0)->bgr = - ((JMR3927_BASE_BAUD + baud / 2) / baud) | - TXx927_SIBGR_BCLK_T0; - - /* HW RTS/CTS control */ - if (ser->flags & ASYNC_HAVE_CTS_LINE) - tx3927_sioptr(0)->flcr = TXx927_SIFLCR_RCS | TXx927_SIFLCR_TES | - TXx927_SIFLCR_RTSTL_MAX /* 15 */; - /* Enable RX/TX */ - tx3927_sioptr(0)->flcr &= ~(TXx927_SIFLCR_RSDE | TXx927_SIFLCR_TSDE); -#endif } diff --git a/arch/mips/jmr3927/rbhma3100/setup.c b/arch/mips/jmr3927/rbhma3100/setup.c index fc523bd..d1ef289 100644 --- a/arch/mips/jmr3927/rbhma3100/setup.c +++ b/arch/mips/jmr3927/rbhma3100/setup.c @@ -54,87 +54,18 @@ #endif #include #include -#include -#include #include -#include #include #include -#include -extern void puts(unsigned char *cp); +extern void puts(const char *cp); /* Tick Timer divider */ #define JMR3927_TIMER_CCD 0 /* 1/2 */ #define JMR3927_TIMER_CLK (JMR3927_IMCLK / (2 << JMR3927_TIMER_CCD)) -unsigned char led_state = 0xf; - -struct { - struct resource ram0; - struct resource ram1; - struct resource pcimem; - struct resource iob; - struct resource ioc; - struct resource pciio; - struct resource jmy1394; - struct resource rom1; - struct resource rom0; - struct resource sio0; - struct resource sio1; -} jmr3927_resources = { - { - .start = 0, - .end = 0x01FFFFFF, - .name = "RAM0", - .flags = IORESOURCE_MEM - }, { - .start = 0x02000000, - .end = 0x03FFFFFF, - .name = "RAM1", - .flags = IORESOURCE_MEM - }, { - .start = 0x08000000, - .end = 0x07FFFFFF, - .name = "PCIMEM", - .flags = IORESOURCE_MEM - }, { - .start = 0x10000000, - .end = 0x13FFFFFF, - .name = "IOB" - }, { - .start = 0x14000000, - .end = 0x14FFFFFF, - .name = "IOC" - }, { - .start = 0x15000000, - .end = 0x15FFFFFF, - .name = "PCIIO" - }, { - .start = 0x1D000000, - .end = 0x1D3FFFFF, - .name = "JMY1394" - }, { - .start = 0x1E000000, - .end = 0x1E3FFFFF, - .name = "ROM1" - }, { - .start = 0x1FC00000, - .end = 0x1FFFFFFF, - .name = "ROM0" - }, { - .start = 0xFFFEF300, - .end = 0xFFFEF3FF, - .name = "SIO0" - }, { - .start = 0xFFFEF400, - .end = 0xFFFEF4FF, - .name = "SIO1" - }, -}; - /* don't enable - see errata */ -int jmr3927_ccfg_toeon = 0; +static int jmr3927_ccfg_toeon; static inline void do_reset(void) { @@ -173,9 +104,15 @@ static cycle_t jmr3927_hpt_read(void) return jiffies * (JMR3927_TIMER_CLK / HZ) + jmr3927_tmrptr->trr; } +static void jmr3927_timer_ack(void) +{ + jmr3927_tmrptr->tisr = 0; /* ack interrupt */ +} + static void __init jmr3927_time_init(void) { clocksource_mips.read = jmr3927_hpt_read; + mips_timer_ack = jmr3927_timer_ack; mips_hpt_frequency = JMR3927_TIMER_CLK; } @@ -190,9 +127,6 @@ void __init plat_timer_setup(struct irqa setup_irq(JMR3927_IRQ_TICK, irq); } -#define USECS_PER_JIFFY (1000000/HZ) - -//#undef DO_WRITE_THROUGH #define DO_WRITE_THROUGH #define DO_ENABLE_CACHE @@ -224,12 +158,6 @@ void __init plat_mem_setup(void) /* Reboot on panic */ panic_timeout = 180; - { - unsigned int conf; - conf = read_c0_conf(); - } - -#if 1 /* cache setup */ { unsigned int conf; @@ -256,16 +184,14 @@ #endif write_c0_conf(conf); write_c0_cache(0); } -#endif /* initialize board */ jmr3927_board_init(); argptr = prom_getcmdline(); - if ((argptr = strstr(argptr, "toeon")) != NULL) { - jmr3927_ccfg_toeon = 1; - } + if ((argptr = strstr(argptr, "toeon")) != NULL) + jmr3927_ccfg_toeon = 1; argptr = prom_getcmdline(); if ((argptr = strstr(argptr, "ip=")) == NULL) { argptr = prom_getcmdline(); @@ -281,7 +207,7 @@ #ifdef CONFIG_SERIAL_TXX9 memset(&req, 0, sizeof(req)); req.line = i; req.iotype = UPIO_MEM; - req.membase = (char *)TX3927_SIO_REG(i); + req.membase = (unsigned char __iomem *)TX3927_SIO_REG(i); req.mapbase = TX3927_SIO_REG(i); req.irq = i == 0 ? JMR3927_IRQ_IRC_SIO0 : JMR3927_IRQ_IRC_SIO1; @@ -303,65 +229,33 @@ #endif static void tx3927_setup(void); -#ifdef CONFIG_PCI -unsigned long mips_pci_io_base; -unsigned long mips_pci_io_size; -unsigned long mips_pci_mem_base; -unsigned long mips_pci_mem_size; -/* for legacy I/O, PCI I/O PCI Bus address must be 0 */ -unsigned long mips_pci_io_pciaddr = 0; -#endif - static void __init jmr3927_board_init(void) { - char *argptr; - -#ifdef CONFIG_PCI - mips_pci_io_base = JMR3927_PCIIO; - mips_pci_io_size = JMR3927_PCIIO_SIZE; - mips_pci_mem_base = JMR3927_PCIMEM; - mips_pci_mem_size = JMR3927_PCIMEM_SIZE; -#endif - tx3927_setup(); - if (jmr3927_have_isac()) { - -#ifdef CONFIG_FB_E1355 - argptr = prom_getcmdline(); - if ((argptr = strstr(argptr, "video=")) == NULL) { - argptr = prom_getcmdline(); - strcat(argptr, " video=e1355fb:crt16h"); - } -#endif - -#ifdef CONFIG_BLK_DEV_IDE - /* overrides PCI-IDE */ -#endif - } - /* SIO0 DTR on */ jmr3927_ioc_reg_out(0, JMR3927_IOC_DTR_ADDR); jmr3927_led_set(0); - - if (jmr3927_have_isac()) - jmr3927_io_led_set(0); printk("JMR-TX3927 (Rev %d) --- IOC(Rev %d) DIPSW:%d,%d,%d,%d\n", jmr3927_ioc_reg_in(JMR3927_IOC_BREV_ADDR) & JMR3927_REV_MASK, jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR) & JMR3927_REV_MASK, jmr3927_dipsw1(), jmr3927_dipsw2(), jmr3927_dipsw3(), jmr3927_dipsw4()); - if (jmr3927_have_isac()) - printk("JMI-3927IO2 --- ISAC(Rev %d) DIPSW:%01x\n", - jmr3927_isac_reg_in(JMR3927_ISAC_REV_ADDR) & JMR3927_REV_MASK, - jmr3927_io_dipsw()); } -void __init tx3927_setup(void) +static void __init tx3927_setup(void) { int i; +#ifdef CONFIG_PCI + unsigned long mips_pci_io_base = JMR3927_PCIIO; + unsigned long mips_pci_io_size = JMR3927_PCIIO_SIZE; + unsigned long mips_pci_mem_base = JMR3927_PCIMEM; + unsigned long mips_pci_mem_size = JMR3927_PCIMEM_SIZE; + /* for legacy I/O, PCI I/O PCI Bus address must be 0 */ + unsigned long mips_pci_io_pciaddr = 0; +#endif /* SDRAMC are configured by PROM */ @@ -475,10 +369,8 @@ #endif tx3927_pcicptr->mbas = ~(mips_pci_mem_size - 1); tx3927_pcicptr->mba = 0; tx3927_pcicptr->tlbmma = 0; -#ifndef JMR3927_INIT_INDIRECT_PCI /* Enable Direct mapping Address Space Decoder */ tx3927_pcicptr->lbc |= TX3927_PCIC_LBC_ILMDE | TX3927_PCIC_LBC_ILIDE; -#endif /* Clear All Local Bus Status */ tx3927_pcicptr->lbstat = TX3927_PCIC_LBIM_ALL; @@ -491,22 +383,15 @@ #endif /* PCIC Int => IRC IRQ10 */ tx3927_pcicptr->il = TX3927_IR_PCI; -#if 1 /* Target Control (per errata) */ tx3927_pcicptr->tc = TX3927_PCIC_TC_OF8E | TX3927_PCIC_TC_IF8E; -#endif /* Enable Bus Arbiter */ -#if 0 - tx3927_pcicptr->req_trace = 0x73737373; -#endif tx3927_pcicptr->pbapmc = TX3927_PCIC_PBAPMC_PBAEN; tx3927_pcicptr->pcicmd = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | -#if 1 PCI_COMMAND_IO | -#endif PCI_COMMAND_PARITY | PCI_COMMAND_SERR; } #endif /* CONFIG_PCI */ @@ -555,8 +440,6 @@ static int __init jmr3927_rtc_init(void) .flags = IORESOURCE_MEM, }; struct platform_device *dev; - if (!jmr3927_have_nvram()) - return -ENODEV; dev = platform_device_register_simple("ds1742", -1, &res, 1); return IS_ERR(dev) ? PTR_ERR(dev) : 0; } diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c index 222de46..761a779 100644 --- a/arch/mips/kernel/asm-offsets.c +++ b/arch/mips/kernel/asm-offsets.c @@ -102,7 +102,6 @@ void output_thread_info_defines(void) offset("#define TI_ADDR_LIMIT ", struct thread_info, addr_limit); offset("#define TI_RESTART_BLOCK ", struct thread_info, restart_block); offset("#define TI_REGS ", struct thread_info, regs); - constant("#define _THREAD_SIZE_ORDER ", THREAD_SIZE_ORDER); constant("#define _THREAD_SIZE ", THREAD_SIZE); constant("#define _THREAD_MASK ", THREAD_MASK); linefeed; diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c index 9c79703..2345160 100644 --- a/arch/mips/kernel/i8259.c +++ b/arch/mips/kernel/i8259.c @@ -328,8 +328,8 @@ void __init init_i8259_irqs (void) { int i; - request_resource(&ioport_resource, &pic1_io_resource); - request_resource(&ioport_resource, &pic2_io_resource); + insert_resource(&ioport_resource, &pic1_io_resource); + insert_resource(&ioport_resource, &pic2_io_resource); init_8259A(0); diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c index 29eadd4..c658001 100644 --- a/arch/mips/kernel/kspd.c +++ b/arch/mips/kernel/kspd.c @@ -17,6 +17,7 @@ */ #include #include +#include #include #include #include @@ -198,7 +199,6 @@ void sp_work_handle_request(void) int cmd; char *vcwd; - mm_segment_t old_fs; int size; ret.retval = -1; @@ -241,8 +241,6 @@ void sp_work_handle_request(void) if ((ret.retval = sp_syscall(__NR_gettimeofday, (int)&tv, (int)&tz, 0,0)) == 0) ret.retval = tv.tv_sec; - - ret.errno = errno; break; case MTSP_SYSCALL_EXIT: @@ -279,7 +277,6 @@ void sp_work_handle_request(void) if (cmd >= 0) { ret.retval = sp_syscall(cmd, generic.arg0, generic.arg1, generic.arg2, generic.arg3); - ret.errno = errno; } else printk(KERN_WARNING "KSPD: Unknown SP syscall number %d\n", sc.cmd); diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index e6e3047..bfc8ca1 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -289,7 +289,7 @@ unsigned int rtlx_write_poll(int index) return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size); } -ssize_t rtlx_read(int index, void __user *buff, size_t count, int user) +ssize_t rtlx_read(int index, void __user *buff, size_t count) { size_t lx_write, fl = 0L; struct rtlx_channel *lx; @@ -331,9 +331,10 @@ out: return count; } -ssize_t rtlx_write(int index, const void __user *buffer, size_t count, int user) +ssize_t rtlx_write(int index, const void __user *buffer, size_t count) { struct rtlx_channel *rt; + unsigned long failed; size_t rt_read; size_t fl; @@ -363,7 +364,7 @@ ssize_t rtlx_write(int index, const void } out: - count -= cailed; + count -= failed; smp_wmb(); rt->rt_write = (rt->rt_write + count) % rt->buffer_size; diff --git a/arch/mips/mips-boards/generic/display.c b/arch/mips/mips-boards/generic/display.c index f653946..548dbe5 100644 --- a/arch/mips/mips-boards/generic/display.c +++ b/arch/mips/mips-boards/generic/display.c @@ -24,16 +24,16 @@ #include void mips_display_message(const char *str) { - static volatile unsigned int *display = NULL; + static unsigned int __iomem *display = NULL; int i; if (unlikely(display == NULL)) - display = (volatile unsigned int *)ioremap(ASCII_DISPLAY_POS_BASE, 16*sizeof(int)); + display = ioremap(ASCII_DISPLAY_POS_BASE, 16*sizeof(int)); for (i = 0; i <= 14; i=i+2) { if (*str) - display[i] = *str++; + writel(*str++, display + i); else - display[i] = ' '; + writel(' ', display + i); } } diff --git a/arch/mips/mips-boards/generic/pci.c b/arch/mips/mips-boards/generic/pci.c index 3192a14..f98d60f 100644 --- a/arch/mips/mips-boards/generic/pci.c +++ b/arch/mips/mips-boards/generic/pci.c @@ -65,7 +65,7 @@ static struct resource msc_io_resource = }; extern struct pci_ops bonito64_pci_ops; -extern struct pci_ops gt64120_pci_ops; +extern struct pci_ops gt64xxx_pci0_ops; extern struct pci_ops msc_pci_ops; static struct pci_controller bonito64_controller = { @@ -76,7 +76,7 @@ static struct pci_controller bonito64_co }; static struct pci_controller gt64120_controller = { - .pci_ops = >64120_pci_ops, + .pci_ops = >64xxx_pci0_ops, .io_resource = >64120_io_resource, .mem_resource = >64120_mem_resource, }; diff --git a/arch/mips/mips-boards/generic/reset.c b/arch/mips/mips-boards/generic/reset.c index 0996ba3..7a1bb51 100644 --- a/arch/mips/mips-boards/generic/reset.c +++ b/arch/mips/mips-boards/generic/reset.c @@ -39,24 +39,24 @@ #endif static void mips_machine_restart(char *command) { - volatile unsigned int *softres_reg = (unsigned int *)ioremap (SOFTRES_REG, sizeof(unsigned int)); + unsigned int __iomem *softres_reg = ioremap(SOFTRES_REG, sizeof(unsigned int)); - *softres_reg = GORESET; + writew(GORESET, softres_reg); } static void mips_machine_halt(void) { - volatile unsigned int *softres_reg = (unsigned int *)ioremap (SOFTRES_REG, sizeof(unsigned int)); + unsigned int __iomem *softres_reg = ioremap(SOFTRES_REG, sizeof(unsigned int)); - *softres_reg = GORESET; + writew(GORESET, softres_reg); } #if defined(CONFIG_MIPS_ATLAS) static void atlas_machine_power_off(void) { - volatile unsigned int *psustby_reg = (unsigned int *)ioremap(ATLAS_PSUSTBY_REG, sizeof(unsigned int)); + unsigned int __iomem *psustby_reg = ioremap(ATLAS_PSUSTBY_REG, sizeof(unsigned int)); - *psustby_reg = ATLAS_GOSTBY; + writew(ATLAS_GOSTBY, psustby_reg); } #endif diff --git a/arch/mips/mips-boards/malta/malta_int.c b/arch/mips/mips-boards/malta/malta_int.c index 3c206bb..83d7602 100644 --- a/arch/mips/mips-boards/malta/malta_int.c +++ b/arch/mips/mips-boards/malta/malta_int.c @@ -42,8 +42,6 @@ #include #include #include -extern void mips_timer_interrupt(void); - static DEFINE_SPINLOCK(mips_irq_lock); static inline int mips_pcibios_iack(void) @@ -85,7 +83,7 @@ static inline int mips_pcibios_iack(void dummy = BONITO_PCIMAP_CFG; iob(); /* sync */ - irq = *(volatile u32 *)(_pcictrl_bonito_pcicfg); + irq = readl((u32 *)_pcictrl_bonito_pcicfg); iob(); /* sync */ irq &= 0xff; BONITO_PCIMAP_CFG = 0; diff --git a/arch/mips/mips-boards/malta/malta_setup.c b/arch/mips/mips-boards/malta/malta_setup.c index 56ea766..7873932 100644 --- a/arch/mips/mips-boards/malta/malta_setup.c +++ b/arch/mips/mips-boards/malta/malta_setup.c @@ -145,7 +145,8 @@ #endif #ifdef CONFIG_BLK_DEV_IDE /* Check PCI clock */ { - int jmpr = (*((volatile unsigned int *)ioremap(MALTA_JMPRS_REG, sizeof(unsigned int))) >> 2) & 0x07; + unsigned int __iomem *jmpr_p = (unsigned int *) ioremap(MALTA_JMPRS_REG, sizeof(unsigned int)); + int jmpr = (readw(jmpr_p) >> 2) & 0x07; static const int pciclocks[] __initdata = { 33, 20, 25, 30, 12, 16, 37, 10 }; @@ -179,7 +180,6 @@ #if defined(CONFIG_VGA_CONSOLE) }; #endif #endif - mips_reboot_setup(); board_time_init = mips_time_init; diff --git a/arch/mips/mips-boards/sim/Makefile b/arch/mips/mips-boards/sim/Makefile index 6aeebc9..dc0bfda 100644 --- a/arch/mips/mips-boards/sim/Makefile +++ b/arch/mips/mips-boards/sim/Makefile @@ -17,7 +17,8 @@ # with this program; if not, write to th # 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # -obj-y := sim_setup.o sim_mem.o sim_time.o sim_int.o sim_cmdline.o +obj-y := sim_platform.o sim_setup.o sim_mem.o sim_time.o sim_int.o \ + sim_cmdline.o obj-$(CONFIG_EARLY_PRINTK) += sim_console.o obj-$(CONFIG_SMP) += sim_smp.o diff --git a/arch/mips/mips-boards/sim/sim_platform.c b/arch/mips/mips-boards/sim/sim_platform.c new file mode 100644 index 0000000..53210a8 --- /dev/null +++ b/arch/mips/mips-boards/sim/sim_platform.c @@ -0,0 +1,35 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2007 by Ralf Baechle (ralf@linux-mips.org) + */ +#include +#include +#include +#include + +static char mipsnet_string[] = "mipsnet"; + +static struct platform_device eth1_device = { + .name = mipsnet_string, + .id = 0, +}; + +/* + * Create a platform device for the GPI port that receives the + * image data from the embedded camera. + */ +static int __init mipsnet_devinit(void) +{ + int err; + + err = platform_device_register(ð1_device); + if (err) + printk(KERN_ERR "%s: registration failed\n", mipsnet_string); + + return err; +} + +device_initcall(mipsnet_devinit); diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index 4e8f1b6..abf99b1 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -96,7 +96,7 @@ void __flush_anon_page(struct page *page kaddr = kmap_coherent(page, vmaddr); flush_data_cache_page((unsigned long)kaddr); - kunmap_coherent(kaddr); + kunmap_coherent(); } } diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index e9951c0..2d1c2c0 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -177,7 +177,7 @@ #endif #define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1))) -void kunmap_coherent(struct page *page) +void kunmap_coherent(void) { #ifndef CONFIG_MIPS_MT_SMTC unsigned int wired; @@ -210,7 +210,7 @@ void copy_user_highpage(struct page *to, if (cpu_has_dc_aliases) { vfrom = kmap_coherent(from, vaddr); copy_page(vto, vfrom); - kunmap_coherent(from); + kunmap_coherent(); } else { vfrom = kmap_atomic(from, KM_USER0); copy_page(vto, vfrom); @@ -233,7 +233,7 @@ void copy_to_user_page(struct vm_area_st if (cpu_has_dc_aliases) { void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); memcpy(vto, src, len); - kunmap_coherent(page); + kunmap_coherent(); } else memcpy(dst, src, len); if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) @@ -250,7 +250,7 @@ void copy_from_user_page(struct vm_area_ void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); memcpy(dst, vfrom, len); - kunmap_coherent(page); + kunmap_coherent(); } else memcpy(dst, src, len); } @@ -351,18 +351,15 @@ #ifdef CONFIG_HIGHMEM #endif kmap_coherent_init(); -#ifdef CONFIG_ISA - if (max_low_pfn >= MAX_DMA_PFN) - if (min_low_pfn >= MAX_DMA_PFN) { - zones_size[ZONE_DMA] = 0; - zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn; - } else { - zones_size[ZONE_DMA] = MAX_DMA_PFN - min_low_pfn; - zones_size[ZONE_NORMAL] = max_low_pfn - MAX_DMA_PFN; - } +#ifdef CONFIG_ZONE_DMA + if (min_low_pfn < MAX_DMA_PFN && MAX_DMA_PFN <= max_low_pfn) { + zones_size[ZONE_DMA] = MAX_DMA_PFN - min_low_pfn; + zones_size[ZONE_NORMAL] = max_low_pfn - MAX_DMA_PFN; + } else if (max_low_pfn < MAX_DMA_PFN) + zones_size[ZONE_DMA] = max_low_pfn - min_low_pfn; else #endif - zones_size[ZONE_DMA] = max_low_pfn - min_low_pfn; + zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn; #ifdef CONFIG_HIGHMEM zones_size[ZONE_HIGHMEM] = highend_pfn - highstart_pfn; diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile index bf85995..df487c0 100644 --- a/arch/mips/pci/Makefile +++ b/arch/mips/pci/Makefile @@ -8,8 +8,7 @@ # # PCI bus host bridge specific code # obj-$(CONFIG_MIPS_BONITO64) += ops-bonito64.o -obj-$(CONFIG_MIPS_GT64111) += ops-gt64111.o -obj-$(CONFIG_MIPS_GT64120) += ops-gt64120.o +obj-$(CONFIG_PCI_GT64XXX_PCI0) += ops-gt64xxx_pci0.o obj-$(CONFIG_PCI_MARVELL) += ops-marvell.o obj-$(CONFIG_MIPS_MSC) += ops-msc.o obj-$(CONFIG_MIPS_NILE4) += ops-nile4.o diff --git a/arch/mips/pci/fixup-jmr3927.c b/arch/mips/pci/fixup-jmr3927.c index 6e72d21..73d1850 100644 --- a/arch/mips/pci/fixup-jmr3927.c +++ b/arch/mips/pci/fixup-jmr3927.c @@ -29,7 +29,6 @@ */ #include #include -#include #include #include @@ -81,14 +80,8 @@ int __init pcibios_map_irq(struct pci_de /* Check OnBoard Ethernet (IDSEL=A24, DevNu=13) */ if (dev->bus->parent == NULL && - slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(24)) { - extern int jmr3927_ether1_irq; - /* check this irq line was reserved for ether1 */ - if (jmr3927_ether1_irq != JMR3927_IRQ_ETHER0) - irq = JMR3927_IRQ_ETHER0; - else - irq = 0; /* disable */ - } + slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(24)) + irq = JMR3927_IRQ_ETHER0; return irq; } diff --git a/arch/mips/pci/ops-gt64111.c b/arch/mips/pci/ops-gt64111.c deleted file mode 100644 index ecd3991..0000000 --- a/arch/mips/pci/ops-gt64111.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 1995, 1996, 1997, 2002 by Ralf Baechle - * Copyright (C) 2001, 2002, 2003 by Liam Davies (ldavies@agile.tv) - */ -#include -#include -#include -#include - -#include -#include -#include - -#include - -/* - * Device 31 on the GT64111 is used to generate PCI special - * cycles, so we shouldn't expected to find a device there ... - */ -static inline int pci_range_ck(struct pci_bus *bus, unsigned int devfn) -{ - if (bus->number == 0 && PCI_SLOT(devfn) < 31) - return 0; - - return -1; -} - -static int gt64111_pci_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 * val) -{ - if (pci_range_ck(bus, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - switch (size) { - case 4: - PCI_CFG_SET(devfn, where); - *val = GT_READ(GT_PCI0_CFGDATA_OFS); - return PCIBIOS_SUCCESSFUL; - - case 2: - PCI_CFG_SET(devfn, (where & ~0x3)); - *val = GT_READ(GT_PCI0_CFGDATA_OFS) - >> ((where & 3) * 8); - return PCIBIOS_SUCCESSFUL; - - case 1: - PCI_CFG_SET(devfn, (where & ~0x3)); - *val = GT_READ(GT_PCI0_CFGDATA_OFS) - >> ((where & 3) * 8); - return PCIBIOS_SUCCESSFUL; - } - - return PCIBIOS_BAD_REGISTER_NUMBER; -} - -static int gt64111_pci_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - u32 tmp; - - if (pci_range_ck(bus, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - switch (size) { - case 4: - PCI_CFG_SET(devfn, where); - GT_WRITE(GT_PCI0_CFGDATA_OFS, val); - - return PCIBIOS_SUCCESSFUL; - - case 2: - PCI_CFG_SET(devfn, (where & ~0x3)); - tmp = GT_READ(GT_PCI0_CFGDATA_OFS); - tmp &= ~(0xffff << ((where & 0x3) * 8)); - tmp |= (val << ((where & 0x3) * 8)); - GT_WRITE(GT_PCI0_CFGDATA_OFS, tmp); - - return PCIBIOS_SUCCESSFUL; - - case 1: - PCI_CFG_SET(devfn, (where & ~0x3)); - tmp = GT_READ(GT_PCI0_CFGDATA_OFS); - tmp &= ~(0xff << ((where & 0x3) * 8)); - tmp |= (val << ((where & 0x3) * 8)); - GT_WRITE(GT_PCI0_CFGDATA_OFS, tmp); - - return PCIBIOS_SUCCESSFUL; - } - - return PCIBIOS_BAD_REGISTER_NUMBER; -} - -struct pci_ops gt64111_pci_ops = { - .read = gt64111_pci_read_config, - .write = gt64111_pci_write_config, -}; diff --git a/arch/mips/pci/ops-gt64120.c b/arch/mips/pci/ops-gt64120.c deleted file mode 100644 index 6335844..0000000 --- a/arch/mips/pci/ops-gt64120.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc. - * All rights reserved. - * Authors: Carsten Langgaard - * Maciej W. Rozycki - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ -#include -#include -#include - -#include - -#define PCI_ACCESS_READ 0 -#define PCI_ACCESS_WRITE 1 - -/* - * PCI configuration cycle AD bus definition - */ -/* Type 0 */ -#define PCI_CFG_TYPE0_REG_SHF 0 -#define PCI_CFG_TYPE0_FUNC_SHF 8 - -/* Type 1 */ -#define PCI_CFG_TYPE1_REG_SHF 0 -#define PCI_CFG_TYPE1_FUNC_SHF 8 -#define PCI_CFG_TYPE1_DEV_SHF 11 -#define PCI_CFG_TYPE1_BUS_SHF 16 - -static int gt64120_pcibios_config_access(unsigned char access_type, - struct pci_bus *bus, unsigned int devfn, int where, u32 * data) -{ - unsigned char busnum = bus->number; - u32 intr; - - if ((busnum == 0) && (devfn >= PCI_DEVFN(31, 0))) - return -1; /* Because of a bug in the galileo (for slot 31). */ - - /* Clear cause register bits */ - GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | - GT_INTRCAUSE_TARABORT0_BIT)); - - /* Setup address */ - GT_WRITE(GT_PCI0_CFGADDR_OFS, - (busnum << GT_PCI0_CFGADDR_BUSNUM_SHF) | - (devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | - ((where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF) | - GT_PCI0_CFGADDR_CONFIGEN_BIT); - - if (access_type == PCI_ACCESS_WRITE) { - if (busnum == 0 && PCI_SLOT(devfn) == 0) { - /* - * The Galileo system controller is acting - * differently than other devices. - */ - GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); - } else - __GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); - } else { - if (busnum == 0 && PCI_SLOT(devfn) == 0) { - /* - * The Galileo system controller is acting - * differently than other devices. - */ - *data = GT_READ(GT_PCI0_CFGDATA_OFS); - } else - *data = __GT_READ(GT_PCI0_CFGDATA_OFS); - } - - /* Check for master or target abort */ - intr = GT_READ(GT_INTRCAUSE_OFS); - - if (intr & (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)) { - /* Error occurred */ - - /* Clear bits */ - GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | - GT_INTRCAUSE_TARABORT0_BIT)); - - return -1; - } - - return 0; -} - - -/* - * We can't address 8 and 16 bit words directly. Instead we have to - * read/write a 32bit word and mask/modify the data we actually want. - */ -static int gt64120_pcibios_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 * val) -{ - u32 data = 0; - - if (gt64120_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, - &data)) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (size == 1) - *val = (data >> ((where & 3) << 3)) & 0xff; - else if (size == 2) - *val = (data >> ((where & 3) << 3)) & 0xffff; - else - *val = data; - - return PCIBIOS_SUCCESSFUL; -} - -static int gt64120_pcibios_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - u32 data = 0; - - if (size == 4) - data = val; - else { - if (gt64120_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, - where, &data)) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (size == 1) - data = (data & ~(0xff << ((where & 3) << 3))) | - (val << ((where & 3) << 3)); - else if (size == 2) - data = (data & ~(0xffff << ((where & 3) << 3))) | - (val << ((where & 3) << 3)); - } - - if (gt64120_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where, - &data)) - return PCIBIOS_DEVICE_NOT_FOUND; - - return PCIBIOS_SUCCESSFUL; -} - -struct pci_ops gt64120_pci_ops = { - .read = gt64120_pcibios_read, - .write = gt64120_pcibios_write -}; diff --git a/arch/mips/pci/ops-gt64xxx_pci0.c b/arch/mips/pci/ops-gt64xxx_pci0.c new file mode 100644 index 0000000..3d896c5 --- /dev/null +++ b/arch/mips/pci/ops-gt64xxx_pci0.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc. + * All rights reserved. + * Authors: Carsten Langgaard + * Maciej W. Rozycki + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ +#include +#include +#include + +#include + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +/* + * PCI configuration cycle AD bus definition + */ +/* Type 0 */ +#define PCI_CFG_TYPE0_REG_SHF 0 +#define PCI_CFG_TYPE0_FUNC_SHF 8 + +/* Type 1 */ +#define PCI_CFG_TYPE1_REG_SHF 0 +#define PCI_CFG_TYPE1_FUNC_SHF 8 +#define PCI_CFG_TYPE1_DEV_SHF 11 +#define PCI_CFG_TYPE1_BUS_SHF 16 + +static int gt64xxx_pci0_pcibios_config_access(unsigned char access_type, + struct pci_bus *bus, unsigned int devfn, int where, u32 * data) +{ + unsigned char busnum = bus->number; + u32 intr; + + if ((busnum == 0) && (devfn >= PCI_DEVFN(31, 0))) + return -1; /* Because of a bug in the galileo (for slot 31). */ + + /* Clear cause register bits */ + GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | + GT_INTRCAUSE_TARABORT0_BIT)); + + /* Setup address */ + GT_WRITE(GT_PCI0_CFGADDR_OFS, + (busnum << GT_PCI0_CFGADDR_BUSNUM_SHF) | + (devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | + ((where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF) | + GT_PCI0_CFGADDR_CONFIGEN_BIT); + + if (access_type == PCI_ACCESS_WRITE) { + if (busnum == 0 && PCI_SLOT(devfn) == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); + } else + __GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); + } else { + if (busnum == 0 && PCI_SLOT(devfn) == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + *data = GT_READ(GT_PCI0_CFGDATA_OFS); + } else + *data = __GT_READ(GT_PCI0_CFGDATA_OFS); + } + + /* Check for master or target abort */ + intr = GT_READ(GT_INTRCAUSE_OFS); + + if (intr & (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)) { + /* Error occurred */ + + /* Clear bits */ + GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | + GT_INTRCAUSE_TARABORT0_BIT)); + + return -1; + } + + return 0; +} + + +/* + * We can't address 8 and 16 bit words directly. Instead we have to + * read/write a 32bit word and mask/modify the data we actually want. + */ +static int gt64xxx_pci0_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + u32 data = 0; + + if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, + where, &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 1) + *val = (data >> ((where & 3) << 3)) & 0xff; + else if (size == 2) + *val = (data >> ((where & 3) << 3)) & 0xffff; + else + *val = data; + + return PCIBIOS_SUCCESSFUL; +} + +static int gt64xxx_pci0_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 data = 0; + + if (size == 4) + data = val; + else { + if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_READ, bus, + devfn, where, &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 1) + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else if (size == 2) + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + } + + if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, + where, &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops gt64xxx_pci0_ops = { + .read = gt64xxx_pci0_pcibios_read, + .write = gt64xxx_pci0_pcibios_write +}; diff --git a/arch/mips/pci/ops-tx3927.c b/arch/mips/pci/ops-tx3927.c index 42530a0..aa698bd 100644 --- a/arch/mips/pci/ops-tx3927.c +++ b/arch/mips/pci/ops-tx3927.c @@ -40,7 +40,6 @@ #include #include #include -#include static inline int mkaddr(unsigned char bus, unsigned char dev_fn, unsigned char where) @@ -130,234 +129,3 @@ struct pci_ops jmr3927_pci_ops = { jmr3927_pci_read_config, jmr3927_pci_write_config, }; - - -#ifndef JMR3927_INIT_INDIRECT_PCI - -inline unsigned long tc_readl(volatile __u32 * addr) -{ - return readl(addr); -} - -inline void tc_writel(unsigned long data, volatile __u32 * addr) -{ - writel(data, addr); -} -#else - -unsigned long tc_readl(volatile __u32 * addr) -{ - unsigned long val; - - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) CPHYSADDR(addr); - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_MEMREAD << PCI_IPCIBE_ICMD_SHIFT) | - PCI_IPCIBE_IBE_LONG; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - val = - le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr-> - ipcidata); - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; - return val; -} - -void tc_writel(unsigned long data, volatile __u32 * addr) -{ - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata = - cpu_to_le32(data); - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) CPHYSADDR(addr); - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_MEMWRITE << PCI_IPCIBE_ICMD_SHIFT) | - PCI_IPCIBE_IBE_LONG; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; -} - -unsigned char tx_ioinb(unsigned char *addr) -{ - unsigned long val; - __u32 ioaddr; - int offset; - int byte; - - ioaddr = (unsigned long) addr; - offset = ioaddr & 0x3; - byte = 0xf & ~(8 >> offset); - - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) ioaddr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | byte; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - val = - le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr-> - ipcidata); - val = val & 0xff; - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; - return val; -} - -void tx_iooutb(unsigned long data, unsigned char *addr) -{ - __u32 ioaddr; - int offset; - int byte; - - data = data | (data << 8) | (data << 16) | (data << 24); - ioaddr = (unsigned long) addr; - offset = ioaddr & 0x3; - byte = 0xf & ~(8 >> offset); - - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata = data; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) ioaddr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | byte; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; -} - -unsigned short tx_ioinw(unsigned short *addr) -{ - unsigned long val; - __u32 ioaddr; - int offset; - int byte; - - ioaddr = (unsigned long) addr; - offset = ioaddr & 0x2; - byte = 3 << offset; - - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) ioaddr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | byte; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - val = - le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr-> - ipcidata); - val = val & 0xffff; - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; - return val; - -} - -void tx_iooutw(unsigned long data, unsigned short *addr) -{ - __u32 ioaddr; - int offset; - int byte; - - data = data | (data << 16); - ioaddr = (unsigned long) addr; - offset = ioaddr & 0x2; - byte = 3 << offset; - - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata = data; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) ioaddr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | byte; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; -} - -unsigned long tx_ioinl(unsigned int *addr) -{ - unsigned long val; - __u32 ioaddr; - - ioaddr = (unsigned long) addr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) ioaddr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | - PCI_IPCIBE_IBE_LONG; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - val = - le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr-> - ipcidata); - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; - return val; -} - -void tx_iooutl(unsigned long data, unsigned int *addr) -{ - __u32 ioaddr; - - ioaddr = (unsigned long) addr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata = - cpu_to_le32(data); - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr = - (unsigned long) ioaddr; - *(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe = - (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | - PCI_IPCIBE_IBE_LONG; - while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); - /* clear by setting */ - tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; -} - -void tx_insbyte(unsigned char *addr, void *buffer, unsigned int count) -{ - unsigned char *ptr = (unsigned char *) buffer; - - while (count--) { - *ptr++ = tx_ioinb(addr); - } -} - -void tx_insword(unsigned short *addr, void *buffer, unsigned int count) -{ - unsigned short *ptr = (unsigned short *) buffer; - - while (count--) { - *ptr++ = tx_ioinw(addr); - } -} - -void tx_inslong(unsigned int *addr, void *buffer, unsigned int count) -{ - unsigned long *ptr = (unsigned long *) buffer; - - while (count--) { - *ptr++ = tx_ioinl(addr); - } -} - -void tx_outsbyte(unsigned char *addr, void *buffer, unsigned int count) -{ - unsigned char *ptr = (unsigned char *) buffer; - - while (count--) { - tx_iooutb(*ptr++, addr); - } -} - -void tx_outsword(unsigned short *addr, void *buffer, unsigned int count) -{ - unsigned short *ptr = (unsigned short *) buffer; - - while (count--) { - tx_iooutw(*ptr++, addr); - } -} - -void tx_outslong(unsigned int *addr, void *buffer, unsigned int count) -{ - unsigned long *ptr = (unsigned long *) buffer; - - while (count--) { - tx_iooutl(*ptr++, addr); - } -} -#endif diff --git a/arch/mips/pci/pci-lasat.c b/arch/mips/pci/pci-lasat.c index 88fb191..985784a 100644 --- a/arch/mips/pci/pci-lasat.c +++ b/arch/mips/pci/pci-lasat.c @@ -12,7 +12,7 @@ #include #include extern struct pci_ops nile4_pci_ops; -extern struct pci_ops gt64120_pci_ops; +extern struct pci_ops gt64xxx_pci0_ops; static struct resource lasat_pci_mem_resource = { .name = "LASAT PCI MEM", .start = 0x18000000, @@ -38,7 +38,7 @@ static int __init lasat_pci_setup(void) switch (mips_machtype) { case MACH_LASAT_100: - lasat_pci_controller.pci_ops = >64120_pci_ops; + lasat_pci_controller.pci_ops = >64xxx_pci0_ops; break; case MACH_LASAT_200: lasat_pci_controller.pci_ops = &nile4_pci_ops; diff --git a/arch/mips/pci/pci-ocelot.c b/arch/mips/pci/pci-ocelot.c index 2b9495d..7f94f26 100644 --- a/arch/mips/pci/pci-ocelot.c +++ b/arch/mips/pci/pci-ocelot.c @@ -81,7 +81,7 @@ static struct resource ocelot_io_resourc }; static struct pci_controller ocelot_pci_controller = { - .pci_ops = gt64120_pci_ops; + .pci_ops = gt64xxx_pci0_ops; .mem_resource = &ocelot_mem_resource; .io_resource = &ocelot_io_resource; }; diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index de7cfc5..8108231 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -77,6 +77,13 @@ pcibios_align_resource(void *data, struc void __init register_pci_controller(struct pci_controller *hose) { + if (request_resource(&iomem_resource, hose->mem_resource) < 0) + goto out; + if (request_resource(&ioport_resource, hose->io_resource) < 0) { + release_resource(hose->mem_resource); + goto out; + } + *hose_tail = hose; hose_tail = &hose->next; @@ -87,6 +94,11 @@ void __init register_pci_controller(stru printk(KERN_WARNING "registering PCI controller with io_map_base unset\n"); } + return; + +out: + printk(KERN_WARNING + "Skipping PCI bus scan due to resource conflict\n"); } /* Most MIPS systems have straight-forward swizzling needs. */ @@ -121,11 +133,6 @@ static int __init pcibios_init(void) /* Scan all of the recorded PCI controllers. */ for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { - if (request_resource(&iomem_resource, hose->mem_resource) < 0) - goto out; - if (request_resource(&ioport_resource, hose->io_resource) < 0) - goto out_free_mem_resource; - if (!hose->iommu) PCI_DMA_BUS_IS_PHYS = 1; @@ -144,14 +151,6 @@ static int __init pcibios_init(void) need_domain_info = 1; } } - continue; - -out_free_mem_resource: - release_resource(hose->mem_resource); - -out: - printk(KERN_WARNING - "Skipping PCI bus scan due to resource conflict\n"); } if (!pci_probe_only) diff --git a/arch/mips/sgi-ip22/ip22-nvram.c b/arch/mips/sgi-ip22/ip22-nvram.c index fd29fd4..e19d60d 100644 --- a/arch/mips/sgi-ip22/ip22-nvram.c +++ b/arch/mips/sgi-ip22/ip22-nvram.c @@ -52,8 +52,7 @@ #define BITS_IN_COMMAND 11 * national semiconductor nv ram chip the op code is 3 bits and * the address is 6/8 bits. */ -static inline void eeprom_cmd(volatile unsigned int *ctrl, unsigned cmd, - unsigned reg) +static inline void eeprom_cmd(unsigned int *ctrl, unsigned cmd, unsigned reg) { unsigned short ser_cmd; int i; @@ -61,33 +60,34 @@ static inline void eeprom_cmd(volatile u ser_cmd = cmd | (reg << (16 - BITS_IN_COMMAND)); for (i = 0; i < BITS_IN_COMMAND; i++) { if (ser_cmd & (1<<15)) /* if high order bit set */ - *ctrl |= EEPROM_DATO; + writel(readl(ctrl) | EEPROM_DATO, ctrl); else - *ctrl &= ~EEPROM_DATO; - *ctrl &= ~EEPROM_ECLK; - *ctrl |= EEPROM_ECLK; + writel(readl(ctrl) & ~EEPROM_DATO, ctrl); + writel(readl(ctrl) & ~EEPROM_ECLK, ctrl); + writel(readl(ctrl) | EEPROM_ECLK, ctrl); ser_cmd <<= 1; } - *ctrl &= ~EEPROM_DATO; /* see data sheet timing diagram */ + /* see data sheet timing diagram */ + writel(readl(ctrl) & ~EEPROM_DATO, ctrl); } -unsigned short ip22_eeprom_read(volatile unsigned int *ctrl, int reg) +unsigned short ip22_eeprom_read(unsigned int *ctrl, int reg) { unsigned short res = 0; int i; - *ctrl &= ~EEPROM_EPROT; + writel(readl(ctrl) & ~EEPROM_EPROT, ctrl); eeprom_cs_on(ctrl); eeprom_cmd(ctrl, EEPROM_READ, reg); /* clock the data ouf of serial mem */ for (i = 0; i < 16; i++) { - *ctrl &= ~EEPROM_ECLK; + writel(readl(ctrl) & ~EEPROM_ECLK, ctrl); delay(); - *ctrl |= EEPROM_ECLK; + writel(readl(ctrl) | EEPROM_ECLK, ctrl); delay(); res <<= 1; - if (*ctrl & EEPROM_DATI) + if (readl(ctrl) & EEPROM_DATI) res |= 1; } diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c index 2055547..8e88a44 100644 --- a/arch/mips/sgi-ip22/ip22-time.c +++ b/arch/mips/sgi-ip22/ip22-time.c @@ -94,7 +94,7 @@ static int indy_rtc_set_time(unsigned lo static unsigned long dosample(void) { u32 ct0, ct1; - volatile u8 msb, lsb; + u8 msb, lsb; /* Start the counter. */ sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | @@ -107,21 +107,21 @@ static unsigned long dosample(void) /* Latch and spin until top byte of counter2 is zero */ do { - sgint->tcword = SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT; - lsb = sgint->tcnt2; - msb = sgint->tcnt2; + writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword); + lsb = readb(&sgint->tcnt2); + msb = readb(&sgint->tcnt2); ct1 = read_c0_count(); } while (msb); /* Stop the counter. */ - sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | - SGINT_TCWORD_MSWST); + writeb(sgint->tcword, (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | + SGINT_TCWORD_MSWST)); /* * Return the difference, this is how far the r4k counter increments * for every 1/HZ seconds. We round off the nearest 1 MHz of master * clock (= 1000000 / HZ / 2). */ - /*return (ct1 - ct0 + (500000/HZ/2)) / (500000/HZ) * (500000/HZ);*/ + return (ct1 - ct0) / (500000/HZ) * (500000/HZ); } diff --git a/arch/mips/sibyte/Kconfig b/arch/mips/sibyte/Kconfig index bdf24a7..e6b003e 100644 --- a/arch/mips/sibyte/Kconfig +++ b/arch/mips/sibyte/Kconfig @@ -2,6 +2,7 @@ config SIBYTE_SB1250 bool select HW_HAS_PCI select SIBYTE_ENABLE_LDT_IF_PCI + select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_SB1xxx_SOC select SYS_SUPPORTS_SMP @@ -34,6 +35,7 @@ config SIBYTE_BCM112X config SIBYTE_BCM1x80 bool select HW_HAS_PCI + select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_SB1xxx_SOC select SYS_SUPPORTS_SMP diff --git a/arch/mips/sibyte/common/Makefile b/arch/mips/sibyte/common/Makefile new file mode 100644 index 0000000..8a06a4f --- /dev/null +++ b/arch/mips/sibyte/common/Makefile @@ -0,0 +1,5 @@ +obj-y := + +obj-$(CONFIG_SIBYTE_TBPROF) += sb_tbprof.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/sibyte/common/sb_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c new file mode 100644 index 0000000..4fcdaa8 --- /dev/null +++ b/arch/mips/sibyte/common/sb_tbprof.c @@ -0,0 +1,601 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2001, 2002, 2003 Broadcom Corporation + * Copyright (C) 2007 Ralf Baechle + * Copyright (C) 2007 MIPS Technologies, Inc. + * written by Ralf Baechle + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) +#include +#include +#include +#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X) +#include +#include +#include +#else +#error invalid SiByte UART configuation +#endif + +#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) +#undef K_INT_TRACE_FREEZE +#define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE +#undef K_INT_PERF_CNT +#define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT +#endif + +#include +#include + +#define SBPROF_TB_MAJOR 240 + +typedef u64 tb_sample_t[6*256]; + +enum open_status { + SB_CLOSED, + SB_OPENING, + SB_OPEN +}; + +struct sbprof_tb { + wait_queue_head_t tb_sync; + wait_queue_head_t tb_read; + struct mutex lock; + enum open_status open; + tb_sample_t *sbprof_tbbuf; + int next_tb_sample; + + volatile int tb_enable; + volatile int tb_armed; + +}; + +static struct sbprof_tb sbp; + +#define MAX_SAMPLE_BYTES (24*1024*1024) +#define MAX_TBSAMPLE_BYTES (12*1024*1024) + +#define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t)) +#define TB_SAMPLE_SIZE (sizeof(tb_sample_t)) +#define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE) + +/* ioctls */ +#define SBPROF_ZBSTART _IOW('s', 0, int) +#define SBPROF_ZBSTOP _IOW('s', 1, int) +#define SBPROF_ZBWAITFULL _IOW('s', 2, int) + +/* + * Routines for using 40-bit SCD cycle counter + * + * Client responsible for either handling interrupts or making sure + * the cycles counter never saturates, e.g., by doing + * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs. + */ + +/* + * Configures SCD counter 0 to count ZCLKs starting from val; + * Configures SCD counters1,2,3 to count nothing. + * Must not be called while gathering ZBbus profiles. + */ + +#define zclk_timer_init(val) \ + __asm__ __volatile__ (".set push;" \ + ".set mips64;" \ + "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ + "sd %0, 0x10($8);" /* write val to counter0 */ \ + "sd %1, 0($8);" /* config counter0 for zclks*/ \ + ".set pop" \ + : /* no outputs */ \ + /* enable, counter0 */ \ + : /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \ + : /* modifies */ "$8" ) + + +/* Reads SCD counter 0 and puts result in value + unsigned long long val; */ +#define zclk_get(val) \ + __asm__ __volatile__ (".set push;" \ + ".set mips64;" \ + "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ + "ld %0, 0x10($8);" /* write val to counter0 */ \ + ".set pop" \ + : /* outputs */ "=r"(val) \ + : /* inputs */ \ + : /* modifies */ "$8" ) + +#define DEVNAME "sb_tbprof" + +#define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES) + +/* + * Support for ZBbus sampling using the trace buffer + * + * We use the SCD performance counter interrupt, caused by a Zclk counter + * overflow, to trigger the start of tracing. + * + * We set the trace buffer to sample everything and freeze on + * overflow. + * + * We map the interrupt for trace_buffer_freeze to handle it on CPU 0. + * + */ + +static u64 tb_period; + +static void arm_tb(void) +{ + u64 scdperfcnt; + u64 next = (1ULL << 40) - tb_period; + u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL; + + /* + * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to + * trigger start of trace. XXX vary sampling period + */ + __raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1)); + scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); + + /* + * Unfortunately, in Pass 2 we must clear all counters to knock down + * a previous interrupt request. This means that bus profiling + * requires ALL of the SCD perf counters. + */ +#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) + __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) | + /* keep counters 0,2,3,4,5,6,7 as is */ + V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ + IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0)); + __raw_writeq( + M_SPC_CFG_ENABLE | /* enable counting */ + M_SPC_CFG_CLEAR | /* clear all counters */ + V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ + IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1)); +#else + __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) | + /* keep counters 0,2,3 as is */ + M_SPC_CFG_ENABLE | /* enable counting */ + M_SPC_CFG_CLEAR | /* clear all counters */ + V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ + IOADDR(A_SCD_PERF_CNT_CFG)); +#endif + __raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1)); + /* Reset the trace buffer */ + __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); +#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT) + /* XXXKW may want to expose control to the data-collector */ + tb_options |= M_SCD_TRACE_CFG_FORCECNT; +#endif + __raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG)); + sbp.tb_armed = 1; +} + +static irqreturn_t sbprof_tb_intr(int irq, void *dev_id) +{ + int i; + + pr_debug(DEVNAME ": tb_intr\n"); + + if (sbp.next_tb_sample < MAX_TB_SAMPLES) { + /* XXX should use XKPHYS to make writes bypass L2 */ + u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++]; + /* Read out trace */ + __raw_writeq(M_SCD_TRACE_CFG_START_READ, + IOADDR(A_SCD_TRACE_CFG)); + __asm__ __volatile__ ("sync" : : : "memory"); + /* Loop runs backwards because bundles are read out in reverse order */ + for (i = 256 * 6; i > 0; i -= 6) { + /* Subscripts decrease to put bundle in the order */ + /* t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */ + p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); + /* read t2 hi */ + p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); + /* read t2 lo */ + p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); + /* read t1 hi */ + p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); + /* read t1 lo */ + p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); + /* read t0 hi */ + p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); + /* read t0 lo */ + } + if (!sbp.tb_enable) { + pr_debug(DEVNAME ": tb_intr shutdown\n"); + __raw_writeq(M_SCD_TRACE_CFG_RESET, + IOADDR(A_SCD_TRACE_CFG)); + sbp.tb_armed = 0; + wake_up_interruptible(&sbp.tb_sync); + } else { + /* knock down current interrupt and get another one later */ + arm_tb(); + } + } else { + /* No more trace buffer samples */ + pr_debug(DEVNAME ": tb_intr full\n"); + __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); + sbp.tb_armed = 0; + if (!sbp.tb_enable) + wake_up_interruptible(&sbp.tb_sync); + wake_up_interruptible(&sbp.tb_read); + } + return IRQ_HANDLED; +} + +static irqreturn_t sbprof_pc_intr(int irq, void *dev_id) +{ + printk(DEVNAME ": unexpected pc_intr"); + return IRQ_NONE; +} + +/* + * Requires: Already called zclk_timer_init with a value that won't + * saturate 40 bits. No subsequent use of SCD performance counters + * or trace buffer. + */ + +static int sbprof_zbprof_start(struct file *filp) +{ + u64 scdperfcnt; + int err; + + if (xchg(&sbp.tb_enable, 1)) + return -EBUSY; + + pr_debug(DEVNAME ": starting\n"); + + sbp.next_tb_sample = 0; + filp->f_pos = 0; + + err = request_irq (K_INT_TRACE_FREEZE, sbprof_tb_intr, 0, + DEVNAME " trace freeze", &sbp); + if (err) + return -EBUSY; + + /* Make sure there isn't a perf-cnt interrupt waiting */ + scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); + /* Disable and clear counters, override SRC_1 */ + __raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) | + M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1), + IOADDR(A_SCD_PERF_CNT_CFG)); + + /* + * We grab this interrupt to prevent others from trying to use + * it, even though we don't want to service the interrupts + * (they only feed into the trace-on-interrupt mechanism) + */ + if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) { + free_irq(K_INT_TRACE_FREEZE, &sbp); + return -EBUSY; + } + + /* + * I need the core to mask these, but the interrupt mapper to + * pass them through. I am exploiting my knowledge that + * cp0_status masks out IP[5]. krw + */ +#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) + __raw_writeq(K_BCM1480_INT_MAP_I3, + IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) + + ((K_BCM1480_INT_PERF_CNT & 0x3f) << 3))); +#else + __raw_writeq(K_INT_MAP_I3, + IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + + (K_INT_PERF_CNT << 3))); +#endif + + /* Initialize address traps */ + __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_0)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_1)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_2)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_3)); + + __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3)); + + __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2)); + __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3)); + + /* Initialize Trace Event 0-7 */ + /* when interrupt */ + __raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7)); + + /* Initialize Trace Sequence 0-7 */ + /* Start on event 0 (interrupt) */ + __raw_writeq(V_SCD_TRSEQ_FUNC_START | 0x0fff, + IOADDR(A_SCD_TRACE_SEQUENCE_0)); + /* dsamp when d used | asamp when a used */ + __raw_writeq(M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE | + K_SCD_TRSEQ_TRIGGER_ALL, + IOADDR(A_SCD_TRACE_SEQUENCE_1)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6)); + __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7)); + + /* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */ +#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) + __raw_writeq(1ULL << (K_BCM1480_INT_PERF_CNT & 0x3f), + IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_TRACE_L))); +#else + __raw_writeq(1ULL << K_INT_PERF_CNT, + IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE))); +#endif + arm_tb(); + + pr_debug(DEVNAME ": done starting\n"); + + return 0; +} + +static int sbprof_zbprof_stop(void) +{ + int err = 0; + + pr_debug(DEVNAME ": stopping\n"); + + if (sbp.tb_enable) { + /* + * XXXKW there is a window here where the intr handler may run, + * see the disable, and do the wake_up before this sleep + * happens. + */ + pr_debug(DEVNAME ": wait for disarm\n"); + err = wait_event_interruptible(sbp.tb_sync, !sbp.tb_armed); + pr_debug(DEVNAME ": disarm complete, stat %d\n", err); + + if (err) + return err; + + sbp.tb_enable = 0; + free_irq(K_INT_TRACE_FREEZE, &sbp); + free_irq(K_INT_PERF_CNT, &sbp); + } + + pr_debug(DEVNAME ": done stopping\n"); + + return err; +} + +static int sbprof_tb_open(struct inode *inode, struct file *filp) +{ + int minor; + + minor = iminor(inode); + if (minor != 0) + return -ENODEV; + + if (xchg(&sbp.open, SB_OPENING) != SB_CLOSED) + return -EBUSY; + + memset(&sbp, 0, sizeof(struct sbprof_tb)); + sbp.sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES); + if (!sbp.sbprof_tbbuf) + return -ENOMEM; + memset(sbp.sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES); + init_waitqueue_head(&sbp.tb_sync); + init_waitqueue_head(&sbp.tb_read); + mutex_init(&sbp.lock); + + sbp.open = SB_OPEN; + + return 0; +} + +static int sbprof_tb_release(struct inode *inode, struct file *filp) +{ + int minor; + + minor = iminor(inode); + if (minor != 0 || !sbp.open) + return -ENODEV; + + mutex_lock(&sbp.lock); + + if (sbp.tb_armed || sbp.tb_enable) + sbprof_zbprof_stop(); + + vfree(sbp.sbprof_tbbuf); + sbp.open = 0; + + mutex_unlock(&sbp.lock); + + return 0; +} + +static ssize_t sbprof_tb_read(struct file *filp, char *buf, + size_t size, loff_t *offp) +{ + int cur_sample, sample_off, cur_count, sample_left; + char *src; + int count = 0; + char *dest = buf; + long cur_off = *offp; + + if (!access_ok(VERIFY_WRITE, buf, size)) + return -EFAULT; + + mutex_lock(&sbp.lock); + + count = 0; + cur_sample = cur_off / TB_SAMPLE_SIZE; + sample_off = cur_off % TB_SAMPLE_SIZE; + sample_left = TB_SAMPLE_SIZE - sample_off; + + while (size && (cur_sample < sbp.next_tb_sample)) { + int err; + + cur_count = size < sample_left ? size : sample_left; + src = (char *)(((long)sbp.sbprof_tbbuf[cur_sample])+sample_off); + err = __copy_to_user(dest, src, cur_count); + if (err) { + *offp = cur_off + cur_count - err; + mutex_unlock(&sbp.lock); + return err; + } + pr_debug(DEVNAME ": read from sample %d, %d bytes\n", + cur_sample, cur_count); + size -= cur_count; + sample_left -= cur_count; + if (!sample_left) { + cur_sample++; + sample_off = 0; + sample_left = TB_SAMPLE_SIZE; + } else { + sample_off += cur_count; + } + cur_off += cur_count; + dest += cur_count; + count += cur_count; + } + *offp = cur_off; + mutex_unlock(&sbp.lock); + + return count; +} + +static long sbprof_tb_ioctl(struct file *filp, + unsigned int command, + unsigned long arg) +{ + int err = 0; + + switch (command) { + case SBPROF_ZBSTART: + mutex_lock(&sbp.lock); + err = sbprof_zbprof_start(filp); + mutex_unlock(&sbp.lock); + break; + + case SBPROF_ZBSTOP: + mutex_lock(&sbp.lock); + err = sbprof_zbprof_stop(); + mutex_unlock(&sbp.lock); + break; + + case SBPROF_ZBWAITFULL: { + err = wait_event_interruptible(sbp.tb_read, TB_FULL); + if (err) + break; + + err = put_user(TB_FULL, (int *) arg); + break; + } + + default: + err = -EINVAL; + break; + } + + return err; +} + +static const struct file_operations sbprof_tb_fops = { + .owner = THIS_MODULE, + .open = sbprof_tb_open, + .release = sbprof_tb_release, + .read = sbprof_tb_read, + .unlocked_ioctl = sbprof_tb_ioctl, + .compat_ioctl = sbprof_tb_ioctl, + .mmap = NULL, +}; + +static struct class *tb_class; +static struct device *tb_dev; + +static int __init sbprof_tb_init(void) +{ + struct device *dev; + struct class *tbc; + int err; + + if (register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) { + printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n", + SBPROF_TB_MAJOR); + return -EIO; + } + + tbc = class_create(THIS_MODULE, "sb_tracebuffer"); + if (IS_ERR(tbc)) { + err = PTR_ERR(tbc); + goto out_chrdev; + } + + tb_class = tbc; + + dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), "tb"); + if (IS_ERR(dev)) { + err = PTR_ERR(dev); + goto out_class; + } + tb_dev = dev; + + sbp.open = 0; + tb_period = zbbus_mhz * 10000LL; + pr_info(DEVNAME ": initialized - tb_period = %lld\n", + (long long) tb_period); + return 0; + +out_class: + class_destroy(tb_class); +out_chrdev: + unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME); + + return err; +} + +static void __exit sbprof_tb_cleanup(void) +{ + device_destroy(tb_class, MKDEV(SBPROF_TB_MAJOR, 0)); + unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME); + class_destroy(tb_class); +} + +module_init(sbprof_tb_init); +module_exit(sbprof_tb_cleanup); + +MODULE_ALIAS_CHARDEV_MAJOR(SBPROF_TB_MAJOR); +MODULE_AUTHOR("Ralf Baechle "); +MODULE_LICENSE("GPL"); diff --git a/arch/mips/sibyte/sb1250/Makefile b/arch/mips/sibyte/sb1250/Makefile index 04c0f1a..df662c6 100644 --- a/arch/mips/sibyte/sb1250/Makefile +++ b/arch/mips/sibyte/sb1250/Makefile @@ -1,6 +1,5 @@ obj-y := setup.o irq.o time.o obj-$(CONFIG_SMP) += smp.o -obj-$(CONFIG_SIBYTE_TBPROF) += bcm1250_tbprof.o obj-$(CONFIG_SIBYTE_STANDALONE) += prom.o obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o diff --git a/arch/mips/sibyte/sb1250/bcm1250_tbprof.c b/arch/mips/sibyte/sb1250/bcm1250_tbprof.c deleted file mode 100644 index ea0ca13..0000000 --- a/arch/mips/sibyte/sb1250/bcm1250_tbprof.c +++ /dev/null @@ -1,571 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) 2001, 2002, 2003 Broadcom Corporation - * Copyright (C) 2007 Ralf Baechle - * Copyright (C) 2007 MIPS Technologies, Inc. - * written by Ralf Baechle - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define SBPROF_TB_MAJOR 240 - -typedef u64 tb_sample_t[6*256]; - -enum open_status { - SB_CLOSED, - SB_OPENING, - SB_OPEN -}; - -struct sbprof_tb { - wait_queue_head_t tb_sync; - wait_queue_head_t tb_read; - struct mutex lock; - enum open_status open; - tb_sample_t *sbprof_tbbuf; - int next_tb_sample; - - volatile int tb_enable; - volatile int tb_armed; - -}; - -static struct sbprof_tb sbp; - -#define MAX_SAMPLE_BYTES (24*1024*1024) -#define MAX_TBSAMPLE_BYTES (12*1024*1024) - -#define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t)) -#define TB_SAMPLE_SIZE (sizeof(tb_sample_t)) -#define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE) - -/* ioctls */ -#define SBPROF_ZBSTART _IOW('s', 0, int) -#define SBPROF_ZBSTOP _IOW('s', 1, int) -#define SBPROF_ZBWAITFULL _IOW('s', 2, int) - -/* - * Routines for using 40-bit SCD cycle counter - * - * Client responsible for either handling interrupts or making sure - * the cycles counter never saturates, e.g., by doing - * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs. - */ - -/* - * Configures SCD counter 0 to count ZCLKs starting from val; - * Configures SCD counters1,2,3 to count nothing. - * Must not be called while gathering ZBbus profiles. - */ - -#define zclk_timer_init(val) \ - __asm__ __volatile__ (".set push;" \ - ".set mips64;" \ - "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ - "sd %0, 0x10($8);" /* write val to counter0 */ \ - "sd %1, 0($8);" /* config counter0 for zclks*/ \ - ".set pop" \ - : /* no outputs */ \ - /* enable, counter0 */ \ - : /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \ - : /* modifies */ "$8" ) - - -/* Reads SCD counter 0 and puts result in value - unsigned long long val; */ -#define zclk_get(val) \ - __asm__ __volatile__ (".set push;" \ - ".set mips64;" \ - "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ - "ld %0, 0x10($8);" /* write val to counter0 */ \ - ".set pop" \ - : /* outputs */ "=r"(val) \ - : /* inputs */ \ - : /* modifies */ "$8" ) - -#define DEVNAME "bcm1250_tbprof" - -#define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES) - -/* - * Support for ZBbus sampling using the trace buffer - * - * We use the SCD performance counter interrupt, caused by a Zclk counter - * overflow, to trigger the start of tracing. - * - * We set the trace buffer to sample everything and freeze on - * overflow. - * - * We map the interrupt for trace_buffer_freeze to handle it on CPU 0. - */ - -static u64 tb_period; - -static void arm_tb(void) -{ - u64 scdperfcnt; - u64 next = (1ULL << 40) - tb_period; - u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL; - - /* - * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to trigger - *start of trace. XXX vary sampling period - */ - __raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1)); - scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); - - /* - * Unfortunately, in Pass 2 we must clear all counters to knock down a - * previous interrupt request. This means that bus profiling requires - * ALL of the SCD perf counters. - */ - __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) | - /* keep counters 0,2,3 as is */ - M_SPC_CFG_ENABLE | /* enable counting */ - M_SPC_CFG_CLEAR | /* clear all counters */ - V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ - IOADDR(A_SCD_PERF_CNT_CFG)); - __raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1)); - - /* Reset the trace buffer */ - __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); -#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT) - /* XXXKW may want to expose control to the data-collector */ - tb_options |= M_SCD_TRACE_CFG_FORCECNT; -#endif - __raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG)); - sbp.tb_armed = 1; -} - -static irqreturn_t sbprof_tb_intr(int irq, void *dev_id) -{ - int i; - - pr_debug(DEVNAME ": tb_intr\n"); - - if (sbp.next_tb_sample < MAX_TB_SAMPLES) { - /* XXX should use XKPHYS to make writes bypass L2 */ - u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++]; - /* Read out trace */ - __raw_writeq(M_SCD_TRACE_CFG_START_READ, - IOADDR(A_SCD_TRACE_CFG)); - __asm__ __volatile__ ("sync" : : : "memory"); - /* Loop runs backwards because bundles are read out in reverse order */ - for (i = 256 * 6; i > 0; i -= 6) { - /* Subscripts decrease to put bundle in the order */ - /* t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */ - p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); - /* read t2 hi */ - p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); - /* read t2 lo */ - p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); - /* read t1 hi */ - p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); - /* read t1 lo */ - p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); - /* read t0 hi */ - p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); - /* read t0 lo */ - } - if (!sbp.tb_enable) { - pr_debug(DEVNAME ": tb_intr shutdown\n"); - __raw_writeq(M_SCD_TRACE_CFG_RESET, - IOADDR(A_SCD_TRACE_CFG)); - sbp.tb_armed = 0; - wake_up(&sbp.tb_sync); - } else { - arm_tb(); /* knock down current interrupt and get another one later */ - } - } else { - /* No more trace buffer samples */ - pr_debug(DEVNAME ": tb_intr full\n"); - __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); - sbp.tb_armed = 0; - if (!sbp.tb_enable) { - wake_up(&sbp.tb_sync); - } - wake_up(&sbp.tb_read); - } - - return IRQ_HANDLED; -} - -static irqreturn_t sbprof_pc_intr(int irq, void *dev_id) -{ - printk(DEVNAME ": unexpected pc_intr"); - return IRQ_NONE; -} - -/* - * Requires: Already called zclk_timer_init with a value that won't - * saturate 40 bits. No subsequent use of SCD performance counters - * or trace buffer. - */ - -static int sbprof_zbprof_start(struct file *filp) -{ - u64 scdperfcnt; - int err; - - if (xchg(&sbp.tb_enable, 1)) - return -EBUSY; - - pr_debug(DEVNAME ": starting\n"); - - sbp.next_tb_sample = 0; - filp->f_pos = 0; - - err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0, - DEVNAME " trace freeze", &sbp); - if (err) - return -EBUSY; - - /* Make sure there isn't a perf-cnt interrupt waiting */ - scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); - /* Disable and clear counters, override SRC_1 */ - __raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) | - M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1), - IOADDR(A_SCD_PERF_CNT_CFG)); - - /* - * We grab this interrupt to prevent others from trying to use it, even - * though we don't want to service the interrupts (they only feed into - * the trace-on-interrupt mechanism) - */ - err = request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, - DEVNAME " scd perfcnt", &sbp); - if (err) - goto out_free_irq; - - /* - * I need the core to mask these, but the interrupt mapper to pass them - * through. I am exploiting my knowledge that cp0_status masks out - * IP[5]. krw - */ - __raw_writeq(K_INT_MAP_I3, - IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + - (K_INT_PERF_CNT << 3))); - - /* Initialize address traps */ - __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_0)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_1)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_2)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_3)); - - __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3)); - - __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2)); - __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3)); - - /* Initialize Trace Event 0-7 */ - /* when interrupt */ - __raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7)); - - /* Initialize Trace Sequence 0-7 */ - /* Start on event 0 (interrupt) */ - __raw_writeq(V_SCD_TRSEQ_FUNC_START | 0x0fff, - IOADDR(A_SCD_TRACE_SEQUENCE_0)); - /* dsamp when d used | asamp when a used */ - __raw_writeq(M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE | - K_SCD_TRSEQ_TRIGGER_ALL, - IOADDR(A_SCD_TRACE_SEQUENCE_1)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6)); - __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7)); - - /* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */ - __raw_writeq(1ULL << K_INT_PERF_CNT, - IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE))); - - arm_tb(); - - pr_debug(DEVNAME ": done starting\n"); - - return 0; - -out_free_irq: - free_irq(K_INT_TRACE_FREEZE, &sbp); - - return err; -} - -static int sbprof_zbprof_stop(void) -{ - int err; - - pr_debug(DEVNAME ": stopping\n"); - - if (sbp.tb_enable) { - /* - * XXXKW there is a window here where the intr handler may run, - * see the disable, and do the wake_up before this sleep - * happens. - */ - pr_debug(DEVNAME ": wait for disarm\n"); - err = wait_event_interruptible(sbp.tb_sync, !sbp.tb_armed); - pr_debug(DEVNAME ": disarm complete, stat %d\n", err); - - if (err) - return err; - - sbp.tb_enable = 0; - free_irq(K_INT_TRACE_FREEZE, &sbp); - free_irq(K_INT_PERF_CNT, &sbp); - } - - pr_debug(DEVNAME ": done stopping\n"); - - return 0; -} - -static int sbprof_tb_open(struct inode *inode, struct file *filp) -{ - int minor; - - minor = iminor(inode); - if (minor != 0) - return -ENODEV; - - if (xchg(&sbp.open, SB_OPENING) != SB_CLOSED) - return -EBUSY; - - memset(&sbp, 0, sizeof(struct sbprof_tb)); - - sbp.sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES); - if (!sbp.sbprof_tbbuf) - return -ENOMEM; - - memset(sbp.sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES); - init_waitqueue_head(&sbp.tb_sync); - init_waitqueue_head(&sbp.tb_read); - mutex_init(&sbp.lock); - - sbp.open = SB_OPEN; - - return 0; -} - -static int sbprof_tb_release(struct inode *inode, struct file *filp) -{ - int minor = iminor(inode); - - if (minor != 0 || !sbp.open) - return -ENODEV; - - mutex_lock(&sbp.lock); - - if (sbp.tb_armed || sbp.tb_enable) - sbprof_zbprof_stop(); - - vfree(sbp.sbprof_tbbuf); - sbp.open = 0; - - mutex_unlock(&sbp.lock); - - return 0; -} - -static ssize_t sbprof_tb_read(struct file *filp, char *buf, - size_t size, loff_t *offp) -{ - int cur_sample, sample_off, cur_count, sample_left; - long cur_off = *offp; - char *dest = buf; - int count = 0; - char *src; - - if (!access_ok(VERIFY_WRITE, buf, size)) - return -EFAULT; - - mutex_lock(&sbp.lock); - - count = 0; - cur_sample = cur_off / TB_SAMPLE_SIZE; - sample_off = cur_off % TB_SAMPLE_SIZE; - sample_left = TB_SAMPLE_SIZE - sample_off; - - while (size && (cur_sample < sbp.next_tb_sample)) { - int err; - - cur_count = size < sample_left ? size : sample_left; - src = (char *)(((long)sbp.sbprof_tbbuf[cur_sample])+sample_off); - err = __copy_to_user(dest, src, cur_count); - if (err) { - *offp = cur_off + cur_count - err; - mutex_unlock(&sbp.lock); - return err; - } - - pr_debug(DEVNAME ": read from sample %d, %d bytes\n", - cur_sample, cur_count); - size -= cur_count; - sample_left -= cur_count; - if (!sample_left) { - cur_sample++; - sample_off = 0; - sample_left = TB_SAMPLE_SIZE; - } else { - sample_off += cur_count; - } - cur_off += cur_count; - dest += cur_count; - count += cur_count; - } - - *offp = cur_off; - mutex_unlock(&sbp.lock); - - return count; -} - -static long sbprof_tb_ioctl(struct file *filp, unsigned int command, - unsigned long arg) -{ - int error = 0; - - switch (command) { - case SBPROF_ZBSTART: - mutex_lock(&sbp.lock); - error = sbprof_zbprof_start(filp); - mutex_unlock(&sbp.lock); - break; - - case SBPROF_ZBSTOP: - mutex_lock(&sbp.lock); - error = sbprof_zbprof_stop(); - mutex_unlock(&sbp.lock); - break; - - case SBPROF_ZBWAITFULL: - error = wait_event_interruptible(sbp.tb_read, TB_FULL); - if (error) - break; - - error = put_user(TB_FULL, (int *) arg); - break; - - default: - error = -EINVAL; - break; - } - - return error; -} - -static const struct file_operations sbprof_tb_fops = { - .owner = THIS_MODULE, - .open = sbprof_tb_open, - .release = sbprof_tb_release, - .read = sbprof_tb_read, - .unlocked_ioctl = sbprof_tb_ioctl, - .compat_ioctl = sbprof_tb_ioctl, - .mmap = NULL, -}; - -static struct class *tb_class; -static struct device *tb_dev; - -static int __init sbprof_tb_init(void) -{ - struct device *dev; - struct class *tbc; - int err; - - if (register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) { - printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n", - SBPROF_TB_MAJOR); - return -EIO; - } - - tbc = class_create(THIS_MODULE, "sb_tracebuffer"); - if (IS_ERR(tbc)) { - err = PTR_ERR(tbc); - goto out_chrdev; - } - - tb_class = tbc; - - dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), "tb"); - if (IS_ERR(dev)) { - err = PTR_ERR(dev); - goto out_class; - } - tb_dev = dev; - - sbp.open = 0; - tb_period = zbbus_mhz * 10000LL; - pr_info(DEVNAME ": initialized - tb_period = %lld\n", tb_period); - - return 0; - -out_class: - class_destroy(tb_class); -out_chrdev: - unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME); - - return err; -} - -static void __exit sbprof_tb_cleanup(void) -{ - device_destroy(tb_class, MKDEV(SBPROF_TB_MAJOR, 0)); - unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME); - class_destroy(tb_class); -} - -module_init(sbprof_tb_init); -module_exit(sbprof_tb_cleanup); - -MODULE_ALIAS_CHARDEV_MAJOR(SBPROF_TB_MAJOR); -MODULE_AUTHOR("Ralf Baechle "); -MODULE_LICENSE("GPL"); diff --git a/arch/mips/sni/pcimt.c b/arch/mips/sni/pcimt.c index 8e8593b..9ee208d 100644 --- a/arch/mips/sni/pcimt.c +++ b/arch/mips/sni/pcimt.c @@ -91,7 +91,7 @@ static struct platform_device pcimt_seri }; static struct resource sni_io_resource = { - .start = 0x00001000UL, + .start = 0x00000000UL, .end = 0x03bfffffUL, .name = "PCIMT IO MEM", .flags = IORESOURCE_IO, @@ -132,107 +132,19 @@ static struct resource pcimt_io_resource }; static struct resource sni_mem_resource = { - .start = 0x10000000UL, - .end = 0xffffffffUL, + .start = 0x18000000UL, + .end = 0x1fbfffffUL, .name = "PCIMT PCI MEM", .flags = IORESOURCE_MEM }; -/* - * The RM200/RM300 has a few holes in it's PCI/EISA memory address space used - * for other purposes. Be paranoid and allocate all of the before the PCI - * code gets a chance to to map anything else there ... - * - * This leaves the following areas available: - * - * 0x10000000 - 0x1009ffff (640kB) PCI/EISA/ISA Bus Memory - * 0x10100000 - 0x13ffffff ( 15MB) PCI/EISA/ISA Bus Memory - * 0x18000000 - 0x1fbfffff (124MB) PCI/EISA Bus Memory - * 0x1ff08000 - 0x1ffeffff (816kB) PCI/EISA Bus Memory - * 0xa0000000 - 0xffffffff (1.5GB) PCI/EISA Bus Memory - */ -static struct resource pcimt_mem_resources[] = { - { - .start = 0x100a0000, - .end = 0x100bffff, - .name = "Video RAM area", - .flags = IORESOURCE_BUSY - }, { - .start = 0x100c0000, - .end = 0x100fffff, - .name = "ISA Reserved", - .flags = IORESOURCE_BUSY - }, { - .start = 0x14000000, - .end = 0x17bfffff, - .name = "PCI IO", - .flags = IORESOURCE_BUSY - }, { - .start = 0x17c00000, - .end = 0x17ffffff, - .name = "Cache Replacement Area", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1a000000, - .end = 0x1a000003, - .name = "PCI INT Acknowledge", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fc00000, - .end = 0x1fc7ffff, - .name = "Boot PROM", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fc80000, - .end = 0x1fcfffff, - .name = "Diag PROM", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fd00000, - .end = 0x1fdfffff, - .name = "X-Bus", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fe00000, - .end = 0x1fefffff, - .name = "BIOS map", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1ff00000, - .end = 0x1ff7ffff, - .name = "NVRAM / EEPROM", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fff0000, - .end = 0x1fffefff, - .name = "ASIC PCI", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1ffff000, - .end = 0x1fffffff, - .name = "MP Agent", - .flags = IORESOURCE_BUSY - }, { - .start = 0x20000000, - .end = 0x9fffffff, - .name = "Main Memory", - .flags = IORESOURCE_BUSY - } -}; - static void __init sni_pcimt_resource_init(void) { int i; /* request I/O space for devices used on all i[345]86 PCs */ for (i = 0; i < ARRAY_SIZE(pcimt_io_resources); i++) - request_resource(&ioport_resource, pcimt_io_resources + i); - - /* request mem space for pcimt-specific devices */ - for (i = 0; i < ARRAY_SIZE(pcimt_mem_resources); i++) - request_resource(&sni_mem_resource, pcimt_mem_resources + i); - - ioport_resource.end = sni_io_resource.end; + request_resource(&sni_io_resource, pcimt_io_resources + i); } extern struct pci_ops sni_pcimt_ops; @@ -240,9 +152,10 @@ extern struct pci_ops sni_pcimt_ops; static struct pci_controller sni_controller = { .pci_ops = &sni_pcimt_ops, .mem_resource = &sni_mem_resource, - .mem_offset = 0x10000000UL, + .mem_offset = 0x00000000UL, .io_resource = &sni_io_resource, - .io_offset = 0x00000000UL + .io_offset = 0x00000000UL, + .io_map_base = SNI_PORT_BASE }; static void enable_pcimt_irq(unsigned int irq) @@ -363,15 +276,17 @@ void __init sni_pcimt_irq_init(void) void sni_pcimt_init(void) { - sni_pcimt_resource_init(); sni_pcimt_detect(); sni_pcimt_sc_init(); rtc_mips_get_time = mc146818_get_cmos_time; rtc_mips_set_time = mc146818_set_rtc_mmss; board_time_init = sni_cpu_time_init; + ioport_resource.end = sni_io_resource.end; #ifdef CONFIG_PCI + PCIBIOS_MIN_IO = 0x9000; register_pci_controller(&sni_controller); #endif + sni_pcimt_resource_init(); } static int __init snirm_pcimt_setup_devinit(void) diff --git a/arch/mips/sni/pcit.c b/arch/mips/sni/pcit.c index 1dfc3f0..00d151f 100644 --- a/arch/mips/sni/pcit.c +++ b/arch/mips/sni/pcit.c @@ -43,7 +43,7 @@ static struct platform_device pcit_seria }; static struct plat_serial8250_port pcit_cplus_data[] = { - PORT(0x3f8, 4), + PORT(0x3f8, 0), PORT(0x2f8, 3), PORT(0x3e8, 4), PORT(0x2e8, 3), @@ -59,9 +59,9 @@ static struct platform_device pcit_cplus }; static struct resource sni_io_resource = { - .start = 0x00001000UL, + .start = 0x00000000UL, .end = 0x03bfffffUL, - .name = "PCIT IO MEM", + .name = "PCIT IO", .flags = IORESOURCE_IO, }; @@ -92,6 +92,11 @@ static struct resource pcit_io_resources .name = "dma2", .flags = IORESOURCE_BUSY }, { + .start = 0xcf8, + .end = 0xcfb, + .name = "PCI config addr", + .flags = IORESOURCE_BUSY + }, { .start = 0xcfc, .end = 0xcff, .name = "PCI config data", @@ -100,107 +105,19 @@ static struct resource pcit_io_resources }; static struct resource sni_mem_resource = { - .start = 0x10000000UL, - .end = 0xffffffffUL, + .start = 0x18000000UL, + .end = 0x1fbfffffUL, .name = "PCIT PCI MEM", .flags = IORESOURCE_MEM }; -/* - * The RM200/RM300 has a few holes in it's PCI/EISA memory address space used - * for other purposes. Be paranoid and allocate all of the before the PCI - * code gets a chance to to map anything else there ... - * - * This leaves the following areas available: - * - * 0x10000000 - 0x1009ffff (640kB) PCI/EISA/ISA Bus Memory - * 0x10100000 - 0x13ffffff ( 15MB) PCI/EISA/ISA Bus Memory - * 0x18000000 - 0x1fbfffff (124MB) PCI/EISA Bus Memory - * 0x1ff08000 - 0x1ffeffff (816kB) PCI/EISA Bus Memory - * 0xa0000000 - 0xffffffff (1.5GB) PCI/EISA Bus Memory - */ -static struct resource pcit_mem_resources[] = { - { - .start = 0x14000000, - .end = 0x17bfffff, - .name = "PCI IO", - .flags = IORESOURCE_BUSY - }, { - .start = 0x17c00000, - .end = 0x17ffffff, - .name = "Cache Replacement Area", - .flags = IORESOURCE_BUSY - }, { - .start = 0x180a0000, - .end = 0x180bffff, - .name = "Video RAM area", - .flags = IORESOURCE_BUSY - }, { - .start = 0x180c0000, - .end = 0x180fffff, - .name = "ISA Reserved", - .flags = IORESOURCE_BUSY - }, { - .start = 0x19000000, - .end = 0x1fbfffff, - .name = "PCI MEM", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fc00000, - .end = 0x1fc7ffff, - .name = "Boot PROM", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fc80000, - .end = 0x1fcfffff, - .name = "Diag PROM", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fd00000, - .end = 0x1fdfffff, - .name = "X-Bus", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fe00000, - .end = 0x1fefffff, - .name = "BIOS map", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1ff00000, - .end = 0x1ff7ffff, - .name = "NVRAM / EEPROM", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1fff0000, - .end = 0x1fffefff, - .name = "MAUI ASIC", - .flags = IORESOURCE_BUSY - }, { - .start = 0x1ffff000, - .end = 0x1fffffff, - .name = "MP Agent", - .flags = IORESOURCE_BUSY - }, { - .start = 0x20000000, - .end = 0x9fffffff, - .name = "Main Memory", - .flags = IORESOURCE_BUSY - } -}; - static void __init sni_pcit_resource_init(void) { int i; /* request I/O space for devices used on all i[345]86 PCs */ for (i = 0; i < ARRAY_SIZE(pcit_io_resources); i++) - request_resource(&ioport_resource, pcit_io_resources + i); - - /* request mem space for pcimt-specific devices */ - for (i = 0; i < ARRAY_SIZE(pcit_mem_resources); i++) - request_resource(&sni_mem_resource, pcit_mem_resources + i); - - ioport_resource.end = sni_io_resource.end; + request_resource(&sni_io_resource, pcit_io_resources + i); } @@ -209,9 +126,10 @@ extern struct pci_ops sni_pcit_ops; static struct pci_controller sni_pcit_controller = { .pci_ops = &sni_pcit_ops, .mem_resource = &sni_mem_resource, - .mem_offset = 0x10000000UL, + .mem_offset = 0x00000000UL, .io_resource = &sni_io_resource, - .io_offset = 0x00000000UL + .io_offset = 0x00000000UL, + .io_map_base = SNI_PORT_BASE }; static void enable_pcit_irq(unsigned int irq) @@ -262,7 +180,7 @@ static void pcit_hwint0(void) int irq; clear_c0_status(IE_IRQ0); - irq = ffs((pending >> 16) & 0x7f); + irq = ffs((pending >> 16) & 0x3f); if (likely(irq > 0)) do_IRQ (irq + SNI_PCIT_INT_START - 1); @@ -289,6 +207,8 @@ static void sni_pcit_hwint_cplus(void) if (pending & C_IRQ0) pcit_hwint0(); + else if (pending & C_IRQ1) + do_IRQ (MIPS_CPU_IRQ_BASE + 3); else if (pending & C_IRQ2) do_IRQ (MIPS_CPU_IRQ_BASE + 4); else if (pending & C_IRQ3) @@ -317,21 +237,23 @@ void __init sni_pcit_cplus_irq_init(void mips_cpu_irq_init(); for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++) set_irq_chip(i, &pcit_irq_type); - *(volatile u32 *)SNI_PCIT_INT_REG = 0; + *(volatile u32 *)SNI_PCIT_INT_REG = 0x40000000; sni_hwint = sni_pcit_hwint_cplus; change_c0_status(ST0_IM, IE_IRQ0); - setup_irq (SNI_PCIT_INT_START + 6, &sni_isa_irq); + setup_irq (MIPS_CPU_IRQ_BASE + 3, &sni_isa_irq); } void sni_pcit_init(void) { - sni_pcit_resource_init(); rtc_mips_get_time = mc146818_get_cmos_time; rtc_mips_set_time = mc146818_set_rtc_mmss; board_time_init = sni_cpu_time_init; + ioport_resource.end = sni_io_resource.end; #ifdef CONFIG_PCI + PCIBIOS_MIN_IO = 0x9000; register_pci_controller(&sni_pcit_controller); #endif + sni_pcit_resource_init(); } static int __init snirm_pcit_setup_devinit(void) diff --git a/arch/mips/vr41xx/Kconfig b/arch/mips/vr41xx/Kconfig index 92f41f6..8f4d3e7 100644 --- a/arch/mips/vr41xx/Kconfig +++ b/arch/mips/vr41xx/Kconfig @@ -1,6 +1,10 @@ -config CASIO_E55 - bool "Support for CASIO CASSIOPEIA E-10/15/55/65" +choice + prompt "Machine type" depends on MACH_VR41XX + default TANBAC_TB022X + +config CASIO_E55 + bool "CASIO CASSIOPEIA E-10/15/55/65" select DMA_NONCOHERENT select IRQ_CPU select ISA @@ -8,8 +12,7 @@ config CASIO_E55 select SYS_SUPPORTS_LITTLE_ENDIAN config IBM_WORKPAD - bool "Support for IBM WorkPad z50" - depends on MACH_VR41XX + bool "IBM WorkPad z50" select DMA_NONCOHERENT select IRQ_CPU select ISA @@ -17,26 +20,18 @@ config IBM_WORKPAD select SYS_SUPPORTS_LITTLE_ENDIAN config NEC_CMBVR4133 - bool "Support for NEC CMB-VR4133" - depends on MACH_VR41XX + bool "NEC CMB-VR4133" select DMA_NONCOHERENT select IRQ_CPU select HW_HAS_PCI select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_LITTLE_ENDIAN -config ROCKHOPPER - bool "Support for Rockhopper baseboard" - depends on NEC_CMBVR4133 - select I8259 - select HAVE_STD_PC_SERIAL_PORT - config TANBAC_TB022X - bool "Support for TANBAC VR4131 multichip module and TANBAC VR4131DIMM" - depends on MACH_VR41XX + bool "TANBAC VR4131 multichip module and TANBAC VR4131DIMM" select DMA_NONCOHERENT - select HW_HAS_PCI select IRQ_CPU + select HW_HAS_PCI select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_LITTLE_ENDIAN help @@ -46,40 +41,65 @@ config TANBAC_TB022X Please refer to about VR4131 multichip module and VR4131DIMM. -config TANBAC_TB0226 - bool "Support for TANBAC Mbase(TB0226)" +config VICTOR_MPC30X + bool "Victor MP-C303/304" + select DMA_NONCOHERENT + select IRQ_CPU + select HW_HAS_PCI + select PCI_VR41XX + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + +config ZAO_CAPCELLA + bool "ZAO Networks Capcella" + select DMA_NONCOHERENT + select IRQ_CPU + select HW_HAS_PCI + select PCI_VR41XX + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + +endchoice + +config ROCKHOPPER + bool "Support for Rockhopper base board" + depends on NEC_CMBVR4133 + select PCI_VR41XX + select I8259 + select HAVE_STD_PC_SERIAL_PORT + +choice + prompt "Base board type" depends on TANBAC_TB022X + default TANBAC_TB0287 + +config TANBAC_TB0219 + bool "TANBAC DIMM Evaluation Kit(TB0219)" select GPIO_VR41XX + select PCI_VR41XX + help + The TANBAC DIMM Evaluation Kit(TB0219) is a MIPS-based platform + manufactured by TANBAC. + Please refer to about DIMM Evaluation Kit. + +config TANBAC_TB0226 + bool "TANBAC Mbase(TB0226)" + select GPIO_VR41XX + select PCI_VR41XX help The TANBAC Mbase(TB0226) is a MIPS-based platform manufactured by TANBAC. Please refer to about Mbase. config TANBAC_TB0287 - bool "Support for TANBAC Mini-ITX DIMM base(TB0287)" - depends on TANBAC_TB022X + bool "TANBAC Mini-ITX DIMM base(TB0287)" + select PCI_VR41XX help The TANBAC Mini-ITX DIMM base(TB0287) is a MIPS-based platform manufactured by TANBAC. Please refer to about Mini-ITX DIMM base. -config VICTOR_MPC30X - bool "Support for Victor MP-C303/304" - depends on MACH_VR41XX - select DMA_NONCOHERENT - select HW_HAS_PCI - select IRQ_CPU - select SYS_SUPPORTS_32BIT_KERNEL - select SYS_SUPPORTS_LITTLE_ENDIAN - -config ZAO_CAPCELLA - bool "Support for ZAO Networks Capcella" - depends on MACH_VR41XX - select DMA_NONCOHERENT - select HW_HAS_PCI - select IRQ_CPU - select SYS_SUPPORTS_32BIT_KERNEL - select SYS_SUPPORTS_LITTLE_ENDIAN +endchoice config PCI_VR41XX bool "Add PCI control unit support of NEC VR4100 series" diff --git a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig index 782906b..eb2f9a3 100644 --- a/arch/parisc/configs/c3000_defconfig +++ b/arch/parisc/configs/c3000_defconfig @@ -435,7 +435,6 @@ # CONFIG_SCSI_SATA_SIS is not set # CONFIG_SCSI_SATA_ULI is not set CONFIG_SCSI_SATA_VIA=m # CONFIG_SCSI_SATA_VITESSE is not set -CONFIG_SCSI_SATA_INTEL_COMBINED=y # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_IPS is not set diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 6dfbd52..a54a9a2 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -11,6 +11,11 @@ config PPC64 This option selects whether a 32-bit or a 64-bit kernel will be built. +config PPC_PM_NEEDS_RTC_LIB + bool + select RTC_LIB + default y if PM + config PPC32 bool default y if !PPC64 @@ -89,7 +94,7 @@ config SCHED_NO_NO_OMIT_FRAME_POINTER config ARCH_MAY_HAVE_PC_FDC bool - default y + default !PPC_PSERIES || PCI config PPC_OF def_bool y @@ -157,17 +162,20 @@ config PPC_83xx select FSL_SOC select 83xx select PPC_FPU + select WANT_DEVICE_TREE config PPC_85xx bool "Freescale 85xx" select E500 select FSL_SOC select 85xx + select WANT_DEVICE_TREE config PPC_86xx bool "Freescale 86xx" select 6xx select FSL_SOC + select FSL_PCIE select PPC_FPU select ALTIVEC help @@ -186,7 +194,6 @@ config 44x bool "AMCC 44x" select PPC_DCR_NATIVE - config E200 bool "Freescale e200" @@ -367,394 +374,7 @@ endmenu source "init/Kconfig" -menu "Platform support" - depends on PPC64 || CLASSIC32 - -choice - prompt "Machine type" - default PPC_MULTIPLATFORM - -config PPC_MULTIPLATFORM - bool "Generic desktop/server/laptop" - help - Select this option if configuring for an IBM pSeries or - RS/6000 machine, an Apple machine, or a PReP, CHRP, - Maple or Cell-based machine. - -config EMBEDDED6xx - bool "Embedded 6xx/7xx/7xxx-based board" - depends on PPC32 && (BROKEN||BROKEN_ON_SMP) - -config APUS - bool "Amiga-APUS" - depends on PPC32 && BROKEN - help - Select APUS if configuring for a PowerUP Amiga. - More information is available at: - . -endchoice - -config QUICC_ENGINE - bool - depends on PPC_MPC836x || PPC_MPC832x - default y - help - The QUICC Engine (QE) is a new generation of communications - coprocessors on Freescale embedded CPUs (akin to CPM in older chips). - Selecting this option means that you wish to build a kernel - for a machine with a QE coprocessor. - -config PPC_PSERIES - depends on PPC_MULTIPLATFORM && PPC64 - bool "IBM pSeries & new (POWER5-based) iSeries" - select MPIC - select PPC_I8259 - select PPC_RTAS - select RTAS_ERROR_LOGGING - select PPC_UDBG_16550 - select PPC_NATIVE - default y - -config PPC_ISERIES - bool "IBM Legacy iSeries" - depends on PPC_MULTIPLATFORM && PPC64 - select PPC_INDIRECT_IO - -config PPC_CHRP - bool "Common Hardware Reference Platform (CHRP) based machines" - depends on PPC_MULTIPLATFORM && PPC32 - select MPIC - select PPC_I8259 - select PPC_INDIRECT_PCI - select PPC_RTAS - select PPC_MPC106 - select PPC_UDBG_16550 - select PPC_NATIVE - default y - -config PPC_MPC52xx - bool - default n - -config PPC_MPC5200 - bool - select PPC_MPC52xx - default n - -config PPC_MPC5200_BUGFIX - bool "MPC5200 (L25R) bugfix support" - depends on PPC_MPC5200 - default n - help - Enable workarounds for original MPC5200 errata. This is not required - for MPC5200B based boards. - - It is safe to say 'Y' here - -config PPC_EFIKA - bool "bPlan Efika 5k2. MPC5200B based computer" - depends on PPC_MULTIPLATFORM && PPC32 - select PPC_RTAS - select RTAS_PROC - select PPC_MPC52xx - select PPC_NATIVE - default n - -config PPC_LITE5200 - bool "Freescale Lite5200 Eval Board" - depends on PPC_MULTIPLATFORM && PPC32 - select PPC_MPC5200 - default n - -config PPC_PMAC - bool "Apple PowerMac based machines" - depends on PPC_MULTIPLATFORM - select MPIC - select PPC_INDIRECT_PCI if PPC32 - select PPC_MPC106 if PPC32 - select PPC_NATIVE - default y - -config PPC_PMAC64 - bool - depends on PPC_PMAC && POWER4 - select MPIC - select U3_DART - select MPIC_BROKEN_U3 - select GENERIC_TBSYNC - select PPC_970_NAP - default y - -config PPC_PREP - bool "PowerPC Reference Platform (PReP) based machines" - depends on PPC_MULTIPLATFORM && PPC32 && BROKEN - select MPIC - select PPC_I8259 - select PPC_INDIRECT_PCI - select PPC_UDBG_16550 - select PPC_NATIVE - default n - -config PPC_MAPLE - depends on PPC_MULTIPLATFORM && PPC64 - bool "Maple 970FX Evaluation Board" - select MPIC - select U3_DART - select MPIC_BROKEN_U3 - select GENERIC_TBSYNC - select PPC_UDBG_16550 - select PPC_970_NAP - select PPC_NATIVE - select PPC_RTAS - select MMIO_NVRAM - select ATA_NONSTANDARD if ATA - default n - help - This option enables support for the Maple 970FX Evaluation Board. - For more information, refer to - -config PPC_PASEMI - depends on PPC_MULTIPLATFORM && PPC64 - bool "PA Semi SoC-based platforms" - default n - select MPIC - select PPC_UDBG_16550 - select GENERIC_TBSYNC - select PPC_NATIVE - help - This option enables support for PA Semi's PWRficient line - of SoC processors, including PA6T-1682M - -config PPC_CELL - bool - default n - -config PPC_CELL_NATIVE - bool - select PPC_CELL - select PPC_DCR_MMIO - select PPC_OF_PLATFORM_PCI - select PPC_INDIRECT_IO - select PPC_NATIVE - select MPIC - default n - -config PPC_IBM_CELL_BLADE - bool "IBM Cell Blade" - depends on PPC_MULTIPLATFORM && PPC64 - select PPC_CELL_NATIVE - select PPC_RTAS - select MMIO_NVRAM - select PPC_UDBG_16550 - select UDBG_RTAS_CONSOLE - -config PPC_PS3 - bool "Sony PS3 (incomplete)" - depends on PPC_MULTIPLATFORM && PPC64 - select PPC_CELL - select USB_ARCH_HAS_OHCI - select USB_OHCI_LITTLE_ENDIAN - select USB_OHCI_BIG_ENDIAN_MMIO - select USB_ARCH_HAS_EHCI - select USB_EHCI_BIG_ENDIAN_MMIO - help - This option enables support for the Sony PS3 game console - and other platforms using the PS3 hypervisor. - Support for this platform is not yet complete, so - enabling this will not result in a bootable kernel on a - PS3 system. - -config PPC_CELLEB - bool "Toshiba's Cell Reference Set 'Celleb' Architecture" - depends on PPC_MULTIPLATFORM && PPC64 - select PPC_CELL - select PPC_OF_PLATFORM_PCI - select HAS_TXX9_SERIAL - select PPC_UDBG_BEAT - select USB_OHCI_BIG_ENDIAN_MMIO - select USB_EHCI_BIG_ENDIAN_MMIO - -config PPC_NATIVE - bool - depends on PPC_MULTIPLATFORM - help - Support for running natively on the hardware, i.e. without - a hypervisor. This option is not user-selectable but should - be selected by all platforms that need it. - -config UDBG_RTAS_CONSOLE - bool "RTAS based debug console" - depends on PPC_RTAS - default n - -config PPC_UDBG_BEAT - bool "BEAT based debug console" - depends on PPC_CELLEB - default n - -config XICS - depends on PPC_PSERIES - bool - default y - -config U3_DART - bool - depends on PPC_MULTIPLATFORM && PPC64 - default n - -config PPC_RTAS - bool - default n - -config RTAS_ERROR_LOGGING - bool - depends on PPC_RTAS - default n - -config RTAS_PROC - bool "Proc interface to RTAS" - depends on PPC_RTAS - default y - -config RTAS_FLASH - tristate "Firmware flash interface" - depends on PPC64 && RTAS_PROC - -config PPC_PMI - tristate "Support for PMI" - depends PPC_IBM_CELL_BLADE - help - PMI (Platform Management Interrupt) is a way to - communicate with the BMC (Baseboard Mangement Controller). - It is used in some IBM Cell blades. - default m - -config MMIO_NVRAM - bool - default n - -config MPIC_BROKEN_U3 - bool - depends on PPC_MAPLE - default y - -config IBMVIO - depends on PPC_PSERIES || PPC_ISERIES - bool - default y - -config IBMEBUS - depends on PPC_PSERIES - bool "Support for GX bus based adapters" - help - Bus device driver for GX bus based adapters. - -config PPC_MPC106 - bool - default n - -config PPC_970_NAP - bool - default n - -config PPC_INDIRECT_IO - bool - select GENERIC_IOMAP - default n - -config GENERIC_IOMAP - bool - default n - -source "drivers/cpufreq/Kconfig" - -config CPU_FREQ_PMAC - bool "Support for Apple PowerBooks" - depends on CPU_FREQ && ADB_PMU && PPC32 - select CPU_FREQ_TABLE - help - This adds support for frequency switching on Apple PowerBooks, - this currently includes some models of iBook & Titanium - PowerBook. - -config CPU_FREQ_PMAC64 - bool "Support for some Apple G5s" - depends on CPU_FREQ && PPC64 - select CPU_FREQ_TABLE - help - This adds support for frequency switching on Apple iMac G5, - and some of the more recent desktop G5 machines as well. - -config PPC601_SYNC_FIX - bool "Workarounds for PPC601 bugs" - depends on 6xx && (PPC_PREP || PPC_PMAC) - help - Some versions of the PPC601 (the first PowerPC chip) have bugs which - mean that extra synchronization instructions are required near - certain instructions, typically those that make major changes to the - CPU state. These extra instructions reduce performance slightly. - If you say N here, these extra instructions will not be included, - resulting in a kernel which will run faster but may not run at all - on some systems with the PPC601 chip. - - If in doubt, say Y here. - -config TAU - bool "On-chip CPU temperature sensor support" - depends on 6xx - help - G3 and G4 processors have an on-chip temperature sensor called the - 'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die - temperature within 2-4 degrees Celsius. This option shows the current - on-die temperature in /proc/cpuinfo if the cpu supports it. - - Unfortunately, on some chip revisions, this sensor is very inaccurate - and in many cases, does not work at all, so don't assume the cpu - temp is actually what /proc/cpuinfo says it is. - -config TAU_INT - bool "Interrupt driven TAU driver (DANGEROUS)" - depends on TAU - ---help--- - The TAU supports an interrupt driven mode which causes an interrupt - whenever the temperature goes out of range. This is the fastest way - to get notified the temp has exceeded a range. With this option off, - a timer is used to re-check the temperature periodically. - - However, on some cpus it appears that the TAU interrupt hardware - is buggy and can cause a situation which would lead unexplained hard - lockups. - - Unless you are extending the TAU driver, or enjoy kernel/hardware - debugging, leave this option off. - -config TAU_AVERAGE - bool "Average high and low temp" - depends on TAU - ---help--- - The TAU hardware can compare the temperature to an upper and lower - bound. The default behavior is to show both the upper and lower - bound in /proc/cpuinfo. If the range is large, the temperature is - either changing a lot, or the TAU hardware is broken (likely on some - G4's). If the range is small (around 4 degrees), the temperature is - relatively stable. If you say Y here, a single temperature value, - halfway between the upper and lower bounds, will be reported in - /proc/cpuinfo. - - If in doubt, say N here. - -endmenu - -source arch/powerpc/platforms/embedded6xx/Kconfig -source arch/powerpc/platforms/4xx/Kconfig -source arch/powerpc/platforms/82xx/Kconfig -source arch/powerpc/platforms/83xx/Kconfig -source arch/powerpc/platforms/85xx/Kconfig -source arch/powerpc/platforms/86xx/Kconfig -source arch/powerpc/platforms/8xx/Kconfig -source arch/powerpc/platforms/cell/Kconfig -source arch/powerpc/platforms/ps3/Kconfig -source arch/powerpc/platforms/pasemi/Kconfig +source "arch/powerpc/platforms/Kconfig" menu "Kernel options" @@ -837,15 +457,6 @@ config CRASH_DUMP Don't change this unless you know what you are doing. -config EMBEDDEDBOOT - bool - depends on 8xx || 8260 - default y - -config PC_KEYBOARD - bool "PC PS/2 style Keyboard" - depends on 4xx || CPM2 - config PPCBUG_NVRAM bool "Enable reading PPCBUG NVRAM during boot" if PPLUS || LOPEC default y if PPC_PREP @@ -859,8 +470,6 @@ config IRQ_ALL_CPUS CPU. Generally saying Y is safe, although some problems have been reported with SMP Power Macintoshes with this option enabled. -source "arch/powerpc/platforms/pseries/Kconfig" - config NUMA bool "NUMA support" depends on PPC64 @@ -910,10 +519,10 @@ config PPC_64K_PAGES depends on PPC64 help This option changes the kernel logical page size to 64k. On machines - without processor support for 64k pages, the kernel will simulate - them by loading each individual 4k page on demand transparently, - while on hardware with such support, it will be used to map - normal application pages. + without processor support for 64k pages, the kernel will simulate + them by loading each individual 4k page on demand transparently, + while on hardware with such support, it will be used to map + normal application pages. config SCHED_SMT bool "SMT (Hyperthreading) scheduler support" @@ -931,8 +540,6 @@ config PROC_DEVICETREE an image of the device tree that the kernel copies from Open Firmware or other boot firmware. If unsure, say Y here. -source "arch/powerpc/platforms/prep/Kconfig" - config CMDLINE_BOOL bool "Default bootloader kernel arguments" @@ -967,6 +574,29 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +config WANT_DEVICE_TREE + bool + default n + +config DEVICE_TREE + string "Static device tree source file" + depends on WANT_DEVICE_TREE + help + This specifies the device tree source (.dts) file to be + compiled and included when building the bootwrapper. If a + relative filename is given, then it will be relative to + arch/powerpc/boot/dts. If you are not using the bootwrapper, + or do not need to build a dts into the bootwrapper, this + field is ignored. + + For example, this is required when building a cuImage target + for an older U-Boot, which cannot pass a device tree itself. + Such a kernel will not work with a newer U-Boot that tries to + pass a device tree (unless you tell it not to). If your U-Boot + does not mention a device tree in "help bootm", then use the + cuImage target and specify a device tree here. Otherwise, use + the uImage target and leave this field blank. + endmenu config ISA_DMA_API @@ -995,24 +625,17 @@ config GENERIC_ISA_DMA depends on PPC64 || POWER4 || 6xx && !CPM2 default y -config MPIC - bool - default n - -config MPIC_WEIRD - bool - default n - -config PPC_I8259 - bool - default n - config PPC_INDIRECT_PCI bool depends on PCI default y if 40x || 44x default n +config PPC_INDIRECT_PCI_BE + bool + depends PPC_INDIRECT_PCI + default n + config EISA bool @@ -1022,13 +645,18 @@ config SBUS config FSL_SOC bool +config FSL_PCIE + bool + depends on PPC_86xx + # Yes MCA RS/6000s exist but Linux-PPC does not currently support any config MCA bool config PCI bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \ - || PPC_MPC52xx || (EMBEDDED && PPC_ISERIES) || MPC7448HPC2 || PPC_PS3 + || PPC_MPC52xx || (EMBEDDED && (PPC_PSERIES || PPC_ISERIES)) \ + || MPC7448HPC2 || PPC_PS3 default y if !40x && !CPM2 && !8xx && !APUS && !PPC_83xx \ && !PPC_85xx && !PPC_86xx default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS @@ -1228,12 +856,10 @@ # XXX source "arch/ppc/8260_io/Kconfig" source "arch/powerpc/sysdev/qe_lib/Kconfig" -source "arch/powerpc/platforms/iseries/Kconfig" - source "lib/Kconfig" menu "Instrumentation Support" - depends on EXPERIMENTAL + depends on EXPERIMENTAL source "arch/powerpc/oprofile/Kconfig" diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index d39d133..86aa374 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -18,6 +18,15 @@ config DEBUG_STACK_USAGE This option will slow down process creation somewhat. +config DEBUG_PAGEALLOC + bool "Debug page memory allocations" + depends on DEBUG_KERNEL && !SOFTWARE_SUSPEND + help + Unmap pages from the kernel linear mapping after free_pages(). + This results in a large slowdown, but helps to find certain types + of memory corruptions. + + config HCALL_STATS bool "Hypervisor call instrumentation" depends on PPC_PSERIES && DEBUG_FS @@ -132,8 +141,7 @@ config BOOTX_TEXT config SERIAL_TEXT_DEBUG bool "Support for early boot texts over serial port" - depends on 4xx || LOPEC || MV64X60 || PPLUS || PRPMC800 || \ - PPC_GEN550 || PPC_MPC52xx + depends on 4xx config PPC_EARLY_DEBUG bool "Early debugging (dangerous)" diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index a00fe72..7949920 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -102,9 +102,9 @@ # Enable unit-at-a-time mode when possib # kernel considerably. CFLAGS += $(call cc-option,-funit-at-a-time) -ifndef CONFIG_FSL_BOOKE -CFLAGS += -mstring -endif +# Never use string load/store instructions as they are +# often slow when they are implemented at all +CFLAGS += -mno-string ifeq ($(CONFIG_6xx),y) CFLAGS += -mcpu=powerpc @@ -148,7 +148,7 @@ all: $(KBUILD_IMAGE) CPPFLAGS_vmlinux.lds := -Upowerpc -BOOT_TARGETS = zImage zImage.initrd uImage +BOOT_TARGETS = zImage zImage.initrd uImage cuImage PHONY += $(BOOT_TARGETS) @@ -166,6 +166,9 @@ define archhelp @echo ' *_defconfig - Select default config from arch/$(ARCH)/configs' endef +install: + $(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install + archclean: $(Q)$(MAKE) $(clean)=$(boot) diff --git a/arch/powerpc/boot/.gitignore b/arch/powerpc/boot/.gitignore index 0734b2f..eec7af7 100644 --- a/arch/powerpc/boot/.gitignore +++ b/arch/powerpc/boot/.gitignore @@ -18,6 +18,9 @@ kernel-vmlinux.strip.c kernel-vmlinux.strip.gz mktree uImage +cuImage +cuImage.bin.gz +cuImage.elf zImage zImage.chrp zImage.coff diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index dc77940..3716594 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -40,10 +40,11 @@ zliblinuxheader := zlib.h zconf.h zutil. $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ $(addprefix $(obj)/,$(zlibheader)) -src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ - ns16550.c serial.c simple_alloc.c div64.S util.S $(zlib) -src-plat := of.c -src-boot := crt0.S $(src-wlib) $(src-plat) empty.c +src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ + ns16550.c serial.c simple_alloc.c div64.S util.S \ + gunzip_util.c elf_util.c $(zlib) devtree.c +src-plat := of.c cuboot-83xx.c cuboot-85xx.c +src-boot := $(src-wlib) $(src-plat) empty.c src-boot := $(addprefix $(obj)/, $(src-boot)) obj-boot := $(addsuffix .o, $(basename $(src-boot))) @@ -75,7 +76,7 @@ quiet_cmd_copy_zliblinuxheader = COPY @cp $< $@ clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ - empty.c zImage zImage.coff.lds zImage.lds zImage.sandpoint + empty.c zImage.coff.lds zImage.lds quiet_cmd_bootcc = BOOTCC $@ cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< @@ -84,23 +85,25 @@ quiet_cmd_bootas = BOOTAS $@ cmd_bootas = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTAFLAGS) -c -o $@ $< quiet_cmd_bootar = BOOTAR $@ - cmd_bootar = $(CROSS32AR) -cr $@.$$$$ $^; mv $@.$$$$ $@ + cmd_bootar = $(CROSS32AR) -cr $@.$$$$ $(filter-out FORCE,$^); mv $@.$$$$ $@ -$(patsubst %.c,%.o, $(filter %.c, $(src-boot))): %.o: %.c +$(patsubst %.c,%.o, $(filter %.c, $(src-boot))): %.o: %.c FORCE $(call if_changed_dep,bootcc) -$(patsubst %.S,%.o, $(filter %.S, $(src-boot))): %.o: %.S +$(patsubst %.S,%.o, $(filter %.S, $(src-boot))): %.o: %.S FORCE $(call if_changed_dep,bootas) -$(obj)/wrapper.a: $(obj-wlib) - $(call cmd,bootar) +$(obj)/wrapper.a: $(obj-wlib) FORCE + $(call if_changed,bootar) hostprogs-y := addnote addRamDisk hack-coff mktree -extra-y := $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ +targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a) +extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ $(obj)/zImage.lds $(obj)/zImage.coff.lds wrapper :=$(srctree)/$(src)/wrapper -wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) +wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) \ + $(wrapper) FORCE ############# # Bits for building various flavours of zImage @@ -113,50 +116,10 @@ CROSSWRAP := -C "$(CROSS_COMPILE)" endif endif +# args (to if_changed): 1 = (this rule), 2 = platform, 3 = dts 4=dtb 5=initrd quiet_cmd_wrap = WRAP $@ - cmd_wrap =$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) vmlinux -quiet_cmd_wrap_initrd = WRAP $@ - cmd_wrap_initrd =$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \ - -i $(obj)/ramdisk.image.gz vmlinux - -$(obj)/zImage.chrp: vmlinux $(wrapperbits) - $(call cmd,wrap,chrp) - -$(obj)/zImage.initrd.chrp: vmlinux $(wrapperbits) - $(call cmd,wrap_initrd,chrp) - -$(obj)/zImage.pseries: vmlinux $(wrapperbits) - $(call cmd,wrap,pseries) - -$(obj)/zImage.initrd.pseries: vmlinux $(wrapperbits) - $(call cmd,wrap_initrd,pseries) - -$(obj)/zImage.pmac: vmlinux $(wrapperbits) - $(call cmd,wrap,pmac) - -$(obj)/zImage.initrd.pmac: vmlinux $(wrapperbits) - $(call cmd,wrap_initrd,pmac) - -$(obj)/zImage.coff: vmlinux $(wrapperbits) - $(call cmd,wrap,pmaccoff) - -$(obj)/zImage.initrd.coff: vmlinux $(wrapperbits) - $(call cmd,wrap_initrd,pmaccoff) - -$(obj)/zImage.miboot: vmlinux $(wrapperbits) - $(call cmd,wrap,miboot) - -$(obj)/zImage.initrd.miboot: vmlinux $(wrapperbits) - $(call cmd,wrap_initrd,miboot) - -$(obj)/zImage.ps3: vmlinux - $(STRIP) -s -R .comment $< -o $@ - -$(obj)/zImage.initrd.ps3: vmlinux - @echo " WARNING zImage.initrd.ps3 not supported (yet)" - -$(obj)/uImage: vmlinux $(wrapperbits) - $(call cmd,wrap,uboot) + cmd_wrap =$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \ + $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux image-$(CONFIG_PPC_PSERIES) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.pseries @@ -166,7 +129,7 @@ image-$(CONFIG_PPC_CELLEB) += zImage.ps image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp image-$(CONFIG_PPC_PMAC) += zImage.pmac -image-$(CONFIG_DEFAULT_UIMAGE) += uImage +image-$(CONFIG_DEFAULT_UIMAGE) += uImage cuImage # For 32-bit powermacs, build the COFF and miboot images # as well as the ELF images. @@ -174,16 +137,55 @@ ifeq ($(CONFIG_PPC32),y) image-$(CONFIG_PPC_PMAC) += zImage.coff zImage.miboot endif +initrd- := $(patsubst zImage%, zImage.initrd%, $(image-n) $(image-)) initrd-y := $(patsubst zImage%, zImage.initrd%, $(image-y)) +initrd-y := $(filter-out $(image-y), $(initrd-y)) +targets += $(image-y) $(initrd-y) + +$(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz + +# Don't put the ramdisk on the pattern rule; when its missing make will try +# the pattern rule with less dependencies that also matches (even with the +# hard dependency listed). +$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz) + +$(obj)/zImage.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*) + +$(obj)/zImage.ps3: vmlinux + $(STRIP) -s -R .comment $< -o $@ + +$(obj)/zImage.initrd.ps3: vmlinux + @echo " WARNING zImage.initrd.ps3 not supported (yet)" + +$(obj)/uImage: vmlinux $(wrapperbits) + $(call if_changed,wrap,uboot) + +cuboot-plat-$(CONFIG_83xx) += 83xx +cuboot-plat-$(CONFIG_85xx) += 85xx +cuboot-plat-y += unknown-platform + +dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\ + ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE) + +$(obj)/cuImage: vmlinux $(wrapperbits) + $(call if_changed,wrap,cuboot-$(word 1,$(cuboot-plat-y)),$(dts)) $(obj)/zImage: $(addprefix $(obj)/, $(image-y)) @rm -f $@; ln $< $@ $(obj)/zImage.initrd: $(addprefix $(obj)/, $(initrd-y)) @rm -f $@; ln $< $@ -install: $(CONFIGURE) $(image-y) +install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y)) sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $< -clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.strip.gz) -clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.bin.gz) -clean-files += $(image-) +# anything not in $(targets) +clean-files += $(image-) $(initrd-) zImage zImage.initrd \ + cuImage.elf cuImage.bin.gz + +# clean up files cached by wrapper +clean-kernel := vmlinux.strip vmlinux.bin +clean-kernel += $(addsuffix .gz,$(clean-kernel)) +# If not absolute clean-files are relative to $(obj). +clean-files += $(addprefix $(objtree)/, $(clean-kernel)) diff --git a/arch/powerpc/boot/crt0.S b/arch/powerpc/boot/crt0.S index 70e65b1..5a4215c 100644 --- a/arch/powerpc/boot/crt0.S +++ b/arch/powerpc/boot/crt0.S @@ -16,8 +16,11 @@ #include "ppc_asm.h" _zimage_start_opd: .long _zimage_start, 0, 0, 0 + .weak _zimage_start .globl _zimage_start _zimage_start: + .globl _zimage_start_lib +_zimage_start_lib: /* Work out the offset between the address we were linked at and the address where we're running. */ bl 1f @@ -44,7 +47,7 @@ _zimage_start: addi r9,r9,4 bdnz 2b - /* Do a cache flush for our text, in case OF didn't */ + /* Do a cache flush for our text, in case the loader didn't */ 3: lis r9,_start@ha addi r9,r9,_start@l add r9,r0,r9 @@ -59,6 +62,34 @@ _zimage_start: sync isync - mr r6,r1 - b start + /* Clear the BSS */ + lis r9,__bss_start@ha + addi r9,r9,__bss_start@l + add r9,r0,r9 + lis r8,_end@ha + addi r8,r8,_end@l + add r8,r0,r8 + li r10,0 +5: stw r10,0(r9) + addi r9,r9,4 + cmplw cr0,r9,r8 + blt 5b + /* Possibly set up a custom stack */ +.weak _platform_stack_top + lis r8,_platform_stack_top@ha + addi r8,r8,_platform_stack_top@l + cmpwi r8,0 + beq 6f + add r8,r0,r8 + lwz r1,0(r8) + add r1,r0,r1 + li r0,0 + stwu r0,-16(r1) /* establish a stack frame */ +6: + + /* Call platform_init() */ + bl platform_init + + /* Call start */ + b start diff --git a/arch/powerpc/boot/cuboot-83xx.c b/arch/powerpc/boot/cuboot-83xx.c new file mode 100644 index 0000000..6cbc20a --- /dev/null +++ b/arch/powerpc/boot/cuboot-83xx.c @@ -0,0 +1,68 @@ +/* + * Old U-boot compatibility for 83xx + * + * Author: Scott Wood + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include "ops.h" +#include "stdio.h" + +#define TARGET_83xx +#include "ppcboot.h" + +static bd_t bd; +extern char _end[]; +extern char _dtb_start[], _dtb_end[]; + +static void platform_fixups(void) +{ + void *soc; + + dt_fixup_memory(bd.bi_memstart, bd.bi_memsize); + dt_fixup_mac_addresses(bd.bi_enetaddr, bd.bi_enet1addr); + dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 4, bd.bi_busfreq); + + /* Unfortunately, the specific model number is encoded in the + * soc node name in existing dts files -- once that is fixed, + * this can do a simple path lookup. + */ + soc = find_node_by_devtype(NULL, "soc"); + if (soc) { + void *serial = NULL; + + setprop(soc, "bus-frequency", &bd.bi_busfreq, + sizeof(bd.bi_busfreq)); + + while ((serial = find_node_by_devtype(serial, "serial"))) { + if (get_parent(serial) != soc) + continue; + + setprop(serial, "clock-frequency", &bd.bi_busfreq, + sizeof(bd.bi_busfreq)); + } + } +} + +void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; + unsigned long avail_ram = end_of_ram - (unsigned long)_end; + + memcpy(&bd, (bd_t *)r3, sizeof(bd)); + loader_info.initrd_addr = r4; + loader_info.initrd_size = r4 ? r5 : 0; + loader_info.cmdline = (char *)r6; + loader_info.cmdline_len = r7 - r6; + + simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); + ft_init(_dtb_start, _dtb_end - _dtb_start, 32); + serial_console_init(); + platform_ops.fixups = platform_fixups; +} diff --git a/arch/powerpc/boot/cuboot-85xx.c b/arch/powerpc/boot/cuboot-85xx.c new file mode 100644 index 0000000..f88ba00 --- /dev/null +++ b/arch/powerpc/boot/cuboot-85xx.c @@ -0,0 +1,69 @@ +/* + * Old U-boot compatibility for 85xx + * + * Author: Scott Wood + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include "ops.h" +#include "stdio.h" + +#define TARGET_85xx +#include "ppcboot.h" + +static bd_t bd; +extern char _end[]; +extern char _dtb_start[], _dtb_end[]; + +static void platform_fixups(void) +{ + void *soc; + + dt_fixup_memory(bd.bi_memstart, bd.bi_memsize); + dt_fixup_mac_addresses(bd.bi_enetaddr, bd.bi_enet1addr, + bd.bi_enet2addr); + dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 8, bd.bi_busfreq); + + /* Unfortunately, the specific model number is encoded in the + * soc node name in existing dts files -- once that is fixed, + * this can do a simple path lookup. + */ + soc = find_node_by_devtype(NULL, "soc"); + if (soc) { + void *serial = NULL; + + setprop(soc, "bus-frequency", &bd.bi_busfreq, + sizeof(bd.bi_busfreq)); + + while ((serial = find_node_by_devtype(serial, "serial"))) { + if (get_parent(serial) != soc) + continue; + + setprop(serial, "clock-frequency", &bd.bi_busfreq, + sizeof(bd.bi_busfreq)); + } + } +} + +void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; + unsigned long avail_ram = end_of_ram - (unsigned long)_end; + + memcpy(&bd, (bd_t *)r3, sizeof(bd)); + loader_info.initrd_addr = r4; + loader_info.initrd_size = r4 ? r5 : 0; + loader_info.cmdline = (char *)r6; + loader_info.cmdline_len = r7 - r6; + + simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); + ft_init(_dtb_start, _dtb_end - _dtb_start, 32); + serial_console_init(); + platform_ops.fixups = platform_fixups; +} diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c new file mode 100644 index 0000000..c995155 --- /dev/null +++ b/arch/powerpc/boot/devtree.c @@ -0,0 +1,307 @@ +/* + * devtree.c - convenience functions for device tree manipulation + * Copyright 2007 David Gibson, IBM Corporation. + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * + * Authors: David Gibson + * Scott Wood + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "ops.h" + +void dt_fixup_memory(u64 start, u64 size) +{ + void *root, *memory; + int naddr, nsize, i; + u32 memreg[4]; + + root = finddevice("/"); + if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0) + naddr = 2; + if (naddr < 1 || naddr > 2) + fatal("Can't cope with #address-cells == %d in /\n\r", naddr); + + if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0) + nsize = 1; + if (nsize < 1 || nsize > 2) + fatal("Can't cope with #size-cells == %d in /\n\r", nsize); + + i = 0; + if (naddr == 2) + memreg[i++] = start >> 32; + memreg[i++] = start & 0xffffffff; + if (nsize == 2) + memreg[i++] = size >> 32; + memreg[i++] = size & 0xffffffff; + + memory = finddevice("/memory"); + if (! memory) { + memory = create_node(NULL, "memory"); + setprop_str(memory, "device_type", "memory"); + } + + printf("Memory <- <0x%x", memreg[0]); + for (i = 1; i < (naddr + nsize); i++) + printf(" 0x%x", memreg[i]); + printf("> (%ldMB)\n\r", (unsigned long)(size >> 20)); + + setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32)); +} + +#define MHZ(x) ((x + 500000) / 1000000) + +void dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus) +{ + void *devp = NULL; + + printf("CPU clock-frequency <- 0x%x (%dMHz)\n\r", cpu, MHZ(cpu)); + printf("CPU timebase-frequency <- 0x%x (%dMHz)\n\r", tb, MHZ(tb)); + if (bus > 0) + printf("CPU bus-frequency <- 0x%x (%dMHz)\n\r", bus, MHZ(bus)); + + while ((devp = find_node_by_devtype(devp, "cpu"))) { + setprop_val(devp, "clock-frequency", cpu); + setprop_val(devp, "timebase-frequency", tb); + if (bus > 0) + setprop_val(devp, "bus-frequency", bus); + } +} + +void dt_fixup_clock(const char *path, u32 freq) +{ + void *devp = finddevice(path); + + if (devp) { + printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq)); + setprop_val(devp, "clock-frequency", freq); + } +} + +void __dt_fixup_mac_addresses(u32 startindex, ...) +{ + va_list ap; + u32 index = startindex; + void *devp; + const u8 *addr; + + va_start(ap, startindex); + while ((addr = va_arg(ap, const u8 *))) { + devp = find_node_by_prop_value(NULL, "linux,network-index", + (void*)&index, sizeof(index)); + + printf("ENET%d: local-mac-address <-" + " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index, + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + if (devp) + setprop(devp, "local-mac-address", addr, 6); + + index++; + } + va_end(ap); +} + +#define MAX_ADDR_CELLS 4 +#define MAX_RANGES 8 + +static void get_reg_format(void *node, u32 *naddr, u32 *nsize) +{ + if (getprop(node, "#address-cells", naddr, 4) != 4) + *naddr = 2; + if (getprop(node, "#size-cells", nsize, 4) != 4) + *nsize = 1; +} + +static void copy_val(u32 *dest, u32 *src, int naddr) +{ + int pad = MAX_ADDR_CELLS - naddr; + + memset(dest, 0, pad * 4); + memcpy(dest + pad, src, naddr * 4); +} + +static int sub_reg(u32 *reg, u32 *sub) +{ + int i, borrow = 0; + + for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) { + int prev_borrow = borrow; + borrow = reg[i] < sub[i] + prev_borrow; + reg[i] -= sub[i] + prev_borrow; + } + + return !borrow; +} + +static int add_reg(u32 *reg, u32 *add, int naddr) +{ + int i, carry = 0; + + for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) { + u64 tmp = (u64)reg[i] + add[i] + carry; + carry = tmp >> 32; + reg[i] = (u32)tmp; + } + + return !carry; +} + +/* It is assumed that if the first byte of reg fits in a + * range, then the whole reg block fits. + */ +static int compare_reg(u32 *reg, u32 *range, u32 *rangesize) +{ + int i; + u32 end; + + for (i = 0; i < MAX_ADDR_CELLS; i++) { + if (reg[i] < range[i]) + return 0; + if (reg[i] > range[i]) + break; + } + + for (i = 0; i < MAX_ADDR_CELLS; i++) { + end = range[i] + rangesize[i]; + + if (reg[i] < end) + break; + if (reg[i] > end) + return 0; + } + + return reg[i] != end; +} + +/* reg must be MAX_ADDR_CELLS */ +static int find_range(u32 *reg, u32 *ranges, int nregaddr, + int naddr, int nsize, int buflen) +{ + int nrange = nregaddr + naddr + nsize; + int i; + + for (i = 0; i + nrange <= buflen; i += nrange) { + u32 range_addr[MAX_ADDR_CELLS]; + u32 range_size[MAX_ADDR_CELLS]; + + copy_val(range_addr, ranges + i, naddr); + copy_val(range_size, ranges + i + nregaddr + naddr, nsize); + + if (compare_reg(reg, range_addr, range_size)) + return i; + } + + return -1; +} + +/* Currently only generic buses without special encodings are supported. + * In particular, PCI is not supported. Also, only the beginning of the + * reg block is tracked; size is ignored except in ranges. + */ +static u32 dt_xlate_buf[MAX_ADDR_CELLS * MAX_RANGES * 3]; + +static int dt_xlate(void *node, int res, int reglen, unsigned long *addr, + unsigned long *size) +{ + u32 last_addr[MAX_ADDR_CELLS]; + u32 this_addr[MAX_ADDR_CELLS]; + void *parent; + u64 ret_addr, ret_size; + u32 naddr, nsize, prev_naddr; + int buflen, offset; + + parent = get_parent(node); + if (!parent) + return 0; + + get_reg_format(parent, &naddr, &nsize); + + if (nsize > 2) + return 0; + + offset = (naddr + nsize) * res; + + if (reglen < offset + naddr + nsize || + sizeof(dt_xlate_buf) < offset + naddr + nsize) + return 0; + + copy_val(last_addr, dt_xlate_buf + offset, naddr); + + ret_size = dt_xlate_buf[offset + naddr]; + if (nsize == 2) { + ret_size <<= 32; + ret_size |= dt_xlate_buf[offset + naddr + 1]; + } + + while ((node = get_parent(node))) { + prev_naddr = naddr; + + get_reg_format(node, &naddr, &nsize); + + buflen = getprop(node, "ranges", dt_xlate_buf, + sizeof(dt_xlate_buf)); + if (buflen < 0) + continue; + if (buflen > sizeof(dt_xlate_buf)) + return 0; + + offset = find_range(last_addr, dt_xlate_buf, prev_naddr, + naddr, nsize, buflen / 4); + + if (offset < 0) + return 0; + + copy_val(this_addr, dt_xlate_buf + offset, prev_naddr); + + if (!sub_reg(last_addr, this_addr)) + return 0; + + copy_val(this_addr, dt_xlate_buf + offset + prev_naddr, naddr); + + if (!add_reg(last_addr, this_addr, naddr)) + return 0; + } + + if (naddr > 2) + return 0; + + ret_addr = ((u64)last_addr[2] << 32) | last_addr[3]; + + if (sizeof(void *) == 4 && + (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL || + ret_addr + ret_size > 0x100000000ULL)) + return 0; + + *addr = ret_addr; + if (size) + *size = ret_size; + + return 1; +} + +int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size) +{ + int reglen; + + reglen = getprop(node, "reg", dt_xlate_buf, sizeof(dt_xlate_buf)) / 4; + return dt_xlate(node, res, reglen, addr, size); +} + +int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr) +{ + + if (buflen > sizeof(dt_xlate_buf)) + return 0; + + memcpy(dt_xlate_buf, buf, buflen); + return dt_xlate(node, 0, buflen / 4, xlated_addr, NULL); +} diff --git a/arch/powerpc/boot/dts/kuroboxHD.dts b/arch/powerpc/boot/dts/kuroboxHD.dts index b897918..157dc98 100644 --- a/arch/powerpc/boot/dts/kuroboxHD.dts +++ b/arch/powerpc/boot/dts/kuroboxHD.dts @@ -29,7 +29,6 @@ build with: "dtc -f -I dts -O dtb -o kur cpus { linux,phandle = <2000>; - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; @@ -126,17 +125,17 @@ build with: "dtc -f -I dts -O dtb -o kur interrupt-parent = <4400>; interrupt-map-mask = ; interrupt-map = < - /* IDSEL 0x11 - IRQ0 ETH */ + /* IDSEL 11 - IRQ0 ETH */ 5800 0 0 1 4400 0 1 5800 0 0 2 4400 1 1 5800 0 0 3 4400 2 1 5800 0 0 4 4400 3 1 - /* IDSEL 0x12 - IRQ1 IDE0 */ + /* IDSEL 12 - IRQ1 IDE0 */ 6000 0 0 1 4400 1 1 6000 0 0 2 4400 2 1 6000 0 0 3 4400 3 1 6000 0 0 4 4400 0 1 - /* IDSEL 0x14 - IRQ3 USB2.0 */ + /* IDSEL 14 - IRQ3 USB2.0 */ 7000 0 0 1 4400 3 1 7000 0 0 2 4400 3 1 7000 0 0 3 4400 3 1 diff --git a/arch/powerpc/boot/dts/kuroboxHG.dts b/arch/powerpc/boot/dts/kuroboxHG.dts index 7531027..919eb29 100644 --- a/arch/powerpc/boot/dts/kuroboxHG.dts +++ b/arch/powerpc/boot/dts/kuroboxHG.dts @@ -29,7 +29,6 @@ build with: "dtc -f -I dts -O dtb -o kur cpus { linux,phandle = <2000>; - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; @@ -126,17 +125,17 @@ build with: "dtc -f -I dts -O dtb -o kur interrupt-parent = <4400>; interrupt-map-mask = ; interrupt-map = < - /* IDSEL 0x11 - IRQ0 ETH */ + /* IDSEL 11 - IRQ0 ETH */ 5800 0 0 1 4400 0 1 5800 0 0 2 4400 1 1 5800 0 0 3 4400 2 1 5800 0 0 4 4400 3 1 - /* IDSEL 0x12 - IRQ1 IDE0 */ + /* IDSEL 12 - IRQ1 IDE0 */ 6000 0 0 1 4400 1 1 6000 0 0 2 4400 2 1 6000 0 0 3 4400 3 1 6000 0 0 4 4400 0 1 - /* IDSEL 0x14 - IRQ3 USB2.0 */ + /* IDSEL 14 - IRQ3 USB2.0 */ 7000 0 0 1 4400 3 1 7000 0 0 2 4400 3 1 7000 0 0 3 4400 3 1 diff --git a/arch/powerpc/boot/dts/lite5200.dts b/arch/powerpc/boot/dts/lite5200.dts index c03103c..ba54c6b 100644 --- a/arch/powerpc/boot/dts/lite5200.dts +++ b/arch/powerpc/boot/dts/lite5200.dts @@ -24,7 +24,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/lite5200b.dts b/arch/powerpc/boot/dts/lite5200b.dts index 3875ca9..2e00308 100644 --- a/arch/powerpc/boot/dts/lite5200b.dts +++ b/arch/powerpc/boot/dts/lite5200b.dts @@ -24,7 +24,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc7448hpc2.dts b/arch/powerpc/boot/dts/mpc7448hpc2.dts index 41d0720..6fa3754 100644 --- a/arch/powerpc/boot/dts/mpc7448hpc2.dts +++ b/arch/powerpc/boot/dts/mpc7448hpc2.dts @@ -19,7 +19,6 @@ linux,phandle = <100>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells =<0>; linux,phandle = <200>; diff --git a/arch/powerpc/boot/dts/mpc8272ads.dts b/arch/powerpc/boot/dts/mpc8272ads.dts index 260b2e4..423eedc 100644 --- a/arch/powerpc/boot/dts/mpc8272ads.dts +++ b/arch/powerpc/boot/dts/mpc8272ads.dts @@ -17,7 +17,6 @@ linux,phandle = <100>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; linux,phandle = <200>; diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts index 6d72190..a1533cc 100644 --- a/arch/powerpc/boot/dts/mpc8313erdb.dts +++ b/arch/powerpc/boot/dts/mpc8313erdb.dts @@ -16,7 +16,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc832x_mds.dts b/arch/powerpc/boot/dts/mpc832x_mds.dts index 06b3106..c798491 100644 --- a/arch/powerpc/boot/dts/mpc832x_mds.dts +++ b/arch/powerpc/boot/dts/mpc832x_mds.dts @@ -16,7 +16,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts new file mode 100644 index 0000000..b55bced --- /dev/null +++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts @@ -0,0 +1,291 @@ +/* + * MPC832x RDB Device Tree Source + * + * Copyright 2007 Freescale Semiconductor Inc. + * + * 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. + */ + +/ { + model = "MPC8323ERDB"; + compatible = "MPC8323ERDB", "MPC832xRDB", "MPC83xxRDB"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,8323@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <20>; // 32 bytes + i-cache-line-size = <20>; // 32 bytes + d-cache-size = <4000>; // L1, 16K + i-cache-size = <4000>; // L1, 16K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + 32-bit; + }; + }; + + memory { + device_type = "memory"; + reg = <00000000 04000000>; + }; + + soc8323@e0000000 { + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + device_type = "soc"; + ranges = <0 e0000000 00100000>; + reg = ; + bus-frequency = <0>; + + wdt@200 { + device_type = "watchdog"; + compatible = "mpc83xx_wdt"; + reg = <200 100>; + }; + + i2c@3000 { + device_type = "i2c"; + compatible = "fsl-i2c"; + reg = <3000 100>; + interrupts = ; + interrupt-parent = <&pic>; + dfsrr; + }; + + serial@4500 { + device_type = "serial"; + compatible = "ns16550"; + reg = <4500 100>; + clock-frequency = <0>; + interrupts = <9 8>; + interrupt-parent = <&pic>; + }; + + serial@4600 { + device_type = "serial"; + compatible = "ns16550"; + reg = <4600 100>; + clock-frequency = <0>; + interrupts = ; + interrupt-parent = <&pic>; + }; + + crypto@30000 { + device_type = "crypto"; + model = "SEC2"; + compatible = "talitos"; + reg = <30000 7000>; + interrupts = ; + interrupt-parent = <&pic>; + /* Rev. 2.2 */ + num-channels = <1>; + channel-fifo-len = <18>; + exec-units-mask = <0000004c>; + descriptor-types-mask = <0122003f>; + }; + + pci@8500 { + interrupt-map-mask = ; + interrupt-map = < + /* IDSEL 0x10 AD16 (USB) */ + 8000 0 0 1 &pic 11 8 + + /* IDSEL 0x11 AD17 (Mini1)*/ + 8800 0 0 1 &pic 12 8 + 8800 0 0 2 &pic 13 8 + 8800 0 0 3 &pic 14 8 + 8800 0 0 4 &pic 30 8 + + /* IDSEL 0x12 AD18 (PCI/Mini2) */ + 9000 0 0 1 &pic 13 8 + 9000 0 0 2 &pic 14 8 + 9000 0 0 3 &pic 30 8 + 9000 0 0 4 &pic 11 8>; + + interrupt-parent = <&pic>; + interrupts = <42 8>; + bus-range = <0 0>; + ranges = <42000000 0 80000000 80000000 0 10000000 + 02000000 0 90000000 90000000 0 10000000 + 01000000 0 d0000000 d0000000 0 04000000>; + clock-frequency = <0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <8500 100>; + compatible = "83xx"; + device_type = "pci"; + }; + + pic:pic@700 { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <700 100>; + built-in; + device_type = "ipic"; + }; + + par_io@1400 { + reg = <1400 100>; + device_type = "par_io"; + num-ports = <7>; + + ucc2pio:ucc_pin@02 { + pio-map = < + /* port pin dir open_drain assignment has_irq */ + 3 4 3 0 2 0 /* MDIO */ + 3 5 1 0 2 0 /* MDC */ + 3 15 2 0 1 0 /* RX_CLK (CLK16) */ + 3 17 2 0 1 0 /* TX_CLK (CLK3) */ + 0 12 1 0 1 0 /* TxD0 */ + 0 13 1 0 1 0 /* TxD1 */ + 0 14 1 0 1 0 /* TxD2 */ + 0 15 1 0 1 0 /* TxD3 */ + 0 16 2 0 1 0 /* RxD0 */ + 0 17 2 0 1 0 /* RxD1 */ + 0 18 2 0 1 0 /* RxD2 */ + 0 19 2 0 1 0 /* RxD3 */ + 0 1a 2 0 1 0 /* RX_ER */ + 0 1b 1 0 1 0 /* TX_ER */ + 0 1c 2 0 1 0 /* RX_DV */ + 0 1d 2 0 1 0 /* COL */ + 0 1e 1 0 1 0 /* TX_EN */ + 0 1f 2 0 1 0>; /* CRS */ + }; + ucc3pio:ucc_pin@03 { + pio-map = < + /* port pin dir open_drain assignment has_irq */ + 0 d 2 0 1 0 /* RX_CLK (CLK9) */ + 3 18 2 0 1 0 /* TX_CLK (CLK10) */ + 1 0 1 0 1 0 /* TxD0 */ + 1 1 1 0 1 0 /* TxD1 */ + 1 2 1 0 1 0 /* TxD2 */ + 1 3 1 0 1 0 /* TxD3 */ + 1 4 2 0 1 0 /* RxD0 */ + 1 5 2 0 1 0 /* RxD1 */ + 1 6 2 0 1 0 /* RxD2 */ + 1 7 2 0 1 0 /* RxD3 */ + 1 8 2 0 1 0 /* RX_ER */ + 1 9 1 0 1 0 /* TX_ER */ + 1 a 2 0 1 0 /* RX_DV */ + 1 b 2 0 1 0 /* COL */ + 1 c 1 0 1 0 /* TX_EN */ + 1 d 2 0 1 0>; /* CRS */ + }; + }; + }; + + qe@e0100000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "qe"; + model = "QE"; + ranges = <0 e0100000 00100000>; + reg = ; + brg-frequency = <0>; + bus-frequency = ; + + muram@10000 { + device_type = "muram"; + ranges = <0 00010000 00004000>; + + data-only@0 { + reg = <0 4000>; + }; + }; + + spi@4c0 { + device_type = "spi"; + compatible = "fsl_spi"; + reg = <4c0 40>; + interrupts = <2>; + interrupt-parent = <&qeic>; + mode = "cpu"; + }; + + spi@500 { + device_type = "spi"; + compatible = "fsl_spi"; + reg = <500 40>; + interrupts = <1>; + interrupt-parent = <&qeic>; + mode = "cpu"; + }; + + ucc@3000 { + device_type = "network"; + compatible = "ucc_geth"; + model = "UCC"; + device-id = <2>; + reg = <3000 200>; + interrupts = <21>; + interrupt-parent = <&qeic>; + mac-address = [ 00 04 9f ef 03 02 ]; + rx-clock = <20>; + tx-clock = <13>; + phy-handle = <&phy00>; + pio-handle = <&ucc2pio>; + }; + + ucc@2200 { + device_type = "network"; + compatible = "ucc_geth"; + model = "UCC"; + device-id = <3>; + reg = <2200 200>; + interrupts = <22>; + interrupt-parent = <&qeic>; + mac-address = [ 00 04 9f ef 03 01 ]; + rx-clock = <19>; + tx-clock = <1a>; + phy-handle = <&phy04>; + pio-handle = <&ucc3pio>; + }; + + mdio@3120 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3120 18>; + device_type = "mdio"; + compatible = "ucc_geth_phy"; + + phy00:ethernet-phy@00 { + interrupt-parent = <&pic>; + interrupts = <0>; + reg = <0>; + device_type = "ethernet-phy"; + interface = <3>; //ENET_100_MII + }; + phy04:ethernet-phy@04 { + interrupt-parent = <&pic>; + interrupts = <0>; + reg = <4>; + device_type = "ethernet-phy"; + interface = <3>; + }; + }; + + qeic:qeic@80 { + interrupt-controller; + device_type = "qeic"; + #address-cells = <0>; + #interrupt-cells = <1>; + reg = <80 80>; + built-in; + big-endian; + interrupts = <20 8 21 8>; //high:32 low:33 + interrupt-parent = <&pic>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts index 61b550b..db0d003 100644 --- a/arch/powerpc/boot/dts/mpc8349emitx.dts +++ b/arch/powerpc/boot/dts/mpc8349emitx.dts @@ -15,7 +15,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8349emitxgp.dts b/arch/powerpc/boot/dts/mpc8349emitxgp.dts index b2e1a5e..f636528 100644 --- a/arch/powerpc/boot/dts/mpc8349emitxgp.dts +++ b/arch/powerpc/boot/dts/mpc8349emitxgp.dts @@ -15,7 +15,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts index e4b43c2..07bcc51 100644 --- a/arch/powerpc/boot/dts/mpc834x_mds.dts +++ b/arch/powerpc/boot/dts/mpc834x_mds.dts @@ -16,7 +16,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts index 4fe45c0..7f578eb 100644 --- a/arch/powerpc/boot/dts/mpc836x_mds.dts +++ b/arch/powerpc/boot/dts/mpc836x_mds.dts @@ -21,7 +21,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8540ads.dts b/arch/powerpc/boot/dts/mpc8540ads.dts index 3c0917f..f261d64 100644 --- a/arch/powerpc/boot/dts/mpc8540ads.dts +++ b/arch/powerpc/boot/dts/mpc8540ads.dts @@ -17,7 +17,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8541cds.dts b/arch/powerpc/boot/dts/mpc8541cds.dts index 2a1ae76..5fdcb69 100644 --- a/arch/powerpc/boot/dts/mpc8541cds.dts +++ b/arch/powerpc/boot/dts/mpc8541cds.dts @@ -17,7 +17,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8544ds.dts b/arch/powerpc/boot/dts/mpc8544ds.dts new file mode 100644 index 0000000..6b08460 --- /dev/null +++ b/arch/powerpc/boot/dts/mpc8544ds.dts @@ -0,0 +1,136 @@ +/* + * MPC8544 DS Device Tree Source + * + * Copyright 2007 Freescale Semiconductor Inc. + * + * 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. + */ + +/ { + model = "MPC8544DS"; + compatible = "MPC8544DS", "MPC85xxDS"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #cpus = <1>; + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,8544@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <20>; // 32 bytes + i-cache-line-size = <20>; // 32 bytes + d-cache-size = <8000>; // L1, 32K + i-cache-size = <8000>; // L1, 32K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + 32-bit; + }; + }; + + memory { + device_type = "memory"; + reg = <00000000 00000000>; // Filled by U-Boot + }; + + soc8544@e0000000 { + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + device_type = "soc"; + ranges = <0 e0000000 00100000>; + reg = ; // CCSRBAR 1M + bus-frequency = <0>; // Filled out by uboot. + + i2c@3000 { + device_type = "i2c"; + compatible = "fsl-i2c"; + reg = <3000 100>; + interrupts = <1b 2>; + interrupt-parent = <&mpic>; + dfsrr; + }; + + mdio@24520 { + #address-cells = <1>; + #size-cells = <0>; + device_type = "mdio"; + compatible = "gianfar"; + reg = <24520 20>; + phy0: ethernet-phy@0 { + interrupt-parent = <&mpic>; + interrupts = <3a 1>; + reg = <0>; + device_type = "ethernet-phy"; + }; + phy1: ethernet-phy@1 { + interrupt-parent = <&mpic>; + interrupts = <3a 1>; + reg = <1>; + device_type = "ethernet-phy"; + }; + }; + + ethernet@24000 { + #address-cells = <1>; + #size-cells = <0>; + device_type = "network"; + model = "TSEC"; + compatible = "gianfar"; + reg = <24000 1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = ; + interrupt-parent = <&mpic>; + phy-handle = <&phy0>; + }; + + ethernet@26000 { + #address-cells = <1>; + #size-cells = <0>; + device_type = "network"; + model = "TSEC"; + compatible = "gianfar"; + reg = <26000 1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = ; + interrupt-parent = <&mpic>; + phy-handle = <&phy1>; + }; + + serial@4500 { + device_type = "serial"; + compatible = "ns16550"; + reg = <4500 100>; + clock-frequency = <0>; + interrupts = <1a 2>; + interrupt-parent = <&mpic>; + }; + + serial@4600 { + device_type = "serial"; + compatible = "ns16550"; + reg = <4600 100>; + clock-frequency = <0>; + interrupts = <1a 2>; + interrupt-parent = <&mpic>; + }; + + mpic: pic@40000 { + clock-frequency = <0>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <40000 40000>; + built-in; + compatible = "chrp,open-pic"; + device_type = "open-pic"; + big-endian; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts index 7eb5d81..b2b2200 100644 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ b/arch/powerpc/boot/dts/mpc8548cds.dts @@ -17,7 +17,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8555cds.dts b/arch/powerpc/boot/dts/mpc8555cds.dts index 5f9c102..68a4795 100644 --- a/arch/powerpc/boot/dts/mpc8555cds.dts +++ b/arch/powerpc/boot/dts/mpc8555cds.dts @@ -17,7 +17,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8560ads.dts b/arch/powerpc/boot/dts/mpc8560ads.dts index 1050263..1f2afe9 100644 --- a/arch/powerpc/boot/dts/mpc8560ads.dts +++ b/arch/powerpc/boot/dts/mpc8560ads.dts @@ -17,7 +17,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8568mds.dts b/arch/powerpc/boot/dts/mpc8568mds.dts index bf49d8c..7361b36 100644 --- a/arch/powerpc/boot/dts/mpc8568mds.dts +++ b/arch/powerpc/boot/dts/mpc8568mds.dts @@ -21,7 +21,6 @@ #size-cells = <1>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/mpc8641_hpcn.dts b/arch/powerpc/boot/dts/mpc8641_hpcn.dts index 8a4995a..260b264 100644 --- a/arch/powerpc/boot/dts/mpc8641_hpcn.dts +++ b/arch/powerpc/boot/dts/mpc8641_hpcn.dts @@ -17,7 +17,6 @@ #size-cells = <1>; cpus { - #cpus = <2>; #address-cells = <1>; #size-cells = <0>; @@ -300,6 +299,30 @@ }; }; + + pci@9000 { + compatible = "86xx"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <9000 1000>; + bus-range = <0 ff>; + ranges = <02000000 0 a0000000 a0000000 0 20000000 + 01000000 0 00000000 e3000000 0 00100000>; + clock-frequency = <1fca055>; + interrupt-parent = <&mpic>; + interrupts = <19 2>; + interrupt-map-mask = ; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 44 1 + 0000 0 0 2 &mpic 45 1 + 0000 0 0 3 &mpic 46 1 + 0000 0 0 4 &mpic 47 1 + >; + }; + mpic: pic@40000 { clock-frequency = <0>; interrupt-controller; diff --git a/arch/powerpc/boot/dts/mpc866ads.dts b/arch/powerpc/boot/dts/mpc866ads.dts index 2b56b5d..c0d06fd 100644 --- a/arch/powerpc/boot/dts/mpc866ads.dts +++ b/arch/powerpc/boot/dts/mpc866ads.dts @@ -18,7 +18,6 @@ linux,phandle = <100>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; linux,phandle = <200>; diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index faecd08..110bf61 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -18,7 +18,6 @@ linux,phandle = <100>; cpus { - #cpus = <1>; #address-cells = <1>; #size-cells = <0>; linux,phandle = <200>; diff --git a/arch/powerpc/boot/elf.h b/arch/powerpc/boot/elf.h index d4828fc..1941bc5 100644 --- a/arch/powerpc/boot/elf.h +++ b/arch/powerpc/boot/elf.h @@ -146,4 +146,12 @@ #define EV_NUM 2 #define ELFOSABI_NONE 0 #define ELFOSABI_LINUX 3 +struct elf_info { + unsigned long loadsize; + unsigned long memsize; + unsigned long elfoffset; +}; +int parse_elf64(void *hdr, struct elf_info *info); +int parse_elf32(void *hdr, struct elf_info *info); + #endif /* _PPC_BOOT_ELF_H_ */ diff --git a/arch/powerpc/boot/elf_util.c b/arch/powerpc/boot/elf_util.c new file mode 100644 index 0000000..7454aa4 --- /dev/null +++ b/arch/powerpc/boot/elf_util.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include "elf.h" +#include "page.h" +#include "string.h" +#include "stdio.h" + +int parse_elf64(void *hdr, struct elf_info *info) +{ + Elf64_Ehdr *elf64 = hdr; + Elf64_Phdr *elf64ph; + unsigned int i; + + if (!(elf64->e_ident[EI_MAG0] == ELFMAG0 && + elf64->e_ident[EI_MAG1] == ELFMAG1 && + elf64->e_ident[EI_MAG2] == ELFMAG2 && + elf64->e_ident[EI_MAG3] == ELFMAG3 && + elf64->e_ident[EI_CLASS] == ELFCLASS64 && + elf64->e_ident[EI_DATA] == ELFDATA2MSB && + elf64->e_type == ET_EXEC && + elf64->e_machine == EM_PPC64)) + return 0; + + elf64ph = (Elf64_Phdr *)((unsigned long)elf64 + + (unsigned long)elf64->e_phoff); + for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++) + if (elf64ph->p_type == PT_LOAD) + break; + if (i >= (unsigned int)elf64->e_phnum) + return 0; + + info->loadsize = (unsigned long)elf64ph->p_filesz; + info->memsize = (unsigned long)elf64ph->p_memsz; + info->elfoffset = (unsigned long)elf64ph->p_offset; + + return 1; +} + +int parse_elf32(void *hdr, struct elf_info *info) +{ + Elf32_Ehdr *elf32 = hdr; + Elf32_Phdr *elf32ph; + unsigned int i; + + if (!(elf32->e_ident[EI_MAG0] == ELFMAG0 && + elf32->e_ident[EI_MAG1] == ELFMAG1 && + elf32->e_ident[EI_MAG2] == ELFMAG2 && + elf32->e_ident[EI_MAG3] == ELFMAG3 && + elf32->e_ident[EI_CLASS] == ELFCLASS32 && + elf32->e_ident[EI_DATA] == ELFDATA2MSB && + elf32->e_type == ET_EXEC && + elf32->e_machine == EM_PPC)) + return 0; + + elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff); + for (i = 0; i < elf32->e_phnum; i++, elf32ph++) + if (elf32ph->p_type == PT_LOAD) + break; + if (i >= elf32->e_phnum) + return 0; + + info->loadsize = elf32ph->p_filesz; + info->memsize = elf32ph->p_memsz; + info->elfoffset = elf32ph->p_offset; + return 1; +} diff --git a/arch/powerpc/boot/flatdevtree.c b/arch/powerpc/boot/flatdevtree.c index c76c194..d00fbd9 100644 --- a/arch/powerpc/boot/flatdevtree.c +++ b/arch/powerpc/boot/flatdevtree.c @@ -29,12 +29,20 @@ #include "flatdevtree_env.h" #define _ALIGN(x, al) (((x) + (al) - 1) & ~((al) - 1)) +static char *ft_root_node(struct ft_cxt *cxt) +{ + return cxt->rgn[FT_STRUCT].start; +} + /* Routines for keeping node ptrs returned by ft_find_device current */ /* First entry not used b/c it would return 0 and be taken as NULL/error */ -static void *ft_node_add(struct ft_cxt *cxt, char *node) +static void *ft_get_phandle(struct ft_cxt *cxt, char *node) { unsigned int i; + if (!node) + return NULL; + for (i = 1; i < cxt->nodes_used; i++) /* already there? */ if (cxt->node_tbl[i] == node) return (void *)i; @@ -238,7 +246,7 @@ static int ft_shuffle(struct ft_cxt *cxt if (rgn == FT_STRUCT) ft_node_update_before(cxt, p, -nextra); } - *p -= nextra; + *pp -= nextra; cxt->rgn[rgn].start -= nextra; cxt->rgn[rgn].size += nextra; return 1; @@ -253,8 +261,14 @@ static int ft_make_space(struct ft_cxt * char *str, *next; enum ft_rgn_id r; - if (!cxt->isordered && !ft_reorder(cxt, nextra)) - return 0; + if (!cxt->isordered) { + unsigned long rgn_off = *pp - cxt->rgn[rgn].start; + + if (!ft_reorder(cxt, nextra)) + return 0; + + *pp = cxt->rgn[rgn].start + rgn_off; + } if (ft_shuffle(cxt, pp, rgn, nextra)) return 1; @@ -415,7 +429,7 @@ int ft_prop(struct ft_cxt *cxt, const ch { int off, len; - off = lookup_string(cxt, name); + off = map_string(cxt, name); if (off == NO_STRING) return -1; @@ -590,7 +604,7 @@ int ft_add_rsvmap(struct ft_cxt *cxt, u6 void ft_begin_tree(struct ft_cxt *cxt) { - cxt->p = cxt->rgn[FT_STRUCT].start; + cxt->p = ft_root_node(cxt); } void ft_end_tree(struct ft_cxt *cxt) @@ -636,8 +650,21 @@ void *ft_find_device(struct ft_cxt *cxt, /* require absolute path */ if (srch_path[0] != '/') return NULL; - node = ft_find_descendent(cxt, cxt->rgn[FT_STRUCT].start, srch_path); - return ft_node_add(cxt, node); + node = ft_find_descendent(cxt, ft_root_node(cxt), srch_path); + return ft_get_phandle(cxt, node); +} + +void *ft_find_device_rel(struct ft_cxt *cxt, const void *top, + const char *srch_path) +{ + char *node; + + node = ft_node_ph2node(cxt, top); + if (node == NULL) + return NULL; + + node = ft_find_descendent(cxt, node, srch_path); + return ft_get_phandle(cxt, node); } void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path) @@ -701,23 +728,18 @@ void *ft_find_descendent(struct ft_cxt * return NULL; } -void *ft_get_parent(struct ft_cxt *cxt, const void *phandle) +void *__ft_get_parent(struct ft_cxt *cxt, void *node) { - void *node; int d; struct ft_atom atom; char *p; - node = ft_node_ph2node(cxt, phandle); - if (node == NULL) - return NULL; - for (d = 0; cxt->genealogy[d] != NULL; ++d) if (cxt->genealogy[d] == node) - return cxt->genealogy[d > 0 ? d - 1 : 0]; + return d > 0 ? cxt->genealogy[d - 1] : NULL; /* have to do it the hard way... */ - p = cxt->rgn[FT_STRUCT].start; + p = ft_root_node(cxt); d = 0; while ((p = ft_next(cxt, p, &atom)) != NULL) { switch (atom.tag) { @@ -726,7 +748,7 @@ void *ft_get_parent(struct ft_cxt *cxt, if (node == atom.data) { /* found it */ cxt->genealogy[d + 1] = NULL; - return d > 0 ? cxt->genealogy[d - 1] : node; + return d > 0 ? cxt->genealogy[d - 1] : NULL; } ++d; break; @@ -738,41 +760,131 @@ void *ft_get_parent(struct ft_cxt *cxt, return NULL; } -int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, - void *buf, const unsigned int buflen) +void *ft_get_parent(struct ft_cxt *cxt, const void *phandle) { - struct ft_atom atom; - void *node; - char *p; - int depth; - unsigned int size; - - node = ft_node_ph2node(cxt, phandle); + void *node = ft_node_ph2node(cxt, phandle); if (node == NULL) - return -1; + return NULL; - depth = 0; - p = (char *)node; + node = __ft_get_parent(cxt, node); + return ft_get_phandle(cxt, node); +} - while ((p = ft_next(cxt, p, &atom)) != NULL) { +static const void *__ft_get_prop(struct ft_cxt *cxt, void *node, + const char *propname, unsigned int *len) +{ + struct ft_atom atom; + int depth = 0; + + while ((node = ft_next(cxt, node, &atom)) != NULL) { switch (atom.tag) { case OF_DT_BEGIN_NODE: ++depth; break; + case OF_DT_PROP: - if ((depth != 1) || strcmp(atom.name, propname)) + if (depth != 1 || strcmp(atom.name, propname)) break; - size = min(atom.size, buflen); - memcpy(buf, atom.data, size); - return atom.size; + + if (len) + *len = atom.size; + + return atom.data; + case OF_DT_END_NODE: if (--depth <= 0) - return -1; + return NULL; } } + + return NULL; +} + +int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + void *buf, const unsigned int buflen) +{ + const void *data; + unsigned int size; + + void *node = ft_node_ph2node(cxt, phandle); + if (!node) + return -1; + + data = __ft_get_prop(cxt, node, propname, &size); + if (data) { + unsigned int clipped_size = min(size, buflen); + memcpy(buf, data, clipped_size); + return size; + } + return -1; } +void *__ft_find_node_by_prop_value(struct ft_cxt *cxt, void *prev, + const char *propname, const char *propval, + unsigned int proplen) +{ + struct ft_atom atom; + char *p = ft_root_node(cxt); + char *next; + int past_prev = prev ? 0 : 1; + int depth = -1; + + while ((next = ft_next(cxt, p, &atom)) != NULL) { + const void *data; + unsigned int size; + + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + depth++; + + if (prev == p) { + past_prev = 1; + break; + } + + if (!past_prev || depth < 1) + break; + + data = __ft_get_prop(cxt, p, propname, &size); + if (!data || size != proplen) + break; + if (memcmp(data, propval, size)) + break; + + return p; + + case OF_DT_END_NODE: + if (depth-- == 0) + return NULL; + + break; + } + + p = next; + } + + return NULL; +} + +void *ft_find_node_by_prop_value(struct ft_cxt *cxt, const void *prev, + const char *propname, const char *propval, + int proplen) +{ + void *node = NULL; + + if (prev) { + node = ft_node_ph2node(cxt, prev); + + if (!node) + return NULL; + } + + node = __ft_find_node_by_prop_value(cxt, node, propname, + propval, proplen); + return ft_get_phandle(cxt, node); +} + int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, const void *buf, const unsigned int buflen) { @@ -849,19 +961,26 @@ int ft_del_prop(struct ft_cxt *cxt, cons return -1; } -void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *path) +void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *name) { struct ft_atom atom; char *p, *next; int depth = 0; - p = cxt->rgn[FT_STRUCT].start; + if (parent) { + p = ft_node_ph2node(cxt, parent); + if (!p) + return NULL; + } else { + p = ft_root_node(cxt); + } + while ((next = ft_next(cxt, p, &atom)) != NULL) { switch (atom.tag) { case OF_DT_BEGIN_NODE: ++depth; - if (depth == 1 && strcmp(atom.name, path) == 0) - /* duplicate node path, return error */ + if (depth == 1 && strcmp(atom.name, name) == 0) + /* duplicate node name, return error */ return NULL; break; case OF_DT_END_NODE: @@ -870,7 +989,7 @@ void *ft_create_node(struct ft_cxt *cxt, break; /* end of node, insert here */ cxt->p = p; - ft_begin_node(cxt, path); + ft_begin_node(cxt, name); ft_end_node(cxt); return p; } diff --git a/arch/powerpc/boot/flatdevtree.h b/arch/powerpc/boot/flatdevtree.h index b9cd9f6..cb26325 100644 --- a/arch/powerpc/boot/flatdevtree.h +++ b/arch/powerpc/boot/flatdevtree.h @@ -97,10 +97,17 @@ int ft_add_rsvmap(struct ft_cxt *cxt, u6 void ft_dump_blob(const void *bphp); void ft_merge_blob(struct ft_cxt *cxt, void *blob); void *ft_find_device(struct ft_cxt *cxt, const char *srch_path); +void *ft_find_device_rel(struct ft_cxt *cxt, const void *top, + const char *srch_path); void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path); int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, void *buf, const unsigned int buflen); int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, const void *buf, const unsigned int buflen); +void *ft_get_parent(struct ft_cxt *cxt, const void *phandle); +void *ft_find_node_by_prop_value(struct ft_cxt *cxt, const void *prev, + const char *propname, const char *propval, + int proplen); +void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *name); #endif /* FLATDEVTREE_H */ diff --git a/arch/powerpc/boot/flatdevtree_misc.c b/arch/powerpc/boot/flatdevtree_misc.c index 04da38f..4341e65 100644 --- a/arch/powerpc/boot/flatdevtree_misc.c +++ b/arch/powerpc/boot/flatdevtree_misc.c @@ -16,24 +16,43 @@ #include "ops.h" static struct ft_cxt cxt; -static void *ft_finddevice(const char *name) +static void *fdtm_finddevice(const char *name) { return ft_find_device(&cxt, name); } -static int ft_getprop(const void *phandle, const char *propname, void *buf, - const int buflen) +static int fdtm_getprop(const void *phandle, const char *propname, + void *buf, const int buflen) { return ft_get_prop(&cxt, phandle, propname, buf, buflen); } -static int ft_setprop(const void *phandle, const char *propname, - const void *buf, const int buflen) +static int fdtm_setprop(const void *phandle, const char *propname, + const void *buf, const int buflen) { return ft_set_prop(&cxt, phandle, propname, buf, buflen); } -static unsigned long ft_finalize(void) +static void *fdtm_get_parent(const void *phandle) +{ + return ft_get_parent(&cxt, phandle); +} + +static void *fdtm_create_node(const void *phandle, const char *name) +{ + return ft_create_node(&cxt, phandle, name); +} + +static void *fdtm_find_node_by_prop_value(const void *prev, + const char *propname, + const char *propval, + int proplen) +{ + return ft_find_node_by_prop_value(&cxt, prev, propname, + propval, proplen); +} + +static unsigned long fdtm_finalize(void) { ft_end_tree(&cxt); return (unsigned long)cxt.bph; @@ -41,10 +60,13 @@ static unsigned long ft_finalize(void) int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device) { - dt_ops.finddevice = ft_finddevice; - dt_ops.getprop = ft_getprop; - dt_ops.setprop = ft_setprop; - dt_ops.finalize = ft_finalize; + dt_ops.finddevice = fdtm_finddevice; + dt_ops.getprop = fdtm_getprop; + dt_ops.setprop = fdtm_setprop; + dt_ops.get_parent = fdtm_get_parent; + dt_ops.create_node = fdtm_create_node; + dt_ops.find_node_by_prop_value = fdtm_find_node_by_prop_value; + dt_ops.finalize = fdtm_finalize; return ft_open(&cxt, dt_blob, max_size, max_find_device, platform_ops.realloc); diff --git a/arch/powerpc/boot/gunzip_util.c b/arch/powerpc/boot/gunzip_util.c new file mode 100644 index 0000000..df8ab07 --- /dev/null +++ b/arch/powerpc/boot/gunzip_util.c @@ -0,0 +1,206 @@ +/* + * Copyright 2007 David Gibson, IBM Corporation. + * Based on earlier work, Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include "string.h" +#include "stdio.h" +#include "ops.h" +#include "gunzip_util.h" + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +/** + * gunzip_start - prepare to decompress gzip data + * @state: decompressor state structure to be initialized + * @src: buffer containing gzip compressed or uncompressed data + * @srclen: size in bytes of the buffer at src + * + * If the buffer at @src contains a gzip header, this function + * initializes zlib to decompress the data, storing the decompression + * state in @state. The other functions in this file can then be used + * to decompress data from the gzipped stream. + * + * If the buffer at @src does not contain a gzip header, it is assumed + * to contain uncompressed data. The buffer information is recorded + * in @state and the other functions in this file will simply copy + * data from the uncompressed data stream at @src. + * + * Any errors, such as bad compressed data, cause an error to be + * printed an the platform's exit() function to be called. + */ +void gunzip_start(struct gunzip_state *state, void *src, int srclen) +{ + char *hdr = src; + int hdrlen = 0; + + memset(state, 0, sizeof(*state)); + + /* Check for gzip magic number */ + if ((hdr[0] == 0x1f) && (hdr[1] == 0x8b)) { + /* gzip data, initialize zlib parameters */ + int r, flags; + + state->s.workspace = state->scratch; + if (zlib_inflate_workspacesize() > sizeof(state->scratch)) + fatal("insufficient scratch space for gunzip\n\r"); + + /* skip header */ + hdrlen = 10; + flags = hdr[3]; + if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0) + fatal("bad gzipped data\n\r"); + if ((flags & EXTRA_FIELD) != 0) + hdrlen = 12 + hdr[10] + (hdr[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (hdr[hdrlen++] != 0) + ; + if ((flags & COMMENT) != 0) + while (hdr[hdrlen++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + hdrlen += 2; + if (hdrlen >= srclen) + fatal("gunzip_start: ran out of data in header\n\r"); + + r = zlib_inflateInit2(&state->s, -MAX_WBITS); + if (r != Z_OK) + fatal("inflateInit2 returned %d\n\r", r); + } + + state->s.next_in = src + hdrlen; + state->s.avail_in = srclen - hdrlen; +} + +/** + * gunzip_partial - extract bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @dst: buffer to store extracted data + * @dstlen: maximum number of bytes to extract + * + * This function extracts at most @dstlen bytes from the data stream + * previously associated with @state by gunzip_start(), decompressing + * if necessary. Exactly @dstlen bytes are extracted unless the data + * stream doesn't contain enough bytes, in which case the entire + * remainder of the stream is decompressed. + * + * Returns the actual number of bytes extracted. If any errors occur, + * such as a corrupted compressed stream, an error is printed an the + * platform's exit() function is called. + */ +int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen) +{ + int len; + + if (state->s.workspace) { + /* gunzipping */ + int r; + + state->s.next_out = dst; + state->s.avail_out = dstlen; + r = zlib_inflate(&state->s, Z_FULL_FLUSH); + if (r != Z_OK && r != Z_STREAM_END) + fatal("inflate returned %d msg: %s\n\r", r, state->s.msg); + len = state->s.next_out - (unsigned char *)dst; + } else { + /* uncompressed image */ + len = min(state->s.avail_in, (unsigned)dstlen); + memcpy(dst, state->s.next_in, len); + state->s.next_in += len; + state->s.avail_in -= len; + } + return len; +} + +/** + * gunzip_exactly - extract a fixed number of bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @dst: buffer to store extracted data + * @dstlen: number of bytes to extract + * + * This function extracts exactly @dstlen bytes from the data stream + * previously associated with @state by gunzip_start(), decompressing + * if necessary. + * + * If there are less @dstlen bytes available in the data stream, or if + * any other errors occur, such as a corrupted compressed stream, an + * error is printed an the platform's exit() function is called. + */ +void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) +{ + int len; + + len = gunzip_partial(state, dst, dstlen); + if (len < dstlen) + fatal("\n\rgunzip_exactly: ran out of data!" + " Wanted %d, got %d.\n\r", dstlen, len); +} + +/** + * gunzip_discard - discard bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @len: number of bytes to discard + * + * This function extracts, then discards exactly @len bytes from the + * data stream previously associated with @state by gunzip_start(). + * Subsequent gunzip_partial(), gunzip_exactly() or gunzip_finish() + * calls will extract the data following the discarded bytes in the + * data stream. + * + * If there are less @len bytes available in the data stream, or if + * any other errors occur, such as a corrupted compressed stream, an + * error is printed an the platform's exit() function is called. + */ +void gunzip_discard(struct gunzip_state *state, int len) +{ + static char discard_buf[128]; + + while (len > sizeof(discard_buf)) { + gunzip_exactly(state, discard_buf, sizeof(discard_buf)); + len -= sizeof(discard_buf); + } + + if (len > 0) + gunzip_exactly(state, discard_buf, len); +} + +/** + * gunzip_finish - extract all remaining bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @dst: buffer to store extracted data + * @dstlen: maximum number of bytes to extract + * + * This function extracts all remaining data, or at most @dstlen + * bytes, from the stream previously associated with @state by + * gunzip_start(). zlib is then shut down, so it is an error to use + * any of the functions in this file on @state until it is + * re-initialized with another call to gunzip_start(). + * + * If any errors occur, such as a corrupted compressed stream, an + * error is printed an the platform's exit() function is called. + */ +int gunzip_finish(struct gunzip_state *state, void *dst, int dstlen) +{ + int len; + + if (state->s.workspace) { + len = gunzip_partial(state, dst, dstlen); + zlib_inflateEnd(&state->s); + } else { + /* uncompressed image */ + len = min(state->s.avail_in, (unsigned)dstlen); + memcpy(dst, state->s.next_in, len); + } + + return len; +} diff --git a/arch/powerpc/boot/gunzip_util.h b/arch/powerpc/boot/gunzip_util.h new file mode 100644 index 0000000..b3dfa6e --- /dev/null +++ b/arch/powerpc/boot/gunzip_util.h @@ -0,0 +1,45 @@ +/* + * Decompression convenience functions + * + * Copyright 2007 David Gibson, IBM Corporation. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#ifndef _PPC_BOOT_GUNZIP_UTIL_H_ +#define _PPC_BOOT_GUNZIP_UTIL_H_ + +#include "zlib.h" + +/* + * These functions are designed to make life easy for decompressing + * kernel images, initrd images or any other gzip compressed image, + * particularly if its useful to decompress part of the image (e.g. to + * examine headers) before decompressing the remainder. + * + * To use: + * - declare a gunzip_state structure + * - use gunzip_start() to initialize the state, associating it + * with a stream of compressed data + * - use gunzip_partial(), gunzip_exactly() and gunzip_discard() + * in any combination to extract pieces of data from the stream + * - Finally use gunzip_finish() to extract the tail of the + * compressed stream and wind up zlib + */ + +/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */ +#define GUNZIP_SCRATCH_SIZE 46912 + +struct gunzip_state { + z_stream s; + char scratch[46912]; +}; + +void gunzip_start(struct gunzip_state *state, void *src, int srclen); +int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen); +void gunzip_exactly(struct gunzip_state *state, void *dst, int len); +void gunzip_discard(struct gunzip_state *state, int len); +int gunzip_finish(struct gunzip_state *state, void *dst, int len); + +#endif /* _PPC_BOOT_GUNZIP_UTIL_H_ */ diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 6f6b50d..56b56a8 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -14,11 +14,10 @@ #include "elf.h" #include "page.h" #include "string.h" #include "stdio.h" -#include "zlib.h" #include "ops.h" +#include "gunzip_util.h" #include "flatdevtree.h" - -extern void flush_cache(void *, unsigned long); +#include "reg.h" extern char _start[]; extern char __bss_start[]; @@ -30,304 +29,173 @@ extern char _initrd_end[]; extern char _dtb_start[]; extern char _dtb_end[]; +static struct gunzip_state gzstate; + struct addr_range { - unsigned long addr; + void *addr; unsigned long size; - unsigned long memsize; }; -static struct addr_range vmlinux; -static struct addr_range vmlinuz; -static struct addr_range initrd; - -static unsigned long elfoffset; -static int is_64bit; - -/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */ -static char scratch[46912]; -static char elfheader[256]; typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); #undef DEBUG -#define HEAD_CRC 2 -#define EXTRA_FIELD 4 -#define ORIG_NAME 8 -#define COMMENT 0x10 -#define RESERVED 0xe0 - -static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +static struct addr_range prep_kernel(void) { - z_stream s; - int r, i, flags; - - /* skip header */ - i = 10; - flags = src[3]; - if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) { - printf("bad gzipped data\n\r"); - exit(); - } - if ((flags & EXTRA_FIELD) != 0) - i = 12 + src[10] + (src[11] << 8); - if ((flags & ORIG_NAME) != 0) - while (src[i++] != 0) - ; - if ((flags & COMMENT) != 0) - while (src[i++] != 0) - ; - if ((flags & HEAD_CRC) != 0) - i += 2; - if (i >= *lenp) { - printf("gunzip: ran out of data in header\n\r"); - exit(); - } + char elfheader[256]; + void *vmlinuz_addr = _vmlinux_start; + unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start; + void *addr = 0; + struct elf_info ei; + int len; - if (zlib_inflate_workspacesize() > sizeof(scratch)) { - printf("gunzip needs more mem\n"); - exit(); - } - memset(&s, 0, sizeof(s)); - s.workspace = scratch; - r = zlib_inflateInit2(&s, -MAX_WBITS); - if (r != Z_OK) { - printf("inflateInit2 returned %d\n\r", r); - exit(); - } - s.next_in = src + i; - s.avail_in = *lenp - i; - s.next_out = dst; - s.avail_out = dstlen; - r = zlib_inflate(&s, Z_FULL_FLUSH); - if (r != Z_OK && r != Z_STREAM_END) { - printf("inflate returned %d msg: %s\n\r", r, s.msg); - exit(); + /* gunzip the ELF header of the kernel */ + gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size); + gunzip_exactly(&gzstate, elfheader, sizeof(elfheader)); + + if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) + fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); + + if (platform_ops.image_hdr) + platform_ops.image_hdr(elfheader); + + /* We need to alloc the memsize: gzip will expand the kernel + * text/data, then possible rubbish we don't care about. But + * the kernel bss must be claimed (it will be zero'd by the + * kernel itself) + */ + printf("Allocating 0x%lx bytes for kernel ...\n\r", ei.memsize); + + if (platform_ops.vmlinux_alloc) { + addr = platform_ops.vmlinux_alloc(ei.memsize); + } else { + if ((unsigned long)_start < ei.memsize) + fatal("Insufficient memory for kernel at address 0!" + " (_start=%p)\n\r", _start); } - *lenp = s.next_out - (unsigned char *) dst; - zlib_inflateEnd(&s); -} -static int is_elf64(void *hdr) -{ - Elf64_Ehdr *elf64 = hdr; - Elf64_Phdr *elf64ph; - unsigned int i; - - if (!(elf64->e_ident[EI_MAG0] == ELFMAG0 && - elf64->e_ident[EI_MAG1] == ELFMAG1 && - elf64->e_ident[EI_MAG2] == ELFMAG2 && - elf64->e_ident[EI_MAG3] == ELFMAG3 && - elf64->e_ident[EI_CLASS] == ELFCLASS64 && - elf64->e_ident[EI_DATA] == ELFDATA2MSB && - elf64->e_type == ET_EXEC && - elf64->e_machine == EM_PPC64)) - return 0; - - elf64ph = (Elf64_Phdr *)((unsigned long)elf64 + - (unsigned long)elf64->e_phoff); - for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++) - if (elf64ph->p_type == PT_LOAD) - break; - if (i >= (unsigned int)elf64->e_phnum) - return 0; - - elfoffset = (unsigned long)elf64ph->p_offset; - vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset; - vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset; - - is_64bit = 1; - return 1; -} + /* Finally, gunzip the kernel */ + printf("gunzipping (0x%p <- 0x%p:0x%p)...", addr, + vmlinuz_addr, vmlinuz_addr+vmlinuz_size); + /* discard up to the actual load data */ + gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader)); + len = gunzip_finish(&gzstate, addr, ei.loadsize); + if (len != ei.loadsize) + fatal("ran out of data! only got 0x%x of 0x%lx bytes.\n\r", + len, ei.loadsize); + printf("done 0x%x bytes\n\r", len); -static int is_elf32(void *hdr) -{ - Elf32_Ehdr *elf32 = hdr; - Elf32_Phdr *elf32ph; - unsigned int i; - - if (!(elf32->e_ident[EI_MAG0] == ELFMAG0 && - elf32->e_ident[EI_MAG1] == ELFMAG1 && - elf32->e_ident[EI_MAG2] == ELFMAG2 && - elf32->e_ident[EI_MAG3] == ELFMAG3 && - elf32->e_ident[EI_CLASS] == ELFCLASS32 && - elf32->e_ident[EI_DATA] == ELFDATA2MSB && - elf32->e_type == ET_EXEC && - elf32->e_machine == EM_PPC)) - return 0; - - elf32 = (Elf32_Ehdr *)elfheader; - elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff); - for (i = 0; i < elf32->e_phnum; i++, elf32ph++) - if (elf32ph->p_type == PT_LOAD) - break; - if (i >= elf32->e_phnum) - return 0; - - elfoffset = elf32ph->p_offset; - vmlinux.size = elf32ph->p_filesz + elf32ph->p_offset; - vmlinux.memsize = elf32ph->p_memsz + elf32ph->p_offset; - return 1; + flush_cache(addr, ei.loadsize); + + return (struct addr_range){addr, ei.memsize}; } -static void prep_kernel(unsigned long a1, unsigned long a2) +static struct addr_range prep_initrd(struct addr_range vmlinux, void *chosen, + unsigned long initrd_addr, + unsigned long initrd_size) { - int len; - - vmlinuz.addr = (unsigned long)_vmlinux_start; - vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); - - /* gunzip the ELF header of the kernel */ - if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { - len = vmlinuz.size; - gunzip(elfheader, sizeof(elfheader), - (unsigned char *)vmlinuz.addr, &len); - } else - memcpy(elfheader, (const void *)vmlinuz.addr, - sizeof(elfheader)); - - if (!is_elf64(elfheader) && !is_elf32(elfheader)) { - printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); - exit(); + /* If we have an image attached to us, it overrides anything + * supplied by the loader. */ + if (_initrd_end > _initrd_start) { + printf("Attached initrd image at 0x%p-0x%p\n\r", + _initrd_start, _initrd_end); + initrd_addr = (unsigned long)_initrd_start; + initrd_size = _initrd_end - _initrd_start; + } else if (initrd_size > 0) { + printf("Using loader supplied ramdisk at 0x%lx-0x%lx\n\r", + initrd_addr, initrd_addr + initrd_size); } - if (platform_ops.image_hdr) - platform_ops.image_hdr(elfheader); - /* We need to alloc the memsize plus the file offset since gzip - * will expand the header (file offset), then the kernel, then - * possible rubbish we don't care about. But the kernel bss must - * be claimed (it will be zero'd by the kernel itself) - */ - printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); - vmlinux.addr = (unsigned long)malloc(vmlinux.memsize); - if (vmlinux.addr == 0) { - printf("Can't allocate memory for kernel image !\n\r"); - exit(); - } + /* If there's no initrd at all, we're done */ + if (! initrd_size) + return (struct addr_range){0, 0}; /* - * Now find the initrd - * - * First see if we have an image attached to us. If so - * allocate memory for it and copy it there. + * If the initrd is too low it will be clobbered when the + * kernel relocates to its final location. In this case, + * allocate a safer place and move it. */ - initrd.size = (unsigned long)(_initrd_end - _initrd_start); - initrd.memsize = initrd.size; - if (initrd.size > 0) { + if (initrd_addr < vmlinux.size) { + void *old_addr = (void *)initrd_addr; + printf("Allocating 0x%lx bytes for initrd ...\n\r", - initrd.size); - initrd.addr = (unsigned long)malloc((u32)initrd.size); - if (initrd.addr == 0) { - printf("Can't allocate memory for initial " - "ramdisk !\n\r"); - exit(); - } - printf("initial ramdisk moving 0x%lx <- 0x%lx " - "(0x%lx bytes)\n\r", initrd.addr, - (unsigned long)_initrd_start, initrd.size); - memmove((void *)initrd.addr, (void *)_initrd_start, - initrd.size); - printf("initrd head: 0x%lx\n\r", - *((unsigned long *)initrd.addr)); - } else if (a2 != 0) { - /* Otherwise, see if yaboot or another loader gave us an initrd */ - initrd.addr = a1; - initrd.memsize = initrd.size = a2; - printf("Using loader supplied initrd at 0x%lx (0x%lx bytes)\n\r", - initrd.addr, initrd.size); + initrd_size); + initrd_addr = (unsigned long)malloc(initrd_size); + if (! initrd_addr) + fatal("Can't allocate memory for initial " + "ramdisk !\n\r"); + printf("Relocating initrd 0x%lx <- 0x%p (0x%lx bytes)\n\r", + initrd_addr, old_addr, initrd_size); + memmove((void *)initrd_addr, old_addr, initrd_size); } - /* Eventually gunzip the kernel */ - if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { - printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", - vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); - len = vmlinuz.size; - gunzip((void *)vmlinux.addr, vmlinux.memsize, - (unsigned char *)vmlinuz.addr, &len); - printf("done 0x%lx bytes\n\r", len); - } else { - memmove((void *)vmlinux.addr,(void *)vmlinuz.addr, - vmlinuz.size); - } + printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd_addr)); - /* Skip over the ELF header */ -#ifdef DEBUG - printf("... skipping 0x%lx bytes of ELF header\n\r", - elfoffset); -#endif - vmlinux.addr += elfoffset; + /* Tell the kernel initrd address via device tree */ + setprop_val(chosen, "linux,initrd-start", (u32)(initrd_addr)); + setprop_val(chosen, "linux,initrd-end", (u32)(initrd_addr+initrd_size)); - flush_cache((void *)vmlinux.addr, vmlinux.size); + return (struct addr_range){(void *)initrd_addr, initrd_size}; } /* A buffer that may be edited by tools operating on a zImage binary so as to * edit the command line passed to vmlinux (by setting /chosen/bootargs). * The buffer is put in it's own section so that tools may locate it easier. */ -static char builtin_cmdline[COMMAND_LINE_SIZE] +static char cmdline[COMMAND_LINE_SIZE] __attribute__((__section__("__builtin_cmdline"))); -static void get_cmdline(char *buf, int size) +static void prep_cmdline(void *chosen) { - void *devp; - int len = strlen(builtin_cmdline); - - buf[0] = '\0'; - - if (len > 0) { /* builtin_cmdline overrides dt's /chosen/bootargs */ - len = min(len, size-1); - strncpy(buf, builtin_cmdline, len); - buf[len] = '\0'; - } - else if ((devp = finddevice("/chosen"))) - getprop(devp, "bootargs", buf, size); -} + if (cmdline[0] == '\0') + getprop(chosen, "bootargs", cmdline, COMMAND_LINE_SIZE-1); -static void set_cmdline(char *buf) -{ - void *devp; + printf("\n\rLinux/PowerPC load: %s", cmdline); + /* If possible, edit the command line */ + if (console_ops.edit_cmdline) + console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE); + printf("\n\r"); - if ((devp = finddevice("/chosen"))) - setprop(devp, "bootargs", buf, strlen(buf) + 1); + /* Put the command line back into the devtree for the kernel */ + setprop_str(chosen, "bootargs", cmdline); } struct platform_ops platform_ops; struct dt_ops dt_ops; struct console_ops console_ops; +struct loader_info loader_info; -void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) +void start(void) { + struct addr_range vmlinux, initrd; kernel_entry_t kentry; - char cmdline[COMMAND_LINE_SIZE]; unsigned long ft_addr = 0; + void *chosen; - memset(__bss_start, 0, _end - __bss_start); - memset(&platform_ops, 0, sizeof(platform_ops)); - memset(&dt_ops, 0, sizeof(dt_ops)); - memset(&console_ops, 0, sizeof(console_ops)); + /* Do this first, because malloc() could clobber the loader's + * command line. Only use the loader command line if a + * built-in command line wasn't set by an external tool */ + if ((loader_info.cmdline_len > 0) && (cmdline[0] == '\0')) + memmove(cmdline, loader_info.cmdline, + min(loader_info.cmdline_len, COMMAND_LINE_SIZE-1)); - if (platform_init(promptr, _dtb_start, _dtb_end)) - exit(); if (console_ops.open && (console_ops.open() < 0)) exit(); if (platform_ops.fixups) platform_ops.fixups(); printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", - _start, sp); + _start, get_sp()); - prep_kernel(a1, a2); + /* Ensure that the device tree has a /chosen node */ + chosen = finddevice("/chosen"); + if (!chosen) + chosen = create_node(NULL, "chosen"); - /* If cmdline came from zimage wrapper or if we can edit the one - * in the dt, print it out and edit it, if possible. - */ - if ((strlen(builtin_cmdline) > 0) || console_ops.edit_cmdline) { - get_cmdline(cmdline, COMMAND_LINE_SIZE); - printf("\n\rLinux/PowerPC load: %s", cmdline); - if (console_ops.edit_cmdline) - console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE); - printf("\n\r"); - set_cmdline(cmdline); - } + vmlinux = prep_kernel(); + initrd = prep_initrd(vmlinux, chosen, + loader_info.initrd_addr, loader_info.initrd_size); + prep_cmdline(chosen); printf("Finalizing device tree..."); if (dt_ops.finalize) @@ -335,7 +203,7 @@ void start(unsigned long a1, unsigned lo if (ft_addr) printf(" flat tree at 0x%lx\n\r", ft_addr); else - printf(" using OF tree (promptr=%p)\n\r", promptr); + printf(" using OF tree (promptr=%p)\n\r", loader_info.promptr); if (console_ops.close) console_ops.close(); @@ -344,10 +212,9 @@ void start(unsigned long a1, unsigned lo if (ft_addr) kentry(ft_addr, 0, NULL); else - /* XXX initrd addr/size should be passed in properties */ - kentry(initrd.addr, initrd.size, promptr); + kentry((unsigned long)initrd.addr, initrd.size, + loader_info.promptr); - /* console closed so printf below may not work */ - printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); - exit(); + /* console closed so printf in fatal below may not work */ + fatal("Error: Linux kernel returned to zImage boot wrapper!\n\r"); } diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c index 1ffe72e..f8f1b2f 100644 --- a/arch/powerpc/boot/ns16550.c +++ b/arch/powerpc/boot/ns16550.c @@ -55,10 +55,15 @@ static u8 ns16550_tstc(void) int ns16550_console_init(void *devp, struct serial_console_data *scdp) { int n; + unsigned long reg_phys; n = getprop(devp, "virtual-reg", ®_base, sizeof(reg_base)); - if (n != sizeof(reg_base)) - return -1; + if (n != sizeof(reg_base)) { + if (!dt_xlate_reg(devp, 0, ®_phys, NULL)) + return -1; + + reg_base = (void *)reg_phys; + } n = getprop(devp, "reg-shift", ®_shift, sizeof(reg_shift)); if (n != sizeof(reg_shift)) diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index 0182f38..d16ee3e 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c @@ -173,7 +173,7 @@ static void *claim(unsigned long virt, u return (void *) virt; } -static void *of_try_claim(u32 size) +static void *of_try_claim(unsigned long size) { unsigned long addr = 0; @@ -208,6 +208,16 @@ static void of_image_hdr(const void *hdr } } +static void *of_vmlinux_alloc(unsigned long size) +{ + void *p = malloc(size); + + if (!p) + fatal("Can't allocate memory for kernel image!\n\r"); + + return p; +} + static void of_exit(void) { call_prom("exit", 0, 0); @@ -256,11 +266,12 @@ static void of_console_write(char *buf, call_prom("write", 3, 1, of_stdout_handle, buf, len); } -int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end) +void platform_init(unsigned long a1, unsigned long a2, void *promptr) { platform_ops.image_hdr = of_image_hdr; platform_ops.malloc = of_try_claim; platform_ops.exit = of_exit; + platform_ops.vmlinux_alloc = of_vmlinux_alloc; dt_ops.finddevice = of_finddevice; dt_ops.getprop = of_getprop; @@ -270,5 +281,9 @@ int platform_init(void *promptr, char *d console_ops.write = of_console_write; prom = (int (*)(void *))promptr; - return 0; + loader_info.promptr = promptr; + if (a1 && a2 && a2 != 0xdeadbeef) { + loader_info.initrd_addr = a1; + loader_info.initrd_size = a2; + } } diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 8abb651..73bd47a 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -11,7 +11,9 @@ #ifndef _PPC_BOOT_OPS_H_ #define _PPC_BOOT_OPS_H_ +#include #include "types.h" +#include "string.h" #define COMMAND_LINE_SIZE 512 #define MAX_PATH_LEN 256 @@ -21,10 +23,11 @@ #define MAX_PROP_LEN 256 /* What should struct platform_ops { void (*fixups)(void); void (*image_hdr)(const void *); - void * (*malloc)(u32 size); + void * (*malloc)(unsigned long size); void (*free)(void *ptr); void * (*realloc)(void *ptr, unsigned long size); void (*exit)(void); + void * (*vmlinux_alloc)(unsigned long size); }; extern struct platform_ops platform_ops; @@ -35,6 +38,12 @@ struct dt_ops { const int buflen); int (*setprop)(const void *phandle, const char *name, const void *buf, const int buflen); + void *(*get_parent)(const void *phandle); + /* The node must not already exist. */ + void *(*create_node)(const void *parent, const char *name); + void *(*find_node_by_prop_value)(const void *prev, + const char *propname, + const char *propval, int proplen); unsigned long (*finalize)(void); }; extern struct dt_ops dt_ops; @@ -58,13 +67,23 @@ struct serial_console_data { void (*close)(void); }; -int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end); +struct loader_info { + void *promptr; + unsigned long initrd_addr, initrd_size; + char *cmdline; + int cmdline_len; +}; +extern struct loader_info loader_info; + +void start(void); int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device); int serial_console_init(void); int ns16550_console_init(void *devp, struct serial_console_data *scdp); -void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, - u32 max_allocs); - +void *simple_alloc_init(char *base, unsigned long heap_size, + unsigned long granularity, unsigned long max_allocs); +extern void flush_cache(void *, unsigned long); +int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size); +int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr); static inline void *finddevice(const char *name) { @@ -76,12 +95,76 @@ static inline int getprop(void *devp, co return (dt_ops.getprop) ? dt_ops.getprop(devp, name, buf, buflen) : -1; } -static inline int setprop(void *devp, const char *name, void *buf, int buflen) +static inline int setprop(void *devp, const char *name, + const void *buf, int buflen) { return (dt_ops.setprop) ? dt_ops.setprop(devp, name, buf, buflen) : -1; } +#define setprop_val(devp, name, val) \ + do { \ + typeof(val) x = (val); \ + setprop((devp), (name), &x, sizeof(x)); \ + } while (0) + +static inline int setprop_str(void *devp, const char *name, const char *buf) +{ + if (dt_ops.setprop) + return dt_ops.setprop(devp, name, buf, strlen(buf) + 1); + + return -1; +} + +static inline void *get_parent(const char *devp) +{ + return dt_ops.get_parent ? dt_ops.get_parent(devp) : NULL; +} + +static inline void *create_node(const void *parent, const char *name) +{ + return dt_ops.create_node ? dt_ops.create_node(parent, name) : NULL; +} + -static inline void *malloc(u32 size) +static inline void *find_node_by_prop_value(const void *prev, + const char *propname, + const char *propval, int proplen) +{ + if (dt_ops.find_node_by_prop_value) + return dt_ops.find_node_by_prop_value(prev, propname, + propval, proplen); + + return NULL; +} + +static inline void *find_node_by_prop_value_str(const void *prev, + const char *propname, + const char *propval) +{ + return find_node_by_prop_value(prev, propname, propval, + strlen(propval) + 1); +} + +static inline void *find_node_by_devtype(const void *prev, + const char *type) +{ + return find_node_by_prop_value_str(prev, "device_type", type); +} + +void dt_fixup_memory(u64 start, u64 size); +void dt_fixup_cpu_clocks(u32 cpufreq, u32 tbfreq, u32 busfreq); +void dt_fixup_clock(const char *path, u32 freq); +void __dt_fixup_mac_addresses(u32 startindex, ...); +#define dt_fixup_mac_addresses(...) \ + __dt_fixup_mac_addresses(0, __VA_ARGS__, NULL) + + +static inline void *find_node_by_linuxphandle(const u32 linuxphandle) +{ + return find_node_by_prop_value(NULL, "linux,phandle", + (char *)&linuxphandle, sizeof(u32)); +} + +static inline void *malloc(unsigned long size) { return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL; } @@ -98,5 +181,11 @@ static inline void exit(void) platform_ops.exit(); for(;;); } +#define fatal(args...) { printf(args); exit(); } + + +#define BSS_STACK(size) \ + static char _bss_stack[size]; \ + void *_platform_stack_top = _bss_stack + sizeof(_bss_stack); #endif /* _PPC_BOOT_OPS_H_ */ diff --git a/arch/powerpc/boot/ppcboot.h b/arch/powerpc/boot/ppcboot.h new file mode 100644 index 0000000..5290ff2 --- /dev/null +++ b/arch/powerpc/boot/ppcboot.h @@ -0,0 +1,108 @@ +/* + * This interface is used for compatibility with old U-boots *ONLY*. + * Please do not imitate or extend this. + */ + +/* + * (C) Copyright 2000, 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __PPCBOOT_H__ +#define __PPCBOOT_H__ + +/* + * Board information passed to kernel from PPCBoot + * + * include/asm-ppc/ppcboot.h + */ + +#include "types.h" + +typedef struct bd_info { + unsigned long bi_memstart; /* start of DRAM memory */ + unsigned long bi_memsize; /* size of DRAM memory in bytes */ + unsigned long bi_flashstart; /* start of FLASH memory */ + unsigned long bi_flashsize; /* size of FLASH memory */ + unsigned long bi_flashoffset; /* reserved area for startup monitor */ + unsigned long bi_sramstart; /* start of SRAM memory */ + unsigned long bi_sramsize; /* size of SRAM memory */ +#if defined(TARGET_8xx) || defined(TARGET_CPM2) || defined(TARGET_85xx) ||\ + defined(TARGET_83xx) + unsigned long bi_immr_base; /* base of IMMR register */ +#endif +#if defined(TARGET_PPC_MPC52xx) + unsigned long bi_mbar_base; /* base of internal registers */ +#endif + unsigned long bi_bootflags; /* boot / reboot flag (for LynxOS) */ + unsigned long bi_ip_addr; /* IP Address */ + unsigned char bi_enetaddr[6]; /* Ethernet address */ + unsigned short bi_ethspeed; /* Ethernet speed in Mbps */ + unsigned long bi_intfreq; /* Internal Freq, in MHz */ + unsigned long bi_busfreq; /* Bus Freq, in MHz */ +#if defined(TARGET_CPM2) + unsigned long bi_cpmfreq; /* CPM_CLK Freq, in MHz */ + unsigned long bi_brgfreq; /* BRG_CLK Freq, in MHz */ + unsigned long bi_sccfreq; /* SCC_CLK Freq, in MHz */ + unsigned long bi_vco; /* VCO Out from PLL, in MHz */ +#endif +#if defined(TARGET_PPC_MPC52xx) + unsigned long bi_ipbfreq; /* IPB Bus Freq, in MHz */ + unsigned long bi_pcifreq; /* PCI Bus Freq, in MHz */ +#endif + unsigned long bi_baudrate; /* Console Baudrate */ +#if defined(TARGET_4xx) + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[32]; /* Version of the ROM (IBM) */ + unsigned int bi_procfreq; /* CPU (Internal) Freq, in Hz */ + unsigned int bi_plb_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ + unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ +#endif +#if defined(TARGET_HYMOD) + hymod_conf_t bi_hymod_conf; /* hymod configuration information */ +#endif +#if defined(TARGET_EVB64260) || defined(TARGET_405EP) || defined(TARGET_44x) || \ + defined(TARGET_85xx) || defined(TARGET_83xx) + /* second onboard ethernet port */ + unsigned char bi_enet1addr[6]; +#define HAVE_ENET1ADDR +#endif +#if defined(TARGET_EVB64260) || defined(TARGET_440GX) || defined(TARGET_85xx) + /* third onboard ethernet ports */ + unsigned char bi_enet2addr[6]; +#define HAVE_ENET2ADDR +#endif +#if defined(TARGET_440GX) + /* fourth onboard ethernet ports */ + unsigned char bi_enet3addr[6]; +#define HAVE_ENET3ADDR +#endif +#if defined(TARGET_4xx) + unsigned int bi_opbfreq; /* OB clock in Hz */ + int bi_iic_fast[2]; /* Use fast i2c mode */ +#endif +#if defined(TARGET_440GX) + int bi_phynum[4]; /* phy mapping */ + int bi_phymode[4]; /* phy mode */ +#endif +} bd_t; + +#define bi_tbfreq bi_intfreq + +#endif /* __PPCBOOT_H__ */ diff --git a/arch/powerpc/boot/reg.h b/arch/powerpc/boot/reg.h new file mode 100644 index 0000000..d3cd9ee --- /dev/null +++ b/arch/powerpc/boot/reg.h @@ -0,0 +1,22 @@ +#ifndef _PPC_BOOT_REG_H +#define _PPC_BOOT_REG_H +/* + * Copyright 2007 Davud Gibson, IBM Corporation. + * + * 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. + */ + +static inline u32 mfpvr(void) +{ + u32 pvr; + asm volatile ("mfpvr %0" : "=r"(pvr)); + return pvr; +} + +register void *__stack_pointer asm("r1"); +#define get_sp() (__stack_pointer) + +#endif /* _PPC_BOOT_REG_H */ diff --git a/arch/powerpc/boot/simple_alloc.c b/arch/powerpc/boot/simple_alloc.c index cfe3a75..65ec135 100644 --- a/arch/powerpc/boot/simple_alloc.c +++ b/arch/powerpc/boot/simple_alloc.c @@ -19,24 +19,24 @@ #define ENTRY_BEEN_USED 0x01 #define ENTRY_IN_USE 0x02 static struct alloc_info { - u32 flags; - u32 base; - u32 size; + unsigned long flags; + unsigned long base; + unsigned long size; } *alloc_tbl; -static u32 tbl_entries; -static u32 alloc_min; -static u32 next_base; -static u32 space_left; +static unsigned long tbl_entries; +static unsigned long alloc_min; +static unsigned long next_base; +static unsigned long space_left; /* * First time an entry is used, its base and size are set. * An entry can be freed and re-malloc'd but its base & size don't change. * Should be smart enough for needs of bootwrapper. */ -static void *simple_malloc(u32 size) +static void *simple_malloc(unsigned long size) { - u32 i; + unsigned long i; struct alloc_info *p = alloc_tbl; if (size == 0) @@ -67,13 +67,14 @@ err_out: static struct alloc_info *simple_find_entry(void *ptr) { - u32 i; + unsigned long i; struct alloc_info *p = alloc_tbl; for (i=0; iflags & ENTRY_BEEN_USED)) break; - if ((p->flags & ENTRY_IN_USE) && (p->base == (u32)ptr)) + if ((p->flags & ENTRY_IN_USE) && + (p->base == (unsigned long)ptr)) return p; } return NULL; @@ -122,10 +123,10 @@ static void *simple_realloc(void *ptr, u * Returns addr of first byte after heap so caller can see if it took * too much space. If so, change args & try again. */ -void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, - u32 max_allocs) +void *simple_alloc_init(char *base, unsigned long heap_size, + unsigned long granularity, unsigned long max_allocs) { - u32 heap_base, tbl_size; + unsigned long heap_base, tbl_size; heap_size = _ALIGN_UP(heap_size, granularity); alloc_min = granularity; @@ -136,7 +137,7 @@ void *simple_alloc_init(char *base, u32 alloc_tbl = (struct alloc_info *)_ALIGN_UP((unsigned long)base, 8); memset(alloc_tbl, 0, tbl_size); - heap_base = _ALIGN_UP((u32)alloc_tbl + tbl_size, alloc_min); + heap_base = _ALIGN_UP((unsigned long)alloc_tbl + tbl_size, alloc_min); next_base = heap_base; space_left = heap_size; diff --git a/arch/powerpc/boot/stdio.h b/arch/powerpc/boot/stdio.h index 73b8a91..adffc58 100644 --- a/arch/powerpc/boot/stdio.h +++ b/arch/powerpc/boot/stdio.h @@ -7,11 +7,12 @@ #define ENOMEM 12 /* Out of Memory */ #define EINVAL 22 /* Invalid argument */ #define ENOSPC 28 /* No space left on device */ -extern int printf(const char *fmt, ...); +extern int printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); #define fprintf(fmt, args...) printf(args) -extern int sprintf(char *buf, const char *fmt, ...); +extern int sprintf(char *buf, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); extern int vsprintf(char *buf, const char *fmt, va_list args); diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index 024e4d4..5cedd90 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -29,6 +29,7 @@ initrd= dtb= dts= cacheit= +gzip=.gz # cross-compilation prefix CROSS= @@ -42,7 +43,7 @@ tmpdir=. usage() { echo 'Usage: wrapper [-o output] [-p platform] [-i initrd]' >&2 echo ' [-d devtree] [-s tree.dts] [-c] [-C cross-prefix]' >&2 - echo ' [-D datadir] [-W workingdir] [vmlinux]' >&2 + echo ' [-D datadir] [-W workingdir] [--no-gzip] [vmlinux]' >&2 exit 1 } @@ -91,6 +92,9 @@ while [ "$#" -gt 0 ]; do [ "$#" -gt 0 ] || usage tmpdir="$1" ;; + --no-gzip) + gzip= + ;; -?) usage ;; @@ -137,31 +141,44 @@ miboot|uboot) ksection=image isection=initrd ;; +cuboot*) + gzip= + ;; esac vmz="$tmpdir/`basename \"$kernel\"`.$ext" -if [ -z "$cacheit" -o ! -f "$vmz.gz" -o "$vmz.gz" -ot "$kernel" ]; then +if [ -z "$cacheit" -o ! -f "$vmz$gzip" -o "$vmz$gzip" -ot "$kernel" ]; then ${CROSS}objcopy $objflags "$kernel" "$vmz.$$" - gzip -f -9 "$vmz.$$" + + if [ -n "$gzip" ]; then + gzip -f -9 "$vmz.$$" + fi + if [ -n "$cacheit" ]; then - mv -f "$vmz.$$.gz" "$vmz.gz" + mv -f "$vmz.$$$gzip" "$vmz$gzip" else vmz="$vmz.$$" fi fi +vmz="$vmz$gzip" + case "$platform" in -uboot) - rm -f "$ofile" +uboot|cuboot*) version=`${CROSS}strings "$kernel" | grep '^Linux version [-0-9.]' | \ cut -d' ' -f3` if [ -n "$version" ]; then version="-n Linux-$version" fi +esac + +case "$platform" in +uboot) + rm -f "$ofile" mkimage -A ppc -O linux -T kernel -C gzip -a 00000000 -e 00000000 \ - $version -d "$vmz.gz" "$ofile" + $version -d "$vmz" "$ofile" if [ -z "$cacheit" ]; then - rm -f $vmz.gz + rm -f "$vmz" fi exit 0 ;; @@ -173,9 +190,9 @@ addsec() { --set-section-flags=$3=contents,alloc,load,readonly,data } -addsec $tmp "$vmz.gz" $ksection $object/empty.o +addsec $tmp "$vmz" $ksection $object/empty.o if [ -z "$cacheit" ]; then - rm -f "$vmz.gz" + rm -f "$vmz" fi if [ -n "$initrd" ]; then @@ -191,7 +208,7 @@ fi if [ "$platform" != "miboot" ]; then ${CROSS}ld -m elf32ppc -T $lds -o "$ofile" \ - $object/crt0.o $platformo $tmp $object/wrapper.a + $platformo $tmp $object/wrapper.a rm $tmp fi @@ -201,7 +218,19 @@ pseries|chrp) $object/addnote "$ofile" ;; pmaccoff) - ${CROSS}objcopy -O aixcoff-rs6000 --set-start 0x500000 "$ofile" + entry=`objdump -f "$ofile" | grep '^start address ' | \ + cut -d' ' -f3` + ${CROSS}objcopy -O aixcoff-rs6000 --set-start "$entry" "$ofile" $object/hack-coff "$ofile" ;; +cuboot*) + base=`${CROSS}nm "$ofile" | grep ' _start$' | cut -d' ' -f1` + entry=`${CROSS}objdump -f "$ofile" | grep '^start address ' | \ + cut -d' ' -f3` + mv "$ofile" "$ofile".elf + ${CROSS}objcopy -O binary "$ofile".elf "$ofile".bin + gzip -f -9 "$ofile".bin + mkimage -A ppc -O linux -T kernel -C gzip -a "$base" -e "$entry" \ + $version -d "$ofile".bin.gz "$ofile" + ;; esac diff --git a/arch/powerpc/boot/zImage.coff.lds.S b/arch/powerpc/boot/zImage.coff.lds.S index a360905..fe87a90 100644 --- a/arch/powerpc/boot/zImage.coff.lds.S +++ b/arch/powerpc/boot/zImage.coff.lds.S @@ -1,5 +1,6 @@ OUTPUT_ARCH(powerpc:common) -ENTRY(_start) +ENTRY(_zimage_start_opd) +EXTERN(_zimage_start_opd) SECTIONS { . = (5*1024*1024); diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S index 4be3c64..f6e380f 100644 --- a/arch/powerpc/boot/zImage.lds.S +++ b/arch/powerpc/boot/zImage.lds.S @@ -1,5 +1,6 @@ OUTPUT_ARCH(powerpc:common) ENTRY(_zimage_start) +EXTERN(_zimage_start) SECTIONS { . = (4*1024*1024); diff --git a/arch/powerpc/configs/cell_defconfig b/arch/powerpc/configs/cell_defconfig index cf7e316..6061e5f 100644 --- a/arch/powerpc/configs/cell_defconfig +++ b/arch/powerpc/configs/cell_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.21-rc3 -# Fri Mar 9 23:34:53 2007 +# Linux kernel version: 2.6.21-rc6 +# Mon Apr 23 20:46:48 2007 # CONFIG_PPC64=y CONFIG_64BIT=y @@ -139,11 +139,31 @@ # CONFIG_PPC_MPC5200 is not set # CONFIG_PPC_PMAC is not set # CONFIG_PPC_MAPLE is not set # CONFIG_PPC_PASEMI is not set +CONFIG_PPC_CELLEB=y +CONFIG_PPC_PS3=y + +# +# PS3 Platform Options +# +# CONFIG_PS3_ADVANCED is not set +CONFIG_PS3_HTAB_SIZE=20 +# CONFIG_PS3_DYNAMIC_DMA is not set +CONFIG_PS3_USE_LPAR_ADDR=y +CONFIG_PS3_VUART=y +CONFIG_PS3_PS3AV=y +CONFIG_PS3_SYS_MANAGER=y CONFIG_PPC_CELL=y CONFIG_PPC_CELL_NATIVE=y CONFIG_PPC_IBM_CELL_BLADE=y -CONFIG_PPC_PS3=y -CONFIG_PPC_CELLEB=y + +# +# Cell Broadband Engine options +# +CONFIG_SPU_FS=m +CONFIG_SPU_BASE=y +CONFIG_CBE_RAS=y +CONFIG_CBE_THERM=m +CONFIG_CBE_CPUFREQ=m CONFIG_PPC_NATIVE=y CONFIG_UDBG_RTAS_CONSOLE=y CONFIG_PPC_UDBG_BEAT=y @@ -175,26 +195,6 @@ # CONFIG_WANT_EARLY_SERIAL is not set CONFIG_MPIC=y # -# Cell Broadband Engine options -# -CONFIG_SPU_FS=m -CONFIG_SPU_BASE=y -CONFIG_CBE_RAS=y -CONFIG_CBE_THERM=m -CONFIG_CBE_CPUFREQ=m - -# -# PS3 Platform Options -# -# CONFIG_PS3_ADVANCED is not set -CONFIG_PS3_HTAB_SIZE=20 -# CONFIG_PS3_DYNAMIC_DMA is not set -CONFIG_PS3_USE_LPAR_ADDR=y -CONFIG_PS3_VUART=y -CONFIG_PS3_PS3AV=y -CONFIG_PS3_SYS_MANAGER=y - -# # Kernel options # # CONFIG_HZ_100 is not set @@ -534,7 +534,6 @@ CONFIG_BLK_DEV_GENERIC=y # CONFIG_BLK_DEV_OPTI621 is not set CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_IDEDMA_FORCED is not set -CONFIG_IDEDMA_PCI_AUTO=y # CONFIG_IDEDMA_ONLYDISK is not set CONFIG_BLK_DEV_AEC62XX=y # CONFIG_BLK_DEV_ALI15X3 is not set @@ -561,11 +560,10 @@ # CONFIG_BLK_DEV_SLC90E66 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_VIA82CXXX is not set # CONFIG_BLK_DEV_TC86C001 is not set -CONFIG_BLK_DEV_IDE_CELLEB=y +CONFIG_BLK_DEV_CELLEB=y # CONFIG_IDE_ARM is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set -CONFIG_IDEDMA_AUTO=y # CONFIG_BLK_DEV_HD is not set # @@ -937,7 +935,7 @@ CONFIG_UNIX98_PTYS=y # CONFIG_LEGACY_PTYS is not set CONFIG_HVC_DRIVER=y CONFIG_HVC_RTAS=y -# CONFIG_HVC_BEAT is not set +CONFIG_HVC_BEAT=y # # IPMI @@ -1482,6 +1480,8 @@ # # Distributed Lock Manager # # CONFIG_DLM is not set +# CONFIG_UCC_SLOW is not set +# CONFIG_UCC_FAST is not set # # Library routines @@ -1540,6 +1540,7 @@ # CONFIG_RCU_TORTURE_TEST is not set # CONFIG_FAULT_INJECTION is not set # CONFIG_DEBUG_STACKOVERFLOW is not set # CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_PAGEALLOC is not set CONFIG_DEBUGGER=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index 7724847..3ccf19d 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig @@ -143,7 +143,7 @@ CONFIG_PPC_NATIVE=y CONFIG_U3_DART=y # CONFIG_PPC_RTAS is not set # CONFIG_MMIO_NVRAM is not set -CONFIG_MPIC_BROKEN_U3=y +CONFIG_MPIC_U3_HT_IRQS=y # CONFIG_PPC_MPC106 is not set CONFIG_PPC_970_NAP=y # CONFIG_PPC_INDIRECT_IO is not set diff --git a/arch/powerpc/configs/maple_defconfig b/arch/powerpc/configs/maple_defconfig index de97f2f..15366f0 100644 --- a/arch/powerpc/configs/maple_defconfig +++ b/arch/powerpc/configs/maple_defconfig @@ -146,7 +146,7 @@ # CONFIG_RTAS_ERROR_LOGGING is not set CONFIG_RTAS_PROC=y # CONFIG_RTAS_FLASH is not set # CONFIG_MMIO_NVRAM is not set -CONFIG_MPIC_BROKEN_U3=y +CONFIG_MPIC_U3_HT_IRQS=y # CONFIG_PPC_MPC106 is not set CONFIG_PPC_970_NAP=y # CONFIG_PPC_INDIRECT_IO is not set diff --git a/arch/powerpc/configs/mpc832x_rdb_defconfig b/arch/powerpc/configs/mpc832x_rdb_defconfig new file mode 100644 index 0000000..56fc0a8 --- /dev/null +++ b/arch/powerpc/configs/mpc832x_rdb_defconfig @@ -0,0 +1,1292 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.21-rc3 +# Mon Mar 12 17:32:19 2007 +# +# CONFIG_PPC64 is not set +CONFIG_PPC32=y +CONFIG_PPC_MERGE=y +CONFIG_MMU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_IRQ_PER_CPU=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_ILOG2_U32=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_PPC=y +CONFIG_EARLY_PRINTK=y +CONFIG_GENERIC_NVRAM=y +CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_PPC_OF=y +CONFIG_PPC_UDBG_16550=y +# CONFIG_GENERIC_TBSYNC is not set +CONFIG_AUDIT_ARCH=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFAULT_UIMAGE=y + +# +# Processor support +# +# CONFIG_CLASSIC32 is not set +# CONFIG_PPC_82xx is not set +CONFIG_PPC_83xx=y +# CONFIG_PPC_85xx is not set +# CONFIG_PPC_86xx is not set +# CONFIG_PPC_8xx is not set +# CONFIG_40x is not set +# CONFIG_44x is not set +# CONFIG_E200 is not set +CONFIG_6xx=y +CONFIG_83xx=y +CONFIG_PPC_FPU=y +# CONFIG_PPC_DCR_NATIVE is not set +# CONFIG_PPC_DCR_MMIO is not set +CONFIG_PPC_STD_MMU=y +CONFIG_PPC_STD_MMU_32=y +# CONFIG_SMP is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_IPC_NS is not set +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_UTS_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +# CONFIG_EPOLL is not set +CONFIG_SHMEM=y +CONFIG_SLAB=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_KMOD is not set + +# +# Block layer +# +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" +CONFIG_QUICC_ENGINE=y +CONFIG_PPC_GEN550=y +# CONFIG_WANT_EARLY_SERIAL is not set + +# +# Platform support +# +# CONFIG_MPC8313_RDB is not set +# CONFIG_MPC832x_MDS is not set +CONFIG_MPC832x_RDB=y +# CONFIG_MPC834x_MDS is not set +# CONFIG_MPC834x_ITX is not set +# CONFIG_MPC836x_MDS is not set +CONFIG_PPC_MPC832x=y +# CONFIG_MPIC is not set + +# +# Kernel options +# +# CONFIG_HIGHMEM is not set +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_MATH_EMULATION=y +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_ARCH_POPULATES_NODE_MAP=y +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_PROC_DEVICETREE=y +# CONFIG_CMDLINE_BOOL is not set +# CONFIG_PM is not set +CONFIG_SECCOMP=y +CONFIG_ISA_DMA_API=y + +# +# Bus options +# +CONFIG_ZONE_DMA=y +CONFIG_GENERIC_ISA_DMA=y +# CONFIG_MPIC_WEIRD is not set +# CONFIG_PPC_I8259 is not set +CONFIG_PPC_INDIRECT_PCI=y +CONFIG_FSL_SOC=y +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +# CONFIG_PCIEPORTBUS is not set + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set + +# +# Advanced setup +# +# CONFIG_ADVANCED_OPTIONS is not set + +# +# Default settings for advanced configuration options are used +# +CONFIG_HIGHMEM_START=0xfe000000 +CONFIG_LOWMEM_SIZE=0x30000000 +CONFIG_KERNEL_START=0xc0000000 +CONFIG_TASK_SIZE=0x80000000 +CONFIG_BOOT_LOAD=0x00800000 + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_NETDEBUG is not set +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_SYS_HYPERVISOR is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +# CONFIG_PNPACPI is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=32768 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# Misc devices +# +# CONFIG_SGI_IOC4 is not set +# CONFIG_TIFM_CORE is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set + +# +# SCSI low-level drivers +# +# CONFIG_ISCSI_TCP is not set +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_LPFC is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_SRP is not set + +# +# Serial ATA (prod) and Parallel ATA (experimental) drivers +# +# CONFIG_ATA is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_SPI is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + +# +# Macintosh device drivers +# +# CONFIG_MAC_EMUMOUSEBTN is not set +# CONFIG_WINDFARM is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_CASSINI is not set +# CONFIG_NET_VENDOR_3COM is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 is not set +# CONFIG_NET_PCI is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +CONFIG_E1000=y +# CONFIG_E1000_NAPI is not set +# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SIS190 is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set +# CONFIG_BNX2 is not set +# CONFIG_GIANFAR is not set +CONFIG_UCC_GETH=y +CONFIG_UGETH_NAPI=y +# CONFIG_UGETH_MAGIC_PACKET is not set +# CONFIG_UGETH_FILTERING is not set +# CONFIG_UGETH_TX_ON_DEMOND is not set +# CONFIG_QLA3XXX is not set +# CONFIG_ATL1 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NETXEN_NIC is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_UARTLITE is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_OF_PLATFORM is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_83xx_WDT=y + +# +# PCI-based Watchdog Cards +# +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_WDTPCI is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_HW_RANDOM=y +# CONFIG_NVRAM is not set +CONFIG_GEN_RTC=y +# CONFIG_GEN_RTC_X is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_PIIX4 is not set +CONFIG_I2C_MPC=y +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PASEMI is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_M41T00 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Hardware Monitoring support +# +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set +# CONFIG_USB_DABUSB is not set + +# +# Graphics support +# +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_FB is not set +# CONFIG_FB_IBM_GXT4500 is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# HID Devices +# +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EHCI_BIG_ENDIAN_MMIO is not set +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PPC_OF=y +CONFIG_USB_OHCI_HCD_PPC_OF_BE=y +# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set +CONFIG_USB_OHCI_HCD_PCI=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Input Devices +# +# CONFIG_USB_HID is not set + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_ACECAD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_TOUCHSCREEN is not set +# CONFIG_USB_YEALINK is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_APPLETOUCH is not set +# CONFIG_USB_GTCO is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET_MII is not set +# CONFIG_USB_USBNET is not set +CONFIG_USB_MON=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# LED devices +# +# CONFIG_NEW_LEDS is not set + +# +# LED drivers +# + +# +# LED Triggers +# + +# +# InfiniBand support +# +# CONFIG_INFINIBAND is not set + +# +# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) +# + +# +# Real Time Clock +# +# CONFIG_RTC_CLASS is not set + +# +# DMA Engine support +# +# CONFIG_DMA_ENGINE is not set + +# +# DMA Clients +# + +# +# DMA Devices +# + +# +# Auxiliary Display support +# + +# +# Virtualization +# + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +CONFIG_LDM_PARTITION=y +# CONFIG_LDM_DEBUG is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +CONFIG_NLS_CODEPAGE_932=y +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +CONFIG_NLS_ISO8859_8=y +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Distributed Lock Manager +# +# CONFIG_DLM is not set + +# +# QE Options +# +CONFIG_UCC_SLOW=y +CONFIG_UCC_FAST=y +CONFIG_UCC=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y + +# +# Instrumentation Support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_BOOTX_TEXT is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set +# CONFIG_PPC_EARLY_DEBUG is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_MANAGER=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Hardware crypto devices +# diff --git a/arch/powerpc/configs/mpc8544_ds_defconfig b/arch/powerpc/configs/mpc8544_ds_defconfig new file mode 100644 index 0000000..b563513 --- /dev/null +++ b/arch/powerpc/configs/mpc8544_ds_defconfig @@ -0,0 +1,1077 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.21-rc3 +# Mon Mar 19 17:18:49 2007 +# +# CONFIG_PPC64 is not set +CONFIG_PPC32=y +CONFIG_PPC_MERGE=y +CONFIG_MMU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_IRQ_PER_CPU=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_ILOG2_U32=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_PPC=y +CONFIG_EARLY_PRINTK=y +CONFIG_GENERIC_NVRAM=y +CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_PPC_OF=y +CONFIG_PPC_UDBG_16550=y +# CONFIG_GENERIC_TBSYNC is not set +CONFIG_AUDIT_ARCH=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFAULT_UIMAGE=y + +# +# Processor support +# +# CONFIG_CLASSIC32 is not set +# CONFIG_PPC_82xx is not set +# CONFIG_PPC_83xx is not set +CONFIG_PPC_85xx=y +# CONFIG_PPC_86xx is not set +# CONFIG_PPC_8xx is not set +# CONFIG_40x is not set +# CONFIG_44x is not set +# CONFIG_E200 is not set +CONFIG_85xx=y +CONFIG_E500=y +# CONFIG_PPC_DCR_NATIVE is not set +# CONFIG_PPC_DCR_MMIO is not set +CONFIG_BOOKE=y +CONFIG_FSL_BOOKE=y +# CONFIG_PHYS_64BIT is not set +# CONFIG_SPE is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_IPC_NS=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_UTS_NS is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SHMEM=y +CONFIG_SLAB=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# Block layer +# +CONFIG_BLOCK=y +CONFIG_LBD=y +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_WANT_EARLY_SERIAL is not set + +# +# Platform support +# +# CONFIG_MPC8540_ADS is not set +# CONFIG_MPC8560_ADS is not set +# CONFIG_MPC85xx_CDS is not set +# CONFIG_MPC85xx_MDS is not set +CONFIG_MPC8544_DS=y +CONFIG_MPC85xx=y +CONFIG_PPC_INDIRECT_PCI_BE=y +CONFIG_MPIC=y + +# +# Kernel options +# +CONFIG_HIGHMEM=y +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=m +CONFIG_MATH_EMULATION=y +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_ARCH_POPULATES_NODE_MAP=y +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_PROC_DEVICETREE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="root=/dev/sda3 rw console=ttyS0,115200" +# CONFIG_PM is not set +CONFIG_SECCOMP=y +CONFIG_ISA_DMA_API=y + +# +# Bus options +# +CONFIG_ZONE_DMA=y +# CONFIG_MPIC_WEIRD is not set +# CONFIG_PPC_I8259 is not set +CONFIG_PPC_INDIRECT_PCI=y +CONFIG_FSL_SOC=y +# CONFIG_PCI is not set +# CONFIG_PCI_DOMAINS is not set + +# +# PCCARD (PCMCIA/CardBus) support +# + +# +# PCI Hotplug Support +# + +# +# Advanced setup +# +# CONFIG_ADVANCED_OPTIONS is not set + +# +# Default settings for advanced configuration options are used +# +CONFIG_HIGHMEM_START=0xfe000000 +CONFIG_LOWMEM_SIZE=0x30000000 +CONFIG_KERNEL_START=0xc0000000 +CONFIG_TASK_SIZE=0x80000000 +CONFIG_BOOT_LOAD=0x00800000 + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_NETDEBUG is not set +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +CONFIG_XFRM_USER=m +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +CONFIG_NET_KEY=m +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +# CONFIG_IP_ROUTE_MULTIPATH_CACHED is not set +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set +CONFIG_FIB_RULES=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +# CONFIG_PNPACPI is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# Misc devices +# + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +CONFIG_SCSI_LOGGING=y +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set + +# +# SCSI low-level drivers +# +# CONFIG_ISCSI_TCP is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Serial ATA (prod) and Parallel ATA (experimental) drivers +# +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +# CONFIG_PATA_PLATFORM is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Macintosh device drivers +# +# CONFIG_MAC_EMUMOUSEBTN is not set +# CONFIG_WINDFARM is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +CONFIG_VITESSE_PHY=y +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_FIXED_PHY is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y + +# +# Ethernet (1000 Mbit) +# +CONFIG_GIANFAR=y +CONFIG_GFAR_NAPI=y + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_UARTLITE is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_OF_PLATFORM is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_HW_RANDOM is not set +CONFIG_NVRAM=y +CONFIG_GEN_RTC=y +CONFIG_GEN_RTC_X=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Hardware Monitoring support +# +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +CONFIG_DVB=y +CONFIG_DVB_CORE=m +# CONFIG_DVB_CORE_ATTACH is not set + +# +# Supported DVB Frontends +# + +# +# Customise DVB Frontends +# +# CONFIG_DVB_FE_CUSTOMISE is not set + +# +# DVB-S (satellite) frontends +# + +# +# DVB-T (terrestrial) frontends +# + +# +# DVB-C (cable) frontends +# + +# +# ATSC (North American/Korean Terrestrial/Cable DTV) frontends +# + +# +# Tuners/PLL support +# + +# +# Miscellaneous devices +# + +# +# Graphics support +# +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_FB is not set +# CONFIG_FB_IBM_GXT4500 is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# HID Devices +# +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set + +# +# USB support +# +# CONFIG_USB_ARCH_HAS_HCD is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# LED devices +# +# CONFIG_NEW_LEDS is not set + +# +# LED drivers +# + +# +# LED Triggers +# + +# +# InfiniBand support +# + +# +# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) +# + +# +# Real Time Clock +# +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set + +# +# RTC drivers +# +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_TEST is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# DMA Engine support +# +# CONFIG_DMA_ENGINE is not set + +# +# DMA Clients +# + +# +# DMA Devices +# + +# +# Auxiliary Display support +# + +# +# Virtualization +# + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=y +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_RW is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +CONFIG_ADFS_FS=m +# CONFIG_ADFS_FS_RW is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +CONFIG_CRAMFS=y +CONFIG_VXFS_FS=m +CONFIG_HPFS_FS=m +CONFIG_QNX4FS_FS=m +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_UFS_DEBUG is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=m + +# +# Distributed Lock Manager +# +# CONFIG_DLM is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y + +# +# Instrumentation Support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_LIST is not set +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUGGER is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_BOOTX_TEXT is not set +# CONFIG_PPC_EARLY_DEBUG is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_ECB is not set +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Hardware crypto devices +# diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index a8da0ae..126b9f8 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -152,7 +152,7 @@ CONFIG_RTAS_ERROR_LOGGING=y CONFIG_RTAS_PROC=y CONFIG_RTAS_FLASH=m CONFIG_MMIO_NVRAM=y -CONFIG_MPIC_BROKEN_U3=y +CONFIG_MPIC_U3_HT_IRQS=y CONFIG_IBMVIO=y # CONFIG_IBMEBUS is not set # CONFIG_PPC_MPC106 is not set diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 8120d42..e0fa80e 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -25,8 +25,8 @@ obj-$(CONFIG_PPC_970_NAP) += idle_power4 obj-$(CONFIG_PPC_OF) += of_device.o of_platform.o prom_parse.o procfs-$(CONFIG_PPC64) := proc_ppc64.o obj-$(CONFIG_PROC_FS) += $(procfs-y) -rtaspci-$(CONFIG_PPC64) := rtas_pci.o -obj-$(CONFIG_PPC_RTAS) += rtas.o rtas-rtc.o $(rtaspci-y) +rtaspci-$(CONFIG_PPC64)-$(CONFIG_PCI) := rtas_pci.o +obj-$(CONFIG_PPC_RTAS) += rtas.o rtas-rtc.o $(rtaspci-y-y) obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o obj-$(CONFIG_RTAS_PROC) += rtas-proc.o obj-$(CONFIG_LPARCFG) += lparcfg.o diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 4734b5d..5c9ff7f 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -241,7 +241,7 @@ #endif if (user_mode(regs) && !access_ok(VERIFY_WRITE, p, size)) return -EFAULT; for (i = 0; i < size / sizeof(long); ++i) - if (__put_user(0, p+i)) + if (__put_user_inatomic(0, p+i)) return -EFAULT; return 1; } @@ -288,7 +288,8 @@ static int emulate_multiple(struct pt_re } else { unsigned long pc = regs->nip ^ (swiz & 4); - if (__get_user(instr, (unsigned int __user *)pc)) + if (__get_user_inatomic(instr, + (unsigned int __user *)pc)) return -EFAULT; if (swiz == 0 && (flags & SW)) instr = cpu_to_le32(instr); @@ -324,27 +325,31 @@ static int emulate_multiple(struct pt_re ((nb0 + 3) / 4) * sizeof(unsigned long)); for (i = 0; i < nb; ++i, ++p) - if (__get_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p))) + if (__get_user_inatomic(REG_BYTE(rptr, i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; if (nb0 > 0) { rptr = ®s->gpr[0]; addr += nb; for (i = 0; i < nb0; ++i, ++p) - if (__get_user(REG_BYTE(rptr, i ^ bswiz), - SWIZ_PTR(p))) + if (__get_user_inatomic(REG_BYTE(rptr, + i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; } } else { for (i = 0; i < nb; ++i, ++p) - if (__put_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p))) + if (__put_user_inatomic(REG_BYTE(rptr, i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; if (nb0 > 0) { rptr = ®s->gpr[0]; addr += nb; for (i = 0; i < nb0; ++i, ++p) - if (__put_user(REG_BYTE(rptr, i ^ bswiz), - SWIZ_PTR(p))) + if (__put_user_inatomic(REG_BYTE(rptr, + i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; } } @@ -398,7 +403,8 @@ int fix_alignment(struct pt_regs *regs) if (cpu_has_feature(CPU_FTR_PPC_LE) && (regs->msr & MSR_LE)) pc ^= 4; - if (unlikely(__get_user(instr, (unsigned int __user *)pc))) + if (unlikely(__get_user_inatomic(instr, + (unsigned int __user *)pc))) return -EFAULT; if (cpu_has_feature(CPU_FTR_REAL_LE) && (regs->msr & MSR_LE)) instr = cpu_to_le32(instr); @@ -474,16 +480,16 @@ int fix_alignment(struct pt_regs *regs) p = (unsigned long) addr; switch (nb) { case 8: - ret |= __get_user(data.v[0], SWIZ_PTR(p++)); - ret |= __get_user(data.v[1], SWIZ_PTR(p++)); - ret |= __get_user(data.v[2], SWIZ_PTR(p++)); - ret |= __get_user(data.v[3], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[0], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[1], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[2], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[3], SWIZ_PTR(p++)); case 4: - ret |= __get_user(data.v[4], SWIZ_PTR(p++)); - ret |= __get_user(data.v[5], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[4], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[5], SWIZ_PTR(p++)); case 2: - ret |= __get_user(data.v[6], SWIZ_PTR(p++)); - ret |= __get_user(data.v[7], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[6], SWIZ_PTR(p++)); + ret |= __get_user_inatomic(data.v[7], SWIZ_PTR(p++)); if (unlikely(ret)) return -EFAULT; } @@ -551,16 +557,16 @@ #endif p = (unsigned long) addr; switch (nb) { case 8: - ret |= __put_user(data.v[0], SWIZ_PTR(p++)); - ret |= __put_user(data.v[1], SWIZ_PTR(p++)); - ret |= __put_user(data.v[2], SWIZ_PTR(p++)); - ret |= __put_user(data.v[3], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[0], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[1], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[2], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[3], SWIZ_PTR(p++)); case 4: - ret |= __put_user(data.v[4], SWIZ_PTR(p++)); - ret |= __put_user(data.v[5], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[4], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[5], SWIZ_PTR(p++)); case 2: - ret |= __put_user(data.v[6], SWIZ_PTR(p++)); - ret |= __put_user(data.v[7], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[6], SWIZ_PTR(p++)); + ret |= __put_user_inatomic(data.v[7], SWIZ_PTR(p++)); } if (unlikely(ret)) return -EFAULT; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 030d300..0c5150c 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -77,7 +77,6 @@ #ifdef CONFIG_PPC64 DEFINE(KSP_VSID, offsetof(struct thread_struct, ksp_vsid)); #else /* CONFIG_PPC64 */ DEFINE(PGDIR, offsetof(struct thread_struct, pgdir)); - DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0)); DEFINE(PT_PTRACED, PT_PTRACED); @@ -140,6 +139,7 @@ #endif /* CONFIG_HUGETLB_PAGE */ DEFINE(PACA_SYSTEM_TIME, offsetof(struct paca_struct, system_time)); DEFINE(PACA_SLBSHADOWPTR, offsetof(struct paca_struct, slb_shadow_ptr)); DEFINE(PACA_DATA_OFFSET, offsetof(struct paca_struct, data_offset)); + DEFINE(PACA_TRAP_SAVE, offsetof(struct paca_struct, trap_save)); DEFINE(SLBSHADOW_STACKVSID, offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].vsid)); diff --git a/arch/powerpc/kernel/btext.c b/arch/powerpc/kernel/btext.c index 3678997..e7b6846 100644 --- a/arch/powerpc/kernel/btext.c +++ b/arch/powerpc/kernel/btext.c @@ -161,33 +161,33 @@ int btext_initialize(struct device_node unsigned long address = 0; const u32 *prop; - prop = get_property(np, "linux,bootx-width", NULL); + prop = of_get_property(np, "linux,bootx-width", NULL); if (prop == NULL) - prop = get_property(np, "width", NULL); + prop = of_get_property(np, "width", NULL); if (prop == NULL) return -EINVAL; width = *prop; - prop = get_property(np, "linux,bootx-height", NULL); + prop = of_get_property(np, "linux,bootx-height", NULL); if (prop == NULL) - prop = get_property(np, "height", NULL); + prop = of_get_property(np, "height", NULL); if (prop == NULL) return -EINVAL; height = *prop; - prop = get_property(np, "linux,bootx-depth", NULL); + prop = of_get_property(np, "linux,bootx-depth", NULL); if (prop == NULL) - prop = get_property(np, "depth", NULL); + prop = of_get_property(np, "depth", NULL); if (prop == NULL) return -EINVAL; depth = *prop; pitch = width * ((depth + 7) / 8); - prop = get_property(np, "linux,bootx-linebytes", NULL); + prop = of_get_property(np, "linux,bootx-linebytes", NULL); if (prop == NULL) - prop = get_property(np, "linebytes", NULL); + prop = of_get_property(np, "linebytes", NULL); if (prop && *prop != 0xffffffffu) pitch = *prop; if (pitch == 1) pitch = 0x1000; - prop = get_property(np, "address", NULL); + prop = of_get_property(np, "address", NULL); if (prop) address = *prop; @@ -219,7 +219,7 @@ int __init btext_find_display(int allow_ struct device_node *np = NULL; int rc = -ENODEV; - name = get_property(of_chosen, "linux,stdout-path", NULL); + name = of_get_property(of_chosen, "linux,stdout-path", NULL); if (name != NULL) { np = of_find_node_by_path(name); if (np != NULL) { @@ -236,7 +236,7 @@ int __init btext_find_display(int allow_ return rc; for (np = NULL; (np = of_find_node_by_type(np, "display"));) { - if (get_property(np, "linux,opened", NULL)) { + if (of_get_property(np, "linux,opened", NULL)) { printk("trying %s ...\n", np->full_name); rc = btext_initialize(np); printk("result: %d\n", rc); diff --git a/arch/powerpc/kernel/cpu_setup_pa6t.S b/arch/powerpc/kernel/cpu_setup_pa6t.S index 4047be2..d62cb9c 100644 --- a/arch/powerpc/kernel/cpu_setup_pa6t.S +++ b/arch/powerpc/kernel/cpu_setup_pa6t.S @@ -34,7 +34,7 @@ _GLOBAL(__setup_cpu_pa6t) beqlr mfspr r0,SPRN_HID5 - ori r0,r0,0x30 + ori r0,r0,0x38 mtspr SPRN_HID5,r0 mfspr r0,SPRN_LPCR diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index e4006dc..9cb24d2 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -389,6 +389,8 @@ #ifdef CONFIG_PPC64 .pmc_type = PPC_PMC_PA6T, .cpu_setup = __setup_cpu_pa6t, .cpu_restore = __restore_cpu_pa6t, + .oprofile_cpu_type = "ppc64/pa6t", + .oprofile_type = PPC_OPROFILE_PA6T, .platform = "pa6t", }, { /* default match */ @@ -558,6 +560,18 @@ #if CLASSIC_PPC .cpu_setup = __setup_cpu_750cx, .platform = "ppc750", }, + { /* 750CL */ + .pvr_mask = 0xfffff0f0, + .pvr_value = 0x00087010, + .cpu_name = "750CL", + .cpu_features = CPU_FTRS_750CL, + .cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750, + .platform = "ppc750", + }, { /* 745/755 */ .pvr_mask = 0xfffff000, .pvr_value = 0x00083000, diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index c03e829..c29d165 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -191,7 +191,6 @@ stack_ovf: 0: _GLOBAL(DoSyscall) - stw r0,THREAD+LAST_SYSCALL(r2) stw r3,ORIG_GPR3(r1) li r12,0 stw r12,RESULT(r1) diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 97cedcd..1111fce 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -278,8 +278,12 @@ #define EXCEPTION_PROLOG_COMMON(n, area) beq- 1f; \ ld r1,PACAKSAVE(r13); /* kernel stack to use */ \ 1: cmpdi cr1,r1,0; /* check if r1 is in userspace */ \ - bge- cr1,bad_stack; /* abort if it is */ \ - std r9,_CCR(r1); /* save CR in stackframe */ \ + bge- cr1,2f; /* abort if it is */ \ + b 3f; \ +2: li r1,(n); /* will be reloaded later */ \ + sth r1,PACA_TRAP_SAVE(r13); \ + b bad_stack; \ +3: std r9,_CCR(r1); /* save CR in stackframe */ \ std r11,_NIP(r1); /* save SRR0 in stackframe */ \ std r12,_MSR(r1); /* save SRR1 in stackframe */ \ std r10,0(r1); /* make stack chain pointer */ \ @@ -940,6 +944,8 @@ bad_stack: SAVE_2GPRS(7,r1) SAVE_10GPRS(12,r1) SAVE_10GPRS(22,r1) + lhz r12,PACA_TRAP_SAVE(r13) + std r12,_TRAP(r1) addi r11,r1,INT_FRAME_SIZE std r11,0(r1) li r12,0 @@ -1555,7 +1561,6 @@ _GLOBAL(generic_secondary_smp_init) /* turn on 64-bit mode */ bl .enable_64b_mode - isync /* Set up a paca value for this processor. Since we have the * physical cpu id in r24, we need to search the pacas to find @@ -1735,10 +1740,6 @@ _STATIC(__boot_from_prom) /* We never return */ trap -/* - * At this point, r3 contains the physical address we are running at, - * returned by prom_init() - */ _STATIC(__after_prom_start) /* @@ -1851,7 +1852,6 @@ __secondary_start_pmac_0: _GLOBAL(pmac_secondary_start) /* turn on 64-bit mode */ bl .enable_64b_mode - isync /* Copy some CPU settings from CPU 0 */ bl .__restore_cpu_ppc970 diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 82bd2f1..9a8c9af 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -2,36 +2,37 @@ * IBM PowerPC IBM eBus Infrastructure Support. * * Copyright (c) 2005 IBM Corporation + * Joachim Fenkes * Heiko J Schick - * + * * All rights reserved. * - * This source code is distributed under a dual license of GPL v2.0 and OpenIB - * BSD. + * This source code is distributed under a dual license of GPL v2.0 and OpenIB + * BSD. * * OpenIB BSD License * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation * and/or other materials - * provided with the distribution. + * provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ @@ -43,19 +44,19 @@ #include #include #include -static struct ibmebus_dev ibmebus_bus_device = { /* fake "parent" device */ - .name = ibmebus_bus_device.ofdev.dev.bus_id, - .ofdev.dev.bus_id = "ibmebus", - .ofdev.dev.bus = &ibmebus_bus_type, +static struct device ibmebus_bus_device = { /* fake "parent" device */ + .bus_id = "ibmebus", }; +struct bus_type ibmebus_bus_type; + static void *ibmebus_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) { void *mem; - + mem = kmalloc(size, flag); *dma_handle = (dma_addr_t)mem; @@ -63,7 +64,7 @@ static void *ibmebus_alloc_coherent(stru } static void ibmebus_free_coherent(struct device *dev, - size_t size, void *vaddr, + size_t size, void *vaddr, dma_addr_t dma_handle) { kfree(vaddr); @@ -79,7 +80,7 @@ static dma_addr_t ibmebus_map_single(str static void ibmebus_unmap_single(struct device *dev, dma_addr_t dma_addr, - size_t size, + size_t size, enum dma_data_direction direction) { return; @@ -90,13 +91,13 @@ static int ibmebus_map_sg(struct device int nents, enum dma_data_direction direction) { int i; - + for (i = 0; i < nents; i++) { - sg[i].dma_address = (dma_addr_t)page_address(sg[i].page) + sg[i].dma_address = (dma_addr_t)page_address(sg[i].page) + sg[i].offset; sg[i].dma_length = sg[i].length; } - + return nents; } @@ -128,15 +129,15 @@ static int ibmebus_bus_probe(struct devi struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver); const struct of_device_id *id; int error = -ENODEV; - + if (!ibmebusdrv->probe) return error; - + id = of_match_device(ibmebusdrv->id_table, &ibmebusdev->ofdev); if (id) { error = ibmebusdrv->probe(ibmebusdev, id); } - + return error; } @@ -144,11 +145,11 @@ static int ibmebus_bus_remove(struct dev { struct ibmebus_dev *ibmebusdev = to_ibmebus_dev(dev); struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver); - + if (ibmebusdrv->remove) { return ibmebusdrv->remove(ibmebusdev); } - + return 0; } @@ -158,21 +159,12 @@ static void __devinit ibmebus_dev_releas kfree(to_ibmebus_dev(dev)); } -static ssize_t ibmebusdev_show_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%s\n", to_ibmebus_dev(dev)->name); -} -static DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, ibmebusdev_show_name, - NULL); - -static struct ibmebus_dev* __devinit ibmebus_register_device_common( +static int __devinit ibmebus_register_device_common( struct ibmebus_dev *dev, const char *name) { int err = 0; - dev->name = name; - dev->ofdev.dev.parent = &ibmebus_bus_device.ofdev.dev; + dev->ofdev.dev.parent = &ibmebus_bus_device; dev->ofdev.dev.bus = &ibmebus_bus_type; dev->ofdev.dev.release = ibmebus_dev_release; @@ -181,17 +173,15 @@ static struct ibmebus_dev* __devinit ibm dev->ofdev.dev.archdata.numa_node = of_node_to_nid(dev->ofdev.node); /* An ibmebusdev is based on a of_device. We have to change the - * bus type to use our own DMA mapping operations. - */ + * bus type to use our own DMA mapping operations. + */ if ((err = of_device_register(&dev->ofdev)) != 0) { printk(KERN_ERR "%s: failed to register device (%d).\n", __FUNCTION__, err); - return NULL; + return -ENODEV; } - - device_create_file(&dev->ofdev.dev, &dev_attr_name); - - return dev; + + return 0; } static struct ibmebus_dev* __devinit ibmebus_register_device_node( @@ -201,35 +191,35 @@ static struct ibmebus_dev* __devinit ibm const char *loc_code; int length; - loc_code = get_property(dn, "ibm,loc-code", NULL); + loc_code = of_get_property(dn, "ibm,loc-code", NULL); if (!loc_code) { printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n", __FUNCTION__, dn->name ? dn->name : ""); - return NULL; + return ERR_PTR(-EINVAL); } - + if (strlen(loc_code) == 0) { printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n", __FUNCTION__); - return NULL; + return ERR_PTR(-EINVAL); } dev = kzalloc(sizeof(struct ibmebus_dev), GFP_KERNEL); if (!dev) { - return NULL; + return ERR_PTR(-ENOMEM); } dev->ofdev.node = of_node_get(dn); - + length = strlen(loc_code); - memcpy(dev->ofdev.dev.bus_id, loc_code - + (length - min(length, BUS_ID_SIZE - 1)), + memcpy(dev->ofdev.dev.bus_id, loc_code + + (length - min(length, BUS_ID_SIZE - 1)), min(length, BUS_ID_SIZE - 1)); /* Register with generic device framework. */ - if (ibmebus_register_device_common(dev, dn->name) == NULL) { + if (ibmebus_register_device_common(dev, dn->name) != 0) { kfree(dev); - return NULL; + return ERR_PTR(-ENODEV); } return dev; @@ -238,17 +228,16 @@ static struct ibmebus_dev* __devinit ibm static void ibmebus_probe_of_nodes(char* name) { struct device_node *dn = NULL; - + while ((dn = of_find_node_by_name(dn, name))) { - if (ibmebus_register_device_node(dn) == NULL) { + if (IS_ERR(ibmebus_register_device_node(dn))) { of_node_put(dn); - return; } } - + of_node_put(dn); - + return; } @@ -262,17 +251,21 @@ static void ibmebus_add_devices_by_id(st return; } -static int ibmebus_match_helper(struct device *dev, void *data) +static int ibmebus_match_name(struct device *dev, void *data) { - if (strcmp((char*)data, to_ibmebus_dev(dev)->name) == 0) + const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev); + const char *name; + + name = of_get_property(ebus_dev->ofdev.node, "name", NULL); + + if (name && (strcmp(data, name) == 0)) return 1; - + return 0; } static int ibmebus_unregister_device(struct device *dev) { - device_remove_file(dev, &dev_attr_name); of_device_unregister(to_of_device(dev)); return 0; @@ -281,17 +274,16 @@ static int ibmebus_unregister_device(str static void ibmebus_remove_devices_by_id(struct of_device_id *idt) { struct device *dev; - + while (strlen(idt->name) > 0) { - while ((dev = bus_find_device(&ibmebus_bus_type, NULL, + while ((dev = bus_find_device(&ibmebus_bus_type, NULL, (void*)idt->name, - ibmebus_match_helper))) { + ibmebus_match_name))) { ibmebus_unregister_device(dev); } idt++; - } - + return; } @@ -307,30 +299,33 @@ int ibmebus_register_driver(struct ibmeb if ((err = driver_register(&drv->driver) != 0)) return err; + /* remove all supported devices first, in case someone + * probed them manually before registering the driver */ + ibmebus_remove_devices_by_id(drv->id_table); ibmebus_add_devices_by_id(drv->id_table); - + return 0; } EXPORT_SYMBOL(ibmebus_register_driver); void ibmebus_unregister_driver(struct ibmebus_driver *drv) -{ +{ driver_unregister(&drv->driver); ibmebus_remove_devices_by_id(drv->id_table); } EXPORT_SYMBOL(ibmebus_unregister_driver); int ibmebus_request_irq(struct ibmebus_dev *dev, - u32 ist, + u32 ist, irq_handler_t handler, unsigned long irq_flags, const char * devname, void *dev_id) { unsigned int irq = irq_create_mapping(NULL, ist); - + if (irq == NO_IRQ) return -EINVAL; - + return request_irq(irq, handler, irq_flags, devname, dev_id); } @@ -339,56 +334,163 @@ EXPORT_SYMBOL(ibmebus_request_irq); void ibmebus_free_irq(struct ibmebus_dev *dev, u32 ist, void *dev_id) { unsigned int irq = irq_find_mapping(NULL, ist); - + free_irq(irq, dev_id); } EXPORT_SYMBOL(ibmebus_free_irq); static int ibmebus_bus_match(struct device *dev, struct device_driver *drv) -{ +{ const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev); struct ibmebus_driver *ebus_drv = to_ibmebus_driver(drv); const struct of_device_id *ids = ebus_drv->id_table; const struct of_device_id *found_id; - + if (!ids) return 0; - + found_id = of_match_device(ids, &ebus_dev->ofdev); if (found_id) return 1; - + return 0; } +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev); + const char *name = of_get_property(ebus_dev->ofdev.node, "name", NULL); + return sprintf(buf, "%s\n", name); +} + +static struct device_attribute ibmebus_dev_attrs[] = { + __ATTR_RO(name), + __ATTR_NULL +}; + +static int ibmebus_match_path(struct device *dev, void *data) +{ + int rc; + struct device_node *dn = + of_node_get(to_ibmebus_dev(dev)->ofdev.node); + + rc = (dn->full_name && (strcasecmp((char*)data, dn->full_name) == 0)); + + of_node_put(dn); + return rc; +} + +static char *ibmebus_chomp(const char *in, size_t count) +{ + char *out = (char*)kmalloc(count + 1, GFP_KERNEL); + if (!out) + return NULL; + + memcpy(out, in, count); + out[count] = '\0'; + if (out[count - 1] == '\n') + out[count - 1] = '\0'; + + return out; +} + +static ssize_t ibmebus_store_probe(struct bus_type *bus, + const char *buf, size_t count) +{ + struct device_node *dn = NULL; + struct ibmebus_dev *dev; + char *path; + ssize_t rc; + + path = ibmebus_chomp(buf, count); + if (!path) + return -ENOMEM; + + if (bus_find_device(&ibmebus_bus_type, NULL, path, + ibmebus_match_path)) { + printk(KERN_WARNING "%s: %s has already been probed\n", + __FUNCTION__, path); + rc = -EINVAL; + goto out; + } + + if ((dn = of_find_node_by_path(path))) { + dev = ibmebus_register_device_node(dn); + of_node_put(dn); + rc = IS_ERR(dev) ? PTR_ERR(dev) : count; + } else { + printk(KERN_WARNING "%s: no such device node: %s\n", + __FUNCTION__, path); + rc = -ENODEV; + } + +out: + kfree(path); + return rc; +} + +static ssize_t ibmebus_store_remove(struct bus_type *bus, + const char *buf, size_t count) +{ + struct device *dev; + char *path; + + path = ibmebus_chomp(buf, count); + if (!path) + return -ENOMEM; + + if ((dev = bus_find_device(&ibmebus_bus_type, NULL, path, + ibmebus_match_path))) { + ibmebus_unregister_device(dev); + + kfree(path); + return count; + } else { + printk(KERN_WARNING "%s: %s not on the bus\n", + __FUNCTION__, path); + + kfree(path); + return -ENODEV; + } +} + +static struct bus_attribute ibmebus_bus_attrs[] = { + __ATTR(probe, S_IWUSR, NULL, ibmebus_store_probe), + __ATTR(remove, S_IWUSR, NULL, ibmebus_store_remove), + __ATTR_NULL +}; + struct bus_type ibmebus_bus_type = { - .name = "ibmebus", - .match = ibmebus_bus_match, + .name = "ibmebus", + .match = ibmebus_bus_match, + .dev_attrs = ibmebus_dev_attrs, + .bus_attrs = ibmebus_bus_attrs }; EXPORT_SYMBOL(ibmebus_bus_type); static int __init ibmebus_bus_init(void) { int err; - + printk(KERN_INFO "IBM eBus Device Driver\n"); - + err = bus_register(&ibmebus_bus_type); if (err) { printk(KERN_ERR ":%s: failed to register IBM eBus.\n", __FUNCTION__); return err; } - - err = device_register(&ibmebus_bus_device.ofdev.dev); + + err = device_register(&ibmebus_bus_device); if (err) { - printk(KERN_WARNING "%s: device_register returned %i\n", + printk(KERN_WARNING "%s: device_register returned %i\n", __FUNCTION__, err); bus_unregister(&ibmebus_bus_type); return err; } - + return 0; } __initcall(ibmebus_bus_init); diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index 95edad4..c08ceca 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -47,6 +47,8 @@ #else static int novmerge = 1; #endif +static int protect4gb = 1; + static inline unsigned long iommu_num_pages(unsigned long vaddr, unsigned long slen) { @@ -58,6 +60,16 @@ static inline unsigned long iommu_num_pa return npages; } +static int __init setup_protect4gb(char *str) +{ + if (strcmp(str, "on") == 0) + protect4gb = 1; + else if (strcmp(str, "off") == 0) + protect4gb = 0; + + return 1; +} + static int __init setup_iommu(char *str) { if (!strcmp(str, "novmerge")) @@ -67,6 +79,7 @@ static int __init setup_iommu(char *str) return 1; } +__setup("protect4gb=", setup_protect4gb); __setup("iommu=", setup_iommu); static unsigned long iommu_range_alloc(struct iommu_table *tbl, @@ -429,6 +442,9 @@ void iommu_unmap_sg(struct iommu_table * struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid) { unsigned long sz; + unsigned long start_index, end_index; + unsigned long entries_per_4g; + unsigned long index; static int welcomed = 0; struct page *page; @@ -450,7 +466,7 @@ struct iommu_table *iommu_init_table(str #ifdef CONFIG_CRASH_DUMP if (ppc_md.tce_get) { - unsigned long index, tceval; + unsigned long tceval; unsigned long tcecount = 0; /* @@ -480,6 +496,23 @@ #else ppc_md.tce_free(tbl, tbl->it_offset, tbl->it_size); #endif + /* + * DMA cannot cross 4 GB boundary. Mark last entry of each 4 + * GB chunk as reserved. + */ + if (protect4gb) { + entries_per_4g = 0x100000000l >> IOMMU_PAGE_SHIFT; + + /* Mark the last bit before a 4GB boundary as used */ + start_index = tbl->it_offset | (entries_per_4g - 1); + start_index -= tbl->it_offset; + + end_index = tbl->it_size; + + for (index = start_index; index < end_index - 1; index += entries_per_4g) + __set_bit(index, tbl->it_map); + } + if (!welcomed) { printk(KERN_INFO "IOMMU table initialized, virtual merging %s\n", novmerge ? "disabled" : "enabled"); diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 1009308..6c83fe2 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -394,7 +394,7 @@ EXPORT_SYMBOL(do_softirq); #ifdef CONFIG_PPC_MERGE static LIST_HEAD(irq_hosts); -static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(irq_big_lock); static DEFINE_PER_CPU(unsigned int, irq_radix_reader); static unsigned int irq_radix_writer; struct irq_map_entry irq_map[NR_IRQS]; diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index dd2886f..ef647e7 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -59,12 +59,14 @@ int __kprobes arch_prepare_kprobe(struct } if (!ret) { - memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); + memcpy(p->ainsn.insn, p->addr, + MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); p->opcode = *p->addr; flush_icache_range((unsigned long)p->ainsn.insn, (unsigned long)p->ainsn.insn + sizeof(kprobe_opcode_t)); } + p->ainsn.boostable = 0; return ret; } @@ -232,6 +234,38 @@ static int __kprobes kprobe_handler(stru return 1; ss_probe: + if (p->ainsn.boostable >= 0) { + unsigned int insn = *p->ainsn.insn; + + /* regs->nip is also adjusted if emulate_step returns 1 */ + ret = emulate_step(regs, insn); + if (ret > 0) { + /* + * Once this instruction has been boosted + * successfully, set the boostable flag + */ + if (unlikely(p->ainsn.boostable == 0)) + p->ainsn.boostable = 1; + + if (p->post_handler) + p->post_handler(p, regs, 0); + + kcb->kprobe_status = KPROBE_HIT_SSDONE; + reset_current_kprobe(); + preempt_enable_no_resched(); + return 1; + } else if (ret < 0) { + /* + * We don't allow kprobes on mtmsr(d)/rfi(d), etc. + * So, we should never get here... but, its still + * good to catch them, just in case... + */ + printk("Can't step on instruction %x\n", insn); + BUG(); + } else if (ret == 0) + /* This instruction can't be boosted */ + p->ainsn.boostable = -1; + } prepare_singlestep(p, regs); kcb->kprobe_status = KPROBE_HIT_SS; return 1; diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 325f490..63dd2c3 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -44,12 +44,12 @@ static int __init add_legacy_port(struct int index; /* get clock freq. if present */ - clk = get_property(np, "clock-frequency", NULL); + clk = of_get_property(np, "clock-frequency", NULL); if (clk && *clk) clock = *clk; /* get default speed if present */ - spd = get_property(np, "current-speed", NULL); + spd = of_get_property(np, "current-speed", NULL); /* If we have a location index, then try to use it */ if (want_index >= 0 && want_index < MAX_LEGACY_SERIAL_PORTS) @@ -121,11 +121,11 @@ static int __init add_legacy_soc_port(st /* We only support ports that have a clock frequency properly * encoded in the device-tree. */ - if (get_property(np, "clock-frequency", NULL) == NULL) + if (of_get_property(np, "clock-frequency", NULL) == NULL) return -1; /* if rtas uses this device, don't try to use it as well */ - if (get_property(np, "used-by-rtas", NULL) != NULL) + if (of_get_property(np, "used-by-rtas", NULL) != NULL) return -1; /* Get the address */ @@ -157,7 +157,7 @@ static int __init add_legacy_isa_port(st DBG(" -> add_legacy_isa_port(%s)\n", np->full_name); /* Get the ISA port number */ - reg = get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); if (reg == NULL) return -1; @@ -168,7 +168,7 @@ static int __init add_legacy_isa_port(st /* Now look for an "ibm,aix-loc" property that gives us ordering * if any... */ - typep = get_property(np, "ibm,aix-loc", NULL); + typep = of_get_property(np, "ibm,aix-loc", NULL); /* If we have a location index, then use it */ if (typep && *typep == 'S') @@ -206,7 +206,7 @@ static int __init add_legacy_pci_port(st * compatible UARTs on PCI need all sort of quirks (port offsets * etc...) that this code doesn't know about */ - if (get_property(np, "clock-frequency", NULL) == NULL) + if (of_get_property(np, "clock-frequency", NULL) == NULL) return -1; /* Get the PCI address. Assume BAR 0 */ @@ -232,7 +232,7 @@ static int __init add_legacy_pci_port(st * we get to their "reg" property */ if (np != pci_dev) { - const u32 *reg = get_property(np, "reg", NULL); + const u32 *reg = of_get_property(np, "reg", NULL); if (reg && (*reg < 4)) index = lindex = *reg; } @@ -296,7 +296,7 @@ void __init find_legacy_serial_ports(voi DBG(" -> find_legacy_serial_port()\n"); /* Now find out if one of these is out firmware console */ - path = get_property(of_chosen, "linux,stdout-path", NULL); + path = of_get_property(of_chosen, "linux,stdout-path", NULL); if (path != NULL) { stdout = of_find_node_by_path(path); if (stdout) @@ -529,7 +529,7 @@ static int __init check_legacy_serial_co } /* We are getting a weird phandle from OF ... */ /* ... So use the full path instead */ - name = get_property(of_chosen, "linux,stdout-path", NULL); + name = of_get_property(of_chosen, "linux,stdout-path", NULL); if (name == NULL) { DBG(" no linux,stdout-path !\n"); return -ENODEV; @@ -541,12 +541,12 @@ static int __init check_legacy_serial_co } DBG("stdout is %s\n", prom_stdout->full_name); - name = get_property(prom_stdout, "name", NULL); + name = of_get_property(prom_stdout, "name", NULL); if (!name) { DBG(" stdout package has no name !\n"); goto not_found; } - spd = get_property(prom_stdout, "current-speed", NULL); + spd = of_get_property(prom_stdout, "current-speed", NULL); if (spd) speed = *spd; diff --git a/arch/powerpc/kernel/lparcfg.c b/arch/powerpc/kernel/lparcfg.c index 89486b6..c492cee 100644 --- a/arch/powerpc/kernel/lparcfg.c +++ b/arch/powerpc/kernel/lparcfg.c @@ -130,30 +130,31 @@ #ifdef CONFIG_PPC_PSERIES /* * Methods used to fetch LPAR data when running on a pSeries platform. */ -/* find a better place for this function... */ static void log_plpar_hcall_return(unsigned long rc, char *tag) { - if (rc == 0) /* success, return */ + switch(rc) { + case 0: return; -/* check for null tag ? */ - if (rc == H_HARDWARE) - printk(KERN_INFO - "plpar-hcall (%s) failed with hardware fault\n", tag); - else if (rc == H_FUNCTION) - printk(KERN_INFO - "plpar-hcall (%s) failed; function not allowed\n", tag); - else if (rc == H_AUTHORITY) - printk(KERN_INFO - "plpar-hcall (%s) failed; not authorized to this" - " function\n", tag); - else if (rc == H_PARAMETER) - printk(KERN_INFO "plpar-hcall (%s) failed; Bad parameter(s)\n", - tag); - else - printk(KERN_INFO - "plpar-hcall (%s) failed with unexpected rc(0x%lx)\n", - tag, rc); - + case H_HARDWARE: + printk(KERN_INFO "plpar-hcall (%s) " + "Hardware fault\n", tag); + return; + case H_FUNCTION: + printk(KERN_INFO "plpar-hcall (%s) " + "Function not allowed\n", tag); + return; + case H_AUTHORITY: + printk(KERN_INFO "plpar-hcall (%s) " + "Not authorized to this function\n", tag); + return; + case H_PARAMETER: + printk(KERN_INFO "plpar-hcall (%s) " + "Bad parameter(s)\n",tag); + return; + default: + printk(KERN_INFO "plpar-hcall (%s) " + "Unexpected rc(0x%lx)\n", tag, rc); + } } /* @@ -321,15 +322,16 @@ static int pseries_lparcfg_data(struct s struct device_node *rtas_node; const int *lrdrp = NULL; - rtas_node = find_path_device("/rtas"); + rtas_node = of_find_node_by_path("/rtas"); if (rtas_node) - lrdrp = get_property(rtas_node, "ibm,lrdr-capacity", NULL); + lrdrp = of_get_property(rtas_node, "ibm,lrdr-capacity", NULL); if (lrdrp == NULL) { partition_potential_processors = vdso_data->processorCount; } else { partition_potential_processors = *(lrdrp + 4); } + of_node_put(rtas_node); partition_active_processors = lparcfg_count_active_processors(); @@ -537,25 +539,27 @@ static int lparcfg_data(struct seq_file seq_printf(m, "%s %s \n", MODULE_NAME, MODULE_VERS); - rootdn = find_path_device("/"); + rootdn = of_find_node_by_path("/"); if (rootdn) { - tmp = get_property(rootdn, "model", NULL); + tmp = of_get_property(rootdn, "model", NULL); if (tmp) { model = tmp; /* Skip "IBM," - see platforms/iseries/dt.c */ if (firmware_has_feature(FW_FEATURE_ISERIES)) model += 4; } - tmp = get_property(rootdn, "system-id", NULL); + tmp = of_get_property(rootdn, "system-id", NULL); if (tmp) { system_id = tmp; /* Skip "IBM," - see platforms/iseries/dt.c */ if (firmware_has_feature(FW_FEATURE_ISERIES)) system_id += 4; } - lp_index_ptr = get_property(rootdn, "ibm,partition-no", NULL); + lp_index_ptr = of_get_property(rootdn, "ibm,partition-no", + NULL); if (lp_index_ptr) lp_index = *lp_index_ptr; + of_node_put(rootdn); } seq_printf(m, "serial_number=%s\n", system_id); seq_printf(m, "system_type=%s\n", model); diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index a24b09c..704375b 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -72,8 +72,8 @@ int default_machine_kexec_prepare(struct /* We also should not overwrite the tce tables */ for (node = of_find_node_by_type(NULL, "pci"); node != NULL; node = of_find_node_by_type(node, "pci")) { - basep = get_property(node, "linux,tce-base", NULL); - sizep = get_property(node, "linux,tce-size", NULL); + basep = of_get_property(node, "linux,tce-base", NULL); + sizep = of_get_property(node, "linux,tce-size", NULL); if (basep == NULL || sizep == NULL) continue; @@ -294,19 +294,19 @@ static unsigned long htab_base, kernel_e static struct property htab_base_prop = { .name = "linux,htab-base", .length = sizeof(unsigned long), - .value = (unsigned char *)&htab_base, + .value = &htab_base, }; static struct property htab_size_prop = { .name = "linux,htab-size", .length = sizeof(unsigned long), - .value = (unsigned char *)&htab_size_bytes, + .value = &htab_size_bytes, }; static struct property kernel_end_prop = { .name = "linux,kernel-end", .length = sizeof(unsigned long), - .value = (unsigned char *)&kernel_end, + .value = &kernel_end, }; static void __init export_htab_values(void) @@ -335,7 +335,7 @@ static void __init export_htab_values(vo static struct property crashk_base_prop = { .name = "linux,crashkernel-base", .length = sizeof(unsigned long), - .value = (unsigned char *)&crashk_res.start, + .value = &crashk_res.start, }; static unsigned long crashk_size; @@ -343,7 +343,7 @@ static unsigned long crashk_size; static struct property crashk_size_prop = { .name = "linux,crashkernel-size", .length = sizeof(unsigned long), - .value = (unsigned char *)&crashk_size, + .value = &crashk_size, }; static void __init export_crashk_values(void) diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 412bea3..98decf8 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -734,10 +734,6 @@ _GLOBAL(abs) sub r3,r3,r4 blr -_GLOBAL(_get_SP) - mr r3,r1 /* Close enough */ - blr - /* * Create a kernel thread * kernel_thread(fn, arg, flags) diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c index e921514..0c8ea76 100644 --- a/arch/powerpc/kernel/of_device.c +++ b/arch/powerpc/kernel/of_device.c @@ -120,6 +120,117 @@ void of_device_unregister(struct of_devi } +static ssize_t of_device_get_modalias(struct of_device *ofdev, + char *str, ssize_t len) +{ + const char *compat; + int cplen, i; + ssize_t tsize, csize, repend; + + /* Name & Type */ + csize = snprintf(str, len, "of:N%sT%s", + ofdev->node->name, ofdev->node->type); + + /* Get compatible property if any */ + compat = of_get_property(ofdev->node, "compatible", &cplen); + if (!compat) + return csize; + + /* Find true end (we tolerate multiple \0 at the end */ + for (i=(cplen-1); i>=0 && !compat[i]; i--) + cplen--; + if (!cplen) + return csize; + cplen++; + + /* Check space (need cplen+1 chars including final \0) */ + tsize = csize + cplen; + repend = tsize; + + if (csize>=len) /* @ the limit, all is already filled */ + return tsize; + + if (tsize>=len) { /* limit compat list */ + cplen = len-csize-1; + repend = len; + } + + /* Copy and do char replacement */ + memcpy(&str[csize+1], compat, cplen); + for (i=csize; inode->name)) + return -ENOMEM; + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_TYPE=%s", ofdev->node->type)) + return -ENOMEM; + + /* Since the compatible field can contain pretty much anything + * it's not really legal to split it out with commas. We split it + * up using a number of environment variables instead. */ + + compat = of_get_property(ofdev->node, "compatible", &cplen); + while (compat && *compat && cplen > 0) { + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_COMPATIBLE_%d=%s", seen, compat)) + return -ENOMEM; + + sl = strlen (compat) + 1; + compat += sl; + cplen -= sl; + seen++; + } + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_COMPATIBLE_N=%d", seen)) + return -ENOMEM; + + /* modalias is trickier, we add it in 2 steps */ + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=")) + return -ENOMEM; + + sl = of_device_get_modalias(ofdev, &buffer[length-1], + buffer_size-length); + if (sl >= (buffer_size-length)) + return -ENOMEM; + + length += sl; + + envp[i] = NULL; + + return 0; +} + + EXPORT_SYMBOL(of_match_node); EXPORT_SYMBOL(of_match_device); EXPORT_SYMBOL(of_device_register); @@ -127,3 +238,4 @@ EXPORT_SYMBOL(of_device_unregister); EXPORT_SYMBOL(of_dev_get); EXPORT_SYMBOL(of_dev_put); EXPORT_SYMBOL(of_release_dev); +EXPORT_SYMBOL(of_device_uevent); diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index b734517..908ed79 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -133,6 +133,7 @@ static int of_platform_device_resume(str struct bus_type of_platform_bus_type = { .name = "of_platform", .match = of_platform_bus_match, + .uevent = of_device_uevent, .probe = of_platform_device_probe, .remove = of_platform_device_remove, .suspend = of_platform_device_suspend, @@ -177,7 +178,7 @@ static void of_platform_make_bus_id(stru * and 'D' for MMIO DCRs. */ #ifdef CONFIG_PPC_DCR - reg = get_property(node, "dcr-reg", NULL); + reg = of_get_property(node, "dcr-reg", NULL); if (reg) { #ifdef CONFIG_PPC_DCR_NATIVE snprintf(name, BUS_ID_SIZE, "d%x.%s", @@ -197,7 +198,7 @@ #endif /* CONFIG_PPC_DCR */ /* * For MMIO, get the physical address */ - reg = get_property(node, "reg", NULL); + reg = of_get_property(node, "reg", NULL); if (reg) { addr = of_translate_address(node, reg); if (addr != OF_BAD_ADDR) { @@ -475,9 +476,6 @@ static struct of_platform_driver of_pci_ .name = "of-pci", .match_table = of_pci_phb_ids, .probe = of_pci_phb_probe, - .driver = { - .multithread_probe = 1, - }, }; static __init int of_pci_phb_init(void) diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index d8ef2e1..f022862 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -637,7 +637,7 @@ make_one_node_map(struct device_node* no if (pci_bus >= pci_bus_count) return; - bus_range = get_property(node, "bus-range", &len); + bus_range = of_get_property(node, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, " "assuming it starts at 0\n", node->full_name); @@ -649,17 +649,20 @@ make_one_node_map(struct device_node* no struct pci_dev* dev; const unsigned int *class_code, *reg; - class_code = get_property(node, "class-code", NULL); + class_code = of_get_property(node, "class-code", NULL); if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) continue; - reg = get_property(node, "reg", NULL); + reg = of_get_property(node, "reg", NULL); if (!reg) continue; - dev = pci_find_slot(pci_bus, ((reg[0] >> 8) & 0xff)); - if (!dev || !dev->subordinate) + dev = pci_get_bus_and_slot(pci_bus, ((reg[0] >> 8) & 0xff)); + if (!dev || !dev->subordinate) { + pci_dev_put(dev); continue; + } make_one_node_map(node, dev->subordinate->number); + pci_dev_put(dev); } } @@ -669,6 +672,7 @@ pcibios_make_OF_bus_map(void) int i; struct pci_controller* hose; struct property *map_prop; + struct device_node *dn; pci_to_OF_bus_map = kmalloc(pci_bus_count, GFP_KERNEL); if (!pci_to_OF_bus_map) { @@ -690,12 +694,13 @@ pcibios_make_OF_bus_map(void) continue; make_one_node_map(node, hose->first_busno); } - map_prop = of_find_property(find_path_device("/"), - "pci-OF-bus-map", NULL); + dn = of_find_node_by_path("/"); + map_prop = of_find_property(dn, "pci-OF-bus-map", NULL); if (map_prop) { BUG_ON(pci_bus_count > map_prop->length); memcpy(map_prop->value, pci_to_OF_bus_map, pci_bus_count); } + of_node_put(dn); #ifdef DEBUG printk("PCI->OF bus map:\n"); for (i=0; i> 8) != PCI_CLASS_BRIDGE_PCI && (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) && strcmp(node->name, "multifunc-device")) @@ -744,7 +749,7 @@ static struct device_node *scan_OF_for_p unsigned int psize; while ((np = of_get_next_child(parent, np)) != NULL) { - reg = get_property(np, "reg", &psize); + reg = of_get_property(np, "reg", &psize); if (reg == NULL || psize < 4) continue; if (((reg[0] >> 8) & 0xff) == devfn) @@ -859,7 +864,7 @@ pci_device_from_OF_node(struct device_no if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child, find_OF_pci_device_filter, (void *)node)) return -ENODEV; - reg = get_property(node, "reg", NULL); + reg = of_get_property(node, "reg", NULL); if (!reg) return -ENODEV; *bus = (reg[0] >> 16) & 0xff; @@ -895,14 +900,14 @@ pci_process_bridge_OF_ranges(struct pci_ int rlen = 0, orig_rlen; int memno = 0; struct resource *res; - int np, na = prom_n_addr_cells(dev); + int np, na = of_n_addr_cells(dev); np = na + 5; /* First we try to merge ranges to fix a problem with some pmacs * that can have more than 3 ranges, fortunately using contiguous * addresses -- BenH */ - dt_ranges = get_property(dev, "ranges", &rlen); + dt_ranges = of_get_property(dev, "ranges", &rlen); if (!dt_ranges) return; /* Sanity check, though hopefully that never happens */ @@ -1006,14 +1011,19 @@ void __init pci_create_OF_bus_map(void) { struct property* of_prop; - + struct device_node *dn; + of_prop = (struct property*) alloc_bootmem(sizeof(struct property) + 256); - if (of_prop && find_path_device("/")) { + if (!of_prop) + return; + dn = of_find_node_by_path("/"); + if (dn) { memset(of_prop, -1, sizeof(struct property) + 256); of_prop->name = "pci-OF-bus-map"; of_prop->length = 256; - of_prop->value = (unsigned char *)&of_prop[1]; - prom_add_property(find_path_device("/"), of_prop); + of_prop->value = &of_prop[1]; + prom_add_property(dn, of_prop); + of_node_put(dn); } } diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 7e97d71..60d7d4b 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -61,8 +61,7 @@ void iSeries_pcibios_init(void); LIST_HEAD(hose_list); -struct dma_mapping_ops *pci_dma_ops; -EXPORT_SYMBOL(pci_dma_ops); +static struct dma_mapping_ops *pci_dma_ops; int global_phb_number; /* Global phb counter */ @@ -70,6 +69,17 @@ int global_phb_number; /* Global phb co struct pci_dev *ppc64_isabridge_dev = NULL; EXPORT_SYMBOL_GPL(ppc64_isabridge_dev); +void set_pci_dma_ops(struct dma_mapping_ops *dma_ops) +{ + pci_dma_ops = dma_ops; +} + +struct dma_mapping_ops *get_pci_dma_ops(void) +{ + return pci_dma_ops; +} +EXPORT_SYMBOL(get_pci_dma_ops); + static void fixup_broken_pcnet32(struct pci_dev* dev) { if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) { @@ -258,7 +268,7 @@ static u32 get_int_prop(struct device_no const u32 *prop; int len; - prop = get_property(np, name, &len); + prop = of_get_property(np, name, &len); if (prop && len >= 4) return *prop; return def; @@ -291,7 +301,7 @@ static void pci_parse_of_addrs(struct de u32 i; int proplen; - addrs = get_property(node, "assigned-addresses", &proplen); + addrs = of_get_property(node, "assigned-addresses", &proplen); if (!addrs) return; DBG(" parse addresses (%d bytes) @ %p\n", proplen, addrs); @@ -333,7 +343,7 @@ struct pci_dev *of_create_pci_dev(struct dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); if (!dev) return NULL; - type = get_property(node, "device_type", NULL); + type = of_get_property(node, "device_type", NULL); if (type == NULL) type = ""; @@ -397,7 +407,7 @@ void __devinit of_scan_bus(struct device while ((child = of_get_next_child(node, child)) != NULL) { DBG(" * %s\n", child->full_name); - reg = get_property(child, "reg", ®len); + reg = of_get_property(child, "reg", ®len); if (reg == NULL || reglen < 20) continue; devfn = (reg[0] >> 8) & 0xff; @@ -430,13 +440,13 @@ void __devinit of_scan_pci_bridge(struct DBG("of_scan_pci_bridge(%s)\n", node->full_name); /* parse bus-range property */ - busrange = get_property(node, "bus-range", &len); + busrange = of_get_property(node, "bus-range", &len); if (busrange == NULL || len != 8) { printk(KERN_DEBUG "Can't get bus-range for PCI-PCI bridge %s\n", node->full_name); return; } - ranges = get_property(node, "ranges", &len); + ranges = of_get_property(node, "ranges", &len); if (ranges == NULL) { printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", node->full_name); @@ -900,7 +910,7 @@ static void __devinit pci_process_ISA_OF unsigned int size; int rlen = 0; - range = get_property(isa_node, "ranges", &rlen); + range = of_get_property(isa_node, "ranges", &rlen); if (range == NULL || (rlen < sizeof(struct isa_range))) { printk(KERN_ERR "no ISA ranges or unexpected isa range size," "mapping 64k\n"); @@ -947,7 +957,7 @@ void __devinit pci_process_bridge_OF_ran int rlen = 0; int memno = 0; struct resource *res; - int np, na = prom_n_addr_cells(dev); + int np, na = of_n_addr_cells(dev); unsigned long pci_addr, cpu_phys_addr; np = na + 5; @@ -960,7 +970,7 @@ void __devinit pci_process_bridge_OF_ran * (size depending on dev->n_addr_cells) * cells 4+5 or 5+6: the size of the range */ - ranges = get_property(dev, "ranges", &rlen); + ranges = of_get_property(dev, "ranges", &rlen); if (ranges == NULL) return; hose->io_base_phys = 0; diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 68df018..d7d36df 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -40,7 +40,8 @@ #include static void * __devinit update_dn_pci_info(struct device_node *dn, void *data) { struct pci_controller *phb = data; - const int *type = get_property(dn, "ibm,pci-config-space-type", NULL); + const int *type = + of_get_property(dn, "ibm,pci-config-space-type", NULL); const u32 *regs; struct pci_dn *pdn; @@ -54,14 +55,14 @@ static void * __devinit update_dn_pci_in dn->data = pdn; pdn->node = dn; pdn->phb = phb; - regs = get_property(dn, "reg", NULL); + regs = of_get_property(dn, "reg", NULL); if (regs) { /* First register entry is addr (00BBSS00) */ pdn->busno = (regs[0] >> 16) & 0xff; pdn->devfn = (regs[0] >> 8) & 0xff; } if (firmware_has_feature(FW_FEATURE_ISERIES)) { - const u32 *busp = get_property(dn, "linux,subbus", NULL); + const u32 *busp = of_get_property(dn, "linux,subbus", NULL); if (busp) pdn->bussubno = *busp; } @@ -100,7 +101,7 @@ void *traverse_pci_devices(struct device u32 class; nextdn = NULL; - classp = get_property(dn, "class-code", NULL); + classp = of_get_property(dn, "class-code", NULL); class = classp ? *classp : 0; if (pre && ((ret = pre(dn, data)) != NULL)) diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index ecee596..ff252aa 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -20,7 +20,6 @@ #include #include #include #include -#include #include #include #include @@ -84,8 +83,6 @@ EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(strcat); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strcasecmp); -EXPORT_SYMBOL(strncasecmp); EXPORT_SYMBOL(csum_partial); EXPORT_SYMBOL(csum_partial_copy_generic); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index e53b298..e509aae 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -305,9 +305,7 @@ #ifdef CONFIG_PPC64 /* for now */ set_dabr(new->thread.dabr); __get_cpu_var(current_dabr) = new->thread.dabr; } - - flush_tlb_pending(); -#endif +#endif /* CONFIG_PPC64 */ new_thread = &new->thread; old_thread = ¤t->thread; @@ -402,11 +400,11 @@ static void printbits(unsigned long val, } #ifdef CONFIG_PPC64 -#define REG "%016lX" +#define REG "%016lx" #define REGS_PER_LINE 4 #define LAST_VOLATILE 13 #else -#define REG "%08lX" +#define REG "%08lx" #define REGS_PER_LINE 8 #define LAST_VOLATILE 12 #endif @@ -421,7 +419,7 @@ void show_regs(struct pt_regs * regs) regs, regs->trap, print_tainted(), init_utsname()->release); printk("MSR: "REG" ", regs->msr); printbits(regs->msr, msr_bits); - printk(" CR: %08lX XER: %08lX\n", regs->ccr, regs->xer); + printk(" CR: %08lx XER: %08lx\n", regs->ccr, regs->xer); trap = TRAP(regs); if (trap == 0x300 || trap == 0x600) printk("DAR: "REG", DSISR: "REG"\n", regs->dar, regs->dsisr); @@ -572,7 +570,6 @@ #ifdef CONFIG_PPC64 kregs->nip = *((unsigned long *)ret_from_fork); #else kregs->nip = (unsigned long)ret_from_fork; - p->thread.last_syscall = -1; #endif return 0; @@ -823,6 +820,35 @@ out: return error; } +#ifdef CONFIG_IRQSTACKS +static inline int valid_irq_stack(unsigned long sp, struct task_struct *p, + unsigned long nbytes) +{ + unsigned long stack_page; + unsigned long cpu = task_cpu(p); + + /* + * Avoid crashing if the stack has overflowed and corrupted + * task_cpu(p), which is in the thread_info struct. + */ + if (cpu < NR_CPUS && cpu_possible(cpu)) { + stack_page = (unsigned long) hardirq_ctx[cpu]; + if (sp >= stack_page + sizeof(struct thread_struct) + && sp <= stack_page + THREAD_SIZE - nbytes) + return 1; + + stack_page = (unsigned long) softirq_ctx[cpu]; + if (sp >= stack_page + sizeof(struct thread_struct) + && sp <= stack_page + THREAD_SIZE - nbytes) + return 1; + } + return 0; +} + +#else +#define valid_irq_stack(sp, p, nb) 0 +#endif /* CONFIG_IRQSTACKS */ + int validate_sp(unsigned long sp, struct task_struct *p, unsigned long nbytes) { @@ -832,19 +858,7 @@ int validate_sp(unsigned long sp, struct && sp <= stack_page + THREAD_SIZE - nbytes) return 1; -#ifdef CONFIG_IRQSTACKS - stack_page = (unsigned long) hardirq_ctx[task_cpu(p)]; - if (sp >= stack_page + sizeof(struct thread_struct) - && sp <= stack_page + THREAD_SIZE - nbytes) - return 1; - - stack_page = (unsigned long) softirq_ctx[task_cpu(p)]; - if (sp >= stack_page + sizeof(struct thread_struct) - && sp <= stack_page + THREAD_SIZE - nbytes) - return 1; -#endif - - return 0; + return valid_irq_stack(sp, p, nbytes); } #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 8d52b23..caef555 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -390,18 +390,19 @@ #endif if (allnextpp) { pp->name = "name"; pp->length = sz; - pp->value = (unsigned char *)(pp + 1); + pp->value = pp + 1; *prev_pp = pp; prev_pp = &pp->next; memcpy(pp->value, ps, sz - 1); ((char *)pp->value)[sz - 1] = 0; - DBG("fixed up name for %s -> %s\n", pathp, pp->value); + DBG("fixed up name for %s -> %s\n", pathp, + (char *)pp->value); } } if (allnextpp) { *prev_pp = NULL; - np->name = get_property(np, "name", NULL); - np->type = get_property(np, "device_type", NULL); + np->name = of_get_property(np, "name", NULL); + np->type = of_get_property(np, "device_type", NULL); if (!np->name) np->name = ""; @@ -719,6 +720,7 @@ static int __init early_init_dt_scan_cho const char *uname, int depth, void *data) { unsigned long *lprop; + u32 *prop; unsigned long l; char *p; @@ -760,6 +762,22 @@ #ifdef CONFIG_KEXEC crashk_res.end = crashk_res.start + *lprop - 1; #endif +#ifdef CONFIG_BLK_DEV_INITRD + DBG("Looking for initrd properties... "); + prop = of_get_flat_dt_prop(node, "linux,initrd-start", &l); + if (prop) { + initrd_start = (unsigned long)__va(of_read_ulong(prop, l/4)); + prop = of_get_flat_dt_prop(node, "linux,initrd-end", &l); + if (prop) { + initrd_end = (unsigned long)__va(of_read_ulong(prop, l/4)); + initrd_below_start_ok = 1; + } else { + initrd_start = 0; + } + } + DBG("initrd_start=0x%lx initrd_end=0x%lx\n", initrd_start, initrd_end); +#endif /* CONFIG_BLK_DEV_INITRD */ + /* Retreive command line */ p = of_get_flat_dt_prop(node, "bootargs", &l); if (p != NULL && l > 0) @@ -926,6 +944,12 @@ static void __init early_reserve_mem(voi self_size = initial_boot_params->totalsize; lmb_reserve(self_base, self_size); +#ifdef CONFIG_BLK_DEV_INITRD + /* then reserve the initrd, if any */ + if (initrd_start && (initrd_end > initrd_start)) + lmb_reserve(__pa(initrd_start), initrd_end - initrd_start); +#endif /* CONFIG_BLK_DEV_INITRD */ + #ifdef CONFIG_PPC32 /* * Handle the case where we might be booting from an old kexec @@ -954,9 +978,6 @@ #endif size = *(reserve_map++); if (size == 0) break; - /* skip if the reservation is for the blob */ - if (base == self_base && size == self_size) - continue; DBG("reserving: %llx -> %llx\n", base, size); lmb_reserve(base, size); } @@ -1021,102 +1042,46 @@ #endif #undef printk -int -prom_n_addr_cells(struct device_node* np) +int of_n_addr_cells(struct device_node* np) { const int *ip; do { if (np->parent) np = np->parent; - ip = get_property(np, "#address-cells", NULL); + ip = of_get_property(np, "#address-cells", NULL); if (ip != NULL) return *ip; } while (np->parent); /* No #address-cells property for the root node, default to 1 */ return 1; } -EXPORT_SYMBOL(prom_n_addr_cells); +EXPORT_SYMBOL(of_n_addr_cells); -int -prom_n_size_cells(struct device_node* np) +int of_n_size_cells(struct device_node* np) { const int* ip; do { if (np->parent) np = np->parent; - ip = get_property(np, "#size-cells", NULL); + ip = of_get_property(np, "#size-cells", NULL); if (ip != NULL) return *ip; } while (np->parent); /* No #size-cells property for the root node, default to 1 */ return 1; } -EXPORT_SYMBOL(prom_n_size_cells); - -/** - * Construct and return a list of the device_nodes with a given name. - */ -struct device_node *find_devices(const char *name) -{ - struct device_node *head, **prevp, *np; - - prevp = &head; - for (np = allnodes; np != 0; np = np->allnext) { - if (np->name != 0 && strcasecmp(np->name, name) == 0) { - *prevp = np; - prevp = &np->next; - } - } - *prevp = NULL; - return head; -} -EXPORT_SYMBOL(find_devices); - -/** - * Construct and return a list of the device_nodes with a given type. - */ -struct device_node *find_type_devices(const char *type) -{ - struct device_node *head, **prevp, *np; - - prevp = &head; - for (np = allnodes; np != 0; np = np->allnext) { - if (np->type != 0 && strcasecmp(np->type, type) == 0) { - *prevp = np; - prevp = &np->next; - } - } - *prevp = NULL; - return head; -} -EXPORT_SYMBOL(find_type_devices); - -/** - * Returns all nodes linked together - */ -struct device_node *find_all_nodes(void) -{ - struct device_node *head, **prevp, *np; - - prevp = &head; - for (np = allnodes; np != 0; np = np->allnext) { - *prevp = np; - prevp = &np->next; - } - *prevp = NULL; - return head; -} -EXPORT_SYMBOL(find_all_nodes); +EXPORT_SYMBOL(of_n_size_cells); /** Checks if the given "compat" string matches one of the strings in * the device's "compatible" property */ -int device_is_compatible(const struct device_node *device, const char *compat) +int of_device_is_compatible(const struct device_node *device, + const char *compat) { const char* cp; int cplen, l; - cp = get_property(device, "compatible", &cplen); + cp = of_get_property(device, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) { @@ -1129,7 +1094,7 @@ int device_is_compatible(const struct de return 0; } -EXPORT_SYMBOL(device_is_compatible); +EXPORT_SYMBOL(of_device_is_compatible); /** @@ -1143,51 +1108,13 @@ int machine_is_compatible(const char *co root = of_find_node_by_path("/"); if (root) { - rc = device_is_compatible(root, compat); + rc = of_device_is_compatible(root, compat); of_node_put(root); } return rc; } EXPORT_SYMBOL(machine_is_compatible); -/** - * Construct and return a list of the device_nodes with a given type - * and compatible property. - */ -struct device_node *find_compatible_devices(const char *type, - const char *compat) -{ - struct device_node *head, **prevp, *np; - - prevp = &head; - for (np = allnodes; np != 0; np = np->allnext) { - if (type != NULL - && !(np->type != 0 && strcasecmp(np->type, type) == 0)) - continue; - if (device_is_compatible(np, compat)) { - *prevp = np; - prevp = &np->next; - } - } - *prevp = NULL; - return head; -} -EXPORT_SYMBOL(find_compatible_devices); - -/** - * Find the device_node with a given full_name. - */ -struct device_node *find_path_device(const char *path) -{ - struct device_node *np; - - for (np = allnodes; np != 0; np = np->allnext) - if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) - return np; - return NULL; -} -EXPORT_SYMBOL(find_path_device); - /******* * * New implementation of the OF "find" APIs, return a refcounted @@ -1280,7 +1207,7 @@ struct device_node *of_find_compatible_n if (type != NULL && !(np->type != 0 && strcasecmp(np->type, type) == 0)) continue; - if (device_is_compatible(np, compatible) && of_node_get(np)) + if (of_device_is_compatible(np, compatible) && of_node_get(np)) break; } of_node_put(from); @@ -1527,8 +1454,8 @@ static int of_finish_dynamic_node(struct int err = 0; const phandle *ibm_phandle; - node->name = get_property(node, "name", NULL); - node->type = get_property(node, "device_type", NULL); + node->name = of_get_property(node, "name", NULL); + node->type = of_get_property(node, "device_type", NULL); if (!parent) { err = -ENODEV; @@ -1542,7 +1469,7 @@ static int of_finish_dynamic_node(struct return -ENODEV; /* fix up new node's linux_phandle field */ - if ((ibm_phandle = get_property(node, "ibm,phandle", NULL))) + if ((ibm_phandle = of_get_property(node, "ibm,phandle", NULL))) node->linux_phandle = *ibm_phandle; out: @@ -1605,13 +1532,13 @@ EXPORT_SYMBOL(of_find_property); * Find a property with a given name for a given node * and return the value. */ -const void *get_property(const struct device_node *np, const char *name, +const void *of_get_property(const struct device_node *np, const char *name, int *lenp) { struct property *pp = of_find_property(np,name,lenp); return pp ? pp->value : NULL; } -EXPORT_SYMBOL(get_property); +EXPORT_SYMBOL(of_get_property); /* * Add a property to a node @@ -1742,10 +1669,10 @@ struct device_node *of_get_cpu_node(int /* Check for ibm,ppc-interrupt-server#s. If it doesn't exist * fallback to "reg" property and assume no threads */ - intserv = get_property(np, "ibm,ppc-interrupt-server#s", + intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &plen); if (intserv == NULL) { - const u32 *reg = get_property(np, "reg", NULL); + const u32 *reg = of_get_property(np, "reg", NULL); if (reg == NULL) continue; if (*reg == hardid) { diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 4fb5938..e27d9d1 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -2035,39 +2035,50 @@ #define fixup_device_tree_maple() #endif #ifdef CONFIG_PPC_CHRP -/* Pegasos and BriQ lacks the "ranges" property in the isa node */ +/* + * Pegasos and BriQ lacks the "ranges" property in the isa node + * Pegasos needs decimal IRQ 14/15, not hexadecimal + */ static void __init fixup_device_tree_chrp(void) { - phandle isa; - u32 isa_ranges[6]; + phandle ph; + u32 prop[6]; u32 rloc = 0x01006000; /* IO space; PCI device = 12 */ char *name; int rc; name = "/pci@80000000/isa@c"; - isa = call_prom("finddevice", 1, 1, ADDR(name)); - if (!PHANDLE_VALID(isa)) { + ph = call_prom("finddevice", 1, 1, ADDR(name)); + if (!PHANDLE_VALID(ph)) { name = "/pci@ff500000/isa@6"; - isa = call_prom("finddevice", 1, 1, ADDR(name)); + ph = call_prom("finddevice", 1, 1, ADDR(name)); rloc = 0x01003000; /* IO space; PCI device = 6 */ } - if (!PHANDLE_VALID(isa)) - return; - - rc = prom_getproplen(isa, "ranges"); - if (rc != 0 && rc != PROM_ERROR) - return; - - prom_printf("Fixing up missing ISA range on Pegasos...\n"); + if (PHANDLE_VALID(ph)) { + rc = prom_getproplen(ph, "ranges"); + if (rc == 0 || rc == PROM_ERROR) { + prom_printf("Fixing up missing ISA range on Pegasos...\n"); + + prop[0] = 0x1; + prop[1] = 0x0; + prop[2] = rloc; + prop[3] = 0x0; + prop[4] = 0x0; + prop[5] = 0x00010000; + prom_setprop(ph, name, "ranges", prop, sizeof(prop)); + } + } - isa_ranges[0] = 0x1; - isa_ranges[1] = 0x0; - isa_ranges[2] = rloc; - isa_ranges[3] = 0x0; - isa_ranges[4] = 0x0; - isa_ranges[5] = 0x00010000; - prom_setprop(isa, name, "ranges", - isa_ranges, sizeof(isa_ranges)); + name = "/pci@80000000/ide@C,1"; + ph = call_prom("finddevice", 1, 1, ADDR(name)); + if (PHANDLE_VALID(ph)) { + prom_printf("Fixing up IDE interrupt on Pegasos...\n"); + prop[0] = 14; + prop[1] = 0x0; + prop[2] = 15; + prop[3] = 0x0; + prom_setprop(ph, name, "interrupts", prop, 4*sizeof(u32)); + } } #else #define fixup_device_tree_chrp() diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index 91b443c..aa40a53 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -68,9 +68,9 @@ static void of_bus_default_count_cells(s int *addrc, int *sizec) { if (addrc) - *addrc = prom_n_addr_cells(dev); + *addrc = of_n_addr_cells(dev); if (sizec) - *sizec = prom_n_size_cells(dev); + *sizec = of_n_size_cells(dev); } static u64 of_bus_default_map(u32 *addr, const u32 *range, @@ -196,7 +196,7 @@ const u32 *of_get_pci_address(struct dev return NULL; /* Get "reg" or "assigned-addresses" property */ - prop = get_property(dev, bus->addresses, &psize); + prop = of_get_property(dev, bus->addresses, &psize); if (prop == NULL) return NULL; psize /= 4; @@ -438,7 +438,7 @@ static int of_translate_one(struct devic * to translate addresses that aren't supposed to be translated in * the first place. --BenH. */ - ranges = get_property(parent, "ranges", &rlen); + ranges = of_get_property(parent, "ranges", &rlen); if (ranges == NULL || rlen == 0) { offset = of_read_number(addr, na); memset(addr, 0, pna * 4); @@ -578,7 +578,7 @@ const u32 *of_get_address(struct device_ return NULL; /* Get "reg" or "assigned-addresses" property */ - prop = get_property(dev, bus->addresses, &psize); + prop = of_get_property(dev, bus->addresses, &psize); if (prop == NULL) return NULL; psize /= 4; @@ -650,17 +650,17 @@ void of_parse_dma_window(struct device_n /* busno is always one cell */ *busno = *(dma_window++); - prop = get_property(dn, "ibm,#dma-address-cells", NULL); + prop = of_get_property(dn, "ibm,#dma-address-cells", NULL); if (!prop) - prop = get_property(dn, "#address-cells", NULL); + prop = of_get_property(dn, "#address-cells", NULL); - cells = prop ? *(u32 *)prop : prom_n_addr_cells(dn); + cells = prop ? *(u32 *)prop : of_n_addr_cells(dn); *phys = of_read_number(dma_window, cells); dma_window += cells; - prop = get_property(dn, "ibm,#dma-size-cells", NULL); - cells = prop ? *(u32 *)prop : prom_n_size_cells(dn); + prop = of_get_property(dn, "ibm,#dma-size-cells", NULL); + cells = prop ? *(u32 *)prop : of_n_size_cells(dn); *size = of_read_number(dma_window, cells); } @@ -680,7 +680,7 @@ static struct device_node *of_irq_find_p return NULL; do { - parp = get_property(child, "interrupt-parent", NULL); + parp = of_get_property(child, "interrupt-parent", NULL); if (parp == NULL) p = of_get_parent(child); else { @@ -691,7 +691,7 @@ static struct device_node *of_irq_find_p } of_node_put(child); child = p; - } while (p && get_property(p, "#interrupt-cells", NULL) == NULL); + } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL); return p; } @@ -716,7 +716,7 @@ void of_irq_map_init(unsigned int flags) struct device_node *np; for(np = NULL; (np = of_find_all_nodes(np)) != NULL;) { - if (get_property(np, "interrupt-controller", NULL) + if (of_get_property(np, "interrupt-controller", NULL) == NULL) continue; /* Skip /chosen/interrupt-controller */ @@ -755,7 +755,7 @@ int of_irq_map_raw(struct device_node *p * is none, we are nice and just walk up the tree */ do { - tmp = get_property(ipar, "#interrupt-cells", NULL); + tmp = of_get_property(ipar, "#interrupt-cells", NULL); if (tmp != NULL) { intsize = *tmp; break; @@ -779,7 +779,7 @@ int of_irq_map_raw(struct device_node *p */ old = of_node_get(ipar); do { - tmp = get_property(old, "#address-cells", NULL); + tmp = of_get_property(old, "#address-cells", NULL); tnode = of_get_parent(old); of_node_put(old); old = tnode; @@ -795,7 +795,8 @@ int of_irq_map_raw(struct device_node *p /* Now check if cursor is an interrupt-controller and if it is * then we are done */ - if (get_property(ipar, "interrupt-controller", NULL) != NULL) { + if (of_get_property(ipar, "interrupt-controller", NULL) != + NULL) { DBG(" -> got it !\n"); memcpy(out_irq->specifier, intspec, intsize * sizeof(u32)); @@ -806,7 +807,7 @@ int of_irq_map_raw(struct device_node *p } /* Now look for an interrupt-map */ - imap = get_property(ipar, "interrupt-map", &imaplen); + imap = of_get_property(ipar, "interrupt-map", &imaplen); /* No interrupt map, check for an interrupt parent */ if (imap == NULL) { DBG(" -> no map, getting parent\n"); @@ -816,7 +817,7 @@ int of_irq_map_raw(struct device_node *p imaplen /= sizeof(u32); /* Look for a mask */ - imask = get_property(ipar, "interrupt-map-mask", NULL); + imask = of_get_property(ipar, "interrupt-map-mask", NULL); /* If we were passed no "reg" property and we attempt to parse * an interrupt-map, then #address-cells must be 0. @@ -863,15 +864,13 @@ int of_irq_map_raw(struct device_node *p /* Get #interrupt-cells and #address-cells of new * parent */ - tmp = get_property(newpar, "#interrupt-cells", - NULL); + tmp = of_get_property(newpar, "#interrupt-cells", NULL); if (tmp == NULL) { DBG(" -> parent lacks #interrupt-cells !\n"); goto fail; } newintsize = *tmp; - tmp = get_property(newpar, "#address-cells", - NULL); + tmp = of_get_property(newpar, "#address-cells", NULL); newaddrsize = (tmp == NULL) ? 0 : *tmp; DBG(" -> newintsize=%d, newaddrsize=%d\n", @@ -928,7 +927,7 @@ static int of_irq_map_oldworld(struct de * everything together on these) */ while (device) { - ints = get_property(device, "AAPL,interrupts", &intlen); + ints = of_get_property(device, "AAPL,interrupts", &intlen); if (ints != NULL) break; device = device->parent; @@ -970,13 +969,13 @@ int of_irq_map_one(struct device_node *d return of_irq_map_oldworld(device, index, out_irq); /* Get the interrupts property */ - intspec = get_property(device, "interrupts", &intlen); + intspec = of_get_property(device, "interrupts", &intlen); if (intspec == NULL) return -EINVAL; intlen /= sizeof(u32); /* Get the reg property (if any) */ - addr = get_property(device, "reg", NULL); + addr = of_get_property(device, "reg", NULL); /* Look for the interrupt parent. */ p = of_irq_find_parent(device); @@ -984,7 +983,7 @@ int of_irq_map_one(struct device_node *d return -EINVAL; /* Get size of interrupt specifier */ - tmp = get_property(p, "#interrupt-cells", NULL); + tmp = of_get_property(p, "#interrupt-cells", NULL); if (tmp == NULL) { of_node_put(p); return -EINVAL; diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c index 6cbf2ae..190b7ed 100644 --- a/arch/powerpc/kernel/rtas-proc.c +++ b/arch/powerpc/kernel/rtas-proc.c @@ -450,7 +450,7 @@ static int ppc_rtas_sensors_show(struct int llen, offs; sprintf (rstr, SENSOR_PREFIX"%04d", p->token); - loc = get_property(rtas_node, rstr, &llen); + loc = of_get_property(rtas_node, rstr, &llen); /* A sensor may have multiple instances */ for (j = 0, offs = 0; j <= p->quant; j++) { @@ -477,7 +477,7 @@ static int ppc_rtas_find_all_sensors(voi const unsigned int *utmp; int len, i; - utmp = get_property(rtas_node, "rtas-sensors", &len); + utmp = of_get_property(rtas_node, "rtas-sensors", &len); if (utmp == NULL) { printk (KERN_ERR "error: could not get rtas-sensors\n"); return 1; diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 9d0735a..2147807 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -192,18 +192,19 @@ void rtas_progress(char *s, unsigned sho if (display_width == 0) { display_width = 0x10; - if ((root = find_path_device("/rtas"))) { - if ((p = get_property(root, + if ((root = of_find_node_by_path("/rtas"))) { + if ((p = of_get_property(root, "ibm,display-line-length", NULL))) display_width = *p; - if ((p = get_property(root, + if ((p = of_get_property(root, "ibm,form-feed", NULL))) form_feed = *p; - if ((p = get_property(root, + if ((p = of_get_property(root, "ibm,display-number-of-lines", NULL))) display_lines = *p; - row_width = get_property(root, + row_width = of_get_property(root, "ibm,display-truncation-length", NULL); + of_node_put(root); } display_character = rtas_token("display-character"); set_indicator = rtas_token("set-indicator"); @@ -298,7 +299,7 @@ int rtas_token(const char *service) const int *tokp; if (rtas.dev == NULL) return RTAS_UNKNOWN_SERVICE; - tokp = get_property(rtas.dev, service, NULL); + tokp = of_get_property(rtas.dev, service, NULL); return tokp ? *tokp : RTAS_UNKNOWN_SERVICE; } EXPORT_SYMBOL(rtas_token); @@ -832,12 +833,12 @@ void __init rtas_initialize(void) if (rtas.dev) { const u32 *basep, *entryp, *sizep; - basep = get_property(rtas.dev, "linux,rtas-base", NULL); - sizep = get_property(rtas.dev, "rtas-size", NULL); + basep = of_get_property(rtas.dev, "linux,rtas-base", NULL); + sizep = of_get_property(rtas.dev, "rtas-size", NULL); if (basep != NULL && sizep != NULL) { rtas.base = *basep; rtas.size = *sizep; - entryp = get_property(rtas.dev, + entryp = of_get_property(rtas.dev, "linux,rtas-entry", NULL); if (entryp == NULL) /* Ugh */ rtas.entry = rtas.base; diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index ace9f4c..f228682 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -60,7 +60,7 @@ static int of_device_available(struct de { const char *status; - status = get_property(dn, "status", NULL); + status = of_get_property(dn, "status", NULL); if (!status) return 1; @@ -177,7 +177,7 @@ struct pci_ops rtas_pci_ops = { int is_python(struct device_node *dev) { - const char *model = get_property(dev, "model", NULL); + const char *model = of_get_property(dev, "model", NULL); if (model && strstr(model, "Python")) return 1; @@ -247,7 +247,7 @@ static int phb_set_bus_ranges(struct dev const int *bus_range; unsigned int len; - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { return 1; } @@ -274,7 +274,7 @@ int __devinit rtas_setup_phb(struct pci_ return 0; } -unsigned long __init find_and_init_phbs(void) +void __init find_and_init_phbs(void) { struct device_node *node; struct pci_controller *phb; @@ -309,18 +309,16 @@ unsigned long __init find_and_init_phbs( if (of_chosen) { const int *prop; - prop = get_property(of_chosen, + prop = of_get_property(of_chosen, "linux,pci-probe-only", NULL); if (prop) pci_probe_only = *prop; - prop = get_property(of_chosen, + prop = of_get_property(of_chosen, "linux,pci-assign-all-buses", NULL); if (prop) pci_assign_all_buses = *prop; } - - return 0; } /* RPA-specific bits for removing PHBs */ diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 89cfaf4..3708037 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -21,7 +21,6 @@ #include #include #include #include -#include #include #include #include @@ -304,26 +303,8 @@ struct seq_operations cpuinfo_op = { void __init check_for_initrd(void) { #ifdef CONFIG_BLK_DEV_INITRD - const unsigned int *prop; - int len; - - DBG(" -> check_for_initrd()\n"); - - if (of_chosen) { - prop = get_property(of_chosen, "linux,initrd-start", &len); - if (prop != NULL) { - initrd_start = (unsigned long) - __va(of_read_ulong(prop, len / 4)); - prop = get_property(of_chosen, - "linux,initrd-end", &len); - if (prop != NULL) { - initrd_end = (unsigned long) - __va(of_read_ulong(prop, len / 4)); - initrd_below_start_ok = 1; - } else - initrd_start = 0; - } - } + DBG(" -> check_for_initrd() initrd_start=0x%lx initrd_end=0x%lx\n", + initrd_start, initrd_end); /* If we were passed an initrd, set the ROOT_DEV properly if the values * look sensible. If not, clear initrd reference. @@ -371,11 +352,12 @@ void __init smp_setup_cpu_maps(void) const int *intserv; int j, len = sizeof(u32), nthreads = 1; - intserv = get_property(dn, "ibm,ppc-interrupt-server#s", &len); + intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", + &len); if (intserv) nthreads = len / sizeof(int); else { - intserv = get_property(dn, "reg", NULL); + intserv = of_get_property(dn, "reg", NULL); if (!intserv) intserv = &cpu; /* assume logical == phys */ } @@ -398,10 +380,10 @@ #ifdef CONFIG_PPC64 int num_addr_cell, num_size_cell, maxcpus; const unsigned int *ireg; - num_addr_cell = prom_n_addr_cells(dn); - num_size_cell = prom_n_size_cells(dn); + num_addr_cell = of_n_addr_cells(dn); + num_size_cell = of_n_size_cells(dn); - ireg = get_property(dn, "ibm,lrdr-capacity", NULL); + ireg = of_get_property(dn, "ibm,lrdr-capacity", NULL); if (!ireg) goto out; @@ -496,11 +478,39 @@ void probe_machine(void) printk(KERN_INFO "Using %s machine description\n", ppc_md.name); } +/* Match a class of boards, not a specific device configuration. */ int check_legacy_ioport(unsigned long base_port) { - if (ppc_md.check_legacy_ioport == NULL) - return 0; - return ppc_md.check_legacy_ioport(base_port); + struct device_node *parent, *np = NULL; + int ret = -ENODEV; + + switch(base_port) { + case I8042_DATA_REG: + np = of_find_node_by_type(NULL, "8042"); + break; + case FDC_BASE: /* FDC1 */ + np = of_find_node_by_type(NULL, "fdc"); + break; +#ifdef CONFIG_PPC_PREP + case _PIDXR: + case _PNPWRP: + case PNPBIOS_BASE: + /* implement me */ +#endif + default: + /* ipmi is supposed to fail here */ + break; + } + if (!np) + return ret; + parent = of_get_parent(np); + if (parent) { + if (strcmp(parent->type, "isa") == 0) + ret = 0; + of_node_put(parent); + } + of_node_put(np); + return ret; } EXPORT_SYMBOL(check_legacy_ioport); diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 44a6a3c..35f8f44 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -92,7 +92,8 @@ unsigned long __init early_init(unsigned /* First zero the BSS -- use memset_io, some platforms don't have * caches on yet */ - memset_io((void __iomem *)PTRRELOC(&__bss_start), 0, _end - __bss_start); + memset_io((void __iomem *)PTRRELOC(&__bss_start), 0, + __bss_stop - __bss_start); /* * Identify the CPU type and fix up code sections @@ -195,18 +196,22 @@ EXPORT_SYMBOL(nvram_sync); #endif /* CONFIG_NVRAM */ -static struct cpu cpu_devices[NR_CPUS]; +static DEFINE_PER_CPU(struct cpu, cpu_devices); int __init ppc_init(void) { - int i; + int cpu; /* clear the progress line */ - if ( ppc_md.progress ) ppc_md.progress(" ", 0xffff); + if (ppc_md.progress) + ppc_md.progress(" ", 0xffff); /* register CPU devices */ - for_each_possible_cpu(i) - register_cpu(&cpu_devices[i], i); + for_each_possible_cpu(cpu) { + struct cpu *c = &per_cpu(cpu_devices, cpu); + c->hotpluggable = 1; + register_cpu(c, cpu); + } /* call platform init */ if (ppc_md.init != NULL) { diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 3733de3..22083ce 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -20,7 +20,6 @@ #include #include #include #include -#include #include #include #include @@ -110,7 +109,7 @@ static void check_smt_enabled(void) dn = of_find_node_by_path("/options"); if (dn) { - smt_option = get_property(dn, "ibm,smt-enabled", NULL); + smt_option = of_get_property(dn, "ibm,smt-enabled", NULL); if (smt_option) { if (!strcmp(smt_option, "on")) @@ -305,10 +304,10 @@ static void __init initialize_cache_info size = 0; lsize = cur_cpu_spec->dcache_bsize; - sizep = get_property(np, "d-cache-size", NULL); + sizep = of_get_property(np, "d-cache-size", NULL); if (sizep != NULL) size = *sizep; - lsizep = get_property(np, dc, NULL); + lsizep = of_get_property(np, dc, NULL); if (lsizep != NULL) lsize = *lsizep; if (sizep == 0 || lsizep == 0) @@ -322,10 +321,10 @@ static void __init initialize_cache_info size = 0; lsize = cur_cpu_spec->icache_bsize; - sizep = get_property(np, "i-cache-size", NULL); + sizep = of_get_property(np, "i-cache-size", NULL); if (sizep != NULL) size = *sizep; - lsizep = get_property(np, ic, NULL); + lsizep = of_get_property(np, ic, NULL); if (lsizep != NULL) lsize = *lsizep; if (sizep == 0 || lsizep == 0) diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 924d692..d8e503b 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -428,10 +428,6 @@ void generic_mach_cpu_die(void) smp_wmb(); while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE) cpu_relax(); - -#ifdef CONFIG_PPC64 - flush_tlb_pending(); -#endif cpu_set(cpu, cpu_online_map); local_irq_enable(); } diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index 673e8d9..047246a 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -53,10 +53,6 @@ #include #include #include -/* readdir & getdents */ -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de))) -#define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) - struct old_linux_dirent32 { u32 d_ino; u32 d_offset; diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index d57818a..933e214 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -66,16 +66,17 @@ static int __init smt_setup(void) if (!cpu_has_feature(CPU_FTR_SMT)) return -ENODEV; - options = find_path_device("/options"); + options = of_find_node_by_path("/options"); if (!options) return -ENODEV; - val = get_property(options, "ibm,smt-snooze-delay", NULL); + val = of_get_property(options, "ibm,smt-snooze-delay", NULL); if (!smt_snooze_cmdline && val) { for_each_possible_cpu(cpu) per_cpu(smt_snooze_delay, cpu) = *val; } + of_node_put(options); return 0; } __initcall(smt_setup); @@ -189,12 +190,12 @@ SYSFS_PMCSETUP(purr, SPRN_PURR); SYSFS_PMCSETUP(spurr, SPRN_SPURR); SYSFS_PMCSETUP(dscr, SPRN_DSCR); -SYSFS_PMCSETUP(pa6t_pmc0, PA6T_SPRN_PMC0); -SYSFS_PMCSETUP(pa6t_pmc1, PA6T_SPRN_PMC1); -SYSFS_PMCSETUP(pa6t_pmc2, PA6T_SPRN_PMC2); -SYSFS_PMCSETUP(pa6t_pmc3, PA6T_SPRN_PMC3); -SYSFS_PMCSETUP(pa6t_pmc4, PA6T_SPRN_PMC4); -SYSFS_PMCSETUP(pa6t_pmc5, PA6T_SPRN_PMC5); +SYSFS_PMCSETUP(pa6t_pmc0, SPRN_PA6T_PMC0); +SYSFS_PMCSETUP(pa6t_pmc1, SPRN_PA6T_PMC1); +SYSFS_PMCSETUP(pa6t_pmc2, SPRN_PA6T_PMC2); +SYSFS_PMCSETUP(pa6t_pmc3, SPRN_PA6T_PMC3); +SYSFS_PMCSETUP(pa6t_pmc4, SPRN_PA6T_PMC4); +SYSFS_PMCSETUP(pa6t_pmc5, SPRN_PA6T_PMC5); static SYSDEV_ATTR(mmcra, 0600, show_mmcra, store_mmcra); diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index f6f0c6b..7cedef8 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -834,7 +834,7 @@ static int __init get_freq(char *name, i cpu = of_find_node_by_type(NULL, "cpu"); if (cpu) { - fp = get_property(cpu, name, NULL); + fp = of_get_property(cpu, name, NULL); if (fp) { found = 1; *val = of_read_ulong(fp, cells); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 17724fb..f786222 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -90,21 +90,11 @@ EXPORT_SYMBOL(unregister_die_notifier); * Trap & Exception support */ -static DEFINE_SPINLOCK(die_lock); - -int die(const char *str, struct pt_regs *regs, long err) -{ - static int die_counter; - - if (debugger(regs)) - return 1; - - console_verbose(); - spin_lock_irq(&die_lock); - bust_spinlocks(1); #ifdef CONFIG_PMAC_BACKLIGHT +static void pmac_backlight_unblank(void) +{ mutex_lock(&pmac_backlight_mutex); - if (machine_is(powermac) && pmac_backlight) { + if (pmac_backlight) { struct backlight_properties *props; props = &pmac_backlight->props; @@ -113,26 +103,67 @@ #ifdef CONFIG_PMAC_BACKLIGHT backlight_update_status(pmac_backlight); } mutex_unlock(&pmac_backlight_mutex); +} +#else +static inline void pmac_backlight_unblank(void) { } #endif - printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); + +int die(const char *str, struct pt_regs *regs, long err) +{ + static struct { + spinlock_t lock; + u32 lock_owner; + int lock_owner_depth; + } die = { + .lock = __SPIN_LOCK_UNLOCKED(die.lock), + .lock_owner = -1, + .lock_owner_depth = 0 + }; + static int die_counter; + unsigned long flags; + + if (debugger(regs)) + return 1; + + oops_enter(); + + if (die.lock_owner != raw_smp_processor_id()) { + console_verbose(); + spin_lock_irqsave(&die.lock, flags); + die.lock_owner = smp_processor_id(); + die.lock_owner_depth = 0; + bust_spinlocks(1); + if (machine_is(powermac)) + pmac_backlight_unblank(); + } else { + local_save_flags(flags); + } + + if (++die.lock_owner_depth < 3) { + printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); #ifdef CONFIG_PREEMPT - printk("PREEMPT "); + printk("PREEMPT "); #endif #ifdef CONFIG_SMP - printk("SMP NR_CPUS=%d ", NR_CPUS); + printk("SMP NR_CPUS=%d ", NR_CPUS); #endif #ifdef CONFIG_DEBUG_PAGEALLOC - printk("DEBUG_PAGEALLOC "); + printk("DEBUG_PAGEALLOC "); #endif #ifdef CONFIG_NUMA - printk("NUMA "); + printk("NUMA "); #endif - printk("%s\n", ppc_md.name ? "" : ppc_md.name); + printk("%s\n", ppc_md.name ? ppc_md.name : ""); + + print_modules(); + show_regs(regs); + } else { + printk("Recursive die() failure, output suppressed\n"); + } - print_modules(); - show_regs(regs); bust_spinlocks(0); - spin_unlock_irq(&die_lock); + die.lock_owner = -1; + spin_unlock_irqrestore(&die.lock, flags); if (kexec_should_crash(current) || kexec_sr_activated(smp_processor_id())) @@ -145,6 +176,7 @@ #endif if (panic_on_oops) panic("Fatal exception"); + oops_exit(); do_exit(err); return 0; diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index 2968ffe..9eaefac 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -81,7 +81,7 @@ #endif struct iommu_table *tbl; unsigned long offset, size; - dma_window = get_property(dev->dev.archdata.of_node, + dma_window = of_get_property(dev->dev.archdata.of_node, "ibm,my-dma-window", NULL); if (!dma_window) return NULL; @@ -226,7 +226,7 @@ struct vio_dev * __devinit vio_register_ return NULL; } - unit_address = get_property(of_node, "reg", NULL); + unit_address = of_get_property(of_node, "reg", NULL); if (unit_address == NULL) { printk(KERN_WARNING "%s: node %s missing 'reg'\n", __FUNCTION__, @@ -246,7 +246,7 @@ struct vio_dev * __devinit vio_register_ viodev->type = of_node->type; viodev->unit_address = *unit_address; if (firmware_has_feature(FW_FEATURE_ISERIES)) { - unit_address = get_property(of_node, + unit_address = of_get_property(of_node, "linux,unit_address", NULL); if (unit_address != NULL) viodev->unit_address = *unit_address; @@ -308,7 +308,7 @@ #endif /* CONFIG_PPC_ISERIES */ return err; } - node_vroot = find_devices("vdevice"); + node_vroot = of_find_node_by_name(NULL, "vdevice"); if (node_vroot) { struct device_node *of_node; @@ -322,6 +322,7 @@ #endif /* CONFIG_PPC_ISERIES */ __FUNCTION__, of_node); vio_register_device_node(of_node); } + of_node_put(node_vroot); } return 0; @@ -377,7 +378,7 @@ static int vio_hotplug(struct device *de dn = dev->archdata.of_node; if (!dn) return -ENODEV; - cp = get_property(dn, "compatible", &length); + cp = of_get_property(dn, "compatible", &length); if (!cp) return -ENODEV; @@ -406,12 +407,12 @@ struct bus_type vio_bus_type = { * @which: The property/attribute to be extracted. * @length: Pointer to length of returned data size (unused if NULL). * - * Calls prom.c's get_property() to return the value of the + * Calls prom.c's of_get_property() to return the value of the * attribute specified by @which */ const void *vio_get_attribute(struct vio_dev *vdev, char *which, int *length) { - return get_property(vdev->dev.archdata.of_node, which, length); + return of_get_property(vdev->dev.archdata.of_node, which, length); } EXPORT_SYMBOL(vio_get_attribute); @@ -443,7 +444,7 @@ struct vio_dev *vio_find_node(struct dev char kobj_name[BUS_ID_SIZE]; /* construct the kobject name from the device node */ - unit_address = get_property(vnode, "reg", NULL); + unit_address = of_get_property(vnode, "reg", NULL); if (!unit_address) return NULL; snprintf(kobj_name, BUS_ID_SIZE, "%x", *unit_address); diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 4b1ba49..450258d 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -7,13 +7,12 @@ EXTRA_CFLAGS += -mno-minimal-toc endif ifeq ($(CONFIG_PPC_MERGE),y) -obj-y := string.o strcase.o +obj-y := string.o obj-$(CONFIG_PPC32) += div64.o copy_32.o checksum_32.o endif obj-$(CONFIG_PPC64) += checksum_64.o copypage_64.o copyuser_64.o \ - memcpy_64.o usercopy_64.o mem_64.o string.o \ - strcase.o + memcpy_64.o usercopy_64.o mem_64.o string.o obj-$(CONFIG_QUICC_ENGINE) += rheap.o obj-$(CONFIG_XMON) += sstep.o obj-$(CONFIG_KPROBES) += sstep.o diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S index a6b54cb..25ec537 100644 --- a/arch/powerpc/lib/copyuser_64.S +++ b/arch/powerpc/lib/copyuser_64.S @@ -24,7 +24,7 @@ _GLOBAL(__copy_tofrom_user) dcbt 0,r4 beq .Lcopy_page_4K andi. r6,r6,7 - mtcrf 0x01,r5 + PPC_MTOCRF 0x01,r5 blt cr1,.Lshort_copy bne .Ldst_unaligned .Ldst_aligned: @@ -135,7 +135,7 @@ _GLOBAL(__copy_tofrom_user) b .Ldo_tail .Ldst_unaligned: - mtcrf 0x01,r6 /* put #bytes to 8B bdry into cr7 */ + PPC_MTOCRF 0x01,r6 /* put #bytes to 8B bdry into cr7 */ subf r5,r6,r5 li r7,0 cmpldi r1,r5,16 @@ -150,7 +150,7 @@ _GLOBAL(__copy_tofrom_user) 2: bf cr7*4+1,3f 37: lwzx r0,r7,r4 83: stwx r0,r7,r3 -3: mtcrf 0x01,r5 +3: PPC_MTOCRF 0x01,r5 add r4,r6,r4 add r3,r6,r3 b .Ldst_aligned diff --git a/arch/powerpc/lib/locks.c b/arch/powerpc/lib/locks.c index 80b482c..79d0fa3 100644 --- a/arch/powerpc/lib/locks.c +++ b/arch/powerpc/lib/locks.c @@ -43,9 +43,11 @@ void __spin_yield(raw_spinlock_t *lock) if (firmware_has_feature(FW_FEATURE_ISERIES)) HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, ((u64)holder_cpu << 32) | yield_count); +#ifdef CONFIG_PPC_SPLPAR else plpar_hcall_norets(H_CONFER, get_hard_smp_processor_id(holder_cpu), yield_count); +#endif } /* @@ -72,9 +74,11 @@ void __rw_yield(raw_rwlock_t *rw) if (firmware_has_feature(FW_FEATURE_ISERIES)) HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, ((u64)holder_cpu << 32) | yield_count); +#ifdef CONFIG_PPC_SPLPAR else plpar_hcall_norets(H_CONFER, get_hard_smp_processor_id(holder_cpu), yield_count); +#endif } #endif diff --git a/arch/powerpc/lib/mem_64.S b/arch/powerpc/lib/mem_64.S index 68df202..11ce045 100644 --- a/arch/powerpc/lib/mem_64.S +++ b/arch/powerpc/lib/mem_64.S @@ -19,7 +19,7 @@ _GLOBAL(memset) rlwimi r4,r4,16,0,15 cmplw cr1,r5,r0 /* do we get that far? */ rldimi r4,r4,32,0 - mtcrf 1,r0 + PPC_MTOCRF 1,r0 mr r6,r3 blt cr1,8f beq+ 3f /* if already 8-byte aligned */ @@ -49,7 +49,7 @@ _GLOBAL(memset) bdnz 4b 5: srwi. r0,r5,3 clrlwi r5,r5,29 - mtcrf 1,r0 + PPC_MTOCRF 1,r0 beq 8f bf 29,6f std r4,0(r6) @@ -65,7 +65,7 @@ _GLOBAL(memset) std r4,0(r6) addi r6,r6,8 8: cmpwi r5,0 - mtcrf 1,r5 + PPC_MTOCRF 1,r5 beqlr+ bf 29,9f stw r4,0(r6) diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S index 7173ba9..3f13112 100644 --- a/arch/powerpc/lib/memcpy_64.S +++ b/arch/powerpc/lib/memcpy_64.S @@ -12,7 +12,7 @@ #include .align 7 _GLOBAL(memcpy) std r3,48(r1) /* save destination pointer for return value */ - mtcrf 0x01,r5 + PPC_MTOCRF 0x01,r5 cmpldi cr1,r5,16 neg r6,r3 # LS 3 bits = # bytes to 8-byte dest bdry andi. r6,r6,7 @@ -128,7 +128,7 @@ _GLOBAL(memcpy) b .Ldo_tail .Ldst_unaligned: - mtcrf 0x01,r6 # put #bytes to 8B bdry into cr7 + PPC_MTOCRF 0x01,r6 # put #bytes to 8B bdry into cr7 subf r5,r6,r5 li r7,0 cmpldi r1,r5,16 @@ -143,7 +143,7 @@ _GLOBAL(memcpy) 2: bf cr7*4+1,3f lwzx r0,r7,r4 stwx r0,r7,r3 -3: mtcrf 0x01,r5 +3: PPC_MTOCRF 0x01,r5 add r4,r6,r4 add r3,r6,r3 b .Ldst_aligned diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index 7e8ded0..4aae0c3 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -54,7 +54,7 @@ static int __kprobes branch_taken(unsign */ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) { - unsigned int opcode, rd; + unsigned int opcode, rs, rb, rd, spr; unsigned long int imm; opcode = instr >> 26; @@ -152,6 +152,49 @@ #ifdef CONFIG_PPC64 regs->nip &= 0xffffffffUL; return 1; #endif + case 0x26: /* mfcr */ + regs->gpr[rd] = regs->ccr; + regs->gpr[rd] &= 0xffffffffUL; + goto mtspr_out; + case 0x2a6: /* mfspr */ + spr = (instr >> 11) & 0x3ff; + switch (spr) { + case 0x20: /* mfxer */ + regs->gpr[rd] = regs->xer; + regs->gpr[rd] &= 0xffffffffUL; + goto mtspr_out; + case 0x100: /* mflr */ + regs->gpr[rd] = regs->link; + goto mtspr_out; + case 0x120: /* mfctr */ + regs->gpr[rd] = regs->ctr; + goto mtspr_out; + } + break; + case 0x378: /* orx */ + rs = (instr >> 21) & 0x1f; + rb = (instr >> 11) & 0x1f; + if (rs == rb) { /* mr */ + rd = (instr >> 16) & 0x1f; + regs->gpr[rd] = regs->gpr[rs]; + goto mtspr_out; + } + break; + case 0x3a6: /* mtspr */ + spr = (instr >> 11) & 0x3ff; + switch (spr) { + case 0x20: /* mtxer */ + regs->xer = (regs->gpr[rd] & 0xffffffffUL); + goto mtspr_out; + case 0x100: /* mtlr */ + regs->link = regs->gpr[rd]; + goto mtspr_out; + case 0x120: /* mtctr */ + regs->ctr = regs->gpr[rd]; +mtspr_out: + regs->nip += 4; + return 1; + } } } return 0; diff --git a/arch/powerpc/lib/strcase.c b/arch/powerpc/lib/strcase.c deleted file mode 100644 index f8ec1eb..0000000 --- a/arch/powerpc/lib/strcase.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include - -int strcasecmp(const char *s1, const char *s2) -{ - int c1, c2; - - do { - c1 = tolower(*s1++); - c2 = tolower(*s2++); - } while (c1 == c2 && c1 != 0); - return c1 - c2; -} - -int strncasecmp(const char *s1, const char *s2, size_t n) -{ - int c1, c2; - - do { - c1 = tolower(*s1++); - c2 = tolower(*s2++); - } while ((--n > 0) && c1 == c2 && c1 != 0); - return c1 - c2; -} diff --git a/arch/powerpc/mm/hash_low_32.S b/arch/powerpc/mm/hash_low_32.S index bd68df5..ddceefc 100644 --- a/arch/powerpc/mm/hash_low_32.S +++ b/arch/powerpc/mm/hash_low_32.S @@ -283,6 +283,7 @@ #define PTE_SIZE 8 #define PTEG_SIZE 64 #define LG_PTEG_SIZE 6 #define LDPTEu lwzu +#define LDPTE lwz #define STPTE stw #define CMPPTE cmpw #define PTE_H 0x40 @@ -389,13 +390,30 @@ _GLOBAL(hash_page_patch_C) * and we know there is a definite (although small) speed * advantage to putting the PTE in the primary PTEG, we always * put the PTE in the primary PTEG. + * + * In addition, we skip any slot that is mapping kernel text in + * order to avoid a deadlock when not using BAT mappings if + * trying to hash in the kernel hash code itself after it has + * already taken the hash table lock. This works in conjunction + * with pre-faulting of the kernel text. + * + * If the hash table bucket is full of kernel text entries, we'll + * lockup here but that shouldn't happen */ - addis r4,r7,next_slot@ha + +1: addis r4,r7,next_slot@ha /* get next evict slot */ lwz r6,next_slot@l(r4) - addi r6,r6,PTE_SIZE + addi r6,r6,PTE_SIZE /* search for candidate */ andi. r6,r6,7*PTE_SIZE stw r6,next_slot@l(r4) add r4,r3,r6 + LDPTE r0,PTE_SIZE/2(r4) /* get PTE second word */ + clrrwi r0,r0,12 + lis r6,etext@h + ori r6,r6,etext@l /* get etext */ + tophys(r6,r6) + cmpl cr0,r0,r6 /* compare and try again */ + blt 1b #ifndef CONFIG_SMP /* Store PTE in PTEG */ diff --git a/arch/powerpc/mm/hash_low_64.S b/arch/powerpc/mm/hash_low_64.S index 9bc0a9c..e64ce3e 100644 --- a/arch/powerpc/mm/hash_low_64.S +++ b/arch/powerpc/mm/hash_low_64.S @@ -445,9 +445,12 @@ END_FTR_SECTION(CPU_FTR_NOEXECUTE|CPU_FT htab_insert_pte: /* real page number in r5, PTE RPN value + index */ - rldicl r5,r31,64-PTE_RPN_SHIFT,PTE_RPN_SHIFT + andis. r0,r31,_PAGE_4K_PFN@h + srdi r5,r31,PTE_RPN_SHIFT + bne- htab_special_pfn sldi r5,r5,PAGE_SHIFT-HW_PAGE_SHIFT add r5,r5,r25 +htab_special_pfn: sldi r5,r5,HW_PAGE_SHIFT /* Calculate primary group hash */ diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index 6f1016a..79aedaf 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c @@ -505,7 +505,7 @@ static inline int tlb_batching_enabled(v int enabled = 1; if (root) { - const char *model = get_property(root, "model", NULL); + const char *model = of_get_property(root, "model", NULL); if (model && !strcmp(model, "IBM,9076-N81")) enabled = 0; of_node_put(root); diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 3c7fe2c..4961846 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -100,6 +100,11 @@ #endif #ifdef CONFIG_PPC_64K_PAGES int mmu_ci_restrictions; #endif +#ifdef CONFIG_DEBUG_PAGEALLOC +static u8 *linear_map_hash_slots; +static unsigned long linear_map_hash_count; +static spinlock_t linear_map_hash_lock; +#endif /* CONFIG_DEBUG_PAGEALLOC */ /* There are definitions of page sizes arrays to be used when none * is provided by the firmware. @@ -152,11 +157,10 @@ int htab_bolt_mapping(unsigned long vsta for (vaddr = vstart, paddr = pstart; vaddr < vend; vaddr += step, paddr += step) { - unsigned long vpn, hash, hpteg; + unsigned long hash, hpteg; unsigned long vsid = get_kernel_vsid(vaddr); unsigned long va = (vsid << 28) | (vaddr & 0x0fffffff); - vpn = va >> shift; tmp_mode = mode; /* Make non-kernel text non-executable */ @@ -174,6 +178,10 @@ int htab_bolt_mapping(unsigned long vsta if (ret < 0) break; +#ifdef CONFIG_DEBUG_PAGEALLOC + if ((paddr >> PAGE_SHIFT) < linear_map_hash_count) + linear_map_hash_slots[paddr >> PAGE_SHIFT] = ret | 0x80; +#endif /* CONFIG_DEBUG_PAGEALLOC */ } return ret < 0 ? ret : 0; } @@ -281,6 +289,7 @@ static void __init htab_init_page_sizes( memcpy(mmu_psize_defs, mmu_psize_defaults_gp, sizeof(mmu_psize_defaults_gp)); found: +#ifndef CONFIG_DEBUG_PAGEALLOC /* * Pick a size for the linear mapping. Currently, we only support * 16M, 1M and 4K which is the default @@ -289,6 +298,7 @@ static void __init htab_init_page_sizes( mmu_linear_psize = MMU_PAGE_16M; else if (mmu_psize_defs[MMU_PAGE_1M].shift) mmu_linear_psize = MMU_PAGE_1M; +#endif /* CONFIG_DEBUG_PAGEALLOC */ #ifdef CONFIG_PPC_64K_PAGES /* @@ -303,12 +313,14 @@ #ifdef CONFIG_PPC_64K_PAGES if (mmu_psize_defs[MMU_PAGE_64K].shift) { mmu_virtual_psize = MMU_PAGE_64K; mmu_vmalloc_psize = MMU_PAGE_64K; + if (mmu_linear_psize == MMU_PAGE_4K) + mmu_linear_psize = MMU_PAGE_64K; if (cpu_has_feature(CPU_FTR_CI_LARGE_PAGE)) mmu_io_psize = MMU_PAGE_64K; else mmu_ci_restrictions = 1; } -#endif +#endif /* CONFIG_PPC_64K_PAGES */ printk(KERN_DEBUG "Page orders: linear mapping = %d, " "virtual = %d, io = %d\n", @@ -476,6 +488,13 @@ void __init htab_initialize(void) mode_rw = _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_COHERENT | PP_RWXX; +#ifdef CONFIG_DEBUG_PAGEALLOC + linear_map_hash_count = lmb_end_of_DRAM() >> PAGE_SHIFT; + linear_map_hash_slots = __va(lmb_alloc_base(linear_map_hash_count, + 1, lmb.rmo_size)); + memset(linear_map_hash_slots, 0, linear_map_hash_count); +#endif /* CONFIG_DEBUG_PAGEALLOC */ + /* On U3 based machines, we need to reserve the DART area and * _NOT_ map it to avoid cache paradoxes as it's remapped non * cacheable later on @@ -573,6 +592,27 @@ unsigned int hash_page_do_lazy_icache(un return pp; } +/* + * Demote a segment to using 4k pages. + * For now this makes the whole process use 4k pages. + */ +void demote_segment_4k(struct mm_struct *mm, unsigned long addr) +{ +#ifdef CONFIG_PPC_64K_PAGES + if (mm->context.user_psize == MMU_PAGE_4K) + return; + mm->context.user_psize = MMU_PAGE_4K; + mm->context.sllp = SLB_VSID_USER | mmu_psize_defs[MMU_PAGE_4K].sllp; + get_paca()->context = mm->context; + slb_flush_and_rebolt(); +#ifdef CONFIG_SPE_BASE + spu_flush_all_slbs(mm); +#endif +#endif +} + +EXPORT_SYMBOL_GPL(demote_segment_4k); + /* Result code is: * 0 - handled * 1 - normal page fault @@ -665,15 +705,19 @@ #endif #ifndef CONFIG_PPC_64K_PAGES rc = __hash_page_4K(ea, access, vsid, ptep, trap, local); #else + /* If _PAGE_4K_PFN is set, make sure this is a 4k segment */ + if (pte_val(*ptep) & _PAGE_4K_PFN) { + demote_segment_4k(mm, ea); + psize = MMU_PAGE_4K; + } + if (mmu_ci_restrictions) { /* If this PTE is non-cacheable, switch to 4k */ if (psize == MMU_PAGE_64K && (pte_val(*ptep) & _PAGE_NO_CACHE)) { if (user_region) { + demote_segment_4k(mm, ea); psize = MMU_PAGE_4K; - mm->context.user_psize = MMU_PAGE_4K; - mm->context.sllp = SLB_VSID_USER | - mmu_psize_defs[MMU_PAGE_4K].sllp; } else if (ea < VMALLOC_END) { /* * some driver did a non-cacheable mapping @@ -756,16 +800,8 @@ #else if (mmu_ci_restrictions) { /* If this PTE is non-cacheable, switch to 4k */ if (mm->context.user_psize == MMU_PAGE_64K && - (pte_val(*ptep) & _PAGE_NO_CACHE)) { - mm->context.user_psize = MMU_PAGE_4K; - mm->context.sllp = SLB_VSID_USER | - mmu_psize_defs[MMU_PAGE_4K].sllp; - get_paca()->context = mm->context; - slb_flush_and_rebolt(); -#ifdef CONFIG_SPE_BASE - spu_flush_all_slbs(mm); -#endif - } + (pte_val(*ptep) & _PAGE_NO_CACHE)) + demote_segment_4k(mm, ea); } if (mm->context.user_psize == MMU_PAGE_64K) __hash_page_64K(ea, access, vsid, ptep, trap, local); @@ -825,3 +861,62 @@ void low_hash_fault(struct pt_regs *regs } bad_page_fault(regs, address, SIGBUS); } + +#ifdef CONFIG_DEBUG_PAGEALLOC +static void kernel_map_linear_page(unsigned long vaddr, unsigned long lmi) +{ + unsigned long hash, hpteg, vsid = get_kernel_vsid(vaddr); + unsigned long va = (vsid << 28) | (vaddr & 0x0fffffff); + unsigned long mode = _PAGE_ACCESSED | _PAGE_DIRTY | + _PAGE_COHERENT | PP_RWXX | HPTE_R_N; + int ret; + + hash = hpt_hash(va, PAGE_SHIFT); + hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP); + + ret = ppc_md.hpte_insert(hpteg, va, __pa(vaddr), + mode, HPTE_V_BOLTED, mmu_linear_psize); + BUG_ON (ret < 0); + spin_lock(&linear_map_hash_lock); + BUG_ON(linear_map_hash_slots[lmi] & 0x80); + linear_map_hash_slots[lmi] = ret | 0x80; + spin_unlock(&linear_map_hash_lock); +} + +static void kernel_unmap_linear_page(unsigned long vaddr, unsigned long lmi) +{ + unsigned long hash, hidx, slot, vsid = get_kernel_vsid(vaddr); + unsigned long va = (vsid << 28) | (vaddr & 0x0fffffff); + + hash = hpt_hash(va, PAGE_SHIFT); + spin_lock(&linear_map_hash_lock); + BUG_ON(!(linear_map_hash_slots[lmi] & 0x80)); + hidx = linear_map_hash_slots[lmi] & 0x7f; + linear_map_hash_slots[lmi] = 0; + spin_unlock(&linear_map_hash_lock); + if (hidx & _PTEIDX_SECONDARY) + hash = ~hash; + slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; + slot += hidx & _PTEIDX_GROUP_IX; + ppc_md.hpte_invalidate(slot, va, mmu_linear_psize, 0); +} + +void kernel_map_pages(struct page *page, int numpages, int enable) +{ + unsigned long flags, vaddr, lmi; + int i; + + local_irq_save(flags); + for (i = 0; i < numpages; i++, page++) { + vaddr = (unsigned long)page_address(page); + lmi = __pa(vaddr) >> PAGE_SHIFT; + if (lmi >= linear_map_hash_count) + continue; + if (enable) + kernel_map_linear_page(vaddr, lmi); + else + kernel_unmap_linear_page(vaddr, lmi); + } + local_irq_restore(flags); +} +#endif /* CONFIG_DEBUG_PAGEALLOC */ diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index f6ffaaa..8508f97 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -316,12 +316,11 @@ void set_huge_pte_at(struct mm_struct *m { if (pte_present(*ptep)) { /* We open-code pte_clear because we need to pass the right - * argument to hpte_update (huge / !huge) + * argument to hpte_need_flush (huge / !huge). Might not be + * necessary anymore if we make hpte_need_flush() get the + * page size from the slices */ - unsigned long old = pte_update(ptep, ~0UL); - if (old & _PAGE_HASHPTE) - hpte_update(mm, addr & HPAGE_MASK, ptep, old, 1); - flush_tlb_pending(); + pte_update(mm, addr & HPAGE_MASK, ptep, ~0UL, 1); } *ptep = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS); } @@ -329,12 +328,7 @@ void set_huge_pte_at(struct mm_struct *m pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - unsigned long old = pte_update(ptep, ~0UL); - - if (old & _PAGE_HASHPTE) - hpte_update(mm, addr & HPAGE_MASK, ptep, old, 1); - *ptep = __pte(0); - + unsigned long old = pte_update(mm, addr, ptep, ~0UL, 1); return __pte(old); } diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index 0e53ca8..5fce6cc 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -115,6 +115,10 @@ void MMU_setup(void) if (strstr(cmd_line, "noltlbs")) { __map_without_ltlbs = 1; } +#ifdef CONFIG_DEBUG_PAGEALLOC + __map_without_bats = 1; + __map_without_ltlbs = 1; +#endif } /* diff --git a/arch/powerpc/mm/lmb.c b/arch/powerpc/mm/lmb.c index 716a290..e3a1e8d 100644 --- a/arch/powerpc/mm/lmb.c +++ b/arch/powerpc/mm/lmb.c @@ -146,6 +146,10 @@ static long __init lmb_add_region(struct unsigned long rgnbase = rgn->region[i].base; unsigned long rgnsize = rgn->region[i].size; + if ((rgnbase == base) && (rgnsize == size)) + /* Already have this region, so we're done */ + return 0; + adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); if ( adjacent > 0 ) { rgn->region[i].base -= size; diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 52f397c..c4bcd75 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -58,9 +58,6 @@ int init_bootmem_done; int mem_init_done; unsigned long memory_limit; -extern void hash_preload(struct mm_struct *mm, unsigned long ea, - unsigned long access, unsigned long trap); - int page_is_ram(unsigned long pfn) { unsigned long paddr = (pfn << PAGE_SHIFT); diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h index bea2d21..9c4538b 100644 --- a/arch/powerpc/mm/mmu_decl.h +++ b/arch/powerpc/mm/mmu_decl.h @@ -19,9 +19,14 @@ * 2 of the License, or (at your option) any later version. * */ +#include #include #include +extern void hash_preload(struct mm_struct *mm, unsigned long ea, + unsigned long access, unsigned long trap); + + #ifdef CONFIG_PPC32 extern void mapin_ram(void); extern int map_page(unsigned long va, phys_addr_t pa, int flags); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index e86c37c..b3a592b 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -74,7 +74,7 @@ static struct device_node * __cpuinit fi while ((cpu_node = of_find_node_by_type(cpu_node, "cpu")) != NULL) { /* Try interrupt server first */ - interrupt_server = get_property(cpu_node, + interrupt_server = of_get_property(cpu_node, "ibm,ppc-interrupt-server#s", &len); len = len / sizeof(u32); @@ -85,7 +85,7 @@ static struct device_node * __cpuinit fi return cpu_node; } } else { - reg = get_property(cpu_node, "reg", &len); + reg = of_get_property(cpu_node, "reg", &len); if (reg && (len > 0) && (reg[0] == hw_cpuid)) return cpu_node; } @@ -97,7 +97,7 @@ static struct device_node * __cpuinit fi /* must hold reference to node during call */ static const int *of_get_associativity(struct device_node *dev) { - return get_property(dev, "ibm,associativity", NULL); + return of_get_property(dev, "ibm,associativity", NULL); } /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa @@ -179,7 +179,7 @@ static int __init find_min_common_depth( * configuration (should be all 0's) and the second is for a normal * NUMA configuration. */ - ref_points = get_property(rtas_root, + ref_points = of_get_property(rtas_root, "ibm,associativity-reference-points", &len); if ((len >= 1) && ref_points) { @@ -201,8 +201,8 @@ static void __init get_n_mem_cells(int * if (!memory) panic("numa.c: No memory nodes found!"); - *n_addr_cells = prom_n_addr_cells(memory); - *n_size_cells = prom_n_size_cells(memory); + *n_addr_cells = of_n_addr_cells(memory); + *n_size_cells = of_n_size_cells(memory); of_node_put(memory); } @@ -308,9 +308,9 @@ static void __init parse_drconf_memory(s int nid, default_nid = 0; unsigned int start, ai, flags; - lm = get_property(memory, "ibm,lmb-size", &ls); - dm = get_property(memory, "ibm,dynamic-memory", &ld); - aa = get_property(memory, "ibm,associativity-lookup-arrays", &la); + lm = of_get_property(memory, "ibm,lmb-size", &ls); + dm = of_get_property(memory, "ibm,dynamic-memory", &ld); + aa = of_get_property(memory, "ibm,associativity-lookup-arrays", &la); if (!lm || !dm || !aa || ls < sizeof(unsigned int) || ld < sizeof(unsigned int) || la < 2 * sizeof(unsigned int)) @@ -404,10 +404,10 @@ static int __init parse_numa_properties( const unsigned int *memcell_buf; unsigned int len; - memcell_buf = get_property(memory, + memcell_buf = of_get_property(memory, "linux,usable-memory", &len); if (!memcell_buf || len <= 0) - memcell_buf = get_property(memory, "reg", &len); + memcell_buf = of_get_property(memory, "reg", &len); if (!memcell_buf || len <= 0) continue; @@ -725,7 +725,7 @@ int hot_add_scn_to_nid(unsigned long scn const unsigned int *memcell_buf; unsigned int len; - memcell_buf = get_property(memory, "reg", &len); + memcell_buf = of_get_property(memory, "reg", &len); if (!memcell_buf || len <= 0) continue; diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index c284bda..bca5603 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -183,8 +183,8 @@ __ioremap(phys_addr_t addr, unsigned lon * mem_init() sets high_memory so only do the check after that. */ if (mem_init_done && (p < virt_to_phys(high_memory))) { - printk("__ioremap(): phys addr "PHYS_FMT" is RAM lr %p\n", p, - __builtin_return_address(0)); + printk("__ioremap(): phys addr 0x%llx is RAM lr %p\n", + (unsigned long long)p, __builtin_return_address(0)); return NULL; } @@ -266,9 +266,12 @@ int map_page(unsigned long va, phys_addr pg = pte_alloc_kernel(pd, va); if (pg != 0) { err = 0; - set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags))); - if (mem_init_done) - flush_HPTE(0, va, pmd_val(*pd)); + /* The PTE should never be already set nor present in the + * hash table + */ + BUG_ON(pte_val(*pg) & (_PAGE_PRESENT | _PAGE_HASHPTE)); + set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, + __pgprot(flags))); } return err; } @@ -279,16 +282,19 @@ int map_page(unsigned long va, phys_addr void __init mapin_ram(void) { unsigned long v, p, s, f; + int ktext; s = mmu_mapin_ram(); v = KERNELBASE + s; p = PPC_MEMSTART + s; for (; s < total_lowmem; s += PAGE_SIZE) { - if ((char *) v >= _stext && (char *) v < etext) - f = _PAGE_RAM_TEXT; - else - f = _PAGE_RAM; + ktext = ((char *) v >= _stext && (char *) v < etext); + f = ktext ?_PAGE_RAM_TEXT : _PAGE_RAM; map_page(v, p, f); +#ifdef CONFIG_PPC_STD_MMU_32 + if (ktext) + hash_preload(&init_mm, v, 0, 0x300); +#endif v += PAGE_SIZE; p += PAGE_SIZE; } @@ -445,3 +451,55 @@ #endif return ret; } +#ifdef CONFIG_DEBUG_PAGEALLOC + +static int __change_page_attr(struct page *page, pgprot_t prot) +{ + pte_t *kpte; + pmd_t *kpmd; + unsigned long address; + + BUG_ON(PageHighMem(page)); + address = (unsigned long)page_address(page); + + if (v_mapped_by_bats(address) || v_mapped_by_tlbcam(address)) + return 0; + if (!get_pteptr(&init_mm, address, &kpte, &kpmd)) + return -EINVAL; + set_pte_at(&init_mm, address, kpte, mk_pte(page, prot)); + wmb(); + flush_HPTE(0, address, pmd_val(*kpmd)); + pte_unmap(kpte); + + return 0; +} + +/* + * Change the page attributes of an page in the linear mapping. + * + * THIS CONFLICTS WITH BAT MAPPINGS, DEBUG USE ONLY + */ +static int change_page_attr(struct page *page, int numpages, pgprot_t prot) +{ + int i, err = 0; + unsigned long flags; + + local_irq_save(flags); + for (i = 0; i < numpages; i++, page++) { + err = __change_page_attr(page, prot); + if (err) + break; + } + local_irq_restore(flags); + return err; +} + + +void kernel_map_pages(struct page *page, int numpages, int enable) +{ + if (PageHighMem(page)) + return; + + change_page_attr(page, numpages, enable ? PAGE_KERNEL : __pgprot(0)); +} +#endif /* CONFIG_DEBUG_PAGEALLOC */ diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c index 7cceb2c..0506667 100644 --- a/arch/powerpc/mm/ppc_mmu_32.c +++ b/arch/powerpc/mm/ppc_mmu_32.c @@ -85,8 +85,10 @@ #else unsigned long max_size = (256<<20); unsigned long align; - if (__map_without_bats) + if (__map_without_bats) { + printk(KERN_DEBUG "RAM mapped without BATs\n"); return 0; + } /* Set up BAT2 and if necessary BAT3 to cover RAM. */ diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c index b58baa6..fd8d08c 100644 --- a/arch/powerpc/mm/tlb_64.c +++ b/arch/powerpc/mm/tlb_64.c @@ -120,17 +120,20 @@ void pgtable_free_tlb(struct mmu_gather } /* - * Update the MMU hash table to correspond with a change to - * a Linux PTE. If wrprot is true, it is permissible to - * change the existing HPTE to read-only rather than removing it - * (if we remove it we should clear the _PTE_HPTEFLAGS bits). + * A linux PTE was changed and the corresponding hash table entry + * neesd to be flushed. This function will either perform the flush + * immediately or will batch it up if the current CPU has an active + * batch on it. + * + * Must be called from within some kind of spinlock/non-preempt region... */ -void hpte_update(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, unsigned long pte, int huge) +void hpte_need_flush(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long pte, int huge) { struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); - unsigned long vsid; + unsigned long vsid, vaddr; unsigned int psize; + real_pte_t rpte; int i; i = batch->index; @@ -151,6 +154,26 @@ #endif } else psize = pte_pagesize_index(pte); + /* Build full vaddr */ + if (!is_kernel_addr(addr)) { + vsid = get_vsid(mm->context.id, addr); + WARN_ON(vsid == 0); + } else + vsid = get_kernel_vsid(addr); + vaddr = (vsid << 28 ) | (addr & 0x0fffffff); + rpte = __real_pte(__pte(pte), ptep); + + /* + * Check if we have an active batch on this CPU. If not, just + * flush now and return. For now, we don global invalidates + * in that case, might be worth testing the mm cpu mask though + * and decide to use local invalidates instead... + */ + if (!batch->active) { + flush_hash_page(vaddr, rpte, psize, 0); + return; + } + /* * This can happen when we are in the middle of a TLB batch and * we encounter memory pressure (eg copy_page_range when it tries @@ -162,47 +185,42 @@ #endif * batch */ if (i != 0 && (mm != batch->mm || batch->psize != psize)) { - flush_tlb_pending(); + __flush_tlb_pending(batch); i = 0; } if (i == 0) { batch->mm = mm; batch->psize = psize; } - if (!is_kernel_addr(addr)) { - vsid = get_vsid(mm->context.id, addr); - WARN_ON(vsid == 0); - } else - vsid = get_kernel_vsid(addr); - batch->vaddr[i] = (vsid << 28 ) | (addr & 0x0fffffff); - batch->pte[i] = __real_pte(__pte(pte), ptep); + batch->pte[i] = rpte; + batch->vaddr[i] = vaddr; batch->index = ++i; if (i >= PPC64_TLB_BATCH_NR) - flush_tlb_pending(); + __flush_tlb_pending(batch); } +/* + * This function is called when terminating an mmu batch or when a batch + * is full. It will perform the flush of all the entries currently stored + * in a batch. + * + * Must be called from within some kind of spinlock/non-preempt region... + */ void __flush_tlb_pending(struct ppc64_tlb_batch *batch) { - int i; - int cpu; cpumask_t tmp; - int local = 0; + int i, local = 0; - BUG_ON(in_interrupt()); - - cpu = get_cpu(); i = batch->index; - tmp = cpumask_of_cpu(cpu); + tmp = cpumask_of_cpu(smp_processor_id()); if (cpus_equal(batch->mm->cpu_vm_mask, tmp)) local = 1; - if (i == 1) flush_hash_page(batch->vaddr[0], batch->pte[0], batch->psize, local); else flush_hash_range(i, local); batch->index = 0; - put_cpu(); } void pte_free_finish(void) diff --git a/arch/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile index 4ccef2d..4b5f952 100644 --- a/arch/powerpc/oprofile/Makefile +++ b/arch/powerpc/oprofile/Makefile @@ -12,6 +12,6 @@ DRIVER_OBJS := $(addprefix ../../../driv oprofile-y := $(DRIVER_OBJS) common.o backtrace.o oprofile-$(CONFIG_PPC_CELL_NATIVE) += op_model_cell.o -oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o +oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o oprofile-$(CONFIG_6xx) += op_model_7450.o diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c index fbd62ea..1a7ef7e 100644 --- a/arch/powerpc/oprofile/common.c +++ b/arch/powerpc/oprofile/common.c @@ -160,6 +160,9 @@ #endif case PPC_OPROFILE_POWER4: model = &op_model_power4; break; + case PPC_OPROFILE_PA6T: + model = &op_model_pa6t; + break; #endif #ifdef CONFIG_6xx case PPC_OPROFILE_G4: diff --git a/arch/powerpc/oprofile/op_model_cell.c b/arch/powerpc/oprofile/op_model_cell.c index e08e1d7..626b29f 100644 --- a/arch/powerpc/oprofile/op_model_cell.c +++ b/arch/powerpc/oprofile/op_model_cell.c @@ -37,6 +37,7 @@ #include #include #include "../platforms/cell/interrupt.h" +#include "../platforms/cell/cbe_regs.h" #define PPU_CYCLES_EVENT_NUM 1 /* event number for CYCLES */ #define PPU_CYCLES_GRP_NUM 1 /* special group number for identifying @@ -130,7 +131,7 @@ static int pm_rtas_token; static u32 reset_value[NR_PHYS_CTRS]; static int num_counters; static int oprofile_running; -static spinlock_t virt_cntr_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(virt_cntr_lock); static u32 ctr_enabled; diff --git a/arch/powerpc/oprofile/op_model_pa6t.c b/arch/powerpc/oprofile/op_model_pa6t.c new file mode 100644 index 0000000..e8a56b0 --- /dev/null +++ b/arch/powerpc/oprofile/op_model_pa6t.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Shashi Rao, PA Semi + * + * Maintained by: Olof Johansson + * + * Based on arch/powerpc/oprofile/op_model_power4.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned char oprofile_running; + +/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */ +static u64 mmcr0_val; +static u64 mmcr1_val; + +/* inited in pa6t_reg_setup */ +static u64 reset_value[OP_MAX_COUNTER]; + +static inline u64 ctr_read(unsigned int i) +{ + switch (i) { + case 0: + return mfspr(SPRN_PA6T_PMC0); + case 1: + return mfspr(SPRN_PA6T_PMC1); + case 2: + return mfspr(SPRN_PA6T_PMC2); + case 3: + return mfspr(SPRN_PA6T_PMC3); + case 4: + return mfspr(SPRN_PA6T_PMC4); + case 5: + return mfspr(SPRN_PA6T_PMC5); + default: + printk(KERN_ERR "ctr_read called with bad arg %u\n", i); + return 0; + } +} + +static inline void ctr_write(unsigned int i, u64 val) +{ + switch (i) { + case 0: + mtspr(SPRN_PA6T_PMC0, val); + break; + case 1: + mtspr(SPRN_PA6T_PMC1, val); + break; + case 2: + mtspr(SPRN_PA6T_PMC2, val); + break; + case 3: + mtspr(SPRN_PA6T_PMC3, val); + break; + case 4: + mtspr(SPRN_PA6T_PMC4, val); + break; + case 5: + mtspr(SPRN_PA6T_PMC5, val); + break; + default: + printk(KERN_ERR "ctr_write called with bad arg %u\n", i); + break; + } +} + + +/* precompute the values to stuff in the hardware registers */ +static void pa6t_reg_setup(struct op_counter_config *ctr, + struct op_system_config *sys, + int num_ctrs) +{ + int pmc; + + /* + * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the + * event_mappings file by turning off the counters that the user doesn't + * care about + * + * setup user and kernel profiling + */ + for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) + if (!ctr[pmc].enabled) { + sys->mmcr0 &= ~(0x1UL << pmc); + sys->mmcr0 &= ~(0x1UL << (pmc+12)); + pr_debug("turned off counter %u\n", pmc); + } + + if (sys->enable_kernel) + sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN; + else + sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN); + + if (sys->enable_user) + sys->mmcr0 |= PA6T_MMCR0_PREN; + else + sys->mmcr0 &= ~PA6T_MMCR0_PREN; + + /* + * The performance counter event settings are given in the mmcr0 and + * mmcr1 values passed from the user in the op_system_config + * structure (sys variable). + */ + mmcr0_val = sys->mmcr0; + mmcr1_val = sys->mmcr1; + pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0); + pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1); + + for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) { + /* counters are 40 bit. Move to cputable at some point? */ + reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count; + pr_debug("reset_value for pmc%u inited to 0x%lx\n", + pmc, reset_value[pmc]); + } +} + +/* configure registers on this cpu */ +static void pa6t_cpu_setup(struct op_counter_config *ctr) +{ + u64 mmcr0 = mmcr0_val; + u64 mmcr1 = mmcr1_val; + + /* Default is all PMCs off */ + mmcr0 &= ~(0x3FUL); + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + /* program selected programmable events in */ + mtspr(SPRN_PA6T_MMCR1, mmcr1); + + pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(), + mfspr(SPRN_PA6T_MMCR0)); + pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(), + mfspr(SPRN_PA6T_MMCR1)); +} + +static void pa6t_start(struct op_counter_config *ctr) +{ + int i; + + /* Hold off event counting until rfid */ + u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; + + for (i = 0; i < cur_cpu_spec->num_pmcs; i++) + if (ctr[i].enabled) + ctr_write(i, reset_value[i]); + else + ctr_write(i, 0UL); + + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + oprofile_running = 1; + + pr_debug("start on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0); +} + +static void pa6t_stop(void) +{ + u64 mmcr0; + + /* freeze counters */ + mmcr0 = mfspr(SPRN_PA6T_MMCR0); + mmcr0 |= PA6T_MMCR0_FCM0; + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + oprofile_running = 0; + + pr_debug("stop on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0); +} + +/* handle the perfmon overflow vector */ +static void pa6t_handle_interrupt(struct pt_regs *regs, + struct op_counter_config *ctr) +{ + unsigned long pc = mfspr(SPRN_PA6T_SIAR); + int is_kernel = is_kernel_addr(pc); + u64 val; + int i; + u64 mmcr0; + + /* disable perfmon counting until rfid */ + mmcr0 = mfspr(SPRN_PA6T_MMCR0); + mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS); + + /* Record samples. We've got one global bit for whether a sample + * was taken, so add it for any counter that triggered overflow. + */ + for (i = 0; i < cur_cpu_spec->num_pmcs; i++) { + val = ctr_read(i); + if (val & (0x1UL << 39)) { /* Overflow bit set */ + if (oprofile_running && ctr[i].enabled) { + if (mmcr0 & PA6T_MMCR0_SIARLOG) + oprofile_add_ext_sample(pc, regs, i, is_kernel); + ctr_write(i, reset_value[i]); + } else { + ctr_write(i, 0UL); + } + } + } + + /* Restore mmcr0 to a good known value since the PMI changes it */ + mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; + mtspr(SPRN_PA6T_MMCR0, mmcr0); +} + +struct op_powerpc_model op_model_pa6t = { + .reg_setup = pa6t_reg_setup, + .cpu_setup = pa6t_cpu_setup, + .start = pa6t_start, + .stop = pa6t_stop, + .handle_interrupt = pa6t_handle_interrupt, +}; diff --git a/arch/powerpc/platforms/4xx/Kconfig b/arch/powerpc/platforms/4xx/Kconfig index 2f2a13e..ded357c 100644 --- a/arch/powerpc/platforms/4xx/Kconfig +++ b/arch/powerpc/platforms/4xx/Kconfig @@ -3,278 +3,206 @@ config 4xx depends on 40x || 44x default y -config WANT_EARLY_SERIAL +config BOOKE bool - select SERIAL_8250 - default n - -menu "AMCC 4xx options" - depends on 4xx - -choice - prompt "Machine Type" - depends on 40x - default WALNUT - -config BUBINGA - bool "Bubinga" - select WANT_EARLY_SERIAL - help - This option enables support for the IBM 405EP evaluation board. - -config CPCI405 - bool "CPCI405" - help - This option enables support for the CPCI405 board. - -config EP405 - bool "EP405/EP405PC" - help - This option enables support for the EP405/EP405PC boards. - -config REDWOOD_5 - bool "Redwood-5" - help - This option enables support for the IBM STB04 evaluation board. - -config REDWOOD_6 - bool "Redwood-6" - help - This option enables support for the IBM STBx25xx evaluation board. - -config SYCAMORE - bool "Sycamore" - help - This option enables support for the IBM PPC405GPr evaluation board. - -config WALNUT - bool "Walnut" - help - This option enables support for the IBM PPC405GP evaluation board. - -config XILINX_ML300 - bool "Xilinx-ML300" - help - This option enables support for the Xilinx ML300 evaluation board. - -endchoice - -choice - prompt "Machine Type" depends on 44x - default EBONY - -config BAMBOO - bool "Bamboo" - select WANT_EARLY_SERIAL - help - This option enables support for the IBM PPC440EP evaluation board. - -config EBONY - bool "Ebony" - select WANT_EARLY_SERIAL - help - This option enables support for the IBM PPC440GP evaluation board. - -config LUAN - bool "Luan" - select WANT_EARLY_SERIAL - help - This option enables support for the IBM PPC440SP evaluation board. - -config OCOTEA - bool "Ocotea" - select WANT_EARLY_SERIAL - help - This option enables support for the IBM PPC440GX evaluation board. + default y -endchoice +menu "AMCC 40x options" + depends on 40x -config EP405PC - bool "EP405PC Support" - depends on EP405 +#config BUBINGA +# bool "Bubinga" +# depends on 40x +# default n +# select 405EP +# help +# This option enables support for the IBM 405EP evaluation board. + +#config CPCI405 +# bool "CPCI405" +# depends on 40x +# default n +# select 405GP +# help +# This option enables support for the CPCI405 board. + +#config EP405 +# bool "EP405/EP405PC" +# depends on 40x +# default n +# select 405GP +# help +# This option enables support for the EP405/EP405PC boards. + +#config EP405PC +# bool "EP405PC Support" +# depends on EP405 +# default y +# help +# This option enables support for the extra features of the EP405PC board. + +#config REDWOOD_5 +# bool "Redwood-5" +# depends on 40x +# default n +# select STB03xxx +# help +# This option enables support for the IBM STB04 evaluation board. + +#config REDWOOD_6 +# bool "Redwood-6" +# depends on 40x +# default n +# select STB03xxx +# help +# This option enables support for the IBM STBx25xx evaluation board. + +#config SYCAMORE +# bool "Sycamore" +# depends on 40x +# default n +# select 405GPR +# help +# This option enables support for the IBM PPC405GPr evaluation board. + +#config WALNUT +# bool "Walnut" +# depends on 40x +# default y +# select 405GP +# help +# This option enables support for the IBM PPC405GP evaluation board. + +#config XILINX_ML300 +# bool "Xilinx-ML300" +# depends on 40x +# default y +# select VIRTEX_II_PRO +# help +# This option enables support for the Xilinx ML300 evaluation board. +endmenu -# It's often necessary to know the specific 4xx processor type. -# Fortunately, it is impled (so far) from the board type, so we -# don't need to ask more redundant questions. +# 40x specific CPU modules, selected based on the board above. config NP405H bool - depends on ASH - default y + #depends on ASH -config 440EP +# OAK doesn't exist but wanted to keep this around for any future 403GCX boards +config 403GCX bool - depends on BAMBOO - select PPC_FPU - default y + #depends on OAK + select IBM405_ERR51 -config 440GP +config 405GP bool - depends on EBONY - default y + select IBM405_ERR77 + select IBM405_ERR51 -config 440GX +config 405EP bool - depends on OCOTEA - default y -config 440SP +config 405GPR bool - depends on LUAN - default y -config 440 +config VIRTEX_II_PRO bool - depends on 440GP || 440SP || 440EP - default y + select IBM405_ERR77 + select IBM405_ERR51 -config 440A +config STB03xxx bool - depends on 440GX - default y + select IBM405_ERR77 + select IBM405_ERR51 -config IBM440EP_ERR42 - bool - depends on 440EP - default y +# 40x errata/workaround config symbols, selected by the CPU models above # All 405-based cores up until the 405GPR and 405EP have this errata. config IBM405_ERR77 bool - depends on 40x && !403GCX && !405GPR && !405EP - default y # All 40x-based cores, up until the 405GPR and 405EP have this errata. config IBM405_ERR51 bool - depends on 40x && !405GPR && !405EP - default y -config BOOKE - bool +menu "AMCC 44x options" depends on 44x - default y - -config IBM_OCP - bool - depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || OCOTEA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT - default y -config XILINX_OCP - bool - depends on XILINX_ML300 - default y +#config BAMBOO +# bool "Bamboo" +# depends on 44x +# default n +# select 440EP +# help +# This option enables support for the IBM PPC440EP evaluation board. -config IBM_EMAC4 - bool - depends on 440GX || 440SP - default y - -config BIOS_FIXUP - bool - depends on BUBINGA || EP405 || SYCAMORE || WALNUT - default y - -# OAK doesn't exist but wanted to keep this around for any future 403GCX boards -config 403GCX - bool - depends on OAK - default y - -config 405EP - bool - depends on BUBINGA - default y - -config 405GP - bool - depends on CPCI405 || EP405 || WALNUT - default y - -config 405GPR - bool - depends on SYCAMORE +config EBONY + bool "Ebony" + depends on 44x default y + select 440GP + help + This option enables support for the IBM PPC440GP evaluation board. -config VIRTEX_II_PRO - bool - depends on XILINX_ML300 - default y +#config LUAN +# bool "Luan" +# depends on 44x +# default n +# select 440SP +# help +# This option enables support for the IBM PPC440SP evaluation board. + +#config OCOTEA +# bool "Ocotea" +# depends on 44x +# default n +# select 440GX +# help +# This option enables support for the IBM PPC440GX evaluation board. -config STB03xxx - bool - depends on REDWOOD_5 || REDWOOD_6 - default y +endmenu -config EMBEDDEDBOOT +# 44x specific CPU modules, selected based on the board above. +config 440EP bool - depends on EP405 || XILINX_ML300 - default y + select PPC_FPU + select IBM440EP_ERR42 -config IBM_OPENBIOS +config 440GP bool - depends on ASH || BUBINGA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT - default y + select IBM_NEW_EMAC_ZMII -config PPC4xx_DMA - bool "PPC4xx DMA controller support" - depends on 4xx - -config PPC4xx_EDMA +config 440GX bool - depends on !STB03xxx && PPC4xx_DMA - default y -config PPC_GEN550 +config 440SP bool - depends on 4xx - default y - -choice - prompt "TTYS0 device and default console" - depends on 40x - default UART0_TTYS0 - -config UART0_TTYS0 - bool "UART0" -config UART0_TTYS1 - bool "UART1" - -endchoice - -config SERIAL_SICC - bool "SICC Serial port support" - depends on STB03xxx - -config UART1_DFLT_CONSOLE +config 440A bool - depends on SERIAL_SICC && UART0_TTYS1 + depends on 440GX default y -config SERIAL_SICC_CONSOLE +# 44x errata/workaround config symbols, selected by the CPU models above +config IBM440EP_ERR42 bool - depends on SERIAL_SICC && UART0_TTYS1 - default y -endmenu +#config XILINX_OCP +# bool +# depends on XILINX_ML300 +# default y -menu "IBM 40x options" - depends on 40x - -config SERIAL_SICC - bool "SICC Serial port" - depends on STB03xxx - -config UART1_DFLT_CONSOLE - bool - depends on SERIAL_SICC && UART0_TTYS1 - default y +#config BIOS_FIXUP +# bool +# depends on BUBINGA || EP405 || SYCAMORE || WALNUT +# default y -config SERIAL_SICC_CONSOLE - bool - depends on SERIAL_SICC && UART0_TTYS1 - default y +#config PPC4xx_DMA +# bool "PPC4xx DMA controller support" +# depends on 4xx -endmenu +#config PPC4xx_EDMA +# bool +# depends on !STB03xxx && PPC4xx_DMA +# default y diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig new file mode 100644 index 0000000..bc4aa4a --- /dev/null +++ b/arch/powerpc/platforms/52xx/Kconfig @@ -0,0 +1,35 @@ +config PPC_MPC52xx + bool + default n + +config PPC_MPC5200 + bool + select PPC_MPC52xx + default n + +config PPC_MPC5200_BUGFIX + bool "MPC5200 (L25R) bugfix support" + depends on PPC_MPC5200 + default n + help + Enable workarounds for original MPC5200 errata. This is not required + for MPC5200B based boards. + + It is safe to say 'Y' here + +config PPC_EFIKA + bool "bPlan Efika 5k2. MPC5200B based computer" + depends on PPC_MULTIPLATFORM && PPC32 + select PPC_RTAS + select RTAS_PROC + select PPC_MPC52xx + select PPC_NATIVE + default n + +config PPC_LITE5200 + bool "Freescale Lite5200 Eval Board" + depends on PPC_MULTIPLATFORM && PPC32 + select PPC_MPC5200 + default n + + diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index 8de0341..a6bba97 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -112,7 +112,7 @@ void __init efika_pcisetup(void) return; } - bus_range = get_property(pcictrl, "bus-range", &len); + bus_range = of_get_property(pcictrl, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING EFIKA_PLATFORM_NAME ": Can't get bus-range for %s\n", pcictrl->full_name); @@ -158,18 +158,17 @@ #endif static void efika_show_cpuinfo(struct seq_file *m) { struct device_node *root; - const char *revision = NULL; - const char *codegendescription = NULL; - const char *codegenvendor = NULL; + const char *revision; + const char *codegendescription; + const char *codegenvendor; root = of_find_node_by_path("/"); if (!root) return; - revision = get_property(root, "revision", NULL); - codegendescription = - get_property(root, "CODEGEN,description", NULL); - codegenvendor = get_property(root, "CODEGEN,vendor", NULL); + revision = of_get_property(root, "revision", NULL); + codegendescription = of_get_property(root, "CODEGEN,description", NULL); + codegenvendor = of_get_property(root, "CODEGEN,vendor", NULL); if (codegendescription) seq_printf(m, "machine\t\t: %s\n", codegendescription); diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index cc3b40d..8e2646a 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -94,8 +94,8 @@ static void __init lite5200_setup_arch(v np = of_find_node_by_type(NULL, "cpu"); if (np) { - unsigned int *fp = - (int *)get_property(np, "clock-frequency", NULL); + const unsigned int *fp = + of_get_property(np, "clock-frequency", NULL); if (fp != 0) loops_per_jiffy = *fp / HZ; else @@ -108,9 +108,11 @@ static void __init lite5200_setup_arch(v lite5200_setup_cpu(); /* Platorm specific */ #ifdef CONFIG_PCI - np = of_find_node_by_type(np, "pci"); - if (np) + np = of_find_node_by_type(NULL, "pci"); + if (np) { mpc52xx_add_bridge(np); + of_node_put(np); + } #endif #ifdef CONFIG_BLK_DEV_INITRD @@ -132,7 +134,7 @@ void lite5200_show_cpuinfo(struct seq_fi const char *model = NULL; if (np) - model = get_property(np, "model", NULL); + model = of_get_property(np, "model", NULL); seq_printf(m, "vendor\t\t: Freescale Semiconductor\n"); seq_printf(m, "machine\t\t: %s\n", model ? model : "unknown"); diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c index ed0cb69..2dd415f 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_common.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c @@ -60,7 +60,7 @@ mpc52xx_find_ipb_freq(struct device_node of_node_get(node); while (node) { - p_ipb_freq = get_property(node, "bus-frequency", NULL); + p_ipb_freq = of_get_property(node, "bus-frequency", NULL); if (p_ipb_freq) break; diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c index faf161b..34d34a2 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c @@ -370,7 +370,7 @@ mpc52xx_add_bridge(struct device_node *n return -EINVAL; } - bus_range = get_property(node, "bus-range", &len); + bus_range = of_get_property(node, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get %s bus-range, assume bus 0\n", node->full_name); diff --git a/arch/powerpc/platforms/82xx/Kconfig b/arch/powerpc/platforms/82xx/Kconfig index 47d841e..de7fce9 100644 --- a/arch/powerpc/platforms/82xx/Kconfig +++ b/arch/powerpc/platforms/82xx/Kconfig @@ -1,21 +1,36 @@ -menu "Platform support" - depends on PPC_82xx - choice - prompt "Machine Type" - default MPC82xx_ADS + prompt "Machine Type" + depends on PPC_82xx + default MPC82xx_ADS config MPC82xx_ADS - bool "Freescale MPC82xx ADS" - select DEFAULT_UIMAGE - select PQ2ADS - select 8272 - select 8260 - select CPM2 - select FSL_SOC - help - This option enables support for the MPC8272 ADS board + bool "Freescale MPC82xx ADS" + select DEFAULT_UIMAGE + select PQ2ADS + select 8272 + select 8260 + select FSL_SOC + help + This option enables support for the MPC8272 ADS board endchoice -endmenu +config PQ2ADS + bool + default n + +config 8260 + bool + depends on 6xx + select CPM2 + help + The MPC8260 is a typical embedded CPU made by Freescale. Selecting + this option means that you wish to build a kernel for a machine with + an 8260 class CPU. + +config 8272 + bool + select 8260 + help + The MPC8272 CPM has a different internal dpram setup than other CPM2 + devices diff --git a/arch/powerpc/platforms/82xx/mpc82xx.c b/arch/powerpc/platforms/82xx/mpc82xx.c index 74e7892..cc9900d 100644 --- a/arch/powerpc/platforms/82xx/mpc82xx.c +++ b/arch/powerpc/platforms/82xx/mpc82xx.c @@ -55,17 +55,17 @@ #include "pq2ads.h" static int __init get_freq(char *name, unsigned long *val) { struct device_node *cpu; - unsigned int *fp; + const unsigned int *fp; int found = 0; /* The cpu node should have timebase and clock frequency properties */ cpu = of_find_node_by_type(NULL, "cpu"); if (cpu) { - fp = (unsigned int *)get_property(cpu, name, NULL); + fp = of_get_property(cpu, name, NULL); if (fp) { found = 1; - *val = *fp++; + *val = *fp; } of_node_put(cpu); diff --git a/arch/powerpc/platforms/82xx/mpc82xx_ads.c b/arch/powerpc/platforms/82xx/mpc82xx_ads.c index 7334c1a..47cb09f 100644 --- a/arch/powerpc/platforms/82xx/mpc82xx_ads.c +++ b/arch/powerpc/platforms/82xx/mpc82xx_ads.c @@ -456,7 +456,7 @@ void m82xx_pci_init_irq(void) iounmap(immap); return; } - irq_map = get_property(np, "interrupt-map", &size); + irq_map = of_get_property(np, "interrupt-map", &size); if ((!irq_map) || (size <= 7)) { printk(KERN_INFO "No interrupt-map property of pci node\n"); iounmap(immap); @@ -481,7 +481,7 @@ void m82xx_pci_init_irq(void) } pci_pic_node = of_node_get(np); /* PCI interrupt controller registers: status and mask */ - regs = get_property(np, "reg", &size); + regs = of_get_property(np, "reg", &size); if ((!regs) || (size <= 2)) { printk(KERN_INFO "No reg property in pci pic node\n"); iounmap(immap); @@ -521,20 +521,20 @@ void __init add_bridge(struct device_nod struct pci_controller *hose; struct resource r; const int *bus_range; - const void *ptr; + const uint *ptr; memset(&r, 0, sizeof(r)); if (of_address_to_resource(np, 0, &r)) { printk(KERN_INFO "No PCI reg property in device tree\n"); return; } - if (!(ptr = get_property(np, "clock-frequency", NULL))) { + if (!(ptr = of_get_property(np, "clock-frequency", NULL))) { printk(KERN_INFO "No clock-frequency property in PCI node"); return; } - pci_clk_frq = *(uint *) ptr; + pci_clk_frq = *ptr; of_node_put(np); - bus_range = get_property(np, "bus-range", &len); + bus_range = of_get_property(np, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", np->full_name); diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig index 713b31a..19cafdf 100644 --- a/arch/powerpc/platforms/83xx/Kconfig +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -1,8 +1,6 @@ -menu "Platform support" - depends on PPC_83xx - choice prompt "Machine Type" + depends on PPC_83xx default MPC834x_MDS config MPC8313_RDB @@ -18,6 +16,13 @@ config MPC832x_MDS help This option enables support for the MPC832x MDS evaluation board. +config MPC832x_RDB + bool "Freescale MPC832x RDB" + select DEFAULT_UIMAGE + select QUICC_ENGINE + help + This option enables support for the MPC8323 RDB board. + config MPC834x_MDS bool "Freescale MPC834x MDS" select DEFAULT_UIMAGE @@ -57,7 +62,7 @@ config PPC_MPC832x bool select PPC_UDBG_16550 select PPC_INDIRECT_PCI - default y if MPC832x_MDS + default y if MPC832x_MDS || MPC832x_RDB config MPC834x bool @@ -70,5 +75,3 @@ config PPC_MPC836x select PPC_UDBG_16550 select PPC_INDIRECT_PCI default y if MPC836x_MDS - -endmenu diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile index dfc970d..31a91b5 100644 --- a/arch/powerpc/platforms/83xx/Makefile +++ b/arch/powerpc/platforms/83xx/Makefile @@ -4,6 +4,7 @@ # obj-y := misc.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_MPC8313_RDB) += mpc8313_rdb.o +obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o obj-$(CONFIG_MPC834x_ITX) += mpc834x_itx.o obj-$(CONFIG_MPC836x_MDS) += mpc836x_mds.o diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index 17e3a3c..fff09f5 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -41,7 +41,6 @@ #include #include #include "mpc83xx.h" -#include "mpc832x_mds.h" #undef DEBUG #ifdef DEBUG diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.h b/arch/powerpc/platforms/83xx/mpc832x_mds.h deleted file mode 100644 index a495889..0000000 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Description: - * MPC832x MDS board specific header. - * - * 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. - * - */ - -#ifndef __MACH_MPC832x_MDS_H__ -#define __MACH_MPC832x_MDS_H__ - -extern u8 *get_bcsr(void); - -#endif /* __MACH_MPC832x_MDS_H__ */ diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c new file mode 100644 index 0000000..6b71e9f --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -0,0 +1,138 @@ +/* + * arch/powerpc/platforms/83xx/mpc832x_rdb.c + * + * Copyright (C) Freescale Semiconductor, Inc. 2007. All rights reserved. + * + * Description: + * MPC832x RDB board specific routines. + * This file is based on mpc832x_mds.c and mpc8313_rdb.c + * Author: Michael Barkowski + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "mpc83xx.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +#ifndef CONFIG_PCI +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +#endif + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc832x_rdb_setup_arch(void) +{ + struct device_node *np; + + if (ppc_md.progress) + ppc_md.progress("mpc832x_rdb_setup_arch()", 0); + +#ifdef CONFIG_PCI + for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) + add_bridge(np); + + ppc_md.pci_exclude_device = mpc83xx_exclude_device; +#endif + +#ifdef CONFIG_QUICC_ENGINE + qe_reset(); + + if ((np = of_find_node_by_name(np, "par_io")) != NULL) { + par_io_init(np); + of_node_put(np); + + for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) + par_io_of_config(np); + } +#endif /* CONFIG_QUICC_ENGINE */ +} + +static struct of_device_id mpc832x_ids[] = { + { .type = "soc", }, + { .compatible = "soc", }, + { .type = "qe", }, + {}, +}; + +static int __init mpc832x_declare_of_platform_devices(void) +{ + if (!machine_is(mpc832x_rdb)) + return 0; + + /* Publish the QE devices */ + of_platform_bus_probe(NULL, mpc832x_ids, NULL); + + return 0; +} +device_initcall(mpc832x_declare_of_platform_devices); + +void __init mpc832x_rdb_init_IRQ(void) +{ + + struct device_node *np; + + np = of_find_node_by_type(NULL, "ipic"); + if (!np) + return; + + ipic_init(np, 0); + + /* Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + ipic_set_default_priority(); + of_node_put(np); + +#ifdef CONFIG_QUICC_ENGINE + np = of_find_node_by_type(NULL, "qeic"); + if (!np) + return; + + qe_ic_init(np, 0); + of_node_put(np); +#endif /* CONFIG_QUICC_ENGINE */ +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc832x_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC832xRDB"); +} + +define_machine(mpc832x_rdb) { + .name = "MPC832x RDB", + .probe = mpc832x_rdb_probe, + .setup_arch = mpc832x_rdb_setup_arch, + .init_IRQ = mpc832x_rdb_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.h b/arch/powerpc/platforms/83xx/mpc834x_itx.h deleted file mode 100644 index 174ca4e..0000000 --- a/arch/powerpc/platforms/83xx/mpc834x_itx.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * arch/powerpc/platforms/83xx/mpc834x_itx.h - * - * MPC834X ITX common board definitions - * - * Maintainer: Kumar Gala - * - * 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. - * - */ - -#ifndef __MACH_MPC83XX_ITX_H__ -#define __MACH_MPC83XX_ITX_H__ - -#define PIRQA MPC83xx_IRQ_EXT4 -#define PIRQB MPC83xx_IRQ_EXT5 -#define PIRQC MPC83xx_IRQ_EXT6 -#define PIRQD MPC83xx_IRQ_EXT7 - -#endif /* __MACH_MPC83XX_ITX_H__ */ diff --git a/arch/powerpc/platforms/83xx/pci.c b/arch/powerpc/platforms/83xx/pci.c index 9c36505..774457d 100644 --- a/arch/powerpc/platforms/83xx/pci.c +++ b/arch/powerpc/platforms/83xx/pci.c @@ -60,7 +60,7 @@ int __init add_bridge(struct device_node has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); /* Get bus range if any */ - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index e764c0a..629926e 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -1,8 +1,6 @@ -menu "Platform support" - depends on PPC_85xx - choice prompt "Machine Type" + depends on PPC_85xx default MPC8540_ADS config MPC8540_ADS @@ -30,6 +28,12 @@ # select QUICC_ENGINE help This option enables support for the MPC85xx MDS board +config MPC8544_DS + bool "Freescale MPC8544 DS" + select DEFAULT_UIMAGE + help + This option enables support for the MPC8544 DS board + endchoice config MPC8540 @@ -40,33 +44,15 @@ config MPC8540 config MPC8560 bool - select PPC_INDIRECT_PCI + select CPM2 default y if MPC8560_ADS config MPC85xx bool select PPC_UDBG_16550 select PPC_INDIRECT_PCI + select PPC_INDIRECT_PCI_BE + select MPIC select SERIAL_8250_SHARE_IRQ if SERIAL_8250 - default y if MPC8540_ADS || MPC85xx_CDS || MPC8560_ADS || MPC85xx_MDS - -config PPC_INDIRECT_PCI_BE - bool - depends on PPC_85xx - default y - -config MPIC - bool - default y - -config CPM2 - bool - depends on MPC8560 - default y - help - The CPM2 (Communications Processor Module) is a coprocessor on - embedded CPUs made by Motorola. Selecting this option means that - you wish to build a kernel for a machine with a CPM2 coprocessor - on it. - -endmenu + default y if MPC8540_ADS || MPC85xx_CDS || MPC8560_ADS \ + || MPC85xx_MDS || MPC8544_DS diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 4e63917..4e02cbb 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_PPC_85xx) += misc.o pci.o obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o +obj-$(CONFIG_MPC8544_DS) += mpc8544_ds.o obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o diff --git a/arch/powerpc/platforms/85xx/mpc8544_ds.c b/arch/powerpc/platforms/85xx/mpc8544_ds.c new file mode 100644 index 0000000..2867f85 --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc8544_ds.c @@ -0,0 +1,144 @@ +/* + * MPC8544 DS Board Setup + * + * Author Xianghua Xiao (x.xiao@freescale.com) + * Copyright 2007 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mpc85xx.h" + +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +#else +#define DBG(fmt, args...) +#endif + + +void __init mpc8544_ds_pic_init(void) +{ + struct mpic *mpic; + struct resource r; + struct device_node *np = NULL; +#ifdef CONFIG_PPC_I8259 + struct device_node *cascade_node = NULL; + int cascade_irq; +#endif + + np = of_find_node_by_type(np, "open-pic"); + + if (np == NULL) { + printk(KERN_ERR "Could not find open-pic node\n"); + return; + } + + if (of_address_to_resource(np, 0, &r)) { + printk(KERN_ERR "Failed to map mpic register space\n"); + of_node_put(np); + return; + } + + /* Alloc mpic structure and per isu has 16 INT entries. */ + mpic = mpic_alloc(np, r.start, + MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + 16, 64, " OPENPIC "); + BUG_ON(mpic == NULL); + + /* + * 48 Internal Interrupts + */ + mpic_assign_isu(mpic, 0, r.start + 0x10200); + mpic_assign_isu(mpic, 1, r.start + 0x10400); + mpic_assign_isu(mpic, 2, r.start + 0x10600); + + /* + * 16 External interrupts + */ + mpic_assign_isu(mpic, 3, r.start + 0x10000); + + mpic_init(mpic); + +#ifdef CONFIG_PPC_I8259 + /* Initialize the i8259 controller */ + for_each_node_by_type(np, "interrupt-controller") + if (device_is_compatible(np, "chrp,iic")) { + cascade_node = np; + break; + } + + if (cascade_node == NULL) { + printk(KERN_DEBUG "Could not find i8259 PIC\n"); + return; + } + + cascade_irq = irq_of_parse_and_map(cascade_node, 0); + if (cascade_irq == NO_IRQ) { + printk(KERN_ERR "Failed to map cascade interrupt\n"); + return; + } + + DBG("mpc8544ds: cascade mapped to irq %d\n", cascade_irq); + + i8259_init(cascade_node, 0); + of_node_put(cascade_node); + + set_irq_chained_handler(cascade_irq, mpc8544_8259_cascade); +#endif /* CONFIG_PPC_I8259 */ +} + + +/* + * Setup the architecture + */ +static void __init mpc8544_ds_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc8544_ds_setup_arch()", 0); + + printk("MPC8544 DS board from Freescale Semiconductor\n"); +} + + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc8544_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC8544DS"); +} + +define_machine(mpc8544_ds) { + .name = "MPC8544 DS", + .probe = mpc8544_ds_probe, + .setup_arch = mpc8544_ds_setup_arch, + .init_IRQ = mpc8544_ds_pic_init, + .get_irq = mpic_get_irq, + .restart = mpc85xx_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c index 8ed034a..5d27621 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -227,7 +227,7 @@ #endif if (cpu != 0) { const unsigned int *fp; - fp = get_property(cpu, "clock-frequency", NULL); + fp = of_get_property(cpu, "clock-frequency", NULL); if (fp != 0) loops_per_jiffy = *fp / HZ; else diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 4232686..7e71636 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -237,7 +237,7 @@ #endif if (cpu != 0) { const unsigned int *fp; - fp = get_property(cpu, "clock-frequency", NULL); + fp = of_get_property(cpu, "clock-frequency", NULL); if (fp != 0) loops_per_jiffy = *fp / HZ; else diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 81144d2..54db416 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -80,7 +80,7 @@ static void __init mpc85xx_mds_setup_arc np = of_find_node_by_type(NULL, "cpu"); if (np != NULL) { const unsigned int *fp = - get_property(np, "clock-frequency", NULL); + of_get_property(np, "clock-frequency", NULL); if (fp != NULL) loops_per_jiffy = *fp / HZ; else diff --git a/arch/powerpc/platforms/85xx/pci.c b/arch/powerpc/platforms/85xx/pci.c index 05930ee..48f17e2 100644 --- a/arch/powerpc/platforms/85xx/pci.c +++ b/arch/powerpc/platforms/85xx/pci.c @@ -51,7 +51,7 @@ int __init add_bridge(struct device_node has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); /* Get bus range if any */ - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index 0c70944..d1bcff5 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -1,8 +1,6 @@ -menu "Platform Support" - depends on PPC_86xx - choice prompt "Machine Type" + depends on PPC_86xx default MPC8641_HPCN config MPC8641_HPCN @@ -14,20 +12,10 @@ config MPC8641_HPCN endchoice - config MPC8641 bool select PPC_INDIRECT_PCI + select PPC_INDIRECT_PCI_BE select PPC_UDBG_16550 + select MPIC default y if MPC8641_HPCN - -config MPIC - bool - default y - -config PPC_INDIRECT_PCI_BE - bool - depends on PPC_86xx - default y - -endmenu diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile index 476a6ee..418fd8f 100644 --- a/arch/powerpc/platforms/86xx/Makefile +++ b/arch/powerpc/platforms/86xx/Makefile @@ -4,4 +4,4 @@ # obj-$(CONFIG_SMP) += mpc86xx_smp.o obj-$(CONFIG_MPC8641_HPCN) += mpc86xx_hpcn.o -obj-$(CONFIG_PCI) += pci.o mpc86xx_pcie.o +obj-$(CONFIG_PCI) += pci.o diff --git a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c index f42f801..3d3d98f 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c +++ b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c @@ -349,7 +349,7 @@ mpc86xx_hpcn_setup_arch(void) if (np != 0) { const unsigned int *fp; - fp = get_property(np, "clock-frequency", NULL); + fp = of_get_property(np, "clock-frequency", NULL); if (fp != 0) loops_per_jiffy = *fp / HZ; else @@ -384,7 +384,7 @@ mpc86xx_hpcn_show_cpuinfo(struct seq_fil root = of_find_node_by_path("/"); if (root) - model = get_property(root, "model", NULL); + model = of_get_property(root, "model", NULL); seq_printf(m, "Machine\t\t: %s\n", model); of_node_put(root); diff --git a/arch/powerpc/platforms/86xx/mpc86xx_pcie.c b/arch/powerpc/platforms/86xx/mpc86xx_pcie.c deleted file mode 100644 index a2f4f73..0000000 --- a/arch/powerpc/platforms/86xx/mpc86xx_pcie.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Support for indirect PCI bridges. - * - * Copyright (C) 1998 Gabriel Paubert. - * - * 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. - * - * "Temporary" MPC8548 Errata file - - * The standard indirect_pci code should work with future silicon versions. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mpc86xx.h" - -#define PCI_CFG_OUT out_be32 - -/* ERRATA PCI-Ex 14 PCIE Controller timeout */ -#define PCIE_FIX out_be32(hose->cfg_addr+0x4, 0x0400ffff) - - -static int -indirect_read_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, - int len, u32 *val) -{ - struct pci_controller *hose = bus->sysdata; - volatile void __iomem *cfg_data; - u32 temp; - - if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Possible artifact of CDCpp50937 needs further investigation */ - if (devfn != 0x0 && bus->number == 0xff) - return PCIBIOS_DEVICE_NOT_FOUND; - - PCIE_FIX; - if (bus->number == 0xff) { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } else { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000001 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } - - /* - * Note: the caller has already checked that offset is - * suitably aligned and that len is 1, 2 or 4. - */ - /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ - cfg_data = hose->cfg_data; - PCIE_FIX; - temp = in_le32(cfg_data); - switch (len) { - case 1: - *val = (temp >> (((offset & 3))*8)) & 0xff; - break; - case 2: - *val = (temp >> (((offset & 3))*8)) & 0xffff; - break; - default: - *val = temp; - break; - } - return PCIBIOS_SUCCESSFUL; -} - -static int -indirect_write_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, - int len, u32 val) -{ - struct pci_controller *hose = bus->sysdata; - volatile void __iomem *cfg_data; - u32 temp; - - if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Possible artifact of CDCpp50937 needs further investigation */ - if (devfn != 0x0 && bus->number == 0xff) - return PCIBIOS_DEVICE_NOT_FOUND; - - PCIE_FIX; - if (bus->number == 0xff) { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } else { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000001 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } - - /* - * Note: the caller has already checked that offset is - * suitably aligned and that len is 1, 2 or 4. - */ - /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ - cfg_data = hose->cfg_data; - switch (len) { - case 1: - PCIE_FIX; - temp = in_le32(cfg_data); - temp = (temp & ~(0xff << ((offset & 3) * 8))) | - (val << ((offset & 3) * 8)); - PCIE_FIX; - out_le32(cfg_data, temp); - break; - case 2: - PCIE_FIX; - temp = in_le32(cfg_data); - temp = (temp & ~(0xffff << ((offset & 3) * 8))); - temp |= (val << ((offset & 3) * 8)) ; - PCIE_FIX; - out_le32(cfg_data, temp); - break; - default: - PCIE_FIX; - out_le32(cfg_data, val); - break; - } - PCIE_FIX; - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops indirect_pcie_ops = { - indirect_read_config_pcie, - indirect_write_config_pcie -}; - -void __init -setup_indirect_pcie_nomap(struct pci_controller* hose, void __iomem * cfg_addr, - void __iomem * cfg_data) -{ - hose->cfg_addr = cfg_addr; - hose->cfg_data = cfg_data; - hose->ops = &indirect_pcie_ops; -} - -void __init -setup_indirect_pcie(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) -{ - unsigned long base = cfg_addr & PAGE_MASK; - void __iomem *mbase, *addr, *data; - - mbase = ioremap(base, PAGE_SIZE); - addr = mbase + (cfg_addr & ~PAGE_MASK); - if ((cfg_data & PAGE_MASK) != base) - mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); - data = mbase + (cfg_data & ~PAGE_MASK); - setup_indirect_pcie_nomap(hose, addr, data); -} diff --git a/arch/powerpc/platforms/86xx/pci.c b/arch/powerpc/platforms/86xx/pci.c index 481e18e..8235c56 100644 --- a/arch/powerpc/platforms/86xx/pci.c +++ b/arch/powerpc/platforms/86xx/pci.c @@ -22,9 +22,9 @@ #include #include #include #include -#include #include #include +#include #include "mpc86xx.h" @@ -163,7 +163,7 @@ int __init add_bridge(struct device_node has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); /* Get bus range if any */ - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); diff --git a/arch/powerpc/platforms/8xx/Kconfig b/arch/powerpc/platforms/8xx/Kconfig index beea683..39bb8c5 100644 --- a/arch/powerpc/platforms/8xx/Kconfig +++ b/arch/powerpc/platforms/8xx/Kconfig @@ -1,6 +1,3 @@ -menu "Platform support" - depends on PPC_8xx - config FADS bool @@ -9,6 +6,7 @@ config CPM1 choice prompt "8xx Machine Type" + depends on PPC_8xx depends on 8xx default MPC885ADS @@ -36,38 +34,36 @@ config MPC885ADS endchoice menu "Freescale Ethernet driver platform-specific options" - depends on (FS_ENET && MPC885ADS) - - config MPC8xx_SECOND_ETH - bool "Second Ethernet channel" - depends on MPC885ADS - default y - help - This enables support for second Ethernet on MPC885ADS and MPC86xADS boards. - The latter will use SCC1, for 885ADS you can select it below. - - choice - prompt "Second Ethernet channel" - depends on MPC8xx_SECOND_ETH - default MPC8xx_SECOND_ETH_FEC2 - - config MPC8xx_SECOND_ETH_FEC2 - bool "FEC2" - depends on MPC885ADS - help - Enable FEC2 to serve as 2-nd Ethernet channel. Note that SMC2 - (often 2-nd UART) will not work if this is enabled. - - config MPC8xx_SECOND_ETH_SCC3 - bool "SCC3" - depends on MPC885ADS - help - Enable SCC3 to serve as 2-nd Ethernet channel. Note that SMC1 - (often 1-nd UART) will not work if this is enabled. - - endchoice + depends on (FS_ENET && MPC885ADS) -endmenu + config MPC8xx_SECOND_ETH + bool "Second Ethernet channel" + depends on MPC885ADS + default y + help + This enables support for second Ethernet on MPC885ADS and MPC86xADS boards. + The latter will use SCC1, for 885ADS you can select it below. + + choice + prompt "Second Ethernet channel" + depends on MPC8xx_SECOND_ETH + default MPC8xx_SECOND_ETH_FEC2 + + config MPC8xx_SECOND_ETH_FEC2 + bool "FEC2" + depends on MPC885ADS + help + Enable FEC2 to serve as 2-nd Ethernet channel. Note that SMC2 + (often 2-nd UART) will not work if this is enabled. + + config MPC8xx_SECOND_ETH_SCC3 + bool "SCC3" + depends on MPC885ADS + help + Enable SCC3 to serve as 2-nd Ethernet channel. Note that SMC1 + (often 1-nd UART) will not work if this is enabled. + + endchoice endmenu @@ -98,7 +94,7 @@ config 8xx_CPU6 require workarounds for Linux (and most other OSes to work). If you get a BUG() very early in boot, this might fix the problem. For more details read the document entitled "MPC860 Family Device Errata - Reference" on Motorola's website. This option also incurs a + Reference" on Freescale's website. This option also incurs a performance hit. If in doubt, say N here. @@ -135,4 +131,3 @@ config UCODE_PATCH depends on !NO_UCODE_PATCH endmenu - diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c index 9ed7125..0901dba 100644 --- a/arch/powerpc/platforms/8xx/m8xx_setup.c +++ b/arch/powerpc/platforms/8xx/m8xx_setup.c @@ -85,17 +85,17 @@ init_internal_rtc(void) static int __init get_freq(char *name, unsigned long *val) { struct device_node *cpu; - unsigned int *fp; + const unsigned int *fp; int found = 0; /* The cpu node should have timebase and clock frequency properties */ cpu = of_find_node_by_type(NULL, "cpu"); if (cpu) { - fp = (unsigned int *)get_property(cpu, name, NULL); + fp = of_get_property(cpu, name, NULL); if (fp) { found = 1; - *val = *fp++; + *val = *fp; } of_node_put(cpu); @@ -262,7 +262,7 @@ void mpc8xx_show_cpuinfo(struct seq_file root = of_find_node_by_path("/"); if (root) - model = get_property(root, "model", NULL); + model = of_get_property(root, "model", NULL); seq_printf(m, "Machine\t\t: %s\n", model); of_node_put(root); diff --git a/arch/powerpc/platforms/8xx/mpc86xads.h b/arch/powerpc/platforms/8xx/mpc86xads.h index b5d19dd..59bad2f 100644 --- a/arch/powerpc/platforms/8xx/mpc86xads.h +++ b/arch/powerpc/platforms/8xx/mpc86xads.h @@ -37,7 +37,7 @@ #define MPC8xx_CPM_OFFSET (0x9c0) #define CPM_MAP_ADDR (get_immrbase() + MPC8xx_CPM_OFFSET) #define CPM_IRQ_OFFSET 16 // for compability with cpm_uart driver -#define PCMCIA_MEM_ADDR (uint)0xff020000) +#define PCMCIA_MEM_ADDR ((uint)0xff020000) #define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) /* Bits of interest in the BCSRs. diff --git a/arch/powerpc/platforms/8xx/mpc86xads_setup.c b/arch/powerpc/platforms/8xx/mpc86xads_setup.c index ef52ce7..a35315a 100644 --- a/arch/powerpc/platforms/8xx/mpc86xads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc86xads_setup.c @@ -247,7 +247,7 @@ void init_smc_ioports(struct fs_uart_pla } } -int platform_device_skip(char *model, int id) +int platform_device_skip(const char *model, int id) { return 0; } @@ -260,7 +260,7 @@ static void __init mpc86xads_setup_arch( if (cpu != 0) { const unsigned int *fp; - fp = get_property(cpu, "clock-frequency", NULL); + fp = of_get_property(cpu, "clock-frequency", NULL); if (fp != 0) loops_per_jiffy = *fp / HZ; else diff --git a/arch/powerpc/platforms/8xx/mpc885ads.h b/arch/powerpc/platforms/8xx/mpc885ads.h index 30cbebf..7c31aec 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads.h +++ b/arch/powerpc/platforms/8xx/mpc885ads.h @@ -37,7 +37,7 @@ #define MPC8xx_CPM_OFFSET (0x9c0) #define CPM_MAP_ADDR (get_immrbase() + MPC8xx_CPM_OFFSET) #define CPM_IRQ_OFFSET 16 // for compability with cpm_uart driver -#define PCMCIA_MEM_ADDR (uint)0xff020000) +#define PCMCIA_MEM_ADDR ((uint)0xff020000) #define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) /* Bits of interest in the BCSRs. diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c index c5fefdf..a57b577 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c @@ -322,7 +322,7 @@ void init_smc_ioports(struct fs_uart_pla } } -int platform_device_skip(char *model, int id) +int platform_device_skip(const char *model, int id) { #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3 const char *dev = "FEC"; @@ -346,7 +346,7 @@ static void __init mpc885ads_setup_arch( if (cpu != 0) { const unsigned int *fp; - fp = get_property(cpu, "clock-frequency", NULL); + fp = of_get_property(cpu, "clock-frequency", NULL); if (fp != 0) loops_per_jiffy = *fp / HZ; else diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig new file mode 100644 index 0000000..51e3334 --- /dev/null +++ b/arch/powerpc/platforms/Kconfig @@ -0,0 +1,259 @@ +menu "Platform support" + +choice + prompt "Machine type" + depends on PPC64 || CLASSIC32 + default PPC_MULTIPLATFORM + +config PPC_MULTIPLATFORM + bool "Generic desktop/server/laptop" + help + Select this option if configuring for an IBM pSeries or + RS/6000 machine, an Apple machine, or a PReP, CHRP, + Maple or Cell-based machine. + +config EMBEDDED6xx + bool "Embedded 6xx/7xx/7xxx-based board" + depends on PPC32 && (BROKEN||BROKEN_ON_SMP) + +config APUS + bool "Amiga-APUS" + depends on PPC32 && BROKEN + help + Select APUS if configuring for a PowerUP Amiga. + More information is available at: + . +endchoice + +source "arch/powerpc/platforms/pseries/Kconfig" +source "arch/powerpc/platforms/iseries/Kconfig" +source "arch/powerpc/platforms/chrp/Kconfig" +source "arch/powerpc/platforms/52xx/Kconfig" +source "arch/powerpc/platforms/powermac/Kconfig" +source "arch/powerpc/platforms/prep/Kconfig" +source "arch/powerpc/platforms/maple/Kconfig" +source "arch/powerpc/platforms/pasemi/Kconfig" +source "arch/powerpc/platforms/celleb/Kconfig" +source "arch/powerpc/platforms/ps3/Kconfig" +source "arch/powerpc/platforms/cell/Kconfig" +source "arch/powerpc/platforms/8xx/Kconfig" +source "arch/powerpc/platforms/82xx/Kconfig" +source "arch/powerpc/platforms/83xx/Kconfig" +source "arch/powerpc/platforms/85xx/Kconfig" +source "arch/powerpc/platforms/86xx/Kconfig" +source "arch/powerpc/platforms/embedded6xx/Kconfig" +#source "arch/powerpc/platforms/4xx/Kconfig + +config PPC_NATIVE + bool + depends on PPC_MULTIPLATFORM + help + Support for running natively on the hardware, i.e. without + a hypervisor. This option is not user-selectable but should + be selected by all platforms that need it. + +config UDBG_RTAS_CONSOLE + bool "RTAS based debug console" + depends on PPC_RTAS + default n + +config PPC_UDBG_BEAT + bool "BEAT based debug console" + depends on PPC_CELLEB + default n + +config XICS + depends on PPC_PSERIES + bool + default y + +config MPIC + bool + default n + +config MPIC_WEIRD + bool + default n + +config PPC_I8259 + bool + default n + +config U3_DART + bool + depends on PPC_MULTIPLATFORM && PPC64 + default n + +config PPC_RTAS + bool + default n + +config RTAS_ERROR_LOGGING + bool + depends on PPC_RTAS + default n + +config RTAS_PROC + bool "Proc interface to RTAS" + depends on PPC_RTAS + default y + +config RTAS_FLASH + tristate "Firmware flash interface" + depends on PPC64 && RTAS_PROC + +config PPC_PMI + tristate "Support for PMI" + depends PPC_IBM_CELL_BLADE + help + PMI (Platform Management Interrupt) is a way to + communicate with the BMC (Baseboard Mangement Controller). + It is used in some IBM Cell blades. + default m + +config MMIO_NVRAM + bool + default n + +config MPIC_U3_HT_IRQS + bool + depends on PPC_MAPLE + default y + +config IBMVIO + depends on PPC_PSERIES || PPC_ISERIES + bool + default y + +config IBMEBUS + depends on PPC_PSERIES + bool "Support for GX bus based adapters" + help + Bus device driver for GX bus based adapters. + +config PPC_MPC106 + bool + default n + +config PPC_970_NAP + bool + default n + +config PPC_INDIRECT_IO + bool + select GENERIC_IOMAP + default n + +config GENERIC_IOMAP + bool + default n + +source "drivers/cpufreq/Kconfig" + +menu "CPU Frequency drivers" + depends on CPU_FREQ + +config CPU_FREQ_PMAC + bool "Support for Apple PowerBooks" + depends on ADB_PMU && PPC32 + select CPU_FREQ_TABLE + help + This adds support for frequency switching on Apple PowerBooks, + this currently includes some models of iBook & Titanium + PowerBook. + +config CPU_FREQ_PMAC64 + bool "Support for some Apple G5s" + depends on PPC_PMAC && PPC64 + select CPU_FREQ_TABLE + help + This adds support for frequency switching on Apple iMac G5, + and some of the more recent desktop G5 machines as well. + +config PPC_PASEMI_CPUFREQ + bool "Support for PA Semi PWRficient" + depends on PPC_PASEMI + default y + select CPU_FREQ_TABLE + help + This adds the support for frequency switching on PA Semi + PWRficient processors. + +endmenu + +config PPC601_SYNC_FIX + bool "Workarounds for PPC601 bugs" + depends on 6xx && (PPC_PREP || PPC_PMAC) + help + Some versions of the PPC601 (the first PowerPC chip) have bugs which + mean that extra synchronization instructions are required near + certain instructions, typically those that make major changes to the + CPU state. These extra instructions reduce performance slightly. + If you say N here, these extra instructions will not be included, + resulting in a kernel which will run faster but may not run at all + on some systems with the PPC601 chip. + + If in doubt, say Y here. + +config TAU + bool "On-chip CPU temperature sensor support" + depends on CLASSIC32 + help + G3 and G4 processors have an on-chip temperature sensor called the + 'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die + temperature within 2-4 degrees Celsius. This option shows the current + on-die temperature in /proc/cpuinfo if the cpu supports it. + + Unfortunately, on some chip revisions, this sensor is very inaccurate + and in many cases, does not work at all, so don't assume the cpu + temp is actually what /proc/cpuinfo says it is. + +config TAU_INT + bool "Interrupt driven TAU driver (DANGEROUS)" + depends on TAU + ---help--- + The TAU supports an interrupt driven mode which causes an interrupt + whenever the temperature goes out of range. This is the fastest way + to get notified the temp has exceeded a range. With this option off, + a timer is used to re-check the temperature periodically. + + However, on some cpus it appears that the TAU interrupt hardware + is buggy and can cause a situation which would lead unexplained hard + lockups. + + Unless you are extending the TAU driver, or enjoy kernel/hardware + debugging, leave this option off. + +config TAU_AVERAGE + bool "Average high and low temp" + depends on TAU + ---help--- + The TAU hardware can compare the temperature to an upper and lower + bound. The default behavior is to show both the upper and lower + bound in /proc/cpuinfo. If the range is large, the temperature is + either changing a lot, or the TAU hardware is broken (likely on some + G4's). If the range is small (around 4 degrees), the temperature is + relatively stable. If you say Y here, a single temperature value, + halfway between the upper and lower bounds, will be reported in + /proc/cpuinfo. + + If in doubt, say N here. + +config QUICC_ENGINE + bool + help + The QUICC Engine (QE) is a new generation of communications + coprocessors on Freescale embedded CPUs (akin to CPM in older chips). + Selecting this option means that you wish to build a kernel + for a machine with a QE coprocessor. + +config CPM2 + bool + default n + help + The CPM2 (Communications Processor Module) is a coprocessor on + embedded CPUs made by Freescale. Selecting this option means that + you wish to build a kernel for a machine with a CPM2 coprocessor + on it (826x, 827x, 8560). + +endmenu diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 06a85b7..8255177 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -1,3 +1,26 @@ +config PPC_CELL + bool + default n + +config PPC_CELL_NATIVE + bool + select PPC_CELL + select PPC_DCR_MMIO + select PPC_OF_PLATFORM_PCI + select PPC_INDIRECT_IO + select PPC_NATIVE + select MPIC + default n + +config PPC_IBM_CELL_BLADE + bool "IBM Cell Blade" + depends on PPC_MULTIPLATFORM && PPC64 + select PPC_CELL_NATIVE + select PPC_RTAS + select MMIO_NVRAM + select PPC_UDBG_16550 + select UDBG_RTAS_CONSOLE + menu "Cell Broadband Engine options" depends on PPC_CELL @@ -18,6 +41,7 @@ config SPU_BASE config CBE_RAS bool "RAS features for bare metal Cell BE" + depends on PPC_CELL_NATIVE default y config CBE_THERM diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq.c b/arch/powerpc/platforms/cell/cbe_cpufreq.c index a3850fd..f9ac3fe 100644 --- a/arch/powerpc/platforms/cell/cbe_cpufreq.c +++ b/arch/powerpc/platforms/cell/cbe_cpufreq.c @@ -25,9 +25,12 @@ #include #include #include +#include #include #include #include +#include +#include #include "cbe_regs.h" @@ -68,6 +71,38 @@ static u64 MIC_Slow_Next_Timer_table[] = * hardware specific functions */ +static struct of_device *pmi_dev; + +static int set_pmode_pmi(int cpu, unsigned int pmode) +{ + int ret; + pmi_message_t pmi_msg; +#ifdef DEBUG + u64 time; +#endif + + pmi_msg.type = PMI_TYPE_FREQ_CHANGE; + pmi_msg.data1 = cbe_cpu_to_node(cpu); + pmi_msg.data2 = pmode; + +#ifdef DEBUG + time = (u64) get_cycles(); +#endif + + pmi_send_message(pmi_dev, pmi_msg); + ret = pmi_msg.data2; + + pr_debug("PMI returned slow mode %d\n", ret); + +#ifdef DEBUG + time = (u64) get_cycles() - time; /* actual cycles (not cpu cycles!) */ + time = 1000000000 * time / CLOCK_TICK_RATE; /* time in ns (10^-9) */ + pr_debug("had to wait %lu ns for a transition\n", time); +#endif + return ret; +} + + static int get_pmode(int cpu) { int ret; @@ -79,7 +114,7 @@ static int get_pmode(int cpu) return ret; } -static int set_pmode(int cpu, unsigned int pmode) +static int set_pmode_reg(int cpu, unsigned int pmode) { struct cbe_pmd_regs __iomem *pmd_regs; struct cbe_mic_tm_regs __iomem *mic_tm_regs; @@ -120,37 +155,71 @@ static int set_pmode(int cpu, unsigned i return 0; } +static int set_pmode(int cpu, unsigned int slow_mode) { + if (pmi_dev) + return set_pmode_pmi(cpu, slow_mode); + else + return set_pmode_reg(cpu, slow_mode); +} + +static void cbe_cpufreq_handle_pmi(struct of_device *dev, pmi_message_t pmi_msg) +{ + struct cpufreq_policy policy; + u8 cpu; + u8 cbe_pmode_new; + + BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE); + + cpu = cbe_node_to_cpu(pmi_msg.data1); + cbe_pmode_new = pmi_msg.data2; + + cpufreq_get_policy(&policy, cpu); + + policy.max = min(policy.max, cbe_freqs[cbe_pmode_new].frequency); + policy.min = min(policy.min, policy.max); + + pr_debug("cbe_handle_pmi: new policy.min=%d policy.max=%d\n", policy.min, policy.max); + cpufreq_set_policy(&policy); +} + +static struct pmi_handler cbe_pmi_handler = { + .type = PMI_TYPE_FREQ_CHANGE, + .handle_pmi_message = cbe_cpufreq_handle_pmi, +}; + + /* * cpufreq functions */ -static int cbe_cpufreq_cpu_init (struct cpufreq_policy *policy) +static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy) { - u32 *max_freq; + const u32 *max_freqp; + u32 max_freq; int i, cur_pmode; struct device_node *cpu; cpu = of_get_cpu_node(policy->cpu, NULL); - if(!cpu) + if (!cpu) return -ENODEV; pr_debug("init cpufreq on CPU %d\n", policy->cpu); - max_freq = (u32*) get_property(cpu, "clock-frequency", NULL); + max_freqp = of_get_property(cpu, "clock-frequency", NULL); - if(!max_freq) + if (!max_freqp) return -EINVAL; - // we need the freq in kHz - *max_freq /= 1000; + /* we need the freq in kHz */ + max_freq = *max_freqp / 1000; - pr_debug("max clock-frequency is at %u kHz\n", *max_freq); + pr_debug("max clock-frequency is at %u kHz\n", max_freq); pr_debug("initializing frequency table\n"); - // initialize frequency table + /* initialize frequency table */ for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { - cbe_freqs[i].frequency = *max_freq / cbe_freqs[i].index; + cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index; pr_debug("%d: %d\n", i, cbe_freqs[i].frequency); } @@ -167,10 +236,10 @@ #ifdef CONFIG_SMP policy->cpus = cpu_sibling_map[policy->cpu]; #endif - cpufreq_frequency_table_get_attr (cbe_freqs, policy->cpu); + cpufreq_frequency_table_get_attr(cbe_freqs, policy->cpu); /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max are set correctly */ - return cpufreq_frequency_table_cpuinfo (policy, cbe_freqs); + return cpufreq_frequency_table_cpuinfo(policy, cbe_freqs); } static int cbe_cpufreq_cpu_exit(struct cpufreq_policy *policy) @@ -202,7 +271,7 @@ static int cbe_cpufreq_target(struct cpu freqs.new = cbe_freqs[cbe_pmode_new].frequency; freqs.cpu = policy->cpu; - mutex_lock (&cbe_switch_mutex); + mutex_lock(&cbe_switch_mutex); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", @@ -233,11 +302,26 @@ static struct cpufreq_driver cbe_cpufreq static int __init cbe_cpufreq_init(void) { + struct device_node *np; + + if (!machine_is(cell)) + return -ENODEV; + + np = of_find_node_by_type(NULL, "ibm,pmi"); + + pmi_dev = of_find_device_by_node(np); + + if (pmi_dev) + pmi_register_handler(pmi_dev, &cbe_pmi_handler); + return cpufreq_register_driver(&cbe_cpufreq_driver); } static void __exit cbe_cpufreq_exit(void) { + if (pmi_dev) + pmi_unregister_handler(pmi_dev, &cbe_pmi_handler); + cpufreq_unregister_driver(&cbe_cpufreq_driver); } diff --git a/arch/powerpc/platforms/cell/cbe_regs.c b/arch/powerpc/platforms/cell/cbe_regs.c index 9a0ee62..12c9674 100644 --- a/arch/powerpc/platforms/cell/cbe_regs.c +++ b/arch/powerpc/platforms/cell/cbe_regs.c @@ -14,6 +14,8 @@ #include #include #include #include +#include +#include #include "cbe_regs.h" @@ -27,6 +29,7 @@ #include "cbe_regs.h" static struct cbe_regs_map { struct device_node *cpu_node; + struct device_node *be_node; struct cbe_pmd_regs __iomem *pmd_regs; struct cbe_iic_regs __iomem *iic_regs; struct cbe_mic_tm_regs __iomem *mic_tm_regs; @@ -37,30 +40,43 @@ static int cbe_regs_map_count; static struct cbe_thread_map { struct device_node *cpu_node; + struct device_node *be_node; struct cbe_regs_map *regs; + unsigned int thread_id; + unsigned int cbe_id; } cbe_thread_map[NR_CPUS]; +static cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = CPU_MASK_NONE }; +static cpumask_t cbe_first_online_cpu = CPU_MASK_NONE; + static struct cbe_regs_map *cbe_find_map(struct device_node *np) { int i; struct device_node *tmp_np; - if (strcasecmp(np->type, "spe") == 0) { - if (np->data == NULL) { - /* walk up path until cpu node was found */ - tmp_np = np->parent; - while (tmp_np != NULL && strcasecmp(tmp_np->type, "cpu") != 0) - tmp_np = tmp_np->parent; + if (strcasecmp(np->type, "spe")) { + for (i = 0; i < cbe_regs_map_count; i++) + if (cbe_regs_maps[i].cpu_node == np || + cbe_regs_maps[i].be_node == np) + return &cbe_regs_maps[i]; + return NULL; + } - np->data = cbe_find_map(tmp_np); - } + if (np->data) return np->data; - } - for (i = 0; i < cbe_regs_map_count; i++) - if (cbe_regs_maps[i].cpu_node == np) - return &cbe_regs_maps[i]; - return NULL; + /* walk up path until cpu or be node was found */ + tmp_np = np; + do { + tmp_np = tmp_np->parent; + /* on a correct devicetree we wont get up to root */ + BUG_ON(!tmp_np); + } while (strcasecmp(tmp_np->type, "cpu") && + strcasecmp(tmp_np->type, "be")); + + np->data = cbe_find_map(tmp_np); + + return np->data; } struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np) @@ -130,38 +146,105 @@ struct cbe_mic_tm_regs __iomem *cbe_get_ } EXPORT_SYMBOL_GPL(cbe_get_cpu_mic_tm_regs); -/* FIXME - * This is little more than a stub at the moment. It should be - * fleshed out so that it works for both SMT and non-SMT, no - * matter if the passed cpu is odd or even. - * For SMT enabled, returns 0 for even-numbered cpu; otherwise 1. - * For SMT disabled, returns 0 for all cpus. - */ u32 cbe_get_hw_thread_id(int cpu) { - return (cpu & 1); + return cbe_thread_map[cpu].thread_id; } EXPORT_SYMBOL_GPL(cbe_get_hw_thread_id); -void __init cbe_regs_init(void) +u32 cbe_cpu_to_node(int cpu) { - int i; - struct device_node *cpu; + return cbe_thread_map[cpu].cbe_id; +} +EXPORT_SYMBOL_GPL(cbe_cpu_to_node); - /* Build local fast map of CPUs */ - for_each_possible_cpu(i) - cbe_thread_map[i].cpu_node = of_get_cpu_node(i, NULL); +u32 cbe_node_to_cpu(int node) +{ + return find_first_bit( (unsigned long *) &cbe_local_mask[node], sizeof(cpumask_t)); +} +EXPORT_SYMBOL_GPL(cbe_node_to_cpu); - /* Find maps for each device tree CPU */ - for_each_node_by_type(cpu, "cpu") { - struct cbe_regs_map *map = &cbe_regs_maps[cbe_regs_map_count++]; +static struct device_node *cbe_get_be_node(int cpu_id) +{ + struct device_node *np; + + for_each_node_by_type (np, "be") { + int len,i; + const phandle *cpu_handle; + + cpu_handle = of_get_property(np, "cpus", &len); + + for (i=0; ibe_node) { + struct device_node *be, *np; + + be = map->be_node; + + for_each_node_by_type(np, "pervasive") + if (of_get_parent(np) == be) + map->pmd_regs = of_iomap(np, 0); + + for_each_node_by_type(np, "CBEA-Internal-Interrupt-Controller") + if (of_get_parent(np) == be) + map->iic_regs = of_iomap(np, 2); + for_each_node_by_type(np, "mic-tm") + if (of_get_parent(np) == be) + map->mic_tm_regs = of_iomap(np, 0); + } else { + struct device_node *cpu; /* That hack must die die die ! */ const struct address_prop { unsigned long address; unsigned int len; } __attribute__((packed)) *prop; + cpu = map->cpu_node; + + prop = of_get_property(cpu, "pervasive", NULL); + if (prop != NULL) + map->pmd_regs = ioremap(prop->address, prop->len); + + prop = of_get_property(cpu, "iic", NULL); + if (prop != NULL) + map->iic_regs = ioremap(prop->address, prop->len); + + prop = of_get_property(cpu, "mic-tm", NULL); + if (prop != NULL) + map->mic_tm_regs = ioremap(prop->address, prop->len); + } +} + + +void __init cbe_regs_init(void) +{ + int i; + unsigned int thread_id; + struct device_node *cpu; + + /* Build local fast map of CPUs */ + for_each_possible_cpu(i) { + cbe_thread_map[i].cpu_node = of_get_cpu_node(i, &thread_id); + cbe_thread_map[i].be_node = cbe_get_be_node(i); + cbe_thread_map[i].thread_id = thread_id; + } + + /* Find maps for each device tree CPU */ + for_each_node_by_type(cpu, "cpu") { + struct cbe_regs_map *map; + unsigned int cbe_id; + + cbe_id = cbe_regs_map_count++; + map = &cbe_regs_maps[cbe_id]; if (cbe_regs_map_count > MAX_CBE) { printk(KERN_ERR "cbe_regs: More BE chips than supported" @@ -170,22 +253,21 @@ void __init cbe_regs_init(void) return; } map->cpu_node = cpu; - for_each_possible_cpu(i) - if (cbe_thread_map[i].cpu_node == cpu) - cbe_thread_map[i].regs = map; - prop = get_property(cpu, "pervasive", NULL); - if (prop != NULL) - map->pmd_regs = ioremap(prop->address, prop->len); + for_each_possible_cpu(i) { + struct cbe_thread_map *thread = &cbe_thread_map[i]; - prop = get_property(cpu, "iic", NULL); - if (prop != NULL) - map->iic_regs = ioremap(prop->address, prop->len); + if (thread->cpu_node == cpu) { + thread->regs = map; + thread->cbe_id = cbe_id; + map->be_node = thread->be_node; + cpu_set(i, cbe_local_mask[cbe_id]); + if(thread->thread_id == 0) + cpu_set(i, cbe_first_online_cpu); + } + } - prop = (struct address_prop *)get_property(cpu, "mic-tm", - NULL); - if (prop != NULL) - map->mic_tm_regs = ioremap(prop->address, prop->len); + cbe_fill_regs_map(map); } } diff --git a/arch/powerpc/platforms/cell/cbe_regs.h b/arch/powerpc/platforms/cell/cbe_regs.h index 440a7ec..17d5971 100644 --- a/arch/powerpc/platforms/cell/cbe_regs.h +++ b/arch/powerpc/platforms/cell/cbe_regs.h @@ -255,6 +255,11 @@ #define CBE_MIC_DISABLE_PWR_SAV_1 0x8000 extern struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np); extern struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu); +/* some utility functions to deal with SMT */ +extern u32 cbe_get_hw_thread_id(int cpu); +extern u32 cbe_cpu_to_node(int cpu); +extern u32 cbe_node_to_cpu(int node); + /* Init this module early */ extern void cbe_regs_init(void); diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c index 70e0d96..f370f0f 100644 --- a/arch/powerpc/platforms/cell/cbe_thermal.c +++ b/arch/powerpc/platforms/cell/cbe_thermal.c @@ -1,6 +1,31 @@ /* * thermal support for the cell processor * + * This module adds some sysfs attributes to cpu and spu nodes. + * Base for measurements are the digital thermal sensors (DTS) + * located on the chip. + * The accuracy is 2 degrees, starting from 65 up to 125 degrees celsius + * The attributes can be found under + * /sys/devices/system/cpu/cpuX/thermal + * /sys/devices/system/spu/spuX/thermal + * + * The following attributes are added for each node: + * temperature: + * contains the current temperature measured by the DTS + * throttle_begin: + * throttling begins when temperature is greater or equal to + * throttle_begin. Setting this value to 125 prevents throttling. + * throttle_end: + * throttling is being ceased, if the temperature is lower than + * throttle_end. Due to a delay between applying throttling and + * a reduced temperature this value should be less than throttle_begin. + * A value equal to throttle_begin provides only a very little hysteresis. + * throttle_full_stop: + * If the temperatrue is greater or equal to throttle_full_stop, + * full throttling is applied to the cpu or spu. This value should be + * greater than throttle_begin and throttle_end. Setting this value to + * 65 prevents the unit from running code at all. + * * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 * * Author: Christian Krafft @@ -31,6 +56,26 @@ #include #include "cbe_regs.h" #include "spu_priv1_mmio.h" +#define TEMP_MIN 65 +#define TEMP_MAX 125 + +#define SYSDEV_PREFIX_ATTR(_prefix,_name,_mode) \ +struct sysdev_attribute attr_ ## _prefix ## _ ## _name = { \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ + .show = _prefix ## _show_ ## _name, \ + .store = _prefix ## _store_ ## _name, \ +}; + +static inline u8 reg_to_temp(u8 reg_value) +{ + return ((reg_value & 0x3f) << 1) + TEMP_MIN; +} + +static inline u8 temp_to_reg(u8 temp) +{ + return ((temp - TEMP_MIN) >> 1) & 0x3f; +} + static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev) { struct spu *spu; @@ -43,14 +88,14 @@ static struct cbe_pmd_regs __iomem *get_ /* returns the value for a given spu in a given register */ static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg) { - unsigned int *id; + const unsigned int *id; union spe_reg value; struct spu *spu; /* getting the id from the reg attribute will not work on future device-tree layouts * in future we should store the id to the spu struct and use it here */ spu = container_of(sysdev, struct spu, sysdev); - id = (unsigned int *)get_property(spu_devnode(spu), "reg", NULL); + id = of_get_property(spu_devnode(spu), "reg", NULL); value.val = in_be64(®->val); return value.spe[*id]; @@ -58,20 +103,81 @@ static u8 spu_read_register_value(struct static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf) { - int value; + u8 value; struct cbe_pmd_regs __iomem *pmd_regs; pmd_regs = get_pmd_regs(sysdev); value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1); - /* clear all other bits */ + + return sprintf(buf, "%d\n", reg_to_temp(value)); +} + +static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, int pos) +{ + u64 value; + + value = in_be64(&pmd_regs->tm_tpr.val); + /* access the corresponding byte */ + value >>= pos; value &= 0x3F; - /* temp is stored in steps of 2 degrees */ - value *= 2; - /* base temp is 65 degrees */ - value += 65; - return sprintf(buf, "%d\n", (int) value); + return sprintf(buf, "%d\n", reg_to_temp(value)); +} + +static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos) +{ + u64 reg_value; + int temp; + u64 new_value; + int ret; + + ret = sscanf(buf, "%u", &temp); + + if (ret != 1 || temp < TEMP_MIN || temp > TEMP_MAX) + return -EINVAL; + + new_value = temp_to_reg(temp); + + reg_value = in_be64(&pmd_regs->tm_tpr.val); + + /* zero out bits for new value */ + reg_value &= ~(0xffull << pos); + /* set bits to new value */ + reg_value |= new_value << pos; + + out_be64(&pmd_regs->tm_tpr.val, reg_value); + return size; +} + +static ssize_t spu_show_throttle_end(struct sys_device *sysdev, char *buf) +{ + return show_throttle(get_pmd_regs(sysdev), buf, 0); +} + +static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, char *buf) +{ + return show_throttle(get_pmd_regs(sysdev), buf, 8); +} + +static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +{ + return show_throttle(get_pmd_regs(sysdev), buf, 16); +} + +static ssize_t spu_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +{ + return store_throttle(get_pmd_regs(sysdev), buf, size, 0); +} + +static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +{ + return store_throttle(get_pmd_regs(sysdev), buf, size, 8); +} + +static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +{ + return store_throttle(get_pmd_regs(sysdev), buf, size, 16); } static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos) @@ -82,16 +188,9 @@ static ssize_t ppe_show_temp(struct sys_ pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id); value = in_be64(&pmd_regs->ts_ctsr2); - /* access the corresponding byte */ - value >>= pos; - /* clear all other bits */ - value &= 0x3F; - /* temp is stored in steps of 2 degrees */ - value *= 2; - /* base temp is 65 degrees */ - value += 65; + value = (value >> pos) & 0x3f; - return sprintf(buf, "%d\n", (int) value); + return sprintf(buf, "%d\n", reg_to_temp(value)); } @@ -108,13 +207,52 @@ static ssize_t ppe_show_temp1(struct sys return ppe_show_temp(sysdev, buf, 0); } +static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, char *buf) +{ + return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32); +} + +static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, char *buf) +{ + return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40); +} + +static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +{ + return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48); +} + +static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +{ + return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32); +} + +static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +{ + return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40); +} + +static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +{ + return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48); +} + + static struct sysdev_attribute attr_spu_temperature = { .attr = {.name = "temperature", .mode = 0400 }, .show = spu_show_temp, }; +static SYSDEV_PREFIX_ATTR(spu, throttle_end, 0600); +static SYSDEV_PREFIX_ATTR(spu, throttle_begin, 0600); +static SYSDEV_PREFIX_ATTR(spu, throttle_full_stop, 0600); + + static struct attribute *spu_attributes[] = { &attr_spu_temperature.attr, + &attr_spu_throttle_end.attr, + &attr_spu_throttle_begin.attr, + &attr_spu_throttle_full_stop.attr, NULL, }; @@ -133,9 +271,16 @@ static struct sysdev_attribute attr_ppe_ .show = ppe_show_temp1, }; +static SYSDEV_PREFIX_ATTR(ppe, throttle_end, 0600); +static SYSDEV_PREFIX_ATTR(ppe, throttle_begin, 0600); +static SYSDEV_PREFIX_ATTR(ppe, throttle_full_stop, 0600); + static struct attribute *ppe_attributes[] = { &attr_ppe_temperature0.attr, &attr_ppe_temperature1.attr, + &attr_ppe_throttle_end.attr, + &attr_ppe_throttle_begin.attr, + &attr_ppe_throttle_full_stop.attr, NULL, }; diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c index 6666d03..4fc4e92 100644 --- a/arch/powerpc/platforms/cell/interrupt.c +++ b/arch/powerpc/platforms/cell/interrupt.c @@ -261,7 +261,7 @@ static int iic_host_xlate(struct irq_hos return -ENODEV; if (intsize != 1) return -ENODEV; - val = get_property(ct, "#interrupt-cells", NULL); + val = of_get_property(ct, "#interrupt-cells", NULL); if (val == NULL || *val != 1) return -ENODEV; @@ -327,7 +327,7 @@ static int __init setup_iic(void) if (!device_is_compatible(dn, "IBM,CBEA-Internal-Interrupt-Controller")) continue; - np = get_property(dn, "ibm,interrupt-server-ranges", NULL); + np = of_get_property(dn, "ibm,interrupt-server-ranges", NULL); if (np == NULL) { printk(KERN_WARNING "IIC: CPU association not found\n"); of_node_put(dn); diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c index 7c73128..d68d920 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.c +++ b/arch/powerpc/platforms/cell/io-workarounds.c @@ -318,7 +318,7 @@ static int __init spider_pci_workaround_ */ list_for_each_entry(phb, &hose_list, list_node) { struct device_node *np = phb->arch_data; - const char *model = get_property(np, "model", NULL); + const char *model = of_get_property(np, "model", NULL); /* If no model property or name isn't exactly "pci", skip */ if (model == NULL || strcmp(np->name, "pci")) diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 67d617b..760caa7 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -291,9 +291,9 @@ static int cell_iommu_find_ioc(int nid, const unsigned int *nidp; const unsigned long *tmp; - nidp = get_property(np, "node-id", NULL); + nidp = of_get_property(np, "node-id", NULL); if (nidp && *nidp == nid) { - tmp = get_property(np, "ioc-translation", NULL); + tmp = of_get_property(np, "ioc-translation", NULL); if (tmp) { *base = *tmp; of_node_put(np); @@ -430,7 +430,7 @@ cell_iommu_setup_window(struct cbe_iommu struct iommu_window *window; const unsigned int *ioid; - ioid = get_property(np, "ioid", NULL); + ioid = of_get_property(np, "ioid", NULL); if (ioid == NULL) printk(KERN_WARNING "iommu: missing ioid for %s using 0\n", np->full_name); @@ -496,7 +496,7 @@ static void cell_dma_dev_setup(struct de struct dev_archdata *archdata = &dev->archdata; /* If we run without iommu, no need to do anything */ - if (pci_dma_ops == &dma_direct_ops) + if (get_pci_dma_ops() == &dma_direct_ops) return; /* Current implementation uses the first window available in that @@ -530,7 +530,7 @@ static int cell_of_bus_notify(struct not return 0; /* We use the PCI DMA ops */ - dev->archdata.dma_ops = pci_dma_ops; + dev->archdata.dma_ops = get_pci_dma_ops(); cell_dma_dev_setup(dev); @@ -549,7 +549,7 @@ static int __init cell_iommu_get_window( unsigned long index; /* Use ibm,dma-window if available, else, hard code ! */ - dma_window = get_property(np, "ibm,dma-window", NULL); + dma_window = of_get_property(np, "ibm,dma-window", NULL); if (dma_window == NULL) { *base = 0; *size = 0x80000000u; @@ -646,7 +646,7 @@ static int __init cell_iommu_init_disabl unsigned long base = 0, size; /* When no iommu is present, we use direct DMA ops */ - pci_dma_ops = &dma_direct_ops; + set_pci_dma_ops(&dma_direct_ops); /* First make sure all IOC translation is turned off */ cell_disable_iommus(); @@ -734,7 +734,7 @@ static int __init cell_iommu_init(void) } /* Setup default PCI iommu ops */ - pci_dma_ops = &dma_iommu_ops; + set_pci_dma_ops(&dma_iommu_ops); bail: /* Register callbacks on OF platform device addition/removal diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c index 0984c70..3961a08 100644 --- a/arch/powerpc/platforms/cell/ras.c +++ b/arch/powerpc/platforms/cell/ras.c @@ -3,11 +3,13 @@ #define DEBUG #include #include #include +#include #include #include #include #include +#include #include "ras.h" #include "cbe_regs.h" @@ -82,6 +84,164 @@ static int cbe_machine_check_handler(str return 0; } +struct ptcal_area { + struct list_head list; + int nid; + int order; + struct page *pages; +}; + +static LIST_HEAD(ptcal_list); + +static int ptcal_start_tok, ptcal_stop_tok; + +static int __init cbe_ptcal_enable_on_node(int nid, int order) +{ + struct ptcal_area *area; + int ret = -ENOMEM; + unsigned long addr; + +#ifdef CONFIG_CRASH_DUMP + rtas_call(ptcal_stop_tok, 1, 1, NULL, nid); +#endif + + area = kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + goto out_err; + + area->nid = nid; + area->order = order; + area->pages = alloc_pages_node(area->nid, GFP_KERNEL, area->order); + + if (!area->pages) + goto out_free_area; + + addr = __pa(page_address(area->pages)); + + ret = -EIO; + if (rtas_call(ptcal_start_tok, 3, 1, NULL, area->nid, + (unsigned int)(addr >> 32), + (unsigned int)(addr & 0xffffffff))) { + printk(KERN_ERR "%s: error enabling PTCAL on node %d!\n", + __FUNCTION__, nid); + goto out_free_pages; + } + + list_add(&area->list, &ptcal_list); + + return 0; + +out_free_pages: + __free_pages(area->pages, area->order); +out_free_area: + kfree(area); +out_err: + return ret; +} + +static int __init cbe_ptcal_enable(void) +{ + const u32 *size; + struct device_node *np; + int order, found_mic = 0; + + np = of_find_node_by_path("/rtas"); + if (!np) + return -ENODEV; + + size = of_get_property(np, "ibm,cbe-ptcal-size", NULL); + if (!size) + return -ENODEV; + + pr_debug("%s: enabling PTCAL, size = 0x%x\n", __FUNCTION__, *size); + order = get_order(*size); + of_node_put(np); + + /* support for malta device trees, with be@/mic@ nodes */ + for_each_node_by_type(np, "mic-tm") { + cbe_ptcal_enable_on_node(of_node_to_nid(np), order); + found_mic = 1; + } + + if (found_mic) + return 0; + + /* support for older device tree - use cpu nodes */ + for_each_node_by_type(np, "cpu") { + const u32 *nid = of_get_property(np, "node-id", NULL); + if (!nid) { + printk(KERN_ERR "%s: node %s is missing node-id?\n", + __FUNCTION__, np->full_name); + continue; + } + cbe_ptcal_enable_on_node(*nid, order); + found_mic = 1; + } + + return found_mic ? 0 : -ENODEV; +} + +static int cbe_ptcal_disable(void) +{ + struct ptcal_area *area, *tmp; + int ret = 0; + + pr_debug("%s: disabling PTCAL\n", __FUNCTION__); + + list_for_each_entry_safe(area, tmp, &ptcal_list, list) { + /* disable ptcal on this node */ + if (rtas_call(ptcal_stop_tok, 1, 1, NULL, area->nid)) { + printk(KERN_ERR "%s: error disabling PTCAL " + "on node %d!\n", __FUNCTION__, + area->nid); + ret = -EIO; + continue; + } + + /* ensure we can access the PTCAL area */ + memset(page_address(area->pages), 0, + 1 << (area->order + PAGE_SHIFT)); + + /* clean up */ + list_del(&area->list); + __free_pages(area->pages, area->order); + kfree(area); + } + + return ret; +} + +static int cbe_ptcal_notify_reboot(struct notifier_block *nb, + unsigned long code, void *data) +{ + return cbe_ptcal_disable(); +} + +static struct notifier_block cbe_ptcal_reboot_notifier = { + .notifier_call = cbe_ptcal_notify_reboot +}; + +int __init cbe_ptcal_init(void) +{ + int ret; + ptcal_start_tok = rtas_token("ibm,cbe-start-ptcal"); + ptcal_stop_tok = rtas_token("ibm,cbe-stop-ptcal"); + + if (ptcal_start_tok == RTAS_UNKNOWN_SERVICE + || ptcal_stop_tok == RTAS_UNKNOWN_SERVICE) + return -ENODEV; + + ret = register_reboot_notifier(&cbe_ptcal_reboot_notifier); + if (ret) { + printk(KERN_ERR "Can't disable PTCAL, so not enabling\n"); + return ret; + } + + return cbe_ptcal_enable(); +} + +arch_initcall(cbe_ptcal_init); + void __init cbe_ras_init(void) { unsigned long hid0; diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index 36989c2..54b9618 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -71,7 +71,7 @@ static void cell_show_cpuinfo(struct seq root = of_find_node_by_path("/"); if (root) - model = get_property(root, "model", NULL); + model = of_get_property(root, "model", NULL); seq_printf(m, "machine\t\t: CHRP %s\n", model); of_node_put(root); } @@ -190,15 +190,6 @@ static int __init cell_probe(void) return 1; } -/* - * Cell has no legacy IO; anything calling this function has to - * fail or bad things will happen - */ -static int cell_check_legacy_ioport(unsigned int baseport) -{ - return -ENODEV; -} - define_machine(cell) { .name = "Cell", .probe = cell_probe, @@ -211,7 +202,6 @@ define_machine(cell) { .get_rtc_time = rtas_get_rtc_time, .set_rtc_time = rtas_set_rtc_time, .calibrate_decr = generic_calibrate_decr, - .check_legacy_ioport = cell_check_legacy_ioport, .progress = cell_progress, .init_IRQ = cell_init_irq, .pci_setup_phb = rtas_setup_phb, diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index 21a9ebd..fb1f157 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -254,25 +254,25 @@ static unsigned int __init spider_find_c } /* Now do the horrible hacks */ - tmp = get_property(pic->of_node, "#interrupt-cells", NULL); + tmp = of_get_property(pic->of_node, "#interrupt-cells", NULL); if (tmp == NULL) return NO_IRQ; intsize = *tmp; - imap = get_property(pic->of_node, "interrupt-map", &imaplen); + imap = of_get_property(pic->of_node, "interrupt-map", &imaplen); if (imap == NULL || imaplen < (intsize + 1)) return NO_IRQ; iic = of_find_node_by_phandle(imap[intsize]); if (iic == NULL) return NO_IRQ; imap += intsize + 1; - tmp = get_property(iic, "#interrupt-cells", NULL); + tmp = of_get_property(iic, "#interrupt-cells", NULL); if (tmp == NULL) return NO_IRQ; intsize = *tmp; /* Assume unit is last entry of interrupt specifier */ unit = imap[intsize - 1]; /* Ok, we have a unit, now let's try to get the node */ - tmp = get_property(iic, "ibm,interrupt-server-ranges", NULL); + tmp = of_get_property(iic, "ibm,interrupt-server-ranges", NULL); if (tmp == NULL) { of_node_put(iic); return NO_IRQ; diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index eba7a26..fec5152 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -36,12 +36,14 @@ #include #include const struct spu_management_ops *spu_management_ops; +EXPORT_SYMBOL_GPL(spu_management_ops); + const struct spu_priv1_ops *spu_priv1_ops; static struct list_head spu_list[MAX_NUMNODES]; static LIST_HEAD(spu_full_list); static DEFINE_MUTEX(spu_mutex); -static spinlock_t spu_list_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(spu_list_lock); EXPORT_SYMBOL_GPL(spu_priv1_ops); @@ -290,7 +292,6 @@ spu_irq_class_1(int irq, void *data) return stat ? IRQ_HANDLED : IRQ_NONE; } -EXPORT_SYMBOL_GPL(spu_irq_class_1_bottom); static irqreturn_t spu_irq_class_2(int irq, void *data) @@ -431,10 +432,11 @@ struct spu *spu_alloc_node(int node) spu = list_entry(spu_list[node].next, struct spu, list); list_del_init(&spu->list); pr_debug("Got SPU %d %d\n", spu->number, spu->node); - spu_init_channels(spu); } mutex_unlock(&spu_mutex); + if (spu) + spu_init_channels(spu); return spu; } EXPORT_SYMBOL_GPL(spu_alloc_node); @@ -461,108 +463,6 @@ void spu_free(struct spu *spu) } EXPORT_SYMBOL_GPL(spu_free); -static int spu_handle_mm_fault(struct spu *spu) -{ - struct mm_struct *mm = spu->mm; - struct vm_area_struct *vma; - u64 ea, dsisr, is_write; - int ret; - - ea = spu->dar; - dsisr = spu->dsisr; -#if 0 - if (!IS_VALID_EA(ea)) { - return -EFAULT; - } -#endif /* XXX */ - if (mm == NULL) { - return -EFAULT; - } - if (mm->pgd == NULL) { - return -EFAULT; - } - - down_read(&mm->mmap_sem); - vma = find_vma(mm, ea); - if (!vma) - goto bad_area; - if (vma->vm_start <= ea) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; -#if 0 - if (expand_stack(vma, ea)) - goto bad_area; -#endif /* XXX */ -good_area: - is_write = dsisr & MFC_DSISR_ACCESS_PUT; - if (is_write) { - if (!(vma->vm_flags & VM_WRITE)) - goto bad_area; - } else { - if (dsisr & MFC_DSISR_ACCESS_DENIED) - goto bad_area; - if (!(vma->vm_flags & (VM_READ | VM_EXEC))) - goto bad_area; - } - ret = 0; - switch (handle_mm_fault(mm, vma, ea, is_write)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - ret = -EFAULT; - goto bad_area; - case VM_FAULT_OOM: - ret = -ENOMEM; - goto bad_area; - default: - BUG(); - } - up_read(&mm->mmap_sem); - return ret; - -bad_area: - up_read(&mm->mmap_sem); - return -EFAULT; -} - -int spu_irq_class_1_bottom(struct spu *spu) -{ - u64 ea, dsisr, access, error = 0UL; - int ret = 0; - - ea = spu->dar; - dsisr = spu->dsisr; - if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) { - u64 flags; - - access = (_PAGE_PRESENT | _PAGE_USER); - access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL; - local_irq_save(flags); - if (hash_page(ea, access, 0x300) != 0) - error |= CLASS1_ENABLE_STORAGE_FAULT_INTR; - local_irq_restore(flags); - } - if (error & CLASS1_ENABLE_STORAGE_FAULT_INTR) { - if ((ret = spu_handle_mm_fault(spu)) != 0) - error |= CLASS1_ENABLE_STORAGE_FAULT_INTR; - else - error &= ~CLASS1_ENABLE_STORAGE_FAULT_INTR; - } - spu->dar = 0UL; - spu->dsisr = 0UL; - if (!error) { - spu_restart_dma(spu); - } else { - spu->dma_callback(spu, SPE_EVENT_SPE_DATA_STORAGE); - } - return ret; -} - struct sysdev_class spu_sysdev_class = { set_kset_name("spu") }; @@ -636,12 +536,6 @@ static int spu_create_sysdev(struct spu return 0; } -static void spu_destroy_sysdev(struct spu *spu) -{ - sysfs_remove_device_from_node(&spu->sysdev, spu->node); - sysdev_unregister(&spu->sysdev); -} - static int __init create_spu(void *data) { struct spu *spu; @@ -693,58 +587,37 @@ out: return ret; } -static void destroy_spu(struct spu *spu) -{ - list_del_init(&spu->list); - list_del_init(&spu->full_list); - - spu_destroy_sysdev(spu); - spu_free_irqs(spu); - spu_destroy_spu(spu); - kfree(spu); -} - -static void cleanup_spu_base(void) -{ - struct spu *spu, *tmp; - int node; - - mutex_lock(&spu_mutex); - for (node = 0; node < MAX_NUMNODES; node++) { - list_for_each_entry_safe(spu, tmp, &spu_list[node], list) - destroy_spu(spu); - } - mutex_unlock(&spu_mutex); - sysdev_class_unregister(&spu_sysdev_class); -} -module_exit(cleanup_spu_base); - static int __init init_spu_base(void) { - int i, ret; + int i, ret = 0; + + for (i = 0; i < MAX_NUMNODES; i++) + INIT_LIST_HEAD(&spu_list[i]); if (!spu_management_ops) - return 0; + goto out; /* create sysdev class for spus */ ret = sysdev_class_register(&spu_sysdev_class); if (ret) - return ret; - - for (i = 0; i < MAX_NUMNODES; i++) - INIT_LIST_HEAD(&spu_list[i]); + goto out; ret = spu_enumerate_spus(create_spu); if (ret) { printk(KERN_WARNING "%s: Error initializing spus\n", __FUNCTION__); - cleanup_spu_base(); - return ret; + goto out_unregister_sysdev_class; } xmon_register_spus(&spu_full_list); + return 0; + + out_unregister_sysdev_class: + sysdev_class_unregister(&spu_sysdev_class); + out: + return ret; } module_init(init_spu_base); diff --git a/arch/powerpc/platforms/cell/spu_coredump.c b/arch/powerpc/platforms/cell/spu_coredump.c index 6915b41..4fd37ff 100644 --- a/arch/powerpc/platforms/cell/spu_coredump.c +++ b/arch/powerpc/platforms/cell/spu_coredump.c @@ -26,19 +26,18 @@ #include #include -static struct spu_coredump_calls spu_coredump_calls; +static struct spu_coredump_calls *spu_coredump_calls; static DEFINE_MUTEX(spu_coredump_mutex); int arch_notes_size(void) { long ret; - struct module *owner = spu_coredump_calls.owner; ret = -ENOSYS; mutex_lock(&spu_coredump_mutex); - if (owner && try_module_get(owner)) { - ret = spu_coredump_calls.arch_notes_size(); - module_put(owner); + if (spu_coredump_calls && try_module_get(spu_coredump_calls->owner)) { + ret = spu_coredump_calls->arch_notes_size(); + module_put(spu_coredump_calls->owner); } mutex_unlock(&spu_coredump_mutex); return ret; @@ -46,36 +45,35 @@ int arch_notes_size(void) void arch_write_notes(struct file *file) { - struct module *owner = spu_coredump_calls.owner; - mutex_lock(&spu_coredump_mutex); - if (owner && try_module_get(owner)) { - spu_coredump_calls.arch_write_notes(file); - module_put(owner); + if (spu_coredump_calls && try_module_get(spu_coredump_calls->owner)) { + spu_coredump_calls->arch_write_notes(file); + module_put(spu_coredump_calls->owner); } mutex_unlock(&spu_coredump_mutex); } int register_arch_coredump_calls(struct spu_coredump_calls *calls) { - if (spu_coredump_calls.owner) - return -EBUSY; + int ret = 0; + mutex_lock(&spu_coredump_mutex); - spu_coredump_calls.arch_notes_size = calls->arch_notes_size; - spu_coredump_calls.arch_write_notes = calls->arch_write_notes; - spu_coredump_calls.owner = calls->owner; + if (spu_coredump_calls) + ret = -EBUSY; + else + spu_coredump_calls = calls; mutex_unlock(&spu_coredump_mutex); - return 0; + return ret; } EXPORT_SYMBOL_GPL(register_arch_coredump_calls); void unregister_arch_coredump_calls(struct spu_coredump_calls *calls) { - BUG_ON(spu_coredump_calls.owner != calls->owner); + BUG_ON(spu_coredump_calls != calls); mutex_lock(&spu_coredump_mutex); - spu_coredump_calls.owner = NULL; + spu_coredump_calls = NULL; mutex_unlock(&spu_coredump_mutex); } EXPORT_SYMBOL_GPL(unregister_arch_coredump_calls); diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c index e34599f..1d4562a 100644 --- a/arch/powerpc/platforms/cell/spu_manage.c +++ b/arch/powerpc/platforms/cell/spu_manage.c @@ -48,11 +48,11 @@ static u64 __init find_spu_unit_number(s { const unsigned int *prop; int proplen; - prop = get_property(spe, "unit-id", &proplen); + prop = of_get_property(spe, "unit-id", &proplen); if (proplen == 4) return (u64)*prop; - prop = get_property(spe, "reg", &proplen); + prop = of_get_property(spe, "reg", &proplen); if (proplen == 4) return (u64)*prop; @@ -76,12 +76,12 @@ static int __init spu_map_interrupts_old int nid; /* Get the interrupt source unit from the device-tree */ - tmp = get_property(np, "isrc", NULL); + tmp = of_get_property(np, "isrc", NULL); if (!tmp) return -ENODEV; isrc = tmp[0]; - tmp = get_property(np->parent->parent, "node-id", NULL); + tmp = of_get_property(np->parent->parent, "node-id", NULL); if (!tmp) { printk(KERN_WARNING "%s: can't find node-id\n", __FUNCTION__); nid = spu->node; @@ -110,7 +110,7 @@ static void __iomem * __init spu_map_pro } __attribute__((packed)) *prop; int proplen; - prop = get_property(n, name, &proplen); + prop = of_get_property(n, name, &proplen); if (prop == NULL || proplen != sizeof (struct address_prop)) return NULL; @@ -124,11 +124,11 @@ static int __init spu_map_device_old(str int ret; ret = -ENODEV; - spu->name = get_property(node, "name", NULL); + spu->name = of_get_property(node, "name", NULL); if (!spu->name) goto out; - prop = get_property(node, "local-store", NULL); + prop = of_get_property(node, "local-store", NULL); if (!prop) goto out; spu->local_store_phys = *(unsigned long *)prop; @@ -139,7 +139,7 @@ static int __init spu_map_device_old(str if (!spu->local_store) goto out; - prop = get_property(node, "problem", NULL); + prop = of_get_property(node, "problem", NULL); if (!prop) goto out_unmap; spu->problem_phys = *(unsigned long *)prop; @@ -226,7 +226,7 @@ static int __init spu_map_device(struct struct device_node *np = spu->devnode; int ret = -ENODEV; - spu->name = get_property(np, "name", NULL); + spu->name = of_get_property(np, "name", NULL); if (!spu->name) goto out; diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile index 472217d..2cd89c1 100644 --- a/arch/powerpc/platforms/cell/spufs/Makefile +++ b/arch/powerpc/platforms/cell/spufs/Makefile @@ -1,4 +1,4 @@ -obj-y += switch.o +obj-y += switch.o fault.o obj-$(CONFIG_SPU_FS) += spufs.o spufs-y += inode.o file.o context.o syscalls.o coredump.o diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index 1898f0d..3322528 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -350,6 +350,11 @@ static int spu_backing_send_mfc_command( return ret; } +static void spu_backing_restart_dma(struct spu_context *ctx) +{ + /* nothing to do here */ +} + struct spu_context_ops spu_backing_ops = { .mbox_read = spu_backing_mbox_read, .mbox_stat_read = spu_backing_mbox_stat_read, @@ -376,4 +381,5 @@ struct spu_context_ops spu_backing_ops = .read_mfc_tagstatus = spu_backing_read_mfc_tagstatus, .get_mfc_free_elements = spu_backing_get_mfc_free_elements, .send_mfc_command = spu_backing_send_mfc_command, + .restart_dma = spu_backing_restart_dma, }; diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 04ad2e3..a87d9ca 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -41,9 +41,10 @@ struct spu_context *alloc_spu_context(st goto out_free; } spin_lock_init(&ctx->mmio_lock); + spin_lock_init(&ctx->mapping_lock); kref_init(&ctx->kref); mutex_init(&ctx->state_mutex); - init_MUTEX(&ctx->run_sema); + mutex_init(&ctx->run_mutex); init_waitqueue_head(&ctx->ibox_wq); init_waitqueue_head(&ctx->wbox_wq); init_waitqueue_head(&ctx->stop_wq); @@ -51,6 +52,7 @@ struct spu_context *alloc_spu_context(st ctx->state = SPU_STATE_SAVED; ctx->ops = &spu_backing_ops; ctx->owner = get_task_mm(current); + INIT_LIST_HEAD(&ctx->rq); if (gang) spu_gang_add_ctx(gang, ctx); ctx->rt_priority = current->rt_priority; @@ -75,6 +77,7 @@ void destroy_spu_context(struct kref *kr spu_fini_csa(&ctx->csa); if (ctx->gang) spu_gang_remove_ctx(ctx->gang, ctx); + BUG_ON(!list_empty(&ctx->rq)); kfree(ctx); } @@ -119,46 +122,6 @@ void spu_unmap_mappings(struct spu_conte } /** - * spu_acquire_exclusive - lock spu contex and protect against userspace access - * @ctx: spu contex to lock - * - * Note: - * Returns 0 and with the context locked on success - * Returns negative error and with the context _unlocked_ on failure. - */ -int spu_acquire_exclusive(struct spu_context *ctx) -{ - int ret = -EINVAL; - - spu_acquire(ctx); - /* - * Context is about to be freed, so we can't acquire it anymore. - */ - if (!ctx->owner) - goto out_unlock; - - if (ctx->state == SPU_STATE_SAVED) { - ret = spu_activate(ctx, 0); - if (ret) - goto out_unlock; - } else { - /* - * We need to exclude userspace access to the context. - * - * To protect against memory access we invalidate all ptes - * and make sure the pagefault handlers block on the mutex. - */ - spu_unmap_mappings(ctx); - } - - return 0; - - out_unlock: - spu_release(ctx); - return ret; -} - -/** * spu_acquire_runnable - lock spu contex and make sure it is in runnable state * @ctx: spu contex to lock * diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c index 725e195..5d9ad5a 100644 --- a/arch/powerpc/platforms/cell/spufs/coredump.c +++ b/arch/powerpc/platforms/cell/spufs/coredump.c @@ -169,12 +169,12 @@ static void spufs_arch_write_note(struct struct spu_context *ctx; loff_t pos = 0; int sz, dfd, rc, total = 0; - const int bufsz = 4096; + const int bufsz = PAGE_SIZE; char *name; char fullname[80], *buf; struct elf_note en; - buf = kmalloc(bufsz, GFP_KERNEL); + buf = (void *)get_zeroed_page(GFP_KERNEL); if (!buf) return; @@ -187,9 +187,8 @@ static void spufs_arch_write_note(struct sz = spufs_coredump_read[i].size; ctx = ctx_info->ctx; - if (!ctx) { - return; - } + if (!ctx) + goto out; sprintf(fullname, "SPU/%d/%s", dfd, name); en.n_namesz = strlen(fullname) + 1; @@ -197,23 +196,25 @@ static void spufs_arch_write_note(struct en.n_type = NT_SPU; if (!spufs_dump_write(file, &en, sizeof(en))) - return; + goto out; if (!spufs_dump_write(file, fullname, en.n_namesz)) - return; + goto out; if (!spufs_dump_seek(file, roundup((unsigned long)file->f_pos, 4))) - return; + goto out; do { rc = do_coredump_read(i, ctx, buf, bufsz, &pos); if (rc > 0) { if (!spufs_dump_write(file, buf, rc)) - return; + goto out; total += rc; } } while (rc == bufsz && total < sz); spufs_dump_seek(file, roundup((unsigned long)file->f_pos - total + sz, 4)); +out: + free_page((unsigned long)buf); } static void spufs_arch_write_notes(struct file *file) diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c new file mode 100644 index 0000000..0f75c07 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -0,0 +1,211 @@ +/* + * Low-level SPU handling + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include + +#include +#include + +#include "spufs.h" + +/* + * This ought to be kept in sync with the powerpc specific do_page_fault + * function. Currently, there are a few corner cases that we haven't had + * to handle fortunately. + */ +static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea, unsigned long dsisr) +{ + struct vm_area_struct *vma; + unsigned long is_write; + int ret; + +#if 0 + if (!IS_VALID_EA(ea)) { + return -EFAULT; + } +#endif /* XXX */ + if (mm == NULL) { + return -EFAULT; + } + if (mm->pgd == NULL) { + return -EFAULT; + } + + down_read(&mm->mmap_sem); + vma = find_vma(mm, ea); + if (!vma) + goto bad_area; + if (vma->vm_start <= ea) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (expand_stack(vma, ea)) + goto bad_area; +good_area: + is_write = dsisr & MFC_DSISR_ACCESS_PUT; + if (is_write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } else { + if (dsisr & MFC_DSISR_ACCESS_DENIED) + goto bad_area; + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + ret = 0; + switch (handle_mm_fault(mm, vma, ea, is_write)) { + case VM_FAULT_MINOR: + current->min_flt++; + break; + case VM_FAULT_MAJOR: + current->maj_flt++; + break; + case VM_FAULT_SIGBUS: + ret = -EFAULT; + goto bad_area; + case VM_FAULT_OOM: + ret = -ENOMEM; + goto bad_area; + default: + BUG(); + } + up_read(&mm->mmap_sem); + return ret; + +bad_area: + up_read(&mm->mmap_sem); + return -EFAULT; +} + +static void spufs_handle_dma_error(struct spu_context *ctx, + unsigned long ea, int type) +{ + if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) { + ctx->event_return |= type; + wake_up_all(&ctx->stop_wq); + } else { + siginfo_t info; + memset(&info, 0, sizeof(info)); + + switch (type) { + case SPE_EVENT_INVALID_DMA: + info.si_signo = SIGBUS; + info.si_code = BUS_OBJERR; + break; + case SPE_EVENT_SPE_DATA_STORAGE: + info.si_signo = SIGBUS; + info.si_addr = (void __user *)ea; + info.si_code = BUS_ADRERR; + break; + case SPE_EVENT_DMA_ALIGNMENT: + info.si_signo = SIGBUS; + /* DAR isn't set for an alignment fault :( */ + info.si_code = BUS_ADRALN; + break; + case SPE_EVENT_SPE_ERROR: + info.si_signo = SIGILL; + info.si_addr = (void __user *)(unsigned long) + ctx->ops->npc_read(ctx) - 4; + info.si_code = ILL_ILLOPC; + break; + } + if (info.si_signo) + force_sig_info(info.si_signo, &info, current); + } +} + +void spufs_dma_callback(struct spu *spu, int type) +{ + spufs_handle_dma_error(spu->ctx, spu->dar, type); +} +EXPORT_SYMBOL_GPL(spufs_dma_callback); + +/* + * bottom half handler for page faults, we can't do this from + * interrupt context, since we might need to sleep. + * we also need to give up the mutex so we can get scheduled + * out while waiting for the backing store. + * + * TODO: try calling hash_page from the interrupt handler first + * in order to speed up the easy case. + */ +int spufs_handle_class1(struct spu_context *ctx) +{ + u64 ea, dsisr, access; + unsigned long flags; + int ret; + + /* + * dar and dsisr get passed from the registers + * to the spu_context, to this function, but not + * back to the spu if it gets scheduled again. + * + * if we don't handle the fault for a saved context + * in time, we can still expect to get the same fault + * the immediately after the context restore. + */ + if (ctx->state == SPU_STATE_RUNNABLE) { + ea = ctx->spu->dar; + dsisr = ctx->spu->dsisr; + ctx->spu->dar= ctx->spu->dsisr = 0; + } else { + ea = ctx->csa.priv1.mfc_dar_RW; + dsisr = ctx->csa.priv1.mfc_dsisr_RW; + ctx->csa.priv1.mfc_dar_RW = 0; + ctx->csa.priv1.mfc_dsisr_RW = 0; + } + + if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) + return 0; + + pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea, + dsisr, ctx->state); + + /* we must not hold the lock when entering spu_handle_mm_fault */ + spu_release(ctx); + + access = (_PAGE_PRESENT | _PAGE_USER); + access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL; + local_irq_save(flags); + ret = hash_page(ea, access, 0x300); + local_irq_restore(flags); + + /* hashing failed, so try the actual fault handler */ + if (ret) + ret = spu_handle_mm_fault(current->mm, ea, dsisr); + + spu_acquire(ctx); + /* + * If we handled the fault successfully and are in runnable + * state, restart the DMA. + * In case of unhandled error report the problem to user space. + */ + if (!ret) { + if (ctx->spu) + ctx->ops->restart_dma(ctx); + } else + spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); + + return ret; +} +EXPORT_SYMBOL_GPL(spufs_handle_class1); diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 505266a..d010b24 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -44,9 +44,25 @@ spufs_mem_open(struct inode *inode, stru { struct spufs_inode_info *i = SPUFS_I(inode); struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); file->private_data = ctx; - ctx->local_store = inode->i_mapping; - smp_wmb(); + if (!i->i_openers++) + ctx->local_store = inode->i_mapping; + spin_unlock(&ctx->mapping_lock); + return 0; +} + +static int +spufs_mem_release(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); + if (!--i->i_openers) + ctx->local_store = NULL; + spin_unlock(&ctx->mapping_lock); return 0; } @@ -149,6 +165,7 @@ spufs_mem_mmap(struct file *file, struct static const struct file_operations spufs_mem_fops = { .open = spufs_mem_open, + .release = spufs_mem_release, .read = spufs_mem_read, .write = spufs_mem_write, .llseek = generic_file_llseek, @@ -238,16 +255,33 @@ static int spufs_cntl_open(struct inode struct spufs_inode_info *i = SPUFS_I(inode); struct spu_context *ctx = i->i_ctx; + spin_lock(&ctx->mapping_lock); file->private_data = ctx; - ctx->cntl = inode->i_mapping; - smp_wmb(); + if (!i->i_openers++) + ctx->cntl = inode->i_mapping; + spin_unlock(&ctx->mapping_lock); return simple_attr_open(inode, file, spufs_cntl_get, spufs_cntl_set, "0x%08lx"); } +static int +spufs_cntl_release(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + simple_attr_close(inode, file); + + spin_lock(&ctx->mapping_lock); + if (!--i->i_openers) + ctx->cntl = NULL; + spin_unlock(&ctx->mapping_lock); + return 0; +} + static const struct file_operations spufs_cntl_fops = { .open = spufs_cntl_open, - .release = simple_attr_close, + .release = spufs_cntl_release, .read = simple_attr_read, .write = simple_attr_write, .mmap = spufs_cntl_mmap, @@ -723,12 +757,28 @@ static int spufs_signal1_open(struct ino { struct spufs_inode_info *i = SPUFS_I(inode); struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); file->private_data = ctx; - ctx->signal1 = inode->i_mapping; - smp_wmb(); + if (!i->i_openers++) + ctx->signal1 = inode->i_mapping; + spin_unlock(&ctx->mapping_lock); return nonseekable_open(inode, file); } +static int +spufs_signal1_release(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); + if (!--i->i_openers) + ctx->signal1 = NULL; + spin_unlock(&ctx->mapping_lock); + return 0; +} + static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf, size_t len, loff_t *pos) { @@ -821,6 +871,7 @@ static int spufs_signal1_mmap(struct fil static const struct file_operations spufs_signal1_fops = { .open = spufs_signal1_open, + .release = spufs_signal1_release, .read = spufs_signal1_read, .write = spufs_signal1_write, .mmap = spufs_signal1_mmap, @@ -830,12 +881,28 @@ static int spufs_signal2_open(struct ino { struct spufs_inode_info *i = SPUFS_I(inode); struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); file->private_data = ctx; - ctx->signal2 = inode->i_mapping; - smp_wmb(); + if (!i->i_openers++) + ctx->signal2 = inode->i_mapping; + spin_unlock(&ctx->mapping_lock); return nonseekable_open(inode, file); } +static int +spufs_signal2_release(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); + if (!--i->i_openers) + ctx->signal2 = NULL; + spin_unlock(&ctx->mapping_lock); + return 0; +} + static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf, size_t len, loff_t *pos) { @@ -932,6 +999,7 @@ #endif /* !SPUFS_MMAP_4K */ static const struct file_operations spufs_signal2_fops = { .open = spufs_signal2_open, + .release = spufs_signal2_release, .read = spufs_signal2_read, .write = spufs_signal2_write, .mmap = spufs_signal2_mmap, @@ -1031,13 +1099,30 @@ static int spufs_mss_open(struct inode * struct spu_context *ctx = i->i_ctx; file->private_data = i->i_ctx; - ctx->mss = inode->i_mapping; - smp_wmb(); + + spin_lock(&ctx->mapping_lock); + if (!i->i_openers++) + ctx->mss = inode->i_mapping; + spin_unlock(&ctx->mapping_lock); return nonseekable_open(inode, file); } +static int +spufs_mss_release(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); + if (!--i->i_openers) + ctx->mss = NULL; + spin_unlock(&ctx->mapping_lock); + return 0; +} + static const struct file_operations spufs_mss_fops = { .open = spufs_mss_open, + .release = spufs_mss_release, .mmap = spufs_mss_mmap, }; @@ -1072,14 +1157,30 @@ static int spufs_psmap_open(struct inode struct spufs_inode_info *i = SPUFS_I(inode); struct spu_context *ctx = i->i_ctx; + spin_lock(&ctx->mapping_lock); file->private_data = i->i_ctx; - ctx->psmap = inode->i_mapping; - smp_wmb(); + if (!i->i_openers++) + ctx->psmap = inode->i_mapping; + spin_unlock(&ctx->mapping_lock); return nonseekable_open(inode, file); } +static int +spufs_psmap_release(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); + if (!--i->i_openers) + ctx->psmap = NULL; + spin_unlock(&ctx->mapping_lock); + return 0; +} + static const struct file_operations spufs_psmap_fops = { .open = spufs_psmap_open, + .release = spufs_psmap_release, .mmap = spufs_psmap_mmap, }; @@ -1126,12 +1227,27 @@ static int spufs_mfc_open(struct inode * if (atomic_read(&inode->i_count) != 1) return -EBUSY; + spin_lock(&ctx->mapping_lock); file->private_data = ctx; - ctx->mfc = inode->i_mapping; - smp_wmb(); + if (!i->i_openers++) + ctx->mfc = inode->i_mapping; + spin_unlock(&ctx->mapping_lock); return nonseekable_open(inode, file); } +static int +spufs_mfc_release(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + spin_lock(&ctx->mapping_lock); + if (!--i->i_openers) + ctx->mfc = NULL; + spin_unlock(&ctx->mapping_lock); + return 0; +} + /* interrupt-level mfc callback function. */ void spufs_mfc_callback(struct spu *spu) { @@ -1313,7 +1429,10 @@ static ssize_t spufs_mfc_write(struct fi if (ret) goto out; - spu_acquire_runnable(ctx, 0); + ret = spu_acquire_runnable(ctx, 0); + if (ret) + goto out; + if (file->f_flags & O_NONBLOCK) { ret = ctx->ops->send_mfc_command(ctx, &cmd); } else { @@ -1399,6 +1518,7 @@ static int spufs_mfc_fasync(int fd, stru static const struct file_operations spufs_mfc_fops = { .open = spufs_mfc_open, + .release = spufs_mfc_release, .read = spufs_mfc_read, .write = spufs_mfc_write, .poll = spufs_mfc_poll, diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c index ae42e03..428875c 100644 --- a/arch/powerpc/platforms/cell/spufs/hw_ops.c +++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c @@ -296,6 +296,14 @@ static int spu_hw_send_mfc_command(struc } } +static void spu_hw_restart_dma(struct spu_context *ctx) +{ + struct spu_priv2 __iomem *priv2 = ctx->spu->priv2; + + if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &ctx->spu->flags)) + out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND); +} + struct spu_context_ops spu_hw_ops = { .mbox_read = spu_hw_mbox_read, .mbox_stat_read = spu_hw_mbox_stat_read, @@ -320,4 +328,5 @@ struct spu_context_ops spu_hw_ops = { .read_mfc_tagstatus = spu_hw_read_mfc_tagstatus, .get_mfc_free_elements = spu_hw_get_mfc_free_elements, .send_mfc_command = spu_hw_send_mfc_command, + .restart_dma = spu_hw_restart_dma, }; diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 8079983..13e4f70 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -36,6 +36,7 @@ #include #include #include #include +#include #include #include "spufs.h" @@ -54,6 +55,7 @@ spufs_alloc_inode(struct super_block *sb ei->i_gang = NULL; ei->i_ctx = NULL; + ei->i_openers = 0; return &ei->vfs_inode; } @@ -520,13 +522,14 @@ out: /* File system initialization */ enum { - Opt_uid, Opt_gid, Opt_err, + Opt_uid, Opt_gid, Opt_mode, Opt_err, }; static match_table_t spufs_tokens = { - { Opt_uid, "uid=%d" }, - { Opt_gid, "gid=%d" }, - { Opt_err, NULL }, + { Opt_uid, "uid=%d" }, + { Opt_gid, "gid=%d" }, + { Opt_mode, "mode=%o" }, + { Opt_err, NULL }, }; static int @@ -553,6 +556,11 @@ spufs_parse_options(char *options, struc return 0; root->i_gid = option; break; + case Opt_mode: + if (match_octal(&args[0], &option)) + return 0; + root->i_mode = option | S_IFDIR; + break; default: return 0; } @@ -560,6 +568,11 @@ spufs_parse_options(char *options, struc return 1; } +static void spufs_exit_isolated_loader(void) +{ + kfree(isolated_loader); +} + static void spufs_init_isolated_loader(void) { @@ -571,7 +584,7 @@ spufs_init_isolated_loader(void) if (!dn) return; - loader = get_property(dn, "loader", &size); + loader = of_get_property(dn, "loader", &size); if (!loader) return; @@ -653,6 +666,10 @@ static int __init spufs_init(void) { int ret; + ret = -ENODEV; + if (!spu_management_ops) + goto out; + ret = -ENOMEM; spufs_inode_cache = kmem_cache_create("spufs_inode_cache", sizeof(struct spufs_inode_info), 0, @@ -660,25 +677,29 @@ static int __init spufs_init(void) if (!spufs_inode_cache) goto out; - if (spu_sched_init() != 0) { - kmem_cache_destroy(spufs_inode_cache); - goto out; - } - ret = register_filesystem(&spufs_type); + ret = spu_sched_init(); if (ret) goto out_cache; + ret = register_filesystem(&spufs_type); + if (ret) + goto out_sched; ret = register_spu_syscalls(&spufs_calls); if (ret) goto out_fs; ret = register_arch_coredump_calls(&spufs_coredump_calls); if (ret) - goto out_fs; + goto out_syscalls; spufs_init_isolated_loader(); return 0; + +out_syscalls: + unregister_spu_syscalls(&spufs_calls); out_fs: unregister_filesystem(&spufs_type); +out_sched: + spu_sched_exit(); out_cache: kmem_cache_destroy(spufs_inode_cache); out: @@ -689,6 +710,7 @@ module_init(spufs_init); static void __exit spufs_exit(void) { spu_sched_exit(); + spufs_exit_isolated_loader(); unregister_arch_coredump_calls(&spufs_coredump_calls); unregister_spu_syscalls(&spufs_calls); unregister_filesystem(&spufs_type); diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index f95a611..5762660 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -18,27 +18,6 @@ void spufs_stop_callback(struct spu *spu wake_up_all(&ctx->stop_wq); } -void spufs_dma_callback(struct spu *spu, int type) -{ - struct spu_context *ctx = spu->ctx; - - if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) { - ctx->event_return |= type; - wake_up_all(&ctx->stop_wq); - } else { - switch (type) { - case SPE_EVENT_DMA_ALIGNMENT: - case SPE_EVENT_SPE_DATA_STORAGE: - case SPE_EVENT_INVALID_DMA: - force_sig(SIGBUS, /* info, */ current); - break; - case SPE_EVENT_SPE_ERROR: - force_sig(SIGILL, /* info */ current); - break; - } - } -} - static inline int spu_stopped(struct spu_context *ctx, u32 * stat) { struct spu *spu; @@ -63,13 +42,18 @@ static int spu_setup_isolated(struct spu const u32 status_loading = SPU_STATUS_RUNNING | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; + ret = -ENODEV; if (!isolated_loader) - return -ENODEV; - - ret = spu_acquire_exclusive(ctx); - if (ret) goto out; + /* + * We need to exclude userspace access to the context. + * + * To protect against memory access we invalidate all ptes + * and make sure the pagefault handlers block on the mutex. + */ + spu_unmap_mappings(ctx); + mfc_cntl = &ctx->spu->priv2->mfc_control_RW; /* purge the MFC DMA queue to ensure no spurious accesses before we @@ -82,7 +66,7 @@ static int spu_setup_isolated(struct spu printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", __FUNCTION__); ret = -EIO; - goto out_unlock; + goto out; } cond_resched(); } @@ -119,12 +103,15 @@ static int spu_setup_isolated(struct spu pr_debug("%s: isolated LOAD failed\n", __FUNCTION__); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); ret = -EACCES; + goto out_drop_priv; + } - } else if (!(status & SPU_STATUS_ISOLATED_STATE)) { + if (!(status & SPU_STATUS_ISOLATED_STATE)) { /* This isn't allowed by the CBEA, but check anyway */ pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); ret = -EINVAL; + goto out_drop_priv; } out_drop_priv: @@ -132,30 +119,19 @@ out_drop_priv: sr1 |= MFC_STATE1_PROBLEM_STATE_MASK; spu_mfc_sr1_set(ctx->spu, sr1); -out_unlock: - spu_release(ctx); out: return ret; } -static inline int spu_run_init(struct spu_context *ctx, u32 * npc) +static int spu_run_init(struct spu_context *ctx, u32 * npc) { - int ret; - unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; - - ret = spu_acquire_runnable(ctx, 0); - if (ret) - return ret; - if (ctx->flags & SPU_CREATE_ISOLATE) { + unsigned long runcntl; + if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) { - /* Need to release ctx, because spu_setup_isolated will - * acquire it exclusively. - */ - spu_release(ctx); - ret = spu_setup_isolated(ctx); - if (!ret) - ret = spu_acquire_runnable(ctx, 0); + int ret = spu_setup_isolated(ctx); + if (ret) + return ret; } /* if userspace has set the runcntrl register (eg, to issue an @@ -164,16 +140,17 @@ static inline int spu_run_init(struct sp (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); if (runcntl == 0) runcntl = SPU_RUNCNTL_RUNNABLE; + ctx->ops->runcntl_write(ctx, runcntl); } else { spu_start_tick(ctx); ctx->ops->npc_write(ctx, *npc); + ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); } - ctx->ops->runcntl_write(ctx, runcntl); - return ret; + return 0; } -static inline int spu_run_fini(struct spu_context *ctx, u32 * npc, +static int spu_run_fini(struct spu_context *ctx, u32 * npc, u32 * status) { int ret = 0; @@ -189,19 +166,27 @@ static inline int spu_run_fini(struct sp return ret; } -static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc, +static int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc, u32 *status) { int ret; - if ((ret = spu_run_fini(ctx, npc, status)) != 0) + ret = spu_run_fini(ctx, npc, status); + if (ret) return ret; - if (*status & (SPU_STATUS_STOPPED_BY_STOP | - SPU_STATUS_STOPPED_BY_HALT)) { + + if (*status & (SPU_STATUS_STOPPED_BY_STOP | SPU_STATUS_STOPPED_BY_HALT)) return *status; - } - if ((ret = spu_run_init(ctx, npc)) != 0) + + ret = spu_acquire_runnable(ctx, 0); + if (ret) + return ret; + + ret = spu_run_init(ctx, npc); + if (ret) { + spu_release(ctx); return ret; + } return 0; } @@ -253,17 +238,17 @@ int spu_process_callback(struct spu_cont { struct spu_syscall_block s; u32 ls_pointer, npc; - char *ls; + void __iomem *ls; long spu_ret; int ret; /* get syscall block from local store */ - npc = ctx->ops->npc_read(ctx); - ls = ctx->ops->get_ls(ctx); - ls_pointer = *(u32*)(ls + npc); + npc = ctx->ops->npc_read(ctx) & ~3; + ls = (void __iomem *)ctx->ops->get_ls(ctx); + ls_pointer = in_be32(ls + npc); if (ls_pointer > (LS_SIZE - sizeof(s))) return -EFAULT; - memcpy(&s, ls + ls_pointer, sizeof (s)); + memcpy_fromio(&s, ls + ls_pointer, sizeof(s)); /* do actual syscall without pinning the spu */ ret = 0; @@ -283,7 +268,7 @@ int spu_process_callback(struct spu_cont } /* write result, jump over indirect pointer */ - memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret)); + memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret)); ctx->ops->npc_write(ctx, npc); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); return ret; @@ -292,11 +277,8 @@ int spu_process_callback(struct spu_cont static inline int spu_process_events(struct spu_context *ctx) { struct spu *spu = ctx->spu; - u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED; int ret = 0; - if (spu->dsisr & pte_fault) - ret = spu_irq_class_1_bottom(spu); if (spu->class_0_pending) ret = spu_irq_class_0_bottom(spu); if (!ret && signal_pending(current)) @@ -310,14 +292,21 @@ long spufs_run_spu(struct file *file, st int ret; u32 status; - if (down_interruptible(&ctx->run_sema)) + if (mutex_lock_interruptible(&ctx->run_mutex)) return -ERESTARTSYS; ctx->ops->master_start(ctx); ctx->event_return = 0; - ret = spu_run_init(ctx, npc); + + ret = spu_acquire_runnable(ctx, 0); if (ret) + return ret; + + ret = spu_run_init(ctx, npc); + if (ret) { + spu_release(ctx); goto out; + } do { ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); @@ -330,6 +319,10 @@ long spufs_run_spu(struct file *file, st break; status &= ~SPU_STATUS_STOPPED_BY_STOP; } + ret = spufs_handle_class1(ctx); + if (ret) + break; + if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { ret = spu_reacquire_runnable(ctx, npc, &status); if (ret) { @@ -363,6 +356,6 @@ out2: out: *event = ctx->event_return; - up(&ctx->run_sema); + mutex_unlock(&ctx->run_mutex); return ret; } diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 39823ce..91030b8 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -71,14 +71,27 @@ static inline int node_allowed(int node) void spu_start_tick(struct spu_context *ctx) { - if (ctx->policy == SCHED_RR) + if (ctx->policy == SCHED_RR) { + /* + * Make sure the exiting bit is cleared. + */ + clear_bit(SPU_SCHED_EXITING, &ctx->sched_flags); + mb(); queue_delayed_work(spu_sched_wq, &ctx->sched_work, SPU_TIMESLICE); + } } void spu_stop_tick(struct spu_context *ctx) { - if (ctx->policy == SCHED_RR) + if (ctx->policy == SCHED_RR) { + /* + * While the work can be rearming normally setting this flag + * makes sure it does not rearm itself anymore. + */ + set_bit(SPU_SCHED_EXITING, &ctx->sched_flags); + mb(); cancel_delayed_work(&ctx->sched_work); + } } void spu_sched_tick(struct work_struct *work) @@ -86,7 +99,15 @@ void spu_sched_tick(struct work_struct * struct spu_context *ctx = container_of(work, struct spu_context, sched_work.work); struct spu *spu; - int rearm = 1; + int preempted = 0; + + /* + * If this context is being stopped avoid rescheduling from the + * scheduler tick because we would block on the state_mutex. + * The caller will yield the spu later on anyway. + */ + if (test_bit(SPU_SCHED_EXITING, &ctx->sched_flags)) + return; mutex_lock(&ctx->state_mutex); spu = ctx->spu; @@ -94,12 +115,19 @@ void spu_sched_tick(struct work_struct * int best = sched_find_first_bit(spu_prio->bitmap); if (best <= ctx->prio) { spu_deactivate(ctx); - rearm = 0; + preempted = 1; } } mutex_unlock(&ctx->state_mutex); - if (rearm) + if (preempted) { + /* + * We need to break out of the wait loop in spu_run manually + * to ensure this context gets put on the runqueue again + * ASAP. + */ + wake_up(&ctx->stop_wq); + } else spu_start_tick(ctx); } @@ -208,58 +236,40 @@ static void spu_unbind_context(struct sp * spu_add_to_rq - add a context to the runqueue * @ctx: context to add */ -static void spu_add_to_rq(struct spu_context *ctx) +static void __spu_add_to_rq(struct spu_context *ctx) { - spin_lock(&spu_prio->runq_lock); - list_add_tail(&ctx->rq, &spu_prio->runq[ctx->prio]); - set_bit(ctx->prio, spu_prio->bitmap); - spin_unlock(&spu_prio->runq_lock); -} + int prio = ctx->prio; -/** - * spu_del_from_rq - remove a context from the runqueue - * @ctx: context to remove - */ -static void spu_del_from_rq(struct spu_context *ctx) -{ - spin_lock(&spu_prio->runq_lock); - list_del_init(&ctx->rq); - if (list_empty(&spu_prio->runq[ctx->prio])) - clear_bit(ctx->prio, spu_prio->bitmap); - spin_unlock(&spu_prio->runq_lock); + list_add_tail(&ctx->rq, &spu_prio->runq[prio]); + set_bit(prio, spu_prio->bitmap); } -/** - * spu_grab_context - remove one context from the runqueue - * @prio: priority of the context to be removed - * - * This function removes one context from the runqueue for priority @prio. - * If there is more than one context with the given priority the first - * task on the runqueue will be taken. - * - * Returns the spu_context it just removed. - * - * Must be called with spu_prio->runq_lock held. - */ -static struct spu_context *spu_grab_context(int prio) +static void __spu_del_from_rq(struct spu_context *ctx) { - struct list_head *rq = &spu_prio->runq[prio]; + int prio = ctx->prio; - if (list_empty(rq)) - return NULL; - return list_entry(rq->next, struct spu_context, rq); + if (!list_empty(&ctx->rq)) + list_del_init(&ctx->rq); + if (list_empty(&spu_prio->runq[prio])) + clear_bit(prio, spu_prio->bitmap); } static void spu_prio_wait(struct spu_context *ctx) { DEFINE_WAIT(wait); + spin_lock(&spu_prio->runq_lock); prepare_to_wait_exclusive(&ctx->stop_wq, &wait, TASK_INTERRUPTIBLE); if (!signal_pending(current)) { + __spu_add_to_rq(ctx); + spin_unlock(&spu_prio->runq_lock); mutex_unlock(&ctx->state_mutex); schedule(); mutex_lock(&ctx->state_mutex); + spin_lock(&spu_prio->runq_lock); + __spu_del_from_rq(ctx); } + spin_unlock(&spu_prio->runq_lock); __set_current_state(TASK_RUNNING); remove_wait_queue(&ctx->stop_wq, &wait); } @@ -280,9 +290,14 @@ static void spu_reschedule(struct spu *s spin_lock(&spu_prio->runq_lock); best = sched_find_first_bit(spu_prio->bitmap); if (best < MAX_PRIO) { - struct spu_context *ctx = spu_grab_context(best); - if (ctx) - wake_up(&ctx->stop_wq); + struct list_head *rq = &spu_prio->runq[best]; + struct spu_context *ctx; + + BUG_ON(list_empty(rq)); + + ctx = list_entry(rq->next, struct spu_context, rq); + __spu_del_from_rq(ctx); + wake_up(&ctx->stop_wq); } spin_unlock(&spu_prio->runq_lock); } @@ -365,6 +380,12 @@ static struct spu *find_victim(struct sp } spu_unbind_context(spu, victim); mutex_unlock(&victim->state_mutex); + /* + * We need to break out of the wait loop in spu_run + * manually to ensure this context gets put on the + * runqueue again ASAP. + */ + wake_up(&victim->stop_wq); return spu; } } @@ -377,7 +398,7 @@ static struct spu *find_victim(struct sp * @ctx: spu context to schedule * @flags: flags (currently ignored) * - * Tries to find a free spu to run @ctx. If no free spu is availble + * Tries to find a free spu to run @ctx. If no free spu is available * add the context to the runqueue so it gets woken up once an spu * is available. */ @@ -402,9 +423,7 @@ int spu_activate(struct spu_context *ctx return 0; } - spu_add_to_rq(ctx); spu_prio_wait(ctx); - spu_del_from_rq(ctx); } while (!signal_pending(current)); return -ERESTARTSYS; @@ -438,7 +457,6 @@ void spu_deactivate(struct spu_context * void spu_yield(struct spu_context *ctx) { struct spu *spu; - int need_yield = 0; if (mutex_trylock(&ctx->state_mutex)) { if ((spu = ctx->spu) != NULL) { @@ -447,13 +465,10 @@ void spu_yield(struct spu_context *ctx) pr_debug("%s: yielding SPU %d NODE %d\n", __FUNCTION__, spu->number, spu->node); spu_deactivate(ctx); - need_yield = 1; } } mutex_unlock(&ctx->state_mutex); } - if (unlikely(need_yield)) - yield(); } int __init spu_sched_init(void) diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 5c4e47d..0a947fd 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -41,7 +41,7 @@ struct spu_gang; /* ctx->sched_flags */ enum { - SPU_SCHED_WAKE = 0, /* currently unused */ + SPU_SCHED_EXITING = 0, }; struct spu_context { @@ -50,16 +50,17 @@ struct spu_context { spinlock_t mmio_lock; /* protects mmio access */ struct address_space *local_store; /* local store mapping. */ struct address_space *mfc; /* 'mfc' area mappings. */ - struct address_space *cntl; /* 'control' area mappings. */ - struct address_space *signal1; /* 'signal1' area mappings. */ - struct address_space *signal2; /* 'signal2' area mappings. */ - struct address_space *mss; /* 'mss' area mappings. */ - struct address_space *psmap; /* 'psmap' area mappings. */ + struct address_space *cntl; /* 'control' area mappings. */ + struct address_space *signal1; /* 'signal1' area mappings. */ + struct address_space *signal2; /* 'signal2' area mappings. */ + struct address_space *mss; /* 'mss' area mappings. */ + struct address_space *psmap; /* 'psmap' area mappings. */ + spinlock_t mapping_lock; u64 object_id; /* user space pointer for oprofile */ enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state; struct mutex state_mutex; - struct semaphore run_sema; + struct mutex run_mutex; struct mm_struct *owner; @@ -140,6 +141,7 @@ struct spu_context_ops { struct spu_dma_info * info); void (*proxydma_info_read) (struct spu_context * ctx, struct spu_proxydma_info * info); + void (*restart_dma)(struct spu_context *ctx); }; extern struct spu_context_ops spu_hw_ops; @@ -149,6 +151,7 @@ struct spufs_inode_info { struct spu_context *i_ctx; struct spu_gang *i_gang; struct inode vfs_inode; + int i_openers; }; #define SPUFS_I(inode) \ container_of(inode, struct spufs_inode_info, vfs_inode) @@ -170,6 +173,9 @@ int put_spu_gang(struct spu_gang *gang); void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx); void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx); +/* fault handling */ +int spufs_handle_class1(struct spu_context *ctx); + /* context management */ static inline void spu_acquire(struct spu_context *ctx) { @@ -190,7 +196,6 @@ void spu_unmap_mappings(struct spu_conte void spu_forget(struct spu_context *ctx); int spu_acquire_runnable(struct spu_context *ctx, unsigned long flags); void spu_acquire_saved(struct spu_context *ctx); -int spu_acquire_exclusive(struct spu_context *ctx); int spu_activate(struct spu_context *ctx, unsigned long flags); void spu_deactivate(struct spu_context *ctx); @@ -218,14 +223,13 @@ ({ \ prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE); \ if (condition) \ break; \ - if (!signal_pending(current)) { \ - spu_release(ctx); \ - schedule(); \ - spu_acquire(ctx); \ - continue; \ + if (signal_pending(current)) { \ + __ret = -ERESTARTSYS; \ + break; \ } \ - __ret = -ERESTARTSYS; \ - break; \ + spu_release(ctx); \ + schedule(); \ + spu_acquire(ctx); \ } \ finish_wait(&(wq), &__wait); \ __ret; \ diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index fd91c73..8347c4a 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -2084,6 +2084,10 @@ int spu_save(struct spu_state *prev, str int rc; acquire_spu_lock(spu); /* Step 1. */ + prev->dar = spu->dar; + prev->dsisr = spu->dsisr; + spu->dar = 0; + spu->dsisr = 0; rc = __do_spu_save(prev, spu); /* Steps 2-53. */ release_spu_lock(spu); if (rc != 0 && rc != 2 && rc != 6) { @@ -2109,9 +2113,9 @@ int spu_restore(struct spu_state *new, s acquire_spu_lock(spu); harvest(NULL, spu); - spu->dar = 0; - spu->dsisr = 0; spu->slb_replace = 0; + new->dar = 0; + new->dsisr = 0; spu->class_0_pending = 0; rc = __do_spu_restore(new, spu); release_spu_lock(spu); diff --git a/arch/powerpc/platforms/celleb/Kconfig b/arch/powerpc/platforms/celleb/Kconfig new file mode 100644 index 0000000..2db1e29 --- /dev/null +++ b/arch/powerpc/platforms/celleb/Kconfig @@ -0,0 +1,9 @@ +config PPC_CELLEB + bool "Toshiba's Cell Reference Set 'Celleb' Architecture" + depends on PPC_MULTIPLATFORM && PPC64 + select PPC_CELL + select PPC_OF_PLATFORM_PCI + select HAS_TXX9_SERIAL + select PPC_UDBG_BEAT + select USB_OHCI_BIG_ENDIAN_MMIO + select USB_EHCI_BIG_ENDIAN_MMIO diff --git a/arch/powerpc/platforms/celleb/iommu.c b/arch/powerpc/platforms/celleb/iommu.c index f63b94c..755d869 100644 --- a/arch/powerpc/platforms/celleb/iommu.c +++ b/arch/powerpc/platforms/celleb/iommu.c @@ -37,7 +37,7 @@ static int __init find_dma_window(u64 *i const unsigned long *dma_window; for_each_node_by_type(dn, "ioif") { - dma_window = get_property(dn, "toshiba,dma-window", NULL); + dma_window = of_get_property(dn, "toshiba,dma-window", NULL); if (dma_window) { *io_space_id = (dma_window[0] >> 32) & 0xffffffffUL; *ioid = dma_window[0] & 0x7ffUL; @@ -80,7 +80,7 @@ static int celleb_of_bus_notify(struct n if (action != BUS_NOTIFY_ADD_DEVICE) return 0; - dev->archdata.dma_ops = pci_dma_ops; + dev->archdata.dma_ops = get_pci_dma_ops(); return 0; } @@ -95,7 +95,7 @@ static int __init celleb_init_iommu(void return -ENODEV; celleb_init_direct_mapping(); - pci_dma_ops = &dma_direct_ops; + set_pci_dma_ops(&dma_direct_ops); bus_register_notifier(&of_platform_bus_type, &celleb_of_bus_notifier); return 0; diff --git a/arch/powerpc/platforms/celleb/pci.c b/arch/powerpc/platforms/celleb/pci.c index 98de836..d1adf34 100644 --- a/arch/powerpc/platforms/celleb/pci.c +++ b/arch/powerpc/platforms/celleb/pci.c @@ -309,13 +309,13 @@ static int __devinit celleb_setup_fake_p goto error; } - name = get_property(node, "model", &rlen); + name = of_get_property(node, "model", &rlen); if (!name) { printk(KERN_ERR "PCI: model property not found.\n"); goto error; } - wi4 = get_property(node, "reg", &rlen); + wi4 = of_get_property(node, "reg", &rlen); if (wi4 == NULL) goto error; @@ -352,10 +352,10 @@ static int __devinit celleb_setup_fake_p } pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res); - wi0 = get_property(node, "device-id", NULL); - wi1 = get_property(node, "vendor-id", NULL); - wi2 = get_property(node, "class-code", NULL); - wi3 = get_property(node, "revision-id", NULL); + wi0 = of_get_property(node, "device-id", NULL); + wi1 = of_get_property(node, "vendor-id", NULL); + wi2 = of_get_property(node, "class-code", NULL); + wi3 = of_get_property(node, "revision-id", NULL); celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff); celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff); @@ -376,7 +376,7 @@ static int __devinit celleb_setup_fake_p celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr); - li = get_property(node, "interrupts", &rlen); + li = of_get_property(node, "interrupts", &rlen); val = li[0]; celleb_config_write_fake(*config, PCI_INTERRUPT_PIN, 1, 1); celleb_config_write_fake(*config, PCI_INTERRUPT_LINE, 1, val); @@ -424,7 +424,7 @@ static int __devinit phb_set_bus_ranges( const int *bus_range; unsigned int len; - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) return 1; @@ -451,7 +451,7 @@ int __devinit celleb_setup_phb(struct pc struct device_node *node; unsigned int rlen; - name = get_property(dev, "name", &rlen); + name = of_get_property(dev, "name", &rlen); if (!name) return 1; diff --git a/arch/powerpc/platforms/celleb/setup.c b/arch/powerpc/platforms/celleb/setup.c index 5f4d0d9..596ab2a 100644 --- a/arch/powerpc/platforms/celleb/setup.c +++ b/arch/powerpc/platforms/celleb/setup.c @@ -67,7 +67,7 @@ static void celleb_show_cpuinfo(struct s root = of_find_node_by_path("/"); if (root) - model = get_property(root, "model", NULL); + model = of_get_property(root, "model", NULL); /* using "CHRP" is to trick anaconda into installing FCx into Celleb */ seq_printf(m, "machine\t\t: %s %s\n", celleb_machine_type, model); of_node_put(root); @@ -128,15 +128,6 @@ static int __init celleb_probe(void) return 1; } -/* - * Cell has no legacy IO; anything calling this function has to - * fail or bad things will happen - */ -static int celleb_check_legacy_ioport(unsigned int baseport) -{ - return -ENODEV; -} - #ifdef CONFIG_KEXEC static void celleb_kexec_cpu_down(int crash, int secondary) { @@ -173,7 +164,6 @@ define_machine(celleb) { .get_rtc_time = beat_get_rtc_time, .set_rtc_time = beat_set_rtc_time, .calibrate_decr = generic_calibrate_decr, - .check_legacy_ioport = celleb_check_legacy_ioport, .progress = celleb_progress, .power_save = beat_power_save, .nvram_size = beat_nvram_get_size, diff --git a/arch/powerpc/platforms/chrp/Kconfig b/arch/powerpc/platforms/chrp/Kconfig new file mode 100644 index 0000000..d2c6905 --- /dev/null +++ b/arch/powerpc/platforms/chrp/Kconfig @@ -0,0 +1,11 @@ +config PPC_CHRP + bool "Common Hardware Reference Platform (CHRP) based machines" + depends on PPC_MULTIPLATFORM && PPC32 + select MPIC + select PPC_I8259 + select PPC_INDIRECT_PCI + select PPC_RTAS + select PPC_MPC106 + select PPC_UDBG_16550 + select PPC_NATIVE + default y diff --git a/arch/powerpc/platforms/chrp/nvram.c b/arch/powerpc/platforms/chrp/nvram.c index 0dd4a64..8efd424 100644 --- a/arch/powerpc/platforms/chrp/nvram.c +++ b/arch/powerpc/platforms/chrp/nvram.c @@ -74,7 +74,7 @@ void __init chrp_nvram_init(void) if (nvram == NULL) return; - nbytes_p = get_property(nvram, "#bytes", &proplen); + nbytes_p = of_get_property(nvram, "#bytes", &proplen); if (nbytes_p == NULL || proplen != sizeof(unsigned int)) return; diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index ddb4a11..1469d64 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -7,7 +7,6 @@ #include #include #include #include -#include #include #include @@ -137,9 +136,11 @@ hydra_init(void) struct device_node *np; struct resource r; - np = find_devices("mac-io"); - if (np == NULL || of_address_to_resource(np, 0, &r)) + np = of_find_node_by_name(NULL, "mac-io"); + if (np == NULL || of_address_to_resource(np, 0, &r)) { + of_node_put(np); return 0; + } Hydra = ioremap(r.start, r.end-r.start); printk("Hydra Mac I/O at %llx\n", (unsigned long long)r.start); printk("Hydra Feature_Control was %x", @@ -186,10 +187,9 @@ setup_python(struct pci_controller *hose /* Marvell Discovery II based Pegasos 2 */ static void __init setup_peg2(struct pci_controller *hose, struct device_node *dev) { - struct device_node *root = find_path_device("/"); + struct device_node *root = of_find_node_by_path("/"); struct device_node *rtas; - of_node_get(root); rtas = of_find_node_by_name (root, "rtas"); if (rtas) { hose->ops = &rtas_pci_ops; @@ -199,6 +199,7 @@ static void __init setup_peg2(struct pci " your firmware\n"); } pci_assign_all_buses = 1; + /* keep the reference to the root node */ } void __init @@ -211,14 +212,14 @@ chrp_find_bridges(void) const unsigned int *dma; const char *model, *machine; int is_longtrail = 0, is_mot = 0, is_pegasos = 0; - struct device_node *root = find_path_device("/"); + struct device_node *root = of_find_node_by_path("/"); struct resource r; /* * The PCI host bridge nodes on some machines don't have * properties to adequately identify them, so we have to * look at what sort of machine this is as well. */ - machine = get_property(root, "model", NULL); + machine = of_get_property(root, "model", NULL); if (machine != NULL) { is_longtrail = strncmp(machine, "IBM,LongTrail", 13) == 0; is_mot = strncmp(machine, "MOT", 3) == 0; @@ -237,7 +238,7 @@ chrp_find_bridges(void) dev->full_name); continue; } - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s\n", dev->full_name); @@ -263,7 +264,7 @@ chrp_find_bridges(void) hose->first_busno = bus_range[0]; hose->last_busno = bus_range[1]; - model = get_property(dev, "model", NULL); + model = of_get_property(dev, "model", NULL); if (model == NULL) model = ""; if (device_is_compatible(dev, "IBM,python")) { @@ -285,7 +286,8 @@ chrp_find_bridges(void) r.start + 0x000f8000, r.start + 0x000f8010); if (index == 0) { - dma = get_property(dev, "system-dma-base",&len); + dma = of_get_property(dev, "system-dma-base", + &len); if (dma && len >= sizeof(*dma)) { dma = (unsigned int *) (((unsigned long)dma) + @@ -303,12 +305,13 @@ chrp_find_bridges(void) /* check the first bridge for a property that we can use to set pci_dram_offset */ - dma = get_property(dev, "ibm,dma-ranges", &len); + dma = of_get_property(dev, "ibm,dma-ranges", &len); if (index == 0 && dma != NULL && len >= 6 * sizeof(*dma)) { pci_dram_offset = dma[2] - dma[3]; printk("pci_dram_offset = %lx\n", pci_dram_offset); } } + of_node_put(root); } /* SL82C105 IDE Control/Status Register */ diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index 117c9a0..1870038 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -28,7 +28,6 @@ #include #include #include #include -#include #include #include #include @@ -111,9 +110,9 @@ void chrp_show_cpuinfo(struct seq_file * struct device_node *root; const char *model = ""; - root = find_path_device("/"); + root = of_find_node_by_path("/"); if (root) - model = get_property(root, "model", NULL); + model = of_get_property(root, "model", NULL); seq_printf(m, "machine\t\t: CHRP %s\n", model); /* longtrail (goldengate) stuff */ @@ -161,6 +160,7 @@ void chrp_show_cpuinfo(struct seq_file * gg2_cachetypes[(t>>2) & 3], gg2_cachemodes[t & 3]); } + of_node_put(root); } /* @@ -205,13 +205,15 @@ static void __init sio_init(void) { struct device_node *root; - if ((root = find_path_device("/")) && - !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13)) { + if ((root = of_find_node_by_path("/")) && + !strncmp(of_get_property(root, "model", NULL), + "IBM,LongTrail", 13)) { /* logical device 0 (KBC/Keyboard) */ sio_fixup_irq("keyboard", 0, 1, 2); /* select logical device 1 (KBC/Mouse) */ sio_fixup_irq("mouse", 1, 12, 2); } + of_node_put(root); } @@ -224,12 +226,12 @@ static void __init pegasos_set_l2cr(void return; /* Enable L2 cache if needed */ - np = find_type_devices("cpu"); + np = of_find_node_by_type(NULL, "cpu"); if (np != NULL) { - const unsigned int *l2cr = get_property(np, "l2cr", NULL); + const unsigned int *l2cr = of_get_property(np, "l2cr", NULL); if (l2cr == NULL) { printk ("Pegasos l2cr : no cpu l2cr property found\n"); - return; + goto out; } if (!((*l2cr) & 0x80000000)) { printk ("Pegasos l2cr : L2 cache was not active, " @@ -238,6 +240,8 @@ static void __init pegasos_set_l2cr(void _set_L2CR((*l2cr) | 0x80000000); } } +out: + of_node_put(np); } static void briq_restart(char *cmd) @@ -250,14 +254,14 @@ static void briq_restart(char *cmd) void __init chrp_setup_arch(void) { - struct device_node *root = find_path_device ("/"); + struct device_node *root = of_find_node_by_path("/"); const char *machine = NULL; /* init to some ~sane value until calibrate_delay() runs */ loops_per_jiffy = 50000000/HZ; if (root) - machine = get_property(root, "model", NULL); + machine = of_get_property(root, "model", NULL); if (machine && strncmp(machine, "Pegasos", 7) == 0) { _chrp_type = _CHRP_Pegasos; } else if (machine && strncmp(machine, "IBM", 3) == 0) { @@ -273,6 +277,7 @@ void __init chrp_setup_arch(void) /* Let's assume it is an IBM chrp if all else fails */ _chrp_type = _CHRP_IBM; } + of_node_put(root); printk("chrp type = %x [%s]\n", _chrp_type, chrp_names[_chrp_type]); rtas_initialize(); @@ -361,8 +366,8 @@ static void __init chrp_find_openpic(voi return; root = of_find_node_by_path("/"); if (root) { - opprop = get_property(root, "platform-open-pic", &oplen); - na = prom_n_addr_cells(root); + opprop = of_get_property(root, "platform-open-pic", &oplen); + na = of_n_addr_cells(root); } if (opprop && oplen >= na * sizeof(unsigned int)) { opaddr = opprop[na-1]; /* assume 32-bit */ @@ -378,7 +383,7 @@ static void __init chrp_find_openpic(voi printk(KERN_INFO "OpenPIC at %lx\n", opaddr); - iranges = get_property(np, "interrupt-ranges", &len); + iranges = of_get_property(np, "interrupt-ranges", &len); if (iranges == NULL) len = 0; /* non-distributed mpic */ else @@ -427,7 +432,7 @@ static void __init chrp_find_openpic(voi of_node_put(np); } -#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) +#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_XMON) static struct irqaction xmon_irqaction = { .handler = xmon_irq, .mask = CPU_MASK_NONE, @@ -463,15 +468,16 @@ static void __init chrp_find_8259(void) * Also, Pegasos-type platforms don't have a proper node to start * from anyway */ - for (np = find_devices("pci"); np != NULL; np = np->next) { - const unsigned int *addrp = get_property(np, + for_each_node_by_name(np, "pci") { + const unsigned int *addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL); if (addrp == NULL) continue; - chrp_int_ack = addrp[prom_n_addr_cells(np)-1]; + chrp_int_ack = addrp[of_n_addr_cells(np)-1]; break; } + of_node_put(np); if (np == NULL) printk(KERN_WARNING "Cannot find PCI interrupt acknowledge" " address, polling\n"); @@ -493,7 +499,7 @@ static void __init chrp_find_8259(void) void __init chrp_init_IRQ(void) { -#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) +#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_XMON) struct device_node *kbd; #endif chrp_find_openpic(); @@ -510,13 +516,14 @@ #endif /* CONFIG_SMP */ if (_chrp_type == _CHRP_Pegasos) ppc_md.get_irq = i8259_irq; -#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) +#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_XMON) /* see if there is a keyboard in the device tree with a parent of type "adb" */ - for (kbd = find_devices("keyboard"); kbd; kbd = kbd->next) + for_each_node_by_name(kbd, "keyboard") if (kbd->parent && kbd->parent->type && strcmp(kbd->parent->type, "adb") == 0) break; + of_node_put(kbd); if (kbd) setup_irq(HYDRA_INT_ADB_NMI, &xmon_irqaction); #endif @@ -542,9 +549,9 @@ #endif /* Get the event scan rate for the rtas so we know how * often it expects a heartbeat. -- Cort */ - device = find_devices("rtas"); + device = of_find_node_by_name(NULL, "rtas"); if (device) - p = get_property(device, "rtas-event-scan-rate", NULL); + p = of_get_property(device, "rtas-event-scan-rate", NULL); if (p && *p) { /* * Arrange to call chrp_event_scan at least *p times @@ -571,6 +578,7 @@ #endif printk("RTAS Event Scan Rate: %u (%lu jiffies)\n", *p, interval); } + of_node_put(device); if (ppc_md.progress) ppc_md.progress(" Have fun! ", 0x7777); diff --git a/arch/powerpc/platforms/chrp/time.c b/arch/powerpc/platforms/chrp/time.c index 7d78890..96d1e4b 100644 --- a/arch/powerpc/platforms/chrp/time.c +++ b/arch/powerpc/platforms/chrp/time.c @@ -39,12 +39,17 @@ long __init chrp_time_init(void) struct resource r; int base; - rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); + rtcs = of_find_compatible_node(NULL, "rtc", "pnpPNP,b00"); if (rtcs == NULL) - rtcs = find_compatible_devices("rtc", "ds1385-rtc"); - if (rtcs == NULL || of_address_to_resource(rtcs, 0, &r)) + rtcs = of_find_compatible_node(NULL, "rtc", "ds1385-rtc"); + if (rtcs == NULL) + return 0; + if (of_address_to_resource(rtcs, 0, &r)) { + of_node_put(rtcs); return 0; - + } + of_node_put(rtcs); + base = r.start; nvram_as1 = 0; nvram_as0 = base; diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index 3410bcb..9557908 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig @@ -2,78 +2,6 @@ choice prompt "Machine Type" depends on EMBEDDED6xx -config KATANA - bool "Artesyn-Katana" - help - Select KATANA if configuring an Artesyn KATANA 750i or 3750 - cPCI board. - -config WILLOW - bool "Cogent-Willow" - -config CPCI690 - bool "Force-CPCI690" - help - Select CPCI690 if configuring a Force CPCI690 cPCI board. - -config POWERPMC250 - bool "Force-PowerPMC250" - -config CHESTNUT - bool "IBM 750FX Eval board or 750GX Eval board" - help - Select CHESTNUT if configuring an IBM 750FX Eval Board or a - IBM 750GX Eval board. - -config SPRUCE - bool "IBM-Spruce" - select PPC_INDIRECT_PCI - -config HDPU - bool "Sky-HDPU" - help - Select HDPU if configuring a Sky Computers Compute Blade. - -config HDPU_FEATURES - depends on HDPU - tristate "HDPU-Features" - help - Select to enable HDPU enhanced features. - -config EV64260 - bool "Marvell-EV64260BP" - help - Select EV64260 if configuring a Marvell (formerly Galileo) - EV64260BP Evaluation platform. - -config LOPEC - bool "Motorola-LoPEC" - select PPC_I8259 - -config MVME5100 - bool "Motorola-MVME5100" - select PPC_INDIRECT_PCI - -config PPLUS - bool "Motorola-PowerPlus" - select PPC_I8259 - select PPC_INDIRECT_PCI - -config PRPMC750 - bool "Motorola-PrPMC750" - select PPC_INDIRECT_PCI - -config PRPMC800 - bool "Motorola-PrPMC800" - select PPC_INDIRECT_PCI - -config SANDPOINT - bool "Motorola-Sandpoint" - select PPC_I8259 - help - Select SANDPOINT if configuring for a Motorola Sandpoint X3 - (any flavor). - config LINKSTATION bool "Linkstation / Kurobox(HG) from Buffalo" select MPIC @@ -97,212 +25,24 @@ config MPC7448HPC2 help Select MPC7448HPC2 if configuring for Freescale MPC7448HPC2 (Taiga) platform - -config RADSTONE_PPC7D - bool "Radstone Technology PPC7D board" - select PPC_I8259 - -config PAL4 - bool "SBS-Palomar4" - -config EST8260 - bool "EST8260" - ---help--- - The EST8260 is a single-board computer manufactured by Wind River - Systems, Inc. (formerly Embedded Support Tools Corp.) and based on - the MPC8260. Wind River Systems has a website at - , but the EST8260 cannot be found on it - and has probably been discontinued or rebadged. - -config SBC82xx - bool "SBC82xx" - ---help--- - SBC PowerQUICC II, single-board computer with MPC82xx CPU - Manufacturer: Wind River Systems, Inc. - Date of Release: May 2003 - End of Life: - - URL: - -config SBS8260 - bool "SBS8260" - -config RPX8260 - bool "RPXSUPER" - -config TQM8260 - bool "TQM8260" - ---help--- - MPC8260 based module, little larger than credit card, - up to 128 MB global + 64 MB local RAM, 32 MB Flash, - 32 kB EEPROM, 256 kB L@ Cache, 10baseT + 100baseT Ethernet, - 2 x serial ports, ... - Manufacturer: TQ Components, www.tq-group.de - Date of Release: June 2001 - End of Life: not yet :-) - URL: - -config ADS8272 - bool "ADS8272" - -config PQ2FADS - bool "Freescale-PQ2FADS" - help - Select PQ2FADS if you wish to configure for a Freescale - PQ2FADS board (-VR or -ZU). - -config EV64360 - bool "Marvell-EV64360BP" - help - Select EV64360 if configuring a Marvell EV64360BP Evaluation - platform. endchoice -config PQ2ADS - bool - depends on ADS8272 - default y - -config TQM8xxL - bool - depends on 8xx && (TQM823L || TQM850L || FPS850L || TQM855L || TQM860L) - default y - -config 8260 - bool "CPM2 Support" if WILLOW - depends on 6xx - default y if TQM8260 || RPX8260 || EST8260 || SBS8260 || SBC82xx || PQ2FADS - help - The MPC8260 is a typical embedded CPU made by Motorola. Selecting - this option means that you wish to build a kernel for a machine with - an 8260 class CPU. - -config 8272 - bool - depends on 6xx - default y if ADS8272 - select 8260 - help - The MPC8272 CPM has a different internal dpram setup than other CPM2 - devices - -config CPM2 - bool - depends on 8260 || MPC8560 || MPC8555 - default y - help - The CPM2 (Communications Processor Module) is a coprocessor on - embedded CPUs made by Motorola. Selecting this option means that - you wish to build a kernel for a machine with a CPM2 coprocessor - on it (826x, 827x, 8560). - -config PPC_GEN550 - bool - depends on SANDPOINT || SPRUCE || PPLUS || \ - PRPMC750 || PRPMC800 || LOPEC || \ - (EV64260 && !SERIAL_MPSC) || CHESTNUT || RADSTONE_PPC7D || \ - 83xx || LINKSTATION - default y - -config FORCE - bool - depends on 6xx && POWERPMC250 - default y - -config GT64260 - bool - depends on EV64260 || CPCI690 - default y - -config MV64360 # Really MV64360 & MV64460 - bool - depends on CHESTNUT || KATANA || RADSTONE_PPC7D || HDPU || EV64360 - default y - -config MV64X60 - bool - depends on (GT64260 || MV64360) - select PPC_INDIRECT_PCI - default y - config TSI108_BRIDGE bool depends on MPC7448HPC2 default y -menu "Set bridge options" - depends on MV64X60 - -config NOT_COHERENT_CACHE - bool "Turn off Cache Coherency" - default n - help - Some 64x60 bridges lock up when trying to enforce cache coherency. - When this option is selected, cache coherency will be turned off. - Note that this can cause other problems (e.g., stale data being - speculatively loaded via a cached mapping). Use at your own risk. - -config MV64X60_BASE - hex "Set bridge base used by firmware" - default "0xf1000000" - help - A firmware can leave the base address of the bridge's registers at - a non-standard location. If so, set this value to reflect the - address of that non-standard location. - -config MV64X60_NEW_BASE - hex "Set bridge base used by kernel" - default "0xf1000000" - help - If the current base address of the bridge's registers is not where - you want it, set this value to the address that you want it moved to. - -endmenu - -config NONMONARCH_SUPPORT - bool "Enable Non-Monarch Support" - depends on PRPMC800 - -config HARRIER - bool - depends on PRPMC800 - default y - -config EPIC_SERIAL_MODE - bool - depends on 6xx && (LOPEC || SANDPOINT) - default y - config MPC10X_BRIDGE bool - depends on POWERPMC250 || LOPEC || SANDPOINT || LINKSTATION + depends on LINKSTATION select PPC_INDIRECT_PCI default y config MPC10X_OPENPIC bool - depends on POWERPMC250 || LOPEC || SANDPOINT || LINKSTATION + depends on LINKSTATION default y config MPC10X_STORE_GATHERING bool "Enable MPC10x store gathering" depends on MPC10X_BRIDGE - -config SANDPOINT_ENABLE_UART1 - bool "Enable DUART mode on Sandpoint" - depends on SANDPOINT - help - If this option is enabled then the MPC824x processor will run - in DUART mode instead of UART mode. - -config HARRIER_STORE_GATHERING - bool "Enable Harrier store gathering" - depends on HARRIER - -config MVME5100_IPMC761_PRESENT - bool "MVME5100 configured with an IPMC761" - depends on MVME5100 - select PPC_I8259 - -config SPRUCE_BAUD_33M - bool "Spruce baud clock support" - depends on SPRUCE diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c index 3f6c411..b412f00 100644 --- a/arch/powerpc/platforms/embedded6xx/linkstation.c +++ b/arch/powerpc/platforms/embedded6xx/linkstation.c @@ -58,11 +58,11 @@ static int __init add_bridge(struct devi { int len; struct pci_controller *hose; - int *bus_range; + const int *bus_range; printk("Adding PCI host bridge %s\n", dev->full_name); - bus_range = (int *) get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); @@ -106,7 +106,7 @@ static void __init linkstation_init_IRQ( { struct mpic *mpic; struct device_node *dnp; - void *prop; + const u32 *prop; int size; phys_addr_t paddr; @@ -114,7 +114,7 @@ static void __init linkstation_init_IRQ( if (dnp == NULL) return; - prop = (struct device_node *)get_property(dnp, "reg", &size); + prop = of_get_property(dnp, "reg", &size); paddr = (phys_addr_t)of_translate_address(dnp, prop); mpic = mpic_alloc(dnp, paddr, MPIC_PRIMARY | MPIC_WANTS_RESET, 4, 32, " EPIC "); diff --git a/arch/powerpc/platforms/embedded6xx/ls_uart.c b/arch/powerpc/platforms/embedded6xx/ls_uart.c index 0e83776..d0bee9f 100644 --- a/arch/powerpc/platforms/embedded6xx/ls_uart.c +++ b/arch/powerpc/platforms/embedded6xx/ls_uart.c @@ -110,8 +110,8 @@ static int __init ls_uarts_init(void) if (!avr) return -EINVAL; - avr_clock = *(u32*)get_property(avr, "clock-frequency", &len); - phys_addr = ((u32*)get_property(avr, "reg", &len))[0]; + avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len); + phys_addr = ((u32*)of_get_property(avr, "reg", &len))[0]; if (!avr_clock || !phys_addr) return -EINVAL; diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index 3fcc85f..c3f64dd 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -25,7 +25,6 @@ #include #include #include #include -#include #include #include #include @@ -82,7 +81,7 @@ static void __init mpc7448_hpc2_setup_ar if (cpu != 0) { const unsigned int *fp; - fp = get_property(cpu, "clock-frequency", NULL); + fp = of_get_property(cpu, "clock-frequency", NULL); if (fp != 0) loops_per_jiffy = *fp / HZ; else @@ -91,16 +90,6 @@ static void __init mpc7448_hpc2_setup_ar } tsi108_csr_vir_base = get_vir_csrbase(); -#ifdef CONFIG_ROOT_NFS - ROOT_DEV = Root_NFS; -#else - ROOT_DEV = Root_HDA1; -#endif - -#ifdef CONFIG_BLK_DEV_INITRD - ROOT_DEV = Root_RAM0; -#endif - /* setup PCI host bridge */ #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) @@ -143,7 +132,7 @@ #endif tsi_pic = of_find_node_by_type(NULL, "open-pic"); if (tsi_pic) { unsigned int size; - const void *prop = get_property(tsi_pic, "reg", &size); + const void *prop = of_get_property(tsi_pic, "reg", &size); mpic_paddr = of_translate_address(tsi_pic, prop); } diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig index 54e6b3b..46c3a8e 100644 --- a/arch/powerpc/platforms/iseries/Kconfig +++ b/arch/powerpc/platforms/iseries/Kconfig @@ -1,3 +1,7 @@ +config PPC_ISERIES + bool "IBM Legacy iSeries" + depends on PPC_MULTIPLATFORM && PPC64 + select PPC_INDIRECT_IO menu "iSeries device drivers" depends on PPC_ISERIES diff --git a/arch/powerpc/platforms/iseries/iommu.c b/arch/powerpc/platforms/iseries/iommu.c index d7a756d..3b6a966 100644 --- a/arch/powerpc/platforms/iseries/iommu.c +++ b/arch/powerpc/platforms/iseries/iommu.c @@ -171,7 +171,7 @@ void iommu_devnode_init_iSeries(struct p { struct iommu_table *tbl; struct pci_dn *pdn = PCI_DN(dn); - const u32 *lsn = get_property(dn, "linux,logical-slot-number", NULL); + const u32 *lsn = of_get_property(dn, "linux,logical-slot-number", NULL); BUG_ON(lsn == NULL); @@ -194,5 +194,5 @@ void iommu_init_early_iSeries(void) ppc_md.tce_build = tce_build_iSeries; ppc_md.tce_free = tce_free_iSeries; - pci_dma_ops = &dma_iommu_ops; + set_pci_dma_ops(&dma_iommu_ops); } diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c index 5225abf..63b3367 100644 --- a/arch/powerpc/platforms/iseries/irq.c +++ b/arch/powerpc/platforms/iseries/irq.c @@ -30,7 +30,6 @@ #include #include #include #include -#include #include #include @@ -337,6 +336,8 @@ #endif return irq; } +#ifdef CONFIG_PCI + static int iseries_irq_host_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw) { @@ -384,3 +385,4 @@ void __init iSeries_init_IRQ(void) "failed with rc 0x%x\n", ret); } +#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c index 4a06d9c..9c97422 100644 --- a/arch/powerpc/platforms/iseries/pci.c +++ b/arch/powerpc/platforms/iseries/pci.c @@ -24,7 +24,6 @@ #include #include #include #include -#include #include #include @@ -177,7 +176,7 @@ void __init iSeries_pci_final_fixup(void struct pci_dn *pdn = PCI_DN(node); const u32 *agent; - agent = get_property(node, "linux,agent-id", NULL); + agent = of_get_property(node, "linux,agent-id", NULL); if ((pdn != NULL) && (agent != NULL)) { u8 irq = iSeries_allocate_IRQ(pdn->busno, 0, pdn->bussubno); @@ -755,7 +754,7 @@ void __init iSeries_pcibios_init(void) if ((node->type == NULL) || (strcmp(node->type, "pci") != 0)) continue; - busp = get_property(node, "bus-range", NULL); + busp = of_get_property(node, "bus-range", NULL); if (busp == NULL) continue; bus = *busp; diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index cce7e30..7f5dcee 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -628,15 +628,6 @@ static void iseries_iounmap(volatile voi { } -/* - * iSeries has no legacy IO, anything calling this function has to - * fail or bad things will happen - */ -static int iseries_check_legacy_ioport(unsigned int baseport) -{ - return -ENODEV; -} - static int __init iseries_probe(void) { unsigned long root = of_get_flat_dt_root(); @@ -667,7 +658,6 @@ define_machine(iseries) { .calibrate_decr = generic_calibrate_decr, .progress = iSeries_progress, .probe = iseries_probe, - .check_legacy_ioport = iseries_check_legacy_ioport, .ioremap = iseries_ioremap, .iounmap = iseries_iounmap, /* XXX Implement enable_pmcs for iSeries */ diff --git a/arch/powerpc/platforms/iseries/viopath.c b/arch/powerpc/platforms/iseries/viopath.c index e2100ec..2ca2d8a 100644 --- a/arch/powerpc/platforms/iseries/viopath.c +++ b/arch/powerpc/platforms/iseries/viopath.c @@ -155,7 +155,7 @@ static int proc_viopath_show(struct seq_ node = of_find_node_by_path("/"); sysid = NULL; if (node != NULL) - sysid = get_property(node, "system-id", NULL); + sysid = of_get_property(node, "system-id", NULL); if (sysid == NULL) seq_printf(m, "SRLNBR=\n"); diff --git a/arch/powerpc/platforms/maple/Kconfig b/arch/powerpc/platforms/maple/Kconfig new file mode 100644 index 0000000..f7c95eb --- /dev/null +++ b/arch/powerpc/platforms/maple/Kconfig @@ -0,0 +1,17 @@ +config PPC_MAPLE + depends on PPC_MULTIPLATFORM && PPC64 + bool "Maple 970FX Evaluation Board" + select MPIC + select U3_DART + select MPIC_U3_HT_IRQS + select GENERIC_TBSYNC + select PPC_UDBG_16550 + select PPC_970_NAP + select PPC_NATIVE + select PPC_RTAS + select MMIO_NVRAM + select ATA_NONSTANDARD if ATA + default n + help + This option enables support for the Maple 970FX Evaluation Board. + For more information, refer to diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index 73c5990..b1d3b99 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c @@ -44,11 +44,11 @@ static int __init fixup_one_level_bus_ra int len; /* For PCI<->PCI bridges or CardBus bridges, we go down */ - class_code = get_property(node, "class-code", NULL); + class_code = of_get_property(node, "class-code", NULL); if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) continue; - bus_range = get_property(node, "bus-range", &len); + bus_range = of_get_property(node, "bus-range", &len); if (bus_range != NULL && len > 2 * sizeof(int)) { if (bus_range[1] > higher) higher = bus_range[1]; @@ -77,7 +77,7 @@ static void __init fixup_bus_range(struc bridge->full_name); return; } - bus_range = (int *)prop->value; + bus_range = prop->value; bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); } @@ -454,7 +454,7 @@ static int __init add_bridge(struct devi DBG("Adding PCI host bridge %s\n", dev->full_name); - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n", dev->full_name); diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 82d3f9e..2a30c5b 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -32,7 +32,6 @@ #include #include #include #include -#include #include #include #include @@ -114,8 +113,8 @@ static void maple_restart(char *cmd) printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); goto fail; } - maple_nvram_offset = get_property(sp, "restart-addr", NULL); - maple_nvram_command = get_property(sp, "restart-value", NULL); + maple_nvram_offset = of_get_property(sp, "restart-addr", NULL); + maple_nvram_command = of_get_property(sp, "restart-value", NULL); of_node_put(sp); /* send command */ @@ -141,8 +140,8 @@ static void maple_power_off(void) printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); goto fail; } - maple_nvram_offset = get_property(sp, "power-off-addr", NULL); - maple_nvram_command = get_property(sp, "power-off-value", NULL); + maple_nvram_offset = of_get_property(sp, "power-off-addr", NULL); + maple_nvram_command = of_get_property(sp, "power-off-value", NULL); of_node_put(sp); /* send command */ @@ -249,8 +248,8 @@ static void __init maple_init_IRQ(void) /* Find address list in /platform-open-pic */ root = of_find_node_by_path("/"); - naddr = prom_n_addr_cells(root); - opprop = get_property(root, "platform-open-pic", &opplen); + naddr = of_n_addr_cells(root); + opprop = of_get_property(root, "platform-open-pic", &opplen); if (opprop != 0) { openpic_addr = of_read_number(opprop, naddr); has_isus = (opplen > naddr); @@ -261,11 +260,11 @@ static void __init maple_init_IRQ(void) BUG_ON(openpic_addr == 0); /* Check for a big endian MPIC */ - if (get_property(np, "big-endian", NULL) != NULL) + if (of_get_property(np, "big-endian", NULL) != NULL) flags |= MPIC_BIG_ENDIAN; /* XXX Maple specific bits */ - flags |= MPIC_BROKEN_U3 | MPIC_WANTS_RESET; + flags |= MPIC_U3_HT_IRQS | MPIC_WANTS_RESET; /* All U3/U4 are big-endian, older SLOF firmware doesn't encode this */ flags |= MPIC_BIG_ENDIAN; diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig index 68dc529..eb4dbc7 100644 --- a/arch/powerpc/platforms/pasemi/Kconfig +++ b/arch/powerpc/platforms/pasemi/Kconfig @@ -1,3 +1,15 @@ +config PPC_PASEMI + depends on PPC_MULTIPLATFORM && PPC64 + bool "PA Semi SoC-based platforms" + default n + select MPIC + select PPC_UDBG_16550 + select GENERIC_TBSYNC + select PPC_NATIVE + help + This option enables support for PA Semi's PWRficient line + of SoC processors, including PA6T-1682M + menu "PA Semi PWRficient options" depends on PPC_PASEMI @@ -7,4 +19,11 @@ config PPC_PASEMI_IOMMU help IOMMU support for PA6T-1682M +config PPC_PASEMI_MDIO + depends on PHYLIB + tristate "MDIO support via GPIO" + default y + help + Driver for MDIO via GPIO on PWRficient platforms + endmenu diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile index e657cca..2cd2a4f 100644 --- a/arch/powerpc/platforms/pasemi/Makefile +++ b/arch/powerpc/platforms/pasemi/Makefile @@ -1,2 +1,3 @@ obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o - +obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o +obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/arch/powerpc/platforms/pasemi/cpufreq.c new file mode 100644 index 0000000..2a57d60 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/cpufreq.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2007 PA Semi, Inc + * + * Authors: Egor Martovetsky + * Olof Johansson + * + * Maintained by: Olof Johansson + * + * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c: + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#include +#include +#include + +#define SDCASR_REG 0x0100 +#define SDCASR_REG_STRIDE 0x1000 +#define SDCPWR_CFGA0_REG 0x0100 +#define SDCPWR_PWST0_REG 0x0000 +#define SDCPWR_GIZTIME_REG 0x0440 + +/* SDCPWR_GIZTIME_REG fields */ +#define SDCPWR_GIZTIME_GR 0x80000000 +#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff + +/* Offset of ASR registers from SDC base */ +#define SDCASR_OFFSET 0x120000 + +static void __iomem *sdcpwr_mapbase; +static void __iomem *sdcasr_mapbase; + +static DEFINE_MUTEX(pas_switch_mutex); + +/* Current astate, is used when waking up from power savings on + * one core, in case the other core has switched states during + * the idle time. + */ +static int current_astate; + +/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ +static struct cpufreq_frequency_table pas_freqs[] = { + {0, 0}, + {1, 0}, + {2, 0}, + {3, 0}, + {4, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +static struct freq_attr *pas_cpu_freqs_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +/* + * hardware specific functions + */ + +static int get_astate_freq(int astate) +{ + u32 ret; + ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)); + + return ret & 0x3f; +} + +static int get_cur_astate(int cpu) +{ + u32 ret; + + ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG); + ret = (ret >> (cpu * 4)) & 0x7; + + return ret; +} + +static int get_gizmo_latency(void) +{ + u32 giztime, ret; + + giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG); + + /* just provide the upper bound */ + if (giztime & SDCPWR_GIZTIME_GR) + ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000; + else + ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000; + + return ret; +} + +static void set_astate(int cpu, unsigned int astate) +{ + u64 flags; + + /* Return if called before init has run */ + if (unlikely(!sdcasr_mapbase)) + return; + + local_irq_save(flags); + + out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate); + + local_irq_restore(flags); +} + +void restore_astate(int cpu) +{ + set_astate(cpu, current_astate); +} + +/* + * cpufreq functions + */ + +static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + const u32 *max_freqp; + u32 max_freq; + int i, cur_astate; + struct resource res; + struct device_node *cpu, *dn; + int err = -ENODEV; + + cpu = of_get_cpu_node(policy->cpu, NULL); + + if (!cpu) + goto out; + + dn = of_find_compatible_node(NULL, "sdc", "1682m-sdc"); + if (!dn) + goto out; + err = of_address_to_resource(dn, 0, &res); + of_node_put(dn); + if (err) + goto out; + sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000); + if (!sdcasr_mapbase) { + err = -EINVAL; + goto out; + } + + dn = of_find_compatible_node(NULL, "gizmo", "1682m-gizmo"); + if (!dn) { + err = -ENODEV; + goto out_unmap_sdcasr; + } + err = of_address_to_resource(dn, 0, &res); + of_node_put(dn); + if (err) + goto out_unmap_sdcasr; + sdcpwr_mapbase = ioremap(res.start, 0x1000); + if (!sdcpwr_mapbase) { + err = -EINVAL; + goto out_unmap_sdcasr; + } + + pr_debug("init cpufreq on CPU %d\n", policy->cpu); + + max_freqp = of_get_property(cpu, "clock-frequency", NULL); + if (!max_freqp) { + err = -EINVAL; + goto out_unmap_sdcpwr; + } + + /* we need the freq in kHz */ + max_freq = *max_freqp / 1000; + + pr_debug("max clock-frequency is at %u kHz\n", max_freq); + pr_debug("initializing frequency table\n"); + + /* initialize frequency table */ + for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { + pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000; + pr_debug("%d: %d\n", i, pas_freqs[i].frequency); + } + + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + + policy->cpuinfo.transition_latency = get_gizmo_latency(); + + cur_astate = get_cur_astate(policy->cpu); + pr_debug("current astate is at %d\n",cur_astate); + + policy->cur = pas_freqs[cur_astate].frequency; + policy->cpus = cpu_online_map; + + cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu); + + /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max + * are set correctly + */ + return cpufreq_frequency_table_cpuinfo(policy, pas_freqs); + +out_unmap_sdcpwr: + iounmap(sdcpwr_mapbase); + +out_unmap_sdcasr: + iounmap(sdcasr_mapbase); +out: + return err; +} + +static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + if (sdcasr_mapbase) + iounmap(sdcasr_mapbase); + if (sdcpwr_mapbase) + iounmap(sdcpwr_mapbase); + + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static int pas_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, pas_freqs); +} + +static int pas_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct cpufreq_freqs freqs; + int pas_astate_new; + int i; + + cpufreq_frequency_table_target(policy, + pas_freqs, + target_freq, + relation, + &pas_astate_new); + + freqs.old = policy->cur; + freqs.new = pas_freqs[pas_astate_new].frequency; + freqs.cpu = policy->cpu; + + mutex_lock(&pas_switch_mutex); + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", + policy->cpu, + pas_freqs[pas_astate_new].frequency, + pas_freqs[pas_astate_new].index); + + current_astate = pas_astate_new; + + for_each_online_cpu(i) + set_astate(i, pas_astate_new); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + mutex_unlock(&pas_switch_mutex); + + return 0; +} + +static struct cpufreq_driver pas_cpufreq_driver = { + .name = "pas-cpufreq", + .owner = THIS_MODULE, + .flags = CPUFREQ_CONST_LOOPS, + .init = pas_cpufreq_cpu_init, + .exit = pas_cpufreq_cpu_exit, + .verify = pas_cpufreq_verify, + .target = pas_cpufreq_target, + .attr = pas_cpu_freqs_attr, +}; + +/* + * module init and destoy + */ + +static int __init pas_cpufreq_init(void) +{ + if (!machine_is_compatible("PA6T-1682M")) + return -ENODEV; + + return cpufreq_register_driver(&pas_cpufreq_driver); +} + +static void __exit pas_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&pas_cpufreq_driver); +} + +module_init(pas_cpufreq_init); +module_exit(pas_cpufreq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Egor Martovetsky , Olof Johansson "); diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c new file mode 100644 index 0000000..c91a335 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Olof Johansson, PA Semi + * + * Maintained by: Olof Johansson + * + * Based on drivers/net/fs_enet/mii-bitbang.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DELAY 1 + +static void __iomem *gpio_regs; + +struct gpio_priv { + int mdc_pin; + int mdio_pin; +}; + +#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin) +#define MDIO_PIN(bus) (((struct gpio_priv *)bus->priv)->mdio_pin) + +static inline void mdio_lo(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus)); +} + +static inline void mdio_hi(struct mii_bus *bus) +{ + out_le32(gpio_regs, 1 << MDIO_PIN(bus)); +} + +static inline void mdc_lo(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus)); +} + +static inline void mdc_hi(struct mii_bus *bus) +{ + out_le32(gpio_regs, 1 << MDC_PIN(bus)); +} + +static inline void mdio_active(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus))); +} + +static inline void mdio_tristate(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus))); +} + +static inline int mdio_read(struct mii_bus *bus) +{ + return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus))); +} + +static void clock_out(struct mii_bus *bus, int bit) +{ + if (bit) + mdio_hi(bus); + else + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); +} + +/* Utility to send the preamble, address, and register (common to read and write). */ +static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg) +{ + int i; + + /* CFE uses a really long preamble (40 bits). We'll do the same. */ + mdio_active(bus); + for (i = 0; i < 40; i++) { + clock_out(bus, 1); + } + + /* send the start bit (01) and the read opcode (10) or write (10) */ + clock_out(bus, 0); + clock_out(bus, 1); + + clock_out(bus, read); + clock_out(bus, !read); + + /* send the PHY address */ + for (i = 0; i < 5; i++) { + clock_out(bus, (addr & 0x10) != 0); + addr <<= 1; + } + + /* send the register address */ + for (i = 0; i < 5; i++) { + clock_out(bus, (reg & 0x10) != 0); + reg <<= 1; + } +} + +static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location) +{ + u16 rdreg; + int ret, i; + u8 addr = phy_id & 0xff; + u8 reg = location & 0xff; + + bitbang_pre(bus, 1, addr, reg); + + /* tri-state our MDIO I/O pin so we can read */ + mdio_tristate(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + + /* read 16 bits of register data, MSB first */ + rdreg = 0; + for (i = 0; i < 16; i++) { + mdc_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + udelay(DELAY); + rdreg <<= 1; + rdreg |= mdio_read(bus); + } + + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + udelay(DELAY); + + ret = rdreg; + + return ret; +} + +static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val) +{ + int i; + + u8 addr = phy_id & 0xff; + u8 reg = location & 0xff; + u16 value = val & 0xffff; + + bitbang_pre(bus, 0, addr, reg); + + /* send the turnaround (10) */ + mdc_lo(bus); + mdio_hi(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + + /* write 16 bits of register data, MSB first */ + for (i = 0; i < 16; i++) { + mdc_lo(bus); + if (value & 0x8000) + mdio_hi(bus); + else + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + value <<= 1; + } + + /* + * Tri-state the MDIO line. + */ + mdio_tristate(bus); + mdc_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + return 0; +} + +static int gpio_mdio_reset(struct mii_bus *bus) +{ + /*nothing here - dunno how to reset it*/ + return 0; +} + + +static int __devinit gpio_mdio_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->node; + struct device_node *gpio_np; + struct mii_bus *new_bus; + struct resource res; + struct gpio_priv *priv; + const unsigned int *prop; + int err = 0; + int i; + + gpio_np = of_find_compatible_node(NULL, "gpio", "1682m-gpio"); + + if (!gpio_np) + return -ENODEV; + + err = of_address_to_resource(gpio_np, 0, &res); + of_node_put(gpio_np); + + if (err) + return -EINVAL; + + if (!gpio_regs) + gpio_regs = ioremap(res.start, 0x100); + + if (!gpio_regs) + return -EPERM; + + priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + + if (new_bus == NULL) + return -ENOMEM; + + new_bus->name = "pasemi gpio mdio bus", + new_bus->read = &gpio_mdio_read, + new_bus->write = &gpio_mdio_write, + new_bus->reset = &gpio_mdio_reset, + + prop = of_get_property(np, "reg", NULL); + new_bus->id = *prop; + new_bus->priv = priv; + + new_bus->phy_mask = 0; + + new_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + for(i = 0; i < PHY_MAX_ADDR; ++i) + new_bus->irq[i] = irq_create_mapping(NULL, 10); + + + prop = of_get_property(np, "mdc-pin", NULL); + priv->mdc_pin = *prop; + + prop = of_get_property(np, "mdio-pin", NULL); + priv->mdio_pin = *prop; + + new_bus->dev = dev; + dev_set_drvdata(dev, new_bus); + + err = mdiobus_register(new_bus); + + if (0 != err) { + printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n", + new_bus->name, err); + goto bus_register_fail; + } + + return 0; + +bus_register_fail: + kfree(new_bus); + + return err; +} + + +static int gpio_mdio_remove(struct of_device *dev) +{ + struct mii_bus *bus = dev_get_drvdata(&dev->dev); + + mdiobus_unregister(bus); + + dev_set_drvdata(&dev->dev, NULL); + + kfree(bus->priv); + bus->priv = NULL; + kfree(bus); + + return 0; +} + +static struct of_device_id gpio_mdio_match[] = +{ + { + .compatible = "gpio-mdio", + }, + {}, +}; + +static struct of_platform_driver gpio_mdio_driver = +{ + .name = "gpio-mdio-bitbang", + .match_table = gpio_mdio_match, + .probe = gpio_mdio_probe, + .remove = gpio_mdio_remove, +}; + +int gpio_mdio_init(void) +{ + return of_register_platform_driver(&gpio_mdio_driver); +} + +void gpio_mdio_exit(void) +{ + of_unregister_platform_driver(&gpio_mdio_driver); +} +device_initcall(gpio_mdio_init); + diff --git a/arch/powerpc/platforms/pasemi/idle.c b/arch/powerpc/platforms/pasemi/idle.c index 1ca3ff3..5985ce0 100644 --- a/arch/powerpc/platforms/pasemi/idle.c +++ b/arch/powerpc/platforms/pasemi/idle.c @@ -61,6 +61,10 @@ static int pasemi_system_reset_exception /* do system reset */ return 0; } + + /* Set higher astate since we come out of power savings at 0 */ + restore_astate(hard_smp_processor_id()); + /* everything handled */ regs->msr |= MSR_RI; return 1; @@ -68,6 +72,11 @@ static int pasemi_system_reset_exception void __init pasemi_idle_init(void) { +#ifndef CONFIG_PPC_PASEMI_CPUFREQ + printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n"); + current_mode = 0; +#endif + ppc_md.system_reset_exception = pasemi_system_reset_exception; ppc_md.power_save = modes[current_mode].entry; printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name); diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c index 71dbf1a..95fa6a7 100644 --- a/arch/powerpc/platforms/pasemi/iommu.c +++ b/arch/powerpc/platforms/pasemi/iommu.c @@ -249,13 +249,13 @@ #ifndef CONFIG_PPC_PASEMI_IOMMU iommu_off = 1; #else iommu_off = of_chosen && - get_property(of_chosen, "linux,iommu-off", NULL); + of_get_property(of_chosen, "linux,iommu-off", NULL); #endif if (iommu_off) { /* Direct I/O, IOMMU off */ ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_null; ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_null; - pci_dma_ops = &dma_direct_ops; + set_pci_dma_ops(&dma_direct_ops); return; } @@ -266,7 +266,7 @@ #endif ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pasemi; ppc_md.tce_build = iobmap_build; ppc_md.tce_free = iobmap_free; - pci_dma_ops = &dma_iommu_ops; + set_pci_dma_ops(&dma_iommu_ops); } void __init alloc_iobmap_l2(void) diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h index 2d3927e..be84954 100644 --- a/arch/powerpc/platforms/pasemi/pasemi.h +++ b/arch/powerpc/platforms/pasemi/pasemi.h @@ -14,6 +14,14 @@ extern void __init pasemi_idle_init(void extern void idle_spin(void); extern void idle_doze(void); +/* Restore astate to last set */ +#ifdef CONFIG_PPC_PASEMI_CPUFREQ +extern void restore_astate(int cpu); +#else +static inline void restore_astate(int cpu) +{ +} +#endif #endif /* _PASEMI_PASEMI_H */ diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index 7ecb2ba..056243d 100644 --- a/arch/powerpc/platforms/pasemi/pci.c +++ b/arch/powerpc/platforms/pasemi/pci.c @@ -33,7 +33,17 @@ #include #define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off)) -#define CONFIG_OFFSET_VALID(off) ((off) < 4096) +static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset) +{ + /* Device 0 Function 0 is special: It's config space spans function 1 as + * well, so allow larger offset. It's really a two-function device but the + * second function does not probe. + */ + if (bus == 0 && devfn == 0) + return offset < 8192; + else + return offset < 4096; +} static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose, u8 bus, u8 devfn, int offset) @@ -51,7 +61,7 @@ static int pa_pxp_read_config(struct pci if (!hose) return PCIBIOS_DEVICE_NOT_FOUND; - if (!CONFIG_OFFSET_VALID(offset)) + if (!pa_pxp_offset_valid(bus->number, devfn, offset)) return PCIBIOS_BAD_REGISTER_NUMBER; addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset); @@ -85,7 +95,7 @@ static int pa_pxp_write_config(struct pc if (!hose) return PCIBIOS_DEVICE_NOT_FOUND; - if (!CONFIG_OFFSET_VALID(offset)) + if (!pa_pxp_offset_valid(bus->number, devfn, offset)) return PCIBIOS_BAD_REGISTER_NUMBER; addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset); diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index 449cf1a..f88f0ec 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c @@ -35,6 +35,7 @@ #include #include #include #include +#include #include "pasemi.h" @@ -101,12 +102,6 @@ #endif pasemi_idle_init(); } -/* No legacy IO on our parts */ -static int pas_check_legacy_ioport(unsigned int baseport) -{ - return -ENODEV; -} - static __init void pas_init_IRQ(void) { struct device_node *np; @@ -136,8 +131,8 @@ static __init void pas_init_IRQ(void) /* Find address list in /platform-open-pic */ root = of_find_node_by_path("/"); - naddr = prom_n_addr_cells(root); - opprop = get_property(root, "platform-open-pic", &opplen); + naddr = of_n_addr_cells(root); + opprop = of_get_property(root, "platform-open-pic", &opplen); if (!opprop) { printk(KERN_ERR "No platform-open-pic property.\n"); of_node_put(root); @@ -147,7 +142,7 @@ static __init void pas_init_IRQ(void) printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); mpic = mpic_alloc(mpic_node, openpic_addr, - MPIC_PRIMARY|MPIC_LARGE_VECTORS, + MPIC_PRIMARY|MPIC_LARGE_VECTORS|MPIC_WANTS_RESET, 0, 0, " PAS-OPIC "); BUG_ON(!mpic); @@ -209,6 +204,20 @@ static void __init pas_init_early(void) iommu_init_early_pasemi(); } +static struct of_device_id pasemi_bus_ids[] = { + { .type = "sdc", }, + {}, +}; + +static int __init pasemi_publish_devices(void) +{ + /* Publish OF platform devices for southbridge IOs */ + of_platform_bus_probe(NULL, pasemi_bus_ids, NULL); + + return 0; +} +device_initcall(pasemi_publish_devices); + /* * Called very early, MMU is off, device-tree isn't unflattened @@ -237,7 +246,6 @@ define_machine(pas) { .restart = pas_restart, .get_boot_time = pas_get_boot_time, .calibrate_decr = generic_calibrate_decr, - .check_legacy_ioport = pas_check_legacy_ioport, .progress = pas_progress, .machine_check_exception = pas_machine_check_handler, .pci_irq_fixup = pas_pci_irq_fixup, diff --git a/arch/powerpc/platforms/powermac/Kconfig b/arch/powerpc/platforms/powermac/Kconfig new file mode 100644 index 0000000..5b7afe5 --- /dev/null +++ b/arch/powerpc/platforms/powermac/Kconfig @@ -0,0 +1,20 @@ +config PPC_PMAC + bool "Apple PowerMac based machines" + depends on PPC_MULTIPLATFORM + select MPIC + select PPC_INDIRECT_PCI if PPC32 + select PPC_MPC106 if PPC32 + select PPC_NATIVE + default y + +config PPC_PMAC64 + bool + depends on PPC_PMAC && POWER4 + select MPIC + select U3_DART + select MPIC_U3_HT_IRQS + select GENERIC_TBSYNC + select PPC_970_NAP + default y + + diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c index de7440e..d679964 100644 --- a/arch/powerpc/platforms/powermac/backlight.c +++ b/arch/powerpc/platforms/powermac/backlight.c @@ -56,13 +56,16 @@ struct backlight_device *pmac_backlight; int pmac_has_backlight_type(const char *type) { - struct device_node* bk_node = find_devices("backlight"); + struct device_node* bk_node = of_find_node_by_name(NULL, "backlight"); if (bk_node) { - const char *prop = get_property(bk_node, + const char *prop = of_get_property(bk_node, "backlight-control", NULL); - if (prop && strncmp(prop, type, strlen(type)) == 0) + if (prop && strncmp(prop, type, strlen(type)) == 0) { + of_node_put(bk_node); return 1; + } + of_node_put(bk_node); } return 0; diff --git a/arch/powerpc/platforms/powermac/cpufreq_32.c b/arch/powerpc/platforms/powermac/cpufreq_32.c index c2b6b41..8943a94 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_32.c +++ b/arch/powerpc/platforms/powermac/cpufreq_32.c @@ -421,7 +421,7 @@ static int pmac_cpufreq_cpu_init(struct static u32 read_gpio(struct device_node *np) { - const u32 *reg = get_property(np, "reg", NULL); + const u32 *reg = of_get_property(np, "reg", NULL); u32 offset; if (reg == NULL) @@ -521,13 +521,14 @@ static int pmac_cpufreq_init_MacRISC3(st int lenp, rc; const u32 *freqs, *ratio; - freqs = get_property(cpunode, "bus-frequencies", &lenp); + freqs = of_get_property(cpunode, "bus-frequencies", &lenp); lenp /= sizeof(u32); if (freqs == NULL || lenp != 2) { printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n"); return 1; } - ratio = get_property(cpunode, "processor-to-bus-ratio*2", NULL); + ratio = of_get_property(cpunode, "processor-to-bus-ratio*2", + NULL); if (ratio == NULL) { printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n"); return 1; @@ -562,7 +563,7 @@ static int pmac_cpufreq_init_MacRISC3(st /* If we use the PMU, look for the min & max frequencies in the * device-tree */ - value = get_property(cpunode, "min-clock-frequency", NULL); + value = of_get_property(cpunode, "min-clock-frequency", NULL); if (!value) return 1; low_freq = (*value) / 1000; @@ -571,7 +572,7 @@ static int pmac_cpufreq_init_MacRISC3(st if (low_freq < 100000) low_freq *= 10; - value = get_property(cpunode, "max-clock-frequency", NULL); + value = of_get_property(cpunode, "max-clock-frequency", NULL); if (!value) return 1; hi_freq = (*value) / 1000; @@ -585,7 +586,7 @@ static int pmac_cpufreq_init_7447A(struc { struct device_node *volt_gpio_np; - if (get_property(cpunode, "dynamic-power-step", NULL) == NULL) + if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) return 1; volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); @@ -614,11 +615,11 @@ static int pmac_cpufreq_init_750FX(struc u32 pvr; const u32 *value; - if (get_property(cpunode, "dynamic-power-step", NULL) == NULL) + if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) return 1; hi_freq = cur_freq; - value = get_property(cpunode, "reduced-clock-frequency", NULL); + value = of_get_property(cpunode, "reduced-clock-frequency", NULL); if (!value) return 1; low_freq = (*value) / 1000; @@ -657,19 +658,19 @@ static int __init pmac_cpufreq_setup(voi return 0; /* Assume only one CPU */ - cpunode = find_type_devices("cpu"); + cpunode = of_find_node_by_type(NULL, "cpu"); if (!cpunode) goto out; /* Get current cpu clock freq */ - value = get_property(cpunode, "clock-frequency", NULL); + value = of_get_property(cpunode, "clock-frequency", NULL); if (!value) goto out; cur_freq = (*value) / 1000; /* Check for 7447A based MacRISC3 */ if (machine_is_compatible("MacRISC3") && - get_property(cpunode, "dynamic-power-step", NULL) && + of_get_property(cpunode, "dynamic-power-step", NULL) && PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { pmac_cpufreq_init_7447A(cpunode); /* Check for other MacRISC3 machines */ @@ -707,6 +708,7 @@ static int __init pmac_cpufreq_setup(voi else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000) pmac_cpufreq_init_750FX(cpunode); out: + of_node_put(cpunode); if (set_speed_proc == NULL) return -ENODEV; diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/arch/powerpc/platforms/powermac/cpufreq_64.c index 9d22361..567d552 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_64.c +++ b/arch/powerpc/platforms/powermac/cpufreq_64.c @@ -410,7 +410,7 @@ static int __init g5_neo2_cpufreq_init(s /* Get first CPU node */ for (cpunode = NULL; (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) { - const u32 *reg = get_property(cpunode, "reg", NULL); + const u32 *reg = of_get_property(cpunode, "reg", NULL); if (reg == NULL || (*reg) != 0) continue; if (!strcmp(cpunode->type, "cpu")) @@ -422,7 +422,7 @@ static int __init g5_neo2_cpufreq_init(s } /* Check 970FX for now */ - valp = get_property(cpunode, "cpu-version", NULL); + valp = of_get_property(cpunode, "cpu-version", NULL); if (!valp) { DBG("No cpu-version property !\n"); goto bail_noprops; @@ -434,7 +434,7 @@ static int __init g5_neo2_cpufreq_init(s } /* Look for the powertune data in the device-tree */ - g5_pmode_data = get_property(cpunode, "power-mode-data",&psize); + g5_pmode_data = of_get_property(cpunode, "power-mode-data",&psize); if (!g5_pmode_data) { DBG("No power-mode-data !\n"); goto bail_noprops; @@ -493,7 +493,7 @@ static int __init g5_neo2_cpufreq_init(s * half freq in this version. So far, I haven't yet seen a machine * supporting anything else. */ - valp = get_property(cpunode, "clock-frequency", NULL); + valp = of_get_property(cpunode, "clock-frequency", NULL); if (!valp) return -ENODEV; max_freq = (*valp)/1000; @@ -563,7 +563,7 @@ static int __init g5_pm72_cpufreq_init(s /* Lookup the cpuid eeprom node */ cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0"); if (cpuid != NULL) - eeprom = get_property(cpuid, "cpuid", NULL); + eeprom = of_get_property(cpuid, "cpuid", NULL); if (eeprom == NULL) { printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n"); rc = -ENODEV; @@ -573,13 +573,13 @@ static int __init g5_pm72_cpufreq_init(s /* Lookup the i2c hwclock */ for (hwclock = NULL; (hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){ - const char *loc = get_property(hwclock, + const char *loc = of_get_property(hwclock, "hwctrl-location", NULL); if (loc == NULL) continue; if (strcmp(loc, "CPU CLOCK")) continue; - if (!get_property(hwclock, "platform-get-frequency", NULL)) + if (!of_get_property(hwclock, "platform-get-frequency", NULL)) continue; break; } @@ -638,7 +638,7 @@ static int __init g5_pm72_cpufreq_init(s */ /* Get max frequency from device-tree */ - valp = get_property(cpunode, "clock-frequency", NULL); + valp = of_get_property(cpunode, "clock-frequency", NULL); if (!valp) { printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n"); rc = -ENODEV; diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index 24cc50c..52cfdd8 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -1044,6 +1044,7 @@ core99_reset_cpu(struct device_node *nod unsigned long flags; struct macio_chip *macio; struct device_node *np; + struct device_node *cpus; const int dflt_reset_lines[] = { KL_GPIO_RESET_CPU0, KL_GPIO_RESET_CPU1, KL_GPIO_RESET_CPU2, @@ -1053,12 +1054,12 @@ core99_reset_cpu(struct device_node *nod if (macio->type != macio_keylargo) return -ENODEV; - np = find_path_device("/cpus"); - if (np == NULL) + cpus = of_find_node_by_path("/cpus"); + if (cpus == NULL) return -ENODEV; - for (np = np->child; np != NULL; np = np->sibling) { - const u32 *num = get_property(np, "reg", NULL); - const u32 *rst = get_property(np, "soft-reset", NULL); + for (np = cpus->child; np != NULL; np = np->sibling) { + const u32 *num = of_get_property(np, "reg", NULL); + const u32 *rst = of_get_property(np, "soft-reset", NULL); if (num == NULL || rst == NULL) continue; if (param == *num) { @@ -1066,6 +1067,7 @@ core99_reset_cpu(struct device_node *nod break; } } + of_node_put(cpus); if (np == NULL || reset_io == 0) reset_io = dflt_reset_lines[param]; @@ -1095,7 +1097,7 @@ core99_usb_enable(struct device_node *no macio->type != macio_intrepid) return -ENODEV; - prop = get_property(node, "AAPL,clock-id", NULL); + prop = of_get_property(node, "AAPL,clock-id", NULL); if (!prop) return -ENODEV; if (strncmp(prop, "usb0u048", 8) == 0) @@ -1497,17 +1499,18 @@ static long g5_reset_cpu(struct device_n unsigned long flags; struct macio_chip *macio; struct device_node *np; + struct device_node *cpus; macio = &macio_chips[0]; if (macio->type != macio_keylargo2 && macio->type != macio_shasta) return -ENODEV; - np = find_path_device("/cpus"); - if (np == NULL) + cpus = of_find_node_by_path("/cpus"); + if (cpus == NULL) return -ENODEV; - for (np = np->child; np != NULL; np = np->sibling) { - const u32 *num = get_property(np, "reg", NULL); - const u32 *rst = get_property(np, "soft-reset", NULL); + for (np = cpus->child; np != NULL; np = np->sibling) { + const u32 *num = of_get_property(np, "reg", NULL); + const u32 *rst = of_get_property(np, "soft-reset", NULL); if (num == NULL || rst == NULL) continue; if (param == *num) { @@ -1515,6 +1518,7 @@ static long g5_reset_cpu(struct device_n break; } } + of_node_put(cpus); if (np == NULL || reset_io == 0) return -ENODEV; @@ -2404,14 +2408,15 @@ static int __init probe_motherboard(void struct macio_chip *macio = &macio_chips[0]; const char *model = NULL; struct device_node *dt; + int ret = 0; /* Lookup known motherboard type in device-tree. First try an * exact match on the "model" property, then try a "compatible" * match is none is found. */ - dt = find_devices("device-tree"); + dt = of_find_node_by_name(NULL, "device-tree"); if (dt != NULL) - model = get_property(dt, "model", NULL); + model = of_get_property(dt, "model", NULL); for(i=0; model && i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) { if (strcmp(model, pmac_mb_defs[i].model_string) == 0) { pmac_mb = pmac_mb_defs[i]; @@ -2474,15 +2479,18 @@ #else /* CONFIG_POWER4 */ break; #endif /* CONFIG_POWER4 */ default: - return -ENODEV; + ret = -ENODEV; + goto done; } found: #ifndef CONFIG_POWER4 /* Fixup Hooper vs. Comet */ if (pmac_mb.model_id == PMAC_TYPE_HOOPER) { u32 __iomem * mach_id_ptr = ioremap(0xf3000034, 4); - if (!mach_id_ptr) - return -ENODEV; + if (!mach_id_ptr) { + ret = -ENODEV; + goto done; + } /* Here, I used to disable the media-bay on comet. It * appears this is wrong, the floppy connector is actually * a kind of media-bay and works with the current driver. @@ -2499,18 +2507,26 @@ #ifndef CONFIG_POWER4 * that all Apple OF revs did it properly, I do it the paranoid way. */ while (uninorth_base && uninorth_rev > 3) { - struct device_node *np = find_path_device("/cpus"); - if (!np || !np->child) { + struct device_node *cpus = of_find_node_by_path("/cpus"); + struct device_node *np; + + if (!cpus || !cpus->child) { printk(KERN_WARNING "Can't find CPU(s) in device tree !\n"); + of_node_put(cpus); break; } - np = np->child; + np = cpus->child; /* Nap mode not supported on SMP */ - if (np->sibling) + if (np->sibling) { + of_node_put(cpus); break; + } /* Nap mode not supported if flush-on-lock property is present */ - if (get_property(np, "flush-on-lock", NULL)) + if (of_get_property(np, "flush-on-lock", NULL)) { + of_node_put(cpus); break; + } + of_node_put(cpus); powersave_nap = 1; printk(KERN_DEBUG "Processor NAP mode on idle enabled.\n"); break; @@ -2532,7 +2548,9 @@ #endif /* CONFIG_POWER4 */ printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name); - return 0; +done: + of_node_put(dt); + return ret; } /* Initialize the Core99 UniNorth host bridge and memory controller @@ -2558,7 +2576,7 @@ static void __init probe_uninorth(void) if (uninorth_node == NULL) return; - addrp = get_property(uninorth_node, "reg", NULL); + addrp = of_get_property(uninorth_node, "reg", NULL); if (addrp == NULL) return; address = of_translate_address(uninorth_node, addrp); @@ -2642,7 +2660,7 @@ static void __init probe_one_macio(const return; } if (type == macio_keylargo || type == macio_keylargo2) { - const u32 *did = get_property(node, "device-id", NULL); + const u32 *did = of_get_property(node, "device-id", NULL); if (*did == 0x00000025) type = macio_pangea; if (*did == 0x0000003e) @@ -2655,7 +2673,7 @@ static void __init probe_one_macio(const macio_chips[i].base = base; macio_chips[i].flags = MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON; macio_chips[i].name = macio_names[type]; - revp = get_property(node, "revision-id", NULL); + revp = of_get_property(node, "revision-id", NULL); if (revp) macio_chips[i].rev = *revp; printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n", @@ -2706,8 +2724,8 @@ initial_serial_shutdown(struct device_no int port_type = PMAC_SCC_ASYNC; int modem = 0; - slots = get_property(np, "slot-names", &len); - conn = get_property(np, "AAPL,connector", &len); + slots = of_get_property(np, "slot-names", &len); + conn = of_get_property(np, "AAPL,connector", &len); if (conn && (strcmp(conn, "infrared") == 0)) port_type = PMAC_SCC_IRDA; else if (device_is_compatible(np, "cobalt")) @@ -2735,12 +2753,14 @@ set_initial_features(void) * differenciate them all and since that hack was there for a long * time, I'll keep it around */ - if (macio_chips[0].type == macio_ohare && !find_devices("via-pmu")) { + if (macio_chips[0].type == macio_ohare) { struct macio_chip *macio = &macio_chips[0]; - MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES); - } else if (macio_chips[0].type == macio_ohare) { - struct macio_chip *macio = &macio_chips[0]; - MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + np = of_find_node_by_name(NULL, "via-pmu"); + if (np) + MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + else + MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES); + of_node_put(np); } else if (macio_chips[1].type == macio_ohare) { struct macio_chip *macio = &macio_chips[1]; MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); @@ -2833,14 +2853,13 @@ #else /* CONFIG_POWER4 */ } /* Switch airport off */ - np = find_devices("radio"); - while(np) { + for_each_node_by_name(np, "radio") { if (np && np->parent == macio_chips[0].of_node) { macio_chips[0].flags |= MACIO_FLAG_AIRPORT_ON; core99_airport_enable(np, 0, 0); } - np = np->next; } + of_node_put(np); } /* On all machines that support sound PM, switch sound off */ @@ -2860,16 +2879,12 @@ #else /* CONFIG_POWER4 */ #endif /* CONFIG_POWER4 */ /* On all machines, switch modem & serial ports off */ - np = find_devices("ch-a"); - while(np) { + for_each_node_by_name(np, "ch-a") initial_serial_shutdown(np); - np = np->next; - } - np = find_devices("ch-b"); - while(np) { + of_node_put(np); + for_each_node_by_name(np, "ch-b") initial_serial_shutdown(np); - np = np->next; - } + of_node_put(np); } void __init diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index bfc4829..5430e14 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -491,7 +491,7 @@ static struct pmac_i2c_host_kw *__init k * on all i2c keywest nodes so far ... we would have to fallback * to macio parsing if that wasn't the case */ - addrp = get_property(np, "AAPL,address", NULL); + addrp = of_get_property(np, "AAPL,address", NULL); if (addrp == NULL) { printk(KERN_ERR "low_i2c: Can't find address for %s\n", np->full_name); @@ -505,13 +505,13 @@ static struct pmac_i2c_host_kw *__init k host->timeout_timer.function = kw_i2c_timeout; host->timeout_timer.data = (unsigned long)host; - psteps = get_property(np, "AAPL,address-step", NULL); + psteps = of_get_property(np, "AAPL,address-step", NULL); steps = psteps ? (*psteps) : 0x10; for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) steps >>= 1; /* Select interface rate */ host->speed = KW_I2C_MODE_25KHZ; - prate = get_property(np, "AAPL,i2c-rate", NULL); + prate = of_get_property(np, "AAPL,i2c-rate", NULL); if (prate) switch(*prate) { case 100: host->speed = KW_I2C_MODE_100KHZ; @@ -619,7 +619,7 @@ static void __init kw_i2c_probe(void) } else { for (child = NULL; (child = of_get_next_child(np, child)) != NULL;) { - const u32 *reg = get_property(child, + const u32 *reg = of_get_property(child, "reg", NULL); if (reg == NULL) continue; @@ -905,7 +905,7 @@ static void __init smu_i2c_probe(void) if (strcmp(busnode->type, "i2c") && strcmp(busnode->type, "i2c-bus")) continue; - reg = get_property(busnode, "reg", NULL); + reg = of_get_property(busnode, "reg", NULL); if (reg == NULL) continue; @@ -950,7 +950,8 @@ struct pmac_i2c_bus *pmac_i2c_find_bus(s if (p == bus->busnode) { if (prev && bus->flags & pmac_i2c_multibus) { const u32 *reg; - reg = get_property(prev, "reg", NULL); + reg = of_get_property(prev, "reg", + NULL); if (!reg) continue; if (((*reg) >> 8) != bus->channel) @@ -971,7 +972,7 @@ EXPORT_SYMBOL_GPL(pmac_i2c_find_bus); u8 pmac_i2c_get_dev_addr(struct device_node *device) { - const u32 *reg = get_property(device, "reg", NULL); + const u32 *reg = of_get_property(device, "reg", NULL); if (reg == NULL) return 0; diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index 6fbac30..22c4ae4 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -70,11 +70,11 @@ static int __init fixup_one_level_bus_ra int len; /* For PCI<->PCI bridges or CardBus bridges, we go down */ - class_code = get_property(node, "class-code", NULL); + class_code = of_get_property(node, "class-code", NULL); if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) continue; - bus_range = get_property(node, "bus-range", &len); + bus_range = of_get_property(node, "bus-range", &len); if (bus_range != NULL && len > 2 * sizeof(int)) { if (bus_range[1] > higher) higher = bus_range[1]; @@ -100,7 +100,7 @@ static void __init fixup_bus_range(struc if (prop == NULL || prop->length < 2 * sizeof(int)) return; - bus_range = (int *)prop->value; + bus_range = prop->value; bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); } @@ -246,8 +246,8 @@ static int chaos_validate_dev(struct pci if (np == NULL) return PCIBIOS_DEVICE_NOT_FOUND; - vendor = get_property(np, "vendor-id", NULL); - device = get_property(np, "device-id", NULL); + vendor = of_get_property(np, "vendor-id", NULL); + device = of_get_property(np, "device-id", NULL); if (vendor == NULL || device == NULL) return PCIBIOS_DEVICE_NOT_FOUND; @@ -622,13 +622,14 @@ static void __init init_p2pbridge(void) /* XXX it would be better here to identify the specific PCI-PCI bridge chip we have. */ - if ((p2pbridge = find_devices("pci-bridge")) == 0 + p2pbridge = of_find_node_by_name(NULL, "pci-bridge"); + if (p2pbridge == NULL || p2pbridge->parent == NULL || strcmp(p2pbridge->parent->name, "pci") != 0) - return; + goto done; if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) { DBG("Can't find PCI infos for PCI<->PCI bridge\n"); - return; + goto done; } /* Warning: At this point, we have not yet renumbered all busses. * So we must use OF walking to find out hose @@ -636,16 +637,18 @@ static void __init init_p2pbridge(void) hose = pci_find_hose_for_OF_device(p2pbridge); if (!hose) { DBG("Can't find hose for PCI<->PCI bridge\n"); - return; + goto done; } if (early_read_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, &val) < 0) { printk(KERN_ERR "init_p2pbridge: couldn't read bridge" " control\n"); - return; + goto done; } val &= ~PCI_BRIDGE_CTL_MASTER_ABORT; early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val); +done: + of_node_put(p2pbridge); } static void __init init_second_ohare(void) @@ -691,17 +694,17 @@ static void __init fixup_nec_usb2(void) const u32 *prop; u8 bus, devfn; - prop = get_property(nec, "vendor-id", NULL); + prop = of_get_property(nec, "vendor-id", NULL); if (prop == NULL) continue; if (0x1033 != *prop) continue; - prop = get_property(nec, "device-id", NULL); + prop = of_get_property(nec, "device-id", NULL); if (prop == NULL) continue; if (0x0035 != *prop) continue; - prop = get_property(nec, "reg", NULL); + prop = of_get_property(nec, "reg", NULL); if (prop == NULL) continue; devfn = (prop[0] >> 8) & 0xff; @@ -909,7 +912,7 @@ static int __init add_bridge(struct devi has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); /* Get bus range if any */ - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); @@ -1199,8 +1202,7 @@ #ifdef CONFIG_BLK_DEV_IDE } #endif /* CONFIG_BLK_DEV_IDE */ - nd = find_devices("firewire"); - while (nd) { + for_each_node_by_name(nd, "firewire") { if (nd->parent && (device_is_compatible(nd, "pci106b,18") || device_is_compatible(nd, "pci106b,30") || device_is_compatible(nd, "pci11c1,5811")) @@ -1208,15 +1210,14 @@ #endif /* CONFIG_BLK_DEV_IDE */ pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0); pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0); } - nd = nd->next; } - nd = find_devices("ethernet"); - while (nd) { + of_node_put(nd); + for_each_node_by_name(nd, "ethernet") { if (nd->parent && device_is_compatible(nd, "gmac") && device_is_compatible(nd->parent, "uni-north")) pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0); - nd = nd->next; } + of_node_put(nd); } #ifdef CONFIG_PPC32 diff --git a/arch/powerpc/platforms/powermac/pfunc_base.c b/arch/powerpc/platforms/powermac/pfunc_base.c index 5c6c15c..45d54b9 100644 --- a/arch/powerpc/platforms/powermac/pfunc_base.c +++ b/arch/powerpc/platforms/powermac/pfunc_base.c @@ -114,7 +114,7 @@ static void macio_gpio_init_one(struct m * we just create them all */ for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) { - const u32 *reg = get_property(gp, "reg", NULL); + const u32 *reg = of_get_property(gp, "reg", NULL); unsigned long offset; if (reg == NULL) continue; diff --git a/arch/powerpc/platforms/powermac/pfunc_core.c b/arch/powerpc/platforms/powermac/pfunc_core.c index 7651f27..8543423 100644 --- a/arch/powerpc/platforms/powermac/pfunc_core.c +++ b/arch/powerpc/platforms/powermac/pfunc_core.c @@ -692,8 +692,7 @@ #define PP_PREFIX "platform-do-" name = pp->name + plen; if (strlen(name) && pp->length >= 12) count += pmf_add_function_prop(dev, driverdata, name, - (u32 *)pp->value, - pp->length); + pp->value, pp->length); } return count; } @@ -821,7 +820,7 @@ struct pmf_function *__pmf_find_function * one, then we fallback to a direct call attempt */ snprintf(fname, 63, "platform-%s", name); - prop = get_property(target, fname, NULL); + prop = of_get_property(target, fname, NULL); if (prop == NULL) goto find_it; ph = *prop; diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 5e5c0e4..ae5097a 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -482,14 +482,14 @@ static struct mpic * __init pmac_setup_o pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0); flags |= MPIC_WANTS_RESET; - if (get_property(np, "big-endian", NULL)) + if (of_get_property(np, "big-endian", NULL)) flags |= MPIC_BIG_ENDIAN; /* Primary Big Endian means HT interrupts. This is quite dodgy * but works until I find a better way */ if (master && (flags & MPIC_BIG_ENDIAN)) - flags |= MPIC_BROKEN_U3; + flags |= MPIC_U3_HT_IRQS; mpic = mpic_alloc(np, r.start, flags, 0, 0, name); if (mpic == NULL) @@ -510,7 +510,7 @@ static int __init pmac_pic_probe_mpic(vo for (np = NULL; (np = of_find_node_by_type(np, "open-pic")) != NULL;) { if (master == NULL && - get_property(np, "interrupts", NULL) == NULL) + of_get_property(np, "interrupts", NULL) == NULL) master = of_node_get(np); else if (slave == NULL) slave = of_node_get(np); @@ -575,7 +575,7 @@ void __init pmac_pic_init(void) #ifdef CONFIG_PPC32 if (!pmac_newworld) flags |= OF_IMAP_OLDWORLD_MAC; - if (get_property(of_chosen, "linux,bootx", NULL) != NULL) + if (of_get_property(of_chosen, "linux,bootx", NULL) != NULL) flags |= OF_IMAP_NO_PHANDLE; #endif /* CONFIG_PPC_32 */ diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 651fa42..b820cab 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -42,7 +42,6 @@ #include #include #include #include -#include #include #include #include @@ -135,12 +134,12 @@ static void pmac_show_cpuinfo(struct seq seq_printf(m, "machine\t\t: "); np = of_find_node_by_path("/"); if (np != NULL) { - pp = get_property(np, "model", NULL); + pp = of_get_property(np, "model", NULL); if (pp != NULL) seq_printf(m, "%s\n", pp); else seq_printf(m, "PowerMac\n"); - pp = get_property(np, "compatible", &plen); + pp = of_get_property(np, "compatible", &plen); if (pp != NULL) { seq_printf(m, "motherboard\t:"); while (plen > 0) { @@ -164,11 +163,13 @@ static void pmac_show_cpuinfo(struct seq if (np == NULL) np = of_find_node_by_type(NULL, "cache"); if (np != NULL) { - const unsigned int *ic = get_property(np, "i-cache-size", NULL); - const unsigned int *dc = get_property(np, "d-cache-size", NULL); + const unsigned int *ic = + of_get_property(np, "i-cache-size", NULL); + const unsigned int *dc = + of_get_property(np, "d-cache-size", NULL); seq_printf(m, "L2 cache\t:"); has_l2cache = 1; - if (get_property(np, "cache-unified", NULL) != 0 && dc) { + if (of_get_property(np, "cache-unified", NULL) != 0 && dc) { seq_printf(m, " %dK unified", *dc / 1024); } else { if (ic) @@ -177,7 +178,7 @@ static void pmac_show_cpuinfo(struct seq seq_printf(m, "%s %dK data", (ic? " +": ""), *dc / 1024); } - pp = get_property(np, "ram-type", NULL); + pp = of_get_property(np, "ram-type", NULL); if (pp) seq_printf(m, " %s", pp); seq_printf(m, "\n"); @@ -192,8 +193,11 @@ static void pmac_show_cpuinfo(struct seq #ifndef CONFIG_ADB_CUDA int find_via_cuda(void) { - if (!find_devices("via-cuda")) + struct device_node *dn = of_find_node_by_name(NULL, "via-cuda"); + + if (!dn) return 0; + of_node_put(dn); printk("WARNING ! Your machine is CUDA-based but your kernel\n"); printk(" wasn't compiled with CONFIG_ADB_CUDA option !\n"); return 0; @@ -203,8 +207,11 @@ #endif #ifndef CONFIG_ADB_PMU int find_via_pmu(void) { - if (!find_devices("via-pmu")) + struct device_node *dn = of_find_node_by_name(NULL, "via-pmu"); + + if (!dn) return 0; + of_node_put(dn); printk("WARNING ! Your machine is PMU-based but your kernel\n"); printk(" wasn't compiled with CONFIG_ADB_PMU option !\n"); return 0; @@ -224,6 +231,8 @@ static volatile u32 *sysctrl_regs; static void __init ohare_init(void) { + struct device_node *dn; + /* this area has the CPU identification register and some registers used by smp boards */ sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000); @@ -233,7 +242,9 @@ static void __init ohare_init(void) * We assume that we have a PSX memory controller iff * we have an ohare I/O controller. */ - if (find_devices("ohare") != NULL) { + dn = of_find_node_by_name(NULL, "ohare"); + if (dn) { + of_node_put(dn); if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) { if (sysctrl_regs[4] & 0x10) sysctrl_regs[4] |= 0x04000020; @@ -249,18 +260,19 @@ static void __init l2cr_init(void) { /* Checks "l2cr-value" property in the registry */ if (cpu_has_feature(CPU_FTR_L2CR)) { - struct device_node *np = find_devices("cpus"); + struct device_node *np = of_find_node_by_name(NULL, "cpus"); if (np == 0) - np = find_type_devices("cpu"); + np = of_find_node_by_type(NULL, "cpu"); if (np != 0) { const unsigned int *l2cr = - get_property(np, "l2cr-value", NULL); + of_get_property(np, "l2cr-value", NULL); if (l2cr != 0) { ppc_override_l2cr = 1; ppc_override_l2cr_value = *l2cr; _set_L2CR(0); _set_L2CR(ppc_override_l2cr_value); } + of_node_put(np); } } @@ -286,7 +298,7 @@ static void __init pmac_setup_arch(void) loops_per_jiffy = 50000000 / HZ; cpu = of_find_node_by_type(NULL, "cpu"); if (cpu != NULL) { - fp = get_property(cpu, "clock-frequency", NULL); + fp = of_get_property(cpu, "clock-frequency", NULL); if (fp != NULL) { if (pvr >= 0x30 && pvr < 0x80) /* PPC970 etc. */ @@ -303,7 +315,7 @@ static void __init pmac_setup_arch(void) /* See if newworld or oldworld */ for (ic = NULL; (ic = of_find_all_nodes(ic)) != NULL; ) - if (get_property(ic, "interrupt-controller", NULL)) + if (of_get_property(ic, "interrupt-controller", NULL)) break; if (ic) { pmac_newworld = 1; @@ -341,8 +353,15 @@ #endif #ifdef CONFIG_SMP /* Check for Core99 */ - if (find_devices("uni-n") || find_devices("u3") || find_devices("u4")) + ic = of_find_node_by_name(NULL, "uni-n"); + if (!ic) + ic = of_find_node_by_name(NULL, "u3"); + if (!ic) + ic = of_find_node_by_name(NULL, "u4"); + if (ic) { + of_node_put(ic); smp_ops = &core99_smp_ops; + } #ifdef CONFIG_PPC32 else smp_ops = &psurge_smp_ops; @@ -616,15 +635,6 @@ #ifdef CONFIG_PPC64 #endif } -/* - * pmac has no legacy IO, anything calling this function has to - * fail or bad things will happen - */ -static int pmac_check_legacy_ioport(unsigned int baseport) -{ - return -ENODEV; -} - static int __init pmac_declare_of_platform_devices(void) { struct device_node *np; @@ -736,7 +746,6 @@ define_machine(powermac) { .get_rtc_time = pmac_get_rtc_time, .calibrate_decr = pmac_calibrate_decr, .feature_call = pmac_do_feature_call, - .check_legacy_ioport = pmac_check_legacy_ioport, .progress = udbg_progress, #ifdef CONFIG_PPC64 .pci_probe_mode = pmac_pci_probe_mode, diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index d73fb73..6f32c4e 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -264,6 +264,7 @@ static void __init psurge_quad_init(void static int __init smp_psurge_probe(void) { int i, ncpus; + struct device_node *dn; /* We don't do SMP on the PPC601 -- paulus */ if (PVR_VER(mfspr(SPRN_PVR)) == 1) @@ -279,8 +280,10 @@ static int __init smp_psurge_probe(void) * in the hammerhead memory controller in the case of the * dual-cpu powersurge board. -- paulus. */ - if (find_devices("hammerhead") == NULL) + dn = of_find_node_by_name(NULL, "hammerhead"); + if (dn == NULL) return 1; + of_node_put(dn); hhead_base = ioremap(HAMMERHEAD_BASE, 0x800); quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024); @@ -567,7 +570,7 @@ static void __init smp_core99_setup_i2c_ pmac_tb_clock_chip_host = pmac_i2c_find_bus(cc); if (pmac_tb_clock_chip_host == NULL) continue; - reg = get_property(cc, "reg", NULL); + reg = of_get_property(cc, "reg", NULL); if (reg == NULL) continue; switch (*reg) { @@ -695,7 +698,7 @@ #ifdef CONFIG_PPC64 struct device_node *cpus = of_find_node_by_path("/cpus"); if (cpus && - get_property(cpus, "platform-cpu-timebase", NULL)) { + of_get_property(cpus, "platform-cpu-timebase", NULL)) { pmac_tb_freeze = smp_core99_pfunc_tb_freeze; printk(KERN_INFO "Processor timebase sync using" " platform function\n"); @@ -712,7 +715,7 @@ #else /* CONFIG_PPC64 */ core99_tb_gpio = KL_GPIO_TB_ENABLE; /* default value */ cpu = of_find_node_by_type(NULL, "cpu"); if (cpu != NULL) { - tbprop = get_property(cpu, "timebase-enable", NULL); + tbprop = of_get_property(cpu, "timebase-enable", NULL); if (tbprop) core99_tb_gpio = *tbprop; of_node_put(cpu); diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c index a417390..bf9da56 100644 --- a/arch/powerpc/platforms/powermac/time.c +++ b/arch/powerpc/platforms/powermac/time.c @@ -297,49 +297,11 @@ int __init via_calibrate_decr(void) } #endif -#ifdef CONFIG_PM -/* - * Reset the time after a sleep. - */ -static int -time_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - static unsigned long time_diff; - unsigned long flags; - unsigned long seq; - struct timespec tv; - - switch (when) { - case PBOOK_SLEEP_NOW: - do { - seq = read_seqbegin_irqsave(&xtime_lock, flags); - time_diff = xtime.tv_sec - pmac_get_boot_time(); - } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); - break; - case PBOOK_WAKE: - tv.tv_sec = pmac_get_boot_time() + time_diff; - tv.tv_nsec = 0; - do_settimeofday(&tv); - break; - } - return PBOOK_SLEEP_OK; -} - -static struct pmu_sleep_notifier time_sleep_notifier = { - time_sleep_notify, SLEEP_LEVEL_MISC, -}; -#endif /* CONFIG_PM */ - /* * Query the OF and get the decr frequency. */ void __init pmac_calibrate_decr(void) { -#if defined(CONFIG_PM) && defined(CONFIG_ADB_PMU) - /* XXX why here? */ - pmu_register_sleep_notifier(&time_sleep_notifier); -#endif - generic_calibrate_decr(); #ifdef CONFIG_PPC32 diff --git a/arch/powerpc/platforms/powermac/udbg_scc.c b/arch/powerpc/platforms/powermac/udbg_scc.c index 379db05..47de4d3 100644 --- a/arch/powerpc/platforms/powermac/udbg_scc.c +++ b/arch/powerpc/platforms/powermac/udbg_scc.c @@ -81,7 +81,7 @@ void udbg_scc_init(int force_scc) macio = of_get_parent(escc); if (macio == NULL) goto bail; - path = get_property(of_chosen, "linux,stdout-path", NULL); + path = of_get_property(of_chosen, "linux,stdout-path", NULL); if (path != NULL) stdout = of_find_node_by_path(path); for (ch = NULL; (ch = of_get_next_child(escc, ch)) != NULL;) { @@ -96,13 +96,13 @@ void udbg_scc_init(int force_scc) ch = ch_def ? ch_def : ch_a; /* Get address within mac-io ASIC */ - reg = get_property(escc, "reg", NULL); + reg = of_get_property(escc, "reg", NULL); if (reg == NULL) goto bail; addr = reg[0]; /* Get address of mac-io PCI itself */ - reg = get_property(macio, "assigned-addresses", NULL); + reg = of_get_property(macio, "assigned-addresses", NULL); if (reg == NULL) goto bail; addr += reg[2]; diff --git a/arch/powerpc/platforms/prep/Kconfig b/arch/powerpc/platforms/prep/Kconfig index 673ac47..29d4112 100644 --- a/arch/powerpc/platforms/prep/Kconfig +++ b/arch/powerpc/platforms/prep/Kconfig @@ -1,3 +1,12 @@ +config PPC_PREP + bool "PowerPC Reference Platform (PReP) based machines" + depends on PPC_MULTIPLATFORM && PPC32 && BROKEN + select MPIC + select PPC_I8259 + select PPC_INDIRECT_PCI + select PPC_UDBG_16550 + select PPC_NATIVE + default n config PREP_RESIDUAL bool "Support for PReP Residual Data" diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 1a481a6..40f0008 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -1,3 +1,19 @@ +config PPC_PS3 + bool "Sony PS3 (incomplete)" + depends on PPC_MULTIPLATFORM && PPC64 + select PPC_CELL + select USB_ARCH_HAS_OHCI + select USB_OHCI_LITTLE_ENDIAN + select USB_OHCI_BIG_ENDIAN_MMIO + select USB_ARCH_HAS_EHCI + select USB_EHCI_BIG_ENDIAN_MMIO + help + This option enables support for the Sony PS3 game console + and other platforms using the PS3 hypervisor. + Support for this platform is not yet complete, so + enabling this will not result in a bootable kernel on a + PS3 system. + menu "PS3 Platform Options" depends on PPC_PS3 diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index e12e59f..ea60c45 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -39,7 +39,7 @@ static unsigned long htab_addr; static unsigned char *bolttab; static unsigned char *inusetab; -static spinlock_t ps3_bolttab_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(ps3_bolttab_lock); #define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \ _debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index a57032c..16e4e40 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -1,3 +1,13 @@ +config PPC_PSERIES + depends on PPC_MULTIPLATFORM && PPC64 + bool "IBM pSeries & new (POWER5-based) iSeries" + select MPIC + select PPC_I8259 + select PPC_RTAS + select RTAS_ERROR_LOGGING + select PPC_UDBG_16550 + select PPC_NATIVE + default y config PPC_SPLPAR depends on PPC_PSERIES diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 2dfd050..90235d5 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -2,14 +2,15 @@ ifeq ($(CONFIG_PPC64),y) EXTRA_CFLAGS += -mno-minimal-toc endif -obj-y := pci.o lpar.o hvCall.o nvram.o reconfig.o \ - setup.o iommu.o ras.o rtasd.o pci_dlpar.o \ +obj-y := lpar.o hvCall.o nvram.o reconfig.o \ + setup.o iommu.o ras.o rtasd.o \ firmware.o power.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o obj-$(CONFIG_KEXEC) += kexec.o +obj-$(CONFIG_PCI) += pci.o pci_dlpar.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 6cedbc0..48fbd44 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -74,7 +74,10 @@ #undef DEBUG * is broken and panic. This sets the threshold for how many read * attempts we allow before panicking. */ -#define EEH_MAX_FAILS 100000 +#define EEH_MAX_FAILS 2100000 + +/* Time to wait for a PCI slot to retport status, in milliseconds */ +#define PCI_BUS_RESET_WAIT_MSEC (60*1000) /* RTAS tokens */ static int ibm_set_eeh_option; @@ -83,6 +86,7 @@ static int ibm_read_slot_reset_state; static int ibm_read_slot_reset_state2; static int ibm_slot_error_detail; static int ibm_get_config_addr_info; +static int ibm_get_config_addr_info2; static int ibm_configure_bridge; int eeh_subsystem_enabled; @@ -168,6 +172,55 @@ static int read_slot_reset_state(struct } /** + * eeh_wait_for_slot_status - returns error status of slot + * @pdn pci device node + * @max_wait_msecs maximum number to millisecs to wait + * + * Return negative value if a permanent error, else return + * Partition Endpoint (PE) status value. + * + * If @max_wait_msecs is positive, then this routine will + * sleep until a valid status can be obtained, or until + * the max allowed wait time is exceeded, in which case + * a -2 is returned. + */ +int +eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) +{ + int rc; + int rets[3]; + int mwait; + + while (1) { + rc = read_slot_reset_state(pdn, rets); + if (rc) return rc; + if (rets[1] == 0) return -1; /* EEH is not supported */ + + if (rets[0] != 5) return rets[0]; /* return actual status */ + + if (rets[2] == 0) return -1; /* permanently unavailable */ + + if (max_wait_msecs <= 0) return -1; + + mwait = rets[2]; + if (mwait <= 0) { + printk (KERN_WARNING + "EEH: Firmware returned bad wait value=%d\n", mwait); + mwait = 1000; + } else if (mwait > 300*1000) { + printk (KERN_WARNING + "EEH: Firmware is taking too long, time=%d\n", mwait); + mwait = 300*1000; + } + max_wait_msecs -= mwait; + msleep (mwait); + } + + printk(KERN_WARNING "EEH: Timed out waiting for slot status\n"); + return -2; +} + +/** * eeh_token_to_phys - convert EEH address token to phys address * @token i/o token, should be address in the form 0xA.... */ @@ -229,7 +282,7 @@ void eeh_mark_slot (struct device_node * dn = find_device_pe (dn); /* Back up one, since config addrs might be shared */ - if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr) + if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) dn = dn->parent; PCI_DN(dn)->eeh_mode |= mode_flag; @@ -263,7 +316,7 @@ void eeh_clear_slot (struct device_node dn = find_device_pe (dn); /* Back up one, since config addrs might be shared */ - if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr) + if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) dn = dn->parent; PCI_DN(dn)->eeh_mode &= ~mode_flag; @@ -293,7 +346,6 @@ int eeh_dn_check_failure(struct device_n int rets[3]; unsigned long flags; struct pci_dn *pdn; - enum pci_channel_state state; int rc = 0; total_mmio_ffs++; @@ -367,25 +419,25 @@ #endif goto dn_unlock; } - /* If EEH is not supported on this device, punt. */ - if (rets[1] != 1) { - printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n", - ret, dn->full_name); + /* Note that config-io to empty slots may fail; + * they are empty when they don't have children. */ + if ((rets[0] == 5) && (dn->child == NULL)) { false_positives++; rc = 0; goto dn_unlock; } - /* If not the kind of error we know about, punt. */ - if (rets[0] != 2 && rets[0] != 4 && rets[0] != 5) { + /* If EEH is not supported on this device, punt. */ + if (rets[1] != 1) { + printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n", + ret, dn->full_name); false_positives++; rc = 0; goto dn_unlock; } - /* Note that config-io to empty slots may fail; - * we recognize empty because they don't have children. */ - if ((rets[0] == 5) && (dn->child == NULL)) { + /* If not the kind of error we know about, punt. */ + if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) { false_positives++; rc = 0; goto dn_unlock; @@ -399,17 +451,12 @@ #endif eeh_mark_slot (dn, EEH_MODE_ISOLATED); spin_unlock_irqrestore(&confirm_error_lock, flags); - state = pci_channel_io_normal; - if ((rets[0] == 2) || (rets[0] == 4)) - state = pci_channel_io_frozen; - if (rets[0] == 5) - state = pci_channel_io_perm_failure; - eeh_send_failure_event (dn, dev, state, rets[2]); + eeh_send_failure_event (dn, dev); /* Most EEH events are due to device driver bugs. Having * a stack trace will help the device-driver authors figure * out what happened. So print that out. */ - if (rets[0] != 5) dump_stack(); + dump_stack(); return 1; dn_unlock: @@ -458,38 +505,6 @@ EXPORT_SYMBOL(eeh_check_failure); /* The code below deals with error recovery */ /** - * eeh_slot_availability - returns error status of slot - * @pdn pci device node - * - * Return negative value if a permanent error, else return - * a number of milliseconds to wait until the PCI slot is - * ready to be used. - */ -static int -eeh_slot_availability(struct pci_dn *pdn) -{ - int rc; - int rets[3]; - - rc = read_slot_reset_state(pdn, rets); - - if (rc) return rc; - - if (rets[1] == 0) return -1; /* EEH is not supported */ - if (rets[0] == 0) return 0; /* Oll Korrect */ - if (rets[0] == 5) { - if (rets[2] == 0) return -1; /* permanently unavailable */ - return rets[2]; /* number of millisecs to wait */ - } - if (rets[0] == 1) - return 250; - - printk (KERN_ERR "EEH: Slot unavailable: rc=%d, rets=%d %d %d\n", - rc, rets[0], rets[1], rets[2]); - return -2; -} - -/** * rtas_pci_enable - enable MMIO or DMA transfers for this slot * @pdn pci device node */ @@ -512,9 +527,13 @@ rtas_pci_enable(struct pci_dn *pdn, int function); if (rc) - printk(KERN_WARNING "EEH: Cannot enable function %d, err=%d dn=%s\n", + printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n", function, rc, pdn->node->full_name); + rc = eeh_wait_for_slot_status (pdn, PCI_BUS_RESET_WAIT_MSEC); + if ((rc == 4) && (function == EEH_THAW_MMIO)) + return 0; + return rc; } @@ -595,36 +614,24 @@ int rtas_set_slot_reset(struct pci_dn *p { int i, rc; - __rtas_set_slot_reset(pdn); + /* Take three shots at resetting the bus */ + for (i=0; i<3; i++) { + __rtas_set_slot_reset(pdn); - /* Now double check with the firmware to make sure the device is - * ready to be used; if not, wait for recovery. */ - for (i=0; i<10; i++) { - rc = eeh_slot_availability (pdn); + rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); if (rc == 0) return 0; - if (rc == -2) { - printk (KERN_ERR "EEH: failed (%d) to reset slot %s\n", - i, pdn->node->full_name); - __rtas_set_slot_reset(pdn); - continue; - } - if (rc < 0) { printk (KERN_ERR "EEH: unrecoverable slot failure %s\n", pdn->node->full_name); return -1; } - - msleep (rc+100); + printk (KERN_ERR "EEH: bus reset %d failed on slot %s\n", + i+1, pdn->node->full_name); } - rc = eeh_slot_availability (pdn); - if (rc) - printk (KERN_ERR "EEH: timeout resetting slot %s\n", pdn->node->full_name); - - return rc; + return -1; } /* ------------------------------------------------------- */ @@ -744,16 +751,48 @@ struct eeh_early_enable_info { unsigned int buid_lo; }; +static int get_pe_addr (int config_addr, + struct eeh_early_enable_info *info) +{ + unsigned int rets[3]; + int ret; + + /* Use latest config-addr token on power6 */ + if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { + /* Make sure we have a PE in hand */ + ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets, + config_addr, info->buid_hi, info->buid_lo, 1); + if (ret || (rets[0]==0)) + return 0; + + ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets, + config_addr, info->buid_hi, info->buid_lo, 0); + if (ret) + return 0; + return rets[0]; + } + + /* Use older config-addr token on power5 */ + if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets, + config_addr, info->buid_hi, info->buid_lo, 0); + if (ret) + return 0; + return rets[0]; + } + return 0; +} + /* Enable eeh for the given device node. */ static void *early_enable_eeh(struct device_node *dn, void *data) { unsigned int rets[3]; struct eeh_early_enable_info *info = data; int ret; - const char *status = get_property(dn, "status", NULL); - const u32 *class_code = get_property(dn, "class-code", NULL); - const u32 *vendor_id = get_property(dn, "vendor-id", NULL); - const u32 *device_id = get_property(dn, "device-id", NULL); + const char *status = of_get_property(dn, "status", NULL); + const u32 *class_code = of_get_property(dn, "class-code", NULL); + const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL); + const u32 *device_id = of_get_property(dn, "device-id", NULL); const u32 *regs; int enable; struct pci_dn *pdn = PCI_DN(dn); @@ -796,7 +835,7 @@ #endif /* Ok... see if this device supports EEH. Some do, some don't, * and the only way to find out is to check each and every one. */ - regs = get_property(dn, "reg", NULL); + regs = of_get_property(dn, "reg", NULL); if (regs) { /* First register entry is addr (00BBSS00) */ /* Try to enable eeh */ @@ -810,15 +849,7 @@ #endif /* If the newer, better, ibm,get-config-addr-info is supported, * then use that instead. */ - pdn->eeh_pe_config_addr = 0; - if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { - ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets, - pdn->eeh_config_addr, - info->buid_hi, info->buid_lo, - 0); - if (ret == 0) - pdn->eeh_pe_config_addr = rets[0]; - } + pdn->eeh_pe_config_addr = get_pe_addr(pdn->eeh_config_addr, info); /* Some older systems (Power4) allow the * ibm,set-eeh-option call to succeed even on nodes @@ -889,6 +920,7 @@ void __init eeh_init(void) ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info"); + ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2"); ibm_configure_bridge = rtas_token ("ibm,configure-bridge"); if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index a4c0bf8..3170e00 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -158,7 +158,8 @@ static void eeh_report_reset(struct pci_ return; rc = driver->err_handler->slot_reset(dev); - if (*res == PCI_ERS_RESULT_NONE) *res = rc; + if ((*res == PCI_ERS_RESULT_NONE) || + (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc; if (*res == PCI_ERS_RESULT_DISCONNECT && rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; } @@ -248,6 +249,7 @@ static void eeh_report_failure(struct pc static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) { + struct device_node *dn; int cnt, rc; /* pcibios will clear the counter; save the value */ @@ -263,23 +265,20 @@ static int eeh_reset_device (struct pci_ if (rc) return rc; - /* New-style config addrs might be shared across multiple devices, - * Walk over all functions on this device */ - if (pe_dn->eeh_pe_config_addr) { - struct device_node *pe = pe_dn->node; - pe = pe->parent->child; - while (pe) { - struct pci_dn *ppe = PCI_DN(pe); - if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) { - rtas_configure_bridge(ppe); - eeh_restore_bars(ppe); - } - pe = pe->sibling; + /* Walk over all functions on this device. */ + dn = pe_dn->node; + if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) + dn = dn->parent->child; + + while (dn) { + struct pci_dn *ppe = PCI_DN(dn); + /* On Power4, always true because eeh_pe_config_addr=0 */ + if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) { + rtas_configure_bridge(ppe); + eeh_restore_bars(ppe); } - } else { - rtas_configure_bridge(pe_dn); - eeh_restore_bars(pe_dn); - } + dn = dn->sibling; + } /* Give the system 5 seconds to finish running the user-space * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes, @@ -299,7 +298,7 @@ static int eeh_reset_device (struct pci_ /* The longest amount of time to wait for a pci device * to come back on line, in seconds. */ -#define MAX_WAIT_FOR_RECOVERY 15 +#define MAX_WAIT_FOR_RECOVERY 150 struct pci_dn * handle_eeh_events (struct eeh_event *event) { @@ -315,14 +314,14 @@ struct pci_dn * handle_eeh_events (struc if (!frozen_dn) { - location = get_property(event->dn, "ibm,loc-code", NULL); + location = of_get_property(event->dn, "ibm,loc-code", NULL); location = location ? location : "unknown"; printk(KERN_ERR "EEH: Error: Cannot find partition endpoint " "for location=%s pci addr=%s\n", location, pci_name(event->dev)); return NULL; } - location = get_property(frozen_dn, "ibm,loc-code", NULL); + location = of_get_property(frozen_dn, "ibm,loc-code", NULL); location = location ? location : "unknown"; /* There are two different styles for coming up with the PE. @@ -341,13 +340,6 @@ struct pci_dn * handle_eeh_events (struc return NULL; } -#if 0 - /* We may get "permanent failure" messages on empty slots. - * These are false alarms. Empty slots have no child dn. */ - if ((event->state == pci_channel_io_perm_failure) && (frozen_device == NULL)) - return; -#endif - frozen_pdn = PCI_DN(frozen_dn); frozen_pdn->eeh_freeze_count++; @@ -362,13 +354,12 @@ #endif if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES) goto excess_failures; - /* If the reset state is a '5' and the time to reset is 0 (infinity) - * or is more then 15 seconds, then mark this as a permanent failure. - */ - if ((event->state == pci_channel_io_perm_failure) && - ((event->time_unavail <= 0) || - (event->time_unavail > MAX_WAIT_FOR_RECOVERY*1000))) + /* Get the current PCI slot state. */ + rc = eeh_wait_for_slot_status (frozen_pdn, MAX_WAIT_FOR_RECOVERY*1000); + if (rc < 0) { + printk(KERN_WARNING "EEH: Permanent failure\n"); goto hard_fail; + } eeh_slot_error_detail(frozen_pdn, 1 /* Temporary Error */); printk(KERN_WARNING @@ -390,14 +381,18 @@ #endif */ if (result == PCI_ERS_RESULT_NONE) { rc = eeh_reset_device(frozen_pdn, frozen_bus); - if (rc) + if (rc) { + printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc); goto hard_fail; + } } /* If all devices reported they can proceed, then re-enable MMIO */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { rc = rtas_pci_enable(frozen_pdn, EEH_THAW_MMIO); + if (rc < 0) + goto hard_fail; if (rc) { result = PCI_ERS_RESULT_NEED_RESET; } else { @@ -410,6 +405,8 @@ #endif if (result == PCI_ERS_RESULT_CAN_RECOVER) { rc = rtas_pci_enable(frozen_pdn, EEH_THAW_DMA); + if (rc < 0) + goto hard_fail; if (rc) result = PCI_ERS_RESULT_NEED_RESET; else @@ -417,21 +414,28 @@ #endif } /* If any device has a hard failure, then shut off everything. */ - if (result == PCI_ERS_RESULT_DISCONNECT) + if (result == PCI_ERS_RESULT_DISCONNECT) { + printk(KERN_WARNING "EEH: Device driver gave up\n"); goto hard_fail; + } /* If any device called out for a reset, then reset the slot */ if (result == PCI_ERS_RESULT_NEED_RESET) { rc = eeh_reset_device(frozen_pdn, NULL); - if (rc) + if (rc) { + printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc); goto hard_fail; + } result = PCI_ERS_RESULT_NONE; pci_walk_bus(frozen_bus, eeh_report_reset, &result); } /* All devices should claim they have recovered by now. */ - if (result != PCI_ERS_RESULT_RECOVERED) + if ((result != PCI_ERS_RESULT_RECOVERED) && + (result != PCI_ERS_RESULT_NONE)) { + printk(KERN_WARNING "EEH: Not recovered\n"); goto hard_fail; + } /* Tell all device drivers that they can resume operations */ pci_walk_bus(frozen_bus, eeh_report_resume, NULL); diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c index 49037ed..ddb80f5 100644 --- a/arch/powerpc/platforms/pseries/eeh_event.c +++ b/arch/powerpc/platforms/pseries/eeh_event.c @@ -118,9 +118,7 @@ static void eeh_thread_launcher(struct w * (from a workqueue). */ int eeh_send_failure_event (struct device_node *dn, - struct pci_dev *dev, - enum pci_channel_state state, - int time_unavail) + struct pci_dev *dev) { unsigned long flags; struct eeh_event *event; @@ -128,7 +126,7 @@ int eeh_send_failure_event (struct devic if (!mem_init_done) { printk(KERN_ERR "EEH: event during early boot not handled\n"); - location = get_property(dn, "ibm,loc-code", NULL); + location = of_get_property(dn, "ibm,loc-code", NULL); printk(KERN_ERR "EEH: device node = %s\n", dn->full_name); printk(KERN_ERR "EEH: PCI location = %s\n", location); return 1; @@ -144,8 +142,6 @@ int eeh_send_failure_event (struct devic event->dn = dn; event->dev = dev; - event->state = state; - event->time_unavail = time_unavail; /* We may or may not be called in an interrupt context */ spin_lock_irqsave(&eeh_eventlist_lock, flags); diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c index 90522e3..29bf83b 100644 --- a/arch/powerpc/platforms/pseries/firmware.c +++ b/arch/powerpc/platforms/pseries/firmware.c @@ -80,7 +80,7 @@ void __init fw_feature_init(void) goto out; } - hypertas = get_property(dn, "ibm,hypertas-functions", &len); + hypertas = of_get_property(dn, "ibm,hypertas-functions", &len); if (hypertas == NULL) goto out; diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index f460b9c..9711eb0 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -143,7 +143,7 @@ static int pseries_add_processor(struct int err = -ENOSPC, len, nthreads, i; const u32 *intserv; - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &len); if (!intserv) return 0; @@ -203,7 +203,7 @@ static void pseries_remove_processor(str int len, nthreads, i; const u32 *intserv; - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &len); if (!intserv) return; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index e6653a8..66665c8 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -242,6 +242,7 @@ static unsigned long tce_get_pSeriesLP(s return tce_ret; } +#ifdef CONFIG_PCI static void iommu_table_setparms(struct pci_controller *phb, struct device_node *dn, struct iommu_table *tbl) @@ -252,8 +253,8 @@ static void iommu_table_setparms(struct node = (struct device_node *)phb->arch_data; - basep = get_property(node, "linux,tce-base", NULL); - sizep = get_property(node, "linux,tce-size", NULL); + basep = of_get_property(node, "linux,tce-base", NULL); + sizep = of_get_property(node, "linux,tce-size", NULL); if (basep == NULL || sizep == NULL) { printk(KERN_ERR "PCI_DMA: iommu_table_setparms: %s has " "missing tce entries !\n", dn->full_name); @@ -403,7 +404,7 @@ static void pci_dma_bus_setup_pSeriesLP( /* Find nearest ibm,dma-window, walking up the device tree */ for (pdn = dn; pdn != NULL; pdn = pdn->parent) { - dma_window = get_property(pdn, "ibm,dma-window", NULL); + dma_window = of_get_property(pdn, "ibm,dma-window", NULL); if (dma_window != NULL) break; } @@ -478,29 +479,6 @@ static void pci_dma_dev_setup_pSeries(st pci_name(dev)); } -static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node) -{ - int err = NOTIFY_OK; - struct device_node *np = node; - struct pci_dn *pci = PCI_DN(np); - - switch (action) { - case PSERIES_RECONFIG_REMOVE: - if (pci && pci->iommu_table && - get_property(np, "ibm,dma-window", NULL)) - iommu_free_table(np); - break; - default: - err = NOTIFY_DONE; - break; - } - return err; -} - -static struct notifier_block iommu_reconfig_nb = { - .notifier_call = iommu_reconfig_notifier, -}; - static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) { struct device_node *pdn, *dn; @@ -521,7 +499,7 @@ static void pci_dma_dev_setup_pSeriesLP( for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table; pdn = pdn->parent) { - dma_window = get_property(pdn, "ibm,dma-window", NULL); + dma_window = of_get_property(pdn, "ibm,dma-window", NULL); if (dma_window) break; } @@ -554,15 +532,44 @@ static void pci_dma_dev_setup_pSeriesLP( dev->dev.archdata.dma_data = pci->iommu_table; } +#else /* CONFIG_PCI */ +#define pci_dma_bus_setup_pSeries NULL +#define pci_dma_dev_setup_pSeries NULL +#define pci_dma_bus_setup_pSeriesLP NULL +#define pci_dma_dev_setup_pSeriesLP NULL +#endif /* !CONFIG_PCI */ + +static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node) +{ + int err = NOTIFY_OK; + struct device_node *np = node; + struct pci_dn *pci = PCI_DN(np); + + switch (action) { + case PSERIES_RECONFIG_REMOVE: + if (pci && pci->iommu_table && + of_get_property(np, "ibm,dma-window", NULL)) + iommu_free_table(np); + break; + default: + err = NOTIFY_DONE; + break; + } + return err; +} + +static struct notifier_block iommu_reconfig_nb = { + .notifier_call = iommu_reconfig_notifier, +}; /* These are called very early. */ void iommu_init_early_pSeries(void) { - if (of_chosen && get_property(of_chosen, "linux,iommu-off", NULL)) { + if (of_chosen && of_get_property(of_chosen, "linux,iommu-off", NULL)) { /* Direct I/O, IOMMU off */ ppc_md.pci_dma_dev_setup = NULL; ppc_md.pci_dma_bus_setup = NULL; - pci_dma_ops = &dma_direct_ops; + set_pci_dma_ops(&dma_direct_ops); return; } @@ -588,6 +595,6 @@ void iommu_init_early_pSeries(void) pSeries_reconfig_notifier_register(&iommu_reconfig_nb); - pci_dma_ops = &dma_iommu_ops; + set_pci_dma_ops(&dma_iommu_ops); } diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 843ee96..3a70e8a 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -209,13 +209,13 @@ void __init find_udbg_vterm(void) /* find the boot console from /chosen/stdout */ if (!of_chosen) return; - name = get_property(of_chosen, "linux,stdout-path", NULL); + name = of_get_property(of_chosen, "linux,stdout-path", NULL); if (name == NULL) return; stdout_node = of_find_node_by_path(name); if (!stdout_node) return; - name = get_property(stdout_node, "name", NULL); + name = of_get_property(stdout_node, "name", NULL); if (!name) { printk(KERN_WARNING "stdout node missing 'name' property!\n"); goto out; @@ -226,7 +226,7 @@ void __init find_udbg_vterm(void) /* Check if it's a virtual terminal */ if (strncmp(name, "vty", 3) != 0) goto out; - termno = get_property(stdout_node, "reg", NULL); + termno = of_get_property(stdout_node, "reg", NULL); if (termno == NULL) goto out; vtermno = termno[0]; diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 64163ce..f68903e 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -130,7 +130,7 @@ int __init pSeries_nvram_init(void) if (nvram == NULL) return -ENODEV; - nbytes_p = get_property(nvram, "#bytes", &proplen); + nbytes_p = of_get_property(nvram, "#bytes", &proplen); if (nbytes_p == NULL || proplen != sizeof(unsigned int)) return -EIO; diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c index fa59124..2c6ded2 100644 --- a/arch/powerpc/platforms/pseries/pci.c +++ b/arch/powerpc/platforms/pseries/pci.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,7 @@ void pcibios_name_device(struct pci_dev */ dn = pci_device_to_OF_node(dev); if (dn) { - char *loc_code = get_property(dn, "ibm,loc-code", 0); + const char *loc_code = of_get_property(dn, "ibm,loc-code", 0); if (loc_code) { int loc_len = strlen(loc_code); if (loc_len < sizeof(dev->dev.name)) { diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index ac56b86..fdc1a36 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -29,6 +29,7 @@ #include #include #include #include +#include static struct pci_bus * find_bus_among_children(struct pci_bus *bus, diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index edc0388..53aa041 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -85,7 +85,7 @@ static void request_ras_irqs(struct devi * map those interrupts using the default interrupt host and default * trigger */ - opicprop = get_property(np, "open-pic-interrupt", &opicplen); + opicprop = of_get_property(np, "open-pic-interrupt", &opicplen); if (opicprop) { opicplen /= sizeof(u32); for (i = 0; i < opicplen; i++) { diff --git a/arch/powerpc/platforms/pseries/rtasd.c b/arch/powerpc/platforms/pseries/rtasd.c index 77d0937..9797b10 100644 --- a/arch/powerpc/platforms/pseries/rtasd.c +++ b/arch/powerpc/platforms/pseries/rtasd.c @@ -363,7 +363,7 @@ static int get_eventscan_parms(void) node = of_find_node_by_path("/rtas"); - ip = get_property(node, "rtas-event-scan-rate", NULL); + ip = of_get_property(node, "rtas-event-scan-rate", NULL); if (ip == NULL) { printk(KERN_ERR "rtasd: no rtas-event-scan-rate\n"); of_node_put(node); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 34aff47..33eec28 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -65,6 +65,7 @@ #include #include #include #include +#include #include "plpar_wrappers.h" #include "pseries.h" @@ -92,7 +93,7 @@ static void pSeries_show_cpuinfo(struct root = of_find_node_by_path("/"); if (root) - model = get_property(root, "model", NULL); + model = of_get_property(root, "model", NULL); seq_printf(m, "machine\t\t: CHRP %s\n", model); of_node_put(root); } @@ -138,8 +139,8 @@ static void __init pseries_mpic_init_IRQ struct mpic *mpic; np = of_find_node_by_path("/"); - naddr = prom_n_addr_cells(np); - opprop = get_property(np, "platform-open-pic", &opplen); + naddr = of_n_addr_cells(np); + opprop = of_get_property(np, "platform-open-pic", &opplen); if (opprop != 0) { openpic_addr = of_read_number(opprop, naddr); printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); @@ -188,11 +189,11 @@ static void __init pseries_mpic_init_IRQ break; if (strcmp(np->name, "pci") != 0) continue; - addrp = get_property(np, "8259-interrupt-acknowledge", + addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL); if (addrp == NULL) continue; - naddr = prom_n_addr_cells(np); + naddr = of_n_addr_cells(np); intack = addrp[naddr-1]; if (naddr > 1) intack |= ((unsigned long)addrp[naddr-2]) << 32; @@ -225,7 +226,7 @@ static void __init pseries_discover_pic( for (np = NULL; (np = of_find_node_by_name(np, "interrupt-controller"));) { - typep = get_property(np, "compatible", NULL); + typep = of_get_property(np, "compatible", NULL); if (strstr(typep, "open-pic")) { pSeries_mpic_node = of_node_get(np); ppc_md.init_IRQ = pseries_mpic_init_IRQ; @@ -334,32 +335,6 @@ static void __init pSeries_init_early(vo DBG(" <- pSeries_init_early()\n"); } - -static int pSeries_check_legacy_ioport(unsigned int baseport) -{ - struct device_node *np; - -#define I8042_DATA_REG 0x60 -#define FDC_BASE 0x3f0 - - - switch(baseport) { - case I8042_DATA_REG: - np = of_find_node_by_type(NULL, "8042"); - if (np == NULL) - return -ENODEV; - of_node_put(np); - break; - case FDC_BASE: - np = of_find_node_by_type(NULL, "fdc"); - if (np == NULL) - return -ENODEV; - of_node_put(np); - break; - } - return 0; -} - /* * Called very early, MMU is off, device-tree isn't unflattened */ @@ -514,6 +489,10 @@ void pSeries_power_off(void) for (;;); } +#ifndef CONFIG_PCI +void pSeries_final_fixup(void) { } +#endif + define_machine(pseries) { .name = "pSeries", .probe = pSeries_probe, @@ -532,7 +511,6 @@ define_machine(pseries) { .set_rtc_time = rtas_set_rtc_time, .calibrate_decr = generic_calibrate_decr, .progress = rtas_progress, - .check_legacy_ioport = pSeries_check_legacy_ioport, .system_reset_exception = pSeries_system_reset_exception, .machine_check_exception = pSeries_machine_check_exception, }; diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 81d172d..896cbf3 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -576,7 +576,7 @@ static void __init xics_init_one_node(st * This happens to be the case so far but we are playing with fire... * should be fixed one of these days. -BenH. */ - ireg = get_property(np, "ibm,interrupt-server-ranges", NULL); + ireg = of_get_property(np, "ibm,interrupt-server-ranges", NULL); /* Do that ever happen ? we'll know soon enough... but even good'old * f80 does have that property .. @@ -588,7 +588,7 @@ static void __init xics_init_one_node(st */ *indx = *ireg; } - ireg = get_property(np, "reg", &ilen); + ireg = of_get_property(np, "reg", &ilen); if (!ireg) panic("xics_init_IRQ: can't find interrupt reg property"); @@ -640,10 +640,10 @@ static void __init xics_setup_8259_casca break; if (strcmp(np->name, "pci") != 0) continue; - addrp = get_property(np, "8259-interrupt-acknowledge", NULL); + addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL); if (addrp == NULL) continue; - naddr = prom_n_addr_cells(np); + naddr = of_n_addr_cells(np); intack = addrp[naddr-1]; if (naddr > 1) intack |= ((unsigned long)addrp[naddr-2]) << 32; @@ -664,10 +664,11 @@ static struct device_node *cpuid_to_of_n int i, len; const u32 *intserv; - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", + &len); if (!intserv) - intserv = get_property(np, "reg", &len); + intserv = of_get_property(np, "reg", &len); i = len / sizeof(u32); @@ -709,7 +710,7 @@ void __init xics_init_IRQ(void) /* Find the server numbers for the boot cpu. */ np = cpuid_to_of_node(boot_cpuid); BUG_ON(!np); - ireg = get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); + ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); if (!ireg) goto skip_gserver_check; i = ilen / sizeof(int); @@ -725,7 +726,7 @@ void __init xics_init_IRQ(void) default_server = hcpuid; default_distrib_server = ireg[j+1]; - isize = get_property(np, + isize = of_get_property(np, "ibm,interrupt-server#-size", NULL); if (isize) interrupt_server_size = *isize; diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 26ca3ff..e96ca96 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -11,12 +11,17 @@ obj-$(CONFIG_PPC_PMI) += pmi.o obj-$(CONFIG_U3_DART) += dart_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_FSL_SOC) += fsl_soc.o +obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ +# contains only the suspend handler for time +obj-$(CONFIG_PM) += timer.o + ifeq ($(CONFIG_PPC_MERGE),y) obj-$(CONFIG_PPC_I8259) += i8259.o obj-$(CONFIG_PPC_83xx) += ipic.o +obj-$(CONFIG_4xx) += uic.o endif # Temporary hack until we have migrated to asm-powerpc diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c index 1488535..336186d 100644 --- a/arch/powerpc/sysdev/dart_iommu.c +++ b/arch/powerpc/sysdev/dart_iommu.c @@ -333,7 +333,7 @@ void iommu_init_early_dart(void) ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_dart; /* Setup pci_dma ops */ - pci_dma_ops = &dma_iommu_ops; + set_pci_dma_ops(&dma_iommu_ops); return; } @@ -343,7 +343,7 @@ void iommu_init_early_dart(void) ppc_md.pci_dma_bus_setup = NULL; /* Setup pci_dma ops */ - pci_dma_ops = &dma_direct_ops; + set_pci_dma_ops(&dma_direct_ops); } diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c index 1fc5819..574b6ef 100644 --- a/arch/powerpc/sysdev/dcr.c +++ b/arch/powerpc/sysdev/dcr.c @@ -26,7 +26,7 @@ #include unsigned int dcr_resource_start(struct device_node *np, unsigned int index) { unsigned int ds; - const u32 *dr = get_property(np, "dcr-reg", &ds); + const u32 *dr = of_get_property(np, "dcr-reg", &ds); if (dr == NULL || ds & 1 || index >= (ds / 8)) return 0; @@ -37,7 +37,7 @@ unsigned int dcr_resource_start(struct d unsigned int dcr_resource_len(struct device_node *np, unsigned int index) { unsigned int ds; - const u32 *dr = get_property(np, "dcr-reg", &ds); + const u32 *dr = of_get_property(np, "dcr-reg", &ds); if (dr == NULL || ds & 1 || index >= (ds / 8)) return 0; @@ -53,9 +53,9 @@ static struct device_node * find_dcr_par const u32 *p; for (par = of_node_get(node); par;) { - if (get_property(par, "dcr-controller", NULL)) + if (of_get_property(par, "dcr-controller", NULL)) break; - p = get_property(par, "dcr-parent", NULL); + p = of_get_property(par, "dcr-parent", NULL); tmp = par; if (p == NULL) par = of_get_parent(par); @@ -80,13 +80,13 @@ u64 of_translate_dcr_address(struct devi return OF_BAD_ADDR; /* Stride is not properly defined yet, default to 0x10 for Axon */ - p = get_property(dp, "dcr-mmio-stride", NULL); + p = of_get_property(dp, "dcr-mmio-stride", NULL); stride = (p == NULL) ? 0x10 : *p; /* XXX FIXME: Which property name is to use of the 2 following ? */ - p = get_property(dp, "dcr-mmio-range", NULL); + p = of_get_property(dp, "dcr-mmio-range", NULL); if (p == NULL) - p = get_property(dp, "dcr-mmio-space", NULL); + p = of_get_property(dp, "dcr-mmio-space", NULL); if (p == NULL) return OF_BAD_ADDR; diff --git a/arch/powerpc/sysdev/fsl_pcie.c b/arch/powerpc/sysdev/fsl_pcie.c new file mode 100644 index 0000000..041c07e --- /dev/null +++ b/arch/powerpc/sysdev/fsl_pcie.c @@ -0,0 +1,171 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * 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. + * + * "Temporary" MPC8548 Errata file - + * The standard indirect_pci code should work with future silicon versions. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PCI_CFG_OUT out_be32 + +/* ERRATA PCI-Ex 14 PCIE Controller timeout */ +#define PCIE_FIX out_be32(hose->cfg_addr+0x4, 0x0400ffff) + + +static int +indirect_read_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u32 temp; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Possible artifact of CDCpp50937 needs further investigation */ + if (devfn != 0x0 && bus->number == 0xff) + return PCIBIOS_DEVICE_NOT_FOUND; + + PCIE_FIX; + if (bus->number == 0xff) { + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((offset & 0xf00) << 16) | + ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) ))); + } else { + PCI_CFG_OUT(hose->cfg_addr, + (0x80000001 | ((offset & 0xf00) << 16) | + ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) ))); + } + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ + cfg_data = hose->cfg_data; + PCIE_FIX; + temp = in_le32(cfg_data); + switch (len) { + case 1: + *val = (temp >> (((offset & 3))*8)) & 0xff; + break; + case 2: + *val = (temp >> (((offset & 3))*8)) & 0xffff; + break; + default: + *val = temp; + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_write_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u32 temp; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Possible artifact of CDCpp50937 needs further investigation */ + if (devfn != 0x0 && bus->number == 0xff) + return PCIBIOS_DEVICE_NOT_FOUND; + + PCIE_FIX; + if (bus->number == 0xff) { + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((offset & 0xf00) << 16) | + ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) ))); + } else { + PCI_CFG_OUT(hose->cfg_addr, + (0x80000001 | ((offset & 0xf00) << 16) | + ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) ))); + } + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ + cfg_data = hose->cfg_data; + switch (len) { + case 1: + PCIE_FIX; + temp = in_le32(cfg_data); + temp = (temp & ~(0xff << ((offset & 3) * 8))) | + (val << ((offset & 3) * 8)); + PCIE_FIX; + out_le32(cfg_data, temp); + break; + case 2: + PCIE_FIX; + temp = in_le32(cfg_data); + temp = (temp & ~(0xffff << ((offset & 3) * 8))); + temp |= (val << ((offset & 3) * 8)) ; + PCIE_FIX; + out_le32(cfg_data, temp); + break; + default: + PCIE_FIX; + out_le32(cfg_data, val); + break; + } + PCIE_FIX; + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops indirect_pcie_ops = { + indirect_read_config_pcie, + indirect_write_config_pcie +}; + +void __init +setup_indirect_pcie_nomap(struct pci_controller* hose, void __iomem * cfg_addr, + void __iomem * cfg_data) +{ + hose->cfg_addr = cfg_addr; + hose->cfg_data = cfg_data; + hose->ops = &indirect_pcie_ops; +} + +void __init +setup_indirect_pcie(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + unsigned long base = cfg_addr & PAGE_MASK; + void __iomem *mbase, *addr, *data; + + mbase = ioremap(base, PAGE_SIZE); + addr = mbase + (cfg_addr & ~PAGE_MASK); + if ((cfg_data & PAGE_MASK) != base) + mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); + data = mbase + (cfg_data & ~PAGE_MASK); + setup_indirect_pcie_nomap(hose, addr, data); +} diff --git a/arch/powerpc/sysdev/fsl_pcie.h b/arch/powerpc/sysdev/fsl_pcie.h new file mode 100644 index 0000000..8d9779c --- /dev/null +++ b/arch/powerpc/sysdev/fsl_pcie.h @@ -0,0 +1,94 @@ +/* + * MPC85xx/86xx PCI Express structure define + * + * Copyright 2007 Freescale Semiconductor, Inc + * + * 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. + * + */ + +#ifdef __KERNEL__ +#ifndef __POWERPC_FSL_PCIE_H +#define __POWERPC_FSL_PCIE_H + +/* PCIE Express IO block registers in 85xx/86xx */ + +struct ccsr_pex { + __be32 __iomem pex_config_addr; /* 0x.000 - PCI Express Configuration Address Register */ + __be32 __iomem pex_config_data; /* 0x.004 - PCI Express Configuration Data Register */ + u8 __iomem res1[4]; + __be32 __iomem pex_otb_cpl_tor; /* 0x.00c - PCI Express Outbound completion timeout register */ + __be32 __iomem pex_conf_tor; /* 0x.010 - PCI Express configuration timeout register */ + u8 __iomem res2[12]; + __be32 __iomem pex_pme_mes_dr; /* 0x.020 - PCI Express PME and message detect register */ + __be32 __iomem pex_pme_mes_disr; /* 0x.024 - PCI Express PME and message disable register */ + __be32 __iomem pex_pme_mes_ier; /* 0x.028 - PCI Express PME and message interrupt enable register */ + __be32 __iomem pex_pmcr; /* 0x.02c - PCI Express power management command register */ + u8 __iomem res3[3024]; + __be32 __iomem pexotar0; /* 0x.c00 - PCI Express outbound translation address register 0 */ + __be32 __iomem pexotear0; /* 0x.c04 - PCI Express outbound translation extended address register 0*/ + u8 __iomem res4[8]; + __be32 __iomem pexowar0; /* 0x.c10 - PCI Express outbound window attributes register 0*/ + u8 __iomem res5[12]; + __be32 __iomem pexotar1; /* 0x.c20 - PCI Express outbound translation address register 1 */ + __be32 __iomem pexotear1; /* 0x.c24 - PCI Express outbound translation extended address register 1*/ + __be32 __iomem pexowbar1; /* 0x.c28 - PCI Express outbound window base address register 1*/ + u8 __iomem res6[4]; + __be32 __iomem pexowar1; /* 0x.c30 - PCI Express outbound window attributes register 1*/ + u8 __iomem res7[12]; + __be32 __iomem pexotar2; /* 0x.c40 - PCI Express outbound translation address register 2 */ + __be32 __iomem pexotear2; /* 0x.c44 - PCI Express outbound translation extended address register 2*/ + __be32 __iomem pexowbar2; /* 0x.c48 - PCI Express outbound window base address register 2*/ + u8 __iomem res8[4]; + __be32 __iomem pexowar2; /* 0x.c50 - PCI Express outbound window attributes register 2*/ + u8 __iomem res9[12]; + __be32 __iomem pexotar3; /* 0x.c60 - PCI Express outbound translation address register 3 */ + __be32 __iomem pexotear3; /* 0x.c64 - PCI Express outbound translation extended address register 3*/ + __be32 __iomem pexowbar3; /* 0x.c68 - PCI Express outbound window base address register 3*/ + u8 __iomem res10[4]; + __be32 __iomem pexowar3; /* 0x.c70 - PCI Express outbound window attributes register 3*/ + u8 __iomem res11[12]; + __be32 __iomem pexotar4; /* 0x.c80 - PCI Express outbound translation address register 4 */ + __be32 __iomem pexotear4; /* 0x.c84 - PCI Express outbound translation extended address register 4*/ + __be32 __iomem pexowbar4; /* 0x.c88 - PCI Express outbound window base address register 4*/ + u8 __iomem res12[4]; + __be32 __iomem pexowar4; /* 0x.c90 - PCI Express outbound window attributes register 4*/ + u8 __iomem res13[12]; + u8 __iomem res14[256]; + __be32 __iomem pexitar3; /* 0x.da0 - PCI Express inbound translation address register 3 */ + u8 __iomem res15[4]; + __be32 __iomem pexiwbar3; /* 0x.da8 - PCI Express inbound window base address register 3 */ + __be32 __iomem pexiwbear3; /* 0x.dac - PCI Express inbound window base extended address register 3 */ + __be32 __iomem pexiwar3; /* 0x.db0 - PCI Express inbound window attributes register 3 */ + u8 __iomem res16[12]; + __be32 __iomem pexitar2; /* 0x.dc0 - PCI Express inbound translation address register 2 */ + u8 __iomem res17[4]; + __be32 __iomem pexiwbar2; /* 0x.dc8 - PCI Express inbound window base address register 2 */ + __be32 __iomem pexiwbear2; /* 0x.dcc - PCI Express inbound window base extended address register 2 */ + __be32 __iomem pexiwar2; /* 0x.dd0 - PCI Express inbound window attributes register 2 */ + u8 __iomem res18[12]; + __be32 __iomem pexitar1; /* 0x.de0 - PCI Express inbound translation address register 2 */ + u8 __iomem res19[4]; + __be32 __iomem pexiwbar1; /* 0x.de8 - PCI Express inbound window base address register 2 */ + __be32 __iomem pexiwbear1; /* 0x.dec - PCI Express inbound window base extended address register 2 */ + __be32 __iomem pexiwar1; /* 0x.df0 - PCI Express inbound window attributes register 2 */ + u8 __iomem res20[12]; + __be32 __iomem pex_err_dr; /* 0x.e00 - PCI Express error detect register */ + u8 __iomem res21[4]; + __be32 __iomem pex_err_en; /* 0x.e08 - PCI Express error interrupt enable register */ + u8 __iomem res22[4]; + __be32 __iomem pex_err_disr; /* 0x.e10 - PCI Express error disable register */ + u8 __iomem res23[12]; + __be32 __iomem pex_err_cap_stat; /* 0x.e20 - PCI Express error capture status register */ + u8 __iomem res24[4]; + __be32 __iomem pex_err_cap_r0; /* 0x.e28 - PCI Express error capture register 0 */ + __be32 __iomem pex_err_cap_r1; /* 0x.e2c - PCI Express error capture register 0 */ + __be32 __iomem pex_err_cap_r2; /* 0x.e30 - PCI Express error capture register 0 */ + __be32 __iomem pex_err_cap_r3; /* 0x.e34 - PCI Express error capture register 0 */ +}; + +#endif /* __POWERPC_FSL_PCIE_H */ +#endif /* __KERNEL__ */ diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index d20f029..8a123c7 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -52,7 +52,7 @@ phys_addr_t get_immrbase(void) soc = of_find_node_by_type(NULL, "soc"); if (soc) { unsigned int size; - const void *prop = get_property(soc, "reg", &size); + const void *prop = of_get_property(soc, "reg", &size); if (prop) immrbase = of_translate_address(soc, prop); @@ -78,8 +78,8 @@ u32 get_brgfreq(void) node = of_find_node_by_type(NULL, "cpm"); if (node) { unsigned int size; - const unsigned int *prop = get_property(node, "brg-frequency", - &size); + const unsigned int *prop = of_get_property(node, + "brg-frequency", &size); if (prop) brgfreq = *prop; @@ -103,8 +103,8 @@ u32 get_baudrate(void) node = of_find_node_by_type(NULL, "serial"); if (node) { unsigned int size; - const unsigned int *prop = get_property(node, "current-speed", - &size); + const unsigned int *prop = of_get_property(node, + "current-speed", &size); if (prop) fs_baudrate = *prop; @@ -153,7 +153,8 @@ static int __init gfar_mdio_of_init(void while ((child = of_get_next_child(np, child)) != NULL) { int irq = irq_of_parse_and_map(child, 0); if (irq != NO_IRQ) { - const u32 *id = get_property(child, "reg", NULL); + const u32 *id = of_get_property(child, + "reg", NULL); mdio_data.irq[*id] = irq; } } @@ -209,7 +210,7 @@ static int __init gfar_of_init(void) of_irq_to_resource(np, 0, &r[1]); - model = get_property(np, "model", NULL); + model = of_get_property(np, "model", NULL); /* If we aren't the FEC we have multiple interrupts */ if (model && strcasecmp(model, "FEC")) { @@ -253,7 +254,7 @@ static int __init gfar_of_init(void) FSL_GIANFAR_DEV_HAS_VLAN | FSL_GIANFAR_DEV_HAS_EXTENDED_HASH; - ph = get_property(np, "phy-handle", NULL); + ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); if (phy == NULL) { @@ -263,7 +264,7 @@ static int __init gfar_of_init(void) mdio = of_get_parent(phy); - id = get_property(phy, "reg", NULL); + id = of_get_property(phy, "reg", NULL); ret = of_address_to_resource(mdio, 0, &res); if (ret) { of_node_put(phy); @@ -325,11 +326,11 @@ static int __init fsl_i2c_of_init(void) } i2c_data.device_flags = 0; - flags = get_property(np, "dfsrr", NULL); + flags = of_get_property(np, "dfsrr", NULL); if (flags) i2c_data.device_flags |= FSL_I2C_DEV_SEPARATE_DFSRR; - flags = get_property(np, "fsl5200-clocking", NULL); + flags = of_get_property(np, "fsl5200-clocking", NULL); if (flags) i2c_data.device_flags |= FSL_I2C_DEV_CLOCK_5200; @@ -374,7 +375,7 @@ static int __init mpc83xx_wdt_init(void) goto nosoc; } - freq = get_property(soc, "bus-frequency", NULL); + freq = of_get_property(soc, "bus-frequency", NULL); if (!freq) { ret = -ENODEV; goto err; @@ -466,15 +467,15 @@ static int __init fsl_usb_of_init(void) usb_data.operating_mode = FSL_USB2_MPH_HOST; - prop = get_property(np, "port0", NULL); + prop = of_get_property(np, "port0", NULL); if (prop) usb_data.port_enables |= FSL_USB2_PORT0_ENABLED; - prop = get_property(np, "port1", NULL); + prop = of_get_property(np, "port1", NULL); if (prop) usb_data.port_enables |= FSL_USB2_PORT1_ENABLED; - prop = get_property(np, "phy_type", NULL); + prop = of_get_property(np, "phy_type", NULL); usb_data.phy_mode = determine_usb_phy(prop); ret = @@ -501,7 +502,7 @@ static int __init fsl_usb_of_init(void) of_irq_to_resource(np, 0, &r[1]); - prop = get_property(np, "dr_mode", NULL); + prop = of_get_property(np, "dr_mode", NULL); if (!prop || !strcmp(prop, "host")) { usb_data.operating_mode = FSL_USB2_DR_HOST; @@ -538,7 +539,7 @@ static int __init fsl_usb_of_init(void) goto err; } - prop = get_property(np, "phy_type", NULL); + prop = of_get_property(np, "phy_type", NULL); usb_data.phy_mode = determine_usb_phy(prop); if (usb_dev_dr_host) { @@ -633,7 +634,7 @@ static int __init fs_enet_of_init(void) goto err; } - model = get_property(np, "model", NULL); + model = of_get_property(np, "model", NULL); if (model == NULL) { ret = -ENODEV; goto unreg; @@ -643,7 +644,7 @@ static int __init fs_enet_of_init(void) if (mac_addr) memcpy(fs_enet_data.macaddr, mac_addr, 6); - ph = get_property(np, "phy-handle", NULL); + ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); if (phy == NULL) { @@ -651,12 +652,12 @@ static int __init fs_enet_of_init(void) goto unreg; } - phy_addr = get_property(phy, "reg", NULL); + phy_addr = of_get_property(phy, "reg", NULL); fs_enet_data.phy_addr = *phy_addr; - phy_irq = get_property(phy, "interrupts", NULL); + phy_irq = of_get_property(phy, "interrupts", NULL); - id = get_property(np, "device-id", NULL); + id = of_get_property(np, "device-id", NULL); fs_enet_data.fs_no = *id; strcpy(fs_enet_data.fs_type, model); @@ -668,8 +669,10 @@ static int __init fs_enet_of_init(void) goto unreg; } - fs_enet_data.clk_rx = *((u32 *) get_property(np, "rx-clock", NULL)); - fs_enet_data.clk_tx = *((u32 *) get_property(np, "tx-clock", NULL)); + fs_enet_data.clk_rx = *((u32 *)of_get_property(np, + "rx-clock", NULL)); + fs_enet_data.clk_tx = *((u32 *)of_get_property(np, + "tx-clock", NULL)); if (strstr(model, "FCC")) { int fcc_index = *id - 1; @@ -690,7 +693,7 @@ static int __init fs_enet_of_init(void) fs_enet_data.bus_id = (char*)&bus_id[(*id)]; fs_enet_data.init_ioports = init_fcc_ioports; - mdio_bb_prop = get_property(phy, "bitbang", NULL); + mdio_bb_prop = of_get_property(phy, "bitbang", NULL); if (mdio_bb_prop) { struct platform_device *fs_enet_mdio_bb_dev; struct fs_mii_bb_platform_info fs_enet_mdio_bb_data; @@ -796,10 +799,10 @@ static int __init cpm_uart_of_init(void) goto err; } - id = get_property(np, "device-id", NULL); + id = of_get_property(np, "device-id", NULL); cpm_uart_data.fs_no = *id; - model = (char*)get_property(np, "model", NULL); + model = of_get_property(np, "model", NULL); strcpy(cpm_uart_data.fs_type, model); cpm_uart_data.uart_clk = ppc_proc_freq; @@ -808,8 +811,10 @@ static int __init cpm_uart_of_init(void) cpm_uart_data.tx_buf_size = 32; cpm_uart_data.rx_num_fifo = 4; cpm_uart_data.rx_buf_size = 32; - cpm_uart_data.clk_rx = *((u32 *) get_property(np, "rx-clock", NULL)); - cpm_uart_data.clk_tx = *((u32 *) get_property(np, "tx-clock", NULL)); + cpm_uart_data.clk_rx = *((u32 *)of_get_property(np, + "rx-clock", NULL)); + cpm_uart_data.clk_tx = *((u32 *)of_get_property(np, + "tx-clock", NULL)); ret = platform_device_add_data(cpm_uart_dev, &cpm_uart_data, @@ -833,7 +838,7 @@ #endif /* CONFIG_CPM2 */ #ifdef CONFIG_8xx extern void init_scc_ioports(struct fs_platform_info*); -extern int platform_device_skip(char *model, int id); +extern int platform_device_skip(const char *model, int id); static int __init fs_enet_mdio_of_init(void) { @@ -900,21 +905,22 @@ static int __init fs_enet_of_init(void) struct resource r[4]; struct device_node *phy = NULL, *mdio = NULL; struct fs_platform_info fs_enet_data; - unsigned int *id, *phy_addr; + const unsigned int *id; + const unsigned int *phy_addr; void *mac_addr; - phandle *ph; - char *model; + const phandle *ph; + const char *model; memset(r, 0, sizeof(r)); memset(&fs_enet_data, 0, sizeof(fs_enet_data)); - model = (char *)get_property(np, "model", NULL); + model = of_get_property(np, "model", NULL); if (model == NULL) { ret = -ENODEV; goto unreg; } - id = (u32 *) get_property(np, "device-id", NULL); + id = of_get_property(np, "device-id", NULL); fs_enet_data.fs_no = *id; if (platform_device_skip(model, *id)) @@ -929,12 +935,12 @@ static int __init fs_enet_of_init(void) if (mac_addr) memcpy(fs_enet_data.macaddr, mac_addr, 6); - ph = (phandle *) get_property(np, "phy-handle", NULL); + ph = of_get_property(np, "phy-handle", NULL); if (ph != NULL) phy = of_find_node_by_phandle(*ph); if (phy != NULL) { - phy_addr = (u32 *) get_property(phy, "reg", NULL); + phy_addr = of_get_property(phy, "reg", NULL); fs_enet_data.phy_addr = *phy_addr; fs_enet_data.has_phy = 1; @@ -947,7 +953,7 @@ static int __init fs_enet_of_init(void) } } - model = (char*)get_property(np, "model", NULL); + model = of_get_property(np, "model", NULL); strcpy(fs_enet_data.fs_type, model); if (strstr(model, "FEC")) { @@ -1038,8 +1044,8 @@ static int __init cpm_smc_uart_of_init(v i++) { struct resource r[3]; struct fs_uart_platform_info cpm_uart_data; - int *id; - char *model; + const int *id; + const char *model; memset(r, 0, sizeof(r)); memset(&cpm_uart_data, 0, sizeof(cpm_uart_data)); @@ -1066,10 +1072,10 @@ static int __init cpm_smc_uart_of_init(v goto err; } - model = (char*)get_property(np, "model", NULL); + model = of_get_property(np, "model", NULL); strcpy(cpm_uart_data.fs_type, model); - id = (int*)get_property(np, "device-id", NULL); + id = of_get_property(np, "device-id", NULL); cpm_uart_data.fs_no = *id; cpm_uart_data.uart_clk = ppc_proc_freq; diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index bcfb900..0b84b7c 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -304,7 +304,7 @@ static void __init mpic_test_broken_ipi( } } -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS /* Test if an interrupt is sourced from HyperTransport (used on broken U3s) * to force the edge setting on the MPIC and do the ack workaround. @@ -476,7 +476,7 @@ static void __init mpic_scan_ht_pics(str } } -#else /* CONFIG_MPIC_BROKEN_U3 */ +#else /* CONFIG_MPIC_U3_HT_IRQS */ static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) { @@ -487,7 +487,7 @@ static void __init mpic_scan_ht_pics(str { } -#endif /* CONFIG_MPIC_BROKEN_U3 */ +#endif /* CONFIG_MPIC_U3_HT_IRQS */ #define mpic_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq) @@ -615,7 +615,7 @@ #endif mpic_eoi(mpic); } -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS static void mpic_unmask_ht_irq(unsigned int irq) { @@ -665,7 +665,7 @@ #endif mpic_ht_end_irq(mpic, src); mpic_eoi(mpic); } -#endif /* !CONFIG_MPIC_BROKEN_U3 */ +#endif /* !CONFIG_MPIC_U3_HT_IRQS */ #ifdef CONFIG_SMP @@ -788,7 +788,7 @@ static struct irq_chip mpic_ipi_chip = { }; #endif /* CONFIG_SMP */ -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS static struct irq_chip mpic_irq_ht_chip = { .startup = mpic_startup_ht_irq, .shutdown = mpic_shutdown_ht_irq, @@ -797,7 +797,7 @@ static struct irq_chip mpic_irq_ht_chip .eoi = mpic_end_ht_irq, .set_type = mpic_set_irq_type, }; -#endif /* CONFIG_MPIC_BROKEN_U3 */ +#endif /* CONFIG_MPIC_U3_HT_IRQS */ static int mpic_host_match(struct irq_host *h, struct device_node *node) @@ -837,11 +837,11 @@ #endif /* CONFIG_SMP */ /* Default chip */ chip = &mpic->hc_irq; -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS /* Check for HT interrupts, override vecpri */ if (mpic_is_ht_interrupt(mpic, hw)) chip = &mpic->hc_ht_irq; -#endif /* CONFIG_MPIC_BROKEN_U3 */ +#endif /* CONFIG_MPIC_U3_HT_IRQS */ DBG("mpic: mapping to irq chip @%p\n", chip); @@ -937,12 +937,12 @@ struct mpic * __init mpic_alloc(struct d mpic->hc_irq.typename = name; if (flags & MPIC_PRIMARY) mpic->hc_irq.set_affinity = mpic_set_affinity; -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS mpic->hc_ht_irq = mpic_irq_ht_chip; mpic->hc_ht_irq.typename = name; if (flags & MPIC_PRIMARY) mpic->hc_ht_irq.set_affinity = mpic_set_affinity; -#endif /* CONFIG_MPIC_BROKEN_U3 */ +#endif /* CONFIG_MPIC_U3_HT_IRQS */ #ifdef CONFIG_SMP mpic->hc_ipi = mpic_ipi_chip; @@ -970,7 +970,7 @@ #endif /* CONFIG_SMP */ mpic->spurious_vec = intvec_top; /* Check for "big-endian" in device-tree */ - if (node && get_property(node, "big-endian", NULL) != NULL) + if (node && of_get_property(node, "big-endian", NULL) != NULL) mpic->flags |= MPIC_BIG_ENDIAN; @@ -986,13 +986,13 @@ #endif BUG_ON(paddr == 0 && node == NULL); /* If no physical address passed in, check if it's dcr based */ - if (paddr == 0 && get_property(node, "dcr-reg", NULL) != NULL) + if (paddr == 0 && of_get_property(node, "dcr-reg", NULL) != NULL) mpic->flags |= MPIC_USES_DCR; #ifdef CONFIG_PPC_DCR if (mpic->flags & MPIC_USES_DCR) { const u32 *dbasep; - dbasep = get_property(node, "dcr-reg", NULL); + dbasep = of_get_property(node, "dcr-reg", NULL); BUG_ON(dbasep == NULL); mpic->dcr_base = *dbasep; mpic->reg_type = mpic_access_dcr; @@ -1006,7 +1006,7 @@ #endif /* CONFIG_PPC_DCR */ */ if (paddr == 0 && !(mpic->flags & MPIC_USES_DCR)) { const u32 *reg; - reg = get_property(node, "reg", NULL); + reg = of_get_property(node, "reg", NULL); BUG_ON(reg == NULL); paddr = of_translate_address(node, reg); BUG_ON(paddr == OF_BAD_ADDR); @@ -1142,7 +1142,7 @@ void __init mpic_init(struct mpic *mpic) /* Do the HT PIC fixups on U3 broken mpic */ DBG("MPIC flags: %x\n", mpic->flags); - if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY)) + if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) mpic_scan_ht_pics(mpic); for (i = 0; i < mpic->num_sources; i++) { diff --git a/arch/powerpc/sysdev/pmi.c b/arch/powerpc/sysdev/pmi.c index a528201..85a7c99 100644 --- a/arch/powerpc/sysdev/pmi.c +++ b/arch/powerpc/sysdev/pmi.c @@ -33,7 +33,7 @@ #include #include #include #include - +#include struct pmi_data { struct list_head handler; @@ -49,21 +49,6 @@ struct pmi_data { }; - -static void __iomem *of_iomap(struct device_node *np) -{ - struct resource res; - - if (of_address_to_resource(np, 0, &res)) - return NULL; - - pr_debug("Resource start: 0x%lx\n", res.start); - pr_debug("Resource end: 0x%lx\n", res.end); - - return ioremap(res.start, 1 + res.end - res.start); -} - - static int pmi_irq_handler(int irq, void *dev_id) { struct pmi_data *data; @@ -118,6 +103,7 @@ out: static struct of_device_id pmi_match[] = { { .type = "ibm,pmi", .name = "ibm,pmi" }, + { .type = "ibm,pmi" }, {}, }; @@ -153,7 +139,7 @@ static int pmi_of_probe(struct of_device goto out; } - data->pmi_reg = of_iomap(np); + data->pmi_reg = of_iomap(np, 0); if (!data->pmi_reg) { printk(KERN_ERR "pmi: invalid register address.\n"); rc = -EFAULT; @@ -279,6 +265,9 @@ void pmi_register_handler(struct of_devi struct pmi_data *data; data = device->dev.driver_data; + if (!data) + return; + spin_lock(&data->handler_spinlock); list_add_tail(&handler->node, &data->handler); spin_unlock(&data->handler_spinlock); @@ -289,10 +278,12 @@ void pmi_unregister_handler(struct of_de struct pmi_handler *handler) { struct pmi_data *data; + data = device->dev.driver_data; - pr_debug("pmi: unregistering handler %p\n", handler); + if (!data) + return; - data = device->dev.driver_data; + pr_debug("pmi: unregistering handler %p\n", handler); spin_lock(&data->handler_spinlock); list_del(&handler->node); diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig index a725e80..887739f 100644 --- a/arch/powerpc/sysdev/qe_lib/Kconfig +++ b/arch/powerpc/sysdev/qe_lib/Kconfig @@ -2,11 +2,8 @@ # # QE Communication options # -menu "QE Options" - depends on QUICC_ENGINE - config UCC_SLOW - bool "UCC Slow Protocols Support" + bool default n select UCC help @@ -14,10 +11,9 @@ config UCC_SLOW protocols: UART, BISYNC, QMC config UCC_FAST - bool "UCC Fast Protocols Support" + bool default n select UCC - select UCC_SLOW help This option provides qe_lib support to UCC fast protocols: HDLC, Ethernet, ATM, transparent @@ -26,5 +22,3 @@ config UCC bool default y if UCC_FAST || UCC_SLOW -endmenu - diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index 43f6cc9..7f4c075 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c @@ -71,7 +71,7 @@ phys_addr_t get_qe_base(void) qe = of_find_node_by_type(NULL, "qe"); if (qe) { unsigned int size; - const void *prop = get_property(qe, "reg", &size); + const void *prop = of_get_property(qe, "reg", &size); qebase = of_translate_address(qe, prop); of_node_put(qe); }; @@ -158,7 +158,7 @@ unsigned int get_brg_clk(void) qe = of_find_node_by_type(NULL, "qe"); if (qe) { unsigned int size; - const u32 *prop = get_property(qe, "brg-frequency", &size); + const u32 *prop = of_get_property(qe, "brg-frequency", &size); brg_clk = *prop; of_node_put(qe); }; diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c index 0afe6bf..e32b45b 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_io.c +++ b/arch/powerpc/sysdev/qe_lib/qe_io.c @@ -53,7 +53,7 @@ int par_io_init(struct device_node *np) return ret; par_io = ioremap(res.start, res.end - res.start + 1); - num_ports = get_property(np, "num-ports", NULL); + num_ports = of_get_property(np, "num-ports", NULL); if (num_ports) num_par_io_ports = *num_ports; @@ -161,7 +161,7 @@ int par_io_of_config(struct device_node return -1; } - ph = get_property(np, "pio-handle", NULL); + ph = of_get_property(np, "pio-handle", NULL); if (ph == 0) { printk(KERN_ERR "pio-handle not available \n"); return -1; @@ -169,7 +169,7 @@ int par_io_of_config(struct device_node pio = of_find_node_by_phandle(*ph); - pio_map = get_property(pio, "pio-map", &pio_map_len); + pio_map = of_get_property(pio, "pio-map", &pio_map_len); if (pio_map == NULL) { printk(KERN_ERR "pio-map is not set! \n"); return -1; diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c index a457ac1..66137bf 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c +++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c @@ -210,6 +210,9 @@ int ucc_fast_init(struct ucc_fast_info * uf_regs = uccf->uf_regs; uccf->p_ucce = (u32 *) & (uf_regs->ucce); uccf->p_uccm = (u32 *) & (uf_regs->uccm); +#ifdef CONFIG_UGETH_TX_ON_DEMAND + uccf->p_utodr = (u16 *) & (uf_regs->utodr); +#endif #ifdef STATISTICS uccf->tx_frames = 0; uccf->rx_frames = 0; diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/arch/powerpc/sysdev/qe_lib/ucc_slow.c index 817df73..b930d68 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc_slow.c +++ b/arch/powerpc/sysdev/qe_lib/ucc_slow.c @@ -187,7 +187,7 @@ #endif /* STATISTICS */ uccs->us_pram = qe_muram_addr(uccs->us_pram_offset); /* Init Guemr register */ - if ((ret = ucc_init_guemr((struct ucc_common *) (us_info->regs)))) { + if ((ret = ucc_init_guemr((struct ucc_common *) us_regs))) { printk(KERN_ERR "%s: cannot init GUEMR", __FUNCTION__); ucc_slow_free(uccs); return ret; @@ -195,7 +195,7 @@ #endif /* STATISTICS */ /* Set UCC to slow type */ if ((ret = ucc_set_type(us_info->ucc_num, - (struct ucc_common *) (us_info->regs), + (struct ucc_common *) us_regs, UCC_SPEED_TYPE_SLOW))) { printk(KERN_ERR "%s: cannot set UCC type", __FUNCTION__); ucc_slow_free(uccs); diff --git a/arch/powerpc/sysdev/timer.c b/arch/powerpc/sysdev/timer.c new file mode 100644 index 0000000..4a01748 --- /dev/null +++ b/arch/powerpc/sysdev/timer.c @@ -0,0 +1,71 @@ +/* + * Common code to keep time when machine suspends. + * + * Copyright 2007 Johannes Berg + * + * GPLv2 + */ + +#include +#include +#include + +static unsigned long suspend_rtc_time; + +/* + * Reset the time after a sleep. + */ +static int timer_resume(struct sys_device *dev) +{ + struct timeval tv; + struct timespec ts; + struct rtc_time cur_rtc_tm; + unsigned long cur_rtc_time, diff; + + /* get current RTC time and convert to seconds */ + get_rtc_time(&cur_rtc_tm); + rtc_tm_to_time(&cur_rtc_tm, &cur_rtc_time); + + diff = cur_rtc_time - suspend_rtc_time; + + /* adjust time of day by seconds that elapsed while + * we were suspended */ + do_gettimeofday(&tv); + ts.tv_sec = tv.tv_sec + diff; + ts.tv_nsec = tv.tv_usec * NSEC_PER_USEC; + do_settimeofday(&ts); + + return 0; +} + +static int timer_suspend(struct sys_device *dev, pm_message_t state) +{ + struct rtc_time suspend_rtc_tm; + WARN_ON(!ppc_md.get_rtc_time); + + get_rtc_time(&suspend_rtc_tm); + rtc_tm_to_time(&suspend_rtc_tm, &suspend_rtc_time); + + return 0; +} + +static struct sysdev_class timer_sysclass = { + .resume = timer_resume, + .suspend = timer_suspend, + set_kset_name("timer"), +}; + +static struct sys_device device_timer = { + .id = 0, + .cls = &timer_sysclass, +}; + +static int time_init_device(void) +{ + int error = sysdev_class_register(&timer_sysclass); + if (!error) + error = sysdev_register(&device_timer); + return error; +} + +device_initcall(time_init_device); diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c index 97f37ef..337039e 100644 --- a/arch/powerpc/sysdev/tsi108_dev.c +++ b/arch/powerpc/sysdev/tsi108_dev.c @@ -48,7 +48,7 @@ phys_addr_t get_csrbase(void) tsi = of_find_node_by_type(NULL, "tsi-bridge"); if (tsi) { unsigned int size; - const void *prop = get_property(tsi, "reg", &size); + const void *prop = of_get_property(tsi, "reg", &size); tsi108_csr_base = of_translate_address(tsi, prop); of_node_put(tsi); }; @@ -77,10 +77,10 @@ static int __init tsi108_eth_of_init(voi struct resource r[2]; struct device_node *phy; hw_info tsi_eth_data; - unsigned int *id; - unsigned int *phy_id; + const unsigned int *id; + const unsigned int *phy_id; const void *mac_addr; - phandle *ph; + const phandle *ph; memset(r, 0, sizeof(r)); memset(&tsi_eth_data, 0, sizeof(tsi_eth_data)); @@ -107,10 +107,10 @@ static int __init tsi108_eth_of_init(voi goto err; } - mac_addr = get_property(np, "address", NULL); + mac_addr = of_get_property(np, "address", NULL); memcpy(tsi_eth_data.mac_addr, mac_addr, 6); - ph = (phandle *) get_property(np, "phy-handle", NULL); + ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); if (phy == NULL) { @@ -118,8 +118,8 @@ static int __init tsi108_eth_of_init(voi goto unreg; } - id = (u32 *) get_property(phy, "reg", NULL); - phy_id = (u32 *) get_property(phy, "phy-id", NULL); + id = of_get_property(phy, "reg", NULL); + phy_id = of_get_property(phy, "phy-id", NULL); ret = of_address_to_resource(phy, 0, &res); if (ret) { of_node_put(phy); diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c index ae249c6..58b9e7f 100644 --- a/arch/powerpc/sysdev/tsi108_pci.c +++ b/arch/powerpc/sysdev/tsi108_pci.c @@ -211,7 +211,7 @@ int __init tsi108_setup_pci(struct devic has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); /* Get bus range if any */ - bus_range = get_property(dev, "bus-range", &len); + bus_range = of_get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); diff --git a/arch/powerpc/sysdev/uic.c b/arch/powerpc/sysdev/uic.c new file mode 100644 index 0000000..968fb40 --- /dev/null +++ b/arch/powerpc/sysdev/uic.c @@ -0,0 +1,342 @@ +/* + * arch/powerpc/sysdev/uic.c + * + * IBM PowerPC 4xx Universal Interrupt Controller + * + * Copyright 2007 David Gibson , IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NR_UIC_INTS 32 + +#define UIC_SR 0x0 +#define UIC_ER 0x2 +#define UIC_CR 0x3 +#define UIC_PR 0x4 +#define UIC_TR 0x5 +#define UIC_MSR 0x6 +#define UIC_VR 0x7 +#define UIC_VCR 0x8 + +#define uic_irq_to_hw(virq) (irq_map[virq].hwirq) + +struct uic *primary_uic; + +struct uic { + int index; + int dcrbase; + + spinlock_t lock; + + /* The remapper for this UIC */ + struct irq_host *irqhost; + + /* For secondary UICs, the cascade interrupt's irqaction */ + struct irqaction cascade; + + /* The device node of the interrupt controller */ + struct device_node *of_node; +}; + +static void uic_unmask_irq(unsigned int virq) +{ + struct uic *uic = get_irq_chip_data(virq); + unsigned int src = uic_irq_to_hw(virq); + unsigned long flags; + u32 er; + + spin_lock_irqsave(&uic->lock, flags); + er = mfdcr(uic->dcrbase + UIC_ER); + er |= 1 << (31 - src); + mtdcr(uic->dcrbase + UIC_ER, er); + spin_unlock_irqrestore(&uic->lock, flags); +} + +static void uic_mask_irq(unsigned int virq) +{ + struct uic *uic = get_irq_chip_data(virq); + unsigned int src = uic_irq_to_hw(virq); + unsigned long flags; + u32 er; + + spin_lock_irqsave(&uic->lock, flags); + er = mfdcr(uic->dcrbase + UIC_ER); + er &= ~(1 << (31 - src)); + mtdcr(uic->dcrbase + UIC_ER, er); + spin_unlock_irqrestore(&uic->lock, flags); +} + +static void uic_ack_irq(unsigned int virq) +{ + struct uic *uic = get_irq_chip_data(virq); + unsigned int src = uic_irq_to_hw(virq); + unsigned long flags; + + spin_lock_irqsave(&uic->lock, flags); + mtdcr(uic->dcrbase + UIC_SR, 1 << (31-src)); + spin_unlock_irqrestore(&uic->lock, flags); +} + +static int uic_set_irq_type(unsigned int virq, unsigned int flow_type) +{ + struct uic *uic = get_irq_chip_data(virq); + unsigned int src = uic_irq_to_hw(virq); + struct irq_desc *desc = get_irq_desc(virq); + unsigned long flags; + int trigger, polarity; + u32 tr, pr, mask; + + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_NONE: + uic_mask_irq(virq); + return 0; + + case IRQ_TYPE_EDGE_RISING: + trigger = 1; polarity = 1; + break; + case IRQ_TYPE_EDGE_FALLING: + trigger = 1; polarity = 0; + break; + case IRQ_TYPE_LEVEL_HIGH: + trigger = 0; polarity = 1; + break; + case IRQ_TYPE_LEVEL_LOW: + trigger = 0; polarity = 0; + break; + default: + return -EINVAL; + } + + mask = ~(1 << (31 - src)); + + spin_lock_irqsave(&uic->lock, flags); + tr = mfdcr(uic->dcrbase + UIC_TR); + pr = mfdcr(uic->dcrbase + UIC_PR); + tr = (tr & mask) | (trigger << (31-src)); + pr = (pr & mask) | (polarity << (31-src)); + + mtdcr(uic->dcrbase + UIC_PR, pr); + mtdcr(uic->dcrbase + UIC_TR, tr); + + desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); + desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; + if (trigger) + desc->status |= IRQ_LEVEL; + + spin_unlock_irqrestore(&uic->lock, flags); + + return 0; +} + +static struct irq_chip uic_irq_chip = { + .typename = " UIC ", + .unmask = uic_unmask_irq, + .mask = uic_mask_irq, +/* .mask_ack = uic_mask_irq_and_ack, */ + .ack = uic_ack_irq, + .set_type = uic_set_irq_type, +}; + +static int uic_host_match(struct irq_host *h, struct device_node *node) +{ + struct uic *uic = h->host_data; + return uic->of_node == node; +} + +static int uic_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct uic *uic = h->host_data; + + set_irq_chip_data(virq, uic); + /* Despite the name, handle_level_irq() works for both level + * and edge irqs on UIC. FIXME: check this is correct */ + set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq); + + /* Set default irq type */ + set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static int uic_host_xlate(struct irq_host *h, struct device_node *ct, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) + +{ + /* UIC intspecs must have 2 cells */ + BUG_ON(intsize != 2); + *out_hwirq = intspec[0]; + *out_type = intspec[1]; + return 0; +} + +static struct irq_host_ops uic_host_ops = { + .match = uic_host_match, + .map = uic_host_map, + .xlate = uic_host_xlate, +}; + +irqreturn_t uic_cascade(int virq, void *data) +{ + struct uic *uic = data; + u32 msr; + int src; + int subvirq; + + msr = mfdcr(uic->dcrbase + UIC_MSR); + src = 32 - ffs(msr); + + subvirq = irq_linear_revmap(uic->irqhost, src); + generic_handle_irq(subvirq); + + return IRQ_HANDLED; +} + +static struct uic * __init uic_init_one(struct device_node *node) +{ + struct uic *uic; + const u32 *indexp, *dcrreg; + int len; + + BUG_ON(! device_is_compatible(node, "ibm,uic")); + + uic = alloc_bootmem(sizeof(*uic)); + if (! uic) + return NULL; /* FIXME: panic? */ + + memset(uic, 0, sizeof(*uic)); + spin_lock_init(&uic->lock); + uic->of_node = of_node_get(node); + indexp = of_get_property(node, "cell-index", &len); + if (!indexp || (len != sizeof(u32))) { + printk(KERN_ERR "uic: Device node %s has missing or invalid " + "cell-index property\n", node->full_name); + return NULL; + } + uic->index = *indexp; + + dcrreg = of_get_property(node, "dcr-reg", &len); + if (!dcrreg || (len != 2*sizeof(u32))) { + printk(KERN_ERR "uic: Device node %s has missing or invalid " + "dcr-reg property\n", node->full_name); + return NULL; + } + uic->dcrbase = *dcrreg; + + uic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, NR_UIC_INTS, + &uic_host_ops, -1); + if (! uic->irqhost) { + of_node_put(node); + return NULL; /* FIXME: panic? */ + } + + uic->irqhost->host_data = uic; + + /* Start with all interrupts disabled, level and non-critical */ + mtdcr(uic->dcrbase + UIC_ER, 0); + mtdcr(uic->dcrbase + UIC_CR, 0); + mtdcr(uic->dcrbase + UIC_TR, 0); + /* Clear any pending interrupts, in case the firmware left some */ + mtdcr(uic->dcrbase + UIC_SR, 0xffffffff); + + printk ("UIC%d (%d IRQ sources) at DCR 0x%x\n", uic->index, + NR_UIC_INTS, uic->dcrbase); + + return uic; +} + +void __init uic_init_tree(void) +{ + struct device_node *np; + struct uic *uic; + const u32 *interrupts; + + /* First locate and initialize the top-level UIC */ + + np = of_find_compatible_node(NULL, NULL, "ibm,uic"); + while (np) { + interrupts = of_get_property(np, "interrupts", NULL); + if (! interrupts) + break; + + np = of_find_compatible_node(np, NULL, "ibm,uic"); + } + + BUG_ON(!np); /* uic_init_tree() assumes there's a UIC as the + * top-level interrupt controller */ + primary_uic = uic_init_one(np); + if (! primary_uic) + panic("Unable to initialize primary UIC %s\n", np->full_name); + + irq_set_default_host(primary_uic->irqhost); + of_node_put(np); + + /* The scan again for cascaded UICs */ + np = of_find_compatible_node(NULL, NULL, "ibm,uic"); + while (np) { + interrupts = of_get_property(np, "interrupts", NULL); + if (interrupts) { + /* Secondary UIC */ + int cascade_virq; + int ret; + + uic = uic_init_one(np); + if (! uic) + panic("Unable to initialize a secondary UIC %s\n", + np->full_name); + + cascade_virq = irq_of_parse_and_map(np, 0); + + uic->cascade.handler = uic_cascade; + uic->cascade.name = "UIC cascade"; + uic->cascade.dev_id = uic; + + ret = setup_irq(cascade_virq, &uic->cascade); + if (ret) + printk(KERN_ERR "Failed to setup_irq(%d) for " + "UIC%d cascade\n", cascade_virq, + uic->index); + + /* FIXME: setup critical cascade?? */ + } + + np = of_find_compatible_node(np, NULL, "ibm,uic"); + } +} + +/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ +unsigned int uic_get_irq(void) +{ + u32 msr; + int src; + + BUG_ON(! primary_uic); + + msr = mfdcr(primary_uic->dcrbase + UIC_MSR); + src = 32 - ffs(msr); + + return irq_linear_revmap(primary_uic->irqhost, src); +} diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index bf299b6..b481db1 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -330,18 +330,17 @@ #endif static int xmon_core(struct pt_regs *regs, int fromipi) { int cmd = 0; - unsigned long msr; struct bpt *bp; long recurse_jmp[JMP_BUF_LEN]; unsigned long offset; + unsigned long flags; #ifdef CONFIG_SMP int cpu; int secondary; unsigned long timeout; #endif - msr = mfmsr(); - mtmsr(msr & ~MSR_EE); /* disable interrupts */ + local_irq_save(flags); bp = in_breakpoint_table(regs->nip, &offset); if (bp != NULL) { @@ -516,7 +515,7 @@ #endif insert_cpu_bpts(); - mtmsr(msr); /* restore interrupt enable */ + local_irq_restore(flags); return cmd != 'X' && cmd != EOF; } @@ -1360,8 +1359,12 @@ static void print_bug_trap(struct pt_reg if (is_warning_bug(bug)) return; +#ifdef CONFIG_DEBUG_BUGVERBOSE printf("kernel BUG at %s:%u!\n", bug->file, bug->line); +#else + printf("kernel BUG at %p!\n", (void *)bug->bug_addr); +#endif } void excprint(struct pt_regs *fp) diff --git a/arch/ppc/8260_io/enet.c b/arch/ppc/8260_io/enet.c index a6056c2..48ce84f 100644 --- a/arch/ppc/8260_io/enet.c +++ b/arch/ppc/8260_io/enet.c @@ -477,7 +477,6 @@ #endif cep->stats.rx_dropped++; } else { - skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, (unsigned char *)__va(bdp->cbd_bufaddr), diff --git a/arch/ppc/8260_io/fcc_enet.c b/arch/ppc/8260_io/fcc_enet.c index 06b84c3..9db825f 100644 --- a/arch/ppc/8260_io/fcc_enet.c +++ b/arch/ppc/8260_io/fcc_enet.c @@ -734,7 +734,6 @@ #endif cep->stats.rx_dropped++; } else { - skb->dev = dev; skb_put(skb,pkt_len); /* Make room */ eth_copy_and_sum(skb, (unsigned char *)__va(bdp->cbd_bufaddr), diff --git a/arch/ppc/8xx_io/Kconfig b/arch/ppc/8xx_io/Kconfig index 57dacf9..c623e44 100644 --- a/arch/ppc/8xx_io/Kconfig +++ b/arch/ppc/8xx_io/Kconfig @@ -74,10 +74,6 @@ config ENET_BIG_BUFFERS Allocate large buffers for MPC8xx Ethernet. Increases throughput and decreases the likelihood of dropped packets, but costs memory. -config HTDMSOUND - bool "Embedded Planet HIOX Audio" - depends on SOUND=y - # This doesn't really belong here, but it is convenient to ask # 8xx specific questions. comment "Generic MPC8xx Options" diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile index d876018..1051a06 100644 --- a/arch/ppc/8xx_io/Makefile +++ b/arch/ppc/8xx_io/Makefile @@ -7,4 +7,3 @@ obj-y := commproc.o obj-$(CONFIG_FEC_ENET) += fec.o obj-$(CONFIG_SCC_ENET) += enet.o obj-$(CONFIG_UCODE_PATCH) += micropatch.o -obj-$(CONFIG_HTDMSOUND) += cs4218_tdm.o diff --git a/arch/ppc/8xx_io/cs4218.h b/arch/ppc/8xx_io/cs4218.h deleted file mode 100644 index e5f9430..0000000 --- a/arch/ppc/8xx_io/cs4218.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef _cs4218_h_ -/* - * Hacked version of linux/drivers/sound/dmasound/dmasound.h - * - * - * Minor numbers for the sound driver. - * - * Unfortunately Creative called the codec chip of SB as a DSP. For this - * reason the /dev/dsp is reserved for digitized audio use. There is a - * device for true DSP processors but it will be called something else. - * In v3.0 it's /dev/sndproc but this could be a temporary solution. - */ -#define _cs4218_h_ - -#include - -#define SND_NDEVS 256 /* Number of supported devices */ -#define SND_DEV_CTL 0 /* Control port /dev/mixer */ -#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM - synthesizer and MIDI output) */ -#define SND_DEV_MIDIN 2 /* Raw midi access */ -#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ -#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ -#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ -#define SND_DEV_STATUS 6 /* /dev/sndstat */ -/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ -#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ -#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ -#define SND_DEV_PSS SND_DEV_SNDPROC - -/* switch on various prinks */ -#define DEBUG_DMASOUND 1 - -#define MAX_AUDIO_DEV 5 -#define MAX_MIXER_DEV 4 -#define MAX_SYNTH_DEV 3 -#define MAX_MIDI_DEV 6 -#define MAX_TIMER_DEV 3 - -#define MAX_CATCH_RADIUS 10 - -#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) -#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) - -#define IOCTL_IN(arg, ret) \ - do { int error = get_user(ret, (int *)(arg)); \ - if (error) return error; \ - } while (0) -#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) - -static inline int ioctl_return(int *addr, int value) -{ - return value < 0 ? value : put_user(value, addr); -} - -#define HAS_RECORD - - /* - * Initialization - */ - -/* description of the set-up applies to either hard or soft settings */ - -typedef struct { - int format; /* AFMT_* */ - int stereo; /* 0 = mono, 1 = stereo */ - int size; /* 8/16 bit*/ - int speed; /* speed */ -} SETTINGS; - - /* - * Machine definitions - */ - -typedef struct { - const char *name; - const char *name2; - void (*open)(void); - void (*release)(void); - void *(*dma_alloc)(unsigned int, gfp_t); - void (*dma_free)(void *, unsigned int); - int (*irqinit)(void); -#ifdef MODULE - void (*irqcleanup)(void); -#endif - void (*init)(void); - void (*silence)(void); - int (*setFormat)(int); - int (*setVolume)(int); - int (*setBass)(int); - int (*setTreble)(int); - int (*setGain)(int); - void (*play)(void); - void (*record)(void); /* optional */ - void (*mixer_init)(void); /* optional */ - int (*mixer_ioctl)(u_int, u_long); /* optional */ - int (*write_sq_setup)(void); /* optional */ - int (*read_sq_setup)(void); /* optional */ - int (*sq_open)(mode_t); /* optional */ - int (*state_info)(char *, size_t); /* optional */ - void (*abort_read)(void); /* optional */ - int min_dsp_speed; - int max_dsp_speed; - int version ; - int hardware_afmts ; /* OSS says we only return h'ware info */ - /* when queried via SNDCTL_DSP_GETFMTS */ - int capabilities ; /* low-level reply to SNDCTL_DSP_GETCAPS */ - SETTINGS default_hard ; /* open() or init() should set something valid */ - SETTINGS default_soft ; /* you can make it look like old OSS, if you want to */ -} MACHINE; - - /* - * Low level stuff - */ - -typedef struct { - ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); -} TRANS; - - - /* - * Sound queue stuff, the heart of the driver - */ - -struct sound_queue { - /* buffers allocated for this queue */ - int numBufs; /* real limits on what the user can have */ - int bufSize; /* in bytes */ - char **buffers; - - /* current parameters */ - int locked ; /* params cannot be modified when != 0 */ - int user_frags ; /* user requests this many */ - int user_frag_size ; /* of this size */ - int max_count; /* actual # fragments <= numBufs */ - int block_size; /* internal block size in bytes */ - int max_active; /* in-use fragments <= max_count */ - - /* it shouldn't be necessary to declare any of these volatile */ - int front, rear, count; - int rear_size; - /* - * The use of the playing field depends on the hardware - * - * Atari, PMac: The number of frames that are loaded/playing - * - * Amiga: Bit 0 is set: a frame is loaded - * Bit 1 is set: a frame is playing - */ - int active; - wait_queue_head_t action_queue, open_queue, sync_queue; - int open_mode; - int busy, syncing, xruns, died; -}; - -#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) -#define WAKE_UP(queue) (wake_up_interruptible(&queue)) - -#endif /* _cs4218_h_ */ diff --git a/arch/ppc/8xx_io/cs4218_tdm.c b/arch/ppc/8xx_io/cs4218_tdm.c deleted file mode 100644 index a956f28..0000000 --- a/arch/ppc/8xx_io/cs4218_tdm.c +++ /dev/null @@ -1,2833 +0,0 @@ - -/* This is a modified version of linux/drivers/sound/dmasound.c to - * support the CS4218 codec on the 8xx TDM port. Thanks to everyone - * that contributed to the dmasound software (which includes me :-). - * - * The CS4218 is configured in Mode 4, sub-mode 0. This provides - * left/right data only on the TDM port, as a 32-bit word, per frame - * pulse. The control of the CS4218 is provided by some other means, - * like the SPI port. - * Dan Malek (dmalek@jlc.net) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* Should probably do something different with this path name..... - * Actually, I should just stop using it... - */ -#include "cs4218.h" -#include - -#include -#include -#include - -#define DMASND_CS4218 5 - -#define MAX_CATCH_RADIUS 10 -#define MIN_BUFFERS 4 -#define MIN_BUFSIZE 4 -#define MAX_BUFSIZE 128 - -#define HAS_8BIT_TABLES - -static int sq_unit = -1; -static int mixer_unit = -1; -static int state_unit = -1; -static int irq_installed = 0; -static char **sound_buffers = NULL; -static char **sound_read_buffers = NULL; - -static DEFINE_SPINLOCK(cs4218_lock); - -/* Local copies of things we put in the control register. Output - * volume, like most codecs is really attenuation. - */ -static int cs4218_rate_index; - -/* - * Stuff for outputting a beep. The values range from -327 to +327 - * so we can multiply by an amplitude in the range 0..100 to get a - * signed short value to put in the output buffer. - */ -static short beep_wform[256] = { - 0, 40, 79, 117, 153, 187, 218, 245, - 269, 288, 304, 316, 323, 327, 327, 324, - 318, 310, 299, 288, 275, 262, 249, 236, - 224, 213, 204, 196, 190, 186, 183, 182, - 182, 183, 186, 189, 192, 196, 200, 203, - 206, 208, 209, 209, 209, 207, 204, 201, - 197, 193, 188, 183, 179, 174, 170, 166, - 163, 161, 160, 159, 159, 160, 161, 162, - 164, 166, 168, 169, 171, 171, 171, 170, - 169, 167, 163, 159, 155, 150, 144, 139, - 133, 128, 122, 117, 113, 110, 107, 105, - 103, 103, 103, 103, 104, 104, 105, 105, - 105, 103, 101, 97, 92, 86, 78, 68, - 58, 45, 32, 18, 3, -11, -26, -41, - -55, -68, -79, -88, -95, -100, -102, -102, - -99, -93, -85, -75, -62, -48, -33, -16, - 0, 16, 33, 48, 62, 75, 85, 93, - 99, 102, 102, 100, 95, 88, 79, 68, - 55, 41, 26, 11, -3, -18, -32, -45, - -58, -68, -78, -86, -92, -97, -101, -103, - -105, -105, -105, -104, -104, -103, -103, -103, - -103, -105, -107, -110, -113, -117, -122, -128, - -133, -139, -144, -150, -155, -159, -163, -167, - -169, -170, -171, -171, -171, -169, -168, -166, - -164, -162, -161, -160, -159, -159, -160, -161, - -163, -166, -170, -174, -179, -183, -188, -193, - -197, -201, -204, -207, -209, -209, -209, -208, - -206, -203, -200, -196, -192, -189, -186, -183, - -182, -182, -183, -186, -190, -196, -204, -213, - -224, -236, -249, -262, -275, -288, -299, -310, - -318, -324, -327, -327, -323, -316, -304, -288, - -269, -245, -218, -187, -153, -117, -79, -40, -}; - -#define BEEP_SPEED 5 /* 22050 Hz sample rate */ -#define BEEP_BUFLEN 512 -#define BEEP_VOLUME 15 /* 0 - 100 */ - -static int beep_volume = BEEP_VOLUME; -static int beep_playing = 0; -static int beep_state = 0; -static short *beep_buf; -static void (*orig_mksound)(unsigned int, unsigned int); - -/* This is found someplace else......I guess in the keyboard driver - * we don't include. - */ -static void (*kd_mksound)(unsigned int, unsigned int); - -static int catchRadius = 0; -static int numBufs = 4, bufSize = 32; -static int numReadBufs = 4, readbufSize = 32; - - -/* TDM/Serial transmit and receive buffer descriptors. -*/ -static volatile cbd_t *rx_base, *rx_cur, *tx_base, *tx_cur; - -module_param(catchRadius, int, 0); -module_param(numBufs, int, 0); -module_param(bufSize, int, 0); -module_param(numreadBufs, int, 0); -module_param(readbufSize, int, 0); - -#define arraysize(x) (sizeof(x)/sizeof(*(x))) -#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) -#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) - -#define IOCTL_IN(arg, ret) \ - do { int error = get_user(ret, (int *)(arg)); \ - if (error) return error; \ - } while (0) -#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) - -/* CS4218 serial port control in mode 4. -*/ -#define CS_INTMASK ((uint)0x40000000) -#define CS_DO1 ((uint)0x20000000) -#define CS_LATTEN ((uint)0x1f000000) -#define CS_RATTEN ((uint)0x00f80000) -#define CS_MUTE ((uint)0x00040000) -#define CS_ISL ((uint)0x00020000) -#define CS_ISR ((uint)0x00010000) -#define CS_LGAIN ((uint)0x0000f000) -#define CS_RGAIN ((uint)0x00000f00) - -#define CS_LATTEN_SET(X) (((X) & 0x1f) << 24) -#define CS_RATTEN_SET(X) (((X) & 0x1f) << 19) -#define CS_LGAIN_SET(X) (((X) & 0x0f) << 12) -#define CS_RGAIN_SET(X) (((X) & 0x0f) << 8) - -#define CS_LATTEN_GET(X) (((X) >> 24) & 0x1f) -#define CS_RATTEN_GET(X) (((X) >> 19) & 0x1f) -#define CS_LGAIN_GET(X) (((X) >> 12) & 0x0f) -#define CS_RGAIN_GET(X) (((X) >> 8) & 0x0f) - -/* The control register is effectively write only. We have to keep a copy - * of what we write. - */ -static uint cs4218_control; - -/* A place to store expanding information. -*/ -static int expand_bal; -static int expand_data; - -/* Since I can't make the microcode patch work for the SPI, I just - * clock the bits using software. - */ -static void sw_spi_init(void); -static void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt); -static uint cs4218_ctl_write(uint ctlreg); - -/*** Some low level helpers **************************************************/ - -/* 16 bit mu-law */ - -static short ulaw2dma16[] = { - -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, - -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, - -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, - -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0, -}; - -/* 16 bit A-law */ - -static short alaw2dma16[] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, - -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, - -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, - -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848, -}; - - -/*** Translations ************************************************************/ - - -static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); - - -/*** Low level stuff *********************************************************/ - -struct cs_sound_settings { - MACHINE mach; /* machine dependent things */ - SETTINGS hard; /* hardware settings */ - SETTINGS soft; /* software settings */ - SETTINGS dsp; /* /dev/dsp default settings */ - TRANS *trans_write; /* supported translations for playback */ - TRANS *trans_read; /* supported translations for record */ - int volume_left; /* volume (range is machine dependent) */ - int volume_right; - int bass; /* tone (range is machine dependent) */ - int treble; - int gain; - int minDev; /* minor device number currently open */ -}; - -static struct cs_sound_settings sound; - -static void *CS_Alloc(unsigned int size, gfp_t flags); -static void CS_Free(void *ptr, unsigned int size); -static int CS_IrqInit(void); -#ifdef MODULE -static void CS_IrqCleanup(void); -#endif /* MODULE */ -static void CS_Silence(void); -static void CS_Init(void); -static void CS_Play(void); -static void CS_Record(void); -static int CS_SetFormat(int format); -static int CS_SetVolume(int volume); -static void cs4218_tdm_tx_intr(void *devid); -static void cs4218_tdm_rx_intr(void *devid); -static void cs4218_intr(void *devid); -static int cs_get_volume(uint reg); -static int cs_volume_setter(int volume, int mute); -static int cs_get_gain(uint reg); -static int cs_set_gain(int gain); -static void cs_mksound(unsigned int hz, unsigned int ticks); -static void cs_nosound(unsigned long xx); - -/*** Mid level stuff *********************************************************/ - - -static void sound_silence(void); -static void sound_init(void); -static int sound_set_format(int format); -static int sound_set_speed(int speed); -static int sound_set_stereo(int stereo); -static int sound_set_volume(int volume); - -static ssize_t sound_copy_translate(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t sound_copy_translate_read(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); - - -/* - * /dev/mixer abstraction - */ - -struct sound_mixer { - int busy; - int modify_counter; -}; - -static struct sound_mixer mixer; - -static struct sound_queue sq; -static struct sound_queue read_sq; - -#define sq_block_address(i) (sq.buffers[i]) -#define SIGNAL_RECEIVED (signal_pending(current)) -#define NON_BLOCKING(open_mode) (open_mode & O_NONBLOCK) -#define ONE_SECOND HZ /* in jiffies (100ths of a second) */ -#define NO_TIME_LIMIT 0xffffffff - -/* - * /dev/sndstat - */ - -struct sound_state { - int busy; - char buf[512]; - int len, ptr; -}; - -static struct sound_state state; - -/*** Common stuff ********************************************************/ - -static long long sound_lseek(struct file *file, long long offset, int orig); - -/*** Config & Setup **********************************************************/ - -void dmasound_setup(char *str, int *ints); - -/*** Translations ************************************************************/ - - -/* ++TeSche: radically changed for new expanding purposes... - * - * These two routines now deal with copying/expanding/translating the samples - * from user space into our buffer at the right frequency. They take care about - * how much data there's actually to read, how much buffer space there is and - * to convert samples into the right frequency/encoding. They will only work on - * complete samples so it may happen they leave some bytes in the input stream - * if the user didn't write a multiple of the current sample size. They both - * return the number of bytes they've used from both streams so you may detect - * such a situation. Luckily all programs should be able to cope with that. - * - * I think I've optimized anything as far as one can do in plain C, all - * variables should fit in registers and the loops are really short. There's - * one loop for every possible situation. Writing a more generalized and thus - * parameterized loop would only produce slower code. Feel free to optimize - * this in assembler if you like. :) - * - * I think these routines belong here because they're not yet really hardware - * independent, especially the fact that the Falcon can play 16bit samples - * only in stereo is hardcoded in both of them! - * - * ++geert: split in even more functions (one per format) - */ - -static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - short *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16; - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = data << 8; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = data << 8; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = (data ^ 0x80) << 8; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = (data ^ 0x80) << 8; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -/* This is the default format of the codec. Signed, 16-bit stereo - * generated by an application shouldn't have to be copied at all. - * We should just get the phsical address of the buffers and update - * the TDM BDs directly. - */ -static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - if (!stereo) { - short *up = (short *) userPtr; - while (count > 0) { - short data; - if (get_user(data, up++)) - return -EFAULT; - *fp++ = data; - *fp++ = data; - count--; - } - } else { - if (copy_from_user(fp, userPtr, count * 4)) - return -EFAULT; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - -static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - short *up = (short *) userPtr; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - while (count > 0) { - int data; - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - *fp++ = data; - if (stereo) { - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - } - *fp++ = data; - count--; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - - -static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned short *table = (unsigned short *) - (sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16); - unsigned int data = expand_data; - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - int bal = expand_bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int utotal, ftotal; - int stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c]; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + table[c]; - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - int bal = expand_bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = c << 8; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + (c << 8); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - int bal = expand_bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = (c ^ 0x80) << 8; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + ((c ^ 0x80) << 8); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - unsigned short *up = (unsigned short *) userPtr; - int bal = expand_bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - unsigned short c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(data, up++)) - return -EFAULT; - if (stereo) { - if (get_user(c, up++)) - return -EFAULT; - data = (data << 16) + c; - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 4: utotal * 2; -} - - -static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - unsigned short *up = (unsigned short *) userPtr; - int bal = expand_bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - unsigned short c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - if (stereo) { - if (get_user(c, up++)) - return -EFAULT; - data = (data << 16) + (c ^ mask); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 4: utotal * 2; -} - -static ssize_t cs4218_ct_s8_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - - val = *p++; - data = val >> 8; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - if (stereo) { - val = *p; - data = val >> 8; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - } - p++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t cs4218_ct_u8_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - - val = *p++; - data = (val >> 8) ^ 0x80; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - if (stereo) { - val = *p; - data = (val >> 8) ^ 0x80; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - } - p++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - if (!stereo) { - short *up = (short *) userPtr; - while (count > 0) { - short data; - data = *fp; - if (put_user(data, up++)) - return -EFAULT; - fp+=2; - count--; - } - } else { - if (copy_to_user((u_char *)userPtr, fp, count * 4)) - return -EFAULT; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - -static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - short *up = (short *) userPtr; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - while (count > 0) { - int data; - - data = *fp++; - data ^= mask; - if (put_user(data, up++)) - return -EFAULT; - if (stereo) { - data = *fp; - data ^= mask; - if (put_user(data, up++)) - return -EFAULT; - } - fp++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - -static TRANS transCSNormal = { - cs4218_ct_law, cs4218_ct_law, cs4218_ct_s8, cs4218_ct_u8, - cs4218_ct_s16, cs4218_ct_u16, cs4218_ct_s16, cs4218_ct_u16 -}; - -static TRANS transCSExpand = { - cs4218_ctx_law, cs4218_ctx_law, cs4218_ctx_s8, cs4218_ctx_u8, - cs4218_ctx_s16, cs4218_ctx_u16, cs4218_ctx_s16, cs4218_ctx_u16 -}; - -static TRANS transCSNormalRead = { - NULL, NULL, cs4218_ct_s8_read, cs4218_ct_u8_read, - cs4218_ct_s16_read, cs4218_ct_u16_read, - cs4218_ct_s16_read, cs4218_ct_u16_read -}; - -/*** Low level stuff *********************************************************/ - -static void *CS_Alloc(unsigned int size, gfp_t flags) -{ - int order; - - size >>= 13; - for (order=0; order < 5; order++) { - if (size == 0) - break; - size >>= 1; - } - return (void *)__get_free_pages(flags, order); -} - -static void CS_Free(void *ptr, unsigned int size) -{ - int order; - - size >>= 13; - for (order=0; order < 5; order++) { - if (size == 0) - break; - size >>= 1; - } - free_pages((ulong)ptr, order); -} - -static int __init CS_IrqInit(void) -{ - cpm_install_handler(CPMVEC_SMC2, cs4218_intr, NULL); - return 1; -} - -#ifdef MODULE -static void CS_IrqCleanup(void) -{ - volatile smc_t *sp; - volatile cpm8xx_t *cp; - - /* First disable transmitter and receiver. - */ - sp = &cpmp->cp_smc[1]; - sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); - - /* And now shut down the SMC. - */ - cp = cpmp; /* Get pointer to Communication Processor */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, - CPM_CR_STOP_TX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - - /* Release the interrupt handler. - */ - cpm_free_handler(CPMVEC_SMC2); - - kfree(beep_buf); - kd_mksound = orig_mksound; -} -#endif /* MODULE */ - -static void CS_Silence(void) -{ - volatile smc_t *sp; - - /* Disable transmitter. - */ - sp = &cpmp->cp_smc[1]; - sp->smc_smcmr &= ~SMCMR_TEN; -} - -/* Frequencies depend upon external oscillator. There are two - * choices, 12.288 and 11.2896 MHz. The RPCG audio supports both through - * and external control register selection bit. - */ -static int cs4218_freqs[] = { - /* 12.288 11.2896 */ - 48000, 44100, - 32000, 29400, - 24000, 22050, - 19200, 17640, - 16000, 14700, - 12000, 11025, - 9600, 8820, - 8000, 7350 -}; - -static void CS_Init(void) -{ - int i, tolerance; - - switch (sound.soft.format) { - case AFMT_S16_LE: - case AFMT_U16_LE: - sound.hard.format = AFMT_S16_LE; - break; - default: - sound.hard.format = AFMT_S16_BE; - break; - } - sound.hard.stereo = 1; - sound.hard.size = 16; - - /* - * If we have a sample rate which is within catchRadius percent - * of the requested value, we don't have to expand the samples. - * Otherwise choose the next higher rate. - */ - i = (sizeof(cs4218_freqs) / sizeof(int)); - do { - tolerance = catchRadius * cs4218_freqs[--i] / 100; - } while (sound.soft.speed > cs4218_freqs[i] + tolerance && i > 0); - if (sound.soft.speed >= cs4218_freqs[i] - tolerance) - sound.trans_write = &transCSNormal; - else - sound.trans_write = &transCSExpand; - sound.trans_read = &transCSNormalRead; - sound.hard.speed = cs4218_freqs[i]; - cs4218_rate_index = i; - - /* The CS4218 has seven selectable clock dividers for the sample - * clock. The HIOX then provides one of two external rates. - * An even numbered frequency table index uses the high external - * clock rate. - */ - *(uint *)HIOX_CSR4_ADDR &= ~(HIOX_CSR4_AUDCLKHI | HIOX_CSR4_AUDCLKSEL); - if ((i & 1) == 0) - *(uint *)HIOX_CSR4_ADDR |= HIOX_CSR4_AUDCLKHI; - i >>= 1; - *(uint *)HIOX_CSR4_ADDR |= (i & HIOX_CSR4_AUDCLKSEL); - - expand_bal = -sound.soft.speed; -} - -static int CS_SetFormat(int format) -{ - int size; - - switch (format) { - case AFMT_QUERY: - return sound.soft.format; - case AFMT_MU_LAW: - case AFMT_A_LAW: - case AFMT_U8: - case AFMT_S8: - size = 8; - break; - case AFMT_S16_BE: - case AFMT_U16_BE: - case AFMT_S16_LE: - case AFMT_U16_LE: - size = 16; - break; - default: /* :-) */ - printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", - format); - size = 8; - format = AFMT_U8; - } - - sound.soft.format = format; - sound.soft.size = size; - if (sound.minDev == SND_DEV_DSP) { - sound.dsp.format = format; - sound.dsp.size = size; - } - - CS_Init(); - - return format; -} - -/* Volume is the amount of attenuation we tell the codec to impose - * on the outputs. There are 32 levels, with 0 the "loudest". - */ -#define CS_VOLUME_TO_MASK(x) (31 - ((((x) - 1) * 31) / 99)) -#define CS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 31)) - -static int cs_get_volume(uint reg) -{ - int volume; - - volume = CS_MASK_TO_VOLUME(CS_LATTEN_GET(reg)); - volume |= CS_MASK_TO_VOLUME(CS_RATTEN_GET(reg)) << 8; - return volume; -} - -static int cs_volume_setter(int volume, int mute) -{ - uint tempctl; - - if (mute && volume == 0) { - tempctl = cs4218_control | CS_MUTE; - } else { - tempctl = cs4218_control & ~CS_MUTE; - tempctl = tempctl & ~(CS_LATTEN | CS_RATTEN); - tempctl |= CS_LATTEN_SET(CS_VOLUME_TO_MASK(volume & 0xff)); - tempctl |= CS_RATTEN_SET(CS_VOLUME_TO_MASK((volume >> 8) & 0xff)); - volume = cs_get_volume(tempctl); - } - if (tempctl != cs4218_control) { - cs4218_ctl_write(tempctl); - } - return volume; -} - - -/* Gain has 16 steps from 0 to 15. These are in 1.5dB increments from - * 0 (no gain) to 22.5 dB. - */ -#define CS_RECLEVEL_TO_GAIN(v) \ - ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) -#define CS_GAIN_TO_RECLEVEL(v) (((v) * 20 + 2) / 3) - -static int cs_get_gain(uint reg) -{ - int gain; - - gain = CS_GAIN_TO_RECLEVEL(CS_LGAIN_GET(reg)); - gain |= CS_GAIN_TO_RECLEVEL(CS_RGAIN_GET(reg)) << 8; - return gain; -} - -static int cs_set_gain(int gain) -{ - uint tempctl; - - tempctl = cs4218_control & ~(CS_LGAIN | CS_RGAIN); - tempctl |= CS_LGAIN_SET(CS_RECLEVEL_TO_GAIN(gain & 0xff)); - tempctl |= CS_RGAIN_SET(CS_RECLEVEL_TO_GAIN((gain >> 8) & 0xff)); - gain = cs_get_gain(tempctl); - - if (tempctl != cs4218_control) { - cs4218_ctl_write(tempctl); - } - return gain; -} - -static int CS_SetVolume(int volume) -{ - return cs_volume_setter(volume, CS_MUTE); -} - -static void CS_Play(void) -{ - int i, count; - unsigned long flags; - volatile cbd_t *bdp; - volatile cpm8xx_t *cp; - - /* Protect buffer */ - spin_lock_irqsave(&cs4218_lock, flags); -#if 0 - if (awacs_beep_state) { - /* sound takes precedence over beeps */ - out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (awacs_rate_index << 8)); - out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count]))); - - beep_playing = 0; - awacs_beep_state = 0; - } -#endif - i = sq.front + sq.active; - if (i >= sq.max_count) - i -= sq.max_count; - while (sq.active < 2 && sq.active < sq.count) { - count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size; - if (count < sq.block_size && !sq.syncing) - /* last block not yet filled, and we're not syncing. */ - break; - - bdp = &tx_base[i]; - bdp->cbd_datlen = count; - - flush_dcache_range((ulong)sound_buffers[i], - (ulong)(sound_buffers[i] + count)); - - if (++i >= sq.max_count) - i = 0; - - if (sq.active == 0) { - /* The SMC does not load its fifo until the first - * TDM frame pulse, so the transmit data gets shifted - * by one word. To compensate for this, we incorrectly - * transmit the first buffer and shorten it by one - * word. Subsequent buffers are then aligned properly. - */ - bdp->cbd_datlen -= 2; - - /* Start up the SMC Transmitter. - */ - cp = cpmp; - cp->cp_smc[1].smc_smcmr |= SMCMR_TEN; - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, - CPM_CR_RESTART_TX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - } - - /* Buffer is ready now. - */ - bdp->cbd_sc |= BD_SC_READY; - - ++sq.active; - } - spin_unlock_irqrestore(&cs4218_lock, flags); -} - - -static void CS_Record(void) -{ - unsigned long flags; - volatile smc_t *sp; - - if (read_sq.active) - return; - - /* Protect buffer */ - spin_lock_irqsave(&cs4218_lock, flags); - - /* This is all we have to do......Just start it up. - */ - sp = &cpmp->cp_smc[1]; - sp->smc_smcmr |= SMCMR_REN; - - read_sq.active = 1; - - spin_unlock_irqrestore(&cs4218_lock, flags); -} - - -static void -cs4218_tdm_tx_intr(void *devid) -{ - int i = sq.front; - volatile cbd_t *bdp; - - while (sq.active > 0) { - bdp = &tx_base[i]; - if (bdp->cbd_sc & BD_SC_READY) - break; /* this frame is still going */ - --sq.count; - --sq.active; - if (++i >= sq.max_count) - i = 0; - } - if (i != sq.front) - WAKE_UP(sq.action_queue); - sq.front = i; - - CS_Play(); - - if (!sq.active) - WAKE_UP(sq.sync_queue); -} - - -static void -cs4218_tdm_rx_intr(void *devid) -{ - - /* We want to blow 'em off when shutting down. - */ - if (read_sq.active == 0) - return; - - /* Check multiple buffers in case we were held off from - * interrupt processing for a long time. Geeze, I really hope - * this doesn't happen. - */ - while ((rx_base[read_sq.rear].cbd_sc & BD_SC_EMPTY) == 0) { - - /* Invalidate the data cache range for this buffer. - */ - invalidate_dcache_range( - (uint)(sound_read_buffers[read_sq.rear]), - (uint)(sound_read_buffers[read_sq.rear] + read_sq.block_size)); - - /* Make buffer available again and move on. - */ - rx_base[read_sq.rear].cbd_sc |= BD_SC_EMPTY; - read_sq.rear++; - - /* Wrap the buffer ring. - */ - if (read_sq.rear >= read_sq.max_active) - read_sq.rear = 0; - - /* If we have caught up to the front buffer, bump it. - * This will cause weird (but not fatal) results if the - * read loop is currently using this buffer. The user is - * behind in this case anyway, so weird things are going - * to happen. - */ - if (read_sq.rear == read_sq.front) { - read_sq.front++; - if (read_sq.front >= read_sq.max_active) - read_sq.front = 0; - } - } - - WAKE_UP(read_sq.action_queue); -} - -static void cs_nosound(unsigned long xx) -{ - unsigned long flags; - - /* not sure if this is needed, since hardware command is #if 0'd */ - spin_lock_irqsave(&cs4218_lock, flags); - if (beep_playing) { -#if 0 - st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); -#endif - beep_playing = 0; - } - spin_unlock_irqrestore(&cs4218_lock, flags); -} - -static DEFINE_TIMER(beep_timer, cs_nosound, 0, 0); - -static void cs_mksound(unsigned int hz, unsigned int ticks) -{ - unsigned long flags; - int beep_speed = BEEP_SPEED; - int srate = cs4218_freqs[beep_speed]; - int period, ncycles, nsamples; - int i, j, f; - short *p; - static int beep_hz_cache; - static int beep_nsamples_cache; - static int beep_volume_cache; - - if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { -#if 1 - /* this is a hack for broken X server code */ - hz = 750; - ticks = 12; -#else - /* cancel beep currently playing */ - awacs_nosound(0); - return; -#endif - } - /* lock while modifying beep_timer */ - spin_lock_irqsave(&cs4218_lock, flags); - del_timer(&beep_timer); - if (ticks) { - beep_timer.expires = jiffies + ticks; - add_timer(&beep_timer); - } - if (beep_playing || sq.active || beep_buf == NULL) { - spin_unlock_irqrestore(&cs4218_lock, flags); - return; /* too hard, sorry :-( */ - } - beep_playing = 1; -#if 0 - st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); -#endif - spin_unlock_irqrestore(&cs4218_lock, flags); - - if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { - nsamples = beep_nsamples_cache; - } else { - period = srate * 256 / hz; /* fixed point */ - ncycles = BEEP_BUFLEN * 256 / period; - nsamples = (period * ncycles) >> 8; - f = ncycles * 65536 / nsamples; - j = 0; - p = beep_buf; - for (i = 0; i < nsamples; ++i, p += 2) { - p[0] = p[1] = beep_wform[j >> 8] * beep_volume; - j = (j + f) & 0xffff; - } - beep_hz_cache = hz; - beep_volume_cache = beep_volume; - beep_nsamples_cache = nsamples; - } - -#if 0 - st_le16(&beep_dbdma_cmd->req_count, nsamples*4); - st_le16(&beep_dbdma_cmd->xfer_status, 0); - st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); - st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); - awacs_beep_state = 1; - - spin_lock_irqsave(&cs4218_lock, flags); - if (beep_playing) { /* i.e. haven't been terminated already */ - out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (beep_speed << 8)); - out_le32(&awacs->byteswap, 0); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); - out_le32(&awacs_txdma->control, RUN | (RUN << 16)); - } - spin_unlock_irqrestore(&cs4218_lock, flags); -#endif -} - -static MACHINE mach_cs4218 = { - .owner = THIS_MODULE, - .name = "HIOX CS4218", - .name2 = "Built-in Sound", - .dma_alloc = CS_Alloc, - .dma_free = CS_Free, - .irqinit = CS_IrqInit, -#ifdef MODULE - .irqcleanup = CS_IrqCleanup, -#endif /* MODULE */ - .init = CS_Init, - .silence = CS_Silence, - .setFormat = CS_SetFormat, - .setVolume = CS_SetVolume, - .play = CS_Play -}; - - -/*** Mid level stuff *********************************************************/ - - -static void sound_silence(void) -{ - /* update hardware settings one more */ - (*sound.mach.init)(); - - (*sound.mach.silence)(); -} - - -static void sound_init(void) -{ - (*sound.mach.init)(); -} - - -static int sound_set_format(int format) -{ - return(*sound.mach.setFormat)(format); -} - - -static int sound_set_speed(int speed) -{ - if (speed < 0) - return(sound.soft.speed); - - sound.soft.speed = speed; - (*sound.mach.init)(); - if (sound.minDev == SND_DEV_DSP) - sound.dsp.speed = sound.soft.speed; - - return(sound.soft.speed); -} - - -static int sound_set_stereo(int stereo) -{ - if (stereo < 0) - return(sound.soft.stereo); - - stereo = !!stereo; /* should be 0 or 1 now */ - - sound.soft.stereo = stereo; - if (sound.minDev == SND_DEV_DSP) - sound.dsp.stereo = stereo; - (*sound.mach.init)(); - - return(stereo); -} - - -static int sound_set_volume(int volume) -{ - return(*sound.mach.setVolume)(volume); -} - -static ssize_t sound_copy_translate(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; - - switch (sound.soft.format) { - case AFMT_MU_LAW: - ct_func = sound.trans_write->ct_ulaw; - break; - case AFMT_A_LAW: - ct_func = sound.trans_write->ct_alaw; - break; - case AFMT_S8: - ct_func = sound.trans_write->ct_s8; - break; - case AFMT_U8: - ct_func = sound.trans_write->ct_u8; - break; - case AFMT_S16_BE: - ct_func = sound.trans_write->ct_s16be; - break; - case AFMT_U16_BE: - ct_func = sound.trans_write->ct_u16be; - break; - case AFMT_S16_LE: - ct_func = sound.trans_write->ct_s16le; - break; - case AFMT_U16_LE: - ct_func = sound.trans_write->ct_u16le; - break; - } - if (ct_func) - return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); - else - return 0; -} - -static ssize_t sound_copy_translate_read(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; - - switch (sound.soft.format) { - case AFMT_MU_LAW: - ct_func = sound.trans_read->ct_ulaw; - break; - case AFMT_A_LAW: - ct_func = sound.trans_read->ct_alaw; - break; - case AFMT_S8: - ct_func = sound.trans_read->ct_s8; - break; - case AFMT_U8: - ct_func = sound.trans_read->ct_u8; - break; - case AFMT_S16_BE: - ct_func = sound.trans_read->ct_s16be; - break; - case AFMT_U16_BE: - ct_func = sound.trans_read->ct_u16be; - break; - case AFMT_S16_LE: - ct_func = sound.trans_read->ct_s16le; - break; - case AFMT_U16_LE: - ct_func = sound.trans_read->ct_u16le; - break; - } - if (ct_func) - return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); - else - return 0; -} - - -/* - * /dev/mixer abstraction - */ - -static int mixer_open(struct inode *inode, struct file *file) -{ - mixer.busy = 1; - return nonseekable_open(inode, file); -} - - -static int mixer_release(struct inode *inode, struct file *file) -{ - mixer.busy = 0; - return 0; -} - - -static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) -{ - int data; - uint tmpcs; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - mixer.modify_counter++; - if (cmd == OSS_GETVERSION) - return IOCTL_OUT(arg, SOUND_VERSION); - switch (cmd) { - case SOUND_MIXER_INFO: { - mixer_info info; - strlcpy(info.id, "CS4218_TDM", sizeof(info.id)); - strlcpy(info.name, "CS4218_TDM", sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer.modify_counter; - if (copy_to_user((int *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_RECLEV - | SOUND_MASK_ALTPCM; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - if (cs4218_control & CS_DO1) - data = SOUND_MASK_LINE; - else - data = SOUND_MASK_MIC; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE | SOUND_MASK_MIC); - if (data & SOUND_MASK_LINE) - tmpcs = cs4218_control | - (CS_ISL | CS_ISR | CS_DO1); - if (data & SOUND_MASK_MIC) - tmpcs = cs4218_control & - ~(CS_ISL | CS_ISR | CS_DO1); - if (tmpcs != cs4218_control) - cs4218_ctl_write(tmpcs); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_RECLEV; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_READ_VOLUME: - data = (cs4218_control & CS_MUTE)? 0: - cs_get_volume(cs4218_control); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_volume(data)); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = cs_set_gain(data); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = cs_get_gain(cs4218_control); - return IOCTL_OUT(arg, data); - } - - return -EINVAL; -} - - -static const struct file_operations mixer_fops = -{ - .owner = THIS_MODULE, - .llseek = sound_lseek, - .ioctl = mixer_ioctl, - .open = mixer_open, - .release = mixer_release, -}; - - -static void __init mixer_init(void) -{ - mixer_unit = register_sound_mixer(&mixer_fops, -1); - if (mixer_unit < 0) - return; - - mixer.busy = 0; - sound.treble = 0; - sound.bass = 0; - - /* Set Line input, no gain, no attenuation. - */ - cs4218_control = CS_ISL | CS_ISR | CS_DO1; - cs4218_control |= CS_LGAIN_SET(0) | CS_RGAIN_SET(0); - cs4218_control |= CS_LATTEN_SET(0) | CS_RATTEN_SET(0); - cs4218_ctl_write(cs4218_control); -} - - -/* - * Sound queue stuff, the heart of the driver - */ - - -static int sq_allocate_buffers(void) -{ - int i; - - if (sound_buffers) - return 0; - sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); - if (!sound_buffers) - return -ENOMEM; - for (i = 0; i < numBufs; i++) { - sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); - if (!sound_buffers[i]) { - while (i--) - sound.mach.dma_free (sound_buffers[i], bufSize << 10); - kfree (sound_buffers); - sound_buffers = 0; - return -ENOMEM; - } - } - return 0; -} - - -static void sq_release_buffers(void) -{ - int i; - - if (sound_buffers) { - for (i = 0; i < numBufs; i++) - sound.mach.dma_free (sound_buffers[i], bufSize << 10); - kfree (sound_buffers); - sound_buffers = 0; - } -} - - -static int sq_allocate_read_buffers(void) -{ - int i; - - if (sound_read_buffers) - return 0; - sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL); - if (!sound_read_buffers) - return -ENOMEM; - for (i = 0; i < numBufs; i++) { - sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10, - GFP_KERNEL); - if (!sound_read_buffers[i]) { - while (i--) - sound.mach.dma_free (sound_read_buffers[i], - readbufSize << 10); - kfree (sound_read_buffers); - sound_read_buffers = 0; - return -ENOMEM; - } - } - return 0; -} - -static void sq_release_read_buffers(void) -{ - int i; - - if (sound_read_buffers) { - cpmp->cp_smc[1].smc_smcmr &= ~SMCMR_REN; - for (i = 0; i < numReadBufs; i++) - sound.mach.dma_free (sound_read_buffers[i], - bufSize << 10); - kfree (sound_read_buffers); - sound_read_buffers = 0; - } -} - - -static void sq_setup(int numBufs, int bufSize, char **write_buffers) -{ - int i; - volatile cbd_t *bdp; - volatile cpm8xx_t *cp; - volatile smc_t *sp; - - /* Make sure the SMC transmit is shut down. - */ - cp = cpmp; - sp = &cpmp->cp_smc[1]; - sp->smc_smcmr &= ~SMCMR_TEN; - - sq.max_count = numBufs; - sq.max_active = numBufs; - sq.block_size = bufSize; - sq.buffers = write_buffers; - - sq.front = sq.count = 0; - sq.rear = -1; - sq.syncing = 0; - sq.active = 0; - - bdp = tx_base; - for (i=0; icbd_bufaddr = virt_to_bus(write_buffers[i]); - bdp++; - } - - /* This causes the SMC to sync up with the first buffer again. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_TX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); -} - -static void read_sq_setup(int numBufs, int bufSize, char **read_buffers) -{ - int i; - volatile cbd_t *bdp; - volatile cpm8xx_t *cp; - volatile smc_t *sp; - - /* Make sure the SMC receive is shut down. - */ - cp = cpmp; - sp = &cpmp->cp_smc[1]; - sp->smc_smcmr &= ~SMCMR_REN; - - read_sq.max_count = numBufs; - read_sq.max_active = numBufs; - read_sq.block_size = bufSize; - read_sq.buffers = read_buffers; - - read_sq.front = read_sq.count = 0; - read_sq.rear = 0; - read_sq.rear_size = 0; - read_sq.syncing = 0; - read_sq.active = 0; - - bdp = rx_base; - for (i=0; icbd_bufaddr = virt_to_bus(read_buffers[i]); - bdp->cbd_datlen = read_sq.block_size; - bdp++; - } - - /* This causes the SMC to sync up with the first buffer again. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_RX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); -} - - -static void sq_play(void) -{ - (*sound.mach.play)(); -} - - -/* ++TeSche: radically changed this one too */ - -static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, - loff_t *ppos) -{ - ssize_t uWritten = 0; - u_char *dest; - ssize_t uUsed, bUsed, bLeft; - - /* ++TeSche: Is something like this necessary? - * Hey, that's an honest question! Or does any other part of the - * filesystem already checks this situation? I really don't know. - */ - if (uLeft == 0) - return 0; - - /* The interrupt doesn't start to play the last, incomplete frame. - * Thus we can append to it without disabling the interrupts! (Note - * also that sq.rear isn't affected by the interrupt.) - */ - - if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) { - dest = sq_block_address(sq.rear); - bUsed = sq.rear_size; - uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); - if (uUsed <= 0) - return uUsed; - src += uUsed; - uWritten += uUsed; - uLeft -= uUsed; - sq.rear_size = bUsed; - } - - do { - while (sq.count == sq.max_active) { - sq_play(); - if (NON_BLOCKING(sq.open_mode)) - return uWritten > 0 ? uWritten : -EAGAIN; - SLEEP(sq.action_queue); - if (SIGNAL_RECEIVED) - return uWritten > 0 ? uWritten : -EINTR; - } - - /* Here, we can avoid disabling the interrupt by first - * copying and translating the data, and then updating - * the sq variables. Until this is done, the interrupt - * won't see the new frame and we can work on it - * undisturbed. - */ - - dest = sq_block_address((sq.rear+1) % sq.max_count); - bUsed = 0; - bLeft = sq.block_size; - uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); - if (uUsed <= 0) - break; - src += uUsed; - uWritten += uUsed; - uLeft -= uUsed; - if (bUsed) { - sq.rear = (sq.rear+1) % sq.max_count; - sq.rear_size = bUsed; - sq.count++; - } - } while (bUsed); /* uUsed may have been 0 */ - - sq_play(); - - return uUsed < 0? uUsed: uWritten; -} - - -/***********/ - -/* Here is how the values are used for reading. - * The value 'active' simply indicates the DMA is running. This is - * done so the driver semantics are DMA starts when the first read is - * posted. The value 'front' indicates the buffer we should next - * send to the user. The value 'rear' indicates the buffer the DMA is - * currently filling. When 'front' == 'rear' the buffer "ring" is - * empty (we always have an empty available). The 'rear_size' is used - * to track partial offsets into the current buffer. Right now, I just keep - * The DMA running. If the reader can't keep up, the interrupt tosses - * the oldest buffer. We could also shut down the DMA in this case. - */ -static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, - loff_t *ppos) -{ - - ssize_t uRead, bLeft, bUsed, uUsed; - - if (uLeft == 0) - return 0; - - if (!read_sq.active) - CS_Record(); /* Kick off the record process. */ - - uRead = 0; - - /* Move what the user requests, depending upon other options. - */ - while (uLeft > 0) { - - /* When front == rear, the DMA is not done yet. - */ - while (read_sq.front == read_sq.rear) { - if (NON_BLOCKING(read_sq.open_mode)) { - return uRead > 0 ? uRead : -EAGAIN; - } - SLEEP(read_sq.action_queue); - if (SIGNAL_RECEIVED) - return uRead > 0 ? uRead : -EINTR; - } - - /* The amount we move is either what is left in the - * current buffer or what the user wants. - */ - bLeft = read_sq.block_size - read_sq.rear_size; - bUsed = read_sq.rear_size; - uUsed = sound_copy_translate_read(dst, uLeft, - read_sq.buffers[read_sq.front], &bUsed, bLeft); - if (uUsed <= 0) - return uUsed; - dst += uUsed; - uRead += uUsed; - uLeft -= uUsed; - read_sq.rear_size += bUsed; - if (read_sq.rear_size >= read_sq.block_size) { - read_sq.rear_size = 0; - read_sq.front++; - if (read_sq.front >= read_sq.max_active) - read_sq.front = 0; - } - } - return uRead; -} - -static int sq_open(struct inode *inode, struct file *file) -{ - int rc = 0; - - if (file->f_mode & FMODE_WRITE) { - if (sq.busy) { - rc = -EBUSY; - if (NON_BLOCKING(file->f_flags)) - goto err_out; - rc = -EINTR; - while (sq.busy) { - SLEEP(sq.open_queue); - if (SIGNAL_RECEIVED) - goto err_out; - } - } - sq.busy = 1; /* Let's play spot-the-race-condition */ - - if (sq_allocate_buffers()) goto err_out_nobusy; - - sq_setup(numBufs, bufSize<<10,sound_buffers); - sq.open_mode = file->f_mode; - } - - - if (file->f_mode & FMODE_READ) { - if (read_sq.busy) { - rc = -EBUSY; - if (NON_BLOCKING(file->f_flags)) - goto err_out; - rc = -EINTR; - while (read_sq.busy) { - SLEEP(read_sq.open_queue); - if (SIGNAL_RECEIVED) - goto err_out; - } - rc = 0; - } - read_sq.busy = 1; - if (sq_allocate_read_buffers()) goto err_out_nobusy; - - read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers); - read_sq.open_mode = file->f_mode; - } - - /* Start up the 4218 by: - * Reset. - * Enable, unreset. - */ - *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_RSTAUDIO; - eieio(); - *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_ENAUDIO; - mdelay(50); - *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; - - /* We need to send the current control word in case someone - * opened /dev/mixer and changed things while we were shut - * down. Chances are good the initialization that follows - * would have done this, but it is still possible it wouldn't. - */ - cs4218_ctl_write(cs4218_control); - - sound.minDev = iminor(inode) & 0x0f; - sound.soft = sound.dsp; - sound.hard = sound.dsp; - sound_init(); - if ((iminor(inode) & 0x0f) == SND_DEV_AUDIO) { - sound_set_speed(8000); - sound_set_stereo(0); - sound_set_format(AFMT_MU_LAW); - } - - return nonseekable_open(inode, file); - -err_out_nobusy: - if (file->f_mode & FMODE_WRITE) { - sq.busy = 0; - WAKE_UP(sq.open_queue); - } - if (file->f_mode & FMODE_READ) { - read_sq.busy = 0; - WAKE_UP(read_sq.open_queue); - } -err_out: - return rc; -} - - -static void sq_reset(void) -{ - sound_silence(); - sq.active = 0; - sq.count = 0; - sq.front = (sq.rear+1) % sq.max_count; -#if 0 - init_tdm_buffers(); -#endif -} - - -static int sq_fsync(struct file *filp, struct dentry *dentry) -{ - int rc = 0; - - sq.syncing = 1; - sq_play(); /* there may be an incomplete frame waiting */ - - while (sq.active) { - SLEEP(sq.sync_queue); - if (SIGNAL_RECEIVED) { - /* While waiting for audio output to drain, an - * interrupt occurred. Stop audio output immediately - * and clear the queue. */ - sq_reset(); - rc = -EINTR; - break; - } - } - - sq.syncing = 0; - return rc; -} - -static int sq_release(struct inode *inode, struct file *file) -{ - int rc = 0; - - if (sq.busy) - rc = sq_fsync(file, file->f_path.dentry); - sound.soft = sound.dsp; - sound.hard = sound.dsp; - sound_silence(); - - sq_release_read_buffers(); - sq_release_buffers(); - - if (file->f_mode & FMODE_READ) { - read_sq.busy = 0; - WAKE_UP(read_sq.open_queue); - } - - if (file->f_mode & FMODE_WRITE) { - sq.busy = 0; - WAKE_UP(sq.open_queue); - } - - /* Shut down the SMC. - */ - cpmp->cp_smc[1].smc_smcmr &= ~(SMCMR_TEN | SMCMR_REN); - - /* Shut down the codec. - */ - *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; - eieio(); - *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_ENAUDIO; - - /* Wake up a process waiting for the queue being released. - * Note: There may be several processes waiting for a call - * to open() returning. */ - - return rc; -} - - -static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) -{ - u_long fmt; - int data; -#if 0 - int size, nbufs; -#else - int size; -#endif - - switch (cmd) { - case SNDCTL_DSP_RESET: - sq_reset(); - return 0; - case SNDCTL_DSP_POST: - case SNDCTL_DSP_SYNC: - return sq_fsync(file, file->f_path.dentry); - - /* ++TeSche: before changing any of these it's - * probably wise to wait until sound playing has - * settled down. */ - case SNDCTL_DSP_SPEED: - sq_fsync(file, file->f_path.dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_speed(data)); - case SNDCTL_DSP_STEREO: - sq_fsync(file, file->f_path.dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_stereo(data)); - case SOUND_PCM_WRITE_CHANNELS: - sq_fsync(file, file->f_path.dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); - case SNDCTL_DSP_SETFMT: - sq_fsync(file, file->f_path.dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_format(data)); - case SNDCTL_DSP_GETFMTS: - fmt = 0; - if (sound.trans_write) { - if (sound.trans_write->ct_ulaw) - fmt |= AFMT_MU_LAW; - if (sound.trans_write->ct_alaw) - fmt |= AFMT_A_LAW; - if (sound.trans_write->ct_s8) - fmt |= AFMT_S8; - if (sound.trans_write->ct_u8) - fmt |= AFMT_U8; - if (sound.trans_write->ct_s16be) - fmt |= AFMT_S16_BE; - if (sound.trans_write->ct_u16be) - fmt |= AFMT_U16_BE; - if (sound.trans_write->ct_s16le) - fmt |= AFMT_S16_LE; - if (sound.trans_write->ct_u16le) - fmt |= AFMT_U16_LE; - } - return IOCTL_OUT(arg, fmt); - case SNDCTL_DSP_GETBLKSIZE: - size = sq.block_size - * sound.soft.size * (sound.soft.stereo + 1) - / (sound.hard.size * (sound.hard.stereo + 1)); - return IOCTL_OUT(arg, size); - case SNDCTL_DSP_SUBDIVIDE: - break; -#if 0 /* Sorry can't do this at the moment. The CPM allocated buffers - * long ago that can't be changed. - */ - case SNDCTL_DSP_SETFRAGMENT: - if (sq.count || sq.active || sq.syncing) - return -EINVAL; - IOCTL_IN(arg, size); - nbufs = size >> 16; - if (nbufs < 2 || nbufs > numBufs) - nbufs = numBufs; - size &= 0xffff; - if (size >= 8 && size <= 30) { - size = 1 << size; - size *= sound.hard.size * (sound.hard.stereo + 1); - size /= sound.soft.size * (sound.soft.stereo + 1); - if (size > (bufSize << 10)) - size = bufSize << 10; - } else - size = bufSize << 10; - sq_setup(numBufs, size, sound_buffers); - sq.max_active = nbufs; - return 0; -#endif - - default: - return mixer_ioctl(inode, file, cmd, arg); - } - return -EINVAL; -} - - - -static const struct file_operations sq_fops = -{ - .owner = THIS_MODULE, - .llseek = sound_lseek, - .read = sq_read, /* sq_read */ - .write = sq_write, - .ioctl = sq_ioctl, - .open = sq_open, - .release = sq_release, -}; - - -static void __init sq_init(void) -{ - sq_unit = register_sound_dsp(&sq_fops, -1); - if (sq_unit < 0) - return; - - init_waitqueue_head(&sq.action_queue); - init_waitqueue_head(&sq.open_queue); - init_waitqueue_head(&sq.sync_queue); - init_waitqueue_head(&read_sq.action_queue); - init_waitqueue_head(&read_sq.open_queue); - init_waitqueue_head(&read_sq.sync_queue); - - sq.busy = 0; - read_sq.busy = 0; - - /* whatever you like as startup mode for /dev/dsp, - * (/dev/audio hasn't got a startup mode). note that - * once changed a new open() will *not* restore these! - */ - sound.dsp.format = AFMT_S16_BE; - sound.dsp.stereo = 1; - sound.dsp.size = 16; - - /* set minimum rate possible without expanding */ - sound.dsp.speed = 8000; - - /* before the first open to /dev/dsp this wouldn't be set */ - sound.soft = sound.dsp; - sound.hard = sound.dsp; - - sound_silence(); -} - -/* - * /dev/sndstat - */ - - -/* state.buf should not overflow! */ - -static int state_open(struct inode *inode, struct file *file) -{ - char *buffer = state.buf, *mach = "", cs4218_buf[50]; - int len = 0; - - if (state.busy) - return -EBUSY; - - state.ptr = 0; - state.busy = 1; - - sprintf(cs4218_buf, "Crystal CS4218 on TDM, "); - mach = cs4218_buf; - - len += sprintf(buffer+len, "%sDMA sound driver:\n", mach); - - len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format); - switch (sound.soft.format) { - case AFMT_MU_LAW: - len += sprintf(buffer+len, " (mu-law)"); - break; - case AFMT_A_LAW: - len += sprintf(buffer+len, " (A-law)"); - break; - case AFMT_U8: - len += sprintf(buffer+len, " (unsigned 8 bit)"); - break; - case AFMT_S8: - len += sprintf(buffer+len, " (signed 8 bit)"); - break; - case AFMT_S16_BE: - len += sprintf(buffer+len, " (signed 16 bit big)"); - break; - case AFMT_U16_BE: - len += sprintf(buffer+len, " (unsigned 16 bit big)"); - break; - case AFMT_S16_LE: - len += sprintf(buffer+len, " (signed 16 bit little)"); - break; - case AFMT_U16_LE: - len += sprintf(buffer+len, " (unsigned 16 bit little)"); - break; - } - len += sprintf(buffer+len, "\n"); - len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", - sound.soft.speed, sound.hard.speed); - len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", - sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono"); - len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" - " sq.max_active = %d\n", - sq.block_size, sq.max_count, sq.max_active); - len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, - sq.rear_size); - len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", - sq.active, sq.syncing); - state.len = len; - return nonseekable_open(inode, file); -} - - -static int state_release(struct inode *inode, struct file *file) -{ - state.busy = 0; - return 0; -} - - -static ssize_t state_read(struct file *file, char *buf, size_t count, - loff_t *ppos) -{ - int n = state.len - state.ptr; - if (n > count) - n = count; - if (n <= 0) - return 0; - if (copy_to_user(buf, &state.buf[state.ptr], n)) - return -EFAULT; - state.ptr += n; - return n; -} - - -static const struct file_operations state_fops = -{ - .owner = THIS_MODULE, - .llseek = sound_lseek, - .read = state_read, - .open = state_open, - .release = state_release, -}; - - -static void __init state_init(void) -{ - state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); - if (state_unit < 0) - return; - state.busy = 0; -} - - -/*** Common stuff ********************************************************/ - -static long long sound_lseek(struct file *file, long long offset, int orig) -{ - return -ESPIPE; -} - - -/*** Config & Setup **********************************************************/ - - -int __init tdm8xx_sound_init(void) -{ - int i, has_sound; - uint dp_offset; - volatile uint *sirp; - volatile cbd_t *bdp; - volatile cpm8xx_t *cp; - volatile smc_t *sp; - volatile smc_uart_t *up; - volatile immap_t *immap; - - has_sound = 0; - - /* Program the SI/TSA to use TDMa, connected to SMC2, for 4 bytes. - */ - cp = cpmp; /* Get pointer to Communication Processor */ - immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ - - /* Set all TDMa control bits to zero. This enables most features - * we want. - */ - cp->cp_simode &= ~0x00000fff; - - /* Enable common receive/transmit clock pins, use IDL format. - * Sync on falling edge, transmit rising clock, receive falling - * clock, delay 1 bit on both Tx and Rx. Common Tx/Rx clocks and - * sync. - * Connect SMC2 to TSA. - */ - cp->cp_simode |= 0x80000141; - - /* Configure port A pins for TDMa operation. - * The RPX-Lite (MPC850/823) loses SMC2 when TDM is used. - */ - immap->im_ioport.iop_papar |= 0x01c0; /* Enable TDMa functions */ - immap->im_ioport.iop_padir |= 0x00c0; /* Enable TDMa Tx/Rx */ - immap->im_ioport.iop_padir &= ~0x0100; /* Enable L1RCLKa */ - - immap->im_ioport.iop_pcpar |= 0x0800; /* Enable L1RSYNCa */ - immap->im_ioport.iop_pcdir &= ~0x0800; - - /* Initialize the SI TDM routing table. We use TDMa only. - * The receive table and transmit table each have only one - * entry, to capture/send four bytes after each frame pulse. - * The 16-bit ram entry is 0000 0001 1000 1111. (SMC2) - */ - cp->cp_sigmr = 0; - sirp = (uint *)cp->cp_siram; - - *sirp = 0x018f0000; /* Receive entry */ - sirp += 64; - *sirp = 0x018f0000; /* Tramsmit entry */ - - /* Enable single TDMa routing. - */ - cp->cp_sigmr = 0x04; - - /* Initialize the SMC for transparent operation. - */ - sp = &cpmp->cp_smc[1]; - up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC2]; - - /* We need to allocate a transmit and receive buffer - * descriptors from dual port ram. - */ - dp_addr = cpm_dpalloc(sizeof(cbd_t) * numReadBufs, 8); - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; - up->smc_rbase = dp_offset; - rx_cur = rx_base = (cbd_t *)bdp; - - for (i=0; i<(numReadBufs-1); i++) { - bdp->cbd_bufaddr = 0; - bdp->cbd_datlen = 0; - bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; - bdp++; - } - bdp->cbd_bufaddr = 0; - bdp->cbd_datlen = 0; - bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; - - /* Now, do the same for the transmit buffers. - */ - dp_offset = cpm_dpalloc(sizeof(cbd_t) * numBufs, 8); - - bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; - up->smc_tbase = dp_offset; - tx_cur = tx_base = (cbd_t *)bdp; - - for (i=0; i<(numBufs-1); i++) { - bdp->cbd_bufaddr = 0; - bdp->cbd_datlen = 0; - bdp->cbd_sc = BD_SC_INTRPT; - bdp++; - } - bdp->cbd_bufaddr = 0; - bdp->cbd_datlen = 0; - bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); - - /* Set transparent SMC mode. - * A few things are specific to our application. The codec interface - * is MSB first, hence the REVD selection. The CD/CTS pulse are - * used by the TSA to indicate the frame start to the SMC. - */ - up->smc_rfcr = SCC_EB; - up->smc_tfcr = SCC_EB; - up->smc_mrblr = readbufSize * 1024; - - /* Set 16-bit reversed data, transparent mode. - */ - sp->smc_smcmr = smcr_mk_clen(15) | - SMCMR_SM_TRANS | SMCMR_REVD | SMCMR_BS; - - /* Enable and clear events. - * Because of FIFO delays, all we need is the receive interrupt - * and we can process both the current receive and current - * transmit interrupt within a few microseconds of the transmit. - */ - sp->smc_smce = 0xff; - sp->smc_smcm = SMCM_TXE | SMCM_TX | SMCM_RX; - - /* Send the CPM an initialize command. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, - CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - - sound.mach = mach_cs4218; - has_sound = 1; - - /* Initialize beep stuff */ - orig_mksound = kd_mksound; - kd_mksound = cs_mksound; - beep_buf = kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); - if (beep_buf == NULL) - printk(KERN_WARNING "dmasound: no memory for " - "beep buffer\n"); - - if (!has_sound) - return -ENODEV; - - /* Initialize the software SPI. - */ - sw_spi_init(); - - /* Set up sound queue, /dev/audio and /dev/dsp. */ - - /* Set default settings. */ - sq_init(); - - /* Set up /dev/sndstat. */ - state_init(); - - /* Set up /dev/mixer. */ - mixer_init(); - - if (!sound.mach.irqinit()) { - printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); - return -ENODEV; - } -#ifdef MODULE - irq_installed = 1; -#endif - - printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", - numBufs, bufSize); - - return 0; -} - -/* Due to FIFOs and bit delays, the transmit interrupt occurs a few - * microseconds ahead of the receive interrupt. - * When we get an interrupt, we service the transmit first, then - * check for a receive to prevent the overhead of returning through - * the interrupt handler only to get back here right away during - * full duplex operation. - */ -static void -cs4218_intr(void *dev_id) -{ - volatile smc_t *sp; - volatile cpm8xx_t *cp; - - sp = &cpmp->cp_smc[1]; - - if (sp->smc_smce & SCCM_TX) { - sp->smc_smce = SCCM_TX; - cs4218_tdm_tx_intr((void *)sp); - } - - if (sp->smc_smce & SCCM_RX) { - sp->smc_smce = SCCM_RX; - cs4218_tdm_rx_intr((void *)sp); - } - - if (sp->smc_smce & SCCM_TXE) { - /* Transmit underrun. This happens with the application - * didn't keep up sending buffers. We tell the SMC to - * restart, which will cause it to poll the current (next) - * BD. If the user supplied data since this occurred, - * we just start running again. If they didn't, the SMC - * will poll the descriptor until data is placed there. - */ - sp->smc_smce = SCCM_TXE; - cp = cpmp; /* Get pointer to Communication Processor */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, - CPM_CR_RESTART_TX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - } -} - - -#define MAXARGS 8 /* Should be sufficient for now */ - -void __init dmasound_setup(char *str, int *ints) -{ - /* check the bootstrap parameter for "dmasound=" */ - - switch (ints[0]) { - case 3: - if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) - printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius); - else - catchRadius = ints[3]; - /* fall through */ - case 2: - if (ints[1] < MIN_BUFFERS) - printk("dmasound_setup: invalid number of buffers, using default = %d\n", numBufs); - else - numBufs = ints[1]; - if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) - printk("dmasound_setup: invalid buffer size, using default = %d\n", bufSize); - else - bufSize = ints[2]; - break; - case 0: - break; - default: - printk("dmasound_setup: invalid number of arguments\n"); - } -} - -/* Software SPI functions. - * These are on Port B. - */ -#define PB_SPICLK ((uint)0x00000002) -#define PB_SPIMOSI ((uint)0x00000004) -#define PB_SPIMISO ((uint)0x00000008) - -static -void sw_spi_init(void) -{ - volatile cpm8xx_t *cp; - volatile uint *hcsr4; - - hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; - cp = cpmp; /* Get pointer to Communication Processor */ - - *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ - - /* Make these Port B signals general purpose I/O. - * First, make sure the clock is low. - */ - cp->cp_pbdat &= ~PB_SPICLK; - cp->cp_pbpar &= ~(PB_SPICLK | PB_SPIMOSI | PB_SPIMISO); - - /* Clock and Master Output are outputs. - */ - cp->cp_pbdir |= (PB_SPICLK | PB_SPIMOSI); - - /* Master Input. - */ - cp->cp_pbdir &= ~PB_SPIMISO; - -} - -/* Write the CS4218 control word out the SPI port. While the - * the control word is going out, the status word is arriving. - */ -static -uint cs4218_ctl_write(uint ctlreg) -{ - uint status; - - sw_spi_io((u_char *)&ctlreg, (u_char *)&status, 4); - - /* Shadow the control register.....I guess we could do - * the same for the status, but for now we just return it - * and let the caller decide. - */ - cs4218_control = ctlreg; - return status; -} - -static -void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt) -{ - int bits, i; - u_char outbyte, inbyte; - volatile cpm8xx_t *cp; - volatile uint *hcsr4; - - hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; - cp = cpmp; /* Get pointer to Communication Processor */ - - /* The timing on the bus is pretty slow. Code inefficiency - * and eieio() is our friend here :-). - */ - cp->cp_pbdat &= ~PB_SPICLK; - *hcsr4 |= HIOX_CSR4_AUDSPISEL; /* Enable SPI select */ - eieio(); - - /* Clock in/out the bytes. Data is valid on the falling edge - * of the clock. Data is MSB first. - */ - for (i=0; icp_pbdat |= PB_SPICLK; - eieio(); - if (outbyte & 0x80) - cp->cp_pbdat |= PB_SPIMOSI; - else - cp->cp_pbdat &= ~PB_SPIMOSI; - eieio(); - cp->cp_pbdat &= ~PB_SPICLK; - eieio(); - outbyte <<= 1; - inbyte <<= 1; - if (cp->cp_pbdat & PB_SPIMISO) - inbyte |= 1; - } - *ibuf++ = inbyte; - } - - *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ - eieio(); -} - -void cleanup_module(void) -{ - if (irq_installed) { - sound_silence(); -#ifdef MODULE - sound.mach.irqcleanup(); -#endif - } - - sq_release_read_buffers(); - sq_release_buffers(); - - if (mixer_unit >= 0) - unregister_sound_mixer(mixer_unit); - if (state_unit >= 0) - unregister_sound_special(state_unit); - if (sq_unit >= 0) - unregister_sound_dsp(sq_unit); -} - -module_init(tdm8xx_sound_init); -module_exit(cleanup_module); - diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c index b23c45b..bfa3f52 100644 --- a/arch/ppc/8xx_io/enet.c +++ b/arch/ppc/8xx_io/enet.c @@ -506,7 +506,6 @@ #endif cep->stats.rx_dropped++; } else { - skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, cep->rx_vaddr[bdp - cep->rx_bd_base], diff --git a/arch/ppc/8xx_io/fec.c b/arch/ppc/8xx_io/fec.c index e6c28fb..57a9a61 100644 --- a/arch/ppc/8xx_io/fec.c +++ b/arch/ppc/8xx_io/fec.c @@ -724,7 +724,6 @@ #endif printk("%s: Memory squeeze, dropping packet.\n", dev->name); fep->stats.rx_dropped++; } else { - skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, data, pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); diff --git a/arch/ppc/boot/common/misc-common.c b/arch/ppc/boot/common/misc-common.c index 8e1fccd..9589969 100644 --- a/arch/ppc/boot/common/misc-common.c +++ b/arch/ppc/boot/common/misc-common.c @@ -57,7 +57,8 @@ unsigned char *ISA_io = NULL; #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_UARTLITE_CONSOLE) extern unsigned long com_port; extern int serial_tstc(unsigned long com_port); @@ -80,7 +81,8 @@ int tstc(void) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_UARTLITE_CONSOLE) if(keyb_present) return (CRT_tstc() || serial_tstc(com_port)); else @@ -95,7 +97,8 @@ int getc(void) while (1) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_UARTLITE_CONSOLE) if (serial_tstc(com_port)) return (serial_getc(com_port)); #endif /* serial console */ @@ -112,7 +115,8 @@ putc(const char c) #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_UARTLITE_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); @@ -161,7 +165,8 @@ void puts(const char *s) while ( ( c = *s++ ) != '\0' ) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_UARTLITE_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); #endif /* serial console */ diff --git a/arch/ppc/boot/simple/Makefile b/arch/ppc/boot/simple/Makefile index bcfb6cd..5b87779 100644 --- a/arch/ppc/boot/simple/Makefile +++ b/arch/ppc/boot/simple/Makefile @@ -201,6 +201,7 @@ boot-$(CONFIG_8260) += m8260_tty.o endif boot-$(CONFIG_SERIAL_MPC52xx_CONSOLE) += mpc52xx_tty.o boot-$(CONFIG_SERIAL_MPSC_CONSOLE) += mv64x60_tty.o +boot-$(CONFIG_SERIAL_UARTLITE_CONSOLE) += uartlite_tty.o LIBS := $(common)/lib.a $(bootlib)/lib.a ifeq ($(CONFIG_PPC_PREP),y) diff --git a/arch/ppc/boot/simple/uartlite_tty.c b/arch/ppc/boot/simple/uartlite_tty.c new file mode 100644 index 0000000..0eae1ea --- /dev/null +++ b/arch/ppc/boot/simple/uartlite_tty.c @@ -0,0 +1,37 @@ +/* + * Xilinx UARTLITE bootloader driver + * + * Copyright (c) 2007 Secret Lab Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#define UARTLITE_BASEADDR ((void*)(XPAR_UARTLITE_0_BASEADDR)) + +void +serial_putc(unsigned long com_port, unsigned char c) +{ + while ((in_be32(UARTLITE_BASEADDR + 0x8) & 0x08) != 0); /* spin */ + out_be32(UARTLITE_BASEADDR + 0x4, c); +} + +unsigned char +serial_getc(unsigned long com_port) +{ + while ((in_be32(UARTLITE_BASEADDR + 0x8) & 0x01) == 0); /* spin */ + return in_be32(UARTLITE_BASEADDR); +} + +int +serial_tstc(unsigned long com_port) +{ + return ((in_be32(UARTLITE_BASEADDR + 0x8) & 0x01) != 0); +} diff --git a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c index 1f91eca..c5850a2 100644 --- a/arch/ppc/kernel/asm-offsets.c +++ b/arch/ppc/kernel/asm-offsets.c @@ -40,7 +40,6 @@ main(void) DEFINE(PTRACE, offsetof(struct task_struct, ptrace)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); DEFINE(PGDIR, offsetof(struct thread_struct, pgdir)); - DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode)); DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index a9d4553..ab64256 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -191,7 +191,6 @@ stack_ovf: 0: _GLOBAL(DoSyscall) - stw r0,THREAD+LAST_SYSCALL(r2) stw r3,ORIG_GPR3(r1) li r12,0 stw r12,RESULT(r1) diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 1318b6f..4ad4996 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -93,8 +93,6 @@ EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(strcat); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strcasecmp); -EXPORT_SYMBOL(strncasecmp); EXPORT_SYMBOL(__div64_32); EXPORT_SYMBOL(csum_partial); diff --git a/arch/ppc/lib/Makefile b/arch/ppc/lib/Makefile index 50358e4..422bef9 100644 --- a/arch/ppc/lib/Makefile +++ b/arch/ppc/lib/Makefile @@ -2,7 +2,7 @@ # # Makefile for ppc-specific library files.. # -obj-y := checksum.o string.o strcase.o div64.o +obj-y := checksum.o string.o div64.o obj-$(CONFIG_8xx) += rheap.o obj-$(CONFIG_CPM2) += rheap.o diff --git a/arch/ppc/lib/strcase.c b/arch/ppc/lib/strcase.c deleted file mode 100644 index 3b0094c..0000000 --- a/arch/ppc/lib/strcase.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include - -int strcasecmp(const char *s1, const char *s2) -{ - int c1, c2; - - do { - c1 = tolower(*s1++); - c2 = tolower(*s2++); - } while (c1 == c2 && c1 != 0); - return c1 - c2; -} - -int strncasecmp(const char *s1, const char *s2, size_t n) -{ - int c1, c2; - - do { - c1 = tolower(*s1++); - c2 = tolower(*s2++); - } while ((--n > 0) && c1 == c2 && c1 != 0); - return c1 - c2; -} diff --git a/arch/ppc/platforms/4xx/Kconfig b/arch/ppc/platforms/4xx/Kconfig index 705ae56..76551b6 100644 --- a/arch/ppc/platforms/4xx/Kconfig +++ b/arch/ppc/platforms/4xx/Kconfig @@ -29,6 +29,7 @@ config CPCI405 config EP405 bool "EP405/EP405PC" + select EMBEDDEDBOOT help This option enables support for the EP405/EP405PC boards. @@ -54,11 +55,15 @@ config WALNUT config XILINX_ML300 bool "Xilinx-ML300" + select XILINX_VIRTEX_II_PRO + select EMBEDDEDBOOT help This option enables support for the Xilinx ML300 evaluation board. config XILINX_ML403 bool "Xilinx-ML403" + select XILINX_VIRTEX_4_FX + select EMBEDDEDBOOT help This option enables support for the Xilinx ML403 evaluation board. endchoice @@ -215,18 +220,14 @@ config 405GPR config XILINX_VIRTEX_II_PRO bool - depends on XILINX_ML300 - default y + select XILINX_VIRTEX config XILINX_VIRTEX_4_FX bool - depends on XILINX_ML403 - default y + select XILINX_VIRTEX config XILINX_VIRTEX bool - depends on XILINX_VIRTEX_II_PRO || XILINX_VIRTEX_4_FX - default y config STB03xxx bool @@ -235,8 +236,6 @@ config STB03xxx config EMBEDDEDBOOT bool - depends on EP405 || XILINX_ML300 || XILINX_ML403 - default y config IBM_OPENBIOS bool diff --git a/arch/ppc/platforms/4xx/Makefile b/arch/ppc/platforms/4xx/Makefile index fa6610b..723ad79 100644 --- a/arch/ppc/platforms/4xx/Makefile +++ b/arch/ppc/platforms/4xx/Makefile @@ -28,5 +28,4 @@ obj-$(CONFIG_440SP) += ibm440sp.o obj-$(CONFIG_440SPE) += ppc440spe.o obj-$(CONFIG_405EP) += ibm405ep.o obj-$(CONFIG_405GPR) += ibm405gpr.o -obj-$(CONFIG_XILINX_VIRTEX) += virtex.o diff --git a/arch/ppc/platforms/4xx/ocotea.c b/arch/ppc/platforms/4xx/ocotea.c index 84e999d..5e994e1 100644 --- a/arch/ppc/platforms/4xx/ocotea.c +++ b/arch/ppc/platforms/4xx/ocotea.c @@ -178,7 +178,7 @@ ocotea_setup_pcix(void) /* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */ PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH); PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL); - PCIX_WRITEL(0xe0000007, PCIX0_PIM0SA); + PCIX_WRITEL(0x80000007, PCIX0_PIM0SA); eieio(); } @@ -289,7 +289,7 @@ ocotea_setup_arch(void) * from FPGA, because it can be changed by on-board switches * --ebs */ - ibm440gx_get_clocks(&clocks, 33333333, 6 * 1843200); + ibm440gx_get_clocks(&clocks, 33300000, 6 * 1843200); ocp_sys_info.opb_bus_freq = clocks.opb; /* Setup TODC access */ diff --git a/arch/ppc/platforms/4xx/taishan.c b/arch/ppc/platforms/4xx/taishan.c index bb0253e..5d9af8d 100644 --- a/arch/ppc/platforms/4xx/taishan.c +++ b/arch/ppc/platforms/4xx/taishan.c @@ -235,7 +235,7 @@ taishan_setup_pcix(void) /* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */ PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH); PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL); - PCIX_WRITEL(0xe0000007, PCIX0_PIM0SA); + PCIX_WRITEL(0x80000007, PCIX0_PIM0SA); PCIX_WRITEL(0xffffffff, PCIX0_PIM0SAH); iounmap(pcix_reg_base); diff --git a/arch/ppc/platforms/4xx/virtex.c b/arch/ppc/platforms/4xx/virtex.c deleted file mode 100644 index 133a831..0000000 --- a/arch/ppc/platforms/4xx/virtex.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Virtex-II Pro & Virtex-4 FX common infrastructure - * - * Maintainer: Grant Likely - * - * Copyright 2005 Secret Lab Technologies Ltd. - * Copyright 2005 General Dynamics Canada Ltd. - * Copyright 2005 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define XPAR_UART(num) { \ - .mapbase = XPAR_UARTNS550_##num##_BASEADDR + 3, \ - .irq = XPAR_INTC_0_UARTNS550_##num##_VEC_ID, \ - .iotype = UPIO_MEM, \ - .uartclk = XPAR_UARTNS550_##num##_CLOCK_FREQ_HZ, \ - .flags = UPF_BOOT_AUTOCONF, \ - .regshift = 2, \ - } - -struct plat_serial8250_port serial_platform_data[] = { -#ifdef XPAR_UARTNS550_0_BASEADDR - XPAR_UART(0), -#endif -#ifdef XPAR_UARTNS550_1_BASEADDR - XPAR_UART(1), -#endif -#ifdef XPAR_UARTNS550_2_BASEADDR - XPAR_UART(2), -#endif -#ifdef XPAR_UARTNS550_3_BASEADDR - XPAR_UART(3), -#endif - { }, /* terminated by empty record */ -}; - -struct platform_device ppc_sys_platform_devices[] = { - [VIRTEX_UART] = { - .name = "serial8250", - .id = 0, - .dev.platform_data = serial_platform_data, - }, -}; - diff --git a/arch/ppc/platforms/4xx/virtex.h b/arch/ppc/platforms/4xx/virtex.h index c14325d..7382804 100644 --- a/arch/ppc/platforms/4xx/virtex.h +++ b/arch/ppc/platforms/4xx/virtex.h @@ -1,35 +1,35 @@ /* - * arch/ppc/platforms/4xx/virtex.h + * Basic Virtex platform defines, included by * - * Include file that defines the Xilinx Virtex-II Pro processor + * 2005-2007 (c) Secret Lab Technologies Ltd. + * 2002-2004 (c) MontaVista Software, Inc. * - * Author: MontaVista Software, Inc. - * source@mvista.com - * - * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is licensed - * "as is" without any warranty of any kind, whether express or implied. + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. */ #ifdef __KERNEL__ #ifndef __ASM_VIRTEX_H__ #define __ASM_VIRTEX_H__ -/* serial defines */ - #include +#include /* Ugly, ugly, ugly! BASE_BAUD defined here to keep 8250.c happy. */ #if !defined(BASE_BAUD) #define BASE_BAUD (0) /* dummy value; not used */ #endif - -/* Device type enumeration for platform bus definitions */ + #ifndef __ASSEMBLY__ -enum ppc_sys_devices { - VIRTEX_UART, NUM_PPC_SYS_DEVS, -}; -#endif - +extern const char* virtex_machine_name; +#define PPC4xx_MACHINE_NAME (virtex_machine_name) +#endif /* !__ASSEMBLY__ */ + +/* We don't need anything mapped. Size of zero will accomplish that. */ +#define PPC4xx_ONB_IO_PADDR 0u +#define PPC4xx_ONB_IO_VADDR 0u +#define PPC4xx_ONB_IO_SIZE 0u + #endif /* __ASM_VIRTEX_H__ */ #endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.c b/arch/ppc/platforms/4xx/xilinx_ml300.c index fb5f0b5..6e522fe 100644 --- a/arch/ppc/platforms/4xx/xilinx_ml300.c +++ b/arch/ppc/platforms/4xx/xilinx_ml300.c @@ -18,9 +18,9 @@ #include #include #include #include -#include #include +#include #include /* @@ -53,24 +53,9 @@ #include mapbase; - serial_req.membase = pdata->membase; - serial_req.irq = pdata->irq; - serial_req.uartclk = pdata->uartclk; - serial_req.regshift = pdata->regshift; - serial_req.iotype = pdata->iotype; - serial_req.flags = pdata->flags; - gen550_init(num, &serial_req); -#endif -} - -void __init -ml300_early_serial_map(void) -{ -#ifdef CONFIG_SERIAL_8250 - struct plat_serial8250_port *pdata; - int i = 0; - - pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(VIRTEX_UART); - while(pdata && pdata->flags) - { - pdata->membase = ioremap(pdata->mapbase, 0x100); - ml300_early_serial_init(i, pdata); - pdata++; - i++; - } -#endif /* CONFIG_SERIAL_8250 */ -} - void __init ml300_setup_arch(void) { - ml300_early_serial_map(); + virtex_early_serial_map(); ppc4xx_setup_arch(); /* calls ppc4xx_find_bridges() */ /* Identify the system */ - printk(KERN_INFO "Xilinx Virtex-II Pro port\n"); - printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); + printk(KERN_INFO "Xilinx ML300 Reference System (Virtex-II Pro)\n"); } /* Called after board_setup_irq from ppc4xx_init_IRQ(). */ @@ -156,8 +103,6 @@ platform_init(unsigned long r3, unsigned { ppc4xx_init(r3, r4, r5, r6, r7); - identify_ppc_sys_by_id(mfspr(SPRN_PVR)); - ppc_md.setup_arch = ml300_setup_arch; ppc_md.setup_io_mappings = ml300_map_io; ppc_md.init_IRQ = ml300_init_irq; @@ -167,7 +112,7 @@ #if defined(XPAR_POWER_0_POWERDOWN_BASEA #endif #ifdef CONFIG_KGDB - ppc_md.early_serial_map = ml300_early_serial_map; + ppc_md.early_serial_map = virtex_early_serial_map; #endif } diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.h b/arch/ppc/platforms/4xx/xilinx_ml300.h deleted file mode 100644 index 3d57332..0000000 --- a/arch/ppc/platforms/4xx/xilinx_ml300.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Include file that defines the Xilinx ML300 evaluation board - * - * Author: MontaVista Software, Inc. - * source@mvista.com - * - * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is licensed - * "as is" without any warranty of any kind, whether express or implied. - */ - -#ifdef __KERNEL__ -#ifndef __ASM_XILINX_ML300_H__ -#define __ASM_XILINX_ML300_H__ - -/* ML300 has a Xilinx Virtex-II Pro processor */ -#include - -#ifndef __ASSEMBLY__ - -#include - -typedef struct board_info { - unsigned int bi_memsize; /* DRAM installed, in bytes */ - unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ - unsigned int bi_intfreq; /* Processor speed, in Hz */ - unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ - unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ -} bd_t; - -/* Some 4xx parts use a different timebase frequency from the internal clock. -*/ -#define bi_tbfreq bi_intfreq - -#endif /* !__ASSEMBLY__ */ - -/* We don't need anything mapped. Size of zero will accomplish that. */ -#define PPC4xx_ONB_IO_PADDR 0u -#define PPC4xx_ONB_IO_VADDR 0u -#define PPC4xx_ONB_IO_SIZE 0u - -#define PPC4xx_MACHINE_NAME "Xilinx ML300 Reference System" - -#endif /* __ASM_XILINX_ML300_H__ */ -#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/xilinx_ml403.c b/arch/ppc/platforms/4xx/xilinx_ml403.c index cb3bf7a..bc3ace3 100644 --- a/arch/ppc/platforms/4xx/xilinx_ml403.c +++ b/arch/ppc/platforms/4xx/xilinx_ml403.c @@ -1,11 +1,9 @@ /* - * arch/ppc/platforms/4xx/xilinx_ml403.c - * * Xilinx ML403 evaluation board initialization * * Author: Grant Likely * - * 2005 (c) Secret Lab Technologies Ltd. + * 2005-2007 (c) Secret Lab Technologies Ltd. * 2002-2004 (c) MontaVista Software, Inc. * * This file is licensed under the terms of the GNU General Public License @@ -22,9 +20,9 @@ #include #include #include #include -#include #include +#include #include /* @@ -57,24 +55,9 @@ #include mapbase; - serial_req.membase = pdata->membase; - serial_req.irq = pdata->irq; - serial_req.uartclk = pdata->uartclk; - serial_req.regshift = pdata->regshift; - serial_req.iotype = pdata->iotype; - serial_req.flags = pdata->flags; - gen550_init(num, &serial_req); -#endif -} - -void __init -ml403_early_serial_map(void) -{ -#ifdef CONFIG_SERIAL_8250 - struct plat_serial8250_port *pdata; - int i = 0; - - pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(VIRTEX_UART); - while(pdata && pdata->flags) - { - pdata->membase = ioremap(pdata->mapbase, 0x100); - ml403_early_serial_init(i, pdata); - pdata++; - i++; - } -#endif /* CONFIG_SERIAL_8250 */ -} - void __init ml403_setup_arch(void) { - ml403_early_serial_map(); + virtex_early_serial_map(); ppc4xx_setup_arch(); /* calls ppc4xx_find_bridges() */ /* Identify the system */ @@ -159,8 +105,6 @@ platform_init(unsigned long r3, unsigned { ppc4xx_init(r3, r4, r5, r6, r7); - identify_ppc_sys_by_id(mfspr(SPRN_PVR)); - ppc_md.setup_arch = ml403_setup_arch; ppc_md.setup_io_mappings = ml403_map_io; ppc_md.init_IRQ = ml403_init_irq; @@ -170,7 +114,7 @@ #if defined(XPAR_POWER_0_POWERDOWN_BASEA #endif #ifdef CONFIG_KGDB - ppc_md.early_serial_map = ml403_early_serial_map; + ppc_md.early_serial_map = virtex_early_serial_map; #endif } diff --git a/arch/ppc/platforms/4xx/xilinx_ml403.h b/arch/ppc/platforms/4xx/xilinx_ml403.h deleted file mode 100644 index 4735969..0000000 --- a/arch/ppc/platforms/4xx/xilinx_ml403.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * arch/ppc/platforms/4xx/xilinx_ml403.h - * - * Include file that defines the Xilinx ML403 reference design - * - * Author: Grant Likely - * - * 2005 (c) Secret Lab Technologies Ltd. - * 2002-2004 (c) MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifdef __KERNEL__ -#ifndef __ASM_XILINX_ML403_H__ -#define __ASM_XILINX_ML403_H__ - -/* ML403 has a Xilinx Virtex-4 FPGA with a PPC405 hard core */ -#include - -#ifndef __ASSEMBLY__ - -#include - -typedef struct board_info { - unsigned int bi_memsize; /* DRAM installed, in bytes */ - unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ - unsigned int bi_intfreq; /* Processor speed, in Hz */ - unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ - unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ -} bd_t; - -/* Some 4xx parts use a different timebase frequency from the internal clock. -*/ -#define bi_tbfreq bi_intfreq - -#endif /* !__ASSEMBLY__ */ - -/* We don't need anything mapped. Size of zero will accomplish that. */ -#define PPC4xx_ONB_IO_PADDR 0u -#define PPC4xx_ONB_IO_VADDR 0u -#define PPC4xx_ONB_IO_SIZE 0u - -#define PPC4xx_MACHINE_NAME "Xilinx ML403 Reference Design" - -#endif /* __ASM_XILINX_ML403_H__ */ -#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/xparameters/xparameters.h b/arch/ppc/platforms/4xx/xparameters/xparameters.h index 66ec5f3..01aa043 100644 --- a/arch/ppc/platforms/4xx/xparameters/xparameters.h +++ b/arch/ppc/platforms/4xx/xparameters/xparameters.h @@ -34,3 +34,63 @@ #ifndef SERIAL_PORT_DFNS .io_type = SERIAL_IO_MEM, \ }, #endif + +/* + * A few reasonable defaults for the #defines which could be missing depending + * on the IP version or variant (e.g. OPB vs PLB) + */ + +#ifndef XPAR_EMAC_0_CAM_EXIST +#define XPAR_EMAC_0_CAM_EXIST 0 +#endif +#ifndef XPAR_EMAC_0_JUMBO_EXIST +#define XPAR_EMAC_0_JUMBO_EXIST 0 +#endif +#ifndef XPAR_EMAC_0_TX_DRE_TYPE +#define XPAR_EMAC_0_TX_DRE_TYPE 0 +#endif +#ifndef XPAR_EMAC_0_RX_DRE_TYPE +#define XPAR_EMAC_0_RX_DRE_TYPE 0 +#endif +#ifndef XPAR_EMAC_0_TX_INCLUDE_CSUM +#define XPAR_EMAC_0_TX_INCLUDE_CSUM 0 +#endif +#ifndef XPAR_EMAC_0_RX_INCLUDE_CSUM +#define XPAR_EMAC_0_RX_INCLUDE_CSUM 0 +#endif + +#ifndef XPAR_EMAC_1_CAM_EXIST +#define XPAR_EMAC_1_CAM_EXIST 0 +#endif +#ifndef XPAR_EMAC_1_JUMBO_EXIST +#define XPAR_EMAC_1_JUMBO_EXIST 0 +#endif +#ifndef XPAR_EMAC_1_TX_DRE_TYPE +#define XPAR_EMAC_1_TX_DRE_TYPE 0 +#endif +#ifndef XPAR_EMAC_1_RX_DRE_TYPE +#define XPAR_EMAC_1_RX_DRE_TYPE 0 +#endif +#ifndef XPAR_EMAC_1_TX_INCLUDE_CSUM +#define XPAR_EMAC_1_TX_INCLUDE_CSUM 0 +#endif +#ifndef XPAR_EMAC_1_RX_INCLUDE_CSUM +#define XPAR_EMAC_1_RX_INCLUDE_CSUM 0 +#endif + +#ifndef XPAR_GPIO_0_IS_DUAL +#define XPAR_GPIO_0_IS_DUAL 0 +#endif +#ifndef XPAR_GPIO_1_IS_DUAL +#define XPAR_GPIO_1_IS_DUAL 0 +#endif +#ifndef XPAR_GPIO_2_IS_DUAL +#define XPAR_GPIO_2_IS_DUAL 0 +#endif +#ifndef XPAR_GPIO_3_IS_DUAL +#define XPAR_GPIO_3_IS_DUAL 0 +#endif +#ifndef XPAR_GPIO_4_IS_DUAL +#define XPAR_GPIO_4_IS_DUAL 0 +#endif + diff --git a/arch/ppc/platforms/rpxclassic.h b/arch/ppc/platforms/rpxclassic.h index 57a2a55..a3c1118 100644 --- a/arch/ppc/platforms/rpxclassic.h +++ b/arch/ppc/platforms/rpxclassic.h @@ -69,10 +69,6 @@ #define BCSR2_EN232XCVR ((uint)0x000080 #define BCSR2_QSPACESEL ((uint)0x00004000) #define BCSR2_FETHLEDMODE ((uint)0x00000800) /* CLLF */ -#if defined(CONFIG_HTDMSOUND) -#include -#endif - /* define IO_BASE for pcmcia, CLLF only */ #if !defined(CONFIG_PCI) #define _IO_BASE 0x80000000 diff --git a/arch/ppc/platforms/rpxhiox.h b/arch/ppc/platforms/rpxhiox.h deleted file mode 100644 index c3fa5a6..0000000 --- a/arch/ppc/platforms/rpxhiox.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * The Embedded Planet HIOX expansion card definitions. - * There were a few different versions of these cards, but only - * the one that escaped real production is defined here. - * - * Copyright (c) 2000 Dan Malek (dmalek@jlc.net) - */ -#ifndef __MACH_RPX_HIOX_DEFS -#define __MACH_RPX_HIOX_DEFS - -#define HIOX_CSR_ADDR ((uint)0xfac00000) -#define HIOX_CSR_SIZE ((uint)(4 * 1024)) -#define HIOX_CSR0_ADDR HIOX_CSR_ADDR -#define HIOX_CSR4_ADDR ((uint)0xfac00004) - -#define HIOX_CSR0_DEFAULT ((uint)0x380f3c00) -#define HIOX_CSR0_ENSCC2 ((uint)0x80000000) -#define HIOX_CSR0_ENSMC2 ((uint)0x04000000) -#define HIOX_CSR0_ENVDOCLK ((uint)0x02000000) -#define HIOX_CSR0_VDORST_HL ((uint)0x01000000) -#define HIOX_CSR0_RS232SEL ((uint)0x0000c000) -#define HIOX_CSR0_SCC3SEL ((uint)0x0000c000) -#define HIOX_CSR0_SMC1SEL ((uint)0x00008000) -#define HIOX_CSR0_SCC1SEL ((uint)0x00004000) -#define HIOX_CSR0_ENTOUCH ((uint)0x00000080) -#define HIOX_CSR0_PDOWN100 ((uint)0x00000060) -#define HIOX_CSR0_PDOWN10 ((uint)0x00000040) -#define HIOX_CSR0_PDOWN1 ((uint)0x00000020) -#define HIOX_CSR0_TSELSPI ((uint)0x00000010) -#define HIOX_CSR0_TIRQSTAT ((uint)0x00000008) -#define HIOX_CSR4_DEFAULT ((uint)0x00000000) -#define HIOX_CSR4_ENTIRQ2 ((uint)0x20000000) -#define HIOX_CSR4_ENTIRQ3 ((uint)0x10000000) -#define HIOX_CSR4_ENAUDIO ((uint)0x00000080) -#define HIOX_CSR4_RSTAUDIO ((uint)0x00000040) /* 0 == reset */ -#define HIOX_CSR4_AUDCLKHI ((uint)0x00000020) -#define HIOX_CSR4_AUDSPISEL ((uint)0x00000010) -#define HIOX_CSR4_AUDIRQSTAT ((uint)0x00000008) -#define HIOX_CSR4_AUDCLKSEL ((uint)0x00000007) - -#endif diff --git a/arch/ppc/platforms/rpxlite.h b/arch/ppc/platforms/rpxlite.h index 7197806..b615501 100644 --- a/arch/ppc/platforms/rpxlite.h +++ b/arch/ppc/platforms/rpxlite.h @@ -57,10 +57,6 @@ #define BCSR1_PCVCTL5 ((uint)0x #define BCSR1_PCVCTL6 ((uint)0x00020000) #define BCSR1_PCVCTL7 ((uint)0x00010000) -#if defined(CONFIG_HTDMSOUND) -#include -#endif - /* define IO_BASE for pcmcia */ #define _IO_BASE 0x80000000 #define _IO_BASE_SIZE 0x1000 diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 0991111..9569415 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -18,7 +18,8 @@ obj-$(CONFIG_440SP) += ibm440gx_common. obj-$(CONFIG_440SPE) += ibm440gx_common.o ibm440sp_common.o ppc440spe_pcie.o ifeq ($(CONFIG_4xx),y) ifeq ($(CONFIG_XILINX_VIRTEX),y) -obj-$(CONFIG_40x) += xilinx_pic.o ppc_sys.o +obj-$(CONFIG_40x) += xilinx_pic.o +obj-y += virtex_devices.o else ifeq ($(CONFIG_403),y) obj-$(CONFIG_40x) += ppc403_pic.o diff --git a/arch/ppc/syslib/cpc710.h b/arch/ppc/syslib/cpc710.h deleted file mode 100644 index 5299bf8..0000000 --- a/arch/ppc/syslib/cpc710.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Definitions for the IBM CPC710 PCI Host Bridge - * - * Author: Matt Porter - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifndef __PPC_PLATFORMS_CPC710_H -#define __PPC_PLATFORMS_CPC710_H - -/* General bridge and memory controller registers */ -#define PIDR 0xff000008 -#define CNFR 0xff00000c -#define RSTR 0xff000010 -#define UCTL 0xff001000 -#define MPSR 0xff001010 -#define SIOC 0xff001020 -#define ABCNTL 0xff001030 -#define SRST 0xff001040 -#define ERRC 0xff001050 -#define SESR 0xff001060 -#define SEAR 0xff001070 -#define SIOC1 0xff001090 -#define PGCHP 0xff001100 -#define GPDIR 0xff001130 -#define GPOUT 0xff001150 -#define ATAS 0xff001160 -#define AVDG 0xff001170 -#define MCCR 0xff001200 -#define MESR 0xff001220 -#define MEAR 0xff001230 -#define MCER0 0xff001300 -#define MCER1 0xff001310 -#define MCER2 0xff001320 -#define MCER3 0xff001330 -#define MCER4 0xff001340 -#define MCER5 0xff001350 -#define MCER6 0xff001360 -#define MCER7 0xff001370 - -/* - * PCI32/64 configuration registers - * Given as offsets from their - * respective physical segment BAR - */ -#define PIBAR 0x000f7800 -#define PMBAR 0x000f7810 -#define MSIZE 0x000f7f40 -#define IOSIZE 0x000f7f60 -#define SMBAR 0x000f7f80 -#define SIBAR 0x000f7fc0 -#define PSSIZE 0x000f8100 -#define PPSIZE 0x000f8110 -#define BARPS 0x000f8120 -#define BARPP 0x000f8130 -#define PSBAR 0x000f8140 -#define PPBAR 0x000f8150 -#define BPMDLK 0x000f8200 /* Bottom of Peripheral Memory Space */ -#define TPMDLK 0x000f8210 /* Top of Peripheral Memory Space */ -#define BIODLK 0x000f8220 /* Bottom of Peripheral I/O Space */ -#define TIODLK 0x000f8230 /* Top of Perioheral I/O Space */ -#define DLKCTRL 0x000f8240 /* Deadlock control */ -#define DLKDEV 0x000f8250 /* Deadlock device */ - -/* System standard configuration registers space */ -#define DCR 0xff200000 -#define DID 0xff200004 -#define BAR 0xff200018 - -/* Device specific configuration space */ -#define PCIENB 0xff201000 - -/* Configuration space registers */ -#define CPC710_BUS_NUMBER 0x40 -#define CPC710_SUB_BUS_NUMBER 0x41 - -#endif /* __PPC_PLATFORMS_CPC710_H */ diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c index 01e48d8..9caf850 100644 --- a/arch/ppc/syslib/m8xx_setup.c +++ b/arch/ppc/syslib/m8xx_setup.c @@ -413,7 +413,7 @@ #if !defined(CONFIG_PCI) io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO); #endif #endif -#if defined(CONFIG_HTDMSOUND) || defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX) +#if defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX) io_block_mapping(HIOX_CSR_ADDR, HIOX_CSR_ADDR, HIOX_CSR_SIZE, _PAGE_IO); #endif #ifdef CONFIG_FADS diff --git a/arch/ppc/syslib/ppc4xx_sgdma.c b/arch/ppc/syslib/ppc4xx_sgdma.c index 2f83e16..939abe3 100644 --- a/arch/ppc/syslib/ppc4xx_sgdma.c +++ b/arch/ppc/syslib/ppc4xx_sgdma.c @@ -27,6 +27,7 @@ #include #include #include +#include #include void diff --git a/arch/ppc/syslib/virtex_devices.c b/arch/ppc/syslib/virtex_devices.c new file mode 100644 index 0000000..1654678 --- /dev/null +++ b/arch/ppc/syslib/virtex_devices.c @@ -0,0 +1,233 @@ +/* + * Virtex hard ppc405 core common device listing + * + * Copyright 2005-2007 Secret Lab Technologies Ltd. + * Copyright 2005 Freescale Semiconductor Inc. + * Copyright 2002-2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * UARTLITE: shortcut macro for single instance + */ +#define XPAR_UARTLITE(num) { \ + .name = "uartlite", \ + .id = num, \ + .num_resources = 2, \ + .resource = (struct resource[]) { \ + { \ + .start = XPAR_UARTLITE_##num##_BASEADDR + 3, \ + .end = XPAR_UARTLITE_##num##_HIGHADDR, \ + .flags = IORESOURCE_MEM, \ + }, \ + { \ + .start = XPAR_INTC_0_UARTLITE_##num##_VEC_ID, \ + .flags = IORESOURCE_IRQ, \ + }, \ + }, \ +} + +/* + * Full UART: shortcut macro for single instance + platform data structure + */ +#define XPAR_UART(num) { \ + .mapbase = XPAR_UARTNS550_##num##_BASEADDR + 3, \ + .irq = XPAR_INTC_0_UARTNS550_##num##_VEC_ID, \ + .iotype = UPIO_MEM, \ + .uartclk = XPAR_UARTNS550_##num##_CLOCK_FREQ_HZ, \ + .flags = UPF_BOOT_AUTOCONF, \ + .regshift = 2, \ +} + +/* + * SystemACE: shortcut macro for single instance + */ +#define XPAR_SYSACE(num) { \ + .name = "xsysace", \ + .id = XPAR_SYSACE_##num##_DEVICE_ID, \ + .num_resources = 2, \ + .resource = (struct resource[]) { \ + { \ + .start = XPAR_SYSACE_##num##_BASEADDR, \ + .end = XPAR_SYSACE_##num##_HIGHADDR, \ + .flags = IORESOURCE_MEM, \ + }, \ + { \ + .start = XPAR_INTC_0_SYSACE_##num##_VEC_ID, \ + .flags = IORESOURCE_IRQ, \ + }, \ + }, \ +} + + +/* UART 8250 driver platform data table */ +struct plat_serial8250_port virtex_serial_platform_data[] = { +#if defined(XPAR_UARTNS550_0_BASEADDR) + XPAR_UART(0), +#endif +#if defined(XPAR_UARTNS550_1_BASEADDR) + XPAR_UART(1), +#endif +#if defined(XPAR_UARTNS550_2_BASEADDR) + XPAR_UART(2), +#endif +#if defined(XPAR_UARTNS550_3_BASEADDR) + XPAR_UART(3), +#endif +#if defined(XPAR_UARTNS550_4_BASEADDR) + XPAR_UART(4), +#endif +#if defined(XPAR_UARTNS550_5_BASEADDR) + XPAR_UART(5), +#endif +#if defined(XPAR_UARTNS550_6_BASEADDR) + XPAR_UART(6), +#endif +#if defined(XPAR_UARTNS550_7_BASEADDR) + XPAR_UART(7), +#endif + { }, /* terminated by empty record */ +}; + + +struct platform_device virtex_platform_devices[] = { + /* UARTLITE instances */ +#if defined(XPAR_UARTLITE_0_BASEADDR) + XPAR_UARTLITE(0), +#endif +#if defined(XPAR_UARTLITE_1_BASEADDR) + XPAR_UARTLITE(1), +#endif +#if defined(XPAR_UARTLITE_2_BASEADDR) + XPAR_UARTLITE(2), +#endif +#if defined(XPAR_UARTLITE_3_BASEADDR) + XPAR_UARTLITE(3), +#endif +#if defined(XPAR_UARTLITE_4_BASEADDR) + XPAR_UARTLITE(4), +#endif +#if defined(XPAR_UARTLITE_5_BASEADDR) + XPAR_UARTLITE(5), +#endif +#if defined(XPAR_UARTLITE_6_BASEADDR) + XPAR_UARTLITE(6), +#endif +#if defined(XPAR_UARTLITE_7_BASEADDR) + XPAR_UARTLITE(7), +#endif + + /* Full UART instances */ +#if defined(XPAR_UARTNS550_0_BASEADDR) + { + .name = "serial8250", + .id = 0, + .dev.platform_data = virtex_serial_platform_data, + }, +#endif + + /* SystemACE instances */ +#if defined(XPAR_SYSACE_0_BASEADDR) + XPAR_SYSACE(0), +#endif +#if defined(XPAR_SYSACE_1_BASEADDR) + XPAR_SYSACE(1), +#endif + + /* ML300/403 reference design framebuffer */ +#if defined(XPAR_TFT_0_BASEADDR) + { + .name = "xilinxfb", + .id = 0, + .num_resources = 1, + .resource = (struct resource[]) { + { + .start = XPAR_TFT_0_BASEADDR, + .end = XPAR_TFT_0_BASEADDR+7, + .flags = IORESOURCE_IO, + }, + }, + }, +#endif +}; + +/* Early serial support functions */ +static void __init +virtex_early_serial_init(int num, struct plat_serial8250_port *pdata) +{ +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + struct uart_port serial_req; + + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.mapbase = pdata->mapbase; + serial_req.membase = pdata->membase; + serial_req.irq = pdata->irq; + serial_req.uartclk = pdata->uartclk; + serial_req.regshift = pdata->regshift; + serial_req.iotype = pdata->iotype; + serial_req.flags = pdata->flags; + gen550_init(num, &serial_req); +#endif +} + +void __init +virtex_early_serial_map(void) +{ +#ifdef CONFIG_SERIAL_8250 + struct plat_serial8250_port *pdata; + int i = 0; + + pdata = virtex_serial_platform_data; + while(pdata && pdata->flags) { + pdata->membase = ioremap(pdata->mapbase, 0x100); + virtex_early_serial_init(i, pdata); + pdata++; + i++; + } +#endif /* CONFIG_SERIAL_8250 */ +} + +/* + * default fixup routine; do nothing and return success. + * + * Reimplement this routine in your custom board support file to + * override the default behaviour + */ +int __attribute__ ((weak)) +virtex_device_fixup(struct platform_device *dev) +{ + return 0; +} + +static int __init virtex_init(void) +{ + struct platform_device *index = virtex_platform_devices; + unsigned int ret = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(virtex_platform_devices); i++, index++) { + if (virtex_device_fixup(index) != 0) + continue; + + if (platform_device_register(index)) { + ret = 1; + printk(KERN_ERR "cannot register dev %s:%d\n", + index->name, index->id); + } + } + return ret; +} + +subsys_initcall(virtex_init); diff --git a/arch/ppc/syslib/virtex_devices.h b/arch/ppc/syslib/virtex_devices.h new file mode 100644 index 0000000..4a17dd3 --- /dev/null +++ b/arch/ppc/syslib/virtex_devices.h @@ -0,0 +1,27 @@ +/* + * Common support header for virtex ppc405 platforms + * + * Copyright 2007 Secret Lab Technologies Ltd. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __ASM_VIRTEX_DEVICES_H__ +#define __ASM_VIRTEX_DEVICES_H__ + +#include + +void __init virtex_early_serial_map(void); + +/* Prototype for device fixup routine. Implement this routine in the + * board specific fixup code and the generic setup code will call it for + * each device is the platform device list. + * + * If the hook returns a non-zero value, then the device will not get + * registered with the platform bus + */ +int virtex_device_fixup(struct platform_device *dev); + +#endif /* __ASM_VIRTEX_DEVICES_H__ */ diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 0f293aa..e6ec418 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -41,6 +41,11 @@ config GENERIC_HWEIGHT config GENERIC_TIME def_bool y +config GENERIC_BUG + bool + depends on BUG + default y + config NO_IOMEM def_bool y @@ -514,6 +519,14 @@ config KEXEC current kernel, and to start another kernel. It is like a reboot but is independent of hardware/microcode support. +config ZFCPDUMP + tristate "zfcpdump support" + select SMP + default n + help + Select this option if you want to build an zfcpdump enabled kernel. + Refer to "Documentation/s390/zfcpdump.txt" for more details on this. + endmenu source "net/Kconfig" diff --git a/arch/s390/Makefile b/arch/s390/Makefile index b1e5584..68441e0 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -67,8 +67,10 @@ endif ifeq ($(call cc-option-yn,-mstack-size=8192 -mstack-guard=128),y) cflags-$(CONFIG_CHECK_STACK) += -mstack-size=$(STACK_SIZE) +ifneq ($(call cc-option-yn,-mstack-size=8192),y) cflags-$(CONFIG_CHECK_STACK) += -mstack-guard=$(CONFIG_STACK_GUARD) endif +endif ifeq ($(call cc-option-yn,-mwarn-dynamicstack),y) cflags-$(CONFIG_WARN_STACK) += -mwarn-dynamicstack @@ -103,6 +105,9 @@ install: vmlinux image: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ +zfcpdump: + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + archclean: $(Q)$(MAKE) $(clean)=$(boot) diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index 0c3cf4b..ee89b33 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -668,45 +668,7 @@ EXPORT_SYMBOL_GPL(appldata_register_ops) EXPORT_SYMBOL_GPL(appldata_unregister_ops); EXPORT_SYMBOL_GPL(appldata_diag); -#ifdef MODULE -/* - * Kernel symbols needed by appldata_mem and appldata_os modules. - * However, if this file is compiled as a module (for testing only), these - * symbols are not exported. In this case, we define them locally and export - * those. - */ -void si_swapinfo(struct sysinfo *val) -{ - val->freeswap = -1ul; - val->totalswap = -1ul; -} - -unsigned long avenrun[3] = {-1 - FIXED_1/200, -1 - FIXED_1/200, - -1 - FIXED_1/200}; -int nr_threads = -1; - -void get_full_page_state(struct page_state *ps) -{ - memset(ps, -1, sizeof(struct page_state)); -} - -unsigned long nr_running(void) -{ - return -1; -} - -unsigned long nr_iowait(void) -{ - return -1; -} - -/*unsigned long nr_context_switches(void) -{ - return -1; -}*/ -#endif /* MODULE */ EXPORT_SYMBOL_GPL(si_swapinfo); EXPORT_SYMBOL_GPL(nr_threads); EXPORT_SYMBOL_GPL(nr_running); EXPORT_SYMBOL_GPL(nr_iowait); -//EXPORT_SYMBOL_GPL(nr_context_switches); diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c index f64b8c8..a43f348 100644 --- a/arch/s390/appldata/appldata_net_sum.c +++ b/arch/s390/appldata/appldata_net_sum.c @@ -108,9 +108,6 @@ static void appldata_get_net_sum_data(vo collisions = 0; read_lock(&dev_base_lock); for (dev = dev_base; dev != NULL; dev = dev->next) { - if (dev->get_stats == NULL) { - continue; - } stats = dev->get_stats(dev); rx_packets += stats->rx_packets; tx_packets += stats->tx_packets; diff --git a/arch/s390/crypto/sha1_s390.c b/arch/s390/crypto/sha1_s390.c index 969639f..af4460e 100644 --- a/arch/s390/crypto/sha1_s390.c +++ b/arch/s390/crypto/sha1_s390.c @@ -25,99 +25,100 @@ */ #include #include -#include #include -#include -#include + #include "crypt_s390.h" #define SHA1_DIGEST_SIZE 20 #define SHA1_BLOCK_SIZE 64 -struct crypt_s390_sha1_ctx { - u64 count; +struct s390_sha1_ctx { + u64 count; /* message length */ u32 state[5]; - u32 buf_len; - u8 buffer[2 * SHA1_BLOCK_SIZE]; + u8 buf[2 * SHA1_BLOCK_SIZE]; }; static void sha1_init(struct crypto_tfm *tfm) { - struct crypt_s390_sha1_ctx *ctx = crypto_tfm_ctx(tfm); - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; - ctx->state[4] = 0xC3D2E1F0; - - ctx->count = 0; - ctx->buf_len = 0; + struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + + sctx->state[0] = 0x67452301; + sctx->state[1] = 0xEFCDAB89; + sctx->state[2] = 0x98BADCFE; + sctx->state[3] = 0x10325476; + sctx->state[4] = 0xC3D2E1F0; + sctx->count = 0; } static void sha1_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len) { - struct crypt_s390_sha1_ctx *sctx; - long imd_len; - - sctx = crypto_tfm_ctx(tfm); - sctx->count += len * 8; /* message bit length */ - - /* anything in buffer yet? -> must be completed */ - if (sctx->buf_len && (sctx->buf_len + len) >= SHA1_BLOCK_SIZE) { - /* complete full block and hash */ - memcpy(sctx->buffer + sctx->buf_len, data, - SHA1_BLOCK_SIZE - sctx->buf_len); - crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buffer, - SHA1_BLOCK_SIZE); - data += SHA1_BLOCK_SIZE - sctx->buf_len; - len -= SHA1_BLOCK_SIZE - sctx->buf_len; - sctx->buf_len = 0; + struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + unsigned int index; + int ret; + + /* how much is already in the buffer? */ + index = sctx->count & 0x3f; + + sctx->count += len; + + if (index + len < SHA1_BLOCK_SIZE) + goto store; + + /* process one stored block */ + if (index) { + memcpy(sctx->buf + index, data, SHA1_BLOCK_SIZE - index); + ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buf, + SHA1_BLOCK_SIZE); + BUG_ON(ret != SHA1_BLOCK_SIZE); + data += SHA1_BLOCK_SIZE - index; + len -= SHA1_BLOCK_SIZE - index; } - /* rest of data contains full blocks? */ - imd_len = len & ~0x3ful; - if (imd_len) { - crypt_s390_kimd(KIMD_SHA_1, sctx->state, data, imd_len); - data += imd_len; - len -= imd_len; + /* process as many blocks as possible */ + if (len >= SHA1_BLOCK_SIZE) { + ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, data, + len & ~(SHA1_BLOCK_SIZE - 1)); + BUG_ON(ret != (len & ~(SHA1_BLOCK_SIZE - 1))); + data += ret; + len -= ret; } - /* anything left? store in buffer */ - if (len) { - memcpy(sctx->buffer + sctx->buf_len , data, len); - sctx->buf_len += len; - } -} +store: + /* anything left? */ + if (len) + memcpy(sctx->buf + index , data, len); +} -static void pad_message(struct crypt_s390_sha1_ctx* sctx) +/* Add padding and return the message digest. */ +static void sha1_final(struct crypto_tfm *tfm, u8 *out) { - int index; + struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + u64 bits; + unsigned int index, end; + int ret; + + /* must perform manual padding */ + index = sctx->count & 0x3f; + end = (index < 56) ? SHA1_BLOCK_SIZE : (2 * SHA1_BLOCK_SIZE); - index = sctx->buf_len; - sctx->buf_len = (sctx->buf_len < 56) ? - SHA1_BLOCK_SIZE:2 * SHA1_BLOCK_SIZE; /* start pad with 1 */ - sctx->buffer[index] = 0x80; + sctx->buf[index] = 0x80; + /* pad with zeros */ index++; - memset(sctx->buffer + index, 0x00, sctx->buf_len - index); - /* append length */ - memcpy(sctx->buffer + sctx->buf_len - 8, &sctx->count, - sizeof sctx->count); -} + memset(sctx->buf + index, 0x00, end - index - 8); -/* Add padding and return the message digest. */ -static void sha1_final(struct crypto_tfm *tfm, u8 *out) -{ - struct crypt_s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + /* append message length */ + bits = sctx->count * 8; + memcpy(sctx->buf + end - 8, &bits, sizeof(bits)); + + ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buf, end); + BUG_ON(ret != end); - /* must perform manual padding */ - pad_message(sctx); - crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buffer, sctx->buf_len); /* copy digest to out */ memcpy(out, sctx->state, SHA1_DIGEST_SIZE); + /* wipe context */ memset(sctx, 0, sizeof *sctx); } @@ -128,7 +129,7 @@ static struct crypto_alg alg = { .cra_priority = CRYPT_S390_PRIORITY, .cra_flags = CRYPTO_ALG_TYPE_DIGEST, .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct crypt_s390_sha1_ctx), + .cra_ctxsize = sizeof(struct s390_sha1_ctx), .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(alg.cra_list), .cra_u = { .digest = { diff --git a/arch/s390/crypto/sha256_s390.c b/arch/s390/crypto/sha256_s390.c index 78436c6..2ced333 100644 --- a/arch/s390/crypto/sha256_s390.c +++ b/arch/s390/crypto/sha256_s390.c @@ -26,7 +26,7 @@ #define SHA256_DIGEST_SIZE 32 #define SHA256_BLOCK_SIZE 64 struct s390_sha256_ctx { - u64 count; + u64 count; /* message length */ u32 state[8]; u8 buf[2 * SHA256_BLOCK_SIZE]; }; @@ -54,10 +54,9 @@ static void sha256_update(struct crypto_ int ret; /* how much is already in the buffer? */ - index = sctx->count / 8 & 0x3f; + index = sctx->count & 0x3f; - /* update message bit length */ - sctx->count += len * 8; + sctx->count += len; if ((index + len) < SHA256_BLOCK_SIZE) goto store; @@ -87,12 +86,17 @@ store: memcpy(sctx->buf + index , data, len); } -static void pad_message(struct s390_sha256_ctx* sctx) +/* Add padding and return the message digest */ +static void sha256_final(struct crypto_tfm *tfm, u8 *out) { - int index, end; + struct s390_sha256_ctx *sctx = crypto_tfm_ctx(tfm); + u64 bits; + unsigned int index, end; + int ret; - index = sctx->count / 8 & 0x3f; - end = index < 56 ? SHA256_BLOCK_SIZE : 2 * SHA256_BLOCK_SIZE; + /* must perform manual padding */ + index = sctx->count & 0x3f; + end = (index < 56) ? SHA256_BLOCK_SIZE : (2 * SHA256_BLOCK_SIZE); /* start pad with 1 */ sctx->buf[index] = 0x80; @@ -102,21 +106,11 @@ static void pad_message(struct s390_sha2 memset(sctx->buf + index, 0x00, end - index - 8); /* append message length */ - memcpy(sctx->buf + end - 8, &sctx->count, sizeof sctx->count); - - sctx->count = end * 8; -} - -/* Add padding and return the message digest */ -static void sha256_final(struct crypto_tfm *tfm, u8 *out) -{ - struct s390_sha256_ctx *sctx = crypto_tfm_ctx(tfm); - - /* must perform manual padding */ - pad_message(sctx); + bits = sctx->count * 8; + memcpy(sctx->buf + end - 8, &bits, sizeof(bits)); - crypt_s390_kimd(KIMD_SHA_256, sctx->state, sctx->buf, - sctx->count / 8); + ret = crypt_s390_kimd(KIMD_SHA_256, sctx->state, sctx->buf, end); + BUG_ON(ret != end); /* copy digest to out */ memcpy(out, sctx->state, SHA256_DIGEST_SIZE); diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 741d2bb..0e4da8a 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -12,6 +12,7 @@ # CONFIG_ARCH_HAS_ILOG2_U32 is not set # CONFIG_ARCH_HAS_ILOG2_U64 is not set CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_BUG=y CONFIG_NO_IOMEM=y CONFIG_S390=y CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" @@ -166,6 +167,7 @@ CONFIG_NO_IDLE_HZ=y CONFIG_NO_IDLE_HZ_INIT=y CONFIG_S390_HYPFS_FS=y CONFIG_KEXEC=y +# CONFIG_ZFCPDUMP is not set # # Networking @@ -705,6 +707,7 @@ # CONFIG_PROVE_LOCKING is not set CONFIG_DEBUG_SPINLOCK_SLEEP=y # CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set # CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_DEBUG_INFO is not set # CONFIG_DEBUG_VM is not set # CONFIG_DEBUG_LIST is not set diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 5492d25..3195d37 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -6,7 +6,7 @@ EXTRA_AFLAGS := -traditional obj-y := bitmap.o traps.o time.o process.o base.o early.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ - semaphore.o s390_ext.o debug.o irq.o ipl.o + semaphore.o s390_ext.o debug.o irq.o ipl.o dis.o obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index 664c669..5236fdb 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -495,29 +495,34 @@ sys32_rt_sigqueueinfo(int pid, int sig, * sys32_execve() executes a new program after the asm stub has set * things up for us. This should basically do what I want it to. */ -asmlinkage long -sys32_execve(struct pt_regs regs) +asmlinkage long sys32_execve(void) { - int error; - char * filename; + struct pt_regs *regs = task_pt_regs(current); + char *filename; + unsigned long result; + int rc; - filename = getname(compat_ptr(regs.orig_gpr2)); - error = PTR_ERR(filename); - if (IS_ERR(filename)) + filename = getname(compat_ptr(regs->orig_gpr2)); + if (IS_ERR(filename)) { + result = PTR_ERR(filename); goto out; - error = compat_do_execve(filename, compat_ptr(regs.gprs[3]), - compat_ptr(regs.gprs[4]), ®s); - if (error == 0) - { - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); - current->thread.fp_regs.fpc=0; - asm volatile("sfpc %0,0" : : "d" (0)); } + rc = compat_do_execve(filename, compat_ptr(regs->gprs[3]), + compat_ptr(regs->gprs[4]), regs); + if (rc) { + result = rc; + goto out_putname; + } + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); + current->thread.fp_regs.fpc=0; + asm volatile("sfpc %0,0" : : "d" (0)); + result = regs->gprs[2]; +out_putname: putname(filename); out: - return error; + return result; } @@ -918,19 +923,20 @@ asmlinkage long sys32_write(unsigned int return sys_write(fd, buf, count); } -asmlinkage long sys32_clone(struct pt_regs regs) +asmlinkage long sys32_clone(void) { - unsigned long clone_flags; - unsigned long newsp; + struct pt_regs *regs = task_pt_regs(current); + unsigned long clone_flags; + unsigned long newsp; int __user *parent_tidptr, *child_tidptr; - clone_flags = regs.gprs[3] & 0xffffffffUL; - newsp = regs.orig_gpr2 & 0x7fffffffUL; - parent_tidptr = compat_ptr(regs.gprs[4]); - child_tidptr = compat_ptr(regs.gprs[5]); - if (!newsp) - newsp = regs.gprs[15]; - return do_fork(clone_flags, newsp, ®s, 0, + clone_flags = regs->gprs[3] & 0xffffffffUL; + newsp = regs->orig_gpr2 & 0x7fffffffUL; + parent_tidptr = compat_ptr(regs->gprs[4]); + child_tidptr = compat_ptr(regs->gprs[5]); + if (!newsp) + newsp = regs->gprs[15]; + return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); } diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index 887a988..80a54a0 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -255,9 +255,9 @@ sys32_rt_sigaction(int sig, const struct } asmlinkage long -sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss, - struct pt_regs *regs) +sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss) { + struct pt_regs *regs = task_pt_regs(current); stack_t kss, koss; unsigned long ss_sp; int ret, err = 0; @@ -344,8 +344,9 @@ static int restore_sigregs32(struct pt_r return 0; } -asmlinkage long sys32_sigreturn(struct pt_regs *regs) +asmlinkage long sys32_sigreturn(void) { + struct pt_regs *regs = task_pt_regs(current); sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; sigset_t set; @@ -370,8 +371,9 @@ badframe: return 0; } -asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs) +asmlinkage long sys32_rt_sigreturn(void) { + struct pt_regs *regs = task_pt_regs(current); rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; sigset_t set; stack_t st; @@ -407,8 +409,8 @@ asmlinkage long sys32_rt_sigreturn(struc return regs->gprs[2]; badframe: - force_sig(SIGSEGV, current); - return 0; + force_sig(SIGSEGV, current); + return 0; } /* diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c new file mode 100644 index 0000000..dabaf98 --- /dev/null +++ b/arch/s390/kernel/dis.c @@ -0,0 +1,1278 @@ +/* + * arch/s390/kernel/dis.c + * + * Disassemble s390 instructions. + * + * Copyright IBM Corp. 2007 + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_64BIT +#define ONELONG "%08lx: " +#else /* CONFIG_64BIT */ +#define ONELONG "%016lx: " +#endif /* CONFIG_64BIT */ + +#define OPERAND_GPR 0x1 /* Operand printed as %rx */ +#define OPERAND_FPR 0x2 /* Operand printed as %fx */ +#define OPERAND_AR 0x4 /* Operand printed as %ax */ +#define OPERAND_CR 0x8 /* Operand printed as %cx */ +#define OPERAND_DISP 0x10 /* Operand printed as displacement */ +#define OPERAND_BASE 0x20 /* Operand printed as base register */ +#define OPERAND_INDEX 0x40 /* Operand printed as index register */ +#define OPERAND_PCREL 0x80 /* Operand printed as pc-relative symbol */ +#define OPERAND_SIGNED 0x100 /* Operand printed as signed value */ +#define OPERAND_LENGTH 0x200 /* Operand printed as length (+1) */ + +enum { + UNUSED, /* Indicates the end of the operand list */ + R_8, /* GPR starting at position 8 */ + R_12, /* GPR starting at position 12 */ + R_16, /* GPR starting at position 16 */ + R_20, /* GPR starting at position 20 */ + R_24, /* GPR starting at position 24 */ + R_28, /* GPR starting at position 28 */ + R_32, /* GPR starting at position 32 */ + F_8, /* FPR starting at position 8 */ + F_12, /* FPR starting at position 12 */ + F_16, /* FPR starting at position 16 */ + F_20, /* FPR starting at position 16 */ + F_24, /* FPR starting at position 24 */ + F_28, /* FPR starting at position 28 */ + F_32, /* FPR starting at position 32 */ + A_8, /* Access reg. starting at position 8 */ + A_12, /* Access reg. starting at position 12 */ + A_24, /* Access reg. starting at position 24 */ + A_28, /* Access reg. starting at position 28 */ + C_8, /* Control reg. starting at position 8 */ + C_12, /* Control reg. starting at position 12 */ + B_16, /* Base register starting at position 16 */ + B_32, /* Base register starting at position 32 */ + X_12, /* Index register starting at position 12 */ + D_20, /* Displacement starting at position 20 */ + D_36, /* Displacement starting at position 36 */ + D20_20, /* 20 bit displacement starting at 20 */ + L4_8, /* 4 bit length starting at position 8 */ + L4_12, /* 4 bit length starting at position 12 */ + L8_8, /* 8 bit length starting at position 8 */ + U4_8, /* 4 bit unsigned value starting at 8 */ + U4_12, /* 4 bit unsigned value starting at 12 */ + U4_16, /* 4 bit unsigned value starting at 16 */ + U4_20, /* 4 bit unsigned value starting at 20 */ + U8_8, /* 8 bit unsigned value starting at 8 */ + U8_16, /* 8 bit unsigned value starting at 16 */ + I16_16, /* 16 bit signed value starting at 16 */ + U16_16, /* 16 bit unsigned value starting at 16 */ + J16_16, /* PC relative jump offset at 16 */ + J32_16, /* PC relative long offset at 16 */ + I32_16, /* 32 bit signed value starting at 16 */ + U32_16, /* 32 bit unsigned value starting at 16 */ + M_16, /* 4 bit optional mask starting at 16 */ + RO_28, /* optional GPR starting at position 28 */ +}; + +/* + * Enumeration of the different instruction formats. + * For details consult the principles of operation. + */ +enum { + INSTR_INVALID, + INSTR_E, INSTR_RIE_RRP, INSTR_RIL_RI, INSTR_RIL_RP, INSTR_RIL_RU, + INSTR_RIL_UP, INSTR_RI_RI, INSTR_RI_RP, INSTR_RI_RU, INSTR_RI_UP, + INSTR_RRE_00, INSTR_RRE_0R, INSTR_RRE_AA, INSTR_RRE_AR, INSTR_RRE_F0, + INSTR_RRE_FF, INSTR_RRE_R0, INSTR_RRE_RA, INSTR_RRE_RF, INSTR_RRE_RR, + INSTR_RRE_RR_OPT, INSTR_RRF_F0FF, INSTR_RRF_FUFF, INSTR_RRF_M0RR, + INSTR_RRF_R0RR, INSTR_RRF_RURR, INSTR_RRF_U0FF, INSTR_RRF_U0RF, + INSTR_RR_FF, INSTR_RR_R0, INSTR_RR_RR, INSTR_RR_U0, INSTR_RR_UR, + INSTR_RSE_CCRD, INSTR_RSE_RRRD, INSTR_RSE_RURD, INSTR_RSI_RRP, + INSTR_RSL_R0RD, INSTR_RSY_AARD, INSTR_RSY_CCRD, INSTR_RSY_RRRD, + INSTR_RSY_RURD, INSTR_RS_AARD, INSTR_RS_CCRD, INSTR_RS_R0RD, + INSTR_RS_RRRD, INSTR_RS_RURD, INSTR_RXE_FRRD, INSTR_RXE_RRRD, + INSTR_RXF_FRRDF, INSTR_RXY_FRRD, INSTR_RXY_RRRD, INSTR_RX_FRRD, + INSTR_RX_RRRD, INSTR_RX_URRD, INSTR_SIY_URD, INSTR_SI_URD, + INSTR_SSE_RDRD, INSTR_SSF_RRDRD, INSTR_SS_L0RDRD, INSTR_SS_LIRDRD, + INSTR_SS_LLRDRD, INSTR_SS_RRRDRD, INSTR_SS_RRRDRD2, INSTR_SS_RRRDRD3, + INSTR_S_00, INSTR_S_RD, +}; + +struct operand { + int bits; /* The number of bits in the operand. */ + int shift; /* The number of bits to shift. */ + int flags; /* One bit syntax flags. */ +}; + +struct insn { + const char name[5]; + unsigned char opfrag; + unsigned char format; +}; + +static const struct operand operands[] = +{ + [UNUSED] = { 0, 0, 0 }, + [R_8] = { 4, 8, OPERAND_GPR }, + [R_12] = { 4, 12, OPERAND_GPR }, + [R_16] = { 4, 16, OPERAND_GPR }, + [R_20] = { 4, 20, OPERAND_GPR }, + [R_24] = { 4, 24, OPERAND_GPR }, + [R_28] = { 4, 28, OPERAND_GPR }, + [R_32] = { 4, 32, OPERAND_GPR }, + [F_8] = { 4, 8, OPERAND_FPR }, + [F_12] = { 4, 12, OPERAND_FPR }, + [F_16] = { 4, 16, OPERAND_FPR }, + [F_20] = { 4, 16, OPERAND_FPR }, + [F_24] = { 4, 24, OPERAND_FPR }, + [F_28] = { 4, 28, OPERAND_FPR }, + [F_32] = { 4, 32, OPERAND_FPR }, + [A_8] = { 4, 8, OPERAND_AR }, + [A_12] = { 4, 12, OPERAND_AR }, + [A_24] = { 4, 24, OPERAND_AR }, + [A_28] = { 4, 28, OPERAND_AR }, + [C_8] = { 4, 8, OPERAND_CR }, + [C_12] = { 4, 12, OPERAND_CR }, + [B_16] = { 4, 16, OPERAND_BASE | OPERAND_GPR }, + [B_32] = { 4, 32, OPERAND_BASE | OPERAND_GPR }, + [X_12] = { 4, 12, OPERAND_INDEX | OPERAND_GPR }, + [D_20] = { 12, 20, OPERAND_DISP }, + [D_36] = { 12, 36, OPERAND_DISP }, + [D20_20] = { 20, 20, OPERAND_DISP | OPERAND_SIGNED }, + [L4_8] = { 4, 8, OPERAND_LENGTH }, + [L4_12] = { 4, 12, OPERAND_LENGTH }, + [L8_8] = { 8, 8, OPERAND_LENGTH }, + [U4_8] = { 4, 8, 0 }, + [U4_12] = { 4, 12, 0 }, + [U4_16] = { 4, 16, 0 }, + [U4_20] = { 4, 20, 0 }, + [U8_8] = { 8, 8, 0 }, + [U8_16] = { 8, 16, 0 }, + [I16_16] = { 16, 16, OPERAND_SIGNED }, + [U16_16] = { 16, 16, 0 }, + [J16_16] = { 16, 16, OPERAND_PCREL }, + [J32_16] = { 32, 16, OPERAND_PCREL }, + [I32_16] = { 32, 16, OPERAND_SIGNED }, + [U32_16] = { 32, 16, 0 }, + [M_16] = { 4, 16, 0 }, + [RO_28] = { 4, 28, OPERAND_GPR } +}; + +static const unsigned char formats[][7] = { + [INSTR_E] = { 0xff, 0,0,0,0,0,0 }, /* e.g. pr */ + [INSTR_RIE_RRP] = { 0xff, R_8,R_12,J16_16,0,0,0 }, /* e.g. brxhg */ + [INSTR_RIL_RP] = { 0x0f, R_8,J32_16,0,0,0,0 }, /* e.g. brasl */ + [INSTR_RIL_UP] = { 0x0f, U4_8,J32_16,0,0,0,0 }, /* e.g. brcl */ + [INSTR_RIL_RI] = { 0x0f, R_8,I32_16,0,0,0,0 }, /* e.g. afi */ + [INSTR_RIL_RU] = { 0x0f, R_8,U32_16,0,0,0,0 }, /* e.g. alfi */ + [INSTR_RI_RI] = { 0x0f, R_8,I16_16,0,0,0,0 }, /* e.g. ahi */ + [INSTR_RI_RP] = { 0x0f, R_8,J16_16,0,0,0,0 }, /* e.g. brct */ + [INSTR_RI_RU] = { 0x0f, R_8,U16_16,0,0,0,0 }, /* e.g. tml */ + [INSTR_RI_UP] = { 0x0f, U4_8,J16_16,0,0,0,0 }, /* e.g. brc */ + [INSTR_RRE_00] = { 0xff, 0,0,0,0,0,0 }, /* e.g. palb */ + [INSTR_RRE_0R] = { 0xff, R_28,0,0,0,0,0 }, /* e.g. tb */ + [INSTR_RRE_AA] = { 0xff, A_24,A_28,0,0,0,0 }, /* e.g. cpya */ + [INSTR_RRE_AR] = { 0xff, A_24,R_28,0,0,0,0 }, /* e.g. sar */ + [INSTR_RRE_F0] = { 0xff, F_24,0,0,0,0,0 }, /* e.g. sqer */ + [INSTR_RRE_FF] = { 0xff, F_24,F_28,0,0,0,0 }, /* e.g. debr */ + [INSTR_RRE_R0] = { 0xff, R_24,0,0,0,0,0 }, /* e.g. ipm */ + [INSTR_RRE_RA] = { 0xff, R_24,A_28,0,0,0,0 }, /* e.g. ear */ + [INSTR_RRE_RF] = { 0xff, R_24,F_28,0,0,0,0 }, /* e.g. cefbr */ + [INSTR_RRE_RR] = { 0xff, R_24,R_28,0,0,0,0 }, /* e.g. lura */ + [INSTR_RRE_RR_OPT]= { 0xff, R_24,RO_28,0,0,0,0 }, /* efpc, sfpc */ + [INSTR_RRF_F0FF] = { 0xff, F_16,F_24,F_28,0,0,0 }, /* e.g. madbr */ + [INSTR_RRF_FUFF] = { 0xff, F_24,F_16,F_28,U4_20,0,0 },/* e.g. didbr */ + [INSTR_RRF_RURR] = { 0xff, R_24,R_28,R_16,U4_20,0,0 },/* e.g. .insn */ + [INSTR_RRF_R0RR] = { 0xff, R_24,R_28,R_16,0,0,0 }, /* e.g. idte */ + [INSTR_RRF_U0FF] = { 0xff, F_24,U4_16,F_28,0,0,0 }, /* e.g. fixr */ + [INSTR_RRF_U0RF] = { 0xff, R_24,U4_16,F_28,0,0,0 }, /* e.g. cfebr */ + [INSTR_RRF_M0RR] = { 0xff, R_24,R_28,M_16,0,0,0 }, /* e.g. sske */ + [INSTR_RR_FF] = { 0xff, F_8,F_12,0,0,0,0 }, /* e.g. adr */ + [INSTR_RR_R0] = { 0xff, R_8, 0,0,0,0,0 }, /* e.g. spm */ + [INSTR_RR_RR] = { 0xff, R_8,R_12,0,0,0,0 }, /* e.g. lr */ + [INSTR_RR_U0] = { 0xff, U8_8, 0,0,0,0,0 }, /* e.g. svc */ + [INSTR_RR_UR] = { 0xff, U4_8,R_12,0,0,0,0 }, /* e.g. bcr */ + [INSTR_RSE_RRRD] = { 0xff, R_8,R_12,D_20,B_16,0,0 }, /* e.g. lmh */ + [INSTR_RSE_CCRD] = { 0xff, C_8,C_12,D_20,B_16,0,0 }, /* e.g. lmh */ + [INSTR_RSE_RURD] = { 0xff, R_8,U4_12,D_20,B_16,0,0 }, /* e.g. icmh */ + [INSTR_RSL_R0RD] = { 0xff, R_8,D_20,B_16,0,0,0 }, /* e.g. tp */ + [INSTR_RSI_RRP] = { 0xff, R_8,R_12,J16_16,0,0,0 }, /* e.g. brxh */ + [INSTR_RSY_RRRD] = { 0xff, R_8,R_12,D20_20,B_16,0,0 },/* e.g. stmy */ + [INSTR_RSY_RURD] = { 0xff, R_8,U4_12,D20_20,B_16,0,0 }, + /* e.g. icmh */ + [INSTR_RSY_AARD] = { 0xff, A_8,A_12,D20_20,B_16,0,0 },/* e.g. lamy */ + [INSTR_RSY_CCRD] = { 0xff, C_8,C_12,D20_20,B_16,0,0 },/* e.g. lamy */ + [INSTR_RS_AARD] = { 0xff, A_8,A_12,D_20,B_16,0,0 }, /* e.g. lam */ + [INSTR_RS_CCRD] = { 0xff, C_8,C_12,D_20,B_16,0,0 }, /* e.g. lctl */ + [INSTR_RS_R0RD] = { 0xff, R_8,D_20,B_16,0,0,0 }, /* e.g. sll */ + [INSTR_RS_RRRD] = { 0xff, R_8,R_12,D_20,B_16,0,0 }, /* e.g. cs */ + [INSTR_RS_RURD] = { 0xff, R_8,U4_12,D_20,B_16,0,0 }, /* e.g. icm */ + [INSTR_RXE_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 }, /* e.g. axbr */ + [INSTR_RXE_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 }, /* e.g. lg */ + [INSTR_RXF_FRRDF] = { 0xff, F_32,F_8,D_20,X_12,B_16,0 }, + /* e.g. madb */ + [INSTR_RXY_RRRD] = { 0xff, R_8,D20_20,X_12,B_16,0,0 },/* e.g. ly */ + [INSTR_RXY_FRRD] = { 0xff, F_8,D20_20,X_12,B_16,0,0 },/* e.g. ley */ + [INSTR_RX_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 }, /* e.g. ae */ + [INSTR_RX_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 }, /* e.g. l */ + [INSTR_RX_URRD] = { 0x00, U4_8,D_20,X_12,B_16,0,0 }, /* e.g. bc */ + [INSTR_SI_URD] = { 0x00, D_20,B_16,U8_8,0,0,0 }, /* e.g. cli */ + [INSTR_SIY_URD] = { 0xff, D20_20,B_16,U8_8,0,0,0 }, /* e.g. tmy */ + [INSTR_SSE_RDRD] = { 0xff, D_20,B_16,D_36,B_32,0,0 }, /* e.g. mvsdk */ + [INSTR_SS_L0RDRD] = { 0xff, D_20,L8_8,B_16,D_36,B_32,0 }, + /* e.g. mvc */ + [INSTR_SS_LIRDRD] = { 0xff, D_20,L4_8,B_16,D_36,B_32,U4_12 }, + /* e.g. srp */ + [INSTR_SS_LLRDRD] = { 0xff, D_20,L4_8,B_16,D_36,L4_12,B_32 }, + /* e.g. pack */ + [INSTR_SS_RRRDRD] = { 0xff, D_20,R_8,B_16,D_36,B_32,R_12 }, + /* e.g. mvck */ + [INSTR_SS_RRRDRD2]= { 0xff, R_8,D_20,B_16,R_12,D_36,B_32 }, + /* e.g. plo */ + [INSTR_SS_RRRDRD3]= { 0xff, R_8,R_12,D_20,B_16,D_36,B_32 }, + /* e.g. lmd */ + [INSTR_S_00] = { 0xff, 0,0,0,0,0,0 }, /* e.g. hsch */ + [INSTR_S_RD] = { 0xff, D_20,B_16,0,0,0,0 }, /* e.g. lpsw */ + [INSTR_SSF_RRDRD] = { 0x00, D_20,B_16,D_36,B_32,R_8,0 }, + /* e.g. mvcos */ +}; + +static struct insn opcode[] = { +#ifdef CONFIG_64BIT + { "lmd", 0xef, INSTR_SS_RRRDRD3 }, +#endif + { "spm", 0x04, INSTR_RR_R0 }, + { "balr", 0x05, INSTR_RR_RR }, + { "bctr", 0x06, INSTR_RR_RR }, + { "bcr", 0x07, INSTR_RR_UR }, + { "svc", 0x0a, INSTR_RR_U0 }, + { "bsm", 0x0b, INSTR_RR_RR }, + { "bassm", 0x0c, INSTR_RR_RR }, + { "basr", 0x0d, INSTR_RR_RR }, + { "mvcl", 0x0e, INSTR_RR_RR }, + { "clcl", 0x0f, INSTR_RR_RR }, + { "lpr", 0x10, INSTR_RR_RR }, + { "lnr", 0x11, INSTR_RR_RR }, + { "ltr", 0x12, INSTR_RR_RR }, + { "lcr", 0x13, INSTR_RR_RR }, + { "nr", 0x14, INSTR_RR_RR }, + { "clr", 0x15, INSTR_RR_RR }, + { "or", 0x16, INSTR_RR_RR }, + { "xr", 0x17, INSTR_RR_RR }, + { "lr", 0x18, INSTR_RR_RR }, + { "cr", 0x19, INSTR_RR_RR }, + { "ar", 0x1a, INSTR_RR_RR }, + { "sr", 0x1b, INSTR_RR_RR }, + { "mr", 0x1c, INSTR_RR_RR }, + { "dr", 0x1d, INSTR_RR_RR }, + { "alr", 0x1e, INSTR_RR_RR }, + { "slr", 0x1f, INSTR_RR_RR }, + { "lpdr", 0x20, INSTR_RR_FF }, + { "lndr", 0x21, INSTR_RR_FF }, + { "ltdr", 0x22, INSTR_RR_FF }, + { "lcdr", 0x23, INSTR_RR_FF }, + { "hdr", 0x24, INSTR_RR_FF }, + { "ldxr", 0x25, INSTR_RR_FF }, + { "lrdr", 0x25, INSTR_RR_FF }, + { "mxr", 0x26, INSTR_RR_FF }, + { "mxdr", 0x27, INSTR_RR_FF }, + { "ldr", 0x28, INSTR_RR_FF }, + { "cdr", 0x29, INSTR_RR_FF }, + { "adr", 0x2a, INSTR_RR_FF }, + { "sdr", 0x2b, INSTR_RR_FF }, + { "mdr", 0x2c, INSTR_RR_FF }, + { "ddr", 0x2d, INSTR_RR_FF }, + { "awr", 0x2e, INSTR_RR_FF }, + { "swr", 0x2f, INSTR_RR_FF }, + { "lper", 0x30, INSTR_RR_FF }, + { "lner", 0x31, INSTR_RR_FF }, + { "lter", 0x32, INSTR_RR_FF }, + { "lcer", 0x33, INSTR_RR_FF }, + { "her", 0x34, INSTR_RR_FF }, + { "ledr", 0x35, INSTR_RR_FF }, + { "lrer", 0x35, INSTR_RR_FF }, + { "axr", 0x36, INSTR_RR_FF }, + { "sxr", 0x37, INSTR_RR_FF }, + { "ler", 0x38, INSTR_RR_FF }, + { "cer", 0x39, INSTR_RR_FF }, + { "aer", 0x3a, INSTR_RR_FF }, + { "ser", 0x3b, INSTR_RR_FF }, + { "mder", 0x3c, INSTR_RR_FF }, + { "mer", 0x3c, INSTR_RR_FF }, + { "der", 0x3d, INSTR_RR_FF }, + { "aur", 0x3e, INSTR_RR_FF }, + { "sur", 0x3f, INSTR_RR_FF }, + { "sth", 0x40, INSTR_RX_RRRD }, + { "la", 0x41, INSTR_RX_RRRD }, + { "stc", 0x42, INSTR_RX_RRRD }, + { "ic", 0x43, INSTR_RX_RRRD }, + { "ex", 0x44, INSTR_RX_RRRD }, + { "bal", 0x45, INSTR_RX_RRRD }, + { "bct", 0x46, INSTR_RX_RRRD }, + { "bc", 0x47, INSTR_RX_URRD }, + { "lh", 0x48, INSTR_RX_RRRD }, + { "ch", 0x49, INSTR_RX_RRRD }, + { "ah", 0x4a, INSTR_RX_RRRD }, + { "sh", 0x4b, INSTR_RX_RRRD }, + { "mh", 0x4c, INSTR_RX_RRRD }, + { "bas", 0x4d, INSTR_RX_RRRD }, + { "cvd", 0x4e, INSTR_RX_RRRD }, + { "cvb", 0x4f, INSTR_RX_RRRD }, + { "st", 0x50, INSTR_RX_RRRD }, + { "lae", 0x51, INSTR_RX_RRRD }, + { "n", 0x54, INSTR_RX_RRRD }, + { "cl", 0x55, INSTR_RX_RRRD }, + { "o", 0x56, INSTR_RX_RRRD }, + { "x", 0x57, INSTR_RX_RRRD }, + { "l", 0x58, INSTR_RX_RRRD }, + { "c", 0x59, INSTR_RX_RRRD }, + { "a", 0x5a, INSTR_RX_RRRD }, + { "s", 0x5b, INSTR_RX_RRRD }, + { "m", 0x5c, INSTR_RX_RRRD }, + { "d", 0x5d, INSTR_RX_RRRD }, + { "al", 0x5e, INSTR_RX_RRRD }, + { "sl", 0x5f, INSTR_RX_RRRD }, + { "std", 0x60, INSTR_RX_FRRD }, + { "mxd", 0x67, INSTR_RX_FRRD }, + { "ld", 0x68, INSTR_RX_FRRD }, + { "cd", 0x69, INSTR_RX_FRRD }, + { "ad", 0x6a, INSTR_RX_FRRD }, + { "sd", 0x6b, INSTR_RX_FRRD }, + { "md", 0x6c, INSTR_RX_FRRD }, + { "dd", 0x6d, INSTR_RX_FRRD }, + { "aw", 0x6e, INSTR_RX_FRRD }, + { "sw", 0x6f, INSTR_RX_FRRD }, + { "ste", 0x70, INSTR_RX_FRRD }, + { "ms", 0x71, INSTR_RX_RRRD }, + { "le", 0x78, INSTR_RX_FRRD }, + { "ce", 0x79, INSTR_RX_FRRD }, + { "ae", 0x7a, INSTR_RX_FRRD }, + { "se", 0x7b, INSTR_RX_FRRD }, + { "mde", 0x7c, INSTR_RX_FRRD }, + { "me", 0x7c, INSTR_RX_FRRD }, + { "de", 0x7d, INSTR_RX_FRRD }, + { "au", 0x7e, INSTR_RX_FRRD }, + { "su", 0x7f, INSTR_RX_FRRD }, + { "ssm", 0x80, INSTR_S_RD }, + { "lpsw", 0x82, INSTR_S_RD }, + { "diag", 0x83, INSTR_RS_RRRD }, + { "brxh", 0x84, INSTR_RSI_RRP }, + { "brxle", 0x85, INSTR_RSI_RRP }, + { "bxh", 0x86, INSTR_RS_RRRD }, + { "bxle", 0x87, INSTR_RS_RRRD }, + { "srl", 0x88, INSTR_RS_R0RD }, + { "sll", 0x89, INSTR_RS_R0RD }, + { "sra", 0x8a, INSTR_RS_R0RD }, + { "sla", 0x8b, INSTR_RS_R0RD }, + { "srdl", 0x8c, INSTR_RS_R0RD }, + { "sldl", 0x8d, INSTR_RS_R0RD }, + { "srda", 0x8e, INSTR_RS_R0RD }, + { "slda", 0x8f, INSTR_RS_R0RD }, + { "stm", 0x90, INSTR_RS_RRRD }, + { "tm", 0x91, INSTR_SI_URD }, + { "mvi", 0x92, INSTR_SI_URD }, + { "ts", 0x93, INSTR_S_RD }, + { "ni", 0x94, INSTR_SI_URD }, + { "cli", 0x95, INSTR_SI_URD }, + { "oi", 0x96, INSTR_SI_URD }, + { "xi", 0x97, INSTR_SI_URD }, + { "lm", 0x98, INSTR_RS_RRRD }, + { "trace", 0x99, INSTR_RS_RRRD }, + { "lam", 0x9a, INSTR_RS_AARD }, + { "stam", 0x9b, INSTR_RS_AARD }, + { "mvcle", 0xa8, INSTR_RS_RRRD }, + { "clcle", 0xa9, INSTR_RS_RRRD }, + { "stnsm", 0xac, INSTR_SI_URD }, + { "stosm", 0xad, INSTR_SI_URD }, + { "sigp", 0xae, INSTR_RS_RRRD }, + { "mc", 0xaf, INSTR_SI_URD }, + { "lra", 0xb1, INSTR_RX_RRRD }, + { "stctl", 0xb6, INSTR_RS_CCRD }, + { "lctl", 0xb7, INSTR_RS_CCRD }, + { "cs", 0xba, INSTR_RS_RRRD }, + { "cds", 0xbb, INSTR_RS_RRRD }, + { "clm", 0xbd, INSTR_RS_RURD }, + { "stcm", 0xbe, INSTR_RS_RURD }, + { "icm", 0xbf, INSTR_RS_RURD }, + { "mvn", 0xd1, INSTR_SS_L0RDRD }, + { "mvc", 0xd2, INSTR_SS_L0RDRD }, + { "mvz", 0xd3, INSTR_SS_L0RDRD }, + { "nc", 0xd4, INSTR_SS_L0RDRD }, + { "clc", 0xd5, INSTR_SS_L0RDRD }, + { "oc", 0xd6, INSTR_SS_L0RDRD }, + { "xc", 0xd7, INSTR_SS_L0RDRD }, + { "mvck", 0xd9, INSTR_SS_RRRDRD }, + { "mvcp", 0xda, INSTR_SS_RRRDRD }, + { "mvcs", 0xdb, INSTR_SS_RRRDRD }, + { "tr", 0xdc, INSTR_SS_L0RDRD }, + { "trt", 0xdd, INSTR_SS_L0RDRD }, + { "ed", 0xde, INSTR_SS_L0RDRD }, + { "edmk", 0xdf, INSTR_SS_L0RDRD }, + { "pku", 0xe1, INSTR_SS_L0RDRD }, + { "unpku", 0xe2, INSTR_SS_L0RDRD }, + { "mvcin", 0xe8, INSTR_SS_L0RDRD }, + { "pka", 0xe9, INSTR_SS_L0RDRD }, + { "unpka", 0xea, INSTR_SS_L0RDRD }, + { "plo", 0xee, INSTR_SS_RRRDRD2 }, + { "srp", 0xf0, INSTR_SS_LIRDRD }, + { "mvo", 0xf1, INSTR_SS_LLRDRD }, + { "pack", 0xf2, INSTR_SS_LLRDRD }, + { "unpk", 0xf3, INSTR_SS_LLRDRD }, + { "zap", 0xf8, INSTR_SS_LLRDRD }, + { "cp", 0xf9, INSTR_SS_LLRDRD }, + { "ap", 0xfa, INSTR_SS_LLRDRD }, + { "sp", 0xfb, INSTR_SS_LLRDRD }, + { "mp", 0xfc, INSTR_SS_LLRDRD }, + { "dp", 0xfd, INSTR_SS_LLRDRD }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_01[] = { +#ifdef CONFIG_64BIT + { "sam64", 0x0e, INSTR_E }, +#endif + { "pr", 0x01, INSTR_E }, + { "upt", 0x02, INSTR_E }, + { "sckpf", 0x07, INSTR_E }, + { "tam", 0x0b, INSTR_E }, + { "sam24", 0x0c, INSTR_E }, + { "sam31", 0x0d, INSTR_E }, + { "trap2", 0xff, INSTR_E }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_a5[] = { +#ifdef CONFIG_64BIT + { "iihh", 0x00, INSTR_RI_RU }, + { "iihl", 0x01, INSTR_RI_RU }, + { "iilh", 0x02, INSTR_RI_RU }, + { "iill", 0x03, INSTR_RI_RU }, + { "nihh", 0x04, INSTR_RI_RU }, + { "nihl", 0x05, INSTR_RI_RU }, + { "nilh", 0x06, INSTR_RI_RU }, + { "nill", 0x07, INSTR_RI_RU }, + { "oihh", 0x08, INSTR_RI_RU }, + { "oihl", 0x09, INSTR_RI_RU }, + { "oilh", 0x0a, INSTR_RI_RU }, + { "oill", 0x0b, INSTR_RI_RU }, + { "llihh", 0x0c, INSTR_RI_RU }, + { "llihl", 0x0d, INSTR_RI_RU }, + { "llilh", 0x0e, INSTR_RI_RU }, + { "llill", 0x0f, INSTR_RI_RU }, +#endif + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_a7[] = { +#ifdef CONFIG_64BIT + { "tmhh", 0x02, INSTR_RI_RU }, + { "tmhl", 0x03, INSTR_RI_RU }, + { "brctg", 0x07, INSTR_RI_RP }, + { "lghi", 0x09, INSTR_RI_RI }, + { "aghi", 0x0b, INSTR_RI_RI }, + { "mghi", 0x0d, INSTR_RI_RI }, + { "cghi", 0x0f, INSTR_RI_RI }, +#endif + { "tmlh", 0x00, INSTR_RI_RU }, + { "tmll", 0x01, INSTR_RI_RU }, + { "brc", 0x04, INSTR_RI_UP }, + { "bras", 0x05, INSTR_RI_RP }, + { "brct", 0x06, INSTR_RI_RP }, + { "lhi", 0x08, INSTR_RI_RI }, + { "ahi", 0x0a, INSTR_RI_RI }, + { "mhi", 0x0c, INSTR_RI_RI }, + { "chi", 0x0e, INSTR_RI_RI }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_b2[] = { +#ifdef CONFIG_64BIT + { "sske", 0x2b, INSTR_RRF_M0RR }, + { "stckf", 0x7c, INSTR_S_RD }, + { "cu21", 0xa6, INSTR_RRF_M0RR }, + { "cuutf", 0xa6, INSTR_RRF_M0RR }, + { "cu12", 0xa7, INSTR_RRF_M0RR }, + { "cutfu", 0xa7, INSTR_RRF_M0RR }, + { "stfle", 0xb0, INSTR_S_RD }, + { "lpswe", 0xb2, INSTR_S_RD }, +#endif + { "stidp", 0x02, INSTR_S_RD }, + { "sck", 0x04, INSTR_S_RD }, + { "stck", 0x05, INSTR_S_RD }, + { "sckc", 0x06, INSTR_S_RD }, + { "stckc", 0x07, INSTR_S_RD }, + { "spt", 0x08, INSTR_S_RD }, + { "stpt", 0x09, INSTR_S_RD }, + { "spka", 0x0a, INSTR_S_RD }, + { "ipk", 0x0b, INSTR_S_00 }, + { "ptlb", 0x0d, INSTR_S_00 }, + { "spx", 0x10, INSTR_S_RD }, + { "stpx", 0x11, INSTR_S_RD }, + { "stap", 0x12, INSTR_S_RD }, + { "sie", 0x14, INSTR_S_RD }, + { "pc", 0x18, INSTR_S_RD }, + { "sac", 0x19, INSTR_S_RD }, + { "cfc", 0x1a, INSTR_S_RD }, + { "ipte", 0x21, INSTR_RRE_RR }, + { "ipm", 0x22, INSTR_RRE_R0 }, + { "ivsk", 0x23, INSTR_RRE_RR }, + { "iac", 0x24, INSTR_RRE_R0 }, + { "ssar", 0x25, INSTR_RRE_R0 }, + { "epar", 0x26, INSTR_RRE_R0 }, + { "esar", 0x27, INSTR_RRE_R0 }, + { "pt", 0x28, INSTR_RRE_RR }, + { "iske", 0x29, INSTR_RRE_RR }, + { "rrbe", 0x2a, INSTR_RRE_RR }, + { "sske", 0x2b, INSTR_RRE_RR }, + { "tb", 0x2c, INSTR_RRE_0R }, + { "dxr", 0x2d, INSTR_RRE_F0 }, + { "pgin", 0x2e, INSTR_RRE_RR }, + { "pgout", 0x2f, INSTR_RRE_RR }, + { "csch", 0x30, INSTR_S_00 }, + { "hsch", 0x31, INSTR_S_00 }, + { "msch", 0x32, INSTR_S_RD }, + { "ssch", 0x33, INSTR_S_RD }, + { "stsch", 0x34, INSTR_S_RD }, + { "tsch", 0x35, INSTR_S_RD }, + { "tpi", 0x36, INSTR_S_RD }, + { "sal", 0x37, INSTR_S_00 }, + { "rsch", 0x38, INSTR_S_00 }, + { "stcrw", 0x39, INSTR_S_RD }, + { "stcps", 0x3a, INSTR_S_RD }, + { "rchp", 0x3b, INSTR_S_00 }, + { "schm", 0x3c, INSTR_S_00 }, + { "bakr", 0x40, INSTR_RRE_RR }, + { "cksm", 0x41, INSTR_RRE_RR }, + { "sqdr", 0x44, INSTR_RRE_F0 }, + { "sqer", 0x45, INSTR_RRE_F0 }, + { "stura", 0x46, INSTR_RRE_RR }, + { "msta", 0x47, INSTR_RRE_R0 }, + { "palb", 0x48, INSTR_RRE_00 }, + { "ereg", 0x49, INSTR_RRE_RR }, + { "esta", 0x4a, INSTR_RRE_RR }, + { "lura", 0x4b, INSTR_RRE_RR }, + { "tar", 0x4c, INSTR_RRE_AR }, + { "cpya", INSTR_RRE_AA }, + { "sar", 0x4e, INSTR_RRE_AR }, + { "ear", 0x4f, INSTR_RRE_RA }, + { "csp", 0x50, INSTR_RRE_RR }, + { "msr", 0x52, INSTR_RRE_RR }, + { "mvpg", 0x54, INSTR_RRE_RR }, + { "mvst", 0x55, INSTR_RRE_RR }, + { "cuse", 0x57, INSTR_RRE_RR }, + { "bsg", 0x58, INSTR_RRE_RR }, + { "bsa", 0x5a, INSTR_RRE_RR }, + { "clst", 0x5d, INSTR_RRE_RR }, + { "srst", 0x5e, INSTR_RRE_RR }, + { "cmpsc", 0x63, INSTR_RRE_RR }, + { "cmpsc", 0x63, INSTR_RRE_RR }, + { "siga", 0x74, INSTR_S_RD }, + { "xsch", 0x76, INSTR_S_00 }, + { "rp", 0x77, INSTR_S_RD }, + { "stcke", 0x78, INSTR_S_RD }, + { "sacf", 0x79, INSTR_S_RD }, + { "stsi", 0x7d, INSTR_S_RD }, + { "srnm", 0x99, INSTR_S_RD }, + { "stfpc", 0x9c, INSTR_S_RD }, + { "lfpc", 0x9d, INSTR_S_RD }, + { "tre", 0xa5, INSTR_RRE_RR }, + { "cuutf", 0xa6, INSTR_RRE_RR }, + { "cutfu", 0xa7, INSTR_RRE_RR }, + { "stfl", 0xb1, INSTR_S_RD }, + { "trap4", 0xff, INSTR_S_RD }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_b3[] = { +#ifdef CONFIG_64BIT + { "maylr", 0x38, INSTR_RRF_F0FF }, + { "mylr", 0x39, INSTR_RRF_F0FF }, + { "mayr", 0x3a, INSTR_RRF_F0FF }, + { "myr", 0x3b, INSTR_RRF_F0FF }, + { "mayhr", 0x3c, INSTR_RRF_F0FF }, + { "myhr", 0x3d, INSTR_RRF_F0FF }, + { "cegbr", 0xa4, INSTR_RRE_RR }, + { "cdgbr", 0xa5, INSTR_RRE_RR }, + { "cxgbr", 0xa6, INSTR_RRE_RR }, + { "cgebr", 0xa8, INSTR_RRF_U0RF }, + { "cgdbr", 0xa9, INSTR_RRF_U0RF }, + { "cgxbr", 0xaa, INSTR_RRF_U0RF }, + { "cfer", 0xb8, INSTR_RRF_U0RF }, + { "cfdr", 0xb9, INSTR_RRF_U0RF }, + { "cfxr", 0xba, INSTR_RRF_U0RF }, + { "cegr", 0xc4, INSTR_RRE_RR }, + { "cdgr", 0xc5, INSTR_RRE_RR }, + { "cxgr", 0xc6, INSTR_RRE_RR }, + { "cger", 0xc8, INSTR_RRF_U0RF }, + { "cgdr", 0xc9, INSTR_RRF_U0RF }, + { "cgxr", 0xca, INSTR_RRF_U0RF }, +#endif + { "lpebr", 0x00, INSTR_RRE_FF }, + { "lnebr", 0x01, INSTR_RRE_FF }, + { "ltebr", 0x02, INSTR_RRE_FF }, + { "lcebr", 0x03, INSTR_RRE_FF }, + { "ldebr", 0x04, INSTR_RRE_FF }, + { "lxdbr", 0x05, INSTR_RRE_FF }, + { "lxebr", 0x06, INSTR_RRE_FF }, + { "mxdbr", 0x07, INSTR_RRE_FF }, + { "kebr", 0x08, INSTR_RRE_FF }, + { "cebr", 0x09, INSTR_RRE_FF }, + { "aebr", 0x0a, INSTR_RRE_FF }, + { "sebr", 0x0b, INSTR_RRE_FF }, + { "mdebr", 0x0c, INSTR_RRE_FF }, + { "debr", 0x0d, INSTR_RRE_FF }, + { "maebr", 0x0e, INSTR_RRF_F0FF }, + { "msebr", 0x0f, INSTR_RRF_F0FF }, + { "lpdbr", 0x10, INSTR_RRE_FF }, + { "lndbr", 0x11, INSTR_RRE_FF }, + { "ltdbr", 0x12, INSTR_RRE_FF }, + { "lcdbr", 0x13, INSTR_RRE_FF }, + { "sqebr", 0x14, INSTR_RRE_FF }, + { "sqdbr", 0x15, INSTR_RRE_FF }, + { "sqxbr", 0x16, INSTR_RRE_FF }, + { "meebr", 0x17, INSTR_RRE_FF }, + { "kdbr", 0x18, INSTR_RRE_FF }, + { "cdbr", 0x19, INSTR_RRE_FF }, + { "adbr", 0x1a, INSTR_RRE_FF }, + { "sdbr", 0x1b, INSTR_RRE_FF }, + { "mdbr", 0x1c, INSTR_RRE_FF }, + { "ddbr", 0x1d, INSTR_RRE_FF }, + { "madbr", 0x1e, INSTR_RRF_F0FF }, + { "msdbr", 0x1f, INSTR_RRF_F0FF }, + { "lder", 0x24, INSTR_RRE_FF }, + { "lxdr", 0x25, INSTR_RRE_FF }, + { "lxer", 0x26, INSTR_RRE_FF }, + { "maer", 0x2e, INSTR_RRF_F0FF }, + { "mser", 0x2f, INSTR_RRF_F0FF }, + { "sqxr", 0x36, INSTR_RRE_FF }, + { "meer", 0x37, INSTR_RRE_FF }, + { "madr", 0x3e, INSTR_RRF_F0FF }, + { "msdr", 0x3f, INSTR_RRF_F0FF }, + { "lpxbr", 0x40, INSTR_RRE_FF }, + { "lnxbr", 0x41, INSTR_RRE_FF }, + { "ltxbr", 0x42, INSTR_RRE_FF }, + { "lcxbr", 0x43, INSTR_RRE_FF }, + { "ledbr", 0x44, INSTR_RRE_FF }, + { "ldxbr", 0x45, INSTR_RRE_FF }, + { "lexbr", 0x46, INSTR_RRE_FF }, + { "fixbr", 0x47, INSTR_RRF_U0FF }, + { "kxbr", 0x48, INSTR_RRE_FF }, + { "cxbr", 0x49, INSTR_RRE_FF }, + { "axbr", 0x4a, INSTR_RRE_FF }, + { "sxbr", 0x4b, INSTR_RRE_FF }, + { "mxbr", 0x4c, INSTR_RRE_FF }, + { "dxbr", 0x4d, INSTR_RRE_FF }, + { "tbedr", 0x50, INSTR_RRF_U0FF }, + { "tbdr", 0x51, INSTR_RRF_U0FF }, + { "diebr", 0x53, INSTR_RRF_FUFF }, + { "fiebr", 0x57, INSTR_RRF_U0FF }, + { "thder", 0x58, INSTR_RRE_RR }, + { "thdr", 0x59, INSTR_RRE_RR }, + { "didbr", 0x5b, INSTR_RRF_FUFF }, + { "fidbr", 0x5f, INSTR_RRF_U0FF }, + { "lpxr", 0x60, INSTR_RRE_FF }, + { "lnxr", 0x61, INSTR_RRE_FF }, + { "ltxr", 0x62, INSTR_RRE_FF }, + { "lcxr", 0x63, INSTR_RRE_FF }, + { "lxr", 0x65, INSTR_RRE_RR }, + { "lexr", 0x66, INSTR_RRE_FF }, + { "fixr", 0x67, INSTR_RRF_U0FF }, + { "cxr", 0x69, INSTR_RRE_FF }, + { "lzer", 0x74, INSTR_RRE_R0 }, + { "lzdr", 0x75, INSTR_RRE_R0 }, + { "lzxr", 0x76, INSTR_RRE_R0 }, + { "fier", 0x77, INSTR_RRF_U0FF }, + { "fidr", 0x7f, INSTR_RRF_U0FF }, + { "sfpc", 0x84, INSTR_RRE_RR_OPT }, + { "efpc", 0x8c, INSTR_RRE_RR_OPT }, + { "cefbr", 0x94, INSTR_RRE_RF }, + { "cdfbr", 0x95, INSTR_RRE_RF }, + { "cxfbr", 0x96, INSTR_RRE_RF }, + { "cfebr", 0x98, INSTR_RRF_U0RF }, + { "cfdbr", 0x99, INSTR_RRF_U0RF }, + { "cfxbr", 0x9a, INSTR_RRF_U0RF }, + { "cefr", 0xb4, INSTR_RRE_RF }, + { "cdfr", 0xb5, INSTR_RRE_RF }, + { "cxfr", 0xb6, INSTR_RRE_RF }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_b9[] = { +#ifdef CONFIG_64BIT + { "lpgr", 0x00, INSTR_RRE_RR }, + { "lngr", 0x01, INSTR_RRE_RR }, + { "ltgr", 0x02, INSTR_RRE_RR }, + { "lcgr", 0x03, INSTR_RRE_RR }, + { "lgr", 0x04, INSTR_RRE_RR }, + { "lurag", 0x05, INSTR_RRE_RR }, + { "lgbr", 0x06, INSTR_RRE_RR }, + { "lghr", 0x07, INSTR_RRE_RR }, + { "agr", 0x08, INSTR_RRE_RR }, + { "sgr", 0x09, INSTR_RRE_RR }, + { "algr", 0x0a, INSTR_RRE_RR }, + { "slgr", 0x0b, INSTR_RRE_RR }, + { "msgr", 0x0c, INSTR_RRE_RR }, + { "dsgr", 0x0d, INSTR_RRE_RR }, + { "eregg", 0x0e, INSTR_RRE_RR }, + { "lrvgr", 0x0f, INSTR_RRE_RR }, + { "lpgfr", 0x10, INSTR_RRE_RR }, + { "lngfr", 0x11, INSTR_RRE_RR }, + { "ltgfr", 0x12, INSTR_RRE_RR }, + { "lcgfr", 0x13, INSTR_RRE_RR }, + { "lgfr", 0x14, INSTR_RRE_RR }, + { "llgfr", 0x16, INSTR_RRE_RR }, + { "llgtr", 0x17, INSTR_RRE_RR }, + { "agfr", 0x18, INSTR_RRE_RR }, + { "sgfr", 0x19, INSTR_RRE_RR }, + { "algfr", 0x1a, INSTR_RRE_RR }, + { "slgfr", 0x1b, INSTR_RRE_RR }, + { "msgfr", 0x1c, INSTR_RRE_RR }, + { "dsgfr", 0x1d, INSTR_RRE_RR }, + { "cgr", 0x20, INSTR_RRE_RR }, + { "clgr", 0x21, INSTR_RRE_RR }, + { "sturg", 0x25, INSTR_RRE_RR }, + { "lbr", 0x26, INSTR_RRE_RR }, + { "lhr", 0x27, INSTR_RRE_RR }, + { "cgfr", 0x30, INSTR_RRE_RR }, + { "clgfr", 0x31, INSTR_RRE_RR }, + { "bctgr", 0x46, INSTR_RRE_RR }, + { "ngr", 0x80, INSTR_RRE_RR }, + { "ogr", 0x81, INSTR_RRE_RR }, + { "xgr", 0x82, INSTR_RRE_RR }, + { "flogr", 0x83, INSTR_RRE_RR }, + { "llgcr", 0x84, INSTR_RRE_RR }, + { "llghr", 0x85, INSTR_RRE_RR }, + { "mlgr", 0x86, INSTR_RRE_RR }, + { "dlgr", 0x87, INSTR_RRE_RR }, + { "alcgr", 0x88, INSTR_RRE_RR }, + { "slbgr", 0x89, INSTR_RRE_RR }, + { "cspg", 0x8a, INSTR_RRE_RR }, + { "idte", 0x8e, INSTR_RRF_R0RR }, + { "llcr", 0x94, INSTR_RRE_RR }, + { "llhr", 0x95, INSTR_RRE_RR }, + { "esea", 0x9d, INSTR_RRE_R0 }, + { "lptea", 0xaa, INSTR_RRF_RURR }, + { "cu14", 0xb0, INSTR_RRF_M0RR }, + { "cu24", 0xb1, INSTR_RRF_M0RR }, + { "cu41", 0xb2, INSTR_RRF_M0RR }, + { "cu42", 0xb3, INSTR_RRF_M0RR }, +#endif + { "kmac", 0x1e, INSTR_RRE_RR }, + { "lrvr", 0x1f, INSTR_RRE_RR }, + { "km", 0x2e, INSTR_RRE_RR }, + { "kmc", 0x2f, INSTR_RRE_RR }, + { "kimd", 0x3e, INSTR_RRE_RR }, + { "klmd", 0x3f, INSTR_RRE_RR }, + { "epsw", 0x8d, INSTR_RRE_RR }, + { "trtt", 0x90, INSTR_RRE_RR }, + { "trtt", 0x90, INSTR_RRF_M0RR }, + { "trto", 0x91, INSTR_RRE_RR }, + { "trto", 0x91, INSTR_RRF_M0RR }, + { "trot", 0x92, INSTR_RRE_RR }, + { "trot", 0x92, INSTR_RRF_M0RR }, + { "troo", 0x93, INSTR_RRE_RR }, + { "troo", 0x93, INSTR_RRF_M0RR }, + { "mlr", 0x96, INSTR_RRE_RR }, + { "dlr", 0x97, INSTR_RRE_RR }, + { "alcr", 0x98, INSTR_RRE_RR }, + { "slbr", 0x99, INSTR_RRE_RR }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_c0[] = { +#ifdef CONFIG_64BIT + { "lgfi", 0x01, INSTR_RIL_RI }, + { "xihf", 0x06, INSTR_RIL_RU }, + { "xilf", 0x07, INSTR_RIL_RU }, + { "iihf", 0x08, INSTR_RIL_RU }, + { "iilf", 0x09, INSTR_RIL_RU }, + { "nihf", 0x0a, INSTR_RIL_RU }, + { "nilf", 0x0b, INSTR_RIL_RU }, + { "oihf", 0x0c, INSTR_RIL_RU }, + { "oilf", 0x0d, INSTR_RIL_RU }, + { "llihf", 0x0e, INSTR_RIL_RU }, + { "llilf", 0x0f, INSTR_RIL_RU }, +#endif + { "larl", 0x00, INSTR_RIL_RP }, + { "brcl", 0x04, INSTR_RIL_UP }, + { "brasl", 0x05, INSTR_RIL_RP }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_c2[] = { +#ifdef CONFIG_64BIT + { "slgfi", 0x04, INSTR_RIL_RU }, + { "slfi", 0x05, INSTR_RIL_RU }, + { "agfi", 0x08, INSTR_RIL_RI }, + { "afi", 0x09, INSTR_RIL_RI }, + { "algfi", 0x0a, INSTR_RIL_RU }, + { "alfi", 0x0b, INSTR_RIL_RU }, + { "cgfi", 0x0c, INSTR_RIL_RI }, + { "cfi", 0x0d, INSTR_RIL_RI }, + { "clgfi", 0x0e, INSTR_RIL_RU }, + { "clfi", 0x0f, INSTR_RIL_RU }, +#endif + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_c8[] = { +#ifdef CONFIG_64BIT + { "mvcos", 0x00, INSTR_SSF_RRDRD }, +#endif + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_e3[] = { +#ifdef CONFIG_64BIT + { "ltg", 0x02, INSTR_RXY_RRRD }, + { "lrag", 0x03, INSTR_RXY_RRRD }, + { "lg", 0x04, INSTR_RXY_RRRD }, + { "cvby", 0x06, INSTR_RXY_RRRD }, + { "ag", 0x08, INSTR_RXY_RRRD }, + { "sg", 0x09, INSTR_RXY_RRRD }, + { "alg", 0x0a, INSTR_RXY_RRRD }, + { "slg", 0x0b, INSTR_RXY_RRRD }, + { "msg", 0x0c, INSTR_RXY_RRRD }, + { "dsg", 0x0d, INSTR_RXY_RRRD }, + { "cvbg", 0x0e, INSTR_RXY_RRRD }, + { "lrvg", 0x0f, INSTR_RXY_RRRD }, + { "lt", 0x12, INSTR_RXY_RRRD }, + { "lray", 0x13, INSTR_RXY_RRRD }, + { "lgf", 0x14, INSTR_RXY_RRRD }, + { "lgh", 0x15, INSTR_RXY_RRRD }, + { "llgf", 0x16, INSTR_RXY_RRRD }, + { "llgt", 0x17, INSTR_RXY_RRRD }, + { "agf", 0x18, INSTR_RXY_RRRD }, + { "sgf", 0x19, INSTR_RXY_RRRD }, + { "algf", 0x1a, INSTR_RXY_RRRD }, + { "slgf", 0x1b, INSTR_RXY_RRRD }, + { "msgf", 0x1c, INSTR_RXY_RRRD }, + { "dsgf", 0x1d, INSTR_RXY_RRRD }, + { "cg", 0x20, INSTR_RXY_RRRD }, + { "clg", 0x21, INSTR_RXY_RRRD }, + { "stg", 0x24, INSTR_RXY_RRRD }, + { "cvdy", 0x26, INSTR_RXY_RRRD }, + { "cvdg", 0x2e, INSTR_RXY_RRRD }, + { "strvg", 0x2f, INSTR_RXY_RRRD }, + { "cgf", 0x30, INSTR_RXY_RRRD }, + { "clgf", 0x31, INSTR_RXY_RRRD }, + { "strvh", 0x3f, INSTR_RXY_RRRD }, + { "bctg", 0x46, INSTR_RXY_RRRD }, + { "sty", 0x50, INSTR_RXY_RRRD }, + { "msy", 0x51, INSTR_RXY_RRRD }, + { "ny", 0x54, INSTR_RXY_RRRD }, + { "cly", 0x55, INSTR_RXY_RRRD }, + { "oy", 0x56, INSTR_RXY_RRRD }, + { "xy", 0x57, INSTR_RXY_RRRD }, + { "ly", 0x58, INSTR_RXY_RRRD }, + { "cy", 0x59, INSTR_RXY_RRRD }, + { "ay", 0x5a, INSTR_RXY_RRRD }, + { "sy", 0x5b, INSTR_RXY_RRRD }, + { "aly", 0x5e, INSTR_RXY_RRRD }, + { "sly", 0x5f, INSTR_RXY_RRRD }, + { "sthy", 0x70, INSTR_RXY_RRRD }, + { "lay", 0x71, INSTR_RXY_RRRD }, + { "stcy", 0x72, INSTR_RXY_RRRD }, + { "icy", 0x73, INSTR_RXY_RRRD }, + { "lb", 0x76, INSTR_RXY_RRRD }, + { "lgb", 0x77, INSTR_RXY_RRRD }, + { "lhy", 0x78, INSTR_RXY_RRRD }, + { "chy", 0x79, INSTR_RXY_RRRD }, + { "ahy", 0x7a, INSTR_RXY_RRRD }, + { "shy", 0x7b, INSTR_RXY_RRRD }, + { "ng", 0x80, INSTR_RXY_RRRD }, + { "og", 0x81, INSTR_RXY_RRRD }, + { "xg", 0x82, INSTR_RXY_RRRD }, + { "mlg", 0x86, INSTR_RXY_RRRD }, + { "dlg", 0x87, INSTR_RXY_RRRD }, + { "alcg", 0x88, INSTR_RXY_RRRD }, + { "slbg", 0x89, INSTR_RXY_RRRD }, + { "stpq", 0x8e, INSTR_RXY_RRRD }, + { "lpq", 0x8f, INSTR_RXY_RRRD }, + { "llgc", 0x90, INSTR_RXY_RRRD }, + { "llgh", 0x91, INSTR_RXY_RRRD }, + { "llc", 0x94, INSTR_RXY_RRRD }, + { "llh", 0x95, INSTR_RXY_RRRD }, +#endif + { "lrv", 0x1e, INSTR_RXY_RRRD }, + { "lrvh", 0x1f, INSTR_RXY_RRRD }, + { "strv", 0x3e, INSTR_RXY_RRRD }, + { "ml", 0x96, INSTR_RXY_RRRD }, + { "dl", 0x97, INSTR_RXY_RRRD }, + { "alc", 0x98, INSTR_RXY_RRRD }, + { "slb", 0x99, INSTR_RXY_RRRD }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_e5[] = { +#ifdef CONFIG_64BIT + { "strag", 0x02, INSTR_SSE_RDRD }, +#endif + { "lasp", 0x00, INSTR_SSE_RDRD }, + { "tprot", 0x01, INSTR_SSE_RDRD }, + { "mvcsk", 0x0e, INSTR_SSE_RDRD }, + { "mvcdk", 0x0f, INSTR_SSE_RDRD }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_eb[] = { +#ifdef CONFIG_64BIT + { "lmg", 0x04, INSTR_RSY_RRRD }, + { "srag", 0x0a, INSTR_RSY_RRRD }, + { "slag", 0x0b, INSTR_RSY_RRRD }, + { "srlg", 0x0c, INSTR_RSY_RRRD }, + { "sllg", 0x0d, INSTR_RSY_RRRD }, + { "tracg", 0x0f, INSTR_RSY_RRRD }, + { "csy", 0x14, INSTR_RSY_RRRD }, + { "rllg", 0x1c, INSTR_RSY_RRRD }, + { "clmh", 0x20, INSTR_RSY_RURD }, + { "clmy", 0x21, INSTR_RSY_RURD }, + { "stmg", 0x24, INSTR_RSY_RRRD }, + { "stctg", 0x25, INSTR_RSY_CCRD }, + { "stmh", 0x26, INSTR_RSY_RRRD }, + { "stcmh", 0x2c, INSTR_RSY_RURD }, + { "stcmy", 0x2d, INSTR_RSY_RURD }, + { "lctlg", 0x2f, INSTR_RSY_CCRD }, + { "csg", 0x30, INSTR_RSY_RRRD }, + { "cdsy", 0x31, INSTR_RSY_RRRD }, + { "cdsg", 0x3e, INSTR_RSY_RRRD }, + { "bxhg", 0x44, INSTR_RSY_RRRD }, + { "bxleg", 0x45, INSTR_RSY_RRRD }, + { "tmy", 0x51, INSTR_SIY_URD }, + { "mviy", 0x52, INSTR_SIY_URD }, + { "niy", 0x54, INSTR_SIY_URD }, + { "cliy", 0x55, INSTR_SIY_URD }, + { "oiy", 0x56, INSTR_SIY_URD }, + { "xiy", 0x57, INSTR_SIY_URD }, + { "icmh", 0x80, INSTR_RSE_RURD }, + { "icmh", 0x80, INSTR_RSY_RURD }, + { "icmy", 0x81, INSTR_RSY_RURD }, + { "clclu", 0x8f, INSTR_RSY_RRRD }, + { "stmy", 0x90, INSTR_RSY_RRRD }, + { "lmh", 0x96, INSTR_RSY_RRRD }, + { "lmy", 0x98, INSTR_RSY_RRRD }, + { "lamy", 0x9a, INSTR_RSY_AARD }, + { "stamy", 0x9b, INSTR_RSY_AARD }, +#endif + { "rll", 0x1d, INSTR_RSY_RRRD }, + { "mvclu", 0x8e, INSTR_RSY_RRRD }, + { "tp", 0xc0, INSTR_RSL_R0RD }, + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_ec[] = { +#ifdef CONFIG_64BIT + { "brxhg", 0x44, INSTR_RIE_RRP }, + { "brxlg", 0x45, INSTR_RIE_RRP }, +#endif + { "", 0, INSTR_INVALID } +}; + +static struct insn opcode_ed[] = { +#ifdef CONFIG_64BIT + { "mayl", 0x38, INSTR_RXF_FRRDF }, + { "myl", 0x39, INSTR_RXF_FRRDF }, + { "may", 0x3a, INSTR_RXF_FRRDF }, + { "my", 0x3b, INSTR_RXF_FRRDF }, + { "mayh", 0x3c, INSTR_RXF_FRRDF }, + { "myh", 0x3d, INSTR_RXF_FRRDF }, + { "ley", 0x64, INSTR_RXY_FRRD }, + { "ldy", 0x65, INSTR_RXY_FRRD }, + { "stey", 0x66, INSTR_RXY_FRRD }, + { "stdy", 0x67, INSTR_RXY_FRRD }, +#endif + { "ldeb", 0x04, INSTR_RXE_FRRD }, + { "lxdb", 0x05, INSTR_RXE_FRRD }, + { "lxeb", 0x06, INSTR_RXE_FRRD }, + { "mxdb", 0x07, INSTR_RXE_FRRD }, + { "keb", 0x08, INSTR_RXE_FRRD }, + { "ceb", 0x09, INSTR_RXE_FRRD }, + { "aeb", 0x0a, INSTR_RXE_FRRD }, + { "seb", 0x0b, INSTR_RXE_FRRD }, + { "mdeb", 0x0c, INSTR_RXE_FRRD }, + { "deb", 0x0d, INSTR_RXE_FRRD }, + { "maeb", 0x0e, INSTR_RXF_FRRDF }, + { "mseb", 0x0f, INSTR_RXF_FRRDF }, + { "tceb", 0x10, INSTR_RXE_FRRD }, + { "tcdb", 0x11, INSTR_RXE_FRRD }, + { "tcxb", 0x12, INSTR_RXE_FRRD }, + { "sqeb", 0x14, INSTR_RXE_FRRD }, + { "sqdb", 0x15, INSTR_RXE_FRRD }, + { "meeb", 0x17, INSTR_RXE_FRRD }, + { "kdb", 0x18, INSTR_RXE_FRRD }, + { "cdb", 0x19, INSTR_RXE_FRRD }, + { "adb", 0x1a, INSTR_RXE_FRRD }, + { "sdb", 0x1b, INSTR_RXE_FRRD }, + { "mdb", 0x1c, INSTR_RXE_FRRD }, + { "ddb", 0x1d, INSTR_RXE_FRRD }, + { "madb", 0x1e, INSTR_RXF_FRRDF }, + { "msdb", 0x1f, INSTR_RXF_FRRDF }, + { "lde", 0x24, INSTR_RXE_FRRD }, + { "lxd", 0x25, INSTR_RXE_FRRD }, + { "lxe", 0x26, INSTR_RXE_FRRD }, + { "mae", 0x2e, INSTR_RXF_FRRDF }, + { "mse", 0x2f, INSTR_RXF_FRRDF }, + { "sqe", 0x34, INSTR_RXE_FRRD }, + { "mee", 0x37, INSTR_RXE_FRRD }, + { "mad", 0x3e, INSTR_RXF_FRRDF }, + { "msd", 0x3f, INSTR_RXF_FRRDF }, + { "", 0, INSTR_INVALID } +}; + +/* Extracts an operand value from an instruction. */ +static unsigned int extract_operand(unsigned char *code, + const struct operand *operand) +{ + unsigned int val; + int bits; + + /* Extract fragments of the operand byte for byte. */ + code += operand->shift / 8; + bits = (operand->shift & 7) + operand->bits; + val = 0; + do { + val <<= 8; + val |= (unsigned int) *code++; + bits -= 8; + } while (bits > 0); + val >>= -bits; + val &= ((1U << (operand->bits - 1)) << 1) - 1; + + /* Check for special long displacement case. */ + if (operand->bits == 20 && operand->shift == 20) + val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; + + /* Sign extend value if the operand is signed or pc relative. */ + if ((operand->flags & (OPERAND_SIGNED | OPERAND_PCREL)) && + (val & (1U << (operand->bits - 1)))) + val |= (-1U << (operand->bits - 1)) << 1; + + /* Double value if the operand is pc relative. */ + if (operand->flags & OPERAND_PCREL) + val <<= 1; + + /* Length x in an instructions has real length x + 1. */ + if (operand->flags & OPERAND_LENGTH) + val++; + return val; +} + +static inline int insn_length(unsigned char code) +{ + return ((((int) code + 64) >> 7) + 1) << 1; +} + +static struct insn *find_insn(unsigned char *code) +{ + unsigned char opfrag = code[1]; + unsigned char opmask; + struct insn *table; + + switch (code[0]) { + case 0x01: + table = opcode_01; + break; + case 0xa5: + table = opcode_a5; + break; + case 0xa7: + table = opcode_a7; + break; + case 0xb2: + table = opcode_b2; + break; + case 0xb3: + table = opcode_b3; + break; + case 0xb9: + table = opcode_b9; + break; + case 0xc0: + table = opcode_c0; + break; + case 0xc2: + table = opcode_c2; + break; + case 0xc8: + table = opcode_c8; + break; + case 0xe3: + table = opcode_e3; + opfrag = code[5]; + break; + case 0xe5: + table = opcode_e5; + break; + case 0xeb: + table = opcode_eb; + opfrag = code[5]; + break; + case 0xec: + table = opcode_ec; + opfrag = code[5]; + break; + case 0xed: + table = opcode_ed; + opfrag = code[5]; + break; + default: + table = opcode; + opfrag = code[0]; + break; + } + while (table->format != INSTR_INVALID) { + opmask = formats[table->format][0]; + if (table->opfrag == (opfrag & opmask)) + return table; + table++; + } + return NULL; +} + +static int print_insn(char *buffer, unsigned char *code, unsigned long addr) +{ + struct insn *insn; + const unsigned char *ops; + const struct operand *operand; + unsigned int value; + char separator; + char *ptr; + + ptr = buffer; + insn = find_insn(code); + if (insn) { + ptr += sprintf(ptr, "%.5s\t", insn->name); + /* Extract the operands. */ + separator = 0; + for (ops = formats[insn->format] + 1; *ops != 0; ops++) { + operand = operands + *ops; + value = extract_operand(code, operand); + if ((operand->flags & OPERAND_INDEX) && value == 0) + continue; + if ((operand->flags & OPERAND_BASE) && + value == 0 && separator == '(') { + separator = ','; + continue; + } + if (separator) + ptr += sprintf(ptr, "%c", separator); + if (operand->flags & OPERAND_GPR) + ptr += sprintf(ptr, "%%r%i", value); + else if (operand->flags & OPERAND_FPR) + ptr += sprintf(ptr, "%%f%i", value); + else if (operand->flags & OPERAND_AR) + ptr += sprintf(ptr, "%%a%i", value); + else if (operand->flags & OPERAND_CR) + ptr += sprintf(ptr, "%%c%i", value); + else if (operand->flags & OPERAND_PCREL) + ptr += sprintf(ptr, "%lx", value + addr); + else if (operand->flags & OPERAND_SIGNED) + ptr += sprintf(ptr, "%i", value); + else + ptr += sprintf(ptr, "%u", value); + if (operand->flags & OPERAND_DISP) + separator = '('; + else if (operand->flags & OPERAND_BASE) { + ptr += sprintf(ptr, ")"); + separator = ','; + } else + separator = ','; + } + } else + ptr += sprintf(ptr, "unknown"); + return (int) (ptr - buffer); +} + +void show_code(struct pt_regs *regs) +{ + char *mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl"; + unsigned char code[64]; + char buffer[64], *ptr; + mm_segment_t old_fs; + unsigned long addr; + int start, end, opsize, hops, i; + + /* Get a snapshot of the 64 bytes surrounding the fault address. */ + old_fs = get_fs(); + set_fs((regs->psw.mask & PSW_MASK_PSTATE) ? USER_DS : KERNEL_DS); + for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) { + addr = regs->psw.addr - 34 + start; + if (__copy_from_user(code + start - 2, + (char __user *) addr, 2)) + break; + } + for (end = 32; end < 64; end += 2) { + addr = regs->psw.addr + end - 32; + if (__copy_from_user(code + end, + (char __user *) addr, 2)) + break; + } + set_fs(old_fs); + /* Code snapshot useable ? */ + if ((regs->psw.addr & 1) || start >= end) { + printk("%s Code: Bad PSW.\n", mode); + return; + } + /* Find a starting point for the disassembly. */ + while (start < 32) { + hops = 0; + for (i = 0, hops = 0; start + i < 32 && hops < 3; hops++) { + if (!find_insn(code + start + i)) + break; + i += insn_length(code[start + i]); + } + if (start + i == 32) + /* Looks good, sequence ends at PSW. */ + break; + start += 2; + } + /* Decode the instructions. */ + ptr = buffer; + ptr += sprintf(ptr, "%s Code:", mode); + hops = 0; + while (start < end && hops < 8) { + *ptr++ = (start == 32) ? '>' : ' '; + addr = regs->psw.addr + start - 32; + ptr += sprintf(ptr, ONELONG, addr); + opsize = insn_length(code[start]); + if (start + opsize >= end) + break; + for (i = 0; i < opsize; i++) + ptr += sprintf(ptr, "%02x", code[start + i]); + *ptr++ = '\t'; + if (i < 6) + *ptr++ = '\t'; + ptr += print_insn(ptr, code + start, addr); + start += opsize; + printk(buffer); + ptr = buffer; + ptr += sprintf(ptr, "\n "); + hops++; + } + printk("\n"); +} diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 5e47936..50538e5 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -253,11 +253,10 @@ #ifndef CONFIG_64BIT break; #endif /* - * Finish memory detection at the first hole, unless - * - we reached the hsa -> skip it. - * - we know there must be more. + * Finish memory detection at the first hole + * if storage size is unknown. */ - if (cc == -1UL && !memsize && old_addr != ADDR2G) + if (cc == -1UL && !memsize) break; if (memsize && addr >= memsize) break; diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index dddc3de..c8a2212 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -249,8 +249,6 @@ sysc_do_restart: bnz BASED(sysc_tracesys) basr %r14,%r8 # call sys_xxxx st %r2,SP_R2(%r15) # store return value (change R2 on stack) - # ATTENTION: check sys_execve_glue before - # changing anything here !! sysc_return: tm SP_PSW+1(%r15),0x01 # returning to user ? @@ -381,50 +379,37 @@ ret_from_fork: b BASED(sysc_return) # -# clone, fork, vfork, exec and sigreturn need glue, -# because they all expect pt_regs as parameter, -# but are called with different parameter. -# return-address is set up above +# kernel_execve function needs to deal with pt_regs that is not +# at the usual place # -sys_clone_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,BASED(.Lclone) - br %r1 # branch to sys_clone - -sys_fork_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,BASED(.Lfork) - br %r1 # branch to sys_fork - -sys_vfork_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,BASED(.Lvfork) - br %r1 # branch to sys_vfork - -sys_execve_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,BASED(.Lexecve) - lr %r12,%r14 # save return address - basr %r14,%r1 # call sys_execve - ltr %r2,%r2 # check if execve failed - bnz 0(%r12) # it did fail -> store result in gpr2 - b 4(%r12) # SKIP ST 2,SP_R2(15) after BASR 14,8 - # in system_call/sysc_tracesys - -sys_sigreturn_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - l %r1,BASED(.Lsigreturn) - br %r1 # branch to sys_sigreturn - -sys_rt_sigreturn_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - l %r1,BASED(.Lrt_sigreturn) - br %r1 # branch to sys_sigreturn - -sys_sigaltstack_glue: - la %r4,SP_PTREGS(%r15) # load pt_regs as parameter - l %r1,BASED(.Lsigaltstack) - br %r1 # branch to sys_sigreturn + .globl kernel_execve +kernel_execve: + stm %r12,%r15,48(%r15) + lr %r14,%r15 + l %r13,__LC_SVC_NEW_PSW+4 + s %r15,BASED(.Lc_spsize) + st %r14,__SF_BACKCHAIN(%r15) + la %r12,SP_PTREGS(%r15) + xc 0(__PT_SIZE,%r12),0(%r12) + l %r1,BASED(.Ldo_execve) + lr %r5,%r12 + basr %r14,%r1 + ltr %r2,%r2 + be BASED(0f) + a %r15,BASED(.Lc_spsize) + lm %r12,%r15,48(%r15) + br %r14 + # execve succeeded. +0: stnsm __SF_EMPTY(%r15),0xfc # disable interrupts + l %r15,__LC_KERNEL_STACK # load ksp + s %r15,BASED(.Lc_spsize) # make room for registers & psw + l %r9,__LC_THREAD_INFO + mvc SP_PTREGS(__PT_SIZE,%r15),0(%r12) # copy pt_regs + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + l %r1,BASED(.Lexecve_tail) + basr %r14,%r1 + b BASED(sysc_return) /* * Program check handler routine @@ -1031,19 +1016,11 @@ #endif .Ldo_extint: .long do_extint .Ldo_signal: .long do_signal .Lhandle_per: .long do_single_step +.Ldo_execve: .long do_execve +.Lexecve_tail: .long execve_tail .Ljump_table: .long pgm_check_table .Lschedule: .long schedule -.Lclone: .long sys_clone -.Lexecve: .long sys_execve -.Lfork: .long sys_fork -.Lrt_sigreturn: .long sys_rt_sigreturn -.Lrt_sigsuspend: - .long sys_rt_sigsuspend -.Lsigreturn: .long sys_sigreturn -.Lsigsuspend: .long sys_sigsuspend -.Lsigaltstack: .long sys_sigaltstack .Ltrace: .long syscall_trace -.Lvfork: .long sys_vfork .Lschedtail: .long schedule_tail .Lsysc_table: .long sys_call_table #ifdef CONFIG_TRACE_IRQFLAGS diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 0f758c3..93745fd 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -244,8 +244,6 @@ #endif jnz sysc_tracesys basr %r14,%r8 # call sys_xxxx stg %r2,SP_R2(%r15) # store return value (change R2 on stack) - # ATTENTION: check sys_execve_glue before - # changing anything here !! sysc_return: tm SP_PSW+1(%r15),0x01 # returning to user ? @@ -371,77 +369,35 @@ ret_from_fork: j sysc_return # -# clone, fork, vfork, exec and sigreturn need glue, -# because they all expect pt_regs as parameter, -# but are called with different parameter. -# return-address is set up above +# kernel_execve function needs to deal with pt_regs that is not +# at the usual place # -sys_clone_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - jg sys_clone # branch to sys_clone - -#ifdef CONFIG_COMPAT -sys32_clone_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - jg sys32_clone # branch to sys32_clone -#endif - -sys_fork_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - jg sys_fork # branch to sys_fork - -sys_vfork_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - jg sys_vfork # branch to sys_vfork - -sys_execve_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - lgr %r12,%r14 # save return address - brasl %r14,sys_execve # call sys_execve - ltgr %r2,%r2 # check if execve failed - bnz 0(%r12) # it did fail -> store result in gpr2 - b 6(%r12) # SKIP STG 2,SP_R2(15) in - # system_call/sysc_tracesys -#ifdef CONFIG_COMPAT -sys32_execve_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs - lgr %r12,%r14 # save return address - brasl %r14,sys32_execve # call sys32_execve - ltgr %r2,%r2 # check if execve failed - bnz 0(%r12) # it did fail -> store result in gpr2 - b 6(%r12) # SKIP STG 2,SP_R2(15) in - # system_call/sysc_tracesys -#endif - -sys_sigreturn_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - jg sys_sigreturn # branch to sys_sigreturn - -#ifdef CONFIG_COMPAT -sys32_sigreturn_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - jg sys32_sigreturn # branch to sys32_sigreturn -#endif - -sys_rt_sigreturn_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - jg sys_rt_sigreturn # branch to sys_sigreturn - -#ifdef CONFIG_COMPAT -sys32_rt_sigreturn_glue: - la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - jg sys32_rt_sigreturn # branch to sys32_sigreturn -#endif - -sys_sigaltstack_glue: - la %r4,SP_PTREGS(%r15) # load pt_regs as parameter - jg sys_sigaltstack # branch to sys_sigreturn - -#ifdef CONFIG_COMPAT -sys32_sigaltstack_glue: - la %r4,SP_PTREGS(%r15) # load pt_regs as parameter - jg sys32_sigaltstack_wrapper # branch to sys_sigreturn -#endif + .globl kernel_execve +kernel_execve: + stmg %r12,%r15,96(%r15) + lgr %r14,%r15 + aghi %r15,-SP_SIZE + stg %r14,__SF_BACKCHAIN(%r15) + la %r12,SP_PTREGS(%r15) + xc 0(__PT_SIZE,%r12),0(%r12) + lgr %r5,%r12 + brasl %r14,do_execve + ltgfr %r2,%r2 + je 0f + aghi %r15,SP_SIZE + lmg %r12,%r15,96(%r15) + br %r14 + # execve succeeded. +0: stnsm __SF_EMPTY(%r15),0xfc # disable interrupts + lg %r15,__LC_KERNEL_STACK # load ksp + aghi %r15,-SP_SIZE # make room for registers & psw + lg %r13,__LC_SVC_NEW_PSW+8 + lg %r9,__LC_THREAD_INFO + mvc SP_PTREGS(__PT_SIZE,%r15),0(%r12) # copy pt_regs + xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + brasl %r14,execve_tail + j sysc_return /* * Program check handler routine diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 3701070..a87b197 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -39,7 +39,69 @@ startup_continue: basr %r13,0 # get base .LPG1: sll %r13,1 # remove high order bit srl %r13,1 - lhi %r1,1 # mode 1 = esame + +#ifdef CONFIG_ZFCPDUMP + + # check if we have been ipled using zfcp dump: + + tm 0xb9,0x01 # test if subchannel is enabled + jno .nodump # subchannel disabled + l %r1,0xb8 + la %r5,.Lipl_schib-.LPG1(%r13) + stsch 0(%r5) # get schib of subchannel + jne .nodump # schib not available + tm 5(%r5),0x01 # devno valid? + jno .nodump + tm 4(%r5),0x80 # qdio capable device? + jno .nodump + l %r2,20(%r0) # address of ipl parameter block + lhi %r3,0 + ic %r3,0x148(%r2) # get opt field + chi %r3,0x20 # load with dump? + jne .nodump + + # store all prefix registers in case of load with dump: + + la %r7,0 # base register for 0 page + la %r8,0 # first cpu + l %r11,.Lpref_arr_ptr-.LPG1(%r13) # address of prefix array + ahi %r11,4 # skip boot cpu + lr %r12,%r11 + ahi %r12,(CONFIG_NR_CPUS*4) # end of prefix array + stap .Lcurrent_cpu+2-.LPG1(%r13) # store current cpu addr +1: + cl %r8,.Lcurrent_cpu-.LPG1(%r13) # is ipl cpu ? + je 4f # if yes get next cpu +2: + lr %r9,%r7 + sigp %r9,%r8,0x9 # stop & store status of cpu + brc 8,3f # accepted + brc 4,4f # status stored: next cpu + brc 2,2b # busy: try again + brc 1,4f # not op: next cpu +3: + mvc 0(4,%r11),264(%r7) # copy prefix register to prefix array + ahi %r11,4 # next element in prefix array + clr %r11,%r12 + je 5f # no more space in prefix array +4: + ahi %r8,1 # next cpu (r8 += 1) + cl %r8,.Llast_cpu-.LPG1(%r13) # is last possible cpu ? + jl 1b # jump if not last cpu +5: + lhi %r1,2 # mode 2 = esame (dump) + j 6f + .align 4 +.Lipl_schib: + .rept 13 + .long 0 + .endr +.nodump: + lhi %r1,1 # mode 1 = esame (normal ipl) +6: +#else + lhi %r1,1 # mode 1 = esame (normal ipl) +#endif /* CONFIG_ZFCPDUMP */ mvi __LC_AR_MODE_ID,1 # set esame flag slr %r0,%r0 # set cpuid to zero sigp %r1,%r0,0x12 # switch to esame mode @@ -149,6 +211,14 @@ # .L4malign:.quad 0xffffffffffc00000 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 .Lnop: .long 0x07000700 +#ifdef CONFIG_ZFCPDUMP +.Lcurrent_cpu: + .long 0x0 +.Llast_cpu: + .long 0x0000ffff +.Lpref_arr_ptr: + .long zfcpdump_prefix_array +#endif /* CONFIG_ZFCPDUMP */ .Lparmaddr: .quad PARMAREA .align 64 diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index f731185..06833ac 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -29,36 +29,21 @@ #define SCCB_VALID (s390_readinfo_sccb.h #define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm) #define SCCB_FLAG (s390_readinfo_sccb.flags) -enum ipl_type { - IPL_TYPE_NONE = 1, - IPL_TYPE_UNKNOWN = 2, - IPL_TYPE_CCW = 4, - IPL_TYPE_FCP = 8, - IPL_TYPE_NSS = 16, -}; - -#define IPL_NONE_STR "none" -#define IPL_UNKNOWN_STR "unknown" -#define IPL_CCW_STR "ccw" -#define IPL_FCP_STR "fcp" -#define IPL_NSS_STR "nss" - -/* - * Must be in data section since the bss section - * is not cleared when these are accessed. - */ -u16 ipl_devno __attribute__((__section__(".data"))) = 0; -u32 ipl_flags __attribute__((__section__(".data"))) = 0; +#define IPL_UNKNOWN_STR "unknown" +#define IPL_CCW_STR "ccw" +#define IPL_FCP_STR "fcp" +#define IPL_FCP_DUMP_STR "fcp_dump" +#define IPL_NSS_STR "nss" static char *ipl_type_str(enum ipl_type type) { switch (type) { - case IPL_TYPE_NONE: - return IPL_NONE_STR; case IPL_TYPE_CCW: return IPL_CCW_STR; case IPL_TYPE_FCP: return IPL_FCP_STR; + case IPL_TYPE_FCP_DUMP: + return IPL_FCP_DUMP_STR; case IPL_TYPE_NSS: return IPL_NSS_STR; case IPL_TYPE_UNKNOWN: @@ -67,15 +52,55 @@ static char *ipl_type_str(enum ipl_type } } +enum dump_type { + DUMP_TYPE_NONE = 1, + DUMP_TYPE_CCW = 2, + DUMP_TYPE_FCP = 4, +}; + +#define DUMP_NONE_STR "none" +#define DUMP_CCW_STR "ccw" +#define DUMP_FCP_STR "fcp" + +static char *dump_type_str(enum dump_type type) +{ + switch (type) { + case DUMP_TYPE_NONE: + return DUMP_NONE_STR; + case DUMP_TYPE_CCW: + return DUMP_CCW_STR; + case DUMP_TYPE_FCP: + return DUMP_FCP_STR; + default: + return NULL; + } +} + +/* + * Must be in data section since the bss section + * is not cleared when these are accessed. + */ +static u16 ipl_devno __attribute__((__section__(".data"))) = 0; +u32 ipl_flags __attribute__((__section__(".data"))) = 0; + enum ipl_method { - IPL_METHOD_NONE, - IPL_METHOD_CCW_CIO, - IPL_METHOD_CCW_DIAG, - IPL_METHOD_CCW_VM, - IPL_METHOD_FCP_RO_DIAG, - IPL_METHOD_FCP_RW_DIAG, - IPL_METHOD_FCP_RO_VM, - IPL_METHOD_NSS, + REIPL_METHOD_CCW_CIO, + REIPL_METHOD_CCW_DIAG, + REIPL_METHOD_CCW_VM, + REIPL_METHOD_FCP_RO_DIAG, + REIPL_METHOD_FCP_RW_DIAG, + REIPL_METHOD_FCP_RO_VM, + REIPL_METHOD_FCP_DUMP, + REIPL_METHOD_NSS, + REIPL_METHOD_DEFAULT, +}; + +enum dump_method { + DUMP_METHOD_NONE, + DUMP_METHOD_CCW_CIO, + DUMP_METHOD_CCW_DIAG, + DUMP_METHOD_CCW_VM, + DUMP_METHOD_FCP_DIAG, }; enum shutdown_action { @@ -107,15 +132,15 @@ static int diag308_set_works = 0; static int reipl_capabilities = IPL_TYPE_UNKNOWN; static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; -static enum ipl_method reipl_method = IPL_METHOD_NONE; +static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; static struct ipl_parameter_block *reipl_block_fcp; static struct ipl_parameter_block *reipl_block_ccw; static char reipl_nss_name[NSS_NAME_SIZE + 1]; -static int dump_capabilities = IPL_TYPE_NONE; -static enum ipl_type dump_type = IPL_TYPE_NONE; -static enum ipl_method dump_method = IPL_METHOD_NONE; +static int dump_capabilities = DUMP_TYPE_NONE; +static enum dump_type dump_type = DUMP_TYPE_NONE; +static enum dump_method dump_method = DUMP_METHOD_NONE; static struct ipl_parameter_block *dump_block_fcp; static struct ipl_parameter_block *dump_block_ccw; @@ -134,6 +159,7 @@ int diag308(unsigned long subcode, void : "d" (subcode) : "cc", "memory"); return _rc; } +EXPORT_SYMBOL_GPL(diag308); /* SYSFS */ @@ -197,7 +223,7 @@ static void make_attrs_ro(struct attribu * ipl section */ -static enum ipl_type ipl_get_type(void) +static __init enum ipl_type get_ipl_type(void) { struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; @@ -211,12 +237,44 @@ static enum ipl_type ipl_get_type(void) return IPL_TYPE_UNKNOWN; if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP) return IPL_TYPE_UNKNOWN; + if (ipl->ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP) + return IPL_TYPE_FCP_DUMP; return IPL_TYPE_FCP; } +void __init setup_ipl_info(void) +{ + ipl_info.type = get_ipl_type(); + switch (ipl_info.type) { + case IPL_TYPE_CCW: + ipl_info.data.ccw.dev_id.devno = ipl_devno; + ipl_info.data.ccw.dev_id.ssid = 0; + break; + case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: + ipl_info.data.fcp.dev_id.devno = + IPL_PARMBLOCK_START->ipl_info.fcp.devno; + ipl_info.data.fcp.dev_id.ssid = 0; + ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; + ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; + break; + case IPL_TYPE_NSS: + strncpy(ipl_info.data.nss.name, kernel_nss_name, + sizeof(ipl_info.data.nss.name)); + break; + case IPL_TYPE_UNKNOWN: + default: + /* We have no info to copy */ + break; + } +} + +struct ipl_info ipl_info; +EXPORT_SYMBOL_GPL(ipl_info); + static ssize_t ipl_type_show(struct subsystem *subsys, char *page) { - return sprintf(page, "%s\n", ipl_type_str(ipl_get_type())); + return sprintf(page, "%s\n", ipl_type_str(ipl_info.type)); } static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); @@ -225,10 +283,11 @@ static ssize_t sys_ipl_device_show(struc { struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; - switch (ipl_get_type()) { + switch (ipl_info.type) { case IPL_TYPE_CCW: return sprintf(page, "0.0.%04x\n", ipl_devno); case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno); default: return 0; @@ -485,23 +544,29 @@ static int reipl_set_type(enum ipl_type switch(type) { case IPL_TYPE_CCW: if (MACHINE_IS_VM) - reipl_method = IPL_METHOD_CCW_VM; + reipl_method = REIPL_METHOD_CCW_VM; else - reipl_method = IPL_METHOD_CCW_CIO; + reipl_method = REIPL_METHOD_CCW_CIO; break; case IPL_TYPE_FCP: if (diag308_set_works) - reipl_method = IPL_METHOD_FCP_RW_DIAG; + reipl_method = REIPL_METHOD_FCP_RW_DIAG; else if (MACHINE_IS_VM) - reipl_method = IPL_METHOD_FCP_RO_VM; + reipl_method = REIPL_METHOD_FCP_RO_VM; else - reipl_method = IPL_METHOD_FCP_RO_DIAG; + reipl_method = REIPL_METHOD_FCP_RO_DIAG; + break; + case IPL_TYPE_FCP_DUMP: + reipl_method = REIPL_METHOD_FCP_DUMP; break; case IPL_TYPE_NSS: - reipl_method = IPL_METHOD_NSS; + reipl_method = REIPL_METHOD_NSS; + break; + case IPL_TYPE_UNKNOWN: + reipl_method = REIPL_METHOD_DEFAULT; break; default: - reipl_method = IPL_METHOD_NONE; + BUG(); } reipl_type = type; return 0; @@ -579,22 +644,22 @@ static struct attribute_group dump_ccw_a /* dump type */ -static int dump_set_type(enum ipl_type type) +static int dump_set_type(enum dump_type type) { if (!(dump_capabilities & type)) return -EINVAL; switch(type) { - case IPL_TYPE_CCW: + case DUMP_TYPE_CCW: if (MACHINE_IS_VM) - dump_method = IPL_METHOD_CCW_VM; + dump_method = DUMP_METHOD_CCW_VM; else - dump_method = IPL_METHOD_CCW_CIO; + dump_method = DUMP_METHOD_CCW_CIO; break; - case IPL_TYPE_FCP: - dump_method = IPL_METHOD_FCP_RW_DIAG; + case DUMP_TYPE_FCP: + dump_method = DUMP_METHOD_FCP_DIAG; break; default: - dump_method = IPL_METHOD_NONE; + dump_method = DUMP_METHOD_NONE; } dump_type = type; return 0; @@ -602,7 +667,7 @@ static int dump_set_type(enum ipl_type t static ssize_t dump_type_show(struct subsystem *subsys, char *page) { - return sprintf(page, "%s\n", ipl_type_str(dump_type)); + return sprintf(page, "%s\n", dump_type_str(dump_type)); } static ssize_t dump_type_store(struct subsystem *subsys, const char *buf, @@ -610,12 +675,12 @@ static ssize_t dump_type_store(struct su { int rc = -EINVAL; - if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0) - rc = dump_set_type(IPL_TYPE_NONE); - else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0) - rc = dump_set_type(IPL_TYPE_CCW); - else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) - rc = dump_set_type(IPL_TYPE_FCP); + if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_NONE); + else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_CCW); + else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_FCP); return (rc != 0) ? rc : len; } @@ -664,14 +729,14 @@ void do_reipl(void) char loadparm[LOADPARM_LEN + 1]; switch (reipl_method) { - case IPL_METHOD_CCW_CIO: + case REIPL_METHOD_CCW_CIO: devid.devno = reipl_block_ccw->ipl_info.ccw.devno; - if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno) + if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno) diag308(DIAG308_IPL, NULL); devid.ssid = 0; reipl_ccw_dev(&devid); break; - case IPL_METHOD_CCW_VM: + case REIPL_METHOD_CCW_VM: reipl_get_ascii_loadparm(loadparm); if (strlen(loadparm) == 0) sprintf(buf, "IPL %X", @@ -681,30 +746,32 @@ void do_reipl(void) reipl_block_ccw->ipl_info.ccw.devno, loadparm); __cpcmd(buf, NULL, 0, NULL); break; - case IPL_METHOD_CCW_DIAG: + case REIPL_METHOD_CCW_DIAG: diag308(DIAG308_SET, reipl_block_ccw); diag308(DIAG308_IPL, NULL); break; - case IPL_METHOD_FCP_RW_DIAG: + case REIPL_METHOD_FCP_RW_DIAG: diag308(DIAG308_SET, reipl_block_fcp); diag308(DIAG308_IPL, NULL); break; - case IPL_METHOD_FCP_RO_DIAG: + case REIPL_METHOD_FCP_RO_DIAG: diag308(DIAG308_IPL, NULL); break; - case IPL_METHOD_FCP_RO_VM: + case REIPL_METHOD_FCP_RO_VM: __cpcmd("IPL", NULL, 0, NULL); break; - case IPL_METHOD_NSS: + case REIPL_METHOD_NSS: sprintf(buf, "IPL %s", reipl_nss_name); __cpcmd(buf, NULL, 0, NULL); break; - case IPL_METHOD_NONE: - default: + case REIPL_METHOD_DEFAULT: if (MACHINE_IS_VM) __cpcmd("IPL", NULL, 0, NULL); diag308(DIAG308_IPL, NULL); break; + case REIPL_METHOD_FCP_DUMP: + default: + break; } signal_processor(smp_processor_id(), sigp_stop_and_store_status); } @@ -715,28 +782,28 @@ static void do_dump(void) static char buf[100]; switch (dump_method) { - case IPL_METHOD_CCW_CIO: + case DUMP_METHOD_CCW_CIO: smp_send_stop(); devid.devno = dump_block_ccw->ipl_info.ccw.devno; devid.ssid = 0; reipl_ccw_dev(&devid); break; - case IPL_METHOD_CCW_VM: + case DUMP_METHOD_CCW_VM: smp_send_stop(); sprintf(buf, "STORE STATUS"); __cpcmd(buf, NULL, 0, NULL); sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); __cpcmd(buf, NULL, 0, NULL); break; - case IPL_METHOD_CCW_DIAG: + case DUMP_METHOD_CCW_DIAG: diag308(DIAG308_SET, dump_block_ccw); diag308(DIAG308_DUMP, NULL); break; - case IPL_METHOD_FCP_RW_DIAG: + case DUMP_METHOD_FCP_DIAG: diag308(DIAG308_SET, dump_block_fcp); diag308(DIAG308_DUMP, NULL); break; - case IPL_METHOD_NONE: + case DUMP_METHOD_NONE: default: return; } @@ -777,12 +844,13 @@ static int __init ipl_init(void) rc = firmware_register(&ipl_subsys); if (rc) return rc; - switch (ipl_get_type()) { + switch (ipl_info.type) { case IPL_TYPE_CCW: rc = sysfs_create_group(&ipl_subsys.kset.kobj, &ipl_ccw_attr_group); break; case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: rc = ipl_register_fcp_files(); break; case IPL_TYPE_NSS: @@ -852,7 +920,7 @@ static int __init reipl_ccw_init(void) /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ if (!MACHINE_IS_VM) sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; - if (ipl_get_type() == IPL_TYPE_CCW) + if (ipl_info.type == IPL_TYPE_CCW) reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; reipl_capabilities |= IPL_TYPE_CCW; return 0; @@ -862,9 +930,9 @@ static int __init reipl_fcp_init(void) { int rc; - if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP)) + if ((!diag308_set_works) && (ipl_info.type != IPL_TYPE_FCP)) return 0; - if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP)) + if ((!diag308_set_works) && (ipl_info.type == IPL_TYPE_FCP)) make_attrs_ro(reipl_fcp_attrs); reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); @@ -875,7 +943,7 @@ static int __init reipl_fcp_init(void) free_page((unsigned long)reipl_block_fcp); return rc; } - if (ipl_get_type() == IPL_TYPE_FCP) { + if (ipl_info.type == IPL_TYPE_FCP) { memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); } else { reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; @@ -909,7 +977,7 @@ static int __init reipl_init(void) rc = reipl_nss_init(); if (rc) return rc; - rc = reipl_set_type(ipl_get_type()); + rc = reipl_set_type(ipl_info.type); if (rc) return rc; return 0; @@ -931,7 +999,7 @@ static int __init dump_ccw_init(void) dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; - dump_capabilities |= IPL_TYPE_CCW; + dump_capabilities |= DUMP_TYPE_CCW; return 0; } @@ -956,7 +1024,7 @@ static int __init dump_fcp_init(void) dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN; dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP; dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP; - dump_capabilities |= IPL_TYPE_FCP; + dump_capabilities |= DUMP_TYPE_FCP; return 0; } @@ -995,7 +1063,7 @@ static int __init dump_init(void) rc = dump_fcp_init(); if (rc) return rc; - dump_set_type(IPL_TYPE_NONE); + dump_set_type(DUMP_TYPE_NONE); return 0; } @@ -1038,6 +1106,27 @@ static int __init s390_ipl_init(void) __initcall(s390_ipl_init); +void __init ipl_save_parameters(void) +{ + struct cio_iplinfo iplinfo; + unsigned int *ipl_ptr; + void *src, *dst; + + if (cio_get_iplinfo(&iplinfo)) + return; + + ipl_devno = iplinfo.devno; + ipl_flags |= IPL_DEVNO_VALID; + if (!iplinfo.is_qdio) + return; + ipl_flags |= IPL_PARMBLOCK_VALID; + ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR; + src = (void *)(unsigned long)*ipl_ptr; + dst = (void *)IPL_PARMBLOCK_ORIGIN; + memmove(dst, src, PAGE_SIZE); + *ipl_ptr = IPL_PARMBLOCK_ORIGIN; +} + static LIST_HEAD(rcall); static DEFINE_MUTEX(rcall_mutex); diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 39d1dd7..59b4e79 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -31,6 +31,7 @@ #include #include #include #include +#include #if 0 #define DEBUGP printk @@ -398,9 +399,10 @@ int module_finalize(const Elf_Ehdr *hdr, struct module *me) { vfree(me->arch.syminfo); - return 0; + return module_bug_finalize(hdr, sechdrs, me); } void module_arch_cleanup(struct module *mod) { + module_bug_cleanup(mod); } diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 5acfac6..11d9b01 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -280,24 +280,26 @@ #endif /* CONFIG_64BIT */ return 0; } -asmlinkage long sys_fork(struct pt_regs regs) +asmlinkage long sys_fork(void) { - return do_fork(SIGCHLD, regs.gprs[15], ®s, 0, NULL, NULL); + struct pt_regs *regs = task_pt_regs(current); + return do_fork(SIGCHLD, regs->gprs[15], regs, 0, NULL, NULL); } -asmlinkage long sys_clone(struct pt_regs regs) +asmlinkage long sys_clone(void) { - unsigned long clone_flags; - unsigned long newsp; + struct pt_regs *regs = task_pt_regs(current); + unsigned long clone_flags; + unsigned long newsp; int __user *parent_tidptr, *child_tidptr; - clone_flags = regs.gprs[3]; - newsp = regs.orig_gpr2; - parent_tidptr = (int __user *) regs.gprs[4]; - child_tidptr = (int __user *) regs.gprs[5]; - if (!newsp) - newsp = regs.gprs[15]; - return do_fork(clone_flags, newsp, ®s, 0, + clone_flags = regs->gprs[3]; + newsp = regs->orig_gpr2; + parent_tidptr = (int __user *) regs->gprs[4]; + child_tidptr = (int __user *) regs->gprs[5]; + if (!newsp) + newsp = regs->gprs[15]; + return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); } @@ -311,40 +313,52 @@ asmlinkage long sys_clone(struct pt_regs * do not have enough call-clobbered registers to hold all * the information you need. */ -asmlinkage long sys_vfork(struct pt_regs regs) +asmlinkage long sys_vfork(void) { + struct pt_regs *regs = task_pt_regs(current); return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, - regs.gprs[15], ®s, 0, NULL, NULL); + regs->gprs[15], regs, 0, NULL, NULL); +} + +asmlinkage void execve_tail(void) +{ + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); + current->thread.fp_regs.fpc = 0; + if (MACHINE_HAS_IEEE) + asm volatile("sfpc %0,%0" : : "d" (0)); } /* * sys_execve() executes a new program. */ -asmlinkage long sys_execve(struct pt_regs regs) +asmlinkage long sys_execve(void) { - int error; - char * filename; - - filename = getname((char __user *) regs.orig_gpr2); - error = PTR_ERR(filename); - if (IS_ERR(filename)) - goto out; - error = do_execve(filename, (char __user * __user *) regs.gprs[3], - (char __user * __user *) regs.gprs[4], ®s); - if (error == 0) { - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); - current->thread.fp_regs.fpc = 0; - if (MACHINE_HAS_IEEE) - asm volatile("sfpc %0,%0" : : "d" (0)); + struct pt_regs *regs = task_pt_regs(current); + char *filename; + unsigned long result; + int rc; + + filename = getname((char __user *) regs->orig_gpr2); + if (IS_ERR(filename)) { + result = PTR_ERR(filename); + goto out; } - putname(filename); + rc = do_execve(filename, (char __user * __user *) regs->gprs[3], + (char __user * __user *) regs->gprs[4], regs); + if (rc) { + result = rc; + goto out_putname; + } + execve_tail(); + result = regs->gprs[2]; +out_putname: + putname(filename); out: - return error; + return result; } - /* * fill in the FPU structure for a core dump. */ diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 863c8d0..3dfd098 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -285,6 +285,26 @@ #endif } } +#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) +static void __init setup_zfcpdump(unsigned int console_devno) +{ + static char str[64]; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return; + if (console_devno != -1) + sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x", + ipl_info.data.fcp.dev_id.devno, console_devno); + else + sprintf(str, "cio_ignore=all,!0.0.%04x", + ipl_info.data.fcp.dev_id.devno); + strcat(COMMAND_LINE, str); + console_loglevel = 2; +} +#else +static inline void setup_zfcpdump(unsigned int console_devno) {} +#endif /* CONFIG_ZFCPDUMP */ + #ifdef CONFIG_SMP void (*_machine_restart)(char *command) = machine_restart_smp; void (*_machine_halt)(void) = machine_halt_smp; @@ -586,13 +606,20 @@ setup_resources(void) } } +unsigned long real_memory_size; +EXPORT_SYMBOL_GPL(real_memory_size); + static void __init setup_memory_end(void) { - unsigned long real_size, memory_size; + unsigned long memory_size; unsigned long max_mem, max_phys; int i; - memory_size = real_size = 0; +#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) + if (ipl_info.type == IPL_TYPE_FCP_DUMP) + memory_end = ZFCPDUMP_HSA_SIZE; +#endif + memory_size = 0; max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE; memory_end &= PAGE_MASK; @@ -601,7 +628,8 @@ static void __init setup_memory_end(void for (i = 0; i < MEMORY_CHUNKS; i++) { struct mem_chunk *chunk = &memory_chunk[i]; - real_size = max(real_size, chunk->addr + chunk->size); + real_memory_size = max(real_memory_size, + chunk->addr + chunk->size); if (chunk->addr >= max_mem) { memset(chunk, 0, sizeof(*chunk)); continue; @@ -765,6 +793,7 @@ #endif /* CONFIG_64BIT */ parse_early_param(); + setup_ipl_info(); setup_memory_end(); setup_addressing_mode(); setup_memory(); @@ -782,6 +811,9 @@ #endif /* CONFIG_64BIT */ /* Setup default console */ conmode_default(); + + /* Setup zfcpdump support */ + setup_zfcpdump(console_devno); } void print_cpu_info(struct cpuinfo_S390 *cpuinfo) diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 554f9cf..3c41907 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -102,9 +102,9 @@ sys_sigaction(int sig, const struct old_ } asmlinkage long -sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, - struct pt_regs *regs) +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) { + struct pt_regs *regs = task_pt_regs(current); return do_sigaltstack(uss, uoss, regs->gprs[15]); } @@ -163,8 +163,9 @@ static int restore_sigregs(struct pt_reg return 0; } -asmlinkage long sys_sigreturn(struct pt_regs *regs) +asmlinkage long sys_sigreturn(void) { + struct pt_regs *regs = task_pt_regs(current); sigframe __user *frame = (sigframe __user *)regs->gprs[15]; sigset_t set; @@ -189,8 +190,9 @@ badframe: return 0; } -asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) +asmlinkage long sys_rt_sigreturn(void) { + struct pt_regs *regs = task_pt_regs(current); rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; sigset_t set; diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 97764f7..3754e20 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -1,12 +1,12 @@ /* * arch/s390/kernel/smp.c * - * Copyright (C) IBM Corp. 1999,2006 + * Copyright IBM Corp. 1999,2007 * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * Heiko Carstens (heiko.carstens@de.ibm.com) + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * Heiko Carstens (heiko.carstens@de.ibm.com) * - * based on other smp stuff by + * based on other smp stuff by * (c) 1995 Alan Cox, CymruNET Ltd * (c) 1998 Ingo Molnar * @@ -31,6 +31,7 @@ #include #include #include #include +#include #include #include #include @@ -40,17 +41,19 @@ #include #include #include #include - -extern volatile int __cpu_logical_map[]; +#include /* * An array with a pointer the lowcore of every CPU. */ - struct _lowcore *lowcore_ptr[NR_CPUS]; +EXPORT_SYMBOL(lowcore_ptr); cpumask_t cpu_online_map = CPU_MASK_NONE; +EXPORT_SYMBOL(cpu_online_map); + cpumask_t cpu_possible_map = CPU_MASK_NONE; +EXPORT_SYMBOL(cpu_possible_map); static struct task_struct *current_set[NR_CPUS]; @@ -70,7 +73,7 @@ struct call_data_struct { int wait; }; -static struct call_data_struct * call_data; +static struct call_data_struct *call_data; /* * 'Call function' interrupt callback @@ -150,8 +153,8 @@ out: * * Run a function on all other CPUs. * - * You must not call this function with disabled interrupts or from a - * hardware interrupt handler. You may call it from a bottom half. + * You must not call this function with disabled interrupts, from a + * hardware interrupt handler or from a bottom half. */ int smp_call_function(void (*func) (void *info), void *info, int nonatomic, int wait) @@ -177,11 +180,11 @@ EXPORT_SYMBOL(smp_call_function); * * Run a function on one processor. * - * You must not call this function with disabled interrupts or from a - * hardware interrupt handler. You may call it from a bottom half. + * You must not call this function with disabled interrupts, from a + * hardware interrupt handler or from a bottom half. */ int smp_call_function_on(void (*func) (void *info), void *info, int nonatomic, - int wait, int cpu) + int wait, int cpu) { cpumask_t map = CPU_MASK_NONE; @@ -195,9 +198,9 @@ EXPORT_SYMBOL(smp_call_function_on); static void do_send_stop(void) { - int cpu, rc; + int cpu, rc; - /* stop all processors */ + /* stop all processors */ for_each_online_cpu(cpu) { if (cpu == smp_processor_id()) continue; @@ -209,9 +212,9 @@ static void do_send_stop(void) static void do_store_status(void) { - int cpu, rc; + int cpu, rc; - /* store status of all processors in their lowcores (real 0) */ + /* store status of all processors in their lowcores (real 0) */ for_each_online_cpu(cpu) { if (cpu == smp_processor_id()) continue; @@ -219,8 +222,8 @@ static void do_store_status(void) rc = signal_processor_p( (__u32)(unsigned long) lowcore_ptr[cpu], cpu, sigp_store_status_at_address); - } while(rc == sigp_busy); - } + } while (rc == sigp_busy); + } } static void do_wait_for_stop(void) @@ -231,7 +234,7 @@ static void do_wait_for_stop(void) for_each_online_cpu(cpu) { if (cpu == smp_processor_id()) continue; - while(!smp_cpu_not_running(cpu)) + while (!smp_cpu_not_running(cpu)) cpu_relax(); } } @@ -245,7 +248,7 @@ void smp_send_stop(void) /* Disable all interrupts/machine checks */ __load_psw_mask(psw_kernel_bits & ~PSW_MASK_MCHECK); - /* write magic number to zero page (absolute 0) */ + /* write magic number to zero page (absolute 0) */ lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC; /* stop other processors. */ @@ -261,8 +264,7 @@ void smp_send_stop(void) /* * Reboot, halt and power_off routines for SMP. */ - -void machine_restart_smp(char * __unused) +void machine_restart_smp(char *__unused) { smp_send_stop(); do_reipl(); @@ -293,17 +295,17 @@ void machine_power_off_smp(void) static void do_ext_call_interrupt(__u16 code) { - unsigned long bits; + unsigned long bits; - /* - * handle bit signal external calls - * - * For the ec_schedule signal we have to do nothing. All the work - * is done automatically when we return from the interrupt. - */ + /* + * handle bit signal external calls + * + * For the ec_schedule signal we have to do nothing. All the work + * is done automatically when we return from the interrupt. + */ bits = xchg(&S390_lowcore.ext_call_fast, 0); - if (test_bit(ec_call_function, &bits)) + if (test_bit(ec_call_function, &bits)) do_call_function(); } @@ -313,11 +315,11 @@ static void do_ext_call_interrupt(__u16 */ static void smp_ext_bitcall(int cpu, ec_bit_sig sig) { - /* - * Set signaling bit in lowcore of target cpu and kick it - */ + /* + * Set signaling bit in lowcore of target cpu and kick it + */ set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast); - while(signal_processor(cpu, sigp_emergency_signal) == sigp_busy) + while (signal_processor(cpu, sigp_emergency_signal) == sigp_busy) udelay(10); } @@ -332,7 +334,7 @@ void smp_ptlb_callback(void *info) void smp_ptlb_all(void) { - on_each_cpu(smp_ptlb_callback, NULL, 0, 1); + on_each_cpu(smp_ptlb_callback, NULL, 0, 1); } EXPORT_SYMBOL(smp_ptlb_all); #endif /* ! CONFIG_64BIT */ @@ -344,7 +346,7 @@ #endif /* ! CONFIG_64BIT */ */ void smp_send_reschedule(int cpu) { - smp_ext_bitcall(cpu, ec_schedule); + smp_ext_bitcall(cpu, ec_schedule); } /* @@ -358,11 +360,12 @@ struct ec_creg_mask_parms { /* * callback for setting/clearing control bits */ -static void smp_ctl_bit_callback(void *info) { +static void smp_ctl_bit_callback(void *info) +{ struct ec_creg_mask_parms *pp = info; unsigned long cregs[16]; int i; - + __ctl_store(cregs, 0, 15); for (i = 0; i <= 15; i++) cregs[i] = (cregs[i] & pp->andvals[i]) | pp->orvals[i]; @@ -381,6 +384,7 @@ void smp_ctl_set_bit(int cr, int bit) parms.orvals[cr] = 1 << bit; on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1); } +EXPORT_SYMBOL(smp_ctl_set_bit); /* * Clear a bit in a control register of all cpus @@ -394,13 +398,72 @@ void smp_ctl_clear_bit(int cr, int bit) parms.andvals[cr] = ~(1L << bit); on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1); } +EXPORT_SYMBOL(smp_ctl_clear_bit); + +#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) + +/* + * zfcpdump_prefix_array holds prefix registers for the following scenario: + * 64 bit zfcpdump kernel and 31 bit kernel which is to be dumped. We have to + * save its prefix registers, since they get lost, when switching from 31 bit + * to 64 bit. + */ +unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \ + __attribute__((__section__(".data"))); + +static void __init smp_get_save_areas(void) +{ + unsigned int cpu, cpu_num, rc; + __u16 boot_cpu_addr; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return; + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + cpu_num = 1; + for (cpu = 0; cpu <= 65535; cpu++) { + if ((u16) cpu == boot_cpu_addr) + continue; + __cpu_logical_map[1] = (__u16) cpu; + if (signal_processor(1, sigp_sense) == sigp_not_operational) + continue; + if (cpu_num >= NR_CPUS) { + printk("WARNING: Registers for cpu %i are not " + "saved, since dump kernel was compiled with" + "NR_CPUS=%i!\n", cpu_num, NR_CPUS); + continue; + } + zfcpdump_save_areas[cpu_num] = + alloc_bootmem(sizeof(union save_area)); + while (1) { + rc = signal_processor(1, sigp_stop_and_store_status); + if (rc != sigp_busy) + break; + cpu_relax(); + } + memcpy(zfcpdump_save_areas[cpu_num], + (void *)(unsigned long) store_prefix() + + SAVE_AREA_BASE, SAVE_AREA_SIZE); +#ifdef __s390x__ + /* copy original prefix register */ + zfcpdump_save_areas[cpu_num]->s390x.pref_reg = + zfcpdump_prefix_array[cpu_num]; +#endif + cpu_num++; + } +} + +union save_area *zfcpdump_save_areas[NR_CPUS + 1]; +EXPORT_SYMBOL_GPL(zfcpdump_save_areas); + +#else +#define smp_get_save_areas() do { } while (0) +#endif /* * Lets check how many CPUs we have. */ -static unsigned int -__init smp_count_cpus(void) +static unsigned int __init smp_count_cpus(void) { unsigned int cpu, num_cpus; __u16 boot_cpu_addr; @@ -416,31 +479,30 @@ __init smp_count_cpus(void) if ((__u16) cpu == boot_cpu_addr) continue; __cpu_logical_map[1] = (__u16) cpu; - if (signal_processor(1, sigp_sense) == - sigp_not_operational) + if (signal_processor(1, sigp_sense) == sigp_not_operational) continue; num_cpus++; } - printk("Detected %d CPU's\n",(int) num_cpus); + printk("Detected %d CPU's\n", (int) num_cpus); printk("Boot cpu address %2X\n", boot_cpu_addr); return num_cpus; } /* - * Activate a secondary processor. + * Activate a secondary processor. */ int __devinit start_secondary(void *cpuvoid) { - /* Setup the cpu */ - cpu_init(); + /* Setup the cpu */ + cpu_init(); preempt_disable(); /* Enable TOD clock interrupts on the secondary cpu. */ - init_cpu_timer(); + init_cpu_timer(); #ifdef CONFIG_VIRT_TIMER /* Enable cpu timer interrupts on the secondary cpu. */ - init_cpu_vtimer(); + init_cpu_vtimer(); #endif /* Enable pfault pseudo page faults on this cpu. */ pfault_init(); @@ -449,11 +511,11 @@ #endif cpu_set(smp_processor_id(), cpu_online_map); /* Switch on interrupts */ local_irq_enable(); - /* Print info about this processor */ - print_cpu_info(&S390_lowcore.cpu_data); - /* cpu_idle will call schedule for us */ - cpu_idle(); - return 0; + /* Print info about this processor */ + print_cpu_info(&S390_lowcore.cpu_data); + /* cpu_idle will call schedule for us */ + cpu_idle(); + return 0; } static void __init smp_create_idle(unsigned int cpu) @@ -470,56 +532,13 @@ static void __init smp_create_idle(unsig current_set[cpu] = p; } -/* Reserving and releasing of CPUs */ - -static DEFINE_SPINLOCK(smp_reserve_lock); -static int smp_cpu_reserved[NR_CPUS]; - -int -smp_get_cpu(cpumask_t cpu_mask) -{ - unsigned long flags; - int cpu; - - spin_lock_irqsave(&smp_reserve_lock, flags); - /* Try to find an already reserved cpu. */ - for_each_cpu_mask(cpu, cpu_mask) { - if (smp_cpu_reserved[cpu] != 0) { - smp_cpu_reserved[cpu]++; - /* Found one. */ - goto out; - } - } - /* Reserve a new cpu from cpu_mask. */ - for_each_cpu_mask(cpu, cpu_mask) { - if (cpu_online(cpu)) { - smp_cpu_reserved[cpu]++; - goto out; - } - } - cpu = -ENODEV; -out: - spin_unlock_irqrestore(&smp_reserve_lock, flags); - return cpu; -} - -void -smp_put_cpu(int cpu) -{ - unsigned long flags; - - spin_lock_irqsave(&smp_reserve_lock, flags); - smp_cpu_reserved[cpu]--; - spin_unlock_irqrestore(&smp_reserve_lock, flags); -} - -static int -cpu_stopped(int cpu) +static int cpu_stopped(int cpu) { __u32 status; /* Check for stopped state */ - if (signal_processor_ps(&status, 0, cpu, sigp_sense) == sigp_status_stored) { + if (signal_processor_ps(&status, 0, cpu, sigp_sense) == + sigp_status_stored) { if (status & 0x40) return 1; } @@ -528,14 +547,13 @@ cpu_stopped(int cpu) /* Upping and downing of CPUs */ -int -__cpu_up(unsigned int cpu) +int __cpu_up(unsigned int cpu) { struct task_struct *idle; - struct _lowcore *cpu_lowcore; + struct _lowcore *cpu_lowcore; struct stack_frame *sf; - sigp_ccode ccode; - int curr_cpu; + sigp_ccode ccode; + int curr_cpu; for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) { __cpu_logical_map[cpu] = (__u16) curr_cpu; @@ -548,7 +566,7 @@ __cpu_up(unsigned int cpu) ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]), cpu, sigp_set_prefix); - if (ccode){ + if (ccode) { printk("sigp_set_prefix failed for cpu %d " "with condition code %d\n", (int) cpu, (int) ccode); @@ -556,9 +574,9 @@ __cpu_up(unsigned int cpu) } idle = current_set[cpu]; - cpu_lowcore = lowcore_ptr[cpu]; + cpu_lowcore = lowcore_ptr[cpu]; cpu_lowcore->kernel_stack = (unsigned long) - task_stack_page(idle) + (THREAD_SIZE); + task_stack_page(idle) + THREAD_SIZE; sf = (struct stack_frame *) (cpu_lowcore->kernel_stack - sizeof(struct pt_regs) - sizeof(struct stack_frame)); @@ -570,11 +588,11 @@ __cpu_up(unsigned int cpu) " stam 0,15,0(%0)" : : "a" (&cpu_lowcore->access_regs_save_area) : "memory"); cpu_lowcore->percpu_offset = __per_cpu_offset[cpu]; - cpu_lowcore->current_task = (unsigned long) idle; - cpu_lowcore->cpu_data.cpu_nr = cpu; + cpu_lowcore->current_task = (unsigned long) idle; + cpu_lowcore->cpu_data.cpu_nr = cpu; eieio(); - while (signal_processor(cpu,sigp_restart) == sigp_busy) + while (signal_processor(cpu, sigp_restart) == sigp_busy) udelay(10); while (!cpu_online(cpu)) @@ -589,6 +607,7 @@ void __init smp_setup_cpu_possible_map(v { unsigned int phy_cpus, pos_cpus, cpu; + smp_get_save_areas(); phy_cpus = smp_count_cpus(); pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS); @@ -620,18 +639,11 @@ static int __init setup_possible_cpus(ch } early_param("possible_cpus", setup_possible_cpus); -int -__cpu_disable(void) +int __cpu_disable(void) { - unsigned long flags; struct ec_creg_mask_parms cr_parms; int cpu = smp_processor_id(); - spin_lock_irqsave(&smp_reserve_lock, flags); - if (smp_cpu_reserved[cpu] != 0) { - spin_unlock_irqrestore(&smp_reserve_lock, flags); - return -EBUSY; - } cpu_clear(cpu, cpu_online_map); /* Disable pfault pseudo page faults on this cpu. */ @@ -642,24 +654,23 @@ __cpu_disable(void) /* disable all external interrupts */ cr_parms.orvals[0] = 0; - cr_parms.andvals[0] = ~(1<<15 | 1<<14 | 1<<13 | 1<<12 | - 1<<11 | 1<<10 | 1<< 6 | 1<< 4); + cr_parms.andvals[0] = ~(1 << 15 | 1 << 14 | 1 << 13 | 1 << 12 | + 1 << 11 | 1 << 10 | 1 << 6 | 1 << 4); /* disable all I/O interrupts */ cr_parms.orvals[6] = 0; - cr_parms.andvals[6] = ~(1<<31 | 1<<30 | 1<<29 | 1<<28 | - 1<<27 | 1<<26 | 1<<25 | 1<<24); + cr_parms.andvals[6] = ~(1 << 31 | 1 << 30 | 1 << 29 | 1 << 28 | + 1 << 27 | 1 << 26 | 1 << 25 | 1 << 24); /* disable most machine checks */ cr_parms.orvals[14] = 0; - cr_parms.andvals[14] = ~(1<<28 | 1<<27 | 1<<26 | 1<<25 | 1<<24); + cr_parms.andvals[14] = ~(1 << 28 | 1 << 27 | 1 << 26 | + 1 << 25 | 1 << 24); smp_ctl_bit_callback(&cr_parms); - spin_unlock_irqrestore(&smp_reserve_lock, flags); return 0; } -void -__cpu_die(unsigned int cpu) +void __cpu_die(unsigned int cpu) { /* Wait until target cpu is down */ while (!smp_cpu_not_running(cpu)) @@ -667,13 +678,12 @@ __cpu_die(unsigned int cpu) printk("Processor %d spun down\n", cpu); } -void -cpu_die(void) +void cpu_die(void) { idle_task_exit(); signal_processor(smp_processor_id(), sigp_stop); BUG(); - for(;;); + for (;;); } #endif /* CONFIG_HOTPLUG_CPU */ @@ -686,36 +696,36 @@ void __init smp_prepare_cpus(unsigned in { unsigned long stack; unsigned int cpu; - int i; - - /* request the 0x1201 emergency signal external interrupt */ - if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0) - panic("Couldn't request external interrupt 0x1201"); - memset(lowcore_ptr,0,sizeof(lowcore_ptr)); - /* - * Initialize prefix pages and stacks for all possible cpus - */ + int i; + + /* request the 0x1201 emergency signal external interrupt */ + if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0) + panic("Couldn't request external interrupt 0x1201"); + memset(lowcore_ptr, 0, sizeof(lowcore_ptr)); + /* + * Initialize prefix pages and stacks for all possible cpus + */ print_cpu_info(&S390_lowcore.cpu_data); - for_each_possible_cpu(i) { + for_each_possible_cpu(i) { lowcore_ptr[i] = (struct _lowcore *) - __get_free_pages(GFP_KERNEL|GFP_DMA, - sizeof(void*) == 8 ? 1 : 0); - stack = __get_free_pages(GFP_KERNEL,ASYNC_ORDER); - if (lowcore_ptr[i] == NULL || stack == 0ULL) + __get_free_pages(GFP_KERNEL | GFP_DMA, + sizeof(void*) == 8 ? 1 : 0); + stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER); + if (!lowcore_ptr[i] || !stack) panic("smp_boot_cpus failed to allocate memory\n"); *(lowcore_ptr[i]) = S390_lowcore; - lowcore_ptr[i]->async_stack = stack + (ASYNC_SIZE); - stack = __get_free_pages(GFP_KERNEL,0); - if (stack == 0ULL) + lowcore_ptr[i]->async_stack = stack + ASYNC_SIZE; + stack = __get_free_pages(GFP_KERNEL, 0); + if (!stack) panic("smp_boot_cpus failed to allocate memory\n"); - lowcore_ptr[i]->panic_stack = stack + (PAGE_SIZE); + lowcore_ptr[i]->panic_stack = stack + PAGE_SIZE; #ifndef CONFIG_64BIT if (MACHINE_HAS_IEEE) { lowcore_ptr[i]->extended_save_area_addr = - (__u32) __get_free_pages(GFP_KERNEL,0); - if (lowcore_ptr[i]->extended_save_area_addr == 0) + (__u32) __get_free_pages(GFP_KERNEL, 0); + if (!lowcore_ptr[i]->extended_save_area_addr) panic("smp_boot_cpus failed to " "allocate memory\n"); } @@ -754,34 +764,63 @@ void smp_cpus_done(unsigned int max_cpus */ int setup_profiling_timer(unsigned int multiplier) { - return 0; + return 0; } static DEFINE_PER_CPU(struct cpu, cpu_devices); +static ssize_t show_capability(struct sys_device *dev, char *buf) +{ + unsigned int capability; + int rc; + + rc = get_cpu_capability(&capability); + if (rc) + return rc; + return sprintf(buf, "%u\n", capability); +} +static SYSDEV_ATTR(capability, 0444, show_capability, NULL); + +static int __cpuinit smp_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned int)(long)hcpu; + struct cpu *c = &per_cpu(cpu_devices, cpu); + struct sys_device *s = &c->sysdev; + + switch (action) { + case CPU_ONLINE: + if (sysdev_create_file(s, &attr_capability)) + return NOTIFY_BAD; + break; + case CPU_DEAD: + sysdev_remove_file(s, &attr_capability); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata smp_cpu_nb = { + .notifier_call = smp_cpu_notify, +}; + static int __init topology_init(void) { int cpu; - int ret; + + register_cpu_notifier(&smp_cpu_nb); for_each_possible_cpu(cpu) { struct cpu *c = &per_cpu(cpu_devices, cpu); + struct sys_device *s = &c->sysdev; c->hotpluggable = 1; - ret = register_cpu(c, cpu); - if (ret) - printk(KERN_WARNING "topology_init: register_cpu %d " - "failed (%d)\n", cpu, ret); + register_cpu(c, cpu); + if (!cpu_online(cpu)) + continue; + s = &c->sysdev; + sysdev_create_file(s, &attr_capability); } return 0; } - subsys_initcall(topology_init); - -EXPORT_SYMBOL(cpu_online_map); -EXPORT_SYMBOL(cpu_possible_map); -EXPORT_SYMBOL(lowcore_ptr); -EXPORT_SYMBOL(smp_ctl_set_bit); -EXPORT_SYMBOL(smp_ctl_clear_bit); -EXPORT_SYMBOL(smp_get_cpu); -EXPORT_SYMBOL(smp_put_cpu); diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c index 584ed95..3a77c22 100644 --- a/arch/s390/kernel/sys_s390.c +++ b/arch/s390/kernel/sys_s390.c @@ -266,23 +266,3 @@ s390_fadvise64_64(struct fadvise64_64_ar return -EFAULT; return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice); } - -/* - * Do a system call from kernel instead of calling sys_execve so we - * end up with proper pt_regs. - */ -int kernel_execve(const char *filename, char *const argv[], char *const envp[]) -{ - register const char *__arg1 asm("2") = filename; - register char *const*__arg2 asm("3") = argv; - register char *const*__arg3 asm("4") = envp; - register long __svcres asm("2"); - asm volatile( - "svc %b1" - : "=d" (__svcres) - : "i" (__NR_execve), - "0" (__arg1), - "d" (__arg2), - "d" (__arg3) : "memory"); - return __svcres; -} diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index c774f10..cd8d321 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -10,7 +10,7 @@ #define NI_SYSCALL SYSCALL(sys_ni_syscal NI_SYSCALL /* 0 */ SYSCALL(sys_exit,sys_exit,sys32_exit_wrapper) -SYSCALL(sys_fork_glue,sys_fork_glue,sys_fork_glue) +SYSCALL(sys_fork,sys_fork,sys_fork) SYSCALL(sys_read,sys_read,sys32_read_wrapper) SYSCALL(sys_write,sys_write,sys32_write_wrapper) SYSCALL(sys_open,sys_open,sys32_open_wrapper) /* 5 */ @@ -19,7 +19,7 @@ SYSCALL(sys_restart_syscall,sys_restart_ SYSCALL(sys_creat,sys_creat,sys32_creat_wrapper) SYSCALL(sys_link,sys_link,sys32_link_wrapper) SYSCALL(sys_unlink,sys_unlink,sys32_unlink_wrapper) /* 10 */ -SYSCALL(sys_execve_glue,sys_execve_glue,sys32_execve_glue) +SYSCALL(sys_execve,sys_execve,sys32_execve) SYSCALL(sys_chdir,sys_chdir,sys32_chdir_wrapper) SYSCALL(sys_time,sys_ni_syscall,sys32_time_wrapper) /* old time syscall */ SYSCALL(sys_mknod,sys_mknod,sys32_mknod_wrapper) @@ -127,8 +127,8 @@ SYSCALL(sys_swapoff,sys_swapoff,sys32_sw SYSCALL(sys_sysinfo,sys_sysinfo,compat_sys_sysinfo_wrapper) SYSCALL(sys_ipc,sys_ipc,sys32_ipc_wrapper) SYSCALL(sys_fsync,sys_fsync,sys32_fsync_wrapper) -SYSCALL(sys_sigreturn_glue,sys_sigreturn_glue,sys32_sigreturn_glue) -SYSCALL(sys_clone_glue,sys_clone_glue,sys32_clone_glue) /* 120 */ +SYSCALL(sys_sigreturn,sys_sigreturn,sys32_sigreturn) +SYSCALL(sys_clone,sys_clone,sys32_clone) /* 120 */ SYSCALL(sys_setdomainname,sys_setdomainname,sys32_setdomainname_wrapper) SYSCALL(sys_newuname,s390x_newuname,sys32_newuname_wrapper) NI_SYSCALL /* modify_ldt for i386 */ @@ -181,7 +181,7 @@ SYSCALL(sys_nfsservctl,sys_nfsservctl,co SYSCALL(sys_setresgid16,sys_ni_syscall,sys32_setresgid16_wrapper) /* 170 old setresgid16 syscall */ SYSCALL(sys_getresgid16,sys_ni_syscall,sys32_getresgid16_wrapper) /* old getresgid16 syscall */ SYSCALL(sys_prctl,sys_prctl,sys32_prctl_wrapper) -SYSCALL(sys_rt_sigreturn_glue,sys_rt_sigreturn_glue,sys32_rt_sigreturn_glue) +SYSCALL(sys_rt_sigreturn,sys_rt_sigreturn,sys32_rt_sigreturn) SYSCALL(sys_rt_sigaction,sys_rt_sigaction,sys32_rt_sigaction_wrapper) SYSCALL(sys_rt_sigprocmask,sys_rt_sigprocmask,sys32_rt_sigprocmask_wrapper) /* 175 */ SYSCALL(sys_rt_sigpending,sys_rt_sigpending,sys32_rt_sigpending_wrapper) @@ -194,11 +194,11 @@ SYSCALL(sys_chown16,sys_ni_syscall,sys32 SYSCALL(sys_getcwd,sys_getcwd,sys32_getcwd_wrapper) SYSCALL(sys_capget,sys_capget,sys32_capget_wrapper) SYSCALL(sys_capset,sys_capset,sys32_capset_wrapper) /* 185 */ -SYSCALL(sys_sigaltstack_glue,sys_sigaltstack_glue,sys32_sigaltstack_glue) +SYSCALL(sys_sigaltstack,sys_sigaltstack,sys32_sigaltstack) SYSCALL(sys_sendfile,sys_sendfile64,sys32_sendfile_wrapper) NI_SYSCALL /* streams1 */ NI_SYSCALL /* streams2 */ -SYSCALL(sys_vfork_glue,sys_vfork_glue,sys_vfork_glue) /* 190 */ +SYSCALL(sys_vfork,sys_vfork,sys_vfork) /* 190 */ SYSCALL(sys_getrlimit,sys_getrlimit,compat_sys_getrlimit_wrapper) SYSCALL(sys_mmap2,sys_mmap2,sys32_mmap2_wrapper) SYSCALL(sys_truncate64,sys_ni_syscall,sys32_truncate64_wrapper) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index e1ad464..711dae8 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -280,7 +280,6 @@ static void clock_comparator_interrupt(_ } static void etr_reset(void); -static void etr_init(void); static void etr_ext_handler(__u16); /* @@ -355,7 +354,6 @@ #endif #ifdef CONFIG_VIRT_TIMER vtime_init(); #endif - etr_init(); } /* @@ -426,11 +424,11 @@ static struct etr_aib etr_port1; static int etr_port1_uptodate; static unsigned long etr_events; static struct timer_list etr_timer; -static struct tasklet_struct etr_tasklet; static DEFINE_PER_CPU(atomic_t, etr_sync_word); static void etr_timeout(unsigned long dummy); -static void etr_tasklet_fn(unsigned long dummy); +static void etr_work_fn(struct work_struct *work); +static DECLARE_WORK(etr_work, etr_work_fn); /* * The etr get_clock function. It will write the current clock value @@ -507,29 +505,31 @@ static void etr_reset(void) } } -static void etr_init(void) +static int __init etr_init(void) { struct etr_aib aib; if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) - return; + return 0; /* Check if this machine has the steai instruction. */ if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0) set_bit(ETR_FLAG_STEAI, &etr_flags); setup_timer(&etr_timer, etr_timeout, 0UL); - tasklet_init(&etr_tasklet, etr_tasklet_fn, 0); if (!etr_port0_online && !etr_port1_online) set_bit(ETR_FLAG_EACCES, &etr_flags); if (etr_port0_online) { set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } if (etr_port1_online) { set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } + return 0; } +arch_initcall(etr_init); + /* * Two sorts of ETR machine checks. The architecture reads: * "When a machine-check niterruption occurs and if a switch-to-local or @@ -549,7 +549,7 @@ void etr_switch_to_local(void) return; etr_disable_sync_clock(NULL); set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } /* @@ -564,7 +564,7 @@ void etr_sync_check(void) return; etr_disable_sync_clock(NULL); set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } /* @@ -591,13 +591,13 @@ static void etr_ext_handler(__u16 code) * Both ports are not up-to-date now. */ set_bit(ETR_EVENT_PORT_ALERT, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } static void etr_timeout(unsigned long dummy) { set_bit(ETR_EVENT_UPDATE, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } /* @@ -927,7 +927,7 @@ static struct etr_eacr etr_handle_update if (!eacr.e0 && !eacr.e1) return eacr; - /* Update port0 or port1 with aib stored in etr_tasklet_fn. */ + /* Update port0 or port1 with aib stored in etr_work_fn. */ if (aib->esw.q == 0) { /* Information for port 0 stored. */ if (eacr.p0 && !etr_port0_uptodate) { @@ -1007,7 +1007,7 @@ static void etr_update_eacr(struct etr_e * particular this is the only function that calls etr_update_eacr(), * it "controls" the etr control register. */ -static void etr_tasklet_fn(unsigned long dummy) +static void etr_work_fn(struct work_struct *work) { unsigned long long now; struct etr_eacr eacr; @@ -1220,13 +1220,13 @@ static ssize_t etr_online_store(struct s return count; /* Nothing to do. */ etr_port0_online = value; set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } else { if (etr_port1_online == value) return count; /* Nothing to do. */ etr_port1_online = value; set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events); - tasklet_hi_schedule(&etr_tasklet); + schedule_work(&etr_work); } return count; } diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index f0e5a32..49dec83 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -30,7 +30,7 @@ #include #include #include #include - +#include #include #include #include @@ -188,18 +188,31 @@ void dump_stack(void) EXPORT_SYMBOL(dump_stack); +static inline int mask_bits(struct pt_regs *regs, unsigned long bits) +{ + return (regs->psw.mask & bits) / ((~bits + 1) & bits); +} + void show_registers(struct pt_regs *regs) { - mm_segment_t old_fs; char *mode; - int i; mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl"; printk("%s PSW : %p %p", mode, (void *) regs->psw.mask, (void *) regs->psw.addr); print_symbol(" (%s)\n", regs->psw.addr & PSW_ADDR_INSN); - printk("%s GPRS: " FOURLONG, mode, + printk(" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x " + "P:%x AS:%x CC:%x PM:%x", mask_bits(regs, PSW_MASK_PER), + mask_bits(regs, PSW_MASK_DAT), mask_bits(regs, PSW_MASK_IO), + mask_bits(regs, PSW_MASK_EXT), mask_bits(regs, PSW_MASK_KEY), + mask_bits(regs, PSW_MASK_MCHECK), mask_bits(regs, PSW_MASK_WAIT), + mask_bits(regs, PSW_MASK_PSTATE), mask_bits(regs, PSW_MASK_ASC), + mask_bits(regs, PSW_MASK_CC), mask_bits(regs, PSW_MASK_PM)); +#ifdef CONFIG_64BIT + printk(" EA:%x", mask_bits(regs, PSW_BASE_BITS)); +#endif + printk("\n%s GPRS: " FOURLONG, mode, regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]); printk(" " FOURLONG, regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]); @@ -208,41 +221,7 @@ void show_registers(struct pt_regs *regs printk(" " FOURLONG, regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]); -#if 0 - /* FIXME: this isn't needed any more but it changes the ksymoops - * input. To remove or not to remove ... */ - save_access_regs(regs->acrs); - printk("%s ACRS: %08x %08x %08x %08x\n", mode, - regs->acrs[0], regs->acrs[1], regs->acrs[2], regs->acrs[3]); - printk(" %08x %08x %08x %08x\n", - regs->acrs[4], regs->acrs[5], regs->acrs[6], regs->acrs[7]); - printk(" %08x %08x %08x %08x\n", - regs->acrs[8], regs->acrs[9], regs->acrs[10], regs->acrs[11]); - printk(" %08x %08x %08x %08x\n", - regs->acrs[12], regs->acrs[13], regs->acrs[14], regs->acrs[15]); -#endif - - /* - * Print the first 20 byte of the instruction stream at the - * time of the fault. - */ - old_fs = get_fs(); - if (regs->psw.mask & PSW_MASK_PSTATE) - set_fs(USER_DS); - else - set_fs(KERNEL_DS); - printk("%s Code: ", mode); - for (i = 0; i < 20; i++) { - unsigned char c; - if (__get_user(c, (char __user *)(regs->psw.addr + i))) { - printk(" Bad PSW."); - break; - } - printk("%02x ", c); - } - set_fs(old_fs); - - printk("\n"); + show_code(regs); } /* This is called from fs/proc/array.c */ @@ -318,6 +297,11 @@ #if defined(CONFIG_SYSCTL) || defined(CO #endif } +int is_valid_bugaddr(unsigned long addr) +{ + return 1; +} + static void __kprobes inline do_trap(long interruption_code, int signr, char *str, struct pt_regs *regs, siginfo_t *info) @@ -344,8 +328,14 @@ static void __kprobes inline do_trap(lon fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); if (fixup) regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE; - else - die(str, regs, interruption_code); + else { + enum bug_trap_type btt; + + btt = report_bug(regs->psw.addr & PSW_ADDR_INSN); + if (btt == BUG_TRAP_TYPE_WARN) + return; + die(str, regs, interruption_code); + } } } diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index c30716a..418f642 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -45,6 +45,8 @@ #endif __ex_table : { *(__ex_table) } __stop___ex_table = .; + BUG_TABLE + .data : { /* Data */ *(.data) CONSTRUCTORS @@ -77,6 +79,12 @@ #endif *(.init.text) _einittext = .; } + /* + * .exit.text is discarded at runtime, not link time, + * to deal with references from __bug_table + */ + .exit.text : { *(.exit.text) } + .init.data : { *(.init.data) } . = ALIGN(256); __setup_start = .; @@ -116,7 +124,7 @@ #endif /* Sections to be discarded */ /DISCARD/ : { - *(.exit.text) *(.exit.data) *(.exitcall.exit) + *(.exit.data) *(.exitcall.exit) } /* Stabs debugging sections. */ diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 9d5b028..1e1a6ee 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -128,7 +128,7 @@ static inline void set_vtimer(__u64 expi S390_lowcore.last_update_timer = expires; /* store expire time for this CPU timer */ - per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires; + __get_cpu_var(virt_cpu_timer).to_expire = expires; } #else static inline void set_vtimer(__u64 expires) @@ -137,7 +137,7 @@ static inline void set_vtimer(__u64 expi asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer)); /* store expire time for this CPU timer */ - per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires; + __get_cpu_var(virt_cpu_timer).to_expire = expires; } #endif @@ -145,7 +145,7 @@ static void start_cpu_timer(void) { struct vtimer_queue *vt_list; - vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + vt_list = &__get_cpu_var(virt_cpu_timer); /* CPU timer interrupt is pending, don't reprogramm it */ if (vt_list->idle & 1LL<<63) @@ -159,7 +159,7 @@ static void stop_cpu_timer(void) { struct vtimer_queue *vt_list; - vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + vt_list = &__get_cpu_var(virt_cpu_timer); /* nothing to do */ if (list_empty(&vt_list->list)) { @@ -219,7 +219,7 @@ static void do_callbacks(struct list_hea if (list_empty(cb_list)) return; - vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + vt_list = &__get_cpu_var(virt_cpu_timer); list_for_each_entry_safe(event, tmp, cb_list, entry) { fn = event->function; @@ -244,7 +244,6 @@ static void do_callbacks(struct list_hea */ static void do_cpu_timer_interrupt(__u16 error_code) { - int cpu; __u64 next, delta; struct vtimer_queue *vt_list; struct vtimer_list *event, *tmp; @@ -253,8 +252,7 @@ static void do_cpu_timer_interrupt(__u16 struct list_head cb_list; INIT_LIST_HEAD(&cb_list); - cpu = smp_processor_id(); - vt_list = &per_cpu(virt_cpu_timer, cpu); + vt_list = &__get_cpu_var(virt_cpu_timer); /* walk timer list, fire all expired events */ spin_lock(&vt_list->lock); @@ -534,7 +532,7 @@ void init_cpu_vtimer(void) /* enable cpu timer interrupts */ __ctl_set_bit(0,10); - vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + vt_list = &__get_cpu_var(virt_cpu_timer); INIT_LIST_HEAD(&vt_list->list); spin_lock_init(&vt_list->lock); vt_list->to_expire = 0; diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index 7a44fed..59aea65 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -5,6 +5,6 @@ # EXTRA_AFLAGS := -traditional lib-y += delay.o string.o uaccess_std.o uaccess_pt.o qrnnd.o -lib-$(CONFIG_32BIT) += div64.o +obj-$(CONFIG_32BIT) += div64.o lib-$(CONFIG_64BIT) += uaccess_mvcos.o lib-$(CONFIG_SMP) += spinlock.o diff --git a/arch/s390/lib/div64.c b/arch/s390/lib/div64.c index 0481f34..a5f8300 100644 --- a/arch/s390/lib/div64.c +++ b/arch/s390/lib/div64.c @@ -147,5 +147,3 @@ uint32_t __div64_32(uint64_t *n, uint32_ } #endif /* MARCH_G5 */ - -EXPORT_SYMBOL(__div64_32); diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 7462aeb..2b76a87 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -26,9 +26,9 @@ #include #include #include #include +#include #include -#include #include #include #include @@ -63,21 +63,25 @@ int unregister_page_fault_notifier(struc return atomic_notifier_chain_unregister(¬ify_page_fault_chain, nb); } -static inline int notify_page_fault(enum die_val val, const char *str, - struct pt_regs *regs, long err, int trap, int sig) +static int __kprobes __notify_page_fault(struct pt_regs *regs, long err) { - struct die_args args = { - .regs = regs, - .str = str, - .err = err, - .trapnr = trap, - .signr = sig - }; - return atomic_notifier_call_chain(¬ify_page_fault_chain, val, &args); + struct die_args args = { .str = "page fault", + .trapnr = 14, + .signr = SIGSEGV }; + args.regs = regs; + args.err = err; + return atomic_notifier_call_chain(¬ify_page_fault_chain, + DIE_PAGE_FAULT, &args); +} + +static inline int notify_page_fault(struct pt_regs *regs, long err) +{ + if (unlikely(kprobe_running())) + return __notify_page_fault(regs, err); + return NOTIFY_DONE; } #else -static inline int notify_page_fault(enum die_val val, const char *str, - struct pt_regs *regs, long err, int trap, int sig) +static inline int notify_page_fault(struct pt_regs *regs, long err) { return NOTIFY_DONE; } @@ -170,74 +174,127 @@ #endif force_sig_info(SIGSEGV, &si, current); } +static void do_no_context(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + const struct exception_table_entry *fixup; + + /* Are we prepared to handle this kernel fault? */ + fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK); + if (fixup) { + regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE; + return; + } + + /* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + if (check_space(current) == 0) + printk(KERN_ALERT "Unable to handle kernel pointer dereference" + " at virtual kernel address %p\n", (void *)address); + else + printk(KERN_ALERT "Unable to handle kernel paging request" + " at virtual user address %p\n", (void *)address); + + die("Oops", regs, error_code); + do_exit(SIGKILL); +} + +static void do_low_address(struct pt_regs *regs, unsigned long error_code) +{ + /* Low-address protection hit in kernel mode means + NULL pointer write access in kernel mode. */ + if (regs->psw.mask & PSW_MASK_PSTATE) { + /* Low-address protection hit in user mode 'cannot happen'. */ + die ("Low-address protection", regs, error_code); + do_exit(SIGKILL); + } + + do_no_context(regs, error_code, 0); +} + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +static int do_out_of_memory(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; + + up_read(&mm->mmap_sem); + if (is_init(tsk)) { + yield(); + down_read(&mm->mmap_sem); + return 1; + } + printk("VM: killing process %s\n", tsk->comm); + if (regs->psw.mask & PSW_MASK_PSTATE) + do_exit(SIGKILL); + do_no_context(regs, error_code, address); + return 0; +} + +static void do_sigbus(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; + + up_read(&mm->mmap_sem); + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + tsk->thread.prot_addr = address; + tsk->thread.trap_no = error_code; + force_sig(SIGBUS, tsk); + + /* Kernel mode? Handle exceptions or die */ + if (!(regs->psw.mask & PSW_MASK_PSTATE)) + do_no_context(regs, error_code, address); +} + #ifdef CONFIG_S390_EXEC_PROTECT extern long sys_sigreturn(struct pt_regs *regs); extern long sys_rt_sigreturn(struct pt_regs *regs); extern long sys32_sigreturn(struct pt_regs *regs); extern long sys32_rt_sigreturn(struct pt_regs *regs); -static inline void do_sigreturn(struct mm_struct *mm, struct pt_regs *regs, - int rt) +static int signal_return(struct mm_struct *mm, struct pt_regs *regs, + unsigned long address, unsigned long error_code) { + u16 instruction; + int rc, compat; + + pagefault_disable(); + rc = __get_user(instruction, (u16 __user *) regs->psw.addr); + pagefault_enable(); + if (rc) + return -EFAULT; + up_read(&mm->mmap_sem); clear_tsk_thread_flag(current, TIF_SINGLE_STEP); #ifdef CONFIG_COMPAT - if (test_tsk_thread_flag(current, TIF_31BIT)) { - if (rt) - sys32_rt_sigreturn(regs); - else - sys32_sigreturn(regs); - return; - } -#endif /* CONFIG_COMPAT */ - if (rt) - sys_rt_sigreturn(regs); + compat = test_tsk_thread_flag(current, TIF_31BIT); + if (compat && instruction == 0x0a77) + sys32_sigreturn(regs); + else if (compat && instruction == 0x0aad) + sys32_rt_sigreturn(regs); else +#endif + if (instruction == 0x0a77) sys_sigreturn(regs); - return; -} - -static int signal_return(struct mm_struct *mm, struct pt_regs *regs, - unsigned long address, unsigned long error_code) -{ - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - u16 *instruction; - unsigned long pfn, uaddr = regs->psw.addr; - - spin_lock(&mm->page_table_lock); - pgd = pgd_offset(mm, uaddr); - if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) - goto out_fault; - pmd = pmd_offset(pgd, uaddr); - if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) - goto out_fault; - pte = pte_offset_map(pmd_offset(pgd_offset(mm, uaddr), uaddr), uaddr); - if (!pte || !pte_present(*pte)) - goto out_fault; - pfn = pte_pfn(*pte); - if (!pfn_valid(pfn)) - goto out_fault; - spin_unlock(&mm->page_table_lock); - - instruction = (u16 *) ((pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE-1))); - if (*instruction == 0x0a77) - do_sigreturn(mm, regs, 0); - else if (*instruction == 0x0aad) - do_sigreturn(mm, regs, 1); + else if (instruction == 0x0aad) + sys_rt_sigreturn(regs); else { - printk("- XXX - do_exception: task = %s, primary, NO EXEC " - "-> SIGSEGV\n", current->comm); - up_read(&mm->mmap_sem); current->thread.prot_addr = address; current->thread.trap_no = error_code; do_sigsegv(regs, error_code, SEGV_MAPERR, address); } return 0; -out_fault: - spin_unlock(&mm->page_table_lock); - return -EFAULT; } #endif /* CONFIG_S390_EXEC_PROTECT */ @@ -253,49 +310,23 @@ #endif /* CONFIG_S390_EXEC_PROTECT */ * 3b Region third trans. -> Not present (nullification) */ static inline void -do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) +do_exception(struct pt_regs *regs, unsigned long error_code, int write) { - struct task_struct *tsk; - struct mm_struct *mm; - struct vm_area_struct * vma; - unsigned long address; - const struct exception_table_entry *fixup; - int si_code; + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct *vma; + unsigned long address; int space; + int si_code; - tsk = current; - mm = tsk->mm; - - if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, - SIGSEGV) == NOTIFY_STOP) + if (notify_page_fault(regs, error_code) == NOTIFY_STOP) return; - /* - * Check for low-address protection. This needs to be treated - * as a special case because the translation exception code - * field is not guaranteed to contain valid data in this case. - */ - if (is_protection && !(S390_lowcore.trans_exc_code & 4)) { - - /* Low-address protection hit in kernel mode means - NULL pointer write access in kernel mode. */ - if (!(regs->psw.mask & PSW_MASK_PSTATE)) { - address = 0; - space = 0; - goto no_context; - } - - /* Low-address protection hit in user mode 'cannot happen'. */ - die ("Low-address protection", regs, error_code); - do_exit(SIGKILL); - } + tsk = current; + mm = tsk->mm; - /* - * get the failing address - * more specific the segment and page table portion of - * the address - */ - address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK; + /* get the failing address and the affected space */ + address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK; space = check_space(tsk); /* @@ -313,7 +344,7 @@ do_exception(struct pt_regs *regs, unsig */ local_irq_enable(); - down_read(&mm->mmap_sem); + down_read(&mm->mmap_sem); si_code = SEGV_MAPERR; vma = find_vma(mm, address); @@ -330,19 +361,19 @@ #ifdef CONFIG_S390_EXEC_PROTECT return; #endif - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (expand_stack(vma, address)) + goto bad_area; /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ good_area: si_code = SEGV_ACCERR; - if (!is_protection) { + if (!write) { /* page not present, check vm flags */ if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) goto bad_area; @@ -357,7 +388,7 @@ survive: * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, address, is_protection)) { + switch (handle_mm_fault(mm, vma, address, write)) { case VM_FAULT_MINOR: tsk->min_flt++; break; @@ -365,9 +396,12 @@ survive: tsk->maj_flt++; break; case VM_FAULT_SIGBUS: - goto do_sigbus; + do_sigbus(regs, error_code, address); + return; case VM_FAULT_OOM: - goto out_of_memory; + if (do_out_of_memory(regs, error_code, address)) + goto survive; + return; default: BUG(); } @@ -385,75 +419,34 @@ survive: * Fix it, but check if it's kernel or user first.. */ bad_area: - up_read(&mm->mmap_sem); + up_read(&mm->mmap_sem); - /* User mode accesses just cause a SIGSEGV */ - if (regs->psw.mask & PSW_MASK_PSTATE) { - tsk->thread.prot_addr = address; - tsk->thread.trap_no = error_code; + /* User mode accesses just cause a SIGSEGV */ + if (regs->psw.mask & PSW_MASK_PSTATE) { + tsk->thread.prot_addr = address; + tsk->thread.trap_no = error_code; do_sigsegv(regs, error_code, si_code, address); - return; + return; } no_context: - /* Are we prepared to handle this kernel fault? */ - fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK); - if (fixup) { - regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE; - return; - } - -/* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - if (space == 0) - printk(KERN_ALERT "Unable to handle kernel pointer dereference" - " at virtual kernel address %p\n", (void *)address); - else - printk(KERN_ALERT "Unable to handle kernel paging request" - " at virtual user address %p\n", (void *)address); - - die("Oops", regs, error_code); - do_exit(SIGKILL); - - -/* - * We ran out of memory, or some other thing happened to us that made - * us unable to handle the page fault gracefully. -*/ -out_of_memory: - up_read(&mm->mmap_sem); - if (is_init(tsk)) { - yield(); - down_read(&mm->mmap_sem); - goto survive; - } - printk("VM: killing process %s\n", tsk->comm); - if (regs->psw.mask & PSW_MASK_PSTATE) - do_exit(SIGKILL); - goto no_context; - -do_sigbus: - up_read(&mm->mmap_sem); - - /* - * Send a sigbus, regardless of whether we were in kernel - * or user mode. - */ - tsk->thread.prot_addr = address; - tsk->thread.trap_no = error_code; - force_sig(SIGBUS, tsk); - - /* Kernel mode? Handle exceptions or die */ - if (!(regs->psw.mask & PSW_MASK_PSTATE)) - goto no_context; + do_no_context(regs, error_code, address); } void __kprobes do_protection_exception(struct pt_regs *regs, unsigned long error_code) { + /* Protection exception is supressing, decrement psw address. */ regs->psw.addr -= (error_code >> 16); + /* + * Check for low-address protection. This needs to be treated + * as a special case because the translation exception code + * field is not guaranteed to contain valid data in this case. + */ + if (unlikely(!(S390_lowcore.trans_exc_code & 4))) { + do_low_address(regs, error_code); + return; + } do_exception(regs, 4, 1); } diff --git a/arch/sh/boards/hp6xx/pm.c b/arch/sh/boards/hp6xx/pm.c index d194773..8143d1b 100644 --- a/arch/sh/boards/hp6xx/pm.c +++ b/arch/sh/boards/hp6xx/pm.c @@ -27,9 +27,6 @@ #ifdef CONFIG_HD64461_ENABLER u16 hd64461_stbcr; #endif - if (state != PM_SUSPEND_MEM) - return -EINVAL; - #ifdef CONFIG_HD64461_ENABLER outb(0, HD64461_PCC1CSCIER); @@ -70,12 +67,9 @@ #endif return 0; } -/* - * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. - */ static struct pm_ops hp6x0_pm_ops = { - .pm_disk_mode = PM_DISK_FIRMWARE, .enter = hp6x0_pm_enter, + .valid = pm_valid_only_mem, }; static int __init hp6x0_pm_init(void) diff --git a/arch/sh/lib/Makefile b/arch/sh/lib/Makefile index b5681e3..0b9cca5 100644 --- a/arch/sh/lib/Makefile +++ b/arch/sh/lib/Makefile @@ -3,7 +3,7 @@ # Makefile for SuperH-specific library f # lib-y = delay.o memset.o memmove.o memchr.o \ - checksum.o strcasecmp.o strlen.o div64.o udivdi3.o \ + checksum.o strlen.o div64.o udivdi3.o \ div64-generic.o memcpy-y := memcpy.o diff --git a/arch/sh/lib/strcasecmp.c b/arch/sh/lib/strcasecmp.c deleted file mode 100644 index 4e57a21..0000000 --- a/arch/sh/lib/strcasecmp.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * linux/arch/alpha/lib/strcasecmp.c - */ - -#include - - -/* We handle nothing here except the C locale. Since this is used in - only one place, on strings known to contain only 7 bit ASCII, this - is ok. */ - -int strcasecmp(const char *a, const char *b) -{ - int ca, cb; - - do { - ca = *a++ & 0xff; - cb = *b++ & 0xff; - if (ca >= 'A' && ca <= 'Z') - ca += 'a' - 'A'; - if (cb >= 'A' && cb <= 'Z') - cb += 'a' - 'A'; - } while (ca == cb && ca != '\0'); - - return ca - cb; -} diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c index ba58c3a..7bb86b9 100644 --- a/arch/sparc/kernel/ebus.c +++ b/arch/sparc/kernel/ebus.c @@ -25,7 +25,7 @@ #include struct linux_ebus *ebus_chain = NULL; /* We are together with pcic.c under CONFIG_PCI. */ -extern unsigned int pcic_pin_to_irq(unsigned int, char *name); +extern unsigned int pcic_pin_to_irq(unsigned int, const char *name); /* * IRQ Blacklist @@ -69,7 +69,7 @@ static inline unsigned long ebus_alloc(s /* */ -int __init ebus_blacklist_irq(char *name) +int __init ebus_blacklist_irq(const char *name) { struct ebus_device_irq *dp; @@ -86,8 +86,8 @@ int __init ebus_blacklist_irq(char *name void __init fill_ebus_child(struct device_node *dp, struct linux_ebus_child *dev) { - int *regs; - int *irqs; + const int *regs; + const int *irqs; int i, len; dev->prom_node = dp; @@ -146,9 +146,9 @@ void __init fill_ebus_child(struct devic void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev) { - struct linux_prom_registers *regs; + const struct linux_prom_registers *regs; struct linux_ebus_child *child; - int *irqs; + const int *irqs; int i, n, len; unsigned long baseaddr; @@ -269,7 +269,7 @@ void __init fill_ebus_device(struct devi void __init ebus_init(void) { - struct linux_prom_pci_registers *regs; + const struct linux_prom_pci_registers *regs; struct linux_pbm_info *pbm; struct linux_ebus_device *dev; struct linux_ebus *ebus; diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c index 48c24f7..fd7f8cb 100644 --- a/arch/sparc/kernel/of_device.c +++ b/arch/sparc/kernel/of_device.c @@ -210,7 +210,7 @@ struct of_bus { int *addrc, int *sizec); int (*map)(u32 *addr, const u32 *range, int na, int ns, int pna); - unsigned int (*get_flags)(u32 *addr); + unsigned int (*get_flags)(const u32 *addr); }; /* @@ -270,7 +270,7 @@ static int of_bus_default_map(u32 *addr, return 0; } -static unsigned int of_bus_default_get_flags(u32 *addr) +static unsigned int of_bus_default_get_flags(const u32 *addr) { return IORESOURCE_MEM; } @@ -334,7 +334,7 @@ static int of_bus_pci_map(u32 *addr, con return 0; } -static unsigned int of_bus_pci_get_flags(u32 *addr) +static unsigned int of_bus_pci_get_flags(const u32 *addr) { unsigned int flags = 0; u32 w = addr[0]; @@ -375,7 +375,7 @@ static int of_bus_sbus_map(u32 *addr, co return of_bus_default_map(addr, range, na, ns, pna); } -static unsigned int of_bus_sbus_get_flags(u32 *addr) +static unsigned int of_bus_sbus_get_flags(const u32 *addr) { return IORESOURCE_MEM; } @@ -432,7 +432,7 @@ static int __init build_one_resource(str u32 *addr, int na, int ns, int pna) { - u32 *ranges; + const u32 *ranges; unsigned int rlen; int rone; @@ -470,7 +470,7 @@ static void __init build_device_resource struct of_bus *bus; int na, ns; int index, num_reg; - void *preg; + const void *preg; if (!parent) return; @@ -492,7 +492,7 @@ static void __init build_device_resource for (index = 0; index < num_reg; index++) { struct resource *r = &op->resource[index]; u32 addr[OF_MAX_ADDR_CELLS]; - u32 *reg = (preg + (index * ((na + ns) * 4))); + const u32 *reg = (preg + (index * ((na + ns) * 4))); struct device_node *dp = op->node; struct device_node *pp = p_op->node; struct of_bus *pbus, *dbus; @@ -559,7 +559,7 @@ static struct of_device * __init scan_on struct device *parent) { struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL); - struct linux_prom_irqs *intr; + const struct linux_prom_irqs *intr; int len, i; if (!op) @@ -579,7 +579,8 @@ static struct of_device * __init scan_on for (i = 0; i < op->num_irqs; i++) op->irqs[i] = intr[i].pri; } else { - unsigned int *irq = of_get_property(dp, "interrupts", &len); + const unsigned int *irq = + of_get_property(dp, "interrupts", &len); if (irq) { op->num_irqs = len / sizeof(unsigned int); @@ -594,7 +595,7 @@ static struct of_device * __init scan_on 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0, }; struct device_node *io_unit, *sbi = dp->parent; - struct linux_prom_registers *regs; + const struct linux_prom_registers *regs; int board, slot; while (sbi) { diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 1c927c5..5ca7e8f 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -37,8 +37,6 @@ #include #include -unsigned int pcic_pin_to_irq(unsigned int pin, char *name); - /* * I studied different documents and many live PROMs both from 2.30 * family and 3.xx versions. I came to the amazing conclusion: there is @@ -681,7 +679,7 @@ void __devinit pcibios_fixup_bus(struct * pcic_pin_to_irq() is exported to ebus.c. */ unsigned int -pcic_pin_to_irq(unsigned int pin, char *name) +pcic_pin_to_irq(unsigned int pin, const char *name) { struct linux_pcic *pcic = &pcic0; unsigned int irq; diff --git a/arch/sparc/kernel/prom.c b/arch/sparc/kernel/prom.c index 2cc302b..eed140b 100644 --- a/arch/sparc/kernel/prom.c +++ b/arch/sparc/kernel/prom.c @@ -32,12 +32,13 @@ static struct device_node *allnodes; */ static DEFINE_RWLOCK(devtree_lock); -int of_device_is_compatible(struct device_node *device, const char *compat) +int of_device_is_compatible(const struct device_node *device, + const char *compat) { const char* cp; int cplen, l; - cp = (char *) of_get_property(device, "compatible", &cplen); + cp = of_get_property(device, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) { @@ -150,13 +151,14 @@ struct device_node *of_find_compatible_n } EXPORT_SYMBOL(of_find_compatible_node); -struct property *of_find_property(struct device_node *np, const char *name, +struct property *of_find_property(const struct device_node *np, + const char *name, int *lenp) { struct property *pp; for (pp = np->properties; pp != 0; pp = pp->next) { - if (strcmp(pp->name, name) == 0) { + if (strcasecmp(pp->name, name) == 0) { if (lenp != 0) *lenp = pp->length; break; @@ -170,7 +172,8 @@ EXPORT_SYMBOL(of_find_property); * Find a property with a given name for a given node * and return the value. */ -void *of_get_property(struct device_node *np, const char *name, int *lenp) +const void *of_get_property(const struct device_node *np, const char *name, + int *lenp) { struct property *pp = of_find_property(np,name,lenp); return pp ? pp->value : NULL; @@ -192,7 +195,7 @@ EXPORT_SYMBOL(of_getintprop_default); int of_n_addr_cells(struct device_node *np) { - int* ip; + const int* ip; do { if (np->parent) np = np->parent; @@ -207,7 +210,7 @@ EXPORT_SYMBOL(of_n_addr_cells); int of_n_size_cells(struct device_node *np) { - int* ip; + const int* ip; do { if (np->parent) np = np->parent; @@ -239,7 +242,7 @@ int of_set_property(struct device_node * while (*prevp) { struct property *prop = *prevp; - if (!strcmp(prop->name, name)) { + if (!strcasecmp(prop->name, name)) { void *old_val = prop->value; int ret; diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index 9bb1240..f1401b5 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -301,7 +301,7 @@ #ifndef CONFIG_SUN4 static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dp = op->node; - char *model = of_get_property(dp, "model", NULL); + const char *model = of_get_property(dp, "model", NULL); if (!model) return -ENODEV; diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 1a6348b..590a41b 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -19,6 +19,14 @@ config SPARC64 SPARC64 ports; its web page is available at . +config GENERIC_TIME + bool + default y + +config GENERIC_CLOCKEVENTS + bool + default y + config 64BIT def_bool y @@ -34,10 +42,6 @@ config LOCKDEP_SUPPORT bool default y -config TIME_INTERPOLATION - bool - default y - config ARCH_MAY_HAVE_PC_FDC bool default y @@ -113,6 +117,8 @@ config GENERIC_HARDIRQS menu "General machine setup" +source "kernel/time/Kconfig" + config SMP bool "Symmetric multi-processing support" ---help--- @@ -214,6 +220,7 @@ config ARCH_SPARSEMEM_ENABLE config ARCH_SPARSEMEM_DEFAULT def_bool y + select SPARSEMEM_STATIC config LARGE_ALLOCS def_bool y diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c index e724c54..c65b2f9 100644 --- a/arch/sparc64/kernel/central.c +++ b/arch/sparc64/kernel/central.c @@ -32,7 +32,7 @@ static void central_probe_failure(int li static void central_ranges_init(struct linux_central *central) { struct device_node *dp = central->prom_node; - void *pval; + const void *pval; int len; central->num_central_ranges = 0; @@ -47,7 +47,7 @@ static void central_ranges_init(struct l static void fhc_ranges_init(struct linux_fhc *fhc) { struct device_node *dp = fhc->prom_node; - void *pval; + const void *pval; int len; fhc->num_fhc_ranges = 0; @@ -119,7 +119,7 @@ static unsigned long prom_reg_to_paddr(s static void probe_other_fhcs(void) { struct device_node *dp; - struct linux_prom64_registers *fpregs; + const struct linux_prom64_registers *fpregs; for_each_node_by_name(dp, "fhc") { struct linux_fhc *fhc; @@ -190,7 +190,8 @@ static void probe_clock_board(struct lin struct device_node *fp) { struct device_node *dp; - struct linux_prom_registers cregs[3], *pr; + struct linux_prom_registers cregs[3]; + const struct linux_prom_registers *pr; int nslots, tmp, nregs; dp = fp->child; @@ -299,7 +300,8 @@ static void init_all_fhc_hw(void) void central_probe(void) { - struct linux_prom_registers fpregs[6], *pr; + struct linux_prom_registers fpregs[6]; + const struct linux_prom_registers *pr; struct linux_fhc *fhc; struct device_node *dp, *fp; int err; diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c index 9699abe..777d345 100644 --- a/arch/sparc64/kernel/chmc.c +++ b/arch/sparc64/kernel/chmc.c @@ -343,8 +343,8 @@ static int init_one_mctrl(struct device_ { struct mctrl_info *mp = kzalloc(sizeof(*mp), GFP_KERNEL); int portid = of_getintprop_default(dp, "portid", -1); - struct linux_prom64_registers *regs; - void *pval; + const struct linux_prom64_registers *regs; + const void *pval; int len; if (!mp) diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 35bf895..0ace17b 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -285,7 +285,7 @@ static void __init fill_ebus_child(struc int non_standard_regs) { struct of_device *op; - int *regs; + const int *regs; int i, len; dev->prom_node = dp; @@ -438,11 +438,9 @@ static struct pci_dev *find_next_ebus(st void __init ebus_init(void) { - struct pci_pbm_info *pbm; struct linux_ebus_device *dev; struct linux_ebus *ebus; struct pci_dev *pdev; - struct pcidev_cookie *cookie; struct device_node *dp; int is_rio; int num_ebus = 0; @@ -453,8 +451,7 @@ void __init ebus_init(void) return; } - cookie = pdev->sysdata; - dp = cookie->prom_node; + dp = pci_device_to_OF_node(pdev); ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus)); ebus->next = NULL; @@ -480,8 +477,7 @@ void __init ebus_init(void) break; } ebus->is_rio = is_rio; - cookie = pdev->sysdata; - dp = cookie->prom_node; + dp = pci_device_to_OF_node(pdev); continue; } printk("ebus%d:", num_ebus); @@ -489,7 +485,6 @@ void __init ebus_init(void) ebus->index = num_ebus; ebus->prom_node = dp; ebus->self = pdev; - ebus->parent = pbm = cookie->pbm; ebus->ofdev.node = dp; ebus->ofdev.dev.parent = &pdev->dev; @@ -531,8 +526,7 @@ void __init ebus_init(void) if (!pdev) break; - cookie = pdev->sysdata; - dp = cookie->prom_node; + dp = pci_device_to_OF_node(pdev); ebus->next = ebus_alloc(sizeof(struct linux_ebus)); ebus = ebus->next; diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index c443db1..6241e3d 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -589,32 +589,6 @@ void ack_bad_irq(unsigned int virt_irq) ino, virt_irq); } -#ifndef CONFIG_SMP -extern irqreturn_t timer_interrupt(int, void *); - -void timer_irq(int irq, struct pt_regs *regs) -{ - unsigned long clr_mask = 1 << irq; - unsigned long tick_mask = tick_ops->softint_mask; - struct pt_regs *old_regs; - - if (get_softint() & tick_mask) { - irq = 0; - clr_mask = tick_mask; - } - clear_softint(clr_mask); - - old_regs = set_irq_regs(regs); - irq_enter(); - - kstat_this_cpu.irqs[0]++; - timer_interrupt(irq, NULL); - - irq_exit(); - set_irq_regs(old_regs); -} -#endif - void handler_irq(int irq, struct pt_regs *regs) { struct ino_bucket *bucket; @@ -653,7 +627,7 @@ static u64 prom_limit0, prom_limit1; static void map_prom_timers(void) { struct device_node *dp; - unsigned int *addr; + const unsigned int *addr; /* PROM timer node hangs out in the top level of device siblings... */ dp = of_find_node_by_path("/"); diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c index 98721a8..6a6882e 100644 --- a/arch/sparc64/kernel/isa.c +++ b/arch/sparc64/kernel/isa.c @@ -24,27 +24,9 @@ static void __init report_dev(struct spa static void __init isa_dev_get_resource(struct sparc_isa_device *isa_dev) { - struct linux_prom_registers *pregs; - unsigned long base, len; - int prop_len; - - pregs = of_get_property(isa_dev->prom_node, "reg", &prop_len); - if (!pregs) - return; - - /* Only the first one is interesting. */ - len = pregs[0].reg_size; - base = (((unsigned long)pregs[0].which_io << 32) | - (unsigned long)pregs[0].phys_addr); - base += isa_dev->bus->parent->io_space.start; - - isa_dev->resource.start = base; - isa_dev->resource.end = (base + len - 1UL); - isa_dev->resource.flags = IORESOURCE_IO; - isa_dev->resource.name = isa_dev->prom_node->name; + struct of_device *op = of_find_device_by_node(isa_dev->prom_node); - request_resource(&isa_dev->bus->parent->io_space, - &isa_dev->resource); + memcpy(&isa_dev->resource, &op->resource[0], sizeof(struct resource)); } static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev) @@ -158,19 +140,10 @@ void __init isa_init(void) pdev = NULL; while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) { - struct pcidev_cookie *pdev_cookie; - struct pci_pbm_info *pbm; struct sparc_isa_bridge *isa_br; struct device_node *dp; - pdev_cookie = pdev->sysdata; - if (!pdev_cookie) { - printk("ISA: Warning, ISA bridge ignored due to " - "lack of OBP data.\n"); - continue; - } - pbm = pdev_cookie->pbm; - dp = pdev_cookie->prom_node; + dp = pci_device_to_OF_node(pdev); isa_br = kzalloc(sizeof(*isa_br), GFP_KERNEL); if (!isa_br) { @@ -195,10 +168,9 @@ void __init isa_init(void) isa_br->next = isa_chain; isa_chain = isa_br; - isa_br->parent = pbm; isa_br->self = pdev; isa_br->index = index++; - isa_br->prom_node = pdev_cookie->prom_node; + isa_br->prom_node = dp; printk("isa%d:", isa_br->index); diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index fb9bf1e..9ac9a30 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c @@ -245,7 +245,7 @@ struct of_bus { int *addrc, int *sizec); int (*map)(u32 *addr, const u32 *range, int na, int ns, int pna); - unsigned int (*get_flags)(u32 *addr); + unsigned int (*get_flags)(const u32 *addr); }; /* @@ -305,7 +305,7 @@ static int of_bus_default_map(u32 *addr, return 0; } -static unsigned int of_bus_default_get_flags(u32 *addr) +static unsigned int of_bus_default_get_flags(const u32 *addr) { return IORESOURCE_MEM; } @@ -317,6 +317,11 @@ static unsigned int of_bus_default_get_f static int of_bus_pci_match(struct device_node *np) { if (!strcmp(np->type, "pci") || !strcmp(np->type, "pciex")) { + const char *model = of_get_property(np, "model", NULL); + + if (model && !strcmp(model, "SUNW,simba")) + return 0; + /* Do not do PCI specific frobbing if the * PCI bridge lacks a ranges property. We * want to pass it through up to the next @@ -332,6 +337,21 @@ static int of_bus_pci_match(struct devic return 0; } +static int of_bus_simba_match(struct device_node *np) +{ + const char *model = of_get_property(np, "model", NULL); + + if (model && !strcmp(model, "SUNW,simba")) + return 1; + return 0; +} + +static int of_bus_simba_map(u32 *addr, const u32 *range, + int na, int ns, int pna) +{ + return 0; +} + static void of_bus_pci_count_cells(struct device_node *np, int *addrc, int *sizec) { @@ -369,7 +389,7 @@ static int of_bus_pci_map(u32 *addr, con return 0; } -static unsigned int of_bus_pci_get_flags(u32 *addr) +static unsigned int of_bus_pci_get_flags(const u32 *addr) { unsigned int flags = 0; u32 w = addr[0]; @@ -436,6 +456,15 @@ static struct of_bus of_busses[] = { .map = of_bus_pci_map, .get_flags = of_bus_pci_get_flags, }, + /* SIMBA */ + { + .name = "simba", + .addr_prop_name = "assigned-addresses", + .match = of_bus_simba_match, + .count_cells = of_bus_pci_count_cells, + .map = of_bus_simba_map, + .get_flags = of_bus_pci_get_flags, + }, /* SBUS */ { .name = "sbus", @@ -482,7 +511,7 @@ static int __init build_one_resource(str u32 *addr, int na, int ns, int pna) { - u32 *ranges; + const u32 *ranges; unsigned int rlen; int rone; @@ -513,7 +542,7 @@ static int __init build_one_resource(str static int __init use_1to1_mapping(struct device_node *pp) { - char *model; + const char *model; /* If this is on the PMU bus, don't try to translate it even * if a ranges property exists. @@ -548,7 +577,7 @@ static void __init build_device_resource struct of_bus *bus; int na, ns; int index, num_reg; - void *preg; + const void *preg; if (!parent) return; @@ -578,7 +607,7 @@ static void __init build_device_resource for (index = 0; index < num_reg; index++) { struct resource *r = &op->resource[index]; u32 addr[OF_MAX_ADDR_CELLS]; - u32 *reg = (preg + (index * ((na + ns) * 4))); + const u32 *reg = (preg + (index * ((na + ns) * 4))); struct device_node *dp = op->node; struct device_node *pp = p_op->node; struct of_bus *pbus, *dbus; @@ -643,14 +672,14 @@ static void __init build_device_resource static struct device_node * __init apply_interrupt_map(struct device_node *dp, struct device_node *pp, - u32 *imap, int imlen, u32 *imask, + const u32 *imap, int imlen, const u32 *imask, unsigned int *irq_p) { struct device_node *cp; unsigned int irq = *irq_p; struct of_bus *bus; phandle handle; - u32 *reg; + const u32 *reg; int na, num_reg, i; bus = of_match_bus(pp); @@ -705,7 +734,7 @@ static unsigned int __init pci_irq_swizz struct device_node *pp, unsigned int irq) { - struct linux_prom_pci_registers *regs; + const struct linux_prom_pci_registers *regs; unsigned int bus, devfn, slot, ret; if (irq < 1 || irq > 4) @@ -730,12 +759,6 @@ static unsigned int __init pci_irq_swizz * D: 2-bit slot number, derived from PCI device number as * (dev - 1) for bus A, or (dev - 2) for bus B * L: 2-bit line number - * - * Actually, more "portable" way to calculate the funky - * slot number is to subtract pbm->pci_first_slot from the - * device number, and that's exactly what the pre-OF - * sparc64 code did, but we're building this stuff generically - * using the OBP tree, not in the PCI controller layer. */ if (bus & 0x80) { /* PBM-A */ @@ -794,7 +817,7 @@ static unsigned int __init build_one_dev pp = dp->parent; ip = NULL; while (pp) { - void *imap, *imsk; + const void *imap, *imsk; int imlen; imap = of_get_property(pp, "interrupt-map", &imlen); @@ -859,7 +882,7 @@ static struct of_device * __init scan_on struct device *parent) { struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL); - unsigned int *irq; + const unsigned int *irq; int len, i; if (!op) diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 1210988..023af41 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -1,9 +1,11 @@ -/* $Id: pci.c,v 1.39 2002/01/05 01:13:43 davem Exp $ - * pci.c: UltraSparc PCI controller support. +/* pci.c: UltraSparc PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) + * + * OF tree based PCI bus probing taken from the PowerPC port + * with minor modifications, see there for credits. */ #include @@ -24,6 +26,9 @@ #include #include #include #include +#include + +#include "pci_impl.h" unsigned long pci_memspace_mask = 0xffffffffUL; @@ -277,10 +282,10 @@ int __init pcic_present(void) return pci_controller_scan(pci_is_controller); } -struct pci_iommu_ops *pci_iommu_ops; +const struct pci_iommu_ops *pci_iommu_ops; EXPORT_SYMBOL(pci_iommu_ops); -extern struct pci_iommu_ops pci_sun4u_iommu_ops, +extern const struct pci_iommu_ops pci_sun4u_iommu_ops, pci_sun4v_iommu_ops; /* Find each controller in the system, attach and initialize @@ -300,6 +305,467 @@ static void __init pci_controller_probe( pci_controller_scan(pci_controller_init); } +static unsigned long pci_parse_of_flags(u32 addr0) +{ + unsigned long flags = 0; + + if (addr0 & 0x02000000) { + flags = IORESOURCE_MEM | PCI_BASE_ADDRESS_SPACE_MEMORY; + flags |= (addr0 >> 22) & PCI_BASE_ADDRESS_MEM_TYPE_64; + flags |= (addr0 >> 28) & PCI_BASE_ADDRESS_MEM_TYPE_1M; + if (addr0 & 0x40000000) + flags |= IORESOURCE_PREFETCH + | PCI_BASE_ADDRESS_MEM_PREFETCH; + } else if (addr0 & 0x01000000) + flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO; + return flags; +} + +/* The of_device layer has translated all of the assigned-address properties + * into physical address resources, we only have to figure out the register + * mapping. + */ +static void pci_parse_of_addrs(struct of_device *op, + struct device_node *node, + struct pci_dev *dev) +{ + struct resource *op_res; + const u32 *addrs; + int proplen; + + addrs = of_get_property(node, "assigned-addresses", &proplen); + if (!addrs) + return; + printk(" parse addresses (%d bytes) @ %p\n", proplen, addrs); + op_res = &op->resource[0]; + for (; proplen >= 20; proplen -= 20, addrs += 5, op_res++) { + struct resource *res; + unsigned long flags; + int i; + + flags = pci_parse_of_flags(addrs[0]); + if (!flags) + continue; + i = addrs[0] & 0xff; + printk(" start: %lx, end: %lx, i: %x\n", + op_res->start, op_res->end, i); + + if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) { + res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2]; + } else if (i == dev->rom_base_reg) { + res = &dev->resource[PCI_ROM_RESOURCE]; + flags |= IORESOURCE_READONLY | IORESOURCE_CACHEABLE; + } else { + printk(KERN_ERR "PCI: bad cfg reg num 0x%x\n", i); + continue; + } + res->start = op_res->start; + res->end = op_res->end; + res->flags = flags; + res->name = pci_name(dev); + } +} + +struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, + struct device_node *node, + struct pci_bus *bus, int devfn, + int host_controller) +{ + struct dev_archdata *sd; + struct pci_dev *dev; + const char *type; + u32 class; + + dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); + if (!dev) + return NULL; + + sd = &dev->dev.archdata; + sd->iommu = pbm->iommu; + sd->stc = &pbm->stc; + sd->host_controller = pbm; + sd->prom_node = node; + sd->op = of_find_device_by_node(node); + sd->msi_num = 0xffffffff; + + type = of_get_property(node, "device_type", NULL); + if (type == NULL) + type = ""; + + printk(" create device, devfn: %x, type: %s hostcontroller(%d)\n", + devfn, type, host_controller); + + dev->bus = bus; + dev->sysdata = node; + dev->dev.parent = bus->bridge; + dev->dev.bus = &pci_bus_type; + dev->devfn = devfn; + dev->multifunction = 0; /* maybe a lie? */ + + if (host_controller) { + dev->vendor = 0x108e; + dev->device = 0x8000; + dev->subsystem_vendor = 0x0000; + dev->subsystem_device = 0x0000; + dev->cfg_size = 256; + dev->class = PCI_CLASS_BRIDGE_HOST << 8; + sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus), + 0x00, PCI_SLOT(devfn), PCI_FUNC(devfn)); + } else { + dev->vendor = of_getintprop_default(node, "vendor-id", 0xffff); + dev->device = of_getintprop_default(node, "device-id", 0xffff); + dev->subsystem_vendor = + of_getintprop_default(node, "subsystem-vendor-id", 0); + dev->subsystem_device = + of_getintprop_default(node, "subsystem-id", 0); + + dev->cfg_size = pci_cfg_space_size(dev); + + /* We can't actually use the firmware value, we have + * to read what is in the register right now. One + * reason is that in the case of IDE interfaces the + * firmware can sample the value before the the IDE + * interface is programmed into native mode. + */ + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); + dev->class = class >> 8; + + sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus), + dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); + } + printk(" class: 0x%x device name: %s\n", + dev->class, pci_name(dev)); + + dev->current_state = 4; /* unknown power state */ + dev->error_state = pci_channel_io_normal; + + if (host_controller) { + dev->hdr_type = PCI_HEADER_TYPE_BRIDGE; + dev->rom_base_reg = PCI_ROM_ADDRESS1; + dev->irq = PCI_IRQ_NONE; + } else { + if (!strcmp(type, "pci") || !strcmp(type, "pciex")) { + /* a PCI-PCI bridge */ + dev->hdr_type = PCI_HEADER_TYPE_BRIDGE; + dev->rom_base_reg = PCI_ROM_ADDRESS1; + } else if (!strcmp(type, "cardbus")) { + dev->hdr_type = PCI_HEADER_TYPE_CARDBUS; + } else { + dev->hdr_type = PCI_HEADER_TYPE_NORMAL; + dev->rom_base_reg = PCI_ROM_ADDRESS; + + dev->irq = sd->op->irqs[0]; + if (dev->irq == 0xffffffff) + dev->irq = PCI_IRQ_NONE; + } + } + pci_parse_of_addrs(sd->op, node, dev); + + printk(" adding to system ...\n"); + + pci_device_add(dev, bus); + + return dev; +} + +static void __init apb_calc_first_last(u8 map, u32 *first_p, u32 *last_p) +{ + u32 idx, first, last; + + first = 8; + last = 0; + for (idx = 0; idx < 8; idx++) { + if ((map & (1 << idx)) != 0) { + if (first > idx) + first = idx; + if (last < idx) + last = idx; + } + } + + *first_p = first; + *last_p = last; +} + +static void __init pci_resource_adjust(struct resource *res, + struct resource *root) +{ + res->start += root->start; + res->end += root->start; +} + +/* Cook up fake bus resources for SUNW,simba PCI bridges which lack + * a proper 'ranges' property. + */ +static void __init apb_fake_ranges(struct pci_dev *dev, + struct pci_bus *bus, + struct pci_pbm_info *pbm) +{ + struct resource *res; + u32 first, last; + u8 map; + + pci_read_config_byte(dev, APB_IO_ADDRESS_MAP, &map); + apb_calc_first_last(map, &first, &last); + res = bus->resource[0]; + res->start = (first << 21); + res->end = (last << 21) + ((1 << 21) - 1); + res->flags = IORESOURCE_IO; + pci_resource_adjust(res, &pbm->io_space); + + pci_read_config_byte(dev, APB_MEM_ADDRESS_MAP, &map); + apb_calc_first_last(map, &first, &last); + res = bus->resource[1]; + res->start = (first << 21); + res->end = (last << 21) + ((1 << 21) - 1); + res->flags = IORESOURCE_MEM; + pci_resource_adjust(res, &pbm->mem_space); +} + +static void __init pci_of_scan_bus(struct pci_pbm_info *pbm, + struct device_node *node, + struct pci_bus *bus); + +#define GET_64BIT(prop, i) ((((u64) (prop)[(i)]) << 32) | (prop)[(i)+1]) + +void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, + struct device_node *node, + struct pci_dev *dev) +{ + struct pci_bus *bus; + const u32 *busrange, *ranges; + int len, i, simba; + struct resource *res; + unsigned int flags; + u64 size; + + printk("of_scan_pci_bridge(%s)\n", node->full_name); + + /* parse bus-range property */ + busrange = of_get_property(node, "bus-range", &len); + if (busrange == NULL || len != 8) { + printk(KERN_DEBUG "Can't get bus-range for PCI-PCI bridge %s\n", + node->full_name); + return; + } + ranges = of_get_property(node, "ranges", &len); + simba = 0; + if (ranges == NULL) { + const char *model = of_get_property(node, "model", NULL); + if (model && !strcmp(model, "SUNW,simba")) { + simba = 1; + } else { + printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", + node->full_name); + return; + } + } + + bus = pci_add_new_bus(dev->bus, dev, busrange[0]); + if (!bus) { + printk(KERN_ERR "Failed to create pci bus for %s\n", + node->full_name); + return; + } + + bus->primary = dev->bus->number; + bus->subordinate = busrange[1]; + bus->bridge_ctl = 0; + + /* parse ranges property, or cook one up by hand for Simba */ + /* PCI #address-cells == 3 and #size-cells == 2 always */ + res = &dev->resource[PCI_BRIDGE_RESOURCES]; + for (i = 0; i < PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES; ++i) { + res->flags = 0; + bus->resource[i] = res; + ++res; + } + if (simba) { + apb_fake_ranges(dev, bus, pbm); + goto simba_cont; + } + i = 1; + for (; len >= 32; len -= 32, ranges += 8) { + struct resource *root; + + flags = pci_parse_of_flags(ranges[0]); + size = GET_64BIT(ranges, 6); + if (flags == 0 || size == 0) + continue; + if (flags & IORESOURCE_IO) { + res = bus->resource[0]; + if (res->flags) { + printk(KERN_ERR "PCI: ignoring extra I/O range" + " for bridge %s\n", node->full_name); + continue; + } + root = &pbm->io_space; + } else { + if (i >= PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES) { + printk(KERN_ERR "PCI: too many memory ranges" + " for bridge %s\n", node->full_name); + continue; + } + res = bus->resource[i]; + ++i; + root = &pbm->mem_space; + } + + res->start = GET_64BIT(ranges, 1); + res->end = res->start + size - 1; + res->flags = flags; + + /* Another way to implement this would be to add an of_device + * layer routine that can calculate a resource for a given + * range property value in a PCI device. + */ + pci_resource_adjust(res, root); + } +simba_cont: + sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), + bus->number); + printk(" bus name: %s\n", bus->name); + + pci_of_scan_bus(pbm, node, bus); +} + +static void __init pci_of_scan_bus(struct pci_pbm_info *pbm, + struct device_node *node, + struct pci_bus *bus) +{ + struct device_node *child; + const u32 *reg; + int reglen, devfn; + struct pci_dev *dev; + + printk("PCI: scan_bus[%s] bus no %d\n", + node->full_name, bus->number); + + child = NULL; + while ((child = of_get_next_child(node, child)) != NULL) { + printk(" * %s\n", child->full_name); + reg = of_get_property(child, "reg", ®len); + if (reg == NULL || reglen < 20) + continue; + devfn = (reg[0] >> 8) & 0xff; + + /* create a new pci_dev for this device */ + dev = of_create_pci_dev(pbm, child, bus, devfn, 0); + if (!dev) + continue; + printk("PCI: dev header type: %x\n", dev->hdr_type); + + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + of_scan_pci_bridge(pbm, child, dev); + } +} + +static ssize_t +show_pciobppath_attr(struct device * dev, struct device_attribute * attr, char * buf) +{ + struct pci_dev *pdev; + struct device_node *dp; + + pdev = to_pci_dev(dev); + dp = pdev->dev.archdata.prom_node; + + return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name); +} + +static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL); + +static void __devinit pci_bus_register_of_sysfs(struct pci_bus *bus) +{ + struct pci_dev *dev; + struct pci_bus *child_bus; + int err; + + list_for_each_entry(dev, &bus->devices, bus_list) { + /* we don't really care if we can create this file or + * not, but we need to assign the result of the call + * or the world will fall under alien invasion and + * everybody will be frozen on a spaceship ready to be + * eaten on alpha centauri by some green and jelly + * humanoid. + */ + err = sysfs_create_file(&dev->dev.kobj, &dev_attr_obppath.attr); + } + list_for_each_entry(child_bus, &bus->children, node) + pci_bus_register_of_sysfs(child_bus); +} + +int pci_host_bridge_read_pci_cfg(struct pci_bus *bus_dev, + unsigned int devfn, + int where, int size, + u32 *value) +{ + static u8 fake_pci_config[] = { + 0x8e, 0x10, /* Vendor: 0x108e (Sun) */ + 0x00, 0x80, /* Device: 0x8000 (PBM) */ + 0x46, 0x01, /* Command: 0x0146 (SERR, PARITY, MASTER, MEM) */ + 0xa0, 0x22, /* Status: 0x02a0 (DEVSEL_MED, FB2B, 66MHZ) */ + 0x00, 0x00, 0x00, 0x06, /* Class: 0x06000000 host bridge */ + 0x00, /* Cacheline: 0x00 */ + 0x40, /* Latency: 0x40 */ + 0x00, /* Header-Type: 0x00 normal */ + }; + + *value = 0; + if (where >= 0 && where < sizeof(fake_pci_config) && + (where + size) >= 0 && + (where + size) < sizeof(fake_pci_config) && + size <= sizeof(u32)) { + while (size--) { + *value <<= 8; + *value |= fake_pci_config[where + size]; + } + } + + return PCIBIOS_SUCCESSFUL; +} + +int pci_host_bridge_write_pci_cfg(struct pci_bus *bus_dev, + unsigned int devfn, + int where, int size, + u32 value) +{ + return PCIBIOS_SUCCESSFUL; +} + +struct pci_bus * __init pci_scan_one_pbm(struct pci_pbm_info *pbm) +{ + struct pci_controller_info *p = pbm->parent; + struct device_node *node = pbm->prom_node; + struct pci_dev *host_pdev; + struct pci_bus *bus; + + printk("PCI: Scanning PBM %s\n", node->full_name); + + /* XXX parent device? XXX */ + bus = pci_create_bus(NULL, pbm->pci_first_busno, p->pci_ops, pbm); + if (!bus) { + printk(KERN_ERR "Failed to create bus for %s\n", + node->full_name); + return NULL; + } + bus->secondary = pbm->pci_first_busno; + bus->subordinate = pbm->pci_last_busno; + + bus->resource[0] = &pbm->io_space; + bus->resource[1] = &pbm->mem_space; + + /* Create the dummy host bridge and link it in. */ + host_pdev = of_create_pci_dev(pbm, node, bus, 0x00, 1); + bus->self = host_pdev; + + pci_of_scan_bus(pbm, node, bus); + pci_bus_add_devices(bus); + pci_bus_register_of_sysfs(bus); + + return bus; +} + static void __init pci_scan_each_controller_bus(void) { struct pci_controller_info *p; @@ -360,8 +826,33 @@ void pcibios_align_resource(void *data, { } -int pcibios_enable_device(struct pci_dev *pdev, int mask) +int pcibios_enable_device(struct pci_dev *dev, int mask) { + u16 cmd, oldcmd; + int i; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + oldcmd = cmd; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *res = &dev->resource[i]; + + /* Only set up the requested stuff */ + if (!(mask & (1<flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (res->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + + if (cmd != oldcmd) { + printk(KERN_DEBUG "PCI: Enabling device: (%s), cmd %x\n", + pci_name(dev), cmd); + /* Enable the appropriate bits in the PCI command register. */ + pci_write_config_word(dev, PCI_COMMAND, cmd); + } return 0; } @@ -380,7 +871,7 @@ void pcibios_resource_to_bus(struct pci_ else root = &pbm->mem_space; - pbm->parent->resource_adjust(pdev, &zero_res, root); + pci_resource_adjust(&zero_res, root); region->start = res->start - zero_res.start; region->end = res->end - zero_res.start; @@ -401,7 +892,7 @@ void pcibios_bus_to_resource(struct pci_ else root = &pbm->mem_space; - pbm->parent->resource_adjust(pdev, res, root); + pci_resource_adjust(res, root); } EXPORT_SYMBOL(pcibios_bus_to_resource); @@ -422,55 +913,17 @@ char * __devinit pcibios_setup(char *str static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state) { - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm; + struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; struct pci_controller_info *p; unsigned long space_size, user_offset, user_size; - if (!pcp) - return -ENXIO; - pbm = pcp->pbm; - if (!pbm) - return -ENXIO; - p = pbm->parent; - if (p->pbms_same_domain) { - unsigned long lowest, highest; - - lowest = ~0UL; highest = 0UL; - if (mmap_state == pci_mmap_io) { - if (p->pbm_A.io_space.flags) { - lowest = p->pbm_A.io_space.start; - highest = p->pbm_A.io_space.end + 1; - } - if (p->pbm_B.io_space.flags) { - if (lowest > p->pbm_B.io_space.start) - lowest = p->pbm_B.io_space.start; - if (highest < p->pbm_B.io_space.end + 1) - highest = p->pbm_B.io_space.end + 1; - } - space_size = highest - lowest; - } else { - if (p->pbm_A.mem_space.flags) { - lowest = p->pbm_A.mem_space.start; - highest = p->pbm_A.mem_space.end + 1; - } - if (p->pbm_B.mem_space.flags) { - if (lowest > p->pbm_B.mem_space.start) - lowest = p->pbm_B.mem_space.start; - if (highest < p->pbm_B.mem_space.end + 1) - highest = p->pbm_B.mem_space.end + 1; - } - space_size = highest - lowest; - } + if (mmap_state == pci_mmap_io) { + space_size = (pbm->io_space.end - + pbm->io_space.start) + 1; } else { - if (mmap_state == pci_mmap_io) { - space_size = (pbm->io_space.end - - pbm->io_space.start) + 1; - } else { - space_size = (pbm->mem_space.end - - pbm->mem_space.start) + 1; - } + space_size = (pbm->mem_space.end - + pbm->mem_space.start) + 1; } /* Make sure the request is in range. */ @@ -481,31 +934,12 @@ static int __pci_mmap_make_offset_bus(st (user_offset + user_size) > space_size) return -EINVAL; - if (p->pbms_same_domain) { - unsigned long lowest = ~0UL; - - if (mmap_state == pci_mmap_io) { - if (p->pbm_A.io_space.flags) - lowest = p->pbm_A.io_space.start; - if (p->pbm_B.io_space.flags && - lowest > p->pbm_B.io_space.start) - lowest = p->pbm_B.io_space.start; - } else { - if (p->pbm_A.mem_space.flags) - lowest = p->pbm_A.mem_space.start; - if (p->pbm_B.mem_space.flags && - lowest > p->pbm_B.mem_space.start) - lowest = p->pbm_B.mem_space.start; - } - vma->vm_pgoff = (lowest + user_offset) >> PAGE_SHIFT; + if (mmap_state == pci_mmap_io) { + vma->vm_pgoff = (pbm->io_space.start + + user_offset) >> PAGE_SHIFT; } else { - if (mmap_state == pci_mmap_io) { - vma->vm_pgoff = (pbm->io_space.start + - user_offset) >> PAGE_SHIFT; - } else { - vma->vm_pgoff = (pbm->mem_space.start + - user_offset) >> PAGE_SHIFT; - } + vma->vm_pgoff = (pbm->mem_space.start + + user_offset) >> PAGE_SHIFT; } return 0; @@ -639,9 +1073,8 @@ int pci_domain_nr(struct pci_bus *pbus) struct pci_controller_info *p = pbm->parent; ret = p->index; - if (p->pbms_same_domain == 0) - ret = ((ret << 1) + - ((pbm == &pbm->parent->pbm_B) ? 1 : 0)); + ret = ((ret << 1) + + ((pbm == &pbm->parent->pbm_B) ? 1 : 0)); } return ret; @@ -651,8 +1084,7 @@ EXPORT_SYMBOL(pci_domain_nr); #ifdef CONFIG_PCI_MSI int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) { - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; + struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; struct pci_controller_info *p = pbm->parent; int virt_irq, err; @@ -670,8 +1102,7 @@ void arch_teardown_msi_irq(unsigned int { struct msi_desc *entry = get_irq_msi(virt_irq); struct pci_dev *pdev = entry->dev; - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; + struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; struct pci_controller_info *p = pbm->parent; if (!pbm->msi_num || !p->setup_msi_irq) @@ -683,9 +1114,7 @@ #endif /* !(CONFIG_PCI_MSI) */ struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) { - struct pcidev_cookie *pc = pdev->sysdata; - - return pc->op->node; + return pdev->dev.archdata.prom_node; } EXPORT_SYMBOL(pci_device_to_OF_node); diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 5a92cb9..1e6aeed 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -1,7 +1,6 @@ -/* $Id: pci_common.c,v 1.29 2002/02/01 00:56:03 davem Exp $ - * pci_common.c: PCI controller common support. +/* pci_common.c: PCI controller common support. * - * Copyright (C) 1999 David S. Miller (davem@redhat.com) + * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) */ #include @@ -16,748 +15,137 @@ #include #include "pci_impl.h" -/* Fix self device of BUS and hook it into BUS->self. - * The pci_scan_bus does not do this for the host bridge. - */ -void __init pci_fixup_host_bridge_self(struct pci_bus *pbus) -{ - struct pci_dev *pdev; - - list_for_each_entry(pdev, &pbus->devices, bus_list) { - if (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) { - pbus->self = pdev; - return; - } - } - - prom_printf("PCI: Critical error, cannot find host bridge PDEV.\n"); - prom_halt(); -} - -/* Find the OBP PROM device tree node for a PCI device. */ -static struct device_node * __init -find_device_prom_node(struct pci_pbm_info *pbm, struct pci_dev *pdev, - struct device_node *bus_node, - struct linux_prom_pci_registers **pregs, - int *nregs) +static void pci_register_legacy_regions(struct resource *io_res, + struct resource *mem_res) { - struct device_node *dp; - - *nregs = 0; - - /* - * Return the PBM's PROM node in case we are it's PCI device, - * as the PBM's reg property is different to standard PCI reg - * properties. We would delete this device entry otherwise, - * which confuses XFree86's device probing... - */ - if ((pdev->bus->number == pbm->pci_bus->number) && (pdev->devfn == 0) && - (pdev->vendor == PCI_VENDOR_ID_SUN) && - (pdev->device == PCI_DEVICE_ID_SUN_PBM || - pdev->device == PCI_DEVICE_ID_SUN_SCHIZO || - pdev->device == PCI_DEVICE_ID_SUN_TOMATILLO || - pdev->device == PCI_DEVICE_ID_SUN_SABRE || - pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD)) - return bus_node; - - dp = bus_node->child; - while (dp) { - struct linux_prom_pci_registers *regs; - struct property *prop; - int len; - - prop = of_find_property(dp, "reg", &len); - if (!prop) - goto do_next_sibling; - - regs = prop->value; - if (((regs[0].phys_hi >> 8) & 0xff) == pdev->devfn) { - *pregs = regs; - *nregs = len / sizeof(struct linux_prom_pci_registers); - return dp; - } - - do_next_sibling: - dp = dp->sibling; - } - - return NULL; -} + struct resource *p; -/* Older versions of OBP on PCI systems encode 64-bit MEM - * space assignments incorrectly, this fixes them up. We also - * take the opportunity here to hide other kinds of bogus - * assignments. - */ -static void __init fixup_obp_assignments(struct pci_dev *pdev, - struct pcidev_cookie *pcp) -{ - int i; - - if (pdev->vendor == PCI_VENDOR_ID_AL && - (pdev->device == PCI_DEVICE_ID_AL_M7101 || - pdev->device == PCI_DEVICE_ID_AL_M1533)) { - int i; - - /* Zap all of the normal resources, they are - * meaningless and generate bogus resource collision - * messages. This is OpenBoot's ill-fated attempt to - * represent the implicit resources that these devices - * have. - */ - pcp->num_prom_assignments = 0; - for (i = 0; i < 6; i++) { - pdev->resource[i].start = - pdev->resource[i].end = - pdev->resource[i].flags = 0; - } - pdev->resource[PCI_ROM_RESOURCE].start = - pdev->resource[PCI_ROM_RESOURCE].end = - pdev->resource[PCI_ROM_RESOURCE].flags = 0; + /* VGA Video RAM. */ + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) return; - } - - for (i = 0; i < pcp->num_prom_assignments; i++) { - struct linux_prom_pci_registers *ap; - int space; - ap = &pcp->prom_assignments[i]; - space = ap->phys_hi >> 24; - if ((space & 0x3) == 2 && - (space & 0x4) != 0) { - ap->phys_hi &= ~(0x7 << 24); - ap->phys_hi |= 0x3 << 24; - } - } -} - -static ssize_t -show_pciobppath_attr(struct device * dev, struct device_attribute * attr, char * buf) -{ - struct pci_dev *pdev; - struct pcidev_cookie *sysdata; - - pdev = to_pci_dev(dev); - sysdata = pdev->sysdata; - - return snprintf (buf, PAGE_SIZE, "%s\n", sysdata->prom_node->full_name); -} - -static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL); + p->name = "Video RAM area"; + p->start = mem_res->start + 0xa0000UL; + p->end = p->start + 0x1ffffUL; + p->flags = IORESOURCE_BUSY; + request_resource(mem_res, p); -/* Fill in the PCI device cookie sysdata for the given - * PCI device. This cookie is the means by which one - * can get to OBP and PCI controller specific information - * for a PCI device. - */ -static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, - struct pci_dev *pdev, - struct device_node *bus_node) -{ - struct linux_prom_pci_registers *pregs = NULL; - struct pcidev_cookie *pcp; - struct device_node *dp; - struct property *prop; - int nregs, len, err; - - dp = find_device_prom_node(pbm, pdev, bus_node, - &pregs, &nregs); - if (!dp) { - /* If it is not in the OBP device tree then - * there must be a damn good reason for it. - * - * So what we do is delete the device from the - * PCI device tree completely. This scenario - * is seen, for example, on CP1500 for the - * second EBUS/HappyMeal pair if the external - * connector for it is not present. - */ - pci_remove_bus_device(pdev); + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) return; - } - - pcp = kzalloc(sizeof(*pcp), GFP_ATOMIC); - if (pcp == NULL) { - prom_printf("PCI_COOKIE: Fatal malloc error, aborting...\n"); - prom_halt(); - } - pcp->pbm = pbm; - pcp->prom_node = dp; - pcp->op = of_find_device_by_node(dp); - memcpy(pcp->prom_regs, pregs, - nregs * sizeof(struct linux_prom_pci_registers)); - pcp->num_prom_regs = nregs; - - /* We can't have the pcidev_cookie assignments be just - * direct pointers into the property value, since they - * are potentially modified by the probing process. - */ - prop = of_find_property(dp, "assigned-addresses", &len); - if (!prop) { - pcp->num_prom_assignments = 0; - } else { - memcpy(pcp->prom_assignments, prop->value, len); - pcp->num_prom_assignments = - (len / sizeof(pcp->prom_assignments[0])); - } - - if (strcmp(dp->name, "ebus") == 0) { - struct linux_prom_ebus_ranges *erng; - int iter; - - /* EBUS is special... */ - prop = of_find_property(dp, "ranges", &len); - if (!prop) { - prom_printf("EBUS: Fatal error, no range property\n"); - prom_halt(); - } - erng = prop->value; - len = (len / sizeof(erng[0])); - for (iter = 0; iter < len; iter++) { - struct linux_prom_ebus_ranges *ep = &erng[iter]; - struct linux_prom_pci_registers *ap; - - ap = &pcp->prom_assignments[iter]; - - ap->phys_hi = ep->parent_phys_hi; - ap->phys_mid = ep->parent_phys_mid; - ap->phys_lo = ep->parent_phys_lo; - ap->size_hi = 0; - ap->size_lo = ep->size; - } - pcp->num_prom_assignments = len; - } - - fixup_obp_assignments(pdev, pcp); - - pdev->sysdata = pcp; - - /* we don't really care if we can create this file or not, - * but we need to assign the result of the call or the world will fall - * under alien invasion and everybody will be frozen on a spaceship - * ready to be eaten on alpha centauri by some green and jelly humanoid. - */ - err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_obppath.attr); -} - -void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus, - struct pci_pbm_info *pbm, - struct device_node *dp) -{ - struct pci_dev *pdev, *pdev_next; - struct pci_bus *this_pbus, *pbus_next; - - /* This must be _safe because the cookie fillin - routine can delete devices from the tree. */ - list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list) - pdev_cookie_fillin(pbm, pdev, dp); - - list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) { - struct pcidev_cookie *pcp = this_pbus->self->sysdata; - - pci_fill_in_pbm_cookies(this_pbus, pbm, pcp->prom_node); - } -} -static void __init bad_assignment(struct pci_dev *pdev, - struct linux_prom_pci_registers *ap, - struct resource *res, - int do_prom_halt) -{ - prom_printf("PCI: Bogus PROM assignment. BUS[%02x] DEVFN[%x]\n", - pdev->bus->number, pdev->devfn); - if (ap) - prom_printf("PCI: phys[%08x:%08x:%08x] size[%08x:%08x]\n", - ap->phys_hi, ap->phys_mid, ap->phys_lo, - ap->size_hi, ap->size_lo); - if (res) - prom_printf("PCI: RES[%016lx-->%016lx:(%lx)]\n", - res->start, res->end, res->flags); - if (do_prom_halt) - prom_halt(); -} - -static struct resource * -__init get_root_resource(struct linux_prom_pci_registers *ap, - struct pci_pbm_info *pbm) -{ - int space = (ap->phys_hi >> 24) & 3; - - switch (space) { - case 0: - /* Configuration space, silently ignore it. */ - return NULL; - - case 1: - /* 16-bit IO space */ - return &pbm->io_space; - - case 2: - /* 32-bit MEM space */ - return &pbm->mem_space; - - case 3: - /* 64-bit MEM space, these are allocated out of - * the 32-bit mem_space range for the PBM, ie. - * we just zero out the upper 32-bits. - */ - return &pbm->mem_space; - - default: - printk("PCI: What is resource space %x?\n", space); - return NULL; - }; -} - -static struct resource * -__init get_device_resource(struct linux_prom_pci_registers *ap, - struct pci_dev *pdev) -{ - struct resource *res; - int breg = (ap->phys_hi & 0xff); - - switch (breg) { - case PCI_ROM_ADDRESS: - /* Unfortunately I have seen several cases where - * buggy FCODE uses a space value of '1' (I/O space) - * in the register property for the ROM address - * so disable this sanity check for now. - */ -#if 0 - { - int space = (ap->phys_hi >> 24) & 3; - - /* It had better be MEM space. */ - if (space != 2) - bad_assignment(pdev, ap, NULL, 0); - } -#endif - res = &pdev->resource[PCI_ROM_RESOURCE]; - break; - - case PCI_BASE_ADDRESS_0: - case PCI_BASE_ADDRESS_1: - case PCI_BASE_ADDRESS_2: - case PCI_BASE_ADDRESS_3: - case PCI_BASE_ADDRESS_4: - case PCI_BASE_ADDRESS_5: - res = &pdev->resource[(breg - PCI_BASE_ADDRESS_0) / 4]; - break; - - default: - bad_assignment(pdev, ap, NULL, 0); - res = NULL; - break; - }; - - return res; -} - -static void __init pdev_record_assignments(struct pci_pbm_info *pbm, - struct pci_dev *pdev) -{ - struct pcidev_cookie *pcp = pdev->sysdata; - int i; - - for (i = 0; i < pcp->num_prom_assignments; i++) { - struct linux_prom_pci_registers *ap; - struct resource *root, *res; - - /* The format of this property is specified in - * the PCI Bus Binding to IEEE1275-1994. - */ - ap = &pcp->prom_assignments[i]; - root = get_root_resource(ap, pbm); - res = get_device_resource(ap, pdev); - if (root == NULL || res == NULL || - res->flags == 0) - continue; - - /* Ok we know which resource this PROM assignment is - * for, sanity check it. - */ - if ((res->start & 0xffffffffUL) != ap->phys_lo) - bad_assignment(pdev, ap, res, 1); - - /* If it is a 64-bit MEM space assignment, verify that - * the resource is too and that the upper 32-bits match. - */ - if (((ap->phys_hi >> 24) & 3) == 3) { - if (((res->flags & IORESOURCE_MEM) == 0) || - ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) - != PCI_BASE_ADDRESS_MEM_TYPE_64)) - bad_assignment(pdev, ap, res, 1); - if ((res->start >> 32) != ap->phys_mid) - bad_assignment(pdev, ap, res, 1); - - /* PBM cannot generate cpu initiated PIOs - * to the full 64-bit space. Therefore the - * upper 32-bits better be zero. If it is - * not, just skip it and we will assign it - * properly ourselves. - */ - if ((res->start >> 32) != 0UL) { - printk(KERN_ERR "PCI: OBP assigns out of range MEM address " - "%016lx for region %ld on device %s\n", - res->start, (res - &pdev->resource[0]), pci_name(pdev)); - continue; - } - } - - /* Adjust the resource into the physical address space - * of this PBM. - */ - pbm->parent->resource_adjust(pdev, res, root); - - if (request_resource(root, res) < 0) { - int rnum; - - /* OK, there is some conflict. But this is fine - * since we'll reassign it in the fixup pass. - * - * Do not print the warning for ROM resources - * as such a conflict is quite common and - * harmless as the ROM bar is disabled. - */ - rnum = (res - &pdev->resource[0]); - if (rnum != PCI_ROM_RESOURCE) - printk(KERN_ERR "PCI: Resource collision, " - "region %d " - "[%016lx:%016lx] of device %s\n", - rnum, - res->start, res->end, - pci_name(pdev)); - } - } -} - -void __init pci_record_assignments(struct pci_pbm_info *pbm, - struct pci_bus *pbus) -{ - struct pci_dev *dev; - struct pci_bus *bus; + p->name = "System ROM"; + p->start = mem_res->start + 0xf0000UL; + p->end = p->start + 0xffffUL; + p->flags = IORESOURCE_BUSY; + request_resource(mem_res, p); - list_for_each_entry(dev, &pbus->devices, bus_list) - pdev_record_assignments(pbm, dev); + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return; - list_for_each_entry(bus, &pbus->children, node) - pci_record_assignments(pbm, bus); + p->name = "Video ROM"; + p->start = mem_res->start + 0xc0000UL; + p->end = p->start + 0x7fffUL; + p->flags = IORESOURCE_BUSY; + request_resource(mem_res, p); } -/* Return non-zero if PDEV has implicit I/O resources even - * though it may not have an I/O base address register - * active. - */ -static int __init has_implicit_io(struct pci_dev *pdev) +static void pci_register_iommu_region(struct pci_pbm_info *pbm) { - int class = pdev->class >> 8; + const u32 *vdma = of_get_property(pbm->prom_node, "virtual-dma", NULL); - if (class == PCI_CLASS_NOT_DEFINED || - class == PCI_CLASS_NOT_DEFINED_VGA || - class == PCI_CLASS_STORAGE_IDE || - (pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) - return 1; + if (vdma) { + struct resource *rp = kmalloc(sizeof(*rp), GFP_KERNEL); - return 0; -} - -static void __init pdev_assign_unassigned(struct pci_pbm_info *pbm, - struct pci_dev *pdev) -{ - u32 reg; - u16 cmd; - int i, io_seen, mem_seen; - - io_seen = mem_seen = 0; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *root, *res; - unsigned long size, min, max, align; - - res = &pdev->resource[i]; - - if (res->flags & IORESOURCE_IO) - io_seen++; - else if (res->flags & IORESOURCE_MEM) - mem_seen++; - - /* If it is already assigned or the resource does - * not exist, there is nothing to do. - */ - if (res->parent != NULL || res->flags == 0UL) - continue; - - /* Determine the root we allocate from. */ - if (res->flags & IORESOURCE_IO) { - root = &pbm->io_space; - min = root->start + 0x400UL; - max = root->end; - } else { - root = &pbm->mem_space; - min = root->start; - max = min + 0x80000000UL; - } - - size = res->end - res->start; - align = size + 1; - if (allocate_resource(root, res, size + 1, min, max, align, NULL, NULL) < 0) { - /* uh oh */ - prom_printf("PCI: Failed to allocate resource %d for %s\n", - i, pci_name(pdev)); + if (!rp) { + prom_printf("Cannot allocate IOMMU resource.\n"); prom_halt(); } - - /* Update PCI config space. */ - pbm->parent->base_address_update(pdev, i); - } - - /* Special case, disable the ROM. Several devices - * act funny (ie. do not respond to memory space writes) - * when it is left enabled. A good example are Qlogic,ISP - * adapters. - */ - pci_read_config_dword(pdev, PCI_ROM_ADDRESS, ®); - reg &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(pdev, PCI_ROM_ADDRESS, reg); - - /* If we saw I/O or MEM resources, enable appropriate - * bits in PCI command register. - */ - if (io_seen || mem_seen) { - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - if (io_seen || has_implicit_io(pdev)) - cmd |= PCI_COMMAND_IO; - if (mem_seen) - cmd |= PCI_COMMAND_MEMORY; - pci_write_config_word(pdev, PCI_COMMAND, cmd); - } - - /* If this is a PCI bridge or an IDE controller, - * enable bus mastering. In the former case also - * set the cache line size correctly. - */ - if (((pdev->class >> 8) == PCI_CLASS_BRIDGE_PCI) || - (((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) && - ((pdev->class & 0x80) != 0))) { - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MASTER; - pci_write_config_word(pdev, PCI_COMMAND, cmd); - - if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_PCI) - pci_write_config_byte(pdev, - PCI_CACHE_LINE_SIZE, - (64 / sizeof(u32))); + rp->name = "IOMMU"; + rp->start = pbm->mem_space.start + (unsigned long) vdma[0]; + rp->end = rp->start + (unsigned long) vdma[1] - 1UL; + rp->flags = IORESOURCE_BUSY; + request_resource(&pbm->mem_space, rp); } } -void __init pci_assign_unassigned(struct pci_pbm_info *pbm, - struct pci_bus *pbus) +void pci_determine_mem_io_space(struct pci_pbm_info *pbm) { - struct pci_dev *dev; - struct pci_bus *bus; - - list_for_each_entry(dev, &pbus->devices, bus_list) - pdev_assign_unassigned(pbm, dev); + const struct linux_prom_pci_ranges *pbm_ranges; + int i, saw_mem, saw_io; + int num_pbm_ranges; - list_for_each_entry(bus, &pbus->children, node) - pci_assign_unassigned(pbm, bus); -} + saw_mem = saw_io = 0; + pbm_ranges = of_get_property(pbm->prom_node, "ranges", &i); + num_pbm_ranges = i / sizeof(*pbm_ranges); -static void __init pdev_fixup_irq(struct pci_dev *pdev) -{ - struct pcidev_cookie *pcp = pdev->sysdata; - struct of_device *op = pcp->op; + for (i = 0; i < num_pbm_ranges; i++) { + const struct linux_prom_pci_ranges *pr = &pbm_ranges[i]; + unsigned long a; + u32 parent_phys_hi, parent_phys_lo; + int type; - if (op->irqs[0] == 0xffffffff) { - pdev->irq = PCI_IRQ_NONE; - return; - } + parent_phys_hi = pr->parent_phys_hi; + parent_phys_lo = pr->parent_phys_lo; + if (tlb_type == hypervisor) + parent_phys_hi &= 0x0fffffff; - pdev->irq = op->irqs[0]; + type = (pr->child_phys_hi >> 24) & 0x3; + a = (((unsigned long)parent_phys_hi << 32UL) | + ((unsigned long)parent_phys_lo << 0UL)); - pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, - pdev->irq & PCI_IRQ_INO); -} - -void __init pci_fixup_irq(struct pci_pbm_info *pbm, - struct pci_bus *pbus) -{ - struct pci_dev *dev; - struct pci_bus *bus; - - list_for_each_entry(dev, &pbus->devices, bus_list) - pdev_fixup_irq(dev); - - list_for_each_entry(bus, &pbus->children, node) - pci_fixup_irq(pbm, bus); -} - -static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz) -{ - u16 cmd; - u8 hdr_type, min_gnt, ltimer; - - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MASTER; - pci_write_config_word(pdev, PCI_COMMAND, cmd); - - /* Read it back, if the mastering bit did not - * get set, the device does not support bus - * mastering so we have nothing to do here. - */ - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - if ((cmd & PCI_COMMAND_MASTER) == 0) - return; - - /* Set correct cache line size, 64-byte on all - * Sparc64 PCI systems. Note that the value is - * measured in 32-bit words. - */ - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, - 64 / sizeof(u32)); - - pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr_type); - hdr_type &= ~0x80; - if (hdr_type != PCI_HEADER_TYPE_NORMAL) - return; - - /* If the latency timer is already programmed with a non-zero - * value, assume whoever set it (OBP or whoever) knows what - * they are doing. - */ - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, <imer); - if (ltimer != 0) - return; - - /* XXX Since I'm tipping off the min grant value to - * XXX choose a suitable latency timer value, I also - * XXX considered making use of the max latency value - * XXX as well. Unfortunately I've seen too many bogusly - * XXX low settings for it to the point where it lacks - * XXX any usefulness. In one case, an ethernet card - * XXX claimed a min grant of 10 and a max latency of 5. - * XXX Now, if I had two such cards on the same bus I - * XXX could not set the desired burst period (calculated - * XXX from min grant) without violating the max latency - * XXX bound. Duh... - * XXX - * XXX I blame dumb PC bios implementors for stuff like - * XXX this, most of them don't even try to do something - * XXX sensible with latency timer values and just set some - * XXX default value (usually 32) into every device. - */ - - pci_read_config_byte(pdev, PCI_MIN_GNT, &min_gnt); - - if (min_gnt == 0) { - /* If no min_gnt setting then use a default - * value. - */ - if (is_66mhz) - ltimer = 16; - else - ltimer = 32; - } else { - int shift_factor; - - if (is_66mhz) - shift_factor = 2; - else - shift_factor = 3; - - /* Use a default value when the min_gnt value - * is erroneously high. - */ - if (((unsigned int) min_gnt << shift_factor) > 512 || - ((min_gnt << shift_factor) & 0xff) == 0) { - ltimer = 8 << shift_factor; - } else { - ltimer = min_gnt << shift_factor; - } - } + switch (type) { + case 0: + /* PCI config space, 16MB */ + pbm->config_space = a; + break; - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ltimer); -} + case 1: + /* 16-bit IO space, 16MB */ + pbm->io_space.start = a; + pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); + pbm->io_space.flags = IORESOURCE_IO; + saw_io = 1; + break; -void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm, - struct pci_bus *pbus) -{ - struct pci_dev *pdev; - int all_are_66mhz; - u16 status; + case 2: + /* 32-bit MEM space, 2GB */ + pbm->mem_space.start = a; + pbm->mem_space.end = a + (0x80000000UL - 1UL); + pbm->mem_space.flags = IORESOURCE_MEM; + saw_mem = 1; + break; - if (pbm->is_66mhz_capable == 0) { - all_are_66mhz = 0; - goto out; - } + case 3: + /* XXX 64-bit MEM handling XXX */ - all_are_66mhz = 1; - list_for_each_entry(pdev, &pbus->devices, bus_list) { - pci_read_config_word(pdev, PCI_STATUS, &status); - if (!(status & PCI_STATUS_66MHZ)) { - all_are_66mhz = 0; + default: break; - } + }; } -out: - pbm->all_devs_66mhz = all_are_66mhz; - - printk("PCI%d(PBM%c): Bus running at %dMHz\n", - pbm->parent->index, - (pbm == &pbm->parent->pbm_A) ? 'A' : 'B', - (all_are_66mhz ? 66 : 33)); -} - -void pci_setup_busmastering(struct pci_pbm_info *pbm, - struct pci_bus *pbus) -{ - struct pci_dev *dev; - struct pci_bus *bus; - int is_66mhz; - - is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz; - - list_for_each_entry(dev, &pbus->devices, bus_list) - pdev_setup_busmastering(dev, is_66mhz); - - list_for_each_entry(bus, &pbus->children, node) - pci_setup_busmastering(pbm, bus); -} - -void pci_register_legacy_regions(struct resource *io_res, - struct resource *mem_res) -{ - struct resource *p; - - /* VGA Video RAM. */ - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return; - p->name = "Video RAM area"; - p->start = mem_res->start + 0xa0000UL; - p->end = p->start + 0x1ffffUL; - p->flags = IORESOURCE_BUSY; - request_resource(mem_res, p); + if (!saw_io || !saw_mem) { + prom_printf("%s: Fatal error, missing %s PBM range.\n", + pbm->name, + (!saw_io ? "IO" : "MEM")); + prom_halt(); + } - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return; + printk("%s: PCI IO[%lx] MEM[%lx]\n", + pbm->name, + pbm->io_space.start, + pbm->mem_space.start); - p->name = "System ROM"; - p->start = mem_res->start + 0xf0000UL; - p->end = p->start + 0xffffUL; - p->flags = IORESOURCE_BUSY; - request_resource(mem_res, p); + pbm->io_space.name = pbm->mem_space.name = pbm->name; - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return; + request_resource(&ioport_resource, &pbm->io_space); + request_resource(&iomem_resource, &pbm->mem_space); - p->name = "Video ROM"; - p->start = mem_res->start + 0xc0000UL; - p->end = p->start + 0x7fffUL; - p->flags = IORESOURCE_BUSY; - request_resource(mem_res, p); + pci_register_legacy_regions(&pbm->io_space, + &pbm->mem_space); + pci_register_iommu_region(pbm); } /* Generic helper routines for PCI error reporting. */ diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h index 971e2be..1208583 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -1,7 +1,6 @@ -/* $Id: pci_impl.h,v 1.9 2001/06/13 06:34:30 davem Exp $ - * pci_impl.h: Helper definitions for PCI controller support. +/* pci_impl.h: Helper definitions for PCI controller support. * - * Copyright (C) 1999 David S. Miller (davem@redhat.com) + * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) */ #ifndef PCI_IMPL_H @@ -13,26 +12,22 @@ #include #include extern struct pci_controller_info *pci_controller_root; +extern unsigned long pci_memspace_mask; extern int pci_num_controllers; /* PCI bus scanning and fixup support. */ -extern void pci_fixup_host_bridge_self(struct pci_bus *pbus); -extern void pci_fill_in_pbm_cookies(struct pci_bus *pbus, - struct pci_pbm_info *pbm, - struct device_node *prom_node); -extern void pci_record_assignments(struct pci_pbm_info *pbm, - struct pci_bus *pbus); -extern void pci_assign_unassigned(struct pci_pbm_info *pbm, - struct pci_bus *pbus); -extern void pci_fixup_irq(struct pci_pbm_info *pbm, - struct pci_bus *pbus); -extern void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm, - struct pci_bus *pbus); -extern void pci_setup_busmastering(struct pci_pbm_info *pbm, - struct pci_bus *pbus); -extern void pci_register_legacy_regions(struct resource *io_res, - struct resource *mem_res); +extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm); +extern void pci_determine_mem_io_space(struct pci_pbm_info *pbm); + +extern int pci_host_bridge_read_pci_cfg(struct pci_bus *bus_dev, + unsigned int devfn, + int where, int size, + u32 *value); +extern int pci_host_bridge_write_pci_cfg(struct pci_bus *bus_dev, + unsigned int devfn, + int where, int size, + u32 value); /* Error reporting support. */ extern void pci_scan_for_target_abort(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *); diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c index 7aca0f3..6671277 100644 --- a/arch/sparc64/kernel/pci_iommu.c +++ b/arch/sparc64/kernel/pci_iommu.c @@ -1,7 +1,6 @@ -/* $Id: pci_iommu.c,v 1.17 2001/12/17 07:05:09 davem Exp $ - * pci_iommu.c: UltraSparc PCI controller IOM/STC support. +/* pci_iommu.c: UltraSparc PCI controller IOM/STC support. * - * Copyright (C) 1999 David S. Miller (davem@redhat.com) + * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) * Copyright (C) 1999, 2000 Jakub Jelinek (jakub@redhat.com) */ @@ -36,7 +35,7 @@ #define pci_iommu_write(__reg, __val) \ "i" (ASI_PHYS_BYPASS_EC_E)) /* Must be invoked under the IOMMU lock. */ -static void __iommu_flushall(struct pci_iommu *iommu) +static void __iommu_flushall(struct iommu *iommu) { unsigned long tag; int entry; @@ -64,7 +63,7 @@ #define IOPTE_STREAMING(CTX) \ #define IOPTE_IS_DUMMY(iommu, iopte) \ ((iopte_val(*iopte) & IOPTE_PAGE) == (iommu)->dummy_page_pa) -static inline void iopte_make_dummy(struct pci_iommu *iommu, iopte_t *iopte) +static inline void iopte_make_dummy(struct iommu *iommu, iopte_t *iopte) { unsigned long val = iopte_val(*iopte); @@ -75,9 +74,9 @@ static inline void iopte_make_dummy(stru } /* Based largely upon the ppc64 iommu allocator. */ -static long pci_arena_alloc(struct pci_iommu *iommu, unsigned long npages) +static long pci_arena_alloc(struct iommu *iommu, unsigned long npages) { - struct pci_iommu_arena *arena = &iommu->arena; + struct iommu_arena *arena = &iommu->arena; unsigned long n, i, start, end, limit; int pass; @@ -116,7 +115,7 @@ again: return n; } -static void pci_arena_free(struct pci_iommu_arena *arena, unsigned long base, unsigned long npages) +static void pci_arena_free(struct iommu_arena *arena, unsigned long base, unsigned long npages) { unsigned long i; @@ -124,7 +123,7 @@ static void pci_arena_free(struct pci_io __clear_bit(i, arena->map); } -void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask) +void pci_iommu_table_init(struct iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask) { unsigned long i, tsbbase, order, sz, num_tsb_entries; @@ -170,7 +169,7 @@ void pci_iommu_table_init(struct pci_iom iopte_make_dummy(iommu, &iommu->page_table[i]); } -static inline iopte_t *alloc_npages(struct pci_iommu *iommu, unsigned long npages) +static inline iopte_t *alloc_npages(struct iommu *iommu, unsigned long npages) { long entry; @@ -181,12 +180,12 @@ static inline iopte_t *alloc_npages(stru return iommu->page_table + entry; } -static inline void free_npages(struct pci_iommu *iommu, dma_addr_t base, unsigned long npages) +static inline void free_npages(struct iommu *iommu, dma_addr_t base, unsigned long npages) { pci_arena_free(&iommu->arena, base >> IO_PAGE_SHIFT, npages); } -static int iommu_alloc_ctx(struct pci_iommu *iommu) +static int iommu_alloc_ctx(struct iommu *iommu) { int lowest = iommu->ctx_lowest_free; int sz = IOMMU_NUM_CTXS - lowest; @@ -205,7 +204,7 @@ static int iommu_alloc_ctx(struct pci_io return n; } -static inline void iommu_free_ctx(struct pci_iommu *iommu, int ctx) +static inline void iommu_free_ctx(struct iommu *iommu, int ctx) { if (likely(ctx)) { __clear_bit(ctx, iommu->ctx_bitmap); @@ -220,8 +219,7 @@ static inline void iommu_free_ctx(struct */ static void *pci_4u_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp, gfp_t gfp) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct iommu *iommu; iopte_t *iopte; unsigned long flags, order, first_page; void *ret; @@ -237,8 +235,7 @@ static void *pci_4u_alloc_consistent(str return NULL; memset((char *)first_page, 0, PAGE_SIZE << order); - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; + iommu = pdev->dev.archdata.iommu; spin_lock_irqsave(&iommu->lock, flags); iopte = alloc_npages(iommu, size >> IO_PAGE_SHIFT); @@ -268,14 +265,12 @@ static void *pci_4u_alloc_consistent(str /* Free and unmap a consistent DMA translation. */ static void pci_4u_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_t dvma) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct iommu *iommu; iopte_t *iopte; unsigned long flags, order, npages; npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT; - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; + iommu = pdev->dev.archdata.iommu; iopte = iommu->page_table + ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT); @@ -295,18 +290,16 @@ static void pci_4u_free_consistent(struc */ static dma_addr_t pci_4u_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - struct pci_strbuf *strbuf; + struct iommu *iommu; + struct strbuf *strbuf; iopte_t *base; unsigned long flags, npages, oaddr; unsigned long i, base_paddr, ctx; u32 bus_addr, ret; unsigned long iopte_protection; - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - strbuf = &pcp->pbm->stc; + iommu = pdev->dev.archdata.iommu; + strbuf = pdev->dev.archdata.stc; if (unlikely(direction == PCI_DMA_NONE)) goto bad_no_ctx; @@ -349,7 +342,7 @@ bad_no_ctx: return PCI_DMA_ERROR_CODE; } -static void pci_strbuf_flush(struct pci_strbuf *strbuf, struct pci_iommu *iommu, u32 vaddr, unsigned long ctx, unsigned long npages, int direction) +static void pci_strbuf_flush(struct strbuf *strbuf, struct iommu *iommu, u32 vaddr, unsigned long ctx, unsigned long npages, int direction) { int limit; @@ -416,9 +409,8 @@ do_flush_sync: /* Unmap a single streaming mode DMA translation. */ static void pci_4u_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - struct pci_strbuf *strbuf; + struct iommu *iommu; + struct strbuf *strbuf; iopte_t *base; unsigned long flags, npages, ctx, i; @@ -428,9 +420,8 @@ static void pci_4u_unmap_single(struct p return; } - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - strbuf = &pcp->pbm->stc; + iommu = pdev->dev.archdata.iommu; + strbuf = pdev->dev.archdata.stc; npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK); npages >>= IO_PAGE_SHIFT; @@ -549,9 +540,8 @@ static inline void fill_sg(iopte_t *iopt */ static int pci_4u_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - struct pci_strbuf *strbuf; + struct iommu *iommu; + struct strbuf *strbuf; unsigned long flags, ctx, npages, iopte_protection; iopte_t *base; u32 dma_base; @@ -570,9 +560,8 @@ static int pci_4u_map_sg(struct pci_dev return 1; } - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - strbuf = &pcp->pbm->stc; + iommu = pdev->dev.archdata.iommu; + strbuf = pdev->dev.archdata.stc; if (unlikely(direction == PCI_DMA_NONE)) goto bad_no_ctx; @@ -636,9 +625,8 @@ bad_no_ctx: /* Unmap a set of streaming mode DMA translations. */ static void pci_4u_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - struct pci_strbuf *strbuf; + struct iommu *iommu; + struct strbuf *strbuf; iopte_t *base; unsigned long flags, ctx, i, npages; u32 bus_addr; @@ -648,9 +636,8 @@ static void pci_4u_unmap_sg(struct pci_d WARN_ON(1); } - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - strbuf = &pcp->pbm->stc; + iommu = pdev->dev.archdata.iommu; + strbuf = pdev->dev.archdata.stc; bus_addr = sglist->dma_address & IO_PAGE_MASK; @@ -696,14 +683,12 @@ #endif */ static void pci_4u_dma_sync_single_for_cpu(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - struct pci_strbuf *strbuf; + struct iommu *iommu; + struct strbuf *strbuf; unsigned long flags, ctx, npages; - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - strbuf = &pcp->pbm->stc; + iommu = pdev->dev.archdata.iommu; + strbuf = pdev->dev.archdata.stc; if (!strbuf->strbuf_enabled) return; @@ -736,15 +721,13 @@ static void pci_4u_dma_sync_single_for_c */ static void pci_4u_dma_sync_sg_for_cpu(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - struct pci_strbuf *strbuf; + struct iommu *iommu; + struct strbuf *strbuf; unsigned long flags, ctx, npages, i; u32 bus_addr; - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - strbuf = &pcp->pbm->stc; + iommu = pdev->dev.archdata.iommu; + strbuf = pdev->dev.archdata.stc; if (!strbuf->strbuf_enabled) return; @@ -775,7 +758,7 @@ static void pci_4u_dma_sync_sg_for_cpu(s spin_unlock_irqrestore(&iommu->lock, flags); } -struct pci_iommu_ops pci_sun4u_iommu_ops = { +const struct pci_iommu_ops pci_sun4u_iommu_ops = { .alloc_consistent = pci_4u_alloc_consistent, .free_consistent = pci_4u_free_consistent, .map_single = pci_4u_map_single, @@ -809,13 +792,12 @@ static void ali_sound_dma_hack(struct pc int pci_dma_supported(struct pci_dev *pdev, u64 device_mask) { - struct pcidev_cookie *pcp = pdev->sysdata; u64 dma_addr_mask; if (pdev == NULL) { dma_addr_mask = 0xffffffff; } else { - struct pci_iommu *iommu = pcp->pbm->iommu; + struct iommu *iommu = pdev->dev.archdata.iommu; dma_addr_mask = iommu->dma_addr_mask; diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index fda5db2..253d40e 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -1,7 +1,6 @@ -/* $Id: pci_psycho.c,v 1.33 2002/02/01 00:58:33 davem Exp $ - * pci_psycho.c: PSYCHO/U2P specific PCI controller support. +/* pci_psycho.c: PSYCHO/U2P specific PCI controller support. * - * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) + * Copyright (C) 1997, 1998, 1999, 2007 David S. Miller (davem@davemloft.net) * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) */ @@ -119,6 +118,10 @@ static int psycho_read_pci_cfg(struct pc u16 tmp16; u8 tmp8; + if (bus_dev == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where, + size, value); + switch (size) { case 1: *value = 0xff; @@ -172,6 +175,9 @@ static int psycho_write_pci_cfg(struct p unsigned char bus = bus_dev->number; u32 *addr; + if (bus_dev == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where, + size, value); addr = psycho_pci_config_mkaddr(pbm, bus, devfn, where); if (!addr) return PCIBIOS_SUCCESSFUL; @@ -263,7 +269,7 @@ static void __psycho_check_one_stc(struc struct pci_pbm_info *pbm, int is_pbm_a) { - struct pci_strbuf *strbuf = &pbm->stc; + struct strbuf *strbuf = &pbm->stc; unsigned long regbase = p->pbm_A.controller_regs; unsigned long err_base, tag_base, line_base; u64 control; @@ -412,7 +418,7 @@ static void psycho_check_iommu_error(str unsigned long afar, enum psycho_error_type type) { - struct pci_iommu *iommu = p->pbm_A.iommu; + struct iommu *iommu = p->pbm_A.iommu; unsigned long iommu_tag[16]; unsigned long iommu_data[16]; unsigned long flags; @@ -895,59 +901,6 @@ static void psycho_register_error_handle } /* PSYCHO boot time probing and initialization. */ -static void psycho_resource_adjust(struct pci_dev *pdev, - struct resource *res, - struct resource *root) -{ - res->start += root->start; - res->end += root->start; -} - -static void psycho_base_address_update(struct pci_dev *pdev, int resource) -{ - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; - struct resource *res, *root; - u32 reg; - int where, size, is_64bit; - - res = &pdev->resource[resource]; - if (resource < 6) { - where = PCI_BASE_ADDRESS_0 + (resource * 4); - } else if (resource == PCI_ROM_RESOURCE) { - where = pdev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - is_64bit = 0; - if (res->flags & IORESOURCE_IO) - root = &pbm->io_space; - else { - root = &pbm->mem_space; - if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) - == PCI_BASE_ADDRESS_MEM_TYPE_64) - is_64bit = 1; - } - - size = res->end - res->start; - pci_read_config_dword(pdev, where, ®); - reg = ((reg & size) | - (((u32)(res->start - root->start)) & ~size)); - if (resource == PCI_ROM_RESOURCE) { - reg |= PCI_ROM_ADDRESS_ENABLE; - res->flags |= IORESOURCE_ROM_ENABLE; - } - pci_write_config_dword(pdev, where, reg); - - /* This knows that the upper 32-bits of the address - * must be zero. Our PCI common layer enforces this. - */ - if (is_64bit) - pci_write_config_dword(pdev, where + 4, 0); -} - static void pbm_config_busmastering(struct pci_pbm_info *pbm) { u8 *addr; @@ -968,28 +921,7 @@ static void pbm_config_busmastering(stru static void pbm_scan_bus(struct pci_controller_info *p, struct pci_pbm_info *pbm) { - struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - - if (!cookie) { - prom_printf("PSYCHO: Critical allocation failure.\n"); - prom_halt(); - } - - /* All we care about is the PBM. */ - cookie->pbm = pbm; - - pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, - p->pci_ops, - pbm); - pci_fixup_host_bridge_self(pbm->pci_bus); - pbm->pci_bus->self->sysdata = cookie; - - pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node); - pci_record_assignments(pbm, pbm->pci_bus); - pci_assign_unassigned(pbm, pbm->pci_bus); - pci_fixup_irq(pbm, pbm->pci_bus); - pci_determine_66mhz_disposition(pbm, pbm->pci_bus); - pci_setup_busmastering(pbm, pbm->pci_bus); + pbm->pci_bus = pci_scan_one_pbm(pbm); } static void psycho_scan_bus(struct pci_controller_info *p) @@ -1009,7 +941,7 @@ static void psycho_scan_bus(struct pci_c static void psycho_iommu_init(struct pci_controller_info *p) { - struct pci_iommu *iommu = p->pbm_A.iommu; + struct iommu *iommu = p->pbm_A.iommu; unsigned long i; u64 control; @@ -1094,19 +1026,6 @@ static void psycho_controller_hwinit(str psycho_write(p->pbm_A.controller_regs + PSYCHO_PCIB_DIAG, tmp); } -static void pbm_register_toplevel_resources(struct pci_controller_info *p, - struct pci_pbm_info *pbm) -{ - char *name = pbm->name; - - pbm->io_space.name = pbm->mem_space.name = name; - - request_resource(&ioport_resource, &pbm->io_space); - request_resource(&iomem_resource, &pbm->mem_space); - pci_register_legacy_regions(&pbm->io_space, - &pbm->mem_space); -} - static void psycho_pbm_strbuf_init(struct pci_controller_info *p, struct pci_pbm_info *pbm, int is_pbm_a) @@ -1172,19 +1091,11 @@ static void psycho_pbm_init(struct pci_c unsigned int *busrange; struct property *prop; struct pci_pbm_info *pbm; - int len; - if (is_pbm_a) { + if (is_pbm_a) pbm = &p->pbm_A; - pbm->pci_first_slot = 1; - pbm->io_space.start = pbm->controller_regs + PSYCHO_IOSPACE_A; - pbm->mem_space.start = pbm->controller_regs + PSYCHO_MEMSPACE_A; - } else { + else pbm = &p->pbm_B; - pbm->pci_first_slot = 2; - pbm->io_space.start = pbm->controller_regs + PSYCHO_IOSPACE_B; - pbm->mem_space.start = pbm->controller_regs + PSYCHO_MEMSPACE_B; - } pbm->chip_type = PBM_CHIP_TYPE_PSYCHO; pbm->chip_version = 0; @@ -1196,41 +1107,15 @@ static void psycho_pbm_init(struct pci_c if (prop) pbm->chip_revision = *(int *) prop->value; - pbm->io_space.end = pbm->io_space.start + PSYCHO_IOSPACE_SIZE; - pbm->io_space.flags = IORESOURCE_IO; - pbm->mem_space.end = pbm->mem_space.start + PSYCHO_MEMSPACE_SIZE; - pbm->mem_space.flags = IORESOURCE_MEM; - pbm->parent = p; pbm->prom_node = dp; pbm->name = dp->full_name; - pbm_register_toplevel_resources(p, pbm); - printk("%s: PSYCHO PCI Bus Module ver[%x:%x]\n", pbm->name, pbm->chip_version, pbm->chip_revision); - prop = of_find_property(dp, "ranges", &len); - if (prop) { - pbm->pbm_ranges = prop->value; - pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); - } else { - pbm->num_pbm_ranges = 0; - } - - prop = of_find_property(dp, "interrupt-map", &len); - if (prop) { - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - - prop = of_find_property(dp, "interrupt-map-mask", NULL); - pbm->pbm_intmask = prop->value; - } else { - pbm->num_pbm_intmap = 0; - } + pci_determine_mem_io_space(pbm); prop = of_find_property(dp, "bus-range", NULL); busrange = prop->value; @@ -1246,7 +1131,7 @@ void psycho_init(struct device_node *dp, { struct linux_prom64_registers *pr_regs; struct pci_controller_info *p; - struct pci_iommu *iommu; + struct iommu *iommu; struct property *prop; u32 upa_portid; int is_pbm_a; @@ -1269,7 +1154,7 @@ void psycho_init(struct device_node *dp, prom_printf("PSYCHO: Fatal memory allocation error.\n"); prom_halt(); } - iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC); + iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); if (!iommu) { prom_printf("PSYCHO: Fatal memory allocation error.\n"); prom_halt(); @@ -1282,10 +1167,7 @@ void psycho_init(struct device_node *dp, p->pbm_A.portid = upa_portid; p->pbm_B.portid = upa_portid; p->index = pci_num_controllers++; - p->pbms_same_domain = 0; p->scan_bus = psycho_scan_bus; - p->base_address_update = psycho_base_address_update; - p->resource_adjust = psycho_resource_adjust; p->pci_ops = &psycho_ops; prop = of_find_property(dp, "reg", NULL); diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index 94bb681..397862f 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -1,7 +1,6 @@ -/* $Id: pci_sabre.c,v 1.42 2002/01/23 11:27:32 davem Exp $ - * pci_sabre.c: Sabre specific PCI controller support. +/* pci_sabre.c: Sabre specific PCI controller support. * - * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) + * Copyright (C) 1997, 1998, 1999, 2007 David S. Miller (davem@davemloft.net) * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) */ @@ -254,9 +253,6 @@ static int __sabre_out_of_range(struct p return 0; return ((pbm->parent == 0) || - ((pbm == &pbm->parent->pbm_B) && - (bus == pbm->pci_first_busno) && - PCI_SLOT(devfn) > 8) || ((pbm == &pbm->parent->pbm_A) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 8)); @@ -322,6 +318,12 @@ static int __sabre_read_pci_cfg(struct p static int sabre_read_pci_cfg(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value) { + struct pci_pbm_info *pbm = bus->sysdata; + + if (bus == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_read_pci_cfg(bus, devfn, where, + size, value); + if (!bus->number && sabre_out_of_range(devfn)) { switch (size) { case 1: @@ -438,6 +440,12 @@ static int __sabre_write_pci_cfg(struct static int sabre_write_pci_cfg(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value) { + struct pci_pbm_info *pbm = bus->sysdata; + + if (bus == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_write_pci_cfg(bus, devfn, where, + size, value); + if (bus->number) return __sabre_write_pci_cfg(bus, devfn, where, size, value); @@ -490,7 +498,7 @@ static void sabre_check_iommu_error(stru unsigned long afsr, unsigned long afar) { - struct pci_iommu *iommu = p->pbm_A.iommu; + struct iommu *iommu = p->pbm_A.iommu; unsigned long iommu_tag[16]; unsigned long iommu_data[16]; unsigned long flags; @@ -710,8 +718,8 @@ static irqreturn_t sabre_pcierr_intr_oth p->index); ret = IRQ_HANDLED; } - pci_read_config_word(sabre_root_bus->self, - PCI_STATUS, &stat); + pci_bus_read_config_word(sabre_root_bus, 0, + PCI_STATUS, &stat); if (stat & (PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_REC_TARGET_ABORT | @@ -719,8 +727,8 @@ static irqreturn_t sabre_pcierr_intr_oth PCI_STATUS_SIG_SYSTEM_ERROR)) { printk("SABRE%d: PCI bus error, PCI_STATUS[%04x]\n", p->index, stat); - pci_write_config_word(sabre_root_bus->self, - PCI_STATUS, 0xffff); + pci_bus_write_config_word(sabre_root_bus, 0, + PCI_STATUS, 0xffff); ret = IRQ_HANDLED; } return ret; @@ -800,12 +808,10 @@ static irqreturn_t sabre_pcierr_intr(int if (error_bits & (SABRE_PIOAFSR_PTA | SABRE_PIOAFSR_STA)) { sabre_check_iommu_error(p, afsr, afar); pci_scan_for_target_abort(p, &p->pbm_A, p->pbm_A.pci_bus); - pci_scan_for_target_abort(p, &p->pbm_B, p->pbm_B.pci_bus); } - if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA)) { + if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA)) pci_scan_for_master_abort(p, &p->pbm_A, p->pbm_A.pci_bus); - pci_scan_for_master_abort(p, &p->pbm_B, p->pbm_B.pci_bus); - } + /* For excessive retries, SABRE/PBM will abort the device * and there is no way to specifically check for excessive * retries in the config space status registers. So what @@ -813,10 +819,8 @@ static irqreturn_t sabre_pcierr_intr(int * abort events. */ - if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR)) { + if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR)) pci_scan_for_parity_error(p, &p->pbm_A, p->pbm_A.pci_bus); - pci_scan_for_parity_error(p, &p->pbm_B, p->pbm_B.pci_bus); - } return IRQ_HANDLED; } @@ -869,144 +873,52 @@ static void sabre_register_error_handler sabre_write(base + SABRE_PCICTRL, tmp); } -static void sabre_resource_adjust(struct pci_dev *pdev, - struct resource *res, - struct resource *root) -{ - struct pci_pbm_info *pbm = pdev->bus->sysdata; - unsigned long base; - - if (res->flags & IORESOURCE_IO) - base = pbm->controller_regs + SABRE_IOSPACE; - else - base = pbm->controller_regs + SABRE_MEMSPACE; - - res->start += base; - res->end += base; -} - -static void sabre_base_address_update(struct pci_dev *pdev, int resource) -{ - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; - struct resource *res; - unsigned long base; - u32 reg; - int where, size, is_64bit; - - res = &pdev->resource[resource]; - if (resource < 6) { - where = PCI_BASE_ADDRESS_0 + (resource * 4); - } else if (resource == PCI_ROM_RESOURCE) { - where = pdev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - is_64bit = 0; - if (res->flags & IORESOURCE_IO) - base = pbm->controller_regs + SABRE_IOSPACE; - else { - base = pbm->controller_regs + SABRE_MEMSPACE; - if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) - == PCI_BASE_ADDRESS_MEM_TYPE_64) - is_64bit = 1; - } - - size = res->end - res->start; - pci_read_config_dword(pdev, where, ®); - reg = ((reg & size) | - (((u32)(res->start - base)) & ~size)); - if (resource == PCI_ROM_RESOURCE) { - reg |= PCI_ROM_ADDRESS_ENABLE; - res->flags |= IORESOURCE_ROM_ENABLE; - } - pci_write_config_dword(pdev, where, reg); - - /* This knows that the upper 32-bits of the address - * must be zero. Our PCI common layer enforces this. - */ - if (is_64bit) - pci_write_config_dword(pdev, where + 4, 0); -} - static void apb_init(struct pci_controller_info *p, struct pci_bus *sabre_bus) { struct pci_dev *pdev; list_for_each_entry(pdev, &sabre_bus->devices, bus_list) { - if (pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SIMBA) { - u32 word32; u16 word16; - sabre_read_pci_cfg(pdev->bus, pdev->devfn, - PCI_COMMAND, 2, &word32); - word16 = (u16) word32; + pci_read_config_word(pdev, PCI_COMMAND, &word16); word16 |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; - word32 = (u32) word16; - sabre_write_pci_cfg(pdev->bus, pdev->devfn, - PCI_COMMAND, 2, word32); + pci_write_config_word(pdev, PCI_COMMAND, word16); /* Status register bits are "write 1 to clear". */ - sabre_write_pci_cfg(pdev->bus, pdev->devfn, - PCI_STATUS, 2, 0xffff); - sabre_write_pci_cfg(pdev->bus, pdev->devfn, - PCI_SEC_STATUS, 2, 0xffff); + pci_write_config_word(pdev, PCI_STATUS, 0xffff); + pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff); /* Use a primary/seconday latency timer value * of 64. */ - sabre_write_pci_cfg(pdev->bus, pdev->devfn, - PCI_LATENCY_TIMER, 1, 64); - sabre_write_pci_cfg(pdev->bus, pdev->devfn, - PCI_SEC_LATENCY_TIMER, 1, 64); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); + pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 64); /* Enable reporting/forwarding of master aborts, * parity, and SERR. */ - sabre_write_pci_cfg(pdev->bus, pdev->devfn, - PCI_BRIDGE_CONTROL, 1, - (PCI_BRIDGE_CTL_PARITY | - PCI_BRIDGE_CTL_SERR | - PCI_BRIDGE_CTL_MASTER_ABORT)); + pci_write_config_byte(pdev, PCI_BRIDGE_CONTROL, + (PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_MASTER_ABORT)); } } } -static struct pcidev_cookie *alloc_bridge_cookie(struct pci_pbm_info *pbm) -{ - struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - - if (!cookie) { - prom_printf("SABRE: Critical allocation failure.\n"); - prom_halt(); - } - - /* All we care about is the PBM. */ - cookie->pbm = pbm; - - return cookie; -} - static void sabre_scan_bus(struct pci_controller_info *p) { static int once; - struct pci_bus *sabre_bus, *pbus; - struct pci_pbm_info *pbm; - struct pcidev_cookie *cookie; - int sabres_scanned; + struct pci_bus *pbus; /* The APB bridge speaks to the Sabre host PCI bridge * at 66Mhz, but the front side of APB runs at 33Mhz * for both segments. */ p->pbm_A.is_66mhz_capable = 0; - p->pbm_B.is_66mhz_capable = 0; /* This driver has not been verified to handle * multiple SABREs yet, so trap this. @@ -1020,56 +932,13 @@ static void sabre_scan_bus(struct pci_co } once++; - cookie = alloc_bridge_cookie(&p->pbm_A); - - sabre_bus = pci_scan_bus(p->pci_first_busno, - p->pci_ops, - &p->pbm_A); - pci_fixup_host_bridge_self(sabre_bus); - sabre_bus->self->sysdata = cookie; - - sabre_root_bus = sabre_bus; - - apb_init(p, sabre_bus); - - sabres_scanned = 0; - - list_for_each_entry(pbus, &sabre_bus->children, node) { - - if (pbus->number == p->pbm_A.pci_first_busno) { - pbm = &p->pbm_A; - } else if (pbus->number == p->pbm_B.pci_first_busno) { - pbm = &p->pbm_B; - } else - continue; - - cookie = alloc_bridge_cookie(pbm); - pbus->self->sysdata = cookie; - - sabres_scanned++; + pbus = pci_scan_one_pbm(&p->pbm_A); + if (!pbus) + return; - pbus->sysdata = pbm; - pbm->pci_bus = pbus; - pci_fill_in_pbm_cookies(pbus, pbm, pbm->prom_node); - pci_record_assignments(pbm, pbus); - pci_assign_unassigned(pbm, pbus); - pci_fixup_irq(pbm, pbus); - pci_determine_66mhz_disposition(pbm, pbus); - pci_setup_busmastering(pbm, pbus); - } + sabre_root_bus = pbus; - if (!sabres_scanned) { - /* Hummingbird, no APBs. */ - pbm = &p->pbm_A; - sabre_bus->sysdata = pbm; - pbm->pci_bus = sabre_bus; - pci_fill_in_pbm_cookies(sabre_bus, pbm, pbm->prom_node); - pci_record_assignments(pbm, sabre_bus); - pci_assign_unassigned(pbm, sabre_bus); - pci_fixup_irq(pbm, sabre_bus); - pci_determine_66mhz_disposition(pbm, sabre_bus); - pci_setup_busmastering(pbm, sabre_bus); - } + apb_init(p, pbus); sabre_register_error_handlers(p); } @@ -1078,7 +947,7 @@ static void sabre_iommu_init(struct pci_ int tsbsize, unsigned long dvma_offset, u32 dma_mask) { - struct pci_iommu *iommu = p->pbm_A.iommu; + struct iommu *iommu = p->pbm_A.iommu; unsigned long i; u64 control; @@ -1126,224 +995,31 @@ static void sabre_iommu_init(struct pci_ sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL, control); } -static void pbm_register_toplevel_resources(struct pci_controller_info *p, - struct pci_pbm_info *pbm) -{ - char *name = pbm->name; - unsigned long ibase = p->pbm_A.controller_regs + SABRE_IOSPACE; - unsigned long mbase = p->pbm_A.controller_regs + SABRE_MEMSPACE; - unsigned int devfn; - unsigned long first, last, i; - u8 *addr, map; - - sprintf(name, "SABRE%d PBM%c", - p->index, - (pbm == &p->pbm_A ? 'A' : 'B')); - pbm->io_space.name = pbm->mem_space.name = name; - - devfn = PCI_DEVFN(1, (pbm == &p->pbm_A) ? 0 : 1); - addr = sabre_pci_config_mkaddr(pbm, 0, devfn, APB_IO_ADDRESS_MAP); - map = 0; - pci_config_read8(addr, &map); - - first = 8; - last = 0; - for (i = 0; i < 8; i++) { - if ((map & (1 << i)) != 0) { - if (first > i) - first = i; - if (last < i) - last = i; - } - } - pbm->io_space.start = ibase + (first << 21UL); - pbm->io_space.end = ibase + (last << 21UL) + ((1 << 21UL) - 1); - pbm->io_space.flags = IORESOURCE_IO; - - addr = sabre_pci_config_mkaddr(pbm, 0, devfn, APB_MEM_ADDRESS_MAP); - map = 0; - pci_config_read8(addr, &map); - - first = 8; - last = 0; - for (i = 0; i < 8; i++) { - if ((map & (1 << i)) != 0) { - if (first > i) - first = i; - if (last < i) - last = i; - } - } - pbm->mem_space.start = mbase + (first << 29UL); - pbm->mem_space.end = mbase + (last << 29UL) + ((1 << 29UL) - 1); - pbm->mem_space.flags = IORESOURCE_MEM; - - if (request_resource(&ioport_resource, &pbm->io_space) < 0) { - prom_printf("Cannot register PBM-%c's IO space.\n", - (pbm == &p->pbm_A ? 'A' : 'B')); - prom_halt(); - } - if (request_resource(&iomem_resource, &pbm->mem_space) < 0) { - prom_printf("Cannot register PBM-%c's MEM space.\n", - (pbm == &p->pbm_A ? 'A' : 'B')); - prom_halt(); - } - - /* Register legacy regions if this PBM covers that area. */ - if (pbm->io_space.start == ibase && - pbm->mem_space.start == mbase) - pci_register_legacy_regions(&pbm->io_space, - &pbm->mem_space); -} - -static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 dma_start, u32 dma_end) +static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp) { struct pci_pbm_info *pbm; - struct device_node *node; - struct property *prop; - u32 *busrange; - int len, simbas_found; - - simbas_found = 0; - node = dp->child; - while (node != NULL) { - if (strcmp(node->name, "pci")) - goto next_pci; - - prop = of_find_property(node, "model", NULL); - if (!prop || strncmp(prop->value, "SUNW,simba", prop->length)) - goto next_pci; - - simbas_found++; - - prop = of_find_property(node, "bus-range", NULL); - busrange = prop->value; - if (busrange[0] == 1) - pbm = &p->pbm_B; - else - pbm = &p->pbm_A; - - pbm->name = node->full_name; - printk("%s: SABRE PCI Bus Module\n", pbm->name); - - pbm->chip_type = PBM_CHIP_TYPE_SABRE; - pbm->parent = p; - pbm->prom_node = node; - pbm->pci_first_slot = 1; - pbm->pci_first_busno = busrange[0]; - pbm->pci_last_busno = busrange[1]; - - prop = of_find_property(node, "ranges", &len); - if (prop) { - pbm->pbm_ranges = prop->value; - pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); - } else { - pbm->num_pbm_ranges = 0; - } - prop = of_find_property(node, "interrupt-map", &len); - if (prop) { - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - - prop = of_find_property(node, "interrupt-map-mask", - NULL); - pbm->pbm_intmask = prop->value; - } else { - pbm->num_pbm_intmap = 0; - } + pbm = &p->pbm_A; + pbm->name = dp->full_name; + printk("%s: SABRE PCI Bus Module\n", pbm->name); - pbm_register_toplevel_resources(p, pbm); - - next_pci: - node = node->sibling; - } - if (simbas_found == 0) { - struct resource *rp; + pbm->chip_type = PBM_CHIP_TYPE_SABRE; + pbm->parent = p; + pbm->prom_node = dp; + pbm->pci_first_busno = p->pci_first_busno; + pbm->pci_last_busno = p->pci_last_busno; - /* No APBs underneath, probably this is a hummingbird - * system. - */ - pbm = &p->pbm_A; - pbm->parent = p; - pbm->prom_node = dp; - pbm->pci_first_busno = p->pci_first_busno; - pbm->pci_last_busno = p->pci_last_busno; - - prop = of_find_property(dp, "ranges", &len); - if (prop) { - pbm->pbm_ranges = prop->value; - pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); - } else { - pbm->num_pbm_ranges = 0; - } - - prop = of_find_property(dp, "interrupt-map", &len); - if (prop) { - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - - prop = of_find_property(dp, "interrupt-map-mask", - NULL); - pbm->pbm_intmask = prop->value; - } else { - pbm->num_pbm_intmap = 0; - } - - pbm->name = dp->full_name; - printk("%s: SABRE PCI Bus Module\n", pbm->name); - - pbm->io_space.name = pbm->mem_space.name = pbm->name; - - /* Hack up top-level resources. */ - pbm->io_space.start = p->pbm_A.controller_regs + SABRE_IOSPACE; - pbm->io_space.end = pbm->io_space.start + (1UL << 24) - 1UL; - pbm->io_space.flags = IORESOURCE_IO; - - pbm->mem_space.start = - (p->pbm_A.controller_regs + SABRE_MEMSPACE); - pbm->mem_space.end = - (pbm->mem_space.start + ((1UL << 32UL) - 1UL)); - pbm->mem_space.flags = IORESOURCE_MEM; - - if (request_resource(&ioport_resource, &pbm->io_space) < 0) { - prom_printf("Cannot register Hummingbird's IO space.\n"); - prom_halt(); - } - if (request_resource(&iomem_resource, &pbm->mem_space) < 0) { - prom_printf("Cannot register Hummingbird's MEM space.\n"); - prom_halt(); - } - - rp = kmalloc(sizeof(*rp), GFP_KERNEL); - if (!rp) { - prom_printf("Cannot allocate IOMMU resource.\n"); - prom_halt(); - } - rp->name = "IOMMU"; - rp->start = pbm->mem_space.start + (unsigned long) dma_start; - rp->end = pbm->mem_space.start + (unsigned long) dma_end - 1UL; - rp->flags = IORESOURCE_BUSY; - request_resource(&pbm->mem_space, rp); - - pci_register_legacy_regions(&pbm->io_space, - &pbm->mem_space); - } + pci_determine_mem_io_space(pbm); } void sabre_init(struct device_node *dp, char *model_name) { - struct linux_prom64_registers *pr_regs; + const struct linux_prom64_registers *pr_regs; struct pci_controller_info *p; - struct pci_iommu *iommu; - struct property *prop; + struct iommu *iommu; int tsbsize; - u32 *busrange; - u32 *vdma; + const u32 *busrange; + const u32 *vdma; u32 upa_portid, dma_mask; u64 clear_irq; @@ -1351,13 +1027,9 @@ void sabre_init(struct device_node *dp, if (!strcmp(model_name, "pci108e,a001")) hummingbird_p = 1; else if (!strcmp(model_name, "SUNW,sabre")) { - prop = of_find_property(dp, "compatible", NULL); - if (prop) { - const char *compat = prop->value; - - if (!strcmp(compat, "pci108e,a001")) - hummingbird_p = 1; - } + const char *compat = of_get_property(dp, "compatible", NULL); + if (compat && !strcmp(compat, "pci108e,a001")) + hummingbird_p = 1; if (!hummingbird_p) { struct device_node *dp; @@ -1381,37 +1053,28 @@ void sabre_init(struct device_node *dp, prom_printf("SABRE: Error, kmalloc(pci_iommu) failed.\n"); prom_halt(); } - p->pbm_A.iommu = p->pbm_B.iommu = iommu; + p->pbm_A.iommu = iommu; - upa_portid = 0xff; - prop = of_find_property(dp, "upa-portid", NULL); - if (prop) - upa_portid = *(u32 *) prop->value; + upa_portid = of_getintprop_default(dp, "upa-portid", 0xff); p->next = pci_controller_root; pci_controller_root = p; p->pbm_A.portid = upa_portid; - p->pbm_B.portid = upa_portid; p->index = pci_num_controllers++; - p->pbms_same_domain = 1; p->scan_bus = sabre_scan_bus; - p->base_address_update = sabre_base_address_update; - p->resource_adjust = sabre_resource_adjust; p->pci_ops = &sabre_ops; /* * Map in SABRE register set and report the presence of this SABRE. */ - prop = of_find_property(dp, "reg", NULL); - pr_regs = prop->value; + pr_regs = of_get_property(dp, "reg", NULL); /* * First REG in property is base of entire SABRE register space. */ p->pbm_A.controller_regs = pr_regs[0].phys_addr; - p->pbm_B.controller_regs = pr_regs[0].phys_addr; /* Clear interrupts */ @@ -1429,11 +1092,10 @@ void sabre_init(struct device_node *dp, SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN)); /* Now map in PCI config space for entire SABRE. */ - p->pbm_A.config_space = p->pbm_B.config_space = + p->pbm_A.config_space = (p->pbm_A.controller_regs + SABRE_CONFIGSPACE); - prop = of_find_property(dp, "virtual-dma", NULL); - vdma = prop->value; + vdma = of_get_property(dp, "virtual-dma", NULL); dma_mask = vdma[0]; switch(vdma[1]) { @@ -1457,13 +1119,12 @@ void sabre_init(struct device_node *dp, sabre_iommu_init(p, tsbsize, vdma[0], dma_mask); - prop = of_find_property(dp, "bus-range", NULL); - busrange = prop->value; + busrange = of_get_property(dp, "bus-range", NULL); p->pci_first_busno = busrange[0]; p->pci_last_busno = busrange[1]; /* * Look for APB underneath. */ - sabre_pbm_init(p, dp, vdma[0], vdma[0] + vdma[1]); + sabre_pbm_init(p, dp); } diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c index 66911b1..91a7385 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -1,7 +1,6 @@ -/* $Id: pci_schizo.c,v 1.24 2002/01/23 11:27:32 davem Exp $ - * pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support. +/* pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support. * - * Copyright (C) 2001, 2002, 2003 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2002, 2003, 2007 David S. Miller (davem@davemloft.net) */ #include @@ -126,6 +125,9 @@ static int schizo_read_pci_cfg(struct pc u16 tmp16; u8 tmp8; + if (bus_dev == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where, + size, value); switch (size) { case 1: *value = 0xff; @@ -179,6 +181,9 @@ static int schizo_write_pci_cfg(struct p unsigned char bus = bus_dev->number; u32 *addr; + if (bus_dev == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where, + size, value); addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where); if (!addr) return PCIBIOS_SUCCESSFUL; @@ -274,7 +279,7 @@ #define SCHIZO_STCLINE_FOFN 0x0000000000 static void __schizo_check_stc_error_pbm(struct pci_pbm_info *pbm, enum schizo_error_type type) { - struct pci_strbuf *strbuf = &pbm->stc; + struct strbuf *strbuf = &pbm->stc; unsigned long regbase = pbm->pbm_regs; unsigned long err_base, tag_base, line_base; u64 control; @@ -382,7 +387,7 @@ #define SCHIZO_IOMMU_DATA_PPAGE 0x000000 static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, enum schizo_error_type type) { - struct pci_iommu *iommu = pbm->iommu; + struct iommu *iommu = pbm->iommu; unsigned long iommu_tag[16]; unsigned long iommu_data[16]; unsigned long flags; @@ -1229,42 +1234,8 @@ static void pbm_config_busmastering(stru pci_config_write8(addr, 64); } -static void pbm_scan_bus(struct pci_controller_info *p, - struct pci_pbm_info *pbm) -{ - struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - - if (!cookie) { - prom_printf("%s: Critical allocation failure.\n", pbm->name); - prom_halt(); - } - - /* All we care about is the PBM. */ - cookie->pbm = pbm; - - pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, - p->pci_ops, - pbm); - pci_fixup_host_bridge_self(pbm->pci_bus); - pbm->pci_bus->self->sysdata = cookie; - - pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node); - pci_record_assignments(pbm, pbm->pci_bus); - pci_assign_unassigned(pbm, pbm->pci_bus); - pci_fixup_irq(pbm, pbm->pci_bus); - pci_determine_66mhz_disposition(pbm, pbm->pci_bus); - pci_setup_busmastering(pbm, pbm->pci_bus); -} - -static void __schizo_scan_bus(struct pci_controller_info *p, - int chip_type) +static void schizo_scan_bus(struct pci_controller_info *p) { - if (!p->pbm_B.prom_node || !p->pbm_A.prom_node) { - printk("PCI: Only one PCI bus module of controller found.\n"); - printk("PCI: Ignoring entire controller.\n"); - return; - } - pbm_config_busmastering(&p->pbm_B); p->pbm_B.is_66mhz_capable = (of_find_property(p->pbm_B.prom_node, "66mhz-capable", NULL) @@ -1273,154 +1244,19 @@ static void __schizo_scan_bus(struct pci p->pbm_A.is_66mhz_capable = (of_find_property(p->pbm_A.prom_node, "66mhz-capable", NULL) != NULL); - pbm_scan_bus(p, &p->pbm_B); - pbm_scan_bus(p, &p->pbm_A); + + p->pbm_B.pci_bus = pci_scan_one_pbm(&p->pbm_B); + p->pbm_A.pci_bus = pci_scan_one_pbm(&p->pbm_A); /* After the PCI bus scan is complete, we can register * the error interrupt handlers. */ - if (chip_type == PBM_CHIP_TYPE_TOMATILLO) + if (p->pbm_B.chip_type == PBM_CHIP_TYPE_TOMATILLO) tomatillo_register_error_handlers(p); else schizo_register_error_handlers(p); } -static void schizo_scan_bus(struct pci_controller_info *p) -{ - __schizo_scan_bus(p, PBM_CHIP_TYPE_SCHIZO); -} - -static void tomatillo_scan_bus(struct pci_controller_info *p) -{ - __schizo_scan_bus(p, PBM_CHIP_TYPE_TOMATILLO); -} - -static void schizo_base_address_update(struct pci_dev *pdev, int resource) -{ - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; - struct resource *res, *root; - u32 reg; - int where, size, is_64bit; - - res = &pdev->resource[resource]; - if (resource < 6) { - where = PCI_BASE_ADDRESS_0 + (resource * 4); - } else if (resource == PCI_ROM_RESOURCE) { - where = pdev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - is_64bit = 0; - if (res->flags & IORESOURCE_IO) - root = &pbm->io_space; - else { - root = &pbm->mem_space; - if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) - == PCI_BASE_ADDRESS_MEM_TYPE_64) - is_64bit = 1; - } - - size = res->end - res->start; - pci_read_config_dword(pdev, where, ®); - reg = ((reg & size) | - (((u32)(res->start - root->start)) & ~size)); - if (resource == PCI_ROM_RESOURCE) { - reg |= PCI_ROM_ADDRESS_ENABLE; - res->flags |= IORESOURCE_ROM_ENABLE; - } - pci_write_config_dword(pdev, where, reg); - - /* This knows that the upper 32-bits of the address - * must be zero. Our PCI common layer enforces this. - */ - if (is_64bit) - pci_write_config_dword(pdev, where + 4, 0); -} - -static void schizo_resource_adjust(struct pci_dev *pdev, - struct resource *res, - struct resource *root) -{ - res->start += root->start; - res->end += root->start; -} - -/* Use ranges property to determine where PCI MEM, I/O, and Config - * space are for this PCI bus module. - */ -static void schizo_determine_mem_io_space(struct pci_pbm_info *pbm) -{ - int i, saw_cfg, saw_mem, saw_io; - - saw_cfg = saw_mem = saw_io = 0; - for (i = 0; i < pbm->num_pbm_ranges; i++) { - struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i]; - unsigned long a; - int type; - - type = (pr->child_phys_hi >> 24) & 0x3; - a = (((unsigned long)pr->parent_phys_hi << 32UL) | - ((unsigned long)pr->parent_phys_lo << 0UL)); - - switch (type) { - case 0: - /* PCI config space, 16MB */ - pbm->config_space = a; - saw_cfg = 1; - break; - - case 1: - /* 16-bit IO space, 16MB */ - pbm->io_space.start = a; - pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); - pbm->io_space.flags = IORESOURCE_IO; - saw_io = 1; - break; - - case 2: - /* 32-bit MEM space, 2GB */ - pbm->mem_space.start = a; - pbm->mem_space.end = a + (0x80000000UL - 1UL); - pbm->mem_space.flags = IORESOURCE_MEM; - saw_mem = 1; - break; - - default: - break; - }; - } - - if (!saw_cfg || !saw_io || !saw_mem) { - prom_printf("%s: Fatal error, missing %s PBM range.\n", - pbm->name, - ((!saw_cfg ? - "CFG" : - (!saw_io ? - "IO" : "MEM")))); - prom_halt(); - } - - printk("%s: PCI CFG[%lx] IO[%lx] MEM[%lx]\n", - pbm->name, - pbm->config_space, - pbm->io_space.start, - pbm->mem_space.start); -} - -static void pbm_register_toplevel_resources(struct pci_controller_info *p, - struct pci_pbm_info *pbm) -{ - pbm->io_space.name = pbm->mem_space.name = pbm->name; - - request_resource(&ioport_resource, &pbm->io_space); - request_resource(&iomem_resource, &pbm->mem_space); - pci_register_legacy_regions(&pbm->io_space, - &pbm->mem_space); -} - #define SCHIZO_STRBUF_CONTROL (0x02800UL) #define SCHIZO_STRBUF_FLUSH (0x02808UL) #define SCHIZO_STRBUF_FSYNC (0x02810UL) @@ -1472,7 +1308,7 @@ #define SCHIZO_IOMMU_CTXFLUSH (0x00218U static void schizo_pbm_iommu_init(struct pci_pbm_info *pbm) { - struct pci_iommu *iommu = pbm->iommu; + struct iommu *iommu = pbm->iommu; unsigned long i, tagbase, database; struct property *prop; u32 vdma[2], dma_mask; @@ -1654,14 +1490,12 @@ static void schizo_pbm_init(struct pci_c struct device_node *dp, u32 portid, int chip_type) { - struct linux_prom64_registers *regs; - struct property *prop; - unsigned int *busrange; + const struct linux_prom64_registers *regs; + const unsigned int *busrange; struct pci_pbm_info *pbm; const char *chipset_name; - u32 *ino_bitmap; + const u32 *ino_bitmap; int is_pbm_a; - int len; switch (chip_type) { case PBM_CHIP_TYPE_TOMATILLO: @@ -1689,11 +1523,9 @@ static void schizo_pbm_init(struct pci_c * 3) PBM PCI config space * 4) Ichip regs */ - prop = of_find_property(dp, "reg", NULL); - regs = prop->value; + regs = of_get_property(dp, "reg", NULL); is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000); - if (is_pbm_a) pbm = &p->pbm_A; else @@ -1702,17 +1534,10 @@ static void schizo_pbm_init(struct pci_c pbm->portid = portid; pbm->parent = p; pbm->prom_node = dp; - pbm->pci_first_slot = 1; pbm->chip_type = chip_type; - pbm->chip_version = 0; - prop = of_find_property(dp, "version#", NULL); - if (prop) - pbm->chip_version = *(int *) prop->value; - pbm->chip_revision = 0; - prop = of_find_property(dp, "module-revision#", NULL); - if (prop) - pbm->chip_revision = *(int *) prop->value; + pbm->chip_version = of_getintprop_default(dp, "version#", 0); + pbm->chip_revision = of_getintprop_default(dp, "module-version#", 0); pbm->pbm_regs = regs[0].phys_addr; pbm->controller_regs = regs[1].phys_addr - 0x10000UL; @@ -1723,40 +1548,18 @@ static void schizo_pbm_init(struct pci_c pbm->name = dp->full_name; printk("%s: %s PCI Bus Module ver[%x:%x]\n", - pbm->name, - (chip_type == PBM_CHIP_TYPE_TOMATILLO ? - "TOMATILLO" : "SCHIZO"), + pbm->name, chipset_name, pbm->chip_version, pbm->chip_revision); schizo_pbm_hw_init(pbm); - prop = of_find_property(dp, "ranges", &len); - pbm->pbm_ranges = prop->value; - pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); + pci_determine_mem_io_space(pbm); - schizo_determine_mem_io_space(pbm); - pbm_register_toplevel_resources(p, pbm); - - prop = of_find_property(dp, "interrupt-map", &len); - if (prop) { - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - - prop = of_find_property(dp, "interrupt-map-mask", NULL); - pbm->pbm_intmask = prop->value; - } else { - pbm->num_pbm_intmap = 0; - } - - prop = of_find_property(dp, "ino-bitmap", NULL); - ino_bitmap = prop->value; + ino_bitmap = of_get_property(dp, "ino-bitmap", NULL); pbm->ino_bitmap = (((u64)ino_bitmap[1] << 32UL) | ((u64)ino_bitmap[0] << 0UL)); - prop = of_find_property(dp, "bus-range", NULL); - busrange = prop->value; + busrange = of_get_property(dp, "bus-range", NULL); pbm->pci_first_busno = busrange[0]; pbm->pci_last_busno = busrange[1]; @@ -1777,15 +1580,10 @@ static inline int portid_compare(u32 x, static void __schizo_init(struct device_node *dp, char *model_name, int chip_type) { struct pci_controller_info *p; - struct pci_iommu *iommu; - struct property *prop; - int is_pbm_a; + struct iommu *iommu; u32 portid; - portid = 0xff; - prop = of_find_property(dp, "portid", NULL); - if (prop) - portid = *(u32 *) prop->value; + portid = of_getintprop_default(dp, "portid", 0xff); for (p = pci_controller_root; p; p = p->next) { struct pci_pbm_info *pbm; @@ -1798,48 +1596,43 @@ static void __schizo_init(struct device_ &p->pbm_B); if (portid_compare(pbm->portid, portid, chip_type)) { - is_pbm_a = (p->pbm_A.prom_node == NULL); schizo_pbm_init(p, dp, portid, chip_type); return; } } p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) { - prom_printf("SCHIZO: Fatal memory allocation error.\n"); - prom_halt(); - } + if (!p) + goto memfail; + + iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); + if (!iommu) + goto memfail; - iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC); - if (!iommu) { - prom_printf("SCHIZO: Fatal memory allocation error.\n"); - prom_halt(); - } p->pbm_A.iommu = iommu; - iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC); - if (!iommu) { - prom_printf("SCHIZO: Fatal memory allocation error.\n"); - prom_halt(); - } + iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); + if (!iommu) + goto memfail; + p->pbm_B.iommu = iommu; p->next = pci_controller_root; pci_controller_root = p; p->index = pci_num_controllers++; - p->pbms_same_domain = 0; - p->scan_bus = (chip_type == PBM_CHIP_TYPE_TOMATILLO ? - tomatillo_scan_bus : - schizo_scan_bus); - p->base_address_update = schizo_base_address_update; - p->resource_adjust = schizo_resource_adjust; + p->scan_bus = schizo_scan_bus; p->pci_ops = &schizo_ops; /* Like PSYCHO we have a 2GB aligned area for memory space. */ pci_memspace_mask = 0x7fffffffUL; schizo_pbm_init(p, dp, portid, chip_type); + return; + +memfail: + prom_printf("SCHIZO: Fatal memory allocation error.\n"); + prom_halt(); } void schizo_init(struct device_node *dp, char *model_name) diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index ec22cd6..94295c2 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -1,6 +1,6 @@ /* pci_sun4v.c: SUN4V specific PCI controller support. * - * Copyright (C) 2006 David S. Miller (davem@davemloft.net) + * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net) */ #include @@ -29,7 +29,7 @@ #include "pci_sun4v.h" #define PGLIST_NENTS (PAGE_SIZE / sizeof(u64)) -struct pci_iommu_batch { +struct iommu_batch { struct pci_dev *pdev; /* Device mapping is for. */ unsigned long prot; /* IOMMU page protections */ unsigned long entry; /* Index into IOTSB. */ @@ -37,12 +37,12 @@ struct pci_iommu_batch { unsigned long npages; /* Number of pages in list. */ }; -static DEFINE_PER_CPU(struct pci_iommu_batch, pci_iommu_batch); +static DEFINE_PER_CPU(struct iommu_batch, pci_iommu_batch); /* Interrupts must be disabled. */ static inline void pci_iommu_batch_start(struct pci_dev *pdev, unsigned long prot, unsigned long entry) { - struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch); + struct iommu_batch *p = &__get_cpu_var(pci_iommu_batch); p->pdev = pdev; p->prot = prot; @@ -51,10 +51,10 @@ static inline void pci_iommu_batch_start } /* Interrupts must be disabled. */ -static long pci_iommu_batch_flush(struct pci_iommu_batch *p) +static long pci_iommu_batch_flush(struct iommu_batch *p) { - struct pcidev_cookie *pcp = p->pdev->sysdata; - unsigned long devhandle = pcp->pbm->devhandle; + struct pci_pbm_info *pbm = p->pdev->dev.archdata.host_controller; + unsigned long devhandle = pbm->devhandle; unsigned long prot = p->prot; unsigned long entry = p->entry; u64 *pglist = p->pglist; @@ -89,7 +89,7 @@ static long pci_iommu_batch_flush(struct /* Interrupts must be disabled. */ static inline long pci_iommu_batch_add(u64 phys_page) { - struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch); + struct iommu_batch *p = &__get_cpu_var(pci_iommu_batch); BUG_ON(p->npages >= PGLIST_NENTS); @@ -103,14 +103,14 @@ static inline long pci_iommu_batch_add(u /* Interrupts must be disabled. */ static inline long pci_iommu_batch_end(void) { - struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch); + struct iommu_batch *p = &__get_cpu_var(pci_iommu_batch); BUG_ON(p->npages >= PGLIST_NENTS); return pci_iommu_batch_flush(p); } -static long pci_arena_alloc(struct pci_iommu_arena *arena, unsigned long npages) +static long pci_arena_alloc(struct iommu_arena *arena, unsigned long npages) { unsigned long n, i, start, end, limit; int pass; @@ -149,7 +149,7 @@ again: return n; } -static void pci_arena_free(struct pci_iommu_arena *arena, unsigned long base, unsigned long npages) +static void pci_arena_free(struct iommu_arena *arena, unsigned long base, unsigned long npages) { unsigned long i; @@ -159,8 +159,7 @@ static void pci_arena_free(struct pci_io static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp, gfp_t gfp) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct iommu *iommu; unsigned long flags, order, first_page, npages, n; void *ret; long entry; @@ -178,8 +177,7 @@ static void *pci_4v_alloc_consistent(str memset((char *)first_page, 0, PAGE_SIZE << order); - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; + iommu = pdev->dev.archdata.iommu; spin_lock_irqsave(&iommu->lock, flags); entry = pci_arena_alloc(&iommu->arena, npages); @@ -226,15 +224,15 @@ arena_alloc_fail: static void pci_4v_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_t dvma) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct pci_pbm_info *pbm; + struct iommu *iommu; unsigned long flags, order, npages, entry; u32 devhandle; npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT; - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - devhandle = pcp->pbm->devhandle; + iommu = pdev->dev.archdata.iommu; + pbm = pdev->dev.archdata.host_controller; + devhandle = pbm->devhandle; entry = ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT); spin_lock_irqsave(&iommu->lock, flags); @@ -259,16 +257,14 @@ static void pci_4v_free_consistent(struc static dma_addr_t pci_4v_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct iommu *iommu; unsigned long flags, npages, oaddr; unsigned long i, base_paddr; u32 bus_addr, ret; unsigned long prot; long entry; - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; + iommu = pdev->dev.archdata.iommu; if (unlikely(direction == PCI_DMA_NONE)) goto bad; @@ -324,8 +320,8 @@ iommu_map_fail: static void pci_4v_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct pci_pbm_info *pbm; + struct iommu *iommu; unsigned long flags, npages; long entry; u32 devhandle; @@ -336,9 +332,9 @@ static void pci_4v_unmap_single(struct p return; } - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - devhandle = pcp->pbm->devhandle; + iommu = pdev->dev.archdata.iommu; + pbm = pdev->dev.archdata.host_controller; + devhandle = pbm->devhandle; npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK); npages >>= IO_PAGE_SHIFT; @@ -460,8 +456,7 @@ iommu_map_failed: static int pci_4v_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct iommu *iommu; unsigned long flags, npages, prot; u32 dma_base; struct scatterlist *sgtmp; @@ -480,8 +475,7 @@ static int pci_4v_map_sg(struct pci_dev return 1; } - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; + iommu = pdev->dev.archdata.iommu; if (unlikely(direction == PCI_DMA_NONE)) goto bad; @@ -537,8 +531,8 @@ iommu_map_failed: static void pci_4v_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; + struct pci_pbm_info *pbm; + struct iommu *iommu; unsigned long flags, i, npages; long entry; u32 devhandle, bus_addr; @@ -548,9 +542,9 @@ static void pci_4v_unmap_sg(struct pci_d WARN_ON(1); } - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - devhandle = pcp->pbm->devhandle; + iommu = pdev->dev.archdata.iommu; + pbm = pdev->dev.archdata.host_controller; + devhandle = pbm->devhandle; bus_addr = sglist->dma_address & IO_PAGE_MASK; @@ -589,7 +583,7 @@ static void pci_4v_dma_sync_sg_for_cpu(s /* Nothing to do... */ } -struct pci_iommu_ops pci_sun4v_iommu_ops = { +const struct pci_iommu_ops pci_sun4v_iommu_ops = { .alloc_consistent = pci_4v_alloc_consistent, .free_consistent = pci_4v_free_consistent, .map_single = pci_4v_map_single, @@ -600,132 +594,12 @@ struct pci_iommu_ops pci_sun4v_iommu_ops .dma_sync_sg_for_cpu = pci_4v_dma_sync_sg_for_cpu, }; -/* SUN4V PCI configuration space accessors. */ - -struct pdev_entry { - struct pdev_entry *next; - u32 devhandle; - unsigned int bus; - unsigned int device; - unsigned int func; -}; - -#define PDEV_HTAB_SIZE 16 -#define PDEV_HTAB_MASK (PDEV_HTAB_SIZE - 1) -static struct pdev_entry *pdev_htab[PDEV_HTAB_SIZE]; - -static inline unsigned int pdev_hashfn(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) -{ - unsigned int val; - - val = (devhandle ^ (devhandle >> 4)); - val ^= bus; - val ^= device; - val ^= func; - - return val & PDEV_HTAB_MASK; -} - -static int pdev_htab_add(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) -{ - struct pdev_entry *p = kmalloc(sizeof(*p), GFP_KERNEL); - struct pdev_entry **slot; - - if (!p) - return -ENOMEM; - - slot = &pdev_htab[pdev_hashfn(devhandle, bus, device, func)]; - p->next = *slot; - *slot = p; - - p->devhandle = devhandle; - p->bus = bus; - p->device = device; - p->func = func; - - return 0; -} - -/* Recursively descend into the OBP device tree, rooted at toplevel_node, - * looking for a PCI device matching bus and devfn. - */ -static int obp_find(struct device_node *toplevel_node, unsigned int bus, unsigned int devfn) -{ - toplevel_node = toplevel_node->child; - - while (toplevel_node != NULL) { - struct linux_prom_pci_registers *regs; - struct property *prop; - int ret; - - ret = obp_find(toplevel_node, bus, devfn); - if (ret != 0) - return ret; - - prop = of_find_property(toplevel_node, "reg", NULL); - if (!prop) - goto next_sibling; - - regs = prop->value; - if (((regs->phys_hi >> 16) & 0xff) == bus && - ((regs->phys_hi >> 8) & 0xff) == devfn) - break; - - next_sibling: - toplevel_node = toplevel_node->sibling; - } - - return toplevel_node != NULL; -} - -static int pdev_htab_populate(struct pci_pbm_info *pbm) -{ - u32 devhandle = pbm->devhandle; - unsigned int bus; - - for (bus = pbm->pci_first_busno; bus <= pbm->pci_last_busno; bus++) { - unsigned int devfn; - - for (devfn = 0; devfn < 256; devfn++) { - unsigned int device = PCI_SLOT(devfn); - unsigned int func = PCI_FUNC(devfn); - - if (obp_find(pbm->prom_node, bus, devfn)) { - int err = pdev_htab_add(devhandle, bus, - device, func); - if (err) - return err; - } - } - } - - return 0; -} - -static struct pdev_entry *pdev_find(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) -{ - struct pdev_entry *p; - - p = pdev_htab[pdev_hashfn(devhandle, bus, device, func)]; - while (p) { - if (p->devhandle == devhandle && - p->bus == bus && - p->device == device && - p->func == func) - break; - - p = p->next; - } - - return p; -} - static inline int pci_sun4v_out_of_range(struct pci_pbm_info *pbm, unsigned int bus, unsigned int device, unsigned int func) { if (bus < pbm->pci_first_busno || bus > pbm->pci_last_busno) return 1; - return pdev_find(pbm->devhandle, bus, device, func) == NULL; + return 0; } static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, @@ -738,6 +612,9 @@ static int pci_sun4v_read_pci_cfg(struct unsigned int func = PCI_FUNC(devfn); unsigned long ret; + if (bus_dev == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where, + size, value); if (pci_sun4v_out_of_range(pbm, bus, device, func)) { ret = ~0UL; } else { @@ -776,6 +653,9 @@ static int pci_sun4v_write_pci_cfg(struc unsigned int func = PCI_FUNC(devfn); unsigned long ret; + if (bus_dev == pbm->pci_bus && devfn == 0x00) + return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where, + size, value); if (pci_sun4v_out_of_range(pbm, bus, device, func)) { /* Do nothing. */ } else { @@ -800,27 +680,7 @@ static struct pci_ops pci_sun4v_ops = { static void pbm_scan_bus(struct pci_controller_info *p, struct pci_pbm_info *pbm) { - struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - - if (!cookie) { - prom_printf("%s: Critical allocation failure.\n", pbm->name); - prom_halt(); - } - - /* All we care about is the PBM. */ - cookie->pbm = pbm; - - pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, p->pci_ops, pbm); -#if 0 - pci_fixup_host_bridge_self(pbm->pci_bus); - pbm->pci_bus->self->sysdata = cookie; -#endif - pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node); - pci_record_assignments(pbm, pbm->pci_bus); - pci_assign_unassigned(pbm, pbm->pci_bus); - pci_fixup_irq(pbm, pbm->pci_bus); - pci_determine_66mhz_disposition(pbm, pbm->pci_bus); - pci_setup_busmastering(pbm, pbm->pci_bus); + pbm->pci_bus = pci_scan_one_pbm(pbm); } static void pci_sun4v_scan_bus(struct pci_controller_info *p) @@ -844,130 +704,10 @@ static void pci_sun4v_scan_bus(struct pc /* XXX register error interrupt handlers XXX */ } -static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource) -{ - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; - struct resource *res, *root; - u32 reg; - int where, size, is_64bit; - - res = &pdev->resource[resource]; - if (resource < 6) { - where = PCI_BASE_ADDRESS_0 + (resource * 4); - } else if (resource == PCI_ROM_RESOURCE) { - where = pdev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - /* XXX 64-bit MEM handling is not %100 correct... XXX */ - is_64bit = 0; - if (res->flags & IORESOURCE_IO) - root = &pbm->io_space; - else { - root = &pbm->mem_space; - if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) - == PCI_BASE_ADDRESS_MEM_TYPE_64) - is_64bit = 1; - } - - size = res->end - res->start; - pci_read_config_dword(pdev, where, ®); - reg = ((reg & size) | - (((u32)(res->start - root->start)) & ~size)); - if (resource == PCI_ROM_RESOURCE) { - reg |= PCI_ROM_ADDRESS_ENABLE; - res->flags |= IORESOURCE_ROM_ENABLE; - } - pci_write_config_dword(pdev, where, reg); - - /* This knows that the upper 32-bits of the address - * must be zero. Our PCI common layer enforces this. - */ - if (is_64bit) - pci_write_config_dword(pdev, where + 4, 0); -} - -static void pci_sun4v_resource_adjust(struct pci_dev *pdev, - struct resource *res, - struct resource *root) -{ - res->start += root->start; - res->end += root->start; -} - -/* Use ranges property to determine where PCI MEM, I/O, and Config - * space are for this PCI bus module. - */ -static void pci_sun4v_determine_mem_io_space(struct pci_pbm_info *pbm) -{ - int i, saw_mem, saw_io; - - saw_mem = saw_io = 0; - for (i = 0; i < pbm->num_pbm_ranges; i++) { - struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i]; - unsigned long a; - int type; - - type = (pr->child_phys_hi >> 24) & 0x3; - a = (((unsigned long)pr->parent_phys_hi << 32UL) | - ((unsigned long)pr->parent_phys_lo << 0UL)); - - switch (type) { - case 1: - /* 16-bit IO space, 16MB */ - pbm->io_space.start = a; - pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); - pbm->io_space.flags = IORESOURCE_IO; - saw_io = 1; - break; - - case 2: - /* 32-bit MEM space, 2GB */ - pbm->mem_space.start = a; - pbm->mem_space.end = a + (0x80000000UL - 1UL); - pbm->mem_space.flags = IORESOURCE_MEM; - saw_mem = 1; - break; - - case 3: - /* XXX 64-bit MEM handling XXX */ - - default: - break; - }; - } - - if (!saw_io || !saw_mem) { - prom_printf("%s: Fatal error, missing %s PBM range.\n", - pbm->name, - (!saw_io ? "IO" : "MEM")); - prom_halt(); - } - - printk("%s: PCI IO[%lx] MEM[%lx]\n", - pbm->name, - pbm->io_space.start, - pbm->mem_space.start); -} - -static void pbm_register_toplevel_resources(struct pci_controller_info *p, - struct pci_pbm_info *pbm) -{ - pbm->io_space.name = pbm->mem_space.name = pbm->name; - - request_resource(&ioport_resource, &pbm->io_space); - request_resource(&iomem_resource, &pbm->mem_space); - pci_register_legacy_regions(&pbm->io_space, - &pbm->mem_space); -} - static unsigned long probe_existing_entries(struct pci_pbm_info *pbm, - struct pci_iommu *iommu) + struct iommu *iommu) { - struct pci_iommu_arena *arena = &iommu->arena; + struct iommu_arena *arena = &iommu->arena; unsigned long i, cnt = 0; u32 devhandle; @@ -994,7 +734,7 @@ static unsigned long probe_existing_entr static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm) { - struct pci_iommu *iommu = pbm->iommu; + struct iommu *iommu = pbm->iommu; struct property *prop; unsigned long num_tsb_entries, sz; u32 vdma[2], dma_mask, dma_offset; @@ -1281,7 +1021,7 @@ h_error: static void pci_sun4v_msi_init(struct pci_pbm_info *pbm) { - u32 *val; + const u32 *val; int len; val = of_get_property(pbm->prom_node, "#msi-eqs", &len); @@ -1289,16 +1029,16 @@ static void pci_sun4v_msi_init(struct pc goto no_msi; pbm->msiq_num = *val; if (pbm->msiq_num) { - struct msiq_prop { + const struct msiq_prop { u32 first_msiq; u32 num_msiq; u32 first_devino; } *mqp; - struct msi_range_prop { + const struct msi_range_prop { u32 first_msi; u32 num_msi; } *mrng; - struct addr_range_prop { + const struct addr_range_prop { u32 msi32_high; u32 msi32_low; u32 msi32_len; @@ -1410,8 +1150,7 @@ static int pci_sun4v_setup_msi_irq(unsig struct pci_dev *pdev, struct msi_desc *entry) { - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; + struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; unsigned long devino, msiqid; struct msi_msg msg; int msi_num, err; @@ -1455,7 +1194,7 @@ static int pci_sun4v_setup_msi_irq(unsig if (pci_sun4v_msi_setvalid(pbm->devhandle, msi_num, HV_MSIVALID_VALID)) goto out_err; - pcp->msi_num = msi_num; + pdev->dev.archdata.msi_num = msi_num; if (entry->msi_attrib.is_64) { msg.address_hi = pbm->msi64_start >> 32; @@ -1484,12 +1223,11 @@ out_err: static void pci_sun4v_teardown_msi_irq(unsigned int virt_irq, struct pci_dev *pdev) { - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; + struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; unsigned long msiqid, err; unsigned int msi_num; - msi_num = pcp->msi_num; + msi_num = pdev->dev.archdata.msi_num; err = pci_sun4v_msi_getmsiq(pbm->devhandle, msi_num, &msiqid); if (err) { printk(KERN_ERR "%s: getmsiq gives error %lu\n", @@ -1516,8 +1254,6 @@ #endif /* !(CONFIG_PCI_MSI) */ static void pci_sun4v_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 devhandle) { struct pci_pbm_info *pbm; - struct property *prop; - int len, i; if (devhandle & 0x40) pbm = &p->pbm_B; @@ -1526,7 +1262,6 @@ static void pci_sun4v_pbm_init(struct pc pbm->parent = p; pbm->prom_node = dp; - pbm->pci_first_slot = 1; pbm->devhandle = devhandle; @@ -1534,39 +1269,17 @@ static void pci_sun4v_pbm_init(struct pc printk("%s: SUN4V PCI Bus Module\n", pbm->name); - prop = of_find_property(dp, "ranges", &len); - pbm->pbm_ranges = prop->value; - pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); - - /* Mask out the top 8 bits of the ranges, leaving the real - * physical address. - */ - for (i = 0; i < pbm->num_pbm_ranges; i++) - pbm->pbm_ranges[i].parent_phys_hi &= 0x0fffffff; - - pci_sun4v_determine_mem_io_space(pbm); - pbm_register_toplevel_resources(p, pbm); - - prop = of_find_property(dp, "interrupt-map", &len); - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - - prop = of_find_property(dp, "interrupt-map-mask", NULL); - pbm->pbm_intmask = prop->value; + pci_determine_mem_io_space(pbm); pci_sun4v_get_bus_range(pbm); pci_sun4v_iommu_init(pbm); pci_sun4v_msi_init(pbm); - - pdev_htab_populate(pbm); } void sun4v_pci_init(struct device_node *dp, char *model_name) { struct pci_controller_info *p; - struct pci_iommu *iommu; + struct iommu *iommu; struct property *prop; struct linux_prom64_registers *regs; u32 devhandle; @@ -1606,13 +1319,13 @@ void sun4v_pci_init(struct device_node * if (!p) goto fatal_memory_error; - iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC); + iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); if (!iommu) goto fatal_memory_error; p->pbm_A.iommu = iommu; - iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC); + iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); if (!iommu) goto fatal_memory_error; @@ -1622,11 +1335,8 @@ void sun4v_pci_init(struct device_node * pci_controller_root = p; p->index = pci_num_controllers++; - p->pbms_same_domain = 0; p->scan_bus = pci_sun4v_scan_bus; - p->base_address_update = pci_sun4v_base_address_update; - p->resource_adjust = pci_sun4v_resource_adjust; #ifdef CONFIG_PCI_MSI p->setup_msi_irq = pci_sun4v_setup_msi_irq; p->teardown_msi_irq = pci_sun4v_teardown_msi_irq; diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index b291060..a114151 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -28,6 +28,7 @@ #include #include #include #include +#include #include #include @@ -88,12 +89,14 @@ void cpu_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while(1) { - if (need_resched()) { - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } - sparc64_yield(); + tick_nohz_stop_sched_tick(); + while (!need_resched()) + sparc64_yield(); + tick_nohz_restart_sched_tick(); + + preempt_enable_no_resched(); + schedule(); + preempt_disable(); } } diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c index 0917c24..5e1fcd0 100644 --- a/arch/sparc64/kernel/prom.c +++ b/arch/sparc64/kernel/prom.c @@ -36,12 +36,13 @@ static struct device_node *allnodes; */ static DEFINE_RWLOCK(devtree_lock); -int of_device_is_compatible(struct device_node *device, const char *compat) +int of_device_is_compatible(const struct device_node *device, + const char *compat) { const char* cp; int cplen, l; - cp = (char *) of_get_property(device, "compatible", &cplen); + cp = of_get_property(device, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) { @@ -154,13 +155,14 @@ struct device_node *of_find_compatible_n } EXPORT_SYMBOL(of_find_compatible_node); -struct property *of_find_property(struct device_node *np, const char *name, +struct property *of_find_property(const struct device_node *np, + const char *name, int *lenp) { struct property *pp; for (pp = np->properties; pp != 0; pp = pp->next) { - if (strcmp(pp->name, name) == 0) { + if (strcasecmp(pp->name, name) == 0) { if (lenp != 0) *lenp = pp->length; break; @@ -174,7 +176,8 @@ EXPORT_SYMBOL(of_find_property); * Find a property with a given name for a given node * and return the value. */ -void *of_get_property(struct device_node *np, const char *name, int *lenp) +const void *of_get_property(const struct device_node *np, const char *name, + int *lenp) { struct property *pp = of_find_property(np,name,lenp); return pp ? pp->value : NULL; @@ -196,7 +199,7 @@ EXPORT_SYMBOL(of_getintprop_default); int of_n_addr_cells(struct device_node *np) { - int* ip; + const int* ip; do { if (np->parent) np = np->parent; @@ -211,7 +214,7 @@ EXPORT_SYMBOL(of_n_addr_cells); int of_n_size_cells(struct device_node *np) { - int* ip; + const int* ip; do { if (np->parent) np = np->parent; @@ -243,7 +246,7 @@ int of_set_property(struct device_node * while (*prevp) { struct property *prop = *prevp; - if (!strcmp(prop->name, name)) { + if (!strcasecmp(prop->name, name)) { void *old_val = prop->value; int ret; @@ -397,7 +400,7 @@ static unsigned int psycho_irq_build(str static void psycho_irq_trans_init(struct device_node *dp) { - struct linux_prom64_registers *regs; + const struct linux_prom64_registers *regs; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = psycho_irq_build; @@ -547,7 +550,7 @@ #define sabre_iclr_offset(ino) static int sabre_device_needs_wsync(struct device_node *dp) { struct device_node *parent = dp->parent; - char *parent_model, *parent_compat; + const char *parent_model, *parent_compat; /* This traversal up towards the root is meant to * handle two cases: @@ -589,7 +592,7 @@ static unsigned int sabre_irq_build(stru { struct sabre_irq_data *irq_data = _data; unsigned long controller_regs = irq_data->controller_regs; - struct linux_prom_pci_registers *regs; + const struct linux_prom_pci_registers *regs; unsigned long imap, iclr; unsigned long imap_off, iclr_off; int inofixup = 0; @@ -639,9 +642,9 @@ static unsigned int sabre_irq_build(stru static void sabre_irq_trans_init(struct device_node *dp) { - struct linux_prom64_registers *regs; + const struct linux_prom64_registers *regs; struct sabre_irq_data *irq_data; - u32 *busrange; + const u32 *busrange; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = sabre_irq_build; @@ -795,7 +798,7 @@ static unsigned int schizo_irq_build(str static void __schizo_irq_trans_init(struct device_node *dp, int is_tomatillo) { - struct linux_prom64_registers *regs; + const struct linux_prom64_registers *regs; struct schizo_irq_data *irq_data; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); @@ -836,7 +839,7 @@ static unsigned int pci_sun4v_irq_build( static void pci_sun4v_irq_trans_init(struct device_node *dp) { - struct linux_prom64_registers *regs; + const struct linux_prom64_registers *regs; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = pci_sun4v_irq_build; @@ -940,7 +943,7 @@ static unsigned int sbus_of_build_irq(st void *_data) { unsigned long reg_base = (unsigned long) _data; - struct linux_prom_registers *regs; + const struct linux_prom_registers *regs; unsigned long imap, iclr; int sbus_slot = 0; int sbus_level = 0; @@ -994,7 +997,7 @@ static unsigned int sbus_of_build_irq(st static void sbus_irq_trans_init(struct device_node *dp) { - struct linux_prom64_registers *regs; + const struct linux_prom64_registers *regs; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = sbus_of_build_irq; @@ -1080,7 +1083,7 @@ static unsigned int sun4v_vdev_irq_build static void sun4v_vdev_irq_trans_init(struct device_node *dp) { - struct linux_prom64_registers *regs; + const struct linux_prom64_registers *regs; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = sun4v_vdev_irq_build; diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c index 14f78fb..3b05428 100644 --- a/arch/sparc64/kernel/sbus.c +++ b/arch/sparc64/kernel/sbus.c @@ -26,23 +26,9 @@ #include "iommu_common.h" #define MAP_BASE ((u32)0xc0000000) -struct sbus_iommu_arena { - unsigned long *map; - unsigned int hint; - unsigned int limit; -}; - -struct sbus_iommu { - spinlock_t lock; - - struct sbus_iommu_arena arena; - - iopte_t *page_table; - unsigned long strbuf_regs; - unsigned long iommu_regs; - unsigned long sbus_control_reg; - - volatile unsigned long strbuf_flushflag; +struct sbus_info { + struct iommu iommu; + struct strbuf strbuf; }; /* Offsets from iommu_regs */ @@ -58,16 +44,17 @@ #define IOMMU_DRAMDIAG (0x4600UL - 0x240 #define IOMMU_DRAM_VALID (1UL << 30UL) -static void __iommu_flushall(struct sbus_iommu *iommu) +static void __iommu_flushall(struct iommu *iommu) { - unsigned long tag = iommu->iommu_regs + IOMMU_TAGDIAG; + unsigned long tag; int entry; + tag = iommu->iommu_control + (IOMMU_TAGDIAG - IOMMU_CONTROL); for (entry = 0; entry < 16; entry++) { upa_writeq(0, tag); tag += 8UL; } - upa_readq(iommu->sbus_control_reg); + upa_readq(iommu->write_complete_reg); } /* Offsets from strbuf_regs */ @@ -82,15 +69,14 @@ #define STRBUF_LTAGDIAG (0x5900UL - 0x28 #define STRBUF_TAG_VALID 0x02UL -static void sbus_strbuf_flush(struct sbus_iommu *iommu, u32 base, unsigned long npages, int direction) +static void sbus_strbuf_flush(struct iommu *iommu, struct strbuf *strbuf, u32 base, unsigned long npages, int direction) { unsigned long n; int limit; n = npages; while (n--) - upa_writeq(base + (n << IO_PAGE_SHIFT), - iommu->strbuf_regs + STRBUF_PFLUSH); + upa_writeq(base + (n << IO_PAGE_SHIFT), strbuf->strbuf_pflush); /* If the device could not have possibly put dirty data into * the streaming cache, no flush-flag synchronization needs @@ -99,15 +85,14 @@ static void sbus_strbuf_flush(struct sbu if (direction == SBUS_DMA_TODEVICE) return; - iommu->strbuf_flushflag = 0UL; + *(strbuf->strbuf_flushflag) = 0UL; /* Whoopee cushion! */ - upa_writeq(__pa(&iommu->strbuf_flushflag), - iommu->strbuf_regs + STRBUF_FSYNC); - upa_readq(iommu->sbus_control_reg); + upa_writeq(strbuf->strbuf_flushflag_pa, strbuf->strbuf_fsync); + upa_readq(iommu->write_complete_reg); limit = 100000; - while (iommu->strbuf_flushflag == 0UL) { + while (*(strbuf->strbuf_flushflag) == 0UL) { limit--; if (!limit) break; @@ -121,9 +106,9 @@ static void sbus_strbuf_flush(struct sbu } /* Based largely upon the ppc64 iommu allocator. */ -static long sbus_arena_alloc(struct sbus_iommu *iommu, unsigned long npages) +static long sbus_arena_alloc(struct iommu *iommu, unsigned long npages) { - struct sbus_iommu_arena *arena = &iommu->arena; + struct iommu_arena *arena = &iommu->arena; unsigned long n, i, start, end, limit; int pass; @@ -162,7 +147,7 @@ again: return n; } -static void sbus_arena_free(struct sbus_iommu_arena *arena, unsigned long base, unsigned long npages) +static void sbus_arena_free(struct iommu_arena *arena, unsigned long base, unsigned long npages) { unsigned long i; @@ -170,7 +155,7 @@ static void sbus_arena_free(struct sbus_ __clear_bit(i, arena->map); } -static void sbus_iommu_table_init(struct sbus_iommu *iommu, unsigned int tsbsize) +static void sbus_iommu_table_init(struct iommu *iommu, unsigned int tsbsize) { unsigned long tsbbase, order, sz, num_tsb_entries; @@ -178,13 +163,14 @@ static void sbus_iommu_table_init(struct /* Setup initial software IOMMU state. */ spin_lock_init(&iommu->lock); + iommu->page_table_map_base = MAP_BASE; /* Allocate and initialize the free area map. */ sz = num_tsb_entries / 8; sz = (sz + 7UL) & ~7UL; iommu->arena.map = kzalloc(sz, GFP_KERNEL); if (!iommu->arena.map) { - prom_printf("PCI_IOMMU: Error, kmalloc(arena.map) failed.\n"); + prom_printf("SBUS_IOMMU: Error, kmalloc(arena.map) failed.\n"); prom_halt(); } iommu->arena.limit = num_tsb_entries; @@ -200,7 +186,7 @@ static void sbus_iommu_table_init(struct memset(iommu->page_table, 0, tsbsize); } -static inline iopte_t *alloc_npages(struct sbus_iommu *iommu, unsigned long npages) +static inline iopte_t *alloc_npages(struct iommu *iommu, unsigned long npages) { long entry; @@ -211,14 +197,15 @@ static inline iopte_t *alloc_npages(stru return iommu->page_table + entry; } -static inline void free_npages(struct sbus_iommu *iommu, dma_addr_t base, unsigned long npages) +static inline void free_npages(struct iommu *iommu, dma_addr_t base, unsigned long npages) { sbus_arena_free(&iommu->arena, base >> IO_PAGE_SHIFT, npages); } void *sbus_alloc_consistent(struct sbus_dev *sdev, size_t size, dma_addr_t *dvma_addr) { - struct sbus_iommu *iommu; + struct sbus_info *info; + struct iommu *iommu; iopte_t *iopte; unsigned long flags, order, first_page; void *ret; @@ -234,7 +221,8 @@ void *sbus_alloc_consistent(struct sbus_ return NULL; memset((char *)first_page, 0, PAGE_SIZE << order); - iommu = sdev->bus->iommu; + info = sdev->bus->iommu; + iommu = &info->iommu; spin_lock_irqsave(&iommu->lock, flags); iopte = alloc_npages(iommu, size >> IO_PAGE_SHIFT); @@ -245,7 +233,7 @@ void *sbus_alloc_consistent(struct sbus_ return NULL; } - *dvma_addr = (MAP_BASE + + *dvma_addr = (iommu->page_table_map_base + ((iopte - iommu->page_table) << IO_PAGE_SHIFT)); ret = (void *) first_page; npages = size >> IO_PAGE_SHIFT; @@ -263,18 +251,20 @@ void *sbus_alloc_consistent(struct sbus_ void sbus_free_consistent(struct sbus_dev *sdev, size_t size, void *cpu, dma_addr_t dvma) { - struct sbus_iommu *iommu; + struct sbus_info *info; + struct iommu *iommu; iopte_t *iopte; unsigned long flags, order, npages; npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT; - iommu = sdev->bus->iommu; + info = sdev->bus->iommu; + iommu = &info->iommu; iopte = iommu->page_table + - ((dvma - MAP_BASE) >> IO_PAGE_SHIFT); + ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT); spin_lock_irqsave(&iommu->lock, flags); - free_npages(iommu, dvma - MAP_BASE, npages); + free_npages(iommu, dvma - iommu->page_table_map_base, npages); spin_unlock_irqrestore(&iommu->lock, flags); @@ -285,14 +275,16 @@ void sbus_free_consistent(struct sbus_de dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *ptr, size_t sz, int direction) { - struct sbus_iommu *iommu; + struct sbus_info *info; + struct iommu *iommu; iopte_t *base; unsigned long flags, npages, oaddr; unsigned long i, base_paddr; u32 bus_addr, ret; unsigned long iopte_protection; - iommu = sdev->bus->iommu; + info = sdev->bus->iommu; + iommu = &info->iommu; if (unlikely(direction == SBUS_DMA_NONE)) BUG(); @@ -308,7 +300,7 @@ dma_addr_t sbus_map_single(struct sbus_d if (unlikely(!base)) BUG(); - bus_addr = (MAP_BASE + + bus_addr = (iommu->page_table_map_base + ((base - iommu->page_table) << IO_PAGE_SHIFT)); ret = bus_addr | (oaddr & ~IO_PAGE_MASK); base_paddr = __pa(oaddr & IO_PAGE_MASK); @@ -325,7 +317,9 @@ dma_addr_t sbus_map_single(struct sbus_d void sbus_unmap_single(struct sbus_dev *sdev, dma_addr_t bus_addr, size_t sz, int direction) { - struct sbus_iommu *iommu = sdev->bus->iommu; + struct sbus_info *info = sdev->bus->iommu; + struct iommu *iommu = &info->iommu; + struct strbuf *strbuf = &info->strbuf; iopte_t *base; unsigned long flags, npages, i; @@ -335,15 +329,15 @@ void sbus_unmap_single(struct sbus_dev * npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK); npages >>= IO_PAGE_SHIFT; base = iommu->page_table + - ((bus_addr - MAP_BASE) >> IO_PAGE_SHIFT); + ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); bus_addr &= IO_PAGE_MASK; spin_lock_irqsave(&iommu->lock, flags); - sbus_strbuf_flush(iommu, bus_addr, npages, direction); + sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction); for (i = 0; i < npages; i++) iopte_val(base[i]) = 0UL; - free_npages(iommu, bus_addr - MAP_BASE, npages); + free_npages(iommu, bus_addr - iommu->page_table_map_base, npages); spin_unlock_irqrestore(&iommu->lock, flags); } @@ -425,7 +419,8 @@ static inline void fill_sg(iopte_t *iopt int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sglist, int nelems, int direction) { - struct sbus_iommu *iommu; + struct sbus_info *info; + struct iommu *iommu; unsigned long flags, npages, iopte_protection; iopte_t *base; u32 dma_base; @@ -442,7 +437,8 @@ int sbus_map_sg(struct sbus_dev *sdev, s return 1; } - iommu = sdev->bus->iommu; + info = sdev->bus->iommu; + iommu = &info->iommu; if (unlikely(direction == SBUS_DMA_NONE)) BUG(); @@ -456,7 +452,7 @@ int sbus_map_sg(struct sbus_dev *sdev, s if (unlikely(base == NULL)) BUG(); - dma_base = MAP_BASE + + dma_base = iommu->page_table_map_base + ((base - iommu->page_table) << IO_PAGE_SHIFT); /* Normalize DVMA addresses. */ @@ -485,7 +481,9 @@ #endif void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sglist, int nelems, int direction) { - struct sbus_iommu *iommu; + struct sbus_info *info; + struct iommu *iommu; + struct strbuf *strbuf; iopte_t *base; unsigned long flags, i, npages; u32 bus_addr; @@ -493,7 +491,9 @@ void sbus_unmap_sg(struct sbus_dev *sdev if (unlikely(direction == SBUS_DMA_NONE)) BUG(); - iommu = sdev->bus->iommu; + info = sdev->bus->iommu; + iommu = &info->iommu; + strbuf = &info->strbuf; bus_addr = sglist->dma_address & IO_PAGE_MASK; @@ -505,29 +505,33 @@ void sbus_unmap_sg(struct sbus_dev *sdev bus_addr) >> IO_PAGE_SHIFT; base = iommu->page_table + - ((bus_addr - MAP_BASE) >> IO_PAGE_SHIFT); + ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); spin_lock_irqsave(&iommu->lock, flags); - sbus_strbuf_flush(iommu, bus_addr, npages, direction); + sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction); for (i = 0; i < npages; i++) iopte_val(base[i]) = 0UL; - free_npages(iommu, bus_addr - MAP_BASE, npages); + free_npages(iommu, bus_addr - iommu->page_table_map_base, npages); spin_unlock_irqrestore(&iommu->lock, flags); } void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t bus_addr, size_t sz, int direction) { - struct sbus_iommu *iommu; + struct sbus_info *info; + struct iommu *iommu; + struct strbuf *strbuf; unsigned long flags, npages; - iommu = sdev->bus->iommu; + info = sdev->bus->iommu; + iommu = &info->iommu; + strbuf = &info->strbuf; npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK); npages >>= IO_PAGE_SHIFT; bus_addr &= IO_PAGE_MASK; spin_lock_irqsave(&iommu->lock, flags); - sbus_strbuf_flush(iommu, bus_addr, npages, direction); + sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction); spin_unlock_irqrestore(&iommu->lock, flags); } @@ -537,11 +541,15 @@ void sbus_dma_sync_single_for_device(str void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sglist, int nelems, int direction) { - struct sbus_iommu *iommu; + struct sbus_info *info; + struct iommu *iommu; + struct strbuf *strbuf; unsigned long flags, npages, i; u32 bus_addr; - iommu = sdev->bus->iommu; + info = sdev->bus->iommu; + iommu = &info->iommu; + strbuf = &info->strbuf; bus_addr = sglist[0].dma_address & IO_PAGE_MASK; for (i = 0; i < nelems; i++) { @@ -553,7 +561,7 @@ void sbus_dma_sync_sg_for_cpu(struct sbu - bus_addr) >> IO_PAGE_SHIFT; spin_lock_irqsave(&iommu->lock, flags); - sbus_strbuf_flush(iommu, bus_addr, npages, direction); + sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction); spin_unlock_irqrestore(&iommu->lock, flags); } @@ -564,12 +572,13 @@ void sbus_dma_sync_sg_for_device(struct /* Enable 64-bit DVMA mode for the given device. */ void sbus_set_sbus64(struct sbus_dev *sdev, int bursts) { - struct sbus_iommu *iommu = sdev->bus->iommu; + struct sbus_info *info = sdev->bus->iommu; + struct iommu *iommu = &info->iommu; int slot = sdev->slot; unsigned long cfg_reg; u64 val; - cfg_reg = iommu->sbus_control_reg; + cfg_reg = iommu->write_complete_reg; switch (slot) { case 0: cfg_reg += 0x20UL; @@ -704,8 +713,9 @@ static unsigned long sysio_imap_to_iclr( unsigned int sbus_build_irq(void *buscookie, unsigned int ino) { struct sbus_bus *sbus = (struct sbus_bus *)buscookie; - struct sbus_iommu *iommu = sbus->iommu; - unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL; + struct sbus_info *info = sbus->iommu; + struct iommu *iommu = &info->iommu; + unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long imap, iclr; int sbus_level = 0; @@ -766,8 +776,9 @@ #define SYSIO_UEAFSR_RESV2 0x0000001fff static irqreturn_t sysio_ue_handler(int irq, void *dev_id) { struct sbus_bus *sbus = dev_id; - struct sbus_iommu *iommu = sbus->iommu; - unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL; + struct sbus_info *info = sbus->iommu; + struct iommu *iommu = &info->iommu; + unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long afsr_reg, afar_reg; unsigned long afsr, afar, error_bits; int reported; @@ -838,8 +849,9 @@ #define SYSIO_CEAFSR_RESV2 0x0000001fff static irqreturn_t sysio_ce_handler(int irq, void *dev_id) { struct sbus_bus *sbus = dev_id; - struct sbus_iommu *iommu = sbus->iommu; - unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL; + struct sbus_info *info = sbus->iommu; + struct iommu *iommu = &info->iommu; + unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long afsr_reg, afar_reg; unsigned long afsr, afar, error_bits; int reported; @@ -915,12 +927,13 @@ #define SYSIO_SBAFSR_RESV3 0x0000001fff static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) { struct sbus_bus *sbus = dev_id; - struct sbus_iommu *iommu = sbus->iommu; + struct sbus_info *info = sbus->iommu; + struct iommu *iommu = &info->iommu; unsigned long afsr_reg, afar_reg, reg_base; unsigned long afsr, afar, error_bits; int reported; - reg_base = iommu->sbus_control_reg - 0x2000UL; + reg_base = iommu->write_complete_reg - 0x2000UL; afsr_reg = reg_base + SYSIO_SBUS_AFSR; afar_reg = reg_base + SYSIO_SBUS_AFAR; @@ -982,8 +995,9 @@ #define SYSIO_SBUSERR_INO 0x36 static void __init sysio_register_error_handlers(struct sbus_bus *sbus) { - struct sbus_iommu *iommu = sbus->iommu; - unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL; + struct sbus_info *info = sbus->iommu; + struct iommu *iommu = &info->iommu; + unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned int irq; u64 control; @@ -1017,18 +1031,20 @@ static void __init sysio_register_error_ SYSIO_ECNTRL_CEEN), reg_base + ECC_CONTROL); - control = upa_readq(iommu->sbus_control_reg); + control = upa_readq(iommu->write_complete_reg); control |= 0x100UL; /* SBUS Error Interrupt Enable */ - upa_writeq(control, iommu->sbus_control_reg); + upa_writeq(control, iommu->write_complete_reg); } /* Boot time initialization. */ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) { - struct linux_prom64_registers *pr; + const struct linux_prom64_registers *pr; struct device_node *dp; - struct sbus_iommu *iommu; - unsigned long regs; + struct sbus_info *info; + struct iommu *iommu; + struct strbuf *strbuf; + unsigned long regs, reg_base; u64 control; int i; @@ -1043,33 +1059,42 @@ static void __init sbus_iommu_init(int _ } regs = pr->phys_addr; - iommu = kmalloc(sizeof(*iommu) + SMP_CACHE_BYTES, GFP_ATOMIC); - if (iommu == NULL) { - prom_printf("sbus_iommu_init: Fatal error, kmalloc(iommu) failed\n"); + info = kzalloc(sizeof(*info), GFP_ATOMIC); + if (info == NULL) { + prom_printf("sbus_iommu_init: Fatal error, " + "kmalloc(info) failed\n"); prom_halt(); } - /* Align on E$ line boundary. */ - iommu = (struct sbus_iommu *) - (((unsigned long)iommu + (SMP_CACHE_BYTES - 1UL)) & - ~(SMP_CACHE_BYTES - 1UL)); + iommu = &info->iommu; + strbuf = &info->strbuf; - memset(iommu, 0, sizeof(*iommu)); + reg_base = regs + SYSIO_IOMMUREG_BASE; + iommu->iommu_control = reg_base + IOMMU_CONTROL; + iommu->iommu_tsbbase = reg_base + IOMMU_TSBBASE; + iommu->iommu_flush = reg_base + IOMMU_FLUSH; - /* Setup spinlock. */ - spin_lock_init(&iommu->lock); + reg_base = regs + SYSIO_STRBUFREG_BASE; + strbuf->strbuf_control = reg_base + STRBUF_CONTROL; + strbuf->strbuf_pflush = reg_base + STRBUF_PFLUSH; + strbuf->strbuf_fsync = reg_base + STRBUF_FSYNC; - /* Init register offsets. */ - iommu->iommu_regs = regs + SYSIO_IOMMUREG_BASE; - iommu->strbuf_regs = regs + SYSIO_STRBUFREG_BASE; + strbuf->strbuf_enabled = 1; + + strbuf->strbuf_flushflag = (volatile unsigned long *) + ((((unsigned long)&strbuf->__flushflag_buf[0]) + + 63UL) + & ~63UL); + strbuf->strbuf_flushflag_pa = (unsigned long) + __pa(strbuf->strbuf_flushflag); /* The SYSIO SBUS control register is used for dummy reads * in order to ensure write completion. */ - iommu->sbus_control_reg = regs + 0x2000UL; + iommu->write_complete_reg = regs + 0x2000UL; /* Link into SYSIO software state. */ - sbus->iommu = iommu; + sbus->iommu = info; printk("SYSIO: UPA portID %x, at %016lx\n", sbus->portid, regs); @@ -1077,40 +1102,44 @@ static void __init sbus_iommu_init(int _ /* Setup for TSB_SIZE=7, TBW_SIZE=0, MMU_DE=1, MMU_EN=1 */ sbus_iommu_table_init(iommu, IO_TSB_SIZE); - control = upa_readq(iommu->iommu_regs + IOMMU_CONTROL); + control = upa_readq(iommu->iommu_control); control = ((7UL << 16UL) | (0UL << 2UL) | (1UL << 1UL) | (1UL << 0UL)); - upa_writeq(control, iommu->iommu_regs + IOMMU_CONTROL); + upa_writeq(control, iommu->iommu_control); /* Clean out any cruft in the IOMMU using * diagnostic accesses. */ for (i = 0; i < 16; i++) { - unsigned long dram = iommu->iommu_regs + IOMMU_DRAMDIAG; - unsigned long tag = iommu->iommu_regs + IOMMU_TAGDIAG; + unsigned long dram, tag; + + dram = iommu->iommu_control + (IOMMU_DRAMDIAG - IOMMU_CONTROL); + tag = iommu->iommu_control + (IOMMU_TAGDIAG - IOMMU_CONTROL); dram += (unsigned long)i * 8UL; tag += (unsigned long)i * 8UL; upa_writeq(0, dram); upa_writeq(0, tag); } - upa_readq(iommu->sbus_control_reg); + upa_readq(iommu->write_complete_reg); /* Give the TSB to SYSIO. */ - upa_writeq(__pa(iommu->page_table), iommu->iommu_regs + IOMMU_TSBBASE); + upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase); /* Setup streaming buffer, DE=1 SB_EN=1 */ control = (1UL << 1UL) | (1UL << 0UL); - upa_writeq(control, iommu->strbuf_regs + STRBUF_CONTROL); + upa_writeq(control, strbuf->strbuf_control); /* Clear out the tags using diagnostics. */ for (i = 0; i < 16; i++) { unsigned long ptag, ltag; - ptag = iommu->strbuf_regs + STRBUF_PTAGDIAG; - ltag = iommu->strbuf_regs + STRBUF_LTAGDIAG; + ptag = strbuf->strbuf_control + + (STRBUF_PTAGDIAG - STRBUF_CONTROL); + ltag = strbuf->strbuf_control + + (STRBUF_LTAGDIAG - STRBUF_CONTROL); ptag += (unsigned long)i * 8UL; ltag += (unsigned long)i * 8UL; @@ -1119,9 +1148,9 @@ static void __init sbus_iommu_init(int _ } /* Enable DVMA arbitration for all devices/slots. */ - control = upa_readq(iommu->sbus_control_reg); + control = upa_readq(iommu->write_complete_reg); control |= 0x3fUL; - upa_writeq(control, iommu->sbus_control_reg); + upa_writeq(control, iommu->write_complete_reg); /* Now some Xfire specific grot... */ if (this_is_starfire) @@ -1133,7 +1162,7 @@ static void __init sbus_iommu_init(int _ void sbus_fill_device_irq(struct sbus_dev *sdev) { struct device_node *dp = of_find_node_by_phandle(sdev->prom_node); - struct linux_prom_irqs *irqs; + const struct linux_prom_irqs *irqs; irqs = of_get_property(dp, "interrupts", NULL); if (!irqs) { diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index fc99f7b..d4f0a70 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -45,7 +45,7 @@ #include extern void calibrate_delay(void); /* Please don't make this stuff initdata!!! --DaveM */ -static unsigned char boot_cpu_id; +unsigned char boot_cpu_id; cpumask_t cpu_online_map __read_mostly = CPU_MASK_NONE; cpumask_t phys_cpu_present_map __read_mostly = CPU_MASK_NONE; @@ -81,8 +81,6 @@ void __init smp_store_cpu_info(int id) struct device_node *dp; int def; - /* multiplier and counter set by - smp_setup_percpu_timer() */ cpu_data(id).udelay_val = loops_per_jiffy; cpu_find_by_mid(id, &dp); @@ -125,7 +123,7 @@ void __init smp_store_cpu_info(int id) cpu_data(id).ecache_size, cpu_data(id).ecache_line_size); } -static void smp_setup_percpu_timer(void); +extern void setup_sparc64_timer(void); static volatile unsigned long callin_flag = 0; @@ -140,7 +138,7 @@ void __init smp_callin(void) __flush_tlb_all(); - smp_setup_percpu_timer(); + setup_sparc64_timer(); if (cheetah_pcache_forced_on) cheetah_enable_pcache(); @@ -177,8 +175,6 @@ void cpu_panic(void) panic("SMP bolixed\n"); } -static unsigned long current_tick_offset __read_mostly; - /* This tick register synchronization scheme is taken entirely from * the ia64 port, see arch/ia64/kernel/smpboot.c for details and credit. * @@ -261,7 +257,7 @@ #endif } else adj = -delta; - tick_ops->add_tick(adj, current_tick_offset); + tick_ops->add_tick(adj); } #if DEBUG_TICK_SYNC t[i].rt = rt; @@ -1180,117 +1176,15 @@ void smp_penguin_jailcell(int irq, struc preempt_enable(); } -#define prof_multiplier(__cpu) cpu_data(__cpu).multiplier -#define prof_counter(__cpu) cpu_data(__cpu).counter - -void smp_percpu_timer_interrupt(struct pt_regs *regs) -{ - unsigned long compare, tick, pstate; - int cpu = smp_processor_id(); - int user = user_mode(regs); - struct pt_regs *old_regs; - - /* - * Check for level 14 softint. - */ - { - unsigned long tick_mask = tick_ops->softint_mask; - - if (!(get_softint() & tick_mask)) { - extern void handler_irq(int, struct pt_regs *); - - handler_irq(14, regs); - return; - } - clear_softint(tick_mask); - } - - old_regs = set_irq_regs(regs); - do { - profile_tick(CPU_PROFILING); - if (!--prof_counter(cpu)) { - irq_enter(); - - if (cpu == boot_cpu_id) { - kstat_this_cpu.irqs[0]++; - timer_tick_interrupt(regs); - } - - update_process_times(user); - - irq_exit(); - - prof_counter(cpu) = prof_multiplier(cpu); - } - - /* Guarantee that the following sequences execute - * uninterrupted. - */ - __asm__ __volatile__("rdpr %%pstate, %0\n\t" - "wrpr %0, %1, %%pstate" - : "=r" (pstate) - : "i" (PSTATE_IE)); - - compare = tick_ops->add_compare(current_tick_offset); - tick = tick_ops->get_tick(); - - /* Restore PSTATE_IE. */ - __asm__ __volatile__("wrpr %0, 0x0, %%pstate" - : /* no outputs */ - : "r" (pstate)); - } while (time_after_eq(tick, compare)); - set_irq_regs(old_regs); -} - -static void __init smp_setup_percpu_timer(void) -{ - int cpu = smp_processor_id(); - unsigned long pstate; - - prof_counter(cpu) = prof_multiplier(cpu) = 1; - - /* Guarantee that the following sequences execute - * uninterrupted. - */ - __asm__ __volatile__("rdpr %%pstate, %0\n\t" - "wrpr %0, %1, %%pstate" - : "=r" (pstate) - : "i" (PSTATE_IE)); - - tick_ops->init_tick(current_tick_offset); - - /* Restore PSTATE_IE. */ - __asm__ __volatile__("wrpr %0, 0x0, %%pstate" - : /* no outputs */ - : "r" (pstate)); -} - void __init smp_tick_init(void) { boot_cpu_id = hard_smp_processor_id(); - current_tick_offset = timer_tick_offset; - - prof_counter(boot_cpu_id) = prof_multiplier(boot_cpu_id) = 1; } /* /proc/profile writes can call this, don't __init it please. */ -static DEFINE_SPINLOCK(prof_setup_lock); - int setup_profiling_timer(unsigned int multiplier) { - unsigned long flags; - int i; - - if ((!multiplier) || (timer_tick_offset / multiplier) < 1000) - return -EINVAL; - - spin_lock_irqsave(&prof_setup_lock, flags); - for_each_possible_cpu(i) - prof_multiplier(i) = multiplier; - current_tick_offset = (timer_tick_offset / multiplier); - spin_unlock_irqrestore(&prof_setup_lock, flags); - - return 0; + return -EINVAL; } static void __init smp_tune_scheduling(void) diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index beffc82..d00f51a 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -212,7 +212,6 @@ EXPORT_SYMBOL(insl); #ifdef CONFIG_PCI EXPORT_SYMBOL(ebus_chain); EXPORT_SYMBOL(isa_chain); -EXPORT_SYMBOL(pci_memspace_mask); EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); EXPORT_SYMBOL(pci_map_single); diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index f84da4f..259063f 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -31,6 +31,9 @@ #include #include #include #include +#include +#include +#include #include #include @@ -60,6 +63,7 @@ static void __iomem *mstk48t59_regs; static int set_rtc_mmss(unsigned long); #define TICK_PRIV_BIT (1UL << 63) +#define TICKCMP_IRQ_BIT (1UL << 63) #ifdef CONFIG_SMP unsigned long profile_pc(struct pt_regs *regs) @@ -93,21 +97,22 @@ static void tick_disable_protection(void : "g2"); } -static void tick_init_tick(unsigned long offset) +static void tick_disable_irq(void) { - tick_disable_protection(); - __asm__ __volatile__( - " rd %%tick, %%g1\n" - " andn %%g1, %1, %%g1\n" " ba,pt %%xcc, 1f\n" - " add %%g1, %0, %%g1\n" + " nop\n" " .align 64\n" - "1: wr %%g1, 0x0, %%tick_cmpr\n" + "1: wr %0, 0x0, %%tick_cmpr\n" " rd %%tick_cmpr, %%g0" : /* no outputs */ - : "r" (offset), "r" (TICK_PRIV_BIT) - : "g1"); + : "r" (TICKCMP_IRQ_BIT)); +} + +static void tick_init_tick(void) +{ + tick_disable_protection(); + tick_disable_irq(); } static unsigned long tick_get_tick(void) @@ -121,20 +126,14 @@ static unsigned long tick_get_tick(void) return ret & ~TICK_PRIV_BIT; } -static unsigned long tick_get_compare(void) +static int tick_add_compare(unsigned long adj) { - unsigned long ret; + unsigned long orig_tick, new_tick, new_compare; - __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" - "mov %0, %0" - : "=r" (ret)); + __asm__ __volatile__("rd %%tick, %0" + : "=r" (orig_tick)); - return ret; -} - -static unsigned long tick_add_compare(unsigned long adj) -{ - unsigned long new_compare; + orig_tick &= ~TICKCMP_IRQ_BIT; /* Workaround for Spitfire Errata (#54 I think??), I discovered * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch @@ -145,44 +144,41 @@ static unsigned long tick_add_compare(un * at the start of an I-cache line, and perform a dummy * read back from %tick_cmpr right after writing to it. -DaveM */ - __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" - "ba,pt %%xcc, 1f\n\t" - " add %0, %1, %0\n\t" + __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" + " add %1, %2, %0\n\t" ".align 64\n" "1:\n\t" "wr %0, 0, %%tick_cmpr\n\t" - "rd %%tick_cmpr, %%g0" - : "=&r" (new_compare) - : "r" (adj)); + "rd %%tick_cmpr, %%g0\n\t" + : "=r" (new_compare) + : "r" (orig_tick), "r" (adj)); - return new_compare; + __asm__ __volatile__("rd %%tick, %0" + : "=r" (new_tick)); + new_tick &= ~TICKCMP_IRQ_BIT; + + return ((long)(new_tick - (orig_tick+adj))) > 0L; } -static unsigned long tick_add_tick(unsigned long adj, unsigned long offset) +static unsigned long tick_add_tick(unsigned long adj) { - unsigned long new_tick, tmp; + unsigned long new_tick; /* Also need to handle Blackbird bug here too. */ __asm__ __volatile__("rd %%tick, %0\n\t" - "add %0, %2, %0\n\t" + "add %0, %1, %0\n\t" "wrpr %0, 0, %%tick\n\t" - "andn %0, %4, %1\n\t" - "ba,pt %%xcc, 1f\n\t" - " add %1, %3, %1\n\t" - ".align 64\n" - "1:\n\t" - "wr %1, 0, %%tick_cmpr\n\t" - "rd %%tick_cmpr, %%g0" - : "=&r" (new_tick), "=&r" (tmp) - : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT)); + : "=&r" (new_tick) + : "r" (adj)); return new_tick; } static struct sparc64_tick_ops tick_operations __read_mostly = { + .name = "tick", .init_tick = tick_init_tick, + .disable_irq = tick_disable_irq, .get_tick = tick_get_tick, - .get_compare = tick_get_compare, .add_tick = tick_add_tick, .add_compare = tick_add_compare, .softint_mask = 1UL << 0, @@ -190,7 +186,15 @@ static struct sparc64_tick_ops tick_oper struct sparc64_tick_ops *tick_ops __read_mostly = &tick_operations; -static void stick_init_tick(unsigned long offset) +static void stick_disable_irq(void) +{ + __asm__ __volatile__( + "wr %0, 0x0, %%asr25" + : /* no outputs */ + : "r" (TICKCMP_IRQ_BIT)); +} + +static void stick_init_tick(void) { /* Writes to the %tick and %stick register are not * allowed on sun4v. The Hypervisor controls that @@ -198,6 +202,7 @@ static void stick_init_tick(unsigned lon */ if (tlb_type != hypervisor) { tick_disable_protection(); + tick_disable_irq(); /* Let the user get at STICK too. */ __asm__ __volatile__( @@ -209,14 +214,7 @@ static void stick_init_tick(unsigned lon : "g1", "g2"); } - __asm__ __volatile__( - " rd %%asr24, %%g1\n" - " andn %%g1, %1, %%g1\n" - " add %%g1, %0, %%g1\n" - " wr %%g1, 0x0, %%asr25" - : /* no outputs */ - : "r" (offset), "r" (TICK_PRIV_BIT) - : "g1"); + stick_disable_irq(); } static unsigned long stick_get_tick(void) @@ -229,49 +227,43 @@ static unsigned long stick_get_tick(void return ret & ~TICK_PRIV_BIT; } -static unsigned long stick_get_compare(void) +static unsigned long stick_add_tick(unsigned long adj) { - unsigned long ret; - - __asm__ __volatile__("rd %%asr25, %0" - : "=r" (ret)); - - return ret; -} - -static unsigned long stick_add_tick(unsigned long adj, unsigned long offset) -{ - unsigned long new_tick, tmp; + unsigned long new_tick; __asm__ __volatile__("rd %%asr24, %0\n\t" - "add %0, %2, %0\n\t" + "add %0, %1, %0\n\t" "wr %0, 0, %%asr24\n\t" - "andn %0, %4, %1\n\t" - "add %1, %3, %1\n\t" - "wr %1, 0, %%asr25" - : "=&r" (new_tick), "=&r" (tmp) - : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT)); + : "=&r" (new_tick) + : "r" (adj)); return new_tick; } -static unsigned long stick_add_compare(unsigned long adj) +static int stick_add_compare(unsigned long adj) { - unsigned long new_compare; + unsigned long orig_tick, new_tick; - __asm__ __volatile__("rd %%asr25, %0\n\t" - "add %0, %1, %0\n\t" - "wr %0, 0, %%asr25" - : "=&r" (new_compare) - : "r" (adj)); + __asm__ __volatile__("rd %%asr24, %0" + : "=r" (orig_tick)); + orig_tick &= ~TICKCMP_IRQ_BIT; + + __asm__ __volatile__("wr %0, 0, %%asr25" + : /* no outputs */ + : "r" (orig_tick + adj)); + + __asm__ __volatile__("rd %%asr24, %0" + : "=r" (new_tick)); + new_tick &= ~TICKCMP_IRQ_BIT; - return new_compare; + return ((long)(new_tick - (orig_tick+adj))) > 0L; } static struct sparc64_tick_ops stick_operations __read_mostly = { + .name = "stick", .init_tick = stick_init_tick, + .disable_irq = stick_disable_irq, .get_tick = stick_get_tick, - .get_compare = stick_get_compare, .add_tick = stick_add_tick, .add_compare = stick_add_compare, .softint_mask = 1UL << 16, @@ -320,20 +312,6 @@ static unsigned long __hbird_read_stick( return ret; } -static unsigned long __hbird_read_compare(void) -{ - unsigned long low, high; - unsigned long addr = HBIRD_STICKCMP_ADDR; - - __asm__ __volatile__("ldxa [%2] %3, %0\n\t" - "add %2, 0x8, %2\n\t" - "ldxa [%2] %3, %1" - : "=&r" (low), "=&r" (high), "=&r" (addr) - : "i" (ASI_PHYS_BYPASS_EC_E), "2" (addr)); - - return (high << 32UL) | low; -} - static void __hbird_write_stick(unsigned long val) { unsigned long low = (val & 0xffffffffUL); @@ -364,10 +342,13 @@ static void __hbird_write_compare(unsign "i" (ASI_PHYS_BYPASS_EC_E)); } -static void hbtick_init_tick(unsigned long offset) +static void hbtick_disable_irq(void) { - unsigned long val; + __hbird_write_compare(TICKCMP_IRQ_BIT); +} +static void hbtick_init_tick(void) +{ tick_disable_protection(); /* XXX This seems to be necessary to 'jumpstart' Hummingbird @@ -377,8 +358,7 @@ static void hbtick_init_tick(unsigned lo */ __hbird_write_stick(__hbird_read_stick()); - val = __hbird_read_stick() & ~TICK_PRIV_BIT; - __hbird_write_compare(val + offset); + hbtick_disable_irq(); } static unsigned long hbtick_get_tick(void) @@ -386,122 +366,95 @@ static unsigned long hbtick_get_tick(voi return __hbird_read_stick() & ~TICK_PRIV_BIT; } -static unsigned long hbtick_get_compare(void) -{ - return __hbird_read_compare(); -} - -static unsigned long hbtick_add_tick(unsigned long adj, unsigned long offset) +static unsigned long hbtick_add_tick(unsigned long adj) { unsigned long val; val = __hbird_read_stick() + adj; __hbird_write_stick(val); - val &= ~TICK_PRIV_BIT; - __hbird_write_compare(val + offset); - return val; } -static unsigned long hbtick_add_compare(unsigned long adj) +static int hbtick_add_compare(unsigned long adj) { - unsigned long val = __hbird_read_compare() + adj; + unsigned long val = __hbird_read_stick(); + unsigned long val2; - val &= ~TICK_PRIV_BIT; + val &= ~TICKCMP_IRQ_BIT; + val += adj; __hbird_write_compare(val); - return val; + val2 = __hbird_read_stick() & ~TICKCMP_IRQ_BIT; + + return ((long)(val2 - val)) > 0L; } static struct sparc64_tick_ops hbtick_operations __read_mostly = { + .name = "hbtick", .init_tick = hbtick_init_tick, + .disable_irq = hbtick_disable_irq, .get_tick = hbtick_get_tick, - .get_compare = hbtick_get_compare, .add_tick = hbtick_add_tick, .add_compare = hbtick_add_compare, .softint_mask = 1UL << 0, }; -/* timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - * - * NOTE: On SUN5 systems the ticker interrupt comes in using 2 - * interrupts, one at level14 and one with softint bit 0. - */ -unsigned long timer_tick_offset __read_mostly; - static unsigned long timer_ticks_per_nsec_quotient __read_mostly; #define TICK_SIZE (tick_nsec / 1000) -static inline void timer_check_rtc(void) -{ - /* last time the cmos clock got updated */ - static long last_rtc_update; - - /* Determine when to update the Mostek clock. */ - if (ntp_synced() && - xtime.tv_sec > last_rtc_update + 660 && - (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && - (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; - /* do it again in 60 s */ - } -} +#define USEC_AFTER 500000 +#define USEC_BEFORE 500000 -irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - unsigned long ticks, compare, pstate; +static void sync_cmos_clock(unsigned long dummy); - write_seqlock(&xtime_lock); +static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0); - do { -#ifndef CONFIG_SMP - profile_tick(CPU_PROFILING); - update_process_times(user_mode(get_irq_regs())); -#endif - do_timer(1); +static void sync_cmos_clock(unsigned long dummy) +{ + struct timeval now, next; + int fail = 1; - /* Guarantee that the following sequences execute - * uninterrupted. + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + * This code is run on a timer. If the clock is set, that timer + * may not expire at the correct time. Thus, we adjust... + */ + if (!ntp_synced()) + /* + * Not synced, exit, do not restart a timer (if one is + * running, let it run out). */ - __asm__ __volatile__("rdpr %%pstate, %0\n\t" - "wrpr %0, %1, %%pstate" - : "=r" (pstate) - : "i" (PSTATE_IE)); + return; - compare = tick_ops->add_compare(timer_tick_offset); - ticks = tick_ops->get_tick(); + do_gettimeofday(&now); + if (now.tv_usec >= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 && + now.tv_usec <= USEC_BEFORE + ((unsigned) TICK_SIZE) / 2) + fail = set_rtc_mmss(now.tv_sec); - /* Restore PSTATE_IE. */ - __asm__ __volatile__("wrpr %0, 0x0, %%pstate" - : /* no outputs */ - : "r" (pstate)); - } while (time_after_eq(ticks, compare)); + next.tv_usec = USEC_AFTER - now.tv_usec; + if (next.tv_usec <= 0) + next.tv_usec += USEC_PER_SEC; - timer_check_rtc(); + if (!fail) + next.tv_sec = 659; + else + next.tv_sec = 0; - write_sequnlock(&xtime_lock); - - return IRQ_HANDLED; + if (next.tv_usec >= USEC_PER_SEC) { + next.tv_sec++; + next.tv_usec -= USEC_PER_SEC; + } + mod_timer(&sync_cmos_timer, jiffies + timeval_to_jiffies(&next)); } -#ifdef CONFIG_SMP -void timer_tick_interrupt(struct pt_regs *regs) +void notify_arch_cmos_timer(void) { - write_seqlock(&xtime_lock); - - do_timer(1); - - timer_check_rtc(); - - write_sequnlock(&xtime_lock); + mod_timer(&sync_cmos_timer, jiffies + 1); } -#endif /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ static void __init kick_start_clock(void) @@ -751,7 +704,7 @@ retry: return -EOPNOTSUPP; } -static int __init clock_model_matches(char *model) +static int __init clock_model_matches(const char *model) { if (strcmp(model, "mk48t02") && strcmp(model, "mk48t08") && @@ -768,7 +721,7 @@ static int __init clock_model_matches(ch static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dp = op->node; - char *model = of_get_property(dp, "model", NULL); + const char *model = of_get_property(dp, "model", NULL); unsigned long size, flags; void __iomem *regs; @@ -900,7 +853,6 @@ #endif prop = of_find_property(dp, "stick-frequency", NULL); } clock = *(unsigned int *) prop->value; - timer_tick_offset = clock / HZ; #ifdef CONFIG_SMP smp_tick_init(); @@ -909,26 +861,6 @@ #endif return clock; } -static void sparc64_start_timers(void) -{ - unsigned long pstate; - - /* Guarantee that the following sequences execute - * uninterrupted. - */ - __asm__ __volatile__("rdpr %%pstate, %0\n\t" - "wrpr %0, %1, %%pstate" - : "=r" (pstate) - : "i" (PSTATE_IE)); - - tick_ops->init_tick(timer_tick_offset); - - /* Restore PSTATE_IE. */ - __asm__ __volatile__("wrpr %0, 0x0, %%pstate" - : /* no outputs */ - : "r" (pstate)); -} - struct freq_table { unsigned long clock_tick_ref; unsigned int ref_freq; @@ -975,29 +907,148 @@ static struct notifier_block sparc64_cpu #endif /* CONFIG_CPU_FREQ */ -static struct time_interpolator sparc64_cpu_interpolator = { - .source = TIME_SOURCE_CPU, - .shift = 16, - .mask = 0xffffffffffffffffLL +static int sparc64_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + return tick_ops->add_compare(delta) ? -ETIME : 0; +} + +static void sparc64_timer_setup(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_ONESHOT: + break; + + case CLOCK_EVT_MODE_SHUTDOWN: + tick_ops->disable_irq(); + break; + + case CLOCK_EVT_MODE_PERIODIC: + case CLOCK_EVT_MODE_UNUSED: + WARN_ON(1); + break; + }; +} + +static struct clock_event_device sparc64_clockevent = { + .features = CLOCK_EVT_FEAT_ONESHOT, + .set_mode = sparc64_timer_setup, + .set_next_event = sparc64_next_event, + .rating = 100, + .shift = 30, + .irq = -1, }; +static DEFINE_PER_CPU(struct clock_event_device, sparc64_events); -/* The quotient formula is taken from the IA64 port. */ -#define SPARC64_NSEC_PER_CYC_SHIFT 10UL -void __init time_init(void) +void timer_interrupt(int irq, struct pt_regs *regs) { - unsigned long clock = sparc64_init_timers(); + struct pt_regs *old_regs = set_irq_regs(regs); + unsigned long tick_mask = tick_ops->softint_mask; + int cpu = smp_processor_id(); + struct clock_event_device *evt = &per_cpu(sparc64_events, cpu); + + clear_softint(tick_mask); + + irq_enter(); - sparc64_cpu_interpolator.frequency = clock; - register_time_interpolator(&sparc64_cpu_interpolator); + kstat_this_cpu.irqs[0]++; - /* Now that the interpolator is registered, it is - * safe to start the timer ticking. + if (unlikely(!evt->event_handler)) { + printk(KERN_WARNING + "Spurious SPARC64 timer interrupt on cpu %d\n", cpu); + } else + evt->event_handler(evt); + + irq_exit(); + + set_irq_regs(old_regs); +} + +void __devinit setup_sparc64_timer(void) +{ + struct clock_event_device *sevt; + unsigned long pstate; + + /* Guarantee that the following sequences execute + * uninterrupted. */ - sparc64_start_timers(); + __asm__ __volatile__("rdpr %%pstate, %0\n\t" + "wrpr %0, %1, %%pstate" + : "=r" (pstate) + : "i" (PSTATE_IE)); + + tick_ops->init_tick(); + + /* Restore PSTATE_IE. */ + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : /* no outputs */ + : "r" (pstate)); + + sevt = &__get_cpu_var(sparc64_events); + + memcpy(sevt, &sparc64_clockevent, sizeof(*sevt)); + sevt->cpumask = cpumask_of_cpu(smp_processor_id()); + + clockevents_register_device(sevt); +} + +#define SPARC64_NSEC_PER_CYC_SHIFT 32UL + +static struct clocksource clocksource_tick = { + .rating = 100, + .mask = CLOCKSOURCE_MASK(64), + .shift = 16, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init setup_clockevent_multiplier(unsigned long hz) +{ + unsigned long mult, shift = 32; + + while (1) { + mult = div_sc(hz, NSEC_PER_SEC, shift); + if (mult && (mult >> 32UL) == 0UL) + break; + + shift--; + } + + sparc64_clockevent.shift = shift; + sparc64_clockevent.mult = mult; +} + +void __init time_init(void) +{ + unsigned long clock = sparc64_init_timers(); timer_ticks_per_nsec_quotient = - (((NSEC_PER_SEC << SPARC64_NSEC_PER_CYC_SHIFT) + - (clock / 2)) / clock); + clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT); + + clocksource_tick.name = tick_ops->name; + clocksource_tick.mult = + clocksource_hz2mult(clock, + clocksource_tick.shift); + clocksource_tick.read = tick_ops->get_tick; + + printk("clocksource: mult[%x] shift[%d]\n", + clocksource_tick.mult, clocksource_tick.shift); + + clocksource_register(&clocksource_tick); + + sparc64_clockevent.name = tick_ops->name; + + setup_clockevent_multiplier(clock); + + sparc64_clockevent.max_delta_ns = + clockevent_delta2ns(0x7fffffffffffffff, &sparc64_clockevent); + sparc64_clockevent.min_delta_ns = + clockevent_delta2ns(0xF, &sparc64_clockevent); + + printk("clockevent: mult[%lx] shift[%d]\n", + sparc64_clockevent.mult, sparc64_clockevent.shift); + + setup_sparc64_timer(); #ifdef CONFIG_CPU_FREQ cpufreq_register_notifier(&sparc64_cpufreq_notifier_block, @@ -1126,10 +1177,6 @@ #endif #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ static unsigned char mini_rtc_status; /* bitmapped status byte. */ -/* months start at 0 now */ -static unsigned char days_in_mo[] = -{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - #define FEBRUARY 2 #define STARTOFTIME 1970 #define SECDAY 86400L @@ -1278,8 +1325,7 @@ static int mini_rtc_ioctl(struct inode * case RTC_SET_TIME: /* Set the RTC */ { - int year; - unsigned char leap_yr; + int year, days; if (!capable(CAP_SYS_TIME)) return -EACCES; @@ -1288,14 +1334,14 @@ static int mini_rtc_ioctl(struct inode * return -EFAULT; year = wtime.tm_year + 1900; - leap_yr = ((!(year % 4) && (year % 100)) || - !(year % 400)); + days = month_days[wtime.tm_mon] + + ((wtime.tm_mon == 1) && leapyear(year)); - if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1)) + if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || + (wtime.tm_mday < 1)) return -EINVAL; - if (wtime.tm_mday < 0 || wtime.tm_mday > - (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr))) + if (wtime.tm_mday < 0 || wtime.tm_mday > days) return -EINVAL; if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 || diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index d7d2a8b..7575aa3 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -60,11 +60,7 @@ #endif tl0_irq5: TRAP_IRQ(handler_irq, 5) tl0_irq6: BTRAP(0x46) BTRAP(0x47) BTRAP(0x48) BTRAP(0x49) tl0_irq10: BTRAP(0x4a) BTRAP(0x4b) BTRAP(0x4c) BTRAP(0x4d) -#ifndef CONFIG_SMP -tl0_irq14: TRAP_IRQ(timer_irq, 14) -#else -tl0_irq14: TICK_SMP_IRQ -#endif +tl0_irq14: TRAP_IRQ(timer_interrupt, 14) tl0_irq15: TRAP_IRQ(handler_irq, 15) tl0_resv050: BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55) tl0_resv056: BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b) diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index f146071..cafadcb 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -122,24 +122,19 @@ static void __init read_obp_memory(const size = 0UL; base = new_base; } - regs[i].phys_addr = base; - regs[i].reg_size = size; - } - - for (i = 0; i < ents; i++) { - if (regs[i].reg_size == 0UL) { - int j; - - for (j = i; j < ents - 1; j++) { - regs[j].phys_addr = - regs[j+1].phys_addr; - regs[j].reg_size = - regs[j+1].reg_size; - } - - ents--; + if (size == 0UL) { + /* If it is empty, simply get rid of it. + * This simplifies the logic of the other + * functions that process these arrays. + */ + memmove(®s[i], ®s[i + 1], + (ents - i - 1) * sizeof(regs[0])); i--; + ents--; + continue; } + regs[i].phys_addr = base; + regs[i].reg_size = size; } *num_ents = ents; @@ -154,15 +149,6 @@ unsigned long *sparc64_valid_addr_bitmap unsigned long kern_base __read_mostly; unsigned long kern_size __read_mostly; -/* get_new_mmu_context() uses "cache + 1". */ -DEFINE_SPINLOCK(ctx_alloc_lock); -unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1; -#define CTX_BMAP_SLOTS (1UL << (CTX_NR_BITS - 6)) -unsigned long mmu_context_bmap[CTX_BMAP_SLOTS]; - -/* References to special section boundaries */ -extern char _start[], _end[]; - /* Initial ramdisk setup */ extern unsigned long sparc_ramdisk_image64; extern unsigned int sparc_ramdisk_image; @@ -406,19 +392,70 @@ void __kprobes flush_icache_range(unsign if (tlb_type == spitfire) { unsigned long kaddr; - for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) - __flush_icache_page(__get_phys(kaddr)); + /* This code only runs on Spitfire cpus so this is + * why we can assume _PAGE_PADDR_4U. + */ + for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) { + unsigned long paddr, mask = _PAGE_PADDR_4U; + + if (kaddr >= PAGE_OFFSET) + paddr = kaddr & mask; + else { + pgd_t *pgdp = pgd_offset_k(kaddr); + pud_t *pudp = pud_offset(pgdp, kaddr); + pmd_t *pmdp = pmd_offset(pudp, kaddr); + pte_t *ptep = pte_offset_kernel(pmdp, kaddr); + + paddr = pte_val(*ptep) & mask; + } + __flush_icache_page(paddr); + } } } void show_mem(void) { - printk("Mem-info:\n"); + unsigned long total = 0, reserved = 0; + unsigned long shared = 0, cached = 0; + pg_data_t *pgdat; + + printk(KERN_INFO "Mem-info:\n"); show_free_areas(); - printk("Free swap: %6ldkB\n", + printk(KERN_INFO "Free swap: %6ldkB\n", nr_swap_pages << (PAGE_SHIFT-10)); - printk("%ld pages of RAM\n", num_physpages); - printk("%lu free pages\n", nr_free_pages()); + for_each_online_pgdat(pgdat) { + unsigned long i, flags; + + pgdat_resize_lock(pgdat, &flags); + for (i = 0; i < pgdat->node_spanned_pages; i++) { + struct page *page = pgdat_page_nr(pgdat, i); + total++; + if (PageReserved(page)) + reserved++; + else if (PageSwapCache(page)) + cached++; + else if (page_count(page)) + shared += page_count(page) - 1; + } + pgdat_resize_unlock(pgdat, &flags); + } + + printk(KERN_INFO "%lu pages of RAM\n", total); + printk(KERN_INFO "%lu reserved pages\n", reserved); + printk(KERN_INFO "%lu pages shared\n", shared); + printk(KERN_INFO "%lu pages swap cached\n", cached); + + printk(KERN_INFO "%lu pages dirty\n", + global_page_state(NR_FILE_DIRTY)); + printk(KERN_INFO "%lu pages writeback\n", + global_page_state(NR_WRITEBACK)); + printk(KERN_INFO "%lu pages mapped\n", + global_page_state(NR_FILE_MAPPED)); + printk(KERN_INFO "%lu pages slab\n", + global_page_state(NR_SLAB_RECLAIMABLE) + + global_page_state(NR_SLAB_UNRECLAIMABLE)); + printk(KERN_INFO "%lu pages pagetables\n", + global_page_state(NR_PAGETABLE)); } void mmu_info(struct seq_file *m) @@ -658,6 +695,13 @@ void __flush_dcache_range(unsigned long } #endif /* DCACHE_ALIASING_POSSIBLE */ +/* get_new_mmu_context() uses "cache + 1". */ +DEFINE_SPINLOCK(ctx_alloc_lock); +unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1; +#define MAX_CTX_NR (1UL << CTX_NR_BITS) +#define CTX_BMAP_SLOTS BITS_TO_LONGS(MAX_CTX_NR) +DECLARE_BITMAP(mmu_context_bmap, MAX_CTX_NR); + /* Caller does TLB context flushing on local CPU if necessary. * The caller also ensures that CTX_VALID(mm->context) is false. * @@ -717,95 +761,6 @@ out: smp_new_mmu_context_version(); } -void sparc_ultra_dump_itlb(void) -{ - int slot; - - if (tlb_type == spitfire) { - printk ("Contents of itlb: "); - for (slot = 0; slot < 14; slot++) printk (" "); - printk ("%2x:%016lx,%016lx\n", - 0, - spitfire_get_itlb_tag(0), spitfire_get_itlb_data(0)); - for (slot = 1; slot < 64; slot+=3) { - printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx %2x:%016lx,%016lx\n", - slot, - spitfire_get_itlb_tag(slot), spitfire_get_itlb_data(slot), - slot+1, - spitfire_get_itlb_tag(slot+1), spitfire_get_itlb_data(slot+1), - slot+2, - spitfire_get_itlb_tag(slot+2), spitfire_get_itlb_data(slot+2)); - } - } else if (tlb_type == cheetah || tlb_type == cheetah_plus) { - printk ("Contents of itlb0:\n"); - for (slot = 0; slot < 16; slot+=2) { - printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n", - slot, - cheetah_get_litlb_tag(slot), cheetah_get_litlb_data(slot), - slot+1, - cheetah_get_litlb_tag(slot+1), cheetah_get_litlb_data(slot+1)); - } - printk ("Contents of itlb2:\n"); - for (slot = 0; slot < 128; slot+=2) { - printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n", - slot, - cheetah_get_itlb_tag(slot), cheetah_get_itlb_data(slot), - slot+1, - cheetah_get_itlb_tag(slot+1), cheetah_get_itlb_data(slot+1)); - } - } -} - -void sparc_ultra_dump_dtlb(void) -{ - int slot; - - if (tlb_type == spitfire) { - printk ("Contents of dtlb: "); - for (slot = 0; slot < 14; slot++) printk (" "); - printk ("%2x:%016lx,%016lx\n", 0, - spitfire_get_dtlb_tag(0), spitfire_get_dtlb_data(0)); - for (slot = 1; slot < 64; slot+=3) { - printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx %2x:%016lx,%016lx\n", - slot, - spitfire_get_dtlb_tag(slot), spitfire_get_dtlb_data(slot), - slot+1, - spitfire_get_dtlb_tag(slot+1), spitfire_get_dtlb_data(slot+1), - slot+2, - spitfire_get_dtlb_tag(slot+2), spitfire_get_dtlb_data(slot+2)); - } - } else if (tlb_type == cheetah || tlb_type == cheetah_plus) { - printk ("Contents of dtlb0:\n"); - for (slot = 0; slot < 16; slot+=2) { - printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n", - slot, - cheetah_get_ldtlb_tag(slot), cheetah_get_ldtlb_data(slot), - slot+1, - cheetah_get_ldtlb_tag(slot+1), cheetah_get_ldtlb_data(slot+1)); - } - printk ("Contents of dtlb2:\n"); - for (slot = 0; slot < 512; slot+=2) { - printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n", - slot, - cheetah_get_dtlb_tag(slot, 2), cheetah_get_dtlb_data(slot, 2), - slot+1, - cheetah_get_dtlb_tag(slot+1, 2), cheetah_get_dtlb_data(slot+1, 2)); - } - if (tlb_type == cheetah_plus) { - printk ("Contents of dtlb3:\n"); - for (slot = 0; slot < 512; slot+=2) { - printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n", - slot, - cheetah_get_dtlb_tag(slot, 3), cheetah_get_dtlb_data(slot, 3), - slot+1, - cheetah_get_dtlb_tag(slot+1, 3), cheetah_get_dtlb_data(slot+1, 3)); - } - } - } -} - -extern unsigned long cmdline_memory_size; - /* Find a free area for the bootmem map, avoiding the kernel image * and the initial ramdisk. */ @@ -815,8 +770,8 @@ static unsigned long __init choose_bootm unsigned long avoid_start, avoid_end, bootmap_size; int i; - bootmap_size = ((end_pfn - start_pfn) + 7) / 8; - bootmap_size = ALIGN(bootmap_size, sizeof(long)); + bootmap_size = bootmem_bootmap_pages(end_pfn - start_pfn); + bootmap_size <<= PAGE_SHIFT; avoid_start = avoid_end = 0; #ifdef CONFIG_BLK_DEV_INITRD @@ -983,6 +938,20 @@ #endif } } +/* About pages_avail, this is the value we will use to calculate + * the zholes_size[] argument given to free_area_init_node(). The + * page allocator uses this to calculate nr_kernel_pages, + * nr_all_pages and zone->present_pages. On NUMA it is used + * to calculate zone->min_unmapped_pages and zone->min_slab_pages. + * + * So this number should really be set to what the page allocator + * actually ends up with. This means: + * 1) It should include bootmem map pages, we'll release those. + * 2) It should not include the kernel image, except for the + * __init sections which we will also release. + * 3) It should include the initrd image, since we'll release + * that too. + */ static unsigned long __init bootmem_init(unsigned long *pages_avail, unsigned long phys_base) { @@ -1069,7 +1038,6 @@ #ifdef CONFIG_DEBUG_BOOTMEM initrd_start, initrd_end); #endif reserve_bootmem(initrd_start, size); - *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; initrd_start += PAGE_OFFSET; initrd_end += PAGE_OFFSET; @@ -1082,6 +1050,11 @@ #endif reserve_bootmem(kern_base, kern_size); *pages_avail -= PAGE_ALIGN(kern_size) >> PAGE_SHIFT; + /* Add back in the initmem pages. */ + size = ((unsigned long)(__init_end) & PAGE_MASK) - + PAGE_ALIGN((unsigned long)__init_begin); + *pages_avail += size >> PAGE_SHIFT; + /* Reserve the bootmem map. We do not account for it * in pages_avail because we will release that memory * in free_all_bootmem. @@ -1092,7 +1065,6 @@ #ifdef CONFIG_DEBUG_BOOTMEM (bootmap_pfn << PAGE_SHIFT), size); #endif reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size); - *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; for (i = 0; i < pavail_ents; i++) { unsigned long start_pfn, end_pfn; @@ -1584,6 +1556,10 @@ void __init mem_init(void) #ifdef CONFIG_DEBUG_BOOTMEM prom_printf("mem_init: Calling free_all_bootmem().\n"); #endif + + /* We subtract one to account for the mem_map_zero page + * allocated below. + */ totalram_pages = num_physpages = free_all_bootmem() - 1; /* @@ -1883,62 +1859,6 @@ static unsigned long kern_large_tte(unsi return val | paddr; } -/* - * Translate PROM's mapping we capture at boot time into physical address. - * The second parameter is only set from prom_callback() invocations. - */ -unsigned long prom_virt_to_phys(unsigned long promva, int *error) -{ - unsigned long mask; - int i; - - mask = _PAGE_PADDR_4U; - if (tlb_type == hypervisor) - mask = _PAGE_PADDR_4V; - - for (i = 0; i < prom_trans_ents; i++) { - struct linux_prom_translation *p = &prom_trans[i]; - - if (promva >= p->virt && - promva < (p->virt + p->size)) { - unsigned long base = p->data & mask; - - if (error) - *error = 0; - return base + (promva & (8192 - 1)); - } - } - if (error) - *error = 1; - return 0UL; -} - -/* XXX We should kill off this ugly thing at so me point. XXX */ -unsigned long sun4u_get_pte(unsigned long addr) -{ - pgd_t *pgdp; - pud_t *pudp; - pmd_t *pmdp; - pte_t *ptep; - unsigned long mask = _PAGE_PADDR_4U; - - if (tlb_type == hypervisor) - mask = _PAGE_PADDR_4V; - - if (addr >= PAGE_OFFSET) - return addr & mask; - - if ((addr >= LOW_OBP_ADDRESS) && (addr < HI_OBP_ADDRESS)) - return prom_virt_to_phys(addr, NULL); - - pgdp = pgd_offset_k(addr); - pudp = pud_offset(pgdp, addr); - pmdp = pmd_offset(pudp, addr); - ptep = pte_offset_kernel(pmdp, addr); - - return pte_val(*ptep) & mask; -} - /* If not locked, zap it. */ void __flush_tlb_all(void) { diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index 9fcaad6..542c808 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -224,7 +224,8 @@ static char *serial(char *buffer, int sz *buffer = 0; if (dp) { - char *val = of_get_property(dp, "system-board-serial#", &len); + const char *val = + of_get_property(dp, "system-board-serial#", &len); if (val && len > 0) { if (len > sz) diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c index 9c2e7a7..adeece1 100644 --- a/arch/um/drivers/daemon_kern.c +++ b/arch/um/drivers/daemon_kern.c @@ -46,7 +46,7 @@ static int daemon_read(int fd, struct sk { *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); if(*skb == NULL) return(-ENOMEM); - return(net_recvfrom(fd, (*skb)->mac.raw, + return(net_recvfrom(fd, skb_mac_header(*skb), (*skb)->dev->mtu + ETH_HEADER_OTHER)); } diff --git a/arch/um/drivers/mcast_kern.c b/arch/um/drivers/mcast_kern.c index 52ccb7b..e6b8e0d 100644 --- a/arch/um/drivers/mcast_kern.c +++ b/arch/um/drivers/mcast_kern.c @@ -50,7 +50,7 @@ static int mcast_read(int fd, struct sk_ { *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); if(*skb == NULL) return(-ENOMEM); - return(net_recvfrom(fd, (*skb)->mac.raw, + return(net_recvfrom(fd, skb_mac_header(*skb), (*skb)->dev->mtu + ETH_HEADER_OTHER)); } diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index 04e31f8..8593037 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -55,7 +55,7 @@ static int uml_net_rx(struct net_device skb->dev = dev; skb_put(skb, dev->mtu); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); pkt_len = (*lp->read)(lp->fd, &skb, lp); if (pkt_len > 0) { diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c index e67362a..9488493 100644 --- a/arch/um/drivers/pcap_kern.c +++ b/arch/um/drivers/pcap_kern.c @@ -36,7 +36,7 @@ static int pcap_read(int fd, struct sk_b { *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); if(*skb == NULL) return(-ENOMEM); - return(pcap_user_read(fd, (*skb)->mac.raw, + return(pcap_user_read(fd, skb_mac_header(*skb), (*skb)->dev->mtu + ETH_HEADER_OTHER, (struct pcap_data *) &lp->user)); } diff --git a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c index 25634bd..125c44f 100644 --- a/arch/um/drivers/slip_kern.c +++ b/arch/um/drivers/slip_kern.c @@ -49,7 +49,7 @@ static unsigned short slip_protocol(stru static int slip_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) { - return(slip_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, + return(slip_user_read(fd, skb_mac_header(*skb), (*skb)->dev->mtu, (struct slip_data *) &lp->user)); } diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c index b3ed8fb..0a0324a 100644 --- a/arch/um/drivers/slirp_kern.c +++ b/arch/um/drivers/slirp_kern.c @@ -53,7 +53,7 @@ static unsigned short slirp_protocol(str static int slirp_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) { - return(slirp_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, + return(slirp_user_read(fd, skb_mac_header(*skb), (*skb)->dev->mtu, (struct slirp_data *) &lp->user)); } diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c index 7054182..1268914 100644 --- a/arch/um/os-Linux/drivers/ethertap_kern.c +++ b/arch/um/os-Linux/drivers/ethertap_kern.c @@ -43,7 +43,7 @@ static int etap_read(int fd, struct sk_b *skb = ether_adjust_skb(*skb, ETH_HEADER_ETHERTAP); if(*skb == NULL) return(-ENOMEM); - len = net_recvfrom(fd, (*skb)->mac.raw, + len = net_recvfrom(fd, skb_mac_header(*skb), (*skb)->dev->mtu + 2 * ETH_HEADER_ETHERTAP); if(len <= 0) return(len); skb_pull(*skb, 2); diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c index 76570a2..f1714e7 100644 --- a/arch/um/os-Linux/drivers/tuntap_kern.c +++ b/arch/um/os-Linux/drivers/tuntap_kern.c @@ -43,7 +43,7 @@ static int tuntap_read(int fd, struct sk { *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); if(*skb == NULL) return(-ENOMEM); - return(net_read(fd, (*skb)->mac.raw, + return(net_read(fd, skb_mac_header(*skb), (*skb)->dev->mtu + ETH_HEADER_OTHER)); } diff --git a/arch/x86_64/defconfig b/arch/x86_64/defconfig index 7a1e251..b263788 100644 --- a/arch/x86_64/defconfig +++ b/arch/x86_64/defconfig @@ -631,7 +631,6 @@ # CONFIG_SATA_ULI is not set CONFIG_SATA_VIA=y # CONFIG_SATA_VITESSE is not set # CONFIG_SATA_INIC162X is not set -CONFIG_SATA_INTEL_COMBINED=y CONFIG_SATA_ACPI=y # CONFIG_PATA_ALI is not set # CONFIG_PATA_AMD is not set diff --git a/arch/xtensa/lib/Makefile b/arch/xtensa/lib/Makefile index ed935b5..6c4fdd8 100644 --- a/arch/xtensa/lib/Makefile +++ b/arch/xtensa/lib/Makefile @@ -2,6 +2,6 @@ # # Makefile for Xtensa-specific library files. # -lib-y += memcopy.o memset.o checksum.o strcasecmp.o \ +lib-y += memcopy.o memset.o checksum.o \ usercopy.o strncpy_user.o strnlen_user.o lib-$(CONFIG_PCI) += pci-auto.o diff --git a/arch/xtensa/lib/strcasecmp.c b/arch/xtensa/lib/strcasecmp.c deleted file mode 100644 index 165b2d6..0000000 --- a/arch/xtensa/lib/strcasecmp.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * linux/arch/xtensa/lib/strcasecmp.c - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file "COPYING" in the main directory of - * this archive for more details. - * - * Copyright (C) 2002 Tensilica Inc. - */ - -#include - - -/* We handle nothing here except the C locale. Since this is used in - only one place, on strings known to contain only 7 bit ASCII, this - is ok. */ - -int strcasecmp(const char *a, const char *b) -{ - int ca, cb; - - do { - ca = *a++ & 0xff; - cb = *b++ & 0xff; - if (ca >= 'A' && ca <= 'Z') - ca += 'a' - 'A'; - if (cb >= 'A' && cb <= 'Z') - cb += 'a' - 'A'; - } while (ca == cb && ca != '\0'); - - return ca - cb; -} diff --git a/arch/xtensa/platform-iss/network.c b/arch/xtensa/platform-iss/network.c index 8ebfc87..ab05bff 100644 --- a/arch/xtensa/platform-iss/network.c +++ b/arch/xtensa/platform-iss/network.c @@ -386,7 +386,7 @@ static int iss_net_rx(struct net_device /* Setup skb */ skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); pkt_len = lp->tp.read(lp, &skb); skb_put(skb, pkt_len); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index f92ba2a..64df3fa 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -26,19 +25,17 @@ static int cfq_slice_async = HZ / 25; static const int cfq_slice_async_rq = 2; static int cfq_slice_idle = HZ / 125; +/* + * grace period before allowing idle class to get disk access + */ #define CFQ_IDLE_GRACE (HZ / 10) -#define CFQ_SLICE_SCALE (5) - -#define CFQ_KEY_ASYNC (0) /* - * for the hash of cfqq inside the cfqd + * below this threshold, we consider thinktime immediate */ -#define CFQ_QHASH_SHIFT 6 -#define CFQ_QHASH_ENTRIES (1 << CFQ_QHASH_SHIFT) -#define list_entry_qhash(entry) hlist_entry((entry), struct cfq_queue, cfq_hash) +#define CFQ_MIN_TT (2) -#define list_entry_cfqq(ptr) list_entry((ptr), struct cfq_queue, cfq_list) +#define CFQ_SLICE_SCALE (5) #define RQ_CIC(rq) ((struct cfq_io_context*)(rq)->elevator_private) #define RQ_CFQQ(rq) ((rq)->elevator_private2) @@ -56,17 +53,21 @@ #define cfq_class_rt(cfqq) ((cfqq)->iopr #define ASYNC (0) #define SYNC (1) -#define cfq_cfqq_dispatched(cfqq) \ - ((cfqq)->on_dispatch[ASYNC] + (cfqq)->on_dispatch[SYNC]) - -#define cfq_cfqq_class_sync(cfqq) ((cfqq)->key != CFQ_KEY_ASYNC) - -#define cfq_cfqq_sync(cfqq) \ - (cfq_cfqq_class_sync(cfqq) || (cfqq)->on_dispatch[SYNC]) - #define sample_valid(samples) ((samples) > 80) /* + * Most of our rbtree usage is for sorting with min extraction, so + * if we cache the leftmost node we don't have to walk down the tree + * to find it. Idea borrowed from Ingo Molnars CFS scheduler. We should + * move this into the elevator for the rq sorting as well. + */ +struct cfq_rb_root { + struct rb_root rb; + struct rb_node *left; +}; +#define CFQ_RB_ROOT (struct cfq_rb_root) { RB_ROOT, NULL, } + +/* * Per block device queue structure */ struct cfq_data { @@ -75,18 +76,11 @@ struct cfq_data { /* * rr list of queues with requests and the count of them */ - struct list_head rr_list[CFQ_PRIO_LISTS]; - struct list_head busy_rr; - struct list_head cur_rr; - struct list_head idle_rr; + struct cfq_rb_root service_tree; unsigned int busy_queues; - /* - * cfqq lookup hash - */ - struct hlist_head *cfq_hash; - int rq_in_driver; + int sync_flight; int hw_tag; /* @@ -97,12 +91,10 @@ struct cfq_data { struct cfq_queue *active_queue; struct cfq_io_context *active_cic; - int cur_prio, cur_end_prio; - unsigned int dispatch_slice; struct timer_list idle_class_timer; - sector_t last_sector; + sector_t last_position; unsigned long last_end_request; /* @@ -117,6 +109,9 @@ struct cfq_data { unsigned int cfq_slice_idle; struct list_head cic_list; + + sector_t new_seek_mean; + u64 new_seek_total; }; /* @@ -127,12 +122,10 @@ struct cfq_queue { atomic_t ref; /* parent cfq_data */ struct cfq_data *cfqd; - /* cfqq lookup hash */ - struct hlist_node cfq_hash; - /* hash key */ - unsigned int key; - /* member of the rr/busy/cur/idle cfqd list */ - struct list_head cfq_list; + /* service_tree member */ + struct rb_node rb_node; + /* service_tree key */ + unsigned long rb_key; /* sorted list of pending requests */ struct rb_root sort_list; /* if fifo isn't expired, next request to serve */ @@ -147,11 +140,10 @@ struct cfq_queue { struct list_head fifo; unsigned long slice_end; - unsigned long service_last; long slice_resid; - /* number of requests that are on the dispatch list */ - int on_dispatch[2]; + /* number of requests that are on the dispatch list or inside driver */ + int dispatched; /* io prio of this group */ unsigned short ioprio, org_ioprio; @@ -159,6 +151,8 @@ struct cfq_queue { /* various state flags, see below */ unsigned int flags; + + sector_t last_request_pos; }; enum cfqq_state_flags { @@ -172,6 +166,7 @@ enum cfqq_state_flags { CFQ_CFQQ_FLAG_prio_changed, /* task priority has changed */ CFQ_CFQQ_FLAG_queue_new, /* queue never been serviced */ CFQ_CFQQ_FLAG_slice_new, /* no requests dispatched in slice */ + CFQ_CFQQ_FLAG_sync, /* synchronous queue */ }; #define CFQ_CFQQ_FNS(name) \ @@ -198,11 +193,38 @@ CFQ_CFQQ_FNS(idle_window); CFQ_CFQQ_FNS(prio_changed); CFQ_CFQQ_FNS(queue_new); CFQ_CFQQ_FNS(slice_new); +CFQ_CFQQ_FNS(sync); #undef CFQ_CFQQ_FNS -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short); static void cfq_dispatch_insert(request_queue_t *, struct request *); -static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, unsigned int key, struct task_struct *tsk, gfp_t gfp_mask); +static struct cfq_queue *cfq_get_queue(struct cfq_data *, int, + struct task_struct *, gfp_t); +static struct cfq_io_context *cfq_cic_rb_lookup(struct cfq_data *, + struct io_context *); + +static inline struct cfq_queue *cic_to_cfqq(struct cfq_io_context *cic, + int is_sync) +{ + return cic->cfqq[!!is_sync]; +} + +static inline void cic_set_cfqq(struct cfq_io_context *cic, + struct cfq_queue *cfqq, int is_sync) +{ + cic->cfqq[!!is_sync] = cfqq; +} + +/* + * We regard a request as SYNC, if it's either a read or has the SYNC bit + * set (in which case it could also be direct WRITE). + */ +static inline int cfq_bio_sync(struct bio *bio) +{ + if (bio_data_dir(bio) == READ || bio_sync(bio)) + return 1; + + return 0; +} /* * scheduler run of queue, if there are requests pending and no one in the @@ -221,44 +243,31 @@ static int cfq_queue_empty(request_queue return !cfqd->busy_queues; } -static inline pid_t cfq_queue_pid(struct task_struct *task, int rw, int is_sync) -{ - /* - * Use the per-process queue, for read requests and syncronous writes - */ - if (!(rw & REQ_RW) || is_sync) - return task->pid; - - return CFQ_KEY_ASYNC; -} - /* * Scale schedule slice based on io priority. Use the sync time slice only * if a queue is marked sync and has sync io queued. A sync queue with async * io only, should not get full sync slice length. */ -static inline int -cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) +static inline int cfq_prio_slice(struct cfq_data *cfqd, int sync, + unsigned short prio) { - const int base_slice = cfqd->cfq_slice[cfq_cfqq_sync(cfqq)]; + const int base_slice = cfqd->cfq_slice[sync]; - WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR); + WARN_ON(prio >= IOPRIO_BE_NR); + + return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - prio)); +} - return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - cfqq->ioprio)); +static inline int +cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + return cfq_prio_slice(cfqd, cfq_cfqq_sync(cfqq), cfqq->ioprio); } static inline void cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) { cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies; - cfqq->slice_end += cfqq->slice_resid; - - /* - * Don't carry over residual for more than one slice, we only want - * to slightly correct the fairness. Carrying over forever would - * easily introduce oscillations. - */ - cfqq->slice_resid = 0; } /* @@ -307,7 +316,7 @@ #define CFQ_RQ2_WRAP 0x02 /* request 2 w s1 = rq1->sector; s2 = rq2->sector; - last = cfqd->last_sector; + last = cfqd->last_position; /* * by definition, 1KiB is 2 sectors @@ -372,6 +381,26 @@ #define CFQ_RQ2_WRAP 0x02 /* request 2 w } /* + * The below is leftmost cache rbtree addon + */ +static struct rb_node *cfq_rb_first(struct cfq_rb_root *root) +{ + if (!root->left) + root->left = rb_first(&root->rb); + + return root->left; +} + +static void cfq_rb_erase(struct rb_node *n, struct cfq_rb_root *root) +{ + if (root->left == n) + root->left = NULL; + + rb_erase(n, &root->rb); + RB_CLEAR_NODE(n); +} + +/* * would be nice to take fifo expire time into account as well */ static struct request * @@ -398,78 +427,96 @@ cfq_find_next_rq(struct cfq_data *cfqd, return cfq_choose_req(cfqd, next, prev); } -static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) +static unsigned long cfq_slice_offset(struct cfq_data *cfqd, + struct cfq_queue *cfqq) { - struct cfq_data *cfqd = cfqq->cfqd; - struct list_head *list, *n; - struct cfq_queue *__cfqq; - /* - * Resorting requires the cfqq to be on the RR list already. + * just an approximation, should be ok. */ - if (!cfq_cfqq_on_rr(cfqq)) - return; + return (cfqd->busy_queues - 1) * (cfq_prio_slice(cfqd, 1, 0) - + cfq_prio_slice(cfqd, cfq_cfqq_sync(cfqq), cfqq->ioprio)); +} - list_del(&cfqq->cfq_list); +/* + * The cfqd->service_tree holds all pending cfq_queue's that have + * requests waiting to be processed. It is sorted in the order that + * we will service the queues. + */ +static void cfq_service_tree_add(struct cfq_data *cfqd, + struct cfq_queue *cfqq, int add_front) +{ + struct rb_node **p = &cfqd->service_tree.rb.rb_node; + struct rb_node *parent = NULL; + unsigned long rb_key; + int left; + + if (!add_front) { + rb_key = cfq_slice_offset(cfqd, cfqq) + jiffies; + rb_key += cfqq->slice_resid; + cfqq->slice_resid = 0; + } else + rb_key = 0; - if (cfq_class_rt(cfqq)) - list = &cfqd->cur_rr; - else if (cfq_class_idle(cfqq)) - list = &cfqd->idle_rr; - else { + if (!RB_EMPTY_NODE(&cfqq->rb_node)) { /* - * if cfqq has requests in flight, don't allow it to be - * found in cfq_set_active_queue before it has finished them. - * this is done to increase fairness between a process that - * has lots of io pending vs one that only generates one - * sporadically or synchronously + * same position, nothing more to do */ - if (cfq_cfqq_dispatched(cfqq)) - list = &cfqd->busy_rr; - else - list = &cfqd->rr_list[cfqq->ioprio]; + if (rb_key == cfqq->rb_key) + return; + + cfq_rb_erase(&cfqq->rb_node, &cfqd->service_tree); } - if (preempted || cfq_cfqq_queue_new(cfqq)) { - /* - * If this queue was preempted or is new (never been serviced), - * let it be added first for fairness but beind other new - * queues. - */ - n = list; - while (n->next != list) { - __cfqq = list_entry_cfqq(n->next); - if (!cfq_cfqq_queue_new(__cfqq)) - break; + left = 1; + while (*p) { + struct cfq_queue *__cfqq; + struct rb_node **n; + + parent = *p; + __cfqq = rb_entry(parent, struct cfq_queue, rb_node); - n = n->next; - } - list_add_tail(&cfqq->cfq_list, n); - } else if (!cfq_cfqq_class_sync(cfqq)) { - /* - * async queue always goes to the end. this wont be overly - * unfair to writes, as the sort of the sync queue wont be - * allowed to pass the async queue again. - */ - list_add_tail(&cfqq->cfq_list, list); - } else { /* - * sort by last service, but don't cross a new or async - * queue. we don't cross a new queue because it hasn't been - * service before, and we don't cross an async queue because - * it gets added to the end on expire. + * sort RT queues first, we always want to give + * preference to them. IDLE queues goes to the back. + * after that, sort on the next service time. */ - n = list; - while ((n = n->prev) != list) { - struct cfq_queue *__cfqq = list_entry_cfqq(n); + if (cfq_class_rt(cfqq) > cfq_class_rt(__cfqq)) + n = &(*p)->rb_left; + else if (cfq_class_rt(cfqq) < cfq_class_rt(__cfqq)) + n = &(*p)->rb_right; + else if (cfq_class_idle(cfqq) < cfq_class_idle(__cfqq)) + n = &(*p)->rb_left; + else if (cfq_class_idle(cfqq) > cfq_class_idle(__cfqq)) + n = &(*p)->rb_right; + else if (rb_key < __cfqq->rb_key) + n = &(*p)->rb_left; + else + n = &(*p)->rb_right; - if (!cfq_cfqq_class_sync(cfqq) || !__cfqq->service_last) - break; - if (time_before(__cfqq->service_last, cfqq->service_last)) - break; - } - list_add(&cfqq->cfq_list, n); + if (n == &(*p)->rb_right) + left = 0; + + p = n; } + + if (left) + cfqd->service_tree.left = &cfqq->rb_node; + + cfqq->rb_key = rb_key; + rb_link_node(&cfqq->rb_node, parent, p); + rb_insert_color(&cfqq->rb_node, &cfqd->service_tree.rb); +} + +/* + * Update cfqq's position in the service tree. + */ +static void cfq_resort_rr_list(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + /* + * Resorting requires the cfqq to be on the RR list already. + */ + if (cfq_cfqq_on_rr(cfqq)) + cfq_service_tree_add(cfqd, cfqq, 0); } /* @@ -483,15 +530,21 @@ cfq_add_cfqq_rr(struct cfq_data *cfqd, s cfq_mark_cfqq_on_rr(cfqq); cfqd->busy_queues++; - cfq_resort_rr_list(cfqq, 0); + cfq_resort_rr_list(cfqd, cfqq); } +/* + * Called when the cfqq no longer has requests pending, remove it from + * the service tree. + */ static inline void cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) { BUG_ON(!cfq_cfqq_on_rr(cfqq)); cfq_clear_cfqq_on_rr(cfqq); - list_del_init(&cfqq->cfq_list); + + if (!RB_EMPTY_NODE(&cfqq->rb_node)) + cfq_rb_erase(&cfqq->rb_node, &cfqd->service_tree); BUG_ON(!cfqd->busy_queues); cfqd->busy_queues--; @@ -552,10 +605,14 @@ static struct request * cfq_find_rq_fmerge(struct cfq_data *cfqd, struct bio *bio) { struct task_struct *tsk = current; - pid_t key = cfq_queue_pid(tsk, bio_data_dir(bio), bio_sync(bio)); + struct cfq_io_context *cic; struct cfq_queue *cfqq; - cfqq = cfq_find_cfq_hash(cfqd, key, tsk->ioprio); + cic = cfq_cic_rb_lookup(cfqd, tsk->io_context); + if (!cic) + return NULL; + + cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio)); if (cfqq) { sector_t sector = bio->bi_sector + bio_sectors(bio); @@ -579,6 +636,8 @@ static void cfq_activate_request(request */ if (!cfqd->hw_tag && cfqd->rq_in_driver > 4) cfqd->hw_tag = 1; + + cfqd->last_position = rq->hard_sector + rq->hard_nr_sectors; } static void cfq_deactivate_request(request_queue_t *q, struct request *rq) @@ -605,8 +664,7 @@ static void cfq_remove_request(struct re } } -static int -cfq_merge(request_queue_t *q, struct request **req, struct bio *bio) +static int cfq_merge(request_queue_t *q, struct request **req, struct bio *bio) { struct cfq_data *cfqd = q->elevator->elevator_data; struct request *__rq; @@ -648,23 +706,24 @@ static int cfq_allow_merge(request_queue struct bio *bio) { struct cfq_data *cfqd = q->elevator->elevator_data; - const int rw = bio_data_dir(bio); + struct cfq_io_context *cic; struct cfq_queue *cfqq; - pid_t key; /* * Disallow merge of a sync bio into an async request. */ - if ((bio_data_dir(bio) == READ || bio_sync(bio)) && !rq_is_sync(rq)) + if (cfq_bio_sync(bio) && !rq_is_sync(rq)) return 0; /* * Lookup the cfqq that this bio will be queued with. Allow * merge only if rq is queued there. */ - key = cfq_queue_pid(current, rw, bio_sync(bio)); - cfqq = cfq_find_cfq_hash(cfqd, key, current->ioprio); + cic = cfq_cic_rb_lookup(cfqd, current->io_context); + if (!cic) + return 0; + cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio)); if (cfqq == RQ_CFQQ(rq)) return 1; @@ -684,6 +743,7 @@ __cfq_set_active_queue(struct cfq_data * cfq_clear_cfqq_must_alloc_slice(cfqq); cfq_clear_cfqq_fifo_expire(cfqq); cfq_mark_cfqq_slice_new(cfqq); + cfq_clear_cfqq_queue_new(cfqq); } cfqd->active_queue = cfqq; @@ -694,23 +754,21 @@ __cfq_set_active_queue(struct cfq_data * */ static void __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, - int preempted, int timed_out) + int timed_out) { if (cfq_cfqq_wait_request(cfqq)) del_timer(&cfqd->idle_slice_timer); cfq_clear_cfqq_must_dispatch(cfqq); cfq_clear_cfqq_wait_request(cfqq); - cfq_clear_cfqq_queue_new(cfqq); /* - * store what was left of this slice, if the queue idled out - * or was preempted + * store what was left of this slice, if the queue idled/timed out */ if (timed_out && !cfq_cfqq_slice_new(cfqq)) cfqq->slice_resid = cfqq->slice_end - jiffies; - cfq_resort_rr_list(cfqq, preempted); + cfq_resort_rr_list(cfqd, cfqq); if (cfqq == cfqd->active_queue) cfqd->active_queue = NULL; @@ -719,163 +777,152 @@ __cfq_slice_expired(struct cfq_data *cfq put_io_context(cfqd->active_cic->ioc); cfqd->active_cic = NULL; } - - cfqd->dispatch_slice = 0; } -static inline void cfq_slice_expired(struct cfq_data *cfqd, int preempted, - int timed_out) +static inline void cfq_slice_expired(struct cfq_data *cfqd, int timed_out) { struct cfq_queue *cfqq = cfqd->active_queue; if (cfqq) - __cfq_slice_expired(cfqd, cfqq, preempted, timed_out); + __cfq_slice_expired(cfqd, cfqq, timed_out); } /* - * 0 - * 0,1 - * 0,1,2 - * 0,1,2,3 - * 0,1,2,3,4 - * 0,1,2,3,4,5 - * 0,1,2,3,4,5,6 - * 0,1,2,3,4,5,6,7 + * Get next queue for service. Unless we have a queue preemption, + * we'll simply select the first cfqq in the service tree. */ -static int cfq_get_next_prio_level(struct cfq_data *cfqd) +static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd) { - int prio, wrap; + struct cfq_queue *cfqq; + struct rb_node *n; - prio = -1; - wrap = 0; - do { - int p; + if (RB_EMPTY_ROOT(&cfqd->service_tree.rb)) + return NULL; - for (p = cfqd->cur_prio; p <= cfqd->cur_end_prio; p++) { - if (!list_empty(&cfqd->rr_list[p])) { - prio = p; - break; - } - } + n = cfq_rb_first(&cfqd->service_tree); + cfqq = rb_entry(n, struct cfq_queue, rb_node); - if (prio != -1) - break; - cfqd->cur_prio = 0; - if (++cfqd->cur_end_prio == CFQ_PRIO_LISTS) { - cfqd->cur_end_prio = 0; - if (wrap) - break; - wrap = 1; - } - } while (1); + if (cfq_class_idle(cfqq)) { + unsigned long end; - if (unlikely(prio == -1)) - return -1; + /* + * if we have idle queues and no rt or be queues had + * pending requests, either allow immediate service if + * the grace period has passed or arm the idle grace + * timer + */ + end = cfqd->last_end_request + CFQ_IDLE_GRACE; + if (time_before(jiffies, end)) { + mod_timer(&cfqd->idle_class_timer, end); + cfqq = NULL; + } + } - BUG_ON(prio >= CFQ_PRIO_LISTS); + return cfqq; +} - list_splice_init(&cfqd->rr_list[prio], &cfqd->cur_rr); +/* + * Get and set a new active queue for service. + */ +static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd) +{ + struct cfq_queue *cfqq; - cfqd->cur_prio = prio + 1; - if (cfqd->cur_prio > cfqd->cur_end_prio) { - cfqd->cur_end_prio = cfqd->cur_prio; - cfqd->cur_prio = 0; - } - if (cfqd->cur_end_prio == CFQ_PRIO_LISTS) { - cfqd->cur_prio = 0; - cfqd->cur_end_prio = 0; - } + cfqq = cfq_get_next_queue(cfqd); + __cfq_set_active_queue(cfqd, cfqq); + return cfqq; +} - return prio; +static inline sector_t cfq_dist_from_last(struct cfq_data *cfqd, + struct request *rq) +{ + if (rq->sector >= cfqd->last_position) + return rq->sector - cfqd->last_position; + else + return cfqd->last_position - rq->sector; } -static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd) +static inline int cfq_rq_close(struct cfq_data *cfqd, struct request *rq) { - struct cfq_queue *cfqq = NULL; + struct cfq_io_context *cic = cfqd->active_cic; - if (!list_empty(&cfqd->cur_rr) || cfq_get_next_prio_level(cfqd) != -1) { - /* - * if current list is non-empty, grab first entry. if it is - * empty, get next prio level and grab first entry then if any - * are spliced - */ - cfqq = list_entry_cfqq(cfqd->cur_rr.next); - } else if (!list_empty(&cfqd->busy_rr)) { - /* - * If no new queues are available, check if the busy list has - * some before falling back to idle io. - */ - cfqq = list_entry_cfqq(cfqd->busy_rr.next); - } else if (!list_empty(&cfqd->idle_rr)) { - /* - * if we have idle queues and no rt or be queues had pending - * requests, either allow immediate service if the grace period - * has passed or arm the idle grace timer - */ - unsigned long end = cfqd->last_end_request + CFQ_IDLE_GRACE; + if (!sample_valid(cic->seek_samples)) + return 0; - if (time_after_eq(jiffies, end)) - cfqq = list_entry_cfqq(cfqd->idle_rr.next); - else - mod_timer(&cfqd->idle_class_timer, end); - } + return cfq_dist_from_last(cfqd, rq) <= cic->seek_mean; +} - __cfq_set_active_queue(cfqd, cfqq); - return cfqq; +static int cfq_close_cooperator(struct cfq_data *cfq_data, + struct cfq_queue *cfqq) +{ + /* + * We should notice if some of the queues are cooperating, eg + * working closely on the same area of the disk. In that case, + * we can group them together and don't waste time idling. + */ + return 0; } -#define CIC_SEEKY(cic) ((cic)->seek_mean > (128 * 1024)) +#define CIC_SEEKY(cic) ((cic)->seek_mean > (8 * 1024)) -static int cfq_arm_slice_timer(struct cfq_data *cfqd) +static void cfq_arm_slice_timer(struct cfq_data *cfqd) { struct cfq_queue *cfqq = cfqd->active_queue; struct cfq_io_context *cic; unsigned long sl; WARN_ON(!RB_EMPTY_ROOT(&cfqq->sort_list)); + WARN_ON(cfq_cfqq_slice_new(cfqq)); /* * idle is disabled, either manually or by past process history */ - if (!cfqd->cfq_slice_idle) - return 0; - if (!cfq_cfqq_idle_window(cfqq)) - return 0; + if (!cfqd->cfq_slice_idle || !cfq_cfqq_idle_window(cfqq)) + return; + /* * task has exited, don't wait */ cic = cfqd->active_cic; if (!cic || !cic->ioc->task) - return 0; + return; + + /* + * See if this prio level has a good candidate + */ + if (cfq_close_cooperator(cfqd, cfqq) && + (sample_valid(cic->ttime_samples) && cic->ttime_mean > 2)) + return; cfq_mark_cfqq_must_dispatch(cfqq); cfq_mark_cfqq_wait_request(cfqq); - sl = min(cfqq->slice_end - 1, (unsigned long) cfqd->cfq_slice_idle); - /* * we don't want to idle for seeks, but we do want to allow * fair distribution of slice time for a process doing back-to-back * seeks. so allow a little bit of time for him to submit a new rq */ + sl = cfqd->cfq_slice_idle; if (sample_valid(cic->seek_samples) && CIC_SEEKY(cic)) - sl = min(sl, msecs_to_jiffies(2)); + sl = min(sl, msecs_to_jiffies(CFQ_MIN_TT)); mod_timer(&cfqd->idle_slice_timer, jiffies + sl); - return 1; } +/* + * Move request from internal lists to the request queue dispatch list. + */ static void cfq_dispatch_insert(request_queue_t *q, struct request *rq) { struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq = RQ_CFQQ(rq); cfq_remove_request(rq); - cfqq->on_dispatch[rq_is_sync(rq)]++; + cfqq->dispatched++; elv_dispatch_sort(q, rq); - rq = list_entry(q->queue_head.prev, struct request, queuelist); - cfqd->last_sector = rq->sector + rq->nr_sectors; + if (cfq_cfqq_sync(cfqq)) + cfqd->sync_flight++; } /* @@ -895,13 +942,13 @@ static inline struct request *cfq_check_ if (list_empty(&cfqq->fifo)) return NULL; - fifo = cfq_cfqq_class_sync(cfqq); + fifo = cfq_cfqq_sync(cfqq); rq = rq_entry_fifo(cfqq->fifo.next); - if (time_after(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo])) - return rq; + if (time_before(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo])) + return NULL; - return NULL; + return rq; } static inline int @@ -915,7 +962,8 @@ cfq_prio_to_maxrq(struct cfq_data *cfqd, } /* - * get next queue for service + * Select a queue for service. If we have a current active queue, + * check whether to continue servicing it, or retrieve and set a new one. */ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd) { @@ -926,33 +974,41 @@ static struct cfq_queue *cfq_select_queu goto new_queue; /* - * slice has expired + * The active queue has run out of time, expire it and select new. */ - if (!cfq_cfqq_must_dispatch(cfqq) && cfq_slice_used(cfqq)) + if (cfq_slice_used(cfqq)) goto expire; /* - * if queue has requests, dispatch one. if not, check if - * enough slice is left to wait for one + * The active queue has requests and isn't expired, allow it to + * dispatch. */ if (!RB_EMPTY_ROOT(&cfqq->sort_list)) goto keep_queue; - else if (cfq_cfqq_slice_new(cfqq) || cfq_cfqq_dispatched(cfqq)) { + + /* + * No requests pending. If the active queue still has requests in + * flight or is idling for a new request, allow either of these + * conditions to happen (or time out) before selecting a new queue. + */ + if (timer_pending(&cfqd->idle_slice_timer) || + (cfqq->dispatched && cfq_cfqq_idle_window(cfqq))) { cfqq = NULL; goto keep_queue; - } else if (cfq_cfqq_class_sync(cfqq)) { - if (cfq_arm_slice_timer(cfqd)) - return NULL; } expire: - cfq_slice_expired(cfqd, 0, 0); + cfq_slice_expired(cfqd, 0); new_queue: cfqq = cfq_set_active_queue(cfqd); keep_queue: return cfqq; } +/* + * Dispatch some requests from cfqq, moving them to the request queue + * dispatch list. + */ static int __cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq, int max_dispatch) @@ -975,7 +1031,6 @@ __cfq_dispatch_requests(struct cfq_data */ cfq_dispatch_insert(cfqd->queue, rq); - cfqd->dispatch_slice++; dispatched++; if (!cfqd->active_cic) { @@ -993,57 +1048,54 @@ __cfq_dispatch_requests(struct cfq_data * queue always expire after 1 dispatch round. */ if (cfqd->busy_queues > 1 && ((!cfq_cfqq_sync(cfqq) && - cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) || + dispatched >= cfq_prio_to_maxrq(cfqd, cfqq)) || cfq_class_idle(cfqq))) { cfqq->slice_end = jiffies + 1; - cfq_slice_expired(cfqd, 0, 0); + cfq_slice_expired(cfqd, 0); } return dispatched; } -static int -cfq_forced_dispatch_cfqqs(struct list_head *list) +static inline int __cfq_forced_dispatch_cfqq(struct cfq_queue *cfqq) { - struct cfq_queue *cfqq, *next; - int dispatched; + int dispatched = 0; - dispatched = 0; - list_for_each_entry_safe(cfqq, next, list, cfq_list) { - while (cfqq->next_rq) { - cfq_dispatch_insert(cfqq->cfqd->queue, cfqq->next_rq); - dispatched++; - } - BUG_ON(!list_empty(&cfqq->fifo)); + while (cfqq->next_rq) { + cfq_dispatch_insert(cfqq->cfqd->queue, cfqq->next_rq); + dispatched++; } + BUG_ON(!list_empty(&cfqq->fifo)); return dispatched; } -static int -cfq_forced_dispatch(struct cfq_data *cfqd) +/* + * Drain our current requests. Used for barriers and when switching + * io schedulers on-the-fly. + */ +static int cfq_forced_dispatch(struct cfq_data *cfqd) { - int i, dispatched = 0; + int dispatched = 0; + struct rb_node *n; - for (i = 0; i < CFQ_PRIO_LISTS; i++) - dispatched += cfq_forced_dispatch_cfqqs(&cfqd->rr_list[i]); + while ((n = cfq_rb_first(&cfqd->service_tree)) != NULL) { + struct cfq_queue *cfqq = rb_entry(n, struct cfq_queue, rb_node); - dispatched += cfq_forced_dispatch_cfqqs(&cfqd->busy_rr); - dispatched += cfq_forced_dispatch_cfqqs(&cfqd->cur_rr); - dispatched += cfq_forced_dispatch_cfqqs(&cfqd->idle_rr); + dispatched += __cfq_forced_dispatch_cfqq(cfqq); + } - cfq_slice_expired(cfqd, 0, 0); + cfq_slice_expired(cfqd, 0); BUG_ON(cfqd->busy_queues); return dispatched; } -static int -cfq_dispatch_requests(request_queue_t *q, int force) +static int cfq_dispatch_requests(request_queue_t *q, int force) { struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_queue *cfqq, *prev_cfqq; + struct cfq_queue *cfqq; int dispatched; if (!cfqd->busy_queues) @@ -1053,36 +1105,28 @@ cfq_dispatch_requests(request_queue_t *q return cfq_forced_dispatch(cfqd); dispatched = 0; - prev_cfqq = NULL; while ((cfqq = cfq_select_queue(cfqd)) != NULL) { int max_dispatch; - if (cfqd->busy_queues > 1) { - /* - * Don't repeat dispatch from the previous queue. - */ - if (prev_cfqq == cfqq) - break; + max_dispatch = cfqd->cfq_quantum; + if (cfq_class_idle(cfqq)) + max_dispatch = 1; - /* - * So we have dispatched before in this round, if the - * next queue has idling enabled (must be sync), don't - * allow it service until the previous have continued. - */ - if (cfqd->rq_in_driver && cfq_cfqq_idle_window(cfqq)) + if (cfqq->dispatched >= max_dispatch) { + if (cfqd->busy_queues > 1) + break; + if (cfqq->dispatched >= 4 * max_dispatch) break; } + if (cfqd->sync_flight && !cfq_cfqq_sync(cfqq)) + break; + cfq_clear_cfqq_must_dispatch(cfqq); cfq_clear_cfqq_wait_request(cfqq); del_timer(&cfqd->idle_slice_timer); - max_dispatch = cfqd->cfq_quantum; - if (cfq_class_idle(cfqq)) - max_dispatch = 1; - dispatched += __cfq_dispatch_requests(cfqd, cfqq, max_dispatch); - prev_cfqq = cfqq; } return dispatched; @@ -1108,48 +1152,21 @@ static void cfq_put_queue(struct cfq_que BUG_ON(cfq_cfqq_on_rr(cfqq)); if (unlikely(cfqd->active_queue == cfqq)) { - __cfq_slice_expired(cfqd, cfqq, 0, 0); + __cfq_slice_expired(cfqd, cfqq, 0); cfq_schedule_dispatch(cfqd); } - /* - * it's on the empty list and still hashed - */ - list_del(&cfqq->cfq_list); - hlist_del(&cfqq->cfq_hash); kmem_cache_free(cfq_pool, cfqq); } -static struct cfq_queue * -__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned int prio, - const int hashval) -{ - struct hlist_head *hash_list = &cfqd->cfq_hash[hashval]; - struct hlist_node *entry; - struct cfq_queue *__cfqq; - - hlist_for_each_entry(__cfqq, entry, hash_list, cfq_hash) { - const unsigned short __p = IOPRIO_PRIO_VALUE(__cfqq->org_ioprio_class, __cfqq->org_ioprio); - - if (__cfqq->key == key && (__p == prio || !prio)) - return __cfqq; - } - - return NULL; -} - -static struct cfq_queue * -cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned short prio) -{ - return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT)); -} - static void cfq_free_io_context(struct io_context *ioc) { struct cfq_io_context *__cic; struct rb_node *n; int freed = 0; + ioc->ioc_data = NULL; + while ((n = rb_first(&ioc->cic_root)) != NULL) { __cic = rb_entry(n, struct cfq_io_context, rb_node); rb_erase(&__cic->rb_node, &ioc->cic_root); @@ -1166,7 +1183,7 @@ static void cfq_free_io_context(struct i static void cfq_exit_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq) { if (unlikely(cfqq == cfqd->active_queue)) { - __cfq_slice_expired(cfqd, cfqq, 0, 0); + __cfq_slice_expired(cfqd, cfqq, 0); cfq_schedule_dispatch(cfqd); } @@ -1191,10 +1208,6 @@ static void __cfq_exit_single_io_context } } - -/* - * Called with interrupts disabled - */ static void cfq_exit_single_io_context(struct cfq_io_context *cic) { struct cfq_data *cfqd = cic->key; @@ -1208,15 +1221,20 @@ static void cfq_exit_single_io_context(s } } +/* + * The process that ioc belongs to has exited, we need to clean up + * and put the internal structures we have that belongs to that process. + */ static void cfq_exit_io_context(struct io_context *ioc) { struct cfq_io_context *__cic; struct rb_node *n; + ioc->ioc_data = NULL; + /* * put the reference this task is holding to the various queues */ - n = rb_first(&ioc->cic_root); while (n != NULL) { __cic = rb_entry(n, struct cfq_io_context, rb_node); @@ -1284,8 +1302,6 @@ static void cfq_init_prio_data(struct cf */ cfqq->org_ioprio = cfqq->ioprio; cfqq->org_ioprio_class = cfqq->ioprio_class; - - cfq_resort_rr_list(cfqq, 0); cfq_clear_cfqq_prio_changed(cfqq); } @@ -1303,7 +1319,7 @@ static inline void changed_ioprio(struct cfqq = cic->cfqq[ASYNC]; if (cfqq) { struct cfq_queue *new_cfqq; - new_cfqq = cfq_get_queue(cfqd, CFQ_KEY_ASYNC, cic->ioc->task, + new_cfqq = cfq_get_queue(cfqd, ASYNC, cic->ioc->task, GFP_ATOMIC); if (new_cfqq) { cic->cfqq[ASYNC] = new_cfqq; @@ -1335,16 +1351,16 @@ static void cfq_ioc_set_ioprio(struct io } static struct cfq_queue * -cfq_get_queue(struct cfq_data *cfqd, unsigned int key, struct task_struct *tsk, +cfq_get_queue(struct cfq_data *cfqd, int is_sync, struct task_struct *tsk, gfp_t gfp_mask) { - const int hashval = hash_long(key, CFQ_QHASH_SHIFT); struct cfq_queue *cfqq, *new_cfqq = NULL; - unsigned short ioprio; + struct cfq_io_context *cic; retry: - ioprio = tsk->ioprio; - cfqq = __cfq_find_cfq_hash(cfqd, key, ioprio, hashval); + cic = cfq_cic_rb_lookup(cfqd, tsk->io_context); + /* cic always exists here */ + cfqq = cic_to_cfqq(cic, is_sync); if (!cfqq) { if (new_cfqq) { @@ -1369,20 +1385,20 @@ retry: memset(cfqq, 0, sizeof(*cfqq)); - INIT_HLIST_NODE(&cfqq->cfq_hash); - INIT_LIST_HEAD(&cfqq->cfq_list); + RB_CLEAR_NODE(&cfqq->rb_node); INIT_LIST_HEAD(&cfqq->fifo); - cfqq->key = key; - hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); atomic_set(&cfqq->ref, 0); cfqq->cfqd = cfqd; - if (key != CFQ_KEY_ASYNC) + if (is_sync) { cfq_mark_cfqq_idle_window(cfqq); + cfq_mark_cfqq_sync(cfqq); + } cfq_mark_cfqq_prio_changed(cfqq); cfq_mark_cfqq_queue_new(cfqq); + cfq_init_prio_data(cfqq); } @@ -1395,10 +1411,17 @@ out: return cfqq; } +/* + * We drop cfq io contexts lazily, so we may find a dead one. + */ static void cfq_drop_dead_cic(struct io_context *ioc, struct cfq_io_context *cic) { WARN_ON(!list_empty(&cic->queue_list)); + + if (ioc->ioc_data == cic) + ioc->ioc_data = NULL; + rb_erase(&cic->rb_node, &ioc->cic_root); kmem_cache_free(cfq_ioc_pool, cic); elv_ioc_count_dec(ioc_count); @@ -1411,6 +1434,16 @@ cfq_cic_rb_lookup(struct cfq_data *cfqd, struct cfq_io_context *cic; void *k, *key = cfqd; + if (unlikely(!ioc)) + return NULL; + + /* + * we maintain a last-hit cache, to avoid browsing over the tree + */ + cic = ioc->ioc_data; + if (cic && cic->key == cfqd) + return cic; + restart: n = ioc->cic_root.rb_node; while (n) { @@ -1426,8 +1459,10 @@ restart: n = n->rb_left; else if (key > k) n = n->rb_right; - else + else { + ioc->ioc_data = cic; return cic; + } } return NULL; @@ -1524,7 +1559,8 @@ cfq_update_io_thinktime(struct cfq_data } static void -cfq_update_io_seektime(struct cfq_io_context *cic, struct request *rq) +cfq_update_io_seektime(struct cfq_data *cfqd, struct cfq_io_context *cic, + struct request *rq) { sector_t sdist; u64 total; @@ -1534,6 +1570,11 @@ cfq_update_io_seektime(struct cfq_io_con else sdist = cic->last_request_pos - rq->sector; + if (!cic->seek_samples) { + cfqd->new_seek_total = (7*cic->seek_total + (u64)256*sdist) / 8; + cfqd->new_seek_mean = cfqd->new_seek_total / 256; + } + /* * Don't allow the seek distance to get too large from the * odd fragment, pagein, etc @@ -1558,7 +1599,12 @@ static void cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, struct cfq_io_context *cic) { - int enable_idle = cfq_cfqq_idle_window(cfqq); + int enable_idle; + + if (!cfq_cfqq_sync(cfqq)) + return; + + enable_idle = cfq_cfqq_idle_window(cfqq); if (!cic->ioc->task || !cfqd->cfq_slice_idle || (cfqd->hw_tag && CIC_SEEKY(cic))) @@ -1584,24 +1630,28 @@ static int cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, struct request *rq) { - struct cfq_queue *cfqq = cfqd->active_queue; + struct cfq_queue *cfqq; - if (cfq_class_idle(new_cfqq)) + cfqq = cfqd->active_queue; + if (!cfqq) return 0; - if (!cfqq) + if (cfq_slice_used(cfqq)) + return 1; + + if (cfq_class_idle(new_cfqq)) return 0; if (cfq_class_idle(cfqq)) return 1; - if (!cfq_cfqq_wait_request(new_cfqq)) - return 0; + /* * if the new request is sync, but the currently running queue is * not, let the sync request have priority. */ if (rq_is_sync(rq) && !cfq_cfqq_sync(cfqq)) return 1; + /* * So both queues are sync. Let the new request get disk time if * it's a metadata request and the current queue is doing regular IO. @@ -1609,6 +1659,16 @@ cfq_should_preempt(struct cfq_data *cfqd if (rq_is_meta(rq) && !cfqq->meta_pending) return 1; + if (!cfqd->active_cic || !cfq_cfqq_wait_request(cfqq)) + return 0; + + /* + * if this request is as-good as one we would expect from the + * current cfqq, let it preempt + */ + if (cfq_rq_close(cfqd, rq)) + return 1; + return 0; } @@ -1618,14 +1678,15 @@ cfq_should_preempt(struct cfq_data *cfqd */ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) { - cfq_slice_expired(cfqd, 1, 1); + cfq_slice_expired(cfqd, 1); /* * Put the new queue at the front of the of the current list, * so we know that it will be selected next. */ BUG_ON(!cfq_cfqq_on_rr(cfqq)); - list_move(&cfqq->cfq_list, &cfqd->cur_rr); + + cfq_service_tree_add(cfqd, cfqq, 1); cfqq->slice_end = 0; cfq_mark_cfqq_slice_new(cfqq); @@ -1644,28 +1705,12 @@ cfq_rq_enqueued(struct cfq_data *cfqd, s if (rq_is_meta(rq)) cfqq->meta_pending++; - /* - * we never wait for an async request and we don't allow preemption - * of an async request. so just return early - */ - if (!rq_is_sync(rq)) { - /* - * sync process issued an async request, if it's waiting - * then expire it and kick rq handling. - */ - if (cic == cfqd->active_cic && - del_timer(&cfqd->idle_slice_timer)) { - cfq_slice_expired(cfqd, 0, 0); - blk_start_queueing(cfqd->queue); - } - return; - } - cfq_update_io_thinktime(cfqd, cic); - cfq_update_io_seektime(cic, rq); + cfq_update_io_seektime(cfqd, cic, rq); cfq_update_idle_window(cfqd, cfqq, cic); cic->last_request_pos = rq->sector + rq->nr_sectors; + cfqq->last_request_pos = cic->last_request_pos; if (cfqq == cfqd->active_queue) { /* @@ -1714,16 +1759,16 @@ static void cfq_completed_request(reques now = jiffies; WARN_ON(!cfqd->rq_in_driver); - WARN_ON(!cfqq->on_dispatch[sync]); + WARN_ON(!cfqq->dispatched); cfqd->rq_in_driver--; - cfqq->on_dispatch[sync]--; - cfqq->service_last = now; + cfqq->dispatched--; + + if (cfq_cfqq_sync(cfqq)) + cfqd->sync_flight--; if (!cfq_class_idle(cfqq)) cfqd->last_end_request = now; - cfq_resort_rr_list(cfqq, 0); - if (sync) RQ_CIC(rq)->last_end_request = now; @@ -1737,12 +1782,13 @@ static void cfq_completed_request(reques cfq_clear_cfqq_slice_new(cfqq); } if (cfq_slice_used(cfqq)) - cfq_slice_expired(cfqd, 0, 1); - else if (sync && RB_EMPTY_ROOT(&cfqq->sort_list)) { - if (!cfq_arm_slice_timer(cfqd)) - cfq_schedule_dispatch(cfqd); - } + cfq_slice_expired(cfqd, 1); + else if (sync && RB_EMPTY_ROOT(&cfqq->sort_list)) + cfq_arm_slice_timer(cfqd); } + + if (!cfqd->rq_in_driver) + cfq_schedule_dispatch(cfqd); } /* @@ -1751,9 +1797,6 @@ static void cfq_completed_request(reques */ static void cfq_prio_boost(struct cfq_queue *cfqq) { - const int ioprio_class = cfqq->ioprio_class; - const int ioprio = cfqq->ioprio; - if (has_fs_excl()) { /* * boost idle prio on transactions that would lock out other @@ -1772,12 +1815,6 @@ static void cfq_prio_boost(struct cfq_qu if (cfqq->ioprio != cfqq->org_ioprio) cfqq->ioprio = cfqq->org_ioprio; } - - /* - * refile between round-robin lists if we moved the priority class - */ - if ((ioprio_class != cfqq->ioprio_class || ioprio != cfqq->ioprio)) - cfq_resort_rr_list(cfqq, 0); } static inline int __cfq_may_queue(struct cfq_queue *cfqq) @@ -1795,10 +1832,8 @@ static int cfq_may_queue(request_queue_t { struct cfq_data *cfqd = q->elevator->elevator_data; struct task_struct *tsk = current; + struct cfq_io_context *cic; struct cfq_queue *cfqq; - unsigned int key; - - key = cfq_queue_pid(tsk, rw, rw & REQ_RW_SYNC); /* * don't force setup of a queue from here, as a call to may_queue @@ -1806,7 +1841,11 @@ static int cfq_may_queue(request_queue_t * so just lookup a possibly existing queue, or return 'may queue' * if that fails */ - cfqq = cfq_find_cfq_hash(cfqd, key, tsk->ioprio); + cic = cfq_cic_rb_lookup(cfqd, tsk->io_context); + if (!cic) + return ELV_MQUEUE_MAY; + + cfqq = cic_to_cfqq(cic, rw & REQ_RW_SYNC); if (cfqq) { cfq_init_prio_data(cfqq); cfq_prio_boost(cfqq); @@ -1850,7 +1889,6 @@ cfq_set_request(request_queue_t *q, stru struct cfq_io_context *cic; const int rw = rq_data_dir(rq); const int is_sync = rq_is_sync(rq); - pid_t key = cfq_queue_pid(tsk, rw, is_sync); struct cfq_queue *cfqq; unsigned long flags; @@ -1863,14 +1901,15 @@ cfq_set_request(request_queue_t *q, stru if (!cic) goto queue_fail; - if (!cic->cfqq[is_sync]) { - cfqq = cfq_get_queue(cfqd, key, tsk, gfp_mask); + cfqq = cic_to_cfqq(cic, is_sync); + if (!cfqq) { + cfqq = cfq_get_queue(cfqd, is_sync, tsk, gfp_mask); + if (!cfqq) goto queue_fail; - cic->cfqq[is_sync] = cfqq; - } else - cfqq = cic->cfqq[is_sync]; + cic_set_cfqq(cic, cfqq, is_sync); + } cfqq->allocated[rw]++; cfq_clear_cfqq_must_alloc(cfqq); @@ -1940,7 +1979,7 @@ static void cfq_idle_slice_timer(unsigne } } expire: - cfq_slice_expired(cfqd, 0, timed_out); + cfq_slice_expired(cfqd, timed_out); out_kick: cfq_schedule_dispatch(cfqd); out_cont: @@ -1986,7 +2025,7 @@ static void cfq_exit_queue(elevator_t *e spin_lock_irq(q->queue_lock); if (cfqd->active_queue) - __cfq_slice_expired(cfqd, cfqd->active_queue, 0, 0); + __cfq_slice_expired(cfqd, cfqd->active_queue, 0); while (!list_empty(&cfqd->cic_list)) { struct cfq_io_context *cic = list_entry(cfqd->cic_list.next, @@ -2000,14 +2039,12 @@ static void cfq_exit_queue(elevator_t *e cfq_shutdown_timer_wq(cfqd); - kfree(cfqd->cfq_hash); kfree(cfqd); } static void *cfq_init_queue(request_queue_t *q) { struct cfq_data *cfqd; - int i; cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL, q->node); if (!cfqd) @@ -2015,21 +2052,9 @@ static void *cfq_init_queue(request_queu memset(cfqd, 0, sizeof(*cfqd)); - for (i = 0; i < CFQ_PRIO_LISTS; i++) - INIT_LIST_HEAD(&cfqd->rr_list[i]); - - INIT_LIST_HEAD(&cfqd->busy_rr); - INIT_LIST_HEAD(&cfqd->cur_rr); - INIT_LIST_HEAD(&cfqd->idle_rr); + cfqd->service_tree = CFQ_RB_ROOT; INIT_LIST_HEAD(&cfqd->cic_list); - cfqd->cfq_hash = kmalloc_node(sizeof(struct hlist_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL, q->node); - if (!cfqd->cfq_hash) - goto out_free; - - for (i = 0; i < CFQ_QHASH_ENTRIES; i++) - INIT_HLIST_HEAD(&cfqd->cfq_hash[i]); - cfqd->queue = q; init_timer(&cfqd->idle_slice_timer); @@ -2053,9 +2078,6 @@ static void *cfq_init_queue(request_queu cfqd->cfq_slice_idle = cfq_slice_idle; return cfqd; -out_free: - kfree(cfqd); - return NULL; } static void cfq_slab_kill(void) @@ -2087,7 +2109,6 @@ fail: /* * sysfs parts below --> */ - static ssize_t cfq_var_show(unsigned int var, char *page) { diff --git a/block/elevator.c b/block/elevator.c index 96a00c8..ce866eb 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -134,13 +134,13 @@ static struct elevator_type *elevator_ge { struct elevator_type *e; - spin_lock_irq(&elv_list_lock); + spin_lock(&elv_list_lock); e = elevator_find(name); if (e && !try_module_get(e->elevator_owner)) e = NULL; - spin_unlock_irq(&elv_list_lock); + spin_unlock(&elv_list_lock); return e; } @@ -965,10 +965,11 @@ void elv_unregister_queue(struct request int elv_register(struct elevator_type *e) { char *def = ""; - spin_lock_irq(&elv_list_lock); + + spin_lock(&elv_list_lock); BUG_ON(elevator_find(e->elevator_name)); list_add_tail(&e->list, &elv_list); - spin_unlock_irq(&elv_list_lock); + spin_unlock(&elv_list_lock); if (!strcmp(e->elevator_name, chosen_elevator) || (!*chosen_elevator && @@ -998,9 +999,9 @@ void elv_unregister(struct elevator_type read_unlock(&tasklist_lock); } - spin_lock_irq(&elv_list_lock); + spin_lock(&elv_list_lock); list_del_init(&e->list); - spin_unlock_irq(&elv_list_lock); + spin_unlock(&elv_list_lock); } EXPORT_SYMBOL_GPL(elv_unregister); @@ -1118,7 +1119,7 @@ ssize_t elv_iosched_show(request_queue_t struct list_head *entry; int len = 0; - spin_lock_irq(&elv_list_lock); + spin_lock(&elv_list_lock); list_for_each(entry, &elv_list) { struct elevator_type *__e; @@ -1128,7 +1129,7 @@ ssize_t elv_iosched_show(request_queue_t else len += sprintf(name+len, "%s ", __e->elevator_name); } - spin_unlock_irq(&elv_list_lock); + spin_unlock(&elv_list_lock); len += sprintf(len+name, "\n"); return len; diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index 3de0695..123003a 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -3741,6 +3741,7 @@ static struct io_context *current_io_con ret->nr_batch_requests = 0; /* because this is 0 */ ret->aic = NULL; ret->cic_root.rb_node = NULL; + ret->ioc_data = NULL; /* make sure set_task_ioprio() sees the settings above */ smp_wmb(); tsk->io_context = ret; diff --git a/crypto/michael_mic.c b/crypto/michael_mic.c index 094397b..9e917b8 100644 --- a/crypto/michael_mic.c +++ b/crypto/michael_mic.c @@ -3,7 +3,7 @@ * * Michael MIC (IEEE 802.11i/TKIP) keyed digest * - * Copyright (c) 2004 Jouni Malinen + * Copyright (c) 2004 Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -173,4 +173,4 @@ module_exit(michael_mic_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Michael MIC"); -MODULE_AUTHOR("Jouni Malinen "); +MODULE_AUTHOR("Jouni Malinen "); diff --git a/drivers/Makefile b/drivers/Makefile index 3a718f5..920c975 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -72,7 +72,6 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ -obj-$(CONFIG_IPATH_CORE) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index e2ce4a9..139f41f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -85,8 +85,8 @@ config ACPI_PROCFS depends on ACPI default y ---help--- - Procfs interface for ACPI is made optional for back-compatible. - As the same functions are duplicated in sysfs interface + The Procfs interface for ACPI is made optional for backward compatibility. + As the same functions are duplicated in the sysfs interface and this proc interface will be removed some time later, it's marked as deprecated. ( /proc/acpi/debug_layer && debug_level are deprecated by @@ -218,43 +218,6 @@ config ACPI_ASUS NOTE: This driver is deprecated and will probably be removed soon, use asus-laptop instead. -config ACPI_IBM - tristate "IBM ThinkPad Laptop Extras" - depends on X86 - select BACKLIGHT_CLASS_DEVICE - ---help--- - This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds - support for Fn-Fx key combinations, Bluetooth control, video - output switching, ThinkLight control, UltraBay eject and more. - For more information about this driver see - and . - - If you have an IBM ThinkPad laptop, say Y or M here. - -config ACPI_IBM_DOCK - bool "Legacy Docking Station Support" - depends on ACPI_IBM - depends on ACPI_DOCK=n - default n - ---help--- - Allows the ibm_acpi driver to handle docking station events. - This support is obsoleted by CONFIG_HOTPLUG_PCI_ACPI. It will - allow locking and removing the laptop from the docking station, - but will not properly connect PCI devices. - - If you are not sure, say N here. - -config ACPI_IBM_BAY - bool "Legacy Removable Bay Support" - depends on ACPI_IBM - default y - ---help--- - Allows the ibm_acpi driver to handle removable bays. It will allow - disabling the device in the bay, and also generate notifications when - the bay lever is ejected or inserted. - - If you are not sure, say Y here. - config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on X86 @@ -388,11 +351,10 @@ config ACPI_HOTPLUG_MEMORY config ACPI_SBS tristate "Smart Battery System (EXPERIMENTAL)" - depends on X86 && I2C + depends on X86 depends on EXPERIMENTAL help This driver adds support for the Smart Battery System. - Depends on I2C (Device Drivers ---> I2C support) A "Smart Battery" is quite old and quite rare compared to today's ACPI "Control Method" battery. diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 5956e9f..d4336f1 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -1,6 +1,6 @@ # # Makefile for the Linux ACPI interpreter -# +# export ACPI_CFLAGS @@ -32,16 +32,17 @@ # processor-objs += processor_core.o processor_throttling.o \ processor_idle.o processor_thermal.o ifdef CONFIG_CPU_FREQ -processor-objs += processor_perflib.o +processor-objs += processor_perflib.o endif obj-y += sleep/ obj-y += bus.o glue.o obj-y += scan.o +# Keep EC driver first. Initialization of others depend on it. +obj-$(CONFIG_ACPI_EC) += ec.o obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_BUTTON) += button.o -obj-$(CONFIG_ACPI_EC) += ec.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_DOCK) += dock.o obj-$(CONFIG_ACPI_BAY) += bay.o @@ -55,8 +56,7 @@ obj-$(CONFIG_ACPI_SYSTEM) += system.o ev obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o -obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-y += cm_sbs.o -obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o +obj-$(CONFIG_ACPI_SBS) += sbs.o diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index c261726..e65628a 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -44,11 +44,6 @@ MODULE_AUTHOR("Naveen B S parent) device->status = device->parent->status; else - STRUCT_TO_INT(device->status) = 0x0F; + STRUCT_TO_INT(device->status) = + ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; if (device->status.functional && !device->status.present) { printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: " diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 0930d94..0dd3bf7 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -49,8 +49,6 @@ MODULE_AUTHOR("Anil S Keshavamurthy"); MODULE_DESCRIPTION("ACPI container driver"); MODULE_LICENSE("GPL"); -#define ACPI_STA_PRESENT (0x00000001) - static int acpi_container_add(struct acpi_device *device); static int acpi_container_remove(struct acpi_device *device, int type); @@ -75,13 +73,13 @@ static int is_device_present(acpi_handle status = acpi_get_handle(handle, "_STA", &temp); if (ACPI_FAILURE(status)) - return 1; /* _STA not found, assmue device present */ + return 1; /* _STA not found, assume device present */ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); if (ACPI_FAILURE(status)) return 0; /* Firmware error */ - return ((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT); + return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT); } /*******************************************************************/ diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 54a697f..4546bf8 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -29,6 +29,7 @@ #include #include #include #include +#include #include #include @@ -667,6 +668,23 @@ static ssize_t write_undock(struct devic } DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock); +/* + * show_dock_uid - read method for "uid" file in sysfs + */ +static ssize_t show_dock_uid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long lbuf; + acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf); + if(ACPI_FAILURE(status)) { + return 0; + } + return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf); +} +DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); + + + /** * dock_add - add a new dock station * @handle: the dock station handle @@ -715,6 +733,13 @@ static int dock_add(acpi_handle handle) kfree(dock_station); return ret; } + ret = device_create_file(&dock_device.dev, &dev_attr_uid); + if (ret) { + printk("Error %d adding sysfs file\n", ret); + platform_device_unregister(&dock_device); + kfree(dock_station); + return ret; + } /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a802962..e08cf98 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1,6 +1,8 @@ /* - * acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $) + * ec.c - ACPI Embedded Controller Driver (v2.0) * + * Copyright (C) 2006, 2007 Alexey Starikovskiy + * Copyright (C) 2006 Denis Sadykov * Copyright (C) 2004 Luming Yu * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh @@ -91,9 +93,9 @@ static struct acpi_driver acpi_ec_driver }; /* If we find an EC via the ECDT, we need to keep a ptr to its context */ +/* External interfaces use first EC only, so remember */ static struct acpi_ec { acpi_handle handle; - unsigned long uid; unsigned long gpe; unsigned long command_addr; unsigned long data_addr; @@ -101,12 +103,8 @@ static struct acpi_ec { struct mutex lock; atomic_t query_pending; atomic_t event_count; - atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ wait_queue_head_t wait; -} *ec_ecdt; - -/* External interfaces use first EC only, so remember */ -static struct acpi_device *first_ec; +} *boot_ec, *first_ec; /* -------------------------------------------------------------------------- Transaction Management @@ -173,56 +171,6 @@ static int acpi_ec_wait(struct acpi_ec * return -ETIME; } -#ifdef ACPI_FUTURE_USAGE -/* - * Note: samsung nv5000 doesn't work with ec burst mode. - * http://bugzilla.kernel.org/show_bug.cgi?id=4980 - */ -int acpi_ec_enter_burst_mode(struct acpi_ec *ec) -{ - u8 tmp = 0; - u8 status = 0; - - status = acpi_ec_read_status(ec); - if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) { - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - if (status) - goto end; - acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); - tmp = acpi_ec_read_data(ec); - if (tmp != 0x90) { /* Burst ACK byte */ - return -EINVAL; - } - } - - atomic_set(&ec->leaving_burst, 0); - return 0; - end: - ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode")); - return -1; -} - -int acpi_ec_leave_burst_mode(struct acpi_ec *ec) -{ - u8 status = 0; - - status = acpi_ec_read_status(ec); - if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) { - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - if (status) - goto end; - acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE); - acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - } - atomic_set(&ec->leaving_burst, 1); - return 0; - end: - ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode")); - return -1; -} -#endif /* ACPI_FUTURE_USAGE */ - static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, const u8 * wdata, unsigned wdata_len, u8 * rdata, unsigned rdata_len) @@ -312,6 +260,21 @@ static int acpi_ec_transaction(struct ac return status; } +/* + * Note: samsung nv5000 doesn't work with ec burst mode. + * http://bugzilla.kernel.org/show_bug.cgi?id=4980 + */ +int acpi_ec_burst_enable(struct acpi_ec *ec) +{ + u8 d; + return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1); +} + +int acpi_ec_burst_disable(struct acpi_ec *ec) +{ + return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0); +} + static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) { int result; @@ -333,18 +296,33 @@ static int acpi_ec_write(struct acpi_ec /* * Externally callable EC access functions. For now, assume 1 EC only */ +int ec_burst_enable(void) +{ + if (!first_ec) + return -ENODEV; + return acpi_ec_burst_enable(first_ec); +} + +EXPORT_SYMBOL(ec_burst_enable); + +int ec_burst_disable(void) +{ + if (!first_ec) + return -ENODEV; + return acpi_ec_burst_disable(first_ec); +} + +EXPORT_SYMBOL(ec_burst_disable); + int ec_read(u8 addr, u8 * val) { - struct acpi_ec *ec; int err; u8 temp_data; if (!first_ec) return -ENODEV; - ec = acpi_driver_data(first_ec); - - err = acpi_ec_read(ec, addr, &temp_data); + err = acpi_ec_read(first_ec, addr, &temp_data); if (!err) { *val = temp_data; @@ -357,15 +335,12 @@ EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) { - struct acpi_ec *ec; int err; if (!first_ec) return -ENODEV; - ec = acpi_driver_data(first_ec); - - err = acpi_ec_write(ec, addr, val); + err = acpi_ec_write(first_ec, addr, val); return err; } @@ -376,14 +351,10 @@ int ec_transaction(u8 command, const u8 * wdata, unsigned wdata_len, u8 * rdata, unsigned rdata_len) { - struct acpi_ec *ec; - if (!first_ec) return -ENODEV; - ec = acpi_driver_data(first_ec); - - return acpi_ec_transaction(ec, command, wdata, + return acpi_ec_transaction(first_ec, command, wdata, wdata_len, rdata, rdata_len); } @@ -420,7 +391,7 @@ static int acpi_ec_query(struct acpi_ec static void acpi_ec_gpe_query(void *ec_cxt) { - struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; + struct acpi_ec *ec = ec_cxt; u8 value = 0; char object_name[8]; @@ -438,8 +409,9 @@ static u32 acpi_ec_gpe_handler(void *dat { acpi_status status = AE_OK; u8 value; - struct acpi_ec *ec = (struct acpi_ec *)data; + struct acpi_ec *ec = data; atomic_inc(&ec->event_count); + if (acpi_ec_mode == EC_INTR) { wake_up(&ec->wait); } @@ -482,7 +454,7 @@ acpi_ec_space_handler(u32 function, void *handler_context, void *region_context) { int result = 0; - struct acpi_ec *ec = NULL; + struct acpi_ec *ec = handler_context; u64 temp = *value; acpi_integer f_v = 0; int i = 0; @@ -494,8 +466,6 @@ acpi_ec_space_handler(u32 function, return AE_BAD_PARAMETER; } - ec = (struct acpi_ec *)handler_context; - next_byte: switch (function) { case ACPI_READ: @@ -551,18 +521,16 @@ static struct proc_dir_entry *acpi_ec_di static int acpi_ec_read_info(struct seq_file *seq, void *offset) { - struct acpi_ec *ec = (struct acpi_ec *)seq->private; + struct acpi_ec *ec = seq->private; if (!ec) goto end; - seq_printf(seq, "gpe: 0x%02x\n", (u32) ec->gpe); - seq_printf(seq, "ports: 0x%02x, 0x%02x\n", - (u32) ec->command_addr, (u32) ec->data_addr); - seq_printf(seq, "use global lock: %s\n", + seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe); + seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n", + (unsigned)ec->command_addr, (unsigned)ec->data_addr); + seq_printf(seq, "use global lock:\t%s\n", ec->global_lock ? "yes" : "no"); - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); - end: return 0; } @@ -619,154 +587,122 @@ static int acpi_ec_remove_fs(struct acpi /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ +static acpi_status +ec_parse_io_ports(struct acpi_resource *resource, void *context); + +static acpi_status +ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval); + +static struct acpi_ec *make_acpi_ec(void) +{ + struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec) + return NULL; + + atomic_set(&ec->query_pending, 1); + atomic_set(&ec->event_count, 1); + mutex_init(&ec->lock); + init_waitqueue_head(&ec->wait); + + return ec; +} static int acpi_ec_add(struct acpi_device *device) { - int result = 0; acpi_status status = AE_OK; struct acpi_ec *ec = NULL; if (!device) return -EINVAL; - ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); - if (!ec) - return -ENOMEM; - - ec->handle = device->handle; - ec->uid = -1; - mutex_init(&ec->lock); - atomic_set(&ec->query_pending, 0); - atomic_set(&ec->event_count, 1); - if (acpi_ec_mode == EC_INTR) { - atomic_set(&ec->leaving_burst, 1); - init_waitqueue_head(&ec->wait); - } strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_EC_CLASS); - acpi_driver_data(device) = ec; - - /* Use the global lock for all EC transactions? */ - acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); - - /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: - http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ - if (ec_ecdt) { - acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler); - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, - &acpi_ec_gpe_handler); + ec = make_acpi_ec(); + if (!ec) + return -ENOMEM; - kfree(ec_ecdt); + status = ec_parse_device(device->handle, 0, ec, NULL); + if (status != AE_CTRL_TERMINATE) { + kfree(ec); + return -EINVAL; } - /* Get GPE bit assignment (EC events). */ - /* TODO: Add support for _GPE returning a package */ - status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Obtaining GPE bit assignment")); - result = -ENODEV; - goto end; - } + /* Check if we found the boot EC */ + if (boot_ec) { + if (boot_ec->gpe == ec->gpe) { + /* We might have incorrect info for GL at boot time */ + mutex_lock(&boot_ec->lock); + boot_ec->global_lock = ec->global_lock; + mutex_unlock(&boot_ec->lock); + kfree(ec); + ec = boot_ec; + } + } else + first_ec = ec; + ec->handle = device->handle; + acpi_driver_data(device) = ec; - result = acpi_ec_add_fs(device); - if (result) - goto end; + acpi_ec_add_fs(device); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", acpi_device_name(device), acpi_device_bid(device), (u32) ec->gpe)); - if (!first_ec) - first_ec = device; - - end: - if (result) - kfree(ec); - - return result; + return 0; } static int acpi_ec_remove(struct acpi_device *device, int type) { - struct acpi_ec *ec = NULL; + struct acpi_ec *ec; if (!device) return -EINVAL; ec = acpi_driver_data(device); - acpi_ec_remove_fs(device); + acpi_driver_data(device) = NULL; + if (ec == first_ec) + first_ec = NULL; - kfree(ec); - + /* Don't touch boot EC */ + if (boot_ec != ec) + kfree(ec); return 0; } static acpi_status -acpi_ec_io_ports(struct acpi_resource *resource, void *context) +ec_parse_io_ports(struct acpi_resource *resource, void *context) { - struct acpi_ec *ec = (struct acpi_ec *)context; + struct acpi_ec *ec = context; - if (resource->type != ACPI_RESOURCE_TYPE_IO) { + if (resource->type != ACPI_RESOURCE_TYPE_IO) return AE_OK; - } /* * The first address region returned is the data port, and * the second address region returned is the status/command * port. */ - if (ec->data_addr == 0) { + if (ec->data_addr == 0) ec->data_addr = resource->data.io.minimum; - } else if (ec->command_addr == 0) { + else if (ec->command_addr == 0) ec->command_addr = resource->data.io.minimum; - } else { + else return AE_CTRL_TERMINATE; - } return AE_OK; } -static int acpi_ec_start(struct acpi_device *device) +static int ec_install_handlers(struct acpi_ec *ec) { - acpi_status status = AE_OK; - struct acpi_ec *ec = NULL; - - if (!device) - return -EINVAL; - - ec = acpi_driver_data(device); - - if (!ec) - return -EINVAL; - - /* - * Get I/O port addresses. Convert to GAS format. - */ - status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, - acpi_ec_io_ports, ec); - if (ACPI_FAILURE(status) || ec->command_addr == 0) { - ACPI_EXCEPTION((AE_INFO, status, - "Error getting I/O port addresses")); - return -ENODEV; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", - ec->gpe, ec->command_addr, ec->data_addr)); - - /* - * Install GPE handler - */ + acpi_status status; status = acpi_install_gpe_handler(NULL, ec->gpe, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); - if (ACPI_FAILURE(status)) { + if (ACPI_FAILURE(status)) return -ENODEV; - } + acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); @@ -779,18 +715,49 @@ static int acpi_ec_start(struct acpi_dev return -ENODEV; } - return AE_OK; + /* EC is fully operational, allow queries */ + atomic_set(&ec->query_pending, 0); + + return 0; +} + +static int acpi_ec_start(struct acpi_device *device) +{ + struct acpi_ec *ec; + + if (!device) + return -EINVAL; + + ec = acpi_driver_data(device); + + if (!ec) + return -EINVAL; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", + ec->gpe, ec->command_addr, ec->data_addr)); + + /* Boot EC is already working */ + if (ec == boot_ec) + return 0; + + return ec_install_handlers(ec); } static int acpi_ec_stop(struct acpi_device *device, int type) { - acpi_status status = AE_OK; - struct acpi_ec *ec = NULL; + acpi_status status; + struct acpi_ec *ec; if (!device) return -EINVAL; ec = acpi_driver_data(device); + if (!ec) + return -EINVAL; + + /* Don't touch boot EC */ + if (ec == boot_ec) + return 0; status = acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, @@ -805,164 +772,67 @@ static int acpi_ec_stop(struct acpi_devi return 0; } -static acpi_status __init -acpi_fake_ecdt_callback(acpi_handle handle, - u32 Level, void *context, void **retval) +static acpi_status +ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) { acpi_status status; - mutex_init(&ec_ecdt->lock); - atomic_set(&ec_ecdt->event_count, 1); - if (acpi_ec_mode == EC_INTR) { - init_waitqueue_head(&ec_ecdt->wait); - } + struct acpi_ec *ec = context; status = acpi_walk_resources(handle, METHOD_NAME__CRS, - acpi_ec_io_ports, ec_ecdt); + ec_parse_io_ports, ec); if (ACPI_FAILURE(status)) return status; - ec_ecdt->uid = -1; - acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); - - status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe); + /* Get GPE bit assignment (EC events). */ + /* TODO: Add support for _GPE returning a package */ + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); if (ACPI_FAILURE(status)) return status; - ec_ecdt->global_lock = TRUE; - ec_ecdt->handle = handle; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", - ec_ecdt->gpe, ec_ecdt->command_addr, - ec_ecdt->data_addr)); - return AE_CTRL_TERMINATE; -} - -/* - * Some BIOS (such as some from Gateway laptops) access EC region very early - * such as in BAT0._INI or EC._INI before an EC device is found and - * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily - * required, but if EC regison is accessed early, it is required. - * The routine tries to workaround the BIOS bug by pre-scan EC device - * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any - * op region (since _REG isn't invoked yet). The assumption is true for - * all systems found. - */ -static int __init acpi_ec_fake_ecdt(void) -{ - acpi_status status; - int ret = 0; + /* Use the global lock for all EC transactions? */ + acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT")); + ec->handle = handle; - ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); - if (!ec_ecdt) { - ret = -ENOMEM; - goto error; - } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", + ec->gpe, ec->command_addr, ec->data_addr)); - status = acpi_get_devices(ACPI_EC_HID, - acpi_fake_ecdt_callback, NULL, NULL); - if (ACPI_FAILURE(status)) { - kfree(ec_ecdt); - ec_ecdt = NULL; - ret = -ENODEV; - ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT")); - goto error; - } - return 0; - error: - return ret; + return AE_CTRL_TERMINATE; } -static int __init acpi_ec_get_real_ecdt(void) +int __init acpi_ec_ecdt_probe(void) { + int ret; acpi_status status; struct acpi_table_ecdt *ecdt_ptr; - status = acpi_get_table(ACPI_SIG_ECDT, 1, - (struct acpi_table_header **)&ecdt_ptr); - if (ACPI_FAILURE(status)) - return -ENODEV; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); - + boot_ec = make_acpi_ec(); + if (!boot_ec) + return -ENOMEM; /* - * Generate a temporary ec context to use until the namespace is scanned + * Generate a boot ec context */ - ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); - if (!ec_ecdt) - return -ENOMEM; - mutex_init(&ec_ecdt->lock); - atomic_set(&ec_ecdt->event_count, 1); - if (acpi_ec_mode == EC_INTR) { - init_waitqueue_head(&ec_ecdt->wait); - } - ec_ecdt->command_addr = ecdt_ptr->control.address; - ec_ecdt->data_addr = ecdt_ptr->data.address; - ec_ecdt->gpe = ecdt_ptr->gpe; - /* use the GL just to be safe */ - ec_ecdt->global_lock = TRUE; - ec_ecdt->uid = ecdt_ptr->uid; - - status = acpi_get_handle(NULL, ecdt_ptr->id, &ec_ecdt->handle); - if (ACPI_FAILURE(status)) { + status = acpi_get_table(ACPI_SIG_ECDT, 1, + (struct acpi_table_header **)&ecdt_ptr); + if (ACPI_FAILURE(status)) goto error; - } - - return 0; - error: - ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); - kfree(ec_ecdt); - ec_ecdt = NULL; - return -ENODEV; -} - -static int __initdata acpi_fake_ecdt_enabled; -int __init acpi_ec_ecdt_probe(void) -{ - acpi_status status; - int ret; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); - ret = acpi_ec_get_real_ecdt(); - /* Try to make a fake ECDT */ - if (ret && acpi_fake_ecdt_enabled) { - ret = acpi_ec_fake_ecdt(); - } + boot_ec->command_addr = ecdt_ptr->control.address; + boot_ec->data_addr = ecdt_ptr->data.address; + boot_ec->gpe = ecdt_ptr->gpe; + boot_ec->handle = ACPI_ROOT_OBJECT; - if (ret) + ret = ec_install_handlers(boot_ec); + if (!ret) { + first_ec = boot_ec; return 0; - - /* - * Install GPE handler - */ - status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe, - ACPI_GPE_EDGE_TRIGGERED, - &acpi_ec_gpe_handler, ec_ecdt); - if (ACPI_FAILURE(status)) { - goto error; } - acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR); - - status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler, - &acpi_ec_space_setup, - ec_ecdt); - if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, - &acpi_ec_gpe_handler); - goto error; - } - - return 0; - error: - ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); - kfree(ec_ecdt); - ec_ecdt = NULL; + kfree(boot_ec); + boot_ec = NULL; return -ENODEV; } @@ -1003,13 +873,6 @@ static void __exit acpi_ec_exit(void) } #endif /* 0 */ -static int __init acpi_fake_ecdt_setup(char *str) -{ - acpi_fake_ecdt_enabled = 1; - return 1; -} - -__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); static int __init acpi_ec_set_intr_mode(char *str) { int intr; @@ -1017,12 +880,8 @@ static int __init acpi_ec_set_intr_mode( if (!get_option(&str, &intr)) return 0; - if (intr) { - acpi_ec_mode = EC_INTR; - } else { - acpi_ec_mode = EC_POLL; - } - acpi_ec_driver.ops.add = acpi_ec_add; + acpi_ec_mode = (intr) ? EC_INTR : EC_POLL; + printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); return 1; diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c deleted file mode 100644 index acab4a4..0000000 --- a/drivers/acpi/i2c_ec.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * SMBus driver for ACPI Embedded Controller ($Revision: 1.3 $) - * - * Copyright (c) 2002, 2005 Ducrot Bruno - * Copyright (c) 2005 Rich Townsend (tiny hacks & tweaks) - * - * 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "i2c_ec.h" - -#define xudelay(t) udelay(t) -#define xmsleep(t) msleep(t) - -#define ACPI_EC_HC_COMPONENT 0x00080000 -#define ACPI_EC_HC_CLASS "ec_hc_smbus" -#define ACPI_EC_HC_HID "ACPI0001" -#define ACPI_EC_HC_DEVICE_NAME "EC HC smbus" - -#define _COMPONENT ACPI_EC_HC_COMPONENT - -ACPI_MODULE_NAME("i2c_ec"); - -static int acpi_ec_hc_add(struct acpi_device *device); -static int acpi_ec_hc_remove(struct acpi_device *device, int type); - -static struct acpi_driver acpi_ec_hc_driver = { - .name = "i2c_ec", - .class = ACPI_EC_HC_CLASS, - .ids = ACPI_EC_HC_HID, - .ops = { - .add = acpi_ec_hc_add, - .remove = acpi_ec_hc_remove, - }, -}; - -/* Various bit mask for EC_SC (R) */ -#define OBF 0x01 -#define IBF 0x02 -#define CMD 0x08 -#define BURST 0x10 -#define SCI_EVT 0x20 -#define SMI_EVT 0x40 - -/* Commands for EC_SC (W) */ -#define RD_EC 0x80 -#define WR_EC 0x81 -#define BE_EC 0x82 -#define BD_EC 0x83 -#define QR_EC 0x84 - -/* - * ACPI 2.0 chapter 13 SMBus 2.0 EC register model - */ - -#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */ -#define ACPI_EC_SMB_STS 0x01 /* status */ -#define ACPI_EC_SMB_ADDR 0x02 /* address */ -#define ACPI_EC_SMB_CMD 0x03 /* command */ -#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */ -#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */ -#define ACPI_EC_SMB_ALRM_A 0x25 /* alarm address */ -#define ACPI_EC_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ - -#define ACPI_EC_SMB_STS_DONE 0x80 -#define ACPI_EC_SMB_STS_ALRM 0x40 -#define ACPI_EC_SMB_STS_RES 0x20 -#define ACPI_EC_SMB_STS_STATUS 0x1f - -#define ACPI_EC_SMB_STATUS_OK 0x00 -#define ACPI_EC_SMB_STATUS_FAIL 0x07 -#define ACPI_EC_SMB_STATUS_DNAK 0x10 -#define ACPI_EC_SMB_STATUS_DERR 0x11 -#define ACPI_EC_SMB_STATUS_CMD_DENY 0x12 -#define ACPI_EC_SMB_STATUS_UNKNOWN 0x13 -#define ACPI_EC_SMB_STATUS_ACC_DENY 0x17 -#define ACPI_EC_SMB_STATUS_TIMEOUT 0x18 -#define ACPI_EC_SMB_STATUS_NOTSUP 0x19 -#define ACPI_EC_SMB_STATUS_BUSY 0x1A -#define ACPI_EC_SMB_STATUS_PEC 0x1F - -#define ACPI_EC_SMB_PRTCL_WRITE 0x00 -#define ACPI_EC_SMB_PRTCL_READ 0x01 -#define ACPI_EC_SMB_PRTCL_QUICK 0x02 -#define ACPI_EC_SMB_PRTCL_BYTE 0x04 -#define ACPI_EC_SMB_PRTCL_BYTE_DATA 0x06 -#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08 -#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a -#define ACPI_EC_SMB_PRTCL_PROC_CALL 0x0c -#define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL 0x0d -#define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA 0x4a -#define ACPI_EC_SMB_PRTCL_PEC 0x80 - -/* Length of pre/post transaction sleep (msec) */ -#define ACPI_EC_SMB_TRANSACTION_SLEEP 1 -#define ACPI_EC_SMB_ACCESS_SLEEP1 1 -#define ACPI_EC_SMB_ACCESS_SLEEP2 10 - -static int acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data) -{ - u8 val; - int err; - - err = ec_read(smbus->base + address, &val); - if (!err) { - *data = val; - } - xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); - return (err); -} - -static int acpi_ec_smb_write(struct acpi_ec_smbus *smbus, u8 address, u8 data) -{ - int err; - - err = ec_write(smbus->base + address, data); - return (err); -} - -static int -acpi_ec_smb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, - char read_write, u8 command, int size, - union i2c_smbus_data *data) -{ - struct acpi_ec_smbus *smbus = adap->algo_data; - unsigned char protocol, len = 0, pec, temp[2] = { 0, 0 }; - int i; - - if (read_write == I2C_SMBUS_READ) { - protocol = ACPI_EC_SMB_PRTCL_READ; - } else { - protocol = ACPI_EC_SMB_PRTCL_WRITE; - } - pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0; - - switch (size) { - - case I2C_SMBUS_QUICK: - protocol |= ACPI_EC_SMB_PRTCL_QUICK; - read_write = I2C_SMBUS_WRITE; - break; - - case I2C_SMBUS_BYTE: - if (read_write == I2C_SMBUS_WRITE) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); - } - protocol |= ACPI_EC_SMB_PRTCL_BYTE; - break; - - case I2C_SMBUS_BYTE_DATA: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); - } - protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA; - break; - - case I2C_SMBUS_WORD_DATA: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, - data->word >> 8); - } - protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec; - break; - - case I2C_SMBUS_BLOCK_DATA: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - len = min_t(u8, data->block[0], 32); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); - for (i = 0; i < len; i++) - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, - data->block[i + 1]); - } - protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec; - break; - - case I2C_SMBUS_I2C_BLOCK_DATA: - len = min_t(u8, data->block[0], 32); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); - if (read_write == I2C_SMBUS_WRITE) { - for (i = 0; i < len; i++) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, - data->block[i + 1]); - } - } - protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA; - break; - - case I2C_SMBUS_PROC_CALL: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8); - protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; - - case I2C_SMBUS_BLOCK_PROC_CALL: - protocol |= pec; - len = min_t(u8, data->block[0], 31); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); - for (i = 0; i < len; i++) - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, - data->block[i + 1]); - protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; - - default: - ACPI_DEBUG_PRINT((ACPI_DB_WARN, "EC SMBus adapter: " - "Unsupported transaction %d\n", size)); - return (-1); - } - - acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol); - - acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); - - if (~temp[0] & ACPI_EC_SMB_STS_DONE) { - xudelay(500); - acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); - } - if (~temp[0] & ACPI_EC_SMB_STS_DONE) { - xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2); - acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); - } - if ((~temp[0] & ACPI_EC_SMB_STS_DONE) - || (temp[0] & ACPI_EC_SMB_STS_STATUS)) { - return (-1); - } - - if (read_write == I2C_SMBUS_WRITE) { - return (0); - } - - switch (size) { - - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte); - break; - - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0); - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1); - data->word = (temp[1] << 8) | temp[0]; - break; - - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_BLOCK_PROC_CALL: - len = 0; - acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len); - len = min_t(u8, len, 32); - case I2C_SMBUS_I2C_BLOCK_DATA: - for (i = 0; i < len; i++) - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i, - data->block + i + 1); - data->block[0] = len; - break; - } - - return (0); -} - -static u32 acpi_ec_smb_func(struct i2c_adapter *adapter) -{ - - return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA | - I2C_FUNC_SMBUS_PROC_CALL | - I2C_FUNC_SMBUS_BLOCK_PROC_CALL | - I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC); -} - -static const struct i2c_algorithm acpi_ec_smbus_algorithm = { - .smbus_xfer = acpi_ec_smb_access, - .functionality = acpi_ec_smb_func, -}; - -static int acpi_ec_hc_add(struct acpi_device *device) -{ - int status; - unsigned long val; - struct acpi_ec_hc *ec_hc; - struct acpi_ec_smbus *smbus; - - if (!device) { - return -EINVAL; - } - - ec_hc = kzalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL); - if (!ec_hc) { - return -ENOMEM; - } - - smbus = kzalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL); - if (!smbus) { - kfree(ec_hc); - return -ENOMEM; - } - - ec_hc->handle = device->handle; - strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS); - acpi_driver_data(device) = ec_hc; - - status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error obtaining _EC\n")); - kfree(ec_hc); - kfree(smbus); - return -EIO; - } - - smbus->ec = acpi_driver_data(device->parent); - smbus->base = (val & 0xff00ull) >> 8; - smbus->alert = val & 0xffull; - - smbus->adapter.owner = THIS_MODULE; - smbus->adapter.algo = &acpi_ec_smbus_algorithm; - smbus->adapter.algo_data = smbus; - smbus->adapter.dev.parent = &device->dev; - - if (i2c_add_adapter(&smbus->adapter)) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "EC SMBus adapter: Failed to register adapter\n")); - kfree(smbus); - kfree(ec_hc); - return -EIO; - } - - ec_hc->smbus = smbus; - - printk(KERN_INFO PREFIX "%s [%s]\n", - acpi_device_name(device), acpi_device_bid(device)); - - return AE_OK; -} - -static int acpi_ec_hc_remove(struct acpi_device *device, int type) -{ - struct acpi_ec_hc *ec_hc; - - if (!device) { - return -EINVAL; - } - ec_hc = acpi_driver_data(device); - - i2c_del_adapter(&ec_hc->smbus->adapter); - kfree(ec_hc->smbus); - kfree(ec_hc); - - return AE_OK; -} - -static int __init acpi_ec_hc_init(void) -{ - int result; - - result = acpi_bus_register_driver(&acpi_ec_hc_driver); - if (result < 0) { - return -ENODEV; - } - return 0; -} - -static void __exit acpi_ec_hc_exit(void) -{ - acpi_bus_unregister_driver(&acpi_ec_hc_driver); -} - -struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device) -{ - return acpi_driver_data(device->parent); -} - -EXPORT_SYMBOL(acpi_get_ec_hc); - -module_init(acpi_ec_hc_init); -module_exit(acpi_ec_hc_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ducrot Bruno"); -MODULE_DESCRIPTION("ACPI EC SMBus driver"); diff --git a/drivers/acpi/i2c_ec.h b/drivers/acpi/i2c_ec.h deleted file mode 100644 index 7c53fb7..0000000 --- a/drivers/acpi/i2c_ec.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SMBus driver for ACPI Embedded Controller ($Revision: 1.2 $) - * - * Copyright (c) 2002, 2005 Ducrot Bruno - * - * 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 version 2. - */ - -struct acpi_ec_smbus { - struct i2c_adapter adapter; - union acpi_ec *ec; - int base; - int alert; -}; - -struct acpi_ec_hc { - acpi_handle handle; - struct acpi_ec_smbus *smbus; -}; - -struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device); diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c deleted file mode 100644 index dc10966..0000000 --- a/drivers/acpi/ibm_acpi.c +++ /dev/null @@ -1,2798 +0,0 @@ -/* - * ibm_acpi.c - IBM ThinkPad ACPI Extras - * - * - * Copyright (C) 2004-2005 Borislav Deianov - * Copyright (C) 2006 Henrique de Moraes Holschuh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define IBM_VERSION "0.13" - -/* - * Changelog: - * - * 2006-11-22 0.13 new maintainer - * changelog now lives in git commit history, and will - * not be updated further in-file. - * - * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels - * 2005-03-17 0.11 support for 600e, 770x - * thanks to Jamie Lentin - * support for 770e, G41 - * G40 and G41 don't have a thinklight - * temperatures no longer experimental - * experimental brightness control - * experimental volume control - * experimental fan enable/disable - * 2005-01-16 0.10 fix module loading on R30, R31 - * 2005-01-16 0.9 support for 570, R30, R31 - * ultrabay support on A22p, A3x - * limit arg for cmos, led, beep, drop experimental status - * more capable led control on A21e, A22p, T20-22, X20 - * experimental temperatures and fan speed - * experimental embedded controller register dump - * mark more functions as __init, drop incorrect __exit - * use MODULE_VERSION - * thanks to Henrik Brix Andersen - * fix parameter passing on module loading - * thanks to Rusty Russell - * thanks to Jim Radford - * 2004-11-08 0.8 fix init error case, don't return from a macro - * thanks to Chris Wright - * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 - * fix led control on A21e - * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device - * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 - * proc file format changed - * video_switch command - * experimental cmos control - * experimental led control - * experimental acpi sounds - * 2004-09-16 0.4 support for module parameters - * hotkey mask can be prefixed by 0x - * video output switching - * video expansion control - * ultrabay eject support - * removed lcd brightness/on/off control, didn't work - * 2004-08-17 0.3 support for R40 - * lcd off, brightness control - * thinklight on/off - * 2004-08-14 0.2 support for T series, X20 - * bluetooth enable/disable - * hotkey events disabled by default - * removed fan control, currently useless - * 2004-08-09 0.1 initial release, support for X series - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#define IBM_NAME "ibm" -#define IBM_DESC "IBM ThinkPad ACPI Extras" -#define IBM_FILE "ibm_acpi" -#define IBM_URL "http://ibm-acpi.sf.net/" - -MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); -MODULE_DESCRIPTION(IBM_DESC); -MODULE_VERSION(IBM_VERSION); -MODULE_LICENSE("GPL"); - -#define IBM_DIR IBM_NAME - -#define IBM_LOG IBM_FILE ": " -#define IBM_ERR KERN_ERR IBM_LOG -#define IBM_NOTICE KERN_NOTICE IBM_LOG -#define IBM_INFO KERN_INFO IBM_LOG -#define IBM_DEBUG KERN_DEBUG IBM_LOG - -#define IBM_MAX_ACPI_ARGS 3 - -#define __unused __attribute__ ((unused)) - -static int experimental; -module_param(experimental, int, 0); - -static acpi_handle root_handle = NULL; - -#define IBM_HANDLE(object, parent, paths...) \ - static acpi_handle object##_handle; \ - static acpi_handle *object##_parent = &parent##_handle; \ - static char *object##_path; \ - static char *object##_paths[] = { paths } - -IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ - "\\_SB.PCI.ISA.EC", /* 570 */ - "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ - "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ - "\\_SB.PCI0.ICH3.EC0", /* R31 */ - "\\_SB.PCI0.LPC.EC", /* all others */ - ); - -IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ - "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ - "\\_SB.PCI0.VID0", /* 770e */ - "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ - "\\_SB.PCI0.AGP.VID", /* all others */ - ); /* R30, R31 */ - -IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ - -IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ - "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ - "\\CMS", /* R40, R40e */ - ); /* all others */ -#ifdef CONFIG_ACPI_IBM_DOCK -IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ - "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ - "\\_SB.PCI0.PCI1.DOCK", /* all others */ - "\\_SB.PCI.ISA.SLCE", /* 570 */ - ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ -#endif -#ifdef CONFIG_ACPI_IBM_BAY -IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ - "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ - "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ - ); /* A21e, R30, R31 */ - -IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ - "_EJ0", /* all others */ - ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ - -IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ - "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ - ); /* all others */ - -IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ - "_EJ0", /* 770x */ - ); /* all others */ -#endif /* CONFIG_ACPI_IBM_BAY */ - -/* don't list other alternatives as we install a notify handler on the 570 */ -IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ - -IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ - "^HKEY", /* R30, R31 */ - "HKEY", /* all others */ - ); /* 570 */ - -IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ -IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ - -IBM_HANDLE(led, ec, "SLED", /* 570 */ - "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - "LED", /* all others */ - ); /* R30, R31 */ - -IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ -IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ -IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ -IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ - -IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ - "\\FSPD", /* 600e/x, 770e, 770x */ - ); /* all others */ - -IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ - "JFNS", /* 770x-JL */ - ); /* all others */ - -#define IBM_HKEY_HID "IBM0068" -#define IBM_PCI_HID "PNP0A03" - -enum thermal_access_mode { - IBMACPI_THERMAL_NONE = 0, /* No thermal support */ - IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ - IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ - IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ - IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ -}; - -#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ -struct ibm_thermal_sensors_struct { - s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; -}; - -/* - * FAN ACCESS MODES - * - * IBMACPI_FAN_RD_ACPI_GFAN: - * ACPI GFAN method: returns fan level - * - * see IBMACPI_FAN_WR_ACPI_SFAN - * EC 0x2f not available if GFAN exists - * - * IBMACPI_FAN_WR_ACPI_SFAN: - * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) - * - * EC 0x2f might be available *for reading*, but never for writing. - * - * IBMACPI_FAN_WR_TPEC: - * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported - * on almost all ThinkPads - * - * Fan speed changes of any sort (including those caused by the - * disengaged mode) are usually done slowly by the firmware as the - * maximum ammount of fan duty cycle change per second seems to be - * limited. - * - * Reading is not available if GFAN exists. - * Writing is not available if SFAN exists. - * - * Bits - * 7 automatic mode engaged; - * (default operation mode of the ThinkPad) - * fan level is ignored in this mode. - * 6 disengage mode (takes precedence over bit 7); - * not available on all thinkpads. May disable - * the tachometer, and speeds up fan to 100% duty-cycle, - * which speeds it up far above the standard RPM - * levels. It is not impossible that it could cause - * hardware damage. - * 5-3 unused in some models. Extra bits for fan level - * in others, but still useless as all values above - * 7 map to the same speed as level 7 in these models. - * 2-0 fan level (0..7 usually) - * 0x00 = stop - * 0x07 = max (set when temperatures critical) - * Some ThinkPads may have other levels, see - * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41) - * - * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at - * boot. Apparently the EC does not intialize it, so unless ACPI DSDT - * does so, its initial value is meaningless (0x07). - * - * For firmware bugs, refer to: - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues - * - * ---- - * - * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): - * Main fan tachometer reading (in RPM) - * - * This register is present on all ThinkPads with a new-style EC, and - * it is known not to be present on the A21m/e, and T22, as there is - * something else in offset 0x84 according to the ACPI DSDT. Other - * ThinkPads from this same time period (and earlier) probably lack the - * tachometer as well. - * - * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare - * was never fixed by IBM to report the EC firmware version string - * probably support the tachometer (like the early X models), so - * detecting it is quite hard. We need more data to know for sure. - * - * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings - * might result. - * - * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this - * register is not invalidated in ThinkPads that disable tachometer - * readings. Thus, the tachometer readings go stale. - * - * For firmware bugs, refer to: - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues - * - * IBMACPI_FAN_WR_ACPI_FANS: - * ThinkPad X31, X40, X41. Not available in the X60. - * - * FANS ACPI handle: takes three arguments: low speed, medium speed, - * high speed. ACPI DSDT seems to map these three speeds to levels - * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH - * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") - * - * The speeds are stored on handles - * (FANA:FAN9), (FANC:FANB), (FANE:FAND). - * - * There are three default speed sets, acessible as handles: - * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H - * - * ACPI DSDT switches which set is in use depending on various - * factors. - * - * IBMACPI_FAN_WR_TPEC is also available and should be used to - * command the fan. The X31/X40/X41 seems to have 8 fan levels, - * but the ACPI tables just mention level 7. - */ - -enum fan_status_access_mode { - IBMACPI_FAN_NONE = 0, /* No fan status or control */ - IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ - IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ -}; - -enum fan_control_access_mode { - IBMACPI_FAN_WR_NONE = 0, /* No fan control */ - IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ - IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ - IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ -}; - -enum fan_control_commands { - IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ - IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ - IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, - * and also watchdog cmd */ -}; - -enum { /* Fan control constants */ - fan_status_offset = 0x2f, /* EC register 0x2f */ - fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) - * 0x84 must be read before 0x85 */ - - IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer - * disengaged */ - IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan - * control */ -}; - -static char *ibm_thinkpad_ec_found = NULL; - -struct ibm_struct { - char *name; - char param[32]; - - char *hid; - struct acpi_driver *driver; - - int (*init) (void); - int (*read) (char *); - int (*write) (char *); - void (*exit) (void); - - void (*notify) (struct ibm_struct *, u32); - acpi_handle *handle; - int type; - struct acpi_device *device; - - int driver_registered; - int proc_created; - int init_called; - int notify_installed; - - int experimental; -}; - -static struct proc_dir_entry *proc_dir = NULL; - -static struct backlight_device *ibm_backlight_device = NULL; - -#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") -#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") -#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) - -static int acpi_evalf(acpi_handle handle, - void *res, char *method, char *fmt, ...) -{ - char *fmt0 = fmt; - struct acpi_object_list params; - union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; - struct acpi_buffer result, *resultp; - union acpi_object out_obj; - acpi_status status; - va_list ap; - char res_type; - int success; - int quiet; - - if (!*fmt) { - printk(IBM_ERR "acpi_evalf() called with empty format\n"); - return 0; - } - - if (*fmt == 'q') { - quiet = 1; - fmt++; - } else - quiet = 0; - - res_type = *(fmt++); - - params.count = 0; - params.pointer = &in_objs[0]; - - va_start(ap, fmt); - while (*fmt) { - char c = *(fmt++); - switch (c) { - case 'd': /* int */ - in_objs[params.count].integer.value = va_arg(ap, int); - in_objs[params.count++].type = ACPI_TYPE_INTEGER; - break; - /* add more types as needed */ - default: - printk(IBM_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", c); - return 0; - } - } - va_end(ap); - - if (res_type != 'v') { - result.length = sizeof(out_obj); - result.pointer = &out_obj; - resultp = &result; - } else - resultp = NULL; - - status = acpi_evaluate_object(handle, method, ¶ms, resultp); - - switch (res_type) { - case 'd': /* int */ - if (res) - *(int *)res = out_obj.integer.value; - success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; - break; - case 'v': /* void */ - success = status == AE_OK; - break; - /* add more types as needed */ - default: - printk(IBM_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", res_type); - return 0; - } - - if (!success && !quiet) - printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", - method, fmt0, status); - - return success; -} - -static void __unused acpi_print_int(acpi_handle handle, char *method) -{ - int i; - - if (acpi_evalf(handle, &i, method, "d")) - printk(IBM_INFO "%s = 0x%x\n", method, i); - else - printk(IBM_ERR "error calling %s\n", method); -} - -static char *next_cmd(char **cmds) -{ - char *start = *cmds; - char *end; - - while ((end = strchr(start, ',')) && end == start) - start = end + 1; - - if (!end) - return NULL; - - *end = 0; - *cmds = end + 1; - return start; -} - -static int ibm_acpi_driver_init(void) -{ - printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); - printk(IBM_INFO "%s\n", IBM_URL); - - if (ibm_thinkpad_ec_found) - printk(IBM_INFO "ThinkPad EC firmware %s\n", - ibm_thinkpad_ec_found); - - return 0; -} - -static int driver_read(char *p) -{ - int len = 0; - - len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); - len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); - - return len; -} - -static int hotkey_supported; -static int hotkey_mask_supported; -static int hotkey_orig_status; -static int hotkey_orig_mask; - -static int hotkey_get(int *status, int *mask) -{ - if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) - return 0; - - if (hotkey_mask_supported) - if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) - return 0; - - return 1; -} - -static int hotkey_set(int status, int mask) -{ - int i; - - if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) - return 0; - - if (hotkey_mask_supported) - for (i = 0; i < 32; i++) { - int bit = ((1 << i) & mask) != 0; - if (!acpi_evalf(hkey_handle, - NULL, "MHKM", "vdd", i + 1, bit)) - return 0; - } - - return 1; -} - -static int hotkey_init(void) -{ - /* hotkey not supported on 570 */ - hotkey_supported = hkey_handle != NULL; - - if (hotkey_supported) { - /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - A30, R30, R31, T20-22, X20-21, X22-24 */ - hotkey_mask_supported = - acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); - - if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask)) - return -ENODEV; - } - - return 0; -} - -static int hotkey_read(char *p) -{ - int status, mask; - int len = 0; - - if (!hotkey_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - - if (!hotkey_get(&status, &mask)) - return -EIO; - - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); - if (hotkey_mask_supported) { - len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); - len += sprintf(p + len, - "commands:\tenable, disable, reset, \n"); - } else { - len += sprintf(p + len, "mask:\t\tnot supported\n"); - len += sprintf(p + len, "commands:\tenable, disable, reset\n"); - } - - return len; -} - -static int hotkey_write(char *buf) -{ - int status, mask; - char *cmd; - int do_cmd = 0; - - if (!hotkey_supported) - return -ENODEV; - - if (!hotkey_get(&status, &mask)) - return -EIO; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status = 1; - } else if (strlencmp(cmd, "disable") == 0) { - status = 0; - } else if (strlencmp(cmd, "reset") == 0) { - status = hotkey_orig_status; - mask = hotkey_orig_mask; - } else if (sscanf(cmd, "0x%x", &mask) == 1) { - /* mask set */ - } else if (sscanf(cmd, "%x", &mask) == 1) { - /* mask set */ - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !hotkey_set(status, mask)) - return -EIO; - - return 0; -} - -static void hotkey_exit(void) -{ - if (hotkey_supported) - hotkey_set(hotkey_orig_status, hotkey_orig_mask); -} - -static void hotkey_notify(struct ibm_struct *ibm, u32 event) -{ - int hkey; - - if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) - acpi_bus_generate_event(ibm->device, event, hkey); - else { - printk(IBM_ERR "unknown hotkey event %d\n", event); - acpi_bus_generate_event(ibm->device, event, 0); - } -} - -static int bluetooth_supported; - -static int bluetooth_init(void) -{ - /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ - bluetooth_supported = hkey_handle && - acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); - - return 0; -} - -static int bluetooth_status(void) -{ - int status; - - if (!bluetooth_supported || - !acpi_evalf(hkey_handle, &status, "GBDC", "d")) - status = 0; - - return status; -} - -static int bluetooth_read(char *p) -{ - int len = 0; - int status = bluetooth_status(); - - if (!bluetooth_supported) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!(status & 1)) - len += sprintf(p + len, "status:\t\tnot installed\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; -} - -static int bluetooth_write(char *buf) -{ - int status = bluetooth_status(); - char *cmd; - int do_cmd = 0; - - if (!bluetooth_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status |= 2; - } else if (strlencmp(cmd, "disable") == 0) { - status &= ~2; - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) - return -EIO; - - return 0; -} - -static int wan_supported; - -static int wan_init(void) -{ - wan_supported = hkey_handle && - acpi_evalf(hkey_handle, NULL, "GWAN", "qv"); - - return 0; -} - -static int wan_status(void) -{ - int status; - - if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d")) - status = 0; - - return status; -} - -static int wan_read(char *p) -{ - int len = 0; - int status = wan_status(); - - if (!wan_supported) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!(status & 1)) - len += sprintf(p + len, "status:\t\tnot installed\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; -} - -static int wan_write(char *buf) -{ - int status = wan_status(); - char *cmd; - int do_cmd = 0; - - if (!wan_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status |= 2; - } else if (strlencmp(cmd, "disable") == 0) { - status &= ~2; - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) - return -EIO; - - return 0; -} - -enum video_access_mode { - IBMACPI_VIDEO_NONE = 0, - IBMACPI_VIDEO_570, /* 570 */ - IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */ - IBMACPI_VIDEO_NEW, /* all others */ -}; - -static enum video_access_mode video_supported; -static int video_orig_autosw; - -static int video_init(void) -{ - int ivga; - - if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) - /* G41, assume IVGA doesn't change */ - vid_handle = vid2_handle; - - if (!vid_handle) - /* video switching not supported on R30, R31 */ - video_supported = IBMACPI_VIDEO_NONE; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) - /* 570 */ - video_supported = IBMACPI_VIDEO_570; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) - /* 600e/x, 770e, 770x */ - video_supported = IBMACPI_VIDEO_770; - else - /* all others */ - video_supported = IBMACPI_VIDEO_NEW; - - return 0; -} - -static int video_status(void) -{ - int status = 0; - int i; - - if (video_supported == IBMACPI_VIDEO_570) { - if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) - status = i & 3; - } else if (video_supported == IBMACPI_VIDEO_770) { - if (acpi_evalf(NULL, &i, "\\VCDL", "d")) - status |= 0x01 * i; - if (acpi_evalf(NULL, &i, "\\VCDC", "d")) - status |= 0x02 * i; - } else if (video_supported == IBMACPI_VIDEO_NEW) { - acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); - if (acpi_evalf(NULL, &i, "\\VCDC", "d")) - status |= 0x02 * i; - - acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); - if (acpi_evalf(NULL, &i, "\\VCDL", "d")) - status |= 0x01 * i; - if (acpi_evalf(NULL, &i, "\\VCDD", "d")) - status |= 0x08 * i; - } - - return status; -} - -static int video_autosw(void) -{ - int autosw = 0; - - if (video_supported == IBMACPI_VIDEO_570) - acpi_evalf(vid_handle, &autosw, "SWIT", "d"); - else if (video_supported == IBMACPI_VIDEO_770 || - video_supported == IBMACPI_VIDEO_NEW) - acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); - - return autosw & 1; -} - -static int video_read(char *p) -{ - int status = video_status(); - int autosw = video_autosw(); - int len = 0; - - if (!video_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); - len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); - if (video_supported == IBMACPI_VIDEO_NEW) - len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); - len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); - len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); - len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); - if (video_supported == IBMACPI_VIDEO_NEW) - len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); - len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); - len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); - - return len; -} - -static int video_switch(void) -{ - int autosw = video_autosw(); - int ret; - - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - ret = video_supported == IBMACPI_VIDEO_570 ? - acpi_evalf(ec_handle, NULL, "_Q16", "v") : - acpi_evalf(vid_handle, NULL, "VSWT", "v"); - acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); - - return ret; -} - -static int video_expand(void) -{ - if (video_supported == IBMACPI_VIDEO_570) - return acpi_evalf(ec_handle, NULL, "_Q17", "v"); - else if (video_supported == IBMACPI_VIDEO_770) - return acpi_evalf(vid_handle, NULL, "VEXP", "v"); - else - return acpi_evalf(NULL, NULL, "\\VEXP", "v"); -} - -static int video_switch2(int status) -{ - int ret; - - if (video_supported == IBMACPI_VIDEO_570) { - ret = acpi_evalf(NULL, NULL, - "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); - } else if (video_supported == IBMACPI_VIDEO_770) { - int autosw = video_autosw(); - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - - ret = acpi_evalf(vid_handle, NULL, - "ASWT", "vdd", status * 0x100, 0); - - acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); - } else { - ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && - acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); - } - - return ret; -} - -static int video_write(char *buf) -{ - char *cmd; - int enable, disable, status; - - if (!video_supported) - return -ENODEV; - - enable = disable = 0; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "lcd_enable") == 0) { - enable |= 0x01; - } else if (strlencmp(cmd, "lcd_disable") == 0) { - disable |= 0x01; - } else if (strlencmp(cmd, "crt_enable") == 0) { - enable |= 0x02; - } else if (strlencmp(cmd, "crt_disable") == 0) { - disable |= 0x02; - } else if (video_supported == IBMACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_enable") == 0) { - enable |= 0x08; - } else if (video_supported == IBMACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_disable") == 0) { - disable |= 0x08; - } else if (strlencmp(cmd, "auto_enable") == 0) { - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - } else if (strlencmp(cmd, "auto_disable") == 0) { - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) - return -EIO; - } else if (strlencmp(cmd, "video_switch") == 0) { - if (!video_switch()) - return -EIO; - } else if (strlencmp(cmd, "expand_toggle") == 0) { - if (!video_expand()) - return -EIO; - } else - return -EINVAL; - } - - if (enable || disable) { - status = (video_status() & 0x0f & ~disable) | enable; - if (!video_switch2(status)) - return -EIO; - } - - return 0; -} - -static void video_exit(void) -{ - acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw); -} - -static int light_supported; -static int light_status_supported; - -static int light_init(void) -{ - /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ - light_supported = (cmos_handle || lght_handle) && !ledb_handle; - - if (light_supported) - /* light status not supported on - 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ - light_status_supported = acpi_evalf(ec_handle, NULL, - "KBLT", "qv"); - - return 0; -} - -static int light_read(char *p) -{ - int len = 0; - int status = 0; - - if (!light_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - } else if (!light_status_supported) { - len += sprintf(p + len, "status:\t\tunknown\n"); - len += sprintf(p + len, "commands:\ton, off\n"); - } else { - if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) - return -EIO; - len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); - len += sprintf(p + len, "commands:\ton, off\n"); - } - - return len; -} - -static int light_write(char *buf) -{ - int cmos_cmd, lght_cmd; - char *cmd; - int success; - - if (!light_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "on") == 0) { - cmos_cmd = 0x0c; - lght_cmd = 1; - } else if (strlencmp(cmd, "off") == 0) { - cmos_cmd = 0x0d; - lght_cmd = 0; - } else - return -EINVAL; - - success = cmos_handle ? - acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : - acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); - if (!success) - return -EIO; - } - - return 0; -} - -#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY) -static int _sta(acpi_handle handle) -{ - int status; - - if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) - status = 0; - - return status; -} -#endif - -#ifdef CONFIG_ACPI_IBM_DOCK -#define dock_docked() (_sta(dock_handle) & 1) - -static int dock_read(char *p) -{ - int len = 0; - int docked = dock_docked(); - - if (!dock_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!docked) - len += sprintf(p + len, "status:\t\tundocked\n"); - else { - len += sprintf(p + len, "status:\t\tdocked\n"); - len += sprintf(p + len, "commands:\tdock, undock\n"); - } - - return len; -} - -static int dock_write(char *buf) -{ - char *cmd; - - if (!dock_docked()) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "undock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || - !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) - return -EIO; - } else if (strlencmp(cmd, "dock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static void dock_notify(struct ibm_struct *ibm, u32 event) -{ - int docked = dock_docked(); - int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID); - - if (event == 1 && !pci) /* 570 */ - acpi_bus_generate_event(ibm->device, event, 1); /* button */ - else if (event == 1 && pci) /* 570 */ - acpi_bus_generate_event(ibm->device, event, 3); /* dock */ - else if (event == 3 && docked) - acpi_bus_generate_event(ibm->device, event, 1); /* button */ - else if (event == 3 && !docked) - acpi_bus_generate_event(ibm->device, event, 2); /* undock */ - else if (event == 0 && docked) - acpi_bus_generate_event(ibm->device, event, 3); /* dock */ - else { - printk(IBM_ERR "unknown dock event %d, status %d\n", - event, _sta(dock_handle)); - acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ - } -} -#endif - -#ifdef CONFIG_ACPI_IBM_BAY -static int bay_status_supported; -static int bay_status2_supported; -static int bay_eject_supported; -static int bay_eject2_supported; - -static int bay_init(void) -{ - bay_status_supported = bay_handle && - acpi_evalf(bay_handle, NULL, "_STA", "qv"); - bay_status2_supported = bay2_handle && - acpi_evalf(bay2_handle, NULL, "_STA", "qv"); - - bay_eject_supported = bay_handle && bay_ej_handle && - (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); - bay_eject2_supported = bay2_handle && bay2_ej_handle && - (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); - - return 0; -} - -#define bay_occupied(b) (_sta(b##_handle) & 1) - -static int bay_read(char *p) -{ - int len = 0; - int occupied = bay_occupied(bay); - int occupied2 = bay_occupied(bay2); - int eject, eject2; - - len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ? - (occupied ? "occupied" : "unoccupied") : - "not supported"); - if (bay_status2_supported) - len += sprintf(p + len, "status2:\t%s\n", occupied2 ? - "occupied" : "unoccupied"); - - eject = bay_eject_supported && occupied; - eject2 = bay_eject2_supported && occupied2; - - if (eject && eject2) - len += sprintf(p + len, "commands:\teject, eject2\n"); - else if (eject) - len += sprintf(p + len, "commands:\teject\n"); - else if (eject2) - len += sprintf(p + len, "commands:\teject2\n"); - - return len; -} - -static int bay_write(char *buf) -{ - char *cmd; - - if (!bay_eject_supported && !bay_eject2_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (bay_eject_supported && strlencmp(cmd, "eject") == 0) { - if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else if (bay_eject2_supported && - strlencmp(cmd, "eject2") == 0) { - if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static void bay_notify(struct ibm_struct *ibm, u32 event) -{ - acpi_bus_generate_event(ibm->device, event, 0); -} -#endif /* CONFIG_ACPI_IBM_BAY */ - -static int cmos_read(char *p) -{ - int len = 0; - - /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - R30, R31, T20-22, X20-21 */ - if (!cmos_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t ( is 0-21)\n"); - } - - return len; -} - -static int cmos_eval(int cmos_cmd) -{ - if (cmos_handle) - return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd); - else - return 1; -} - -static int cmos_write(char *buf) -{ - char *cmd; - int cmos_cmd; - - if (!cmos_handle) - return -EINVAL; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%u", &cmos_cmd) == 1 && - cmos_cmd >= 0 && cmos_cmd <= 21) { - /* cmos_cmd set */ - } else - return -EINVAL; - - if (!cmos_eval(cmos_cmd)) - return -EIO; - } - - return 0; -} - -enum led_access_mode { - IBMACPI_LED_NONE = 0, - IBMACPI_LED_570, /* 570 */ - IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - IBMACPI_LED_NEW, /* all others */ -}; -static enum led_access_mode led_supported; - -static int led_init(void) -{ - if (!led_handle) - /* led not supported on R30, R31 */ - led_supported = IBMACPI_LED_NONE; - else if (strlencmp(led_path, "SLED") == 0) - /* 570 */ - led_supported = IBMACPI_LED_570; - else if (strlencmp(led_path, "SYSL") == 0) - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - led_supported = IBMACPI_LED_OLD; - else - /* all others */ - led_supported = IBMACPI_LED_NEW; - - return 0; -} - -#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) - -static int led_read(char *p) -{ - int len = 0; - - if (!led_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - len += sprintf(p + len, "status:\t\tsupported\n"); - - if (led_supported == IBMACPI_LED_570) { - /* 570 */ - int i, status; - for (i = 0; i < 8; i++) { - if (!acpi_evalf(ec_handle, - &status, "GLED", "dd", 1 << i)) - return -EIO; - len += sprintf(p + len, "%d:\t\t%s\n", - i, led_status(status)); - } - } - - len += sprintf(p + len, "commands:\t" - " on, off, blink ( is 0-7)\n"); - - return len; -} - -/* off, on, blink */ -static const int led_sled_arg1[] = { 0, 1, 3 }; -static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ -static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ -static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; - -#define EC_HLCL 0x0c -#define EC_HLBL 0x0d -#define EC_HLMS 0x0e - -static int led_write(char *buf) -{ - char *cmd; - int led, ind, ret; - - if (!led_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) - return -EINVAL; - - if (strstr(cmd, "off")) { - ind = 0; - } else if (strstr(cmd, "on")) { - ind = 1; - } else if (strstr(cmd, "blink")) { - ind = 2; - } else - return -EINVAL; - - if (led_supported == IBMACPI_LED_570) { - /* 570 */ - led = 1 << led; - if (!acpi_evalf(led_handle, NULL, NULL, "vdd", - led, led_sled_arg1[ind])) - return -EIO; - } else if (led_supported == IBMACPI_LED_OLD) { - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ - led = 1 << led; - ret = ec_write(EC_HLMS, led); - if (ret >= 0) - ret = - ec_write(EC_HLBL, led * led_exp_hlbl[ind]); - if (ret >= 0) - ret = - ec_write(EC_HLCL, led * led_exp_hlcl[ind]); - if (ret < 0) - return ret; - } else { - /* all others */ - if (!acpi_evalf(led_handle, NULL, NULL, "vdd", - led, led_led_arg1[ind])) - return -EIO; - } - } - - return 0; -} - -static int beep_read(char *p) -{ - int len = 0; - - if (!beep_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t ( is 0-17)\n"); - } - - return len; -} - -static int beep_write(char *buf) -{ - char *cmd; - int beep_cmd; - - if (!beep_handle) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%u", &beep_cmd) == 1 && - beep_cmd >= 0 && beep_cmd <= 17) { - /* beep_cmd set */ - } else - return -EINVAL; - if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) - return -EIO; - } - - return 0; -} - -static int acpi_ec_read(int i, u8 * p) -{ - int v; - - if (ecrd_handle) { - if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) - return 0; - *p = v; - } else { - if (ec_read(i, p) < 0) - return 0; - } - - return 1; -} - -static int acpi_ec_write(int i, u8 v) -{ - if (ecwr_handle) { - if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) - return 0; - } else { - if (ec_write(i, v) < 0) - return 0; - } - - return 1; -} - -static enum thermal_access_mode thermal_read_mode; - -static int thermal_init(void) -{ - u8 t, ta1, ta2; - int i; - int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - - if (ibm_thinkpad_ec_found && experimental) { - /* - * Direct EC access mode: sensors at registers - * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for - * non-implemented, thermal sensors return 0x80 when - * not available - */ - - ta1 = ta2 = 0; - for (i = 0; i < 8; i++) { - if (likely(acpi_ec_read(0x78 + i, &t))) { - ta1 |= t; - } else { - ta1 = 0; - break; - } - if (likely(acpi_ec_read(0xC0 + i, &t))) { - ta2 |= t; - } else { - ta1 = 0; - break; - } - } - if (ta1 == 0) { - /* This is sheer paranoia, but we handle it anyway */ - if (acpi_tmp7) { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "falling back to ACPI TMPx access mode\n"); - thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; - } else { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "disabling thermal sensors access\n"); - thermal_read_mode = IBMACPI_THERMAL_NONE; - } - } else { - thermal_read_mode = - (ta2 != 0) ? - IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; - } - } else if (acpi_tmp7) { - if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { - /* 600e/x, 770e, 770x */ - thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; - } else { - /* Standard ACPI TMPx access, max 8 sensors */ - thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; - } - } else { - /* temperatures not supported on 570, G4x, R30, R31, R32 */ - thermal_read_mode = IBMACPI_THERMAL_NONE; - } - - return 0; -} - -static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) -{ - int i, t; - s8 tmp; - char tmpi[] = "TMPi"; - - if (!s) - return -EINVAL; - - switch (thermal_read_mode) { -#if IBMACPI_MAX_THERMAL_SENSORS >= 16 - case IBMACPI_THERMAL_TPEC_16: - for (i = 0; i < 8; i++) { - if (!acpi_ec_read(0xC0 + i, &tmp)) - return -EIO; - s->temp[i + 8] = tmp * 1000; - } - /* fallthrough */ -#endif - case IBMACPI_THERMAL_TPEC_8: - for (i = 0; i < 8; i++) { - if (!acpi_ec_read(0x78 + i, &tmp)) - return -EIO; - s->temp[i] = tmp * 1000; - } - return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; - - case IBMACPI_THERMAL_ACPI_UPDT: - if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) - return -EIO; - for (i = 0; i < 8; i++) { - tmpi[3] = '0' + i; - if (!acpi_evalf(ec_handle, &t, tmpi, "d")) - return -EIO; - s->temp[i] = (t - 2732) * 100; - } - return 8; - - case IBMACPI_THERMAL_ACPI_TMP07: - for (i = 0; i < 8; i++) { - tmpi[3] = '0' + i; - if (!acpi_evalf(ec_handle, &t, tmpi, "d")) - return -EIO; - s->temp[i] = t * 1000; - } - return 8; - - case IBMACPI_THERMAL_NONE: - default: - return 0; - } -} - -static int thermal_read(char *p) -{ - int len = 0; - int n, i; - struct ibm_thermal_sensors_struct t; - - n = thermal_get_sensors(&t); - if (unlikely(n < 0)) - return n; - - len += sprintf(p + len, "temperatures:\t"); - - if (n > 0) { - for (i = 0; i < (n - 1); i++) - len += sprintf(p + len, "%d ", t.temp[i] / 1000); - len += sprintf(p + len, "%d\n", t.temp[i] / 1000); - } else - len += sprintf(p + len, "not supported\n"); - - return len; -} - -static u8 ecdump_regs[256]; - -static int ecdump_read(char *p) -{ - int len = 0; - int i, j; - u8 v; - - len += sprintf(p + len, "EC " - " +00 +01 +02 +03 +04 +05 +06 +07" - " +08 +09 +0a +0b +0c +0d +0e +0f\n"); - for (i = 0; i < 256; i += 16) { - len += sprintf(p + len, "EC 0x%02x:", i); - for (j = 0; j < 16; j++) { - if (!acpi_ec_read(i + j, &v)) - break; - if (v != ecdump_regs[i + j]) - len += sprintf(p + len, " *%02x", v); - else - len += sprintf(p + len, " %02x", v); - ecdump_regs[i + j] = v; - } - len += sprintf(p + len, "\n"); - if (j != 16) - break; - } - - /* These are way too dangerous to advertise openly... */ -#if 0 - len += sprintf(p + len, "commands:\t0x 0x" - " ( is 00-ff, is 00-ff)\n"); - len += sprintf(p + len, "commands:\t0x " - " ( is 00-ff, is 0-255)\n"); -#endif - return len; -} - -static int ecdump_write(char *buf) -{ - char *cmd; - int i, v; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { - /* i and v set */ - } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { - /* i and v set */ - } else - return -EINVAL; - if (i >= 0 && i < 256 && v >= 0 && v < 256) { - if (!acpi_ec_write(i, v)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static int brightness_offset = 0x31; - -static int brightness_get(struct backlight_device *bd) -{ - u8 level; - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; - - level &= 0x7; - - return level; -} - -static int brightness_read(char *p) -{ - int len = 0; - int level; - - if ((level = brightness_get(NULL)) < 0) { - len += sprintf(p + len, "level:\t\tunreadable\n"); - } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); - len += sprintf(p + len, "commands:\tup, down\n"); - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-7)\n"); - } - - return len; -} - -#define BRIGHTNESS_UP 4 -#define BRIGHTNESS_DOWN 5 - -static int brightness_set(int value) -{ - int cmos_cmd, inc, i; - int current_value = brightness_get(NULL); - - value &= 7; - - cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; - inc = value > current_value ? 1 : -1; - for (i = current_value; i != value; i += inc) { - if (!cmos_eval(cmos_cmd)) - return -EIO; - if (!acpi_ec_write(brightness_offset, i + inc)) - return -EIO; - } - - return 0; -} - -static int brightness_write(char *buf) -{ - int level; - int new_level; - char *cmd; - - while ((cmd = next_cmd(&buf))) { - if ((level = brightness_get(NULL)) < 0) - return level; - level &= 7; - - if (strlencmp(cmd, "up") == 0) { - new_level = level == 7 ? 7 : level + 1; - } else if (strlencmp(cmd, "down") == 0) { - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 7) { - /* new_level set */ - } else - return -EINVAL; - - brightness_set(new_level); - } - - return 0; -} - -static int brightness_update_status(struct backlight_device *bd) -{ - return brightness_set( - (bd->props.fb_blank == FB_BLANK_UNBLANK && - bd->props.power == FB_BLANK_UNBLANK) ? - bd->props.brightness : 0); -} - -static struct backlight_ops ibm_backlight_data = { - .get_brightness = brightness_get, - .update_status = brightness_update_status, -}; - -static int brightness_init(void) -{ - int b; - - b = brightness_get(NULL); - if (b < 0) - return b; - - ibm_backlight_device = backlight_device_register("ibm", NULL, NULL, - &ibm_backlight_data); - if (IS_ERR(ibm_backlight_device)) { - printk(IBM_ERR "Could not register backlight device\n"); - return PTR_ERR(ibm_backlight_device); - } - - ibm_backlight_device->props.max_brightness = 7; - ibm_backlight_device->props.brightness = b; - backlight_update_status(ibm_backlight_device); - - return 0; -} - -static void brightness_exit(void) -{ - if (ibm_backlight_device) { - backlight_device_unregister(ibm_backlight_device); - ibm_backlight_device = NULL; - } -} - -static int volume_offset = 0x30; - -static int volume_read(char *p) -{ - int len = 0; - u8 level; - - if (!acpi_ec_read(volume_offset, &level)) { - len += sprintf(p + len, "level:\t\tunreadable\n"); - } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); - len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); - len += sprintf(p + len, "commands:\tup, down, mute\n"); - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-15)\n"); - } - - return len; -} - -#define VOLUME_DOWN 0 -#define VOLUME_UP 1 -#define VOLUME_MUTE 2 - -static int volume_write(char *buf) -{ - int cmos_cmd, inc, i; - u8 level, mute; - int new_level, new_mute; - char *cmd; - - while ((cmd = next_cmd(&buf))) { - if (!acpi_ec_read(volume_offset, &level)) - return -EIO; - new_mute = mute = level & 0x40; - new_level = level = level & 0xf; - - if (strlencmp(cmd, "up") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 15 ? 15 : level + 1; - } else if (strlencmp(cmd, "down") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 15) { - /* new_level set */ - } else if (strlencmp(cmd, "mute") == 0) { - new_mute = 0x40; - } else - return -EINVAL; - - if (new_level != level) { /* mute doesn't change */ - cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN; - inc = new_level > level ? 1 : -1; - - if (mute && (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, level))) - return -EIO; - - for (i = level; i != new_level; i += inc) - if (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, i + inc)) - return -EIO; - - if (mute && (!cmos_eval(VOLUME_MUTE) || - !acpi_ec_write(volume_offset, - new_level + mute))) - return -EIO; - } - - if (new_mute != mute) { /* level doesn't change */ - cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP; - - if (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, level + new_mute)) - return -EIO; - } - } - - return 0; -} - -static enum fan_status_access_mode fan_status_access_mode; -static enum fan_control_access_mode fan_control_access_mode; -static enum fan_control_commands fan_control_commands; - -static int fan_control_status_known; -static u8 fan_control_initial_status; - -static void fan_watchdog_fire(struct work_struct *ignored); -static int fan_watchdog_maxinterval; -static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire); - -static int fan_init(void) -{ - fan_status_access_mode = IBMACPI_FAN_NONE; - fan_control_access_mode = IBMACPI_FAN_WR_NONE; - fan_control_commands = 0; - fan_control_status_known = 1; - fan_watchdog_maxinterval = 0; - - if (gfan_handle) { - /* 570, 600e/x, 770e, 770x */ - fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN; - } else { - /* all other ThinkPads: note that even old-style - * ThinkPad ECs supports the fan control register */ - if (likely(acpi_ec_read(fan_status_offset, - &fan_control_initial_status))) { - fan_status_access_mode = IBMACPI_FAN_RD_TPEC; - - /* In some ThinkPads, neither the EC nor the ACPI - * DSDT initialize the fan status, and it ends up - * being set to 0x07 when it *could* be either - * 0x07 or 0x80. - * - * Enable for TP-1Y (T43), TP-78 (R51e), - * TP-76 (R52), TP-70 (T43, R52), which are known - * to be buggy. */ - if (fan_control_initial_status == 0x07 && - ibm_thinkpad_ec_found && - ((ibm_thinkpad_ec_found[0] == '1' && - ibm_thinkpad_ec_found[1] == 'Y') || - (ibm_thinkpad_ec_found[0] == '7' && - (ibm_thinkpad_ec_found[1] == '6' || - ibm_thinkpad_ec_found[1] == '8' || - ibm_thinkpad_ec_found[1] == '0')) - )) { - printk(IBM_NOTICE - "fan_init: initial fan status is " - "unknown, assuming it is in auto " - "mode\n"); - fan_control_status_known = 0; - } - } else { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "fan status and control unavailable\n"); - return 0; - } - } - - if (sfan_handle) { - /* 570, 770x-JL */ - fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; - fan_control_commands |= - IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; - } else { - if (!gfan_handle) { - /* gfan without sfan means no fan control */ - /* all other models implement TP EC 0x2f control */ - - if (fans_handle) { - /* X31, X40, X41 */ - fan_control_access_mode = - IBMACPI_FAN_WR_ACPI_FANS; - fan_control_commands |= - IBMACPI_FAN_CMD_SPEED | - IBMACPI_FAN_CMD_LEVEL | - IBMACPI_FAN_CMD_ENABLE; - } else { - fan_control_access_mode = IBMACPI_FAN_WR_TPEC; - fan_control_commands |= - IBMACPI_FAN_CMD_LEVEL | - IBMACPI_FAN_CMD_ENABLE; - } - } - } - - return 0; -} - -static int fan_get_status(u8 *status) -{ - u8 s; - - /* TODO: - * Add IBMACPI_FAN_RD_ACPI_FANS ? */ - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_ACPI_GFAN: - /* 570, 600e/x, 770e, 770x */ - - if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) - return -EIO; - - if (likely(status)) - *status = s & 0x07; - - break; - - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_status_offset, &s))) - return -EIO; - - if (likely(status)) - *status = s; - - break; - - default: - return -ENXIO; - } - - return 0; -} - -static int fan_get_speed(unsigned int *speed) -{ - u8 hi, lo; - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || - !acpi_ec_read(fan_rpm_offset + 1, &hi))) - return -EIO; - - if (likely(speed)) - *speed = (hi << 8) | lo; - - break; - - default: - return -ENXIO; - } - - return 0; -} - -static void fan_exit(void) -{ - cancel_delayed_work(&fan_watchdog_task); - flush_scheduled_work(); -} - -static void fan_watchdog_reset(void) -{ - static int fan_watchdog_active = 0; - - if (fan_watchdog_active) - cancel_delayed_work(&fan_watchdog_task); - - if (fan_watchdog_maxinterval > 0) { - fan_watchdog_active = 1; - if (!schedule_delayed_work(&fan_watchdog_task, - msecs_to_jiffies(fan_watchdog_maxinterval - * 1000))) { - printk(IBM_ERR "failed to schedule the fan watchdog, " - "watchdog will not trigger\n"); - } - } else - fan_watchdog_active = 0; -} - -static int fan_read(char *p) -{ - int len = 0; - int rc; - u8 status; - unsigned int speed = 0; - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_ACPI_GFAN: - /* 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) - return rc; - - len += sprintf(p + len, "status:\t\t%s\n" - "level:\t\t%d\n", - (status != 0) ? "enabled" : "disabled", status); - break; - - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) - return rc; - - if (unlikely(!fan_control_status_known)) { - if (status != fan_control_initial_status) - fan_control_status_known = 1; - else - /* Return most likely status. In fact, it - * might be the only possible status */ - status = IBMACPI_FAN_EC_AUTO; - } - - len += sprintf(p + len, "status:\t\t%s\n", - (status != 0) ? "enabled" : "disabled"); - - /* No ThinkPad boots on disengaged mode, we can safely - * assume the tachometer is online if fan control status - * was unknown */ - if ((rc = fan_get_speed(&speed)) < 0) - return rc; - - len += sprintf(p + len, "speed:\t\t%d\n", speed); - - if (status & IBMACPI_FAN_EC_DISENGAGED) - /* Disengaged mode takes precedence */ - len += sprintf(p + len, "level:\t\tdisengaged\n"); - else if (status & IBMACPI_FAN_EC_AUTO) - len += sprintf(p + len, "level:\t\tauto\n"); - else - len += sprintf(p + len, "level:\t\t%d\n", status); - break; - - case IBMACPI_FAN_NONE: - default: - len += sprintf(p + len, "status:\t\tnot supported\n"); - } - - if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { - len += sprintf(p + len, "commands:\tlevel "); - - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_SFAN: - len += sprintf(p + len, " ( is 0-7)\n"); - break; - - default: - len += sprintf(p + len, " ( is 0-7, " - "auto, disengaged)\n"); - break; - } - } - - if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) - len += sprintf(p + len, "commands:\tenable, disable\n" - "commands:\twatchdog ( is 0 (off), " - "1-120 (seconds))\n"); - - if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) - len += sprintf(p + len, "commands:\tspeed " - " ( is 0-65535)\n"); - - return len; -} - -static int fan_set_level(int level) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_SFAN: - if (level >= 0 && level <= 7) { - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) - return -EIO; - } else - return -EINVAL; - break; - - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if ((level != IBMACPI_FAN_EC_AUTO) && - (level != IBMACPI_FAN_EC_DISENGAGED) && - ((level < 0) || (level > 7))) - return -EINVAL; - - if (!acpi_ec_write(fan_status_offset, level)) - return -EIO; - else - fan_control_status_known = 1; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_enable(void) -{ - u8 s; - int rc; - - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if ((rc = fan_get_status(&s)) < 0) - return rc; - - /* Don't go out of emergency fan mode */ - if (s != 7) - s = IBMACPI_FAN_EC_AUTO; - - if (!acpi_ec_write(fan_status_offset, s)) - return -EIO; - else - fan_control_status_known = 1; - break; - - case IBMACPI_FAN_WR_ACPI_SFAN: - if ((rc = fan_get_status(&s)) < 0) - return rc; - - s &= 0x07; - - /* Set fan to at least level 4 */ - if (s < 4) - s = 4; - - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) - return -EIO; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_disable(void) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if (!acpi_ec_write(fan_status_offset, 0x00)) - return -EIO; - else - fan_control_status_known = 1; - break; - - case IBMACPI_FAN_WR_ACPI_SFAN: - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) - return -EIO; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_speed(int speed) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - if (speed >= 0 && speed <= 65535) { - if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", - speed, speed, speed)) - return -EIO; - } else - return -EINVAL; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_write_cmd_level(const char *cmd, int *rc) -{ - int level; - - if (strlencmp(cmd, "level auto") == 0) - level = IBMACPI_FAN_EC_AUTO; - else if (strlencmp(cmd, "level disengaged") == 0) - level = IBMACPI_FAN_EC_DISENGAGED; - else if (sscanf(cmd, "level %d", &level) != 1) - return 0; - - if ((*rc = fan_set_level(level)) == -ENXIO) - printk(IBM_ERR "level command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_enable(const char *cmd, int *rc) -{ - if (strlencmp(cmd, "enable") != 0) - return 0; - - if ((*rc = fan_set_enable()) == -ENXIO) - printk(IBM_ERR "enable command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_disable(const char *cmd, int *rc) -{ - if (strlencmp(cmd, "disable") != 0) - return 0; - - if ((*rc = fan_set_disable()) == -ENXIO) - printk(IBM_ERR "disable command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_speed(const char *cmd, int *rc) -{ - int speed; - - /* TODO: - * Support speed ? */ - - if (sscanf(cmd, "speed %d", &speed) != 1) - return 0; - - if ((*rc = fan_set_speed(speed)) == -ENXIO) - printk(IBM_ERR "speed command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_watchdog(const char *cmd, int *rc) -{ - int interval; - - if (sscanf(cmd, "watchdog %d", &interval) != 1) - return 0; - - if (interval < 0 || interval > 120) - *rc = -EINVAL; - else - fan_watchdog_maxinterval = interval; - - return 1; -} - -static int fan_write(char *buf) -{ - char *cmd; - int rc = 0; - - while (!rc && (cmd = next_cmd(&buf))) { - if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) && - fan_write_cmd_level(cmd, &rc)) && - !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && - (fan_write_cmd_enable(cmd, &rc) || - fan_write_cmd_disable(cmd, &rc) || - fan_write_cmd_watchdog(cmd, &rc))) && - !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && - fan_write_cmd_speed(cmd, &rc)) - ) - rc = -EINVAL; - else if (!rc) - fan_watchdog_reset(); - } - - return rc; -} - -static void fan_watchdog_fire(struct work_struct *ignored) -{ - printk(IBM_NOTICE "fan watchdog: enabling fan\n"); - if (fan_set_enable()) { - printk(IBM_ERR "fan watchdog: error while enabling fan\n"); - /* reschedule for later */ - fan_watchdog_reset(); - } -} - -static struct ibm_struct ibms[] = { - { - .name = "driver", - .init = ibm_acpi_driver_init, - .read = driver_read, - }, - { - .name = "hotkey", - .hid = IBM_HKEY_HID, - .init = hotkey_init, - .read = hotkey_read, - .write = hotkey_write, - .exit = hotkey_exit, - .notify = hotkey_notify, - .handle = &hkey_handle, - .type = ACPI_DEVICE_NOTIFY, - }, - { - .name = "bluetooth", - .init = bluetooth_init, - .read = bluetooth_read, - .write = bluetooth_write, - }, - { - .name = "wan", - .init = wan_init, - .read = wan_read, - .write = wan_write, - .experimental = 1, - }, - { - .name = "video", - .init = video_init, - .read = video_read, - .write = video_write, - .exit = video_exit, - }, - { - .name = "light", - .init = light_init, - .read = light_read, - .write = light_write, - }, -#ifdef CONFIG_ACPI_IBM_DOCK - { - .name = "dock", - .read = dock_read, - .write = dock_write, - .notify = dock_notify, - .handle = &dock_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, - { - .name = "dock", - .hid = IBM_PCI_HID, - .notify = dock_notify, - .handle = &pci_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, -#endif -#ifdef CONFIG_ACPI_IBM_BAY - { - .name = "bay", - .init = bay_init, - .read = bay_read, - .write = bay_write, - .notify = bay_notify, - .handle = &bay_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, -#endif /* CONFIG_ACPI_IBM_BAY */ - { - .name = "cmos", - .read = cmos_read, - .write = cmos_write, - }, - { - .name = "led", - .init = led_init, - .read = led_read, - .write = led_write, - }, - { - .name = "beep", - .read = beep_read, - .write = beep_write, - }, - { - .name = "thermal", - .init = thermal_init, - .read = thermal_read, - }, - { - .name = "ecdump", - .read = ecdump_read, - .write = ecdump_write, - .experimental = 1, - }, - { - .name = "brightness", - .read = brightness_read, - .write = brightness_write, - .init = brightness_init, - .exit = brightness_exit, - }, - { - .name = "volume", - .read = volume_read, - .write = volume_write, - }, - { - .name = "fan", - .read = fan_read, - .write = fan_write, - .init = fan_init, - .exit = fan_exit, - .experimental = 1, - }, -}; - -static int dispatch_read(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct ibm_struct *ibm = data; - int len; - - if (!ibm || !ibm->read) - return -EINVAL; - - len = ibm->read(page); - if (len < 0) - return len; - - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - - return len; -} - -static int dispatch_write(struct file *file, const char __user * userbuf, - unsigned long count, void *data) -{ - struct ibm_struct *ibm = data; - char *kernbuf; - int ret; - - if (!ibm || !ibm->write) - return -EINVAL; - - kernbuf = kmalloc(count + 2, GFP_KERNEL); - if (!kernbuf) - return -ENOMEM; - - if (copy_from_user(kernbuf, userbuf, count)) { - kfree(kernbuf); - return -EFAULT; - } - - kernbuf[count] = 0; - strcat(kernbuf, ","); - ret = ibm->write(kernbuf); - if (ret == 0) - ret = count; - - kfree(kernbuf); - - return ret; -} - -static void dispatch_notify(acpi_handle handle, u32 event, void *data) -{ - struct ibm_struct *ibm = data; - - if (!ibm || !ibm->notify) - return; - - ibm->notify(ibm, event); -} - -static int __init setup_notify(struct ibm_struct *ibm) -{ - acpi_status status; - int ret; - - if (!*ibm->handle) - return 0; - - ret = acpi_bus_get_device(*ibm->handle, &ibm->device); - if (ret < 0) { - printk(IBM_ERR "%s device not present\n", ibm->name); - return -ENODEV; - } - - acpi_driver_data(ibm->device) = ibm; - sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name); - - status = acpi_install_notify_handler(*ibm->handle, ibm->type, - dispatch_notify, ibm); - if (ACPI_FAILURE(status)) { - if (status == AE_ALREADY_EXISTS) { - printk(IBM_NOTICE "another device driver is already handling %s events\n", - ibm->name); - } else { - printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", - ibm->name, status); - } - return -ENODEV; - } - ibm->notify_installed = 1; - return 0; -} - -static int __init ibm_device_add(struct acpi_device *device) -{ - return 0; -} - -static int __init register_driver(struct ibm_struct *ibm) -{ - int ret; - - ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); - if (!ibm->driver) { - printk(IBM_ERR "kmalloc(ibm->driver) failed\n"); - return -1; - } - - sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name); - ibm->driver->ids = ibm->hid; - ibm->driver->ops.add = &ibm_device_add; - - ret = acpi_bus_register_driver(ibm->driver); - if (ret < 0) { - printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", - ibm->hid, ret); - kfree(ibm->driver); - } - - return ret; -} - -static void ibm_exit(struct ibm_struct *ibm); - -static int __init ibm_init(struct ibm_struct *ibm) -{ - int ret; - struct proc_dir_entry *entry; - - if (ibm->experimental && !experimental) - return 0; - - if (ibm->hid) { - ret = register_driver(ibm); - if (ret < 0) - return ret; - ibm->driver_registered = 1; - } - - if (ibm->init) { - ret = ibm->init(); - if (ret != 0) - return ret; - ibm->init_called = 1; - } - - if (ibm->read) { - entry = create_proc_entry(ibm->name, - S_IFREG | S_IRUGO | S_IWUSR, - proc_dir); - if (!entry) { - printk(IBM_ERR "unable to create proc entry %s\n", - ibm->name); - return -ENODEV; - } - entry->owner = THIS_MODULE; - entry->data = ibm; - entry->read_proc = &dispatch_read; - if (ibm->write) - entry->write_proc = &dispatch_write; - ibm->proc_created = 1; - } - - if (ibm->notify) { - ret = setup_notify(ibm); - if (ret == -ENODEV) { - printk(IBM_NOTICE "disabling subdriver %s\n", - ibm->name); - ibm_exit(ibm); - return 0; - } - if (ret < 0) - return ret; - } - - return 0; -} - -static void ibm_exit(struct ibm_struct *ibm) -{ - if (ibm->notify_installed) - acpi_remove_notify_handler(*ibm->handle, ibm->type, - dispatch_notify); - - if (ibm->proc_created) - remove_proc_entry(ibm->name, proc_dir); - - if (ibm->init_called && ibm->exit) - ibm->exit(); - - if (ibm->driver_registered) { - acpi_bus_unregister_driver(ibm->driver); - kfree(ibm->driver); - } -} - -static void __init ibm_handle_init(char *name, - acpi_handle * handle, acpi_handle parent, - char **paths, int num_paths, char **path) -{ - int i; - acpi_status status; - - for (i = 0; i < num_paths; i++) { - status = acpi_get_handle(parent, paths[i], handle); - if (ACPI_SUCCESS(status)) { - *path = paths[i]; - return; - } - } - - *handle = NULL; -} - -#define IBM_HANDLE_INIT(object) \ - ibm_handle_init(#object, &object##_handle, *object##_parent, \ - object##_paths, ARRAY_SIZE(object##_paths), &object##_path) - -static int __init set_ibm_param(const char *val, struct kernel_param *kp) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(ibms); i++) - if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) { - if (strlen(val) > sizeof(ibms[i].param) - 2) - return -ENOSPC; - strcpy(ibms[i].param, val); - strcat(ibms[i].param, ","); - return 0; - } - - return -EINVAL; -} - -#define IBM_PARAM(feature) \ - module_param_call(feature, set_ibm_param, NULL, NULL, 0) - -IBM_PARAM(hotkey); -IBM_PARAM(bluetooth); -IBM_PARAM(video); -IBM_PARAM(light); -#ifdef CONFIG_ACPI_IBM_DOCK -IBM_PARAM(dock); -#endif -#ifdef CONFIG_ACPI_IBM_BAY -IBM_PARAM(bay); -#endif /* CONFIG_ACPI_IBM_BAY */ -IBM_PARAM(cmos); -IBM_PARAM(led); -IBM_PARAM(beep); -IBM_PARAM(ecdump); -IBM_PARAM(brightness); -IBM_PARAM(volume); -IBM_PARAM(fan); - -static void acpi_ibm_exit(void) -{ - int i; - - for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) - ibm_exit(&ibms[i]); - - if (proc_dir) - remove_proc_entry(IBM_DIR, acpi_root_dir); - - if (ibm_thinkpad_ec_found) - kfree(ibm_thinkpad_ec_found); -} - -static char* __init check_dmi_for_ec(void) -{ - struct dmi_device *dev = NULL; - char ec_fw_string[18]; - - /* - * ThinkPad T23 or newer, A31 or newer, R50e or newer, - * X32 or newer, all Z series; Some models must have an - * up-to-date BIOS or they will not be detected. - * - * See http://thinkwiki.org/wiki/List_of_DMI_IDs - */ - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, - "IBM ThinkPad Embedded Controller -[%17c", - ec_fw_string) == 1) { - ec_fw_string[sizeof(ec_fw_string) - 1] = 0; - ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; - return kstrdup(ec_fw_string, GFP_KERNEL); - } - } - return NULL; -} - -static int __init acpi_ibm_init(void) -{ - int ret, i; - - if (acpi_disabled) - return -ENODEV; - - /* ec is required because many other handles are relative to it */ - IBM_HANDLE_INIT(ec); - if (!ec_handle) { - printk(IBM_ERR "ec object not found\n"); - return -ENODEV; - } - - /* Models with newer firmware report the EC in DMI */ - ibm_thinkpad_ec_found = check_dmi_for_ec(); - - /* these handles are not required */ - IBM_HANDLE_INIT(vid); - IBM_HANDLE_INIT(vid2); - IBM_HANDLE_INIT(ledb); - IBM_HANDLE_INIT(led); - IBM_HANDLE_INIT(hkey); - IBM_HANDLE_INIT(lght); - IBM_HANDLE_INIT(cmos); -#ifdef CONFIG_ACPI_IBM_DOCK - IBM_HANDLE_INIT(dock); -#endif - IBM_HANDLE_INIT(pci); -#ifdef CONFIG_ACPI_IBM_BAY - IBM_HANDLE_INIT(bay); - if (bay_handle) - IBM_HANDLE_INIT(bay_ej); - IBM_HANDLE_INIT(bay2); - if (bay2_handle) - IBM_HANDLE_INIT(bay2_ej); -#endif /* CONFIG_ACPI_IBM_BAY */ - IBM_HANDLE_INIT(beep); - IBM_HANDLE_INIT(ecrd); - IBM_HANDLE_INIT(ecwr); - IBM_HANDLE_INIT(fans); - IBM_HANDLE_INIT(gfan); - IBM_HANDLE_INIT(sfan); - - proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); - if (!proc_dir) { - printk(IBM_ERR "unable to create proc dir %s", IBM_DIR); - acpi_ibm_exit(); - return -ENODEV; - } - proc_dir->owner = THIS_MODULE; - - for (i = 0; i < ARRAY_SIZE(ibms); i++) { - ret = ibm_init(&ibms[i]); - if (ret >= 0 && *ibms[i].param) - ret = ibms[i].write(ibms[i].param); - if (ret < 0) { - acpi_ibm_exit(); - return ret; - } - } - - return 0; -} - -module_init(acpi_ibm_init); -module_exit(acpi_ibm_exit); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 99d1516..f7de02a 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -70,8 +70,6 @@ #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 #define ACPI_PROCESSOR_LIMIT_USER 0 #define ACPI_PROCESSOR_LIMIT_THERMAL 1 -#define ACPI_STA_PRESENT 0x00000001 - #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_core"); @@ -779,7 +777,7 @@ static int is_processor_present(acpi_han status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) { + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) { ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present")); return 0; } diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index cdf7894..ae0654c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -51,14 +51,6 @@ #ifdef CONFIG_X86 #include #endif -/* - * Include the apic definitions for x86 to have the APIC timer related defines - * available also for UP (on SMP it gets magically included via linux/smp.h). - */ -#ifdef CONFIG_X86 -#include -#endif - #include #include diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 59640d9..c1bae10 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -30,30 +30,10 @@ #include #include #include #include -#include +#include +#include #include -#include "i2c_ec.h" - -#define DEF_CAPACITY_UNIT 3 -#define MAH_CAPACITY_UNIT 1 -#define MWH_CAPACITY_UNIT 2 -#define CAPACITY_UNIT DEF_CAPACITY_UNIT - -#define REQUEST_UPDATE_MODE 1 -#define QUEUE_UPDATE_MODE 2 - -#define DATA_TYPE_COMMON 0 -#define DATA_TYPE_INFO 1 -#define DATA_TYPE_STATE 2 -#define DATA_TYPE_ALARM 3 -#define DATA_TYPE_AC_STATE 4 - -extern struct proc_dir_entry *acpi_lock_ac_dir(void); -extern struct proc_dir_entry *acpi_lock_battery_dir(void); -extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); -extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); - #define ACPI_SBS_COMPONENT 0x00080000 #define ACPI_SBS_CLASS "sbs" #define ACPI_AC_CLASS "ac_adapter" @@ -74,39 +54,75 @@ #define ACPI_SBS_BATTERY_NOTIFY_INFO 0x8 #define _COMPONENT ACPI_SBS_COMPONENT -#define MAX_SBS_BAT 4 -#define MAX_SMBUS_ERR 1 - ACPI_MODULE_NAME("sbs"); MODULE_AUTHOR("Rich Townsend"); MODULE_DESCRIPTION("Smart Battery System ACPI interface driver"); MODULE_LICENSE("GPL"); -static struct semaphore sbs_sem; +#define xmsleep(t) msleep(t) + +#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */ + +#define ACPI_EC_SMB_STS 0x01 /* status */ +#define ACPI_EC_SMB_ADDR 0x02 /* address */ +#define ACPI_EC_SMB_CMD 0x03 /* command */ +#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */ +#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */ -#define UPDATE_MODE QUEUE_UPDATE_MODE -/* REQUEST_UPDATE_MODE QUEUE_UPDATE_MODE */ -#define UPDATE_INFO_MODE 0 -#define UPDATE_TIME 60 -#define UPDATE_TIME2 0 +#define ACPI_EC_SMB_STS_DONE 0x80 +#define ACPI_EC_SMB_STS_STATUS 0x1f -static int capacity_mode = CAPACITY_UNIT; -static int update_mode = UPDATE_MODE; -static int update_info_mode = UPDATE_INFO_MODE; -static int update_time = UPDATE_TIME; -static int update_time2 = UPDATE_TIME2; +#define ACPI_EC_SMB_PRTCL_WRITE 0x00 +#define ACPI_EC_SMB_PRTCL_READ 0x01 +#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08 +#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a -module_param(capacity_mode, int, 0); -module_param(update_mode, int, 0); -module_param(update_info_mode, int, 0); -module_param(update_time, int, 0); -module_param(update_time2, int, 0); +#define ACPI_EC_SMB_TRANSACTION_SLEEP 1 +#define ACPI_EC_SMB_ACCESS_SLEEP1 1 +#define ACPI_EC_SMB_ACCESS_SLEEP2 10 + +#define DEF_CAPACITY_UNIT 3 +#define MAH_CAPACITY_UNIT 1 +#define MWH_CAPACITY_UNIT 2 +#define CAPACITY_UNIT DEF_CAPACITY_UNIT + +#define REQUEST_UPDATE_MODE 1 +#define QUEUE_UPDATE_MODE 2 + +#define DATA_TYPE_COMMON 0 +#define DATA_TYPE_INFO 1 +#define DATA_TYPE_STATE 2 +#define DATA_TYPE_ALARM 3 +#define DATA_TYPE_AC_STATE 4 + +extern struct proc_dir_entry *acpi_lock_ac_dir(void); +extern struct proc_dir_entry *acpi_lock_battery_dir(void); +extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); +extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); + +#define MAX_SBS_BAT 4 +#define ACPI_SBS_BLOCK_MAX 32 + +#define ACPI_SBS_SMBUS_READ 1 +#define ACPI_SBS_SMBUS_WRITE 2 + +#define ACPI_SBS_WORD_DATA 1 +#define ACPI_SBS_BLOCK_DATA 2 + +#define UPDATE_DELAY 10 + +/* 0 - every time, > 0 - by update_time */ +static unsigned int update_time = 120; + +static unsigned int capacity_mode = CAPACITY_UNIT; + +module_param(update_time, uint, 0644); +module_param(capacity_mode, uint, 0444); static int acpi_sbs_add(struct acpi_device *device); static int acpi_sbs_remove(struct acpi_device *device, int type); -static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus); -static void acpi_sbs_update_queue(void *data); +static int acpi_sbs_resume(struct acpi_device *device); static struct acpi_driver acpi_sbs_driver = { .name = "sbs", @@ -115,9 +131,14 @@ static struct acpi_driver acpi_sbs_drive .ops = { .add = acpi_sbs_add, .remove = acpi_sbs_remove, + .resume = acpi_sbs_resume, }, }; +struct acpi_ac { + int ac_present; +}; + struct acpi_battery_info { int capacity_mode; s16 full_charge_capacity; @@ -126,18 +147,16 @@ struct acpi_battery_info { int vscale; int ipscale; s16 serial_number; - char manufacturer_name[I2C_SMBUS_BLOCK_MAX + 3]; - char device_name[I2C_SMBUS_BLOCK_MAX + 3]; - char device_chemistry[I2C_SMBUS_BLOCK_MAX + 3]; + char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3]; + char device_name[ACPI_SBS_BLOCK_MAX + 3]; + char device_chemistry[ACPI_SBS_BLOCK_MAX + 3]; }; struct acpi_battery_state { s16 voltage; s16 amperage; s16 remaining_capacity; - s16 average_time_to_empty; - s16 average_time_to_full; - s16 battery_status; + s16 battery_state; }; struct acpi_battery_alarm { @@ -146,9 +165,9 @@ struct acpi_battery_alarm { struct acpi_battery { int alive; - int battery_present; int id; int init_state; + int battery_present; struct acpi_sbs *sbs; struct acpi_battery_info info; struct acpi_battery_state state; @@ -158,186 +177,251 @@ struct acpi_battery { struct acpi_sbs { acpi_handle handle; + int base; struct acpi_device *device; struct acpi_ec_smbus *smbus; + struct mutex mutex; int sbsm_present; int sbsm_batteries_supported; - int ac_present; struct proc_dir_entry *ac_entry; + struct acpi_ac ac; struct acpi_battery battery[MAX_SBS_BAT]; - int update_info_mode; int zombie; - int update_time; - int update_time2; struct timer_list update_timer; + int run_cnt; + int update_proc_flg; }; -static void acpi_update_delay(struct acpi_sbs *sbs); -static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type); +static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type); +static void acpi_sbs_update_time(void *data); + +union sbs_rw_data { + u16 word; + u8 block[ACPI_SBS_BLOCK_MAX + 2]; +}; + +static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr, + char read_write, u8 command, int size, + union sbs_rw_data *data); /* -------------------------------------------------------------------------- SMBus Communication -------------------------------------------------------------------------- */ -static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus) +static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data) { - union i2c_smbus_data data; - int result = 0; - char *err_str; - int err_number; + u8 val; + int err; - data.word = 0; + err = ec_read(sbs->base + address, &val); + if (!err) { + *data = val; + } + xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); + return (err); +} - result = smbus->adapter.algo-> - smbus_xfer(&smbus->adapter, - ACPI_SB_SMBUS_ADDR, - 0, I2C_SMBUS_READ, 0x16, I2C_SMBUS_BLOCK_DATA, &data); +static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data) +{ + int err; - err_number = (data.word & 0x000f); + err = ec_write(sbs->base + address, data); + return (err); +} - switch (data.word & 0x000f) { - case 0x0000: - err_str = "unexpected bus error"; - break; - case 0x0001: - err_str = "busy"; - break; - case 0x0002: - err_str = "reserved command"; - break; - case 0x0003: - err_str = "unsupported command"; - break; - case 0x0004: - err_str = "access denied"; +static int +acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr, + char read_write, u8 command, int size, + union sbs_rw_data *data) +{ + unsigned char protocol, len = 0, temp[2] = { 0, 0 }; + int i; + + if (read_write == ACPI_SBS_SMBUS_READ) { + protocol = ACPI_EC_SMB_PRTCL_READ; + } else { + protocol = ACPI_EC_SMB_PRTCL_WRITE; + } + + switch (size) { + + case ACPI_SBS_WORD_DATA: + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command); + if (read_write == ACPI_SBS_SMBUS_WRITE) { + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word); + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1, + data->word >> 8); + } + protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA; break; - case 0x0005: - err_str = "overflow/underflow"; + case ACPI_SBS_BLOCK_DATA: + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command); + if (read_write == ACPI_SBS_SMBUS_WRITE) { + len = min_t(u8, data->block[0], 32); + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len); + for (i = 0; i < len; i++) + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i, + data->block[i + 1]); + } + protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA; break; - case 0x0006: - err_str = "bad size"; + default: + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "unsupported transaction %d", size)); + return (-1); + } + + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1); + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol); + + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); + + if (~temp[0] & ACPI_EC_SMB_STS_DONE) { + xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1); + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); + } + if (~temp[0] & ACPI_EC_SMB_STS_DONE) { + xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2); + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); + } + if ((~temp[0] & ACPI_EC_SMB_STS_DONE) + || (temp[0] & ACPI_EC_SMB_STS_STATUS)) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "transaction %d error", size)); + return (-1); + } + + if (read_write == ACPI_SBS_SMBUS_WRITE) { + return (0); + } + + switch (size) { + + case ACPI_SBS_WORD_DATA: + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp); + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1); + data->word = (temp[1] << 8) | temp[0]; break; - case 0x0007: - err_str = "unknown error"; + + case ACPI_SBS_BLOCK_DATA: + len = 0; + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len); + len = min_t(u8, len, 32); + for (i = 0; i < len; i++) + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i, + data->block + i + 1); + data->block[0] = len; break; default: - err_str = "unrecognized error"; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "unsupported transaction %d", size)); + return (-1); } - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "%s: ret %i, err %i\n", err_str, result, err_number)); + + return (0); } static int -acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, int addr, int func, - u16 * word, - void (*err_handler) (struct acpi_ec_smbus * smbus)) +acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word) { - union i2c_smbus_data data; + union sbs_rw_data data; int result = 0; - int i; - if (err_handler == NULL) { - err_handler = acpi_battery_smbus_err_handler; - } - - for (i = 0; i < MAX_SMBUS_ERR; i++) { - result = - smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, - I2C_SMBUS_READ, func, - I2C_SMBUS_WORD_DATA, &data); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "try %i: smbus->adapter.algo->smbus_xfer() failed\n", - i)); - if (err_handler) { - err_handler(smbus); - } - } else { - *word = data.word; - break; - } + result = acpi_ec_sbs_access(sbs, addr, + ACPI_SBS_SMBUS_READ, func, + ACPI_SBS_WORD_DATA, &data); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ec_sbs_access() failed")); + } else { + *word = data.word; } return result; } static int -acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, int addr, int func, - char *str, - void (*err_handler) (struct acpi_ec_smbus * smbus)) +acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str) { - union i2c_smbus_data data; + union sbs_rw_data data; int result = 0; - int i; - - if (err_handler == NULL) { - err_handler = acpi_battery_smbus_err_handler; - } - for (i = 0; i < MAX_SMBUS_ERR; i++) { - result = - smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, - I2C_SMBUS_READ, func, - I2C_SMBUS_BLOCK_DATA, - &data); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "try %i: smbus->adapter.algo->smbus_xfer() failed\n", - i)); - if (err_handler) { - err_handler(smbus); - } - } else { - strncpy(str, (const char *)data.block + 1, - data.block[0]); - str[data.block[0]] = 0; - break; - } + result = acpi_ec_sbs_access(sbs, addr, + ACPI_SBS_SMBUS_READ, func, + ACPI_SBS_BLOCK_DATA, &data); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ec_sbs_access() failed")); + } else { + strncpy(str, (const char *)data.block + 1, data.block[0]); + str[data.block[0]] = 0; } return result; } static int -acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, int addr, int func, - int word, - void (*err_handler) (struct acpi_ec_smbus * smbus)) +acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word) { - union i2c_smbus_data data; + union sbs_rw_data data; int result = 0; - int i; - - if (err_handler == NULL) { - err_handler = acpi_battery_smbus_err_handler; - } data.word = word; - for (i = 0; i < MAX_SMBUS_ERR; i++) { - result = - smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, - I2C_SMBUS_WRITE, func, - I2C_SMBUS_WORD_DATA, &data); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "try %i: smbus->adapter.algo" - "->smbus_xfer() failed\n", i)); - if (err_handler) { - err_handler(smbus); - } - } else { - break; - } + result = acpi_ec_sbs_access(sbs, addr, + ACPI_SBS_SMBUS_WRITE, func, + ACPI_SBS_WORD_DATA, &data); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ec_sbs_access() failed")); } return result; } +static int sbs_zombie(struct acpi_sbs *sbs) +{ + return (sbs->zombie); +} + +static int sbs_mutex_lock(struct acpi_sbs *sbs) +{ + if (sbs_zombie(sbs)) { + return -ENODEV; + } + mutex_lock(&sbs->mutex); + return 0; +} + +static void sbs_mutex_unlock(struct acpi_sbs *sbs) +{ + mutex_unlock(&sbs->mutex); +} + /* -------------------------------------------------------------------------- Smart Battery System Management -------------------------------------------------------------------------- */ -/* Smart Battery */ +static int acpi_check_update_proc(struct acpi_sbs *sbs) +{ + acpi_status status = AE_OK; + + if (update_time == 0) { + sbs->update_proc_flg = 0; + return 0; + } + if (sbs->update_proc_flg == 0) { + status = acpi_os_execute(OSL_GPE_HANDLER, + acpi_sbs_update_time, sbs); + if (status != AE_OK) { + ACPI_EXCEPTION((AE_INFO, status, + "acpi_os_execute() failed")); + return 1; + } + sbs->update_proc_flg = 1; + } + return 0; +} static int acpi_sbs_generate_event(struct acpi_device *device, int event, int state, char *bid, char *class) @@ -366,12 +450,11 @@ static int acpi_battery_get_present(stru int result = 0; int is_present = 0; - result = acpi_sbs_smbus_read_word(battery->sbs->smbus, - ACPI_SBSM_SMBUS_ADDR, 0x01, - &state, NULL); + result = acpi_sbs_read_word(battery->sbs, + ACPI_SBSM_SMBUS_ADDR, 0x01, &state); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); } if (!result) { is_present = (state & 0x000f) & (1 << battery->id); @@ -381,45 +464,33 @@ static int acpi_battery_get_present(stru return result; } -static int acpi_battery_is_present(struct acpi_battery *battery) -{ - return (battery->battery_present); -} - -static int acpi_ac_is_present(struct acpi_sbs *sbs) -{ - return (sbs->ac_present); -} - static int acpi_battery_select(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 state; int foo; - if (battery->sbs->sbsm_present) { + if (sbs->sbsm_present) { /* Take special care not to knobble other nibbles of * state (aka selector_state), since * it causes charging to halt on SBSELs */ result = - acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, - &state, NULL); + acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } foo = (state & 0x0fff) | (1 << (battery->id + 12)); result = - acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, - foo, NULL); + acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } } @@ -430,15 +501,14 @@ static int acpi_battery_select(struct ac static int acpi_sbsm_get_info(struct acpi_sbs *sbs) { - struct acpi_ec_smbus *smbus = sbs->smbus; int result = 0; s16 battery_system_info; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04, - &battery_system_info, NULL); + result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04, + &battery_system_info); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -451,53 +521,50 @@ static int acpi_sbsm_get_info(struct acp static int acpi_battery_get_info(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 battery_mode; s16 specification_info; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } battery->info.capacity_mode = (battery_mode & 0x8000) >> 15; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10, - &battery->info.full_charge_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10, + &battery->info.full_charge_capacity); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18, - &battery->info.design_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18, + &battery->info.design_capacity); if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19, - &battery->info.design_voltage, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19, + &battery->info.design_voltage); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a, - &specification_info, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a, + &specification_info); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -529,37 +596,35 @@ static int acpi_battery_get_info(struct battery->info.ipscale = 1; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c, - &battery->info.serial_number, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c, + &battery->info.serial_number); if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20, - battery->info.manufacturer_name, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20, + battery->info.manufacturer_name); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_str() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_str() failed")); goto end; } - result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21, - battery->info.device_name, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21, + battery->info.device_name); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_str() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_str() failed")); goto end; } - result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22, - battery->info.device_chemistry, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22, + battery->info.device_chemistry); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_str() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_str() failed")); goto end; } @@ -567,103 +632,60 @@ static int acpi_battery_get_info(struct return result; } -static void acpi_update_delay(struct acpi_sbs *sbs) -{ - if (sbs->zombie) { - return; - } - if (sbs->update_time2 > 0) { - msleep(sbs->update_time2 * 1000); - } -} - static int acpi_battery_get_state(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09, - &battery->state.voltage, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09, + &battery->state.voltage); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a, - &battery->state.amperage, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a, + &battery->state.amperage); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f, - &battery->state.remaining_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f, + &battery->state.remaining_capacity); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12, - &battery->state.average_time_to_empty, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16, + &battery->state.battery_state); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13, - &battery->state.average_time_to_full, - &acpi_battery_smbus_err_handler); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); - goto end; - } - - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16, - &battery->state.battery_status, - &acpi_battery_smbus_err_handler); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); - goto end; - } - - acpi_update_delay(battery->sbs); - end: return result; } static int acpi_battery_get_alarm(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, - &battery->alarm.remaining_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, + &battery->alarm.remaining_capacity); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - end: return result; @@ -672,15 +694,15 @@ static int acpi_battery_get_alarm(struct static int acpi_battery_set_alarm(struct acpi_battery *battery, unsigned long alarm) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 battery_mode; int foo; result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_select() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); goto end; } @@ -688,33 +710,29 @@ static int acpi_battery_set_alarm(struct if (alarm > 0) { result = - acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, - &acpi_battery_smbus_err_handler); + acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } result = - acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, - battery_mode & 0xbfff, - &acpi_battery_smbus_err_handler); + acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, + battery_mode & 0xbfff); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } } foo = alarm / (battery->info.capacity_mode ? 10 : 1); - result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, - foo, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } @@ -725,6 +743,7 @@ static int acpi_battery_set_alarm(struct static int acpi_battery_set_mode(struct acpi_battery *battery) { + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 battery_mode; @@ -732,12 +751,11 @@ static int acpi_battery_set_mode(struct goto end; } - result = acpi_sbs_smbus_read_word(battery->sbs->smbus, - ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, NULL); + result = acpi_sbs_read_word(sbs, + ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -746,21 +764,19 @@ static int acpi_battery_set_mode(struct } else { battery_mode |= 0x8000; } - result = acpi_sbs_smbus_write_word(battery->sbs->smbus, - ACPI_SB_SMBUS_ADDR, 0x03, - battery_mode, NULL); + result = acpi_sbs_write_word(sbs, + ACPI_SB_SMBUS_ADDR, 0x03, battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(battery->sbs->smbus, - ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, NULL); + result = acpi_sbs_read_word(sbs, + ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -774,36 +790,36 @@ static int acpi_battery_init(struct acpi result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_init() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); goto end; } result = acpi_battery_set_mode(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_set_mode() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_set_mode() failed")); goto end; } result = acpi_battery_get_info(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_info() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_info() failed")); goto end; } result = acpi_battery_get_state(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_state() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_state() failed")); goto end; } result = acpi_battery_get_alarm(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_alarm() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_alarm() failed")); goto end; } @@ -813,20 +829,19 @@ static int acpi_battery_init(struct acpi static int acpi_ac_get_present(struct acpi_sbs *sbs) { - struct acpi_ec_smbus *smbus = sbs->smbus; int result = 0; s16 charger_status; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13, - &charger_status, NULL); + result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13, + &charger_status); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - sbs->ac_present = (charger_status & 0x8000) >> 15; + sbs->ac.ac_present = (charger_status & 0x8000) >> 15; end: @@ -852,8 +867,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_ if (!*dir) { *dir = proc_mkdir(dir_name, parent_dir); if (!*dir) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "proc_mkdir() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "proc_mkdir() failed")); return -ENODEV; } (*dir)->owner = THIS_MODULE; @@ -863,8 +878,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_ if (info_fops) { entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir); if (!entry) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "create_proc_entry() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "create_proc_entry() failed")); } else { entry->proc_fops = info_fops; entry->data = data; @@ -876,8 +891,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_ if (state_fops) { entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir); if (!entry) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "create_proc_entry() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "create_proc_entry() failed")); } else { entry->proc_fops = state_fops; entry->data = data; @@ -889,8 +904,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_ if (alarm_fops) { entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir); if (!entry) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "create_proc_entry() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "create_proc_entry() failed")); } else { entry->proc_fops = alarm_fops; entry->data = data; @@ -923,24 +938,27 @@ static struct proc_dir_entry *acpi_batte static int acpi_battery_read_info(struct seq_file *seq, void *offset) { struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; int cscale; int result = 0; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_INFO); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } - if (acpi_battery_is_present(battery)) { + if (battery->battery_present) { seq_printf(seq, "present: yes\n"); } else { seq_printf(seq, "present: no\n"); @@ -952,13 +970,13 @@ static int acpi_battery_read_info(struct } else { cscale = battery->info.ipscale; } - seq_printf(seq, "design capacity: %i%s", + seq_printf(seq, "design capacity: %i%s\n", battery->info.design_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); - seq_printf(seq, "last full capacity: %i%s", + seq_printf(seq, "last full capacity: %i%s\n", battery->info.full_charge_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); seq_printf(seq, "battery technology: rechargeable\n"); @@ -984,7 +1002,7 @@ static int acpi_battery_read_info(struct end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); return result; } @@ -996,26 +1014,29 @@ static int acpi_battery_info_open_fs(str static int acpi_battery_read_state(struct seq_file *seq, void *offset) { - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; int result = 0; int cscale; int foo; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_STATE); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } - if (acpi_battery_is_present(battery)) { + if (battery->battery_present) { seq_printf(seq, "present: yes\n"); } else { seq_printf(seq, "present: no\n"); @@ -1028,7 +1049,7 @@ static int acpi_battery_read_state(struc cscale = battery->info.ipscale; } - if (battery->state.battery_status & 0x0010) { + if (battery->state.battery_state & 0x0010) { seq_printf(seq, "capacity state: critical\n"); } else { seq_printf(seq, "capacity state: ok\n"); @@ -1052,16 +1073,16 @@ static int acpi_battery_read_state(struc battery->info.capacity_mode ? "mW" : "mA"); } - seq_printf(seq, "remaining capacity: %i%s", + seq_printf(seq, "remaining capacity: %i%s\n", battery->state.remaining_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); seq_printf(seq, "present voltage: %i mV\n", battery->state.voltage * battery->info.vscale); end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); return result; } @@ -1074,24 +1095,27 @@ static int acpi_battery_state_open_fs(st static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) { struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; int result = 0; int cscale; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_ALARM); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } - if (!acpi_battery_is_present(battery)) { + if (!battery->battery_present) { seq_printf(seq, "present: no\n"); goto end; } @@ -1104,16 +1128,16 @@ static int acpi_battery_read_alarm(struc seq_printf(seq, "alarm: "); if (battery->alarm.remaining_capacity) { - seq_printf(seq, "%i%s", + seq_printf(seq, "%i%s\n", battery->alarm.remaining_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); } else { seq_printf(seq, "disabled\n"); } end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); return result; } @@ -1124,16 +1148,19 @@ acpi_battery_write_alarm(struct file *fi { struct seq_file *seq = file->private_data; struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; char alarm_string[12] = { '\0' }; int result, old_alarm, new_alarm; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (!acpi_battery_is_present(battery)) { + if (!battery->battery_present) { result = -ENODEV; goto end; } @@ -1155,21 +1182,21 @@ acpi_battery_write_alarm(struct file *fi result = acpi_battery_set_alarm(battery, new_alarm); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_set_alarm() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_set_alarm() failed")); acpi_battery_set_alarm(battery, old_alarm); goto end; } result = acpi_battery_get_alarm(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_alarm() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_alarm() failed")); acpi_battery_set_alarm(battery, old_alarm); goto end; } end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); if (result) { return result; @@ -1217,24 +1244,22 @@ static int acpi_ac_read_state(struct seq struct acpi_sbs *sbs = seq->private; int result; - if (sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); - - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(sbs, DATA_TYPE_AC_STATE); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } seq_printf(seq, "state: %s\n", - sbs->ac_present ? "on-line" : "off-line"); + sbs->ac.ac_present ? "on-line" : "off-line"); - up(&sbs_sem); + sbs_mutex_unlock(sbs); return 0; } @@ -1275,25 +1300,25 @@ static int acpi_battery_add(struct acpi_ result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_select() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); goto end; } result = acpi_battery_get_present(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_present() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_present() failed")); goto end; } - is_present = acpi_battery_is_present(battery); + is_present = battery->battery_present; if (is_present) { result = acpi_battery_init(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_init() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_init() failed")); goto end; } battery->init_state = 1; @@ -1308,12 +1333,16 @@ static int acpi_battery_add(struct acpi_ &acpi_battery_state_fops, &acpi_battery_alarm_fops, battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generic_add_fs() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generic_add_fs() failed")); goto end; } battery->alive = 1; + printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n", + ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name, + sbs->battery->battery_present ? "present" : "absent"); + end: return result; } @@ -1333,8 +1362,8 @@ static int acpi_ac_add(struct acpi_sbs * result = acpi_ac_get_present(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_ac_get_present() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ac_get_present() failed")); goto end; } @@ -1343,11 +1372,15 @@ static int acpi_ac_add(struct acpi_sbs * ACPI_AC_DIR_NAME, NULL, &acpi_ac_state_fops, NULL, sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generic_add_fs() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generic_add_fs() failed")); goto end; } + printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n", + ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), + ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line"); + end: return result; @@ -1361,45 +1394,85 @@ static void acpi_ac_remove(struct acpi_s } } -static void acpi_sbs_update_queue_run(unsigned long data) +static void acpi_sbs_update_time_run(unsigned long data) { - acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_queue, (void *)data); + acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data); } -static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) +static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type) { struct acpi_battery *battery; - int result = 0; - int old_ac_present; - int old_battery_present; - int new_ac_present; - int new_battery_present; - int id; + int result = 0, cnt; + int old_ac_present = -1; + int old_battery_present = -1; + int new_ac_present = -1; + int new_battery_present = -1; + int id_min = 0, id_max = MAX_SBS_BAT - 1; char dir_name[32]; - int do_battery_init, do_ac_init; - s16 old_remaining_capacity; + int do_battery_init = 0, do_ac_init = 0; + int old_remaining_capacity = 0; + int update_ac = 1, update_battery = 1; + int up_tm = update_time; - if (sbs->zombie) { + if (sbs_zombie(sbs)) { goto end; } - old_ac_present = acpi_ac_is_present(sbs); + if (id >= 0) { + id_min = id_max = id; + } + + if (data_type == DATA_TYPE_COMMON && up_tm > 0) { + cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm); + if (sbs->run_cnt % cnt != 0) { + update_battery = 0; + } + } + + sbs->run_cnt++; + + if (!update_ac && !update_battery) { + goto end; + } + + old_ac_present = sbs->ac.ac_present; result = acpi_ac_get_present(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_ac_get_present() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ac_get_present() failed")); } - new_ac_present = acpi_ac_is_present(sbs); + new_ac_present = sbs->ac.ac_present; do_ac_init = (old_ac_present != new_ac_present); + if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) { + do_ac_init = 1; + } - if (data_type == DATA_TYPE_AC_STATE) { + if (do_ac_init) { + result = acpi_sbs_generate_event(sbs->device, + ACPI_SBS_AC_NOTIFY_STATUS, + new_ac_present, + ACPI_AC_DIR_NAME, + ACPI_AC_CLASS); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generate_event() failed")); + } + } + + if (data_type == DATA_TYPE_COMMON) { + if (!do_ac_init && !update_battery) { + goto end; + } + } + + if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) { goto end; } - for (id = 0; id < MAX_SBS_BAT; id++) { + for (id = id_min; id <= id_max; id++) { battery = &sbs->battery[id]; if (battery->alive == 0) { continue; @@ -1407,94 +1480,92 @@ static int acpi_sbs_update_run(struct ac old_remaining_capacity = battery->state.remaining_capacity; - old_battery_present = acpi_battery_is_present(battery); + old_battery_present = battery->battery_present; result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_select() failed\n")); - } - if (sbs->zombie) { - goto end; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); } result = acpi_battery_get_present(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_present() failed\n")); - } - if (sbs->zombie) { - goto end; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_present() failed")); } - new_battery_present = acpi_battery_is_present(battery); + new_battery_present = battery->battery_present; do_battery_init = ((old_battery_present != new_battery_present) && new_battery_present); - - if (sbs->zombie) { + if (!new_battery_present) + goto event; + if (do_ac_init || do_battery_init) { + result = acpi_battery_init(battery); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_init() " + "failed")); + } + } + if (sbs_zombie(sbs)) { goto end; } - if (do_ac_init || do_battery_init || - update_info_mode || sbs->update_info_mode) { - if (sbs->update_info_mode) { - sbs->update_info_mode = 0; - } else { - sbs->update_info_mode = 1; - } - result = acpi_battery_init(battery); + + if ((data_type == DATA_TYPE_COMMON + || data_type == DATA_TYPE_INFO) + && new_battery_present) { + result = acpi_battery_get_info(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_init() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_info() failed")); } } if (data_type == DATA_TYPE_INFO) { continue; } - - if (sbs->zombie) { + if (sbs_zombie(sbs)) { goto end; } - if (new_battery_present) { - result = acpi_battery_get_alarm(battery); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_alarm() " - "failed\n")); - } - if (data_type == DATA_TYPE_ALARM) { - continue; - } + if ((data_type == DATA_TYPE_COMMON + || data_type == DATA_TYPE_STATE) + && new_battery_present) { result = acpi_battery_get_state(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_state() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_state() failed")); } } - if (sbs->zombie) { - goto end; + if (data_type == DATA_TYPE_STATE) { + goto event; } - if (data_type != DATA_TYPE_COMMON) { - continue; + if (sbs_zombie(sbs)) { + goto end; } - if (old_battery_present != new_battery_present) { - sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); - result = acpi_sbs_generate_event(sbs->device, - ACPI_SBS_BATTERY_NOTIFY_STATUS, - new_battery_present, - dir_name, - ACPI_BATTERY_CLASS); + if ((data_type == DATA_TYPE_COMMON + || data_type == DATA_TYPE_ALARM) + && new_battery_present) { + result = acpi_battery_get_alarm(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generate_event() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_alarm() " + "failed")); } } - if (old_remaining_capacity != battery->state.remaining_capacity) { + if (data_type == DATA_TYPE_ALARM) { + continue; + } + if (sbs_zombie(sbs)) { + goto end; + } + + event: + + if (old_battery_present != new_battery_present || do_ac_init || + old_remaining_capacity != + battery->state.remaining_capacity) { sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); result = acpi_sbs_generate_event(sbs->device, ACPI_SBS_BATTERY_NOTIFY_STATUS, @@ -1502,138 +1573,120 @@ static int acpi_sbs_update_run(struct ac dir_name, ACPI_BATTERY_CLASS); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generate_event() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generate_event() " + "failed")); } } - - } - if (sbs->zombie) { - goto end; - } - if (data_type != DATA_TYPE_COMMON) { - goto end; - } - - if (old_ac_present != new_ac_present) { - result = acpi_sbs_generate_event(sbs->device, - ACPI_SBS_AC_NOTIFY_STATUS, - new_ac_present, - ACPI_AC_DIR_NAME, - ACPI_AC_CLASS); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generate_event() failed\n")); - } } end: + return result; } -static void acpi_sbs_update_queue(void *data) +static void acpi_sbs_update_time(void *data) { struct acpi_sbs *sbs = data; unsigned long delay = -1; int result; + unsigned int up_tm = update_time; - if (sbs->zombie) { - goto end; - } + if (sbs_mutex_lock(sbs)) + return; - result = acpi_sbs_update_run(sbs, DATA_TYPE_COMMON); + result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } - if (sbs->zombie) { + if (sbs_zombie(sbs)) { goto end; } - if (update_mode == REQUEST_UPDATE_MODE) { - goto end; + if (!up_tm) { + if (timer_pending(&sbs->update_timer)) + del_timer(&sbs->update_timer); + } else { + delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm); + delay = jiffies + HZ * delay; + if (timer_pending(&sbs->update_timer)) { + mod_timer(&sbs->update_timer, delay); + } else { + sbs->update_timer.data = (unsigned long)data; + sbs->update_timer.function = acpi_sbs_update_time_run; + sbs->update_timer.expires = delay; + add_timer(&sbs->update_timer); + } } - delay = jiffies + HZ * update_time; - sbs->update_timer.data = (unsigned long)data; - sbs->update_timer.function = acpi_sbs_update_queue_run; - sbs->update_timer.expires = delay; - add_timer(&sbs->update_timer); end: - ; + + sbs_mutex_unlock(sbs); } static int acpi_sbs_add(struct acpi_device *device) { struct acpi_sbs *sbs = NULL; - struct acpi_ec_hc *ec_hc = NULL; - int result, remove_result = 0; + int result = 0, remove_result = 0; unsigned long sbs_obj; - int id, cnt; + int id; acpi_status status = AE_OK; + unsigned long val; + + status = + acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC")); + return -EIO; + } sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL); if (!sbs) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "kmalloc() failed\n")); - return -ENOMEM; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed")); + result = -ENOMEM; + goto end; } - cnt = 0; - while (cnt < 10) { - cnt++; - ec_hc = acpi_get_ec_hc(device); - if (ec_hc) { - break; - } - msleep(1000); - } + mutex_init(&sbs->mutex); - if (!ec_hc) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_get_ec_hc() failed: " - "NO driver found for EC HC SMBus\n")); - result = -ENODEV; - goto end; - } + sbs_mutex_lock(sbs); + sbs->base = (val & 0xff00ull) >> 8; sbs->device = device; - sbs->smbus = ec_hc->smbus; strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_SBS_CLASS); acpi_driver_data(device) = sbs; - sbs->update_time = 0; - sbs->update_time2 = 0; - result = acpi_ac_add(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_ac_add() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed")); goto end; } - result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); - if (ACPI_FAILURE(result)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_evaluate_integer() failed\n")); + status = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); + if (status) { + ACPI_EXCEPTION((AE_INFO, status, + "acpi_evaluate_integer() failed")); result = -EIO; goto end; } - if (sbs_obj > 0) { result = acpi_sbsm_get_info(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbsm_get_info() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbsm_get_info() failed")); goto end; } sbs->sbsm_present = 1; } + if (sbs->sbsm_present == 0) { result = acpi_battery_add(sbs, 0); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_add() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_add() failed")); goto end; } } else { @@ -1641,9 +1694,8 @@ static int acpi_sbs_add(struct acpi_devi if ((sbs->sbsm_batteries_supported & (1 << id))) { result = acpi_battery_add(sbs, id); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_add() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_add() failed")); goto end; } } @@ -1653,33 +1705,26 @@ static int acpi_sbs_add(struct acpi_devi sbs->handle = device->handle; init_timer(&sbs->update_timer); - if (update_mode == QUEUE_UPDATE_MODE) { - status = acpi_os_execute(OSL_GPE_HANDLER, - acpi_sbs_update_queue, sbs); - if (status != AE_OK) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_os_execute() failed\n")); - } - } - sbs->update_time = update_time; - sbs->update_time2 = update_time2; - - printk(KERN_INFO PREFIX "%s [%s]\n", - acpi_device_name(device), acpi_device_bid(device)); + result = acpi_check_update_proc(sbs); + if (result) + goto end; end: + + sbs_mutex_unlock(sbs); + if (result) { remove_result = acpi_sbs_remove(device, 0); if (remove_result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_remove() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_remove() failed")); } } return result; } -int acpi_sbs_remove(struct acpi_device *device, int type) +static int acpi_sbs_remove(struct acpi_device *device, int type) { struct acpi_sbs *sbs; int id; @@ -1688,15 +1733,14 @@ int acpi_sbs_remove(struct acpi_device * return -EINVAL; } - sbs = (struct acpi_sbs *)acpi_driver_data(device); - + sbs = acpi_driver_data(device); if (!sbs) { return -EINVAL; } + sbs_mutex_lock(sbs); + sbs->zombie = 1; - sbs->update_time = 0; - sbs->update_time2 = 0; del_timer_sync(&sbs->update_timer); acpi_os_wait_events_complete(NULL); del_timer_sync(&sbs->update_timer); @@ -1707,11 +1751,41 @@ int acpi_sbs_remove(struct acpi_device * acpi_ac_remove(sbs); + sbs_mutex_unlock(sbs); + + mutex_destroy(&sbs->mutex); + kfree(sbs); return 0; } +static void acpi_sbs_rmdirs(void) +{ + if (acpi_ac_dir) { + acpi_unlock_ac_dir(acpi_ac_dir); + acpi_ac_dir = NULL; + } + if (acpi_battery_dir) { + acpi_unlock_battery_dir(acpi_battery_dir); + acpi_battery_dir = NULL; + } +} + +static int acpi_sbs_resume(struct acpi_device *device) +{ + struct acpi_sbs *sbs; + + if (!device) + return -EINVAL; + + sbs = device->driver_data; + + sbs->run_cnt = 0; + + return 0; +} + static int __init acpi_sbs_init(void) { int result = 0; @@ -1719,35 +1793,34 @@ static int __init acpi_sbs_init(void) if (acpi_disabled) return -ENODEV; - init_MUTEX(&sbs_sem); - if (capacity_mode != DEF_CAPACITY_UNIT && capacity_mode != MAH_CAPACITY_UNIT && capacity_mode != MWH_CAPACITY_UNIT) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_sbs_init: " - "invalid capacity_mode = %d\n", - capacity_mode)); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "invalid capacity_mode = %d", capacity_mode)); return -EINVAL; } acpi_ac_dir = acpi_lock_ac_dir(); if (!acpi_ac_dir) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_lock_ac_dir() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_lock_ac_dir() failed")); return -ENODEV; } acpi_battery_dir = acpi_lock_battery_dir(); if (!acpi_battery_dir) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_lock_battery_dir() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_lock_battery_dir() failed")); + acpi_sbs_rmdirs(); return -ENODEV; } result = acpi_bus_register_driver(&acpi_sbs_driver); if (result < 0) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_bus_register_driver() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_bus_register_driver() failed")); + acpi_sbs_rmdirs(); return -ENODEV; } @@ -1756,13 +1829,9 @@ static int __init acpi_sbs_init(void) static void __exit acpi_sbs_exit(void) { - acpi_bus_unregister_driver(&acpi_sbs_driver); - acpi_unlock_ac_dir(acpi_ac_dir); - acpi_ac_dir = NULL; - acpi_unlock_battery_dir(acpi_battery_dir); - acpi_battery_dir = NULL; + acpi_sbs_rmdirs(); return; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index bb0e0da..d80dd84 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1068,7 +1068,9 @@ acpi_add_single_object(struct acpi_devic } break; default: - STRUCT_TO_INT(device->status) = 0x0F; + STRUCT_TO_INT(device->status) = + ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; break; } diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 37a0930..f8c6341 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -168,9 +168,18 @@ int acpi_suspend(u32 acpi_state) static int acpi_pm_state_valid(suspend_state_t pm_state) { - u32 acpi_state = acpi_suspend_states[pm_state]; + u32 acpi_state; + + switch (pm_state) { + case PM_SUSPEND_ON: + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + acpi_state = acpi_suspend_states[pm_state]; - return sleep_states[acpi_state]; + return sleep_states[acpi_state]; + default: + return 0; + } } static struct pm_ops acpi_pm_ops = { diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index ccc11b3..2d912b7 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -350,21 +350,31 @@ acpi_system_wakeup_device_seq_show(struc { struct list_head *node, *next; - seq_printf(seq, "Device Sleep state Status\n"); + seq_printf(seq, "Device\tS-state\t Status Sysfs node\n"); spin_lock(&acpi_device_lock); list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); + struct device *ldev; if (!dev->wakeup.flags.valid) continue; spin_unlock(&acpi_device_lock); - seq_printf(seq, "%4s %4d %s%8s\n", + + ldev = acpi_get_physical_device(dev->handle); + seq_printf(seq, "%s\t S%d\t%c%-8s ", dev->pnp.bus_id, (u32) dev->wakeup.sleep_state, - dev->wakeup.flags.run_wake ? "*" : "", + dev->wakeup.flags.run_wake ? '*' : ' ', dev->wakeup.state.enabled ? "enabled" : "disabled"); + if (ldev) + seq_printf(seq, "%s:%s", + ldev->bus ? ldev->bus->name : "no-bus", + ldev->bus_id); + seq_printf(seq, "\n"); + put_device(ldev); + spin_lock(&acpi_device_lock); } spin_unlock(&acpi_device_lock); diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index 807c711..1db833e 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -347,6 +347,18 @@ static void acpi_tb_convert_fadt(void) acpi_gbl_xpm1b_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id; } + + /* + * For ACPI 1.0 FADTs, ensure that reserved fields (which should be zero) + * are indeed zero. This will workaround BIOSs that inadvertently placed + * values in these fields. + */ + if (acpi_gbl_FADT.header.revision < 3) { + acpi_gbl_FADT.preferred_profile = 0; + acpi_gbl_FADT.pstate_control = 0; + acpi_gbl_FADT.cst_control = 0; + acpi_gbl_FADT.boot_flags = 0; + } } /****************************************************************************** diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index fd54750..268e301 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -47,14 +47,13 @@ #ifdef CONFIG_HOTPLUG static int amba_uevent(struct device *dev, char **envp, int nr_env, char *buf, int bufsz) { struct amba_device *pcdev = to_amba_device(dev); + int retval = 0, i = 0, len = 0; - if (nr_env < 2) - return -ENOMEM; - - snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid); - *envp++ = buf; - *envp++ = NULL; - return 0; + retval = add_uevent_var(envp, nr_env, &i, + buf, bufsz, &len, + "AMBA_ID=%08x", pcdev->periphid); + envp[i] = NULL; + return retval; } #else #define amba_uevent NULL diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 7bdbe5a..365c306 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -156,11 +156,6 @@ config SATA_INIC162X help This option enables support for Initio 162x Serial ATA. -config SATA_INTEL_COMBINED - bool - depends on IDE=y && !BLK_DEV_IDE_SATA && (SATA_AHCI || ATA_PIIX) - default y - config SATA_ACPI bool depends on ACPI && PCI @@ -184,7 +179,7 @@ config PATA_ALI If unsure, say N. config PATA_AMD - tristate "AMD/NVidia PATA support (Experimental)" + tristate "AMD/NVidia PATA support" depends on PCI help This option enables support for the AMD and NVidia PATA @@ -209,6 +204,16 @@ config PATA_ATIIXP If unsure, say N. +config PATA_CMD640_PCI + tristate "CMD640 PCI PATA support (Very Experimental)" + depends on PCI && EXPERIMENTAL + help + This option enables support for the CMD640 PCI IDE + interface chip. Only the primary channel is currently + supported. + + If unsure, say N. + config PATA_CMD64X tristate "CMD64x PATA support (Very Experimental)" depends on PCI&& EXPERIMENTAL @@ -273,7 +278,7 @@ config ATA_GENERIC If unsure, say N. config PATA_HPT366 - tristate "HPT 366/368 PATA support (Very Experimental)" + tristate "HPT 366/368 PATA support (Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the HPT 366 and 368 @@ -282,7 +287,7 @@ config PATA_HPT366 If unsure, say N. config PATA_HPT37X - tristate "HPT 370/370A/371/372/374/302 PATA support (Very Experimental)" + tristate "HPT 370/370A/371/372/374/302 PATA support (Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the majority of the later HPT @@ -309,7 +314,7 @@ config PATA_HPT3X3 If unsure, say N. config PATA_ISAPNP - tristate "ISA Plug and Play PATA support (Very Experimental)" + tristate "ISA Plug and Play PATA support (Experimental)" depends on EXPERIMENTAL && ISAPNP help This option enables support for ISA plug & play ATA @@ -318,8 +323,8 @@ config PATA_ISAPNP If unsure, say N. config PATA_IT821X - tristate "IT8211/2 PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "IT8211/2 PATA support" + depends on PCI help This option enables support for the ITE 8211 and 8212 PATA controllers via the new ATA layer, including RAID @@ -390,10 +395,10 @@ config PATA_MPIIX If unsure, say N. config PATA_OLDPIIX - tristate "Intel PATA old PIIX support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "Intel PATA old PIIX support" + depends on PCI help - This option enables support for old(?) PIIX PATA support. + This option enables support for early PIIX PATA support. If unsure, say N. @@ -444,7 +449,7 @@ config PATA_PCMCIA If unsure, say N. config PATA_PDC_OLD - tristate "Older Promise PATA controller support (Very Experimental)" + tristate "Older Promise PATA controller support (Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the Promise 20246, 20262, 20263, @@ -459,7 +464,7 @@ config PATA_QDI Support for QDI 6500 and 6580 PATA controllers on VESA local bus. config PATA_RADISYS - tristate "RADISYS 82600 PATA support (Very experimental)" + tristate "RADISYS 82600 PATA support (Very Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the RADISYS 82600 @@ -477,7 +482,7 @@ config PATA_RZ1000 If unsure, say N. config PATA_SC1200 - tristate "SC1200 PATA support (Raving Lunatic)" + tristate "SC1200 PATA support (Very Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the NatSemi/AMD SC1200 SoC @@ -486,8 +491,8 @@ config PATA_SC1200 If unsure, say N. config PATA_SERVERWORKS - tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support" + depends on PCI help This option enables support for the Serverworks OSB4/CSB5/CSB6 and HT1000 PATA controllers, via the new ATA layer. diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 13d7397..b7055e3 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_PATA_ALI) += pata_ali.o obj-$(CONFIG_PATA_AMD) += pata_amd.o obj-$(CONFIG_PATA_ARTOP) += pata_artop.o obj-$(CONFIG_PATA_ATIIXP) += pata_atiixp.o +obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o obj-$(CONFIG_PATA_CMD64X) += pata_cmd64x.o obj-$(CONFIG_PATA_CS5520) += pata_cs5520.o obj-$(CONFIG_PATA_CS5530) += pata_cs5530.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index fd27227..34c5534 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -170,6 +170,10 @@ enum { AHCI_FLAG_IGN_IRQ_IF_ERR = (1 << 25), /* ignore IRQ_IF_ERR */ AHCI_FLAG_HONOR_PI = (1 << 26), /* honor PORTS_IMPL */ AHCI_FLAG_IGN_SERR_INTERNAL = (1 << 27), /* ignore SERR_INTERNAL */ + + AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | + ATA_FLAG_SKIP_D2H_BSY, }; struct ahci_cmd_hdr { @@ -188,8 +192,10 @@ struct ahci_sg { }; struct ahci_host_priv { - u32 cap; /* cache of HOST_CAP register */ - u32 port_map; /* cache of HOST_PORTS_IMPL reg */ + u32 cap; /* cap to use */ + u32 port_map; /* port map to use */ + u32 saved_cap; /* saved initial cap */ + u32 saved_port_map; /* saved initial port_map */ }; struct ahci_port_priv { @@ -209,7 +215,6 @@ static u32 ahci_scr_read (struct ata_por static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); -static irqreturn_t ahci_interrupt (int irq, void *dev_instance); static void ahci_irq_clear(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); @@ -263,7 +268,6 @@ static const struct ata_port_operations .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue, - .irq_handler = ahci_interrupt, .irq_clear = ahci_irq_clear, .irq_on = ata_dummy_irq_on, .irq_ack = ata_dummy_irq_ack, @@ -298,7 +302,6 @@ static const struct ata_port_operations .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue, - .irq_handler = ahci_interrupt, .irq_clear = ahci_irq_clear, .irq_on = ata_dummy_irq_on, .irq_ack = ata_dummy_irq_ack, @@ -324,58 +327,41 @@ #endif static const struct ata_port_info ahci_port_info[] = { /* board_ahci */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY, + .flags = AHCI_FLAG_COMMON, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, /* board_ahci_pi */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | AHCI_FLAG_HONOR_PI, + .flags = AHCI_FLAG_COMMON | AHCI_FLAG_HONOR_PI, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, /* board_ahci_vt8251 */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | - ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_HRST_TO_RESUME | + AHCI_FLAG_NO_NCQ, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_vt8251_ops, }, /* board_ahci_ign_iferr */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | - AHCI_FLAG_IGN_IRQ_IF_ERR, + .flags = AHCI_FLAG_COMMON | AHCI_FLAG_IGN_IRQ_IF_ERR, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, /* board_ahci_sb600 */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | + .flags = AHCI_FLAG_COMMON | AHCI_FLAG_IGN_SERR_INTERNAL, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, - }; static const struct pci_device_id ahci_pci_tbl[] = { @@ -413,11 +399,11 @@ static const struct pci_device_id ahci_p PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr }, /* ATI */ - { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 non-raid */ - { PCI_VDEVICE(ATI, 0x4381), board_ahci }, /* ATI SB600 raid */ + { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ /* VIA */ { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ + { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */ /* NVIDIA */ { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */ @@ -471,10 +457,100 @@ static inline int ahci_nr_ports(u32 cap) return (cap & 0x1f) + 1; } -static inline void __iomem *ahci_port_base(void __iomem *base, - unsigned int port) +static inline void __iomem *ahci_port_base(struct ata_port *ap) +{ + void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; + + return mmio + 0x100 + (ap->port_no * 0x80); +} + +/** + * ahci_save_initial_config - Save and fixup initial config values + * @pdev: target PCI device + * @pi: associated ATA port info + * @hpriv: host private area to store config values + * + * Some registers containing configuration info might be setup by + * BIOS and might be cleared on reset. This function saves the + * initial values of those registers into @hpriv such that they + * can be restored after controller reset. + * + * If inconsistent, config values are fixed up by this function. + * + * LOCKING: + * None. + */ +static void ahci_save_initial_config(struct pci_dev *pdev, + const struct ata_port_info *pi, + struct ahci_host_priv *hpriv) { - return base + 0x100 + (port * 0x80); + void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; + u32 cap, port_map; + int i; + + /* Values prefixed with saved_ are written back to host after + * reset. Values without are used for driver operation. + */ + hpriv->saved_cap = cap = readl(mmio + HOST_CAP); + hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); + + /* fixup zero port_map */ + if (!port_map) { + port_map = (1 << ahci_nr_ports(hpriv->cap)) - 1; + dev_printk(KERN_WARNING, &pdev->dev, + "PORTS_IMPL is zero, forcing 0x%x\n", port_map); + + /* write the fixed up value to the PI register */ + hpriv->saved_port_map = port_map; + } + + /* cross check port_map and cap.n_ports */ + if (pi->flags & AHCI_FLAG_HONOR_PI) { + u32 tmp_port_map = port_map; + int n_ports = ahci_nr_ports(cap); + + for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { + if (tmp_port_map & (1 << i)) { + n_ports--; + tmp_port_map &= ~(1 << i); + } + } + + /* Whine if inconsistent. No need to update cap. + * port_map is used to determine number of ports. + */ + if (n_ports || tmp_port_map) + dev_printk(KERN_WARNING, &pdev->dev, + "nr_ports (%u) and implemented port map " + "(0x%x) don't match\n", + ahci_nr_ports(cap), port_map); + } else { + /* fabricate port_map from cap.nr_ports */ + port_map = (1 << ahci_nr_ports(cap)) - 1; + } + + /* record values to use during operation */ + hpriv->cap = cap; + hpriv->port_map = port_map; +} + +/** + * ahci_restore_initial_config - Restore initial config + * @host: target ATA host + * + * Restore initial config stored by ahci_save_initial_config(). + * + * LOCKING: + * None. + */ +static void ahci_restore_initial_config(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; + + writel(hpriv->saved_cap, mmio + HOST_CAP); + writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); + (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ } static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) @@ -511,8 +587,9 @@ static void ahci_scr_write (struct ata_p writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); } -static void ahci_start_engine(void __iomem *port_mmio) +static void ahci_start_engine(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* start DMA */ @@ -522,8 +599,9 @@ static void ahci_start_engine(void __iom readl(port_mmio + PORT_CMD); /* flush */ } -static int ahci_stop_engine(void __iomem *port_mmio) +static int ahci_stop_engine(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; tmp = readl(port_mmio + PORT_CMD); @@ -545,19 +623,23 @@ static int ahci_stop_engine(void __iomem return 0; } -static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap, - dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +static void ahci_start_fis_rx(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; u32 tmp; /* set FIS registers */ - if (cap & HOST_CAP_64) - writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); - writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + if (hpriv->cap & HOST_CAP_64) + writel((pp->cmd_slot_dma >> 16) >> 16, + port_mmio + PORT_LST_ADDR_HI); + writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); - if (cap & HOST_CAP_64) - writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); - writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + if (hpriv->cap & HOST_CAP_64) + writel((pp->rx_fis_dma >> 16) >> 16, + port_mmio + PORT_FIS_ADDR_HI); + writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); /* enable FIS reception */ tmp = readl(port_mmio + PORT_CMD); @@ -568,8 +650,9 @@ static void ahci_start_fis_rx(void __iom readl(port_mmio + PORT_CMD); } -static int ahci_stop_fis_rx(void __iomem *port_mmio) +static int ahci_stop_fis_rx(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* disable FIS reception */ @@ -586,14 +669,16 @@ static int ahci_stop_fis_rx(void __iomem return 0; } -static void ahci_power_up(void __iomem *port_mmio, u32 cap) +static void ahci_power_up(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); u32 cmd; cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; /* spin up device */ - if (cap & HOST_CAP_SSS) { + if (hpriv->cap & HOST_CAP_SSS) { cmd |= PORT_CMD_SPIN_UP; writel(cmd, port_mmio + PORT_CMD); } @@ -603,11 +688,13 @@ static void ahci_power_up(void __iomem * } #ifdef CONFIG_PM -static void ahci_power_down(void __iomem *port_mmio, u32 cap) +static void ahci_power_down(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); u32 cmd, scontrol; - if (!(cap & HOST_CAP_SSS)) + if (!(hpriv->cap & HOST_CAP_SSS)) return; /* put device into listen mode, first set PxSCTL.DET to 0 */ @@ -622,29 +709,28 @@ static void ahci_power_down(void __iomem } #endif -static void ahci_init_port(void __iomem *port_mmio, u32 cap, - dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +static void ahci_init_port(struct ata_port *ap) { /* enable FIS reception */ - ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma); + ahci_start_fis_rx(ap); /* enable DMA */ - ahci_start_engine(port_mmio); + ahci_start_engine(ap); } -static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) +static int ahci_deinit_port(struct ata_port *ap, const char **emsg) { int rc; /* disable DMA */ - rc = ahci_stop_engine(port_mmio); + rc = ahci_stop_engine(ap); if (rc) { *emsg = "failed to stop engine"; return rc; } /* disable FIS reception */ - rc = ahci_stop_fis_rx(port_mmio); + rc = ahci_stop_fis_rx(ap); if (rc) { *emsg = "failed stop FIS RX"; return rc; @@ -653,12 +739,11 @@ static int ahci_deinit_port(void __iomem return 0; } -static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) +static int ahci_reset_controller(struct ata_host *host) { - u32 cap_save, impl_save, tmp; - - cap_save = readl(mmio + HOST_CAP); - impl_save = readl(mmio + HOST_PORTS_IMPL); + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; + u32 tmp; /* global controller reset */ tmp = readl(mmio + HOST_CTL); @@ -674,7 +759,7 @@ static int ahci_reset_controller(void __ tmp = readl(mmio + HOST_CTL); if (tmp & HOST_RESET) { - dev_printk(KERN_ERR, &pdev->dev, + dev_printk(KERN_ERR, host->dev, "controller reset failed (0x%x)\n", tmp); return -EIO; } @@ -683,18 +768,8 @@ static int ahci_reset_controller(void __ writel(HOST_AHCI_EN, mmio + HOST_CTL); (void) readl(mmio + HOST_CTL); /* flush */ - /* These write-once registers are normally cleared on reset. - * Restore BIOS values... which we HOPE were present before - * reset. - */ - if (!impl_save) { - impl_save = (1 << ahci_nr_ports(cap_save)) - 1; - dev_printk(KERN_WARNING, &pdev->dev, - "PORTS_IMPL is zero, forcing 0x%x\n", impl_save); - } - writel(cap_save, mmio + HOST_CAP); - writel(impl_save, mmio + HOST_PORTS_IMPL); - (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + /* some registers might be cleared on reset. restore initial values */ + ahci_restore_initial_config(host); if (pdev->vendor == PCI_VENDOR_ID_INTEL) { u16 tmp16; @@ -708,23 +783,23 @@ static int ahci_reset_controller(void __ return 0; } -static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, - int n_ports, unsigned int port_flags, - struct ahci_host_priv *hpriv) +static void ahci_init_controller(struct ata_host *host) { + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; int i, rc; u32 tmp; - for (i = 0; i < n_ports; i++) { - void __iomem *port_mmio = ahci_port_base(mmio, i); + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + void __iomem *port_mmio = ahci_port_base(ap); const char *emsg = NULL; - if ((port_flags & AHCI_FLAG_HONOR_PI) && - !(hpriv->port_map & (1 << i))) + if (ata_port_is_dummy(ap)) continue; /* make sure port is not active */ - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + rc = ahci_deinit_port(ap, &emsg); if (rc) dev_printk(KERN_WARNING, &pdev->dev, "%s (%d)\n", emsg, rc); @@ -752,7 +827,7 @@ static void ahci_init_controller(void __ static unsigned int ahci_dev_classify(struct ata_port *ap) { - void __iomem *port_mmio = ap->ioaddr.cmd_addr; + void __iomem *port_mmio = ahci_port_base(ap); struct ata_taskfile tf; u32 tmp; @@ -802,8 +877,7 @@ static int ahci_clo(struct ata_port *ap) static int ahci_softreset(struct ata_port *ap, unsigned int *class) { struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ahci_port_base(ap); const u32 cmd_fis_len = 5; /* five dwords */ const char *reason = NULL; struct ata_taskfile tf; @@ -820,7 +894,7 @@ static int ahci_softreset(struct ata_por } /* prepare for SRST (AHCI-1.1 10.4.1) */ - rc = ahci_stop_engine(port_mmio); + rc = ahci_stop_engine(ap); if (rc) { reason = "failed to stop engine"; goto fail_restart; @@ -840,7 +914,7 @@ static int ahci_softreset(struct ata_por } /* restart engine */ - ahci_start_engine(port_mmio); + ahci_start_engine(ap); ata_tf_init(ap->device, &tf); fis = pp->cmd_tbl; @@ -899,7 +973,7 @@ static int ahci_softreset(struct ata_por return 0; fail_restart: - ahci_start_engine(port_mmio); + ahci_start_engine(ap); fail: ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); return rc; @@ -910,13 +984,11 @@ static int ahci_hardreset(struct ata_por struct ahci_port_priv *pp = ap->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); int rc; DPRINTK("ENTER\n"); - ahci_stop_engine(port_mmio); + ahci_stop_engine(ap); /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(ap->device, &tf); @@ -925,7 +997,7 @@ static int ahci_hardreset(struct ata_por rc = sata_std_hardreset(ap, class); - ahci_start_engine(port_mmio); + ahci_start_engine(ap); if (rc == 0 && ata_port_online(ap)) *class = ahci_dev_classify(ap); @@ -938,20 +1010,18 @@ static int ahci_hardreset(struct ata_por static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); int rc; DPRINTK("ENTER\n"); - ahci_stop_engine(port_mmio); + ahci_stop_engine(ap); rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context)); /* vt8251 needs SError cleared for the port to operate */ ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR)); - ahci_start_engine(port_mmio); + ahci_start_engine(ap); DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); @@ -963,7 +1033,7 @@ static int ahci_vt8251_hardreset(struct static void ahci_postreset(struct ata_port *ap, unsigned int *class) { - void __iomem *port_mmio = ap->ioaddr.cmd_addr; + void __iomem *port_mmio = ahci_port_base(ap); u32 new_tmp, tmp; ata_std_postreset(ap, class); @@ -1131,8 +1201,7 @@ static void ahci_error_intr(struct ata_p static void ahci_host_intr(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ap->ioaddr.cmd_addr; struct ata_eh_info *ehi = &ap->eh_info; struct ahci_port_priv *pp = ap->private_data; u32 status, qc_active; @@ -1283,7 +1352,7 @@ static irqreturn_t ahci_interrupt(int ir static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - void __iomem *port_mmio = ap->ioaddr.cmd_addr; + void __iomem *port_mmio = ahci_port_base(ap); if (qc->tf.protocol == ATA_PROT_NCQ) writel(1 << qc->tag, port_mmio + PORT_SCR_ACT); @@ -1295,8 +1364,7 @@ static unsigned int ahci_qc_issue(struct static void ahci_freeze(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ahci_port_base(ap); /* turn IRQ off */ writel(0, port_mmio + PORT_IRQ_MASK); @@ -1305,7 +1373,7 @@ static void ahci_freeze(struct ata_port static void ahci_thaw(struct ata_port *ap) { void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* clear IRQ */ @@ -1319,13 +1387,10 @@ static void ahci_thaw(struct ata_port *a static void ahci_error_handler(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); + ahci_stop_engine(ap); + ahci_start_engine(ap); } /* perform recovery */ @@ -1335,13 +1400,10 @@ static void ahci_error_handler(struct at static void ahci_vt8251_error_handler(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); + ahci_stop_engine(ap); + ahci_start_engine(ap); } /* perform recovery */ @@ -1352,36 +1414,26 @@ static void ahci_vt8251_error_handler(st static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - - if (qc->err_mask) { + if (qc->flags & ATA_QCFLAG_FAILED) { /* make DMA engine forget about the failed command */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); + ahci_stop_engine(ap); + ahci_start_engine(ap); } } #ifdef CONFIG_PM static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) { - struct ahci_host_priv *hpriv = ap->host->private_data; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); const char *emsg = NULL; int rc; - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + rc = ahci_deinit_port(ap, &emsg); if (rc == 0) - ahci_power_down(port_mmio, hpriv->cap); + ahci_power_down(ap); else { ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); - ahci_init_port(port_mmio, hpriv->cap, - pp->cmd_slot_dma, pp->rx_fis_dma); + ahci_init_port(ap); } return rc; @@ -1389,13 +1441,8 @@ static int ahci_port_suspend(struct ata_ static int ahci_port_resume(struct ata_port *ap) { - struct ahci_port_priv *pp = ap->private_data; - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - - ahci_power_up(port_mmio, hpriv->cap); - ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); + ahci_power_up(ap); + ahci_init_port(ap); return 0; } @@ -1423,8 +1470,6 @@ static int ahci_pci_device_suspend(struc static int ahci_pci_device_resume(struct pci_dev *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); - struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; int rc; rc = ata_pci_device_do_resume(pdev); @@ -1432,12 +1477,11 @@ static int ahci_pci_device_resume(struct return rc; if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(mmio, pdev); + rc = ahci_reset_controller(host); if (rc) return rc; - ahci_init_controller(mmio, pdev, host->n_ports, - host->ports[0]->flags, hpriv); + ahci_init_controller(host); } ata_host_resume(host); @@ -1449,10 +1493,7 @@ #endif static int ahci_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; - struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); void *mem; dma_addr_t mem_dma; int rc; @@ -1500,85 +1541,29 @@ static int ahci_port_start(struct ata_po ap->private_data = pp; /* power up port */ - ahci_power_up(port_mmio, hpriv->cap); + ahci_power_up(ap); /* initialize port */ - ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); + ahci_init_port(ap); return 0; } static void ahci_port_stop(struct ata_port *ap) { - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); const char *emsg = NULL; int rc; /* de-initialize port */ - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + rc = ahci_deinit_port(ap, &emsg); if (rc) ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); } -static void ahci_setup_port(struct ata_ioports *port, void __iomem *base, - unsigned int port_idx) +static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) { - VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx); - base = ahci_port_base(base, port_idx); - VPRINTK("base now==0x%lx\n", base); - - port->cmd_addr = base; - port->scr_addr = base + PORT_SCR; - - VPRINTK("EXIT\n"); -} - -static int ahci_host_init(struct ata_probe_ent *probe_ent) -{ - struct ahci_host_priv *hpriv = probe_ent->private_data; - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR]; - unsigned int i, cap_n_ports, using_dac; int rc; - rc = ahci_reset_controller(mmio, pdev); - if (rc) - return rc; - - hpriv->cap = readl(mmio + HOST_CAP); - hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); - cap_n_ports = ahci_nr_ports(hpriv->cap); - - VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n", - hpriv->cap, hpriv->port_map, cap_n_ports); - - if (probe_ent->port_flags & AHCI_FLAG_HONOR_PI) { - unsigned int n_ports = cap_n_ports; - u32 port_map = hpriv->port_map; - int max_port = 0; - - for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { - if (port_map & (1 << i)) { - n_ports--; - port_map &= ~(1 << i); - max_port = i; - } else - probe_ent->dummy_port_mask |= 1 << i; - } - - if (n_ports || port_map) - dev_printk(KERN_WARNING, &pdev->dev, - "nr_ports (%u) and implemented port map " - "(0x%x) don't match\n", - cap_n_ports, hpriv->port_map); - - probe_ent->n_ports = max_port + 1; - } else - probe_ent->n_ports = cap_n_ports; - - using_dac = hpriv->cap & HOST_CAP_64; if (using_dac && !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); @@ -1604,23 +1589,14 @@ static int ahci_host_init(struct ata_pro return rc; } } - - for (i = 0; i < probe_ent->n_ports; i++) - ahci_setup_port(&probe_ent->port[i], mmio, i); - - ahci_init_controller(mmio, pdev, probe_ent->n_ports, - probe_ent->port_flags, hpriv); - - pci_set_master(pdev); - return 0; } -static void ahci_print_info(struct ata_probe_ent *probe_ent) +static void ahci_print_info(struct ata_host *host) { - struct ahci_host_priv *hpriv = probe_ent->private_data; - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR]; + struct ahci_host_priv *hpriv = host->private_data; + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; u32 vers, cap, impl, speed; const char *speed_s; u16 cc; @@ -1690,11 +1666,12 @@ static void ahci_print_info(struct ata_p static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - unsigned int board_idx = (unsigned int) ent->driver_data; + struct ata_port_info pi = ahci_port_info[ent->driver_data]; + const struct ata_port_info *ppi[] = { &pi, NULL }; struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; struct ahci_host_priv *hpriv; - int rc; + struct ata_host *host; + int i, rc; VPRINTK("ENTER\n"); @@ -1703,6 +1680,7 @@ static int ahci_init_one(struct pci_dev if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -1716,44 +1694,49 @@ static int ahci_init_one(struct pci_dev if (pci_enable_msi(pdev)) pci_intx(pdev, 1); - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; - probe_ent->sht = ahci_port_info[board_idx].sht; - probe_ent->port_flags = ahci_port_info[board_idx].flags; - probe_ent->pio_mask = ahci_port_info[board_idx].pio_mask; - probe_ent->udma_mask = ahci_port_info[board_idx].udma_mask; - probe_ent->port_ops = ahci_port_info[board_idx].port_ops; + /* save initial config */ + ahci_save_initial_config(pdev, &pi, hpriv); - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - probe_ent->private_data = hpriv; + /* prepare host */ + if (!(pi.flags & AHCI_FLAG_NO_NCQ) && (hpriv->cap & HOST_CAP_NCQ)) + pi.flags |= ATA_FLAG_NCQ; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map)); + if (!host) + return -ENOMEM; + host->iomap = pcim_iomap_table(pdev); + host->private_data = hpriv; + + for (i = 0; i < host->n_ports; i++) { + if (hpriv->port_map & (1 << i)) { + struct ata_port *ap = host->ports[i]; + void __iomem *port_mmio = ahci_port_base(ap); + + ap->ioaddr.cmd_addr = port_mmio; + ap->ioaddr.scr_addr = port_mmio + PORT_SCR; + } else + host->ports[i]->ops = &ata_dummy_port_ops; + } /* initialize adapter */ - rc = ahci_host_init(probe_ent); + rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); if (rc) return rc; - if (!(probe_ent->port_flags & AHCI_FLAG_NO_NCQ) && - (hpriv->cap & HOST_CAP_NCQ)) - probe_ent->port_flags |= ATA_FLAG_NCQ; - - ahci_print_info(probe_ent); + rc = ahci_reset_controller(host); + if (rc) + return rc; - if (!ata_device_add(probe_ent)) - return -ENODEV; + ahci_init_controller(host); + ahci_print_info(host); - devm_kfree(dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, + &ahci_sht); } static int __init ahci_init(void) diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c index d8e7988..92a491d 100644 --- a/drivers/ata/ata_generic.c +++ b/drivers/ata/ata_generic.c @@ -33,35 +33,6 @@ #define DRV_VERSION "0.2.11" */ /** - * generic_pre_reset - probe begin - * @ap: ATA port - * - * Set up cable type and use generic probe init - */ - -static int generic_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - - -/** - * generic_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * @classes: - * - * LOCKING: - * None (inherited from caller). - */ - - -static void generic_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, generic_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - -/** * generic_set_mode - mode setting * @ap: interface to set up * @unused: returned device on error @@ -144,8 +115,9 @@ static struct ata_port_operations generi .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = generic_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index b952c58..55d306a 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -93,7 +93,7 @@ #include #include #define DRV_NAME "ata_piix" -#define DRV_VERSION "2.10ac1" +#define DRV_VERSION "2.11" enum { PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ @@ -155,11 +155,11 @@ struct piix_host_priv { static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static void piix_pata_error_handler(struct ata_port *ap); -static void ich_pata_error_handler(struct ata_port *ap); static void piix_sata_error_handler(struct ata_port *ap); static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev); static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev); static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev); +static int ich_pata_cable_detect(struct ata_port *ap); static unsigned int in_module_init = 1; @@ -305,6 +305,7 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = piix_pata_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, @@ -336,8 +337,9 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ich_pata_error_handler, + .error_handler = piix_pata_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ich_pata_cable_detect, .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, @@ -580,12 +582,13 @@ static const struct ich_laptop ich_lapto /* devid, subvendor, subdev */ { 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */ { 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */ + { 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */ /* end marker */ { 0, } }; /** - * piix_pata_cbl_detect - Probe host controller cable detect info + * ich_pata_cable_detect - Probe host controller cable detect info * @ap: Port for which cable detect info is desired * * Read 80c cable indicator from ATA PCI device's PCI config @@ -595,23 +598,18 @@ static const struct ich_laptop ich_lapto * None (inherited from caller). */ -static void ich_pata_cbl_detect(struct ata_port *ap) +static int ich_pata_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); const struct ich_laptop *lap = &ich_laptop[0]; u8 tmp, mask; - /* no 80c support in host controller? */ - if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0) - goto cbl40; - /* Check for specials - Acer Aspire 5602WLMi */ while (lap->device) { if (lap->device == pdev->device && lap->subvendor == pdev->subsystem_vendor && lap->subdevice == pdev->subsystem_device) { - ap->cbl = ATA_CBL_PATA40_SHORT; - return; + return ATA_CBL_PATA40_SHORT; } lap++; } @@ -620,20 +618,14 @@ static void ich_pata_cbl_detect(struct a mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC; pci_read_config_byte(pdev, PIIX_IOCFG, &tmp); if ((tmp & mask) == 0) - goto cbl40; - - ap->cbl = ATA_CBL_PATA80; - return; - -cbl40: - ap->cbl = ATA_CBL_PATA40; + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } /** * piix_pata_prereset - prereset for PATA host controller * @ap: Target port * - * * LOCKING: * None (inherited from caller). */ @@ -643,8 +635,6 @@ static int piix_pata_prereset(struct ata if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) return -ENOENT; - - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -655,30 +645,6 @@ static void piix_pata_error_handler(stru } -/** - * ich_pata_prereset - prereset for PATA host controller - * @ap: Target port - * - * - * LOCKING: - * None (inherited from caller). - */ -static int ich_pata_prereset(struct ata_port *ap) -{ - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - - if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) - return -ENOENT; - ich_pata_cbl_detect(ap); - return ata_std_prereset(ap); -} - -static void ich_pata_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, ich_pata_prereset, ata_std_softreset, NULL, - ata_std_postreset); -} - static void piix_sata_error_handler(struct ata_port *ap) { ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, NULL, diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 0abd72d..ca67484 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -72,7 +72,7 @@ static unsigned int ata_dev_init_params( static unsigned int ata_dev_set_xfermode(struct ata_device *dev); static void ata_dev_xfermask(struct ata_device *dev); -static unsigned int ata_print_id = 1; +unsigned int ata_print_id = 1; static struct workqueue_struct *ata_wq; struct workqueue_struct *ata_aux_wq; @@ -89,6 +89,10 @@ int libata_fua = 0; module_param_named(fua, libata_fua, int, 0444); MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)"); +static int ata_ignore_hpa = 0; +module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644); +MODULE_PARM_DESC(ignore_hpa, "Ignore HPA limit (0=keep BIOS limits, 1=ignore limits, using full disk)"); + static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ; module_param(ata_probe_timeout, int, 0444); MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)"); @@ -808,6 +812,205 @@ void ata_id_c_string(const u16 *id, unsi *p = '\0'; } +static u64 ata_tf_to_lba48(struct ata_taskfile *tf) +{ + u64 sectors = 0; + + sectors |= ((u64)(tf->hob_lbah & 0xff)) << 40; + sectors |= ((u64)(tf->hob_lbam & 0xff)) << 32; + sectors |= (tf->hob_lbal & 0xff) << 24; + sectors |= (tf->lbah & 0xff) << 16; + sectors |= (tf->lbam & 0xff) << 8; + sectors |= (tf->lbal & 0xff); + + return ++sectors; +} + +static u64 ata_tf_to_lba(struct ata_taskfile *tf) +{ + u64 sectors = 0; + + sectors |= (tf->device & 0x0f) << 24; + sectors |= (tf->lbah & 0xff) << 16; + sectors |= (tf->lbam & 0xff) << 8; + sectors |= (tf->lbal & 0xff); + + return ++sectors; +} + +/** + * ata_read_native_max_address_ext - LBA48 native max query + * @dev: Device to query + * + * Perform an LBA48 size query upon the device in question. Return the + * actual LBA48 size or zero if the command fails. + */ + +static u64 ata_read_native_max_address_ext(struct ata_device *dev) +{ + unsigned int err; + struct ata_taskfile tf; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_READ_NATIVE_MAX_EXT; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + tf.device |= 0x40; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba48(&tf); +} + +/** + * ata_read_native_max_address - LBA28 native max query + * @dev: Device to query + * + * Performa an LBA28 size query upon the device in question. Return the + * actual LBA28 size or zero if the command fails. + */ + +static u64 ata_read_native_max_address(struct ata_device *dev) +{ + unsigned int err; + struct ata_taskfile tf; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_READ_NATIVE_MAX; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + tf.device |= 0x40; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba(&tf); +} + +/** + * ata_set_native_max_address_ext - LBA48 native max set + * @dev: Device to query + * + * Perform an LBA48 size set max upon the device in question. Return the + * actual LBA48 size or zero if the command fails. + */ + +static u64 ata_set_native_max_address_ext(struct ata_device *dev, u64 new_sectors) +{ + unsigned int err; + struct ata_taskfile tf; + + new_sectors--; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_SET_MAX_EXT; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + tf.device |= 0x40; + + tf.lbal = (new_sectors >> 0) & 0xff; + tf.lbam = (new_sectors >> 8) & 0xff; + tf.lbah = (new_sectors >> 16) & 0xff; + + tf.hob_lbal = (new_sectors >> 24) & 0xff; + tf.hob_lbam = (new_sectors >> 32) & 0xff; + tf.hob_lbah = (new_sectors >> 40) & 0xff; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba48(&tf); +} + +/** + * ata_set_native_max_address - LBA28 native max set + * @dev: Device to query + * + * Perform an LBA28 size set max upon the device in question. Return the + * actual LBA28 size or zero if the command fails. + */ + +static u64 ata_set_native_max_address(struct ata_device *dev, u64 new_sectors) +{ + unsigned int err; + struct ata_taskfile tf; + + new_sectors--; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_SET_MAX; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + + tf.lbal = (new_sectors >> 0) & 0xff; + tf.lbam = (new_sectors >> 8) & 0xff; + tf.lbah = (new_sectors >> 16) & 0xff; + tf.device |= ((new_sectors >> 24) & 0x0f) | 0x40; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba(&tf); +} + +/** + * ata_hpa_resize - Resize a device with an HPA set + * @dev: Device to resize + * + * Read the size of an LBA28 or LBA48 disk with HPA features and resize + * it if required to the full size of the media. The caller must check + * the drive has the HPA feature set enabled. + */ + +static u64 ata_hpa_resize(struct ata_device *dev) +{ + u64 sectors = dev->n_sectors; + u64 hpa_sectors; + + if (ata_id_has_lba48(dev->id)) + hpa_sectors = ata_read_native_max_address_ext(dev); + else + hpa_sectors = ata_read_native_max_address(dev); + + /* if no hpa, both should be equal */ + ata_dev_printk(dev, KERN_INFO, "%s 1: sectors = %lld, " + "hpa_sectors = %lld\n", + __FUNCTION__, (long long)sectors, (long long)hpa_sectors); + + if (hpa_sectors > sectors) { + ata_dev_printk(dev, KERN_INFO, + "Host Protected Area detected:\n" + "\tcurrent size: %lld sectors\n" + "\tnative size: %lld sectors\n", + (long long)sectors, (long long)hpa_sectors); + + if (ata_ignore_hpa) { + if (ata_id_has_lba48(dev->id)) + hpa_sectors = ata_set_native_max_address_ext(dev, hpa_sectors); + else + hpa_sectors = ata_set_native_max_address(dev, + hpa_sectors); + + if (hpa_sectors) { + ata_dev_printk(dev, KERN_INFO, "native size " + "increased to %lld sectors\n", + (long long)hpa_sectors); + return hpa_sectors; + } + } + } + return sectors; +} + static u64 ata_id_n_sectors(const u16 *id) { if (ata_id_has_lba(id)) { @@ -1270,12 +1473,16 @@ unsigned ata_exec_internal_sg(struct ata if (ap->ops->post_internal_cmd) ap->ops->post_internal_cmd(qc); - if ((qc->flags & ATA_QCFLAG_FAILED) && !qc->err_mask) { - if (ata_msg_warn(ap)) - ata_dev_printk(dev, KERN_WARNING, - "zero err_mask for failed " - "internal command, assuming AC_ERR_OTHER\n"); - qc->err_mask |= AC_ERR_OTHER; + /* perform minimal error analysis */ + if (qc->flags & ATA_QCFLAG_FAILED) { + if (qc->result_tf.command & (ATA_ERR | ATA_DF)) + qc->err_mask |= AC_ERR_DEV; + + if (!qc->err_mask) + qc->err_mask |= AC_ERR_OTHER; + + if (qc->err_mask & ~AC_ERR_OTHER) + qc->err_mask &= ~AC_ERR_OTHER; } /* finish up */ @@ -1379,30 +1586,44 @@ unsigned int ata_do_simple_cmd(struct at * Check if the current speed of the device requires IORDY. Used * by various controllers for chip configuration. */ - + unsigned int ata_pio_need_iordy(const struct ata_device *adev) { - int pio; - int speed = adev->pio_mode - XFER_PIO_0; - - if (speed < 2) + /* Controller doesn't support IORDY. Probably a pointless check + as the caller should know this */ + if (adev->ap->flags & ATA_FLAG_NO_IORDY) return 0; - if (speed > 2) + /* PIO3 and higher it is mandatory */ + if (adev->pio_mode > XFER_PIO_2) + return 1; + /* We turn it on when possible */ + if (ata_id_has_iordy(adev->id)) return 1; + return 0; +} +/** + * ata_pio_mask_no_iordy - Return the non IORDY mask + * @adev: ATA device + * + * Compute the highest mode possible if we are not using iordy. Return + * -1 if no iordy mode is available. + */ + +static u32 ata_pio_mask_no_iordy(const struct ata_device *adev) +{ /* If we have no drive specific rule, then PIO 2 is non IORDY */ - if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE */ - pio = adev->id[ATA_ID_EIDE_PIO]; + u16 pio = adev->id[ATA_ID_EIDE_PIO]; /* Is the speed faster than the drive allows non IORDY ? */ if (pio) { /* This is cycle times not frequency - watch the logic! */ if (pio > 240) /* PIO2 is 240nS per cycle */ - return 1; - return 0; + return 3 << ATA_SHIFT_PIO; + return 7 << ATA_SHIFT_PIO; } } - return 0; + return 3 << ATA_SHIFT_PIO; } /** @@ -1431,13 +1652,13 @@ int ata_dev_read_id(struct ata_device *d struct ata_taskfile tf; unsigned int err_mask = 0; const char *reason; + int tried_spinup = 0; int rc; if (ata_msg_ctl(ap)) ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__); ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */ - retry: ata_tf_init(dev, &tf); @@ -1494,6 +1715,32 @@ int ata_dev_read_id(struct ata_device *d goto err_out; } + if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) { + tried_spinup = 1; + /* + * Drive powered-up in standby mode, and requires a specific + * SET_FEATURES spin-up subcommand before it will accept + * anything other than the original IDENTIFY command. + */ + ata_tf_init(dev, &tf); + tf.command = ATA_CMD_SET_FEATURES; + tf.feature = SETFEATURES_SPINUP; + tf.protocol = ATA_PROT_NODATA; + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err_mask) { + rc = -EIO; + reason = "SPINUP failed"; + goto err_out; + } + /* + * If the drive initially returned incomplete IDENTIFY info, + * we now must reissue the IDENTIFY command. + */ + if (id[2] == 0x37c8) + goto retry; + } + if ((flags & ATA_READID_POSTRESET) && class == ATA_DEV_ATA) { /* * The exact sequence expected by certain pre-ATA4 drives is: @@ -1560,20 +1807,6 @@ static void ata_dev_config_ncq(struct at snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth); } -static void ata_set_port_max_cmd_len(struct ata_port *ap) -{ - int i; - - if (ap->scsi_host) { - unsigned int len = 0; - - for (i = 0; i < ATA_MAX_DEVICES; i++) - len = max(len, ap->device[i].cdb_len); - - ap->scsi_host->max_cmd_len = len; - } -} - /** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure @@ -1658,6 +1891,7 @@ int ata_dev_configure(struct ata_device snprintf(revbuf, 7, "ATA-%d", ata_id_major_version(id)); dev->n_sectors = ata_id_n_sectors(id); + dev->n_sectors_boot = dev->n_sectors; /* SCSI only uses 4-char revisions, dump full 8 chars from ATA */ ata_id_c_string(dev->id, fwrevbuf, ATA_ID_FW_REV, @@ -1684,6 +1918,9 @@ int ata_dev_configure(struct ata_device dev->flags |= ATA_DFLAG_FLUSH_EXT; } + if (ata_id_hpa_enabled(dev->id)) + dev->n_sectors = ata_hpa_resize(dev); + /* config NCQ */ ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc)); @@ -1773,8 +2010,6 @@ int ata_dev_configure(struct ata_device } } - ata_set_port_max_cmd_len(ap); - /* limit bridge transfers to udma5, 200 sectors */ if (ata_dev_knobble(dev)) { if (ata_msg_drv(ap) && print_info) @@ -1785,14 +2020,15 @@ int ata_dev_configure(struct ata_device } if (ata_device_blacklisted(dev) & ATA_HORKAGE_MAX_SEC_128) - dev->max_sectors = min(ATA_MAX_SECTORS_128, dev->max_sectors); + dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128, + dev->max_sectors); /* limit ATAPI DMA to R/W commands only */ if (ata_device_blacklisted(dev) & ATA_HORKAGE_DMA_RW_ONLY) dev->horkage |= ATA_HORKAGE_DMA_RW_ONLY; if (ap->ops->dev_config) - ap->ops->dev_config(ap, dev); + ap->ops->dev_config(dev); if (ata_msg_probe(ap)) ata_dev_printk(dev, KERN_DEBUG, "%s: EXIT, drv_stat = 0x%x\n", @@ -1807,6 +2043,56 @@ err_out_nosup: } /** + * ata_cable_40wire - return 40 wire cable type + * @ap: port + * + * Helper method for drivers which want to hardwire 40 wire cable + * detection. + */ + +int ata_cable_40wire(struct ata_port *ap) +{ + return ATA_CBL_PATA40; +} + +/** + * ata_cable_80wire - return 80 wire cable type + * @ap: port + * + * Helper method for drivers which want to hardwire 80 wire cable + * detection. + */ + +int ata_cable_80wire(struct ata_port *ap) +{ + return ATA_CBL_PATA80; +} + +/** + * ata_cable_unknown - return unknown PATA cable. + * @ap: port + * + * Helper method for drivers which have no PATA cable detection. + */ + +int ata_cable_unknown(struct ata_port *ap) +{ + return ATA_CBL_PATA_UNK; +} + +/** + * ata_cable_sata - return SATA cable type + * @ap: port + * + * Helper method for drivers which have SATA cables + */ + +int ata_cable_sata(struct ata_port *ap) +{ + return ATA_CBL_SATA; +} + +/** * ata_bus_probe - Reset and probe ATA bus * @ap: Bus to probe * @@ -1876,6 +2162,10 @@ int ata_bus_probe(struct ata_port *ap) goto fail; } + /* Now ask for the cable type as PDIAG- should have been released */ + if (ap->ops->cable_detect) + ap->cbl = ap->ops->cable_detect(ap); + /* After the identify sequence we can now set up the devices. We do this in the normal order so that the user doesn't get confused */ @@ -1958,7 +2248,7 @@ void ata_port_probe(struct ata_port *ap) * LOCKING: * None. */ -static void sata_print_link_status(struct ata_port *ap) +void sata_print_link_status(struct ata_port *ap) { u32 sstatus, scontrol, tmp; @@ -2352,6 +2642,12 @@ int ata_timing_compute(struct ata_device t->active += (t->cycle - (t->active + t->recover)) / 2; t->recover = t->cycle - t->active; } + + /* In a few cases quantisation may produce enough errors to + leave t->cycle too low for the sum of active and recovery + if so we must correct this */ + if (t->active + t->recover > t->cycle) + t->cycle = t->active + t->recover; return 0; } @@ -2481,12 +2777,13 @@ static int ata_dev_set_mode(struct ata_d } /** - * ata_set_mode - Program timings and issue SET FEATURES - XFER + * ata_do_set_mode - Program timings and issue SET FEATURES - XFER * @ap: port on which timings will be programmed * @r_failed_dev: out paramter for failed device * - * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If - * ata_set_mode() fails, pointer to the failing device is + * Standard implementation of the function used to tune and set + * ATA device disk transfer mode (PIO3, UDMA6, etc.). If + * ata_dev_set_mode() fails, pointer to the failing device is * returned in @r_failed_dev. * * LOCKING: @@ -2495,14 +2792,12 @@ static int ata_dev_set_mode(struct ata_d * RETURNS: * 0 on success, negative errno otherwise */ -int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) + +int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) { struct ata_device *dev; int i, rc = 0, used_dma = 0, found = 0; - /* has private set_mode? */ - if (ap->ops->set_mode) - return ap->ops->set_mode(ap, r_failed_dev); /* step 1: calculate xfer_mask */ for (i = 0; i < ATA_MAX_DEVICES; i++) { @@ -2587,6 +2882,29 @@ int ata_set_mode(struct ata_port *ap, st } /** + * ata_set_mode - Program timings and issue SET FEATURES - XFER + * @ap: port on which timings will be programmed + * @r_failed_dev: out paramter for failed device + * + * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If + * ata_set_mode() fails, pointer to the failing device is + * returned in @r_failed_dev. + * + * LOCKING: + * PCI/etc. bus probe sem. + * + * RETURNS: + * 0 on success, negative errno otherwise + */ +int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + /* has private set_mode? */ + if (ap->ops->set_mode) + return ap->ops->set_mode(ap, r_failed_dev); + return ata_do_set_mode(ap, r_failed_dev); +} + +/** * ata_tf_to_host - issue ATA taskfile to host controller * @ap: port to which command is being issued * @tf: ATA taskfile register set @@ -3267,6 +3585,11 @@ static int ata_dev_same_device(struct at "%llu != %llu\n", (unsigned long long)dev->n_sectors, (unsigned long long)new_n_sectors); + /* Are we the boot time size - if so we appear to be the + same disk at this point and our HPA got reapplied */ + if (ata_ignore_hpa && dev->n_sectors_boot == new_n_sectors + && ata_id_hpa_enabled(new_id)) + return 1; return 0; } @@ -3441,19 +3764,7 @@ static void ata_dev_xfermask(struct ata_ xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, ap->udma_mask); - /* Apply cable rule here. Don't apply it early because when - * we handle hot plug the cable type can itself change. - */ - if (ap->cbl == ATA_CBL_PATA40) - xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); - /* Apply drive side cable rule. Unknown or 80 pin cables reported - * host side are checked drive side as well. Cases where we know a - * 40wire cable is used safely for 80 are not checked here. - */ - if (ata_drive_40wire(dev->id) && (ap->cbl == ATA_CBL_PATA_UNK || ap->cbl == ATA_CBL_PATA80)) - xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); - - + /* drive modes available */ xfer_mask &= ata_pack_xfermask(dev->pio_mask, dev->mwdma_mask, dev->udma_mask); xfer_mask &= ata_id_xfermask(dev->id); @@ -3482,8 +3793,30 @@ static void ata_dev_xfermask(struct ata_ "other device, disabling DMA\n"); } + if (ap->flags & ATA_FLAG_NO_IORDY) + xfer_mask &= ata_pio_mask_no_iordy(dev); + if (ap->ops->mode_filter) - xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask); + xfer_mask = ap->ops->mode_filter(dev, xfer_mask); + + /* Apply cable rule here. Don't apply it early because when + * we handle hot plug the cable type can itself change. + * Check this last so that we know if the transfer rate was + * solely limited by the cable. + * Unknown or 80 wire cables reported host side are checked + * drive side as well. Cases where we know a 40wire cable + * is used safely for 80 are not checked here. + */ + if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA)) + /* UDMA/44 or higher would be available */ + if((ap->cbl == ATA_CBL_PATA40) || + (ata_drive_40wire(dev->id) && + (ap->cbl == ATA_CBL_PATA_UNK || + ap->cbl == ATA_CBL_PATA80))) { + ata_dev_printk(dev, KERN_WARNING, + "limited to UDMA/33 due to 40-wire cable\n"); + xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); + } ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask, &dev->udma_mask); @@ -4022,10 +4355,10 @@ void ata_data_xfer_noirq(struct ata_devi /** - * ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data. + * ata_pio_sector - Transfer a sector of data. * @qc: Command on going * - * Transfer ATA_SECT_SIZE of data from/to the ATA device. + * Transfer qc->sect_size bytes of data from/to the ATA device. * * LOCKING: * Inherited from caller. @@ -4040,7 +4373,7 @@ static void ata_pio_sector(struct ata_qu unsigned int offset; unsigned char *buf; - if (qc->curbytes == qc->nbytes - ATA_SECT_SIZE) + if (qc->curbytes == qc->nbytes - qc->sect_size) ap->hsm_task_state = HSM_ST_LAST; page = sg[qc->cursg].page; @@ -4060,17 +4393,17 @@ static void ata_pio_sector(struct ata_qu buf = kmap_atomic(page, KM_IRQ0); /* do the actual data transfer */ - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); + ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write); kunmap_atomic(buf, KM_IRQ0); local_irq_restore(flags); } else { buf = page_address(page); - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); + ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write); } - qc->curbytes += ATA_SECT_SIZE; - qc->cursg_ofs += ATA_SECT_SIZE; + qc->curbytes += qc->sect_size; + qc->cursg_ofs += qc->sect_size; if (qc->cursg_ofs == (&sg[qc->cursg])->length) { qc->cursg++; @@ -4079,10 +4412,10 @@ static void ata_pio_sector(struct ata_qu } /** - * ata_pio_sectors - Transfer one or many 512-byte sectors. + * ata_pio_sectors - Transfer one or many sectors. * @qc: Command on going * - * Transfer one or many ATA_SECT_SIZE of data from/to the + * Transfer one or many sectors of data from/to the * ATA device for the DRQ request. * * LOCKING: @@ -4097,7 +4430,7 @@ static void ata_pio_sectors(struct ata_q WARN_ON(qc->dev->multi_count == 0); - nsect = min((qc->nbytes - qc->curbytes) / ATA_SECT_SIZE, + nsect = min((qc->nbytes - qc->curbytes) / qc->sect_size, qc->dev->multi_count); while (nsect--) ata_pio_sector(qc); @@ -5577,42 +5910,35 @@ void ata_dev_init(struct ata_device *dev } /** - * ata_port_init - Initialize an ata_port structure - * @ap: Structure to initialize - * @host: Collection of hosts to which @ap belongs - * @ent: Probe information provided by low-level driver - * @port_no: Port number associated with this ata_port + * ata_port_alloc - allocate and initialize basic ATA port resources + * @host: ATA host this allocated port belongs to * - * Initialize a new ata_port structure. + * Allocate and initialize basic ATA port resources. + * + * RETURNS: + * Allocate ATA port on success, NULL on failure. * * LOCKING: - * Inherited from caller. + * Inherited from calling layer (may sleep). */ -void ata_port_init(struct ata_port *ap, struct ata_host *host, - const struct ata_probe_ent *ent, unsigned int port_no) +struct ata_port *ata_port_alloc(struct ata_host *host) { + struct ata_port *ap; unsigned int i; + DPRINTK("ENTER\n"); + + ap = kzalloc(sizeof(*ap), GFP_KERNEL); + if (!ap) + return NULL; + ap->lock = &host->lock; ap->flags = ATA_FLAG_DISABLED; - ap->print_id = ata_print_id++; + ap->print_id = -1; ap->ctl = ATA_DEVCTL_OBS; ap->host = host; - ap->dev = ent->dev; - ap->port_no = port_no; - if (port_no == 1 && ent->pinfo2) { - ap->pio_mask = ent->pinfo2->pio_mask; - ap->mwdma_mask = ent->pinfo2->mwdma_mask; - ap->udma_mask = ent->pinfo2->udma_mask; - ap->flags |= ent->pinfo2->flags; - ap->ops = ent->pinfo2->port_ops; - } else { - ap->pio_mask = ent->pio_mask; - ap->mwdma_mask = ent->mwdma_mask; - ap->udma_mask = ent->udma_mask; - ap->flags |= ent->port_flags; - ap->ops = ent->port_ops; - } + ap->dev = host->dev; + ap->hw_sata_spd_limit = UINT_MAX; ap->active_tag = ATA_TAG_POISON; ap->last_ctl = 0xFF; @@ -5632,10 +5958,7 @@ #endif INIT_LIST_HEAD(&ap->eh_done_q); init_waitqueue_head(&ap->eh_wait_q); - /* set cable type */ ap->cbl = ATA_CBL_NONE; - if (ap->flags & ATA_FLAG_SATA) - ap->cbl = ATA_CBL_SATA; for (i = 0; i < ATA_MAX_DEVICES; i++) { struct ata_device *dev = &ap->device[i]; @@ -5648,100 +5971,209 @@ #ifdef ATA_IRQ_TRAP ap->stats.unhandled_irq = 1; ap->stats.idle_irq = 1; #endif + return ap; +} - memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports)); +static void ata_host_release(struct device *gendev, void *res) +{ + struct ata_host *host = dev_get_drvdata(gendev); + int i; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + if (!ap) + continue; + + if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop) + ap->ops->port_stop(ap); + } + + if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop) + host->ops->host_stop(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + if (!ap) + continue; + + if (ap->scsi_host) + scsi_host_put(ap->scsi_host); + + kfree(ap); + host->ports[i] = NULL; + } + + dev_set_drvdata(gendev, NULL); } /** - * ata_port_init_shost - Initialize SCSI host associated with ATA port - * @ap: ATA port to initialize SCSI host for - * @shost: SCSI host associated with @ap + * ata_host_alloc - allocate and init basic ATA host resources + * @dev: generic device this host is associated with + * @max_ports: maximum number of ATA ports associated with this host * - * Initialize SCSI host @shost associated with ATA port @ap. + * Allocate and initialize basic ATA host resources. LLD calls + * this function to allocate a host, initializes it fully and + * attaches it using ata_host_register(). + * + * @max_ports ports are allocated and host->n_ports is + * initialized to @max_ports. The caller is allowed to decrease + * host->n_ports before calling ata_host_register(). The unused + * ports will be automatically freed on registration. + * + * RETURNS: + * Allocate ATA host on success, NULL on failure. * * LOCKING: - * Inherited from caller. + * Inherited from calling layer (may sleep). */ -static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost) +struct ata_host *ata_host_alloc(struct device *dev, int max_ports) { - ap->scsi_host = shost; + struct ata_host *host; + size_t sz; + int i; + + DPRINTK("ENTER\n"); + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return NULL; + + /* alloc a container for our list of ATA ports (buses) */ + sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *); + /* alloc a container for our list of ATA ports (buses) */ + host = devres_alloc(ata_host_release, sz, GFP_KERNEL); + if (!host) + goto err_out; + + devres_add(dev, host); + dev_set_drvdata(dev, host); - shost->unique_id = ap->print_id; - shost->max_id = 16; - shost->max_lun = 1; - shost->max_channel = 1; - shost->max_cmd_len = 12; + spin_lock_init(&host->lock); + host->dev = dev; + host->n_ports = max_ports; + + /* allocate ports bound to this host */ + for (i = 0; i < max_ports; i++) { + struct ata_port *ap; + + ap = ata_port_alloc(host); + if (!ap) + goto err_out; + + ap->port_no = i; + host->ports[i] = ap; + } + + devres_remove_group(dev, NULL); + return host; + + err_out: + devres_release_group(dev, NULL); + return NULL; } /** - * ata_port_add - Attach low-level ATA driver to system - * @ent: Information provided by low-level driver - * @host: Collections of ports to which we add - * @port_no: Port number associated with this host + * ata_host_alloc_pinfo - alloc host and init with port_info array + * @dev: generic device this host is associated with + * @ppi: array of ATA port_info to initialize host with + * @n_ports: number of ATA ports attached to this host * - * Attach low-level ATA driver to system. - * - * LOCKING: - * PCI/etc. bus probe sem. + * Allocate ATA host and initialize with info from @ppi. If NULL + * terminated, @ppi may contain fewer entries than @n_ports. The + * last entry will be used for the remaining ports. * * RETURNS: - * New ata_port on success, for NULL on error. + * Allocate ATA host on success, NULL on failure. + * + * LOCKING: + * Inherited from calling layer (may sleep). */ -static struct ata_port * ata_port_add(const struct ata_probe_ent *ent, - struct ata_host *host, - unsigned int port_no) +struct ata_host *ata_host_alloc_pinfo(struct device *dev, + const struct ata_port_info * const * ppi, + int n_ports) { - struct Scsi_Host *shost; - struct ata_port *ap; - - DPRINTK("ENTER\n"); + const struct ata_port_info *pi; + struct ata_host *host; + int i, j; - if (!ent->port_ops->error_handler && - !(ent->port_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) { - printk(KERN_ERR "ata%u: no reset mechanism available\n", - port_no); + host = ata_host_alloc(dev, n_ports); + if (!host) return NULL; - } - shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port)); - if (!shost) - return NULL; + for (i = 0, j = 0, pi = NULL; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; - shost->transportt = &ata_scsi_transport_template; + if (ppi[j]) + pi = ppi[j++]; - ap = ata_shost_to_port(shost); + ap->pio_mask = pi->pio_mask; + ap->mwdma_mask = pi->mwdma_mask; + ap->udma_mask = pi->udma_mask; + ap->flags |= pi->flags; + ap->ops = pi->port_ops; - ata_port_init(ap, host, ent, port_no); - ata_port_init_shost(ap, shost); + if (!host->ops && (pi->port_ops != &ata_dummy_port_ops)) + host->ops = pi->port_ops; + if (!host->private_data && pi->private_data) + host->private_data = pi->private_data; + } - return ap; + return host; } -static void ata_host_release(struct device *gendev, void *res) +/** + * ata_host_start - start and freeze ports of an ATA host + * @host: ATA host to start ports for + * + * Start and then freeze ports of @host. Started status is + * recorded in host->flags, so this function can be called + * multiple times. Ports are guaranteed to get started only + * once. If host->ops isn't initialized yet, its set to the + * first non-dummy port ops. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 if all ports are started successfully, -errno otherwise. + */ +int ata_host_start(struct ata_host *host) { - struct ata_host *host = dev_get_drvdata(gendev); - int i; + int i, rc; + + if (host->flags & ATA_HOST_STARTED) + return 0; for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - if (ap && ap->ops->port_stop) - ap->ops->port_stop(ap); + if (!host->ops && !ata_port_is_dummy(ap)) + host->ops = ap->ops; + + if (ap->ops->port_start) { + rc = ap->ops->port_start(ap); + if (rc) { + ata_port_printk(ap, KERN_ERR, "failed to " + "start port (errno=%d)\n", rc); + goto err_out; + } + } + + ata_eh_freeze_port(ap); } - if (host->ops->host_stop) - host->ops->host_stop(host); + host->flags |= ATA_HOST_STARTED; + return 0; - for (i = 0; i < host->n_ports; i++) { + err_out: + while (--i >= 0) { struct ata_port *ap = host->ports[i]; - if (ap) - scsi_host_put(ap->scsi_host); - - host->ports[i] = NULL; + if (ap->ops->port_stop) + ap->ops->port_stop(ap); } - - dev_set_drvdata(gendev, NULL); + return rc; } /** @@ -5755,7 +6187,7 @@ static void ata_host_release(struct devi * PCI/etc. bus probe sem. * */ - +/* KILLME - the only user left is ipr */ void ata_host_init(struct ata_host *host, struct device *dev, unsigned long flags, const struct ata_port_operations *ops) { @@ -5766,155 +6198,95 @@ void ata_host_init(struct ata_host *host } /** - * ata_device_add - Register hardware device with ATA and SCSI layers - * @ent: Probe information describing hardware device to be registered + * ata_host_register - register initialized ATA host + * @host: ATA host to register + * @sht: template for SCSI host * - * This function processes the information provided in the probe - * information struct @ent, allocates the necessary ATA and SCSI - * host information structures, initializes them, and registers - * everything with requisite kernel subsystems. - * - * This function requests irqs, probes the ATA bus, and probes - * the SCSI bus. + * Register initialized ATA host. @host is allocated using + * ata_host_alloc() and fully initialized by LLD. This function + * starts ports, registers @host with ATA and SCSI layers and + * probe registered devices. * * LOCKING: - * PCI/etc. bus probe sem. + * Inherited from calling layer (may sleep). * * RETURNS: - * Number of ports registered. Zero on error (no ports registered). + * 0 on success, -errno otherwise. */ -int ata_device_add(const struct ata_probe_ent *ent) +int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) { - unsigned int i; - struct device *dev = ent->dev; - struct ata_host *host; - int rc; - - DPRINTK("ENTER\n"); + int i, rc; - if (ent->irq == 0) { - dev_printk(KERN_ERR, dev, "is not available: No interrupt assigned.\n"); - return 0; + /* host must have been started */ + if (!(host->flags & ATA_HOST_STARTED)) { + dev_printk(KERN_ERR, host->dev, + "BUG: trying to register unstarted host\n"); + WARN_ON(1); + return -EINVAL; } - if (!devres_open_group(dev, ata_device_add, GFP_KERNEL)) - return 0; + /* Blow away unused ports. This happens when LLD can't + * determine the exact number of ports to allocate at + * allocation time. + */ + for (i = host->n_ports; host->ports[i]; i++) + kfree(host->ports[i]); - /* alloc a container for our list of ATA ports (buses) */ - host = devres_alloc(ata_host_release, sizeof(struct ata_host) + - (ent->n_ports * sizeof(void *)), GFP_KERNEL); - if (!host) - goto err_out; - devres_add(dev, host); - dev_set_drvdata(dev, host); + /* give ports names and add SCSI hosts */ + for (i = 0; i < host->n_ports; i++) + host->ports[i]->print_id = ata_print_id++; - ata_host_init(host, dev, ent->_host_flags, ent->port_ops); - host->n_ports = ent->n_ports; - host->irq = ent->irq; - host->irq2 = ent->irq2; - host->iomap = ent->iomap; - host->private_data = ent->private_data; + rc = ata_scsi_add_hosts(host, sht); + if (rc) + return rc; - /* register each port bound to this device */ + /* set cable, sata_spd_limit and report */ for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap; - unsigned long xfer_mode_mask; - int irq_line = ent->irq; + struct ata_port *ap = host->ports[i]; + int irq_line; + u32 scontrol; + unsigned long xfer_mask; - ap = ata_port_add(ent, host, i); - host->ports[i] = ap; - if (!ap) - goto err_out; + /* set SATA cable type if still unset */ + if (ap->cbl == ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA)) + ap->cbl = ATA_CBL_SATA; - /* dummy? */ - if (ent->dummy_port_mask & (1 << i)) { - ata_port_printk(ap, KERN_INFO, "DUMMY\n"); - ap->ops = &ata_dummy_port_ops; - continue; - } - - /* start port */ - rc = ap->ops->port_start(ap); - if (rc) { - host->ports[i] = NULL; - scsi_host_put(ap->scsi_host); - goto err_out; + /* init sata_spd_limit to the current value */ + if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) { + int spd = (scontrol >> 4) & 0xf; + ap->hw_sata_spd_limit &= (1 << spd) - 1; } + ap->sata_spd_limit = ap->hw_sata_spd_limit; - /* Report the secondary IRQ for second channel legacy */ - if (i == 1 && ent->irq2) - irq_line = ent->irq2; + /* report the secondary IRQ for second channel legacy */ + irq_line = host->irq; + if (i == 1 && host->irq2) + irq_line = host->irq2; - xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) | - (ap->mwdma_mask << ATA_SHIFT_MWDMA) | - (ap->pio_mask << ATA_SHIFT_PIO); + xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, + ap->udma_mask); /* print per-port info to dmesg */ - ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p " - "ctl 0x%p bmdma 0x%p irq %d\n", - ap->flags & ATA_FLAG_SATA ? 'S' : 'P', - ata_mode_string(xfer_mode_mask), - ap->ioaddr.cmd_addr, - ap->ioaddr.ctl_addr, - ap->ioaddr.bmdma_addr, - irq_line); - - /* freeze port before requesting IRQ */ - ata_eh_freeze_port(ap); - } - - /* obtain irq, that may be shared between channels */ - rc = devm_request_irq(dev, ent->irq, ent->port_ops->irq_handler, - ent->irq_flags, DRV_NAME, host); - if (rc) { - dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n", - ent->irq, rc); - goto err_out; - } - - /* do we have a second IRQ for the other channel, eg legacy mode */ - if (ent->irq2) { - /* We will get weird core code crashes later if this is true - so trap it now */ - BUG_ON(ent->irq == ent->irq2); - - rc = devm_request_irq(dev, ent->irq2, - ent->port_ops->irq_handler, ent->irq_flags, - DRV_NAME, host); - if (rc) { - dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n", - ent->irq2, rc); - goto err_out; - } + if (!ata_port_is_dummy(ap)) + ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p " + "ctl 0x%p bmdma 0x%p irq %d\n", + ap->cbl == ATA_CBL_SATA ? 'S' : 'P', + ata_mode_string(xfer_mask), + ap->ioaddr.cmd_addr, + ap->ioaddr.ctl_addr, + ap->ioaddr.bmdma_addr, + irq_line); + else + ata_port_printk(ap, KERN_INFO, "DUMMY\n"); } - /* resource acquisition complete */ - devres_remove_group(dev, ata_device_add); - /* perform each probe synchronously */ DPRINTK("probe begin\n"); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - u32 scontrol; int rc; - /* init sata_spd_limit to the current value */ - if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) { - int spd = (scontrol >> 4) & 0xf; - ap->hw_sata_spd_limit &= (1 << spd) - 1; - } - ap->sata_spd_limit = ap->hw_sata_spd_limit; - - rc = scsi_add_host(ap->scsi_host, dev); - if (rc) { - ata_port_printk(ap, KERN_ERR, "scsi_add_host failed\n"); - /* FIXME: do something useful here */ - /* FIXME: handle unconditional calls to - * scsi_scan_host and ata_host_remove, below, - * at the very least - */ - } - + /* probe */ if (ap->ops->error_handler) { struct ata_eh_info *ehi = &ap->eh_info; unsigned long flags; @@ -5959,16 +6331,52 @@ int ata_device_add(const struct ata_prob ata_scsi_scan_host(ap); } - VPRINTK("EXIT, returning %u\n", ent->n_ports); - return ent->n_ports; /* success */ - - err_out: - devres_release_group(dev, ata_device_add); - VPRINTK("EXIT, returning %d\n", rc); return 0; } /** + * ata_host_activate - start host, request IRQ and register it + * @host: target ATA host + * @irq: IRQ to request + * @irq_handler: irq_handler used when requesting IRQ + * @irq_flags: irq_flags used when requesting IRQ + * @sht: scsi_host_template to use when registering the host + * + * After allocating an ATA host and initializing it, most libata + * LLDs perform three steps to activate the host - start host, + * request IRQ and register it. This helper takes necessasry + * arguments and performs the three steps in one go. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_host_activate(struct ata_host *host, int irq, + irq_handler_t irq_handler, unsigned long irq_flags, + struct scsi_host_template *sht) +{ + int rc; + + rc = ata_host_start(host); + if (rc) + return rc; + + rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags, + dev_driver_string(host->dev), host); + if (rc) + return rc; + + rc = ata_host_register(host, sht); + /* if failed, just free the IRQ and leave ports alone */ + if (rc) + devm_free_irq(host->dev, irq, host); + + return rc; +} + +/** * ata_port_detach - Detach ATA port in prepration of device removal * @ap: ATA port to be detached * @@ -6043,32 +6451,6 @@ void ata_host_detach(struct ata_host *ho ata_port_detach(host->ports[i]); } -struct ata_probe_ent * -ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) -{ - struct ata_probe_ent *probe_ent; - - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) { - printk(KERN_ERR DRV_NAME "(%s): out of memory\n", - kobject_name(&(dev->kobj))); - return NULL; - } - - INIT_LIST_HEAD(&probe_ent->node); - probe_ent->dev = dev; - - probe_ent->sht = port->sht; - probe_ent->port_flags = port->flags; - probe_ent->pio_mask = port->pio_mask; - probe_ent->mwdma_mask = port->mwdma_mask; - probe_ent->udma_mask = port->udma_mask; - probe_ent->port_ops = port->port_ops; - probe_ent->private_data = port->private_data; - - return probe_ent; -} - /** * ata_std_ports - initialize ioaddr with standard port offsets. * @ioaddr: IO address structure to be initialized @@ -6334,6 +6716,10 @@ const struct ata_port_operations ata_dum .port_stop = ata_dummy_noret, }; +const struct ata_port_info ata_dummy_port_info = { + .port_ops = &ata_dummy_port_ops, +}; + /* * libata is essentially a library of internal helper functions for * low-level ATA host controller drivers. As such, the API/ABI is @@ -6345,10 +6731,15 @@ EXPORT_SYMBOL_GPL(sata_deb_timing_normal EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); EXPORT_SYMBOL_GPL(sata_deb_timing_long); EXPORT_SYMBOL_GPL(ata_dummy_port_ops); +EXPORT_SYMBOL_GPL(ata_dummy_port_info); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_ports); EXPORT_SYMBOL_GPL(ata_host_init); -EXPORT_SYMBOL_GPL(ata_device_add); +EXPORT_SYMBOL_GPL(ata_host_alloc); +EXPORT_SYMBOL_GPL(ata_host_alloc_pinfo); +EXPORT_SYMBOL_GPL(ata_host_start); +EXPORT_SYMBOL_GPL(ata_host_register); +EXPORT_SYMBOL_GPL(ata_host_activate); EXPORT_SYMBOL_GPL(ata_host_detach); EXPORT_SYMBOL_GPL(ata_sg_init); EXPORT_SYMBOL_GPL(ata_sg_init_one); @@ -6360,6 +6751,7 @@ EXPORT_SYMBOL_GPL(ata_tf_load); EXPORT_SYMBOL_GPL(ata_tf_read); EXPORT_SYMBOL_GPL(ata_noop_dev_select); EXPORT_SYMBOL_GPL(ata_std_dev_select); +EXPORT_SYMBOL_GPL(sata_print_link_status); EXPORT_SYMBOL_GPL(ata_tf_to_fis); EXPORT_SYMBOL_GPL(ata_tf_from_fis); EXPORT_SYMBOL_GPL(ata_check_status); @@ -6367,6 +6759,7 @@ EXPORT_SYMBOL_GPL(ata_altstatus); EXPORT_SYMBOL_GPL(ata_exec_command); EXPORT_SYMBOL_GPL(ata_port_start); EXPORT_SYMBOL_GPL(ata_interrupt); +EXPORT_SYMBOL_GPL(ata_do_set_mode); EXPORT_SYMBOL_GPL(ata_data_xfer); EXPORT_SYMBOL_GPL(ata_data_xfer_noirq); EXPORT_SYMBOL_GPL(ata_qc_prep); @@ -6429,7 +6822,8 @@ EXPORT_SYMBOL_GPL(ata_timing_merge); #ifdef CONFIG_PCI EXPORT_SYMBOL_GPL(pci_test_config_bits); -EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); +EXPORT_SYMBOL_GPL(ata_pci_init_native_host); +EXPORT_SYMBOL_GPL(ata_pci_prepare_native_host); EXPORT_SYMBOL_GPL(ata_pci_init_one); EXPORT_SYMBOL_GPL(ata_pci_remove_one); #ifdef CONFIG_PM @@ -6461,3 +6855,8 @@ EXPORT_SYMBOL_GPL(ata_dummy_irq_on); EXPORT_SYMBOL_GPL(ata_irq_ack); EXPORT_SYMBOL_GPL(ata_dummy_irq_ack); EXPORT_SYMBOL_GPL(ata_dev_try_classify); + +EXPORT_SYMBOL_GPL(ata_cable_40wire); +EXPORT_SYMBOL_GPL(ata_cable_80wire); +EXPORT_SYMBOL_GPL(ata_cable_unknown); +EXPORT_SYMBOL_GPL(ata_cable_sata); diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 39f556c..2bff9ad 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1056,7 +1056,7 @@ static void ata_eh_analyze_serror(struct } if (serror & SERR_INTERNAL) { err_mask |= AC_ERR_SYSTEM; - action |= ATA_EH_SOFTRESET; + action |= ATA_EH_HARDRESET; } if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG)) ata_ehi_hotplugged(&ehc->i); @@ -1151,7 +1151,9 @@ static unsigned int ata_eh_analyze_tf(st return ATA_EH_SOFTRESET; } - if (!(qc->err_mask & AC_ERR_DEV)) + if (stat & (ATA_ERR | ATA_DF)) + qc->err_mask |= AC_ERR_DEV; + else return 0; switch (qc->dev->class) { @@ -1669,7 +1671,10 @@ static int ata_eh_reset(struct ata_port reset == softreset ? "soft" : "hard"); /* mark that this EH session started with reset */ - ehc->i.flags |= ATA_EHI_DID_RESET; + if (reset == hardreset) + ehc->i.flags |= ATA_EHI_DID_HARDRESET; + else + ehc->i.flags |= ATA_EHI_DID_SOFTRESET; rc = ata_do_reset(ap, reset, classes); @@ -1808,6 +1813,10 @@ static int ata_eh_revalidate_and_attach( } } + /* PDIAG- should have been released, ask cable type if post-reset */ + if ((ehc->i.flags & ATA_EHI_DID_RESET) && ap->ops->cable_detect) + ap->cbl = ap->ops->cable_detect(ap); + /* Configure new devices forward such that user doesn't see * device detection messages backwards. */ diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e936443..9afba2b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -104,7 +104,7 @@ static const u8 def_control_mpage[CONTRO * libata transport template. libata doesn't do real transport stuff. * It just needs the eh_timed_out hook. */ -struct scsi_transport_template ata_scsi_transport_template = { +static struct scsi_transport_template ata_scsi_transport_template = { .eh_strategy_handler = ata_scsi_error, .eh_timed_out = ata_scsi_timed_out, .user_scan = ata_scsi_user_scan, @@ -2678,6 +2678,18 @@ static unsigned int ata_scsi_pass_thru(s tf->device = qc->dev->devno ? tf->device | ATA_DEV1 : tf->device & ~ATA_DEV1; + /* READ/WRITE LONG use a non-standard sect_size */ + qc->sect_size = ATA_SECT_SIZE; + switch (tf->command) { + case ATA_CMD_READ_LONG: + case ATA_CMD_READ_LONG_ONCE: + case ATA_CMD_WRITE_LONG: + case ATA_CMD_WRITE_LONG_ONCE: + if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1) + goto invalid_fld; + qc->sect_size = scmd->request_bufflen; + } + /* * Filter SET_FEATURES - XFER MODE command -- otherwise, * SET_FEATURES - XFER MODE must be preceded/succeeded @@ -2792,8 +2804,9 @@ static inline int __ata_scsi_queuecmd(st { int rc = 0; - if (unlikely(!scmd->cmd_len)) { - ata_dev_printk(dev, KERN_WARNING, "WARNING: zero len CDB\n"); + if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len)) { + DPRINTK("bad CDB len=%u, max=%u\n", + scmd->cmd_len, dev->cdb_len); scmd->result = DID_ERROR << 16; done(scmd); return 0; @@ -2948,6 +2961,48 @@ void ata_scsi_simulate(struct ata_device } } +int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) +{ + int i, rc; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + struct Scsi_Host *shost; + + rc = -ENOMEM; + shost = scsi_host_alloc(sht, sizeof(struct ata_port *)); + if (!shost) + goto err_alloc; + + *(struct ata_port **)&shost->hostdata[0] = ap; + ap->scsi_host = shost; + + shost->transportt = &ata_scsi_transport_template; + shost->unique_id = ap->print_id; + shost->max_id = 16; + shost->max_lun = 1; + shost->max_channel = 1; + shost->max_cmd_len = 16; + + rc = scsi_add_host(ap->scsi_host, ap->host->dev); + if (rc) + goto err_add; + } + + return 0; + + err_add: + scsi_host_put(host->ports[i]->scsi_host); + err_alloc: + while (--i >= 0) { + struct Scsi_Host *shost = host->ports[i]->scsi_host; + + scsi_remove_host(shost); + scsi_host_put(shost); + } + return rc; +} + void ata_scsi_scan_host(struct ata_port *ap) { unsigned int i; @@ -3224,21 +3279,21 @@ struct ata_port *ata_sas_port_alloc(stru struct ata_port_info *port_info, struct Scsi_Host *shost) { - struct ata_port *ap = kzalloc(sizeof(*ap), GFP_KERNEL); - struct ata_probe_ent *ent; + struct ata_port *ap; + ap = ata_port_alloc(host); if (!ap) return NULL; - ent = ata_probe_ent_alloc(host->dev, port_info); - if (!ent) { - kfree(ap); - return NULL; - } - - ata_port_init(ap, host, ent, 0); + ap->port_no = 0; ap->lock = shost->host_lock; - devm_kfree(host->dev, ent); + ap->pio_mask = port_info->pio_mask; + ap->mwdma_mask = port_info->mwdma_mask; + ap->udma_mask = port_info->udma_mask; + ap->flags |= port_info->flags; + ap->ops = port_info->port_ops; + ap->cbl = ATA_CBL_SATA; + return ap; } EXPORT_SYMBOL_GPL(ata_sas_port_alloc); @@ -3294,8 +3349,10 @@ int ata_sas_port_init(struct ata_port *a { int rc = ap->ops->port_start(ap); - if (!rc) + if (!rc) { + ap->print_id = ata_print_id++; rc = ata_bus_probe(ap); + } return rc; } diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 2ffcca0..d211db6 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -526,168 +526,399 @@ static int ata_resources_present(struct port = port * 2; for (i = 0; i < 2; i ++) { if (pci_resource_start(pdev, port + i) == 0 || - pci_resource_len(pdev, port + i) == 0) - return 0; + pci_resource_len(pdev, port + i) == 0) + return 0; } return 1; } /** - * ata_pci_init_native_mode - Initialize native-mode driver - * @pdev: pci device to be initialized - * @port: array[2] of pointers to port info structures. - * @ports: bitmap of ports present - * - * Utility function which allocates and initializes an - * ata_probe_ent structure for a standard dual-port - * PIO-based IDE controller. The returned ata_probe_ent - * structure can be passed to ata_device_add(). The returned - * ata_probe_ent structure should then be freed with kfree(). - * - * The caller need only pass the address of the primary port, the - * secondary will be deduced automatically. If the device has non - * standard secondary port mappings this function can be called twice, - * once for each interface. + * ata_pci_init_bmdma - acquire PCI BMDMA resources and init ATA host + * @host: target ATA host + * + * Acquire PCI BMDMA resources and initialize @host accordingly. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. */ +static int ata_pci_init_bmdma(struct ata_host *host) +{ + struct device *gdev = host->dev; + struct pci_dev *pdev = to_pci_dev(gdev); + int i, rc; + + /* TODO: If we get no DMA mask we should fall back to PIO */ + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + + /* request and iomap DMA region */ + rc = pcim_iomap_regions(pdev, 1 << 4, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n"); + return -ENOMEM; + } + host->iomap = pcim_iomap_table(pdev); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + void __iomem *bmdma = host->iomap[4] + 8 * i; + + if (ata_port_is_dummy(ap)) + continue; + + ap->ioaddr.bmdma_addr = bmdma; + if ((!(ap->flags & ATA_FLAG_IGN_SIMPLEX)) && + (ioread8(bmdma + 2) & 0x80)) + host->flags |= ATA_HOST_SIMPLEX; + } + + return 0; +} -struct ata_probe_ent * -ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports) +/** + * ata_pci_init_native_host - acquire native ATA resources and init host + * @host: target ATA host + * @port_mask: ports to consider + * + * Acquire native PCI ATA resources for @host and initialize + * @host accordoingly. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_pci_init_native_host(struct ata_host *host, unsigned int port_mask) { - struct ata_probe_ent *probe_ent; - int i, p = 0; - void __iomem * const *iomap; - - /* iomap BARs */ - for (i = 0; i < 4; i++) { - if (pcim_iomap(pdev, i, 0) == NULL) { - dev_printk(KERN_ERR, &pdev->dev, - "failed to iomap PCI BAR %d\n", i); - return NULL; + struct device *gdev = host->dev; + struct pci_dev *pdev = to_pci_dev(gdev); + int i, rc; + + /* Discard disabled ports. Some controllers show their unused + * channels this way. Disabled ports are made dummy. + */ + for (i = 0; i < 2; i++) { + if ((port_mask & (1 << i)) && !ata_resources_present(pdev, i)) { + host->ports[i]->ops = &ata_dummy_port_ops; + port_mask &= ~(1 << i); } } - pcim_iomap(pdev, 4, 0); /* may fail */ - iomap = pcim_iomap_table(pdev); - - /* alloc and init probe_ent */ - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); - if (!probe_ent) - return NULL; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - - /* Discard disabled ports. Some controllers show their - unused channels this way */ - if (ata_resources_present(pdev, 0) == 0) - ports &= ~ATA_PORT_PRIMARY; - if (ata_resources_present(pdev, 1) == 0) - ports &= ~ATA_PORT_SECONDARY; - - if (ports & ATA_PORT_PRIMARY) { - probe_ent->port[p].cmd_addr = iomap[0]; - probe_ent->port[p].altstatus_addr = - probe_ent->port[p].ctl_addr = (void __iomem *) - ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS); - if (iomap[4]) { - if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(iomap[4] + 2) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; - probe_ent->port[p].bmdma_addr = iomap[4]; - } - ata_std_ports(&probe_ent->port[p]); - p++; + if (!port_mask) { + dev_printk(KERN_ERR, gdev, "no available port\n"); + return -ENODEV; } - if (ports & ATA_PORT_SECONDARY) { - probe_ent->port[p].cmd_addr = iomap[2]; - probe_ent->port[p].altstatus_addr = - probe_ent->port[p].ctl_addr = (void __iomem *) - ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS); - if (iomap[4]) { - if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(iomap[4] + 10) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; - probe_ent->port[p].bmdma_addr = iomap[4] + 8; + /* request, iomap BARs and init port addresses accordingly */ + for (i = 0; i < 2; i++) { + struct ata_port *ap = host->ports[i]; + int base = i * 2; + void __iomem * const *iomap; + + if (!(port_mask & (1 << i))) + continue; + + rc = pcim_iomap_regions(pdev, 0x3 << base, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, gdev, "failed to request/iomap " + "BARs for port %d (errno=%d)\n", i, rc); + if (rc == -EBUSY) + pcim_pin_device(pdev); + return rc; } - ata_std_ports(&probe_ent->port[p]); - probe_ent->pinfo2 = port[1]; - p++; + host->iomap = iomap = pcim_iomap_table(pdev); + + ap->ioaddr.cmd_addr = iomap[base]; + ap->ioaddr.altstatus_addr = + ap->ioaddr.ctl_addr = (void __iomem *) + ((unsigned long)iomap[base + 1] | ATA_PCI_CTL_OFS); + ata_std_ports(&ap->ioaddr); } - probe_ent->n_ports = p; - return probe_ent; + return 0; } -static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, - struct ata_port_info **port, int port_mask) +/** + * ata_pci_prepare_native_host - helper to prepare native PCI ATA host + * @pdev: target PCI device + * @ppi: array of port_info + * @n_ports: number of ports to allocate + * @r_host: out argument for the initialized ATA host + * + * Helper to allocate ATA host for @pdev, acquire all native PCI + * resources and initialize it accordingly in one go. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_pci_prepare_native_host(struct pci_dev *pdev, + const struct ata_port_info * const * ppi, + int n_ports, struct ata_host **r_host) +{ + struct ata_host *host; + unsigned int port_mask; + int rc; + + if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) + return -ENOMEM; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, + "failed to allocate ATA host\n"); + rc = -ENOMEM; + goto err_out; + } + + port_mask = ATA_PORT_PRIMARY; + if (n_ports > 1) + port_mask |= ATA_PORT_SECONDARY; + + rc = ata_pci_init_native_host(host, port_mask); + if (rc) + goto err_out; + + /* init DMA related stuff */ + rc = ata_pci_init_bmdma(host); + if (rc) + goto err_bmdma; + + devres_remove_group(&pdev->dev, NULL); + *r_host = host; + return 0; + + err_bmdma: + /* This is necessary because PCI and iomap resources are + * merged and releasing the top group won't release the + * acquired resources if some of those have been acquired + * before entering this function. + */ + pcim_iounmap_regions(pdev, 0xf); + err_out: + devres_release_group(&pdev->dev, NULL); + return rc; +} + +struct ata_legacy_devres { + unsigned int mask; + unsigned long cmd_port[2]; + void __iomem * cmd_addr[2]; + void __iomem * ctl_addr[2]; + unsigned int irq[2]; + void * irq_dev_id[2]; +}; + +static void ata_legacy_free_irqs(struct ata_legacy_devres *legacy_dr) { - struct ata_probe_ent *probe_ent; - void __iomem *iomap[5] = { }, *bmdma; - - if (port_mask & ATA_PORT_PRIMARY) { - iomap[0] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CMD, 8); - iomap[1] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CTL, 1); - if (!iomap[0] || !iomap[1]) - return NULL; + int i; + + for (i = 0; i < 2; i++) { + if (!legacy_dr->irq[i]) + continue; + + free_irq(legacy_dr->irq[i], legacy_dr->irq_dev_id[i]); + legacy_dr->irq[i] = 0; + legacy_dr->irq_dev_id[i] = NULL; } +} + +static void ata_legacy_release(struct device *gdev, void *res) +{ + struct ata_legacy_devres *this = res; + int i; - if (port_mask & ATA_PORT_SECONDARY) { - iomap[2] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CMD, 8); - iomap[3] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CTL, 1); - if (!iomap[2] || !iomap[3]) - return NULL; + ata_legacy_free_irqs(this); + + for (i = 0; i < 2; i++) { + if (this->cmd_addr[i]) + ioport_unmap(this->cmd_addr[i]); + if (this->ctl_addr[i]) + ioport_unmap(this->ctl_addr[i]); + if (this->cmd_port[i]) + release_region(this->cmd_port[i], 8); } +} - bmdma = pcim_iomap(pdev, 4, 16); /* may fail */ - - /* alloc and init probe_ent */ - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); - if (!probe_ent) - return NULL; - - probe_ent->n_ports = 2; - probe_ent->irq_flags = IRQF_SHARED; - - if (port_mask & ATA_PORT_PRIMARY) { - probe_ent->irq = ATA_PRIMARY_IRQ(pdev); - probe_ent->port[0].cmd_addr = iomap[0]; - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = iomap[1]; - if (bmdma) { - probe_ent->port[0].bmdma_addr = bmdma; - if ((!(port[0]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(bmdma + 2) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; - } - ata_std_ports(&probe_ent->port[0]); - } else - probe_ent->dummy_port_mask |= ATA_PORT_PRIMARY; +static int ata_init_legacy_port(struct ata_port *ap, + struct ata_legacy_devres *legacy_dr) +{ + struct ata_host *host = ap->host; + int port_no = ap->port_no; + unsigned long cmd_port, ctl_port; + + if (port_no == 0) { + cmd_port = ATA_PRIMARY_CMD; + ctl_port = ATA_PRIMARY_CTL; + } else { + cmd_port = ATA_SECONDARY_CMD; + ctl_port = ATA_SECONDARY_CTL; + } + + /* request cmd_port */ + if (request_region(cmd_port, 8, "libata")) + legacy_dr->cmd_port[port_no] = cmd_port; + else { + dev_printk(KERN_WARNING, host->dev, + "0x%0lX IDE port busy\n", cmd_port); + return -EBUSY; + } + + /* iomap cmd and ctl ports */ + legacy_dr->cmd_addr[port_no] = ioport_map(cmd_port, 8); + legacy_dr->ctl_addr[port_no] = ioport_map(ctl_port, 1); + if (!legacy_dr->cmd_addr[port_no] || !legacy_dr->ctl_addr[port_no]) + return -ENOMEM; + + /* init IO addresses */ + ap->ioaddr.cmd_addr = legacy_dr->cmd_addr[port_no]; + ap->ioaddr.altstatus_addr = legacy_dr->ctl_addr[port_no]; + ap->ioaddr.ctl_addr = legacy_dr->ctl_addr[port_no]; + ata_std_ports(&ap->ioaddr); + + return 0; +} + +/** + * ata_init_legacy_host - acquire legacy ATA resources and init ATA host + * @host: target ATA host + * @legacy_mask: out parameter, mask indicating ports is in legacy mode + * @was_busy: out parameter, indicates whether any port was busy + * + * Acquire legacy ATA resources for ports. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +static int ata_init_legacy_host(struct ata_host *host, + unsigned int *legacy_mask, int *was_busy) +{ + struct device *gdev = host->dev; + struct ata_legacy_devres *legacy_dr; + int i, rc; + + if (!devres_open_group(gdev, NULL, GFP_KERNEL)) + return -ENOMEM; + + rc = -ENOMEM; + legacy_dr = devres_alloc(ata_legacy_release, sizeof(*legacy_dr), + GFP_KERNEL); + if (!legacy_dr) + goto err_out; + devres_add(gdev, legacy_dr); + + for (i = 0; i < 2; i++) { + *legacy_mask &= ~(1 << i); + rc = ata_init_legacy_port(host->ports[i], legacy_dr); + if (rc == 0) + legacy_dr->mask |= 1 << i; + else if (rc == -EBUSY) + (*was_busy)++; + } + + if (!legacy_dr->mask) + return -EBUSY; + + for (i = 0; i < 2; i++) + if (!(legacy_dr->mask & (1 << i))) + host->ports[i]->ops = &ata_dummy_port_ops; + + *legacy_mask |= legacy_dr->mask; - if (port_mask & ATA_PORT_SECONDARY) { - if (probe_ent->irq) - probe_ent->irq2 = ATA_SECONDARY_IRQ(pdev); + devres_remove_group(gdev, NULL); + return 0; + + err_out: + devres_release_group(gdev, NULL); + return rc; +} + +/** + * ata_request_legacy_irqs - request legacy ATA IRQs + * @host: target ATA host + * @handler: array of IRQ handlers + * @irq_flags: array of IRQ flags + * @dev_id: array of IRQ dev_ids + * + * Request legacy IRQs for non-dummy legacy ports in @host. All + * IRQ parameters are passed as array to allow ports to have + * separate IRQ handlers. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +static int ata_request_legacy_irqs(struct ata_host *host, + irq_handler_t const *handler, + const unsigned int *irq_flags, + void * const *dev_id) +{ + struct device *gdev = host->dev; + struct ata_legacy_devres *legacy_dr; + int i, rc; + + legacy_dr = devres_find(host->dev, ata_legacy_release, NULL, NULL); + BUG_ON(!legacy_dr); + + for (i = 0; i < host->n_ports; i++) { + unsigned int irq; + + /* FIXME: ATA_*_IRQ() should take generic device not pci_dev */ + if (i == 0) + irq = ATA_PRIMARY_IRQ(to_pci_dev(gdev)); else - probe_ent->irq = ATA_SECONDARY_IRQ(pdev); - probe_ent->port[1].cmd_addr = iomap[2]; - probe_ent->port[1].altstatus_addr = - probe_ent->port[1].ctl_addr = iomap[3]; - if (bmdma) { - probe_ent->port[1].bmdma_addr = bmdma + 8; - if ((!(port[1]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(bmdma + 10) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; + irq = ATA_SECONDARY_IRQ(to_pci_dev(gdev)); + + if (!(legacy_dr->mask & (1 << i))) + continue; + + if (!handler[i]) { + dev_printk(KERN_ERR, gdev, + "NULL handler specified for port %d\n", i); + rc = -EINVAL; + goto err_out; } - ata_std_ports(&probe_ent->port[1]); - /* FIXME: could be pointing to stack area; must copy */ - probe_ent->pinfo2 = port[1]; - } else - probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY; + rc = request_irq(irq, handler[i], irq_flags[i], DRV_NAME, + dev_id[i]); + if (rc) { + dev_printk(KERN_ERR, gdev, + "irq %u request failed (errno=%d)\n", irq, rc); + goto err_out; + } - return probe_ent; -} + /* record irq allocation in legacy_dr */ + legacy_dr->irq[i] = irq; + legacy_dr->irq_dev_id[i] = dev_id[i]; + + /* only used to print info */ + if (i == 0) + host->irq = irq; + else + host->irq2 = irq; + } + return 0; + + err_out: + ata_legacy_free_irqs(legacy_dr); + return rc; +} /** * ata_pci_init_one - Initialize/register PCI IDE host controller @@ -718,8 +949,8 @@ int ata_pci_init_one (struct pci_dev *pd unsigned int n_ports) { struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent = NULL; - struct ata_port_info *port[2]; + struct ata_host *host = NULL; + const struct ata_port_info *port[2]; u8 mask; unsigned int legacy_mode = 0; int rc; @@ -732,10 +963,7 @@ int ata_pci_init_one (struct pci_dev *pd BUG_ON(n_ports < 1 || n_ports > 2); port[0] = port_info[0]; - if (n_ports > 1) - port[1] = port_info[1]; - else - port[1] = port[0]; + port[1] = (n_ports > 1) ? port_info[1] : NULL; /* FIXME: Really for ATA it isn't safe because the device may be multi-purpose and we want to leave it alone if it was already @@ -743,7 +971,7 @@ int ata_pci_init_one (struct pci_dev *pd Checking dev->is_enabled is insufficient as this is not set at boot for the primary video which is BIOS enabled - */ + */ rc = pcim_enable_device(pdev); if (rc) @@ -769,96 +997,68 @@ #if defined(CONFIG_NO_ATA_LEGACY) #endif } + /* alloc and init host */ + host = ata_host_alloc_pinfo(dev, port, n_ports); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, + "failed to allocate ATA host\n"); + rc = -ENOMEM; + goto err_out; + } + if (!legacy_mode) { - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); + unsigned int port_mask; + + port_mask = ATA_PORT_PRIMARY; + if (n_ports > 1) + port_mask |= ATA_PORT_SECONDARY; + + rc = ata_pci_init_native_host(host, port_mask); + if (rc) goto err_out; - } } else { - /* Deal with combined mode hack. This side of the logic all - goes away once the combined mode hack is killed in 2.6.21 */ - if (!devm_request_region(dev, ATA_PRIMARY_CMD, 8, "libata")) { - struct resource *conflict, res; - res.start = ATA_PRIMARY_CMD; - res.end = ATA_PRIMARY_CMD + 8 - 1; - conflict = ____request_resource(&ioport_resource, &res); - while (conflict->child) - conflict = ____request_resource(conflict, &res); - if (!strcmp(conflict->name, "libata")) - legacy_mode |= ATA_PORT_PRIMARY; - else { - pcim_pin_device(pdev); - printk(KERN_WARNING "ata: 0x%0X IDE port busy\n" \ - "ata: conflict with %s\n", - ATA_PRIMARY_CMD, - conflict->name); - } - } else - legacy_mode |= ATA_PORT_PRIMARY; - - if (!devm_request_region(dev, ATA_SECONDARY_CMD, 8, "libata")) { - struct resource *conflict, res; - res.start = ATA_SECONDARY_CMD; - res.end = ATA_SECONDARY_CMD + 8 - 1; - conflict = ____request_resource(&ioport_resource, &res); - while (conflict->child) - conflict = ____request_resource(conflict, &res); - if (!strcmp(conflict->name, "libata")) - legacy_mode |= ATA_PORT_SECONDARY; - else { - pcim_pin_device(pdev); - printk(KERN_WARNING "ata: 0x%X IDE port busy\n" \ - "ata: conflict with %s\n", - ATA_SECONDARY_CMD, - conflict->name); - } - } else - legacy_mode |= ATA_PORT_SECONDARY; - - if (legacy_mode & ATA_PORT_PRIMARY) - pci_request_region(pdev, 1, DRV_NAME); - if (legacy_mode & ATA_PORT_SECONDARY) - pci_request_region(pdev, 3, DRV_NAME); - /* If there is a DMA resource, allocate it */ - pci_request_region(pdev, 4, DRV_NAME); - } + int was_busy = 0; - /* we have legacy mode, but all ports are unavailable */ - if (legacy_mode == (1 << 3)) { - rc = -EBUSY; - goto err_out; + rc = ata_init_legacy_host(host, &legacy_mode, &was_busy); + if (was_busy) + pcim_pin_device(pdev); + if (rc) + goto err_out; + + /* request respective PCI regions, may fail */ + rc = pci_request_region(pdev, 1, DRV_NAME); + rc = pci_request_region(pdev, 3, DRV_NAME); } - /* TODO: If we get no DMA mask we should fall back to PIO */ - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + /* init BMDMA, may fail */ + ata_pci_init_bmdma(host); + pci_set_master(pdev); + + /* start host and request IRQ */ + rc = ata_host_start(host); if (rc) goto err_out; - if (legacy_mode) { - probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode); - } else { - if (n_ports == 2) - probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - else - probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY); + if (!legacy_mode) + rc = devm_request_irq(dev, pdev->irq, + port_info[0]->port_ops->irq_handler, + IRQF_SHARED, DRV_NAME, host); + else { + irq_handler_t handler[2] = { host->ops->irq_handler, + host->ops->irq_handler }; + unsigned int irq_flags[2] = { IRQF_SHARED, IRQF_SHARED }; + void *dev_id[2] = { host, host }; + + rc = ata_request_legacy_irqs(host, handler, irq_flags, dev_id); } - if (!probe_ent) { - rc = -ENOMEM; + if (rc) goto err_out; - } - - pci_set_master(pdev); - if (!ata_device_add(probe_ent)) { - rc = -ENODEV; + /* register */ + rc = ata_host_register(host, port_info[0]->sht); + if (rc) goto err_out; - } - devm_kfree(dev, probe_ent); devres_remove_group(dev, NULL); return 0; @@ -893,12 +1093,12 @@ int ata_pci_clear_simplex(struct pci_dev return 0; } -unsigned long ata_pci_default_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long xfer_mask) +unsigned long ata_pci_default_filter(struct ata_device *adev, unsigned long xfer_mask) { /* Filter out DMA modes if the device has been configured by the BIOS as PIO only */ - if (ap->ioaddr.bmdma_addr == 0) + if (adev->ap->ioaddr.bmdma_addr == 0) xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); return xfer_mask; } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 1f1e3a5..5f4d40c 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -52,6 +52,7 @@ enum { ATA_DNXFER_QUIET = (1 << 31), }; +extern unsigned int ata_print_id; extern struct workqueue_struct *ata_aux_wq; extern int atapi_enabled; extern int atapi_dmadir; @@ -92,10 +93,7 @@ extern int ata_flush_cache(struct ata_de extern void ata_dev_init(struct ata_device *dev); extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); -extern void ata_port_init(struct ata_port *ap, struct ata_host *host, - const struct ata_probe_ent *ent, unsigned int port_no); -extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev, - const struct ata_port_info *port); +extern struct ata_port *ata_port_alloc(struct ata_host *host); /* libata-acpi.c */ #ifdef CONFIG_SATA_ACPI @@ -113,8 +111,8 @@ static inline int ata_acpi_push_id(struc #endif /* libata-scsi.c */ -extern struct scsi_transport_template ata_scsi_transport_template; - +extern int ata_scsi_add_hosts(struct ata_host *host, + struct scsi_host_template *sht); extern void ata_scsi_scan_host(struct ata_port *ap); extern int ata_scsi_offline_dev(struct ata_device *dev); extern void ata_scsi_hotplug(struct work_struct *work); diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index 11ea552..d40edeb 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -34,7 +34,7 @@ #include #include #define DRV_NAME "pata_ali" -#define DRV_VERSION "0.7.3" +#define DRV_VERSION "0.7.4" /* * Cable special cases @@ -90,59 +90,6 @@ static int ali_c2_cable_detect(struct at } /** - * ali_early_error_handler - reset for eary chip - * @ap: ATA port - * - * Handle the reset callback for the later chips with cable detect - */ - -static int ali_c2_pre_reset(struct ata_port *ap) -{ - ap->cbl = ali_c2_cable_detect(ap); - return ata_std_prereset(ap); -} - -static void ali_c2_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, ali_c2_pre_reset, - ata_std_softreset, NULL, - ata_std_postreset); -} - -/** - * ali_early_cable_detect - cable detection - * @ap: ATA port - * - * Perform cable detection for older chipsets. This turns out to be - * rather easy to implement - */ - -static int ali_early_cable_detect(struct ata_port *ap) -{ - return ATA_CBL_PATA40; -} - -/** - * ali_early_probe_init - reset for early chip - * @ap: ATA port - * - * Handle the reset callback for the early (pre cable detect) chips. - */ - -static int ali_early_pre_reset(struct ata_port *ap) -{ - ap->cbl = ali_early_cable_detect(ap); - return ata_std_prereset(ap); -} - -static void ali_early_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, ali_early_pre_reset, - ata_std_softreset, NULL, - ata_std_postreset); -} - -/** * ali_20_filter - filter for earlier ALI DMA * @ap: ALi ATA port * @adev: attached device @@ -151,7 +98,7 @@ static void ali_early_error_handler(stru * fix that later on. Also ensure we do not do UDMA on WDC drives */ -static unsigned long ali_20_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long ali_20_filter(struct ata_device *adev, unsigned long mask) { char model_num[ATA_ID_PROD_LEN + 1]; /* No DMA on anything but a disk for now */ @@ -160,7 +107,7 @@ static unsigned long ali_20_filter(const ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); if (strstr(model_num, "WDC")) return mask &= ~ATA_MASK_UDMA; - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** @@ -314,7 +261,6 @@ static void ali_set_dmamode(struct ata_p /** * ali_lock_sectors - Keep older devices to 255 sector mode - * @ap: ATA port * @adev: Device * * Called during the bus probe for each device that is found. We use @@ -324,7 +270,7 @@ static void ali_set_dmamode(struct ata_p * slower PIO methods */ -static void ali_lock_sectors(struct ata_port *ap, struct ata_device *adev) +static void ali_lock_sectors(struct ata_device *adev) { adev->max_sectors = 255; } @@ -366,8 +312,9 @@ static struct ata_port_operations ali_ea .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_early_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -402,8 +349,9 @@ static struct ata_port_operations ali_20 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_early_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -440,8 +388,9 @@ static struct ata_port_operations ali_c2 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_c2_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ali_c2_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -477,8 +426,9 @@ static struct ata_port_operations ali_c5 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_c2_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ali_c2_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index 1838176..536ee89 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -25,7 +25,7 @@ #include #include #define DRV_NAME "pata_amd" -#define DRV_VERSION "0.2.8" +#define DRV_VERSION "0.3.8" /** * timing_setup - shared timing computation and load @@ -119,32 +119,25 @@ static void timing_setup(struct ata_port } /** - * amd_probe_init - cable detection + * amd_probe_init - perform reset handling * @ap: ATA port * - * Perform cable detection. The BIOS stores this in PCI config - * space for us. + * Reset sequence checking enable bits to see which ports are + * active. */ static int amd_pre_reset(struct ata_port *ap) { - static const u32 bitmask[2] = {0x03, 0x0C}; static const struct pci_bits amd_enable_bits[] = { { 0x40, 1, 0x02, 0x02 }, { 0x40, 1, 0x01, 0x01 } }; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 ata66; if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x42, &ata66); - if (ata66 & bitmask[ap->port_no]) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -156,28 +149,16 @@ static void amd_error_handler(struct ata ata_std_postreset); } -static int amd_early_pre_reset(struct ata_port *ap) +static int amd_cable_detect(struct ata_port *ap) { + static const u32 bitmask[2] = {0x03, 0x0C}; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - static struct pci_bits amd_enable_bits[] = { - { 0x40, 1, 0x02, 0x02 }, - { 0x40, 1, 0x01, 0x01 } - }; - - if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) - return -ENOENT; - - /* No host side cable detection */ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); - -} + u8 ata66; -static void amd_early_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, amd_early_pre_reset, - ata_std_softreset, NULL, - ata_std_postreset); + pci_read_config_byte(pdev, 0x42, &ata66); + if (ata66 & bitmask[ap->port_no]) + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; } /** @@ -247,31 +228,16 @@ static void amd133_set_dmamode(struct at */ static int nv_pre_reset(struct ata_port *ap) { - static const u8 bitmask[2] = {0x03, 0x0C}; static const struct pci_bits nv_enable_bits[] = { { 0x50, 1, 0x02, 0x02 }, { 0x50, 1, 0x01, 0x01 } }; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 ata66; - u16 udma; if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x52, &ata66); - if (ata66 & bitmask[ap->port_no]) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; - - /* We now have to double check because the Nvidia boxes BIOS - doesn't always set the cable bits but does set mode bits */ - - pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma); - if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400) - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } @@ -281,6 +247,29 @@ static void nv_error_handler(struct ata_ ata_std_softreset, NULL, ata_std_postreset); } + +static int nv_cable_detect(struct ata_port *ap) +{ + static const u8 bitmask[2] = {0x03, 0x0C}; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 ata66; + u16 udma; + int cbl; + + pci_read_config_byte(pdev, 0x52, &ata66); + if (ata66 & bitmask[ap->port_no]) + cbl = ATA_CBL_PATA80; + else + cbl = ATA_CBL_PATA40; + + /* We now have to double check because the Nvidia boxes BIOS + doesn't always set the cable bits but does set mode bits */ + pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma); + if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400) + cbl = ATA_CBL_PATA80; + return cbl; +} + /** * nv100_set_piomode - set initial PIO mode data * @ap: ATA interface @@ -353,8 +342,9 @@ static struct ata_port_operations amd33_ .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = amd_early_error_handler, + .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -387,8 +377,9 @@ static struct ata_port_operations amd66_ .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = amd_early_error_handler, + .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -423,6 +414,7 @@ static struct ata_port_operations amd100 .thaw = ata_bmdma_thaw, .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -457,6 +449,7 @@ static struct ata_port_operations amd133 .thaw = ata_bmdma_thaw, .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = amd_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -491,6 +484,7 @@ static struct ata_port_operations nv100_ .thaw = ata_bmdma_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = nv_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -525,6 +519,7 @@ static struct ata_port_operations nv133_ .thaw = ata_bmdma_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = nv_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c index 21c3028..00e9ec3 100644 --- a/drivers/ata/pata_artop.c +++ b/drivers/ata/pata_artop.c @@ -49,8 +49,6 @@ static int artop6210_pre_reset(struct at if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) return -ENOENT; - - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -85,18 +83,28 @@ static int artop6260_pre_reset(struct at }; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 tmp; /* Odd numbered device ids are the units with enable bits (the -R cards) */ if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) return -ENOENT; + return ata_std_prereset(ap); +} +/** + * artop6260_cable_detect - identify cable type + * @ap: Port + * + * Identify the cable type for the ARTOp interface in question + */ + +static int artop6260_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 tmp; pci_read_config_byte(pdev, 0x49, &tmp); if (tmp & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } /** @@ -225,7 +233,7 @@ static void artop6260_set_piomode(struct /** * artop6210_set_dmamode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring - * @adev: um + * @adev: Device whose timings we are configuring * * Set DMA mode for device, in host controller PCI config space. * @@ -333,6 +341,7 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = artop6210_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -366,6 +375,7 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = artop6260_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = artop6260_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index 51d9923..39c871a 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -22,7 +22,7 @@ #include #include #define DRV_NAME "pata_atiixp" -#define DRV_VERSION "0.4.4" +#define DRV_VERSION "0.4.5" enum { ATIIXP_IDE_PIO_TIMING = 0x40, @@ -35,23 +35,15 @@ enum { static int atiixp_pre_reset(struct ata_port *ap) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); static const struct pci_bits atiixp_enable_bits[] = { { 0x48, 1, 0x01, 0x00 }, { 0x48, 1, 0x08, 0x00 } }; - u8 udma; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no])) return -ENOENT; - /* Hack from drivers/ide/pci. Really we want to know how to do the - raw detection not play follow the bios mode guess */ - pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma); - if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -60,6 +52,19 @@ static void atiixp_error_handler(struct ata_bmdma_drive_eh(ap, atiixp_pre_reset, ata_std_softreset, NULL, ata_std_postreset); } +static int atiixp_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 udma; + + /* Hack from drivers/ide/pci. Really we want to know how to do the + raw detection not play follow the bios mode guess */ + pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma); + if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40) + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; +} + /** * atiixp_set_pio_timing - set initial PIO mode data * @ap: ATA interface @@ -245,6 +250,7 @@ static struct ata_port_operations atiixp .thaw = ata_bmdma_thaw, .error_handler = atiixp_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = atiixp_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = atiixp_bmdma_start, diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c new file mode 100644 index 0000000..2105985 --- /dev/null +++ b/drivers/ata/pata_cmd640.c @@ -0,0 +1,312 @@ +/* + * pata_cmd640.c - CMD640 PCI PATA for new ATA layer + * (C) 2007 Red Hat Inc + * Alan Cox + * + * Based upon + * linux/drivers/ide/pci/cmd640.c Version 1.02 Sep 01, 1996 + * + * Copyright (C) 1995-1996 Linus Torvalds & authors (see driver) + * + * This drives only the PCI version of the controller. If you have a + * VLB one then we have enough docs to support it but you can write + * your own code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_cmd640" +#define DRV_VERSION "0.0.5" + +struct cmd640_reg { + int last; + u8 reg58[ATA_MAX_DEVICES]; +}; + +enum { + CFR = 0x50, + CNTRL = 0x51, + CMDTIM = 0x52, + ARTIM0 = 0x53, + DRWTIM0 = 0x54, + ARTIM23 = 0x57, + DRWTIM23 = 0x58, + BRST = 0x59 +}; + +/** + * cmd640_set_piomode - set initial PIO mode data + * @ap: ATA port + * @adev: ATA device + * + * Called to do the PIO mode setup. + */ + +static void cmd640_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct cmd640_reg *timing = ap->private_data; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct ata_timing t; + const unsigned long T = 1000000 / 33; + const u8 setup_data[] = { 0x40, 0x40, 0x40, 0x80, 0x00 }; + u8 reg; + int arttim = ARTIM0 + 2 * adev->devno; + struct ata_device *pair = ata_dev_pair(adev); + + if (ata_timing_compute(adev, adev->pio_mode, &t, T, 0) < 0) { + printk(KERN_ERR DRV_NAME ": mode computation failed.\n"); + return; + } + + /* The second channel has shared timings and the setup timing is + messy to switch to merge it for worst case */ + if (ap->port_no && pair) { + struct ata_timing p; + ata_timing_compute(pair, pair->pio_mode, &p, T, 1); + ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP); + } + + /* Make the timings fit */ + if (t.recover > 16) { + t.active += t.recover - 16; + t.recover = 16; + } + if (t.active > 16) + t.active = 16; + + /* Now convert the clocks into values we can actually stuff into + the chip */ + + if (t.recover > 1) + t.recover--; /* 640B only */ + else + t.recover = 15; + + if (t.setup > 4) + t.setup = 0xC0; + else + t.setup = setup_data[t.setup]; + + if (ap->port_no == 0) { + t.active &= 0x0F; /* 0 = 16 */ + + /* Load setup timing */ + pci_read_config_byte(pdev, arttim, ®); + reg &= 0x3F; + reg |= t.setup; + pci_write_config_byte(pdev, arttim, reg); + + /* Load active/recovery */ + pci_write_config_byte(pdev, arttim + 1, (t.active << 4) | t.recover); + } else { + /* Save the shared timings for channel, they will be loaded + by qc_issue_prot. Reloading the setup time is expensive + so we keep a merged one loaded */ + pci_read_config_byte(pdev, ARTIM23, ®); + reg &= 0x3F; + reg |= t.setup; + pci_write_config_byte(pdev, ARTIM23, reg); + timing->reg58[adev->devno] = (t.active << 4) | t.recover; + } +} + + +/** + * cmd640_qc_issue_prot - command preparation hook + * @qc: Command to be issued + * + * Channel 1 has shared timings. We must reprogram the + * clock each drive 2/3 switch we do. + */ + +static unsigned int cmd640_qc_issue_prot(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_device *adev = qc->dev; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct cmd640_reg *timing = ap->private_data; + + if (ap->port_no != 0 && adev->devno != timing->last) { + pci_write_config_byte(pdev, DRWTIM23, timing->reg58[adev->devno]); + timing->last = adev->devno; + } + return ata_qc_issue_prot(qc); +} + +/** + * cmd640_port_start - port setup + * @ap: ATA port being set up + * + * The CMD640 needs to maintain private data structures so we + * allocate space here. + */ + +static int cmd640_port_start(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct cmd640_reg *timing; + + int ret = ata_port_start(ap); + if (ret < 0) + return ret; + + timing = devm_kzalloc(&pdev->dev, sizeof(struct cmd640_reg), GFP_KERNEL); + if (timing == NULL) + return -ENOMEM; + timing->last = -1; /* Force a load */ + ap->private_data = timing; + return ret; +} + +static struct scsi_host_template cmd640_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .slave_destroy = ata_scsi_slave_destroy, + .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM + .resume = ata_scsi_device_resume, + .suspend = ata_scsi_device_suspend, +#endif +}; + +static struct ata_port_operations cmd640_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cmd640_set_piomode, + .mode_filter = ata_pci_default_filter, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = cmd640_qc_issue_prot, + + /* In theory this is not needed once we kill the prefetcher */ + .data_xfer = ata_data_xfer_noirq, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + .irq_on = ata_irq_on, + .irq_ack = ata_irq_ack, + + .port_start = cmd640_port_start, +}; + +static void cmd640_hardware_init(struct pci_dev *pdev) +{ + u8 r; + u8 ctrl; + + /* CMD640 detected, commiserations */ + pci_write_config_byte(pdev, 0x5B, 0x00); + /* Get version info */ + pci_read_config_byte(pdev, CFR, &r); + /* PIO0 command cycles */ + pci_write_config_byte(pdev, CMDTIM, 0); + /* 512 byte bursts (sector) */ + pci_write_config_byte(pdev, BRST, 0x40); + /* + * A reporter a long time ago + * Had problems with the data fifo + * So don't run the risk + * Of putting crap on the disk + * For its better just to go slow + */ + /* Do channel 0 */ + pci_read_config_byte(pdev, CNTRL, &ctrl); + pci_write_config_byte(pdev, CNTRL, ctrl | 0xC0); + /* Ditto for channel 1 */ + pci_read_config_byte(pdev, ARTIM23, &ctrl); + ctrl |= 0x0C; + pci_write_config_byte(pdev, ARTIM23, ctrl); +} + +static int cmd640_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + static struct ata_port_info info = { + .sht = &cmd640_sht, + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .port_ops = &cmd640_port_ops + }; + + static struct ata_port_info *port_info[2] = { &info, &info }; + + cmd640_hardware_init(pdev); + return ata_pci_init_one(pdev, port_info, 2); +} + +static int cmd640_reinit_one(struct pci_dev *pdev) +{ + cmd640_hardware_init(pdev); +#ifdef CONFIG_PM + return ata_pci_device_resume(pdev); +#else + return 0; +#endif +} + +static const struct pci_device_id cmd640[] = { + { PCI_VDEVICE(CMD, 0x640), 0 }, + { }, +}; + +static struct pci_driver cmd640_pci_driver = { + .name = DRV_NAME, + .id_table = cmd640, + .probe = cmd640_init_one, + .remove = ata_pci_remove_one, +#ifdef CONFIG_PM + .suspend = ata_pci_device_suspend, +#endif + .resume = cmd640_reinit_one, +}; + +static int __init cmd640_init(void) +{ + return pci_register_driver(&cmd640_pci_driver); +} + +static void __exit cmd640_exit(void) +{ + pci_unregister_driver(&cmd640_pci_driver); +} + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for CMD640 PATA controllers"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, cmd640); +MODULE_VERSION(DRV_VERSION); + +module_init(cmd640_init); +module_exit(cmd640_exit); diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index 5b13bdd..3989cc5 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -75,13 +75,7 @@ enum { DTPR1 = 0x7C }; -static int cmd64x_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static int cmd648_pre_reset(struct ata_port *ap) +static int cmd648_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 r; @@ -89,21 +83,8 @@ static int cmd648_pre_reset(struct ata_p /* Check cable detect bits */ pci_read_config_byte(pdev, BMIDECSR, &r); if (r & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; - - return ata_std_prereset(ap); -} - -static void cmd64x_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, cmd64x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - -static void cmd648_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, cmd648_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; } /** @@ -304,8 +285,9 @@ static struct ata_port_operations cmd64x .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cmd64x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -338,8 +320,9 @@ static struct ata_port_operations cmd646 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cmd64x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -372,8 +355,9 @@ static struct ata_port_operations cmd648 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cmd648_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = cmd648_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index 55cc293..79bef0d 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -139,18 +139,6 @@ static void cs5520_set_piomode(struct at cs5520_set_timings(ap, adev, adev->pio_mode); } - -static int cs5520_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static void cs5520_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, cs5520_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - static struct scsi_host_template cs5520_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -186,8 +174,9 @@ static struct ata_port_operations cs5520 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cs5520_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -197,7 +186,6 @@ static struct ata_port_operations cs5520 .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -205,91 +193,104 @@ static struct ata_port_operations cs5520 .port_start = ata_port_start, }; -static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) +static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { + struct ata_port_info pi = { + .flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, + .port_ops = &cs5520_port_ops, + }; + const struct ata_port_info *ppi[2]; u8 pcicfg; - void __iomem *iomap[5]; - static struct ata_probe_ent probe[2]; - int ports = 0; + void *iomap[5]; + struct ata_host *host; + struct ata_ioports *ioaddr; + int i, rc; /* IDE port enable bits */ - pci_read_config_byte(dev, 0x60, &pcicfg); + pci_read_config_byte(pdev, 0x60, &pcicfg); /* Check if the ATA ports are enabled */ if ((pcicfg & 3) == 0) return -ENODEV; + ppi[0] = ppi[1] = &ata_dummy_port_info; + if (pcicfg & 1) + ppi[0] = π + if (pcicfg & 2) + ppi[1] = π + if ((pcicfg & 0x40) == 0) { - printk(KERN_WARNING DRV_NAME ": DMA mode disabled. Enabling.\n"); - pci_write_config_byte(dev, 0x60, pcicfg | 0x40); + dev_printk(KERN_WARNING, &pdev->dev, + "DMA mode disabled. Enabling.\n"); + pci_write_config_byte(pdev, 0x60, pcicfg | 0x40); } + pi.mwdma_mask = id->driver_data; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2); + if (!host) + return -ENOMEM; + /* Perform set up for DMA */ - if (pci_enable_device_bars(dev, 1<<2)) { + if (pci_enable_device_bars(pdev, 1<<2)) { printk(KERN_ERR DRV_NAME ": unable to configure BAR2.\n"); return -ENODEV; } - pci_set_master(dev); - if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) { + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { printk(KERN_ERR DRV_NAME ": unable to configure DMA mask.\n"); return -ENODEV; } - if (pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK)) { + if (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { printk(KERN_ERR DRV_NAME ": unable to configure consistent DMA mask.\n"); return -ENODEV; } - /* Map IO ports */ - iomap[0] = devm_ioport_map(&dev->dev, 0x1F0, 8); - iomap[1] = devm_ioport_map(&dev->dev, 0x3F6, 1); - iomap[2] = devm_ioport_map(&dev->dev, 0x170, 8); - iomap[3] = devm_ioport_map(&dev->dev, 0x376, 1); - iomap[4] = pcim_iomap(dev, 2, 0); + /* Map IO ports and initialize host accordingly */ + iomap[0] = devm_ioport_map(&pdev->dev, 0x1F0, 8); + iomap[1] = devm_ioport_map(&pdev->dev, 0x3F6, 1); + iomap[2] = devm_ioport_map(&pdev->dev, 0x170, 8); + iomap[3] = devm_ioport_map(&pdev->dev, 0x376, 1); + iomap[4] = pcim_iomap(pdev, 2, 0); if (!iomap[0] || !iomap[1] || !iomap[2] || !iomap[3] || !iomap[4]) return -ENOMEM; - /* We have to do our own plumbing as the PCI setup for this - chipset is non-standard so we can't punt to the libata code */ - - INIT_LIST_HEAD(&probe[0].node); - probe[0].dev = pci_dev_to_dev(dev); - probe[0].port_ops = &cs5520_port_ops; - probe[0].sht = &cs5520_sht; - probe[0].pio_mask = 0x1F; - probe[0].mwdma_mask = id->driver_data; - probe[0].irq = 14; - probe[0].irq_flags = 0; - probe[0].port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST; - probe[0].n_ports = 1; - probe[0].port[0].cmd_addr = iomap[0]; - probe[0].port[0].ctl_addr = iomap[1]; - probe[0].port[0].altstatus_addr = iomap[1]; - probe[0].port[0].bmdma_addr = iomap[4]; - - /* The secondary lurks at different addresses but is otherwise - the same beastie */ - - probe[1] = probe[0]; - INIT_LIST_HEAD(&probe[1].node); - probe[1].irq = 15; - probe[1].port[0].cmd_addr = iomap[2]; - probe[1].port[0].ctl_addr = iomap[3]; - probe[1].port[0].altstatus_addr = iomap[3]; - probe[1].port[0].bmdma_addr = iomap[4] + 8; - - /* Let libata fill in the port details */ - ata_std_ports(&probe[0].port[0]); - ata_std_ports(&probe[1].port[0]); - - /* Now add the ports that are active */ - if (pcicfg & 1) - ports += ata_device_add(&probe[0]); - if (pcicfg & 2) - ports += ata_device_add(&probe[1]); - if (ports) - return 0; - return -ENODEV; + ioaddr = &host->ports[0]->ioaddr; + ioaddr->cmd_addr = iomap[0]; + ioaddr->ctl_addr = iomap[1]; + ioaddr->altstatus_addr = iomap[1]; + ioaddr->bmdma_addr = iomap[4]; + ata_std_ports(ioaddr); + + ioaddr = &host->ports[1]->ioaddr; + ioaddr->cmd_addr = iomap[2]; + ioaddr->ctl_addr = iomap[3]; + ioaddr->altstatus_addr = iomap[3]; + ioaddr->bmdma_addr = iomap[4] + 8; + ata_std_ports(ioaddr); + + /* activate the host */ + pci_set_master(pdev); + rc = ata_host_start(host); + if (rc) + return rc; + + for (i = 0; i < 2; i++) { + static const int irq[] = { 14, 15 }; + struct ata_port *ap = host->ports[0]; + + if (ata_port_is_dummy(ap)) + continue; + + rc = devm_request_irq(&pdev->dev, irq[ap->port_no], + ata_interrupt, 0, DRV_NAME, host); + if (rc) + return rc; + } + + return ata_host_register(host, &cs5520_sht); } /** diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index db63e80..29642d5 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -160,18 +160,6 @@ static unsigned int cs5530_qc_issue_prot return ata_qc_issue_prot(qc); } -static int cs5530_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static void cs5530_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, cs5530_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - - static struct scsi_host_template cs5530_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -213,8 +201,9 @@ static struct ata_port_operations cs5530 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cs5530_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = cs5530_qc_issue_prot, diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c index 1572e5c..08cccc9 100644 --- a/drivers/ata/pata_cs5535.c +++ b/drivers/ata/pata_cs5535.c @@ -70,36 +70,23 @@ #define CS5535_CABLE_DETECT 0x48 #define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL)==0x00009172 ) /** - * cs5535_pre_reset - detect cable type + * cs5535_cable_detect - detect cable type * @ap: Port to detect on * * Perform cable detection for ATA66 capable cable. Return a libata * cable type. */ -static int cs5535_pre_reset(struct ata_port *ap) +static int cs5535_cable_detect(struct ata_port *ap) { u8 cable; struct pci_dev *pdev = to_pci_dev(ap->host->dev); pci_read_config_byte(pdev, CS5535_CABLE_DETECT, &cable); if (cable & 1) - ap->cbl = ATA_CBL_PATA80; + return ATA_CBL_PATA80; else - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -/** - * cs5535_error_handler - reset/probe - * @ap: Port to reset - * - * Reset and configure a port - */ - -static void cs5535_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, cs5535_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + return ATA_CBL_PATA40; } /** @@ -205,8 +192,9 @@ static struct ata_port_operations cs5535 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cs5535_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = cs5535_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c index f69dde5..6ec049c 100644 --- a/drivers/ata/pata_cypress.c +++ b/drivers/ata/pata_cypress.c @@ -41,17 +41,6 @@ enum { CY82_INDEX_TIMEOUT = 0x32 }; -static int cy82c693_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static void cy82c693_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, cy82c693_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - /** * cy82c693_set_piomode - set initial PIO mode data * @ap: ATA interface @@ -156,8 +145,9 @@ static struct ata_port_operations cy82c6 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cy82c693_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c index dac7a65..a321685 100644 --- a/drivers/ata/pata_efar.c +++ b/drivers/ata/pata_efar.c @@ -22,10 +22,10 @@ #include #include #define DRV_NAME "pata_efar" -#define DRV_VERSION "0.4.3" +#define DRV_VERSION "0.4.4" /** - * efar_pre_reset - check for 40/80 pin + * efar_pre_reset - Enable bits * @ap: Port * * Perform cable detection for the EFAR ATA interface. This is @@ -38,18 +38,11 @@ static int efar_pre_reset(struct ata_por { 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */ { 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */ }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 tmp; if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x47, &tmp); - if (tmp & (2 >> ap->port_no)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } @@ -67,6 +60,25 @@ static void efar_error_handler(struct at } /** + * efar_cable_detect - check for 40/80 pin + * @ap: Port + * + * Perform cable detection for the EFAR ATA interface. This is + * different to the PIIX arrangement + */ + +static int efar_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 tmp; + + pci_read_config_byte(pdev, 0x47, &tmp); + if (tmp & (2 >> ap->port_no)) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + +/** * efar_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring * @adev: um @@ -256,6 +268,7 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = efar_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = efar_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index baf35f8..93cfa6d 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -27,7 +27,7 @@ #include #include #define DRV_NAME "pata_hpt366" -#define DRV_VERSION "0.6.0" +#define DRV_VERSION "0.6.1" struct hpt_clock { u8 xfer_speed; @@ -169,13 +169,12 @@ static int hpt_dma_blacklisted(const str /** * hpt366_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device * * Block UDMA on devices that cause trouble with this controller. */ -static unsigned long hpt366_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long hpt366_filter(struct ata_device *adev, unsigned long mask) { if (adev->class == ATA_DEV_ATA) { if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33)) @@ -185,7 +184,7 @@ static unsigned long hpt366_filter(const if (hpt_dma_blacklisted(adev, "UDMA4", bad_ata66_4)) mask &= ~(0x0F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** @@ -210,24 +209,28 @@ static u32 hpt36x_find_mode(struct ata_p return 0xffffffffU; /* silence compiler warning */ } +static int hpt36x_cable_detect(struct ata_port *ap) +{ + u8 ata66; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + + pci_read_config_byte(pdev, 0x5A, &ata66); + if (ata66 & (1 << ap->port_no)) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + static int hpt36x_pre_reset(struct ata_port *ap) { static const struct pci_bits hpt36x_enable_bits[] = { { 0x50, 1, 0x04, 0x04 }, { 0x54, 1, 0x04, 0x04 } }; - - u8 ata66; struct pci_dev *pdev = to_pci_dev(ap->host->dev); if (!pci_test_config_bits(pdev, &hpt36x_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x5A, &ata66); - if (ata66 & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } @@ -354,6 +357,7 @@ static struct ata_port_operations hpt366 .thaw = ata_bmdma_thaw, .error_handler = hpt36x_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = hpt36x_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index f331eee..41d8312 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -8,6 +8,7 @@ * Copyright (C) 1999-2003 Andre Hedrick * Portions Copyright (C) 2001 Sun Microsystems, Inc. * Portions Copyright (C) 2003 Red Hat Inc + * Portions Copyright (C) 2005-2006 MontaVista Software, Inc. * * TODO * PLL mode @@ -25,7 +26,7 @@ #include #include #define DRV_NAME "pata_hpt37x" -#define DRV_VERSION "0.6.0" +#define DRV_VERSION "0.6.5" struct hpt_clock { u8 xfer_speed; @@ -61,201 +62,75 @@ struct hpt_chip { * 31 FIFO enable. */ -/* from highpoint documentation. these are old values */ -static const struct hpt_clock hpt370_timings_33[] = { -/* { XFER_UDMA_5, 0x1A85F442, 0x16454e31 }, */ - { XFER_UDMA_5, 0x16454e31 }, - { XFER_UDMA_4, 0x16454e31 }, - { XFER_UDMA_3, 0x166d4e31 }, - { XFER_UDMA_2, 0x16494e31 }, - { XFER_UDMA_1, 0x164d4e31 }, - { XFER_UDMA_0, 0x16514e31 }, - - { XFER_MW_DMA_2, 0x26514e21 }, - { XFER_MW_DMA_1, 0x26514e33 }, - { XFER_MW_DMA_0, 0x26514e97 }, - - { XFER_PIO_4, 0x06514e21 }, - { XFER_PIO_3, 0x06514e22 }, - { XFER_PIO_2, 0x06514e33 }, - { XFER_PIO_1, 0x06914e43 }, - { XFER_PIO_0, 0x06914e57 }, - { 0, 0x06514e57 } +static struct hpt_clock hpt37x_timings_33[] = { + { XFER_UDMA_6, 0x12446231 }, /* 0x12646231 ?? */ + { XFER_UDMA_5, 0x12446231 }, + { XFER_UDMA_4, 0x12446231 }, + { XFER_UDMA_3, 0x126c6231 }, + { XFER_UDMA_2, 0x12486231 }, + { XFER_UDMA_1, 0x124c6233 }, + { XFER_UDMA_0, 0x12506297 }, + + { XFER_MW_DMA_2, 0x22406c31 }, + { XFER_MW_DMA_1, 0x22406c33 }, + { XFER_MW_DMA_0, 0x22406c97 }, + + { XFER_PIO_4, 0x06414e31 }, + { XFER_PIO_3, 0x06414e42 }, + { XFER_PIO_2, 0x06414e53 }, + { XFER_PIO_1, 0x06814e93 }, + { XFER_PIO_0, 0x06814ea7 } }; -static const struct hpt_clock hpt370_timings_66[] = { - { XFER_UDMA_5, 0x14846231 }, - { XFER_UDMA_4, 0x14886231 }, - { XFER_UDMA_3, 0x148c6231 }, - { XFER_UDMA_2, 0x148c6231 }, - { XFER_UDMA_1, 0x14906231 }, - { XFER_UDMA_0, 0x14986231 }, - - { XFER_MW_DMA_2, 0x26514e21 }, - { XFER_MW_DMA_1, 0x26514e33 }, - { XFER_MW_DMA_0, 0x26514e97 }, - - { XFER_PIO_4, 0x06514e21 }, - { XFER_PIO_3, 0x06514e22 }, - { XFER_PIO_2, 0x06514e33 }, - { XFER_PIO_1, 0x06914e43 }, - { XFER_PIO_0, 0x06914e57 }, - { 0, 0x06514e57 } +static struct hpt_clock hpt37x_timings_50[] = { + { XFER_UDMA_6, 0x12848242 }, + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a } }; -/* these are the current (4 sep 2001) timings from highpoint */ -static const struct hpt_clock hpt370a_timings_33[] = { - { XFER_UDMA_5, 0x12446231 }, - { XFER_UDMA_4, 0x12446231 }, - { XFER_UDMA_3, 0x126c6231 }, - { XFER_UDMA_2, 0x12486231 }, - { XFER_UDMA_1, 0x124c6233 }, - { XFER_UDMA_0, 0x12506297 }, - - { XFER_MW_DMA_2, 0x22406c31 }, - { XFER_MW_DMA_1, 0x22406c33 }, - { XFER_MW_DMA_0, 0x22406c97 }, - - { XFER_PIO_4, 0x06414e31 }, - { XFER_PIO_3, 0x06414e42 }, - { XFER_PIO_2, 0x06414e53 }, - { XFER_PIO_1, 0x06814e93 }, - { XFER_PIO_0, 0x06814ea7 }, - { 0, 0x06814ea7 } +static struct hpt_clock hpt37x_timings_66[] = { + { XFER_UDMA_6, 0x1c869c62 }, + { XFER_UDMA_5, 0x1cae9c62 }, /* 0x1c8a9c62 */ + { XFER_UDMA_4, 0x1c8a9c62 }, + { XFER_UDMA_3, 0x1c8e9c62 }, + { XFER_UDMA_2, 0x1c929c62 }, + { XFER_UDMA_1, 0x1c9a9c62 }, + { XFER_UDMA_0, 0x1c829c62 }, + + { XFER_MW_DMA_2, 0x2c829c62 }, + { XFER_MW_DMA_1, 0x2c829c66 }, + { XFER_MW_DMA_0, 0x2c829d2e }, + + { XFER_PIO_4, 0x0c829c62 }, + { XFER_PIO_3, 0x0c829c84 }, + { XFER_PIO_2, 0x0c829ca6 }, + { XFER_PIO_1, 0x0d029d26 }, + { XFER_PIO_0, 0x0d029d5e } }; -/* 2x 33MHz timings */ -static const struct hpt_clock hpt370a_timings_66[] = { - { XFER_UDMA_5, 0x1488e673 }, - { XFER_UDMA_4, 0x1488e673 }, - { XFER_UDMA_3, 0x1498e673 }, - { XFER_UDMA_2, 0x1490e673 }, - { XFER_UDMA_1, 0x1498e677 }, - { XFER_UDMA_0, 0x14a0e73f }, - - { XFER_MW_DMA_2, 0x2480fa73 }, - { XFER_MW_DMA_1, 0x2480fa77 }, - { XFER_MW_DMA_0, 0x2480fb3f }, - - { XFER_PIO_4, 0x0c82be73 }, - { XFER_PIO_3, 0x0c82be95 }, - { XFER_PIO_2, 0x0c82beb7 }, - { XFER_PIO_1, 0x0d02bf37 }, - { XFER_PIO_0, 0x0d02bf5f }, - { 0, 0x0d02bf5f } -}; - -static const struct hpt_clock hpt370a_timings_50[] = { - { XFER_UDMA_5, 0x12848242 }, - { XFER_UDMA_4, 0x12ac8242 }, - { XFER_UDMA_3, 0x128c8242 }, - { XFER_UDMA_2, 0x120c8242 }, - { XFER_UDMA_1, 0x12148254 }, - { XFER_UDMA_0, 0x121882ea }, - - { XFER_MW_DMA_2, 0x22808242 }, - { XFER_MW_DMA_1, 0x22808254 }, - { XFER_MW_DMA_0, 0x228082ea }, - - { XFER_PIO_4, 0x0a81f442 }, - { XFER_PIO_3, 0x0a81f443 }, - { XFER_PIO_2, 0x0a81f454 }, - { XFER_PIO_1, 0x0ac1f465 }, - { XFER_PIO_0, 0x0ac1f48a }, - { 0, 0x0ac1f48a } -}; - -static const struct hpt_clock hpt372_timings_33[] = { - { XFER_UDMA_6, 0x1c81dc62 }, - { XFER_UDMA_5, 0x1c6ddc62 }, - { XFER_UDMA_4, 0x1c8ddc62 }, - { XFER_UDMA_3, 0x1c8edc62 }, /* checkme */ - { XFER_UDMA_2, 0x1c91dc62 }, - { XFER_UDMA_1, 0x1c9adc62 }, /* checkme */ - { XFER_UDMA_0, 0x1c82dc62 }, /* checkme */ - - { XFER_MW_DMA_2, 0x2c829262 }, - { XFER_MW_DMA_1, 0x2c829266 }, /* checkme */ - { XFER_MW_DMA_0, 0x2c82922e }, /* checkme */ - - { XFER_PIO_4, 0x0c829c62 }, - { XFER_PIO_3, 0x0c829c84 }, - { XFER_PIO_2, 0x0c829ca6 }, - { XFER_PIO_1, 0x0d029d26 }, - { XFER_PIO_0, 0x0d029d5e }, - { 0, 0x0d029d5e } -}; - -static const struct hpt_clock hpt372_timings_50[] = { - { XFER_UDMA_5, 0x12848242 }, - { XFER_UDMA_4, 0x12ac8242 }, - { XFER_UDMA_3, 0x128c8242 }, - { XFER_UDMA_2, 0x120c8242 }, - { XFER_UDMA_1, 0x12148254 }, - { XFER_UDMA_0, 0x121882ea }, - - { XFER_MW_DMA_2, 0x22808242 }, - { XFER_MW_DMA_1, 0x22808254 }, - { XFER_MW_DMA_0, 0x228082ea }, - - { XFER_PIO_4, 0x0a81f442 }, - { XFER_PIO_3, 0x0a81f443 }, - { XFER_PIO_2, 0x0a81f454 }, - { XFER_PIO_1, 0x0ac1f465 }, - { XFER_PIO_0, 0x0ac1f48a }, - { 0, 0x0a81f443 } -}; - -static const struct hpt_clock hpt372_timings_66[] = { - { XFER_UDMA_6, 0x1c869c62 }, - { XFER_UDMA_5, 0x1cae9c62 }, - { XFER_UDMA_4, 0x1c8a9c62 }, - { XFER_UDMA_3, 0x1c8e9c62 }, - { XFER_UDMA_2, 0x1c929c62 }, - { XFER_UDMA_1, 0x1c9a9c62 }, - { XFER_UDMA_0, 0x1c829c62 }, - - { XFER_MW_DMA_2, 0x2c829c62 }, - { XFER_MW_DMA_1, 0x2c829c66 }, - { XFER_MW_DMA_0, 0x2c829d2e }, - - { XFER_PIO_4, 0x0c829c62 }, - { XFER_PIO_3, 0x0c829c84 }, - { XFER_PIO_2, 0x0c829ca6 }, - { XFER_PIO_1, 0x0d029d26 }, - { XFER_PIO_0, 0x0d029d5e }, - { 0, 0x0d029d26 } -}; - -static const struct hpt_clock hpt374_timings_33[] = { - { XFER_UDMA_6, 0x12808242 }, - { XFER_UDMA_5, 0x12848242 }, - { XFER_UDMA_4, 0x12ac8242 }, - { XFER_UDMA_3, 0x128c8242 }, - { XFER_UDMA_2, 0x120c8242 }, - { XFER_UDMA_1, 0x12148254 }, - { XFER_UDMA_0, 0x121882ea }, - - { XFER_MW_DMA_2, 0x22808242 }, - { XFER_MW_DMA_1, 0x22808254 }, - { XFER_MW_DMA_0, 0x228082ea }, - - { XFER_PIO_4, 0x0a81f442 }, - { XFER_PIO_3, 0x0a81f443 }, - { XFER_PIO_2, 0x0a81f454 }, - { XFER_PIO_1, 0x0ac1f465 }, - { XFER_PIO_0, 0x0ac1f48a }, - { 0, 0x06814e93 } -}; static const struct hpt_chip hpt370 = { "HPT370", 48, { - hpt370_timings_33, + hpt37x_timings_33, NULL, NULL, - hpt370_timings_66 + NULL } }; @@ -263,10 +138,10 @@ static const struct hpt_chip hpt370a = { "HPT370A", 48, { - hpt370a_timings_33, + hpt37x_timings_33, NULL, - hpt370a_timings_50, - hpt370a_timings_66 + hpt37x_timings_50, + NULL } }; @@ -274,10 +149,10 @@ static const struct hpt_chip hpt372 = { "HPT372", 55, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -285,10 +160,10 @@ static const struct hpt_chip hpt302 = { "HPT302", 66, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -296,10 +171,10 @@ static const struct hpt_chip hpt371 = { "HPT371", 66, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -307,10 +182,10 @@ static const struct hpt_chip hpt372a = { "HPT372A", 66, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -318,7 +193,7 @@ static const struct hpt_chip hpt374 = { "HPT374", 48, { - hpt374_timings_33, + hpt37x_timings_33, NULL, NULL, NULL @@ -397,13 +272,12 @@ static const char *bad_ata100_5[] = { /** * hpt370_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device * * Block UDMA on devices that cause trouble with this controller. */ -static unsigned long hpt370_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long hpt370_filter(struct ata_device *adev, unsigned long mask) { if (adev->class == ATA_DEV_ATA) { if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33)) @@ -411,24 +285,23 @@ static unsigned long hpt370_filter(const if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5)) mask &= ~(0x1F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** * hpt370a_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device * * Block UDMA on devices that cause trouble with this controller. */ -static unsigned long hpt370a_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask) { if (adev->class != ATA_DEV_ATA) { if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5)) mask &= ~ (0x1F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** @@ -462,8 +335,7 @@ static int hpt37x_pre_reset(struct ata_p ap->cbl = ATA_CBL_PATA80; /* Reset the state machine */ - pci_write_config_byte(pdev, 0x50, 0x37); - pci_write_config_byte(pdev, 0x54, 0x37); + pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); udelay(100); return ata_std_prereset(ap); @@ -513,8 +385,7 @@ static int hpt374_pre_reset(struct ata_p ap->cbl = ATA_CBL_PATA80; /* Reset the state machine */ - pci_write_config_byte(pdev, 0x50, 0x37); - pci_write_config_byte(pdev, 0x54, 0x37); + pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); udelay(100); return ata_std_prereset(ap); @@ -1032,6 +903,24 @@ static int hpt37x_init_one(struct pci_de .udma_mask = 0x3f, .port_ops = &hpt370a_port_ops }; + /* HPT370 - UDMA100 */ + static struct ata_port_info info_hpt370_33 = { + .sht = &hpt37x_sht, + .flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x0f, + .port_ops = &hpt370_port_ops + }; + /* HPT370A - UDMA100 */ + static struct ata_port_info info_hpt370a_33 = { + .sht = &hpt37x_sht, + .flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x0f, + .port_ops = &hpt370a_port_ops + }; /* HPT371, 372 and friends - UDMA133 */ static struct ata_port_info info_hpt372 = { .sht = &hpt37x_sht, @@ -1067,7 +956,11 @@ static int hpt37x_init_one(struct pci_de u8 irqmask; u32 class_rev; + u8 mcr1; u32 freq; + int prefer_dpll = 1; + + unsigned long iobase = pci_resource_start(dev, 4); const struct hpt_chip *chip_table; int clock_slot; @@ -1088,10 +981,12 @@ static int hpt37x_init_one(struct pci_de case 3: port = &info_hpt370; chip_table = &hpt370; + prefer_dpll = 0; break; case 4: port = &info_hpt370a; chip_table = &hpt370a; + prefer_dpll = 0; break; case 5: port = &info_hpt372; @@ -1119,8 +1014,16 @@ static int hpt37x_init_one(struct pci_de chip_table = &hpt302; break; case PCI_DEVICE_ID_TTI_HPT371: + if (class_rev > 1) + return -ENODEV; port = &info_hpt372; chip_table = &hpt371; + /* Single channel device, master is not present + but the BIOS (or us for non x86) must mark it + absent */ + pci_read_config_byte(dev, 0x50, &mcr1); + mcr1 &= ~0x04; + pci_write_config_byte(dev, 0x50, mcr1); break; case PCI_DEVICE_ID_TTI_HPT374: chip_table = &hpt374; @@ -1150,8 +1053,18 @@ static int hpt37x_init_one(struct pci_de */ pci_write_config_byte(dev, 0x5b, 0x23); + + /* + * HighPoint does this for HPT372A. + * NOTE: This register is only writeable via I/O space. + */ + if (chip_table == &hpt372a) + outb(0x0e, iobase + 0x9c); - pci_read_config_dword(dev, 0x70, &freq); + /* Some devices do not let this value be accessed via PCI space + according to the old driver */ + + freq = inl(iobase + 0x90); if ((freq >> 12) != 0xABCDE) { int i; u8 sr; @@ -1162,7 +1075,7 @@ static int hpt37x_init_one(struct pci_de /* This is the process the HPT371 BIOS is reported to use */ for(i = 0; i < 128; i++) { pci_read_config_byte(dev, 0x78, &sr); - total += sr; + total += sr & 0x1FF; udelay(15); } freq = total / 128; @@ -1173,15 +1086,27 @@ static int hpt37x_init_one(struct pci_de * Turn the frequency check into a band and then find a timing * table to match it. */ - + clock_slot = hpt37x_clock_slot(freq, chip_table->base); - if (chip_table->clocks[clock_slot] == NULL) { + if (chip_table->clocks[clock_slot] == NULL || prefer_dpll) { /* * We need to try PLL mode instead + * + * For non UDMA133 capable devices we should + * use a 50MHz DPLL by choice */ - unsigned int f_low = (MHz[clock_slot] * chip_table->base) / 192; - unsigned int f_high = f_low + 2; + unsigned int f_low, f_high; int adjust; + + clock_slot = 2; + if (port->udma_mask & 0xE0) + clock_slot = 3; + + f_low = (MHz[clock_slot] * chip_table->base) / 192; + f_high = f_low + 2; + + /* Select the DPLL clock. */ + pci_write_config_byte(dev, 0x5b, 0x21); for(adjust = 0; adjust < 8; adjust++) { if (hpt37x_calibrate_dpll(dev)) @@ -1197,25 +1122,27 @@ static int hpt37x_init_one(struct pci_de printk(KERN_WARNING "hpt37x: DPLL did not stabilize.\n"); return -ENODEV; } - /* Check if this works for all cases */ - port->private_data = (void *)hpt370_timings_66; + if (clock_slot == 3) + port->private_data = (void *)hpt37x_timings_66; + else + port->private_data = (void *)hpt37x_timings_50; printk(KERN_INFO "hpt37x: Bus clock %dMHz, using DPLL.\n", MHz[clock_slot]); } else { port->private_data = (void *)chip_table->clocks[clock_slot]; /* - * Perform a final fixup. The 371 and 372 clock determines - * if UDMA133 is available. - */ - - if (clock_slot == 2 && chip_table == &hpt372) { /* 50Mhz */ - printk(KERN_WARNING "pata_hpt37x: No UDMA133 support available with 50MHz bus clock.\n"); - if (port == &info_hpt372) - port = &info_hpt372_50; - else BUG(); - } + * Perform a final fixup. Note that we will have used the + * DPLL on the HPT372 which means we don't have to worry + * about lack of UDMA133 support on lower clocks + */ + + if (clock_slot < 2 && port == &info_hpt370) + port = &info_hpt370_33; + if (clock_slot < 2 && port == &info_hpt370a) + port = &info_hpt370a_33; printk(KERN_INFO "hpt37x: %s: Bus clock %dMHz.\n", chip_table->name, MHz[clock_slot]); } + port_info[0] = port_info[1] = port; /* Now kick off ATA set up */ return ata_pci_init_one(dev, port_info, 2); diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 65f2e18..6a34521 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -8,10 +8,10 @@ * Copyright (C) 1999-2003 Andre Hedrick * Portions Copyright (C) 2001 Sun Microsystems, Inc. * Portions Copyright (C) 2003 Red Hat Inc + * Portions Copyright (C) 2005-2006 MontaVista Software, Inc. * * * TODO - * 371N * Work out best PLL policy */ @@ -25,7 +25,7 @@ #include #include #define DRV_NAME "pata_hpt3x2n" -#define DRV_VERSION "0.3.2" +#define DRV_VERSION "0.3.3" enum { HPT_PCI_FAST = (1 << 31), @@ -115,14 +115,13 @@ static u32 hpt3x2n_find_mode(struct ata_ } /** - * hpt3x2n_pre_reset - reset the hpt3x2n bus - * @ap: ATA port to reset + * hpt3x2n_cable_detect - Detect the cable type + * @ap: ATA port to detect on * - * Perform the initial reset handling for the 3x2n series controllers. - * Reset the hardware and state machine, obtain the cable type. + * Return the cable type attached to this port */ -static int hpt3xn_pre_reset(struct ata_port *ap) +static int hpt3x2n_cable_detect(struct ata_port *ap) { u8 scr2, ata66; struct pci_dev *pdev = to_pci_dev(ap->host->dev); @@ -135,15 +134,26 @@ static int hpt3xn_pre_reset(struct ata_p pci_write_config_byte(pdev, 0x5B, scr2); if (ata66 & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA40; + return ATA_CBL_PATA40; else - ap->cbl = ATA_CBL_PATA80; + return ATA_CBL_PATA80; +} + +/** + * hpt3x2n_pre_reset - reset the hpt3x2n bus + * @ap: ATA port to reset + * @deadline: deadline jiffies for the operation + * + * Perform the initial reset handling for the 3x2n series controllers. + * Reset the hardware and state machine, + */ +static int hpt3xn_pre_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); /* Reset the state machine */ - pci_write_config_byte(pdev, 0x50, 0x37); - pci_write_config_byte(pdev, 0x54, 0x37); + pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); udelay(100); - return ata_std_prereset(ap); } @@ -364,6 +374,7 @@ static struct ata_port_operations hpt3x2 .thaw = ata_bmdma_thaw, .error_handler = hpt3x2n_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = hpt3x2n_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -422,8 +433,9 @@ static int hpt3x2n_pci_clock(struct pci_ { unsigned long freq; u32 fcnt; + unsigned long iobase = pci_resource_start(pdev, 4); - pci_read_config_dword(pdev, 0x70/*CHECKME*/, &fcnt); + fcnt = inl(iobase + 0x90); /* Not PCI readable for some chips */ if ((fcnt >> 12) != 0xABCDE) { printk(KERN_WARNING "hpt3xn: BIOS clock data not set.\n"); return 33; /* Not BIOS set */ @@ -492,6 +504,7 @@ static int hpt3x2n_init_one(struct pci_d unsigned int pci_mhz; unsigned int f_low, f_high; int adjust; + unsigned long iobase = pci_resource_start(dev, 4); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xFF; @@ -501,6 +514,11 @@ static int hpt3x2n_init_one(struct pci_d if (class_rev < 6) return -ENODEV; break; + case PCI_DEVICE_ID_TTI_HPT371: + if (class_rev < 2) + return -ENODEV; + /* 371N if rev > 1 */ + break; case PCI_DEVICE_ID_TTI_HPT372: /* 372N if rev >= 1*/ if (class_rev == 0) @@ -528,6 +546,19 @@ static int hpt3x2n_init_one(struct pci_d irqmask &= ~0x10; pci_write_config_byte(dev, 0x5a, irqmask); + /* + * HPT371 chips physically have only one channel, the secondary one, + * but the primary channel registers do exist! Go figure... + * So, we manually disable the non-existing channel here + * (if the BIOS hasn't done this already). + */ + if (dev->device == PCI_DEVICE_ID_TTI_HPT371) { + u8 mcr1; + pci_read_config_byte(dev, 0x50, &mcr1); + mcr1 &= ~0x04; + pci_write_config_byte(dev, 0x50, mcr1); + } + /* Tune the PLL. HPT recommend using 75 for SATA, 66 for UDMA133 or 50 for UDMA100. Right now we always use 66 */ @@ -546,14 +577,24 @@ static int hpt3x2n_init_one(struct pci_d break; pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low); } - if (adjust == 8) - printk(KERN_WARNING "hpt3xn: DPLL did not stabilize.\n"); + if (adjust == 8) { + printk(KERN_WARNING "hpt3x2n: DPLL did not stabilize.\n"); + return -ENODEV; + } /* Set our private data up. We only need a few flags so we use it directly */ port->private_data = NULL; - if (pci_mhz > 60) + if (pci_mhz > 60) { port->private_data = (void *)PCI66; + /* + * On HPT371N, if ATA clock is 66 MHz we must set bit 2 in + * the MISC. register to stretch the UltraDMA Tss timing. + * NOTE: This register is only writeable via I/O space. + */ + if (dev->device == PCI_DEVICE_ID_TTI_HPT371) + outb(inb(iobase + 0x9c) | 0x04, iobase + 0x9c); + } /* Now kick off ATA set up */ port_info[0] = port_info[1] = port; @@ -562,6 +603,7 @@ static int hpt3x2n_init_one(struct pci_d static const struct pci_device_id hpt3x2n[] = { { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT371), }, { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), }, { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), }, { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372N), }, diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c index 813485c..ac28ec8 100644 --- a/drivers/ata/pata_hpt3x3.c +++ b/drivers/ata/pata_hpt3x3.c @@ -25,25 +25,6 @@ #include #define DRV_NAME "pata_hpt3x3" #define DRV_VERSION "0.4.2" -static int hpt3x3_probe_init(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -/** - * hpt3x3_probe_reset - reset the hpt3x3 bus - * @ap: ATA port to reset - * - * Perform the housekeeping when doing an ATA bus reeset. We just - * need to force the cable type. - */ - -static void hpt3x3_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, hpt3x3_probe_init, ata_std_softreset, NULL, ata_std_postreset); -} - /** * hpt3x3_set_piomode - PIO setup * @ap: ATA interface @@ -139,8 +120,9 @@ static struct ata_port_operations hpt3x3 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = hpt3x3_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c index 1a61cc8..d042efd 100644 --- a/drivers/ata/pata_isapnp.c +++ b/drivers/ata/pata_isapnp.c @@ -49,13 +49,13 @@ static struct ata_port_operations isapnp .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -74,8 +74,10 @@ static struct ata_port_operations isapnp static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev_id) { - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; void __iomem *cmd_addr, *ctl_addr; + int rc; if (pnp_port_valid(idev, 0) == 0) return -ENODEV; @@ -84,34 +86,36 @@ static int isapnp_init_one(struct pnp_de if (pnp_irq_valid(idev, 0) == 0) return -ENODEV; + /* allocate host */ + host = ata_host_alloc(&idev->dev, 1); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ cmd_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 0), 8); if (!cmd_addr) return -ENOMEM; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &idev->dev; - ae.port_ops = &isapnp_port_ops; - ae.sht = &isapnp_sht; - ae.n_ports = 1; - ae.pio_mask = 1; /* ISA so PIO 0 cycles */ - ae.irq = pnp_irq(idev, 0); - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS; - ae.port[0].cmd_addr = cmd_addr; + ap = host->ports[0]; + + ap->ops = &isapnp_port_ops; + ap->pio_mask = 1; + ap->flags |= ATA_FLAG_SLAVE_POSS; + + ap->ioaddr.cmd_addr = cmd_addr; if (pnp_port_valid(idev, 1) == 0) { ctl_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 1), 1); - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ae.port_flags |= ATA_FLAG_SRST; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; } - ata_std_ports(&ae.port[0]); - if (ata_device_add(&ae) == 0) - return -ENODEV; - return 0; + ata_std_ports(&ap->ioaddr); + + /* activate */ + return ata_host_activate(host, pnp_irq(idev, 0), ata_interrupt, 0, + &isapnp_sht); } /** diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c index ea73470..011306e 100644 --- a/drivers/ata/pata_it8213.c +++ b/drivers/ata/pata_it8213.c @@ -25,8 +25,8 @@ #define DRV_VERSION "0.0.2" * it8213_pre_reset - check for 40/80 pin * @ap: Port * - * Perform cable detection for the 8213 ATA interface. This is - * different to the PIIX arrangement + * Filter out ports by the enable bits before doing the normal reset + * and probe. */ static int it8213_pre_reset(struct ata_port *ap) @@ -34,23 +34,14 @@ static int it8213_pre_reset(struct ata_p static const struct pci_bits it8213_enable_bits[] = { { 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */ }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 tmp; - if (!pci_test_config_bits(pdev, &it8213_enable_bits[ap->port_no])) return -ENOENT; - - pci_read_config_byte(pdev, 0x42, &tmp); - if (tmp & 2) /* The initial docs are incorrect */ - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } /** - * it8213_probe_reset - Probe specified port on PATA host controller + * it8213_error_handler - Probe specified port on PATA host controller * @ap: Port to probe * * LOCKING: @@ -63,9 +54,27 @@ static void it8213_error_handler(struct } /** + * it8213_cable_detect - check for 40/80 pin + * @ap: Port + * + * Perform cable detection for the 8213 ATA interface. This is + * different to the PIIX arrangement + */ + +static int it8213_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 tmp; + pci_read_config_byte(pdev, 0x42, &tmp); + if (tmp & 2) /* The initial docs are incorrect */ + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + +/** * it8213_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring - * @adev: um + * @adev: Device whose timings we are configuring * * Set PIO mode for device, in host controller PCI config space. * @@ -268,6 +277,7 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = it8213_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = it8213_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 35ecb2b..f1f8cec 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -80,7 +80,7 @@ #include #define DRV_NAME "pata_it821x" -#define DRV_VERSION "0.3.4" +#define DRV_VERSION "0.3.6" struct it821x_dev { @@ -113,31 +113,6 @@ #define MWDMA_OFF 0 static int it8212_noraid; /** - * it821x_pre_reset - probe - * @ap: ATA port - * - * Set the cable type - */ - -static int it821x_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - -/** - * it821x_error_handler - probe/reset - * @ap: ATA port - * - * Set the cable type and trigger a probe - */ - -static void it821x_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, it821x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - -/** * it821x_program - program the PIO/MWDMA registers * @ap: ATA port * @adev: Device to program @@ -520,7 +495,6 @@ static int it821x_smart_set_mode(struct /** * it821x_dev_config - Called each device identify - * @ap: ATA port * @adev: Device that has just been identified * * Perform the initial setup needed for each device that is chip @@ -531,7 +505,7 @@ static int it821x_smart_set_mode(struct * basically we need to filter commands for this chip. */ -static void it821x_dev_config(struct ata_port *ap, struct ata_device *adev) +static void it821x_dev_config(struct ata_device *adev) { unsigned char model_num[ATA_ID_PROD_LEN + 1]; @@ -667,8 +641,9 @@ static struct ata_port_operations it821x .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = it821x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -703,8 +678,9 @@ static struct ata_port_operations it821x .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = it821x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = it821x_passthru_bmdma_start, diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index c6f0e19..420c343 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -129,8 +129,8 @@ static struct ata_port_operations ixp4xx .qc_issue = ata_qc_issue_prot, .eng_timeout = ata_eng_timeout, .data_xfer = ixp4xx_mmio_data_xfer, + .cable_detect = ata_cable_40wire, - .irq_handler = ata_interrupt, .irq_clear = ixp4xx_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -173,12 +173,12 @@ #endif static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) { - int ret; unsigned int irq; struct resource *cs0, *cs1; - struct ata_probe_ent ae; - + struct ata_host *host; + struct ata_port *ap; struct ixp4xx_pata_data *data = pdev->dev.platform_data; + int rc; cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -186,6 +186,12 @@ static __devinit int ixp4xx_pata_probe(s if (!cs0 || !cs1) return -EINVAL; + /* allocate host */ + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ pdev->dev.coherent_dma_mask = DMA_32BIT_MASK; data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000); @@ -199,32 +205,22 @@ static __devinit int ixp4xx_pata_probe(s *data->cs0_cfg = data->cs0_bits; *data->cs1_cfg = data->cs1_bits; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); + ap = host->ports[0]; - ae.dev = &pdev->dev; - ae.port_ops = &ixp4xx_port_ops; - ae.sht = &ixp4xx_sht; - ae.n_ports = 1; - ae.pio_mask = 0x1f; /* PIO4 */ - ae.irq = irq; - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY - | ATA_FLAG_NO_ATAPI | ATA_FLAG_SRST; + ap->ops = &ixp4xx_port_ops; + ap->pio_mask = 0x1f; /* PIO4 */ + ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI; /* run in polling mode if no irq has been assigned */ if (!irq) - ae.port_flags |= ATA_FLAG_PIO_POLLING; + ap->flags |= ATA_FLAG_PIO_POLLING; - ixp4xx_setup_port(&ae.port[0], data); + ixp4xx_setup_port(&ap->ioaddr, data); dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); - ret = ata_device_add(&ae); - if (ret == 0) - return -ENODEV; - - return 0; + /* activate host */ + return ata_host_activate(host, irq, ata_interrupt, 0, &ixp4xx_sht); } static __devexit int ixp4xx_pata_remove(struct platform_device *dev) diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index 86fbcd6..7070992 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -162,6 +162,7 @@ static struct ata_port_operations simple .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -185,6 +186,7 @@ static struct ata_port_operations legacy .check_status = ata_check_status, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .cable_detect = ata_cable_40wire, .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, @@ -305,6 +307,7 @@ static struct ata_port_operations pdc202 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -360,6 +363,7 @@ static struct ata_port_operations ht6560 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -426,6 +430,7 @@ static struct ata_port_operations ht6560 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -547,6 +552,7 @@ static struct ata_port_operations opti82 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -680,6 +686,7 @@ static struct ata_port_operations opti82 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = opti82c46x_qc_issue_prot, @@ -709,7 +716,8 @@ static struct ata_port_operations opti82 static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl, int irq) { struct legacy_data *ld = &legacy_data[nr_legacy_host]; - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; struct platform_device *pdev; struct ata_port_operations *ops = &legacy_port_ops; void __iomem *io_addr, *ctrl_addr; @@ -791,24 +799,23 @@ static __init int legacy_init_one(int po if (ops == &legacy_port_ops && (autospeed & mask)) ops = &simple_port_ops; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - ae.port_ops = ops; - ae.sht = &legacy_sht; - ae.n_ports = 1; - ae.pio_mask = pio_modes; - ae.irq = irq; - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST|iordy; - ae.port[0].cmd_addr = io_addr; - ae.port[0].altstatus_addr = ctrl_addr; - ae.port[0].ctl_addr = ctrl_addr; - ata_std_ports(&ae.port[0]); - ae.private_data = ld; - - ret = -ENODEV; - if (!ata_device_add(&ae)) + ret = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto fail; + ap = host->ports[0]; + + ap->ops = ops; + ap->pio_mask = pio_modes; + ap->flags |= ATA_FLAG_SLAVE_POSS | iordy; + ap->ioaddr.cmd_addr = io_addr; + ap->ioaddr.altstatus_addr = ctrl_addr; + ap->ioaddr.ctl_addr = ctrl_addr; + ata_std_ports(&ap->ioaddr); + ap->private_data = ld; + + ret = ata_host_activate(host, irq, ata_interrupt, 0, &legacy_sht); + if (ret) goto fail; legacy_host[nr_legacy_host++] = dev_get_drvdata(&pdev->dev); diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c index 6dd7c4e..d9b94a1 100644 --- a/drivers/ata/pata_marvell.c +++ b/drivers/ata/pata_marvell.c @@ -20,7 +20,7 @@ #include #include #define DRV_NAME "pata_marvell" -#define DRV_VERSION "0.1.1" +#define DRV_VERSION "0.1.4" /** * marvell_pre_reset - check for 40/80 pin @@ -52,22 +52,23 @@ static int marvell_pre_reset(struct ata_ if ((pdev->device == 0x6145) && (ap->port_no == 0) && (!(devices & 0x10))) /* PATA enable ? */ return -ENOENT; + return ata_std_prereset(ap); +} +static int marvell_cable_detect(struct ata_port *ap) +{ /* Cable type */ switch(ap->port_no) { case 0: if (ioread8(ap->ioaddr.bmdma_addr + 1) & 1) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - break; - + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; case 1: /* Legacy SATA port */ - ap->cbl = ATA_CBL_SATA; - break; + return ATA_CBL_SATA; } - return ata_std_prereset(ap); + BUG(); + return 0; /* Our BUG macro needs the right markup */ } /** @@ -123,6 +124,7 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = marvell_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = marvell_cable_detect, /* BMDMA handling is PCI ATA format, use helpers */ .bmdma_setup = ata_bmdma_setup, diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 882c36e..9587a89 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -24,7 +24,7 @@ #include #define DRV_NAME "mpc52xx_ata" -#define DRV_VERSION "0.1.0" +#define DRV_VERSION "0.1.0ac2" /* Private structures used by the driver */ @@ -297,38 +297,37 @@ static struct ata_port_operations mpc52x .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, .error_handler = mpc52xx_ata_error_handler, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, .port_start = ata_port_start, }; -static struct ata_probe_ent mpc52xx_ata_probe_ent = { - .port_ops = &mpc52xx_ata_port_ops, - .sht = &mpc52xx_ata_sht, - .n_ports = 1, - .pio_mask = 0x1f, /* Up to PIO4 */ - .mwdma_mask = 0x00, /* No MWDMA */ - .udma_mask = 0x00, /* No UDMA */ - .port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, - .irq_flags = 0, -}; - static int __devinit mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv) { - struct ata_probe_ent *ae = &mpc52xx_ata_probe_ent; - struct ata_ioports *aio = &ae->port[0]; - int rv; - - INIT_LIST_HEAD(&ae->node); - ae->dev = dev; - ae->irq = priv->ata_irq; - + struct ata_host *host; + struct ata_port *ap; + struct ata_ioports *aio; + int rc; + + host = ata_host_alloc(dev, 1); + if (!host) + return -ENOMEM; + + ap = host->ports[0]; + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->pio_mask = 0x1f; /* Up to PIO4 */ + ap->mwdma_mask = 0x00; /* No MWDMA */ + ap->udma_mask = 0x00; /* No UDMA */ + ap->ops = &mpc52xx_ata_port_ops; + host->private_data = priv; + + aio = &ap->ioaddr; aio->cmd_addr = NULL; /* Don't have a classic reg block */ aio->altstatus_addr = &priv->ata_regs->tf_control; aio->ctl_addr = &priv->ata_regs->tf_control; @@ -343,11 +342,9 @@ mpc52xx_ata_init_one(struct device *dev, aio->status_addr = &priv->ata_regs->tf_command; aio->command_addr = &priv->ata_regs->tf_command; - ae->private_data = priv; - - rv = ata_device_add(ae); - - return rv ? 0 : -EINVAL; + /* activate host */ + return ata_host_activate(host, priv->ata_irq, ata_interrupt, 0, + &mpc52xx_ata_sht); } static struct mpc52xx_ata_priv * diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c index 4abe45a..987c5fa 100644 --- a/drivers/ata/pata_mpiix.c +++ b/drivers/ata/pata_mpiix.c @@ -35,7 +35,7 @@ #include #include #define DRV_NAME "pata_mpiix" -#define DRV_VERSION "0.7.5" +#define DRV_VERSION "0.7.6" enum { IDETIM = 0x6C, /* IDE control register */ @@ -53,7 +53,6 @@ static int mpiix_pre_reset(struct ata_po if (!pci_test_config_bits(pdev, &mpiix_enable_bits)) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -185,12 +184,12 @@ static struct ata_port_operations mpiix_ .thaw = ata_bmdma_thaw, .error_handler = mpiix_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = mpiix_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -201,8 +200,9 @@ static struct ata_port_operations mpiix_ static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id) { /* Single threaded by the PCI probe logic */ - static struct ata_probe_ent probe; static int printed_version; + struct ata_host *host; + struct ata_port *ap; void __iomem *cmd_addr, *ctl_addr; u16 idetim; int irq; @@ -210,6 +210,10 @@ static int mpiix_init_one(struct pci_dev if (!printed_version++) dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n"); + host = ata_host_alloc(&dev->dev, 1); + if (!host) + return -ENOMEM; + /* MPIIX has many functions which can be turned on or off according to other devices present. Make sure IDE is enabled before we try and use it */ @@ -238,27 +242,21 @@ static int mpiix_init_one(struct pci_dev without BARs set fools the setup. #2 If you pci_disable_device the MPIIX your box goes castors up */ - INIT_LIST_HEAD(&probe.node); - probe.dev = pci_dev_to_dev(dev); - probe.port_ops = &mpiix_port_ops; - probe.sht = &mpiix_sht; - probe.pio_mask = 0x1F; - probe.irq_flags = IRQF_SHARED; - probe.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; - probe.n_ports = 1; + ap = host->ports[0]; + ap->ops = &mpiix_port_ops; + ap->pio_mask = 0x1F; + ap->flags |= ATA_FLAG_SLAVE_POSS; - probe.irq = irq; - probe.port[0].cmd_addr = cmd_addr; - probe.port[0].ctl_addr = ctl_addr; - probe.port[0].altstatus_addr = ctl_addr; + ap->ioaddr.cmd_addr = cmd_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ap->ioaddr.altstatus_addr = ctl_addr; /* Let libata fill in the port details */ - ata_std_ports(&probe.port[0]); + ata_std_ports(&ap->ioaddr); - /* Now add the port that is active */ - if (ata_device_add(&probe)) - return 0; - return -ENODEV; + /* activate host */ + return ata_host_activate(host, irq, ata_interrupt, IRQF_SHARED, + &mpiix_sht); } static const struct pci_device_id mpiix[] = { diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c index 38f99b3..dbba5b7 100644 --- a/drivers/ata/pata_netcell.c +++ b/drivers/ata/pata_netcell.c @@ -16,33 +16,7 @@ #include #include #define DRV_NAME "pata_netcell" -#define DRV_VERSION "0.1.6" - -/** - * netcell_probe_init - check for 40/80 pin - * @ap: Port - * - * Cables are handled by the RAID controller. Report 80 pin. - */ - -static int netcell_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - -/** - * netcell_probe_reset - Probe specified port on PATA host controller - * @ap: Port to probe - * - * LOCKING: - * None (inherited from caller). - */ - -static void netcell_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, netcell_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} +#define DRV_VERSION "0.1.7" /* No PIO or DMA methods needed for this device */ @@ -81,8 +55,9 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = netcell_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_80wire, /* BMDMA handling is PCI ATA format, use helpers */ .bmdma_setup = ata_bmdma_setup, diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c index 9944a28..078aeda 100644 --- a/drivers/ata/pata_ns87410.c +++ b/drivers/ata/pata_ns87410.c @@ -28,13 +28,13 @@ #include #include #define DRV_NAME "pata_ns87410" -#define DRV_VERSION "0.4.3" +#define DRV_VERSION "0.4.6" /** * ns87410_pre_reset - probe begin * @ap: ATA port * - * Set up cable type and use generic probe init + * Check enabled ports */ static int ns87410_pre_reset(struct ata_port *ap) @@ -47,7 +47,6 @@ static int ns87410_pre_reset(struct ata_ if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -177,6 +176,7 @@ static struct ata_port_operations ns8741 .thaw = ata_bmdma_thaw, .error_handler = ns87410_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ns87410_qc_issue_prot, diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index da68cd1..dea4690 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -25,7 +25,7 @@ #include #include #define DRV_NAME "pata_oldpiix" -#define DRV_VERSION "0.5.4" +#define DRV_VERSION "0.5.5" /** * oldpiix_pre_reset - probe begin @@ -44,7 +44,6 @@ static int oldpiix_pre_reset(struct ata_ if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -65,7 +64,7 @@ static void oldpiix_pata_error_handler(s /** * oldpiix_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring - * @adev: um + * @adev: Device whose timings we are configuring * * Set PIO mode for device, in host controller PCI config space. * @@ -255,6 +254,7 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = oldpiix_pata_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c index 3fd3a35..13b63e2 100644 --- a/drivers/ata/pata_opti.c +++ b/drivers/ata/pata_opti.c @@ -34,7 +34,7 @@ #include #include #define DRV_NAME "pata_opti" -#define DRV_VERSION "0.2.8" +#define DRV_VERSION "0.2.9" enum { READ_REG = 0, /* index of Read cycle timing register */ @@ -61,8 +61,6 @@ static int opti_pre_reset(struct ata_por if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no])) return -ENOENT; - - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -198,6 +196,7 @@ static struct ata_port_operations opti_p .thaw = ata_bmdma_thaw, .error_handler = opti_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c index 9764907..b70e04c 100644 --- a/drivers/ata/pata_optidma.c +++ b/drivers/ata/pata_optidma.c @@ -33,7 +33,7 @@ #include #include #define DRV_NAME "pata_optidma" -#define DRV_VERSION "0.2.4" +#define DRV_VERSION "0.3.2" enum { READ_REG = 0, /* index of Read cycle timing register */ @@ -62,7 +62,6 @@ static int optidma_pre_reset(struct ata_ if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits)) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -115,7 +114,7 @@ static void optidma_lock(struct ata_port } /** - * optidma_set_mode - set mode data + * optidma_mode_setup - set mode data * @ap: ATA interface * @adev: ATA device * @mode: Mode to set @@ -128,7 +127,7 @@ static void optidma_lock(struct ata_port * IRQ here we depend on the host set locking to avoid catastrophe. */ -static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode) +static void optidma_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode) { struct ata_device *pair = ata_dev_pair(adev); int pio = adev->pio_mode - XFER_PIO_0; @@ -202,7 +201,7 @@ static void optidma_set_mode(struct ata_ } /** - * optiplus_set_mode - DMA setup for Firestar Plus + * optiplus_mode_setup - DMA setup for Firestar Plus * @ap: ATA port * @adev: device * @mode: desired mode @@ -213,7 +212,7 @@ static void optidma_set_mode(struct ata_ * one */ -static void optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode) +static void optiplus_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 udcfg; @@ -225,7 +224,7 @@ static void optiplus_set_mode(struct ata pci_read_config_byte(pdev, 0x44, &udcfg); if (mode <= XFER_UDMA_0) { udcfg &= ~(1 << unit); - optidma_set_mode(ap, adev, adev->dma_mode); + optidma_mode_setup(ap, adev, adev->dma_mode); } else { udcfg |= (1 << unit); if (ap->port_no) { @@ -253,7 +252,7 @@ static void optiplus_set_mode(struct ata static void optidma_set_pio_mode(struct ata_port *ap, struct ata_device *adev) { - optidma_set_mode(ap, adev, adev->pio_mode); + optidma_mode_setup(ap, adev, adev->pio_mode); } /** @@ -268,7 +267,7 @@ static void optidma_set_pio_mode(struct static void optidma_set_dma_mode(struct ata_port *ap, struct ata_device *adev) { - optidma_set_mode(ap, adev, adev->dma_mode); + optidma_mode_setup(ap, adev, adev->dma_mode); } /** @@ -283,7 +282,7 @@ static void optidma_set_dma_mode(struct static void optiplus_set_pio_mode(struct ata_port *ap, struct ata_device *adev) { - optiplus_set_mode(ap, adev, adev->pio_mode); + optiplus_mode_setup(ap, adev, adev->pio_mode); } /** @@ -298,7 +297,7 @@ static void optiplus_set_pio_mode(struct static void optiplus_set_dma_mode(struct ata_port *ap, struct ata_device *adev) { - optiplus_set_mode(ap, adev, adev->dma_mode); + optiplus_mode_setup(ap, adev, adev->dma_mode); } /** @@ -322,26 +321,29 @@ static u8 optidma_make_bits43(struct ata } /** - * optidma_post_set_mode - finalize PCI setup + * optidma_set_mode - mode setup * @ap: port to set up * - * Finalise the configuration by writing the nibble of extra bits - * of data into the chip. + * Use the standard setup to tune the chipset and then finalise the + * configuration by writing the nibble of extra bits of data into + * the chip. */ -static void optidma_post_set_mode(struct ata_port *ap) +static int optidma_set_mode(struct ata_port *ap, struct ata_device **r_failed) { u8 r; int nybble = 4 * ap->port_no; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - - pci_read_config_byte(pdev, 0x43, &r); - - r &= (0x0F << nybble); - r |= (optidma_make_bits43(&ap->device[0]) + - (optidma_make_bits43(&ap->device[0]) << 2)) << nybble; - - pci_write_config_byte(pdev, 0x43, r); + int rc = ata_do_set_mode(ap, r_failed); + if (rc == 0) { + pci_read_config_byte(pdev, 0x43, &r); + + r &= (0x0F << nybble); + r |= (optidma_make_bits43(&ap->device[0]) + + (optidma_make_bits43(&ap->device[0]) << 2)) << nybble; + pci_write_config_byte(pdev, 0x43, r); + } + return rc; } static struct scsi_host_template optidma_sht = { @@ -381,7 +383,8 @@ static struct ata_port_operations optidm .thaw = ata_bmdma_thaw, .post_internal_cmd = ata_bmdma_post_internal_cmd, .error_handler = optidma_error_handler, - .post_set_mode = optidma_post_set_mode, + .set_mode = optidma_set_mode, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -416,7 +419,8 @@ static struct ata_port_operations optipl .thaw = ata_bmdma_thaw, .post_internal_cmd = ata_bmdma_post_internal_cmd, .error_handler = optidma_error_handler, - .post_set_mode = optidma_post_set_mode, + .set_mode = optidma_set_mode, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 103720f..75dc847 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -42,7 +42,7 @@ #include #define DRV_NAME "pata_pcmcia" -#define DRV_VERSION "0.3.0" +#define DRV_VERSION "0.3.1" /* * Private data structure to glue stuff together @@ -54,6 +54,39 @@ struct ata_pcmcia_info { dev_node_t node; }; +/** + * pcmcia_set_mode - PCMCIA specific mode setup + * @ap: Port + * @r_failed_dev: Return pointer for failed device + * + * Perform the tuning and setup of the devices and timings, which + * for PCMCIA is the same as any other controller. We wrap it however + * as we need to spot hardware with incorrect or missing master/slave + * decode, which alas is embarrassingly common in the PC world + */ + +static int pcmcia_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + struct ata_device *master = &ap->device[0]; + struct ata_device *slave = &ap->device[1]; + + if (!ata_dev_enabled(master) || !ata_dev_enabled(slave)) + return ata_do_set_mode(ap, r_failed_dev); + + if (memcmp(master->id + ATA_ID_FW_REV, slave->id + ATA_ID_FW_REV, + ATA_ID_FW_REV_LEN + ATA_ID_PROD_LEN) == 0) + { + /* Suspicious match, but could be two cards from + the same vendor - check serial */ + if (memcmp(master->id + ATA_ID_SERNO, slave->id + ATA_ID_SERNO, + ATA_ID_SERNO_LEN) == 0 && master->id[ATA_ID_SERNO] >> 8) { + ata_dev_printk(slave, KERN_WARNING, "is a ghost device, ignoring.\n"); + ata_dev_disable(slave); + } + } + return ata_do_set_mode(ap, r_failed_dev); +} + static struct scsi_host_template pcmcia_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -73,6 +106,7 @@ static struct scsi_host_template pcmcia_ }; static struct ata_port_operations pcmcia_port_ops = { + .set_mode = pcmcia_set_mode, .port_disable = ata_port_disable, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -84,13 +118,13 @@ static struct ata_port_operations pcmcia .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer_noirq, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -111,7 +145,8 @@ do { last_fn = (fn); if ((last_ret = (re static int pcmcia_init_one(struct pcmcia_device *pdev) { - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; struct ata_pcmcia_info *info; tuple_t tuple; struct { @@ -255,24 +290,24 @@ next_entry: * Having done the PCMCIA plumbing the ATA side is relatively * sane. */ - - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - ae.port_ops = &pcmcia_port_ops; - ae.sht = &pcmcia_sht; - ae.n_ports = 1; - ae.pio_mask = 1; /* ISA so PIO 0 cycles */ - ae.irq = pdev->irq.AssignedIRQ; - ae.irq_flags = IRQF_SHARED; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; - ae.port[0].cmd_addr = io_addr; - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ata_std_ports(&ae.port[0]); - - ret = -ENODEV; - if (ata_device_add(&ae) == 0) + ret = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto failed; + ap = host->ports[0]; + + ap->ops = &pcmcia_port_ops; + ap->pio_mask = 1; /* ISA so PIO 0 cycles */ + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->ioaddr.cmd_addr = io_addr; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ata_std_ports(&ap->ioaddr); + + /* activate */ + ret = ata_host_activate(host, pdev->irq.AssignedIRQ, ata_interrupt, + IRQF_SHARED, &pcmcia_sht); + if (ret) goto failed; info->ndev = 1; diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index 93bcdad..a61cbc1 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -35,7 +35,7 @@ #include #include #define DRV_NAME "pata_pdc2027x" -#define DRV_VERSION "0.8" +#define DRV_VERSION "0.9" #undef PDC_DEBUG #ifdef PDC_DEBUG @@ -66,8 +66,10 @@ static int pdc2027x_init_one(struct pci_ static void pdc2027x_error_handler(struct ata_port *ap); static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev); static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev); -static void pdc2027x_post_set_mode(struct ata_port *ap); static int pdc2027x_check_atapi_dma(struct ata_queued_cmd *qc); +static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask); +static int pdc2027x_cable_detect(struct ata_port *ap); +static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed); /* * ATA Timing Tables based on 133MHz controller clock. @@ -146,6 +148,7 @@ static struct scsi_host_template pdc2027 static struct ata_port_operations pdc2027x_pata100_ops = { .port_disable = ata_port_disable, + .mode_filter = ata_pci_default_filter, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -166,8 +169,8 @@ static struct ata_port_operations pdc202 .thaw = ata_bmdma_thaw, .error_handler = pdc2027x_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = pdc2027x_cable_detect, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -179,7 +182,8 @@ static struct ata_port_operations pdc202 .port_disable = ata_port_disable, .set_piomode = pdc2027x_set_piomode, .set_dmamode = pdc2027x_set_dmamode, - .post_set_mode = pdc2027x_post_set_mode, + .set_mode = pdc2027x_set_mode, + .mode_filter = pdc2027x_mode_filter, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -200,8 +204,8 @@ static struct ata_port_operations pdc202 .thaw = ata_bmdma_thaw, .error_handler = pdc2027x_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = pdc2027x_cable_detect, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -212,7 +216,6 @@ static struct ata_port_operations pdc202 static struct ata_port_info pdc2027x_port_info[] = { /* PDC_UDMA_100 */ { - .sht = &pdc2027x_sht, .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO, .pio_mask = 0x1f, /* pio0-4 */ @@ -222,7 +225,6 @@ static struct ata_port_info pdc2027x_por }, /* PDC_UDMA_133 */ { - .sht = &pdc2027x_sht, .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO, .pio_mask = 0x1f, /* pio0-4 */ @@ -261,7 +263,7 @@ static inline void __iomem *dev_mmio(str } /** - * pdc2027x_pata_cbl_detect - Probe host controller cable detect info + * pdc2027x_pata_cable_detect - Probe host controller cable detect info * @ap: Port for which cable detect info is desired * * Read 80c cable indicator from Promise extended register. @@ -270,7 +272,7 @@ static inline void __iomem *dev_mmio(str * LOCKING: * None (inherited from caller). */ -static void pdc2027x_cbl_detect(struct ata_port *ap) +static int pdc2027x_cable_detect(struct ata_port *ap) { u32 cgcr; @@ -281,13 +283,10 @@ static void pdc2027x_cbl_detect(struct a PDPRINTK("No cable or 80-conductor cable on port %d\n", ap->port_no); - ap->cbl = ATA_CBL_PATA80; - return; - + return ATA_CBL_PATA80; cbl40: printk(KERN_INFO DRV_NAME ": 40-conductor cable detected on port %d\n", ap->port_no); - ap->cbl = ATA_CBL_PATA40; - ap->udma_mask &= ATA_UDMA_MASK_40C; + return ATA_CBL_PATA40; } /** @@ -314,7 +313,6 @@ static int pdc2027x_prereset(struct ata_ /* Check whether port enabled */ if (!pdc2027x_port_enabled(ap)) return -ENOENT; - pdc2027x_cbl_detect(ap); return ata_std_prereset(ap); } @@ -334,6 +332,32 @@ static void pdc2027x_error_handler(struc } /** + * pdc2720x_mode_filter - mode selection filter + * @adev: ATA device + * @mask: list of modes proposed + * + * Block UDMA on devices that cause trouble with this controller. + */ + +static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask) +{ + unsigned char model_num[ATA_ID_PROD_LEN + 1]; + struct ata_device *pair = ata_dev_pair(adev); + + if (adev->class != ATA_DEV_ATA || adev->devno == 0 || pair == NULL) + return ata_pci_default_filter(adev, mask); + + /* Check for slave of a Maxtor at UDMA6 */ + ata_id_c_string(pair->id, model_num, ATA_ID_PROD, + ATA_ID_PROD_LEN + 1); + /* If the master is a maxtor in UDMA6 then the slave should not use UDMA 6 */ + if(strstr(model_num, "Maxtor") == 0 && pair->dma_mode == XFER_UDMA_6) + mask &= ~ (1 << (6 + ATA_SHIFT_UDMA)); + + return ata_pci_default_filter(adev, mask); +} + +/** * pdc2027x_set_piomode - Initialize host controller PATA PIO timings * @ap: Port to configure * @adev: um @@ -444,17 +468,22 @@ static void pdc2027x_set_dmamode(struct } /** - * pdc2027x_post_set_mode - Set the timing registers back to correct values. + * pdc2027x_set_mode - Set the timing registers back to correct values. * @ap: Port to configure + * @r_failed: Returned device for failure * * The pdc2027x hardware will look at "SET FEATURES" and change the timing registers * automatically. The values set by the hardware might be incorrect, under 133Mhz PLL. * This function overwrites the possibly incorrect values set by the hardware to be correct. */ -static void pdc2027x_post_set_mode(struct ata_port *ap) +static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed) { int i; + i = ata_do_set_mode(ap, r_failed); + if (i < 0) + return i; + for (i = 0; i < ATA_MAX_DEVICES; i++) { struct ata_device *dev = &ap->device[i]; @@ -476,6 +505,7 @@ static void pdc2027x_post_set_mode(struc } } } + return 0; } /** @@ -521,12 +551,12 @@ static int pdc2027x_check_atapi_dma(stru /** * pdc_read_counter - Read the ctr counter - * @probe_ent: for the port address + * @host: target ATA host */ -static long pdc_read_counter(struct ata_probe_ent *probe_ent) +static long pdc_read_counter(struct ata_host *host) { - void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR]; long counter; int retry = 1; u32 bccrl, bccrh, bccrlv, bccrhv; @@ -564,12 +594,12 @@ retry: * adjust_pll - Adjust the PLL input clock in Hz. * * @pdc_controller: controller specific information - * @probe_ent: For the port address + * @host: target ATA host * @pll_clock: The input of PLL in HZ */ -static void pdc_adjust_pll(struct ata_probe_ent *probe_ent, long pll_clock, unsigned int board_idx) +static void pdc_adjust_pll(struct ata_host *host, long pll_clock, unsigned int board_idx) { - void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR]; u16 pll_ctl; long pll_clock_khz = pll_clock / 1000; long pout_required = board_idx? PDC_133_MHZ:PDC_100_MHZ; @@ -649,19 +679,19 @@ #endif /** * detect_pll_input_clock - Detect the PLL input clock in Hz. - * @probe_ent: for the port address + * @host: target ATA host * Ex. 16949000 on 33MHz PCI bus for pdc20275. * Half of the PCI clock. */ -static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent) +static long pdc_detect_pll_input_clock(struct ata_host *host) { - void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR]; u32 scr; long start_count, end_count; long pll_clock; /* Read current counter value */ - start_count = pdc_read_counter(probe_ent); + start_count = pdc_read_counter(host); /* Start the test mode */ scr = readl(mmio_base + PDC_SYS_CTL); @@ -673,7 +703,7 @@ static long pdc_detect_pll_input_clock(s mdelay(100); /* Read the counter values again */ - end_count = pdc_read_counter(probe_ent); + end_count = pdc_read_counter(host); /* Stop the test mode */ scr = readl(mmio_base + PDC_SYS_CTL); @@ -692,11 +722,10 @@ static long pdc_detect_pll_input_clock(s /** * pdc_hardware_init - Initialize the hardware. - * @pdev: instance of pci_dev found - * @pdc_controller: controller specific information - * @pe: for the port address + * @host: target ATA host + * @board_idx: board identifier */ -static int pdc_hardware_init(struct pci_dev *pdev, struct ata_probe_ent *pe, unsigned int board_idx) +static int pdc_hardware_init(struct ata_host *host, unsigned int board_idx) { long pll_clock; @@ -706,15 +735,15 @@ static int pdc_hardware_init(struct pci_ * Ex. 25MHz or 40MHz, we have to adjust the cycle_time. * The pdc20275 controller employs PLL circuit to help correct timing registers setting. */ - pll_clock = pdc_detect_pll_input_clock(pe); + pll_clock = pdc_detect_pll_input_clock(host); if (pll_clock < 0) /* counter overflow? Try again. */ - pll_clock = pdc_detect_pll_input_clock(pe); + pll_clock = pdc_detect_pll_input_clock(host); - dev_printk(KERN_INFO, &pdev->dev, "PLL input clock %ld kHz\n", pll_clock/1000); + dev_printk(KERN_INFO, host->dev, "PLL input clock %ld kHz\n", pll_clock/1000); /* Adjust PLL control register */ - pdc_adjust_pll(pe, pll_clock, board_idx); + pdc_adjust_pll(host, pll_clock, board_idx); return 0; } @@ -746,8 +775,7 @@ static void pdc_ata_setup_port(struct at * Called when an instance of PCI adapter is inserted. * This function checks whether the hardware is supported, * initialize hardware and register an instance of ata_host to - * libata by providing struct ata_probe_ent and ata_device_add(). - * (implements struct pci_driver.probe() ) + * libata. (implements struct pci_driver.probe() ) * * @pdev: instance of pci_dev found * @ent: matching entry in the id_tbl[] @@ -756,14 +784,21 @@ static int __devinit pdc2027x_init_one(s { static int printed_version; unsigned int board_idx = (unsigned int) ent->driver_data; - - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = + { &pdc2027x_port_info[board_idx], NULL }; + struct ata_host *host; void __iomem *mmio_base; int rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -771,6 +806,7 @@ static int __devinit pdc2027x_init_one(s rc = pcim_iomap_regions(pdev, 1 << PDC_MMIO_BAR, DRV_NAME); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) @@ -780,46 +816,22 @@ static int __devinit pdc2027x_init_one(s if (rc) return rc; - /* Prepare the probe entry */ - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = pdc2027x_port_info[board_idx].sht; - probe_ent->port_flags = pdc2027x_port_info[board_idx].flags; - probe_ent->pio_mask = pdc2027x_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc2027x_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc2027x_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc2027x_port_info[board_idx].port_ops; + mmio_base = host->iomap[PDC_MMIO_BAR]; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); + pdc_ata_setup_port(&host->ports[0]->ioaddr, mmio_base + 0x17c0); + host->ports[0]->ioaddr.bmdma_addr = mmio_base + 0x1000; + pdc_ata_setup_port(&host->ports[1]->ioaddr, mmio_base + 0x15c0); + host->ports[1]->ioaddr.bmdma_addr = mmio_base + 0x1008; - mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; - - pdc_ata_setup_port(&probe_ent->port[0], mmio_base + 0x17c0); - probe_ent->port[0].bmdma_addr = mmio_base + 0x1000; - pdc_ata_setup_port(&probe_ent->port[1], mmio_base + 0x15c0); - probe_ent->port[1].bmdma_addr = mmio_base + 0x1008; - - probe_ent->n_ports = 2; - - pci_set_master(pdev); //pci_enable_intx(pdev); /* initialize adapter */ - if (pdc_hardware_init(pdev, probe_ent, board_idx) != 0) + if (pdc_hardware_init(host, board_idx) != 0) return -EIO; - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &pdc2027x_sht); } /** diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c index 0a14933..ee636be 100644 --- a/drivers/ata/pata_pdc202xx_old.c +++ b/drivers/ata/pata_pdc202xx_old.c @@ -22,45 +22,17 @@ #include #include #define DRV_NAME "pata_pdc202xx_old" -#define DRV_VERSION "0.4.0" +#define DRV_VERSION "0.4.2" -/** - * pdc2024x_pre_reset - probe begin - * @ap: ATA port - * - * Set up cable type and use generic probe init - */ - -static int pdc2024x_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - - -static void pdc2024x_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, pdc2024x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - - -static int pdc2026x_pre_reset(struct ata_port *ap) +static int pdc2026x_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u16 cis; pci_read_config_word(pdev, 0x50, &cis); if (cis & (1 << (10 + ap->port_no))) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; - - return ata_std_prereset(ap); -} - -static void pdc2026x_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, pdc2026x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; } /** @@ -244,7 +216,6 @@ static void pdc2026x_bmdma_stop(struct a /** * pdc2026x_dev_config - device setup hook - * @ap: ATA port * @adev: newly found device * * Perform chip specific early setup. We need to lock the transfer @@ -252,7 +223,7 @@ static void pdc2026x_bmdma_stop(struct a * barf. */ -static void pdc2026x_dev_config(struct ata_port *ap, struct ata_device *adev) +static void pdc2026x_dev_config(struct ata_device *adev) { adev->max_sectors = 256; } @@ -292,8 +263,9 @@ static struct ata_port_operations pdc202 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = pdc2024x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -326,8 +298,9 @@ static struct ata_port_operations pdc202 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = pdc2026x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = pdc2026x_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = pdc2026x_bmdma_start, diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 4b82a54..a0a650c 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -80,13 +80,13 @@ static struct ata_port_operations pata_p .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer_noirq, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -135,7 +135,8 @@ static void pata_platform_setup_port(str static int __devinit pata_platform_probe(struct platform_device *pdev) { struct resource *io_res, *ctl_res; - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; unsigned int mmio; /* @@ -175,44 +176,41 @@ static int __devinit pata_platform_probe /* * Now that that's out of the way, wire up the port.. */ - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - ae.port_ops = &pata_platform_port_ops; - ae.sht = &pata_platform_sht; - ae.n_ports = 1; - ae.pio_mask = pio_mask; - ae.irq = platform_get_irq(pdev, 0); - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + return -ENOMEM; + ap = host->ports[0]; + + ap->ops = &pata_platform_port_ops; + ap->pio_mask = pio_mask; + ap->flags |= ATA_FLAG_SLAVE_POSS; /* * Handle the MMIO case */ if (mmio) { - ae.port[0].cmd_addr = devm_ioremap(&pdev->dev, io_res->start, + ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, io_res->start, io_res->end - io_res->start + 1); - ae.port[0].ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start, + ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start, ctl_res->end - ctl_res->start + 1); } else { - ae.port[0].cmd_addr = devm_ioport_map(&pdev->dev, io_res->start, + ap->ioaddr.cmd_addr = devm_ioport_map(&pdev->dev, io_res->start, io_res->end - io_res->start + 1); - ae.port[0].ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start, + ap->ioaddr.ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start, ctl_res->end - ctl_res->start + 1); } - if (!ae.port[0].cmd_addr || !ae.port[0].ctl_addr) { + if (!ap->ioaddr.cmd_addr || !ap->ioaddr.ctl_addr) { dev_err(&pdev->dev, "failed to map IO/CTL base\n"); return -ENOMEM; } - ae.port[0].altstatus_addr = ae.port[0].ctl_addr; + ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; - pata_platform_setup_port(&ae.port[0], pdev->dev.platform_data); + pata_platform_setup_port(&ap->ioaddr, pdev->dev.platform_data); - if (unlikely(ata_device_add(&ae) == 0)) - return -ENODEV; - - return 0; + /* activate */ + return ata_host_activate(host, platform_get_irq(pdev, 0), ata_interrupt, + 0, &pata_platform_sht); } /** diff --git a/drivers/ata/pata_qdi.c b/drivers/ata/pata_qdi.c index c381001..27685ce 100644 --- a/drivers/ata/pata_qdi.c +++ b/drivers/ata/pata_qdi.c @@ -183,13 +183,13 @@ static struct ata_port_operations qdi650 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = qdi_qc_issue_prot, .data_xfer = qdi_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -211,13 +211,13 @@ static struct ata_port_operations qdi658 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = qdi_qc_issue_prot, .data_xfer = qdi_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -238,8 +238,9 @@ static struct ata_port_operations qdi658 static __init int qdi_init_one(unsigned long port, int type, unsigned long io, int irq, int fast) { - struct ata_probe_ent ae; struct platform_device *pdev; + struct ata_host *host; + struct ata_port *ap; void __iomem *io_addr, *ctl_addr; int ret; @@ -257,34 +258,31 @@ static __init int qdi_init_one(unsigned if (!io_addr || !ctl_addr) goto fail; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; + ret = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto fail; + ap = host->ports[0]; if (type == 6580) { - ae.port_ops = &qdi6580_port_ops; - ae.pio_mask = 0x1F; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; + ap->ops = &qdi6580_port_ops; + ap->pio_mask = 0x1F; + ap->flags |= ATA_FLAG_SLAVE_POSS; } else { - ae.port_ops = &qdi6500_port_ops; - ae.pio_mask = 0x07; /* Actually PIO3 !IORDY is possible */ - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | - ATA_FLAG_NO_IORDY; + ap->ops = &qdi6500_port_ops; + ap->pio_mask = 0x07; /* Actually PIO3 !IORDY is possible */ + ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY; } - ae.sht = &qdi_sht; - ae.n_ports = 1; - ae.irq = irq; - ae.irq_flags = 0; - ae.port[0].cmd_addr = io_addr; - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ata_std_ports(&ae.port[0]); + ap->ioaddr.cmd_addr = io_addr; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ata_std_ports(&ap->ioaddr); /* * Hook in a private data structure per channel */ - ae.private_data = &qdi_data[nr_qdi_host]; + ap->private_data = &qdi_data[nr_qdi_host]; qdi_data[nr_qdi_host].timing = port; qdi_data[nr_qdi_host].fast = fast; @@ -292,8 +290,9 @@ static __init int qdi_init_one(unsigned printk(KERN_INFO DRV_NAME": qd%d at 0x%lx.\n", type, io); - ret = -ENODEV; - if (!ata_device_add(&ae)) + /* activate */ + ret = ata_host_activate(host, irq, ata_interrupt, 0, &qdi_sht); + if (ret) goto fail; qdi_host[nr_qdi_host++] = dev_get_drvdata(&pdev->dev); diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c index 9a9132c..1c54673 100644 --- a/drivers/ata/pata_radisys.c +++ b/drivers/ata/pata_radisys.c @@ -24,40 +24,12 @@ #include #include #define DRV_NAME "pata_radisys" -#define DRV_VERSION "0.4.1" - -/** - * radisys_probe_init - probe begin - * @ap: ATA port - * - * Set up cable type and use generic probe init - */ - -static int radisys_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - - -/** - * radisys_pata_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * @classes: - * - * LOCKING: - * None (inherited from caller). - */ - -static void radisys_pata_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, radisys_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} +#define DRV_VERSION "0.4.4" /** * radisys_set_piomode - Initialize host controller PATA PIO timings - * @ap: Port whose timings we are configuring - * @adev: um + * @ap: ATA port + * @adev: Device whose timings we are configuring * * Set PIO mode for device, in host controller PCI config space. * @@ -248,8 +220,9 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = radisys_pata_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c index f522daa..85c4529 100644 --- a/drivers/ata/pata_rz1000.c +++ b/drivers/ata/pata_rz1000.c @@ -25,31 +25,6 @@ #define DRV_VERSION "0.2.3" /** - * rz1000_prereset - probe begin - * @ap: ATA port - * - * Set up cable type and use generics - */ - -static int rz1000_prereset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -/** - * rz1000_error_handler - probe reset - * @ap: ATA port - * - * Perform the ATA standard reset sequence - */ - -static void rz1000_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, rz1000_prereset, ata_std_softreset, NULL, ata_std_postreset); -} - -/** * rz1000_set_mode - mode setting function * @ap: ATA interface * @unused: returned device on set_mode failure @@ -122,8 +97,9 @@ static struct ata_port_operations rz1000 .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = rz1000_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index 93b3ed0..66e8ff4 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -216,6 +216,7 @@ static struct ata_port_operations sc1200 .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index f3ed141..5df354d 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -1016,7 +1016,6 @@ static const struct ata_port_operations .error_handler = scc_error_handler, .post_internal_cmd = scc_bmdma_stop, - .irq_handler = ata_interrupt, .irq_clear = scc_bmdma_irq_clear, .irq_on = scc_irq_on, .irq_ack = scc_irq_ack, @@ -1027,7 +1026,6 @@ static const struct ata_port_operations static struct ata_port_info scc_port_info[] = { { - .sht = &scc_sht, .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x00, @@ -1040,10 +1038,10 @@ static struct ata_port_info scc_port_inf * scc_reset_controller - initialize SCC PATA controller. */ -static int scc_reset_controller(struct ata_probe_ent *probe_ent) +static int scc_reset_controller(struct ata_host *host) { - void __iomem *ctrl_base = probe_ent->iomap[SCC_CTRL_BAR]; - void __iomem *bmid_base = probe_ent->iomap[SCC_BMID_BAR]; + void __iomem *ctrl_base = host->iomap[SCC_CTRL_BAR]; + void __iomem *bmid_base = host->iomap[SCC_BMID_BAR]; void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL; void __iomem *mode_port = ctrl_base + SCC_CTL_MODEREG; void __iomem *ecmode_port = ctrl_base + SCC_CTL_ECMODE; @@ -1104,17 +1102,15 @@ static void scc_setup_ports (struct ata_ ioaddr->command_addr = ioaddr->cmd_addr + SCC_REG_CMD; } -static int scc_host_init(struct ata_probe_ent *probe_ent) +static int scc_host_init(struct ata_host *host) { - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); + struct pci_dev *pdev = to_pci_dev(host->dev); int rc; - rc = scc_reset_controller(probe_ent); + rc = scc_reset_controller(host); if (rc) return rc; - probe_ent->n_ports = 1; - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) return rc; @@ -1122,7 +1118,7 @@ static int scc_host_init(struct ata_prob if (rc) return rc; - scc_setup_ports(&probe_ent->port[0], probe_ent->iomap[SCC_BMID_BAR]); + scc_setup_ports(&host->ports[0]->ioaddr, host->iomap[SCC_BMID_BAR]); pci_set_master(pdev); @@ -1145,14 +1141,18 @@ static int scc_init_one (struct pci_dev { static int printed_version; unsigned int board_idx = (unsigned int) ent->driver_data; + const struct ata_port_info *ppi[] = { &scc_port_info[board_idx], NULL }; struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; int rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + host = ata_port_alloc_pinfo(&pdev->dev, ppi, 1); + if (!host) + return -ENOMEM; + rc = pcim_enable_device(pdev); if (rc) return rc; @@ -1162,33 +1162,14 @@ static int scc_init_one (struct pci_dev pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) - return -ENOMEM; - - probe_ent->dev = dev; - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = scc_port_info[board_idx].sht; - probe_ent->port_flags = scc_port_info[board_idx].flags; - probe_ent->pio_mask = scc_port_info[board_idx].pio_mask; - probe_ent->udma_mask = scc_port_info[board_idx].udma_mask; - probe_ent->port_ops = scc_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - - rc = scc_host_init(probe_ent); + rc = scc_host_init(host); if (rc) return rc; - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &scc_sht); } static struct pci_driver scc_pci_driver = { diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c index 598eef8..3956ef2 100644 --- a/drivers/ata/pata_serverworks.c +++ b/drivers/ata/pata_serverworks.c @@ -1,5 +1,5 @@ /* - * ata-serverworks.c - Serverworks PATA for new ATA layer + * pata_serverworks.c - Serverworks PATA for new ATA layer * (C) 2005 Red Hat Inc * Alan Cox * @@ -137,14 +137,14 @@ static struct sv_cable_table cable_detec }; /** - * serverworks_pre_reset - cable detection + * serverworks_cable_detect - cable detection * @ap: ATA port * * Perform cable detection according to the device and subvendor * identifications */ -static int serverworks_pre_reset(struct ata_port *ap) { +static int serverworks_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); struct sv_cable_table *cb = cable_detect; @@ -152,8 +152,7 @@ static int serverworks_pre_reset(struct if (cb->device == pdev->device && (cb->subvendor == pdev->subsystem_vendor || cb->subvendor == PCI_ANY_ID)) { - ap->cbl = cb->cable_detect(ap); - return ata_std_prereset(ap); + return cb->cable_detect(ap); } cb++; } @@ -162,11 +161,6 @@ static int serverworks_pre_reset(struct return -1; /* kill compiler warning */ } -static void serverworks_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, serverworks_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - /** * serverworks_is_csb - Check for CSB or OSB * @pdev: PCI device to check @@ -191,31 +185,31 @@ static u8 serverworks_is_csb(struct pci_ /** * serverworks_osb4_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device + * @mask: Mask of proposed modes * * Filter the offered modes for the device to apply controller * specific rules. OSB4 requires no UDMA for disks due to a FIFO * bug we hit. */ -static unsigned long serverworks_osb4_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask) { if (adev->class == ATA_DEV_ATA) mask &= ~ATA_MASK_UDMA; - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** * serverworks_csb_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device + * @mask: Mask of proposed modes * * Check the blacklist and disable UDMA5 if matched */ -static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask) { const char *p; char model_num[ATA_ID_PROD_LEN + 1]; @@ -223,7 +217,7 @@ static unsigned long serverworks_csb_fil /* Disk, UDMA */ if (adev->class != ATA_DEV_ATA) - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); /* Actually do need to check */ ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); @@ -232,7 +226,7 @@ static unsigned long serverworks_csb_fil if (!strcmp(p, model_num)) mask &= ~(0x1F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } @@ -339,8 +333,9 @@ static struct ata_port_operations server .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = serverworks_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = serverworks_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -374,8 +369,9 @@ static struct ata_port_operations server .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = serverworks_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = serverworks_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index dab2889..6770820 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -33,7 +33,7 @@ #include #include #define DRV_NAME "pata_sil680" -#define DRV_VERSION "0.4.5" +#define DRV_VERSION "0.4.6" /** * sil680_selreg - return register base @@ -91,12 +91,6 @@ static int sil680_cable_detect(struct at return ATA_CBL_PATA40; } -static int sil680_pre_reset(struct ata_port *ap) -{ - ap->cbl = sil680_cable_detect(ap); - return ata_std_prereset(ap); -} - /** * sil680_bus_reset - reset the SIL680 bus * @ap: ATA port to reset @@ -119,7 +113,7 @@ static int sil680_bus_reset(struct ata_p static void sil680_error_handler(struct ata_port *ap) { - ata_bmdma_drive_eh(ap, sil680_pre_reset, sil680_bus_reset, NULL, ata_std_postreset); + ata_bmdma_drive_eh(ap, ata_std_prereset, sil680_bus_reset, NULL, ata_std_postreset); } /** @@ -257,6 +251,7 @@ static struct ata_port_operations sil680 .thaw = ata_bmdma_thaw, .error_handler = sil680_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = sil680_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index 8dc3bc4..a3fbcee 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -35,7 +35,7 @@ #include #include "sis.h" #define DRV_NAME "pata_sis" -#define DRV_VERSION "0.5.0" +#define DRV_VERSION "0.5.1" struct sis_chipset { u16 device; /* PCI host ID */ @@ -86,106 +86,55 @@ static int sis_port_base(struct ata_devi } /** - * sis_133_pre_reset - check for 40/80 pin + * sis_133_cable_detect - check for 40/80 pin * @ap: Port * * Perform cable detection for the later UDMA133 capable * SiS chipset. */ -static int sis_133_pre_reset(struct ata_port *ap) +static int sis_133_cable_detect(struct ata_port *ap) { - static const struct pci_bits sis_enable_bits[] = { - { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */ - { 0x4aU, 1U, 0x04UL, 0x04UL }, /* port 1 */ - }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); u16 tmp; - if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) - return -ENOENT; - /* The top bit of this register is the cable detect bit */ pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp); if ((tmp & 0x8000) && !sis_short_ata40(pdev)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - - return ata_std_prereset(ap); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } /** - * sis_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * - * LOCKING: - * None (inherited from caller). - */ - -static void sis_133_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, sis_133_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - - -/** - * sis_66_pre_reset - check for 40/80 pin + * sis_66_cable_detect - check for 40/80 pin * @ap: Port * * Perform cable detection on the UDMA66, UDMA100 and early UDMA133 * SiS IDE controllers. */ -static int sis_66_pre_reset(struct ata_port *ap) +static int sis_66_cable_detect(struct ata_port *ap) { - static const struct pci_bits sis_enable_bits[] = { - { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */ - { 0x4aU, 1U, 0x04UL, 0x04UL }, /* port 1 */ - }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 tmp; - if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) { - ata_port_disable(ap); - ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n"); - return 0; - } /* Older chips keep cable detect in bits 4/5 of reg 0x48 */ pci_read_config_byte(pdev, 0x48, &tmp); tmp >>= ap->port_no; if ((tmp & 0x10) && !sis_short_ata40(pdev)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - - return ata_std_prereset(ap); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } -/** - * sis_66_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * @classes: - * - * LOCKING: - * None (inherited from caller). - */ - -static void sis_66_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, sis_66_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} /** - * sis_old_pre_reset - probe begin + * sis_pre_reset - probe begin * @ap: ATA port * * Set up cable type and use generic probe init */ -static int sis_old_pre_reset(struct ata_port *ap) +static int sis_pre_reset(struct ata_port *ap) { static const struct pci_bits sis_enable_bits[] = { { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */ @@ -194,27 +143,23 @@ static int sis_old_pre_reset(struct ata_ struct pci_dev *pdev = to_pci_dev(ap->host->dev); - if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) { - ata_port_disable(ap); - ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n"); - return 0; - } - ap->cbl = ATA_CBL_PATA40; + if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) + return -ENOENT; return ata_std_prereset(ap); } /** - * sis_old_error_handler - Probe specified port on PATA host controller + * sis_error_handler - Probe specified port on PATA host controller * @ap: Port to probe * * LOCKING: * None (inherited from caller). */ -static void sis_old_error_handler(struct ata_port *ap) +static void sis_error_handler(struct ata_port *ap) { - ata_bmdma_drive_eh(ap, sis_old_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + ata_bmdma_drive_eh(ap, sis_pre_reset, ata_std_softreset, NULL, ata_std_postreset); } /** @@ -494,7 +439,7 @@ static void sis_133_early_set_dmamode (s int drive_pci = sis_port_base(adev); u16 timing; - const u16 udma_bits[] = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100}; + static const u16 udma_bits[] = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100}; pci_read_config_word(pdev, drive_pci, &timing); @@ -531,8 +476,8 @@ static void sis_133_set_dmamode (struct u32 reg54; /* bits 4- cycle time 8 - cvs time */ - const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 }; - const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 }; + static const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 }; + static const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 }; /* If bit 14 is set then the registers are mapped at 0x70 not 0x40 */ pci_read_config_dword(pdev, 0x54, ®54); @@ -595,8 +540,9 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_133_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = sis_133_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -628,8 +574,9 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_66_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = sis_66_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -661,9 +608,9 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_66_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - + .cable_detect = sis_66_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -692,10 +639,11 @@ static const struct ata_port_operations .check_status = ata_check_status, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .cable_detect = sis_66_cable_detect, .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_66_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .bmdma_setup = ata_bmdma_setup, @@ -728,8 +676,9 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_old_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index b681441..da9e22b 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -58,7 +58,6 @@ static int sl82c105_pre_reset(struct ata if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -238,6 +237,7 @@ static struct ata_port_operations sl82c1 .thaw = ata_bmdma_thaw, .error_handler = sl82c105_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = sl82c105_bmdma_start, diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c index 71418f2..e618ffd 100644 --- a/drivers/ata/pata_triflex.c +++ b/drivers/ata/pata_triflex.c @@ -43,7 +43,7 @@ #include #include #define DRV_NAME "pata_triflex" -#define DRV_VERSION "0.2.7" +#define DRV_VERSION "0.2.8" /** * triflex_prereset - probe begin @@ -63,7 +63,6 @@ static int triflex_prereset(struct ata_p if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -214,6 +213,7 @@ static struct ata_port_operations trifle .thaw = ata_bmdma_thaw, .error_handler = triflex_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = triflex_bmdma_start, diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 946ade0..96b7179 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -62,7 +62,7 @@ #include #include #define DRV_NAME "pata_via" -#define DRV_VERSION "0.2.1" +#define DRV_VERSION "0.3.1" /* * The following comes directly from Vojtech Pavlik's ide/pci/via82cxxx @@ -135,16 +135,23 @@ static const struct via_isa_bridge { */ static int via_cable_detect(struct ata_port *ap) { + const struct via_isa_bridge *config = ap->host->private_data; struct pci_dev *pdev = to_pci_dev(ap->host->dev); u32 ata66; + /* Early chips are 40 wire */ + if ((config->flags & VIA_UDMA) < VIA_UDMA_66) + return ATA_CBL_PATA40; + /* UDMA 66 chips have only drive side logic */ + else if((config->flags & VIA_UDMA) < VIA_UDMA_100) + return ATA_CBL_PATA_UNK; + /* UDMA 100 or later */ pci_read_config_dword(pdev, 0x50, &ata66); /* Check both the drive cable reporting bits, we might not have two drives */ if (ata66 & (0x10100000 >> (16 * ap->port_no))) return ATA_CBL_PATA80; - else - return ATA_CBL_PATA40; + return ATA_CBL_PATA40; } static int via_pre_reset(struct ata_port *ap) @@ -156,22 +163,10 @@ static int via_pre_reset(struct ata_port { 0x40, 1, 0x02, 0x02 }, { 0x40, 1, 0x01, 0x01 } }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no])) return -ENOENT; } - - if ((config->flags & VIA_UDMA) >= VIA_UDMA_100) - ap->cbl = via_cable_detect(ap); - /* The UDMA66 series has no cable detect so do drive side detect */ - else if ((config->flags & VIA_UDMA) < VIA_UDMA_66) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA_UNK; - - return ata_std_prereset(ap); } @@ -327,6 +322,7 @@ static struct ata_port_operations via_po .thaw = ata_bmdma_thaw, .error_handler = via_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = via_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -362,6 +358,7 @@ static struct ata_port_operations via_po .thaw = ata_bmdma_thaw, .error_handler = via_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = via_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_winbond.c b/drivers/ata/pata_winbond.c index 6c11103..cc4ad27 100644 --- a/drivers/ata/pata_winbond.c +++ b/drivers/ata/pata_winbond.c @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -36,7 +35,7 @@ #else static int probe_winbond; #endif -static spinlock_t winbond_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(winbond_lock); static void winbond_writecfg(unsigned long port, u8 reg, u8 val) { @@ -152,13 +151,13 @@ static struct ata_port_operations winbon .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = winbond_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -179,11 +178,9 @@ static struct ata_port_operations winbon static __init int winbond_init_one(unsigned long port) { - struct ata_probe_ent ae; struct platform_device *pdev; - int ret; u8 reg; - int i; + int i, rc; reg = winbond_readcfg(port, 0x81); reg |= 0x80; /* jumpered mode off */ @@ -202,58 +199,56 @@ static __init int winbond_init_one(unsig for (i = 0; i < 2 ; i ++) { unsigned long cmd_port = 0x1F0 - (0x80 * i); + struct ata_host *host; + struct ata_port *ap; void __iomem *cmd_addr, *ctl_addr; - if (reg & (1 << i)) { - /* - * Fill in a probe structure first of all - */ - - pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); - - cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8); - ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1); - if (!cmd_addr || !ctl_addr) { - platform_device_unregister(pdev); - return -ENOMEM; - } - - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - - ae.port_ops = &winbond_port_ops; - ae.pio_mask = 0x1F; - - ae.sht = &winbond_sht; - - ae.n_ports = 1; - ae.irq = 14 + i; - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; - ae.port[0].cmd_addr = cmd_addr; - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ata_std_ports(&ae.port[0]); - /* - * Hook in a private data structure per channel - */ - ae.private_data = &winbond_data[nr_winbond_host]; - winbond_data[nr_winbond_host].config = port; - winbond_data[nr_winbond_host].platform_dev = pdev; - - ret = ata_device_add(&ae); - if (ret == 0) { - platform_device_unregister(pdev); - return -ENODEV; - } - winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev); - } + if (!(reg & (1 << i))) + continue; + + pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + rc = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto err_unregister; + + rc = -ENOMEM; + cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8); + ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1); + if (!cmd_addr || !ctl_addr) + goto err_unregister; + + ap = host->ports[0]; + ap->ops = &winbond_port_ops; + ap->pio_mask = 0x1F; + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->ioaddr.cmd_addr = cmd_addr; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ata_std_ports(&ap->ioaddr); + + /* hook in a private data structure per channel */ + host->private_data = &winbond_data[nr_winbond_host]; + winbond_data[nr_winbond_host].config = port; + winbond_data[nr_winbond_host].platform_dev = pdev; + + /* activate */ + rc = ata_host_activate(host, 14 + i, ata_interrupt, 0, + &winbond_sht); + if (rc) + goto err_unregister; + + winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev); } return 0; + + err_unregister: + platform_device_unregister(pdev); + return rc; } /** diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 5dd3ca8..52b6953 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -52,9 +52,9 @@ #define ADMA_ATA_REGS(base,port_no) ((ba /* macro to calculate base address for ADMA regs */ #define ADMA_REGS(base,port_no) ((base) + 0x80 + ((port_no) * 0x20)) -/* macro to obtain addresses from ata_host */ -#define ADMA_HOST_REGS(host,port_no) \ - ADMA_REGS((host)->iomap[ADMA_MMIO_BAR], port_no) +/* macro to obtain addresses from ata_port */ +#define ADMA_PORT_REGS(ap) \ + ADMA_REGS((ap)->host->iomap[ADMA_MMIO_BAR], ap->port_no) enum { ADMA_MMIO_BAR = 4, @@ -128,7 +128,6 @@ struct adma_port_priv { static int adma_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t adma_intr (int irq, void *dev_instance); static int adma_port_start(struct ata_port *ap); static void adma_host_stop(struct ata_host *host); static void adma_port_stop(struct ata_port *ap); @@ -172,7 +171,6 @@ static const struct ata_port_operations .qc_issue = adma_qc_issue, .eng_timeout = adma_eng_timeout, .data_xfer = ata_data_xfer, - .irq_handler = adma_intr, .irq_clear = adma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -186,7 +184,6 @@ static const struct ata_port_operations static struct ata_port_info adma_port_info[] = { /* board_1841_idx */ { - .sht = &adma_ata_sht, .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, @@ -229,8 +226,10 @@ static void adma_irq_clear(struct ata_po /* nothing */ } -static void adma_reset_engine(void __iomem *chan) +static void adma_reset_engine(struct ata_port *ap) { + void __iomem *chan = ADMA_PORT_REGS(ap); + /* reset ADMA to idle state */ writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL); udelay(2); @@ -241,14 +240,14 @@ static void adma_reset_engine(void __iom static void adma_reinit_engine(struct ata_port *ap) { struct adma_port_priv *pp = ap->private_data; - void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); /* mask/clear ATA interrupts */ writeb(ATA_NIEN, ap->ioaddr.ctl_addr); ata_check_status(ap); /* reset the ADMA engine */ - adma_reset_engine(chan); + adma_reset_engine(ap); /* set in-FIFO threshold to 0x100 */ writew(0x100, chan + ADMA_FIFO_IN); @@ -268,7 +267,7 @@ static void adma_reinit_engine(struct at static inline void adma_enter_reg_mode(struct ata_port *ap) { - void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); writew(aPIOMD4, chan + ADMA_CONTROL); readb(chan + ADMA_STATUS); /* flush */ @@ -415,7 +414,7 @@ #endif static inline void adma_packet_start(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); VPRINTK("ENTER, ap %p\n", ap); @@ -453,7 +452,7 @@ static inline unsigned int adma_intr_pkt struct ata_port *ap = host->ports[port_no]; struct adma_port_priv *pp; struct ata_queued_cmd *qc; - void __iomem *chan = ADMA_HOST_REGS(host, port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); u8 status = readb(chan + ADMA_STATUS); if (status == 0) @@ -575,7 +574,7 @@ static int adma_port_start(struct ata_po static void adma_port_stop(struct ata_port *ap) { - adma_reset_engine(ADMA_HOST_REGS(ap->host, ap->port_no)); + adma_reset_engine(ap); } static void adma_host_stop(struct ata_host *host) @@ -583,21 +582,19 @@ static void adma_host_stop(struct ata_ho unsigned int port_no; for (port_no = 0; port_no < ADMA_PORTS; ++port_no) - adma_reset_engine(ADMA_HOST_REGS(host, port_no)); + adma_reset_engine(host->ports[port_no]); } -static void adma_host_init(unsigned int chip_id, - struct ata_probe_ent *probe_ent) +static void adma_host_init(struct ata_host *host, unsigned int chip_id) { unsigned int port_no; - void __iomem *mmio_base = probe_ent->iomap[ADMA_MMIO_BAR]; /* enable/lock aGO operation */ - writeb(7, mmio_base + ADMA_MODE_LOCK); + writeb(7, host->iomap[ADMA_MMIO_BAR] + ADMA_MODE_LOCK); /* reset the ADMA logic */ for (port_no = 0; port_no < ADMA_PORTS; ++port_no) - adma_reset_engine(ADMA_REGS(mmio_base, port_no)); + adma_reset_engine(host->ports[port_no]); } static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base) @@ -623,14 +620,21 @@ static int adma_ata_init_one(struct pci_ const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - void __iomem *mmio_base; unsigned int board_idx = (unsigned int) ent->driver_data; + const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL }; + struct ata_host *host; + void __iomem *mmio_base; int rc, port_no; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -641,46 +645,23 @@ static int adma_ata_init_one(struct pci_ rc = pcim_iomap_regions(pdev, 1 << ADMA_MMIO_BAR, DRV_NAME); if (rc) return rc; - mmio_base = pcim_iomap_table(pdev)[ADMA_MMIO_BAR]; + host->iomap = pcim_iomap_table(pdev); + mmio_base = host->iomap[ADMA_MMIO_BAR]; rc = adma_set_dma_masks(pdev, mmio_base); if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = adma_port_info[board_idx].sht; - probe_ent->port_flags = adma_port_info[board_idx].flags; - probe_ent->pio_mask = adma_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = adma_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = adma_port_info[board_idx].udma_mask; - probe_ent->port_ops = adma_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->n_ports = ADMA_PORTS; - probe_ent->iomap = pcim_iomap_table(pdev); - - for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { - adma_ata_setup_port(&probe_ent->port[port_no], + for (port_no = 0; port_no < ADMA_PORTS; ++port_no) + adma_ata_setup_port(&host->ports[port_no]->ioaddr, ADMA_ATA_REGS(mmio_base, port_no)); - } - - pci_set_master(pdev); /* initialize adapter */ - adma_host_init(board_idx, probe_ent); + adma_host_init(host, board_idx); - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, adma_intr, IRQF_SHARED, + &adma_ata_sht); } static int __init adma_ata_init(void) diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 1e21688..f099a1d 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -488,11 +488,11 @@ static void inic_error_handler(struct at static void inic_post_internal_cmd(struct ata_queued_cmd *qc) { /* make DMA engine forget about the failed command */ - if (qc->err_mask) + if (qc->flags & ATA_QCFLAG_FAILED) inic_reset_port(inic_port_base(qc->ap)); } -static void inic_dev_config(struct ata_port *ap, struct ata_device *dev) +static void inic_dev_config(struct ata_device *dev) { /* inic can only handle upto LBA28 max sectors */ if (dev->max_sectors > ATA_MAX_SECTORS) @@ -559,7 +559,6 @@ static struct ata_port_operations inic_p .bmdma_stop = inic_bmdma_stop, .bmdma_status = inic_bmdma_status, - .irq_handler = inic_interrupt, .irq_clear = inic_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -580,7 +579,6 @@ static struct ata_port_operations inic_p }; static struct ata_port_info inic_port_info = { - .sht = &inic_sht, /* For some reason, ATA_PROT_ATAPI is broken on this * controller, and no, PIO_POLLING does't fix it. It somehow * manages to report the wrong ireason and ignoring ireason @@ -642,7 +640,9 @@ static int inic_pci_device_resume(struct void __iomem *mmio_base = host->iomap[MMIO_BAR]; int rc; - ata_pci_device_do_resume(pdev); + rc = ata_pci_device_do_resume(pdev); + if (rc) + return rc; if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { rc = init_controller(mmio_base, hpriv->cached_hctl); @@ -659,8 +659,8 @@ #endif static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_port_info *pinfo = &inic_port_info; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = { &inic_port_info, NULL }; + struct ata_host *host; struct inic_host_priv *hpriv; void __iomem * const *iomap; int i, rc; @@ -668,6 +668,15 @@ static int inic_init_one(struct pci_dev if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS); + hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); + if (!host || !hpriv) + return -ENOMEM; + + host->private_data = hpriv; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -675,7 +684,22 @@ static int inic_init_one(struct pci_dev rc = pcim_iomap_regions(pdev, 0x3f, DRV_NAME); if (rc) return rc; - iomap = pcim_iomap_table(pdev); + host->iomap = iomap = pcim_iomap_table(pdev); + + for (i = 0; i < NR_PORTS; i++) { + struct ata_ioports *port = &host->ports[i]->ioaddr; + void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE; + + port->cmd_addr = iomap[2 * i]; + port->altstatus_addr = + port->ctl_addr = (void __iomem *) + ((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS); + port->scr_addr = port_base + PORT_SCR; + + ata_std_ports(port); + } + + hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL); /* Set dma_mask. This devices doesn't support 64bit addressing. */ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); @@ -692,43 +716,6 @@ static int inic_init_one(struct pci_dev return rc; } - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); - if (!probe_ent || !hpriv) - return -ENOMEM; - - probe_ent->dev = &pdev->dev; - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = pinfo->sht; - probe_ent->port_flags = pinfo->flags; - probe_ent->pio_mask = pinfo->pio_mask; - probe_ent->mwdma_mask = pinfo->mwdma_mask; - probe_ent->udma_mask = pinfo->udma_mask; - probe_ent->port_ops = pinfo->port_ops; - probe_ent->n_ports = NR_PORTS; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - - probe_ent->iomap = iomap; - - for (i = 0; i < NR_PORTS; i++) { - struct ata_ioports *port = &probe_ent->port[i]; - void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE; - - port->cmd_addr = iomap[2 * i]; - port->altstatus_addr = - port->ctl_addr = (void __iomem *) - ((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS); - port->scr_addr = port_base + PORT_SCR; - - ata_std_ports(port); - } - - probe_ent->private_data = hpriv; - hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL); - rc = init_controller(iomap[MMIO_BAR], hpriv->cached_hctl); if (rc) { dev_printk(KERN_ERR, &pdev->dev, @@ -737,13 +724,8 @@ static int inic_init_one(struct pci_dev } pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - - return 0; + return ata_host_activate(host, pdev->irq, inic_interrupt, IRQF_SHARED, + &inic_sht); } static const struct pci_device_id inic_pci_tbl[] = { diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index a65ba63..cb9b9ac 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -253,10 +253,7 @@ #define IS_GEN_II(hpriv) IS_60XX(hpriv) #define IS_GEN_IIE(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_IIE) enum { - /* Our DMA boundary is determined by an ePRD being unable to handle - * anything larger than 64KB - */ - MV_DMA_BOUNDARY = 0xffffU, + MV_DMA_BOUNDARY = 0xffffffffU, EDMA_REQ_Q_BASE_LO_MASK = 0xfffffc00U, @@ -350,7 +347,6 @@ static void mv_port_stop(struct ata_port static void mv_qc_prep(struct ata_queued_cmd *qc); static void mv_qc_prep_iie(struct ata_queued_cmd *qc); static unsigned int mv_qc_issue(struct ata_queued_cmd *qc); -static irqreturn_t mv_interrupt(int irq, void *dev_instance); static void mv_eng_timeout(struct ata_port *ap); static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); @@ -384,10 +380,10 @@ static struct scsi_host_template mv_sht .queuecommand = ata_scsi_queuecmd, .can_queue = MV_USE_Q_DEPTH, .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = MV_MAX_SG_CT / 2, + .sg_tablesize = MV_MAX_SG_CT, .cmd_per_lun = ATA_SHT_CMD_PER_LUN, .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, + .use_clustering = 1, .proc_name = DRV_NAME, .dma_boundary = MV_DMA_BOUNDARY, .slave_configure = ata_scsi_slave_config, @@ -405,6 +401,7 @@ static const struct ata_port_operations .dev_select = ata_std_dev_select, .phy_reset = mv_phy_reset, + .cable_detect = ata_cable_sata, .qc_prep = mv_qc_prep, .qc_issue = mv_qc_issue, @@ -412,7 +409,6 @@ static const struct ata_port_operations .eng_timeout = mv_eng_timeout, - .irq_handler = mv_interrupt, .irq_clear = mv_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -434,6 +430,7 @@ static const struct ata_port_operations .dev_select = ata_std_dev_select, .phy_reset = mv_phy_reset, + .cable_detect = ata_cable_sata, .qc_prep = mv_qc_prep, .qc_issue = mv_qc_issue, @@ -441,7 +438,6 @@ static const struct ata_port_operations .eng_timeout = mv_eng_timeout, - .irq_handler = mv_interrupt, .irq_clear = mv_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -463,6 +459,7 @@ static const struct ata_port_operations .dev_select = ata_std_dev_select, .phy_reset = mv_phy_reset, + .cable_detect = ata_cable_sata, .qc_prep = mv_qc_prep_iie, .qc_issue = mv_qc_issue, @@ -470,7 +467,6 @@ static const struct ata_port_operations .eng_timeout = mv_eng_timeout, - .irq_handler = mv_interrupt, .irq_clear = mv_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -484,35 +480,30 @@ static const struct ata_port_operations static const struct ata_port_info mv_port_info[] = { { /* chip_504x */ - .sht = &mv_sht, .flags = MV_COMMON_FLAGS, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv5_ops, }, { /* chip_508x */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv5_ops, }, { /* chip_5080 */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv5_ops, }, { /* chip_604x */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv6_ops, }, { /* chip_608x */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS | MV_FLAG_DUAL_HC), .pio_mask = 0x1f, /* pio0-4 */ @@ -520,14 +511,12 @@ static const struct ata_port_info mv_por .port_ops = &mv6_ops, }, { /* chip_6042 */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv_iie_ops, }, { /* chip_7042 */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ @@ -551,6 +540,9 @@ static const struct pci_device_id mv_pci { PCI_VDEVICE(TTI, 0x2310), chip_7042 }, + /* add Marvell 7042 support */ + { PCI_VDEVICE(MARVELL, 0x7042), chip_7042 }, + { } /* terminate list */ }; @@ -585,6 +577,39 @@ static const struct mv_hw_ops mv6xxx_ops static int msi; /* Use PCI msi; either zero (off, default) or non-zero */ +/* move to PCI layer or libata core? */ +static int pci_go_64(struct pci_dev *pdev) +{ + int rc; + + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { + rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "64-bit DMA enable failed\n"); + return rc; + } + } + } else { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + return rc; + } + } + + return rc; +} + /* * Functions */ @@ -798,20 +823,18 @@ static u32 mv_scr_read(struct ata_port * { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (0xffffffffU != ofs) { + if (0xffffffffU != ofs) return readl(mv_ap_base(ap) + ofs); - } else { + else return (u32) ofs; - } } static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (0xffffffffU != ofs) { + if (0xffffffffU != ofs) writelfl(val, mv_ap_base(ap) + ofs); - } } static void mv_edma_cfg(struct mv_host_priv *hpriv, void __iomem *port_mmio) @@ -959,38 +982,30 @@ static void mv_port_stop(struct ata_port * LOCKING: * Inherited from caller. */ -static void mv_fill_sg(struct ata_queued_cmd *qc) +static unsigned int mv_fill_sg(struct ata_queued_cmd *qc) { struct mv_port_priv *pp = qc->ap->private_data; - unsigned int i = 0; + unsigned int n_sg = 0; struct scatterlist *sg; + struct mv_sg *mv_sg; + mv_sg = pp->sg_tbl; ata_for_each_sg(sg, qc) { - dma_addr_t addr; - u32 sg_len, len, offset; - - addr = sg_dma_address(sg); - sg_len = sg_dma_len(sg); - - while (sg_len) { - offset = addr & MV_DMA_BOUNDARY; - len = sg_len; - if ((offset + sg_len) > 0x10000) - len = 0x10000 - offset; + dma_addr_t addr = sg_dma_address(sg); + u32 sg_len = sg_dma_len(sg); - pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff); - pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); - pp->sg_tbl[i].flags_size = cpu_to_le32(len & 0xffff); + mv_sg->addr = cpu_to_le32(addr & 0xffffffff); + mv_sg->addr_hi = cpu_to_le32((addr >> 16) >> 16); + mv_sg->flags_size = cpu_to_le32(sg_len & 0xffff); - sg_len -= len; - addr += len; + if (ata_sg_is_last(sg, qc)) + mv_sg->flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL); - if (!sg_len && ata_sg_is_last(sg, qc)) - pp->sg_tbl[i].flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL); - - i++; - } + mv_sg++; + n_sg++; } + + return n_sg; } static inline unsigned mv_inc_q_index(unsigned index) @@ -1320,17 +1335,15 @@ static void mv_host_intr(struct ata_host int shift, port, port0, hard_port, handled; unsigned int err_mask; - if (hc == 0) { + if (hc == 0) port0 = 0; - } else { + else port0 = MV_PORTS_PER_HC; - } /* we'll need the HC success int register in most cases */ hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS); - if (hc_irq_cause) { + if (hc_irq_cause) writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); - } VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n", hc,relevant,hc_irq_cause); @@ -1425,9 +1438,8 @@ static irqreturn_t mv_interrupt(int irq, /* check the cases where we either have nothing pending or have read * a bogus register value which can indicate HW removal or PCI fault */ - if (!irq_stat || (0xffffffffU == irq_stat)) { + if (!irq_stat || (0xffffffffU == irq_stat)) return IRQ_NONE; - } n_hcs = mv_get_hc_count(host->ports[0]->flags); spin_lock(&host->lock); @@ -1952,7 +1964,6 @@ comreset_retry: ata_port_disable(ap); return; } - ap->cbl = ATA_CBL_SATA; /* even after SStatus reflects that device is ready, * it seems to take a while for link to be fully @@ -2077,9 +2088,10 @@ static void mv_port_init(struct ata_iopo readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS)); } -static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv, - unsigned int board_idx) +static int mv_chip_id(struct ata_host *host, unsigned int board_idx) { + struct pci_dev *pdev = to_pci_dev(host->dev); + struct mv_host_priv *hpriv = host->private_data; u8 rev_id; u32 hp_flags = hpriv->hp_flags; @@ -2177,8 +2189,8 @@ static int mv_chip_id(struct pci_dev *pd /** * mv_init_host - Perform some early initialization of the host. - * @pdev: host PCI device - * @probe_ent: early data struct representing the host + * @host: ATA host to initialize + * @board_idx: controller index * * If possible, do an early global reset of the host. Then do * our port init and clear/unmask all/relevant host interrupts. @@ -2186,24 +2198,23 @@ static int mv_chip_id(struct pci_dev *pd * LOCKING: * Inherited from caller. */ -static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent, - unsigned int board_idx) +static int mv_init_host(struct ata_host *host, unsigned int board_idx) { int rc = 0, n_hc, port, hc; - void __iomem *mmio = probe_ent->iomap[MV_PRIMARY_BAR]; - struct mv_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[MV_PRIMARY_BAR]; + struct mv_host_priv *hpriv = host->private_data; /* global interrupt mask */ writel(0, mmio + HC_MAIN_IRQ_MASK_OFS); - rc = mv_chip_id(pdev, hpriv, board_idx); + rc = mv_chip_id(host, board_idx); if (rc) goto done; - n_hc = mv_get_hc_count(probe_ent->port_flags); - probe_ent->n_ports = MV_PORTS_PER_HC * n_hc; + n_hc = mv_get_hc_count(host->ports[0]->flags); - for (port = 0; port < probe_ent->n_ports; port++) + for (port = 0; port < host->n_ports; port++) hpriv->ops->read_preamp(hpriv, port, mmio); rc = hpriv->ops->reset_hc(hpriv, mmio, n_hc); @@ -2214,7 +2225,7 @@ static int mv_init_host(struct pci_dev * hpriv->ops->reset_bus(pdev, mmio); hpriv->ops->enable_leds(hpriv, mmio); - for (port = 0; port < probe_ent->n_ports; port++) { + for (port = 0; port < host->n_ports; port++) { if (IS_60XX(hpriv)) { void __iomem *port_mmio = mv_port_base(mmio, port); @@ -2227,9 +2238,9 @@ static int mv_init_host(struct pci_dev * hpriv->ops->phy_errata(hpriv, mmio, port); } - for (port = 0; port < probe_ent->n_ports; port++) { + for (port = 0; port < host->n_ports; port++) { void __iomem *port_mmio = mv_port_base(mmio, port); - mv_port_init(&probe_ent->port[port], port_mmio); + mv_port_init(&host->ports[port]->ioaddr, port_mmio); } for (hc = 0; hc < n_hc; hc++) { @@ -2268,17 +2279,17 @@ done: /** * mv_print_info - Dump key info to kernel log for perusal. - * @probe_ent: early data struct representing the host + * @host: ATA host to print info about * * FIXME: complete this. * * LOCKING: * Inherited from caller. */ -static void mv_print_info(struct ata_probe_ent *probe_ent) +static void mv_print_info(struct ata_host *host) { - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - struct mv_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = to_pci_dev(host->dev); + struct mv_host_priv *hpriv = host->private_data; u8 rev_id, scc; const char *scc_s; @@ -2297,7 +2308,7 @@ static void mv_print_info(struct ata_pro dev_printk(KERN_INFO, &pdev->dev, "%u slots %u ports %s mode IRQ via %s\n", - (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports, + (unsigned)MV_MAX_Q_DEPTH, host->n_ports, scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx"); } @@ -2312,50 +2323,42 @@ static void mv_print_info(struct ata_pro static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; - struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; - struct mv_host_priv *hpriv; unsigned int board_idx = (unsigned int)ent->driver_data; - int rc; + const struct ata_port_info *ppi[] = { &mv_port_info[board_idx], NULL }; + struct ata_host *host; + struct mv_host_priv *hpriv; + int n_ports, rc; if (!printed_version++) dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + n_ports = mv_get_hc_count(ppi[0]->flags) * MV_PORTS_PER_HC; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); + if (!host || !hpriv) + return -ENOMEM; + host->private_data = hpriv; + + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; - pci_set_master(pdev); rc = pcim_iomap_regions(pdev, 1 << MV_PRIMARY_BAR, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - - probe_ent->sht = mv_port_info[board_idx].sht; - probe_ent->port_flags = mv_port_info[board_idx].flags; - probe_ent->pio_mask = mv_port_info[board_idx].pio_mask; - probe_ent->udma_mask = mv_port_info[board_idx].udma_mask; - probe_ent->port_ops = mv_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - probe_ent->private_data = hpriv; + rc = pci_go_64(pdev); + if (rc) + return rc; /* initialize adapter */ - rc = mv_init_host(pdev, probe_ent, board_idx); + rc = mv_init_host(host, board_idx); if (rc) return rc; @@ -2364,13 +2367,11 @@ static int mv_init_one(struct pci_dev *p pci_intx(pdev, 1); mv_dump_pci_cfg(pdev, 0x68); - mv_print_info(probe_ent); + mv_print_info(host); - if (ata_device_add(probe_ent) == 0) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, mv_interrupt, IRQF_SHARED, + &mv_sht); } static int __init mv_init(void) diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 9d9670a..0216974 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -260,6 +260,7 @@ #endif static void nv_adma_error_handler(struct ata_port *ap); static void nv_adma_host_stop(struct ata_host *host); static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc); +static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf); enum nv_host_type { @@ -368,7 +369,6 @@ static const struct ata_port_operations .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_generic_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -395,7 +395,6 @@ static const struct ata_port_operations .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_nf2_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -422,7 +421,6 @@ static const struct ata_port_operations .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_ck804_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -435,7 +433,7 @@ static const struct ata_port_operations static const struct ata_port_operations nv_adma_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, - .tf_read = ata_tf_read, + .tf_read = nv_adma_tf_read, .check_atapi_dma = nv_adma_check_atapi_dma, .exec_command = ata_exec_command, .check_status = ata_check_status, @@ -451,7 +449,6 @@ static const struct ata_port_operations .error_handler = nv_adma_error_handler, .post_internal_cmd = nv_adma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_adma_interrupt, .irq_clear = nv_adma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -476,6 +473,7 @@ static struct ata_port_info nv_port_info .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_generic_ops, + .irq_handler = nv_generic_interrupt, }, /* nforce2/3 */ { @@ -486,6 +484,7 @@ static struct ata_port_info nv_port_info .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_nf2_ops, + .irq_handler = nv_nf2_interrupt, }, /* ck804 */ { @@ -496,6 +495,7 @@ static struct ata_port_info nv_port_info .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_ck804_ops, + .irq_handler = nv_ck804_interrupt, }, /* ADMA */ { @@ -507,6 +507,7 @@ static struct ata_port_info nv_port_info .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_adma_ops, + .irq_handler = nv_adma_interrupt, }, }; @@ -667,6 +668,18 @@ static int nv_adma_check_atapi_dma(struc return !(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE); } +static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + /* Since commands where a result TF is requested are not + executed in ADMA mode, the only time this function will be called + in ADMA mode will be if a command fails. In this case we + don't care about going into register mode with ADMA commands + pending, as the commands will all shortly be aborted anyway. */ + nv_adma_register_mode(ap); + + ata_tf_read(ap, tf); +} + static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb) { unsigned int idx = 0; @@ -738,19 +751,11 @@ static int nv_adma_check_cpb(struct ata_ return 1; } - if (flags & NV_CPB_RESP_DONE) { + if (likely(flags & NV_CPB_RESP_DONE)) { struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num); VPRINTK("CPB flags done, flags=0x%x\n", flags); if (likely(qc)) { - /* Grab the ATA port status for non-NCQ commands. - For NCQ commands the current status may have nothing to do with - the command just completed. */ - if (qc->tf.protocol != ATA_PROT_NCQ) { - u8 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4)); - qc->err_mask |= ac_err_mask(ata_status); - } - DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num, - qc->err_mask); + DPRINTK("Completing qc from tag %d\n",cpb_num); ata_qc_complete(qc); } else { struct ata_eh_info *ehi = &ap->eh_info; @@ -1074,14 +1079,14 @@ static int nv_adma_port_resume(struct at } #endif -static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port) +static void nv_adma_setup_port(struct ata_port *ap) { - void __iomem *mmio = probe_ent->iomap[NV_MMIO_BAR]; - struct ata_ioports *ioport = &probe_ent->port[port]; + void __iomem *mmio = ap->host->iomap[NV_MMIO_BAR]; + struct ata_ioports *ioport = &ap->ioaddr; VPRINTK("ENTER\n"); - mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE; + mmio += NV_ADMA_PORT + ap->port_no * NV_ADMA_PORT_SIZE; ioport->cmd_addr = mmio; ioport->data_addr = mmio + (ATA_REG_DATA * 4); @@ -1098,9 +1103,9 @@ static void nv_adma_setup_port(struct at ioport->ctl_addr = mmio + 0x20; } -static int nv_adma_host_init(struct ata_probe_ent *probe_ent) +static int nv_adma_host_init(struct ata_host *host) { - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); + struct pci_dev *pdev = to_pci_dev(host->dev); unsigned int i; u32 tmp32; @@ -1115,8 +1120,8 @@ static int nv_adma_host_init(struct ata_ pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32); - for (i = 0; i < probe_ent->n_ports; i++) - nv_adma_setup_port(probe_ent, i); + for (i = 0; i < host->n_ports; i++) + nv_adma_setup_port(host->ports[i]); return 0; } @@ -1167,9 +1172,11 @@ static int nv_adma_use_reg_mode(struct a struct nv_adma_port_priv *pp = qc->ap->private_data; /* ADMA engine can only be used for non-ATAPI DMA commands, - or interrupt-driven no-data commands. */ + or interrupt-driven no-data commands, where a result taskfile + is not required. */ if((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) || - (qc->tf.flags & ATA_TFLAG_POLLING)) + (qc->tf.flags & ATA_TFLAG_POLLING) || + (qc->flags & ATA_QCFLAG_RESULT_TF)) return 1; if((qc->flags & ATA_QCFLAG_DMAMAP) || @@ -1473,14 +1480,13 @@ static void nv_adma_error_handler(struct static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; - struct ata_port_info *ppi[2]; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[2]; + struct ata_host *host; struct nv_host_priv *hpriv; int rc; u32 bar; void __iomem *base; unsigned long type = ent->driver_data; - int mask_set = 0; // Make sure this is a SATA controller by counting the number of bars // (NVIDIA SATA controllers will always have six bars). Otherwise, @@ -1496,50 +1502,38 @@ static int nv_init_one (struct pci_dev * if (rc) return rc; - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - - if(type >= CK804 && adma_enabled) { + /* determine type and allocate host */ + if (type >= CK804 && adma_enabled) { dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n"); type = ADMA; - if(!pci_set_dma_mask(pdev, DMA_64BIT_MASK) && - !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) - mask_set = 1; - } - - if(!mask_set) { - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; } - rc = -ENOMEM; + ppi[0] = ppi[1] = &nv_port_info[type]; + rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host); + if (rc) + return rc; hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; + hpriv->type = type; + host->private_data = hpriv; - ppi[0] = ppi[1] = &nv_port_info[type]; - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return -ENOMEM; - - if (!pcim_iomap(pdev, NV_MMIO_BAR, 0)) - return -EIO; - probe_ent->iomap = pcim_iomap_table(pdev); + /* set 64bit dma masks, may fail */ + if (type == ADMA) { + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) + pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + } - probe_ent->private_data = hpriv; - hpriv->type = type; + /* request and iomap NV_MMIO_BAR */ + rc = pcim_iomap_regions(pdev, 1 << NV_MMIO_BAR, DRV_NAME); + if (rc) + return rc; - base = probe_ent->iomap[NV_MMIO_BAR]; - probe_ent->port[0].scr_addr = base + NV_PORT0_SCR_REG_OFFSET; - probe_ent->port[1].scr_addr = base + NV_PORT1_SCR_REG_OFFSET; + /* configure SCR access */ + base = host->iomap[NV_MMIO_BAR]; + host->ports[0]->ioaddr.scr_addr = base + NV_PORT0_SCR_REG_OFFSET; + host->ports[1]->ioaddr.scr_addr = base + NV_PORT1_SCR_REG_OFFSET; /* enable SATA space for CK804 */ if (type >= CK804) { @@ -1550,20 +1544,16 @@ static int nv_init_one (struct pci_dev * pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); } - pci_set_master(pdev); - + /* init ADMA */ if (type == ADMA) { - rc = nv_adma_host_init(probe_ent); + rc = nv_adma_host_init(host); if (rc) return rc; } - rc = ata_device_add(probe_ent); - if (rc != NV_PORTS) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, ppi[0]->irq_handler, + IRQF_SHARED, ppi[0]->sht); } static void nv_remove_one (struct pci_dev *pdev) diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 2339813..f56549b 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -45,10 +45,11 @@ #include #include "sata_promise.h" #define DRV_NAME "sata_promise" -#define DRV_VERSION "2.00" +#define DRV_VERSION "2.05" enum { + PDC_MAX_PORTS = 4, PDC_MMIO_BAR = 3, /* register offsets */ @@ -70,14 +71,31 @@ enum { PDC_TBG_MODE = 0x41C, /* TBG mode (not SATAII) */ PDC_SLEW_CTL = 0x470, /* slew rate control reg (not SATAII) */ - PDC_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) | - (1<<8) | (1<<9) | (1<<10), + /* PDC_GLOBAL_CTL bit definitions */ + PDC_PH_ERR = (1 << 8), /* PCI error while loading packet */ + PDC_SH_ERR = (1 << 9), /* PCI error while loading S/G table */ + PDC_DH_ERR = (1 << 10), /* PCI error while loading data */ + PDC2_HTO_ERR = (1 << 12), /* host bus timeout */ + PDC2_ATA_HBA_ERR = (1 << 13), /* error during SATA DATA FIS transmission */ + PDC2_ATA_DMA_CNT_ERR = (1 << 14), /* DMA DATA FIS size differs from S/G count */ + PDC_OVERRUN_ERR = (1 << 19), /* S/G byte count larger than HD requires */ + PDC_UNDERRUN_ERR = (1 << 20), /* S/G byte count less than HD requires */ + PDC_DRIVE_ERR = (1 << 21), /* drive error */ + PDC_PCI_SYS_ERR = (1 << 22), /* PCI system error */ + PDC1_PCI_PARITY_ERR = (1 << 23), /* PCI parity error (from SATA150 driver) */ + PDC1_ERR_MASK = PDC1_PCI_PARITY_ERR, + PDC2_ERR_MASK = PDC2_HTO_ERR | PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR, + PDC_ERR_MASK = (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC_OVERRUN_ERR + | PDC_UNDERRUN_ERR | PDC_DRIVE_ERR | PDC_PCI_SYS_ERR + | PDC1_ERR_MASK | PDC2_ERR_MASK), board_2037x = 0, /* FastTrak S150 TX2plus */ - board_20319 = 1, /* FastTrak S150 TX4 */ - board_20619 = 2, /* FastTrak TX4000 */ - board_2057x = 3, /* SATAII150 Tx2plus */ - board_40518 = 4, /* SATAII150 Tx4 */ + board_2037x_pata = 1, /* FastTrak S150 TX2plus PATA port */ + board_20319 = 2, /* FastTrak S150 TX4 */ + board_20619 = 3, /* FastTrak TX4000 */ + board_2057x = 4, /* SATAII150 Tx2plus */ + board_2057x_pata = 5, /* SATAII150 Tx2plus */ + board_40518 = 6, /* SATAII150 Tx4 */ PDC_HAS_PATA = (1 << 1), /* PDC20375/20575 has PATA */ @@ -100,8 +118,10 @@ enum { ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, - /* hp->flags bits */ - PDC_FLAG_GEN_II = (1 << 0), + /* ap->flags bits */ + PDC_FLAG_GEN_II = (1 << 24), + PDC_FLAG_SATA_PATA = (1 << 25), /* supports SATA + PATA */ + PDC_FLAG_4_PORTS = (1 << 26), /* 4 ports */ }; @@ -110,28 +130,25 @@ struct pdc_port_priv { dma_addr_t pkt_dma; }; -struct pdc_host_priv { - unsigned long flags; - unsigned long port_flags[ATA_MAX_PORTS]; -}; - static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg); static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t pdc_interrupt (int irq, void *dev_instance); -static int pdc_port_start(struct ata_port *ap); +static int pdc_common_port_start(struct ata_port *ap); +static int pdc_sata_port_start(struct ata_port *ap); static void pdc_qc_prep(struct ata_queued_cmd *qc); static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static int pdc_check_atapi_dma(struct ata_queued_cmd *qc); -static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc); +static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc); static void pdc_irq_clear(struct ata_port *ap); static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc); static void pdc_freeze(struct ata_port *ap); static void pdc_thaw(struct ata_port *ap); -static void pdc_error_handler(struct ata_port *ap); +static void pdc_pata_error_handler(struct ata_port *ap); +static void pdc_sata_error_handler(struct ata_port *ap); static void pdc_post_internal_cmd(struct ata_queued_cmd *qc); - +static int pdc_pata_cable_detect(struct ata_port *ap); +static int pdc_sata_cable_detect(struct ata_port *ap); static struct scsi_host_template pdc_ata_sht = { .module = THIS_MODULE, @@ -164,17 +181,17 @@ static const struct ata_port_operations .qc_issue = pdc_qc_issue_prot, .freeze = pdc_freeze, .thaw = pdc_thaw, - .error_handler = pdc_error_handler, + .error_handler = pdc_sata_error_handler, .post_internal_cmd = pdc_post_internal_cmd, + .cable_detect = pdc_sata_cable_detect, .data_xfer = ata_data_xfer, - .irq_handler = pdc_interrupt, .irq_clear = pdc_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, .scr_read = pdc_sata_scr_read, .scr_write = pdc_sata_scr_write, - .port_start = pdc_port_start, + .port_start = pdc_sata_port_start, }; /* First-generation chips need a more restrictive ->check_atapi_dma op */ @@ -185,23 +202,23 @@ static const struct ata_port_operations .check_status = ata_check_status, .exec_command = pdc_exec_command_mmio, .dev_select = ata_std_dev_select, - .check_atapi_dma = pdc_old_check_atapi_dma, + .check_atapi_dma = pdc_old_sata_check_atapi_dma, .qc_prep = pdc_qc_prep, .qc_issue = pdc_qc_issue_prot, .freeze = pdc_freeze, .thaw = pdc_thaw, - .error_handler = pdc_error_handler, + .error_handler = pdc_sata_error_handler, .post_internal_cmd = pdc_post_internal_cmd, + .cable_detect = pdc_sata_cable_detect, .data_xfer = ata_data_xfer, - .irq_handler = pdc_interrupt, .irq_clear = pdc_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, .scr_read = pdc_sata_scr_read, .scr_write = pdc_sata_scr_write, - .port_start = pdc_port_start, + .port_start = pdc_sata_port_start, }; static const struct ata_port_operations pdc_pata_ops = { @@ -217,32 +234,41 @@ static const struct ata_port_operations .qc_issue = pdc_qc_issue_prot, .freeze = pdc_freeze, .thaw = pdc_thaw, - .error_handler = pdc_error_handler, + .error_handler = pdc_pata_error_handler, .post_internal_cmd = pdc_post_internal_cmd, + .cable_detect = pdc_pata_cable_detect, .data_xfer = ata_data_xfer, - .irq_handler = pdc_interrupt, .irq_clear = pdc_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, - .port_start = pdc_port_start, + .port_start = pdc_common_port_start, }; static const struct ata_port_info pdc_port_info[] = { /* board_2037x */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_SATA_PATA, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &pdc_old_sata_ops, }, + /* board_2037x_pata */ + { + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &pdc_pata_ops, + }, + /* board_20319 */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_4_PORTS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ @@ -251,8 +277,8 @@ static const struct ata_port_info pdc_po /* board_20619 */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS | + PDC_FLAG_4_PORTS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ @@ -261,18 +287,28 @@ static const struct ata_port_info pdc_po /* board_2057x */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_GEN_II | PDC_FLAG_SATA_PATA, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &pdc_sata_ops, }, + /* board_2057x_pata */ + { + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS, + PDC_FLAG_GEN_II, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &pdc_pata_ops, + }, + /* board_40518 */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_GEN_II | PDC_FLAG_4_PORTS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ @@ -313,18 +349,12 @@ static struct pci_driver pdc_ata_pci_dri }; -static int pdc_port_start(struct ata_port *ap) +static int pdc_common_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; - struct pdc_host_priv *hp = ap->host->private_data; struct pdc_port_priv *pp; int rc; - /* fix up port flags and cable type for SATA+PATA chips */ - ap->flags |= hp->port_flags[ap->port_no]; - if (ap->flags & ATA_FLAG_SATA) - ap->cbl = ATA_CBL_SATA; - rc = ata_port_start(ap); if (rc) return rc; @@ -339,8 +369,19 @@ static int pdc_port_start(struct ata_por ap->private_data = pp; + return 0; +} + +static int pdc_sata_port_start(struct ata_port *ap) +{ + int rc; + + rc = pdc_common_port_start(ap); + if (rc) + return rc; + /* fix up PHYMODE4 align timing */ - if ((hp->flags & PDC_FLAG_GEN_II) && sata_scr_valid(ap)) { + if (ap->flags & PDC_FLAG_GEN_II) { void __iomem *mmio = (void __iomem *) ap->ioaddr.scr_addr; unsigned int tmp; @@ -374,23 +415,25 @@ static void pdc_reset_port(struct ata_po readl(mmio); /* flush */ } -static void pdc_pata_cbl_detect(struct ata_port *ap) +static int pdc_pata_cable_detect(struct ata_port *ap) { u8 tmp; void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_CTLSTAT + 0x03; tmp = readb(mmio); + if (tmp & 0x01) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} - if (tmp & 0x01) { - ap->cbl = ATA_CBL_PATA40; - ap->udma_mask &= ATA_UDMA_MASK_40C; - } else - ap->cbl = ATA_CBL_PATA80; +static int pdc_sata_cable_detect(struct ata_port *ap) +{ + return ATA_CBL_SATA; } static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) { - if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA) + if (sc_reg > SCR_CONTROL) return 0xffffffffU; return readl(ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -399,7 +442,7 @@ static u32 pdc_sata_scr_read (struct ata static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) { - if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA) + if (sc_reg > SCR_CONTROL) return; writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -555,52 +598,79 @@ static void pdc_thaw(struct ata_port *ap readl(mmio + PDC_CTLSTAT); /* flush */ } -static int pdc_pre_reset(struct ata_port *ap) -{ - if (!sata_scr_valid(ap)) - pdc_pata_cbl_detect(ap); - return ata_std_prereset(ap); -} - -static void pdc_error_handler(struct ata_port *ap) +static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset) { - ata_reset_fn_t hardreset; - if (!(ap->pflags & ATA_PFLAG_FROZEN)) pdc_reset_port(ap); - hardreset = NULL; - if (sata_scr_valid(ap)) - hardreset = sata_std_hardreset; - /* perform recovery */ - ata_do_eh(ap, pdc_pre_reset, ata_std_softreset, hardreset, + ata_do_eh(ap, ata_std_prereset, ata_std_softreset, hardreset, ata_std_postreset); } +static void pdc_pata_error_handler(struct ata_port *ap) +{ + pdc_common_error_handler(ap, NULL); +} + +static void pdc_sata_error_handler(struct ata_port *ap) +{ + pdc_common_error_handler(ap, sata_std_hardreset); +} + static void pdc_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - /* make DMA engine forget about the failed command */ - if (qc->err_mask) + if (qc->flags & ATA_QCFLAG_FAILED) pdc_reset_port(ap); } +static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc, + u32 port_status, u32 err_mask) +{ + struct ata_eh_info *ehi = &ap->eh_info; + unsigned int ac_err_mask = 0; + + ata_ehi_clear_desc(ehi); + ata_ehi_push_desc(ehi, "port_status 0x%08x", port_status); + port_status &= err_mask; + + if (port_status & PDC_DRIVE_ERR) + ac_err_mask |= AC_ERR_DEV; + if (port_status & (PDC_OVERRUN_ERR | PDC_UNDERRUN_ERR)) + ac_err_mask |= AC_ERR_HSM; + if (port_status & (PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR)) + ac_err_mask |= AC_ERR_ATA_BUS; + if (port_status & (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC2_HTO_ERR + | PDC_PCI_SYS_ERR | PDC1_PCI_PARITY_ERR)) + ac_err_mask |= AC_ERR_HOST_BUS; + + if (sata_scr_valid(ap)) + ehi->serror |= pdc_sata_scr_read(ap, SCR_ERROR); + + qc->err_mask |= ac_err_mask; + + pdc_reset_port(ap); +} + static inline unsigned int pdc_host_intr( struct ata_port *ap, struct ata_queued_cmd *qc) { unsigned int handled = 0; - u32 tmp; - void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL; + void __iomem *port_mmio = ap->ioaddr.cmd_addr; + u32 port_status, err_mask; - tmp = readl(mmio); - if (tmp & PDC_ERR_MASK) { - qc->err_mask |= AC_ERR_DEV; - pdc_reset_port(ap); + err_mask = PDC_ERR_MASK; + if (ap->flags & PDC_FLAG_GEN_II) + err_mask &= ~PDC1_ERR_MASK; + else + err_mask &= ~PDC2_ERR_MASK; + port_status = readl(port_mmio + PDC_GLOBAL_CTL); + if (unlikely(port_status & err_mask)) { + pdc_error_intr(ap, qc, port_status, err_mask); + return 1; } switch (qc->tf.protocol) { @@ -767,44 +837,40 @@ static int pdc_check_atapi_dma(struct at return pio; } -static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc) +static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc) { - struct ata_port *ap = qc->ap; - /* First generation chips cannot use ATAPI DMA on SATA ports */ - if (sata_scr_valid(ap)) - return 1; - return pdc_check_atapi_dma(qc); + return 1; } -static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base, - void __iomem *scr_addr) +static void pdc_ata_setup_port(struct ata_port *ap, + void __iomem *base, void __iomem *scr_addr) { - port->cmd_addr = base; - port->data_addr = base; - port->feature_addr = - port->error_addr = base + 0x4; - port->nsect_addr = base + 0x8; - port->lbal_addr = base + 0xc; - port->lbam_addr = base + 0x10; - port->lbah_addr = base + 0x14; - port->device_addr = base + 0x18; - port->command_addr = - port->status_addr = base + 0x1c; - port->altstatus_addr = - port->ctl_addr = base + 0x38; - port->scr_addr = scr_addr; + ap->ioaddr.cmd_addr = base; + ap->ioaddr.data_addr = base; + ap->ioaddr.feature_addr = + ap->ioaddr.error_addr = base + 0x4; + ap->ioaddr.nsect_addr = base + 0x8; + ap->ioaddr.lbal_addr = base + 0xc; + ap->ioaddr.lbam_addr = base + 0x10; + ap->ioaddr.lbah_addr = base + 0x14; + ap->ioaddr.device_addr = base + 0x18; + ap->ioaddr.command_addr = + ap->ioaddr.status_addr = base + 0x1c; + ap->ioaddr.altstatus_addr = + ap->ioaddr.ctl_addr = base + 0x38; + ap->ioaddr.scr_addr = scr_addr; } -static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) +static void pdc_host_init(struct ata_host *host) { - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; - struct pdc_host_priv *hp = pe->private_data; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; + int is_gen2 = host->ports[0]->flags & PDC_FLAG_GEN_II; int hotplug_offset; u32 tmp; - if (hp->flags & PDC_FLAG_GEN_II) + if (is_gen2) hotplug_offset = PDC2_SATA_PLUG_CSR; else hotplug_offset = PDC_SATA_PLUG_CSR; @@ -818,7 +884,7 @@ static void pdc_host_init(unsigned int c /* enable BMR_BURST, maybe change FIFO_SHD to 8 dwords */ tmp = readl(mmio + PDC_FLASH_CTL); tmp |= 0x02000; /* bit 13 (enable bmr burst) */ - if (!(hp->flags & PDC_FLAG_GEN_II)) + if (!is_gen2) tmp |= 0x10000; /* bit 16 (fifo threshold at 8 dw) */ writel(tmp, mmio + PDC_FLASH_CTL); @@ -831,7 +897,7 @@ static void pdc_host_init(unsigned int c writel(tmp | 0xff0000, mmio + hotplug_offset); /* don't initialise TBG or SLEW on 2nd generation chips */ - if (hp->flags & PDC_FLAG_GEN_II) + if (is_gen2) return; /* reduce TBG clock to 133 Mhz. */ @@ -853,16 +919,16 @@ static void pdc_host_init(unsigned int c static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; - struct pdc_host_priv *hp; + const struct ata_port_info *pi = &pdc_port_info[ent->driver_data]; + const struct ata_port_info *ppi[PDC_MAX_PORTS]; + struct ata_host *host; void __iomem *base; - unsigned int board_idx = (unsigned int) ent->driver_data; - int rc; - u8 tmp; + int n_ports, i, rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* enable and acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -872,89 +938,49 @@ static int pdc_ata_init_one (struct pci_ pcim_pin_device(pdev); if (rc) return rc; + base = pcim_iomap_table(pdev)[PDC_MMIO_BAR]; - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; + /* determine port configuration and setup host */ + n_ports = 2; + if (pi->flags & PDC_FLAG_4_PORTS) + n_ports = 4; + for (i = 0; i < n_ports; i++) + ppi[i] = pi; - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); + if (pi->flags & PDC_FLAG_SATA_PATA) { + u8 tmp = readb(base + PDC_FLASH_CTL+1); + if (!(tmp & 0x80)) { + ppi[n_ports++] = pi + 1; + dev_printk(KERN_INFO, &pdev->dev, "PATA port found\n"); + } + } - hp = devm_kzalloc(&pdev->dev, sizeof(*hp), GFP_KERNEL); - if (hp == NULL) + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n"); return -ENOMEM; - - probe_ent->private_data = hp; - - probe_ent->sht = pdc_port_info[board_idx].sht; - probe_ent->port_flags = pdc_port_info[board_idx].flags; - probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - - base = probe_ent->iomap[PDC_MMIO_BAR]; - - pdc_ata_setup_port(&probe_ent->port[0], base + 0x200, base + 0x400); - pdc_ata_setup_port(&probe_ent->port[1], base + 0x280, base + 0x500); - - /* notice 4-port boards */ - switch (board_idx) { - case board_40518: - hp->flags |= PDC_FLAG_GEN_II; - /* Fall through */ - case board_20319: - probe_ent->n_ports = 4; - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, base + 0x600); - pdc_ata_setup_port(&probe_ent->port[3], base + 0x380, base + 0x700); - break; - case board_2057x: - hp->flags |= PDC_FLAG_GEN_II; - /* Fall through */ - case board_2037x: - /* TX2plus boards also have a PATA port */ - tmp = readb(base + PDC_FLASH_CTL+1); - if (!(tmp & 0x80)) { - probe_ent->n_ports = 3; - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, NULL); - hp->port_flags[2] = ATA_FLAG_SLAVE_POSS; - printk(KERN_INFO DRV_NAME " PATA port found\n"); - } else - probe_ent->n_ports = 2; - hp->port_flags[0] = ATA_FLAG_SATA; - hp->port_flags[1] = ATA_FLAG_SATA; - break; - case board_20619: - probe_ent->n_ports = 4; - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, NULL); - pdc_ata_setup_port(&probe_ent->port[3], base + 0x380, NULL); - break; - default: - BUG(); - break; } + host->iomap = pcim_iomap_table(pdev); - pci_set_master(pdev); + for (i = 0; i < host->n_ports; i++) + pdc_ata_setup_port(host->ports[i], + base + 0x200 + i * 0x80, + base + 0x400 + i * 0x100); /* initialize adapter */ - pdc_host_init(board_idx, probe_ent); + pdc_host_init(host); - if (!ata_device_add(probe_ent)) - return -ENODEV; + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; - devm_kfree(&pdev->dev, probe_ent); - return 0; + /* start host, request IRQ and attach */ + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, pdc_interrupt, IRQF_SHARED, + &pdc_ata_sht); } diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 8786b45..f5a05de 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -114,7 +114,6 @@ struct qs_port_priv { static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg); static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t qs_intr (int irq, void *dev_instance); static int qs_port_start(struct ata_port *ap); static void qs_host_stop(struct ata_host *host); static void qs_phy_reset(struct ata_port *ap); @@ -158,7 +157,6 @@ static const struct ata_port_operations .qc_issue = qs_qc_issue, .data_xfer = ata_data_xfer, .eng_timeout = qs_eng_timeout, - .irq_handler = qs_intr, .irq_clear = qs_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -173,7 +171,6 @@ static const struct ata_port_operations static const struct ata_port_info qs_port_info[] = { /* board_2068_idx */ { - .sht = &qs_ata_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | //FIXME ATA_FLAG_SRST | @@ -530,16 +527,16 @@ static void qs_host_stop(struct ata_host writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ } -static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe) +static void qs_host_init(struct ata_host *host, unsigned int chip_id) { - void __iomem *mmio_base = pe->iomap[QS_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[QS_MMIO_BAR]; unsigned int port_no; writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */ writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ /* reset each channel in turn */ - for (port_no = 0; port_no < pe->n_ports; ++port_no) { + for (port_no = 0; port_no < host->n_ports; ++port_no) { u8 __iomem *chan = mmio_base + (port_no * 0x4000); writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1); writeb(QS_CTR0_REG, chan + QS_CCT_CTR0); @@ -547,7 +544,7 @@ static void qs_host_init(unsigned int ch } writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */ - for (port_no = 0; port_no < pe->n_ports; ++port_no) { + for (port_no = 0; port_no < host->n_ports; ++port_no) { u8 __iomem *chan = mmio_base + (port_no * 0x4000); /* set FIFO depths to same settings as Windows driver */ writew(32, chan + QS_CFC_HUFT); @@ -607,14 +604,20 @@ static int qs_ata_init_one(struct pci_de const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; - void __iomem * const *iomap; unsigned int board_idx = (unsigned int) ent->driver_data; + const struct ata_port_info *ppi[] = { &qs_port_info[board_idx], NULL }; + struct ata_host *host; int rc, port_no; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, QS_PORTS); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -625,47 +628,24 @@ static int qs_ata_init_one(struct pci_de rc = pcim_iomap_regions(pdev, 1 << QS_MMIO_BAR, DRV_NAME); if (rc) return rc; - iomap = pcim_iomap_table(pdev); + host->iomap = pcim_iomap_table(pdev); - rc = qs_set_dma_masks(pdev, iomap[QS_MMIO_BAR]); + rc = qs_set_dma_masks(pdev, host->iomap[QS_MMIO_BAR]); if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = qs_port_info[board_idx].sht; - probe_ent->port_flags = qs_port_info[board_idx].flags; - probe_ent->pio_mask = qs_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = qs_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = qs_port_info[board_idx].udma_mask; - probe_ent->port_ops = qs_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = iomap; - probe_ent->n_ports = QS_PORTS; - - for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { + for (port_no = 0; port_no < host->n_ports; ++port_no) { void __iomem *chan = - probe_ent->iomap[QS_MMIO_BAR] + (port_no * 0x4000); - qs_ata_setup_port(&probe_ent->port[port_no], chan); + host->iomap[QS_MMIO_BAR] + (port_no * 0x4000); + qs_ata_setup_port(&host->ports[port_no]->ioaddr, chan); } - pci_set_master(pdev); - /* initialize adapter */ - qs_host_init(board_idx, probe_ent); + qs_host_init(host, board_idx); - if (ata_device_add(probe_ent) != QS_PORTS) - return -EIO; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED, + &qs_ata_sht); } static int __init qs_ata_init(void) diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 917b7ea..0a1e417 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -46,7 +46,7 @@ #include #include #define DRV_NAME "sata_sil" -#define DRV_VERSION "2.1" +#define DRV_VERSION "2.2" enum { SIL_MMIO_BAR = 5, @@ -114,11 +114,10 @@ static int sil_init_one (struct pci_dev #ifdef CONFIG_PM static int sil_pci_device_resume(struct pci_dev *pdev); #endif -static void sil_dev_config(struct ata_port *ap, struct ata_device *dev); +static void sil_dev_config(struct ata_device *dev); static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); -static void sil_post_set_mode (struct ata_port *ap); -static irqreturn_t sil_interrupt(int irq, void *dev_instance); +static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed); static void sil_freeze(struct ata_port *ap); static void sil_thaw(struct ata_port *ap); @@ -197,7 +196,7 @@ static const struct ata_port_operations .check_status = ata_check_status, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, - .post_set_mode = sil_post_set_mode, + .set_mode = sil_set_mode, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, .bmdma_stop = ata_bmdma_stop, @@ -209,7 +208,6 @@ static const struct ata_port_operations .thaw = sil_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = sil_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -221,7 +219,6 @@ static const struct ata_port_operations static const struct ata_port_info sil_port_info[] = { /* sil_3112 */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -230,7 +227,6 @@ static const struct ata_port_info sil_po }, /* sil_3112_no_sata_irq */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE | SIL_FLAG_NO_SATA_IRQ, .pio_mask = 0x1f, /* pio0-4 */ @@ -240,7 +236,6 @@ static const struct ata_port_info sil_po }, /* sil_3512 */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -249,7 +244,6 @@ static const struct ata_port_info sil_po }, /* sil_3114 */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -297,7 +291,16 @@ static unsigned char sil_get_device_cach return cache_line; } -static void sil_post_set_mode (struct ata_port *ap) +/** + * sil_set_mode - wrap set_mode functions + * @ap: port to set up + * @r_failed: returned device when we fail + * + * Wrap the libata method for device setup as after the setup we need + * to inspect the results and do some configuration work + */ + +static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed) { struct ata_host *host = ap->host; struct ata_device *dev; @@ -305,6 +308,11 @@ static void sil_post_set_mode (struct at void __iomem *addr = mmio_base + sil_port[ap->port_no].xfer_mode; u32 tmp, dev_mode[2]; unsigned int i; + int rc; + + rc = ata_do_set_mode(ap, r_failed); + if (rc) + return rc; for (i = 0; i < 2; i++) { dev = &ap->device[i]; @@ -323,6 +331,7 @@ static void sil_post_set_mode (struct at tmp |= (dev_mode[1] << 4); writel(tmp, addr); readl(addr); /* flush */ + return 0; } static inline void __iomem *sil_scr_addr(struct ata_port *ap, unsigned int sc_reg) @@ -521,7 +530,6 @@ static void sil_thaw(struct ata_port *ap /** * sil_dev_config - Apply device/host-specific errata fixups - * @ap: Port containing device to be examined * @dev: Device to be examined * * After the IDENTIFY [PACKET] DEVICE step is complete, and a @@ -548,8 +556,9 @@ static void sil_thaw(struct ata_port *ap * appreciated. * - But then again UDMA5 is hardly anything to complain about */ -static void sil_dev_config(struct ata_port *ap, struct ata_device *dev) +static void sil_dev_config(struct ata_device *dev) { + struct ata_port *ap = dev->ap; int print_info = ap->eh_context.i.flags & ATA_EHI_PRINTINFO; unsigned int n, quirks = 0; unsigned char model_num[ATA_ID_PROD_LEN + 1]; @@ -583,10 +592,10 @@ static void sil_dev_config(struct ata_po } } -static void sil_init_controller(struct pci_dev *pdev, - int n_ports, unsigned long port_flags, - void __iomem *mmio_base) +static void sil_init_controller(struct ata_host *host) { + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio_base = host->iomap[SIL_MMIO_BAR]; u8 cls; u32 tmp; int i; @@ -596,7 +605,7 @@ static void sil_init_controller(struct p if (cls) { cls >>= 3; cls++; /* cls = (line_size/8)+1 */ - for (i = 0; i < n_ports; i++) + for (i = 0; i < host->n_ports; i++) writew(cls << 8 | cls, mmio_base + sil_port[i].fifo_cfg); } else @@ -604,10 +613,10 @@ static void sil_init_controller(struct p "cache line size not set. Driver may not function\n"); /* Apply R_ERR on DMA activate FIS errata workaround */ - if (port_flags & SIL_FLAG_RERR_ON_DMA_ACT) { + if (host->ports[0]->flags & SIL_FLAG_RERR_ON_DMA_ACT) { int cnt; - for (i = 0, cnt = 0; i < n_ports; i++) { + for (i = 0, cnt = 0; i < host->n_ports; i++) { tmp = readl(mmio_base + sil_port[i].sfis_cfg); if ((tmp & 0x3) != 0x01) continue; @@ -620,7 +629,7 @@ static void sil_init_controller(struct p } } - if (n_ports == 4) { + if (host->n_ports == 4) { /* flip the magic "make 4 ports work" bit */ tmp = readl(mmio_base + sil_port[2].bmdma); if ((tmp & SIL_INTR_STEERING) == 0) @@ -632,15 +641,26 @@ static void sil_init_controller(struct p static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; + int board_id = ent->driver_data; + const struct ata_port_info *ppi[] = { &sil_port_info[board_id], NULL }; + struct ata_host *host; void __iomem *mmio_base; - int rc; + int n_ports, rc; unsigned int i; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + n_ports = 2; + if (board_id == sil_3114) + n_ports = 4; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -650,6 +670,7 @@ static int sil_init_one (struct pci_dev pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) @@ -658,45 +679,25 @@ static int sil_init_one (struct pci_dev if (rc) return rc; - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; + mmio_base = host->iomap[SIL_MMIO_BAR]; - INIT_LIST_HEAD(&probe_ent->node); - probe_ent->dev = pci_dev_to_dev(pdev); - probe_ent->port_ops = sil_port_info[ent->driver_data].port_ops; - probe_ent->sht = sil_port_info[ent->driver_data].sht; - probe_ent->n_ports = (ent->driver_data == sil_3114) ? 4 : 2; - probe_ent->pio_mask = sil_port_info[ent->driver_data].pio_mask; - probe_ent->mwdma_mask = sil_port_info[ent->driver_data].mwdma_mask; - probe_ent->udma_mask = sil_port_info[ent->driver_data].udma_mask; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->port_flags = sil_port_info[ent->driver_data].flags; - - probe_ent->iomap = pcim_iomap_table(pdev); - - mmio_base = probe_ent->iomap[SIL_MMIO_BAR]; - - for (i = 0; i < probe_ent->n_ports; i++) { - probe_ent->port[i].cmd_addr = mmio_base + sil_port[i].tf; - probe_ent->port[i].altstatus_addr = - probe_ent->port[i].ctl_addr = mmio_base + sil_port[i].ctl; - probe_ent->port[i].bmdma_addr = mmio_base + sil_port[i].bmdma; - probe_ent->port[i].scr_addr = mmio_base + sil_port[i].scr; - ata_std_ports(&probe_ent->port[i]); + for (i = 0; i < host->n_ports; i++) { + struct ata_ioports *ioaddr = &host->ports[i]->ioaddr; + + ioaddr->cmd_addr = mmio_base + sil_port[i].tf; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = mmio_base + sil_port[i].ctl; + ioaddr->bmdma_addr = mmio_base + sil_port[i].bmdma; + ioaddr->scr_addr = mmio_base + sil_port[i].scr; + ata_std_ports(ioaddr); } - sil_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags, - mmio_base); + /* initialize and activate */ + sil_init_controller(host); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, sil_interrupt, IRQF_SHARED, + &sil_sht); } #ifdef CONFIG_PM @@ -709,8 +710,7 @@ static int sil_pci_device_resume(struct if (rc) return rc; - sil_init_controller(pdev, host->n_ports, host->ports[0]->flags, - host->iomap[SIL_MMIO_BAR]); + sil_init_controller(host); ata_host_resume(host); return 0; diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 5614df8..e6223ba 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -323,7 +323,7 @@ struct sil24_port_priv { struct ata_taskfile tf; /* Cached taskfile registers */ }; -static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev); +static void sil24_dev_config(struct ata_device *dev); static u8 sil24_check_status(struct ata_port *ap); static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg); static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); @@ -331,7 +331,6 @@ static void sil24_tf_read(struct ata_por static void sil24_qc_prep(struct ata_queued_cmd *qc); static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); static void sil24_irq_clear(struct ata_port *ap); -static irqreturn_t sil24_interrupt(int irq, void *dev_instance); static void sil24_freeze(struct ata_port *ap); static void sil24_thaw(struct ata_port *ap); static void sil24_error_handler(struct ata_port *ap); @@ -401,7 +400,6 @@ static const struct ata_port_operations .qc_prep = sil24_qc_prep, .qc_issue = sil24_qc_issue, - .irq_handler = sil24_interrupt, .irq_clear = sil24_irq_clear, .irq_on = ata_dummy_irq_on, .irq_ack = ata_dummy_irq_ack, @@ -424,10 +422,9 @@ static const struct ata_port_operations #define SIL24_NPORTS2FLAG(nports) ((((unsigned)(nports) - 1) & 0x3) << 30) #define SIL24_FLAG2NPORTS(flag) ((((flag) >> 30) & 0x3) + 1) -static struct ata_port_info sil24_port_info[] = { +static const struct ata_port_info sil24_port_info[] = { /* sil_3124 */ { - .sht = &sil24_sht, .flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) | SIL24_FLAG_PCIX_IRQ_WOC, .pio_mask = 0x1f, /* pio0-4 */ @@ -437,7 +434,6 @@ static struct ata_port_info sil24_port_i }, /* sil_3132 */ { - .sht = &sil24_sht, .flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2), .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -446,7 +442,6 @@ static struct ata_port_info sil24_port_i }, /* sil_3131/sil_3531 */ { - .sht = &sil24_sht, .flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1), .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -462,9 +457,9 @@ static int sil24_tag(int tag) return tag; } -static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev) +static void sil24_dev_config(struct ata_device *dev) { - void __iomem *port = ap->ioaddr.cmd_addr; + void __iomem *port = dev->ap->ioaddr.cmd_addr; if (dev->cdb_len == 16) writel(PORT_CS_CDB16, port + PORT_CTRL_STAT); @@ -924,11 +919,8 @@ static void sil24_post_internal_cmd(stru { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - /* make DMA engine forget about the failed command */ - if (qc->err_mask) + if (qc->flags & ATA_QCFLAG_FAILED) sil24_init_port(ap); } @@ -964,11 +956,10 @@ static int sil24_port_start(struct ata_p return 0; } -static void sil24_init_controller(struct pci_dev *pdev, int n_ports, - unsigned long port_flags, - void __iomem *host_base, - void __iomem *port_base) +static void sil24_init_controller(struct ata_host *host) { + void __iomem *host_base = host->iomap[SIL24_HOST_BAR]; + void __iomem *port_base = host->iomap[SIL24_PORT_BAR]; u32 tmp; int i; @@ -979,7 +970,7 @@ static void sil24_init_controller(struct writel(0, host_base + HOST_CTRL); /* init ports */ - for (i = 0; i < n_ports; i++) { + for (i = 0; i < host->n_ports; i++) { void __iomem *port = port_base + i * PORT_REGS_SIZE; /* Initial PHY setting */ @@ -993,12 +984,12 @@ static void sil24_init_controller(struct PORT_CS_PORT_RST, PORT_CS_PORT_RST, 10, 100); if (tmp & PORT_CS_PORT_RST) - dev_printk(KERN_ERR, &pdev->dev, + dev_printk(KERN_ERR, host->dev, "failed to clear port RST\n"); } /* Configure IRQ WoC */ - if (port_flags & SIL24_FLAG_PCIX_IRQ_WOC) + if (host->ports[0]->flags & SIL24_FLAG_PCIX_IRQ_WOC) writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); else writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); @@ -1026,18 +1017,17 @@ static void sil24_init_controller(struct static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; - struct device *dev = &pdev->dev; - unsigned int board_id = (unsigned int)ent->driver_data; - struct ata_port_info *pinfo = &sil24_port_info[board_id]; - struct ata_probe_ent *probe_ent; - void __iomem *host_base; - void __iomem *port_base; + struct ata_port_info pi = sil24_port_info[ent->driver_data]; + const struct ata_port_info *ppi[] = { &pi, NULL }; + void __iomem * const *iomap; + struct ata_host *host; int i, rc; u32 tmp; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -1047,33 +1037,36 @@ static int sil24_init_one(struct pci_dev DRV_NAME); if (rc) return rc; + iomap = pcim_iomap_table(pdev); - /* allocate & init probe_ent */ - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) - return -ENOMEM; + /* apply workaround for completion IRQ loss on PCI-X errata */ + if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) { + tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL); + if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL)) + dev_printk(KERN_INFO, &pdev->dev, + "Applying completion IRQ loss on PCI-X " + "errata fix\n"); + else + pi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; + } - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); + /* allocate and fill host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, + SIL24_FLAG2NPORTS(ppi[0]->flags)); + if (!host) + return -ENOMEM; + host->iomap = iomap; - probe_ent->sht = pinfo->sht; - probe_ent->port_flags = pinfo->flags; - probe_ent->pio_mask = pinfo->pio_mask; - probe_ent->mwdma_mask = pinfo->mwdma_mask; - probe_ent->udma_mask = pinfo->udma_mask; - probe_ent->port_ops = pinfo->port_ops; - probe_ent->n_ports = SIL24_FLAG2NPORTS(pinfo->flags); + for (i = 0; i < host->n_ports; i++) { + void __iomem *port = iomap[SIL24_PORT_BAR] + i * PORT_REGS_SIZE; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); + host->ports[i]->ioaddr.cmd_addr = port; + host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL; - host_base = probe_ent->iomap[SIL24_HOST_BAR]; - port_base = probe_ent->iomap[SIL24_PORT_BAR]; + ata_std_ports(&host->ports[i]->ioaddr); + } - /* - * Configure the device - */ + /* configure and activate the device */ if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (rc) { @@ -1099,36 +1092,11 @@ static int sil24_init_one(struct pci_dev } } - /* Apply workaround for completion IRQ loss on PCI-X errata */ - if (probe_ent->port_flags & SIL24_FLAG_PCIX_IRQ_WOC) { - tmp = readl(host_base + HOST_CTRL); - if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL)) - dev_printk(KERN_INFO, &pdev->dev, - "Applying completion IRQ loss on PCI-X " - "errata fix\n"); - else - probe_ent->port_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; - } - - for (i = 0; i < probe_ent->n_ports; i++) { - void __iomem *port = port_base + i * PORT_REGS_SIZE; - - probe_ent->port[i].cmd_addr = port; - probe_ent->port[i].scr_addr = port + PORT_SCONTROL; - - ata_std_ports(&probe_ent->port[i]); - } - - sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags, - host_base, port_base); + sil24_init_controller(host); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED, + &sil24_sht); } #ifdef CONFIG_PM @@ -1136,7 +1104,6 @@ static int sil24_pci_device_resume(struc { struct ata_host *host = dev_get_drvdata(&pdev->dev); void __iomem *host_base = host->iomap[SIL24_HOST_BAR]; - void __iomem *port_base = host->iomap[SIL24_PORT_BAR]; int rc; rc = ata_pci_device_do_resume(pdev); @@ -1146,8 +1113,7 @@ static int sil24_pci_device_resume(struc if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) writel(HOST_CTRL_GLOBAL_RST, host_base + HOST_CTRL); - sil24_init_controller(pdev, host->n_ports, host->ports[0]->flags, - host_base, port_base); + sil24_init_controller(host); ata_host_resume(host); diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index a787f0d..d8ee062 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -121,7 +121,6 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -131,7 +130,6 @@ static const struct ata_port_operations }; static struct ata_port_info sis_port_info = { - .sht = &sis_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = 0x1f, .mwdma_mask = 0x7, @@ -256,12 +254,13 @@ static void sis_scr_write (struct ata_po static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - int rc; + struct ata_port_info pi = sis_port_info; + const struct ata_port_info *ppi[2] = { &pi, &pi }; + struct ata_host *host; u32 genctl, val; - struct ata_port_info pi = sis_port_info, *ppi[2] = { &pi, &pi }; u8 pmr; u8 port2_start = 0x20; + int rc; if (!printed_version++) dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); @@ -270,19 +269,6 @@ static int sis_init_one (struct pci_dev if (rc) return rc; - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - /* check and see if the SCRs are in IO space or PCI cfg space */ pci_read_config_dword(pdev, SIS_GENCTL, &genctl); if ((genctl & GENCTL_IOMAPPED_SCR) == 0) @@ -349,30 +335,26 @@ static int sis_init_one (struct pci_dev break; } - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return -ENOMEM; + rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host); + if (rc) + return rc; - if (!(probe_ent->port_flags & SIS_FLAG_CFGSCR)) { + if (!(pi.flags & SIS_FLAG_CFGSCR)) { void __iomem *mmio; - mmio = pcim_iomap(pdev, SIS_SCR_PCI_BAR, 0); - if (!mmio) - return -ENOMEM; + rc = pcim_iomap_regions(pdev, 1 << SIS_SCR_PCI_BAR, DRV_NAME); + if (rc) + return rc; + mmio = host->iomap[SIS_SCR_PCI_BAR]; - probe_ent->port[0].scr_addr = mmio; - probe_ent->port[1].scr_addr = mmio + port2_start; + host->ports[0]->ioaddr.scr_addr = mmio; + host->ports[1]->ioaddr.scr_addr = mmio + port2_start; } pci_set_master(pdev); pci_intx(pdev, 1); - - if (!ata_device_add(probe_ent)) - return -EIO; - - devm_kfree(&pdev->dev, probe_ent); - return 0; - + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &sis_sht); } static int __init sis_init(void) diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index b121195..cc07aac 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -56,7 +56,9 @@ #define DRV_NAME "sata_svw" #define DRV_VERSION "2.1" enum { - K2_FLAG_NO_ATAPI_DMA = (1 << 29), + /* ap->flags bits */ + K2_FLAG_SATA_8_PORTS = (1 << 24), + K2_FLAG_NO_ATAPI_DMA = (1 << 25), /* Taskfile registers offsets */ K2_SATA_TF_CMD_OFFSET = 0x00, @@ -90,17 +92,6 @@ enum { board_svw8 = 1, }; -static const struct k2_board_info { - unsigned int n_ports; - unsigned long port_flags; -} k2_board_info[] = { - /* board_svw4 */ - { 4, K2_FLAG_NO_ATAPI_DMA }, - - /* board_svw8 */ - { 8, K2_FLAG_NO_ATAPI_DMA }, -}; - static u8 k2_stat_check_status(struct ata_port *ap); @@ -354,7 +345,6 @@ static const struct ata_port_operations .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -363,6 +353,28 @@ static const struct ata_port_operations .port_start = ata_port_start, }; +static const struct ata_port_info k2_port_info[] = { + /* board_svw4 */ + { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &k2_sata_ops, + }, + /* board_svw8 */ + { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA | + K2_FLAG_SATA_8_PORTS, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &k2_sata_ops, + }, +}; + static void k2_sata_setup_port(struct ata_ioports *port, void __iomem *base) { port->cmd_addr = base + K2_SATA_TF_CMD_OFFSET; @@ -386,17 +398,24 @@ static void k2_sata_setup_port(struct at static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = + { &k2_port_info[ent->driver_data], NULL }; + struct ata_host *host; void __iomem *mmio_base; - const struct k2_board_info *board_info = - &k2_board_info[ent->driver_data]; - int rc; - int i; + int n_ports, i, rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + n_ports = 4; + if (ppi[0]->flags & K2_FLAG_SATA_8_PORTS) + n_ports = 8; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + if (!host) + return -ENOMEM; + /* * If this driver happens to only be useful on Apple's K2, then * we should check that here as it has a normal Serverworks ID @@ -404,6 +423,7 @@ static int k2_sata_init_one (struct pci_ rc = pcim_enable_device(pdev); if (rc) return rc; + /* * Check if we have resources mapped at all (second function may * have been disabled by firmware) @@ -417,6 +437,15 @@ static int k2_sata_init_one (struct pci_ pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); + mmio_base = host->iomap[5]; + + /* different controllers have different number of ports - currently 4 or 8 */ + /* All ports are on the same function. Multi-function device is no + * longer available. This should not be seen in any system. */ + for (i = 0; i < host->n_ports; i++) + k2_sata_setup_port(&host->ports[i]->ioaddr, + mmio_base + i * K2_SATA_PORT_OFFSET); rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) @@ -425,38 +454,6 @@ static int k2_sata_init_one (struct pci_ if (rc) return rc; - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = &k2_sata_sht; - probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | board_info->port_flags; - probe_ent->port_ops = &k2_sata_ops; - probe_ent->n_ports = 4; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - - /* We don't care much about the PIO/UDMA masks, but the core won't like us - * if we don't fill these - */ - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x7; - probe_ent->udma_mask = 0x7f; - - mmio_base = probe_ent->iomap[5]; - - /* different controllers have different number of ports - currently 4 or 8 */ - /* All ports are on the same function. Multi-function device is no - * longer available. This should not be seen in any system. */ - for (i = 0; i < board_info->n_ports; i++) - k2_sata_setup_port(&probe_ent->port[i], - mmio_base + i * K2_SATA_PORT_OFFSET); - /* Clear a magic bit in SCR1 according to Darwin, those help * some funky seagate drives (though so far, those were already * set by the firmware on the machines I had access to) @@ -469,12 +466,8 @@ static int k2_sata_init_one (struct pci_ writel(0x0, mmio_base + K2_SATA_SIM_OFFSET); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &k2_sata_sht); } /* 0x240 is device ID for Apple K2 device diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index 1a081c3..3a4f445 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -151,24 +151,23 @@ struct pdc_host_priv { static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance); static void pdc_eng_timeout(struct ata_port *ap); static void pdc_20621_phy_reset (struct ata_port *ap); static int pdc_port_start(struct ata_port *ap); static void pdc20621_qc_prep(struct ata_queued_cmd *qc); static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); -static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe); -static int pdc20621_detect_dimm(struct ata_probe_ent *pe); -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, +static unsigned int pdc20621_dimm_init(struct ata_host *host); +static int pdc20621_detect_dimm(struct ata_host *host); +static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device, u32 subaddr, u32 *pdata); -static int pdc20621_prog_dimm0(struct ata_probe_ent *pe); -static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe); +static int pdc20621_prog_dimm0(struct ata_host *host); +static unsigned int pdc20621_prog_dimm_global(struct ata_host *host); #ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, +static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, u32 offset, u32 size); #endif -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, +static void pdc20621_put_to_dimm(struct ata_host *host, void *psource, u32 offset, u32 size); static void pdc20621_irq_clear(struct ata_port *ap); static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc); @@ -204,7 +203,6 @@ static const struct ata_port_operations .qc_issue = pdc20621_qc_issue_prot, .data_xfer = ata_data_xfer, .eng_timeout = pdc_eng_timeout, - .irq_handler = pdc20621_interrupt, .irq_clear = pdc20621_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -214,7 +212,6 @@ static const struct ata_port_operations static const struct ata_port_info pdc_port_info[] = { /* board_20621 */ { - .sht = &pdc_sata_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST | ATA_FLAG_MMIO | ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, @@ -882,15 +879,15 @@ static void pdc_sata_setup_port(struct a #ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, +static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, u32 offset, u32 size) { u32 window_size; u16 idx; u8 page_mask; long dist; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; - void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; + void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -937,15 +934,15 @@ static void pdc20621_get_from_dimm(struc #endif -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, +static void pdc20621_put_to_dimm(struct ata_host *host, void *psource, u32 offset, u32 size) { u32 window_size; u16 idx; u8 page_mask; long dist; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; - void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; + void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -987,10 +984,10 @@ static void pdc20621_put_to_dimm(struct } -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, +static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device, u32 subaddr, u32 *pdata) { - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; u32 i2creg = 0; u32 status; u32 count =0; @@ -1023,17 +1020,17 @@ static unsigned int pdc20621_i2c_read(st } -static int pdc20621_detect_dimm(struct ata_probe_ent *pe) +static int pdc20621_detect_dimm(struct ata_host *host) { u32 data=0 ; - if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_SYSTEM_FREQ, &data)) { if (data == 100) return 100; } else return 0; - if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { + if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { if(data <= 0x75) return 133; } else @@ -1043,13 +1040,13 @@ static int pdc20621_detect_dimm(struct a } -static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) +static int pdc20621_prog_dimm0(struct ata_host *host) { u32 spd0[50]; u32 data = 0; int size, i; u8 bdimmsize; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; static const struct { unsigned int reg; unsigned int ofs; @@ -1072,7 +1069,7 @@ static int pdc20621_prog_dimm0(struct at mmio += PDC_CHIP0_OFS; for(i=0; iiomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1129,7 +1126,7 @@ static unsigned int pdc20621_prog_dimm_g readl(mmio + PDC_SDRAM_CONTROL_OFFSET); /* Turn on for ECC */ - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE, &spd0); if (spd0 == 0x02) { data |= (0x01 << 16); @@ -1156,7 +1153,7 @@ static unsigned int pdc20621_prog_dimm_g } -static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) +static unsigned int pdc20621_dimm_init(struct ata_host *host) { int speed, size, length; u32 addr,spd0,pci_status; @@ -1166,7 +1163,7 @@ static unsigned int pdc20621_dimm_init(s u32 ticks=0; u32 clock=0; u32 fparam=0; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1225,18 +1222,18 @@ static unsigned int pdc20621_dimm_init(s Read SPD of DIMM by I2C interface, and program the DIMM Module Controller. */ - if (!(speed = pdc20621_detect_dimm(pe))) { + if (!(speed = pdc20621_detect_dimm(host))) { printk(KERN_ERR "Detect Local DIMM Fail\n"); return 1; /* DIMM error */ } VPRINTK("Local DIMM Speed = %d\n", speed); /* Programming DIMM0 Module Control Register (index_CID0:80h) */ - size = pdc20621_prog_dimm0(pe); + size = pdc20621_prog_dimm0(host); VPRINTK("Local DIMM Size = %dMB\n",size); /* Programming DIMM Module Global Control Register (index_CID0:88h) */ - if (pdc20621_prog_dimm_global(pe)) { + if (pdc20621_prog_dimm_global(host)) { printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n"); return 1; } @@ -1249,20 +1246,20 @@ #ifdef ATA_VERBOSE_DEBUG '9','8','0','3','1','6','1','2',0,0}; u8 test_parttern2[40] = {0}; - pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x10040, 40); - pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x40, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x10040, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x40, 40); - pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x10040, 40); + pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40); printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040, + pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x10040, 40); printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); - pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x40, 40); + pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40); printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); } @@ -1270,14 +1267,14 @@ #endif /* ECC initiliazation. */ - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE, &spd0); if (spd0 == 0x02) { VPRINTK("Start ECC initialization\n"); addr = 0; length = size * 1024 * 1024; while (addr < length) { - pdc20621_put_to_dimm(pe, (void *) &tmp, addr, + pdc20621_put_to_dimm(host, (void *) &tmp, addr, sizeof(u32)); addr += sizeof(u32); } @@ -1287,10 +1284,10 @@ #endif } -static void pdc_20621_init(struct ata_probe_ent *pe) +static void pdc_20621_init(struct ata_host *host) { u32 tmp; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1321,15 +1318,25 @@ static void pdc_20621_init(struct ata_pr static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = + { &pdc_port_info[ent->driver_data], NULL }; + struct ata_host *host; void __iomem *base; struct pdc_host_priv *hpriv; - unsigned int board_idx = (unsigned int) ent->driver_data; int rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4); + hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); + if (!host || !hpriv) + return -ENOMEM; + + host->private_data = hpriv; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -1340,7 +1347,15 @@ static int pdc_sata_init_one (struct pci pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); + + base = host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS; + pdc_sata_setup_port(&host->ports[0]->ioaddr, base + 0x200); + pdc_sata_setup_port(&host->ports[1]->ioaddr, base + 0x280); + pdc_sata_setup_port(&host->ports[2]->ioaddr, base + 0x300); + pdc_sata_setup_port(&host->ports[3]->ioaddr, base + 0x380); + /* configure and activate */ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) return rc; @@ -1348,50 +1363,13 @@ static int pdc_sata_init_one (struct pci if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) + if (pdc20621_dimm_init(host)) return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - - probe_ent->sht = pdc_port_info[board_idx].sht; - probe_ent->port_flags = pdc_port_info[board_idx].flags; - probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - - probe_ent->private_data = hpriv; - base = probe_ent->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS; - - probe_ent->n_ports = 4; - pdc_sata_setup_port(&probe_ent->port[0], base + 0x200); - pdc_sata_setup_port(&probe_ent->port[1], base + 0x280); - pdc_sata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_sata_setup_port(&probe_ent->port[3], base + 0x380); + pdc_20621_init(host); pci_set_master(pdev); - - /* initialize adapter */ - /* initialize local dimm */ - if (pdc20621_dimm_init(probe_ent)) - return -ENOMEM; - pdc_20621_init(probe_ent); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, pdc20621_interrupt, + IRQF_SHARED, &pdc_sata_sht); } diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index d659ace..f74e383 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -115,7 +115,6 @@ static const struct ata_port_operations .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -127,7 +126,6 @@ static const struct ata_port_operations }; static struct ata_port_info uli_port_info = { - .sht = &uli_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_IGN_SIMPLEX, .pio_mask = 0x1f, /* pio0-4 */ @@ -185,12 +183,13 @@ static void uli_scr_write (struct ata_po static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; - struct ata_port_info *ppi[2]; - int rc; + const struct ata_port_info *ppi[] = { &uli_port_info, NULL }; unsigned int board_idx = (unsigned int) ent->driver_data; + struct ata_host *host; struct uli_priv *hpriv; void __iomem * const *iomap; + struct ata_ioports *ioaddr; + int n_ports, rc; if (!printed_version++) dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); @@ -199,54 +198,42 @@ static int uli_init_one (struct pci_dev if (rc) return rc; - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + n_ports = 2; + if (board_idx == uli_5287) + n_ports = 4; + rc = ata_pci_prepare_native_host(pdev, ppi, n_ports, &host); if (rc) return rc; - ppi[0] = ppi[1] = &uli_port_info; - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return -ENOMEM; - hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; + host->private_data = hpriv; - probe_ent->private_data = hpriv; - - iomap = pcim_iomap_table(pdev); + iomap = host->iomap; switch (board_idx) { case uli_5287: hpriv->scr_cfg_addr[0] = ULI5287_BASE; hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; - probe_ent->n_ports = 4; - probe_ent->port[2].cmd_addr = iomap[0] + 8; - probe_ent->port[2].altstatus_addr = - probe_ent->port[2].ctl_addr = (void __iomem *) + ioaddr = &host->ports[2]->ioaddr; + ioaddr->cmd_addr = iomap[0] + 8; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = (void __iomem *) ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS) + 4; - probe_ent->port[2].bmdma_addr = iomap[4] + 16; + ioaddr->bmdma_addr = iomap[4] + 16; hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4; + ata_std_ports(ioaddr); - probe_ent->port[3].cmd_addr = iomap[2] + 8; - probe_ent->port[3].altstatus_addr = - probe_ent->port[3].ctl_addr = (void __iomem *) + ioaddr = &host->ports[3]->ioaddr; + ioaddr->cmd_addr = iomap[2] + 8; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = (void __iomem *) ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS) + 4; - probe_ent->port[3].bmdma_addr = iomap[4] + 24; + ioaddr->bmdma_addr = iomap[4] + 24; hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5; - - ata_std_ports(&probe_ent->port[2]); - ata_std_ports(&probe_ent->port[3]); + ata_std_ports(ioaddr); break; case uli_5289: @@ -266,12 +253,8 @@ static int uli_init_one (struct pci_dev pci_set_master(pdev); pci_intx(pdev, 1); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &uli_sht); } static int __init uli_init(void) diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 598e6a2..1d855f5 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -64,8 +64,6 @@ enum { PORT0 = (1 << 1), PORT1 = (1 << 0), ALL_PORTS = PORT0 | PORT1, - PATA_PORT = 2, /* PATA is port 2 */ - N_PORTS = 3, NATIVE_MODE_ALL = (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4), @@ -78,11 +76,9 @@ static u32 svia_scr_read (struct ata_por static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void svia_noop_freeze(struct ata_port *ap); static void vt6420_error_handler(struct ata_port *ap); -static void vt6421_sata_error_handler(struct ata_port *ap); -static void vt6421_pata_error_handler(struct ata_port *ap); +static int vt6421_pata_cable_detect(struct ata_port *ap); static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev); static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev); -static int vt6421_port_start(struct ata_port *ap); static const struct pci_device_id svia_pci_tbl[] = { { PCI_VDEVICE(VIA, 0x5337), vt6420 }, @@ -141,7 +137,6 @@ static const struct ata_port_operations .error_handler = vt6420_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -172,15 +167,15 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = vt6421_pata_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = vt6421_pata_cable_detect, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, - .port_start = vt6421_port_start, + .port_start = ata_port_start, }; static const struct ata_port_operations vt6421_sata_ops = { @@ -203,10 +198,10 @@ static const struct ata_port_operations .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = vt6421_sata_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_sata, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -214,11 +209,10 @@ static const struct ata_port_operations .scr_read = svia_scr_read, .scr_write = svia_scr_write, - .port_start = vt6421_port_start, + .port_start = ata_port_start, }; -static struct ata_port_info vt6420_port_info = { - .sht = &svia_sht, +static const struct ata_port_info vt6420_port_info = { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = 0x1f, .mwdma_mask = 0x07, @@ -226,6 +220,22 @@ static struct ata_port_info vt6420_port_ .port_ops = &vt6420_sata_ops, }; +static struct ata_port_info vt6421_sport_info = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &vt6421_sata_ops, +}; + +static struct ata_port_info vt6421_pport_info = { + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY, + .pio_mask = 0x1f, + .mwdma_mask = 0, + .udma_mask = 0x7f, + .port_ops = &vt6421_pata_ops, +}; + MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers"); MODULE_LICENSE("GPL"); @@ -330,35 +340,15 @@ static void vt6420_error_handler(struct NULL, ata_std_postreset); } -static int vt6421_pata_prereset(struct ata_port *ap) +static int vt6421_pata_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 tmp; pci_read_config_byte(pdev, PATA_UDMA_TIMING, &tmp); if (tmp & 0x10) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - return 0; -} - -static void vt6421_pata_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, vt6421_pata_prereset, ata_std_softreset, - NULL, ata_std_postreset); -} - -static int vt6421_sata_prereset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_SATA; - return 0; -} - -static void vt6421_sata_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, vt6421_sata_prereset, ata_std_softreset, - NULL, ata_std_postreset); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev) @@ -375,16 +365,6 @@ static void vt6421_set_dma_mode(struct a pci_write_config_byte(pdev, PATA_UDMA_TIMING, udma_bits[adev->pio_mode - XFER_UDMA_0]); } -static int vt6421_port_start(struct ata_port *ap) -{ - if (ap->port_no == PATA_PORT) { - ap->ops = &vt6421_pata_ops; - ap->mwdma_mask = 0; - ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST; - } - return ata_port_start(ap); -} - static const unsigned int svia_bar_sizes[] = { 8, 4, 8, 4, 16, 256 }; @@ -403,79 +383,78 @@ static void __iomem * vt6421_scr_addr(vo return addr + (port * 64); } -static void vt6421_init_addrs(struct ata_probe_ent *probe_ent, - void __iomem * const *iomap, unsigned int port) +static void vt6421_init_addrs(struct ata_port *ap) { - void __iomem *reg_addr = iomap[port]; - void __iomem *bmdma_addr = iomap[4] + (port * 8); - - probe_ent->port[port].cmd_addr = reg_addr; - probe_ent->port[port].altstatus_addr = - probe_ent->port[port].ctl_addr = (void __iomem *) + void __iomem * const * iomap = ap->host->iomap; + void __iomem *reg_addr = iomap[ap->port_no]; + void __iomem *bmdma_addr = iomap[4] + (ap->port_no * 8); + struct ata_ioports *ioaddr = &ap->ioaddr; + + ioaddr->cmd_addr = reg_addr; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = (void __iomem *) ((unsigned long)(reg_addr + 8) | ATA_PCI_CTL_OFS); - probe_ent->port[port].bmdma_addr = bmdma_addr; - probe_ent->port[port].scr_addr = vt6421_scr_addr(iomap[5], port); + ioaddr->bmdma_addr = bmdma_addr; + ioaddr->scr_addr = vt6421_scr_addr(iomap[5], ap->port_no); - ata_std_ports(&probe_ent->port[port]); + ata_std_ports(ioaddr); } -static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev) +static int vt6420_prepare_host(struct pci_dev *pdev, struct ata_host **r_host) { - struct ata_probe_ent *probe_ent; - struct ata_port_info *ppi[2]; - void __iomem *bar5; + const struct ata_port_info *ppi[] = { &vt6420_port_info, NULL }; + struct ata_host *host; + int rc; - ppi[0] = ppi[1] = &vt6420_port_info; - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return NULL; + rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host); + if (rc) + return rc; + *r_host = host; - bar5 = pcim_iomap(pdev, 5, 0); - if (!bar5) { + rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME); + if (rc) { dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n"); - return NULL; + return rc; } - probe_ent->port[0].scr_addr = svia_scr_addr(bar5, 0); - probe_ent->port[1].scr_addr = svia_scr_addr(bar5, 1); + host->ports[0]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 0); + host->ports[1]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 1); - return probe_ent; + return 0; } -static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev) +static int vt6421_prepare_host(struct pci_dev *pdev, struct ata_host **r_host) { - struct ata_probe_ent *probe_ent; - unsigned int i; + const struct ata_port_info *ppi[] = + { &vt6421_sport_info, &vt6421_sport_info, &vt6421_pport_info }; + struct ata_host *host; + int i, rc; + + *r_host = host = ata_host_alloc_pinfo(&pdev->dev, ppi, ARRAY_SIZE(ppi)); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n"); + return -ENOMEM; + } - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) - return NULL; - - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = &svia_sht; - probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY; - probe_ent->port_ops = &vt6421_sata_ops; - probe_ent->n_ports = N_PORTS; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x07; - probe_ent->udma_mask = 0x7f; - - for (i = 0; i < 6; i++) - if (!pcim_iomap(pdev, i, 0)) { - dev_printk(KERN_ERR, &pdev->dev, - "failed to iomap PCI BAR %d\n", i); - return NULL; - } + rc = pcim_iomap_regions(pdev, 0x1f, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, "failed to request/iomap " + "PCI BARs (errno=%d)\n", rc); + return rc; + } + host->iomap = pcim_iomap_table(pdev); - for (i = 0; i < N_PORTS; i++) - vt6421_init_addrs(probe_ent, pcim_iomap_table(pdev), i); + for (i = 0; i < host->n_ports; i++) + vt6421_init_addrs(host->ports[i]); - return probe_ent; + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + + return 0; } static void svia_configure(struct pci_dev *pdev) @@ -522,7 +501,7 @@ static int svia_init_one (struct pci_dev static int printed_version; unsigned int i; int rc; - struct ata_probe_ent *probe_ent; + struct ata_host *host; int board_id = (int) ent->driver_data; const int *bar_sizes; u8 tmp8; @@ -534,12 +513,6 @@ static int svia_init_one (struct pci_dev if (rc) return rc; - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - if (board_id == vt6420) { pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8); if (tmp8 & SATA_2DEV) { @@ -565,32 +538,18 @@ static int svia_init_one (struct pci_dev return -ENODEV; } - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - if (board_id == vt6420) - probe_ent = vt6420_init_probe_ent(pdev); + rc = vt6420_prepare_host(pdev, &host); else - probe_ent = vt6421_init_probe_ent(pdev); - - if (!probe_ent) { - dev_printk(KERN_ERR, &pdev->dev, "out of memory\n"); - return -ENOMEM; - } + rc = vt6421_prepare_host(pdev, &host); + if (rc) + return rc; svia_configure(pdev); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &svia_sht); } static int __init svia_init(void) diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 170bad1..80126f8 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -333,7 +333,6 @@ static const struct ata_port_operations .thaw = vsc_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = vsc_sata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -367,30 +366,50 @@ static void __devinit vsc_sata_setup_por static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { + static const struct ata_port_info pi = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &vsc_sata_ops, + }; + const struct ata_port_info *ppi[] = { &pi, NULL }; static int printed_version; - struct ata_probe_ent *probe_ent; + struct ata_host *host; void __iomem *mmio_base; - int rc; + int i, rc; u8 cls; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4); + if (!host) + return -ENOMEM; + rc = pcim_enable_device(pdev); if (rc) return rc; - /* - * Check if we have needed resource mapped. - */ + /* check if we have needed resource mapped */ if (pci_resource_len(pdev, 0) == 0) return -ENODEV; + /* map IO regions and intialize host accordingly */ rc = pcim_iomap_regions(pdev, 1 << VSC_MMIO_BAR, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); + + mmio_base = host->iomap[VSC_MMIO_BAR]; + + for (i = 0; i < host->n_ports; i++) + vsc_sata_setup_port(&host->ports[i]->ioaddr, + mmio_base + (i + 1) * VSC_SATA_PORT_OFFSET); /* * Use 32 bit DMA mask, because 64 bit address support is poor. @@ -402,12 +421,6 @@ static int __devinit vsc_sata_init_one ( if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - /* * Due to a bug in the chip, the default cache line size can't be * used (unless the default is non-zero). @@ -418,33 +431,6 @@ static int __devinit vsc_sata_init_one ( if (pci_enable_msi(pdev) == 0) pci_intx(pdev, 0); - else - probe_ent->irq_flags = IRQF_SHARED; - - probe_ent->sht = &vsc_sata_sht; - probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO; - probe_ent->port_ops = &vsc_sata_ops; - probe_ent->n_ports = 4; - probe_ent->irq = pdev->irq; - probe_ent->iomap = pcim_iomap_table(pdev); - - /* We don't care much about the PIO/UDMA masks, but the core won't like us - * if we don't fill these - */ - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x07; - probe_ent->udma_mask = 0x7f; - - mmio_base = probe_ent->iomap[VSC_MMIO_BAR]; - - /* We have 4 ports per PCI function */ - vsc_sata_setup_port(&probe_ent->port[0], mmio_base + 1 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[1], mmio_base + 2 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[2], mmio_base + 3 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[3], mmio_base + 4 * VSC_SATA_PORT_OFFSET); - - pci_set_master(pdev); /* * Config offset 0x98 is "Extended Control and Status Register 0" @@ -454,11 +440,9 @@ static int __devinit vsc_sata_init_one ( */ pci_write_config_dword(pdev, 0x98, 0); - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, vsc_sata_interrupt, + IRQF_SHARED, &vsc_sata_sht); } static const struct pci_device_id vsc_sata_pci_tbl[] = { diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index 3c372e0..59651ab 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -821,7 +821,7 @@ static inline void fill_rx_pool (amb_dev } // cast needed as there is no %? for pointer differences PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li", - skb, skb->head, (long) (skb->end - skb->head)); + skb, skb->head, (long) (skb_end_pointer(skb) - skb->head)); rx.handle = virt_to_bus (skb); rx.host_address = cpu_to_be32 (virt_to_bus (skb->data)); if (rx_give (dev, &rx, pool)) diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c index fc518d8..02ad83d 100644 --- a/drivers/atm/atmtcp.c +++ b/drivers/atm/atmtcp.c @@ -221,7 +221,7 @@ static int atmtcp_v_send(struct atm_vcc hdr->vpi = htons(vcc->vpi); hdr->vci = htons(vcc->vci); hdr->length = htonl(skb->len); - memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); + skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); if (vcc->pop) vcc->pop(vcc,skb); else dev_kfree_skb(skb); out_vcc->push(out_vcc,new_skb); @@ -310,7 +310,7 @@ static int atmtcp_c_send(struct atm_vcc goto done; } __net_timestamp(new_skb); - memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); + skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); out_vcc->push(out_vcc,new_skb); atomic_inc(&vcc->stats->tx); atomic_inc(&out_vcc->stats->rx); @@ -352,7 +352,7 @@ static struct atm_dev atmtcp_control_dev .ops = &atmtcp_c_dev_ops, .type = "atmtcp", .number = 999, - .lock = SPIN_LOCK_UNLOCKED + .lock = __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock) }; diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index 8fccf01..0d3a38b 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -536,7 +536,7 @@ static int rx_aal0(struct atm_vcc *vcc) return 0; } skb_put(skb,length); - skb_set_timestamp(skb, &eni_vcc->timestamp); + skb->tstamp = eni_vcc->timestamp; DPRINTK("got len %ld\n",length); if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1; eni_vcc->rxing++; @@ -701,7 +701,7 @@ static void get_service(struct atm_dev * DPRINTK("Grr, servicing VCC %ld twice\n",vci); continue; } - do_gettimeofday(&ENI_VCC(vcc)->timestamp); + ENI_VCC(vcc)->timestamp = ktime_get_real(); ENI_VCC(vcc)->next = NULL; if (vcc->qos.rxtp.traffic_class == ATM_CBR) { if (eni_dev->fast) diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h index 385090c..d04fefb 100644 --- a/drivers/atm/eni.h +++ b/drivers/atm/eni.h @@ -59,7 +59,7 @@ struct eni_vcc { int rxing; /* number of pending PDUs */ int servicing; /* number of waiting VCs (0 or 1) */ int txing; /* number of pending TX bytes */ - struct timeval timestamp; /* for RX timing */ + ktime_t timestamp; /* for RX timing */ struct atm_vcc *next; /* next pending RX */ struct sk_buff *last; /* last PDU being DMAed (used to carry discard information) */ diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index a7c0ed3..405ee5e 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -1,6 +1,4 @@ /* - $Id: fore200e.c,v 1.5 2000/04/14 10:10:34 davem Exp $ - A FORE Systems 200E-series driver for ATM on Linux. Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003. @@ -1502,9 +1500,9 @@ fore200e_open(struct atm_vcc *vcc) /* pseudo-CBR bandwidth requested? */ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { - down(&fore200e->rate_sf); + mutex_lock(&fore200e->rate_mtx); if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) { - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); kfree(fore200e_vcc); vc_map->vcc = NULL; @@ -1513,7 +1511,7 @@ fore200e_open(struct atm_vcc *vcc) /* reserve bandwidth */ fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr; - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); } vcc->itf = vcc->dev->number; @@ -1599,9 +1597,9 @@ fore200e_close(struct atm_vcc* vcc) /* release reserved bandwidth, if any */ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { - down(&fore200e->rate_sf); + mutex_lock(&fore200e->rate_mtx); fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); clear_bit(ATM_VF_HASQOS, &vcc->flags); } @@ -2064,16 +2062,16 @@ fore200e_change_qos(struct atm_vcc* vcc, if ((qos->txtp.traffic_class == ATM_CBR) && (qos->txtp.max_pcr > 0)) { - down(&fore200e->rate_sf); + mutex_lock(&fore200e->rate_mtx); if (fore200e->available_cell_rate + vcc->qos.txtp.max_pcr < qos->txtp.max_pcr) { - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); return -EAGAIN; } fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; fore200e->available_cell_rate -= qos->txtp.max_pcr; - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); memcpy(&vcc->qos, qos, sizeof(struct atm_qos)); @@ -2459,7 +2457,7 @@ fore200e_initialize(struct fore200e* for DPRINTK(2, "device %s being initialized\n", fore200e->name); - init_MUTEX(&fore200e->rate_sf); + mutex_init(&fore200e->rate_mtx); spin_lock_init(&fore200e->q_lock); cpq = fore200e->cp_queues = fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET; diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h index f9abfda..b85a546 100644 --- a/drivers/atm/fore200e.h +++ b/drivers/atm/fore200e.h @@ -869,7 +869,7 @@ typedef struct fore200e { struct stats* stats; /* last snapshot of the stats */ - struct semaphore rate_sf; /* protects rate reservation ops */ + struct mutex rate_mtx; /* protects rate reservation ops */ spinlock_t q_lock; /* protects queue ops */ #ifdef FORE200E_USE_TASKLET struct tasklet_struct tx_tasklet; /* performs tx interrupt work */ diff --git a/drivers/atm/he.c b/drivers/atm/he.c index 8510026..d33aba6 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -1901,13 +1901,13 @@ #endif case ATM_AAL0: /* 2.10.1.5 raw cell receive */ skb->len = ATM_AAL0_SDU; - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); break; case ATM_AAL5: /* 2.10.1.2 aal5 receive */ skb->len = AAL5_LEN(skb->data, he_vcc->pdu_len); - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); #ifdef USE_CHECKSUM_HW if (vcc->vpi == 0 && vcc->vci >= ATM_NOT_RSV_VCI) { skb->ip_summed = CHECKSUM_COMPLETE; diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index b4b8014..057efbc 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -1065,7 +1065,8 @@ dequeue_rx(struct idt77252_dev *card, st vcc = vc->rx_vcc; pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); if ((vcc->qos.aal == ATM_AAL0) || (vcc->qos.aal == ATM_AAL34)) { @@ -1194,7 +1195,8 @@ dequeue_rx(struct idt77252_dev *card, st } pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); sb_pool_remove(card, skb); skb_trim(skb, len); @@ -1267,7 +1269,7 @@ idt77252_rx_raw(struct idt77252_dev *car tail = readl(SAR_REG_RAWCT); pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue), - queue->end - queue->head - 16, + skb_end_pointer(queue) - queue->head - 16, PCI_DMA_FROMDEVICE); while (head != tail) { @@ -1363,7 +1365,8 @@ drop: queue = card->raw_cell_head; pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue), - queue->end - queue->data, + (skb_end_pointer(queue) - + queue->data), PCI_DMA_FROMDEVICE); } else { card->raw_cell_head = NULL; @@ -1816,7 +1819,8 @@ push_rx_skb(struct idt77252_dev *card, s u32 handle; u32 addr; - skb->data = skb->tail = skb->head; + skb->data = skb->head; + skb_reset_tail_pointer(skb); skb->len = 0; skb_reserve(skb, 16); @@ -1835,7 +1839,6 @@ push_rx_skb(struct idt77252_dev *card, s skb_put(skb, SAR_FB_SIZE_3); break; default: - dev_kfree_skb(skb); return -1; } @@ -1874,7 +1877,7 @@ add_rx_skb(struct idt77252_dev *card, in } paddr = pci_map_single(card->pcidev, skb->data, - skb->end - skb->data, + skb_end_pointer(skb) - skb->data, PCI_DMA_FROMDEVICE); IDT77252_PRV_PADDR(skb) = paddr; @@ -1888,7 +1891,7 @@ add_rx_skb(struct idt77252_dev *card, in outunmap: pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, PCI_DMA_FROMDEVICE); handle = IDT77252_PRV_POOL(skb); card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL; @@ -1905,12 +1908,14 @@ recycle_rx_skb(struct idt77252_dev *card int err; pci_dma_sync_single_for_device(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); err = push_rx_skb(card, skb, POOL_QUEUE(handle)); if (err) { pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); sb_pool_remove(card, skb); dev_kfree_skb(skb); } @@ -3122,7 +3127,8 @@ deinit_card(struct idt77252_dev *card) if (skb) { pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, + (skb_end_pointer(skb) - + skb->data), PCI_DMA_FROMDEVICE); card->sbpool[i].skb[j] = NULL; dev_kfree_skb(skb); diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index aab9b37..14ced85 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -2208,7 +2208,7 @@ static void dequeue_rx(ns_dev *card, ns_ if (i == 1 && ns_rsqe_eopdu(rsqe)) *((u32 *) sb->data) |= 0x00000002; skb_put(sb, NS_AAL0_HEADER); - memcpy(sb->tail, cell, ATM_CELL_PAYLOAD); + memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD); skb_put(sb, ATM_CELL_PAYLOAD); ATM_SKB(sb)->vcc = vcc; __net_timestamp(sb); @@ -2252,7 +2252,8 @@ static void dequeue_rx(ns_dev *card, ns_ vc->rx_iov = iovb; NS_SKB(iovb)->iovcnt = 0; iovb->len = 0; - iovb->tail = iovb->data = iovb->head; + iovb->data = iovb->head; + skb_reset_tail_pointer(iovb); NS_SKB(iovb)->vcc = vcc; /* IMPORTANT: a pointer to the sk_buff containing the small or large buffer is stored as iovec base, NOT a pointer to the @@ -2265,7 +2266,8 @@ static void dequeue_rx(ns_dev *card, ns_ recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, NS_MAX_IOVECS); NS_SKB(iovb)->iovcnt = 0; iovb->len = 0; - iovb->tail = iovb->data = iovb->head; + iovb->data = iovb->head; + skb_reset_tail_pointer(iovb); NS_SKB(iovb)->vcc = vcc; } iov = &((struct iovec *) iovb->data)[NS_SKB(iovb)->iovcnt++]; @@ -2393,7 +2395,7 @@ #ifdef NS_USE_DESTRUCTORS skb->destructor = ns_lb_destructor; #endif /* NS_USE_DESTRUCTORS */ skb_push(skb, NS_SMBUFSIZE); - memcpy(skb->data, sb->data, NS_SMBUFSIZE); + skb_copy_from_linear_data(sb, skb->data, NS_SMBUFSIZE); skb_put(skb, len - NS_SMBUFSIZE); ATM_SKB(skb)->vcc = vcc; __net_timestamp(skb); @@ -2477,7 +2479,7 @@ #endif /* NS_USE_DESTRUCTORS */ { /* Copy the small buffer to the huge buffer */ sb = (struct sk_buff *) iov->iov_base; - memcpy(hb->data, sb->data, iov->iov_len); + skb_copy_from_linear_data(sb, hb->data, iov->iov_len); skb_put(hb, iov->iov_len); remaining = len - iov->iov_len; iov++; @@ -2489,7 +2491,7 @@ #endif /* NS_USE_DESTRUCTORS */ { lb = (struct sk_buff *) iov->iov_base; tocopy = min_t(int, remaining, iov->iov_len); - memcpy(hb->tail, lb->data, tocopy); + skb_copy_from_linear_data(lb, skb_tail_pointer(hb), tocopy); skb_put(hb, tocopy); iov++; remaining -= tocopy; diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 2222073..1ec0654 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(attribute_container_cl static struct list_head attribute_container_list; -static DECLARE_MUTEX(attribute_container_mutex); +static DEFINE_MUTEX(attribute_container_mutex); /** * attribute_container_register - register an attribute container @@ -77,9 +77,9 @@ attribute_container_register(struct attr klist_init(&cont->containers,internal_container_klist_get, internal_container_klist_put); - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_add_tail(&cont->node, &attribute_container_list); - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); return 0; } @@ -94,7 +94,7 @@ int attribute_container_unregister(struct attribute_container *cont) { int retval = -EBUSY; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); spin_lock(&cont->containers.k_lock); if (!list_empty(&cont->containers.k_list)) goto out; @@ -102,7 +102,7 @@ attribute_container_unregister(struct at list_del(&cont->node); out: spin_unlock(&cont->containers.k_lock); - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); return retval; } @@ -145,7 +145,7 @@ attribute_container_add_device(struct de { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { struct internal_container *ic; @@ -173,7 +173,7 @@ attribute_container_add_device(struct de attribute_container_add_class_device(&ic->classdev); klist_add_tail(&ic->node, &cont->containers); } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /* FIXME: can't break out of this unless klist_iter_exit is also @@ -211,7 +211,7 @@ attribute_container_remove_device(struct { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { struct internal_container *ic; struct klist_iter iter; @@ -234,7 +234,7 @@ attribute_container_remove_device(struct } } } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /** @@ -255,7 +255,7 @@ attribute_container_device_trigger(struc { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { struct internal_container *ic; struct klist_iter iter; @@ -273,7 +273,7 @@ attribute_container_device_trigger(struc fn(cont, dev, &ic->classdev); } } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /** @@ -295,12 +295,12 @@ attribute_container_trigger(struct devic { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { if (cont->match(cont, dev)) fn(cont, dev); } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /** diff --git a/drivers/base/base.h b/drivers/base/base.h index de7e144..d597f26 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -16,7 +16,7 @@ extern int cpu_dev_init(void); extern int attribute_container_init(void); extern int bus_add_device(struct device * dev); -extern int bus_attach_device(struct device * dev); +extern void bus_attach_device(struct device * dev); extern void bus_remove_device(struct device * dev); extern struct bus_type *get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 253868e..1d76e23 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -27,6 +27,9 @@ #define to_drv_attr(_attr) container_of( #define to_driver(obj) container_of(obj, struct device_driver, kobj) +static int __must_check bus_rescan_devices_helper(struct device *dev, + void *data); + static ssize_t drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { @@ -60,8 +63,19 @@ static struct sysfs_ops driver_sysfs_ops static void driver_release(struct kobject * kobj) { - struct device_driver * drv = to_driver(kobj); - complete(&drv->unloaded); + /* + * Yes this is an empty release function, it is this way because struct + * device is always a static object, not a dynamic one. Yes, this is + * not nice and bad, but remember, drivers are code, reference counted + * by the module count, not a device, which is really data. And yes, + * in the future I do want to have all drivers be created dynamically, + * and am working toward that goal, but it will take a bit longer... + * + * But do not let this example give _anyone_ the idea that they can + * create a release function without any code in it at all, to do that + * is almost always wrong. If you have any questions about this, + * please send an email to + */ } static struct kobj_type ktype_driver = { @@ -133,7 +147,6 @@ static decl_subsys(bus, &ktype_bus, NULL #ifdef CONFIG_HOTPLUG - /* Manually detach a device from its associated driver. */ static int driver_helper(struct device *dev, void *data) { @@ -199,6 +212,33 @@ static ssize_t driver_bind(struct device } static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); +static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%d\n", bus->drivers_autoprobe); +} + +static ssize_t store_drivers_autoprobe(struct bus_type *bus, + const char *buf, size_t count) +{ + if (buf[0] == '0') + bus->drivers_autoprobe = 0; + else + bus->drivers_autoprobe = 1; + return count; +} + +static ssize_t store_drivers_probe(struct bus_type *bus, + const char *buf, size_t count) +{ + struct device *dev; + + dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); + if (!dev) + return -ENODEV; + if (bus_rescan_devices_helper(dev, NULL) != 0) + return -EINVAL; + return count; +} #endif static struct device * next_device(struct klist_iter * i) @@ -418,21 +458,21 @@ out_put: * - Add device to bus's list of devices. * - Try to attach to driver. */ -int bus_attach_device(struct device * dev) +void bus_attach_device(struct device * dev) { struct bus_type *bus = dev->bus; int ret = 0; if (bus) { dev->is_registered = 1; - ret = device_attach(dev); - if (ret >= 0) { + if (bus->drivers_autoprobe) + ret = device_attach(dev); + WARN_ON(ret < 0); + if (ret >= 0) klist_add_tail(&dev->knode_bus, &bus->klist_devices); - ret = 0; - } else + else dev->is_registered = 0; } - return ret; } /** @@ -515,9 +555,41 @@ static void remove_bind_files(struct dev driver_remove_file(drv, &driver_attr_bind); driver_remove_file(drv, &driver_attr_unbind); } + +static int add_probe_files(struct bus_type *bus) +{ + int retval; + + bus->drivers_probe_attr.attr.name = "drivers_probe"; + bus->drivers_probe_attr.attr.mode = S_IWUSR; + bus->drivers_probe_attr.attr.owner = bus->owner; + bus->drivers_probe_attr.store = store_drivers_probe; + retval = bus_create_file(bus, &bus->drivers_probe_attr); + if (retval) + goto out; + + bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe"; + bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO; + bus->drivers_autoprobe_attr.attr.owner = bus->owner; + bus->drivers_autoprobe_attr.show = show_drivers_autoprobe; + bus->drivers_autoprobe_attr.store = store_drivers_autoprobe; + retval = bus_create_file(bus, &bus->drivers_autoprobe_attr); + if (retval) + bus_remove_file(bus, &bus->drivers_probe_attr); +out: + return retval; +} + +static void remove_probe_files(struct bus_type *bus) +{ + bus_remove_file(bus, &bus->drivers_autoprobe_attr); + bus_remove_file(bus, &bus->drivers_probe_attr); +} #else static inline int add_bind_files(struct device_driver *drv) { return 0; } static inline void remove_bind_files(struct device_driver *drv) {} +static inline int add_probe_files(struct bus_type *bus) { return 0; } +static inline void remove_probe_files(struct bus_type *bus) {} #endif /** @@ -531,7 +603,7 @@ int bus_add_driver(struct device_driver int error = 0; if (!bus) - return 0; + return -EINVAL; pr_debug("bus %s: add driver %s\n", bus->name, drv->name); error = kobject_set_name(&drv->kobj, "%s", drv->name); @@ -541,9 +613,11 @@ int bus_add_driver(struct device_driver if ((error = kobject_register(&drv->kobj))) goto out_put_bus; - error = driver_attach(drv); - if (error) - goto out_unregister; + if (drv->bus->drivers_autoprobe) { + error = driver_attach(drv); + if (error) + goto out_unregister; + } klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); @@ -605,8 +679,6 @@ static int __must_check bus_rescan_devic ret = device_attach(dev); if (dev->parent) up(&dev->parent->sem); - if (ret > 0) - ret = 0; } return ret < 0 ? ret : 0; } @@ -762,6 +834,12 @@ int bus_register(struct bus_type * bus) klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); klist_init(&bus->klist_drivers, NULL, NULL); + + bus->drivers_autoprobe = 1; + retval = add_probe_files(bus); + if (retval) + goto bus_probe_files_fail; + retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; @@ -770,6 +848,8 @@ int bus_register(struct bus_type * bus) return 0; bus_attrs_fail: + remove_probe_files(bus); +bus_probe_files_fail: kset_unregister(&bus->drivers); bus_drivers_fail: kset_unregister(&bus->devices); @@ -779,7 +859,6 @@ out: return retval; } - /** * bus_unregister - remove a bus from the system * @bus: bus. @@ -791,6 +870,7 @@ void bus_unregister(struct bus_type * bu { pr_debug("bus %s: unregistering\n", bus->name); bus_remove_attrs(bus); + remove_probe_files(bus); kset_unregister(&bus->drivers); kset_unregister(&bus->devices); subsystem_unregister(&bus->subsys); diff --git a/drivers/base/class.c b/drivers/base/class.c index d596812..80bbb20 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -145,6 +145,7 @@ int class_register(struct class * cls) INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->devices); INIT_LIST_HEAD(&cls->interfaces); + kset_init(&cls->class_dirs); init_MUTEX(&cls->sem); error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); if (error) @@ -163,7 +164,6 @@ int class_register(struct class * cls) void class_unregister(struct class * cls) { pr_debug("device class '%s': unregistering\n", cls->name); - kobject_unregister(cls->virtual_dir); remove_class_attrs(cls); subsystem_unregister(&cls->subsys); } diff --git a/drivers/base/core.c b/drivers/base/core.c index d7fcf82..8aa090d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -43,7 +43,8 @@ int (*platform_notify_remove)(struct dev const char *dev_driver_string(struct device *dev) { return dev->driver ? dev->driver->name : - (dev->bus ? dev->bus->name : ""); + (dev->bus ? dev->bus->name : + (dev->class ? dev->class->name : "")); } EXPORT_SYMBOL(dev_driver_string); @@ -119,6 +120,8 @@ static int dev_uevent_filter(struct kset if (ktype == &ktype_device) { struct device *dev = to_dev(kobj); + if (dev->uevent_suppress) + return 0; if (dev->bus) return 1; if (dev->class) @@ -156,6 +159,11 @@ static int dev_uevent(struct kset *kset, "MINOR=%u", MINOR(dev->devt)); } + if (dev->type && dev->type->name) + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVTYPE=%s", dev->type->name); + if (dev->driver) add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, @@ -238,71 +246,152 @@ static struct kset_uevent_ops device_uev .uevent = dev_uevent, }; +static ssize_t show_uevent(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kobject *top_kobj; + struct kset *kset; + char *envp[32]; + char data[PAGE_SIZE]; + char *pos; + int i; + size_t count = 0; + int retval; + + /* search the kset, the device belongs to */ + top_kobj = &dev->kobj; + if (!top_kobj->kset && top_kobj->parent) { + do { + top_kobj = top_kobj->parent; + } while (!top_kobj->kset && top_kobj->parent); + } + if (!top_kobj->kset) + goto out; + kset = top_kobj->kset; + if (!kset->uevent_ops || !kset->uevent_ops->uevent) + goto out; + + /* respect filter */ + if (kset->uevent_ops && kset->uevent_ops->filter) + if (!kset->uevent_ops->filter(kset, &dev->kobj)) + goto out; + + /* let the kset specific function add its keys */ + pos = data; + retval = kset->uevent_ops->uevent(kset, &dev->kobj, + envp, ARRAY_SIZE(envp), + pos, PAGE_SIZE); + if (retval) + goto out; + + /* copy keys to file */ + for (i = 0; envp[i]; i++) { + pos = &buf[count]; + count += sprintf(pos, "%s\n", envp[i]); + } +out: + return count; +} + static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + if (memcmp(buf, "add", 3) != 0) + dev_err(dev, "uevent: unsupported action-string; this will " + "be ignored in a future kernel version"); kobject_uevent(&dev->kobj, KOBJ_ADD); return count; } -static int device_add_groups(struct device *dev) +static int device_add_attributes(struct device *dev, + struct device_attribute *attrs) +{ + int error = 0; + int i; + + if (attrs) { + for (i = 0; attr_name(attrs[i]); i++) { + error = device_create_file(dev, &attrs[i]); + if (error) + break; + } + if (error) + while (--i >= 0) + device_remove_file(dev, &attrs[i]); + } + return error; +} + +static void device_remove_attributes(struct device *dev, + struct device_attribute *attrs) { int i; + + if (attrs) + for (i = 0; attr_name(attrs[i]); i++) + device_remove_file(dev, &attrs[i]); +} + +static int device_add_groups(struct device *dev, + struct attribute_group **groups) +{ int error = 0; + int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - error = sysfs_create_group(&dev->kobj, dev->groups[i]); + if (groups) { + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(&dev->kobj, groups[i]); if (error) { while (--i >= 0) - sysfs_remove_group(&dev->kobj, dev->groups[i]); - goto out; + sysfs_remove_group(&dev->kobj, groups[i]); + break; } } } -out: return error; } -static void device_remove_groups(struct device *dev) +static void device_remove_groups(struct device *dev, + struct attribute_group **groups) { int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - sysfs_remove_group(&dev->kobj, dev->groups[i]); - } - } + + if (groups) + for (i = 0; groups[i]; i++) + sysfs_remove_group(&dev->kobj, groups[i]); } static int device_add_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; - int error = 0; - int i; + int error; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) { - error = device_create_file(dev, &class->dev_attrs[i]); - if (error) - break; - } + if (class) { + error = device_add_attributes(dev, class->dev_attrs); if (error) - while (--i >= 0) - device_remove_file(dev, &class->dev_attrs[i]); + return error; } - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) { - error = device_create_file(dev, &type->attrs[i]); - if (error) - break; - } + if (type) { + error = device_add_groups(dev, type->groups); if (error) - while (--i >= 0) - device_remove_file(dev, &type->attrs[i]); + goto err_remove_class_attrs; } + error = device_add_groups(dev, dev->groups); + if (error) + goto err_remove_type_groups; + + return 0; + + err_remove_type_groups: + if (type) + device_remove_groups(dev, type->groups); + err_remove_class_attrs: + if (class) + device_remove_attributes(dev, class->dev_attrs); + return error; } @@ -310,17 +399,14 @@ static void device_remove_attrs(struct d { struct class *class = dev->class; struct device_type *type = dev->type; - int i; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) - device_remove_file(dev, &class->dev_attrs[i]); - } + device_remove_groups(dev, dev->groups); - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) - device_remove_file(dev, &type->attrs[i]); - } + if (type) + device_remove_groups(dev, type->groups); + + if (class) + device_remove_attributes(dev, class->dev_attrs); } @@ -394,9 +480,10 @@ void device_remove_bin_file(struct devic EXPORT_SYMBOL_GPL(device_remove_bin_file); /** - * device_schedule_callback - helper to schedule a callback for a device + * device_schedule_callback_owner - helper to schedule a callback for a device * @dev: device. * @func: callback function to invoke later. + * @owner: module owning the callback routine * * Attribute methods must not unregister themselves or their parent device * (which would amount to the same thing). Attempts to do so will deadlock, @@ -407,20 +494,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file * argument in the workqueue's process context. @dev will be pinned until * @func returns. * + * This routine is usually called via the inline device_schedule_callback(), + * which automatically sets @owner to THIS_MODULE. + * * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated. + * be allocated, -ENODEV if a reference to @owner isn't available. * * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an * underlying sysfs routine (since it is intended for use by attribute * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. */ -int device_schedule_callback(struct device *dev, - void (*func)(struct device *)) +int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner) { return sysfs_schedule_callback(&dev->kobj, - (void (*)(void *)) func, dev); + (void (*)(void *)) func, dev, owner); } -EXPORT_SYMBOL_GPL(device_schedule_callback); +EXPORT_SYMBOL_GPL(device_schedule_callback_owner); static void klist_children_get(struct klist_node *n) { @@ -477,34 +567,58 @@ static struct kobject * get_device_paren return NULL; } #else -static struct kobject * virtual_device_parent(struct device *dev) +static struct kobject *virtual_device_parent(struct device *dev) { - if (!dev->class) - return ERR_PTR(-ENODEV); + static struct kobject *virtual_dir = NULL; - if (!dev->class->virtual_dir) { - static struct kobject *virtual_dir = NULL; + if (!virtual_dir) + virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); - if (!virtual_dir) - virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); - dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); - } - - return dev->class->virtual_dir; + return virtual_dir; } static struct kobject * get_device_parent(struct device *dev, struct device *parent) { - /* if this is a class device, and has no parent, create one */ - if ((dev->class) && (parent == NULL)) { - return virtual_device_parent(dev); - } else if (parent) + if (dev->class) { + struct kobject *kobj = NULL; + struct kobject *parent_kobj; + struct kobject *k; + + /* + * If we have no parent, we live in "virtual". + * Class-devices with a bus-device as parent, live + * in a class-directory to prevent namespace collisions. + */ + if (parent == NULL) + parent_kobj = virtual_device_parent(dev); + else if (parent->class) + return &parent->kobj; + else + parent_kobj = &parent->kobj; + + /* find our class-directory at the parent and reference it */ + spin_lock(&dev->class->class_dirs.list_lock); + list_for_each_entry(k, &dev->class->class_dirs.list, entry) + if (k->parent == parent_kobj) { + kobj = kobject_get(k); + break; + } + spin_unlock(&dev->class->class_dirs.list_lock); + if (kobj) + return kobj; + + /* or create a new class-directory at the parent device */ + return kobject_kset_add_dir(&dev->class->class_dirs, + parent_kobj, dev->class->name); + } + + if (parent) return &parent->kobj; return NULL; } - #endif + static int setup_parent(struct device *dev, struct device *parent) { struct kobject *kobj; @@ -541,7 +655,6 @@ int device_add(struct device *dev) pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); parent = get_device(dev->parent); - error = setup_parent(dev, parent); if (error) goto Error; @@ -562,10 +675,11 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); dev->uevent_attr.attr.name = "uevent"; - dev->uevent_attr.attr.mode = S_IWUSR; + dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR; if (dev->driver) dev->uevent_attr.attr.owner = dev->driver->owner; dev->uevent_attr.store = store_uevent; + dev->uevent_attr.show = show_uevent; error = device_create_file(dev, &dev->uevent_attr); if (error) goto attrError; @@ -614,16 +728,12 @@ #endif if ((error = device_add_attrs(dev))) goto AttrsError; - if ((error = device_add_groups(dev))) - goto GroupError; if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) goto BusError; - if (!dev->uevent_suppress) - kobject_uevent(&dev->kobj, KOBJ_ADD); - if ((error = bus_attach_device(dev))) - goto AttachError; + kobject_uevent(&dev->kobj, KOBJ_ADD); + bus_attach_device(dev); if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); @@ -639,19 +749,15 @@ #endif up(&dev->class->sem); } Done: - kfree(class_name); + kfree(class_name); put_device(dev); return error; - AttachError: - bus_remove_device(dev); BusError: device_pm_remove(dev); PMError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); - device_remove_groups(dev); - GroupError: device_remove_attrs(dev); AttrsError: if (dev->devt_attr) { @@ -677,15 +783,6 @@ #ifdef CONFIG_SYSFS_DEPRECATED #endif sysfs_remove_link(&dev->kobj, "device"); } - - down(&dev->class->sem); - /* notify any interfaces that the device is now gone */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) - if (class_intf->remove_dev) - class_intf->remove_dev(dev, class_intf); - /* remove the device from the class list */ - list_del_init(&dev->node); - up(&dev->class->sem); } ueventattrError: device_remove_file(dev, &dev->uevent_attr); @@ -796,9 +893,33 @@ #endif /* remove the device from the class list */ list_del_init(&dev->node); up(&dev->class->sem); + + /* If we live in a parent class-directory, unreference it */ + if (dev->kobj.parent->kset == &dev->class->class_dirs) { + struct device *d; + int other = 0; + + /* + * if we are the last child of our class, delete + * our class-directory at this parent + */ + down(&dev->class->sem); + list_for_each_entry(d, &dev->class->devices, node) { + if (d == dev) + continue; + if (d->kobj.parent == dev->kobj.parent) { + other = 1; + break; + } + } + if (!other) + kobject_del(dev->kobj.parent); + + kobject_put(dev->kobj.parent); + up(&dev->class->sem); + } } device_remove_file(dev, &dev->uevent_attr); - device_remove_groups(dev); device_remove_attrs(dev); bus_remove_device(dev); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6a48824..18dba8e 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -94,19 +94,11 @@ int device_bind_driver(struct device *de return ret; } -struct stupid_thread_structure { - struct device_driver *drv; - struct device *dev; -}; - static atomic_t probe_count = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); -static int really_probe(void *void_data) +static int really_probe(struct device *dev, struct device_driver *drv) { - struct stupid_thread_structure *data = void_data; - struct device_driver *drv = data->drv; - struct device *dev = data->dev; int ret = 0; atomic_inc(&probe_count); @@ -154,7 +146,6 @@ probe_failed: */ ret = 0; done: - kfree(data); atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; @@ -186,16 +177,14 @@ int driver_probe_done(void) * format of the ID structures, nor what is to be considered a match and * what is not. * - * This function returns 1 if a match is found, an error if one occurs - * (that is not -ENODEV or -ENXIO), and 0 otherwise. + * This function returns 1 if a match is found, -ENODEV if the device is + * not registered, and 0 otherwise. * * This function must be called with @dev->sem held. When called for a * USB interface, @dev->parent->sem must be held as well. */ int driver_probe_device(struct device_driver * drv, struct device * dev) { - struct stupid_thread_structure *data; - struct task_struct *probe_task; int ret = 0; if (!device_is_registered(dev)) @@ -206,19 +195,7 @@ int driver_probe_device(struct device_dr pr_debug("%s: Matched Device %s with Driver %s\n", drv->bus->name, dev->bus_id, drv->name); - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - data->drv = drv; - data->dev = dev; - - if (drv->multithread_probe) { - probe_task = kthread_run(really_probe, data, - "probe-%s", dev->bus_id); - if (IS_ERR(probe_task)) - ret = really_probe(data); - } else - ret = really_probe(data); + ret = really_probe(dev, drv); done: return ret; @@ -230,30 +207,57 @@ static int __device_attach(struct device return driver_probe_device(drv, dev); } +static int device_probe_drivers(void *data) +{ + struct device *dev = data; + int ret = 0; + + if (dev->bus) { + down(&dev->sem); + ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + up(&dev->sem); + } + return ret; +} + /** * device_attach - try to attach device to a driver. * @dev: device. * * Walk the list of drivers that the bus has and call * driver_probe_device() for each pair. If a compatible - * pair is found, break out and return. + * pair is found, break out and return. If the bus specifies + * multithreaded probing, walking the list of drivers is done + * on a probing thread. * * Returns 1 if the device was bound to a driver; - * 0 if no matching device was found; error code otherwise. + * 0 if no matching device was found or multithreaded probing is done; + * -ENODEV if the device is not registered. * * When called for a USB interface, @dev->parent->sem must be held. */ int device_attach(struct device * dev) { int ret = 0; + struct task_struct *probe_task = ERR_PTR(-ENOMEM); down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; - } else - ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + else { + dev->driver = NULL; + ret = 0; + } + } else { + if (dev->bus->multithread_probe) + probe_task = kthread_run(device_probe_drivers, dev, + "probe-%s", dev->bus_id); + if(IS_ERR(probe_task)) + ret = bus_for_each_drv(dev->bus, NULL, dev, + __device_attach); + } up(&dev->sem); return ret; } diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c index cd467c9..9406259 100644 --- a/drivers/base/dmapool.c +++ b/drivers/base/dmapool.c @@ -37,7 +37,7 @@ struct dma_page { /* cacheable header fo #define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000) -static DECLARE_MUTEX (pools_lock); +static DEFINE_MUTEX (pools_lock); static ssize_t show_pools (struct device *dev, struct device_attribute *attr, char *buf) @@ -55,7 +55,7 @@ show_pools (struct device *dev, struct d size -= temp; next += temp; - down (&pools_lock); + mutex_lock(&pools_lock); list_for_each_entry(pool, &dev->dma_pools, pools) { unsigned pages = 0; unsigned blocks = 0; @@ -73,7 +73,7 @@ show_pools (struct device *dev, struct d size -= temp; next += temp; } - up (&pools_lock); + mutex_unlock(&pools_lock); return PAGE_SIZE - size; } @@ -143,7 +143,7 @@ dma_pool_create (const char *name, struc if (dev) { int ret; - down (&pools_lock); + mutex_lock(&pools_lock); if (list_empty (&dev->dma_pools)) ret = device_create_file (dev, &dev_attr_pools); else @@ -155,7 +155,7 @@ dma_pool_create (const char *name, struc kfree(retval); retval = NULL; } - up (&pools_lock); + mutex_unlock(&pools_lock); } else INIT_LIST_HEAD (&retval->pools); @@ -231,11 +231,11 @@ #endif void dma_pool_destroy (struct dma_pool *pool) { - down (&pools_lock); + mutex_lock(&pools_lock); list_del (&pool->pools); if (pool->dev && list_empty (&pool->dev->dma_pools)) device_remove_file (pool->dev, &dev_attr_pools); - up (&pools_lock); + mutex_unlock(&pools_lock); while (!list_empty (&pool->page_list)) { struct dma_page *page; diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 082bfde..eb11475 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -149,10 +149,6 @@ void put_driver(struct device_driver * d * We pass off most of the work to the bus_add_driver() call, * since most of the things we have to do deal with the bus * structures. - * - * The one interesting aspect is that we setup @drv->unloaded - * as a completion that gets complete when the driver reference - * count reaches 0. */ int driver_register(struct device_driver * drv) { @@ -162,35 +158,19 @@ int driver_register(struct device_driver printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); } klist_init(&drv->klist_devices, NULL, NULL); - init_completion(&drv->unloaded); return bus_add_driver(drv); } - /** * driver_unregister - remove driver from system. * @drv: driver. * * Again, we pass off most of the work to the bus-level call. - * - * Though, once that is done, we wait until @drv->unloaded is completed. - * This will block until the driver refcount reaches 0, and it is - * released. Only modular drivers will call this function, and we - * have to guarantee that it won't complete, letting the driver - * unload until all references are gone. */ void driver_unregister(struct device_driver * drv) { bus_remove_driver(drv); - /* - * If the driver is a module, we are probably in - * the module unload path, and we want to wait - * for everything to unload before we can actually - * finish the unload. - */ - if (drv->owner) - wait_for_completion(&drv->unloaded); } /** diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index c0a979a..97ab5bd 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -31,8 +31,6 @@ enum { FW_STATUS_LOADING, FW_STATUS_DONE, FW_STATUS_ABORT, - FW_STATUS_READY, - FW_STATUS_READY_NOHOTPLUG, }; static int loading_timeout = 60; /* In seconds */ @@ -96,9 +94,6 @@ static int firmware_uevent(struct device struct firmware_priv *fw_priv = dev_get_drvdata(dev); int i = 0, len = 0; - if (!test_bit(FW_STATUS_READY, &fw_priv->status)) - return -ENODEV; - if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "FIRMWARE=%s", fw_priv->fw_id)) return -ENOMEM; @@ -333,6 +328,7 @@ static int fw_register_device(struct dev f_dev->parent = device; f_dev->class = &firmware_class; dev_set_drvdata(f_dev, fw_priv); + f_dev->uevent_suppress = 1; retval = device_register(f_dev); if (retval) { printk(KERN_ERR "%s: device_register failed\n", @@ -382,9 +378,7 @@ static int fw_setup_device(struct firmwa } if (uevent) - set_bit(FW_STATUS_READY, &fw_priv->status); - else - set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status); + f_dev->uevent_suppress = 0; *dev_p = f_dev; goto out; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index bbbb973..05dc876 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -29,6 +29,9 @@ LIST_HEAD(dpm_off_irq); DECLARE_MUTEX(dpm_sem); DECLARE_MUTEX(dpm_list_sem); +int (*platform_enable_wakeup)(struct device *dev, int is_on); + + /** * device_pm_set_parent - Specify power dependency. * @dev: Device who needs power. diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 020be36..a2c6418 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -26,7 +26,9 @@ int resume_device(struct device * dev) TRACE_DEVICE(dev); TRACE_RESUME(0); + down(&dev->sem); + if (dev->power.pm_parent && dev->power.pm_parent->power.power_state.event) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", @@ -34,15 +36,24 @@ int resume_device(struct device * dev) dev->power.pm_parent->bus_id, dev->power.pm_parent->power.power_state.event); } + if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); error = dev->bus->resume(dev); } - if (dev->class && dev->class->resume) { + + if (!error && dev->type && dev->type->resume) { + dev_dbg(dev,"resuming\n"); + error = dev->type->resume(dev); + } + + if (!error && dev->class && dev->class->resume) { dev_dbg(dev,"class resume\n"); error = dev->class->resume(dev); } + up(&dev->sem); + TRACE_RESUME(error); return error; } diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c index 3483ae4..58b6f77 100644 --- a/drivers/base/power/shutdown.c +++ b/drivers/base/power/shutdown.c @@ -36,7 +36,6 @@ void device_shutdown(void) { struct device * dev, *devn; - down_write(&devices_subsys.rwsem); list_for_each_entry_safe_reverse(dev, devn, &devices_subsys.kset.list, kobj.entry) { if (dev->bus && dev->bus->shutdown) { @@ -47,7 +46,6 @@ void device_shutdown(void) dev->driver->shutdown(dev); } } - up_write(&devices_subsys.rwsem); sysdev_shutdown(); } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index ece136b..42d2b86 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -78,6 +78,18 @@ int suspend_device(struct device * dev, suspend_report_result(dev->class->suspend, error); } + if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) { + dev_dbg(dev, "%s%s\n", + suspend_verb(state.event), + ((state.event == PM_EVENT_SUSPEND) + && device_may_wakeup(dev)) + ? ", may wakeup" + : "" + ); + error = dev->type->suspend(dev, state); + suspend_report_result(dev->type->suspend, error); + } + if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) { dev_dbg(dev, "%s%s\n", suspend_verb(state.event), diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 2308e83..1d84668 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -48,6 +48,15 @@ struct aoe_hdr { __be32 tag; }; +#ifdef __KERNEL__ +#include + +static inline struct aoe_hdr *aoe_hdr(const struct sk_buff *skb) +{ + return (struct aoe_hdr *)skb_mac_header(skb); +} +#endif + struct aoe_atahdr { unsigned char aflags; unsigned char errfeat; diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 8d17d8d..1a6aeac 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -27,7 +27,8 @@ new_skb(ulong len) skb = alloc_skb(len, GFP_ATOMIC); if (skb) { - skb->nh.raw = skb->mac.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); skb->protocol = __constant_htons(ETH_P_AOE); skb->priority = 0; skb->next = skb->prev = NULL; @@ -118,7 +119,7 @@ aoecmd_ata_rw(struct aoedev *d, struct f /* initialize the headers & frame */ skb = f->skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ah = (struct aoe_atahdr *) (h+1); skb_put(skb, sizeof *h + sizeof *ah); memset(h, 0, skb->len); @@ -207,7 +208,7 @@ aoecmd_cfg_pkts(ushort aoemajor, unsigne skb->dev = ifp; if (sl_tail == NULL) sl_tail = skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); memset(h, 0, sizeof *h + sizeof *ch); memset(h->dst, 0xff, sizeof h->dst); @@ -300,7 +301,7 @@ rexmit(struct aoedev *d, struct frame *f aoechr_error(buf); skb = f->skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ah = (struct aoe_atahdr *) (h+1); f->tag = n; h->tag = cpu_to_be32(n); @@ -529,7 +530,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) char ebuf[128]; u16 aoemajor; - hin = (struct aoe_hdr *) skb->mac.raw; + hin = aoe_hdr(skb); aoemajor = be16_to_cpu(get_unaligned(&hin->major)); d = aoedev_by_aoeaddr(aoemajor, hin->minor); if (d == NULL) { @@ -561,7 +562,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) calc_rttavg(d, tsince(f->tag)); ahin = (struct aoe_atahdr *) (hin+1); - hout = (struct aoe_hdr *) f->skb->mac.raw; + hout = aoe_hdr(f->skb); ahout = (struct aoe_atahdr *) (hout+1); buf = f->buf; @@ -695,7 +696,7 @@ aoecmd_ata_id(struct aoedev *d) /* initialize the headers & frame */ skb = f->skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ah = (struct aoe_atahdr *) (h+1); skb_put(skb, sizeof *h + sizeof *ah); memset(h, 0, skb->len); @@ -726,7 +727,7 @@ aoecmd_cfg_rsp(struct sk_buff *skb) enum { MAXFRAMES = 16 }; u16 n; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ch = (struct aoe_cfghdr *) (h+1); /* diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c index aab6d91..f9ddfda 100644 --- a/drivers/block/aoe/aoenet.c +++ b/drivers/block/aoe/aoenet.c @@ -123,7 +123,7 @@ aoenet_rcv(struct sk_buff *skb, struct n goto exit; skb_push(skb, ETH_HLEN); /* (1) */ - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); n = be32_to_cpu(get_unaligned(&h->tag)); if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) goto exit; diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 2098eff..746a118 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -2132,10 +2132,13 @@ static int ub_get_pipes(struct ub_dev *s if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { /* BULK in or out? */ - if (ep->bEndpointAddress & USB_DIR_IN) - ep_in = ep; - else - ep_out = ep; + if (ep->bEndpointAddress & USB_DIR_IN) { + if (ep_in == NULL) + ep_in = ep; + } else { + if (ep_out == NULL) + ep_out = ep; + } } } diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 4c766f3..b990805 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -527,7 +527,7 @@ static int bfusb_send_frame(struct sk_bu buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size; memcpy(skb_put(nskb, 3), buf, 3); - memcpy(skb_put(nskb, size), skb->data + sent, size); + skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size); sent += size; count -= size; diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index acfb6a4..851de4d 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -461,20 +461,20 @@ static void bluecard_receive(bluecard_in switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: - eh = (struct hci_event_hdr *)(info->rx_skb->data); + eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: - ah = (struct hci_acl_hdr *)(info->rx_skb->data); + ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: - sh = (struct hci_sco_hdr *)(info->rx_skb->data); + sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 9fca651..e8ebd5d 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -231,7 +231,7 @@ static void bpa10x_wakeup(struct bpa10x_ cr = (struct usb_ctrlrequest *) urb->setup_packet; cr->wLength = __cpu_to_le16(skb->len); - memcpy(urb->transfer_buffer, skb->data, skb->len); + skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); urb->transfer_buffer_length = skb->len; err = usb_submit_urb(urb, GFP_ATOMIC); @@ -250,7 +250,7 @@ static void bpa10x_wakeup(struct bpa10x_ skb = skb_dequeue(&data->tx_queue); if (skb) { - memcpy(urb->transfer_buffer, skb->data, skb->len); + skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); urb->transfer_buffer_length = skb->len; err = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 18b0f39..3951607 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -303,20 +303,20 @@ static void bt3c_receive(bt3c_info_t *in switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: - eh = (struct hci_event_hdr *)(info->rx_skb->data); + eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: - ah = (struct hci_acl_hdr *)(info->rx_skb->data); + ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: - sh = (struct hci_sco_hdr *)(info->rx_skb->data); + sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index c1bce75..d7d2ea0 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -250,20 +250,20 @@ static void btuart_receive(btuart_info_t switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: - eh = (struct hci_event_hdr *)(info->rx_skb->data); + eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: - ah = (struct hci_acl_hdr *)(info->rx_skb->data); + ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: - sh = (struct hci_sco_hdr *)(info->rx_skb->data); + sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 459aa97..7f9c54b 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -425,7 +425,7 @@ static int dtl1_hci_send_frame(struct sk return -ENOMEM; skb_reserve(s, NSHL); - memcpy(skb_put(s, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len); if (skb->len & 0x0001) *skb_put(s, 1) = 0; /* PAD */ diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 34f0afc..bfbae14 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -188,7 +188,7 @@ static int h4_recv(struct hci_uart *hu, continue; case H4_W4_EVENT_HDR: - eh = (struct hci_event_hdr *) h4->rx_skb->data; + eh = hci_event_hdr(h4->rx_skb); BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); @@ -196,7 +196,7 @@ static int h4_recv(struct hci_uart *hu, continue; case H4_W4_ACL_HDR: - ah = (struct hci_acl_hdr *) h4->rx_skb->data; + ah = hci_acl_hdr(h4->rx_skb); dlen = __le16_to_cpu(ah->dlen); BT_DBG("ACL header: dlen %d", dlen); @@ -205,7 +205,7 @@ static int h4_recv(struct hci_uart *hu, continue; case H4_W4_SCO_HDR: - sh = (struct hci_sco_hdr *) h4->rx_skb->data; + sh = hci_sco_hdr(h4->rx_skb); BT_DBG("SCO header: dlen %d", sh->dlen); diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c index 8dcf9d2..c70d52a 100644 --- a/drivers/char/briq_panel.c +++ b/drivers/char/briq_panel.c @@ -202,13 +202,16 @@ static struct miscdevice briq_panel_misc static int __init briq_panel_init(void) { - struct device_node *root = find_path_device("/"); + struct device_node *root = of_find_node_by_path("/"); const char *machine; int i; machine = get_property(root, "model", NULL); - if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) + if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) { + of_node_put(root); return -ENODEV; + } + of_node_put(root); printk(KERN_INFO "briq_panel: v%s Dr. Karsten Jeppesen (kj@totalimpact.com)\n", diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index a0a88aa..0f9ed7b 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -47,8 +47,6 @@ #include "hvc_console.h" #define HVC_MAJOR 229 #define HVC_MINOR 0 -#define TIMEOUT (10) - /* * Wait this long per iteration while trying to push buffered data to the * hypervisor before allowing the tty to complete a close operation. @@ -104,12 +102,12 @@ static DEFINE_SPINLOCK(hvc_structs_lock) /* * This value is used to assign a tty->index value to a hvc_struct based * upon order of exposure via hvc_probe(), when we can not match it to - * a console canidate registered with hvc_instantiate(). + * a console candidate registered with hvc_instantiate(). */ static int last_hvc = -1; /* - * Do not call this function with either the hvc_strucst_lock or the hvc_struct + * Do not call this function with either the hvc_structs_lock or the hvc_struct * lock held. If successful, this function increments the kobject reference * count against the target hvc_struct so it should be released when finished. */ @@ -162,7 +160,7 @@ void hvc_console_print(struct console *c if (index >= MAX_NR_HVC_CONSOLES) return; - /* This console adapter was removed so it is not useable. */ + /* This console adapter was removed so it is not usable. */ if (vtermnos[index] < 0) return; @@ -220,7 +218,7 @@ struct console hvc_con_driver = { }; /* - * Early console initialization. Preceeds driver initialization. + * Early console initialization. Precedes driver initialization. * * (1) we are first, and the user specified another driver * -- index will remain -1 @@ -257,7 +255,7 @@ int hvc_instantiate(uint32_t vtermno, in if (vtermnos[index] != -1) return -1; - /* make sure no no tty has been registerd in this index */ + /* make sure no no tty has been registered in this index */ hp = hvc_get_by_index(index); if (hp) { kobject_put(&hp->kobj); @@ -267,7 +265,7 @@ int hvc_instantiate(uint32_t vtermno, in vtermnos[index] = vtermno; cons_ops[index] = ops; - /* reserve all indices upto and including this index */ + /* reserve all indices up to and including this index */ if (last_hvc < index) last_hvc = index; @@ -528,7 +526,7 @@ static int hvc_write(struct tty_struct * /* * This is actually a contract between the driver and the tty layer outlining - * how much write room the driver can guarentee will be sent OR BUFFERED. This + * how much write room the driver can guarantee will be sent OR BUFFERED. This * driver MUST honor the return value. */ static int hvc_write_room(struct tty_struct *tty) @@ -550,6 +548,18 @@ static int hvc_chars_in_buffer(struct tt return hp->n_outbuf; } +/* + * timeout will vary between the MIN and MAX values defined here. By default + * and during console activity we will use a default MIN_TIMEOUT of 10. When + * the console is idle, we increase the timeout value on each pass through + * msleep until we reach the max. This may be noticeable as a brief (average + * one second) delay on the console before the console responds to input when + * there has been no input for some time. + */ +#define MIN_TIMEOUT (10) +#define MAX_TIMEOUT (2000) +static u32 timeout = MIN_TIMEOUT; + #define HVC_POLL_READ 0x00000001 #define HVC_POLL_WRITE 0x00000002 @@ -642,9 +652,14 @@ #endif /* CONFIG_MAGIC_SYSRQ */ bail: spin_unlock_irqrestore(&hp->lock, flags); - if (read_total) + if (read_total) { + /* Activity is occurring, so reset the polling backoff value to + a minimum for performance. */ + timeout = MIN_TIMEOUT; + tty_flip_buffer_push(tty); - + } + return poll_mask; } @@ -688,8 +703,12 @@ int khvcd(void *unused) if (!hvc_kicked) { if (poll_mask == 0) schedule(); - else - msleep_interruptible(TIMEOUT); + else { + if (timeout < MAX_TIMEOUT) + timeout += (timeout >> 6) + 1; + + msleep_interruptible(timeout); + } } __set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); @@ -794,7 +813,7 @@ int __devexit hvc_remove(struct hvc_stru /* * We 'put' the instance that was grabbed when the kobject instance - * was intialized using kobject_init(). Let the last holder of this + * was initialized using kobject_init(). Let the last holder of this * kobject cause it to be removed, which will probably be the tty_hangup * below. */ @@ -850,7 +869,7 @@ int __init hvc_init(void) } module_init(hvc_init); -/* This isn't particularily necessary due to this being a console driver +/* This isn't particularly necessary due to this being a console driver * but it is nice to be thorough. */ static void __exit hvc_exit(void) diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index f144a94..ec420fe 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -575,7 +575,7 @@ static int hvc_find_vtys(void) (num_found >= VTTY_PORTS)) break; - vtermno = get_property(vty, "reg", NULL); + vtermno = of_get_property(vty, "reg", NULL); if (!vtermno) continue; diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index f9c0084..94a542e 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -153,7 +153,7 @@ static int hvc_find_vtys(void) if (num_found >= MAX_NR_HVC_CONSOLES) break; - vtermno = get_property(vty, "reg", NULL); + vtermno = of_get_property(vty, "reg", NULL); if (!vtermno) continue; diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 50315d6..d5a752d 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -1279,8 +1279,8 @@ static int __init hvsi_console_init(void struct hvsi_struct *hp; const uint32_t *vtermno, *irq; - vtermno = get_property(vty, "reg", NULL); - irq = get_property(vty, "interrupts", NULL); + vtermno = of_get_property(vty, "reg", NULL); + irq = of_get_property(vty, "interrupts", NULL); if (!vtermno || !irq) continue; diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 8d025e9..157b1d0 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -4169,7 +4169,7 @@ static int hdlcdev_xmit(struct sk_buff * netif_stop_queue(dev); /* copy data to device buffers */ - memcpy(info->tx_buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, info->tx_buf, skb->len); info->tx_get = 0; info->tx_put = info->tx_count = skb->len; diff --git a/drivers/char/random.c b/drivers/char/random.c index b9dc7aa..46c1b97 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -881,15 +881,15 @@ EXPORT_SYMBOL(get_random_bytes); */ static void init_std_data(struct entropy_store *r) { - struct timeval tv; + ktime_t now; unsigned long flags; spin_lock_irqsave(&r->lock, flags); r->entropy_count = 0; spin_unlock_irqrestore(&r->lock, flags); - do_gettimeofday(&tv); - add_entropy_words(r, (__u32 *)&tv, sizeof(tv)/4); + now = ktime_get_real(); + add_entropy_words(r, (__u32 *)&now, sizeof(now)/4); add_entropy_words(r, (__u32 *)utsname(), sizeof(*(utsname()))/4); } @@ -911,14 +911,12 @@ void rand_initialize_irq(int irq) return; /* - * If kmalloc returns null, we just won't use that entropy + * If kzalloc returns null, we just won't use that entropy * source. */ - state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL); - if (state) { - memset(state, 0, sizeof(struct timer_rand_state)); + state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL); + if (state) irq_timer_state[irq] = state; - } } #ifdef CONFIG_BLOCK @@ -927,14 +925,12 @@ void rand_initialize_disk(struct gendisk struct timer_rand_state *state; /* - * If kmalloc returns null, we just won't use that entropy + * If kzalloc returns null, we just won't use that entropy * source. */ - state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL); - if (state) { - memset(state, 0, sizeof(struct timer_rand_state)); + state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL); + if (state) disk->random = state; - } } #endif @@ -1469,7 +1465,6 @@ #if defined(CONFIG_IPV6) || defined(CONF __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, __be16 sport, __be16 dport) { - struct timeval tv; __u32 seq; __u32 hash[12]; struct keydata *keyptr = get_keyptr(); @@ -1485,8 +1480,7 @@ __u32 secure_tcpv6_sequence_number(__be3 seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK; seq += keyptr->count; - do_gettimeofday(&tv); - seq += tv.tv_usec + tv.tv_sec * 1000000; + seq += ktime_get_real().tv64; return seq; } @@ -1521,7 +1515,6 @@ #ifdef CONFIG_INET __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) { - struct timeval tv; __u32 seq; __u32 hash[4]; struct keydata *keyptr = get_keyptr(); @@ -1543,12 +1536,11 @@ __u32 secure_tcp_sequence_number(__be32 * As close as possible to RFC 793, which * suggests using a 250 kHz clock. * Further reading shows this assumes 2 Mb/s networks. - * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate. + * For 10 Gb/s Ethernet, a 1 GHz clock is appropriate. * That's funny, Linux has one built in! Use it! * (Networks are faster now - should this be increased?) */ - do_gettimeofday(&tv); - seq += tv.tv_usec + tv.tv_sec * 1000000; + seq += ktime_get_real().tv64; #if 0 printk("init_seq(%lx, %lx, %d, %d) = %d\n", saddr, daddr, sport, dport, seq); @@ -1556,8 +1548,6 @@ #endif return seq; } -EXPORT_SYMBOL(secure_tcp_sequence_number); - /* Generate secure starting point for ephemeral IPV4 transport port search */ u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) { @@ -1598,7 +1588,6 @@ #if defined(CONFIG_IP_DCCP) || defined(C u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) { - struct timeval tv; u64 seq; __u32 hash[4]; struct keydata *keyptr = get_keyptr(); @@ -1611,8 +1600,7 @@ u64 secure_dccp_sequence_number(__be32 s seq = half_md4_transform(hash, keyptr->secret); seq |= ((u64)keyptr->count) << (32 - HASH_BITS); - do_gettimeofday(&tv); - seq += tv.tv_usec + tv.tv_sec * 1000000; + seq += ktime_get_real().tv64; seq &= (1ull << 48) - 1; #if 0 printk("dccp init_seq(%lx, %lx, %d, %d) = %d\n", diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 7823757..3ef593a 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1,6 +1,8 @@ /* * Sony Programmable I/O Control Device driver for VAIO * + * Copyright (C) 2007 Mattia Dongili + * * Copyright (C) 2001-2005 Stelian Pop * * Copyright (C) 2005 Narayanan R S @@ -95,6 +97,11 @@ module_param(useinput, int, 0444); MODULE_PARM_DESC(useinput, "set this if you would like sonypi to feed events to the input subsystem"); +static int check_ioport = 1; +module_param(check_ioport, int, 0444); +MODULE_PARM_DESC(check_ioport, + "set this to 0 if you think the automatic ioport check for sony-laptop is wrong"); + #define SONYPI_DEVICE_MODEL_TYPE1 1 #define SONYPI_DEVICE_MODEL_TYPE2 2 #define SONYPI_DEVICE_MODEL_TYPE3 3 @@ -477,7 +484,7 @@ static struct sonypi_device { u16 evtype_offset; int camera_power; int bluetooth_power; - struct semaphore lock; + struct mutex lock; struct kfifo *fifo; spinlock_t fifo_lock; wait_queue_head_t fifo_proc_list; @@ -884,7 +891,7 @@ int sonypi_camera_command(int command, u if (!camera) return -EIO; - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); switch (command) { case SONYPI_COMMAND_SETCAMERA: @@ -919,7 +926,7 @@ int sonypi_camera_command(int command, u command); break; } - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return 0; } @@ -938,20 +945,20 @@ static int sonypi_misc_fasync(int fd, st static int sonypi_misc_release(struct inode *inode, struct file *file) { sonypi_misc_fasync(-1, file, 0); - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); sonypi_device.open_count--; - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return 0; } static int sonypi_misc_open(struct inode *inode, struct file *file) { - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); /* Flush input queue on first open */ if (!sonypi_device.open_count) kfifo_reset(sonypi_device.fifo); sonypi_device.open_count++; - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return 0; } @@ -1001,7 +1008,7 @@ static int sonypi_misc_ioctl(struct inod u8 val8; u16 val16; - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); switch (cmd) { case SONYPI_IOCGBRT: if (sonypi_ec_read(SONYPI_LCD_LIGHT, &val8)) { @@ -1101,7 +1108,7 @@ static int sonypi_misc_ioctl(struct inod default: ret = -EINVAL; } - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return ret; } @@ -1260,6 +1267,28 @@ static int __devinit sonypi_create_input static int __devinit sonypi_setup_ioports(struct sonypi_device *dev, const struct sonypi_ioport_list *ioport_list) { + /* try to detect if sony-laptop is being used and thus + * has already requested one of the known ioports. + * As in the deprecated check_region this is racy has we have + * multiple ioports available and one of them can be requested + * between this check and the subsequent request. Anyway, as an + * attempt to be some more user-friendly as we currently are, + * this is enough. + */ + const struct sonypi_ioport_list *check = ioport_list; + while (check_ioport && check->port1) { + if (!request_region(check->port1, + sonypi_device.region_size, + "Sony Programable I/O Device Check")) { + printk(KERN_ERR "sonypi: ioport 0x%.4x busy, using sony-laptop? " + "if not use check_ioport=0\n", + check->port1); + return -EBUSY; + } + release_region(check->port1, sonypi_device.region_size); + check++; + } + while (ioport_list->port1) { if (request_region(ioport_list->port1, @@ -1321,6 +1350,10 @@ static int __devinit sonypi_probe(struct struct pci_dev *pcidev; int error; + printk(KERN_WARNING "sonypi: please try the sony-laptop module instead " + "and report failures, see also " + "http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n"); + spin_lock_init(&sonypi_device.fifo_lock); sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL, &sonypi_device.fifo_lock); @@ -1330,7 +1363,7 @@ static int __devinit sonypi_probe(struct } init_waitqueue_head(&sonypi_device.fifo_proc_list); - init_MUTEX(&sonypi_device.lock); + mutex_init(&sonypi_device.lock); sonypi_device.bluetooth_power = -1; if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index aefd683..3c85200 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -53,8 +53,8 @@ static void __iomem * atmel_get_base_add } reg = get_property(dn, "reg", ®len); - naddrc = prom_n_addr_cells(dn); - nsizec = prom_n_size_cells(dn); + naddrc = of_n_addr_cells(dn); + nsizec = of_n_size_cells(dn); of_node_put(dn); diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index e812aa1..60198a7 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -548,7 +548,7 @@ config MV64X60_WDT depends on WATCHDOG && MV64X60 config BOOKE_WDT - tristate "PowerPC Book-E Watchdog Timer" + bool "PowerPC Book-E Watchdog Timer" depends on WATCHDOG && (BOOKE || 4xx) ---help--- Please see Documentation/watchdog/watchdog-api.txt for diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c index 5ac309e..5cfcff5 100644 --- a/drivers/clocksource/acpi_pm.c +++ b/drivers/clocksource/acpi_pm.c @@ -26,7 +26,7 @@ #include /* * The I/O port the PMTMR resides at. * The location is detected during setup_arch(), - * in arch/i386/acpi/boot.c + * in arch/i386/kernel/acpi/boot.c */ u32 pmtmr_ioport __read_mostly; diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index a905f78..a7b9e9b 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -212,7 +212,7 @@ static void cn_rx_skb(struct sk_buff *__ skb = skb_get(__skb); if (skb->len >= NLMSG_SPACE(0)) { - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(struct cn_msg) || skb->len < nlh->nlmsg_len || @@ -448,7 +448,7 @@ static int __devinit cn_init(void) dev->nls = netlink_kernel_create(NETLINK_CONNECTOR, CN_NETLINK_USERS + 0xf, - dev->input, THIS_MODULE); + dev->input, NULL, THIS_MODULE); if (!dev->nls) return -EIO; diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 850788f..8fbe9fd 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -36,5 +36,7 @@ config HID_DEBUG If unsure, say N +source "drivers/hid/usbhid/Kconfig" + endmenu diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 52e97d8..68d1376 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -6,3 +6,7 @@ hid-objs := hid-core.o hid-input.o obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o +obj-$(CONFIG_USB_HID) += usbhid/ +obj-$(CONFIG_USB_MOUSE) += usbhid/ +obj-$(CONFIG_USB_KBD) += usbhid/ + diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 1cca32f..62e21cc 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik * Copyright (c) 2005 Michael Haboustak for Concept2, Inc - * Copyright (c) 2006 Jiri Kosina + * Copyright (c) 2006-2007 Jiri Kosina */ /* @@ -37,7 +37,7 @@ #include */ #define DRIVER_VERSION "v2.6" -#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik" +#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina" #define DRIVER_DESC "HID core driver" #define DRIVER_LICENSE "GPL" @@ -872,8 +872,13 @@ static void hid_output_field(struct hid_ unsigned count = field->report_count; unsigned offset = field->report_offset; unsigned size = field->report_size; + unsigned bitsused = offset + count * size; unsigned n; + /* make sure the unused bits in the last byte are zeros */ + if (count > 0 && size > 0 && (bitsused % 8) != 0) + data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1; + for (n = 0; n < count; n++) { if (field->logical_minimum < 0) /* signed values */ implement(data, offset + n * size, size, s32ton(field->value[n], size)); diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index c843402..a19b65e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -431,6 +431,15 @@ #endif case 0x000: goto ignore; case 0x034: map_key_clear(KEY_SLEEP); break; case 0x036: map_key_clear(BTN_MISC); break; + /* + * The next three are reported by Belkin wireless + * keyboard (1020:0006). These values are "reserved" + * in HUT 1.12. + */ + case 0x03a: map_key_clear(KEY_SOUND); break; + case 0x03b: map_key_clear(KEY_CAMERA); break; + case 0x03c: map_key_clear(KEY_DOCUMENTS); break; + case 0x040: map_key_clear(KEY_MENU); break; case 0x045: map_key_clear(KEY_RADIO); break; @@ -531,10 +540,26 @@ #endif case 0x302: map_key_clear(KEY_PROG2); break; case 0x303: map_key_clear(KEY_PROG3); break; - /* Reported on Logitech S510 wireless keyboard */ + /* Reported on certain Logitech wireless keyboards */ + case 0x1001: map_key_clear(KEY_MESSENGER); break; + case 0x1003: map_key_clear(KEY_SOUND); break; + case 0x1004: map_key_clear(KEY_VIDEO); break; + case 0x1005: map_key_clear(KEY_AUDIO); break; + case 0x100a: map_key_clear(KEY_DOCUMENTS); break; + case 0x1011: map_key_clear(KEY_PREVIOUSSONG); break; + case 0x1012: map_key_clear(KEY_NEXTSONG); break; + case 0x1013: map_key_clear(KEY_CAMERA); break; + case 0x1014: map_key_clear(KEY_MESSENGER); break; + case 0x1015: map_key_clear(KEY_RECORD); break; + case 0x1016: map_key_clear(KEY_PLAYER); break; + case 0x1017: map_key_clear(KEY_EJECTCD); break; + case 0x1019: map_key_clear(KEY_PROG1); break; + case 0x101a: map_key_clear(KEY_PROG2); break; + case 0x101b: map_key_clear(KEY_PROG3); break; case 0x101f: map_key_clear(KEY_ZOOMIN); break; case 0x1020: map_key_clear(KEY_ZOOMOUT); break; case 0x1021: map_key_clear(KEY_ZOOMRESET); break; + case 0x1023: map_key_clear(KEY_CLOSE); break; /* this one is marked as 'Rotate' */ case 0x1028: map_key_clear(KEY_ANGLE); break; case 0x1029: map_key_clear(KEY_SHUFFLE); break; diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig new file mode 100644 index 0000000..7c87bdc --- /dev/null +++ b/drivers/hid/usbhid/Kconfig @@ -0,0 +1,149 @@ +comment "USB Input Devices" + depends on USB + +config USB_HID + tristate "USB Human Interface Device (full HID) support" + default y + depends on USB && INPUT + select HID + ---help--- + Say Y here if you want full HID support to connect USB keyboards, + mice, joysticks, graphic tablets, or any other HID based devices + to your computer via USB, as well as Uninterruptible Power Supply + (UPS) and monitor control devices. + + You can't use this driver and the HIDBP (Boot Protocol) keyboard + and mouse drivers at the same time. More information is available: + . + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called usbhid. + +comment "Input core support is needed for USB HID input layer or HIDBP support" + depends on USB_HID && INPUT=n + +config USB_HIDINPUT_POWERBOOK + bool "Enable support for iBook/PowerBook special keys" + default n + depends on USB_HID + help + Say Y here if you want support for the special keys (Fn, Numlock) on + Apple iBooks and PowerBooks. + + If unsure, say N. + +config HID_FF + bool "Force feedback support (EXPERIMENTAL)" + depends on USB_HID && EXPERIMENTAL + help + Say Y here is you want force feedback support for a few HID devices. + See below for a list of supported devices. + + See for a description of the force + feedback API. + + If unsure, say N. + +config HID_PID + bool "PID device support" + depends on HID_FF + help + Say Y here if you have a PID-compliant device and wish to enable force + feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such + devices. + +config LOGITECH_FF + bool "Logitech devices support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have one of these devices: + - Logitech WingMan Cordless RumblePad + - Logitech WingMan Cordless RumblePad 2 + - Logitech WingMan Force 3D + - Logitech Formula Force EX + - Logitech MOMO Force wheel + + and if you want to enable force feedback for them. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config PANTHERLORD_FF + bool "PantherLord USB/PS2 2in1 Adapter support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want + to enable force feedback support for it. + +config THRUSTMASTER_FF + bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" + depends on HID_FF && EXPERIMENTAL + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, + and want to enable force feedback support for it. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config ZEROPLUS_FF + bool "Zeroplus based game controller support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a Zeroplus based game controller and want to + enable force feedback for it. + +config USB_HIDDEV + bool "/dev/hiddev raw HID device support" + depends on USB_HID + help + Say Y here if you want to support HID devices (from the USB + specification standpoint) that aren't strictly user interface + devices, like monitor controls and Uninterruptable Power Supplies. + + This module supports these devices separately using a separate + event interface on /dev/usb/hiddevX (char 180:96 to 180:111). + + If unsure, say Y. + +menu "USB HID Boot Protocol drivers" + depends on USB!=n && USB_HID!=y + +config USB_KBD + tristate "USB HIDBP Keyboard (simple Boot) support" + depends on USB && INPUT + ---help--- + Say Y here only if you are absolutely sure that you don't want + to use the generic HID driver for your USB keyboard and prefer + to use the keyboard in its limited Boot Protocol mode instead. + + This is almost certainly not what you want. This is mostly + useful for embedded applications or simple keyboards. + + To compile this driver as a module, choose M here: the + module will be called usbkbd. + + If even remotely unsure, say N. + +config USB_MOUSE + tristate "USB HIDBP Mouse (simple Boot) support" + depends on USB && INPUT + ---help--- + Say Y here only if you are absolutely sure that you don't want + to use the generic HID driver for your USB mouse and prefer + to use the mouse in its limited Boot Protocol mode instead. + + This is almost certainly not what you want. This is mostly + useful for embedded applications or simple mice. + + To compile this driver as a module, choose M here: the + module will be called usbmouse. + + If even remotely unsure, say N. + +endmenu + + diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile new file mode 100644 index 0000000..8e6ab5b --- /dev/null +++ b/drivers/hid/usbhid/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the USB input drivers +# + +# Multipart objects. +usbhid-objs := hid-core.o hid-quirks.o + +# Optional parts of multipart objects. + +ifeq ($(CONFIG_USB_HIDDEV),y) + usbhid-objs += hiddev.o +endif +ifeq ($(CONFIG_HID_PID),y) + usbhid-objs += hid-pidff.o +endif +ifeq ($(CONFIG_LOGITECH_FF),y) + usbhid-objs += hid-lgff.o +endif +ifeq ($(CONFIG_PANTHERLORD_FF),y) + usbhid-objs += hid-plff.o +endif +ifeq ($(CONFIG_THRUSTMASTER_FF),y) + usbhid-objs += hid-tmff.o +endif +ifeq ($(CONFIG_ZEROPLUS_FF),y) + usbhid-objs += hid-zpff.o +endif +ifeq ($(CONFIG_HID_FF),y) + usbhid-objs += hid-ff.o +endif + +obj-$(CONFIG_USB_HID) += usbhid.o +obj-$(CONFIG_USB_KBD) += usbkbd.o +obj-$(CONFIG_USB_MOUSE) += usbmouse.o + diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c new file mode 100644 index 0000000..91d6103 --- /dev/null +++ b/drivers/hid/usbhid/hid-core.c @@ -0,0 +1,1114 @@ +/* + * USB HID support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include "usbhid.h" + +/* + * Version Information + */ + +#define DRIVER_VERSION "v2.6" +#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina" +#define DRIVER_DESC "USB HID core driver" +#define DRIVER_LICENSE "GPL" + +static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", + "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; +/* + * Module parameters. + */ + +static unsigned int hid_mousepoll_interval; +module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); +MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); + +/* Quirks specified at module load time */ +static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; +module_param_array_named(quirks, quirks_param, charp, NULL, 0444); +MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " + " quirks=vendorID:productID:quirks" + " where vendorID, productID, and quirks are all in" + " 0x-prefixed hex"); +/* + * Input submission and I/O error handler. + */ + +static void hid_io_error(struct hid_device *hid); + +/* Start up the input URB */ +static int hid_start_in(struct hid_device *hid) +{ + unsigned long flags; + int rc = 0; + struct usbhid_device *usbhid = hid->driver_data; + + spin_lock_irqsave(&usbhid->inlock, flags); + if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) && + !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { + rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); + if (rc != 0) + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + } + spin_unlock_irqrestore(&usbhid->inlock, flags); + return rc; +} + +/* I/O retry timer routine */ +static void hid_retry_timeout(unsigned long _hid) +{ + struct hid_device *hid = (struct hid_device *) _hid; + struct usbhid_device *usbhid = hid->driver_data; + + dev_dbg(&usbhid->intf->dev, "retrying intr urb\n"); + if (hid_start_in(hid)) + hid_io_error(hid); +} + +/* Workqueue routine to reset the device or clear a halt */ +static void hid_reset(struct work_struct *work) +{ + struct usbhid_device *usbhid = + container_of(work, struct usbhid_device, reset_work); + struct hid_device *hid = usbhid->hid; + int rc_lock, rc = 0; + + if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { + dev_dbg(&usbhid->intf->dev, "clear halt\n"); + rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); + clear_bit(HID_CLEAR_HALT, &usbhid->iofl); + hid_start_in(hid); + } + + else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { + dev_dbg(&usbhid->intf->dev, "resetting device\n"); + rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); + if (rc_lock >= 0) { + rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); + if (rc_lock) + usb_unlock_device(hid_to_usb_dev(hid)); + } + clear_bit(HID_RESET_PENDING, &usbhid->iofl); + } + + switch (rc) { + case 0: + if (!test_bit(HID_IN_RUNNING, &usbhid->iofl)) + hid_io_error(hid); + break; + default: + err("can't reset device, %s-%s/input%d, status %d", + hid_to_usb_dev(hid)->bus->bus_name, + hid_to_usb_dev(hid)->devpath, + usbhid->ifnum, rc); + /* FALLTHROUGH */ + case -EHOSTUNREACH: + case -ENODEV: + case -EINTR: + break; + } +} + +/* Main I/O error handler */ +static void hid_io_error(struct hid_device *hid) +{ + unsigned long flags; + struct usbhid_device *usbhid = hid->driver_data; + + spin_lock_irqsave(&usbhid->inlock, flags); + + /* Stop when disconnected */ + if (usb_get_intfdata(usbhid->intf) == NULL) + goto done; + + /* If it has been a while since the last error, we'll assume + * this a brand new error and reset the retry timeout. */ + if (time_after(jiffies, usbhid->stop_retry + HZ/2)) + usbhid->retry_delay = 0; + + /* When an error occurs, retry at increasing intervals */ + if (usbhid->retry_delay == 0) { + usbhid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */ + usbhid->stop_retry = jiffies + msecs_to_jiffies(1000); + } else if (usbhid->retry_delay < 100) + usbhid->retry_delay *= 2; + + if (time_after(jiffies, usbhid->stop_retry)) { + + /* Retries failed, so do a port reset */ + if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { + schedule_work(&usbhid->reset_work); + goto done; + } + } + + mod_timer(&usbhid->io_retry, + jiffies + msecs_to_jiffies(usbhid->retry_delay)); +done: + spin_unlock_irqrestore(&usbhid->inlock, flags); +} + +/* + * Input interrupt completion handler. + */ + +static void hid_irq_in(struct urb *urb) +{ + struct hid_device *hid = urb->context; + struct usbhid_device *usbhid = hid->driver_data; + int status; + + switch (urb->status) { + case 0: /* success */ + usbhid->retry_delay = 0; + hid_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, + urb->actual_length, 1); + break; + case -EPIPE: /* stall */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + set_bit(HID_CLEAR_HALT, &usbhid->iofl); + schedule_work(&usbhid->reset_work); + return; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: /* unplug */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + return; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ETIME: /* protocol error or unplug */ + case -ETIMEDOUT: /* Should never happen, but... */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + hid_io_error(hid); + return; + default: /* error */ + warn("input irq status %d received", urb->status); + } + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + if (status != -EPERM) { + err("can't resubmit intr, %s-%s/input%d, status %d", + hid_to_usb_dev(hid)->bus->bus_name, + hid_to_usb_dev(hid)->devpath, + usbhid->ifnum, status); + hid_io_error(hid); + } + } +} + +static int hid_submit_out(struct hid_device *hid) +{ + struct hid_report *report; + struct usbhid_device *usbhid = hid->driver_data; + + report = usbhid->out[usbhid->outtail]; + + hid_output_report(report, usbhid->outbuf); + usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); + usbhid->urbout->dev = hid_to_usb_dev(hid); + + dbg("submitting out urb"); + + if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { + err("usb_submit_urb(out) failed"); + return -1; + } + + return 0; +} + +static int hid_submit_ctrl(struct hid_device *hid) +{ + struct hid_report *report; + unsigned char dir; + int len; + struct usbhid_device *usbhid = hid->driver_data; + + report = usbhid->ctrl[usbhid->ctrltail].report; + dir = usbhid->ctrl[usbhid->ctrltail].dir; + + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + if (dir == USB_DIR_OUT) { + hid_output_report(report, usbhid->ctrlbuf); + usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); + usbhid->urbctrl->transfer_buffer_length = len; + } else { + int maxpacket, padlen; + + usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); + maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); + if (maxpacket > 0) { + padlen = (len + maxpacket - 1) / maxpacket; + padlen *= maxpacket; + if (padlen > usbhid->bufsize) + padlen = usbhid->bufsize; + } else + padlen = 0; + usbhid->urbctrl->transfer_buffer_length = padlen; + } + usbhid->urbctrl->dev = hid_to_usb_dev(hid); + + usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; + usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT; + usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id); + usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); + usbhid->cr->wLength = cpu_to_le16(len); + + dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u", + usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", + usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); + + if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { + err("usb_submit_urb(ctrl) failed"); + return -1; + } + + return 0; +} + +/* + * Output interrupt completion handler. + */ + +static void hid_irq_out(struct urb *urb) +{ + struct hid_device *hid = urb->context; + struct usbhid_device *usbhid = hid->driver_data; + unsigned long flags; + int unplug = 0; + + switch (urb->status) { + case 0: /* success */ + break; + case -ESHUTDOWN: /* unplug */ + unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + break; + default: /* error */ + warn("output irq status %d received", urb->status); + } + + spin_lock_irqsave(&usbhid->outlock, flags); + + if (unplug) + usbhid->outtail = usbhid->outhead; + else + usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); + + if (usbhid->outhead != usbhid->outtail) { + if (hid_submit_out(hid)) { + clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + wake_up(&hid->wait); + } + spin_unlock_irqrestore(&usbhid->outlock, flags); + return; + } + + clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + spin_unlock_irqrestore(&usbhid->outlock, flags); + wake_up(&hid->wait); +} + +/* + * Control pipe completion handler. + */ + +static void hid_ctrl(struct urb *urb) +{ + struct hid_device *hid = urb->context; + struct usbhid_device *usbhid = hid->driver_data; + unsigned long flags; + int unplug = 0; + + spin_lock_irqsave(&usbhid->ctrllock, flags); + + switch (urb->status) { + case 0: /* success */ + if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) + hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, + urb->transfer_buffer, urb->actual_length, 0); + break; + case -ESHUTDOWN: /* unplug */ + unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -EPIPE: /* report not available */ + break; + default: /* error */ + warn("ctrl urb status %d received", urb->status); + } + + if (unplug) + usbhid->ctrltail = usbhid->ctrlhead; + else + usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); + + if (usbhid->ctrlhead != usbhid->ctrltail) { + if (hid_submit_ctrl(hid)) { + clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + wake_up(&hid->wait); + } + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + return; + } + + clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + wake_up(&hid->wait); +} + +void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) +{ + int head; + unsigned long flags; + struct usbhid_device *usbhid = hid->driver_data; + + if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) + return; + + if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { + + spin_lock_irqsave(&usbhid->outlock, flags); + + if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) { + spin_unlock_irqrestore(&usbhid->outlock, flags); + warn("output queue full"); + return; + } + + usbhid->out[usbhid->outhead] = report; + usbhid->outhead = head; + + if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) + if (hid_submit_out(hid)) + clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + + spin_unlock_irqrestore(&usbhid->outlock, flags); + return; + } + + spin_lock_irqsave(&usbhid->ctrllock, flags); + + if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) { + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + warn("control queue full"); + return; + } + + usbhid->ctrl[usbhid->ctrlhead].report = report; + usbhid->ctrl[usbhid->ctrlhead].dir = dir; + usbhid->ctrlhead = head; + + if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) + if (hid_submit_ctrl(hid)) + clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + + spin_unlock_irqrestore(&usbhid->ctrllock, flags); +} + +static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct hid_device *hid = dev->private; + struct hid_field *field; + int offset; + + if (type == EV_FF) + return input_ff_event(dev, type, code, value); + + if (type != EV_LED) + return -1; + + if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { + warn("event field not found"); + return -1; + } + + hid_set_field(field, offset, value); + usbhid_submit_report(hid, field->report, USB_DIR_OUT); + + return 0; +} + +int usbhid_wait_io(struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) && + !test_bit(HID_OUT_RUNNING, &usbhid->iofl)), + 10*HZ)) { + dbg("timeout waiting for ctrl or out queue to clear"); + return -1; + } + + return 0; +} + +static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report, + ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, + unsigned char type, void *buf, int size) +{ + int result, retries = 4; + + memset(buf, 0, size); + + do { + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT); + retries--; + } while (result < size && retries); + return result; +} + +int usbhid_open(struct hid_device *hid) +{ + ++hid->open; + if (hid_start_in(hid)) + hid_io_error(hid); + return 0; +} + +void usbhid_close(struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (!--hid->open) + usb_kill_urb(usbhid->urbin); +} + +/* + * Initialize all reports + */ + +void usbhid_init_reports(struct hid_device *hid) +{ + struct hid_report *report; + struct usbhid_device *usbhid = hid->driver_data; + int err, ret; + + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) + usbhid_submit_report(hid, report, USB_DIR_IN); + + list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + usbhid_submit_report(hid, report, USB_DIR_IN); + + err = 0; + ret = usbhid_wait_io(hid); + while (ret) { + err |= ret; + if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) + usb_kill_urb(usbhid->urbctrl); + if (test_bit(HID_OUT_RUNNING, &usbhid->iofl)) + usb_kill_urb(usbhid->urbout); + ret = usbhid_wait_io(hid); + } + + if (err) + warn("timeout initializing reports"); +} + +/* + * Reset LEDs which BIOS might have left on. For now, just NumLock (0x01). + */ +static int hid_find_field_early(struct hid_device *hid, unsigned int page, + unsigned int hid_code, struct hid_field **pfield) +{ + struct hid_report *report; + struct hid_field *field; + struct hid_usage *usage; + int i, j; + + list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) { + usage = &field->usage[j]; + if ((usage->hid & HID_USAGE_PAGE) == page && + (usage->hid & 0xFFFF) == hid_code) { + *pfield = field; + return j; + } + } + } + } + return -1; +} + +static void usbhid_set_leds(struct hid_device *hid) +{ + struct hid_field *field; + int offset; + + if ((offset = hid_find_field_early(hid, HID_UP_LED, 0x01, &field)) != -1) { + hid_set_field(field, offset, 0); + usbhid_submit_report(hid, field->report, USB_DIR_OUT); + } +} + +/* + * Traverse the supplied list of reports and find the longest + */ +static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max) +{ + struct hid_report *report; + int size; + + list_for_each_entry(report, &hid->report_enum[type].report_list, list) { + size = ((report->size - 1) >> 3) + 1; + if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered) + size++; + if (*max < size) + *max = size; + } +} + +static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma))) + return -1; + if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma))) + return -1; + if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma))) + return -1; + if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma))) + return -1; + + return 0; +} + +static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (usbhid->inbuf) + usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); + if (usbhid->outbuf) + usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); + if (usbhid->cr) + usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); + if (usbhid->ctrlbuf) + usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); +} + +/* + * Cherry Cymotion keyboard have an invalid HID report descriptor, + * that needs fixing before we can parse it. + */ + +static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize) +{ + if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { + info("Fixing up Cherry Cymotion report descriptor"); + rdesc[11] = rdesc[16] = 0xff; + rdesc[12] = rdesc[17] = 0x03; + } +} + +/* + * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller + * to "operational". Without this, the ps3 controller will not report any + * events. + */ +static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) +{ + int result; + char *buf = kmalloc(18, GFP_KERNEL); + + if (!buf) + return; + + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + (3 << 8) | 0xf2, ifnum, buf, 17, + USB_CTRL_GET_TIMEOUT); + + if (result < 0) + err("%s failed: %d\n", __func__, result); + + kfree(buf); +} + +/* + * Certain Logitech keyboards send in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 90 && rdesc[83] == 0x26 + && rdesc[84] == 0x8c + && rdesc[85] == 0x02) { + info("Fixing up Logitech keyboard report descriptor"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + +static struct hid_device *usb_hid_configure(struct usb_interface *intf) +{ + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_device *dev = interface_to_usbdev (intf); + struct hid_descriptor *hdesc; + struct hid_device *hid; + u32 quirks = 0; + unsigned rsize = 0; + char *rdesc; + int n, len, insize = 0; + struct usbhid_device *usbhid; + + quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Many keyboards and mice don't like to be polled for reports, + * so we will always set the HID_QUIRK_NOGET flag for them. */ + if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) { + if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD || + interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) + quirks |= HID_QUIRK_NOGET; + } + + if (quirks & HID_QUIRK_IGNORE) + return NULL; + + if ((quirks & HID_QUIRK_IGNORE_MOUSE) && + (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) + return NULL; + + + if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && + (!interface->desc.bNumEndpoints || + usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { + dbg("class descriptor not present\n"); + return NULL; + } + + for (n = 0; n < hdesc->bNumDescriptors; n++) + if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) + rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); + + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dbg("weird size of report descriptor (%u)", rsize); + return NULL; + } + + if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) { + dbg("couldn't allocate rdesc memory"); + return NULL; + } + + hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); + + if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { + dbg("reading report descriptor failed"); + kfree(rdesc); + return NULL; + } + + if ((quirks & HID_QUIRK_CYMOTION)) + hid_fixup_cymotion_descriptor(rdesc, rsize); + + if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR) + hid_fixup_logitech_descriptor(rdesc, rsize); + +#ifdef CONFIG_HID_DEBUG + printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); + for (n = 0; n < rsize; n++) + printk(" %02x", (unsigned char) rdesc[n]); + printk("\n"); +#endif + + if (!(hid = hid_parse_report(rdesc, n))) { + dbg("parsing report descriptor failed"); + kfree(rdesc); + return NULL; + } + + kfree(rdesc); + hid->quirks = quirks; + + if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL))) + goto fail; + + hid->driver_data = usbhid; + usbhid->hid = hid; + + usbhid->bufsize = HID_MIN_BUFFER_SIZE; + hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize); + hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize); + hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize); + + if (usbhid->bufsize > HID_MAX_BUFFER_SIZE) + usbhid->bufsize = HID_MAX_BUFFER_SIZE; + + hid_find_max_report(hid, HID_INPUT_REPORT, &insize); + + if (insize > HID_MAX_BUFFER_SIZE) + insize = HID_MAX_BUFFER_SIZE; + + if (hid_alloc_buffers(dev, hid)) { + hid_free_buffers(dev, hid); + goto fail; + } + + for (n = 0; n < interface->desc.bNumEndpoints; n++) { + + struct usb_endpoint_descriptor *endpoint; + int pipe; + int interval; + + endpoint = &interface->endpoint[n].desc; + if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ + continue; + + interval = endpoint->bInterval; + + /* Change the polling interval of mice. */ + if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) + interval = hid_mousepoll_interval; + + if (usb_endpoint_dir_in(endpoint)) { + if (usbhid->urbin) + continue; + if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize, + hid_irq_in, hid, interval); + usbhid->urbin->transfer_dma = usbhid->inbuf_dma; + usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } else { + if (usbhid->urbout) + continue; + if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress); + usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0, + hid_irq_out, hid, interval); + usbhid->urbout->transfer_dma = usbhid->outbuf_dma; + usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + } + + if (!usbhid->urbin) { + err("couldn't find an input interrupt endpoint"); + goto fail; + } + + init_waitqueue_head(&hid->wait); + + INIT_WORK(&usbhid->reset_work, hid_reset); + setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); + + spin_lock_init(&usbhid->inlock); + spin_lock_init(&usbhid->outlock); + spin_lock_init(&usbhid->ctrllock); + + hid->version = le16_to_cpu(hdesc->bcdHID); + hid->country = hdesc->bCountryCode; + hid->dev = &intf->dev; + usbhid->intf = intf; + usbhid->ifnum = interface->desc.bInterfaceNumber; + + hid->name[0] = 0; + + if (dev->manufacturer) + strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(hid->name, " ", sizeof(hid->name)); + strlcat(hid->name, dev->product, sizeof(hid->name)); + } + + if (!strlen(hid->name)) + snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + hid->bus = BUS_USB; + hid->vendor = le16_to_cpu(dev->descriptor.idVendor); + hid->product = le16_to_cpu(dev->descriptor.idProduct); + + usb_make_path(dev, hid->phys, sizeof(hid->phys)); + strlcat(hid->phys, "/input", sizeof(hid->phys)); + len = strlen(hid->phys); + if (len < sizeof(hid->phys) - 1) + snprintf(hid->phys + len, sizeof(hid->phys) - len, + "%d", intf->altsetting[0].desc.bInterfaceNumber); + + if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) + hid->uniq[0] = 0; + + usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); + if (!usbhid->urbctrl) + goto fail; + + usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr, + usbhid->ctrlbuf, 1, hid_ctrl, hid); + usbhid->urbctrl->setup_dma = usbhid->cr_dma; + usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma; + usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); + hid->hidinput_input_event = usb_hidinput_input_event; + hid->hid_open = usbhid_open; + hid->hid_close = usbhid_close; +#ifdef CONFIG_USB_HIDDEV + hid->hiddev_hid_event = hiddev_hid_event; + hid->hiddev_report_event = hiddev_report_event; +#endif + return hid; + +fail: + usb_free_urb(usbhid->urbin); + usb_free_urb(usbhid->urbout); + usb_free_urb(usbhid->urbctrl); + hid_free_buffers(dev, hid); + hid_free_device(hid); + + return NULL; +} + +static void hid_disconnect(struct usb_interface *intf) +{ + struct hid_device *hid = usb_get_intfdata (intf); + struct usbhid_device *usbhid; + + if (!hid) + return; + + usbhid = hid->driver_data; + + spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ + usb_set_intfdata(intf, NULL); + spin_unlock_irq(&usbhid->inlock); + usb_kill_urb(usbhid->urbin); + usb_kill_urb(usbhid->urbout); + usb_kill_urb(usbhid->urbctrl); + + del_timer_sync(&usbhid->io_retry); + flush_scheduled_work(); + + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hid); + if (hid->claimed & HID_CLAIMED_HIDDEV) + hiddev_disconnect(hid); + + usb_free_urb(usbhid->urbin); + usb_free_urb(usbhid->urbctrl); + usb_free_urb(usbhid->urbout); + + hid_free_buffers(hid_to_usb_dev(hid), hid); + hid_free_device(hid); +} + +static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct hid_device *hid; + char path[64]; + int i; + char *c; + + dbg("HID probe called for ifnum %d", + intf->altsetting->desc.bInterfaceNumber); + + if (!(hid = usb_hid_configure(intf))) + return -ENODEV; + + usbhid_init_reports(hid); + hid_dump_device(hid); + if (hid->quirks & HID_QUIRK_RESET_LEDS) + usbhid_set_leds(hid); + + if (!hidinput_connect(hid)) + hid->claimed |= HID_CLAIMED_INPUT; + if (!hiddev_connect(hid)) + hid->claimed |= HID_CLAIMED_HIDDEV; + + usb_set_intfdata(intf, hid); + + if (!hid->claimed) { + printk ("HID device not claimed by input or hiddev\n"); + hid_disconnect(intf); + return -ENODEV; + } + + if ((hid->claimed & HID_CLAIMED_INPUT)) + hid_ff_init(hid); + + if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) + hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), + intf->cur_altsetting->desc.bInterfaceNumber); + + printk(KERN_INFO); + + if (hid->claimed & HID_CLAIMED_INPUT) + printk("input"); + if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV)) + printk(","); + if (hid->claimed & HID_CLAIMED_HIDDEV) + printk("hiddev%d", hid->minor); + + c = "Device"; + for (i = 0; i < hid->maxcollection; i++) { + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && + (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK && + (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) { + c = hid_types[hid->collection[i].usage & 0xffff]; + break; + } + } + + usb_make_path(interface_to_usbdev(intf), path, 63); + + printk(": USB HID v%x.%02x %s [%s] on %s\n", + hid->version >> 8, hid->version & 0xff, c, hid->name, path); + + return 0; +} + +static int hid_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct hid_device *hid = usb_get_intfdata (intf); + struct usbhid_device *usbhid = hid->driver_data; + + spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ + set_bit(HID_SUSPENDED, &usbhid->iofl); + spin_unlock_irq(&usbhid->inlock); + del_timer(&usbhid->io_retry); + usb_kill_urb(usbhid->urbin); + dev_dbg(&intf->dev, "suspend\n"); + return 0; +} + +static int hid_resume(struct usb_interface *intf) +{ + struct hid_device *hid = usb_get_intfdata (intf); + struct usbhid_device *usbhid = hid->driver_data; + int status; + + clear_bit(HID_SUSPENDED, &usbhid->iofl); + usbhid->retry_delay = 0; + status = hid_start_in(hid); + dev_dbg(&intf->dev, "resume status %d\n", status); + return status; +} + +/* Treat USB reset pretty much the same as suspend/resume */ +static void hid_pre_reset(struct usb_interface *intf) +{ + /* FIXME: What if the interface is already suspended? */ + hid_suspend(intf, PMSG_ON); +} + +static void hid_post_reset(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev (intf); + + hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); + /* FIXME: Any more reinitialization needed? */ + + hid_resume(intf); +} + +static struct usb_device_id hid_usb_ids [] = { + { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, + .bInterfaceClass = USB_INTERFACE_CLASS_HID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, hid_usb_ids); + +static struct usb_driver hid_driver = { + .name = "usbhid", + .probe = hid_probe, + .disconnect = hid_disconnect, + .suspend = hid_suspend, + .resume = hid_resume, + .pre_reset = hid_pre_reset, + .post_reset = hid_post_reset, + .id_table = hid_usb_ids, +}; + +static int __init hid_init(void) +{ + int retval; + retval = usbhid_quirks_init(quirks_param); + if (retval) + goto usbhid_quirks_init_fail; + retval = hiddev_init(); + if (retval) + goto hiddev_init_fail; + retval = usb_register(&hid_driver); + if (retval) + goto usb_register_fail; + info(DRIVER_VERSION ":" DRIVER_DESC); + + return 0; +usb_register_fail: + hiddev_exit(); +hiddev_init_fail: + usbhid_quirks_exit(); +usbhid_quirks_init_fail: + return retval; +} + +static void __exit hid_exit(void) +{ + usb_deregister(&hid_driver); + hiddev_exit(); + usbhid_quirks_exit(); +} + +module_init(hid_init); +module_exit(hid_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c new file mode 100644 index 0000000..23431fb --- /dev/null +++ b/drivers/hid/usbhid/hid-ff.c @@ -0,0 +1,91 @@ +/* + * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $ + * + * Force feedback support for hid devices. + * Not all hid devices use the same protocol. For example, some use PID, + * other use their own proprietary procotol. + * + * Copyright (c) 2002-2004 Johann Deneux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include + +#undef DEBUG +#include + +#include +#include "usbhid.h" + +/* + * This table contains pointers to initializers. To add support for new + * devices, you need to add the USB vendor and product ids here. + */ +struct hid_ff_initializer { + u16 idVendor; + u16 idProduct; + int (*init)(struct hid_device*); +}; + +/* + * We try pidff when no other driver is found because PID is the + * standards compliant way of implementing force feedback in HID. + * pidff_init() will quickly abort if the device doesn't appear to + * be a PID device + */ +static struct hid_ff_initializer inits[] = { +#ifdef CONFIG_LOGITECH_FF + { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ + { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ + { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ + { 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */ + { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ + { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ + { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ +#endif +#ifdef CONFIG_PANTHERLORD_FF + { 0x810, 0x0001, hid_plff_init }, +#endif +#ifdef CONFIG_THRUSTMASTER_FF + { 0x44f, 0xb300, hid_tmff_init }, + { 0x44f, 0xb304, hid_tmff_init }, +#endif +#ifdef CONFIG_ZEROPLUS_FF + { 0xc12, 0x0005, hid_zpff_init }, + { 0xc12, 0x0030, hid_zpff_init }, +#endif + { 0, 0, hid_pidff_init} /* Matches anything */ +}; + +int hid_ff_init(struct hid_device* hid) +{ + struct hid_ff_initializer *init; + int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor); + int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct); + + for (init = inits; init->idVendor; init++) + if (init->idVendor == vendor && init->idProduct == product) + break; + + return init->init(hid); +} +EXPORT_SYMBOL_GPL(hid_ff_init); + diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c new file mode 100644 index 0000000..92d2553 --- /dev/null +++ b/drivers/hid/usbhid/hid-lgff.c @@ -0,0 +1,151 @@ +/* + * Force feedback support for hid-compliant for some of the devices from + * Logitech, namely: + * - WingMan Cordless RumblePad + * - WingMan Force 3D + * + * Copyright (c) 2002-2004 Johann Deneux + * Copyright (c) 2006 Anssi Hannula + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include +#include +#include +#include "usbhid.h" + +struct dev_type { + u16 idVendor; + u16 idProduct; + const signed short *ff; +}; + +static const signed short ff_rumble[] = { + FF_RUMBLE, + -1 +}; + +static const signed short ff_joystick[] = { + FF_CONSTANT, + -1 +}; + +static const struct dev_type devices[] = { + { 0x046d, 0xc211, ff_rumble }, + { 0x046d, 0xc219, ff_rumble }, + { 0x046d, 0xc283, ff_joystick }, + { 0x046d, 0xc286, ff_joystick }, + { 0x046d, 0xc294, ff_joystick }, + { 0x046d, 0xc295, ff_joystick }, + { 0x046d, 0xca03, ff_joystick }, +}; + +static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + int x, y; + unsigned int left, right; + +#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff + + switch (effect->type) { + case FF_CONSTANT: + x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */ + y = effect->u.ramp.end_level + 0x7f; + CLAMP(x); + CLAMP(y); + report->field[0]->value[0] = 0x51; + report->field[0]->value[1] = 0x08; + report->field[0]->value[2] = x; + report->field[0]->value[3] = y; + dbg("(x, y)=(%04x, %04x)", x, y); + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + + case FF_RUMBLE: + right = effect->u.rumble.strong_magnitude; + left = effect->u.rumble.weak_magnitude; + right = right * 0xff / 0xffff; + left = left * 0xff / 0xffff; + CLAMP(left); + CLAMP(right); + report->field[0]->value[0] = 0x42; + report->field[0]->value[1] = 0x00; + report->field[0]->value[2] = left; + report->field[0]->value[3] = right; + dbg("(left, right)=(%04x, %04x)", left, right); + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + } + return 0; +} + +int hid_lgff_init(struct hid_device* hid) +{ + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; + const signed short *ff_bits = ff_joystick; + int error; + int i; + + /* Find the report to use */ + if (list_empty(report_list)) { + err("No output report found"); + return -1; + } + + /* Check that the report looks ok */ + report = list_entry(report_list->next, struct hid_report, list); + if (!report) { + err("NULL output report"); + return -1; + } + + field = report->field[0]; + if (!field) { + err("NULL field"); + return -1; + } + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + if (dev->id.vendor == devices[i].idVendor && + dev->id.product == devices[i].idProduct) { + ff_bits = devices[i].ff; + break; + } + } + + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], dev->ffbit); + + error = input_ff_create_memless(dev, NULL, hid_lgff_play); + if (error) + return error; + + printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); + + return 0; +} diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c new file mode 100644 index 0000000..f5a90e9 --- /dev/null +++ b/drivers/hid/usbhid/hid-pidff.c @@ -0,0 +1,1331 @@ +/* + * Force feedback driver for USB HID PID compliant devices + * + * Copyright (c) 2005, 2006 Anssi Hannula + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg) + +#include +#include + +#include + +#include "usbhid.h" + +#define PID_EFFECTS_MAX 64 + +/* Report usage table used to put reports into an array */ + +#define PID_SET_EFFECT 0 +#define PID_EFFECT_OPERATION 1 +#define PID_DEVICE_GAIN 2 +#define PID_POOL 3 +#define PID_BLOCK_LOAD 4 +#define PID_BLOCK_FREE 5 +#define PID_DEVICE_CONTROL 6 +#define PID_CREATE_NEW_EFFECT 7 + +#define PID_REQUIRED_REPORTS 7 + +#define PID_SET_ENVELOPE 8 +#define PID_SET_CONDITION 9 +#define PID_SET_PERIODIC 10 +#define PID_SET_CONSTANT 11 +#define PID_SET_RAMP 12 +static const u8 pidff_reports[] = { + 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab, + 0x5a, 0x5f, 0x6e, 0x73, 0x74 +}; + +/* device_control is really 0x95, but 0x96 specified as it is the usage of +the only field in that report */ + +/* Value usage tables used to put fields and values into arrays */ + +#define PID_EFFECT_BLOCK_INDEX 0 + +#define PID_DURATION 1 +#define PID_GAIN 2 +#define PID_TRIGGER_BUTTON 3 +#define PID_TRIGGER_REPEAT_INT 4 +#define PID_DIRECTION_ENABLE 5 +#define PID_START_DELAY 6 +static const u8 pidff_set_effect[] = { + 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7 +}; + +#define PID_ATTACK_LEVEL 1 +#define PID_ATTACK_TIME 2 +#define PID_FADE_LEVEL 3 +#define PID_FADE_TIME 4 +static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e }; + +#define PID_PARAM_BLOCK_OFFSET 1 +#define PID_CP_OFFSET 2 +#define PID_POS_COEFFICIENT 3 +#define PID_NEG_COEFFICIENT 4 +#define PID_POS_SATURATION 5 +#define PID_NEG_SATURATION 6 +#define PID_DEAD_BAND 7 +static const u8 pidff_set_condition[] = { + 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65 +}; + +#define PID_MAGNITUDE 1 +#define PID_OFFSET 2 +#define PID_PHASE 3 +#define PID_PERIOD 4 +static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 }; +static const u8 pidff_set_constant[] = { 0x22, 0x70 }; + +#define PID_RAMP_START 1 +#define PID_RAMP_END 2 +static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 }; + +#define PID_RAM_POOL_AVAILABLE 1 +static const u8 pidff_block_load[] = { 0x22, 0xac }; + +#define PID_LOOP_COUNT 1 +static const u8 pidff_effect_operation[] = { 0x22, 0x7c }; + +static const u8 pidff_block_free[] = { 0x22 }; + +#define PID_DEVICE_GAIN_FIELD 0 +static const u8 pidff_device_gain[] = { 0x7e }; + +#define PID_RAM_POOL_SIZE 0 +#define PID_SIMULTANEOUS_MAX 1 +#define PID_DEVICE_MANAGED_POOL 2 +static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 }; + +/* Special field key tables used to put special field keys into arrays */ + +#define PID_ENABLE_ACTUATORS 0 +#define PID_RESET 1 +static const u8 pidff_device_control[] = { 0x97, 0x9a }; + +#define PID_CONSTANT 0 +#define PID_RAMP 1 +#define PID_SQUARE 2 +#define PID_SINE 3 +#define PID_TRIANGLE 4 +#define PID_SAW_UP 5 +#define PID_SAW_DOWN 6 +#define PID_SPRING 7 +#define PID_DAMPER 8 +#define PID_INERTIA 9 +#define PID_FRICTION 10 +static const u8 pidff_effect_types[] = { + 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x40, 0x41, 0x42, 0x43 +}; + +#define PID_BLOCK_LOAD_SUCCESS 0 +#define PID_BLOCK_LOAD_FULL 1 +static const u8 pidff_block_load_status[] = { 0x8c, 0x8d }; + +#define PID_EFFECT_START 0 +#define PID_EFFECT_STOP 1 +static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b }; + +struct pidff_usage { + struct hid_field *field; + s32 *value; +}; + +struct pidff_device { + struct hid_device *hid; + + struct hid_report *reports[sizeof(pidff_reports)]; + + struct pidff_usage set_effect[sizeof(pidff_set_effect)]; + struct pidff_usage set_envelope[sizeof(pidff_set_envelope)]; + struct pidff_usage set_condition[sizeof(pidff_set_condition)]; + struct pidff_usage set_periodic[sizeof(pidff_set_periodic)]; + struct pidff_usage set_constant[sizeof(pidff_set_constant)]; + struct pidff_usage set_ramp[sizeof(pidff_set_ramp)]; + + struct pidff_usage device_gain[sizeof(pidff_device_gain)]; + struct pidff_usage block_load[sizeof(pidff_block_load)]; + struct pidff_usage pool[sizeof(pidff_pool)]; + struct pidff_usage effect_operation[sizeof(pidff_effect_operation)]; + struct pidff_usage block_free[sizeof(pidff_block_free)]; + + /* Special field is a field that is not composed of + usage<->value pairs that pidff_usage values are */ + + /* Special field in create_new_effect */ + struct hid_field *create_new_effect_type; + + /* Special fields in set_effect */ + struct hid_field *set_effect_type; + struct hid_field *effect_direction; + + /* Special field in device_control */ + struct hid_field *device_control; + + /* Special field in block_load */ + struct hid_field *block_load_status; + + /* Special field in effect_operation */ + struct hid_field *effect_operation_status; + + int control_id[sizeof(pidff_device_control)]; + int type_id[sizeof(pidff_effect_types)]; + int status_id[sizeof(pidff_block_load_status)]; + int operation_id[sizeof(pidff_effect_operation_status)]; + + int pid_id[PID_EFFECTS_MAX]; +}; + +/* + * Scale an unsigned value with range 0..max for the given field + */ +static int pidff_rescale(int i, int max, struct hid_field *field) +{ + return i * (field->logical_maximum - field->logical_minimum) / max + + field->logical_minimum; +} + +/* + * Scale a signed value in range -0x8000..0x7fff for the given field + */ +static int pidff_rescale_signed(int i, struct hid_field *field) +{ + return i == 0 ? 0 : i > + 0 ? i * field->logical_maximum / 0x7fff : i * + field->logical_minimum / -0x8000; +} + +static void pidff_set(struct pidff_usage *usage, u16 value) +{ + usage->value[0] = pidff_rescale(value, 0xffff, usage->field); + debug("calculated from %d to %d", value, usage->value[0]); +} + +static void pidff_set_signed(struct pidff_usage *usage, s16 value) +{ + if (usage->field->logical_minimum < 0) + usage->value[0] = pidff_rescale_signed(value, usage->field); + else { + if (value < 0) + usage->value[0] = + pidff_rescale(-value, 0x8000, usage->field); + else + usage->value[0] = + pidff_rescale(value, 0x7fff, usage->field); + } + debug("calculated from %d to %d", value, usage->value[0]); +} + +/* + * Send envelope report to the device + */ +static void pidff_set_envelope_report(struct pidff_device *pidff, + struct ff_envelope *envelope) +{ + pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + pidff->set_envelope[PID_ATTACK_LEVEL].value[0] = + pidff_rescale(envelope->attack_level > + 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff, + pidff->set_envelope[PID_ATTACK_LEVEL].field); + pidff->set_envelope[PID_FADE_LEVEL].value[0] = + pidff_rescale(envelope->fade_level > + 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff, + pidff->set_envelope[PID_FADE_LEVEL].field); + + pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; + pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; + + debug("attack %u => %d", envelope->attack_level, + pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], + USB_DIR_OUT); +} + +/* + * Test if the new envelope differs from old one + */ +static int pidff_needs_set_envelope(struct ff_envelope *envelope, + struct ff_envelope *old) +{ + return envelope->attack_level != old->attack_level || + envelope->fade_level != old->fade_level || + envelope->attack_length != old->attack_length || + envelope->fade_length != old->fade_length; +} + +/* + * Send constant force report to the device + */ +static void pidff_set_constant_force_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], + effect->u.constant.level); + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], + USB_DIR_OUT); +} + +/* + * Test if the constant parameters have changed between effects + */ +static int pidff_needs_set_constant(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->u.constant.level != old->u.constant.level; +} + +/* + * Send set effect report to the device + */ +static void pidff_set_effect_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff->set_effect_type->value[0] = + pidff->create_new_effect_type->value[0]; + pidff->set_effect[PID_DURATION].value[0] = effect->replay.length; + pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button; + pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = + effect->trigger.interval; + pidff->set_effect[PID_GAIN].value[0] = + pidff->set_effect[PID_GAIN].field->logical_maximum; + pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; + pidff->effect_direction->value[0] = + pidff_rescale(effect->direction, 0xffff, + pidff->effect_direction); + pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], + USB_DIR_OUT); +} + +/* + * Test if the values used in set_effect have changed + */ +static int pidff_needs_set_effect(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->replay.length != old->replay.length || + effect->trigger.interval != old->trigger.interval || + effect->trigger.button != old->trigger.button || + effect->direction != old->direction || + effect->replay.delay != old->replay.delay; +} + +/* + * Send periodic effect report to the device + */ +static void pidff_set_periodic_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE], + effect->u.periodic.magnitude); + pidff_set_signed(&pidff->set_periodic[PID_OFFSET], + effect->u.periodic.offset); + pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); + pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], + USB_DIR_OUT); + +} + +/* + * Test if periodic effect parameters have changed + */ +static int pidff_needs_set_periodic(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->u.periodic.magnitude != old->u.periodic.magnitude || + effect->u.periodic.offset != old->u.periodic.offset || + effect->u.periodic.phase != old->u.periodic.phase || + effect->u.periodic.period != old->u.periodic.period; +} + +/* + * Send condition effect reports to the device + */ +static void pidff_set_condition_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + int i; + + pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + for (i = 0; i < 2; i++) { + pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i; + pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET], + effect->u.condition[i].center); + pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], + effect->u.condition[i].right_coeff); + pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], + effect->u.condition[i].left_coeff); + pidff_set(&pidff->set_condition[PID_POS_SATURATION], + effect->u.condition[i].right_saturation); + pidff_set(&pidff->set_condition[PID_NEG_SATURATION], + effect->u.condition[i].left_saturation); + pidff_set(&pidff->set_condition[PID_DEAD_BAND], + effect->u.condition[i].deadband); + usbhid_wait_io(pidff->hid); + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], + USB_DIR_OUT); + } +} + +/* + * Test if condition effect parameters have changed + */ +static int pidff_needs_set_condition(struct ff_effect *effect, + struct ff_effect *old) +{ + int i; + int ret = 0; + + for (i = 0; i < 2; i++) { + struct ff_condition_effect *cond = &effect->u.condition[i]; + struct ff_condition_effect *old_cond = &old->u.condition[i]; + + ret |= cond->center != old_cond->center || + cond->right_coeff != old_cond->right_coeff || + cond->left_coeff != old_cond->left_coeff || + cond->right_saturation != old_cond->right_saturation || + cond->left_saturation != old_cond->left_saturation || + cond->deadband != old_cond->deadband; + } + + return ret; +} + +/* + * Send ramp force report to the device + */ +static void pidff_set_ramp_force_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_ramp[PID_RAMP_START], + effect->u.ramp.start_level); + pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], + effect->u.ramp.end_level); + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], + USB_DIR_OUT); +} + +/* + * Test if ramp force parameters have changed + */ +static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old) +{ + return effect->u.ramp.start_level != old->u.ramp.start_level || + effect->u.ramp.end_level != old->u.ramp.end_level; +} + +/* + * Send a request for effect upload to the device + * + * Returns 0 if device reported success, -ENOSPC if the device reported memory + * is full. Upon unknown response the function will retry for 60 times, if + * still unsuccessful -EIO is returned. + */ +static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) +{ + int j; + + pidff->create_new_effect_type->value[0] = efnum; + usbhid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], + USB_DIR_OUT); + debug("create_new_effect sent, type: %d", efnum); + + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; + pidff->block_load_status->value[0] = 0; + usbhid_wait_io(pidff->hid); + + for (j = 0; j < 60; j++) { + debug("pid_block_load requested"); + usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD], + USB_DIR_IN); + usbhid_wait_io(pidff->hid); + if (pidff->block_load_status->value[0] == + pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { + debug("device reported free memory: %d bytes", + pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? + pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); + return 0; + } + if (pidff->block_load_status->value[0] == + pidff->status_id[PID_BLOCK_LOAD_FULL]) { + debug("not enough memory free: %d bytes", + pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? + pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); + return -ENOSPC; + } + } + printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n"); + return -EIO; +} + +/* + * Play the effect with PID id n times + */ +static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) +{ + pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; + + if (n == 0) { + pidff->effect_operation_status->value[0] = + pidff->operation_id[PID_EFFECT_STOP]; + } else { + pidff->effect_operation_status->value[0] = + pidff->operation_id[PID_EFFECT_START]; + pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; + } + + usbhid_wait_io(pidff->hid); + usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], + USB_DIR_OUT); +} + +/** + * Play the effect with effect id @effect_id for @value times + */ +static int pidff_playback(struct input_dev *dev, int effect_id, int value) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_playback_pid(pidff, pidff->pid_id[effect_id], value); + + return 0; +} + +/* + * Erase effect with PID id + */ +static void pidff_erase_pid(struct pidff_device *pidff, int pid_id) +{ + pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; + usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE], + USB_DIR_OUT); +} + +/* + * Stop and erase effect with effect_id + */ +static int pidff_erase_effect(struct input_dev *dev, int effect_id) +{ + struct pidff_device *pidff = dev->ff->private; + int pid_id = pidff->pid_id[effect_id]; + + debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); + pidff_playback_pid(pidff, pid_id, 0); + pidff_erase_pid(pidff, pid_id); + + return 0; +} + +/* + * Effect upload handler + */ +static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, + struct ff_effect *old) +{ + struct pidff_device *pidff = dev->ff->private; + int type_id; + int error; + + switch (effect->type) { + case FF_CONSTANT: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_CONSTANT]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_constant(effect, old)) + pidff_set_constant_force_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.constant.envelope, + &old->u.constant.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.constant.envelope); + break; + + case FF_PERIODIC: + if (!old) { + switch (effect->u.periodic.waveform) { + case FF_SQUARE: + type_id = PID_SQUARE; + break; + case FF_TRIANGLE: + type_id = PID_TRIANGLE; + break; + case FF_SINE: + type_id = PID_SINE; + break; + case FF_SAW_UP: + type_id = PID_SAW_UP; + break; + case FF_SAW_DOWN: + type_id = PID_SAW_DOWN; + break; + default: + printk(KERN_ERR + "hid-pidff: invalid waveform\n"); + return -EINVAL; + } + + error = pidff_request_effect_upload(pidff, + pidff->type_id[type_id]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_periodic(effect, old)) + pidff_set_periodic_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.periodic.envelope, + &old->u.periodic.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.periodic.envelope); + break; + + case FF_RAMP: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_RAMP]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_ramp(effect, old)) + pidff_set_ramp_force_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.ramp.envelope, + &old->u.ramp.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.ramp.envelope); + break; + + case FF_SPRING: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_SPRING]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_FRICTION: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_FRICTION]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_DAMPER: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_DAMPER]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_INERTIA: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_INERTIA]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + default: + printk(KERN_ERR "hid-pidff: invalid type\n"); + return -EINVAL; + } + + if (!old) + pidff->pid_id[effect->id] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + debug("uploaded"); + + return 0; +} + +/* + * set_gain() handler + */ +static void pidff_set_gain(struct input_dev *dev, u16 gain) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); + usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + USB_DIR_OUT); +} + +static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) +{ + struct hid_field *field = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field; + + if (!magnitude) { + pidff_playback_pid(pidff, field->logical_minimum, 0); + return; + } + + pidff_playback_pid(pidff, field->logical_minimum, 1); + + pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum; + pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING]; + pidff->set_effect[PID_DURATION].value[0] = 0; + pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0; + pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; + pidff_set(&pidff->set_effect[PID_GAIN], magnitude); + pidff->set_effect[PID_START_DELAY].value[0] = 0; + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], + USB_DIR_OUT); +} + +/* + * pidff_set_autocenter() handler + */ +static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_autocenter(pidff, magnitude); +} + +/* + * Find fields from a report and fill a pidff_usage + */ +static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, + struct hid_report *report, int count, int strict) +{ + int i, j, k, found; + + for (k = 0; k < count; k++) { + found = 0; + for (i = 0; i < report->maxfield; i++) { + if (report->field[i]->maxusage != + report->field[i]->report_count) { + debug("maxusage and report_count do not match, " + "skipping"); + continue; + } + for (j = 0; j < report->field[i]->maxusage; j++) { + if (report->field[i]->usage[j].hid == + (HID_UP_PID | table[k])) { + debug("found %d at %d->%d", k, i, j); + usage[k].field = report->field[i]; + usage[k].value = + &report->field[i]->value[j]; + found = 1; + break; + } + } + if (found) + break; + } + if (!found && strict) { + debug("failed to locate %d", k); + return -1; + } + } + return 0; +} + +/* + * Return index into pidff_reports for the given usage + */ +static int pidff_check_usage(int usage) +{ + int i; + + for (i = 0; i < sizeof(pidff_reports); i++) + if (usage == (HID_UP_PID | pidff_reports[i])) + return i; + + return -1; +} + +/* + * Find the reports and fill pidff->reports[] + * report_type specifies either OUTPUT or FEATURE reports + */ +static void pidff_find_reports(struct hid_device *hid, int report_type, + struct pidff_device *pidff) +{ + struct hid_report *report; + int i, ret; + + list_for_each_entry(report, + &hid->report_enum[report_type].report_list, list) { + if (report->maxfield < 1) + continue; + ret = pidff_check_usage(report->field[0]->logical); + if (ret != -1) { + debug("found usage 0x%02x from field->logical", + pidff_reports[ret]); + pidff->reports[ret] = report; + continue; + } + + /* + * Sometimes logical collections are stacked to indicate + * different usages for the report and the field, in which + * case we want the usage of the parent. However, Linux HID + * implementation hides this fact, so we have to dig it up + * ourselves + */ + i = report->field[0]->usage[0].collection_index; + if (i <= 0 || + hid->collection[i - 1].type != HID_COLLECTION_LOGICAL) + continue; + ret = pidff_check_usage(hid->collection[i - 1].usage); + if (ret != -1 && !pidff->reports[ret]) { + debug("found usage 0x%02x from collection array", + pidff_reports[ret]); + pidff->reports[ret] = report; + } + } +} + +/* + * Test if the required reports have been found + */ +static int pidff_reports_ok(struct pidff_device *pidff) +{ + int i; + + for (i = 0; i <= PID_REQUIRED_REPORTS; i++) { + if (!pidff->reports[i]) { + debug("%d missing", i); + return 0; + } + } + + return 1; +} + +/* + * Find a field with a specific usage within a report + */ +static struct hid_field *pidff_find_special_field(struct hid_report *report, + int usage, int enforce_min) +{ + int i; + + for (i = 0; i < report->maxfield; i++) { + if (report->field[i]->logical == (HID_UP_PID | usage) && + report->field[i]->report_count > 0) { + if (!enforce_min || + report->field[i]->logical_minimum == 1) + return report->field[i]; + else { + printk(KERN_ERR "hid-pidff: logical_minimum " + "is not 1 as it should be\n"); + return NULL; + } + } + } + return NULL; +} + +/* + * Fill a pidff->*_id struct table + */ +static int pidff_find_special_keys(int *keys, struct hid_field *fld, + const u8 *usagetable, int count) +{ + + int i, j; + int found = 0; + + for (i = 0; i < count; i++) { + for (j = 0; j < fld->maxusage; j++) { + if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) { + keys[i] = j + 1; + found++; + break; + } + } + } + return found; +} + +#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \ + pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \ + sizeof(pidff_ ## name)) + +/* + * Find and check the special fields + */ +static int pidff_find_special_fields(struct pidff_device *pidff) +{ + debug("finding special fields"); + + pidff->create_new_effect_type = + pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT], + 0x25, 1); + pidff->set_effect_type = + pidff_find_special_field(pidff->reports[PID_SET_EFFECT], + 0x25, 1); + pidff->effect_direction = + pidff_find_special_field(pidff->reports[PID_SET_EFFECT], + 0x57, 0); + pidff->device_control = + pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL], + 0x96, 1); + pidff->block_load_status = + pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD], + 0x8b, 1); + pidff->effect_operation_status = + pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION], + 0x78, 1); + + debug("search done"); + + if (!pidff->create_new_effect_type || !pidff->set_effect_type) { + printk(KERN_ERR "hid-pidff: effect lists not found\n"); + return -1; + } + + if (!pidff->effect_direction) { + printk(KERN_ERR "hid-pidff: direction field not found\n"); + return -1; + } + + if (!pidff->device_control) { + printk(KERN_ERR "hid-pidff: device control field not found\n"); + return -1; + } + + if (!pidff->block_load_status) { + printk(KERN_ERR + "hid-pidff: block load status field not found\n"); + return -1; + } + + if (!pidff->effect_operation_status) { + printk(KERN_ERR + "hid-pidff: effect operation field not found\n"); + return -1; + } + + pidff_find_special_keys(pidff->control_id, pidff->device_control, + pidff_device_control, + sizeof(pidff_device_control)); + + PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control); + + if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type, + effect_types)) { + printk(KERN_ERR "hid-pidff: no effect types found\n"); + return -1; + } + + if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status, + block_load_status) != + sizeof(pidff_block_load_status)) { + printk(KERN_ERR + "hidpidff: block load status identifiers not found\n"); + return -1; + } + + if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status, + effect_operation_status) != + sizeof(pidff_effect_operation_status)) { + printk(KERN_ERR + "hidpidff: effect operation identifiers not found\n"); + return -1; + } + + return 0; +} + +/** + * Find the implemented effect types + */ +static int pidff_find_effects(struct pidff_device *pidff, + struct input_dev *dev) +{ + int i; + + for (i = 0; i < sizeof(pidff_effect_types); i++) { + int pidff_type = pidff->type_id[i]; + if (pidff->set_effect_type->usage[pidff_type].hid != + pidff->create_new_effect_type->usage[pidff_type].hid) { + printk(KERN_ERR "hid-pidff: " + "effect type number %d is invalid\n", i); + return -1; + } + } + + if (pidff->type_id[PID_CONSTANT]) + set_bit(FF_CONSTANT, dev->ffbit); + if (pidff->type_id[PID_RAMP]) + set_bit(FF_RAMP, dev->ffbit); + if (pidff->type_id[PID_SQUARE]) { + set_bit(FF_SQUARE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SINE]) { + set_bit(FF_SINE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_TRIANGLE]) { + set_bit(FF_TRIANGLE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SAW_UP]) { + set_bit(FF_SAW_UP, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SAW_DOWN]) { + set_bit(FF_SAW_DOWN, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SPRING]) + set_bit(FF_SPRING, dev->ffbit); + if (pidff->type_id[PID_DAMPER]) + set_bit(FF_DAMPER, dev->ffbit); + if (pidff->type_id[PID_INERTIA]) + set_bit(FF_INERTIA, dev->ffbit); + if (pidff->type_id[PID_FRICTION]) + set_bit(FF_FRICTION, dev->ffbit); + + return 0; + +} + +#define PIDFF_FIND_FIELDS(name, report, strict) \ + pidff_find_fields(pidff->name, pidff_ ## name, \ + pidff->reports[report], \ + sizeof(pidff_ ## name), strict) + +/* + * Fill and check the pidff_usages + */ +static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev) +{ + int envelope_ok = 0; + + if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) { + printk(KERN_ERR + "hid-pidff: unknown set_effect report layout\n"); + return -ENODEV; + } + + PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0); + if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) { + printk(KERN_ERR + "hid-pidff: unknown pid_block_load report layout\n"); + return -ENODEV; + } + + if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) { + printk(KERN_ERR + "hid-pidff: unknown effect_operation report layout\n"); + return -ENODEV; + } + + if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) { + printk(KERN_ERR + "hid-pidff: unknown pid_block_free report layout\n"); + return -ENODEV; + } + + if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1)) + envelope_ok = 1; + + if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev)) + return -ENODEV; + + if (!envelope_ok) { + if (test_and_clear_bit(FF_CONSTANT, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has constant effect but no envelope\n"); + if (test_and_clear_bit(FF_RAMP, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has ramp effect but no envelope\n"); + + if (test_and_clear_bit(FF_PERIODIC, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has periodic effect but no envelope\n"); + } + + if (test_bit(FF_CONSTANT, dev->ffbit) && + PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown constant effect layout\n"); + clear_bit(FF_CONSTANT, dev->ffbit); + } + + if (test_bit(FF_RAMP, dev->ffbit) && + PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) { + printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n"); + clear_bit(FF_RAMP, dev->ffbit); + } + + if ((test_bit(FF_SPRING, dev->ffbit) || + test_bit(FF_DAMPER, dev->ffbit) || + test_bit(FF_FRICTION, dev->ffbit) || + test_bit(FF_INERTIA, dev->ffbit)) && + PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown condition effect layout\n"); + clear_bit(FF_SPRING, dev->ffbit); + clear_bit(FF_DAMPER, dev->ffbit); + clear_bit(FF_FRICTION, dev->ffbit); + clear_bit(FF_INERTIA, dev->ffbit); + } + + if (test_bit(FF_PERIODIC, dev->ffbit) && + PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown periodic effect layout\n"); + clear_bit(FF_PERIODIC, dev->ffbit); + } + + PIDFF_FIND_FIELDS(pool, PID_POOL, 0); + + if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1)) + set_bit(FF_GAIN, dev->ffbit); + + return 0; +} + +/* + * Reset the device + */ +static void pidff_reset(struct pidff_device *pidff) +{ + struct hid_device *hid = pidff->hid; + int i = 0; + + pidff->device_control->value[0] = pidff->control_id[PID_RESET]; + /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ + usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + usbhid_wait_io(hid); + usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + usbhid_wait_io(hid); + + pidff->device_control->value[0] = + pidff->control_id[PID_ENABLE_ACTUATORS]; + usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + usbhid_wait_io(hid); + + /* pool report is sometimes messed up, refetch it */ + usbhid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN); + usbhid_wait_io(hid); + + if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { + int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0]; + while (sim_effects < 2) { + if (i++ > 20) { + printk(KERN_WARNING "hid-pidff: device reports " + "%d simultaneous effects\n", + sim_effects); + break; + } + debug("pid_pool requested again"); + usbhid_submit_report(hid, pidff->reports[PID_POOL], + USB_DIR_IN); + usbhid_wait_io(hid); + } + } +} + +/* + * Test if autocenter modification is using the supported method + */ +static int pidff_check_autocenter(struct pidff_device *pidff, + struct input_dev *dev) +{ + int error; + + /* + * Let's find out if autocenter modification is supported + * Specification doesn't specify anything, so we request an + * effect upload and cancel it immediately. If the approved + * effect id was one above the minimum, then we assume the first + * effect id is a built-in spring type effect used for autocenter + */ + + error = pidff_request_effect_upload(pidff, 1); + if (error) { + printk(KERN_ERR "hid-pidff: upload request failed\n"); + return error; + } + + if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] == + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) { + pidff_autocenter(pidff, 0xffff); + set_bit(FF_AUTOCENTER, dev->ffbit); + } else { + printk(KERN_NOTICE "hid-pidff: " + "device has unknown autocenter control method\n"); + } + + pidff_erase_pid(pidff, + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]); + + return 0; + +} + +/* + * Check if the device is PID and initialize it + */ +int hid_pidff_init(struct hid_device *hid) +{ + struct pidff_device *pidff; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct input_dev *dev = hidinput->input; + struct ff_device *ff; + int max_effects; + int error; + + debug("starting pid init"); + + if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { + debug("not a PID device, no output report"); + return -ENODEV; + } + + pidff = kzalloc(sizeof(*pidff), GFP_KERNEL); + if (!pidff) + return -ENOMEM; + + pidff->hid = hid; + + pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff); + pidff_find_reports(hid, HID_FEATURE_REPORT, pidff); + + if (!pidff_reports_ok(pidff)) { + debug("reports not ok, aborting"); + error = -ENODEV; + goto fail; + } + + error = pidff_init_fields(pidff, dev); + if (error) + goto fail; + + pidff_reset(pidff); + + if (test_bit(FF_GAIN, dev->ffbit)) { + pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); + usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + USB_DIR_OUT); + } + + error = pidff_check_autocenter(pidff, dev); + if (error) + goto fail; + + max_effects = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum - + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + + 1; + debug("max effects is %d", max_effects); + + if (max_effects > PID_EFFECTS_MAX) + max_effects = PID_EFFECTS_MAX; + + if (pidff->pool[PID_SIMULTANEOUS_MAX].value) + debug("max simultaneous effects is %d", + pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); + + if (pidff->pool[PID_RAM_POOL_SIZE].value) + debug("device memory size is %d bytes", + pidff->pool[PID_RAM_POOL_SIZE].value[0]); + + if (pidff->pool[PID_DEVICE_MANAGED_POOL].value && + pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) { + printk(KERN_NOTICE "hid-pidff: " + "device does not support device managed pool\n"); + goto fail; + } + + error = input_ff_create(dev, max_effects); + if (error) + goto fail; + + ff = dev->ff; + ff->private = pidff; + ff->upload = pidff_upload_effect; + ff->erase = pidff_erase_effect; + ff->set_gain = pidff_set_gain; + ff->set_autocenter = pidff_set_autocenter; + ff->playback = pidff_playback; + + printk(KERN_INFO "Force feedback for USB HID PID devices by " + "Anssi Hannula \n"); + + return 0; + + fail: + kfree(pidff); + return error; +} diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/usbhid/hid-plff.c new file mode 100644 index 0000000..76d2e6e --- /dev/null +++ b/drivers/hid/usbhid/hid-plff.c @@ -0,0 +1,129 @@ +/* + * Force feedback support for PantherLord USB/PS2 2in1 Adapter devices + * + * Copyright (c) 2007 Anssi Hannula + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) + +#include +#include +#include +#include "usbhid.h" + +struct plff_device { + struct hid_report *report; +}; + +static int hid_plff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct plff_device *plff = data; + int left, right; + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + debug("called with 0x%04x 0x%04x", left, right); + + left = left * 0x7f / 0xffff; + right = right * 0x7f / 0xffff; + + plff->report->field[0]->value[2] = left; + plff->report->field[0]->value[3] = right; + debug("running with 0x%02x 0x%02x", left, right); + usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + + return 0; +} + +int hid_plff_init(struct hid_device *hid) +{ + struct plff_device *plff; + struct hid_report *report; + struct hid_input *hidinput; + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct list_head *report_ptr = report_list; + struct input_dev *dev; + int error; + + /* The device contains 2 output reports (one for each + HID_QUIRK_MULTI_INPUT device), both containing 1 field, which + contains 4 ff00.0002 usages and 4 16bit absolute values. + + The 2 input reports also contain a field which contains + 8 ff00.0001 usages and 8 boolean values. Their meaning is + currently unknown. */ + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-plff: no output reports found\n"); + return -ENODEV; + } + + list_for_each_entry(hidinput, &hid->inputs, list) { + + report_ptr = report_ptr->next; + + if (report_ptr == report_list) { + printk(KERN_ERR "hid-plff: required output report is missing\n"); + return -ENODEV; + } + + report = list_entry(report_ptr, struct hid_report, list); + if (report->maxfield < 1) { + printk(KERN_ERR "hid-plff: no fields in the report\n"); + return -ENODEV; + } + + if (report->field[0]->report_count < 4) { + printk(KERN_ERR "hid-plff: not enough values in the field\n"); + return -ENODEV; + } + + plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); + if (!plff) + return -ENOMEM; + + dev = hidinput->input; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, plff, hid_plff_play); + if (error) { + kfree(plff); + return error; + } + + plff->report = report; + plff->report->field[0]->value[0] = 0x00; + plff->report->field[0]->value[1] = 0x00; + plff->report->field[0]->value[2] = 0x00; + plff->report->field[0]->value[3] = 0x00; + usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + } + + printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 " + "2in1 Adapters by Anssi Hannula \n"); + + return 0; +} diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c new file mode 100644 index 0000000..17a8755 --- /dev/null +++ b/drivers/hid/usbhid/hid-quirks.c @@ -0,0 +1,681 @@ +/* + * USB HID quirks support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include + +#define USB_VENDOR_ID_A4TECH 0x09da +#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 + +#define USB_VENDOR_ID_AASHIMA 0x06d6 +#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 +#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 +#define USB_DEVICE_ID_ACECAD_302 0x0008 + +#define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_DEVICE_ID_AIPTEK_01 0x0001 +#define USB_DEVICE_ID_AIPTEK_10 0x0010 +#define USB_DEVICE_ID_AIPTEK_20 0x0020 +#define USB_DEVICE_ID_AIPTEK_21 0x0021 +#define USB_DEVICE_ID_AIPTEK_22 0x0022 +#define USB_DEVICE_ID_AIPTEK_23 0x0023 +#define USB_DEVICE_ID_AIPTEK_24 0x0024 + +#define USB_VENDOR_ID_AIRCABLE 0x16CA +#define USB_DEVICE_ID_AIRCABLE1 0x1502 + +#define USB_VENDOR_ID_ALCOR 0x058f +#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 + +#define USB_VENDOR_ID_ALPS 0x0433 +#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 + +#define USB_VENDOR_ID_APPLE 0x05ac +#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f +#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 +#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 +#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 +#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 +#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 +#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 +#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a +#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b +#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c +#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a +#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b +#define USB_DEVICE_ID_APPLE_IR 0x8240 + +#define USB_VENDOR_ID_ATEN 0x0557 +#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 +#define USB_DEVICE_ID_ATEN_CS124U 0x2202 +#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 +#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 +#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 + +#define USB_VENDOR_ID_BELKIN 0x050d +#define USB_DEVICE_ID_FLIP_KVM 0x3201 + +#define USB_VENDOR_ID_BERKSHIRE 0x0c98 +#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 + +#define USB_VENDOR_ID_CHERRY 0x046a +#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 + +#define USB_VENDOR_ID_CHIC 0x05fe +#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 + +#define USB_VENDOR_ID_CIDC 0x1677 + +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff + +#define USB_VENDOR_ID_CYPRESS 0x04b4 +#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 +#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 +#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 + +#define USB_VENDOR_ID_DELL 0x413c +#define USB_DEVICE_ID_DELL_W7658 0x2005 + +#define USB_VENDOR_ID_DELORME 0x1163 +#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 +#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 + +#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f +#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 + +#define USB_VENDOR_ID_GLAB 0x06c2 +#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 +#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 +#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 +#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044 +#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045 +#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051 +#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 +#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 + +#define USB_VENDOR_ID_GRIFFIN 0x077d +#define USB_DEVICE_ID_POWERMATE 0x0410 +#define USB_DEVICE_ID_SOUNDKNOB 0x04AA + +#define USB_VENDOR_ID_GTCO 0x078c +#define USB_DEVICE_ID_GTCO_90 0x0090 +#define USB_DEVICE_ID_GTCO_100 0x0100 +#define USB_DEVICE_ID_GTCO_101 0x0101 +#define USB_DEVICE_ID_GTCO_103 0x0103 +#define USB_DEVICE_ID_GTCO_104 0x0104 +#define USB_DEVICE_ID_GTCO_105 0x0105 +#define USB_DEVICE_ID_GTCO_106 0x0106 +#define USB_DEVICE_ID_GTCO_107 0x0107 +#define USB_DEVICE_ID_GTCO_108 0x0108 +#define USB_DEVICE_ID_GTCO_200 0x0200 +#define USB_DEVICE_ID_GTCO_201 0x0201 +#define USB_DEVICE_ID_GTCO_202 0x0202 +#define USB_DEVICE_ID_GTCO_203 0x0203 +#define USB_DEVICE_ID_GTCO_204 0x0204 +#define USB_DEVICE_ID_GTCO_205 0x0205 +#define USB_DEVICE_ID_GTCO_206 0x0206 +#define USB_DEVICE_ID_GTCO_207 0x0207 +#define USB_DEVICE_ID_GTCO_300 0x0300 +#define USB_DEVICE_ID_GTCO_301 0x0301 +#define USB_DEVICE_ID_GTCO_302 0x0302 +#define USB_DEVICE_ID_GTCO_303 0x0303 +#define USB_DEVICE_ID_GTCO_304 0x0304 +#define USB_DEVICE_ID_GTCO_305 0x0305 +#define USB_DEVICE_ID_GTCO_306 0x0306 +#define USB_DEVICE_ID_GTCO_307 0x0307 +#define USB_DEVICE_ID_GTCO_308 0x0308 +#define USB_DEVICE_ID_GTCO_309 0x0309 +#define USB_DEVICE_ID_GTCO_400 0x0400 +#define USB_DEVICE_ID_GTCO_401 0x0401 +#define USB_DEVICE_ID_GTCO_402 0x0402 +#define USB_DEVICE_ID_GTCO_403 0x0403 +#define USB_DEVICE_ID_GTCO_404 0x0404 +#define USB_DEVICE_ID_GTCO_405 0x0405 +#define USB_DEVICE_ID_GTCO_500 0x0500 +#define USB_DEVICE_ID_GTCO_501 0x0501 +#define USB_DEVICE_ID_GTCO_502 0x0502 +#define USB_DEVICE_ID_GTCO_503 0x0503 +#define USB_DEVICE_ID_GTCO_504 0x0504 +#define USB_DEVICE_ID_GTCO_1000 0x1000 +#define USB_DEVICE_ID_GTCO_1001 0x1001 +#define USB_DEVICE_ID_GTCO_1002 0x1002 +#define USB_DEVICE_ID_GTCO_1003 0x1003 +#define USB_DEVICE_ID_GTCO_1004 0x1004 +#define USB_DEVICE_ID_GTCO_1005 0x1005 +#define USB_DEVICE_ID_GTCO_1006 0x1006 + +#define USB_VENDOR_ID_HAPP 0x078b +#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 +#define USB_DEVICE_ID_UGCI_FLYING 0x0020 +#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 + +#define USB_VENDOR_ID_IMATION 0x0718 +#define USB_DEVICE_ID_DISC_STAKKA 0xd000 + +#define USB_VENDOR_ID_KBGEAR 0x084e +#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 + +#define USB_VENDOR_ID_LD 0x0f11 +#define USB_DEVICE_ID_LD_CASSY 0x1000 +#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 +#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 +#define USB_DEVICE_ID_LD_JWM 0x1080 +#define USB_DEVICE_ID_LD_DMMP 0x1081 +#define USB_DEVICE_ID_LD_UMIP 0x1090 +#define USB_DEVICE_ID_LD_XRAY1 0x1100 +#define USB_DEVICE_ID_LD_XRAY2 0x1101 +#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 +#define USB_DEVICE_ID_LD_COM3LAB 0x2000 +#define USB_DEVICE_ID_LD_TELEPORT 0x2010 +#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 +#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 +#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 +#define USB_DEVICE_ID_S510_RECEIVER 0xc50c +#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 +#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 +#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 + +#define USB_VENDOR_ID_MCC 0x09db +#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 +#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a + +#define USB_VENDOR_ID_MGE 0x0463 +#define USB_DEVICE_ID_MGE_UPS 0xffff +#define USB_DEVICE_ID_MGE_UPS1 0x0001 + +#define USB_VENDOR_ID_NEC 0x073e +#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 + +#define USB_VENDOR_ID_ONTRAK 0x0a07 +#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 + +#define USB_VENDOR_ID_PANJIT 0x134c + +#define USB_VENDOR_ID_PANTHERLORD 0x0810 +#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 + +#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 +#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 + +#define USB_VENDOR_ID_SAITEK 0x06a3 +#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 + +#define USB_VENDOR_ID_SONY 0x054c +#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 + +#define USB_VENDOR_ID_SUN 0x0430 +#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab + +#define USB_VENDOR_ID_TOPMAX 0x0663 +#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 + +#define USB_VENDOR_ID_TURBOX 0x062a +#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 + +#define USB_VENDOR_ID_VERNIER 0x08f7 +#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 +#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 +#define USB_DEVICE_ID_VERNIER_SKIP 0x0003 +#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 + +#define USB_VENDOR_ID_WACOM 0x056a + +#define USB_VENDOR_ID_WISEGROUP 0x0925 +#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 +#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 +#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 +#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800 +#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 + +#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 +#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 + +#define USB_VENDOR_ID_YEALINK 0x6993 +#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 + +/* + * Alphabetically sorted blacklist by quirk type. + */ + +static const struct hid_blacklist { + __u16 idVendor; + __u16 idProduct; + __u32 quirks; +} hid_blacklist[] = { + + { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, + + { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, + + { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, + + { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, + + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, + + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_LOGITECH_DESCRIPTOR }, + + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, + + { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, + { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, + + { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, + + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + + { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + + { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, + + { 0, 0 } +}; + +/* Dynamic HID quirks list - specified at runtime */ +struct quirks_list_struct { + struct hid_blacklist hid_bl_item; + struct list_head node; +}; + +static LIST_HEAD(dquirks_list); +static DECLARE_RWSEM(dquirks_rwsem); + +/* Runtime ("dynamic") quirks manipulation functions */ + +/** + * usbhid_exists_dquirk: find any dynamic quirks for a USB HID device + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * + * Description: + * Scans dquirks_list for a matching dynamic quirk and returns + * the pointer to the relevant struct hid_blacklist if found. + * Must be called with a read lock held on dquirks_rwsem. + * + * Returns: NULL if no quirk found, struct hid_blacklist * if found. + */ +static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor, + const u16 idProduct) +{ + struct quirks_list_struct *q; + struct hid_blacklist *bl_entry = NULL; + + list_for_each_entry(q, &dquirks_list, node) { + if (q->hid_bl_item.idVendor == idVendor && + q->hid_bl_item.idProduct == idProduct) { + bl_entry = &q->hid_bl_item; + break; + } + } + + if (bl_entry != NULL) + dbg("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + bl_entry->quirks, bl_entry->idVendor, + bl_entry->idProduct); + + return bl_entry; +} + + +/** + * usbhid_modify_dquirk: add/replace a HID quirk + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * @quirks: the u32 quirks value to add/replace + * + * Description: + * If an dynamic quirk exists in memory for this (idVendor, + * idProduct) pair, replace its quirks value with what was + * provided. Otherwise, add the quirk to the dynamic quirks list. + * + * Returns: 0 OK, -error on failure. + */ +int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, + const u32 quirks) +{ + struct quirks_list_struct *q_new, *q; + int list_edited = 0; + + if (!idVendor) { + dbg("Cannot add a quirk with idVendor = 0"); + return -EINVAL; + } + + q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL); + if (!q_new) { + dbg("Could not allocate quirks_list_struct"); + return -ENOMEM; + } + + q_new->hid_bl_item.idVendor = idVendor; + q_new->hid_bl_item.idProduct = idProduct; + q_new->hid_bl_item.quirks = quirks; + + down_write(&dquirks_rwsem); + + list_for_each_entry(q, &dquirks_list, node) { + + if (q->hid_bl_item.idVendor == idVendor && + q->hid_bl_item.idProduct == idProduct) { + + list_replace(&q->node, &q_new->node); + kfree(q); + list_edited = 1; + break; + + } + + } + + if (!list_edited) + list_add_tail(&q_new->node, &dquirks_list); + + up_write(&dquirks_rwsem); + + return 0; +} + + +/** + * usbhid_remove_all_dquirks: remove all runtime HID quirks from memory + * + * Description: + * Free all memory associated with dynamic quirks - called before + * module unload. + * + */ +static void usbhid_remove_all_dquirks(void) +{ + struct quirks_list_struct *q, *temp; + + down_write(&dquirks_rwsem); + list_for_each_entry_safe(q, temp, &dquirks_list, node) { + list_del(&q->node); + kfree(q); + } + up_write(&dquirks_rwsem); + +} + +/** + * usbhid_quirks_init: apply USB HID quirks specified at module load time + */ +int usbhid_quirks_init(char **quirks_param) +{ + u16 idVendor, idProduct; + u32 quirks; + int n = 0, m; + + for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) { + + m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x", + &idVendor, &idProduct, &quirks); + + if (m != 3 || + usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) { + printk(KERN_WARNING + "Could not parse HID quirk module param %s\n", + quirks_param[n]); + } + } + + return 0; +} + +/** + * usbhid_quirks_exit: release memory associated with dynamic_quirks + * + * Description: + * Release all memory associated with dynamic quirks. Called upon + * module unload. + * + * Returns: nothing + */ +void usbhid_quirks_exit(void) +{ + usbhid_remove_all_dquirks(); +} + +/** + * usbhid_exists_squirk: return any static quirks for a USB HID device + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * + * Description: + * Given a USB vendor ID and product ID, return a pointer to + * the hid_blacklist entry associated with that device. + * + * Returns: pointer if quirk found, or NULL if no quirks found. + */ +static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor, + const u16 idProduct) +{ + const struct hid_blacklist *bl_entry = NULL; + int n = 0; + + for (; hid_blacklist[n].idVendor; n++) + if (hid_blacklist[n].idVendor == idVendor && + hid_blacklist[n].idProduct == idProduct) + bl_entry = &hid_blacklist[n]; + + if (bl_entry != NULL) + dbg("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + bl_entry->quirks, bl_entry->idVendor, + bl_entry->idProduct); + return bl_entry; +} + +/** + * usbhid_lookup_quirk: return any quirks associated with a USB HID device + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * + * Description: + * Given a USB vendor ID and product ID, return any quirks associated + * with that device. + * + * Returns: a u32 quirks value. + */ +u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) +{ + u32 quirks = 0; + const struct hid_blacklist *bl_entry = NULL; + + /* Ignore all Wacom devices */ + if (idVendor == USB_VENDOR_ID_WACOM) + return HID_QUIRK_IGNORE; + + /* ignore all Code Mercenaries IOWarrior devices */ + if (idVendor == USB_VENDOR_ID_CODEMERCS) + if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && + idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) + return HID_QUIRK_IGNORE; + + down_read(&dquirks_rwsem); + bl_entry = usbhid_exists_dquirk(idVendor, idProduct); + if (!bl_entry) + bl_entry = usbhid_exists_squirk(idVendor, idProduct); + if (bl_entry) + quirks = bl_entry->quirks; + up_read(&dquirks_rwsem); + + return quirks; +} + diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c new file mode 100644 index 0000000..ab67331 --- /dev/null +++ b/drivers/hid/usbhid/hid-tmff.c @@ -0,0 +1,147 @@ +/* + * Force feedback support for various HID compliant devices by ThrustMaster: + * ThrustMaster FireStorm Dual Power 2 + * and possibly others whose device ids haven't been added. + * + * Modified to support ThrustMaster devices by Zinx Verituse + * on 2003-01-25 from the Logitech force feedback driver, + * which is by Johann Deneux. + * + * Copyright (c) 2003 Zinx Verituse + * Copyright (c) 2002 Johann Deneux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#undef DEBUG +#include + +#include +#include "usbhid.h" + +/* Usages for thrustmaster devices I know about */ +#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) + + +struct tmff_device { + struct hid_report *report; + struct hid_field *rumble; +}; + +/* Changes values from 0 to 0xffff into values from minimum to maximum */ +static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) +{ + int ret; + + ret = (in * (maximum - minimum) / 0xffff) + minimum; + if (ret < minimum) + return minimum; + if (ret > maximum) + return maximum; + return ret; +} + +static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct tmff_device *tmff = data; + int left, right; /* Rumbling */ + + left = hid_tmff_scale(effect->u.rumble.weak_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); + right = hid_tmff_scale(effect->u.rumble.strong_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); + + tmff->rumble->value[0] = left; + tmff->rumble->value[1] = right; + dbg("(left,right)=(%08x, %08x)", left, right); + usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + + return 0; +} + +int hid_tmff_init(struct hid_device *hid) +{ + struct tmff_device *tmff; + struct list_head *pos; + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct input_dev *input_dev = hidinput->input; + int error; + + tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); + if (!tmff) + return -ENOMEM; + + /* Find the report to use */ + __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { + struct hid_report *report = (struct hid_report *)pos; + int fieldnum; + + for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { + struct hid_field *field = report->field[fieldnum]; + + if (field->maxusage <= 0) + continue; + + switch (field->usage[0].hid) { + case THRUSTMASTER_USAGE_RUMBLE_LR: + if (field->report_count < 2) { + warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2"); + continue; + } + + if (field->logical_maximum == field->logical_minimum) { + warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum"); + continue; + } + + if (tmff->report && tmff->report != report) { + warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); + continue; + } + + if (tmff->rumble && tmff->rumble != field) { + warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); + continue; + } + + tmff->report = report; + tmff->rumble = field; + + set_bit(FF_RUMBLE, input_dev->ffbit); + break; + + default: + warn("ignoring unknown output usage %08x", field->usage[0].hid); + continue; + } + } + } + + error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); + if (error) { + kfree(tmff); + return error; + } + + info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse "); + + return 0; +} + diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c new file mode 100644 index 0000000..7bd8238 --- /dev/null +++ b/drivers/hid/usbhid/hid-zpff.c @@ -0,0 +1,111 @@ +/* + * Force feedback support for Zeroplus based devices + * + * Copyright (c) 2005, 2006 Anssi Hannula + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg) + +#include +#include +#include +#include "usbhid.h" + +struct zpff_device { + struct hid_report *report; +}; + +static int hid_zpff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct zpff_device *zpff = data; + int left, right; + + /* + * The following is specified the other way around in the Zeroplus + * datasheet but the order below is correct for the XFX Executioner; + * however it is possible that the XFX Executioner is an exception + */ + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + debug("called with 0x%04x 0x%04x", left, right); + + left = left * 0x7f / 0xffff; + right = right * 0x7f / 0xffff; + + zpff->report->field[2]->value[0] = left; + zpff->report->field[3]->value[0] = right; + debug("running with 0x%02x 0x%02x", left, right); + usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + + return 0; +} + +int hid_zpff_init(struct hid_device *hid) +{ + struct zpff_device *zpff; + struct hid_report *report; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int error; + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-zpff: no output report found\n"); + return -ENODEV; + } + + report = list_entry(report_list->next, struct hid_report, list); + + if (report->maxfield < 4) { + printk(KERN_ERR "hid-zpff: not enough fields in report\n"); + return -ENODEV; + } + + zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); + if (!zpff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, zpff, hid_zpff_play); + if (error) { + kfree(zpff); + return error; + } + + zpff->report = report; + zpff->report->field[0]->value[0] = 0x00; + zpff->report->field[1]->value[0] = 0x02; + zpff->report->field[2]->value[0] = 0x00; + zpff->report->field[3]->value[0] = 0x00; + usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + + printk(KERN_INFO "Force feedback for Zeroplus based devices by " + "Anssi Hannula \n"); + + return 0; +} diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c new file mode 100644 index 0000000..a8b3d66 --- /dev/null +++ b/drivers/hid/usbhid/hiddev.c @@ -0,0 +1,847 @@ +/* + * Copyright (c) 2001 Paul Stewart + * Copyright (c) 2001 Vojtech Pavlik + * + * HID char devices, giving access to raw HID device events. + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to Paul Stewart + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbhid.h" + +#ifdef CONFIG_USB_DYNAMIC_MINORS +#define HIDDEV_MINOR_BASE 0 +#define HIDDEV_MINORS 256 +#else +#define HIDDEV_MINOR_BASE 96 +#define HIDDEV_MINORS 16 +#endif +#define HIDDEV_BUFFER_SIZE 64 + +struct hiddev { + int exist; + int open; + wait_queue_head_t wait; + struct hid_device *hid; + struct list_head list; +}; + +struct hiddev_list { + struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE]; + int head; + int tail; + unsigned flags; + struct fasync_struct *fasync; + struct hiddev *hiddev; + struct list_head node; +}; + +static struct hiddev *hiddev_table[HIDDEV_MINORS]; + +/* + * Find a report, given the report's type and ID. The ID can be specified + * indirectly by REPORT_ID_FIRST (which returns the first report of the given + * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the + * given type which follows old_id. + */ +static struct hid_report * +hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) +{ + unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK; + unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK; + struct hid_report_enum *report_enum; + struct hid_report *report; + struct list_head *list; + + if (rinfo->report_type < HID_REPORT_TYPE_MIN || + rinfo->report_type > HID_REPORT_TYPE_MAX) + return NULL; + + report_enum = hid->report_enum + + (rinfo->report_type - HID_REPORT_TYPE_MIN); + + switch (flags) { + case 0: /* Nothing to do -- report_id is already set correctly */ + break; + + case HID_REPORT_ID_FIRST: + if (list_empty(&report_enum->report_list)) + return NULL; + + list = report_enum->report_list.next; + report = list_entry(list, struct hid_report, list); + rinfo->report_id = report->id; + break; + + case HID_REPORT_ID_NEXT: + report = report_enum->report_id_hash[rid]; + if (!report) + return NULL; + + list = report->list.next; + if (list == &report_enum->report_list) + return NULL; + + report = list_entry(list, struct hid_report, list); + rinfo->report_id = report->id; + break; + + default: + return NULL; + } + + return report_enum->report_id_hash[rinfo->report_id]; +} + +/* + * Perform an exhaustive search of the report table for a usage, given its + * type and usage id. + */ +static struct hid_field * +hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) +{ + int i, j; + struct hid_report *report; + struct hid_report_enum *report_enum; + struct hid_field *field; + + if (uref->report_type < HID_REPORT_TYPE_MIN || + uref->report_type > HID_REPORT_TYPE_MAX) + return NULL; + + report_enum = hid->report_enum + + (uref->report_type - HID_REPORT_TYPE_MIN); + + list_for_each_entry(report, &report_enum->report_list, list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == uref->usage_code) { + uref->report_id = report->id; + uref->field_index = i; + uref->usage_index = j; + return field; + } + } + } + } + + return NULL; +} + +static void hiddev_send_event(struct hid_device *hid, + struct hiddev_usage_ref *uref) +{ + struct hiddev *hiddev = hid->hiddev; + struct hiddev_list *list; + + list_for_each_entry(list, &hiddev->list, node) { + if (uref->field_index != HID_FIELD_INDEX_NONE || + (list->flags & HIDDEV_FLAG_REPORT) != 0) { + list->buffer[list->head] = *uref; + list->head = (list->head + 1) & + (HIDDEV_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } + } + + wake_up_interruptible(&hiddev->wait); +} + +/* + * This is where hid.c calls into hiddev to pass an event that occurred over + * the interrupt pipe + */ +void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned type = field->report_type; + struct hiddev_usage_ref uref; + + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); + uref.report_id = field->report->id; + uref.field_index = field->index; + uref.usage_index = (usage - field->usage); + uref.usage_code = usage->hid; + uref.value = value; + + hiddev_send_event(hid, &uref); +} +EXPORT_SYMBOL_GPL(hiddev_hid_event); + +void hiddev_report_event(struct hid_device *hid, struct hid_report *report) +{ + unsigned type = report->type; + struct hiddev_usage_ref uref; + + memset(&uref, 0, sizeof(uref)); + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); + uref.report_id = report->id; + uref.field_index = HID_FIELD_INDEX_NONE; + + hiddev_send_event(hid, &uref); +} + +/* + * fasync file op + */ +static int hiddev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct hiddev_list *list = file->private_data; + + retval = fasync_helper(fd, file, on, &list->fasync); + + return retval < 0 ? retval : 0; +} + + +/* + * release file op + */ +static int hiddev_release(struct inode * inode, struct file * file) +{ + struct hiddev_list *list = file->private_data; + + hiddev_fasync(-1, file, 0); + list_del(&list->node); + + if (!--list->hiddev->open) { + if (list->hiddev->exist) + usbhid_close(list->hiddev->hid); + else + kfree(list->hiddev); + } + + kfree(list); + + return 0; +} + +/* + * open file op + */ +static int hiddev_open(struct inode *inode, struct file *file) +{ + struct hiddev_list *list; + + int i = iminor(inode) - HIDDEV_MINOR_BASE; + + if (i >= HIDDEV_MINORS || !hiddev_table[i]) + return -ENODEV; + + if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) + return -ENOMEM; + + list->hiddev = hiddev_table[i]; + list_add_tail(&list->node, &hiddev_table[i]->list); + file->private_data = list; + + if (!list->hiddev->open++) + if (list->hiddev->exist) + usbhid_open(hiddev_table[i]->hid); + + return 0; +} + +/* + * "write" file op + */ +static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +/* + * "read" file op + */ +static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct hiddev_list *list = file->private_data; + int event_size; + int retval = 0; + + event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? + sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); + + if (count < event_size) + return 0; + + while (retval == 0) { + if (list->head == list->tail) { + add_wait_queue(&list->hiddev->wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (list->head == list->tail) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + if (!list->hiddev->exist) { + retval = -EIO; + break; + } + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->hiddev->wait, &wait); + } + + if (retval) + return retval; + + + while (list->head != list->tail && + retval + event_size <= count) { + if ((list->flags & HIDDEV_FLAG_UREF) == 0) { + if (list->buffer[list->tail].field_index != + HID_FIELD_INDEX_NONE) { + struct hiddev_event event; + event.hid = list->buffer[list->tail].usage_code; + event.value = list->buffer[list->tail].value; + if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) + return -EFAULT; + retval += sizeof(struct hiddev_event); + } + } else { + if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || + (list->flags & HIDDEV_FLAG_REPORT) != 0) { + if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) + return -EFAULT; + retval += sizeof(struct hiddev_usage_ref); + } + } + list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); + } + + } + + return retval; +} + +/* + * "poll" file op + * No kernel lock - fine + */ +static unsigned int hiddev_poll(struct file *file, poll_table *wait) +{ + struct hiddev_list *list = file->private_data; + + poll_wait(file, &list->hiddev->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + if (!list->hiddev->exist) + return POLLERR | POLLHUP; + return 0; +} + +/* + * "ioctl" file op + */ +static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct hiddev_list *list = file->private_data; + struct hiddev *hiddev = list->hiddev; + struct hid_device *hid = hiddev->hid; + struct usb_device *dev = hid_to_usb_dev(hid); + struct hiddev_collection_info cinfo; + struct hiddev_report_info rinfo; + struct hiddev_field_info finfo; + struct hiddev_usage_ref_multi *uref_multi = NULL; + struct hiddev_usage_ref *uref; + struct hiddev_devinfo dinfo; + struct hid_report *report; + struct hid_field *field; + struct usbhid_device *usbhid = hid->driver_data; + void __user *user_arg = (void __user *)arg; + int i; + + if (!hiddev->exist) + return -EIO; + + switch (cmd) { + + case HIDIOCGVERSION: + return put_user(HID_VERSION, (int __user *)arg); + + case HIDIOCAPPLICATION: + if (arg < 0 || arg >= hid->maxapplication) + return -EINVAL; + + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && arg-- == 0) + break; + + if (i == hid->maxcollection) + return -EINVAL; + + return hid->collection[i].usage; + + case HIDIOCGDEVINFO: + dinfo.bustype = BUS_USB; + dinfo.busnum = dev->bus->busnum; + dinfo.devnum = dev->devnum; + dinfo.ifnum = usbhid->ifnum; + dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor); + dinfo.product = le16_to_cpu(dev->descriptor.idProduct); + dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice); + dinfo.num_applications = hid->maxapplication; + if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) + return -EFAULT; + + return 0; + + case HIDIOCGFLAG: + if (put_user(list->flags, (int __user *)arg)) + return -EFAULT; + + return 0; + + case HIDIOCSFLAG: + { + int newflags; + if (get_user(newflags, (int __user *)arg)) + return -EFAULT; + + if ((newflags & ~HIDDEV_FLAGS) != 0 || + ((newflags & HIDDEV_FLAG_REPORT) != 0 && + (newflags & HIDDEV_FLAG_UREF) == 0)) + return -EINVAL; + + list->flags = newflags; + + return 0; + } + + case HIDIOCGSTRING: + { + int idx, len; + char *buf; + + if (get_user(idx, (int __user *)arg)) + return -EFAULT; + + if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) { + kfree(buf); + return -EINVAL; + } + + if (copy_to_user(user_arg+sizeof(int), buf, len+1)) { + kfree(buf); + return -EFAULT; + } + + kfree(buf); + + return len; + } + + case HIDIOCINITREPORT: + usbhid_init_reports(hid); + + return 0; + + case HIDIOCGREPORT: + if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) + return -EFAULT; + + if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT) + return -EINVAL; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + usbhid_submit_report(hid, report, USB_DIR_IN); + usbhid_wait_io(hid); + + return 0; + + case HIDIOCSREPORT: + if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) + return -EFAULT; + + if (rinfo.report_type == HID_REPORT_TYPE_INPUT) + return -EINVAL; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + usbhid_submit_report(hid, report, USB_DIR_OUT); + usbhid_wait_io(hid); + + return 0; + + case HIDIOCGREPORTINFO: + if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) + return -EFAULT; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + rinfo.num_fields = report->maxfield; + + if (copy_to_user(user_arg, &rinfo, sizeof(rinfo))) + return -EFAULT; + + return 0; + + case HIDIOCGFIELDINFO: + if (copy_from_user(&finfo, user_arg, sizeof(finfo))) + return -EFAULT; + rinfo.report_type = finfo.report_type; + rinfo.report_id = finfo.report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + if (finfo.field_index >= report->maxfield) + return -EINVAL; + + field = report->field[finfo.field_index]; + memset(&finfo, 0, sizeof(finfo)); + finfo.report_type = rinfo.report_type; + finfo.report_id = rinfo.report_id; + finfo.field_index = field->report_count - 1; + finfo.maxusage = field->maxusage; + finfo.flags = field->flags; + finfo.physical = field->physical; + finfo.logical = field->logical; + finfo.application = field->application; + finfo.logical_minimum = field->logical_minimum; + finfo.logical_maximum = field->logical_maximum; + finfo.physical_minimum = field->physical_minimum; + finfo.physical_maximum = field->physical_maximum; + finfo.unit_exponent = field->unit_exponent; + finfo.unit = field->unit; + + if (copy_to_user(user_arg, &finfo, sizeof(finfo))) + return -EFAULT; + + return 0; + + case HIDIOCGUCODE: + uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); + if (!uref_multi) + return -ENOMEM; + uref = &uref_multi->uref; + if (copy_from_user(uref, user_arg, sizeof(*uref))) + goto fault; + + rinfo.report_type = uref->report_type; + rinfo.report_id = uref->report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + goto inval; + + if (uref->field_index >= report->maxfield) + goto inval; + + field = report->field[uref->field_index]; + if (uref->usage_index >= field->maxusage) + goto inval; + + uref->usage_code = field->usage[uref->usage_index].hid; + + if (copy_to_user(user_arg, uref, sizeof(*uref))) + goto fault; + + kfree(uref_multi); + return 0; + + case HIDIOCGUSAGE: + case HIDIOCSUSAGE: + case HIDIOCGUSAGES: + case HIDIOCSUSAGES: + case HIDIOCGCOLLECTIONINDEX: + uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); + if (!uref_multi) + return -ENOMEM; + uref = &uref_multi->uref; + if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { + if (copy_from_user(uref_multi, user_arg, + sizeof(*uref_multi))) + goto fault; + } else { + if (copy_from_user(uref, user_arg, sizeof(*uref))) + goto fault; + } + + if (cmd != HIDIOCGUSAGE && + cmd != HIDIOCGUSAGES && + uref->report_type == HID_REPORT_TYPE_INPUT) + goto inval; + + if (uref->report_id == HID_REPORT_ID_UNKNOWN) { + field = hiddev_lookup_usage(hid, uref); + if (field == NULL) + goto inval; + } else { + rinfo.report_type = uref->report_type; + rinfo.report_id = uref->report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + goto inval; + + if (uref->field_index >= report->maxfield) + goto inval; + + field = report->field[uref->field_index]; + + if (cmd == HIDIOCGCOLLECTIONINDEX) { + if (uref->usage_index >= field->maxusage) + goto inval; + } else if (uref->usage_index >= field->report_count) + goto inval; + + else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && + (uref_multi->num_values > HID_MAX_MULTI_USAGES || + uref->usage_index + uref_multi->num_values > field->report_count)) + goto inval; + } + + switch (cmd) { + case HIDIOCGUSAGE: + uref->value = field->value[uref->usage_index]; + if (copy_to_user(user_arg, uref, sizeof(*uref))) + goto fault; + goto goodreturn; + + case HIDIOCSUSAGE: + field->value[uref->usage_index] = uref->value; + goto goodreturn; + + case HIDIOCGCOLLECTIONINDEX: + kfree(uref_multi); + return field->usage[uref->usage_index].collection_index; + case HIDIOCGUSAGES: + for (i = 0; i < uref_multi->num_values; i++) + uref_multi->values[i] = + field->value[uref->usage_index + i]; + if (copy_to_user(user_arg, uref_multi, + sizeof(*uref_multi))) + goto fault; + goto goodreturn; + case HIDIOCSUSAGES: + for (i = 0; i < uref_multi->num_values; i++) + field->value[uref->usage_index + i] = + uref_multi->values[i]; + goto goodreturn; + } + +goodreturn: + kfree(uref_multi); + return 0; +fault: + kfree(uref_multi); + return -EFAULT; +inval: + kfree(uref_multi); + return -EINVAL; + + case HIDIOCGCOLLECTIONINFO: + if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) + return -EFAULT; + + if (cinfo.index >= hid->maxcollection) + return -EINVAL; + + cinfo.type = hid->collection[cinfo.index].type; + cinfo.usage = hid->collection[cinfo.index].usage; + cinfo.level = hid->collection[cinfo.index].level; + + if (copy_to_user(user_arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + default: + + if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) { + int len; + if (!hid->name) + return 0; + len = strlen(hid->name) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->name, len) ? + -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) { + int len; + if (!hid->phys) + return 0; + len = strlen(hid->phys) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->phys, len) ? + -EFAULT : len; + } + } + return -EINVAL; +} + +static const struct file_operations hiddev_fops = { + .owner = THIS_MODULE, + .read = hiddev_read, + .write = hiddev_write, + .poll = hiddev_poll, + .open = hiddev_open, + .release = hiddev_release, + .ioctl = hiddev_ioctl, + .fasync = hiddev_fasync, +}; + +static struct usb_class_driver hiddev_class = { + .name = "hiddev%d", + .fops = &hiddev_fops, + .minor_base = HIDDEV_MINOR_BASE, +}; + +/* + * This is where hid.c calls us to connect a hid device to the hiddev driver + */ +int hiddev_connect(struct hid_device *hid) +{ + struct hiddev *hiddev; + struct usbhid_device *usbhid = hid->driver_data; + int i; + int retval; + + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && + !IS_INPUT_APPLICATION(hid->collection[i].usage)) + break; + + if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) + return -1; + + if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) + return -1; + + retval = usb_register_dev(usbhid->intf, &hiddev_class); + if (retval) { + err("Not able to get a minor for this device."); + kfree(hiddev); + return -1; + } + + init_waitqueue_head(&hiddev->wait); + INIT_LIST_HEAD(&hiddev->list); + hiddev->hid = hid; + hiddev->exist = 1; + + hid->minor = usbhid->intf->minor; + hid->hiddev = hiddev; + + hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; + + return 0; +} + +/* + * This is where hid.c calls us to disconnect a hiddev device from the + * corresponding hid device (usually because the usb device has disconnected) + */ +static struct usb_class_driver hiddev_class; +void hiddev_disconnect(struct hid_device *hid) +{ + struct hiddev *hiddev = hid->hiddev; + struct usbhid_device *usbhid = hid->driver_data; + + hiddev->exist = 0; + + hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; + usb_deregister_dev(usbhid->intf, &hiddev_class); + + if (hiddev->open) { + usbhid_close(hiddev->hid); + wake_up_interruptible(&hiddev->wait); + } else { + kfree(hiddev); + } +} + +/* Currently this driver is a USB driver. It's not a conventional one in + * the sense that it doesn't probe at the USB level. Instead it waits to + * be connected by HID through the hiddev_connect / hiddev_disconnect + * routines. The reason to register as a USB device is to gain part of the + * minor number space from the USB major. + * + * In theory, should the HID code be generalized to more than one physical + * medium (say, IEEE 1384), this driver will probably need to register its + * own major number, and in doing so, no longer need to register with USB. + * At that point the probe routine and hiddev_driver struct below will no + * longer be useful. + */ + + +/* We never attach in this manner, and rely on HID to connect us. This + * is why there is no disconnect routine defined in the usb_driver either. + */ +static int hiddev_usbd_probe(struct usb_interface *intf, + const struct usb_device_id *hiddev_info) +{ + return -ENODEV; +} + + +static /* const */ struct usb_driver hiddev_driver = { + .name = "hiddev", + .probe = hiddev_usbd_probe, +}; + +int __init hiddev_init(void) +{ + return usb_register(&hiddev_driver); +} + +void hiddev_exit(void) +{ + usb_deregister(&hiddev_driver); +} diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h new file mode 100644 index 0000000..0023f96 --- /dev/null +++ b/drivers/hid/usbhid/usbhid.h @@ -0,0 +1,87 @@ +#ifndef __USBHID_H +#define __USBHID_H + +/* + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2006 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +/* API provided by hid-core.c for USB HID drivers */ +int usbhid_wait_io(struct hid_device* hid); +void usbhid_close(struct hid_device *hid); +int usbhid_open(struct hid_device *hid); +void usbhid_init_reports(struct hid_device *hid); +void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir); + +/* + * USB-specific HID struct, to be pointed to + * from struct hid_device->driver_data + */ + +struct usbhid_device { + struct hid_device *hid; /* pointer to corresponding HID dev */ + + struct usb_interface *intf; /* USB interface */ + int ifnum; /* USB interface number */ + + unsigned int bufsize; /* URB buffer size */ + + struct urb *urbin; /* Input URB */ + char *inbuf; /* Input buffer */ + dma_addr_t inbuf_dma; /* Input buffer dma */ + spinlock_t inlock; /* Input fifo spinlock */ + + struct urb *urbctrl; /* Control URB */ + struct usb_ctrlrequest *cr; /* Control request struct */ + dma_addr_t cr_dma; /* Control request struct dma */ + struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */ + unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ + char *ctrlbuf; /* Control buffer */ + dma_addr_t ctrlbuf_dma; /* Control buffer dma */ + spinlock_t ctrllock; /* Control fifo spinlock */ + + struct urb *urbout; /* Output URB */ + struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ + unsigned char outhead, outtail; /* Output pipe fifo head & tail */ + char *outbuf; /* Output buffer */ + dma_addr_t outbuf_dma; /* Output buffer dma */ + spinlock_t outlock; /* Output fifo spinlock */ + + unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ + struct timer_list io_retry; /* Retry timer */ + unsigned long stop_retry; /* Time to give up, in jiffies */ + unsigned int retry_delay; /* Delay length in ms */ + struct work_struct reset_work; /* Task context for resets */ + +}; + +#define hid_to_usb_dev(hid_dev) \ + container_of(hid_dev->dev->parent, struct usb_device, dev) + +#endif + diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c new file mode 100644 index 0000000..65aa12e --- /dev/null +++ b/drivers/hid/usbhid/usbkbd.c @@ -0,0 +1,367 @@ +/* + * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * USB HIDBP Keyboard support + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "" +#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_DESC "USB HID Boot Protocol keyboard driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +static unsigned char usb_kbd_keycode[256] = { + 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, + 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, + 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, + 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, + 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, + 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, + 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0, + 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, + 150,158,159,128,136,177,178,176,142,152,173,140 +}; + +struct usb_kbd { + struct input_dev *dev; + struct usb_device *usbdev; + unsigned char old[8]; + struct urb *irq, *led; + unsigned char newleds; + char name[128]; + char phys[64]; + + unsigned char *new; + struct usb_ctrlrequest *cr; + unsigned char *leds; + dma_addr_t cr_dma; + dma_addr_t new_dma; + dma_addr_t leds_dma; +}; + +static void usb_kbd_irq(struct urb *urb) +{ + struct usb_kbd *kbd = urb->context; + int i; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + for (i = 0; i < 8; i++) + input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); + + for (i = 2; i < 8; i++) { + + if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) { + if (usb_kbd_keycode[kbd->old[i]]) + input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); + else + info("Unknown key (scancode %#x) released.", kbd->old[i]); + } + + if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) { + if (usb_kbd_keycode[kbd->new[i]]) + input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); + else + info("Unknown key (scancode %#x) pressed.", kbd->new[i]); + } + } + + input_sync(kbd->dev); + + memcpy(kbd->old, kbd->new, 8); + +resubmit: + i = usb_submit_urb (urb, GFP_ATOMIC); + if (i) + err ("can't resubmit intr, %s-%s/input0, status %d", + kbd->usbdev->bus->bus_name, + kbd->usbdev->devpath, i); +} + +static int usb_kbd_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct usb_kbd *kbd = dev->private; + + if (type != EV_LED) + return -1; + + + kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | + (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | + (!!test_bit(LED_NUML, dev->led)); + + if (kbd->led->status == -EINPROGRESS) + return 0; + + if (*(kbd->leds) == kbd->newleds) + return 0; + + *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; + if (usb_submit_urb(kbd->led, GFP_ATOMIC)) + err("usb_submit_urb(leds) failed"); + + return 0; +} + +static void usb_kbd_led(struct urb *urb) +{ + struct usb_kbd *kbd = urb->context; + + if (urb->status) + warn("led urb status %d received", urb->status); + + if (*(kbd->leds) == kbd->newleds) + return; + + *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; + if (usb_submit_urb(kbd->led, GFP_ATOMIC)) + err("usb_submit_urb(leds) failed"); +} + +static int usb_kbd_open(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + kbd->irq->dev = kbd->usbdev; + if (usb_submit_urb(kbd->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_kbd_close(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + usb_kill_urb(kbd->irq); +} + +static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) +{ + if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) + return -1; + if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) + return -1; + if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma))) + return -1; + if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma))) + return -1; + if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) + return -1; + + return 0; +} + +static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) +{ + usb_free_urb(kbd->irq); + usb_free_urb(kbd->led); + if (kbd->new) + usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); + if (kbd->cr) + usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); + if (kbd->leds) + usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); +} + +static int usb_kbd_probe(struct usb_interface *iface, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(iface); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_kbd *kbd; + struct input_dev *input_dev; + int i, pipe, maxp; + int error = -ENOMEM; + + interface = iface->cur_altsetting; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbd || !input_dev) + goto fail1; + + if (usb_kbd_alloc_mem(dev, kbd)) + goto fail2; + + kbd->usbdev = dev; + kbd->dev = input_dev; + + if (dev->manufacturer) + strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(kbd->name, " ", sizeof(kbd->name)); + strlcat(kbd->name, dev->product, sizeof(kbd->name)); + } + + if (!strlen(kbd->name)) + snprintf(kbd->name, sizeof(kbd->name), + "USB HIDBP Keyboard %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); + strlcpy(kbd->phys, "/input0", sizeof(kbd->phys)); + + input_dev->name = kbd->name; + input_dev->phys = kbd->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->cdev.dev = &iface->dev; + input_dev->private = kbd; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); + input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); + + for (i = 0; i < 255; i++) + set_bit(usb_kbd_keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); + + input_dev->event = usb_kbd_event; + input_dev->open = usb_kbd_open; + input_dev->close = usb_kbd_close; + + usb_fill_int_urb(kbd->irq, dev, pipe, + kbd->new, (maxp > 8 ? 8 : maxp), + usb_kbd_irq, kbd, endpoint->bInterval); + kbd->irq->transfer_dma = kbd->new_dma; + kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kbd->cr->bRequest = 0x09; + kbd->cr->wValue = cpu_to_le16(0x200); + kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); + kbd->cr->wLength = cpu_to_le16(1); + + usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), + (void *) kbd->cr, kbd->leds, 1, + usb_kbd_led, kbd); + kbd->led->setup_dma = kbd->cr_dma; + kbd->led->transfer_dma = kbd->leds_dma; + kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); + + error = input_register_device(kbd->dev); + if (error) + goto fail2; + + usb_set_intfdata(iface, kbd); + return 0; + +fail2: + usb_kbd_free_mem(dev, kbd); +fail1: + input_free_device(input_dev); + kfree(kbd); + return error; +} + +static void usb_kbd_disconnect(struct usb_interface *intf) +{ + struct usb_kbd *kbd = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (kbd) { + usb_kill_urb(kbd->irq); + input_unregister_device(kbd->dev); + usb_kbd_free_mem(interface_to_usbdev(intf), kbd); + kfree(kbd); + } +} + +static struct usb_device_id usb_kbd_id_table [] = { + { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, + USB_INTERFACE_PROTOCOL_KEYBOARD) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_kbd_id_table); + +static struct usb_driver usb_kbd_driver = { + .name = "usbkbd", + .probe = usb_kbd_probe, + .disconnect = usb_kbd_disconnect, + .id_table = usb_kbd_id_table, +}; + +static int __init usb_kbd_init(void) +{ + int result = usb_register(&usb_kbd_driver); + if (result == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return result; +} + +static void __exit usb_kbd_exit(void) +{ + usb_deregister(&usb_kbd_driver); +} + +module_init(usb_kbd_init); +module_exit(usb_kbd_exit); diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c new file mode 100644 index 0000000..573776d --- /dev/null +++ b/drivers/hid/usbhid/usbmouse.c @@ -0,0 +1,252 @@ +/* + * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * USB HIDBP Mouse support + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.6" +#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_DESC "USB HID Boot Protocol mouse driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +struct usb_mouse { + char name[128]; + char phys[64]; + struct usb_device *usbdev; + struct input_dev *dev; + struct urb *irq; + + signed char *data; + dma_addr_t data_dma; +}; + +static void usb_mouse_irq(struct urb *urb) +{ + struct usb_mouse *mouse = urb->context; + signed char *data = mouse->data; + struct input_dev *dev = mouse->dev; + int status; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + input_report_key(dev, BTN_LEFT, data[0] & 0x01); + input_report_key(dev, BTN_RIGHT, data[0] & 0x02); + input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); + input_report_key(dev, BTN_SIDE, data[0] & 0x08); + input_report_key(dev, BTN_EXTRA, data[0] & 0x10); + + input_report_rel(dev, REL_X, data[1]); + input_report_rel(dev, REL_Y, data[2]); + input_report_rel(dev, REL_WHEEL, data[3]); + + input_sync(dev); +resubmit: + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status) + err ("can't resubmit intr, %s-%s/input0, status %d", + mouse->usbdev->bus->bus_name, + mouse->usbdev->devpath, status); +} + +static int usb_mouse_open(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + mouse->irq->dev = mouse->usbdev; + if (usb_submit_urb(mouse->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_mouse_close(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + usb_kill_urb(mouse->irq); +} + +static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_mouse *mouse; + struct input_dev *input_dev; + int pipe, maxp; + int error = -ENOMEM; + + interface = intf->cur_altsetting; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!mouse || !input_dev) + goto fail1; + + mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma); + if (!mouse->data) + goto fail1; + + mouse->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!mouse->irq) + goto fail2; + + mouse->usbdev = dev; + mouse->dev = input_dev; + + if (dev->manufacturer) + strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(mouse->name, " ", sizeof(mouse->name)); + strlcat(mouse->name, dev->product, sizeof(mouse->name)); + } + + if (!strlen(mouse->name)) + snprintf(mouse->name, sizeof(mouse->name), + "USB HIDBP Mouse %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); + strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); + + input_dev->name = mouse->name; + input_dev->phys = mouse->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->cdev.dev = &intf->dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); + input_dev->relbit[0] |= BIT(REL_WHEEL); + + input_dev->private = mouse; + input_dev->open = usb_mouse_open; + input_dev->close = usb_mouse_close; + + usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, + (maxp > 8 ? 8 : maxp), + usb_mouse_irq, mouse, endpoint->bInterval); + mouse->irq->transfer_dma = mouse->data_dma; + mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(mouse->dev); + if (error) + goto fail3; + + usb_set_intfdata(intf, mouse); + return 0; + +fail3: + usb_free_urb(mouse->irq); +fail2: + usb_buffer_free(dev, 8, mouse->data, mouse->data_dma); +fail1: + input_free_device(input_dev); + kfree(mouse); + return error; +} + +static void usb_mouse_disconnect(struct usb_interface *intf) +{ + struct usb_mouse *mouse = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (mouse) { + usb_kill_urb(mouse->irq); + input_unregister_device(mouse->dev); + usb_free_urb(mouse->irq); + usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); + kfree(mouse); + } +} + +static struct usb_device_id usb_mouse_id_table [] = { + { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, + USB_INTERFACE_PROTOCOL_MOUSE) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); + +static struct usb_driver usb_mouse_driver = { + .name = "usbmouse", + .probe = usb_mouse_probe, + .disconnect = usb_mouse_disconnect, + .id_table = usb_mouse_id_table, +}; + +static int __init usb_mouse_init(void) +{ + int retval = usb_register(&usb_mouse_driver); + if (retval == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return retval; +} + +static void __exit usb_mouse_exit(void) +{ + usb_deregister(&usb_mouse_driver); +} + +module_init(usb_mouse_init); +module_exit(usb_mouse_exit); diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index f1f0f5d..f5ebad5 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c @@ -141,10 +141,10 @@ static void ams_worker(struct work_struc int ams_sensor_attach(void) { int result; - u32 *prop; + const u32 *prop; /* Get orientation */ - prop = (u32*)get_property(ams_info.of_node, "orientation", NULL); + prop = get_property(ams_info.of_node, "orientation", NULL); if (!prop) return -ENODEV; ams_info.orient1 = *prop; diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index 0d24bdf..485d333 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c @@ -263,7 +263,7 @@ int __init ams_i2c_init(struct device_no { char *tmp_bus; int result; - u32 *prop; + const u32 *prop; mutex_lock(&ams_info.lock); @@ -276,7 +276,7 @@ int __init ams_i2c_init(struct device_no ams_info.bustype = BUS_I2C; /* look for bus either using "reg" or by path */ - prop = (u32*)get_property(ams_info.of_node, "reg", NULL); + prop = get_property(ams_info.of_node, "reg", NULL); if (!prop) { result = -ENODEV; diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c index 4636ae0..1b01c21 100644 --- a/drivers/hwmon/ams/ams-pmu.c +++ b/drivers/hwmon/ams/ams-pmu.c @@ -146,7 +146,7 @@ static void ams_pmu_exit(void) int __init ams_pmu_init(struct device_node *np) { - u32 *prop; + const u32 *prop; int result; mutex_lock(&ams_info.lock); @@ -160,7 +160,7 @@ int __init ams_pmu_init(struct device_no ams_info.bustype = BUS_HOST; /* Get PMU command, should be 0x4e, but we can never know */ - prop = (u32*)get_property(ams_info.of_node, "reg", NULL); + prop = get_property(ams_info.of_node, "reg", NULL); if (!prop) { result = -ENODEV; goto exit; diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index afb71c6..a9e0b30 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -310,14 +310,12 @@ static int proc_ide_read_driver ide_driver_t *ide_drv; int len; - down_read(&dev->bus->subsys.rwsem); if (dev->driver) { ide_drv = container_of(dev->driver, ide_driver_t, gen_driver); len = sprintf(page, "%s version %s\n", dev->driver->name, ide_drv->version); } else len = sprintf(page, "ide-default version 0.9.newide\n"); - up_read(&dev->bus->subsys.rwsem); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } @@ -327,7 +325,6 @@ static int ide_replace_subdriver(ide_dri int ret = 1; int err; - down_write(&dev->bus->subsys.rwsem); device_release_driver(dev); /* FIXME: device can still be in use by previous driver */ strlcpy(drive->driver_req, driver, sizeof(drive->driver_req)); @@ -345,7 +342,6 @@ static int ide_replace_subdriver(ide_dri } if (dev->driver && !strcmp(dev->driver->name, driver)) ret = 0; - up_write(&dev->bus->subsys.rwsem); return ret; } diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig index cd84a55..61d7809 100644 --- a/drivers/ieee1394/Kconfig +++ b/drivers/ieee1394/Kconfig @@ -1,11 +1,8 @@ -# -*- shell-script -*- - menu "IEEE 1394 (FireWire) support" config IEEE1394 tristate "IEEE 1394 (FireWire) support" depends on PCI || BROKEN - select NET help IEEE 1394 describes a high performance serial bus, which is also known as FireWire(tm) or i.Link(tm) and is used for connecting all @@ -35,24 +32,7 @@ config IEEE1394_VERBOSEDEBUG Say Y if you really want or need the debugging output, everyone else says N. -config IEEE1394_EXTRA_CONFIG_ROMS - bool "Build in extra config rom entries for certain functionality" - depends on IEEE1394 - help - Some IEEE1394 functionality depends on extra config rom entries - being available in the host adapters CSR. These options will - allow you to choose which ones. - -config IEEE1394_CONFIG_ROM_IP1394 - bool "IP-1394 Entry" - depends on IEEE1394_EXTRA_CONFIG_ROMS && IEEE1394 - help - Adds an entry for using IP-over-1394. If you want to use your - IEEE1394 bus as a network for IP systems (including interacting - with MacOSX and WinXP IP-over-1394), enable this option and the - eth1394 option below. - -comment "Device Drivers" +comment "Controllers" depends on IEEE1394 comment "Texas Instruments PCILynx requires I2C" @@ -70,6 +50,10 @@ config IEEE1394_PCILYNX To compile this driver as a module, say M here: the module will be called pcilynx. + Only some old and now very rare PCI and CardBus cards and + PowerMacs G3 B&W contain the PCILynx controller. Therefore + almost everybody can say N here. + config IEEE1394_OHCI1394 tristate "OHCI-1394 support" depends on PCI && IEEE1394 @@ -83,7 +67,7 @@ config IEEE1394_OHCI1394 To compile this driver as a module, say M here: the module will be called ohci1394. -comment "Protocol Drivers" +comment "Protocols" depends on IEEE1394 config IEEE1394_VIDEO1394 @@ -121,11 +105,15 @@ config IEEE1394_SBP2_PHYS_DMA This option is buggy and currently broken on some architectures. If unsure, say N. +config IEEE1394_ETH1394_ROM_ENTRY + depends on IEEE1394 + bool + default n + config IEEE1394_ETH1394 - tristate "Ethernet over 1394" + tristate "IP over 1394" depends on IEEE1394 && EXPERIMENTAL && INET - select IEEE1394_CONFIG_ROM_IP1394 - select IEEE1394_EXTRA_CONFIG_ROMS + select IEEE1394_ETH1394_ROM_ENTRY help This driver implements a functional majority of RFC 2734: IPv4 over 1394. It will provide IP connectivity with implementations of RFC @@ -134,6 +122,8 @@ config IEEE1394_ETH1394 This driver is still considered experimental. It does not yet support MCAP, therefore multicast support is significantly limited. + The module is called eth1394 although it does not emulate Ethernet. + config IEEE1394_DV1394 tristate "OHCI-DV I/O support (deprecated)" depends on IEEE1394 && IEEE1394_OHCI1394 @@ -146,12 +136,12 @@ config IEEE1394_RAWIO tristate "Raw IEEE1394 I/O support" depends on IEEE1394 help - Say Y here if you want support for the raw device. This is generally - a good idea, so you should say Y here. The raw device enables - direct communication of user programs with the IEEE 1394 bus and - thus with the attached peripherals. + This option adds support for the raw1394 device file which enables + direct communication of user programs with the IEEE 1394 bus and thus + with the attached peripherals. Almost all application programs which + access FireWire require this option. - To compile this driver as a module, say M here: the - module will be called raw1394. + To compile this driver as a module, say M here: the module will be + called raw1394. endmenu diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c index e2de6fa..1b98120 100644 --- a/drivers/ieee1394/config_roms.c +++ b/drivers/ieee1394/config_roms.c @@ -26,12 +26,6 @@ struct hpsb_config_rom_entry { /* Base initialization, called at module load */ int (*init)(void); - /* Add entry to specified host */ - int (*add)(struct hpsb_host *host); - - /* Remove entry from specified host */ - void (*remove)(struct hpsb_host *host); - /* Cleanup called at module exit */ void (*cleanup)(void); @@ -39,7 +33,7 @@ struct hpsb_config_rom_entry { unsigned int flag; }; - +/* The default host entry. This must succeed. */ int hpsb_default_host_entry(struct hpsb_host *host) { struct csr1212_keyval *root; @@ -63,9 +57,9 @@ int hpsb_default_host_entry(struct hpsb_ return -ENOMEM; } - ret = csr1212_associate_keyval(vend_id, text); + csr1212_associate_keyval(vend_id, text); csr1212_release_keyval(text); - ret |= csr1212_attach_keyval_to_directory(root, vend_id); + ret = csr1212_attach_keyval_to_directory(root, vend_id); csr1212_release_keyval(vend_id); if (ret != CSR1212_SUCCESS) { csr1212_destroy_csr(host->csr.rom); @@ -78,7 +72,7 @@ int hpsb_default_host_entry(struct hpsb_ } -#ifdef CONFIG_IEEE1394_CONFIG_ROM_IP1394 +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY #include "eth1394.h" static struct csr1212_keyval *ip1394_ud; @@ -103,10 +97,12 @@ static int config_rom_ip1394_init(void) if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc) goto ip1394_fail; - if (csr1212_associate_keyval(spec_id, spec_desc) == CSR1212_SUCCESS && - csr1212_associate_keyval(ver, ver_desc) == CSR1212_SUCCESS && - csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) == CSR1212_SUCCESS && - csr1212_attach_keyval_to_directory(ip1394_ud, ver) == CSR1212_SUCCESS) + csr1212_associate_keyval(spec_id, spec_desc); + csr1212_associate_keyval(ver, ver_desc); + if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) + == CSR1212_SUCCESS && + csr1212_attach_keyval_to_directory(ip1394_ud, ver) + == CSR1212_SUCCESS) ret = 0; ip1394_fail: @@ -135,7 +131,7 @@ static void config_rom_ip1394_cleanup(vo } } -static int config_rom_ip1394_add(struct hpsb_host *host) +int hpsb_config_rom_ip1394_add(struct hpsb_host *host) { if (!ip1394_ud) return -ENODEV; @@ -144,92 +140,55 @@ static int config_rom_ip1394_add(struct ip1394_ud) != CSR1212_SUCCESS) return -ENOMEM; + host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394; + host->update_config_rom = 1; return 0; } +EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add); -static void config_rom_ip1394_remove(struct hpsb_host *host) +void hpsb_config_rom_ip1394_remove(struct hpsb_host *host) { csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud); + host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394; + host->update_config_rom = 1; } +EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove); static struct hpsb_config_rom_entry ip1394_entry = { .name = "ip1394", .init = config_rom_ip1394_init, - .add = config_rom_ip1394_add, - .remove = config_rom_ip1394_remove, .cleanup = config_rom_ip1394_cleanup, .flag = HPSB_CONFIG_ROM_ENTRY_IP1394, }; -#endif /* CONFIG_IEEE1394_CONFIG_ROM_IP1394 */ +#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */ static struct hpsb_config_rom_entry *const config_rom_entries[] = { -#ifdef CONFIG_IEEE1394_CONFIG_ROM_IP1394 +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY &ip1394_entry, #endif - NULL, }; - +/* Initialize all config roms */ int hpsb_init_config_roms(void) { int i, error = 0; - for (i = 0; config_rom_entries[i]; i++) { - if (!config_rom_entries[i]->init) - continue; - + for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) if (config_rom_entries[i]->init()) { HPSB_ERR("Failed to initialize config rom entry `%s'", config_rom_entries[i]->name); error = -1; - } else - HPSB_DEBUG("Initialized config rom entry `%s'", - config_rom_entries[i]->name); - } - - return error; -} - -void hpsb_cleanup_config_roms(void) -{ - int i; - - for (i = 0; config_rom_entries[i]; i++) { - if (config_rom_entries[i]->cleanup) - config_rom_entries[i]->cleanup(); - } -} - -int hpsb_add_extra_config_roms(struct hpsb_host *host) -{ - int i, error = 0; - - for (i = 0; config_rom_entries[i]; i++) { - if (config_rom_entries[i]->add(host)) { - HPSB_ERR("fw-host%d: Failed to attach config rom entry `%s'", - host->id, config_rom_entries[i]->name); - error = -1; - } else { - host->config_roms |= config_rom_entries[i]->flag; - host->update_config_rom = 1; } - } return error; } -void hpsb_remove_extra_config_roms(struct hpsb_host *host) +/* Cleanup all config roms */ +void hpsb_cleanup_config_roms(void) { int i; - for (i = 0; config_rom_entries[i]; i++) { - if (!(host->config_roms & config_rom_entries[i]->flag)) - continue; - - config_rom_entries[i]->remove(host); - - host->config_roms &= ~config_rom_entries[i]->flag; - host->update_config_rom = 1; - } + for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) + config_rom_entries[i]->cleanup(); } diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h index 0a70544..1f5cd1f 100644 --- a/drivers/ieee1394/config_roms.h +++ b/drivers/ieee1394/config_roms.h @@ -1,27 +1,19 @@ #ifndef _IEEE1394_CONFIG_ROMS_H #define _IEEE1394_CONFIG_ROMS_H -#include "ieee1394_types.h" -#include "hosts.h" +struct hpsb_host; -/* The default host entry. This must succeed. */ int hpsb_default_host_entry(struct hpsb_host *host); - -/* Initialize all config roms */ int hpsb_init_config_roms(void); - -/* Cleanup all config roms */ void hpsb_cleanup_config_roms(void); -/* Add extra config roms to specified host */ -int hpsb_add_extra_config_roms(struct hpsb_host *host); - -/* Remove extra config roms from specified host */ -void hpsb_remove_extra_config_roms(struct hpsb_host *host); - - /* List of flags to check if a host contains a certain extra config rom * entry. Available in the host->config_roms member. */ #define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001 +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY +int hpsb_config_rom_ip1394_add(struct hpsb_host *host); +void hpsb_config_rom_ip1394_remove(struct hpsb_host *host); +#endif + #endif /* _IEEE1394_CONFIG_ROMS_H */ diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c index c28f639..d08166b 100644 --- a/drivers/ieee1394/csr1212.c +++ b/drivers/ieee1394/csr1212.c @@ -31,12 +31,13 @@ /* TODO List: * - Verify interface consistency: i.e., public functions that take a size * parameter expect size to be in bytes. - * - Convenience functions for reading a block of data from a given offset. */ -#ifndef __KERNEL__ -#include -#endif +#include +#include +#include +#include +#include #include "csr1212.h" @@ -46,7 +47,7 @@ #define __I (1 << CSR1212_KV_TYPE_IMMEDI #define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET) #define __D (1 << CSR1212_KV_TYPE_DIRECTORY) #define __L (1 << CSR1212_KV_TYPE_LEAF) -static const u_int8_t csr1212_key_id_type_map[0x30] = { +static const u8 csr1212_key_id_type_map[0x30] = { __C, /* used by Apple iSight */ __D | __L, /* Descriptor */ __I | __D | __L, /* Bus_Dependent_Info */ @@ -82,10 +83,10 @@ #undef __D #undef __L -#define quads_to_bytes(_q) ((_q) * sizeof(u_int32_t)) -#define bytes_to_quads(_b) (((_b) + sizeof(u_int32_t) - 1) / sizeof(u_int32_t)) +#define quads_to_bytes(_q) ((_q) * sizeof(u32)) +#define bytes_to_quads(_b) (((_b) + sizeof(u32) - 1) / sizeof(u32)) -static inline void free_keyval(struct csr1212_keyval *kv) +static void free_keyval(struct csr1212_keyval *kv) { if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) @@ -94,14 +95,14 @@ static inline void free_keyval(struct cs CSR1212_FREE(kv); } -static u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length) +static u16 csr1212_crc16(const u32 *buffer, size_t length) { int shift; - u_int32_t data; - u_int16_t sum, crc = 0; + u32 data; + u16 sum, crc = 0; for (; length; length--) { - data = CSR1212_BE32_TO_CPU(*buffer); + data = be32_to_cpu(*buffer); buffer++; for (shift = 28; shift >= 0; shift -= 4 ) { sum = ((crc >> 12) ^ (data >> shift)) & 0xf; @@ -110,21 +111,18 @@ static u_int16_t csr1212_crc16(const u_i crc &= 0xffff; } - return CSR1212_CPU_TO_BE16(crc); + return cpu_to_be16(crc); } -#if 0 -/* Microsoft computes the CRC with the bytes in reverse order. Therefore we - * have a special version of the CRC algorithm to account for their buggy - * software. */ -static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length) +/* Microsoft computes the CRC with the bytes in reverse order. */ +static u16 csr1212_msft_crc16(const u32 *buffer, size_t length) { int shift; - u_int32_t data; - u_int16_t sum, crc = 0; + u32 data; + u16 sum, crc = 0; for (; length; length--) { - data = CSR1212_LE32_TO_CPU(*buffer); + data = le32_to_cpu(*buffer); buffer++; for (shift = 28; shift >= 0; shift -= 4 ) { sum = ((crc >> 12) ^ (data >> shift)) & 0xf; @@ -133,38 +131,35 @@ static u_int16_t csr1212_msft_crc16(cons crc &= 0xffff; } - return CSR1212_CPU_TO_BE16(crc); + return cpu_to_be16(crc); } -#endif -static inline struct csr1212_dentry *csr1212_find_keyval(struct csr1212_keyval *dir, - struct csr1212_keyval *kv) +static struct csr1212_dentry * +csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv) { struct csr1212_dentry *pos; for (pos = dir->value.directory.dentries_head; - pos != NULL; pos = pos->next) { + pos != NULL; pos = pos->next) if (pos->kv == kv) return pos; - } return NULL; } - -static inline struct csr1212_keyval *csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, - u_int32_t offset) +static struct csr1212_keyval * +csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset) { struct csr1212_keyval *kv; - for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) { + for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) if (kv->offset == offset) return kv; - } return NULL; } /* Creation Routines */ + struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, size_t bus_info_size, void *private) { @@ -202,27 +197,17 @@ struct csr1212_csr *csr1212_create_csr(s return csr; } - - void csr1212_init_local_csr(struct csr1212_csr *csr, - const u_int32_t *bus_info_data, int max_rom) + const u32 *bus_info_data, int max_rom) { static const int mr_map[] = { 4, 64, 1024, 0 }; -#ifdef __KERNEL__ BUG_ON(max_rom & ~0x3); csr->max_rom = mr_map[max_rom]; -#else - if (max_rom & ~0x3) /* caller supplied invalid argument */ - csr->max_rom = 0; - else - csr->max_rom = mr_map[max_rom]; -#endif memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len); } - -static struct csr1212_keyval *csr1212_new_keyval(u_int8_t type, u_int8_t key) +static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key) { struct csr1212_keyval *kv; @@ -246,10 +231,11 @@ static struct csr1212_keyval *csr1212_ne return kv; } -struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value) +struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); if (!kv) return NULL; @@ -258,10 +244,12 @@ struct csr1212_keyval *csr1212_new_immed return kv; } -struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len) +static struct csr1212_keyval * +csr1212_new_leaf(u8 key, const void *data, size_t data_len) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); if (!kv) return NULL; @@ -285,10 +273,12 @@ struct csr1212_keyval *csr1212_new_leaf( return kv; } -struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset) +static struct csr1212_keyval * +csr1212_new_csr_offset(u8 key, u32 csr_offset) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); if (!kv) return NULL; @@ -299,10 +289,11 @@ struct csr1212_keyval *csr1212_new_csr_o return kv; } -struct csr1212_keyval *csr1212_new_directory(u_int8_t key) +struct csr1212_keyval *csr1212_new_directory(u8 key) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); if (!kv) return NULL; @@ -314,43 +305,29 @@ struct csr1212_keyval *csr1212_new_direc return kv; } -int csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate) +void csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate) { - if (!kv || !associate) - return CSR1212_EINVAL; - - if (kv->key.id == CSR1212_KV_ID_DESCRIPTOR || - (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && - associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && - associate->key.id < 0x30)) - return CSR1212_EINVAL; - - if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) - return CSR1212_EINVAL; - - if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) - return CSR1212_EINVAL; - - if (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) - return CSR1212_EINVAL; - - if (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY) - return CSR1212_EINVAL; + BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR || + (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && + associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && + associate->key.id < 0x30) || + (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) || + (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) || + (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) || + (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY)); if (kv->associate) csr1212_release_keyval(kv->associate); associate->refcnt++; kv->associate = associate; - - return CSR1212_SUCCESS; } int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, @@ -358,12 +335,11 @@ int csr1212_attach_keyval_to_directory(s { struct csr1212_dentry *dentry; - if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) - return CSR1212_EINVAL; + BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY); dentry = CSR1212_MALLOC(sizeof(*dentry)); if (!dentry) - return CSR1212_ENOMEM; + return -ENOMEM; dentry->kv = kv; @@ -382,66 +358,22 @@ int csr1212_attach_keyval_to_directory(s return CSR1212_SUCCESS; } -struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key, - u_int32_t value) -{ - struct csr1212_keyval *kvs, *kvk, *kvv; - - kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); - kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); - kvv = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_DATA, value); - - if (!kvs || !kvk || !kvv) { - if (kvs) - free_keyval(kvs); - if (kvk) - free_keyval(kvk); - if (kvv) - free_keyval(kvv); - return NULL; - } - - /* Don't keep a local reference to the extended key or value. */ - kvk->refcnt = 0; - kvv->refcnt = 0; - - csr1212_associate_keyval(kvk, kvv); - csr1212_associate_keyval(kvs, kvk); - - return kvs; -} - -struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key, - const void *data, size_t data_len) -{ - struct csr1212_keyval *kvs, *kvk, *kvv; - - kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); - kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); - kvv = csr1212_new_leaf(CSR1212_KV_ID_EXTENDED_DATA, data, data_len); - - if (!kvs || !kvk || !kvv) { - if (kvs) - free_keyval(kvs); - if (kvk) - free_keyval(kvk); - if (kvv) - free_keyval(kvv); - return NULL; - } - - /* Don't keep a local reference to the extended key or value. */ - kvk->refcnt = 0; - kvv->refcnt = 0; - - csr1212_associate_keyval(kvk, kvv); - csr1212_associate_keyval(kvs, kvk); - - return kvs; -} - -struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t specifier_id, - const void *data, size_t data_len) +#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ + (&((kv)->value.leaf.data[1])) + +#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ + ((kv)->value.leaf.data[0] = \ + cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ + ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) +#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ + ((kv)->value.leaf.data[0] = \ + cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ + ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) + +static struct csr1212_keyval * +csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id, + const void *data, size_t data_len) { struct csr1212_keyval *kv; @@ -453,197 +385,72 @@ struct csr1212_keyval *csr1212_new_descr CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype); CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id); - if (data) { + if (data) memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len); - } - - return kv; -} - - -struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, - u_int16_t cset, - u_int16_t language, - const void *data, - size_t data_len) -{ - struct csr1212_keyval *kv; - char *lstr; - - kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len + - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); - if (!kv) - return NULL; - - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth); - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset); - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); - - lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); - - /* make sure last quadlet is zeroed out */ - *((u_int32_t*)&(lstr[(data_len - 1) & ~0x3])) = 0; - - /* don't copy the NUL terminator */ - memcpy(lstr, data, data_len); return kv; } +/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */ static int csr1212_check_minimal_ascii(const char *s) { static const char minimal_ascii_table[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f, - 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 1 2 4 8 16 32 64 128 */ + 128, /* --, --, --, --, --, --, --, 07, */ + 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */ + 0, /* --, --, --, --, --, --, --, --, */ + 0, /* --, --, --, --, --, --, --, --, */ + 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */ + 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */ + 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */ + 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */ + 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */ + 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */ + 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */ + 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */ + 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */ + 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */ + 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */ + 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */ }; + int i, j; + for (; *s; s++) { - if (minimal_ascii_table[*s & 0x7F] != *s) - return -1; /* failed */ + i = *s >> 3; /* i = *s / 8; */ + j = 1 << (*s & 3); /* j = 1 << (*s % 8); */ + + if (i >= ARRAY_SIZE(minimal_ascii_table) || + !(minimal_ascii_table[i] & j)) + return -EINVAL; } - /* String conforms to minimal-ascii, as specified by IEEE 1212, - * par. 7.4 */ return 0; } +/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */ struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) { - /* Check if string conform to minimal_ascii format */ - if (csr1212_check_minimal_ascii(s)) - return NULL; - - /* IEEE 1212, par. 7.5.4.1 Textual descriptors (minimal ASCII) */ - return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s)); -} - -struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, - u_int8_t palette_depth, - u_int8_t color_space, - u_int16_t language, - u_int16_t hscan, - u_int16_t vscan, - u_int32_t *palette, - u_int32_t *pixels) -{ - static const int pd[4] = { 0, 4, 16, 256 }; - static const int cs[16] = { 4, 2 }; struct csr1212_keyval *kv; - int palette_size; - int pixel_size = (hscan * vscan + 3) & ~0x3; + u32 *text; + size_t str_len, quads; - if (!pixels || (!palette && palette_depth) || - (palette_depth & ~0x3) || (color_space & ~0xf)) + if (!s || !*s || csr1212_check_minimal_ascii(s)) return NULL; - palette_size = pd[palette_depth] * cs[color_space]; - - kv = csr1212_new_descriptor_leaf(1, 0, NULL, - palette_size + pixel_size + - CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD); + str_len = strlen(s); + quads = bytes_to_quads(str_len); + kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) + + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); if (!kv) return NULL; - CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan); - - if (palette_size) - memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv), palette, - palette_size); - - memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(kv), pixels, pixel_size); - - return kv; -} - -struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, - u_int64_t address) -{ - struct csr1212_keyval *kv; - - /* IEEE 1212, par. 7.5.4.3 Modifiable descriptors */ - kv = csr1212_new_leaf(CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR, NULL, sizeof(u_int64_t)); - if(!kv) - return NULL; - - CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, max_size); - CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, address); - CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, address); + kv->value.leaf.data[1] = 0; /* width, character_set, language */ + text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); + text[quads - 1] = 0; /* padding */ + memcpy(text, s, str_len); return kv; } -static int csr1212_check_keyword(const char *s) -{ - for (; *s; s++) { - - if (('A' <= *s) && (*s <= 'Z')) - continue; - if (('0' <= *s) && (*s <= '9')) - continue; - if (*s == '-') - continue; - - return -1; /* failed */ - } - /* String conforms to keyword, as specified by IEEE 1212, - * par. 7.6.5 */ - return CSR1212_SUCCESS; -} - -struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[]) -{ - struct csr1212_keyval *kv; - char *buffer; - int i, data_len = 0; - - /* Check all keywords to see if they conform to restrictions: - * Only the following characters is allowed ['A'..'Z','0'..'9','-'] - * Each word is zero-terminated. - * Also calculate the total length of the keywords. - */ - for (i = 0; i < strc; i++) { - if (!strv[i] || csr1212_check_keyword(strv[i])) { - return NULL; - } - data_len += strlen(strv[i]) + 1; /* Add zero-termination char. */ - } - - /* IEEE 1212, par. 7.6.5 Keyword leaves */ - kv = csr1212_new_leaf(CSR1212_KV_ID_KEYWORD, NULL, data_len); - if (!kv) - return NULL; - - buffer = (char *)kv->value.leaf.data; - - /* make sure last quadlet is zeroed out */ - *((u_int32_t*)&(buffer[(data_len - 1) & ~0x3])) = 0; - - /* Copy keyword(s) into leaf data buffer */ - for (i = 0; i < strc; i++) { - int len = strlen(strv[i]) + 1; - memcpy(buffer, strv[i], len); - buffer += len; - } - return kv; -} - /* Destruction Routines */ @@ -674,23 +481,12 @@ void csr1212_detach_keyval_from_director csr1212_release_keyval(kv); } - -void csr1212_disassociate_keyval(struct csr1212_keyval *kv) -{ - if (kv->associate) { - csr1212_release_keyval(kv->associate); - } - - kv->associate = NULL; -} - - /* This function is used to free the memory taken by a keyval. If the given * keyval is a directory type, then any keyvals contained in that directory * will be destroyed as well if their respective refcnts are 0. By means of * list manipulation, this routine will descend a directory structure in a * non-recursive manner. */ -void _csr1212_destroy_keyval(struct csr1212_keyval *kv) +static void csr1212_destroy_keyval(struct csr1212_keyval *kv) { struct csr1212_keyval *k, *a; struct csr1212_dentry dentry; @@ -715,11 +511,13 @@ void _csr1212_destroy_keyval(struct csr1 a = k->associate; if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { - /* If the current entry is a directory, then move all + /* If the current entry is a directory, move all * the entries to the destruction list. */ if (k->value.directory.dentries_head) { - tail->next = k->value.directory.dentries_head; - k->value.directory.dentries_head->prev = tail; + tail->next = + k->value.directory.dentries_head; + k->value.directory.dentries_head->prev = + tail; tail = k->value.directory.dentries_tail; } } @@ -729,15 +527,22 @@ void _csr1212_destroy_keyval(struct csr1 head = head->next; if (head) { - if (head->prev && head->prev != &dentry) { + if (head->prev && head->prev != &dentry) CSR1212_FREE(head->prev); - } head->prev = NULL; - } else if (tail != &dentry) + } else if (tail != &dentry) { CSR1212_FREE(tail); + } } } +void csr1212_release_keyval(struct csr1212_keyval *kv) +{ + if (kv->refcnt > 1) + kv->refcnt--; + else + csr1212_destroy_keyval(kv); +} void csr1212_destroy_csr(struct csr1212_csr *csr) { @@ -763,49 +568,51 @@ void csr1212_destroy_csr(struct csr1212_ } - /* CSR Image Creation */ static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) { struct csr1212_csr_rom_cache *cache; - u_int64_t csr_addr; + u64 csr_addr; - if (!csr || !csr->ops || !csr->ops->allocate_addr_range || - !csr->ops->release_addr || csr->max_rom < 1) - return CSR1212_EINVAL; + BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range || + !csr->ops->release_addr || csr->max_rom < 1); /* ROM size must be a multiple of csr->max_rom */ romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); - csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, csr->private); - if (csr_addr == CSR1212_INVALID_ADDR_SPACE) { - return CSR1212_ENOMEM; - } + csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, + csr->private); + if (csr_addr == CSR1212_INVALID_ADDR_SPACE) + return -ENOMEM; + if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { /* Invalid address returned from allocate_addr_range(). */ csr->ops->release_addr(csr_addr, csr->private); - return CSR1212_ENOMEM; + return -ENOMEM; } - cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize); + cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, + romsize); if (!cache) { csr->ops->release_addr(csr_addr, csr->private); - return CSR1212_ENOMEM; + return -ENOMEM; } - cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, CSR1212_KV_ID_EXTENDED_ROM); + cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, + CSR1212_KV_ID_EXTENDED_ROM); if (!cache->ext_rom) { csr->ops->release_addr(csr_addr, csr->private); CSR1212_FREE(cache); - return CSR1212_ENOMEM; + return -ENOMEM; } - if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != CSR1212_SUCCESS) { + if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != + CSR1212_SUCCESS) { csr1212_release_keyval(cache->ext_rom); csr->ops->release_addr(csr_addr, csr->private); CSR1212_FREE(cache); - return CSR1212_ENOMEM; + return -ENOMEM; } cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; cache->ext_rom->value.leaf.len = -1; @@ -818,8 +625,8 @@ static int csr1212_append_new_cache(stru return CSR1212_SUCCESS; } -static inline void csr1212_remove_cache(struct csr1212_csr *csr, - struct csr1212_csr_rom_cache *cache) +static void csr1212_remove_cache(struct csr1212_csr *csr, + struct csr1212_csr_rom_cache *cache) { if (csr->cache_head == cache) csr->cache_head = cache->next; @@ -832,7 +639,8 @@ static inline void csr1212_remove_cache( cache->next->prev = cache->prev; if (cache->ext_rom) { - csr1212_detach_keyval_from_directory(csr->root_kv, cache->ext_rom); + csr1212_detach_keyval_from_directory(csr->root_kv, + cache->ext_rom); csr1212_release_keyval(cache->ext_rom); } @@ -852,28 +660,29 @@ static int csr1212_generate_layout_subdi dentry = dentry->next) { for (dkv = dentry->kv; dkv; dkv = dkv->associate) { /* Special Case: Extended Key Specifier_ID */ - if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) { + if (dkv->key.id == + CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) last_extkey_spec = dkv; - } else if (dkv->value.immediate != last_extkey_spec->value.immediate) { + else if (dkv->value.immediate != + last_extkey_spec->value.immediate) last_extkey_spec = dkv; - } else { + else continue; - } /* Special Case: Extended Key */ } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) { + if (last_extkey == NULL) last_extkey = dkv; - } else if (dkv->value.immediate != last_extkey->value.immediate) { + else if (dkv->value.immediate != + last_extkey->value.immediate) last_extkey = dkv; - } else { + else continue; - } } num_entries += 1; - switch(dkv->key.type) { + switch (dkv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: @@ -891,8 +700,9 @@ static int csr1212_generate_layout_subdi /* Special case: Extended ROM leafs */ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { dkv->value.leaf.len = -1; - /* Don't add Extended ROM leafs in the layout list, - * they are handled differently. */ + /* Don't add Extended ROM leafs in the + * layout list, they are handled + * differently. */ break; } @@ -908,20 +718,21 @@ static int csr1212_generate_layout_subdi return num_entries; } -size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) +static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) { struct csr1212_keyval *ltail = kv; size_t agg_size = 0; - while(kv) { - switch(kv->key.type) { + while (kv) { + switch (kv->key.type) { case CSR1212_KV_TYPE_LEAF: /* Add 1 quadlet for crc/len field */ agg_size += kv->value.leaf.len + 1; break; case CSR1212_KV_TYPE_DIRECTORY: - kv->value.directory.len = csr1212_generate_layout_subdir(kv, <ail); + kv->value.directory.len = + csr1212_generate_layout_subdir(kv, <ail); /* Add 1 quadlet for crc/len field */ agg_size += kv->value.directory.len + 1; break; @@ -931,9 +742,9 @@ size_t csr1212_generate_layout_order(str return quads_to_bytes(agg_size); } -struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, - struct csr1212_keyval *start_kv, - int start_pos) +static struct csr1212_keyval * +csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, + struct csr1212_keyval *start_kv, int start_pos) { struct csr1212_keyval *kv = start_kv; struct csr1212_keyval *okv = start_kv; @@ -942,13 +753,12 @@ struct csr1212_keyval *csr1212_generate_ cache->layout_head = kv; - while(kv && pos < cache->size) { + while (kv && pos < cache->size) { /* Special case: Extended ROM leafs */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) kv->offset = cache->offset + pos; - } - switch(kv->key.type) { + switch (kv->key.type) { case CSR1212_KV_TYPE_LEAF: kv_len = kv->value.leaf.len; break; @@ -959,6 +769,7 @@ struct csr1212_keyval *csr1212_generate_ default: /* Should never get here */ + WARN_ON(1); break; } @@ -972,46 +783,55 @@ struct csr1212_keyval *csr1212_generate_ } cache->layout_tail = okv; - cache->len = (okv->offset - cache->offset) + quads_to_bytes(okv_len + 1); + cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1); return kv; } -static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir, - u_int32_t *data_buffer) +#define CSR1212_KV_KEY_SHIFT 24 +#define CSR1212_KV_KEY_TYPE_SHIFT 6 +#define CSR1212_KV_KEY_ID_MASK 0x3f +#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */ + +static void +csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer) { struct csr1212_dentry *dentry; struct csr1212_keyval *last_extkey_spec = NULL; struct csr1212_keyval *last_extkey = NULL; int index = 0; - for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) { + for (dentry = dir->value.directory.dentries_head; + dentry; + dentry = dentry->next) { struct csr1212_keyval *a; for (a = dentry->kv; a; a = a->associate) { - u_int32_t value = 0; + u32 value = 0; /* Special Case: Extended Key Specifier_ID */ - if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) { + if (a->key.id == + CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) last_extkey_spec = a; - } else if (a->value.immediate != last_extkey_spec->value.immediate) { + else if (a->value.immediate != + last_extkey_spec->value.immediate) last_extkey_spec = a; - } else { + else continue; - } + /* Special Case: Extended Key */ } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) { + if (last_extkey == NULL) last_extkey = a; - } else if (a->value.immediate != last_extkey->value.immediate) { + else if (a->value.immediate != + last_extkey->value.immediate) last_extkey = a; - } else { + else continue; - } } - switch(a->key.type) { + switch (a->key.type) { case CSR1212_KV_TYPE_IMMEDIATE: value = a->value.immediate; break; @@ -1030,32 +850,46 @@ static void csr1212_generate_tree_subdir break; default: /* Should never get here */ - break; /* GDB breakpoint */ + WARN_ON(1); + break; } - value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << CSR1212_KV_KEY_SHIFT; + value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << + CSR1212_KV_KEY_SHIFT; value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << - (CSR1212_KV_KEY_SHIFT + CSR1212_KV_KEY_TYPE_SHIFT); - data_buffer[index] = CSR1212_CPU_TO_BE32(value); + (CSR1212_KV_KEY_SHIFT + + CSR1212_KV_KEY_TYPE_SHIFT); + data_buffer[index] = cpu_to_be32(value); index++; } } } -void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) +struct csr1212_keyval_img { + u16 length; + u16 crc; + + /* Must be last */ + u32 data[0]; /* older gcc can't handle [] which is standard */ +}; + +static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) { struct csr1212_keyval *kv, *nkv; struct csr1212_keyval_img *kvi; - for (kv = cache->layout_head; kv != cache->layout_tail->next; kv = nkv) { - kvi = (struct csr1212_keyval_img *) - (cache->data + bytes_to_quads(kv->offset - cache->offset)); - switch(kv->key.type) { + for (kv = cache->layout_head; + kv != cache->layout_tail->next; + kv = nkv) { + kvi = (struct csr1212_keyval_img *)(cache->data + + bytes_to_quads(kv->offset - cache->offset)); + switch (kv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: /* Should never get here */ - break; /* GDB breakpoint */ + WARN_ON(1); + break; case CSR1212_KV_TYPE_LEAF: /* Don't copy over Extended ROM areas, they are @@ -1064,15 +898,16 @@ void csr1212_fill_cache(struct csr1212_c memcpy(kvi->data, kv->value.leaf.data, quads_to_bytes(kv->value.leaf.len)); - kvi->length = CSR1212_CPU_TO_BE16(kv->value.leaf.len); + kvi->length = cpu_to_be16(kv->value.leaf.len); kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); break; case CSR1212_KV_TYPE_DIRECTORY: csr1212_generate_tree_subdir(kv, kvi->data); - kvi->length = CSR1212_CPU_TO_BE16(kv->value.directory.len); - kvi->crc = csr1212_crc16(kvi->data, kv->value.directory.len); + kvi->length = cpu_to_be16(kv->value.directory.len); + kvi->crc = csr1212_crc16(kvi->data, + kv->value.directory.len); break; } @@ -1086,6 +921,10 @@ void csr1212_fill_cache(struct csr1212_c } } +/* This size is arbitrarily chosen. + * The struct overhead is subtracted for more economic allocations. */ +#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache)) + int csr1212_generate_csr_image(struct csr1212_csr *csr) { struct csr1212_bus_info_block_img *bi; @@ -1095,8 +934,7 @@ int csr1212_generate_csr_image(struct cs int ret; int init_offset; - if (!csr) - return CSR1212_EINVAL; + BUG_ON(!csr); cache = csr->cache_head; @@ -1113,18 +951,21 @@ int csr1212_generate_csr_image(struct cs init_offset = csr->bus_info_len; - for (kv = csr->root_kv, cache = csr->cache_head; kv; cache = cache->next) { + for (kv = csr->root_kv, cache = csr->cache_head; + kv; + cache = cache->next) { if (!cache) { /* Estimate approximate number of additional cache * regions needed (it assumes that the cache holding * the first 1K Config ROM space always exists). */ int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - - (2 * sizeof(u_int32_t))) + 1; + (2 * sizeof(u32))) + 1; /* Add additional cache regions, extras will be * removed later */ for (; est_c; est_c--) { - ret = csr1212_append_new_cache(csr, CSR1212_EXTENDED_ROM_SIZE); + ret = csr1212_append_new_cache(csr, + CSR1212_EXTENDED_ROM_SIZE); if (ret != CSR1212_SUCCESS) return ret; } @@ -1136,7 +977,7 @@ int csr1212_generate_csr_image(struct cs } kv = csr1212_generate_positions(cache, kv, init_offset); agg_size -= cache->len; - init_offset = sizeof(u_int32_t); + init_offset = sizeof(u32); } /* Remove unused, excess cache regions */ @@ -1149,15 +990,14 @@ int csr1212_generate_csr_image(struct cs /* Go through the list backward so that when done, the correct CRC * will be calculated for the Extended ROM areas. */ - for(cache = csr->cache_tail; cache; cache = cache->prev) { + for (cache = csr->cache_tail; cache; cache = cache->prev) { /* Only Extended ROM caches should have this set. */ if (cache->ext_rom) { int leaf_size; /* Make sure the Extended ROM leaf is a multiple of * max_rom in size. */ - if (csr->max_rom < 1) - return CSR1212_EINVAL; + BUG_ON(csr->max_rom < 1); leaf_size = (cache->len + (csr->max_rom - 1)) & ~(csr->max_rom - 1); @@ -1166,7 +1006,7 @@ int csr1212_generate_csr_image(struct cs leaf_size - cache->len); /* Subtract leaf header */ - leaf_size -= sizeof(u_int32_t); + leaf_size -= sizeof(u32); /* Update the Extended ROM leaf length */ cache->ext_rom->value.leaf.len = @@ -1184,33 +1024,31 @@ int csr1212_generate_csr_image(struct cs /* Set the length and CRC of the extended ROM. */ struct csr1212_keyval_img *kvi = (struct csr1212_keyval_img*)cache->data; + u16 len = bytes_to_quads(cache->len) - 1; - kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1); - kvi->crc = csr1212_crc16(kvi->data, - bytes_to_quads(cache->len) - 1); - + kvi->length = cpu_to_be16(len); + kvi->crc = csr1212_crc16(kvi->data, len); } } return CSR1212_SUCCESS; } -int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t len) +int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len) { struct csr1212_csr_rom_cache *cache; - for (cache = csr->cache_head; cache; cache = cache->next) { + for (cache = csr->cache_head; cache; cache = cache->next) if (offset >= cache->offset && (offset + len) <= (cache->offset + cache->size)) { - memcpy(buffer, - &cache->data[bytes_to_quads(offset - cache->offset)], + memcpy(buffer, &cache->data[ + bytes_to_quads(offset - cache->offset)], len); return CSR1212_SUCCESS; } - } - return CSR1212_ENOENT; -} + return -ENOENT; +} /* Parse a chunk of data as a Config ROM */ @@ -1227,46 +1065,43 @@ static int csr1212_parse_bus_info_block( * Unfortunately, many IEEE 1394 devices do not abide by that, so the * bus info block will be read 1 quadlet at a time. The rest of the * ConfigROM will be read according to the max_rom field. */ - for (i = 0; i < csr->bus_info_len; i += sizeof(csr1212_quad_t)) { + for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) { ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - sizeof(csr1212_quad_t), - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); + sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)], + csr->private); if (ret != CSR1212_SUCCESS) return ret; /* check ROM header's info_length */ if (i == 0 && - CSR1212_BE32_TO_CPU(csr->cache_head->data[0]) >> 24 != + be32_to_cpu(csr->cache_head->data[0]) >> 24 != bytes_to_quads(csr->bus_info_len) - 1) - return CSR1212_EINVAL; + return -EINVAL; } bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data; csr->crc_len = quads_to_bytes(bi->crc_length); - /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that is not - * always the case, so read the rest of the crc area 1 quadlet at a time. */ - for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(csr1212_quad_t)) { + /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that + * is not always the case, so read the rest of the crc area 1 quadlet at + * a time. */ + for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) { ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - sizeof(csr1212_quad_t), - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); + sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)], + csr->private); if (ret != CSR1212_SUCCESS) return ret; } -#if 0 - /* Apparently there are too many differnt wrong implementations of the - * CRC algorithm that verifying them is moot. */ + /* Apparently there are many different wrong implementations of the CRC + * algorithm. We don't fail, we just warn. */ if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) && (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc)) - return CSR1212_EINVAL; -#endif + printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n"); cr = CSR1212_MALLOC(sizeof(*cr)); if (!cr) - return CSR1212_ENOMEM; + return -ENOMEM; cr->next = NULL; cr->prev = NULL; @@ -1279,21 +1114,26 @@ #endif return CSR1212_SUCCESS; } -static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, - csr1212_quad_t ki, - u_int32_t kv_pos) +#define CSR1212_KV_KEY(q) (be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT) +#define CSR1212_KV_KEY_TYPE(q) (CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT) +#define CSR1212_KV_KEY_ID(q) (CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK) +#define CSR1212_KV_VAL_MASK 0xffffff +#define CSR1212_KV_VAL(q) (be32_to_cpu(q) & CSR1212_KV_VAL_MASK) + +static int +csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) { int ret = CSR1212_SUCCESS; struct csr1212_keyval *k = NULL; - u_int32_t offset; + u32 offset; - switch(CSR1212_KV_KEY_TYPE(ki)) { + switch (CSR1212_KV_KEY_TYPE(ki)) { case CSR1212_KV_TYPE_IMMEDIATE: k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki), CSR1212_KV_VAL(ki)); if (!k) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } k->refcnt = 0; /* Don't keep local reference when parsing. */ @@ -1303,8 +1143,8 @@ static int csr1212_parse_dir_entry(struc k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), CSR1212_KV_VAL(ki)); if (!k) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } k->refcnt = 0; /* Don't keep local reference when parsing. */ break; @@ -1316,8 +1156,8 @@ static int csr1212_parse_dir_entry(struc /* Uh-oh. Can't have a relative offset of 0 for Leaves * or Directories. The Config ROM image is most likely * messed up, so we'll just abort here. */ - ret = CSR1212_EIO; - goto fail; + ret = -EIO; + goto out; } k = csr1212_find_keyval_offset(dir, offset); @@ -1325,14 +1165,14 @@ static int csr1212_parse_dir_entry(struc if (k) break; /* Found it. */ - if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) { + if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki)); - } else { + else k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0); - } + if (!k) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } k->refcnt = 0; /* Don't keep local reference when parsing. */ k->valid = 0; /* Contents not read yet so it's not valid. */ @@ -1344,16 +1184,12 @@ static int csr1212_parse_dir_entry(struc dir->next = k; } ret = csr1212_attach_keyval_to_directory(dir, k); - -fail: - if (ret != CSR1212_SUCCESS) { - if (k) - free_keyval(k); - } +out: + if (ret != CSR1212_SUCCESS && k != NULL) + free_keyval(k); return ret; } - int csr1212_parse_keyval(struct csr1212_keyval *kv, struct csr1212_csr_rom_cache *cache) { @@ -1362,24 +1198,20 @@ int csr1212_parse_keyval(struct csr1212_ int ret = CSR1212_SUCCESS; int kvi_len; - kvi = (struct csr1212_keyval_img*)&cache->data[bytes_to_quads(kv->offset - - cache->offset)]; - kvi_len = CSR1212_BE16_TO_CPU(kvi->length); + kvi = (struct csr1212_keyval_img*) + &cache->data[bytes_to_quads(kv->offset - cache->offset)]; + kvi_len = be16_to_cpu(kvi->length); -#if 0 - /* Apparently there are too many differnt wrong implementations of the - * CRC algorithm that verifying them is moot. */ + /* Apparently there are many different wrong implementations of the CRC + * algorithm. We don't fail, we just warn. */ if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) && - (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) { - ret = CSR1212_EINVAL; - goto fail; - } -#endif + (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) + printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n"); - switch(kv->key.type) { + switch (kv->key.type) { case CSR1212_KV_TYPE_DIRECTORY: for (i = 0; i < kvi_len; i++) { - csr1212_quad_t ki = kvi->data[i]; + u32 ki = kvi->data[i]; /* Some devices put null entries in their unit * directories. If we come across such an entry, @@ -1387,76 +1219,72 @@ #endif if (ki == 0x0) continue; ret = csr1212_parse_dir_entry(kv, ki, - (kv->offset + - quads_to_bytes(i + 1))); + kv->offset + quads_to_bytes(i + 1)); } kv->value.directory.len = kvi_len; break; case CSR1212_KV_TYPE_LEAF: if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { - kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len)); + size_t size = quads_to_bytes(kvi_len); + + kv->value.leaf.data = CSR1212_MALLOC(size); if (!kv->value.leaf.data) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } kv->value.leaf.len = kvi_len; - memcpy(kv->value.leaf.data, kvi->data, quads_to_bytes(kvi_len)); + memcpy(kv->value.leaf.data, kvi->data, size); } break; } kv->valid = 1; - -fail: +out: return ret; } - -int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) +static int +csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) { struct csr1212_cache_region *cr, *ncr, *newcr = NULL; struct csr1212_keyval_img *kvi = NULL; struct csr1212_csr_rom_cache *cache; int cache_index; - u_int64_t addr; - u_int32_t *cache_ptr; - u_int16_t kv_len = 0; + u64 addr; + u32 *cache_ptr; + u16 kv_len = 0; - if (!csr || !kv || csr->max_rom < 1) - return CSR1212_EINVAL; + BUG_ON(!csr || !kv || csr->max_rom < 1); /* First find which cache the data should be in (or go in if not read * yet). */ - for (cache = csr->cache_head; cache; cache = cache->next) { + for (cache = csr->cache_head; cache; cache = cache->next) if (kv->offset >= cache->offset && kv->offset < (cache->offset + cache->size)) break; - } if (!cache) { - csr1212_quad_t q; - u_int32_t cache_size; + u32 q, cache_size; /* Only create a new cache for Extended ROM leaves. */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - return CSR1212_EINVAL; + return -EINVAL; if (csr->ops->bus_read(csr, CSR1212_REGISTER_SPACE_BASE + kv->offset, - sizeof(csr1212_quad_t), &q, csr->private)) { - return CSR1212_EIO; - } + sizeof(u32), &q, csr->private)) + return -EIO; - kv->value.leaf.len = CSR1212_BE32_TO_CPU(q) >> 16; + kv->value.leaf.len = be32_to_cpu(q) >> 16; cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + (csr->max_rom - 1)) & ~(csr->max_rom - 1); cache = csr1212_rom_cache_malloc(kv->offset, cache_size); if (!cache) - return CSR1212_ENOMEM; + return -ENOMEM; kv->value.leaf.data = &cache->data[1]; csr->cache_tail->next = cache; @@ -1465,12 +1293,11 @@ int _csr1212_read_keyval(struct csr1212_ csr->cache_tail = cache; cache->filled_head = CSR1212_MALLOC(sizeof(*cache->filled_head)); - if (!cache->filled_head) { - return CSR1212_ENOMEM; - } + if (!cache->filled_head) + return -ENOMEM; cache->filled_head->offset_start = 0; - cache->filled_head->offset_end = sizeof(csr1212_quad_t); + cache->filled_head->offset_end = sizeof(u32); cache->filled_tail = cache->filled_head; cache->filled_head->next = NULL; cache->filled_head->prev = NULL; @@ -1488,7 +1315,7 @@ int _csr1212_read_keyval(struct csr1212_ if (cache_index < cr->offset_start) { newcr = CSR1212_MALLOC(sizeof(*newcr)); if (!newcr) - return CSR1212_ENOMEM; + return -ENOMEM; newcr->offset_start = cache_index & ~(csr->max_rom - 1); newcr->offset_end = newcr->offset_start; @@ -1501,18 +1328,18 @@ int _csr1212_read_keyval(struct csr1212_ (cache_index < cr->offset_end)) { kvi = (struct csr1212_keyval_img*) (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + - 1); + kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); break; - } else if (cache_index == cr->offset_end) + } else if (cache_index == cr->offset_end) { break; + } } if (!cr) { cr = cache->filled_tail; newcr = CSR1212_MALLOC(sizeof(*newcr)); if (!newcr) - return CSR1212_ENOMEM; + return -ENOMEM; newcr->offset_start = cache_index & ~(csr->max_rom - 1); newcr->offset_end = newcr->offset_start; @@ -1534,7 +1361,7 @@ int _csr1212_read_keyval(struct csr1212_ csr->private)) { if (csr->max_rom == 4) /* We've got problems! */ - return CSR1212_EIO; + return -EIO; /* Apperently the max_rom value was a lie, set it to * do quadlet reads and try again. */ @@ -1548,8 +1375,7 @@ int _csr1212_read_keyval(struct csr1212_ if (!kvi && (cr->offset_end > cache_index)) { kvi = (struct csr1212_keyval_img*) (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + - 1); + kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); } if ((kv_len + (kv->offset - cache->offset)) > cache->size) { @@ -1557,7 +1383,7 @@ int _csr1212_read_keyval(struct csr1212_ * beyond the ConfigROM image region and thus beyond the * end of our cache region. Therefore, we abort now * rather than seg faulting later. */ - return CSR1212_EIO; + return -EIO; } ncr = cr->next; @@ -1579,7 +1405,16 @@ int _csr1212_read_keyval(struct csr1212_ return csr1212_parse_keyval(kv, cache); } - +struct csr1212_keyval * +csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) +{ + if (!kv) + return NULL; + if (!kv->valid) + if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) + return NULL; + return kv; +} int csr1212_parse_csr(struct csr1212_csr *csr) { @@ -1587,20 +1422,19 @@ int csr1212_parse_csr(struct csr1212_csr struct csr1212_dentry *dentry; int ret; - if (!csr || !csr->ops || !csr->ops->bus_read) - return CSR1212_EINVAL; + BUG_ON(!csr || !csr->ops || !csr->ops->bus_read); ret = csr1212_parse_bus_info_block(csr); if (ret != CSR1212_SUCCESS) return ret; - if (!csr->ops->get_max_rom) + if (!csr->ops->get_max_rom) { csr->max_rom = mr_map[0]; /* default value */ - else { + } else { int i = csr->ops->get_max_rom(csr->bus_info_data, csr->private); if (i & ~0x3) - return CSR1212_EINVAL; + return -EINVAL; csr->max_rom = mr_map[i]; } @@ -1613,7 +1447,7 @@ int csr1212_parse_csr(struct csr1212_csr csr->root_kv->valid = 0; csr->root_kv->next = csr->root_kv; csr->root_kv->prev = csr->root_kv; - ret = _csr1212_read_keyval(csr, csr->root_kv); + ret = csr1212_read_keyval(csr, csr->root_kv); if (ret != CSR1212_SUCCESS) return ret; @@ -1623,7 +1457,7 @@ int csr1212_parse_csr(struct csr1212_csr dentry; dentry = dentry->next) { if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM && !dentry->kv->valid) { - ret = _csr1212_read_keyval(csr, dentry->kv); + ret = csr1212_read_keyval(csr, dentry->kv); if (ret != CSR1212_SUCCESS) return ret; } diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h index 17ddd72..df909ce 100644 --- a/drivers/ieee1394/csr1212.h +++ b/drivers/ieee1394/csr1212.h @@ -30,94 +30,13 @@ #ifndef __CSR1212_H__ #define __CSR1212_H__ - -/* Compatibility layer */ -#ifdef __KERNEL__ - #include #include -#include -#include -#include - -#define CSR1212_MALLOC(size) vmalloc((size)) -#define CSR1212_FREE(ptr) vfree(ptr) -#define CSR1212_BE16_TO_CPU(quad) be16_to_cpu(quad) -#define CSR1212_CPU_TO_BE16(quad) cpu_to_be16(quad) -#define CSR1212_BE32_TO_CPU(quad) be32_to_cpu(quad) -#define CSR1212_CPU_TO_BE32(quad) cpu_to_be32(quad) -#define CSR1212_BE64_TO_CPU(quad) be64_to_cpu(quad) -#define CSR1212_CPU_TO_BE64(quad) cpu_to_be64(quad) - -#define CSR1212_LE16_TO_CPU(quad) le16_to_cpu(quad) -#define CSR1212_CPU_TO_LE16(quad) cpu_to_le16(quad) -#define CSR1212_LE32_TO_CPU(quad) le32_to_cpu(quad) -#define CSR1212_CPU_TO_LE32(quad) cpu_to_le32(quad) -#define CSR1212_LE64_TO_CPU(quad) le64_to_cpu(quad) -#define CSR1212_CPU_TO_LE64(quad) cpu_to_le64(quad) - -#include -#define CSR1212_SUCCESS (0) -#define CSR1212_EINVAL (-EINVAL) -#define CSR1212_ENOMEM (-ENOMEM) -#define CSR1212_ENOENT (-ENOENT) -#define CSR1212_EIO (-EIO) -#define CSR1212_EBUSY (-EBUSY) - -#else /* Userspace */ - -#include -#include -#define CSR1212_MALLOC(size) malloc(size) -#define CSR1212_FREE(ptr) free(ptr) -#include -#if __BYTE_ORDER == __LITTLE_ENDIAN -#include -#define CSR1212_BE16_TO_CPU(quad) bswap_16(quad) -#define CSR1212_CPU_TO_BE16(quad) bswap_16(quad) -#define CSR1212_BE32_TO_CPU(quad) bswap_32(quad) -#define CSR1212_CPU_TO_BE32(quad) bswap_32(quad) -#define CSR1212_BE64_TO_CPU(quad) bswap_64(quad) -#define CSR1212_CPU_TO_BE64(quad) bswap_64(quad) - -#define CSR1212_LE16_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_LE16(quad) (quad) -#define CSR1212_LE32_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_LE32(quad) (quad) -#define CSR1212_LE64_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_LE64(quad) (quad) -#else -#define CSR1212_BE16_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_BE16(quad) (quad) -#define CSR1212_BE32_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_BE32(quad) (quad) -#define CSR1212_BE64_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_BE64(quad) (quad) - -#define CSR1212_LE16_TO_CPU(quad) bswap_16(quad) -#define CSR1212_CPU_TO_LE16(quad) bswap_16(quad) -#define CSR1212_LE32_TO_CPU(quad) bswap_32(quad) -#define CSR1212_CPU_TO_LE32(quad) bswap_32(quad) -#define CSR1212_LE64_TO_CPU(quad) bswap_64(quad) -#define CSR1212_CPU_TO_LE64(quad) bswap_64(quad) -#endif - -#include -#define CSR1212_SUCCESS (0) -#define CSR1212_EINVAL (EINVAL) -#define CSR1212_ENOMEM (ENOMEM) -#define CSR1212_ENOENT (ENOENT) -#define CSR1212_EIO (EIO) -#define CSR1212_EBUSY (EBUSY) - -#endif +#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL) +#define CSR1212_FREE(ptr) kfree(ptr) -#define CSR1212_KV_VAL_MASK 0xffffff -#define CSR1212_KV_KEY_SHIFT 24 -#define CSR1212_KV_KEY_TYPE_SHIFT 6 -#define CSR1212_KV_KEY_ID_MASK 0x3f -#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* After shift */ +#define CSR1212_SUCCESS (0) /* CSR 1212 key types */ @@ -190,48 +109,22 @@ #define CSR1212_UNITS_SPACE_SIZE ((256 #define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE) #define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) -#define CSR1212_EXTENDED_ROM_SIZE (0x10000 * sizeof(u_int32_t)) - #define CSR1212_INVALID_ADDR_SPACE -1 + /* Config ROM image structures */ struct csr1212_bus_info_block_img { - u_int8_t length; - u_int8_t crc_length; - u_int16_t crc; + u8 length; + u8 crc_length; + u16 crc; /* Must be last */ - u_int32_t data[0]; /* older gcc can't handle [] which is standard */ -}; - -#define CSR1212_KV_KEY(quad) (CSR1212_BE32_TO_CPU(quad) >> CSR1212_KV_KEY_SHIFT) -#define CSR1212_KV_KEY_TYPE(quad) (CSR1212_KV_KEY(quad) >> CSR1212_KV_KEY_TYPE_SHIFT) -#define CSR1212_KV_KEY_ID(quad) (CSR1212_KV_KEY(quad) & CSR1212_KV_KEY_ID_MASK) -#define CSR1212_KV_VAL(quad) (CSR1212_BE32_TO_CPU(quad) & CSR1212_KV_VAL_MASK) - -#define CSR1212_SET_KV_KEY(quad, key) ((quad) = \ - CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | ((key) << CSR1212_KV_KEY_SHIFT))) -#define CSR1212_SET_KV_VAL(quad, val) ((quad) = \ - CSR1212_CPU_TO_BE32((CSR1212_KV_KEY(quad) << CSR1212_KV_KEY_SHIFT) | (val))) -#define CSR1212_SET_KV_TYPEID(quad, type, id) ((quad) = \ - CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | \ - (((((type) & CSR1212_KV_KEY_TYPE_MASK) << CSR1212_KV_KEY_TYPE_SHIFT) | \ - ((id) & CSR1212_KV_KEY_ID_MASK)) << CSR1212_KV_KEY_SHIFT))) - -typedef u_int32_t csr1212_quad_t; - - -struct csr1212_keyval_img { - u_int16_t length; - u_int16_t crc; - - /* Must be last */ - csr1212_quad_t data[0]; /* older gcc can't handle [] which is standard */ + u32 data[0]; /* older gcc can't handle [] which is standard */ }; struct csr1212_leaf { int len; - u_int32_t *data; + u32 *data; }; struct csr1212_dentry { @@ -246,12 +139,12 @@ struct csr1212_directory { struct csr1212_keyval { struct { - u_int8_t type; - u_int8_t id; + u8 type; + u8 id; } key; union { - u_int32_t immediate; - u_int32_t csr_offset; + u32 immediate; + u32 csr_offset; struct csr1212_leaf leaf; struct csr1212_directory directory; } value; @@ -260,15 +153,15 @@ struct csr1212_keyval { /* used in generating and/or parsing CSR image */ struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ - u_int32_t offset; /* position in CSR from 0xffff f000 0000 */ - u_int8_t valid; /* flag indicating keyval has valid data*/ + u32 offset; /* position in CSR from 0xffff f000 0000 */ + u8 valid; /* flag indicating keyval has valid data*/ }; struct csr1212_cache_region { struct csr1212_cache_region *next, *prev; - u_int32_t offset_start; /* inclusive */ - u_int32_t offset_end; /* exclusive */ + u32 offset_start; /* inclusive */ + u32 offset_end; /* exclusive */ }; struct csr1212_csr_rom_cache { @@ -276,18 +169,18 @@ struct csr1212_csr_rom_cache { struct csr1212_cache_region *filled_head, *filled_tail; struct csr1212_keyval *layout_head, *layout_tail; size_t size; - u_int32_t offset; + u32 offset; struct csr1212_keyval *ext_rom; size_t len; /* Must be last */ - u_int32_t data[0]; /* older gcc can't handle [] which is standard */ + u32 data[0]; /* older gcc can't handle [] which is standard */ }; struct csr1212_csr { size_t bus_info_len; /* bus info block length in bytes */ size_t crc_len; /* crc length in bytes */ - u_int32_t *bus_info_data; /* bus info data incl bus name and EUI */ + u32 *bus_info_data; /* bus info data incl bus name and EUI */ void *private; /* private, bus specific data */ struct csr1212_bus_ops *ops; @@ -305,52 +198,38 @@ struct csr1212_bus_ops { * from remote nodes when parsing a Config ROM (i.e., read Config ROM * entries located in the Units Space. Must return 0 on success * anything else indicates an error. */ - int (*bus_read) (struct csr1212_csr *csr, u_int64_t addr, - u_int16_t length, void *buffer, void *private); + int (*bus_read) (struct csr1212_csr *csr, u64 addr, + u16 length, void *buffer, void *private); /* This function is used by csr1212 to allocate a region in units space * in the event that Config ROM entries don't all fit in the predefined * 1K region. The void *private parameter is private member of struct * csr1212_csr. */ - u_int64_t (*allocate_addr_range) (u_int64_t size, u_int32_t alignment, - void *private); - + u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private); /* This function is used by csr1212 to release a region in units space * that is no longer needed. */ - void (*release_addr) (u_int64_t addr, void *private); + void (*release_addr) (u64 addr, void *private); /* This function is used by csr1212 to determine the max read request * supported by a remote node when reading the ConfigROM space. Must * return 0, 1, or 2 per IEEE 1212. */ - int (*get_max_rom) (u_int32_t *bus_info, void *private); + int (*get_max_rom) (u32 *bus_info, void *private); }; - - /* Descriptor Leaf manipulation macros */ #define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24 #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff -#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) +#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) #define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) >> CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) + (be32_to_cpu((kv)->value.leaf.data[0]) >> \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) & \ + (be32_to_cpu((kv)->value.leaf.data[0]) & \ CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) -#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ - (&((kv)->value.leaf.data[1])) - -#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ - ((kv)->value.leaf.data[0] = \ - CSR1212_CPU_TO_BE32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ - ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) -#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ - ((kv)->value.leaf.data[0] = \ - CSR1212_CPU_TO_BE32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ - CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ - ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) + /* Text Descriptor Leaf manipulation macros */ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28 @@ -358,182 +237,21 @@ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ + (be32_to_cpu((kv)->value.leaf.data[1]) >> \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ - ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) + ((be32_to_cpu((kv)->value.leaf.data[1]) >> \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) & \ + (be32_to_cpu((kv)->value.leaf.data[1]) & \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \ (&((kv)->value.leaf.data[2])) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \ - ((kv)->value.leaf.data[1] = \ - ((kv)->value.leaf.data[1] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((width) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \ - ((kv)->value.leaf.data[1] = \ - ((kv)->value.leaf.data[1] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((char_set) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT)) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ - ((kv)->value.leaf.data[1] = \ - ((kv)->value.leaf.data[1] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ - CSR1212_CPU_TO_BE32(((language) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) - - -/* Icon Descriptor Leaf manipulation macros */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK 0xffffff -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT 30 -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK 0x3 /* after shift */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT 16 -#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK 0xf /* after shift */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff -#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT 16 -#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK 0xffff /* after shift */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK 0xffff -#define CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD (3 * sizeof(u_int32_t)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[2]) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv) \ - ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN(kv) \ - ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) >> \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_SHIFT) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv) \ - (&((kv)->value.leaf.data[5])) - -static inline u_int32_t *CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(struct csr1212_keyval *kv) -{ - static const int pd[4] = { 0, 4, 16, 256 }; - static const int cs[16] = { 4, 2 }; - int ps = pd[CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv)]; - - return &kv->value.leaf.data[5 + - (ps * cs[CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv)]) / - sizeof(u_int32_t)]; -} - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version) \ - ((kv)->value.leaf.data[2] = \ - ((kv)->value.leaf.data[2] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) | \ - CSR1212_CPU_TO_BE32(((version) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth) \ - ((kv)->value.leaf.data[3] = \ - ((kv)->value.leaf.data[3] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK << \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((palette_depth) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK) << \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space) \ - ((kv)->value.leaf.data[3] = \ - ((kv)->value.leaf.data[3] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK << \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((color_space) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) << \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ - ((kv)->value.leaf.data[3] = \ - ((kv)->value.leaf.data[3] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ - CSR1212_CPU_TO_BE32(((language) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan) \ - ((kv)->value.leaf.data[4] = \ - ((kv)->value.leaf.data[4] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK << \ - CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((hscan) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK) << \ - CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan) \ - ((kv)->value.leaf.data[4] = \ - (((kv)->value.leaf.data[4] & \ - CSR1212_CPU_TO_BE32(~CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) | \ - CSR1212_CPU_TO_BE32(((vscan) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) - - -/* Modifiable Descriptor Leaf manipulation macros */ -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT 16 -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK 0xffff -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_SHIFT 32 -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK 0xffff -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK 0xffffffffULL - -#define CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE(kv) \ - CSR1212_BE16_TO_CPU((kv)->value.leaf.data[0] >> CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE_SHIFT) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_ADDRESS(kv) \ - (CSR1212_BE16_TO_CPU(((u_int64_t)((kv)->value.leaf.data[0])) << \ - CSR1212_MODIFIABLE_DESCRIPTOR_ADDR_HI_SHIFT) | \ - CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1])) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, size) \ - ((kv)->value.leaf.data[0] = \ - ((kv)->value.leaf.data[0] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK << \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((size) & \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK) << \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT)) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, addr) \ - ((kv)->value.leaf.data[0] = \ - ((kv)->value.leaf.data[0] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) | \ - CSR1212_CPU_TO_BE32(((addr) & \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, addr) \ - ((kv)->value.leaf.data[1] = \ - CSR1212_CPU_TO_BE32(addr & CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK)) - - /* The following 2 function are for creating new Configuration ROM trees. The * first function is used for both creating local trees and parsing remote @@ -543,11 +261,10 @@ extern struct csr1212_csr *csr1212_creat size_t bus_info_size, void *private); extern void csr1212_init_local_csr(struct csr1212_csr *csr, - const u_int32_t *bus_info_data, int max_rom); + const u32 *bus_info_data, int max_rom); -/* The following function destroys a Configuration ROM tree and release all - * memory taken by the tree. */ +/* Destroy a Configuration ROM tree and release all memory taken by the tree. */ extern void csr1212_destroy_csr(struct csr1212_csr *csr); @@ -555,50 +272,20 @@ extern void csr1212_destroy_csr(struct c * a Configuration ROM tree. Code that creates new keyvals with these functions * must release those keyvals with csr1212_release_keyval() when they are no * longer needed. */ -extern struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value); -extern struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, - size_t data_len); -extern struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, - u_int32_t csr_offset); -extern struct csr1212_keyval *csr1212_new_directory(u_int8_t key); -extern struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, - u_int32_t key, - u_int32_t value); -extern struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, - u_int32_t key, - const void *data, - size_t data_len); -extern struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, - u_int32_t specifier_id, - const void *data, - size_t data_len); -extern struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, - u_int16_t cset, - u_int16_t language, - const void *data, - size_t data_len); +extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value); +extern struct csr1212_keyval *csr1212_new_directory(u8 key); extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); -extern struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, - u_int8_t palette_depth, - u_int8_t color_space, - u_int16_t language, - u_int16_t hscan, - u_int16_t vscan, - u_int32_t *palette, - u_int32_t *pixels); -extern struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, - u_int64_t address); -extern struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, - const char *strv[]); - - -/* The following functions manage association between keyvals. Typically, + + +/* The following function manages association between keyvals. Typically, * Descriptor Leaves and Directories will be associated with another keyval and * it is desirable for the Descriptor keyval to be place immediately after the - * keyval that it is associated with.*/ -extern int csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate); -extern void csr1212_disassociate_keyval(struct csr1212_keyval *kv); + * keyval that it is associated with. + * Take care with subsequent ROM modifications: There is no function to remove + * previously specified associations. + */ +extern void csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate); /* The following functions manage the association of a keyval and directories. @@ -609,23 +296,15 @@ extern void csr1212_detach_keyval_from_d struct csr1212_keyval *kv); -/* The following functions create a Configuration ROM image from the tree of - * keyvals provided. csr1212_generate_csr_image() creates a complete image in - * the list of caches available via csr->cache_head. The other functions are - * provided should there be a need to create a flat image without restrictions - * placed by IEEE 1212. */ -extern struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, - struct csr1212_keyval *start_kv, - int start_pos); -extern size_t csr1212_generate_layout_order(struct csr1212_keyval *kv); -extern void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache); +/* Creates a complete Configuration ROM image in the list of caches available + * via csr->cache_head. */ extern int csr1212_generate_csr_image(struct csr1212_csr *csr); /* This is a convience function for reading a block of data out of one of the * caches in the csr->cache_head list. */ -extern int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, - u_int32_t len); +extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, + u32 len); /* The following functions are in place for parsing Configuration ROM images. @@ -635,15 +314,11 @@ extern int csr1212_parse_keyval(struct c struct csr1212_csr_rom_cache *cache); extern int csr1212_parse_csr(struct csr1212_csr *csr); -/* These are internal functions referenced by inline functions below. */ -extern int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); -extern void _csr1212_destroy_keyval(struct csr1212_keyval *kv); - /* This function allocates a new cache which may be used for either parsing or * generating sub-sets of Configuration ROM images. */ -static inline struct csr1212_csr_rom_cache *csr1212_rom_cache_malloc(u_int32_t offset, - size_t size) +static inline struct csr1212_csr_rom_cache * +csr1212_rom_cache_malloc(u32 offset, size_t size) { struct csr1212_csr_rom_cache *cache; @@ -667,16 +342,8 @@ static inline struct csr1212_csr_rom_cac /* This function ensures that a keyval contains data when referencing a keyval * created by parsing a Configuration ROM. */ -static inline struct csr1212_keyval *csr1212_get_keyval(struct csr1212_csr *csr, - struct csr1212_keyval *kv) -{ - if (!kv) - return NULL; - if (!kv->valid) - if (_csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) - return NULL; - return kv; -} +extern struct csr1212_keyval * +csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); /* This function increments the reference count for a keyval should there be a @@ -691,37 +358,29 @@ static inline void csr1212_keep_keyval(s * keyval when there are no more users of the keyval. This should be called by * any code that calls csr1212_keep_keyval() or any of the keyval creation * routines csr1212_new_*(). */ -static inline void csr1212_release_keyval(struct csr1212_keyval *kv) -{ - if (kv->refcnt > 1) - kv->refcnt--; - else - _csr1212_destroy_keyval(kv); -} +extern void csr1212_release_keyval(struct csr1212_keyval *kv); /* * This macro allows for looping over the keyval entries in a directory and it * ensures that keyvals from remote ConfigROMs are parsed properly. * - * _csr is a struct csr1212_csr * that points to CSR associated with dir. - * _kv is a struct csr1212_keyval * that'll point to the current keyval (loop index). - * _dir is a struct csr1212_keyval * that points to the directory to be looped. - * _pos is a struct csr1212_dentry * that is used internally for indexing. + * struct csr1212_csr *_csr points to the CSR associated with dir. + * struct csr1212_keyval *_kv points to the current keyval (loop index). + * struct csr1212_keyval *_dir points to the directory to be looped. + * struct csr1212_dentry *_pos is used internally for indexing. * * kv will be NULL upon exit of the loop. */ -#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ - for (csr1212_get_keyval((_csr), (_dir)), \ - _pos = (_dir)->value.directory.dentries_head, \ - _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL; \ - (_kv) && (_pos); \ - (_kv->associate == NULL) ? \ - ((_pos = _pos->next), \ - (_kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : \ - NULL)) : \ +#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ + for (csr1212_get_keyval((_csr), (_dir)), \ + _pos = (_dir)->value.directory.dentries_head, \ + _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\ + (_kv) && (_pos); \ + (_kv->associate == NULL) ? \ + ((_pos = _pos->next), (_kv = (_pos) ? \ + csr1212_get_keyval((_csr), _pos->kv) : \ + NULL)) : \ (_kv = csr1212_get_keyval((_csr), _kv->associate))) - - #endif /* __CSR1212_H__ */ diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c index c68f328..45d6055 100644 --- a/drivers/ieee1394/dma.c +++ b/drivers/ieee1394/dma.c @@ -62,6 +62,9 @@ void dma_prog_region_free(struct dma_pro /* dma_region */ +/** + * dma_region_init - clear out all fields but do not allocate anything + */ void dma_region_init(struct dma_region *dma) { dma->kvirt = NULL; @@ -71,6 +74,9 @@ void dma_region_init(struct dma_region * dma->sglist = NULL; } +/** + * dma_region_alloc - allocate the buffer and map it to the IOMMU + */ int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction) { @@ -128,6 +134,9 @@ int dma_region_alloc(struct dma_region * return -ENOMEM; } +/** + * dma_region_free - unmap and free the buffer + */ void dma_region_free(struct dma_region *dma) { if (dma->n_dma_pages) { @@ -167,6 +176,12 @@ static inline int dma_region_find(struct return i; } +/** + * dma_region_offset_to_bus - get bus address of an offset within a DMA region + * + * Returns the DMA bus address of the byte with the given @offset relative to + * the beginning of the @dma. + */ dma_addr_t dma_region_offset_to_bus(struct dma_region * dma, unsigned long offset) { @@ -177,6 +192,9 @@ dma_addr_t dma_region_offset_to_bus(stru return sg_dma_address(sg) + rem; } +/** + * dma_region_sync_for_cpu - sync the CPU's view of the buffer + */ void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len) { @@ -193,6 +211,9 @@ void dma_region_sync_for_cpu(struct dma_ dma->direction); } +/** + * dma_region_sync_for_device - sync the IO bus' view of the buffer + */ void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len) { @@ -244,6 +265,9 @@ static struct vm_operations_struct dma_r .nopage = dma_region_pagefault, }; +/** + * dma_region_mmap - map the buffer into a user space process + */ int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma) { diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h index a1682ab..2727bcd 100644 --- a/drivers/ieee1394/dma.h +++ b/drivers/ieee1394/dma.h @@ -66,35 +66,23 @@ struct dma_region { int direction; }; -/* clear out all fields but do not allocate anything */ void dma_region_init(struct dma_region *dma); - -/* allocate the buffer and map it to the IOMMU */ int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction); - -/* unmap and free the buffer */ void dma_region_free(struct dma_region *dma); - -/* sync the CPU's view of the buffer */ void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len); - -/* sync the IO bus' view of the buffer */ void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len); - -/* map the buffer into a user space process */ int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma); +dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, + unsigned long offset); -/* macro to index into a DMA region (or dma_prog_region) */ +/** + * dma_region_i - macro to index into a DMA region (or dma_prog_region) + */ #define dma_region_i(_dma, _type, _index) \ ( ((_type*) ((_dma)->kvirt)) + (_index) ) -/* return the DMA bus address of the byte with the given offset - * relative to the beginning of the dma_region */ -dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, - unsigned long offset); - #endif /* IEEE1394_DMA_H */ diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c index 03e44b3..2296d43 100644 --- a/drivers/ieee1394/eth1394.c +++ b/drivers/ieee1394/eth1394.c @@ -1,5 +1,5 @@ /* - * eth1394.c -- Ethernet driver for Linux IEEE-1394 Subsystem + * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem * * Copyright (C) 2001-2003 Ben Collins * 2000 Bonin Franck @@ -22,10 +22,9 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* This driver intends to support RFC 2734, which describes a method for - * transporting IPv4 datagrams over IEEE-1394 serial busses. This driver - * will ultimately support that method, but currently falls short in - * several areas. +/* + * This driver intends to support RFC 2734, which describes a method for + * transporting IPv4 datagrams over IEEE-1394 serial busses. * * TODO: * RFC 2734 related: @@ -40,7 +39,6 @@ * - Consider garbage collecting old partial datagrams after X amount of time */ - #include #include @@ -52,7 +50,6 @@ #include #include #include -#include #include #include #include @@ -84,10 +81,6 @@ #define ETH1394_PRINT_G(level, fmt, args #define ETH1394_PRINT(level, dev_name, fmt, args...) \ printk(level "%s: %s: " fmt, driver_name, dev_name, ## args) -#define DEBUG(fmt, args...) \ - printk(KERN_ERR "%s:%s[%d]: " fmt "\n", driver_name, __FUNCTION__, __LINE__, ## args) -#define TRACE() printk(KERN_ERR "%s:%s[%d] ---- TRACE\n", driver_name, __FUNCTION__, __LINE__) - struct fragment_info { struct list_head list; int offset; @@ -105,9 +98,9 @@ struct partial_datagram { }; struct pdg_list { - struct list_head list; /* partial datagram list per node */ - unsigned int sz; /* partial datagram list size per node */ - spinlock_t lock; /* partial datagram lock */ + struct list_head list; /* partial datagram list per node */ + unsigned int sz; /* partial datagram list size per node */ + spinlock_t lock; /* partial datagram lock */ }; struct eth1394_host_info { @@ -121,16 +114,14 @@ struct eth1394_node_ref { }; struct eth1394_node_info { - u16 maxpayload; /* Max payload */ - u8 sspd; /* Max speed */ - u64 fifo; /* FIFO address */ - struct pdg_list pdg; /* partial RX datagram lists */ - int dgl; /* Outgoing datagram label */ + u16 maxpayload; /* max payload */ + u8 sspd; /* max speed */ + u64 fifo; /* FIFO address */ + struct pdg_list pdg; /* partial RX datagram lists */ + int dgl; /* outgoing datagram label */ }; -/* Our ieee1394 highlevel driver */ -#define ETH1394_DRIVER_NAME "eth1394" -static const char driver_name[] = ETH1394_DRIVER_NAME; +static const char driver_name[] = "eth1394"; static struct kmem_cache *packet_task_cache; @@ -138,18 +129,12 @@ static struct hpsb_highlevel eth1394_hig /* Use common.lf to determine header len */ static const int hdr_type_len[] = { - sizeof (struct eth1394_uf_hdr), - sizeof (struct eth1394_ff_hdr), - sizeof (struct eth1394_sf_hdr), - sizeof (struct eth1394_sf_hdr) + sizeof(struct eth1394_uf_hdr), + sizeof(struct eth1394_ff_hdr), + sizeof(struct eth1394_sf_hdr), + sizeof(struct eth1394_sf_hdr) }; -/* Change this to IEEE1394_SPEED_S100 to make testing easier */ -#define ETH1394_SPEED_DEF IEEE1394_SPEED_MAX - -/* For now, this needs to be 1500, so that XP works with us */ -#define ETH1394_DATA_LEN ETH_DATA_LEN - static const u16 eth1394_speedto_maxpayload[] = { /* S100, S200, S400, S800, S1600, S3200 */ 512, 1024, 2048, 4096, 4096, 4096 @@ -159,7 +144,8 @@ MODULE_AUTHOR("Ben Collins (bcollins@deb MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)"); MODULE_LICENSE("GPL"); -/* The max_partial_datagrams parameter is the maximum number of fragmented +/* + * The max_partial_datagrams parameter is the maximum number of fragmented * datagrams per node that eth1394 will keep in memory. Providing an upper * bound allows us to limit the amount of memory that partial datagrams * consume in the event that some partial datagrams are never completed. @@ -179,10 +165,7 @@ static int ether1394_header_parse(struct static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh); static void ether1394_header_cache_update(struct hh_cache *hh, struct net_device *dev, - unsigned char * haddr); -static int ether1394_mac_addr(struct net_device *dev, void *p); - -static void purge_partial_datagram(struct list_head *old); + unsigned char *haddr); static int ether1394_tx(struct sk_buff *skb, struct net_device *dev); static void ether1394_iso(struct hpsb_iso *iso); @@ -190,9 +173,9 @@ static struct ethtool_ops ethtool_ops; static int ether1394_write(struct hpsb_host *host, int srcid, int destid, quadlet_t *data, u64 addr, size_t len, u16 flags); -static void ether1394_add_host (struct hpsb_host *host); -static void ether1394_remove_host (struct hpsb_host *host); -static void ether1394_host_reset (struct hpsb_host *host); +static void ether1394_add_host(struct hpsb_host *host); +static void ether1394_remove_host(struct hpsb_host *host); +static void ether1394_host_reset(struct hpsb_host *host); /* Function for incoming 1394 packets */ static struct hpsb_address_ops addr_ops = { @@ -207,89 +190,107 @@ static struct hpsb_highlevel eth1394_hig .host_reset = ether1394_host_reset, }; +static int ether1394_recv_init(struct eth1394_priv *priv) +{ + unsigned int iso_buf_size; + + /* FIXME: rawiso limits us to PAGE_SIZE */ + iso_buf_size = min((unsigned int)PAGE_SIZE, + 2 * (1U << (priv->host->csr.max_rec + 1))); + + priv->iso = hpsb_iso_recv_init(priv->host, + ETHER1394_GASP_BUFFERS * iso_buf_size, + ETHER1394_GASP_BUFFERS, + priv->broadcast_channel, + HPSB_ISO_DMA_PACKET_PER_BUFFER, + 1, ether1394_iso); + if (priv->iso == NULL) { + ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n"); + priv->bc_state = ETHER1394_BC_ERROR; + return -EAGAIN; + } + + if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) + priv->bc_state = ETHER1394_BC_STOPPED; + else + priv->bc_state = ETHER1394_BC_RUNNING; + return 0; +} /* This is called after an "ifup" */ -static int ether1394_open (struct net_device *dev) +static int ether1394_open(struct net_device *dev) { struct eth1394_priv *priv = netdev_priv(dev); - int ret = 0; + int ret; - /* Something bad happened, don't even try */ if (priv->bc_state == ETHER1394_BC_ERROR) { - /* we'll try again */ - priv->iso = hpsb_iso_recv_init(priv->host, - ETHER1394_ISO_BUF_SIZE, - ETHER1394_GASP_BUFFERS, - priv->broadcast_channel, - HPSB_ISO_DMA_PACKET_PER_BUFFER, - 1, ether1394_iso); - if (priv->iso == NULL) { - ETH1394_PRINT(KERN_ERR, dev->name, - "Could not allocate isochronous receive " - "context for the broadcast channel\n"); - priv->bc_state = ETHER1394_BC_ERROR; - ret = -EAGAIN; - } else { - if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) - priv->bc_state = ETHER1394_BC_STOPPED; - else - priv->bc_state = ETHER1394_BC_RUNNING; - } + ret = ether1394_recv_init(priv); + if (ret) + return ret; } - - if (ret) - return ret; - - netif_start_queue (dev); + netif_start_queue(dev); return 0; } /* This is called after an "ifdown" */ -static int ether1394_stop (struct net_device *dev) +static int ether1394_stop(struct net_device *dev) { - netif_stop_queue (dev); + netif_stop_queue(dev); return 0; } /* Return statistics to the caller */ -static struct net_device_stats *ether1394_stats (struct net_device *dev) +static struct net_device_stats *ether1394_stats(struct net_device *dev) { return &(((struct eth1394_priv *)netdev_priv(dev))->stats); } -/* What to do if we timeout. I think a host reset is probably in order, so - * that's what we do. Should we increment the stat counters too? */ -static void ether1394_tx_timeout (struct net_device *dev) +/* FIXME: What to do if we timeout? I think a host reset is probably in order, + * so that's what we do. Should we increment the stat counters too? */ +static void ether1394_tx_timeout(struct net_device *dev) { - ETH1394_PRINT (KERN_ERR, dev->name, "Timeout, resetting host %s\n", - ((struct eth1394_priv *)netdev_priv(dev))->host->driver->name); + struct hpsb_host *host = + ((struct eth1394_priv *)netdev_priv(dev))->host; - highlevel_host_reset (((struct eth1394_priv *)netdev_priv(dev))->host); + ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n"); + ether1394_host_reset(host); +} - netif_wake_queue (dev); +static inline int ether1394_max_mtu(struct hpsb_host* host) +{ + return (1 << (host->csr.max_rec + 1)) + - sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD; } static int ether1394_change_mtu(struct net_device *dev, int new_mtu) { - struct eth1394_priv *priv = netdev_priv(dev); + int max_mtu; - if ((new_mtu < 68) || - (new_mtu > min(ETH1394_DATA_LEN, - (int)((1 << (priv->host->csr.max_rec + 1)) - - (sizeof(union eth1394_hdr) + - ETHER1394_GASP_OVERHEAD))))) + if (new_mtu < 68) return -EINVAL; + + max_mtu = ether1394_max_mtu( + ((struct eth1394_priv *)netdev_priv(dev))->host); + if (new_mtu > max_mtu) { + ETH1394_PRINT(KERN_INFO, dev->name, + "Local node constrains MTU to %d\n", max_mtu); + return -ERANGE; + } + dev->mtu = new_mtu; return 0; } static void purge_partial_datagram(struct list_head *old) { - struct partial_datagram *pd = list_entry(old, struct partial_datagram, list); + struct partial_datagram *pd; struct list_head *lh, *n; + struct fragment_info *fi; + + pd = list_entry(old, struct partial_datagram, list); list_for_each_safe(lh, n, &pd->frag_info) { - struct fragment_info *fi = list_entry(lh, struct fragment_info, list); + fi = list_entry(lh, struct fragment_info, list); list_del(lh); kfree(fi); } @@ -330,35 +331,26 @@ static struct eth1394_node_ref *eth1394_ nodeid_t nodeid) { struct eth1394_node_ref *node; - list_for_each_entry(node, inl, list) { + + list_for_each_entry(node, inl, list) if (node->ud->ne->nodeid == nodeid) return node; - } return NULL; } -static int eth1394_probe(struct device *dev) +static int eth1394_new_node(struct eth1394_host_info *hi, + struct unit_directory *ud) { - struct unit_directory *ud; - struct eth1394_host_info *hi; struct eth1394_priv *priv; struct eth1394_node_ref *new_node; struct eth1394_node_info *node_info; - ud = container_of(dev, struct unit_directory, device); - - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - new_node = kmalloc(sizeof(*new_node), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + new_node = kmalloc(sizeof(*new_node), GFP_KERNEL); if (!new_node) return -ENOMEM; - node_info = kmalloc(sizeof(*node_info), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + node_info = kmalloc(sizeof(*node_info), GFP_KERNEL); if (!node_info) { kfree(new_node); return -ENOMEM; @@ -374,10 +366,22 @@ static int eth1394_probe(struct device * priv = netdev_priv(hi->dev); list_add_tail(&new_node->list, &priv->ip_node_list); - return 0; } +static int eth1394_probe(struct device *dev) +{ + struct unit_directory *ud; + struct eth1394_host_info *hi; + + ud = container_of(dev, struct unit_directory, device); + hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); + if (!hi) + return -ENOENT; + + return eth1394_new_node(hi, ud); +} + static int eth1394_remove(struct device *dev) { struct unit_directory *ud; @@ -396,24 +400,23 @@ static int eth1394_remove(struct device priv = netdev_priv(hi->dev); old_node = eth1394_find_node(&priv->ip_node_list, ud); + if (!old_node) + return 0; - if (old_node) { - list_del(&old_node->list); - kfree(old_node); + list_del(&old_node->list); + kfree(old_node); - node_info = (struct eth1394_node_info*)ud->device.driver_data; + node_info = (struct eth1394_node_info*)ud->device.driver_data; - spin_lock_irqsave(&node_info->pdg.lock, flags); - /* The partial datagram list should be empty, but we'll just - * make sure anyway... */ - list_for_each_safe(lh, n, &node_info->pdg.list) { - purge_partial_datagram(lh); - } - spin_unlock_irqrestore(&node_info->pdg.lock, flags); + spin_lock_irqsave(&node_info->pdg.lock, flags); + /* The partial datagram list should be empty, but we'll just + * make sure anyway... */ + list_for_each_safe(lh, n, &node_info->pdg.list) + purge_partial_datagram(lh); + spin_unlock_irqrestore(&node_info->pdg.lock, flags); - kfree(node_info); - ud->device.driver_data = NULL; - } + kfree(node_info); + ud->device.driver_data = NULL; return 0; } @@ -422,44 +425,19 @@ static int eth1394_update(struct unit_di struct eth1394_host_info *hi; struct eth1394_priv *priv; struct eth1394_node_ref *node; - struct eth1394_node_info *node_info; hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); if (!hi) return -ENOENT; priv = netdev_priv(hi->dev); - node = eth1394_find_node(&priv->ip_node_list, ud); + if (node) + return 0; - if (!node) { - node = kmalloc(sizeof(*node), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); - if (!node) - return -ENOMEM; - - node_info = kmalloc(sizeof(*node_info), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); - if (!node_info) { - kfree(node); - return -ENOMEM; - } - - spin_lock_init(&node_info->pdg.lock); - INIT_LIST_HEAD(&node_info->pdg.list); - node_info->pdg.sz = 0; - - ud->device.driver_data = node_info; - node->ud = ud; - - priv = netdev_priv(hi->dev); - list_add_tail(&node->list, &priv->ip_node_list); - } - - return 0; + return eth1394_new_node(hi, ud); } - static struct ieee1394_device_id eth1394_id_table[] = { { .match_flags = (IEEE1394_MATCH_SPECIFIER_ID | @@ -473,7 +451,7 @@ static struct ieee1394_device_id eth1394 MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table); static struct hpsb_protocol_driver eth1394_proto_driver = { - .name = ETH1394_DRIVER_NAME, + .name = driver_name, .id_table = eth1394_id_table, .update = eth1394_update, .driver = { @@ -482,47 +460,50 @@ static struct hpsb_protocol_driver eth13 }, }; - -static void ether1394_reset_priv (struct net_device *dev, int set_mtu) +static void ether1394_reset_priv(struct net_device *dev, int set_mtu) { unsigned long flags; int i; struct eth1394_priv *priv = netdev_priv(dev); struct hpsb_host *host = priv->host; - u64 guid = get_unaligned((u64*)&(host->csr.rom->bus_info_data[3])); - u16 maxpayload = 1 << (host->csr.max_rec + 1); + u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3])); int max_speed = IEEE1394_SPEED_MAX; - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); - memset(priv->ud_list, 0, sizeof(struct node_entry*) * ALL_NODES); + memset(priv->ud_list, 0, sizeof(priv->ud_list)); priv->bc_maxpayload = 512; /* Determine speed limit */ - for (i = 0; i < host->node_count; i++) + /* FIXME: This is broken for nodes with link speed < PHY speed, + * and it is suboptimal for S200B...S800B hardware. + * The result of nodemgr's speed probe should be used somehow. */ + for (i = 0; i < host->node_count; i++) { + /* take care of S100B...S400B PHY ports */ + if (host->speed[i] == SELFID_SPEED_UNKNOWN) { + max_speed = IEEE1394_SPEED_100; + break; + } if (max_speed > host->speed[i]) max_speed = host->speed[i]; + } priv->bc_sspd = max_speed; - /* We'll use our maxpayload as the default mtu */ if (set_mtu) { - dev->mtu = min(ETH1394_DATA_LEN, - (int)(maxpayload - - (sizeof(union eth1394_hdr) + - ETHER1394_GASP_OVERHEAD))); + /* Use the RFC 2734 default 1500 octets or the maximum payload + * as initial MTU */ + dev->mtu = min(1500, ether1394_max_mtu(host)); /* Set our hardware address while we're at it */ memcpy(dev->dev_addr, &guid, sizeof(u64)); memset(dev->broadcast, 0xff, sizeof(u64)); } - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } -/* This function is called right before register_netdev */ -static void ether1394_init_dev (struct net_device *dev) +static void ether1394_init_dev(struct net_device *dev) { - /* Our functions */ dev->open = ether1394_open; dev->stop = ether1394_stop; dev->hard_start_xmit = ether1394_tx; @@ -535,10 +516,9 @@ static void ether1394_init_dev (struct n dev->hard_header_cache = ether1394_header_cache; dev->header_cache_update= ether1394_header_cache_update; dev->hard_header_parse = ether1394_header_parse; - dev->set_mac_address = ether1394_mac_addr; + SET_ETHTOOL_OPS(dev, ðtool_ops); - /* Some constants */ dev->watchdog_timeo = ETHER1394_TIMEOUT; dev->flags = IFF_BROADCAST | IFF_MULTICAST; dev->features = NETIF_F_HIGHDMA; @@ -546,7 +526,8 @@ static void ether1394_init_dev (struct n dev->hard_header_len = ETH1394_HLEN; dev->type = ARPHRD_IEEE1394; - ether1394_reset_priv (dev, 1); + /* FIXME: This value was copied from ether_setup(). Is it too much? */ + dev->tx_queue_len = 1000; } /* @@ -554,34 +535,33 @@ static void ether1394_init_dev (struct n * when the module is installed. This is where we add all of our ethernet * devices. One for each host. */ -static void ether1394_add_host (struct hpsb_host *host) +static void ether1394_add_host(struct hpsb_host *host) { struct eth1394_host_info *hi = NULL; struct net_device *dev = NULL; struct eth1394_priv *priv; u64 fifo_addr; - if (!(host->config_roms & HPSB_CONFIG_ROM_ENTRY_IP1394)) + if (hpsb_config_rom_ip1394_add(host) != 0) { + ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n"); return; + } fifo_addr = hpsb_allocate_and_register_addrspace( ð1394_highlevel, host, &addr_ops, ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN, CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE); - if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) - goto out; - - /* We should really have our own alloc_hpsbdev() function in - * net_init.c instead of calling the one for ethernet then hijacking - * it for ourselves. That way we'd be a real networking device. */ - dev = alloc_etherdev(sizeof (struct eth1394_priv)); + if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) { + ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n"); + hpsb_config_rom_ip1394_remove(host); + return; + } + dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev); if (dev == NULL) { - ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to allocate " - "etherdevice for IEEE 1394 device %s-%d\n", - host->driver->name, host->id); + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); goto out; - } + } SET_MODULE_OWNER(dev); #if 0 @@ -590,31 +570,26 @@ #if 0 #endif priv = netdev_priv(dev); - INIT_LIST_HEAD(&priv->ip_node_list); - spin_lock_init(&priv->lock); priv->host = host; priv->local_fifo = fifo_addr; hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi)); - if (hi == NULL) { - ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to create " - "hostinfo for IEEE 1394 device %s-%d\n", - host->driver->name, host->id); + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); goto out; - } + } - ether1394_init_dev(dev); + ether1394_reset_priv(dev, 1); - if (register_netdev (dev)) { - ETH1394_PRINT (KERN_ERR, dev->name, "Error registering network driver\n"); + if (register_netdev(dev)) { + ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n"); goto out; } - ETH1394_PRINT (KERN_INFO, dev->name, "IEEE-1394 IPv4 over 1394 Ethernet (fw-host%d)\n", - host->id); + ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n", + host->id); hi->host = host; hi->dev = dev; @@ -623,61 +598,37 @@ #endif * be checked when the eth device is opened. */ priv->broadcast_channel = host->csr.broadcast_channel & 0x3f; - priv->iso = hpsb_iso_recv_init(host, - ETHER1394_ISO_BUF_SIZE, - ETHER1394_GASP_BUFFERS, - priv->broadcast_channel, - HPSB_ISO_DMA_PACKET_PER_BUFFER, - 1, ether1394_iso); - if (priv->iso == NULL) { - ETH1394_PRINT(KERN_ERR, dev->name, - "Could not allocate isochronous receive context " - "for the broadcast channel\n"); - priv->bc_state = ETHER1394_BC_ERROR; - } else { - if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) - priv->bc_state = ETHER1394_BC_STOPPED; - else - priv->bc_state = ETHER1394_BC_RUNNING; - } - + ether1394_recv_init(priv); return; - out: - if (dev != NULL) + if (dev) free_netdev(dev); if (hi) hpsb_destroy_hostinfo(ð1394_highlevel, host); - - return; + hpsb_unregister_addrspace(ð1394_highlevel, host, fifo_addr); + hpsb_config_rom_ip1394_remove(host); } /* Remove a card from our list */ -static void ether1394_remove_host (struct hpsb_host *host) +static void ether1394_remove_host(struct hpsb_host *host) { struct eth1394_host_info *hi; + struct eth1394_priv *priv; hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (hi != NULL) { - struct eth1394_priv *priv = netdev_priv(hi->dev); - - hpsb_unregister_addrspace(ð1394_highlevel, host, - priv->local_fifo); - - if (priv->iso != NULL) - hpsb_iso_shutdown(priv->iso); - - if (hi->dev) { - unregister_netdev (hi->dev); - free_netdev(hi->dev); - } - } - - return; + if (!hi) + return; + priv = netdev_priv(hi->dev); + hpsb_unregister_addrspace(ð1394_highlevel, host, priv->local_fifo); + hpsb_config_rom_ip1394_remove(host); + if (priv->iso) + hpsb_iso_shutdown(priv->iso); + unregister_netdev(hi->dev); + free_netdev(hi->dev); } -/* A reset has just arisen */ -static void ether1394_host_reset (struct hpsb_host *host) +/* A bus reset happened */ +static void ether1394_host_reset(struct hpsb_host *host) { struct eth1394_host_info *hi; struct eth1394_priv *priv; @@ -690,24 +641,23 @@ static void ether1394_host_reset (struct hi = hpsb_get_hostinfo(ð1394_highlevel, host); /* This can happen for hosts that we don't use */ - if (hi == NULL) + if (!hi) return; dev = hi->dev; - priv = (struct eth1394_priv *)netdev_priv(dev); + priv = netdev_priv(dev); - /* Reset our private host data, but not our mtu */ - netif_stop_queue (dev); - ether1394_reset_priv (dev, 0); + /* Reset our private host data, but not our MTU */ + netif_stop_queue(dev); + ether1394_reset_priv(dev, 0); list_for_each_entry(node, &priv->ip_node_list, list) { - node_info = (struct eth1394_node_info*)node->ud->device.driver_data; + node_info = node->ud->device.driver_data; spin_lock_irqsave(&node_info->pdg.lock, flags); - list_for_each_safe(lh, n, &node_info->pdg.list) { + list_for_each_safe(lh, n, &node_info->pdg.list) purge_partial_datagram(lh); - } INIT_LIST_HEAD(&(node_info->pdg.list)); node_info->pdg.sz = 0; @@ -715,7 +665,7 @@ static void ether1394_host_reset (struct spin_unlock_irqrestore(&node_info->pdg.lock, flags); } - netif_wake_queue (dev); + netif_wake_queue(dev); } /****************************************** @@ -723,7 +673,6 @@ static void ether1394_host_reset (struct ******************************************/ /* These functions have been adapted from net/ethernet/eth.c */ - /* Create a fake MAC header for an arbitrary protocol layer. * saddr=NULL means use device source address * daddr=NULL means leave destination address (eg unresolved arp). */ @@ -731,25 +680,24 @@ static int ether1394_header(struct sk_bu unsigned short type, void *daddr, void *saddr, unsigned len) { - struct eth1394hdr *eth = (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN); + struct eth1394hdr *eth = + (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN); eth->h_proto = htons(type); - if (dev->flags & (IFF_LOOPBACK|IFF_NOARP)) { + if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { memset(eth->h_dest, 0, dev->addr_len); - return(dev->hard_header_len); + return dev->hard_header_len; } if (daddr) { - memcpy(eth->h_dest,daddr,dev->addr_len); + memcpy(eth->h_dest, daddr, dev->addr_len); return dev->hard_header_len; } return -dev->hard_header_len; - } - /* Rebuild the faked MAC header. This is called after an ARP * (or in future other address resolution) has completed on this * sk_buff. We now let ARP fill in the other fields. @@ -760,38 +708,30 @@ static int ether1394_header(struct sk_bu static int ether1394_rebuild_header(struct sk_buff *skb) { struct eth1394hdr *eth = (struct eth1394hdr *)skb->data; - struct net_device *dev = skb->dev; - switch (eth->h_proto) { - -#ifdef CONFIG_INET - case __constant_htons(ETH_P_IP): - return arp_find((unsigned char*)ð->h_dest, skb); -#endif - default: - ETH1394_PRINT(KERN_DEBUG, dev->name, - "unable to resolve type %04x addresses.\n", - ntohs(eth->h_proto)); - break; - } + if (eth->h_proto == htons(ETH_P_IP)) + return arp_find((unsigned char *)ð->h_dest, skb); + ETH1394_PRINT(KERN_DEBUG, skb->dev->name, + "unable to resolve type %04x addresses\n", + ntohs(eth->h_proto)); return 0; } static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr) { struct net_device *dev = skb->dev; + memcpy(haddr, dev->dev_addr, ETH1394_ALEN); return ETH1394_ALEN; } - static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh) { unsigned short type = hh->hh_type; - struct eth1394hdr *eth = (struct eth1394hdr*)(((u8*)hh->hh_data) + - (16 - ETH1394_HLEN)); struct net_device *dev = neigh->dev; + struct eth1394hdr *eth = + (struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN); if (type == htons(ETH_P_802_3)) return -1; @@ -808,38 +748,25 @@ static void ether1394_header_cache_updat struct net_device *dev, unsigned char * haddr) { - memcpy(((u8*)hh->hh_data) + (16 - ETH1394_HLEN), haddr, dev->addr_len); + memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len); } -static int ether1394_mac_addr(struct net_device *dev, void *p) -{ - if (netif_running(dev)) - return -EBUSY; - - /* Not going to allow setting the MAC address, we really need to use - * the real one supplied by the hardware */ - return -EINVAL; - } - - - /****************************************** * Datagram reception code ******************************************/ /* Copied from net/ethernet/eth.c */ -static inline u16 ether1394_type_trans(struct sk_buff *skb, - struct net_device *dev) +static u16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev) { struct eth1394hdr *eth; unsigned char *rawp; - skb->mac.raw = skb->data; - skb_pull (skb, ETH1394_HLEN); + skb_reset_mac_header(skb); + skb_pull(skb, ETH1394_HLEN); eth = eth1394_hdr(skb); if (*eth->h_dest & 1) { - if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len)==0) + if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0) skb->pkt_type = PACKET_BROADCAST; #if 0 else @@ -848,47 +775,45 @@ #endif } else { if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len)) skb->pkt_type = PACKET_OTHERHOST; - } + } - if (ntohs (eth->h_proto) >= 1536) + if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; rawp = skb->data; - if (*(unsigned short *)rawp == 0xFFFF) - return htons (ETH_P_802_3); + if (*(unsigned short *)rawp == 0xFFFF) + return htons(ETH_P_802_3); - return htons (ETH_P_802_2); + return htons(ETH_P_802_2); } /* Parse an encapsulated IP1394 header into an ethernet frame packet. * We also perform ARP translation here, if need be. */ -static inline u16 ether1394_parse_encap(struct sk_buff *skb, - struct net_device *dev, - nodeid_t srcid, nodeid_t destid, - u16 ether_type) +static u16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev, + nodeid_t srcid, nodeid_t destid, + u16 ether_type) { struct eth1394_priv *priv = netdev_priv(dev); u64 dest_hw; unsigned short ret = 0; - /* Setup our hw addresses. We use these to build the - * ethernet header. */ + /* Setup our hw addresses. We use these to build the ethernet header. */ if (destid == (LOCAL_BUS | ALL_NODES)) dest_hw = ~0ULL; /* broadcast */ else - dest_hw = cpu_to_be64((((u64)priv->host->csr.guid_hi) << 32) | + dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 | priv->host->csr.guid_lo); /* If this is an ARP packet, convert it. First, we want to make * use of some of the fields, since they tell us a little bit * about the sending machine. */ if (ether_type == htons(ETH_P_ARP)) { - struct eth1394_arp *arp1394 = (struct eth1394_arp*)skb->data; + struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; struct arphdr *arp = (struct arphdr *)skb->data; unsigned char *arp_ptr = (unsigned char *)(arp + 1); u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 | - ntohl(arp1394->fifo_lo); + ntohl(arp1394->fifo_lo); u8 max_rec = min(priv->host->csr.max_rec, (u8)(arp1394->max_rec)); int sspd = arp1394->sspd; @@ -902,16 +827,17 @@ static inline u16 ether1394_parse_encap( if (sspd > 5 || sspd < 0) sspd = 0; - maxpayload = min(eth1394_speedto_maxpayload[sspd], (u16)(1 << (max_rec + 1))); + maxpayload = min(eth1394_speedto_maxpayload[sspd], + (u16)(1 << (max_rec + 1))); guid = get_unaligned(&arp1394->s_uniq_id); node = eth1394_find_node_guid(&priv->ip_node_list, be64_to_cpu(guid)); - if (!node) { + if (!node) return 0; - } - node_info = (struct eth1394_node_info*)node->ud->device.driver_data; + node_info = + (struct eth1394_node_info *)node->ud->device.driver_data; /* Update our speed/payload/fifo_offset table */ node_info->maxpayload = maxpayload; @@ -930,7 +856,7 @@ static inline u16 ether1394_parse_encap( arp->ar_hln = 8; arp_ptr += arp->ar_hln; /* skip over sender unique id */ - *(u32*)arp_ptr = arp1394->sip; /* move sender IP addr */ + *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */ arp_ptr += arp->ar_pln; /* skip over sender IP addr */ if (arp->ar_op == htons(ARPOP_REQUEST)) @@ -947,65 +873,65 @@ static inline u16 ether1394_parse_encap( return ret; } -static inline int fragment_overlap(struct list_head *frag_list, int offset, int len) +static int fragment_overlap(struct list_head *frag_list, int offset, int len) { struct fragment_info *fi; + int end = offset + len; - list_for_each_entry(fi, frag_list, list) { - if ( ! ((offset > (fi->offset + fi->len - 1)) || - ((offset + len - 1) < fi->offset))) + list_for_each_entry(fi, frag_list, list) + if (offset < fi->offset + fi->len && end > fi->offset) return 1; - } + return 0; } -static inline struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl) +static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl) { struct partial_datagram *pd; - list_for_each_entry(pd, pdgl, list) { + list_for_each_entry(pd, pdgl, list) if (pd->dgl == dgl) return &pd->list; - } + return NULL; } /* Assumes that new fragment does not overlap any existing fragments */ -static inline int new_fragment(struct list_head *frag_info, int offset, int len) +static int new_fragment(struct list_head *frag_info, int offset, int len) { struct list_head *lh; struct fragment_info *fi, *fi2, *new; list_for_each(lh, frag_info) { fi = list_entry(lh, struct fragment_info, list); - if ((fi->offset + fi->len) == offset) { + if (fi->offset + fi->len == offset) { /* The new fragment can be tacked on to the end */ fi->len += len; /* Did the new fragment plug a hole? */ fi2 = list_entry(lh->next, struct fragment_info, list); - if ((fi->offset + fi->len) == fi2->offset) { + if (fi->offset + fi->len == fi2->offset) { /* glue fragments together */ fi->len += fi2->len; list_del(lh->next); kfree(fi2); } return 0; - } else if ((offset + len) == fi->offset) { + } else if (offset + len == fi->offset) { /* The new fragment can be tacked on to the beginning */ fi->offset = offset; fi->len += len; /* Did the new fragment plug a hole? */ fi2 = list_entry(lh->prev, struct fragment_info, list); - if ((fi2->offset + fi2->len) == fi->offset) { + if (fi2->offset + fi2->len == fi->offset) { /* glue fragments together */ fi2->len += fi->len; list_del(lh); kfree(fi); } return 0; - } else if (offset > (fi->offset + fi->len)) { + } else if (offset > fi->offset + fi->len) { break; - } else if ((offset + len) < fi->offset) { + } else if (offset + len < fi->offset) { lh = lh->prev; break; } @@ -1019,14 +945,12 @@ static inline int new_fragment(struct li new->len = len; list_add(&new->list, lh); - return 0; } -static inline int new_partial_datagram(struct net_device *dev, - struct list_head *pdgl, int dgl, - int dg_size, char *frag_buf, - int frag_off, int frag_len) +static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl, + int dgl, int dg_size, char *frag_buf, + int frag_off, int frag_len) { struct partial_datagram *new; @@ -1059,33 +983,33 @@ static inline int new_partial_datagram(s memcpy(new->pbuf + frag_off, frag_buf, frag_len); list_add(&new->list, pdgl); - return 0; } -static inline int update_partial_datagram(struct list_head *pdgl, struct list_head *lh, - char *frag_buf, int frag_off, int frag_len) +static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh, + char *frag_buf, int frag_off, int frag_len) { - struct partial_datagram *pd = list_entry(lh, struct partial_datagram, list); + struct partial_datagram *pd = + list_entry(lh, struct partial_datagram, list); - if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) { + if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) return -ENOMEM; - } memcpy(pd->pbuf + frag_off, frag_buf, frag_len); /* Move list entry to beginnig of list so that oldest partial * datagrams percolate to the end of the list */ list_move(lh, pdgl); - return 0; } -static inline int is_datagram_complete(struct list_head *lh, int dg_size) +static int is_datagram_complete(struct list_head *lh, int dg_size) { - struct partial_datagram *pd = list_entry(lh, struct partial_datagram, list); - struct fragment_info *fi = list_entry(pd->frag_info.next, - struct fragment_info, list); + struct partial_datagram *pd; + struct fragment_info *fi; + + pd = list_entry(lh, struct partial_datagram, list); + fi = list_entry(pd->frag_info.next, struct fragment_info, list); return (fi->len == dg_size); } @@ -1108,7 +1032,7 @@ static int ether1394_data_handler(struct if (!ud) { struct eth1394_node_ref *node; node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid); - if (!node) { + if (unlikely(!node)) { HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid " "lookup failure: " NODE_BUS_FMT, NODE_BUS_ARGS(priv->host, srcid)); @@ -1120,7 +1044,7 @@ static int ether1394_data_handler(struct priv->ud_list[NODEID_TO_NODE(srcid)] = ud; } - node_info = (struct eth1394_node_info*)ud->device.driver_data; + node_info = (struct eth1394_node_info *)ud->device.driver_data; /* First, did we receive a fragmented or unfragmented datagram? */ hdr->words.word1 = ntohs(hdr->words.word1); @@ -1133,13 +1057,14 @@ static int ether1394_data_handler(struct * high level network layer. */ skb = dev_alloc_skb(len + dev->hard_header_len + 15); - if (!skb) { - HPSB_PRINT (KERN_ERR, "ether1394 rx: low on mem\n"); + if (unlikely(!skb)) { + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); priv->stats.rx_dropped++; return -1; } skb_reserve(skb, (dev->hard_header_len + 15) & ~15); - memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, len - hdr_len); + memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, + len - hdr_len); ether_type = hdr->uf.ether_type; } else { /* A datagram fragment has been received, now the fun begins. */ @@ -1224,9 +1149,8 @@ static int ether1394_data_handler(struct pd = list_entry(lh, struct partial_datagram, list); - if (hdr->common.lf == ETH1394_HDR_LF_FF) { + if (hdr->common.lf == ETH1394_HDR_LF_FF) pd->ether_type = ether_type; - } if (is_datagram_complete(lh, dg_size)) { ether_type = pd->ether_type; @@ -1253,8 +1177,8 @@ static int ether1394_data_handler(struct skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid, ether_type); - spin_lock_irqsave(&priv->lock, flags); + if (!skb->protocol) { priv->stats.rx_errors++; priv->stats.rx_dropped++; @@ -1288,9 +1212,9 @@ static int ether1394_write(struct hpsb_h struct eth1394_host_info *hi; hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (hi == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n", - host->driver->name); + if (unlikely(!hi)) { + ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", + host->id); return RCODE_ADDRESS_ERROR; } @@ -1314,9 +1238,9 @@ static void ether1394_iso(struct hpsb_is int nready; hi = hpsb_get_hostinfo(ð1394_highlevel, iso->host); - if (hi == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n", - iso->host->driver->name); + if (unlikely(!hi)) { + ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", + iso->host->id); return; } @@ -1326,20 +1250,20 @@ static void ether1394_iso(struct hpsb_is for (i = 0; i < nready; i++) { struct hpsb_iso_packet_info *info = &iso->infos[(iso->first_packet + i) % iso->buf_packets]; - data = (quadlet_t*) (iso->data_buf.kvirt + info->offset); + data = (quadlet_t *)(iso->data_buf.kvirt + info->offset); /* skip over GASP header */ buf = (char *)data + 8; len = info->len - 8; - specifier_id = (((be32_to_cpu(data[0]) & 0xffff) << 8) | - ((be32_to_cpu(data[1]) & 0xff000000) >> 24)); + specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 | + (be32_to_cpu(data[1]) & 0xff000000) >> 24; source_id = be32_to_cpu(data[0]) >> 16; priv = netdev_priv(dev); - if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) || - specifier_id != ETHER1394_GASP_SPECIFIER_ID) { + if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) + || specifier_id != ETHER1394_GASP_SPECIFIER_ID) { /* This packet is not for us */ continue; } @@ -1367,35 +1291,31 @@ static void ether1394_iso(struct hpsb_is * speed, and unicast FIFO address information between the sender_unique_id * and the IP addresses. */ -static inline void ether1394_arp_to_1394arp(struct sk_buff *skb, - struct net_device *dev) +static void ether1394_arp_to_1394arp(struct sk_buff *skb, + struct net_device *dev) { struct eth1394_priv *priv = netdev_priv(dev); - struct arphdr *arp = (struct arphdr *)skb->data; unsigned char *arp_ptr = (unsigned char *)(arp + 1); struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; - /* Believe it or not, all that need to happen is sender IP get moved - * and set hw_addr_len, max_rec, sspd, fifo_hi and fifo_lo. */ arp1394->hw_addr_len = 16; arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN); arp1394->max_rec = priv->host->csr.max_rec; arp1394->sspd = priv->host->csr.lnk_spd; - arp1394->fifo_hi = htons (priv->local_fifo >> 32); - arp1394->fifo_lo = htonl (priv->local_fifo & ~0x0); - - return; + arp1394->fifo_hi = htons(priv->local_fifo >> 32); + arp1394->fifo_lo = htonl(priv->local_fifo & ~0x0); } /* We need to encapsulate the standard header with our own. We use the * ethernet header's proto for our own. */ -static inline unsigned int ether1394_encapsulate_prep(unsigned int max_payload, - __be16 proto, - union eth1394_hdr *hdr, - u16 dg_size, u16 dgl) +static unsigned int ether1394_encapsulate_prep(unsigned int max_payload, + __be16 proto, + union eth1394_hdr *hdr, + u16 dg_size, u16 dgl) { - unsigned int adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_UF]; + unsigned int adj_max_payload = + max_payload - hdr_type_len[ETH1394_HDR_LF_UF]; /* Does it all fit in one packet? */ if (dg_size <= adj_max_payload) { @@ -1408,19 +1328,19 @@ static inline unsigned int ether1394_enc hdr->ff.dgl = dgl; adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF]; } - return((dg_size + (adj_max_payload - 1)) / adj_max_payload); + return (dg_size + adj_max_payload - 1) / adj_max_payload; } -static inline unsigned int ether1394_encapsulate(struct sk_buff *skb, - unsigned int max_payload, - union eth1394_hdr *hdr) +static unsigned int ether1394_encapsulate(struct sk_buff *skb, + unsigned int max_payload, + union eth1394_hdr *hdr) { union eth1394_hdr *bufhdr; int ftype = hdr->common.lf; int hdrsz = hdr_type_len[ftype]; unsigned int adj_max_payload = max_payload - hdrsz; - switch(ftype) { + switch (ftype) { case ETH1394_HDR_LF_UF: bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); bufhdr->words.word1 = htons(hdr->words.word1); @@ -1449,11 +1369,10 @@ static inline unsigned int ether1394_enc bufhdr->words.word3 = htons(hdr->words.word3); bufhdr->words.word4 = 0; } - return min(max_payload, skb->len); } -static inline struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host) +static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host) { struct hpsb_packet *p; @@ -1466,61 +1385,57 @@ static inline struct hpsb_packet *ether1 return p; } -static inline int ether1394_prep_write_packet(struct hpsb_packet *p, - struct hpsb_host *host, - nodeid_t node, u64 addr, - void * data, int tx_len) +static int ether1394_prep_write_packet(struct hpsb_packet *p, + struct hpsb_host *host, nodeid_t node, + u64 addr, void *data, int tx_len) { p->node_id = node; p->data = NULL; p->tcode = TCODE_WRITEB; - p->header[1] = (host->node_id << 16) | (addr >> 32); + p->header[1] = host->node_id << 16 | addr >> 32; p->header[2] = addr & 0xffffffff; p->header_size = 16; p->expect_response = 1; if (hpsb_get_tlabel(p)) { - ETH1394_PRINT_G(KERN_ERR, "No more tlabels left while sending " - "to node " NODE_BUS_FMT "\n", NODE_BUS_ARGS(host, node)); + ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n"); return -1; } - p->header[0] = (p->node_id << 16) | (p->tlabel << 10) - | (1 << 8) | (TCODE_WRITEB << 4); + p->header[0] = + p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4; p->header[3] = tx_len << 16; p->data_size = (tx_len + 3) & ~3; - p->data = (quadlet_t*)data; + p->data = data; return 0; } -static inline void ether1394_prep_gasp_packet(struct hpsb_packet *p, - struct eth1394_priv *priv, - struct sk_buff *skb, int length) +static void ether1394_prep_gasp_packet(struct hpsb_packet *p, + struct eth1394_priv *priv, + struct sk_buff *skb, int length) { p->header_size = 4; p->tcode = TCODE_STREAM_DATA; - p->header[0] = (length << 16) | (3 << 14) - | ((priv->broadcast_channel) << 8) - | (TCODE_STREAM_DATA << 4); + p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 | + TCODE_STREAM_DATA << 4; p->data_size = length; - p->data = ((quadlet_t*)skb->data) - 2; - p->data[0] = cpu_to_be32((priv->host->node_id << 16) | + p->data = (quadlet_t *)skb->data - 2; + p->data[0] = cpu_to_be32(priv->host->node_id << 16 | ETHER1394_GASP_SPECIFIER_ID_HI); - p->data[1] = cpu_to_be32((ETHER1394_GASP_SPECIFIER_ID_LO << 24) | + p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 | ETHER1394_GASP_VERSION); - /* Setting the node id to ALL_NODES (not LOCAL_BUS | ALL_NODES) - * prevents hpsb_send_packet() from setting the speed to an arbitrary - * value based on packet->node_id if packet->node_id is not set. */ - p->node_id = ALL_NODES; p->speed_code = priv->bc_sspd; + + /* prevent hpsb_send_packet() from overriding our speed code */ + p->node_id = LOCAL_BUS | ALL_NODES; } -static inline void ether1394_free_packet(struct hpsb_packet *packet) +static void ether1394_free_packet(struct hpsb_packet *packet) { if (packet->tcode != TCODE_STREAM_DATA) hpsb_free_tlabel(packet); @@ -1539,7 +1454,7 @@ static int ether1394_send_packet(struct return -1; if (ptask->tx_type == ETH1394_GASP) { - int length = tx_len + (2 * sizeof(quadlet_t)); + int length = tx_len + 2 * sizeof(quadlet_t); ether1394_prep_gasp_packet(packet, priv, ptask->skb, length); } else if (ether1394_prep_write_packet(packet, priv->host, @@ -1562,13 +1477,11 @@ static int ether1394_send_packet(struct return 0; } - /* Task function to be run when a datagram transmission is completed */ -static inline void ether1394_dg_complete(struct packet_task *ptask, int fail) +static void ether1394_dg_complete(struct packet_task *ptask, int fail) { struct sk_buff *skb = ptask->skb; - struct net_device *dev = skb->dev; - struct eth1394_priv *priv = netdev_priv(dev); + struct eth1394_priv *priv = netdev_priv(skb->dev); unsigned long flags; /* Statistics */ @@ -1586,7 +1499,6 @@ static inline void ether1394_dg_complete kmem_cache_free(packet_task_cache, ptask); } - /* Callback for when a packet has been sent and the status of that packet is * known */ static void ether1394_complete_cb(void *__ptask) @@ -1614,19 +1526,15 @@ static void ether1394_complete_cb(void * } } - - /* Transmit a packet (called by kernel) */ -static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) +static int ether1394_tx(struct sk_buff *skb, struct net_device *dev) { - gfp_t kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; struct eth1394hdr *eth; struct eth1394_priv *priv = netdev_priv(dev); __be16 proto; unsigned long flags; nodeid_t dest_node; eth1394_tx_type tx_type; - int ret = 0; unsigned int tx_len; unsigned int max_payload; u16 dg_size; @@ -1635,29 +1543,24 @@ static int ether1394_tx (struct sk_buff struct eth1394_node_ref *node; struct eth1394_node_info *node_info = NULL; - ptask = kmem_cache_alloc(packet_task_cache, kmflags); - if (ptask == NULL) { - ret = -ENOMEM; + ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC); + if (ptask == NULL) goto fail; - } /* XXX Ignore this for now. Noticed that when MacOSX is the IRM, * it does not set our validity bit. We need to compensate for * that somewhere else, but not in eth1394. */ #if 0 - if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) { - ret = -EAGAIN; + if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) goto fail; - } #endif - if ((skb = skb_share_check (skb, kmflags)) == NULL) { - ret = -ENOMEM; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) goto fail; - } /* Get rid of the fake eth1394 header, but save a pointer */ - eth = (struct eth1394hdr*)skb->data; + eth = (struct eth1394hdr *)skb->data; skb_pull(skb, ETH1394_HLEN); proto = eth->h_proto; @@ -1668,11 +1571,11 @@ #endif if (memcmp(eth->h_dest, dev->broadcast, ETH1394_ALEN) == 0 || proto == htons(ETH_P_ARP) || (proto == htons(ETH_P_IP) && - IN_MULTICAST(ntohl(skb->nh.iph->daddr)))) { + IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { tx_type = ETH1394_GASP; dest_node = LOCAL_BUS | ALL_NODES; max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD; - BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD)); + BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); dgl = priv->bc_dgl; if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) priv->bc_dgl++; @@ -1681,19 +1584,17 @@ #endif node = eth1394_find_node_guid(&priv->ip_node_list, be64_to_cpu(guid)); - if (!node) { - ret = -EAGAIN; + if (!node) goto fail; - } - node_info = (struct eth1394_node_info*)node->ud->device.driver_data; - if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) { - ret = -EAGAIN; + + node_info = + (struct eth1394_node_info *)node->ud->device.driver_data; + if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) goto fail; - } dest_node = node->ud->ne->nodeid; max_payload = node_info->maxpayload; - BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD)); + BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); dgl = node_info->dgl; if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) @@ -1703,7 +1604,7 @@ #endif /* If this is an ARP packet, convert it */ if (proto == htons(ETH_P_ARP)) - ether1394_arp_to_1394arp (skb, dev); + ether1394_arp_to_1394arp(skb, dev); ptask->hdr.words.word1 = 0; ptask->hdr.words.word2 = 0; @@ -1726,9 +1627,8 @@ #endif ptask->tx_type = tx_type; ptask->max_payload = max_payload; - ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, proto, - &ptask->hdr, dg_size, - dgl); + ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, + proto, &ptask->hdr, dg_size, dgl); /* Add the encapsulation header to the fragment */ tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr); @@ -1737,7 +1637,7 @@ #endif goto fail; netif_wake_queue(dev); - return 0; + return NETDEV_TX_OK; fail: if (ptask) kmem_cache_free(packet_task_cache, ptask); @@ -1745,40 +1645,56 @@ fail: if (skb != NULL) dev_kfree_skb(skb); - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); priv->stats.tx_dropped++; priv->stats.tx_errors++; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); if (netif_queue_stopped(dev)) netif_wake_queue(dev); - return 0; /* returning non-zero causes serious problems */ + /* + * FIXME: According to a patch from 2003-02-26, "returning non-zero + * causes serious problems" here, allegedly. Before that patch, + * -ERRNO was returned which is not appropriate under Linux 2.6. + * Perhaps more needs to be done? Stop the queue in serious + * conditions and restart it elsewhere? + */ + /* return NETDEV_TX_BUSY; */ + return NETDEV_TX_OK; } -static void ether1394_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +static void ether1394_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - strcpy (info->driver, driver_name); - /* FIXME XXX provide sane businfo */ - strcpy (info->bus_info, "ieee1394"); + strcpy(info->driver, driver_name); + strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */ } static struct ethtool_ops ethtool_ops = { .get_drvinfo = ether1394_get_drvinfo }; -static int __init ether1394_init_module (void) +static int __init ether1394_init_module(void) { - packet_task_cache = kmem_cache_create("packet_task", sizeof(struct packet_task), + int err; + + packet_task_cache = kmem_cache_create("packet_task", + sizeof(struct packet_task), 0, 0, NULL, NULL); + if (!packet_task_cache) + return -ENOMEM; - /* Register ourselves as a highlevel driver */ hpsb_register_highlevel(ð1394_highlevel); - - return hpsb_register_protocol(ð1394_proto_driver); + err = hpsb_register_protocol(ð1394_proto_driver); + if (err) { + hpsb_unregister_highlevel(ð1394_highlevel); + kmem_cache_destroy(packet_task_cache); + } + return err; } -static void __exit ether1394_exit_module (void) +static void __exit ether1394_exit_module(void) { hpsb_unregister_protocol(ð1394_proto_driver); hpsb_unregister_highlevel(ð1394_highlevel); diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h index c45cbff..a3439ee 100644 --- a/drivers/ieee1394/eth1394.h +++ b/drivers/ieee1394/eth1394.h @@ -25,8 +25,11 @@ #ifndef __ETH1394_H #define __ETH1394_H #include +#include +#include #include "ieee1394.h" +#include "ieee1394_types.h" /* Register for incoming packets. This is 4096 bytes, which supports up to * S3200 (per Table 16-3 of IEEE 1394b-2002). */ @@ -34,22 +37,15 @@ #define ETHER1394_REGION_ADDR_LEN 4096 /* GASP identifier numbers for IPv4 over IEEE 1394 */ #define ETHER1394_GASP_SPECIFIER_ID 0x00005E -#define ETHER1394_GASP_SPECIFIER_ID_HI ((ETHER1394_GASP_SPECIFIER_ID >> 8) & 0xffff) -#define ETHER1394_GASP_SPECIFIER_ID_LO (ETHER1394_GASP_SPECIFIER_ID & 0xff) +#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff) +#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff) #define ETHER1394_GASP_VERSION 1 -#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* GASP header overhead */ +#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */ -#define ETHER1394_GASP_BUFFERS 16 +#define ETHER1394_GASP_BUFFERS 16 -/* rawiso buffer size - due to a limitation in rawiso, we must limit each - * GASP buffer to be less than PAGE_SIZE. */ -#define ETHER1394_ISO_BUF_SIZE ETHER1394_GASP_BUFFERS * \ - min((unsigned int)PAGE_SIZE, \ - 2 * (1U << (priv->host->csr.max_rec + 1))) - -/* Node set == 64 */ -#define NODE_SET (ALL_NODES + 1) +#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */ enum eth1394_bc_states { ETHER1394_BC_ERROR, ETHER1394_BC_RUNNING, @@ -85,19 +81,14 @@ struct eth1394hdr { unsigned short h_proto; /* packet type ID field */ } __attribute__((packed)); -#ifdef __KERNEL__ -#include - static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb) { - return (struct eth1394hdr *)skb->mac.raw; + return (struct eth1394hdr *)skb_mac_header(skb); } -#endif typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type; /* IP1394 headers */ -#include /* Unfragmented */ #if defined __BIG_ENDIAN_BITFIELD diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c index 694da82..83a4933 100644 --- a/drivers/ieee1394/highlevel.c +++ b/drivers/ieee1394/highlevel.c @@ -70,8 +70,12 @@ static struct hl_host_info *hl_get_hosti return NULL; } -/* Returns a per host/driver data structure that was previously stored by - * hpsb_create_hostinfo. */ +/** + * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host + * + * Returns a per @host and @hl driver data structure that was previously stored + * by hpsb_create_hostinfo. + */ void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) { struct hl_host_info *hi = hl_get_hostinfo(hl, host); @@ -79,7 +83,13 @@ void *hpsb_get_hostinfo(struct hpsb_high return hi ? hi->data : NULL; } -/* If size is zero, then the return here is only valid for error checking */ +/** + * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host + * + * Allocate a hostinfo pointer backed by memory with @data_size and bind it to + * to this @hl driver and @host. If @data_size is zero, then the return here is + * only valid for error checking. + */ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, size_t data_size) { @@ -113,6 +123,11 @@ void *hpsb_create_hostinfo(struct hpsb_h return data; } +/** + * hpsb_set_hostinfo - set the hostinfo pointer to something useful + * + * Usually follows a call to hpsb_create_hostinfo, where the size is 0. + */ int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data) { @@ -132,6 +147,11 @@ int hpsb_set_hostinfo(struct hpsb_highle return -EINVAL; } +/** + * hpsb_destroy_hostinfo - free and remove a hostinfo pointer + * + * Free and remove the hostinfo pointer bound to this @hl driver and @host. + */ void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) { struct hl_host_info *hi; @@ -147,6 +167,12 @@ void hpsb_destroy_hostinfo(struct hpsb_h return; } +/** + * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo + * + * Sets an alternate lookup key for the hostinfo bound to this @hl driver and + * @host. + */ void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key) { @@ -158,6 +184,9 @@ void hpsb_set_hostinfo_key(struct hpsb_h return; } +/** + * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key + */ void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key) { struct hl_host_info *hi; @@ -189,6 +218,12 @@ static int highlevel_for_each_host_reg(s return 0; } +/** + * hpsb_register_highlevel - register highlevel driver + * + * The name pointer in @hl has to stay valid at all times because the string is + * not copied. + */ void hpsb_register_highlevel(struct hpsb_highlevel *hl) { unsigned long flags; @@ -258,6 +293,9 @@ static int highlevel_for_each_host_unreg return 0; } +/** + * hpsb_unregister_highlevel - unregister highlevel driver + */ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) { unsigned long flags; @@ -273,6 +311,19 @@ void hpsb_unregister_highlevel(struct hp nodemgr_for_each_host(hl, highlevel_for_each_host_unreg); } +/** + * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space + * + * @start and @end are 48 bit pointers and have to be quadlet aligned. + * @end points to the first address behind the handled addresses. This + * function can be called multiple times for a single hpsb_highlevel @hl to + * implement sparse register sets. The requested region must not overlap any + * previously allocated region, otherwise registering will fail. + * + * It returns true for successful allocation. Address spaces can be + * unregistered with hpsb_unregister_addrspace. All remaining address spaces + * are automatically deallocated together with the hpsb_highlevel @hl. + */ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, struct hpsb_address_ops *ops, @@ -348,6 +399,19 @@ u64 hpsb_allocate_and_register_addrspace return retval; } +/** + * hpsb_register_addrspace - register a host address space + * + * @start and @end are 48 bit pointers and have to be quadlet aligned. + * @end points to the first address behind the handled addresses. This + * function can be called multiple times for a single hpsb_highlevel @hl to + * implement sparse register sets. The requested region must not overlap any + * previously allocated region, otherwise registering will fail. + * + * It returns true for successful allocation. Address spaces can be + * unregistered with hpsb_unregister_addrspace. All remaining address spaces + * are automatically deallocated together with the hpsb_highlevel @hl. + */ int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, struct hpsb_address_ops *ops, u64 start, u64 end) { @@ -419,6 +483,11 @@ int hpsb_unregister_addrspace(struct hps return retval; } +/** + * hpsb_listen_channel - enable receving a certain isochronous channel + * + * Reception is handled through the @hl's iso_receive op. + */ int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel) { @@ -431,6 +500,9 @@ int hpsb_listen_channel(struct hpsb_high return 0; } +/** + * hpsb_unlisten_channel - disable receving a certain isochronous channel + */ void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel) { @@ -528,6 +600,17 @@ void highlevel_fcp_request(struct hpsb_h read_unlock_irqrestore(&hl_irqs_lock, flags); } +/* + * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64: + * + * These functions are called to handle transactions. They are called when a + * packet arrives. The flags argument contains the second word of the first + * header quadlet of the incoming packet (containing transaction label, retry + * code, transaction code and priority). These functions either return a + * response code or a negative number. In the first case a response will be + * generated. In the latter case, no response will be sent and the driver which + * handled the request will send the response itself. + */ int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, unsigned int length, u16 flags) { diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h index 4b33011..63474f7 100644 --- a/drivers/ieee1394/highlevel.h +++ b/drivers/ieee1394/highlevel.h @@ -99,16 +99,6 @@ struct hpsb_address_ops { void highlevel_add_host(struct hpsb_host *host); void highlevel_remove_host(struct hpsb_host *host); void highlevel_host_reset(struct hpsb_host *host); - -/* - * These functions are called to handle transactions. They are called when a - * packet arrives. The flags argument contains the second word of the first - * header quadlet of the incoming packet (containing transaction label, retry - * code, transaction code and priority). These functions either return a - * response code or a negative number. In the first case a response will be - * generated. In the latter case, no response will be sent and the driver which - * handled the request will send the response itself. - */ int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, unsigned int length, u16 flags); int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, @@ -119,30 +109,13 @@ int highlevel_lock(struct hpsb_host *hos int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags); - void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length); void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, void *data, size_t length); -/* - * Register highlevel driver. The name pointer has to stay valid at all times - * because the string is not copied. - */ void hpsb_register_highlevel(struct hpsb_highlevel *hl); void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); -/* - * Register handlers for host address spaces. Start and end are 48 bit pointers - * and have to be quadlet aligned. Argument "end" points to the first address - * behind the handled addresses. This function can be called multiple times for - * a single hpsb_highlevel to implement sparse register sets. The requested - * region must not overlap any previously allocated region, otherwise - * registering will fail. - * - * It returns true for successful allocation. Address spaces can be - * unregistered with hpsb_unregister_addrspace. All remaining address spaces - * are automatically deallocated together with the hpsb_highlevel. - */ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, struct hpsb_address_ops *ops, @@ -152,45 +125,19 @@ int hpsb_register_addrspace(struct hpsb_ struct hpsb_address_ops *ops, u64 start, u64 end); int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, u64 start); - -/* - * Enable or disable receving a certain isochronous channel through the - * iso_receive op. - */ int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned int channel); + unsigned int channel); void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel); -/* Retrieve a hostinfo pointer bound to this driver/host */ void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); - -/* Allocate a hostinfo pointer of data_size bound to this driver/host */ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, size_t data_size); - -/* Free and remove the hostinfo pointer bound to this driver/host */ void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); - -/* Set an alternate lookup key for the hostinfo bound to this driver/host */ void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key); - -/* Retrieve the alternate lookup key for the hostinfo bound to this - * driver/host */ -unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, - struct hpsb_host *host); - -/* Retrieve a hostinfo pointer bound to this driver using its alternate key */ void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); - -/* Set the hostinfo pointer to something useful. Usually follows a call to - * hpsb_create_hostinfo, where the size is 0. */ int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data); -/* Retrieve hpsb_host using a highlevel handle and a key */ -struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, - unsigned long key); - #endif /* IEEE1394_HIGHLEVEL_H */ diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c index 32a1309..6164a9a 100644 --- a/drivers/ieee1394/hosts.c +++ b/drivers/ieee1394/hosts.c @@ -94,14 +94,6 @@ static int alloc_hostnum_cb(struct hpsb_ return 0; } -/* - * The pending_packet_queue is special in that it's processed - * from hardirq context too (such as hpsb_bus_reset()). Hence - * split the lock class from the usual networking skb-head - * lock class by using a separate key for it: - */ -static struct lock_class_key pending_packet_queue_key; - static DEFINE_MUTEX(host_num_alloc); /** @@ -137,9 +129,7 @@ struct hpsb_host *hpsb_alloc_host(struct h->hostdata = h + 1; h->driver = drv; - skb_queue_head_init(&h->pending_packet_queue); - lockdep_set_class(&h->pending_packet_queue.lock, - &pending_packet_queue_key); + INIT_LIST_HEAD(&h->pending_packets); INIT_LIST_HEAD(&h->addr_space); for (i = 2; i < 16; i++) @@ -190,7 +180,7 @@ int hpsb_add_host(struct hpsb_host *host { if (hpsb_default_host_entry(host)) return -ENOMEM; - hpsb_add_extra_config_roms(host); + highlevel_add_host(host); return 0; } @@ -212,12 +202,19 @@ void hpsb_remove_host(struct hpsb_host * host->driver = &dummy_driver; highlevel_remove_host(host); - hpsb_remove_extra_config_roms(host); class_device_unregister(&host->class_dev); device_unregister(&host->device); } +/** + * hpsb_update_config_rom_image - updates configuration ROM image of a host + * + * Updates the configuration ROM image of a host. rom_version must be the + * current version, otherwise it will fail with return value -1. If this + * host does not support config-rom-update, it will return -%EINVAL. + * Return value 0 indicates success. + */ int hpsb_update_config_rom_image(struct hpsb_host *host) { unsigned long reset_delay; diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h index 4bf4fb7..feb55d0 100644 --- a/drivers/ieee1394/hosts.h +++ b/drivers/ieee1394/hosts.h @@ -3,7 +3,6 @@ #define _IEEE1394_HOSTS_H #include #include -#include #include #include #include @@ -25,8 +24,7 @@ struct hpsb_host { atomic_t generation; - struct sk_buff_head pending_packet_queue; - + struct list_head pending_packets; struct timer_list timeout; unsigned long timeout_interval; @@ -202,12 +200,6 @@ struct hpsb_host *hpsb_alloc_host(struct int hpsb_add_host(struct hpsb_host *host); void hpsb_resume_host(struct hpsb_host *host); void hpsb_remove_host(struct hpsb_host *host); - -/* Updates the configuration rom image of a host. rom_version must be the - * current version, otherwise it will fail with return value -1. If this - * host does not support config-rom-update, it will return -EINVAL. - * Return value 0 indicates success. - */ int hpsb_update_config_rom_image(struct hpsb_host *host); #endif /* _IEEE1394_HOSTS_H */ diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index d791d08..8f71b6a 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -30,7 +30,6 @@ #include #include #include #include -#include #include #include #include @@ -96,13 +95,15 @@ static void queue_packet_complete(struct /** - * hpsb_set_packet_complete_task - set the task that runs when a packet - * completes. You cannot call this more than once on a single packet - * before it is sent. - * + * hpsb_set_packet_complete_task - set task that runs when a packet completes * @packet: the packet whose completion we want the task added to * @routine: function to call * @data: data (if any) to pass to the above function + * + * Set the task that runs when a packet completes. You cannot call this more + * than once on a single packet before it is sent. + * + * Typically, the complete @routine is responsible to call hpsb_free_packet(). */ void hpsb_set_packet_complete_task(struct hpsb_packet *packet, void (*routine)(void *), void *data) @@ -115,12 +116,12 @@ void hpsb_set_packet_complete_task(struc /** * hpsb_alloc_packet - allocate new packet structure - * @data_size: size of the data block to be allocated + * @data_size: size of the data block to be allocated, in bytes * * This function allocates, initializes and returns a new &struct hpsb_packet. - * It can be used in interrupt context. A header block is always included, its - * size is big enough to contain all possible 1394 headers. The data block is - * only allocated when @data_size is not zero. + * It can be used in interrupt context. A header block is always included and + * initialized with zeros. Its size is big enough to contain all possible 1394 + * headers. The data block is only allocated if @data_size is not zero. * * For packets for which responses will be received the @data_size has to be big * enough to contain the response's data block since no further allocation @@ -135,50 +136,49 @@ void hpsb_set_packet_complete_task(struc */ struct hpsb_packet *hpsb_alloc_packet(size_t data_size) { - struct hpsb_packet *packet = NULL; - struct sk_buff *skb; + struct hpsb_packet *packet; data_size = ((data_size + 3) & ~3); - skb = alloc_skb(data_size + sizeof(*packet), GFP_ATOMIC); - if (skb == NULL) + packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC); + if (!packet) return NULL; - memset(skb->data, 0, data_size + sizeof(*packet)); - - packet = (struct hpsb_packet *)skb->data; - packet->skb = skb; - - packet->header = packet->embedded_header; packet->state = hpsb_unused; packet->generation = -1; INIT_LIST_HEAD(&packet->driver_list); + INIT_LIST_HEAD(&packet->queue); atomic_set(&packet->refcnt, 1); if (data_size) { - packet->data = (quadlet_t *)(skb->data + sizeof(*packet)); - packet->data_size = data_size; + packet->data = packet->embedded_data; + packet->allocated_data_size = data_size; } - return packet; } - /** * hpsb_free_packet - free packet and data associated with it * @packet: packet to free (is NULL safe) * - * This function will free packet->data and finally the packet itself. + * Frees @packet->data only if it was allocated through hpsb_alloc_packet(). */ void hpsb_free_packet(struct hpsb_packet *packet) { if (packet && atomic_dec_and_test(&packet->refcnt)) { - BUG_ON(!list_empty(&packet->driver_list)); - kfree_skb(packet->skb); + BUG_ON(!list_empty(&packet->driver_list) || + !list_empty(&packet->queue)); + kfree(packet); } } - +/** + * hpsb_reset_bus - initiate bus reset on the given host + * @host: host controller whose bus to reset + * @type: one of enum reset_types + * + * Returns 1 if bus reset already in progress, 0 otherwise. + */ int hpsb_reset_bus(struct hpsb_host *host, int type) { if (!host->in_bus_reset) { @@ -229,6 +229,14 @@ int hpsb_read_cycle_timer(struct hpsb_ho return 0; } +/** + * hpsb_bus_reset - notify a bus reset to the core + * + * For host driver module usage. Safe to use in interrupt context, although + * quite complex; so you may want to run it in the bottom rather than top half. + * + * Returns 1 if bus reset already in progress, 0 otherwise. + */ int hpsb_bus_reset(struct hpsb_host *host) { if (host->in_bus_reset) { @@ -405,6 +413,14 @@ #endif } +/** + * hpsb_selfid_received - hand over received selfid packet to the core + * + * For host driver module usage. Safe to use in interrupt context. + * + * The host driver should have done a successful complement check (second + * quadlet is complement of first) beforehand. + */ void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid) { if (host->in_bus_reset) { @@ -416,6 +432,15 @@ void hpsb_selfid_received(struct hpsb_ho } } +/** + * hpsb_selfid_complete - notify completion of SelfID stage to the core + * + * For host driver module usage. Safe to use in interrupt context, although + * quite complex; so you may want to run it in the bottom rather than top half. + * + * Notify completion of SelfID stage to the core and report new physical ID + * and whether host is root now. + */ void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot) { if (!host->in_bus_reset) @@ -462,30 +487,41 @@ void hpsb_selfid_complete(struct hpsb_ho highlevel_host_reset(host); } +static spinlock_t pending_packets_lock = SPIN_LOCK_UNLOCKED; +/** + * hpsb_packet_sent - notify core of sending a packet + * + * For host driver module usage. Safe to call from within a transmit packet + * routine. + * + * Notify core of sending a packet. Ackcode is the ack code returned for async + * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE + * for other cases (internal errors that don't justify a panic). + */ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, int ackcode) { unsigned long flags; - spin_lock_irqsave(&host->pending_packet_queue.lock, flags); + spin_lock_irqsave(&pending_packets_lock, flags); packet->ack_code = ackcode; if (packet->no_waiter || packet->state == hpsb_complete) { /* if packet->no_waiter, must not have a tlabel allocated */ - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + spin_unlock_irqrestore(&pending_packets_lock, flags); hpsb_free_packet(packet); return; } atomic_dec(&packet->refcnt); /* drop HC's reference */ - /* here the packet must be on the host->pending_packet_queue */ + /* here the packet must be on the host->pending_packets queue */ if (ackcode != ACK_PENDING || !packet->expect_response) { packet->state = hpsb_complete; - __skb_unlink(packet->skb, &host->pending_packet_queue); - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + list_del_init(&packet->queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); queue_packet_complete(packet); return; } @@ -493,7 +529,7 @@ void hpsb_packet_sent(struct hpsb_host * packet->state = hpsb_pending; packet->sendtime = jiffies; - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + spin_unlock_irqrestore(&pending_packets_lock, flags); mod_timer(&host->timeout, jiffies + host->timeout_interval); } @@ -504,9 +540,10 @@ void hpsb_packet_sent(struct hpsb_host * * @rootid: root whose force_root bit should get set (-1 = don't set force_root) * @gapcnt: gap count value to set (-1 = don't set gap count) * - * This function sends a PHY config packet on the bus through the specified host. + * This function sends a PHY config packet on the bus through the specified + * host. * - * Return value: 0 for success or error number otherwise. + * Return value: 0 for success or negative error number otherwise. */ int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt) { @@ -567,12 +604,16 @@ int hpsb_send_packet(struct hpsb_packet WARN_ON(packet->no_waiter && packet->expect_response); if (!packet->no_waiter || packet->expect_response) { + unsigned long flags; + atomic_inc(&packet->refcnt); /* Set the initial "sendtime" to 10 seconds from now, to prevent premature expiry. If a packet takes more than 10 seconds to hit the wire, we have bigger problems :) */ packet->sendtime = jiffies + 10 * HZ; - skb_queue_tail(&host->pending_packet_queue, packet->skb); + spin_lock_irqsave(&pending_packets_lock, flags); + list_add_tail(&packet->queue, &host->pending_packets); + spin_unlock_irqrestore(&pending_packets_lock, flags); } if (packet->node_id == host->node_id) { @@ -621,6 +662,12 @@ static void complete_packet(void *data) complete((struct completion *) data); } +/** + * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes + * @packet: packet to send + * + * Return value: 0 on success, negative errno on failure. + */ int hpsb_send_packet_and_wait(struct hpsb_packet *packet) { struct completion done; @@ -642,86 +689,97 @@ static void send_packet_nocare(struct hp } } +static size_t packet_size_to_data_size(size_t packet_size, size_t header_size, + size_t buffer_size, int tcode) +{ + size_t ret = packet_size <= header_size ? 0 : packet_size - header_size; + + if (unlikely(ret > buffer_size)) + ret = buffer_size; + + if (unlikely(ret + header_size != packet_size)) + HPSB_ERR("unexpected packet size %zd (tcode %d), bug?", + packet_size, tcode); + return ret; +} static void handle_packet_response(struct hpsb_host *host, int tcode, quadlet_t *data, size_t size) { - struct hpsb_packet *packet = NULL; - struct sk_buff *skb; - int tcode_match = 0; - int tlabel; + struct hpsb_packet *packet; + int tlabel = (data[0] >> 10) & 0x3f; + size_t header_size; unsigned long flags; - tlabel = (data[0] >> 10) & 0x3f; - - spin_lock_irqsave(&host->pending_packet_queue.lock, flags); + spin_lock_irqsave(&pending_packets_lock, flags); - skb_queue_walk(&host->pending_packet_queue, skb) { - packet = (struct hpsb_packet *)skb->data; - if ((packet->tlabel == tlabel) - && (packet->node_id == (data[1] >> 16))){ - break; - } - - packet = NULL; - } + list_for_each_entry(packet, &host->pending_packets, queue) + if (packet->tlabel == tlabel && + packet->node_id == (data[1] >> 16)) + goto found; - if (packet == NULL) { - HPSB_DEBUG("unsolicited response packet received - no tlabel match"); - dump_packet("contents", data, 16, -1); - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); - return; - } + spin_unlock_irqrestore(&pending_packets_lock, flags); + HPSB_DEBUG("unsolicited response packet received - %s", + "no tlabel match"); + dump_packet("contents", data, 16, -1); + return; +found: switch (packet->tcode) { case TCODE_WRITEQ: case TCODE_WRITEB: - if (tcode != TCODE_WRITE_RESPONSE) + if (unlikely(tcode != TCODE_WRITE_RESPONSE)) break; - tcode_match = 1; - memcpy(packet->header, data, 12); - break; + header_size = 12; + size = 0; + goto dequeue; + case TCODE_READQ: - if (tcode != TCODE_READQ_RESPONSE) + if (unlikely(tcode != TCODE_READQ_RESPONSE)) break; - tcode_match = 1; - memcpy(packet->header, data, 16); - break; + header_size = 16; + size = 0; + goto dequeue; + case TCODE_READB: - if (tcode != TCODE_READB_RESPONSE) + if (unlikely(tcode != TCODE_READB_RESPONSE)) break; - tcode_match = 1; - BUG_ON(packet->skb->len - sizeof(*packet) < size - 16); - memcpy(packet->header, data, 16); - memcpy(packet->data, data + 4, size - 16); - break; + header_size = 16; + size = packet_size_to_data_size(size, header_size, + packet->allocated_data_size, + tcode); + goto dequeue; + case TCODE_LOCK_REQUEST: - if (tcode != TCODE_LOCK_RESPONSE) + if (unlikely(tcode != TCODE_LOCK_RESPONSE)) break; - tcode_match = 1; - size = min((size - 16), (size_t)8); - BUG_ON(packet->skb->len - sizeof(*packet) < size); - memcpy(packet->header, data, 16); - memcpy(packet->data, data + 4, size); - break; + header_size = 16; + size = packet_size_to_data_size(min(size, (size_t)(16 + 8)), + header_size, + packet->allocated_data_size, + tcode); + goto dequeue; } - if (!tcode_match) { - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); - HPSB_INFO("unsolicited response packet received - tcode mismatch"); - dump_packet("contents", data, 16, -1); - return; - } + spin_unlock_irqrestore(&pending_packets_lock, flags); + HPSB_DEBUG("unsolicited response packet received - %s", + "tcode mismatch"); + dump_packet("contents", data, 16, -1); + return; - __skb_unlink(skb, &host->pending_packet_queue); +dequeue: + list_del_init(&packet->queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); if (packet->state == hpsb_queued) { packet->sendtime = jiffies; packet->ack_code = ACK_PENDING; } - packet->state = hpsb_complete; - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + + memcpy(packet->header, data, header_size); + if (size) + memcpy(packet->data, data + 4, size); queue_packet_complete(packet); } @@ -735,6 +793,7 @@ static struct hpsb_packet *create_reply_ p = hpsb_alloc_packet(dsize); if (unlikely(p == NULL)) { /* FIXME - send data_error response */ + HPSB_ERR("out of memory, cannot send response packet"); return NULL; } @@ -784,7 +843,6 @@ static void fill_async_readblock_resp(st static void fill_async_write_resp(struct hpsb_packet *packet, int rcode) { PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE); - packet->header[2] = 0; packet->header_size = 12; packet->data_size = 0; } @@ -801,12 +859,9 @@ static void fill_async_lock_resp(struct packet->data_size = length; } -#define PREP_REPLY_PACKET(length) \ - packet = create_reply_packet(host, data, length); \ - if (packet == NULL) break - static void handle_incoming_packet(struct hpsb_host *host, int tcode, - quadlet_t *data, size_t size, int write_acked) + quadlet_t *data, size_t size, + int write_acked) { struct hpsb_packet *packet; int length, rcode, extcode; @@ -816,74 +871,72 @@ static void handle_incoming_packet(struc u16 flags = (u16) data[0]; u64 addr; - /* big FIXME - no error checking is done for an out of bounds length */ + /* FIXME? + * Out-of-bounds lengths are left for highlevel_read|write to cap. */ switch (tcode) { case TCODE_WRITEQ: addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data+3, + rcode = highlevel_write(host, source, dest, data + 3, addr, 4, flags); - - if (!write_acked - && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) - && (rcode >= 0)) { - /* not a broadcast write, reply */ - PREP_REPLY_PACKET(0); - fill_async_write_resp(packet, rcode); - send_packet_nocare(packet); - } - break; + goto handle_write_request; case TCODE_WRITEB: addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data+4, - addr, data[3]>>16, flags); - - if (!write_acked - && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) - && (rcode >= 0)) { - /* not a broadcast write, reply */ - PREP_REPLY_PACKET(0); + rcode = highlevel_write(host, source, dest, data + 4, + addr, data[3] >> 16, flags); +handle_write_request: + if (rcode < 0 || write_acked || + NODEID_TO_NODE(data[0] >> 16) == NODE_MASK) + return; + /* not a broadcast write, reply */ + packet = create_reply_packet(host, data, 0); + if (packet) { fill_async_write_resp(packet, rcode); send_packet_nocare(packet); } - break; + return; case TCODE_READQ: addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; rcode = highlevel_read(host, source, &buffer, addr, 4, flags); + if (rcode < 0) + return; - if (rcode >= 0) { - PREP_REPLY_PACKET(0); + packet = create_reply_packet(host, data, 0); + if (packet) { fill_async_readquad_resp(packet, rcode, buffer); send_packet_nocare(packet); } - break; + return; case TCODE_READB: length = data[3] >> 16; - PREP_REPLY_PACKET(length); + packet = create_reply_packet(host, data, length); + if (!packet) + return; addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; rcode = highlevel_read(host, source, packet->data, addr, length, flags); - - if (rcode >= 0) { - fill_async_readblock_resp(packet, rcode, length); - send_packet_nocare(packet); - } else { + if (rcode < 0) { hpsb_free_packet(packet); + return; } - break; + fill_async_readblock_resp(packet, rcode, length); + send_packet_nocare(packet); + return; case TCODE_LOCK_REQUEST: length = data[3] >> 16; extcode = data[3] & 0xffff; addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - PREP_REPLY_PACKET(8); + packet = create_reply_packet(host, data, 8); + if (!packet) + return; - if ((extcode == 0) || (extcode >= 7)) { + if (extcode == 0 || extcode >= 7) { /* let switch default handle error */ length = 0; } @@ -891,12 +944,12 @@ static void handle_incoming_packet(struc switch (length) { case 4: rcode = highlevel_lock(host, source, packet->data, addr, - data[4], 0, extcode,flags); + data[4], 0, extcode, flags); fill_async_lock_resp(packet, rcode, extcode, 4); break; case 8: - if ((extcode != EXTCODE_FETCH_ADD) - && (extcode != EXTCODE_LITTLE_ADD)) { + if (extcode != EXTCODE_FETCH_ADD && + extcode != EXTCODE_LITTLE_ADD) { rcode = highlevel_lock(host, source, packet->data, addr, data[5], data[4], @@ -920,29 +973,38 @@ static void handle_incoming_packet(struc break; default: rcode = RCODE_TYPE_ERROR; - fill_async_lock_resp(packet, rcode, - extcode, 0); + fill_async_lock_resp(packet, rcode, extcode, 0); } - if (rcode >= 0) { - send_packet_nocare(packet); - } else { + if (rcode < 0) hpsb_free_packet(packet); - } - break; + else + send_packet_nocare(packet); + return; } - } -#undef PREP_REPLY_PACKET - +/** + * hpsb_packet_received - hand over received packet to the core + * + * For host driver module usage. + * + * The contents of data are expected to be the full packet but with the CRCs + * left out (data block follows header immediately), with the header (i.e. the + * first four quadlets) in machine byte order and the data block in big endian. + * *@data can be safely overwritten after this call. + * + * If the packet is a write request, @write_acked is to be set to true if it was + * ack_complete'd already, false otherwise. This argument is ignored for any + * other packet type. + */ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, int write_acked) { int tcode; - if (host->in_bus_reset) { - HPSB_INFO("received packet during reset; ignoring"); + if (unlikely(host->in_bus_reset)) { + HPSB_DEBUG("received packet during reset; ignoring"); return; } @@ -976,23 +1038,27 @@ void hpsb_packet_received(struct hpsb_ho break; default: - HPSB_NOTICE("received packet with bogus transaction code %d", - tcode); + HPSB_DEBUG("received packet with bogus transaction code %d", + tcode); break; } } - static void abort_requests(struct hpsb_host *host) { - struct hpsb_packet *packet; - struct sk_buff *skb; + struct hpsb_packet *packet, *p; + struct list_head tmp; + unsigned long flags; host->driver->devctl(host, CANCEL_REQUESTS, 0); - while ((skb = skb_dequeue(&host->pending_packet_queue)) != NULL) { - packet = (struct hpsb_packet *)skb->data; + INIT_LIST_HEAD(&tmp); + spin_lock_irqsave(&pending_packets_lock, flags); + list_splice_init(&host->pending_packets, &tmp); + spin_unlock_irqrestore(&pending_packets_lock, flags); + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); packet->state = hpsb_complete; packet->ack_code = ACKX_ABORTED; queue_packet_complete(packet); @@ -1002,87 +1068,90 @@ static void abort_requests(struct hpsb_h void abort_timedouts(unsigned long __opaque) { struct hpsb_host *host = (struct hpsb_host *)__opaque; - unsigned long flags; - struct hpsb_packet *packet; - struct sk_buff *skb; - unsigned long expire; + struct hpsb_packet *packet, *p; + struct list_head tmp; + unsigned long flags, expire, j; spin_lock_irqsave(&host->csr.lock, flags); expire = host->csr.expire; spin_unlock_irqrestore(&host->csr.lock, flags); - /* Hold the lock around this, since we aren't dequeuing all - * packets, just ones we need. */ - spin_lock_irqsave(&host->pending_packet_queue.lock, flags); - - while (!skb_queue_empty(&host->pending_packet_queue)) { - skb = skb_peek(&host->pending_packet_queue); - - packet = (struct hpsb_packet *)skb->data; + j = jiffies; + INIT_LIST_HEAD(&tmp); + spin_lock_irqsave(&pending_packets_lock, flags); - if (time_before(packet->sendtime + expire, jiffies)) { - __skb_unlink(skb, &host->pending_packet_queue); - packet->state = hpsb_complete; - packet->ack_code = ACKX_TIMEOUT; - queue_packet_complete(packet); - } else { + list_for_each_entry_safe(packet, p, &host->pending_packets, queue) { + if (time_before(packet->sendtime + expire, j)) + list_move_tail(&packet->queue, &tmp); + else /* Since packets are added to the tail, the oldest * ones are first, always. When we get to one that * isn't timed out, the rest aren't either. */ break; - } } + if (!list_empty(&host->pending_packets)) + mod_timer(&host->timeout, j + host->timeout_interval); - if (!skb_queue_empty(&host->pending_packet_queue)) - mod_timer(&host->timeout, jiffies + host->timeout_interval); + spin_unlock_irqrestore(&pending_packets_lock, flags); - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); + packet->state = hpsb_complete; + packet->ack_code = ACKX_TIMEOUT; + queue_packet_complete(packet); + } } - -/* Kernel thread and vars, which handles packets that are completed. Only - * packets that have a "complete" function are sent here. This way, the - * completion is run out of kernel context, and doesn't block the rest of - * the stack. */ static struct task_struct *khpsbpkt_thread; -static struct sk_buff_head hpsbpkt_queue; +static LIST_HEAD(hpsbpkt_queue); static void queue_packet_complete(struct hpsb_packet *packet) { + unsigned long flags; + if (packet->no_waiter) { hpsb_free_packet(packet); return; } if (packet->complete_routine != NULL) { - skb_queue_tail(&hpsbpkt_queue, packet->skb); + spin_lock_irqsave(&pending_packets_lock, flags); + list_add_tail(&packet->queue, &hpsbpkt_queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); wake_up_process(khpsbpkt_thread); } return; } +/* + * Kernel thread which handles packets that are completed. This way the + * packet's "complete" function is asynchronously run in process context. + * Only packets which have a "complete" function may be sent here. + */ static int hpsbpkt_thread(void *__hi) { - struct sk_buff *skb; - struct hpsb_packet *packet; - void (*complete_routine)(void*); - void *complete_data; + struct hpsb_packet *packet, *p; + struct list_head tmp; + int may_schedule; current->flags |= PF_NOFREEZE; while (!kthread_should_stop()) { - while ((skb = skb_dequeue(&hpsbpkt_queue)) != NULL) { - packet = (struct hpsb_packet *)skb->data; - - complete_routine = packet->complete_routine; - complete_data = packet->complete_data; - packet->complete_routine = packet->complete_data = NULL; + INIT_LIST_HEAD(&tmp); + spin_lock_irq(&pending_packets_lock); + list_splice_init(&hpsbpkt_queue, &tmp); + spin_unlock_irq(&pending_packets_lock); - complete_routine(complete_data); + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); + packet->complete_routine(packet->complete_data); } set_current_state(TASK_INTERRUPTIBLE); - if (!skb_peek(&hpsbpkt_queue)) + spin_lock_irq(&pending_packets_lock); + may_schedule = list_empty(&hpsbpkt_queue); + spin_unlock_irq(&pending_packets_lock); + if (may_schedule) schedule(); __set_current_state(TASK_RUNNING); } @@ -1093,8 +1162,6 @@ static int __init ieee1394_init(void) { int i, ret; - skb_queue_head_init(&hpsbpkt_queue); - /* non-fatal error */ if (hpsb_init_config_roms()) { HPSB_ERR("Failed to initialize some config rom entries.\n"); @@ -1268,7 +1335,6 @@ EXPORT_SYMBOL(hpsb_destroy_hostinfo); EXPORT_SYMBOL(hpsb_set_hostinfo_key); EXPORT_SYMBOL(hpsb_get_hostinfo_bykey); EXPORT_SYMBOL(hpsb_set_hostinfo); -EXPORT_SYMBOL(highlevel_host_reset); /** nodemgr.c **/ EXPORT_SYMBOL(hpsb_node_fill_packet); @@ -1311,11 +1377,10 @@ EXPORT_SYMBOL(hpsb_iso_wake); EXPORT_SYMBOL(hpsb_iso_recv_flush); /** csr1212.c **/ -EXPORT_SYMBOL(csr1212_new_directory); EXPORT_SYMBOL(csr1212_attach_keyval_to_directory); EXPORT_SYMBOL(csr1212_detach_keyval_from_directory); -EXPORT_SYMBOL(csr1212_release_keyval); -EXPORT_SYMBOL(csr1212_read); +EXPORT_SYMBOL(csr1212_get_keyval); +EXPORT_SYMBOL(csr1212_new_directory); EXPORT_SYMBOL(csr1212_parse_keyval); -EXPORT_SYMBOL(_csr1212_read_keyval); -EXPORT_SYMBOL(_csr1212_destroy_keyval); +EXPORT_SYMBOL(csr1212_read); +EXPORT_SYMBOL(csr1212_release_keyval); diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h index bd29d8e..ad52652 100644 --- a/drivers/ieee1394/ieee1394_core.h +++ b/drivers/ieee1394/ieee1394_core.h @@ -4,7 +4,6 @@ #define _IEEE1394_CORE_H #include #include #include -#include #include #include @@ -13,7 +12,7 @@ #include "ieee1394_types.h" struct hpsb_packet { /* This struct is basically read-only for hosts with the exception of - * the data buffer contents and xnext - see below. */ + * the data buffer contents and driver_list. */ /* This can be used for host driver internal linking. * @@ -49,134 +48,65 @@ struct hpsb_packet { /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */ unsigned speed_code:2; - /* - * *header and *data are guaranteed to be 32-bit DMAable and may be - * overwritten to allow in-place byte swapping. Neither of these is - * CRCed (the sizes also don't include CRC), but contain space for at - * least one additional quadlet to allow in-place CRCing. The memory is - * also guaranteed to be DMA mappable. - */ - quadlet_t *header; - quadlet_t *data; - size_t header_size; - size_t data_size; - struct hpsb_host *host; unsigned int generation; atomic_t refcnt; + struct list_head queue; /* Function (and possible data to pass to it) to call when this * packet is completed. */ void (*complete_routine)(void *); void *complete_data; - /* XXX This is just a hack at the moment */ - struct sk_buff *skb; - /* Store jiffies for implementing bus timeouts. */ unsigned long sendtime; - quadlet_t embedded_header[5]; + /* Sizes are in bytes. *data can be DMA-mapped. */ + size_t allocated_data_size; /* as allocated */ + size_t data_size; /* as filled in */ + size_t header_size; /* as filled in, not counting the CRC */ + quadlet_t *data; + quadlet_t header[5]; + quadlet_t embedded_data[0]; /* keep as last member */ }; -/* Set a task for when a packet completes */ void hpsb_set_packet_complete_task(struct hpsb_packet *packet, void (*routine)(void *), void *data); - static inline struct hpsb_packet *driver_packet(struct list_head *l) { return list_entry(l, struct hpsb_packet, driver_list); } - void abort_timedouts(unsigned long __opaque); - struct hpsb_packet *hpsb_alloc_packet(size_t data_size); void hpsb_free_packet(struct hpsb_packet *packet); -/* - * Generation counter for the complete 1394 subsystem. Generation gets - * incremented on every change in the subsystem (e.g. bus reset). +/** + * get_hpsb_generation - generation counter for the complete 1394 subsystem * - * Use the functions, not the variable. + * Generation gets incremented on every change in the subsystem (notably on bus + * resets). Use the functions, not the variable. */ static inline unsigned int get_hpsb_generation(struct hpsb_host *host) { return atomic_read(&host->generation); } -/* - * Send a PHY configuration packet, return 0 on success, negative - * errno on failure. - */ int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt); - -/* - * Queue packet for transmitting, return 0 on success, negative errno - * on failure. - */ int hpsb_send_packet(struct hpsb_packet *packet); - -/* - * Queue packet for transmitting, and block until the transaction - * completes. Return 0 on success, negative errno on failure. - */ int hpsb_send_packet_and_wait(struct hpsb_packet *packet); - -/* Initiate bus reset on the given host. Returns 1 if bus reset already in - * progress, 0 otherwise. */ int hpsb_reset_bus(struct hpsb_host *host, int type); - int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, u64 *local_time); -/* - * The following functions are exported for host driver module usage. All of - * them are safe to use in interrupt contexts, although some are quite - * complicated so you may want to run them in bottom halves instead of calling - * them directly. - */ - -/* Notify a bus reset to the core. Returns 1 if bus reset already in progress, - * 0 otherwise. */ int hpsb_bus_reset(struct hpsb_host *host); - -/* - * Hand over received selfid packet to the core. Complement check (second - * quadlet is complement of first) is expected to be done and successful. - */ void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid); - -/* - * Notify completion of SelfID stage to the core and report new physical ID - * and whether host is root now. - */ void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot); - -/* - * Notify core of sending a packet. Ackcode is the ack code returned for async - * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE - * for other cases (internal errors that don't justify a panic). Safe to call - * from within a transmit packet routine. - */ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, int ackcode); - -/* - * Hand over received packet to the core. The contents of data are expected to - * be the full packet but with the CRCs left out (data block follows header - * immediately), with the header (i.e. the first four quadlets) in machine byte - * order and the data block in big endian. *data can be safely overwritten - * after this call. - * - * If the packet is a write request, write_acked is to be set to true if it was - * ack_complete'd already, false otherwise. This arg is ignored for any other - * packet type. - */ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, int write_acked); - /* * CHARACTER DEVICE DISPATCHING * @@ -217,7 +147,9 @@ #define IEEE1394_DV1394_DEV MKDEV(IEEE #define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \ IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) -/* return the index (within a minor number block) of a file */ +/** + * ieee1394_file_to_instance - get the index within a minor number block + */ static inline unsigned char ieee1394_file_to_instance(struct file *file) { return file->f_path.dentry->d_inode->i_cindex; diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index 0833fc9..40078ce 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -10,11 +10,16 @@ */ #include +#include +#include #include +#include +#include /* because linux/wait.h is broken if CONFIG_SMP=n */ #include #include #include +#include #include "ieee1394.h" #include "ieee1394_types.h" @@ -32,7 +37,7 @@ #define PREP_ASYNC_HEAD_ADDRESS(tc) \ #ifndef HPSB_DEBUG_TLABELS static #endif -spinlock_t hpsb_tlabel_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(hpsb_tlabel_lock); static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq); @@ -212,6 +217,15 @@ void hpsb_free_tlabel(struct hpsb_packet wake_up_interruptible(&tlabel_wq); } +/** + * hpsb_packet_success - Make sense of the ack and reply codes + * + * Make sense of the ack and reply codes and return more convenient error codes: + * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can + * probably resolved by retry. -%EREMOTEIO = node suffers from an internal + * error. -%EACCES = this transaction is not allowed on requested address. + * -%EINVAL = invalid address at node. + */ int hpsb_packet_success(struct hpsb_packet *packet) { switch (packet->ack_code) { @@ -364,6 +378,13 @@ struct hpsb_packet *hpsb_make_streampack } packet->host = host; + /* Because it is too difficult to determine all PHY speeds and link + * speeds here, we use S100... */ + packet->speed_code = IEEE1394_SPEED_100; + + /* ...and prevent hpsb_send_packet() from overriding it. */ + packet->node_id = LOCAL_BUS | ALL_NODES; + if (hpsb_get_tlabel(packet)) { hpsb_free_packet(packet); return NULL; @@ -493,6 +514,16 @@ struct hpsb_packet *hpsb_make_isopacket( * avoid in kernel buffers for user space callers */ +/** + * hpsb_read - generic read function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet read request if @length == 4 and and a block read request otherwise. + * It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t * buffer, size_t length) { @@ -532,6 +563,16 @@ int hpsb_read(struct hpsb_host *host, no return retval; } +/** + * hpsb_write - generic write function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet write request if @length == 4 and and a block write request + * otherwise. It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t * buffer, size_t length) { diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h index c1369c4..86b8ee6 100644 --- a/drivers/ieee1394/ieee1394_transactions.h +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -27,27 +27,7 @@ struct hpsb_packet *hpsb_make_writepacke struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer, int length, int channel, int tag, int sync); - -/* - * hpsb_packet_success - Make sense of the ack and reply codes and - * return more convenient error codes: - * 0 success - * -EBUSY node is busy, try again - * -EAGAIN error which can probably resolved by retry - * -EREMOTEIO node suffers from an internal error - * -EACCES this transaction is not allowed on requested address - * -EINVAL invalid address at node - */ int hpsb_packet_success(struct hpsb_packet *packet); - -/* - * The generic read and write functions. All recognize the local node ID - * and act accordingly. Read and write automatically use quadlet commands if - * length == 4 and and block commands otherwise (however, they do not yet - * support lengths that are not a multiple of 4). You must explicitly specifiy - * the generation for which the node ID is valid, to avoid sending packets to - * the wrong nodes when we race with a bus reset. - */ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length); int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c index c6227e5..07ca35c 100644 --- a/drivers/ieee1394/iso.c +++ b/drivers/ieee1394/iso.c @@ -10,11 +10,15 @@ */ #include +#include #include #include "hosts.h" #include "iso.h" +/** + * hpsb_iso_stop - stop DMA + */ void hpsb_iso_stop(struct hpsb_iso *iso) { if (!(iso->flags & HPSB_ISO_DRIVER_STARTED)) @@ -25,6 +29,9 @@ void hpsb_iso_stop(struct hpsb_iso *iso) iso->flags &= ~HPSB_ISO_DRIVER_STARTED; } +/** + * hpsb_iso_shutdown - deallocate buffer and DMA context + */ void hpsb_iso_shutdown(struct hpsb_iso *iso) { if (iso->flags & HPSB_ISO_DRIVER_INIT) { @@ -130,6 +137,9 @@ static struct hpsb_iso *hpsb_iso_common_ return NULL; } +/** + * hpsb_iso_n_ready - returns number of packets ready to send or receive + */ int hpsb_iso_n_ready(struct hpsb_iso *iso) { unsigned long flags; @@ -142,6 +152,9 @@ int hpsb_iso_n_ready(struct hpsb_iso *is return val; } +/** + * hpsb_iso_xmit_init - allocate the buffer and DMA context + */ struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -172,6 +185,11 @@ struct hpsb_iso *hpsb_iso_xmit_init(stru return NULL; } +/** + * hpsb_iso_recv_init - allocate the buffer and DMA context + * + * Note, if channel = -1, multi-channel receive is enabled. + */ struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -199,6 +217,11 @@ struct hpsb_iso *hpsb_iso_recv_init(stru return NULL; } +/** + * hpsb_iso_recv_listen_channel + * + * multi-channel only + */ int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel) { if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) @@ -206,6 +229,11 @@ int hpsb_iso_recv_listen_channel(struct return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel); } +/** + * hpsb_iso_recv_unlisten_channel + * + * multi-channel only + */ int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel) { if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) @@ -213,6 +241,11 @@ int hpsb_iso_recv_unlisten_channel(struc return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel); } +/** + * hpsb_iso_recv_set_channel_mask + * + * multi-channel only + */ int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) { if (iso->type != HPSB_ISO_RECV || iso->channel != -1) @@ -221,6 +254,12 @@ int hpsb_iso_recv_set_channel_mask(struc (unsigned long)&mask); } +/** + * hpsb_iso_recv_flush - check for arrival of new packets + * + * check for arrival of new packets immediately (even if irq_interval + * has not yet been reached) + */ int hpsb_iso_recv_flush(struct hpsb_iso *iso) { if (iso->type != HPSB_ISO_RECV) @@ -238,6 +277,9 @@ static int do_iso_xmit_start(struct hpsb return retval; } +/** + * hpsb_iso_xmit_start - start DMA + */ int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer) { if (iso->type != HPSB_ISO_XMIT) @@ -270,6 +312,9 @@ int hpsb_iso_xmit_start(struct hpsb_iso return 0; } +/** + * hpsb_iso_recv_start - start DMA + */ int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) { int retval = 0; @@ -306,8 +351,7 @@ int hpsb_iso_recv_start(struct hpsb_iso } /* check to make sure the user has not supplied bogus values of offset/len - that would cause the kernel to access memory outside the buffer */ - + * that would cause the kernel to access memory outside the buffer */ static int hpsb_iso_check_offset_len(struct hpsb_iso *iso, unsigned int offset, unsigned short len, unsigned int *out_offset, @@ -331,6 +375,12 @@ static int hpsb_iso_check_offset_len(str return 0; } +/** + * hpsb_iso_xmit_queue_packet - queue a packet for transmission. + * + * @offset is relative to the beginning of the DMA buffer, where the packet's + * data payload should already have been placed. + */ int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy) { @@ -380,6 +430,9 @@ int hpsb_iso_xmit_queue_packet(struct hp return rv; } +/** + * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted + */ int hpsb_iso_xmit_sync(struct hpsb_iso *iso) { if (iso->type != HPSB_ISO_XMIT) @@ -390,6 +443,15 @@ int hpsb_iso_xmit_sync(struct hpsb_iso * iso->buf_packets); } +/** + * hpsb_iso_packet_sent + * + * Available to low-level drivers. + * + * Call after a packet has been transmitted to the bus (interrupt context is + * OK). @cycle is the _exact_ cycle the packet was sent on. @error should be + * non-zero if some sort of error occurred when sending the packet. + */ void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error) { unsigned long flags; @@ -413,6 +475,13 @@ void hpsb_iso_packet_sent(struct hpsb_is spin_unlock_irqrestore(&iso->lock, flags); } +/** + * hpsb_iso_packet_received + * + * Available to low-level drivers. + * + * Call after a packet has been received (interrupt context is OK). + */ void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, u16 total_len, u16 cycle, u8 channel, u8 tag, u8 sy) @@ -442,6 +511,11 @@ void hpsb_iso_packet_received(struct hps spin_unlock_irqrestore(&iso->lock, flags); } +/** + * hpsb_iso_recv_release_packets - release packets, reuse buffer + * + * @n_packets have been read out of the buffer, re-use the buffer space + */ int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) { unsigned long flags; @@ -477,6 +551,13 @@ int hpsb_iso_recv_release_packets(struct return rv; } +/** + * hpsb_iso_wake + * + * Available to low-level drivers. + * + * Call to wake waiting processes after buffer space has opened up. + */ void hpsb_iso_wake(struct hpsb_iso *iso) { wake_up_interruptible(&iso->waitq); diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h index 1210a97..b94e55e 100644 --- a/drivers/ieee1394/iso.h +++ b/drivers/ieee1394/iso.h @@ -150,8 +150,6 @@ #define HPSB_ISO_DRIVER_STARTED (1<<1) /* functions available to high-level drivers (e.g. raw1394) */ -/* allocate the buffer and DMA context */ - struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -159,8 +157,6 @@ struct hpsb_iso* hpsb_iso_xmit_init(stru int speed, int irq_interval, void (*callback)(struct hpsb_iso*)); - -/* note: if channel = -1, multi-channel receive is enabled */ struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -168,56 +164,29 @@ struct hpsb_iso* hpsb_iso_recv_init(stru int dma_mode, int irq_interval, void (*callback)(struct hpsb_iso*)); - -/* multi-channel only */ int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel); int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel); int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask); - -/* start/stop DMA */ int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, int prebuffer); int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, int tag_mask, int sync); void hpsb_iso_stop(struct hpsb_iso *iso); - -/* deallocate buffer and DMA context */ void hpsb_iso_shutdown(struct hpsb_iso *iso); - -/* queue a packet for transmission. - * 'offset' is relative to the beginning of the DMA buffer, where the packet's - * data payload should already have been placed. */ int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy); - -/* wait until all queued packets have been transmitted to the bus */ int hpsb_iso_xmit_sync(struct hpsb_iso *iso); - -/* N packets have been read out of the buffer, re-use the buffer space */ -int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, - unsigned int n_packets); - -/* check for arrival of new packets immediately (even if irq_interval - * has not yet been reached) */ +int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, + unsigned int n_packets); int hpsb_iso_recv_flush(struct hpsb_iso *iso); - -/* returns # of packets ready to send or receive */ int hpsb_iso_n_ready(struct hpsb_iso *iso); /* the following are callbacks available to low-level drivers */ -/* call after a packet has been transmitted to the bus (interrupt context is OK) - * 'cycle' is the _exact_ cycle the packet was sent on - * 'error' should be non-zero if some sort of error occurred when sending the - * packet */ void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error); - -/* call after a packet has been received (interrupt context OK) */ void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, u16 total_len, u16 cycle, u8 channel, u8 tag, u8 sy); - -/* call to wake waiting processes after buffer space has opened up. */ void hpsb_iso_wake(struct hpsb_iso *iso); #endif /* IEEE1394_ISO_H */ diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index c5ace19..6a1a057 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -16,6 +16,7 @@ #include #include #include #include +#include #include #include @@ -115,7 +116,7 @@ static int nodemgr_bus_read(struct csr12 static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci) { - return (CSR1212_BE32_TO_CPU(bus_info_data[2]) >> 8) & 0x3; + return (be32_to_cpu(bus_info_data[2]) >> 8) & 0x3; } static struct csr1212_bus_ops nodemgr_csr_ops = { @@ -370,9 +371,7 @@ static ssize_t fw_set_ignore_driver(stru if (state == 1) { ud->ignore_driver = 1; - down_write(&ieee1394_bus_type.subsys.rwsem); device_release_driver(dev); - up_write(&ieee1394_bus_type.subsys.rwsem); } else if (state == 0) ud->ignore_driver = 0; @@ -582,7 +581,7 @@ static void nodemgr_create_drv_files(str goto fail; return; fail: - HPSB_ERR("Failed to add sysfs attribute for driver %s", driver->name); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -606,8 +605,7 @@ static void nodemgr_create_ne_dev_files( goto fail; return; fail: - HPSB_ERR("Failed to add sysfs attribute for node %016Lx", - (unsigned long long)ne->guid); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -621,7 +619,7 @@ static void nodemgr_create_host_dev_file goto fail; return; fail: - HPSB_ERR("Failed to add sysfs attribute for host %d", host->id); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -681,8 +679,7 @@ static void nodemgr_create_ud_dev_files( } return; fail: - HPSB_ERR("Failed to add sysfs attributes for unit %s", - ud->device.bus_id); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -1146,13 +1143,13 @@ static void nodemgr_process_root_directo last_key_id = kv->key.id; } - if (ne->vendor_name_kv && - device_create_file(&ne->device, &dev_attr_ne_vendor_name_kv)) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute for node %016Lx", - (unsigned long long)ne->guid); + if (ne->vendor_name_kv) { + int error = device_create_file(&ne->device, + &dev_attr_ne_vendor_name_kv); + + if (error && error != -EEXIST) + HPSB_ERR("Failed to add sysfs attribute"); + } } #ifdef CONFIG_HOTPLUG @@ -1163,6 +1160,7 @@ static int nodemgr_uevent(struct class_d struct unit_directory *ud; int i = 0; int length = 0; + int retval = 0; /* ieee1394:venNmoNspNverN */ char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1]; @@ -1176,14 +1174,11 @@ static int nodemgr_uevent(struct class_d #define PUT_ENVP(fmt,val) \ do { \ - int printed; \ - envp[i++] = buffer; \ - printed = snprintf(buffer, buffer_size - length, \ - fmt, val); \ - if ((buffer_size - (length+printed) <= 0) || (i >= num_envp)) \ - return -ENOMEM; \ - length += printed+1; \ - buffer += printed+1; \ + retval = add_uevent_var(envp, num_envp, &i, \ + buffer, buffer_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ } while (0) PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id); @@ -1393,12 +1388,10 @@ static void nodemgr_suspend_ne(struct no if (ud->ne != ne) continue; - down_write(&ieee1394_bus_type.subsys.rwsem); if (ud->device.driver && (!ud->device.driver->suspend || ud->device.driver->suspend(&ud->device, PMSG_SUSPEND))) device_release_driver(&ud->device); - up_write(&ieee1394_bus_type.subsys.rwsem); } up(&nodemgr_ud_class.sem); } @@ -1418,10 +1411,8 @@ static void nodemgr_resume_ne(struct nod if (ud->ne != ne) continue; - down_read(&ieee1394_bus_type.subsys.rwsem); if (ud->device.driver && ud->device.driver->resume) ud->device.driver->resume(&ud->device); - up_read(&ieee1394_bus_type.subsys.rwsem); } up(&nodemgr_ud_class.sem); @@ -1442,7 +1433,6 @@ static void nodemgr_update_pdrv(struct n if (ud->ne != ne) continue; - down_write(&ieee1394_bus_type.subsys.rwsem); if (ud->device.driver) { pdrv = container_of(ud->device.driver, struct hpsb_protocol_driver, @@ -1450,7 +1440,6 @@ static void nodemgr_update_pdrv(struct n if (pdrv->update && pdrv->update(ud)) device_release_driver(&ud->device); } - up_write(&ieee1394_bus_type.subsys.rwsem); } up(&nodemgr_ud_class.sem); } @@ -1748,7 +1737,19 @@ exit: return 0; } -int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)) +/** + * nodemgr_for_each_host - call a function for each IEEE 1394 host + * @data: an address to supply to the callback + * @cb: function to call for each host + * + * Iterate the hosts, calling a given function with supplied data for each host. + * If the callback fails on a host, i.e. if it returns a non-zero value, the + * iteration is stopped. + * + * Return value: 0 on success, non-zero on failure (same as returned by last run + * of the callback). + */ +int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) { struct class_device *cdev; struct hpsb_host *host; @@ -1758,7 +1759,7 @@ int nodemgr_for_each_host(void *__data, list_for_each_entry(cdev, &hpsb_host_class.children, node) { host = container_of(cdev, struct hpsb_host, class_dev); - if ((error = cb(host, __data))) + if ((error = cb(host, data))) break; } up(&hpsb_host_class.sem); @@ -1766,7 +1767,7 @@ int nodemgr_for_each_host(void *__data, return error; } -/* The following four convenience functions use a struct node_entry +/* The following two convenience functions use a struct node_entry * for addressing a node on the bus. They are intended for use by any * process context, not just the nodemgr thread, so we need to be a * little careful when reading out the node ID and generation. The @@ -1781,12 +1782,20 @@ int nodemgr_for_each_host(void *__data, * ID's. */ -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt) +/** + * hpsb_node_fill_packet - fill some destination information into a packet + * @ne: destination node + * @packet: packet to fill in + * + * This will fill in the given, pre-initialised hpsb_packet with the current + * information from the node entry (host, node ID, bus generation number). + */ +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet) { - pkt->host = ne->host; - pkt->generation = ne->generation; + packet->host = ne->host; + packet->generation = ne->generation; barrier(); - pkt->node_id = ne->nodeid; + packet->node_id = ne->nodeid; } int hpsb_node_write(struct node_entry *ne, u64 addr, diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h index 4147303..e7ac683 100644 --- a/drivers/ieee1394/nodemgr.h +++ b/drivers/ieee1394/nodemgr.h @@ -153,30 +153,10 @@ static inline int hpsb_node_entry_valid( { return ne->generation == get_hpsb_generation(ne->host); } - -/* - * This will fill in the given, pre-initialised hpsb_packet with the current - * information from the node entry (host, node ID, generation number). It will - * return false if the node owning the GUID is not accessible (and not modify - * the hpsb_packet) and return true otherwise. - * - * Note that packet sending may still fail in hpsb_send_packet if a bus reset - * happens while you are trying to set up the packet (due to obsolete generation - * number). It will at least reliably fail so that you don't accidentally and - * unknowingly send your packet to the wrong node. - */ -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt); - -int hpsb_node_read(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length); +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet); int hpsb_node_write(struct node_entry *ne, u64 addr, quadlet_t *buffer, size_t length); -int hpsb_node_lock(struct node_entry *ne, u64 addr, - int extcode, quadlet_t *data, quadlet_t arg); - -/* Iterate the hosts, calling a given function with supplied data for each - * host. */ -int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)); +int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)); int init_ieee1394_nodemgr(void); void cleanup_ieee1394_nodemgr(void); diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index 06fac0d..5dadfd2 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -507,9 +507,8 @@ static void ohci_initialize(struct ti_oh /* Set up self-id dma buffer */ reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); - /* enable self-id and phys */ - reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID | - OHCI1394_LinkControl_RcvPhyPkt); + /* enable self-id */ + reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID); /* Set the Config ROM mapping register */ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); @@ -518,9 +517,6 @@ static void ohci_initialize(struct ti_oh ohci->max_packet_size = 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); - /* Don't accept phy packets into AR request context */ - reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); - /* Clear the interrupt mask */ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); @@ -617,7 +613,7 @@ #ifdef CONFIG_IEEE1394_VERBOSEDEBUG #endif PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, " - "attempting to setting max_packet_size to 512 bytes"); + "attempting to set max_packet_size to 512 bytes"); reg_write(ohci, OHCI1394_BusOptions, (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002); ohci->max_packet_size = 512; @@ -2377,6 +2373,7 @@ static irqreturn_t ohci_irq_handler(int if (event & OHCI1394_postedWriteErr) { PRINT(KERN_ERR, "physical posted write error"); /* no recovery strategy yet, had to involve protocol drivers */ + event &= ~OHCI1394_postedWriteErr; } if (event & OHCI1394_cycleTooLong) { if(printk_ratelimit()) @@ -3658,6 +3655,7 @@ #endif /* essentially the only purpose of this code is to allow another module to hook into ohci's interrupt handler */ +/* returns zero if successful, one if DMA context is locked up */ int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) { int i=0; diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index fa05f11..f1ad539 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -461,9 +461,7 @@ int ohci1394_register_iso_tasklet(struct struct ohci1394_iso_tasklet *tasklet); void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, struct ohci1394_iso_tasklet *tasklet); - -/* returns zero if successful, one if DMA context is locked up */ -int ohci1394_stop_context (struct ti_ohci *ohci, int reg, char *msg); +int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); struct ti_ohci *ohci1394_get_struct(int card_num); #endif diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index bb897a3..c6aefd9 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -938,7 +938,8 @@ static int handle_async_send(struct file int header_length = req->req.misc & 0xffff; int expect_response = req->req.misc >> 16; - if ((header_length > req->req.length) || (header_length < 12)) { + if (header_length > req->req.length || header_length < 12 || + header_length > FIELD_SIZEOF(struct hpsb_packet, header)) { req->req.error = RAW1394_ERROR_INVALID_ARG; req->req.length = 0; queue_complete_req(req); diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 4edfff4..4cb6fa2 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -59,8 +59,10 @@ #include #include #include #include +#include #include #include +#include #include #include #include @@ -469,19 +471,13 @@ static void sbp2util_write_doorbell(stru static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu) { struct sbp2_fwhost_info *hi = lu->hi; - int i; - unsigned long flags, orbs; struct sbp2_command_info *cmd; + int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS; - orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); for (i = 0; i < orbs; i++) { - cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); - if (!cmd) { - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) return -ENOMEM; - } cmd->command_orb_dma = dma_map_single(hi->host->device.parent, &cmd->command_orb, sizeof(struct sbp2_command_orb), @@ -489,11 +485,10 @@ static int sbp2util_create_command_orb_p cmd->sge_dma = dma_map_single(hi->host->device.parent, &cmd->scatter_gather_element, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); INIT_LIST_HEAD(&cmd->list); list_add_tail(&cmd->list, &lu->cmd_orb_completed); } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); return 0; } @@ -514,7 +509,7 @@ static void sbp2util_remove_command_orb_ DMA_TO_DEVICE); dma_unmap_single(host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); kfree(cmd); } spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); @@ -757,6 +752,11 @@ #ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA SBP2_ERR("failed to register lower 4GB address range"); goto failed_alloc; } +#else + if (dma_set_mask(hi->host->device.parent, DMA_32BIT_MASK)) { + SBP2_ERR("failed to set 4GB DMA mask"); + goto failed_alloc; + } #endif } @@ -865,11 +865,8 @@ static int sbp2_start_device(struct sbp2 if (!lu->login_orb) goto alloc_fail; - if (sbp2util_create_command_orb_pool(lu)) { - SBP2_ERR("sbp2util_create_command_orb_pool failed!"); - sbp2_remove_device(lu); - return -ENOMEM; - } + if (sbp2util_create_command_orb_pool(lu)) + goto alloc_fail; /* Wait a second before trying to log in. Previously logged in * initiators need a chance to reconnect. */ @@ -1628,7 +1625,7 @@ static void sbp2_link_orb_command(struct DMA_TO_DEVICE); dma_sync_single_for_device(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); /* check to see if there are any previous orbs to use */ spin_lock_irqsave(&lu->cmd_orb_lock, flags); @@ -1794,7 +1791,7 @@ static int sbp2_handle_status_write(stru DMA_TO_DEVICE); dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); /* Grab SCSI command pointers and check status. */ /* * FIXME: If the src field in the status is 1, the ORB DMA must @@ -1926,7 +1923,7 @@ static void sbp2scsi_complete_all_comman DMA_TO_DEVICE); dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); sbp2util_mark_command_completed(lu, cmd); if (cmd->Current_SCpnt) { cmd->Current_SCpnt->result = status << 16; @@ -2057,7 +2054,7 @@ static int sbp2scsi_abort(struct scsi_cm dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); sbp2util_mark_command_completed(lu, cmd); if (cmd->Current_SCpnt) { cmd->Current_SCpnt->result = DID_ABORT << 16; diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h index 9ae8423..44402b9 100644 --- a/drivers/ieee1394/sbp2.h +++ b/drivers/ieee1394/sbp2.h @@ -250,15 +250,15 @@ enum sbp2_dma_types { /* Per SCSI command */ struct sbp2_command_info { struct list_head list; - struct sbp2_command_orb command_orb ____cacheline_aligned; - dma_addr_t command_orb_dma ____cacheline_aligned; + struct sbp2_command_orb command_orb; + dma_addr_t command_orb_dma; struct scsi_cmnd *Current_SCpnt; void (*Current_done)(struct scsi_cmnd *); /* Also need s/g structure for each sbp2 command */ struct sbp2_unrestricted_page_table - scatter_gather_element[SG_ALL] ____cacheline_aligned; - dma_addr_t sge_dma ____cacheline_aligned; + scatter_gather_element[SG_ALL] __attribute__((aligned(8))); + dma_addr_t sge_dma; void *sge_buffer; dma_addr_t cmd_dma; enum sbp2_dma_types dma_type; diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 13efd41..6edfecf 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved. + * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved. * Copyright (c) 2005 Intel Corporation. All rights reserved. * Copyright (c) 2005 Mellanox Technologies Ltd. All rights reserved. * @@ -31,7 +31,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: mad.c 5596 2006-03-03 01:00:07Z sean.hefty $ */ #include #include @@ -668,7 +667,7 @@ static void build_smp_wc(struct ib_qp *q static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv, struct ib_mad_send_wr_private *mad_send_wr) { - int ret; + int ret = 0; struct ib_smp *smp = mad_send_wr->send_buf.mad; unsigned long flags; struct ib_mad_local_private *local; @@ -688,14 +687,15 @@ static int handle_outgoing_dr_smp(struct */ if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) == IB_LID_PERMISSIVE && - !smi_handle_dr_smp_send(smp, device->node_type, port_num)) { + smi_handle_dr_smp_send(smp, device->node_type, port_num) == + IB_SMI_DISCARD) { ret = -EINVAL; printk(KERN_ERR PFX "Invalid directed route\n"); goto out; } + /* Check to post send on QP or process locally */ - ret = smi_check_local_smp(smp, device); - if (!ret) + if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD) goto out; local = kmalloc(sizeof *local, GFP_ATOMIC); @@ -1874,18 +1874,22 @@ static void ib_mad_recv_done_handler(str if (recv->mad.mad.mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) { - if (!smi_handle_dr_smp_recv(&recv->mad.smp, - port_priv->device->node_type, - port_priv->port_num, - port_priv->device->phys_port_cnt)) + if (smi_handle_dr_smp_recv(&recv->mad.smp, + port_priv->device->node_type, + port_priv->port_num, + port_priv->device->phys_port_cnt) == + IB_SMI_DISCARD) goto out; - if (!smi_check_forward_dr_smp(&recv->mad.smp)) + + if (smi_check_forward_dr_smp(&recv->mad.smp) == IB_SMI_LOCAL) goto local; - if (!smi_handle_dr_smp_send(&recv->mad.smp, - port_priv->device->node_type, - port_priv->port_num)) + + if (smi_handle_dr_smp_send(&recv->mad.smp, + port_priv->device->node_type, + port_priv->port_num) == IB_SMI_DISCARD) goto out; - if (!smi_check_local_smp(&recv->mad.smp, port_priv->device)) + + if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD) goto out; } diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 68db633..9a7eaad 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -57,6 +57,7 @@ MODULE_LICENSE("Dual BSD/GPL"); struct ib_sa_sm_ah { struct ib_ah *ah; struct kref ref; + u8 src_path_mask; }; struct ib_sa_port { @@ -380,6 +381,7 @@ static void update_sm_ah(struct work_str } kref_init(&new_ah->ref); + new_ah->src_path_mask = (1 << port_attr.lmc) - 1; memset(&ah_attr, 0, sizeof ah_attr); ah_attr.dlid = port_attr.sm_lid; @@ -460,6 +462,25 @@ void ib_sa_cancel_query(int id, struct i } EXPORT_SYMBOL(ib_sa_cancel_query); +static u8 get_src_path_mask(struct ib_device *device, u8 port_num) +{ + struct ib_sa_device *sa_dev; + struct ib_sa_port *port; + unsigned long flags; + u8 src_path_mask; + + sa_dev = ib_get_client_data(device, &sa_client); + if (!sa_dev) + return 0x7f; + + port = &sa_dev->port[port_num - sa_dev->start_port]; + spin_lock_irqsave(&port->ah_lock, flags); + src_path_mask = port->sm_ah ? port->sm_ah->src_path_mask : 0x7f; + spin_unlock_irqrestore(&port->ah_lock, flags); + + return src_path_mask; +} + int ib_init_ah_from_path(struct ib_device *device, u8 port_num, struct ib_sa_path_rec *rec, struct ib_ah_attr *ah_attr) { @@ -469,7 +490,8 @@ int ib_init_ah_from_path(struct ib_devic memset(ah_attr, 0, sizeof *ah_attr); ah_attr->dlid = be16_to_cpu(rec->dlid); ah_attr->sl = rec->sl; - ah_attr->src_path_bits = be16_to_cpu(rec->slid) & 0x7f; + ah_attr->src_path_bits = be16_to_cpu(rec->slid) & + get_src_path_mask(device, port_num); ah_attr->port_num = port_num; ah_attr->static_rate = rec->rate; diff --git a/drivers/infiniband/core/smi.c b/drivers/infiniband/core/smi.c index 54b81e1..2bca753 100644 --- a/drivers/infiniband/core/smi.c +++ b/drivers/infiniband/core/smi.c @@ -3,7 +3,7 @@ * Copyright (c) 2004, 2005 Infinicon Corporation. All rights reserved. * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved. * Copyright (c) 2004, 2005 Topspin Corporation. All rights reserved. - * Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved. + * Copyright (c) 2004-2007 Voltaire Corporation. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -34,7 +34,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: smi.c 1389 2004-12-27 22:56:47Z roland $ */ #include @@ -44,9 +43,8 @@ #include "smi.h" * Fixup a directed route SMP for sending * Return 0 if the SMP should be discarded */ -int smi_handle_dr_smp_send(struct ib_smp *smp, - u8 node_type, - int port_num) +enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp, + u8 node_type, int port_num) { u8 hop_ptr, hop_cnt; @@ -59,18 +57,18 @@ int smi_handle_dr_smp_send(struct ib_smp if (hop_cnt && hop_ptr == 0) { smp->hop_ptr++; return (smp->initial_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:2 */ if (hop_ptr && hop_ptr < hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; /* smp->return_path set when received */ smp->hop_ptr++; return (smp->initial_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:3 -- We're at the end of the DR segment of path */ @@ -78,29 +76,30 @@ int smi_handle_dr_smp_send(struct ib_smp /* smp->return_path set when received */ smp->hop_ptr++; return (node_type == RDMA_NODE_IB_SWITCH || - smp->dr_dlid == IB_LID_PERMISSIVE); + smp->dr_dlid == IB_LID_PERMISSIVE ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */ /* C14-9:5 -- Fail unreasonable hop pointer */ - return (hop_ptr == hop_cnt + 1); + return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD); } else { /* C14-13:1 */ if (hop_cnt && hop_ptr == hop_cnt + 1) { smp->hop_ptr--; return (smp->return_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:2 */ if (2 <= hop_ptr && hop_ptr <= hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; smp->hop_ptr--; return (smp->return_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:3 -- at the end of the DR segment of path */ @@ -108,15 +107,16 @@ int smi_handle_dr_smp_send(struct ib_smp smp->hop_ptr--; /* C14-13:3 -- SMPs destined for SM shouldn't be here */ return (node_type == RDMA_NODE_IB_SWITCH || - smp->dr_slid == IB_LID_PERMISSIVE); + smp->dr_slid == IB_LID_PERMISSIVE ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */ if (hop_ptr == 0) - return 1; + return IB_SMI_HANDLE; /* C14-13:5 -- Check for unreasonable hop pointer */ - return 0; + return IB_SMI_DISCARD; } } @@ -124,10 +124,8 @@ int smi_handle_dr_smp_send(struct ib_smp * Adjust information for a received SMP * Return 0 if the SMP should be dropped */ -int smi_handle_dr_smp_recv(struct ib_smp *smp, - u8 node_type, - int port_num, - int phys_port_cnt) +enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type, + int port_num, int phys_port_cnt) { u8 hop_ptr, hop_cnt; @@ -138,16 +136,17 @@ int smi_handle_dr_smp_recv(struct ib_smp if (!ib_get_smp_direction(smp)) { /* C14-9:1 -- sender should have incremented hop_ptr */ if (hop_cnt && hop_ptr == 0) - return 0; + return IB_SMI_DISCARD; /* C14-9:2 -- intermediate hop */ if (hop_ptr && hop_ptr < hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; smp->return_path[hop_ptr] = port_num; /* smp->hop_ptr updated when sending */ - return (smp->initial_path[hop_ptr+1] <= phys_port_cnt); + return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:3 -- We're at the end of the DR segment of path */ @@ -157,12 +156,13 @@ int smi_handle_dr_smp_recv(struct ib_smp /* smp->hop_ptr updated when sending */ return (node_type == RDMA_NODE_IB_SWITCH || - smp->dr_dlid == IB_LID_PERMISSIVE); + smp->dr_dlid == IB_LID_PERMISSIVE ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */ /* C14-9:5 -- fail unreasonable hop pointer */ - return (hop_ptr == hop_cnt + 1); + return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD); } else { @@ -170,16 +170,17 @@ int smi_handle_dr_smp_recv(struct ib_smp if (hop_cnt && hop_ptr == hop_cnt + 1) { smp->hop_ptr--; return (smp->return_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:2 */ if (2 <= hop_ptr && hop_ptr <= hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; /* smp->hop_ptr updated when sending */ - return (smp->return_path[hop_ptr-1] <= phys_port_cnt); + return (smp->return_path[hop_ptr-1] <= phys_port_cnt ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:3 -- We're at the end of the DR segment of path */ @@ -187,23 +188,20 @@ int smi_handle_dr_smp_recv(struct ib_smp if (smp->dr_slid == IB_LID_PERMISSIVE) { /* giving SMP to SM - update hop_ptr */ smp->hop_ptr--; - return 1; + return IB_SMI_HANDLE; } /* smp->hop_ptr updated when sending */ - return (node_type == RDMA_NODE_IB_SWITCH); + return (node_type == RDMA_NODE_IB_SWITCH ? + IB_SMI_HANDLE: IB_SMI_DISCARD); } /* C14-13:4 -- hop_ptr = 0 -> give to SM */ /* C14-13:5 -- Check for unreasonable hop pointer */ - return (hop_ptr == 0); + return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD); } } -/* - * Return 1 if the received DR SMP should be forwarded to the send queue - * Return 0 if the SMP should be completed up the stack - */ -int smi_check_forward_dr_smp(struct ib_smp *smp) +enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp) { u8 hop_ptr, hop_cnt; @@ -213,23 +211,25 @@ int smi_check_forward_dr_smp(struct ib_s if (!ib_get_smp_direction(smp)) { /* C14-9:2 -- intermediate hop */ if (hop_ptr && hop_ptr < hop_cnt) - return 1; + return IB_SMI_SEND; /* C14-9:3 -- at the end of the DR segment of path */ if (hop_ptr == hop_cnt) - return (smp->dr_dlid == IB_LID_PERMISSIVE); + return (smp->dr_dlid == IB_LID_PERMISSIVE ? + IB_SMI_SEND : IB_SMI_LOCAL); /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */ if (hop_ptr == hop_cnt + 1) - return 1; + return IB_SMI_SEND; } else { - /* C14-13:2 */ + /* C14-13:2 -- intermediate hop */ if (2 <= hop_ptr && hop_ptr <= hop_cnt) - return 1; + return IB_SMI_SEND; /* C14-13:3 -- at the end of the DR segment of path */ if (hop_ptr == 1) - return (smp->dr_slid != IB_LID_PERMISSIVE); + return (smp->dr_slid != IB_LID_PERMISSIVE ? + IB_SMI_SEND : IB_SMI_LOCAL); } - return 0; + return IB_SMI_LOCAL; } diff --git a/drivers/infiniband/core/smi.h b/drivers/infiniband/core/smi.h index 3011bfd..9a4b349 100644 --- a/drivers/infiniband/core/smi.h +++ b/drivers/infiniband/core/smi.h @@ -3,7 +3,7 @@ * Copyright (c) 2004 Infinicon Corporation. All rights reserved. * Copyright (c) 2004 Intel Corporation. All rights reserved. * Copyright (c) 2004 Topspin Corporation. All rights reserved. - * Copyright (c) 2004 Voltaire Corporation. All rights reserved. + * Copyright (c) 2004-2007 Voltaire Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -33,7 +33,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: smi.h 1389 2004-12-27 22:56:47Z roland $ */ #ifndef __SMI_H_ @@ -41,26 +40,33 @@ #define __SMI_H_ #include -int smi_handle_dr_smp_recv(struct ib_smp *smp, - u8 node_type, - int port_num, - int phys_port_cnt); -extern int smi_check_forward_dr_smp(struct ib_smp *smp); -extern int smi_handle_dr_smp_send(struct ib_smp *smp, - u8 node_type, - int port_num); +enum smi_action { + IB_SMI_DISCARD, + IB_SMI_HANDLE +}; + +enum smi_forward_action { + IB_SMI_LOCAL, /* SMP should be completed up the stack */ + IB_SMI_SEND, /* received DR SMP should be forwarded to the send queue */ +}; + +enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type, + int port_num, int phys_port_cnt); +extern enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp); +extern enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp, + u8 node_type, int port_num); /* * Return 1 if the SMP should be handled by the local SMA/SM via process_mad */ -static inline int smi_check_local_smp(struct ib_smp *smp, - struct ib_device *device) +static inline enum smi_action smi_check_local_smp(struct ib_smp *smp, + struct ib_device *device) { /* C14-9:3 -- We're at the end of the DR segment of path */ /* C14-9:4 -- Hop Pointer = Hop Count + 1 -> give to SMA/SM */ return ((device->process_mad && !ib_get_smp_direction(smp) && - (smp->hop_ptr == smp->hop_cnt + 1))); + (smp->hop_ptr == smp->hop_cnt + 1)) ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } - #endif /* __SMI_H_ */ diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 000c086..08c299e 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -683,6 +683,7 @@ int ib_device_register_sysfs(struct ib_d class_dev->class = &ib_class; class_dev->class_data = device; + class_dev->dev = device->dma_device; strlcpy(class_dev->class_id, device->name, BUS_ID_SIZE); INIT_LIST_HEAD(&device->port_list); diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index ee51d79..2586a3e 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -407,29 +407,18 @@ static ssize_t ib_ucm_event(struct ib_uc mutex_lock(&file->file_mutex); while (list_empty(&file->events)) { + mutex_unlock(&file->file_mutex); - if (file->filp->f_flags & O_NONBLOCK) { - result = -EAGAIN; - break; - } + if (file->filp->f_flags & O_NONBLOCK) + return -EAGAIN; - if (signal_pending(current)) { - result = -ERESTARTSYS; - break; - } + if (wait_event_interruptible(file->poll_wait, + !list_empty(&file->events))) + return -ERESTARTSYS; - prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE); - - mutex_unlock(&file->file_mutex); - schedule(); mutex_lock(&file->file_mutex); - - finish_wait(&file->poll_wait, &wait); } - if (result) - goto done; - uevent = list_entry(file->events.next, struct ib_ucm_event, file_list); if (ib_ucm_new_cm_id(uevent->resp.event)) { diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index c859134..53b4c94 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -306,26 +306,18 @@ static ssize_t ucma_get_event(struct ucm mutex_lock(&file->mut); while (list_empty(&file->event_list)) { - if (file->filp->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - break; - } + mutex_unlock(&file->mut); - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } + if (file->filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(file->poll_wait, + !list_empty(&file->event_list))) + return -ERESTARTSYS; - prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE); - mutex_unlock(&file->mut); - schedule(); mutex_lock(&file->mut); - finish_wait(&file->poll_wait, &wait); } - if (ret) - goto done; - uevent = list_entry(file->event_list.next, struct ucma_event, list); if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) { diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index c069ebe..8199b83 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -135,7 +135,7 @@ static const dev_t base_dev = MKDEV(IB_U static DEFINE_SPINLOCK(port_lock); static struct ib_umad_port *umad_port[IB_UMAD_MAX_PORTS]; -static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS * 2); +static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS); static void ib_umad_add_one(struct ib_device *device); static void ib_umad_remove_one(struct ib_device *device); @@ -231,12 +231,17 @@ static void recv_handler(struct ib_mad_a packet->mad.hdr.path_bits = mad_recv_wc->wc->dlid_path_bits; packet->mad.hdr.grh_present = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH); if (packet->mad.hdr.grh_present) { - /* XXX parse GRH */ - packet->mad.hdr.gid_index = 0; - packet->mad.hdr.hop_limit = 0; - packet->mad.hdr.traffic_class = 0; - memset(packet->mad.hdr.gid, 0, 16); - packet->mad.hdr.flow_label = 0; + struct ib_ah_attr ah_attr; + + ib_init_ah_from_wc(agent->device, agent->port_num, + mad_recv_wc->wc, mad_recv_wc->recv_buf.grh, + &ah_attr); + + packet->mad.hdr.gid_index = ah_attr.grh.sgid_index; + packet->mad.hdr.hop_limit = ah_attr.grh.hop_limit; + packet->mad.hdr.traffic_class = ah_attr.grh.traffic_class; + memcpy(packet->mad.hdr.gid, &ah_attr.grh.dgid, 16); + packet->mad.hdr.flow_label = cpu_to_be32(ah_attr.grh.flow_label); } if (queue_packet(file, agent, packet)) @@ -473,6 +478,7 @@ static ssize_t ib_umad_write(struct file if (packet->mad.hdr.grh_present) { ah_attr.ah_flags = IB_AH_GRH; memcpy(ah_attr.grh.dgid.raw, packet->mad.hdr.gid, 16); + ah_attr.grh.sgid_index = packet->mad.hdr.gid_index; ah_attr.grh.flow_label = be32_to_cpu(packet->mad.hdr.flow_label); ah_attr.grh.hop_limit = packet->mad.hdr.hop_limit; ah_attr.grh.traffic_class = packet->mad.hdr.traffic_class; diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c index 59243d9..58bc272 100644 --- a/drivers/infiniband/hw/amso1100/c2.c +++ b/drivers/infiniband/hw/amso1100/c2.c @@ -439,7 +439,8 @@ static void c2_rx_error(struct c2_port * } /* Setup the skb for reuse since we're dropping this pkt */ - elem->skb->tail = elem->skb->data = elem->skb->head; + elem->skb->data = elem->skb->head; + skb_reset_tail_pointer(elem->skb); /* Zero out the rxp hdr in the sk_buff */ memset(elem->skb->data, 0, sizeof(*rxp_hdr)); @@ -521,9 +522,8 @@ static void c2_rx_interrupt(struct net_d * "sizeof(struct c2_rxp_hdr)". */ skb->data += sizeof(*rxp_hdr); - skb->tail = skb->data + buflen; + skb_set_tail_pointer(skb, buflen); skb->len = buflen; - skb->dev = netdev; skb->protocol = eth_type_trans(skb, netdev); netif_rx(skb); diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c index fef9727..607c09b 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.c +++ b/drivers/infiniband/hw/amso1100/c2_provider.c @@ -796,7 +796,6 @@ int c2_register_device(struct c2_dev *de memcpy(&dev->ibdev.node_guid, dev->pseudo_netdev->dev_addr, 6); dev->ibdev.phys_port_cnt = 1; dev->ibdev.dma_device = &dev->pcidev->dev; - dev->ibdev.class_dev.dev = &dev->pcidev->dev; dev->ibdev.query_device = c2_query_device; dev->ibdev.query_port = c2_query_port; dev->ibdev.modify_port = c2_modify_port; diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 2d2de9b..3b4b0ac 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -477,7 +477,7 @@ static void send_mpa_req(struct iwch_ep BUG_ON(skb_cloned(skb)); mpalen = sizeof(*mpa) + ep->plen; - if (skb->data + mpalen + sizeof(*req) > skb->end) { + if (skb->data + mpalen + sizeof(*req) > skb_end_pointer(skb)) { kfree_skb(skb); skb=alloc_skb(mpalen + sizeof(*req), GFP_KERNEL); if (!skb) { @@ -507,7 +507,7 @@ static void send_mpa_req(struct iwch_ep */ skb_get(skb); set_arp_failure_handler(skb, arp_failure_discard); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); len = skb->len; req = (struct tx_data_wr *) skb_push(skb, sizeof(*req)); req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA)); @@ -559,7 +559,7 @@ static int send_mpa_reject(struct iwch_e skb_get(skb); skb->priority = CPL_PRIORITY_DATA; set_arp_failure_handler(skb, arp_failure_discard); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); req = (struct tx_data_wr *) skb_push(skb, sizeof(*req)); req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA)); req->wr_lo = htonl(V_WR_TID(ep->hwtid)); @@ -610,7 +610,7 @@ static int send_mpa_reply(struct iwch_ep */ skb_get(skb); set_arp_failure_handler(skb, arp_failure_discard); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); len = skb->len; req = (struct tx_data_wr *) skb_push(skb, sizeof(*req)); req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA)); @@ -821,7 +821,8 @@ static void process_mpa_reply(struct iwc /* * copy the new data into our accumulation buffer. */ - memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len); + skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]), + skb->len); ep->mpa_pkt_len += skb->len; /* @@ -940,7 +941,8 @@ static void process_mpa_request(struct i /* * Copy the new data into our accumulation buffer. */ - memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len); + skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]), + skb->len); ep->mpa_pkt_len += skb->len; /* @@ -1619,7 +1621,8 @@ static int terminate(struct t3cdev *tdev PDBG("%s ep %p\n", __FUNCTION__, ep); skb_pull(skb, sizeof(struct cpl_rdma_terminate)); PDBG("%s saving %d bytes of term msg\n", __FUNCTION__, skb->len); - memcpy(ep->com.qp->attr.terminate_buffer, skb->data, skb->len); + skb_copy_from_linear_data(skb, ep->com.qp->attr.terminate_buffer, + skb->len); ep->com.qp->attr.terminate_msg_len = skb->len; ep->com.qp->attr.is_terminate_local = 0; return CPL_RET_BUF_DONE; diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 24e0df0..af28a31 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1108,7 +1108,6 @@ int iwch_register_device(struct iwch_dev memcpy(dev->ibdev.node_desc, IWCH_NODE_DESC, sizeof(IWCH_NODE_DESC)); dev->ibdev.phys_port_cnt = dev->rdev.port_info.nports; dev->ibdev.dma_device = &(dev->rdev.rnic_info.pdev->dev); - dev->ibdev.class_dev.dev = &(dev->rdev.rnic_info.pdev->dev); dev->ibdev.query_device = iwch_query_device; dev->ibdev.query_port = iwch_query_port; dev->ibdev.modify_port = iwch_modify_port; diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h index 82ded44..10fb8fb 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/infiniband/hw/ehca/ehca_classes.h @@ -106,6 +106,7 @@ struct ehca_shca { struct ehca_mr *maxmr; struct ehca_pd *pd; struct h_galpas galpas; + struct mutex modify_mutex; }; struct ehca_pd { diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c index 30eb45d..32b55a4 100644 --- a/drivers/infiniband/hw/ehca/ehca_hca.c +++ b/drivers/infiniband/hw/ehca/ehca_hca.c @@ -147,6 +147,7 @@ int ehca_query_port(struct ib_device *ib break; } + props->port_cap_flags = rblock->capability_mask; props->gid_tbl_len = rblock->gid_tbl_len; props->max_msg_sz = rblock->max_msg_sz; props->bad_pkey_cntr = rblock->bad_pkey_cntr; @@ -236,10 +237,60 @@ query_gid1: return ret; } +const u32 allowed_port_caps = ( + IB_PORT_SM | IB_PORT_LED_INFO_SUP | IB_PORT_CM_SUP | + IB_PORT_SNMP_TUNNEL_SUP | IB_PORT_DEVICE_MGMT_SUP | + IB_PORT_VENDOR_CLASS_SUP); + int ehca_modify_port(struct ib_device *ibdev, u8 port, int port_modify_mask, struct ib_port_modify *props) { - /* Not implemented yet */ - return -EFAULT; + int ret = 0; + struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device); + struct hipz_query_port *rblock; + u32 cap; + u64 hret; + + if ((props->set_port_cap_mask | props->clr_port_cap_mask) + & ~allowed_port_caps) { + ehca_err(&shca->ib_device, "Non-changeable bits set in masks " + "set=%x clr=%x allowed=%x", props->set_port_cap_mask, + props->clr_port_cap_mask, allowed_port_caps); + return -EINVAL; + } + + if (mutex_lock_interruptible(&shca->modify_mutex)) + return -ERESTARTSYS; + + rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL); + if (!rblock) { + ehca_err(&shca->ib_device, "Can't allocate rblock memory."); + ret = -ENOMEM; + goto modify_port1; + } + + if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) { + ehca_err(&shca->ib_device, "Can't query port properties"); + ret = -EINVAL; + goto modify_port2; + } + + cap = (rblock->capability_mask | props->set_port_cap_mask) + & ~props->clr_port_cap_mask; + + hret = hipz_h_modify_port(shca->ipz_hca_handle, port, + cap, props->init_type, port_modify_mask); + if (hret != H_SUCCESS) { + ehca_err(&shca->ib_device, "Modify port failed hret=%lx", hret); + ret = -EINVAL; + } + +modify_port2: + ehca_free_fw_ctrlblock(rblock); + +modify_port1: + mutex_unlock(&shca->modify_mutex); + + return ret; } diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index 059da96..4700085 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -565,11 +565,11 @@ static int __devinit ehca_probe(struct i const struct of_device_id *id) { struct ehca_shca *shca; - u64 *handle; + const u64 *handle; struct ib_pd *ibpd; int ret; - handle = (u64 *)get_property(dev->ofdev.node, "ibm,hca-handle", NULL); + handle = get_property(dev->ofdev.node, "ibm,hca-handle", NULL); if (!handle) { ehca_gen_err("Cannot get eHCA handle for adapter: %s.", dev->ofdev.node->full_name); @@ -587,6 +587,7 @@ static int __devinit ehca_probe(struct i ehca_gen_err("Cannot allocate shca memory."); return -ENOMEM; } + mutex_init(&shca->modify_mutex); shca->ibmebus_dev = dev; shca->ipz_hca_handle.handle = *handle; diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c index 3fb46e6..b564fcd 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.c +++ b/drivers/infiniband/hw/ehca/hcp_if.c @@ -70,6 +70,10 @@ #define H_ALL_RES_QP_ACT_RECV_SGE #define H_ALL_RES_QP_SQUEUE_SIZE_PAGES EHCA_BMASK_IBM(0, 31) #define H_ALL_RES_QP_RQUEUE_SIZE_PAGES EHCA_BMASK_IBM(32, 63) +#define H_MP_INIT_TYPE EHCA_BMASK_IBM(44, 47) +#define H_MP_SHUTDOWN EHCA_BMASK_IBM(48, 48) +#define H_MP_RESET_QKEY_CTR EHCA_BMASK_IBM(49, 49) + /* direct access qp controls */ #define DAQP_CTRL_ENABLE 0x01 #define DAQP_CTRL_SEND_COMP 0x20 @@ -364,6 +368,26 @@ u64 hipz_h_query_port(const struct ipz_a return ret; } +u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle, + const u8 port_id, const u32 port_cap, + const u8 init_type, const int modify_mask) +{ + u64 port_attributes = port_cap; + + if (modify_mask & IB_PORT_SHUTDOWN) + port_attributes |= EHCA_BMASK_SET(H_MP_SHUTDOWN, 1); + if (modify_mask & IB_PORT_INIT_TYPE) + port_attributes |= EHCA_BMASK_SET(H_MP_INIT_TYPE, init_type); + if (modify_mask & IB_PORT_RESET_QKEY_CNTR) + port_attributes |= EHCA_BMASK_SET(H_MP_RESET_QKEY_CTR, 1); + + return ehca_plpar_hcall_norets(H_MODIFY_PORT, + adapter_handle.handle, /* r4 */ + port_id, /* r5 */ + port_attributes, /* r6 */ + 0, 0, 0, 0); +} + u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle, struct hipz_query_hca *query_hca_rblock) { diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/infiniband/hw/ehca/hcp_if.h index 587ebd4..2869f7d 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.h +++ b/drivers/infiniband/hw/ehca/hcp_if.h @@ -85,6 +85,10 @@ u64 hipz_h_query_port(const struct ipz_a const u8 port_id, struct hipz_query_port *query_port_response_block); +u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle, + const u8 port_id, const u32 port_cap, + const u8 init_type, const int modify_mask); + u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle, struct hipz_query_hca *query_hca_rblock); diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h index 54139d3..10c008f 100644 --- a/drivers/infiniband/hw/ipath/ipath_common.h +++ b/drivers/infiniband/hw/ipath/ipath_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 QLogic, Inc. All rights reserved. + * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -78,6 +78,8 @@ #define IPATH_IB_LINKACTIVE 2 #define IPATH_IB_LINKINIT 3 #define IPATH_IB_LINKDOWN_SLEEP 4 #define IPATH_IB_LINKDOWN_DISABLE 5 +#define IPATH_IB_LINK_LOOPBACK 6 /* enable local loopback */ +#define IPATH_IB_LINK_EXTERNAL 7 /* normal, disable local loopback */ /* * stats maintained by the driver. For now, at least, this is global @@ -316,11 +318,17 @@ struct ipath_base_info { /* address of readonly memory copy of the rcvhdrq tail register. */ __u64 spi_rcvhdr_tailaddr; - /* shared memory pages for subports if IPATH_RUNTIME_MASTER is set */ + /* shared memory pages for subports if port is shared */ __u64 spi_subport_uregbase; __u64 spi_subport_rcvegrbuf; __u64 spi_subport_rcvhdr_base; + /* shared memory page for hardware port if it is shared */ + __u64 spi_port_uregbase; + __u64 spi_port_rcvegrbuf; + __u64 spi_port_rcvhdr_base; + __u64 spi_port_rcvhdr_tailaddr; + } __attribute__ ((aligned(8))); @@ -344,7 +352,7 @@ #define IPATH_USER_SWMAJOR 1 * may not be implemented; the user code must deal with this if it * cares, or it must abort after initialization reports the difference. */ -#define IPATH_USER_SWMINOR 3 +#define IPATH_USER_SWMINOR 5 #define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR) @@ -418,11 +426,14 @@ #define IPATH_CMD_RECV_CTRL 18 /* contro #define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */ #define IPATH_CMD_TID_FREE 20 /* free expected TID entries */ #define IPATH_CMD_SET_PART_KEY 21 /* add partition key */ -#define IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes */ +#define __IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes (for old user code) */ #define IPATH_CMD_ASSIGN_PORT 23 /* allocate HCA and port */ #define IPATH_CMD_USER_INIT 24 /* set up userspace */ +#define IPATH_CMD_UNUSED_1 25 +#define IPATH_CMD_UNUSED_2 26 +#define IPATH_CMD_PIOAVAILUPD 27 /* force an update of PIOAvail reg */ -#define IPATH_CMD_MAX 24 +#define IPATH_CMD_MAX 27 struct ipath_port_info { __u32 num_active; /* number of active units */ @@ -430,7 +441,7 @@ struct ipath_port_info { __u16 port; /* port on unit assigned to caller */ __u16 subport; /* subport on unit assigned to caller */ __u16 num_ports; /* number of ports available on unit */ - __u16 num_subports; /* number of subport slaves opened on port */ + __u16 num_subports; /* number of subports opened on port */ }; struct ipath_tid_info { diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c index 87462e0..ea78e6d 100644 --- a/drivers/infiniband/hw/ipath/ipath_cq.c +++ b/drivers/infiniband/hw/ipath/ipath_cq.c @@ -76,7 +76,20 @@ void ipath_cq_enter(struct ipath_cq *cq, } return; } - wc->queue[head] = *entry; + wc->queue[head].wr_id = entry->wr_id; + wc->queue[head].status = entry->status; + wc->queue[head].opcode = entry->opcode; + wc->queue[head].vendor_err = entry->vendor_err; + wc->queue[head].byte_len = entry->byte_len; + wc->queue[head].imm_data = (__u32 __force)entry->imm_data; + wc->queue[head].qp_num = entry->qp->qp_num; + wc->queue[head].src_qp = entry->src_qp; + wc->queue[head].wc_flags = entry->wc_flags; + wc->queue[head].pkey_index = entry->pkey_index; + wc->queue[head].slid = entry->slid; + wc->queue[head].sl = entry->sl; + wc->queue[head].dlid_path_bits = entry->dlid_path_bits; + wc->queue[head].port_num = entry->port_num; wc->head = next; if (cq->notify == IB_CQ_NEXT_COMP || @@ -122,9 +135,30 @@ int ipath_poll_cq(struct ib_cq *ibcq, in if (tail > (u32) cq->ibcq.cqe) tail = (u32) cq->ibcq.cqe; for (npolled = 0; npolled < num_entries; ++npolled, ++entry) { + struct ipath_qp *qp; + if (tail == wc->head) break; - *entry = wc->queue[tail]; + + qp = ipath_lookup_qpn(&to_idev(cq->ibcq.device)->qp_table, + wc->queue[tail].qp_num); + entry->qp = &qp->ibqp; + if (atomic_dec_and_test(&qp->refcount)) + wake_up(&qp->wait); + + entry->wr_id = wc->queue[tail].wr_id; + entry->status = wc->queue[tail].status; + entry->opcode = wc->queue[tail].opcode; + entry->vendor_err = wc->queue[tail].vendor_err; + entry->byte_len = wc->queue[tail].byte_len; + entry->imm_data = wc->queue[tail].imm_data; + entry->src_qp = wc->queue[tail].src_qp; + entry->wc_flags = wc->queue[tail].wc_flags; + entry->pkey_index = wc->queue[tail].pkey_index; + entry->slid = wc->queue[tail].slid; + entry->sl = wc->queue[tail].sl; + entry->dlid_path_bits = wc->queue[tail].dlid_path_bits; + entry->port_num = wc->queue[tail].port_num; if (tail >= cq->ibcq.cqe) tail = 0; else diff --git a/drivers/infiniband/hw/ipath/ipath_debug.h b/drivers/infiniband/hw/ipath/ipath_debug.h index df69f0d..42bfbdb 100644 --- a/drivers/infiniband/hw/ipath/ipath_debug.h +++ b/drivers/infiniband/hw/ipath/ipath_debug.h @@ -57,6 +57,7 @@ #define __IPATH_PKTDBG 0x80 /* prin #define __IPATH_PROCDBG 0x100 /* print mmap/nopage stuff, not using VDBG any more */ #define __IPATH_MMDBG 0x200 +#define __IPATH_ERRPKTDBG 0x400 #define __IPATH_USER_SEND 0x1000 /* use user mode send */ #define __IPATH_KERNEL_SEND 0x2000 /* use kernel mode send */ #define __IPATH_EPKTDBG 0x4000 /* print ethernet packet data */ diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c index 0f13a21..63e8368 100644 --- a/drivers/infiniband/hw/ipath/ipath_diag.c +++ b/drivers/infiniband/hw/ipath/ipath_diag.c @@ -296,7 +296,7 @@ static int ipath_diag_open(struct inode } fp->private_data = dd; - ipath_diag_inuse = 1; + ipath_diag_inuse = -2; diag_set_link = 0; ret = 0; @@ -461,6 +461,8 @@ static ssize_t ipath_diag_read(struct fi else if ((count % 4) || (*off % 4)) /* address or length is not 32-bit aligned, hence invalid */ ret = -EINVAL; + else if (ipath_diag_inuse < 1 && (*off || count != 8)) + ret = -EINVAL; /* prevent cat /dev/ipath_diag* */ else if ((count % 8) || (*off % 8)) /* address or length not 64-bit aligned; do 32-bit reads */ ret = ipath_read_umem32(dd, data, kreg_base + *off, count); @@ -470,6 +472,8 @@ static ssize_t ipath_diag_read(struct fi if (ret >= 0) { *off += count; ret = count; + if (ipath_diag_inuse == -2) + ipath_diag_inuse++; } return ret; @@ -489,6 +493,9 @@ static ssize_t ipath_diag_write(struct f else if ((count % 4) || (*off % 4)) /* address or length is not 32-bit aligned, hence invalid */ ret = -EINVAL; + else if ((ipath_diag_inuse == -1 && (*off || count != 8)) || + ipath_diag_inuse == -2) /* read qw off 0, write qw off 0 */ + ret = -EINVAL; /* before any other write allowed */ else if ((count % 8) || (*off % 8)) /* address or length not 64-bit aligned; do 32-bit writes */ ret = ipath_write_umem32(dd, kreg_base + *off, data, count); @@ -498,6 +505,8 @@ static ssize_t ipath_diag_write(struct f if (ret >= 0) { *off += count; ret = count; + if (ipath_diag_inuse == -1) + ipath_diag_inuse = 1; /* all read/write OK now */ } return ret; diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index ae7f21a..e3a2232 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -390,15 +390,23 @@ static int __devinit ipath_init_one(stru /* setup the chip-specific functions, as early as possible. */ switch (ent->device) { -#ifdef CONFIG_HT_IRQ case PCI_DEVICE_ID_INFINIPATH_HT: +#ifdef CONFIG_HT_IRQ ipath_init_iba6110_funcs(dd); break; +#else + ipath_dev_err(dd, "QLogic HT device 0x%x cannot work if " + "CONFIG_HT_IRQ is not enabled\n", ent->device); + return -ENODEV; #endif -#ifdef CONFIG_PCI_MSI case PCI_DEVICE_ID_INFINIPATH_PE800: +#ifdef CONFIG_PCI_MSI ipath_init_iba6120_funcs(dd); break; +#else + ipath_dev_err(dd, "QLogic PCIE device 0x%x cannot work if " + "CONFIG_PCI_MSI is not enabled\n", ent->device); + return -ENODEV; #endif default: ipath_dev_err(dd, "Found unknown QLogic deviceid 0x%x, " @@ -486,7 +494,7 @@ #endif ret = ipath_init_chip(dd, 0); /* do the chip-specific init */ if (ret) - goto bail_iounmap; + goto bail_irqsetup; ret = ipath_enable_wc(dd); @@ -505,6 +513,9 @@ #endif goto bail; +bail_irqsetup: + if (pdev->irq) free_irq(pdev->irq, dd); + bail_iounmap: iounmap((volatile void __iomem *) dd->ipath_kregbase); @@ -525,8 +536,6 @@ static void __devexit cleanup_device(str { int port; - ipath_shutdown_device(dd); - if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { /* can't do anything more with chip; needs re-init */ *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT; @@ -594,8 +603,9 @@ static void __devexit cleanup_device(str ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n", dd->ipath_pageshadow); - vfree(dd->ipath_pageshadow); + tmpp = dd->ipath_pageshadow; dd->ipath_pageshadow = NULL; + vfree(tmpp); } /* @@ -622,6 +632,12 @@ static void __devexit ipath_remove_one(s ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd); + /* + * disable the IB link early, to be sure no new packets arrive, which + * complicates the shutdown process + */ + ipath_shutdown_device(dd); + if (dd->verbs_dev) ipath_unregister_ib_device(dd->verbs_dev); @@ -754,9 +770,42 @@ static int ipath_wait_linkstate(struct i return (dd->ipath_flags & state) ? 0 : -ETIMEDOUT; } -void ipath_decode_err(char *buf, size_t blen, ipath_err_t err) +/* + * Decode the error status into strings, deciding whether to always + * print * it or not depending on "normal packet errors" vs everything + * else. Return 1 if "real" errors, otherwise 0 if only packet + * errors, so caller can decide what to print with the string. + */ +int ipath_decode_err(char *buf, size_t blen, ipath_err_t err) { + int iserr = 1; *buf = '\0'; + if (err & INFINIPATH_E_PKTERRS) { + if (!(err & ~INFINIPATH_E_PKTERRS)) + iserr = 0; // if only packet errors. + if (ipath_debug & __IPATH_ERRPKTDBG) { + if (err & INFINIPATH_E_REBP) + strlcat(buf, "EBP ", blen); + if (err & INFINIPATH_E_RVCRC) + strlcat(buf, "VCRC ", blen); + if (err & INFINIPATH_E_RICRC) { + strlcat(buf, "CRC ", blen); + // clear for check below, so only once + err &= INFINIPATH_E_RICRC; + } + if (err & INFINIPATH_E_RSHORTPKTLEN) + strlcat(buf, "rshortpktlen ", blen); + if (err & INFINIPATH_E_SDROPPEDDATAPKT) + strlcat(buf, "sdroppeddatapkt ", blen); + if (err & INFINIPATH_E_SPKTLEN) + strlcat(buf, "spktlen ", blen); + } + if ((err & INFINIPATH_E_RICRC) && + !(err&(INFINIPATH_E_RVCRC|INFINIPATH_E_REBP))) + strlcat(buf, "CRC ", blen); + if (!iserr) + goto done; + } if (err & INFINIPATH_E_RHDRLEN) strlcat(buf, "rhdrlen ", blen); if (err & INFINIPATH_E_RBADTID) @@ -767,12 +816,12 @@ void ipath_decode_err(char *buf, size_t strlcat(buf, "rhdr ", blen); if (err & INFINIPATH_E_RLONGPKTLEN) strlcat(buf, "rlongpktlen ", blen); - if (err & INFINIPATH_E_RSHORTPKTLEN) - strlcat(buf, "rshortpktlen ", blen); if (err & INFINIPATH_E_RMAXPKTLEN) strlcat(buf, "rmaxpktlen ", blen); if (err & INFINIPATH_E_RMINPKTLEN) strlcat(buf, "rminpktlen ", blen); + if (err & INFINIPATH_E_SMINPKTLEN) + strlcat(buf, "sminpktlen ", blen); if (err & INFINIPATH_E_RFORMATERR) strlcat(buf, "rformaterr ", blen); if (err & INFINIPATH_E_RUNSUPVL) @@ -781,32 +830,20 @@ void ipath_decode_err(char *buf, size_t strlcat(buf, "runexpchar ", blen); if (err & INFINIPATH_E_RIBFLOW) strlcat(buf, "ribflow ", blen); - if (err & INFINIPATH_E_REBP) - strlcat(buf, "EBP ", blen); if (err & INFINIPATH_E_SUNDERRUN) strlcat(buf, "sunderrun ", blen); if (err & INFINIPATH_E_SPIOARMLAUNCH) strlcat(buf, "spioarmlaunch ", blen); if (err & INFINIPATH_E_SUNEXPERRPKTNUM) strlcat(buf, "sunexperrpktnum ", blen); - if (err & INFINIPATH_E_SDROPPEDDATAPKT) - strlcat(buf, "sdroppeddatapkt ", blen); if (err & INFINIPATH_E_SDROPPEDSMPPKT) strlcat(buf, "sdroppedsmppkt ", blen); if (err & INFINIPATH_E_SMAXPKTLEN) strlcat(buf, "smaxpktlen ", blen); - if (err & INFINIPATH_E_SMINPKTLEN) - strlcat(buf, "sminpktlen ", blen); if (err & INFINIPATH_E_SUNSUPVL) strlcat(buf, "sunsupVL ", blen); - if (err & INFINIPATH_E_SPKTLEN) - strlcat(buf, "spktlen ", blen); if (err & INFINIPATH_E_INVALIDADDR) strlcat(buf, "invalidaddr ", blen); - if (err & INFINIPATH_E_RICRC) - strlcat(buf, "CRC ", blen); - if (err & INFINIPATH_E_RVCRC) - strlcat(buf, "VCRC ", blen); if (err & INFINIPATH_E_RRCVEGRFULL) strlcat(buf, "rcvegrfull ", blen); if (err & INFINIPATH_E_RRCVHDRFULL) @@ -819,6 +856,8 @@ void ipath_decode_err(char *buf, size_t strlcat(buf, "hardware ", blen); if (err & INFINIPATH_E_RESET) strlcat(buf, "reset ", blen); +done: + return iserr; } /** @@ -1662,6 +1701,22 @@ int ipath_set_linkstate(struct ipath_dev lstate = IPATH_LINKACTIVE; break; + case IPATH_IB_LINK_LOOPBACK: + dev_info(&dd->pcidev->dev, "Enabling IB local loopback\n"); + dd->ipath_ibcctrl |= INFINIPATH_IBCC_LOOPBACK; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + ret = 0; + goto bail; // no state change to wait for + + case IPATH_IB_LINK_EXTERNAL: + dev_info(&dd->pcidev->dev, "Disabling IB local loopback (normal)\n"); + dd->ipath_ibcctrl &= ~INFINIPATH_IBCC_LOOPBACK; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + ret = 0; + goto bail; // no state change to wait for + default: ipath_dbg("Invalid linkstate 0x%x requested\n", newstate); ret = -EINVAL; @@ -1765,29 +1820,6 @@ int ipath_set_lid(struct ipath_devdata * return 0; } -/** - * ipath_read_kreg64_port - read a device's per-port 64-bit kernel register - * @dd: the infinipath device - * @regno: the register number to read - * @port: the port containing the register - * - * Registers that vary with the chip implementation constants (port) - * use this routine. - */ -u64 ipath_read_kreg64_port(const struct ipath_devdata *dd, ipath_kreg regno, - unsigned port) -{ - u16 where; - - if (port < dd->ipath_portcnt && - (regno == dd->ipath_kregs->kr_rcvhdraddr || - regno == dd->ipath_kregs->kr_rcvhdrtailaddr)) - where = regno + port; - else - where = -1; - - return ipath_read_kreg64(dd, where); -} /** * ipath_write_kreg_port - write a device's per-port 64-bit kernel register @@ -1973,7 +2005,8 @@ static int __init infinipath_init(void) { int ret; - ipath_dbg(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version); + if (ipath_debug & __IPATH_DBG) + printk(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version); /* * These must be called before the driver is registered with diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c index a4019a6..030185f 100644 --- a/drivers/infiniband/hw/ipath/ipath_eeprom.c +++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c @@ -626,6 +626,10 @@ void ipath_get_eeprom_info(struct ipath_ } else memcpy(dd->ipath_serial, ifp->if_serial, sizeof ifp->if_serial); + if (!strstr(ifp->if_comment, "Tested successfully")) + ipath_dev_err(dd, "Board SN %s did not pass functional " + "test: %s\n", dd->ipath_serial, + ifp->if_comment); ipath_cdbg(VERBOSE, "Initted GUID to %llx from eeprom\n", (unsigned long long) be64_to_cpu(dd->ipath_guid)); diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 5d64ff8..1272aaf 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 QLogic, Inc. All rights reserved. + * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -41,12 +41,6 @@ #include #include "ipath_kernel.h" #include "ipath_common.h" -/* - * mmap64 doesn't allow all 64 bits for 32-bit applications - * so only use the low 43 bits. - */ -#define MMAP64_MASK 0x7FFFFFFFFFFUL - static int ipath_open(struct inode *, struct file *); static int ipath_close(struct inode *, struct file *); static ssize_t ipath_write(struct file *, const char __user *, size_t, @@ -63,6 +57,24 @@ static const struct file_operations ipat .mmap = ipath_mmap }; +/* + * Convert kernel virtual addresses to physical addresses so they don't + * potentially conflict with the chip addresses used as mmap offsets. + * It doesn't really matter what mmap offset we use as long as we can + * interpret it correctly. + */ +static u64 cvt_kvaddr(void *p) +{ + struct page *page; + u64 paddr = 0; + + page = vmalloc_to_page(p); + if (page) + paddr = page_to_pfn(page) << PAGE_SHIFT; + + return paddr; +} + static int ipath_get_base_info(struct file *fp, void __user *ubase, size_t ubase_size) { @@ -87,7 +99,7 @@ static int ipath_get_base_info(struct fi sz = sizeof(*kinfo); /* If port sharing is not requested, allow the old size structure */ if (!shared) - sz -= 3 * sizeof(u64); + sz -= 7 * sizeof(u64); if (ubase_size < sz) { ipath_cdbg(PROC, "Base size %zu, need %zu (version mismatch?)\n", @@ -165,24 +177,41 @@ static int ipath_get_base_info(struct fi kinfo->spi_piobufbase = (u64) pd->port_piobufs + dd->ipath_palign * (dd->ipath_pbufsport - kinfo->spi_piocnt); - kinfo->__spi_uregbase = (u64) dd->ipath_uregbase + - dd->ipath_palign * pd->port_port; } else { unsigned slave = subport_fp(fp) - 1; kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt; kinfo->spi_piobufbase = (u64) pd->port_piobufs + dd->ipath_palign * kinfo->spi_piocnt * slave; - kinfo->__spi_uregbase = ((u64) pd->subport_uregbase + - PAGE_SIZE * slave) & MMAP64_MASK; + } + if (shared) { + kinfo->spi_port_uregbase = (u64) dd->ipath_uregbase + + dd->ipath_palign * pd->port_port; + kinfo->spi_port_rcvegrbuf = kinfo->spi_rcv_egrbufs; + kinfo->spi_port_rcvhdr_base = kinfo->spi_rcvhdr_base; + kinfo->spi_port_rcvhdr_tailaddr = kinfo->spi_rcvhdr_tailaddr; - kinfo->spi_rcvhdr_base = ((u64) pd->subport_rcvhdr_base + - pd->port_rcvhdrq_size * slave) & MMAP64_MASK; - kinfo->spi_rcvhdr_tailaddr = - (u64) pd->port_rcvhdrqtailaddr_phys & MMAP64_MASK; - kinfo->spi_rcv_egrbufs = ((u64) pd->subport_rcvegrbuf + - dd->ipath_rcvegrcnt * dd->ipath_rcvegrbufsize * slave) & - MMAP64_MASK; + kinfo->__spi_uregbase = cvt_kvaddr(pd->subport_uregbase + + PAGE_SIZE * subport_fp(fp)); + + kinfo->spi_rcvhdr_base = cvt_kvaddr(pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * subport_fp(fp)); + kinfo->spi_rcvhdr_tailaddr = 0; + kinfo->spi_rcv_egrbufs = cvt_kvaddr(pd->subport_rcvegrbuf + + pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size * + subport_fp(fp)); + + kinfo->spi_subport_uregbase = + cvt_kvaddr(pd->subport_uregbase); + kinfo->spi_subport_rcvegrbuf = + cvt_kvaddr(pd->subport_rcvegrbuf); + kinfo->spi_subport_rcvhdr_base = + cvt_kvaddr(pd->subport_rcvhdr_base); + ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n", + kinfo->spi_port, kinfo->spi_runtime_flags, + (unsigned long long) kinfo->spi_subport_uregbase, + (unsigned long long) kinfo->spi_subport_rcvegrbuf, + (unsigned long long) kinfo->spi_subport_rcvhdr_base); } kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) / @@ -199,20 +228,10 @@ static int ipath_get_base_info(struct fi if (master) { kinfo->spi_runtime_flags |= IPATH_RUNTIME_MASTER; - kinfo->spi_subport_uregbase = - (u64) pd->subport_uregbase & MMAP64_MASK; - kinfo->spi_subport_rcvegrbuf = - (u64) pd->subport_rcvegrbuf & MMAP64_MASK; - kinfo->spi_subport_rcvhdr_base = - (u64) pd->subport_rcvhdr_base & MMAP64_MASK; - ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n", - kinfo->spi_port, kinfo->spi_runtime_flags, - (unsigned long long) kinfo->spi_subport_uregbase, - (unsigned long long) kinfo->spi_subport_rcvegrbuf, - (unsigned long long) kinfo->spi_subport_rcvhdr_base); } - if (copy_to_user(ubase, kinfo, sizeof(*kinfo))) + sz = (ubase_size < sizeof(*kinfo)) ? ubase_size : sizeof(*kinfo); + if (copy_to_user(ubase, kinfo, sz)) ret = -EFAULT; bail: @@ -1132,67 +1151,55 @@ static int mmap_kvaddr(struct vm_area_st struct ipath_devdata *dd; void *addr; size_t size; - int ret; + int ret = 0; /* If the port is not shared, all addresses should be physical */ - if (!pd->port_subport_cnt) { - ret = -EINVAL; + if (!pd->port_subport_cnt) goto bail; - } dd = pd->port_dd; size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size; /* - * Master has all the slave uregbase, rcvhdrq, and - * rcvegrbufs mmapped. + * Each process has all the subport uregbase, rcvhdrq, and + * rcvegrbufs mmapped - as an array for all the processes, + * and also separately for this process. */ - if (subport == 0) { - unsigned num_slaves = pd->port_subport_cnt - 1; - - if (pgaddr == ((u64) pd->subport_uregbase & MMAP64_MASK)) { - addr = pd->subport_uregbase; - size = PAGE_SIZE * num_slaves; - } else if (pgaddr == ((u64) pd->subport_rcvhdr_base & - MMAP64_MASK)) { - addr = pd->subport_rcvhdr_base; - size = pd->port_rcvhdrq_size * num_slaves; - } else if (pgaddr == ((u64) pd->subport_rcvegrbuf & - MMAP64_MASK)) { - addr = pd->subport_rcvegrbuf; - size *= num_slaves; - } else { - ret = -EINVAL; - goto bail; - } - } else if (pgaddr == (((u64) pd->subport_uregbase + - PAGE_SIZE * (subport - 1)) & MMAP64_MASK)) { - addr = pd->subport_uregbase + PAGE_SIZE * (subport - 1); - size = PAGE_SIZE; - } else if (pgaddr == (((u64) pd->subport_rcvhdr_base + - pd->port_rcvhdrq_size * (subport - 1)) & - MMAP64_MASK)) { - addr = pd->subport_rcvhdr_base + - pd->port_rcvhdrq_size * (subport - 1); - size = pd->port_rcvhdrq_size; - } else if (pgaddr == (((u64) pd->subport_rcvegrbuf + - size * (subport - 1)) & MMAP64_MASK)) { - addr = pd->subport_rcvegrbuf + size * (subport - 1); - /* rcvegrbufs are read-only on the slave */ - if (vma->vm_flags & VM_WRITE) { - dev_info(&dd->pcidev->dev, - "Can't map eager buffers as " - "writable (flags=%lx)\n", vma->vm_flags); - ret = -EPERM; - goto bail; - } - /* - * Don't allow permission to later change to writeable - * with mprotect. - */ - vma->vm_flags &= ~VM_MAYWRITE; + if (pgaddr == cvt_kvaddr(pd->subport_uregbase)) { + addr = pd->subport_uregbase; + size = PAGE_SIZE * pd->port_subport_cnt; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base)) { + addr = pd->subport_rcvhdr_base; + size = pd->port_rcvhdrq_size * pd->port_subport_cnt; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf)) { + addr = pd->subport_rcvegrbuf; + size *= pd->port_subport_cnt; + } else if (pgaddr == cvt_kvaddr(pd->subport_uregbase + + PAGE_SIZE * subport)) { + addr = pd->subport_uregbase + PAGE_SIZE * subport; + size = PAGE_SIZE; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * subport)) { + addr = pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * subport; + size = pd->port_rcvhdrq_size; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf + + size * subport)) { + addr = pd->subport_rcvegrbuf + size * subport; + /* rcvegrbufs are read-only on the slave */ + if (vma->vm_flags & VM_WRITE) { + dev_info(&dd->pcidev->dev, + "Can't map eager buffers as " + "writable (flags=%lx)\n", vma->vm_flags); + ret = -EPERM; + goto bail; + } + /* + * Don't allow permission to later change to writeable + * with mprotect. + */ + vma->vm_flags &= ~VM_MAYWRITE; } else { - ret = -EINVAL; goto bail; } len = vma->vm_end - vma->vm_start; @@ -1205,7 +1212,7 @@ static int mmap_kvaddr(struct vm_area_st vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT; vma->vm_ops = &ipath_file_vm_ops; vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND; - ret = 0; + ret = 1; bail: return ret; @@ -1265,19 +1272,20 @@ static int ipath_mmap(struct file *fp, s * Check for kernel virtual addresses first, anything else must * match a HW or memory address. */ - if (pgaddr >= (1ULL<<40)) { - ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp)); + ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp)); + if (ret) { + if (ret > 0) + ret = 0; goto bail; } + ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; if (!pd->port_subport_cnt) { /* port is not shared */ - ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; piocnt = dd->ipath_pbufsport; piobufs = pd->port_piobufs; } else if (!subport_fp(fp)) { /* caller is the master */ - ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; piocnt = (dd->ipath_pbufsport / pd->port_subport_cnt) + (dd->ipath_pbufsport % pd->port_subport_cnt); piobufs = pd->port_piobufs + @@ -1286,7 +1294,6 @@ static int ipath_mmap(struct file *fp, s unsigned slave = subport_fp(fp) - 1; /* caller is a slave */ - ureg = 0; piocnt = dd->ipath_pbufsport / pd->port_subport_cnt; piobufs = pd->port_piobufs + dd->ipath_palign * piocnt * slave; } @@ -1300,9 +1307,6 @@ static int ipath_mmap(struct file *fp, s ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0, (void *) dd->ipath_pioavailregs_dma, "pioavail registers"); - else if (subport_fp(fp)) - /* Subports don't mmap the physical receive buffers */ - ret = -EINVAL; else if (pgaddr == pd->port_rcvegr_phys) ret = mmap_rcvegrbufs(vma, pd); else if (pgaddr == (u64) pd->port_rcvhdrq_phys) @@ -1400,32 +1404,41 @@ static int init_subports(struct ipath_de const struct ipath_user_info *uinfo) { int ret = 0; - unsigned num_slaves; + unsigned num_subports; size_t size; - /* Old user binaries don't know about subports */ - if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) - goto bail; /* * If the user is requesting zero or one port, * skip the subport allocation. */ if (uinfo->spu_subport_cnt <= 1) goto bail; - if (uinfo->spu_subport_cnt > 4) { + + /* Old user binaries don't know about new subport implementation */ + if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) { + dev_info(&dd->pcidev->dev, + "Mismatched user minor version (%d) and driver " + "minor version (%d) while port sharing. Ensure " + "that driver and library are from the same " + "release.\n", + (int) (uinfo->spu_userversion & 0xffff), + IPATH_USER_SWMINOR); + goto bail; + } + if (uinfo->spu_subport_cnt > INFINIPATH_MAX_SUBPORT) { ret = -EINVAL; goto bail; } - num_slaves = uinfo->spu_subport_cnt - 1; - pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves); + num_subports = uinfo->spu_subport_cnt; + pd->subport_uregbase = vmalloc(PAGE_SIZE * num_subports); if (!pd->subport_uregbase) { ret = -ENOMEM; goto bail; } /* Note: pd->port_rcvhdrq_size isn't initialized yet. */ size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize * - sizeof(u32), PAGE_SIZE) * num_slaves; + sizeof(u32), PAGE_SIZE) * num_subports; pd->subport_rcvhdr_base = vmalloc(size); if (!pd->subport_rcvhdr_base) { ret = -ENOMEM; @@ -1434,7 +1447,7 @@ static int init_subports(struct ipath_de pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size * - num_slaves); + num_subports); if (!pd->subport_rcvegrbuf) { ret = -ENOMEM; goto bail_rhdr; @@ -1443,6 +1456,12 @@ static int init_subports(struct ipath_de pd->port_subport_cnt = uinfo->spu_subport_cnt; pd->port_subport_id = uinfo->spu_subport_id; pd->active_slaves = 1; + set_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag); + memset(pd->subport_uregbase, 0, PAGE_SIZE * num_subports); + memset(pd->subport_rcvhdr_base, 0, size); + memset(pd->subport_rcvegrbuf, 0, pd->port_rcvegrbuf_chunks * + pd->port_rcvegrbuf_size * + num_subports); goto bail; bail_rhdr: @@ -1573,18 +1592,19 @@ static int find_best_unit(struct file *f */ if (!cpus_empty(current->cpus_allowed) && !cpus_full(current->cpus_allowed)) { - int ncpus = num_online_cpus(), curcpu = -1; + int ncpus = num_online_cpus(), curcpu = -1, nset = 0; for (i = 0; i < ncpus; i++) if (cpu_isset(i, current->cpus_allowed)) { ipath_cdbg(PROC, "%s[%u] affinity set for " - "cpu %d\n", current->comm, - current->pid, i); + "cpu %d/%d\n", current->comm, + current->pid, i, ncpus); curcpu = i; + nset++; } - if (curcpu != -1) { + if (curcpu != -1 && nset != ncpus) { if (npresent) { prefunit = curcpu / (ncpus / npresent); - ipath_dbg("%s[%u] %d chips, %d cpus, " + ipath_cdbg(PROC,"%s[%u] %d chips, %d cpus, " "%d cpus/chip, select unit %d\n", current->comm, current->pid, npresent, ncpus, ncpus / npresent, @@ -1764,11 +1784,17 @@ static int ipath_do_user_init(struct fil const struct ipath_user_info *uinfo) { int ret; - struct ipath_portdata *pd; + struct ipath_portdata *pd = port_fp(fp); struct ipath_devdata *dd; u32 head32; - pd = port_fp(fp); + /* Subports don't need to initialize anything since master did it. */ + if (subport_fp(fp)) { + ret = wait_event_interruptible(pd->port_wait, + !test_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag)); + goto done; + } + dd = pd->port_dd; if (uinfo->spu_rcvhdrsize) { @@ -1826,6 +1852,11 @@ static int ipath_do_user_init(struct fil dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD); ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, dd->ipath_rcvctrl); + /* Notify any waiting slaves */ + if (pd->port_subport_cnt) { + clear_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag); + wake_up(&pd->port_wait); + } done: return ret; } @@ -2017,6 +2048,17 @@ static int ipath_get_slave_info(struct i return ret; } +static int ipath_force_pio_avail_update(struct ipath_devdata *dd) +{ + u64 reg = dd->ipath_sendctrl; + + clear_bit(IPATH_S_PIOBUFAVAILUPD, ®); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, reg); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl); + + return 0; +} + static ssize_t ipath_write(struct file *fp, const char __user *data, size_t count, loff_t *off) { @@ -2071,27 +2113,35 @@ static ssize_t ipath_write(struct file * dest = &cmd.cmd.part_key; src = &ucmd->cmd.part_key; break; - case IPATH_CMD_SLAVE_INFO: + case __IPATH_CMD_SLAVE_INFO: copy = sizeof(cmd.cmd.slave_mask_addr); dest = &cmd.cmd.slave_mask_addr; src = &ucmd->cmd.slave_mask_addr; break; + case IPATH_CMD_PIOAVAILUPD: // force an update of PIOAvail reg + copy = 0; + src = NULL; + dest = NULL; + break; default: ret = -EINVAL; goto bail; } - if ((count - consumed) < copy) { - ret = -EINVAL; - goto bail; - } + if (copy) { + if ((count - consumed) < copy) { + ret = -EINVAL; + goto bail; + } - if (copy_from_user(dest, src, copy)) { - ret = -EFAULT; - goto bail; + if (copy_from_user(dest, src, copy)) { + ret = -EFAULT; + goto bail; + } + + consumed += copy; } - consumed += copy; pd = port_fp(fp); if (!pd && cmd.type != __IPATH_CMD_USER_INIT && cmd.type != IPATH_CMD_ASSIGN_PORT) { @@ -2137,11 +2187,14 @@ static ssize_t ipath_write(struct file * case IPATH_CMD_SET_PART_KEY: ret = ipath_set_part_key(pd, cmd.cmd.part_key); break; - case IPATH_CMD_SLAVE_INFO: + case __IPATH_CMD_SLAVE_INFO: ret = ipath_get_slave_info(pd, (void __user *) (unsigned long) cmd.cmd.slave_mask_addr); break; + case IPATH_CMD_PIOAVAILUPD: + ret = ipath_force_pio_avail_update(pd->port_dd); + break; } if (ret >= 0) diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c index 9934825..4171198 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6110.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c @@ -43,6 +43,9 @@ #include #include "ipath_kernel.h" #include "ipath_registers.h" +static void ipath_setup_ht_setextled(struct ipath_devdata *, u64, u64); + + /* * This lists the InfiniPath registers, in the actual chip layout. * This structure should never be directly accessed. @@ -208,8 +211,8 @@ static const struct ipath_kregs ipath_ht .kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus), .kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig), /* - * These should not be used directly via ipath_read_kreg64(), - * use them with ipath_read_kreg64_port(), + * These should not be used directly via ipath_write_kreg64(), + * use them with ipath_write_kreg64_port(), */ .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0), .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0) @@ -284,6 +287,14 @@ #define INFINIPATH_EXTS_SERDESSEL 0x4 #define INFINIPATH_EXTS_MEMBIST_ENDTEST 0x0000000000004000 #define INFINIPATH_EXTS_MEMBIST_CORRECT 0x0000000000008000 + +/* TID entries (memory), HT-only */ +#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL /* 40 bits valid */ +#define INFINIPATH_RT_VALID 0x8000000000000000ULL +#define INFINIPATH_RT_ADDR_SHIFT 0 +#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFFULL +#define INFINIPATH_RT_BUFSIZE_SHIFT 48 + /* * masks and bits that are different in different chips, or present only * in one @@ -402,6 +413,14 @@ static const struct ipath_hwerror_msgs i INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"), }; +#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \ + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \ + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) +#define RXE_EAGER_PARITY (INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID \ + << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) + +static int ipath_ht_txe_recover(struct ipath_devdata *); + /** * ipath_ht_handle_hwerrors - display hardware errors. * @dd: the infinipath device @@ -450,13 +469,12 @@ static void ipath_ht_handle_hwerrors(str /* * make sure we get this much out, unless told to be quiet, + * it's a parity error we may recover from, * or it's occurred within the last 5 seconds */ - if ((hwerrs & ~(dd->ipath_lasthwerror | - ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) || - (ipath_debug & __IPATH_VERBDBG)) + if ((hwerrs & ~(dd->ipath_lasthwerror | TXE_PIO_PARITY | + RXE_EAGER_PARITY)) || + (ipath_debug & __IPATH_VERBDBG)) dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx " "(cleared)\n", (unsigned long long) hwerrs); dd->ipath_lasthwerror |= hwerrs; @@ -467,7 +485,7 @@ static void ipath_ht_handle_hwerrors(str (hwerrs & ~dd->ipath_hwe_bitsextant)); ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control); - if (ctrl & INFINIPATH_C_FREEZEMODE) { + if ((ctrl & INFINIPATH_C_FREEZEMODE) && !ipath_diag_inuse) { /* * parity errors in send memory are recoverable, * just cancel the send (if indicated in * sendbuffererror), @@ -476,50 +494,14 @@ static void ipath_ht_handle_hwerrors(str * occur if a processor speculative read is done to the PIO * buffer while we are sending a packet, for example. */ - if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { - ipath_stats.sps_txeparity++; - ipath_dbg("Recovering from TXE parity error (%llu), " - "hwerrstatus=%llx\n", - (unsigned long long) ipath_stats.sps_txeparity, - (unsigned long long) hwerrs); - ipath_disarm_senderrbufs(dd); - hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT); - if (!hwerrs) { /* else leave in freeze mode */ - ipath_write_kreg(dd, - dd->ipath_kregs->kr_control, - dd->ipath_control); - return; - } - } - if (hwerrs) { - /* - * if any set that we aren't ignoring; only - * make the complaint once, in case it's stuck - * or recurring, and we get here multiple - * times. - */ - if (dd->ipath_flags & IPATH_INITTED) { - ipath_dev_err(dd, "Fatal Hardware Error (freeze " - "mode), no longer usable, SN %.16s\n", - dd->ipath_serial); - isfatal = 1; - } - *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; - /* mark as having had error */ - *dd->ipath_statusp |= IPATH_STATUS_HWERROR; - /* - * mark as not usable, at a minimum until driver - * is reloaded, probably until reboot, since no - * other reset is possible. - */ - dd->ipath_flags &= ~IPATH_INITTED; - } else { - ipath_dbg("Clearing freezemode on ignored hardware " - "error\n"); + if ((hwerrs & TXE_PIO_PARITY) && ipath_ht_txe_recover(dd)) + hwerrs &= ~TXE_PIO_PARITY; + if (hwerrs & RXE_EAGER_PARITY) + ipath_dev_err(dd, "RXE parity, Eager TID error is not " + "recoverable\n"); + if (!hwerrs) { + ipath_dbg("Clearing freezemode on ignored or " + "recovered hardware error\n"); ctrl &= ~INFINIPATH_C_FREEZEMODE; ipath_write_kreg(dd, dd->ipath_kregs->kr_control, ctrl); @@ -587,7 +569,39 @@ #define _IPATH_PLL_FAIL (INFINIPATH_HWE_ dd->ipath_hwerrmask); } - ipath_dev_err(dd, "%s hardware error\n", msg); + if (hwerrs) { + /* + * if any set that we aren't ignoring; only + * make the complaint once, in case it's stuck + * or recurring, and we get here multiple + * times. + * force link down, so switch knows, and + * LEDs are turned off + */ + if (dd->ipath_flags & IPATH_INITTED) { + ipath_set_linkstate(dd, IPATH_IB_LINKDOWN); + ipath_setup_ht_setextled(dd, + INFINIPATH_IBCS_L_STATE_DOWN, + INFINIPATH_IBCS_LT_STATE_DISABLED); + ipath_dev_err(dd, "Fatal Hardware Error (freeze " + "mode), no longer usable, SN %.16s\n", + dd->ipath_serial); + isfatal = 1; + } + *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; + /* mark as having had error */ + *dd->ipath_statusp |= IPATH_STATUS_HWERROR; + /* + * mark as not usable, at a minimum until driver + * is reloaded, probably until reboot, since no + * other reset is possible. + */ + dd->ipath_flags &= ~IPATH_INITTED; + } + else + *msg = 0; /* recovered from all of them */ + if (*msg) + ipath_dev_err(dd, "%s hardware error\n", msg); if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) /* * for status file; if no trailing brace is copied, @@ -658,7 +672,8 @@ static int ipath_ht_boardname(struct ipa if (n) snprintf(name, namelen, "%s", n); - if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 || dd->ipath_minrev > 3)) { + if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 || + dd->ipath_minrev > 3)) { /* * This version of the driver only supports Rev 3.2 and 3.3 */ @@ -1163,6 +1178,8 @@ static void ipath_ht_init_hwerrors(struc if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST)) ipath_dev_err(dd, "MemBIST did not complete!\n"); + if (extsval & INFINIPATH_EXTS_MEMBIST_CORRECT) + ipath_dbg("MemBIST corrected\n"); ipath_check_htlink(dd); @@ -1366,6 +1383,9 @@ static void ipath_ht_put_tid(struct ipat u64 __iomem *tidptr, u32 type, unsigned long pa) { + if (!dd->ipath_kregbase) + return; + if (pa != dd->ipath_tidinvalid) { if (unlikely((pa & ~INFINIPATH_RT_ADDR_MASK))) { dev_info(&dd->pcidev->dev, @@ -1382,10 +1402,10 @@ static void ipath_ht_put_tid(struct ipat pa |= lenvalid | INFINIPATH_RT_VALID; } } - if (dd->ipath_kregbase) - writeq(pa, tidptr); + writeq(pa, tidptr); } + /** * ipath_ht_clear_tid - clear all TID entries for a port, expected and eager * @dd: the infinipath device @@ -1515,7 +1535,7 @@ static int ipath_ht_early_init(struct ip INFINIPATH_S_ABORT); ipath_get_eeprom_info(dd); - if(dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' && + if (dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' && dd->ipath_serial[1] == '2' && dd->ipath_serial[2] == '8') { /* * Later production QHT7040 has same changes as QHT7140, so @@ -1528,6 +1548,24 @@ static int ipath_ht_early_init(struct ip return 0; } + +static int ipath_ht_txe_recover(struct ipath_devdata *dd) +{ + int cnt = ++ipath_stats.sps_txeparity; + if (cnt >= IPATH_MAX_PARITY_ATTEMPTS) { + if (cnt == IPATH_MAX_PARITY_ATTEMPTS) + ipath_dev_err(dd, + "Too many attempts to recover from " + "TXE parity, giving up\n"); + return 0; + } + dev_info(&dd->pcidev->dev, + "Recovering from TXE PIO parity error\n"); + ipath_disarm_senderrbufs(dd, 1); + return 1; +} + + /** * ipath_init_ht_get_base_info - set chip-specific flags for user code * @dd: the infinipath device diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c index 05918e1..1b9c308 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6120.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c @@ -43,6 +43,8 @@ #include #include "ipath_kernel.h" #include "ipath_registers.h" +static void ipath_setup_pe_setextled(struct ipath_devdata *, u64, u64); + /* * This file contains all the chip-specific register information and * access functions for the QLogic InfiniPath PCI-Express chip. @@ -207,8 +209,8 @@ static const struct ipath_kregs ipath_pe .kr_ibpllcfg = IPATH_KREG_OFFSET(IBPLLCfg), /* - * These should not be used directly via ipath_read_kreg64(), - * use them with ipath_read_kreg64_port() + * These should not be used directly via ipath_write_kreg64(), + * use them with ipath_write_kreg64_port(), */ .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0), .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0), @@ -321,6 +323,12 @@ static const struct ipath_hwerror_msgs i INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"), }; +#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \ + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \ + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) + +static int ipath_pe_txe_recover(struct ipath_devdata *); + /** * ipath_pe_handle_hwerrors - display hardware errors. * @dd: the infinipath device @@ -394,32 +402,21 @@ static void ipath_pe_handle_hwerrors(str * occur if a processor speculative read is done to the PIO * buffer while we are sending a packet, for example. */ - if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { - ipath_stats.sps_txeparity++; - ipath_dbg("Recovering from TXE parity error (%llu), " - "hwerrstatus=%llx\n", - (unsigned long long) ipath_stats.sps_txeparity, - (unsigned long long) hwerrs); - ipath_disarm_senderrbufs(dd); - hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT); - if (!hwerrs) { /* else leave in freeze mode */ - ipath_write_kreg(dd, - dd->ipath_kregs->kr_control, - dd->ipath_control); - return; - } - } + if ((hwerrs & TXE_PIO_PARITY) && ipath_pe_txe_recover(dd)) + hwerrs &= ~TXE_PIO_PARITY; if (hwerrs) { /* * if any set that we aren't ignoring only make the * complaint once, in case it's stuck or recurring, * and we get here multiple times + * Force link down, so switch knows, and + * LEDs are turned off */ if (dd->ipath_flags & IPATH_INITTED) { + ipath_set_linkstate(dd, IPATH_IB_LINKDOWN); + ipath_setup_pe_setextled(dd, + INFINIPATH_IBCS_L_STATE_DOWN, + INFINIPATH_IBCS_LT_STATE_DISABLED); ipath_dev_err(dd, "Fatal Hardware Error (freeze " "mode), no longer usable, SN %.16s\n", dd->ipath_serial); @@ -493,7 +490,8 @@ #define _IPATH_PLL_FAIL (INFINIPATH_HWE_ dd->ipath_hwerrmask); } - ipath_dev_err(dd, "%s hardware error\n", msg); + if (*msg) + ipath_dev_err(dd, "%s hardware error\n", msg); if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) { /* * for /sys status file ; if no trailing } is copied, we'll @@ -581,6 +579,8 @@ static void ipath_pe_init_hwerrors(struc if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST)) ipath_dev_err(dd, "MemBIST did not complete!\n"); + if (extsval & INFINIPATH_EXTS_MEMBIST_FOUND) + ipath_dbg("MemBIST corrected\n"); val = ~0ULL; /* barring bugs, all hwerrors become interrupts, */ @@ -1330,6 +1330,35 @@ static void ipath_pe_free_irq(struct ipa dd->ipath_irq = 0; } +/* + * On platforms using this chip, and not having ordered WC stores, we + * can get TXE parity errors due to speculative reads to the PIO buffers, + * and this, due to a chip bug can result in (many) false parity error + * reports. So it's a debug print on those, and an info print on systems + * where the speculative reads don't occur. + * Because we can get lots of false errors, we have no upper limit + * on recovery attempts on those platforms. + */ +static int ipath_pe_txe_recover(struct ipath_devdata *dd) +{ + if (ipath_unordered_wc()) + ipath_dbg("Recovering from TXE PIO parity error\n"); + else { + int cnt = ++ipath_stats.sps_txeparity; + if (cnt >= IPATH_MAX_PARITY_ATTEMPTS) { + if (cnt == IPATH_MAX_PARITY_ATTEMPTS) + ipath_dev_err(dd, + "Too many attempts to recover from " + "TXE parity, giving up\n"); + return 0; + } + dev_info(&dd->pcidev->dev, + "Recovering from TXE PIO parity error\n"); + } + ipath_disarm_senderrbufs(dd, 1); + return 1; +} + /** * ipath_init_iba6120_funcs - set up the chip-specific function pointers * @dd: the infinipath device diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c index d4f6b52..7045ba6 100644 --- a/drivers/infiniband/hw/ipath/ipath_init_chip.c +++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c @@ -216,6 +216,20 @@ static int bringup_link(struct ipath_dev return ret; } +static struct ipath_portdata *create_portdata0(struct ipath_devdata *dd) +{ + struct ipath_portdata *pd = NULL; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (pd) { + pd->port_dd = dd; + pd->port_cnt = 1; + /* The port 0 pkey table is used by the layer interface. */ + pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY; + } + return pd; +} + static int init_chip_first(struct ipath_devdata *dd, struct ipath_portdata **pdp) { @@ -271,20 +285,16 @@ static int init_chip_first(struct ipath_ goto done; } - dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL); + pd = create_portdata0(dd); - if (!dd->ipath_pd[0]) { + if (!pd) { ipath_dev_err(dd, "Unable to allocate portdata for port " "0, failing\n"); ret = -ENOMEM; goto done; } - pd = dd->ipath_pd[0]; - pd->port_dd = dd; - pd->port_port = 0; - pd->port_cnt = 1; - /* The port 0 pkey table is used by the layer interface. */ - pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY; + dd->ipath_pd[0] = pd; + dd->ipath_rcvtidcnt = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt); dd->ipath_rcvtidbase = @@ -590,6 +600,10 @@ static int init_housekeeping(struct ipat goto done; } + + /* clear diagctrl register, in case diags were running and crashed */ + ipath_write_kreg (dd, dd->ipath_kregs->kr_hwdiagctrl, 0); + /* clear the initial reset flag, in case first driver load */ ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, INFINIPATH_E_RESET); @@ -668,6 +682,7 @@ int ipath_init_chip(struct ipath_devdata { int ret = 0, i; u32 val32, kpiobufs; + u32 piobufs, uports; u64 val; struct ipath_portdata *pd = NULL; /* keep gcc4 happy */ gfp_t gfp_flags = GFP_USER | __GFP_COMP; @@ -702,16 +717,17 @@ int ipath_init_chip(struct ipath_devdata * the in memory DMA'ed copies of the registers. This has to * be done early, before we calculate lastport, etc. */ - val = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; + piobufs = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; /* * calc number of pioavail registers, and save it; we have 2 * bits per buffer. */ - dd->ipath_pioavregs = ALIGN(val, sizeof(u64) * BITS_PER_BYTE / 2) + dd->ipath_pioavregs = ALIGN(piobufs, sizeof(u64) * BITS_PER_BYTE / 2) / (sizeof(u64) * BITS_PER_BYTE / 2); + uports = dd->ipath_cfgports ? dd->ipath_cfgports - 1 : 0; if (ipath_kpiobufs == 0) { /* not set by user (this is default) */ - if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) > 128) + if (piobufs >= (uports * IPATH_MIN_USER_PORT_BUFCNT) + 32) kpiobufs = 32; else kpiobufs = 16; @@ -719,31 +735,25 @@ int ipath_init_chip(struct ipath_devdata else kpiobufs = ipath_kpiobufs; - if (kpiobufs > - (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - - (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT))) { - i = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - - (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT); + if (kpiobufs + (uports * IPATH_MIN_USER_PORT_BUFCNT) > piobufs) { + i = (int) piobufs - + (int) (uports * IPATH_MIN_USER_PORT_BUFCNT); if (i < 0) i = 0; - dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs for " - "kernel leaves too few for %d user ports " + dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs of " + "%d for kernel leaves too few for %d user ports " "(%d each); using %u\n", kpiobufs, - dd->ipath_cfgports - 1, - IPATH_MIN_USER_PORT_BUFCNT, i); + piobufs, uports, IPATH_MIN_USER_PORT_BUFCNT, i); /* * shouldn't change ipath_kpiobufs, because could be * different for different devices... */ kpiobufs = i; } - dd->ipath_lastport_piobuf = - dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - kpiobufs; - dd->ipath_pbufsport = dd->ipath_cfgports > 1 - ? dd->ipath_lastport_piobuf / (dd->ipath_cfgports - 1) - : 0; - val32 = dd->ipath_lastport_piobuf - - (dd->ipath_pbufsport * (dd->ipath_cfgports - 1)); + dd->ipath_lastport_piobuf = piobufs - kpiobufs; + dd->ipath_pbufsport = + uports ? dd->ipath_lastport_piobuf / uports : 0; + val32 = dd->ipath_lastport_piobuf - (dd->ipath_pbufsport * uports); if (val32 > 0) { ipath_dbg("allocating %u pbufs/port leaves %u unused, " "add to kernel\n", dd->ipath_pbufsport, val32); @@ -754,8 +764,7 @@ int ipath_init_chip(struct ipath_devdata dd->ipath_lastpioindex = dd->ipath_lastport_piobuf; ipath_cdbg(VERBOSE, "%d PIO bufs for kernel out of %d total %u " "each for %u user ports\n", kpiobufs, - dd->ipath_piobcnt2k + dd->ipath_piobcnt4k, - dd->ipath_pbufsport, dd->ipath_cfgports - 1); + piobufs, dd->ipath_pbufsport, uports); dd->ipath_f_early_init(dd); @@ -839,11 +848,24 @@ int ipath_init_chip(struct ipath_devdata * Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing * re-init, the simplest way to handle this is to free * existing, and re-allocate. + * Need to re-create rest of port 0 portdata as well. */ if (reinit) { - struct ipath_portdata *pd = dd->ipath_pd[0]; - dd->ipath_pd[0] = NULL; - ipath_free_pddata(dd, pd); + /* Alloc and init new ipath_portdata for port0, + * Then free old pd. Could lead to fragmentation, but also + * makes later support for hot-swap easier. + */ + struct ipath_portdata *npd; + npd = create_portdata0(dd); + if (npd) { + ipath_free_pddata(dd, pd); + dd->ipath_pd[0] = pd = npd; + } else { + ipath_dev_err(dd, "Unable to allocate portdata for" + " port 0, failing\n"); + ret = -ENOMEM; + goto done; + } } dd->ipath_f_tidtemplate(dd); ret = ipath_create_rcvhdrq(dd, pd); diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index 72b9e27..45d0331 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -38,10 +38,39 @@ #include "ipath_verbs.h" #include "ipath_common.h" /* + * clear (write) a pio buffer, to clear a parity error. This routine + * should only be called when in freeze mode, and the buffer should be + * canceled afterwards. + */ +static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum) +{ + u32 __iomem *pbuf; + u32 dwcnt; /* dword count to write */ + if (pnum < dd->ipath_piobcnt2k) { + pbuf = (u32 __iomem *) (dd->ipath_pio2kbase + pnum * + dd->ipath_palign); + dwcnt = dd->ipath_piosize2k >> 2; + } + else { + pbuf = (u32 __iomem *) (dd->ipath_pio4kbase + + (pnum - dd->ipath_piobcnt2k) * dd->ipath_4kalign); + dwcnt = dd->ipath_piosize4k >> 2; + } + dev_info(&dd->pcidev->dev, + "Rewrite PIO buffer %u, to recover from parity error\n", + pnum); + *pbuf = dwcnt+1; /* no flush required, since already in freeze */ + while(--dwcnt) + *pbuf++ = 0; +} + +/* * Called when we might have an error that is specific to a particular * PIO buffer, and may need to cancel that buffer, so it can be re-used. + * If rewrite is true, and bits are set in the sendbufferror registers, + * we'll write to the buffer, for error recovery on parity errors. */ -void ipath_disarm_senderrbufs(struct ipath_devdata *dd) +void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite) { u32 piobcnt; unsigned long sbuf[4]; @@ -74,8 +103,11 @@ void ipath_disarm_senderrbufs(struct ipa } for (i = 0; i < piobcnt; i++) - if (test_bit(i, sbuf)) + if (test_bit(i, sbuf)) { + if (rewrite) + ipath_clrpiobuf(dd, i); ipath_disarm_piobufs(dd, i, 1); + } dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */ } } @@ -114,7 +146,7 @@ static u64 handle_e_sum_errs(struct ipat { u64 ignore_this_time = 0; - ipath_disarm_senderrbufs(dd); + ipath_disarm_senderrbufs(dd, 0); if ((errs & E_SUM_LINK_PKTERRS) && !(dd->ipath_flags & IPATH_LINKACTIVE)) { /* @@ -403,10 +435,13 @@ static void handle_supp_msgs(struct ipat * happens so often we never want to count it. */ if (dd->ipath_lasterror & ~INFINIPATH_E_IBSTATUSCHANGED) { - ipath_decode_err(msg, sizeof msg, dd->ipath_lasterror & - ~INFINIPATH_E_IBSTATUSCHANGED); + int iserr; + iserr = ipath_decode_err(msg, sizeof msg, + dd->ipath_lasterror & + ~INFINIPATH_E_IBSTATUSCHANGED); if (dd->ipath_lasterror & - ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) + ~(INFINIPATH_E_RRCVEGRFULL | + INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS)) ipath_dev_err(dd, "Suppressed %u messages for " "fast-repeating errors (%s) (%llx)\n", supp_msgs, msg, @@ -420,8 +455,13 @@ static void handle_supp_msgs(struct ipat * them. So only complain about these at debug * level. */ - ipath_dbg("Suppressed %u messages for %s\n", - supp_msgs, msg); + if (iserr) + ipath_dbg("Suppressed %u messages for %s\n", + supp_msgs, msg); + else + ipath_cdbg(ERRPKT, + "Suppressed %u messages for %s\n", + supp_msgs, msg); } } } @@ -462,7 +502,7 @@ static int handle_errors(struct ipath_de { char msg[512]; u64 ignore_this_time = 0; - int i; + int i, iserr = 0; int chkerrpkts = 0, noprint = 0; unsigned supp_msgs; @@ -502,6 +542,7 @@ static int handle_errors(struct ipath_de } if (supp_msgs == 250000) { + int s_iserr; /* * It's not entirely reasonable assuming that the errors set * in the last clear period are all responsible for the @@ -511,17 +552,17 @@ static int handle_errors(struct ipath_de dd->ipath_maskederrs |= dd->ipath_lasterror | errs; ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, ~dd->ipath_maskederrs); - ipath_decode_err(msg, sizeof msg, + s_iserr = ipath_decode_err(msg, sizeof msg, (dd->ipath_maskederrs & ~dd-> ipath_ignorederrs)); if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) & - ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) - ipath_dev_err(dd, "Disabling error(s) %llx because " - "occurring too frequently (%s)\n", - (unsigned long long) - (dd->ipath_maskederrs & - ~dd->ipath_ignorederrs), msg); + ~(INFINIPATH_E_RRCVEGRFULL | + INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS)) + ipath_dev_err(dd, "Temporarily disabling " + "error(s) %llx reporting; too frequent (%s)\n", + (unsigned long long) (dd->ipath_maskederrs & + ~dd->ipath_ignorederrs), msg); else { /* * rcvegrfull and rcvhdrqfull are "normal", @@ -530,8 +571,15 @@ static int handle_errors(struct ipath_de * processing them. So only complain about * these at debug level. */ - ipath_dbg("Disabling frequent queue full errors " - "(%s)\n", msg); + if (s_iserr) + ipath_dbg("Temporarily disabling reporting " + "too frequent queue full errors (%s)\n", + msg); + else + ipath_cdbg(ERRPKT, + "Temporarily disabling reporting too" + " frequent packet errors (%s)\n", + msg); } /* @@ -589,6 +637,8 @@ static int handle_errors(struct ipath_de ipath_stats.sps_crcerrs++; chkerrpkts = 1; } + iserr = errs & ~(E_SUM_PKTERRS | INFINIPATH_E_PKTERRS); + /* * We don't want to print these two as they happen, or we can make @@ -677,8 +727,13 @@ static int handle_errors(struct ipath_de *dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF; } - if (!noprint && *msg) - ipath_dev_err(dd, "%s error\n", msg); + if (!noprint && *msg) { + if (iserr) + ipath_dev_err(dd, "%s error\n", msg); + else + dev_info(&dd->pcidev->dev, "%s packet problems\n", + msg); + } if (dd->ipath_state_wanted & dd->ipath_flags) { ipath_cdbg(VERBOSE, "driver wanted state %x, iflags now %x, " "waking\n", dd->ipath_state_wanted, @@ -819,11 +874,10 @@ static void handle_urcv(struct ipath_dev struct ipath_portdata *pd = dd->ipath_pd[i]; if (portr & (1 << i) && pd && pd->port_cnt && test_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag)) { - int rcbit; clear_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); - rcbit = i + INFINIPATH_R_INTRAVAIL_SHIFT; - clear_bit(1UL << rcbit, &dd->ipath_rcvctrl); + clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT, + &dd->ipath_rcvctrl); wake_up_interruptible(&pd->port_wait); rcvdint = 1; } diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index 6d8d05f..e900c25 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h @@ -590,7 +590,6 @@ int ipath_enable_wc(struct ipath_devdata void ipath_disable_wc(struct ipath_devdata *dd); int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp); void ipath_shutdown_device(struct ipath_devdata *); -void ipath_disarm_senderrbufs(struct ipath_devdata *); struct file_operations; int ipath_cdev_init(int minor, char *name, const struct file_operations *fops, @@ -611,7 +610,7 @@ struct sk_buff *ipath_alloc_skb(struct i extern int ipath_diag_inuse; irqreturn_t ipath_intr(int irq, void *devid); -void ipath_decode_err(char *buf, size_t blen, ipath_err_t err); +int ipath_decode_err(char *buf, size_t blen, ipath_err_t err); #if __IPATH_INFO || __IPATH_DBG extern const char *ipath_ibcstatus_str[]; #endif @@ -701,6 +700,8 @@ #define IPATH_GPIO_ERRINTR_MASK 0x38 #define IPATH_PORT_WAITING_RCV 2 /* waiting for a PIO buffer to be available */ #define IPATH_PORT_WAITING_PIO 3 + /* master has not finished initializing */ +#define IPATH_PORT_MASTER_UNINIT 4 /* free up any allocated data at closes */ void ipath_free_data(struct ipath_portdata *dd); @@ -711,6 +712,7 @@ void ipath_init_iba6120_funcs(struct ipa void ipath_init_iba6110_funcs(struct ipath_devdata *); void ipath_get_eeprom_info(struct ipath_devdata *); u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg); +void ipath_disarm_senderrbufs(struct ipath_devdata *, int); /* * number of words used for protocol header if not set by ipath_userinit(); @@ -754,8 +756,6 @@ int ipath_eeprom_write(struct ipath_devd /* these are used for the registers that vary with port */ void ipath_write_kreg_port(const struct ipath_devdata *, ipath_kreg, unsigned, u64); -u64 ipath_read_kreg64_port(const struct ipath_devdata *, ipath_kreg, - unsigned); /* * We could have a single register get/put routine, that takes a group type, @@ -897,6 +897,8 @@ #endif extern unsigned ipath_debug; /* debugging bit mask */ +#define IPATH_MAX_PARITY_ATTEMPTS 10000 /* max times to try recovery */ + const char *ipath_get_unit_name(int unit); extern struct mutex ipath_mutex; diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c index 851763d..dd487c1 100644 --- a/drivers/infiniband/hw/ipath/ipath_keys.c +++ b/drivers/infiniband/hw/ipath/ipath_keys.c @@ -61,7 +61,7 @@ int ipath_alloc_lkey(struct ipath_lkey_t r = (r + 1) & (rkt->max - 1); if (r == n) { spin_unlock_irqrestore(&rkt->lock, flags); - ipath_dbg(KERN_INFO "LKEY table full\n"); + ipath_dbg("LKEY table full\n"); ret = 0; goto bail; } @@ -133,6 +133,12 @@ int ipath_lkey_ok(struct ipath_qp *qp, s * being reversible by calling bus_to_virt(). */ if (sge->lkey == 0) { + struct ipath_pd *pd = to_ipd(qp->ibqp.pd); + + if (pd->user) { + ret = 0; + goto bail; + } isge->mr = NULL; isge->vaddr = (void *) sge->addr; isge->length = sge->length; @@ -206,6 +212,12 @@ int ipath_rkey_ok(struct ipath_qp *qp, s * (see ipath_get_dma_mr and ipath_dma.c). */ if (rkey == 0) { + struct ipath_pd *pd = to_ipd(qp->ibqp.pd); + + if (pd->user) { + ret = 0; + goto bail; + } sge->mr = NULL; sge->vaddr = (void *) vaddr; sge->length = len; diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c index 8cc8598..31e7073 100644 --- a/drivers/infiniband/hw/ipath/ipath_mr.c +++ b/drivers/infiniband/hw/ipath/ipath_mr.c @@ -210,9 +210,15 @@ struct ib_mr *ipath_reg_user_mr(struct i m = 0; n = 0; list_for_each_entry(chunk, ®ion->chunk_list, list) { - for (i = 0; i < chunk->nmap; i++) { - mr->mr.map[m]->segs[n].vaddr = - page_address(chunk->page_list[i].page); + for (i = 0; i < chunk->nents; i++) { + void *vaddr; + + vaddr = page_address(chunk->page_list[i].page); + if (!vaddr) { + ret = ERR_PTR(-EINVAL); + goto bail; + } + mr->mr.map[m]->segs[n].vaddr = vaddr; mr->mr.map[m]->segs[n].length = region->page_size; n++; if (n == IPATH_SEGSZ) { diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c index 64f07b1..16db9ac 100644 --- a/drivers/infiniband/hw/ipath/ipath_qp.c +++ b/drivers/infiniband/hw/ipath/ipath_qp.c @@ -81,11 +81,51 @@ static u32 credit_table[31] = { 32768 /* 1E */ }; -static u32 alloc_qpn(struct ipath_qp_table *qpt) + +static void get_map_page(struct ipath_qp_table *qpt, struct qpn_map *map) +{ + unsigned long page = get_zeroed_page(GFP_KERNEL); + unsigned long flags; + + /* + * Free the page if someone raced with us installing it. + */ + + spin_lock_irqsave(&qpt->lock, flags); + if (map->page) + free_page(page); + else + map->page = (void *)page; + spin_unlock_irqrestore(&qpt->lock, flags); +} + + +static int alloc_qpn(struct ipath_qp_table *qpt, enum ib_qp_type type) { u32 i, offset, max_scan, qpn; struct qpn_map *map; - u32 ret; + u32 ret = -1; + + if (type == IB_QPT_SMI) + ret = 0; + else if (type == IB_QPT_GSI) + ret = 1; + + if (ret != -1) { + map = &qpt->map[0]; + if (unlikely(!map->page)) { + get_map_page(qpt, map); + if (unlikely(!map->page)) { + ret = -ENOMEM; + goto bail; + } + } + if (!test_and_set_bit(ret, map->page)) + atomic_dec(&map->n_free); + else + ret = -EBUSY; + goto bail; + } qpn = qpt->last + 1; if (qpn >= QPN_MAX) @@ -95,19 +135,7 @@ static u32 alloc_qpn(struct ipath_qp_tab max_scan = qpt->nmaps - !offset; for (i = 0;;) { if (unlikely(!map->page)) { - unsigned long page = get_zeroed_page(GFP_KERNEL); - unsigned long flags; - - /* - * Free the page if someone raced with us - * installing it: - */ - spin_lock_irqsave(&qpt->lock, flags); - if (map->page) - free_page(page); - else - map->page = (void *)page; - spin_unlock_irqrestore(&qpt->lock, flags); + get_map_page(qpt, map); if (unlikely(!map->page)) break; } @@ -151,7 +179,7 @@ static u32 alloc_qpn(struct ipath_qp_tab qpn = mk_qpn(qpt, map, offset); } - ret = 0; + ret = -ENOMEM; bail: return ret; @@ -180,29 +208,19 @@ static int ipath_alloc_qpn(struct ipath_ enum ib_qp_type type) { unsigned long flags; - u32 qpn; int ret; - if (type == IB_QPT_SMI) - qpn = 0; - else if (type == IB_QPT_GSI) - qpn = 1; - else { - /* Allocate the next available QPN */ - qpn = alloc_qpn(qpt); - if (qpn == 0) { - ret = -ENOMEM; - goto bail; - } - } - qp->ibqp.qp_num = qpn; + ret = alloc_qpn(qpt, type); + if (ret < 0) + goto bail; + qp->ibqp.qp_num = ret; /* Add the QP to the hash table. */ spin_lock_irqsave(&qpt->lock, flags); - qpn %= qpt->max; - qp->next = qpt->table[qpn]; - qpt->table[qpn] = qp; + ret %= qpt->max; + qp->next = qpt->table[ret]; + qpt->table[ret] = qp; atomic_inc(&qp->refcount); spin_unlock_irqrestore(&qpt->lock, flags); @@ -245,9 +263,7 @@ static void ipath_free_qp(struct ipath_q if (!fnd) return; - /* If QPN is not reserved, mark QPN free in the bitmap. */ - if (qp->ibqp.qp_num > 1) - free_qpn(qpt, qp->ibqp.qp_num); + free_qpn(qpt, qp->ibqp.qp_num); wait_event(qp->wait, !atomic_read(&qp->refcount)); } @@ -270,11 +286,10 @@ void ipath_free_all_qps(struct ipath_qp_ while (qp) { nqp = qp->next; - if (qp->ibqp.qp_num > 1) - free_qpn(qpt, qp->ibqp.qp_num); + free_qpn(qpt, qp->ibqp.qp_num); if (!atomic_dec_and_test(&qp->refcount) || !ipath_destroy_qp(&qp->ibqp)) - ipath_dbg(KERN_INFO "QP memory leak!\n"); + ipath_dbg("QP memory leak!\n"); qp = nqp; } } @@ -320,7 +335,8 @@ static void ipath_reset_qp(struct ipath_ qp->remote_qpn = 0; qp->qkey = 0; qp->qp_access_flags = 0; - clear_bit(IPATH_S_BUSY, &qp->s_flags); + qp->s_busy = 0; + qp->s_flags &= ~IPATH_S_SIGNAL_REQ_WR; qp->s_hdrwords = 0; qp->s_psn = 0; qp->r_psn = 0; @@ -333,7 +349,6 @@ static void ipath_reset_qp(struct ipath_ qp->r_state = IB_OPCODE_UC_SEND_LAST; } qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; - qp->r_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; qp->r_nak_state = 0; qp->r_wrid_valid = 0; qp->s_rnr_timeout = 0; @@ -344,6 +359,10 @@ static void ipath_reset_qp(struct ipath_ qp->s_ssn = 1; qp->s_lsn = 0; qp->s_wait_credit = 0; + memset(qp->s_ack_queue, 0, sizeof(qp->s_ack_queue)); + qp->r_head_ack_queue = 0; + qp->s_tail_ack_queue = 0; + qp->s_num_rd_atomic = 0; if (qp->r_rq.wq) { qp->r_rq.wq->head = 0; qp->r_rq.wq->tail = 0; @@ -357,7 +376,7 @@ static void ipath_reset_qp(struct ipath_ * @err: the receive completion error to signal if a RWQE is active * * Flushes both send and receive work queues. - * QP s_lock should be held and interrupts disabled. + * The QP s_lock should be held and interrupts disabled. */ void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err) @@ -365,7 +384,7 @@ void ipath_error_qp(struct ipath_qp *qp, struct ipath_ibdev *dev = to_idev(qp->ibqp.device); struct ib_wc wc; - ipath_dbg(KERN_INFO "QP%d/%d in error state\n", + ipath_dbg("QP%d/%d in error state\n", qp->ibqp.qp_num, qp->remote_qpn); spin_lock(&dev->pending_lock); @@ -389,6 +408,8 @@ void ipath_error_qp(struct ipath_qp *qp, wc.port_num = 0; if (qp->r_wrid_valid) { qp->r_wrid_valid = 0; + wc.wr_id = qp->r_wr_id; + wc.opcode = IB_WC_RECV; wc.status = err; ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1); } @@ -503,13 +524,17 @@ int ipath_modify_qp(struct ib_qp *ibqp, attr->path_mig_state != IB_MIG_REARM) goto inval; + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) + if (attr->max_dest_rd_atomic > IPATH_MAX_RDMA_ATOMIC) + goto inval; + switch (new_state) { case IB_QPS_RESET: ipath_reset_qp(qp); break; case IB_QPS_ERR: - ipath_error_qp(qp, IB_WC_GENERAL_ERR); + ipath_error_qp(qp, IB_WC_WR_FLUSH_ERR); break; default: @@ -559,6 +584,12 @@ int ipath_modify_qp(struct ib_qp *ibqp, if (attr_mask & IB_QP_QKEY) qp->qkey = attr->qkey; + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) + qp->r_max_rd_atomic = attr->max_dest_rd_atomic; + + if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) + qp->s_max_rd_atomic = attr->max_rd_atomic; + qp->state = new_state; spin_unlock_irqrestore(&qp->s_lock, flags); @@ -598,8 +629,8 @@ int ipath_query_qp(struct ib_qp *ibqp, s attr->alt_pkey_index = 0; attr->en_sqd_async_notify = 0; attr->sq_draining = 0; - attr->max_rd_atomic = 1; - attr->max_dest_rd_atomic = 1; + attr->max_rd_atomic = qp->s_max_rd_atomic; + attr->max_dest_rd_atomic = qp->r_max_rd_atomic; attr->min_rnr_timer = qp->r_min_rnr_timer; attr->port_num = 1; attr->timeout = qp->timeout; @@ -614,7 +645,7 @@ int ipath_query_qp(struct ib_qp *ibqp, s init_attr->recv_cq = qp->ibqp.recv_cq; init_attr->srq = qp->ibqp.srq; init_attr->cap = attr->cap; - if (qp->s_flags & (1 << IPATH_S_SIGNAL_REQ_WR)) + if (qp->s_flags & IPATH_S_SIGNAL_REQ_WR) init_attr->sq_sig_type = IB_SIGNAL_REQ_WR; else init_attr->sq_sig_type = IB_SIGNAL_ALL_WR; @@ -786,7 +817,7 @@ struct ib_qp *ipath_create_qp(struct ib_ qp->s_size = init_attr->cap.max_send_wr + 1; qp->s_max_sge = init_attr->cap.max_send_sge; if (init_attr->sq_sig_type == IB_SIGNAL_REQ_WR) - qp->s_flags = 1 << IPATH_S_SIGNAL_REQ_WR; + qp->s_flags = IPATH_S_SIGNAL_REQ_WR; else qp->s_flags = 0; dev = to_idev(ibpd->device); @@ -958,7 +989,7 @@ bail: * @wc: the WC responsible for putting the QP in this state * * Flushes the send work queue. - * The QP s_lock should be held. + * The QP s_lock should be held and interrupts disabled. */ void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc) @@ -966,7 +997,7 @@ void ipath_sqerror_qp(struct ipath_qp *q struct ipath_ibdev *dev = to_idev(qp->ibqp.device); struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last); - ipath_dbg(KERN_INFO "Send queue error on QP%d/%d: err: %d\n", + ipath_dbg("Send queue error on QP%d/%d: err: %d\n", qp->ibqp.qp_num, qp->remote_qpn, wc->status); spin_lock(&dev->pending_lock); @@ -984,12 +1015,12 @@ void ipath_sqerror_qp(struct ipath_qp *q wc->status = IB_WC_WR_FLUSH_ERR; while (qp->s_last != qp->s_head) { + wqe = get_swqe_ptr(qp, qp->s_last); wc->wr_id = wqe->wr.wr_id; wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1); if (++qp->s_last >= qp->s_size) qp->s_last = 0; - wqe = get_swqe_ptr(qp, qp->s_last); } qp->s_cur = qp->s_tail = qp->s_head; qp->state = IB_QPS_SQE; diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c index 5ff20cb..b4b88d0 100644 --- a/drivers/infiniband/hw/ipath/ipath_rc.c +++ b/drivers/infiniband/hw/ipath/ipath_rc.c @@ -37,6 +37,19 @@ #include "ipath_kernel.h" /* cut down ridiculously long IB macro names */ #define OP(x) IB_OPCODE_RC_##x +static u32 restart_sge(struct ipath_sge_state *ss, struct ipath_swqe *wqe, + u32 psn, u32 pmtu) +{ + u32 len; + + len = ((psn - wqe->psn) & IPATH_PSN_MASK) * pmtu; + ss->sge = wqe->sg_list[0]; + ss->sg_list = wqe->sg_list + 1; + ss->num_sge = wqe->wr.num_sge; + ipath_skip_sge(ss, len); + return wqe->length - len; +} + /** * ipath_init_restart- initialize the qp->s_sge after a restart * @qp: the QP who's SGE we're restarting @@ -47,15 +60,9 @@ #define OP(x) IB_OPCODE_RC_##x static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe) { struct ipath_ibdev *dev; - u32 len; - len = ((qp->s_psn - wqe->psn) & IPATH_PSN_MASK) * - ib_mtu_enum_to_int(qp->path_mtu); - qp->s_sge.sge = wqe->sg_list[0]; - qp->s_sge.sg_list = wqe->sg_list + 1; - qp->s_sge.num_sge = wqe->wr.num_sge; - ipath_skip_sge(&qp->s_sge, len); - qp->s_len = wqe->length - len; + qp->s_len = restart_sge(&qp->s_sge, wqe, qp->s_psn, + ib_mtu_enum_to_int(qp->path_mtu)); dev = to_idev(qp->ibqp.device); spin_lock(&dev->pending_lock); if (list_empty(&qp->timerwait)) @@ -70,107 +77,123 @@ static void ipath_init_restart(struct ip * @ohdr: a pointer to the IB header being constructed * @pmtu: the path MTU * - * Return bth0 if constructed; otherwise, return 0. + * Return 1 if constructed; otherwise, return 0. + * Note that we are in the responder's side of the QP context. * Note the QP s_lock must be held. */ -u32 ipath_make_rc_ack(struct ipath_qp *qp, - struct ipath_other_headers *ohdr, - u32 pmtu) +static int ipath_make_rc_ack(struct ipath_qp *qp, + struct ipath_other_headers *ohdr, + u32 pmtu, u32 *bth0p, u32 *bth2p) { + struct ipath_ack_entry *e; u32 hwords; u32 len; u32 bth0; + u32 bth2; /* header size in 32-bit words LRH+BTH = (8+12)/4. */ hwords = 5; - /* - * Send a response. Note that we are in the responder's - * side of the QP context. - */ switch (qp->s_ack_state) { - case OP(RDMA_READ_REQUEST): - qp->s_cur_sge = &qp->s_rdma_sge; - len = qp->s_rdma_len; - if (len > pmtu) { - len = pmtu; - qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); - } else - qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); - qp->s_rdma_len -= len; + case OP(RDMA_READ_RESPONSE_LAST): + case OP(RDMA_READ_RESPONSE_ONLY): + case OP(ATOMIC_ACKNOWLEDGE): + qp->s_ack_state = OP(ACKNOWLEDGE); + /* FALLTHROUGH */ + case OP(ACKNOWLEDGE): + /* Check for no next entry in the queue. */ + if (qp->r_head_ack_queue == qp->s_tail_ack_queue) { + if (qp->s_flags & IPATH_S_ACK_PENDING) + goto normal; + goto bail; + } + + e = &qp->s_ack_queue[qp->s_tail_ack_queue]; + if (e->opcode == OP(RDMA_READ_REQUEST)) { + /* Copy SGE state in case we need to resend */ + qp->s_ack_rdma_sge = e->rdma_sge; + qp->s_cur_sge = &qp->s_ack_rdma_sge; + len = e->rdma_sge.sge.sge_length; + if (len > pmtu) { + len = pmtu; + qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); + } else { + qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); + if (++qp->s_tail_ack_queue > + IPATH_MAX_RDMA_ATOMIC) + qp->s_tail_ack_queue = 0; + } + ohdr->u.aeth = ipath_compute_aeth(qp); + hwords++; + qp->s_ack_rdma_psn = e->psn; + bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; + } else { + /* COMPARE_SWAP or FETCH_ADD */ + qp->s_cur_sge = NULL; + len = 0; + qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE); + ohdr->u.at.aeth = ipath_compute_aeth(qp); + ohdr->u.at.atomic_ack_eth[0] = + cpu_to_be32(e->atomic_data >> 32); + ohdr->u.at.atomic_ack_eth[1] = + cpu_to_be32(e->atomic_data); + hwords += sizeof(ohdr->u.at) / sizeof(u32); + bth2 = e->psn; + if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC) + qp->s_tail_ack_queue = 0; + } bth0 = qp->s_ack_state << 24; - ohdr->u.aeth = ipath_compute_aeth(qp); - hwords++; break; case OP(RDMA_READ_RESPONSE_FIRST): qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE); /* FALLTHROUGH */ case OP(RDMA_READ_RESPONSE_MIDDLE): - qp->s_cur_sge = &qp->s_rdma_sge; - len = qp->s_rdma_len; + len = qp->s_ack_rdma_sge.sge.sge_length; if (len > pmtu) len = pmtu; else { ohdr->u.aeth = ipath_compute_aeth(qp); hwords++; qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); + if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC) + qp->s_tail_ack_queue = 0; } - qp->s_rdma_len -= len; bth0 = qp->s_ack_state << 24; - break; - - case OP(RDMA_READ_RESPONSE_LAST): - case OP(RDMA_READ_RESPONSE_ONLY): - /* - * We have to prevent new requests from changing - * the r_sge state while a ipath_verbs_send() - * is in progress. - */ - qp->s_ack_state = OP(ACKNOWLEDGE); - bth0 = 0; - goto bail; - - case OP(COMPARE_SWAP): - case OP(FETCH_ADD): - qp->s_cur_sge = NULL; - len = 0; - /* - * Set the s_ack_state so the receive interrupt handler - * won't try to send an ACK (out of order) until this one - * is actually sent. - */ - qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); - bth0 = OP(ATOMIC_ACKNOWLEDGE) << 24; - ohdr->u.at.aeth = ipath_compute_aeth(qp); - ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data); - hwords += sizeof(ohdr->u.at) / 4; + bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; break; default: - /* Send a regular ACK. */ - qp->s_cur_sge = NULL; - len = 0; + normal: /* - * Set the s_ack_state so the receive interrupt handler - * won't try to send an ACK (out of order) until this one - * is actually sent. + * Send a regular ACK. + * Set the s_ack_state so we wait until after sending + * the ACK before setting s_ack_state to ACKNOWLEDGE + * (see above). */ - qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); - bth0 = OP(ACKNOWLEDGE) << 24; + qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE); + qp->s_flags &= ~IPATH_S_ACK_PENDING; + qp->s_cur_sge = NULL; if (qp->s_nak_state) - ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | - (qp->s_nak_state << - IPATH_AETH_CREDIT_SHIFT)); + ohdr->u.aeth = + cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | + (qp->s_nak_state << + IPATH_AETH_CREDIT_SHIFT)); else ohdr->u.aeth = ipath_compute_aeth(qp); hwords++; + len = 0; + bth0 = OP(ACKNOWLEDGE) << 24; + bth2 = qp->s_ack_psn & IPATH_PSN_MASK; } qp->s_hdrwords = hwords; qp->s_cur_size = len; + *bth0p = bth0; + *bth2p = bth2; + return 1; bail: - return bth0; + return 0; } /** @@ -197,9 +220,16 @@ int ipath_make_rc_req(struct ipath_qp *q u32 bth2; char newreq; + /* Sending responses has higher priority over sending requests. */ + if ((qp->r_head_ack_queue != qp->s_tail_ack_queue || + (qp->s_flags & IPATH_S_ACK_PENDING) || + qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE) && + ipath_make_rc_ack(qp, ohdr, pmtu, bth0p, bth2p)) + goto done; + if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) || qp->s_rnr_timeout) - goto done; + goto bail; /* Limit the number of packets sent without an ACK. */ if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) { @@ -210,7 +240,7 @@ int ipath_make_rc_req(struct ipath_qp *q list_add_tail(&qp->timerwait, &dev->pending[dev->pending_index]); spin_unlock(&dev->pending_lock); - goto done; + goto bail; } /* header size in 32-bit words LRH+BTH = (8+12)/4. */ @@ -232,7 +262,16 @@ int ipath_make_rc_req(struct ipath_qp *q if (qp->s_cur == qp->s_tail) { /* Check if send work queue is empty. */ if (qp->s_tail == qp->s_head) - goto done; + goto bail; + /* + * If a fence is requested, wait for previous + * RDMA read and atomic operations to finish. + */ + if ((wqe->wr.send_flags & IB_SEND_FENCE) && + qp->s_num_rd_atomic) { + qp->s_flags |= IPATH_S_FENCE_PENDING; + goto bail; + } wqe->psn = qp->s_next_psn; newreq = 1; } @@ -250,7 +289,7 @@ int ipath_make_rc_req(struct ipath_qp *q /* If no credit, return. */ if (qp->s_lsn != (u32) -1 && ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) - goto done; + goto bail; wqe->lpsn = wqe->psn; if (len > pmtu) { wqe->lpsn += (len - 1) / pmtu; @@ -281,13 +320,13 @@ int ipath_make_rc_req(struct ipath_qp *q /* If no credit, return. */ if (qp->s_lsn != (u32) -1 && ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) - goto done; + goto bail; ohdr->u.rc.reth.vaddr = cpu_to_be64(wqe->wr.wr.rdma.remote_addr); ohdr->u.rc.reth.rkey = cpu_to_be32(wqe->wr.wr.rdma.rkey); ohdr->u.rc.reth.length = cpu_to_be32(len); - hwords += sizeof(struct ib_reth) / 4; + hwords += sizeof(struct ib_reth) / sizeof(u32); wqe->lpsn = wqe->psn; if (len > pmtu) { wqe->lpsn += (len - 1) / pmtu; @@ -312,14 +351,17 @@ int ipath_make_rc_req(struct ipath_qp *q break; case IB_WR_RDMA_READ: - ohdr->u.rc.reth.vaddr = - cpu_to_be64(wqe->wr.wr.rdma.remote_addr); - ohdr->u.rc.reth.rkey = - cpu_to_be32(wqe->wr.wr.rdma.rkey); - ohdr->u.rc.reth.length = cpu_to_be32(len); - qp->s_state = OP(RDMA_READ_REQUEST); - hwords += sizeof(ohdr->u.rc.reth) / 4; + /* + * Don't allow more operations to be started + * than the QP limits allow. + */ if (newreq) { + if (qp->s_num_rd_atomic >= + qp->s_max_rd_atomic) { + qp->s_flags |= IPATH_S_RDMAR_PENDING; + goto bail; + } + qp->s_num_rd_atomic++; if (qp->s_lsn != (u32) -1) qp->s_lsn++; /* @@ -330,6 +372,13 @@ int ipath_make_rc_req(struct ipath_qp *q qp->s_next_psn += (len - 1) / pmtu; wqe->lpsn = qp->s_next_psn++; } + ohdr->u.rc.reth.vaddr = + cpu_to_be64(wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = + cpu_to_be32(wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length = cpu_to_be32(len); + qp->s_state = OP(RDMA_READ_REQUEST); + hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32); ss = NULL; len = 0; if (++qp->s_cur == qp->s_size) @@ -338,32 +387,48 @@ int ipath_make_rc_req(struct ipath_qp *q case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: - if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) - qp->s_state = OP(COMPARE_SWAP); - else - qp->s_state = OP(FETCH_ADD); - ohdr->u.atomic_eth.vaddr = cpu_to_be64( - wqe->wr.wr.atomic.remote_addr); - ohdr->u.atomic_eth.rkey = cpu_to_be32( - wqe->wr.wr.atomic.rkey); - ohdr->u.atomic_eth.swap_data = cpu_to_be64( - wqe->wr.wr.atomic.swap); - ohdr->u.atomic_eth.compare_data = cpu_to_be64( - wqe->wr.wr.atomic.compare_add); - hwords += sizeof(struct ib_atomic_eth) / 4; + /* + * Don't allow more operations to be started + * than the QP limits allow. + */ if (newreq) { + if (qp->s_num_rd_atomic >= + qp->s_max_rd_atomic) { + qp->s_flags |= IPATH_S_RDMAR_PENDING; + goto bail; + } + qp->s_num_rd_atomic++; if (qp->s_lsn != (u32) -1) qp->s_lsn++; wqe->lpsn = wqe->psn; } - if (++qp->s_cur == qp->s_size) - qp->s_cur = 0; + if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) { + qp->s_state = OP(COMPARE_SWAP); + ohdr->u.atomic_eth.swap_data = cpu_to_be64( + wqe->wr.wr.atomic.swap); + ohdr->u.atomic_eth.compare_data = cpu_to_be64( + wqe->wr.wr.atomic.compare_add); + } else { + qp->s_state = OP(FETCH_ADD); + ohdr->u.atomic_eth.swap_data = cpu_to_be64( + wqe->wr.wr.atomic.compare_add); + ohdr->u.atomic_eth.compare_data = 0; + } + ohdr->u.atomic_eth.vaddr[0] = cpu_to_be32( + wqe->wr.wr.atomic.remote_addr >> 32); + ohdr->u.atomic_eth.vaddr[1] = cpu_to_be32( + wqe->wr.wr.atomic.remote_addr); + ohdr->u.atomic_eth.rkey = cpu_to_be32( + wqe->wr.wr.atomic.rkey); + hwords += sizeof(struct ib_atomic_eth) / sizeof(u32); ss = NULL; len = 0; + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; break; default: - goto done; + goto bail; } qp->s_sge.sge = wqe->sg_list[0]; qp->s_sge.sg_list = wqe->sg_list + 1; @@ -379,7 +444,7 @@ int ipath_make_rc_req(struct ipath_qp *q qp->s_psn = wqe->lpsn + 1; else { qp->s_psn++; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; } /* @@ -406,7 +471,7 @@ int ipath_make_rc_req(struct ipath_qp *q /* FALLTHROUGH */ case OP(SEND_MIDDLE): bth2 = qp->s_psn++ & IPATH_PSN_MASK; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; ss = &qp->s_sge; len = qp->s_len; @@ -442,7 +507,7 @@ int ipath_make_rc_req(struct ipath_qp *q /* FALLTHROUGH */ case OP(RDMA_WRITE_MIDDLE): bth2 = qp->s_psn++ & IPATH_PSN_MASK; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; ss = &qp->s_sge; len = qp->s_len; @@ -479,9 +544,9 @@ int ipath_make_rc_req(struct ipath_qp *q cpu_to_be32(wqe->wr.wr.rdma.rkey); ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len); qp->s_state = OP(RDMA_READ_REQUEST); - hwords += sizeof(ohdr->u.rc.reth) / 4; + hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32); bth2 = qp->s_psn++ & IPATH_PSN_MASK; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; ss = NULL; len = 0; @@ -489,20 +554,6 @@ int ipath_make_rc_req(struct ipath_qp *q if (qp->s_cur == qp->s_size) qp->s_cur = 0; break; - - case OP(RDMA_READ_REQUEST): - case OP(COMPARE_SWAP): - case OP(FETCH_ADD): - /* - * We shouldn't start anything new until this request is - * finished. The ACK will handle rescheduling us. XXX The - * number of outstanding ones is negotiated at connection - * setup time (see pg. 258,289)? XXX Also, if we support - * multiple outstanding requests, we need to check the WQE - * IB_SEND_FENCE flag and not send a new request if a RDMA - * read or atomic is pending. - */ - goto done; } if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0) bth2 |= 1 << 31; /* Request ACK. */ @@ -512,9 +563,10 @@ int ipath_make_rc_req(struct ipath_qp *q qp->s_cur_size = len; *bth0p = bth0 | (qp->s_state << 24); *bth2p = bth2; +done: return 1; -done: +bail: return 0; } @@ -524,7 +576,8 @@ done: * * This is called from ipath_rc_rcv() and only uses the receive * side QP state. - * Note that RDMA reads are handled in the send side QP state and tasklet. + * Note that RDMA reads and atomics are handled in the + * send side QP state and tasklet. */ static void send_rc_ack(struct ipath_qp *qp) { @@ -535,6 +588,10 @@ static void send_rc_ack(struct ipath_qp struct ipath_ib_header hdr; struct ipath_other_headers *ohdr; + /* Don't send ACK or NAK if a RDMA read or atomic is pending. */ + if (qp->r_head_ack_queue != qp->s_tail_ack_queue) + goto queue_ack; + /* Construct the header. */ ohdr = &hdr.u.oth; lrh0 = IPATH_LRH_BTH; @@ -548,19 +605,14 @@ static void send_rc_ack(struct ipath_qp lrh0 = IPATH_LRH_GRH; } /* read pkey_index w/o lock (its atomic) */ - bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index); + bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index) | + OP(ACKNOWLEDGE) << 24; if (qp->r_nak_state) ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | (qp->r_nak_state << IPATH_AETH_CREDIT_SHIFT)); else ohdr->u.aeth = ipath_compute_aeth(qp); - if (qp->r_ack_state >= OP(COMPARE_SWAP)) { - bth0 |= OP(ATOMIC_ACKNOWLEDGE) << 24; - ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data); - hwords += sizeof(ohdr->u.at.atomic_ack_eth) / 4; - } else - bth0 |= OP(ACKNOWLEDGE) << 24; lrh0 |= qp->remote_ah_attr.sl << 4; hdr.lrh[0] = cpu_to_be16(lrh0); hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid); @@ -574,31 +626,31 @@ static void send_rc_ack(struct ipath_qp * If we can send the ACK, clear the ACK state. */ if (ipath_verbs_send(dev->dd, hwords, (u32 *) &hdr, 0, NULL) == 0) { - qp->r_ack_state = OP(ACKNOWLEDGE); dev->n_unicast_xmit++; - } else { - /* - * We are out of PIO buffers at the moment. - * Pass responsibility for sending the ACK to the - * send tasklet so that when a PIO buffer becomes - * available, the ACK is sent ahead of other outgoing - * packets. - */ - dev->n_rc_qacks++; - spin_lock_irq(&qp->s_lock); - /* Don't coalesce if a RDMA read or atomic is pending. */ - if (qp->s_ack_state == OP(ACKNOWLEDGE) || - qp->s_ack_state < OP(RDMA_READ_REQUEST)) { - qp->s_ack_state = qp->r_ack_state; - qp->s_nak_state = qp->r_nak_state; - qp->s_ack_psn = qp->r_ack_psn; - qp->r_ack_state = OP(ACKNOWLEDGE); - } - spin_unlock_irq(&qp->s_lock); - - /* Call ipath_do_rc_send() in another thread. */ - tasklet_hi_schedule(&qp->s_task); + goto done; } + + /* + * We are out of PIO buffers at the moment. + * Pass responsibility for sending the ACK to the + * send tasklet so that when a PIO buffer becomes + * available, the ACK is sent ahead of other outgoing + * packets. + */ + dev->n_rc_qacks++; + +queue_ack: + spin_lock_irq(&qp->s_lock); + qp->s_flags |= IPATH_S_ACK_PENDING; + qp->s_nak_state = qp->r_nak_state; + qp->s_ack_psn = qp->r_ack_psn; + spin_unlock_irq(&qp->s_lock); + + /* Call ipath_do_rc_send() in another thread. */ + tasklet_hi_schedule(&qp->s_task); + +done: + return; } /** @@ -727,7 +779,7 @@ void ipath_restart_rc(struct ipath_qp *q if (wqe->wr.opcode == IB_WR_RDMA_READ) dev->n_rc_resends++; else - dev->n_rc_resends += (int)qp->s_psn - (int)psn; + dev->n_rc_resends += (qp->s_psn - psn) & IPATH_PSN_MASK; reset_psn(qp, psn); tasklet_hi_schedule(&qp->s_task); @@ -775,10 +827,6 @@ static int do_rc_ack(struct ipath_qp *qp list_del_init(&qp->timerwait); spin_unlock(&dev->pending_lock); - /* Nothing is pending to ACK/NAK. */ - if (unlikely(qp->s_last == qp->s_tail)) - goto bail; - /* * Note that NAKs implicitly ACK outstanding SEND and RDMA write * requests and implicitly NAK RDMA read and atomic requests issued @@ -806,7 +854,7 @@ static int do_rc_ack(struct ipath_qp *qp */ if ((wqe->wr.opcode == IB_WR_RDMA_READ && (opcode != OP(RDMA_READ_RESPONSE_LAST) || - ipath_cmp24(ack_psn, wqe->lpsn) != 0)) || + ipath_cmp24(ack_psn, wqe->lpsn) != 0)) || ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) && (opcode != OP(ATOMIC_ACKNOWLEDGE) || @@ -824,20 +872,33 @@ static int do_rc_ack(struct ipath_qp *qp */ goto bail; } - if (wqe->wr.opcode == IB_WR_RDMA_READ || - wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || - wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) - tasklet_hi_schedule(&qp->s_task); + if (qp->s_num_rd_atomic && + (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || + wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)) { + qp->s_num_rd_atomic--; + /* Restart sending task if fence is complete */ + if ((qp->s_flags & IPATH_S_FENCE_PENDING) && + !qp->s_num_rd_atomic) { + qp->s_flags &= ~IPATH_S_FENCE_PENDING; + tasklet_hi_schedule(&qp->s_task); + } else if (qp->s_flags & IPATH_S_RDMAR_PENDING) { + qp->s_flags &= ~IPATH_S_RDMAR_PENDING; + tasklet_hi_schedule(&qp->s_task); + } + } /* Post a send completion queue entry if requested. */ - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wqe->wr.send_flags & IB_SEND_SIGNALED)) { wc.wr_id = wqe->wr.wr_id; wc.status = IB_WC_SUCCESS; wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; wc.vendor_err = 0; wc.byte_len = wqe->length; + wc.imm_data = 0; wc.qp = &qp->ibqp; wc.src_qp = qp->remote_qpn; + wc.wc_flags = 0; wc.pkey_index = 0; wc.slid = qp->remote_ah_attr.dlid; wc.sl = qp->remote_ah_attr.sl; @@ -854,15 +915,19 @@ static int do_rc_ack(struct ipath_qp *qp if (qp->s_last == qp->s_cur) { if (++qp->s_cur >= qp->s_size) qp->s_cur = 0; + qp->s_last = qp->s_cur; + if (qp->s_last == qp->s_tail) + break; wqe = get_swqe_ptr(qp, qp->s_cur); qp->s_state = OP(SEND_LAST); qp->s_psn = wqe->psn; + } else { + if (++qp->s_last >= qp->s_size) + qp->s_last = 0; + if (qp->s_last == qp->s_tail) + break; + wqe = get_swqe_ptr(qp, qp->s_last); } - if (++qp->s_last >= qp->s_size) - qp->s_last = 0; - wqe = get_swqe_ptr(qp, qp->s_last); - if (qp->s_last == qp->s_tail) - break; } switch (aeth >> 29) { @@ -874,6 +939,18 @@ static int do_rc_ack(struct ipath_qp *qp list_add_tail(&qp->timerwait, &dev->pending[dev->pending_index]); spin_unlock(&dev->pending_lock); + /* + * If we get a partial ACK for a resent operation, + * we can stop resending the earlier packets and + * continue with the next packet the receiver wants. + */ + if (ipath_cmp24(qp->s_psn, psn) <= 0) { + reset_psn(qp, psn + 1); + tasklet_hi_schedule(&qp->s_task); + } + } else if (ipath_cmp24(qp->s_psn, psn) <= 0) { + qp->s_state = OP(SEND_LAST); + qp->s_psn = psn + 1; } ipath_get_credit(qp, aeth); qp->s_rnr_retry = qp->s_rnr_retry_cnt; @@ -884,22 +961,23 @@ static int do_rc_ack(struct ipath_qp *qp case 1: /* RNR NAK */ dev->n_rnr_naks++; + if (qp->s_last == qp->s_tail) + goto bail; if (qp->s_rnr_retry == 0) { - if (qp->s_last == qp->s_tail) - goto bail; - wc.status = IB_WC_RNR_RETRY_EXC_ERR; goto class_b; } if (qp->s_rnr_retry_cnt < 7) qp->s_rnr_retry--; - if (qp->s_last == qp->s_tail) - goto bail; /* The last valid PSN is the previous PSN. */ update_last_psn(qp, psn - 1); - dev->n_rc_resends += (int)qp->s_psn - (int)psn; + if (wqe->wr.opcode == IB_WR_RDMA_READ) + dev->n_rc_resends++; + else + dev->n_rc_resends += + (qp->s_psn - psn) & IPATH_PSN_MASK; reset_psn(qp, psn); @@ -910,26 +988,20 @@ static int do_rc_ack(struct ipath_qp *qp goto bail; case 3: /* NAK */ - /* The last valid PSN seen is the previous request's. */ - if (qp->s_last != qp->s_tail) - update_last_psn(qp, wqe->psn - 1); + if (qp->s_last == qp->s_tail) + goto bail; + /* The last valid PSN is the previous PSN. */ + update_last_psn(qp, psn - 1); switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) & IPATH_AETH_CREDIT_MASK) { case 0: /* PSN sequence error */ dev->n_seq_naks++; /* - * Back up to the responder's expected PSN. XXX + * Back up to the responder's expected PSN. * Note that we might get a NAK in the middle of an * RDMA READ response which terminates the RDMA * READ. */ - if (qp->s_last == qp->s_tail) - break; - - if (ipath_cmp24(psn, wqe->psn) < 0) - break; - - /* Retry the request. */ ipath_restart_rc(qp, psn, &wc); break; @@ -1003,6 +1075,7 @@ static inline void ipath_rc_rcv_resp(str u32 psn, u32 hdrsize, u32 pmtu, int header_in_data) { + struct ipath_swqe *wqe; unsigned long flags; struct ib_wc wc; int diff; @@ -1032,6 +1105,10 @@ static inline void ipath_rc_rcv_resp(str goto ack_done; } + if (unlikely(qp->s_last == qp->s_tail)) + goto ack_done; + wqe = get_swqe_ptr(qp, qp->s_last); + switch (opcode) { case OP(ACKNOWLEDGE): case OP(ATOMIC_ACKNOWLEDGE): @@ -1042,38 +1119,49 @@ static inline void ipath_rc_rcv_resp(str aeth = be32_to_cpu(((__be32 *) data)[0]); data += sizeof(__be32); } - if (opcode == OP(ATOMIC_ACKNOWLEDGE)) - *(u64 *) qp->s_sge.sge.vaddr = *(u64 *) data; + if (opcode == OP(ATOMIC_ACKNOWLEDGE)) { + u64 val; + + if (!header_in_data) { + __be32 *p = ohdr->u.at.atomic_ack_eth; + + val = ((u64) be32_to_cpu(p[0]) << 32) | + be32_to_cpu(p[1]); + } else + val = be64_to_cpu(((__be64 *) data)[0]); + *(u64 *) wqe->sg_list[0].vaddr = val; + } if (!do_rc_ack(qp, aeth, psn, opcode) || opcode != OP(RDMA_READ_RESPONSE_FIRST)) goto ack_done; hdrsize += 4; + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; /* - * do_rc_ack() has already checked the PSN so skip - * the sequence check. + * If this is a response to a resent RDMA read, we + * have to be careful to copy the data to the right + * location. */ - goto rdma_read; + qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge, + wqe, psn, pmtu); + goto read_middle; case OP(RDMA_READ_RESPONSE_MIDDLE): /* no AETH, no ACK */ if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { dev->n_rdma_seq++; - if (qp->s_last != qp->s_tail) - ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); goto ack_done; } - rdma_read: - if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST))) - goto ack_done; + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; + read_middle: if (unlikely(tlen != (hdrsize + pmtu + 4))) - goto ack_done; - if (unlikely(pmtu >= qp->s_len)) - goto ack_done; + goto ack_len_err; + if (unlikely(pmtu >= qp->s_rdma_read_len)) + goto ack_len_err; + /* We got a response so update the timeout. */ - if (unlikely(qp->s_last == qp->s_tail || - get_swqe_ptr(qp, qp->s_last)->wr.opcode != - IB_WR_RDMA_READ)) - goto ack_done; spin_lock(&dev->pending_lock); if (qp->s_rnr_timeout == 0 && !list_empty(&qp->timerwait)) list_move_tail(&qp->timerwait, @@ -1082,67 +1170,97 @@ static inline void ipath_rc_rcv_resp(str /* * Update the RDMA receive state but do the copy w/o * holding the locks and blocking interrupts. - * XXX Yet another place that affects relaxed RDMA order - * since we don't want s_sge modified. */ - qp->s_len -= pmtu; + qp->s_rdma_read_len -= pmtu; update_last_psn(qp, psn); spin_unlock_irqrestore(&qp->s_lock, flags); - ipath_copy_sge(&qp->s_sge, data, pmtu); + ipath_copy_sge(&qp->s_rdma_read_sge, data, pmtu); goto bail; - case OP(RDMA_READ_RESPONSE_LAST): - /* ACKs READ req. */ + case OP(RDMA_READ_RESPONSE_ONLY): if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { dev->n_rdma_seq++; - if (qp->s_last != qp->s_tail) - ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); goto ack_done; } - /* FALLTHROUGH */ - case OP(RDMA_READ_RESPONSE_ONLY): - if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST))) - goto ack_done; + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; + /* Get the number of bytes the message was padded by. */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + /* + * Check that the data size is >= 0 && <= pmtu. + * Remember to account for the AETH header (4) and + * ICRC (4). + */ + if (unlikely(tlen < (hdrsize + pad + 8))) + goto ack_len_err; /* - * Get the number of bytes the message was padded by. + * If this is a response to a resent RDMA read, we + * have to be careful to copy the data to the right + * location. */ + qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge, + wqe, psn, pmtu); + goto read_last; + + case OP(RDMA_READ_RESPONSE_LAST): + /* ACKs READ req. */ + if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { + dev->n_rdma_seq++; + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + goto ack_done; + } + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; + /* Get the number of bytes the message was padded by. */ pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; /* * Check that the data size is >= 1 && <= pmtu. * Remember to account for the AETH header (4) and * ICRC (4). */ - if (unlikely(tlen <= (hdrsize + pad + 8))) { - /* XXX Need to generate an error CQ entry. */ - goto ack_done; - } + if (unlikely(tlen <= (hdrsize + pad + 8))) + goto ack_len_err; + read_last: tlen -= hdrsize + pad + 8; - if (unlikely(tlen != qp->s_len)) { - /* XXX Need to generate an error CQ entry. */ - goto ack_done; - } + if (unlikely(tlen != qp->s_rdma_read_len)) + goto ack_len_err; if (!header_in_data) aeth = be32_to_cpu(ohdr->u.aeth); else { aeth = be32_to_cpu(((__be32 *) data)[0]); data += sizeof(__be32); } - ipath_copy_sge(&qp->s_sge, data, tlen); - if (do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST))) { - /* - * Change the state so we contimue - * processing new requests and wake up the - * tasklet if there are posted sends. - */ - qp->s_state = OP(SEND_LAST); - if (qp->s_tail != qp->s_head) - tasklet_hi_schedule(&qp->s_task); - } + ipath_copy_sge(&qp->s_rdma_read_sge, data, tlen); + (void) do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST)); goto ack_done; } ack_done: spin_unlock_irqrestore(&qp->s_lock, flags); + goto bail; + +ack_op_err: + wc.status = IB_WC_LOC_QP_OP_ERR; + goto ack_err; + +ack_len_err: + wc.status = IB_WC_LOC_LEN_ERR; +ack_err: + wc.wr_id = wqe->wr.wr_id; + wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc.vendor_err = 0; + wc.byte_len = 0; + wc.imm_data = 0; + wc.qp = &qp->ibqp; + wc.src_qp = qp->remote_qpn; + wc.wc_flags = 0; + wc.pkey_index = 0; + wc.slid = qp->remote_ah_attr.dlid; + wc.sl = qp->remote_ah_attr.sl; + wc.dlid_path_bits = 0; + wc.port_num = 0; + ipath_sqerror_qp(qp, &wc); bail: return; } @@ -1162,7 +1280,7 @@ bail: * incoming RC packet for the given QP. * Called at interrupt level. * Return 1 if no more processing is needed; otherwise return 0 to - * schedule a response to be sent and the s_lock unlocked. + * schedule a response to be sent. */ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, struct ipath_other_headers *ohdr, @@ -1173,25 +1291,23 @@ static inline int ipath_rc_rcv_error(str int diff, int header_in_data) { - struct ib_reth *reth; + struct ipath_ack_entry *e; + u8 i, prev; + int old_req; if (diff > 0) { /* * Packet sequence error. * A NAK will ACK earlier sends and RDMA writes. - * Don't queue the NAK if a RDMA read, atomic, or - * NAK is pending though. + * Don't queue the NAK if we already sent one. */ - if (qp->s_ack_state != OP(ACKNOWLEDGE) || - qp->r_nak_state != 0) - goto done; - if (qp->r_ack_state < OP(COMPARE_SWAP)) { - qp->r_ack_state = OP(SEND_ONLY); + if (!qp->r_nak_state) { qp->r_nak_state = IB_NAK_PSN_ERROR; /* Use the expected PSN. */ qp->r_ack_psn = qp->r_psn; + goto send_ack; } - goto send_ack; + goto done; } /* @@ -1204,8 +1320,46 @@ static inline int ipath_rc_rcv_error(str * can coalesce an outstanding duplicate ACK. We have to * send the earliest so that RDMA reads can be restarted at * the requester's expected PSN. + * + * First, find where this duplicate PSN falls within the + * ACKs previously sent. */ - if (opcode == OP(RDMA_READ_REQUEST)) { + psn &= IPATH_PSN_MASK; + e = NULL; + old_req = 1; + spin_lock_irq(&qp->s_lock); + for (i = qp->r_head_ack_queue; ; i = prev) { + if (i == qp->s_tail_ack_queue) + old_req = 0; + if (i) + prev = i - 1; + else + prev = IPATH_MAX_RDMA_ATOMIC; + if (prev == qp->r_head_ack_queue) { + e = NULL; + break; + } + e = &qp->s_ack_queue[prev]; + if (!e->opcode) { + e = NULL; + break; + } + if (ipath_cmp24(psn, e->psn) >= 0) + break; + } + switch (opcode) { + case OP(RDMA_READ_REQUEST): { + struct ib_reth *reth; + u32 offset; + u32 len; + + /* + * If we didn't find the RDMA read request in the ack queue, + * or the send tasklet is already backed up to send an + * earlier entry, we can ignore this request. + */ + if (!e || e->opcode != OP(RDMA_READ_REQUEST) || old_req) + goto unlock_done; /* RETH comes after BTH */ if (!header_in_data) reth = &ohdr->u.rc.reth; @@ -1214,88 +1368,87 @@ static inline int ipath_rc_rcv_error(str data += sizeof(*reth); } /* - * If we receive a duplicate RDMA request, it means the - * requester saw a sequence error and needs to restart - * from an earlier point. We can abort the current - * RDMA read send in that case. + * Address range must be a subset of the original + * request and start on pmtu boundaries. + * We reuse the old ack_queue slot since the requester + * should not back up and request an earlier PSN for the + * same request. */ - spin_lock_irq(&qp->s_lock); - if (qp->s_ack_state != OP(ACKNOWLEDGE) && - (qp->s_hdrwords || ipath_cmp24(psn, qp->s_ack_psn) >= 0)) { - /* - * We are already sending earlier requested data. - * Don't abort it to send later out of sequence data. - */ - spin_unlock_irq(&qp->s_lock); - goto done; - } - qp->s_rdma_len = be32_to_cpu(reth->length); - if (qp->s_rdma_len != 0) { + offset = ((psn - e->psn) & IPATH_PSN_MASK) * + ib_mtu_enum_to_int(qp->path_mtu); + len = be32_to_cpu(reth->length); + if (unlikely(offset + len > e->rdma_sge.sge.sge_length)) + goto unlock_done; + if (len != 0) { u32 rkey = be32_to_cpu(reth->rkey); u64 vaddr = be64_to_cpu(reth->vaddr); int ok; - /* - * Address range must be a subset of the original - * request and start on pmtu boundaries. - */ - ok = ipath_rkey_ok(qp, &qp->s_rdma_sge, - qp->s_rdma_len, vaddr, rkey, + ok = ipath_rkey_ok(qp, &e->rdma_sge, + len, vaddr, rkey, IB_ACCESS_REMOTE_READ); - if (unlikely(!ok)) { - spin_unlock_irq(&qp->s_lock); - goto done; - } + if (unlikely(!ok)) + goto unlock_done; } else { - qp->s_rdma_sge.sg_list = NULL; - qp->s_rdma_sge.num_sge = 0; - qp->s_rdma_sge.sge.mr = NULL; - qp->s_rdma_sge.sge.vaddr = NULL; - qp->s_rdma_sge.sge.length = 0; - qp->s_rdma_sge.sge.sge_length = 0; + e->rdma_sge.sg_list = NULL; + e->rdma_sge.num_sge = 0; + e->rdma_sge.sge.mr = NULL; + e->rdma_sge.sge.vaddr = NULL; + e->rdma_sge.sge.length = 0; + e->rdma_sge.sge.sge_length = 0; } - qp->s_ack_state = opcode; - qp->s_ack_psn = psn; - spin_unlock_irq(&qp->s_lock); - tasklet_hi_schedule(&qp->s_task); - goto send_ack; + e->psn = psn; + qp->s_ack_state = OP(ACKNOWLEDGE); + qp->s_tail_ack_queue = prev; + break; } - /* - * A pending RDMA read will ACK anything before it so - * ignore earlier duplicate requests. - */ - if (qp->s_ack_state != OP(ACKNOWLEDGE)) - goto done; - - /* - * If an ACK is pending, don't replace the pending ACK - * with an earlier one since the later one will ACK the earlier. - * Also, if we already have a pending atomic, send it. - */ - if (qp->r_ack_state != OP(ACKNOWLEDGE) && - (ipath_cmp24(psn, qp->r_ack_psn) <= 0 || - qp->r_ack_state >= OP(COMPARE_SWAP))) - goto send_ack; - switch (opcode) { case OP(COMPARE_SWAP): - case OP(FETCH_ADD): + case OP(FETCH_ADD): { /* - * Check for the PSN of the last atomic operation - * performed and resend the result if found. + * If we didn't find the atomic request in the ack queue + * or the send tasklet is already backed up to send an + * earlier entry, we can ignore this request. */ - if ((psn & IPATH_PSN_MASK) != qp->r_atomic_psn) - goto done; + if (!e || e->opcode != (u8) opcode || old_req) + goto unlock_done; + qp->s_ack_state = OP(ACKNOWLEDGE); + qp->s_tail_ack_queue = prev; + break; + } + + default: + if (old_req) + goto unlock_done; + /* + * Resend the most recent ACK if this request is + * after all the previous RDMA reads and atomics. + */ + if (i == qp->r_head_ack_queue) { + spin_unlock_irq(&qp->s_lock); + qp->r_nak_state = 0; + qp->r_ack_psn = qp->r_psn - 1; + goto send_ack; + } + /* + * Resend the RDMA read or atomic op which + * ACKs this duplicate request. + */ + qp->s_ack_state = OP(ACKNOWLEDGE); + qp->s_tail_ack_queue = i; break; } - qp->r_ack_state = opcode; qp->r_nak_state = 0; - qp->r_ack_psn = psn; -send_ack: - return 0; + spin_unlock_irq(&qp->s_lock); + tasklet_hi_schedule(&qp->s_task); +unlock_done: + spin_unlock_irq(&qp->s_lock); done: return 1; + +send_ack: + return 0; } static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err) @@ -1391,15 +1544,7 @@ void ipath_rc_rcv(struct ipath_ibdev *de opcode == OP(SEND_LAST_WITH_IMMEDIATE)) break; nack_inv: - /* - * A NAK will ACK earlier sends and RDMA writes. - * Don't queue the NAK if a RDMA read, atomic, or NAK - * is pending though. - */ - if (qp->r_ack_state >= OP(COMPARE_SWAP)) - goto send_ack; ipath_rc_error(qp, IB_WC_REM_INV_REQ_ERR); - qp->r_ack_state = OP(SEND_ONLY); qp->r_nak_state = IB_NAK_INVALID_REQUEST; qp->r_ack_psn = qp->r_psn; goto send_ack; @@ -1441,9 +1586,8 @@ void ipath_rc_rcv(struct ipath_ibdev *de * Don't queue the NAK if a RDMA read or atomic * is pending though. */ - if (qp->r_ack_state >= OP(COMPARE_SWAP)) - goto send_ack; - qp->r_ack_state = OP(SEND_ONLY); + if (qp->r_nak_state) + goto done; qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer; qp->r_ack_psn = qp->r_psn; goto send_ack; @@ -1567,7 +1711,19 @@ void ipath_rc_rcv(struct ipath_ibdev *de goto rnr_nak; goto send_last_imm; - case OP(RDMA_READ_REQUEST): + case OP(RDMA_READ_REQUEST): { + struct ipath_ack_entry *e; + u32 len; + u8 next; + + if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ))) + goto nack_acc; + next = qp->r_head_ack_queue + 1; + if (next > IPATH_MAX_RDMA_ATOMIC) + next = 0; + if (unlikely(next == qp->s_tail_ack_queue)) + goto nack_inv; + e = &qp->s_ack_queue[qp->r_head_ack_queue]; /* RETH comes after BTH */ if (!header_in_data) reth = &ohdr->u.rc.reth; @@ -1575,72 +1731,75 @@ void ipath_rc_rcv(struct ipath_ibdev *de reth = (struct ib_reth *)data; data += sizeof(*reth); } - if (unlikely(!(qp->qp_access_flags & - IB_ACCESS_REMOTE_READ))) - goto nack_acc; - spin_lock_irq(&qp->s_lock); - qp->s_rdma_len = be32_to_cpu(reth->length); - if (qp->s_rdma_len != 0) { + len = be32_to_cpu(reth->length); + if (len) { u32 rkey = be32_to_cpu(reth->rkey); u64 vaddr = be64_to_cpu(reth->vaddr); int ok; /* Check rkey & NAK */ - ok = ipath_rkey_ok(qp, &qp->s_rdma_sge, - qp->s_rdma_len, vaddr, rkey, - IB_ACCESS_REMOTE_READ); - if (unlikely(!ok)) { - spin_unlock_irq(&qp->s_lock); + ok = ipath_rkey_ok(qp, &e->rdma_sge, len, vaddr, + rkey, IB_ACCESS_REMOTE_READ); + if (unlikely(!ok)) goto nack_acc; - } /* * Update the next expected PSN. We add 1 later * below, so only add the remainder here. */ - if (qp->s_rdma_len > pmtu) - qp->r_psn += (qp->s_rdma_len - 1) / pmtu; + if (len > pmtu) + qp->r_psn += (len - 1) / pmtu; } else { - qp->s_rdma_sge.sg_list = NULL; - qp->s_rdma_sge.num_sge = 0; - qp->s_rdma_sge.sge.mr = NULL; - qp->s_rdma_sge.sge.vaddr = NULL; - qp->s_rdma_sge.sge.length = 0; - qp->s_rdma_sge.sge.sge_length = 0; + e->rdma_sge.sg_list = NULL; + e->rdma_sge.num_sge = 0; + e->rdma_sge.sge.mr = NULL; + e->rdma_sge.sge.vaddr = NULL; + e->rdma_sge.sge.length = 0; + e->rdma_sge.sge.sge_length = 0; } + e->opcode = opcode; + e->psn = psn; /* * We need to increment the MSN here instead of when we * finish sending the result since a duplicate request would * increment it more than once. */ qp->r_msn++; - - qp->s_ack_state = opcode; - qp->s_ack_psn = psn; - spin_unlock_irq(&qp->s_lock); - qp->r_psn++; qp->r_state = opcode; qp->r_nak_state = 0; + barrier(); + qp->r_head_ack_queue = next; /* Call ipath_do_rc_send() in another thread. */ tasklet_hi_schedule(&qp->s_task); goto done; + } case OP(COMPARE_SWAP): case OP(FETCH_ADD): { struct ib_atomic_eth *ateth; + struct ipath_ack_entry *e; u64 vaddr; + atomic64_t *maddr; u64 sdata; u32 rkey; + u8 next; + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_ATOMIC))) + goto nack_acc; + next = qp->r_head_ack_queue + 1; + if (next > IPATH_MAX_RDMA_ATOMIC) + next = 0; + if (unlikely(next == qp->s_tail_ack_queue)) + goto nack_inv; if (!header_in_data) ateth = &ohdr->u.atomic_eth; - else { + else ateth = (struct ib_atomic_eth *)data; - data += sizeof(*ateth); - } - vaddr = be64_to_cpu(ateth->vaddr); + vaddr = ((u64) be32_to_cpu(ateth->vaddr[0]) << 32) | + be32_to_cpu(ateth->vaddr[1]); if (unlikely(vaddr & (sizeof(u64) - 1))) goto nack_inv; rkey = be32_to_cpu(ateth->rkey); @@ -1649,63 +1808,50 @@ void ipath_rc_rcv(struct ipath_ibdev *de sizeof(u64), vaddr, rkey, IB_ACCESS_REMOTE_ATOMIC))) goto nack_acc; - if (unlikely(!(qp->qp_access_flags & - IB_ACCESS_REMOTE_ATOMIC))) - goto nack_acc; /* Perform atomic OP and save result. */ + maddr = (atomic64_t *) qp->r_sge.sge.vaddr; sdata = be64_to_cpu(ateth->swap_data); - spin_lock_irq(&dev->pending_lock); - qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr; - if (opcode == OP(FETCH_ADD)) - *(u64 *) qp->r_sge.sge.vaddr = - qp->r_atomic_data + sdata; - else if (qp->r_atomic_data == - be64_to_cpu(ateth->compare_data)) - *(u64 *) qp->r_sge.sge.vaddr = sdata; - spin_unlock_irq(&dev->pending_lock); + e = &qp->s_ack_queue[qp->r_head_ack_queue]; + e->atomic_data = (opcode == OP(FETCH_ADD)) ? + (u64) atomic64_add_return(sdata, maddr) - sdata : + (u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr, + be64_to_cpu(ateth->compare_data), + sdata); + e->opcode = opcode; + e->psn = psn & IPATH_PSN_MASK; qp->r_msn++; - qp->r_atomic_psn = psn & IPATH_PSN_MASK; - psn |= 1 << 31; - break; + qp->r_psn++; + qp->r_state = opcode; + qp->r_nak_state = 0; + barrier(); + qp->r_head_ack_queue = next; + + /* Call ipath_do_rc_send() in another thread. */ + tasklet_hi_schedule(&qp->s_task); + + goto done; } default: - /* Drop packet for unknown opcodes. */ - goto done; + /* NAK unknown opcodes. */ + goto nack_inv; } qp->r_psn++; qp->r_state = opcode; + qp->r_ack_psn = psn; qp->r_nak_state = 0; /* Send an ACK if requested or required. */ - if (psn & (1 << 31)) { - /* - * Coalesce ACKs unless there is a RDMA READ or - * ATOMIC pending. - */ - if (qp->r_ack_state < OP(COMPARE_SWAP)) { - qp->r_ack_state = opcode; - qp->r_ack_psn = psn; - } + if (psn & (1 << 31)) goto send_ack; - } goto done; nack_acc: - /* - * A NAK will ACK earlier sends and RDMA writes. - * Don't queue the NAK if a RDMA read, atomic, or NAK - * is pending though. - */ - if (qp->r_ack_state < OP(COMPARE_SWAP)) { - ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR); - qp->r_ack_state = OP(RDMA_WRITE_ONLY); - qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; - qp->r_ack_psn = qp->r_psn; - } + ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR); + qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; + qp->r_ack_psn = qp->r_psn; + send_ack: - /* Send ACK right away unless the send tasklet has a pending ACK. */ - if (qp->s_ack_state == OP(ACKNOWLEDGE)) - send_rc_ack(qp); + send_rc_ack(qp); done: return; diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h index dffc760..c182bcd 100644 --- a/drivers/infiniband/hw/ipath/ipath_registers.h +++ b/drivers/infiniband/hw/ipath/ipath_registers.h @@ -126,9 +126,18 @@ #define INFINIPATH_E_INVALIDADDR 0x0 #define INFINIPATH_E_RESET 0x0004000000000000ULL #define INFINIPATH_E_HARDWARE 0x0008000000000000ULL +/* + * this is used to print "common" packet errors only when the + * __IPATH_ERRPKTDBG bit is set in ipath_debug. + */ +#define INFINIPATH_E_PKTERRS ( INFINIPATH_E_SPKTLEN \ + | INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_RVCRC \ + | INFINIPATH_E_RICRC | INFINIPATH_E_RSHORTPKTLEN \ + | INFINIPATH_E_REBP ) + /* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */ /* TXEMEMPARITYERR bit 0: PIObuf, 1: PIOpbc, 2: launchfifo - * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: eagerTID, 3: expTID + * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: expTID, 3: eagerTID * bit 4: flag buffer, 5: datainfo, 6: header info */ #define INFINIPATH_HWE_TXEMEMPARITYERR_MASK 0xFULL #define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40 @@ -143,8 +152,8 @@ #define INFINIPATH_HWE_TXEMEMPARITYERR_P /* rxe mem parity errors (shift by INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) */ #define INFINIPATH_HWE_RXEMEMPARITYERR_RCVBUF 0x01ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_LOOKUPQ 0x02ULL -#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x04ULL -#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x08ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x04ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x08ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF 0x10ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO 0x40ULL @@ -299,13 +308,6 @@ #define INFINIPATH_XGXS_MDIOADDR_SHIFT 4 #define INFINIPATH_XGXS_RX_POL_SHIFT 19 #define INFINIPATH_XGXS_RX_POL_MASK 0xfULL -#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL /* 40 bits valid */ - -/* TID entries (memory), HT-only */ -#define INFINIPATH_RT_VALID 0x8000000000000000ULL -#define INFINIPATH_RT_ADDR_SHIFT 0 -#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFF -#define INFINIPATH_RT_BUFSIZE_SHIFT 48 /* * IPATH_PIO_MAXIBHDR is the max IB header size allowed for in our diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index e86cb17..d9c2a9b 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -202,6 +202,7 @@ int ipath_get_rwqe(struct ipath_qp *qp, wq->tail = tail; ret = 1; + qp->r_wrid_valid = 1; if (handler) { u32 n; @@ -229,7 +230,6 @@ int ipath_get_rwqe(struct ipath_qp *qp, } } spin_unlock_irqrestore(&rq->lock, flags); - qp->r_wrid_valid = 1; bail: return ret; @@ -255,6 +255,7 @@ static void ipath_ruc_loopback(struct ip unsigned long flags; struct ib_wc wc; u64 sdata; + atomic64_t *maddr; qp = ipath_lookup_qpn(&dev->qp_table, sqp->remote_qpn); if (!qp) { @@ -265,7 +266,8 @@ static void ipath_ruc_loopback(struct ip again: spin_lock_irqsave(&sqp->s_lock, flags); - if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK)) { + if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK) || + qp->s_rnr_timeout) { spin_unlock_irqrestore(&sqp->s_lock, flags); goto done; } @@ -310,7 +312,7 @@ again: sqp->s_rnr_retry--; dev->n_rnr_naks++; sqp->s_rnr_timeout = - ib_ipath_rnr_table[sqp->r_min_rnr_timer]; + ib_ipath_rnr_table[qp->r_min_rnr_timer]; ipath_insert_rnr_queue(sqp); goto done; } @@ -343,20 +345,22 @@ again: wc.sl = sqp->remote_ah_attr.sl; wc.dlid_path_bits = 0; wc.port_num = 0; + spin_lock_irqsave(&sqp->s_lock, flags); ipath_sqerror_qp(sqp, &wc); + spin_unlock_irqrestore(&sqp->s_lock, flags); goto done; } break; case IB_WR_RDMA_READ: + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_READ))) + goto acc_err; if (unlikely(!ipath_rkey_ok(qp, &sqp->s_sge, wqe->length, wqe->wr.wr.rdma.remote_addr, wqe->wr.wr.rdma.rkey, IB_ACCESS_REMOTE_READ))) goto acc_err; - if (unlikely(!(qp->qp_access_flags & - IB_ACCESS_REMOTE_READ))) - goto acc_err; qp->r_sge.sge = wqe->sg_list[0]; qp->r_sge.sg_list = wqe->sg_list + 1; qp->r_sge.num_sge = wqe->wr.num_sge; @@ -364,22 +368,22 @@ again: case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_ATOMIC))) + goto acc_err; if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64), - wqe->wr.wr.rdma.remote_addr, - wqe->wr.wr.rdma.rkey, + wqe->wr.wr.atomic.remote_addr, + wqe->wr.wr.atomic.rkey, IB_ACCESS_REMOTE_ATOMIC))) goto acc_err; /* Perform atomic OP and save result. */ - sdata = wqe->wr.wr.atomic.swap; - spin_lock_irqsave(&dev->pending_lock, flags); - qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr; - if (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) - *(u64 *) qp->r_sge.sge.vaddr = - qp->r_atomic_data + sdata; - else if (qp->r_atomic_data == wqe->wr.wr.atomic.compare_add) - *(u64 *) qp->r_sge.sge.vaddr = sdata; - spin_unlock_irqrestore(&dev->pending_lock, flags); - *(u64 *) sqp->s_sge.sge.vaddr = qp->r_atomic_data; + maddr = (atomic64_t *) qp->r_sge.sge.vaddr; + sdata = wqe->wr.wr.atomic.compare_add; + *(u64 *) sqp->s_sge.sge.vaddr = + (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) ? + (u64) atomic64_add_return(sdata, maddr) - sdata : + (u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr, + sdata, wqe->wr.wr.atomic.swap); goto send_comp; default: @@ -440,7 +444,7 @@ again: send_comp: sqp->s_rnr_retry = sqp->s_rnr_retry_cnt; - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &sqp->s_flags) || + if (!(sqp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wqe->wr.send_flags & IB_SEND_SIGNALED)) { wc.wr_id = wqe->wr.wr_id; wc.status = IB_WC_SUCCESS; @@ -502,7 +506,7 @@ void ipath_no_bufs_available(struct ipat * We clear the tasklet flag now since we are committing to return * from the tasklet function. */ - clear_bit(IPATH_S_BUSY, &qp->s_flags); + clear_bit(IPATH_S_BUSY, &qp->s_busy); tasklet_unlock(&qp->s_task); want_buffer(dev->dd); dev->n_piowait++; @@ -541,6 +545,9 @@ int ipath_post_ruc_send(struct ipath_qp wr->sg_list[0].addr & (sizeof(u64) - 1))) { ret = -EINVAL; goto bail; + } else if (wr->opcode >= IB_WR_RDMA_READ && !qp->s_max_rd_atomic) { + ret = -EINVAL; + goto bail; } /* IB spec says that num_sge == 0 is OK. */ if (wr->num_sge > qp->s_max_sge) { @@ -647,7 +654,7 @@ void ipath_do_ruc_send(unsigned long dat u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); struct ipath_other_headers *ohdr; - if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags)) + if (test_and_set_bit(IPATH_S_BUSY, &qp->s_busy)) goto bail; if (unlikely(qp->remote_ah_attr.dlid == dev->dd->ipath_lid)) { @@ -683,19 +690,15 @@ again: */ spin_lock_irqsave(&qp->s_lock, flags); - /* Sending responses has higher priority over sending requests. */ - if (qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE && - (bth0 = ipath_make_rc_ack(qp, ohdr, pmtu)) != 0) - bth2 = qp->s_ack_psn++ & IPATH_PSN_MASK; - else if (!((qp->ibqp.qp_type == IB_QPT_RC) ? - ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) : - ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) { + if (!((qp->ibqp.qp_type == IB_QPT_RC) ? + ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) : + ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) { /* * Clear the busy bit before unlocking to avoid races with * adding new work queue items and then failing to process * them. */ - clear_bit(IPATH_S_BUSY, &qp->s_flags); + clear_bit(IPATH_S_BUSY, &qp->s_busy); spin_unlock_irqrestore(&qp->s_lock, flags); goto bail; } @@ -728,7 +731,7 @@ again: goto again; clear: - clear_bit(IPATH_S_BUSY, &qp->s_flags); + clear_bit(IPATH_S_BUSY, &qp->s_busy); bail: return; } diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c index 30a8259..9307f71 100644 --- a/drivers/infiniband/hw/ipath/ipath_stats.c +++ b/drivers/infiniband/hw/ipath/ipath_stats.c @@ -207,7 +207,7 @@ void ipath_get_faststats(unsigned long o * don't access the chip while running diags, or memory diags can * fail */ - if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) || + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) || ipath_diag_inuse) /* but re-arm the timer, for diags case; won't hurt other */ goto done; @@ -237,11 +237,13 @@ void ipath_get_faststats(unsigned long o if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) && time_after(jiffies, dd->ipath_unmasktime)) { char ebuf[256]; - ipath_decode_err(ebuf, sizeof ebuf, + int iserr; + iserr = ipath_decode_err(ebuf, sizeof ebuf, (dd->ipath_maskederrs & ~dd-> ipath_ignorederrs)); if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) & - ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) + ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL | + INFINIPATH_E_PKTERRS )) ipath_dev_err(dd, "Re-enabling masked errors " "(%s)\n", ebuf); else { @@ -252,8 +254,12 @@ void ipath_get_faststats(unsigned long o * them. So only complain about these at debug * level. */ - ipath_dbg("Disabling frequent queue full errors " - "(%s)\n", ebuf); + if (iserr) + ipath_dbg("Re-enabling queue full errors (%s)\n", + ebuf); + else + ipath_cdbg(ERRPKT, "Re-enabling packet" + " problem interrupt (%s)\n", ebuf); } dd->ipath_maskederrs = dd->ipath_ignorederrs; ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c index 325d663..1c2b03c 100644 --- a/drivers/infiniband/hw/ipath/ipath_uc.c +++ b/drivers/infiniband/hw/ipath/ipath_uc.c @@ -42,7 +42,7 @@ static void complete_last_send(struct ip { if (++qp->s_last == qp->s_size) qp->s_last = 0; - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wqe->wr.send_flags & IB_SEND_SIGNALED)) { wc->wr_id = wqe->wr.wr_id; wc->status = IB_WC_SUCCESS; @@ -344,13 +344,13 @@ void ipath_uc_rcv(struct ipath_ibdev *de send_first: if (qp->r_reuse_sge) { qp->r_reuse_sge = 0; - qp->r_sge = qp->s_rdma_sge; + qp->r_sge = qp->s_rdma_read_sge; } else if (!ipath_get_rwqe(qp, 0)) { dev->n_pkt_drops++; goto done; } /* Save the WQE so we can reuse it in case of an error. */ - qp->s_rdma_sge = qp->r_sge; + qp->s_rdma_read_sge = qp->r_sge; qp->r_rcv_len = 0; if (opcode == OP(SEND_ONLY)) goto send_last; diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c index 9a3e546..a518f7c 100644 --- a/drivers/infiniband/hw/ipath/ipath_ud.c +++ b/drivers/infiniband/hw/ipath/ipath_ud.c @@ -308,6 +308,11 @@ int ipath_post_ud_send(struct ipath_qp * goto bail; } + if (wr->wr.ud.ah->pd != qp->ibqp.pd) { + ret = -EPERM; + goto bail; + } + /* IB spec says that num_sge == 0 is OK. */ if (wr->num_sge > qp->s_max_sge) { ret = -EINVAL; @@ -467,7 +472,7 @@ int ipath_post_ud_send(struct ipath_qp * done: /* Queue the completion status entry. */ - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wr->send_flags & IB_SEND_SIGNALED)) { wc.wr_id = wr->wr_id; wc.status = IB_WC_SUCCESS; @@ -647,6 +652,7 @@ void ipath_ud_rcv(struct ipath_ibdev *de ipath_skip_sge(&qp->r_sge, sizeof(struct ib_grh)); ipath_copy_sge(&qp->r_sge, data, wc.byte_len - sizeof(struct ib_grh)); + qp->r_wrid_valid = 0; wc.wr_id = qp->r_wr_id; wc.status = IB_WC_SUCCESS; wc.opcode = IB_WC_RECV; diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index 2aaacdb..18c6df2 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -438,6 +438,10 @@ void ipath_ib_rcv(struct ipath_ibdev *de struct ipath_mcast *mcast; struct ipath_mcast_qp *p; + if (lnh != IPATH_LRH_GRH) { + dev->n_pkt_drops++; + goto bail; + } mcast = ipath_mcast_find(&hdr->u.l.grh.dgid); if (mcast == NULL) { dev->n_pkt_drops++; @@ -445,8 +449,7 @@ void ipath_ib_rcv(struct ipath_ibdev *de } dev->n_multicast_rcv++; list_for_each_entry_rcu(p, &mcast->qp_list, list) - ipath_qp_rcv(dev, hdr, lnh == IPATH_LRH_GRH, data, - tlen, p->qp); + ipath_qp_rcv(dev, hdr, 1, data, tlen, p->qp); /* * Notify ipath_multicast_detach() if it is waiting for us * to finish. @@ -773,7 +776,6 @@ int ipath_verbs_send(struct ipath_devdat /* +1 is for the qword padding of pbc */ plen = hdrwords + ((len + 3) >> 2) + 1; if (unlikely((plen << 2) > dd->ipath_ibmaxlen)) { - ipath_dbg("packet len 0x%x too long, failing\n", plen); ret = -EINVAL; goto bail; } @@ -980,14 +982,14 @@ static int ipath_query_device(struct ib_ props->max_cqe = ib_ipath_max_cqes; props->max_mr = dev->lk_table.max; props->max_pd = ib_ipath_max_pds; - props->max_qp_rd_atom = 1; - props->max_qp_init_rd_atom = 1; + props->max_qp_rd_atom = IPATH_MAX_RDMA_ATOMIC; + props->max_qp_init_rd_atom = 255; /* props->max_res_rd_atom */ props->max_srq = ib_ipath_max_srqs; props->max_srq_wr = ib_ipath_max_srq_wrs; props->max_srq_sge = ib_ipath_max_srq_sges; /* props->local_ca_ack_delay */ - props->atomic_cap = IB_ATOMIC_HCA; + props->atomic_cap = IB_ATOMIC_GLOB; props->max_pkeys = ipath_get_npkeys(dev->dd); props->max_mcast_grp = ib_ipath_max_mcast_grps; props->max_mcast_qp_attach = ib_ipath_max_mcast_qp_attached; @@ -1557,7 +1559,6 @@ int ipath_register_ib_device(struct ipat dev->node_type = RDMA_NODE_IB_CA; dev->phys_port_cnt = 1; dev->dma_device = &dd->pcidev->dev; - dev->class_dev.dev = dev->dma_device; dev->query_device = ipath_query_device; dev->modify_device = ipath_modify_device; dev->query_port = ipath_query_port; diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index c0c8d5b..7c4929f 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -40,9 +40,12 @@ #include #include #include #include +#include #include "ipath_layer.h" +#define IPATH_MAX_RDMA_ATOMIC 4 + #define QPN_MAX (1 << 24) #define QPNMAP_ENTRIES (QPN_MAX / PAGE_SIZE / BITS_PER_BYTE) @@ -89,7 +92,7 @@ struct ib_reth { } __attribute__ ((packed)); struct ib_atomic_eth { - __be64 vaddr; + __be32 vaddr[2]; /* unaligned so access as 2 32-bit words */ __be32 rkey; __be64 swap_data; __be64 compare_data; @@ -108,7 +111,7 @@ struct ipath_other_headers { } rc; struct { __be32 aeth; - __be64 atomic_ack_eth; + __be32 atomic_ack_eth[2]; } at; __be32 imm_data; __be32 aeth; @@ -186,7 +189,7 @@ struct ipath_mmap_info { struct ipath_cq_wc { u32 head; /* index of next entry to fill */ u32 tail; /* index of next ib_poll_cq() entry */ - struct ib_wc queue[1]; /* this is actually size ibcq.cqe + 1 */ + struct ib_uverbs_wc queue[1]; /* this is actually size ibcq.cqe + 1 */ }; /* @@ -312,6 +315,19 @@ struct ipath_sge_state { }; /* + * This structure holds the information that the send tasklet needs + * to send a RDMA read response or atomic operation. + */ +struct ipath_ack_entry { + u8 opcode; + u32 psn; + union { + struct ipath_sge_state rdma_sge; + u64 atomic_data; + }; +}; + +/* * Variables prefixed with s_ are for the requester (sender). * Variables prefixed with r_ are for the responder (receiver). * Variables prefixed with ack_ are for responder replies. @@ -333,24 +349,24 @@ struct ipath_qp { struct ipath_mmap_info *ip; struct ipath_sge_state *s_cur_sge; struct ipath_sge_state s_sge; /* current send request data */ - /* current RDMA read send data */ - struct ipath_sge_state s_rdma_sge; + struct ipath_ack_entry s_ack_queue[IPATH_MAX_RDMA_ATOMIC + 1]; + struct ipath_sge_state s_ack_rdma_sge; + struct ipath_sge_state s_rdma_read_sge; struct ipath_sge_state r_sge; /* current receive data */ spinlock_t s_lock; - unsigned long s_flags; + unsigned long s_busy; u32 s_hdrwords; /* size of s_hdr in 32 bit words */ u32 s_cur_size; /* size of send packet in bytes */ u32 s_len; /* total length of s_sge */ - u32 s_rdma_len; /* total length of s_rdma_sge */ + u32 s_rdma_read_len; /* total length of s_rdma_read_sge */ u32 s_next_psn; /* PSN for next request */ u32 s_last_psn; /* last response PSN processed */ u32 s_psn; /* current packet sequence number */ - u32 s_ack_psn; /* PSN for RDMA_READ */ + u32 s_ack_rdma_psn; /* PSN for sending RDMA read responses */ + u32 s_ack_psn; /* PSN for acking sends and RDMA writes */ u32 s_rnr_timeout; /* number of milliseconds for RNR timeout */ u32 r_ack_psn; /* PSN for next ACK or atomic ACK */ u64 r_wr_id; /* ID for current receive WQE */ - u64 r_atomic_data; /* data for last atomic op */ - u32 r_atomic_psn; /* PSN of last atomic op */ u32 r_len; /* total length of r_sge */ u32 r_rcv_len; /* receive data len processed */ u32 r_psn; /* expected rcv packet sequence number */ @@ -360,12 +376,13 @@ struct ipath_qp { u8 s_ack_state; /* opcode of packet to ACK */ u8 s_nak_state; /* non-zero if NAK is pending */ u8 r_state; /* opcode of last packet received */ - u8 r_ack_state; /* opcode of packet to ACK */ u8 r_nak_state; /* non-zero if NAK is pending */ u8 r_min_rnr_timer; /* retry timeout value for RNR NAKs */ u8 r_reuse_sge; /* for UC receive errors */ u8 r_sge_inx; /* current index into sg_list */ u8 r_wrid_valid; /* r_wrid set but CQ entry not yet made */ + u8 r_max_rd_atomic; /* max number of RDMA read/atomic to receive */ + u8 r_head_ack_queue; /* index into s_ack_queue[] */ u8 qp_access_flags; u8 s_max_sge; /* size of s_wq->sg_list */ u8 s_retry_cnt; /* number of times to retry */ @@ -374,6 +391,10 @@ struct ipath_qp { u8 s_rnr_retry; /* requester RNR retry counter */ u8 s_wait_credit; /* limit number of unacked packets sent */ u8 s_pkey_index; /* PKEY index to use */ + u8 s_max_rd_atomic; /* max number of RDMA read/atomic to send */ + u8 s_num_rd_atomic; /* number of RDMA read/atomic pending */ + u8 s_tail_ack_queue; /* index into s_ack_queue[] */ + u8 s_flags; u8 timeout; /* Timeout for this QP */ enum ib_mtu path_mtu; u32 remote_qpn; @@ -390,11 +411,16 @@ struct ipath_qp { struct ipath_sge r_sg_list[0]; /* verified SGEs */ }; +/* Bit definition for s_busy. */ +#define IPATH_S_BUSY 0 + /* * Bit definitions for s_flags. */ -#define IPATH_S_BUSY 0 -#define IPATH_S_SIGNAL_REQ_WR 1 +#define IPATH_S_SIGNAL_REQ_WR 0x01 +#define IPATH_S_FENCE_PENDING 0x02 +#define IPATH_S_RDMAR_PENDING 0x04 +#define IPATH_S_ACK_PENDING 0x08 #define IPATH_PSN_CREDIT 2048 @@ -706,8 +732,6 @@ int ipath_query_srq(struct ib_srq *ibsrq int ipath_destroy_srq(struct ib_srq *ibsrq); -void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig); - int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry); struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, @@ -757,9 +781,6 @@ u32 ipath_make_grh(struct ipath_ibdev *d void ipath_do_ruc_send(unsigned long data); -u32 ipath_make_rc_ack(struct ipath_qp *qp, struct ipath_other_headers *ohdr, - u32 pmtu); - int ipath_make_rc_req(struct ipath_qp *qp, struct ipath_other_headers *ohdr, u32 pmtu, u32 *bth0p, u32 *bth2p); diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c index 0d9b7d0..773145e 100644 --- a/drivers/infiniband/hw/mthca/mthca_main.c +++ b/drivers/infiniband/hw/mthca/mthca_main.c @@ -1013,14 +1013,14 @@ static struct { u64 latest_fw; u32 flags; } mthca_hca_table[] = { - [TAVOR] = { .latest_fw = MTHCA_FW_VER(3, 4, 0), + [TAVOR] = { .latest_fw = MTHCA_FW_VER(3, 5, 0), .flags = 0 }, - [ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 7, 600), + [ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 8, 200), .flags = MTHCA_FLAG_PCIE }, - [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 1, 400), + [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 2, 0), .flags = MTHCA_FLAG_MEMFREE | MTHCA_FLAG_PCIE }, - [SINAI] = { .latest_fw = MTHCA_FW_VER(1, 1, 0), + [SINAI] = { .latest_fw = MTHCA_FW_VER(1, 2, 0), .flags = MTHCA_FLAG_MEMFREE | MTHCA_FLAG_PCIE | MTHCA_FLAG_SINAI_OPT } @@ -1135,7 +1135,7 @@ static int __mthca_init_one(struct pci_d goto err_cmd; if (mdev->fw_ver < mthca_hca_table[hca_type].latest_fw) { - mthca_warn(mdev, "HCA FW version %d.%d.%d is old (%d.%d.%d is current).\n", + mthca_warn(mdev, "HCA FW version %d.%d.%3d is old (%d.%d.%3d is current).\n", (int) (mdev->fw_ver >> 32), (int) (mdev->fw_ver >> 16) & 0xffff, (int) (mdev->fw_ver & 0xffff), (int) (mthca_hca_table[hca_type].latest_fw >> 32), diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index ee561c5..aa6c70a 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -297,7 +297,8 @@ out: int mthca_write_mtt_size(struct mthca_dev *dev) { - if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy) + if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy || + !(dev->mthca_flags & MTHCA_FLAG_FMR)) /* * Be friendly to WRITE_MTT command * and leave two empty slots for the @@ -355,7 +356,8 @@ int mthca_write_mtt(struct mthca_dev *de int size = mthca_write_mtt_size(dev); int chunk; - if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy) + if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy || + !(dev->mthca_flags & MTHCA_FLAG_FMR)) return __mthca_write_mtt(dev, mtt, start_index, buffer_list, list_len); while (list_len > 0) { diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 0725ad7..47e6fd4 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -1293,7 +1293,6 @@ int mthca_register_device(struct mthca_d dev->ib_dev.node_type = RDMA_NODE_IB_CA; dev->ib_dev.phys_port_cnt = dev->limits.num_ports; dev->ib_dev.dma_device = &dev->pdev->dev; - dev->ib_dev.class_dev.dev = &dev->pdev->dev; dev->ib_dev.query_device = mthca_query_device; dev->ib_dev.query_port = mthca_query_port; dev->ib_dev.modify_device = mthca_modify_device; diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index 1c6b63a..8fe6fee 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -1419,11 +1419,10 @@ void mthca_free_qp(struct mthca_dev *dev * unref the mem-free tables and free the QPN in our table. */ if (!qp->ibqp.uobject) { - mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn, + mthca_cq_clean(dev, recv_cq, qp->qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); - if (qp->ibqp.send_cq != qp->ibqp.recv_cq) - mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn, - qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); + if (send_cq != recv_cq) + mthca_cq_clean(dev, send_cq, qp->qpn, NULL); mthca_free_memfree(dev, qp); mthca_free_wqe_buf(dev, qp); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 2b242a4..0c4e59b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -228,7 +228,6 @@ static int ipoib_cm_req_handler(struct i struct net_device *dev = cm_id->context; struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_cm_rx *p; - unsigned long flags; unsigned psn; int ret; @@ -257,9 +256,9 @@ static int ipoib_cm_req_handler(struct i cm_id->context = p; p->jiffies = jiffies; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); list_add(&p->list, &priv->cm.passive_ids); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); queue_delayed_work(ipoib_workqueue, &priv->cm.stale_task, IPOIB_CM_RX_DELAY); return 0; @@ -277,7 +276,6 @@ static int ipoib_cm_rx_handler(struct ib { struct ipoib_cm_rx *p; struct ipoib_dev_priv *priv; - unsigned long flags; int ret; switch (event->event) { @@ -290,14 +288,14 @@ static int ipoib_cm_rx_handler(struct ib case IB_CM_REJ_RECEIVED: p = cm_id->context; priv = netdev_priv(p->dev); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); if (list_empty(&p->list)) ret = 0; /* Connection is going away already. */ else { list_del_init(&p->list); ret = -ECONNRESET; } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); if (ret) { ib_destroy_qp(p->qp); kfree(p); @@ -351,8 +349,8 @@ void ipoib_cm_handle_rx_wc(struct net_de u64 mapping[IPOIB_CM_RX_SG]; int frags; - ipoib_dbg_data(priv, "cm recv completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "cm recv completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_recvq_size)) { ipoib_warn(priv, "cm recv completion event with wrid %d (> %d)\n", @@ -408,7 +406,7 @@ void ipoib_cm_handle_rx_wc(struct net_de skb_put_frags(skb, IPOIB_CM_HEAD_SIZE, wc->byte_len, newskb); skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, IPOIB_ENCAP_LEN); dev->last_rx = jiffies; @@ -504,8 +502,8 @@ static void ipoib_cm_handle_tx_wc(struct struct ipoib_tx_buf *tx_req; unsigned long flags; - ipoib_dbg_data(priv, "cm send completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "cm send completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_sendq_size)) { ipoib_warn(priv, "cm send completion event with wrid %d (> %d)\n", @@ -612,23 +610,22 @@ void ipoib_cm_dev_stop(struct net_device { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_cm_rx *p; - unsigned long flags; if (!IPOIB_CM_SUPPORTED(dev->dev_addr)) return; ib_destroy_cm_id(priv->cm.id); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); while (!list_empty(&priv->cm.passive_ids)) { p = list_entry(priv->cm.passive_ids.next, typeof(*p), list); list_del_init(&p->list); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); ib_destroy_cm_id(p->id); ib_destroy_qp(p->qp); kfree(p); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); cancel_delayed_work(&priv->cm.stale_task); } @@ -642,7 +639,6 @@ static int ipoib_cm_rep_handler(struct i struct ib_qp_attr qp_attr; int qp_attr_mask, ret; struct sk_buff *skb; - unsigned long flags; p->mtu = be32_to_cpu(data->mtu); @@ -680,12 +676,12 @@ static int ipoib_cm_rep_handler(struct i skb_queue_head_init(&skqueue); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); set_bit(IPOIB_FLAG_OPER_UP, &p->flags); if (p->neigh) while ((skb = __skb_dequeue(&p->neigh->queue))) __skb_queue_tail(&skqueue, skb); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); while ((skb = __skb_dequeue(&skqueue))) { skb->dev = p->dev; @@ -895,7 +891,6 @@ static int ipoib_cm_tx_handler(struct ib struct ipoib_dev_priv *priv = netdev_priv(tx->dev); struct net_device *dev = priv->dev; struct ipoib_neigh *neigh; - unsigned long flags; int ret; switch (event->event) { @@ -914,7 +909,7 @@ static int ipoib_cm_tx_handler(struct ib case IB_CM_REJ_RECEIVED: case IB_CM_TIMEWAIT_EXIT: ipoib_dbg(priv, "CM error %d.\n", event->event); - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); neigh = tx->neigh; @@ -934,7 +929,7 @@ static int ipoib_cm_tx_handler(struct ib } spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); break; default: break; @@ -1023,21 +1018,20 @@ static void ipoib_cm_tx_reap(struct work struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, cm.reap_task); struct ipoib_cm_tx *p; - unsigned long flags; - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); while (!list_empty(&priv->cm.reap_list)) { p = list_entry(priv->cm.reap_list.next, typeof(*p), list); list_del(&p->list); spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); ipoib_cm_tx_destroy(p); - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); } spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); } static void ipoib_cm_skb_reap(struct work_struct *work) @@ -1046,15 +1040,14 @@ static void ipoib_cm_skb_reap(struct wor cm.skb_task); struct net_device *dev = priv->dev; struct sk_buff *skb; - unsigned long flags; unsigned mtu = priv->mcast_mtu; - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); while ((skb = skb_dequeue(&priv->cm.skb_queue))) { spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); if (skb->protocol == htons(ETH_P_IP)) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -1062,11 +1055,11 @@ #if defined(CONFIG_IPV6) || defined(CONF icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); #endif dev_kfree_skb_any(skb); - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); } spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); } void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb, @@ -1088,9 +1081,8 @@ static void ipoib_cm_stale_task(struct w struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, cm.stale_task.work); struct ipoib_cm_rx *p; - unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); while (!list_empty(&priv->cm.passive_ids)) { /* List if sorted by LRU, start from tail, * stop when we see a recently used entry */ @@ -1098,13 +1090,13 @@ static void ipoib_cm_stale_task(struct w if (time_before_eq(jiffies, p->jiffies + IPOIB_CM_RX_TIMEOUT)) break; list_del_init(&p->list); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); ib_destroy_cm_id(p->id); ib_destroy_qp(p->qp); kfree(p); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index ba0ee5c..1bdb910 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -172,8 +172,8 @@ static void ipoib_ib_handle_rx_wc(struct struct sk_buff *skb; u64 addr; - ipoib_dbg_data(priv, "recv completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "recv completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_recvq_size)) { ipoib_warn(priv, "recv completion event with wrid %d (> %d)\n", @@ -216,7 +216,7 @@ static void ipoib_ib_handle_rx_wc(struct if (wc->slid != priv->local_lid || wc->src_qp != priv->qp->qp_num) { skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, IPOIB_ENCAP_LEN); dev->last_rx = jiffies; @@ -245,8 +245,8 @@ static void ipoib_ib_handle_tx_wc(struct struct ipoib_tx_buf *tx_req; unsigned long flags; - ipoib_dbg_data(priv, "send completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "send completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_sendq_size)) { ipoib_warn(priv, "send completion event with wrid %d (> %d)\n", diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index f2a40ae..b4c380c 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -395,14 +395,10 @@ static void path_rec_completion(int stat skb_queue_head_init(&skqueue); if (!status) { - struct ib_ah_attr av = { - .dlid = be16_to_cpu(pathrec->dlid), - .sl = pathrec->sl, - .port_num = priv->port, - .static_rate = pathrec->rate - }; - - ah = ipoib_create_ah(dev, priv->pd, &av); + struct ib_ah_attr av; + + if (!ib_init_ah_from_path(priv->ca, priv->port, pathrec, &av)) + ah = ipoib_create_ah(dev, priv->pd, &av); } spin_lock_irqsave(&priv->lock, flags); diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index a00fe47..bd686a2 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -190,16 +190,14 @@ static void gameport_run_poll_handler(un * Basic gameport -> driver core mappings */ -static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv) +static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv) { int error; - down_write(&gameport_bus.subsys.rwsem); - gameport->dev.driver = &drv->driver; if (drv->connect(gameport, drv)) { gameport->dev.driver = NULL; - goto out; + return -ENODEV; } error = device_bind_driver(&gameport->dev); @@ -211,31 +209,21 @@ static void gameport_bind_driver(struct drv->description, error); drv->disconnect(gameport); gameport->dev.driver = NULL; - goto out; + return error; } - out: - up_write(&gameport_bus.subsys.rwsem); -} - -static void gameport_release_driver(struct gameport *gameport) -{ - down_write(&gameport_bus.subsys.rwsem); - device_release_driver(&gameport->dev); - up_write(&gameport_bus.subsys.rwsem); + return 0; } static void gameport_find_driver(struct gameport *gameport) { int error; - down_write(&gameport_bus.subsys.rwsem); error = device_attach(&gameport->dev); if (error < 0) printk(KERN_WARNING "gameport: device_attach() failed for %s (%s), error: %d\n", gameport->phys, gameport->name, error); - up_write(&gameport_bus.subsys.rwsem); } @@ -483,13 +471,12 @@ static ssize_t gameport_rebind_driver(st { struct gameport *gameport = to_gameport_port(dev); struct device_driver *drv; - int retval; + int error; - retval = mutex_lock_interruptible(&gameport_mutex); - if (retval) - return retval; + error = mutex_lock_interruptible(&gameport_mutex); + if (error) + return error; - retval = count; if (!strncmp(buf, "none", count)) { gameport_disconnect_port(gameport); } else if (!strncmp(buf, "reconnect", count)) { @@ -499,15 +486,15 @@ static ssize_t gameport_rebind_driver(st gameport_find_driver(gameport); } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) { gameport_disconnect_port(gameport); - gameport_bind_driver(gameport, to_gameport_driver(drv)); + error = gameport_bind_driver(gameport, to_gameport_driver(drv)); put_driver(drv); } else { - retval = -EINVAL; + error = -EINVAL; } mutex_unlock(&gameport_mutex); - return retval; + return error ? error : count; } static struct device_attribute gameport_device_attrs[] = { @@ -655,7 +642,7 @@ static void gameport_disconnect_port(str do { parent = s->parent; - gameport_release_driver(s); + device_release_driver(&s->dev); gameport_destroy_port(s); } while ((s = parent) != gameport); } @@ -663,7 +650,7 @@ static void gameport_disconnect_port(str /* * Ok, no children left, now disconnect this port */ - gameport_release_driver(gameport); + device_release_driver(&gameport->dev); } void gameport_rescan(struct gameport *gameport) diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index a15e531..5895202 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -115,18 +115,18 @@ static int serio_match_port(const struct * Basic serio -> driver core mappings */ -static void serio_bind_driver(struct serio *serio, struct serio_driver *drv) +static int serio_bind_driver(struct serio *serio, struct serio_driver *drv) { int error; - down_write(&serio_bus.subsys.rwsem); - if (serio_match_port(drv->id_table, serio)) { + serio->dev.driver = &drv->driver; if (serio_connect_driver(serio, drv)) { serio->dev.driver = NULL; - goto out; + return -ENODEV; } + error = device_bind_driver(&serio->dev); if (error) { printk(KERN_WARNING @@ -136,31 +136,21 @@ static void serio_bind_driver(struct ser drv->description, error); serio_disconnect_driver(serio); serio->dev.driver = NULL; - goto out; + return error; } } - out: - up_write(&serio_bus.subsys.rwsem); -} - -static void serio_release_driver(struct serio *serio) -{ - down_write(&serio_bus.subsys.rwsem); - device_release_driver(&serio->dev); - up_write(&serio_bus.subsys.rwsem); + return 0; } static void serio_find_driver(struct serio *serio) { int error; - down_write(&serio_bus.subsys.rwsem); error = device_attach(&serio->dev); if (error < 0) printk(KERN_WARNING "serio: device_attach() failed for %s (%s), error: %d\n", serio->phys, serio->name, error); - up_write(&serio_bus.subsys.rwsem); } @@ -470,13 +460,12 @@ static ssize_t serio_rebind_driver(struc { struct serio *serio = to_serio_port(dev); struct device_driver *drv; - int retval; + int error; - retval = mutex_lock_interruptible(&serio_mutex); - if (retval) - return retval; + error = mutex_lock_interruptible(&serio_mutex); + if (error) + return error; - retval = count; if (!strncmp(buf, "none", count)) { serio_disconnect_port(serio); } else if (!strncmp(buf, "reconnect", count)) { @@ -486,15 +475,15 @@ static ssize_t serio_rebind_driver(struc serio_find_driver(serio); } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { serio_disconnect_port(serio); - serio_bind_driver(serio, to_serio_driver(drv)); + error = serio_bind_driver(serio, to_serio_driver(drv)); put_driver(drv); } else { - retval = -EINVAL; + error = -EINVAL; } mutex_unlock(&serio_mutex); - return retval; + return error ? error : count; } static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf) @@ -665,7 +654,7 @@ static void serio_disconnect_port(struct do { parent = s->parent; - serio_release_driver(s); + device_release_driver(&s->dev); serio_destroy_port(s); } while ((s = parent) != serio); } @@ -673,7 +662,7 @@ static void serio_disconnect_port(struct /* * Ok, no children left, now disconnect this port */ - serio_release_driver(serio); + device_release_driver(&serio->dev); } void serio_rescan(struct serio *serio) diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c index e3e5c13..ee2b0b9 100644 --- a/drivers/isdn/act2000/module.c +++ b/drivers/isdn/act2000/module.c @@ -442,7 +442,7 @@ act2000_sendbuf(act2000_card *card, int return 0; } skb_reserve(xmit_skb, 19); - memcpy(skb_put(xmit_skb, len), skb->data, len); + skb_copy_from_linear_data(skb, skb_put(xmit_skb, len), len); } else { xmit_skb = skb_clone(skb, GFP_ATOMIC); if (!xmit_skb) { diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c index 2baef34..c8e1c35 100644 --- a/drivers/isdn/gigaset/usb-gigaset.c +++ b/drivers/isdn/gigaset/usb-gigaset.c @@ -652,7 +652,7 @@ static int write_modem(struct cardstate * transmit data */ count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size); - memcpy(ucs->bulk_out_buffer, bcs->tx_skb->data, count); + skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count); skb_pull(bcs->tx_skb, count); atomic_set(&ucs->busy, 1); gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count); diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c index 1e2d38e..428872b 100644 --- a/drivers/isdn/hardware/avm/b1dma.c +++ b/drivers/isdn/hardware/avm/b1dma.c @@ -404,7 +404,8 @@ #ifdef AVM_B1DMA_DEBUG printk(KERN_DEBUG "tx: put 0x%x len=%d\n", skb->data[2], txlen); #endif - memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2); + skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf, + skb->len - 2); } txlen = (txlen + 3) & ~3; diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c index 6f5efa8..d58f927 100644 --- a/drivers/isdn/hardware/avm/c4.c +++ b/drivers/isdn/hardware/avm/c4.c @@ -457,7 +457,8 @@ #ifdef AVM_C4_DEBUG printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n", card->name, skb->data[2], txlen); #endif - memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2); + skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf, + skb->len - 2); } txlen = (txlen + 3) & ~3; diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index ae377e8..1642dca 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -254,14 +254,16 @@ write_modem(struct BCState *bcs) { count = len; if (count > MAX_MODEM_BUF - fp) { count = MAX_MODEM_BUF - fp; - memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count); + skb_copy_from_linear_data(bcs->tx_skb, + cs->hw.elsa.transbuf + fp, count); skb_pull(bcs->tx_skb, count); cs->hw.elsa.transcnt += count; ret = count; count = len - count; fp = 0; } - memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count); + skb_copy_from_linear_data(bcs->tx_skb, + cs->hw.elsa.transbuf + fp, count); skb_pull(bcs->tx_skb, count); cs->hw.elsa.transcnt += count; ret += count; diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index cd3b5ad..3446f24 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1293,7 +1293,8 @@ l2_pull_iqueue(struct FsmInst *fi, int e oskb = skb; skb = alloc_skb(oskb->len + i, GFP_ATOMIC); memcpy(skb_put(skb, i), header, i); - memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); + skb_copy_from_linear_data(oskb, + skb_put(skb, oskb->len), oskb->len); dev_kfree_skb(oskb); } st->l2.l2l1(st, PH_PULL | INDICATION, skb); diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c index b2ae4ec..f854501 100644 --- a/drivers/isdn/hysdn/hycapi.c +++ b/drivers/isdn/hysdn/hycapi.c @@ -398,8 +398,9 @@ static u16 hycapi_send_message(struct ca _len = CAPIMSG_LEN(skb->data); if (_len > 22) { _len2 = _len - 22; - memcpy(msghead, skb->data, 22); - memcpy(skb->data + _len2, msghead, 22); + skb_copy_from_linear_data(skb, msghead, 22); + skb_copy_to_linear_data_offset(skb, _len2, + msghead, 22); skb_pull(skb, _len2); CAPIMSG_SETLEN(skb->data, 22); retval = capilib_data_b3_req(&cinfo->ncci_head, diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c index 557d96c..cfa8fa5 100644 --- a/drivers/isdn/hysdn/hysdn_net.c +++ b/drivers/isdn/hysdn/hysdn_net.c @@ -214,8 +214,6 @@ hysdn_rx_netpkt(hysdn_card * card, unsig lp->stats.rx_dropped++; return; } - skb->dev = &lp->netdev; - /* copy the data */ memcpy(skb_put(skb, len), buf, len); diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c index b7b5aa4..81db4a1 100644 --- a/drivers/isdn/hysdn/hysdn_sched.c +++ b/drivers/isdn/hysdn/hysdn_sched.c @@ -113,7 +113,8 @@ hysdn_sched_tx(hysdn_card *card, unsigne (skb = hysdn_tx_netget(card)) != NULL) { if (skb->len <= maxlen) { - memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */ + /* copy the packet to the buffer */ + skb_copy_from_linear_data(skb, buf, skb->len); *len = skb->len; *chan = CHAN_NDIS_DATA; card->net_tx_busy = 1; /* we are busy sending network data */ @@ -126,7 +127,7 @@ #ifdef CONFIG_HYSDN_CAPI ((skb = hycapi_tx_capiget(card)) != NULL) ) { if (skb->len <= maxlen) { - memcpy(buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, buf, skb->len); *len = skb->len; *chan = CHAN_CAPI; hycapi_tx_capiack(card); diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index 9c926e4..c97330b 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -829,7 +829,7 @@ #endif dflag = 0; } count_put = count_pull; - memcpy(cp, skb->data, count_put); + skb_copy_from_linear_data(skb, cp, count_put); cp += count_put; len -= count_put; #ifdef CONFIG_ISDN_AUDIO diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 838b373..aa83277 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -872,7 +872,8 @@ typedef struct { static void isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) { - u_char *p = skb->nh.raw; /* hopefully, this was set correctly */ + /* hopefully, this was set correctly */ + const u_char *p = skb_network_header(skb); unsigned short proto = ntohs(skb->protocol); int data_ofs; ip_ports *ipp; @@ -880,7 +881,7 @@ isdn_net_log_skb(struct sk_buff * skb, i addinfo[0] = '\0'; /* This check stolen from 2.1.72 dev_queue_xmit_nit() */ - if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) { + if (p < skb->data || skb->network_header >= skb->tail) { /* fall back to old isdn_net_log_packet method() */ char * buf = skb->data; @@ -1121,7 +1122,7 @@ isdn_net_adjust_hdr(struct sk_buff *skb, if (!skb) return; if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { - int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN; + const int pullsize = skb_network_offset(skb) - ETH_HLEN; if (pullsize > 0) { printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize); skb_pull(skb, pullsize); @@ -1366,7 +1367,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); eth = eth_hdr(skb); @@ -1786,7 +1787,7 @@ #endif } skb->dev = ndev; skb->pkt_type = PACKET_HOST; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); #ifdef ISDN_DEBUG_NET_DUMP isdn_dumppkt("R:", skb->data, skb->len, 40); #endif diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 1b2df80..387392c 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -1100,7 +1100,8 @@ #ifdef CONFIG_ISDN_PPP_VJ goto drop_packet; } skb_put(skb, skb_old->len + 128); - memcpy(skb->data, skb_old->data, skb_old->len); + skb_copy_from_linear_data(skb_old, skb->data, + skb_old->len); if (net_dev->local->ppp_slot < 0) { printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n", __FUNCTION__, net_dev->local->ppp_slot); @@ -1167,7 +1168,7 @@ #else /* CONFIG_IPPP_FILTER */ mlp->huptimer = 0; #endif /* CONFIG_IPPP_FILTER */ skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_rx(skb); /* net_dev->local->stats.rx_packets++; done in isdn_net.c */ return; @@ -1902,7 +1903,9 @@ void isdn_ppp_mp_reassembly( isdn_net_de while( from != to ) { unsigned int len = from->len - MP_HEADER_LEN; - memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len); + skb_copy_from_linear_data_offset(from, MP_HEADER_LEN, + skb_put(skb,len), + len); frag = from->next; isdn_ppp_mp_free_skb(mp, from); from = frag; diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index e3add27..e93ad59 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -415,7 +415,8 @@ isdnloop_sendbuf(int channel, struct sk_ spin_lock_irqsave(&card->isdnloop_lock, flags); nskb = dev_alloc_skb(skb->len); if (nskb) { - memcpy(skb_put(nskb, len), skb->data, len); + skb_copy_from_linear_data(skb, + skb_put(nskb, len), len); skb_queue_tail(&card->bqueue[channel], nskb); dev_kfree_skb(skb); } else diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c index 47c59e9..7b55e15 100644 --- a/drivers/isdn/pcbit/capi.c +++ b/drivers/isdn/pcbit/capi.c @@ -429,8 +429,9 @@ #endif if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC))) return -1; - memcpy(info->data.setup.CallingPN, skb->data + count + 1, - len - count); + skb_copy_from_linear_data_offset(skb, count + 1, + info->data.setup.CallingPN, + len - count); info->data.setup.CallingPN[len - count] = 0; } @@ -457,8 +458,9 @@ #endif if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC))) return -1; - memcpy(info->data.setup.CalledPN, skb->data + count + 1, - len - count); + skb_copy_from_linear_data_offset(skb, count + 1, + info->data.setup.CalledPN, + len - count); info->data.setup.CalledPN[len - count] = 0; } @@ -539,7 +541,7 @@ #endif #ifdef DEBUG if (len > 1 && len < 31) { - memcpy(str, skb->data + 2, len - 1); + skb_copy_from_linear_data_offset(skb, 2, str, len - 1); str[len] = 0; printk(KERN_DEBUG "Connected Party Number: %s\n", str); } diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index f729eeb..adfea3c 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -90,7 +90,7 @@ static int autopoll_devs; int __adb_probe_sync; #ifdef CONFIG_PM -static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when); +static void adb_notify_sleep(struct pmu_sleep_notifier *self, int when); static struct pmu_sleep_notifier adb_sleep_notifier = { adb_notify_sleep, SLEEP_LEVEL_ADB, @@ -340,11 +340,9 @@ #ifdef CONFIG_PM /* * notify clients before sleep and reset bus afterwards */ -int +void adb_notify_sleep(struct pmu_sleep_notifier *self, int when) { - int ret; - switch (when) { case PBOOK_SLEEP_REQUEST: adb_got_sleep = 1; @@ -353,22 +351,8 @@ adb_notify_sleep(struct pmu_sleep_notifi /* Stop autopoll */ if (adb_controller->autopoll) adb_controller->autopoll(0); - ret = blocking_notifier_call_chain(&adb_client_list, - ADB_MSG_POWERDOWN, NULL); - if (ret & NOTIFY_STOP_MASK) { - up(&adb_probe_mutex); - return PBOOK_SLEEP_REFUSE; - } - break; - case PBOOK_SLEEP_REJECT: - if (adb_got_sleep) { - adb_got_sleep = 0; - up(&adb_probe_mutex); - adb_reset_bus(); - } - break; - - case PBOOK_SLEEP_NOW: + blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_POWERDOWN, NULL); break; case PBOOK_WAKE: adb_got_sleep = 0; @@ -376,14 +360,13 @@ adb_notify_sleep(struct pmu_sleep_notifi adb_reset_bus(); break; } - return PBOOK_SLEEP_OK; } #endif /* CONFIG_PM */ static int do_adb_reset_bus(void) { - int ret, nret; + int ret; if (adb_controller == NULL) return -ENXIO; @@ -391,13 +374,8 @@ do_adb_reset_bus(void) if (adb_controller->autopoll) adb_controller->autopoll(0); - nret = blocking_notifier_call_chain(&adb_client_list, - ADB_MSG_PRE_RESET, NULL); - if (nret & NOTIFY_STOP_MASK) { - if (adb_controller->autopoll) - adb_controller->autopoll(autopoll_devs); - return -EBUSY; - } + blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_PRE_RESET, NULL); if (sleepy_trackpad) { /* Let the trackpad settle down */ @@ -427,10 +405,8 @@ do_adb_reset_bus(void) } up(&adb_handler_sem); - nret = blocking_notifier_call_chain(&adb_client_list, - ADB_MSG_POST_RESET, NULL); - if (nret & NOTIFY_STOP_MASK) - return -EBUSY; + blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_POST_RESET, NULL); return ret; } diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c index cdd5a0f..e54c4d9 100644 --- a/drivers/macintosh/ans-lcd.c +++ b/drivers/macintosh/ans-lcd.c @@ -145,11 +145,12 @@ anslcd_init(void) int retval; struct device_node* node; - node = find_devices("lcd"); - if (!node || !node->parent) - return -ENODEV; - if (strcmp(node->parent->name, "gc")) + node = of_find_node_by_name(NULL, "lcd"); + if (!node || !node->parent || strcmp(node->parent->name, "gc")) { + of_node_put(node); return -ENODEV; + } + of_node_put(node); anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20); diff --git a/drivers/macintosh/apm_emu.c b/drivers/macintosh/apm_emu.c index c5e4d43..cdb0bea 100644 --- a/drivers/macintosh/apm_emu.c +++ b/drivers/macintosh/apm_emu.c @@ -96,7 +96,7 @@ static DECLARE_WAIT_QUEUE_HEAD(apm_waitq static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); static struct apm_user * user_list; -static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when); +static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when); static struct pmu_sleep_notifier apm_sleep_notifier = { apm_notify_sleep, SLEEP_LEVEL_USERLAND, @@ -352,7 +352,7 @@ static int do_open(struct inode * inode, * doesn't provide a way to NAK, but this could be added * here. */ -static int wait_all_suspend(void) +static void wait_all_suspend(void) { DECLARE_WAITQUEUE(wait, current); @@ -366,24 +366,19 @@ static int wait_all_suspend(void) remove_wait_queue(&apm_suspend_waitqueue, &wait); DBG("apm_emu: wait_all_suspend() - complete !\n"); - - return 1; } -static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when) +static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when) { switch(when) { case PBOOK_SLEEP_REQUEST: queue_event(APM_SYS_SUSPEND, NULL); - if (!wait_all_suspend()) - return PBOOK_SLEEP_REFUSE; + wait_all_suspend(); break; - case PBOOK_SLEEP_REJECT: case PBOOK_WAKE: queue_event(APM_NORMAL_RESUME, NULL); break; } - return PBOOK_SLEEP_OK; } #define APM_CRITICAL 10 diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c index c1fd816..1599dc3 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -102,8 +102,6 @@ int mac_hid_mouse_emulate_buttons(int ca return 0; } -EXPORT_SYMBOL(mac_hid_mouse_emulate_buttons); - static int emumousebtn_input_register(void) { int ret; diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c index 026b67f..79119f5 100644 --- a/drivers/macintosh/macio-adb.c +++ b/drivers/macintosh/macio-adb.c @@ -82,7 +82,14 @@ struct adb_driver macio_adb_driver = { int macio_probe(void) { - return find_compatible_devices("adb", "chrp,adb0")? 0: -ENODEV; + struct device_node *np; + + np = of_find_compatible_node(NULL, "adb", "chrp,adb0"); + if (np) { + of_node_put(np); + return 0; + } + return -ENODEV; } int macio_init(void) @@ -91,12 +98,14 @@ int macio_init(void) struct resource r; unsigned int irq; - adbs = find_compatible_devices("adb", "chrp,adb0"); + adbs = of_find_compatible_node(NULL, "adb", "chrp,adb0"); if (adbs == 0) return -ENXIO; - if (of_address_to_resource(adbs, 0, &r)) + if (of_address_to_resource(adbs, 0, &r)) { + of_node_put(adbs); return -ENXIO; + } adb = ioremap(r.start, sizeof(struct adb_regs)); out_8(&adb->ctrl.r, 0); @@ -107,6 +116,7 @@ int macio_init(void) out_8(&adb->autopoll.r, APE); irq = irq_of_parse_and_map(adbs, 0); + of_node_put(adbs); if (request_irq(irq, macio_adb_interrupt, 0, "ADB", (void *)0)) { printk(KERN_ERR "ADB: can't get irq %d\n", irq); return -EAGAIN; diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index d562160..c96b7fe 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -134,108 +134,12 @@ static int macio_device_resume(struct de return 0; } -static int macio_uevent(struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) -{ - struct macio_dev * macio_dev; - struct of_device * of; - char *scratch; - const char *compat, *compat2; - - int i = 0; - int length, cplen, cplen2, seen = 0; - - if (!dev) - return -ENODEV; - - macio_dev = to_macio_device(dev); - if (!macio_dev) - return -ENODEV; - - of = &macio_dev->ofdev; - - /* stuff we want to pass to /sbin/hotplug */ - envp[i++] = scratch = buffer; - length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - /* Since the compatible field can contain pretty much anything - * it's not really legal to split it out with commas. We split it - * up using a number of environment variables instead. */ - - compat = get_property(of->node, "compatible", &cplen); - compat2 = compat; - cplen2= cplen; - while (compat && cplen > 0) { - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, - "OF_COMPATIBLE_%d=%s", seen, compat); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - length = strlen (compat) + 1; - compat += length; - cplen -= length; - seen++; - } - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "MODALIAS=of:N%sT%s", - of->node->name, of->node->type); - /* overwrite '\0' */ - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - if (!compat2) { - compat2 = ""; - cplen2 = 1; - } - while (cplen2 > 0) { - length = snprintf (scratch, buffer_size, "C%s", compat2); - buffer_size -= length; - if (buffer_size <= 0) - return -ENOMEM; - scratch += length; - length = strlen (compat2) + 1; - compat2 += length; - cplen2 -= length; - } - - envp[i] = NULL; - - return 0; -} - extern struct device_attribute macio_dev_attrs[]; struct bus_type macio_bus_type = { .name = "macio", .match = macio_bus_match, - .uevent = macio_uevent, + .uevent = of_device_uevent, .probe = macio_device_probe, .remove = macio_device_remove, .shutdown = macio_device_shutdown, @@ -491,7 +395,7 @@ #else #endif MAX_NODE_NAME_SIZE, np->name); } else { - reg = get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index, reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c index 8566bdf..cc82679 100644 --- a/drivers/macintosh/macio_sysfs.c +++ b/drivers/macintosh/macio_sysfs.c @@ -21,7 +21,7 @@ compatible_show (struct device *dev, str int length = 0; of = &to_macio_device (dev)->ofdev; - compat = get_property(of->node, "compatible", &cplen); + compat = of_get_property(of->node, "compatible", &cplen); if (!compat) { *buf = '\0'; return 0; @@ -47,18 +47,20 @@ static ssize_t modalias_show (struct dev int length; of = &to_macio_device (dev)->ofdev; - compat = get_property(of->node, "compatible", &cplen); + compat = of_get_property(of->node, "compatible", &cplen); if (!compat) compat = "", cplen = 1; length = sprintf (buf, "of:N%sT%s", of->node->name, of->node->type); buf += length; while (cplen > 0) { int l; - length += sprintf (buf, "C%s", compat); - buf += length; + l = sprintf (buf, "C%s", compat); + length += l; + buf += l; l = strlen (compat) + 1; compat += l; cplen -= l; } + length += sprintf(buf, "\n"); return length; } diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index f83fad2..4177ff0 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -387,7 +387,7 @@ static int __devinit rackmeter_probe(str if (strcmp(np->name, "lightshow") == 0) break; if ((strcmp(np->name, "sound") == 0) && - get_property(np, "virtual", NULL) != NULL) + of_get_property(np, "virtual", NULL) != NULL) break; } if (np == NULL) { diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index c9f3dc4..a98a328 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -491,7 +491,7 @@ int __init smu_init (void) printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n"); goto fail; } - data = get_property(smu->db_node, "reg", NULL); + data = of_get_property(smu->db_node, "reg", NULL); if (data == NULL) { of_node_put(smu->db_node); smu->db_node = NULL; @@ -512,7 +512,7 @@ int __init smu_init (void) smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt"); if (smu->msg_node == NULL) break; - data = get_property(smu->msg_node, "reg", NULL); + data = of_get_property(smu->msg_node, "reg", NULL); if (data == NULL) { of_node_put(smu->msg_node); smu->msg_node = NULL; @@ -952,7 +952,7 @@ static struct smu_sdbp_header *smu_creat prop->name = ((char *)prop) + tlen - 18; sprintf(prop->name, "sdb-partition-%02x", id); prop->length = len; - prop->value = (unsigned char *)hdr; + prop->value = hdr; prop->next = NULL; /* Read the datablock */ @@ -1004,7 +1004,7 @@ const struct smu_sdbp_header *__smu_get_ } else mutex_lock(&smu_part_access); - part = get_property(smu->of_node, pname, size); + part = of_get_property(smu->of_node, pname, size); if (part == NULL) { DPRINTK("trying to extract from SMU ...\n"); part = smu_create_sdb_partition(id); diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index a7ce559..2289034 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -567,13 +567,13 @@ thermostat_init(void) else return -ENODEV; - prop = get_property(np, "hwsensor-params-version", NULL); + prop = of_get_property(np, "hwsensor-params-version", NULL); printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, (*prop == 1)?"":"un"); if (*prop != 1) return -ENODEV; - prop = get_property(np, "reg", NULL); + prop = of_get_property(np, "reg", NULL); if (!prop) return -ENODEV; @@ -591,9 +591,9 @@ thermostat_init(void) "limit_adjust: %d, fan_speed: %d\n", therm_bus, therm_address, limit_adjust, fan_speed); - if (get_property(np, "hwsensor-location", NULL)) { + if (of_get_property(np, "hwsensor-location", NULL)) { for (i = 0; i < 3; i++) { - sensor_location[i] = get_property(np, + sensor_location[i] = of_get_property(np, "hwsensor-location", NULL) + offset; if (sensor_location[i] == NULL) diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 2e4ad44..78ff186 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -674,7 +674,7 @@ static int read_eeprom(int cpu, struct m printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n"); return -ENODEV; } - data = get_property(np, "cpuid", &len); + data = of_get_property(np, "cpuid", &len); if (data == NULL) { printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n"); of_node_put(np); @@ -1337,7 +1337,7 @@ static int init_backside_state(struct ba */ u3 = of_find_node_by_path("/u3@0,f8000000"); if (u3 != NULL) { - const u32 *vers = get_property(u3, "device-rev", NULL); + const u32 *vers = of_get_property(u3, "device-rev", NULL); if (vers) if (((*vers) & 0x3f) < 0x34) u3h = 0; @@ -2129,8 +2129,8 @@ static void fcu_lookup_fans(struct devic continue; /* Lookup for a matching location */ - loc = get_property(np, "location", NULL); - reg = get_property(np, "reg", NULL); + loc = of_get_property(np, "location", NULL); + reg = of_get_property(np, "reg", NULL); if (loc == NULL || reg == NULL) continue; DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index a1d3a98..35233de 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -492,7 +492,7 @@ g4fan_init( void ) if( !(np=of_find_node_by_name(NULL, "power-mgt")) ) return -ENODEV; - info = get_property(np, "thermal-info", NULL); + info = of_get_property(np, "thermal-info", NULL); of_node_put(np); if( !info || !machine_is_compatible("PowerMac3,6") ) diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index d58fcf6..76d2177 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -131,7 +131,7 @@ int __init find_via_cuda(void) if (vias == 0) return 0; - reg = get_property(vias, "reg", NULL); + reg = of_get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-cuda: No \"reg\" property !\n"); goto fail; diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c index 179af10..fc89a70 100644 --- a/drivers/macintosh/via-pmu-led.c +++ b/drivers/macintosh/via-pmu-led.c @@ -81,7 +81,7 @@ #endif }; #ifdef CONFIG_PM -static int pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when) +static void pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when) { unsigned long flags; @@ -99,8 +99,6 @@ static int pmu_led_sleep_call(struct pmu break; } spin_unlock_irqrestore(&pmu_blink_lock, flags); - - return PBOOK_SLEEP_OK; } static struct pmu_sleep_notifier via_pmu_led_sleep_notif = { @@ -120,11 +118,13 @@ static int __init via_pmu_led_init(void) dt = of_find_node_by_path("/"); if (dt == NULL) return -ENODEV; - model = get_property(dt, "model", NULL); + model = of_get_property(dt, "model", NULL); if (model == NULL) return -ENODEV; if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 && - strncmp(model, "iBook", strlen("iBook")) != 0) { + strncmp(model, "iBook", strlen("iBook")) != 0 && + strcmp(model, "PowerMac7,2") != 0 && + strcmp(model, "PowerMac7,3") != 0) { of_node_put(dt); /* ignore */ return -ENODEV; diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index b6073bd..1729d3f 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -289,7 +289,7 @@ int __init find_via_pmu(void) if (vias == NULL) return 0; - reg = get_property(vias, "reg", NULL); + reg = of_get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); goto fail; @@ -319,10 +319,13 @@ int __init find_via_pmu(void) else if (device_is_compatible(vias->parent, "Keylargo") || device_is_compatible(vias->parent, "K2-Keylargo")) { struct device_node *gpiop; + struct device_node *adbp; u64 gaddr = OF_BAD_ADDR; pmu_kind = PMU_KEYLARGO_BASED; - pmu_has_adb = (find_type_devices("adb") != NULL); + adbp = of_find_node_by_type(NULL, "adb"); + pmu_has_adb = (adbp != NULL); + of_node_put(adbp); pmu_intr_mask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | @@ -331,7 +334,7 @@ int __init find_via_pmu(void) gpiop = of_find_node_by_name(NULL, "gpio"); if (gpiop) { - reg = get_property(gpiop, "reg", NULL); + reg = of_get_property(gpiop, "reg", NULL); if (reg) gaddr = of_translate_address(gpiop, reg); if (gaddr != OF_BAD_ADDR) @@ -484,10 +487,11 @@ #ifdef CONFIG_PPC32 pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } else { - struct device_node* prim = find_devices("power-mgt"); + struct device_node* prim = + of_find_node_by_name(NULL, "power-mgt"); const u32 *prim_info = NULL; if (prim) - prim_info = get_property(prim, "prim-info", NULL); + prim_info = of_get_property(prim, "prim-info", NULL); if (prim_info) { /* Other stuffs here yet unknown */ pmu_battery_count = (prim_info[6] >> 16) & 0xff; @@ -495,6 +499,7 @@ #ifdef CONFIG_PPC32 if (pmu_battery_count > 1) pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } + of_node_put(prim); } #endif /* CONFIG_PPC32 */ @@ -1769,35 +1774,21 @@ #endif /* CONFIG_PM */ #if defined(CONFIG_PM) && defined(CONFIG_PPC32) /* Sleep is broadcast last-to-first */ -static int -broadcast_sleep(int when, int fallback) +static void broadcast_sleep(int when) { - int ret = PBOOK_SLEEP_OK; struct list_head *list; struct pmu_sleep_notifier *notifier; for (list = sleep_notifiers.prev; list != &sleep_notifiers; list = list->prev) { notifier = list_entry(list, struct pmu_sleep_notifier, list); - ret = notifier->notifier_call(notifier, when); - if (ret != PBOOK_SLEEP_OK) { - printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n", - when, notifier, notifier->notifier_call); - for (; list != &sleep_notifiers; list = list->next) { - notifier = list_entry(list, struct pmu_sleep_notifier, list); - notifier->notifier_call(notifier, fallback); - } - return ret; - } + notifier->notifier_call(notifier, when); } - return ret; } /* Wake is broadcast first-to-last */ -static int -broadcast_wake(void) +static void broadcast_wake(void) { - int ret = PBOOK_SLEEP_OK; struct list_head *list; struct pmu_sleep_notifier *notifier; @@ -1806,7 +1797,6 @@ broadcast_wake(void) notifier = list_entry(list, struct pmu_sleep_notifier, list); notifier->notifier_call(notifier, PBOOK_WAKE); } - return ret; } /* @@ -2013,12 +2003,8 @@ pmac_suspend_devices(void) pm_prepare_console(); - /* Notify old-style device drivers & userland */ - ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT); - if (ret != PBOOK_SLEEP_OK) { - printk(KERN_ERR "Sleep rejected by drivers\n"); - return -EBUSY; - } + /* Notify old-style device drivers */ + broadcast_sleep(PBOOK_SLEEP_REQUEST); /* Sync the disks. */ /* XXX It would be nice to have some way to ensure that @@ -2028,12 +2014,7 @@ pmac_suspend_devices(void) */ sys_sync(); - /* Sleep can fail now. May not be very robust but useful for debugging */ - ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE); - if (ret != PBOOK_SLEEP_OK) { - printk(KERN_ERR "Driver sleep failed\n"); - return -EBUSY; - } + broadcast_sleep(PBOOK_SLEEP_NOW); /* Send suspend call to devices, hold the device core's dpm_sem */ ret = device_suspend(PMSG_SUSPEND); @@ -2154,7 +2135,7 @@ static int powerbook_sleep_grackle(void) int ret; struct pci_dev *grackle; - grackle = pci_find_slot(0, 0); + grackle = pci_get_bus_and_slot(0, 0); if (!grackle) return -ENODEV; @@ -2202,6 +2183,8 @@ static int powerbook_sleep_grackle(void) pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); pci_write_config_word(grackle, 0x70, pmcr1); + pci_dev_put(grackle); + /* Make sure the PMU is idle */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); restore_via_state(); diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 3f7967f..ab4d1b6 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -176,7 +176,7 @@ static int wf_lm75_attach(struct i2c_ada for (dev = NULL; (dev = of_get_next_child(busnode, dev)) != NULL;) { const char *loc = - get_property(dev, "hwsensor-location", NULL); + of_get_property(dev, "hwsensor-location", NULL); u8 addr; /* We must re-match the adapter in order to properly check diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index eae1189..eaa74af 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c @@ -134,7 +134,7 @@ static int wf_max6690_attach(struct i2c_ if (!device_is_compatible(dev, "max6690")) continue; addr = pmac_i2c_get_dev_addr(dev); - loc = get_property(dev, "hwsensor-location", NULL); + loc = of_get_property(dev, "hwsensor-location", NULL); if (loc == NULL || addr == 0) continue; printk("found max6690, loc=%s addr=0x%02x\n", loc, addr); diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index 31b750d..ff398ad 100644 --- a/drivers/macintosh/windfarm_smu_controls.c +++ b/drivers/macintosh/windfarm_smu_controls.c @@ -167,7 +167,7 @@ static struct smu_fan_control *smu_fan_c if (fct == NULL) return NULL; fct->ctrl.ops = &smu_fan_ops; - l = get_property(node, "location", NULL); + l = of_get_property(node, "location", NULL); if (l == NULL) goto fail; @@ -224,17 +224,17 @@ static struct smu_fan_control *smu_fan_c goto fail; /* Get min & max values*/ - v = get_property(node, "min-value", NULL); + v = of_get_property(node, "min-value", NULL); if (v == NULL) goto fail; fct->min = *v; - v = get_property(node, "max-value", NULL); + v = of_get_property(node, "max-value", NULL); if (v == NULL) goto fail; fct->max = *v; /* Get "reg" value */ - reg = get_property(node, "reg", NULL); + reg = of_get_property(node, "reg", NULL); if (reg == NULL) goto fail; fct->reg = *reg; diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index 83f79de..9a6c2cf 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -241,7 +241,7 @@ static void wf_sat_create(struct i2c_ada char *name; int vsens[2], isens[2]; - reg = get_property(dev, "reg", NULL); + reg = of_get_property(dev, "reg", NULL); if (reg == NULL) return; addr = *reg; @@ -268,9 +268,9 @@ static void wf_sat_create(struct i2c_ada isens[0] = isens[1] = -1; child = NULL; while ((child = of_get_next_child(dev, child)) != NULL) { - reg = get_property(child, "reg", NULL); - type = get_property(child, "device_type", NULL); - loc = get_property(child, "location", NULL); + reg = of_get_property(child, "reg", NULL); + type = of_get_property(child, "device_type", NULL); + loc = of_get_property(child, "location", NULL); if (reg == NULL || loc == NULL) continue; diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c index 01b4c50..9c567b9 100644 --- a/drivers/macintosh/windfarm_smu_sensors.c +++ b/drivers/macintosh/windfarm_smu_sensors.c @@ -204,8 +204,8 @@ static struct smu_ad_sensor *smu_ads_cre ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL); if (ads == NULL) return NULL; - c = get_property(node, "device_type", NULL); - l = get_property(node, "location", NULL); + c = of_get_property(node, "device_type", NULL); + l = of_get_property(node, "location", NULL); if (c == NULL || l == NULL) goto fail; @@ -255,7 +255,7 @@ static struct smu_ad_sensor *smu_ads_cre } else goto fail; - v = get_property(node, "reg", NULL); + v = of_get_property(node, "reg", NULL); if (v == NULL) goto fail; ads->reg = *v; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 4c2471e..d812123 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -867,7 +867,7 @@ static int crypt_ctr(struct dm_target *t goto bad4; } - cc->bs = bioset_create(MIN_IOS, MIN_IOS, 4); + cc->bs = bioset_create(MIN_IOS, MIN_IOS); if (!cc->bs) { ti->error = "Cannot allocate crypt bioset"; goto bad_bs; diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 4eb73d3..8bdc8a8 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -60,7 +60,7 @@ static int resize_pool(unsigned int new_ if (!_io_pool) return -ENOMEM; - _bios = bioset_create(16, 16, 4); + _bios = bioset_create(16, 16); if (!_bios) { mempool_destroy(_io_pool); _io_pool = NULL; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 3668b17..11a98df 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1012,7 +1012,7 @@ static struct mapped_device *alloc_dev(i if (!md->tio_pool) goto bad3; - md->bs = bioset_create(16, 16, 4); + md->bs = bioset_create(16, 16); if (!md->bs) goto bad_no_bioset; diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 03b47a2..cbd1184 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -667,7 +667,7 @@ IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR [ 0x1f ] = KEY_L, [ 0x2b ] = KEY_I, - [ 0x2d ] = KEY_ZOOM, + [ 0x2d ] = KEY_SCREEN, [ 0x1e ] = KEY_ZOOM, [ 0x1b ] = KEY_VOLUMEUP, [ 0x0f ] = KEY_VOLUMEDOWN, @@ -682,12 +682,12 @@ IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR [ 0x3f ] = KEY_UP, [ 0x3e ] = KEY_DOWN, - [ 0x1a ] = KEY_PAUSE, + [ 0x1a ] = KEY_ENTER, [ 0x1d ] = KEY_MENU, - [ 0x19 ] = KEY_PLAY, - [ 0x16 ] = KEY_REWIND, - [ 0x13 ] = KEY_FORWARD, + [ 0x19 ] = KEY_AGAIN, + [ 0x16 ] = KEY_PREVIOUSSONG, + [ 0x13 ] = KEY_NEXTSONG, [ 0x15 ] = KEY_PAUSE, [ 0x0e ] = KEY_REWIND, [ 0x0d ] = KEY_PLAY, @@ -1739,7 +1739,7 @@ IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_ EXPORT_SYMBOL_GPL(ir_codes_encore_enltv); -/* for the Technotrend 1500 bundled remote: */ +/* for the Technotrend 1500 bundled remotes (grey and black): */ IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = { [ 0x01 ] = KEY_POWER, [ 0x02 ] = KEY_SHUFFLE, /* ? double-arrow key */ @@ -1774,6 +1774,12 @@ IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTA [ 0x25 ] = KEY_VOLUMEUP, [ 0x26 ] = KEY_VOLUMEDOWN, [ 0x27 ] = KEY_SETUP, + [ 0x3a ] = KEY_RECORD, /* these keys are only in the black remote */ + [ 0x3b ] = KEY_PLAY, + [ 0x3c ] = KEY_STOP, + [ 0x3d ] = KEY_REWIND, + [ 0x3e ] = KEY_PAUSE, + [ 0x3f ] = KEY_FORWARD, }; EXPORT_SYMBOL_GPL(ir_codes_tt_1500); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 7e0cedc..e3d04a4 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1428,6 +1428,7 @@ static void video_close(struct saa7146_d { struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; struct saa7146_vv *vv = dev->vv_data; + struct videobuf_queue *q = &fh->video_q; int err; if (IS_CAPTURE_ACTIVE(fh) != 0) { @@ -1436,6 +1437,11 @@ static void video_close(struct saa7146_d err = saa7146_stop_preview(fh); } + // release all capture buffers + mutex_lock(&q->lock); + videobuf_read_stop(q); + mutex_unlock(&q->lock); + /* hmm, why is this function declared void? */ /* return err */ } diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index 7987595..a0dcd59 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -9,7 +9,6 @@ config DVB_B2C2_FLEXCOP select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 752cf79..b02c2fd 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -14,7 +14,6 @@ #include "bcm3510.h" #include "stv0297.h" #include "mt312.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "dvb-pll.h" /* lnb control */ @@ -507,7 +506,7 @@ int flexcop_frontend_init(struct flexcop /* try the air atsc 3nd generation (lgdt3303) */ if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; - dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap); + dvb_attach(dvb_pll_attach, fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_lg_tdvs_h06xf); info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c index 6e16680..01af4d2 100644 --- a/drivers/media/dvb/b2c2/flexcop-pci.c +++ b/drivers/media/dvb/b2c2/flexcop-pci.c @@ -127,10 +127,11 @@ static irqreturn_t flexcop_pci_isr(int i { struct flexcop_pci *fc_pci = dev_id; struct flexcop_device *fc = fc_pci->fc_dev; + unsigned long flags; flexcop_ibi_value v; irqreturn_t ret = IRQ_HANDLED; - spin_lock_irq(&fc_pci->irq_lock); + spin_lock_irqsave(&fc_pci->irq_lock,flags); v = fc->read_ibi_reg(fc,irq_20c); @@ -194,7 +195,7 @@ static irqreturn_t flexcop_pci_isr(int i ret = IRQ_NONE; } - spin_unlock_irq(&fc_pci->irq_lock); + spin_unlock_irqrestore(&fc_pci->irq_lock,flags); return ret; } @@ -293,12 +294,12 @@ static int flexcop_pci_init(struct flexc } pci_set_drvdata(fc_pci->pdev, fc_pci); - + spin_lock_init(&fc_pci->irq_lock); if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr, IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0) goto err_pci_iounmap; - spin_lock_init(&fc_pci->irq_lock); + fc_pci->init_state |= FC_PCI_INIT; return ret; diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index dd66b60..cfd6fb7 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -7,7 +7,7 @@ config DVB_BT8XX select DVB_CX24110 if !DVB_FE_CUSTOMISE select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE + select DVB_PLL select DVB_ZL10353 if !DVB_FE_CUSTOMISE select FW_LOADER help diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index 83b090e..df72b4b 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -393,9 +393,7 @@ static struct cards card_list[] __devini { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" }, { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV" }, - { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" }, - - { 0, -1, NULL } + { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" } }; diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 58f69f6..4f1c09b 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -610,7 +610,8 @@ static void frontend_init(struct dvb_bt8 lgdt330x_reset(card); card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); if (card->fe != NULL) { - dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter); + dvb_attach(dvb_pll_attach, card->fe, 0x61, + card->i2c_adapter, &dvb_pll_lg_tdvs_h06xf); dprintk ("dvb_bt8xx: lgdt330x detected\n"); } break; diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h index e75f417..436880e 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -37,8 +37,8 @@ #include "nxt6000.h" #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "zl10353.h" +#include "dvb-pll.h" struct dvb_bt8xx_card { struct mutex lock; diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index a5c0e1a..275df65 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -132,6 +132,11 @@ static int dvb_dvr_open(struct inode *in if (mutex_lock_interruptible(&dmxdev->mutex)) return -ERESTARTSYS; + if (dmxdev->exit) { + mutex_unlock(&dmxdev->mutex); + return -ENODEV; + } + if ((file->f_flags & O_ACCMODE) == O_RDWR) { if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) { mutex_unlock(&dmxdev->mutex); @@ -171,6 +176,7 @@ static int dvb_dvr_open(struct inode *in dmxdev->demux->disconnect_frontend(dmxdev->demux); dmxdev->demux->connect_frontend(dmxdev->demux, front); } + dvbdev->users++; mutex_unlock(&dmxdev->mutex); return 0; } @@ -198,7 +204,16 @@ static int dvb_dvr_release(struct inode vfree(mem); } } - mutex_unlock(&dmxdev->mutex); + /* TODO */ + dvbdev->users--; + if(dvbdev->users==-1 && dmxdev->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + mutex_unlock(&dmxdev->mutex); + wake_up(&dvbdev->wait_queue); + } else + mutex_unlock(&dmxdev->mutex); + return 0; } @@ -215,6 +230,11 @@ static ssize_t dvb_dvr_write(struct file return -EINVAL; if (mutex_lock_interruptible(&dmxdev->mutex)) return -ERESTARTSYS; + + if (dmxdev->exit) { + mutex_unlock(&dmxdev->mutex); + return -ENODEV; + } ret = dmxdev->demux->write(dmxdev->demux, buf, count); mutex_unlock(&dmxdev->mutex); return ret; @@ -227,6 +247,11 @@ static ssize_t dvb_dvr_read(struct file struct dmxdev *dmxdev = dvbdev->priv; int ret; + if (dmxdev->exit) { + mutex_unlock(&dmxdev->mutex); + return -ENODEV; + } + //mutex_lock(&dmxdev->mutex); ret = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, file->f_flags & O_NONBLOCK, @@ -665,6 +690,8 @@ static int dvb_demux_open(struct inode * dmxdevfilter->feed.ts = NULL; init_timer(&dmxdevfilter->timer); + dvbdev->users++; + mutex_unlock(&dmxdev->mutex); return 0; } @@ -943,7 +970,21 @@ static int dvb_demux_release(struct inod struct dmxdev_filter *dmxdevfilter = file->private_data; struct dmxdev *dmxdev = dmxdevfilter->dev; - return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); + int ret; + + ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); + + mutex_lock(&dmxdev->mutex); + dmxdev->dvbdev->users--; + if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + mutex_unlock(&dmxdev->mutex); + wake_up(&dmxdev->dvbdev->wait_queue); + } else + mutex_unlock(&dmxdev->mutex); + + return ret; } static struct file_operations dvb_demux_fops = { @@ -1027,6 +1068,7 @@ static struct file_operations dvb_dvr_fo static struct dvb_device dvbdev_dvr = { .priv = NULL, .readers = 1, + .users = 1, .fops = &dvb_dvr_fops }; @@ -1064,6 +1106,16 @@ EXPORT_SYMBOL(dvb_dmxdev_init); void dvb_dmxdev_release(struct dmxdev *dmxdev) { + dmxdev->exit=1; + if (dmxdev->dvbdev->users > 1) { + wait_event(dmxdev->dvbdev->wait_queue, + dmxdev->dvbdev->users==1); + } + if (dmxdev->dvr_dvbdev->users > 1) { + wait_event(dmxdev->dvr_dvbdev->wait_queue, + dmxdev->dvr_dvbdev->users==1); + } + dvb_unregister_device(dmxdev->dvbdev); dvb_unregister_device(dmxdev->dvr_dvbdev); diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h index d2bee9f..29746e7 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.h +++ b/drivers/media/dvb/dvb-core/dmxdev.h @@ -91,6 +91,8 @@ struct dmxdev { int filternum; int capabilities; + + unsigned int exit:1; #define DMXDEV_CAP_DUPLEX 1 struct dmx_frontend *dvr_orig_fe; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index a21a894..f4e4ca2 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -606,6 +606,7 @@ static void dvb_frontend_stop(struct dvb return; kthread_stop(fepriv->thread); + init_MUTEX (&fepriv->sem); fepriv->state = FESTATE_IDLE; @@ -1023,6 +1024,7 @@ static int dvb_frontend_release(struct i struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; + int ret; dprintk ("%s\n", __FUNCTION__); @@ -1032,7 +1034,14 @@ static int dvb_frontend_release(struct i if (fe->ops.ts_bus_ctrl) fe->ops.ts_bus_ctrl (fe, 0); - return dvb_generic_release (inode, file); + ret = dvb_generic_release (inode, file); + + if (dvbdev->users==-1 && fepriv->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + wake_up(&dvbdev->wait_queue); + } + return ret; } static struct file_operations dvb_frontend_fops = { @@ -1092,8 +1101,15 @@ int dvb_unregister_frontend(struct dvb_f dprintk ("%s\n", __FUNCTION__); mutex_lock(&frontend_mutex); - dvb_unregister_device (fepriv->dvbdev); dvb_frontend_stop (fe); + mutex_unlock(&frontend_mutex); + + if (fepriv->dvbdev->users < -1) + wait_event(fepriv->dvbdev->wait_queue, + fepriv->dvbdev->users==-1); + + mutex_lock(&frontend_mutex); + dvb_unregister_device (fepriv->dvbdev); /* fe is invalid now */ kfree(fepriv); diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 76e9c36..4ebf33a 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -174,7 +174,7 @@ static unsigned short dvb_net_eth_type_t struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb_pull(skb,dev->hard_header_len); eth = eth_hdr(skb); @@ -600,6 +600,7 @@ #endif /* Check CRC32, we've got it in our skb already. */ unsigned short ulen = htons(priv->ule_sndu_len); unsigned short utype = htons(priv->ule_sndu_type); + const u8 *tail; struct kvec iov[3] = { { &ulen, sizeof ulen }, { &utype, sizeof utype }, @@ -613,10 +614,11 @@ #endif } ule_crc = iov_crc32(ule_crc, iov, 3); - expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 | - *((u8 *)priv->ule_skb->tail - 3) << 16 | - *((u8 *)priv->ule_skb->tail - 2) << 8 | - *((u8 *)priv->ule_skb->tail - 1); + tail = skb_tail_pointer(priv->ule_skb); + expected_crc = *(tail - 4) << 24 | + *(tail - 3) << 16 | + *(tail - 2) << 8 | + *(tail - 1); if (ule_crc != expected_crc) { printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n", priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0); @@ -695,7 +697,9 @@ #endif } else { - memcpy(dest_addr, priv->ule_skb->data, ETH_ALEN); + skb_copy_from_linear_data(priv->ule_skb, + dest_addr, + ETH_ALEN); skb_pull(priv->ule_skb, ETH_ALEN); } } @@ -1435,11 +1439,36 @@ static int dvb_net_ioctl(struct inode *i return dvb_usercopy(inode, file, cmd, arg, dvb_net_do_ioctl); } +static int dvb_net_close(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_net *dvbnet = dvbdev->priv; + + if (!dvbdev) + return -ENODEV; + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + dvbdev->readers++; + } else { + dvbdev->writers++; + } + + dvbdev->users++; + + if(dvbdev->users == 1 && dvbnet->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + wake_up(&dvbdev->wait_queue); + } + return 0; +} + + static struct file_operations dvb_net_fops = { .owner = THIS_MODULE, .ioctl = dvb_net_ioctl, .open = dvb_generic_open, - .release = dvb_generic_release, + .release = dvb_net_close, }; static struct dvb_device dvbdev_net = { @@ -1454,6 +1483,11 @@ void dvb_net_release (struct dvb_net *dv { int i; + dvbnet->exit = 1; + if (dvbnet->dvbdev->users < 1) + wait_event(dvbnet->dvbdev->wait_queue, + dvbnet->dvbdev->users==1); + dvb_unregister_device(dvbnet->dvbdev); for (i=0; iadapter = adap; dvbdev->priv = priv; dvbdev->fops = dvbdevfops; + init_waitqueue_head (&dvbdev->wait_queue); memcpy(dvbdev->fops, template->fops, sizeof(struct file_operations)); dvbdev->fops->owner = adap->module; diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 620e788..6dff10e 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -69,6 +69,7 @@ struct dvb_device { int writers; int users; + wait_queue_head_t wait_queue; /* don't really need those !? -- FIXME: use video_usercopy */ int (*kernel_ioctl)(struct inode *inode, struct file *file, unsigned int cmd, void *arg); diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 80f67a5..5448873 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -33,6 +33,7 @@ config DVB_USB_A800 config DVB_USB_DIBUSB_MB tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" depends on DVB_USB + select DVB_PLL select DVB_DIB3000MB select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help @@ -88,6 +89,7 @@ config DVB_USB_DIB0700 config DVB_USB_UMT_010 tristate "HanfTek UMT-010 DVB-T USB2.0 support" depends on DVB_USB + select DVB_PLL select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help @@ -96,9 +98,9 @@ config DVB_USB_UMT_010 config DVB_USB_CXUSB tristate "Conexant USB2.0 hybrid reference design support" depends on DVB_USB + select DVB_PLL select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE help @@ -140,6 +142,7 @@ config DVB_USB_AU6610 config DVB_USB_DIGITV tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" depends on DVB_USB + select DVB_PLL select DVB_NXT6000 if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE help @@ -208,3 +211,10 @@ config DVB_USB_DTT200U The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan). The WT-220U and its clones are pen-sized. + +config DVB_USB_OPERA1 + tristate "Opera1 DVB-S USB2.0 receiver" + depends on DVB_USB + select DVB_STV0299 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Opera DVB-S USB2.0 receiver. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 40f28f5..976f840 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -51,4 +51,8 @@ obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb- dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o +dvb-usb-opera-objs = opera1.o +obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o + + EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c index 0dc66a8..18e0b16 100644 --- a/drivers/media/dvb/dvb-usb/au6610.c +++ b/drivers/media/dvb/dvb-usb/au6610.c @@ -40,7 +40,7 @@ static int au6610_usb_msg(struct dvb_usb } ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, - USB_TYPE_VENDOR|USB_DIR_IN, addr, index, usb_buf, + USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf, sizeof(usb_buf), AU6610_USB_TIMEOUT); if (ret < 0) @@ -124,7 +124,7 @@ static int au6610_identify_state(struct } static struct zl10353_config au6610_zl10353_config = { - .demod_address = 0x1e, + .demod_address = 0x0f, .no_tuner = 1, .parallel_ts = 1, }; @@ -140,7 +140,7 @@ static int au6610_zl10353_frontend_attac } static struct qt1010_config au6610_qt1010_config = { - .i2c_address = 0xc4 + .i2c_address = 0x62 }; static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap) diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 127a94b..bac2ae3 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -27,7 +27,6 @@ #include "cxusb.h" #include "cx22702.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "mt352.h" #include "mt352_priv.h" #include "zl10353.h" @@ -388,7 +387,8 @@ static int cxusb_dtt7579_tuner_attach(st static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap); + dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap, + &dvb_pll_lg_tdvs_h06xf); return 0; } diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 6a4d150..dddf164 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -56,10 +56,6 @@ static int dib0700_ctrl_rd(struct dvb_us if (txlen > 3) index |= tx[3]; - /* think about swapping here */ - value = le16_to_cpu(value); - index = le16_to_cpu(index); - status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0], USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen, USB_CTRL_GET_TIMEOUT); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 148386a..97715f7 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -13,6 +13,7 @@ #define _DVB_USB_IDS_H_ #define USB_VID_ADSTECH 0x06e1 #define USB_VID_ALCOR_MICRO 0x058f #define USB_VID_ANCHOR 0x0547 +#define USB_VID_ANUBIS_ELECTRONIC 0x10fd #define USB_VID_AVERMEDIA 0x07ca #define USB_VID_COMPRO 0x185b #define USB_VID_COMPRO_UNK 0x145f @@ -31,6 +32,7 @@ #define USB_VID_LEADTEK 0x0413 #define USB_VID_LITEON 0x04ca #define USB_VID_MEDION 0x1660 #define USB_VID_MSI 0x0db0 +#define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 @@ -127,6 +129,7 @@ #define USB_PID_KYE_DVB_T_COLD 0x701e #define USB_PID_KYE_DVB_T_WARM 0x701f #define USB_PID_PCTV_200E 0x020e #define USB_PID_PCTV_400E 0x020f +#define USB_PID_PCTV_450E 0x0222 #define USB_PID_LITEON_DVB_T_COLD 0xf000 #define USB_PID_LITEON_DVB_T_WARM 0xf001 #define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360 @@ -139,6 +142,9 @@ #define USB_PID_WINFAST_DTV_DONGLE_STK77 #define USB_PID_GENPIX_8PSK_COLD 0x0200 #define USB_PID_GENPIX_8PSK_WARM 0x0201 #define USB_PID_SIGMATEK_DVB_110 0x6610 +#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513 +#define USB_PID_OPERA1_COLD 0x2830 +#define USB_PID_OPERA1_WARM 0x3829 #endif diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index c9f38a5..e0587e6 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -12,7 +12,7 @@ #include "zl10353.h" #include "qt1010.h" /* debug */ -int dvb_usb_gl861_debug; +static int dvb_usb_gl861_debug; module_param_named(debug,dvb_usb_gl861_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); @@ -20,7 +20,7 @@ static int gl861_i2c_msg(struct dvb_usb_ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) { u16 index; - u16 value = addr << 8; + u16 value = addr << (8 + 1); int wo = (rbuf == NULL || rlen == 0); /* write-only */ u8 req, type; @@ -101,7 +101,7 @@ static int gl861_identify_state(struct u } static struct zl10353_config gl861_zl10353_config = { - .demod_address = 0x1e, + .demod_address = 0x0f, .no_tuner = 1, .parallel_ts = 1, }; @@ -117,7 +117,7 @@ static int gl861_frontend_attach(struct } static struct qt1010_config gl861_qt1010_config = { - .i2c_address = 0xc4 + .i2c_address = 0x62 }; static int gl861_tuner_attach(struct dvb_usb_adapter *adap) diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index d48b24d..45d7bc2 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -14,6 +14,8 @@ #include "m920x.h" #include "mt352.h" #include "mt352_priv.h" #include "qt1010.h" +#include "tda1004x.h" +#include "tda827x.h" /* debug */ static int dvb_usb_m920x_debug; @@ -47,11 +49,15 @@ static inline int m9206_read(struct usb_ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), request, USB_TYPE_VENDOR | USB_DIR_IN, value, index, data, size, 2000); - if (ret < 0) + if (ret < 0) { + printk(KERN_INFO "m920x_read = error: %d\n", ret); return ret; + } - if (ret != size) + if (ret != size) { + deb_rc("m920x_read = no data\n"); return -EIO; + } return 0; } @@ -64,19 +70,22 @@ static inline int m9206_write(struct usb ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), request, USB_TYPE_VENDOR | USB_DIR_OUT, value, index, NULL, 0, 2000); + return ret; } -static int m9206_rc_init(struct usb_device *udev) +static int m9206_init(struct dvb_usb_device *d) { int ret = 0; /* Remote controller init. */ - if ((ret = m9206_write(udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0) - return ret; + if (d->props.rc_query) { + if ((ret = m9206_write(d->udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0) + return ret; - if ((ret = m9206_write(udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0) - return ret; + if ((ret = m9206_write(d->udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0) + return ret; + } return ret; } @@ -87,16 +96,15 @@ static int m9206_rc_query(struct dvb_usb int i, ret = 0; u8 rc_state[2]; - if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0) goto unlock; if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) goto unlock; - for (i = 0; i < ARRAY_SIZE(megasky_rc_keys); i++) - if (megasky_rc_keys[i].data == rc_state[1]) { - *event = megasky_rc_keys[i].event; + for (i = 0; i < d->props.rc_key_map_size; i++) + if (d->props.rc_key_map[i].data == rc_state[1]) { + *event = d->props.rc_key_map[i].event; switch(rc_state[0]) { case 0x80: @@ -137,53 +145,51 @@ static int m9206_i2c_xfer(struct i2c_ada int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - struct m9206_state *m = d->priv; - int i; + int i, j; int ret = 0; + if (!num) + return -EINVAL; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; - if (num > 2) - return -EINVAL; - for (i = 0; i < num; i++) { - if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].addr, 0x80)) != 0) - goto unlock; - - if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[0], 0x0)) != 0) + if (msg[i].flags & (I2C_M_NO_RD_ACK|I2C_M_IGNORE_NAK|I2C_M_TEN) || + msg[i].len == 0) { + /* For a 0 byte message, I think sending the address to index 0x80|0x40 + * would be the correct thing to do. However, zero byte messages are + * only used for probing, and since we don't know how to get the slave's + * ack, we can't probe. */ + ret = -ENOTSUPP; goto unlock; - - if (i + 1 < num && msg[i + 1].flags & I2C_M_RD) { - int i2c_i; - - for (i2c_i = 0; i2c_i < M9206_I2C_MAX; i2c_i++) - if (msg[i].addr == m->i2c_r[i2c_i].addr) - break; - - if (i2c_i >= M9206_I2C_MAX) { - deb_rc("No magic for i2c addr!\n"); - ret = -EINVAL; + } + /* Send START & address/RW bit */ + if (!(msg[i].flags & I2C_M_NOSTART)) { + if ((ret = m9206_write(d->udev, M9206_I2C, (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), 0x80)) != 0) goto unlock; + /* Should check for ack here, if we knew how. */ + } + if (msg[i].flags & I2C_M_RD) { + for (j = 0; j < msg[i].len; j++) { + /* Last byte of transaction? Send STOP, otherwise send ACK. */ + int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x01; + if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x20|stop, &msg[i].buf[j], 1)) != 0) + goto unlock; } - - if ((ret = m9206_write(d->udev, M9206_I2C, m->i2c_r[i2c_i].magic, 0x80)) != 0) - goto unlock; - - if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x60, msg[i + 1].buf, msg[i + 1].len)) != 0) - goto unlock; - - i++; } else { - if (msg[i].len != 2) - return -EINVAL; - - if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[1], 0x40)) != 0) - goto unlock; + for (j = 0; j < msg[i].len; j++) { + /* Last byte of transaction? Then send STOP. */ + int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x00; + if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[j], stop)) != 0) + goto unlock; + /* Should check for ack here too. */ + } } } - ret = i; - unlock: + ret = num; + +unlock: mutex_unlock(&d->i2c_mutex); return ret; @@ -324,6 +330,7 @@ static int m9206_firmware_download(struc i += size; } if (i != fw->size) { + deb_rc("bad firmware file!\n"); ret = -EINVAL; goto done; } @@ -342,10 +349,10 @@ static int m9206_firmware_download(struc } /* Callbacks for DVB USB */ -static int megasky_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, - int *cold) +static int m920x_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) { struct usb_host_interface *alt; @@ -381,20 +388,15 @@ static int megasky_mt352_demod_init(stru } static struct mt352_config megasky_mt352_config = { - .demod_address = 0x1e, + .demod_address = 0x0f, .no_tuner = 1, .demod_init = megasky_mt352_demod_init, }; static int megasky_mt352_frontend_attach(struct dvb_usb_adapter *adap) { - struct m9206_state *m = adap->dev->priv; - deb_rc("megasky_frontend_attach!\n"); - m->i2c_r[M9206_I2C_DEMOD].addr = megasky_mt352_config.demod_address; - m->i2c_r[M9206_I2C_DEMOD].magic = 0x1f; - if ((adap->fe = dvb_attach(mt352_attach, &megasky_mt352_config, &adap->dev->i2c_adap)) == NULL) return -EIO; @@ -402,16 +404,11 @@ static int megasky_mt352_frontend_attach } static struct qt1010_config megasky_qt1010_config = { - .i2c_address = 0xc4 + .i2c_address = 0x62 }; static int megasky_qt1010_tuner_attach(struct dvb_usb_adapter *adap) { - struct m9206_state *m = adap->dev->priv; - - m->i2c_r[M9206_I2C_TUNER].addr = megasky_qt1010_config.i2c_address; - m->i2c_r[M9206_I2C_TUNER].magic = 0xc5; - if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap, &megasky_qt1010_config) == NULL) return -ENODEV; @@ -419,8 +416,40 @@ static int megasky_qt1010_tuner_attach(s return 0; } +static struct tda1004x_config digivox_tda10046_config = { + .demod_address = 0x08, + .invert = 0, + .invert_oclk = 0, + .ts_mode = TDA10046_TS_SERIAL, + .xtal_freq = TDA10046_XTAL_16M, + .if_freq = TDA10046_FREQ_045, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GPTRI, + .request_firmware = NULL, +}; + +static int digivox_tda10046_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb_rc("digivox_tda10046_frontend_attach!\n"); + + if ((adap->fe = dvb_attach(tda10046_attach, &digivox_tda10046_config, + &adap->dev->i2c_adap)) == NULL) + return -EIO; + + return 0; +} + +static int digivox_tda8275_tuner_attach(struct dvb_usb_adapter *adap) +{ + if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, + NULL) == NULL) + return -ENODEV; + return 0; +} + /* DVB USB Driver stuff */ static struct dvb_usb_device_properties megasky_properties; +static struct dvb_usb_device_properties digivox_mini_ii_properties; static int m920x_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -429,30 +458,36 @@ static int m920x_probe(struct usb_interf struct usb_host_interface *alt; int ret; - if ((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) { - deb_rc("probed!\n"); + deb_rc("Probed!\n"); - alt = usb_altnum_to_altsetting(intf, 1); - if (alt == NULL) { - deb_rc("not alt found!\n"); - return -ENODEV; - } + if (((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) || + ((ret = dvb_usb_device_init(intf, &digivox_mini_ii_properties, THIS_MODULE, &d)) == 0)) + goto found; - ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, - alt->desc.bAlternateSetting); - if (ret < 0) - return ret; - - deb_rc("Changed to alternate setting!\n"); + return ret; - if ((ret = m9206_rc_init(d->udev)) != 0) - return ret; +found: + alt = usb_altnum_to_altsetting(intf, 1); + if (alt == NULL) { + deb_rc("No alt found!\n"); + return -ENODEV; } + + ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + if (ret < 0) + return ret; + + if ((ret = m9206_init(d)) != 0) + return ret; + return ret; } static struct usb_device_id m920x_table [] = { { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) }, + { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, + USB_PID_MSI_DIGI_VOX_MINI_II) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, m920x_table); @@ -471,7 +506,7 @@ static struct dvb_usb_device_properties .size_of_priv = sizeof(struct m9206_state), - .identify_state = megasky_identify_state, + .identify_state = m920x_identify_state, .num_adapters = 1, .adapter = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | @@ -502,6 +537,50 @@ static struct dvb_usb_device_properties { "MSI Mega Sky 580 DVB-T USB2.0", { &m920x_table[0], NULL }, { NULL }, + } + } +}; + +static struct dvb_usb_device_properties digivox_mini_ii_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-digivox-02.fw", + .download_firmware = m9206_firmware_download, + + .size_of_priv = sizeof(struct m9206_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m9206_pid_filter, + .pid_filter_ctrl = m9206_pid_filter_ctrl, + + .frontend_attach = digivox_tda10046_frontend_attach, + .tuner_attach = digivox_tda8275_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 0x4000, + } + } + }, + }}, + .i2c_algo = &m9206_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "MSI DIGI VOX mini II DVB-T USB2.0", + { &m920x_table[1], NULL }, + { NULL }, }, } }; diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h index c354196..7dd3db6 100644 --- a/drivers/media/dvb/dvb-usb/m920x.h +++ b/drivers/media/dvb/dvb-usb/m920x.h @@ -19,17 +19,49 @@ #define M9206_FW 0x30 #define M9206_MAX_FILTERS 8 -#define M9206_I2C_TUNER 0 -#define M9206_I2C_DEMOD 1 -#define M9206_I2C_MAX 2 +/* +sequences found in logs: +[index value] +0x80 write addr +(0x00 out byte)* +0x40 out byte + +0x80 write addr +(0x00 out byte)* +0x80 read addr +(0x21 in byte)* +0x60 in byte + +this sequence works: +0x80 read addr +(0x21 in byte)* +0x60 in byte + +Guess at API of the I2C function: +I2C operation is done one byte at a time with USB control messages. The +index the messages is sent to is made up of a set of flags that control +the I2C bus state: +0x80: Send START condition. After a START condition, one would normally + always send the 7-bit slave I2C address as the 7 MSB, followed by + the read/write bit as the LSB. +0x40: Send STOP condition. This should be set on the last byte of an + I2C transaction. +0x20: Read a byte from the slave. As opposed to writing a byte to the + slave. The slave will normally not produce any data unless you + set the R/W bit to 1 when sending the slave's address after the + START condition. +0x01: Respond with ACK, as opposed to a NACK. For a multi-byte read, + the master should send an ACK, that is pull SDA low during the 9th + clock cycle, after every byte but the last. This flags only makes + sense when bit 0x20 is set, indicating a read. + +What any other bits might mean, or how to get the slave's ACK/NACK +response to a write, is unknown. +*/ struct m9206_state { u16 filters[M9206_MAX_FILTERS]; int filtering_enabled; int rep_count; - struct { - unsigned char addr; - unsigned char magic; - }i2c_r[M9206_I2C_MAX]; }; #endif diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c new file mode 100644 index 0000000..518d7ad --- /dev/null +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -0,0 +1,581 @@ +/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card +* +* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org) +* Copyright (C) 2006 Marco Gittler (g.marco@freenet.de) +* +* 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, version 2. +* +* see Documentation/dvb/README.dvb-usb for more information +*/ + +#include "opera1.h" +#include "stv0299.h" + +#define OPERA_READ_MSG 0 +#define OPERA_WRITE_MSG 1 +#define OPERA_I2C_TUNER 0xd1 + +#define READ_FX2_REG_REQ 0xba +#define READ_MAC_ADDR 0x08 +#define OPERA_WRITE_FX2 0xbb +#define OPERA_TUNER_REQ 0xb1 +#define REG_1F_SYMBOLRATE_BYTE0 0x1f +#define REG_20_SYMBOLRATE_BYTE1 0x20 +#define REG_21_SYMBOLRATE_BYTE2 0x21 + +#define ADDR_B600_VOLTAGE_13V (0x02) +#define ADDR_B601_VOLTAGE_18V (0x03) +#define ADDR_B1A6_STREAM_CTRL (0x04) +#define ADDR_B880_READ_REMOTE (0x05) + +struct opera1_state { + u32 last_key_pressed; +}; +struct opera_rc_keys { + u32 keycode; + u32 event; +}; + +int dvb_usb_opera1_debug; +module_param_named(debug, dvb_usb_opera1_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))." + DVB_USB_DEBUG_STATUS); + +static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value, + u8 * data, u16 len, int flags) +{ + int ret; + u8 r; + u8 u8buf[len]; + + unsigned int pipe = (flags == OPERA_READ_MSG) ? + usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0); + u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; + + if (flags == OPERA_WRITE_MSG) + memcpy(u8buf, data, len); + ret = + usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR, + value, 0x0, u8buf, len, 2000); + + if (request == OPERA_TUNER_REQ) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR, + 0x01, 0x0, &r, 1, 2000)<1 || r!=0x08) + return 0; + } + if (flags == OPERA_READ_MSG) + memcpy(data, u8buf, len); + return ret; +} + +/* I2C */ + +static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr, + u8 * buf, u16 len) +{ + int ret = 0; + u8 request; + u16 value; + + if (!dev) { + info("no usb_device"); + return -EINVAL; + } + if (mutex_lock_interruptible(&dev->usb_mutex) < 0) + return -EAGAIN; + + switch (addr>>1){ + case ADDR_B600_VOLTAGE_13V: + request=0xb6; + value=0x00; + break; + case ADDR_B601_VOLTAGE_18V: + request=0xb6; + value=0x01; + break; + case ADDR_B1A6_STREAM_CTRL: + request=0xb1; + value=0xa6; + break; + case ADDR_B880_READ_REMOTE: + request=0xb8; + value=0x80; + break; + default: + request=0xb1; + value=addr; + } + ret = opera1_xilinx_rw(dev->udev, request, + value, buf, len, + addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG); + + mutex_unlock(&dev->usb_mutex); + return ret; +} + +static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0, tmp = 0; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + if ((tmp = opera1_usb_i2c_msgxfer(d, + (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), + msg[i].buf, + msg[i].len + )!= msg[i].len)) { + break; + } + if (dvb_usb_opera1_debug & 0x10) + info("sending i2c mesage %d %d", tmp, msg[i].len); + } + mutex_unlock(&d->i2c_mutex); + return num; +} + +static u32 opera1_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm opera1_i2c_algo = { + .master_xfer = opera1_i2c_xfer, + .functionality = opera1_i2c_func, +}; + +static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + static u8 command_13v[1]={0x00}; + static u8 command_18v[1]={0x01}; + struct i2c_msg msg[] = { + {.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1}, + }; + struct dvb_usb_adapter *udev_adap = + (struct dvb_usb_adapter *)(fe->dvb->priv); + if (voltage == SEC_VOLTAGE_18) { + msg[0].addr = ADDR_B601_VOLTAGE_18V; + msg[0].buf = command_18v; + } + i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, + u32 ratio) +{ + stv0299_writereg(fe, 0x13, 0x98); + stv0299_writereg(fe, 0x14, 0x95); + stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff); + stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff); + stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0); + return 0; + +} +static u8 opera1_inittab[] = { + 0x00, 0xa1, + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, + 0x05, 0x05, + 0x06, 0x02, + 0x07, 0x00, + 0x0b, 0x00, + 0x0c, 0x01, + 0x0d, 0x81, + 0x0e, 0x44, + 0x0f, 0x19, + 0x10, 0x3f, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x98, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0xeb, + 0x17, 0x00, + 0x18, 0x19, + 0x19, 0x8b, + 0x1a, 0x00, + 0x1b, 0x82, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + REG_1F_SYMBOLRATE_BYTE0, 0x06, + REG_20_SYMBOLRATE_BYTE1, 0x50, + REG_21_SYMBOLRATE_BYTE2, 0x10, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x37, + 0x25, 0xbc, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x00, + 0x29, 0x1e, + 0x2a, 0x14, + 0x2b, 0x1f, + 0x2c, 0x09, + 0x2d, 0x0a, + 0x2e, 0x00, + 0x2f, 0x00, + 0x30, 0x00, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x13, + 0xff, 0xff, +}; + +static struct stv0299_config opera1_stv0299_config = { + .demod_address = 0xd0>>1, + .min_delay_ms = 100, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .inittab = opera1_inittab, + .set_symbol_rate = opera1_stv0299_set_symbol_rate, +}; + +static int opera1_frontend_attach(struct dvb_usb_adapter *d) +{ + if ((d->fe = + dvb_attach(stv0299_attach, &opera1_stv0299_config, + &d->dev->i2c_adap)) != NULL) { + d->fe->ops.set_voltage = opera1_set_voltage; + return 0; + } + info("not attached stv0299"); + return -EIO; +} + +static int opera1_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach( + dvb_pll_attach, adap->fe, 0xc0>>1, + &adap->dev->i2c_adap, &dvb_pll_opera1 + ); + return 0; +} + +static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 val = onoff ? 0x01 : 0x00; + + if (dvb_usb_opera1_debug) + info("power %s", onoff ? "on" : "off"); + return opera1_xilinx_rw(d->udev, 0xb7, val, + &val, 1, OPERA_WRITE_MSG); +} + +static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + static u8 buf_start[2] = { 0xff, 0x03 }; + static u8 buf_stop[2] = { 0xff, 0x00 }; + struct i2c_msg start_tuner[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2}, + }; + if (dvb_usb_opera1_debug) + info("streaming %s", onoff ? "on" : "off"); + i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1); + return 0; +} + +static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + u8 b_pid[3]; + struct i2c_msg msg[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, + }; + if (dvb_usb_opera1_debug) + info("pidfilter index: %d pid: %d %s", index, pid, + onoff ? "on" : "off"); + b_pid[0] = (2 * index) + 4; + b_pid[1] = onoff ? (pid & 0xff) : (0x00); + b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00); + i2c_transfer(&adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) +{ + int u = 0x04; + u8 b_pid[3]; + struct i2c_msg msg[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, + }; + if (dvb_usb_opera1_debug) + info("%s hw-pidfilter", onoff ? "enable" : "disable"); + for (; u < 0x7e; u += 2) { + b_pid[0] = u; + b_pid[1] = 0; + b_pid[2] = 0x80; + i2c_transfer(&adap->dev->i2c_adap, msg, 1); + } + return 0; +} + +static struct dvb_usb_rc_key opera1_rc_keys[] = { + {0x5f, 0xa0, KEY_1}, + {0x51, 0xaf, KEY_2}, + {0x5d, 0xa2, KEY_3}, + {0x41, 0xbe, KEY_4}, + {0x0b, 0xf5, KEY_5}, + {0x43, 0xbd, KEY_6}, + {0x47, 0xb8, KEY_7}, + {0x49, 0xb6, KEY_8}, + {0x05, 0xfa, KEY_9}, + {0x45, 0xba, KEY_0}, + {0x09, 0xf6, KEY_UP}, /*chanup */ + {0x1b, 0xe5, KEY_DOWN}, /*chandown */ + {0x5d, 0xa3, KEY_LEFT}, /*voldown */ + {0x5f, 0xa1, KEY_RIGHT}, /*volup */ + {0x07, 0xf8, KEY_SPACE}, /*tab */ + {0x1f, 0xe1, KEY_ENTER}, /*play ok */ + {0x1b, 0xe4, KEY_Z}, /*zoom */ + {0x59, 0xa6, KEY_M}, /*mute */ + {0x5b, 0xa5, KEY_F}, /*tv/f */ + {0x19, 0xe7, KEY_R}, /*rec */ + {0x01, 0xfe, KEY_S}, /*Stop */ + {0x03, 0xfd, KEY_P}, /*pause */ + {0x03, 0xfc, KEY_W}, /*<- -> */ + {0x07, 0xf9, KEY_C}, /*capture */ + {0x47, 0xb9, KEY_Q}, /*exit */ + {0x43, 0xbc, KEY_O}, /*power */ + +}; + +static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) +{ + struct opera1_state *opst = dev->priv; + u8 rcbuffer[32]; + const u16 startmarker1 = 0x10ed; + const u16 startmarker2 = 0x11ec; + struct i2c_msg read_remote[] = { + {.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32}, + }; + int i = 0; + u32 send_key = 0; + + if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) { + for (i = 0; i < 32; i++) { + if (rcbuffer[i]) + send_key |= 1; + if (i < 31) + send_key = send_key << 1; + } + if (send_key & 0x8000) + send_key = (send_key << 1) | (send_key >> 15 & 0x01); + + if (send_key == 0xffff && opst->last_key_pressed != 0) { + *state = REMOTE_KEY_REPEAT; + *event = opst->last_key_pressed; + return 0; + } + for (; send_key != 0;) { + if (send_key >> 16 == startmarker2) { + break; + } else if (send_key >> 16 == startmarker1) { + send_key = + (send_key & 0xfffeffff) | (startmarker1 << 16); + break; + } else + send_key >>= 1; + } + + if (send_key == 0) + return 0; + + send_key = (send_key & 0xffff) | 0x0100; + + for (i = 0; i < ARRAY_SIZE(opera1_rc_keys); i++) { + if ((opera1_rc_keys[i].custom * 256 + + opera1_rc_keys[i].data) == (send_key & 0xffff)) { + *state = REMOTE_KEY_PRESSED; + *event = opera1_rc_keys[i].event; + opst->last_key_pressed = + opera1_rc_keys[i].event; + break; + } + opst->last_key_pressed = 0; + } + } else + *state = REMOTE_NO_KEY_PRESSED; + return 0; +} + +static struct usb_device_id opera1_table[] = { + {USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)}, + {USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, opera1_table); + +static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + u8 command[] = { READ_MAC_ADDR }; + opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG); + opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG); + return 0; +} +static int opera1_xilinx_load_firmware(struct usb_device *dev, + const char *filename) +{ + const struct firmware *fw = NULL; + u8 *b, *p; + int ret = 0, i; + u8 testval; + info("start downloading fpga firmware"); + + if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems.", + filename); + return ret; + } else { + p = kmalloc(fw->size, GFP_KERNEL); + opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG); + if (p != NULL && testval != 0x67) { + + u8 reset = 0, fpga_command = 0; + memcpy(p, fw->data, fw->size); + /* clear fpga ? */ + opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1, + OPERA_WRITE_MSG); + for (i = 0; p[i] != 0 && i < fw->size;) { + b = (u8 *) p + i; + if (opera1_xilinx_rw + (dev, OPERA_WRITE_FX2, 0x0, b + 1, b[0], + OPERA_WRITE_MSG) != b[0] + ) { + err("error while transferring firmware"); + ret = -EINVAL; + break; + } + i = i + 1 + b[0]; + } + /* restart the CPU */ + if (ret || opera1_xilinx_rw + (dev, 0xa0, 0xe600, &reset, 1, + OPERA_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + kfree(p); + } + } + if (fw) { + release_firmware(fw); + } + return ret; +} + +static struct dvb_usb_device_properties opera1_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-opera-01.fw", + .size_of_priv = sizeof(struct opera1_state), + + .power_ctrl = opera1_power_ctrl, + .i2c_algo = &opera1_i2c_algo, + + .rc_key_map = opera1_rc_keys, + .rc_key_map_size = ARRAY_SIZE(opera1_rc_keys), + .rc_interval = 200, + .rc_query = opera1_rc_query, + .read_mac_address = opera1_read_mac_address, + .generic_bulk_ctrl_endpoint = 0x00, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .adapter = { + { + .frontend_attach = opera1_frontend_attach, + .streaming_ctrl = opera1_streaming_ctrl, + .tuner_attach = opera1_tuner_attach, + .caps = + DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter = opera1_pid_filter, + .pid_filter_ctrl = opera1_pid_filter_control, + .pid_filter_count = 252, + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } + }, + .num_device_descs = 1, + .devices = { + {"Opera1 DVB-S USB2.0", + {&opera1_table[0], NULL}, + {&opera1_table[1], NULL}, + }, + } +}; + +static int opera1_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_device *udev = interface_to_usbdev(intf); + + if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM && + udev->descriptor.idVendor == USB_VID_OPERA1 && + (d == NULL + || opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga.fw") != 0) + ) { + return -EINVAL; + } + + if (dvb_usb_device_init(intf, &opera1_properties, THIS_MODULE, &d) != 0) + return -EINVAL; + return 0; +} + +static struct usb_driver opera1_driver = { + .name = "opera1", + .probe = opera1_probe, + .disconnect = dvb_usb_device_exit, + .id_table = opera1_table, +}; + +static int __init opera1_module_init(void) +{ + int result = 0; + if ((result = usb_register(&opera1_driver))) { + err("usb_register failed. Error number %d", result); + } + return result; +} + +static void __exit opera1_module_exit(void) +{ + usb_deregister(&opera1_driver); +} + +module_init(opera1_module_init); +module_exit(opera1_module_exit); + +MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org"); +MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de"); +MODULE_DESCRIPTION("Driver for Opera1 DVB-S device"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/opera1.h b/drivers/media/dvb/dvb-usb/opera1.h new file mode 100644 index 0000000..5317442 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/opera1.h @@ -0,0 +1,9 @@ +#ifndef _OPERA1_H_ +#define _OPERA1_H_ + +#define DVB_USB_LOG_PREFIX "opera" +#include "dvb-usb.h" + +extern int dvb_usb_opera1_debug; +#define deb_xfer(args...) dprintk(dvb_usb_opera1_debug,0x02,args) +#endif diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c index 95d2997..88dc436 100644 --- a/drivers/media/dvb/dvb-usb/ttusb2.c +++ b/drivers/media/dvb/dvb-usb/ttusb2.c @@ -184,6 +184,7 @@ static int ttusb2_probe(struct usb_inter static struct usb_device_id ttusb2_table [] = { { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, ttusb2_table); @@ -227,12 +228,16 @@ static struct dvb_usb_device_properties .generic_bulk_ctrl_endpoint = 0x01, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "Pinnacle 400e DVB-S USB2.0", { &ttusb2_table[0], NULL }, { NULL }, }, + { "Pinnacle 450e DVB-S USB2.0", + { &ttusb2_table[1], NULL }, + { NULL }, + }, } }; diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 22c2cf2..ff44876 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -205,6 +205,13 @@ config DVB_TDA10021 help A DVB-C tuner module. Say Y when you want to support this frontend. +config DVB_TDA10023 + tristate "Philips TDA10023 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-C tuner module. Say Y when you want to support this frontend. + config DVB_STV0297 tristate "ST STV0297 based" depends on DVB_CORE && I2C @@ -280,8 +287,12 @@ comment "Tuners/PLL support" depends on DVB_CORE config DVB_PLL - tristate + tristate "Generic I2C PLL based tuners" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + This module driver a number of tuners based on PLL chips with a + common I2C interface. Say Y when you want to support these tuners. config DVB_TDA826X tristate "Philips TDA826X silicon tuner" @@ -290,6 +301,13 @@ config DVB_TDA826X help A DVB-S silicon tuner module. Say Y when you want to support this tuner. +config DVB_TDA827X + tristate "Philips TDA827X silicon tuner" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T silicon tuner module. Say Y when you want to support this tuner. + config DVB_TUNER_QT1010 tristate "Quantek QT1010 silicon tuner" depends on DVB_CORE && I2C @@ -304,14 +322,6 @@ config DVB_TUNER_MT2060 help A driver for the silicon IF tuner MT2060 from Microtune. -config DVB_TUNER_LGH06XF - tristate "LG TDVS-H06xF ATSC tuner" - depends on DVB_CORE && I2C - select DVB_PLL - default m if DVB_FE_CUSTOMISE - help - A driver for the LG TDVS-H06xF ATSC tuner family. - comment "Miscellaneous devices" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index a646d99..27f3865 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_DVB_MT352) += mt352.o obj-$(CONFIG_DVB_ZL10353) += zl10353.o obj-$(CONFIG_DVB_CX22702) += cx22702.o obj-$(CONFIG_DVB_TDA10021) += tda10021.o +obj-$(CONFIG_DVB_TDA10023) += tda10023.o obj-$(CONFIG_DVB_STV0297) += stv0297.o obj-$(CONFIG_DVB_NXT200X) += nxt200x.o obj-$(CONFIG_DVB_OR51211) += or51211.o @@ -37,7 +38,7 @@ obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_ISL6421) += isl6421.o obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA826X) += tda826x.o +obj-$(CONFIG_DVB_TDA827X) += tda827x.o obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o -obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 62de760..5f96ffd 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -27,17 +27,29 @@ #include "dvb-pll.h" /* ----------------------------------------------------------- */ /* descriptions */ +/* Set AGC TOP value to 103 dBuV: + 0x80 = Control Byte + 0x40 = 250 uA charge pump (irrelevant) + 0x18 = Aux Byte to follow + 0x06 = 64.5 kHz divider (irrelevant) + 0x01 = Disable Vt (aka sleep) + + 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA) + 0x50 = AGC Take over point = 103 dBuV */ +static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 }; + struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { .name = "Thomson dtt7579", .min = 177000000, .max = 858000000, - .count = 5, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0xb4, 0x03 }, + .count = 4, .entries = { - { 0, 36166667, 166666, 0xb4, 0x03 }, /* go sleep */ - { 443250000, 36166667, 166666, 0xb4, 0x02 }, - { 542000000, 36166667, 166666, 0xb4, 0x08 }, - { 771000000, 36166667, 166666, 0xbc, 0x08 }, - { 999999999, 36166667, 166666, 0xf4, 0x08 }, + { 443250000, 166667, 0xb4, 0x02 }, + { 542000000, 166667, 0xb4, 0x08 }, + { 771000000, 166667, 0xbc, 0x08 }, + { 999999999, 166667, 0xf4, 0x08 }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt7579); @@ -46,11 +58,12 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7 .name = "Thomson dtt7610", .min = 44000000, .max = 958000000, + .iffreq= 44000000, .count = 3, .entries = { - { 157250000, 44000000, 62500, 0x8e, 0x39 }, - { 454000000, 44000000, 62500, 0x8e, 0x3a }, - { 999999999, 44000000, 62500, 0x8e, 0x3c }, + { 157250000, 62500, 0x8e, 0x39 }, + { 454000000, 62500, 0x8e, 0x3a }, + { 999999999, 62500, 0x8e, 0x3c }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt7610); @@ -66,14 +79,15 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7 .min = 177000000, .max = 896000000, .setbw = thomson_dtt759x_bw, - .count = 6, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0x84, 0x03 }, + .count = 5, .entries = { - { 0, 36166667, 166666, 0x84, 0x03 }, - { 264000000, 36166667, 166666, 0xb4, 0x02 }, - { 470000000, 36166667, 166666, 0xbc, 0x02 }, - { 735000000, 36166667, 166666, 0xbc, 0x08 }, - { 835000000, 36166667, 166666, 0xf4, 0x08 }, - { 999999999, 36166667, 166666, 0xfc, 0x08 }, + { 264000000, 166667, 0xb4, 0x02 }, + { 470000000, 166667, 0xbc, 0x02 }, + { 735000000, 166667, 0xbc, 0x08 }, + { 835000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x08 }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt759x); @@ -82,14 +96,15 @@ struct dvb_pll_desc dvb_pll_lg_z201 = { .name = "LG z201", .min = 174000000, .max = 862000000, - .count = 6, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0xbc, 0x03 }, + .count = 5, .entries = { - { 0, 36166667, 166666, 0xbc, 0x03 }, - { 157500000, 36166667, 166666, 0xbc, 0x01 }, - { 443250000, 36166667, 166666, 0xbc, 0x02 }, - { 542000000, 36166667, 166666, 0xbc, 0x04 }, - { 830000000, 36166667, 166666, 0xf4, 0x04 }, - { 999999999, 36166667, 166666, 0xfc, 0x04 }, + { 157500000, 166667, 0xbc, 0x01 }, + { 443250000, 166667, 0xbc, 0x02 }, + { 542000000, 166667, 0xbc, 0x04 }, + { 830000000, 166667, 0xf4, 0x04 }, + { 999999999, 166667, 0xfc, 0x04 }, }, }; EXPORT_SYMBOL(dvb_pll_lg_z201); @@ -98,11 +113,12 @@ struct dvb_pll_desc dvb_pll_microtune_40 .name = "Microtune 4042 FI5", .min = 57000000, .max = 858000000, + .iffreq= 44000000, .count = 3, .entries = { - { 162000000, 44000000, 62500, 0x8e, 0xa1 }, - { 457000000, 44000000, 62500, 0x8e, 0x91 }, - { 999999999, 44000000, 62500, 0x8e, 0x31 }, + { 162000000, 62500, 0x8e, 0xa1 }, + { 457000000, 62500, 0x8e, 0x91 }, + { 999999999, 62500, 0x8e, 0x31 }, }, }; EXPORT_SYMBOL(dvb_pll_microtune_4042); @@ -112,11 +128,13 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7 .name = "Thomson dtt761x", .min = 57000000, .max = 863000000, + .iffreq= 44000000, .count = 3, + .initdata = tua603x_agc103, .entries = { - { 147000000, 44000000, 62500, 0x8e, 0x39 }, - { 417000000, 44000000, 62500, 0x8e, 0x3a }, - { 999999999, 44000000, 62500, 0x8e, 0x3c }, + { 147000000, 62500, 0x8e, 0x39 }, + { 417000000, 62500, 0x8e, 0x3a }, + { 999999999, 62500, 0x8e, 0x3c }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt761x); @@ -125,17 +143,18 @@ struct dvb_pll_desc dvb_pll_unknown_1 = .name = "unknown 1", /* used by dntv live dvb-t */ .min = 174000000, .max = 862000000, + .iffreq= 36166667, .count = 9, .entries = { - { 150000000, 36166667, 166666, 0xb4, 0x01 }, - { 173000000, 36166667, 166666, 0xbc, 0x01 }, - { 250000000, 36166667, 166666, 0xb4, 0x02 }, - { 400000000, 36166667, 166666, 0xbc, 0x02 }, - { 420000000, 36166667, 166666, 0xf4, 0x02 }, - { 470000000, 36166667, 166666, 0xfc, 0x02 }, - { 600000000, 36166667, 166666, 0xbc, 0x08 }, - { 730000000, 36166667, 166666, 0xf4, 0x08 }, - { 999999999, 36166667, 166666, 0xfc, 0x08 }, + { 150000000, 166667, 0xb4, 0x01 }, + { 173000000, 166667, 0xbc, 0x01 }, + { 250000000, 166667, 0xb4, 0x02 }, + { 400000000, 166667, 0xbc, 0x02 }, + { 420000000, 166667, 0xf4, 0x02 }, + { 470000000, 166667, 0xfc, 0x02 }, + { 600000000, 166667, 0xbc, 0x08 }, + { 730000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x08 }, }, }; EXPORT_SYMBOL(dvb_pll_unknown_1); @@ -147,11 +166,12 @@ struct dvb_pll_desc dvb_pll_tua6010xs = .name = "Infineon TUA6010XS", .min = 44250000, .max = 858000000, + .iffreq= 36125000, .count = 3, .entries = { - { 115750000, 36125000, 62500, 0x8e, 0x03 }, - { 403250000, 36125000, 62500, 0x8e, 0x06 }, - { 999999999, 36125000, 62500, 0x8e, 0x85 }, + { 115750000, 62500, 0x8e, 0x03 }, + { 403250000, 62500, 0x8e, 0x06 }, + { 999999999, 62500, 0x8e, 0x85 }, }, }; EXPORT_SYMBOL(dvb_pll_tua6010xs); @@ -161,12 +181,13 @@ struct dvb_pll_desc dvb_pll_env57h1xd5 = .name = "Panasonic ENV57H1XD5", .min = 44250000, .max = 858000000, + .iffreq= 36125000, .count = 4, .entries = { - { 153000000, 36291666, 166666, 0xc2, 0x41 }, - { 470000000, 36291666, 166666, 0xc2, 0x42 }, - { 526000000, 36291666, 166666, 0xc2, 0x84 }, - { 999999999, 36291666, 166666, 0xc2, 0xa4 }, + { 153000000, 166667, 0xc2, 0x41 }, + { 470000000, 166667, 0xc2, 0x42 }, + { 526000000, 166667, 0xc2, 0x84 }, + { 999999999, 166667, 0xc2, 0xa4 }, }, }; EXPORT_SYMBOL(dvb_pll_env57h1xd5); @@ -185,20 +206,21 @@ struct dvb_pll_desc dvb_pll_tda665x = { .min = 44250000, .max = 858000000, .setbw = tda665x_bw, + .iffreq= 36166667, .count = 12, .entries = { - { 93834000, 36249333, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, - { 123834000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, - { 161000000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, - { 163834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, - { 253834000, 36249333, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, - { 383834000, 36249333, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, - { 443834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, - { 444000000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, - { 583834000, 36249333, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ }, - { 793834000, 36249333, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ }, - { 444834000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, - { 861000000, 36249333, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, + { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, + { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, + { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, + { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, + { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ }, + { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ }, + { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, + { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, } }; EXPORT_SYMBOL(dvb_pll_tda665x); @@ -216,12 +238,13 @@ struct dvb_pll_desc dvb_pll_tua6034 = { .name = "Infineon TUA6034", .min = 44250000, .max = 858000000, + .iffreq= 36166667, .count = 3, .setbw = tua6034_bw, .entries = { - { 174500000, 36166667, 62500, 0xce, 0x01 }, - { 230000000, 36166667, 62500, 0xce, 0x02 }, - { 999999999, 36166667, 62500, 0xce, 0x04 }, + { 174500000, 62500, 0xce, 0x01 }, + { 230000000, 62500, 0xce, 0x02 }, + { 999999999, 62500, 0xce, 0x04 }, }, }; EXPORT_SYMBOL(dvb_pll_tua6034); @@ -233,11 +256,13 @@ struct dvb_pll_desc dvb_pll_lg_tdvs_h06x .name = "LG TDVS-H06xF", .min = 54000000, .max = 863000000, + .iffreq= 44000000, + .initdata = tua603x_agc103, .count = 3, .entries = { - { 165000000, 44000000, 62500, 0xce, 0x01 }, - { 450000000, 44000000, 62500, 0xce, 0x02 }, - { 999999999, 44000000, 62500, 0xce, 0x04 }, + { 165000000, 62500, 0xce, 0x01 }, + { 450000000, 62500, 0xce, 0x02 }, + { 999999999, 62500, 0xce, 0x04 }, }, }; EXPORT_SYMBOL(dvb_pll_lg_tdvs_h06xf); @@ -255,16 +280,17 @@ struct dvb_pll_desc dvb_pll_fmd1216me = .name = "Philips FMD1216ME", .min = 50870000, .max = 858000000, + .iffreq= 36125000, .setbw = fmd1216me_bw, .count = 7, .entries = { - { 143870000, 36213333, 166667, 0xbc, 0x41 }, - { 158870000, 36213333, 166667, 0xf4, 0x41 }, - { 329870000, 36213333, 166667, 0xbc, 0x42 }, - { 441870000, 36213333, 166667, 0xf4, 0x42 }, - { 625870000, 36213333, 166667, 0xbc, 0x44 }, - { 803870000, 36213333, 166667, 0xf4, 0x44 }, - { 999999999, 36213333, 166667, 0xfc, 0x44 }, + { 143870000, 166667, 0xbc, 0x41 }, + { 158870000, 166667, 0xf4, 0x41 }, + { 329870000, 166667, 0xbc, 0x42 }, + { 441870000, 166667, 0xf4, 0x42 }, + { 625870000, 166667, 0xbc, 0x44 }, + { 803870000, 166667, 0xf4, 0x44 }, + { 999999999, 166667, 0xfc, 0x44 }, } }; EXPORT_SYMBOL(dvb_pll_fmd1216me); @@ -282,13 +308,14 @@ struct dvb_pll_desc dvb_pll_tded4 = { .name = "ALPS TDED4", .min = 47000000, .max = 863000000, + .iffreq= 36166667, .setbw = tded4_bw, .count = 4, .entries = { - { 153000000, 36166667, 166667, 0x85, 0x01 }, - { 470000000, 36166667, 166667, 0x85, 0x02 }, - { 823000000, 36166667, 166667, 0x85, 0x08 }, - { 999999999, 36166667, 166667, 0x85, 0x88 }, + { 153000000, 166667, 0x85, 0x01 }, + { 470000000, 166667, 0x85, 0x02 }, + { 823000000, 166667, 0x85, 0x08 }, + { 999999999, 166667, 0x85, 0x88 }, } }; EXPORT_SYMBOL(dvb_pll_tded4); @@ -300,12 +327,13 @@ struct dvb_pll_desc dvb_pll_tdhu2 = { .name = "ALPS TDHU2", .min = 54000000, .max = 864000000, + .iffreq= 44000000, .count = 4, .entries = { - { 162000000, 44000000, 62500, 0x85, 0x01 }, - { 426000000, 44000000, 62500, 0x85, 0x02 }, - { 782000000, 44000000, 62500, 0x85, 0x08 }, - { 999999999, 44000000, 62500, 0x85, 0x88 }, + { 162000000, 62500, 0x85, 0x01 }, + { 426000000, 62500, 0x85, 0x02 }, + { 782000000, 62500, 0x85, 0x08 }, + { 999999999, 62500, 0x85, 0x88 }, } }; EXPORT_SYMBOL(dvb_pll_tdhu2); @@ -317,11 +345,12 @@ struct dvb_pll_desc dvb_pll_tuv1236d = { .name = "Philips TUV1236D", .min = 54000000, .max = 864000000, + .iffreq= 44000000, .count = 3, .entries = { - { 157250000, 44000000, 62500, 0xc6, 0x41 }, - { 454000000, 44000000, 62500, 0xc6, 0x42 }, - { 999999999, 44000000, 62500, 0xc6, 0x44 }, + { 157250000, 62500, 0xc6, 0x41 }, + { 454000000, 62500, 0xc6, 0x42 }, + { 999999999, 62500, 0xc6, 0x44 }, }, }; EXPORT_SYMBOL(dvb_pll_tuv1236d); @@ -333,14 +362,15 @@ struct dvb_pll_desc dvb_pll_samsung_tbmv .name = "Samsung TBMV30111IN / TBMV30712IN1", .min = 54000000, .max = 860000000, + .iffreq= 44000000, .count = 6, .entries = { - { 172000000, 44000000, 166666, 0xb4, 0x01 }, - { 214000000, 44000000, 166666, 0xb4, 0x02 }, - { 467000000, 44000000, 166666, 0xbc, 0x02 }, - { 721000000, 44000000, 166666, 0xbc, 0x08 }, - { 841000000, 44000000, 166666, 0xf4, 0x08 }, - { 999999999, 44000000, 166666, 0xfc, 0x02 }, + { 172000000, 166667, 0xb4, 0x01 }, + { 214000000, 166667, 0xb4, 0x02 }, + { 467000000, 166667, 0xbc, 0x02 }, + { 721000000, 166667, 0xbc, 0x08 }, + { 841000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x02 }, } }; EXPORT_SYMBOL(dvb_pll_samsung_tbmv); @@ -352,12 +382,13 @@ struct dvb_pll_desc dvb_pll_philips_sd18 .name = "Philips SD1878", .min = 950000, .max = 2150000, + .iffreq= 249, /* zero-IF, offset 249 is to round up */ .count = 4, .entries = { - { 1250000, 499, 500, 0xc4, 0x00}, - { 1550000, 499, 500, 0xc4, 0x40}, - { 2050000, 499, 500, 0xc4, 0x80}, - { 2150000, 499, 500, 0xc4, 0xc0}, + { 1250000, 500, 0xc4, 0x00}, + { 1550000, 500, 0xc4, 0x40}, + { 2050000, 500, 0xc4, 0x80}, + { 2150000, 500, 0xc4, 0xc0}, }, }; EXPORT_SYMBOL(dvb_pll_philips_sd1878_tda8261); @@ -388,18 +419,19 @@ struct dvb_pll_desc dvb_pll_philips_td13 .name = "Philips TD1316", .min = 87000000, .max = 895000000, + .iffreq= 36166667, .setbw = td1316_bw, .count = 9, .entries = { - { 93834000, 36166000, 166666, 0xca, 0x60}, - { 123834000, 36166000, 166666, 0xca, 0xa0}, - { 163834000, 36166000, 166666, 0xca, 0xc0}, - { 253834000, 36166000, 166666, 0xca, 0x60}, - { 383834000, 36166000, 166666, 0xca, 0xa0}, - { 443834000, 36166000, 166666, 0xca, 0xc0}, - { 583834000, 36166000, 166666, 0xca, 0x60}, - { 793834000, 36166000, 166666, 0xca, 0xa0}, - { 858834000, 36166000, 166666, 0xca, 0xe0}, + { 93834000, 166667, 0xca, 0x60}, + { 123834000, 166667, 0xca, 0xa0}, + { 163834000, 166667, 0xca, 0xc0}, + { 253834000, 166667, 0xca, 0x60}, + { 383834000, 166667, 0xca, 0xa0}, + { 443834000, 166667, 0xca, 0xc0}, + { 583834000, 166667, 0xca, 0x60}, + { 793834000, 166667, 0xca, 0xa0}, + { 858834000, 166667, 0xca, 0xe0}, }, }; EXPORT_SYMBOL(dvb_pll_philips_td1316); @@ -409,15 +441,41 @@ struct dvb_pll_desc dvb_pll_thomson_fe66 .name = "Thomson FE6600", .min = 44250000, .max = 858000000, + .iffreq= 36125000, .count = 4, .entries = { - { 250000000, 36213333, 166667, 0xb4, 0x12 }, - { 455000000, 36213333, 166667, 0xfe, 0x11 }, - { 775500000, 36213333, 166667, 0xbc, 0x18 }, - { 999999999, 36213333, 166667, 0xf4, 0x18 }, + { 250000000, 166667, 0xb4, 0x12 }, + { 455000000, 166667, 0xfe, 0x11 }, + { 775500000, 166667, 0xbc, 0x18 }, + { 999999999, 166667, 0xf4, 0x18 }, } }; EXPORT_SYMBOL(dvb_pll_thomson_fe6600); +static void opera1_bw(u8 *buf, u32 freq, int bandwidth) +{ + if (bandwidth == BANDWIDTH_8_MHZ) + buf[2] |= 0x08; +} + +struct dvb_pll_desc dvb_pll_opera1 = { + .name = "Opera Tuner", + .min = 900000, + .max = 2250000, + .iffreq= 0, + .setbw = opera1_bw, + .count = 8, + .entries = { + { 1064000, 500, 0xe5, 0xc6 }, + { 1169000, 500, 0xe5, 0xe6 }, + { 1299000, 500, 0xe5, 0x24 }, + { 1444000, 500, 0xe5, 0x44 }, + { 1606000, 500, 0xe5, 0x64 }, + { 1777000, 500, 0xe5, 0x84 }, + { 1941000, 500, 0xe5, 0xa4 }, + { 2250000, 500, 0xe5, 0xc4 }, + } +}; +EXPORT_SYMBOL(dvb_pll_opera1); struct dvb_pll_priv { /* i2c details */ @@ -459,7 +517,8 @@ int dvb_pll_configure(struct dvb_pll_des if (i == desc->count) return -EINVAL; - div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize; + div = (freq + desc->iffreq + desc->entries[i].stepsize/2) / + desc->entries[i].stepsize; buf[0] = div >> 8; buf[1] = div & 0xff; buf[2] = desc->entries[i].config; @@ -473,7 +532,7 @@ int dvb_pll_configure(struct dvb_pll_des desc->name, div, buf[0], buf[1], buf[2], buf[3]); // calculate the frequency we set it to - return (div * desc->entries[i].stepsize) - desc->entries[i].offset; + return (div * desc->entries[i].stepsize) - desc->iffreq; } EXPORT_SYMBOL(dvb_pll_configure); @@ -487,35 +546,27 @@ static int dvb_pll_release(struct dvb_fr static int dvb_pll_sleep(struct dvb_frontend *fe) { struct dvb_pll_priv *priv = fe->tuner_priv; - u8 buf[4]; - struct i2c_msg msg = - { .addr = priv->pll_i2c_address, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - int i; - int result; if (priv->i2c == NULL) return -EINVAL; - for (i = 0; i < priv->pll_desc->count; i++) { - if (priv->pll_desc->entries[i].limit == 0) - break; - } - if (i == priv->pll_desc->count) - return 0; + if (priv->pll_desc->sleepdata) { + struct i2c_msg msg = { .flags = 0, + .addr = priv->pll_i2c_address, + .buf = priv->pll_desc->sleepdata + 1, + .len = priv->pll_desc->sleepdata[0] }; - buf[0] = 0; - buf[1] = 0; - buf[2] = priv->pll_desc->entries[i].config; - buf[3] = priv->pll_desc->entries[i].cb; + int result; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { - return result; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + return 0; } - - return 0; + /* Shouldn't be called when initdata is NULL, maybe BUG()? */ + return -EINVAL; } static int dvb_pll_set_params(struct dvb_frontend *fe, @@ -599,9 +650,35 @@ static int dvb_pll_get_bandwidth(struct return 0; } +static int dvb_pll_init(struct dvb_frontend *fe) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + + if (priv->i2c == NULL) + return -EINVAL; + + if (priv->pll_desc->initdata) { + struct i2c_msg msg = { .flags = 0, + .addr = priv->pll_i2c_address, + .buf = priv->pll_desc->initdata + 1, + .len = priv->pll_desc->initdata[0] }; + + int result; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + return 0; + } + /* Shouldn't be called when initdata is NULL, maybe BUG()? */ + return -EINVAL; +} + static struct dvb_tuner_ops dvb_pll_tuner_ops = { .release = dvb_pll_release, .sleep = dvb_pll_sleep, + .init = dvb_pll_init, .set_params = dvb_pll_set_params, .calc_regs = dvb_pll_calc_regs, .get_frequency = dvb_pll_get_frequency, @@ -640,9 +717,14 @@ struct dvb_frontend *dvb_pll_attach(stru memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops)); - strncpy(fe->ops.tuner_ops.info.name, desc->name, 128); + strncpy(fe->ops.tuner_ops.info.name, desc->name, + sizeof(fe->ops.tuner_ops.info.name)); fe->ops.tuner_ops.info.frequency_min = desc->min; fe->ops.tuner_ops.info.frequency_min = desc->max; + if (!desc->initdata) + fe->ops.tuner_ops.init = NULL; + if (!desc->sleepdata) + fe->ops.tuner_ops.sleep = NULL; fe->tuner_priv = priv; return fe; diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index 681186a..5209f46 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -12,11 +12,13 @@ struct dvb_pll_desc { char *name; u32 min; u32 max; + u32 iffreq; void (*setbw)(u8 *buf, u32 freq, int bandwidth); + u8 *initdata; + u8 *sleepdata; int count; struct { u32 limit; - u32 offset; u32 stepsize; u8 config; u8 cb; @@ -46,6 +48,7 @@ extern struct dvb_pll_desc dvb_pll_phili extern struct dvb_pll_desc dvb_pll_philips_td1316; extern struct dvb_pll_desc dvb_pll_thomson_fe6600; +extern struct dvb_pll_desc dvb_pll_opera1; extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, u32 freq, int bandwidth); @@ -59,9 +62,20 @@ extern int dvb_pll_configure(struct dvb_ * @param desc dvb_pll_desc to use. * @return Frontend pointer on success, NULL on failure */ +#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE)) extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); +#else +static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, + int pll_addr, + struct i2c_adapter *i2c, + struct dvb_pll_desc *desc) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif #endif diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index 68aad0f..e25286e 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -475,7 +475,7 @@ static int lgdt3302_read_status(struct d *status |= FE_HAS_CARRIER; break; default: - printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); + printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); } return 0; @@ -534,7 +534,7 @@ static int lgdt3303_read_status(struct d } break; default: - printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); + printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); } return 0; } diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c deleted file mode 100644 index 2202d0c..0000000 --- a/drivers/media/dvb/frontends/lgh06xf.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "dvb-pll.h" -#include "lgh06xf.h" - -#define LG_H06XF_PLL_I2C_ADDR 0x61 - -struct lgh06xf_priv { - struct i2c_adapter *i2c; - u32 frequency; -}; - -static int lgh06xf_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static int lgh06xf_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) -{ - struct lgh06xf_priv *priv = fe->tuner_priv; - u8 buf[4]; - struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - u32 frequency; - int result; - - if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, - params->frequency, 0)) < 0) - return result; - else - frequency = result; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "lgh06xf: %s error " - "(addr %02x <- %02x, result = %i)\n", - __FUNCTION__, buf[0], buf[1], result); - if (result < 0) - return result; - else - return -EREMOTEIO; - } - - /* Set the Auxiliary Byte. */ - buf[0] = buf[2]; - buf[0] &= ~0x20; - buf[0] |= 0x18; - buf[1] = 0x50; - msg.len = 2; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "lgh06xf: %s error " - "(addr %02x <- %02x, result = %i)\n", - __FUNCTION__, buf[0], buf[1], result); - if (result < 0) - return result; - else - return -EREMOTEIO; - } - - priv->frequency = frequency; - - return 0; -} - -static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct lgh06xf_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static struct dvb_tuner_ops lgh06xf_tuner_ops = { - .release = lgh06xf_release, - .set_params = lgh06xf_set_params, - .get_frequency = lgh06xf_get_frequency, -}; - -struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c) -{ - struct lgh06xf_priv *priv = NULL; - - priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->i2c = i2c; - - memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name, - sizeof(fe->ops.tuner_ops.info.name)); - - fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min; - fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max; - - fe->tuner_priv = priv; - return fe; -} - -EXPORT_SYMBOL(lgh06xf_attach); - -MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support"); -MODULE_AUTHOR("Michael Krufky"); -MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h deleted file mode 100644 index 510b4be..0000000 --- a/drivers/media/dvb/frontends/lgh06xf.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _LGH06XF_H_ -#define _LGH06XF_H_ -#include "dvb_frontend.h" - -#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE)) -extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, - struct i2c_adapter *i2c); -#else -static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, - struct i2c_adapter *i2c) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); - return NULL; -} -#endif /* CONFIG_DVB_TUNER_LGH06XF */ - -#endif /* _LGH06XF_H_ */ diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c index 5a3a6e5..4e0aca7 100644 --- a/drivers/media/dvb/frontends/or51132.c +++ b/drivers/media/dvb/frontends/or51132.c @@ -1,6 +1,9 @@ /* * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM * + * + * Copyright (C) 2007 Trent Piepho + * * Copyright (C) 2005 Kirk Lapray * * Based on code from Jack Kelliher (kelliher@xmission.com) @@ -69,46 +72,70 @@ struct or51132_state u32 current_frequency; }; -static int i2c_writebytes (struct or51132_state* state, u8 reg, u8 *buf, int len) + +/* Write buffer to demod */ +static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len) { int err; - struct i2c_msg msg; - msg.addr = reg; - msg.flags = 0; - msg.len = len; - msg.buf = buf; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = (u8*)buf, .len = len }; + /* msleep(20); */ /* doesn't appear to be necessary */ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51132: i2c_writebytes error (addr %02x, err == %i)\n", reg, err); + printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n", + msg.addr, msg.len, err); return -EREMOTEIO; } - return 0; } -static u8 i2c_readbytes (struct or51132_state* state, u8 reg, u8* buf, int len) +/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00); + Less code and more efficient that loading a buffer on the stack with + the bytes to send and then calling or51132_writebuf() on that. */ +#define or51132_writebytes(state, data...) \ + ({ const static u8 _data[] = {data}; \ + or51132_writebuf(state, _data, sizeof(_data)); }) + +/* Read data from demod into buffer. Returns 0 on success. */ +static int or51132_readbuf(struct or51132_state *state, u8 *buf, int len) { int err; - struct i2c_msg msg; - msg.addr = reg; - msg.flags = I2C_M_RD; - msg.len = len; - msg.buf = buf; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = I2C_M_RD, .buf = buf, .len = len }; + /* msleep(20); */ /* doesn't appear to be necessary */ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51132: i2c_readbytes error (addr %02x, err == %i)\n", reg, err); + printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n", + msg.addr, msg.len, err); return -EREMOTEIO; } - return 0; } +/* Reads a 16-bit demod register. Returns <0 on error. */ +static int or51132_readreg(struct or51132_state *state, u8 reg) +{ + u8 buf[2] = { 0x04, reg }; + struct i2c_msg msg[2] = { + {.addr = state->config->demod_address, .flags = 0, + .buf = buf, .len = 2 }, + {.addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = buf, .len = 2 }}; + int err; + + if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) { + printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n", + reg, err); + return -EREMOTEIO; + } + return le16_to_cpup((u16*)buf); +} + static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) { struct or51132_state* state = fe->demodulator_priv; - static u8 run_buf[] = {0x7F,0x01}; + const static u8 run_buf[] = {0x7F,0x01}; u8 rec_buf[8]; - u8 cmd_buf[3]; u32 firmwareAsize, firmwareBsize; int i,ret; @@ -121,30 +148,21 @@ static int or51132_load_firmware (struct dprintk("FirmwareB is %i bytes\n",firmwareBsize); /* Upload firmware */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - &fw->data[8],firmwareAsize))) { + if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) { printk(KERN_WARNING "or51132: load_firmware error 1\n"); return ret; } - msleep(1); /* 1ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - &fw->data[8+firmwareAsize],firmwareBsize))) { + if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize], + firmwareBsize))) { printk(KERN_WARNING "or51132: load_firmware error 2\n"); return ret; } - msleep(1); /* 1ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - run_buf,2))) { + if ((ret = or51132_writebuf(state, run_buf, 2))) { printk(KERN_WARNING "or51132: load_firmware error 3\n"); return ret; } - - /* Wait at least 5 msec */ - msleep(20); /* 10ms */ - - if ((ret = i2c_writebytes(state,state->config->demod_address, - run_buf,2))) { + if ((ret = or51132_writebuf(state, run_buf, 2))) { printk(KERN_WARNING "or51132: load_firmware error 4\n"); return ret; } @@ -154,43 +172,25 @@ static int or51132_load_firmware (struct /* Read back ucode version to besure we loaded correctly and are really up and running */ /* Get uCode version */ - cmd_buf[0] = 0x10; - cmd_buf[1] = 0x10; - cmd_buf[2] = 0x00; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,3))) { + if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) { printk(KERN_WARNING "or51132: load_firmware error a\n"); return ret; } - - cmd_buf[0] = 0x04; - cmd_buf[1] = 0x17; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,2))) { + if ((ret = or51132_writebytes(state, 0x04, 0x17))) { printk(KERN_WARNING "or51132: load_firmware error b\n"); return ret; } - - cmd_buf[0] = 0x00; - cmd_buf[1] = 0x00; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,2))) { + if ((ret = or51132_writebytes(state, 0x00, 0x00))) { printk(KERN_WARNING "or51132: load_firmware error c\n"); return ret; } - - for(i=0;i<4;i++) { - msleep(20); /* 20ms */ + for (i=0;i<4;i++) { /* Once upon a time, this command might have had something to do with getting the firmware version, but it's not used anymore: {0x04,0x00,0x30,0x00,i+1} */ /* Read 8 bytes, two bytes at a time */ - if ((ret = i2c_readbytes(state,state->config->demod_address, - &rec_buf[i*2],2))) { + if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) { printk(KERN_WARNING "or51132: load_firmware error d - %d\n",i); return ret; @@ -204,12 +204,7 @@ static int or51132_load_firmware (struct rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f, rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f); - cmd_buf[0] = 0x10; - cmd_buf[1] = 0x00; - cmd_buf[2] = 0x00; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,3))) { + if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) { printk(KERN_WARNING "or51132: load_firmware error e\n"); return ret; } @@ -241,70 +236,55 @@ static int or51132_sleep(struct dvb_fron static int or51132_setmode(struct dvb_frontend* fe) { struct or51132_state* state = fe->demodulator_priv; - unsigned char cmd_buf[3]; + u8 cmd_buf1[3] = {0x04, 0x01, 0x5f}; + u8 cmd_buf2[3] = {0x1c, 0x00, 0 }; dprintk("setmode %d\n",(int)state->current_modulation); - /* set operation mode in Receiver 1 register; */ - cmd_buf[0] = 0x04; - cmd_buf[1] = 0x01; + switch (state->current_modulation) { - case QAM_256: - case QAM_64: - case QAM_AUTO: - /* Auto-deinterleave; MPEG ser, MPEG2tr, phase noise-high*/ - cmd_buf[2] = 0x5F; - break; case VSB_8: - /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high*/ - cmd_buf[2] = 0x50; + /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */ + cmd_buf1[2] = 0x50; + /* REC MODE inv IF spectrum, Normal */ + cmd_buf2[1] = 0x03; + /* Channel MODE ATSC/VSB8 */ + cmd_buf2[2] = 0x06; break; - default: - printk("setmode:Modulation set to unsupported value\n"); - }; - if (i2c_writebytes(state,state->config->demod_address, - cmd_buf,3)) { - printk(KERN_WARNING "or51132: set_mode error 1\n"); - return -1; - } - dprintk("or51132: set #1 to %02x\n", cmd_buf[2]); - - /* Set operation mode in Receiver 6 register */ - cmd_buf[0] = 0x1C; - switch (state->current_modulation) { + /* All QAM modes are: + Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high + REC MODE Normal Carrier Lock */ case QAM_AUTO: - /* REC MODE Normal Carrier Lock */ - cmd_buf[1] = 0x00; /* Channel MODE Auto QAM64/256 */ - cmd_buf[2] = 0x4f; + cmd_buf2[2] = 0x4f; break; case QAM_256: - /* REC MODE Normal Carrier Lock */ - cmd_buf[1] = 0x00; /* Channel MODE QAM256 */ - cmd_buf[2] = 0x45; + cmd_buf2[2] = 0x45; break; case QAM_64: - /* REC MODE Normal Carrier Lock */ - cmd_buf[1] = 0x00; /* Channel MODE QAM64 */ - cmd_buf[2] = 0x43; - break; - case VSB_8: - /* REC MODE inv IF spectrum, Normal */ - cmd_buf[1] = 0x03; - /* Channel MODE ATSC/VSB8 */ - cmd_buf[2] = 0x06; + cmd_buf2[2] = 0x43; break; default: - printk("setmode: Modulation set to unsupported value\n"); - }; - msleep(20); /* 20ms */ - if (i2c_writebytes(state,state->config->demod_address, - cmd_buf,3)) { + printk(KERN_WARNING + "or51132: setmode: Modulation set to unsupported value (%d)\n", + state->current_modulation); + return -EINVAL; + } + + /* Set Receiver 1 register */ + if (or51132_writebuf(state, cmd_buf1, 3)) { + printk(KERN_WARNING "or51132: set_mode error 1\n"); + return -EREMOTEIO; + } + dprintk("set #1 to %02x\n", cmd_buf1[2]); + + /* Set operation mode in Receiver 6 register */ + if (or51132_writebuf(state, cmd_buf2, 3)) { printk(KERN_WARNING "or51132: set_mode error 2\n"); - return -1; + return -EREMOTEIO; } - dprintk("or51132: set #6 to 0x%02x%02x\n", cmd_buf[1], cmd_buf[2]); + dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]); return 0; } @@ -401,28 +381,23 @@ static int or51132_get_parameters(struct struct dvb_frontend_parameters *param) { struct or51132_state* state = fe->demodulator_priv; - u8 buf[2]; + int status; + int retry = 1; +start: /* Receiver Status */ - buf[0]=0x04; - buf[1]=0x00; - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,buf,2)) { - printk(KERN_WARNING "or51132: get_parameters write error\n"); - return -EREMOTEIO; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,buf,2)) { - printk(KERN_WARNING "or51132: get_parameters read error\n"); + if ((status = or51132_readreg(state, 0x00)) < 0) { + printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n"); return -EREMOTEIO; } - switch(buf[0]) { + switch(status&0xff) { case 0x06: param->u.vsb.modulation = VSB_8; break; case 0x43: param->u.vsb.modulation = QAM_64; break; case 0x45: param->u.vsb.modulation = QAM_256; break; default: + if (retry--) goto start; printk(KERN_WARNING "or51132: unknown status 0x%02x\n", - buf[0]); + status&0xff); return -EREMOTEIO; } @@ -438,32 +413,21 @@ static int or51132_get_parameters(struct static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status) { struct or51132_state* state = fe->demodulator_priv; - unsigned char rec_buf[2]; - unsigned char snd_buf[2]; - *status = 0; + int reg; /* Receiver Status */ - snd_buf[0]=0x04; - snd_buf[1]=0x00; - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: read_status write error\n"); - return -1; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_status read error\n"); - return -1; - } - dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]); - - if (rec_buf[1] & 0x01) { /* Receiver Lock */ - *status |= FE_HAS_SIGNAL; - *status |= FE_HAS_CARRIER; - *status |= FE_HAS_VITERBI; - *status |= FE_HAS_SYNC; - *status |= FE_HAS_LOCK; + if ((reg = or51132_readreg(state, 0x00)) < 0) { + printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg); + *status = 0; + return -EREMOTEIO; } + dprintk("%s: read_status %04x\n", __FUNCTION__, reg); + + if (reg & 0x0100) /* Receiver Lock */ + *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI| + FE_HAS_SYNC|FE_HAS_LOCK; + else + *status = 0; return 0; } @@ -506,47 +470,30 @@ static u32 calculate_snr(u32 mse, u32 c) static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) { struct or51132_state* state = fe->demodulator_priv; - u8 rec_buf[2]; - u8 snd_buf[2]; - u32 noise; - u32 c; - u32 usK; - - /* Register is same for VSB or QAM firmware */ - snd_buf[0]=0x04; - snd_buf[1]=0x02; /* SNR after Equalizer */ - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: snr write error\n"); - return -EREMOTEIO; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: snr read error\n"); + int noise, reg; + u32 c, usK = 0; + int retry = 1; + +start: + /* SNR after Equalizer */ + noise = or51132_readreg(state, 0x02); + if (noise < 0) { + printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n"); return -EREMOTEIO; } - noise = rec_buf[0] | (rec_buf[1] << 8); - dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise); + dprintk("read_snr noise (%d)\n", noise); /* Read status, contains modulation type for QAM_AUTO and NTSC filter for VSB */ - snd_buf[0]=0x04; - snd_buf[1]=0x00; /* Status register */ - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: status write error\n"); - return -EREMOTEIO; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: status read error\n"); + reg = or51132_readreg(state, 0x00); + if (reg < 0) { + printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n"); return -EREMOTEIO; } - usK = 0; - switch (rec_buf[0]) { + switch (reg&0xff) { case 0x06: - usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0; + if (reg & 0x1000) usK = 3 << 24; /* Fall through to QAM64 case */ case 0x43: c = 150204167; @@ -555,11 +502,12 @@ static int or51132_read_snr(struct dvb_f c = 150290396; break; default: - printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]); + printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff); + if (retry--) goto start; return -EREMOTEIO; } dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__, - rec_buf[0], rec_buf[1]&0x10?"n":"ff"); + reg&0xff, reg&0x1000?"n":"ff"); /* Calculate SNR using noise, c, and NTSC rejection correction */ state->snr = calculate_snr(noise, c) - usK; @@ -671,6 +619,7 @@ MODULE_PARM_DESC(debug, "Turn on/off fro MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver"); MODULE_AUTHOR("Kirk Lapray"); +MODULE_AUTHOR("Trent Piepho"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(or51132_attach); diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c index 5b9c5bb..1105368 100644 --- a/drivers/media/dvb/frontends/tda10021.c +++ b/drivers/media/dvb/frontends/tda10021.c @@ -30,13 +30,13 @@ #include #include #include "dvb_frontend.h" -#include "tda10021.h" +#include "tda1002x.h" struct tda10021_state { struct i2c_adapter* i2c; /* configuration settings */ - const struct tda10021_config* config; + const struct tda1002x_config* config; struct dvb_frontend frontend; u8 pwm; @@ -53,9 +53,6 @@ #endif static int verbose; #define XIN 57840000UL -#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) -#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) -#define HAS_INVERSION(reg0) (!(reg0 & 0x20)) #define FIN (XIN >> 4) @@ -64,7 +61,7 @@ static u8 tda10021_inittab[0x40]= { 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, - 0xb8, 0x3f, 0xa0, 0x00, 0xcd, 0x01, 0x00, 0xff, + 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, @@ -97,7 +94,8 @@ static u8 tda10021_readreg (struct tda10 int ret; ret = i2c_transfer (state->i2c, msg, 2); - if (ret != 2) + // Don't print an error message if the id is read. + if (ret != 2 && reg != 0x1a) printk("DVB: TDA10021: %s: readreg error (ret == %i)\n", __FUNCTION__, ret); return b1[0]; @@ -136,10 +134,10 @@ static int tda10021_setup_reg0 (struct t { reg0 |= state->reg0 & 0x63; - if (INVERSION_ON == inversion) - ENABLE_INVERSION(reg0); - else if (INVERSION_OFF == inversion) - DISABLE_INVERSION(reg0); + if ((INVERSION_ON == inversion) ^ (state->config->invert == 0)) + reg0 &= ~0x20; + else + reg0 |= 0x20; _tda10021_writereg (state, 0x00, reg0 & 0xfe); _tda10021_writereg (state, 0x00, reg0 | 0x01); @@ -201,16 +199,6 @@ static int tda10021_set_symbolrate (stru return 0; } -static int tda10021_write(struct dvb_frontend* fe, u8 *buf, int len) -{ - struct tda10021_state* state = fe->demodulator_priv; - - if (len != 2) - return -EINVAL; - - return _tda10021_writereg(state, buf[0], buf[1]); -} - static int tda10021_init (struct dvb_frontend *fe) { struct tda10021_state* state = fe->demodulator_priv; @@ -258,6 +246,9 @@ static int tda10021_set_parameters (stru if (qam < 0 || qam > 5) return -EINVAL; + if (p->inversion != INVERSION_ON && p->inversion != INVERSION_OFF) + return -EINVAL; + //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate); if (fe->ops.tuner_ops.set_params) { @@ -366,7 +357,7 @@ static int tda10021_get_frontend(struct -((s32)p->u.qam.symbol_rate * afc) >> 10); } - p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF; + p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF; p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; p->u.qam.fec_inner = FEC_NONE; @@ -408,11 +399,12 @@ static void tda10021_release(struct dvb_ static struct dvb_frontend_ops tda10021_ops; -struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, +struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, struct i2c_adapter* i2c, u8 pwm) { struct tda10021_state* state = NULL; + u8 id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL); @@ -425,7 +417,11 @@ struct dvb_frontend* tda10021_attach(con state->reg0 = tda10021_inittab[0]; /* check if the demod is there */ - if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; + id = tda10021_readreg(state, 0x1a); + if ((id & 0xf0) != 0x70) goto error; + + printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n", + state->config->demod_address, id); /* create dvb_frontend */ memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); @@ -447,7 +443,7 @@ static struct dvb_frontend_ops tda10021_ .frequency_max = 858000000, .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ -#if 0 + #if 0 .frequency_tolerance = ???, .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ #endif @@ -461,7 +457,6 @@ #if 0 .init = tda10021_init, .sleep = tda10021_sleep, - .write = tda10021_write, .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, .set_frontend = tda10021_set_parameters, diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h deleted file mode 100644 index e3da780..0000000 --- a/drivers/media/dvb/frontends/tda10021.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - TDA10021 - Single Chip Cable Channel Receiver driver module - used on the the Siemens DVB-C cards - - Copyright (C) 1999 Convergence Integrated Media GmbH - Copyright (C) 2004 Markus Schulz - Support for TDA10021 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef TDA10021_H -#define TDA10021_H - -#include - -struct tda10021_config -{ - /* the demodulator's i2c address */ - u8 demod_address; -}; - -#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) -extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, - struct i2c_adapter* i2c, u8 pwm); -#else -static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, - struct i2c_adapter* i2c, u8 pwm) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); - return NULL; -} -#endif // CONFIG_DVB_TDA10021 - -static inline int tda10021_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { - int r = 0; - u8 buf[] = {reg, val}; - if (fe->ops.write) - r = fe->ops.write(fe, buf, 2); - return r; -} - -#endif // TDA10021_H diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c new file mode 100644 index 0000000..da796e7 --- /dev/null +++ b/drivers/media/dvb/frontends/tda10023.c @@ -0,0 +1,540 @@ +/* + TDA10023 - DVB-C decoder + (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card) + + Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de) + Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com) + + Remotely based on tda10021.c + Copyright (C) 1999 Convergence Integrated Media GmbH + Copyright (C) 2004 Markus Schulz + Support for TDA10021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dvb_frontend.h" +#include "tda1002x.h" + + +struct tda10023_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct tda1002x_config* config; + struct dvb_frontend frontend; + + u8 pwm; + u8 reg0; +}; + + +#define dprintk(x...) + +static int verbose; + +#define XTAL 28920000UL +#define PLL_M 8UL +#define PLL_P 4UL +#define PLL_N 1UL +#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P)) // -> 57840000 + +static u8 tda10023_inittab[]={ + // reg mask val + 0x2a,0xff,0x02, // PLL3, Bypass, Power Down + 0xff,0x64,0x00, // Sleep 100ms + 0x2a,0xff,0x03, // PLL3, Bypass, Power Down + 0xff,0x64,0x00, // Sleep 100ms + 0x28,0xff,PLL_M-1, // PLL1 M=8 + 0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1), // PLL2 + 0x00,0xff,0x23, // GPR FSAMPLING=1 + 0x2a,0xff,0x08, // PLL3 PSACLK=1 + 0xff,0x64,0x00, // Sleep 100ms + 0x1f,0xff,0x00, // RESET + 0xff,0x64,0x00, // Sleep 100ms + 0xe6,0x0c,0x04, // RSCFG_IND + 0x10,0xc0,0x80, // DECDVBCFG1 PBER=1 + + 0x0e,0xff,0x82, // GAIN1 + 0x03,0x08,0x08, // CLKCONF DYN=1 + 0x2e,0xbf,0x30, // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0 + 0x01,0xff,0x30, // AGCREF + 0x1e,0x84,0x84, // CONTROL SACLK_ON=1 + 0x1b,0xff,0xc8, // ADC TWOS=1 + 0x3b,0xff,0xff, // IFMAX + 0x3c,0xff,0x00, // IFMIN + 0x34,0xff,0x00, // PWMREF + 0x35,0xff,0xff, // TUNMAX + 0x36,0xff,0x00, // TUNMIN + 0x06,0xff,0x7f, // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 // 0x77 + 0x1c,0x30,0x30, // EQCONF2 STEPALGO=SGNALGO=1 + 0x37,0xff,0xf6, // DELTAF_LSB + 0x38,0xff,0xff, // DELTAF_MSB + 0x02,0xff,0x93, // AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 + 0x2d,0xff,0xf6, // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 + 0x04,0x10,0x00, // SWRAMP=1 + 0x12,0xff,0xa1, // INTP1 POCLKP=1 FEL=1 MFS=0 + 0x2b,0x01,0xa1, // INTS1 + 0x20,0xff,0x04, // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? + 0x2c,0xff,0x0d, // INTP/S TRIP=0 TRIS=0 + 0xc4,0xff,0x00, + 0xc3,0x30,0x00, + 0xb5,0xff,0x19, // ERAGC_THD + 0x00,0x03,0x01, // GPR, CLBS soft reset + 0x00,0x03,0x03, // GPR, CLBS soft reset + 0xff,0x64,0x00, // Sleep 100ms + 0xff,0xff,0xff +}; + +static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) +{ + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + int ret; + + ret = i2c_transfer (state->i2c, msg, 2); + if (ret != 2) + printk("DVB: TDA10023: %s: readreg error (ret == %i)\n", + __FUNCTION__, ret); + return b1[0]; +} + +static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer (state->i2c, &msg, 1); + if (ret != 1) + printk("DVB: TDA10023(%d): %s, writereg error " + "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", + state->frontend.dvb->num, __FUNCTION__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + + +static int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data) +{ + if (mask==0xff) + return tda10023_writereg(state, reg, data); + else { + u8 val; + val=tda10023_readreg(state,reg); + val&=~mask; + val|=(data&mask); + return tda10023_writereg(state, reg, val); + } +} + +static void tda10023_writetab(struct tda10023_state* state, u8* tab) +{ + u8 r,m,v; + while (1) { + r=*tab++; + m=*tab++; + v=*tab++; + if (r==0xff) { + if (m==0xff) + break; + else + msleep(m); + } + else + tda10023_writebit(state,r,m,v); + } +} + +//get access to tuner +static int lock_tuner(struct tda10023_state* state) +{ + u8 buf[2] = { 0x0f, 0xc0 }; + struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg, 1) != 1) + { + printk("tda10023: lock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +//release access from tuner +static int unlock_tuner(struct tda10023_state* state) +{ + u8 buf[2] = { 0x0f, 0x40 }; + struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg_post, 1) != 1) + { + printk("tda10023: unlock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +static int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0) +{ + reg0 |= state->reg0 & 0x63; + + tda10023_writereg (state, 0x00, reg0 & 0xfe); + tda10023_writereg (state, 0x00, reg0 | 0x01); + + state->reg0 = reg0; + return 0; +} + +static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) +{ + s32 BDR; + s32 BDRI; + s16 SFIL=0; + u16 NDEC = 0; + + if (sr > (SYSCLK/(2*4))) + sr=SYSCLK/(2*4); + + if (sr<870000) + sr=870000; + + if (sr < (u32)(SYSCLK/98.40)) { + NDEC=3; + SFIL=1; + } else if (sr<(u32)(SYSCLK/64.0)) { + NDEC=3; + SFIL=0; + } else if (sr<(u32)(SYSCLK/49.2)) { + NDEC=2; + SFIL=1; + } else if (sr<(u32)(SYSCLK/32.0)) { + NDEC=2; + SFIL=0; + } else if (sr<(u32)(SYSCLK/24.6)) { + NDEC=1; + SFIL=1; + } else if (sr<(u32)(SYSCLK/16.0)) { + NDEC=1; + SFIL=0; + } else if (sr<(u32)(SYSCLK/12.3)) { + NDEC=0; + SFIL=1; + } + + BDRI=SYSCLK*16; + BDRI>>=NDEC; + BDRI +=sr/2; + BDRI /=sr; + + if (BDRI>255) + BDRI=255; + + { + u64 BDRX; + + BDRX=1<<(24+NDEC); + BDRX*=sr; + do_div(BDRX,SYSCLK); // BDRX/=SYSCLK; + + BDR=(s32)BDRX; + } +// printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC); + tda10023_writebit (state, 0x03, 0xc0, NDEC<<6); + tda10023_writereg (state, 0x0a, BDR&255); + tda10023_writereg (state, 0x0b, (BDR>>8)&255); + tda10023_writereg (state, 0x0c, (BDR>>16)&31); + tda10023_writereg (state, 0x0d, BDRI); + tda10023_writereg (state, 0x3d, (SFIL<<7)); + return 0; +} + +static int tda10023_init (struct dvb_frontend *fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + + dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num); + + tda10023_writetab(state, tda10023_inittab); + + return 0; +} + +static int tda10023_set_parameters (struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10023_state* state = fe->demodulator_priv; + + static int qamvals[6][6] = { + // QAM LOCKTHR MSETH AREF AGCREFNYQ ERAGCNYQ_THD + { (5<<2), 0x78, 0x8c, 0x96, 0x78, 0x4c }, // 4 QAM + { (0<<2), 0x87, 0xa2, 0x91, 0x8c, 0x57 }, // 16 QAM + { (1<<2), 0x64, 0x74, 0x96, 0x8c, 0x57 }, // 32 QAM + { (2<<2), 0x46, 0x43, 0x6a, 0x6a, 0x44 }, // 64 QAM + { (3<<2), 0x36, 0x34, 0x7e, 0x78, 0x4c }, // 128 QAM + { (4<<2), 0x26, 0x23, 0x6c, 0x5c, 0x3c }, // 256 QAM + }; + + int qam = p->u.qam.modulation; + + if (qam < 0 || qam > 5) + return -EINVAL; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + tda10023_set_symbolrate (state, p->u.qam.symbol_rate); + tda10023_writereg (state, 0x05, qamvals[qam][1]); + tda10023_writereg (state, 0x08, qamvals[qam][2]); + tda10023_writereg (state, 0x09, qamvals[qam][3]); + tda10023_writereg (state, 0xb4, qamvals[qam][4]); + tda10023_writereg (state, 0xb6, qamvals[qam][5]); + +// tda10023_writereg (state, 0x04, (p->inversion?0x12:0x32)); +// tda10023_writebit (state, 0x04, 0x60, (p->inversion?0:0x20)); + tda10023_writebit (state, 0x04, 0x40, 0x40); + tda10023_setup_reg0 (state, qamvals[qam][0]); + + return 0; +} + +static int tda10023_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda10023_state* state = fe->demodulator_priv; + int sync; + + *status = 0; + + //0x11[1] == CARLOCK -> Carrier locked + //0x11[2] == FSYNC -> Frame synchronisation + //0x11[3] == FEL -> Front End locked + //0x11[6] == NODVB -> DVB Mode Information + sync = tda10023_readreg (state, 0x11); + + if (sync & 2) + *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; + + if (sync & 4) + *status |= FE_HAS_SYNC|FE_HAS_VITERBI; + + if (sync & 8) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda10023_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 a,b,c; + a=tda10023_readreg(state, 0x14); + b=tda10023_readreg(state, 0x15); + c=tda10023_readreg(state, 0x16)&0xf; + tda10023_writebit (state, 0x10, 0xc0, 0x00); + + *ber = a | (b<<8)| (c<<16); + return 0; +} + +static int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 ifgain=tda10023_readreg(state, 0x2f); + + u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16; + // Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90 + if (gain>0x90) + gain=gain+2*(gain-0x90); + if (gain>255) + gain=255; + + *strength = (gain<<8)|gain; + return 0; +} + +static int tda10023_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda10023_state* state = fe->demodulator_priv; + + u8 quality = ~tda10023_readreg(state, 0x18); + *snr = (quality << 8) | quality; + return 0; +} + +static int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 a,b,c,d; + a= tda10023_readreg (state, 0x74); + b= tda10023_readreg (state, 0x75); + c= tda10023_readreg (state, 0x76); + d= tda10023_readreg (state, 0x77); + *ucblocks = a | (b<<8)|(c<<16)|(d<<24); + + tda10023_writebit (state, 0x10, 0x20,0x00); + tda10023_writebit (state, 0x10, 0x20,0x20); + tda10023_writebit (state, 0x13, 0x01, 0x00); + + return 0; +} + +static int tda10023_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda10023_state* state = fe->demodulator_priv; + int sync,inv; + s8 afc = 0; + + sync = tda10023_readreg(state, 0x11); + afc = tda10023_readreg(state, 0x19); + inv = tda10023_readreg(state, 0x04); + + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" : + "DVB: TDA10023(%d): [AFC (%d) %dHz]\n", + state->frontend.dvb->num, afc, + -((s32)p->u.qam.symbol_rate * afc) >> 10); + } + + p->inversion = (inv&0x20?0:1); + p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; + + p->u.qam.fec_inner = FEC_NONE; + p->frequency = ((p->frequency + 31250) / 62500) * 62500; + + if (sync & 2) + p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10; + + return 0; +} + +static int tda10023_sleep(struct dvb_frontend* fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + + tda10023_writereg (state, 0x1b, 0x02); /* pdown ADC */ + tda10023_writereg (state, 0x00, 0x80); /* standby */ + + return 0; +} + +static int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10023_state* state = fe->demodulator_priv; + + if (enable) { + lock_tuner(state); + } else { + unlock_tuner(state); + } + return 0; +} + +static void tda10023_release(struct dvb_frontend* fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops tda10023_ops; + +struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, + u8 pwm) +{ + struct tda10023_state* state = NULL; + int i; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); + state->pwm = pwm; + for (i=0; i < sizeof(tda10023_inittab)/sizeof(*tda10023_inittab);i+=3) { + if (tda10023_inittab[i] == 0x00) { + state->reg0 = tda10023_inittab[i+2]; + break; + } + } + + // Wakeup if in standby + tda10023_writereg (state, 0x00, 0x33); + /* check if the demod is there */ + if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda10023_ops = { + + .info = { + .name = "Philips TDA10023 DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .symbol_rate_min = (SYSCLK/2)/64, /* SACLK/64 == (SYSCLK/2)/64 */ + .symbol_rate_max = (SYSCLK/2)/4, /* SACLK/4 */ + .caps = 0x400 | //FE_CAN_QAM_4 + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = tda10023_release, + + .init = tda10023_init, + .sleep = tda10023_sleep, + .i2c_gate_ctrl = tda10023_i2c_gate_ctrl, + + .set_frontend = tda10023_set_parameters, + .get_frontend = tda10023_get_frontend, + + .read_status = tda10023_read_status, + .read_ber = tda10023_read_ber, + .read_signal_strength = tda10023_read_signal_strength, + .read_snr = tda10023_read_snr, + .read_ucblocks = tda10023_read_ucblocks, +}; + + +MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver"); +MODULE_AUTHOR("Georg Acher, Hartmut Birr"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda10023_attach); diff --git a/drivers/media/dvb/frontends/tda1002x.h b/drivers/media/dvb/frontends/tda1002x.h new file mode 100644 index 0000000..e9094d8 --- /dev/null +++ b/drivers/media/dvb/frontends/tda1002x.h @@ -0,0 +1,60 @@ +/* + TDA10021/TDA10023 - Single Chip Cable Channel Receiver driver module + used on the the Siemens DVB-C cards + + Copyright (C) 1999 Convergence Integrated Media GmbH + Copyright (C) 2004 Markus Schulz + Support for TDA10021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef TDA1002x_H +#define TDA1002x_H + +#include + +struct tda1002x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + u8 invert; +}; + +#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) +extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm); +#else +static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TDA10021 + +#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) +extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm); +#else +static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TDA10023 + +#endif // TDA1002x_H diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index f4a9cf9..33a8437 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -40,20 +40,6 @@ #include #include "dvb_frontend.h" #include "tda1004x.h" -enum tda1004x_demod { - TDA1004X_DEMOD_TDA10045, - TDA1004X_DEMOD_TDA10046, -}; - -struct tda1004x_state { - struct i2c_adapter* i2c; - const struct tda1004x_config* config; - struct dvb_frontend frontend; - - /* private demod data */ - enum tda1004x_demod demod_type; -}; - static int debug; #define dprintk(args...) \ do { \ @@ -507,35 +493,51 @@ static int tda10046_fwupload(struct dvb_ tda1004x_write_byteI(state, TDA1004X_CONFC4, 0x80); } tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); + /* set GPIO 1 and 3 */ + if (state->config->gpio_config != TDA10046_GPTRI) { + tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33); + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f); + } /* let the clocks recover from sleep */ - msleep(5); + msleep(10); /* The PLLs need to be reprogrammed after sleep */ tda10046_init_plls(fe); + tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0); /* don't re-upload unless necessary */ if (tda1004x_check_upload_ok(state) == 0) return 0; + printk(KERN_INFO "tda1004x: trying to boot from eeprom\n"); + tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4); + msleep(300); + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state) == 0) + return 0; + if (state->config->request_firmware != NULL) { /* request the firmware, this will block until someone uploads it */ printk(KERN_INFO "tda1004x: waiting for firmware upload...\n"); ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); if (ret) { - printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); - return ret; + /* remain compatible to old bug: try to load with tda10045 image name */ + ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); + if (ret) { + printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); + return ret; + } else { + printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n", + TDA10046_DEFAULT_FIRMWARE); + } } - tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST - ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); - release_firmware(fw); - if (ret) - return ret; } else { - /* boot from firmware eeprom */ - printk(KERN_INFO "tda1004x: booting from eeprom\n"); - tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4); - msleep(300); + printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n"); + return -EIO; } + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); + release_firmware(fw); return tda1004x_check_upload_ok(state); } @@ -638,37 +640,33 @@ static int tda10046_init(struct dvb_fron switch (state->config->agc_config) { case TDA10046_AGC_DEFAULT: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities break; case TDA10046_AGC_IFO_AUTO_NEG: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities break; case TDA10046_AGC_IFO_AUTO_POS: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities - break; - case TDA10046_AGC_TDA827X_GP11: - tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup - tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold - tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities - break; - case TDA10046_AGC_TDA827X_GP00: - tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup - tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold - tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities break; - case TDA10046_AGC_TDA827X_GP01: + case TDA10046_AGC_TDA827X: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities break; } + if (state->config->ts_mode == 0) { + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40); + tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); + } else { + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80); + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10, + state->config->invert_oclk << 4); + } tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38); - tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on + tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // } tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // } @@ -678,7 +676,6 @@ static int tda10046_init(struct dvb_fron tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes - tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); return 0; } @@ -705,7 +702,8 @@ static int tda1004x_set_fe(struct dvb_fr // set frequency if (fe->ops.tuner_ops.set_params) { fe->ops.tuner_ops.set_params(fe, fe_params); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); } // Hardcoded to use auto as much as possible on the TDA10045 as it @@ -1165,6 +1163,7 @@ static int tda1004x_read_ber(struct dvb_ static int tda1004x_sleep(struct dvb_frontend* fe) { struct tda1004x_state* state = fe->demodulator_priv; + int gpio_conf; switch (state->demod_type) { case TDA1004X_DEMOD_TDA10045: @@ -1174,6 +1173,13 @@ static int tda1004x_sleep(struct dvb_fro case TDA1004X_DEMOD_TDA10046: /* set outputs to tristate */ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff); + /* invert GPIO 1 and 3 if desired*/ + gpio_conf = state->config->gpio_config; + if (gpio_conf >= TDA10046_GP00_I) + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, + (gpio_conf & 0x0f) ^ 0x0a); + + tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0); tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); break; } diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h index ec502d7..abae843 100644 --- a/drivers/media/dvb/frontends/tda1004x.h +++ b/drivers/media/dvb/frontends/tda1004x.h @@ -35,9 +35,23 @@ enum tda10046_agc { TDA10046_AGC_DEFAULT, /* original configuration */ TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */ TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */ - TDA10046_AGC_TDA827X_GP11, /* IF AGC only, special setup for tda827x */ - TDA10046_AGC_TDA827X_GP00, /* same as above, but GPIOs 0 */ - TDA10046_AGC_TDA827X_GP01, /* same as above, but GPIO3=0 GPIO1=1*/ + TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */ +}; + +/* Many (hybrid) boards use GPIO 1 and 3 + GPIO1 analog - dvb switch + GPIO3 firmware eeprom address switch +*/ +enum tda10046_gpio { + TDA10046_GPTRI = 0x00, /* All GPIOs tristate */ + TDA10046_GP00 = 0x40, /* GPIO3=0, GPIO1=0 */ + TDA10046_GP01 = 0x42, /* GPIO3=0, GPIO1=1 */ + TDA10046_GP10 = 0x48, /* GPIO3=1, GPIO1=0 */ + TDA10046_GP11 = 0x4a, /* GPIO3=1, GPIO1=1 */ + TDA10046_GP00_I = 0x80, /* GPIO3=0, GPIO1=0, invert in sleep mode*/ + TDA10046_GP01_I = 0x82, /* GPIO3=0, GPIO1=1, invert in sleep mode */ + TDA10046_GP10_I = 0x88, /* GPIO3=1, GPIO1=0, invert in sleep mode */ + TDA10046_GP11_I = 0x8a, /* GPIO3=1, GPIO1=1, invert in sleep mode */ }; enum tda10046_if { @@ -47,6 +61,11 @@ enum tda10046_if { TDA10046_FREQ_052, /* low IF, 5.1667 MHZ for tda9889 */ }; +enum tda10046_tsout { + TDA10046_TS_PARALLEL = 0x00, /* parallel transport stream, default */ + TDA10046_TS_SERIAL = 0x01, /* serial transport stream */ +}; + struct tda1004x_config { /* the demodulator's i2c address */ @@ -58,6 +77,9 @@ struct tda1004x_config /* Does the OCLK signal need inverted? */ u8 invert_oclk; + /* parallel or serial transport stream */ + enum tda10046_tsout ts_mode; + /* Xtal frequency, 4 or 16MHz*/ enum tda10046_xtal xtal_freq; @@ -67,11 +89,35 @@ struct tda1004x_config /* AGC configuration */ enum tda10046_agc agc_config; + /* setting of GPIO1 and 3 */ + enum tda10046_gpio gpio_config; + + /* slave address and configuration of the tuner */ + u8 tuner_address; + u8 tuner_config; + u8 antenna_switch; + + /* if the board uses another I2c Bridge (tda8290), its address */ + u8 i2c_gate; + /* request firmware for device */ - /* set this to NULL if the card has a firmware EEPROM */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; +enum tda1004x_demod { + TDA1004X_DEMOD_TDA10045, + TDA1004X_DEMOD_TDA10046, +}; + +struct tda1004x_state { + struct i2c_adapter* i2c; + const struct tda1004x_config* config; + struct dvb_frontend frontend; + + /* private demod data */ + enum tda1004x_demod demod_type; +}; + #if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE)) extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c); diff --git a/drivers/media/dvb/frontends/tda827x.c b/drivers/media/dvb/frontends/tda827x.c new file mode 100644 index 0000000..256fc4b --- /dev/null +++ b/drivers/media/dvb/frontends/tda827x.c @@ -0,0 +1,512 @@ +/* + * + * (c) 2005 Hartmut Hackmann + * (c) 2007 Michael Krufky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "tda827x.h" + +static int debug = 0; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda827x: " args); \ + } while (0) + +struct tda827x_priv { + int i2c_addr; + struct i2c_adapter *i2c_adap; + struct tda827x_config *cfg; + u32 frequency; + u32 bandwidth; +}; + +struct tda827x_data { + u32 lomax; + u8 spd; + u8 bs; + u8 bp; + u8 cp; + u8 gc3; + u8 div1p5; +}; + +static const struct tda827x_data tda827x_dvbt[] = { + { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, + { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, + { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, + { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, + { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, + { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, + { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} +}; + +static int tda827xo_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct tda827x_priv *priv = fe->tuner_priv; + u8 buf[14]; + + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + int i, tuner_freq, if_freq; + u32 N; + + dprintk("%s:\n", __FUNCTION__); + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + if_freq = 4000000; + break; + case BANDWIDTH_7_MHZ: + if_freq = 4500000; + break; + default: /* 8 MHz or Auto */ + if_freq = 5000000; + break; + } + tuner_freq = params->frequency + if_freq; + + i = 0; + while (tda827x_dvbt[i].lomax < tuner_freq) { + if(tda827x_dvbt[i + 1].lomax == 0) + break; + i++; + } + + N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2); + buf[0] = 0; + buf[1] = (N>>8) | 0x40; + buf[2] = N & 0xff; + buf[3] = 0; + buf[4] = 0x52; + buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) + + (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp; + buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f; + buf[7] = 0xbf; + buf[8] = 0x2a; + buf[9] = 0x05; + buf[10] = 0xff; + buf[11] = 0x00; + buf[12] = 0x00; + buf[13] = 0x40; + + msg.len = 14; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) { + printk("%s: could not write to tuner at addr: 0x%02x\n", + __FUNCTION__, priv->i2c_addr << 1); + return -EIO; + } + msleep(500); + /* correct CP value */ + buf[0] = 0x30; + buf[1] = 0x50 + tda827x_dvbt[i].cp; + msg.len = 2; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + priv->frequency = tuner_freq - if_freq; // FIXME + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; + + return 0; +} + +static int tda827xo_sleep(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + static u8 buf[] = { 0x30, 0xd0 }; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + dprintk("%s:\n", __FUNCTION__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + if (priv->cfg && priv->cfg->sleep) + priv->cfg->sleep(fe); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +struct tda827xa_data { + u32 lomax; + u8 svco; + u8 spd; + u8 scr; + u8 sbs; + u8 gc3; +}; + +static const struct tda827xa_data tda827xa_dvbt[] = { + { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, + { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, + { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +}; + +static int tda827xa_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct tda827x_priv *priv = fe->tuner_priv; + u8 buf[11]; + + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + int i, tuner_freq, if_freq; + u32 N; + + dprintk("%s:\n", __FUNCTION__); + if (priv->cfg && priv->cfg->lna_gain) + priv->cfg->lna_gain(fe, 1); + msleep(20); + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + if_freq = 4000000; + break; + case BANDWIDTH_7_MHZ: + if_freq = 4500000; + break; + default: /* 8 MHz or Auto */ + if_freq = 5000000; + break; + } + tuner_freq = params->frequency + if_freq; + + i = 0; + while (tda827xa_dvbt[i].lomax < tuner_freq) { + if(tda827xa_dvbt[i + 1].lomax == 0) + break; + i++; + } + + N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd; + buf[0] = 0; // subaddress + buf[1] = N >> 8; + buf[2] = N & 0xff; + buf[3] = 0; + buf[4] = 0x16; + buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) + + tda827xa_dvbt[i].sbs; + buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4); + buf[7] = 0x1c; + buf[8] = 0x06; + buf[9] = 0x24; + buf[10] = 0x00; + msg.len = 11; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) { + printk("%s: could not write to tuner at addr: 0x%02x\n", + __FUNCTION__, priv->i2c_addr << 1); + return -EIO; + } + buf[0] = 0x90; + buf[1] = 0xff; + buf[2] = 0x60; + buf[3] = 0x00; + buf[4] = 0x59; // lpsel, for 6MHz + 2 + msg.len = 5; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + buf[0] = 0xa0; + buf[1] = 0x40; + msg.len = 2; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + msleep(11); + msg.flags = I2C_M_RD; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + msg.flags = 0; + + buf[1] >>= 4; + dprintk("tda8275a AGC2 gain is: %d\n", buf[1]); + if ((buf[1]) < 2) { + if (priv->cfg && priv->cfg->lna_gain) + priv->cfg->lna_gain(fe, 0); + buf[0] = 0x60; + buf[1] = 0x0c; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + } + + buf[0] = 0xc0; + buf[1] = 0x99; // lpsel, for 6MHz + 2 + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + buf[0] = 0x60; + buf[1] = 0x3c; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + /* correct CP value */ + buf[0] = 0x30; + buf[1] = 0x10 + tda827xa_dvbt[i].scr; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + msleep(163); + buf[0] = 0xc0; + buf[1] = 0x39; // lpsel, for 6MHz + 2 + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + msleep(3); + /* freeze AGC1 */ + buf[0] = 0x50; + buf[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + priv->frequency = tuner_freq - if_freq; // FIXME + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; + + return 0; +} + +static int tda827xa_sleep(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + static u8 buf[] = { 0x30, 0x90 }; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + dprintk("%s:\n", __FUNCTION__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + i2c_transfer(priv->i2c_adap, &msg, 1); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (priv->cfg && priv->cfg->sleep) + priv->cfg->sleep(fe); + + return 0; +} + +static int tda827x_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda827x_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct tda827x_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int tda827x_init(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + dprintk("%s:\n", __FUNCTION__); + if (priv->cfg && priv->cfg->init) + priv->cfg->init(fe); + + return 0; +} + +static int tda827x_probe_version(struct dvb_frontend *fe); + +static int tda827x_initial_init(struct dvb_frontend *fe) +{ + int ret; + ret = tda827x_probe_version(fe); + if (ret) + return ret; + return fe->ops.tuner_ops.init(fe); +} + +static int tda827x_initial_sleep(struct dvb_frontend *fe) +{ + int ret; + ret = tda827x_probe_version(fe); + if (ret) + return ret; + return fe->ops.tuner_ops.sleep(fe); +} + +static struct dvb_tuner_ops tda827xo_tuner_ops = { + .info = { + .name = "Philips TDA827X", + .frequency_min = 55000000, + .frequency_max = 860000000, + .frequency_step = 250000 + }, + .release = tda827x_release, + .init = tda827x_initial_init, + .sleep = tda827x_initial_sleep, + .set_params = tda827xo_set_params, + .get_frequency = tda827x_get_frequency, + .get_bandwidth = tda827x_get_bandwidth, +}; + +static struct dvb_tuner_ops tda827xa_tuner_ops = { + .info = { + .name = "Philips TDA827XA", + .frequency_min = 44000000, + .frequency_max = 906000000, + .frequency_step = 62500 + }, + .release = tda827x_release, + .init = tda827x_init, + .sleep = tda827xa_sleep, + .set_params = tda827xa_set_params, + .get_frequency = tda827x_get_frequency, + .get_bandwidth = tda827x_get_bandwidth, +}; + +static int tda827x_probe_version(struct dvb_frontend *fe) +{ u8 data; + struct tda827x_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD, + .buf = &data, .len = 1 }; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) { + printk("%s: could not read from tuner at addr: 0x%02x\n", + __FUNCTION__, msg.addr << 1); + return -EIO; + } + if ((data & 0x3c) == 0) { + dprintk("tda827x tuner found\n"); + fe->ops.tuner_ops.init = tda827x_init; + fe->ops.tuner_ops.sleep = tda827xo_sleep; + } else { + dprintk("tda827xa tuner found\n"); + memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops)); + } + return 0; +} + +struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c, + struct tda827x_config *cfg) +{ + struct tda827x_priv *priv = NULL; + + dprintk("%s:\n", __FUNCTION__); + priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_addr = addr; + priv->i2c_adap = i2c; + priv->cfg = cfg; + memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + return fe; +} + +EXPORT_SYMBOL(tda827x_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("DVB TDA827x driver"); +MODULE_AUTHOR("Hartmut Hackmann "); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/frontends/tda827x.h b/drivers/media/dvb/frontends/tda827x.h new file mode 100644 index 0000000..69e8263 --- /dev/null +++ b/drivers/media/dvb/frontends/tda827x.h @@ -0,0 +1,62 @@ + /* + DVB Driver for Philips tda827x / tda827xa Silicon tuners + + (c) 2005 Hartmut Hackmann + (c) 2007 Michael Krufky + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef __DVB_TDA827X_H__ +#define __DVB_TDA827X_H__ + +#include +#include "dvb_frontend.h" + +struct tda827x_config +{ + void (*lna_gain) (struct dvb_frontend *fe, int high); + int (*init) (struct dvb_frontend *fe); + int (*sleep) (struct dvb_frontend *fe); +}; + + +/** + * Attach a tda827x tuner to the supplied frontend structure. + * + * @param fe Frontend to attach to. + * @param addr i2c address of the tuner. + * @param i2c i2c adapter to use. + * @param cfg optional callback function pointers. + * @return FE pointer on success, NULL on failure. + */ +#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE)) +extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c, + struct tda827x_config *cfg); +#else +static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, + int addr, + struct i2c_adapter *i2c, + struct tda827x_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TDA827X + +#endif // __DVB_TDA827X_H__ diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/dvb/pluto2/Kconfig index 9b84b1b..7d8e6e8 100644 --- a/drivers/media/dvb/pluto2/Kconfig +++ b/drivers/media/dvb/pluto2/Kconfig @@ -2,7 +2,6 @@ config DVB_PLUTO2 tristate "Pluto2 cards" depends on DVB_CORE && PCI && I2C select I2C_ALGOBIT - select DVB_PLL select DVB_TDA1004X help Support for PCI cards based on the Pluto2 FPGA like the Satelco diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index eec7ccf..7751628 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -3,7 +3,6 @@ config DVB_AV7110 depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select FW_LOADER if !DVB_AV7110_FIRMWARE select VIDEO_SAA7146_VV - select DVB_PLL select DVB_VES1820 if !DVB_FE_CUSTOMISE select DVB_VES1X93 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE @@ -62,13 +61,13 @@ config DVB_BUDGET tristate "Budget cards" depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146 - select DVB_PLL select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_VES1X93 if !DVB_FE_CUSTOMISE select DVB_VES1820 if !DVB_FE_CUSTOMISE select DVB_L64781 if !DVB_FE_CUSTOMISE select DVB_TDA8083 if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select DVB_S5H1420 if !DVB_FE_CUSTOMISE select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE @@ -87,7 +86,6 @@ config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146 - select DVB_PLL select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE @@ -114,6 +112,7 @@ config DVB_BUDGET_AV select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select DVB_TUA6100 if !DVB_FE_CUSTOMISE select FW_LOADER help @@ -130,7 +129,6 @@ config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1 select DVB_AV7110 - select DVB_PLL select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_VES1X93 if !DVB_FE_CUSTOMISE select DVB_TDA8083 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 29ed532..67becdd 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -219,7 +219,10 @@ static void recover_arm(struct av7110 *a av7110->recover(av7110); restart_feeds(av7110); - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config); + +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_check_ir_config(av7110, true); +#endif } static void av7110_arm_sync(struct av7110 *av7110) @@ -250,6 +253,10 @@ static int arm_thread(void *data) if (!av7110->arm_ready) continue; +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_check_ir_config(av7110, false); +#endif + if (mutex_lock_interruptible(&av7110->dcomlock)) break; newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); @@ -667,8 +674,8 @@ static void gpioirq(unsigned long data) return; case DATA_IRCOMMAND: - if (av7110->ir_handler) - av7110->ir_handler(av7110, + if (av7110->ir.ir_handler) + av7110->ir.ir_handler(av7110, swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4))); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); break; @@ -1907,8 +1914,10 @@ static int av7110_fe_lock_fix(struct av7 if (av7110->fe_synced == synced) return 0; - if (av7110->playing) + if (av7110->playing) { + av7110->fe_synced = synced; return 0; + } if (mutex_lock_interruptible(&av7110->pid_mutex)) return -ERESTARTSYS; diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h index b98bd45..115002b 100644 --- a/drivers/media/dvb/ttpci/av7110.h +++ b/drivers/media/dvb/ttpci/av7110.h @@ -5,6 +5,7 @@ #include #include #include #include +#include #include #include @@ -66,6 +67,27 @@ struct dvb_video_events { }; +struct av7110; + +/* infrared remote control */ +struct infrared { + u16 key_map[256]; + struct input_dev *input_dev; + char input_phys[32]; + struct timer_list keyup_timer; + struct tasklet_struct ir_tasklet; + void (*ir_handler)(struct av7110 *av7110, u32 ircom); + u32 ir_command; + u32 ir_config; + u32 device_mask; + u8 protocol; + u8 inversion; + u16 last_key; + u16 last_toggle; + u8 delay_timer_finished; +}; + + /* place to store all the necessary device information */ struct av7110 { @@ -227,10 +249,7 @@ #define TRICK_FREEZE 3 u16 wssMode; u16 wssData; - u32 ir_config; - u32 ir_command; - void (*ir_handler)(struct av7110 *av7110, u32 ircom); - struct tasklet_struct ir_tasklet; + struct infrared ir; /* firmware stuff */ unsigned char *bin_fw; @@ -268,6 +287,7 @@ #define TRICK_FREEZE 3 extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, u16 subpid, u16 pcrpid); +extern int av7110_check_ir_config(struct av7110 *av7110, int force); extern int av7110_ir_init(struct av7110 *av7110); extern void av7110_ir_exit(struct av7110 *av7110); diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index e719af8..654c9e9 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -1009,7 +1009,7 @@ static int dvb_video_ioctl(struct inode if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) ret = av7110_av_stop(av7110, RP_VIDEO); else - ret = vidcom(av7110, VIDEO_CMD_STOP, + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, av7110->videostate.video_blank ? 0 : 1); if (!ret) av7110->trickmode = TRICK_NONE; @@ -1019,7 +1019,7 @@ static int dvb_video_ioctl(struct inode av7110->trickmode = TRICK_NONE; if (av7110->videostate.play_state == VIDEO_FREEZED) { av7110->videostate.play_state = VIDEO_PLAYING; - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (ret) break; } @@ -1034,7 +1034,7 @@ static int dvb_video_ioctl(struct inode ret = av7110_av_start_play(av7110, RP_VIDEO); } if (!ret) - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) av7110->videostate.play_state = VIDEO_PLAYING; break; @@ -1044,7 +1044,7 @@ static int dvb_video_ioctl(struct inode if (av7110->playing & RP_VIDEO) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); else - ret = vidcom(av7110, VIDEO_CMD_FREEZE, 1); + ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); if (!ret) av7110->trickmode = TRICK_FREEZE; break; @@ -1053,7 +1053,7 @@ static int dvb_video_ioctl(struct inode if (av7110->playing & RP_VIDEO) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) { av7110->videostate.play_state = VIDEO_PLAYING; av7110->trickmode = TRICK_NONE; @@ -1136,7 +1136,7 @@ static int dvb_video_ioctl(struct inode ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Scan_I, 2, AV_PES, 0); else - ret = vidcom(av7110, VIDEO_CMD_FFWD, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); if (!ret) { av7110->trickmode = TRICK_FAST; av7110->videostate.play_state = VIDEO_PLAYING; @@ -1147,13 +1147,13 @@ static int dvb_video_ioctl(struct inode if (av7110->playing&RP_VIDEO) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_SLOW, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } else { - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_STOP, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_SLOW, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } if (!ret) { av7110->trickmode = TRICK_SLOW; @@ -1182,10 +1182,10 @@ static int dvb_video_ioctl(struct inode ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_SLOW, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } if (av7110->trickmode == TRICK_FREEZE) - ret = vidcom(av7110, VIDEO_CMD_STOP, 1); + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); } break; diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h index 4e173c6..673d9b3 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ b/drivers/media/dvb/ttpci/av7110_hw.h @@ -216,11 +216,11 @@ #define VID_VC_AND_PS_PREF 0x03 /* PanSc #define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ /* MPEG video decoder commands */ -#define VIDEO_CMD_STOP 0x000e -#define VIDEO_CMD_PLAY 0x000d -#define VIDEO_CMD_FREEZE 0x0102 -#define VIDEO_CMD_FFWD 0x0016 -#define VIDEO_CMD_SLOW 0x0022 +#define AV_VIDEO_CMD_STOP 0x000e +#define AV_VIDEO_CMD_PLAY 0x000d +#define AV_VIDEO_CMD_FREEZE 0x0102 +#define AV_VIDEO_CMD_FFWD 0x0016 +#define AV_VIDEO_CMD_SLOW 0x0022 /* MPEG audio decoder commands */ #define AUDIO_CMD_MUTE 0x0001 diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c index f59465b..a97f166 100644 --- a/drivers/media/dvb/ttpci/av7110_ir.c +++ b/drivers/media/dvb/ttpci/av7110_ir.c @@ -1,8 +1,31 @@ +/* + * Driver for the remote control of SAA7146 based AV7110 cards + * + * Copyright (C) 1999-2003 Holger Waechtler + * Copyright (C) 2003-2007 Oliver Endriss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + */ + + #include #include #include #include -#include #include #include #include @@ -10,18 +33,37 @@ #include #include "av7110.h" #include "av7110_hw.h" -#define UP_TIMEOUT (HZ*7/25) -/* enable ir debugging by or'ing debug with 16 */ +#define AV_CNT 4 + +#define IR_RC5 0 +#define IR_RCMM 1 +#define IR_RC5_EXT 2 /* internal only */ + +#define IR_ALL 0xffffffff + +#define UP_TIMEOUT (HZ*7/25) -static int av_cnt; -static struct av7110 *av_list[4]; -static struct input_dev *input_dev; -static char input_phys[32]; -static u8 delay_timer_finished; +/* Note: enable ir debugging by or'ing debug with 16 */ + +static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; +module_param_array(ir_protocol, int, NULL, 0644); +MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); + +static int ir_inversion[AV_CNT]; +module_param_array(ir_inversion, int, NULL, 0644); +MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); + +static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; +module_param_array(ir_device_mask, uint, NULL, 0644); +MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); + + +static int av_cnt; +static struct av7110 *av_list[AV_CNT]; -static u16 key_map [256] = { +static u16 default_key_map [256] = { KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -45,141 +87,194 @@ static u16 key_map [256] = { }; -static void av7110_emit_keyup(unsigned long data) +/* key-up timer */ +static void av7110_emit_keyup(unsigned long parm) { - if (!data || !test_bit(data, input_dev->key)) + struct infrared *ir = (struct infrared *) parm; + + if (!ir || !test_bit(ir->last_key, ir->input_dev->key)) return; - input_report_key(input_dev, data, 0); - input_sync(input_dev); + input_report_key(ir->input_dev, ir->last_key, 0); + input_sync(ir->input_dev); } -static struct timer_list keyup_timer = { .function = av7110_emit_keyup }; - - +/* tasklet */ static void av7110_emit_key(unsigned long parm) { - struct av7110 *av7110 = (struct av7110 *) parm; - u32 ir_config = av7110->ir_config; - u32 ircom = av7110->ir_command; + struct infrared *ir = (struct infrared *) parm; + u32 ircom = ir->ir_command; u8 data; u8 addr; - static u16 old_toggle = 0; - u16 new_toggle; + u16 toggle; u16 keycode; /* extract device address and data */ - switch (ir_config & 0x0003) { - case 0: /* RC5: 5 bits device address, 6 bits data */ + switch (ir->protocol) { + case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ data = ircom & 0x3f; addr = (ircom >> 6) & 0x1f; + toggle = ircom & 0x0800; break; - case 1: /* RCMM: 8(?) bits device address, 8(?) bits data */ + case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ data = ircom & 0xff; - addr = (ircom >> 8) & 0xff; + addr = (ircom >> 8) & 0x1f; + toggle = ircom & 0x8000; break; - case 2: /* extended RC5: 5 bits device address, 7 bits data */ + case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ data = ircom & 0x3f; addr = (ircom >> 6) & 0x1f; /* invert 7th data bit for backward compatibility with RC5 keymaps */ if (!(ircom & 0x1000)) data |= 0x40; + toggle = ircom & 0x0800; break; default: - printk("invalid ir_config %x\n", ir_config); + printk("%s invalid protocol %x\n", __FUNCTION__, ir->protocol); return; } - keycode = key_map[data]; + input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); + input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); - dprintk(16, "code %08x -> addr %i data 0x%02x -> keycode %i\n", - ircom, addr, data, keycode); + keycode = ir->key_map[data]; - /* check device address (if selected) */ - if (ir_config & 0x4000) - if (addr != ((ir_config >> 16) & 0xff)) - return; + dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", + __FUNCTION__, ircom, addr, data, keycode); + + /* check device address */ + if (!(ir->device_mask & (1 << addr))) + return; if (!keycode) { - printk ("%s: unknown key 0x%02x!!\n", __FUNCTION__, data); + printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", + __FUNCTION__, ircom, addr, data); return; } - if ((ir_config & 0x0003) == 1) - new_toggle = 0; /* RCMM */ - else - new_toggle = (ircom & 0x800); /* RC5, extended RC5 */ - - if (timer_pending(&keyup_timer)) { - del_timer(&keyup_timer); - if (keyup_timer.data != keycode || new_toggle != old_toggle) { - delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keyup_timer.data, 0); - input_event(input_dev, EV_KEY, keycode, 1); - input_sync(input_dev); - } else if (delay_timer_finished) { - input_event(input_dev, EV_KEY, keycode, 2); - input_sync(input_dev); + if (timer_pending(&ir->keyup_timer)) { + del_timer(&ir->keyup_timer); + if (ir->last_key != keycode || toggle != ir->last_toggle) { + ir->delay_timer_finished = 0; + input_event(ir->input_dev, EV_KEY, ir->last_key, 0); + input_event(ir->input_dev, EV_KEY, keycode, 1); + input_sync(ir->input_dev); + } else if (ir->delay_timer_finished) { + input_event(ir->input_dev, EV_KEY, keycode, 2); + input_sync(ir->input_dev); } } else { - delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keycode, 1); - input_sync(input_dev); + ir->delay_timer_finished = 0; + input_event(ir->input_dev, EV_KEY, keycode, 1); + input_sync(ir->input_dev); } - keyup_timer.expires = jiffies + UP_TIMEOUT; - keyup_timer.data = keycode; + ir->last_key = keycode; + ir->last_toggle = toggle; - add_timer(&keyup_timer); + ir->keyup_timer.expires = jiffies + UP_TIMEOUT; + add_timer(&ir->keyup_timer); - old_toggle = new_toggle; } -static void input_register_keys(void) + +/* register with input layer */ +static void input_register_keys(struct infrared *ir) { int i; - memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); + set_bit(EV_KEY, ir->input_dev->evbit); + set_bit(EV_REP, ir->input_dev->evbit); + set_bit(EV_MSC, ir->input_dev->evbit); - for (i = 0; i < ARRAY_SIZE(key_map); i++) { - if (key_map[i] > KEY_MAX) - key_map[i] = 0; - else if (key_map[i] > KEY_RESERVED) - set_bit(key_map[i], input_dev->keybit); + set_bit(MSC_RAW, ir->input_dev->mscbit); + set_bit(MSC_SCAN, ir->input_dev->mscbit); + + memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); + + for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { + if (ir->key_map[i] > KEY_MAX) + ir->key_map[i] = 0; + else if (ir->key_map[i] > KEY_RESERVED) + set_bit(ir->key_map[i], ir->input_dev->keybit); } + + ir->input_dev->keycode = ir->key_map; + ir->input_dev->keycodesize = sizeof(ir->key_map[0]); + ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); } -static void input_repeat_key(unsigned long data) +/* called by the input driver after rep[REP_DELAY] ms */ +static void input_repeat_key(unsigned long parm) { - /* called by the input driver after rep[REP_DELAY] ms */ - delay_timer_finished = 1; + struct infrared *ir = (struct infrared *) parm; + + ir->delay_timer_finished = 1; } -static int av7110_setup_irc_config(struct av7110 *av7110, u32 ir_config) +/* check for configuration changes */ +int av7110_check_ir_config(struct av7110 *av7110, int force) { - int ret = 0; + int i; + int modified = force; + int ret = -ENODEV; - dprintk(4, "%p\n", av7110); - if (av7110) { - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, ir_config); - av7110->ir_config = ir_config; + for (i = 0; i < av_cnt; i++) + if (av7110 == av_list[i]) + break; + + if (i < av_cnt && av7110) { + if ((av7110->ir.protocol & 1) != ir_protocol[i] || + av7110->ir.inversion != ir_inversion[i]) + modified = true; + + if (modified) { + /* protocol */ + if (ir_protocol[i]) { + ir_protocol[i] = 1; + av7110->ir.protocol = IR_RCMM; + av7110->ir.ir_config = 0x0001; + } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { + av7110->ir.protocol = IR_RC5_EXT; + av7110->ir.ir_config = 0x0002; + } else { + av7110->ir.protocol = IR_RC5; + av7110->ir.ir_config = 0x0000; + } + /* inversion */ + if (ir_inversion[i]) { + ir_inversion[i] = 1; + av7110->ir.ir_config |= 0x8000; + } + av7110->ir.inversion = ir_inversion[i]; + /* update ARM */ + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, + av7110->ir.ir_config); + } else + ret = 0; + + /* address */ + if (av7110->ir.device_mask != ir_device_mask[i]) + av7110->ir.device_mask = ir_device_mask[i]; } + return ret; } +/* /proc/av7110_ir interface */ static int av7110_ir_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) { char *page; - int size = 4 + 256 * sizeof(u16); u32 ir_config; + int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; int i; if (count < size) @@ -194,71 +289,86 @@ static int av7110_ir_write_proc(struct f return -EFAULT; } - memcpy(&ir_config, page, 4); - memcpy(&key_map, page + 4, 256 * sizeof(u16)); + memcpy(&ir_config, page, sizeof ir_config); + + for (i = 0; i < av_cnt; i++) { + /* keymap */ + memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, + sizeof(av_list[i]->ir.key_map)); + /* protocol, inversion, address */ + ir_protocol[i] = ir_config & 0x0001; + ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; + if (ir_config & 0x4000) + ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); + else + ir_device_mask[i] = IR_ALL; + /* update configuration */ + av7110_check_ir_config(av_list[i], false); + input_register_keys(&av_list[i]->ir); + } vfree(page); - if (FW_VERSION(av_list[0]->arm_app) >= 0x2620 && !(ir_config & 0x0001)) - ir_config |= 0x0002; /* enable extended RC5 */ - for (i = 0; i < av_cnt; i++) - av7110_setup_irc_config(av_list[i], ir_config); - input_register_keys(); return count; } +/* interrupt handler */ static void ir_handler(struct av7110 *av7110, u32 ircom) { - dprintk(4, "ircommand = %08x\n", ircom); - av7110->ir_command = ircom; - tasklet_schedule(&av7110->ir_tasklet); + dprintk(4, "ir command = %08x\n", ircom); + av7110->ir.ir_command = ircom; + tasklet_schedule(&av7110->ir.ir_tasklet); } int __devinit av7110_ir_init(struct av7110 *av7110) { + struct input_dev *input_dev; static struct proc_dir_entry *e; int err; if (av_cnt >= ARRAY_SIZE(av_list)) return -ENOSPC; - av7110_setup_irc_config(av7110, 0x0001); av_list[av_cnt++] = av7110; + av7110_check_ir_config(av7110, true); - if (av_cnt == 1) { - init_timer(&keyup_timer); - keyup_timer.data = 0; - - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; - - snprintf(input_phys, sizeof(input_phys), - "pci-%s/ir0", pci_name(av7110->dev->pci)); - - input_dev->name = "DVB on-card IR receiver"; - - input_dev->phys = input_phys; - input_dev->id.bustype = BUS_PCI; - input_dev->id.version = 1; - if (av7110->dev->pci->subsystem_vendor) { - input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; - input_dev->id.product = av7110->dev->pci->subsystem_device; - } else { - input_dev->id.vendor = av7110->dev->pci->vendor; - input_dev->id.product = av7110->dev->pci->device; - } - input_dev->cdev.dev = &av7110->dev->pci->dev; - set_bit(EV_KEY, input_dev->evbit); - set_bit(EV_REP, input_dev->evbit); - input_register_keys(); - err = input_register_device(input_dev); - if (err) { - input_free_device(input_dev); - return err; - } - input_dev->timer.function = input_repeat_key; + init_timer(&av7110->ir.keyup_timer); + av7110->ir.keyup_timer.function = av7110_emit_keyup; + av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + av7110->ir.input_dev = input_dev; + snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), + "pci-%s/ir0", pci_name(av7110->dev->pci)); + + input_dev->name = "DVB on-card IR receiver"; + + input_dev->phys = av7110->ir.input_phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 2; + if (av7110->dev->pci->subsystem_vendor) { + input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; + input_dev->id.product = av7110->dev->pci->subsystem_device; + } else { + input_dev->id.vendor = av7110->dev->pci->vendor; + input_dev->id.product = av7110->dev->pci->device; + } + input_dev->cdev.dev = &av7110->dev->pci->dev; + /* initial keymap */ + memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); + input_register_keys(&av7110->ir); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + input_dev->timer.function = input_repeat_key; + input_dev->timer.data = (unsigned long) &av7110->ir; + + if (av_cnt == 1) { e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL); if (e) { e->write_proc = av7110_ir_write_proc; @@ -266,8 +376,8 @@ int __devinit av7110_ir_init(struct av71 } } - tasklet_init(&av7110->ir_tasklet, av7110_emit_key, (unsigned long) av7110); - av7110->ir_handler = ir_handler; + tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); + av7110->ir.ir_handler = ir_handler; return 0; } @@ -280,8 +390,10 @@ void __devexit av7110_ir_exit(struct av7 if (av_cnt == 0) return; - av7110->ir_handler = NULL; - tasklet_kill(&av7110->ir_tasklet); + del_timer_sync(&av7110->ir.keyup_timer); + av7110->ir.ir_handler = NULL; + tasklet_kill(&av7110->ir.ir_tasklet); + for (i = 0; i < av_cnt; i++) if (av_list[i] == av7110) { av_list[i] = av_list[av_cnt-1]; @@ -289,14 +401,13 @@ void __devexit av7110_ir_exit(struct av7 break; } - if (av_cnt == 1) { - del_timer_sync(&keyup_timer); + if (av_cnt == 1) remove_proc_entry("av7110_ir", NULL); - input_unregister_device(input_dev); - } + + input_unregister_device(av7110->ir.input_dev); av_cnt--; } -//MODULE_AUTHOR("Holger Waechtler "); +//MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); //MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 3035b22..0e817d6 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -35,7 +35,7 @@ #include "budget.h" #include "stv0299.h" -#include "tda10021.h" +#include "tda1002x.h" #include "tda1004x.h" #include "tua6100.h" #include "dvb-pll.h" @@ -66,9 +66,6 @@ struct budget_av { int slot_status; struct dvb_ca_en50221 ca; u8 reinitialise_demod:1; - u8 tda10021_poclkp:1; - u8 tda10021_ts_enabled; - int (*tda10021_set_frontend)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); }; static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); @@ -234,12 +231,6 @@ static int ciintf_slot_reset(struct dvb_ if (budget_av->reinitialise_demod) dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); - /* set tda10021 back to original clock configuration on reset */ - if (budget_av->tda10021_poclkp) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); - budget_av->tda10021_ts_enabled = 0; - } - return 0; } @@ -256,11 +247,6 @@ static int ciintf_slot_shutdown(struct d ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); budget_av->slot_status = SLOTSTATUS_NONE; - /* set tda10021 back to original clock configuration when cam removed */ - if (budget_av->tda10021_poclkp) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); - budget_av->tda10021_ts_enabled = 0; - } return 0; } @@ -276,12 +262,6 @@ static int ciintf_slot_ts_enable(struct ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); - /* tda10021 seems to need a different TS clock config when data is routed to the CAM */ - if (budget_av->tda10021_poclkp) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1); - budget_av->tda10021_ts_enabled = 1; - } - return 0; } @@ -631,37 +611,62 @@ static struct stv0299_config cinergy_120 static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget *budget = (struct budget *) fe->dvb->priv; - u8 buf[4]; + u8 buf[6]; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + int i; +#define CU1216_IF 36125000 #define TUNER_MUL 62500 - u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL; + u32 div = (params->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; buf[0] = (div >> 8) & 0x7f; buf[1] = div & 0xff; - buf[2] = 0x86; + buf[2] = 0xce; buf[3] = (params->frequency < 150000000 ? 0x01 : params->frequency < 445000000 ? 0x02 : 0x04); + buf[4] = 0xde; + buf[5] = 0x20; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + /* wait for the pll lock */ + msg.flags = I2C_M_RD; + msg.len = 1; + for (i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) + break; + msleep(10); + } + + /* switch the charge pump to the lower current */ + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[2]; + buf[2] &= ~0x40; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; } -static struct tda10021_config philips_cu1216_config = { +static struct tda1002x_config philips_cu1216_config = { .demod_address = 0x0c, + .invert = 1, }; -static struct tda10021_config philips_cu1216_config_altaddress = { +static struct tda1002x_config philips_cu1216_config_altaddress = { .demod_address = 0x0d, + .invert = 0, }; - - - static int philips_tu1216_tuner_init(struct dvb_frontend *fe) { struct budget *budget = (struct budget *) fe->dvb->priv; @@ -908,41 +913,28 @@ static u8 read_pwm(struct budget_av *bud return pwm; } -#define SUBID_DVBS_KNC1 0x0010 -#define SUBID_DVBS_KNC1_PLUS 0x0011 -#define SUBID_DVBS_TYPHOON 0x4f56 -#define SUBID_DVBS_CINERGY1200 0x1154 -#define SUBID_DVBS_CYNERGY1200N 0x1155 - -#define SUBID_DVBS_TV_STAR 0x0014 -#define SUBID_DVBS_TV_STAR_CI 0x0016 -#define SUBID_DVBS_EASYWATCH_1 0x001a -#define SUBID_DVBS_EASYWATCH 0x001e -#define SUBID_DVBC_EASYWATCH 0x002a -#define SUBID_DVBC_KNC1 0x0020 -#define SUBID_DVBC_KNC1_PLUS 0x0021 -#define SUBID_DVBC_CINERGY1200 0x1156 - -#define SUBID_DVBT_KNC1_PLUS 0x0031 -#define SUBID_DVBT_KNC1 0x0030 -#define SUBID_DVBT_CINERGY1200 0x1157 - - -static int tda10021_set_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *p) -{ - struct budget_av* budget_av = fe->dvb->priv; - int result; - - result = budget_av->tda10021_set_frontend(fe, p); - if (budget_av->tda10021_ts_enabled) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1); - } else { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); - } - - return result; -} +#define SUBID_DVBS_KNC1 0x0010 +#define SUBID_DVBS_KNC1_PLUS 0x0011 +#define SUBID_DVBS_TYPHOON 0x4f56 +#define SUBID_DVBS_CINERGY1200 0x1154 +#define SUBID_DVBS_CYNERGY1200N 0x1155 +#define SUBID_DVBS_TV_STAR 0x0014 +#define SUBID_DVBS_TV_STAR_CI 0x0016 +#define SUBID_DVBS_EASYWATCH_1 0x001a +#define SUBID_DVBS_EASYWATCH 0x001e + +#define SUBID_DVBC_EASYWATCH 0x002a +#define SUBID_DVBC_EASYWATCH_MK3 0x002c +#define SUBID_DVBC_KNC1 0x0020 +#define SUBID_DVBC_KNC1_PLUS 0x0021 +#define SUBID_DVBC_KNC1_MK3 0x0022 +#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 +#define SUBID_DVBC_CINERGY1200 0x1156 +#define SUBID_DVBC_CINERGY1200_MK3 0x1176 + +#define SUBID_DVBT_KNC1_PLUS 0x0031 +#define SUBID_DVBT_KNC1 0x0030 +#define SUBID_DVBT_CINERGY1200 0x1157 static void frontend_init(struct budget_av *budget_av) { @@ -961,6 +953,7 @@ static void frontend_init(struct budget_ case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBT_KNC1_PLUS: case SUBID_DVBC_EASYWATCH: + case SUBID_DVBC_KNC1_PLUS_MK3: saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); break; } @@ -1017,6 +1010,7 @@ static void frontend_init(struct budget_ case SUBID_DVBC_CINERGY1200: case SUBID_DVBC_EASYWATCH: budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; fe = dvb_attach(tda10021_attach, &philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); @@ -1025,9 +1019,20 @@ static void frontend_init(struct budget_ &budget_av->budget.i2c_adap, read_pwm(budget_av)); if (fe) { - budget_av->tda10021_poclkp = 1; - budget_av->tda10021_set_frontend = fe->ops.set_frontend; - fe->ops.set_frontend = tda10021_set_frontend; + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBC_EASYWATCH_MK3: + case SUBID_DVBC_CINERGY1200_MK3: + case SUBID_DVBC_KNC1_MK3: + case SUBID_DVBC_KNC1_PLUS_MK3: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10023_attach, &philips_cu1216_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; } break; @@ -1260,12 +1265,16 @@ MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); +MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); static struct pci_device_id pci_tbl[] = { @@ -1279,13 +1288,17 @@ static struct pci_device_id pci_tbl[] = MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), + MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), + MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), + MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), + MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), { .vendor = 0, diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 464feaf..4ed4599 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -73,21 +73,15 @@ #define SLOTSTATUS_RESET 4 #define SLOTSTATUS_READY 8 #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) -/* Milliseconds during which key presses are regarded as key repeat and during - * which the debounce logic is active +/* + * Milliseconds during which a key is regarded as pressed. + * If an identical command arrives within this time, the timer will start over. */ -#define IR_REPEAT_TIMEOUT 350 +#define IR_KEYPRESS_TIMEOUT 250 /* RC5 device wildcard */ #define IR_DEVICE_ANY 255 -/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two), - * this setting allows the superflous sequences to be ignored - */ -static int debounce = 0; -module_param(debounce, int, 0644); -MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)"); - static int rc5_device = -1; module_param(rc5_device, int, 0644); MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); @@ -99,10 +93,14 @@ MODULE_PARM_DESC(ir_debug, "enable debug struct budget_ci_ir { struct input_dev *dev; struct tasklet_struct msp430_irq_tasklet; + struct timer_list timer_keyup; char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ char phys[32]; struct ir_input_state state; int rc5_device; + u32 last_raw; + u32 ir_key; + bool have_command; }; struct budget_ci { @@ -125,13 +123,8 @@ static void msp430_ir_interrupt(unsigned { struct budget_ci *budget_ci = (struct budget_ci *) data; struct input_dev *dev = budget_ci->ir.dev; - static int bounces = 0; - int device; - int toggle; - static int prev_toggle = -1; - static u32 ir_key; - static int state = 0; u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + u32 raw; /* * The msp430 chip can generate two different bytes, command and device @@ -143,7 +136,7 @@ static void msp430_ir_interrupt(unsigned * bytes and one or more device bytes. For the repeated bytes, the * highest bit (X) is set. The first command byte is always generated * before the first device byte. Other than that, no specific order - * seems to apply. + * seems to apply. To make life interesting, bytes can also be lost. * * Only when we have a command and device byte, a keypress is * generated. @@ -152,53 +145,35 @@ static void msp430_ir_interrupt(unsigned if (ir_debug) printk("budget_ci: received byte 0x%02x\n", command); - /* Is this a repeated byte? */ - if (command & 0x80) - return; + /* Remove repeat bit, we use every command */ + command = command & 0x7f; /* Is this a RC5 command byte? */ if (command & 0x40) { - state = 1; - ir_key = command & 0x3f; + budget_ci->ir.have_command = true; + budget_ci->ir.ir_key = command & 0x3f; return; } /* It's a RC5 device byte */ - if (!state) + if (!budget_ci->ir.have_command) return; - state = 0; - device = command & 0x1f; - toggle = command & 0x20; + budget_ci->ir.have_command = false; - if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device) + if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && + budget_ci->ir.rc5_device != (command & 0x1f)) return; - /* Ignore repeated key sequences if requested */ - if (toggle == prev_toggle && ir_key == dev->repeat_key && - bounces > 0 && timer_pending(&dev->timer)) { - if (ir_debug) - printk("budget_ci: debounce logic ignored IR command\n"); - bounces--; - return; - } - prev_toggle = toggle; - - /* Are we still waiting for a keyup event? */ - if (del_timer(&dev->timer)) - ir_input_nokey(dev, &budget_ci->ir.state); - - /* Generate keypress */ - if (ir_debug) - printk("budget_ci: generating keypress 0x%02x\n", ir_key); - ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8))); - - /* Do we want to delay the keyup event? */ - if (debounce) { - bounces = debounce; - mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT)); - } else { + /* Is this a repeated key sequence? (same device, command, toggle) */ + raw = budget_ci->ir.ir_key | (command << 8); + if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) { ir_input_nokey(dev, &budget_ci->ir.state); + ir_input_keydown(dev, &budget_ci->ir.state, + budget_ci->ir.ir_key, raw); + budget_ci->ir.last_raw = raw; } + + mod_timer(&budget_ci->ir.timer_keyup, jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT)); } static int msp430_ir_init(struct budget_ci *budget_ci) @@ -271,16 +246,21 @@ static int msp430_ir_init(struct budget_ break; } - /* initialise the key-up debounce timeout handler */ - input_dev->timer.function = msp430_ir_keyup; - input_dev->timer.data = (unsigned long) &budget_ci->ir; - + /* initialise the key-up timeout handler */ + init_timer(&budget_ci->ir.timer_keyup); + budget_ci->ir.timer_keyup.function = msp430_ir_keyup; + budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir; + budget_ci->ir.last_raw = 0xffff; /* An impossible value */ error = input_register_device(input_dev); if (error) { printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); goto out2; } + /* note: these must be after input_register_device */ + input_dev->rep[REP_DELAY] = 400; + input_dev->rep[REP_PERIOD] = 250; + tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, (unsigned long) budget_ci); @@ -304,10 +284,8 @@ static void msp430_ir_deinit(struct budg saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); - if (del_timer(&dev->timer)) { - ir_input_nokey(dev, &budget_ci->ir.state); - input_sync(dev); - } + del_timer_sync(&dev->timer); + ir_input_nokey(dev, &budget_ci->ir.state); input_unregister_device(dev); } diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c index e15562f..6b97dc1 100644 --- a/drivers/media/dvb/ttpci/budget-core.c +++ b/drivers/media/dvb/ttpci/budget-core.c @@ -41,11 +41,14 @@ #include "ttpci-eeprom.h" #define TS_WIDTH (2 * TS_SIZE) #define TS_WIDTH_ACTIVY TS_SIZE +#define TS_WIDTH_DVBC TS_SIZE #define TS_HEIGHT_MASK 0xf00 #define TS_HEIGHT_MASK_ACTIVY 0xc00 +#define TS_HEIGHT_MASK_DVBC 0xe00 #define TS_MIN_BUFSIZE_K 188 #define TS_MAX_BUFSIZE_K 1410 #define TS_MAX_BUFSIZE_K_ACTIVY 564 +#define TS_MAX_BUFSIZE_K_DVBC 1316 #define BUFFER_WARNING_WAIT (30*HZ) int budget_debug; @@ -106,6 +109,19 @@ static int start_ts_capture(struct budge saa7146_write(dev, MC2, (MASK_10 | MASK_26)); saa7146_write(dev, BRS_CTRL, 0x60000000); break; + case BUDGET_CIN1200C_MK3: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1CP_MK3: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + break; default: if (budget->video_port == BUDGET_VIDEO_PORTA) { saa7146_write(dev, DD1_INIT, 0x06000200); @@ -122,7 +138,13 @@ static int start_ts_capture(struct budge mdelay(10); saa7146_write(dev, BASE_ODD3, 0); - saa7146_write(dev, BASE_EVEN3, 0); + if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { + // using odd/even buffers + saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); + } else { + // using a single buffer + saa7146_write(dev, BASE_EVEN3, 0); + } saa7146_write(dev, PROT_ADDR3, budget->buffer_size); saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); @@ -399,11 +421,25 @@ int ttpci_budget_init(struct budget *bud budget->card = bi; budget->dev = (struct saa7146_dev *) dev; - if (budget->card->type == BUDGET_FS_ACTIVY) { + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: budget->buffer_width = TS_WIDTH_ACTIVY; max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; height_mask = TS_HEIGHT_MASK_ACTIVY; - } else { + break; + + case BUDGET_KNC1C: + case BUDGET_KNC1CP: + case BUDGET_CIN1200C: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1CP_MK3: + case BUDGET_CIN1200C_MK3: + budget->buffer_width = TS_WIDTH_DVBC; + max_bufsize = TS_MAX_BUFSIZE_K_DVBC; + height_mask = TS_HEIGHT_MASK_DVBC; + break; + + default: budget->buffer_width = TS_WIDTH; max_bufsize = TS_MAX_BUFSIZE_K; height_mask = TS_HEIGHT_MASK; @@ -415,14 +451,22 @@ int ttpci_budget_init(struct budget *bud dma_buffer_size = max_bufsize; budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; - budget->buffer_height &= height_mask; - budget->buffer_size = budget->buffer_height * budget->buffer_width; + if (budget->buffer_height > 0xfff) { + budget->buffer_height /= 2; + budget->buffer_height &= height_mask; + budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; + } else { + budget->buffer_height &= height_mask; + budget->buffer_size = budget->buffer_height * budget->buffer_width; + } budget->buffer_warning_threshold = budget->buffer_size * 80/100; budget->buffer_warnings = 0; budget->buffer_warning_time = jiffies; - dprintk(2, "%s: width = %d, height = %d\n", - budget->dev->name, budget->buffer_width, budget->buffer_height); + dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", + budget->dev->name, + budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", + budget->buffer_width, budget->buffer_height); printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); if ((ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner, &budget->dev->pci->dev)) < 0) { diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h index e8a5c79..d764ffa 100644 --- a/drivers/media/dvb/ttpci/budget.h +++ b/drivers/media/dvb/ttpci/budget.h @@ -99,6 +99,9 @@ #define BUDGET_KNC1SP 11 #define BUDGET_KNC1CP 12 #define BUDGET_KNC1TP 13 #define BUDGET_TVSTAR 14 +#define BUDGET_CIN1200C_MK3 15 +#define BUDGET_KNC1C_MK3 16 +#define BUDGET_KNC1CP_MK3 17 #define BUDGET_VIDEO_PORTA 0 #define BUDGET_VIDEO_PORTB 1 diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig index e78ea92..f546bcc 100644 --- a/drivers/media/dvb/ttusb-budget/Kconfig +++ b/drivers/media/dvb/ttusb-budget/Kconfig @@ -1,7 +1,6 @@ config DVB_TTUSB_BUDGET tristate "Technotrend/Hauppauge Nova-USB devices" depends on DVB_CORE && USB && I2C - select DVB_PLL select DVB_CX22700 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_VES1820 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index b2e88ad..5adc27c 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -231,129 +231,149 @@ static struct v4l2_queryctrl radio_qctrl } }; -static int rt_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-aimslab", sizeof(v->driver)); + strlcpy(v->card, "RadioTrack", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct rt_device *rt=dev->priv; + struct rt_device *rt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-aimslab", sizeof (v->driver)); - strlcpy(v->card, "RadioTrack", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff*rt_getsigstr(rt); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + rt->curfreq = f->frequency; + rt_setfreq(rt, rt->curfreq); + return 0; +} - v->rangelow=(87*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*rt_getsigstr(rt); +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - rt->curfreq = f->frequency; - rt_setfreq(rt, rt->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = rt->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = rt->curvol * 6554; + return 0; + } + return -EINVAL; +} - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=rt->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=rt->curvol * 6554; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - rt_mute(rt); - } else { - rt_setvol(rt,rt->curvol); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - rt_setvol(rt,ctrl->value); - return (0); - } - return -EINVAL; - } +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - rt_do_ioctl); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + rt_mute(rt); + else + rt_setvol(rt,rt->curvol); + return 0; + case V4L2_CID_AUDIO_VOLUME: + rt_setvol(rt,ctrl->value); + return 0; } + return -EINVAL; +} + +static int vidioc_g_audio (struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; } -static int rt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, rt_do_ioctl); + if (a->index != 0) + return -EINVAL; + return 0; } static struct rt_device rtrack_unit; @@ -362,7 +382,7 @@ static const struct file_operations rtra .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = rt_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -374,6 +394,18 @@ static struct video_device rtrack_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &rtrack_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init rtrack_init(void) diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c index 74976cb..fdf5d6e 100644 --- a/drivers/media/radio/radio-gemtek-pci.c +++ b/drivers/media/radio/radio-gemtek-pci.c @@ -192,131 +192,158 @@ static inline unsigned int gemtek_pci_ge return ( inb( card->iobase ) & 0x08 ) ? 0 : 1; } -static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver)); + strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct gemtek_pci_card *card = dev->priv; - switch ( cmd ) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-gemtek-pci", sizeof (v->driver)); - strlcpy(v->card, "GemTek PCI Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; - - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + if (v->index > 0) + return -EINVAL; + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = GEMTEK_PCI_RANGE_LOW; + v->rangehigh = GEMTEK_PCI_RANGE_HIGH; + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff * gemtek_pci_getsignal(card); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; - v->rangelow = GEMTEK_PCI_RANGE_LOW; - v->rangehigh = GEMTEK_PCI_RANGE_HIGH; - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*gemtek_pci_getsignal( card ); + if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) || + (f->frequency > GEMTEK_PCI_RANGE_HIGH) ) + return -EINVAL; + gemtek_pci_setfrequency(card, f->frequency); + card->current_frequency = f->frequency; + card->mute = false; + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; - if (v->index > 0) - return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = card->current_frequency; + return 0; +} +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) || - (f->frequency > GEMTEK_PCI_RANGE_HIGH) ) - return -EINVAL; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = card->mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (card->mute) + ctrl->value = 0; + else + ctrl->value = 65535; + return 0; + } + return -EINVAL; +} - gemtek_pci_setfrequency( card, f->frequency ); - card->current_frequency = f->frequency; - card->mute = false; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=card->mute; - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (card->mute) - ctrl->value=0; - else - ctrl->value=65535; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - gemtek_pci_mute(card); - } else { - gemtek_pci_unmute(card); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) { - gemtek_pci_unmute(card); - } else { - gemtek_pci_mute(card); - } - return (0); - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - gemtek_pci_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + gemtek_pci_mute(card); + else + gemtek_pci_unmute(card); + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) + gemtek_pci_unmute(card); + else + gemtek_pci_mute(card); + return 0; } + return -EINVAL; } -static int gemtek_pci_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } enum { @@ -342,7 +369,7 @@ static const struct file_operations gemt .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = gemtek_pci_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -353,6 +380,18 @@ static struct video_device vdev_template .type = VID_TYPE_TUNER, .hardware = 0, .fops = &gemtek_pci_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id ) diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 36c4be6..b04b6a7 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -161,137 +161,157 @@ static int gemtek_getsigstr(struct gemte return 1; /* signal present */ } -static int gemtek_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); + strlcpy(v->card, "GemTek", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct gemtek_device *rt=dev->priv; + struct gemtek_device *rt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-gemtek", sizeof (v->driver)); - strlcpy(v->card, "GemTek", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff*gemtek_getsigstr(rt); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + rt->curfreq = f->frequency; + /* needs to be called twice in order for getsigstr to work */ + gemtek_setfreq(rt, rt->curfreq); + gemtek_setfreq(rt, rt->curfreq); + return 0; +} - v->rangelow=(87*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*gemtek_getsigstr(rt); +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - rt->curfreq = f->frequency; - /* needs to be called twice in order for getsigstr to work */ - gemtek_setfreq(rt, rt->curfreq); - gemtek_setfreq(rt, rt->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=rt->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (rt->muted) - ctrl->value=0; - else - ctrl->value=65535; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - gemtek_mute(rt); - } else { - gemtek_unmute(rt); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) { - gemtek_unmute(rt); - } else { - gemtek_mute(rt); - } - return (0); - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - gemtek_do_ioctl); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = rt->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (rt->muted) + ctrl->value = 0; + else + ctrl->value = 65535; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + gemtek_mute(rt); + else + gemtek_unmute(rt); + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) + gemtek_unmute(rt); + else + gemtek_mute(rt); + return 0; } + return -EINVAL; } -static int gemtek_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio (struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct gemtek_device gemtek_unit; @@ -300,7 +320,7 @@ static const struct file_operations gemt .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = gemtek_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -312,6 +332,18 @@ static struct video_device gemtek_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &gemtek_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init gemtek_init(void) diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c index e67b7f2..11f80ca 100644 --- a/drivers/media/radio/radio-maestro.c +++ b/drivers/media/radio/radio-maestro.c @@ -75,8 +75,6 @@ #define BITS2FREQ(x) ((x) * FREQ_STEP - static int radio_nr = -1; module_param(radio_nr, int, 0); -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void maestro_remove(struct pci_dev *pdev); @@ -102,18 +100,11 @@ static const struct file_operations maes .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = radio_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; -static struct video_device maestro_radio = { - .name = "Maestro radio", - .type = VID_TYPE_TUNER, - .hardware = 0, - .fops = &maestro_fops, -}; - struct radio_device { u16 io, /* base of Maestro card radio io (GPIO_DATA)*/ muted, /* VIDEO_AUDIO_MUTE */ @@ -190,142 +181,153 @@ static void radio_bits_set(struct radio_ msleep(125); } -static inline int radio_function(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-maestro", sizeof(v->driver)); + strlcpy(v->card, "Maestro Radio", sizeof(v->card)); + sprintf(v->bus_info, "PCI"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct radio_device *card = video_get_drvdata(dev); - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-maestro", sizeof (v->driver)); - strlcpy(v->card, "Maestro Radio", sizeof (v->card)); - sprintf(v->bus_info,"PCI"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; - - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - - if (v->index > 0) - return -EINVAL; - - (void)radio_bits_get(card); + if (v->index > 0) + return -EINVAL; + + (void)radio_bits_get(card); + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_LO; + v->rangehigh = FREQ_HI; + v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + if(card->stereo) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = card->tuned; + return 0; +} - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - v->rangelow = FREQ_LO; - v->rangehigh = FREQ_HI; - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(card->stereo) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=card->tuned; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) + return -EINVAL; + radio_bits_set(card, FREQ2BITS(f->frequency)); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = BITS2FREQ(radio_bits_get(card)); + return 0; +} - if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) - return -EINVAL; - radio_bits_set(card, FREQ2BITS(f->frequency)); +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = BITS2FREQ(radio_bits_get(card)); +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=card->muted; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - { - register u16 io = card->io; - register u16 omask = inw(io + IO_MASK); - outw(~STR_WREN, io + IO_MASK); - outw((card->muted = ctrl->value ) ? - STR_WREN : 0, io); - udelay(4); - outw(omask, io + IO_MASK); - msleep(125); - - return (0); - } - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - radio_function); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = card->muted; + return 0; } + return -EINVAL; } -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { struct video_device *dev = video_devdata(file); struct radio_device *card = video_get_drvdata(dev); - int ret; + register u16 io = card->io; + register u16 omask = inw(io + IO_MASK); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + outw(~STR_WREN, io + IO_MASK); + outw((card->muted = ctrl->value ) ? + STR_WREN : 0, io); + udelay(4); + outw(omask, io + IO_MASK); + msleep(125); + return 0; + } + return -EINVAL; +} - mutex_lock(&card->lock); - ret = video_usercopy(inode, file, cmd, arg, radio_function); - mutex_unlock(&card->lock); +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; - return ret; + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static u16 __devinit radio_power_on(struct radio_device *dev) @@ -352,6 +354,24 @@ static u16 __devinit radio_power_on(stru return (ofreq == radio_bits_get(dev)); } +static struct video_device maestro_radio = { + .name = "Maestro radio", + .type = VID_TYPE_TUNER, + .fops = &maestro_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + static int __devinit maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index f668387..9b493b3 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -122,6 +122,26 @@ static int rt_setfreq(struct rt_device * return 0; } +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver)); + strlcpy(v->card, "RadioTrack II", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + return 0; +} + static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ @@ -129,135 +149,136 @@ static int rt_getsigstr(struct rt_device return 1; /* signal present */ } -static int rt_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct rt_device *rt=dev->priv; + struct rt_device *rt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-rtrack2", sizeof (v->driver)); - strlcpy(v->card, "RadioTrack II", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (88*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF*rt_getsigstr(rt); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + rt->curfreq = f->frequency; + rt_setfreq(rt, rt->curfreq); + return 0; +} - v->rangelow=(88*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*rt_getsigstr(rt); +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - rt->curfreq = f->frequency; - rt_setfreq(rt, rt->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = rt->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (rt->muted) + ctrl->value = 0; + else + ctrl->value = 65535; + return 0; + } + return -EINVAL; +} - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=rt->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (rt->muted) - ctrl->value=0; - else - ctrl->value=65535; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - rt_mute(rt); - } else { - rt_unmute(rt); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) { - rt_unmute(rt); - } else { - rt_mute(rt); - } - return (0); - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - rt_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + rt_mute(rt); + else + rt_unmute(rt); + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) + rt_unmute(rt); + else + rt_mute(rt); + return 0; } + return -EINVAL; } -static int rt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, rt_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct rt_device rtrack2_unit; @@ -266,7 +287,7 @@ static const struct file_operations rtra .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = rt_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -278,6 +299,18 @@ static struct video_device rtrack2_radio .type = VID_TYPE_TUNER, .hardware = 0, .fops = &rtrack2_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, }; static int __init rtrack2_init(void) diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index f4619e4..dc33f19 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -130,137 +130,155 @@ static inline int fmi_getsigstr(struct f return (res & 2) ? 0 : 0xFFFF; } -static int fmi_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { + strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); + strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + int mult; struct video_device *dev = video_devdata(file); - struct fmi_device *fmi=dev->priv; + struct fmi_device *fmi = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-sf16fmi", sizeof (v->driver)); - strlcpy(v->card, "SF16-FMx radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - int mult; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; - v->rangelow = RSF16_MINFREQ/mult; - v->rangehigh = RSF16_MAXFREQ/mult; - v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; - v->capability=fmi->flags&V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_STEREO; - v->signal = fmi_getsigstr(fmi); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; + v->rangelow = RSF16_MINFREQ/mult; + v->rangehigh = RSF16_MAXFREQ/mult; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; + v->capability = fmi->flags&V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_STEREO; + v->signal = fmi_getsigstr(fmi); + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) - f->frequency *= 1000; - if (f->frequency < RSF16_MINFREQ || - f->frequency > RSF16_MAXFREQ ) - return -EINVAL; - /*rounding in steps of 800 to match th freq - that will be used */ - fmi->curfreq = (f->frequency/800)*800; - fmi_setfreq(fmi); + if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) + f->frequency *= 1000; + if (f->frequency < RSF16_MINFREQ || + f->frequency > RSF16_MAXFREQ ) + return -EINVAL; + /*rounding in steps of 800 to match th freq + that will be used */ + fmi->curfreq = (f->frequency/800)*800; + fmi_setfreq(fmi); + return 0; +} - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = fmi->curfreq; - if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) - f->frequency /= 1000; + f->type = V4L2_TUNER_RADIO; + f->frequency = fmi->curfreq; + if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) + f->frequency /= 1000; + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=fmi->curvol; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - { - if (ctrl->value) - fmi_mute(fmi->port); - else - fmi_unmute(fmi->port); - - fmi->curvol=ctrl->value; - return (0); - } - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - fmi_do_ioctl); } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = fmi->curvol; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + fmi_mute(fmi->port); + else + fmi_unmute(fmi->port); + fmi->curvol = ctrl->value; + return 0; + } + return -EINVAL; } -static int fmi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct fmi_device fmi_unit; @@ -269,7 +287,7 @@ static const struct file_operations fmi_ .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = fmi_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -281,6 +299,18 @@ static struct video_device fmi_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &fmi_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; /* ladis: this is my card. does any other types exist? */ diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index b96fafe..e6c125d 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -226,186 +226,204 @@ static int fmr2_setvolume(struct fmr2_de return 0; } -static int fmr2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { + strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver)); + strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + int mult; struct video_device *dev = video_devdata(file); struct fmr2_device *fmr2 = dev->priv; - debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d " - "stereo %d type %d\n", - fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute, - fmr2->stereo, fmr2->card_type)); - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-sf16fmr2", sizeof (v->driver)); - strlcpy(v->card, "SF16-FMR2 radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - int mult; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; - v->rangelow = RSF16_MINFREQ/mult; - v->rangehigh = RSF16_MAXFREQ/mult; - v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; - v->capability=fmr2->flags&V4L2_TUNER_CAP_LOW; - - v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO: - V4L2_TUNER_MODE_MONO; - mutex_lock(&lock); - v->signal = fmr2_getsigstr(fmr2); - mutex_unlock(&lock); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; + v->rangelow = RSF16_MINFREQ/mult; + v->rangehigh = RSF16_MAXFREQ/mult; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; + v->capability = fmr2->flags&V4L2_TUNER_CAP_LOW; + v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO: + V4L2_TUNER_MODE_MONO; + mutex_lock(&lock); + v->signal = fmr2_getsigstr(fmr2); + mutex_unlock(&lock); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) - f->frequency *= 1000; - if (f->frequency < RSF16_MINFREQ || - f->frequency > RSF16_MAXFREQ ) - return -EINVAL; - /*rounding in steps of 200 to match th freq - that will be used */ - fmr2->curfreq = (f->frequency/200)*200; - - /* set card freq (if not muted) */ - if (fmr2->curvol && !fmr2->mute) - { - mutex_lock(&lock); - fmr2_setfreq(fmr2); - mutex_unlock(&lock); - } +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) + f->frequency *= 1000; + if (f->frequency < RSF16_MINFREQ || + f->frequency > RSF16_MAXFREQ ) + return -EINVAL; + /*rounding in steps of 200 to match th freq + that will be used */ + fmr2->curfreq = (f->frequency/200)*200; + + /* set card freq (if not muted) */ + if (fmr2->curvol && !fmr2->mute) { + mutex_lock(&lock); + fmr2_setfreq(fmr2); + mutex_unlock(&lock); + } + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = fmr2->curfreq; - if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) - f->frequency /= 1000; + f->type = V4L2_TUNER_RADIO; + f->frequency = fmr2->curfreq; + if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) + f->frequency /= 1000; + return 0; +} +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if ((fmr2->card_type != 11) + && V4L2_CID_AUDIO_VOLUME) + radio_qctrl[i].step = 65535; + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if ((fmr2->card_type != 11) - && V4L2_CID_AUDIO_VOLUME) - radio_qctrl[i].step=65535; - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = fmr2->mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = fmr2->curvol; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + fmr2->mute = ctrl->value; + if (fmr2->card_type != 11) { + if (!fmr2->mute) + fmr2->curvol = 65535; + else + fmr2->curvol = 0; } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=fmr2->mute; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=fmr2->curvol; - return (0); + break; + case V4L2_CID_AUDIO_VOLUME: + fmr2->curvol = ctrl->value; + if (fmr2->card_type != 11) { + if (fmr2->curvol) { + fmr2->curvol = 65535; + fmr2->mute = 0; + } else { + fmr2->curvol = 0; + fmr2->mute = 1; } - return -EINVAL; } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - fmr2->mute=ctrl->value; - if (fmr2->card_type != 11) { - if (!fmr2->mute) { - fmr2->curvol = 65535; - } else { - fmr2->curvol = 0; - } - } - break; - case V4L2_CID_AUDIO_VOLUME: - fmr2->curvol = ctrl->value; - if (fmr2->card_type != 11) { - if (fmr2->curvol) { - fmr2->curvol = 65535; - fmr2->mute = 0; - } else { - fmr2->curvol = 0; - fmr2->mute = 1; - } - } - break; - default: - return -EINVAL; - } + break; + default: + return -EINVAL; + } + #ifdef DEBUG - if (fmr2->curvol && !fmr2->mute) - printk(KERN_DEBUG "unmute\n"); - else - printk(KERN_DEBUG "mute\n"); + if (fmr2->curvol && !fmr2->mute) + printk(KERN_DEBUG "unmute\n"); + else + printk(KERN_DEBUG "mute\n"); #endif - mutex_lock(&lock); - if (fmr2->curvol && !fmr2->mute) { - fmr2_setvolume(fmr2); - fmr2_setfreq(fmr2); - } else - fmr2_mute(fmr2->port); - mutex_unlock(&lock); - return (0); - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - fmr2_do_ioctl); - } + mutex_lock(&lock); + if (fmr2->curvol && !fmr2->mute) { + fmr2_setvolume(fmr2); + fmr2_setfreq(fmr2); + } else + fmr2_mute(fmr2->port); + mutex_unlock(&lock); + return 0; } -static int fmr2_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) - { - return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl); +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct fmr2_device fmr2_unit; @@ -414,7 +432,7 @@ static const struct file_operations fmr2 .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = fmr2_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -426,6 +444,18 @@ static struct video_device fmr2_radio= . type = VID_TYPE_TUNER, .hardware = 0, .fops = &fmr2_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init fmr2_init(void) diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index d59a27a..e43acfd 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -205,135 +205,152 @@ static int tt_getsigstr(struct tt_device return 1; /* signal present */ } +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-terratec", sizeof(v->driver)); + strlcpy(v->card, "ActiveRadio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} -/* implement the video4linux api */ - -static int tt_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct tt_device *tt=dev->priv; + struct tt_device *tt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-terratec", sizeof (v->driver)); - strlcpy(v->card, "ActiveRadio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF*tt_getsigstr(tt); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; - v->rangelow=(87*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*tt_getsigstr(tt); + tt->curfreq = f->frequency; + tt_setfreq(tt, tt->curfreq); + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; - if (v->index > 0) - return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = tt->curfreq; + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; - tt->curfreq = f->frequency; - tt_setfreq(tt, tt->curfreq); + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = tt->curfreq; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (tt->muted) - ctrl->value=1; - else - ctrl->value=0; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=tt->curvol * 6554; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - tt_mute(tt); - } else { - tt_setvol(tt,tt->curvol); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - tt_setvol(tt,ctrl->value); - return (0); - } - return -EINVAL; - } + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (tt->muted) + ctrl->value = 1; + else + ctrl->value = 0; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = tt->curvol * 6554; + return 0; + } + return -EINVAL; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - tt_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + tt_mute(tt); + else + tt_setvol(tt,tt->curvol); + return 0; + case V4L2_CID_AUDIO_VOLUME: + tt_setvol(tt,ctrl->value); + return 0; } + return -EINVAL; +} + +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; } -static int tt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) { - return video_usercopy(inode, file, cmd, arg, tt_do_ioctl); + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct tt_device terratec_unit; @@ -342,7 +359,7 @@ static const struct file_operations terr .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = tt_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -354,6 +371,18 @@ static struct video_device terratec_radi .type = VID_TYPE_TUNER, .hardware = 0, .fops = &terratec_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, }; static int __init terratec_init(void) diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c index 6d7f1e7..c27c629 100644 --- a/drivers/media/radio/radio-trust.c +++ b/drivers/media/radio/radio-trust.c @@ -192,144 +192,154 @@ static void tr_setfreq(unsigned long f) write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); } -static int tr_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-trust", sizeof (v->driver)); - strlcpy(v->card, "Trust FM Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + strlcpy(v->driver, "radio-trust", sizeof(v->driver)); + strlcpy(v->card, "Trust FM Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - v->rangelow=(87.5*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(tr_getstereo()) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=tr_getsigstr(); +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87.5*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + if (tr_getstereo()) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = tr_getsigstr(); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; - if (v->index > 0) - return -EINVAL; + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + curfreq = f->frequency; + tr_setfreq(curfreq); + return 0; +} - curfreq = f->frequency; - tr_setfreq(curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + f->type = V4L2_TUNER_RADIO; + f->frequency = curfreq; + return 0; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = curfreq; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=curmute; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value= curvol * 2048; - return (0); - case V4L2_CID_AUDIO_BASS: - ctrl->value= curbass * 4370; - return (0); - case V4L2_CID_AUDIO_TREBLE: - ctrl->value= curtreble * 4370; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - tr_setmute(ctrl->value); - return 0; - case V4L2_CID_AUDIO_VOLUME: - tr_setvol(ctrl->value); - return 0; - case V4L2_CID_AUDIO_BASS: - tr_setbass(ctrl->value); - return 0; - case V4L2_CID_AUDIO_TREBLE: - tr_settreble(ctrl->value); - return (0); - } - return -EINVAL; - } + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = curmute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = curvol * 2048; + return 0; + case V4L2_CID_AUDIO_BASS: + ctrl->value = curbass * 4370; + return 0; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = curtreble * 4370; + return 0; + } + return -EINVAL; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - tr_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + tr_setmute(ctrl->value); + return 0; + case V4L2_CID_AUDIO_VOLUME: + tr_setvol(ctrl->value); + return 0; + case V4L2_CID_AUDIO_BASS: + tr_setbass(ctrl->value); + return 0; + case V4L2_CID_AUDIO_TREBLE: + tr_settreble(ctrl->value); + return 0; } + return -EINVAL; } -static int tr_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, tr_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static const struct file_operations trust_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = tr_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -341,6 +351,18 @@ static struct video_device trust_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &trust_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, }; static int __init trust_init(void) diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index 3031fef..8ff5a23 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c @@ -93,8 +93,6 @@ static int typhoon_setfreq(struct typhoo static void typhoon_mute(struct typhoon_device *dev); static void typhoon_unmute(struct typhoon_device *dev); static int typhoon_setvol(struct typhoon_device *dev, int vol); -static int typhoon_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); #ifdef CONFIG_RADIO_TYPHOON_PROC_FS static int typhoon_get_info(char *buf, char **start, off_t offset, int len); #endif @@ -186,129 +184,148 @@ static int typhoon_setvol(struct typhoon return 0; } +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-typhoon", sizeof(v->driver)); + strlcpy(v->card, "Typhoon Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87.5*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF; /* We can't get the signal strength */ + return 0; +} -static int typhoon_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) { struct video_device *dev = video_devdata(file); struct typhoon_device *typhoon = dev->priv; - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-typhoon", sizeof (v->driver)); - strlcpy(v->card, "Typhoon Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + typhoon->curfreq = f->frequency; + typhoon_setfreq(typhoon, typhoon->curfreq); + return 0; +} - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct typhoon_device *typhoon = dev->priv; - if (v->index > 0) - return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = typhoon->curfreq; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + return 0; +} - v->rangelow=(87.5*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xFFFF; /* We can't get the signal strength */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + } + return -EINVAL; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct typhoon_device *typhoon = dev->priv; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = typhoon->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = typhoon->curvol; + return 0; + } + return -EINVAL; +} - typhoon->curfreq = f->frequency; - typhoon_setfreq(typhoon, typhoon->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct typhoon_device *typhoon = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = typhoon->curfreq; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + typhoon_mute(typhoon); + else + typhoon_unmute(typhoon); + return 0; + case V4L2_CID_AUDIO_VOLUME: + typhoon_setvol(typhoon, ctrl->value); + return 0; + } + return -EINVAL; +} - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=typhoon->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=typhoon->curvol; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - typhoon_mute(typhoon); - } else { - typhoon_unmute(typhoon); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - typhoon_setvol(typhoon, ctrl->value); - return (0); - } - return -EINVAL; - } +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - typhoon_do_ioctl); - } + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; } -static int typhoon_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl); + if (a->index != 0) + return -EINVAL; + return 0; } static struct typhoon_device typhoon_unit = @@ -322,7 +339,7 @@ static const struct file_operations typh .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = typhoon_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -334,6 +351,18 @@ static struct video_device typhoon_radio .type = VID_TYPE_TUNER, .hardware = 0, .fops = &typhoon_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; #ifdef CONFIG_RADIO_TYPHOON_PROC_FS diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index ec08491..a471590 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c @@ -230,121 +230,123 @@ static int zol_is_stereo (struct zol_dev return 0; } -static int zol_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver)); + strlcpy(v->card, "Zoltrix Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct zol_device *zol = dev->priv; - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-zoltrix", sizeof (v->driver)); - strlcpy(v->card, "Zoltrix Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - v->rangelow=(88*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(zol_is_stereo(zol)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*zol_getsigstr(zol); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (88*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + if (zol_is_stereo(zol)) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF*zol_getsigstr(zol); + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + zol->curfreq = f->frequency; + zol_setfreq(zol, zol->curfreq); + return 0; +} - zol->curfreq = f->frequency; - zol_setfreq(zol, zol->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; + + f->type = V4L2_TUNER_RADIO; + f->frequency = zol->curfreq; + return 0; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = zol->curfreq; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=zol->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=zol->curvol * 4096; - return (0); - } - return -EINVAL; + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = zol->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = zol->curvol * 4096; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + zol_mute(zol); + else { + zol_unmute(zol); + zol_setvol(zol,zol->curvol); } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - zol_mute(zol); - } else { - zol_unmute(zol); - zol_setvol(zol,zol->curvol); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - zol_setvol(zol,ctrl->value/4096); - return (0); - } - zol->stereo = 1; - zol_setfreq(zol, zol->curfreq); + return 0; + case V4L2_CID_AUDIO_VOLUME: + zol_setvol(zol,ctrl->value/4096); + return 0; + } + zol->stereo = 1; + zol_setfreq(zol, zol->curfreq); #if 0 /* FIXME: Implement stereo/mono switch on V4L2 */ if (v->mode & VIDEO_SOUND_STEREO) { @@ -356,19 +358,39 @@ #if 0 zol_setfreq(zol, zol->curfreq); } #endif - return -EINVAL; - } + return -EINVAL; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - zol_do_ioctl); - } +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; } -static int zol_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) { - return video_usercopy(inode, file, cmd, arg, zol_do_ioctl); + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct zol_device zoltrix_unit; @@ -378,7 +400,7 @@ static const struct file_operations zolt .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = zol_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -390,6 +412,18 @@ static struct video_device zoltrix_radio .type = VID_TYPE_TUNER, .hardware = 0, .fops = &zoltrix_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init zoltrix_init(void) diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 7a61051..bc77378 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -577,14 +577,14 @@ config VIDEO_ZORAN_AVS6EYES config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && SONYPI && VIDEO_V4L1 + depends on PCI && SONY_LAPTOP && VIDEO_V4L1 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in for more information. - If you say Y or M here, you need to say Y or M to "Sony Programmable - I/O Control Device" in the character device section. + If you say Y or M here, you need to say Y or M to "Sony Laptop + Extras" in the misc device section. To compile this driver as a module, choose M here: the module will be called meye. @@ -647,6 +647,8 @@ config VIDEO_HEXIUM_GEMINI source "drivers/media/video/cx88/Kconfig" +source "drivers/media/video/ivtv/Kconfig" + config VIDEO_M32R_AR tristate "AR devices" depends on M32R && VIDEO_V4L1 @@ -761,6 +763,18 @@ source "drivers/media/video/zc0301/Kconf source "drivers/media/video/pwc/Kconfig" +config USB_ZR364XX + tristate "USB ZR364XX Camera support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. + See for more info + and list of supported cameras. + + To compile this driver as a module, choose M here: the + module will be called zr364xx. + endmenu # V4L USB devices endmenu diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 44ccaed..9c2de50 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ +obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o @@ -99,6 +100,7 @@ obj-$(CONFIG_USB_OV511) += ov511 obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_W9968CF) += w9968cf.o +obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 6addc42..6b31e50 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -291,6 +291,9 @@ static struct CARD { { 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" }, + { 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" }, + { 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" }, + /* likely broken, vendor id doesn't match the other magic views ... * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */ @@ -2907,6 +2910,28 @@ struct tvcard bttv_tvcards[] = { .has_radio = 1, .has_remote = 1, }, + [BTTV_BOARD_SSAI_SECURITY] = { + .name = "SSAI Security Video Interface", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .muxsel = { 0, 1, 2, 3 }, + .tuner_type = -1, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_SSAI_ULTRASOUND] = { + .name = "SSAI Ultrasound Video Interface", + .video_inputs = 2, + .audio_inputs = 0, + .tuner = -1, + .svhs = 1, + .muxsel = { 2, 0, 1, 3 }, + .tuner_type = -1, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -2970,20 +2995,20 @@ void __devinit bttv_idcard(struct bttv * if (UNSET != audiomux[0]) { gpiobits = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i]; gpiobits |= audiomux[i]; } } else { gpiobits = audioall; - for (i = 0; i < 4; i++) { + for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { bttv_tvcards[btv->c.type].gpiomux[i] = audioall; } } bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits; printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=", btv->c.nr,bttv_tvcards[btv->c.type].gpiomask); - for (i = 0; i < 5; i++) { + for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]); } printk("\n"); @@ -3638,7 +3663,7 @@ static int __devinit pvr_altera_load(str for (n = 0; n < microlen; n++) { bits = micro[n]; - for ( i = 0 ; i < 8 ; i++ ) { + for (i = 0 ; i < 8 ; i++) { gpio_bits(BTTV_ALT_DCLK,0); if (bits & 0x01) gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA); @@ -3691,7 +3716,7 @@ static void __devinit osprey_eeprom(stru /* this might be an antique... check for MMAC label in eeprom */ if ((ee[0]=='M') && (ee[1]=='M') && (ee[2]=='A') && (ee[3]=='C')) { unsigned char checksum = 0; - for (i =0; i<21; i++) + for (i = 0; i < 21; i++) checksum += ee[i]; if (checksum != ee[21]) return; @@ -3703,12 +3728,13 @@ static void __devinit osprey_eeprom(stru unsigned short type; int offset = 4*16; - for(; offset < 8*16; offset += 16) { + for (; offset < 8*16; offset += 16) { unsigned short checksum = 0; /* verify the checksum */ - for(i = 0; i<14; i++) checksum += ee[i+offset]; - checksum = ~checksum; /* no idea why */ - if ((((checksum>>8)&0x0FF) == ee[offset+14]) && + for (i = 0; i < 14; i++) + checksum += ee[i+offset]; + checksum = ~checksum; /* no idea why */ + if ((((checksum>>8)&0x0FF) == ee[offset+14]) && ((checksum & 0x0FF) == ee[offset+15])) { break; } @@ -3721,7 +3747,6 @@ static void __devinit osprey_eeprom(stru type = (ee[offset+4]<<8) | (ee[offset+5]); switch(type) { - /* 848 based */ case 0x0004: btv->c.type = BTTV_BOARD_OSPREY1x0_848; @@ -4149,8 +4174,7 @@ static int tea5757_read(struct bttv *btv } dprintk("bttv%d: tea5757:",btv->c.nr); - for(i = 0; i < 24; i++) - { + for (i = 0; i < 24; i++) { udelay(5); bus_high(btv,btv->mbox_clk); udelay(5); @@ -4182,8 +4206,7 @@ static int tea5757_write(struct bttv *bt dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value); bus_low(btv,btv->mbox_clk); bus_high(btv,btv->mbox_we); - for(i = 0; i < 25; i++) - { + for (i = 0; i < 25; i++) { if (reg & 0x1000000) bus_high(btv,btv->mbox_data); else @@ -4755,7 +4778,7 @@ static void kodicom4400r_init(struct btt gpio_write(1 << 9); /* reset MUX */ gpio_write(0); /* Preset camera 0 to the 4 controllers */ - for (ix=0; ix<4; ix++) { + for (ix = 0; ix < 4; ix++) { sw_status[ix] = ix; kodicom4400r_write(btv, ix, ix, 1); } diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 5720b77..1c38723 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -164,6 +164,24 @@ static ssize_t show_card(struct class_de static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL); /* ----------------------------------------------------------------------- */ +/* dvb auto-load setup */ +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + request_module("dvb-bt8xx"); +} + +static void request_modules(struct bttv *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + + +/* ----------------------------------------------------------------------- */ /* static data */ /* special timing tables from conexant... */ @@ -4769,9 +4787,11 @@ static int __devinit bttv_probe(struct p disclaim_video_lines(btv); } - /* add subdevices */ - if (bttv_tvcards[btv->c.type].has_dvb) + /* add subdevices and autoload dvb-bt8xx if needed */ + if (bttv_tvcards[btv->c.type].has_dvb) { bttv_sub_add_device(&btv->c, "dvb"); + request_modules(btv); + } bttv_input_init(btv); diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c index ba081f6..84154c2 100644 --- a/drivers/media/video/bt8xx/bttv-gpio.c +++ b/drivers/media/video/bt8xx/bttv-gpio.c @@ -71,7 +71,6 @@ struct bus_type bttv_sub_bus_type = { .probe = bttv_sub_probe, .remove = bttv_sub_remove, }; -EXPORT_SYMBOL(bttv_sub_bus_type); static void release_sub_device(struct device *dev) { @@ -152,7 +151,6 @@ void bttv_gpio_inout(struct bttv_core *c btwrite(data,BT848_GPIO_OUT_EN); spin_unlock_irqrestore(&btv->gpio_lock,flags); } -EXPORT_SYMBOL(bttv_gpio_inout); u32 bttv_gpio_read(struct bttv_core *core) { @@ -162,7 +160,6 @@ u32 bttv_gpio_read(struct bttv_core *cor value = btread(BT848_GPIO_DATA); return value; } -EXPORT_SYMBOL(bttv_gpio_read); void bttv_gpio_write(struct bttv_core *core, u32 value) { @@ -170,7 +167,6 @@ void bttv_gpio_write(struct bttv_core *c btwrite(value,BT848_GPIO_DATA); } -EXPORT_SYMBOL(bttv_gpio_write); void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits) { @@ -185,7 +181,6 @@ void bttv_gpio_bits(struct bttv_core *co btwrite(data,BT848_GPIO_DATA); spin_unlock_irqrestore(&btv->gpio_lock,flags); } -EXPORT_SYMBOL(bttv_gpio_bits); /* * Local variables: diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index 62b8730..0dfa49b 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -412,7 +412,7 @@ static void do_i2c_scan(char *name, stru unsigned char buf; int i,rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c,&buf,0); if (rc < 0) diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/video/bt8xx/bttv-if.c index 19b564a..ecf0798 100644 --- a/drivers/media/video/bt8xx/bttv-if.c +++ b/drivers/media/video/bt8xx/bttv-if.c @@ -33,32 +33,16 @@ #include #include "bttvp.h" -EXPORT_SYMBOL(bttv_get_cardinfo); EXPORT_SYMBOL(bttv_get_pcidev); -EXPORT_SYMBOL(bttv_get_id); EXPORT_SYMBOL(bttv_gpio_enable); EXPORT_SYMBOL(bttv_read_gpio); EXPORT_SYMBOL(bttv_write_gpio); -EXPORT_SYMBOL(bttv_get_gpio_queue); -EXPORT_SYMBOL(bttv_i2c_call); /* ----------------------------------------------------------------------- */ /* Exported functions - for other modules which want to access the */ /* gpio ports (IR for example) */ /* see bttv.h for comments */ -int bttv_get_cardinfo(unsigned int card, int *type, unsigned *cardid) -{ - printk("The bttv_* interface is obsolete and will go away,\n" - "please use the new, sysfs based interface instead.\n"); - if (card >= bttv_num) { - return -1; - } - *type = bttvs[card].c.type; - *cardid = bttvs[card].cardid; - return 0; -} - struct pci_dev* bttv_get_pcidev(unsigned int card) { if (card >= bttv_num) @@ -66,16 +50,6 @@ struct pci_dev* bttv_get_pcidev(unsigned return bttvs[card].c.pci; } -int bttv_get_id(unsigned int card) -{ - printk("The bttv_* interface is obsolete and will go away,\n" - "please use the new, sysfs based interface instead.\n"); - if (card >= bttv_num) { - return -1; - } - return bttvs[card].c.type; -} - int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data) { @@ -130,28 +104,6 @@ int bttv_write_gpio(unsigned int card, u return 0; } -wait_queue_head_t* bttv_get_gpio_queue(unsigned int card) -{ - struct bttv *btv; - - if (card >= bttv_num) { - return NULL; - } - - btv = &bttvs[card]; - if (bttvs[card].shutdown) { - return NULL; - } - return &btv->gpioq; -} - -void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg) -{ - if (card >= bttv_num) - return; - bttv_call_i2c_clients(&bttvs[card], cmd, arg); -} - /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 5491acb..f821ba6 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -168,6 +168,8 @@ #define BTTV_BOARD_ASOUND_SKYEYE 0x8d #define BTTV_BOARD_SABRENT_TVFM 0x8e #define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f #define BTTV_BOARD_MACHTV_MAGICTV 0x90 +#define BTTV_BOARD_SSAI_SECURITY 0x91 +#define BTTV_BOARD_SSAI_ULTRASOUND 0x92 /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 @@ -260,17 +262,8 @@ extern int bttv_handle_chipset(struct bt /* this obsolete -- please use the sysfs-based interface below for new code */ -/* returns card type + card ID (for bt878-based ones) - for possible values see lines below beginning with #define BTTV_BOARD_UNKNOWN - returns negative value if error occurred -*/ -extern int bttv_get_cardinfo(unsigned int card, int *type, - unsigned int *cardid); extern struct pci_dev* bttv_get_pcidev(unsigned int card); -/* obsolete, use bttv_get_cardinfo instead */ -extern int bttv_get_id(unsigned int card); - /* sets GPOE register (BT848_GPIO_OUT_EN) to new value: data | (current_GPOE_value & ~mask) returns negative value if error occurred @@ -290,20 +283,6 @@ extern int bttv_read_gpio(unsigned int c extern int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data); -/* returns pointer to task queue which can be used as parameter to - interruptible_sleep_on - in interrupt handler if BT848_INT_GPINT bit is set - this queue is activated - (wake_up_interruptible) and following call to the function bttv_read_gpio - should return new value of GPDATA, - returns NULL value if error occurred or queue is not available - WARNING: because there is no buffer for GPIO data, one MUST - process data ASAP -*/ -extern wait_queue_head_t* bttv_get_gpio_queue(unsigned int card); - -/* call i2c clients -*/ -extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg); diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index ad79b8d..8f44f02 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -434,6 +434,9 @@ #endif unsigned int users; struct bttv_fh init; + /* used to make dvb-bt8xx autoloadable */ + struct work_struct request_module_wk; + /* Default (0) and current (1) video capturing and overlay cropping parameters in bttv_tvnorm.cropcap units. Protected by bttv.lock. */ diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 710c11a..96254db 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -4,6 +4,7 @@ * sensor. * * Copyright 2006 One Laptop Per Child Association, Inc. + * Copyright 2006-7 Jonathan Corbet * * Written by Jonathan Corbet, corbet@lwn.net. * @@ -22,6 +23,7 @@ #include #include #include #include +#include #include #include #include @@ -36,7 +38,7 @@ #include #include "cafe_ccic-regs.h" -#define CAFE_VERSION 0x000001 +#define CAFE_VERSION 0x000002 /* @@ -164,7 +166,7 @@ struct cafe_camera struct tasklet_struct s_tasklet; /* Current operating parameters */ - enum v4l2_chip_ident sensor_type; /* Currently ov7670 only */ + u32 sensor_type; /* Currently ov7670 only */ struct v4l2_pix_format pix_format; /* Locks */ @@ -704,7 +706,13 @@ static void cafe_ctlr_init(struct cafe_c cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */ cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC); cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS); + /* + * Here we must wait a bit for the controller to come around. + */ + spin_unlock_irqrestore(&cam->dev_lock, flags); mdelay(5); /* FIXME revisit this */ + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC); cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN); /* @@ -772,9 +780,9 @@ static void cafe_ctlr_power_up(struct ca * Control 1 is power down, set to 0 to operate. */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ - mdelay(1); /* Marvell says 1ms will do it */ +// mdelay(1); /* Marvell says 1ms will do it */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); - mdelay(1); /* Enough? */ +// mdelay(1); /* Enough? */ spin_unlock_irqrestore(&cam->dev_lock, flags); } @@ -818,6 +826,7 @@ static int __cafe_cam_reset(struct cafe_ */ static int cafe_cam_init(struct cafe_camera *cam) { + struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR, 0, 0, 0 }; int ret; mutex_lock(&cam->s_mutex); @@ -827,9 +836,11 @@ static int cafe_cam_init(struct cafe_cam ret = __cafe_cam_reset(cam); if (ret) goto out; - ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type); + chip.match_chip = cam->sensor->addr; + ret = __cafe_cam_cmd(cam, VIDIOC_G_CHIP_IDENT, &chip); if (ret) goto out; + cam->sensor_type = chip.ident; // if (cam->sensor->addr != OV7xx0_SID) { if (cam->sensor_type != V4L2_IDENT_OV7670) { cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr); @@ -1792,18 +1803,19 @@ static void cafe_frame_tasklet(unsigned if (list_empty(&cam->sb_avail)) break; /* Leave it valid, hope for better later */ clear_bit(bufno, &cam->flags); - /* - * We could perhaps drop the spinlock during this - * big copy. Something to consider. - */ sbuf = list_entry(cam->sb_avail.next, struct cafe_sio_buffer, list); + /* + * Drop the lock during the big copy. This *should* be safe... + */ + spin_unlock_irqrestore(&cam->dev_lock, flags); memcpy(sbuf->buffer, cam->dma_bufs[bufno], cam->pix_format.sizeimage); sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage; sbuf->v4lbuf.sequence = cam->buf_seq[bufno]; sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + spin_lock_irqsave(&cam->dev_lock, flags); list_move_tail(&sbuf->list, &cam->sb_full); } if (! list_empty(&cam->sb_full)) @@ -2107,6 +2119,7 @@ static int cafe_pci_probe(struct pci_dev cam->v4ldev = cafe_v4l_template; cam->v4ldev.debug = 0; // cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; + cam->v4ldev.dev = &pdev->dev; ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); if (ret) goto out_smbus; @@ -2176,10 +2189,52 @@ static void cafe_pci_remove(struct pci_d } +#ifdef CONFIG_PM +/* + * Basic power management. + */ +static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct cafe_camera *cam = cafe_find_by_pdev(pdev); + int ret; + + ret = pci_save_state(pdev); + if (ret) + return ret; + cafe_ctlr_stop_dma(cam); + cafe_ctlr_power_down(cam); + pci_disable_device(pdev); + return 0; +} + + +static int cafe_pci_resume(struct pci_dev *pdev) +{ + struct cafe_camera *cam = cafe_find_by_pdev(pdev); + int ret = 0; + + ret = pci_restore_state(pdev); + if (ret) + return ret; + ret = pci_enable_device(pdev); + if (ret) { + cam_warn(cam, "Unable to re-enable device on resume!\n"); + return ret; + } + cafe_ctlr_init(cam); + cafe_ctlr_power_up(cam); + set_bit(CF_CONFIG_NEEDED, &cam->flags); + if (cam->state == S_SPECREAD) + cam->state = S_IDLE; /* Don't bother restarting */ + else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING) + ret = cafe_read_setup(cam, cam->state); + return ret; +} + +#endif /* CONFIG_PM */ static struct pci_device_id cafe_ids[] = { - { PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */ { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */ { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */ { 0, } @@ -2192,6 +2247,10 @@ static struct pci_driver cafe_pci_driver .id_table = cafe_ids, .probe = cafe_pci_probe, .remove = cafe_pci_remove, +#ifdef CONFIG_PM + .suspend = cafe_pci_suspend, + .resume = cafe_pci_resume, +#endif }; diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c index b12cec9..19711aa 100644 --- a/drivers/media/video/cpia_pp.c +++ b/drivers/media/video/cpia_pp.c @@ -62,7 +62,6 @@ #define PPCPIA_PARPORT_AUTO -3 #define PPCPIA_PARPORT_OFF -2 #define PPCPIA_PARPORT_NONE -1 -#ifdef MODULE static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; static char *parport[PARPORT_MAX] = {NULL,}; @@ -72,11 +71,6 @@ MODULE_LICENSE("GPL"); module_param_array(parport, charp, NULL, 0); MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); -#else -static int parport_nr[PARPORT_MAX] __initdata = - {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; -static int parport_ptr = 0; -#endif struct pp_cam_entry { struct pardevice *pdev; @@ -141,7 +135,6 @@ static void cpia_pp_run_callback(struct cam = container_of(work, struct pp_cam_entry, cb_task); cb_func = cam->cb_func; cb_data = cam->cb_data; - work_release(work); cb_func(cb_data); } @@ -682,7 +675,7 @@ static int cpia_pp_registerCallback(void if(cam->port->irq != PARPORT_IRQ_NONE) { cam->cb_func = cb; cam->cb_data = cbdata; - INIT_WORK_NAR(&cam->cb_task, cpia_pp_run_callback); + INIT_WORK(&cam->cb_task, cpia_pp_run_callback); } else { retval = -1; } @@ -820,7 +813,7 @@ static struct parport_driver cpia_pp_dri .detach = cpia_pp_detach, }; -static int cpia_pp_init(void) +static int __init cpia_pp_init(void) { printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); @@ -839,8 +832,7 @@ static int cpia_pp_init(void) return 0; } -#ifdef MODULE -int init_module(void) +static int __init cpia_init(void) { if (parport[0]) { /* The user gave some parameters. Let's see what they were. */ @@ -867,38 +859,11 @@ int init_module(void) return cpia_pp_init(); } -void cleanup_module(void) +static void __exit cpia_cleanup(void) { - parport_unregister_driver (&cpia_pp_driver); + parport_unregister_driver(&cpia_pp_driver); return; } -#else /* !MODULE */ - -static int __init cpia_pp_setup(char *str) -{ - int err; - - if (!strncmp(str, "parport", 7)) { - int n = simple_strtoul(str + 7, NULL, 10); - if (parport_ptr < PARPORT_MAX) { - parport_nr[parport_ptr++] = n; - } else { - LOG("too many ports, %s ignored.\n", str); - } - } else if (!strcmp(str, "auto")) { - parport_nr[0] = PPCPIA_PARPORT_AUTO; - } else if (!strcmp(str, "none")) { - parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; - } - - err=cpia_pp_init(); - if (err) - return err; - - return 1; -} - -__setup("cpia_pp=", cpia_pp_setup); - -#endif /* !MODULE */ +module_init(cpia_init); +module_exit(cpia_cleanup); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index de87247..a73e285 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -28,6 +28,7 @@ #include #include #include #include +#include MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); MODULE_AUTHOR("Martin Vaughan"); @@ -103,6 +104,9 @@ static int cs53l32a_command(struct i2c_c cs53l32a_write(client, 0x05, (u8) ctrl->value); break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CS53l32A, 0); + case VIDIOC_LOG_STATUS: { u8 v = cs53l32a_read(client, 0x01); diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index d60cd5e..88dbddd 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -51,6 +51,7 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, V4L2_CID_MPEG_AUDIO_EMPHASIS, V4L2_CID_MPEG_AUDIO_CRC, + V4L2_CID_MPEG_AUDIO_MUTE, V4L2_CID_MPEG_VIDEO_ENCODING, V4L2_CID_MPEG_VIDEO_ASPECT, V4L2_CID_MPEG_VIDEO_B_FRAMES, @@ -60,6 +61,8 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_VIDEO_BITRATE, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, + V4L2_CID_MPEG_VIDEO_MUTE, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, @@ -71,6 +74,7 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, 0 }; @@ -102,6 +106,9 @@ static int cx2341x_get_ctrl(struct cx234 case V4L2_CID_MPEG_AUDIO_CRC: ctrl->value = params->audio_crc; break; + case V4L2_CID_MPEG_AUDIO_MUTE: + ctrl->value = params->audio_mute; + break; case V4L2_CID_MPEG_VIDEO_ENCODING: ctrl->value = params->video_encoding; break; @@ -129,6 +136,12 @@ static int cx2341x_get_ctrl(struct cx234 case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: ctrl->value = params->video_temporal_decimation; break; + case V4L2_CID_MPEG_VIDEO_MUTE: + ctrl->value = params->video_mute; + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + ctrl->value = params->video_mute_yuv; + break; case V4L2_CID_MPEG_STREAM_TYPE: ctrl->value = params->stream_type; break; @@ -168,6 +181,9 @@ static int cx2341x_get_ctrl(struct cx234 case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: ctrl->value = params->video_chroma_median_filter_bottom; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + ctrl->value = params->stream_insert_nav_packets; + break; default: return -EINVAL; } @@ -201,6 +217,9 @@ static int cx2341x_set_ctrl(struct cx234 case V4L2_CID_MPEG_AUDIO_CRC: params->audio_crc = ctrl->value; break; + case V4L2_CID_MPEG_AUDIO_MUTE: + params->audio_mute = ctrl->value; + break; case V4L2_CID_MPEG_VIDEO_ASPECT: params->video_aspect = ctrl->value; break; @@ -243,6 +262,12 @@ static int cx2341x_set_ctrl(struct cx234 case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: params->video_temporal_decimation = ctrl->value; break; + case V4L2_CID_MPEG_VIDEO_MUTE: + params->video_mute = (ctrl->value != 0); + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + params->video_mute_yuv = ctrl->value; + break; case V4L2_CID_MPEG_STREAM_TYPE: params->stream_type = ctrl->value; params->video_encoding = @@ -290,6 +315,9 @@ static int cx2341x_set_ctrl(struct cx234 case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: params->video_chroma_median_filter_bottom = ctrl->value; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + params->stream_insert_nav_packets = ctrl->value; + break; default: return -EINVAL; } @@ -336,6 +364,9 @@ static int cx2341x_ctrl_query_fill(struc case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: name = "Median Chroma Filter Minimum"; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + name = "Insert Navigation Packets"; + break; default: return v4l2_ctrl_query_fill(qctrl, min, max, step, def); @@ -350,6 +381,12 @@ static int cx2341x_ctrl_query_fill(struc min = 0; step = 1; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; + min = 0; + max = 1; + step = 1; + break; default: qctrl->type = V4L2_CTRL_TYPE_INTEGER; break; @@ -505,6 +542,9 @@ int cx2341x_ctrl_query(struct cx2341x_mp qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return 0; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, 0); + default: return v4l2_ctrl_query_fill_std(qctrl); @@ -656,6 +696,7 @@ void cx2341x_fill_defaults(struct cx2341 /* stream */ .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, + .stream_insert_nav_packets = 0, /* audio */ .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, @@ -665,6 +706,7 @@ void cx2341x_fill_defaults(struct cx2341 .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, + .audio_mute = 0, /* video */ .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, @@ -676,6 +718,8 @@ void cx2341x_fill_defaults(struct cx2341 .video_bitrate = 6000000, .video_bitrate_peak = 8000000, .video_temporal_decimation = 0, + .video_mute = 0, + .video_mute_yuv = 0x008080, /* YCbCr value for black */ /* encoding filters */ .video_spatial_filter_mode = V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, @@ -779,6 +823,10 @@ int cx2341x_update(void *priv, cx2341x_m err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties); if (err) return err; } + if (old == NULL || old->audio_mute != new->audio_mute) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, new->audio_mute); + if (err) return err; + } if (old == NULL || old->video_bitrate_mode != new->video_bitrate_mode || old->video_bitrate != new->video_bitrate || old->video_bitrate_peak != new->video_bitrate_peak) { @@ -826,6 +874,15 @@ int cx2341x_update(void *priv, cx2341x_m new->video_temporal_decimation); if (err) return err; } + if (old == NULL || old->video_mute != new->video_mute || + (new->video_mute && old->video_mute_yuv != new->video_mute_yuv)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, new->video_mute | (new->video_mute_yuv << 8)); + if (err) return err; + } + if (old == NULL || old->stream_insert_nav_packets != new->stream_insert_nav_packets) { + err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, 7, new->stream_insert_nav_packets); + if (err) return err; + } return 0; } @@ -854,18 +911,22 @@ void cx2341x_log_status(struct cx2341x_m int temporal = p->video_temporal_filter; /* Stream */ - printk(KERN_INFO "%s: Stream: %s\n", + printk(KERN_INFO "%s: Stream: %s", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); + if (p->stream_insert_nav_packets) + printk(" (with navigation packets)"); + printk("\n"); printk(KERN_INFO "%s: VBI Format: %s\n", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); /* Video */ - printk(KERN_INFO "%s: Video: %dx%d, %d fps\n", + printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", prefix, p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), - p->is_50hz ? 25 : 30); + p->is_50hz ? 25 : 30, + (p->video_mute) ? " (muted)" : ""); printk(KERN_INFO "%s: Video: %s, %s, %s, %d", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), @@ -886,12 +947,13 @@ void cx2341x_log_status(struct cx2341x_m } /* Audio */ - printk(KERN_INFO "%s: Audio: %s, %s, %s, %s", + printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE)); + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), + p->audio_mute ? " (muted)" : ""); if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) { printk(", %s", cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 774d253..1757a58 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -35,6 +35,7 @@ #include #include #include #include +#include #include #include "cx25840-core.h" @@ -827,9 +828,8 @@ #endif cx25840_initialize(client, 0); break; - case VIDIOC_INT_G_CHIP_IDENT: - *(enum v4l2_chip_ident *)arg = state->id; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev); default: return -EINVAL; @@ -847,7 +847,7 @@ static int cx25840_detect_client(struct { struct i2c_client *client; struct cx25840_state *state; - enum v4l2_chip_ident id; + u32 id; u16 device_id; /* Check if the adapter supports the needed features @@ -902,6 +902,7 @@ static int cx25840_detect_client(struct state->audmode = V4L2_TUNER_MODE_LANG1; state->vbi_line_offset = 8; state->id = id; + state->rev = device_id; i2c_attach_client(client); diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 2804906..f4b56d2 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -43,7 +43,8 @@ struct cx25840_state { u32 audclk_freq; int audmode; int vbi_line_offset; - enum v4l2_chip_ident id; + u32 id; + u32 rev; int is_cx25836; }; diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 0e86b9d..e852024 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index b2a66ba..0f9d969 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -53,7 +53,6 @@ config VIDEO_CX88_DVB select DVB_OR51132 if !DVB_FE_CUSTOMISE select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index e4355fd..3956c25 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -232,7 +232,8 @@ static void cx8801_aud_irq(snd_cx88_card cx_write(MO_AUD_INTSTAT, status); if (debug > 1 || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq aud", - cx88_aud_irqs, status, mask); + cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs), + status, mask); /* risc op code error */ if (status & (1 << 16)) { printk(KERN_WARNING "%s/0: audio risc op code error\n",core->name); @@ -413,11 +414,9 @@ static int snd_cx88_hw_params(struct snd dprintk(1,"Setting buffer\n"); - buf = kmalloc(sizeof(*buf),GFP_KERNEL); + buf = kzalloc(sizeof(*buf),GFP_KERNEL); if (NULL == buf) return -ENOMEM; - memset(buf,0,sizeof(*buf)); - buf->vb.memory = V4L2_MEMORY_MMAP; buf->vb.width = chip->period_size; @@ -682,7 +681,7 @@ static int __devinit snd_cx88_create(str return err; } - if (!pci_dma_supported(pci,0xffffffff)) { + if (!pci_dma_supported(pci,DMA_32BIT_MASK)) { dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; cx88_core_put(core,pci); diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 65e9d80..e61102d 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -885,6 +885,12 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_DVB, .vmux = 0, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, }}, .mpeg = CX88_MPEG_DVB, }, @@ -1537,10 +1543,10 @@ struct cx88_subid cx88_subids[] = { },{ .subvendor = 0x17de, .subdevice = 0x0840, - .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, - },{ - .subvendor = 0x1421, - .subdevice = 0x0305, + .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, + },{ + .subvendor = 0x1421, + .subdevice = 0x0305, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, },{ .subvendor = 0x18ac, @@ -1631,6 +1637,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x0070, .subdevice = 0x1402, .card = CX88_BOARD_HAUPPAUGE_HVR3000, + },{ + .subvendor = 0x1421, + .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ + .card = CX88_BOARD_KWORLD_DVBS_100, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); @@ -1786,7 +1796,7 @@ static void dvico_fusionhdtv_hybrid_init { 0x03, 0x0C }, }; - for (i = 0; i < 13; i++) { + for (i = 0; i < ARRAY_SIZE(init_bufs); i++) { msg.buf = init_bufs[i]; msg.len = (i != 12 ? 5 : 2); err = i2c_transfer(&core->i2c_adap, &msg, 1); @@ -1913,12 +1923,21 @@ void cx88_card_setup(struct cx88_core *c if (0 == core->i2c_rc) { /* enable tuner */ int i; - static const u8 buffer [] = { 0x10,0x12,0x13,0x04,0x16,0x00,0x14,0x04,0x017,0x00 }; + static const u8 buffer [][2] = { + {0x10,0x12}, + {0x13,0x04}, + {0x16,0x00}, + {0x14,0x04}, + {0x17,0x00} + }; core->i2c_client.addr = 0x0a; - for (i = 0; i < 5; i++) - if (2 != i2c_master_send(&core->i2c_client,&buffer[i*2],2)) - printk(KERN_WARNING "%s: Unable to enable tuner(%i).\n", + for (i = 0; i < ARRAY_SIZE(buffer); i++) + if (2 != i2c_master_send(&core->i2c_client, + buffer[i],2)) + printk(KERN_WARNING + "%s: Unable to enable " + "tuner(%i).\n", core->name, i); } break; diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index d86813b..f31ec96 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -489,12 +489,12 @@ static char *cx88_pci_irqs[32] = { }; void cx88_print_irqbits(char *name, char *tag, char **strings, - u32 bits, u32 mask) + int len, u32 bits, u32 mask) { unsigned int i; printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits); - for (i = 0; i < 32; i++) { + for (i = 0; i < len; i++) { if (!(bits & (1 << i))) continue; if (strings[i]) @@ -520,8 +520,8 @@ int cx88_core_irq(struct cx88_core *core } if (!handled) cx88_print_irqbits(core->name, "irq pci", - cx88_pci_irqs, status, - core->pci_irqmask); + cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs), + status, core->pci_irqmask); return handled; } diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 4f55602..dbfe4dc 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -42,7 +42,6 @@ #include "zl10353.h" #include "cx22702.h" #include "or51132.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" @@ -476,6 +475,8 @@ static int dvb_register(struct cx8802_de case CX88_BOARD_WINFAST_DTV2000H: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: + case CX88_BOARD_HAUPPAUGE_HVR1300: + case CX88_BOARD_HAUPPAUGE_HVR3000: dev->dvb.frontend = dvb_attach(cx22702_attach, &hauppauge_hvr_config, &dev->core->i2c_adap); @@ -631,8 +632,9 @@ #endif &fusionhdtv_5_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(lgh06xf_attach, dev->dvb.frontend, - &dev->core->i2c_adap); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_lg_tdvs_h06xf); } } break; @@ -650,8 +652,9 @@ #endif &pchdtv_hd5500, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(lgh06xf_attach, dev->dvb.frontend, - &dev->core->i2c_adap); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_lg_tdvs_h06xf); } } break; @@ -692,24 +695,6 @@ #endif dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; - case CX88_BOARD_HAUPPAUGE_HVR1300: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, &dvb_pll_fmd1216me); - } - break; - case CX88_BOARD_HAUPPAUGE_HVR3000: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, &dvb_pll_fmd1216me); - } - break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 9830d5c..7919a1f 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -1,3 +1,4 @@ + /* cx88-i2c.c -- all the i2c code is here @@ -195,7 +196,7 @@ static void do_i2c_scan(char *name, stru unsigned char buf; int i,rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c,&buf,0); if (rc < 0) diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 1fe1a83..b2eb32e 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -49,6 +49,27 @@ #define dprintk(level,fmt, arg...) if (d #define mpeg_dbg(level,fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg) +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk); + + if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_DVB) + request_module("cx88-dvb"); + if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_BLACKBIRD) + request_module("cx88-blackbird"); +} + +static void request_modules(struct cx8802_dev *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + + static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ @@ -345,7 +366,8 @@ static void cx8802_mpeg_irq(struct cx880 if (debug || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq mpeg ", - cx88_mpeg_irqs, status, mask); + cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs), + status, mask); /* risc op code error */ if (status & (1 << 16)) { @@ -427,7 +449,7 @@ int cx8802_init_common(struct cx8802_dev if (pci_enable_device(dev->pci)) return -EIO; pci_set_master(dev->pci); - if (!pci_dma_supported(dev->pci,0xffffffff)) { + if (!pci_dma_supported(dev->pci,DMA_32BIT_MASK)) { printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); return -EIO; } @@ -778,6 +800,9 @@ static int __devinit cx8802_probe(struct /* Maintain a reference so cx88-video can query the 8802 device. */ core->dvbdev = dev; + + /* now autoload cx88-dvb or cx88-blackbird */ + request_modules(dev); return 0; fail_free: diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index bdfe2af..fbce1d5 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1555,7 +1555,8 @@ static void cx8800_vid_irq(struct cx8800 cx_write(MO_VID_INTSTAT, status); if (irq_debug || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq vid", - cx88_vid_irqs, status, mask); + cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs), + status, mask); /* risc op code error */ if (status & (1 << 16)) { @@ -1778,7 +1779,7 @@ static int __devinit cx8800_initdev(stru dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev,0xffffffff)) { + if (!pci_dma_supported(pci_dev,DMA_32BIT_MASK)) { printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; goto fail_core; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index a4f7bef..738d4f2 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -481,6 +481,8 @@ #endif /* List of attached drivers */ struct cx8802_driver drvlist; + struct work_struct request_module_wk; + }; /* ----------------------------------------------------------- */ @@ -510,7 +512,7 @@ #define cx_sandor(sreg,reg,mask,value) \ /* cx88-core.c */ extern void cx88_print_irqbits(char *name, char *tag, char **strings, - u32 bits, u32 mask); + int len, u32 bits, u32 mask); extern int cx88_core_irq(struct cx88_core *core, u32 status); extern void cx88_wakeup(struct cx88_core *core, diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index d829d8f..563a831 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -523,7 +523,7 @@ static void do_i2c_scan(char *name, stru unsigned char buf; int i, rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c, &buf, 0); if (rc < 0) diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 210582d..ed92b6f 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -173,7 +173,7 @@ static int get_key_pinnacle(struct IR_i2 return -EIO; } - for (start = 0; start<4; start++) { + for (start = 0; start < ARRAY_SIZE(b); start++) { if (b[start] == marker) { code=b[(start+parity_offset+1)%4]; parity=b[(start+parity_offset)%4]; diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig new file mode 100644 index 0000000..e854f3f --- /dev/null +++ b/drivers/media/video/ivtv/Kconfig @@ -0,0 +1,26 @@ +config VIDEO_IVTV + tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" + depends on VIDEO_V4L1 && VIDEO_V4L2 && USB && I2C && EXPERIMENTAL + select FW_LOADER + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_CX2341X + select VIDEO_CX25840 + select VIDEO_MSP3400 + select VIDEO_SAA711X + select VIDEO_SAA7127 + select VIDEO_TVAUDIO + select VIDEO_CS53L32A + select VIDEO_WM8775 + select VIDEO_WM8739 + select VIDEO_UPD64031A + select VIDEO_UPD64083 + ---help--- + This is a video4linux driver for Conexant cx23416 or cx23416 based + PCI personal video recorder devices. + + This is used in devices such as the Hauppauge PVR-150/250/350/500 + cards. + + To compile this driver as a module, choose M here: the + module will be called ivtv. diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile new file mode 100644 index 0000000..7e95148 --- /dev/null +++ b/drivers/media/video/ivtv/Makefile @@ -0,0 +1,7 @@ +ivtv-objs := ivtv-audio.o ivtv-cards.o ivtv-controls.o \ + ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \ + ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \ + ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \ + ivtv-vbi.o ivtv-video.o ivtv-yuv.o + +obj-$(CONFIG_VIDEO_IVTV) += ivtv.o diff --git a/drivers/media/video/ivtv/ivtv-audio.c b/drivers/media/video/ivtv/ivtv-audio.c new file mode 100644 index 0000000..d702b8b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-audio.c @@ -0,0 +1,74 @@ +/* + Audio-related ivtv functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" +#include "ivtv-i2c.h" +#include "ivtv-gpio.h" +#include "ivtv-cards.h" +#include "ivtv-audio.h" +#include +#include + +/* Selects the audio input and output according to the current + settings. */ +int ivtv_audio_set_io(struct ivtv *itv) +{ + struct v4l2_routing route; + u32 audio_input; + int mux_input; + + /* Determine which input to use */ + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { + audio_input = itv->card->radio_input.audio_input; + mux_input = itv->card->radio_input.muxer_input; + } else { + audio_input = itv->card->audio_inputs[itv->audio_input].audio_input; + mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input; + } + + /* handle muxer chips */ + route.input = mux_input; + route.output = 0; + ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + + route.input = audio_input; + if (itv->card->hw_audio & IVTV_HW_MSP34XX) { + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); + } + return ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route); +} + +void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route) +{ + ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); +} + +void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq) +{ + static u32 freqs[3] = { 44100, 48000, 32000 }; + + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (freq > 2) + return; + ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); +} + diff --git a/drivers/media/video/ivtv/ivtv-audio.h b/drivers/media/video/ivtv/ivtv-audio.h new file mode 100644 index 0000000..9c42846 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-audio.h @@ -0,0 +1,23 @@ +/* + Audio-related ivtv functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_audio_set_io(struct ivtv *itv); +void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route); +void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq); diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c new file mode 100644 index 0000000..8eab020 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -0,0 +1,964 @@ +/* + Functions to query card hardware + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-i2c.h" + +#include +#include +#include +#include +#include + +#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ + MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER) +#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) + +/********************** card configuration *******************************/ + +/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii + This keeps the PCI ID database up to date. Note that the entries + must be added under vendor 0x4444 (Conexant) as subsystem IDs. + New vendor IDs should still be added to the vendor ID list. */ + +/* Hauppauge PVR-250 cards */ + +/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */ +static const struct ivtv_card ivtv_card_pvr250 = { + .type = IVTV_CARD_PVR_250, + .name = "Hauppauge WinTV PVR-250", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* Hauppauge PVR-350 cards */ + +/* Outputs for Hauppauge PVR350 cards */ +static struct ivtv_card_output ivtv_pvr350_outputs[] = { + { + .name = "S-Video + Composite", + .video_output = 0, + }, { + .name = "Composite", + .video_output = 1, + }, { + .name = "S-Video", + .video_output = 2, + }, { + .name = "RGB", + .video_output = 3, + }, { + .name = "YUV C", + .video_output = 4, + }, { + .name = "YUV V", + .video_output = 5, + } +}; + +static const struct ivtv_card ivtv_card_pvr350 = { + .type = IVTV_CARD_PVR_350, + .name = "Hauppauge WinTV PVR-350", + .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, + .video_outputs = ivtv_pvr350_outputs, + .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* PVR-350 V1 boards have a different audio tuner input and use a + saa7114 instead of a saa7115. + Note that the info below comes from a pre-production model so it may + not be correct. Especially the audio behaves strangely (mono only it seems) */ +static const struct ivtv_card ivtv_card_pvr350_v1 = { + .type = IVTV_CARD_PVR_350_V1, + .name = "Hauppauge WinTV PVR-350 (V1)", + .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, + .video_outputs = ivtv_pvr350_outputs, + .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 | + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_MONO }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* Hauppauge PVR-150/PVR-500 cards */ + +static const struct ivtv_card ivtv_card_pvr150 = { + .type = IVTV_CARD_PVR_150, + .name = "Hauppauge WinTV PVR-150", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_WM8775, + .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 | + IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, CX25840_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, + CX25840_AUDIO8, WM8775_AIN2 }, + { IVTV_CARD_INPUT_LINE_IN1, + CX25840_AUDIO_SERIAL, WM8775_AIN2 }, + { IVTV_CARD_INPUT_LINE_IN2, + CX25840_AUDIO_SERIAL, WM8775_AIN3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, + CX25840_AUDIO_SERIAL, WM8775_AIN4 }, + /* apparently needed for the IR blaster */ + .gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* AVerMedia M179 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_m179[] = { + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf }, + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_m179 = { + .type = IVTV_CARD_M179, + .name = "AVerMedia M179", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xe380, .initial_value = 0x8290 }, + .gpio_audio_input = { .mask = 0x8040, .tuner = 0x8000, .linein = 0x0000 }, + .gpio_audio_mute = { .mask = 0x2000, .mute = 0x2000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0200, .lang2 = 0x0100, .both = 0x0000 }, + .gpio_audio_freq = { .mask = 0x0018, .f32000 = 0x0000, + .f44100 = 0x0008, .f48000 = 0x0010 }, + .gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 }, + .tuners = { + /* As far as we know all M179 cards use this tuner */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC }, + }, + .pci_list = ivtv_pci_m179, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_mpg600 = { + .type = IVTV_CARD_MPG600, + .name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x3080, .initial_value = 0x0004 }, + .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, + .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, + .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, + .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, + .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, + .tuners = { + /* The PAL tuner is confirmed */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_mpg600, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = { + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 }, + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_mpg160 = { + .type = IVTV_CARD_MPG160, + .name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x7080, .initial_value = 0x400c }, + .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, + .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, + .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, + .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, + .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_mpg160, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan PG600/Diamond PVR-550 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_pg600[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_pg600 = { + .type = IVTV_CARD_PG600, + .name = "Yuan PG600, Diamond PVR-550", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_pg600, +}; + +/* ------------------------------------------------------------------------- */ + +/* Adaptec VideOh! AVC-2410 card */ + +static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_avc2410 = { + .type = IVTV_CARD_AVC2410, + .name = "Adaptec VideOh! AVC-2410", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_muxer = IVTV_HW_CS53L32A, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A | + IVTV_HW_SAA7115 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, + MSP_TUNER, CS53L32A_IN0 }, + { IVTV_CARD_INPUT_LINE_IN1, + MSP_SCART1, CS53L32A_IN2 }, + }, + /* This card has no eeprom and in fact the Windows driver relies + on the country/region setting of the user to decide which tuner + is available. */ + .tuners = { + /* This tuner has been verified for the AVC2410 */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + /* This is a good guess, but I'm not totally sure this is + the correct tuner for NTSC. */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_avc2410, +}; + +/* ------------------------------------------------------------------------- */ + +/* Adaptec VideOh! AVC-2010 card */ + +static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_avc2010 = { + .type = IVTV_CARD_AVC2010, + .name = "Adaptec VideOh! AVC-2010", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_CS53L32A, + .hw_audio_ctrl = IVTV_HW_CS53L32A, + .hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115, + .video_inputs = { + { IVTV_CARD_INPUT_SVIDEO1, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_LINE_IN1, CS53L32A_IN2 }, + }, + /* Does not have a tuner */ + .pci_list = ivtv_pci_avc2010, +}; + +/* ------------------------------------------------------------------------- */ + +/* Nagase Transgear 5000TV card */ + +static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_tg5000tv = { + .type = IVTV_CARD_TG5000TV, + .name = "Nagase Transgear 5000TV", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | + IVTV_HW_GPIO, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gr_config = UPD64031A_VERTICAL_EXTERNAL, + .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, + .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, + .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, + .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, + .composite = 0x0010, .svideo = 0x0020 }, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_tg5000tv, +}; + +/* ------------------------------------------------------------------------- */ + +/* AOpen VA2000MAX-SNT6 card */ + +static const struct ivtv_card_pci_info ivtv_pci_va2000[] = { + { PCI_DEVICE_ID_IVTV16, 0, 0xff5f }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_va2000 = { + .type = IVTV_CARD_VA2000MAX_SNT6, + .name = "AOpen VA2000MAX-SNT6", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_UPD6408X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + }, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_va2000, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_cx23416gyc = { + .type = IVTV_CARD_CX23416GYC, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO3 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gr_config = UPD64031A_VERTICAL_EXTERNAL, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_cx23416gyc, +}; + +static const struct ivtv_card ivtv_card_cx23416gyc_nogr = { + .type = IVTV_CARD_CX23416GYC_NOGR, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | + IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, +}; + +static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = { + .type = IVTV_CARD_CX23416GYC_NOGRYCS, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, +}; + +/* ------------------------------------------------------------------------- */ + +/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */ + +static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */ + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gv_mvprx = { + .type = IVTV_CARD_GV_MVPRX, + .name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_WM8739, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO | + IVTV_HW_TUNER | IVTV_HW_WM8739 | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, + .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, + .tuners = { + /* This card has the Panasonic VP27 tuner */ + { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + }, + .pci_list = ivtv_pci_gv_mvprx, +}; + +/* ------------------------------------------------------------------------- */ + +/* I/O Data GV-MVP/RX2E card */ + +static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 }, + {0, 0, 0} +}; + +static const struct ivtv_card ivtv_card_gv_mvprx2e = { + .type = IVTV_CARD_GV_MVPRX2E, + .name = "I/O Data GV-MVP/RX2E", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_WM8739, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | + IVTV_HW_TVAUDIO | IVTV_HW_WM8739, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, + .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, + .tuners = { + /* This card has the Panasonic VP27 tuner */ + { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + }, + .pci_list = ivtv_pci_gv_mvprx2e, +}; + +/* ------------------------------------------------------------------------- */ + +/* GotVIEW PCI DVD card */ + +static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gotview_pci_dvd = { + .type = IVTV_CARD_GOTVIEW_PCI_DVD, + .name = "GotView PCI DVD", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, /* pin 116 */ + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, /* pin 114/109 */ + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, /* pin 118 */ + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN0 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN2 }, + }, + .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 }, + .tuners = { + /* This card has a Philips FQ1216ME MK3 tuner */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + }, + .pci_list = ivtv_pci_gotview_pci_dvd, +}; + +/* ------------------------------------------------------------------------- */ + +/* GotVIEW PCI DVD2 Deluxe card */ + +static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = { + .type = IVTV_CARD_GOTVIEW_PCI_DVD2, + .name = "GotView PCI DVD2 Deluxe", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_GPIO, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, + .gpio_init = { .direction = 0x0800, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, + .tuners = { + /* This card has a Philips FQ1216ME MK5 tuner */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + }, + .pci_list = ivtv_pci_gotview_pci_dvd2, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPC622 miniPCI card */ + +static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_yuan_mpc622 = { + .type = IVTV_CARD_YUAN_MPC622, + .name = "Yuan MPC622", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 }, + .tuners = { + /* This card has the TDA8290/TDA8275 tuner chips */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 }, + }, + .pci_list = ivtv_pci_yuan_mpc622, +}; + +/* ------------------------------------------------------------------------- */ + +/* DIGITAL COWBOY DCT-MTVP1 card */ + +static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_dctmvtvp1 = { + .type = IVTV_CARD_DCTMTVP1, + .name = "Digital Cowboy DCT-MTVP1", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | + IVTV_HW_GPIO, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, + .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, + .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, + .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, + .composite = 0x0010, .svideo = 0x0020}, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_dctmvtvp1, +}; + +/* ------------------------------------------------------------------------- */ + +#ifdef HAVE_XC3028 + +/* Yuan PG600-2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_pg600v2 = { + .type = IVTV_CARD_PG600V2, + .name = "Yuan PG600-2, GotView PCI DVD Lite, Club3D ZAP-TV1x01", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 }, + }, + .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */ + .pci_list = ivtv_pci_pg600v2, +}; +#endif + +static const struct ivtv_card *ivtv_card_list[] = { + &ivtv_card_pvr250, + &ivtv_card_pvr350, + &ivtv_card_pvr150, + &ivtv_card_m179, + &ivtv_card_mpg600, + &ivtv_card_mpg160, + &ivtv_card_pg600, + &ivtv_card_avc2410, + &ivtv_card_avc2010, + &ivtv_card_tg5000tv, + &ivtv_card_va2000, + &ivtv_card_cx23416gyc, + &ivtv_card_gv_mvprx, + &ivtv_card_gv_mvprx2e, + &ivtv_card_gotview_pci_dvd, + &ivtv_card_gotview_pci_dvd2, + &ivtv_card_yuan_mpc622, + &ivtv_card_dctmvtvp1, +#ifdef HAVE_XC3028 + &ivtv_card_pg600v2, +#endif + + /* Variations of standard cards but with the same PCI IDs. + These cards must come last in this list. */ + &ivtv_card_pvr350_v1, + &ivtv_card_cx23416gyc_nogr, + &ivtv_card_cx23416gyc_nogrycs, +}; + +const struct ivtv_card *ivtv_get_card(u16 index) +{ + if (index >= ARRAY_SIZE(ivtv_card_list)) + return NULL; + return ivtv_card_list[index]; +} + +int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input) +{ + const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "S-Video 1", + "S-Video 2", + "Composite 1", + "Composite 2", + "Composite 3" + }; + + memset(input, 0, sizeof(*input)); + if (index >= itv->nof_inputs) + return -EINVAL; + input->index = index; + strcpy(input->name, input_strs[card_input->video_type - 1]); + input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ? + V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); + input->audioset = (1 << itv->nof_audio_inputs) - 1; + input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ? + itv->tuner_std : V4L2_STD_ALL; + return 0; +} + +int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output) +{ + const struct ivtv_card_output *card_output = itv->card->video_outputs + index; + + memset(output, 0, sizeof(*output)); + if (index >= itv->card->nof_outputs) + return -EINVAL; + output->index = index; + strcpy(output->name, card_output->name); + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->audioset = 1; + output->std = V4L2_STD_ALL; + return 0; +} + +int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio) +{ + const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "Line In 1", + "Line In 2" + }; + + memset(audio, 0, sizeof(*audio)); + if (index >= itv->nof_audio_inputs) + return -EINVAL; + strcpy(audio->name, input_strs[aud_input->audio_type - 1]); + audio->index = index; + audio->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output) +{ + memset(aud_output, 0, sizeof(*aud_output)); + if (itv->card->video_outputs == NULL || index != 0) + return -EINVAL; + strcpy(aud_output->name, "A/V Audio Out"); + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h new file mode 100644 index 0000000..15012f8 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -0,0 +1,207 @@ +/* + Functions to query card hardware + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* hardware flags */ +#define IVTV_HW_CX25840 (1 << 0) +#define IVTV_HW_SAA7115 (1 << 1) +#define IVTV_HW_SAA7127 (1 << 2) +#define IVTV_HW_MSP34XX (1 << 3) +#define IVTV_HW_TUNER (1 << 4) +#define IVTV_HW_WM8775 (1 << 5) +#define IVTV_HW_CS53L32A (1 << 6) +#define IVTV_HW_TVEEPROM (1 << 7) +#define IVTV_HW_SAA7114 (1 << 8) +#define IVTV_HW_TVAUDIO (1 << 9) +#define IVTV_HW_UPD64031A (1 << 10) +#define IVTV_HW_UPD6408X (1 << 11) +#define IVTV_HW_SAA717X (1 << 12) +#define IVTV_HW_WM8739 (1 << 13) +#define IVTV_HW_GPIO (1 << 14) + +#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) + +/* video inputs */ +#define IVTV_CARD_INPUT_VID_TUNER 1 +#define IVTV_CARD_INPUT_SVIDEO1 2 +#define IVTV_CARD_INPUT_SVIDEO2 3 +#define IVTV_CARD_INPUT_COMPOSITE1 4 +#define IVTV_CARD_INPUT_COMPOSITE2 5 +#define IVTV_CARD_INPUT_COMPOSITE3 6 + +/* audio inputs */ +#define IVTV_CARD_INPUT_AUD_TUNER 1 +#define IVTV_CARD_INPUT_LINE_IN1 2 +#define IVTV_CARD_INPUT_LINE_IN2 3 + +#define IVTV_CARD_MAX_VIDEO_INPUTS 6 +#define IVTV_CARD_MAX_AUDIO_INPUTS 3 +#define IVTV_CARD_MAX_TUNERS 2 + +/* SAA71XX HW inputs */ +#define IVTV_SAA71XX_COMPOSITE0 0 +#define IVTV_SAA71XX_COMPOSITE1 1 +#define IVTV_SAA71XX_COMPOSITE2 2 +#define IVTV_SAA71XX_COMPOSITE3 3 +#define IVTV_SAA71XX_COMPOSITE4 4 +#define IVTV_SAA71XX_COMPOSITE5 5 +#define IVTV_SAA71XX_SVIDEO0 6 +#define IVTV_SAA71XX_SVIDEO1 7 +#define IVTV_SAA71XX_SVIDEO2 8 +#define IVTV_SAA71XX_SVIDEO3 9 + +/* SAA717X needs to mark the tuner input by ORing with this flag */ +#define IVTV_SAA717X_TUNER_FLAG 0x80 + +/* Dummy HW input */ +#define IVTV_DUMMY_AUDIO 0 + +/* GPIO HW inputs */ +#define IVTV_GPIO_TUNER 0 +#define IVTV_GPIO_LINE_IN 1 + +/* SAA717X HW inputs */ +#define IVTV_SAA717X_IN0 0 +#define IVTV_SAA717X_IN1 1 +#define IVTV_SAA717X_IN2 2 + +/* V4L2 capability aliases */ +#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \ + V4L2_CAP_SLICED_VBI_CAPTURE) +#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \ + V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_POS) + +struct ivtv_card_video_input { + u8 video_type; /* video input type */ + u8 audio_index; /* index in ivtv_card_audio_input array */ + u16 video_input; /* hardware video input */ +}; + +struct ivtv_card_audio_input { + u8 audio_type; /* audio input type */ + u32 audio_input; /* hardware audio input */ + u16 muxer_input; /* hardware muxer input for boards with a + multiplexer chip */ +}; + +struct ivtv_card_output { + u8 name[32]; + u16 video_output; /* hardware video output */ +}; + +struct ivtv_card_pci_info { + u16 device; + u16 subsystem_vendor; + u16 subsystem_device; +}; + +/* GPIO definitions */ + +/* The mask is the set of bits used by the operation */ + +struct ivtv_gpio_init { /* set initial GPIO DIR and OUT values */ + u16 direction; /* DIR setting. Leave to 0 if no init is needed */ + u16 initial_value; +}; + +struct ivtv_gpio_video_input { /* select tuner/line in input */ + u16 mask; /* leave to 0 if not supported */ + u16 tuner; + u16 composite; + u16 svideo; +}; + +struct ivtv_gpio_audio_input { /* select tuner/line in input */ + u16 mask; /* leave to 0 if not supported */ + u16 tuner; + u16 linein; + u16 radio; +}; + +struct ivtv_gpio_audio_mute { + u16 mask; /* leave to 0 if not supported */ + u16 mute; /* set this value to mute, 0 to unmute */ +}; + +struct ivtv_gpio_audio_mode { + u16 mask; /* leave to 0 if not supported */ + u16 mono; /* set audio to mono */ + u16 stereo; /* set audio to stereo */ + u16 lang1; /* set audio to the first language */ + u16 lang2; /* set audio to the second language */ + u16 both; /* both languages are output */ +}; + +struct ivtv_gpio_audio_freq { + u16 mask; /* leave to 0 if not supported */ + u16 f32000; + u16 f44100; + u16 f48000; +}; + +struct ivtv_gpio_audio_detect { + u16 mask; /* leave to 0 if not supported */ + u16 stereo; /* if the input matches this value then + stereo is detected */ +}; + +struct ivtv_card_tuner { + v4l2_std_id std; /* standard for which the tuner is suitable */ + int tuner; /* tuner ID (from tuner.h) */ +}; + +/* for card information/parameters */ +struct ivtv_card { + int type; + char *name; + u32 v4l2_capabilities; + u32 hw_video; /* hardware used to process video */ + u32 hw_audio; /* hardware used to process audio */ + u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 1 dev allowed) */ + u32 hw_muxer; /* hardware used to multiplex audio input */ + u32 hw_all; /* all hardware used by the board */ + struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS]; + struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS]; + struct ivtv_card_audio_input radio_input; + int nof_outputs; + const struct ivtv_card_output *video_outputs; + u8 gr_config; /* config byte for the ghost reduction device */ + + /* GPIO card-specific settings */ + struct ivtv_gpio_init gpio_init; + struct ivtv_gpio_video_input gpio_video_input; + struct ivtv_gpio_audio_input gpio_audio_input; + struct ivtv_gpio_audio_mute gpio_audio_mute; + struct ivtv_gpio_audio_mode gpio_audio_mode; + struct ivtv_gpio_audio_freq gpio_audio_freq; + struct ivtv_gpio_audio_detect gpio_audio_detect; + + struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS]; + + /* list of device and subsystem vendor/devices that + correspond to this card type. */ + const struct ivtv_card_pci_info *pci_list; +}; + +int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input); +int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output); +int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input); +int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output); +const struct ivtv_card *ivtv_get_card(u16 index); diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c new file mode 100644 index 0000000..7a876c3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -0,0 +1,303 @@ +/* + ioctl control functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-ioctl.h" +#include "ivtv-audio.h" +#include "ivtv-i2c.h" +#include "ivtv-mailbox.h" +#include "ivtv-controls.h" + +static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, + V4L2_CID_AUDIO_BALANCE, + V4L2_CID_AUDIO_BASS, + V4L2_CID_AUDIO_TREBLE, + V4L2_CID_AUDIO_MUTE, + V4L2_CID_AUDIO_LOUDNESS, + 0 +}; + +static const u32 *ctrl_classes[] = { + user_ctrls, + cx2341x_mpeg_ctrls, + NULL +}; + +static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl) +{ + const char *name; + + IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id); + + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (qctrl->id == 0) + return -EINVAL; + + switch (qctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + default: + if (cx2341x_ctrl_query(&itv->params, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + } + strncpy(qctrl->name, name, sizeof(qctrl->name) - 1); + qctrl->name[sizeof(qctrl->name) - 1] = 0; + return 0; +} + +static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + + qctrl.id = qmenu->id; + ivtv_queryctrl(itv, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); +} + +static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +{ + s32 v = vctrl->value; + + IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + + default: + IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +{ + IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + default: + IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt) +{ + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) + return -EINVAL; + if (atomic_read(&itv->capturing) > 0) + return -EBUSY; + + /* First try to allocate sliced VBI buffers if needed. */ + if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) { + int i; + + for (i = 0; i < IVTV_VBI_FRAMES; i++) { + /* Yuck, hardcoded. Needs to be a define */ + itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); + if (itv->vbi.sliced_mpeg_data[i] == NULL) { + while (--i >= 0) { + kfree(itv->vbi.sliced_mpeg_data[i]); + itv->vbi.sliced_mpeg_data[i] = NULL; + } + return -ENOMEM; + } + } + } + + itv->vbi.insert_mpeg = fmt; + + if (itv->vbi.insert_mpeg == 0) { + return 0; + } + /* Need sliced data for mpeg insertion */ + if (get_service_set(itv->vbi.sliced_in) == 0) { + if (itv->is_60hz) + itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; + else + itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; + expand_service_set(itv->vbi.sliced_in, itv->is_50hz); + } + return 0; +} + +int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg) +{ + struct v4l2_control ctrl; + + switch (cmd) { + case VIDIOC_QUERYMENU: + IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n"); + return ivtv_querymenu(itv, arg); + + case VIDIOC_QUERYCTRL: + return ivtv_queryctrl(itv, arg); + + case VIDIOC_S_CTRL: + return ivtv_s_ctrl(itv, arg); + + case VIDIOC_G_CTRL: + return ivtv_g_ctrl(itv, arg); + + case VIDIOC_S_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_s_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + struct cx2341x_mpeg_params p = itv->params; + int err = cx2341x_ext_ctrls(&p, arg, cmd); + + if (err) + return err; + + if (p.video_encoding != itv->params.video_encoding) { + int is_mpeg1 = p.video_encoding == + V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_format fmt; + + /* fix videodecoder resolution */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); + fmt.fmt.pix.height = itv->params.height; + itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); + } + err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); + if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) { + err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt); + } + itv->params = p; + itv->dualwatch_stereo_mode = p.audio_properties & 0x0300; + ivtv_audio_set_audio_clock_freq(itv, p.audio_properties & 0x03); + return err; + } + return -EINVAL; + } + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_g_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, arg, cmd); + return -EINVAL; + } + + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, arg, cmd); + return -EINVAL; + } + + default: + return -EINVAL; + } + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h new file mode 100644 index 0000000..5a11149 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-controls.h @@ -0,0 +1,21 @@ +/* + ioctl control functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg); diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c new file mode 100644 index 0000000..45b9328 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -0,0 +1,1374 @@ +/* + ivtv driver initialization and card probing + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Main Driver file for the ivtv project: + * Driver for the Conexant CX23415/CX23416 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.ivtvdriver.org + * + * ----- + * MPG600/MPG160 support by T.Adachi + * and Takeru KOMORIYA + * + * AVerMedia M179 GPIO info by Chris Pinkham + * using information provided by Jiun-Kuei Jung @ AVerMedia. + * + * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta + * using information from T.Adachi,Takeru KOMORIYA and others :-) + * + * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX + * version by T.Adachi. Special thanks Mr.Suzuki + */ + +#include "ivtv-driver.h" +#include "ivtv-version.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-firmware.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-mailbox.h" +#include "ivtv-streams.h" +#include "ivtv-ioctl.h" +#include "ivtv-cards.h" +#include "ivtv-vbi.h" +#include "ivtv-audio.h" +#include "ivtv-gpio.h" +#include "ivtv-yuv.h" + +#include +#include +#include + +/* var to keep track of the number of array elements in use */ +int ivtv_cards_active = 0; + +/* If you have already X v4l cards, then set this to X. This way + the device numbers stay matched. Example: you have a WinTV card + without radio and a PVR-350 with. Normally this would give a + video1 device together with a radio0 device for the PVR. By + setting this to 1 you ensure that radio0 is now also radio1. */ +int ivtv_first_minor = 0; + +/* Master variable for all ivtv info */ +struct ivtv *ivtv_cards[IVTV_MAX_CARDS]; + +/* Protects ivtv_cards_active */ +spinlock_t ivtv_cards_lock = SPIN_LOCK_UNLOCKED; + +/* add your revision and whatnot here */ +static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); + +const u32 yuv_offset[4] = { + IVTV_YUV_BUFFER_OFFSET, + IVTV_YUV_BUFFER_OFFSET_1, + IVTV_YUV_BUFFER_OFFSET_2, + IVTV_YUV_BUFFER_OFFSET_3 +}; + +/* Parameter declarations */ +static int cardtype[IVTV_MAX_CARDS]; +static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +static int cardtype_c = 1; +static int tuner_c = 1; +static int radio_c = 1; +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; + +/* Buffers */ +static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS; +static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS; +static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS; +static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS; +static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS; +static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS; +static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS; + +static int ivtv_yuv_mode = 0; +static int ivtv_yuv_threshold=-1; +static int ivtv_pci_latency = 1; + +int ivtv_debug = 0; + +static int newi2c = -1; + +module_param_array(tuner, int, &tuner_c, 0644); +module_param_array(radio, bool, &radio_c, 0644); +module_param_array(cardtype, int, &cardtype_c, 0644); +module_param_string(pal, pal, sizeof(pal), 0644); +module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); +module_param_named(debug,ivtv_debug, int, 0644); +module_param(ivtv_pci_latency, int, 0644); +module_param(ivtv_yuv_mode, int, 0644); +module_param(ivtv_yuv_threshold, int, 0644); +module_param(ivtv_first_minor, int, 0644); + +module_param(enc_mpg_buffers, int, 0644); +module_param(enc_yuv_buffers, int, 0644); +module_param(enc_vbi_buffers, int, 0644); +module_param(enc_pcm_buffers, int, 0644); +module_param(dec_mpg_buffers, int, 0644); +module_param(dec_yuv_buffers, int, 0644); +module_param(dec_vbi_buffers, int, 0644); + +module_param(newi2c, int, 0644); + +MODULE_PARM_DESC(tuner, "Tuner type selection,\n" + "\t\t\tsee tuner.h for values"); +MODULE_PARM_DESC(radio, + "Enable or disable the radio. Use only if autodetection\n" + "\t\t\tfails. 0 = disable, 1 = enable"); +MODULE_PARM_DESC(cardtype, + "Only use this option if your card is not detected properly.\n" + "\t\tSpecify card type:\n" + "\t\t\t 1 = WinTV PVR 250\n" + "\t\t\t 2 = WinTV PVR 350\n" + "\t\t\t 3 = WinTV PVR-150 or PVR-500\n" + "\t\t\t 4 = AVerMedia M179\n" + "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n" + "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n" + "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n" + "\t\t\t 8 = Adaptec AVC-2410\n" + "\t\t\t 9 = Adaptec AVC-2010\n" + "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n" + "\t\t\t11 = AOpen VA2000MAX-STN6\n" + "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n" + "\t\t\t13 = I/O Data GV-MVP/RX\n" + "\t\t\t14 = I/O Data GV-MVP/RX2E\n" + "\t\t\t15 = GOTVIEW PCI DVD\n" + "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n" + "\t\t\t17 = Yuan MPC622\n" + "\t\t\t18 = Digital Cowboy DCT-MTVP1\n" +#ifdef HAVE_XC3028 + "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01\n" +#endif + "\t\t\t 0 = Autodetect (default)\n" + "\t\t\t-1 = Ignore this card\n\t\t"); +MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); +MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); +MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); +MODULE_PARM_DESC(debug, + "Debug level (bitmask). Default: errors only\n" + "\t\t\t(debug = 511 gives full debugging)"); +MODULE_PARM_DESC(ivtv_pci_latency, + "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" + "\t\t\tDefault: Yes"); +MODULE_PARM_DESC(ivtv_yuv_mode, + "Specify the yuv playback mode:\n" + "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n" + "\t\t\tDefault: 0 (interlaced)"); +MODULE_PARM_DESC(ivtv_yuv_threshold, + "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n" + "\t\t\tDefault: 480");; +MODULE_PARM_DESC(enc_mpg_buffers, + "Encoder MPG Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS)); +MODULE_PARM_DESC(enc_yuv_buffers, + "Encoder YUV Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS)); +MODULE_PARM_DESC(enc_vbi_buffers, + "Encoder VBI Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS)); +MODULE_PARM_DESC(enc_pcm_buffers, + "Encoder PCM buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS)); +MODULE_PARM_DESC(dec_mpg_buffers, + "Decoder MPG buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS)); +MODULE_PARM_DESC(dec_yuv_buffers, + "Decoder YUV buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS)); +MODULE_PARM_DESC(dec_vbi_buffers, + "Decoder VBI buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS)); +MODULE_PARM_DESC(newi2c, + "Use new I2C implementation\n" + "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" + "\t\t\tDefault is autodetect"); + +MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card"); + +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); +MODULE_DESCRIPTION("CX23415/CX23416 driver"); +MODULE_SUPPORTED_DEVICE + ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n" + "\t\t\tYuan MPG series and similar)"); +MODULE_LICENSE("GPL"); + +MODULE_VERSION(IVTV_VERSION); + +void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask) +{ + itv->irqmask &= ~mask; + write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); +} + +void ivtv_set_irq_mask(struct ivtv *itv, u32 mask) +{ + itv->irqmask |= mask; + write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); +} + +int ivtv_set_output_mode(struct ivtv *itv, int mode) +{ + int old_mode; + + spin_lock(&itv->lock); + old_mode = itv->output_mode; + if (old_mode == 0) + itv->output_mode = old_mode = mode; + spin_unlock(&itv->lock); + return old_mode; +} + +struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv) +{ + switch (itv->output_mode) { + case OUT_MPG: + return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + case OUT_YUV: + return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + default: + return NULL; + } +} + +int ivtv_waitq(wait_queue_head_t *waitq) +{ + DEFINE_WAIT(wait); + + prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(waitq, &wait); + return signal_pending(current) ? -EINTR : 0; +} + +/* Generic utility functions */ +int ivtv_sleep_timeout(int timeout, int intr) +{ + int ret; + + do { + set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + timeout = schedule_timeout(timeout); + if (intr && (ret = signal_pending(current))) + return ret; + } while (timeout); + return 0; +} + +/* Release ioremapped memory */ +static void ivtv_iounmap(struct ivtv *itv) +{ + if (itv == NULL) + return; + + /* Release registers memory */ + if (itv->reg_mem != NULL) { + IVTV_DEBUG_INFO("releasing reg_mem\n"); + iounmap(itv->reg_mem); + itv->reg_mem = NULL; + } + /* Release io memory */ + if (itv->has_cx23415 && itv->dec_mem != NULL) { + IVTV_DEBUG_INFO("releasing dec_mem\n"); + iounmap(itv->dec_mem); + } + itv->dec_mem = NULL; + + /* Release io memory */ + if (itv->enc_mem != NULL) { + IVTV_DEBUG_INFO("releasing enc_mem\n"); + iounmap(itv->enc_mem); + itv->enc_mem = NULL; + } +} + +/* Hauppauge card? get values from tveeprom */ +void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv) +{ + u8 eedata[256]; + + itv->i2c_client.addr = 0xA0 >> 1; + tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata)); + tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata); +} + +static void ivtv_process_eeprom(struct ivtv *itv) +{ + struct tveeprom tv; + int pci_slot = PCI_SLOT(itv->dev->devfn); + + ivtv_read_eeprom(itv, &tv); + + /* Many thanks to Steven Toth from Hauppauge for providing the + model numbers */ + switch (tv.model) { + /* In a few cases the PCI subsystem IDs do not correctly + identify the card. A better method is to check the + model number from the eeprom instead. */ + case 32000 ... 32999: + case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */ + case 48400 ... 48599: + itv->card = ivtv_get_card(IVTV_CARD_PVR_250); + break; + case 48100 ... 48399: + case 48600 ... 48999: + itv->card = ivtv_get_card(IVTV_CARD_PVR_350); + break; + case 23000 ... 23999: /* PVR500 */ + case 25000 ... 25999: /* Low profile PVR150 */ + case 26000 ... 26999: /* Regular PVR150 */ + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + break; + case 0: + IVTV_ERR("Invalid EEPROM\n"); + return; + default: + IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model); + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + break; + } + + switch (tv.model) { + /* Old style PVR350 (with an saa7114) uses this input for + the tuner. */ + case 48254: + itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1); + break; + default: + break; + } + + itv->v4l2_cap = itv->card->v4l2_capabilities; + itv->card_name = itv->card->name; + + /* If this is a PVR500 then it should be possible to detect whether it is the + first or second unit by looking at the subsystem device ID: is bit 4 is + set, then it is the second unit (according to info from Hauppauge). + + However, while this works for most cards, I have seen a few PVR500 cards + where both units have the same subsystem ID. + + So instead I look at the reported 'PCI slot' (which is the slot on the PVR500 + PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise + it is the second unit. It is possible that it is a different slot when ivtv is + used in Xen, in that case I ignore this card here. The worst that can happen + is that the card presents itself with a non-working radio device. + + This detection is needed since the eeprom reports incorrectly that a radio is + present on the second unit. */ + if (tv.model / 1000 == 23) { + itv->card_name = "WinTV PVR 500"; + if (pci_slot == 8 || pci_slot == 9) { + int is_first = (pci_slot & 1) == 0; + + itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" : + "WinTV PVR 500 (unit #2)"; + if (!is_first) { + IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n"); + tv.has_radio = 0; + } + } + } + IVTV_INFO("Autodetected %s\n", itv->card_name); + + switch (tv.tuner_hauppauge_model) { + case 85: + case 99: + case 112: + itv->pvr150_workaround = 1; + break; + default: + break; + } + if (tv.tuner_type == TUNER_ABSENT) + IVTV_ERR("tveeprom cannot autodetect tuner!"); + + if (itv->options.tuner == -1) + itv->options.tuner = tv.tuner_type; + if (itv->options.radio == -1) + itv->options.radio = (tv.has_radio != 0); + /* only enable newi2c if an IR blaster is present */ + /* FIXME: for 2.6.20 the test against 2 should be removed */ + if (itv->options.newi2c == -1 && tv.has_ir != -1 && tv.has_ir != 2) { + itv->options.newi2c = (tv.has_ir & 2) ? 1 : 0; + if (itv->options.newi2c) { + IVTV_INFO("reopen i2c bus for IR-blaster support\n"); + exit_ivtv_i2c(itv); + init_ivtv_i2c(itv); + } + } + + if (itv->std != 0) + /* user specified tuner standard */ + return; + + /* autodetect tuner standard */ + if (tv.tuner_formats & V4L2_STD_PAL) { + IVTV_DEBUG_INFO("PAL tuner detected\n"); + itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; + } else if (tv.tuner_formats & V4L2_STD_NTSC) { + IVTV_DEBUG_INFO("NTSC tuner detected\n"); + itv->std |= V4L2_STD_NTSC_M; + } else if (tv.tuner_formats & V4L2_STD_SECAM) { + IVTV_DEBUG_INFO("SECAM tuner detected\n"); + itv->std |= V4L2_STD_SECAM_L; + } else { + IVTV_INFO("No tuner detected, default to NTSC-M\n"); + itv->std |= V4L2_STD_NTSC_M; + } +} + +static v4l2_std_id ivtv_parse_std(struct ivtv *itv) +{ + switch (pal[0]) { + case '6': + return V4L2_STD_PAL_60; + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[1] == 'c' || pal[1] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + default: + IVTV_WARN("pal= argument not recognised\n"); + return 0; + } + + switch (secam[0]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[1] == 'C' || secam[1] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + default: + IVTV_WARN("secam= argument not recognised\n"); + return 0; + } + + switch (ntsc[0]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + default: + IVTV_WARN("ntsc= argument not recognised\n"); + return 0; + } + + /* no match found */ + return 0; +} + +static void ivtv_process_options(struct ivtv *itv) +{ + const char *chipname; + int i, j; + + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers; + itv->options.cardtype = cardtype[itv->num]; + itv->options.tuner = tuner[itv->num]; + itv->options.radio = radio[itv->num]; + itv->options.newi2c = newi2c; + + itv->std = ivtv_parse_std(itv); + itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15); + chipname = itv->has_cx23415 ? "cx23415" : "cx23416"; + if (itv->options.cardtype == -1) { + IVTV_INFO("Ignore card (detected %s based chip)\n", chipname); + return; + } + if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) { + IVTV_INFO("User specified %s card (detected %s based chip)\n", + itv->card->name, chipname); + } else if (itv->options.cardtype != 0) { + IVTV_ERR("Unknown user specified type, trying to autodetect card\n"); + } + if (itv->card == NULL) { + if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE || + itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 || + itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) { + itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150); + IVTV_INFO("Autodetected Hauppauge card (%s based)\n", + chipname); + } + } + if (itv->card == NULL) { + for (i = 0; (itv->card = ivtv_get_card(i)); i++) { + if (itv->card->pci_list == NULL) + continue; + for (j = 0; itv->card->pci_list[j].device; j++) { + if (itv->dev->device != + itv->card->pci_list[j].device) + continue; + if (itv->dev->subsystem_vendor != + itv->card->pci_list[j].subsystem_vendor) + continue; + if (itv->dev->subsystem_device != + itv->card->pci_list[j].subsystem_device) + continue; + IVTV_INFO("Autodetected %s card (%s based)\n", + itv->card->name, chipname); + goto done; + } + } + } +done: + + if (itv->card == NULL) { + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n", + itv->dev->vendor, itv->dev->device); + IVTV_ERR(" subsystem vendor/device: %04x/%04x\n", + itv->dev->subsystem_vendor, itv->dev->subsystem_device); + IVTV_ERR(" %s based\n", chipname); + IVTV_ERR("Defaulting to %s card\n", itv->card->name); + IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); + IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); + IVTV_ERR("Prefix your subject line with [UNKNOWN CARD].\n"); + } + itv->v4l2_cap = itv->card->v4l2_capabilities; + itv->card_name = itv->card->name; +} + +/* Precondition: the ivtv structure has been memset to 0. Only + the dev and num fields have been filled in. + No assumptions on the card type may be made here (see ivtv_init_struct2 + for that). + */ +static int __devinit ivtv_init_struct1(struct ivtv *itv) +{ + itv->base_addr = pci_resource_start(itv->dev, 0); + itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */ + itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ + + mutex_init(&itv->i2c_bus_lock); + mutex_init(&itv->udma.lock); + + spin_lock_init(&itv->lock); + spin_lock_init(&itv->dma_reg_lock); + + itv->irq_work_queues = create_workqueue(itv->name); + if (itv->irq_work_queues == NULL) { + IVTV_ERR("Could not create ivtv workqueue\n"); + return -1; + } + + INIT_WORK(&itv->irq_work_queue, ivtv_irq_work_handler); + + /* start counting open_id at 1 */ + itv->open_id = 1; + + /* Initial settings */ + cx2341x_fill_defaults(&itv->params); + itv->params.port = CX2341X_PORT_MEMORY; + itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI; + init_waitqueue_head(&itv->cap_w); + init_waitqueue_head(&itv->event_waitq); + init_waitqueue_head(&itv->vsync_waitq); + init_waitqueue_head(&itv->dma_waitq); + init_timer(&itv->dma_timer); + itv->dma_timer.function = ivtv_unfinished_dma; + itv->dma_timer.data = (unsigned long)itv; + + itv->cur_dma_stream = -1; + itv->audio_stereo_mode = AUDIO_STEREO; + itv->audio_bilingual_mode = AUDIO_MONO_LEFT; + + /* Ctrls */ + itv->speed = 1000; + + /* VBI */ + itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced; + + /* OSD */ + itv->osd_global_alpha_state = 1; + itv->osd_global_alpha = 255; + + /* YUV */ + atomic_set(&itv->yuv_info.next_dma_frame, -1); + itv->yuv_info.lace_mode = ivtv_yuv_mode; + itv->yuv_info.lace_threshold = ivtv_yuv_threshold; + return 0; +} + +/* Second initialization part. Here the card type has been + autodetected. */ +static void __devinit ivtv_init_struct2(struct ivtv *itv) +{ + int i; + + for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++) + if (itv->card->video_inputs[i].video_type == 0) + break; + itv->nof_inputs = i; + for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++) + if (itv->card->audio_inputs[i].audio_type == 0) + break; + itv->nof_audio_inputs = i; + + /* 0x00EF = saa7114(239) 0x00F0 = saa7115(240) 0x0106 = micro */ + if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X)) + itv->digitizer = 0xF1; + else if (itv->card->hw_all & IVTV_HW_SAA7114) + itv->digitizer = 0xEF; + else /* cx25840 */ + itv->digitizer = 0x140; + + if (itv->card->hw_all & IVTV_HW_CX25840) { + itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */ + } else { + itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */ + } + + /* Find tuner input */ + for (i = 0; i < itv->nof_inputs; i++) { + if (itv->card->video_inputs[i].video_type == + IVTV_CARD_INPUT_VID_TUNER) + break; + } + if (i == itv->nof_inputs) + i = 0; + itv->active_input = i; + itv->audio_input = itv->card->video_inputs[i].audio_index; + if (itv->card->hw_all & IVTV_HW_CX25840) + itv->video_dec_func = ivtv_cx25840; + else if (itv->card->hw_all & IVTV_HW_SAA717X) + itv->video_dec_func = ivtv_saa717x; + else + itv->video_dec_func = ivtv_saa7115; +} + +static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + u16 cmd; + unsigned char pci_latency; + + IVTV_DEBUG_INFO("Enabling pci device\n"); + + if (pci_enable_device(dev)) { + IVTV_ERR("Can't enable device %d!\n", itv->num); + return -EIO; + } + if (pci_set_dma_mask(dev, 0xffffffff)) { + IVTV_ERR("No suitable DMA available on card %d.\n", itv->num); + return -EIO; + } + if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { + IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num); + return -EIO; + } + + if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, + IVTV_REG_SIZE, "ivtv registers")) { + IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + return -EIO; + } + + if (itv->has_cx23415 && + !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE, "ivtv decoder")) { + IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + return -EIO; + } + + /* Check for bus mastering */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n"); + pci_set_master(dev); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + IVTV_ERR("Bus Mastering is not enabled\n"); + return -ENXIO; + } + } + IVTV_DEBUG_INFO("Bus Mastering Enabled.\n"); + + pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 64 && ivtv_pci_latency) { + IVTV_INFO("Unreasonably low latency timer, " + "setting to 64 (was %d)\n", pci_latency); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + } + /* This config space value relates to DMA latencies. The + default value 0x8080 is too low however and will lead + to DMA errors. 0xffff is the max value which solves + these problems. */ + pci_write_config_dword(dev, 0x40, 0xffff); + + IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, " + "irq: %d, latency: %d, memory: 0x%lx\n", + itv->dev->device, itv->card_rev, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + itv->dev->irq, pci_latency, (unsigned long)itv->base_addr); + + return 0; +} + +static void ivtv_request_module(struct ivtv *itv, const char *name) +{ + if (request_module(name) != 0) { + IVTV_ERR("Failed to load module %s\n", name); + } else { + IVTV_DEBUG_INFO("Loaded module %s\n", name); + } +} + +static void ivtv_load_and_init_modules(struct ivtv *itv) +{ + struct v4l2_control ctrl; + u32 hw = itv->card->hw_all; + int i; + + /* load modules */ +#ifndef CONFIG_VIDEO_TUNER + if (hw & IVTV_HW_TUNER) { + ivtv_request_module(itv, "tuner"); +#ifdef HAVE_XC3028 + if (itv->options.tuner == TUNER_XCEIVE_XC3028) + ivtv_request_module(itv, "xc3028-tuner"); +#endif + } +#endif +#ifndef CONFIG_VIDEO_CX25840 + if (hw & IVTV_HW_CX25840) + ivtv_request_module(itv, "cx25840"); +#endif +#ifndef CONFIG_VIDEO_SAA711X + if (hw & IVTV_HW_SAA711X) + ivtv_request_module(itv, "saa7115"); +#endif +#ifndef CONFIG_VIDEO_SAA7127 + if (hw & IVTV_HW_SAA7127) + ivtv_request_module(itv, "saa7127"); +#endif + if (hw & IVTV_HW_SAA717X) + ivtv_request_module(itv, "saa717x"); +#ifndef CONFIG_VIDEO_UPD64031A + if (hw & IVTV_HW_UPD64031A) + ivtv_request_module(itv, "upd64031a"); +#endif +#ifndef CONFIG_VIDEO_UPD64083 + if (hw & IVTV_HW_UPD6408X) + ivtv_request_module(itv, "upd64083"); +#endif +#ifndef CONFIG_VIDEO_MSP3400 + if (hw & IVTV_HW_MSP34XX) + ivtv_request_module(itv, "msp3400"); +#endif + if (hw & IVTV_HW_TVAUDIO) + ivtv_request_module(itv, "tvaudio"); +#ifndef CONFIG_VIDEO_WM8775 + if (hw & IVTV_HW_WM8775) + ivtv_request_module(itv, "wm8775"); +#endif +#ifndef CONFIG_VIDEO_WM8739 + if (hw & IVTV_HW_WM8739) + ivtv_request_module(itv, "wm8739"); +#endif +#ifndef CONFIG_VIDEO_CS53L32A + if (hw & IVTV_HW_CS53L32A) + ivtv_request_module(itv, "cs53l32a"); +#endif + + /* check which i2c devices are actually found */ + for (i = 0; i < 32; i++) { + u32 device = 1 << i; + + if (!(device & hw)) + continue; + if (device == IVTV_HW_GPIO) { + /* GPIO is always available */ + itv->hw_flags |= IVTV_HW_GPIO; + continue; + } + if (ivtv_i2c_hw_addr(itv, device) > 0) + itv->hw_flags |= device; + } + + hw = itv->hw_flags; + + if (itv->card->type == IVTV_CARD_CX23416GYC) { + /* Several variations of this card exist, detect which card + type should be used. */ + if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0) + itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS); + else if ((hw & IVTV_HW_UPD64031A) == 0) + itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR); + } + + if (hw & IVTV_HW_CX25840) { + /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ + ctrl.id = V4L2_CID_PRIVATE_BASE; + ctrl.value = itv->pvr150_workaround; + itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); + + itv->vbi.raw_decoder_line_size = 1444; + itv->vbi.raw_decoder_sav_odd_field = 0x20; + itv->vbi.raw_decoder_sav_even_field = 0x60; + itv->vbi.sliced_decoder_line_size = 272; + itv->vbi.sliced_decoder_sav_odd_field = 0xB0; + itv->vbi.sliced_decoder_sav_even_field = 0xF0; + } + + if (hw & IVTV_HW_SAA711X) { + struct v4l2_chip_ident v = { V4L2_CHIP_MATCH_I2C_DRIVER, I2C_DRIVERID_SAA711X }; + + /* determine the exact saa711x model */ + itv->hw_flags &= ~IVTV_HW_SAA711X; + + ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v); + if (v.ident == V4L2_IDENT_SAA7114) { + itv->hw_flags |= IVTV_HW_SAA7114; + /* VBI is not yet supported by the saa7114 driver. */ + itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE); + } + else { + itv->hw_flags |= IVTV_HW_SAA7115; + } + itv->vbi.raw_decoder_line_size = 1443; + itv->vbi.raw_decoder_sav_odd_field = 0x25; + itv->vbi.raw_decoder_sav_even_field = 0x62; + itv->vbi.sliced_decoder_line_size = 51; + itv->vbi.sliced_decoder_sav_odd_field = 0xAB; + itv->vbi.sliced_decoder_sav_even_field = 0xEC; + } + + if (hw & IVTV_HW_SAA717X) { + itv->vbi.raw_decoder_line_size = 1443; + itv->vbi.raw_decoder_sav_odd_field = 0x25; + itv->vbi.raw_decoder_sav_even_field = 0x62; + itv->vbi.sliced_decoder_line_size = 51; + itv->vbi.sliced_decoder_sav_odd_field = 0xAB; + itv->vbi.sliced_decoder_sav_even_field = 0xEC; + } +} + +static int __devinit ivtv_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + int retval = 0; + int video_input; + int yuv_buf_size; + int vbi_buf_size; + int fw_retry_count = 3; + struct ivtv *itv; + struct v4l2_frequency vf; + + spin_lock(&ivtv_cards_lock); + + /* Make sure we've got a place for this card */ + if (ivtv_cards_active == IVTV_MAX_CARDS) { + printk(KERN_ERR "ivtv: Maximum number of cards detected (%d).\n", + ivtv_cards_active); + spin_unlock(&ivtv_cards_lock); + return -ENOMEM; + } + + itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); + if (itv == 0) { + spin_unlock(&ivtv_cards_lock); + return -ENOMEM; + } + ivtv_cards[ivtv_cards_active] = itv; + itv->dev = dev; + itv->num = ivtv_cards_active++; + snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num); + if (itv->num) { + printk(KERN_INFO "ivtv: ====================== NEXT CARD ======================\n"); + } + + spin_unlock(&ivtv_cards_lock); + + ivtv_process_options(itv); + if (itv->options.cardtype == -1) { + retval = -ENODEV; + goto err; + } + if (ivtv_init_struct1(itv)) { + retval = -ENOMEM; + goto err; + } + + IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr); + + /* PCI Device Setup */ + if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) { + if (retval == -EIO) + goto free_workqueue; + else if (retval == -ENXIO) + goto free_mem; + } + /* save itv in the pci struct for later use */ + pci_set_drvdata(dev, itv); + + /* map io memory */ + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE); + itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET, + IVTV_ENCODER_SIZE); + if (!itv->enc_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_mem; + } + + if (itv->has_cx23415) { + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE); + if (!itv->dec_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_mem; + } + } + else { + itv->dec_mem = itv->enc_mem; + } + + /* map registers memory */ + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + itv->reg_mem = + ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (!itv->reg_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_io; + } + + while (--fw_retry_count > 0) { + /* load firmware */ + if (ivtv_firmware_init(itv) == 0) + break; + if (fw_retry_count > 1) + IVTV_WARN("Retry loading firmware\n"); + } + if (fw_retry_count == 0) { + IVTV_ERR("Error initializing firmware\n"); + goto free_i2c; + } + + /* Try and get firmware versions */ + IVTV_DEBUG_INFO("Getting firmware version..\n"); + ivtv_firmware_versions(itv); + + /* Check yuv output filter table */ + if (itv->has_cx23415) ivtv_yuv_filter_check(itv); + + ivtv_gpio_init(itv); + + /* active i2c */ + IVTV_DEBUG_INFO("activating i2c...\n"); + if (init_ivtv_i2c(itv)) { + IVTV_ERR("Could not initialize i2c\n"); + goto free_irq; + } + + IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active); + + if (itv->card->hw_all & IVTV_HW_TVEEPROM) { +#ifdef CONFIG_VIDEO_TVEEPROM_MODULE + ivtv_request_module(itv, "tveeprom"); +#endif + /* Based on the model number the cardtype may be changed. + The PCI IDs are not always reliable. */ + ivtv_process_eeprom(itv); + } + + if (itv->std == 0) { + itv->std = V4L2_STD_NTSC_M; + } + + if (itv->options.tuner == -1) { + int i; + + for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) { + if ((itv->std & itv->card->tuners[i].std) == 0) + continue; + itv->options.tuner = itv->card->tuners[i].tuner; + break; + } + } + /* if no tuner was found, then pick the first tuner in the card list */ + if (itv->options.tuner == -1 && itv->card->tuners[0].std) { + itv->std = itv->card->tuners[0].std; + itv->options.tuner = itv->card->tuners[0].tuner; + } + if (itv->options.radio == -1) + itv->options.radio = (itv->card->radio_input.audio_type != 0); + + /* The card is now fully identified, continue with card-specific + initialization. */ + ivtv_init_struct2(itv); + + ivtv_load_and_init_modules(itv); + + if (itv->std & V4L2_STD_525_60) { + itv->is_60hz = 1; + itv->is_out_60hz = 1; + } else { + itv->is_50hz = 1; + itv->is_out_50hz = 1; + } + itv->params.video_gop_size = itv->is_60hz ? 15 : 12; + + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000; + + /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */ + yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8; + + /* Setup VBI Raw Size. Should be big enough to hold PAL. + It is possible to switch between PAL and NTSC, so we need to + take the largest size here. */ + /* 1456 is multiple of 16, real size = 1444 */ + itv->vbi.raw_size = 1456; + /* We use a buffer size of 1/2 of the total size needed for a + frame. This is actually very useful, since we now receive + a field at a time and that makes 'compressing' the raw data + down to size by stripping off the SAV codes a lot easier. + Note: having two different buffer sizes prevents standard + switching on the fly. We need to find a better solution... */ + vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36; + + if (itv->options.radio > 0) + itv->v4l2_cap |= V4L2_CAP_RADIO; + + if (itv->options.tuner > -1) { + struct tuner_setup setup; + + setup.addr = ADDR_UNSET; + setup.type = itv->options.tuner; + setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ +#ifdef HAVE_XC3028 + setup.initmode = V4L2_TUNER_ANALOG_TV; + if (itv->options.tuner == TUNER_XCEIVE_XC3028) { + setup.gpio_write = ivtv_reset_tuner_gpio; + setup.gpio_priv = itv; + } +#endif + ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup); + } + + vf.tuner = 0; + vf.type = V4L2_TUNER_ANALOG_TV; + vf.frequency = 6400; /* the tuner 'baseline' frequency */ + if (itv->std & V4L2_STD_NTSC_M) { + /* Why on earth? */ + vf.frequency = 1076; /* ch. 4 67250*16/1000 */ + } + + /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) + are not. */ + itv->tuner_std = itv->std; + + video_input = itv->active_input; + itv->active_input++; /* Force update of input */ + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input); + + /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code + in one place. */ + itv->std++; /* Force full standard initialization */ + itv->std_out = itv->std; + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf); + + retval = ivtv_streams_setup(itv); + if (retval) { + IVTV_ERR("Error %d setting up streams\n", retval); + goto free_i2c; + } + + if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_init_mpeg_decoder(itv); + } + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std); + + IVTV_DEBUG_IRQ("Masking interrupts\n"); + /* clear interrupt mask, effectively disabling interrupts */ + ivtv_set_irq_mask(itv, 0xffffffff); + + /* Register IRQ */ + retval = request_irq(itv->dev->irq, ivtv_irq_handler, + IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv); + if (retval) { + IVTV_ERR("Failed to register irq %d\n", retval); + goto free_streams; + } + + /* On a cx23416 this seems to be able to enable DMA to the chip? */ + if (!itv->has_cx23415) + write_reg_sync(0x03, IVTV_REG_DMACONTROL); + + /* Default interrupts enabled. For the PVR350 this includes the + decoder VSYNC interrupt, which is always on. It is not only used + during decoding but also by the OSD. + Some old PVR250 cards had a cx23415, so testing for that is too + general. Instead test if the card has video output capability. */ + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC); + else + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); + + if (itv->has_cx23415) + ivtv_set_osd_alpha(itv); + + IVTV_INFO("Initialized %s, card #%d\n", itv->card_name, itv->num); + + return 0; + + free_irq: + free_irq(itv->dev->irq, (void *)itv); + free_streams: + ivtv_streams_cleanup(itv); + free_i2c: + exit_ivtv_i2c(itv); + free_io: + ivtv_iounmap(itv); + free_mem: + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (itv->has_cx23415) + release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + free_workqueue: + destroy_workqueue(itv->irq_work_queues); + err: + if (retval == 0) + retval = -ENODEV; + IVTV_ERR("Error %d on initialization\n", retval); + + kfree(ivtv_cards[ivtv_cards_active]); + ivtv_cards[ivtv_cards_active] = NULL; + return retval; +} + +static void ivtv_remove(struct pci_dev *pci_dev) +{ + struct ivtv *itv = pci_get_drvdata(pci_dev); + + IVTV_DEBUG_INFO("Removing Card #%d.\n", itv->num); + + /* Stop all captures */ + IVTV_DEBUG_INFO(" Stopping all streams.\n"); + if (atomic_read(&itv->capturing) > 0) + ivtv_stop_all_captures(itv); + + /* Stop all decoding */ + IVTV_DEBUG_INFO(" Stopping decoding.\n"); + if (atomic_read(&itv->decoding) > 0) { + int type; + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) + type = IVTV_DEC_STREAM_TYPE_YUV; + else + type = IVTV_DEC_STREAM_TYPE_MPG; + ivtv_stop_v4l2_decode_stream(&itv->streams[type], + VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + } + + /* Interrupts */ + IVTV_DEBUG_INFO(" Disabling interrupts.\n"); + ivtv_set_irq_mask(itv, 0xffffffff); + del_timer_sync(&itv->dma_timer); + + /* Stop all Work Queues */ + IVTV_DEBUG_INFO(" Stop Work Queues.\n"); + flush_workqueue(itv->irq_work_queues); + destroy_workqueue(itv->irq_work_queues); + + IVTV_DEBUG_INFO(" Stopping Firmware.\n"); + ivtv_halt_firmware(itv); + + IVTV_DEBUG_INFO(" Unregistering v4l devices.\n"); + ivtv_streams_cleanup(itv); + IVTV_DEBUG_INFO(" Freeing dma resources.\n"); + ivtv_udma_free(itv); + + exit_ivtv_i2c(itv); + + IVTV_DEBUG_INFO(" Releasing irq.\n"); + free_irq(itv->dev->irq, (void *)itv); + + if (itv->dev) { + ivtv_iounmap(itv); + } + + IVTV_DEBUG_INFO(" Releasing mem.\n"); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (itv->has_cx23415) + release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + + pci_disable_device(itv->dev); + + IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num); +} + +/* define a pci_driver for card detection */ +static struct pci_driver ivtv_pci_driver = { + .name = "ivtv", + .id_table = ivtv_pci_tbl, + .probe = ivtv_probe, + .remove = ivtv_remove, +}; + +static int module_start(void) +{ + printk(KERN_INFO "ivtv: ==================== START INIT IVTV ====================\n"); + printk(KERN_INFO "ivtv: version %s (" VERMAGIC_STRING ") loading\n", IVTV_VERSION); + + memset(ivtv_cards, 0, sizeof(ivtv_cards)); + + /* Validate parameters */ + if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) { + printk(KERN_ERR "ivtv: ivtv_first_minor must be between 0 and %d. Exiting...\n", + IVTV_MAX_CARDS - 1); + return -1; + } + + if (ivtv_debug < 0 || ivtv_debug > 511) { + ivtv_debug = 0; + printk(KERN_INFO "ivtv: debug value must be >= 0 and <= 511!\n"); + } + + if (pci_register_driver(&ivtv_pci_driver)) { + printk(KERN_ERR "ivtv: Error detecting PCI card\n"); + return -ENODEV; + } + printk(KERN_INFO "ivtv: ==================== END INIT IVTV ====================\n"); + return 0; +} + +static void module_cleanup(void) +{ + int i, j; + + pci_unregister_driver(&ivtv_pci_driver); + + for (i = 0; i < ivtv_cards_active; i++) { + if (ivtv_cards[i] == NULL) + continue; + for (j = 0; j < IVTV_VBI_FRAMES; j++) { + kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]); + } + kfree(ivtv_cards[i]); + } +} + +/* Note: These symbols are exported because they are used by the ivtv-fb + framebuffer module and an infrared module for the IR-blaster. */ +EXPORT_SYMBOL(ivtv_set_irq_mask); +EXPORT_SYMBOL(ivtv_cards_active); +EXPORT_SYMBOL(ivtv_cards); +EXPORT_SYMBOL(ivtv_api); +EXPORT_SYMBOL(ivtv_vapi); +EXPORT_SYMBOL(ivtv_vapi_result); +EXPORT_SYMBOL(ivtv_clear_irq_mask); +EXPORT_SYMBOL(ivtv_debug); +EXPORT_SYMBOL(ivtv_reset_ir_gpio); +EXPORT_SYMBOL(ivtv_udma_setup); +EXPORT_SYMBOL(ivtv_udma_unmap); +EXPORT_SYMBOL(ivtv_udma_alloc); +EXPORT_SYMBOL(ivtv_udma_prepare); + +module_init(module_start); +module_exit(module_cleanup); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h new file mode 100644 index 0000000..9a412d6 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -0,0 +1,868 @@ +/* + ivtv driver internal defines and structures + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef IVTV_DRIVER_H +#define IVTV_DRIVER_H + +/* Internal header for ivtv project: + * Driver for the cx23415/6 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.ivtvdriver.org + * + * ----- + * MPG600/MPG160 support by T.Adachi + * and Takeru KOMORIYA + * + * AVerMedia M179 GPIO info by Chris Pinkham + * using information provided by Jiun-Kuei Jung @ AVerMedia. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* #define HAVE_XC3028 1 */ + +#include + +#ifdef CONFIG_LIRC_I2C +# error "This driver is not compatible with the LIRC I2C kernel configuration option." +#endif /* CONFIG_LIRC_I2C */ + +#ifndef CONFIG_PCI +# error "This driver requires kernel PCI support." +#endif /* CONFIG_PCI */ + +#define IVTV_ENCODER_OFFSET 0x00000000 +#define IVTV_ENCODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_DECODER_OFFSET 0x01000000 +#define IVTV_DECODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_REG_OFFSET 0x02000000 +#define IVTV_REG_SIZE 0x00010000 + +/* Buffers on hardware offsets */ +#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */ +#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */ + +/* Offset to filter table in firmware */ +#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8 +#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358 + +extern const u32 yuv_offset[4]; + +/* Maximum ivtv driver instances. + Based on 6 PVR500s each with two PVR15s... + TODO: make this dynamic. I believe it is only a global in order to support + ivtv-fb. There must be a better way to do that. */ +#define IVTV_MAX_CARDS 12 + +/* Supported cards */ +#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */ +#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */ +#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two + PVR150s on one PCI board) */ +#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */ +#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */ +#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160 + cx23415 based, but does not have tv-out */ +#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */ +#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */ +#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */ +#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */ +#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */ +#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */ +#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */ +#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */ +#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */ +#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */ +#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */ +#ifdef HAVE_XC3028 +#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */ +#define IVTV_CARD_LAST 18 +#else +#define IVTV_CARD_LAST 17 +#endif + +/* Variants of existing cards but with the same PCI IDs. The driver + detects these based on other device information. + These cards must always come last. + New cards must be inserted above, and the indices of the cards below + must be adjusted accordingly. */ + +/* PVR-350 V1 (uses saa7114) */ +#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1) +/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2) +#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3) + +#define IVTV_ENC_STREAM_TYPE_MPG 0 +#define IVTV_ENC_STREAM_TYPE_YUV 1 +#define IVTV_ENC_STREAM_TYPE_VBI 2 +#define IVTV_ENC_STREAM_TYPE_PCM 3 +#define IVTV_ENC_STREAM_TYPE_RAD 4 +#define IVTV_DEC_STREAM_TYPE_MPG 5 +#define IVTV_DEC_STREAM_TYPE_VBI 6 +#define IVTV_DEC_STREAM_TYPE_VOUT 7 +#define IVTV_DEC_STREAM_TYPE_YUV 8 +#define IVTV_MAX_STREAMS 9 + +#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */ +#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */ +#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */ +#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */ + +#define IVTV_ENC_MEM_START 0x00000000 +#define IVTV_DEC_MEM_START 0x01000000 + +/* system vendor and device IDs */ +#define PCI_VENDOR_ID_ICOMP 0x4444 +#define PCI_DEVICE_ID_IVTV15 0x0803 +#define PCI_DEVICE_ID_IVTV16 0x0016 + +/* subsystem vendor ID */ +#define IVTV_PCI_ID_HAUPPAUGE 0x0070 +#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270 +#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070 +#define IVTV_PCI_ID_ADAPTEC 0x9005 +#define IVTV_PCI_ID_AVERMEDIA 0x1461 +#define IVTV_PCI_ID_YUAN1 0x12ab +#define IVTV_PCI_ID_YUAN2 0xff01 +#define IVTV_PCI_ID_YUAN3 0xffab +#define IVTV_PCI_ID_YUAN4 0xfbab +#define IVTV_PCI_ID_DIAMONDMM 0xff92 +#define IVTV_PCI_ID_IODATA 0x10fc +#define IVTV_PCI_ID_MELCO 0x1154 +#define IVTV_PCI_ID_GOTVIEW1 0xffac +#define IVTV_PCI_ID_GOTVIEW2 0xffad + +/* Decoder Buffer hardware size on Chip */ +#define IVTV_DEC_MAX_BUF 0x00100000 /* max bytes in decoder buffer */ +#define IVTV_DEC_MIN_BUF 0x00010000 /* min bytes in dec buffer */ + +/* ======================================================================== */ +/* ========================== START USER SETTABLE DMA VARIABLES =========== */ +/* ======================================================================== */ + +#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */ + +/* DMA Buffers, Default size in MB allocated */ +#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4 +#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2 +#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1 +#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1 +#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1 +#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1 +#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1 + +/* ======================================================================== */ +/* ========================== END USER SETTABLE DMA VARIABLES ============= */ +/* ======================================================================== */ + +/* Decoder Status Register */ +#define IVTV_DMA_ERR_LIST 0x00000010 +#define IVTV_DMA_ERR_WRITE 0x00000008 +#define IVTV_DMA_ERR_READ 0x00000004 +#define IVTV_DMA_SUCCESS_WRITE 0x00000002 +#define IVTV_DMA_SUCCESS_READ 0x00000001 +#define IVTV_DMA_READ_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ) +#define IVTV_DMA_WRITE_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE) +#define IVTV_DMA_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ) + +/* DMA Registers */ +#define IVTV_REG_DMAXFER (0x0000) +#define IVTV_REG_DMASTATUS (0x0004) +#define IVTV_REG_DECDMAADDR (0x0008) +#define IVTV_REG_ENCDMAADDR (0x000c) +#define IVTV_REG_DMACONTROL (0x0010) +#define IVTV_REG_IRQSTATUS (0x0040) +#define IVTV_REG_IRQMASK (0x0048) + +/* Setup Registers */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) +#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8) +#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC) +#define IVTV_REG_VDM (0x2800) +#define IVTV_REG_AO (0x2D00) +#define IVTV_REG_BYTEFLUSH (0x2D24) +#define IVTV_REG_SPU (0x9050) +#define IVTV_REG_HW_BLOCKS (0x9054) +#define IVTV_REG_VPU (0x9058) +#define IVTV_REG_APU (0xA064) + +#define IVTV_IRQ_ENC_START_CAP (0x1 << 31) +#define IVTV_IRQ_ENC_EOS (0x1 << 30) +#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29) +#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28) +#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27) +#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24) +#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22) +#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20) +#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19) +#define IVTV_IRQ_DMA_ERR (0x1 << 18) +#define IVTV_IRQ_DMA_WRITE (0x1 << 17) +#define IVTV_IRQ_DMA_READ (0x1 << 16) +#define IVTV_IRQ_DEC_VSYNC (0x1 << 10) + +/* IRQ Masks */ +#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ) + +#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS) +#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG) + +/* i2c stuff */ +#define I2C_CLIENTS_MAX 16 + +/* debugging */ + +#define IVTV_DBGFLG_WARN (1 << 0) +#define IVTV_DBGFLG_INFO (1 << 1) +#define IVTV_DBGFLG_API (1 << 2) +#define IVTV_DBGFLG_DMA (1 << 3) +#define IVTV_DBGFLG_IOCTL (1 << 4) +#define IVTV_DBGFLG_I2C (1 << 5) +#define IVTV_DBGFLG_IRQ (1 << 6) +#define IVTV_DBGFLG_DEC (1 << 7) +#define IVTV_DBGFLG_YUV (1 << 8) + +/* NOTE: extra space before comma in 'itv->num , ## args' is required for + gcc-2.95, otherwise it won't compile. */ +#define IVTV_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args) +#define IVTV_DEBUG_API(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +#define IVTV_FB_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) +#define IVTV_FB_DEBUG_API(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_FB_DEBUG_DMA(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_FB_DEBUG_I2C(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_FB_DEBUG_IRQ(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_FB_DEBUG_DEC(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_FB_DEBUG_YUV(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +/* Standard kernel messages */ +#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv%d-fb: " fmt, itv->num , ## args) +#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d-fb: " fmt, itv->num , ## args) +#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args) + +/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ +#define MPEG_FRAME_TYPE_IFRAME 1 +#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 +#define MPEG_FRAME_TYPE_ALL 7 + +/* output modes (cx23415 only) */ +#define OUT_NONE 0 +#define OUT_MPG 1 +#define OUT_YUV 2 +#define OUT_UDMA_YUV 3 +#define OUT_PASSTHROUGH 4 + +#define IVTV_MAX_PGM_INDEX (400) + +extern int ivtv_debug; + + +struct ivtv_options { + int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */ + int cardtype; /* force card type on load */ + int tuner; /* set tuner on load */ + int radio; /* enable/disable radio */ + int newi2c; /* New I2C algorithm */ +}; + +#define IVTV_MBOX_DMA_START 6 +#define IVTV_MBOX_DMA_END 8 +#define IVTV_MBOX_DMA 9 +#define IVTV_MBOX_FIELD_DISPLAYED 8 + +/* ivtv-specific mailbox template */ +struct ivtv_mailbox { + u32 flags; + u32 cmd; + u32 retval; + u32 timeout; + u32 data[CX2341X_MBOX_MAX_DATA]; +}; + +struct ivtv_api_cache { + unsigned long last_jiffies; /* when last command was issued */ + u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */ +}; + +struct ivtv_mailbox_data { + volatile struct ivtv_mailbox __iomem *mbox; + /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes. + If the bit is set, then the corresponding mailbox is in use by the driver. */ + unsigned long busy; + u8 max_mbox; +}; + +/* per-buffer bit flags */ +#define IVTV_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ + +/* per-stream, s_flags */ +#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */ +#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */ +#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */ + +#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */ +#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ +#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ +#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */ +#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */ +#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */ + +/* per-ivtv, i_flags */ +#define IVTV_F_I_DMA 0 /* DMA in progress */ +#define IVTV_F_I_UDMA 1 /* UDMA in progress */ +#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */ +#define IVTV_F_I_SPEED_CHANGE 3 /* A speed change is in progress */ +#define IVTV_F_I_EOS 4 /* End of encoder stream reached */ +#define IVTV_F_I_RADIO_USER 5 /* The radio tuner is selected */ +#define IVTV_F_I_DIG_RST 6 /* Reset digitizer */ +#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */ +#define IVTV_F_I_ENC_VBI 8 /* VBI DMA */ +#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */ +#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */ +#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */ +#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */ +#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */ +#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */ +#define IVTV_F_I_WORK_HANDLER_VBI 15 /* there is work to be done for VBI */ +#define IVTV_F_I_WORK_HANDLER_YUV 16 /* there is work to be done for YUV */ + +/* Event notifications */ +#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ +#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */ +#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */ +#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */ + +/* Scatter-Gather array element, used in DMA transfers */ +struct ivtv_SG_element { + u32 src; + u32 dst; + u32 size; +}; + +struct ivtv_user_dma { + struct mutex lock; + int page_count; + struct page *map[IVTV_DMA_SG_OSD_ENT]; + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT]; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT]; +}; + +struct ivtv_dma_page_info { + unsigned long uaddr; + unsigned long first; + unsigned long last; + unsigned int offset; + unsigned int tail; + int page_count; +}; + +struct ivtv_buffer { + struct list_head list; + dma_addr_t dma_handle; + unsigned long b_flags; + char *buf; + + u32 bytesused; + u32 readpos; +}; + +struct ivtv_queue { + struct list_head list; + u32 buffers; + u32 length; + u32 bytesused; +}; + +struct ivtv; /* forward reference */ + +struct ivtv_stream { + /* These first four fields are always set, even if the stream + is not actually created. */ + struct video_device *v4l2dev; /* NULL when stream not created */ + struct ivtv *itv; /* for ease of use */ + const char *name; /* name of the stream */ + int type; /* stream type */ + + u32 id; + spinlock_t qlock; /* locks access to the queues */ + unsigned long s_flags; /* status flags, see above */ + int dma; /* can be PCI_DMA_TODEVICE, + PCI_DMA_FROMDEVICE or + PCI_DMA_NONE */ + u32 dma_offset; + u32 dma_backup; + u64 dma_pts; + + int subtype; + wait_queue_head_t waitq; + u32 dma_last_offset; + + /* Buffer Stats */ + u32 buffers; + u32 buf_size; + u32 buffers_stolen; + + /* Buffer Queues */ + struct ivtv_queue q_free; /* free buffers */ + struct ivtv_queue q_full; /* full buffers */ + struct ivtv_queue q_io; /* waiting for I/O */ + struct ivtv_queue q_dma; /* waiting for DMA */ + struct ivtv_queue q_predma; /* waiting for DMA */ + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element *SGarray; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist *SGlist; +}; + +struct ivtv_open_id { + u32 open_id; + int type; + enum v4l2_priority prio; + struct ivtv *itv; +}; + +#define IVTV_YUV_UPDATE_HORIZONTAL 0x01 +#define IVTV_YUV_UPDATE_VERTICAL 0x02 + +struct yuv_frame_info +{ + u32 update; + int src_x; + int src_y; + unsigned int src_w; + unsigned int src_h; + int dst_x; + int dst_y; + unsigned int dst_w; + unsigned int dst_h; + int pan_x; + int pan_y; + u32 vis_w; + u32 vis_h; + u32 interlaced_y; + u32 interlaced_uv; + int tru_x; + u32 tru_w; + u32 tru_h; + u32 offset_y; +}; + +#define IVTV_YUV_MODE_INTERLACED 0x00 +#define IVTV_YUV_MODE_PROGRESSIVE 0x01 +#define IVTV_YUV_MODE_AUTO 0x02 +#define IVTV_YUV_MODE_MASK 0x03 + +#define IVTV_YUV_SYNC_EVEN 0x00 +#define IVTV_YUV_SYNC_ODD 0x04 +#define IVTV_YUV_SYNC_MASK 0x04 + +struct yuv_playback_info +{ + u32 reg_2834; + u32 reg_2838; + u32 reg_283c; + u32 reg_2840; + u32 reg_2844; + u32 reg_2848; + u32 reg_2854; + u32 reg_285c; + u32 reg_2864; + + u32 reg_2870; + u32 reg_2874; + u32 reg_2890; + u32 reg_2898; + u32 reg_289c; + + u32 reg_2918; + u32 reg_291c; + u32 reg_2920; + u32 reg_2924; + u32 reg_2928; + u32 reg_292c; + u32 reg_2930; + + u32 reg_2934; + + u32 reg_2938; + u32 reg_293c; + u32 reg_2940; + u32 reg_2944; + u32 reg_2948; + u32 reg_294c; + u32 reg_2950; + u32 reg_2954; + u32 reg_2958; + u32 reg_295c; + u32 reg_2960; + u32 reg_2964; + u32 reg_2968; + u32 reg_296c; + + u32 reg_2970; + + int v_filter_1; + int v_filter_2; + int h_filter; + + u32 osd_x_offset; + u32 osd_y_offset; + + u32 osd_x_pan; + u32 osd_y_pan; + + u32 osd_vis_w; + u32 osd_vis_h; + + int decode_height; + + int frame_interlaced; + int frame_interlaced_last; + + int lace_mode; + int lace_threshold; + int lace_sync_field; + + atomic_t next_dma_frame; + atomic_t next_fill_frame; + + u32 yuv_forced_update; + int update_frame; + struct yuv_frame_info new_frame_info[4]; + struct yuv_frame_info old_frame_info; + struct yuv_frame_info old_frame_info_args; + + void *blanking_ptr; + dma_addr_t blanking_dmaptr; +}; + +#define IVTV_VBI_FRAMES 32 + +/* VBI data */ +struct vbi_info { + u32 dec_start; + u32 enc_start, enc_size; + int fpi; + u32 frame; + u32 dma_offset; + u8 cc_data_odd[256]; + u8 cc_data_even[256]; + int cc_pos; + u8 cc_no_update; + u8 vps[5]; + u8 vps_found; + int wss; + u8 wss_found; + u8 wss_no_update; + u32 raw_decoder_line_size; + u8 raw_decoder_sav_odd_field; + u8 raw_decoder_sav_even_field; + u32 sliced_decoder_line_size; + u8 sliced_decoder_sav_odd_field; + u8 sliced_decoder_sav_even_field; + struct v4l2_format in; + /* convenience pointer to sliced struct in vbi_in union */ + struct v4l2_sliced_vbi_format *sliced_in; + u32 service_set_in; + u32 service_set_out; + int insert_mpeg; + + /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. + One for /dev/vbi0 and one for /dev/vbi8 */ + struct v4l2_sliced_vbi_data sliced_data[36]; + struct v4l2_sliced_vbi_data sliced_dec_data[36]; + + /* Buffer for VBI data inserted into MPEG stream. + The first byte is a dummy byte that's never used. + The next 16 bytes contain the MPEG header for the VBI data, + the remainder is the actual VBI data. + The max size accepted by the MPEG VBI reinsertion turns out + to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, + where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is + a single line header byte and 2 * 18 is the number of VBI lines per frame. + + However, it seems that the data must be 1K aligned, so we have to + pad the data until the 1 or 2 K boundary. + + This pointer array will allocate 2049 bytes to store each VBI frame. */ + u8 *sliced_mpeg_data[IVTV_VBI_FRAMES]; + u32 sliced_mpeg_size[IVTV_VBI_FRAMES]; + struct ivtv_buffer sliced_mpeg_buf; + u32 inserted_frame; + + u32 start[2], count; + u32 raw_size; + u32 sliced_size; +}; + +/* forward declaration of struct defined in ivtv-cards.h */ +struct ivtv_card; + +/* Struct to hold info about ivtv cards */ +struct ivtv { + int num; /* board number, -1 during init! */ + char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */ + struct pci_dev *dev; /* PCI device */ + const struct ivtv_card *card; /* card information */ + const char *card_name; /* full name of the card */ + u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */ + u8 is_50hz; + u8 is_60hz; + u8 is_out_50hz; + u8 is_out_60hz; + u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */ + u8 nof_inputs; /* number of video inputs */ + u8 nof_audio_inputs; /* number of audio inputs */ + u32 v4l2_cap; /* V4L2 capabilities of card */ + u32 hw_flags; /* Hardware description of the board */ + + /* controlling Video decoder function */ + int (*video_dec_func)(struct ivtv *, unsigned int, void *); + + struct ivtv_options options; /* User options */ + int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */ + struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* Stream data */ + int speed; + u8 speed_mute_audio; + unsigned long i_flags; /* global ivtv flags */ + atomic_t capturing; /* count number of active capture streams */ + atomic_t decoding; /* count number of active decoding streams */ + u32 irq_rr_idx; /* Round-robin stream index */ + int cur_dma_stream; /* index of stream doing DMA */ + u32 dma_data_req_offset; + u32 dma_data_req_size; + int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */ + spinlock_t lock; /* lock access to this struct */ + int search_pack_header; + + spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ + + /* User based DMA for OSD */ + struct ivtv_user_dma udma; + + int open_id; /* incremented each time an open occurs, used as unique ID. + starts at 1, so 0 can be used as uninitialized value + in the stream->id. */ + + u32 base_addr; + u32 irqmask; + + struct v4l2_prio_state prio; + struct workqueue_struct *irq_work_queues; + struct work_struct irq_work_queue; + struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */ + + struct vbi_info vbi; + + struct ivtv_mailbox_data enc_mbox; + struct ivtv_mailbox_data dec_mbox; + struct ivtv_api_cache api_cache[256]; /* Cached API Commands */ + + u8 card_rev; + volatile void __iomem *enc_mem, *dec_mem, *reg_mem; + + u32 pgm_info_offset; + u32 pgm_info_num; + u32 pgm_info_write_idx; + u32 pgm_info_read_idx; + struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; + + u64 mpg_data_received; + u64 vbi_data_inserted; + + wait_queue_head_t cap_w; + /* when the next decoder event arrives this queue is woken up */ + wait_queue_head_t event_waitq; + /* when the next decoder vsync arrives this queue is woken up */ + wait_queue_head_t vsync_waitq; + /* when the current DMA is finished this queue is woken up */ + wait_queue_head_t dma_waitq; + + /* OSD support */ + unsigned long osd_video_pbase; + int osd_global_alpha_state; /* 0=off : 1=on */ + int osd_local_alpha_state; /* 0=off : 1=on */ + int osd_color_key_state; /* 0=off : 1=on */ + u8 osd_global_alpha; /* Current global alpha */ + u32 osd_color_key; /* Current color key */ + u32 osd_pixelformat; /* Current pixel format */ + struct v4l2_rect osd_rect; /* Current OSD position and size */ + struct v4l2_rect main_rect; /* Current Main window position and size */ + + u32 last_dec_timing[3]; /* Store last retrieved pts/scr/frame values */ + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + struct mutex i2c_bus_lock; + int i2c_state; + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + + /* v4l2 and User settings */ + + /* codec settings */ + struct cx2341x_mpeg_params params; + u32 audio_input; + u32 active_input; + u32 active_output; + v4l2_std_id std; + v4l2_std_id std_out; + v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ + u8 audio_stereo_mode; + u8 audio_bilingual_mode; + + /* dualwatch */ + unsigned long dualwatch_jiffies; + u16 dualwatch_stereo_mode; + + /* Digitizer type */ + int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ + + u32 lastVsyncFrame; + + struct yuv_playback_info yuv_info; + struct osd_info *osd_info; +}; + +/* Globals */ +extern struct ivtv *ivtv_cards[]; +extern int ivtv_cards_active; +extern int ivtv_first_minor; +extern spinlock_t ivtv_cards_lock; + +/*==============Prototypes==================*/ + +/* Hardware/IRQ */ +void ivtv_set_irq_mask(struct ivtv *itv, u32 mask); +void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask); + +/* try to set output mode, return current mode. */ +int ivtv_set_output_mode(struct ivtv *itv, int mode); + +/* return current output stream based on current mode */ +struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv); + +/* Return non-zero if a signal is pending */ +int ivtv_sleep_timeout(int timeout, int intr); + +/* Wait on queue, returns -EINTR if interrupted */ +int ivtv_waitq(wait_queue_head_t *waitq); + +/* Read Hauppauge eeprom */ +struct tveeprom; /* forward reference */ +void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv); + +/* This is a PCI post thing, where if the pci register is not read, then + the write doesn't always take effect right away. By reading back the + register any pending PCI writes will be performed (in order), and so + you can be sure that the writes are guaranteed to be done. + + Rarely needed, only in some timing sensitive cases. + Apparently if this is not done some motherboards seem + to kill the firmware and get into the broken state until computer is + rebooted. */ +#define write_sync(val, reg) \ + do { writel(val, reg); readl(reg); } while (0) + +#define read_reg(reg) readl(itv->reg_mem + (reg)) +#define write_reg(val, reg) writel(val, itv->reg_mem + (reg)) +#define write_reg_sync(val, reg) \ + do { write_reg(val, reg); read_reg(reg); } while (0) + +#define read_enc(addr) readl(itv->enc_mem + (u32)(addr)) +#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr)) +#define write_enc_sync(val, addr) \ + do { write_enc(val, addr); read_enc(addr); } while (0) + +#define read_dec(addr) readl(itv->dec_mem + (u32)(addr)) +#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr)) +#define write_dec_sync(val, addr) \ + do { write_dec(val, addr); read_dec(addr); } while (0) + +#endif /* IVTV_DRIVER_H */ diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c new file mode 100644 index 0000000..1637097 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -0,0 +1,921 @@ +/* + file operation functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-vbi.h" +#include "ivtv-mailbox.h" +#include "ivtv-audio.h" +#include "ivtv-streams.h" +#include "ivtv-yuv.h" +#include "ivtv-controls.h" +#include "ivtv-ioctl.h" + +/* This function tries to claim the stream for a specific file descriptor. + If no one else is using this stream then the stream is claimed and + associated VBI streams are also automatically claimed. + Possible error returns: -EBUSY if someone else has claimed + the stream or 0 on success. */ +int ivtv_claim_stream(struct ivtv_open_id *id, int type) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[type]; + struct ivtv_stream *s_vbi; + int vbi_type; + + if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + /* someone already claimed this stream */ + if (s->id == id->open_id) { + /* yes, this file descriptor did. So that's OK. */ + return 0; + } + if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI || + type == IVTV_ENC_STREAM_TYPE_VBI)) { + /* VBI is handled already internally, now also assign + the file descriptor to this stream for external + reading of the stream. */ + s->id = id->open_id; + IVTV_DEBUG_INFO("Start Read VBI\n"); + return 0; + } + /* someone else is using this stream already */ + IVTV_DEBUG_INFO("Stream %d is busy\n", type); + return -EBUSY; + } + s->id = id->open_id; + if (type == IVTV_DEC_STREAM_TYPE_VBI) { + /* Enable reinsertion interrupt */ + ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + } + + /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI, + IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI + (provided VBI insertion is on and sliced VBI is selected), for all + other streams we're done */ + if (type == IVTV_DEC_STREAM_TYPE_MPG) { + vbi_type = IVTV_DEC_STREAM_TYPE_VBI; + } else if (type == IVTV_ENC_STREAM_TYPE_MPG && + itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) { + vbi_type = IVTV_ENC_STREAM_TYPE_VBI; + } else { + return 0; + } + s_vbi = &itv->streams[vbi_type]; + + if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) { + /* Enable reinsertion interrupt */ + if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + } + /* mark that it is used internally */ + set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags); + return 0; +} + +/* This function releases a previously claimed stream. It will take into + account associated VBI streams. */ +void ivtv_release_stream(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi; + + s->id = -1; + if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) && + test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { + /* this stream is still in use internally */ + return; + } + if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name); + return; + } + + ivtv_flush_queues(s); + + /* disable reinsertion interrupt */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + + /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI, + IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI, + for all other streams we're done */ + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; + else if (s->type == IVTV_ENC_STREAM_TYPE_MPG) + s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + else + return; + + /* clear internal use flag */ + if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) { + /* was already cleared */ + return; + } + if (s_vbi->id != -1) { + /* VBI stream still claimed by a file descriptor */ + return; + } + /* disable reinsertion interrupt */ + if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags); + ivtv_flush_queues(s_vbi); +} + +static void ivtv_dualwatch(struct ivtv *itv) +{ + struct v4l2_tuner vt; + u16 new_bitmap; + u16 new_stereo_mode; + const u16 stereo_mask = 0x0300; + const u16 dual = 0x0200; + + new_stereo_mode = itv->params.audio_properties & stereo_mask; + memset(&vt, 0, sizeof(vt)); + ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt); + if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) + new_stereo_mode = dual; + + if (new_stereo_mode == itv->dualwatch_stereo_mode) + return; + + new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask); + + IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", + itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); + + if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) { + itv->dualwatch_stereo_mode = new_stereo_mode; + return; + } + IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); +} + +static void ivtv_update_pgm_info(struct ivtv *itv) +{ + u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24; + int cnt; + int i = 0; + + if (wr_idx >= itv->pgm_info_num) { + IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num); + return; + } + cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num; + while (i < cnt) { + int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; + struct v4l2_enc_idx_entry *e = itv->pgm_info + idx; + u32 addr = itv->pgm_info_offset + 4 + idx * 24; + const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 }; + + e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32); + if (e->offset > itv->mpg_data_received) { + break; + } + e->offset += itv->vbi_data_inserted; + e->length = read_enc(addr); + e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32); + e->flags = mapping[read_enc(addr + 12) & 3]; + i++; + } + itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; +} + +static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + struct ivtv_buffer *buf; + DEFINE_WAIT(wait); + + *err = 0; + while (1) { + if (s->type == IVTV_ENC_STREAM_TYPE_MPG) { + /* Process pending program info updates and pending VBI data */ + ivtv_update_pgm_info(itv); + + if (jiffies - itv->dualwatch_jiffies > HZ) { + itv->dualwatch_jiffies = jiffies; + ivtv_dualwatch(itv); + } + + if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { + while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) { + /* byteswap and process VBI data */ + ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type); + ivtv_enqueue(s_vbi, buf, &s_vbi->q_free); + } + } + buf = &itv->vbi.sliced_mpeg_buf; + if (buf->readpos != buf->bytesused) { + return buf; + } + } + + /* do we have leftover data? */ + buf = ivtv_dequeue(s, &s->q_io); + if (buf) + return buf; + + /* do we have new data? */ + buf = ivtv_dequeue(s, &s->q_full); + if (buf) { + if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags)) + return buf; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG) + /* byteswap MPG data */ + ivtv_buf_swap(buf); + else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) { + /* byteswap and process VBI data */ + ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type); + } + return buf; + } + /* return if file was opened with O_NONBLOCK */ + if (non_block) { + *err = -EAGAIN; + return NULL; + } + + /* return if end of stream */ + if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + IVTV_DEBUG_INFO("EOS %s\n", s->name); + return NULL; + } + + /* wait for more data to arrive */ + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become available before we were added to the waitqueue */ + if (!s->q_full.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + if (signal_pending(current)) { + /* return if a signal was received */ + IVTV_DEBUG_INFO("User stopped %s\n", s->name); + *err = -EINTR; + return NULL; + } + } +} + +static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv) +{ + int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; + + itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx]; + itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx]; + itv->vbi.sliced_mpeg_buf.readpos = 0; +} + +static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf, + char __user *ubuf, size_t ucount) +{ + struct ivtv *itv = s->itv; + size_t len = buf->bytesused - buf->readpos; + + if (len > ucount) len = ucount; + if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG && + itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) { + const char *start = buf->buf + buf->readpos; + const char *p = start + 1; + const u8 *q; + u8 ch = itv->search_pack_header ? 0xba : 0xe0; + int stuffing, i; + + while (start + len > p && (q = memchr(p, 0, start + len - p))) { + p = q + 1; + if ((char *)q + 15 >= buf->buf + buf->bytesused || + q[1] != 0 || q[2] != 1 || q[3] != ch) { + continue; + } + if (!itv->search_pack_header) { + if ((q[6] & 0xc0) != 0x80) + continue; + if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) || + ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) { + ch = 0xba; + itv->search_pack_header = 1; + p = q + 9; + } + continue; + } + stuffing = q[13] & 7; + /* all stuffing bytes must be 0xff */ + for (i = 0; i < stuffing; i++) + if (q[14 + i] != 0xff) + break; + if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 && + q[14 + stuffing] == 0 && q[15 + stuffing] == 0 && + q[16 + stuffing] == 1) { + itv->search_pack_header = 0; + len = (char *)q - start; + ivtv_setup_sliced_vbi_buf(itv); + break; + } + } + } + if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { + IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name); + return -EFAULT; + } + /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount, + buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len, + buf == &itv->vbi.sliced_mpeg_buf); */ + buf->readpos += len; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf) + itv->mpg_data_received += len; + return len; +} + +static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block) +{ + struct ivtv *itv = s->itv; + size_t tot_written = 0; + int single_frame = 0; + + if (atomic_read(&itv->capturing) == 0 && s->id == -1) { + /* shouldn't happen */ + IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name); + return -EIO; + } + + /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should + arrive one-by-one, so make sure we never output more than one VBI frame at a time */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI || + (s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set)) + single_frame = 1; + + for (;;) { + struct ivtv_buffer *buf; + int rc; + + buf = ivtv_get_buffer(s, non_block, &rc); + if (buf == NULL && rc == -EAGAIN && tot_written) + break; + if (buf == NULL) + return rc; + rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written); + if (buf != &itv->vbi.sliced_mpeg_buf) { + ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io); + } + else if (buf->readpos == buf->bytesused) { + int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; + itv->vbi.sliced_mpeg_size[idx] = 0; + itv->vbi.inserted_frame++; + itv->vbi_data_inserted += buf->bytesused; + } + if (rc < 0) + return rc; + tot_written += rc; + + if (tot_written == tot_count || single_frame) + break; + } + return tot_written; +} + +static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count, + loff_t *pos, int non_block) +{ + ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0; + struct ivtv *itv = s->itv; + + IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc); + if (rc > 0) + pos += rc; + return rc; +} + +int ivtv_start_capture(struct ivtv_open_id *id) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + struct ivtv_stream *s_vbi; + + if (s->type == IVTV_ENC_STREAM_TYPE_RAD || + s->type == IVTV_DEC_STREAM_TYPE_MPG || + s->type == IVTV_DEC_STREAM_TYPE_YUV || + s->type == IVTV_DEC_STREAM_TYPE_VOUT) { + /* you cannot read from these stream types. */ + return -EPERM; + } + + /* Try to claim this stream. */ + if (ivtv_claim_stream(id, s->type)) + return -EBUSY; + + /* This stream does not need to start capturing */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return 0; + } + + /* If capture is already in progress, then we also have to + do nothing extra. */ + if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return 0; + } + + /* Start VBI capture if required */ + s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { + /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed + automatically when the MPG stream is claimed. + We only need to start the VBI capturing. */ + if (ivtv_start_v4l2_encode_stream(s_vbi)) { + IVTV_DEBUG_WARN("VBI capture start failed\n"); + + /* Failure, clean up and return an error */ + clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + /* also releases the associated VBI stream */ + ivtv_release_stream(s); + return -EIO; + } + IVTV_DEBUG_INFO("VBI insertion started\n"); + } + + /* Tell the card to start capturing */ + if (!ivtv_start_v4l2_encode_stream(s)) { + /* We're done */ + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + /* Resume a possibly paused encoder */ + if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); + return 0; + } + + /* failure, clean up */ + IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); + + /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released + automatically when the MPG stream is released. + We only need to stop the VBI capturing. */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { + ivtv_stop_v4l2_encode_stream(s_vbi, 0); + clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); + } + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + ivtv_release_stream(s); + return -EIO; +} + +ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int rc; + + IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name); + + rc = ivtv_start_capture(id); + if (rc) + return rc; + return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); +} + +int ivtv_start_decoding(struct ivtv_open_id *id, int speed) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + if (atomic_read(&itv->decoding) == 0) { + if (ivtv_claim_stream(id, s->type)) { + /* someone else is using this stream already */ + IVTV_DEBUG_WARN("start decode, stream already claimed\n"); + return -EBUSY; + } + ivtv_start_v4l2_decode_stream(s, 0); + } + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + return ivtv_set_speed(itv, speed); + return 0; +} + +ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + struct ivtv_buffer *buf; + struct ivtv_queue q; + int bytes_written = 0; + int mode; + int rc; + DEFINE_WAIT(wait); + + IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name); + + if (s->type != IVTV_DEC_STREAM_TYPE_MPG && + s->type != IVTV_DEC_STREAM_TYPE_YUV && + s->type != IVTV_DEC_STREAM_TYPE_VOUT) + /* not decoder streams */ + return -EPERM; + + /* Try to claim this stream */ + if (ivtv_claim_stream(id, s->type)) + return -EBUSY; + + /* This stream does not need to start any decoding */ + if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return ivtv_write_vbi(itv, user_buf, count); + } + + mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV; + + if (ivtv_set_output_mode(itv, mode) != mode) { + ivtv_release_stream(s); + return -EBUSY; + } + ivtv_queue_init(&q); + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + +retry: + for (;;) { + /* Gather buffers */ + while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io))) + ivtv_enqueue(s, buf, &q); + while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) { + ivtv_enqueue(s, buf, &q); + } + if (q.buffers) + break; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become free before we were added to the waitqueue */ + if (!s->q_free.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + if (signal_pending(current)) { + IVTV_DEBUG_INFO("User stopped %s\n", s->name); + return -EINTR; + } + } + + /* copy user data into buffers */ + while ((buf = ivtv_dequeue(s, &q))) { + /* Make sure we really got all the user data */ + rc = ivtv_buf_copy_from_user(s, buf, user_buf, count); + + if (rc < 0) { + ivtv_queue_move(s, &q, NULL, &s->q_free, 0); + return rc; + } + user_buf += rc; + count -= rc; + bytes_written += rc; + + if (buf->bytesused != s->buf_size) { + /* incomplete, leave in q_io for next time */ + ivtv_enqueue(s, buf, &s->q_io); + break; + } + /* Byteswap MPEG buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + ivtv_buf_swap(buf); + ivtv_enqueue(s, buf, &s->q_full); + } + + /* Start decoder (returns 0 if already started) */ + rc = ivtv_start_decoding(id, itv->speed); + if (rc) { + IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name); + + /* failure, clean up */ + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return rc; + } + if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) { + if (s->q_full.length >= itv->dma_data_req_size) { + int got_sig; + + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (!(got_sig = signal_pending(current)) && + test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + if (got_sig) { + IVTV_DEBUG_INFO("User interrupted %s\n", s->name); + return -EINTR; + } + + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); + ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1); + } + } + /* more user data is available, wait until buffers become free + to transfer the rest. */ + if (count && !(filp->f_flags & O_NONBLOCK)) + goto retry; + IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); + return bytes_written; +} + +unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int res = 0; + + /* add stream's waitq to the poll list */ + poll_wait(filp, &s->waitq, wait); + + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || + test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + res = POLLPRI; + + /* Allow write if buffers are available for writing */ + if (s->q_free.buffers) + res |= POLLOUT | POLLWRNORM; + return res; +} + +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + /* Start a capture if there is none */ + if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + int rc = ivtv_start_capture(id); + + if (rc) { + IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n", + s->name, rc); + return POLLERR; + } + } + + /* add stream's waitq to the poll list */ + poll_wait(filp, &s->waitq, wait); + + if (eof || s->q_full.length) + return POLLIN | POLLRDNORM; + return 0; +} + +void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + /* 'Unclaim' this stream */ + + /* Stop capturing */ + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + + IVTV_DEBUG_INFO("close stopping capture\n"); + /* Special case: a running VBI capture for VBI insertion + in the mpeg stream. Need to stop that too. */ + if (id->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) && + !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { + IVTV_DEBUG_INFO("close stopping embedded VBI capture\n"); + ivtv_stop_v4l2_encode_stream(s_vbi, 0); + } + if ((id->type == IVTV_DEC_STREAM_TYPE_VBI || + id->type == IVTV_ENC_STREAM_TYPE_VBI) && + test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { + /* Also used internally, don't stop capturing */ + s->id = -1; + } + else { + ivtv_stop_v4l2_encode_stream(s, gop_end); + } + } + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + ivtv_release_stream(s); +} + +static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + /* Stop decoding */ + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + IVTV_DEBUG_INFO("close stopping decode\n"); + + ivtv_stop_v4l2_decode_stream(s, flags, pts); + } + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + if (id->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) { + /* Restore registers we've changed & clean up any mess we've made */ + ivtv_yuv_close(itv); + } + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV) + itv->output_mode = OUT_NONE; + else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG) + itv->output_mode = OUT_NONE; + + itv->speed = 0; + ivtv_release_stream(s); +} + +int ivtv_v4l2_close(struct inode *inode, struct file *filp) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + v4l2_prio_close(&itv->prio, &id->prio); + + /* Easy case first: this stream was never claimed by us */ + if (s->id != id->open_id) { + kfree(id); + return 0; + } + + /* 'Unclaim' this stream */ + + /* Stop radio */ + if (id->type == IVTV_ENC_STREAM_TYPE_RAD) { + /* Closing radio device, return to TV mode */ + ivtv_mute(itv); + /* Mark that the radio is no longer in use */ + clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); + /* Switch tuner to TV */ + ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + /* Select correct audio input (i.e. TV tuner or Line in) */ + ivtv_audio_set_io(itv); + /* Done! Unmute and continue. */ + ivtv_unmute(itv); + ivtv_release_stream(s); + } else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { + ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + } else { + ivtv_stop_capture(id, 0); + } + kfree(id); + return 0; +} + +int ivtv_v4l2_open(struct inode *inode, struct file *filp) +{ + int x, y = 0; + struct ivtv_open_id *item; + struct ivtv *itv = NULL; + struct ivtv_stream *s = NULL; + int minor = MINOR(inode->i_rdev); + + /* Find which card this open was on */ + spin_lock(&ivtv_cards_lock); + for (x = 0; itv == NULL && x < ivtv_cards_active; x++) { + /* find out which stream this open was on */ + for (y = 0; y < IVTV_MAX_STREAMS; y++) { + s = &ivtv_cards[x]->streams[y]; + if (s->v4l2dev && s->v4l2dev->minor == minor) { + itv = ivtv_cards[x]; + break; + } + } + } + spin_unlock(&ivtv_cards_lock); + + if (itv == NULL) { + /* Couldn't find a device registered + on that minor, shouldn't happen! */ + printk(KERN_WARNING "ivtv: no ivtv device found on minor %d\n", minor); + return -ENXIO; + } + + if (y == IVTV_DEC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) + return -EBUSY; + + if (y == IVTV_DEC_STREAM_TYPE_YUV && + test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags)) + return -EBUSY; + + if (y == IVTV_DEC_STREAM_TYPE_YUV) { + if (read_reg(0x82c) == 0) { + IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n"); + /* return -ENODEV; */ + } + ivtv_udma_alloc(itv); + } + + /* Allocate memory */ + item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); + if (NULL == item) { + IVTV_DEBUG_WARN("nomem on v4l2 open\n"); + return -ENOMEM; + } + item->itv = itv; + item->type = y; + v4l2_prio_open(&itv->prio, &item->prio); + + item->open_id = itv->open_id++; + filp->private_data = item; + + if (item->type == IVTV_ENC_STREAM_TYPE_RAD) { + /* Try to claim this stream */ + if (ivtv_claim_stream(item, item->type)) { + /* No, it's already in use */ + kfree(item); + return -EBUSY; + } + + /* We have the radio */ + ivtv_mute(itv); + /* Switch tuner to radio */ + ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL); + /* Mark that the radio is being used. */ + set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); + /* Select the correct audio input (i.e. radio tuner) */ + ivtv_audio_set_io(itv); + /* Done! Unmute and continue. */ + ivtv_unmute(itv); + } + + /* YUV or MPG Decoding Mode? */ + if (y == IVTV_DEC_STREAM_TYPE_MPG) + clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); + else if (y == IVTV_DEC_STREAM_TYPE_YUV) + { + set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); + } + + return 0; +} + +void ivtv_mute(struct ivtv *itv) +{ + struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 }; + + /* Mute sound to avoid pop */ + ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl); + + if (atomic_read(&itv->capturing)) + ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1); + + IVTV_DEBUG_INFO("Mute\n"); +} + +void ivtv_unmute(struct ivtv *itv) +{ + struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 }; + + /* initialize or refresh input */ + if (atomic_read(&itv->capturing) == 0) + ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); + + ivtv_sleep_timeout(HZ / 10, 0); + + if (atomic_read(&itv->capturing)) { + ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); + ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0); + } + + /* Unmute */ + ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl); + IVTV_DEBUG_INFO("Unmute\n"); +} diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h new file mode 100644 index 0000000..74a1745 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-fileops.h @@ -0,0 +1,44 @@ +/* + file operation functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Testing/Debugging */ +int ivtv_v4l2_open(struct inode *inode, struct file *filp); +ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t * pos); +ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count, + loff_t * pos); +int ivtv_v4l2_close(struct inode *inode, struct file *filp); +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait); +unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait); +int ivtv_start_capture(struct ivtv_open_id *id); +void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end); +int ivtv_start_decoding(struct ivtv_open_id *id, int speed); +void ivtv_mute(struct ivtv *itv); +void ivtv_unmute(struct ivtv *itv); + +/* Utilities */ + +/* Try to claim a stream for the filehandle. Return 0 on success, + -EBUSY if stream already claimed. Once a stream is claimed, it + remains claimed until the associated filehandle is closed. */ +int ivtv_claim_stream(struct ivtv_open_id *id, int type); + +/* Release a previously claimed stream. */ +void ivtv_release_stream(struct ivtv_stream *s); diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c new file mode 100644 index 0000000..d4c910b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -0,0 +1,272 @@ +/* + ivtv firmware functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" +#include "ivtv-firmware.h" +#include + +#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE +#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 +#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB +#define IVTV_CMD_VDM_STOP 0x00000000 +#define IVTV_CMD_AO_STOP 0x00000005 +#define IVTV_CMD_APU_PING 0x00000000 +#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE +#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE +#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF +#define IVTV_CMD_SPU_STOP 0x00000001 +#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A +#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640 +#define IVTV_SDRAM_SLEEPTIME (60 * HZ / 100) /* 600 ms */ + +#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg" +#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024) + +/* Encoder/decoder firmware sizes */ +#define IVTV_FW_ENC_SIZE (376836) +#define IVTV_FW_DEC_SIZE (256*1024) + +static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size) +{ + const struct firmware *fw = NULL; + int retries = 3; + +retry: + if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) { + int i; + volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; + const u32 *src = (const u32 *)fw->data; + + /* temporarily allow 256 KB encoding firmwares as well for + compatibility with blackbird cards */ + if (fw->size != size && fw->size != 256 * 1024) { + /* Due to race conditions in firmware loading (esp. with udev <0.95) + the wrong file was sometimes loaded. So we check filesizes to + see if at least the right-sized file was loaded. If not, then we + retry. */ + IVTV_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size); + release_firmware(fw); + retries--; + goto retry; + } + for (i = 0; i < fw->size; i += 4) { + /* no need for endianness conversion on the ppc */ + __raw_writel(*src, dst); + dst++; + src++; + } + release_firmware(fw); + IVTV_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); + return size; + } + IVTV_ERR("unable to open firmware %s (must be %ld bytes)\n", fn, size); + IVTV_ERR("did you put the firmware in the hotplug firmware directory?\n"); + return -ENOMEM; +} + +void ivtv_halt_firmware(struct ivtv *itv) +{ + IVTV_DEBUG_INFO("Preparing for firmware halt.\n"); + if (itv->has_cx23415 && itv->dec_mbox.mbox) + ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0); + if (itv->enc_mbox.mbox) + ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0); + + ivtv_sleep_timeout(HZ / 100, 0); + itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL; + + IVTV_DEBUG_INFO("Stopping VDM\n"); + write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM); + + IVTV_DEBUG_INFO("Stopping AO\n"); + write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO); + + IVTV_DEBUG_INFO("pinging (?) APU\n"); + write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU); + + IVTV_DEBUG_INFO("Stopping VPU\n"); + if (!itv->has_cx23415) + write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU); + else + write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU); + + IVTV_DEBUG_INFO("Resetting Hw Blocks\n"); + write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS); + + IVTV_DEBUG_INFO("Stopping SPU\n"); + write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU); + + ivtv_sleep_timeout(HZ / 100, 0); + + IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n"); + write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE); + + IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n"); + write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH); + + if (itv->has_cx23415) { + IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n"); + write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE); + + IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n"); + write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH); + } + + IVTV_DEBUG_INFO("Sleeping for %dms (600 recommended)\n", + (int)(IVTV_SDRAM_SLEEPTIME * 1000 / HZ)); + ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME, 0); +} + +void ivtv_firmware_versions(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + /* Encoder */ + ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0); + IVTV_INFO("Encoder revision: 0x%08x\n", data[0]); + + if (data[0] != 0x02060039) + IVTV_WARN("Recommended firmware version is 0x02060039.\n"); + + if (itv->has_cx23415) { + /* Decoder */ + ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0); + IVTV_INFO("Decoder revision: 0x%08x\n", data[0]); + } +} + +static int ivtv_firmware_copy(struct ivtv *itv) +{ + IVTV_DEBUG_INFO("Loading encoder image\n"); + if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME, + itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) { + IVTV_DEBUG_WARN("failed loading encoder firmware\n"); + return -3; + } + if (!itv->has_cx23415) + return 0; + + IVTV_DEBUG_INFO("Loading decoder image\n"); + if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME, + itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) { + IVTV_DEBUG_WARN("failed loading decoder firmware\n"); + return -1; + } + return 0; +} + +static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size) +{ + int i; + + /* mailbox is preceeded by a 16 byte 'magic cookie' starting at a 256-byte + address boundary */ + for (i = 0; i < size; i += 0x100) { + if (readl(mem + i) == 0x12345678 && + readl(mem + i + 4) == 0x34567812 && + readl(mem + i + 8) == 0x56781234 && + readl(mem + i + 12) == 0x78123456) { + return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16); + } + } + return NULL; +} + +int ivtv_firmware_init(struct ivtv *itv) +{ + int err; + + ivtv_halt_firmware(itv); + + /* load firmware */ + err = ivtv_firmware_copy(itv); + if (err) { + IVTV_DEBUG_WARN("Error %d loading firmware\n", err); + return err; + } + + /* start firmware */ + write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU); + ivtv_sleep_timeout(HZ / 10, 0); + if (itv->has_cx23415) + write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU); + else + write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU); + ivtv_sleep_timeout(HZ / 10, 0); + + /* find mailboxes and ping firmware */ + itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE); + if (itv->enc_mbox.mbox == NULL) + IVTV_ERR("Encoder mailbox not found\n"); + else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) { + IVTV_ERR("Encoder firmware dead!\n"); + itv->enc_mbox.mbox = NULL; + } + if (itv->enc_mbox.mbox == NULL) + return -ENODEV; + + if (!itv->has_cx23415) + return 0; + + itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE); + if (itv->dec_mbox.mbox == NULL) + IVTV_ERR("Decoder mailbox not found\n"); + else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) { + IVTV_ERR("Decoder firmware dead!\n"); + itv->dec_mbox.mbox = NULL; + } + return itv->dec_mbox.mbox ? 0 : -ENODEV; +} + +void ivtv_init_mpeg_decoder(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + long readbytes; + volatile u8 __iomem *mem_offset; + + data[0] = 0; + data[1] = itv->params.width; /* YUV source width */ + data[2] = itv->params.height; + data[3] = itv->params.audio_properties; /* Audio settings to use, + bitmap. see docs. */ + if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) { + IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n"); + return; + } + + if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) { + IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n"); + return; + } + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); + mem_offset = itv->dec_mem + data[1]; + + if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME, + mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) { + IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n", + IVTV_DECODE_INIT_MPEG_FILENAME); + } else { + ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0); + ivtv_sleep_timeout(HZ / 10, 0); + } + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); +} diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h new file mode 100644 index 0000000..8b2ffe6 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-firmware.h @@ -0,0 +1,25 @@ +/* + ivtv firmware functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_firmware_init(struct ivtv *itv); +void ivtv_firmware_versions(struct ivtv *itv); +void ivtv_halt_firmware(struct ivtv *itv); +void ivtv_init_mpeg_decoder(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c new file mode 100644 index 0000000..bc8f8ca --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -0,0 +1,307 @@ +/* + gpio functions. + Merging GPIO support into driver: + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-gpio.h" +#include + +/* + * GPIO assignment of Yuan MPG600/MPG160 + * + * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 + * OUTPUT IN1 IN0 AM3 AM2 AM1 AM0 + * INPUT DM1 DM0 + * + * IN* : Input selection + * IN1 IN0 + * 1 1 N/A + * 1 0 Line + * 0 1 N/A + * 0 0 Tuner + * + * AM* : Audio Mode + * AM3 0: Normal 1: Mixed(Sub+Main channel) + * AM2 0: Subchannel 1: Main channel + * AM1 0: Stereo 1: Mono + * AM0 0: Normal 1: Mute + * + * DM* : Detected tuner audio Mode + * DM1 0: Stereo 1: Mono + * DM0 0: Multiplex 1: Normal + * + * GPIO Initial Settings + * MPG600 MPG160 + * DIR 0x3080 0x7080 + * OUTPUT 0x000C 0x400C + * + * Special thanks to Makoto Iguchi and Mr. Anonymous + * for analyzing GPIO of MPG160. + * + ***************************************************************************** + * + * GPIO assignment of Avermedia M179 (per information direct from AVerMedia) + * + * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 + * OUTPUT IN0 AM0 IN1 AM1 AM2 IN2 BR0 BR1 + * INPUT + * + * IN* : Input selection + * IN0 IN1 IN2 + * * 1 * Mute + * 0 0 0 Line-In + * 1 0 0 TV Tuner Audio + * 0 0 1 FM Audio + * 1 0 1 Mute + * + * AM* : Audio Mode + * AM0 AM1 AM2 + * 0 0 0 TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP + * 0 0 1 TV Tuner Audio: L_OUT=R_OUT=SAP (SAP) + * 0 1 0 TV Tuner Audio: L_OUT=L, R_OUT=R (stereo) + * 0 1 1 TV Tuner Audio: mute + * 1 * * TV Tuner Audio: L_OUT=R_OUT=(L+R)/2 (mono) + * + * BR* : Audio Sample Rate (BR stands for bitrate for some reason) + * BR0 BR1 + * 0 0 32 kHz + * 0 1 44.1 kHz + * 1 0 48 kHz + * + * DM* : Detected tuner audio Mode + * Unknown currently + * + * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at + * AVerMedia for providing the GPIO information used to add support + * for the M179 cards. + */ + +/********************* GPIO stuffs *********************/ + +/* GPIO registers */ +#define IVTV_REG_GPIO_IN 0x9008 +#define IVTV_REG_GPIO_OUT 0x900c +#define IVTV_REG_GPIO_DIR 0x9020 + +void ivtv_reset_ir_gpio(struct ivtv *itv) +{ + int curdir, curout; + + if (itv->card->type != IVTV_CARD_PVR_150) + return; + IVTV_DEBUG_INFO("Resetting PVR150 IR\n"); + curout = read_reg(IVTV_REG_GPIO_OUT); + curdir = read_reg(IVTV_REG_GPIO_DIR); + curdir |= 0x80; + write_reg(curdir, IVTV_REG_GPIO_DIR); + curout = (curout & ~0xF) | 1; + write_reg(curout, IVTV_REG_GPIO_OUT); + /* We could use something else for smaller time */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + curout |= 2; + write_reg(curout, IVTV_REG_GPIO_OUT); + curdir &= ~0x80; + write_reg(curdir, IVTV_REG_GPIO_DIR); +} + +#ifdef HAVE_XC3028 +int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr) +{ + int curdir, curout; + struct ivtv *itv = (struct ivtv *) priv; + + if (itv->card->type != IVTV_CARD_PG600V2 || itv->options.tuner != TUNER_XCEIVE_XC3028) + return -EINVAL; + IVTV_INFO("Resetting tuner.\n"); + curout = read_reg(IVTV_REG_GPIO_OUT); + curdir = read_reg(IVTV_REG_GPIO_DIR); + curdir |= (1 << 12); /* GPIO bit 12 */ + + curout &= ~(1 << 12); + write_reg(curout, IVTV_REG_GPIO_OUT); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + curout |= (1 << 12); + write_reg(curout, IVTV_REG_GPIO_OUT); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + return 0; +} +#endif + +void ivtv_gpio_init(struct ivtv *itv) +{ + if (itv->card->gpio_init.direction == 0) + return; + + IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); + + /* init output data then direction */ + write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR); +} + +static struct v4l2_queryctrl gpio_ctrl_mute = { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, +}; + +int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg) +{ + struct v4l2_tuner *tuner = arg; + struct v4l2_control *ctrl = arg; + struct v4l2_routing *route = arg; + u16 mask, data; + + switch (command) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + mask = itv->card->gpio_audio_freq.mask; + switch (*(u32 *)arg) { + case 32000: + data = itv->card->gpio_audio_freq.f32000; + break; + case 44100: + data = itv->card->gpio_audio_freq.f44100; + break; + case 48000: + default: + data = itv->card->gpio_audio_freq.f48000; + break; + } + break; + + case VIDIOC_G_TUNER: + mask = itv->card->gpio_audio_detect.mask; + if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) + tuner->rxsubchans = V4L2_TUNER_MODE_STEREO | + V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; + + case VIDIOC_S_TUNER: + mask = itv->card->gpio_audio_mode.mask; + switch (tuner->audmode) { + case V4L2_TUNER_MODE_LANG1: + data = itv->card->gpio_audio_mode.lang1; + break; + case V4L2_TUNER_MODE_LANG2: + data = itv->card->gpio_audio_mode.lang2; + break; + case V4L2_TUNER_MODE_MONO: + data = itv->card->gpio_audio_mode.mono; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + default: + data = itv->card->gpio_audio_mode.stereo; + break; + } + break; + + case AUDC_SET_RADIO: + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.radio; + break; + + case VIDIOC_S_STD: + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.tuner; + break; + + case VIDIOC_INT_S_AUDIO_ROUTING: + if (route->input > 2) + return -EINVAL; + mask = itv->card->gpio_audio_input.mask; + switch (route->input) { + case 0: + data = itv->card->gpio_audio_input.tuner; + break; + case 1: + data = itv->card->gpio_audio_input.linein; + break; + case 2: + default: + data = itv->card->gpio_audio_input.radio; + break; + } + break; + + case VIDIOC_G_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = itv->card->gpio_audio_mute.mute; + ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; + return 0; + + case VIDIOC_S_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; + break; + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + + if (qc->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + *qc = gpio_ctrl_mute; + return 0; + } + + case VIDIOC_LOG_STATUS: + IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), + read_reg(IVTV_REG_GPIO_IN)); + return 0; + + case VIDIOC_INT_S_VIDEO_ROUTING: + if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ + return -EINVAL; + mask = itv->card->gpio_video_input.mask; + if (route->input == 0) + data = itv->card->gpio_video_input.tuner; + else if (route->input == 1) + data = itv->card->gpio_video_input.composite; + else + data = itv->card->gpio_video_input.svideo; + break; + + default: + return -EINVAL; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h new file mode 100644 index 0000000..c301d2a --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-gpio.h @@ -0,0 +1,25 @@ +/* + gpio functions. + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* GPIO stuff */ +void ivtv_gpio_init(struct ivtv *itv); +void ivtv_reset_ir_gpio(struct ivtv *itv); +int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr); +int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg); diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c new file mode 100644 index 0000000..50624c6 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -0,0 +1,748 @@ +/* + I2C functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + This file includes an i2c implementation that was reverse engineered + from the Hauppauge windows driver. Older ivtv versions used i2c-algo-bit, + which whilst fine under most circumstances, had trouble with the Zilog + CPU on the PVR-150 which handles IR functions (occasional inability to + communicate with the chip until it was reset) and also with the i2c + bus being completely unreachable when multiple PVR cards were present. + + The implementation is very similar to i2c-algo-bit, but there are enough + subtle differences that the two are hard to merge. The general strategy + employed by i2c-algo-bit is to use udelay() to implement the timing + when putting out bits on the scl/sda lines. The general strategy taken + here is to poll the lines for state changes (see ivtv_waitscl and + ivtv_waitsda). In addition there are small delays at various locations + which poll the SCL line 5 times (ivtv_scldelay). I would guess that + since this is memory mapped I/O that the length of those delays is tied + to the PCI bus clock. There is some extra code to do with recovery + and retries. Since it is not known what causes the actual i2c problems + in the first place, the only goal if one was to attempt to use + i2c-algo-bit would be to try to make it follow the same code path. + This would be a lot of work, and I'm also not convinced that it would + provide a generic benefit to i2c-algo-bit. Therefore consider this + an engineering solution -- not pretty, but it works. + + Some more general comments about what we are doing: + + The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA) + lines. To communicate on the bus (as a master, we don't act as a slave), + we first initiate a start condition (ivtv_start). We then write the + address of the device that we want to communicate with, along with a flag + that indicates whether this is a read or a write. The slave then issues + an ACK signal (ivtv_ack), which tells us that it is ready for reading / + writing. We then proceed with reading or writing (ivtv_read/ivtv_write), + and finally issue a stop condition (ivtv_stop) to make the bus available + to other masters. + + There is an additional form of transaction where a write may be + immediately followed by a read. In this case, there is no intervening + stop condition. (Only the msp3400 chip uses this method of data transfer). + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-gpio.h" +#include "ivtv-i2c.h" + +#include + +/* i2c implementation for cx23415/6 chip, ivtv project. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + */ +/* i2c stuff */ +#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000 +#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004 +#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008 +#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c + +#ifndef I2C_ADAP_CLASS_TV_ANALOG +#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG +#endif /* I2C_ADAP_CLASS_TV_ANALOG */ + +#define IVTV_CS53L32A_I2C_ADDR 0x11 +#define IVTV_CX25840_I2C_ADDR 0x44 +#define IVTV_SAA7115_I2C_ADDR 0x21 +#define IVTV_SAA7127_I2C_ADDR 0x44 +#define IVTV_SAA717x_I2C_ADDR 0x21 +#define IVTV_MSP3400_I2C_ADDR 0x40 +#define IVTV_HAUPPAUGE_I2C_ADDR 0x50 +#define IVTV_WM8739_I2C_ADDR 0x1a +#define IVTV_WM8775_I2C_ADDR 0x1b +#define IVTV_TEA5767_I2C_ADDR 0x60 +#define IVTV_UPD64031A_I2C_ADDR 0x12 +#define IVTV_UPD64083_I2C_ADDR 0x5c +#define IVTV_TDA985X_I2C_ADDR 0x5b + +/* This array should match the IVTV_HW_ defines */ +static const u8 hw_driverids[] = { + I2C_DRIVERID_CX25840, + I2C_DRIVERID_SAA711X, + I2C_DRIVERID_SAA7127, + I2C_DRIVERID_MSP3400, + I2C_DRIVERID_TUNER, + I2C_DRIVERID_WM8775, + I2C_DRIVERID_CS53L32A, + I2C_DRIVERID_TVEEPROM, + I2C_DRIVERID_SAA711X, + I2C_DRIVERID_TVAUDIO, + I2C_DRIVERID_UPD64031A, + I2C_DRIVERID_UPD64083, + I2C_DRIVERID_SAA717X, + I2C_DRIVERID_WM8739, + 0 /* IVTV_HW_GPIO dummy driver ID */ +}; + +/* This array should match the IVTV_HW_ defines */ +static const char * const hw_drivernames[] = { + "cx2584x", + "saa7115", + "saa7127", + "msp3400", + "tuner", + "wm8775", + "cs53l32a", + "tveeprom", + "saa7114", + "tvaudio", + "upd64031a", + "upd64083", + "saa717x", + "wm8739", + "gpio", +}; + +static int attach_inform(struct i2c_client *client) +{ + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + int i; + + IVTV_DEBUG_I2C("i2c client attach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (itv->i2c_clients[i] == NULL) { + itv->i2c_clients[i] = client; + break; + } + } + if (i == I2C_CLIENTS_MAX) { + IVTV_ERR("insufficient room for new I2C client!\n"); + } + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + int i; + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + + IVTV_DEBUG_I2C("i2c client detach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (itv->i2c_clients[i] == client) { + itv->i2c_clients[i] = NULL; + break; + } + } + IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n", + client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); + + return 0; +} + +/* Set the serial clock line to the desired state */ +static void ivtv_setscl(struct ivtv *itv, int state) +{ + /* write them out */ + /* write bits are inverted */ + write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET); +} + +/* Set the serial data line to the desired state */ +static void ivtv_setsda(struct ivtv *itv, int state) +{ + /* write them out */ + /* write bits are inverted */ + write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET); +} + +/* Read the serial clock line */ +static int ivtv_getscl(struct ivtv *itv) +{ + return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; +} + +/* Read the serial data line */ +static int ivtv_getsda(struct ivtv *itv) +{ + return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; +} + +/* Implement a short delay by polling the serial clock line */ +static void ivtv_scldelay(struct ivtv *itv) +{ + int i; + + for (i = 0; i < 5; ++i) + ivtv_getscl(itv); +} + +/* Wait for the serial clock line to become set to a specific value */ +static int ivtv_waitscl(struct ivtv *itv, int val) +{ + int i; + + ivtv_scldelay(itv); + for (i = 0; i < 1000; ++i) { + if (ivtv_getscl(itv) == val) + return 1; + } + return 0; +} + +/* Wait for the serial data line to become set to a specific value */ +static int ivtv_waitsda(struct ivtv *itv, int val) +{ + int i; + + ivtv_scldelay(itv); + for (i = 0; i < 1000; ++i) { + if (ivtv_getsda(itv) == val) + return 1; + } + return 0; +} + +/* Wait for the slave to issue an ACK */ +static int ivtv_ack(struct ivtv *itv) +{ + int ret = 0; + + if (ivtv_getscl(itv) == 1) { + IVTV_DEBUG_I2C("SCL was high starting an ack\n"); + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n"); + return -EREMOTEIO; + } + } + ivtv_setsda(itv, 1); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitsda(itv, 0)) { + IVTV_DEBUG_I2C("Slave did not ack\n"); + ret = -EREMOTEIO; + } + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n"); + ret = -EREMOTEIO; + } + return ret; +} + +/* Write a single byte to the i2c bus and wait for the slave to ACK */ +static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte) +{ + int i, bit; + + IVTV_DEBUG_I2C("write %x\n",byte); + for (i = 0; i < 8; ++i, byte<<=1) { + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Error setting SCL low\n"); + return -EREMOTEIO; + } + bit = (byte>>7)&1; + ivtv_setsda(itv, bit); + if (!ivtv_waitsda(itv, bit)) { + IVTV_DEBUG_I2C("Error setting SDA\n"); + return -EREMOTEIO; + } + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("Slave not ready for bit\n"); + return -EREMOTEIO; + } + } + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Error setting SCL low\n"); + return -EREMOTEIO; + } + return ivtv_ack(itv); +} + +/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the + final byte) */ +static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack) +{ + int i; + + *byte = 0; + + ivtv_setsda(itv, 1); + ivtv_scldelay(itv); + for (i = 0; i < 8; ++i) { + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("Error setting SCL high\n"); + return -EREMOTEIO; + } + *byte = ((*byte)<<1)|ivtv_getsda(itv); + } + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setsda(itv, nack); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + ivtv_scldelay(itv); + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + IVTV_DEBUG_I2C("read %x\n",*byte); + return 0; +} + +/* Issue a start condition on the i2c bus to alert slaves to prepare for + an address write */ +static int ivtv_start(struct ivtv *itv) +{ + int sda; + + sda = ivtv_getsda(itv); + if (sda != 1) { + IVTV_DEBUG_I2C("SDA was low at start\n"); + ivtv_setsda(itv, 1); + if (!ivtv_waitsda(itv, 1)) { + IVTV_DEBUG_I2C("SDA stuck low\n"); + return -EREMOTEIO; + } + } + if (ivtv_getscl(itv) != 1) { + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("SCL stuck low at start\n"); + return -EREMOTEIO; + } + } + ivtv_setsda(itv, 0); + ivtv_scldelay(itv); + return 0; +} + +/* Issue a stop condition on the i2c bus to release it */ +static int ivtv_stop(struct ivtv *itv) +{ + int i; + + if (ivtv_getscl(itv) != 0) { + IVTV_DEBUG_I2C("SCL not low when stopping\n"); + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("SCL could not be set low\n"); + } + } + ivtv_setsda(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("SCL could not be set high\n"); + return -EREMOTEIO; + } + ivtv_scldelay(itv); + ivtv_setsda(itv, 1); + if (!ivtv_waitsda(itv, 1)) { + IVTV_DEBUG_I2C("resetting I2C\n"); + for (i = 0; i < 16; ++i) { + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + ivtv_scldelay(itv); + ivtv_setsda(itv, 1); + } + ivtv_waitsda(itv, 1); + return -EREMOTEIO; + } + return 0; +} + +/* Write a message to the given i2c slave. do_stop may be 0 to prevent + issuing the i2c stop condition (when following with a read) */ +static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop) +{ + int retry, ret = -EREMOTEIO; + u32 i; + + for (retry = 0; ret != 0 && retry < 8; ++retry) { + ret = ivtv_start(itv); + + if (ret == 0) { + ret = ivtv_sendbyte(itv, addr<<1); + for (i = 0; ret == 0 && i < len; ++i) + ret = ivtv_sendbyte(itv, data[i]); + } + if (ret != 0 || do_stop) { + ivtv_stop(itv); + } + } + if (ret) + IVTV_DEBUG_I2C("i2c write to %x failed\n", addr); + return ret; +} + +/* Read data from the given i2c slave. A stop condition is always issued. */ +static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len) +{ + int retry, ret = -EREMOTEIO; + u32 i; + + for (retry = 0; ret != 0 && retry < 8; ++retry) { + ret = ivtv_start(itv); + if (ret == 0) + ret = ivtv_sendbyte(itv, (addr << 1) | 1); + for (i = 0; ret == 0 && i < len; ++i) { + ret = ivtv_readbyte(itv, &data[i], i == len - 1); + } + ivtv_stop(itv); + } + if (ret) + IVTV_DEBUG_I2C("i2c read from %x failed\n", addr); + return ret; +} + +/* Kernel i2c transfer implementation. Takes a number of messages to be read + or written. If a read follows a write, this will occur without an + intervening stop condition */ +static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct ivtv *itv = i2c_get_adapdata(i2c_adap); + int retval; + int i; + + mutex_lock(&itv->i2c_bus_lock); + for (i = retval = 0; retval == 0 && i < num; i++) { + if (msgs[i].flags & I2C_M_RD) + retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len); + else { + /* if followed by a read, don't stop */ + int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD); + + retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop); + } + } + mutex_unlock(&itv->i2c_bus_lock); + return retval ? retval : num; +} + +/* Kernel i2c capabilities */ +static u32 ivtv_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ivtv_algo = { + .master_xfer = ivtv_xfer, + .functionality = ivtv_functionality, +}; + +/* template for our-bit banger */ +static struct i2c_adapter ivtv_i2c_adap_hw_template = { + .name = "ivtv i2c driver", + .id = I2C_HW_B_CX2341X, + .algo = &ivtv_algo, + .algo_data = NULL, /* filled from template */ + .client_register = attach_inform, + .client_unregister = detach_inform, + .owner = THIS_MODULE, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif +}; + +static void ivtv_setscl_old(void *data, int state) +{ + struct ivtv *itv = (struct ivtv *)data; + + if (state) + itv->i2c_state |= 0x01; + else + itv->i2c_state &= ~0x01; + + /* write them out */ + /* write bits are inverted */ + write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET); +} + +static void ivtv_setsda_old(void *data, int state) +{ + struct ivtv *itv = (struct ivtv *)data; + + if (state) + itv->i2c_state |= 0x01; + else + itv->i2c_state &= ~0x01; + + /* write them out */ + /* write bits are inverted */ + write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET); +} + +static int ivtv_getscl_old(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + + return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; +} + +static int ivtv_getsda_old(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + + return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; +} + +/* template for i2c-bit-algo */ +static struct i2c_adapter ivtv_i2c_adap_template = { + .name = "ivtv i2c driver", + .id = I2C_HW_B_CX2341X, /* algo-bit is OR'd with this */ + .algo = NULL, /* set by i2c-algo-bit */ + .algo_data = NULL, /* filled from template */ + .client_register = attach_inform, + .client_unregister = detach_inform, + .owner = THIS_MODULE, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif +}; + +static struct i2c_algo_bit_data ivtv_i2c_algo_template = { + NULL, /* ?? */ + ivtv_setsda_old, /* setsda function */ + ivtv_setscl_old, /* " */ + ivtv_getsda_old, /* " */ + ivtv_getscl_old, /* " */ + 10, /* udelay */ + 200 /* timeout */ +}; + +static struct i2c_client ivtv_i2c_client_template = { + .name = "ivtv internal", +}; + +int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg) +{ + struct i2c_client *client; + int retval; + int i; + + IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = itv->i2c_clients[i]; + if (client == NULL) { + continue; + } + if (client->driver->command == NULL) { + continue; + } + if (addr == client->addr) { + retval = client->driver->command(client, cmd, arg); + return retval; + } + } + if (cmd != VIDIOC_G_CHIP_IDENT) + IVTV_ERR("i2c addr 0x%02x not found for command 0x%x!\n", addr, cmd); + return -ENODEV; +} + +/* Find the i2c device based on the driver ID and return + its i2c address or -ENODEV if no matching device was found. */ +static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id) +{ + struct i2c_client *client; + int retval = -ENODEV; + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = itv->i2c_clients[i]; + if (client == NULL) + continue; + if (id == client->driver->id) { + retval = client->addr; + break; + } + } + return retval; +} + +/* Find the i2c device name matching the DRIVERID */ +static const char *ivtv_i2c_id_name(u32 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (hw_driverids[i] == id) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device name matching the IVTV_HW_ flag */ +static const char *ivtv_i2c_hw_name(u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device matching the IVTV_HW_ flag and return + its i2c address or -ENODEV if no matching device was found. */ +int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return ivtv_i2c_id_addr(itv, hw_driverids[i]); + return -ENODEV; +} + +/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing. + If hw == IVTV_HW_GPIO then call the gpio handler. */ +int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg) +{ + int addr; + + if (hw == IVTV_HW_GPIO) + return ivtv_gpio(itv, cmd, arg); + if (hw == 0) + return 0; + + addr = ivtv_i2c_hw_addr(itv, hw); + if (addr < 0) { + IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x!\n", + hw, ivtv_i2c_hw_name(hw), cmd); + return addr; + } + return ivtv_call_i2c_client(itv, addr, cmd, arg); +} + +/* Calls i2c device based on I2C driver ID. */ +int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg) +{ + int addr; + + addr = ivtv_i2c_id_addr(itv, id); + if (addr < 0) { + if (cmd != VIDIOC_G_CHIP_IDENT) + IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x!\n", + id, ivtv_i2c_id_name(id), cmd); + return addr; + } + return ivtv_call_i2c_client(itv, addr, cmd, arg); +} + +int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg); +} + +int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg); +} + +int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg); +} + +int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg); +} + +int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg); +} + +int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg); +} + +/* broadcast cmd for all I2C clients and for the gpio subsystem */ +void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg) +{ + if (itv->i2c_adap.algo == NULL) { + IVTV_ERR("adapter is not set"); + return; + } + i2c_clients_command(&itv->i2c_adap, cmd, arg); + if (itv->hw_flags & IVTV_HW_GPIO) + ivtv_gpio(itv, cmd, arg); +} + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG_I2C("i2c init\n"); + + if (itv->options.newi2c > 0) { + memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template, + sizeof(struct i2c_adapter)); + } else { + memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template, + sizeof(struct i2c_adapter)); + memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, + sizeof(struct i2c_algo_bit_data)); + itv->i2c_algo.data = itv; + itv->i2c_adap.algo_data = &itv->i2c_algo; + } + + sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", + itv->num); + i2c_set_adapdata(&itv->i2c_adap, itv); + + memcpy(&itv->i2c_client, &ivtv_i2c_client_template, + sizeof(struct i2c_client)); + itv->i2c_client.adapter = &itv->i2c_adap; + itv->i2c_adap.dev.parent = &itv->dev->dev; + + IVTV_DEBUG_I2C("setting scl and sda to 1\n"); + ivtv_setscl(itv, 1); + ivtv_setsda(itv, 1); + + if (itv->options.newi2c > 0) + return i2c_add_adapter(&itv->i2c_adap); + else + return i2c_bit_add_bus(&itv->i2c_adap); +} + +void __devexit exit_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG_I2C("i2c exit\n"); + + i2c_del_adapter(&itv->i2c_adap); +} diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h new file mode 100644 index 0000000..5d210ad --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-i2c.h @@ -0,0 +1,36 @@ +/* + I2C functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg); + +int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw); +int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg); +int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg); +int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg); +void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg); + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv); +void __devexit exit_ivtv_i2c(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c new file mode 100644 index 0000000..794a6a0 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -0,0 +1,1567 @@ +/* + ioctl system call + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-version.h" +#include "ivtv-mailbox.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-fileops.h" +#include "ivtv-vbi.h" +#include "ivtv-audio.h" +#include "ivtv-video.h" +#include "ivtv-streams.h" +#include "ivtv-yuv.h" +#include "ivtv-ioctl.h" +#include "ivtv-gpio.h" +#include "ivtv-controls.h" +#include "ivtv-cards.h" +#include +#include +#include +#include +#include + +u16 service2vbi(int type) +{ + switch (type) { + case V4L2_SLICED_TELETEXT_B: + return IVTV_SLICED_TYPE_TELETEXT_B; + case V4L2_SLICED_CAPTION_525: + return IVTV_SLICED_TYPE_CAPTION_525; + case V4L2_SLICED_WSS_625: + return IVTV_SLICED_TYPE_WSS_625; + case V4L2_SLICED_VPS: + return IVTV_SLICED_TYPE_VPS; + default: + return 0; + } +} + +static int valid_service_line(int field, int line, int is_pal) +{ + return (is_pal && line >= 6 && (line != 23 || field == 0)) || + (!is_pal && line >= 10 && line < 22); +} + +static u16 select_service_from_set(int field, int line, u16 set, int is_pal) +{ + u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); + int i; + + set = set & valid_set; + if (set == 0 || !valid_service_line(field, line, is_pal)) { + return 0; + } + if (!is_pal) { + if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) + return V4L2_SLICED_CAPTION_525; + } + else { + if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) + return V4L2_SLICED_VPS; + if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) + return V4L2_SLICED_WSS_625; + if (line == 23) + return 0; + } + for (i = 0; i < 32; i++) { + if ((1 << i) & set) + return 1 << i; + } + return 0; +} + +void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + u16 set = fmt->service_set; + int f, l; + + fmt->service_set = 0; + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); + } + } +} + +static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); + set |= fmt->service_lines[f][l]; + } + } + return set != 0; +} + +u16 get_service_set(struct v4l2_sliced_vbi_format *fmt) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + set |= fmt->service_lines[f][l]; + } + } + return set; +} + +static const struct { + v4l2_std_id std; + char *name; +} enum_stds[] = { + { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-L'" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-J" }, + { V4L2_STD_NTSC_M_KR, "NTSC-K" }, +}; + +static const struct v4l2_standard ivtv_std_60hz = +{ + .frameperiod = {.numerator = 1001, .denominator = 30000}, + .framelines = 525, +}; + +static const struct v4l2_standard ivtv_std_50hz = +{ + .frameperiod = {.numerator = 1, .denominator = 25}, + .framelines = 625, +}; + +void ivtv_set_osd_alpha(struct ivtv *itv) +{ + ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3, + itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state); + ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_color_key_state, itv->osd_color_key); +} + +int ivtv_set_speed(struct ivtv *itv, int speed) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + int single_step = (speed == 1 || speed == -1); + DEFINE_WAIT(wait); + + if (speed == 0) speed = 1000; + + /* No change? */ + if (speed == itv->speed && !single_step) + return 0; + + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + + if (single_step && (speed < 0) == (itv->speed < 0)) { + /* Single step video and no need to change direction */ + ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); + itv->speed = speed; + return 0; + } + if (single_step) + /* Need to change direction */ + speed = speed < 0 ? -1000 : 1000; + + data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0; + data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0; + data[1] = (speed < 0); + data[2] = speed < 0 ? 3 : 7; + data[3] = itv->params.video_b_frames; + data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0; + data[5] = 0; + data[6] = 0; + + if (speed == 1500 || speed == -1500) data[0] |= 1; + else if (speed == 2000 || speed == -2000) data[0] |= 2; + else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed); + else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed); + + /* If not decoding, just change speed setting */ + if (atomic_read(&itv->decoding) > 0) { + int got_sig = 0; + + /* Stop all DMA and decoding activity */ + ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0); + + /* Wait for any DMA to finish */ + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (itv->i_flags & IVTV_F_I_DMA) { + got_sig = signal_pending(current); + if (got_sig) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + if (got_sig) + return -EINTR; + + /* Change Speed safely */ + ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data); + IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + } + if (single_step) { + speed = (speed < 0) ? -1 : 1; + ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); + } + itv->speed = speed; + return 0; +} + +static int ivtv_validate_speed(int cur_speed, int new_speed) +{ + int fact = new_speed < 0 ? -1 : 1; + int s; + + if (new_speed < 0) new_speed = -new_speed; + if (cur_speed < 0) cur_speed = -cur_speed; + + if (cur_speed <= new_speed) { + if (new_speed > 1500) return fact * 2000; + if (new_speed > 1000) return fact * 1500; + } + else { + if (new_speed >= 2000) return fact * 2000; + if (new_speed >= 1500) return fact * 1500; + if (new_speed >= 1000) return fact * 1000; + } + if (new_speed == 0) return 1000; + if (new_speed == 1 || new_speed == 1000) return fact * new_speed; + + s = new_speed; + new_speed = 1000 / new_speed; + if (1000 / cur_speed == new_speed) + new_speed += (cur_speed < s) ? -1 : 1; + if (new_speed > 60) return 1000 / (fact * 60); + return 1000 / (fact * new_speed); +} + +static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, + struct video_command *vc, int try) +{ + struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + switch (vc->cmd) { + case VIDEO_CMD_PLAY: { + vc->flags = 0; + vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed); + if (vc->play.speed < 0) + vc->play.format = VIDEO_PLAY_FMT_GOP; + if (try) break; + + if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG) + return -EBUSY; + return ivtv_start_decoding(id, vc->play.speed); + } + + case VIDEO_CMD_STOP: + vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK; + if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY) + vc->stop.pts = 0; + if (try) break; + if (atomic_read(&itv->decoding) == 0) + return 0; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + + itv->output_mode = OUT_NONE; + return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts); + + case VIDEO_CMD_FREEZE: + vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK; + if (try) break; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + if (atomic_read(&itv->decoding) > 0) { + ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, + (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0); + } + break; + + case VIDEO_CMD_CONTINUE: + vc->flags = 0; + if (try) break; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + if (atomic_read(&itv->decoding) > 0) { + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 0); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) +{ + struct v4l2_register *regs = arg; + unsigned long flags; + volatile u8 __iomem *reg_start; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) + reg_start = itv->reg_mem - IVTV_REG_OFFSET; + else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && + regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) + reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; + else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE) + reg_start = itv->enc_mem; + else + return -EINVAL; + + spin_lock_irqsave(&ivtv_cards_lock, flags); + if (cmd == VIDIOC_DBG_G_REGISTER) { + regs->val = readl(regs->reg + reg_start); + } else { + writel(regs->val, regs->reg + reg_start); + } + spin_unlock_irqrestore(&ivtv_cards_lock, flags); + return 0; +} + +static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt) +{ + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + fmt->fmt.pix.left = itv->main_rect.left; + fmt->fmt.pix.top = itv->main_rect.top; + fmt->fmt.pix.width = itv->main_rect.width; + fmt->fmt.pix.height = itv->main_rect.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (itv->output_mode == OUT_UDMA_YUV) { + switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { + case IVTV_YUV_MODE_INTERLACED: + fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ? + V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB; + break; + case IVTV_YUV_MODE_PROGRESSIVE: + fmt->fmt.pix.field = V4L2_FIELD_NONE; + break; + default: + fmt->fmt.pix.field = V4L2_FIELD_ANY; + break; + } + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } + else if (itv->output_mode == OUT_YUV || + streamtype == IVTV_ENC_STREAM_TYPE_YUV || + streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } else { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.sizeimage = 128 * 1024; + } + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt->fmt.pix.left = 0; + fmt->fmt.pix.top = 0; + fmt->fmt.pix.width = itv->params.width; + fmt->fmt.pix.height = itv->params.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (streamtype == IVTV_ENC_STREAM_TYPE_YUV || + streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } else { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.sizeimage = 128 * 1024; + } + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + fmt->fmt.win.chromakey = itv->osd_color_key; + fmt->fmt.win.global_alpha = itv->osd_global_alpha; + break; + + case V4L2_BUF_TYPE_VBI_CAPTURE: + fmt->fmt.vbi.sampling_rate = 27000000; + fmt->fmt.vbi.offset = 248; + fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4; + fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + fmt->fmt.vbi.start[0] = itv->vbi.start[0]; + fmt->fmt.vbi.start[1] = itv->vbi.start[1]; + fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count; + break; + + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + { + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) + return -EINVAL; + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + if (itv->is_60hz) { + vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625; + vbifmt->service_lines[0][16] = V4L2_SLICED_VPS; + } + vbifmt->service_set = get_service_set(vbifmt); + break; + } + + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + { + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { + vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 : + V4L2_SLICED_VBI_525; + expand_service_set(vbifmt, itv->is_50hz); + break; + } + + itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); + vbifmt->service_set = get_service_set(vbifmt); + break; + } + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype, + struct v4l2_format *fmt, int set_fmt) +{ + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + u16 set; + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct v4l2_rect r; + int field; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + field = fmt->fmt.pix.field; + r.top = fmt->fmt.pix.top; + r.left = fmt->fmt.pix.left; + r.width = fmt->fmt.pix.width; + r.height = fmt->fmt.pix.height; + ivtv_get_fmt(itv, streamtype, fmt); + if (itv->output_mode != OUT_UDMA_YUV) { + /* TODO: would setting the rect also be valid for this mode? */ + fmt->fmt.pix.top = r.top; + fmt->fmt.pix.left = r.left; + fmt->fmt.pix.width = r.width; + fmt->fmt.pix.height = r.height; + } + if (itv->output_mode == OUT_UDMA_YUV) { + /* TODO: add checks for validity */ + fmt->fmt.pix.field = field; + } + if (set_fmt) { + if (itv->output_mode == OUT_UDMA_YUV) { + switch (field) { + case V4L2_FIELD_NONE: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE; + break; + case V4L2_FIELD_ANY: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO; + break; + case V4L2_FIELD_INTERLACED_BT: + itv->yuv_info.lace_mode = + IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; + break; + case V4L2_FIELD_INTERLACED_TB: + default: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED; + break; + } + itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; + + /* Force update of yuv registers */ + itv->yuv_info.yuv_forced_update = 1; + return 0; + } + if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + r.width, r.height, r.left, r.top)) + itv->main_rect = r; + else + return -EINVAL; + } + return 0; + } + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) { + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + if (set_fmt) { + itv->osd_color_key = fmt->fmt.win.chromakey; + itv->osd_global_alpha = fmt->fmt.win.global_alpha; + ivtv_set_osd_alpha(itv); + } + return 0; + } + + /* set window size */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + + if (w > 720) w = 720; + else if (w < 1) w = 1; + if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480); + else if (h < 2) h = 2; + ivtv_get_fmt(itv, streamtype, fmt); + fmt->fmt.pix.width = w; + fmt->fmt.pix.height = h; + + if (!set_fmt || (itv->params.width == w && itv->params.height == h)) + return 0; + if (atomic_read(&itv->capturing) > 0) + return -EBUSY; + + itv->params.width = w; + itv->params.height = h; + if (w != 720 || h != (itv->is_50hz ? 576 : 480)) + itv->params.video_temporal_filter = 0; + else + itv->params.video_temporal_filter = 8; + itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + return ivtv_get_fmt(itv, streamtype, fmt); + } + + /* set raw VBI format */ + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (set_fmt && streamtype == IVTV_ENC_STREAM_TYPE_VBI && + itv->vbi.sliced_in->service_set && + atomic_read(&itv->capturing) > 0) { + return -EBUSY; + } + if (set_fmt) { + itv->vbi.sliced_in->service_set = 0; + itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + } + return ivtv_get_fmt(itv, streamtype, fmt); + } + + /* set sliced VBI output + In principle the user could request that only certain + VBI types are output and that the others are ignored. + I.e., suppress CC in the even fields or only output + WSS and no VPS. Currently though there is no choice. */ + if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + return ivtv_get_fmt(itv, streamtype, fmt); + + /* any else but sliced VBI capture is an error */ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) + return ivtv_get_fmt(itv, streamtype, fmt); + + /* set sliced VBI capture format */ + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + + if (vbifmt->service_set) + expand_service_set(vbifmt, itv->is_50hz); + set = check_service_set(vbifmt, itv->is_50hz); + vbifmt->service_set = get_service_set(vbifmt); + + if (!set_fmt) + return 0; + if (set == 0) + return -EINVAL; + if (atomic_read(&itv->capturing) > 0 && itv->vbi.sliced_in->service_set == 0) { + return -EBUSY; + } + itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); + return 0; +} + +static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + struct v4l2_register *reg = arg; + + switch (cmd) { + /* ioctls to allow direct access to the encoder registers for testing */ + case VIDIOC_DBG_G_REGISTER: + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + + case VIDIOC_DBG_S_REGISTER: + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + + case VIDIOC_G_CHIP_IDENT: { + struct v4l2_chip_ident *chip = arg; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (reg->match_type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) { + struct v4l2_chip_ident *chip = arg; + + chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; + } + return 0; + } + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR) + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + return -EINVAL; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; + + ivtv_audio_set_route(itv, route); + break; + } + + case VIDIOC_INT_RESET: + ivtv_reset_ir_gpio(itv); + break; + + default: + return -EINVAL; + } + return 0; +} + +int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = NULL; + + if (filp) id = (struct ivtv_open_id *)filp->private_data; + + switch (cmd) { + case VIDIOC_G_PRIORITY: + { + enum v4l2_priority *p = arg; + + *p = v4l2_prio_max(&itv->prio); + break; + } + + case VIDIOC_S_PRIORITY: + { + enum v4l2_priority *prio = arg; + + return v4l2_prio_change(&itv->prio, &id->prio, *prio); + } + + case VIDIOC_QUERYCAP:{ + struct v4l2_capability *vcap = arg; + + memset(vcap, 0, sizeof(*vcap)); + strcpy(vcap->driver, IVTV_DRIVER_NAME); /* driver name */ + strcpy(vcap->card, itv->card_name); /* card type */ + strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */ + vcap->version = IVTV_DRIVER_VERSION; /* version */ + vcap->capabilities = itv->v4l2_cap; /* capabilities */ + + /* reserved.. must set to 0! */ + vcap->reserved[0] = vcap->reserved[1] = + vcap->reserved[2] = vcap->reserved[3] = 0; + break; + } + + case VIDIOC_ENUMAUDIO:{ + struct v4l2_audio *vin = arg; + + return ivtv_get_audio_input(itv, vin->index, vin); + } + + case VIDIOC_G_AUDIO:{ + struct v4l2_audio *vin = arg; + + vin->index = itv->audio_input; + return ivtv_get_audio_input(itv, vin->index, vin); + } + + case VIDIOC_S_AUDIO:{ + struct v4l2_audio *vout = arg; + + if (vout->index >= itv->nof_audio_inputs) + return -EINVAL; + itv->audio_input = vout->index; + ivtv_audio_set_io(itv); + break; + } + + case VIDIOC_ENUMAUDOUT:{ + struct v4l2_audioout *vin = arg; + + /* set it to defaults from our table */ + return ivtv_get_audio_output(itv, vin->index, vin); + } + + case VIDIOC_G_AUDOUT:{ + struct v4l2_audioout *vin = arg; + + vin->index = 0; + return ivtv_get_audio_output(itv, vin->index, vin); + } + + case VIDIOC_S_AUDOUT:{ + struct v4l2_audioout *vout = arg; + + return ivtv_get_audio_output(itv, vout->index, vout); + } + + case VIDIOC_ENUMINPUT:{ + struct v4l2_input *vin = arg; + + /* set it to defaults from our table */ + return ivtv_get_input(itv, vin->index, vin); + } + + case VIDIOC_ENUMOUTPUT:{ + struct v4l2_output *vout = arg; + + return ivtv_get_output(itv, vout->index, vout); + } + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: { + struct v4l2_format *fmt = arg; + + return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT); + } + + case VIDIOC_G_FMT: { + struct v4l2_format *fmt = arg; + int type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->type = type; + return ivtv_get_fmt(itv, id->type, fmt); + } + + case VIDIOC_S_CROP: { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return itv->video_dec_func(itv, VIDIOC_S_CROP, arg); + } + + case VIDIOC_G_CROP: { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return itv->video_dec_func(itv, VIDIOC_G_CROP, arg); + } + + case VIDIOC_ENUM_FMT: { + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, + { 0, 0, 0, 0 } + }, + { 1, 0, V4L2_FMT_FLAG_COMPRESSED, + "MPEG", V4L2_PIX_FMT_MPEG, + { 0, 0, 0, 0 } + } + }; + struct v4l2_fmtdesc *fmt = arg; + enum v4l2_buf_type type = fmt->type; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + break; + default: + return -EINVAL; + } + if (fmt->index > 1) + return -EINVAL; + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; + } + + case VIDIOC_G_INPUT:{ + *(int *)arg = itv->active_input; + break; + } + + case VIDIOC_S_INPUT:{ + int inp = *(int *)arg; + + if (inp < 0 || inp >= itv->nof_inputs) + return -EINVAL; + + if (inp == itv->active_input) { + IVTV_DEBUG_INFO("Input unchanged\n"); + break; + } + IVTV_DEBUG_INFO("Changing input from %d to %d\n", + itv->active_input, inp); + + itv->active_input = inp; + /* Set the audio input to whatever is appropriate for the + input type. */ + itv->audio_input = itv->card->video_inputs[inp].audio_index; + + /* prevent others from messing with the streams until + we're finished changing inputs. */ + ivtv_mute(itv); + ivtv_video_set_io(itv); + ivtv_audio_set_io(itv); + ivtv_unmute(itv); + break; + } + + case VIDIOC_G_OUTPUT:{ + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + *(int *)arg = itv->active_output; + break; + } + + case VIDIOC_S_OUTPUT:{ + int outp = *(int *)arg; + struct v4l2_routing route; + + if (outp >= itv->card->nof_outputs) + return -EINVAL; + + if (outp == itv->active_output) { + IVTV_DEBUG_INFO("Output unchanged\n"); + break; + } + IVTV_DEBUG_INFO("Changing output from %d to %d\n", + itv->active_output, outp); + + itv->active_output = outp; + route.input = SAA7127_INPUT_TYPE_NORMAL; + route.output = itv->card->video_outputs[outp].video_output; + ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + break; + } + + case VIDIOC_G_FREQUENCY:{ + struct v4l2_frequency *vf = arg; + + if (vf->tuner != 0) + return -EINVAL; + ivtv_call_i2c_clients(itv, cmd, arg); + break; + } + + case VIDIOC_S_FREQUENCY:{ + struct v4l2_frequency vf = *(struct v4l2_frequency *)arg; + + if (vf.tuner != 0) + return -EINVAL; + + ivtv_mute(itv); + IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency); + ivtv_call_i2c_clients(itv, cmd, &vf); + ivtv_unmute(itv); + break; + } + + case VIDIOC_ENUMSTD:{ + struct v4l2_standard *vs = arg; + int idx = vs->index; + + if (idx < 0 || idx >= ARRAY_SIZE(enum_stds)) + return -EINVAL; + + *vs = (enum_stds[idx].std & V4L2_STD_525_60) ? + ivtv_std_60hz : ivtv_std_50hz; + vs->index = idx; + vs->id = enum_stds[idx].std; + strcpy(vs->name, enum_stds[idx].name); + break; + } + + case VIDIOC_G_STD:{ + *(v4l2_std_id *) arg = itv->std; + break; + } + + case VIDIOC_S_STD: { + v4l2_std_id std = *(v4l2_std_id *) arg; + + if ((std & V4L2_STD_ALL) == 0) + return -EINVAL; + + if (std == itv->std) + break; + + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) || + atomic_read(&itv->capturing) > 0 || + atomic_read(&itv->decoding) > 0) { + /* Switching standard would turn off the radio or mess + with already running streams, prevent that by + returning EBUSY. */ + return -EBUSY; + } + + itv->std = std; + itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; + itv->params.is_50hz = itv->is_50hz = !itv->is_60hz; + itv->params.width = 720; + itv->params.height = itv->is_50hz ? 576 : 480; + itv->vbi.count = itv->is_50hz ? 18 : 12; + itv->vbi.start[0] = itv->is_50hz ? 6 : 10; + itv->vbi.start[1] = itv->is_50hz ? 318 : 273; + if (itv->hw_flags & IVTV_HW_CX25840) { + itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; + } + IVTV_DEBUG_INFO("Switching standard to %llx.\n", itv->std); + + /* Tuner */ + ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + /* set display standard */ + itv->std_out = std; + itv->is_out_60hz = itv->is_60hz; + itv->is_out_50hz = itv->is_50hz; + ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); + ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); + itv->main_rect.left = itv->main_rect.top = 0; + itv->main_rect.width = 720; + itv->main_rect.height = itv->params.height; + ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + 720, itv->main_rect.height, 0, 0); + } + break; + } + + case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */ + struct v4l2_tuner *vt = arg; + + if (vt->index != 0) + return -EINVAL; + + ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); + break; + } + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *vt = arg; + + if (vt->index != 0) + return -EINVAL; + + memset(vt, 0, sizeof(*vt)); + ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { + strcpy(vt->name, "ivtv Radio Tuner"); + vt->type = V4L2_TUNER_RADIO; + } else { + strcpy(vt->name, "ivtv TV Tuner"); + vt->type = V4L2_TUNER_ANALOG_TV; + } + break; + } + + case VIDIOC_G_SLICED_VBI_CAP: { + struct v4l2_sliced_vbi_cap *cap = arg; + int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; + int f, l; + enum v4l2_buf_type type = cap->type; + + memset(cap, 0, sizeof(*cap)); + cap->type = type; + if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + if (valid_service_line(f, l, itv->is_50hz)) { + cap->service_lines[f][l] = set; + } + } + } + return 0; + } + if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) + return -EINVAL; + if (itv->is_60hz) { + cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + cap->service_lines[0][23] = V4L2_SLICED_WSS_625; + cap->service_lines[0][16] = V4L2_SLICED_VPS; + } + return 0; + } + return -EINVAL; + } + + case VIDIOC_G_ENC_INDEX: { + struct v4l2_enc_idx *idx = arg; + int i; + + idx->entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) % + IVTV_MAX_PGM_INDEX; + if (idx->entries > V4L2_ENC_IDX_ENTRIES) + idx->entries = V4L2_ENC_IDX_ENTRIES; + for (i = 0; i < idx->entries; i++) { + idx->entry[i] = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX]; + } + itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX; + break; + } + + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: { + struct v4l2_encoder_cmd *enc = arg; + int try = cmd == VIDIOC_TRY_ENCODER_CMD; + + memset(&enc->raw, 0, sizeof(enc->raw)); + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + enc->flags = 0; + if (try) + return 0; + return ivtv_start_capture(id); + + case V4L2_ENC_CMD_STOP: + enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; + if (try) + return 0; + ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); + return 0; + + case V4L2_ENC_CMD_PAUSE: + enc->flags = 0; + if (try) + return 0; + if (!atomic_read(&itv->capturing)) + return -EPERM; + if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + return 0; + ivtv_mute(itv); + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0); + break; + + case V4L2_ENC_CMD_RESUME: + enc->flags = 0; + if (try) + return 0; + if (!atomic_read(&itv->capturing)) + return -EPERM; + if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + return 0; + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); + ivtv_unmute(itv); + break; + default: + return -EINVAL; + } + break; + } + + case VIDIOC_G_FBUF: { + struct v4l2_framebuffer *fb = arg; + + memset(fb, 0, sizeof(*fb)); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + break; + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY | + V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA; + fb->fmt.pixelformat = itv->osd_pixelformat; + fb->fmt.width = itv->osd_rect.width; + fb->fmt.height = itv->osd_rect.height; + fb->fmt.left = itv->osd_rect.left; + fb->fmt.top = itv->osd_rect.top; + fb->base = (void *)itv->osd_video_pbase; + if (itv->osd_global_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; + if (itv->osd_local_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + if (itv->osd_color_key_state) + fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + break; + } + + case VIDIOC_S_FBUF: { + struct v4l2_framebuffer *fb = arg; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + break; + itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; + itv->osd_local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0; + itv->osd_color_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; + break; + } + + case VIDIOC_LOG_STATUS: + { + int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT; + struct v4l2_input vidin; + struct v4l2_audio audin; + int i; + + IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); + if (itv->hw_flags & IVTV_HW_TVEEPROM) { + struct tveeprom tv; + + ivtv_read_eeprom(itv, &tv); + } + ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); + ivtv_get_input(itv, itv->active_input, &vidin); + ivtv_get_audio_input(itv, itv->audio_input, &audin); + IVTV_INFO("Video Input: %s\n", vidin.name); + IVTV_INFO("Audio Input: %s\n", audin.name); + if (has_output) { + struct v4l2_output vidout; + struct v4l2_audioout audout; + int mode = itv->output_mode; + static const char * const output_modes[] = { + "None", + "MPEG Streaming", + "YUV Streaming", + "YUV Frames", + "Passthrough", + }; + + ivtv_get_output(itv, itv->active_output, &vidout); + ivtv_get_audio_output(itv, 0, &audout); + IVTV_INFO("Video Output: %s\n", vidout.name); + IVTV_INFO("Audio Output: %s\n", audout.name); + if (mode < 0 || mode > OUT_PASSTHROUGH) + mode = OUT_NONE; + IVTV_INFO("Output Mode: %s\n", output_modes[mode]); + } + IVTV_INFO("Tuner: %s\n", + test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); + cx2341x_log_status(&itv->params, itv->name); + IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); + for (i = 0; i < IVTV_MAX_STREAMS; i++) { + struct ivtv_stream *s = &itv->streams[i]; + + if (s->v4l2dev == NULL || s->buffers == 0) + continue; + IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, + (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers * s->buf_size) / 1024, s->buffers); + } + IVTV_INFO("Read MPEG/VBI: %lld/%lld bytes\n", itv->mpg_data_received, itv->vbi_data_inserted); + IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + int nonblocking = filp->f_flags & O_NONBLOCK; + struct ivtv_stream *s = &itv->streams[id->type]; + + switch (cmd) { + case IVTV_IOC_DMA_FRAME: { + struct ivtv_dma_frame *args = arg; + + IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL) + return 0; + if (ivtv_claim_stream(id, id->type)) { + return -EBUSY; + } + if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) { + ivtv_release_stream(s); + return -EBUSY; + } + if (args->y_source == NULL) + return 0; + return ivtv_yuv_prep_frame(itv, args); + } + + case VIDEO_GET_PTS: { + u32 data[CX2341X_MBOX_MAX_DATA]; + u64 *pts = arg; + + IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n"); + if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { + *pts = s->dma_pts; + break; + } + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) | + (u64)itv->last_dec_timing[1]; + break; + } + *pts = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *pts = (u64) ((u64) data[2] << 32) | (u64) data[1]; + /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ + } + break; + } + + case VIDEO_GET_FRAME_COUNT: { + u32 data[CX2341X_MBOX_MAX_DATA]; + u64 *frame = arg; + + IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n"); + if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { + *frame = 0; + break; + } + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *frame = itv->last_dec_timing[0]; + break; + } + *frame = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *frame = data[0]; + } + break; + } + + case VIDEO_PLAY: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_PLAY\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_PLAY; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_STOP: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_STOP\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_STOP; + vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_FREEZE: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_FREEZE; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_CONTINUE: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_CONTINUE; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: { + struct video_command *vc = arg; + int try = (cmd == VIDEO_TRY_COMMAND); + + if (try) + IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND\n"); + else + IVTV_DEBUG_IOCTL("VIDEO_COMMAND\n"); + return ivtv_video_command(itv, id, vc, try); + } + + case VIDEO_GET_EVENT: { + struct video_event *ev = arg; + DEFINE_WAIT(wait); + + IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + memset(ev, 0, sizeof(*ev)); + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + + while (1) { + if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + ev->type = VIDEO_EVENT_DECODER_STOPPED; + else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) { + ev->type = VIDEO_EVENT_VSYNC; + ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ? + VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN; + if (itv->output_mode == OUT_UDMA_YUV && + (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) == + IVTV_YUV_MODE_PROGRESSIVE) { + ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE; + } + } + if (ev->type) + return 0; + if (nonblocking) + return -EAGAIN; + /* wait for event */ + prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE); + if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0) + schedule(); + finish_wait(&itv->event_waitq, &wait); + if (signal_pending(current)) { + /* return if a signal was received */ + IVTV_DEBUG_INFO("User stopped wait for event\n"); + return -EINTR; + } + } + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + int ret; + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + case VIDIOC_S_FMT: + case VIDIOC_S_CROP: + case VIDIOC_S_AUDIO: + case VIDIOC_S_AUDOUT: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_S_FBUF: + ret = v4l2_prio_check(&itv->prio, &id->prio); + if (ret) + return ret; + } + + switch (cmd) { + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_G_CHIP_IDENT: + case VIDIOC_INT_S_AUDIO_ROUTING: + case VIDIOC_INT_RESET: + if (ivtv_debug & IVTV_DBGFLG_IOCTL) { + printk(KERN_INFO "ivtv%d ioctl: ", itv->num); + v4l_printk_ioctl(cmd); + } + return ivtv_debug_ioctls(filp, cmd, arg); + + case VIDIOC_G_PRIORITY: + case VIDIOC_S_PRIORITY: + case VIDIOC_QUERYCAP: + case VIDIOC_ENUMINPUT: + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + case VIDIOC_ENUMOUTPUT: + case VIDIOC_G_OUTPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_ENUM_FMT: + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_S_TUNER: + case VIDIOC_G_TUNER: + case VIDIOC_ENUMAUDIO: + case VIDIOC_S_AUDIO: + case VIDIOC_G_AUDIO: + case VIDIOC_ENUMAUDOUT: + case VIDIOC_S_AUDOUT: + case VIDIOC_G_AUDOUT: + case VIDIOC_G_SLICED_VBI_CAP: + case VIDIOC_LOG_STATUS: + case VIDIOC_G_ENC_INDEX: + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_G_FBUF: + case VIDIOC_S_FBUF: + if (ivtv_debug & IVTV_DBGFLG_IOCTL) { + printk(KERN_INFO "ivtv%d ioctl: ", itv->num); + v4l_printk_ioctl(cmd); + } + return ivtv_v4l2_ioctls(itv, filp, cmd, arg); + + case VIDIOC_QUERYMENU: + case VIDIOC_QUERYCTRL: + case VIDIOC_S_CTRL: + case VIDIOC_G_CTRL: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + if (ivtv_debug & IVTV_DBGFLG_IOCTL) { + printk(KERN_INFO "ivtv%d ioctl: ", itv->num); + v4l_printk_ioctl(cmd); + } + return ivtv_control_ioctls(itv, cmd, arg); + + case IVTV_IOC_DMA_FRAME: + case VIDEO_GET_PTS: + case VIDEO_GET_FRAME_COUNT: + case VIDEO_GET_EVENT: + case VIDEO_PLAY: + case VIDEO_STOP: + case VIDEO_FREEZE: + case VIDEO_CONTINUE: + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: + return ivtv_decoder_ioctls(filp, cmd, arg); + + case 0x00005401: /* Handle isatty() calls */ + return -EINVAL; + default: + return v4l_compat_translate_ioctl(inode, filp, cmd, arg, + ivtv_v4l2_do_ioctl); + } + return 0; +} + +int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + + /* Filter dvb ioctls that cannot be handled by video_usercopy */ + switch (cmd) { + case VIDEO_SELECT_SOURCE: + IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX); + + case AUDIO_SET_MUTE: + IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); + itv->speed_mute_audio = arg; + return 0; + + case AUDIO_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); + if (arg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_stereo_mode = arg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + case AUDIO_BILINGUAL_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); + if (arg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_bilingual_mode = arg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + default: + break; + } + return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl); +} diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h new file mode 100644 index 0000000..cbccf7a --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-ioctl.h @@ -0,0 +1,28 @@ +/* + ioctl system call + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +u16 service2vbi(int type); +void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); +u16 get_service_set(struct v4l2_sliced_vbi_format *fmt); +int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); +int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg); +void ivtv_set_osd_alpha(struct ivtv *itv); +int ivtv_set_speed(struct ivtv *itv, int speed); diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c new file mode 100644 index 0000000..c3a047b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -0,0 +1,838 @@ +/* interrupt handling + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-firmware.h" +#include "ivtv-fileops.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-ioctl.h" +#include "ivtv-mailbox.h" +#include "ivtv-vbi.h" +#include "ivtv-yuv.h" + +#define DMA_MAGIC_COOKIE 0x000001fe + +#define SLICED_VBI_PIO 1 + +static void ivtv_dma_dec_start(struct ivtv_stream *s); + +static const int ivtv_stream_map[] = { + IVTV_ENC_STREAM_TYPE_MPG, + IVTV_ENC_STREAM_TYPE_YUV, + IVTV_ENC_STREAM_TYPE_PCM, + IVTV_ENC_STREAM_TYPE_VBI, +}; + +static inline int ivtv_use_pio(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + + return s->dma == PCI_DMA_NONE || + (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set); +} + +void ivtv_irq_work_handler(struct work_struct *work) +{ + struct ivtv *itv = container_of(work, struct ivtv, irq_work_queue); + + DEFINE_WAIT(wait); + + if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags)) + vbi_work_handler(itv); + + if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags)) + ivtv_yuv_work_handler(itv); +} + +/* Determine the required DMA size, setup enough buffers in the predma queue and + actually copy the data from the card to the buffers in case a PIO transfer is + required for this stream. + */ +static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf; + struct list_head *p; + u32 bytes_needed = 0; + u32 offset, size; + u32 UVoffset = 0, UVsize = 0; + int skip_bufs = s->q_predma.buffers; + int idx = s->SG_length; + int rc; + + /* sanity checks */ + if (s->v4l2dev == NULL) { + IVTV_DEBUG_WARN("Stream %s not started\n", s->name); + return -1; + } + if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + IVTV_DEBUG_WARN("Stream %s not open\n", s->name); + return -1; + } + + /* determine offset, size and PTS for the various streams */ + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_MPG: + offset = data[1]; + size = data[2]; + s->dma_pts = 0; + break; + + case IVTV_ENC_STREAM_TYPE_YUV: + offset = data[1]; + size = data[2]; + UVoffset = data[3]; + UVsize = data[4]; + s->dma_pts = ((u64) data[5] << 32) | data[6]; + break; + + case IVTV_ENC_STREAM_TYPE_PCM: + offset = data[1] + 12; + size = data[2] - 12; + s->dma_pts = read_dec(offset - 8) | + ((u64)(read_dec(offset - 12)) << 32); + if (itv->has_cx23415) + offset += IVTV_DECODER_OFFSET; + break; + + case IVTV_ENC_STREAM_TYPE_VBI: + size = itv->vbi.enc_size * itv->vbi.fpi; + offset = read_enc(itv->vbi.enc_start - 4) + 12; + if (offset == 12) { + IVTV_DEBUG_INFO("VBI offset == 0\n"); + return -1; + } + s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32); + break; + + case IVTV_DEC_STREAM_TYPE_VBI: + size = read_dec(itv->vbi.dec_start + 4) + 8; + offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start; + s->dma_pts = 0; + offset += IVTV_DECODER_OFFSET; + break; + default: + /* shouldn't happen */ + return -1; + } + + /* if this is the start of the DMA then fill in the magic cookie */ + if (s->SG_length == 0) { + if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || + s->type == IVTV_DEC_STREAM_TYPE_VBI)) { + s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET); + write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET); + } + else { + s->dma_backup = read_enc(offset); + write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset); + } + s->dma_offset = offset; + } + + bytes_needed = size; + if (s->type == IVTV_ENC_STREAM_TYPE_YUV) { + /* The size for the Y samples needs to be rounded upwards to a + multiple of the buf_size. The UV samples then start in the + next buffer. */ + bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size); + bytes_needed += UVsize; + } + + IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n", + ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset); + + rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed); + if (rc < 0) { /* Insufficient buffers */ + IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n", + bytes_needed, s->name); + return -1; + } + if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) { + IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name); + IVTV_WARN("Cause: the application is not reading fast enough.\n"); + } + s->buffers_stolen = rc; + + /* got the buffers, now fill in SGarray (DMA) or copy the data from the card + to the buffers (PIO). */ + buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); + memset(buf->buf, 0, 128); + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + + if (skip_bufs-- > 0) + continue; + if (!ivtv_use_pio(s)) { + s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle); + s->SGarray[idx].src = cpu_to_le32(offset); + s->SGarray[idx].size = cpu_to_le32(s->buf_size); + } + buf->bytesused = (size < s->buf_size) ? size : s->buf_size; + + /* If PIO, then copy the data from the card to the buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { + memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused); + } + else if (ivtv_use_pio(s)) { + memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused); + } + + s->q_predma.bytesused += buf->bytesused; + size -= buf->bytesused; + offset += s->buf_size; + + /* Sync SG buffers */ + ivtv_buf_sync_for_device(s, buf); + + if (size == 0) { /* YUV */ + /* process the UV section */ + offset = UVoffset; + size = UVsize; + } + idx++; + } + s->SG_length = idx; + return 0; +} + +static void dma_post(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf = NULL; + struct list_head *p; + u32 offset; + u32 *u32buf; + int x = 0; + + if (ivtv_use_pio(s)) { + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + s->SG_length = 0; + } + IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", + s->name, s->dma_offset); + list_for_each(p, &s->q_dma.list) { + buf = list_entry(p, struct ivtv_buffer, list); + u32buf = (u32 *)buf->buf; + + /* Sync Buffer */ + ivtv_buf_sync_for_cpu(s, buf); + + if (x == 0) { + offset = s->dma_last_offset; + if (u32buf[offset / 4] != DMA_MAGIC_COOKIE) + { + for (offset = 0; offset < 64; offset++) { + if (u32buf[offset] == DMA_MAGIC_COOKIE) { + break; + } + } + offset *= 4; + if (offset == 256) { + IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name); + offset = s->dma_last_offset; + } + if (s->dma_last_offset != offset) + IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset); + s->dma_last_offset = offset; + } + if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || + s->type == IVTV_DEC_STREAM_TYPE_VBI)) { + write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET); + } + else { + write_enc_sync(0, s->dma_offset); + } + if (offset) { + buf->bytesused -= offset; + memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset); + } + *u32buf = cpu_to_le32(s->dma_backup); + } + x++; + /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG || + s->type == IVTV_ENC_STREAM_TYPE_VBI) + set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags); + } + if (buf) + buf->bytesused += s->dma_last_offset; + if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) { + /* Parse and Groom VBI Data */ + s->q_dma.bytesused -= buf->bytesused; + ivtv_process_vbi_data(itv, buf, 0, s->type); + s->q_dma.bytesused += buf->bytesused; + if (s->id == -1) { + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); + return; + } + } + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused); + if (s->id != -1) + wake_up(&s->waitq); +} + +void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf; + struct list_head *p; + u32 y_size = itv->params.height * itv->params.width; + u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; + int y_done = 0; + int bytes_written = 0; + unsigned long flags = 0; + int idx = 0; + + IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); + buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + + /* YUV UV Offset from Y Buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) { + offset = uv_offset; + y_done = 1; + } + s->SGarray[idx].src = cpu_to_le32(buf->dma_handle); + s->SGarray[idx].dst = cpu_to_le32(offset); + s->SGarray[idx].size = cpu_to_le32(buf->bytesused); + + offset += buf->bytesused; + bytes_written += buf->bytesused; + + /* Sync SG buffers */ + ivtv_buf_sync_for_device(s, buf); + idx++; + } + s->SG_length = idx; + + /* Mark last buffer size for Interrupt flag */ + s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000); + + /* Sync Hardware SG List of buffers */ + ivtv_stream_sync_for_device(s); + if (lock) + spin_lock_irqsave(&itv->dma_reg_lock, flags); + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) { + ivtv_dma_dec_start(s); + } + else { + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } + if (lock) + spin_unlock_irqrestore(&itv->dma_reg_lock, flags); +} + +/* start the encoder DMA */ +static void ivtv_dma_enc_start(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + int i; + + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + IVTV_DEBUG_DMA("start DMA for %s\n", s->name); + s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256); + + /* If this is an MPEG stream, and VBI data is also pending, then append the + VBI DMA to the MPEG DMA and transfer both sets of data at once. + + VBI DMA is a second class citizen compared to MPEG and mixing them together + will confuse the firmware (the end of a VBI DMA is seen as the end of a + MPEG DMA, thus effectively dropping an MPEG frame). So instead we make + sure we only use the MPEG DMA to transfer the VBI DMA if both are in + use. This way no conflicts occur. */ + clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length && + s->SG_length + s_vbi->SG_length <= s->buffers) { + ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused); + s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256); + for (i = 0; i < s_vbi->SG_length; i++) { + s->SGarray[s->SG_length++] = s_vbi->SGarray[i]; + } + itv->vbi.dma_offset = s_vbi->dma_offset; + s_vbi->SG_length = 0; + set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); + IVTV_DEBUG_DMA("include DMA for %s\n", s->name); + } + + /* Mark last buffer size for Interrupt flag */ + s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000); + + /* Sync Hardware SG List of buffers */ + ivtv_stream_sync_for_device(s); + write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = s->type; + itv->dma_timer.expires = jiffies + HZ / 10; + add_timer(&itv->dma_timer); +} + +static void ivtv_dma_dec_start(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + IVTV_DEBUG_DMA("start DMA for %s\n", s->name); + /* put SG Handle into register 0x0c */ + write_reg(s->SG_handle, IVTV_REG_DECDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = s->type; + itv->dma_timer.expires = jiffies + HZ / 10; + add_timer(&itv->dma_timer); +} + +static void ivtv_irq_dma_read(struct ivtv *itv) +{ + struct ivtv_stream *s = NULL; + struct ivtv_buffer *buf; + int hw_stream_type; + + IVTV_DEBUG_IRQ("DEC DMA READ\n"); + del_timer(&itv->dma_timer); + if (read_reg(IVTV_REG_DMASTATUS) & 0x14) { + IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS)); + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + } + if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { + s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + hw_stream_type = 2; + } + else { + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + hw_stream_type = 0; + } + IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused); + + ivtv_stream_sync_for_cpu(s); + + /* For some reason must kick the firmware, like PIO mode, + I think this tells the firmware we are done and the size + of the xfer so it can calculate what we need next. + I think we can do this part ourselves but would have to + fully calculate xfer info ourselves and not use interrupts + */ + ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused, + hw_stream_type); + + /* Free last DMA call */ + while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) { + ivtv_buf_sync_for_cpu(s, buf); + ivtv_enqueue(s, buf, &s->q_free); + } + wake_up(&s->waitq); + } + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_enc_dma_complete(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + del_timer(&itv->dma_timer); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]); + if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags)) + data[1] = 3; + else if (data[1] > 2) + return; + s = &itv->streams[ivtv_stream_map[data[1]]]; + if (data[0] & 0x18) { + IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]); + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]); + } + s->SG_length = 0; + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + dma_post(s); + ivtv_stream_sync_for_cpu(s); + if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) { + u32 tmp; + + s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + tmp = s->dma_offset; + s->dma_offset = itv->vbi.dma_offset; + dma_post(s); + s->dma_offset = tmp; + } + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_dma_err(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + del_timer(&itv->dma_timer); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1], + read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); + if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && + itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) { + struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream]; + + /* retry */ + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) + ivtv_dma_dec_start(s); + else + ivtv_dma_enc_start(s); + return; + } + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_enc_start_cap(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + /* Get DMA destination and size arguments from card */ + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data); + IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]); + + if (data[0] > 2 || data[1] == 0 || data[2] == 0) { + IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n", + data[0], data[1], data[2]); + return; + } + clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); + s = &itv->streams[ivtv_stream_map[data[0]]]; + if (!stream_enc_dma_append(s, data)) { + if (ivtv_use_pio(s)) { + dma_post(s); + ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]); + } + else { + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } + } +} + +static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) +{ + struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + IVTV_DEBUG_IRQ("ENC START VBI CAP\n"); + s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + + if (ivtv_use_pio(s)) { + if (stream_enc_dma_append(s, data)) + return; + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + s->SG_length = 0; + dma_post(s); + return; + } + /* If more than two VBI buffers are pending, then + clear the old ones and start with this new one. + This can happen during transition stages when MPEG capturing is + started, but the first interrupts haven't arrived yet. During + that period VBI requests can accumulate without being able to + DMA the data. Since at most four VBI DMA buffers are available, + we just drop the old requests when there are already three + requests queued. */ + if (s->SG_length > 2) { + struct list_head *p; + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + ivtv_buf_sync_for_cpu(s, buf); + } + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); + s->SG_length = 0; + } + /* if we can append the data, and the MPEG stream isn't capturing, + then start a DMA request for just the VBI data. */ + if (!stream_enc_dma_append(s, data) && + !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) { + set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } +} + +static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; + + IVTV_DEBUG_IRQ("DEC VBI REINSERT\n"); + if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) && + !stream_enc_dma_append(s, data)) { + dma_post(s); + } +} + +static void ivtv_irq_dec_data_req(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + /* YUV or MPG */ + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { + itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2; + itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0]; + s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + } + else { + itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2]; + itv->dma_data_req_offset = data[1]; + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + } + IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused, + itv->dma_data_req_offset, itv->dma_data_req_size); + if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) { + set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + } + else { + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); + ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0); + } +} + +static void ivtv_irq_vsync(struct ivtv *itv) +{ + /* The vsync interrupt is unusual in that it won't clear until + * the end of the first line for the current field, at which + * point it clears itself. This can result in repeated vsync + * interrupts, or a missed vsync. Read some of the registers + * to determine the line being displayed and ensure we handle + * one vsync per frame. + */ + unsigned int frame = read_reg(0x28c0) & 1; + int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame); + + if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n"); + + if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) || + (frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) { + int next_dma_frame = last_dma_frame; + + if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) { + write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c); + write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); + write_reg(yuv_offset[next_dma_frame] >> 4, 0x834); + write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); + next_dma_frame = (next_dma_frame + 1) & 0x3; + atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame); + } + } + if (frame != (itv->lastVsyncFrame & 1)) { + struct ivtv_stream *s = ivtv_get_output_stream(itv); + int work = 0; + + itv->lastVsyncFrame += 1; + if (frame == 0) { + clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); + } + else { + set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); + } + if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) { + set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags); + wake_up(&itv->event_waitq); + } + wake_up(&itv->vsync_waitq); + if (s) + wake_up(&s->waitq); + + /* Send VBI to saa7127 */ + if (frame) { + set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags); + work = 1; + } + + /* Check if we need to update the yuv registers */ + if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) { + if (!itv->yuv_info.new_frame_info[last_dma_frame].update) + last_dma_frame = (last_dma_frame - 1) & 3; + + if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) { + itv->yuv_info.update_frame = last_dma_frame; + itv->yuv_info.new_frame_info[last_dma_frame].update = 0; + itv->yuv_info.yuv_forced_update = 0; + set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags); + work = 1; + } + } + if (work) + queue_work(itv->irq_work_queues, &itv->irq_work_queue); + } +} + +#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ) + +irqreturn_t ivtv_irq_handler(int irq, void *dev_id) +{ + struct ivtv *itv = (struct ivtv *)dev_id; + u32 combo; + u32 stat; + int i; + u8 vsync_force = 0; + + spin_lock(&itv->dma_reg_lock); + /* get contents of irq status register */ + stat = read_reg(IVTV_REG_IRQSTATUS); + + combo = ~itv->irqmask & stat; + + /* Clear out IRQ */ + if (combo) write_reg(combo, IVTV_REG_IRQSTATUS); + + if (0 == combo) { + /* The vsync interrupt is unusual and clears itself. If we + * took too long, we may have missed it. Do some checks + */ + if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { + /* vsync is enabled, see if we're in a new field */ + if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) { + /* New field, looks like we missed it */ + IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16); + vsync_force = 1; + } + } + + if (!vsync_force) { + /* No Vsync expected, wasn't for us */ + spin_unlock(&itv->dma_reg_lock); + return IRQ_NONE; + } + } + + /* Exclude interrupts noted below from the output, otherwise the log is flooded with + these messages */ + if (combo & ~0xff6d0400) + IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo); + + if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) { + IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n"); + } + + if (combo & IVTV_IRQ_DMA_READ) { + ivtv_irq_dma_read(itv); + } + + if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) { + ivtv_irq_enc_dma_complete(itv); + } + + if (combo & IVTV_IRQ_DMA_ERR) { + ivtv_irq_dma_err(itv); + } + + if (combo & IVTV_IRQ_ENC_START_CAP) { + ivtv_irq_enc_start_cap(itv); + } + + if (combo & IVTV_IRQ_ENC_VBI_CAP) { + ivtv_irq_enc_vbi_cap(itv); + } + + if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) { + ivtv_irq_dev_vbi_reinsert(itv); + } + + if (combo & IVTV_IRQ_ENC_EOS) { + IVTV_DEBUG_IRQ("ENC EOS\n"); + set_bit(IVTV_F_I_EOS, &itv->i_flags); + wake_up(&itv->cap_w); + } + + if (combo & IVTV_IRQ_DEC_DATA_REQ) { + ivtv_irq_dec_data_req(itv); + } + + /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */ + if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { + ivtv_irq_vsync(itv); + } + + if (combo & IVTV_IRQ_ENC_VIM_RST) { + IVTV_DEBUG_IRQ("VIM RST\n"); + /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */ + } + + if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) { + IVTV_DEBUG_INFO("Stereo mode changed\n"); + } + + if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) { + for (i = 0; i < IVTV_MAX_STREAMS; i++) { + int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS; + struct ivtv_stream *s = &itv->streams[idx]; + + if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) + continue; + if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) + ivtv_dma_dec_start(s); + else + ivtv_dma_enc_start(s); + break; + } + if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) { + ivtv_udma_start(itv); + } + } + + spin_unlock(&itv->dma_reg_lock); + + /* If we've just handled a 'forced' vsync, it's safest to say it + * wasn't ours. Another device may have triggered it at just + * the right time. + */ + return vsync_force ? IRQ_NONE : IRQ_HANDLED; +} + +void ivtv_unfinished_dma(unsigned long arg) +{ + struct ivtv *itv = (struct ivtv *)arg; + + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + return; + IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); + + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h new file mode 100644 index 0000000..a43348a --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-irq.h @@ -0,0 +1,26 @@ +/* + interrupt handling + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +irqreturn_t ivtv_irq_handler(int irq, void *dev_id); + +void ivtv_irq_work_handler(struct work_struct *work); +void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock); +void ivtv_unfinished_dma(unsigned long arg); diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c new file mode 100644 index 0000000..6ae42a3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-mailbox.c @@ -0,0 +1,360 @@ +/* + mailbox functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" + +/* Firmware mailbox flags*/ +#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 +#define IVTV_MBOX_DRIVER_DONE 0x00000002 +#define IVTV_MBOX_DRIVER_BUSY 0x00000001 +#define IVTV_MBOX_FREE 0x00000000 + +/* Firmware mailbox standard timeout */ +#define IVTV_API_STD_TIMEOUT 0x02000000 + +#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */ +#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */ +#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */ +#define API_DMA (1 << 3) /* DMA mailbox, has special handling */ +#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */ +#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */ + +struct ivtv_api_info { + int flags; /* Flags, see above */ + const char *name; /* The name of the command */ +}; + +#define API_ENTRY(x, f) [x] = { (f), #x } + +static const struct ivtv_api_info api_info[256] = { + /* MPEG encoder API */ + API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT), + API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE), + API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE), + API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE), + API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA), + API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT), + API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB), + API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE), + API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT), + API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT), + /* Obsolete PULLDOWN API command */ + API_ENTRY(0xb1, API_CACHE), + + /* MPEG decoder API */ + API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT), + API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA), + API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/), + API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE), + API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE), + API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE), + + /* OSD API */ + API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE), + API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE), + API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT), + API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT), + API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT), + API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE), + API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE) +}; + +static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb) +{ + u32 flags = readl(&mbdata->mbox[mb].flags); + int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE); + + /* if the mailbox is free, then try to claim it */ + if (is_free && !test_and_set_bit(mb, &mbdata->busy)) { + write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags); + return 1; + } + return 0; +} + +/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not + attempted here. */ +static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags) +{ + unsigned long then = jiffies; + int i, mb; + int max_mbox = mbdata->max_mbox; + int retries = 100; + + /* All slow commands use the same mailbox, serializing them and also + leaving the other mailbox free for simple fast commands. */ + if ((flags & API_FAST_RESULT) == API_RESULT) + max_mbox = 1; + + /* find free non-DMA mailbox */ + for (i = 0; i < retries; i++) { + for (mb = 1; mb <= max_mbox; mb++) + if (try_mailbox(itv, mbdata, mb)) + return mb; + + /* Sleep before a retry, if not atomic */ + if (!(flags & API_NO_WAIT_MB)) { + if (jiffies - then > retries * HZ / 100) + break; + ivtv_sleep_timeout(HZ / 100, 0); + } + } + return -ENODEV; +} + +static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[]) +{ + int i; + + write_sync(cmd, &mbox->cmd); + write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout); + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + write_sync(data[i], &mbox->data[i]); + + write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags); +} + +static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata) +{ + int i; + + for (i = 0; i <= mbdata->max_mbox; i++) { + IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n", + i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags)); + write_sync(0, &mbdata->mbox[i].flags); + clear_bit(i, &mbdata->busy); + } +} + +static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) +{ + struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox; + volatile struct ivtv_mailbox __iomem *mbox; + int api_timeout = HZ; + int flags, mb, i; + unsigned long then; + + /* sanity checks */ + if (NULL == mbdata) { + IVTV_ERR("No mailbox allocated\n"); + return -ENODEV; + } + if (args < 0 || args > CX2341X_MBOX_MAX_DATA || + cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) { + IVTV_ERR("Invalid API call: cmd = 0x%02x, args = %d\n", cmd, args); + return -EINVAL; + } + + IVTV_DEBUG_API("API Call: %s\n", api_info[cmd].name); + + /* clear possibly uninitialized part of data array */ + for (i = args; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = 0; + + /* If this command was issued within the last 30 minutes and with identical + data, then just return 0 as there is no need to issue this command again. + Just an optimization to prevent unnecessary use of mailboxes. */ + if (itv->api_cache[cmd].last_jiffies && + jiffies - itv->api_cache[cmd].last_jiffies < HZ * 1800 && + !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { + itv->api_cache[cmd].last_jiffies = jiffies; + return 0; + } + + flags = api_info[cmd].flags; + + if (flags & API_DMA) { + for (i = 0; i < 100; i++) { + mb = i % (mbdata->max_mbox + 1); + if (try_mailbox(itv, mbdata, mb)) { + write_mailbox(&mbdata->mbox[mb], cmd, args, data); + clear_bit(mb, &mbdata->busy); + return 0; + } + IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n", + api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags)); + } + IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name); + clear_all_mailboxes(itv, mbdata); + return -EBUSY; + } + + if ((flags & API_FAST_RESULT) == API_FAST_RESULT) + api_timeout = HZ / 10; + + mb = get_mailbox(itv, mbdata, flags); + if (mb < 0) { + IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name); + clear_all_mailboxes(itv, mbdata); + return -EBUSY; + } + mbox = &mbdata->mbox[mb]; + write_mailbox(mbox, cmd, args, data); + if (flags & API_CACHE) { + memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data)); + itv->api_cache[cmd].last_jiffies = jiffies; + } + if ((flags & API_RESULT) == 0) { + clear_bit(mb, &mbdata->busy); + return 0; + } + + /* Get results */ + then = jiffies; + + while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { + if (jiffies - then > api_timeout) { + IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); + /* reset the mailbox, but it is likely too late already */ + write_sync(0, &mbox->flags); + clear_bit(mb, &mbdata->busy); + return -EIO; + } + if (flags & API_NO_WAIT_RES) + mdelay(1); + else + ivtv_sleep_timeout(HZ / 100, 0); + } + if (jiffies - then > HZ / 10) + IVTV_DEBUG_WARN("%s took %lu jiffies (%d per HZ)\n", + api_info[cmd].name, jiffies - then, HZ); + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = readl(&mbox->data[i]); + write_sync(0, &mbox->flags); + clear_bit(mb, &mbdata->busy); + return 0; +} + +int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]) +{ + int res = ivtv_api_call(itv, cmd, args, data); + + /* Allow a single retry, probably already too late though. + If there is no free mailbox then that is usually an indication + of a more serious problem. */ + return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res; +} + +int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + return ivtv_api(priv, cmd, in, data); +} + +int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...) +{ + va_list ap; + int i; + + va_start(ap, args); + for (i = 0; i < args; i++) { + data[i] = va_arg(ap, u32); + } + va_end(ap); + return ivtv_api(itv, cmd, args, data); +} + +int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list ap; + int i; + + va_start(ap, args); + for (i = 0; i < args; i++) { + data[i] = va_arg(ap, u32); + } + va_end(ap); + return ivtv_api(itv, cmd, args, data); +} + +/* This one is for stuff that can't sleep.. irq handlers, etc.. */ +void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[]) +{ + int i; + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = readl(&mbdata->mbox[mb].data[i]); +} diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h new file mode 100644 index 0000000..79b8aec --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-mailbox.h @@ -0,0 +1,25 @@ +/* + mailbox functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]); +int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); +int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); +int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); +int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c new file mode 100644 index 0000000..ccfcef1 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-queue.c @@ -0,0 +1,262 @@ +/* + buffer queues. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-streams.h" +#include "ivtv-queue.h" +#include "ivtv-mailbox.h" + +int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes) +{ + if (s->buf_size - buf->bytesused < copybytes) + copybytes = s->buf_size - buf->bytesused; + if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) { + return -EFAULT; + } + buf->bytesused += copybytes; + return copybytes; +} + +void ivtv_buf_swap(struct ivtv_buffer *buf) +{ + int i; + + for (i = 0; i < buf->bytesused; i += 4) + swab32s((u32 *)(buf->buf + i)); +} + +void ivtv_queue_init(struct ivtv_queue *q) +{ + INIT_LIST_HEAD(&q->list); + q->buffers = 0; + q->length = 0; + q->bytesused = 0; +} + +void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q) +{ + unsigned long flags = 0; + + /* clear the buffer if it is going to be enqueued to the free queue */ + if (q == &s->q_free) { + buf->bytesused = 0; + buf->readpos = 0; + buf->b_flags = 0; + } + spin_lock_irqsave(&s->qlock, flags); + list_add_tail(&buf->list, &q->list); + q->buffers++; + q->length += s->buf_size; + q->bytesused += buf->bytesused - buf->readpos; + spin_unlock_irqrestore(&s->qlock, flags); +} + +struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q) +{ + struct ivtv_buffer *buf = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&s->qlock, flags); + if (!list_empty(&q->list)) { + buf = list_entry(q->list.next, struct ivtv_buffer, list); + list_del_init(q->list.next); + q->buffers--; + q->length -= s->buf_size; + q->bytesused -= buf->bytesused - buf->readpos; + } + spin_unlock_irqrestore(&s->qlock, flags); + return buf; +} + +static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from, + struct ivtv_queue *to, int clear, int full) +{ + struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list); + + list_move_tail(from->list.next, &to->list); + from->buffers--; + from->length -= s->buf_size; + from->bytesused -= buf->bytesused - buf->readpos; + /* special handling for q_free */ + if (clear) + buf->bytesused = buf->readpos = buf->b_flags = 0; + else if (full) { + /* special handling for stolen buffers, assume + all bytes are used. */ + buf->bytesused = s->buf_size; + buf->readpos = buf->b_flags = 0; + } + to->buffers++; + to->length += s->buf_size; + to->bytesused += buf->bytesused - buf->readpos; +} + +/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. + If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. + If 'steal' != NULL, then buffers may also taken from that queue if + needed. + + The buffer is automatically cleared if it goes to the free queue. It is + also cleared if buffers need to be taken from the 'steal' queue and + the 'from' queue is the free queue. + + When 'from' is q_free, then needed_bytes is compared to the total + available buffer length, otherwise needed_bytes is compared to the + bytesused value. For the 'steal' queue the total available buffer + length is always used. + + -ENOMEM is returned if the buffers could not be obtained, 0 if all + buffers where obtained from the 'from' list and if non-zero then + the number of stolen buffers is returned. */ +int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, + struct ivtv_queue *to, int needed_bytes) +{ + unsigned long flags; + int rc = 0; + int from_free = from == &s->q_free; + int to_free = to == &s->q_free; + int bytes_available; + + spin_lock_irqsave(&s->qlock, flags); + if (needed_bytes == 0) { + from_free = 1; + needed_bytes = from->length; + } + + bytes_available = from_free ? from->length : from->bytesused; + bytes_available += steal ? steal->length : 0; + + if (bytes_available < needed_bytes) { + spin_unlock_irqrestore(&s->qlock, flags); + return -ENOMEM; + } + if (from_free) { + u32 old_length = to->length; + + while (to->length - old_length < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + ivtv_queue_move_buf(s, from, to, 1, 0); + } + } + else { + u32 old_bytesused = to->bytesused; + + while (to->bytesused - old_bytesused < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + ivtv_queue_move_buf(s, from, to, to_free, rc); + } + } + spin_unlock_irqrestore(&s->qlock, flags); + return rc; +} + +void ivtv_flush_queues(struct ivtv_stream *s) +{ + ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); +} + +int ivtv_stream_alloc(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + int SGsize = sizeof(struct ivtv_SG_element) * s->buffers; + int i; + + if (s->buffers == 0) + return 0; + + IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n", + s->dma != PCI_DMA_NONE ? "DMA " : "", + s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); + + /* Allocate DMA SG Arrays */ + if (s->dma != PCI_DMA_NONE) { + s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL); + if (s->SGarray == NULL) { + IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name); + return -ENOMEM; + } + s->SG_length = 0; + s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma); + ivtv_stream_sync_for_cpu(s); + } + + /* allocate stream buffers. Initially all buffers are in q_free. */ + for (i = 0; i < s->buffers; i++) { + struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL); + + if (buf == NULL) + break; + buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL); + if (buf->buf == NULL) { + kfree(buf); + break; + } + INIT_LIST_HEAD(&buf->list); + if (s->dma != PCI_DMA_NONE) { + buf->dma_handle = pci_map_single(s->itv->dev, + buf->buf, s->buf_size + 256, s->dma); + ivtv_buf_sync_for_cpu(s, buf); + } + ivtv_enqueue(s, buf, &s->q_free); + } + if (i == s->buffers) + return 0; + IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name); + ivtv_stream_free(s); + return -ENOMEM; +} + +void ivtv_stream_free(struct ivtv_stream *s) +{ + struct ivtv_buffer *buf; + + /* move all buffers to q_free */ + ivtv_flush_queues(s); + + /* empty q_free */ + while ((buf = ivtv_dequeue(s, &s->q_free))) { + if (s->dma != PCI_DMA_NONE) + pci_unmap_single(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); + kfree(buf->buf); + kfree(buf); + } + + /* Free SG Array/Lists */ + if (s->SGarray != NULL) { + if (s->SG_handle != IVTV_DMA_UNMAPPED) { + pci_unmap_single(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); + s->SG_handle = IVTV_DMA_UNMAPPED; + } + s->SGarray = NULL; + s->SG_length = 0; + } +} diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h new file mode 100644 index 0000000..903edd4 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-queue.h @@ -0,0 +1,64 @@ +/* + buffer queues. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define IVTV_DMA_UNMAPPED ((u32) -1) + +/* ivtv_buffer utility functions */ +static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf) +{ + if (s->dma != PCI_DMA_NONE) + pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); +} + +static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf) +{ + if (s->dma != PCI_DMA_NONE) + pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); +} + +int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes); +void ivtv_buf_swap(struct ivtv_buffer *buf); + +/* ivtv_queue utility functions */ +void ivtv_queue_init(struct ivtv_queue *q); +void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q); +struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q); +int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, + struct ivtv_queue *to, int needed_bytes); +void ivtv_flush_queues(struct ivtv_stream *s); + +/* ivtv_stream utility functions */ +int ivtv_stream_alloc(struct ivtv_stream *s); +void ivtv_stream_free(struct ivtv_stream *s); + +static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s) +{ + pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); +} + +static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s) +{ + pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); +} diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c new file mode 100644 index 0000000..01a41a8 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -0,0 +1,977 @@ +/* + init/start/stop/exit stream functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* License: GPL + * Author: Kevin Thayer + * + * This file will hold API related functions, both internal (firmware api) + * and external (v4l2, etc) + * + * ----- + * MPG600/MPG160 support by T.Adachi + * and Takeru KOMORIYA + * + * AVerMedia M179 GPIO info by Chris Pinkham + * using information provided by Jiun-Kuei Jung @ AVerMedia. + */ + +#include "ivtv-driver.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-mailbox.h" +#include "ivtv-audio.h" +#include "ivtv-video.h" +#include "ivtv-vbi.h" +#include "ivtv-ioctl.h" +#include "ivtv-irq.h" +#include "ivtv-streams.h" +#include "ivtv-cards.h" + +static struct file_operations ivtv_v4l2_enc_fops = { + .owner = THIS_MODULE, + .read = ivtv_v4l2_read, + .write = ivtv_v4l2_write, + .open = ivtv_v4l2_open, + .ioctl = ivtv_v4l2_ioctl, + .release = ivtv_v4l2_close, + .poll = ivtv_v4l2_enc_poll, +}; + +static struct file_operations ivtv_v4l2_dec_fops = { + .owner = THIS_MODULE, + .read = ivtv_v4l2_read, + .write = ivtv_v4l2_write, + .open = ivtv_v4l2_open, + .ioctl = ivtv_v4l2_ioctl, + .release = ivtv_v4l2_close, + .poll = ivtv_v4l2_dec_poll, +}; + +static struct { + const char *name; + int vfl_type; + int minor_offset; + int dma, pio; + enum v4l2_buf_type buf_type; + struct file_operations *fops; +} ivtv_stream_info[] = { + { /* IVTV_ENC_STREAM_TYPE_MPG */ + "encoder MPEG", + VFL_TYPE_GRABBER, 0, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_YUV */ + "encoder YUV", + VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_VBI */ + "encoder VBI", + VFL_TYPE_VBI, 0, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_PCM */ + "encoder PCM audio", + VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_RAD */ + "encoder radio", + VFL_TYPE_RADIO, 0, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_DEC_STREAM_TYPE_MPG */ + "decoder MPEG", + VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, + PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + &ivtv_v4l2_dec_fops + }, + { /* IVTV_DEC_STREAM_TYPE_VBI */ + "decoder VBI", + VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_DEC_STREAM_TYPE_VOUT */ + "decoder VOUT", + VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT, + &ivtv_v4l2_dec_fops + }, + { /* IVTV_DEC_STREAM_TYPE_YUV */ + "decoder YUV", + VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, + PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + &ivtv_v4l2_dec_fops + } +}; + +static void ivtv_stream_init(struct ivtv *itv, int type) +{ + struct ivtv_stream *s = &itv->streams[type]; + struct video_device *dev = s->v4l2dev; + + /* we need to keep v4l2dev, so restore it afterwards */ + memset(s, 0, sizeof(*s)); + s->v4l2dev = dev; + + /* initialize ivtv_stream fields */ + s->itv = itv; + s->type = type; + s->name = ivtv_stream_info[type].name; + + if (ivtv_stream_info[type].pio) + s->dma = PCI_DMA_NONE; + else + s->dma = ivtv_stream_info[type].dma; + s->buf_size = itv->stream_buf_size[type]; + if (s->buf_size) + s->buffers = itv->options.megabytes[type] * 1024 * 1024 / s->buf_size; + spin_lock_init(&s->qlock); + init_waitqueue_head(&s->waitq); + s->id = -1; + s->SG_handle = IVTV_DMA_UNMAPPED; + ivtv_queue_init(&s->q_free); + ivtv_queue_init(&s->q_full); + ivtv_queue_init(&s->q_dma); + ivtv_queue_init(&s->q_predma); + ivtv_queue_init(&s->q_io); +} + +static int ivtv_reg_dev(struct ivtv *itv, int type) +{ + struct ivtv_stream *s = &itv->streams[type]; + int vfl_type = ivtv_stream_info[type].vfl_type; + int minor_offset = ivtv_stream_info[type].minor_offset; + int minor; + + /* These four fields are always initialized. If v4l2dev == NULL, then + this stream is not in use. In that case no other fields but these + four can be used. */ + s->v4l2dev = NULL; + s->itv = itv; + s->type = type; + s->name = ivtv_stream_info[type].name; + + /* Check whether the radio is supported */ + if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO)) + return 0; + if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return 0; + + if (minor_offset >= 0) + /* card number + user defined offset + device offset */ + minor = itv->num + ivtv_first_minor + minor_offset; + else + minor = -1; + + /* User explicitly selected 0 buffers for these streams, so don't + create them. */ + if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE && + itv->options.megabytes[type] == 0) { + IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name); + return 0; + } + + ivtv_stream_init(itv, type); + + /* allocate and initialize the v4l2 video device structure */ + s->v4l2dev = video_device_alloc(); + if (s->v4l2dev == NULL) { + IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name); + return -ENOMEM; + } + + s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT | + VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER; + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + s->v4l2dev->type |= VID_TYPE_MPEG_DECODER; + } + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s", + itv->num, s->name); + + s->v4l2dev->minor = minor; + s->v4l2dev->dev = &itv->dev->dev; + s->v4l2dev->fops = ivtv_stream_info[type].fops; + s->v4l2dev->release = video_device_release; + + if (minor >= 0) { + /* Register device. First try the desired minor, then any free one. */ + if (video_register_device(s->v4l2dev, vfl_type, minor) && + video_register_device(s->v4l2dev, vfl_type, -1)) { + IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n", + s->name, minor); + video_device_release(s->v4l2dev); + s->v4l2dev = NULL; + return -ENOMEM; + } + } + else { + /* Don't register a 'hidden' stream (OSD) */ + IVTV_INFO("Created framebuffer stream for %s\n", s->name); + return 0; + } + + switch (vfl_type) { + case VFL_TYPE_GRABBER: + IVTV_INFO("Registered device video%d for %s (%d MB)\n", + s->v4l2dev->minor, s->name, itv->options.megabytes[type]); + break; + case VFL_TYPE_RADIO: + IVTV_INFO("Registered device radio%d for %s\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); + break; + case VFL_TYPE_VBI: + if (itv->options.megabytes[type]) + IVTV_INFO("Registered device vbi%d for %s (%d MB)\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, + s->name, itv->options.megabytes[type]); + else + IVTV_INFO("Registered device vbi%d for %s\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name); + break; + } + return 0; +} + +/* Initialize v4l2 variables and register v4l2 devices */ +int ivtv_streams_setup(struct ivtv *itv) +{ + int type; + + /* Setup V4L2 Devices */ + for (type = 0; type < IVTV_MAX_STREAMS; type++) { + /* Register Device */ + if (ivtv_reg_dev(itv, type)) + break; + + if (itv->streams[type].v4l2dev == NULL) + continue; + + /* Allocate Stream */ + if (ivtv_stream_alloc(&itv->streams[type])) + break; + } + if (type == IVTV_MAX_STREAMS) { + return 0; + } + + /* One or more streams could not be initialized. Clean 'em all up. */ + ivtv_streams_cleanup(itv); + return -ENOMEM; +} + +/* Unregister v4l2 devices */ +void ivtv_streams_cleanup(struct ivtv *itv) +{ + int type; + + /* Teardown all streams */ + for (type = 0; type < IVTV_MAX_STREAMS; type++) { + struct video_device *vdev = itv->streams[type].v4l2dev; + + itv->streams[type].v4l2dev = NULL; + if (vdev == NULL) + continue; + + ivtv_stream_free(&itv->streams[type]); + /* Free Device */ + if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */ + video_device_release(vdev); + else /* All others, just unregister. */ + video_unregister_device(vdev); + } +} + +static void ivtv_vbi_setup(struct ivtv *itv) +{ + int raw = itv->vbi.sliced_in->service_set == 0; + u32 data[CX2341X_MBOX_MAX_DATA]; + int lines; + int i; + + /* If Embed then streamtype must be Program */ + /* TODO: should we really do this? */ + if (0 && !raw && itv->vbi.insert_mpeg) { + itv->params.stream_type = 0; + + /* assign stream type */ + ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type); + } + + /* Reset VBI */ + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); + + if (itv->is_60hz) { + itv->vbi.count = 12; + itv->vbi.start[0] = 10; + itv->vbi.start[1] = 273; + } else { /* PAL/SECAM */ + itv->vbi.count = 18; + itv->vbi.start[0] = 6; + itv->vbi.start[1] = 318; + } + + /* setup VBI registers */ + itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + + /* determine number of lines and total number of VBI bytes. + A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 + The '- 1' byte is probably an unused U or V byte. Or something... + A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal + header, 42 data bytes + checksum (to be confirmed) */ + if (raw) { + lines = itv->vbi.count * 2; + } else { + lines = itv->is_60hz ? 24 : 38; + if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840)) + lines += 2; + } + + itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); + + /* Note: sliced vs raw flag doesn't seem to have any effect + TODO: check mode (0x02) value with older ivtv versions. */ + data[0] = raw | 0x02 | (0xbd << 8); + + /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ + data[1] = 1; + /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */ + data[2] = raw ? 4 : 8; + /* The start/stop codes determine which VBI lines end up in the raw VBI data area. + The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line + is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video) + code. These values for raw VBI are obtained from a driver disassembly. The sliced + start/stop codes was deduced from this, but they do not appear in the driver. + Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54. + However, I have no idea what these values are for. */ + if (itv->hw_flags & IVTV_HW_CX25840) { + /* Setup VBI for the cx25840 digitizer */ + if (raw) { + data[3] = 0x20602060; + data[4] = 0x30703070; + } else { + data[3] = 0xB0F0B0F0; + data[4] = 0xA0E0A0E0; + } + /* Lines per frame */ + data[5] = lines; + /* bytes per line */ + data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); + } else { + /* Setup VBI for the saa7115 digitizer */ + if (raw) { + data[3] = 0x25256262; + data[4] = 0x387F7F7F; + } else { + data[3] = 0xABABECEC; + data[4] = 0xB6F1F1F1; + } + /* Lines per frame */ + data[5] = lines; + /* bytes per line */ + data[6] = itv->vbi.enc_size / lines; + } + + IVTV_DEBUG_INFO( + "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n", + data[0], data[1], data[2], data[5], data[6]); + + ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data); + + /* returns the VBI encoder memory area. */ + itv->vbi.enc_start = data[2]; + itv->vbi.fpi = data[0]; + if (!itv->vbi.fpi) + itv->vbi.fpi = 1; + + IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n", + itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer); + + /* select VBI lines. + Note that the sliced argument seems to have no effect. */ + for (i = 2; i <= 24; i++) { + int valid; + + if (itv->is_60hz) { + valid = i >= 10 && i < 22; + } else { + valid = i >= 6 && i < 24; + } + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1, + valid, 0 , 0, 0); + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000, + valid, 0, 0, 0); + } + + /* Remaining VBI questions: + - Is it possible to select particular VBI lines only for inclusion in the MPEG + stream? Currently you can only get the first X lines. + - Is mixed raw and sliced VBI possible? + - What's the meaning of the raw/sliced flag? + - What's the meaning of params 2, 3 & 4 of the Select VBI command? */ +} + +int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv *itv = s->itv; + int captype = 0, subtype = 0; + int enable_passthrough = 0; + + if (s->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name); + + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_MPG: + captype = 0; + subtype = 3; + + /* Stop Passthrough */ + if (itv->output_mode == OUT_PASSTHROUGH) { + ivtv_passthrough_mode(itv, 0); + enable_passthrough = 1; + } + itv->mpg_data_received = itv->vbi_data_inserted = 0; + itv->dualwatch_jiffies = jiffies; + itv->dualwatch_stereo_mode = itv->params.audio_properties & 0x0300; + itv->search_pack_header = 0; + break; + + case IVTV_ENC_STREAM_TYPE_YUV: + if (itv->output_mode == OUT_PASSTHROUGH) { + captype = 2; + subtype = 11; /* video+audio+decoder */ + break; + } + captype = 1; + subtype = 1; + break; + case IVTV_ENC_STREAM_TYPE_PCM: + captype = 1; + subtype = 2; + break; + case IVTV_ENC_STREAM_TYPE_VBI: + captype = 1; + subtype = 4; + + itv->vbi.frame = 0; + itv->vbi.inserted_frame = 0; + memset(itv->vbi.sliced_mpeg_size, + 0, sizeof(itv->vbi.sliced_mpeg_size)); + break; + default: + return -EINVAL; + } + s->subtype = subtype; + s->buffers_stolen = 0; + + /* mute/unmute video */ + ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0); + + /* Clear Streamoff flags in case left from last capture */ + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + if (atomic_read(&itv->capturing) == 0) { + /* Always use frame based mode. Experiments have demonstrated that byte + stream based mode results in dropped frames and corruption. Not often, + but occasionally. Many thanks go to Leonard Orb who spent a lot of + effort and time trying to trace the cause of the drop outs. */ + /* 1 frame per DMA */ + /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */ + ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1); + + /* Stuff from Windows, we don't know what it is */ + ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0); + /* According to the docs, this should be correct. However, this is + untested. I don't dare enable this without having tested it. + Only very few old cards actually have this hardware combination. + ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, + ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0); + */ + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415); + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0); + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1); + ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); + + /* assign placeholder */ + ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer); + + /* Setup VBI */ + if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) { + ivtv_vbi_setup(itv); + } + + /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */ + ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400); + itv->pgm_info_offset = data[0]; + itv->pgm_info_num = data[1]; + itv->pgm_info_write_idx = 0; + itv->pgm_info_read_idx = 0; + + IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n", + itv->pgm_info_offset, itv->pgm_info_num); + + /* Setup API for Stream */ + cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); + } + + /* Vsync Setup */ + if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { + /* event notification (on) */ + ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1); + ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); + } + + if (atomic_read(&itv->capturing) == 0) { + /* Clear all Pending Interrupts */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + clear_bit(IVTV_F_I_EOS, &itv->i_flags); + + /* Initialize Digitizer for Capture */ + ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); + + ivtv_sleep_timeout(HZ / 10, 0); + } + + /* begin_capture */ + if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype)) + { + IVTV_DEBUG_WARN( "Error starting capture!\n"); + return -EINVAL; + } + + /* Start Passthrough */ + if (enable_passthrough) { + ivtv_passthrough_mode(itv, 1); + } + + if (s->type == IVTV_ENC_STREAM_TYPE_VBI) + ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); + else + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + /* you're live! sit back and await interrupts :) */ + atomic_inc(&itv->capturing); + return 0; +} + +static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv *itv = s->itv; + int datatype; + + if (s->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); + + /* disable VBI signals, if the MPEG stream contains VBI data, + then that data will be processed automatically for you. */ + ivtv_disable_vbi(itv); + + /* set audio mode to left/stereo for dual/stereo mode. */ + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + + /* set number of internal decoder buffers */ + ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0); + + /* prebuffering */ + ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1); + + /* extract from user packets */ + ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1); + itv->vbi.dec_start = data[0]; + + IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n", + itv->vbi.dec_start, data[1]); + + /* set decoder source settings */ + /* Data type: 0 = mpeg from host, + 1 = yuv from encoder, + 2 = yuv_from_host */ + switch (s->type) { + case IVTV_DEC_STREAM_TYPE_YUV: + datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2; + IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype); + break; + case IVTV_DEC_STREAM_TYPE_MPG: + default: + datatype = 0; + break; + } + if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype, + itv->params.width, itv->params.height, itv->params.audio_properties)) { + IVTV_DEBUG_WARN("COULDN'T INITIALIZE DECODER SOURCE\n"); + } + return 0; +} + +int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) +{ + struct ivtv *itv = s->itv; + + if (s->v4l2dev == NULL) + return -EINVAL; + + if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) + return 0; /* already started */ + + IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); + + /* Clear Streamoff */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { + /* Initialize Decoder */ + /* Reprogram Decoder YUV Buffers for YUV */ + write_reg(yuv_offset[0] >> 4, 0x82c); + write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); + write_reg(yuv_offset[0] >> 4, 0x834); + write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); + + write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24); + + write_reg_sync(0x00108080, 0x2898); + /* Enable YUV decoder output */ + write_reg_sync(0x01, IVTV_REG_VDM); + } + + ivtv_setup_v4l2_decode_stream(s); + + /* set dma size to 65536 bytes */ + ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); + + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + /* Zero out decoder counters */ + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]); + + /* turn on notification of dual/stereo mode change */ + ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); + + /* start playback */ + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0); + + /* Clear the following Interrupt mask bits for decoding */ + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE); + IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask); + + /* you're live! sit back and await interrupts :) */ + atomic_inc(&itv->decoding); + return 0; +} + +void ivtv_stop_all_captures(struct ivtv *itv) +{ + int i; + + for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) { + struct ivtv_stream *s = &itv->streams[i]; + + if (s->v4l2dev == NULL) + continue; + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + ivtv_stop_v4l2_encode_stream(s, 0); + } + } +} + +int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) +{ + struct ivtv *itv = s->itv; + DECLARE_WAITQUEUE(wait, current); + int cap_type; + unsigned long then; + int stopmode; + u32 data[CX2341X_MBOX_MAX_DATA]; + + if (s->v4l2dev == NULL) + return -EINVAL; + + /* This function assumes that you are allowed to stop the capture + and that we are actually capturing */ + + IVTV_DEBUG_INFO("Stop Capture\n"); + + if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) + return 0; + if (atomic_read(&itv->capturing) == 0) + return 0; + + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_YUV: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_PCM: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_VBI: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_MPG: + default: + cap_type = 0; + break; + } + + /* Stop Capture Mode */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { + stopmode = 0; + } else { + stopmode = 1; + } + + /* end_capture */ + /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype); + + /* only run these if we're shutting down the last cap */ + if (atomic_read(&itv->capturing) - 1 == 0) { + /* event notification (off) */ + if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { + /* type: 0 = refresh */ + /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ + ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); + ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); + } + } + + then = jiffies; + + if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { + /* only run these if we're shutting down the last cap */ + unsigned long duration; + + then = jiffies; + add_wait_queue(&itv->cap_w, &wait); + + set_current_state(TASK_INTERRUPTIBLE); + + /* wait 2s for EOS interrupt */ + while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && jiffies < then + 2 * HZ) { + schedule_timeout(HZ / 100); + } + + /* To convert jiffies to ms, we must multiply by 1000 + * and divide by HZ. To avoid runtime division, we + * convert this to multiplication by 1000/HZ. + * Since integer division truncates, we get the best + * accuracy if we do a rounding calculation of the constant. + * Think of the case where HZ is 1024. + */ + duration = ((1000 + HZ / 2) / HZ) * (jiffies - then); + + if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) { + IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); + IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); + } else { + IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&itv->cap_w, &wait); + } + + then = jiffies; + /* Make sure DMA is complete */ + add_wait_queue(&s->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + do { + /* check if DMA is pending */ + if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */ + (read_reg(IVTV_REG_DMASTATUS) & 0x02)) { + /* Check for last DMA */ + ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0); + + if (data[0] == 1) { + IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]); + break; + } + } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) { + break; + } + + ivtv_sleep_timeout(HZ / 100, 1); + } while (then + HZ * 2 > jiffies); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->waitq, &wait); + } + + atomic_dec(&itv->capturing); + + /* Clear capture and no-read bits */ + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + + if (s->type == IVTV_ENC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); + + if (atomic_read(&itv->capturing) > 0) { + return 0; + } + + /* Set the following Interrupt mask bits for capture */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + wake_up(&s->waitq); + + return 0; +} + +int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) +{ + struct ivtv *itv = s->itv; + + if (s->v4l2dev == NULL) + return -EINVAL; + + if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG) + return -EINVAL; + + if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags)) + return 0; + + IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", pts, flags); + + /* Stop Decoder */ + if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) { + u32 tmp = 0; + + /* Wait until the decoder is no longer running */ + if (pts) { + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, + 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32)); + } + while (1) { + u32 data[CX2341X_MBOX_MAX_DATA]; + ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0); + if (s->q_full.buffers + s->q_dma.buffers == 0) { + if (tmp == data[3]) + break; + tmp = data[3]; + } + if (ivtv_sleep_timeout(HZ/10, 1)) + break; + } + } + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0); + + /* turn off notification of dual/stereo mode change */ + ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); + + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE); + + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + ivtv_flush_queues(s); + + if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { + /* disable VBI on TV-out */ + ivtv_disable_vbi(itv); + } + + /* decrement decoding */ + atomic_dec(&itv->decoding); + + set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); + wake_up(&itv->event_waitq); + + /* wake up wait queues */ + wake_up(&s->waitq); + + return 0; +} + +int ivtv_passthrough_mode(struct ivtv *itv, int enable) +{ + struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV]; + struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + + if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n"); + + /* Prevent others from starting/stopping streams while we + initiate/terminate passthrough mode */ + if (enable) { + if (itv->output_mode == OUT_PASSTHROUGH) { + return 0; + } + if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH) + return -EBUSY; + + /* Fully initialize stream, and then unflag init */ + set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); + set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); + + /* Setup YUV Decoder */ + ivtv_setup_v4l2_decode_stream(dec_stream); + + /* Start Decoder */ + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1); + atomic_inc(&itv->decoding); + + /* Setup capture if not already done */ + if (atomic_read(&itv->capturing) == 0) { + cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); + } + + /* Start Passthrough Mode */ + ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11); + atomic_inc(&itv->capturing); + return 0; + } + + if (itv->output_mode != OUT_PASSTHROUGH) + return 0; + + /* Stop Passthrough Mode */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11); + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0); + + atomic_dec(&itv->capturing); + atomic_dec(&itv->decoding); + clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); + clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); + itv->output_mode = OUT_NONE; + + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h new file mode 100644 index 0000000..8597b75 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-streams.h @@ -0,0 +1,31 @@ +/* + init/start/stop/exit stream functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_streams_setup(struct ivtv *itv); +void ivtv_streams_cleanup(struct ivtv *itv); + +/* Capture related */ +int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s); +int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end); +int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset); +int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts); + +void ivtv_stop_all_captures(struct ivtv *itv); +int ivtv_passthrough_mode(struct ivtv *itv, int enable); diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c new file mode 100644 index 0000000..bd642e1 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -0,0 +1,200 @@ +/* + User DMA + + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-streams.h" +#include "ivtv-udma.h" + +void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size) +{ + dma_page->uaddr = first & PAGE_MASK; + dma_page->offset = first & ~PAGE_MASK; + dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK); + dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT; + dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT; + dma_page->page_count = dma_page->last - dma_page->first + 1; + if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset; +} + +int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset) +{ + int i, offset; + + offset = dma_page->offset; + + /* Fill SG Array with new values */ + for (i = 0; i < dma_page->page_count; i++) { + if (i == dma_page->page_count - 1) { + dma->SGlist[map_offset].length = dma_page->tail; + } + else { + dma->SGlist[map_offset].length = PAGE_SIZE - offset; + } + dma->SGlist[map_offset].offset = offset; + dma->SGlist[map_offset].page = dma->map[map_offset]; + offset = 0; + map_offset++; + } + return map_offset; +} + +void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) { + int i; + struct scatterlist *sg; + + for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) { + dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg)); + dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg)); + dma->SGarray[i].dst = cpu_to_le32(buffer_offset); + buffer_offset += sg_dma_len(sg); + + split -= sg_dma_len(sg); + if (split == 0) + buffer_offset = buffer_offset_2; + } +} + +/* User DMA Buffers */ +void ivtv_udma_alloc(struct ivtv *itv) +{ + if (itv->udma.SG_handle == 0) { + /* Map DMA Page Array Buffer */ + itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + ivtv_udma_sync_for_cpu(itv); + } +} + +int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, + void __user *userbuf, int size_in_bytes) +{ + struct ivtv_dma_page_info user_dma; + struct ivtv_user_dma *dma = &itv->udma; + int err; + + IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr); + + /* Still in USE */ + if (dma->SG_length || dma->page_count) { + IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n", + dma->SG_length, dma->page_count); + return -EBUSY; + } + + ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes); + + if (user_dma.page_count <= 0) { + IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n", + user_dma.page_count, size_in_bytes, user_dma.offset); + return -EINVAL; + } + + /* Get user pages for DMA Xfer */ + down_read(¤t->mm->mmap_sem); + err = get_user_pages(current, current->mm, + user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL); + up_read(¤t->mm->mmap_sem); + + if (user_dma.page_count != err) { + IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", + err, user_dma.page_count); + return -EINVAL; + } + + dma->page_count = user_dma.page_count; + + /* Fill SG List with new values */ + ivtv_udma_fill_sg_list(dma, &user_dma, 0); + + /* Map SG List */ + dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + + /* Fill SG Array with new values */ + ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); + + /* Tag SG Array with Interrupt Bit */ + dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); + + ivtv_udma_sync_for_device(itv); + return dma->page_count; +} + +void ivtv_udma_unmap(struct ivtv *itv) +{ + struct ivtv_user_dma *dma = &itv->udma; + int i; + + IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n"); + + /* Nothing to free */ + if (dma->page_count == 0) + return; + + /* Unmap Scatterlist */ + if (dma->SG_length) { + pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + dma->SG_length = 0; + } + /* sync DMA */ + ivtv_udma_sync_for_cpu(itv); + + /* Release User Pages */ + for (i = 0; i < dma->page_count; i++) { + put_page(dma->map[i]); + } + dma->page_count = 0; +} + +void ivtv_udma_free(struct ivtv *itv) +{ + /* Unmap SG Array */ + if (itv->udma.SG_handle) { + pci_unmap_single(itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + } + + /* Unmap Scatterlist */ + if (itv->udma.SG_length) { + pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); + } +} + +void ivtv_udma_start(struct ivtv *itv) +{ + IVTV_DEBUG_DMA("start UDMA\n"); + write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + set_bit(IVTV_F_I_UDMA, &itv->i_flags); +} + +void ivtv_udma_prepare(struct ivtv *itv) +{ + unsigned long flags; + + spin_lock_irqsave(&itv->dma_reg_lock, flags); + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + ivtv_udma_start(itv); + else + set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); + spin_unlock_irqrestore(&itv->dma_reg_lock, flags); +} diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h new file mode 100644 index 0000000..e131bcc --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-udma.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2006-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* User DMA functions */ +void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size); +int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset); +void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split); +int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, + void __user *userbuf, int size_in_bytes); +void ivtv_udma_unmap(struct ivtv *itv); +void ivtv_udma_free(struct ivtv *itv); +void ivtv_udma_alloc(struct ivtv *itv); +void ivtv_udma_prepare(struct ivtv *itv); +void ivtv_udma_start(struct ivtv *itv); + +static inline void ivtv_udma_sync_for_device(struct ivtv *itv) +{ + pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} + +static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv) +{ + pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c new file mode 100644 index 0000000..5efa5a8 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -0,0 +1,538 @@ +/* + Vertical Blank Interval support functions + Copyright (C) 2004-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-video.h" +#include "ivtv-vbi.h" +#include "ivtv-ioctl.h" +#include "ivtv-queue.h" + +static int odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static void passthrough_vbi_data(struct ivtv *itv, int cnt) +{ + int wss = 0; + u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 }; + u8 vps[13]; + int found_cc = 0; + int found_wss = 0; + int found_vps = 0; + int cc_pos = itv->vbi.cc_pos; + int i; + + for (i = 0; i < cnt; i++) { + struct v4l2_sliced_vbi_data *d = itv->vbi.sliced_dec_data + i; + + if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) { + found_cc = 1; + if (d->field) { + cc[2] = d->data[0]; + cc[3] = d->data[1]; + } else { + cc[0] = d->data[0]; + cc[1] = d->data[1]; + } + } + else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) { + memcpy(vps, d->data, sizeof(vps)); + found_vps = 1; + } + else if (d->id == V4L2_SLICED_WSS_625 && d->line == 23 && d->field == 0) { + wss = d->data[0] | d->data[1] << 8; + found_wss = 1; + } + } + + if (itv->vbi.wss_found != found_wss || itv->vbi.wss != wss) { + itv->vbi.wss = wss; + itv->vbi.wss_found = found_wss; + set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + } + + if (found_vps || itv->vbi.vps_found) { + itv->vbi.vps[0] = vps[2]; + itv->vbi.vps[1] = vps[8]; + itv->vbi.vps[2] = vps[9]; + itv->vbi.vps[3] = vps[10]; + itv->vbi.vps[4] = vps[11]; + itv->vbi.vps_found = found_vps; + set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + } + + if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) { + itv->vbi.cc_data_odd[cc_pos] = cc[0]; + itv->vbi.cc_data_odd[cc_pos + 1] = cc[1]; + itv->vbi.cc_data_even[cc_pos] = cc[2]; + itv->vbi.cc_data_even[cc_pos + 1] = cc[3]; + itv->vbi.cc_pos = cc_pos + 2; + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + } +} + +static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) +{ + int line = 0; + int i; + u32 linemask[2] = { 0, 0 }; + unsigned short size; + static const u8 mpeg_hdr_data[] = { + 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66, + 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff, + 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80, + 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff + }; + const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ + int idx = itv->vbi.frame % IVTV_VBI_FRAMES; + u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0]; + + for (i = 0; i < lines; i++) { + int f, l; + + if (itv->vbi.sliced_data[i].id == 0) + continue; + + l = itv->vbi.sliced_data[i].line - 6; + f = itv->vbi.sliced_data[i].field; + if (f) + l += 18; + if (l < 32) + linemask[0] |= (1 << l); + else + linemask[1] |= (1 << (l - 32)); + dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id); + memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42); + line++; + } + memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data)); + if (line == 36) { + /* All lines are used, so there is no space for the linemask + (the max size of the VBI data is 36 * 43 + 4 bytes). + So in this case we use the magic number 'ITV0'. */ + memcpy(dst + sd, "ITV0", 4); + memcpy(dst + sd + 4, dst + sd + 12, line * 43); + size = 4 + ((43 * line + 3) & ~3); + } else { + memcpy(dst + sd, "itv0", 4); + memcpy(dst + sd + 4, &linemask[0], 8); + size = 12 + ((43 * line + 3) & ~3); + } + dst[4+16] = (size + 10) >> 8; + dst[5+16] = (size + 10) & 0xff; + dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6); + dst[10+16] = (pts_stamp >> 22) & 0xff; + dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff); + dst[12+16] = (pts_stamp >> 7) & 0xff; + dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1); + itv->vbi.sliced_mpeg_size[idx] = sd + size; +} + +static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p) +{ + u32 linemask[2]; + int i, l, id2; + int line = 0; + + if (!memcmp(p, "itv0", 4)) { + memcpy(linemask, p + 4, 8); + p += 12; + } else if (!memcmp(p, "ITV0", 4)) { + linemask[0] = 0xffffffff; + linemask[1] = 0xf; + p += 4; + } else { + /* unknown VBI data stream */ + return 0; + } + for (i = 0; i < 36; i++) { + int err = 0; + + if (i < 32 && !(linemask[0] & (1 << i))) + continue; + if (i >= 32 && !(linemask[1] & (1 << (i - 32)))) + continue; + id2 = *p & 0xf; + switch (id2) { + case IVTV_SLICED_TYPE_TELETEXT_B: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case IVTV_SLICED_TYPE_CAPTION_525: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[1]) || !odd_parity(p[2]); + break; + case IVTV_SLICED_TYPE_VPS: + id2 = V4L2_SLICED_VPS; + break; + case IVTV_SLICED_TYPE_WSS_625: + id2 = V4L2_SLICED_WSS_625; + break; + default: + id2 = 0; + break; + } + if (err == 0) { + l = (i < 18) ? i + 6 : i - 18 + 6; + itv->vbi.sliced_dec_data[line].line = l; + itv->vbi.sliced_dec_data[line].field = i >= 18; + itv->vbi.sliced_dec_data[line].id = id2; + memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42); + line++; + } + p += 43; + } + while (line < 36) { + itv->vbi.sliced_dec_data[line].id = 0; + itv->vbi.sliced_dec_data[line].line = 0; + itv->vbi.sliced_dec_data[line].field = 0; + line++; + } + return line * sizeof(itv->vbi.sliced_dec_data[0]); +} + +ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count) +{ + /* Should be a __user pointer, but sparse doesn't parse this bit correctly. */ + const struct v4l2_sliced_vbi_data *p = (const struct v4l2_sliced_vbi_data *)ubuf; + u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 }; + int found_cc = 0; + int cc_pos = itv->vbi.cc_pos; + + if (itv->vbi.service_set_out == 0) + return -EPERM; + + while (count >= sizeof(struct v4l2_sliced_vbi_data)) { + switch (p->id) { + case V4L2_SLICED_CAPTION_525: + if (p->id == V4L2_SLICED_CAPTION_525 && + p->line == 21 && + (itv->vbi.service_set_out & + V4L2_SLICED_CAPTION_525) == 0) { + break; + } + found_cc = 1; + if (p->field) { + cc[2] = p->data[0]; + cc[3] = p->data[1]; + } else { + cc[0] = p->data[0]; + cc[1] = p->data[1]; + } + break; + + case V4L2_SLICED_VPS: + if (p->line == 16 && p->field == 0 && + (itv->vbi.service_set_out & V4L2_SLICED_VPS)) { + itv->vbi.vps[0] = p->data[2]; + itv->vbi.vps[1] = p->data[8]; + itv->vbi.vps[2] = p->data[9]; + itv->vbi.vps[3] = p->data[10]; + itv->vbi.vps[4] = p->data[11]; + itv->vbi.vps_found = 1; + set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + } + break; + + case V4L2_SLICED_WSS_625: + if (p->line == 23 && p->field == 0 && + (itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) { + /* No lock needed for WSS */ + itv->vbi.wss = p->data[0] | (p->data[1] << 8); + itv->vbi.wss_found = 1; + set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + } + break; + + default: + break; + } + count -= sizeof(*p); + p++; + } + + if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) { + itv->vbi.cc_data_odd[cc_pos] = cc[0]; + itv->vbi.cc_data_odd[cc_pos + 1] = cc[1]; + itv->vbi.cc_data_even[cc_pos] = cc[2]; + itv->vbi.cc_data_even[cc_pos + 1] = cc[3]; + itv->vbi.cc_pos = cc_pos + 2; + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + } + + return (const char __user *)p - ubuf; +} + +/* Compress raw VBI format, removes leading SAV codes and surplus space after the + field. + Returns new compressed size. */ +static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size) +{ + u32 line_size = itv->vbi.raw_decoder_line_size; + u32 lines = itv->vbi.count; + u8 sav1 = itv->vbi.raw_decoder_sav_odd_field; + u8 sav2 = itv->vbi.raw_decoder_sav_even_field; + u8 *q = buf; + u8 *p; + int i; + + for (i = 0; i < lines; i++) { + p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) { + break; + } + memcpy(q, p + 4, line_size - 4); + q += line_size - 4; + } + return lines * (line_size - 4); +} + + +/* Compressed VBI format, all found sliced blocks put next to one another + Returns new compressed size */ +static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav) +{ + u32 line_size = itv->vbi.sliced_decoder_line_size; + struct v4l2_decode_vbi_line vbi; + int i; + + /* find the first valid line */ + for (i = 0; i < size; i++, buf++) { + if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) + break; + } + + size -= i; + if (size < line_size) { + return line; + } + for (i = 0; i < size / line_size; i++) { + u8 *p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) { + continue; + } + vbi.p = p + 4; + itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + if (vbi.type) { + itv->vbi.sliced_data[line].id = vbi.type; + itv->vbi.sliced_data[line].field = vbi.is_second_field; + itv->vbi.sliced_data[line].line = vbi.line; + memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42); + line++; + } + } + return line; +} + +void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, + u64 pts_stamp, int streamtype) +{ + u8 *p = (u8 *) buf->buf; + u32 size = buf->bytesused; + int y; + + /* Raw VBI data */ + if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) { + u8 type; + + ivtv_buf_swap(buf); + + type = p[3]; + + size = buf->bytesused = compress_raw_buf(itv, p, size); + + /* second field of the frame? */ + if (type == itv->vbi.raw_decoder_sav_even_field) { + /* Dirty hack needed for backwards + compatibility of old VBI software. */ + p += size - 4; + memcpy(p, &itv->vbi.frame, 4); + itv->vbi.frame++; + } + return; + } + + /* Sliced VBI data with data insertion */ + if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) { + int lines; + + ivtv_buf_swap(buf); + + /* first field */ + lines = compress_sliced_buf(itv, 0, p, size / 2, + itv->vbi.sliced_decoder_sav_odd_field); + /* second field */ + /* experimentation shows that the second half does not always begin + at the exact address. So start a bit earlier (hence 32). */ + lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32, + itv->vbi.sliced_decoder_sav_even_field); + /* always return at least one empty line */ + if (lines == 0) { + itv->vbi.sliced_data[0].id = 0; + itv->vbi.sliced_data[0].line = 0; + itv->vbi.sliced_data[0].field = 0; + lines = 1; + } + buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]); + memcpy(p, &itv->vbi.sliced_data[0], size); + + if (itv->vbi.insert_mpeg) { + copy_vbi_data(itv, lines, pts_stamp); + } + itv->vbi.frame++; + return; + } + + /* Sliced VBI re-inserted from an MPEG stream */ + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { + /* If the size is not 4-byte aligned, then the starting address + for the swapping is also shifted. After swapping the data the + real start address of the VBI data is exactly 4 bytes after the + original start. It's a bit fiddly but it works like a charm. + Non-4-byte alignment happens when an lseek is done on the input + mpeg file to a non-4-byte aligned position. So on arrival here + the VBI data is also non-4-byte aligned. */ + int offset = size & 3; + int cnt; + + if (offset) { + p += 4 - offset; + } + /* Swap Buffer */ + for (y = 0; y < size; y += 4) { + swab32s((u32 *)(p + y)); + } + + cnt = ivtv_convert_ivtv_vbi(itv, p + offset); + memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt); + buf->bytesused = cnt; + + passthrough_vbi_data(itv, cnt / sizeof(itv->vbi.sliced_dec_data[0])); + return; + } +} + +void ivtv_disable_vbi(struct ivtv *itv) +{ + clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + ivtv_set_wss(itv, 0, 0); + ivtv_set_cc(itv, 0, 0, 0, 0, 0); + ivtv_set_vps(itv, 0, 0, 0, 0, 0, 0); + itv->vbi.vps_found = itv->vbi.wss_found = 0; + itv->vbi.wss = 0; + itv->vbi.cc_pos = 0; +} + + +void vbi_work_handler(struct ivtv *itv) +{ + struct v4l2_sliced_vbi_data data; + + /* Lock */ + if (itv->output_mode == OUT_PASSTHROUGH) { + /* Note: currently only the saa7115 is used in a PVR350, + so these commands are for now saa7115 specific. */ + if (itv->is_50hz) { + data.id = V4L2_SLICED_WSS_625; + data.field = 0; + + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + ivtv_set_wss(itv, 1, data.data[0] & 0xf); + itv->vbi.wss_no_update = 0; + } else if (itv->vbi.wss_no_update == 4) { + ivtv_set_wss(itv, 1, 0x8); /* 4x3 full format */ + } else { + itv->vbi.wss_no_update++; + } + } + else { + u8 c1 = 0, c2 = 0, c3 = 0, c4 = 0; + int mode = 0; + + data.id = V4L2_SLICED_CAPTION_525; + data.field = 0; + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + mode |= 1; + c1 = data.data[0]; + c2 = data.data[1]; + } + data.field = 1; + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + mode |= 2; + c3 = data.data[0]; + c4 = data.data[1]; + } + if (mode) { + itv->vbi.cc_no_update = 0; + ivtv_set_cc(itv, mode, c1, c2, c3, c4); + } else if (itv->vbi.cc_no_update == 4) { + ivtv_set_cc(itv, 0, 0, 0, 0, 0); + } else { + itv->vbi.cc_no_update++; + } + } + return; + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) { + /* Lock */ + ivtv_set_wss(itv, itv->vbi.wss_found, itv->vbi.wss & 0xf); + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) { + if (itv->vbi.cc_pos == 0) { + ivtv_set_cc(itv, 3, 0x80, 0x80, 0x80, 0x80); + } + while (itv->vbi.cc_pos) { + u8 cc_odd0 = itv->vbi.cc_data_odd[0]; + u8 cc_odd1 = itv->vbi.cc_data_odd[1]; + u8 cc_even0 = itv->vbi.cc_data_even[0]; + u8 cc_even1 = itv->vbi.cc_data_even[1]; + + memcpy(itv->vbi.cc_data_odd, itv->vbi.cc_data_odd + 2, sizeof(itv->vbi.cc_data_odd) - 2); + memcpy(itv->vbi.cc_data_even, itv->vbi.cc_data_even + 2, sizeof(itv->vbi.cc_data_even) - 2); + itv->vbi.cc_pos -= 2; + if (itv->vbi.cc_pos && cc_odd0 == 0x80 && cc_odd1 == 0x80) + continue; + + /* Send to Saa7127 */ + ivtv_set_cc(itv, 3, cc_odd0, cc_odd1, cc_even0, cc_even1); + if (itv->vbi.cc_pos == 0) + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + break; + } + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) { + /* Lock */ + ivtv_set_vps(itv, itv->vbi.vps_found, + itv->vbi.vps[0], itv->vbi.vps[1], + itv->vbi.vps[2], itv->vbi.vps[3], itv->vbi.vps[4]); + } +} diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/video/ivtv/ivtv-vbi.h new file mode 100644 index 0000000..cdaea69 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-vbi.h @@ -0,0 +1,26 @@ +/* + Vertical Blank Interval support functions + Copyright (C) 2004-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count); +void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, + u64 pts_stamp, int streamtype); +int ivtv_used_line(struct ivtv *itv, int line, int field); +void ivtv_disable_vbi(struct ivtv *itv); +void ivtv_set_vbi(unsigned long arg); +void vbi_work_handler(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h new file mode 100644 index 0000000..85530a3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-version.h @@ -0,0 +1,26 @@ +/* + ivtv driver version information + Copyright (C) 2005-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define IVTV_DRIVER_NAME "ivtv" +#define IVTV_DRIVER_VERSION_MAJOR 1 +#define IVTV_DRIVER_VERSION_MINOR 0 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 0 + +#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) +#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/ivtv/ivtv-video.c b/drivers/media/video/ivtv/ivtv-video.c new file mode 100644 index 0000000..5858b19 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-video.c @@ -0,0 +1,142 @@ +/* + saa7127 interface functions + Copyright (C) 2004-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-video.h" +#include "ivtv-i2c.h" +#include "ivtv-gpio.h" +#include "ivtv-cards.h" +#include +#include + +void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3, + u8 vps4, u8 vps5) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + data.id = V4L2_SLICED_VPS; + data.field = 0; + data.line = enabled ? 16 : 0; + data.data[4] = vps1; + data.data[10] = vps2; + data.data[11] = vps3; + data.data[12] = vps4; + data.data[13] = vps5; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + data.id = V4L2_SLICED_CAPTION_525; + data.field = 0; + data.line = (mode & 1) ? 21 : 0; + data.data[0] = cc1; + data.data[1] = cc2; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + data.field = 1; + data.line = (mode & 2) ? 21 : 0; + data.data[0] = cc3; + data.data[1] = cc4; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + /* When using a 50 Hz system, always turn on the + wide screen signal with 4x3 ratio as the default. + Turning this signal on and off can confuse certain + TVs. As far as I can tell there is no reason not to + transmit this signal. */ + if ((itv->std & V4L2_STD_625_50) && !enabled) { + enabled = 1; + mode = 0x08; /* 4x3 full format */ + } + data.id = V4L2_SLICED_WSS_625; + data.field = 0; + data.line = enabled ? 23 : 0; + data.data[0] = mode & 0xff; + data.data[1] = (mode >> 8) & 0xff; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_video_set_io(struct ivtv *itv) +{ + struct v4l2_routing route; + int inp = itv->active_input; + u32 type; + + route.input = itv->card->video_inputs[inp].video_input; + route.output = 0; + itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + + type = itv->card->video_inputs[inp].video_type; + + if (type == IVTV_CARD_INPUT_VID_TUNER) { + route.input = 0; /* Tuner */ + } else if (type < IVTV_CARD_INPUT_COMPOSITE1) { + route.input = 2; /* S-Video */ + } else { + route.input = 1; /* Composite */ + } + + if (itv->card->hw_video & IVTV_HW_GPIO) + ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + + if (itv->card->hw_video & IVTV_HW_UPD64031A) { + if (type == IVTV_CARD_INPUT_VID_TUNER || + type >= IVTV_CARD_INPUT_COMPOSITE1) { + /* Composite: GR on, connect to 3DYCS */ + route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE; + } else { + /* S-Video: GR bypassed, turn it off */ + route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE; + } + route.input |= itv->card->gr_config; + + ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + } + + if (itv->card->hw_video & IVTV_HW_UPD6408X) { + route.input = UPD64083_YCS_MODE; + if (type > IVTV_CARD_INPUT_VID_TUNER && + type < IVTV_CARD_INPUT_COMPOSITE1) { + /* S-Video uses YCNR mode and internal Y-ADC, the upd64031a + is not used. */ + route.input |= UPD64083_YCNR_MODE; + } + else if (itv->card->hw_video & IVTV_HW_UPD64031A) { + /* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */ + if ((type == IVTV_CARD_INPUT_VID_TUNER)|| + (itv->card->type == IVTV_CARD_CX23416GYC)) { + route.input |= UPD64083_EXT_Y_ADC; + } + } + ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + } +} diff --git a/drivers/media/video/ivtv/ivtv-video.h b/drivers/media/video/ivtv/ivtv-video.h new file mode 100644 index 0000000..c8ade5d --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-video.h @@ -0,0 +1,24 @@ +/* + saa7127 interface functions + Copyright (C) 2004-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ivtv_set_wss(struct ivtv *itv, int enabled, int mode); +void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4); +void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3, + u8 vps4, u8 vps5); +void ivtv_video_set_io(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c new file mode 100644 index 0000000..bcea095 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-yuv.c @@ -0,0 +1,1129 @@ +/* + yuv support + + Copyright (C) 2007 Ian Armstrong + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-yuv.h" + +static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, + struct ivtv_dma_frame *args) +{ + struct ivtv_dma_page_info y_dma; + struct ivtv_dma_page_info uv_dma; + + int i; + int y_pages, uv_pages; + + unsigned long y_buffer_offset, uv_buffer_offset; + int y_decode_height, uv_decode_height, y_size; + int frame = atomic_read(&itv->yuv_info.next_fill_frame); + + y_buffer_offset = IVTV_DEC_MEM_START + yuv_offset[frame]; + uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET; + + y_decode_height = uv_decode_height = args->src.height + args->src.top; + + if (y_decode_height < 512-16) + y_buffer_offset += 720 * 16; + + if (y_decode_height & 15) + y_decode_height = (y_decode_height + 16) & ~15; + + if (uv_decode_height & 31) + uv_decode_height = (uv_decode_height + 32) & ~31; + + y_size = 720 * y_decode_height; + + /* Still in USE */ + if (dma->SG_length || dma->page_count) { + IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n", + dma->SG_length, dma->page_count); + return -EBUSY; + } + + ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height); + ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); + + /* Get user pages for DMA Xfer */ + down_read(¤t->mm->mmap_sem); + y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); + uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL); + up_read(¤t->mm->mmap_sem); + + dma->page_count = y_dma.page_count + uv_dma.page_count; + + if (y_pages + uv_pages != dma->page_count) { + IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", + y_pages + uv_pages, dma->page_count); + + for (i = 0; i < dma->page_count; i++) { + put_page(dma->map[i]); + } + dma->page_count = 0; + return -EINVAL; + } + + /* Fill & map SG List */ + ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)); + dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + + /* Fill SG Array with new values */ + ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size); + + /* If we've offset the y plane, ensure top area is blanked */ + if (args->src.height + args->src.top < 512-16) { + if (itv->yuv_info.blanking_dmaptr) { + dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16); + dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr); + dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DEC_MEM_START + yuv_offset[frame]); + dma->SG_length++; + } + } + + /* Tag SG Array with Interrupt Bit */ + dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); + + ivtv_udma_sync_for_device(itv); + return 0; +} + +/* We rely on a table held in the firmware - Quick check. */ +int ivtv_yuv_filter_check(struct ivtv *itv) +{ + int i, offset_y, offset_uv; + + for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) { + if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) || + (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) { + IVTV_WARN ("YUV filter table not found in firmware.\n"); + return -1; + } + } + return 0; +} + +static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2) +{ + int filter_index, filter_line; + + /* If any filter is -1, then don't update it */ + if (h_filter > -1) { + if (h_filter > 4) h_filter = 4; + filter_index = h_filter * 384; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c); + filter_index += 8; + write_reg(0, 0x02818); + write_reg(0, 0x02830); + filter_line ++; + } + IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter); + } + + if (v_filter_1 > -1) { + if (v_filter_1 > 4) v_filter_1 = 4; + filter_index = v_filter_1 * 192; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904); + filter_index += 8; + write_reg(0, 0x02908); + filter_line ++; + } + IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1); + } + + if (v_filter_2 > -1) { + if (v_filter_2 > 4) v_filter_2 = 4; + filter_index = v_filter_2 * 192; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910); + filter_index += 8; + write_reg(0, 0x02914); + filter_line ++; + } + IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2); + } +} + +static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window) +{ + u32 reg_2834, reg_2838, reg_283c; + u32 reg_2844, reg_2854, reg_285c; + u32 reg_2864, reg_2874, reg_2890; + u32 reg_2870, reg_2870_base, reg_2870_offset; + int x_cutoff; + int h_filter; + u32 master_width; + + IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n", + window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x); + + /* How wide is the src image */ + x_cutoff = window->src_w + window->src_x; + + /* Set the display width */ + reg_2834 = window->dst_w; + reg_2838 = reg_2834; + + /* Set the display position */ + reg_2890 = window->dst_x; + + /* Index into the image horizontally */ + reg_2870 = 0; + + /* 2870 is normally fudged to align video coords with osd coords. + If running full screen, it causes an unwanted left shift + Remove the fudge if we almost fill the screen. + Gradually adjust the offset to avoid the video 'snapping' + left/right if it gets dragged through this region. + Only do this if osd is full width. */ + if (window->vis_w == 720) { + if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){ + reg_2870 = 10 - (window->tru_x - window->pan_x) / 4; + } + else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) { + reg_2870 = (10 + (window->tru_x - window->pan_x) / 2); + } + + if (window->dst_w >= window->src_w) + reg_2870 = reg_2870 << 16 | reg_2870; + else + reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1); + } + + if (window->dst_w < window->src_w) + reg_2870 = 0x000d000e - reg_2870; + else + reg_2870 = 0x0012000e - reg_2870; + + /* We're also using 2870 to shift the image left (src_x & negative dst_x) */ + reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19; + + if (window->dst_w >= window->src_w) { + x_cutoff &= ~1; + master_width = (window->src_w * 0x00200000) / (window->dst_w); + if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 2; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + + /* We also need to factor in the scaling + (src_w - dst_w) / (src_w / 4) */ + if (window->dst_w > window->src_w) + reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14); + else + reg_2870_base = 0; + + reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base); + reg_2874 = 0; + } + else if (window->dst_w < window->src_w / 2) { + master_width = (window->src_w * 0x00080000) / window->dst_w; + if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 1; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset); + reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16; + reg_2874 = 0x00000012; + } + else { + master_width = (window->src_w * 0x00100000) / window->dst_w; + if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 1; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1); + reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16; + reg_2874 = 0x00000001; + } + + /* Select the horizontal filter */ + if (window->src_w == window->dst_w) { + /* An exact size match uses filter 0 */ + h_filter = 0; + } + else { + /* Figure out which filter to use */ + h_filter = ((window->src_w << 16) / window->dst_w) >> 15; + h_filter = (h_filter >> 1) + (h_filter & 1); + /* Only an exact size match can use filter 0 */ + if (h_filter == 0) h_filter = 1; + } + + write_reg(reg_2834, 0x02834); + write_reg(reg_2838, 0x02838); + IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838); + + write_reg(reg_283c, 0x0283c); + write_reg(reg_2844, 0x02844); + + IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844); + + write_reg(0x00080514, 0x02840); + write_reg(0x00100514, 0x02848); + IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514); + + write_reg(reg_2854, 0x02854); + IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854); + + write_reg(reg_285c, 0x0285c); + write_reg(reg_2864, 0x02864); + IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864); + + write_reg(reg_2874, 0x02874); + IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874); + + write_reg(reg_2870, 0x02870); + IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870); + + write_reg( reg_2890,0x02890); + IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890); + + /* Only update the filter if we really need to */ + if (h_filter != itv->yuv_info.h_filter) { + ivtv_yuv_filter (itv,h_filter,-1,-1); + itv->yuv_info.h_filter = h_filter; + } +} + +static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window) +{ + u32 master_height; + u32 reg_2918, reg_291c, reg_2920, reg_2928; + u32 reg_2930, reg_2934, reg_293c; + u32 reg_2940, reg_2944, reg_294c; + u32 reg_2950, reg_2954, reg_2958, reg_295c; + u32 reg_2960, reg_2964, reg_2968, reg_296c; + u32 reg_289c; + u32 src_y_major_y, src_y_minor_y; + u32 src_y_major_uv, src_y_minor_uv; + u32 reg_2964_base, reg_2968_base; + int v_filter_1, v_filter_2; + + IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n", + window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y); + + /* What scaling mode is being used... */ + if (window->interlaced_y) { + IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n"); + } + else { + IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n"); + } + + if (window->interlaced_uv) { + IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n"); + } + else { + IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n"); + } + + /* What is the source video being treated as... */ + if (itv->yuv_info.frame_interlaced) { + IVTV_DEBUG_WARN("Source video: Interlaced\n"); + } + else { + IVTV_DEBUG_WARN("Source video: Non-interlaced\n"); + } + + /* We offset into the image using two different index methods, so split + the y source coord into two parts. */ + if (window->src_y < 8) { + src_y_minor_uv = window->src_y; + src_y_major_uv = 0; + } + else { + src_y_minor_uv = 8; + src_y_major_uv = window->src_y - 8; + } + + src_y_minor_y = src_y_minor_uv; + src_y_major_y = src_y_major_uv; + + if (window->offset_y) src_y_minor_y += 16; + + if (window->interlaced_y) + reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y); + else + reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1); + + if (window->interlaced_uv) + reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1); + else + reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv); + + reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14; + reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14; + + if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) { + master_height = (window->src_h * 0x00400000) / window->dst_h; + if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++; + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 3; + reg_2930 = master_height; + reg_2940 = master_height >> 1; + reg_2964_base >>= 3; + reg_2968_base >>= 3; + reg_296c = 0x00000000; + } + else if (window->dst_h >= window->src_h) { + master_height = (window->src_h * 0x00400000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height >> 1; + reg_296c = 0x00000000; + if (window->interlaced_y) { + reg_2964_base >>= 3; + } + else { + reg_296c ++; + reg_2964_base >>= 2; + } + if (window->interlaced_uv) reg_2928 >>= 1; + reg_2968_base >>= 3; + } + else if (window->dst_h >= window->src_h / 2) { + master_height = (window->src_h * 0x00200000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height; + reg_296c = 0x00000101; + if (window->interlaced_y) { + reg_2964_base >>= 2; + } + else { + reg_296c ++; + reg_2964_base >>= 1; + } + if (window->interlaced_uv) reg_2928 >>= 1; + reg_2968_base >>= 2; + } + else { + master_height = (window->src_h * 0x00100000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height; + reg_2964_base >>= 1; + reg_2968_base >>= 2; + reg_296c = 0x00000102; + } + + /* FIXME These registers change depending on scaled / unscaled output + We really need to work out what they should be */ + if (window->src_h == window->dst_h){ + reg_2934 = 0x00020000; + reg_293c = 0x00100000; + reg_2944 = 0x00040000; + reg_294c = 0x000b0000; + } + else { + reg_2934 = 0x00000FF0; + reg_293c = 0x00000FF0; + reg_2944 = 0x00000FF0; + reg_294c = 0x00000FF0; + } + + /* The first line to be displayed */ + reg_2950 = 0x00010000 + src_y_major_y; + if (window->interlaced_y) reg_2950 += 0x00010000; + reg_2954 = reg_2950 + 1; + + reg_2958 = 0x00010000 + (src_y_major_y >> 1); + if (window->interlaced_uv) reg_2958 += 0x00010000; + reg_295c = reg_2958 + 1; + + if (itv->yuv_info.decode_height == 480) + reg_289c = 0x011e0017; + else + reg_289c = 0x01500017; + + if (window->dst_y < 0) + reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1); + else + reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1); + + /* How much of the source to decode. + Take into account the source offset */ + reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) | + ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15); + + /* Calculate correct value for register 2964 */ + if (window->src_h == window->dst_h) + reg_2964 = 1; + else { + reg_2964 = 2 + ((window->dst_h << 1) / window->src_h); + reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1); + } + reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1); + reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94); + + /* Okay, we've wasted time working out the correct value, + but if we use it, it fouls the the window alignment. + Fudge it to what we want... */ + reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16)); + reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16)); + + /* Deviate further from what it should be. I find the flicker headache + inducing so try to reduce it slightly. Leave 2968 as-is otherwise + colours foul. */ + if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h)) + reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2); + + if (!window->interlaced_y) reg_2964 -= 0x00010001; + if (!window->interlaced_uv) reg_2968 -= 0x00010001; + + reg_2964 += ((reg_2964_base << 16) | reg_2964_base); + reg_2968 += ((reg_2968_base << 16) | reg_2968_base); + + /* Select the vertical filter */ + if (window->src_h == window->dst_h) { + /* An exact size match uses filter 0/1 */ + v_filter_1 = 0; + v_filter_2 = 1; + } + else { + /* Figure out which filter to use */ + v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15; + v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); + /* Only an exact size match can use filter 0 */ + if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_2 = v_filter_1; + } + + write_reg(reg_2934, 0x02934); + write_reg(reg_293c, 0x0293c); + IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c); + write_reg(reg_2944, 0x02944); + write_reg(reg_294c, 0x0294c); + IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c); + + /* Ensure 2970 is 0 (does it ever change ?) */ +/* write_reg(0,0x02970); */ +/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */ + + write_reg(reg_2930, 0x02938); + write_reg(reg_2930, 0x02930); + IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930); + + write_reg(reg_2928, 0x02928); + write_reg(reg_2928+0x514, 0x0292C); + IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514); + + write_reg(reg_2920, 0x02920); + write_reg(reg_2920+0x514, 0x02924); + IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920); + + write_reg (reg_2918,0x02918); + write_reg (reg_291c,0x0291C); + IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c); + + write_reg(reg_296c, 0x0296c); + IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c); + + write_reg(reg_2940, 0x02948); + write_reg(reg_2940, 0x02940); + IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940); + + write_reg(reg_2950, 0x02950); + write_reg(reg_2954, 0x02954); + IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954); + + write_reg(reg_2958, 0x02958); + write_reg(reg_295c, 0x0295C); + IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c); + + write_reg(reg_2960, 0x02960); + IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960); + + write_reg(reg_2964, 0x02964); + write_reg(reg_2968, 0x02968); + IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968); + + write_reg( reg_289c,0x0289c); + IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c); + + /* Only update filter 1 if we really need to */ + if (v_filter_1 != itv->yuv_info.v_filter_1) { + ivtv_yuv_filter (itv,-1,v_filter_1,-1); + itv->yuv_info.v_filter_1 = v_filter_1; + } + + /* Only update filter 2 if we really need to */ + if (v_filter_2 != itv->yuv_info.v_filter_2) { + ivtv_yuv_filter (itv,-1,-1,v_filter_2); + itv->yuv_info.v_filter_2 = v_filter_2; + } + + itv->yuv_info.frame_interlaced_last = itv->yuv_info.frame_interlaced; +} + +/* Modify the supplied coordinate information to fit the visible osd area */ +static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window) +{ + int osd_crop, lace_threshold; + u32 osd_scale; + u32 yuv_update = 0; + + lace_threshold = itv->yuv_info.lace_threshold; + if (lace_threshold < 0) + lace_threshold = itv->yuv_info.decode_height - 1; + + /* Work out the lace settings */ + switch (itv->yuv_info.lace_mode) { + case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */ + itv->yuv_info.frame_interlaced = 0; + if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021)) + window->interlaced_y = 0; + else + window->interlaced_y = 1; + + if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) + window->interlaced_uv = 0; + else + window->interlaced_uv = 1; + break; + + case IVTV_YUV_MODE_AUTO: + if (window->tru_h <= lace_threshold || window->tru_h > 576 || window->tru_w > 720){ + itv->yuv_info.frame_interlaced = 0; + if ((window->tru_h < 512) || + (window->tru_h > 576 && window->tru_h < 1021) || + (window->tru_w > 720 && window->tru_h < 1021)) + window->interlaced_y = 0; + else + window->interlaced_y = 1; + + if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) + window->interlaced_uv = 0; + else + window->interlaced_uv = 1; + } + else { + itv->yuv_info.frame_interlaced = 1; + window->interlaced_y = 1; + window->interlaced_uv = 1; + } + break; + + case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */ + default: + itv->yuv_info.frame_interlaced = 1; + window->interlaced_y = 1; + window->interlaced_uv = 1; + break; + } + + /* Sorry, but no negative coords for src */ + if (window->src_x < 0) window->src_x = 0; + if (window->src_y < 0) window->src_y = 0; + + /* Can only reduce width down to 1/4 original size */ + if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) { + window->src_x += osd_crop / 2; + window->src_w = (window->src_w - osd_crop) & ~3; + window->dst_w = window->src_w / 4; + window->dst_w += window->dst_w & 1; + } + + /* Can only reduce height down to 1/4 original size */ + if (window->src_h / window->dst_h >= 2) { + /* Overflow may be because we're running progressive, so force mode switch */ + window->interlaced_y = 1; + /* Make sure we're still within limits for interlace */ + if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) { + /* If we reach here we'll have to force the height. */ + window->src_y += osd_crop / 2; + window->src_h = (window->src_h - osd_crop) & ~3; + window->dst_h = window->src_h / 4; + window->dst_h += window->dst_h & 1; + } + } + + /* If there's nothing to safe to display, we may as well stop now */ + if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + return 0; + } + + /* Ensure video remains inside OSD area */ + osd_scale = (window->src_h << 16) / window->dst_h; + + if ((osd_crop = window->pan_y - window->dst_y) > 0) { + /* Falls off the upper edge - crop */ + window->src_y += (osd_scale * osd_crop) >> 16; + window->src_h -= (osd_scale * osd_crop) >> 16; + window->dst_h -= osd_crop; + window->dst_y = 0; + } + else { + window->dst_y -= window->pan_y; + } + + if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) { + /* Falls off the lower edge - crop */ + window->dst_h -= osd_crop; + window->src_h -= (osd_scale * osd_crop) >> 16; + } + + osd_scale = (window->src_w << 16) / window->dst_w; + + if ((osd_crop = window->pan_x - window->dst_x) > 0) { + /* Fall off the left edge - crop */ + window->src_x += (osd_scale * osd_crop) >> 16; + window->src_w -= (osd_scale * osd_crop) >> 16; + window->dst_w -= osd_crop; + window->dst_x = 0; + } + else { + window->dst_x -= window->pan_x; + } + + if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) { + /* Falls off the right edge - crop */ + window->dst_w -= osd_crop; + window->src_w -= (osd_scale * osd_crop) >> 16; + } + + /* The OSD can be moved. Track to it */ + window->dst_x += itv->yuv_info.osd_x_offset; + window->dst_y += itv->yuv_info.osd_y_offset; + + /* Width & height for both src & dst must be even. + Same for coordinates. */ + window->dst_w &= ~1; + window->dst_x &= ~1; + + window->src_w += window->src_x & 1; + window->src_x &= ~1; + + window->src_w &= ~1; + window->dst_w &= ~1; + + window->dst_h &= ~1; + window->dst_y &= ~1; + + window->src_h += window->src_y & 1; + window->src_y &= ~1; + + window->src_h &= ~1; + window->dst_h &= ~1; + + /* Due to rounding, we may have reduced the output size to <1/4 of the source + Check again, but this time just resize. Don't change source coordinates */ + if (window->dst_w < window->src_w / 4) { + window->src_w &= ~3; + window->dst_w = window->src_w / 4; + window->dst_w += window->dst_w & 1; + } + if (window->dst_h < window->src_h / 4) { + window->src_h &= ~3; + window->dst_h = window->src_h / 4; + window->dst_h += window->dst_h & 1; + } + + /* Check again. If there's nothing to safe to display, stop now */ + if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + return 0; + } + + /* Both x offset & width are linked, so they have to be done together */ + if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) || + (itv->yuv_info.old_frame_info.src_w != window->src_w) || + (itv->yuv_info.old_frame_info.dst_x != window->dst_x) || + (itv->yuv_info.old_frame_info.src_x != window->src_x) || + (itv->yuv_info.old_frame_info.pan_x != window->pan_x) || + (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) { + yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL; + } + + if ((itv->yuv_info.old_frame_info.src_h != window->src_h) || + (itv->yuv_info.old_frame_info.dst_h != window->dst_h) || + (itv->yuv_info.old_frame_info.dst_y != window->dst_y) || + (itv->yuv_info.old_frame_info.src_y != window->src_y) || + (itv->yuv_info.old_frame_info.pan_y != window->pan_y) || + (itv->yuv_info.old_frame_info.vis_h != window->vis_h) || + (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) || + (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) { + yuv_update |= IVTV_YUV_UPDATE_VERTICAL; + } + + return yuv_update; +} + +/* Update the scaling register to the requested value */ +void ivtv_yuv_work_handler (struct ivtv *itv) +{ + struct yuv_frame_info window; + u32 yuv_update; + + int frame = itv->yuv_info.update_frame; + +/* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */ + memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window)); + + /* Update the osd pan info */ + window.pan_x = itv->yuv_info.osd_x_pan; + window.pan_y = itv->yuv_info.osd_y_pan; + window.vis_w = itv->yuv_info.osd_vis_w; + window.vis_h = itv->yuv_info.osd_vis_h; + + /* Calculate the display window coordinates. Exit if nothing left */ + if (!(yuv_update = ivtv_yuv_window_setup (itv, &window))) + return; + + /* Update horizontal settings */ + if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL) + ivtv_yuv_handle_horizontal(itv, &window); + + if (yuv_update & IVTV_YUV_UPDATE_VERTICAL) + ivtv_yuv_handle_vertical(itv, &window); + + memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info)); +} + +static void ivtv_yuv_init (struct ivtv *itv) +{ + IVTV_DEBUG_YUV("ivtv_yuv_init\n"); + + /* Take a snapshot of the current register settings */ + itv->yuv_info.reg_2834 = read_reg(0x02834); + itv->yuv_info.reg_2838 = read_reg(0x02838); + itv->yuv_info.reg_283c = read_reg(0x0283c); + itv->yuv_info.reg_2840 = read_reg(0x02840); + itv->yuv_info.reg_2844 = read_reg(0x02844); + itv->yuv_info.reg_2848 = read_reg(0x02848); + itv->yuv_info.reg_2854 = read_reg(0x02854); + itv->yuv_info.reg_285c = read_reg(0x0285c); + itv->yuv_info.reg_2864 = read_reg(0x02864); + itv->yuv_info.reg_2870 = read_reg(0x02870); + itv->yuv_info.reg_2874 = read_reg(0x02874); + itv->yuv_info.reg_2898 = read_reg(0x02898); + itv->yuv_info.reg_2890 = read_reg(0x02890); + + itv->yuv_info.reg_289c = read_reg(0x0289c); + itv->yuv_info.reg_2918 = read_reg(0x02918); + itv->yuv_info.reg_291c = read_reg(0x0291c); + itv->yuv_info.reg_2920 = read_reg(0x02920); + itv->yuv_info.reg_2924 = read_reg(0x02924); + itv->yuv_info.reg_2928 = read_reg(0x02928); + itv->yuv_info.reg_292c = read_reg(0x0292c); + itv->yuv_info.reg_2930 = read_reg(0x02930); + itv->yuv_info.reg_2934 = read_reg(0x02934); + itv->yuv_info.reg_2938 = read_reg(0x02938); + itv->yuv_info.reg_293c = read_reg(0x0293c); + itv->yuv_info.reg_2940 = read_reg(0x02940); + itv->yuv_info.reg_2944 = read_reg(0x02944); + itv->yuv_info.reg_2948 = read_reg(0x02948); + itv->yuv_info.reg_294c = read_reg(0x0294c); + itv->yuv_info.reg_2950 = read_reg(0x02950); + itv->yuv_info.reg_2954 = read_reg(0x02954); + itv->yuv_info.reg_2958 = read_reg(0x02958); + itv->yuv_info.reg_295c = read_reg(0x0295c); + itv->yuv_info.reg_2960 = read_reg(0x02960); + itv->yuv_info.reg_2964 = read_reg(0x02964); + itv->yuv_info.reg_2968 = read_reg(0x02968); + itv->yuv_info.reg_296c = read_reg(0x0296c); + itv->yuv_info.reg_2970 = read_reg(0x02970); + + itv->yuv_info.v_filter_1 = -1; + itv->yuv_info.v_filter_2 = -1; + itv->yuv_info.h_filter = -1; + + /* Set some valid size info */ + itv->yuv_info.osd_x_offset = read_reg(0x02a04) & 0x00000FFF; + itv->yuv_info.osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF; + + /* Bit 2 of reg 2878 indicates current decoder output format + 0 : NTSC 1 : PAL */ + if (read_reg(0x2878) & 4) + itv->yuv_info.decode_height = 576; + else + itv->yuv_info.decode_height = 480; + + /* If no visible size set, assume full size */ + if (!itv->yuv_info.osd_vis_w) itv->yuv_info.osd_vis_w = 720 - itv->yuv_info.osd_x_offset; + if (!itv->yuv_info.osd_vis_h) itv->yuv_info.osd_vis_h = itv->yuv_info.decode_height - itv->yuv_info.osd_y_offset; + + /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ + itv->yuv_info.blanking_ptr = kzalloc(720*16,GFP_KERNEL); + if (itv->yuv_info.blanking_ptr) { + itv->yuv_info.blanking_dmaptr = pci_map_single(itv->dev, itv->yuv_info.blanking_ptr, 720*16, PCI_DMA_TODEVICE); + } + else { + itv->yuv_info.blanking_dmaptr = 0; + IVTV_DEBUG_WARN ("Failed to allocate yuv blanking buffer\n"); + } + + IVTV_DEBUG_WARN("Enable video output\n"); + write_reg_sync(0x00108080, 0x2898); + + /* Enable YUV decoder output */ + write_reg_sync(0x01, IVTV_REG_VDM); + + set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); + atomic_set(&itv->yuv_info.next_dma_frame,0); +} + +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +{ + DEFINE_WAIT(wait); + int rc = 0; + int got_sig = 0; + int frame, next_fill_frame, last_fill_frame; + + IVTV_DEBUG_INFO("yuv_prep_frame\n"); + + if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv); + + frame = atomic_read(&itv->yuv_info.next_fill_frame); + next_fill_frame = (frame + 1) & 0x3; + last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3; + + if (next_fill_frame != last_fill_frame && last_fill_frame != frame) { + /* Buffers are full - Overwrite the last frame */ + next_fill_frame = frame; + frame = (frame - 1) & 3; + } + + /* Take a snapshot of the yuv coordinate information */ + itv->yuv_info.new_frame_info[frame].src_x = args->src.left; + itv->yuv_info.new_frame_info[frame].src_y = args->src.top; + itv->yuv_info.new_frame_info[frame].src_w = args->src.width; + itv->yuv_info.new_frame_info[frame].src_h = args->src.height; + itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left; + itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top; + itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width; + itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height; + itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left; + itv->yuv_info.new_frame_info[frame].tru_w = args->src_width; + itv->yuv_info.new_frame_info[frame].tru_h = args->src_height; + + /* Are we going to offset the Y plane */ + if (args->src.height + args->src.top < 512-16) + itv->yuv_info.new_frame_info[frame].offset_y = 1; + else + itv->yuv_info.new_frame_info[frame].offset_y = 0; + + /* Snapshot the osd pan info */ + itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan; + itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan; + itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w; + itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h; + + itv->yuv_info.new_frame_info[frame].update = 0; + itv->yuv_info.new_frame_info[frame].interlaced_y = 0; + itv->yuv_info.new_frame_info[frame].interlaced_uv = 0; + + if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], + sizeof (itv->yuv_info.new_frame_info[frame]))) { + memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args)); + itv->yuv_info.new_frame_info[frame].update = 1; +/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ + } + + /* DMA the frame */ + mutex_lock(&itv->udma.lock); + + if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) { + mutex_unlock(&itv->udma.lock); + return rc; + } + + ivtv_udma_prepare(itv); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + /* if no UDMA is pending and no UDMA is in progress, then the DMA + is finished */ + while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { + /* don't interrupt if the DMA is in progress but break off + a still pending DMA. */ + got_sig = signal_pending(current); + if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + + /* Unmap Last DMA Xfer */ + ivtv_udma_unmap(itv); + + if (got_sig) { + IVTV_DEBUG_INFO("User stopped YUV UDMA\n"); + mutex_unlock(&itv->udma.lock); + return -EINTR; + } + + atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame); + + mutex_unlock(&itv->udma.lock); + return rc; +} + +void ivtv_yuv_close(struct ivtv *itv) +{ + int h_filter, v_filter_1, v_filter_2; + + IVTV_DEBUG_YUV("ivtv_yuv_close\n"); + ivtv_waitq(&itv->vsync_waitq); + + atomic_set(&itv->yuv_info.next_dma_frame, -1); + atomic_set(&itv->yuv_info.next_fill_frame, 0); + + /* Reset registers we have changed so mpeg playback works */ + + /* If we fully restore this register, the display may remain active. + Restore, but set one bit to blank the video. Firmware will always + clear this bit when needed, so not a problem. */ + write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898); + + write_reg(itv->yuv_info.reg_2834, 0x02834); + write_reg(itv->yuv_info.reg_2838, 0x02838); + write_reg(itv->yuv_info.reg_283c, 0x0283c); + write_reg(itv->yuv_info.reg_2840, 0x02840); + write_reg(itv->yuv_info.reg_2844, 0x02844); + write_reg(itv->yuv_info.reg_2848, 0x02848); + write_reg(itv->yuv_info.reg_2854, 0x02854); + write_reg(itv->yuv_info.reg_285c, 0x0285c); + write_reg(itv->yuv_info.reg_2864, 0x02864); + write_reg(itv->yuv_info.reg_2870, 0x02870); + write_reg(itv->yuv_info.reg_2874, 0x02874); + write_reg(itv->yuv_info.reg_2890, 0x02890); + write_reg(itv->yuv_info.reg_289c, 0x0289c); + + write_reg(itv->yuv_info.reg_2918, 0x02918); + write_reg(itv->yuv_info.reg_291c, 0x0291c); + write_reg(itv->yuv_info.reg_2920, 0x02920); + write_reg(itv->yuv_info.reg_2924, 0x02924); + write_reg(itv->yuv_info.reg_2928, 0x02928); + write_reg(itv->yuv_info.reg_292c, 0x0292c); + write_reg(itv->yuv_info.reg_2930, 0x02930); + write_reg(itv->yuv_info.reg_2934, 0x02934); + write_reg(itv->yuv_info.reg_2938, 0x02938); + write_reg(itv->yuv_info.reg_293c, 0x0293c); + write_reg(itv->yuv_info.reg_2940, 0x02940); + write_reg(itv->yuv_info.reg_2944, 0x02944); + write_reg(itv->yuv_info.reg_2948, 0x02948); + write_reg(itv->yuv_info.reg_294c, 0x0294c); + write_reg(itv->yuv_info.reg_2950, 0x02950); + write_reg(itv->yuv_info.reg_2954, 0x02954); + write_reg(itv->yuv_info.reg_2958, 0x02958); + write_reg(itv->yuv_info.reg_295c, 0x0295c); + write_reg(itv->yuv_info.reg_2960, 0x02960); + write_reg(itv->yuv_info.reg_2964, 0x02964); + write_reg(itv->yuv_info.reg_2968, 0x02968); + write_reg(itv->yuv_info.reg_296c, 0x0296c); + write_reg(itv->yuv_info.reg_2970, 0x02970); + + /* Prepare to restore filters */ + + /* First the horizontal filter */ + if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) { + /* An exact size match uses filter 0 */ + h_filter = 0; + } + else { + /* Figure out which filter to use */ + h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15; + h_filter = (h_filter >> 1) + (h_filter & 1); + /* Only an exact size match can use filter 0. */ + if (h_filter < 1) h_filter = 1; + } + + /* Now the vertical filter */ + if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) { + /* An exact size match uses filter 0/1 */ + v_filter_1 = 0; + v_filter_2 = 1; + } + else { + /* Figure out which filter to use */ + v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15; + v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); + /* Only an exact size match can use filter 0 */ + if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_2 = v_filter_1; + } + + /* Now restore the filters */ + ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2); + + /* and clear a few registers */ + write_reg(0, 0x02814); + write_reg(0, 0x0282c); + write_reg(0, 0x02904); + write_reg(0, 0x02910); + + /* Release the blanking buffer */ + if (itv->yuv_info.blanking_ptr) { + kfree (itv->yuv_info.blanking_ptr); + itv->yuv_info.blanking_ptr = NULL; + pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); + } + + /* Invalidate the old dimension information */ + itv->yuv_info.old_frame_info.src_w = 0; + itv->yuv_info.old_frame_info.src_h = 0; + itv->yuv_info.old_frame_info_args.src_w = 0; + itv->yuv_info.old_frame_info_args.src_h = 0; + + /* All done. */ + clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); +} + diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h new file mode 100644 index 0000000..88972d3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-yuv.h @@ -0,0 +1,24 @@ +/* + yuv support + + Copyright (C) 2007 Ian Armstrong + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_yuv_filter_check(struct ivtv *itv); +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args); +void ivtv_yuv_close(struct ivtv *itv); +void ivtv_yuv_work_handler (struct ivtv *itv); diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 98681da..664aba8 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -925,13 +925,13 @@ static int meye_do_ioctl(struct inode *i if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV) return -EINVAL; mutex_lock(&meye.lock); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, p->brightness >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, p->hue >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, p->colour >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, p->contrast >> 10); meye.picture = *p; mutex_unlock(&meye.lock); @@ -1043,11 +1043,11 @@ static int meye_do_ioctl(struct inode *i meye.params.quality != jp->quality) mchip_hic_stop(); /* need restart */ meye.params = *jp; - sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, meye.params.sharpness); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, meye.params.agc); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, meye.params.picture); mutex_unlock(&meye.lock); break; @@ -1287,38 +1287,38 @@ static int meye_do_ioctl(struct inode *i mutex_lock(&meye.lock); switch (c->id) { case V4L2_CID_BRIGHTNESS: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); meye.picture.brightness = c->value << 10; break; case V4L2_CID_HUE: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAHUE, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAHUE, c->value); meye.picture.hue = c->value << 10; break; case V4L2_CID_CONTRAST: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERACONTRAST, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); meye.picture.contrast = c->value << 10; break; case V4L2_CID_SATURATION: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERACOLOR, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); meye.picture.colour = c->value << 10; break; case V4L2_CID_AGC: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAAGC, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAAGC, c->value); meye.params.agc = c->value; break; case V4L2_CID_SHARPNESS: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERASHARPNESS, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); meye.params.sharpness = c->value; break; case V4L2_CID_PICTURE: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAPICTURE, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); meye.params.picture = c->value; break; case V4L2_CID_JPEGQUAL: @@ -1848,7 +1848,7 @@ static int __devinit meye_probe(struct p memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); meye.video_dev->dev = &meye.mchip_dev->dev; - if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) { + if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { printk(KERN_ERR "meye: unable to power on the camera\n"); printk(KERN_ERR "meye: did you enable the camera in " "sonypi using the module options ?\n"); @@ -1928,13 +1928,13 @@ static int __devinit meye_probe(struct p meye.params.picture = 0; meye.params.framerate = 0; - sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n", MEYE_DRIVER_VERSION); @@ -1953,7 +1953,7 @@ outremap: outregions: pci_disable_device(meye.mchip_dev); outenabledev: - sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); outsonypienable: kfifo_free(meye.doneq); outkfifoalloc2: @@ -1986,7 +1986,7 @@ static void __devexit meye_remove(struct pci_disable_device(meye.mchip_dev); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); kfifo_free(meye.doneq); kfifo_free(meye.grabq); diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h index ea107cb..323d007 100644 --- a/drivers/media/video/meye.h +++ b/drivers/media/video/meye.h @@ -255,7 +255,7 @@ #define MCHIP_VRJ_START_COMMAND 0x1a0 /****************************************************************************/ /* Sony Programmable I/O Controller for accessing the camera commands */ -#include +#include /* private API definitions */ #include diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index ba1af3c..3bb7d66 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -773,6 +773,9 @@ #endif break; } + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2); + default: /* unknown */ return -EINVAL; @@ -872,6 +875,8 @@ static int msp_attach(struct i2c_adapter snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d", msp_family, msp_product, msp_revision, msp_hard, msp_rom); + /* Rev B=2, C=3, D=4, G=7 */ + state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@'; /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5; diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index 7531efa..ab69a29 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -50,6 +50,7 @@ extern int msp_stereo_thresh; struct msp_state { int rev1, rev2; + int ident; u8 has_nicam; u8 has_radio; u8 has_headphones; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 5ed0adc..03bc369 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -5,6 +5,8 @@ * by Jonathan Corbet with substantial inspiration from Mark * McClelland's ovcamchip code. * + * Copyright 2006-7 Jonathan Corbet + * * This file may be distributed under the terms of the GNU General * Public License, version 2. */ @@ -15,6 +17,7 @@ #include #include #include #include +#include #include @@ -162,6 +165,10 @@ #define REG_CONTRAS 0x56 /* Contrast con #define REG_GFIX 0x69 /* Fix gain control */ +#define REG_REG76 0x76 /* OV's name */ +#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ +#define R76_WHTPCOR 0x40 /* White pixel correction enable */ + #define REG_RGB444 0x8c /* RGB 444 control */ #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ #define R444_RGBX 0x01 /* Empty nibble at end */ @@ -255,7 +262,7 @@ static struct regval_list ov7670_default /* Almost all of these are magic "reserved" values. */ { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, - { 0x16, 0x02 }, { REG_MVFP, 0x07|MVFP_MIRROR }, + { 0x16, 0x02 }, { REG_MVFP, 0x07 }, { 0x21, 0x02 }, { 0x22, 0x91 }, { 0x29, 0x07 }, { 0x33, 0x0b }, { 0x35, 0x0b }, { 0x37, 0x1d }, @@ -380,6 +387,13 @@ static struct regval_list ov7670_fmt_rgb { 0xff, 0xff }, }; +static struct regval_list ov7670_fmt_raw[] = { + { REG_COM7, COM7_BAYER }, + { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ + { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ + { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ + { 0xff, 0xff }, +}; @@ -483,32 +497,39 @@ static struct ov7670_format_struct { __u32 pixelformat; struct regval_list *regs; int cmatrix[CMATRIX_LEN]; + int bpp; /* Bytes per pixel */ } ov7670_formats[] = { { .desc = "YUYV 4:2:2", .pixelformat = V4L2_PIX_FMT_YUYV, .regs = ov7670_fmt_yuv422, .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 2, }, { .desc = "RGB 444", .pixelformat = V4L2_PIX_FMT_RGB444, .regs = ov7670_fmt_rgb444, .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 2, }, { .desc = "RGB 565", .pixelformat = V4L2_PIX_FMT_RGB565, .regs = ov7670_fmt_rgb565, .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 2, + }, + { + .desc = "Raw RGB Bayer", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .regs = ov7670_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + .bpp = 1 }, }; -#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0])) +#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) -/* - * All formats we support are 2 bytes/pixel. - */ -#define BYTES_PER_PIXEL 2 /* * Then there is the issue of window sizes. Try to capture the info here. @@ -685,7 +706,7 @@ static int ov7670_try_fmt(struct i2c_cli */ pix->width = wsize->width; pix->height = wsize->height; - pix->bytesperline = pix->width*BYTES_PER_PIXEL; + pix->bytesperline = pix->width*ov7670_formats[index].bpp; pix->sizeimage = pix->height*pix->bytesperline; return 0; } @@ -1270,9 +1291,8 @@ static int ov7670_command(struct i2c_cli void *arg) { switch (cmd) { - case VIDIOC_INT_G_CHIP_IDENT: - * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670; - return 0; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0); case VIDIOC_INT_RESET: ov7670_reset(client); diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c index 86d2884..1455a8f 100644 --- a/drivers/media/video/planb.c +++ b/drivers/media/video/planb.c @@ -2160,7 +2160,7 @@ static int find_planb(void) if (!machine_is(powermac)) return 0; - planb_devices = find_devices("planb"); + planb_devices = of_find_node_by_name(NULL, "planb"); if (planb_devices == 0) { planb_num=0; printk(KERN_WARNING "PlanB: no device found!\n"); @@ -2175,12 +2175,14 @@ static int find_planb(void) if (planb_devices->n_addrs != 1) { printk (KERN_WARNING "PlanB: expecting 1 address for planb " "(got %d)", planb_devices->n_addrs); + of_node_put(planb_devices); return 0; } if (planb_devices->n_intrs == 0) { printk(KERN_WARNING "PlanB: no intrs for device %s\n", planb_devices->full_name); + of_node_put(planb_devices); return 0; } else { irq = planb_devices->intrs[0].line; @@ -2202,12 +2204,13 @@ static int find_planb(void) confreg = planb_devices->addrs[0].space & 0xff; old_base = planb_devices->addrs[0].address; new_base = 0xf1000000; + of_node_put(planb_devices); DEBUG("PlanB: Found on bus %d, dev %d, func %d, " "membase 0x%x (base reg. 0x%x)\n", bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg); - pdev = pci_find_slot (bus, dev_fn); + pdev = pci_get_bus_and_slot(bus, dev_fn); if (!pdev) { printk(KERN_ERR "planb: cannot find slot\n"); goto err_out; @@ -2237,6 +2240,7 @@ static int find_planb(void) pb->planb_base = planb_regs; pb->planb_base_phys = (struct planb_registers *)new_base; pb->irq = irq; + pb->dev = pdev; return planb_num; @@ -2244,6 +2248,7 @@ err_out_disable: pci_disable_device(pdev); err_out: /* FIXME handle error */ /* comment moved from pci_find_slot, above */ + pci_dev_put(pdev); return 0; } @@ -2271,6 +2276,8 @@ static void release_planb(void) printk(KERN_INFO "PlanB: unregistering with v4l\n"); video_unregister_device(&pb->video_dev); + pci_dev_put(pb->dev); + /* note that iounmap() does nothing on the PPC right now */ iounmap ((void *)pb->planb_base); } diff --git a/drivers/media/video/planb.h b/drivers/media/video/planb.h index 9282321..e21b573 100644 --- a/drivers/media/video/planb.h +++ b/drivers/media/video/planb.h @@ -177,6 +177,7 @@ struct planb { struct mutex lock; unsigned int irq; /* interrupt number */ volatile unsigned int intr_mask; + struct pci_dev *dev; /* Our PCI device */ int overlay; /* overlay running? */ struct planb_window win; diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 5786faf..5669c8c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -324,7 +324,7 @@ static int pvr2_encoder_vcmd(struct pvr2 /* This implements some extra setup for the encoder that seems to be specific to the PVR USB2 hardware. */ -int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) +static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) { int ret = 0; int encMisc3Arg = 0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 16bd741..ce66ab8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -283,6 +283,8 @@ struct pvr2_hdw { int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ + char bus_info[32]; /* Bus location info */ + /* Minor numbers used by v4l logic (yes, this is a hack, as there should be no v4l junk here). Probably a better way to do this. */ int v4l_minor_number_video; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 9916cf3..acf651e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1008,6 +1008,13 @@ unsigned long pvr2_hdw_get_sn(struct pvr return hdw->serial_number; } + +const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw) +{ + return hdw->bus_info; +} + + unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) { return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio; @@ -2105,6 +2112,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct hdw->usb_intf = intf; hdw->usb_dev = interface_to_usbdev(intf); + scnprintf(hdw->bus_info,sizeof(hdw->bus_info), + "usb %s address %d", + hdw->usb_dev->dev.bus_id, + hdw->usb_dev->devnum); + ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; usb_set_interface(hdw->usb_dev,ifnum,0); @@ -3275,7 +3287,9 @@ #ifdef CONFIG_VIDEO_ADV_DEBUG mutex_lock(&hdw->i2c_list_lock); do { list_for_each(item,&hdw->i2c_clients) { cp = list_entry(item,struct pvr2_i2c_client,list); - if (!v4l2_chip_match_i2c_client(cp->client, req.match_type, req.match_chip)) { + if (!v4l2_chip_match_i2c_client( + cp->client, + req.match_type, req.match_chip)) { continue; } stat = pvr2_i2c_client_cmd( diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 0c9cca4..4dba8d0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -124,6 +124,9 @@ struct usb_device *pvr2_hdw_get_dev(stru /* Retrieve serial number of device */ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); +/* Retrieve bus location info of device */ +const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *); + /* Called when hardware has been unplugged */ void pvr2_hdw_disconnect(struct pvr2_hdw *); diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 91396fd..a741c55 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -42,9 +42,11 @@ #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC struct class_device_attribute attr_v4l_minor_number; struct class_device_attribute attr_v4l_radio_minor_number; struct class_device_attribute attr_unit_number; + struct class_device_attribute attr_bus_info; int v4l_minor_number_created_ok; int v4l_radio_minor_number_created_ok; int unit_number_created_ok; + int bus_info_created_ok; }; #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC @@ -705,6 +707,10 @@ #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_tear_down_debugifc(sfp); #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ pvr2_sysfs_tear_down_controls(sfp); + if (sfp->bus_info_created_ok) { + class_device_remove_file(sfp->class_dev, + &sfp->attr_bus_info); + } if (sfp->v4l_minor_number_created_ok) { class_device_remove_file(sfp->class_dev, &sfp->attr_v4l_minor_number); @@ -735,6 +741,16 @@ static ssize_t v4l_minor_number_show(str } +static ssize_t bus_info_show(struct class_device *class_dev,char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = (struct pvr2_sysfs *)class_dev->class_data; + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_bus_info(sfp->channel.hdw)); +} + + static ssize_t v4l_radio_minor_number_show(struct class_device *class_dev, char *buf) { @@ -836,6 +852,20 @@ static void class_dev_create(struct pvr2 sfp->unit_number_created_ok = !0; } + sfp->attr_bus_info.attr.owner = THIS_MODULE; + sfp->attr_bus_info.attr.name = "bus_info_str"; + sfp->attr_bus_info.attr.mode = S_IRUGO; + sfp->attr_bus_info.show = bus_info_show; + sfp->attr_bus_info.store = NULL; + ret = class_device_create_file(sfp->class_dev, + &sfp->attr_bus_info); + if (ret < 0) { + printk(KERN_WARNING "%s: class_device_create_file error: %d\n", + __FUNCTION__, ret); + } else { + sfp->bus_info_created_ok = !0; + } + pvr2_sysfs_add_controls(sfp); #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_add_debugifc(sfp); diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 25d3830..4563b3d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -203,6 +203,8 @@ static int pvr2_v4l2_do_ioctl(struct ino struct v4l2_capability *cap = arg; memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw), + sizeof(cap->bus_info)); ret = 0; break; diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 0bd1155..338ced7 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -140,6 +140,8 @@ static const char *size2name[PSZ_MAX] = An alternate value of 0 means this mode is not available at all. */ +#define PWC_FPS_MAX_NALA 8 + struct Nala_table_entry { char alternate; /* USB alternate setting */ int compressed; /* Compressed yes/no */ @@ -147,7 +149,9 @@ struct Nala_table_entry { unsigned char mode[3]; /* precomputed mode table */ }; -static struct Nala_table_entry Nala_table[PSZ_MAX][8] = +static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; + +static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = { #include "pwc-nala.h" }; @@ -423,6 +427,59 @@ int pwc_set_video_mode(struct pwc_device return 0; } +static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < PWC_FPS_MAX_NALA; i++) { + if (Nala_table[size][i].alternate) { + if (index--==0) return Nala_fps_vector[i]; + } + } + return 0; +} + +static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { + if (Kiara_table[size][i][3].alternate) { + if (index--==0) return Kiara_fps_vector[i]; + } + } + return 0; +} + +static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i=0; i < PWC_FPS_MAX_TIMON; i++) { + if (Timon_table[size][i][3].alternate) { + if (index--==0) return Timon_fps_vector[i]; + } + } + return 0; +} + +unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int ret; + + if (DEVICE_USE_CODEC1(pdev->type)) { + ret = pwc_get_fps_Nala(pdev, index, size); + + } else if (DEVICE_USE_CODEC3(pdev->type)) { + ret = pwc_get_fps_Kiara(pdev, index, size); + + } else { + ret = pwc_get_fps_Timon(pdev, index, size); + } + + return ret; +} + #define BLACK_Y 0 #define BLACK_U 128 #define BLACK_V 128 @@ -1343,7 +1400,7 @@ int pwc_ioctl(struct pwc_device *pdev, u ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); if (ret < 0) break; - ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); + ret = pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); if (ret < 0) break; } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 27ed769..085332a 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -95,8 +95,8 @@ static const struct usb_device_id pwc_de { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ - { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ - { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ @@ -1493,7 +1493,7 @@ static int usb_pwc_probe(struct usb_inte case 0x0329: PWC_INFO("Philips SPC 900NC USB webcam detected.\n"); name = "Philips SPC 900NC webcam"; - type_id = 720; + type_id = 740; break; default: return -ENODEV; @@ -1547,8 +1547,16 @@ static int usb_pwc_probe(struct usb_inte features |= FEATURE_MOTOR_PANTILT; break; case 0x08b6: + PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n"); + name = "Cisco VT Camera"; + type_id = 740; /* CCD sensor */ + break; case 0x08b7: - case 0x08b8: + PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n"); + name = "Logitech ViewPort AV 100"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b8: /* Where this released? */ PWC_INFO("Logitech QuickCam detected (reserved ID).\n"); name = "Logitech QuickCam (res.)"; type_id = 730; /* Assuming CMOS */ diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h index 784bc72..cec6602 100644 --- a/drivers/media/video/pwc/pwc-ioctl.h +++ b/drivers/media/video/pwc/pwc-ioctl.h @@ -2,7 +2,7 @@ #ifndef PWC_IOCTL_H #define PWC_IOCTL_H /* (C) 2001-2004 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -25,7 +25,7 @@ #define PWC_IOCTL_H Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* This is pwc-ioctl.h belonging to PWC 8.12.1 +/* This is pwc-ioctl.h belonging to PWC 10.0.10 It contains structures and defines to communicate from user space directly to the driver. */ @@ -51,6 +51,9 @@ #define PWC_IOCTL_H ... the function */ +#include +#include + /* Enumeration of image sizes */ #define PSZ_SQCIF 0x00 @@ -65,6 +68,8 @@ #define PSZ_MAX 6 /* The frame rate is encoded in the video_window.flags parameter using the upper 16 bits, since some flags are defined nowadays. The following defines provide a mask and shift to filter out this value. + This value can also be passing using the private flag when using v4l2 and + VIDIOC_S_FMT ioctl. In 'Snapshot' mode the camera freezes its automatic exposure and colour balance controls. @@ -73,6 +78,8 @@ #define PWC_FPS_SHIFT 16 #define PWC_FPS_MASK 0x00FF0000 #define PWC_FPS_FRMASK 0x003F0000 #define PWC_FPS_SNAPSHOT 0x00400000 +#define PWC_QLT_MASK 0x03000000 +#define PWC_QLT_SHIFT 24 /* structure for transferring x & y coordinates */ @@ -289,4 +296,29 @@ struct pwc_table_init_buffer { }; #define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) +/* + * This is private command used when communicating with v4l2. + * In the future all private ioctl will be remove/replace to + * use interface offer by v4l2. + */ + +#define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_PRIVATE_COLOUR_MODE (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_AUTOCONTOUR (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_PRIVATE_CONTOUR (V4L2_CID_PRIVATE_BASE + 5) +#define V4L2_CID_PRIVATE_BACKLIGHT (V4L2_CID_PRIVATE_BASE + 6) +#define V4L2_CID_PRIVATE_FLICKERLESS (V4L2_CID_PRIVATE_BASE + 7) +#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8) + +struct pwc_raw_frame { + __le16 type; /* type of the webcam */ + __le16 vbandlength; /* Size of 4lines compressed (used by the decompressor) */ + __u8 cmd[4]; /* the four byte of the command (in case of nala, + only the first 3 bytes is filled) */ + __u8 rawframe[0]; /* frame_size = H/4*vbandlength */ +} __attribute__ ((packed)); + + #endif diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c index fec39cc..f4ae83c 100644 --- a/drivers/media/video/pwc/pwc-kiara.c +++ b/drivers/media/video/pwc/pwc-kiara.c @@ -42,6 +42,8 @@ #include "pwc-kiara.h" #include "pwc-uncompress.h" +const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 }; + const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = { /* SQCIF */ diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h index 0bdb225..047dad8 100644 --- a/drivers/media/video/pwc/pwc-kiara.h +++ b/drivers/media/video/pwc/pwc-kiara.h @@ -29,6 +29,8 @@ #define PWC_KIARA_H #include +#define PWC_FPS_MAX_KIARA 6 + struct Kiara_table_entry { char alternate; /* USB alternate interface */ @@ -37,8 +39,9 @@ struct Kiara_table_entry unsigned char mode[12]; /* precomputed mode settings for cam */ }; -extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; +extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][PWC_FPS_MAX_KIARA][4]; extern const unsigned int KiaraRomTable[8][2][16][8]; +extern const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA]; #endif diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/video/pwc/pwc-timon.c index be65bdc..c56c174 100644 --- a/drivers/media/video/pwc/pwc-timon.c +++ b/drivers/media/video/pwc/pwc-timon.c @@ -40,7 +40,9 @@ #include "pwc-timon.h" -const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = +const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON] = { 5, 10, 15, 20, 25, 30 }; + +const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4] = { /* SQCIF */ { diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h index eef9e2c..a6e2222 100644 --- a/drivers/media/video/pwc/pwc-timon.h +++ b/drivers/media/video/pwc/pwc-timon.h @@ -44,6 +44,8 @@ #define PWC_TIMON_H #include +#define PWC_FPS_MAX_TIMON 6 + struct Timon_table_entry { char alternate; /* USB alternate interface */ @@ -52,9 +54,9 @@ struct Timon_table_entry unsigned char mode[13]; /* precomputed mode settings for cam */ }; -extern const struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; +extern const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4]; extern const unsigned int TimonRomTable [16][2][16][8]; - +extern const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON]; #endif diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index d5e6bc8..32fbe1a 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -1168,7 +1168,7 @@ #endif buf->sequence = 0; buf->memory = V4L2_MEMORY_MMAP; buf->m.offset = pdev->fill_image * pdev->len_per_image; - buf->length = buf->bytesused; + buf->length = pdev->len_per_image; pwc_next_image(pdev); PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index); @@ -1193,6 +1193,64 @@ #endif return 0; } + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *fsize = arg; + unsigned int i = 0, index = fsize->index; + + if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) { + for (i = 0; i < PSZ_MAX; i++) { + if (pdev->image_mask & (1UL << i)) { + if (!index--) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pwc_image_sizes[i].x; + fsize->discrete.height = pwc_image_sizes[i].y; + return 0; + } + } + } + } else if (fsize->index == 0 && + ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) || + (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) { + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pdev->abs_max.x; + fsize->discrete.height = pdev->abs_max.y; + return 0; + } + return -EINVAL; + } + + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *fival = arg; + int size = -1; + unsigned int i; + + for (i = 0; i < PSZ_MAX; i++) { + if (pwc_image_sizes[i].x == fival->width && + pwc_image_sizes[i].y == fival->height) { + size = i; + break; + } + } + + /* TODO: Support raw format */ + if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) { + return -EINVAL; + } + + i = pwc_get_fps(pdev, fival->index, size); + if (!i) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + fival->discrete.denominator = i; + + return 0; + } + default: return pwc_ioctl(pdev, cmd, arg); } /* ..switch */ diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index e778a2b..acbb931 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -44,7 +44,7 @@ #define PWC_MAJOR 10 #define PWC_MINOR 0 #define PWC_EXTRAMINOR 12 #define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR) -#define PWC_VERSION "10.0.12" +#define PWC_VERSION "10.0.13" #define PWC_NAME "pwc" #define PFX PWC_NAME ": " @@ -85,7 +85,7 @@ #define PWC_WARNING(fmt, args...) printk #define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) #define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) -#else /* if ! CONFIG_PWC_DEBUG */ +#else /* if ! CONFIG_USB_PWC_DEBUG */ #define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) #define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) @@ -287,6 +287,7 @@ void pwc_construct(struct pwc_device *pd /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); +extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); /* Calculate the number of bytes per image (not frame) */ extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 4d5bbd8..2d18f00 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -45,6 +45,7 @@ #include #include #include #include +#include #include #include @@ -80,7 +81,7 @@ struct saa711x_state { int sat; int width; int height; - enum v4l2_chip_ident ident; + u32 ident; u32 audclk_freq; u32 crystal_freq; u8 ucgc; @@ -1232,7 +1233,6 @@ static void saa711x_decode_vbi_line(stru static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct saa711x_state *state = i2c_get_clientdata(client); - int *iarg = arg; /* ioctls to allow direct access to the saa7115 registers for testing */ switch (cmd) { @@ -1437,9 +1437,8 @@ #ifdef CONFIG_VIDEO_ADV_DEBUG } #endif - case VIDIOC_INT_G_CHIP_IDENT: - *iarg = state->ident; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); default: return -EINVAL; @@ -1487,6 +1486,7 @@ static int saa711x_attach(struct i2c_ada if (memcmp(name, "1f711", 5)) { v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", address << 1, name); + kfree(client); return 0; } diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 654863d..9f98693 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -54,6 +54,7 @@ #include #include #include #include +#include #include static int debug = 0; @@ -234,7 +235,7 @@ static struct i2c_reg_value saa7127_init struct saa7127_state { v4l2_std_id std; - enum v4l2_chip_ident ident; + u32 ident; enum saa7127_input_type input_type; enum saa7127_output_type output_type; int video_enable; @@ -550,12 +551,12 @@ static int saa7127_command(struct i2c_cl struct v4l2_routing *route = arg; switch (cmd) { - case VIDIOC_S_STD: + case VIDIOC_INT_S_STD_OUTPUT: if (state->std == *(v4l2_std_id *)arg) break; return saa7127_set_std(client, *(v4l2_std_id *)arg); - case VIDIOC_G_STD: + case VIDIOC_INT_G_STD_OUTPUT: *(v4l2_std_id *)arg = state->std; break; @@ -650,9 +651,8 @@ #endif break; } - case VIDIOC_INT_G_CHIP_IDENT: - *(enum v4l2_chip_ident *)arg = state->ident; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); default: return -EINVAL; diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 59da79c..309dca3 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -46,6 +46,7 @@ config VIDEO_SAA7134_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_TDA827X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 89f3210..4ea479b 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -1543,12 +1543,12 @@ struct saa7134_board saa7134_boards[] = },{ .name = name_comp1, .vmux = 0, - .amux = LINE2, + .amux = LINE1, .gpio = 0x02, },{ .name = name_svideo, .vmux = 6, - .amux = LINE2, + .amux = LINE1, .gpio = 0x02, }}, .radio = { @@ -1778,17 +1778,19 @@ struct saa7134_board saa7134_boards[] = [SAA7134_BOARD_FLYDVBTDUO] = { /* LifeView FlyDVB-T DUO */ /* "Nico Sabbi Hartmut Hackmann hartmut.hackmann@t-online.de*/ - .name = "LifeView FlyDVB-T DUO", + .name = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00200000, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 1, .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ .tv = 1, },{ .name = name_comp1, /* Composite signal on S-Video input */ @@ -1803,6 +1805,11 @@ struct saa7134_board saa7134_boards[] = .vmux = 8, .amux = LINE2, }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, }, [SAA7134_BOARD_PHILIPS_TOUGH] = { .name = "Philips TOUGH DVB-T reference design", @@ -2546,8 +2553,9 @@ struct saa7134_board saa7134_boards[] = .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tuner_config = 0, .mpeg = SAA7134_MPEG_DVB, - .gpiomask = 1 << 21, + .gpiomask = 0x0200000, .inputs = {{ .name = name_tv, .vmux = 1, @@ -2624,7 +2632,7 @@ struct saa7134_board saa7134_boards[] = }}, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, .gpio = 0x0200000, }, }, @@ -3043,6 +3051,7 @@ struct saa7134_board saa7134_boards[] = .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tuner_config = 1, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x000200000, .inputs = {{ @@ -3289,6 +3298,115 @@ struct saa7134_board saa7134_boards[] = .amux = LINE1, }}, }, + [SAA7134_BOARD_PHILIPS_TIGER_S] = { + .name = "Philips Tiger - S Reference design", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_M102] = { + .name = "Avermedia M102", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1<<21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_ASUS_P7131_4871] = { + .name = "ASUS P7131 4871", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0200000, + }}, + }, + [SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = { + .name = "ASUSTeK P7131 Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x0200000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3914,7 +4032,7 @@ struct pci_device_id saa7134_pci_tbl[] = .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x1043, .subdevice = 0x4876, - .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -3958,6 +4076,30 @@ struct pci_device_id saa7134_pci_tbl[] = .subdevice = 0x1175, .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf31e, + .driver_data = SAA7134_BOARD_AVERMEDIA_M102, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x4E42, /* MSI */ + .subdevice = 0x0306, /* TV@nywhere DUO */ + .driver_data = SAA7134_BOARD_FLYDVBTDUO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4871, + .driver_data = SAA7134_BOARD_ASUS_P7131_4871, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4857, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3971,7 +4113,6 @@ struct pci_device_id saa7134_pci_tbl[] = .subdevice = 0, .driver_data = SAA7134_BOARD_NOAUTO, },{ - /* --- default catch --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, @@ -4063,6 +4204,7 @@ int saa7134_board_init1(struct saa7134_d case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: @@ -4103,8 +4245,8 @@ int saa7134_board_init1(struct saa7134_d break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); - saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS: /* power-up tuner chip */ @@ -4137,6 +4279,11 @@ int saa7134_board_init1(struct saa7134_d "%s: Dual decoder functionality is disabled for now, use the other chip.\n", dev->name,card(dev).name,dev->name,dev->name); break; + case SAA7134_BOARD_AVERMEDIA_M102: + /* enable tuner */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); + break; } return 0; } @@ -4146,6 +4293,9 @@ int saa7134_board_init2(struct saa7134_d { unsigned char buf; int board; + struct tuner_setup tun_setup; + tun_setup.config = 0; + tun_setup.tuner_callback = saa7134_tuner_callback; switch (dev->board) { case SAA7134_BOARD_BMK_MPEX_NOTUNER: @@ -4162,8 +4312,6 @@ int saa7134_board_init2(struct saa7134_d dev->tuner_type = saa7134_boards[dev->board].tuner_type; if (TUNER_ABSENT != dev->tuner_type) { - struct tuner_setup tun_setup; - tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = ADDR_UNSET; @@ -4173,7 +4321,6 @@ int saa7134_board_init2(struct saa7134_d break; case SAA7134_BOARD_MD7134: { - struct tuner_setup tun_setup; u8 subaddr; u8 data[3]; int ret, tuner_t; @@ -4245,7 +4392,6 @@ int saa7134_board_init2(struct saa7134_d * the channel decoder. We have to make it transparent to find it */ { - struct tuner_setup tun_setup; u8 data[] = { 0x07, 0x02}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); @@ -4258,16 +4404,38 @@ int saa7134_board_init2(struct saa7134_d } break; case SAA7134_BOARD_PHILIPS_TIGER: + case SAA7134_BOARD_PHILIPS_TIGER_S: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + if(dev->autodetected && (dev->eedata[0x49] == 0x50)) { + dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + } + if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { + tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.type = TUNER_PHILIPS_TDA8290; + tun_setup.addr = 0x4b; + tun_setup.config = 2; + + saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); + data[2] = 0x68; + } + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + break; case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_MEDION_MD8800_QUADRO: case SAA7134_BOARD_HAUPPAUGE_HVR1110: /* this is a hybrid board, initialize to analog mode * and configure firmware eeprom address */ { - u8 data[] = { 0x3c, 0x33, 0x68}; + u8 data[] = { 0x3c, 0x33, 0x60}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } @@ -4281,18 +4449,18 @@ int saa7134_board_init2(struct saa7134_d break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - /* make the tda10046 find its eeprom */ + /* initialize analog mode */ { - u8 data[] = { 0x3c, 0x33, 0x62}; + u8 data[] = { 0x3c, 0x33, 0x6a}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: case SAA7134_BOARD_CINERGY_HT_PCI: - /* make the tda10046 find its eeprom */ + /* initialize analog mode */ { - u8 data[] = { 0x3c, 0x33, 0x60}; + u8 data[] = { 0x3c, 0x33, 0x68}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index ed038ff..25f8470 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -117,6 +117,64 @@ void saa7134_track_gpio(struct saa7134_d dev->name, mode, (~mode) & status, mode & status, msg); } +void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) +{ + u32 index, bitval; + + index = 1 << bit_no; + switch (value) { + case 0: /* static value */ + case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value); + /* turn sync mode off if necessary */ + if (index & 0x00c00000) + saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00); + if (value) + bitval = index; + else + bitval = 0; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval); + break; + case 3: /* tristate */ + dprintk("setting GPIO%d to tristate\n", bit_no); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0); + break; + } +} + +int saa7134_tuner_callback(void *ptr, int command, int arg) +{ + u8 sync_control; + struct saa7134_dev *dev = ptr; + + switch (dev->tuner_type) { + case TUNER_PHILIPS_TDA8290: + switch (command) { + case 0: /* switch LNA gain through GPIO 22*/ + saa7134_set_gpio(dev, 22, arg) ; + break; + case 1: /* vsync output at GPIO22. 50 / 60Hz */ + dprintk("setting GPIO22 to vsync %d\n", arg); + saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80); + saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03); + if (arg == 1) + sync_control = 11; + else + sync_control = 17; + saa_writeb(SAA7134_VGATE_START, sync_control); + saa_writeb(SAA7134_VGATE_STOP, sync_control + 1); + saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00); + break; + default: + return -EINVAL; + } + break; + default: + return -ENODEV; + } + return 0; +} + /* ------------------------------------------------------------------ */ @@ -124,55 +182,28 @@ void saa7134_track_gpio(struct saa7134_d /* delayed request_module */ #if defined(CONFIG_MODULES) && defined(MODULE) -static int need_empress; -static int need_dvb; -static int need_alsa; -static int need_oss; -static int pending_call(struct notifier_block *self, unsigned long state, - void *module) -{ - if (module != THIS_MODULE || state != MODULE_STATE_LIVE) - return NOTIFY_DONE; - if (need_empress) +static void request_module_async(struct work_struct *work){ + struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk); + if (card_is_empress(dev)) request_module("saa7134-empress"); - if (need_dvb) + if (card_is_dvb(dev)) request_module("saa7134-dvb"); - if (need_alsa) + if (alsa) request_module("saa7134-alsa"); - if (need_oss) + if (oss) request_module("saa7134-oss"); - return NOTIFY_DONE; } -static int pending_registered; -static struct notifier_block pending_notifier = { - .notifier_call = pending_call, -}; - -static void request_module_depend(char *name, int *flag) +static void request_submodules(struct saa7134_dev *dev) { - int err; - switch (THIS_MODULE->state) { - case MODULE_STATE_COMING: - if (!pending_registered) { - err = register_module_notifier(&pending_notifier); - pending_registered = 1; - } - *flag = 1; - break; - case MODULE_STATE_LIVE: - request_module(name); - break; - default: - /* nothing */; - break; - } + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); } #else -#define request_module_depend(name,flag) +#define request_submodules(dev) #endif /* CONFIG_MODULES */ /* ------------------------------------------------------------------ */ @@ -703,7 +734,6 @@ static int saa7134_hwfini(struct saa7134 saa7134_ts_fini(dev); saa7134_input_fini(dev); saa7134_vbi_fini(dev); - saa7134_video_fini(dev); saa7134_tvaudio_fini(dev); return 0; } @@ -944,18 +974,9 @@ #endif request_module("tuner"); if (card_is_empress(dev)) { request_module("saa6752hs"); - request_module_depend("saa7134-empress",&need_empress); } - if (card_is_dvb(dev)) - request_module_depend("saa7134-dvb",&need_dvb); - - - if (alsa) - request_module_depend("saa7134-alsa",&need_alsa); - - if (oss) - request_module_depend("saa7134-oss",&need_oss); + request_submodules(dev); v4l2_prio_init(&dev->prio); @@ -1013,6 +1034,9 @@ #endif saa7134_dmasound_init(dev); } + if (TUNER_ABSENT != dev->tuner_type) + saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + return 0; fail4: @@ -1152,10 +1176,6 @@ #endif static void saa7134_fini(void) { -#if defined(CONFIG_MODULES) && defined(MODULE) - if (pending_registered) - unregister_module_notifier(&pending_notifier); -#endif /* CONFIG_MODULES */ pci_unregister_driver(&saa7134_pci_driver); } @@ -1164,6 +1184,7 @@ module_exit(saa7134_fini); /* ----------------------------------------------------------- */ +EXPORT_SYMBOL(saa7134_set_gpio); EXPORT_SYMBOL(saa7134_i2c_call_clients); EXPORT_SYMBOL(saa7134_devlist); EXPORT_SYMBOL(saa7134_boards); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index e3059fd..65aec88 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -41,7 +41,9 @@ #include "nxt200x.h" #include "tda10086.h" #include "tda826x.h" +#include "tda827x.h" #include "isl6421.h" + MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -54,7 +56,21 @@ static int use_frontend = 0; module_param(use_frontend, int, 0644); MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); -/* ------------------------------------------------------------------ */ +static int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off)."); + +#define dprintk(fmt, arg...) do { if (debug) \ + printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0) + +/* Print a warning */ +#define wprintk(fmt, arg...) \ + printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ + * mt352 based DVB-T cards + */ + static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) { u32 ok; @@ -75,8 +91,7 @@ static int pinnacle_antenna_pwr(struct s saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); udelay(10); ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27); - printk("%s: %s %s\n", dev->name, __FUNCTION__, - ok ? "on" : "off"); + dprintk("%s %s\n", __FUNCTION__, ok ? "on" : "off"); if (!ok) saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); @@ -96,7 +111,7 @@ static int mt352_pinnacle_init(struct dv static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 }; struct saa7134_dev *dev= fe->dvb->priv; - printk("%s: %s called\n",dev->name,__FUNCTION__); + dprintk("%s called\n", __FUNCTION__); mt352_write(fe, clock_config, sizeof(clock_config)); udelay(200); @@ -185,10 +200,26 @@ static struct mt352_config avermedia_777 .demod_init = mt352_aver777_init, }; -/* ------------------------------------------------------------------ */ -static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +/* ================================================================== + * tda1004x based DVB-T cards, helper functions + */ + +static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct saa7134_dev *dev = fe->dvb->priv; + return request_firmware(fw, name, &dev->pci->dev); +} + +/* ------------------------------------------------------------------ + * these tuners are tu1216, td1316(a) + */ + +static int philips_tda6651_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; u8 tuner_buf[4]; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; @@ -263,15 +294,20 @@ static int philips_tda6651_pll_set(u8 ad if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + wprintk("could not write to tuner at addr: 0x%02x\n", + addr << 1); return -EIO; + } msleep(1); return 0; } -static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) +static int philips_tu1216_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; @@ -287,46 +323,17 @@ static int philips_tda6651_pll_init(u8 a /* ------------------------------------------------------------------ */ -static int philips_tu1216_tuner_60_init(struct dvb_frontend *fe) -{ - return philips_tda6651_pll_init(0x60, fe); -} - -static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - return philips_tda6651_pll_set(0x60, fe, params); -} - -static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct saa7134_dev *dev = fe->dvb->priv; - return request_firmware(fw, name, &dev->pci->dev); -} - static struct tda1004x_config philips_tu1216_60_config = { - .demod_address = 0x8, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int philips_tu1216_tuner_61_init(struct dvb_frontend *fe) -{ - return philips_tda6651_pll_init(0x61, fe); -} - -static int philips_tu1216_tuner_61_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - return philips_tda6651_pll_set(0x61, fe, params); -} - static struct tda1004x_config philips_tu1216_61_config = { .demod_address = 0x8, @@ -335,7 +342,8 @@ static struct tda1004x_config philips_tu .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; /* ------------------------------------------------------------------ */ @@ -343,24 +351,42 @@ static struct tda1004x_config philips_tu static int philips_td1316_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; - struct i2c_msg init_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; + struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; /* setup PLL configuration */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); return 0; } static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { - return philips_tda6651_pll_set(0x61, fe, params); + return philips_tda6651_pll_set(fe, params); } +static int philips_td1316_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; + struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + /* switch the tuner to analog mode */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1) + return -EIO; + return 0; +} + +/* ------------------------------------------------------------------ */ + static int philips_europa_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -380,18 +406,14 @@ static int philips_europa_tuner_init(str static int philips_europa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - /* this message actually turns the tuner back to analog mode */ - static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; - struct i2c_msg analog_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; - i2c_transfer(&dev->i2c_adap, &analog_msg, 1); - msleep(1); + static u8 msg[] = { 0x00, 0x14 }; + struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + if (philips_td1316_tuner_sleep(fe)) + return -EIO; /* switch the board to analog mode */ - analog_msg.addr = 0x43; - analog_msg.len = 0x02; - msg[0] = 0x00; - msg[1] = 0x14; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &analog_msg, 1); @@ -416,7 +438,8 @@ static struct tda1004x_config philips_eu .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_IFO_AUTO_POS, .if_freq = TDA10046_FREQ_052, - .request_firmware = NULL, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; /* ------------------------------------------------------------------ */ @@ -424,9 +447,11 @@ static struct tda1004x_config philips_eu static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; /* this message is to set up ATC and ALC */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -440,9 +465,11 @@ static int philips_fmd1216_tuner_init(st static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; /* this message actually turns the tuner back to analog mode */ - static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -460,8 +487,10 @@ static int philips_fmd1216_tuner_sleep(s static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = tuner_buf,.len = + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; int tuner_frequency = 0; int divider = 0; @@ -536,8 +565,11 @@ static int philips_fmd1216_tuner_set_par if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + wprintk("could not write to tuner at addr: 0x%02x\n", + addr << 1); return -EIO; + } return 0; } @@ -548,582 +580,365 @@ static struct tda1004x_config medion_car .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_IFO_AUTO_NEG, .if_freq = TDA10046_FREQ_3613, - .request_firmware = NULL, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -struct tda827x_data { - u32 lomax; - u8 spd; - u8 bs; - u8 bp; - u8 cp; - u8 gc3; - u8 div1p5; -}; - -static struct tda827x_data tda827x_dvbt[] = { - { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, - { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, - { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, - { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, - { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, - { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, - { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} -}; - -static int philips_tda827x_tuner_init(struct dvb_frontend *fe) -{ - return 0; -} +/* ------------------------------------------------------------------ + * tda 1004x based cards with philips silicon tuner + */ -static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static void philips_tda827x_lna_gain(struct dvb_frontend *fe, int high) { struct saa7134_dev *dev = fe->dvb->priv; - u8 tuner_buf[14]; - - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf, - .len = sizeof(tuner_buf) }; - int i, tuner_freq, if_freq; - u32 N; - switch (params->u.ofdm.bandwidth) { - case BANDWIDTH_6_MHZ: - if_freq = 4000000; - break; - case BANDWIDTH_7_MHZ: - if_freq = 4500000; - break; - default: /* 8 MHz or Auto */ - if_freq = 5000000; - break; - } - tuner_freq = params->frequency + if_freq; - - i = 0; - while (tda827x_dvbt[i].lomax < tuner_freq) { - if(tda827x_dvbt[i + 1].lomax == 0) - break; - i++; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->i2c_gate; + u8 config = state->config->tuner_config; + u8 GP00_CF[] = {0x20, 0x01}; + u8 GP00_LEV[] = {0x22, 0x00}; + + struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = GP00_CF, .len = 2}; + if (config) { + if (high) { + dprintk("setting LNA to high gain\n"); + } else { + dprintk("setting LNA to low gain\n"); + } } - - N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2); - tuner_buf[0] = 0; - tuner_buf[1] = (N>>8) | 0x40; - tuner_buf[2] = N & 0xff; - tuner_buf[3] = 0; - tuner_buf[4] = 0x52; - tuner_buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) + - (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp; - tuner_buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f; - tuner_buf[7] = 0xbf; - tuner_buf[8] = 0x2a; - tuner_buf[9] = 0x05; - tuner_buf[10] = 0xff; - tuner_buf[11] = 0x00; - tuner_buf[12] = 0x00; - tuner_buf[13] = 0x40; - - tuner_msg.len = 14; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(500); - /* correct CP value */ - tuner_buf[0] = 0x30; - tuner_buf[1] = 0x50 + tda827x_dvbt[i].cp; - tuner_msg.len = 2; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - - return 0; -} - -static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda827x_sleep[] = { 0x30, 0xd0}; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tda827x_sleep, - .len = sizeof(tda827x_sleep) }; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - return 0; -} - -static struct tda1004x_config tda827x_lifeview_config = { - .demod_address = 0x08, - .invert = 1, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, - .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, -}; - -/* ------------------------------------------------------------------ */ - -struct tda827xa_data { - u32 lomax; - u8 svco; - u8 spd; - u8 scr; - u8 sbs; - u8 gc3; -}; - -static struct tda827xa_data tda827xa_dvbt[] = { - { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, - { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, - { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}}; - - -static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - struct saa7134_dev *dev = fe->dvb->priv; - u8 tuner_buf[14]; - unsigned char reg2[2]; - - struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = tuner_buf}; - int i, tuner_freq, if_freq; - u32 N; - - switch (params->u.ofdm.bandwidth) { - case BANDWIDTH_6_MHZ: - if_freq = 4000000; + switch (config) { + case 0: /* no LNA */ break; - case BANDWIDTH_7_MHZ: - if_freq = 4500000; + case 1: /* switch is GPIO 0 of tda8290 */ + case 2: + /* turn Vsync off */ + saa7134_set_gpio(dev, 22, 0); + GP00_LEV[1] = high ? 0 : 1; + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + wprintk("could not access tda8290 at addr: 0x%02x\n", + addr << 1); + return; + } + msg.buf = GP00_LEV; + if (config == 2) + GP00_LEV[1] = high ? 1 : 0; + i2c_transfer(&dev->i2c_adap, &msg, 1); break; - default: /* 8 MHz or Auto */ - if_freq = 5000000; + case 3: /* switch with GPIO of saa713x */ + saa7134_set_gpio(dev, 22, high); break; } - tuner_freq = params->frequency + if_freq; - - i = 0; - while (tda827xa_dvbt[i].lomax < tuner_freq) { - if(tda827xa_dvbt[i + 1].lomax == 0) - break; - i++; - } - - N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd; - tuner_buf[0] = 0; // subaddress - tuner_buf[1] = N >> 8; - tuner_buf[2] = N & 0xff; - tuner_buf[3] = 0; - tuner_buf[4] = 0x16; - tuner_buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) + - tda827xa_dvbt[i].sbs; - tuner_buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4); - tuner_buf[7] = 0x0c; - tuner_buf[8] = 0x06; - tuner_buf[9] = 0x24; - tuner_buf[10] = 0xff; - tuner_buf[11] = 0x60; - tuner_buf[12] = 0x00; - tuner_buf[13] = 0x39; // lpsel - msg.len = 14; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - - msg.buf= reg2; - msg.len = 2; - reg2[0] = 0x60; - reg2[1] = 0x3c; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - reg2[0] = 0xa0; - reg2[1] = 0x40; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - msleep(2); - /* correct CP value */ - reg2[0] = 0x30; - reg2[1] = 0x10 + tda827xa_dvbt[i].scr; - msg.len = 2; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - msleep(550); - reg2[0] = 0x50; - reg2[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - return 0; - } -static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe) +static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) { - struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda827xa_sleep[] = { 0x30, 0x90}; - struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827xa_sleep, - .len = sizeof(tda827xa_sleep) }; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - return 0; -} + struct tda1004x_state *state = fe->demodulator_priv; -/* ------------------------------------------------------------------ */ - -static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) -{ - struct saa7134_dev *dev = fe->dvb->priv; + u8 addr = state->config->i2c_gate; static u8 tda8290_close[] = { 0x21, 0xc0}; static u8 tda8290_open[] = { 0x21, 0x80}; - struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; + struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2}; if (enable) { tda8290_msg.buf = tda8290_close; } else { tda8290_msg.buf = tda8290_open; } - if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1) + if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) { + struct saa7134_dev *dev = fe->dvb->priv; + wprintk("could not access tda8290 I2C gate\n"); return -EIO; + } msleep(20); return 0; } /* ------------------------------------------------------------------ */ -static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tda827x_tuner_init(struct dvb_frontend *fe) { - int ret; + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; - ret = philips_tda827xa_pll_set(0x61, fe, params); - if (ret != 0) - return ret; + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; + } return 0; } -static int philips_tiger_tuner_init(struct dvb_frontend *fe) +static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x6a}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + struct tda1004x_state *state = fe->demodulator_priv; - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; + case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + } return 0; } -static int philips_tiger_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; +static struct tda827x_config tda827x_cfg = { + .lna_gain = philips_tda827x_lna_gain, + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep +}; - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; +static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *tda_conf) +{ + dev->dvb.frontend = dvb_attach(tda10046_attach, tda_conf, &dev->i2c_adap); + if (dev->dvb.frontend) { + if (tda_conf->i2c_gate) + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + if (dvb_attach(tda827x_attach, dev->dvb.frontend, tda_conf->tuner_address, + &dev->i2c_adap,&tda827x_cfg) == NULL) { + wprintk("no tda827x tuner found at addr: %02x\n", + tda_conf->tuner_address); + } + } } -static struct tda1004x_config philips_tiger_config = { +/* ------------------------------------------------------------------ */ +static struct tda1004x_config tda827x_lifeview_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int cinergy_ht_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x62}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x60}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} +static struct tda1004x_config philips_tiger_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; static struct tda1004x_config cinergy_ht_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP01, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +static struct tda1004x_config cinergy_ht_pci_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware +}; -static struct tda1004x_config pinnacle_pctv_310i_config = { +static struct tda1004x_config philips_tiger_s_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +static struct tda1004x_config pinnacle_pctv_310i_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 1, + .request_firmware = philips_tda1004x_request_firmware +}; static struct tda1004x_config hauppauge_hvr_1110_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - static struct tda1004x_config asus_p7131_dual_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware }; -static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x6a}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - /* make sure the DVB-T antenna input is set */ - saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000); - return 0; -} - -static int asus_p7131_dual_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - /* reset antenna inputs for analog usage */ - saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000); - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int lifeview_trio_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - - ret = philips_tda827xa_pll_set(0x60, fe, params); - return ret; -} - -static int lifeview_trio_tuner_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_tuner_sleep(0x60, fe); - return 0; -} - static struct tda1004x_config lifeview_trio_config = { .demod_address = 0x09, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP00, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP00_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int ads_duo_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - - ret = philips_tda827xa_pll_set(0x61, fe, params); - return ret; -} - -static int ads_duo_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - /* route TDA8275a AGC input to the channel decoder */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x60); - return 0; -} - -static int ads_duo_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - /* route TDA8275a AGC input to the analog IF chip*/ - saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x20); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} - -static struct tda1004x_config ads_tech_duo_config = { +static struct tda1004x_config tevion_dvbt220rf_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP00, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int tevion_dvb220rf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - ret = philips_tda827xa_pll_set(0x60, fe, params); - return ret; -} - -static int tevion_dvb220rf_tuner_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} +static struct tda1004x_config md8800_dvbt_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware +}; -static struct tda1004x_config tevion_dvbt220rf_config = { +static struct tda1004x_config asus_p7131_4871_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +static struct tda1004x_config asus_p7131_hybrid_lna_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware +}; +/* ------------------------------------------------------------------ + * special case: this card uses saa713x GPIO22 for the mode switch + */ -static int md8800_dvbt_analog_mode(struct dvb_frontend *fe) +static int ads_duo_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); + philips_tda827x_tuner_init(fe); + /* route TDA8275a AGC input to the channel decoder */ + saa7134_set_gpio(dev, 22, 1); return 0; } -static int md8800_dvbt_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int ads_duo_tuner_sleep(struct dvb_frontend *fe) { - int ret; struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda8290_close[] = { 0x21, 0xc0}; - static u8 tda8290_open[] = { 0x21, 0x80}; - struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; - /* close tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_close; - ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - if (ret != 1) - return -EIO; - msleep(20); - ret = philips_tda827xa_pll_set(0x60, fe, params); - if (ret != 0) - return ret; - /* open tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_open; - i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - return ret; + /* route TDA8275a AGC input to the analog IF chip*/ + saa7134_set_gpio(dev, 22, 0); + philips_tda827x_tuner_sleep(fe); + return 0; } -static struct tda1004x_config md8800_dvbt_config = { +static struct tda827x_config ads_duo_cfg = { + .lna_gain = philips_tda827x_lna_gain, + .init = ads_duo_tuner_init, + .sleep = ads_duo_tuner_sleep +}; + +static struct tda1004x_config ads_tech_duo_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP00_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; +/* ================================================================== + * tda10086 based DVB-S cards, helper functions + */ + static struct tda10086_config flydvbs = { .demod_address = 0x0e, .invert = 0, }; -/* ------------------------------------------------------------------ */ +/* ================================================================== + * nxt200x based ATSC cards, helper functions + */ static struct nxt200x_config avertvhda180 = { .demod_address = 0x0a, @@ -1143,10 +958,13 @@ static struct nxt200x_config kworldatsc1 .set_pll_input = nxt200x_set_pll_input, }; -/* ------------------------------------------------------------------ */ +/* ================================================================== + * Core code + */ static int dvb_init(struct saa7134_dev *dev) { + int ret; /* init struct videobuf_dvb */ dev->ts.nr_bufs = 32; dev->ts.nr_packets = 32*4; @@ -1160,7 +978,7 @@ static int dvb_init(struct saa7134_dev * switch (dev->board) { case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: - printk("%s: pinnacle 300i dvb setup\n",dev->name); + dprintk("pinnacle 300i dvb setup\n"); dev->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i, &dev->i2c_adap); if (dev->dvb.frontend) { @@ -1169,7 +987,7 @@ static int dvb_init(struct saa7134_dev * break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: - printk("%s: avertv 777 dvb setup\n",dev->name); + dprintk("avertv 777 dvb setup\n"); dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, &dev->i2c_adap); if (dev->dvb.frontend) { @@ -1191,42 +1009,15 @@ static int dvb_init(struct saa7134_dev * &philips_tu1216_60_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_60_init; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_60_set_params; + dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; } break; case SAA7134_BOARD_FLYDVBTDUO: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } - break; case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } + configure_tda827x_fe(dev, &tda827x_lifeview_config); break; case SAA7134_BOARD_PHILIPS_EUROPA: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_europa_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->original_demod_sleep = dev->dvb.frontend->ops.sleep; - dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep; - dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; - } - break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: dev->dvb.frontend = dvb_attach(tda10046_attach, &philips_europa_config, @@ -1244,125 +1035,61 @@ static int dvb_init(struct saa7134_dev * &philips_tu1216_61_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_61_init; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_61_set_params; + dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; } break; case SAA7134_BOARD_PHILIPS_TIGER: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_tiger_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &philips_tiger_config); break; case SAA7134_BOARD_PINNACLE_PCTV_310i: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &pinnacle_pctv_310i_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &pinnacle_pctv_310i_config); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &hauppauge_hvr_1110_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &hauppauge_hvr_1110_config); break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &asus_p7131_dual_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &asus_p7131_dual_config); break; case SAA7134_BOARD_FLYDVBT_LR301: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } + configure_tda827x_fe(dev, &tda827x_lifeview_config); break; case SAA7134_BOARD_FLYDVB_TRIO: if(! use_frontend) { //terrestrial - dev->dvb.frontend = dvb_attach(tda10046_attach, - &lifeview_trio_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = - lifeview_trio_tuner_set_params; - } + configure_tda827x_fe(dev, &lifeview_trio_config); } else { //satellite dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63, &dev->i2c_adap, 0) == NULL) { - printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__); + wprintk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__); } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__); + wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__); } } } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: dev->dvb.frontend = dvb_attach(tda10046_attach, &ads_tech_duo_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params; + if (dvb_attach(tda827x_attach,dev->dvb.frontend, + ads_tech_duo_config.tuner_address, + &dev->i2c_adap,&ads_duo_cfg) == NULL) { + wprintk("no tda827x tuner found at addr: %02x\n", + ads_tech_duo_config.tuner_address); + } } break; case SAA7134_BOARD_TEVION_DVBT_220RF: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tevion_dvbt220rf_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; - } - break; - case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &ads_tech_duo_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params; - } + configure_tda827x_fe(dev, &tevion_dvbt220rf_config); break; case SAA7134_BOARD_MEDION_MD8800_QUADRO: - dev->dvb.frontend = tda10046_attach(&md8800_dvbt_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = md8800_dvbt_analog_mode; - dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set; - } + configure_tda827x_fe(dev, &md8800_dvbt_config); break; case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, @@ -1386,11 +1113,11 @@ static int dvb_init(struct saa7134_dev * if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - printk("%s: No tda826x found!\n", __FUNCTION__); + wprintk("%s: No tda826x found!\n", __FUNCTION__); } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - printk("%s: No ISL6421 found!\n", __FUNCTION__); + wprintk("%s: No ISL6421 found!\n", __FUNCTION__); } } break; @@ -1415,41 +1142,45 @@ static int dvb_init(struct saa7134_dev * } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &cinergy_ht_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - - } + configure_tda827x_fe(dev, &cinergy_ht_config); break; case SAA7134_BOARD_CINERGY_HT_PCI: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &cinergy_ht_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set; - - } + configure_tda827x_fe(dev, &cinergy_ht_pci_config); + break; + case SAA7134_BOARD_PHILIPS_TIGER_S: + configure_tda827x_fe(dev, &philips_tiger_s_config); + break; + case SAA7134_BOARD_ASUS_P7131_4871: + configure_tda827x_fe(dev, &asus_p7131_4871_config); + break; + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config); break; default: - printk("%s: Huh? unknown DVB card?\n",dev->name); + wprintk("Huh? unknown DVB card?\n"); break; } if (NULL == dev->dvb.frontend) { - printk("%s: frontend initialization failed\n",dev->name); + printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); return -1; } /* register everything else */ - return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); + ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); + + /* this sequence is necessary to make the tda1004x load its firmware + * and to enter analog mode of hybrid boards + */ + if (!ret) { + if (dev->dvb.frontend->ops.init) + dev->dvb.frontend->ops.init(dev->dvb.frontend); + if (dev->dvb.frontend->ops.sleep) + dev->dvb.frontend->ops.sleep(dev->dvb.frontend); + if (dev->dvb.frontend->ops.tuner_ops.sleep) + dev->dvb.frontend->ops.tuner_ops.sleep(dev->dvb.frontend); + } + return ret; } static int dvb_fini(struct saa7134_dev *dev) diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index cce8da6..1cb8c70 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -370,6 +370,8 @@ static int attach_inform(struct i2c_clie tun_setup.type = tuner; tun_setup.addr = saa7134_boards[dev->board].tuner_addr; + tun_setup.config = saa7134_boards[dev->board].tuner_config; + tun_setup.tuner_callback = saa7134_tuner_callback; if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) { @@ -445,7 +447,7 @@ static void do_i2c_scan(char *name, stru unsigned char buf; int i,rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c,&buf,0); if (rc < 0) diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 46c583f..c0de37e 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -321,6 +321,7 @@ int saa7134_input_init1(struct saa7134_d mask_keydown = 0x0040000; break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: ir_codes = ir_codes_asus_pc39; mask_keydown = 0x0040000; rc5_gpio = 1; diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index f2cb630..9985ded 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -26,6 +26,7 @@ #include #include #include #include +#include #include "saa7134-reg.h" #include "saa7134.h" @@ -516,14 +517,12 @@ static int res_get(struct saa7134_dev *d return 1; } -static -int res_check(struct saa7134_fh *fh, unsigned int bit) +static int res_check(struct saa7134_fh *fh, unsigned int bit) { return (fh->resources & bit); } -static -int res_locked(struct saa7134_dev *dev, unsigned int bit) +static int res_locked(struct saa7134_dev *dev, unsigned int bit) { return (dev->resources & bit); } @@ -603,7 +602,14 @@ static void set_tvnorm(struct saa7134_de saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); - saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id); + /* only tell the tuner if this is a tv input */ + if (card_in(dev,dev->ctl_input).tv) { + if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290) + && ((card(dev).tuner_config == 1) + || (card(dev).tuner_config == 2))) + saa7134_set_gpio(dev, 22, 5); + saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id); + } } static void video_mux(struct saa7134_dev *dev, int input) @@ -732,25 +738,6 @@ struct cliplist { __u8 disable; }; -static void sort_cliplist(struct cliplist *cl, int entries) -{ - struct cliplist swap; - int i,j,n; - - for (i = entries-2; i >= 0; i--) { - for (n = 0, j = 0; j <= i; j++) { - if (cl[j].position > cl[j+1].position) { - swap = cl[j]; - cl[j] = cl[j+1]; - cl[j+1] = swap; - n++; - } - } - if (0 == n) - break; - } -} - static void set_cliplist(struct saa7134_dev *dev, int reg, struct cliplist *cl, int entries, char *name) { @@ -784,15 +771,27 @@ static int clip_range(int val) return val; } +/* Sort into smallest position first order */ +static int cliplist_cmp(const void *a, const void *b) +{ + const struct cliplist *cla = a; + const struct cliplist *clb = b; + if (cla->position < clb->position) + return -1; + if (cla->position > clb->position) + return 1; + return 0; +} + static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips, int nclips, int interlace) { struct cliplist col[16], row[16]; - int cols, rows, i; + int cols = 0, rows = 0, i; int div = interlace ? 2 : 1; - memset(col,0,sizeof(col)); cols = 0; - memset(row,0,sizeof(row)); rows = 0; + memset(col, 0, sizeof(col)); + memset(row, 0, sizeof(row)); for (i = 0; i < nclips && i < 8; i++) { col[cols].position = clip_range(clips[i].c.left); col[cols].enable = (1 << i); @@ -808,8 +807,8 @@ static int setup_clipping(struct saa7134 row[rows].disable = (1 << i); rows++; } - sort_cliplist(col,cols); - sort_cliplist(row,rows); + sort(col, cols, sizeof col[0], cliplist_cmp, NULL); + sort(row, rows, sizeof row[0], cliplist_cmp, NULL); set_cliplist(dev,0x380,col,cols,"cols"); set_cliplist(dev,0x384,row,rows,"rows"); return 0; @@ -1261,19 +1260,14 @@ static struct videobuf_queue* saa7134_qu static int saa7134_resource(struct saa7134_fh *fh) { - int res = 0; + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return RESOURCE_VIDEO; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - res = RESOURCE_VIDEO; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - res = RESOURCE_VBI; - break; - default: - BUG(); - } - return res; + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return RESOURCE_VBI; + + BUG(); + return 0; } static int video_open(struct inode *inode, struct file *file) @@ -1461,8 +1455,7 @@ static int video_release(struct inode *i return 0; } -static int -video_mmap(struct file *file, struct vm_area_struct * vma) +static int video_mmap(struct file *file, struct vm_area_struct * vma) { struct saa7134_fh *fh = file->private_data; @@ -2461,12 +2454,6 @@ int saa7134_video_init2(struct saa7134_d return 0; } -int saa7134_video_fini(struct saa7134_dev *dev) -{ - /* nothing */ - return 0; -} - void saa7134_irq_video_intl(struct saa7134_dev *dev) { static const char *st[] = { diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index b3e3957..62224cc 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -231,6 +231,10 @@ #define SAA7134_BOARD_CINERGY_HT_PCMCIA #define SAA7134_BOARD_ENCORE_ENLTV 106 #define SAA7134_BOARD_ENCORE_ENLTV_FM 107 #define SAA7134_BOARD_CINERGY_HT_PCI 108 +#define SAA7134_BOARD_PHILIPS_TIGER_S 109 +#define SAA7134_BOARD_AVERMEDIA_M102 110 +#define SAA7134_BOARD_ASUS_P7131_4871 111 +#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -280,6 +284,7 @@ struct saa7134_board { unsigned char radio_addr; unsigned int tda9887_conf; + unsigned int tuner_config; /* peripheral I/O */ enum saa7134_video_out video_out; @@ -435,6 +440,8 @@ struct saa7134_dev { #ifdef VIDIOC_G_PRIORITY struct v4l2_prio_state prio; #endif + /* workstruct for loading modules */ + struct work_struct request_module_wk; /* insmod option/autodetected */ int autodetected; @@ -562,6 +569,8 @@ extern struct list_head saa7134_devlist extern int saa7134_no_overlay; void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); +void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value); +int saa7134_tuner_callback(void *ptr, int command, int arg); #define SAA7134_PGTABLE_SIZE 4096 @@ -620,7 +629,6 @@ int saa7134_common_ioctl(struct saa7134_ int saa7134_video_init1(struct saa7134_dev *dev); int saa7134_video_init2(struct saa7134_dev *dev); -int saa7134_video_fini(struct saa7134_dev *dev); void saa7134_irq_video_intl(struct saa7134_dev *dev); void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status); diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index 038448f..93fb04e 100644 --- a/drivers/media/video/se401.c +++ b/drivers/media/video/se401.c @@ -450,6 +450,13 @@ static int se401_start_stream(struct usb } for (i=0; isbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + if (!se401->sbuf[i].data) { + for(i = i - 1; i >= 0; i--) { + kfree(se401->sbuf[i].data); + se401->sbuf[i].data = NULL; + } + return -ENOMEM; + } } se401->bayeroffset=0; @@ -458,13 +465,26 @@ static int se401_start_stream(struct usb se401->scratch_overflow=0; for (i=0; iscratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + if (!se401->scratch[i].data) { + for(i = i - 1; i >= 0; i--) { + kfree(se401->scratch[i].data); + se401->scratch[i].data = NULL; + } + goto nomem_sbuf; + } se401->scratch[i].state=BUFFER_UNUSED; } for (i=0; i= 0; i--) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + } + goto nomem_scratch; + } usb_fill_bulk_urb(urb, se401->dev, usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), @@ -482,6 +502,18 @@ static int se401_start_stream(struct usb se401->framecount=0; return 0; + + nomem_scratch: + for (i=0; iscratch[i].data); + se401->scratch[i].data = NULL; + } + nomem_sbuf: + for (i=0; isbuf[i].data); + se401->sbuf[i].data = NULL; + } + return -ENOMEM; } static int se401_stop_stream(struct usb_se401 *se401) diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig index 1a7ccb6..19204f5 100644 --- a/drivers/media/video/sn9c102/Kconfig +++ b/drivers/media/video/sn9c102/Kconfig @@ -1,6 +1,6 @@ config USB_SN9C102 tristate "USB SN9C1xx PC Camera Controller support" - depends on USB && VIDEO_V4L1 + depends on USB && VIDEO_V4L2 ---help--- Say Y here if you want support for cameras based on SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile index 30e3dfe..a56d16f 100644 --- a/drivers/media/video/sn9c102/Makefile +++ b/drivers/media/video/sn9c102/Makefile @@ -1,7 +1,14 @@ -sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \ - sn9c102_ov7630.o sn9c102_ov7660.o sn9c102_pas106b.o \ - sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \ - sn9c102_tas5130d1b.o +sn9c102-objs := sn9c102_core.o \ + sn9c102_hv7131d.o \ + sn9c102_hv7131r.o \ + sn9c102_mi0343.o \ + sn9c102_mi0360.o \ + sn9c102_ov7630.o \ + sn9c102_ov7660.o \ + sn9c102_pas106b.o \ + sn9c102_pas202bcb.o \ + sn9c102_tas5110c1b.o \ + sn9c102_tas5110d.o \ + sn9c102_tas5130d1b.o obj-$(CONFIG_USB_SN9C102) += sn9c102.o - diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 5428f34..680e746 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -78,8 +78,13 @@ enum sn9c102_stream_state { typedef char sn9c102_sof_header_t[62]; +struct sn9c102_sof_t { + sn9c102_sof_header_t header; + u16 bytesread; +}; + struct sn9c102_sysfs_attr { - u8 reg, i2c_reg; + u16 reg, i2c_reg; sn9c102_sof_header_t frame_header; }; @@ -112,7 +117,7 @@ struct sn9c102_device { struct v4l2_jpegcompression compression; struct sn9c102_sysfs_attr sysfs; - sn9c102_sof_header_t sof_header; + struct sn9c102_sof_t sof; u16 reg[384]; struct sn9c102_module_param module_param; @@ -182,8 +187,8 @@ do { if ((level) == 1 || (level) == 2) \ pr_info("sn9c102: " fmt "\n", ## args); \ else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \ - __LINE__ , ## args); \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) #else @@ -194,8 +199,8 @@ #endif #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ + __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index d0e2b40..89f8335 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -44,11 +44,12 @@ #include "sn9c102.h" /*****************************************************************************/ #define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" -#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia" +#define SN9C102_MODULE_ALIAS "sn9c1xx" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.34" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 34) +#define SN9C102_MODULE_VERSION "1:1.39" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 39) /*****************************************************************************/ @@ -56,6 +57,7 @@ MODULE_DEVICE_TABLE(usb, sn9c102_id_tabl MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_ALIAS(SN9C102_MODULE_ALIAS); MODULE_VERSION(SN9C102_MODULE_VERSION); MODULE_LICENSE(SN9C102_MODULE_LICENSE); @@ -106,8 +108,7 @@ MODULE_PARM_DESC(debug, "\n1 = critical errors" "\n2 = significant informations" "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." + "\nLevel 3 is useful for testing only." "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." "\n"); #endif @@ -121,8 +122,8 @@ sn9c102_request_buffers(struct sn9c102_d struct v4l2_pix_format* p = &(cam->sensor.pix_format); struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; @@ -208,27 +209,40 @@ static void sn9c102_queue_unusedframes(s } /*****************************************************************************/ - -int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index) +/* + * Write a sequence of count value/register pairs. Returns -1 after the + * first failed write, or 0 for no errors. + */ +int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2], + int count) { struct usb_device* udev = cam->usbdev; + u8* value = cam->control_buffer; /* Needed for DMA'able memory */ int i, res; - if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg)) - return -1; + for (i = 0; i < count; i++) { + u8 index = valreg[i][1]; + + /* + * index is a u8, so it must be <256 and can't be out of range. + * If we put in a check anyway, gcc annoys us with a warning + * that our check is useless. People get all uppity when they + * see warnings in the kernel compile. + */ + + *value = valreg[i][0]; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x08, 0x41, index, 0, + value, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, " + "index 0x%02X, error %d)", *value, index, res); + return -1; + } - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - index, 0, buff, sizeof(buff), - SN9C102_CTRL_TIMEOUT*sizeof(buff)); - if (res < 0) { - DBG(3, "Failed to write registers (index 0x%02X, error %d)", - index, res); - return -1; + cam->reg[index] = *value; } - for (i = 0; i < sizeof(buff); i++) - cam->reg[index+i] = buff[i]; - return 0; } @@ -485,18 +499,43 @@ static size_t sn9c102_sof_length(struct static void* sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) { - char sof_header[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - size_t soflen = 0, i; + static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + const char *m = mem; + size_t soflen = 0, i, j; soflen = sn9c102_sof_length(cam); - for (i = 0; (len >= soflen) && (i <= len - soflen); i++) - if (!memcmp(mem + i, sof_header, sizeof(sof_header))) { - memcpy(cam->sof_header, mem + i, - sizeof(sn9c102_sof_header_t)); - /* Skip the header */ - return mem + i + soflen; + for (i = 0; i < len; i++) { + size_t b; + + /* Read the variable part of the header */ + if (unlikely(cam->sof.bytesread >= sizeof(marker))) { + cam->sof.header[cam->sof.bytesread] = *(m+i); + if (++cam->sof.bytesread == soflen) { + cam->sof.bytesread = 0; + return mem + i; + } + continue; + } + + /* Search for the SOF marker (fixed part) in the header */ + for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { + if (unlikely(i+j) == len) + return NULL; + if (*(m+i+j) == marker[cam->sof.bytesread]) { + cam->sof.header[cam->sof.bytesread] = *(m+i+j); + if (++cam->sof.bytesread == sizeof(marker)) { + PDBGG("Bytes to analyze: %zd. SOF " + "starts at byte #%zd", len, i); + i += j+1; + break; + } + } else { + cam->sof.bytesread = 0; + break; } + } + } return NULL; } @@ -505,7 +544,7 @@ sn9c102_find_sof_header(struct sn9c102_d static void* sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) { - char eof_header[4][4] = { + static const u8 eof_header[4][4] = { {0x00, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x00}, {0x80, 0x00, 0x00, 0x00}, @@ -513,10 +552,16 @@ sn9c102_find_eof_header(struct sn9c102_d }; size_t i, j; + /* The EOF header does not exist in compressed data */ if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - return NULL; /* EOF header does not exist in compressed data */ + return NULL; + /* + The EOF header might cross the packet boundary, but this is not a + problem, since the end of a frame is determined by checking its size + in the first place. + */ for (i = 0; (len >= 4) && (i <= len - 4); i++) for (j = 0; j < ARRAY_SIZE(eof_header); j++) if (!memcmp(mem + i, eof_header[j], 4)) @@ -529,7 +574,7 @@ sn9c102_find_eof_header(struct sn9c102_d static void sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) { - static u8 jpeg_header[589] = { + static const u8 jpeg_header[589] = { 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, @@ -639,6 +684,7 @@ static void sn9c102_urb_complete(struct cam->stream = STREAM_OFF; if ((*f)) (*f)->state = F_QUEUED; + cam->sof.bytesread = 0; DBG(3, "Stream interrupted by application"); wake_up(&cam->wait_stream); } @@ -676,6 +722,7 @@ static void sn9c102_urb_complete(struct if (status) { DBG(3, "Error in isochronous frame"); (*f)->state = F_ERROR; + cam->sof.bytesread = 0; continue; } @@ -692,13 +739,13 @@ end_of_frame: if (eof) img = (eof > pos) ? eof - pos - 1 : 0; - if ((*f)->buf.bytesused+img > imagesize) { + if ((*f)->buf.bytesused + img > imagesize) { u32 b; b = (*f)->buf.bytesused + img - imagesize; img = imagesize - (*f)->buf.bytesused; - DBG(3, "Expected EOF not found: " - "video frame cut"); + PDBGG("Expected EOF not found: video " + "frame cut"); if (eof) DBG(3, "Exceeded limit: +%u " "bytes", (unsigned)(b)); @@ -719,11 +766,6 @@ end_of_frame: V4L2_PIX_FMT_JPEG) && eof)) { u32 b; - if (cam->sensor.pix_format.pixelformat - == V4L2_PIX_FMT_JPEG) - sn9c102_write_eoimarker(cam, - (*f)); - b = (*f)->buf.bytesused; (*f)->state = F_DONE; (*f)->buf.sequence= ++cam->frame_count; @@ -741,7 +783,7 @@ end_of_frame: spin_unlock(&cam->queue_lock); memcpy(cam->sysfs.frame_header, - cam->sof_header, soflen); + cam->sof.header, soflen); DBG(3, "Video frame captured: %lu " "bytes", (unsigned long)(b)); @@ -791,7 +833,13 @@ start_of_frame: V4L2_PIX_FMT_SN9C10X || cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) { - eof = sof - soflen; + if (sof - pos >= soflen) { + eof = sof - soflen; + } else { /* remove header */ + eof = pos; + (*f)->buf.bytesused -= + (soflen - (sof - pos)); + } goto end_of_frame; } else { DBG(3, "SOF before expected EOF after " @@ -878,6 +926,7 @@ static int sn9c102_start_transfer(struct } cam->frame_current = NULL; + cam->sof.bytesread = 0; for (i = 0; i < SN9C102_URBS; i++) { err = usb_submit_urb(cam->urb[i], GFP_KERNEL); @@ -959,9 +1008,9 @@ static u16 sn9c102_strtou16(const char* if (len < 6) { strncpy(str, buff, len); - str[len+1] = '\0'; + str[len] = '\0'; } else { - strncpy(str, buff, 4); + strncpy(str, buff, 6); str[6] = '\0'; } @@ -1062,7 +1111,7 @@ static ssize_t sn9c102_show_val(struct c count = sprintf(buf, "%d\n", val); - DBG(3, "Read bytes: %zd", count); + DBG(3, "Read bytes: %zd, value: %d", count, val); mutex_unlock(&sn9c102_sysfs_lock); @@ -1197,7 +1246,7 @@ static ssize_t sn9c102_show_i2c_val(stru count = sprintf(buf, "%d\n", val); - DBG(3, "Read bytes: %zd", count); + DBG(3, "Read bytes: %zd, value: %d", count, val); mutex_unlock(&sn9c102_sysfs_lock); @@ -1371,35 +1420,35 @@ static CLASS_DEVICE_ATTR(frame_header, S static int sn9c102_create_sysfs(struct sn9c102_device* cam) { - struct video_device *v4ldev = cam->v4ldev; + struct class_device *classdev = &(cam->v4ldev->class_dev); int err = 0; - if ((err = video_device_create_file(v4ldev, &class_device_attr_reg))) + if ((err = class_device_create_file(classdev, &class_device_attr_reg))) goto err_out; - if ((err = video_device_create_file(v4ldev, &class_device_attr_val))) + if ((err = class_device_create_file(classdev, &class_device_attr_val))) goto err_reg; - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_frame_header))) goto err_val; if (cam->sensor.sysfs_ops) { - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_i2c_reg))) goto err_frame_header; - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_i2c_val))) goto err_i2c_reg; } if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_green))) goto err_i2c_val; } else { - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_blue))) goto err_i2c_val; - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_red))) goto err_blue; } @@ -1407,19 +1456,19 @@ static int sn9c102_create_sysfs(struct s return 0; err_blue: - video_device_remove_file(v4ldev, &class_device_attr_blue); + class_device_remove_file(classdev, &class_device_attr_blue); err_i2c_val: if (cam->sensor.sysfs_ops) - video_device_remove_file(v4ldev, &class_device_attr_i2c_val); + class_device_remove_file(classdev, &class_device_attr_i2c_val); err_i2c_reg: if (cam->sensor.sysfs_ops) - video_device_remove_file(v4ldev, &class_device_attr_i2c_reg); + class_device_remove_file(classdev, &class_device_attr_i2c_reg); err_frame_header: - video_device_remove_file(v4ldev, &class_device_attr_frame_header); + class_device_remove_file(classdev, &class_device_attr_frame_header); err_val: - video_device_remove_file(v4ldev, &class_device_attr_val); + class_device_remove_file(classdev, &class_device_attr_val); err_reg: - video_device_remove_file(v4ldev, &class_device_attr_reg); + class_device_remove_file(classdev, &class_device_attr_reg); err_out: return err; } @@ -1477,10 +1526,10 @@ sn9c102_set_compression(struct sn9c102_d case BRIDGE_SN9C101: case BRIDGE_SN9C102: case BRIDGE_SN9C103: - if (compression->quality == 0) + if (compression->quality == 0) err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17); - else if (compression->quality == 1) + else if (compression->quality == 1) err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17); break; @@ -1489,10 +1538,10 @@ sn9c102_set_compression(struct sn9c102_d if (compression->quality == 0) { for (i = 0; i <= 63; i++) { err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE0[i], + SN9C102_Y_QTABLE1[i], 0x100 + i); err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE0[i], + SN9C102_UV_QTABLE1[i], 0x140 + i); } err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, @@ -1597,9 +1646,13 @@ static int sn9c102_init(struct sn9c102_d if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102 || cam->bridge == BRIDGE_SN9C103) { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1; } else { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; cam->compression.quality = cam->reg[0x18] & 0x40 ? 0 : 1; err += sn9c102_set_compression(cam, &cam->compression); @@ -1805,7 +1858,7 @@ sn9c102_read(struct file* filp, char __u DBG(3, "Close and open the device again to choose " "the read method"); mutex_unlock(&cam->fileop_mutex); - return -EINVAL; + return -EBUSY; } if (cam->io == IO_NONE) { @@ -1845,16 +1898,16 @@ sn9c102_read(struct file* filp, char __u return err; } } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; } else if (timeout == 0 && !(cam->state & DEV_DISCONNECTED)) { DBG(1, "Video frame timeout elapsed"); @@ -2001,7 +2054,12 @@ static int sn9c102_mmap(struct file* fil return -EIO; } - if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { + mutex_unlock(&cam->fileop_mutex); + return -EACCES; + } + + if (cam->io != IO_MMAP || size != PAGE_ALIGN(cam->frame[0].buf.length)) { mutex_unlock(&cam->fileop_mutex); return -EINVAL; @@ -2267,7 +2325,7 @@ sn9c102_vidioc_s_crop(struct sn9c102_dev if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_CROP failed. " "Unmap the buffers first."); - return -EINVAL; + return -EBUSY; } /* Preserve R,G or B origin */ @@ -2410,8 +2468,8 @@ sn9c102_vidioc_enum_fmt(struct sn9c102_d case BRIDGE_SN9C101: case BRIDGE_SN9C102: case BRIDGE_SN9C103: - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; break; case BRIDGE_SN9C105: case BRIDGE_SN9C120: @@ -2445,8 +2503,10 @@ sn9c102_vidioc_g_fmt(struct sn9c102_devi if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X || - pfmt->pixelformat==V4L2_PIX_FMT_JPEG) + pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || + pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? 0 : (pfmt->width * pfmt->priv) / 8; pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); pfmt->field = V4L2_FIELD_NONE; @@ -2521,9 +2581,9 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_ case BRIDGE_SN9C101: case BRIDGE_SN9C102: case BRIDGE_SN9C103: - if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; + if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; break; case BRIDGE_SN9C105: case BRIDGE_SN9C120: @@ -2533,7 +2593,8 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_ break; } pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = pfmt->colorspace; + pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || pix->pixelformat == V4L2_PIX_FMT_JPEG) ? 0 : (pix->width * pix->priv) / 8; @@ -2551,7 +2612,7 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_ if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_FMT failed. Unmap the " "buffers first."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2666,14 +2727,14 @@ sn9c102_vidioc_reqbufs(struct sn9c102_de if (cam->io == IO_READ) { DBG(3, "Close and open the device again to choose the mmap " "I/O method"); - return -EINVAL; + return -EBUSY; } for (i = 0; i < cam->nbuffers; i++) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " "still mapped."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2785,15 +2846,15 @@ sn9c102_vidioc_dqbuf(struct sn9c102_devi if (err) return err; } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; else if (timeout == 0 && !(cam->state & DEV_DISCONNECTED)) { DBG(1, "Video frame timeout elapsed"); @@ -2837,9 +2898,6 @@ sn9c102_vidioc_streamon(struct sn9c102_d if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) return -EINVAL; - if (list_empty(&cam->inqueue)) - return -EINVAL; - cam->stream = STREAM_ON; DBG(3, "Stream on"); @@ -3166,8 +3224,8 @@ sn9c102_usb_probe(struct usb_interface* r = sn9c102_read_reg(cam, 0x00); if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { - DBG(1, "Sorry, this is not a SN9C1xx based camera " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + DBG(1, "Sorry, this is not a SN9C1xx-based camera " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); err = -ENODEV; goto fail; } @@ -3177,19 +3235,19 @@ sn9c102_usb_probe(struct usb_interface* case BRIDGE_SN9C101: case BRIDGE_SN9C102: DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C103: DBG(2, "SN9C103 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C105: DBG(2, "SN9C105 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C120: DBG(2, "SN9C120 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; } @@ -3260,6 +3318,8 @@ #ifdef CONFIG_VIDEO_ADV_DEBUG "device controlling. Error #%d", err); #else DBG(2, "Optional device control through 'sysfs' interface disabled"); + DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " + "configuration option to enable it."); #endif usb_set_intfdata(intf, cam); diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 3a682ec..f49bd8c 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -89,16 +89,22 @@ static const struct usb_device_id sn9c10 { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, /* SN9C120 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, @@ -114,12 +120,15 @@ static const struct usb_device_id sn9c10 Functions must return 0 on success, the appropriate error otherwise. */ extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); /* @@ -128,13 +137,16 @@ extern int sn9c102_probe_tas5130d1b(stru the order of the list below, from top to bottom. */ static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ NULL, }; diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c index 7ae368f..28a861a 100644 --- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -22,19 +22,13 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor hv7131d; - - static int hv7131d_init(struct sn9c102_device* cam) { - int err = 0; + int err; - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x0e, 0x18); - err += sn9c102_write_reg(cam, 0xf2, 0x19); + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x60, 0x17}, + {0x0e, 0x18}, {0xf2, 0x19}); err += sn9c102_i2c_write(cam, 0x01, 0x04); err += sn9c102_i2c_write(cam, 0x02, 0x00); @@ -153,7 +147,7 @@ static int hv7131d_set_pix_format(struct static struct sn9c102_sensor hv7131d = { .name = "HV7131D", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -250,11 +244,10 @@ static struct sn9c102_sensor hv7131d = { int sn9c102_probe_hv7131d(struct sn9c102_device* cam) { - int r0 = 0, r1 = 0, err = 0; + int r0 = 0, r1 = 0, err; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); if (err) return -EIO; @@ -263,7 +256,7 @@ int sn9c102_probe_hv7131d(struct sn9c102 if (r0 < 0 || r1 < 0) return -EIO; - if (r0 != 0x00 && r1 != 0x04) + if (r0 != 0x00 || r1 != 0x04) return -ENODEV; sn9c102_attach_sensor(cam, &hv7131d); diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c new file mode 100644 index 0000000..5a495ba --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_hv7131r.c @@ -0,0 +1,366 @@ +/*************************************************************************** + * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static int hv7131r_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, + {0x20, 0x05}, {0x20, 0x06}, + {0x03, 0x10}, {0x00, 0x14}, + {0x60, 0x17}, {0x0a, 0x18}, + {0xf0, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x44, 0x05}, {0x3e, 0x06}, + {0x1a, 0x07}, {0x03, 0x10}, + {0x08, 0x14}, {0xa3, 0x17}, + {0x4b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); + break; + default: + break; + } + + err += sn9c102_i2c_write(cam, 0x20, 0x00); + err += sn9c102_i2c_write(cam, 0x21, 0xd6); + err += sn9c102_i2c_write(cam, 0x25, 0x06); + + return err; +} + + +static int hv7131r_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLACK_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x08) ? 1 : 0; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131r_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, ctrl->value); + break; + case V4L2_CID_BLACK_LEVEL: + { + int r = sn9c102_i2c_read(cam, 0x01); + if (r < 0) + return -EIO; + err += sn9c102_i2c_write(cam, 0x01, + (ctrl->value<<3) | (r&0xf7)); + } + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131r_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131r_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa0, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } else { + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa5, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x24); + } else { + err += sn9c102_write_reg(cam, 0xa3, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + default: + break; + } + + return err; +} + + +static struct sn9c102_sensor hv7131r = { + .name = "HV7131R", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131r_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x08, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1a, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x2f, + .flags = 0, + }, + { + .id = V4L2_CID_BLACK_LEVEL, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto black level compensation", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &hv7131r_get_ctrl, + .set_ctrl = &hv7131r_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131r_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131r_set_pix_format +}; + + +int sn9c102_probe_hv7131r(struct sn9c102_device* cam) +{ + int devid, err; + + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, + {0x34, 0x01}, {0x20, 0x17}, + {0x34, 0x01}, {0x46, 0x01}); + + if (err) + return -EIO; + + devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); + if (devid < 0) + return -EIO; + + if (devid != 0x02) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131r); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c index a33d1bc..9200845 100644 --- a/drivers/media/video/sn9c102/sn9c102_mi0343.c +++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -22,36 +22,30 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor mi0343; -static u8 mi0343_i2c_data[5+1]; - - static int mi0343_init(struct sn9c102_device* cam) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x0a, 0x14); - err += sn9c102_write_reg(cam, 0x40, 0x01); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x07, 0x18); - err += sn9c102_write_reg(cam, 0xa0, 0x19); - - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x0d, 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x0d, 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x03, 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x04, 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x05, 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x06, 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x62, 0x04, 0x9a, 0, 0); + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); return err; } @@ -60,43 +54,46 @@ static int mi0343_init(struct sn9c102_de static int mi0343_get_ctrl(struct sn9c102_device* cam, struct v4l2_control* ctrl) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[5+1]; + switch (ctrl->id) { case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x09, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[2]; + ctrl->value = data[2]; return 0; case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x35, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, + 2+1, data) < 0) return -EIO; break; case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x20, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0; + ctrl->value = data[3] & 0x20 ? 1 : 0; return 0; case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x20, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0; + ctrl->value = data[3] & 0x80 ? 1 : 0; return 0; case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2d, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, + 2+1, data) < 0) return -EIO; break; case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2c, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, + 2+1, data) < 0) return -EIO; break; case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2e, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, + 2+1, data) < 0) return -EIO; break; default: @@ -108,7 +105,7 @@ static int mi0343_get_ctrl(struct sn9c10 case V4L2_CID_RED_BALANCE: case V4L2_CID_BLUE_BALANCE: case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8); + ctrl->value = data[3] | (data[2] << 8); if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) ctrl->value -= 0x10; else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) @@ -124,6 +121,7 @@ static int mi0343_get_ctrl(struct sn9c10 static int mi0343_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); u16 reg = 0; int err = 0; @@ -143,50 +141,42 @@ static int mi0343_set_ctrl(struct sn9c10 switch (ctrl->id) { case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x09, ctrl->value, 0x00, 0, 0); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x35, reg >> 8, reg & 0xff, 0, 0); break; case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, ctrl->value ? 0x40:0x00, ctrl->value ? 0x20:0x00, 0, 0); break; case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, ctrl->value ? 0x80:0x00, ctrl->value ? 0x80:0x00, 0, 0); break; case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2d, reg >> 8, reg & 0xff, 0, 0); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2c, reg >> 8, reg & 0xff, 0, 0); break; case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2b, reg >> 8, reg & 0xff, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2e, reg >> 8, reg & 0xff, 0, 0); break; @@ -216,16 +206,15 @@ static int mi0343_set_crop(struct sn9c10 static int mi0343_set_pix_format(struct sn9c102_device* cam, const struct v4l2_pix_format* pix) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0a, 0x00, 0x03, 0, 0); err += sn9c102_write_reg(cam, 0x20, 0x19); } else { - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0a, 0x00, 0x05, 0, 0); err += sn9c102_write_reg(cam, 0xa0, 0x19); } @@ -237,7 +226,7 @@ static int mi0343_set_pix_format(struct static struct sn9c102_sensor mi0343 = { .name = "MI-0343", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, .i2c_slave_id = 0x5d, @@ -343,19 +332,20 @@ static struct sn9c102_sensor mi0343 = { int sn9c102_probe_mi0343(struct sn9c102_device* cam) { + u8 data[5+1]; int err = 0; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + if (err) return -EIO; if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, - 2, mi0343_i2c_data) < 0) + 2, data) < 0) return -EIO; - if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3) + if (data[4] != 0x32 || data[3] != 0xe3) return -ENODEV; sn9c102_attach_sensor(cam, &mi0343); diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c new file mode 100644 index 0000000..64698ac --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c @@ -0,0 +1,338 @@ +/*************************************************************************** + * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static int mi0360_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0360_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[5+1]; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[2]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3] & 0x80 ? 1 : 0; + return 0; + default: + return -EINVAL; + } + + return 0; +} + + +static int mi0360_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x35, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2c, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2d, 0x03, ctrl->value, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2b, 0x03, ctrl->value, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2e, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0360_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0360_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x02, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + } else { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0x60, 0x19); + } + + return err; +} + + +static struct sn9c102_sensor mi0360 = { + .name = "MI-0360", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0360_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x0f, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x32, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + }, + .get_ctrl = &mi0360_get_ctrl, + .set_ctrl = &mi0360_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0360_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0360_set_pix_format +}; + + +int sn9c102_probe_mi0360(struct sn9c102_device* cam) +{ + u8 data[5+1]; + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + if (err) + return -EIO; + + if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, + 2+1, data) < 0) + return -EIO; + + if (data[2] != 0x82 || data[3] != 0x43) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0360); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c index 7df09ff..31b6080 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -22,9 +22,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor ov7630; - - static int ov7630_init(struct sn9c102_device* cam) { int err = 0; @@ -32,21 +29,20 @@ static int ov7630_init(struct sn9c102_de switch (sn9c102_get_bridge(cam)) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x0f, 0x18); - err += sn9c102_write_reg(cam, 0x50, 0x19); + err = sn9c102_write_const_regs(cam, {0x00, 0x14}, + {0x60, 0x17}, {0x0f, 0x18}, + {0x50, 0x19}); err += sn9c102_i2c_write(cam, 0x12, 0x8d); err += sn9c102_i2c_write(cam, 0x12, 0x0d); err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x15, 0x34); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1c); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x06); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x15, 0x35); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1c); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x06); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); err += sn9c102_i2c_write(cam, 0x20, 0x44); err += sn9c102_i2c_write(cam, 0x23, 0xee); err += sn9c102_i2c_write(cam, 0x26, 0xa0); @@ -65,42 +61,26 @@ static int ov7630_init(struct sn9c102_de err += sn9c102_i2c_write(cam, 0x71, 0x00); err += sn9c102_i2c_write(cam, 0x74, 0x21); err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, 0x00, 0x02); - err += sn9c102_write_reg(cam, 0x00, 0x03); - err += sn9c102_write_reg(cam, 0x1a, 0x04); - err += sn9c102_write_reg(cam, 0x20, 0x05); - err += sn9c102_write_reg(cam, 0x20, 0x06); - err += sn9c102_write_reg(cam, 0x20, 0x07); - err += sn9c102_write_reg(cam, 0x03, 0x10); - err += sn9c102_write_reg(cam, 0x0a, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x0f, 0x18); - err += sn9c102_write_reg(cam, 0x50, 0x19); - err += sn9c102_write_reg(cam, 0x1d, 0x1a); - err += sn9c102_write_reg(cam, 0x10, 0x1b); - err += sn9c102_write_reg(cam, 0x02, 0x1c); - err += sn9c102_write_reg(cam, 0x03, 0x1d); - err += sn9c102_write_reg(cam, 0x0f, 0x1e); - err += sn9c102_write_reg(cam, 0x0c, 0x1f); - err += sn9c102_write_reg(cam, 0x00, 0x20); - err += sn9c102_write_reg(cam, 0x10, 0x21); - err += sn9c102_write_reg(cam, 0x20, 0x22); - err += sn9c102_write_reg(cam, 0x30, 0x23); - err += sn9c102_write_reg(cam, 0x40, 0x24); - err += sn9c102_write_reg(cam, 0x50, 0x25); - err += sn9c102_write_reg(cam, 0x60, 0x26); - err += sn9c102_write_reg(cam, 0x70, 0x27); - err += sn9c102_write_reg(cam, 0x80, 0x28); - err += sn9c102_write_reg(cam, 0x90, 0x29); - err += sn9c102_write_reg(cam, 0xa0, 0x2a); - err += sn9c102_write_reg(cam, 0xb0, 0x2b); - err += sn9c102_write_reg(cam, 0xc0, 0x2c); - err += sn9c102_write_reg(cam, 0xd0, 0x2d); - err += sn9c102_write_reg(cam, 0xe0, 0x2e); - err += sn9c102_write_reg(cam, 0xf0, 0x2f); - err += sn9c102_write_reg(cam, 0xff, 0x30); + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x20, 0x05}, + {0x20, 0x06}, {0x20, 0x07}, + {0x03, 0x10}, {0x0a, 0x14}, + {0x60, 0x17}, {0x0f, 0x18}, + {0x50, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); err += sn9c102_i2c_write(cam, 0x12, 0x8d); err += sn9c102_i2c_write(cam, 0x12, 0x0d); @@ -108,23 +88,23 @@ static int ov7630_init(struct sn9c102_de err += sn9c102_i2c_write(cam, 0x11, 0x01); err += sn9c102_i2c_write(cam, 0x1b, 0x04); err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); break; default: break; @@ -428,15 +408,14 @@ int sn9c102_probe_ov7630(struct sn9c102_ switch (sn9c102_get_bridge(cam)) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, + {0x00, 0x01}, {0x28, 0x17}); + break; case BRIDGE_SN9C103: /* do _not_ change anything! */ - err += sn9c102_write_reg(cam, 0x09, 0x01); - err += sn9c102_write_reg(cam, 0x42, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); - err += sn9c102_write_reg(cam, 0x44, 0x02); + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, + {0x42, 0x01}, {0x28, 0x17}, + {0x44, 0x02}); pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); if (err || pid < 0) { /* try a different initialization */ err = sn9c102_write_reg(cam, 0x01, 0x01); diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c index d670c24..c898e94 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7660.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c @@ -22,160 +22,84 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor ov7660; - - static int ov7660_init(struct sn9c102_device* cam) { int err = 0; - err += sn9c102_write_reg(cam, 0x40, 0x02); - err += sn9c102_write_reg(cam, 0x00, 0x03); - err += sn9c102_write_reg(cam, 0x1a, 0x04); - err += sn9c102_write_reg(cam, 0x03, 0x10); - err += sn9c102_write_reg(cam, 0x08, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x8b, 0x18); - err += sn9c102_write_reg(cam, 0x00, 0x19); - err += sn9c102_write_reg(cam, 0x1d, 0x1a); - err += sn9c102_write_reg(cam, 0x10, 0x1b); - err += sn9c102_write_reg(cam, 0x02, 0x1c); - err += sn9c102_write_reg(cam, 0x03, 0x1d); - err += sn9c102_write_reg(cam, 0x0f, 0x1e); - err += sn9c102_write_reg(cam, 0x0c, 0x1f); - err += sn9c102_write_reg(cam, 0x00, 0x20); - err += sn9c102_write_reg(cam, 0x29, 0x21); - err += sn9c102_write_reg(cam, 0x40, 0x22); - err += sn9c102_write_reg(cam, 0x54, 0x23); - err += sn9c102_write_reg(cam, 0x66, 0x24); - err += sn9c102_write_reg(cam, 0x76, 0x25); - err += sn9c102_write_reg(cam, 0x85, 0x26); - err += sn9c102_write_reg(cam, 0x94, 0x27); - err += sn9c102_write_reg(cam, 0xa1, 0x28); - err += sn9c102_write_reg(cam, 0xae, 0x29); - err += sn9c102_write_reg(cam, 0xbb, 0x2a); - err += sn9c102_write_reg(cam, 0xc7, 0x2b); - err += sn9c102_write_reg(cam, 0xd3, 0x2c); - err += sn9c102_write_reg(cam, 0xde, 0x2d); - err += sn9c102_write_reg(cam, 0xea, 0x2e); - err += sn9c102_write_reg(cam, 0xf4, 0x2f); - err += sn9c102_write_reg(cam, 0xff, 0x30); - err += sn9c102_write_reg(cam, 0x00, 0x3F); - err += sn9c102_write_reg(cam, 0xC7, 0x40); - err += sn9c102_write_reg(cam, 0x01, 0x41); - err += sn9c102_write_reg(cam, 0x44, 0x42); - err += sn9c102_write_reg(cam, 0x00, 0x43); - err += sn9c102_write_reg(cam, 0x44, 0x44); - err += sn9c102_write_reg(cam, 0x00, 0x45); - err += sn9c102_write_reg(cam, 0x44, 0x46); - err += sn9c102_write_reg(cam, 0x00, 0x47); - err += sn9c102_write_reg(cam, 0xC7, 0x48); - err += sn9c102_write_reg(cam, 0x01, 0x49); - err += sn9c102_write_reg(cam, 0xC7, 0x4A); - err += sn9c102_write_reg(cam, 0x01, 0x4B); - err += sn9c102_write_reg(cam, 0xC7, 0x4C); - err += sn9c102_write_reg(cam, 0x01, 0x4D); - err += sn9c102_write_reg(cam, 0x44, 0x4E); - err += sn9c102_write_reg(cam, 0x00, 0x4F); - err += sn9c102_write_reg(cam, 0x44, 0x50); - err += sn9c102_write_reg(cam, 0x00, 0x51); - err += sn9c102_write_reg(cam, 0x44, 0x52); - err += sn9c102_write_reg(cam, 0x00, 0x53); - err += sn9c102_write_reg(cam, 0xC7, 0x54); - err += sn9c102_write_reg(cam, 0x01, 0x55); - err += sn9c102_write_reg(cam, 0xC7, 0x56); - err += sn9c102_write_reg(cam, 0x01, 0x57); - err += sn9c102_write_reg(cam, 0xC7, 0x58); - err += sn9c102_write_reg(cam, 0x01, 0x59); - err += sn9c102_write_reg(cam, 0x44, 0x5A); - err += sn9c102_write_reg(cam, 0x00, 0x5B); - err += sn9c102_write_reg(cam, 0x44, 0x5C); - err += sn9c102_write_reg(cam, 0x00, 0x5D); - err += sn9c102_write_reg(cam, 0x44, 0x5E); - err += sn9c102_write_reg(cam, 0x00, 0x5F); - err += sn9c102_write_reg(cam, 0xC7, 0x60); - err += sn9c102_write_reg(cam, 0x01, 0x61); - err += sn9c102_write_reg(cam, 0xC7, 0x62); - err += sn9c102_write_reg(cam, 0x01, 0x63); - err += sn9c102_write_reg(cam, 0xC7, 0x64); - err += sn9c102_write_reg(cam, 0x01, 0x65); - err += sn9c102_write_reg(cam, 0x44, 0x66); - err += sn9c102_write_reg(cam, 0x00, 0x67); - err += sn9c102_write_reg(cam, 0x44, 0x68); - err += sn9c102_write_reg(cam, 0x00, 0x69); - err += sn9c102_write_reg(cam, 0x44, 0x6A); - err += sn9c102_write_reg(cam, 0x00, 0x6B); - err += sn9c102_write_reg(cam, 0xC7, 0x6C); - err += sn9c102_write_reg(cam, 0x01, 0x6D); - err += sn9c102_write_reg(cam, 0xC7, 0x6E); - err += sn9c102_write_reg(cam, 0x01, 0x6F); - err += sn9c102_write_reg(cam, 0xC7, 0x70); - err += sn9c102_write_reg(cam, 0x01, 0x71); - err += sn9c102_write_reg(cam, 0x44, 0x72); - err += sn9c102_write_reg(cam, 0x00, 0x73); - err += sn9c102_write_reg(cam, 0x44, 0x74); - err += sn9c102_write_reg(cam, 0x00, 0x75); - err += sn9c102_write_reg(cam, 0x44, 0x76); - err += sn9c102_write_reg(cam, 0x00, 0x77); - err += sn9c102_write_reg(cam, 0xC7, 0x78); - err += sn9c102_write_reg(cam, 0x01, 0x79); - err += sn9c102_write_reg(cam, 0xC7, 0x7A); - err += sn9c102_write_reg(cam, 0x01, 0x7B); - err += sn9c102_write_reg(cam, 0xC7, 0x7C); - err += sn9c102_write_reg(cam, 0x01, 0x7D); - err += sn9c102_write_reg(cam, 0x44, 0x7E); - err += sn9c102_write_reg(cam, 0x00, 0x7F); - err += sn9c102_write_reg(cam, 0x14, 0x84); - err += sn9c102_write_reg(cam, 0x00, 0x85); - err += sn9c102_write_reg(cam, 0x27, 0x86); - err += sn9c102_write_reg(cam, 0x00, 0x87); - err += sn9c102_write_reg(cam, 0x07, 0x88); - err += sn9c102_write_reg(cam, 0x00, 0x89); - err += sn9c102_write_reg(cam, 0xEC, 0x8A); - err += sn9c102_write_reg(cam, 0x0f, 0x8B); - err += sn9c102_write_reg(cam, 0xD8, 0x8C); - err += sn9c102_write_reg(cam, 0x0f, 0x8D); - err += sn9c102_write_reg(cam, 0x3D, 0x8E); - err += sn9c102_write_reg(cam, 0x00, 0x8F); - err += sn9c102_write_reg(cam, 0x3D, 0x90); - err += sn9c102_write_reg(cam, 0x00, 0x91); - err += sn9c102_write_reg(cam, 0xCD, 0x92); - err += sn9c102_write_reg(cam, 0x0f, 0x93); - err += sn9c102_write_reg(cam, 0xf7, 0x94); - err += sn9c102_write_reg(cam, 0x0f, 0x95); - err += sn9c102_write_reg(cam, 0x0C, 0x96); - err += sn9c102_write_reg(cam, 0x00, 0x97); - err += sn9c102_write_reg(cam, 0x00, 0x98); - err += sn9c102_write_reg(cam, 0x66, 0x99); - err += sn9c102_write_reg(cam, 0x05, 0x9A); - err += sn9c102_write_reg(cam, 0x00, 0x9B); - err += sn9c102_write_reg(cam, 0x04, 0x9C); - err += sn9c102_write_reg(cam, 0x00, 0x9D); - err += sn9c102_write_reg(cam, 0x08, 0x9E); - err += sn9c102_write_reg(cam, 0x00, 0x9F); - err += sn9c102_write_reg(cam, 0x2D, 0xC0); - err += sn9c102_write_reg(cam, 0x2D, 0xC1); - err += sn9c102_write_reg(cam, 0x3A, 0xC2); - err += sn9c102_write_reg(cam, 0x05, 0xC3); - err += sn9c102_write_reg(cam, 0x04, 0xC4); - err += sn9c102_write_reg(cam, 0x3F, 0xC5); - err += sn9c102_write_reg(cam, 0x00, 0xC6); - err += sn9c102_write_reg(cam, 0x00, 0xC7); - err += sn9c102_write_reg(cam, 0x50, 0xC8); - err += sn9c102_write_reg(cam, 0x3C, 0xC9); - err += sn9c102_write_reg(cam, 0x28, 0xCA); - err += sn9c102_write_reg(cam, 0xD8, 0xCB); - err += sn9c102_write_reg(cam, 0x14, 0xCC); - err += sn9c102_write_reg(cam, 0xEC, 0xCD); - err += sn9c102_write_reg(cam, 0x32, 0xCE); - err += sn9c102_write_reg(cam, 0xDD, 0xCF); - err += sn9c102_write_reg(cam, 0x32, 0xD0); - err += sn9c102_write_reg(cam, 0xDD, 0xD1); - err += sn9c102_write_reg(cam, 0x6A, 0xD2); - err += sn9c102_write_reg(cam, 0x50, 0xD3); - err += sn9c102_write_reg(cam, 0x00, 0xD4); - err += sn9c102_write_reg(cam, 0x00, 0xD5); - err += sn9c102_write_reg(cam, 0x00, 0xD6); + err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x03, 0x10}, + {0x08, 0x14}, {0x20, 0x17}, + {0x8b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); err += sn9c102_i2c_write(cam, 0x12, 0x80); err += sn9c102_i2c_write(cam, 0x11, 0x09); @@ -572,13 +496,11 @@ static struct sn9c102_sensor ov7660 = { int sn9c102_probe_ov7660(struct sn9c102_device* cam) { - int pid, ver, err = 0; + int pid, ver, err; - err += sn9c102_write_reg(cam, 0x01, 0xf1); - err += sn9c102_write_reg(cam, 0x00, 0xf1); - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); + err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c index 8d79a5f..6715196 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas106b.c +++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -23,19 +23,13 @@ #include #include "sn9c102_sensor.h" -static struct sn9c102_sensor pas106b; - - static int pas106b_init(struct sn9c102_device* cam) { int err = 0; - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x20, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x20, 0x19}, {0x09, 0x18}); err += sn9c102_i2c_write(cam, 0x02, 0x0c); err += sn9c102_i2c_write(cam, 0x05, 0x5a); @@ -172,7 +166,7 @@ static int pas106b_set_pix_format(struct static struct sn9c102_sensor pas106b = { .name = "PAS106B", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -279,16 +273,17 @@ static struct sn9c102_sensor pas106b = { int sn9c102_probe_pas106b(struct sn9c102_device* cam) { - int r0 = 0, r1 = 0, err = 0; + int r0 = 0, r1 = 0, err; unsigned int pid = 0; /* Minimal initialization to enable the I2C communication NOTE: do NOT change the values! */ - err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ - err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ - err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ + err = sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* sensor power down */ + {0x00, 0x01}, /* sensor power on */ + {0x28, 0x17});/* sensor clock 24 MHz */ if (err) return -EIO; diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c index 7894f01..c1b8d6b 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c +++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -28,9 +28,6 @@ #include #include "sn9c102_sensor.h" -static struct sn9c102_sensor pas202bcb; - - static int pas202bcb_init(struct sn9c102_device* cam) { int err = 0; @@ -38,47 +35,29 @@ static int pas202bcb_init(struct sn9c102 switch (sn9c102_get_bridge(cam)) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x20, 0x17}, {0x30, 0x19}, + {0x09, 0x18}); break; case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, 0x00, 0x02); - err += sn9c102_write_reg(cam, 0x00, 0x03); - err += sn9c102_write_reg(cam, 0x1a, 0x04); - err += sn9c102_write_reg(cam, 0x20, 0x05); - err += sn9c102_write_reg(cam, 0x20, 0x06); - err += sn9c102_write_reg(cam, 0x20, 0x07); - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); - err += sn9c102_write_reg(cam, 0x02, 0x1c); - err += sn9c102_write_reg(cam, 0x03, 0x1d); - err += sn9c102_write_reg(cam, 0x0f, 0x1e); - err += sn9c102_write_reg(cam, 0x0c, 0x1f); - err += sn9c102_write_reg(cam, 0x00, 0x20); - err += sn9c102_write_reg(cam, 0x10, 0x21); - err += sn9c102_write_reg(cam, 0x20, 0x22); - err += sn9c102_write_reg(cam, 0x30, 0x23); - err += sn9c102_write_reg(cam, 0x40, 0x24); - err += sn9c102_write_reg(cam, 0x50, 0x25); - err += sn9c102_write_reg(cam, 0x60, 0x26); - err += sn9c102_write_reg(cam, 0x70, 0x27); - err += sn9c102_write_reg(cam, 0x80, 0x28); - err += sn9c102_write_reg(cam, 0x90, 0x29); - err += sn9c102_write_reg(cam, 0xa0, 0x2a); - err += sn9c102_write_reg(cam, 0xb0, 0x2b); - err += sn9c102_write_reg(cam, 0xc0, 0x2c); - err += sn9c102_write_reg(cam, 0xd0, 0x2d); - err += sn9c102_write_reg(cam, 0xe0, 0x2e); - err += sn9c102_write_reg(cam, 0xf0, 0x2f); - err += sn9c102_write_reg(cam, 0xff, 0x30); + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x20, 0x05}, {0x20, 0x06}, + {0x20, 0x07}, {0x00, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x20, 0x17}, {0x30, 0x19}, + {0x09, 0x18}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); break; default: break; @@ -328,15 +307,15 @@ int sn9c102_probe_pas202bcb(struct sn9c1 switch (sn9c102_get_bridge(cam)) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - err += sn9c102_write_reg(cam, 0x01, 0x01); /* power down */ - err += sn9c102_write_reg(cam, 0x40, 0x01); /* power on */ - err += sn9c102_write_reg(cam, 0x28, 0x17); /* clock 24 MHz */ + err = sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* power down */ + {0x40, 0x01}, /* power on */ + {0x28, 0x17});/* clock 24 MHz */ break; case BRIDGE_SN9C103: /* do _not_ change anything! */ - err += sn9c102_write_reg(cam, 0x09, 0x01); - err += sn9c102_write_reg(cam, 0x44, 0x01); - err += sn9c102_write_reg(cam, 0x44, 0x02); - err += sn9c102_write_reg(cam, 0x29, 0x17); + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, + {0x44, 0x01}, {0x44, 0x02}, + {0x29, 0x17}); break; default: break; diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h index 05f2942..1bbf64c 100644 --- a/drivers/media/video/sn9c102/sn9c102_sensor.h +++ b/drivers/media/video/sn9c102/sn9c102_sensor.h @@ -114,9 +114,17 @@ extern int sn9c102_i2c_write(struct sn9c extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); /* I/O on registers in the bridge. Could be used by the sensor methods too */ -extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index); -extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); +extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); +extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], + int count); +/* + * Write multiple registers with constant values. For example: + * sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); + */ +#define sn9c102_write_const_regs(device, data...) \ + ({ const static u8 _data[][2] = {data}; \ + sn9c102_write_regs(device, _data, ARRAY_SIZE(_data)); }) /*****************************************************************************/ diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c index 90023ad..0e7ec86 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -22,21 +22,14 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor tas5110c1b; - - static int tas5110c1b_init(struct sn9c102_device* cam) { int err = 0; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x44, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x0a, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x06, 0x18); - err += sn9c102_write_reg(cam, 0xfb, 0x19); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, + {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); err += sn9c102_i2c_write(cam, 0xc0, 0x80); @@ -98,7 +91,7 @@ static int tas5110c1b_set_pix_format(str static struct sn9c102_sensor tas5110c1b = { .name = "TAS5110C1B", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, @@ -146,7 +139,6 @@ int sn9c102_probe_tas5110c1b(struct sn9c const struct usb_device_id tas5110c1b_id_table[] = { { USB_DEVICE(0x0c45, 0x6001), }, { USB_DEVICE(0x0c45, 0x6005), }, - { USB_DEVICE(0x0c45, 0x6007), }, { USB_DEVICE(0x0c45, 0x60ab), }, { } }; diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c new file mode 100644 index 0000000..83a39e8 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_tas5110d.c @@ -0,0 +1,118 @@ +/*************************************************************************** + * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static int tas5110d_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); + + err += sn9c102_i2c_write(cam, 0x9a, 0xca); + + return err; +} + + +static int tas5110d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + + return err; +} + + +static int tas5110d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x3b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static struct sn9c102_sensor tas5110d = { + .name = "TAS5110D", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x61, + .init = &tas5110d_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110d_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110d_set_pix_format +}; + + +int sn9c102_probe_tas5110d(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110d_id_table[] = { + { USB_DEVICE(0x0c45, 0x6007), }, + { } + }; + + if (!sn9c102_match_id(cam, tas5110d_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110d); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c index cb1b318..5040650 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -22,21 +22,14 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor tas5130d1b; - - static int tas5130d1b_init(struct sn9c102_device* cam) { - int err = 0; + int err; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x04, 0x01); - err += sn9c102_write_reg(cam, 0x01, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x07, 0x18); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, + {0x04, 0x01}, {0x01, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x60, 0x17}, {0x07, 0x18}); return err; } @@ -99,7 +92,7 @@ static int tas5130d1b_set_pix_format(str static struct sn9c102_sensor tas5130d1b = { .name = "TAS5130D1B", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index d1ccc06..4322580 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -45,7 +45,6 @@ #include #include #include #include -#include #include #include diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c index 027c8a0..1a1bef0 100644 --- a/drivers/media/video/tda8290.c +++ b/drivers/media/video/tda8290.c @@ -192,14 +192,52 @@ static struct tda827xa_data tda827xa_ana { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} /* End */ }; +static void tda827xa_lna_gain(struct i2c_client *c, int high) +{ + struct tuner *t = i2c_get_clientdata(c); + unsigned char buf[] = {0x22, 0x01}; + int arg; + struct i2c_msg msg = {.addr = c->addr, .flags = 0, .buf = buf, .len = sizeof(buf)}; + if (t->config) { + if (high) + tuner_dbg("setting LNA to high gain\n"); + else + tuner_dbg("setting LNA to low gain\n"); + } + switch (t->config) { + case 0: /* no LNA */ + break; + case 1: /* switch is GPIO 0 of tda8290 */ + case 2: + /* turn Vsync on */ + if (t->std & V4L2_STD_MN) + arg = 1; + else + arg = 0; + if (t->tuner_callback) + t->tuner_callback(c->adapter->algo_data, 1, arg); + buf[1] = high ? 0 : 1; + if (t->config == 2) + buf[1] = high ? 1 : 0; + i2c_transfer(c->adapter, &msg, 1); + break; + case 3: /* switch with GPIO of saa713x */ + if (t->tuner_callback) + t->tuner_callback(c->adapter->algo_data, 0, high); + break; + } +} + static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) { - unsigned char tuner_reg[14]; - unsigned char reg2[2]; + unsigned char tuner_reg[11]; u32 N; int i; struct tuner *t = i2c_get_clientdata(c); - struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0}; + struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0, .buf = tuner_reg}; + + tda827xa_lna_gain( c, 1); + msleep(10); if (t->mode == V4L2_TUNER_RADIO) freq = freq / 1000; @@ -222,48 +260,58 @@ static void tda827xa_tune(struct i2c_cli tuner_reg[5] = (tda827xa_analog[i].spd << 5) + (tda827xa_analog[i].svco << 3) + tda827xa_analog[i].sbs; tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4); - tuner_reg[7] = 0x0c; + tuner_reg[7] = 0x1c; tuner_reg[8] = 4; tuner_reg[9] = 0x20; - tuner_reg[10] = 0xff; - tuner_reg[11] = 0xe0; - tuner_reg[12] = 0; - tuner_reg[13] = 0x39 + (t->tda827x_lpsel << 1); + tuner_reg[10] = 0x00; + msg.len = 11; + i2c_transfer(c->adapter, &msg, 1); - msg.buf = tuner_reg; - msg.len = 14; + tuner_reg[0] = 0x90; + tuner_reg[1] = 0xff; + tuner_reg[2] = 0xe0; + tuner_reg[3] = 0; + tuner_reg[4] = 0x99 + (t->tda827x_lpsel << 1); + msg.len = 5; i2c_transfer(c->adapter, &msg, 1); - msg.buf= reg2; + tuner_reg[0] = 0xa0; + tuner_reg[1] = 0xc0; msg.len = 2; - reg2[0] = 0x60; - reg2[1] = 0x3c; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xa0; - reg2[1] = 0xc0; + tuner_reg[0] = 0x30; + tuner_reg[1] = 0x10 + tda827xa_analog[i].scr; i2c_transfer(c->adapter, &msg, 1); - msleep(2); - reg2[0] = 0x30; - reg2[1] = 0x10 + tda827xa_analog[i].scr; + msg.flags = I2C_M_RD; + i2c_transfer(c->adapter, &msg, 1); + msg.flags = 0; + tuner_reg[1] >>= 4; + tuner_dbg("AGC2 gain is: %d\n", tuner_reg[1]); + if (tuner_reg[1] < 1) + tda827xa_lna_gain( c, 0); + + msleep(100); + tuner_reg[0] = 0x60; + tuner_reg[1] = 0x3c; i2c_transfer(c->adapter, &msg, 1); - msleep(550); - reg2[0] = 0x50; - reg2[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); + msleep(163); + tuner_reg[0] = 0x50; + tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0x80; - reg2[1] = 0x28; + tuner_reg[0] = 0x80; + tuner_reg[1] = 0x28; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xb0; - reg2[1] = 0x01; + tuner_reg[0] = 0xb0; + tuner_reg[1] = 0x01; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xc0; - reg2[1] = 0x19 + (t->tda827x_lpsel << 1); + tuner_reg[0] = 0xc0; + tuner_reg[1] = 0x19 + (t->tda827x_lpsel << 1); i2c_transfer(c->adapter, &msg, 1); } @@ -319,7 +367,9 @@ static int tda8290_tune(struct i2c_clien unsigned char addr_pll_stat = 0x1b; unsigned char adc_sat, agc_stat, pll_stat; + int i; + tuner_dbg("tda827xa config is 0x%02x\n", t->config); i2c_master_send(c, easy_mode, 2); i2c_master_send(c, agc_out_on, 2); i2c_master_send(c, soft_reset, 2); @@ -340,17 +390,22 @@ static int tda8290_tune(struct i2c_clien tda827xa_tune(c, ifc, freq); else tda827x_tune(c, ifc, freq); + for (i = 0; i < 3; i++) { + i2c_master_send(c, &addr_pll_stat, 1); + i2c_master_recv(c, &pll_stat, 1); + if (pll_stat & 0x80) { + i2c_master_send(c, &addr_adc_sat, 1); + i2c_master_recv(c, &adc_sat, 1); + i2c_master_send(c, &addr_agc_stat, 1); + i2c_master_recv(c, &agc_stat, 1); + tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); + break; + } else { + tuner_dbg("tda8290 not locked, no signal?\n"); + msleep(100); + } + } /* adjust headroom resp. gain */ - i2c_master_send(c, &addr_adc_sat, 1); - i2c_master_recv(c, &adc_sat, 1); - i2c_master_send(c, &addr_agc_stat, 1); - i2c_master_recv(c, &agc_stat, 1); - i2c_master_send(c, &addr_pll_stat, 1); - i2c_master_recv(c, &pll_stat, 1); - if (pll_stat & 0x80) - tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); - else - tuner_dbg("tda8290 not locked, no signal?\n"); if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) { tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n", agc_stat, adc_sat, pll_stat & 0x80); @@ -407,7 +462,6 @@ static void set_audio(struct tuner *t) char* mode; t->tda827x_lpsel = 0; - mode = "xx"; if (t->std & V4L2_STD_MN) { t->sgIF = 92; t->tda8290_easy_mode = 0x01; @@ -437,8 +491,12 @@ static void set_audio(struct tuner *t) t->sgIF = 20; t->tda8290_easy_mode = 0x40; mode = "LC"; + } else { + t->sgIF = 124; + t->tda8290_easy_mode = 0x10; + mode = "xx"; } - tuner_dbg("setting tda8290 to system %s\n", mode); + tuner_dbg("setting tda8290 to system %s\n", mode); } static void set_tv_freq(struct i2c_client *c, unsigned int freq) @@ -487,11 +545,16 @@ static void standby(struct i2c_client *c static void tda8290_init_if(struct i2c_client *c) { + struct tuner *t = i2c_get_clientdata(c); unsigned char set_VS[] = { 0x30, 0x6F }; + unsigned char set_GP00_CF[] = { 0x20, 0x01 }; unsigned char set_GP01_CF[] = { 0x20, 0x0B }; + if ((t->config == 1) || (t->config == 2)) + i2c_master_send(c, set_GP00_CF, 2); + else + i2c_master_send(c, set_GP01_CF, 2); i2c_master_send(c, set_VS, 2); - i2c_master_send(c, set_GP01_CF, 2); } static void tda8290_init_tuner(struct i2c_client *c) @@ -576,6 +639,7 @@ int tda8290_init(struct i2c_client *c) t->has_signal = has_signal; t->standby = standby; t->tda827x_lpsel = 0; + t->mode = V4L2_TUNER_ANALOG_TV; tda8290_init_tuner(c); tda8290_init_if(c); diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 00f0e8b..d110441 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -27,7 +27,6 @@ #include #include #include #include -#include #include diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 15dbc6b..505591a 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -144,7 +144,8 @@ static void set_freq(struct i2c_client * } static void set_type(struct i2c_client *c, unsigned int type, - unsigned int new_mode_mask) + unsigned int new_mode_mask, unsigned int new_config, + int (*tuner_callback) (void *dev, int command,int arg)) { struct tuner *t = i2c_get_clientdata(c); unsigned char buffer[4]; @@ -159,15 +160,20 @@ static void set_type(struct i2c_client * return; } + t->type = type; + t->config = new_config; + if (tuner_callback != NULL) { + tuner_dbg("defining GPIO callback\n"); + t->tuner_callback = tuner_callback; + } + /* This code detects calls by card attach_inform */ if (NULL == t->i2c.dev.driver) { tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr); - t->type=type; return; } - t->type = type; switch (t->type) { case TUNER_MT2032: microtune_init(c); @@ -234,10 +240,11 @@ static void set_addr(struct i2c_client * tuner_dbg("set addr for type %i\n", t->type); - if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET && - (t->mode_mask & tun_setup->mode_mask)) || - tun_setup->addr == c->addr)) { - set_type(c, tun_setup->type, tun_setup->mode_mask); + if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && + (t->mode_mask & tun_setup->mode_mask))) || + (tun_setup->addr == c->addr)) { + set_type(c, tun_setup->type, tun_setup->mode_mask, + tun_setup->config, tun_setup->tuner_callback); } } @@ -496,7 +503,7 @@ static int tuner_attach(struct i2c_adapt register_client: tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name); i2c_attach_client (&t->i2c); - set_type (&t->i2c,t->type, t->mode_mask); + set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback); return 0; } @@ -576,10 +583,11 @@ static int tuner_command(struct i2c_clie switch (cmd) { /* --- configuration --- */ case TUNER_SET_TYPE_ADDR: - tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n", + tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", ((struct tuner_setup *)arg)->type, ((struct tuner_setup *)arg)->addr, - ((struct tuner_setup *)arg)->mode_mask); + ((struct tuner_setup *)arg)->mode_mask, + ((struct tuner_setup *)arg)->config); set_addr(client, (struct tuner_setup *)arg); break; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index d506dfa..a2da5d2 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -25,7 +25,6 @@ #include #include #include #include -#include #include #include #include @@ -33,6 +32,7 @@ #include #include #include +#include #include @@ -1775,6 +1775,9 @@ static int chip_command(struct i2c_clien /* the thread will call checkmode() later */ } break; + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0); } return 0; } diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 4e7c1fa..a1136da 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -163,7 +163,7 @@ hauppauge_tuner[] = /* 60-69 */ { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, { TUNER_ABSENT, "LG M001D MK3"}, - { TUNER_ABSENT, "LG S701D MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, { TUNER_ABSENT, "LG M701D MK3"}, { TUNER_ABSENT, "Temic 4146FM5"}, { TUNER_ABSENT, "Temic 4136FY5"}, @@ -229,6 +229,36 @@ hauppauge_tuner[] = /* 120-129 */ { TUNER_ABSENT, "Xceive XC3028"}, { TUNER_ABSENT, "Philips FQ1216LME MK5"}, + { TUNER_ABSENT, "Philips FQD1216LME"}, + { TUNER_ABSENT, "Conexant CX24118A"}, + { TUNER_ABSENT, "TCL DMF11WIP"}, + { TUNER_ABSENT, "TCL MFNM05_4H_E"}, + { TUNER_ABSENT, "TCL MNM05_4H_E"}, + { TUNER_ABSENT, "TCL MPE05_2H_E"}, + { TUNER_ABSENT, "TCL MQNM05_4_U"}, + { TUNER_ABSENT, "TCL M2523_5NH_E"}, + /* 130-139 */ + { TUNER_ABSENT, "TCL M2523_3DBH_E"}, + { TUNER_ABSENT, "TCL M2523_3DIH_E"}, + { TUNER_ABSENT, "TCL MFPE05_2_U"}, + { TUNER_ABSENT, "Philips FMD1216MEX"}, + { TUNER_ABSENT, "Philips FRH2036B"}, + { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, + { TUNER_ABSENT, "MaxLinear MXL5005"}, + { TUNER_ABSENT, "MaxLinear MXL5003"}, + { TUNER_ABSENT, "Xceive XC2028"}, + { TUNER_ABSENT, "Microtune MT2131"}, + /* 140-149 */ + { TUNER_ABSENT, "Philips 8275A_8295"}, + { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, + { TUNER_ABSENT, "Microtune MT2266"}, + { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, + { TUNER_ABSENT, "LG TAPQ_H702F"}, + { TUNER_ABSENT, "TCL M09WPP_4N_E"}, + { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, + { TUNER_ABSENT, "Philips 18271_8295"}, }; static struct HAUPPAUGE_AUDIOIC @@ -280,11 +310,16 @@ audioIC[] = {AUDIO_CHIP_INTERNAL, "CX883"}, {AUDIO_CHIP_INTERNAL, "CX882"}, {AUDIO_CHIP_INTERNAL, "CX25840"}, - /* 35-38 */ + /* 35-39 */ {AUDIO_CHIP_INTERNAL, "CX25841"}, {AUDIO_CHIP_INTERNAL, "CX25842"}, {AUDIO_CHIP_INTERNAL, "CX25843"}, {AUDIO_CHIP_INTERNAL, "CX23418"}, + {AUDIO_CHIP_INTERNAL, "CX23885"}, + /* 40-42 */ + {AUDIO_CHIP_INTERNAL, "CX23888"}, + {AUDIO_CHIP_INTERNAL, "SAA7131"}, + {AUDIO_CHIP_INTERNAL, "CX23887"}, }; /* This list is supplied by Hauppauge. Thanks! */ @@ -301,8 +336,10 @@ static const char *decoderIC[] = { "CX880", "CX881", "CX883", "SAA7111", "SAA7113", /* 25-29 */ "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", - /* 30-31 */ - "CX25843", "CX23418", + /* 30-34 */ + "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", + /* 35-37 */ + "SAA7131", "CX25837", "CX23887" }; static int hasRadioTuner(int tunerType) diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index 28d1133..0b2a961 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -27,6 +27,7 @@ #include #include #include #include +#include #include // --------------------- read registers functions define ----------------------- @@ -179,6 +180,9 @@ #ifdef CONFIG_VIDEO_ADV_DEBUG } #endif + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64031A, 0); + default: break; } diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index fe38224..401bd21 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -26,6 +26,7 @@ #include #include #include #include +#include #include MODULE_DESCRIPTION("uPD64083 driver"); @@ -155,6 +156,10 @@ #ifdef CONFIG_VIDEO_ADV_DEBUG break; } #endif + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64083, 0); + default: break; } diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c index d34d8c8..687f026 100644 --- a/drivers/media/video/usbvideo/usbvideo.c +++ b/drivers/media/video/usbvideo/usbvideo.c @@ -628,24 +628,21 @@ #endif /* ******************************************************************** */ /* XXX: this piece of crap really wants some error handling.. */ -static void usbvideo_ClientIncModCount(struct uvd *uvd) +static int usbvideo_ClientIncModCount(struct uvd *uvd) { if (uvd == NULL) { err("%s: uvd == NULL", __FUNCTION__); - return; + return -EINVAL; } if (uvd->handle == NULL) { err("%s: uvd->handle == NULL", __FUNCTION__); - return; - } - if (uvd->handle->md_module == NULL) { - err("%s: uvd->handle->md_module == NULL", __FUNCTION__); - return; + return -EINVAL; } if (!try_module_get(uvd->handle->md_module)) { err("%s: try_module_get() == 0", __FUNCTION__); - return; + return -ENODEV; } + return 0; } static void usbvideo_ClientDecModCount(struct uvd *uvd) @@ -712,8 +709,6 @@ int usbvideo_register( cams->num_cameras = num_cams; cams->cam = (struct uvd *) &cams[1]; cams->md_module = md; - if (cams->md_module == NULL) - warn("%s: module == NULL!", __FUNCTION__); mutex_init(&cams->lock); /* to 1 == available */ for (i = 0; i < num_cams; i++) { @@ -1119,7 +1114,8 @@ static int usbvideo_v4l_open(struct inod if (uvd->debug > 1) info("%s($%p)", __FUNCTION__, dev); - usbvideo_ClientIncModCount(uvd); + if (0 < usbvideo_ClientIncModCount(uvd)) + return -ENODEV; mutex_lock(&uvd->lock); if (uvd->user) { diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index a40e583..13f69fe 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -1,6 +1,6 @@ /* - * USBVISION.H - * usbvision header file + * usbvision-cards.c + * usbvision cards definition file * * Copyright (c) 1999-2005 Joerg Heckenbach * @@ -28,129 +28,1060 @@ #include #include #include #include "usbvision.h" +#include "usbvision-cards.h" /* Supported Devices: A table for usbvision.c*/ struct usbvision_device_data_st usbvision_device_data[] = { - {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, - {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, - {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"}, - {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, - {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"}, - {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"}, - {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"}, - {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"}, - {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"}, - {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"}, - {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"}, - {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, - {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"}, - {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"}, - {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"}, - {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, - {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"}, - {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "}, - {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"}, - {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, - {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, - {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"}, - {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"}, - {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"}, - {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"}, - {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"}, - {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"}, - {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) 4D23"}, - {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"}, - {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"}, - {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"}, - {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"}, - {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"}, - {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"}, - {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"}, - {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"}, - {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"}, - {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"}, - {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"}, - {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"}, - {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, - {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"}, - {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"}, - {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, - {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, - {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"}, - {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"}, - {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"}, - {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, - {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"}, - {0x2400, 0x4200, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, - {} /* Terminating entry */ + [XANBOO] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 4, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Xanboo", + }, + [BELKIN_VIDEOBUS_II] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Belkin USB VideoBus II Adapter", + }, + [BELKIN_VIDEOBUS] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Belkin Components USB VideoBus", + }, + [BELKIN_USB_VIDEOBUS_II] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Belkin USB VideoBus II", + }, + [ECHOFX_INTERVIEW_LITE] = { + .Interface = 0, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "echoFX InterView Lite", + }, + [USBGEAR_USBG_V1] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "USBGear USBG-V1 resp. HAMA USB", + }, + [D_LINK_V100] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 4, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "D-Link V100", + }, + [X10_USB_CAMERA] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "X10 USB Camera", + }, + [HPG_WINTV_LIVE_PAL_BG] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Live (PAL B/G)", + }, + [HPG_WINTV_LIVE_PRO_NTSC_MN] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Live Pro (NTSC M/N)", + }, + [ZORAN_PMD_NOGATECH] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 2, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan", + }, + [NOGATECH_USB_TV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = 20, + .ModelString = "Nogatech USB-TV (NTSC) FM", + }, + [PNY_USB_TV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = 20, + .ModelString = "PNY USB-TV (NTSC) FM", + }, + [PV_PLAYTV_USB_PRO_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "PixelView PlayTv-USB PRO (PAL) FM", + }, + [ZT_721] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "ZTV ZT-721 2.4GHz USB A/V Receiver", + }, + [HPG_WINTV_NTSC_MN] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = 20, + .ModelString = "Hauppauge WinTV USB (NTSC M/N)", + }, + [HPG_WINTV_PAL_BG] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL B/G)", + }, + [HPG_WINTV_PAL_I] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL I)", + }, + [HPG_WINTV_PAL_SECAM_L] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL/SECAM L)", + }, + [HPG_WINTV_PAL_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL D/K)", + }, + [HPG_WINTV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (NTSC FM)", + }, + [HPG_WINTV_PAL_BG_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL B/G FM)", + }, + [HPG_WINTV_PAL_I_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL I FM)", + }, + [HPG_WINTV_PAL_D_K_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL D/K FM)", + }, + [HPG_WINTV_PRO_NTSC_MN] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N)", + }, + [HPG_WINTV_PRO_NTSC_MN_V2] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V2", + }, + [HPG_WINTV_PRO_PAL] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)", + }, + [HPG_WINTV_PRO_NTSC_MN_V3] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V3", + }, + [HPG_WINTV_PRO_PAL_BG] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G)", + }, + [HPG_WINTV_PRO_PAL_I] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I)", + }, + [HPG_WINTV_PRO_PAL_SECAM_L] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM L)", + }, + [HPG_WINTV_PRO_PAL_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL D/K)", + }, + [HPG_WINTV_PRO_PAL_SECAM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)", + }, + [HPG_WINTV_PRO_PAL_SECAM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2", + }, + [HPG_WINTV_PRO_PAL_BG_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_ALPS_TSBE1_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G) V2", + }, + [HPG_WINTV_PRO_PAL_BG_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_ALPS_TSBE1_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G,D/K)", + }, + [HPG_WINTV_PRO_PAL_I_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I,D/K)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM)", + }, + [HPG_WINTV_PRO_PAL_BG_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_I_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I FM)", + }, + [HPG_WINTV_PRO_PAL_D_K_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL D/K FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM_V2] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2", + }, + [CAMTEL_TVB330] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 5, + .Y_Offset = 5, + .ModelString = "Camtel Technology USB TV Genie Pro FM Model TVB330", + }, + [DIGITAL_VIDEO_CREATOR_I] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Digital Video Creator I", + }, + [GLOBAL_VILLAGE_GV_007_NTSC] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 82, + .Y_Offset = 20, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Global Village GV-007 (NTSC)", + }, + [DAZZLE_DVC_50_REV_1_NTSC] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)", + }, + [DAZZLE_DVC_80_REV_1_PAL] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)", + }, + [DAZZLE_DVC_90_REV_1_SECAM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)", + }, + [ESKAPE_LABS_MYTV2GO] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Eskape Labs MyTV2Go", + }, + [PINNA_PCTV_USB_PAL] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 0, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4066FY5_PAL_I, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Pinnacle Studio PCTV USB (PAL)", + }, + [PINNA_PCTV_USB_SECAM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Pinnacle Studio PCTV USB (SECAM)", + }, + [PINNA_PCTV_USB_PAL_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 128, + .Y_Offset = 23, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM", + }, + [MIRO_PCTV_USB] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Miro PCTV USB", + }, + [PINNA_PCTV_USB_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM", + }, + [PINNA_PCTV_USB_PAL_FM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V2", + }, + [PINNA_PCTV_USB_NTSC_FM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4039FR5_NTSC, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM V2", + }, + [PINNA_PCTV_USB_PAL_FM_V3] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V3", + }, + [PINNA_LINX_VD_IN_CAB_NTSC] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio Linx Video input cable (NTSC)", + }, + [PINNA_LINX_VD_IN_CAB_PAL] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio Linx Video input cable (PAL)", + }, + [PINNA_PCTV_BUNGEE_PAL_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle PCTV Bungee USB (PAL) FM", + }, + [HPG_WINTV] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTv-USB", + }, }; +const int usbvision_device_data_size=ARRAY_SIZE(usbvision_device_data); /* Supported Devices */ struct usb_device_id usbvision_table [] = { - { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */ - { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */ - { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */ - { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */ - { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */ - { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */ - { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */ - { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */ - { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */ - { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */ - { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */ - { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */ - { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */ - { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */ - { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTv-USB USA */ - { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTv-USB */ - { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTv-USB UK */ - { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTv-USB France */ - { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */ - { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */ - { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */ - { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */ - { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */ - { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/ - { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */ - { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/ - { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */ - { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTv-USB France 4D23*/ - { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTv-USB Model 40209 rev B234 */ - { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */ - { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */ - { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */ - { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */ - { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */ - { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */ - { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */ - { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */ - { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTv-USB Model 40219 Rev E189 */ - { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */ - { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */ - { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */ - { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ - { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */ - { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */ - { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */ - { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ - { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */ - { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */ - - { } /* Terminating entry */ + { USB_DEVICE(0x0a6f, 0x0400), .driver_info=XANBOO }, + { USB_DEVICE(0x050d, 0x0106), .driver_info=BELKIN_VIDEOBUS_II }, + { USB_DEVICE(0x050d, 0x0207), .driver_info=BELKIN_VIDEOBUS }, + { USB_DEVICE(0x050d, 0x0208), .driver_info=BELKIN_USB_VIDEOBUS_II }, + { USB_DEVICE(0x0571, 0x0002), .driver_info=ECHOFX_INTERVIEW_LITE }, + { USB_DEVICE(0x0573, 0x0003), .driver_info=USBGEAR_USBG_V1 }, + { USB_DEVICE(0x0573, 0x0400), .driver_info=D_LINK_V100 }, + { USB_DEVICE(0x0573, 0x2000), .driver_info=X10_USB_CAMERA }, + { USB_DEVICE(0x0573, 0x2d00), .driver_info=HPG_WINTV_LIVE_PAL_BG }, + { USB_DEVICE(0x0573, 0x2d01), .driver_info=HPG_WINTV_LIVE_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x2101), .driver_info=ZORAN_PMD_NOGATECH }, + { USB_DEVICE(0x0573, 0x4100), .driver_info=NOGATECH_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4110), .driver_info=PNY_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4450), .driver_info=PV_PLAYTV_USB_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4550), .driver_info=ZT_721 }, + { USB_DEVICE(0x0573, 0x4d00), .driver_info=HPG_WINTV_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d01), .driver_info=HPG_WINTV_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d02), .driver_info=HPG_WINTV_PAL_I }, + { USB_DEVICE(0x0573, 0x4d03), .driver_info=HPG_WINTV_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d04), .driver_info=HPG_WINTV_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d10), .driver_info=HPG_WINTV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4d11), .driver_info=HPG_WINTV_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d12), .driver_info=HPG_WINTV_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d14), .driver_info=HPG_WINTV_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d2a), .driver_info=HPG_WINTV_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d2b), .driver_info=HPG_WINTV_PRO_NTSC_MN_V2 }, + { USB_DEVICE(0x0573, 0x4d2c), .driver_info=HPG_WINTV_PRO_PAL }, + { USB_DEVICE(0x0573, 0x4d20), .driver_info=HPG_WINTV_PRO_NTSC_MN_V3 }, + { USB_DEVICE(0x0573, 0x4d21), .driver_info=HPG_WINTV_PRO_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d22), .driver_info=HPG_WINTV_PRO_PAL_I }, + { USB_DEVICE(0x0573, 0x4d23), .driver_info=HPG_WINTV_PRO_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d24), .driver_info=HPG_WINTV_PRO_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d25), .driver_info=HPG_WINTV_PRO_PAL_SECAM }, + { USB_DEVICE(0x0573, 0x4d26), .driver_info=HPG_WINTV_PRO_PAL_SECAM_V2 }, + { USB_DEVICE(0x0573, 0x4d27), .driver_info=HPG_WINTV_PRO_PAL_BG_V2 }, + { USB_DEVICE(0x0573, 0x4d28), .driver_info=HPG_WINTV_PRO_PAL_BG_D_K }, + { USB_DEVICE(0x0573, 0x4d29), .driver_info=HPG_WINTV_PRO_PAL_I_D_K }, + { USB_DEVICE(0x0573, 0x4d30), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM }, + { USB_DEVICE(0x0573, 0x4d31), .driver_info=HPG_WINTV_PRO_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d32), .driver_info=HPG_WINTV_PRO_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d34), .driver_info=HPG_WINTV_PRO_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d35), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d36), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d37), .driver_info=HPG_WINTV_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d38), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM_V2 }, + { USB_DEVICE(0x0768, 0x0006), .driver_info=CAMTEL_TVB330 }, + { USB_DEVICE(0x07d0, 0x0001), .driver_info=DIGITAL_VIDEO_CREATOR_I }, + { USB_DEVICE(0x07d0, 0x0002), .driver_info=GLOBAL_VILLAGE_GV_007_NTSC }, + { USB_DEVICE(0x07d0, 0x0003), .driver_info=DAZZLE_DVC_50_REV_1_NTSC }, + { USB_DEVICE(0x07d0, 0x0004), .driver_info=DAZZLE_DVC_80_REV_1_PAL }, + { USB_DEVICE(0x07d0, 0x0005), .driver_info=DAZZLE_DVC_90_REV_1_SECAM }, + { USB_DEVICE(0x07f8, 0x9104), .driver_info=ESKAPE_LABS_MYTV2GO }, + { USB_DEVICE(0x2304, 0x010d), .driver_info=PINNA_PCTV_USB_PAL }, + { USB_DEVICE(0x2304, 0x0109), .driver_info=PINNA_PCTV_USB_SECAM }, + { USB_DEVICE(0x2304, 0x0110), .driver_info=PINNA_PCTV_USB_PAL_FM }, + { USB_DEVICE(0x2304, 0x0111), .driver_info=MIRO_PCTV_USB }, + { USB_DEVICE(0x2304, 0x0112), .driver_info=PINNA_PCTV_USB_NTSC_FM }, + { USB_DEVICE(0x2304, 0x0210), .driver_info=PINNA_PCTV_USB_PAL_FM_V2 }, + { USB_DEVICE(0x2304, 0x0212), .driver_info=PINNA_PCTV_USB_NTSC_FM_V2 }, + { USB_DEVICE(0x2304, 0x0214), .driver_info=PINNA_PCTV_USB_PAL_FM_V3 }, + { USB_DEVICE(0x2304, 0x0300), .driver_info=PINNA_LINX_VD_IN_CAB_NTSC }, + { USB_DEVICE(0x2304, 0x0301), .driver_info=PINNA_LINX_VD_IN_CAB_PAL }, + { USB_DEVICE(0x2304, 0x0419), .driver_info=PINNA_PCTV_BUNGEE_PAL_FM }, + { USB_DEVICE(0x2400, 0x4200), .driver_info=HPG_WINTV }, }; MODULE_DEVICE_TABLE (usb, usbvision_table); diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h new file mode 100644 index 0000000..512c5ce --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-cards.h @@ -0,0 +1,66 @@ +#define XANBOO 0 +#define BELKIN_VIDEOBUS_II 1 +#define BELKIN_VIDEOBUS 2 +#define BELKIN_USB_VIDEOBUS_II 3 +#define ECHOFX_INTERVIEW_LITE 4 +#define USBGEAR_USBG_V1 5 +#define D_LINK_V100 6 +#define X10_USB_CAMERA 7 +#define HPG_WINTV_LIVE_PAL_BG 8 +#define HPG_WINTV_LIVE_PRO_NTSC_MN 9 +#define ZORAN_PMD_NOGATECH 10 +#define NOGATECH_USB_TV_NTSC_FM 11 +#define PNY_USB_TV_NTSC_FM 12 +#define PV_PLAYTV_USB_PRO_PAL_FM 13 +#define ZT_721 14 +#define HPG_WINTV_NTSC_MN 15 +#define HPG_WINTV_PAL_BG 16 +#define HPG_WINTV_PAL_I 17 +#define HPG_WINTV_PAL_SECAM_L 18 +#define HPG_WINTV_PAL_D_K 19 +#define HPG_WINTV_NTSC_FM 20 +#define HPG_WINTV_PAL_BG_FM 21 +#define HPG_WINTV_PAL_I_FM 22 +#define HPG_WINTV_PAL_D_K_FM 23 +#define HPG_WINTV_PRO_NTSC_MN 24 +#define HPG_WINTV_PRO_NTSC_MN_V2 25 +#define HPG_WINTV_PRO_PAL 26 +#define HPG_WINTV_PRO_NTSC_MN_V3 27 +#define HPG_WINTV_PRO_PAL_BG 28 +#define HPG_WINTV_PRO_PAL_I 29 +#define HPG_WINTV_PRO_PAL_SECAM_L 30 +#define HPG_WINTV_PRO_PAL_D_K 31 +#define HPG_WINTV_PRO_PAL_SECAM 32 +#define HPG_WINTV_PRO_PAL_SECAM_V2 33 +#define HPG_WINTV_PRO_PAL_BG_V2 34 +#define HPG_WINTV_PRO_PAL_BG_D_K 35 +#define HPG_WINTV_PRO_PAL_I_D_K 36 +#define HPG_WINTV_PRO_NTSC_MN_FM 37 +#define HPG_WINTV_PRO_PAL_BG_FM 38 +#define HPG_WINTV_PRO_PAL_I_FM 39 +#define HPG_WINTV_PRO_PAL_D_K_FM 40 +#define HPG_WINTV_PRO_TEMIC_PAL_FM 41 +#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42 +#define HPG_WINTV_PRO_PAL_FM 43 +#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44 +#define CAMTEL_TVB330 45 +#define DIGITAL_VIDEO_CREATOR_I 46 +#define GLOBAL_VILLAGE_GV_007_NTSC 47 +#define DAZZLE_DVC_50_REV_1_NTSC 48 +#define DAZZLE_DVC_80_REV_1_PAL 49 +#define DAZZLE_DVC_90_REV_1_SECAM 50 +#define ESKAPE_LABS_MYTV2GO 51 +#define PINNA_PCTV_USB_PAL 52 +#define PINNA_PCTV_USB_SECAM 53 +#define PINNA_PCTV_USB_PAL_FM 54 +#define MIRO_PCTV_USB 55 +#define PINNA_PCTV_USB_NTSC_FM 56 +#define PINNA_PCTV_USB_PAL_FM_V2 57 +#define PINNA_PCTV_USB_NTSC_FM_V2 58 +#define PINNA_PCTV_USB_PAL_FM_V3 59 +#define PINNA_LINX_VD_IN_CAB_NTSC 60 +#define PINNA_LINX_VD_IN_CAB_PAL 61 +#define PINNA_PCTV_BUNGEE_PAL_FM 62 +#define HPG_WINTV 63 + +extern const int usbvision_device_data_size; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index f2154dc..bcb551a 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -2040,8 +2040,8 @@ int usbvision_set_input(struct usb_usbvi return 0; /* Set input format expected from decoder*/ - if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) { - value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff; + if (usbvision_device_data[usbvision->DevModel].Vin_Reg1_override) { + value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1; } else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) { /* SAA7113 uses 8 bit output */ value[0] = USBVISION_8_422_SYNC; @@ -2112,8 +2112,8 @@ int usbvision_set_input(struct usb_usbvi dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */ - if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){ - dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff; + if(usbvision_device_data[usbvision->DevModel].Dvi_yuv_override){ + dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv; } else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) { /* This changes as the fine sync control changes. Further investigation necessary */ @@ -2238,7 +2238,7 @@ static void call_usbvision_power_off(str PDEBUG(DBG_FUNC, ""); down_interruptible(&usbvision->lock); if(usbvision->user == 0) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); usbvision->initialized = 0; diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 609e1fd..025be55 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -1,8 +1,8 @@ /* - * I2C_ALGO_USB.C + * usbvision_i2c.c * i2c algorithm for USB-I2C Bridges * - * Copyright (c) 1999-2005 Joerg Heckenbach + * Copyright (c) 1999-2007 Joerg Heckenbach * Dwaine Garden * * This module is part of usbvision driver project. @@ -39,7 +39,6 @@ #include #include "usbvision.h" #define DBG_I2C 1<<0 -#define DBG_ALGO 1<<1 static int i2c_debug = 0; @@ -49,22 +48,22 @@ MODULE_PARM_DESC(i2c_debug, "enable debu #define PDEBUG(level, fmt, args...) \ if (i2c_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) -static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len); -static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len); static inline int try_write_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) { - void *data; + struct usb_usbvision *usbvision; int i, ret = -1; char buf[4]; - data = i2c_get_adapdata(i2c_adap); + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); buf[0] = 0x00; for (i = 0; i <= retries; i++) { - ret = (usbvision_i2c_write(data, addr, buf, 1)); + ret = (usbvision_i2c_write(usbvision, addr, buf, 1)); if (ret == 1) break; /* success! */ udelay(5); @@ -73,8 +72,8 @@ static inline int try_write_address(stru udelay(10); } if (i) { - PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr); - PDEBUG(DBG_ALGO,"Maybe there's no device at this address"); + PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C,"Maybe there's no device at this address"); } return ret; } @@ -82,13 +81,13 @@ static inline int try_write_address(stru static inline int try_read_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) { - void *data; + struct usb_usbvision *usbvision; int i, ret = -1; char buf[4]; - data = i2c_get_adapdata(i2c_adap); + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); for (i = 0; i <= retries; i++) { - ret = (usbvision_i2c_read(data, addr, buf, 1)); + ret = (usbvision_i2c_read(usbvision, addr, buf, 1)); if (ret == 1) break; /* success! */ udelay(5); @@ -97,8 +96,8 @@ static inline int try_read_address(struc udelay(10); } if (i) { - PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr); - PDEBUG(DBG_ALGO,"Maybe there's no device at this address"); + PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C,"Maybe there's no device at this address"); } return ret; } @@ -152,32 +151,32 @@ static inline int usb_find_address(struc } static int -usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { struct i2c_msg *pmsg; - void *data; + struct usb_usbvision *usbvision; int i, ret; unsigned char addr; - data = i2c_get_adapdata(i2c_adap); + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); for (i = 0; i < num; i++) { pmsg = &msgs[i]; ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); if (ret != 0) { - PDEBUG(DBG_ALGO,"got NAK from device, message #%d", i); + PDEBUG(DBG_I2C,"got NAK from device, message #%d", i); return (ret < 0) ? ret : -EREMOTEIO; } if (pmsg->flags & I2C_M_RD) { /* read bytes into buffer */ - ret = (usbvision_i2c_read(data, addr, pmsg->buf, pmsg->len)); + ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len)); if (ret < pmsg->len) { return (ret < 0) ? ret : -EREMOTEIO; } } else { /* write bytes from buffer */ - ret = (usbvision_i2c_write(data, addr, pmsg->buf, pmsg->len)); + ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len)); if (ret < pmsg->len) { return (ret < 0) ? ret : -EREMOTEIO; } @@ -191,7 +190,7 @@ static int algo_control(struct i2c_adapt return 0; } -static u32 usb_func(struct i2c_adapter *adap) +static u32 functionality(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; } @@ -199,11 +198,11 @@ static u32 usb_func(struct i2c_adapter * /* -----exported algorithm data: ------------------------------------- */ -static struct i2c_algorithm i2c_usb_algo = { - .master_xfer = usb_xfer, +static struct i2c_algorithm usbvision_algo = { + .master_xfer = usbvision_i2c_xfer, .smbus_xfer = NULL, .algo_control = algo_control, - .functionality = usb_func, + .functionality = functionality, }; @@ -213,41 +212,29 @@ static struct i2c_algorithm i2c_usb_algo static int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) { PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); - PDEBUG(DBG_ALGO, "ALGO debugging is enabled [i2c]"); + PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); /* register new adapter to i2c module... */ - adap->algo = &i2c_usb_algo; + adap->algo = &usbvision_algo; adap->timeout = 100; /* default values, should */ adap->retries = 3; /* be replaced by defines */ i2c_add_adapter(adap); - PDEBUG(DBG_ALGO,"i2c bus for %s registered", adap->name); - - return 0; -} - - -int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap) -{ - - i2c_del_adapter(adap); - - PDEBUG(DBG_ALGO,"i2c bus for %s unregistered", adap->name); + PDEBUG(DBG_I2C,"i2c bus for %s registered", adap->name); return 0; } - /* ----------------------------------------------------------------------- */ /* usbvision specific I2C functions */ /* ----------------------------------------------------------------------- */ static struct i2c_adapter i2c_adap_template; static struct i2c_client i2c_client_template; -int usbvision_init_i2c(struct usb_usbvision *usbvision) +int usbvision_i2c_register(struct usb_usbvision *usbvision) { memcpy(&usbvision->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); @@ -265,7 +252,7 @@ int usbvision_init_i2c(struct usb_usbvis usbvision->i2c_client.adapter = &usbvision->i2c_adap; if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { - printk(KERN_ERR "usbvision_init_i2c: can't write reg\n"); + printk(KERN_ERR "usbvision_register: can't write reg\n"); return -EBUSY; } @@ -287,6 +274,16 @@ #endif return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); } +int usbvision_i2c_unregister(struct usb_usbvision *usbvision) +{ + + i2c_del_adapter(&(usbvision->i2c_adap)); + + PDEBUG(DBG_I2C,"i2c bus for %s unregistered", usbvision->i2c_adap.name); + + return 0; +} + void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg) { @@ -300,19 +297,12 @@ static int attach_inform(struct i2c_clie usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); switch (client->addr << 1) { - case 0x43: - case 0x4b: - { - struct tuner_setup tun_setup; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = TUNER_TDA9887; - tun_setup.addr = client->addr; - - call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); - + case 0x42 << 1: + case 0x43 << 1: + case 0x4a << 1: + case 0x4b << 1: + PDEBUG(DBG_I2C,"attach_inform: tda9887 detected."); break; - } case 0x42: PDEBUG(DBG_I2C,"attach_inform: saa7114 detected."); break; @@ -480,7 +470,7 @@ static int usbvision_i2c_write_max4(stru return len; } -static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len) { char *bufPtr = buf; @@ -488,7 +478,6 @@ static int usbvision_i2c_write(void *dat int wrcount = 0; int count; int maxLen = 4; - struct usb_usbvision *usbvision = (struct usb_usbvision *) data; while (len > 0) { count = (len > maxLen) ? maxLen : len; @@ -503,14 +492,13 @@ static int usbvision_i2c_write(void *dat return wrcount; } -static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len) { char temp[4]; int retval, i; int rdcount = 0; int count; - struct usb_usbvision *usbvision = (struct usb_usbvision *) data; while (len > 0) { count = (len > 3) ? 4 : len; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 6fc1455..2167041 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -76,6 +76,7 @@ #include #endif #include "usbvision.h" +#include "usbvision-cards.h" #define DRIVER_AUTHOR "Joerg Heckenbach , Dwaine Garden " #define DRIVER_NAME "usbvision" @@ -150,7 +151,6 @@ static int PowerOnAtOpen = 1; // Set static int video_nr = -1; // Sequential Number of Video Device static int radio_nr = -1; // Sequential Number of Radio Device static int vbi_nr = -1; // Sequential Number of VBI Device -static char *CustomDevice=NULL; // Set as nothing.... // Grab parameters for the device driver @@ -161,7 +161,6 @@ module_param(PowerOnAtOpen, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); module_param(vbi_nr, int, 0444); -module_param(CustomDevice, charp, 0444); #else // Old Style MODULE_PARAM(isocMode, "i"); MODULE_PARM(video_debug, "i"); // Grab the Debug Mode of the device driver @@ -171,7 +170,6 @@ MODULE_PARM(SwitchSVideoInput, "i"); / MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) -MODULE_PARM(CustomDevice, "s"); // .... CustomDevice #endif MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); @@ -180,7 +178,6 @@ MODULE_PARM_DESC(PowerOnAtOpen, " Set th MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null"); // Misc stuff @@ -409,7 +406,7 @@ static int usbvision_v4l2_open(struct in down(&usbvision->lock); if (usbvision->power == 0) { usbvision_power_on(usbvision); - usbvision_init_i2c(usbvision); + usbvision_i2c_register(usbvision); } /* Send init sequence only once, it's large! */ @@ -431,7 +428,7 @@ static int usbvision_v4l2_open(struct in } else { if (PowerOnAtOpen) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); usbvision->initialized = 0; } @@ -1239,7 +1236,7 @@ static int usbvision_radio_open(struct i usbvision_reset_powerOffTimer(usbvision); if (usbvision->power == 0) { usbvision_power_on(usbvision); - usbvision_init_i2c(usbvision); + usbvision_i2c_register(usbvision); } } @@ -1261,7 +1258,7 @@ static int usbvision_radio_open(struct i if (errCode) { if (PowerOnAtOpen) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); usbvision->initialized = 0; } @@ -1744,8 +1741,8 @@ static void usbvision_configure_video(st model = usbvision->DevModel; usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24; - if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) { - usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff; + if (usbvision_device_data[usbvision->DevModel].Vin_Reg2_override) { + usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2; } else { usbvision->Vin_Reg2_Preset = 0; } @@ -1764,7 +1761,7 @@ static void usbvision_configure_video(st usbvision_audio_off(usbvision); //first switch off audio if (!PowerOnAtOpen) { usbvision_power_on(usbvision); //and then power up the noisy tuner - usbvision_init_i2c(usbvision); + usbvision_i2c_register(usbvision); } } @@ -1775,7 +1772,8 @@ static void usbvision_configure_video(st * if it looks like USBVISION video device * */ -static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid) +static int __devinit usbvision_probe(struct usb_interface *intf, + const struct usb_device_id *devid) { struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf)); struct usb_interface *uif; @@ -1786,25 +1784,17 @@ static int __devinit usbvision_probe(str int model,i; PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", - dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + dev->descriptor.idVendor, + dev->descriptor.idProduct, ifnum); - /* Is it an USBVISION video dev? */ - model = 0; - for(model = 0; usbvision_device_data[model].idVendor; model++) { - if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { - continue; - } - if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { - continue; - } - - printk(KERN_INFO "%s: %s found\n", __FUNCTION__, usbvision_device_data[model].ModelString); - break; + model = devid->driver_info; + if ( (model<0) || (model>=usbvision_device_data_size) ) { + PDEBUG(DBG_PROBE, "model out of bounds %d",model); + return -ENODEV; } + printk(KERN_INFO "%s: %s found\n", __FUNCTION__, + usbvision_device_data[model].ModelString); - if (usbvision_device_data[model].idVendor == 0) { - return -ENODEV; //no matching device - } if (usbvision_device_data[model].Interface >= 0) { interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0]; } @@ -1822,16 +1812,15 @@ static int __devinit usbvision_probe(str return -ENODEV; } - usb_get_dev(dev); - if ((usbvision = usbvision_alloc(dev)) == NULL) { err("%s: couldn't allocate USBVision struct", __FUNCTION__); return -ENOMEM; } + if (dev->descriptor.bNumConfigurations > 1) { usbvision->bridgeType = BRIDGE_NT1004; } - else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") { + else if (model == DAZZLE_DVC_90_REV_1_SECAM) { usbvision->bridgeType = BRIDGE_NT1005; } else { @@ -1920,7 +1909,7 @@ static void __devexit usbvision_disconne usbvision_stop_isoc(usbvision); if (usbvision->power) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); } usbvision->remove_pending = 1; // Now all ISO data will be ignored @@ -1951,124 +1940,6 @@ static struct usb_driver usbvision_drive }; /* - * customdevice_process() - * - * This procedure preprocesses CustomDevice parameter if any - * - */ -static void customdevice_process(void) -{ - usbvision_device_data[0]=usbvision_device_data[1]; - usbvision_table[0]=usbvision_table[1]; - - if(CustomDevice) - { - char *parse=CustomDevice; - - PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice); - - /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" - usbvision_device_data[0].idVendor; - usbvision_device_data[0].idProduct; - usbvision_device_data[0].Interface; - usbvision_device_data[0].Codec; - usbvision_device_data[0].VideoChannels; - usbvision_device_data[0].VideoNorm; - usbvision_device_data[0].AudioChannels; - usbvision_device_data[0].Radio; - usbvision_device_data[0].Tuner; - usbvision_device_data[0].TunerType; - usbvision_device_data[0].Vin_Reg1; - usbvision_device_data[0].Vin_Reg2; - usbvision_device_data[0].X_Offset; - usbvision_device_data[0].Y_Offset; - usbvision_device_data[0].Dvi_yuv; - usbvision_device_data[0].ModelString; - */ - - rmspace(parse); - usbvision_device_data[0].ModelString="USBVISION Custom Device"; - - parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idVendor); - goto2next(parse); - PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor); - parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idProduct); - goto2next(parse); - PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct); - sscanf(parse,"%d",&usbvision_device_data[0].Interface); - goto2next(parse); - PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface); - sscanf(parse,"%d",&usbvision_device_data[0].Codec); - goto2next(parse); - PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec); - sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels); - goto2next(parse); - PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels); - - switch(*parse) - { - case 'P': - PDEBUG(DBG_PROBE, "VideoNorm=PAL"); - usbvision_device_data[0].VideoNorm=V4L2_STD_PAL; - break; - - case 'S': - PDEBUG(DBG_PROBE, "VideoNorm=SECAM"); - usbvision_device_data[0].VideoNorm=V4L2_STD_SECAM; - break; - - case 'N': - PDEBUG(DBG_PROBE, "VideoNorm=NTSC"); - usbvision_device_data[0].VideoNorm=V4L2_STD_NTSC; - break; - - default: - PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)"); - usbvision_device_data[0].VideoNorm=V4L2_STD_PAL; - break; - } - goto2next(parse); - - sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels); - goto2next(parse); - PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels); - sscanf(parse,"%d",&usbvision_device_data[0].Radio); - goto2next(parse); - PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio); - sscanf(parse,"%d",&usbvision_device_data[0].Tuner); - goto2next(parse); - PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner); - sscanf(parse,"%d",&usbvision_device_data[0].TunerType); - goto2next(parse); - PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType); - sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1); - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1); - sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2); - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2); - sscanf(parse,"%d",&usbvision_device_data[0].X_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset); - sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset); - sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv); - PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv); - - //add to usbvision_table also - usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE; - usbvision_table[0].idVendor=usbvision_device_data[0].idVendor; - usbvision_table[0].idProduct=usbvision_device_data[0].idProduct; - - } -} - - - -/* * usbvision_init() * * This code is run to initialize the driver. @@ -2092,8 +1963,6 @@ static int __init usbvision_init(void) usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P } - customdevice_process(); - errCode = usb_register(&usbvision_driver); if (errCode == 0) { diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index ad6afd3..bd6f642 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -342,23 +342,24 @@ #define BRIDGE_NT1004 1004 #define BRIDGE_NT1005 1005 struct usbvision_device_data_st { - int idVendor; - int idProduct; - int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ - int Codec; - int VideoChannels; __u64 VideoNorm; - int AudioChannels; - int Radio; - int vbi; - int Tuner; - int TunerType; - int Vin_Reg1; - int Vin_Reg2; - int X_Offset; - int Y_Offset; - int Dvi_yuv; - char *ModelString; + const char *ModelString; + int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ + __u16 Codec; + unsigned VideoChannels:3; + unsigned AudioChannels:2; + unsigned Radio:1; + unsigned vbi:1; + unsigned Tuner:1; + unsigned Vin_Reg1_override:1; /* Override default value with */ + unsigned Vin_Reg2_override:1; /* Vin_Reg1, Vin_Reg2, etc. */ + unsigned Dvi_yuv_override:1; + __u8 Vin_Reg1; + __u8 Vin_Reg2; + __u8 Dvi_yuv; + __u8 TunerType; + __s16 X_Offset; + __s16 Y_Offset; }; /* Declared on usbvision-cards.c */ @@ -481,13 +482,11 @@ struct usb_usbvision { /* i2c-algo-usb declaration */ /* --------------------------------------------------------------- */ -int usbvision_i2c_usb_del_bus(struct i2c_adapter *); - - /* ----------------------------------------------------------------------- */ /* usbvision specific I2C functions */ /* ----------------------------------------------------------------------- */ -int usbvision_init_i2c(struct usb_usbvision *usbvision); +int usbvision_i2c_register(struct usb_usbvision *usbvision); +int usbvision_i2c_unregister(struct usb_usbvision *usbvision); void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg); /* defined in usbvision-core.c */ diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 5474760..49f1df7 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -60,6 +60,7 @@ #include #include #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include +#include #ifdef CONFIG_KMOD #include @@ -260,6 +261,8 @@ char *v4l2_field_names[] = { [V4L2_FIELD_SEQ_TB] = "seq-tb", [V4L2_FIELD_SEQ_BT] = "seq-bt", [V4L2_FIELD_ALTERNATE] = "alternate", + [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", + [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt", }; char *v4l2_type_names[] = { @@ -269,7 +272,8 @@ char *v4l2_type_names[] = { [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", - [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out", + [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over", }; @@ -380,6 +384,8 @@ #if 1 [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", + + [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -410,14 +416,16 @@ #endif [_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)] = "VIDIOC_INT_DECODE_VBI_LINE", [_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA", [_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA", - [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)] = "VIDIOC_INT_G_CHIP_IDENT", [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ", [_IOC_NR(VIDIOC_INT_S_STANDBY)] = "VIDIOC_INT_S_STANDBY", [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING", [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING", [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING", [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING", - [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ" + [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ", + [_IOC_NR(VIDIOC_INT_INIT)] = "VIDIOC_INT_INIT", + [_IOC_NR(VIDIOC_INT_G_STD_OUTPUT)] = "VIDIOC_INT_G_STD_OUTPUT", + [_IOC_NR(VIDIOC_INT_S_STD_OUTPUT)] = "VIDIOC_INT_S_STD_OUTPUT", }; #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) @@ -680,6 +688,7 @@ int v4l2_ctrl_query_fill(struct v4l2_que case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break; case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break; case V4L2_CID_MPEG_AUDIO_CRC: name = "Audio CRC"; break; + case V4L2_CID_MPEG_AUDIO_MUTE: name = "Audio Mute"; break; case V4L2_CID_MPEG_VIDEO_ENCODING: name = "Video Encoding"; break; case V4L2_CID_MPEG_VIDEO_ASPECT: name = "Video Aspect"; break; case V4L2_CID_MPEG_VIDEO_B_FRAMES: name = "Video B Frames"; break; @@ -690,6 +699,8 @@ int v4l2_ctrl_query_fill(struct v4l2_que case V4L2_CID_MPEG_VIDEO_BITRATE: name = "Video Bitrate"; break; case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: name = "Video Peak Bitrate"; break; case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break; + case V4L2_CID_MPEG_VIDEO_MUTE: name = "Video Mute"; break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: name = "Video Mute YUV"; break; case V4L2_CID_MPEG_STREAM_TYPE: name = "Stream Type"; break; case V4L2_CID_MPEG_STREAM_PID_PMT: name = "Stream PMT Program ID"; break; case V4L2_CID_MPEG_STREAM_PID_AUDIO: name = "Stream Audio Program ID"; break; @@ -705,6 +716,7 @@ int v4l2_ctrl_query_fill(struct v4l2_que switch (qctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_LOUDNESS: + case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: case V4L2_CID_MPEG_VIDEO_PULLDOWN: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; @@ -838,6 +850,8 @@ int v4l2_ctrl_query_fill_std(struct v4l2 V4L2_MPEG_AUDIO_CRC_NONE, V4L2_MPEG_AUDIO_CRC_CRC16, 1, V4L2_MPEG_AUDIO_CRC_NONE); + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); case V4L2_CID_MPEG_VIDEO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, @@ -867,6 +881,10 @@ int v4l2_ctrl_query_fill_std(struct v4l2 return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); case V4L2_CID_MPEG_STREAM_TYPE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_STREAM_TYPE_MPEG2_PS, @@ -965,6 +983,22 @@ int v4l2_chip_match_i2c_client(struct i2 } } +int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip, + u32 ident, u32 revision) +{ + if (!v4l2_chip_match_i2c_client(c, chip->match_type, chip->match_chip)) + return 0; + if (chip->ident == V4L2_IDENT_NONE) { + chip->ident = ident; + chip->revision = revision; + } + else { + chip->ident = V4L2_IDENT_AMBIGUOUS; + chip->revision = 0; + } + return 0; +} + int v4l2_chip_match_host(u32 match_type, u32 match_chip) { switch (match_type) { @@ -999,6 +1033,7 @@ EXPORT_SYMBOL(v4l2_ctrl_query_fill); EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); EXPORT_SYMBOL(v4l2_chip_match_i2c_client); +EXPORT_SYMBOL(v4l2_chip_ident_i2c_client); EXPORT_SYMBOL(v4l2_chip_match_host); /* diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c index 290e641..f2bbd7a 100644 --- a/drivers/media/video/videocodec.c +++ b/drivers/media/video/videocodec.c @@ -348,6 +348,9 @@ #define LINESIZE 100 kfree(videocodec_buf); videocodec_buf = kmalloc(size, GFP_KERNEL); + if (!videocodec_buf) + return 0; + i = 0; i += scnprintf(videocodec_buf + i, size - 1, "lave or attached aster name type flags magic "); diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 011938f..80ac5f8 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -318,6 +318,7 @@ static char *v4l2_type_names_FIXME[] = { [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-capture", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over", [V4L2_BUF_TYPE_PRIVATE] = "private", }; @@ -330,6 +331,8 @@ static char *v4l2_field_names_FIXME[] = [V4L2_FIELD_SEQ_TB] = "seq-tb", [V4L2_FIELD_SEQ_BT] = "seq-bt", [V4L2_FIELD_ALTERNATE] = "alternate", + [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", + [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt", }; #define prt_names(a,arr) (((a)>=0)&&((a)vidioc_try_fmt_vbi_output) return (0); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_output_overlay) + return (0); + break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_try_fmt_type_private) return (0); @@ -525,6 +532,10 @@ static int __video_do_ioctl(struct inode ret=vfd->vidioc_enum_fmt_vbi_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_enum_fmt_output_overlay) + ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_enum_fmt_type_private) ret=vfd->vidioc_enum_fmt_type_private(file, @@ -582,6 +593,10 @@ static int __video_do_ioctl(struct inode ret=vfd->vidioc_g_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_g_fmt_output_overlay) + ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_g_fmt_vbi_output) ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f); @@ -630,6 +645,10 @@ static int __video_do_ioctl(struct inode ret=vfd->vidioc_s_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_s_fmt_output_overlay) + ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_s_fmt_vbi_output) ret=vfd->vidioc_s_fmt_vbi_output(file, @@ -680,6 +699,10 @@ static int __video_do_ioctl(struct inode ret=vfd->vidioc_try_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_output_overlay) + ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_try_fmt_vbi_output) ret=vfd->vidioc_try_fmt_vbi_output(file, @@ -1381,6 +1404,11 @@ #endif case VIDIOC_G_PARM: { struct v4l2_streamparm *p=arg; + __u32 type=p->type; + + memset(p,0,sizeof(*p)); + p->type=type; + if (vfd->vidioc_g_parm) { ret=vfd->vidioc_g_parm(file, fh, p); } else { @@ -1392,8 +1420,6 @@ #endif v4l2_video_std_construct(&s, vfd->current_norm, v4l2_norm_to_name(vfd->current_norm)); - memset(p,0,sizeof(*p)); - p->parm.capture.timeperframe = s.frameperiod; ret=0; } @@ -1509,6 +1535,16 @@ #ifdef CONFIG_VIDEO_ADV_DEBUG break; } #endif + case VIDIOC_G_CHIP_IDENT: + { + struct v4l2_chip_ident *p=arg; + if (!vfd->vidioc_g_chip_ident) + break; + ret=vfd->vidioc_g_chip_ident(file, fh, p); + if (!ret) + dbgarg (cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index a9b59c3..8f6741a 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -29,6 +29,7 @@ #include #include #include #include +#include MODULE_DESCRIPTION("wm8739 driver"); MODULE_AUTHOR("T. Adachi, Hans Verkuil"); @@ -236,6 +237,9 @@ static int wm8739_command(struct i2c_cli return -EINVAL; } + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8739, 0); + case VIDIOC_LOG_STATUS: v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index d81a88b..4df5d30 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -33,6 +33,7 @@ #include #include #include #include +#include MODULE_DESCRIPTION("wm8775 driver"); MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); @@ -124,6 +125,9 @@ static int wm8775_command(struct i2c_cli wm8775_write(client, R21, 0x100 + state->input); break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8775, 0); + case VIDIOC_LOG_STATUS: v4l_info(client, "Input: %d%s\n", state->input, state->muted ? " (muted)" : ""); diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c new file mode 100644 index 0000000..b5d3364 --- /dev/null +++ b/drivers/media/video/zr364xx.c @@ -0,0 +1,929 @@ +/* + * Zoran 364xx based USB webcam module version 0.72 + * + * Allows you to use your USB webcam with V4L2 applications + * This is still in heavy developpement ! + * + * Copyright (C) 2004 Antoine Jacquet + * http://royale.zerezo.com/zr364xx/ + * + * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers + * V4L2 version inspired by meye.c driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Version Information */ +#define DRIVER_VERSION "v0.72" +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "Zoran 364xx" + + +/* Camera */ +#define FRAMES 2 +#define MAX_FRAME_SIZE 100000 +#define BUFFER_SIZE 0x1000 +#define CTRL_TIMEOUT 500 + + +/* Debug macro */ +#define DBG(x...) if (debug) info(x) + + +/* Init methods, need to find nicer names for these + * the exact names of the chipsets would be the best if someone finds it */ +#define METHOD0 0 +#define METHOD1 1 +#define METHOD2 2 + + +/* Module parameters */ +static int debug = 0; +static int mode = 0; + + +/* Module parameters interface */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); +module_param(mode, int, 0644); +MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); + + +/* Devices supported by this driver + * .driver_info contains the init method used by the camera */ +static struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, + {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, + {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, + {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, + {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, + {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, + {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, + {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, + {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, + {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, + {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + + +/* Camera stuff */ +struct zr364xx_camera { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface;/* the interface for this device */ + struct video_device *vdev; /* v4l video device */ + u8 *framebuf; + int nb; + unsigned char *buffer; + int skip; + int brightness; + int width; + int height; + int method; + struct mutex lock; +}; + + +/* function used to send initialisation commands to the camera */ +static int send_control_msg(struct usb_device *udev, u8 request, u16 value, + u16 index, unsigned char *cp, u16 size) +{ + int status; + + unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); + if (!transfer_buffer) { + info("kmalloc(%d) failed", size); + return -ENOMEM; + } + + memcpy(transfer_buffer, cp, size); + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, CTRL_TIMEOUT); + + kfree(transfer_buffer); + + if (status < 0) + info("Failed sending control message, error %d.", status); + + return status; +} + + +/* Control messages sent to the camera to initialize it + * and launch the capture */ +typedef struct { + unsigned int value; + unsigned int size; + unsigned char *bytes; +} message; + +/* method 0 */ +static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d3[] = { 0, 0 }; +static message m0[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0x3370, sizeof(m0d1), m0d1}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2610, sizeof(m0d2), m0d2}, + {0xe107, 0, NULL}, + {0x2502, 0, NULL}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m0d3), m0d3}, + {-1, -1, NULL} +}; + +/* method 1 */ +static unsigned char m1d1[] = { 0xff, 0xff }; +static unsigned char m1d2[] = { 0x00, 0x00 }; +static message m1[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m1d1), m1d1}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m1d2), m1d2}, + {-1, -1, NULL} +}; + +/* method 2 */ +static unsigned char m2d1[] = { 0xff, 0xff }; +static message m2[] = { + {0x1f30, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m2d1), m2d1}, + {0x1f70, 0, NULL}, + {-1, -1, NULL} +}; + +/* init table */ +static message *init[3] = { m0, m1, m2 }; + + +/* JPEG static data in header (Huffman table, etc) */ +static unsigned char header1[] = { + 0xFF, 0xD8, + /* + 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', + 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, + */ + 0xFF, 0xDB, 0x00, 0x84 +}; +static unsigned char header2[] = { + 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, + 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, + 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, + 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, + 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, + 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, + 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, + 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, + 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, + 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, + 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00 +}; +static unsigned char header3; + + + +/********************/ +/* V4L2 integration */ +/********************/ + +/* this function reads a full JPEG picture synchronously + * TODO: do it asynchronously... */ +static int read_frame(struct zr364xx_camera *cam, int framenum) +{ + int i, n, temp, head, size, actual_length; + unsigned char *ptr = NULL, *jpeg; + + redo: + /* hardware brightness */ + n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - cam->brightness; + n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + + /* during the first loop we are going to insert JPEG header */ + head = 0; + /* this is the place in memory where we are going to build + * the JPEG image */ + jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE; + /* read data... */ + do { + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, 0x81), + cam->buffer, BUFFER_SIZE, &actual_length, + CTRL_TIMEOUT); + DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]); + DBG("bulk : n=%d size=%d", n, actual_length); + if (n < 0) { + info("error reading bulk msg"); + return 0; + } + if (actual_length < 0 || actual_length > BUFFER_SIZE) { + info("wrong number of bytes"); + return 0; + } + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16*)cam->buffer; + for (i = 0; i < BUFFER_SIZE/2; i++) + swab16s(buf + i); + } + + /* write the JPEG header */ + if (!head) { + DBG("jpeg header"); + ptr = jpeg; + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, cam->buffer, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, cam->buffer + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, cam->buffer + 128, + actual_length - 128); + ptr += actual_length - 128; + head = 1; + DBG("header : %d %d %d %d %d %d %d %d %d", + cam->buffer[0], cam->buffer[1], cam->buffer[2], + cam->buffer[3], cam->buffer[4], cam->buffer[5], + cam->buffer[6], cam->buffer[7], cam->buffer[8]); + } else { + memcpy(ptr, cam->buffer, actual_length); + ptr += actual_length; + } + } + /* ... until there is no more */ + while (actual_length == BUFFER_SIZE); + + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) { + cam->skip--; + goto redo; + } + + /* go back to find the JPEG EOI marker */ + size = ptr - jpeg; + ptr -= 2; + while (ptr > jpeg) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == jpeg) + DBG("No EOI marker"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > jpeg) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != jpeg) { + DBG("Bogus frame ? %d", cam->nb); + goto redo; + } + + DBG("jpeg : %d %d %d %d %d %d %d %d", + jpeg[0], jpeg[1], jpeg[2], jpeg[3], + jpeg[4], jpeg[5], jpeg[6], jpeg[7]); + + return size; +} + + +static ssize_t zr364xx_read(struct file *file, char *buf, size_t cnt, + loff_t * ppos) +{ + unsigned long count = cnt; + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + DBG("zr364xx_read: read %d bytes.", (int) count); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + /* NoMan Sux ! */ + count = read_frame(cam, 0); + + if (copy_to_user(buf, cam->framebuf, count)) + return -EFAULT; + + return count; +} + + +static int zr364xx_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, DRIVER_DESC); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} + +static int zr364xx_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + memset(i, 0, sizeof(*i)); + i->index = 0; + strcpy(i->name, DRIVER_DESC " Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int zr364xx_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int zr364xx_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Brightness"); + c->minimum = 0; + c->maximum = 127; + c->step = 1; + c->default_value = cam->brightness; + c->flags = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + cam->brightness = c->value; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = cam->brightness; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_enum_fmt_cap(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = 0; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, "JPEG"); + f->pixelformat = V4L2_PIX_FMT_JPEG; + return 0; +} + +static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + DBG("ok!"); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + return 0; +} + +static int zr364xx_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + return 0; +} + + +/* open the camera */ +static int zr364xx_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam = video_get_drvdata(vdev); + struct usb_device *udev = cam->udev; + int i, err; + + DBG("zr364xx_open"); + + cam->skip = 2; + + err = video_exclusive_open(inode, file); + if (err < 0) + return err; + + if (!cam->framebuf) { + cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); + if (!cam->framebuf) { + info("vmalloc_32 failed!"); + return -ENOMEM; + } + } + + mutex_lock(&cam->lock); + for (i = 0; init[cam->method][i].size != -1; i++) { + err = + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (err < 0) { + info("error during open sequence: %d", i); + mutex_unlock(&cam->lock); + return err; + } + } + + file->private_data = vdev; + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + + mutex_unlock(&cam->lock); + return 0; +} + + +/* release the camera */ +static int zr364xx_release(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + struct usb_device *udev; + int i, err; + + DBG("zr364xx_release"); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + udev = cam->udev; + + mutex_lock(&cam->lock); + for (i = 0; i < 2; i++) { + err = + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[i][cam->method].bytes, + init[cam->method][i].size); + if (err < 0) { + info("error during release sequence"); + mutex_unlock(&cam->lock); + return err; + } + } + + file->private_data = NULL; + video_exclusive_release(inode, file); + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + + mutex_unlock(&cam->lock); + return 0; +} + + +static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) +{ + void *pos; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + DBG("zr364xx_mmap: %ld\n", size); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + pos = cam->framebuf; + while (size > 0) { + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + + +static struct file_operations zr364xx_fops = { + .owner = THIS_MODULE, + .open = zr364xx_open, + .release = zr364xx_release, + .read = zr364xx_read, + .mmap = zr364xx_mmap, + .ioctl = video_ioctl2, + .llseek = no_llseek, +}; + +static struct video_device zr364xx_template = { + .owner = THIS_MODULE, + .name = DRIVER_DESC, + .type = VID_TYPE_CAPTURE, + .fops = &zr364xx_fops, + .release = video_device_release, + .minor = -1, + + .vidioc_querycap = zr364xx_vidioc_querycap, + .vidioc_enum_fmt_cap = zr364xx_vidioc_enum_fmt_cap, + .vidioc_try_fmt_cap = zr364xx_vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = zr364xx_vidioc_s_fmt_cap, + .vidioc_g_fmt_cap = zr364xx_vidioc_g_fmt_cap, + .vidioc_enum_input = zr364xx_vidioc_enum_input, + .vidioc_g_input = zr364xx_vidioc_g_input, + .vidioc_s_input = zr364xx_vidioc_s_input, + .vidioc_streamon = zr364xx_vidioc_streamon, + .vidioc_streamoff = zr364xx_vidioc_streamoff, + .vidioc_queryctrl = zr364xx_vidioc_queryctrl, + .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl, + .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, +}; + + + +/*******************/ +/* USB integration */ +/*******************/ + +static int zr364xx_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zr364xx_camera *cam = NULL; + + DBG("probing..."); + + info(DRIVER_DESC " compatible webcam plugged"); + info("model %04x:%04x detected", udev->descriptor.idVendor, + udev->descriptor.idProduct); + + if ((cam = + kmalloc(sizeof(struct zr364xx_camera), GFP_KERNEL)) == NULL) { + info("cam: out of memory !"); + return -ENODEV; + } + memset(cam, 0x00, sizeof(struct zr364xx_camera)); + /* save the init method used by this camera */ + cam->method = id->driver_info; + + cam->vdev = video_device_alloc(); + if (cam->vdev == NULL) { + info("cam->vdev: out of memory !"); + kfree(cam); + return -ENODEV; + } + memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); + video_set_drvdata(cam->vdev, cam); + if (debug) + cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + + cam->udev = udev; + + if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) { + info("cam->buffer: out of memory !"); + video_device_release(cam->vdev); + kfree(cam); + return -ENODEV; + } + + switch (mode) { + case 1: + info("160x120 mode selected"); + cam->width = 160; + cam->height = 120; + break; + case 2: + info("640x480 mode selected"); + cam->width = 640; + cam->height = 480; + break; + default: + info("320x240 mode selected"); + cam->width = 320; + cam->height = 240; + break; + } + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + cam->nb = 0; + cam->brightness = 64; + mutex_init(&cam->lock); + + if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + info("video_register_device failed"); + video_device_release(cam->vdev); + kfree(cam->buffer); + kfree(cam); + return -ENODEV; + } + + usb_set_intfdata(intf, cam); + + info(DRIVER_DESC " controlling video device %d", cam->vdev->minor); + return 0; +} + + +static void zr364xx_disconnect(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + dev_set_drvdata(&intf->dev, NULL); + info(DRIVER_DESC " webcam unplugged"); + if (cam->vdev) + video_unregister_device(cam->vdev); + cam->vdev = NULL; + kfree(cam->buffer); + if (cam->framebuf) + vfree(cam->framebuf); + kfree(cam); +} + + + +/**********************/ +/* Module integration */ +/**********************/ + +static struct usb_driver zr364xx_driver = { + .name = "zr364xx", + .probe = zr364xx_probe, + .disconnect = zr364xx_disconnect, + .id_table = device_table +}; + + +static int __init zr364xx_init(void) +{ + int retval; + retval = usb_register(&zr364xx_driver) < 0; + if (retval) + info("usb_register failed!"); + else + info(DRIVER_DESC " module loaded"); + return retval; +} + + +static void __exit zr364xx_exit(void) +{ + info(DRIVER_DESC " module unloaded"); + usb_deregister(&zr364xx_driver); +} + + +module_init(zr364xx_init); +module_exit(zr364xx_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index b691292..7dd34bd 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -714,6 +714,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, s LANSendRequest_t *pSendReq; SGETransaction32_t *pTrans; SGESimple64_t *pSimple; + const unsigned char *mac; dma_addr_t dma; unsigned long flags; int ctx; @@ -753,7 +754,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, s /* Set the mac.raw pointer, since this apparently isn't getting * done before we get the skb. Pull the data pointer past the mac data. */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, 12); dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len, @@ -784,6 +785,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, s // IOC_AND_NETDEV_NAMES_s_s(dev), // ctx, skb, skb->data)); + mac = skb_mac_header(skb); #ifdef QLOGIC_NAA_WORKAROUND { struct NAA_Hosed *nh; @@ -793,12 +795,12 @@ #ifdef QLOGIC_NAA_WORKAROUND drops. */ read_lock_irq(&bad_naa_lock); for (nh = mpt_bad_naa; nh != NULL; nh=nh->next) { - if ((nh->ieee[0] == skb->mac.raw[0]) && - (nh->ieee[1] == skb->mac.raw[1]) && - (nh->ieee[2] == skb->mac.raw[2]) && - (nh->ieee[3] == skb->mac.raw[3]) && - (nh->ieee[4] == skb->mac.raw[4]) && - (nh->ieee[5] == skb->mac.raw[5])) { + if ((nh->ieee[0] == mac[0]) && + (nh->ieee[1] == mac[1]) && + (nh->ieee[2] == mac[2]) && + (nh->ieee[3] == mac[3]) && + (nh->ieee[4] == mac[4]) && + (nh->ieee[5] == mac[5])) { cur_naa = nh->NAA; dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value " "= %04x.\n", cur_naa)); @@ -810,12 +812,12 @@ #ifdef QLOGIC_NAA_WORKAROUND #endif pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa << 16) | - (skb->mac.raw[0] << 8) | - (skb->mac.raw[1] << 0)); - pTrans->TransactionDetails[1] = cpu_to_le32((skb->mac.raw[2] << 24) | - (skb->mac.raw[3] << 16) | - (skb->mac.raw[4] << 8) | - (skb->mac.raw[5] << 0)); + (mac[0] << 8) | + (mac[1] << 0)); + pTrans->TransactionDetails[1] = cpu_to_le32((mac[2] << 24) | + (mac[3] << 16) | + (mac[4] << 8) | + (mac[5] << 0)); pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2]; @@ -930,7 +932,7 @@ mpt_lan_receive_post_turbo(struct net_de pci_dma_sync_single_for_cpu(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); - memcpy(skb_put(skb, len), old_skb->data, len); + skb_copy_from_linear_data(old_skb, skb_put(skb, len), len); pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); @@ -1091,7 +1093,7 @@ mpt_lan_receive_post_reply(struct net_de priv->RcvCtl[ctx].dma, priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); - memcpy(skb_put(skb, l), old_skb->data, l); + skb_copy_from_linear_data(old_skb, skb_put(skb, l), l); pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, @@ -1120,7 +1122,7 @@ mpt_lan_receive_post_reply(struct net_de priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); - memcpy(skb_put(skb, len), old_skb->data, len); + skb_copy_from_linear_data(old_skb, skb_put(skb, len), len); pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, @@ -1549,7 +1551,7 @@ mpt_lan_type_trans(struct sk_buff *skb, struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data; struct fcllc *fcllc; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, sizeof(struct mpt_lan_ohdr)); if (fch->dtype == htons(0xffff)) { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 80b199f..a3c525b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -112,14 +112,70 @@ config SONY_LAPTOP depends on X86 && ACPI select BACKLIGHT_CLASS_DEVICE ---help--- - This mini-driver drives the SNC device present in the ACPI BIOS of - the Sony Vaio laptops. + This mini-driver drives the SNC and SPIC devices present in the ACPI + BIOS of the Sony Vaio laptops. - It gives access to some extra laptop functionalities. In its current - form, this driver let the user set or query the screen brightness - through the backlight subsystem and remove/apply power to some + It gives access to some extra laptop functionalities like Bluetooth, + screen brightness control, Fn keys and allows powering on/off some devices. Read for more information. +config SONY_LAPTOP_OLD + bool "Sonypi compatibility" + depends on SONY_LAPTOP + ---help--- + Build the sonypi driver compatibility code into the sony-laptop driver. + +config THINKPAD_ACPI + tristate "ThinkPad ACPI Laptop Extras" + depends on X86 && ACPI + select BACKLIGHT_CLASS_DEVICE + select HWMON + ---help--- + This is a driver for the IBM and Lenovo ThinkPad laptops. It adds + support for Fn-Fx key combinations, Bluetooth control, video + output switching, ThinkLight control, UltraBay eject and more. + For more information about this driver see + and . + + This driver was formely known as ibm-acpi. + + If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. + +config THINKPAD_ACPI_DEBUG + bool "Verbose debug mode" + depends on THINKPAD_ACPI + default n + ---help--- + Enables extra debugging information, at the expense of a slightly + increase in driver size. + + If you are not sure, say N here. + +config THINKPAD_ACPI_DOCK + bool "Legacy Docking Station Support" + depends on THINKPAD_ACPI + depends on ACPI_DOCK=n + default n + ---help--- + Allows the thinkpad_acpi driver to handle docking station events. + This support was made obsolete by the generic ACPI docking station + support (CONFIG_ACPI_DOCK). It will allow locking and removing the + laptop from the docking station, but will not properly connect PCI + devices. + + If you are not sure, say N here. + +config THINKPAD_ACPI_BAY + bool "Legacy Removable Bay Support" + depends on THINKPAD_ACPI + default y + ---help--- + Allows the thinkpad_acpi driver to handle removable bays. It will + eletrically disable the device in the bay, and also generate + notifications when the bay lever is ejected or inserted. + + If you are not sure, say Y here. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7793ccd..e325164 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_TIFM_CORE) += tifm_c obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o +obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 4b23212..65c32a9 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -3,7 +3,7 @@ * * * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor - * Copyright (C) 2006 Corentin Chary + * Copyright (C) 2006-2007 Corentin Chary * * 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 @@ -48,7 +48,7 @@ #include #include #include -#define ASUS_LAPTOP_VERSION "0.40" +#define ASUS_LAPTOP_VERSION "0.41" #define ASUS_HOTK_NAME "Asus Laptop Support" #define ASUS_HOTK_CLASS "hotkey" @@ -81,7 +81,8 @@ #define MLED_ON 0x04 //mail LED #define TLED_ON 0x08 //touchpad LED #define RLED_ON 0x10 //Record LED #define PLED_ON 0x20 //Phone LED -#define LCD_ON 0x40 //LCD backlight +#define GLED_ON 0x40 //Gaming LED +#define LCD_ON 0x80 //LCD backlight #define ASUS_LOG ASUS_HOTK_FILE ": " #define ASUS_ERR KERN_ERR ASUS_LOG @@ -94,6 +95,19 @@ MODULE_AUTHOR("Julien Lerouge, Karol Koz MODULE_DESCRIPTION(ASUS_HOTK_NAME); MODULE_LICENSE("GPL"); +/* WAPF defines the behavior of the Fn+Fx wlan key + * The significance of values is yet to be found, but + * most of the time: + * 0x0 will do nothing + * 0x1 will allow to control the device with Fn+Fx key. + * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key + * 0x5 like 0x1 or 0x4 + * So, if something doesn't work as you want, just try other values =) + */ +static uint wapf = 1; +module_param(wapf, uint, 0644); +MODULE_PARM_DESC(wapf, "WAPF value"); + #define ASUS_HANDLE(object, paths...) \ static acpi_handle object##_handle = NULL; \ static char *object##_paths[] = { paths } @@ -103,6 +117,7 @@ ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX " ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED"); ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */ ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */ +ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */ /* LEDD */ ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); @@ -221,6 +236,7 @@ ASUS_LED(mled, "mail"); ASUS_LED(tled, "touchpad"); ASUS_LED(rled, "record"); ASUS_LED(pled, "phone"); +ASUS_LED(gled, "gaming"); /* * This function evaluates an ACPI method, given an int as parameter, the @@ -245,32 +261,19 @@ static int write_acpi_int(acpi_handle ha return (status == AE_OK); } -static int read_acpi_int(acpi_handle handle, const char *method, int *val, - struct acpi_object_list *params) -{ - struct acpi_buffer output; - union acpi_object out_obj; - acpi_status status; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - status = acpi_evaluate_object(handle, (char *)method, params, &output); - *val = out_obj.integer.value; - return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); -} - static int read_wireless_status(int mask) { - int status; + ulong status; + acpi_status rv = AE_OK; if (!wireless_status_handle) return (hotk->status & mask) ? 1 : 0; - if (read_acpi_int(wireless_status_handle, NULL, &status, NULL)) { - return (status & mask) ? 1 : 0; - } else + rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status); + if (ACPI_FAILURE(rv)) printk(ASUS_WARNING "Error reading Wireless status\n"); + else + return (status & mask) ? 1 : 0; return (hotk->status & mask) ? 1 : 0; } @@ -285,19 +288,28 @@ static int read_status(int mask) return (hotk->status & mask) ? 1 : 0; } -static void write_status(acpi_handle handle, int out, int mask, int invert) +static void write_status(acpi_handle handle, int out, int mask) { hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask); - if (invert) /* invert target value */ + switch (mask) { + case MLED_ON: out = !out & 0x1; + break; + case GLED_ON: + out = (out & 0x1) + 1; + break; + default: + out &= 0x1; + break; + } if (handle && !write_acpi_int(handle, NULL, out, NULL)) - printk(ASUS_WARNING " write failed\n"); + printk(ASUS_WARNING " write failed %x\n", mask); } /* /sys/class/led handlers */ -#define ASUS_LED_HANDLER(object, mask, invert) \ +#define ASUS_LED_HANDLER(object, mask) \ static void object##_led_set(struct led_classdev *led_cdev, \ enum led_brightness value) \ { \ @@ -307,13 +319,14 @@ #define ASUS_LED_HANDLER(object, mask, i static void object##_led_update(struct work_struct *ignored) \ { \ int value = object##_led_wk; \ - write_status(object##_set_handle, value, (mask), (invert)); \ + write_status(object##_set_handle, value, (mask)); \ } -ASUS_LED_HANDLER(mled, MLED_ON, 1); -ASUS_LED_HANDLER(pled, PLED_ON, 0); -ASUS_LED_HANDLER(rled, RLED_ON, 0); -ASUS_LED_HANDLER(tled, TLED_ON, 0); +ASUS_LED_HANDLER(mled, MLED_ON); +ASUS_LED_HANDLER(pled, PLED_ON); +ASUS_LED_HANDLER(rled, RLED_ON); +ASUS_LED_HANDLER(tled, TLED_ON); +ASUS_LED_HANDLER(gled, GLED_ON); static int get_lcd_state(void) { @@ -338,7 +351,7 @@ static int set_lcd_state(int value) printk(ASUS_WARNING "Error switching LCD\n"); } - write_status(NULL, lcd, LCD_ON, 0); + write_status(NULL, lcd, LCD_ON); return 0; } @@ -354,9 +367,11 @@ static void lcd_blank(int blank) static int read_brightness(struct backlight_device *bd) { - int value; + ulong value; + acpi_status rv = AE_OK; - if (!read_acpi_int(brightness_get_handle, NULL, &value, NULL)) + rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); + if (ACPI_FAILURE(rv)) printk(ASUS_WARNING "Error reading brightness\n"); return value; @@ -403,8 +418,10 @@ static ssize_t show_infos(struct device struct device_attribute *attr, char *page) { int len = 0; - int temp; + ulong temp; char buf[16]; //enough for all info + acpi_status rv = AE_OK; + /* * We use the easy way, we don't care of off and count, so we don't set eof * to 1 @@ -418,9 +435,10 @@ static ssize_t show_infos(struct device * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. * The significance of others is yet to be found. */ - if (read_acpi_int(hotk->handle, "SFUN", &temp, NULL)) - len += - sprintf(page + len, "SFUN value : 0x%04x\n", temp); + rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); + if (!ACPI_FAILURE(rv)) + len += sprintf(page + len, "SFUN value : 0x%04x\n", + (uint) temp); /* * Another value for userspace: the ASYM method returns 0x02 for * battery low and 0x04 for battery critical, its readings tend to be @@ -428,9 +446,10 @@ static ssize_t show_infos(struct device * Note: since not all the laptops provide this method, errors are * silently ignored. */ - if (read_acpi_int(hotk->handle, "ASYM", &temp, NULL)) - len += - sprintf(page + len, "ASYM value : 0x%04x\n", temp); + rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); + if (!ACPI_FAILURE(rv)) + len += sprintf(page + len, "ASYM value : 0x%04x\n", + (uint) temp); if (asus_info) { snprintf(buf, 16, "%d", asus_info->length); len += sprintf(page + len, "DSDT length : %s\n", buf); @@ -465,7 +484,7 @@ static int parse_arg(const char *buf, un } static ssize_t store_status(const char *buf, size_t count, - acpi_handle handle, int mask, int invert) + acpi_handle handle, int mask) { int rv, value; int out = 0; @@ -474,7 +493,7 @@ static ssize_t store_status(const char * if (rv > 0) out = value ? 1 : 0; - write_status(handle, out, mask, invert); + write_status(handle, out, mask); return rv; } @@ -515,7 +534,7 @@ static ssize_t show_wlan(struct device * static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - return store_status(buf, count, wl_switch_handle, WL_ON, 0); + return store_status(buf, count, wl_switch_handle, WL_ON); } /* @@ -531,7 +550,7 @@ static ssize_t store_bluetooth(struct de struct device_attribute *attr, const char *buf, size_t count) { - return store_status(buf, count, bt_switch_handle, BT_ON, 0); + return store_status(buf, count, bt_switch_handle, BT_ON); } /* @@ -547,12 +566,15 @@ static void set_display(int value) static int read_display(void) { - int value = 0; + ulong value = 0; + acpi_status rv = AE_OK; /* In most of the case, we know how to set the display, but sometime we can't read it */ if (display_get_handle) { - if (!read_acpi_int(display_get_handle, NULL, &value, NULL)) + rv = acpi_evaluate_integer(display_get_handle, NULL, + NULL, &value); + if (ACPI_FAILURE(rv)) printk(ASUS_WARNING "Error reading display status\n"); } @@ -656,10 +678,10 @@ static void asus_hotk_notify(acpi_handle * switched */ if (event == ATKD_LCD_ON) { - write_status(NULL, 1, LCD_ON, 0); + write_status(NULL, 1, LCD_ON); lcd_blank(FB_BLANK_UNBLANK); } else if (event == ATKD_LCD_OFF) { - write_status(NULL, 0, LCD_ON, 0); + write_status(NULL, 0, LCD_ON); lcd_blank(FB_BLANK_POWERDOWN); } @@ -771,7 +793,7 @@ static int asus_hotk_get_info(void) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *model = NULL; - int bsts_result, hwrs_result; + ulong bsts_result, hwrs_result; char *string = NULL; acpi_status status; @@ -794,11 +816,16 @@ static int asus_hotk_get_info(void) } /* This needs to be called for some laptops to init properly */ - if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result, NULL)) + status = + acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); + if (ACPI_FAILURE(status)) printk(ASUS_WARNING "Error calling BSTS\n"); else if (bsts_result) printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n", - bsts_result); + (uint) bsts_result); + + /* This too ... */ + write_acpi_int(hotk->handle, "CWAP", wapf, NULL); /* * Try to match the object returned by INIT to the specific model. @@ -831,6 +858,7 @@ static int asus_hotk_get_info(void) ASUS_HANDLE_INIT(tled_set); ASUS_HANDLE_INIT(rled_set); ASUS_HANDLE_INIT(pled_set); + ASUS_HANDLE_INIT(gled_set); ASUS_HANDLE_INIT(ledd_set); @@ -840,7 +868,9 @@ static int asus_hotk_get_info(void) * The significance of others is yet to be found. * If we don't find the method, we assume the device are present. */ - if (!read_acpi_int(hotk->handle, "HRWS", &hwrs_result, NULL)) + status = + acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result); + if (ACPI_FAILURE(status)) hwrs_result = WL_HWRS | BT_HWRS; if (hwrs_result & WL_HWRS) @@ -928,11 +958,15 @@ static int asus_hotk_add(struct acpi_dev asus_hotk_found = 1; /* WLED and BLED are on by default */ - write_status(bt_switch_handle, 1, BT_ON, 0); - write_status(wl_switch_handle, 1, WL_ON, 0); + write_status(bt_switch_handle, 1, BT_ON); + write_status(wl_switch_handle, 1, WL_ON); + + /* If the h/w switch is off, we need to check the real status */ + write_status(NULL, read_status(BT_ON), BT_ON); + write_status(NULL, read_status(WL_ON), WL_ON); /* LCD Backlight is on by default */ - write_status(NULL, 1, LCD_ON, 0); + write_status(NULL, 1, LCD_ON); /* LED display is off by default */ hotk->ledd_status = 0xFFF; @@ -991,6 +1025,7 @@ static void asus_led_exit(void) ASUS_LED_UNREGISTER(tled); ASUS_LED_UNREGISTER(pled); ASUS_LED_UNREGISTER(rled); + ASUS_LED_UNREGISTER(gled); destroy_workqueue(led_workqueue); } @@ -1062,6 +1097,10 @@ static int asus_led_init(struct device * if (rv) return rv; + rv = ASUS_LED_REGISTER(gled, dev); + if (rv) + return rv; + led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!led_workqueue) return -ENOMEM; diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index ac708bc..c15c1f6 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -1,5 +1,5 @@ /* - * ACPI Sony Notebook Control Driver (SNC) + * ACPI Sony Notebook Control Driver (SNC and SPIC) * * Copyright (C) 2004-2005 Stelian Pop * Copyright (C) 2007 Mattia Dongili @@ -7,6 +7,25 @@ * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c * which are copyrighted by their respective authors. * + * The SNY6001 driver part is based on the sonypi driver which includes + * material from: + * + * Copyright (C) 2001-2005 Stelian Pop + * + * Copyright (C) 2005 Narayanan R S + * + * Copyright (C) 2001-2002 Alcve + * + * Copyright (C) 2001 Michael Ashley + * + * Copyright (C) 2001 Junichi Morita + * + * Copyright (C) 2000 Takaya Kinjo + * + * Copyright (C) 2000 Andrew Tridgell + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * * 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 @@ -31,40 +50,404 @@ #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include +#ifdef CONFIG_SONY_LAPTOP_OLD +#include +#include +#endif -#define ACPI_SNC_CLASS "sony" -#define ACPI_SNC_HID "SNY5001" -#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.4" +#define DRV_PFX "sony-laptop: " +#define dprintk(msg...) do { \ + if (debug) printk(KERN_WARNING DRV_PFX msg); \ +} while (0) -/* the device uses 1-based values, while the backlight subsystem uses - 0-based values */ -#define SONY_MAX_BRIGHTNESS 8 +#define SONY_LAPTOP_DRIVER_VERSION "0.5" + +#define SONY_NC_CLASS "sony-nc" +#define SONY_NC_HID "SNY5001" +#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver" -#define LOG_PFX KERN_WARNING "sony-laptop: " +#define SONY_PIC_CLASS "sony-pic" +#define SONY_PIC_HID "SNY6001" +#define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control Driver" MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); -MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME); +MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)"); MODULE_LICENSE("GPL"); +MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION); static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help " "the development of this driver"); -static ssize_t sony_acpi_show(struct device *, struct device_attribute *, +static int no_spic; /* = 0 */ +module_param(no_spic, int, 0444); +MODULE_PARM_DESC(no_spic, + "set this if you don't want to enable the SPIC device"); + +static int compat; /* = 0 */ +module_param(compat, int, 0444); +MODULE_PARM_DESC(compat, + "set this if you want to enable backward compatibility mode"); + +static unsigned long mask = 0xffffffff; +module_param(mask, ulong, 0644); +MODULE_PARM_DESC(mask, + "set this to the mask of event you want to enable (see doc)"); + +static int camera; /* = 0 */ +module_param(camera, int, 0444); +MODULE_PARM_DESC(camera, + "set this to 1 to enable Motion Eye camera controls " + "(only use it if you have a C1VE or C1VN model)"); + +#ifdef CONFIG_SONY_LAPTOP_OLD +static int minor = -1; +module_param(minor, int, 0); +MODULE_PARM_DESC(minor, + "minor number of the misc device for the SPIC compatibility code, " + "default is -1 (automatic)"); +#endif + +/*********** Input Devices ***********/ + +#define SONY_LAPTOP_BUF_SIZE 128 +struct sony_laptop_input_s { + atomic_t users; + struct input_dev *jog_dev; + struct input_dev *key_dev; + struct kfifo *fifo; + spinlock_t fifo_lock; + struct workqueue_struct *wq; +}; +static struct sony_laptop_input_s sony_laptop_input = { + .users = ATOMIC_INIT(0), +}; + +struct sony_laptop_keypress { + struct input_dev *dev; + int key; +}; + +/* Correspondance table between sonypi events and input layer events */ +static struct { + int sonypiev; + int inputev; +} sony_laptop_inputkeys[] = { + { SONYPI_EVENT_CAPTURE_PRESSED, KEY_CAMERA }, + { SONYPI_EVENT_FNKEY_ONLY, KEY_FN }, + { SONYPI_EVENT_FNKEY_ESC, KEY_FN_ESC }, + { SONYPI_EVENT_FNKEY_F1, KEY_FN_F1 }, + { SONYPI_EVENT_FNKEY_F2, KEY_FN_F2 }, + { SONYPI_EVENT_FNKEY_F3, KEY_FN_F3 }, + { SONYPI_EVENT_FNKEY_F4, KEY_FN_F4 }, + { SONYPI_EVENT_FNKEY_F5, KEY_FN_F5 }, + { SONYPI_EVENT_FNKEY_F6, KEY_FN_F6 }, + { SONYPI_EVENT_FNKEY_F7, KEY_FN_F7 }, + { SONYPI_EVENT_FNKEY_F8, KEY_FN_F8 }, + { SONYPI_EVENT_FNKEY_F9, KEY_FN_F9 }, + { SONYPI_EVENT_FNKEY_F10, KEY_FN_F10 }, + { SONYPI_EVENT_FNKEY_F11, KEY_FN_F11 }, + { SONYPI_EVENT_FNKEY_F12, KEY_FN_F12 }, + { SONYPI_EVENT_FNKEY_1, KEY_FN_1 }, + { SONYPI_EVENT_FNKEY_2, KEY_FN_2 }, + { SONYPI_EVENT_FNKEY_D, KEY_FN_D }, + { SONYPI_EVENT_FNKEY_E, KEY_FN_E }, + { SONYPI_EVENT_FNKEY_F, KEY_FN_F }, + { SONYPI_EVENT_FNKEY_S, KEY_FN_S }, + { SONYPI_EVENT_FNKEY_B, KEY_FN_B }, + { SONYPI_EVENT_BLUETOOTH_PRESSED, KEY_BLUE }, + { SONYPI_EVENT_BLUETOOTH_ON, KEY_BLUE }, + { SONYPI_EVENT_PKEY_P1, KEY_PROG1 }, + { SONYPI_EVENT_PKEY_P2, KEY_PROG2 }, + { SONYPI_EVENT_PKEY_P3, KEY_PROG3 }, + { SONYPI_EVENT_BACK_PRESSED, KEY_BACK }, + { SONYPI_EVENT_HELP_PRESSED, KEY_HELP }, + { SONYPI_EVENT_ZOOM_PRESSED, KEY_ZOOM }, + { SONYPI_EVENT_THUMBPHRASE_PRESSED, BTN_THUMB }, + { 0, 0 }, +}; + +/* release buttons after a short delay if pressed */ +static void do_sony_laptop_release_key(struct work_struct *work) +{ + struct sony_laptop_keypress kp; + + while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp, + sizeof(kp)) == sizeof(kp)) { + msleep(10); + input_report_key(kp.dev, kp.key, 0); + input_sync(kp.dev); + } +} +static DECLARE_WORK(sony_laptop_release_key_work, + do_sony_laptop_release_key); + +/* forward event to the input subsytem */ +static void sony_laptop_report_input_event(u8 event) +{ + struct input_dev *jog_dev = sony_laptop_input.jog_dev; + struct input_dev *key_dev = sony_laptop_input.key_dev; + struct sony_laptop_keypress kp = { NULL }; + int i; + + if (event == SONYPI_EVENT_FNKEY_RELEASED) { + /* Nothing, not all VAIOs generate this event */ + return; + } + + /* report events */ + switch (event) { + /* jog_dev events */ + case SONYPI_EVENT_JOGDIAL_UP: + case SONYPI_EVENT_JOGDIAL_UP_PRESSED: + input_report_rel(jog_dev, REL_WHEEL, 1); + input_sync(jog_dev); + return; + + case SONYPI_EVENT_JOGDIAL_DOWN: + case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED: + input_report_rel(jog_dev, REL_WHEEL, -1); + input_sync(jog_dev); + return; + + /* key_dev events */ + case SONYPI_EVENT_JOGDIAL_PRESSED: + kp.key = BTN_MIDDLE; + kp.dev = jog_dev; + break; + + default: + for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) + if (event == sony_laptop_inputkeys[i].sonypiev) { + kp.dev = key_dev; + kp.key = sony_laptop_inputkeys[i].inputev; + break; + } + break; + } + + if (kp.dev) { + input_report_key(kp.dev, kp.key, 1); + input_sync(kp.dev); + kfifo_put(sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp)); + + if (!work_pending(&sony_laptop_release_key_work)) + queue_work(sony_laptop_input.wq, + &sony_laptop_release_key_work); + } else + dprintk("unknown input event %.2x\n", event); +} + +static int sony_laptop_setup_input(void) +{ + struct input_dev *jog_dev; + struct input_dev *key_dev; + int i; + int error; + + /* don't run again if already initialized */ + if (atomic_add_return(1, &sony_laptop_input.users) > 1) + return 0; + + /* kfifo */ + spin_lock_init(&sony_laptop_input.fifo_lock); + sony_laptop_input.fifo = + kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, + &sony_laptop_input.fifo_lock); + if (IS_ERR(sony_laptop_input.fifo)) { + printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + error = PTR_ERR(sony_laptop_input.fifo); + goto err_dec_users; + } + + /* init workqueue */ + sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); + if (!sony_laptop_input.wq) { + printk(KERN_ERR DRV_PFX + "Unabe to create workqueue.\n"); + error = -ENXIO; + goto err_free_kfifo; + } + + /* input keys */ + key_dev = input_allocate_device(); + if (!key_dev) { + error = -ENOMEM; + goto err_destroy_wq; + } + + key_dev->name = "Sony Vaio Keys"; + key_dev->id.bustype = BUS_ISA; + key_dev->id.vendor = PCI_VENDOR_ID_SONY; + + /* Initialize the Input Drivers: special keys */ + key_dev->evbit[0] = BIT(EV_KEY); + for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) + if (sony_laptop_inputkeys[i].inputev) + set_bit(sony_laptop_inputkeys[i].inputev, + key_dev->keybit); + + error = input_register_device(key_dev); + if (error) + goto err_free_keydev; + + sony_laptop_input.key_dev = key_dev; + + /* jogdial */ + jog_dev = input_allocate_device(); + if (!jog_dev) { + error = -ENOMEM; + goto err_unregister_keydev; + } + + jog_dev->name = "Sony Vaio Jogdial"; + jog_dev->id.bustype = BUS_ISA; + jog_dev->id.vendor = PCI_VENDOR_ID_SONY; + + jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE); + jog_dev->relbit[0] = BIT(REL_WHEEL); + + error = input_register_device(jog_dev); + if (error) + goto err_free_jogdev; + + sony_laptop_input.jog_dev = jog_dev; + + return 0; + +err_free_jogdev: + input_free_device(jog_dev); + +err_unregister_keydev: + input_unregister_device(key_dev); + /* to avoid kref underflow below at input_free_device */ + key_dev = NULL; + +err_free_keydev: + input_free_device(key_dev); + +err_destroy_wq: + destroy_workqueue(sony_laptop_input.wq); + +err_free_kfifo: + kfifo_free(sony_laptop_input.fifo); + +err_dec_users: + atomic_dec(&sony_laptop_input.users); + return error; +} + +static void sony_laptop_remove_input(void) +{ + /* cleanup only after the last user has gone */ + if (!atomic_dec_and_test(&sony_laptop_input.users)) + return; + + /* flush workqueue first */ + flush_workqueue(sony_laptop_input.wq); + + /* destroy input devs */ + input_unregister_device(sony_laptop_input.key_dev); + sony_laptop_input.key_dev = NULL; + + if (sony_laptop_input.jog_dev) { + input_unregister_device(sony_laptop_input.jog_dev); + sony_laptop_input.jog_dev = NULL; + } + + destroy_workqueue(sony_laptop_input.wq); + kfifo_free(sony_laptop_input.fifo); +} + +/*********** Platform Device ***********/ + +static atomic_t sony_pf_users = ATOMIC_INIT(0); +static struct platform_driver sony_pf_driver = { + .driver = { + .name = "sony-laptop", + .owner = THIS_MODULE, + } +}; +static struct platform_device *sony_pf_device; + +static int sony_pf_add(void) +{ + int ret = 0; + + /* don't run again if already initialized */ + if (atomic_add_return(1, &sony_pf_users) > 1) + return 0; + + ret = platform_driver_register(&sony_pf_driver); + if (ret) + goto out; + + sony_pf_device = platform_device_alloc("sony-laptop", -1); + if (!sony_pf_device) { + ret = -ENOMEM; + goto out_platform_registered; + } + + ret = platform_device_add(sony_pf_device); + if (ret) + goto out_platform_alloced; + + return 0; + + out_platform_alloced: + platform_device_put(sony_pf_device); + sony_pf_device = NULL; + out_platform_registered: + platform_driver_unregister(&sony_pf_driver); + out: + atomic_dec(&sony_pf_users); + return ret; +} + +static void sony_pf_remove(void) +{ + /* deregister only after the last user has gone */ + if (!atomic_dec_and_test(&sony_pf_users)) + return; + + platform_device_del(sony_pf_device); + platform_device_put(sony_pf_device); + platform_driver_unregister(&sony_pf_driver); +} + +/*********** SNC (SNY5001) Device ***********/ + +/* the device uses 1-based values, while the backlight subsystem uses + 0-based values */ +#define SONY_MAX_BRIGHTNESS 8 + +#define SNC_VALIDATE_IN 0 +#define SNC_VALIDATE_OUT 1 + +static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *, char *); -static ssize_t sony_acpi_store(struct device *, struct device_attribute *, +static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *, const char *, size_t); static int boolean_validate(const int, const int); static int brightness_default_validate(const int, const int); -#define SNC_VALIDATE_IN 0 -#define SNC_VALIDATE_OUT 1 - -struct sony_acpi_value { +struct sony_nc_value { char *name; /* name of the entry */ char **acpiget; /* names of the ACPI get function */ char **acpiset; /* names of the ACPI set function */ @@ -75,65 +458,65 @@ struct sony_acpi_value { struct device_attribute devattr; /* sysfs atribute */ }; -#define HANDLE_NAMES(_name, _values...) \ +#define SNC_HANDLE_NAMES(_name, _values...) \ static char *snc_##_name[] = { _values, NULL } -#define SONY_ACPI_VALUE(_name, _getters, _setters, _validate, _debug) \ +#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \ { \ .name = __stringify(_name), \ .acpiget = _getters, \ .acpiset = _setters, \ .validate = _validate, \ .debug = _debug, \ - .devattr = __ATTR(_name, 0, sony_acpi_show, sony_acpi_store), \ + .devattr = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \ } -#define SONY_ACPI_VALUE_NULL { .name = NULL } +#define SNC_HANDLE_NULL { .name = NULL } -HANDLE_NAMES(fnkey_get, "GHKE"); +SNC_HANDLE_NAMES(fnkey_get, "GHKE"); -HANDLE_NAMES(brightness_def_get, "GPBR"); -HANDLE_NAMES(brightness_def_set, "SPBR"); +SNC_HANDLE_NAMES(brightness_def_get, "GPBR"); +SNC_HANDLE_NAMES(brightness_def_set, "SPBR"); -HANDLE_NAMES(cdpower_get, "GCDP"); -HANDLE_NAMES(cdpower_set, "SCDP", "CDPW"); +SNC_HANDLE_NAMES(cdpower_get, "GCDP"); +SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW"); -HANDLE_NAMES(audiopower_get, "GAZP"); -HANDLE_NAMES(audiopower_set, "AZPW"); +SNC_HANDLE_NAMES(audiopower_get, "GAZP"); +SNC_HANDLE_NAMES(audiopower_set, "AZPW"); -HANDLE_NAMES(lanpower_get, "GLNP"); -HANDLE_NAMES(lanpower_set, "LNPW"); +SNC_HANDLE_NAMES(lanpower_get, "GLNP"); +SNC_HANDLE_NAMES(lanpower_set, "LNPW"); -HANDLE_NAMES(PID_get, "GPID"); +SNC_HANDLE_NAMES(PID_get, "GPID"); -HANDLE_NAMES(CTR_get, "GCTR"); -HANDLE_NAMES(CTR_set, "SCTR"); +SNC_HANDLE_NAMES(CTR_get, "GCTR"); +SNC_HANDLE_NAMES(CTR_set, "SCTR"); -HANDLE_NAMES(PCR_get, "GPCR"); -HANDLE_NAMES(PCR_set, "SPCR"); +SNC_HANDLE_NAMES(PCR_get, "GPCR"); +SNC_HANDLE_NAMES(PCR_set, "SPCR"); -HANDLE_NAMES(CMI_get, "GCMI"); -HANDLE_NAMES(CMI_set, "SCMI"); +SNC_HANDLE_NAMES(CMI_get, "GCMI"); +SNC_HANDLE_NAMES(CMI_set, "SCMI"); -static struct sony_acpi_value sony_acpi_values[] = { - SONY_ACPI_VALUE(brightness_default, snc_brightness_def_get, +static struct sony_nc_value sony_nc_values[] = { + SNC_HANDLE(brightness_default, snc_brightness_def_get, snc_brightness_def_set, brightness_default_validate, 0), - SONY_ACPI_VALUE(fnkey, snc_fnkey_get, NULL, NULL, 0), - SONY_ACPI_VALUE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0), - SONY_ACPI_VALUE(audiopower, snc_audiopower_get, snc_audiopower_set, + SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0), + SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0), + SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set, boolean_validate, 0), - SONY_ACPI_VALUE(lanpower, snc_lanpower_get, snc_lanpower_set, + SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set, boolean_validate, 1), /* unknown methods */ - SONY_ACPI_VALUE(PID, snc_PID_get, NULL, NULL, 1), - SONY_ACPI_VALUE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1), - SONY_ACPI_VALUE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1), - SONY_ACPI_VALUE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1), - SONY_ACPI_VALUE_NULL + SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1), + SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1), + SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1), + SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1), + SNC_HANDLE_NULL }; -static acpi_handle sony_acpi_handle; -static struct acpi_device *sony_acpi_acpi_device = NULL; +static acpi_handle sony_nc_acpi_handle; +static struct acpi_device *sony_nc_acpi_device = NULL; /* * acpi_evaluate_object wrappers @@ -153,7 +536,7 @@ static int acpi_callgetfunc(acpi_handle return 0; } - printk(LOG_PFX "acpi_callreadfunc failed\n"); + printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n"); return -1; } @@ -179,7 +562,7 @@ static int acpi_callsetfunc(acpi_handle if (status == AE_OK) { if (result != NULL) { if (out_obj.type != ACPI_TYPE_INTEGER) { - printk(LOG_PFX "acpi_evaluate_object bad " + printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad " "return type\n"); return -1; } @@ -188,13 +571,13 @@ static int acpi_callsetfunc(acpi_handle return 0; } - printk(LOG_PFX "acpi_evaluate_object failed\n"); + printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n"); return -1; } /* - * sony_acpi_values input/output validate functions + * sony_nc_values input/output validate functions */ /* brightness_default_validate: @@ -229,19 +612,19 @@ static int boolean_validate(const int di } /* - * Sysfs show/store common to all sony_acpi_values + * Sysfs show/store common to all sony_nc_values */ -static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr, +static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, char *buffer) { int value; - struct sony_acpi_value *item = - container_of(attr, struct sony_acpi_value, devattr); + struct sony_nc_value *item = + container_of(attr, struct sony_nc_value, devattr); if (!*item->acpiget) return -EIO; - if (acpi_callgetfunc(sony_acpi_handle, *item->acpiget, &value) < 0) + if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0) return -EIO; if (item->validate) @@ -250,13 +633,13 @@ static ssize_t sony_acpi_show(struct dev return snprintf(buffer, PAGE_SIZE, "%d\n", value); } -static ssize_t sony_acpi_store(struct device *dev, +static ssize_t sony_nc_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { int value; - struct sony_acpi_value *item = - container_of(attr, struct sony_acpi_value, devattr); + struct sony_nc_value *item = + container_of(attr, struct sony_nc_value, devattr); if (!item->acpiset) return -EIO; @@ -272,118 +655,20 @@ static ssize_t sony_acpi_store(struct de if (value < 0) return value; - if (acpi_callsetfunc(sony_acpi_handle, *item->acpiset, value, NULL) < 0) + if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0) return -EIO; item->value = value; item->valid = 1; return count; } -/* - * Platform device - */ -static struct platform_driver sncpf_driver = { - .driver = { - .name = "sony-laptop", - .owner = THIS_MODULE, - } -}; -static struct platform_device *sncpf_device; - -static int sony_snc_pf_add(void) -{ - acpi_handle handle; - struct sony_acpi_value *item; - int ret = 0; - - ret = platform_driver_register(&sncpf_driver); - if (ret) - goto out; - - sncpf_device = platform_device_alloc("sony-laptop", -1); - if (!sncpf_device) { - ret = -ENOMEM; - goto out_platform_registered; - } - - ret = platform_device_add(sncpf_device); - if (ret) - goto out_platform_alloced; - - for (item = sony_acpi_values; item->name; ++item) { - - if (!debug && item->debug) - continue; - - /* find the available acpiget as described in the DSDT */ - for (; item->acpiget && *item->acpiget; ++item->acpiget) { - if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, - *item->acpiget, - &handle))) { - if (debug) - printk(LOG_PFX "Found %s getter: %s\n", - item->name, *item->acpiget); - item->devattr.attr.mode |= S_IRUGO; - break; - } - } - - /* find the available acpiset as described in the DSDT */ - for (; item->acpiset && *item->acpiset; ++item->acpiset) { - if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, - *item->acpiset, - &handle))) { - if (debug) - printk(LOG_PFX "Found %s setter: %s\n", - item->name, *item->acpiset); - item->devattr.attr.mode |= S_IWUSR; - break; - } - } - - if (item->devattr.attr.mode != 0) { - ret = - device_create_file(&sncpf_device->dev, - &item->devattr); - if (ret) - goto out_sysfs; - } - } - - return 0; - - out_sysfs: - for (item = sony_acpi_values; item->name; ++item) { - device_remove_file(&sncpf_device->dev, &item->devattr); - } - platform_device_del(sncpf_device); - out_platform_alloced: - platform_device_put(sncpf_device); - out_platform_registered: - platform_driver_unregister(&sncpf_driver); - out: - return ret; -} - -static void sony_snc_pf_remove(void) -{ - struct sony_acpi_value *item; - - for (item = sony_acpi_values; item->name; ++item) { - device_remove_file(&sncpf_device->dev, &item->devattr); - } - - platform_device_del(sncpf_device); - platform_device_put(sncpf_device); - platform_driver_unregister(&sncpf_driver); -} /* * Backlight device */ static int sony_backlight_update_status(struct backlight_device *bd) { - return acpi_callsetfunc(sony_acpi_handle, "SBRT", + return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", bd->props.brightness + 1, NULL); } @@ -391,7 +676,7 @@ static int sony_backlight_get_brightness { int value; - if (acpi_callgetfunc(sony_acpi_handle, "GBRT", &value)) + if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) return 0; /* brightness levels are 1-based, while backlight ones are 0-based */ return value - 1; @@ -408,9 +693,9 @@ static struct backlight_ops sony_backlig */ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) { - if (debug) - printk(LOG_PFX "sony_acpi_notify, event: %d\n", event); - acpi_bus_generate_event(sony_acpi_acpi_device, 1, event); + dprintk("sony_acpi_notify, event: %d\n", event); + sony_laptop_report_input_event(event); + acpi_bus_generate_event(sony_nc_acpi_device, 1, event); } static acpi_status sony_walk_callback(acpi_handle handle, u32 level, @@ -422,7 +707,7 @@ static acpi_status sony_walk_callback(ac node = (struct acpi_namespace_node *)handle; operand = (union acpi_operand_object *)node->object; - printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii, + printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii, (u32) operand->method.param_count); return AE_OK; @@ -431,16 +716,16 @@ static acpi_status sony_walk_callback(ac /* * ACPI device */ -static int sony_acpi_resume(struct acpi_device *device) +static int sony_nc_resume(struct acpi_device *device) { - struct sony_acpi_value *item; + struct sony_nc_value *item; - for (item = sony_acpi_values; item->name; item++) { + for (item = sony_nc_values; item->name; item++) { int ret; if (!item->valid) continue; - ret = acpi_callsetfunc(sony_acpi_handle, *item->acpiset, + ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, item->value, NULL); if (ret < 0) { printk("%s: %d\n", __FUNCTION__, ret); @@ -450,42 +735,55 @@ static int sony_acpi_resume(struct acpi_ return 0; } -static int sony_acpi_add(struct acpi_device *device) +static int sony_nc_add(struct acpi_device *device) { acpi_status status; int result = 0; acpi_handle handle; + struct sony_nc_value *item; + + printk(KERN_INFO DRV_PFX "%s v%s.\n", + SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); - sony_acpi_acpi_device = device; + sony_nc_acpi_device = device; + strcpy(acpi_device_class(device), "sony/hotkey"); - sony_acpi_handle = device->handle; + sony_nc_acpi_handle = device->handle; if (debug) { - status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle, + status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, 1, sony_walk_callback, NULL, NULL); if (ACPI_FAILURE(status)) { - printk(LOG_PFX "unable to walk acpi resources\n"); + printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); result = -ENODEV; goto outwalk; } } - status = acpi_install_notify_handler(sony_acpi_handle, + /* setup input devices and helper fifo */ + result = sony_laptop_setup_input(); + if (result) { + printk(KERN_ERR DRV_PFX + "Unabe to create input devices.\n"); + goto outwalk; + } + + status = acpi_install_notify_handler(sony_nc_acpi_handle, ACPI_DEVICE_NOTIFY, sony_acpi_notify, NULL); if (ACPI_FAILURE(status)) { - printk(LOG_PFX "unable to install notify handler\n"); + printk(KERN_WARNING DRV_PFX "unable to install notify handler\n"); result = -ENODEV; - goto outwalk; + goto outinput; } - if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) { + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { sony_backlight_device = backlight_device_register("sony", NULL, NULL, &sony_backlight_ops); if (IS_ERR(sony_backlight_device)) { - printk(LOG_PFX "unable to register backlight device\n"); + printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); sony_backlight_device = NULL; } else { sony_backlight_device->props.brightness = @@ -497,68 +795,1497 @@ static int sony_acpi_add(struct acpi_dev } - if (sony_snc_pf_add()) + result = sony_pf_add(); + if (result) goto outbacklight; - printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n"); + /* create sony_pf sysfs attributes related to the SNC device */ + for (item = sony_nc_values; item->name; ++item) { + + if (!debug && item->debug) + continue; + + /* find the available acpiget as described in the DSDT */ + for (; item->acpiget && *item->acpiget; ++item->acpiget) { + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, + *item->acpiget, + &handle))) { + dprintk("Found %s getter: %s\n", + item->name, *item->acpiget); + item->devattr.attr.mode |= S_IRUGO; + break; + } + } + + /* find the available acpiset as described in the DSDT */ + for (; item->acpiset && *item->acpiset; ++item->acpiset) { + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, + *item->acpiset, + &handle))) { + dprintk("Found %s setter: %s\n", + item->name, *item->acpiset); + item->devattr.attr.mode |= S_IWUSR; + break; + } + } + + if (item->devattr.attr.mode != 0) { + result = + device_create_file(&sony_pf_device->dev, + &item->devattr); + if (result) + goto out_sysfs; + } + } return 0; + out_sysfs: + for (item = sony_nc_values; item->name; ++item) { + device_remove_file(&sony_pf_device->dev, &item->devattr); + } + sony_pf_remove(); + outbacklight: if (sony_backlight_device) backlight_device_unregister(sony_backlight_device); - status = acpi_remove_notify_handler(sony_acpi_handle, + status = acpi_remove_notify_handler(sony_nc_acpi_handle, ACPI_DEVICE_NOTIFY, sony_acpi_notify); if (ACPI_FAILURE(status)) - printk(LOG_PFX "unable to remove notify handler\n"); + printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n"); + + outinput: + sony_laptop_remove_input(); + outwalk: return result; } -static int sony_acpi_remove(struct acpi_device *device, int type) +static int sony_nc_remove(struct acpi_device *device, int type) { acpi_status status; + struct sony_nc_value *item; if (sony_backlight_device) backlight_device_unregister(sony_backlight_device); - sony_acpi_acpi_device = NULL; + sony_nc_acpi_device = NULL; - status = acpi_remove_notify_handler(sony_acpi_handle, + status = acpi_remove_notify_handler(sony_nc_acpi_handle, ACPI_DEVICE_NOTIFY, sony_acpi_notify); if (ACPI_FAILURE(status)) - printk(LOG_PFX "unable to remove notify handler\n"); + printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n"); - sony_snc_pf_remove(); + for (item = sony_nc_values; item->name; ++item) { + device_remove_file(&sony_pf_device->dev, &item->devattr); + } - printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n"); + sony_pf_remove(); + sony_laptop_remove_input(); + dprintk(SONY_NC_DRIVER_NAME " removed.\n"); return 0; } -static struct acpi_driver sony_acpi_driver = { - .name = ACPI_SNC_DRIVER_NAME, - .class = ACPI_SNC_CLASS, - .ids = ACPI_SNC_HID, +static struct acpi_driver sony_nc_driver = { + .name = SONY_NC_DRIVER_NAME, + .class = SONY_NC_CLASS, + .ids = SONY_NC_HID, + .owner = THIS_MODULE, .ops = { - .add = sony_acpi_add, - .remove = sony_acpi_remove, - .resume = sony_acpi_resume, + .add = sony_nc_add, + .remove = sony_nc_remove, + .resume = sony_nc_resume, }, }; -static int __init sony_acpi_init(void) +/*********** SPIC (SNY6001) Device ***********/ + +#define SONYPI_DEVICE_TYPE1 0x00000001 +#define SONYPI_DEVICE_TYPE2 0x00000002 +#define SONYPI_DEVICE_TYPE3 0x00000004 + +#define SONY_PIC_EV_MASK 0xff + +struct sony_pic_ioport { + struct acpi_resource_io io; + struct list_head list; +}; + +struct sony_pic_irq { + struct acpi_resource_irq irq; + struct list_head list; +}; + +struct sony_pic_dev { + int model; + u8 camera_power; + u8 bluetooth_power; + u8 wwan_power; + struct acpi_device *acpi_dev; + struct sony_pic_irq *cur_irq; + struct sony_pic_ioport *cur_ioport; + struct list_head interrupts; + struct list_head ioports; + struct mutex lock; +}; + +static struct sony_pic_dev spic_dev = { + .interrupts = LIST_HEAD_INIT(spic_dev.interrupts), + .ioports = LIST_HEAD_INIT(spic_dev.ioports), +}; + +/* Event masks */ +#define SONYPI_JOGGER_MASK 0x00000001 +#define SONYPI_CAPTURE_MASK 0x00000002 +#define SONYPI_FNKEY_MASK 0x00000004 +#define SONYPI_BLUETOOTH_MASK 0x00000008 +#define SONYPI_PKEY_MASK 0x00000010 +#define SONYPI_BACK_MASK 0x00000020 +#define SONYPI_HELP_MASK 0x00000040 +#define SONYPI_LID_MASK 0x00000080 +#define SONYPI_ZOOM_MASK 0x00000100 +#define SONYPI_THUMBPHRASE_MASK 0x00000200 +#define SONYPI_MEYE_MASK 0x00000400 +#define SONYPI_MEMORYSTICK_MASK 0x00000800 +#define SONYPI_BATTERY_MASK 0x00001000 +#define SONYPI_WIRELESS_MASK 0x00002000 + +struct sonypi_event { + u8 data; + u8 event; +}; + +/* The set of possible button release events */ +static struct sonypi_event sonypi_releaseev[] = { + { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0, 0 } +}; + +/* The set of possible jogger events */ +static struct sonypi_event sonypi_joggerev[] = { + { 0x1f, SONYPI_EVENT_JOGDIAL_UP }, + { 0x01, SONYPI_EVENT_JOGDIAL_DOWN }, + { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED }, + { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED }, + { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP }, + { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN }, + { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED }, + { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED }, + { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP }, + { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN }, + { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED }, + { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED }, + { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED }, + { 0, 0 } +}; + +/* The set of possible capture button events */ +static struct sonypi_event sonypi_captureev[] = { + { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED }, + { 0x07, SONYPI_EVENT_CAPTURE_PRESSED }, + { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED }, + { 0, 0 } +}; + +/* The set of possible fnkeys events */ +static struct sonypi_event sonypi_fnkeyev[] = { + { 0x10, SONYPI_EVENT_FNKEY_ESC }, + { 0x11, SONYPI_EVENT_FNKEY_F1 }, + { 0x12, SONYPI_EVENT_FNKEY_F2 }, + { 0x13, SONYPI_EVENT_FNKEY_F3 }, + { 0x14, SONYPI_EVENT_FNKEY_F4 }, + { 0x15, SONYPI_EVENT_FNKEY_F5 }, + { 0x16, SONYPI_EVENT_FNKEY_F6 }, + { 0x17, SONYPI_EVENT_FNKEY_F7 }, + { 0x18, SONYPI_EVENT_FNKEY_F8 }, + { 0x19, SONYPI_EVENT_FNKEY_F9 }, + { 0x1a, SONYPI_EVENT_FNKEY_F10 }, + { 0x1b, SONYPI_EVENT_FNKEY_F11 }, + { 0x1c, SONYPI_EVENT_FNKEY_F12 }, + { 0x1f, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x21, SONYPI_EVENT_FNKEY_1 }, + { 0x22, SONYPI_EVENT_FNKEY_2 }, + { 0x31, SONYPI_EVENT_FNKEY_D }, + { 0x32, SONYPI_EVENT_FNKEY_E }, + { 0x33, SONYPI_EVENT_FNKEY_F }, + { 0x34, SONYPI_EVENT_FNKEY_S }, + { 0x35, SONYPI_EVENT_FNKEY_B }, + { 0x36, SONYPI_EVENT_FNKEY_ONLY }, + { 0, 0 } +}; + +/* The set of possible program key events */ +static struct sonypi_event sonypi_pkeyev[] = { + { 0x01, SONYPI_EVENT_PKEY_P1 }, + { 0x02, SONYPI_EVENT_PKEY_P2 }, + { 0x04, SONYPI_EVENT_PKEY_P3 }, + { 0x5c, SONYPI_EVENT_PKEY_P1 }, + { 0, 0 } +}; + +/* The set of possible bluetooth events */ +static struct sonypi_event sonypi_blueev[] = { + { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED }, + { 0x59, SONYPI_EVENT_BLUETOOTH_ON }, + { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF }, + { 0, 0 } +}; + +/* The set of possible wireless events */ +static struct sonypi_event sonypi_wlessev[] = { + { 0x59, SONYPI_EVENT_WIRELESS_ON }, + { 0x5a, SONYPI_EVENT_WIRELESS_OFF }, + { 0, 0 } +}; + +/* The set of possible back button events */ +static struct sonypi_event sonypi_backev[] = { + { 0x20, SONYPI_EVENT_BACK_PRESSED }, + { 0, 0 } +}; + +/* The set of possible help button events */ +static struct sonypi_event sonypi_helpev[] = { + { 0x3b, SONYPI_EVENT_HELP_PRESSED }, + { 0, 0 } +}; + + +/* The set of possible lid events */ +static struct sonypi_event sonypi_lidev[] = { + { 0x51, SONYPI_EVENT_LID_CLOSED }, + { 0x50, SONYPI_EVENT_LID_OPENED }, + { 0, 0 } +}; + +/* The set of possible zoom events */ +static struct sonypi_event sonypi_zoomev[] = { + { 0x39, SONYPI_EVENT_ZOOM_PRESSED }, + { 0, 0 } +}; + +/* The set of possible thumbphrase events */ +static struct sonypi_event sonypi_thumbphraseev[] = { + { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED }, + { 0, 0 } +}; + +/* The set of possible motioneye camera events */ +static struct sonypi_event sonypi_meyeev[] = { + { 0x00, SONYPI_EVENT_MEYE_FACE }, + { 0x01, SONYPI_EVENT_MEYE_OPPOSITE }, + { 0, 0 } +}; + +/* The set of possible memorystick events */ +static struct sonypi_event sonypi_memorystickev[] = { + { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT }, + { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT }, + { 0, 0 } +}; + +/* The set of possible battery events */ +static struct sonypi_event sonypi_batteryev[] = { + { 0x20, SONYPI_EVENT_BATTERY_INSERT }, + { 0x30, SONYPI_EVENT_BATTERY_REMOVE }, + { 0, 0 } +}; + +static struct sonypi_eventtypes { + int model; + u8 data; + unsigned long mask; + struct sonypi_event * events; +} sony_pic_eventtypes[] = { + { SONYPI_DEVICE_TYPE1, 0, 0xffffffff, sonypi_releaseev }, + { SONYPI_DEVICE_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev }, + { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev }, + { SONYPI_DEVICE_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { SONYPI_DEVICE_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { SONYPI_DEVICE_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev }, + + { SONYPI_DEVICE_TYPE2, 0, 0xffffffff, sonypi_releaseev }, + { SONYPI_DEVICE_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev }, + { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { SONYPI_DEVICE_TYPE2, 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { SONYPI_DEVICE_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev }, + { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev }, + { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev }, + { SONYPI_DEVICE_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev }, + { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { SONYPI_DEVICE_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, + + { SONYPI_DEVICE_TYPE3, 0, 0xffffffff, sonypi_releaseev }, + { SONYPI_DEVICE_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev }, + { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { SONYPI_DEVICE_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { 0 } +}; + +static int sony_pic_detect_device_type(void) +{ + struct pci_dev *pcidev; + int model = 0; + + if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL))) + model = SONYPI_DEVICE_TYPE1; + + else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH6_1, NULL))) + model = SONYPI_DEVICE_TYPE3; + + else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH7_1, NULL))) + model = SONYPI_DEVICE_TYPE3; + + else + model = SONYPI_DEVICE_TYPE2; + + if (pcidev) + pci_dev_put(pcidev); + + printk(KERN_INFO DRV_PFX "detected Type%d model\n", + model == SONYPI_DEVICE_TYPE1 ? 1 : + model == SONYPI_DEVICE_TYPE2 ? 2 : 3); + return model; +} + +#define ITERATIONS_LONG 10000 +#define ITERATIONS_SHORT 10 +#define wait_on_command(command, iterations) { \ + unsigned int n = iterations; \ + while (--n && (command)) \ + udelay(1); \ + if (!n) \ + dprintk("command failed at %s : %s (line %d)\n", \ + __FILE__, __FUNCTION__, __LINE__); \ +} + +static u8 sony_pic_call1(u8 dev) +{ + u8 v1, v2; + + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io.minimum + 4); + v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4); + v2 = inb_p(spic_dev.cur_ioport->io.minimum); + dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1); + return v2; +} + +static u8 sony_pic_call2(u8 dev, u8 fn) +{ + u8 v1; + + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + ITERATIONS_LONG); + outb(fn, spic_dev.cur_ioport->io.minimum); + v1 = inb_p(spic_dev.cur_ioport->io.minimum); + dprintk("sony_pic_call2: 0x%.4x\n", v1); + return v1; +} + +static u8 sony_pic_call3(u8 dev, u8 fn, u8 v) +{ + u8 v1; + + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); + outb(fn, spic_dev.cur_ioport->io.minimum); + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); + outb(v, spic_dev.cur_ioport->io.minimum); + v1 = inb_p(spic_dev.cur_ioport->io.minimum); + dprintk("sony_pic_call3: 0x%.4x\n", v1); + return v1; +} + +/* camera tests and poweron/poweroff */ +#define SONYPI_CAMERA_PICTURE 5 +#define SONYPI_CAMERA_CONTROL 0x10 + +#define SONYPI_CAMERA_BRIGHTNESS 0 +#define SONYPI_CAMERA_CONTRAST 1 +#define SONYPI_CAMERA_HUE 2 +#define SONYPI_CAMERA_COLOR 3 +#define SONYPI_CAMERA_SHARPNESS 4 + +#define SONYPI_CAMERA_EXPOSURE_MASK 0xC +#define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3 +#define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30 +#define SONYPI_CAMERA_MUTE_MASK 0x40 + +/* the rest don't need a loop until not 0xff */ +#define SONYPI_CAMERA_AGC 6 +#define SONYPI_CAMERA_AGC_MASK 0x30 +#define SONYPI_CAMERA_SHUTTER_MASK 0x7 + +#define SONYPI_CAMERA_SHUTDOWN_REQUEST 7 +#define SONYPI_CAMERA_CONTROL 0x10 + +#define SONYPI_CAMERA_STATUS 7 +#define SONYPI_CAMERA_STATUS_READY 0x2 +#define SONYPI_CAMERA_STATUS_POSITION 0x4 + +#define SONYPI_DIRECTION_BACKWARDS 0x4 + +#define SONYPI_CAMERA_REVISION 8 +#define SONYPI_CAMERA_ROMVERSION 9 + +static int __sony_pic_camera_ready(void) +{ + u8 v; + + v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS); + return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY)); +} + +static int __sony_pic_camera_off(void) +{ + if (!camera) { + printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + return -ENODEV; + } + + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, + SONYPI_CAMERA_MUTE_MASK), + ITERATIONS_SHORT); + + if (spic_dev.camera_power) { + sony_pic_call2(0x91, 0); + spic_dev.camera_power = 0; + } + return 0; +} + +static int __sony_pic_camera_on(void) { - return acpi_bus_register_driver(&sony_acpi_driver); + int i, j, x; + + if (!camera) { + printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + return -ENODEV; + } + + if (spic_dev.camera_power) + return 0; + + for (j = 5; j > 0; j--) { + + for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++) + msleep(10); + sony_pic_call1(0x93); + + for (i = 400; i > 0; i--) { + if (__sony_pic_camera_ready()) + break; + msleep(10); + } + if (i) + break; + } + + if (j == 0) { + printk(KERN_WARNING DRV_PFX "failed to power on camera\n"); + return -ENODEV; + } + + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL, + 0x5a), + ITERATIONS_SHORT); + + spic_dev.camera_power = 1; + return 0; +} + +/* External camera command (exported to the motion eye v4l driver) */ +int sony_pic_camera_command(int command, u8 value) +{ + if (!camera) + return -EIO; + + mutex_lock(&spic_dev.lock); + + switch (command) { + case SONY_PIC_COMMAND_SETCAMERA: + if (value) + __sony_pic_camera_on(); + else + __sony_pic_camera_off(); + break; + case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERACONTRAST: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERAHUE: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERACOLOR: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERASHARPNESS: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERAPICTURE: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERAAGC: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value), + ITERATIONS_SHORT); + break; + default: + printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n", + command); + break; + } + mutex_unlock(&spic_dev.lock); + return 0; +} +EXPORT_SYMBOL(sony_pic_camera_command); + +/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */ +static void sony_pic_set_wwanpower(u8 state) +{ + state = !!state; + mutex_lock(&spic_dev.lock); + if (spic_dev.wwan_power == state) { + mutex_unlock(&spic_dev.lock); + return; + } + sony_pic_call2(0xB0, state); + spic_dev.wwan_power = state; + mutex_unlock(&spic_dev.lock); +} + +static ssize_t sony_pic_wwanpower_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + if (count > 31) + return -EINVAL; + + value = simple_strtoul(buffer, NULL, 10); + sony_pic_set_wwanpower(value); + + return count; +} + +static ssize_t sony_pic_wwanpower_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count; + mutex_lock(&spic_dev.lock); + count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power); + mutex_unlock(&spic_dev.lock); + return count; +} + +/* bluetooth subsystem power state */ +static void __sony_pic_set_bluetoothpower(u8 state) +{ + state = !!state; + if (spic_dev.bluetooth_power == state) + return; + sony_pic_call2(0x96, state); + sony_pic_call1(0x82); + spic_dev.bluetooth_power = state; +} + +static ssize_t sony_pic_bluetoothpower_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + if (count > 31) + return -EINVAL; + + value = simple_strtoul(buffer, NULL, 10); + mutex_lock(&spic_dev.lock); + __sony_pic_set_bluetoothpower(value); + mutex_unlock(&spic_dev.lock); + + return count; +} + +static ssize_t sony_pic_bluetoothpower_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + mutex_lock(&spic_dev.lock); + count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power); + mutex_unlock(&spic_dev.lock); + return count; +} + +/* fan speed */ +/* FAN0 information (reverse engineered from ACPI tables) */ +#define SONY_PIC_FAN0_STATUS 0x93 +static int sony_pic_set_fanspeed(unsigned long value) +{ + return ec_write(SONY_PIC_FAN0_STATUS, value); +} + +static int sony_pic_get_fanspeed(u8 *value) +{ + return ec_read(SONY_PIC_FAN0_STATUS, value); +} + +static ssize_t sony_pic_fanspeed_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + if (count > 31) + return -EINVAL; + + value = simple_strtoul(buffer, NULL, 10); + if (sony_pic_set_fanspeed(value)) + return -EIO; + + return count; +} + +static ssize_t sony_pic_fanspeed_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + u8 value = 0; + if (sony_pic_get_fanspeed(&value)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", value); +} + +#define SPIC_ATTR(_name, _mode) \ +struct device_attribute spic_attr_##_name = __ATTR(_name, \ + _mode, sony_pic_## _name ##_show, \ + sony_pic_## _name ##_store) + +static SPIC_ATTR(bluetoothpower, 0644); +static SPIC_ATTR(wwanpower, 0644); +static SPIC_ATTR(fanspeed, 0644); + +static struct attribute *spic_attributes[] = { + &spic_attr_bluetoothpower.attr, + &spic_attr_wwanpower.attr, + &spic_attr_fanspeed.attr, + NULL +}; + +static struct attribute_group spic_attribute_group = { + .attrs = spic_attributes +}; + +/******** SONYPI compatibility **********/ +#ifdef CONFIG_SONY_LAPTOP_OLD + +/* battery / brightness / temperature addresses */ +#define SONYPI_BAT_FLAGS 0x81 +#define SONYPI_LCD_LIGHT 0x96 +#define SONYPI_BAT1_PCTRM 0xa0 +#define SONYPI_BAT1_LEFT 0xa2 +#define SONYPI_BAT1_MAXRT 0xa4 +#define SONYPI_BAT2_PCTRM 0xa8 +#define SONYPI_BAT2_LEFT 0xaa +#define SONYPI_BAT2_MAXRT 0xac +#define SONYPI_BAT1_MAXTK 0xb0 +#define SONYPI_BAT1_FULL 0xb2 +#define SONYPI_BAT2_MAXTK 0xb8 +#define SONYPI_BAT2_FULL 0xba +#define SONYPI_TEMP_STATUS 0xC1 + +struct sonypi_compat_s { + struct fasync_struct *fifo_async; + struct kfifo *fifo; + spinlock_t fifo_lock; + wait_queue_head_t fifo_proc_list; + atomic_t open_count; +}; +static struct sonypi_compat_s sonypi_compat = { + .open_count = ATOMIC_INIT(0), +}; + +static int sonypi_misc_fasync(int fd, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async); + if (retval < 0) + return retval; + return 0; +} + +static int sonypi_misc_release(struct inode *inode, struct file *file) +{ + sonypi_misc_fasync(-1, file, 0); + atomic_dec(&sonypi_compat.open_count); + return 0; +} + +static int sonypi_misc_open(struct inode *inode, struct file *file) +{ + /* Flush input queue on first open */ + if (atomic_inc_return(&sonypi_compat.open_count) == 1) + kfifo_reset(sonypi_compat.fifo); + return 0; +} + +static ssize_t sonypi_misc_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + ssize_t ret; + unsigned char c; + + if ((kfifo_len(sonypi_compat.fifo) == 0) && + (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + ret = wait_event_interruptible(sonypi_compat.fifo_proc_list, + kfifo_len(sonypi_compat.fifo) != 0); + if (ret) + return ret; + + while (ret < count && + (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) { + if (put_user(c, buf++)) + return -EFAULT; + ret++; + } + + if (ret > 0) { + struct inode *inode = file->f_path.dentry->d_inode; + inode->i_atime = current_fs_time(inode->i_sb); + } + + return ret; +} + +static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &sonypi_compat.fifo_proc_list, wait); + if (kfifo_len(sonypi_compat.fifo)) + return POLLIN | POLLRDNORM; + return 0; +} + +static int ec_read16(u8 addr, u16 *value) +{ + u8 val_lb, val_hb; + if (ec_read(addr, &val_lb)) + return -1; + if (ec_read(addr + 1, &val_hb)) + return -1; + *value = val_lb | (val_hb << 8); + return 0; +} + +static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + u8 val8; + u16 val16; + int value; + + mutex_lock(&spic_dev.lock); + switch (cmd) { + case SONYPI_IOCGBRT: + if (sony_backlight_device == NULL) { + ret = -EIO; + break; + } + if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) { + ret = -EIO; + break; + } + val8 = ((value & 0xff) - 1) << 5; + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCSBRT: + if (sony_backlight_device == NULL) { + ret = -EIO; + break; + } + if (copy_from_user(&val8, argp, sizeof(val8))) { + ret = -EFAULT; + break; + } + if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", + (val8 >> 5) + 1, NULL)) { + ret = -EIO; + break; + } + /* sync the backlight device status */ + sony_backlight_device->props.brightness = + sony_backlight_get_brightness(sony_backlight_device); + break; + case SONYPI_IOCGBAT1CAP: + if (ec_read16(SONYPI_BAT1_FULL, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBAT1REM: + if (ec_read16(SONYPI_BAT1_LEFT, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBAT2CAP: + if (ec_read16(SONYPI_BAT2_FULL, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBAT2REM: + if (ec_read16(SONYPI_BAT2_LEFT, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBATFLAGS: + if (ec_read(SONYPI_BAT_FLAGS, &val8)) { + ret = -EIO; + break; + } + val8 &= 0x07; + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCGBLUE: + val8 = spic_dev.bluetooth_power; + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCSBLUE: + if (copy_from_user(&val8, argp, sizeof(val8))) { + ret = -EFAULT; + break; + } + __sony_pic_set_bluetoothpower(val8); + break; + /* FAN Controls */ + case SONYPI_IOCGFAN: + if (sony_pic_get_fanspeed(&val8)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCSFAN: + if (copy_from_user(&val8, argp, sizeof(val8))) { + ret = -EFAULT; + break; + } + if (sony_pic_set_fanspeed(val8)) + ret = -EIO; + break; + /* GET Temperature (useful under APM) */ + case SONYPI_IOCGTEMP: + if (ec_read(SONYPI_TEMP_STATUS, &val8)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&spic_dev.lock); + return ret; +} + +static const struct file_operations sonypi_misc_fops = { + .owner = THIS_MODULE, + .read = sonypi_misc_read, + .poll = sonypi_misc_poll, + .open = sonypi_misc_open, + .release = sonypi_misc_release, + .fasync = sonypi_misc_fasync, + .ioctl = sonypi_misc_ioctl, +}; + +static struct miscdevice sonypi_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sonypi", + .fops = &sonypi_misc_fops, +}; + +static void sonypi_compat_report_event(u8 event) +{ + kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event)); + kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN); + wake_up_interruptible(&sonypi_compat.fifo_proc_list); +} + +static int sonypi_compat_init(void) +{ + int error; + + spin_lock_init(&sonypi_compat.fifo_lock); + sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, + &sonypi_compat.fifo_lock); + if (IS_ERR(sonypi_compat.fifo)) { + printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + return PTR_ERR(sonypi_compat.fifo); + } + + init_waitqueue_head(&sonypi_compat.fifo_proc_list); + + if (minor != -1) + sonypi_misc_device.minor = minor; + error = misc_register(&sonypi_misc_device); + if (error) { + printk(KERN_ERR DRV_PFX "misc_register failed\n"); + goto err_free_kfifo; + } + if (minor == -1) + printk(KERN_INFO DRV_PFX "device allocated minor is %d\n", + sonypi_misc_device.minor); + + return 0; + +err_free_kfifo: + kfifo_free(sonypi_compat.fifo); + return error; +} + +static void sonypi_compat_exit(void) +{ + misc_deregister(&sonypi_misc_device); + kfifo_free(sonypi_compat.fifo); +} +#else +static int sonypi_compat_init(void) { return 0; } +static void sonypi_compat_exit(void) { } +static void sonypi_compat_report_event(u8 event) { } +#endif /* CONFIG_SONY_LAPTOP_OLD */ + +/* + * ACPI callbacks + */ +static acpi_status +sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) +{ + u32 i; + struct sony_pic_dev *dev = (struct sony_pic_dev *)context; + + switch (resource->type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + return AE_OK; + + case ACPI_RESOURCE_TYPE_IRQ: + { + struct acpi_resource_irq *p = &resource->data.irq; + struct sony_pic_irq *interrupt = NULL; + if (!p || !p->interrupt_count) { + /* + * IRQ descriptors may have no IRQ# bits set, + * particularly those those w/ _STA disabled + */ + dprintk("Blank IRQ resource\n"); + return AE_OK; + } + for (i = 0; i < p->interrupt_count; i++) { + if (!p->interrupts[i]) { + printk(KERN_WARNING DRV_PFX + "Invalid IRQ %d\n", + p->interrupts[i]); + continue; + } + interrupt = kzalloc(sizeof(*interrupt), + GFP_KERNEL); + if (!interrupt) + return AE_ERROR; + + list_add_tail(&interrupt->list, &dev->interrupts); + interrupt->irq.triggering = p->triggering; + interrupt->irq.polarity = p->polarity; + interrupt->irq.sharable = p->sharable; + interrupt->irq.interrupt_count = 1; + interrupt->irq.interrupts[0] = p->interrupts[i]; + } + return AE_OK; + } + case ACPI_RESOURCE_TYPE_IO: + { + struct acpi_resource_io *io = &resource->data.io; + struct sony_pic_ioport *ioport = NULL; + if (!io) { + dprintk("Blank IO resource\n"); + return AE_OK; + } + + ioport = kzalloc(sizeof(*ioport), GFP_KERNEL); + if (!ioport) + return AE_ERROR; + + list_add_tail(&ioport->list, &dev->ioports); + memcpy(&ioport->io, io, sizeof(*io)); + return AE_OK; + } + default: + dprintk("Resource %d isn't an IRQ nor an IO port\n", + resource->type); + + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + } + return AE_CTRL_TERMINATE; +} + +static int sony_pic_possible_resources(struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + + if (!device) + return -EINVAL; + + /* get device status */ + /* see acpi_pci_link_get_current acpi_pci_link_get_possible */ + dprintk("Evaluating _STA\n"); + result = acpi_bus_get_status(device); + if (result) { + printk(KERN_WARNING DRV_PFX "Unable to read status\n"); + goto end; + } + + if (!device->status.enabled) + dprintk("Device disabled\n"); + else + dprintk("Device enabled\n"); + + /* + * Query and parse 'method' + */ + dprintk("Evaluating %s\n", METHOD_NAME__PRS); + status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, + sony_pic_read_possible_resource, &spic_dev); + if (ACPI_FAILURE(status)) { + printk(KERN_WARNING DRV_PFX + "Failure evaluating %s\n", + METHOD_NAME__PRS); + result = -ENODEV; + } +end: + return result; +} + +/* + * Disable the spic device by calling its _DIS method + */ +static int sony_pic_disable(struct acpi_device *device) +{ + if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", 0, NULL))) + return -ENXIO; + + dprintk("Device disabled\n"); + return 0; +} + + +/* + * Based on drivers/acpi/pci_link.c:acpi_pci_link_set + * + * Call _SRS to set current resources + */ +static int sony_pic_enable(struct acpi_device *device, + struct sony_pic_ioport *ioport, struct sony_pic_irq *irq) +{ + acpi_status status; + int result = 0; + struct { + struct acpi_resource io_res; + struct acpi_resource irq_res; + struct acpi_resource end; + } *resource; + struct acpi_buffer buffer = { 0, NULL }; + + if (!ioport || !irq) + return -EINVAL; + + /* init acpi_buffer */ + resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL); + if (!resource) + return -ENOMEM; + + buffer.length = sizeof(*resource) + 1; + buffer.pointer = resource; + + /* setup io resource */ + resource->io_res.type = ACPI_RESOURCE_TYPE_IO; + resource->io_res.length = sizeof(struct acpi_resource); + memcpy(&resource->io_res.data.io, &ioport->io, + sizeof(struct acpi_resource_io)); + + /* setup irq resource */ + resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ; + resource->irq_res.length = sizeof(struct acpi_resource); + memcpy(&resource->irq_res.data.irq, &irq->irq, + sizeof(struct acpi_resource_irq)); + /* we requested a shared irq */ + resource->irq_res.data.irq.sharable = ACPI_SHARED; + + resource->end.type = ACPI_RESOURCE_TYPE_END_TAG; + + /* Attempt to set the resource */ + dprintk("Evaluating _SRS\n"); + status = acpi_set_current_resources(device->handle, &buffer); + + /* check for total failure */ + if (ACPI_FAILURE(status)) { + printk(KERN_ERR DRV_PFX "Error evaluating _SRS"); + result = -ENODEV; + goto end; + } + + /* Necessary device initializations calls (from sonypi) */ + sony_pic_call1(0x82); + sony_pic_call2(0x81, 0xff); + sony_pic_call1(compat ? 0x92 : 0x82); + +end: + kfree(resource); + return result; +} + +/***************** + * + * ISR: some event is available + * + *****************/ +static irqreturn_t sony_pic_irq(int irq, void *dev_id) +{ + int i, j; + u32 port_val = 0; + u8 ev = 0; + u8 data_mask = 0; + u8 device_event = 0; + + struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id; + + acpi_os_read_port(dev->cur_ioport->io.minimum, &port_val, + dev->cur_ioport->io.address_length); + ev = port_val & SONY_PIC_EV_MASK; + data_mask = 0xff & (port_val >> (dev->cur_ioport->io.address_length - 8)); + + dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n", + port_val, ev, data_mask, dev->cur_ioport->io.minimum); + + if (ev == 0x00 || ev == 0xff) + return IRQ_HANDLED; + + for (i = 0; sony_pic_eventtypes[i].model; i++) { + + if (spic_dev.model != sony_pic_eventtypes[i].model) + continue; + + if ((data_mask & sony_pic_eventtypes[i].data) != + sony_pic_eventtypes[i].data) + continue; + + if (!(mask & sony_pic_eventtypes[i].mask)) + continue; + + for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) { + if (ev == sony_pic_eventtypes[i].events[j].data) { + device_event = + sony_pic_eventtypes[i].events[j].event; + goto found; + } + } + } + return IRQ_HANDLED; + +found: + sony_laptop_report_input_event(device_event); + acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event); + sonypi_compat_report_event(device_event); + + return IRQ_HANDLED; +} + +/***************** + * + * ACPI driver + * + *****************/ +static int sony_pic_remove(struct acpi_device *device, int type) +{ + struct sony_pic_ioport *io, *tmp_io; + struct sony_pic_irq *irq, *tmp_irq; + + sonypi_compat_exit(); + + if (sony_pic_disable(device)) { + printk(KERN_ERR DRV_PFX "Couldn't disable device.\n"); + return -ENXIO; + } + + free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); + release_region(spic_dev.cur_ioport->io.minimum, + spic_dev.cur_ioport->io.address_length); + + sony_laptop_remove_input(); + + /* pf attrs */ + sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group); + sony_pf_remove(); + + list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { + list_del(&io->list); + kfree(io); + } + list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) { + list_del(&irq->list); + kfree(irq); + } + spic_dev.cur_ioport = NULL; + spic_dev.cur_irq = NULL; + + dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); + return 0; +} + +static int sony_pic_add(struct acpi_device *device) +{ + int result; + struct sony_pic_ioport *io, *tmp_io; + struct sony_pic_irq *irq, *tmp_irq; + + printk(KERN_INFO DRV_PFX "%s v%s.\n", + SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); + + spic_dev.acpi_dev = device; + strcpy(acpi_device_class(device), "sony/hotkey"); + spic_dev.model = sony_pic_detect_device_type(); + mutex_init(&spic_dev.lock); + + /* read _PRS resources */ + result = sony_pic_possible_resources(device); + if (result) { + printk(KERN_ERR DRV_PFX + "Unabe to read possible resources.\n"); + goto err_free_resources; + } + + /* setup input devices and helper fifo */ + result = sony_laptop_setup_input(); + if (result) { + printk(KERN_ERR DRV_PFX + "Unabe to create input devices.\n"); + goto err_free_resources; + } + + /* request io port */ + list_for_each_entry(io, &spic_dev.ioports, list) { + if (request_region(io->io.minimum, io->io.address_length, + "Sony Programable I/O Device")) { + dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n", + io->io.minimum, io->io.maximum, + io->io.address_length); + spic_dev.cur_ioport = io; + break; + } + } + if (!spic_dev.cur_ioport) { + printk(KERN_ERR DRV_PFX "Failed to request_region.\n"); + result = -ENODEV; + goto err_remove_input; + } + + /* request IRQ */ + list_for_each_entry(irq, &spic_dev.interrupts, list) { + if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, + IRQF_SHARED, "sony-laptop", &spic_dev)) { + dprintk("IRQ: %d - triggering: %d - " + "polarity: %d - shr: %d\n", + irq->irq.interrupts[0], + irq->irq.triggering, + irq->irq.polarity, + irq->irq.sharable); + spic_dev.cur_irq = irq; + break; + } + } + if (!spic_dev.cur_irq) { + printk(KERN_ERR DRV_PFX "Failed to request_irq.\n"); + result = -ENODEV; + goto err_release_region; + } + + /* set resource status _SRS */ + result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + if (result) { + printk(KERN_ERR DRV_PFX "Couldn't enable device.\n"); + goto err_free_irq; + } + + spic_dev.bluetooth_power = -1; + /* create device attributes */ + result = sony_pf_add(); + if (result) + goto err_disable_device; + + result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group); + if (result) + goto err_remove_pf; + + if (sonypi_compat_init()) + goto err_remove_pf; + + return 0; + +err_remove_pf: + sony_pf_remove(); + +err_disable_device: + sony_pic_disable(device); + +err_free_irq: + free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); + +err_release_region: + release_region(spic_dev.cur_ioport->io.minimum, + spic_dev.cur_ioport->io.address_length); + +err_remove_input: + sony_laptop_remove_input(); + +err_free_resources: + list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { + list_del(&io->list); + kfree(io); + } + list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) { + list_del(&irq->list); + kfree(irq); + } + spic_dev.cur_ioport = NULL; + spic_dev.cur_irq = NULL; + + return result; +} + +static int sony_pic_suspend(struct acpi_device *device, pm_message_t state) +{ + if (sony_pic_disable(device)) + return -ENXIO; + return 0; +} + +static int sony_pic_resume(struct acpi_device *device) +{ + sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + return 0; +} + +static struct acpi_driver sony_pic_driver = { + .name = SONY_PIC_DRIVER_NAME, + .class = SONY_PIC_CLASS, + .ids = SONY_PIC_HID, + .owner = THIS_MODULE, + .ops = { + .add = sony_pic_add, + .remove = sony_pic_remove, + .suspend = sony_pic_suspend, + .resume = sony_pic_resume, + }, +}; + +static struct dmi_system_id __initdata sonypi_dmi_table[] = { + { + .ident = "Sony Vaio", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"), + }, + }, + { + .ident = "Sony Vaio", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"), + }, + }, + { } +}; + +static int __init sony_laptop_init(void) +{ + int result; + + if (!no_spic && dmi_check_system(sonypi_dmi_table)) { + result = acpi_bus_register_driver(&sony_pic_driver); + if (result) { + printk(KERN_ERR DRV_PFX + "Unable to register SPIC driver."); + goto out; + } + } + + result = acpi_bus_register_driver(&sony_nc_driver); + if (result) { + printk(KERN_ERR DRV_PFX "Unable to register SNC driver."); + goto out_unregister_pic; + } + + return 0; + +out_unregister_pic: + if (!no_spic) + acpi_bus_unregister_driver(&sony_pic_driver); +out: + return result; } -static void __exit sony_acpi_exit(void) +static void __exit sony_laptop_exit(void) { - acpi_bus_unregister_driver(&sony_acpi_driver); + acpi_bus_unregister_driver(&sony_nc_driver); + if (!no_spic) + acpi_bus_unregister_driver(&sony_pic_driver); } -module_init(sony_acpi_init); -module_exit(sony_acpi_exit); +module_init(sony_laptop_init); +module_exit(sony_laptop_exit); diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c new file mode 100644 index 0000000..6c36a55 --- /dev/null +++ b/drivers/misc/thinkpad_acpi.c @@ -0,0 +1,4312 @@ +/* + * thinkpad_acpi.c - ThinkPad ACPI Extras + * + * + * Copyright (C) 2004-2005 Borislav Deianov + * Copyright (C) 2006-2007 Henrique de Moraes Holschuh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#define IBM_VERSION "0.14" +#define TPACPI_SYSFS_VERSION 0x000100 + +/* + * Changelog: + * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to + * drivers/misc. + * + * 2006-11-22 0.13 new maintainer + * changelog now lives in git commit history, and will + * not be updated further in-file. + * + * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels + * 2005-03-17 0.11 support for 600e, 770x + * thanks to Jamie Lentin + * support for 770e, G41 + * G40 and G41 don't have a thinklight + * temperatures no longer experimental + * experimental brightness control + * experimental volume control + * experimental fan enable/disable + * 2005-01-16 0.10 fix module loading on R30, R31 + * 2005-01-16 0.9 support for 570, R30, R31 + * ultrabay support on A22p, A3x + * limit arg for cmos, led, beep, drop experimental status + * more capable led control on A21e, A22p, T20-22, X20 + * experimental temperatures and fan speed + * experimental embedded controller register dump + * mark more functions as __init, drop incorrect __exit + * use MODULE_VERSION + * thanks to Henrik Brix Andersen + * fix parameter passing on module loading + * thanks to Rusty Russell + * thanks to Jim Radford + * 2004-11-08 0.8 fix init error case, don't return from a macro + * thanks to Chris Wright + * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 + * fix led control on A21e + * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device + * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 + * proc file format changed + * video_switch command + * experimental cmos control + * experimental led control + * experimental acpi sounds + * 2004-09-16 0.4 support for module parameters + * hotkey mask can be prefixed by 0x + * video output switching + * video expansion control + * ultrabay eject support + * removed lcd brightness/on/off control, didn't work + * 2004-08-17 0.3 support for R40 + * lcd off, brightness control + * thinklight on/off + * 2004-08-14 0.2 support for T series, X20 + * bluetooth enable/disable + * hotkey events disabled by default + * removed fan control, currently useless + * 2004-08-09 0.1 initial release, support for X series + */ + +#include "thinkpad_acpi.h" + +MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); +MODULE_DESCRIPTION(IBM_DESC); +MODULE_VERSION(IBM_VERSION); +MODULE_LICENSE("GPL"); + +/* Please remove this in year 2009 */ +MODULE_ALIAS("ibm_acpi"); + +#define __unused __attribute__ ((unused)) + +/**************************************************************************** + **************************************************************************** + * + * ACPI Helpers and device model + * + **************************************************************************** + ****************************************************************************/ + +/************************************************************************* + * ACPI basic handles + */ + +static acpi_handle root_handle = NULL; + +#define IBM_HANDLE(object, parent, paths...) \ + static acpi_handle object##_handle; \ + static acpi_handle *object##_parent = &parent##_handle; \ + static char *object##_path; \ + static char *object##_paths[] = { paths } + +IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ + "\\_SB.PCI.ISA.EC", /* 570 */ + "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ + "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ + "\\_SB.PCI0.ICH3.EC0", /* R31 */ + "\\_SB.PCI0.LPC.EC", /* all others */ + ); + +IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ +IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ + + +/************************************************************************* + * Misc ACPI handles + */ + +IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ + "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ + "\\CMS", /* R40, R40e */ + ); /* all others */ + +IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ + "^HKEY", /* R30, R31 */ + "HKEY", /* all others */ + ); /* 570 */ + + +/************************************************************************* + * ACPI helpers + */ + +static int acpi_evalf(acpi_handle handle, + void *res, char *method, char *fmt, ...) +{ + char *fmt0 = fmt; + struct acpi_object_list params; + union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; + struct acpi_buffer result, *resultp; + union acpi_object out_obj; + acpi_status status; + va_list ap; + char res_type; + int success; + int quiet; + + if (!*fmt) { + printk(IBM_ERR "acpi_evalf() called with empty format\n"); + return 0; + } + + if (*fmt == 'q') { + quiet = 1; + fmt++; + } else + quiet = 0; + + res_type = *(fmt++); + + params.count = 0; + params.pointer = &in_objs[0]; + + va_start(ap, fmt); + while (*fmt) { + char c = *(fmt++); + switch (c) { + case 'd': /* int */ + in_objs[params.count].integer.value = va_arg(ap, int); + in_objs[params.count++].type = ACPI_TYPE_INTEGER; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", c); + return 0; + } + } + va_end(ap); + + if (res_type != 'v') { + result.length = sizeof(out_obj); + result.pointer = &out_obj; + resultp = &result; + } else + resultp = NULL; + + status = acpi_evaluate_object(handle, method, ¶ms, resultp); + + switch (res_type) { + case 'd': /* int */ + if (res) + *(int *)res = out_obj.integer.value; + success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; + break; + case 'v': /* void */ + success = status == AE_OK; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", res_type); + return 0; + } + + if (!success && !quiet) + printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", + method, fmt0, status); + + return success; +} + +static void __unused acpi_print_int(acpi_handle handle, char *method) +{ + int i; + + if (acpi_evalf(handle, &i, method, "d")) + printk(IBM_INFO "%s = 0x%x\n", method, i); + else + printk(IBM_ERR "error calling %s\n", method); +} + +static int acpi_ec_read(int i, u8 * p) +{ + int v; + + if (ecrd_handle) { + if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) + return 0; + *p = v; + } else { + if (ec_read(i, p) < 0) + return 0; + } + + return 1; +} + +static int acpi_ec_write(int i, u8 v) +{ + if (ecwr_handle) { + if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) + return 0; + } else { + if (ec_write(i, v) < 0) + return 0; + } + + return 1; +} + +static int _sta(acpi_handle handle) +{ + int status; + + if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) + status = 0; + + return status; +} + +static int issue_thinkpad_cmos_command(int cmos_cmd) +{ + if (!cmos_handle) + return -ENXIO; + + if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd)) + return -EIO; + + return 0; +} + +/************************************************************************* + * ACPI device model + */ + +static void drv_acpi_handle_init(char *name, + acpi_handle *handle, acpi_handle parent, + char **paths, int num_paths, char **path) +{ + int i; + acpi_status status; + + vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n", + name); + + for (i = 0; i < num_paths; i++) { + status = acpi_get_handle(parent, paths[i], handle); + if (ACPI_SUCCESS(status)) { + *path = paths[i]; + dbg_printk(TPACPI_DBG_INIT, + "Found ACPI handle %s for %s\n", + *path, name); + return; + } + } + + vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n", + name); + *handle = NULL; +} + +static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) +{ + struct ibm_struct *ibm = data; + + if (!ibm || !ibm->acpi || !ibm->acpi->notify) + return; + + ibm->acpi->notify(ibm, event); +} + +static int __init setup_acpi_notify(struct ibm_struct *ibm) +{ + acpi_status status; + int rc; + + BUG_ON(!ibm->acpi); + + if (!*ibm->acpi->handle) + return 0; + + vdbg_printk(TPACPI_DBG_INIT, + "setting up ACPI notify for %s\n", ibm->name); + + rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device); + if (rc < 0) { + printk(IBM_ERR "acpi_bus_get_device(%s) failed: %d\n", + ibm->name, rc); + return -ENODEV; + } + + acpi_driver_data(ibm->acpi->device) = ibm; + sprintf(acpi_device_class(ibm->acpi->device), "%s/%s", + IBM_ACPI_EVENT_PREFIX, + ibm->name); + + status = acpi_install_notify_handler(*ibm->acpi->handle, + ibm->acpi->type, dispatch_acpi_notify, ibm); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + printk(IBM_NOTICE "another device driver is already handling %s events\n", + ibm->name); + } else { + printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", + ibm->name, status); + } + return -ENODEV; + } + ibm->flags.acpi_notify_installed = 1; + return 0; +} + +static int __init tpacpi_device_add(struct acpi_device *device) +{ + return 0; +} + +static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) +{ + int rc; + + dbg_printk(TPACPI_DBG_INIT, + "registering %s as an ACPI driver\n", ibm->name); + + BUG_ON(!ibm->acpi); + + ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); + if (!ibm->acpi->driver) { + printk(IBM_ERR "kzalloc(ibm->driver) failed\n"); + return -ENOMEM; + } + + sprintf(ibm->acpi->driver->name, "%s_%s", IBM_NAME, ibm->name); + ibm->acpi->driver->ids = ibm->acpi->hid; + ibm->acpi->driver->ops.add = &tpacpi_device_add; + + rc = acpi_bus_register_driver(ibm->acpi->driver); + if (rc < 0) { + printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", + ibm->acpi->hid, rc); + kfree(ibm->acpi->driver); + ibm->acpi->driver = NULL; + } else if (!rc) + ibm->flags.acpi_driver_registered = 1; + + return rc; +} + + +/**************************************************************************** + **************************************************************************** + * + * Procfs Helpers + * + **************************************************************************** + ****************************************************************************/ + +static int dispatch_procfs_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct ibm_struct *ibm = data; + int len; + + if (!ibm || !ibm->read) + return -EINVAL; + + len = ibm->read(page); + if (len < 0) + return len; + + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +static int dispatch_procfs_write(struct file *file, + const char __user * userbuf, + unsigned long count, void *data) +{ + struct ibm_struct *ibm = data; + char *kernbuf; + int ret; + + if (!ibm || !ibm->write) + return -EINVAL; + + kernbuf = kmalloc(count + 2, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (copy_from_user(kernbuf, userbuf, count)) { + kfree(kernbuf); + return -EFAULT; + } + + kernbuf[count] = 0; + strcat(kernbuf, ","); + ret = ibm->write(kernbuf); + if (ret == 0) + ret = count; + + kfree(kernbuf); + + return ret; +} + +static char *next_cmd(char **cmds) +{ + char *start = *cmds; + char *end; + + while ((end = strchr(start, ',')) && end == start) + start = end + 1; + + if (!end) + return NULL; + + *end = 0; + *cmds = end + 1; + return start; +} + + +/**************************************************************************** + **************************************************************************** + * + * Device model: hwmon and platform + * + **************************************************************************** + ****************************************************************************/ + +static struct platform_device *tpacpi_pdev = NULL; +static struct class_device *tpacpi_hwmon = NULL; + +static struct platform_driver tpacpi_pdriver = { + .driver = { + .name = IBM_DRVR_NAME, + .owner = THIS_MODULE, + }, +}; + + +/************************************************************************* + * thinkpad-acpi driver attributes + */ + +/* interface_version --------------------------------------------------- */ +static ssize_t tpacpi_driver_interface_version_show( + struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION); +} + +static DRIVER_ATTR(interface_version, S_IRUGO, + tpacpi_driver_interface_version_show, NULL); + +/* debug_level --------------------------------------------------------- */ +static ssize_t tpacpi_driver_debug_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level); +} + +static ssize_t tpacpi_driver_debug_store(struct device_driver *drv, + const char *buf, size_t count) +{ + unsigned long t; + + if (parse_strtoul(buf, 0xffff, &t)) + return -EINVAL; + + dbg_level = t; + + return count; +} + +static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, + tpacpi_driver_debug_show, tpacpi_driver_debug_store); + +/* version ------------------------------------------------------------- */ +static ssize_t tpacpi_driver_version_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s v%s\n", IBM_DESC, IBM_VERSION); +} + +static DRIVER_ATTR(version, S_IRUGO, + tpacpi_driver_version_show, NULL); + +/* --------------------------------------------------------------------- */ + +static struct driver_attribute* tpacpi_driver_attributes[] = { + &driver_attr_debug_level, &driver_attr_version, + &driver_attr_interface_version, +}; + +static int __init tpacpi_create_driver_attributes(struct device_driver *drv) +{ + int i, res; + + i = 0; + res = 0; + while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) { + res = driver_create_file(drv, tpacpi_driver_attributes[i]); + i++; + } + + return res; +} + +static void tpacpi_remove_driver_attributes(struct device_driver *drv) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) + driver_remove_file(drv, tpacpi_driver_attributes[i]); +} + +/************************************************************************* + * sysfs support helpers + */ + +struct attribute_set_obj { + struct attribute_set s; + struct attribute *a; +} __attribute__((packed)); + +static struct attribute_set *create_attr_set(unsigned int max_members, + const char* name) +{ + struct attribute_set_obj *sobj; + + if (max_members == 0) + return NULL; + + /* Allocates space for implicit NULL at the end too */ + sobj = kzalloc(sizeof(struct attribute_set_obj) + + max_members * sizeof(struct attribute *), + GFP_KERNEL); + if (!sobj) + return NULL; + sobj->s.max_members = max_members; + sobj->s.group.attrs = &sobj->a; + sobj->s.group.name = name; + + return &sobj->s; +} + +/* not multi-threaded safe, use it in a single thread per set */ +static int add_to_attr_set(struct attribute_set* s, struct attribute *attr) +{ + if (!s || !attr) + return -EINVAL; + + if (s->members >= s->max_members) + return -ENOMEM; + + s->group.attrs[s->members] = attr; + s->members++; + + return 0; +} + +static int add_many_to_attr_set(struct attribute_set* s, + struct attribute **attr, + unsigned int count) +{ + int i, res; + + for (i = 0; i < count; i++) { + res = add_to_attr_set(s, attr[i]); + if (res) + return res; + } + + return 0; +} + +static void delete_attr_set(struct attribute_set* s, struct kobject *kobj) +{ + sysfs_remove_group(kobj, &s->group); + destroy_attr_set(s); +} + +static int parse_strtoul(const char *buf, + unsigned long max, unsigned long *value) +{ + char *endp; + + *value = simple_strtoul(buf, &endp, 0); + while (*endp && isspace(*endp)) + endp++; + if (*endp || *value > max) + return -EINVAL; + + return 0; +} + +/**************************************************************************** + **************************************************************************** + * + * Subdrivers + * + **************************************************************************** + ****************************************************************************/ + +/************************************************************************* + * thinkpad-acpi init subdriver + */ + +static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) +{ + printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); + printk(IBM_INFO "%s\n", IBM_URL); + + if (ibm_thinkpad_ec_found) + printk(IBM_INFO "ThinkPad EC firmware %s\n", + ibm_thinkpad_ec_found); + + return 0; +} + +static int thinkpad_acpi_driver_read(char *p) +{ + int len = 0; + + len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); + len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); + + return len; +} + +static struct ibm_struct thinkpad_acpi_driver_data = { + .name = "driver", + .read = thinkpad_acpi_driver_read, +}; + +/************************************************************************* + * Hotkey subdriver + */ + +static int hotkey_orig_status; +static int hotkey_orig_mask; + +static struct attribute_set *hotkey_dev_attributes = NULL; + +/* sysfs hotkey enable ------------------------------------------------- */ +static ssize_t hotkey_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, status, mask; + + res = hotkey_get(&status, &mask); + if (res) + return res; + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} + +static ssize_t hotkey_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, status, mask; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = hotkey_get(&status, &mask); + if (!res) + res = hotkey_set(t, mask); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_hotkey_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + hotkey_enable_show, hotkey_enable_store); + +/* sysfs hotkey mask --------------------------------------------------- */ +static ssize_t hotkey_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, status, mask; + + res = hotkey_get(&status, &mask); + if (res) + return res; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask); +} + +static ssize_t hotkey_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, status, mask; + + if (parse_strtoul(buf, 0xffff, &t)) + return -EINVAL; + + res = hotkey_get(&status, &mask); + if (!res) + hotkey_set(status, t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_hotkey_mask = + __ATTR(mask, S_IWUSR | S_IRUGO, + hotkey_mask_show, hotkey_mask_store); + +/* sysfs hotkey bios_enabled ------------------------------------------- */ +static ssize_t hotkey_bios_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status); +} + +static struct device_attribute dev_attr_hotkey_bios_enabled = + __ATTR(bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL); + +/* sysfs hotkey bios_mask ---------------------------------------------- */ +static ssize_t hotkey_bios_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask); +} + +static struct device_attribute dev_attr_hotkey_bios_mask = + __ATTR(bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); + +/* --------------------------------------------------------------------- */ + +static struct attribute *hotkey_mask_attributes[] = { + &dev_attr_hotkey_mask.attr, + &dev_attr_hotkey_bios_enabled.attr, + &dev_attr_hotkey_bios_mask.attr, +}; + +static int __init hotkey_init(struct ibm_init_struct *iibm) +{ + int res; + + vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); + + IBM_ACPIHANDLE_INIT(hkey); + mutex_init(&hotkey_mutex); + + /* hotkey not supported on 570 */ + tp_features.hotkey = hkey_handle != NULL; + + vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n", + str_supported(tp_features.hotkey)); + + if (tp_features.hotkey) { + hotkey_dev_attributes = create_attr_set(4, + TPACPI_HOTKEY_SYSFS_GROUP); + if (!hotkey_dev_attributes) + return -ENOMEM; + res = add_to_attr_set(hotkey_dev_attributes, + &dev_attr_hotkey_enable.attr); + if (res) + return res; + + /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + A30, R30, R31, T20-22, X20-21, X22-24 */ + tp_features.hotkey_mask = + acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); + + vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", + str_supported(tp_features.hotkey_mask)); + + res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); + if (!res && tp_features.hotkey_mask) { + res = add_many_to_attr_set(hotkey_dev_attributes, + hotkey_mask_attributes, + ARRAY_SIZE(hotkey_mask_attributes)); + } + if (!res) + res = register_attr_set_with_sysfs( + hotkey_dev_attributes, + &tpacpi_pdev->dev.kobj); + + if (res) + return res; + } + + return (tp_features.hotkey)? 0 : 1; +} + +static void hotkey_exit(void) +{ + int res; + + if (tp_features.hotkey) { + dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n"); + res = hotkey_set(hotkey_orig_status, hotkey_orig_mask); + if (res) + printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n"); + } + + if (hotkey_dev_attributes) { + delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); + hotkey_dev_attributes = NULL; + } +} + +static void hotkey_notify(struct ibm_struct *ibm, u32 event) +{ + int hkey; + + if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) + acpi_bus_generate_event(ibm->acpi->device, event, hkey); + else { + printk(IBM_ERR "unknown hotkey event %d\n", event); + acpi_bus_generate_event(ibm->acpi->device, event, 0); + } +} + +/* + * Call with hotkey_mutex held + */ +static int hotkey_get(int *status, int *mask) +{ + if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) + return -EIO; + + if (tp_features.hotkey_mask) + if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) + return -EIO; + + return 0; +} + +/* + * Call with hotkey_mutex held + */ +static int hotkey_set(int status, int mask) +{ + int i; + + if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) + return -EIO; + + if (tp_features.hotkey_mask) + for (i = 0; i < 32; i++) { + int bit = ((1 << i) & mask) != 0; + if (!acpi_evalf(hkey_handle, + NULL, "MHKM", "vdd", i + 1, bit)) + return -EIO; + } + + return 0; +} + +/* procfs -------------------------------------------------------------- */ +static int hotkey_read(char *p) +{ + int res, status, mask; + int len = 0; + + if (!tp_features.hotkey) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + + res = mutex_lock_interruptible(&hotkey_mutex); + if (res < 0) + return res; + res = hotkey_get(&status, &mask); + mutex_unlock(&hotkey_mutex); + if (res) + return res; + + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); + if (tp_features.hotkey_mask) { + len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); + len += sprintf(p + len, + "commands:\tenable, disable, reset, \n"); + } else { + len += sprintf(p + len, "mask:\t\tnot supported\n"); + len += sprintf(p + len, "commands:\tenable, disable, reset\n"); + } + + return len; +} + +static int hotkey_write(char *buf) +{ + int res, status, mask; + char *cmd; + int do_cmd = 0; + + if (!tp_features.hotkey) + return -ENODEV; + + res = mutex_lock_interruptible(&hotkey_mutex); + if (res < 0) + return res; + + res = hotkey_get(&status, &mask); + if (res) + goto errexit; + + res = 0; + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + status = 1; + } else if (strlencmp(cmd, "disable") == 0) { + status = 0; + } else if (strlencmp(cmd, "reset") == 0) { + status = hotkey_orig_status; + mask = hotkey_orig_mask; + } else if (sscanf(cmd, "0x%x", &mask) == 1) { + /* mask set */ + } else if (sscanf(cmd, "%x", &mask) == 1) { + /* mask set */ + } else { + res = -EINVAL; + goto errexit; + } + do_cmd = 1; + } + + if (do_cmd) + res = hotkey_set(status, mask); + +errexit: + mutex_unlock(&hotkey_mutex); + return res; +} + +static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = { + .hid = IBM_HKEY_HID, + .notify = hotkey_notify, + .handle = &hkey_handle, + .type = ACPI_DEVICE_NOTIFY, +}; + +static struct ibm_struct hotkey_driver_data = { + .name = "hotkey", + .read = hotkey_read, + .write = hotkey_write, + .exit = hotkey_exit, + .acpi = &ibm_hotkey_acpidriver, +}; + +/************************************************************************* + * Bluetooth subdriver + */ + +/* sysfs bluetooth enable ---------------------------------------------- */ +static ssize_t bluetooth_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status; + + status = bluetooth_get_radiosw(); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); +} + +static ssize_t bluetooth_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = bluetooth_set_radiosw(t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_bluetooth_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + bluetooth_enable_show, bluetooth_enable_store); + +/* --------------------------------------------------------------------- */ + +static struct attribute *bluetooth_attributes[] = { + &dev_attr_bluetooth_enable.attr, + NULL +}; + +static const struct attribute_group bluetooth_attr_group = { + .name = TPACPI_BLUETH_SYSFS_GROUP, + .attrs = bluetooth_attributes, +}; + +static int __init bluetooth_init(struct ibm_init_struct *iibm) +{ + int res; + int status = 0; + + vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n"); + + IBM_ACPIHANDLE_INIT(hkey); + + /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ + tp_features.bluetooth = hkey_handle && + acpi_evalf(hkey_handle, &status, "GBDC", "qd"); + + vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n", + str_supported(tp_features.bluetooth), + status); + + if (tp_features.bluetooth) { + if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { + /* no bluetooth hardware present in system */ + tp_features.bluetooth = 0; + dbg_printk(TPACPI_DBG_INIT, + "bluetooth hardware not installed\n"); + } else { + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); + if (res) + return res; + } + } + + return (tp_features.bluetooth)? 0 : 1; +} + +static void bluetooth_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); +} + +static int bluetooth_get_radiosw(void) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) + return -EIO; + + return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0); +} + +static int bluetooth_set_radiosw(int radio_on) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) + return -EIO; + if (radio_on) + status |= TP_ACPI_BLUETOOTH_RADIOSSW; + else + status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; + if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) + return -EIO; + + return 0; +} + +/* procfs -------------------------------------------------------------- */ +static int bluetooth_read(char *p) +{ + int len = 0; + int status = bluetooth_get_radiosw(); + + if (!tp_features.bluetooth) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\t%s\n", + (status)? "enabled" : "disabled"); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int bluetooth_write(char *buf) +{ + char *cmd; + + if (!tp_features.bluetooth) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + bluetooth_set_radiosw(1); + } else if (strlencmp(cmd, "disable") == 0) { + bluetooth_set_radiosw(0); + } else + return -EINVAL; + } + + return 0; +} + +static struct ibm_struct bluetooth_driver_data = { + .name = "bluetooth", + .read = bluetooth_read, + .write = bluetooth_write, + .exit = bluetooth_exit, +}; + +/************************************************************************* + * Wan subdriver + */ + +/* sysfs wan enable ---------------------------------------------------- */ +static ssize_t wan_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status; + + status = wan_get_radiosw(); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); +} + +static ssize_t wan_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = wan_set_radiosw(t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_wan_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + wan_enable_show, wan_enable_store); + +/* --------------------------------------------------------------------- */ + +static struct attribute *wan_attributes[] = { + &dev_attr_wan_enable.attr, + NULL +}; + +static const struct attribute_group wan_attr_group = { + .name = TPACPI_WAN_SYSFS_GROUP, + .attrs = wan_attributes, +}; + +static int __init wan_init(struct ibm_init_struct *iibm) +{ + int res; + int status = 0; + + vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n"); + + IBM_ACPIHANDLE_INIT(hkey); + + tp_features.wan = hkey_handle && + acpi_evalf(hkey_handle, &status, "GWAN", "qd"); + + vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n", + str_supported(tp_features.wan), + status); + + if (tp_features.wan) { + if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { + /* no wan hardware present in system */ + tp_features.wan = 0; + dbg_printk(TPACPI_DBG_INIT, + "wan hardware not installed\n"); + } else { + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); + if (res) + return res; + } + } + + return (tp_features.wan)? 0 : 1; +} + +static void wan_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); +} + +static int wan_get_radiosw(void) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) + return -EIO; + + return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0); +} + +static int wan_set_radiosw(int radio_on) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) + return -EIO; + if (radio_on) + status |= TP_ACPI_WANCARD_RADIOSSW; + else + status &= ~TP_ACPI_WANCARD_RADIOSSW; + if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) + return -EIO; + + return 0; +} + +/* procfs -------------------------------------------------------------- */ +static int wan_read(char *p) +{ + int len = 0; + int status = wan_get_radiosw(); + + if (!tp_features.wan) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\t%s\n", + (status)? "enabled" : "disabled"); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int wan_write(char *buf) +{ + char *cmd; + + if (!tp_features.wan) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + wan_set_radiosw(1); + } else if (strlencmp(cmd, "disable") == 0) { + wan_set_radiosw(0); + } else + return -EINVAL; + } + + return 0; +} + +static struct ibm_struct wan_driver_data = { + .name = "wan", + .read = wan_read, + .write = wan_write, + .exit = wan_exit, + .flags.experimental = 1, +}; + +/************************************************************************* + * Video subdriver + */ + +static enum video_access_mode video_supported; +static int video_orig_autosw; + +IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ + "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ + "\\_SB.PCI0.VID0", /* 770e */ + "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ + "\\_SB.PCI0.AGP.VID", /* all others */ + ); /* R30, R31 */ + +IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ + +static int __init video_init(struct ibm_init_struct *iibm) +{ + int ivga; + + vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n"); + + IBM_ACPIHANDLE_INIT(vid); + IBM_ACPIHANDLE_INIT(vid2); + + if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) + /* G41, assume IVGA doesn't change */ + vid_handle = vid2_handle; + + if (!vid_handle) + /* video switching not supported on R30, R31 */ + video_supported = TPACPI_VIDEO_NONE; + else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) + /* 570 */ + video_supported = TPACPI_VIDEO_570; + else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) + /* 600e/x, 770e, 770x */ + video_supported = TPACPI_VIDEO_770; + else + /* all others */ + video_supported = TPACPI_VIDEO_NEW; + + vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n", + str_supported(video_supported != TPACPI_VIDEO_NONE), + video_supported); + + return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1; +} + +static void video_exit(void) +{ + dbg_printk(TPACPI_DBG_EXIT, + "restoring original video autoswitch mode\n"); + if (video_autosw_set(video_orig_autosw)) + printk(IBM_ERR "error while trying to restore original " + "video autoswitch mode\n"); +} + +static int video_outputsw_get(void) +{ + int status = 0; + int i; + + switch (video_supported) { + case TPACPI_VIDEO_570: + if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", + TP_ACPI_VIDEO_570_PHSCMD)) + return -EIO; + status = i & TP_ACPI_VIDEO_570_PHSMASK; + break; + case TPACPI_VIDEO_770: + if (!acpi_evalf(NULL, &i, "\\VCDL", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_LCD; + if (!acpi_evalf(NULL, &i, "\\VCDC", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_CRT; + break; + case TPACPI_VIDEO_NEW: + if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) || + !acpi_evalf(NULL, &i, "\\VCDC", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_CRT; + + if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) || + !acpi_evalf(NULL, &i, "\\VCDL", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_LCD; + if (!acpi_evalf(NULL, &i, "\\VCDD", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_DVI; + break; + default: + return -ENOSYS; + } + + return status; +} + +static int video_outputsw_set(int status) +{ + int autosw; + int res = 0; + + switch (video_supported) { + case TPACPI_VIDEO_570: + res = acpi_evalf(NULL, NULL, + "\\_SB.PHS2", "vdd", + TP_ACPI_VIDEO_570_PHS2CMD, + status | TP_ACPI_VIDEO_570_PHS2SET); + break; + case TPACPI_VIDEO_770: + autosw = video_autosw_get(); + if (autosw < 0) + return autosw; + + res = video_autosw_set(1); + if (res) + return res; + res = acpi_evalf(vid_handle, NULL, + "ASWT", "vdd", status * 0x100, 0); + if (!autosw && video_autosw_set(autosw)) { + printk(IBM_ERR "video auto-switch left enabled due to error\n"); + return -EIO; + } + break; + case TPACPI_VIDEO_NEW: + res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && + acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); + break; + default: + return -ENOSYS; + } + + return (res)? 0 : -EIO; +} + +static int video_autosw_get(void) +{ + int autosw = 0; + + switch (video_supported) { + case TPACPI_VIDEO_570: + if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d")) + return -EIO; + break; + case TPACPI_VIDEO_770: + case TPACPI_VIDEO_NEW: + if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d")) + return -EIO; + break; + default: + return -ENOSYS; + } + + return autosw & 1; +} + +static int video_autosw_set(int enable) +{ + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0)) + return -EIO; + return 0; +} + +static int video_outputsw_cycle(void) +{ + int autosw = video_autosw_get(); + int res; + + if (autosw < 0) + return autosw; + + switch (video_supported) { + case TPACPI_VIDEO_570: + res = video_autosw_set(1); + if (res) + return res; + res = acpi_evalf(ec_handle, NULL, "_Q16", "v"); + break; + case TPACPI_VIDEO_770: + case TPACPI_VIDEO_NEW: + res = video_autosw_set(1); + if (res) + return res; + res = acpi_evalf(vid_handle, NULL, "VSWT", "v"); + break; + default: + return -ENOSYS; + } + if (!autosw && video_autosw_set(autosw)) { + printk(IBM_ERR "video auto-switch left enabled due to error\n"); + return -EIO; + } + + return (res)? 0 : -EIO; +} + +static int video_expand_toggle(void) +{ + switch (video_supported) { + case TPACPI_VIDEO_570: + return acpi_evalf(ec_handle, NULL, "_Q17", "v")? + 0 : -EIO; + case TPACPI_VIDEO_770: + return acpi_evalf(vid_handle, NULL, "VEXP", "v")? + 0 : -EIO; + case TPACPI_VIDEO_NEW: + return acpi_evalf(NULL, NULL, "\\VEXP", "v")? + 0 : -EIO; + default: + return -ENOSYS; + } + /* not reached */ +} + +static int video_read(char *p) +{ + int status, autosw; + int len = 0; + + if (video_supported == TPACPI_VIDEO_NONE) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + + status = video_outputsw_get(); + if (status < 0) + return status; + + autosw = video_autosw_get(); + if (autosw < 0) + return autosw; + + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); + len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); + if (video_supported == TPACPI_VIDEO_NEW) + len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); + len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); + len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); + len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); + if (video_supported == TPACPI_VIDEO_NEW) + len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); + len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); + len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); + + return len; +} + +static int video_write(char *buf) +{ + char *cmd; + int enable, disable, status; + int res; + + if (video_supported == TPACPI_VIDEO_NONE) + return -ENODEV; + + enable = 0; + disable = 0; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "lcd_enable") == 0) { + enable |= TP_ACPI_VIDEO_S_LCD; + } else if (strlencmp(cmd, "lcd_disable") == 0) { + disable |= TP_ACPI_VIDEO_S_LCD; + } else if (strlencmp(cmd, "crt_enable") == 0) { + enable |= TP_ACPI_VIDEO_S_CRT; + } else if (strlencmp(cmd, "crt_disable") == 0) { + disable |= TP_ACPI_VIDEO_S_CRT; + } else if (video_supported == TPACPI_VIDEO_NEW && + strlencmp(cmd, "dvi_enable") == 0) { + enable |= TP_ACPI_VIDEO_S_DVI; + } else if (video_supported == TPACPI_VIDEO_NEW && + strlencmp(cmd, "dvi_disable") == 0) { + disable |= TP_ACPI_VIDEO_S_DVI; + } else if (strlencmp(cmd, "auto_enable") == 0) { + res = video_autosw_set(1); + if (res) + return res; + } else if (strlencmp(cmd, "auto_disable") == 0) { + res = video_autosw_set(0); + if (res) + return res; + } else if (strlencmp(cmd, "video_switch") == 0) { + res = video_outputsw_cycle(); + if (res) + return res; + } else if (strlencmp(cmd, "expand_toggle") == 0) { + res = video_expand_toggle(); + if (res) + return res; + } else + return -EINVAL; + } + + if (enable || disable) { + status = video_outputsw_get(); + if (status < 0) + return status; + res = video_outputsw_set((status & ~disable) | enable); + if (res) + return res; + } + + return 0; +} + +static struct ibm_struct video_driver_data = { + .name = "video", + .read = video_read, + .write = video_write, + .exit = video_exit, +}; + +/************************************************************************* + * Light (thinklight) subdriver + */ + +IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ +IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ + +static int __init light_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); + + IBM_ACPIHANDLE_INIT(ledb); + IBM_ACPIHANDLE_INIT(lght); + IBM_ACPIHANDLE_INIT(cmos); + + /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ + tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; + + if (tp_features.light) + /* light status not supported on + 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ + tp_features.light_status = + acpi_evalf(ec_handle, NULL, "KBLT", "qv"); + + vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", + str_supported(tp_features.light)); + + return (tp_features.light)? 0 : 1; +} + +static int light_read(char *p) +{ + int len = 0; + int status = 0; + + if (!tp_features.light) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + } else if (!tp_features.light_status) { + len += sprintf(p + len, "status:\t\tunknown\n"); + len += sprintf(p + len, "commands:\ton, off\n"); + } else { + if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) + return -EIO; + len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); + len += sprintf(p + len, "commands:\ton, off\n"); + } + + return len; +} + +static int light_write(char *buf) +{ + int cmos_cmd, lght_cmd; + char *cmd; + int success; + + if (!tp_features.light) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "on") == 0) { + cmos_cmd = 0x0c; + lght_cmd = 1; + } else if (strlencmp(cmd, "off") == 0) { + cmos_cmd = 0x0d; + lght_cmd = 0; + } else + return -EINVAL; + + success = cmos_handle ? + acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : + acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); + if (!success) + return -EIO; + } + + return 0; +} + +static struct ibm_struct light_driver_data = { + .name = "light", + .read = light_read, + .write = light_write, +}; + +/************************************************************************* + * Dock subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_DOCK + +IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ + "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ + "\\_SB.PCI0.PCI1.DOCK", /* all others */ + "\\_SB.PCI.ISA.SLCE", /* 570 */ + ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ + +/* don't list other alternatives as we install a notify handler on the 570 */ +IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ + +static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = { + { + .notify = dock_notify, + .handle = &dock_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, + { + .hid = IBM_PCI_HID, + .notify = dock_notify, + .handle = &pci_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, +}; + +static struct ibm_struct dock_driver_data[2] = { + { + .name = "dock", + .read = dock_read, + .write = dock_write, + .acpi = &ibm_dock_acpidriver[0], + }, + { + .name = "dock", + .acpi = &ibm_dock_acpidriver[1], + }, +}; + +#define dock_docked() (_sta(dock_handle) & 1) + +static int __init dock_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n"); + + IBM_ACPIHANDLE_INIT(dock); + + vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n", + str_supported(dock_handle != NULL)); + + return (dock_handle)? 0 : 1; +} + +static int __init dock_init2(struct ibm_init_struct *iibm) +{ + int dock2_needed; + + vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n"); + + if (dock_driver_data[0].flags.acpi_driver_registered && + dock_driver_data[0].flags.acpi_notify_installed) { + IBM_ACPIHANDLE_INIT(pci); + dock2_needed = (pci_handle != NULL); + vdbg_printk(TPACPI_DBG_INIT, + "dock PCI handler for the TP 570 is %s\n", + str_supported(dock2_needed)); + } else { + vdbg_printk(TPACPI_DBG_INIT, + "dock subdriver part 2 not required\n"); + dock2_needed = 0; + } + + return (dock2_needed)? 0 : 1; +} + +static void dock_notify(struct ibm_struct *ibm, u32 event) +{ + int docked = dock_docked(); + int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID); + + if (event == 1 && !pci) /* 570 */ + acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */ + else if (event == 1 && pci) /* 570 */ + acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */ + else if (event == 3 && docked) + acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */ + else if (event == 3 && !docked) + acpi_bus_generate_event(ibm->acpi->device, event, 2); /* undock */ + else if (event == 0 && docked) + acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */ + else { + printk(IBM_ERR "unknown dock event %d, status %d\n", + event, _sta(dock_handle)); + acpi_bus_generate_event(ibm->acpi->device, event, 0); /* unknown */ + } +} + +static int dock_read(char *p) +{ + int len = 0; + int docked = dock_docked(); + + if (!dock_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!docked) + len += sprintf(p + len, "status:\t\tundocked\n"); + else { + len += sprintf(p + len, "status:\t\tdocked\n"); + len += sprintf(p + len, "commands:\tdock, undock\n"); + } + + return len; +} + +static int dock_write(char *buf) +{ + char *cmd; + + if (!dock_docked()) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "undock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || + !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) + return -EIO; + } else if (strlencmp(cmd, "dock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +#endif /* CONFIG_THINKPAD_ACPI_DOCK */ + +/************************************************************************* + * Bay subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_BAY +IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ + "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ + "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ + ); /* A21e, R30, R31 */ +IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ + "_EJ0", /* all others */ + ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ +IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ + "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ + ); /* all others */ +IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ + "_EJ0", /* 770x */ + ); /* all others */ + +static int __init bay_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n"); + + IBM_ACPIHANDLE_INIT(bay); + if (bay_handle) + IBM_ACPIHANDLE_INIT(bay_ej); + IBM_ACPIHANDLE_INIT(bay2); + if (bay2_handle) + IBM_ACPIHANDLE_INIT(bay2_ej); + + tp_features.bay_status = bay_handle && + acpi_evalf(bay_handle, NULL, "_STA", "qv"); + tp_features.bay_status2 = bay2_handle && + acpi_evalf(bay2_handle, NULL, "_STA", "qv"); + + tp_features.bay_eject = bay_handle && bay_ej_handle && + (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); + tp_features.bay_eject2 = bay2_handle && bay2_ej_handle && + (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); + + vdbg_printk(TPACPI_DBG_INIT, + "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n", + str_supported(tp_features.bay_status), + str_supported(tp_features.bay_eject), + str_supported(tp_features.bay_status2), + str_supported(tp_features.bay_eject2)); + + return (tp_features.bay_status || tp_features.bay_eject || + tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1; +} + +static void bay_notify(struct ibm_struct *ibm, u32 event) +{ + acpi_bus_generate_event(ibm->acpi->device, event, 0); +} + +#define bay_occupied(b) (_sta(b##_handle) & 1) + +static int bay_read(char *p) +{ + int len = 0; + int occupied = bay_occupied(bay); + int occupied2 = bay_occupied(bay2); + int eject, eject2; + + len += sprintf(p + len, "status:\t\t%s\n", + tp_features.bay_status ? + (occupied ? "occupied" : "unoccupied") : + "not supported"); + if (tp_features.bay_status2) + len += sprintf(p + len, "status2:\t%s\n", occupied2 ? + "occupied" : "unoccupied"); + + eject = tp_features.bay_eject && occupied; + eject2 = tp_features.bay_eject2 && occupied2; + + if (eject && eject2) + len += sprintf(p + len, "commands:\teject, eject2\n"); + else if (eject) + len += sprintf(p + len, "commands:\teject\n"); + else if (eject2) + len += sprintf(p + len, "commands:\teject2\n"); + + return len; +} + +static int bay_write(char *buf) +{ + char *cmd; + + if (!tp_features.bay_eject && !tp_features.bay_eject2) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) { + if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) + return -EIO; + } else if (tp_features.bay_eject2 && + strlencmp(cmd, "eject2") == 0) { + if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +static struct tp_acpi_drv_struct ibm_bay_acpidriver = { + .notify = bay_notify, + .handle = &bay_handle, + .type = ACPI_SYSTEM_NOTIFY, +}; + +static struct ibm_struct bay_driver_data = { + .name = "bay", + .read = bay_read, + .write = bay_write, + .acpi = &ibm_bay_acpidriver, +}; + +#endif /* CONFIG_THINKPAD_ACPI_BAY */ + +/************************************************************************* + * CMOS subdriver + */ + +/* sysfs cmos_command -------------------------------------------------- */ +static ssize_t cmos_command_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long cmos_cmd; + int res; + + if (parse_strtoul(buf, 21, &cmos_cmd)) + return -EINVAL; + + res = issue_thinkpad_cmos_command(cmos_cmd); + return (res)? res : count; +} + +static struct device_attribute dev_attr_cmos_command = + __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store); + +/* --------------------------------------------------------------------- */ + +static int __init cmos_init(struct ibm_init_struct *iibm) +{ + int res; + + vdbg_printk(TPACPI_DBG_INIT, + "initializing cmos commands subdriver\n"); + + IBM_ACPIHANDLE_INIT(cmos); + + vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n", + str_supported(cmos_handle != NULL)); + + res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); + if (res) + return res; + + return (cmos_handle)? 0 : 1; +} + +static void cmos_exit(void) +{ + device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); +} + +static int cmos_read(char *p) +{ + int len = 0; + + /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + R30, R31, T20-22, X20-21 */ + if (!cmos_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "commands:\t ( is 0-21)\n"); + } + + return len; +} + +static int cmos_write(char *buf) +{ + char *cmd; + int cmos_cmd, res; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &cmos_cmd) == 1 && + cmos_cmd >= 0 && cmos_cmd <= 21) { + /* cmos_cmd set */ + } else + return -EINVAL; + + res = issue_thinkpad_cmos_command(cmos_cmd); + if (res) + return res; + } + + return 0; +} + +static struct ibm_struct cmos_driver_data = { + .name = "cmos", + .read = cmos_read, + .write = cmos_write, + .exit = cmos_exit, +}; + +/************************************************************************* + * LED subdriver + */ + +static enum led_access_mode led_supported; + +IBM_HANDLE(led, ec, "SLED", /* 570 */ + "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + "LED", /* all others */ + ); /* R30, R31 */ + +static int __init led_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); + + IBM_ACPIHANDLE_INIT(led); + + if (!led_handle) + /* led not supported on R30, R31 */ + led_supported = TPACPI_LED_NONE; + else if (strlencmp(led_path, "SLED") == 0) + /* 570 */ + led_supported = TPACPI_LED_570; + else if (strlencmp(led_path, "SYSL") == 0) + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + led_supported = TPACPI_LED_OLD; + else + /* all others */ + led_supported = TPACPI_LED_NEW; + + vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", + str_supported(led_supported), led_supported); + + return (led_supported != TPACPI_LED_NONE)? 0 : 1; +} + +#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) + +static int led_read(char *p) +{ + int len = 0; + + if (!led_supported) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + len += sprintf(p + len, "status:\t\tsupported\n"); + + if (led_supported == TPACPI_LED_570) { + /* 570 */ + int i, status; + for (i = 0; i < 8; i++) { + if (!acpi_evalf(ec_handle, + &status, "GLED", "dd", 1 << i)) + return -EIO; + len += sprintf(p + len, "%d:\t\t%s\n", + i, led_status(status)); + } + } + + len += sprintf(p + len, "commands:\t" + " on, off, blink ( is 0-7)\n"); + + return len; +} + +/* off, on, blink */ +static const int led_sled_arg1[] = { 0, 1, 3 }; +static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ +static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ +static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; + +static int led_write(char *buf) +{ + char *cmd; + int led, ind, ret; + + if (!led_supported) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) + return -EINVAL; + + if (strstr(cmd, "off")) { + ind = 0; + } else if (strstr(cmd, "on")) { + ind = 1; + } else if (strstr(cmd, "blink")) { + ind = 2; + } else + return -EINVAL; + + if (led_supported == TPACPI_LED_570) { + /* 570 */ + led = 1 << led; + if (!acpi_evalf(led_handle, NULL, NULL, "vdd", + led, led_sled_arg1[ind])) + return -EIO; + } else if (led_supported == TPACPI_LED_OLD) { + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ + led = 1 << led; + ret = ec_write(TPACPI_LED_EC_HLMS, led); + if (ret >= 0) + ret = + ec_write(TPACPI_LED_EC_HLBL, + led * led_exp_hlbl[ind]); + if (ret >= 0) + ret = + ec_write(TPACPI_LED_EC_HLCL, + led * led_exp_hlcl[ind]); + if (ret < 0) + return ret; + } else { + /* all others */ + if (!acpi_evalf(led_handle, NULL, NULL, "vdd", + led, led_led_arg1[ind])) + return -EIO; + } + } + + return 0; +} + +static struct ibm_struct led_driver_data = { + .name = "led", + .read = led_read, + .write = led_write, +}; + +/************************************************************************* + * Beep subdriver + */ + +IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ + +static int __init beep_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); + + IBM_ACPIHANDLE_INIT(beep); + + vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", + str_supported(beep_handle != NULL)); + + return (beep_handle)? 0 : 1; +} + +static int beep_read(char *p) +{ + int len = 0; + + if (!beep_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "commands:\t ( is 0-17)\n"); + } + + return len; +} + +static int beep_write(char *buf) +{ + char *cmd; + int beep_cmd; + + if (!beep_handle) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &beep_cmd) == 1 && + beep_cmd >= 0 && beep_cmd <= 17) { + /* beep_cmd set */ + } else + return -EINVAL; + if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) + return -EIO; + } + + return 0; +} + +static struct ibm_struct beep_driver_data = { + .name = "beep", + .read = beep_read, + .write = beep_write, +}; + +/************************************************************************* + * Thermal subdriver + */ + +static enum thermal_access_mode thermal_read_mode; + +/* sysfs temp##_input -------------------------------------------------- */ + +static ssize_t thermal_temp_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = + to_sensor_dev_attr(attr); + int idx = sensor_attr->index; + s32 value; + int res; + + res = thermal_get_sensor(idx, &value); + if (res) + return res; + if (value == TP_EC_THERMAL_TMP_NA * 1000) + return -ENXIO; + + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \ + SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB) + +static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = { + THERMAL_SENSOR_ATTR_TEMP(1, 0), + THERMAL_SENSOR_ATTR_TEMP(2, 1), + THERMAL_SENSOR_ATTR_TEMP(3, 2), + THERMAL_SENSOR_ATTR_TEMP(4, 3), + THERMAL_SENSOR_ATTR_TEMP(5, 4), + THERMAL_SENSOR_ATTR_TEMP(6, 5), + THERMAL_SENSOR_ATTR_TEMP(7, 6), + THERMAL_SENSOR_ATTR_TEMP(8, 7), + THERMAL_SENSOR_ATTR_TEMP(9, 8), + THERMAL_SENSOR_ATTR_TEMP(10, 9), + THERMAL_SENSOR_ATTR_TEMP(11, 10), + THERMAL_SENSOR_ATTR_TEMP(12, 11), + THERMAL_SENSOR_ATTR_TEMP(13, 12), + THERMAL_SENSOR_ATTR_TEMP(14, 13), + THERMAL_SENSOR_ATTR_TEMP(15, 14), + THERMAL_SENSOR_ATTR_TEMP(16, 15), +}; + +#define THERMAL_ATTRS(X) \ + &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr + +static struct attribute *thermal_temp_input_attr[] = { + THERMAL_ATTRS(8), + THERMAL_ATTRS(9), + THERMAL_ATTRS(10), + THERMAL_ATTRS(11), + THERMAL_ATTRS(12), + THERMAL_ATTRS(13), + THERMAL_ATTRS(14), + THERMAL_ATTRS(15), + THERMAL_ATTRS(0), + THERMAL_ATTRS(1), + THERMAL_ATTRS(2), + THERMAL_ATTRS(3), + THERMAL_ATTRS(4), + THERMAL_ATTRS(5), + THERMAL_ATTRS(6), + THERMAL_ATTRS(7), + NULL +}; + +static const struct attribute_group thermal_temp_input16_group = { + .attrs = thermal_temp_input_attr +}; + +static const struct attribute_group thermal_temp_input8_group = { + .attrs = &thermal_temp_input_attr[8] +}; + +#undef THERMAL_SENSOR_ATTR_TEMP +#undef THERMAL_ATTRS + +/* --------------------------------------------------------------------- */ + +static int __init thermal_init(struct ibm_init_struct *iibm) +{ + u8 t, ta1, ta2; + int i; + int acpi_tmp7; + int res; + + vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n"); + + acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); + + if (ibm_thinkpad_ec_found && experimental) { + /* + * Direct EC access mode: sensors at registers + * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for + * non-implemented, thermal sensors return 0x80 when + * not available + */ + + ta1 = ta2 = 0; + for (i = 0; i < 8; i++) { + if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) { + ta1 |= t; + } else { + ta1 = 0; + break; + } + if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) { + ta2 |= t; + } else { + ta1 = 0; + break; + } + } + if (ta1 == 0) { + /* This is sheer paranoia, but we handle it anyway */ + if (acpi_tmp7) { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "falling back to ACPI TMPx access mode\n"); + thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "disabling thermal sensors access\n"); + thermal_read_mode = TPACPI_THERMAL_NONE; + } + } else { + thermal_read_mode = + (ta2 != 0) ? + TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; + } + } else if (acpi_tmp7) { + if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + /* 600e/x, 770e, 770x */ + thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; + } else { + /* Standard ACPI TMPx access, max 8 sensors */ + thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; + } + } else { + /* temperatures not supported on 570, G4x, R30, R31, R32 */ + thermal_read_mode = TPACPI_THERMAL_NONE; + } + + vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n", + str_supported(thermal_read_mode != TPACPI_THERMAL_NONE), + thermal_read_mode); + + switch(thermal_read_mode) { + case TPACPI_THERMAL_TPEC_16: + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + if (res) + return res; + break; + case TPACPI_THERMAL_TPEC_8: + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input8_group); + if (res) + return res; + break; + case TPACPI_THERMAL_NONE: + default: + return 1; + } + + return 0; +} + +static void thermal_exit(void) +{ + switch(thermal_read_mode) { + case TPACPI_THERMAL_TPEC_16: + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + break; + case TPACPI_THERMAL_TPEC_8: + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + break; + case TPACPI_THERMAL_NONE: + default: + break; + } +} + +/* idx is zero-based */ +static int thermal_get_sensor(int idx, s32 *value) +{ + int t; + s8 tmp; + char tmpi[5]; + + t = TP_EC_THERMAL_TMP0; + + switch (thermal_read_mode) { +#if TPACPI_MAX_THERMAL_SENSORS >= 16 + case TPACPI_THERMAL_TPEC_16: + if (idx >= 8 && idx <= 15) { + t = TP_EC_THERMAL_TMP8; + idx -= 8; + } + /* fallthrough */ +#endif + case TPACPI_THERMAL_TPEC_8: + if (idx <= 7) { + if (!acpi_ec_read(t + idx, &tmp)) + return -EIO; + *value = tmp * 1000; + return 0; + } + break; + + case TPACPI_THERMAL_ACPI_UPDT: + if (idx <= 7) { + snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); + if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) + return -EIO; + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) + return -EIO; + *value = (t - 2732) * 100; + return 0; + } + break; + + case TPACPI_THERMAL_ACPI_TMP07: + if (idx <= 7) { + snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) + return -EIO; + *value = t * 1000; + return 0; + } + break; + + case TPACPI_THERMAL_NONE: + default: + return -ENOSYS; + } + + return -EINVAL; +} + +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) +{ + int res, i; + int n; + + n = 8; + i = 0; + + if (!s) + return -EINVAL; + + if (thermal_read_mode == TPACPI_THERMAL_TPEC_16) + n = 16; + + for(i = 0 ; i < n; i++) { + res = thermal_get_sensor(i, &s->temp[i]); + if (res) + return res; + } + + return n; +} + +static int thermal_read(char *p) +{ + int len = 0; + int n, i; + struct ibm_thermal_sensors_struct t; + + n = thermal_get_sensors(&t); + if (unlikely(n < 0)) + return n; + + len += sprintf(p + len, "temperatures:\t"); + + if (n > 0) { + for (i = 0; i < (n - 1); i++) + len += sprintf(p + len, "%d ", t.temp[i] / 1000); + len += sprintf(p + len, "%d\n", t.temp[i] / 1000); + } else + len += sprintf(p + len, "not supported\n"); + + return len; +} + +static struct ibm_struct thermal_driver_data = { + .name = "thermal", + .read = thermal_read, + .exit = thermal_exit, +}; + +/************************************************************************* + * EC Dump subdriver + */ + +static u8 ecdump_regs[256]; + +static int ecdump_read(char *p) +{ + int len = 0; + int i, j; + u8 v; + + len += sprintf(p + len, "EC " + " +00 +01 +02 +03 +04 +05 +06 +07" + " +08 +09 +0a +0b +0c +0d +0e +0f\n"); + for (i = 0; i < 256; i += 16) { + len += sprintf(p + len, "EC 0x%02x:", i); + for (j = 0; j < 16; j++) { + if (!acpi_ec_read(i + j, &v)) + break; + if (v != ecdump_regs[i + j]) + len += sprintf(p + len, " *%02x", v); + else + len += sprintf(p + len, " %02x", v); + ecdump_regs[i + j] = v; + } + len += sprintf(p + len, "\n"); + if (j != 16) + break; + } + + /* These are way too dangerous to advertise openly... */ +#if 0 + len += sprintf(p + len, "commands:\t0x 0x" + " ( is 00-ff, is 00-ff)\n"); + len += sprintf(p + len, "commands:\t0x " + " ( is 00-ff, is 0-255)\n"); +#endif + return len; +} + +static int ecdump_write(char *buf) +{ + char *cmd; + int i, v; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { + /* i and v set */ + } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { + /* i and v set */ + } else + return -EINVAL; + if (i >= 0 && i < 256 && v >= 0 && v < 256) { + if (!acpi_ec_write(i, v)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +static struct ibm_struct ecdump_driver_data = { + .name = "ecdump", + .read = ecdump_read, + .write = ecdump_write, + .flags.experimental = 1, +}; + +/************************************************************************* + * Backlight/brightness subdriver + */ + +static struct backlight_device *ibm_backlight_device = NULL; + +static struct backlight_ops ibm_backlight_data = { + .get_brightness = brightness_get, + .update_status = brightness_update_status, +}; + +static int __init brightness_init(struct ibm_init_struct *iibm) +{ + int b; + + vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); + + b = brightness_get(NULL); + if (b < 0) + return b; + + ibm_backlight_device = backlight_device_register( + TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, + &ibm_backlight_data); + if (IS_ERR(ibm_backlight_device)) { + printk(IBM_ERR "Could not register backlight device\n"); + return PTR_ERR(ibm_backlight_device); + } + vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); + + ibm_backlight_device->props.max_brightness = 7; + ibm_backlight_device->props.brightness = b; + backlight_update_status(ibm_backlight_device); + + return 0; +} + +static void brightness_exit(void) +{ + if (ibm_backlight_device) { + vdbg_printk(TPACPI_DBG_EXIT, + "calling backlight_device_unregister()\n"); + backlight_device_unregister(ibm_backlight_device); + ibm_backlight_device = NULL; + } +} + +static int brightness_update_status(struct backlight_device *bd) +{ + return brightness_set( + (bd->props.fb_blank == FB_BLANK_UNBLANK && + bd->props.power == FB_BLANK_UNBLANK) ? + bd->props.brightness : 0); +} + +static int brightness_get(struct backlight_device *bd) +{ + u8 level; + if (!acpi_ec_read(brightness_offset, &level)) + return -EIO; + + level &= 0x7; + + return level; +} + +static int brightness_set(int value) +{ + int cmos_cmd, inc, i; + int current_value = brightness_get(NULL); + + value &= 7; + + cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; + inc = value > current_value ? 1 : -1; + for (i = current_value; i != value; i += inc) { + if (issue_thinkpad_cmos_command(cmos_cmd)) + return -EIO; + if (!acpi_ec_write(brightness_offset, i + inc)) + return -EIO; + } + + return 0; +} + +static int brightness_read(char *p) +{ + int len = 0; + int level; + + if ((level = brightness_get(NULL)) < 0) { + len += sprintf(p + len, "level:\t\tunreadable\n"); + } else { + len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); + len += sprintf(p + len, "commands:\tup, down\n"); + len += sprintf(p + len, "commands:\tlevel " + " ( is 0-7)\n"); + } + + return len; +} + +static int brightness_write(char *buf) +{ + int level; + int new_level; + char *cmd; + + while ((cmd = next_cmd(&buf))) { + if ((level = brightness_get(NULL)) < 0) + return level; + level &= 7; + + if (strlencmp(cmd, "up") == 0) { + new_level = level == 7 ? 7 : level + 1; + } else if (strlencmp(cmd, "down") == 0) { + new_level = level == 0 ? 0 : level - 1; + } else if (sscanf(cmd, "level %d", &new_level) == 1 && + new_level >= 0 && new_level <= 7) { + /* new_level set */ + } else + return -EINVAL; + + brightness_set(new_level); + } + + return 0; +} + +static struct ibm_struct brightness_driver_data = { + .name = "brightness", + .read = brightness_read, + .write = brightness_write, + .exit = brightness_exit, +}; + +/************************************************************************* + * Volume subdriver + */ + +static int volume_read(char *p) +{ + int len = 0; + u8 level; + + if (!acpi_ec_read(volume_offset, &level)) { + len += sprintf(p + len, "level:\t\tunreadable\n"); + } else { + len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); + len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); + len += sprintf(p + len, "commands:\tup, down, mute\n"); + len += sprintf(p + len, "commands:\tlevel " + " ( is 0-15)\n"); + } + + return len; +} + +static int volume_write(char *buf) +{ + int cmos_cmd, inc, i; + u8 level, mute; + int new_level, new_mute; + char *cmd; + + while ((cmd = next_cmd(&buf))) { + if (!acpi_ec_read(volume_offset, &level)) + return -EIO; + new_mute = mute = level & 0x40; + new_level = level = level & 0xf; + + if (strlencmp(cmd, "up") == 0) { + if (mute) + new_mute = 0; + else + new_level = level == 15 ? 15 : level + 1; + } else if (strlencmp(cmd, "down") == 0) { + if (mute) + new_mute = 0; + else + new_level = level == 0 ? 0 : level - 1; + } else if (sscanf(cmd, "level %d", &new_level) == 1 && + new_level >= 0 && new_level <= 15) { + /* new_level set */ + } else if (strlencmp(cmd, "mute") == 0) { + new_mute = 0x40; + } else + return -EINVAL; + + if (new_level != level) { /* mute doesn't change */ + cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; + inc = new_level > level ? 1 : -1; + + if (mute && (issue_thinkpad_cmos_command(cmos_cmd) || + !acpi_ec_write(volume_offset, level))) + return -EIO; + + for (i = level; i != new_level; i += inc) + if (issue_thinkpad_cmos_command(cmos_cmd) || + !acpi_ec_write(volume_offset, i + inc)) + return -EIO; + + if (mute && (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) || + !acpi_ec_write(volume_offset, + new_level + mute))) + return -EIO; + } + + if (new_mute != mute) { /* level doesn't change */ + cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; + + if (issue_thinkpad_cmos_command(cmos_cmd) || + !acpi_ec_write(volume_offset, level + new_mute)) + return -EIO; + } + } + + return 0; +} + +static struct ibm_struct volume_driver_data = { + .name = "volume", + .read = volume_read, + .write = volume_write, +}; + +/************************************************************************* + * Fan subdriver + */ + +/* + * FAN ACCESS MODES + * + * TPACPI_FAN_RD_ACPI_GFAN: + * ACPI GFAN method: returns fan level + * + * see TPACPI_FAN_WR_ACPI_SFAN + * EC 0x2f (HFSP) not available if GFAN exists + * + * TPACPI_FAN_WR_ACPI_SFAN: + * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) + * + * EC 0x2f (HFSP) might be available *for reading*, but do not use + * it for writing. + * + * TPACPI_FAN_WR_TPEC: + * ThinkPad EC register 0x2f (HFSP): fan control loop mode + * Supported on almost all ThinkPads + * + * Fan speed changes of any sort (including those caused by the + * disengaged mode) are usually done slowly by the firmware as the + * maximum ammount of fan duty cycle change per second seems to be + * limited. + * + * Reading is not available if GFAN exists. + * Writing is not available if SFAN exists. + * + * Bits + * 7 automatic mode engaged; + * (default operation mode of the ThinkPad) + * fan level is ignored in this mode. + * 6 full speed mode (takes precedence over bit 7); + * not available on all thinkpads. May disable + * the tachometer while the fan controller ramps up + * the speed (which can take up to a few *minutes*). + * Speeds up fan to 100% duty-cycle, which is far above + * the standard RPM levels. It is not impossible that + * it could cause hardware damage. + * 5-3 unused in some models. Extra bits for fan level + * in others, but still useless as all values above + * 7 map to the same speed as level 7 in these models. + * 2-0 fan level (0..7 usually) + * 0x00 = stop + * 0x07 = max (set when temperatures critical) + * Some ThinkPads may have other levels, see + * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41) + * + * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at + * boot. Apparently the EC does not intialize it, so unless ACPI DSDT + * does so, its initial value is meaningless (0x07). + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * ---- + * + * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): + * Main fan tachometer reading (in RPM) + * + * This register is present on all ThinkPads with a new-style EC, and + * it is known not to be present on the A21m/e, and T22, as there is + * something else in offset 0x84 according to the ACPI DSDT. Other + * ThinkPads from this same time period (and earlier) probably lack the + * tachometer as well. + * + * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare + * was never fixed by IBM to report the EC firmware version string + * probably support the tachometer (like the early X models), so + * detecting it is quite hard. We need more data to know for sure. + * + * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings + * might result. + * + * FIRMWARE BUG: may go stale while the EC is switching to full speed + * mode. + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * TPACPI_FAN_WR_ACPI_FANS: + * ThinkPad X31, X40, X41. Not available in the X60. + * + * FANS ACPI handle: takes three arguments: low speed, medium speed, + * high speed. ACPI DSDT seems to map these three speeds to levels + * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH + * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") + * + * The speeds are stored on handles + * (FANA:FAN9), (FANC:FANB), (FANE:FAND). + * + * There are three default speed sets, acessible as handles: + * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H + * + * ACPI DSDT switches which set is in use depending on various + * factors. + * + * TPACPI_FAN_WR_TPEC is also available and should be used to + * command the fan. The X31/X40/X41 seems to have 8 fan levels, + * but the ACPI tables just mention level 7. + */ + +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; + +static u8 fan_control_initial_status; +static u8 fan_control_desired_level; + +static void fan_watchdog_fire(struct work_struct *ignored); +static int fan_watchdog_maxinterval; +static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire); + +IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ +IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ + "\\FSPD", /* 600e/x, 770e, 770x */ + ); /* all others */ +IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ + "JFNS", /* 770x-JL */ + ); /* all others */ + +/* + * SYSFS fan layout: hwmon compatible (device) + * + * pwm*_enable: + * 0: "disengaged" mode + * 1: manual mode + * 2: native EC "auto" mode (recommended, hardware default) + * + * pwm*: set speed in manual mode, ignored otherwise. + * 0 is level 0; 255 is level 7. Intermediate points done with linear + * interpolation. + * + * fan*_input: tachometer reading, RPM + * + * + * SYSFS fan layout: extensions + * + * fan_watchdog (driver): + * fan watchdog interval in seconds, 0 disables (default), max 120 + */ + +/* sysfs fan pwm1_enable ----------------------------------------------- */ +static ssize_t fan_pwm1_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, mode; + u8 status; + + res = fan_get_status_safe(&status); + if (res) + return res; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) { + tp_features.fan_ctrl_status_undef = 0; + } else { + /* Return most likely status. In fact, it + * might be the only possible status */ + status = TP_EC_FAN_AUTO; + } + } + + if (status & TP_EC_FAN_FULLSPEED) { + mode = 0; + } else if (status & TP_EC_FAN_AUTO) { + mode = 2; + } else + mode = 1; + + return snprintf(buf, PAGE_SIZE, "%d\n", mode); +} + +static ssize_t fan_pwm1_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, level; + + if (parse_strtoul(buf, 2, &t)) + return -EINVAL; + + switch (t) { + case 0: + level = TP_EC_FAN_FULLSPEED; + break; + case 1: + level = TPACPI_FAN_LAST_LEVEL; + break; + case 2: + level = TP_EC_FAN_AUTO; + break; + case 3: + /* reserved for software-controlled auto mode */ + return -ENOSYS; + default: + return -EINVAL; + } + + res = fan_set_level_safe(level); + if (res == -ENXIO) + return -EINVAL; + else if (res < 0) + return res; + + fan_watchdog_reset(); + + return count; +} + +static struct device_attribute dev_attr_fan_pwm1_enable = + __ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + fan_pwm1_enable_show, fan_pwm1_enable_store); + +/* sysfs fan pwm1 ------------------------------------------------------ */ +static ssize_t fan_pwm1_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res; + u8 status; + + res = fan_get_status_safe(&status); + if (res) + return res; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) { + tp_features.fan_ctrl_status_undef = 0; + } else { + status = TP_EC_FAN_AUTO; + } + } + + if ((status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) + status = fan_control_desired_level; + + if (status > 7) + status = 7; + + return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7); +} + +static ssize_t fan_pwm1_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long s; + int rc; + u8 status, newlevel; + + if (parse_strtoul(buf, 255, &s)) + return -EINVAL; + + /* scale down from 0-255 to 0-7 */ + newlevel = (s >> 5) & 0x07; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + rc = fan_get_status(&status); + if (!rc && (status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { + rc = fan_set_level(newlevel); + if (rc == -ENXIO) + rc = -EINVAL; + else if (!rc) { + fan_update_desired_level(newlevel); + fan_watchdog_reset(); + } + } + + mutex_unlock(&fan_mutex); + return (rc)? rc : count; +} + +static struct device_attribute dev_attr_fan_pwm1 = + __ATTR(pwm1, S_IWUSR | S_IRUGO, + fan_pwm1_show, fan_pwm1_store); + +/* sysfs fan fan1_input ------------------------------------------------ */ +static ssize_t fan_fan1_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res; + unsigned int speed; + + res = fan_get_speed(&speed); + if (res < 0) + return res; + + return snprintf(buf, PAGE_SIZE, "%u\n", speed); +} + +static struct device_attribute dev_attr_fan_fan1_input = + __ATTR(fan1_input, S_IRUGO, + fan_fan1_input_show, NULL); + +/* sysfs fan fan_watchdog (driver) ------------------------------------- */ +static ssize_t fan_fan_watchdog_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval); +} + +static ssize_t fan_fan_watchdog_store(struct device_driver *drv, + const char *buf, size_t count) +{ + unsigned long t; + + if (parse_strtoul(buf, 120, &t)) + return -EINVAL; + + if (!fan_control_allowed) + return -EPERM; + + fan_watchdog_maxinterval = t; + fan_watchdog_reset(); + + return count; +} + +static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, + fan_fan_watchdog_show, fan_fan_watchdog_store); + +/* --------------------------------------------------------------------- */ +static struct attribute *fan_attributes[] = { + &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, + &dev_attr_fan_fan1_input.attr, + NULL +}; + +static const struct attribute_group fan_attr_group = { + .attrs = fan_attributes, +}; + +static int __init fan_init(struct ibm_init_struct *iibm) +{ + int rc; + + vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n"); + + mutex_init(&fan_mutex); + fan_status_access_mode = TPACPI_FAN_NONE; + fan_control_access_mode = TPACPI_FAN_WR_NONE; + fan_control_commands = 0; + fan_watchdog_maxinterval = 0; + tp_features.fan_ctrl_status_undef = 0; + fan_control_desired_level = 7; + + IBM_ACPIHANDLE_INIT(fans); + IBM_ACPIHANDLE_INIT(gfan); + IBM_ACPIHANDLE_INIT(sfan); + + if (gfan_handle) { + /* 570, 600e/x, 770e, 770x */ + fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; + } else { + /* all other ThinkPads: note that even old-style + * ThinkPad ECs supports the fan control register */ + if (likely(acpi_ec_read(fan_status_offset, + &fan_control_initial_status))) { + fan_status_access_mode = TPACPI_FAN_RD_TPEC; + + /* In some ThinkPads, neither the EC nor the ACPI + * DSDT initialize the fan status, and it ends up + * being set to 0x07 when it *could* be either + * 0x07 or 0x80. + * + * Enable for TP-1Y (T43), TP-78 (R51e), + * TP-76 (R52), TP-70 (T43, R52), which are known + * to be buggy. */ + if (fan_control_initial_status == 0x07 && + ibm_thinkpad_ec_found && + ((ibm_thinkpad_ec_found[0] == '1' && + ibm_thinkpad_ec_found[1] == 'Y') || + (ibm_thinkpad_ec_found[0] == '7' && + (ibm_thinkpad_ec_found[1] == '6' || + ibm_thinkpad_ec_found[1] == '8' || + ibm_thinkpad_ec_found[1] == '0')) + )) { + printk(IBM_NOTICE + "fan_init: initial fan status is " + "unknown, assuming it is in auto " + "mode\n"); + tp_features.fan_ctrl_status_undef = 1; + } + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "fan status and control unavailable\n"); + return 1; + } + } + + if (sfan_handle) { + /* 570, 770x-JL */ + fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE; + } else { + if (!gfan_handle) { + /* gfan without sfan means no fan control */ + /* all other models implement TP EC 0x2f control */ + + if (fans_handle) { + /* X31, X40, X41 */ + fan_control_access_mode = + TPACPI_FAN_WR_ACPI_FANS; + fan_control_commands |= + TPACPI_FAN_CMD_SPEED | + TPACPI_FAN_CMD_LEVEL | + TPACPI_FAN_CMD_ENABLE; + } else { + fan_control_access_mode = TPACPI_FAN_WR_TPEC; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | + TPACPI_FAN_CMD_ENABLE; + } + } + } + + vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n", + str_supported(fan_status_access_mode != TPACPI_FAN_NONE || + fan_control_access_mode != TPACPI_FAN_WR_NONE), + fan_status_access_mode, fan_control_access_mode); + + /* fan control master switch */ + if (!fan_control_allowed) { + fan_control_access_mode = TPACPI_FAN_WR_NONE; + fan_control_commands = 0; + dbg_printk(TPACPI_DBG_INIT, + "fan control features disabled by parameter\n"); + } + + /* update fan_control_desired_level */ + if (fan_status_access_mode != TPACPI_FAN_NONE) + fan_get_status_safe(NULL); + + if (fan_status_access_mode != TPACPI_FAN_NONE || + fan_control_access_mode != TPACPI_FAN_WR_NONE) { + rc = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &fan_attr_group); + if (!(rc < 0)) + rc = driver_create_file(&tpacpi_pdriver.driver, + &driver_attr_fan_watchdog); + if (rc < 0) + return rc; + return 0; + } else + return 1; +} + +/* + * Call with fan_mutex held + */ +static void fan_update_desired_level(u8 status) +{ + if ((status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { + if (status > 7) + fan_control_desired_level = 7; + else + fan_control_desired_level = status; + } +} + +static int fan_get_status(u8 *status) +{ + u8 s; + + /* TODO: + * Add TPACPI_FAN_RD_ACPI_FANS ? */ + + switch (fan_status_access_mode) { + case TPACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + + if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) + return -EIO; + + if (likely(status)) + *status = s & 0x07; + + break; + + case TPACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!acpi_ec_read(fan_status_offset, &s))) + return -EIO; + + if (likely(status)) + *status = s; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static int fan_get_status_safe(u8 *status) +{ + int rc; + u8 s; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + rc = fan_get_status(&s); + if (!rc) + fan_update_desired_level(s); + mutex_unlock(&fan_mutex); + + if (status) + *status = s; + + return rc; +} + +static void fan_exit(void) +{ + vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); + + /* FIXME: can we really do this unconditionally? */ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group); + driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog); + + cancel_delayed_work(&fan_watchdog_task); + flush_scheduled_work(); +} + +static int fan_get_speed(unsigned int *speed) +{ + u8 hi, lo; + + switch (fan_status_access_mode) { + case TPACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || + !acpi_ec_read(fan_rpm_offset + 1, &hi))) + return -EIO; + + if (likely(speed)) + *speed = (hi << 8) | lo; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static void fan_watchdog_fire(struct work_struct *ignored) +{ + int rc; + + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); + rc = fan_set_enable(); + if (rc < 0) { + printk(IBM_ERR "fan watchdog: error %d while enabling fan, " + "will try again later...\n", -rc); + /* reschedule for later */ + fan_watchdog_reset(); + } +} + +static void fan_watchdog_reset(void) +{ + static int fan_watchdog_active = 0; + + if (fan_control_access_mode == TPACPI_FAN_WR_NONE) + return; + + if (fan_watchdog_active) + cancel_delayed_work(&fan_watchdog_task); + + if (fan_watchdog_maxinterval > 0) { + fan_watchdog_active = 1; + if (!schedule_delayed_work(&fan_watchdog_task, + msecs_to_jiffies(fan_watchdog_maxinterval + * 1000))) { + printk(IBM_ERR "failed to schedule the fan watchdog, " + "watchdog will not trigger\n"); + } + } else + fan_watchdog_active = 0; +} + +static int fan_set_level(int level) +{ + if (!fan_control_allowed) + return -EPERM; + + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_SFAN: + if (level >= 0 && level <= 7) { + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) + return -EIO; + } else + return -EINVAL; + break; + + case TPACPI_FAN_WR_ACPI_FANS: + case TPACPI_FAN_WR_TPEC: + if ((level != TP_EC_FAN_AUTO) && + (level != TP_EC_FAN_FULLSPEED) && + ((level < 0) || (level > 7))) + return -EINVAL; + + /* safety net should the EC not support AUTO + * or FULLSPEED mode bits and just ignore them */ + if (level & TP_EC_FAN_FULLSPEED) + level |= 7; /* safety min speed 7 */ + else if (level & TP_EC_FAN_FULLSPEED) + level |= 4; /* safety min speed 4 */ + + if (!acpi_ec_write(fan_status_offset, level)) + return -EIO; + else + tp_features.fan_ctrl_status_undef = 0; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_level_safe(int level) +{ + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + if (level == TPACPI_FAN_LAST_LEVEL) + level = fan_control_desired_level; + + rc = fan_set_level(level); + if (!rc) + fan_update_desired_level(level); + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_set_enable(void) +{ + u8 s; + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_FANS: + case TPACPI_FAN_WR_TPEC: + rc = fan_get_status(&s); + if (rc < 0) + break; + + /* Don't go out of emergency fan mode */ + if (s != 7) { + s &= 0x07; + s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */ + } + + if (!acpi_ec_write(fan_status_offset, s)) + rc = -EIO; + else { + tp_features.fan_ctrl_status_undef = 0; + rc = 0; + } + break; + + case TPACPI_FAN_WR_ACPI_SFAN: + rc = fan_get_status(&s); + if (rc < 0) + break; + + s &= 0x07; + + /* Set fan to at least level 4 */ + s |= 4; + + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) + rc= -EIO; + else + rc = 0; + break; + + default: + rc = -ENXIO; + } + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_set_disable(void) +{ + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + rc = 0; + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_FANS: + case TPACPI_FAN_WR_TPEC: + if (!acpi_ec_write(fan_status_offset, 0x00)) + rc = -EIO; + else { + fan_control_desired_level = 0; + tp_features.fan_ctrl_status_undef = 0; + } + break; + + case TPACPI_FAN_WR_ACPI_SFAN: + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) + rc = -EIO; + else + fan_control_desired_level = 0; + break; + + default: + rc = -ENXIO; + } + + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_set_speed(int speed) +{ + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + rc = 0; + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_FANS: + if (speed >= 0 && speed <= 65535) { + if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", + speed, speed, speed)) + rc = -EIO; + } else + rc = -EINVAL; + break; + + default: + rc = -ENXIO; + } + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_read(char *p) +{ + int len = 0; + int rc; + u8 status; + unsigned int speed = 0; + + switch (fan_status_access_mode) { + case TPACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status_safe(&status)) < 0) + return rc; + + len += sprintf(p + len, "status:\t\t%s\n" + "level:\t\t%d\n", + (status != 0) ? "enabled" : "disabled", status); + break; + + case TPACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status_safe(&status)) < 0) + return rc; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) + tp_features.fan_ctrl_status_undef = 0; + else + /* Return most likely status. In fact, it + * might be the only possible status */ + status = TP_EC_FAN_AUTO; + } + + len += sprintf(p + len, "status:\t\t%s\n", + (status != 0) ? "enabled" : "disabled"); + + if ((rc = fan_get_speed(&speed)) < 0) + return rc; + + len += sprintf(p + len, "speed:\t\t%d\n", speed); + + if (status & TP_EC_FAN_FULLSPEED) + /* Disengaged mode takes precedence */ + len += sprintf(p + len, "level:\t\tdisengaged\n"); + else if (status & TP_EC_FAN_AUTO) + len += sprintf(p + len, "level:\t\tauto\n"); + else + len += sprintf(p + len, "level:\t\t%d\n", status); + break; + + case TPACPI_FAN_NONE: + default: + len += sprintf(p + len, "status:\t\tnot supported\n"); + } + + if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { + len += sprintf(p + len, "commands:\tlevel "); + + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_SFAN: + len += sprintf(p + len, " ( is 0-7)\n"); + break; + + default: + len += sprintf(p + len, " ( is 0-7, " + "auto, disengaged, full-speed)\n"); + break; + } + } + + if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) + len += sprintf(p + len, "commands:\tenable, disable\n" + "commands:\twatchdog ( is 0 (off), " + "1-120 (seconds))\n"); + + if (fan_control_commands & TPACPI_FAN_CMD_SPEED) + len += sprintf(p + len, "commands:\tspeed " + " ( is 0-65535)\n"); + + return len; +} + +static int fan_write_cmd_level(const char *cmd, int *rc) +{ + int level; + + if (strlencmp(cmd, "level auto") == 0) + level = TP_EC_FAN_AUTO; + else if ((strlencmp(cmd, "level disengaged") == 0) | + (strlencmp(cmd, "level full-speed") == 0)) + level = TP_EC_FAN_FULLSPEED; + else if (sscanf(cmd, "level %d", &level) != 1) + return 0; + + if ((*rc = fan_set_level_safe(level)) == -ENXIO) + printk(IBM_ERR "level command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_enable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "enable") != 0) + return 0; + + if ((*rc = fan_set_enable()) == -ENXIO) + printk(IBM_ERR "enable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_disable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "disable") != 0) + return 0; + + if ((*rc = fan_set_disable()) == -ENXIO) + printk(IBM_ERR "disable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_speed(const char *cmd, int *rc) +{ + int speed; + + /* TODO: + * Support speed ? */ + + if (sscanf(cmd, "speed %d", &speed) != 1) + return 0; + + if ((*rc = fan_set_speed(speed)) == -ENXIO) + printk(IBM_ERR "speed command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_watchdog(const char *cmd, int *rc) +{ + int interval; + + if (sscanf(cmd, "watchdog %d", &interval) != 1) + return 0; + + if (interval < 0 || interval > 120) + *rc = -EINVAL; + else + fan_watchdog_maxinterval = interval; + + return 1; +} + +static int fan_write(char *buf) +{ + char *cmd; + int rc = 0; + + while (!rc && (cmd = next_cmd(&buf))) { + if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) && + fan_write_cmd_level(cmd, &rc)) && + !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) && + (fan_write_cmd_enable(cmd, &rc) || + fan_write_cmd_disable(cmd, &rc) || + fan_write_cmd_watchdog(cmd, &rc))) && + !((fan_control_commands & TPACPI_FAN_CMD_SPEED) && + fan_write_cmd_speed(cmd, &rc)) + ) + rc = -EINVAL; + else if (!rc) + fan_watchdog_reset(); + } + + return rc; +} + +static struct ibm_struct fan_driver_data = { + .name = "fan", + .read = fan_read, + .write = fan_write, + .exit = fan_exit, +}; + +/**************************************************************************** + **************************************************************************** + * + * Infrastructure + * + **************************************************************************** + ****************************************************************************/ + +/* /proc support */ +static struct proc_dir_entry *proc_dir = NULL; + +/* Subdriver registry */ +static LIST_HEAD(tpacpi_all_drivers); + + +/* + * Module and infrastructure proble, init and exit handling + */ + +#ifdef CONFIG_THINKPAD_ACPI_DEBUG +static const char * __init str_supported(int is_supported) +{ + static char text_unsupported[] __initdata = "not supported"; + + return (is_supported)? &text_unsupported[4] : &text_unsupported[0]; +} +#endif /* CONFIG_THINKPAD_ACPI_DEBUG */ + +static int __init ibm_init(struct ibm_init_struct *iibm) +{ + int ret; + struct ibm_struct *ibm = iibm->data; + struct proc_dir_entry *entry; + + BUG_ON(ibm == NULL); + + INIT_LIST_HEAD(&ibm->all_drivers); + + if (ibm->flags.experimental && !experimental) + return 0; + + dbg_printk(TPACPI_DBG_INIT, + "probing for %s\n", ibm->name); + + if (iibm->init) { + ret = iibm->init(iibm); + if (ret > 0) + return 0; /* probe failed */ + if (ret) + return ret; + + ibm->flags.init_called = 1; + } + + if (ibm->acpi) { + if (ibm->acpi->hid) { + ret = register_tpacpi_subdriver(ibm); + if (ret) + goto err_out; + } + + if (ibm->acpi->notify) { + ret = setup_acpi_notify(ibm); + if (ret == -ENODEV) { + printk(IBM_NOTICE "disabling subdriver %s\n", + ibm->name); + ret = 0; + goto err_out; + } + if (ret < 0) + goto err_out; + } + } + + dbg_printk(TPACPI_DBG_INIT, + "%s installed\n", ibm->name); + + if (ibm->read) { + entry = create_proc_entry(ibm->name, + S_IFREG | S_IRUGO | S_IWUSR, + proc_dir); + if (!entry) { + printk(IBM_ERR "unable to create proc entry %s\n", + ibm->name); + ret = -ENODEV; + goto err_out; + } + entry->owner = THIS_MODULE; + entry->data = ibm; + entry->read_proc = &dispatch_procfs_read; + if (ibm->write) + entry->write_proc = &dispatch_procfs_write; + ibm->flags.proc_created = 1; + } + + list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers); + + return 0; + +err_out: + dbg_printk(TPACPI_DBG_INIT, + "%s: at error exit path with result %d\n", + ibm->name, ret); + + ibm_exit(ibm); + return (ret < 0)? ret : 0; +} + +static void ibm_exit(struct ibm_struct *ibm) +{ + dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name); + + list_del_init(&ibm->all_drivers); + + if (ibm->flags.acpi_notify_installed) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: acpi_remove_notify_handler\n", ibm->name); + BUG_ON(!ibm->acpi); + acpi_remove_notify_handler(*ibm->acpi->handle, + ibm->acpi->type, + dispatch_acpi_notify); + ibm->flags.acpi_notify_installed = 0; + ibm->flags.acpi_notify_installed = 0; + } + + if (ibm->flags.proc_created) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: remove_proc_entry\n", ibm->name); + remove_proc_entry(ibm->name, proc_dir); + ibm->flags.proc_created = 0; + } + + if (ibm->flags.acpi_driver_registered) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: acpi_bus_unregister_driver\n", ibm->name); + BUG_ON(!ibm->acpi); + acpi_bus_unregister_driver(ibm->acpi->driver); + kfree(ibm->acpi->driver); + ibm->acpi->driver = NULL; + ibm->flags.acpi_driver_registered = 0; + } + + if (ibm->flags.init_called && ibm->exit) { + ibm->exit(); + ibm->flags.init_called = 0; + } + + dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name); +} + +/* Probing */ + +static char *ibm_thinkpad_ec_found = NULL; + +static char* __init check_dmi_for_ec(void) +{ + struct dmi_device *dev = NULL; + char ec_fw_string[18]; + + /* + * ThinkPad T23 or newer, A31 or newer, R50e or newer, + * X32 or newer, all Z series; Some models must have an + * up-to-date BIOS or they will not be detected. + * + * See http://thinkwiki.org/wiki/List_of_DMI_IDs + */ + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (sscanf(dev->name, + "IBM ThinkPad Embedded Controller -[%17c", + ec_fw_string) == 1) { + ec_fw_string[sizeof(ec_fw_string) - 1] = 0; + ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; + return kstrdup(ec_fw_string, GFP_KERNEL); + } + } + return NULL; +} + +static int __init probe_for_thinkpad(void) +{ + int is_thinkpad; + + if (acpi_disabled) + return -ENODEV; + + /* + * Non-ancient models have better DMI tagging, but very old models + * don't. + */ + is_thinkpad = dmi_name_in_vendors("ThinkPad"); + + /* ec is required because many other handles are relative to it */ + IBM_ACPIHANDLE_INIT(ec); + if (!ec_handle) { + if (is_thinkpad) + printk(IBM_ERR + "Not yet supported ThinkPad detected!\n"); + return -ENODEV; + } + + /* + * Risks a regression on very old machines, but reduces potential + * false positives a damn great deal + */ + if (!is_thinkpad) + is_thinkpad = dmi_name_in_vendors("IBM"); + + if (!is_thinkpad && !force_load) + return -ENODEV; + + return 0; +} + + +/* Module init, exit, parameters */ + +static struct ibm_init_struct ibms_init[] __initdata = { + { + .init = thinkpad_acpi_driver_init, + .data = &thinkpad_acpi_driver_data, + }, + { + .init = hotkey_init, + .data = &hotkey_driver_data, + }, + { + .init = bluetooth_init, + .data = &bluetooth_driver_data, + }, + { + .init = wan_init, + .data = &wan_driver_data, + }, + { + .init = video_init, + .data = &video_driver_data, + }, + { + .init = light_init, + .data = &light_driver_data, + }, +#ifdef CONFIG_THINKPAD_ACPI_DOCK + { + .init = dock_init, + .data = &dock_driver_data[0], + }, + { + .init = dock_init2, + .data = &dock_driver_data[1], + }, +#endif +#ifdef CONFIG_THINKPAD_ACPI_BAY + { + .init = bay_init, + .data = &bay_driver_data, + }, +#endif + { + .init = cmos_init, + .data = &cmos_driver_data, + }, + { + .init = led_init, + .data = &led_driver_data, + }, + { + .init = beep_init, + .data = &beep_driver_data, + }, + { + .init = thermal_init, + .data = &thermal_driver_data, + }, + { + .data = &ecdump_driver_data, + }, + { + .init = brightness_init, + .data = &brightness_driver_data, + }, + { + .data = &volume_driver_data, + }, + { + .init = fan_init, + .data = &fan_driver_data, + }, +}; + +static int __init set_ibm_param(const char *val, struct kernel_param *kp) +{ + unsigned int i; + struct ibm_struct *ibm; + + for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { + ibm = ibms_init[i].data; + BUG_ON(ibm == NULL); + + if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { + if (strlen(val) > sizeof(ibms_init[i].param) - 2) + return -ENOSPC; + strcpy(ibms_init[i].param, val); + strcat(ibms_init[i].param, ","); + return 0; + } + } + + return -EINVAL; +} + +static int experimental; +module_param(experimental, int, 0); + +static u32 dbg_level; +module_param_named(debug, dbg_level, uint, 0); + +static int force_load; +module_param(force_load, int, 0); + +static int fan_control_allowed; +module_param_named(fan_control, fan_control_allowed, int, 0); + +#define IBM_PARAM(feature) \ + module_param_call(feature, set_ibm_param, NULL, NULL, 0) + +IBM_PARAM(hotkey); +IBM_PARAM(bluetooth); +IBM_PARAM(video); +IBM_PARAM(light); +#ifdef CONFIG_THINKPAD_ACPI_DOCK +IBM_PARAM(dock); +#endif +#ifdef CONFIG_THINKPAD_ACPI_BAY +IBM_PARAM(bay); +#endif /* CONFIG_THINKPAD_ACPI_BAY */ +IBM_PARAM(cmos); +IBM_PARAM(led); +IBM_PARAM(beep); +IBM_PARAM(ecdump); +IBM_PARAM(brightness); +IBM_PARAM(volume); +IBM_PARAM(fan); + +static int __init thinkpad_acpi_module_init(void) +{ + int ret, i; + + /* Driver-level probe */ + ret = probe_for_thinkpad(); + if (ret) + return ret; + + /* Driver initialization */ + ibm_thinkpad_ec_found = check_dmi_for_ec(); + IBM_ACPIHANDLE_INIT(ecrd); + IBM_ACPIHANDLE_INIT(ecwr); + + proc_dir = proc_mkdir(IBM_PROC_DIR, acpi_root_dir); + if (!proc_dir) { + printk(IBM_ERR "unable to create proc dir " IBM_PROC_DIR); + thinkpad_acpi_module_exit(); + return -ENODEV; + } + proc_dir->owner = THIS_MODULE; + + ret = platform_driver_register(&tpacpi_pdriver); + if (ret) { + printk(IBM_ERR "unable to register platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); + if (ret) { + printk(IBM_ERR "unable to create sysfs driver attributes\n"); + thinkpad_acpi_module_exit(); + return ret; + } + + + /* Device initialization */ + tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1, + NULL, 0); + if (IS_ERR(tpacpi_pdev)) { + ret = PTR_ERR(tpacpi_pdev); + tpacpi_pdev = NULL; + printk(IBM_ERR "unable to register platform device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev); + if (IS_ERR(tpacpi_hwmon)) { + ret = PTR_ERR(tpacpi_hwmon); + tpacpi_hwmon = NULL; + printk(IBM_ERR "unable to register hwmon device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { + ret = ibm_init(&ibms_init[i]); + if (ret >= 0 && *ibms_init[i].param) + ret = ibms_init[i].data->write(ibms_init[i].param); + if (ret < 0) { + thinkpad_acpi_module_exit(); + return ret; + } + } + + return 0; +} + +static void thinkpad_acpi_module_exit(void) +{ + struct ibm_struct *ibm, *itmp; + + list_for_each_entry_safe_reverse(ibm, itmp, + &tpacpi_all_drivers, + all_drivers) { + ibm_exit(ibm); + } + + dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); + + if (tpacpi_hwmon) + hwmon_device_unregister(tpacpi_hwmon); + + if (tpacpi_pdev) + platform_device_unregister(tpacpi_pdev); + + tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); + platform_driver_unregister(&tpacpi_pdriver); + + if (proc_dir) + remove_proc_entry(IBM_PROC_DIR, acpi_root_dir); + + kfree(ibm_thinkpad_ec_found); +} + +module_init(thinkpad_acpi_module_init); +module_exit(thinkpad_acpi_module_exit); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h new file mode 100644 index 0000000..440145a --- /dev/null +++ b/drivers/misc/thinkpad_acpi.h @@ -0,0 +1,572 @@ +/* + * thinkpad_acpi.h - ThinkPad ACPI Extras + * + * + * Copyright (C) 2004-2005 Borislav Deianov + * Copyright (C) 2006-2007 Henrique de Moraes Holschuh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __THINKPAD_ACPI_H__ +#define __THINKPAD_ACPI_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +/**************************************************************************** + * Main driver + */ + +#define IBM_NAME "thinkpad" +#define IBM_DESC "ThinkPad ACPI Extras" +#define IBM_FILE "thinkpad_acpi" +#define IBM_URL "http://ibm-acpi.sf.net/" +#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net" + +#define IBM_PROC_DIR "ibm" +#define IBM_ACPI_EVENT_PREFIX "ibm" +#define IBM_DRVR_NAME IBM_FILE + +#define IBM_LOG IBM_FILE ": " +#define IBM_ERR KERN_ERR IBM_LOG +#define IBM_NOTICE KERN_NOTICE IBM_LOG +#define IBM_INFO KERN_INFO IBM_LOG +#define IBM_DEBUG KERN_DEBUG IBM_LOG + +#define IBM_MAX_ACPI_ARGS 3 + +/* ThinkPad CMOS commands */ +#define TP_CMOS_VOLUME_DOWN 0 +#define TP_CMOS_VOLUME_UP 1 +#define TP_CMOS_VOLUME_MUTE 2 +#define TP_CMOS_BRIGHTNESS_UP 4 +#define TP_CMOS_BRIGHTNESS_DOWN 5 + +#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") +#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") +#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) + +/* Debugging */ +#define TPACPI_DBG_ALL 0xffff +#define TPACPI_DBG_ALL 0xffff +#define TPACPI_DBG_INIT 0x0001 +#define TPACPI_DBG_EXIT 0x0002 +#define dbg_printk(a_dbg_level, format, arg...) \ + do { if (dbg_level & a_dbg_level) \ + printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0) +#ifdef CONFIG_THINKPAD_ACPI_DEBUG +#define vdbg_printk(a_dbg_level, format, arg...) \ + dbg_printk(a_dbg_level, format, ## arg) +static const char *str_supported(int is_supported); +#else +#define vdbg_printk(a_dbg_level, format, arg...) +#endif + +/* ACPI HIDs */ +#define IBM_HKEY_HID "IBM0068" +#define IBM_PCI_HID "PNP0A03" + +/* ACPI helpers */ +static int __must_check acpi_evalf(acpi_handle handle, + void *res, char *method, char *fmt, ...); +static int __must_check acpi_ec_read(int i, u8 * p); +static int __must_check acpi_ec_write(int i, u8 v); +static int __must_check _sta(acpi_handle handle); + +/* ACPI handles */ +static acpi_handle root_handle; /* root namespace */ +static acpi_handle ec_handle; /* EC */ +static acpi_handle ecrd_handle, ecwr_handle; /* 570 EC access */ +static acpi_handle cmos_handle, hkey_handle; /* basic thinkpad handles */ + +static void drv_acpi_handle_init(char *name, + acpi_handle *handle, acpi_handle parent, + char **paths, int num_paths, char **path); +#define IBM_ACPIHANDLE_INIT(object) \ + drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \ + object##_paths, ARRAY_SIZE(object##_paths), &object##_path) + +/* ThinkPad ACPI helpers */ +static int issue_thinkpad_cmos_command(int cmos_cmd); + +/* procfs support */ +static struct proc_dir_entry *proc_dir; + +/* procfs helpers */ +static int dispatch_procfs_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int dispatch_procfs_write(struct file *file, + const char __user * userbuf, + unsigned long count, void *data); +static char *next_cmd(char **cmds); + +/* sysfs support */ +struct attribute_set { + unsigned int members, max_members; + struct attribute_group group; +}; + +static struct attribute_set *create_attr_set(unsigned int max_members, + const char* name); +#define destroy_attr_set(_set) \ + kfree(_set); +static int add_to_attr_set(struct attribute_set* s, struct attribute *attr); +static int add_many_to_attr_set(struct attribute_set* s, + struct attribute **attr, + unsigned int count); +#define register_attr_set_with_sysfs(_attr_set, _kobj) \ + sysfs_create_group(_kobj, &_attr_set->group) +static void delete_attr_set(struct attribute_set* s, struct kobject *kobj); + +static int parse_strtoul(const char *buf, unsigned long max, + unsigned long *value); + +/* Device model */ +static struct platform_device *tpacpi_pdev; +static struct class_device *tpacpi_hwmon; +static struct platform_driver tpacpi_pdriver; +static int tpacpi_create_driver_attributes(struct device_driver *drv); +static void tpacpi_remove_driver_attributes(struct device_driver *drv); + +/* Module */ +static int experimental; +static u32 dbg_level; +static int force_load; +static char *ibm_thinkpad_ec_found; + +static char* check_dmi_for_ec(void); +static int thinkpad_acpi_module_init(void); +static void thinkpad_acpi_module_exit(void); + + +/**************************************************************************** + * Subdrivers + */ + +struct ibm_struct; + +struct tp_acpi_drv_struct { + char *hid; + struct acpi_driver *driver; + + void (*notify) (struct ibm_struct *, u32); + acpi_handle *handle; + u32 type; + struct acpi_device *device; +}; + +struct ibm_struct { + char *name; + + int (*read) (char *); + int (*write) (char *); + void (*exit) (void); + + struct list_head all_drivers; + + struct tp_acpi_drv_struct *acpi; + + struct { + u8 acpi_driver_registered:1; + u8 acpi_notify_installed:1; + u8 proc_created:1; + u8 init_called:1; + u8 experimental:1; + } flags; +}; + +struct ibm_init_struct { + char param[32]; + + int (*init) (struct ibm_init_struct *); + struct ibm_struct *data; +}; + +static struct { +#ifdef CONFIG_THINKPAD_ACPI_BAY + u16 bay_status:1; + u16 bay_eject:1; + u16 bay_status2:1; + u16 bay_eject2:1; +#endif + u16 bluetooth:1; + u16 hotkey:1; + u16 hotkey_mask:1; + u16 light:1; + u16 light_status:1; + u16 wan:1; + u16 fan_ctrl_status_undef:1; +} tp_features; + +static struct list_head tpacpi_all_drivers; + +static struct ibm_init_struct ibms_init[]; +static int set_ibm_param(const char *val, struct kernel_param *kp); +static int ibm_init(struct ibm_init_struct *iibm); +static void ibm_exit(struct ibm_struct *ibm); + + +/* + * procfs master subdriver + */ +static int thinkpad_acpi_driver_init(struct ibm_init_struct *iibm); +static int thinkpad_acpi_driver_read(char *p); + + +/* + * Bay subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_BAY +static acpi_handle bay_handle, bay_ej_handle; +static acpi_handle bay2_handle, bay2_ej_handle; + +static int bay_init(struct ibm_init_struct *iibm); +static void bay_notify(struct ibm_struct *ibm, u32 event); +static int bay_read(char *p); +static int bay_write(char *buf); +#endif /* CONFIG_THINKPAD_ACPI_BAY */ + + +/* + * Beep subdriver + */ + +static acpi_handle beep_handle; + +static int beep_read(char *p); +static int beep_write(char *buf); + + +/* + * Bluetooth subdriver + */ + +#define TPACPI_BLUETH_SYSFS_GROUP "bluetooth" + +enum { + /* ACPI GBDC/SBDC bits */ + TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ + TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ + TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ +}; + +static int bluetooth_init(struct ibm_init_struct *iibm); +static int bluetooth_get_radiosw(void); +static int bluetooth_set_radiosw(int radio_on); +static int bluetooth_read(char *p); +static int bluetooth_write(char *buf); + + +/* + * Brightness (backlight) subdriver + */ + +#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" + +static struct backlight_device *ibm_backlight_device; +static int brightness_offset = 0x31; + +static int brightness_init(struct ibm_init_struct *iibm); +static void brightness_exit(void); +static int brightness_get(struct backlight_device *bd); +static int brightness_set(int value); +static int brightness_update_status(struct backlight_device *bd); +static int brightness_read(char *p); +static int brightness_write(char *buf); + + +/* + * CMOS subdriver + */ + +static int cmos_read(char *p); +static int cmos_write(char *buf); + + +/* + * Dock subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_DOCK +static acpi_handle pci_handle; +static acpi_handle dock_handle; + +static void dock_notify(struct ibm_struct *ibm, u32 event); +static int dock_read(char *p); +static int dock_write(char *buf); +#endif /* CONFIG_THINKPAD_ACPI_DOCK */ + + +/* + * EC dump subdriver + */ + +static int ecdump_read(char *p) ; +static int ecdump_write(char *buf); + + +/* + * Fan subdriver + */ + +enum { /* Fan control constants */ + fan_status_offset = 0x2f, /* EC register 0x2f */ + fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) + * 0x84 must be read before 0x85 */ + + TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ + TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ + + TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */ +}; + +enum fan_status_access_mode { + TPACPI_FAN_NONE = 0, /* No fan status or control */ + TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ +}; + +enum fan_control_access_mode { + TPACPI_FAN_WR_NONE = 0, /* No fan control */ + TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ + TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ +}; + +enum fan_control_commands { + TPACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ + TPACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ + TPACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, + * and also watchdog cmd */ +}; + +static int fan_control_allowed; + +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; +static u8 fan_control_initial_status; +static u8 fan_control_desired_level; +static int fan_watchdog_maxinterval; + +static struct mutex fan_mutex; + +static acpi_handle fans_handle, gfan_handle, sfan_handle; + +static int fan_init(struct ibm_init_struct *iibm); +static void fan_exit(void); +static int fan_get_status(u8 *status); +static int fan_get_status_safe(u8 *status); +static int fan_get_speed(unsigned int *speed); +static void fan_update_desired_level(u8 status); +static void fan_watchdog_fire(struct work_struct *ignored); +static void fan_watchdog_reset(void); +static int fan_set_level(int level); +static int fan_set_level_safe(int level); +static int fan_set_enable(void); +static int fan_set_disable(void); +static int fan_set_speed(int speed); +static int fan_read(char *p); +static int fan_write(char *buf); +static int fan_write_cmd_level(const char *cmd, int *rc); +static int fan_write_cmd_enable(const char *cmd, int *rc); +static int fan_write_cmd_disable(const char *cmd, int *rc); +static int fan_write_cmd_speed(const char *cmd, int *rc); +static int fan_write_cmd_watchdog(const char *cmd, int *rc); + + +/* + * Hotkey subdriver + */ + +#define TPACPI_HOTKEY_SYSFS_GROUP "hotkey" + +static int hotkey_orig_status; +static int hotkey_orig_mask; + +static struct mutex hotkey_mutex; + +static int hotkey_init(struct ibm_init_struct *iibm); +static void hotkey_exit(void); +static int hotkey_get(int *status, int *mask); +static int hotkey_set(int status, int mask); +static void hotkey_notify(struct ibm_struct *ibm, u32 event); +static int hotkey_read(char *p); +static int hotkey_write(char *buf); + + +/* + * LED subdriver + */ + +enum led_access_mode { + TPACPI_LED_NONE = 0, + TPACPI_LED_570, /* 570 */ + TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + TPACPI_LED_NEW, /* all others */ +}; + +enum { /* For TPACPI_LED_OLD */ + TPACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */ + TPACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */ + TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ +}; + +static enum led_access_mode led_supported; +static acpi_handle led_handle; + +static int led_init(struct ibm_init_struct *iibm); +static int led_read(char *p); +static int led_write(char *buf); + +/* + * Light (thinklight) subdriver + */ + +static acpi_handle lght_handle, ledb_handle; + +static int light_init(struct ibm_init_struct *iibm); +static int light_read(char *p); +static int light_write(char *buf); + + +/* + * Thermal subdriver + */ + +enum thermal_access_mode { + TPACPI_THERMAL_NONE = 0, /* No thermal support */ + TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ + TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ + TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ + TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ +}; + +enum { /* TPACPI_THERMAL_TPEC_* */ + TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ + TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ + TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ +}; + +#define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ +struct ibm_thermal_sensors_struct { + s32 temp[TPACPI_MAX_THERMAL_SENSORS]; +}; + +static enum thermal_access_mode thermal_read_mode; + +static int thermal_init(struct ibm_init_struct *iibm); +static int thermal_get_sensor(int idx, s32 *value); +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s); +static int thermal_read(char *p); + + +/* + * Video subdriver + */ + +enum video_access_mode { + TPACPI_VIDEO_NONE = 0, + TPACPI_VIDEO_570, /* 570 */ + TPACPI_VIDEO_770, /* 600e/x, 770e, 770x */ + TPACPI_VIDEO_NEW, /* all others */ +}; + +enum { /* video status flags, based on VIDEO_570 */ + TP_ACPI_VIDEO_S_LCD = 0x01, /* LCD output enabled */ + TP_ACPI_VIDEO_S_CRT = 0x02, /* CRT output enabled */ + TP_ACPI_VIDEO_S_DVI = 0x08, /* DVI output enabled */ +}; + +enum { /* TPACPI_VIDEO_570 constants */ + TP_ACPI_VIDEO_570_PHSCMD = 0x87, /* unknown magic constant :( */ + TP_ACPI_VIDEO_570_PHSMASK = 0x03, /* PHS bits that map to + * video_status_flags */ + TP_ACPI_VIDEO_570_PHS2CMD = 0x8b, /* unknown magic constant :( */ + TP_ACPI_VIDEO_570_PHS2SET = 0x80, /* unknown magic constant :( */ +}; + +static enum video_access_mode video_supported; +static int video_orig_autosw; +static acpi_handle vid_handle, vid2_handle; + +static int video_init(struct ibm_init_struct *iibm); +static void video_exit(void); +static int video_outputsw_get(void); +static int video_outputsw_set(int status); +static int video_autosw_get(void); +static int video_autosw_set(int enable); +static int video_outputsw_cycle(void); +static int video_expand_toggle(void); +static int video_read(char *p); +static int video_write(char *buf); + + +/* + * Volume subdriver + */ + +static int volume_offset = 0x30; + +static int volume_read(char *p); +static int volume_write(char *buf); + + +/* + * Wan subdriver + */ + +#define TPACPI_WAN_SYSFS_GROUP "wwan" + +enum { + /* ACPI GWAN/SWAN bits */ + TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ + TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ + TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ +}; + +static int wan_init(struct ibm_init_struct *iibm); +static int wan_get_radiosw(void); +static int wan_set_radiosw(int radio_on); +static int wan_read(char *p); +static int wan_write(char *buf); + + +#endif /* __THINKPAD_ACPI_H */ diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index d32698b..e0e82d8 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -86,31 +86,26 @@ mmc_bus_uevent(struct device *dev, char { struct mmc_card *card = dev_to_mmc_card(dev); char ccc[13]; - int i = 0; - -#define add_env(fmt,val) \ - ({ \ - int len, ret = -ENOMEM; \ - if (i < num_envp) { \ - envp[i++] = buf; \ - len = snprintf(buf, buf_size, fmt, val) + 1; \ - buf_size -= len; \ - buf += len; \ - if (buf_size >= 0) \ - ret = 0; \ - } \ - ret; \ - }) + int retval = 0, i = 0, length = 0; + +#define add_env(fmt,val) do { \ + retval = add_uevent_var(envp, num_envp, &i, \ + buf, buf_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ +} while (0); for (i = 0; i < 12; i++) ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; ccc[12] = '\0'; - i = 0; add_env("MMC_CCC=%s", ccc); add_env("MMC_MANFID=%06x", card->cid.manfid); add_env("MMC_NAME=%s", mmc_card_name(card)); add_env("MMC_OEMID=%04x", card->cid.oemid); +#undef add_env + envp[i] = NULL; return 0; } diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 26f75c2..c1b47db 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,8 +1,6 @@ # $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $ -menu "Memory Technology Devices (MTD)" - -config MTD +menuconfig MTD tristate "Memory Technology Device (MTD) support" help Memory Technology Devices are flash, RAM and similar chips, often @@ -13,9 +11,10 @@ config MTD them. It will also allow you to select individual drivers for particular hardware and users of MTD devices. If unsure, say N. +if MTD + config MTD_DEBUG bool "Debugging" - depends on MTD help This turns on low-level debugging for the entire MTD sub-system. Normally, you should say 'N'. @@ -29,7 +28,6 @@ config MTD_DEBUG_VERBOSE config MTD_CONCAT tristate "MTD concatenating support" - depends on MTD help Support for concatenating several MTD devices into a single (virtual) one. This allows you to have -for example- a JFFS(2) @@ -38,7 +36,6 @@ config MTD_CONCAT config MTD_PARTITIONS bool "MTD partitioning support" - depends on MTD help If you have a device which needs to divide its flash chip(s) up into multiple 'partitions', each of which appears to the user as @@ -153,11 +150,9 @@ config MTD_AFS_PARTS 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. comment "User Modules And Translation Layers" - depends on MTD config MTD_CHAR tristate "Direct char device access to MTD devices" - depends on MTD help This provides a character device for each MTD device present in the system, allowing the user to read and write directly to the @@ -166,12 +161,12 @@ config MTD_CHAR config MTD_BLKDEVS tristate "Common interface to block layer for MTD 'translation layers'" - depends on MTD && BLOCK + depends on BLOCK default n config MTD_BLOCK tristate "Caching block device access to MTD devices" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- Although most flash chips have an erase size too large to be useful @@ -194,7 +189,7 @@ config MTD_BLOCK config MTD_BLOCK_RO tristate "Readonly block device access to MTD devices" - depends on MTD_BLOCK!=y && MTD && BLOCK + depends on MTD_BLOCK!=y && BLOCK select MTD_BLKDEVS help This allows you to mount read-only file systems (such as cramfs) @@ -206,7 +201,7 @@ config MTD_BLOCK_RO config FTL tristate "FTL (Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the original Flash Translation Layer which @@ -223,7 +218,7 @@ config FTL config NFTL tristate "NFTL (NAND Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the NAND Flash Translation Layer which is @@ -247,7 +242,7 @@ config NFTL_RW config INFTL tristate "INFTL (Inverse NAND Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the Inverse NAND Flash Translation @@ -265,7 +260,7 @@ config INFTL config RFD_FTL tristate "Resident Flash Disk (Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the flash translation layer known @@ -276,7 +271,7 @@ config RFD_FTL config SSFDC tristate "NAND SSFDC (SmartMedia) read only translation layer" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS help This enables read only access to SmartMedia formatted NAND @@ -292,5 +287,6 @@ source "drivers/mtd/nand/Kconfig" source "drivers/mtd/onenand/Kconfig" -endmenu +source "drivers/mtd/ubi/Kconfig" +endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index c130e62..9205540 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -28,3 +28,5 @@ nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ maps/ devices/ nand/ onenand/ + +obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 72e6d73..d28e0fc 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -6,7 +6,6 @@ menu "RAM/ROM/Flash chip drivers" config MTD_CFI tristate "Detect flash chips by Common Flash Interface (CFI) probe" - depends on MTD select MTD_GEN_PROBE help The Common Flash Interface specification was developed by Intel, @@ -18,7 +17,6 @@ config MTD_CFI config MTD_JEDECPROBE tristate "Detect non-CFI AMD/JEDEC-compatible flash chips" - depends on MTD select MTD_GEN_PROBE help This option enables JEDEC-style probing of flash chips which are not @@ -213,21 +211,18 @@ config MTD_CFI_UTIL config MTD_RAM tristate "Support for RAM chips in bus mapping" - depends on MTD help This option enables basic support for RAM chips accessed through a bus mapping driver. config MTD_ROM tristate "Support for ROM chips in bus mapping" - depends on MTD help This option enables basic support for ROM chips accessed through a bus mapping driver. config MTD_ABSENT tristate "Support for absent chips in bus mapping" - depends on MTD help This option enables support for a dummy probing driver used to allocated placeholder MTD devices on systems that have socketed @@ -237,7 +232,6 @@ config MTD_ABSENT with this driver will return -ENODEV upon access. config MTD_OBSOLETE_CHIPS - depends on MTD bool "Older (theoretically obsoleted now) drivers for non-CFI chips" help This option does not enable any code directly, but will allow you to @@ -250,7 +244,7 @@ config MTD_OBSOLETE_CHIPS config MTD_AMDSTD tristate "AMD compatible flash chip support (non-CFI)" - depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN + depends on MTD_OBSOLETE_CHIPS && BROKEN help This option enables support for flash chips using AMD-compatible commands, including some which are not CFI-compatible and hence @@ -260,7 +254,7 @@ config MTD_AMDSTD config MTD_SHARP tristate "pre-CFI Sharp chip support" - depends on MTD && MTD_OBSOLETE_CHIPS + depends on MTD_OBSOLETE_CHIPS help This option enables support for flash chips using Sharp-compatible commands, including some which are not CFI-compatible and hence @@ -268,7 +262,7 @@ config MTD_SHARP config MTD_JEDEC tristate "JEDEC device support" - depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN + depends on MTD_OBSOLETE_CHIPS && BROKEN help Enable older JEDEC flash interface devices for self programming flash. It is commonly used in older AMD chips. It is diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f334959..2f19fa7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -15,6 +15,8 @@ * - optimized write buffer method * 02/05/2002 Christopher Hoover / * - reworked lock/unlock/erase support for var size flash + * 21/03/2007 Rodolfo Giometti + * - auto unlock sectors on resume for auto locking flash on power up */ #include @@ -30,6 +32,7 @@ #include #include #include #include +#include #include #include #include @@ -220,6 +223,15 @@ static void fixup_use_write_buffers(stru } } +/* + * Some chips power-up with all sectors locked by default. + */ +static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param) +{ + printk(KERN_INFO "Using auto-unlock on power-up/resume\n" ); + mtd->flags |= MTD_STUPID_LOCK; +} + static struct cfi_fixup cfi_fixup_table[] = { #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, @@ -232,6 +244,7 @@ #if !FORCE_WORD_WRITE #endif { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, + { MANUFACTURER_INTEL, 0x891c, fixup_use_powerup_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -460,6 +473,7 @@ static struct mtd_info *cfi_intelext_set mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL); } offset += (ersize * ernum); } @@ -1825,8 +1839,7 @@ static void cfi_intelext_sync (struct mt } } -#ifdef DEBUG_LOCK_BITS -static int __xipram do_printlockstatus_oneblock(struct map_info *map, +static int __xipram do_getlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) @@ -1840,8 +1853,17 @@ static int __xipram do_printlockstatus_o chip->state = FL_JEDEC_QUERY; status = cfi_read_query(map, adr+(2*ofs_factor)); xip_enable(map, chip, 0); + return status; +} + +#ifdef DEBUG_LOCK_BITS +static int __xipram do_printlockstatus_oneblock(struct map_info *map, + struct flchip *chip, + unsigned long adr, + int len, void *thunk) +{ printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", - adr, status); + adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk)); return 0; } #endif @@ -2216,14 +2238,45 @@ static int cfi_intelext_get_user_prot_in #endif +static void cfi_intelext_save_locks(struct mtd_info *mtd) +{ + struct mtd_erase_region_info *region; + int block, status, i; + unsigned long adr; + size_t len; + + for (i = 0; i < mtd->numeraseregions; i++) { + region = &mtd->eraseregions[i]; + if (!region->lockmap) + continue; + + for (block = 0; block < region->numblocks; block++){ + len = region->erasesize; + adr = region->offset + block * len; + + status = cfi_varsize_frob(mtd, + do_getlockstatus_oneblock, adr, len, 0); + if (status) + set_bit(block, region->lockmap); + else + clear_bit(block, region->lockmap); + } + } +} + static int cfi_intelext_suspend(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; int i; struct flchip *chip; int ret = 0; + if ((mtd->flags & MTD_STUPID_LOCK) + && extp && (extp->FeatureSupport & (1 << 5))) + cfi_intelext_save_locks(mtd); + for (i=0; !ret && inumchips; i++) { chip = &cfi->chips[i]; @@ -2285,10 +2338,33 @@ static int cfi_intelext_suspend(struct m return ret; } +static void cfi_intelext_restore_locks(struct mtd_info *mtd) +{ + struct mtd_erase_region_info *region; + int block, i; + unsigned long adr; + size_t len; + + for (i = 0; i < mtd->numeraseregions; i++) { + region = &mtd->eraseregions[i]; + if (!region->lockmap) + continue; + + for (block = 0; block < region->numblocks; block++) { + len = region->erasesize; + adr = region->offset + block * len; + + if (!test_bit(block, region->lockmap)) + cfi_intelext_unlock(mtd, adr, len); + } + } +} + static void cfi_intelext_resume(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; int i; struct flchip *chip; @@ -2307,6 +2383,10 @@ static void cfi_intelext_resume(struct m spin_unlock(chip->mutex); } + + if ((mtd->flags & MTD_STUPID_LOCK) + && extp && (extp->FeatureSupport & (1 << 5))) + cfi_intelext_restore_locks(mtd); } static int cfi_intelext_reset(struct mtd_info *mtd) @@ -2347,12 +2427,19 @@ static void cfi_intelext_destroy(struct { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + struct mtd_erase_region_info *region; + int i; cfi_intelext_reset(mtd); unregister_reboot_notifier(&mtd->reboot_notifier); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); kfree(cfi->chips[0].priv); kfree(cfi); + for (i = 0; i < mtd->numeraseregions; i++) { + region = &mtd->eraseregions[i]; + if (region->lockmap) + kfree(region->lockmap); + } kfree(mtd->eraseregions); } diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index 77303ce..ab44f2b 100644 --- a/drivers/mtd/chips/fwh_lock.h +++ b/drivers/mtd/chips/fwh_lock.h @@ -65,11 +65,12 @@ static int fwh_xxlock_oneblock(struct ma return ret; } + chip->oldstate = chip->state; chip->state = xxlt->state; map_write(map, CMD(xxlt->val), adr); /* Done and happy. */ - chip->state = FL_READY; + chip->state = chip->oldstate; put_chip(map, chip, adr); spin_unlock(chip->mutex); return 0; diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 440f685..690c942 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -6,7 +6,7 @@ menu "Self-contained MTD device drivers" config MTD_PMC551 tristate "Ramix PMC551 PCI Mezzanine RAM card support" - depends on MTD && PCI + depends on PCI ---help--- This provides a MTD device driver for the Ramix PMC551 RAM PCI card from Ramix Inc. . @@ -40,7 +40,7 @@ config MTD_PMC551_DEBUG config MTD_MS02NV tristate "DEC MS02-NV NVRAM module support" - depends on MTD && MACH_DECSTATION + depends on MACH_DECSTATION help This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery backed-up NVRAM module. The module was originally meant as an NFS @@ -54,15 +54,23 @@ config MTD_MS02NV config MTD_DATAFLASH tristate "Support for AT45xxx DataFlash" - depends on MTD && SPI_MASTER && EXPERIMENTAL + depends on SPI_MASTER && EXPERIMENTAL help This enables access to AT45xxx DataFlash chips, using SPI. Sometimes DataFlash chips are packaged inside MMC-format cards; at this writing, the MMC stack won't handle those. +config MTD_DATAFLASH26 + tristate "AT91RM9200 DataFlash AT26xxx" + depends on MTD && ARCH_AT91RM9200 && AT91_SPI + help + This enables access to the DataFlash chip (AT26xxx) on an + AT91RM9200-based board. + If you have such a board and such a DataFlash, say 'Y'. + config MTD_M25P80 tristate "Support for M25 SPI Flash" - depends on MTD && SPI_MASTER && EXPERIMENTAL + depends on SPI_MASTER && EXPERIMENTAL help This enables access to ST M25P80 and similar SPI flash chips, used for program and data storage. Set up your spi devices @@ -70,7 +78,6 @@ config MTD_M25P80 config MTD_SLRAM tristate "Uncached system RAM" - depends on MTD help If your CPU cannot cache all of the physical memory in your machine, you can still use it for storage or swap by using this driver to @@ -78,7 +85,6 @@ config MTD_SLRAM config MTD_PHRAM tristate "Physical system RAM" - depends on MTD help This is a re-implementation of the slram driver above. @@ -88,7 +94,7 @@ config MTD_PHRAM config MTD_LART tristate "28F160xx flash driver for LART" - depends on SA1100_LART && MTD + depends on SA1100_LART help This enables the flash driver for LART. Please note that you do not need any mapping/chip driver for LART. This one does it all @@ -96,7 +102,6 @@ config MTD_LART config MTD_MTDRAM tristate "Test driver using RAM" - depends on MTD help This enables a test MTD device driver which uses vmalloc() to provide storage. You probably want to say 'N' unless you're @@ -136,7 +141,7 @@ config MTDRAM_ABS_POS config MTD_BLOCK2MTD tristate "MTD using block device" - depends on MTD && BLOCK + depends on BLOCK help This driver allows a block device to appear as an MTD. It would generally be used in the following cases: @@ -150,7 +155,6 @@ comment "Disk-On-Chip Device Drivers" config MTD_DOC2000 tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)" - depends on MTD select MTD_DOCPROBE select MTD_NAND_IDS ---help--- @@ -173,7 +177,6 @@ config MTD_DOC2000 config MTD_DOC2001 tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)" - depends on MTD select MTD_DOCPROBE select MTD_NAND_IDS ---help--- @@ -195,7 +198,6 @@ config MTD_DOC2001 config MTD_DOC2001PLUS tristate "M-Systems Disk-On-Chip Millennium Plus" - depends on MTD select MTD_DOCPROBE select MTD_NAND_IDS ---help--- diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0f788d5..8ab568b 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,4 +16,5 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o +obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c new file mode 100644 index 0000000..64ce37f --- /dev/null +++ b/drivers/mtd/devices/at91_dataflash26.c @@ -0,0 +1,485 @@ +/* + * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) + * This is a largely modified version of at91_dataflash.c that + * supports AT26xxx dataflash chips. The original driver supports + * AT45xxx chips. + * + * Note: This driver was only tested with an AT26F004. It should be + * easy to make it work with other AT26xxx dataflash devices, though. + * + * Copyright (C) 2007 Hans J. Koch + * original Copyright (C) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. +*/ + +#include +#include +#include +#include + +#include + +#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ + +#define MANUFACTURER_ID_ATMEL 0x1F + +/* command codes */ + +#define AT26_OP_READ_STATUS 0x05 +#define AT26_OP_READ_DEV_ID 0x9F +#define AT26_OP_ERASE_PAGE_4K 0x20 +#define AT26_OP_READ_ARRAY_FAST 0x0B +#define AT26_OP_SEQUENTIAL_WRITE 0xAF +#define AT26_OP_WRITE_ENABLE 0x06 +#define AT26_OP_WRITE_DISABLE 0x04 +#define AT26_OP_SECTOR_PROTECT 0x36 +#define AT26_OP_SECTOR_UNPROTECT 0x39 + +/* status register bits */ + +#define AT26_STATUS_BUSY 0x01 +#define AT26_STATUS_WRITE_ENABLE 0x02 + +struct dataflash_local +{ + int spi; /* SPI chip-select number */ + unsigned int page_size; /* number of bytes per page */ +}; + + +/* Detected DataFlash devices */ +static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; +static int nr_devices = 0; + +/* Allocate a single SPI transfer descriptor. We're assuming that if multiple + SPI transfers occur at the same time, spi_access_bus() will serialize them. + If this is not valid, then either (i) each dataflash 'priv' structure + needs it's own transfer descriptor, (ii) we lock this one, or (iii) use + another mechanism. */ +static struct spi_transfer_list* spi_transfer_desc; + +/* + * Perform a SPI transfer to access the DataFlash device. + */ +static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, + char* txnext, int txnext_len, char* rxnext, int rxnext_len) +{ + struct spi_transfer_list* list = spi_transfer_desc; + + list->tx[0] = tx; list->txlen[0] = tx_len; + list->rx[0] = rx; list->rxlen[0] = rx_len; + + list->tx[1] = txnext; list->txlen[1] = txnext_len; + list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; + + list->nr_transfers = nr; + /* Note: spi_transfer() always returns 0, there are no error checks */ + return spi_transfer(list); +} + +/* + * Return the status of the DataFlash device. + */ +static unsigned char at91_dataflash26_status(void) +{ + unsigned char command[2]; + + command[0] = AT26_OP_READ_STATUS; + command[1] = 0; + + do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); + + return command[1]; +} + +/* + * Poll the DataFlash device until it is READY. + */ +static unsigned char at91_dataflash26_waitready(void) +{ + unsigned char status; + + while (1) { + status = at91_dataflash26_status(); + if (!(status & AT26_STATUS_BUSY)) + return status; + } +} + +/* + * Enable/disable write access + */ + static void at91_dataflash26_write_enable(int enable) +{ + unsigned char cmd[2]; + + DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable); + + if (enable) + cmd[0] = AT26_OP_WRITE_ENABLE; + else + cmd[0] = AT26_OP_WRITE_DISABLE; + cmd[1] = 0; + + do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); +} + +/* + * Protect/unprotect sector + */ + static void at91_dataflash26_sector_protect(loff_t addr, int protect) +{ + unsigned char cmd[4]; + + DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n", + addr, protect); + + if (protect) + cmd[0] = AT26_OP_SECTOR_PROTECT; + else + cmd[0] = AT26_OP_SECTOR_UNPROTECT; + cmd[1] = (addr & 0x00FF0000) >> 16; + cmd[2] = (addr & 0x0000FF00) >> 8; + cmd[3] = (addr & 0x000000FF); + + do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); +} + +/* + * Erase blocks of flash. + */ +static int at91_dataflash26_erase(struct mtd_info *mtd, + struct erase_info *instr) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned char cmd[4]; + + DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n", + instr->addr, instr->len); + + /* Sanity checks */ + if (priv->page_size != 4096) + return -EINVAL; /* Can't handle other sizes at the moment */ + + if ( ((instr->len % mtd->erasesize) != 0) + || ((instr->len % priv->page_size) != 0) + || ((instr->addr % priv->page_size) != 0) + || ((instr->addr + instr->len) > mtd->size)) + return -EINVAL; + + spi_access_bus(priv->spi); + + while (instr->len > 0) { + at91_dataflash26_write_enable(1); + at91_dataflash26_sector_protect(instr->addr, 0); + at91_dataflash26_write_enable(1); + cmd[0] = AT26_OP_ERASE_PAGE_4K; + cmd[1] = (instr->addr & 0x00FF0000) >> 16; + cmd[2] = (instr->addr & 0x0000FF00) >> 8; + cmd[3] = (instr->addr & 0x000000FF); + + DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x" + "0x%02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); + at91_dataflash26_waitready(); + + instr->addr += priv->page_size; /* next page */ + instr->len -= priv->page_size; + } + + at91_dataflash26_write_enable(0); + spi_release_bus(priv->spi); + + /* Inform MTD subsystem that erase is complete */ + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + +/* + * Read from the DataFlash device. + * from : Start offset in flash device + * len : Number of bytes to read + * retlen : Number of bytes actually read + * buf : Buffer that will receive data + */ +static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned char cmd[5]; + + DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n", + from, from+len); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (from + len > mtd->size) + return -EINVAL; + + cmd[0] = AT26_OP_READ_ARRAY_FAST; + cmd[1] = (from & 0x00FF0000) >> 16; + cmd[2] = (from & 0x0000FF00) >> 8; + cmd[3] = (from & 0x000000FF); + /* cmd[4] is a "Don't care" byte */ + + DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + spi_access_bus(priv->spi); + do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len); + spi_release_bus(priv->spi); + + *retlen = len; + return 0; +} + +/* + * Write to the DataFlash device. + * to : Start offset in flash device + * len : Number of bytes to write + * retlen : Number of bytes actually written + * buf : Buffer containing the data + */ +static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned int addr, buf_index = 0; + int ret = -EIO, sector, last_sector; + unsigned char status, cmd[5]; + + DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (to + len > mtd->size) + return -EINVAL; + + spi_access_bus(priv->spi); + + addr = to; + last_sector = -1; + + while (buf_index < len) { + sector = addr / priv->page_size; + /* Write first byte if a new sector begins */ + if (sector != last_sector) { + at91_dataflash26_write_enable(1); + at91_dataflash26_sector_protect(addr, 0); + at91_dataflash26_write_enable(1); + + /* Program first byte of a new sector */ + cmd[0] = AT26_OP_SEQUENTIAL_WRITE; + cmd[1] = (addr & 0x00FF0000) >> 16; + cmd[2] = (addr & 0x0000FF00) >> 8; + cmd[3] = (addr & 0x000000FF); + cmd[4] = buf[buf_index++]; + do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); + status = at91_dataflash26_waitready(); + addr++; + /* On write errors, the chip resets the write enable + flag. This also happens after the last byte of a + sector is successfully programmed. */ + if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) + && ((addr % priv->page_size) != 0) ) { + DEBUG(MTD_DEBUG_LEVEL1, + "write error1: addr=0x%06x, " + "status=0x%02x\n", addr, status); + goto write_err; + } + (*retlen)++; + last_sector = sector; + } + + /* Write subsequent bytes in the same sector */ + cmd[0] = AT26_OP_SEQUENTIAL_WRITE; + cmd[1] = buf[buf_index++]; + do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); + status = at91_dataflash26_waitready(); + addr++; + + if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) + && ((addr % priv->page_size) != 0) ) { + DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, " + "status=0x%02x\n", addr, status); + goto write_err; + } + + (*retlen)++; + } + + ret = 0; + at91_dataflash26_write_enable(0); +write_err: + spi_release_bus(priv->spi); + return ret; +} + +/* + * Initialize and register DataFlash device with MTD subsystem. + */ +static int __init add_dataflash(int channel, char *name, int nr_pages, + int pagesize) +{ + struct mtd_info *device; + struct dataflash_local *priv; + + if (nr_devices >= DATAFLASH_MAX_DEVICES) { + printk(KERN_ERR "at91_dataflash26: Too many devices " + "detected\n"); + return 0; + } + + device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8, + GFP_KERNEL); + if (!device) + return -ENOMEM; + + device->name = (char *)&device[1]; + sprintf(device->name, "%s.spi%d", name, channel); + device->size = nr_pages * pagesize; + device->erasesize = pagesize; + device->owner = THIS_MODULE; + device->type = MTD_DATAFLASH; + device->flags = MTD_CAP_NORFLASH; + device->erase = at91_dataflash26_erase; + device->read = at91_dataflash26_read; + device->write = at91_dataflash26_write; + + priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local), + GFP_KERNEL); + if (!priv) { + kfree(device); + return -ENOMEM; + } + + priv->spi = channel; + priv->page_size = pagesize; + device->priv = priv; + + mtd_devices[nr_devices] = device; + nr_devices++; + printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n", + name, channel, device->size); + + return add_mtd_device(device); +} + +/* + * Detect and initialize DataFlash device connected to specified SPI channel. + * + */ + +struct dataflash26_types { + unsigned char id0; + unsigned char id1; + char *name; + int pagesize; + int nr_pages; +}; + +struct dataflash26_types df26_types[] = { + { + .id0 = 0x04, + .id1 = 0x00, + .name = "AT26F004", + .pagesize = 4096, + .nr_pages = 128, + }, + { + .id0 = 0x45, + .id1 = 0x01, + .name = "AT26DF081A", /* Not tested ! */ + .pagesize = 4096, + .nr_pages = 256, + }, +}; + +static int __init at91_dataflash26_detect(int channel) +{ + unsigned char status, cmd[5]; + int i; + + spi_access_bus(channel); + status = at91_dataflash26_status(); + + if (status == 0 || status == 0xff) { + printk(KERN_ERR "at91_dataflash26_detect: status error %d\n", + status); + spi_release_bus(channel); + return -ENODEV; + } + + cmd[0] = AT26_OP_READ_DEV_ID; + do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); + spi_release_bus(channel); + + if (cmd[1] != MANUFACTURER_ID_ATMEL) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(df26_types); i++) { + if ( cmd[2] == df26_types[i].id0 + && cmd[3] == df26_types[i].id1) + return add_dataflash(channel, + df26_types[i].name, + df26_types[i].nr_pages, + df26_types[i].pagesize); + } + + printk(KERN_ERR "at91_dataflash26_detect: Unsupported device " + "(0x%02x/0x%02x)\n", cmd[2], cmd[3]); + return -ENODEV; +} + +static int __init at91_dataflash26_init(void) +{ + spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), + GFP_KERNEL); + if (!spi_transfer_desc) + return -ENOMEM; + + /* DataFlash (SPI chip select 0) */ + at91_dataflash26_detect(0); + +#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD + /* DataFlash card (SPI chip select 3) */ + at91_dataflash26_detect(3); +#endif + return 0; +} + +static void __exit at91_dataflash26_exit(void) +{ + int i; + + for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { + if (mtd_devices[i]) { + del_mtd_device(mtd_devices[i]); + kfree(mtd_devices[i]->priv); + kfree(mtd_devices[i]); + } + } + nr_devices = 0; + kfree(spi_transfer_desc); +} + +module_init(at91_dataflash26_init); +module_exit(at91_dataflash26_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hans J. Koch"); +MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200"); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index f9f2ce7..ce47544 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -40,56 +40,9 @@ struct block2mtd_dev { static LIST_HEAD(blkmtd_device_list); -#define PAGE_READAHEAD 64 -static void cache_readahead(struct address_space *mapping, int index) +static struct page* page_read(struct address_space *mapping, int index) { filler_t *filler = (filler_t*)mapping->a_ops->readpage; - int i, pagei; - unsigned ret = 0; - unsigned long end_index; - struct page *page; - LIST_HEAD(page_pool); - struct inode *inode = mapping->host; - loff_t isize = i_size_read(inode); - - if (!isize) { - INFO("iSize=0 in cache_readahead\n"); - return; - } - - end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); - - read_lock_irq(&mapping->tree_lock); - for (i = 0; i < PAGE_READAHEAD; i++) { - pagei = index + i; - if (pagei > end_index) { - INFO("Overrun end of disk in cache readahead\n"); - break; - } - page = radix_tree_lookup(&mapping->page_tree, pagei); - if (page && (!i)) - break; - if (page) - continue; - read_unlock_irq(&mapping->tree_lock); - page = page_cache_alloc_cold(mapping); - read_lock_irq(&mapping->tree_lock); - if (!page) - break; - page->index = pagei; - list_add(&page->lru, &page_pool); - ret++; - } - read_unlock_irq(&mapping->tree_lock); - if (ret) - read_cache_pages(mapping, &page_pool, filler, NULL); -} - - -static struct page* page_readahead(struct address_space *mapping, int index) -{ - filler_t *filler = (filler_t*)mapping->a_ops->readpage; - cache_readahead(mapping, index); return read_cache_page(mapping, index, filler, NULL); } @@ -105,14 +58,14 @@ static int _block2mtd_erase(struct block u_long *max; while (pages) { - page = page_readahead(mapping, index); + page = page_read(mapping, index); if (!page) return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); - max = (u_long*)page_address(page) + PAGE_SIZE; - for (p=(u_long*)page_address(page); pblkdev->bd_inode->i_mapping, index); + page = page_read(dev->blkdev->bd_inode->i_mapping, index); if (!page) return -ENOMEM; if (IS_ERR(page)) @@ -213,8 +165,7 @@ static int _block2mtd_write(struct block cpylen = len; // this page len = len - cpylen; - // Get page - page = page_readahead(mapping, index); + page = page_read(mapping, index); if (!page) return -ENOMEM; if (IS_ERR(page)) @@ -308,9 +259,9 @@ #ifndef MODULE /* We might not have rootfs mounted at this point. Try to resolve the device name by other means. */ - dev_t dev = name_to_dev_t(devname); - if (dev != 0) { - bdev = open_by_devnum(dev, FMODE_WRITE | FMODE_READ); + dev_t devt = name_to_dev_t(devname); + if (devt) { + bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); } } #endif diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index bbf0553..d990d81 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -6,7 +6,6 @@ menu "Mapping drivers for chip access" config MTD_COMPLEX_MAPPINGS bool "Support non-linear mappings of flash chips" - depends on MTD help This causes the chip drivers to allow for complicated paged mappings of flash chips. @@ -69,6 +68,39 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree. +config MTD_PMC_MSP_EVM + tristate "CFI Flash device mapped on PMC-Sierra MSP" + depends on PMC_MSP && MTD_CFI + select MTD_PARTITIONS + help + This provides a 'mapping' driver which support the way + in which user-programmable flash chips are connected on the + PMC-Sierra MSP eval/demo boards + +choice + prompt "Maximum mappable memory avialable for flash IO" + depends on MTD_PMC_MSP_EVM + default MSP_FLASH_MAP_LIMIT_32M + +config MSP_FLASH_MAP_LIMIT_32M + bool "32M" + +endchoice + +config MSP_FLASH_MAP_LIMIT + hex + default "0x02000000" + depends on MSP_FLASH_MAP_LIMIT_32M + +config MTD_PMC_MSP_RAMROOT + tristate "Embedded RAM block device for root on PMC-Sierra MSP" + depends on PMC_MSP_EMBEDDED_ROOTFS && \ + (MTD_BLOCK || MTD_BLOCK_RO) && \ + MTD_RAM + help + This provides support for the embedded root file system + on PMC MSP devices. This memory is mapped as a MTD block device. + config MTD_SUN_UFLASH tristate "Sun Microsystems userflash support" depends on SPARC && MTD_CFI @@ -240,13 +272,13 @@ config MTD_NETtel config MTD_ALCHEMY tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support" - depends on SOC_AU1X00 + depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI help Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards config MTD_MTX1 tristate "4G Systems MTX-1 Flash device" - depends on MIPS && MIPS_MTX1 + depends on MIPS_MTX1 && MTD_CFI help Flash memory access on 4G Systems MTX-1 Board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. @@ -384,7 +416,7 @@ config MTD_TQM834x config MTD_OCELOT tristate "Momenco Ocelot boot flash device" - depends on MIPS && MOMENCO_OCELOT + depends on MOMENCO_OCELOT help This enables access routines for the boot flash device and for the NVRAM on the Momenco Ocelot board. If you have one of these boards @@ -517,7 +549,7 @@ config MTD_OMAP_NOR # This needs CFI or JEDEC, depending on the cards found. config MTD_PCI tristate "PCI MTD driver" - depends on MTD && PCI && MTD_COMPLEX_MAPPINGS + depends on PCI && MTD_COMPLEX_MAPPINGS help Mapping for accessing flash devices on add-in cards like the Intel XScale IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode @@ -527,7 +559,7 @@ config MTD_PCI config MTD_PCMCIA tristate "PCMCIA MTD driver" - depends on MTD && PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN + depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN help Map driver for accessing PCMCIA linear flash memory cards. These cards are usually around 4-16MiB in size. This does not include @@ -591,13 +623,12 @@ config MTD_BAST_MAXSIZE config MTD_SHARP_SL bool "ROM mapped on Sharp SL Series" - depends on MTD && ARCH_PXA + depends on ARCH_PXA help This enables access to the flash chip on the Sharp SL Series of PDAs. config MTD_PLATRAM tristate "Map driver for platform device RAM (mtd-ram)" - depends on MTD select MTD_RAM help Map driver for RAM areas described via the platform device diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 071d0bf..de036c5 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -27,6 +27,8 @@ obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o +obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o +obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o obj-$(CONFIG_MTD_PNC2000) += pnc2000.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 7fc8097..84fbe0e 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c @@ -1,10 +1,7 @@ /* * Flash memory access on AMD Alchemy evaluation boards * - * $Id: alchemy-flash.c,v 1.2 2005/11/07 11:14:26 gleixner Exp $ - * * (C) 2003, 2004 Pete Popov - * */ #include @@ -18,12 +15,6 @@ #include #include -#ifdef DEBUG_RW -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - #ifdef CONFIG_MIPS_PB1000 #define BOARD_MAP_NAME "Pb1000 Flash" #define BOARD_FLASH_SIZE 0x00800000 /* 8MB */ diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 3d4a4d8..688ef49 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -338,7 +338,7 @@ static int __init init_ck804xrom(void) } return -ENXIO; #if 0 - return pci_module_init(&ck804xrom_driver); + return pci_register_driver(&ck804xrom_driver); #endif } diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 2b6504e..894c0b2 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -169,7 +169,8 @@ static int platram_probe(struct platform goto exit_free; } - dev_dbg(&pdev->dev, "got platform resource %p (0x%lx)\n", res, res->start); + dev_dbg(&pdev->dev, "got platform resource %p (0x%llx)\n", res, + (unsigned long long)res->start); /* setup map parameters */ diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c new file mode 100644 index 0000000..7e0377e --- /dev/null +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -0,0 +1,184 @@ +/* + * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. + * Config with both CFI and JEDEC device support. + * + * Basically physmap.c with the addition of partitions and + * an array of mapping info to accomodate more than one flash type per board. + * + * Copyright 2005-2007 PMC-Sierra, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +static struct mtd_info **msp_flash; +static struct mtd_partition **msp_parts; +static struct map_info *msp_maps; +static int fcnt; + +#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n",__FUNCTION__,__LINE__) + +int __init init_msp_flash(void) +{ + int i, j; + int offset, coff; + char *env; + int pcnt; + char flash_name[] = "flash0"; + char part_name[] = "flash0_0"; + unsigned addr, size; + + /* If ELB is disabled by "ful-mux" mode, we can't get at flash */ + if ((*DEV_ID_REG & DEV_ID_SINGLE_PC) && + (*ELB_1PC_EN_REG & SINGLE_PCCARD)) { + printk(KERN_NOTICE "Single PC Card mode: no flash access\n"); + return -ENXIO; + } + + /* examine the prom environment for flash devices */ + for (fcnt = 0; (env = prom_getenv(flash_name)); fcnt++) + flash_name[5] = '0' + fcnt + 1; + + if (fcnt < 1) + return -ENXIO; + + printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); + msp_flash = (struct mtd_info **)kmalloc( + fcnt * sizeof(struct map_info *), GFP_KERNEL); + msp_parts = (struct mtd_partition **)kmalloc( + fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); + msp_maps = (struct map_info *)kmalloc( + fcnt * sizeof(struct mtd_info), GFP_KERNEL); + memset(msp_maps, 0, fcnt * sizeof(struct mtd_info)); + + /* loop over the flash devices, initializing each */ + for (i = 0; i < fcnt; i++) { + /* examine the prom environment for flash partititions */ + part_name[5] = '0' + i; + part_name[7] = '0'; + for (pcnt = 0; (env = prom_getenv(part_name)); pcnt++) + part_name[7] = '0' + pcnt + 1; + + if (pcnt == 0) { + printk(KERN_NOTICE "Skipping flash device %d " + "(no partitions defined)\n", i); + continue; + } + + msp_parts[i] = (struct mtd_partition *)kmalloc( + pcnt * sizeof(struct mtd_partition), GFP_KERNEL); + memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition)); + + /* now initialize the devices proper */ + flash_name[5] = '0' + i; + env = prom_getenv(flash_name); + + if (sscanf(env, "%x:%x", &addr, &size) < 2) + return -ENXIO; + addr = CPHYSADDR(addr); + + printk(KERN_NOTICE + "MSP flash device \"%s\": 0x%08x at 0x%08x\n", + flash_name, size, addr); + /* This must matchs the actual size of the flash chip */ + msp_maps[i].size = size; + msp_maps[i].phys = addr; + + /* + * Platforms have a specific limit of the size of memory + * which may be mapped for flash: + */ + if (size > CONFIG_MSP_FLASH_MAP_LIMIT) + size = CONFIG_MSP_FLASH_MAP_LIMIT; + msp_maps[i].virt = ioremap(addr, size); + msp_maps[i].bankwidth = 1; + msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL), + flash_name, 7); + + if (msp_maps[i].virt == NULL) + return -ENXIO; + + for (j = 0; j < pcnt; j++) { + part_name[5] = '0' + i; + part_name[7] = '0' + j; + + env = prom_getenv(part_name); + + if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2) + return -ENXIO; + + msp_parts[i][j].size = size; + msp_parts[i][j].offset = offset; + msp_parts[i][j].name = env + coff; + } + + /* now probe and add the device */ + simple_map_init(&msp_maps[i]); + msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]); + if (msp_flash[i]) { + msp_flash[i]->owner = THIS_MODULE; + add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); + } else { + printk(KERN_ERR "map probe failed for flash\n"); + return -ENXIO; + } + } + + return 0; +} + +static void __exit cleanup_msp_flash(void) +{ + int i; + + for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) { + del_mtd_partitions(msp_flash[i]); + map_destroy(msp_flash[i]); + iounmap((void *)msp_maps[i].virt); + + /* free the memory */ + kfree(msp_maps[i].name); + kfree(msp_parts[i]); + } + + kfree(msp_flash); + kfree(msp_parts); + kfree(msp_maps); +} + +MODULE_AUTHOR("PMC-Sierra, Inc"); +MODULE_DESCRIPTION("MTD map driver for PMC-Sierra MSP boards"); +MODULE_LICENSE("GPL"); + +module_init(init_msp_flash); +module_exit(cleanup_msp_flash); diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c new file mode 100644 index 0000000..18049bc --- /dev/null +++ b/drivers/mtd/maps/pmcmsp-ramroot.c @@ -0,0 +1,105 @@ +/* + * Mapping of the rootfs in a physical region of memory + * + * Copyright (C) 2005-2007 PMC-Sierra Inc. + * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static struct mtd_info *rr_mtd; + +struct map_info rr_map = { + .name = "ramroot", + .bankwidth = 4, +}; + +static int __init init_rrmap(void) +{ + void *ramroot_start; + unsigned long ramroot_size; + + /* Check for supported rootfs types */ + if (get_ramroot(&ramroot_start, &ramroot_size)) { + rr_map.phys = CPHYSADDR(ramroot_start); + rr_map.size = ramroot_size; + + printk(KERN_NOTICE + "PMC embedded root device: 0x%08lx @ 0x%08lx\n", + rr_map.size, (unsigned long)rr_map.phys); + } else { + printk(KERN_ERR + "init_rrmap: no supported embedded rootfs detected!\n"); + return -ENXIO; + } + + /* Map rootfs to I/O space for block device driver */ + rr_map.virt = ioremap(rr_map.phys, rr_map.size); + if (!rr_map.virt) { + printk(KERN_ERR "Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&rr_map); + + rr_mtd = do_map_probe("map_ram", &rr_map); + if (rr_mtd) { + rr_mtd->owner = THIS_MODULE; + + add_mtd_device(rr_mtd); + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index); + + return 0; + } + + iounmap(rr_map.virt); + return -ENXIO; +} + +static void __exit cleanup_rrmap(void) +{ + del_mtd_device(rr_mtd); + map_destroy(rr_mtd); + + iounmap(rr_map.virt); + rr_map.virt = NULL; +} + +MODULE_AUTHOR("PMC-Sierra, Inc"); +MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem"); +MODULE_LICENSE("GPL"); + +module_init(init_rrmap); +module_exit(cleanup_rrmap); diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 4db2055..001af7f 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -39,7 +39,7 @@ MODULE_VERSION("2.0"); static LIST_HEAD(device_list); struct uflash_dev { - char *name; /* device name */ + const char *name; /* device name */ struct map_info map; /* mtd map info */ struct mtd_info *mtd; /* mtd info */ }; @@ -80,7 +80,7 @@ int uflash_devinit(struct linux_ebus_dev up->name = of_get_property(dp, "model", NULL); if (up->name && 0 < strlen(up->name)) - up->map.name = up->name; + up->map.name = (char *)up->name; up->map.phys = res->start; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index b879a66..524b83b 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -20,6 +20,7 @@ #include #include #include #include +#include #include static LIST_HEAD(blktrans_majors); @@ -28,9 +29,7 @@ extern struct mutex mtd_table_mutex; extern struct mtd_info *mtd_table[]; struct mtd_blkcore_priv { - struct completion thread_dead; - int exiting; - wait_queue_head_t thread_wq; + struct task_struct *thread; struct request_queue *rq; spinlock_t queue_lock; }; @@ -83,38 +82,19 @@ static int mtd_blktrans_thread(void *arg /* we might get involved when memory gets low, so use PF_MEMALLOC */ current->flags |= PF_MEMALLOC | PF_NOFREEZE; - daemonize("%sd", tr->name); - - /* daemonize() doesn't do this for us since some kernel threads - actually want to deal with signals. We can't just call - exit_sighand() since that'll cause an oops when we finally - do exit. */ - spin_lock_irq(¤t->sighand->siglock); - sigfillset(¤t->blocked); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - spin_lock_irq(rq->queue_lock); - - while (!tr->blkcore_priv->exiting) { + while (!kthread_should_stop()) { struct request *req; struct mtd_blktrans_dev *dev; int res = 0; - DECLARE_WAITQUEUE(wait, current); req = elv_next_request(rq); if (!req) { - add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(rq->queue_lock); - schedule(); - remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); - spin_lock_irq(rq->queue_lock); - continue; } @@ -133,13 +113,13 @@ static int mtd_blktrans_thread(void *arg } spin_unlock_irq(rq->queue_lock); - complete_and_exit(&tr->blkcore_priv->thread_dead, 0); + return 0; } static void mtd_blktrans_request(struct request_queue *rq) { struct mtd_blktrans_ops *tr = rq->queuedata; - wake_up(&tr->blkcore_priv->thread_wq); + wake_up_process(tr->blkcore_priv->thread); } @@ -388,8 +368,6 @@ int register_mtd_blktrans(struct mtd_blk return ret; } spin_lock_init(&tr->blkcore_priv->queue_lock); - init_completion(&tr->blkcore_priv->thread_dead); - init_waitqueue_head(&tr->blkcore_priv->thread_wq); tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); if (!tr->blkcore_priv->rq) { @@ -403,13 +381,14 @@ int register_mtd_blktrans(struct mtd_blk blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); tr->blkshift = ffs(tr->blksize) - 1; - ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL); - if (ret < 0) { + tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, + "%sd", tr->name); + if (IS_ERR(tr->blkcore_priv->thread)) { blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); kfree(tr->blkcore_priv); mutex_unlock(&mtd_table_mutex); - return ret; + return PTR_ERR(tr->blkcore_priv->thread); } INIT_LIST_HEAD(&tr->devs); @@ -432,9 +411,7 @@ int deregister_mtd_blktrans(struct mtd_b mutex_lock(&mtd_table_mutex); /* Clean up the kernel thread */ - tr->blkcore_priv->exiting = 1; - wake_up(&tr->blkcore_priv->thread_wq); - wait_for_completion(&tr->blkcore_priv->thread_dead); + kthread_stop(tr->blkcore_priv->thread); /* Remove it from the list of active majors */ list_del(&tr->list); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 1592eac..8c86b80 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -553,7 +553,7 @@ static int mtd_ioctl(struct inode *inode ops.datbuf = NULL; ops.mode = MTD_OOB_PLACE; - if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) return -EINVAL; ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2d12dcd..d05873b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,10 +1,7 @@ # drivers/mtd/nand/Kconfig # $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ -menu "NAND Flash Device Drivers" - depends on MTD!=n - -config MTD_NAND +menuconfig MTD_NAND tristate "NAND Device Support" depends on MTD select MTD_NAND_IDS @@ -13,9 +10,10 @@ config MTD_NAND devices. For further information see . +if MTD_NAND + config MTD_NAND_VERIFY_WRITE bool "Verify NAND page writes" - depends on MTD_NAND help This adds an extra check when data is written to the flash. The NAND flash device internally checks only bits transitioning @@ -25,53 +23,61 @@ config MTD_NAND_VERIFY_WRITE config MTD_NAND_ECC_SMC bool "NAND ECC Smart Media byte order" - depends on MTD_NAND default n help Software ECC according to the Smart Media Specification. The original Linux implementation had byte 0 and 1 swapped. +config MTD_NAND_MUSEUM_IDS + bool "Enable chip ids for obsolete ancient NAND devices" + depends on MTD_NAND + default n + help + Enable this option only when your board has first generation + NAND chips (page size 256 byte, erase size 4-8KiB). The IDs + of these chips were reused by later, larger chips. + config MTD_NAND_AUTCPU12 tristate "SmartMediaCard on autronix autcpu12 board" - depends on MTD_NAND && ARCH_AUTCPU12 + depends on ARCH_AUTCPU12 help This enables the driver for the autronix autcpu12 board to access the SmartMediaCard. config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" - depends on MTD_NAND && ARCH_EDB7312 + depends on ARCH_EDB7312 help This enables the driver for the Cirrus Logic EBD7312 evaluation board to access the onboard NAND Flash. config MTD_NAND_H1900 tristate "iPAQ H1900 flash" - depends on MTD_NAND && ARCH_PXA && MTD_PARTITIONS + depends on ARCH_PXA && MTD_PARTITIONS help This enables the driver for the iPAQ h1900 flash. config MTD_NAND_SPIA tristate "NAND Flash device on SPIA board" - depends on ARCH_P720T && MTD_NAND + depends on ARCH_P720T help If you had to ask, you don't have one. Say 'N'. config MTD_NAND_AMS_DELTA tristate "NAND Flash device on Amstrad E3" - depends on MACH_AMS_DELTA && MTD_NAND + depends on MACH_AMS_DELTA help Support for NAND flash on Amstrad E3 (Delta). config MTD_NAND_TOTO tristate "NAND Flash device on TOTO board" - depends on ARCH_OMAP && MTD_NAND && BROKEN + depends on ARCH_OMAP && BROKEN help Support for NAND flash on Texas Instruments Toto platform. config MTD_NAND_TS7250 tristate "NAND Flash device on TS-7250 board" - depends on MACH_TS72XX && MTD_NAND + depends on MACH_TS72XX help Support for NAND flash on Technologic Systems TS-7250 platform. @@ -80,14 +86,14 @@ config MTD_NAND_IDS config MTD_NAND_AU1550 tristate "Au1550/1200 NAND support" - depends on (SOC_AU1200 || SOC_AU1550) && MTD_NAND + depends on SOC_AU1200 || SOC_AU1550 help This enables the driver for the NAND flash controller on the AMD/Alchemy 1550 SOC. config MTD_NAND_RTC_FROM4 tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)" - depends on MTD_NAND && SH_SOLUTION_ENGINE + depends on SH_SOLUTION_ENGINE select REED_SOLOMON select REED_SOLOMON_DEC8 select BITREVERSE @@ -97,13 +103,13 @@ config MTD_NAND_RTC_FROM4 config MTD_NAND_PPCHAMELEONEVB tristate "NAND Flash device on PPChameleonEVB board" - depends on PPCHAMELEONEVB && MTD_NAND && BROKEN + depends on PPCHAMELEONEVB && BROKEN help This enables the NAND flash driver on the PPChameleon EVB Board. config MTD_NAND_S3C2410 tristate "NAND Flash support for S3C2410/S3C2440 SoC" - depends on ARCH_S3C2410 && MTD_NAND + depends on ARCH_S3C2410 help This enables the NAND flash controller on the S3C2410 and S3C2440 SoCs @@ -128,7 +134,7 @@ config MTD_NAND_S3C2410_HWECC config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" - depends on MTD_NAND && 44x + depends on 44x select MTD_NAND_ECC_SMC help NDFC Nand Flash Controllers are integrated in EP44x SoCs @@ -145,7 +151,7 @@ config MTD_NAND_S3C2410_CLKSTOP config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" - depends on MTD_NAND && EXPERIMENTAL + depends on EXPERIMENTAL select REED_SOLOMON select REED_SOLOMON_DEC16 help @@ -215,11 +221,11 @@ config MTD_NAND_DISKONCHIP_BBTWRITE config MTD_NAND_SHARPSL tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" - depends on MTD_NAND && ARCH_PXA + depends on ARCH_PXA config MTD_NAND_BASLER_EXCITE tristate "Support for NAND Flash on Basler eXcite" - depends on MTD_NAND && BASLER_EXCITE + depends on BASLER_EXCITE help This enables the driver for the NAND flash device found on the Basler eXcite Smart Camera. If built as a module, the driver @@ -227,14 +233,14 @@ config MTD_NAND_BASLER_EXCITE config MTD_NAND_CAFE tristate "NAND support for OLPC CAFÉ chip" - depends on MTD_NAND && PCI + depends on PCI help Use NAND flash attached to the CAFÉ chip designed for the $100 laptop. config MTD_NAND_CS553X tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" - depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH) + depends on X86_32 && (X86_PC || X86_GENERICARCH) help The CS553x companion chips for the AMD Geode processor include NAND flash controllers with built-in hardware ECC @@ -247,16 +253,21 @@ config MTD_NAND_CS553X config MTD_NAND_AT91 bool "Support for NAND Flash / SmartMedia on AT91" - depends on MTD_NAND && ARCH_AT91 + depends on ARCH_AT91 help Enables support for NAND Flash / Smart Media Card interface on Atmel AT91 processors. +config MTD_NAND_CM_X270 + tristate "Support for NAND Flash on CM-X270 modules" + depends on MTD_NAND && MACH_ARMCORE + + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" - depends on MTD_NAND && MTD_PARTITIONS + depends on MTD_PARTITIONS help The simulator may simulate various NAND flash chips for the MTD nand layer. -endmenu +endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 80f1dfc..6872031 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nands obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o +obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index fd6bb3e..c328a75 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -530,7 +530,6 @@ static int __devinit cafe_nand_probe(str { struct mtd_info *mtd; struct cafe_priv *cafe; - uint32_t timing1, timing2, timing3; uint32_t ctrl; int err = 0; @@ -587,21 +586,19 @@ static int __devinit cafe_nand_probe(str } if (numtimings == 3) { - timing1 = timing[0]; - timing2 = timing[1]; - timing3 = timing[2]; cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", - timing1, timing2, timing3); + timing[0], timing[1], timing[2]); } else { - timing1 = cafe_readl(cafe, NAND_TIMING1); - timing2 = cafe_readl(cafe, NAND_TIMING2); - timing3 = cafe_readl(cafe, NAND_TIMING3); + timing[0] = cafe_readl(cafe, NAND_TIMING1); + timing[1] = cafe_readl(cafe, NAND_TIMING2); + timing[2] = cafe_readl(cafe, NAND_TIMING3); - if (timing1 | timing2 | timing3) { - cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3); + if (timing[0] | timing[1] | timing[2]) { + cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", + timing[0], timing[1], timing[2]); } else { dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); - timing1 = timing2 = timing3 = 0xffffffff; + timing[0] = timing[1] = timing[2] = 0xffffffff; } } @@ -609,9 +606,9 @@ static int __devinit cafe_nand_probe(str cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 0, NAND_RESET); - cafe_writel(cafe, timing1, NAND_TIMING1); - cafe_writel(cafe, timing2, NAND_TIMING2); - cafe_writel(cafe, timing3, NAND_TIMING3); + cafe_writel(cafe, timing[0], NAND_TIMING1); + cafe_writel(cafe, timing[1], NAND_TIMING2); + cafe_writel(cafe, timing[2], NAND_TIMING3); cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c new file mode 100644 index 0000000..cb663ef --- /dev/null +++ b/drivers/mtd/nand/cmx270_nand.c @@ -0,0 +1,267 @@ +/* + * linux/drivers/mtd/nand/cmx270-nand.c + * + * Copyright (C) 2006 Compulab, Ltd. + * Mike Rapoport + * + * Derived from drivers/mtd/nand/h1910.c + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * CM-X270 board. + */ + +#include +#include + +#include +#include + +#include +#include + +#define GPIO_NAND_CS (11) +#define GPIO_NAND_RB (89) + +/* This macro needed to ensure in-order operation of GPIO and local + * bus. Without both asm command and dummy uncached read there're + * states when NAND access is broken. I've looked for such macro(s) in + * include/asm-arm but found nothing approptiate. + * dmac_clean_range is close, but is makes cache invalidation + * unnecessary here and it cannot be used in module + */ +#define DRAIN_WB() \ + do { \ + unsigned char dummy; \ + asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); \ + dummy=*((unsigned char*)UNCACHED_ADDR); \ + } while(0) + +/* MTD structure for CM-X270 board */ +static struct mtd_info *cmx270_nand_mtd; + +/* remaped IO address of the device */ +static void __iomem *cmx270_nand_io; + +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info[] = { + [0] = { + .name = "cmx270-0", + .offset = 0, + .size = MTDPART_SIZ_FULL + } +}; +#define NUM_PARTITIONS (ARRAY_SIZE(partition_info)) + +const char *part_probes[] = { "cmdlinepart", NULL }; + +static u_char cmx270_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + return (readl(this->IO_ADDR_R) >> 16); +} + +static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_W); +} + +static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R) >> 16; +} + +static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R) >> 16)) + return -EFAULT; + + return 0; +} + +static inline void nand_cs_on(void) +{ + GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS); +} + +static void nand_cs_off(void) +{ + DRAIN_WB(); + + GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS); +} + +/* + * hardware specific access to control-lines + */ +static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip* this = mtd->priv; + unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; + + DRAIN_WB(); + + if (ctrl & NAND_CTRL_CHANGE) { + if ( ctrl & NAND_ALE ) + nandaddr |= (1 << 3); + else + nandaddr &= ~(1 << 3); + if ( ctrl & NAND_CLE ) + nandaddr |= (1 << 2); + else + nandaddr &= ~(1 << 2); + if ( ctrl & NAND_NCE ) + nand_cs_on(); + else + nand_cs_off(); + } + + DRAIN_WB(); + this->IO_ADDR_W = (void __iomem*)nandaddr; + if (dat != NAND_CMD_NONE) + writel((dat << 16), this->IO_ADDR_W); + + DRAIN_WB(); +} + +/* + * read device ready pin + */ +static int cmx270_device_ready(struct mtd_info *mtd) +{ + DRAIN_WB(); + + return (GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB)); +} + +/* + * Main initialization routine + */ +static int cmx270_init(void) +{ + struct nand_chip *this; + const char *part_type; + struct mtd_partition *mtd_parts; + int mtd_parts_nb = 0; + int ret; + + /* Allocate memory for MTD device structure and private data */ + cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), + GFP_KERNEL); + if (!cmx270_nand_mtd) { + printk("Unable to allocate CM-X270 NAND MTD device structure.\n"); + return -ENOMEM; + } + + cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12); + if (!cmx270_nand_io) { + printk("Unable to ioremap NAND device\n"); + ret = -EINVAL; + goto err1; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&cmx270_nand_mtd[1]); + + /* Link the private data with the MTD structure */ + cmx270_nand_mtd->owner = THIS_MODULE; + cmx270_nand_mtd->priv = this; + + /* insert callbacks */ + this->IO_ADDR_R = cmx270_nand_io; + this->IO_ADDR_W = cmx270_nand_io; + this->cmd_ctrl = cmx270_hwcontrol; + this->dev_ready = cmx270_device_ready; + + /* 15 us command delay time */ + this->chip_delay = 20; + this->ecc.mode = NAND_ECC_SOFT; + + /* read/write functions */ + this->read_byte = cmx270_read_byte; + this->read_buf = cmx270_read_buf; + this->write_buf = cmx270_write_buf; + this->verify_buf = cmx270_verify_buf; + + /* Scan to find existence of the device */ + if (nand_scan (cmx270_nand_mtd, 1)) { + printk(KERN_NOTICE "No NAND device\n"); + ret = -ENXIO; + goto err2; + } + +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes, + &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (!mtd_parts_nb) { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb); + if (ret) + goto err2; + + /* Return happy */ + return 0; + +err2: + iounmap(cmx270_nand_io); +err1: + kfree(cmx270_nand_mtd); + + return ret; + +} +module_init(cmx270_init); + +/* + * Clean up routine + */ +static void cmx270_cleanup(void) +{ + /* Release resources, unregister device */ + nand_release(cmx270_nand_mtd); + + iounmap(cmx270_nand_io); + + /* Free the MTD device structure */ + kfree (cmx270_nand_mtd); +} +module_exit(cmx270_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Rapoport "); +MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6af37b8..04de315 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -312,7 +312,7 @@ static int nand_block_bad(struct mtd_inf /* Select the NAND device */ chip->select_chip(mtd, chipnr); } else - page = (int)ofs; + page = (int)(ofs >> chip->page_shift); if (chip->options & NAND_BUSWIDTH_16) { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, @@ -350,7 +350,7 @@ static int nand_default_block_markbad(st int block, ret; /* Get block number */ - block = ((int)ofs) >> chip->bbt_erase_shift; + block = (int)(ofs >> chip->bbt_erase_shift); if (chip->bbt) chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); @@ -771,7 +771,7 @@ static int nand_read_page_swecc(struct m uint8_t *ecc_code = chip->buffers->ecccode; int *eccpos = chip->ecc.layout->eccpos; - nand_read_page_raw(mtd, chip, buf); + chip->ecc.read_page_raw(mtd, chip, buf); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -1426,7 +1426,7 @@ static void nand_write_page_swecc(struct for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - nand_write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf); } /** diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2e2cdf2..2fc674a 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -24,6 +24,8 @@ #include * 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { + +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, @@ -39,6 +41,7 @@ struct nand_flash_dev nand_flash_ids[] = {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, +#endif {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, @@ -137,6 +140,7 @@ struct nand_manufacturers nand_manuf_ids {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_HYNIX, "Hynix"}, + {NAND_MFR_MICRON, "Micron"}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index c3bca95..205df0f 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -37,6 +37,8 @@ #include #include #include #include +#include +#include /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -90,6 +92,15 @@ static uint bus_width = CONFIG_NAND static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; static uint log = CONFIG_NANDSIM_LOG; static uint dbg = CONFIG_NANDSIM_DBG; +static unsigned long parts[MAX_MTD_DEVICES]; +static unsigned int parts_num; +static char *badblocks = NULL; +static char *weakblocks = NULL; +static char *weakpages = NULL; +static unsigned int bitflips = 0; +static char *gravepages = NULL; +static unsigned int rptwear = 0; +static unsigned int overridesize = 0; module_param(first_id_byte, uint, 0400); module_param(second_id_byte, uint, 0400); @@ -104,8 +115,16 @@ module_param(bus_width, uint, 0400) module_param(do_delays, uint, 0400); module_param(log, uint, 0400); module_param(dbg, uint, 0400); - -MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); +module_param_array(parts, ulong, &parts_num, 0400); +module_param(badblocks, charp, 0400); +module_param(weakblocks, charp, 0400); +module_param(weakpages, charp, 0400); +module_param(bitflips, uint, 0400); +module_param(gravepages, charp, 0400); +module_param(rptwear, uint, 0400); +module_param(overridesize, uint, 0400); + +MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); @@ -118,6 +137,23 @@ MODULE_PARM_DESC(bus_width, "Chip's MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); MODULE_PARM_DESC(log, "Perform logging if not zero"); MODULE_PARM_DESC(dbg, "Output debug information if not zero"); +MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas"); +/* Page and erase block positions for the following parameters are independent of any partitions */ +MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas"); +MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]" + " separated by commas e.g. 113:2 means eb 113" + " can be erased only twice before failing"); +MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]" + " separated by commas e.g. 1401:2 means page 1401" + " can be written only twice before failing"); +MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)"); +MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" + " separated by commas e.g. 1401:2 means page 1401" + " can be read only twice before failing"); +MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero"); +MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " + "The size is specified in erase blocks and as the exponent of a power of two" + " e.g. 5 means a size of 32 erase blocks"); /* The largest possible page size */ #define NS_LARGEST_PAGE_SIZE 2048 @@ -131,9 +167,11 @@ #define NS_LOG(args...) \ #define NS_DBG(args...) \ do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) #define NS_WARN(args...) \ - do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0) + do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) #define NS_ERR(args...) \ - do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0) + do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) +#define NS_INFO(args...) \ + do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) /* Busy-wait delay macros (microseconds, milliseconds) */ #define NS_UDELAY(us) \ @@ -238,7 +276,8 @@ union ns_mem { * The structure which describes all the internal simulator data. */ struct nandsim { - struct mtd_partition part; + struct mtd_partition partitions[MAX_MTD_DEVICES]; + unsigned int nbparts; uint busw; /* flash chip bus width (8 or 16) */ u_char ids[4]; /* chip's ID bytes */ @@ -338,6 +377,38 @@ static struct nandsim_operations { STATE_DATAOUT, STATE_READY}} }; +struct weak_block { + struct list_head list; + unsigned int erase_block_no; + unsigned int max_erases; + unsigned int erases_done; +}; + +static LIST_HEAD(weak_blocks); + +struct weak_page { + struct list_head list; + unsigned int page_no; + unsigned int max_writes; + unsigned int writes_done; +}; + +static LIST_HEAD(weak_pages); + +struct grave_page { + struct list_head list; + unsigned int page_no; + unsigned int max_reads; + unsigned int reads_done; +}; + +static LIST_HEAD(grave_pages); + +static unsigned long *erase_block_wear = NULL; +static unsigned int wear_eb_count = 0; +static unsigned long total_wear = 0; +static unsigned int rptwear_cnt = 0; + /* MTD structure for NAND controller */ static struct mtd_info *nsmtd; @@ -381,6 +452,13 @@ static void free_device(struct nandsim * } } +static char *get_partition_name(int i) +{ + char buf[64]; + sprintf(buf, "NAND simulator partition %d", i); + return kstrdup(buf, GFP_KERNEL); +} + /* * Initialize the nandsim structure. * @@ -390,7 +468,9 @@ static int init_nandsim(struct mtd_info { struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); - int i; + int i, ret = 0; + u_int32_t remains; + u_int32_t next_offset; if (NS_IS_INITIALIZED(ns)) { NS_ERR("init_nandsim: nandsim is already initialized\n"); @@ -448,6 +528,40 @@ static int init_nandsim(struct mtd_info } } + /* Fill the partition_info structure */ + if (parts_num > ARRAY_SIZE(ns->partitions)) { + NS_ERR("too many partitions.\n"); + ret = -EINVAL; + goto error; + } + remains = ns->geom.totsz; + next_offset = 0; + for (i = 0; i < parts_num; ++i) { + unsigned long part = parts[i]; + if (!part || part > remains / ns->geom.secsz) { + NS_ERR("bad partition size.\n"); + ret = -EINVAL; + goto error; + } + ns->partitions[i].name = get_partition_name(i); + ns->partitions[i].offset = next_offset; + ns->partitions[i].size = part * ns->geom.secsz; + next_offset += ns->partitions[i].size; + remains -= ns->partitions[i].size; + } + ns->nbparts = parts_num; + if (remains) { + if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { + NS_ERR("too many partitions.\n"); + ret = -EINVAL; + goto error; + } + ns->partitions[i].name = get_partition_name(i); + ns->partitions[i].offset = next_offset; + ns->partitions[i].size = remains; + ns->nbparts += 1; + } + /* Detect how many ID bytes the NAND chip outputs */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (second_id_byte != nand_flash_ids[i].id) @@ -474,7 +588,7 @@ static int init_nandsim(struct mtd_info printk("sector address bytes: %u\n", ns->geom.secaddrbytes); printk("options: %#x\n", ns->options); - if (alloc_device(ns) != 0) + if ((ret = alloc_device(ns)) != 0) goto error; /* Allocate / initialize the internal buffer */ @@ -482,21 +596,17 @@ static int init_nandsim(struct mtd_info if (!ns->buf.byte) { NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", ns->geom.pgszoob); + ret = -ENOMEM; goto error; } memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); - /* Fill the partition_info structure */ - ns->part.name = "NAND simulator partition"; - ns->part.offset = 0; - ns->part.size = ns->geom.totsz; - return 0; error: free_device(ns); - return -ENOMEM; + return ret; } /* @@ -510,6 +620,287 @@ static void free_nandsim(struct nandsim return; } +static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd) +{ + char *w; + int zero_ok; + unsigned int erase_block_no; + loff_t offset; + + if (!badblocks) + return 0; + w = badblocks; + do { + zero_ok = (*w == '0' ? 1 : 0); + erase_block_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !erase_block_no) { + NS_ERR("invalid badblocks.\n"); + return -EINVAL; + } + offset = erase_block_no * ns->geom.secsz; + if (mtd->block_markbad(mtd, offset)) { + NS_ERR("invalid badblocks.\n"); + return -EINVAL; + } + if (*w == ',') + w += 1; + } while (*w); + return 0; +} + +static int parse_weakblocks(void) +{ + char *w; + int zero_ok; + unsigned int erase_block_no; + unsigned int max_erases; + struct weak_block *wb; + + if (!weakblocks) + return 0; + w = weakblocks; + do { + zero_ok = (*w == '0' ? 1 : 0); + erase_block_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !erase_block_no) { + NS_ERR("invalid weakblocks.\n"); + return -EINVAL; + } + max_erases = 3; + if (*w == ':') { + w += 1; + max_erases = simple_strtoul(w, &w, 0); + } + if (*w == ',') + w += 1; + wb = kzalloc(sizeof(*wb), GFP_KERNEL); + if (!wb) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + wb->erase_block_no = erase_block_no; + wb->max_erases = max_erases; + list_add(&wb->list, &weak_blocks); + } while (*w); + return 0; +} + +static int erase_error(unsigned int erase_block_no) +{ + struct weak_block *wb; + + list_for_each_entry(wb, &weak_blocks, list) + if (wb->erase_block_no == erase_block_no) { + if (wb->erases_done >= wb->max_erases) + return 1; + wb->erases_done += 1; + return 0; + } + return 0; +} + +static int parse_weakpages(void) +{ + char *w; + int zero_ok; + unsigned int page_no; + unsigned int max_writes; + struct weak_page *wp; + + if (!weakpages) + return 0; + w = weakpages; + do { + zero_ok = (*w == '0' ? 1 : 0); + page_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !page_no) { + NS_ERR("invalid weakpagess.\n"); + return -EINVAL; + } + max_writes = 3; + if (*w == ':') { + w += 1; + max_writes = simple_strtoul(w, &w, 0); + } + if (*w == ',') + w += 1; + wp = kzalloc(sizeof(*wp), GFP_KERNEL); + if (!wp) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + wp->page_no = page_no; + wp->max_writes = max_writes; + list_add(&wp->list, &weak_pages); + } while (*w); + return 0; +} + +static int write_error(unsigned int page_no) +{ + struct weak_page *wp; + + list_for_each_entry(wp, &weak_pages, list) + if (wp->page_no == page_no) { + if (wp->writes_done >= wp->max_writes) + return 1; + wp->writes_done += 1; + return 0; + } + return 0; +} + +static int parse_gravepages(void) +{ + char *g; + int zero_ok; + unsigned int page_no; + unsigned int max_reads; + struct grave_page *gp; + + if (!gravepages) + return 0; + g = gravepages; + do { + zero_ok = (*g == '0' ? 1 : 0); + page_no = simple_strtoul(g, &g, 0); + if (!zero_ok && !page_no) { + NS_ERR("invalid gravepagess.\n"); + return -EINVAL; + } + max_reads = 3; + if (*g == ':') { + g += 1; + max_reads = simple_strtoul(g, &g, 0); + } + if (*g == ',') + g += 1; + gp = kzalloc(sizeof(*gp), GFP_KERNEL); + if (!gp) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + gp->page_no = page_no; + gp->max_reads = max_reads; + list_add(&gp->list, &grave_pages); + } while (*g); + return 0; +} + +static int read_error(unsigned int page_no) +{ + struct grave_page *gp; + + list_for_each_entry(gp, &grave_pages, list) + if (gp->page_no == page_no) { + if (gp->reads_done >= gp->max_reads) + return 1; + gp->reads_done += 1; + return 0; + } + return 0; +} + +static void free_lists(void) +{ + struct list_head *pos, *n; + list_for_each_safe(pos, n, &weak_blocks) { + list_del(pos); + kfree(list_entry(pos, struct weak_block, list)); + } + list_for_each_safe(pos, n, &weak_pages) { + list_del(pos); + kfree(list_entry(pos, struct weak_page, list)); + } + list_for_each_safe(pos, n, &grave_pages) { + list_del(pos); + kfree(list_entry(pos, struct grave_page, list)); + } + kfree(erase_block_wear); +} + +static int setup_wear_reporting(struct mtd_info *mtd) +{ + size_t mem; + + if (!rptwear) + return 0; + wear_eb_count = mtd->size / mtd->erasesize; + mem = wear_eb_count * sizeof(unsigned long); + if (mem / sizeof(unsigned long) != wear_eb_count) { + NS_ERR("Too many erase blocks for wear reporting\n"); + return -ENOMEM; + } + erase_block_wear = kzalloc(mem, GFP_KERNEL); + if (!erase_block_wear) { + NS_ERR("Too many erase blocks for wear reporting\n"); + return -ENOMEM; + } + return 0; +} + +static void update_wear(unsigned int erase_block_no) +{ + unsigned long wmin = -1, wmax = 0, avg; + unsigned long deciles[10], decile_max[10], tot = 0; + unsigned int i; + + if (!erase_block_wear) + return; + total_wear += 1; + if (total_wear == 0) + NS_ERR("Erase counter total overflow\n"); + erase_block_wear[erase_block_no] += 1; + if (erase_block_wear[erase_block_no] == 0) + NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); + rptwear_cnt += 1; + if (rptwear_cnt < rptwear) + return; + rptwear_cnt = 0; + /* Calc wear stats */ + for (i = 0; i < wear_eb_count; ++i) { + unsigned long wear = erase_block_wear[i]; + if (wear < wmin) + wmin = wear; + if (wear > wmax) + wmax = wear; + tot += wear; + } + for (i = 0; i < 9; ++i) { + deciles[i] = 0; + decile_max[i] = (wmax * (i + 1) + 5) / 10; + } + deciles[9] = 0; + decile_max[9] = wmax; + for (i = 0; i < wear_eb_count; ++i) { + int d; + unsigned long wear = erase_block_wear[i]; + for (d = 0; d < 10; ++d) + if (wear <= decile_max[d]) { + deciles[d] += 1; + break; + } + } + avg = tot / wear_eb_count; + /* Output wear report */ + NS_INFO("*** Wear Report ***\n"); + NS_INFO("Total numbers of erases: %lu\n", tot); + NS_INFO("Number of erase blocks: %u\n", wear_eb_count); + NS_INFO("Average number of erases: %lu\n", avg); + NS_INFO("Maximum number of erases: %lu\n", wmax); + NS_INFO("Minimum number of erases: %lu\n", wmin); + for (i = 0; i < 10; ++i) { + unsigned long from = (i ? decile_max[i - 1] + 1 : 0); + if (from > decile_max[i]) + continue; + NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n", + from, + decile_max[i], + deciles[i]); + } + NS_INFO("*** End of Wear Report ***\n"); +} + /* * Returns the string representation of 'state' state. */ @@ -822,9 +1213,31 @@ static void read_page(struct nandsim *ns NS_DBG("read_page: page %d not allocated\n", ns->regs.row); memset(ns->buf.byte, 0xFF, num); } else { + unsigned int page_no = ns->regs.row; NS_DBG("read_page: page %d allocated, reading from %d\n", ns->regs.row, ns->regs.column + ns->regs.off); + if (read_error(page_no)) { + int i; + memset(ns->buf.byte, 0xFF, num); + for (i = 0; i < num; ++i) + ns->buf.byte[i] = random32(); + NS_WARN("simulating read error in page %u\n", page_no); + return; + } memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); + if (bitflips && random32() < (1 << 22)) { + int flips = 1; + if (bitflips > 1) + flips = (random32() % (int) bitflips) + 1; + while (flips--) { + int pos = random32() % (num * 8); + ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); + NS_WARN("read_page: flipping bit %d in page %d " + "reading from %d ecc: corrected=%u failed=%u\n", + pos, ns->regs.row, ns->regs.column + ns->regs.off, + nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); + } + } } } @@ -883,6 +1296,7 @@ static int do_state_action(struct nandsi { int num; int busdiv = ns->busw == 8 ? 1 : 2; + unsigned int erase_block_no, page_no; action &= ACTION_MASK; @@ -942,14 +1356,24 @@ static int do_state_action(struct nandsi 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; ns->regs.column = 0; + erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift); + NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", ns->regs.row, NS_RAW_OFFSET(ns)); - NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); + NS_LOG("erase sector %u\n", erase_block_no); erase_sector(ns); NS_MDELAY(erase_delay); + if (erase_block_wear) + update_wear(erase_block_no); + + if (erase_error(erase_block_no)) { + NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); + return -1; + } + break; case ACTION_PRGPAGE: @@ -972,6 +1396,8 @@ static int do_state_action(struct nandsi if (prog_page(ns, num) == -1) return -1; + page_no = ns->regs.row; + NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); NS_LOG("programm page %d\n", ns->regs.row); @@ -979,6 +1405,11 @@ static int do_state_action(struct nandsi NS_UDELAY(programm_delay); NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); + if (write_error(page_no)) { + NS_WARN("simulating write failure in page %u\n", page_no); + return -1; + } + break; case ACTION_ZEROOFF: @@ -1503,7 +1934,7 @@ static int __init ns_init_module(void) { struct nand_chip *chip; struct nandsim *nand; - int retval = -ENOMEM; + int retval = -ENOMEM, i; if (bus_width != 8 && bus_width != 16) { NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); @@ -1533,6 +1964,8 @@ static int __init ns_init_module(void) chip->verify_buf = ns_nand_verify_buf; chip->read_word = ns_nand_read_word; chip->ecc.mode = NAND_ECC_SOFT; + /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ + /* and 'badblocks' parameters to work */ chip->options |= NAND_SKIP_BBTSCAN; /* @@ -1557,6 +1990,15 @@ static int __init ns_init_module(void) nsmtd->owner = THIS_MODULE; + if ((retval = parse_weakblocks()) != 0) + goto error; + + if ((retval = parse_weakpages()) != 0) + goto error; + + if ((retval = parse_gravepages()) != 0) + goto error; + if ((retval = nand_scan(nsmtd, 1)) != 0) { NS_ERR("can't register NAND Simulator\n"); if (retval > 0) @@ -1564,23 +2006,44 @@ static int __init ns_init_module(void) goto error; } - if ((retval = init_nandsim(nsmtd)) != 0) { - NS_ERR("scan_bbt: can't initialize the nandsim structure\n"); - goto error; + if (overridesize) { + u_int32_t new_size = nsmtd->erasesize << overridesize; + if (new_size >> overridesize != nsmtd->erasesize) { + NS_ERR("overridesize is too big\n"); + goto err_exit; + } + /* N.B. This relies on nand_scan not doing anything with the size before we change it */ + nsmtd->size = new_size; + chip->chipsize = new_size; + chip->chip_shift = ffs(new_size) - 1; } - if ((retval = nand_default_bbt(nsmtd)) != 0) { - free_nandsim(nand); - goto error; - } + if ((retval = setup_wear_reporting(nsmtd)) != 0) + goto err_exit; + + if ((retval = init_nandsim(nsmtd)) != 0) + goto err_exit; - /* Register NAND as one big partition */ - add_mtd_partitions(nsmtd, &nand->part, 1); + if ((retval = parse_badblocks(nand, nsmtd)) != 0) + goto err_exit; + + if ((retval = nand_default_bbt(nsmtd)) != 0) + goto err_exit; + + /* Register NAND partitions */ + if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0) + goto err_exit; return 0; +err_exit: + free_nandsim(nand); + nand_release(nsmtd); + for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i) + kfree(nand->partitions[i].name); error: kfree(nsmtd); + free_lists(); return retval; } @@ -1593,10 +2056,14 @@ module_init(ns_init_module); static void __exit ns_cleanup_module(void) { struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv); + int i; free_nandsim(ns); /* Free nandsim private resources */ - nand_release(nsmtd); /* Unregisterd drived */ + nand_release(nsmtd); /* Unregister driver */ + for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) + kfree(ns->partitions[i].name); kfree(nsmtd); /* Free other structures */ + free_lists(); } module_exit(ns_cleanup_module); @@ -1604,4 +2071,3 @@ module_exit(ns_cleanup_module); MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Artem B. Bityuckiy"); MODULE_DESCRIPTION ("The NAND flash simulator"); - diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 373bddc..c257d39 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -2,20 +2,18 @@ # # linux/drivers/mtd/onenand/Kconfig # -menu "OneNAND Flash Device Drivers" - depends on MTD != n - -config MTD_ONENAND +menuconfig MTD_ONENAND tristate "OneNAND Device Support" depends on MTD help This enables support for accessing all type of OneNAND flash devices. For further information see - . + + +if MTD_ONENAND config MTD_ONENAND_VERIFY_WRITE bool "Verify OneNAND page writes" - depends on MTD_ONENAND help This adds an extra check when data is written to the flash. The OneNAND flash device internally checks only bits transitioning @@ -25,13 +23,12 @@ config MTD_ONENAND_VERIFY_WRITE config MTD_ONENAND_GENERIC tristate "OneNAND Flash device via platform device driver" - depends on MTD_ONENAND && ARM + depends on ARM help Support for OneNAND flash via platform device driver. config MTD_ONENAND_OTP bool "OneNAND OTP Support" - depends on MTD_ONENAND help One Block of the NAND Flash Array memory is reserved as a One-Time Programmable Block memory area. @@ -43,4 +40,4 @@ config MTD_ONENAND_OTP OTP block is fully-guaranteed to be a valid block. -endmenu +endif # MTD_ONENAND diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 9e14a26..000794c 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -836,9 +836,11 @@ static int onenand_transfer_auto_oob(str int readcol = column; int readend = column + thislen; int lastgap = 0; + unsigned int i; uint8_t *oob_buf = this->oob_buf; - for (free = this->ecclayout->oobfree; free->length; ++free) { + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { if (readcol >= lastgap) readcol += free->offset - lastgap; if (readend >= lastgap) @@ -846,7 +848,8 @@ static int onenand_transfer_auto_oob(str lastgap = free->offset + free->length; } this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); - for (free = this->ecclayout->oobfree; free->length; ++free) { + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { int free_end = free->offset + free->length; if (free->offset < readend && free_end > readcol) { int st = max_t(int,free->offset,readcol); @@ -854,7 +857,7 @@ static int onenand_transfer_auto_oob(str int n = ed - st; memcpy(buf, oob_buf + st, n); buf += n; - } else + } else if (column == 0) break; } return 0; @@ -1280,15 +1283,18 @@ static int onenand_fill_auto_oob(struct int writecol = column; int writeend = column + thislen; int lastgap = 0; + unsigned int i; - for (free = this->ecclayout->oobfree; free->length; ++free) { + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { if (writecol >= lastgap) writecol += free->offset - lastgap; if (writeend >= lastgap) writeend += free->offset - lastgap; lastgap = free->offset + free->length; } - for (free = this->ecclayout->oobfree; free->length; ++free) { + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { int free_end = free->offset + free->length; if (free->offset < writeend && free_end > writecol) { int st = max_t(int,free->offset,writecol); @@ -1296,7 +1302,7 @@ static int onenand_fill_auto_oob(struct int n = ed - st; memcpy(oob_buf + st, buf, n); buf += n; - } else + } else if (column == 0) break; } return 0; @@ -2386,7 +2392,8 @@ int onenand_scan(struct mtd_info *mtd, i * the out of band area */ this->ecclayout->oobavail = 0; - for (i = 0; this->ecclayout->oobfree[i].length; i++) + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && + this->ecclayout->oobfree[i].length; i++) this->ecclayout->oobavail += this->ecclayout->oobfree[i].length; mtd->oobavail = this->ecclayout->oobavail; diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig new file mode 100644 index 0000000..b9daf15 --- /dev/null +++ b/drivers/mtd/ubi/Kconfig @@ -0,0 +1,58 @@ +# drivers/mtd/ubi/Kconfig + +menu "UBI - Unsorted block images" + depends on MTD + +config MTD_UBI + tristate "Enable UBI" + depends on MTD + select CRC32 + help + UBI is a software layer above MTD layer which admits of LVM-like + logical volumes on top of MTD devices, hides some complexities of + flash chips like wear and bad blocks and provides some other useful + capabilities. Please, consult the MTD web site for more details + (www.linux-mtd.infradead.org). + +config MTD_UBI_WL_THRESHOLD + int "UBI wear-leveling threshold" + default 4096 + range 2 65536 + depends on MTD_UBI + help + This parameter defines the maximum difference between the highest + erase counter value and the lowest erase counter value of eraseblocks + of UBI devices. When this threshold is exceeded, UBI starts performing + wear leveling by means of moving data from eraseblock with low erase + counter to eraseblocks with high erase counter. Leave the default + value if unsure. + +config MTD_UBI_BEB_RESERVE + int "Percentage of reserved eraseblocks for bad eraseblocks handling" + default 1 + range 0 25 + depends on MTD_UBI + help + If the MTD device admits of bad eraseblocks (e.g. NAND flash), UBI + reserves some amount of physical eraseblocks to handle new bad + eraseblocks. For example, if a flash physical eraseblock becomes bad, + UBI uses these reserved physical eraseblocks to relocate the bad one. + This option specifies how many physical eraseblocks will be reserved + for bad eraseblock handling (percents of total number of good flash + eraseblocks). If the underlying flash does not admit of bad + eraseblocks (e.g. NOR flash), this value is ignored and nothing is + reserved. Leave the default value if unsure. + +config MTD_UBI_GLUEBI + bool "Emulate MTD devices" + default n + depends on MTD_UBI + help + This option enables MTD devices emulation on top of UBI volumes: for + each UBI volumes an MTD device is created, and all I/O to this MTD + device is redirected to the UBI volume. This is handy to make + MTD-oriented software (like JFFS2) work on top of UBI. Do not enable + this if no legacy software will be used. + +source "drivers/mtd/ubi/Kconfig.debug" +endmenu diff --git a/drivers/mtd/ubi/Kconfig.debug b/drivers/mtd/ubi/Kconfig.debug new file mode 100644 index 0000000..1e2ee22 --- /dev/null +++ b/drivers/mtd/ubi/Kconfig.debug @@ -0,0 +1,104 @@ +comment "UBI debugging options" + depends on MTD_UBI + +config MTD_UBI_DEBUG + bool "UBI debugging" + depends on SYSFS + depends on MTD_UBI + select DEBUG_FS + select KALLSYMS_ALL + help + This option enables UBI debugging. + +config MTD_UBI_DEBUG_MSG + bool "UBI debugging messages" + depends on MTD_UBI_DEBUG + default n + help + This option enables UBI debugging messages. + +config MTD_UBI_DEBUG_PARANOID + bool "Extra self-checks" + default n + depends on MTD_UBI_DEBUG + help + This option enables extra checks in UBI code. Note this slows UBI down + significantly. + +config MTD_UBI_DEBUG_DISABLE_BGT + bool "Do not enable the UBI background thread" + depends on MTD_UBI_DEBUG + default n + help + This option switches the background thread off by default. The thread + may be also be enabled/disabled via UBI sysfs. + +config MTD_UBI_DEBUG_USERSPACE_IO + bool "Direct user-space write/erase support" + default n + depends on MTD_UBI_DEBUG + help + By default, users cannot directly write and erase individual + eraseblocks of dynamic volumes, and have to use update operation + instead. This option enables this capability - it is very useful for + debugging and testing. + +config MTD_UBI_DEBUG_EMULATE_BITFLIPS + bool "Emulate flash bit-flips" + depends on MTD_UBI_DEBUG + default n + help + This option emulates bit-flips with probability 1/50, which in turn + causes scrubbing. Useful for debugging and stressing UBI. + +config MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES + bool "Emulate flash write failures" + depends on MTD_UBI_DEBUG + default n + help + This option emulates write failures with probability 1/100. Useful for + debugging and testing how UBI handlines errors. + +config MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES + bool "Emulate flash erase failures" + depends on MTD_UBI_DEBUG + default n + help + This option emulates erase failures with probability 1/100. Useful for + debugging and testing how UBI handlines errors. + +menu "Additional UBI debugging messages" + depends on MTD_UBI_DEBUG + +config MTD_UBI_DEBUG_MSG_BLD + bool "Additional UBI initialization and build messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables detailed UBI initialization and device build + debugging messages. + +config MTD_UBI_DEBUG_MSG_EBA + bool "Eraseblock association unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI eraseblock + association unit. + +config MTD_UBI_DEBUG_MSG_WL + bool "Wear-leveling unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI wear-leveling + unit. + +config MTD_UBI_DEBUG_MSG_IO + bool "Input/output unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI input/output unit. + +endmenu # UBI debugging messages diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile new file mode 100644 index 0000000..dd834e0 --- /dev/null +++ b/drivers/mtd/ubi/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_MTD_UBI) += ubi.o + +ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o scan.o +ubi-y += misc.o + +ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o +ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c new file mode 100644 index 0000000..555d594 --- /dev/null +++ b/drivers/mtd/ubi/build.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём), + * Frank Haverkamp + */ + +/* + * This file includes UBI initialization and building of UBI devices. At the + * moment UBI devices may only be added while UBI is initialized, but dynamic + * device add/remove functionality is planned. Also, at the moment we only + * attach UBI devices by scanning, which will become a bottleneck when flashes + * reach certain large size. Then one may improve UBI and add other methods. + */ + +#include +#include +#include +#include +#include +#include "ubi.h" + +/* Maximum length of the 'mtd=' parameter */ +#define MTD_PARAM_LEN_MAX 64 + +/** + * struct mtd_dev_param - MTD device parameter description data structure. + * @name: MTD device name or number string + * @vid_hdr_offs: VID header offset + * @data_offs: data offset + */ +struct mtd_dev_param +{ + char name[MTD_PARAM_LEN_MAX]; + int vid_hdr_offs; + int data_offs; +}; + +/* Numbers of elements set in the @mtd_dev_param array */ +static int mtd_devs = 0; + +/* MTD devices specification parameters */ +static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; + +/* Number of UBI devices in system */ +int ubi_devices_cnt; + +/* All UBI devices in system */ +struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; + +/* Root UBI "class" object (corresponds to '//class/ubi/') */ +struct class *ubi_class; + +/* "Show" method for files in '//class/ubi/' */ +static ssize_t ubi_version_show(struct class *class, char *buf) +{ + return sprintf(buf, "%d\n", UBI_VERSION); +} + +/* UBI version attribute ('//class/ubi/version') */ +static struct class_attribute ubi_version = + __ATTR(version, S_IRUGO, ubi_version_show, NULL); + +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* UBI device attributes (correspond to files in '//class/ubi/ubiX') */ +static struct device_attribute dev_eraseblock_size = + __ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_avail_eraseblocks = + __ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_total_eraseblocks = + __ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_volumes_count = + __ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_ec = + __ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_reserved_for_bad = + __ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bad_peb_count = + __ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_vol_count = + __ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_min_io_size = + __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bgt_enabled = + __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); + +/* "Show" method for files in '//class/ubi/ubiX/' */ +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct ubi_device *ubi; + + ubi = container_of(dev, struct ubi_device, dev); + if (attr == &dev_eraseblock_size) + return sprintf(buf, "%d\n", ubi->leb_size); + else if (attr == &dev_avail_eraseblocks) + return sprintf(buf, "%d\n", ubi->avail_pebs); + else if (attr == &dev_total_eraseblocks) + return sprintf(buf, "%d\n", ubi->good_peb_count); + else if (attr == &dev_volumes_count) + return sprintf(buf, "%d\n", ubi->vol_count); + else if (attr == &dev_max_ec) + return sprintf(buf, "%d\n", ubi->max_ec); + else if (attr == &dev_reserved_for_bad) + return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + else if (attr == &dev_bad_peb_count) + return sprintf(buf, "%d\n", ubi->bad_peb_count); + else if (attr == &dev_max_vol_count) + return sprintf(buf, "%d\n", ubi->vtbl_slots); + else if (attr == &dev_min_io_size) + return sprintf(buf, "%d\n", ubi->min_io_size); + else if (attr == &dev_bgt_enabled) + return sprintf(buf, "%d\n", ubi->thread_enabled); + else + BUG(); + + return 0; +} + +/* Fake "release" method for UBI devices */ +static void dev_release(struct device *dev) { } + +/** + * ubi_sysfs_init - initialize sysfs for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int ubi_sysfs_init(struct ubi_device *ubi) +{ + int err; + + ubi->dev.release = dev_release; + ubi->dev.devt = MKDEV(ubi->major, 0); + ubi->dev.class = ubi_class; + sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + err = device_register(&ubi->dev); + if (err) + goto out; + + err = device_create_file(&ubi->dev, &dev_eraseblock_size); + if (err) + goto out_unregister; + err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); + if (err) + goto out_eraseblock_size; + err = device_create_file(&ubi->dev, &dev_total_eraseblocks); + if (err) + goto out_avail_eraseblocks; + err = device_create_file(&ubi->dev, &dev_volumes_count); + if (err) + goto out_total_eraseblocks; + err = device_create_file(&ubi->dev, &dev_max_ec); + if (err) + goto out_volumes_count; + err = device_create_file(&ubi->dev, &dev_reserved_for_bad); + if (err) + goto out_volumes_max_ec; + err = device_create_file(&ubi->dev, &dev_bad_peb_count); + if (err) + goto out_reserved_for_bad; + err = device_create_file(&ubi->dev, &dev_max_vol_count); + if (err) + goto out_bad_peb_count; + err = device_create_file(&ubi->dev, &dev_min_io_size); + if (err) + goto out_max_vol_count; + err = device_create_file(&ubi->dev, &dev_bgt_enabled); + if (err) + goto out_min_io_size; + + return 0; + +out_min_io_size: + device_remove_file(&ubi->dev, &dev_min_io_size); +out_max_vol_count: + device_remove_file(&ubi->dev, &dev_max_vol_count); +out_bad_peb_count: + device_remove_file(&ubi->dev, &dev_bad_peb_count); +out_reserved_for_bad: + device_remove_file(&ubi->dev, &dev_reserved_for_bad); +out_volumes_max_ec: + device_remove_file(&ubi->dev, &dev_max_ec); +out_volumes_count: + device_remove_file(&ubi->dev, &dev_volumes_count); +out_total_eraseblocks: + device_remove_file(&ubi->dev, &dev_total_eraseblocks); +out_avail_eraseblocks: + device_remove_file(&ubi->dev, &dev_avail_eraseblocks); +out_eraseblock_size: + device_remove_file(&ubi->dev, &dev_eraseblock_size); +out_unregister: + device_unregister(&ubi->dev); +out: + ubi_err("failed to initialize sysfs for %s", ubi->ubi_name); + return err; +} + +/** + * ubi_sysfs_close - close sysfs for an UBI device. + * @ubi: UBI device description object + */ +static void ubi_sysfs_close(struct ubi_device *ubi) +{ + device_remove_file(&ubi->dev, &dev_bgt_enabled); + device_remove_file(&ubi->dev, &dev_min_io_size); + device_remove_file(&ubi->dev, &dev_max_vol_count); + device_remove_file(&ubi->dev, &dev_bad_peb_count); + device_remove_file(&ubi->dev, &dev_reserved_for_bad); + device_remove_file(&ubi->dev, &dev_max_ec); + device_remove_file(&ubi->dev, &dev_volumes_count); + device_remove_file(&ubi->dev, &dev_total_eraseblocks); + device_remove_file(&ubi->dev, &dev_avail_eraseblocks); + device_remove_file(&ubi->dev, &dev_eraseblock_size); + device_unregister(&ubi->dev); +} + +/** + * kill_volumes - destroy all volumes. + * @ubi: UBI device description object + */ +static void kill_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) + ubi_free_volume(ubi, i); +} + +/** + * uif_init - initialize user interfaces for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int uif_init(struct ubi_device *ubi) +{ + int i, err; + dev_t dev; + + mutex_init(&ubi->vtbl_mutex); + spin_lock_init(&ubi->volumes_lock); + + sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + + /* + * Major numbers for the UBI character devices are allocated + * dynamically. Major numbers of volume character devices are + * equivalent to ones of the corresponding UBI character device. Minor + * numbers of UBI character devices are 0, while minor numbers of + * volume character devices start from 1. Thus, we allocate one major + * number and ubi->vtbl_slots + 1 minor numbers. + */ + err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name); + if (err) { + ubi_err("cannot register UBI character devices"); + return err; + } + + cdev_init(&ubi->cdev, &ubi_cdev_operations); + ubi->major = MAJOR(dev); + dbg_msg("%s major is %u", ubi->ubi_name, ubi->major); + ubi->cdev.owner = THIS_MODULE; + + dev = MKDEV(ubi->major, 0); + err = cdev_add(&ubi->cdev, dev, 1); + if (err) { + ubi_err("cannot add character device %s", ubi->ubi_name); + goto out_unreg; + } + + err = ubi_sysfs_init(ubi); + if (err) + goto out_cdev; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) { + err = ubi_add_volume(ubi, i); + if (err) + goto out_volumes; + } + + return 0; + +out_volumes: + kill_volumes(ubi); + ubi_sysfs_close(ubi); +out_cdev: + cdev_del(&ubi->cdev); +out_unreg: + unregister_chrdev_region(MKDEV(ubi->major, 0), + ubi->vtbl_slots + 1); + return err; +} + +/** + * uif_close - close user interfaces for an UBI device. + * @ubi: UBI device description object + */ +static void uif_close(struct ubi_device *ubi) +{ + kill_volumes(ubi); + ubi_sysfs_close(ubi); + cdev_del(&ubi->cdev); + unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1); +} + +/** + * attach_by_scanning - attach an MTD device using scanning method. + * @ubi: UBI device descriptor + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, currently this is the only method to attach UBI devices. Hopefully in + * the future we'll have more scalable attaching methods and avoid full media + * scanning. But even in this case scanning will be needed as a fall-back + * attaching method if there are some on-flash table corruptions. + */ +static int attach_by_scanning(struct ubi_device *ubi) +{ + int err; + struct ubi_scan_info *si; + + si = ubi_scan(ubi); + if (IS_ERR(si)) + return PTR_ERR(si); + + ubi->bad_peb_count = si->bad_peb_count; + ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count; + ubi->max_ec = si->max_ec; + ubi->mean_ec = si->mean_ec; + + err = ubi_read_volume_table(ubi, si); + if (err) + goto out_si; + + err = ubi_wl_init_scan(ubi, si); + if (err) + goto out_vtbl; + + err = ubi_eba_init_scan(ubi, si); + if (err) + goto out_wl; + + ubi_scan_destroy_si(si); + return 0; + +out_wl: + ubi_wl_close(ubi); +out_vtbl: + kfree(ubi->vtbl); +out_si: + ubi_scan_destroy_si(si); + return err; +} + +/** + * io_init - initialize I/O unit for a given UBI device. + * @ubi: UBI device description object + * + * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are + * assumed: + * o EC header is always at offset zero - this cannot be changed; + * o VID header starts just after the EC header at the closest address + * aligned to @io->@hdrs_min_io_size; + * o data starts just after the VID header at the closest address aligned to + * @io->@min_io_size + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int io_init(struct ubi_device *ubi) +{ + if (ubi->mtd->numeraseregions != 0) { + /* + * Some flashes have several erase regions. Different regions + * may have different eraseblock size and other + * characteristics. It looks like mostly multi-region flashes + * have one "main" region and one or more small regions to + * store boot loader code or boot parameters or whatever. I + * guess we should just pick the largest region. But this is + * not implemented. + */ + ubi_err("multiple regions, not implemented"); + return -EINVAL; + } + + /* + * Note, in this implementation we support MTD devices with 0x7FFFFFFF + * physical eraseblocks maximum. + */ + + ubi->peb_size = ubi->mtd->erasesize; + ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; + ubi->flash_size = ubi->mtd->size; + + if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) + ubi->bad_allowed = 1; + + ubi->min_io_size = ubi->mtd->writesize; + ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; + + /* Make sure minimal I/O unit is power of 2 */ + if (ubi->min_io_size == 0 || + (ubi->min_io_size & (ubi->min_io_size - 1))) { + ubi_err("bad min. I/O unit"); + return -EINVAL; + } + + ubi_assert(ubi->hdrs_min_io_size > 0); + ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size); + ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0); + + /* Calculate default aligned sizes of EC and VID headers */ + ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size); + ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size); + + dbg_msg("min_io_size %d", ubi->min_io_size); + dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size); + dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize); + dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize); + + if (ubi->vid_hdr_offset == 0) + /* Default offset */ + ubi->vid_hdr_offset = ubi->vid_hdr_aloffset = + ubi->ec_hdr_alsize; + else { + ubi->vid_hdr_aloffset = ubi->vid_hdr_offset & + ~(ubi->hdrs_min_io_size - 1); + ubi->vid_hdr_shift = ubi->vid_hdr_offset - + ubi->vid_hdr_aloffset; + } + + /* Similar for the data offset */ + if (ubi->leb_start == 0) { + ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize; + ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); + } + + dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); + dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); + dbg_msg("vid_hdr_shift %d", ubi->vid_hdr_shift); + dbg_msg("leb_start %d", ubi->leb_start); + + /* The shift must be aligned to 32-bit boundary */ + if (ubi->vid_hdr_shift % 4) { + ubi_err("unaligned VID header shift %d", + ubi->vid_hdr_shift); + return -EINVAL; + } + + /* Check sanity */ + if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE || + ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE || + ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE || + ubi->leb_start % ubi->min_io_size) { + ubi_err("bad VID header (%d) or data offsets (%d)", + ubi->vid_hdr_offset, ubi->leb_start); + return -EINVAL; + } + + /* + * It may happen that EC and VID headers are situated in one minimal + * I/O unit. In this case we can only accept this UBI image in + * read-only mode. + */ + if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) { + ubi_warn("EC and VID headers are in the same minimal I/O unit, " + "switch to read-only mode"); + ubi->ro_mode = 1; + } + + ubi->leb_size = ubi->peb_size - ubi->leb_start; + + if (!(ubi->mtd->flags & MTD_WRITEABLE)) { + ubi_msg("MTD device %d is write-protected, attach in " + "read-only mode", ubi->mtd->index); + ubi->ro_mode = 1; + } + + dbg_msg("leb_size %d", ubi->leb_size); + dbg_msg("ro_mode %d", ubi->ro_mode); + + /* + * Note, ideally, we have to initialize ubi->bad_peb_count here. But + * unfortunately, MTD does not provide this information. We should loop + * over all physical eraseblocks and invoke mtd->block_is_bad() for + * each physical eraseblock. So, we skip ubi->bad_peb_count + * uninitialized and initialize it after scanning. + */ + + return 0; +} + +/** + * attach_mtd_dev - attach an MTD device. + * @mtd_dev: MTD device name or number string + * @vid_hdr_offset: VID header offset + * @data_offset: data offset + * + * This function attaches an MTD device to UBI. It first treats @mtd_dev as the + * MTD device name, and tries to open it by this name. If it is unable to open, + * it tries to convert @mtd_dev to an integer and open the MTD device by its + * number. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, + int data_offset) +{ + struct ubi_device *ubi; + struct mtd_info *mtd; + int i, err; + + mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(mtd)) { + int mtd_num; + char *endp; + + if (PTR_ERR(mtd) != -ENODEV) + return PTR_ERR(mtd); + + /* + * Probably this is not MTD device name but MTD device number - + * check this out. + */ + mtd_num = simple_strtoul(mtd_dev, &endp, 0); + if (*endp != '\0' || mtd_dev == endp) { + ubi_err("incorrect MTD device: \"%s\"", mtd_dev); + return -ENODEV; + } + + mtd = get_mtd_device(NULL, mtd_num); + if (IS_ERR(mtd)) + return PTR_ERR(mtd); + } + + /* Check if we already have the same MTD device attached */ + for (i = 0; i < ubi_devices_cnt; i++) + if (ubi_devices[i]->mtd->index == mtd->index) { + ubi_err("mtd%d is already attached to ubi%d", + mtd->index, i); + err = -EINVAL; + goto out_mtd; + } + + ubi = ubi_devices[ubi_devices_cnt] = kzalloc(sizeof(struct ubi_device), + GFP_KERNEL); + if (!ubi) { + err = -ENOMEM; + goto out_mtd; + } + + ubi->ubi_num = ubi_devices_cnt; + ubi->mtd = mtd; + + dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", + ubi->mtd->index, ubi_devices_cnt, vid_hdr_offset, data_offset); + + ubi->vid_hdr_offset = vid_hdr_offset; + ubi->leb_start = data_offset; + err = io_init(ubi); + if (err) + goto out_free; + + err = attach_by_scanning(ubi); + if (err) { + dbg_err("failed to attach by scanning, error %d", err); + goto out_free; + } + + err = uif_init(ubi); + if (err) + goto out_detach; + + ubi_devices_cnt += 1; + + ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt); + ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("number of good PEBs: %d", ubi->good_peb_count); + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); + ubi_msg("number of user volumes: %d", + ubi->vol_count - UBI_INT_VOL_COUNT); + ubi_msg("available PEBs: %d", ubi->avail_pebs); + ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs); + ubi_msg("number of PEBs reserved for bad PEB handling: %d", + ubi->beb_rsvd_pebs); + ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); + + /* Enable the background thread */ + if (!DBG_DISABLE_BGT) { + ubi->thread_enabled = 1; + wake_up_process(ubi->bgt_thread); + } + + return 0; + +out_detach: + ubi_eba_close(ubi); + ubi_wl_close(ubi); + kfree(ubi->vtbl); +out_free: + kfree(ubi); +out_mtd: + put_mtd_device(mtd); + ubi_devices[ubi_devices_cnt] = NULL; + return err; +} + +/** + * detach_mtd_dev - detach an MTD device. + * @ubi: UBI device description object + */ +static void detach_mtd_dev(struct ubi_device *ubi) +{ + int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; + + dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); + uif_close(ubi); + ubi_eba_close(ubi); + ubi_wl_close(ubi); + kfree(ubi->vtbl); + put_mtd_device(ubi->mtd); + kfree(ubi_devices[ubi_num]); + ubi_devices[ubi_num] = NULL; + ubi_devices_cnt -= 1; + ubi_assert(ubi_devices_cnt >= 0); + ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num); +} + +static int __init ubi_init(void) +{ + int err, i, k; + + /* Ensure that EC and VID headers have correct size */ + BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); + BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); + + if (mtd_devs > UBI_MAX_DEVICES) { + printk("UBI error: too many MTD devices, maximum is %d\n", + UBI_MAX_DEVICES); + return -EINVAL; + } + + ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); + if (IS_ERR(ubi_class)) + return PTR_ERR(ubi_class); + + err = class_create_file(ubi_class, &ubi_version); + if (err) + goto out_class; + + /* Attach MTD devices */ + for (i = 0; i < mtd_devs; i++) { + struct mtd_dev_param *p = &mtd_dev_param[i]; + + cond_resched(); + + if (!p->name) { + dbg_err("empty name"); + err = -EINVAL; + goto out_detach; + } + + err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs); + if (err) + goto out_detach; + } + + return 0; + +out_detach: + for (k = 0; k < i; k++) + detach_mtd_dev(ubi_devices[k]); + class_remove_file(ubi_class, &ubi_version); +out_class: + class_destroy(ubi_class); + return err; +} +module_init(ubi_init); + +static void __exit ubi_exit(void) +{ + int i, n = ubi_devices_cnt; + + for (i = 0; i < n; i++) + detach_mtd_dev(ubi_devices[i]); + class_remove_file(ubi_class, &ubi_version); + class_destroy(ubi_class); +} +module_exit(ubi_exit); + +/** + * bytes_str_to_int - convert a string representing number of bytes to an + * integer. + * @str: the string to convert + * + * This function returns positive resulting integer in case of success and a + * negative error code in case of failure. + */ +static int __init bytes_str_to_int(const char *str) +{ + char *endp; + unsigned long result; + + result = simple_strtoul(str, &endp, 0); + if (str == endp || result < 0) { + printk("UBI error: incorrect bytes count: \"%s\"\n", str); + return -EINVAL; + } + + switch (*endp) { + case 'G': + result *= 1024; + case 'M': + result *= 1024; + case 'K': + case 'k': + result *= 1024; + if (endp[1] == 'i' && (endp[2] == '\0' || + endp[2] == 'B' || endp[2] == 'b')) + endp += 2; + case '\0': + break; + default: + printk("UBI error: incorrect bytes count: \"%s\"\n", str); + return -EINVAL; + } + + return result; +} + +/** + * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter. + * @val: the parameter value to parse + * @kp: not used + * + * This function returns zero in case of success and a negative error code in + * case of error. + */ +static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +{ + int i, len; + struct mtd_dev_param *p; + char buf[MTD_PARAM_LEN_MAX]; + char *pbuf = &buf[0]; + char *tokens[3] = {NULL, NULL, NULL}; + + if (mtd_devs == UBI_MAX_DEVICES) { + printk("UBI error: too many parameters, max. is %d\n", + UBI_MAX_DEVICES); + return -EINVAL; + } + + len = strnlen(val, MTD_PARAM_LEN_MAX); + if (len == MTD_PARAM_LEN_MAX) { + printk("UBI error: parameter \"%s\" is too long, max. is %d\n", + val, MTD_PARAM_LEN_MAX); + return -EINVAL; + } + + if (len == 0) { + printk("UBI warning: empty 'mtd=' parameter - ignored\n"); + return 0; + } + + strcpy(buf, val); + + /* Get rid of the final newline */ + if (buf[len - 1] == '\n') + buf[len - 1] = 0; + + for (i = 0; i < 3; i++) + tokens[i] = strsep(&pbuf, ","); + + if (pbuf) { + printk("UBI error: too many arguments at \"%s\"\n", val); + return -EINVAL; + } + + if (tokens[0] == '\0') + return -EINVAL; + + p = &mtd_dev_param[mtd_devs]; + strcpy(&p->name[0], tokens[0]); + + if (tokens[1]) + p->vid_hdr_offs = bytes_str_to_int(tokens[1]); + if (tokens[2]) + p->data_offs = bytes_str_to_int(tokens[2]); + + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; + if (p->data_offs < 0) + return p->data_offs; + + mtd_devs += 1; + return 0; +} + +module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); +MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " + "mtd=[,,]. " + "Multiple \"mtd\" parameters may be specified.\n" + "MTD devices may be specified by their number or name. " + "Optional \"vid_hdr_offs\" and \"data_offs\" parameters " + "specify UBI VID header position and data starting " + "position to be used by UBI.\n" + "Example: mtd=content,1984,2048 mtd=4 - attach MTD device" + "with name content using VID header offset 1984 and data " + "start 2048, and MTD device number 4 using default " + "offsets"); + +MODULE_VERSION(__stringify(UBI_VERSION)); +MODULE_DESCRIPTION("UBI - Unsorted Block Images"); +MODULE_AUTHOR("Artem Bityutskiy"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c new file mode 100644 index 0000000..6612eb7 --- /dev/null +++ b/drivers/mtd/ubi/cdev.c @@ -0,0 +1,722 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * This file includes implementation of UBI character device operations. + * + * There are two kinds of character devices in UBI: UBI character devices and + * UBI volume character devices. UBI character devices allow users to + * manipulate whole volumes: create, remove, and re-size them. Volume character + * devices provide volume I/O capabilities. + * + * Major and minor numbers are assigned dynamically to both UBI and volume + * character devices. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" + +/* + * Maximum sequence numbers of UBI and volume character device IOCTLs (direct + * logical eraseblock erase is a debug-only feature). + */ +#define UBI_CDEV_IOC_MAX_SEQ 2 +#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO +#define VOL_CDEV_IOC_MAX_SEQ 1 +#else +#define VOL_CDEV_IOC_MAX_SEQ 2 +#endif + +/** + * major_to_device - get UBI device object by character device major number. + * @major: major number + * + * This function returns a pointer to the UBI device object. + */ +static struct ubi_device *major_to_device(int major) +{ + int i; + + for (i = 0; i < ubi_devices_cnt; i++) + if (ubi_devices[i] && ubi_devices[i]->major == major) + return ubi_devices[i]; + BUG(); +} + +/** + * get_exclusive - get exclusive access to an UBI volume. + * @desc: volume descriptor + * + * This function changes UBI volume open mode to "exclusive". Returns previous + * mode value (positive integer) in case of success and a negative error code + * in case of failure. + */ +static int get_exclusive(struct ubi_volume_desc *desc) +{ + int users, err; + struct ubi_volume *vol = desc->vol; + + spin_lock(&vol->ubi->volumes_lock); + users = vol->readers + vol->writers + vol->exclusive; + ubi_assert(users > 0); + if (users > 1) { + dbg_err("%d users for volume %d", users, vol->vol_id); + err = -EBUSY; + } else { + vol->readers = vol->writers = 0; + vol->exclusive = 1; + err = desc->mode; + desc->mode = UBI_EXCLUSIVE; + } + spin_unlock(&vol->ubi->volumes_lock); + + return err; +} + +/** + * revoke_exclusive - revoke exclusive mode. + * @desc: volume descriptor + * @mode: new mode to switch to + */ +static void revoke_exclusive(struct ubi_volume_desc *desc, int mode) +{ + struct ubi_volume *vol = desc->vol; + + spin_lock(&vol->ubi->volumes_lock); + ubi_assert(vol->readers == 0 && vol->writers == 0); + ubi_assert(vol->exclusive == 1 && desc->mode == UBI_EXCLUSIVE); + vol->exclusive = 0; + if (mode == UBI_READONLY) + vol->readers = 1; + else if (mode == UBI_READWRITE) + vol->writers = 1; + else + vol->exclusive = 1; + spin_unlock(&vol->ubi->volumes_lock); + + desc->mode = mode; +} + +static int vol_cdev_open(struct inode *inode, struct file *file) +{ + struct ubi_volume_desc *desc; + const struct ubi_device *ubi = major_to_device(imajor(inode)); + int vol_id = iminor(inode) - 1; + int mode; + + if (file->f_mode & FMODE_WRITE) + mode = UBI_READWRITE; + else + mode = UBI_READONLY; + + dbg_msg("open volume %d, mode %d", vol_id, mode); + + desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + file->private_data = desc; + return 0; +} + +static int vol_cdev_release(struct inode *inode, struct file *file) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + + dbg_msg("release volume %d, mode %d", vol->vol_id, desc->mode); + + if (vol->updating) { + ubi_warn("update of volume %d not finished, volume is damaged", + vol->vol_id); + vol->updating = 0; + kfree(vol->upd_buf); + } + + ubi_close_volume(desc); + return 0; +} + +static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + loff_t new_offset; + + if (vol->updating) { + /* Update is in progress, seeking is prohibited */ + dbg_err("updating"); + return -EBUSY; + } + + switch (origin) { + case 0: /* SEEK_SET */ + new_offset = offset; + break; + case 1: /* SEEK_CUR */ + new_offset = file->f_pos + offset; + break; + case 2: /* SEEK_END */ + new_offset = vol->used_bytes + offset; + break; + default: + return -EINVAL; + } + + if (new_offset < 0 || new_offset > vol->used_bytes) { + dbg_err("bad seek %lld", new_offset); + return -EINVAL; + } + + dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld", + vol->vol_id, offset, origin, new_offset); + + file->f_pos = new_offset; + return new_offset; +} + +static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, + loff_t *offp) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size; + size_t count_save = count; + void *tbuf; + uint64_t tmp; + + dbg_msg("read %zd bytes from offset %lld of volume %d", + count, *offp, vol_id); + + if (vol->updating) { + dbg_err("updating"); + return -EBUSY; + } + if (vol->upd_marker) { + dbg_err("damaged volume, update marker is set"); + return -EBADF; + } + if (*offp == vol->used_bytes || count == 0) + return 0; + + if (vol->corrupted) + dbg_msg("read from corrupted volume %d", vol_id); + + if (*offp + count > vol->used_bytes) + count_save = count = vol->used_bytes - *offp; + + tbuf_size = vol->usable_leb_size; + if (count < tbuf_size) + tbuf_size = ALIGN(count, ubi->min_io_size); + tbuf = kmalloc(tbuf_size, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + len = count > tbuf_size ? tbuf_size : count; + + tmp = *offp; + off = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + + do { + cond_resched(); + + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + + err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0); + if (err) + break; + + off += len; + if (off == vol->usable_leb_size) { + lnum += 1; + off -= vol->usable_leb_size; + } + + count -= len; + *offp += len; + + err = copy_to_user(buf, tbuf, len); + if (err) { + err = -EFAULT; + break; + } + + buf += len; + len = count > tbuf_size ? tbuf_size : count; + } while (count); + + kfree(tbuf); + return err ? err : count_save - count; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO + +/* + * This function allows to directly write to dynamic UBI volumes, without + * issuing the volume update operation. Available only as a debugging feature. + * Very useful for testing UBI. + */ +static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0; + size_t count_save = count; + char *tbuf; + uint64_t tmp; + + dbg_msg("requested: write %zd bytes to offset %lld of volume %u", + count, *offp, desc->vol->vol_id); + + if (vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + tmp = *offp; + off = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + + if (off % ubi->min_io_size) { + dbg_err("unaligned position"); + return -EINVAL; + } + + if (*offp + count > vol->used_bytes) + count_save = count = vol->used_bytes - *offp; + + /* We can write only in fractions of the minimum I/O unit */ + if (count % ubi->min_io_size) { + dbg_err("unaligned write length"); + return -EINVAL; + } + + tbuf_size = vol->usable_leb_size; + if (count < tbuf_size) + tbuf_size = ALIGN(count, ubi->min_io_size); + tbuf = kmalloc(tbuf_size, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + len = count > tbuf_size ? tbuf_size : count; + + while (count) { + cond_resched(); + + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + + err = copy_from_user(tbuf, buf, len); + if (err) { + err = -EFAULT; + break; + } + + err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len, + UBI_UNKNOWN); + if (err) + break; + + off += len; + if (off == vol->usable_leb_size) { + lnum += 1; + off -= vol->usable_leb_size; + } + + count -= len; + *offp += len; + buf += len; + len = count > tbuf_size ? tbuf_size : count; + } + + kfree(tbuf); + return err ? err : count_save - count; +} + +#else +#define vol_cdev_direct_write(file, buf, count, offp) -EPERM +#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */ + +static ssize_t vol_cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp) +{ + int err = 0; + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + if (!vol->updating) + return vol_cdev_direct_write(file, buf, count, offp); + + err = ubi_more_update_data(ubi, vol->vol_id, buf, count); + if (err < 0) { + ubi_err("cannot write %zd bytes of update data", count); + return err; + } + + if (err) { + /* + * Update is finished, @err contains number of actually written + * bytes now. + */ + count = err; + + err = ubi_check_volume(ubi, vol->vol_id); + if (err < 0) + return err; + + if (err) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol->vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + vol->checked = 1; + revoke_exclusive(desc, UBI_READWRITE); + } + + *offp += count; + return count; +} + +static int vol_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + void __user *argp = (void __user *)arg; + + if (_IOC_NR(cmd) > VOL_CDEV_IOC_MAX_SEQ || + _IOC_TYPE(cmd) != UBI_VOL_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_DIR(cmd) && _IOC_READ) + err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) && _IOC_WRITE) + err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch (cmd) { + + /* Volume update command */ + case UBI_IOCVOLUP: + { + int64_t bytes, rsvd_bytes; + + if (!capable(CAP_SYS_RESOURCE)) { + err = -EPERM; + break; + } + + err = copy_from_user(&bytes, argp, sizeof(int64_t)); + if (err) { + err = -EFAULT; + break; + } + + if (desc->mode == UBI_READONLY) { + err = -EROFS; + break; + } + + rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad); + if (bytes < 0 || bytes > rsvd_bytes) { + err = -EINVAL; + break; + } + + err = get_exclusive(desc); + if (err < 0) + break; + + err = ubi_start_update(ubi, vol->vol_id, bytes); + if (bytes == 0) + revoke_exclusive(desc, UBI_READWRITE); + + file->f_pos = 0; + break; + } + +#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO + /* Logical eraseblock erasure command */ + case UBI_IOCEBER: + { + int32_t lnum; + + err = __get_user(lnum, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + if (desc->mode == UBI_READONLY) { + err = -EROFS; + break; + } + + if (lnum < 0 || lnum >= vol->reserved_pebs) { + err = -EINVAL; + break; + } + + if (vol->vol_type != UBI_DYNAMIC_VOLUME) { + err = -EROFS; + break; + } + + dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum); + if (err) + break; + + err = ubi_wl_flush(ubi); + break; + } +#endif + + default: + err = -ENOTTY; + break; + } + + return err; +} + +/** + * verify_mkvol_req - verify volume creation request. + * @ubi: UBI device description object + * @req: the request to check + * + * This function zero if the request is correct, and %-EINVAL if not. + */ +static int verify_mkvol_req(const struct ubi_device *ubi, + const struct ubi_mkvol_req *req) +{ + int n, err = -EINVAL; + + if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 || + req->name_len < 0) + goto bad; + + if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) && + req->vol_id != UBI_VOL_NUM_AUTO) + goto bad; + + if (req->alignment == 0) + goto bad; + + if (req->bytes == 0) + goto bad; + + if (req->vol_type != UBI_DYNAMIC_VOLUME && + req->vol_type != UBI_STATIC_VOLUME) + goto bad; + + if (req->alignment > ubi->leb_size) + goto bad; + + n = req->alignment % ubi->min_io_size; + if (req->alignment != 1 && n) + goto bad; + + if (req->name_len > UBI_VOL_NAME_MAX) { + err = -ENAMETOOLONG; + goto bad; + } + + return 0; + +bad: + dbg_err("bad volume creation request"); + ubi_dbg_dump_mkvol_req(req); + return err; +} + +/** + * verify_rsvol_req - verify volume re-size request. + * @ubi: UBI device description object + * @req: the request to check + * + * This function returns zero if the request is correct, and %-EINVAL if not. + */ +static int verify_rsvol_req(const struct ubi_device *ubi, + const struct ubi_rsvol_req *req) +{ + if (req->bytes <= 0) + return -EINVAL; + + if (req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) + return -EINVAL; + + return 0; +} + +static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ubi_device *ubi; + struct ubi_volume_desc *desc; + void __user *argp = (void __user *)arg; + + if (_IOC_NR(cmd) > UBI_CDEV_IOC_MAX_SEQ || + _IOC_TYPE(cmd) != UBI_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_DIR(cmd) && _IOC_READ) + err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) && _IOC_WRITE) + err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + if (!capable(CAP_SYS_RESOURCE)) + return -EPERM; + + ubi = major_to_device(imajor(inode)); + if (IS_ERR(ubi)) + return PTR_ERR(ubi); + + switch (cmd) { + /* Create volume command */ + case UBI_IOCMKVOL: + { + struct ubi_mkvol_req req; + + dbg_msg("create volume"); + err = __copy_from_user(&req, argp, + sizeof(struct ubi_mkvol_req)); + if (err) { + err = -EFAULT; + break; + } + + err = verify_mkvol_req(ubi, &req); + if (err) + break; + + req.name[req.name_len] = '\0'; + + err = ubi_create_volume(ubi, &req); + if (err) + break; + + err = __put_user(req.vol_id, (__user int32_t *)argp); + if (err) + err = -EFAULT; + + break; + } + + /* Remove volume command */ + case UBI_IOCRMVOL: + { + int vol_id; + + dbg_msg("remove volume"); + err = __get_user(vol_id, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE); + if (IS_ERR(desc)) { + err = PTR_ERR(desc); + break; + } + + err = ubi_remove_volume(desc); + if (err) + ubi_close_volume(desc); + + break; + } + + /* Re-size volume command */ + case UBI_IOCRSVOL: + { + int pebs; + uint64_t tmp; + struct ubi_rsvol_req req; + + dbg_msg("re-size volume"); + err = __copy_from_user(&req, argp, + sizeof(struct ubi_rsvol_req)); + if (err) { + err = -EFAULT; + break; + } + + err = verify_rsvol_req(ubi, &req); + if (err) + break; + + desc = ubi_open_volume(ubi->ubi_num, req.vol_id, UBI_EXCLUSIVE); + if (IS_ERR(desc)) { + err = PTR_ERR(desc); + break; + } + + tmp = req.bytes; + pebs = !!do_div(tmp, desc->vol->usable_leb_size); + pebs += tmp; + + err = ubi_resize_volume(desc, pebs); + ubi_close_volume(desc); + break; + } + + default: + err = -ENOTTY; + break; + } + + return err; +} + +/* UBI character device operations */ +struct file_operations ubi_cdev_operations = { + .owner = THIS_MODULE, + .ioctl = ubi_cdev_ioctl, + .llseek = no_llseek +}; + +/* UBI volume character device operations */ +struct file_operations ubi_vol_cdev_operations = { + .owner = THIS_MODULE, + .open = vol_cdev_open, + .release = vol_cdev_release, + .llseek = vol_cdev_llseek, + .read = vol_cdev_read, + .write = vol_cdev_write, + .ioctl = vol_cdev_ioctl +}; diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c new file mode 100644 index 0000000..8636422 --- /dev/null +++ b/drivers/mtd/ubi/debug.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * Here we keep all the UBI debugging stuff which should normally be disabled + * and compiled-out, but it is extremely helpful when hunting bugs or doing big + * changes. + */ + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG + +#include "ubi.h" + +/** + * ubi_dbg_dump_ec_hdr - dump an erase counter header. + * @ec_hdr: the erase counter header to dump + */ +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) +{ + dbg_msg("erase counter header dump:"); + dbg_msg("magic %#08x", ubi32_to_cpu(ec_hdr->magic)); + dbg_msg("version %d", (int)ec_hdr->version); + dbg_msg("ec %llu", (long long)ubi64_to_cpu(ec_hdr->ec)); + dbg_msg("vid_hdr_offset %d", ubi32_to_cpu(ec_hdr->vid_hdr_offset)); + dbg_msg("data_offset %d", ubi32_to_cpu(ec_hdr->data_offset)); + dbg_msg("hdr_crc %#08x", ubi32_to_cpu(ec_hdr->hdr_crc)); + dbg_msg("erase counter header hexdump:"); + ubi_dbg_hexdump(ec_hdr, UBI_EC_HDR_SIZE); +} + +/** + * ubi_dbg_dump_vid_hdr - dump a volume identifier header. + * @vid_hdr: the volume identifier header to dump + */ +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) +{ + dbg_msg("volume identifier header dump:"); + dbg_msg("magic %08x", ubi32_to_cpu(vid_hdr->magic)); + dbg_msg("version %d", (int)vid_hdr->version); + dbg_msg("vol_type %d", (int)vid_hdr->vol_type); + dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag); + dbg_msg("compat %d", (int)vid_hdr->compat); + dbg_msg("vol_id %d", ubi32_to_cpu(vid_hdr->vol_id)); + dbg_msg("lnum %d", ubi32_to_cpu(vid_hdr->lnum)); + dbg_msg("leb_ver %u", ubi32_to_cpu(vid_hdr->leb_ver)); + dbg_msg("data_size %d", ubi32_to_cpu(vid_hdr->data_size)); + dbg_msg("used_ebs %d", ubi32_to_cpu(vid_hdr->used_ebs)); + dbg_msg("data_pad %d", ubi32_to_cpu(vid_hdr->data_pad)); + dbg_msg("sqnum %llu", + (unsigned long long)ubi64_to_cpu(vid_hdr->sqnum)); + dbg_msg("hdr_crc %08x", ubi32_to_cpu(vid_hdr->hdr_crc)); + dbg_msg("volume identifier header hexdump:"); +} + +/** + * ubi_dbg_dump_vol_info- dump volume information. + * @vol: UBI volume description object + */ +void ubi_dbg_dump_vol_info(const struct ubi_volume *vol) +{ + dbg_msg("volume information dump:"); + dbg_msg("vol_id %d", vol->vol_id); + dbg_msg("reserved_pebs %d", vol->reserved_pebs); + dbg_msg("alignment %d", vol->alignment); + dbg_msg("data_pad %d", vol->data_pad); + dbg_msg("vol_type %d", vol->vol_type); + dbg_msg("name_len %d", vol->name_len); + dbg_msg("usable_leb_size %d", vol->usable_leb_size); + dbg_msg("used_ebs %d", vol->used_ebs); + dbg_msg("used_bytes %lld", vol->used_bytes); + dbg_msg("last_eb_bytes %d", vol->last_eb_bytes); + dbg_msg("corrupted %d", vol->corrupted); + dbg_msg("upd_marker %d", vol->upd_marker); + + if (vol->name_len <= UBI_VOL_NAME_MAX && + strnlen(vol->name, vol->name_len + 1) == vol->name_len) { + dbg_msg("name %s", vol->name); + } else { + dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c", + vol->name[0], vol->name[1], vol->name[2], + vol->name[3], vol->name[4]); + } +} + +/** + * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object. + * @r: the object to dump + * @idx: volume table index + */ +void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) +{ + int name_len = ubi16_to_cpu(r->name_len); + + dbg_msg("volume table record %d dump:", idx); + dbg_msg("reserved_pebs %d", ubi32_to_cpu(r->reserved_pebs)); + dbg_msg("alignment %d", ubi32_to_cpu(r->alignment)); + dbg_msg("data_pad %d", ubi32_to_cpu(r->data_pad)); + dbg_msg("vol_type %d", (int)r->vol_type); + dbg_msg("upd_marker %d", (int)r->upd_marker); + dbg_msg("name_len %d", name_len); + + if (r->name[0] == '\0') { + dbg_msg("name NULL"); + return; + } + + if (name_len <= UBI_VOL_NAME_MAX && + strnlen(&r->name[0], name_len + 1) == name_len) { + dbg_msg("name %s", &r->name[0]); + } else { + dbg_msg("1st 5 characters of the name: %c%c%c%c%c", + r->name[0], r->name[1], r->name[2], r->name[3], + r->name[4]); + } + dbg_msg("crc %#08x", ubi32_to_cpu(r->crc)); +} + +/** + * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object. + * @sv: the object to dump + */ +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) +{ + dbg_msg("volume scanning information dump:"); + dbg_msg("vol_id %d", sv->vol_id); + dbg_msg("highest_lnum %d", sv->highest_lnum); + dbg_msg("leb_count %d", sv->leb_count); + dbg_msg("compat %d", sv->compat); + dbg_msg("vol_type %d", sv->vol_type); + dbg_msg("used_ebs %d", sv->used_ebs); + dbg_msg("last_data_size %d", sv->last_data_size); + dbg_msg("data_pad %d", sv->data_pad); +} + +/** + * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object. + * @seb: the object to dump + * @type: object type: 0 - not corrupted, 1 - corrupted + */ +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type) +{ + dbg_msg("eraseblock scanning information dump:"); + dbg_msg("ec %d", seb->ec); + dbg_msg("pnum %d", seb->pnum); + if (type == 0) { + dbg_msg("lnum %d", seb->lnum); + dbg_msg("scrub %d", seb->scrub); + dbg_msg("sqnum %llu", seb->sqnum); + dbg_msg("leb_ver %u", seb->leb_ver); + } +} + +/** + * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object. + * @req: the object to dump + */ +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req) +{ + char nm[17]; + + dbg_msg("volume creation request dump:"); + dbg_msg("vol_id %d", req->vol_id); + dbg_msg("alignment %d", req->alignment); + dbg_msg("bytes %lld", (long long)req->bytes); + dbg_msg("vol_type %d", req->vol_type); + dbg_msg("name_len %d", req->name_len); + + memcpy(nm, req->name, 16); + nm[16] = 0; + dbg_msg("the 1st 16 characters of the name: %s", nm); +} + +#define BYTES_PER_LINE 32 + +/** + * ubi_dbg_hexdump - dump a buffer. + * @ptr: the buffer to dump + * @size: buffer size which must be multiple of 4 bytes + */ +void ubi_dbg_hexdump(const void *ptr, int size) +{ + int i, k = 0, rows, columns; + const uint8_t *p = ptr; + + size = ALIGN(size, 4); + rows = size/BYTES_PER_LINE + size % BYTES_PER_LINE; + for (i = 0; i < rows; i++) { + int j; + + cond_resched(); + columns = min(size - k, BYTES_PER_LINE) / 4; + if (columns == 0) + break; + printk(KERN_DEBUG "%5d: ", i * BYTES_PER_LINE); + for (j = 0; j < columns; j++) { + int n, N; + + N = size - k > 4 ? 4 : size - k; + for (n = 0; n < N; n++) + printk("%02x", p[k++]); + printk(" "); + } + printk("\n"); + } +} + +#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h new file mode 100644 index 0000000..f816ad9 --- /dev/null +++ b/drivers/mtd/ubi/debug.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_DEBUG_H__ +#define __UBI_DEBUG_H__ + +#ifdef CONFIG_MTD_UBI_DEBUG +#include + +#define ubi_assert(expr) BUG_ON(!(expr)) +#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__) +#else +#define ubi_assert(expr) ({}) +#define dbg_err(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT +#define DBG_DISABLE_BGT 1 +#else +#define DBG_DISABLE_BGT 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG +/* Generic debugging message */ +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) + +#define ubi_dbg_dump_stack() dump_stack() + +struct ubi_ec_hdr; +struct ubi_vid_hdr; +struct ubi_volume; +struct ubi_vtbl_record; +struct ubi_scan_volume; +struct ubi_scan_leb; +struct ubi_mkvol_req; + +void ubi_dbg_print(int type, const char *func, const char *fmt, ...); +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr); +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr); +void ubi_dbg_dump_vol_info(const struct ubi_volume *vol); +void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx); +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv); +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type); +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); +void ubi_dbg_hexdump(const void *buf, int size); + +#else + +#define dbg_msg(fmt, ...) ({}) +#define ubi_dbg_dump_stack() ({}) +#define ubi_dbg_print(func, fmt, ...) ({}) +#define ubi_dbg_dump_ec_hdr(ec_hdr) ({}) +#define ubi_dbg_dump_vid_hdr(vid_hdr) ({}) +#define ubi_dbg_dump_vol_info(vol) ({}) +#define ubi_dbg_dump_vtbl_record(r, idx) ({}) +#define ubi_dbg_dump_sv(sv) ({}) +#define ubi_dbg_dump_seb(seb, type) ({}) +#define ubi_dbg_dump_mkvol_req(req) ({}) +#define ubi_dbg_hexdump(buf, size) ({}) + +#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA +/* Messages from the eraseblock association unit */ +#define dbg_eba(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG eba: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_eba(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL +/* Messages from the wear-leveling unit */ +#define dbg_wl(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG wl: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_wl(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO +/* Messages from the input/output unit */ +#define dbg_io(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG io: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_io(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD +/* Initialization and build messages */ +#define dbg_bld(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG bld: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_bld(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS +/** + * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. + * + * Returns non-zero if a bit-flip should be emulated, otherwise returns zero. + */ +static inline int ubi_dbg_is_bitflip(void) +{ + return !(random32() % 200); +} +#else +#define ubi_dbg_is_bitflip() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES +/** + * ubi_dbg_is_write_failure - if it is time to emulate a write failure. + * + * Returns non-zero if a write failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_write_failure(void) +{ + return !(random32() % 500); +} +#else +#define ubi_dbg_is_write_failure() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES +/** + * ubi_dbg_is_erase_failure - if its time to emulate an erase failure. + * + * Returns non-zero if an erase failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_erase_failure(void) +{ + return !(random32() % 400); +} +#else +#define ubi_dbg_is_erase_failure() 0 +#endif + +#endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c new file mode 100644 index 0000000..d847ee1 --- /dev/null +++ b/drivers/mtd/ubi/eba.c @@ -0,0 +1,1241 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * The UBI Eraseblock Association (EBA) unit. + * + * This unit is responsible for I/O to/from logical eraseblock. + * + * Although in this implementation the EBA table is fully kept and managed in + * RAM, which assumes poor scalability, it might be (partially) maintained on + * flash in future implementations. + * + * The EBA unit implements per-logical eraseblock locking. Before accessing a + * logical eraseblock it is locked for reading or writing. The per-logical + * eraseblock locking is implemented by means of the lock tree. The lock tree + * is an RB-tree which refers all the currently locked logical eraseblocks. The + * lock tree elements are &struct ltree_entry objects. They are indexed by + * (@vol_id, @lnum) pairs. + * + * EBA also maintains the global sequence counter which is incremented each + * time a logical eraseblock is mapped to a physical eraseblock and it is + * stored in the volume identifier header. This means that each VID header has + * a unique sequence number. The sequence number is only increased an we assume + * 64 bits is enough to never overflow. + */ + +#include +#include +#include +#include "ubi.h" + +/** + * struct ltree_entry - an entry in the lock tree. + * @rb: links RB-tree nodes + * @vol_id: volume ID of the locked logical eraseblock + * @lnum: locked logical eraseblock number + * @users: how many tasks are using this logical eraseblock or wait for it + * @mutex: read/write mutex to implement read/write access serialization to + * the (@vol_id, @lnum) logical eraseblock + * + * When a logical eraseblock is being locked - corresponding &struct ltree_entry + * object is inserted to the lock tree (@ubi->ltree). + */ +struct ltree_entry { + struct rb_node rb; + int vol_id; + int lnum; + int users; + struct rw_semaphore mutex; +}; + +/* Slab cache for lock-tree entries */ +static struct kmem_cache *ltree_slab; + +/** + * next_sqnum - get next sequence number. + * @ubi: UBI device description object + * + * This function returns next sequence number to use, which is just the current + * global sequence counter value. It also increases the global sequence + * counter. + */ +static unsigned long long next_sqnum(struct ubi_device *ubi) +{ + unsigned long long sqnum; + + spin_lock(&ubi->ltree_lock); + sqnum = ubi->global_sqnum++; + spin_unlock(&ubi->ltree_lock); + + return sqnum; +} + +/** + * ubi_get_compat - get compatibility flags of a volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function returns compatibility flags for an internal volume. User + * volumes have no compatibility flags, so %0 is returned. + */ +static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id == UBI_LAYOUT_VOL_ID) + return UBI_LAYOUT_VOLUME_COMPAT; + return 0; +} + +/** + * ltree_lookup - look up the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function returns a pointer to the corresponding &struct ltree_entry + * object if the logical eraseblock is locked and %NULL if it is not. + * @ubi->ltree_lock has to be locked. + */ +static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, + int lnum) +{ + struct rb_node *p; + + p = ubi->ltree.rb_node; + while (p) { + struct ltree_entry *le; + + le = rb_entry(p, struct ltree_entry, rb); + + if (vol_id < le->vol_id) + p = p->rb_left; + else if (vol_id > le->vol_id) + p = p->rb_right; + else { + if (lnum < le->lnum) + p = p->rb_left; + else if (lnum > le->lnum) + p = p->rb_right; + else + return le; + } + } + + return NULL; +} + +/** + * ltree_add_entry - add new entry to the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the + * lock tree. If such entry is already there, its usage counter is increased. + * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation + * failed. + */ +static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, + int lnum) +{ + struct ltree_entry *le, *le1, *le_free; + + le = kmem_cache_alloc(ltree_slab, GFP_KERNEL); + if (!le) + return ERR_PTR(-ENOMEM); + + le->vol_id = vol_id; + le->lnum = lnum; + + spin_lock(&ubi->ltree_lock); + le1 = ltree_lookup(ubi, vol_id, lnum); + + if (le1) { + /* + * This logical eraseblock is already locked. The newly + * allocated lock entry is not needed. + */ + le_free = le; + le = le1; + } else { + struct rb_node **p, *parent = NULL; + + /* + * No lock entry, add the newly allocated one to the + * @ubi->ltree RB-tree. + */ + le_free = NULL; + + p = &ubi->ltree.rb_node; + while (*p) { + parent = *p; + le1 = rb_entry(parent, struct ltree_entry, rb); + + if (vol_id < le1->vol_id) + p = &(*p)->rb_left; + else if (vol_id > le1->vol_id) + p = &(*p)->rb_right; + else { + ubi_assert(lnum != le1->lnum); + if (lnum < le1->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&le->rb, parent, p); + rb_insert_color(&le->rb, &ubi->ltree); + } + le->users += 1; + spin_unlock(&ubi->ltree_lock); + + if (le_free) + kmem_cache_free(ltree_slab, le_free); + + return le; +} + +/** + * leb_read_lock - lock logical eraseblock for reading. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for reading. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_read(&le->mutex); + return 0; +} + +/** + * leb_read_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int free = 0; + struct ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + free = 1; + } + spin_unlock(&ubi->ltree_lock); + + up_read(&le->mutex); + if (free) + kmem_cache_free(ltree_slab, le); +} + +/** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_write(&le->mutex); + return 0; +} + +/** + * leb_write_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int free; + struct ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + free = 1; + } else + free = 0; + spin_unlock(&ubi->ltree_lock); + + up_write(&le->mutex); + if (free) + kmem_cache_free(ltree_slab, le); +} + +/** + * ubi_eba_unmap_leb - un-map logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules corresponding + * physical eraseblock for erasure. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum) +{ + int idx = vol_id2idx(ubi, vol_id), err, pnum; + struct ubi_volume *vol = ubi->volumes[idx]; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) + /* This logical eraseblock is already unmapped */ + goto out_unlock; + + dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); + + vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; + err = ubi_wl_put_peb(ubi, pnum, 0); + +out_unlock: + leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * ubi_eba_read_leb - read data. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: buffer to store the read data + * @offset: offset from where to read + * @len: how many bytes to read + * @check: data CRC check flag + * + * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF + * bytes. The @check flag only makes sense for static volumes and forces + * eraseblock data CRC checking. + * + * In case of success this function returns zero. In case of a static volume, + * if data CRC mismatches - %-EBADMSG is returned. %-EBADMSG may also be + * returned for any volume type if an ECC error was detected by the MTD device + * driver. Other negative error cored may be returned in case of other errors. + */ +int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + int offset, int len, int check) +{ + int err, pnum, scrub = 0, idx = vol_id2idx(ubi, vol_id); + struct ubi_vid_hdr *vid_hdr; + struct ubi_volume *vol = ubi->volumes[idx]; + uint32_t crc, crc1; + + err = leb_read_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) { + /* + * The logical eraseblock is not mapped, fill the whole buffer + * with 0xFF bytes. The exception is static volumes for which + * it is an error to read unmapped logical eraseblocks. + */ + dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)", + len, offset, vol_id, lnum); + leb_read_unlock(ubi, vol_id, lnum); + ubi_assert(vol->vol_type != UBI_STATIC_VOLUME); + memset(buf, 0xFF, len); + return 0; + } + + dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + check = 0; + +retry: + if (check) { + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + err = -ENOMEM; + goto out_unlock; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) { + /* + * The header is either absent or corrupted. + * The former case means there is a bug - + * switch to read-only mode just in case. + * The latter case means a real corruption - we + * may try to recover data. FIXME: but this is + * not implemented. + */ + if (err == UBI_IO_BAD_VID_HDR) { + ubi_warn("bad VID header at PEB %d, LEB" + "%d:%d", pnum, vol_id, lnum); + err = -EBADMSG; + } else + ubi_ro_mode(ubi); + } + goto out_free; + } else if (err == UBI_IO_BITFLIPS) + scrub = 1; + + ubi_assert(lnum < ubi32_to_cpu(vid_hdr->used_ebs)); + ubi_assert(len == ubi32_to_cpu(vid_hdr->data_size)); + + crc = ubi32_to_cpu(vid_hdr->data_crc); + ubi_free_vid_hdr(ubi, vid_hdr); + } + + err = ubi_io_read_data(ubi, buf, pnum, offset, len); + if (err) { + if (err == UBI_IO_BITFLIPS) { + scrub = 1; + err = 0; + } else if (err == -EBADMSG) { + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + goto out_unlock; + scrub = 1; + if (!check) { + ubi_msg("force data checking"); + check = 1; + goto retry; + } + } else + goto out_unlock; + } + + if (check) { + crc1 = crc32(UBI_CRC32_INIT, buf, len); + if (crc1 != crc) { + ubi_warn("CRC error: calculated %#08x, must be %#08x", + crc1, crc); + err = -EBADMSG; + goto out_unlock; + } + } + + if (scrub) + err = ubi_wl_scrub_peb(ubi, pnum); + + leb_read_unlock(ubi, vol_id, lnum); + return err; + +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); +out_unlock: + leb_read_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * recover_peb - recover from write failure. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to recover + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data which was not written because of the write failure + * @offset: offset of the failed write + * @len: how many bytes should have been written + * + * This function is called in case of a write failure and moves all good data + * from the potentially bad physical eraseblock to a good physical eraseblock. + * This function also writes the data which was not written due to the failure. + * Returns new physical eraseblock number in case of success, and a negative + * error code in case of failure. + */ +static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, + const void *buf, int offset, int len) +{ + int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + unsigned char *new_buf; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + return -ENOMEM; + } + +retry: + new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN); + if (new_pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + return new_pnum; + } + + ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum); + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) + err = -EIO; + goto out_put; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); + if (err) + goto write_error; + + data_size = offset + len; + new_buf = kmalloc(data_size, GFP_KERNEL); + if (!new_buf) { + err = -ENOMEM; + goto out_put; + } + memset(new_buf + offset, 0xFF, len); + + /* Read everything before the area where the write failure happened */ + if (offset > 0) { + err = ubi_io_read_data(ubi, new_buf, pnum, 0, offset); + if (err && err != UBI_IO_BITFLIPS) { + kfree(new_buf); + goto out_put; + } + } + + memcpy(new_buf + offset, buf, len); + + err = ubi_io_write_data(ubi, new_buf, new_pnum, 0, data_size); + if (err) { + kfree(new_buf); + goto write_error; + } + + kfree(new_buf); + ubi_free_vid_hdr(ubi, vid_hdr); + + vol->eba_tbl[lnum] = new_pnum; + ubi_wl_put_peb(ubi, pnum, 1); + + ubi_msg("data was successfully recovered"); + return 0; + +out_put: + ubi_wl_put_peb(ubi, new_pnum, 1); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + /* + * Bad luck? This physical eraseblock is bad too? Crud. Let's try to + * get another one. + */ + ubi_warn("failed to write to PEB %d", new_pnum); + ubi_wl_put_peb(ubi, new_pnum, 1); + if (++tries > UBI_IO_RETRIES) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + ubi_msg("try again"); + goto retry; +} + +/** + * ubi_eba_write_leb - write data to dynamic volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: the data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes to write + * @dtype: data type + * + * This function writes data to logical eraseblock @lnum of a dynamic volume + * @vol_id. Returns zero in case of success and a negative error code in case + * of failure. In case of error, it is possible that something was still + * written to the flash media, but may be some garbage. + */ +int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int offset, int len, int dtype) +{ + int idx = vol_id2idx(ubi, vol_id), err, pnum, tries = 0; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum >= 0) { + dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write data to PEB %d", pnum); + if (err == -EIO && ubi->bad_allowed) + err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); + if (err) + ubi_ro_mode(ubi); + } + leb_write_unlock(ubi, vol_id, lnum); + return err; + } + + /* + * The logical eraseblock is not mapped. We have to get a free physical + * eraseblock and write the volume identifier header there first. + */ + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + leb_write_unlock(ubi, vol_id, lnum); + return -ENOMEM; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, " + "PEB %d", len, offset, vol_id, lnum, pnum); + goto write_error; + } + + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + /* + * Fortunately, this is the first write operation to this physical + * eraseblock, so just put it and request a new one. We assume that if + * this physical eraseblock went bad, the erase code will handle that. + */ + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ubi_eba_write_leb_st - write data to static volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write + * @dtype: data type + * @used_ebs: how many logical eraseblocks will this volume contain + * + * This function writes data to logical eraseblock @lnum of static volume + * @vol_id. The @used_ebs argument should contain total number of logical + * eraseblock in this static volume. + * + * When writing to the last logical eraseblock, the @len argument doesn't have + * to be aligned to the minimal I/O unit size. Instead, it has to be equivalent + * to the real data size, although the @buf buffer has to contain the + * alignment. In all other cases, @len has to be aligned. + * + * It is prohibited to write more then once to logical eraseblocks of static + * volumes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype, int used_ebs) +{ + int err, pnum, tries = 0, data_size = len; + int idx = vol_id2idx(ubi, vol_id); + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + if (lnum == used_ebs - 1) + /* If this is the last LEB @len may be unaligned */ + len = ALIGN(data_size, ubi->min_io_size); + else + ubi_assert(len % ubi->min_io_size == 0); + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, data_size); + vid_hdr->vol_type = UBI_VID_STATIC; + vid_hdr->data_size = cpu_to_ubi32(data_size); + vid_hdr->used_ebs = cpu_to_ubi32(used_ebs); + vid_hdr->data_crc = cpu_to_ubi32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d", + len, vol_id, lnum, pnum, used_ebs); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + ubi_assert(vol->eba_tbl[lnum] < 0); + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/* + * ubi_eba_atomic_leb_change - change logical eraseblock atomically. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write + * @dtype: data type + * + * This function changes the contents of a logical eraseblock atomically. @buf + * has to contain new logical eraseblock data, and @len - the length of the + * data, which has to be aligned. This function guarantees that in case of an + * unclean reboot the old contents is preserved. Returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype) +{ + int err, pnum, tries = 0, idx = vol_id2idx(ubi, vol_id); + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, len); + vid_hdr->vol_type = UBI_VID_STATIC; + vid_hdr->data_size = cpu_to_ubi32(len); + vid_hdr->copy_flag = 1; + vid_hdr->data_crc = cpu_to_ubi32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", + vol_id, lnum, vol->eba_tbl[lnum], pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return err; + } + + vol->eba_tbl[lnum] = pnum; + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ltree_entry_ctor - lock tree entries slab cache constructor. + * @obj: the lock-tree entry to construct + * @cache: the lock tree entry slab cache + * @flags: constructor flags + */ +static void ltree_entry_ctor(void *obj, struct kmem_cache *cache, + unsigned long flags) +{ + struct ltree_entry *le = obj; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) != + SLAB_CTOR_CONSTRUCTOR) + return; + + le->users = 0; + init_rwsem(&le->mutex); +} + +/** + * ubi_eba_copy_leb - copy logical eraseblock. + * @ubi: UBI device description object + * @from: physical eraseblock number from where to copy + * @to: physical eraseblock number where to copy + * @vid_hdr: VID header of the @from physical eraseblock + * + * This function copies logical eraseblock from physical eraseblock @from to + * physical eraseblock @to. The @vid_hdr buffer may be changed by this + * function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation + * was canceled because bit-flips were detected at the target PEB, and a + * negative error code in case of failure. + */ +int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr) +{ + int err, vol_id, lnum, data_size, aldata_size, pnum, idx; + struct ubi_volume *vol; + uint32_t crc; + void *buf, *buf1 = NULL; + + vol_id = ubi32_to_cpu(vid_hdr->vol_id); + lnum = ubi32_to_cpu(vid_hdr->lnum); + + dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); + + if (vid_hdr->vol_type == UBI_VID_STATIC) { + data_size = ubi32_to_cpu(vid_hdr->data_size); + aldata_size = ALIGN(data_size, ubi->min_io_size); + } else + data_size = aldata_size = + ubi->leb_size - ubi32_to_cpu(vid_hdr->data_pad); + + buf = kmalloc(aldata_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * We do not want anybody to write to this logical eraseblock while we + * are moving it, so we lock it. + */ + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + kfree(buf); + return err; + } + + /* + * But the logical eraseblock might have been put by this time. + * Cancel if it is true. + */ + idx = vol_id2idx(ubi, vol_id); + + /* + * We may race with volume deletion/re-size, so we have to hold + * @ubi->volumes_lock. + */ + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[idx]; + if (!vol) { + dbg_eba("volume %d was removed meanwhile", vol_id); + spin_unlock(&ubi->volumes_lock); + goto out_unlock; + } + + pnum = vol->eba_tbl[lnum]; + if (pnum != from) { + dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " + "PEB %d, cancel", vol_id, lnum, from, pnum); + spin_unlock(&ubi->volumes_lock); + goto out_unlock; + } + spin_unlock(&ubi->volumes_lock); + + /* OK, now the LEB is locked and we can safely start moving it */ + + dbg_eba("read %d bytes of data", aldata_size); + err = ubi_io_read_data(ubi, buf, from, 0, aldata_size); + if (err && err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading data from PEB %d", + err, from); + goto out_unlock; + } + + /* + * Now we have got to calculate how much data we have to to copy. In + * case of a static volume it is fairly easy - the VID header contains + * the data size. In case of a dynamic volume it is more difficult - we + * have to read the contents, cut 0xFF bytes from the end and copy only + * the first part. We must do this to avoid writing 0xFF bytes as it + * may have some side-effects. And not only this. It is important not + * to include those 0xFFs to CRC because later the they may be filled + * by data. + */ + if (vid_hdr->vol_type == UBI_VID_DYNAMIC) + aldata_size = data_size = + ubi_calc_data_len(ubi, buf, data_size); + + cond_resched(); + crc = crc32(UBI_CRC32_INIT, buf, data_size); + cond_resched(); + + /* + * It may turn out to me that the whole @from physical eraseblock + * contains only 0xFF bytes. Then we have to only write the VID header + * and do not write any data. This also means we should not set + * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc. + */ + if (data_size > 0) { + vid_hdr->copy_flag = 1; + vid_hdr->data_size = cpu_to_ubi32(data_size); + vid_hdr->data_crc = cpu_to_ubi32(crc); + } + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + + err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); + if (err) + goto out_unlock; + + cond_resched(); + + /* Read the VID header back and check if it was written correctly */ + err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read VID header back from PEB %d", to); + goto out_unlock; + } + + if (data_size > 0) { + err = ubi_io_write_data(ubi, buf, to, 0, aldata_size); + if (err) + goto out_unlock; + + /* + * We've written the data and are going to read it back to make + * sure it was written correctly. + */ + buf1 = kmalloc(aldata_size, GFP_KERNEL); + if (!buf1) { + err = -ENOMEM; + goto out_unlock; + } + + cond_resched(); + + err = ubi_io_read_data(ubi, buf1, to, 0, aldata_size); + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read data back from PEB %d", + to); + goto out_unlock; + } + + cond_resched(); + + if (memcmp(buf, buf1, aldata_size)) { + ubi_warn("read data back from PEB %d - it is different", + to); + goto out_unlock; + } + } + + ubi_assert(vol->eba_tbl[lnum] == from); + vol->eba_tbl[lnum] = to; + + leb_write_unlock(ubi, vol_id, lnum); + kfree(buf); + kfree(buf1); + + return 0; + +out_unlock: + leb_write_unlock(ubi, vol_id, lnum); + kfree(buf); + kfree(buf1); + return err; +} + +/** + * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int i, j, err, num_volumes; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + struct ubi_scan_leb *seb; + struct rb_node *rb; + + dbg_eba("initialize EBA unit"); + + spin_lock_init(&ubi->ltree_lock); + ubi->ltree = RB_ROOT; + + if (ubi_devices_cnt == 0) { + ltree_slab = kmem_cache_create("ubi_ltree_slab", + sizeof(struct ltree_entry), 0, + 0, <ree_entry_ctor, NULL); + if (!ltree_slab) + return -ENOMEM; + } + + ubi->global_sqnum = si->max_sqnum + 1; + num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + for (i = 0; i < num_volumes; i++) { + vol = ubi->volumes[i]; + if (!vol) + continue; + + cond_resched(); + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), + GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_free; + } + + for (j = 0; j < vol->reserved_pebs; j++) + vol->eba_tbl[j] = UBI_LEB_UNMAPPED; + + sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i)); + if (!sv) + continue; + + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { + if (seb->lnum >= vol->reserved_pebs) + /* + * This may happen in case of an unclean reboot + * during re-size. + */ + ubi_scan_move_to_list(sv, seb, &si->erase); + vol->eba_tbl[seb->lnum] = seb->pnum; + } + } + + if (ubi->bad_allowed) { + ubi_calculate_reserved(ubi); + + if (ubi->avail_pebs < ubi->beb_rsvd_level) { + /* No enough free physical eraseblocks */ + ubi->beb_rsvd_pebs = ubi->avail_pebs; + ubi_warn("cannot reserve enough PEBs for bad PEB " + "handling, reserved %d, need %d", + ubi->beb_rsvd_pebs, ubi->beb_rsvd_level); + } else + ubi->beb_rsvd_pebs = ubi->beb_rsvd_level; + + ubi->avail_pebs -= ubi->beb_rsvd_pebs; + ubi->rsvd_pebs += ubi->beb_rsvd_pebs; + } + + dbg_eba("EBA unit is initialized"); + return 0; + +out_free: + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + kfree(ubi->volumes[i]->eba_tbl); + } + if (ubi_devices_cnt == 0) + kmem_cache_destroy(ltree_slab); + return err; +} + +/** + * ubi_eba_close - close EBA unit. + * @ubi: UBI device description object + */ +void ubi_eba_close(const struct ubi_device *ubi) +{ + int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + dbg_eba("close EBA unit"); + + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + kfree(ubi->volumes[i]->eba_tbl); + } + if (ubi_devices_cnt == 1) + kmem_cache_destroy(ltree_slab); +} diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c new file mode 100644 index 0000000..fc9478d --- /dev/null +++ b/drivers/mtd/ubi/gluebi.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel + */ + +/* + * This file includes implementation of fake MTD devices for each UBI volume. + * This sounds strange, but it is in fact quite useful to make MTD-oriented + * software (including all the legacy software) to work on top of UBI. + * + * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit + * size (mtd->writesize) is equivalent to the UBI minimal I/O unit. The + * eraseblock size is equivalent to the logical eraseblock size of the volume. + */ + +#include +#include "ubi.h" + +/** + * gluebi_get_device - get MTD device reference. + * @mtd: the MTD device description object + * + * This function is called every time the MTD device is being opened and + * implements the MTD get_device() operation. Returns zero in case of success + * and a negative error code in case of failure. + */ +static int gluebi_get_device(struct mtd_info *mtd) +{ + struct ubi_volume *vol; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + + /* + * We do not introduce locks for gluebi reference count because the + * get_device()/put_device() calls are already serialized at MTD. + */ + if (vol->gluebi_refcount > 0) { + /* + * The MTD device is already referenced and this is just one + * more reference. MTD allows many users to open the same + * volume simultaneously and do not distinguish between + * readers/writers/exclusive openers as UBI does. So we do not + * open the UBI volume again - just increase the reference + * counter and return. + */ + vol->gluebi_refcount += 1; + return 0; + } + + /* + * This is the first reference to this UBI volume via the MTD device + * interface. Open the corresponding volume in read-write mode. + */ + vol->gluebi_desc = ubi_open_volume(vol->ubi->ubi_num, vol->vol_id, + UBI_READWRITE); + if (IS_ERR(vol->gluebi_desc)) + return PTR_ERR(vol->gluebi_desc); + vol->gluebi_refcount += 1; + return 0; +} + +/** + * gluebi_put_device - put MTD device reference. + * @mtd: the MTD device description object + * + * This function is called every time the MTD device is being put. Returns + * zero in case of success and a negative error code in case of failure. + */ +static void gluebi_put_device(struct mtd_info *mtd) +{ + struct ubi_volume *vol; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + vol->gluebi_refcount -= 1; + ubi_assert(vol->gluebi_refcount >= 0); + if (vol->gluebi_refcount == 0) + ubi_close_volume(vol->gluebi_desc); +} + +/** + * gluebi_read - read operation of emulated MTD devices. + * @mtd: MTD device description object + * @from: absolute offset from where to read + * @len: how many bytes to read + * @retlen: count of read bytes is returned here + * @buf: buffer to store the read data + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, unsigned char *buf) +{ + int err = 0, lnum, offs, total_read; + struct ubi_volume *vol; + struct ubi_device *ubi; + uint64_t tmp = from; + + dbg_msg("read %zd bytes from offset %lld", len, from); + + if (len < 0 || from < 0 || from + len > mtd->size) + return -EINVAL; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + ubi = vol->ubi; + + offs = do_div(tmp, mtd->erasesize); + lnum = tmp; + + total_read = len; + while (total_read) { + size_t to_read = mtd->erasesize - offs; + + if (to_read > total_read) + to_read = total_read; + + err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs, + to_read, 0); + if (err) + break; + + lnum += 1; + offs = 0; + total_read -= to_read; + buf += to_read; + } + + *retlen = len - total_read; + return err; +} + +/** + * gluebi_write - write operation of emulated MTD devices. + * @mtd: MTD device description object + * @to: absolute offset where to write + * @len: how many bytes to write + * @retlen: count of written bytes is returned here + * @buf: buffer with data to write + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int err = 0, lnum, offs, total_written; + struct ubi_volume *vol; + struct ubi_device *ubi; + uint64_t tmp = to; + + dbg_msg("write %zd bytes to offset %lld", len, to); + + if (len < 0 || to < 0 || len + to > mtd->size) + return -EINVAL; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + ubi = vol->ubi; + + if (ubi->ro_mode) + return -EROFS; + + offs = do_div(tmp, mtd->erasesize); + lnum = tmp; + + if (len % mtd->writesize || offs % mtd->writesize) + return -EINVAL; + + total_written = len; + while (total_written) { + size_t to_write = mtd->erasesize - offs; + + if (to_write > total_written) + to_write = total_written; + + err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs, + to_write, UBI_UNKNOWN); + if (err) + break; + + lnum += 1; + offs = 0; + total_written -= to_write; + buf += to_write; + } + + *retlen = len - total_written; + return err; +} + +/** + * gluebi_erase - erase operation of emulated MTD devices. + * @mtd: the MTD device description object + * @instr: the erase operation description + * + * This function calls the erase callback when finishes. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int err, i, lnum, count; + struct ubi_volume *vol; + struct ubi_device *ubi; + + dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr); + + if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) + return -EINVAL; + + if (instr->len < 0 || instr->addr + instr->len > mtd->size) + return -EINVAL; + + if (instr->addr % mtd->writesize || instr->len % mtd->writesize) + return -EINVAL; + + lnum = instr->addr / mtd->erasesize; + count = instr->len / mtd->erasesize; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + ubi = vol->ubi; + + if (ubi->ro_mode) + return -EROFS; + + for (i = 0; i < count; i++) { + err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i); + if (err) + goto out_err; + } + + /* + * MTD erase operations are synchronous, so we have to make sure the + * physical eraseblock is wiped out. + */ + err = ubi_wl_flush(ubi); + if (err) + goto out_err; + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return 0; + +out_err: + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = lnum * mtd->erasesize; + return err; +} + +/** + * ubi_create_gluebi - initialize gluebi for an UBI volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function is called when an UBI volume is created in order to create + * corresponding fake MTD device. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) +{ + struct mtd_info *mtd = &vol->gluebi_mtd; + + mtd->name = kmemdup(vol->name, vol->name_len + 1, GFP_KERNEL); + if (!mtd->name) + return -ENOMEM; + + mtd->type = MTD_UBIVOLUME; + if (!ubi->ro_mode) + mtd->flags = MTD_WRITEABLE; + mtd->writesize = ubi->min_io_size; + mtd->owner = THIS_MODULE; + mtd->size = vol->usable_leb_size * vol->reserved_pebs; + mtd->erasesize = vol->usable_leb_size; + mtd->read = gluebi_read; + mtd->write = gluebi_write; + mtd->erase = gluebi_erase; + mtd->get_device = gluebi_get_device; + mtd->put_device = gluebi_put_device; + + if (add_mtd_device(mtd)) { + ubi_err("cannot not add MTD device\n"); + kfree(mtd->name); + return -ENFILE; + } + + dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u", + mtd->index, mtd->name, mtd->size, mtd->erasesize); + return 0; +} + +/** + * ubi_destroy_gluebi - close gluebi for an UBI volume. + * @vol: volume description object + * + * This function is called when an UBI volume is removed in order to remove + * corresponding fake MTD device. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_destroy_gluebi(struct ubi_volume *vol) +{ + int err; + struct mtd_info *mtd = &vol->gluebi_mtd; + + dbg_msg("remove mtd%d", mtd->index); + err = del_mtd_device(mtd); + if (err) + return err; + kfree(mtd->name); + return 0; +} diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c new file mode 100644 index 0000000..438914d --- /dev/null +++ b/drivers/mtd/ubi/io.c @@ -0,0 +1,1259 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * UBI input/output unit. + * + * This unit provides a uniform way to work with all kinds of the underlying + * MTD devices. It also implements handy functions for reading and writing UBI + * headers. + * + * We are trying to have a paranoid mindset and not to trust to what we read + * from the flash media in order to be more secure and robust. So this unit + * validates every single header it reads from the flash media. + * + * Some words about how the eraseblock headers are stored. + * + * The erase counter header is always stored at offset zero. By default, the + * VID header is stored after the EC header at the closest aligned offset + * (i.e. aligned to the minimum I/O unit size). Data starts next to the VID + * header at the closest aligned offset. But this default layout may be + * changed. For example, for different reasons (e.g., optimization) UBI may be + * asked to put the VID header at further offset, and even at an unaligned + * offset. Of course, if the offset of the VID header is unaligned, UBI adds + * proper padding in front of it. Data offset may also be changed but it has to + * be aligned. + * + * About minimal I/O units. In general, UBI assumes flash device model where + * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1, + * in case of NAND flash it is a NAND page, etc. This is reported by MTD in the + * @ubi->mtd->writesize field. But as an exception, UBI admits of using another + * (smaller) minimal I/O unit size for EC and VID headers to make it possible + * to do different optimizations. + * + * This is extremely useful in case of NAND flashes which admit of several + * write operations to one NAND page. In this case UBI can fit EC and VID + * headers at one NAND page. Thus, UBI may use "sub-page" size as the minimal + * I/O unit for the headers (the @ubi->hdrs_min_io_size field). But it still + * reports NAND page size (@ubi->min_io_size) as a minimal I/O unit for the UBI + * users. + * + * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so + * although the minimal I/O unit is 2K, UBI uses 512 bytes for EC and VID + * headers. + * + * Q: why not just to treat sub-page as a minimal I/O unit of this flash + * device, e.g., make @ubi->min_io_size = 512 in the example above? + * + * A: because when writing a sub-page, MTD still writes a full 2K page but the + * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing + * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we + * prefer to use sub-pages only for EV and VID headers. + * + * As it was noted above, the VID header may start at a non-aligned offset. + * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page, + * the VID header may reside at offset 1984 which is the last 64 bytes of the + * last sub-page (EC header is always at offset zero). This causes some + * difficulties when reading and writing VID headers. + * + * Suppose we have a 64-byte buffer and we read a VID header at it. We change + * the data and want to write this VID header out. As we can only write in + * 512-byte chunks, we have to allocate one more buffer and copy our VID header + * to offset 448 of this buffer. + * + * The I/O unit does the following trick in order to avoid this extra copy. + * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header + * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the + * VID header is being written out, it shifts the VID header pointer back and + * writes the whole sub-page. + */ + +#include +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum); +static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum); +static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr); +static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum); +static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr); +static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum, + int offset, int len); +#else +#define paranoid_check_not_bad(ubi, pnum) 0 +#define paranoid_check_peb_ec_hdr(ubi, pnum) 0 +#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0 +#define paranoid_check_peb_vid_hdr(ubi, pnum) 0 +#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 +#define paranoid_check_all_ff(ubi, pnum, offset, len) 0 +#endif + +/** + * ubi_io_read - read data from a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer where to store the read data + * @pnum: physical eraseblock number to read from + * @offset: offset within the physical eraseblock from where to read + * @len: how many bytes to read + * + * This function reads data from offset @offset of physical eraseblock @pnum + * and stores the read data in the @buf buffer. The following return codes are + * possible: + * + * o %0 if all the requested data were successfully read; + * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but + * correctable bit-flips were detected; this is harmless but may indicate + * that this eraseblock may become bad soon (but do not have to); + * o %-EBADMSG if the MTD subsystem reported about data data integrity + * problems, for example it can me an ECC error in case of NAND; this most + * probably means that the data is corrupted; + * o %-EIO if some I/O error occurred; + * o other negative error codes in case of other errors. + */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len) +{ + int err, retries = 0; + size_t read; + loff_t addr; + + dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(len > 0); + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + addr = (loff_t)pnum * ubi->peb_size + offset; +retry: + err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); + if (err) { + if (err == -EUCLEAN) { + /* + * -EUCLEAN is reported if there was a bit-flip which + * was corrected, so this is harmless. + */ + ubi_msg("fixable bit-flip detected at PEB %d", pnum); + ubi_assert(len == read); + return UBI_IO_BITFLIPS; + } + + if (read != len && retries++ < UBI_IO_RETRIES) { + dbg_io("error %d while reading %d bytes from PEB %d:%d, " + "read only %zd bytes, retry", + err, len, pnum, offset, read); + yield(); + goto retry; + } + + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offset, read); + ubi_dbg_dump_stack(); + } else { + ubi_assert(len == read); + + if (ubi_dbg_is_bitflip()) { + dbg_msg("bit-flip (emulated)"); + err = UBI_IO_BITFLIPS; + } + } + + return err; +} + +/** + * ubi_io_write - write data to a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer with the data to write + * @pnum: physical eraseblock number to write to + * @offset: offset within the physical eraseblock where to write + * @len: how many bytes to write + * + * This function writes @len bytes of data from buffer @buf to offset @offset + * of physical eraseblock @pnum. If all the data were successfully written, + * zero is returned. If an error occurred, this function returns a negative + * error code. If %-EIO is returned, the physical eraseblock most probably went + * bad. + * + * Note, in case of an error, it is possible that something was still written + * to the flash media, but may be some garbage. + */ +int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len) +{ + int err; + size_t written; + loff_t addr; + + dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(offset % ubi->hdrs_min_io_size == 0); + ubi_assert(len > 0 && len % ubi->hdrs_min_io_size == 0); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + /* The below has to be compiled out if paranoid checks are disabled */ + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + /* The area we are writing to has to contain all 0xFF bytes */ + err = paranoid_check_all_ff(ubi, pnum, offset, len); + if (err) + return err > 0 ? -EINVAL : err; + + if (offset >= ubi->leb_start) { + /* + * We write to the data area of the physical eraseblock. Make + * sure it has valid EC and VID headers. + */ + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + err = paranoid_check_peb_vid_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + } + + if (ubi_dbg_is_write_failure()) { + dbg_err("cannot write %d bytes to PEB %d:%d " + "(emulated)", len, pnum, offset); + ubi_dbg_dump_stack(); + return -EIO; + } + + addr = (loff_t)pnum * ubi->peb_size + offset; + err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); + if (err) { + ubi_err("error %d while writing %d bytes to PEB %d:%d, written" + " %zd bytes", err, len, pnum, offset, written); + ubi_dbg_dump_stack(); + } else + ubi_assert(written == len); + + return err; +} + +/** + * erase_callback - MTD erasure call-back. + * @ei: MTD erase information object. + * + * Note, even though MTD erase interface is asynchronous, all the current + * implementations are synchronous anyway. + */ +static void erase_callback(struct erase_info *ei) +{ + wake_up_interruptible((wait_queue_head_t *)ei->priv); +} + +/** + * do_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to erase + * + * This function synchronously erases physical eraseblock @pnum and returns + * zero in case of success and a negative error code in case of failure. If + * %-EIO is returned, the physical eraseblock most probably went bad. + */ +static int do_sync_erase(const struct ubi_device *ubi, int pnum) +{ + int err, retries = 0; + struct erase_info ei; + wait_queue_head_t wq; + + dbg_io("erase PEB %d", pnum); + +retry: + init_waitqueue_head(&wq); + memset(&ei, 0, sizeof(struct erase_info)); + + ei.mtd = ubi->mtd; + ei.addr = pnum * ubi->peb_size; + ei.len = ubi->peb_size; + ei.callback = erase_callback; + ei.priv = (unsigned long)&wq; + + err = ubi->mtd->erase(ubi->mtd, &ei); + if (err) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("error %d while erasing PEB %d, retry", + err, pnum); + yield(); + goto retry; + } + ubi_err("cannot erase PEB %d, error %d", pnum, err); + ubi_dbg_dump_stack(); + return err; + } + + err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE || + ei.state == MTD_ERASE_FAILED); + if (err) { + ubi_err("interrupted PEB %d erasure", pnum); + return -EINTR; + } + + if (ei.state == MTD_ERASE_FAILED) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("error while erasing PEB %d, retry", pnum); + yield(); + goto retry; + } + ubi_err("cannot erase PEB %d", pnum); + ubi_dbg_dump_stack(); + return -EIO; + } + + err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size); + if (err) + return err > 0 ? -EINVAL : err; + + if (ubi_dbg_is_erase_failure() && !err) { + dbg_err("cannot erase PEB %d (emulated)", pnum); + return -EIO; + } + + return 0; +} + +/** + * check_pattern - check if buffer contains only a certain byte pattern. + * @buf: buffer to check + * @patt: the pattern to check + * @size: buffer size in bytes + * + * This function returns %1 in there are only @patt bytes in @buf, and %0 if + * something else was also found. + */ +static int check_pattern(const void *buf, uint8_t patt, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (((const uint8_t *)buf)[i] != patt) + return 0; + return 1; +} + +/* Patterns to write to a physical eraseblock when torturing it */ +static uint8_t patterns[] = {0xa5, 0x5a, 0x0}; + +/** + * torture_peb - test a supposedly bad physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to test + * + * This function returns %-EIO if the physical eraseblock did not pass the + * test, a positive number of erase operations done if the test was + * successfully passed, and other negative error codes in case of other errors. + */ +static int torture_peb(const struct ubi_device *ubi, int pnum) +{ + void *buf; + int err, i, patt_count; + + buf = kmalloc(ubi->peb_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + patt_count = ARRAY_SIZE(patterns); + ubi_assert(patt_count > 0); + + for (i = 0; i < patt_count; i++) { + err = do_sync_erase(ubi, pnum); + if (err) + goto out; + + /* Make sure the PEB contains only 0xFF bytes */ + err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size); + if (err) + goto out; + + err = check_pattern(buf, 0xFF, ubi->peb_size); + if (err == 0) { + ubi_err("erased PEB %d, but a non-0xFF byte found", + pnum); + err = -EIO; + goto out; + } + + /* Write a pattern and check it */ + memset(buf, patterns[i], ubi->peb_size); + err = ubi_io_write(ubi, buf, pnum, 0, ubi->peb_size); + if (err) + goto out; + + memset(buf, ~patterns[i], ubi->peb_size); + err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size); + if (err) + goto out; + + err = check_pattern(buf, patterns[i], ubi->peb_size); + if (err == 0) { + ubi_err("pattern %x checking failed for PEB %d", + patterns[i], pnum); + err = -EIO; + goto out; + } + } + + err = patt_count; + +out: + if (err == UBI_IO_BITFLIPS || err == -EBADMSG) + /* + * If a bit-flip or data integrity error was detected, the test + * has not passed because it happened on a freshly erased + * physical eraseblock which means something is wrong with it. + */ + err = -EIO; + kfree(buf); + return err; +} + +/** + * ubi_io_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to erase + * @torture: if this physical eraseblock has to be tortured + * + * This function synchronously erases physical eraseblock @pnum. If @torture + * flag is not zero, the physical eraseblock is checked by means of writing + * different patterns to it and reading them back. If the torturing is enabled, + * the physical eraseblock is erased more then once. + * + * This function returns the number of erasures made in case of success, %-EIO + * if the erasure failed or the torturing test failed, and other negative error + * codes in case of other errors. Note, %-EIO means that the physical + * eraseblock is bad. + */ +int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture) +{ + int err, ret = 0; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = paranoid_check_not_bad(ubi, pnum); + if (err != 0) + return err > 0 ? -EINVAL : err; + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (torture) { + ret = torture_peb(ubi, pnum); + if (ret < 0) + return ret; + } + + err = do_sync_erase(ubi, pnum); + if (err) + return err; + + return ret + 1; +} + +/** + * ubi_io_is_bad - check if a physical eraseblock is bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns a positive number if the physical eraseblock is bad, + * zero if not, and a negative error code if an error occurred. + */ +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum) +{ + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->bad_allowed) { + int ret; + + ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size); + if (ret < 0) + ubi_err("error %d while checking if PEB %d is bad", + ret, pnum); + else if (ret) + dbg_io("PEB %d is bad", pnum); + return ret; + } + + return 0; +} + +/** + * ubi_io_mark_bad - mark a physical eraseblock as bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to mark + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (!ubi->bad_allowed) + return 0; + + err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size); + if (err) + ubi_err("cannot mark PEB %d bad, error %d", pnum, err); + return err; +} + +/** + * validate_ec_hdr - validate an erase counter header. + * @ubi: UBI device description object + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header is OK, and %1 if + * not. + */ +static int validate_ec_hdr(const struct ubi_device *ubi, + const struct ubi_ec_hdr *ec_hdr) +{ + long long ec; + int vid_hdr_offset, leb_start; + + ec = ubi64_to_cpu(ec_hdr->ec); + vid_hdr_offset = ubi32_to_cpu(ec_hdr->vid_hdr_offset); + leb_start = ubi32_to_cpu(ec_hdr->data_offset); + + if (ec_hdr->version != UBI_VERSION) { + ubi_err("node with incompatible UBI version found: " + "this UBI version is %d, image version is %d", + UBI_VERSION, (int)ec_hdr->version); + goto bad; + } + + if (vid_hdr_offset != ubi->vid_hdr_offset) { + ubi_err("bad VID header offset %d, expected %d", + vid_hdr_offset, ubi->vid_hdr_offset); + goto bad; + } + + if (leb_start != ubi->leb_start) { + ubi_err("bad data offset %d, expected %d", + leb_start, ubi->leb_start); + goto bad; + } + + if (ec < 0 || ec > UBI_MAX_ERASECOUNTER) { + ubi_err("bad erase counter %lld", ec); + goto bad; + } + + return 0; + +bad: + ubi_err("bad EC header"); + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_ec_hdr - read and check an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to read from + * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter + * header + * @verbose: be verbose if the header is corrupted or was not found + * + * This function reads erase counter header from physical eraseblock @pnum and + * stores it in @ec_hdr. This function also checks CRC checksum of the read + * erase counter header. The following codes may be returned: + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; this is harmless but may indicate that + * this eraseblock may become bad soon (but may be not); + * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted (a CRC error); + * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty; + * o a negative error code in case of failure. + */ +int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose) +{ + int err, read_err = 0; + uint32_t crc, magic, hdr_crc; + + dbg_io("read EC header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (err) { + if (err != UBI_IO_BITFLIPS && err != -EBADMSG) + return err; + + /* + * We read all the data, but either a correctable bit-flip + * occurred, or MTD reported about some data integrity error, + * like an ECC error in case of NAND. The former is harmless, + * the later may mean that the read data is corrupted. But we + * have a CRC check-sum and we will detect this. If the EC + * header is still OK, we just report this as there was a + * bit-flip. + */ + read_err = err; + } + + magic = ubi32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + /* + * The magic field is wrong. Let's check if we have read all + * 0xFF. If yes, this physical eraseblock is assumed to be + * empty. + * + * But if there was a read error, we do not test it for all + * 0xFFs. Even if it does contain all 0xFFs, this error + * indicates that something is still wrong with this physical + * eraseblock and we anyway cannot treat it as empty. + */ + if (read_err != -EBADMSG && + check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { + /* The physical eraseblock is supposedly empty */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err = paranoid_check_all_ff(ubi, pnum, 0, + ubi->peb_size); + if (err) + return err > 0 ? UBI_IO_BAD_EC_HDR : err; + + if (verbose) + ubi_warn("no EC header found at PEB %d, " + "only 0xFF bytes", pnum); + return UBI_IO_PEB_EMPTY; + } + + /* + * This is not a valid erase counter header, and these are not + * 0xFF bytes. Report that the header is corrupted. + */ + if (verbose) { + ubi_warn("bad magic number at PEB %d: %08x instead of " + "%08x", pnum, magic, UBI_EC_HDR_MAGIC); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + + if (hdr_crc != crc) { + if (verbose) { + ubi_warn("bad EC header CRC at PEB %d, calculated %#08x," + " read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + /* And of course validate what has just been read from the media */ + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("validation failed for PEB %d", pnum); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_ec_hdr - write an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to write to + * @ec_hdr: the erase counter header to write + * + * This function writes erase counter header described by @ec_hdr to physical + * eraseblock @pnum. It also fills most fields of @ec_hdr before writing, so + * the caller do not have to fill them. Callers must only fill the @ec_hdr->ec + * field. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock most probably + * went bad. + */ +int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t crc; + + dbg_io("write EC header to PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + ec_hdr->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC); + ec_hdr->version = UBI_VERSION; + ec_hdr->vid_hdr_offset = cpu_to_ubi32(ubi->vid_hdr_offset); + ec_hdr->data_offset = cpu_to_ubi32(ubi->leb_start); + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + ec_hdr->hdr_crc = cpu_to_ubi32(crc); + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + if (err) + return -EINVAL; + + err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); + return err; +} + +/** + * validate_vid_hdr - validate a volume identifier header. + * @ubi: UBI device description object + * @vid_hdr: the volume identifier header to check + * + * This function checks that data stored in the volume identifier header + * @vid_hdr. Returns zero if the VID header is OK and %1 if not. + */ +static int validate_vid_hdr(const struct ubi_device *ubi, + const struct ubi_vid_hdr *vid_hdr) +{ + int vol_type = vid_hdr->vol_type; + int copy_flag = vid_hdr->copy_flag; + int vol_id = ubi32_to_cpu(vid_hdr->vol_id); + int lnum = ubi32_to_cpu(vid_hdr->lnum); + int compat = vid_hdr->compat; + int data_size = ubi32_to_cpu(vid_hdr->data_size); + int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + int data_pad = ubi32_to_cpu(vid_hdr->data_pad); + int data_crc = ubi32_to_cpu(vid_hdr->data_crc); + int usable_leb_size = ubi->leb_size - data_pad; + + if (copy_flag != 0 && copy_flag != 1) { + dbg_err("bad copy_flag"); + goto bad; + } + + if (vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 || + data_pad < 0) { + dbg_err("negative values"); + goto bad; + } + + if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) { + dbg_err("bad vol_id"); + goto bad; + } + + if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) { + dbg_err("bad compat"); + goto bad; + } + + if (vol_id >= UBI_INTERNAL_VOL_START && compat != UBI_COMPAT_DELETE && + compat != UBI_COMPAT_RO && compat != UBI_COMPAT_PRESERVE && + compat != UBI_COMPAT_REJECT) { + dbg_err("bad compat"); + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + dbg_err("bad vol_type"); + goto bad; + } + + if (data_pad >= ubi->leb_size / 2) { + dbg_err("bad data_pad"); + goto bad; + } + + if (vol_type == UBI_VID_STATIC) { + /* + * Although from high-level point of view static volumes may + * contain zero bytes of data, but no VID headers can contain + * zero at these fields, because they empty volumes do not have + * mapped logical eraseblocks. + */ + if (used_ebs == 0) { + dbg_err("zero used_ebs"); + goto bad; + } + if (data_size == 0) { + dbg_err("zero data_size"); + goto bad; + } + if (lnum < used_ebs - 1) { + if (data_size != usable_leb_size) { + dbg_err("bad data_size"); + goto bad; + } + } else if (lnum == used_ebs - 1) { + if (data_size == 0) { + dbg_err("bad data_size at last LEB"); + goto bad; + } + } else { + dbg_err("too high lnum"); + goto bad; + } + } else { + if (copy_flag == 0) { + if (data_crc != 0) { + dbg_err("non-zero data CRC"); + goto bad; + } + if (data_size != 0) { + dbg_err("non-zero data_size"); + goto bad; + } + } else { + if (data_size == 0) { + dbg_err("zero data_size of copy"); + goto bad; + } + } + if (used_ebs != 0) { + dbg_err("bad used_ebs"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("bad VID header"); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_vid_hdr - read and check a volume identifier header. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to read from + * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume + * identifier header + * @verbose: be verbose if the header is corrupted or wasn't found + * + * This function reads the volume identifier header from physical eraseblock + * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read + * volume identifier header. The following codes may be returned: + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; this is harmless but may indicate that + * this eraseblock may become bad soon; + * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC + * error detected); + * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID + * header there); + * o a negative error code in case of failure. + */ +int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose) +{ + int err, read_err = 0; + uint32_t crc, magic, hdr_crc; + void *p; + + dbg_io("read VID header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + if (err) { + if (err != UBI_IO_BITFLIPS && err != -EBADMSG) + return err; + + /* + * We read all the data, but either a correctable bit-flip + * occurred, or MTD reported about some data integrity error, + * like an ECC error in case of NAND. The former is harmless, + * the later may mean the read data is corrupted. But we have a + * CRC check-sum and we will identify this. If the VID header is + * still OK, we just report this as there was a bit-flip. + */ + read_err = err; + } + + magic = ubi32_to_cpu(vid_hdr->magic); + if (magic != UBI_VID_HDR_MAGIC) { + /* + * If we have read all 0xFF bytes, the VID header probably does + * not exist and the physical eraseblock is assumed to be free. + * + * But if there was a read error, we do not test the data for + * 0xFFs. Even if it does contain all 0xFFs, this error + * indicates that something is still wrong with this physical + * eraseblock and it cannot be regarded as free. + */ + if (read_err != -EBADMSG && + check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) { + /* The physical eraseblock is supposedly free */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start, + ubi->leb_size); + if (err) + return err > 0 ? UBI_IO_BAD_VID_HDR : err; + + if (verbose) + ubi_warn("no VID header found at PEB %d, " + "only 0xFF bytes", pnum); + return UBI_IO_PEB_FREE; + } + + /* + * This is not a valid VID header, and these are not 0xFF + * bytes. Report that the header is corrupted. + */ + if (verbose) { + ubi_warn("bad magic number at PEB %d: %08x instead of " + "%08x", pnum, magic, UBI_VID_HDR_MAGIC); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + + if (hdr_crc != crc) { + if (verbose) { + ubi_warn("bad CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + /* Validate the VID header that we have just read */ + err = validate_vid_hdr(ubi, vid_hdr); + if (err) { + ubi_err("validation failed for PEB %d", pnum); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_vid_hdr - write a volume identifier header. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to write to + * @vid_hdr: the volume identifier header to write + * + * This function writes the volume identifier header described by @vid_hdr to + * physical eraseblock @pnum. This function automatically fills the + * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates + * header CRC checksum and stores it at vid_hdr->hdr_crc. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock probably went + * bad. + */ +int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t crc; + void *p; + + dbg_io("write VID header to PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL: err; + + vid_hdr->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC); + vid_hdr->version = UBI_VERSION; + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + vid_hdr->hdr_crc = cpu_to_ubi32(crc); + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + if (err) + return -EINVAL; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_not_bad - ensure that a physical eraseblock is not bad. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to check + * + * This function returns zero if the physical eraseblock is good, a positive + * number if it is bad and a negative error code if an error occurred. + */ +static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + + err = ubi_io_is_bad(ubi, pnum); + if (!err) + return err; + + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_stack(); + return err; +} + +/** + * paranoid_check_ec_hdr - check if an erase counter header is all right. + * @ubi: UBI device description object + * @pnum: physical eraseblock number the erase counter header belongs to + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header contains valid + * values, and %1 if not. + */ +static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t magic; + + magic = ubi32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + ubi_err("bad magic %#08x, must be %#08x", + magic, UBI_EC_HDR_MAGIC); + goto fail; + } + + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("paranoid check failed for PEB %d", pnum); + goto fail; + } + + return 0; + +fail: + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * paranoid_check_peb_ec_hdr - check that the erase counter header of a + * physical eraseblock is in-place and is all right. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the erase counter header is all right, %1 if + * not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) +{ + int err; + uint32_t crc, hdr_crc; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + goto exit; + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + if (hdr_crc != crc) { + ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc); + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + err = 1; + goto exit; + } + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + +exit: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_check_vid_hdr - check that a volume identifier header is all right. + * @ubi: UBI device description object + * @pnum: physical eraseblock number the volume identifier header belongs to + * @vid_hdr: the volume identifier header to check + * + * This function returns zero if the volume identifier header is all right, and + * %1 if not. + */ +static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t magic; + + magic = ubi32_to_cpu(vid_hdr->magic); + if (magic != UBI_VID_HDR_MAGIC) { + ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x", + magic, pnum, UBI_VID_HDR_MAGIC); + goto fail; + } + + err = validate_vid_hdr(ubi, vid_hdr); + if (err) { + ubi_err("paranoid check failed for PEB %d", pnum); + goto fail; + } + + return err; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + return 1; + +} + +/** + * paranoid_check_peb_vid_hdr - check that the volume identifier header of a + * physical eraseblock is in-place and is all right. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the volume identifier header is all right, + * %1 if not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) +{ + int err; + uint32_t crc, hdr_crc; + struct ubi_vid_hdr *vid_hdr; + void *p; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + goto exit; + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + if (hdr_crc != crc) { + ubi_err("bad VID header CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + err = 1; + goto exit; + } + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + +exit: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; +} + +/** + * paranoid_check_all_ff - check that a region of flash is empty. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * @offset: the starting offset within the physical eraseblock to check + * @len: the length of the region to check + * + * This function returns zero if only 0xFF bytes are present at offset + * @offset of the physical eraseblock @pnum, %1 if not, and a negative error + * code if an error occurred. + */ +static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum, + int offset, int len) +{ + size_t read; + int err; + void *buf; + loff_t addr = (loff_t)pnum * ubi->peb_size + offset; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); + if (err && err != -EUCLEAN) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offset, read); + goto error; + } + + err = check_pattern(buf, 0xFF, len); + if (err == 0) { + ubi_err("flash region at PEB %d:%d, length %d does not " + "contain all 0xFF bytes", pnum, offset, len); + goto fail; + } + + kfree(buf); + return 0; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + dbg_msg("hex dump of the %d-%d region", offset, offset + len); + ubi_dbg_hexdump(buf, len); + err = 1; +error: + ubi_dbg_dump_stack(); + kfree(buf); + return err; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c new file mode 100644 index 0000000..d352c45 --- /dev/null +++ b/drivers/mtd/ubi/kapi.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* This file mostly implements UBI kernel API functions */ + +#include +#include +#include +#include "ubi.h" + +/** + * ubi_get_device_info - get information about UBI device. + * @ubi_num: UBI device number + * @di: the information is stored here + * + * This function returns %0 in case of success and a %-ENODEV if there is no + * such UBI device. + */ +int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) +{ + const struct ubi_device *ubi; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || + !ubi_devices[ubi_num]) { + module_put(THIS_MODULE); + return -ENODEV; + } + + ubi = ubi_devices[ubi_num]; + di->ubi_num = ubi->ubi_num; + di->leb_size = ubi->leb_size; + di->min_io_size = ubi->min_io_size; + di->ro_mode = ubi->ro_mode; + di->cdev = MKDEV(ubi->major, 0); + module_put(THIS_MODULE); + return 0; +} +EXPORT_SYMBOL_GPL(ubi_get_device_info); + +/** + * ubi_get_volume_info - get information about UBI volume. + * @desc: volume descriptor + * @vi: the information is stored here + */ +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi) +{ + const struct ubi_volume *vol = desc->vol; + const struct ubi_device *ubi = vol->ubi; + + vi->vol_id = vol->vol_id; + vi->ubi_num = ubi->ubi_num; + vi->size = vol->reserved_pebs; + vi->used_bytes = vol->used_bytes; + vi->vol_type = vol->vol_type; + vi->corrupted = vol->corrupted; + vi->upd_marker = vol->upd_marker; + vi->alignment = vol->alignment; + vi->usable_leb_size = vol->usable_leb_size; + vi->name_len = vol->name_len; + vi->name = vol->name; + vi->cdev = MKDEV(ubi->major, vi->vol_id + 1); +} +EXPORT_SYMBOL_GPL(ubi_get_volume_info); + +/** + * ubi_open_volume - open UBI volume. + * @ubi_num: UBI device number + * @vol_id: volume ID + * @mode: open mode + * + * The @mode parameter specifies if the volume should be opened in read-only + * mode, read-write mode, or exclusive mode. The exclusive mode guarantees that + * nobody else will be able to open this volume. UBI allows to have many volume + * readers and one writer at a time. + * + * If a static volume is being opened for the first time since boot, it will be + * checked by this function, which means it will be fully read and the CRC + * checksum of each logical eraseblock will be checked. + * + * This function returns volume descriptor in case of success and a negative + * error code in case of failure. + */ +struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) +{ + int err; + struct ubi_volume_desc *desc; + struct ubi_device *ubi = ubi_devices[ubi_num]; + struct ubi_volume *vol; + + dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(err); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi) + goto out_put; + + err = -EINVAL; + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + goto out_put; + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) + goto out_put; + + desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); + if (!desc) { + err = -ENOMEM; + goto out_put; + } + + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; + if (!vol) { + err = -ENODEV; + goto out_unlock; + } + + err = -EBUSY; + switch (mode) { + case UBI_READONLY: + if (vol->exclusive) + goto out_unlock; + vol->readers += 1; + break; + + case UBI_READWRITE: + if (vol->exclusive || vol->writers > 0) + goto out_unlock; + vol->writers += 1; + break; + + case UBI_EXCLUSIVE: + if (vol->exclusive || vol->writers || vol->readers) + goto out_unlock; + vol->exclusive = 1; + break; + } + spin_unlock(&ubi->volumes_lock); + + desc->vol = vol; + desc->mode = mode; + + /* + * To prevent simultaneous checks of the same volume we use @vtbl_mutex, + * although it is not the purpose it was introduced for. + */ + mutex_lock(&ubi->vtbl_mutex); + if (!vol->checked) { + /* This is the first open - check the volume */ + err = ubi_check_volume(ubi, vol_id); + if (err < 0) { + mutex_unlock(&ubi->vtbl_mutex); + ubi_close_volume(desc); + return ERR_PTR(err); + } + if (err == 1) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + vol->checked = 1; + } + mutex_unlock(&ubi->vtbl_mutex); + return desc; + +out_unlock: + spin_unlock(&ubi->volumes_lock); + kfree(desc); +out_put: + module_put(THIS_MODULE); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(ubi_open_volume); + +/** + * ubi_open_volume_nm - open UBI volume by name. + * @ubi_num: UBI device number + * @name: volume name + * @mode: open mode + * + * This function is similar to 'ubi_open_volume()', but opens a volume by name. + */ +struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + int mode) +{ + int i, vol_id = -1, len; + struct ubi_volume_desc *ret; + struct ubi_device *ubi; + + dbg_msg("open volume %s, mode %d", name, mode); + + if (!name) + return ERR_PTR(-EINVAL); + + len = strnlen(name, UBI_VOL_NAME_MAX + 1); + if (len > UBI_VOL_NAME_MAX) + return ERR_PTR(-EINVAL); + + ret = ERR_PTR(-ENODEV); + if (!try_module_get(THIS_MODULE)) + return ret; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi_devices[ubi_num]) + goto out_put; + + ubi = ubi_devices[ubi_num]; + + spin_lock(&ubi->volumes_lock); + /* Walk all volumes of this UBI device */ + for (i = 0; i < ubi->vtbl_slots; i++) { + struct ubi_volume *vol = ubi->volumes[i]; + + if (vol && len == vol->name_len && !strcmp(name, vol->name)) { + vol_id = i; + break; + } + } + spin_unlock(&ubi->volumes_lock); + + if (vol_id < 0) + goto out_put; + + ret = ubi_open_volume(ubi_num, vol_id, mode); + +out_put: + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL_GPL(ubi_open_volume_nm); + +/** + * ubi_close_volume - close UBI volume. + * @desc: volume descriptor + */ +void ubi_close_volume(struct ubi_volume_desc *desc) +{ + struct ubi_volume *vol = desc->vol; + + dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); + + spin_lock(&vol->ubi->volumes_lock); + switch (desc->mode) { + case UBI_READONLY: + vol->readers -= 1; + break; + case UBI_READWRITE: + vol->writers -= 1; + break; + case UBI_EXCLUSIVE: + vol->exclusive = 0; + } + spin_unlock(&vol->ubi->volumes_lock); + + kfree(desc); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL_GPL(ubi_close_volume); + +/** + * ubi_leb_read - read data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to read from + * @buf: buffer where to store the read data + * @offset: offset within the logical eraseblock to read from + * @len: how many bytes to read + * @check: whether UBI has to check the read data's CRC or not. + * + * This function reads data from offset @offset of logical eraseblock @lnum and + * stores the data at @buf. When reading from static volumes, @check specifies + * whether the data has to be checked or not. If yes, the whole logical + * eraseblock will be read and its CRC checksum will be checked (i.e., the CRC + * checksum is per-eraseblock). So checking may substantially slow down the + * read speed. The @check argument is ignored for dynamic volumes. + * + * In case of success, this function returns zero. In case of failure, this + * function returns a negative error code. + * + * %-EBADMSG error code is returned: + * o for both static and dynamic volumes if MTD driver has detected a data + * integrity problem (unrecoverable ECC checksum mismatch in case of NAND); + * o for static volumes in case of data CRC mismatch. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF error code. + */ +int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, + int len, int check) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, vol_id = vol->vol_id; + + dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || + lnum >= vol->used_ebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size) + return -EINVAL; + + if (vol->vol_type == UBI_STATIC_VOLUME && lnum == vol->used_ebs - 1 && + offset + len > vol->last_eb_bytes) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + if (len == 0) + return 0; + + err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check); + if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) { + ubi_warn("mark volume %d as corrupted", vol_id); + vol->corrupted = 1; + } + + return err; +} +EXPORT_SYMBOL_GPL(ubi_leb_read); + +/** + * ubi_leb_write - write data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to write to + * @buf: data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes to write + * @dtype: expected data type + * + * This function writes @len bytes of data from @buf to offset @offset of + * logical eraseblock @lnum. The @dtype argument describes expected lifetime of + * the data. + * + * This function takes care of physical eraseblock write failures. If write to + * the physical eraseblock write operation fails, the logical eraseblock is + * re-mapped to another physical eraseblock, the data is recovered, and the + * write finishes. UBI has a pool of reserved physical eraseblocks for this. + * + * If all the data were successfully written, zero is returned. If an error + * occurred and UBI has not been able to recover from it, this function returns + * a negative error code. Note, in case of an error, it is possible that + * something was still written to the flash media, but that may be some + * garbage. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF code. + */ +int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, + int offset, int len, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size || offset % ubi->min_io_size || + len % ubi->min_io_size) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (len == 0) + return 0; + + return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_write); + +/* + * ubi_leb_change - change logical eraseblock atomically. + * @desc: volume descriptor + * @lnum: logical eraseblock number to change + * @buf: data to write + * @len: how many bytes to write + * @dtype: expected data type + * + * This function changes the contents of a logical eraseblock atomically. @buf + * has to contain new logical eraseblock data, and @len - the length of the + * data, which has to be aligned. The length may be shorter then the logical + * eraseblock size, ant the logical eraseblock may be appended to more times + * later on. This function guarantees that in case of an unclean reboot the old + * contents is preserved. Returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, + int len, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || + len > vol->usable_leb_size || len % ubi->min_io_size) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (len == 0) + return 0; + + return ubi_eba_atomic_leb_change(ubi, vol_id, lnum, buf, len, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_change); + +/** + * ubi_leb_erase - erase logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and synchronously erases the + * correspondent physical eraseblock. Returns zero in case of success and a + * negative error code in case of failure. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF code. + */ +int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, vol_id = vol->vol_id; + + dbg_msg("erase LEB %d:%d", vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + err = ubi_eba_unmap_leb(ubi, vol_id, lnum); + if (err) + return err; + + return ubi_wl_flush(ubi); +} +EXPORT_SYMBOL_GPL(ubi_leb_erase); + +/** + * ubi_leb_unmap - un-map logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules the + * corresponding physical eraseblock for erasure, so that it will eventually be + * physically erased in background. This operation is much faster then the + * erase operation. + * + * Unlike erase, the un-map operation does not guarantee that the logical + * eraseblock will contain all 0xFF bytes when UBI is initialized again. For + * example, if several logical eraseblocks are un-mapped, and an unclean reboot + * happens after this, the logical eraseblocks will not necessarily be + * un-mapped again when this MTD device is attached. They may actually be + * mapped to the same physical eraseblocks again. So, this function has to be + * used with care. + * + * In other words, when un-mapping a logical eraseblock, UBI does not store + * any information about this on the flash media, it just marks the logical + * eraseblock as "un-mapped" in RAM. If UBI is detached before the physical + * eraseblock is physically erased, it will be mapped again to the same logical + * eraseblock when the MTD device is attached again. + * + * The main and obvious use-case of this function is when the contents of a + * logical eraseblock has to be re-written. Then it is much more efficient to + * first un-map it, then write new data, rather then first erase it, then write + * new data. Note, once new data has been written to the logical eraseblock, + * UBI guarantees that the old contents has gone forever. In other words, if an + * unclean reboot happens after the logical eraseblock has been un-mapped and + * then written to, it will contain the last written data. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If the volume is damaged because of an interrupted update + * this function just returns immediately with %-EBADF code. + */ +int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("unmap LEB %d:%d", vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return ubi_eba_unmap_leb(ubi, vol_id, lnum); +} +EXPORT_SYMBOL_GPL(ubi_leb_unmap); + +/** + * ubi_is_mapped - check if logical eraseblock is mapped. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function checks if logical eraseblock @lnum is mapped to a physical + * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily + * mean it will still be un-mapped after the UBI device is re-attached. The + * logical eraseblock may become mapped to the physical eraseblock it was last + * mapped to. + * + * This function returns %1 if the LEB is mapped, %0 if not, and a negative + * error code in case of failure. If the volume is damaged because of an + * interrupted update this function just returns immediately with %-EBADF error + * code. + */ +int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + + dbg_msg("test LEB %d:%d", vol->vol_id, lnum); + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return vol->eba_tbl[lnum] >= 0; +} +EXPORT_SYMBOL_GPL(ubi_is_mapped); diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c new file mode 100644 index 0000000..38d4e67 --- /dev/null +++ b/drivers/mtd/ubi/misc.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* Here we keep miscellaneous functions which are used all over the UBI code */ + +#include "ubi.h" + +/** + * calc_data_len - calculate how much real data is stored in a buffer. + * @ubi: UBI device description object + * @buf: a buffer with the contents of the physical eraseblock + * @length: the buffer length + * + * This function calculates how much "real data" is stored in @buf and returnes + * the length. Continuous 0xFF bytes at the end of the buffer are not + * considered as "real data". + */ +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, + int length) +{ + int i; + + ubi_assert(length % ubi->min_io_size == 0); + + for (i = length - 1; i >= 0; i--) + if (((const uint8_t *)buf)[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + length = ALIGN(i + 1, ubi->min_io_size); + return length; +} + +/** + * ubi_check_volume - check the contents of a static volume. + * @ubi: UBI device description object + * @vol_id: ID of the volume to check + * + * This function checks if static volume @vol_id is corrupted by fully reading + * it and checking data CRC. This function returns %0 if the volume is not + * corrupted, %1 if it is corrupted and a negative error code in case of + * failure. Dynamic volumes are not checked and zero is returned immediately. + */ +int ubi_check_volume(struct ubi_device *ubi, int vol_id) +{ + void *buf; + int err = 0, i; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + if (vol->vol_type != UBI_STATIC_VOLUME) + return 0; + + buf = kmalloc(vol->usable_leb_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < vol->used_ebs; i++) { + int size; + + if (i == vol->used_ebs - 1) + size = vol->last_eb_bytes; + else + size = vol->usable_leb_size; + + err = ubi_eba_read_leb(ubi, vol_id, i, buf, 0, size, 1); + if (err) { + if (err == -EBADMSG) + err = 1; + break; + } + } + + kfree(buf); + return err; +} + +/** + * ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad + * eraseblock handling. + * @ubi: UBI device description object + */ +void ubi_calculate_reserved(struct ubi_device *ubi) +{ + ubi->beb_rsvd_level = ubi->good_peb_count/100; + ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE; + if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS) + ubi->beb_rsvd_level = MIN_RESEVED_PEBS; +} diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c new file mode 100644 index 0000000..473f320 --- /dev/null +++ b/drivers/mtd/ubi/scan.c @@ -0,0 +1,1368 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * UBI scanning unit. + * + * This unit is responsible for scanning the flash media, checking UBI + * headers and providing complete information about the UBI flash image. + * + * The scanning information is reoresented by a &struct ubi_scan_info' object. + * Information about found volumes is represented by &struct ubi_scan_volume + * objects which are kept in volume RB-tree with root at the @volumes field. + * The RB-tree is indexed by the volume ID. + * + * Found logical eraseblocks are represented by &struct ubi_scan_leb objects. + * These objects are kept in per-volume RB-trees with the root at the + * corresponding &struct ubi_scan_volume object. To put it differently, we keep + * an RB-tree of per-volume objects and each of these objects is the root of + * RB-tree of per-eraseblock objects. + * + * Corrupted physical eraseblocks are put to the @corr list, free physical + * eraseblocks are put to the @free list and the physical eraseblock to be + * erased are put to the @erase list. + */ + +#include +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_si(const struct ubi_device *ubi, + struct ubi_scan_info *si); +#else +#define paranoid_check_si(ubi, si) 0 +#endif + +/* Temporary variables used during scanning */ +static struct ubi_ec_hdr *ech; +static struct ubi_vid_hdr *vidh; + +int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec, + struct list_head *list) +{ + struct ubi_scan_leb *seb; + + if (list == &si->free) + dbg_bld("add to free: PEB %d, EC %d", pnum, ec); + else if (list == &si->erase) + dbg_bld("add to erase: PEB %d, EC %d", pnum, ec); + else if (list == &si->corr) + dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); + else if (list == &si->alien) + dbg_bld("add to alien: PEB %d, EC %d", pnum, ec); + else + BUG(); + + seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); + if (!seb) + return -ENOMEM; + + seb->pnum = pnum; + seb->ec = ec; + list_add_tail(&seb->u.list, list); + return 0; +} + +/** + * commit_to_mean_value - commit intermediate results to the final mean erase + * counter value. + * @si: scanning information + * + * This is a helper function which calculates partial mean erase counter mean + * value and adds it to the resulting mean value. As we can work only in + * integer arithmetic and we want to calculate the mean value of erase counter + * accurately, we first sum erase counter values in @si->ec_sum variable and + * count these components in @si->ec_count. If this temporary @si->ec_sum is + * going to overflow, we calculate the partial mean value + * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec. + */ +static void commit_to_mean_value(struct ubi_scan_info *si) +{ + si->ec_sum /= si->ec_count; + if (si->ec_sum % si->ec_count >= si->ec_count / 2) + si->mean_ec += 1; + si->mean_ec += si->ec_sum; +} + +/** + * validate_vid_hdr - check that volume identifier header is correct and + * consistent. + * @vid_hdr: the volume identifier header to check + * @sv: information about the volume this logical eraseblock belongs to + * @pnum: physical eraseblock number the VID header came from + * + * This function checks that data stored in @vid_hdr is consistent. Returns + * non-zero if an inconsistency was found and zero if not. + * + * Note, UBI does sanity check of everything it reads from the flash media. + * Most of the checks are done in the I/O unit. Here we check that the + * information in the VID header is consistent to the information in other VID + * headers of the same volume. + */ +static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr, + const struct ubi_scan_volume *sv, int pnum) +{ + int vol_type = vid_hdr->vol_type; + int vol_id = ubi32_to_cpu(vid_hdr->vol_id); + int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + int data_pad = ubi32_to_cpu(vid_hdr->data_pad); + + if (sv->leb_count != 0) { + int sv_vol_type; + + /* + * This is not the first logical eraseblock belonging to this + * volume. Ensure that the data in its VID header is consistent + * to the data in previous logical eraseblock headers. + */ + + if (vol_id != sv->vol_id) { + dbg_err("inconsistent vol_id"); + goto bad; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) + sv_vol_type = UBI_VID_STATIC; + else + sv_vol_type = UBI_VID_DYNAMIC; + + if (vol_type != sv_vol_type) { + dbg_err("inconsistent vol_type"); + goto bad; + } + + if (used_ebs != sv->used_ebs) { + dbg_err("inconsistent used_ebs"); + goto bad; + } + + if (data_pad != sv->data_pad) { + dbg_err("inconsistent data_pad"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("inconsistent VID header at PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_sv(sv); + return -EINVAL; +} + +/** + * add_volume - add volume to the scanning information. + * @si: scanning information + * @vol_id: ID of the volume to add + * @pnum: physical eraseblock number + * @vid_hdr: volume identifier header + * + * If the volume corresponding to the @vid_hdr logical eraseblock is already + * present in the scanning information, this function does nothing. Otherwise + * it adds corresponding volume to the scanning information. Returns a pointer + * to the scanning volume object in case of success and a negative error code + * in case of failure. + */ +static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, + int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + struct ubi_scan_volume *sv; + struct rb_node **p = &si->volumes.rb_node, *parent = NULL; + + ubi_assert(vol_id == ubi32_to_cpu(vid_hdr->vol_id)); + + /* Walk the volume RB-tree to look if this volume is already present */ + while (*p) { + parent = *p; + sv = rb_entry(parent, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) + return sv; + + if (vol_id > sv->vol_id) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + /* The volume is absent - add it */ + sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL); + if (!sv) + return ERR_PTR(-ENOMEM); + + sv->highest_lnum = sv->leb_count = 0; + si->max_sqnum = 0; + sv->vol_id = vol_id; + sv->root = RB_ROOT; + sv->used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + sv->data_pad = ubi32_to_cpu(vid_hdr->data_pad); + sv->compat = vid_hdr->compat; + sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME + : UBI_STATIC_VOLUME; + if (vol_id > si->highest_vol_id) + si->highest_vol_id = vol_id; + + rb_link_node(&sv->rb, parent, p); + rb_insert_color(&sv->rb, &si->volumes); + si->vols_found += 1; + dbg_bld("added volume %d", vol_id); + return sv; +} + +/** + * compare_lebs - find out which logical eraseblock is newer. + * @ubi: UBI device description object + * @seb: first logical eraseblock to compare + * @pnum: physical eraseblock number of the second logical eraseblock to + * compare + * @vid_hdr: volume identifier header of the second logical eraseblock + * + * This function compares 2 copies of a LEB and informs which one is newer. In + * case of success this function returns a positive value, in case of failure, a + * negative error code is returned. The success return codes use the following + * bits: + * o bit 0 is cleared: the first PEB (described by @seb) is newer then the + * second PEB (described by @pnum and @vid_hdr); + * o bit 0 is set: the second PEB is newer; + * o bit 1 is cleared: no bit-flips were detected in the newer LEB; + * o bit 1 is set: bit-flips were detected in the newer LEB; + * o bit 2 is cleared: the older LEB is not corrupted; + * o bit 2 is set: the older LEB is corrupted. + */ +static int compare_lebs(const struct ubi_device *ubi, + const struct ubi_scan_leb *seb, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + void *buf; + int len, err, second_is_newer, bitflips = 0, corrupted = 0; + uint32_t data_crc, crc; + struct ubi_vid_hdr *vidh = NULL; + unsigned long long sqnum2 = ubi64_to_cpu(vid_hdr->sqnum); + + if (seb->sqnum == 0 && sqnum2 == 0) { + long long abs, v1 = seb->leb_ver, v2 = ubi32_to_cpu(vid_hdr->leb_ver); + + /* + * UBI constantly increases the logical eraseblock version + * number and it can overflow. Thus, we have to bear in mind + * that versions that are close to %0xFFFFFFFF are less then + * versions that are close to %0. + * + * The UBI WL unit guarantees that the number of pending tasks + * is not greater then %0x7FFFFFFF. So, if the difference + * between any two versions is greater or equivalent to + * %0x7FFFFFFF, there was an overflow and the logical + * eraseblock with lower version is actually newer then the one + * with higher version. + * + * FIXME: but this is anyway obsolete and will be removed at + * some point. + */ + + dbg_bld("using old crappy leb_ver stuff"); + + abs = v1 - v2; + if (abs < 0) + abs = -abs; + + if (abs < 0x7FFFFFFF) + /* Non-overflow situation */ + second_is_newer = (v2 > v1); + else + second_is_newer = (v2 < v1); + } else + /* Obviously the LEB with lower sequence counter is older */ + second_is_newer = sqnum2 > seb->sqnum; + + /* + * Now we know which copy is newer. If the copy flag of the PEB with + * newer version is not set, then we just return, otherwise we have to + * check data CRC. For the second PEB we already have the VID header, + * for the first one - we'll need to re-read it from flash. + * + * FIXME: this may be optimized so that we wouldn't read twice. + */ + + if (second_is_newer) { + if (!vid_hdr->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("second PEB %d is newer, copy_flag is unset", + pnum); + return 1; + } + } else { + pnum = seb->pnum; + + vidh = ubi_zalloc_vid_hdr(ubi); + if (!vidh) + return -ENOMEM; + + err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + if (err) { + if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else { + dbg_err("VID of PEB %d header is bad, but it " + "was OK earlier", pnum); + if (err > 0) + err = -EIO; + + goto out_free_vidh; + } + } + + if (!vidh->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("first PEB %d is newer, copy_flag is unset", + pnum); + err = bitflips << 1; + goto out_free_vidh; + } + + vid_hdr = vidh; + } + + /* Read the data of the copy and check the CRC */ + + len = ubi32_to_cpu(vid_hdr->data_size); + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto out_free_vidh; + } + + err = ubi_io_read_data(ubi, buf, pnum, 0, len); + if (err && err != UBI_IO_BITFLIPS) + goto out_free_buf; + + data_crc = ubi32_to_cpu(vid_hdr->data_crc); + crc = crc32(UBI_CRC32_INIT, buf, len); + if (crc != data_crc) { + dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x", + pnum, crc, data_crc); + corrupted = 1; + bitflips = 0; + second_is_newer = !second_is_newer; + } else { + dbg_bld("PEB %d CRC is OK", pnum); + bitflips = !!err; + } + + kfree(buf); + ubi_free_vid_hdr(ubi, vidh); + + if (second_is_newer) + dbg_bld("second PEB %d is newer, copy_flag is set", pnum); + else + dbg_bld("first PEB %d is newer, copy_flag is set", pnum); + + return second_is_newer | (bitflips << 1) | (corrupted << 2); + +out_free_buf: + kfree(buf); +out_free_vidh: + ubi_free_vid_hdr(ubi, vidh); + ubi_assert(err < 0); + return err; +} + +/** + * ubi_scan_add_used - add information about a physical eraseblock to the + * scanning information. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: the physical eraseblock number + * @ec: erase counter + * @vid_hdr: the volume identifier header + * @bitflips: if bit-flips were detected when this physical eraseblock was read + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, + int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, + int bitflips) +{ + int err, vol_id, lnum; + uint32_t leb_ver; + unsigned long long sqnum; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct rb_node **p, *parent = NULL; + + vol_id = ubi32_to_cpu(vid_hdr->vol_id); + lnum = ubi32_to_cpu(vid_hdr->lnum); + sqnum = ubi64_to_cpu(vid_hdr->sqnum); + leb_ver = ubi32_to_cpu(vid_hdr->leb_ver); + + dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d", + pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips); + + sv = add_volume(si, vol_id, pnum, vid_hdr); + if (IS_ERR(sv) < 0) + return PTR_ERR(sv); + + /* + * Walk the RB-tree of logical eraseblocks of volume @vol_id to look + * if this is the first instance of this logical eraseblock or not. + */ + p = &sv->root.rb_node; + while (*p) { + int cmp_res; + + parent = *p; + seb = rb_entry(parent, struct ubi_scan_leb, u.rb); + if (lnum != seb->lnum) { + if (lnum < seb->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + continue; + } + + /* + * There is already a physical eraseblock describing the same + * logical eraseblock present. + */ + + dbg_bld("this LEB already exists: PEB %d, sqnum %llu, " + "LEB ver %u, EC %d", seb->pnum, seb->sqnum, + seb->leb_ver, seb->ec); + + /* + * Make sure that the logical eraseblocks have different + * versions. Otherwise the image is bad. + */ + if (seb->leb_ver == leb_ver && leb_ver != 0) { + ubi_err("two LEBs with same version %u", leb_ver); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Make sure that the logical eraseblocks have different + * sequence numbers. Otherwise the image is bad. + * + * FIXME: remove 'sqnum != 0' check when leb_ver is removed. + */ + if (seb->sqnum == sqnum && sqnum != 0) { + ubi_err("two LEBs with same sequence number %llu", + sqnum); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Now we have to drop the older one and preserve the newer + * one. + */ + cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr); + if (cmp_res < 0) + return cmp_res; + + if (cmp_res & 1) { + /* + * This logical eraseblock is newer then the one + * found earlier. + */ + err = validate_vid_hdr(vid_hdr, sv, pnum); + if (err) + return err; + + if (cmp_res & 4) + err = ubi_scan_add_to_list(si, seb->pnum, + seb->ec, &si->corr); + else + err = ubi_scan_add_to_list(si, seb->pnum, + seb->ec, &si->erase); + if (err) + return err; + + seb->ec = ec; + seb->pnum = pnum; + seb->scrub = ((cmp_res & 2) || bitflips); + seb->sqnum = sqnum; + seb->leb_ver = leb_ver; + + if (sv->highest_lnum == lnum) + sv->last_data_size = + ubi32_to_cpu(vid_hdr->data_size); + + return 0; + } else { + /* + * This logical eraseblock is older then the one found + * previously. + */ + if (cmp_res & 4) + return ubi_scan_add_to_list(si, pnum, ec, + &si->corr); + else + return ubi_scan_add_to_list(si, pnum, ec, + &si->erase); + } + } + + /* + * We've met this logical eraseblock for the first time, add it to the + * scanning information. + */ + + err = validate_vid_hdr(vid_hdr, sv, pnum); + if (err) + return err; + + seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); + if (!seb) + return -ENOMEM; + + seb->ec = ec; + seb->pnum = pnum; + seb->lnum = lnum; + seb->sqnum = sqnum; + seb->scrub = bitflips; + seb->leb_ver = leb_ver; + + if (sv->highest_lnum <= lnum) { + sv->highest_lnum = lnum; + sv->last_data_size = ubi32_to_cpu(vid_hdr->data_size); + } + + if (si->max_sqnum < sqnum) + si->max_sqnum = sqnum; + + sv->leb_count += 1; + rb_link_node(&seb->u.rb, parent, p); + rb_insert_color(&seb->u.rb, &sv->root); + return 0; +} + +/** + * ubi_scan_find_sv - find information about a particular volume in the + * scanning information. + * @si: scanning information + * @vol_id: the requested volume ID + * + * This function returns a pointer to the volume description or %NULL if there + * are no data about this volume in the scanning information. + */ +struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, + int vol_id) +{ + struct ubi_scan_volume *sv; + struct rb_node *p = si->volumes.rb_node; + + while (p) { + sv = rb_entry(p, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) + return sv; + + if (vol_id > sv->vol_id) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +/** + * ubi_scan_find_seb - find information about a particular logical + * eraseblock in the volume scanning information. + * @sv: a pointer to the volume scanning information + * @lnum: the requested logical eraseblock + * + * This function returns a pointer to the scanning logical eraseblock or %NULL + * if there are no data about it in the scanning volume information. + */ +struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, + int lnum) +{ + struct ubi_scan_leb *seb; + struct rb_node *p = sv->root.rb_node; + + while (p) { + seb = rb_entry(p, struct ubi_scan_leb, u.rb); + + if (lnum == seb->lnum) + return seb; + + if (lnum > seb->lnum) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +/** + * ubi_scan_rm_volume - delete scanning information about a volume. + * @si: scanning information + * @sv: the volume scanning information to delete + */ +void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv) +{ + struct rb_node *rb; + struct ubi_scan_leb *seb; + + dbg_bld("remove scanning information about volume %d", sv->vol_id); + + while ((rb = rb_first(&sv->root))) { + seb = rb_entry(rb, struct ubi_scan_leb, u.rb); + rb_erase(&seb->u.rb, &sv->root); + list_add_tail(&seb->u.list, &si->erase); + } + + rb_erase(&sv->rb, &si->volumes); + kfree(sv); + si->vols_found -= 1; +} + +/** + * ubi_scan_erase_peb - erase a physical eraseblock. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: physical eraseblock number to erase; + * @ec: erase counter value to write (%UBI_SCAN_UNKNOWN_EC if it is unknown) + * + * This function erases physical eraseblock 'pnum', and writes the erase + * counter header to it. This function should only be used on UBI device + * initialization stages, when the EBA unit had not been yet initialized. This + * function returns zero in case of success and a negative error code in case + * of failure. + */ +int ubi_scan_erase_peb(const struct ubi_device *ubi, + const struct ubi_scan_info *si, int pnum, int ec) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + if ((long long)ec >= UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %d", pnum, ec); + return -EINVAL; + } + + ec_hdr->ec = cpu_to_ubi64(ec); + + err = ubi_io_sync_erase(ubi, pnum, 0); + if (err < 0) + goto out_free; + + err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * ubi_scan_get_free_peb - get a free physical eraseblock. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns a free physical eraseblock. It is supposed to be + * called on the UBI initialization stages when the wear-leveling unit is not + * initialized yet. This function picks a physical eraseblocks from one of the + * lists, writes the EC header if it is needed, and removes it from the list. + * + * This function returns scanning physical eraseblock information in case of + * success and an error code in case of failure. + */ +struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int err = 0, i; + struct ubi_scan_leb *seb; + + if (!list_empty(&si->free)) { + seb = list_entry(si->free.next, struct ubi_scan_leb, u.list); + list_del(&seb->u.list); + dbg_bld("return free PEB %d, EC %d", seb->pnum, seb->ec); + return seb; + } + + for (i = 0; i < 2; i++) { + struct list_head *head; + struct ubi_scan_leb *tmp_seb; + + if (i == 0) + head = &si->erase; + else + head = &si->corr; + + /* + * We try to erase the first physical eraseblock from the @head + * list and pick it if we succeed, or try to erase the + * next one if not. And so forth. We don't want to take care + * about bad eraseblocks here - they'll be handled later. + */ + list_for_each_entry_safe(seb, tmp_seb, head, u.list) { + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + err = ubi_scan_erase_peb(ubi, si, seb->pnum, seb->ec+1); + if (err) + continue; + + seb->ec += 1; + list_del(&seb->u.list); + dbg_bld("return PEB %d, EC %d", seb->pnum, seb->ec); + return seb; + } + } + + ubi_err("no eraseblocks found"); + return ERR_PTR(-ENOSPC); +} + +/** + * process_eb - read UBI headers, check them and add corresponding data + * to the scanning information. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: the physical eraseblock number + * + * This function returns a zero if the physical eraseblock was succesfully + * handled and a negative error code in case of failure. + */ +static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) +{ + long long ec; + int err, bitflips = 0, vol_id, ec_corr = 0; + + dbg_bld("scan PEB %d", pnum); + + /* Skip bad physical eraseblocks */ + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) + return err; + else if (err) { + /* + * FIXME: this is actually duty of the I/O unit to initialize + * this, but MTD does not provide enough information. + */ + si->bad_peb_count += 1; + return 0; + } + + err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + if (err < 0) + return err; + else if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else if (err == UBI_IO_PEB_EMPTY) + return ubi_scan_add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, + &si->erase); + else if (err == UBI_IO_BAD_EC_HDR) { + /* + * We have to also look at the VID header, possibly it is not + * corrupted. Set %bitflips flag in order to make this PEB be + * moved and EC be re-created. + */ + ec_corr = 1; + ec = UBI_SCAN_UNKNOWN_EC; + bitflips = 1; + } + + si->is_empty = 0; + + if (!ec_corr) { + /* Make sure UBI version is OK */ + if (ech->version != UBI_VERSION) { + ubi_err("this UBI version is %d, image version is %d", + UBI_VERSION, (int)ech->version); + return -EINVAL; + } + + ec = ubi64_to_cpu(ech->ec); + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. The EC headers have 64 bits + * reserved, but we anyway make use of only 31 bit + * values, as this seems to be enough for any existing + * flash. Upgrade UBI and use 64-bit erase counters + * internally. + */ + ubi_err("erase counter overflow, max is %d", + UBI_MAX_ERASECOUNTER); + ubi_dbg_dump_ec_hdr(ech); + return -EINVAL; + } + } + + /* OK, we've done with the EC header, let's look at the VID header */ + + err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + if (err < 0) + return err; + else if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else if (err == UBI_IO_BAD_VID_HDR || + (err == UBI_IO_PEB_FREE && ec_corr)) { + /* VID header is corrupted */ + err = ubi_scan_add_to_list(si, pnum, ec, &si->corr); + if (err) + return err; + goto adjust_mean_ec; + } else if (err == UBI_IO_PEB_FREE) { + /* No VID header - the physical eraseblock is free */ + err = ubi_scan_add_to_list(si, pnum, ec, &si->free); + if (err) + return err; + goto adjust_mean_ec; + } + + vol_id = ubi32_to_cpu(vidh->vol_id); + if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) { + int lnum = ubi32_to_cpu(vidh->lnum); + + /* Unsupported internal volume */ + switch (vidh->compat) { + case UBI_COMPAT_DELETE: + ubi_msg("\"delete\" compatible internal volume %d:%d" + " found, remove it", vol_id, lnum); + err = ubi_scan_add_to_list(si, pnum, ec, &si->corr); + if (err) + return err; + break; + + case UBI_COMPAT_RO: + ubi_msg("read-only compatible internal volume %d:%d" + " found, switch to read-only mode", + vol_id, lnum); + ubi->ro_mode = 1; + break; + + case UBI_COMPAT_PRESERVE: + ubi_msg("\"preserve\" compatible internal volume %d:%d" + " found", vol_id, lnum); + err = ubi_scan_add_to_list(si, pnum, ec, &si->alien); + if (err) + return err; + si->alien_peb_count += 1; + return 0; + + case UBI_COMPAT_REJECT: + ubi_err("incompatible internal volume %d:%d found", + vol_id, lnum); + return -EINVAL; + } + } + + /* Both UBI headers seem to be fine */ + err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips); + if (err) + return err; + +adjust_mean_ec: + if (!ec_corr) { + if (si->ec_sum + ec < ec) { + commit_to_mean_value(si); + si->ec_sum = 0; + si->ec_count = 0; + } else { + si->ec_sum += ec; + si->ec_count += 1; + } + + if (ec > si->max_ec) + si->max_ec = ec; + if (ec < si->min_ec) + si->min_ec = ec; + } + + return 0; +} + +/** + * ubi_scan - scan an MTD device. + * @ubi: UBI device description object + * + * This function does full scanning of an MTD device and returns complete + * information about it. In case of failure, an error code is returned. + */ +struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) +{ + int err, pnum; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct ubi_scan_info *si; + + si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL); + if (!si) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&si->corr); + INIT_LIST_HEAD(&si->free); + INIT_LIST_HEAD(&si->erase); + INIT_LIST_HEAD(&si->alien); + si->volumes = RB_ROOT; + si->is_empty = 1; + + err = -ENOMEM; + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) + goto out_si; + + vidh = ubi_zalloc_vid_hdr(ubi); + if (!vidh) + goto out_ech; + + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + cond_resched(); + + dbg_msg("process PEB %d", pnum); + err = process_eb(ubi, si, pnum); + if (err < 0) + goto out_vidh; + } + + dbg_msg("scanning is finished"); + + /* Finish mean erase counter calculations */ + if (si->ec_count) + commit_to_mean_value(si); + + if (si->is_empty) + ubi_msg("empty MTD device detected"); + + /* + * In case of unknown erase counter we use the mean erase counter + * value. + */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + } + + list_for_each_entry(seb, &si->free, u.list) { + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + } + + list_for_each_entry(seb, &si->corr, u.list) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + list_for_each_entry(seb, &si->erase, u.list) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + err = paranoid_check_si(ubi, si); + if (err) { + if (err > 0) + err = -EINVAL; + goto out_vidh; + } + + ubi_free_vid_hdr(ubi, vidh); + kfree(ech); + + return si; + +out_vidh: + ubi_free_vid_hdr(ubi, vidh); +out_ech: + kfree(ech); +out_si: + ubi_scan_destroy_si(si); + return ERR_PTR(err); +} + +/** + * destroy_sv - free the scanning volume information + * @sv: scanning volume information + * + * This function destroys the volume RB-tree (@sv->root) and the scanning + * volume information. + */ +static void destroy_sv(struct ubi_scan_volume *sv) +{ + struct ubi_scan_leb *seb; + struct rb_node *this = sv->root.rb_node; + + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + seb = rb_entry(this, struct ubi_scan_leb, u.rb); + this = rb_parent(this); + if (this) { + if (this->rb_left == &seb->u.rb) + this->rb_left = NULL; + else + this->rb_right = NULL; + } + + kfree(seb); + } + } + kfree(sv); +} + +/** + * ubi_scan_destroy_si - destroy scanning information. + * @si: scanning information + */ +void ubi_scan_destroy_si(struct ubi_scan_info *si) +{ + struct ubi_scan_leb *seb, *seb_tmp; + struct ubi_scan_volume *sv; + struct rb_node *rb; + + list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + + /* Destroy the volume RB-tree */ + rb = si->volumes.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + sv = rb_entry(rb, struct ubi_scan_volume, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &sv->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + destroy_sv(sv); + } + } + + kfree(si); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_si - check if the scanning information is correct and + * consistent. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero if the scanning information is all right, %1 if + * not and a negative error code if an error occurred. + */ +static int paranoid_check_si(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int pnum, err, vols_found = 0; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *last_seb; + uint8_t *buf; + + /* + * At first, check that scanning information is ok. + */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + int leb_count = 0; + + cond_resched(); + + vols_found += 1; + + if (si->is_empty) { + ubi_err("bad is_empty flag"); + goto bad_sv; + } + + if (sv->vol_id < 0 || sv->highest_lnum < 0 || + sv->leb_count < 0 || sv->vol_type < 0 || sv->used_ebs < 0 || + sv->data_pad < 0 || sv->last_data_size < 0) { + ubi_err("negative values"); + goto bad_sv; + } + + if (sv->vol_id >= UBI_MAX_VOLUMES && + sv->vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("bad vol_id"); + goto bad_sv; + } + + if (sv->vol_id > si->highest_vol_id) { + ubi_err("highest_vol_id is %d, but vol_id %d is there", + si->highest_vol_id, sv->vol_id); + goto out; + } + + if (sv->vol_type != UBI_DYNAMIC_VOLUME && + sv->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto bad_sv; + } + + if (sv->data_pad > ubi->leb_size / 2) { + ubi_err("bad data_pad"); + goto bad_sv; + } + + last_seb = NULL; + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + + last_seb = seb; + leb_count += 1; + + if (seb->pnum < 0 || seb->ec < 0) { + ubi_err("negative values"); + goto bad_seb; + } + + if (seb->ec < si->min_ec) { + ubi_err("bad si->min_ec (%d), %d found", + si->min_ec, seb->ec); + goto bad_seb; + } + + if (seb->ec > si->max_ec) { + ubi_err("bad si->max_ec (%d), %d found", + si->max_ec, seb->ec); + goto bad_seb; + } + + if (seb->pnum >= ubi->peb_count) { + ubi_err("too high PEB number %d, total PEBs %d", + seb->pnum, ubi->peb_count); + goto bad_seb; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) { + if (seb->lnum >= sv->used_ebs) { + ubi_err("bad lnum or used_ebs"); + goto bad_seb; + } + } else { + if (sv->used_ebs != 0) { + ubi_err("non-zero used_ebs"); + goto bad_seb; + } + } + + if (seb->lnum > sv->highest_lnum) { + ubi_err("incorrect highest_lnum or lnum"); + goto bad_seb; + } + } + + if (sv->leb_count != leb_count) { + ubi_err("bad leb_count, %d objects in the tree", + leb_count); + goto bad_sv; + } + + if (!last_seb) + continue; + + seb = last_seb; + + if (seb->lnum != sv->highest_lnum) { + ubi_err("bad highest_lnum"); + goto bad_seb; + } + } + + if (vols_found != si->vols_found) { + ubi_err("bad si->vols_found %d, should be %d", + si->vols_found, vols_found); + goto out; + } + + /* Check that scanning information is correct */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + last_seb = NULL; + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + int vol_type; + + cond_resched(); + + last_seb = seb; + + err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1); + if (err && err != UBI_IO_BITFLIPS) { + ubi_err("VID header is not OK (%d)", err); + if (err > 0) + err = -EIO; + return err; + } + + vol_type = vidh->vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + if (sv->vol_type != vol_type) { + ubi_err("bad vol_type"); + goto bad_vid_hdr; + } + + if (seb->sqnum != ubi64_to_cpu(vidh->sqnum)) { + ubi_err("bad sqnum %llu", seb->sqnum); + goto bad_vid_hdr; + } + + if (sv->vol_id != ubi32_to_cpu(vidh->vol_id)) { + ubi_err("bad vol_id %d", sv->vol_id); + goto bad_vid_hdr; + } + + if (sv->compat != vidh->compat) { + ubi_err("bad compat %d", vidh->compat); + goto bad_vid_hdr; + } + + if (seb->lnum != ubi32_to_cpu(vidh->lnum)) { + ubi_err("bad lnum %d", seb->lnum); + goto bad_vid_hdr; + } + + if (sv->used_ebs != ubi32_to_cpu(vidh->used_ebs)) { + ubi_err("bad used_ebs %d", sv->used_ebs); + goto bad_vid_hdr; + } + + if (sv->data_pad != ubi32_to_cpu(vidh->data_pad)) { + ubi_err("bad data_pad %d", sv->data_pad); + goto bad_vid_hdr; + } + + if (seb->leb_ver != ubi32_to_cpu(vidh->leb_ver)) { + ubi_err("bad leb_ver %u", seb->leb_ver); + goto bad_vid_hdr; + } + } + + if (!last_seb) + continue; + + if (sv->highest_lnum != ubi32_to_cpu(vidh->lnum)) { + ubi_err("bad highest_lnum %d", sv->highest_lnum); + goto bad_vid_hdr; + } + + if (sv->last_data_size != ubi32_to_cpu(vidh->data_size)) { + ubi_err("bad last_data_size %d", sv->last_data_size); + goto bad_vid_hdr; + } + } + + /* + * Make sure that all the physical eraseblocks are in one of the lists + * or trees. + */ + buf = kmalloc(ubi->peb_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memset(buf, 1, ubi->peb_count); + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) + return err; + else if (err) + buf[pnum] = 0; + } + + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->free, u.list) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->corr, u.list) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->erase, u.list) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->alien, u.list) + buf[seb->pnum] = 0; + + err = 0; + for (pnum = 0; pnum < ubi->peb_count; pnum++) + if (buf[pnum]) { + ubi_err("PEB %d is not referred", pnum); + err = 1; + } + + kfree(buf); + if (err) + goto out; + return 0; + +bad_seb: + ubi_err("bad scanning information about LEB %d", seb->lnum); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_sv(sv); + goto out; + +bad_sv: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + goto out; + +bad_vid_hdr: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vid_hdr(vidh); + +out: + ubi_dbg_dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h new file mode 100644 index 0000000..3949f61 --- /dev/null +++ b/drivers/mtd/ubi/scan.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_SCAN_H__ +#define __UBI_SCAN_H__ + +/* The erase counter value for this physical eraseblock is unknown */ +#define UBI_SCAN_UNKNOWN_EC (-1) + +/** + * struct ubi_scan_leb - scanning information about a physical eraseblock. + * @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown) + * @pnum: physical eraseblock number + * @lnum: logical eraseblock number + * @scrub: if this physical eraseblock needs scrubbing + * @sqnum: sequence number + * @u: unions RB-tree or @list links + * @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects + * @u.list: link in one of the eraseblock lists + * @leb_ver: logical eraseblock version (obsolete) + * + * One object of this type is allocated for each physical eraseblock during + * scanning. + */ +struct ubi_scan_leb { + int ec; + int pnum; + int lnum; + int scrub; + unsigned long long sqnum; + union { + struct rb_node rb; + struct list_head list; + } u; + uint32_t leb_ver; +}; + +/** + * struct ubi_scan_volume - scanning information about a volume. + * @vol_id: volume ID + * @highest_lnum: highest logical eraseblock number in this volume + * @leb_count: number of logical eraseblocks in this volume + * @vol_type: volume type + * @used_ebs: number of used logical eraseblocks in this volume (only for + * static volumes) + * @last_data_size: amount of data in the last logical eraseblock of this + * volume (always equivalent to the usable logical eraseblock size in case of + * dynamic volumes) + * @data_pad: how many bytes at the end of logical eraseblocks of this volume + * are not used (due to volume alignment) + * @compat: compatibility flags of this volume + * @rb: link in the volume RB-tree + * @root: root of the RB-tree containing all the eraseblock belonging to this + * volume (&struct ubi_scan_leb objects) + * + * One object of this type is allocated for each volume during scanning. + */ +struct ubi_scan_volume { + int vol_id; + int highest_lnum; + int leb_count; + int vol_type; + int used_ebs; + int last_data_size; + int data_pad; + int compat; + struct rb_node rb; + struct rb_root root; +}; + +/** + * struct ubi_scan_info - UBI scanning information. + * @volumes: root of the volume RB-tree + * @corr: list of corrupted physical eraseblocks + * @free: list of free physical eraseblocks + * @erase: list of physical eraseblocks which have to be erased + * @alien: list of physical eraseblocks which should not be used by UBI (e.g., + * @bad_peb_count: count of bad physical eraseblocks + * those belonging to "preserve"-compatible internal volumes) + * @vols_found: number of volumes found during scanning + * @highest_vol_id: highest volume ID + * @alien_peb_count: count of physical eraseblocks in the @alien list + * @is_empty: flag indicating whether the MTD device is empty or not + * @min_ec: lowest erase counter value + * @max_ec: highest erase counter value + * @max_sqnum: highest sequence number value + * @mean_ec: mean erase counter value + * @ec_sum: a temporary variable used when calculating @mean_ec + * @ec_count: a temporary variable used when calculating @mean_ec + * + * This data structure contains the result of scanning and may be used by other + * UBI units to build final UBI data structures, further error-recovery and so + * on. + */ +struct ubi_scan_info { + struct rb_root volumes; + struct list_head corr; + struct list_head free; + struct list_head erase; + struct list_head alien; + int bad_peb_count; + int vols_found; + int highest_vol_id; + int alien_peb_count; + int is_empty; + int min_ec; + int max_ec; + unsigned long long max_sqnum; + int mean_ec; + int ec_sum; + int ec_count; +}; + +struct ubi_device; +struct ubi_vid_hdr; + +/* + * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a + * list. + * + * @sv: volume scanning information + * @seb: scanning eraseblock infprmation + * @list: the list to move to + */ +static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv, + struct ubi_scan_leb *seb, + struct list_head *list) +{ + rb_erase(&seb->u.rb, &sv->root); + list_add_tail(&seb->u.list, list); +} + +int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec, + struct list_head *list); +int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, + int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, + int bitflips); +struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, + int vol_id); +struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, + int lnum); +void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv); +struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi, + struct ubi_scan_info *si); +int ubi_scan_erase_peb(const struct ubi_device *ubi, + const struct ubi_scan_info *si, int pnum, int ec); +struct ubi_scan_info *ubi_scan(struct ubi_device *ubi); +void ubi_scan_destroy_si(struct ubi_scan_info *si); + +#endif /* !__UBI_SCAN_H__ */ diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h new file mode 100644 index 0000000..feb647f --- /dev/null +++ b/drivers/mtd/ubi/ubi.h @@ -0,0 +1,535 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_UBI_H__ +#define __UBI_UBI_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "scan.h" +#include "debug.h" + +/* Maximum number of supported UBI devices */ +#define UBI_MAX_DEVICES 32 + +/* UBI name used for character devices, sysfs, etc */ +#define UBI_NAME_STR "ubi" + +/* Normal UBI messages */ +#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__) +/* UBI warning messages */ +#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \ + __FUNCTION__, ##__VA_ARGS__) +/* UBI error messages */ +#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \ + __FUNCTION__, ##__VA_ARGS__) + +/* Lowest number PEBs reserved for bad PEB handling */ +#define MIN_RESEVED_PEBS 2 + +/* Background thread name pattern */ +#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd" + +/* This marker in the EBA table means that the LEB is um-mapped */ +#define UBI_LEB_UNMAPPED -1 + +/* + * In case of errors, UBI tries to repeat the operation several times before + * returning error. The below constant defines how many times UBI re-tries. + */ +#define UBI_IO_RETRIES 3 + +/* + * Error codes returned by the I/O unit. + * + * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only + * 0xFF bytes + * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a + * valid erase counter header, and the rest are %0xFF bytes + * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC) + * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or + * CRC) + * UBI_IO_BITFLIPS: bit-flips were detected and corrected + */ +enum { + UBI_IO_PEB_EMPTY = 1, + UBI_IO_PEB_FREE, + UBI_IO_BAD_EC_HDR, + UBI_IO_BAD_VID_HDR, + UBI_IO_BITFLIPS +}; + +extern int ubi_devices_cnt; +extern struct ubi_device *ubi_devices[]; + +struct ubi_volume_desc; + +/** + * struct ubi_volume - UBI volume description data structure. + * @dev: device object to make use of the the Linux device model + * @cdev: character device object to create character device + * @ubi: reference to the UBI device description object + * @vol_id: volume ID + * @readers: number of users holding this volume in read-only mode + * @writers: number of users holding this volume in read-write mode + * @exclusive: whether somebody holds this volume in exclusive mode + * @removed: if the volume was removed + * @checked: if this static volume was checked + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @usable_leb_size: logical eraseblock size without padding + * @used_ebs: how many logical eraseblocks in this volume contain data + * @last_eb_bytes: how many bytes are stored in the last logical eraseblock + * @used_bytes: how many bytes of data this volume contains + * @upd_marker: non-zero if the update marker is set for this volume + * @corrupted: non-zero if the volume is corrupted (static volumes only) + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of physical eraseblocks to + * satisfy the requested alignment + * @name_len: volume name length + * @name: volume name + * + * @updating: whether the volume is being updated + * @upd_ebs: how many eraseblocks are expected to be updated + * @upd_bytes: how many bytes are expected to be received + * @upd_received: how many update bytes were already received + * @upd_buf: update buffer which is used to collect update data + * + * @eba_tbl: EBA table of this volume (LEB->PEB mapping) + * + * @gluebi_desc: gluebi UBI volume descriptor + * @gluebi_refcount: reference count of the gluebi MTD device + * @gluebi_mtd: MTD device description object of the gluebi MTD device + * + * The @corrupted field indicates that the volume's contents is corrupted. + * Since UBI protects only static volumes, this field is not relevant to + * dynamic volumes - it is user's responsibility to assure their data + * integrity. + * + * The @upd_marker flag indicates that this volume is either being updated at + * the moment or is damaged because of an unclean reboot. + */ +struct ubi_volume { + struct device dev; + struct cdev cdev; + struct ubi_device *ubi; + int vol_id; + int readers; + int writers; + int exclusive; + int removed; + int checked; + + int reserved_pebs; + int vol_type; + int usable_leb_size; + int used_ebs; + int last_eb_bytes; + long long used_bytes; + int upd_marker; + int corrupted; + int alignment; + int data_pad; + int name_len; + char name[UBI_VOL_NAME_MAX+1]; + + int updating; + int upd_ebs; + long long upd_bytes; + long long upd_received; + void *upd_buf; + + int *eba_tbl; + +#ifdef CONFIG_MTD_UBI_GLUEBI + /* Gluebi-related stuff may be compiled out */ + struct ubi_volume_desc *gluebi_desc; + int gluebi_refcount; + struct mtd_info gluebi_mtd; +#endif +}; + +/** + * struct ubi_volume_desc - descriptor of the UBI volume returned when it is + * opened. + * @vol: reference to the corresponding volume description object + * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) + */ +struct ubi_volume_desc { + struct ubi_volume *vol; + int mode; +}; + +struct ubi_wl_entry; + +/** + * struct ubi_device - UBI device description structure + * @dev: class device object to use the the Linux device model + * @cdev: character device object to create character device + * @ubi_num: UBI device number + * @ubi_name: UBI device name + * @major: character device major number + * @vol_count: number of volumes in this UBI device + * @volumes: volumes of this UBI device + * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, + * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, @vol->readers, + * @vol->writers, @vol->exclusive, @vol->removed, @vol->mapping and + * @vol->eba_tbl. + * + * @rsvd_pebs: count of reserved physical eraseblocks + * @avail_pebs: count of available physical eraseblocks + * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB + * handling + * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling + * + * @vtbl_slots: how many slots are available in the volume table + * @vtbl_size: size of the volume table in bytes + * @vtbl: in-RAM volume table copy + * + * @max_ec: current highest erase counter value + * @mean_ec: current mean erase counter value + * + * global_sqnum: global sequence number + * @ltree_lock: protects the lock tree and @global_sqnum + * @ltree: the lock tree + * @vtbl_mutex: protects on-flash volume table + * + * @used: RB-tree of used physical eraseblocks + * @free: RB-tree of free physical eraseblocks + * @scrub: RB-tree of physical eraseblocks which need scrubbing + * @prot: protection trees + * @prot.pnum: protection tree indexed by physical eraseblock numbers + * @prot.aec: protection tree indexed by absolute erase counter value + * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, + * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works + * fields + * @wl_scheduled: non-zero if the wear-leveling was scheduled + * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any + * physical eraseblock + * @abs_ec: absolute erase counter + * @move_from: physical eraseblock from where the data is being moved + * @move_to: physical eraseblock where the data is being moved to + * @move_from_put: if the "from" PEB was put + * @move_to_put: if the "to" PEB was put + * @works: list of pending works + * @works_count: count of pending works + * @bgt_thread: background thread description object + * @thread_enabled: if the background thread is enabled + * @bgt_name: background thread name + * + * @flash_size: underlying MTD device size (in bytes) + * @peb_count: count of physical eraseblocks on the MTD device + * @peb_size: physical eraseblock size + * @bad_peb_count: count of bad physical eraseblocks + * @good_peb_count: count of good physical eraseblocks + * @min_io_size: minimal input/output unit size of the underlying MTD device + * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers + * @ro_mode: if the UBI device is in read-only mode + * @leb_size: logical eraseblock size + * @leb_start: starting offset of logical eraseblocks within physical + * eraseblocks + * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size + * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size + * @vid_hdr_offset: starting offset of the volume identifier header (might be + * unaligned) + * @vid_hdr_aloffset: starting offset of the VID header aligned to + * @hdrs_min_io_size + * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset + * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or + * not + * @mtd: MTD device descriptor + */ +struct ubi_device { + struct cdev cdev; + struct device dev; + int ubi_num; + char ubi_name[sizeof(UBI_NAME_STR)+5]; + int major; + int vol_count; + struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; + spinlock_t volumes_lock; + + int rsvd_pebs; + int avail_pebs; + int beb_rsvd_pebs; + int beb_rsvd_level; + + int vtbl_slots; + int vtbl_size; + struct ubi_vtbl_record *vtbl; + struct mutex vtbl_mutex; + + int max_ec; + int mean_ec; + + /* EBA unit's stuff */ + unsigned long long global_sqnum; + spinlock_t ltree_lock; + struct rb_root ltree; + + /* Wear-leveling unit's stuff */ + struct rb_root used; + struct rb_root free; + struct rb_root scrub; + struct { + struct rb_root pnum; + struct rb_root aec; + } prot; + spinlock_t wl_lock; + int wl_scheduled; + struct ubi_wl_entry **lookuptbl; + unsigned long long abs_ec; + struct ubi_wl_entry *move_from; + struct ubi_wl_entry *move_to; + int move_from_put; + int move_to_put; + struct list_head works; + int works_count; + struct task_struct *bgt_thread; + int thread_enabled; + char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; + + /* I/O unit's stuff */ + long long flash_size; + int peb_count; + int peb_size; + int bad_peb_count; + int good_peb_count; + int min_io_size; + int hdrs_min_io_size; + int ro_mode; + int leb_size; + int leb_start; + int ec_hdr_alsize; + int vid_hdr_alsize; + int vid_hdr_offset; + int vid_hdr_aloffset; + int vid_hdr_shift; + int bad_allowed; + struct mtd_info *mtd; +}; + +extern struct file_operations ubi_cdev_operations; +extern struct file_operations ubi_vol_cdev_operations; +extern struct class *ubi_class; + +/* vtbl.c */ +int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + struct ubi_vtbl_record *vtbl_rec); +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si); + +/* vmt.c */ +int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req); +int ubi_remove_volume(struct ubi_volume_desc *desc); +int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); +int ubi_add_volume(struct ubi_device *ubi, int vol_id); +void ubi_free_volume(struct ubi_device *ubi, int vol_id); + +/* upd.c */ +int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes); +int ubi_more_update_data(struct ubi_device *ubi, int vol_id, + const void __user *buf, int count); + +/* misc.c */ +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); +int ubi_check_volume(struct ubi_device *ubi, int vol_id); +void ubi_calculate_reserved(struct ubi_device *ubi); + +/* gluebi.c */ +#ifdef CONFIG_MTD_UBI_GLUEBI +int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol); +int ubi_destroy_gluebi(struct ubi_volume *vol); +#else +#define ubi_create_gluebi(ubi, vol) 0 +#define ubi_destroy_gluebi(vol) 0 +#endif + +/* eba.c */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum); +int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + int offset, int len, int check); +int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int offset, int len, int dtype); +int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype, + int used_ebs); +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype); +int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr); +int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +void ubi_eba_close(const struct ubi_device *ubi); + +/* wl.c */ +int ubi_wl_get_peb(struct ubi_device *ubi, int dtype); +int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture); +int ubi_wl_flush(struct ubi_device *ubi); +int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +void ubi_wl_close(struct ubi_device *ubi); + +/* io.c */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len); +int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len); +int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture); +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose); +int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr); +int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose); +int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr); + +/* + * ubi_rb_for_each_entry - walk an RB-tree. + * @rb: a pointer to type 'struct rb_node' to to use as a loop counter + * @pos: a pointer to RB-tree entry type to use as a loop counter + * @root: RB-tree's root + * @member: the name of the 'struct rb_node' within the RB-tree entry + */ +#define ubi_rb_for_each_entry(rb, pos, root, member) \ + for (rb = rb_first(root), \ + pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \ + rb; \ + rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member)) + +/** + * ubi_zalloc_vid_hdr - allocate a volume identifier header object. + * @ubi: UBI device description object + * + * This function returns a pointer to the newly allocated and zero-filled + * volume identifier header object in case of success and %NULL in case of + * failure. + */ +static inline struct ubi_vid_hdr *ubi_zalloc_vid_hdr(const struct ubi_device *ubi) +{ + void *vid_hdr; + + vid_hdr = kzalloc(ubi->vid_hdr_alsize, GFP_KERNEL); + if (!vid_hdr) + return NULL; + + /* + * VID headers may be stored at un-aligned flash offsets, so we shift + * the pointer. + */ + return vid_hdr + ubi->vid_hdr_shift; +} + +/** + * ubi_free_vid_hdr - free a volume identifier header object. + * @ubi: UBI device description object + * @vid_hdr: the object to free + */ +static inline void ubi_free_vid_hdr(const struct ubi_device *ubi, + struct ubi_vid_hdr *vid_hdr) +{ + void *p = vid_hdr; + + if (!p) + return; + + kfree(p - ubi->vid_hdr_shift); +} + +/* + * This function is equivalent to 'ubi_io_read()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_read_data(const struct ubi_device *ubi, void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_read(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/* + * This function is equivalent to 'ubi_io_write()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_write_data(const struct ubi_device *ubi, const void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/** + * ubi_ro_mode - switch to read-only mode. + * @ubi: UBI device description object + */ +static inline void ubi_ro_mode(struct ubi_device *ubi) +{ + ubi->ro_mode = 1; + ubi_warn("switch to read-only mode"); +} + +/** + * vol_id2idx - get table index by volume ID. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static inline int vol_id2idx(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id >= UBI_INTERNAL_VOL_START) + return vol_id - UBI_INTERNAL_VOL_START + ubi->vtbl_slots; + else + return vol_id; +} + +/** + * idx2vol_id - get volume ID by table index. + * @ubi: UBI device description object + * @idx: table index + */ +static inline int idx2vol_id(const struct ubi_device *ubi, int idx) +{ + if (idx >= ubi->vtbl_slots) + return idx - ubi->vtbl_slots + UBI_INTERNAL_VOL_START; + else + return idx; +} + +#endif /* !__UBI_UBI_H__ */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c new file mode 100644 index 0000000..8925b97 --- /dev/null +++ b/drivers/mtd/ubi/upd.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + * + * Jan 2007: Alexander Schmidt, hacked per-volume update. + */ + +/* + * This file contains implementation of the volume update functionality. + * + * The update operation is based on the per-volume update marker which is + * stored in the volume table. The update marker is set before the update + * starts, and removed after the update has been finished. So if the update was + * interrupted by an unclean re-boot or due to some other reasons, the update + * marker stays on the flash media and UBI finds it when it attaches the MTD + * device next time. If the update marker is set for a volume, the volume is + * treated as damaged and most I/O operations are prohibited. Only a new update + * operation is allowed. + * + * Note, in general it is possible to implement the update operation as a + * transaction with a roll-back capability. + */ + +#include +#include +#include +#include "ubi.h" + +/** + * set_update_marker - set update marker. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function sets the update marker flag for volume @vol_id. Returns zero + * in case of success and a negative error code in case of failure. + */ +static int set_update_marker(struct ubi_device *ubi, int vol_id) +{ + int err; + struct ubi_vtbl_record vtbl_rec; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("set update marker for volume %d", vol_id); + + if (vol->upd_marker) { + ubi_assert(ubi->vtbl[vol_id].upd_marker); + dbg_msg("already set"); + return 0; + } + + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + vtbl_rec.upd_marker = 1; + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + vol->upd_marker = 1; + return err; +} + +/** + * clear_update_marker - clear update marker. + * @ubi: UBI device description object + * @vol_id: volume ID + * @bytes: new data size in bytes + * + * This function clears the update marker for volume @vol_id, sets new volume + * data size and clears the "corrupted" flag (static volumes only). Returns + * zero in case of success and a negative error code in case of failure. + */ +static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes) +{ + int err; + uint64_t tmp; + struct ubi_vtbl_record vtbl_rec; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("clear update marker for volume %d", vol_id); + + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); + vtbl_rec.upd_marker = 0; + + if (vol->vol_type == UBI_STATIC_VOLUME) { + vol->corrupted = 0; + vol->used_bytes = tmp = bytes; + vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size); + vol->used_ebs = tmp; + if (vol->last_eb_bytes) + vol->used_ebs += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + vol->upd_marker = 0; + return err; +} + +/** + * ubi_start_update - start volume update. + * @ubi: UBI device description object + * @vol_id: volume ID + * @bytes: update bytes + * + * This function starts volume update operation. If @bytes is zero, the volume + * is just wiped out. Returns zero in case of success and a negative error code + * in case of failure. + */ +int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) +{ + int i, err; + uint64_t tmp; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes); + vol->updating = 1; + + err = set_update_marker(ubi, vol_id); + if (err) + return err; + + /* Before updating - wipe out the volume */ + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol_id, i); + if (err) + return err; + } + + if (bytes == 0) { + err = clear_update_marker(ubi, vol_id, 0); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (!err) + vol->updating = 0; + } + + vol->upd_buf = kmalloc(ubi->leb_size, GFP_KERNEL); + if (!vol->upd_buf) + return -ENOMEM; + + tmp = bytes; + vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size); + vol->upd_ebs += tmp; + vol->upd_bytes = bytes; + vol->upd_received = 0; + return 0; +} + +/** + * write_leb - write update data. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data to write + * @len: data size + * @used_ebs: how many logical eraseblocks will this volume contain (static + * volumes only) + * + * This function writes update data to corresponding logical eraseblock. In + * case of dynamic volume, this function checks if the data contains 0xFF bytes + * at the end. If yes, the 0xFF bytes are cut and not written. So if the whole + * buffer contains only 0xFF bytes, the LEB is left unmapped. + * + * The reason why we skip the trailing 0xFF bytes in case of dynamic volume is + * that we want to make sure that more data may be appended to the logical + * eraseblock in future. Indeed, writing 0xFF bytes may have side effects and + * this PEB won't be writable anymore. So if one writes the file-system image + * to the UBI volume where 0xFFs mean free space - UBI makes sure this free + * space is writable after the update. + * + * We do not do this for static volumes because they are read-only. But this + * also cannot be done because we have to store per-LEB CRC and the correct + * data length. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + int len, int used_ebs) +{ + int err, l; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + l = ALIGN(len, ubi->min_io_size); + memset(buf + len, 0xFF, l - len); + + l = ubi_calc_data_len(ubi, buf, l); + if (l == 0) { + dbg_msg("all %d bytes contain 0xFF - skip", len); + return 0; + } + if (len != l) + dbg_msg("skip last %d bytes (0xFF)", len - l); + + err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l, + UBI_UNKNOWN); + } else { + /* + * When writing static volume, and this is the last logical + * eraseblock, the length (@len) does not have to be aligned to + * the minimal flash I/O unit. The 'ubi_eba_write_leb_st()' + * function accepts exact (unaligned) length and stores it in + * the VID header. And it takes care of proper alignment by + * padding the buffer. Here we just make sure the padding will + * contain zeros, not random trash. + */ + memset(buf + len, 0, vol->usable_leb_size - len); + err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len, + UBI_UNKNOWN, used_ebs); + } + + return err; +} + +/** + * ubi_more_update_data - write more update data. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function writes more data to the volume which is being updated. It may + * be called arbitrary number of times until all of the update data arrive. + * This function returns %0 in case of success, number of bytes written during + * the last call if the whole volume update was successfully finished, and a + * negative error code in case of failure. + */ +int ubi_more_update_data(struct ubi_device *ubi, int vol_id, + const void __user *buf, int count) +{ + uint64_t tmp; + struct ubi_volume *vol = ubi->volumes[vol_id]; + int lnum, offs, err = 0, len, to_write = count; + + dbg_msg("write %d of %lld bytes, %lld already passed", + count, vol->upd_bytes, vol->upd_received); + + if (ubi->ro_mode) + return -EROFS; + + tmp = vol->upd_received; + offs = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + + if (vol->upd_received + count > vol->upd_bytes) + to_write = count = vol->upd_bytes - vol->upd_received; + + /* + * When updating volumes, we accumulate whole logical eraseblock of + * data and write it at once. + */ + if (offs != 0) { + /* + * This is a write to the middle of the logical eraseblock. We + * copy the data to our update buffer and wait for more data or + * flush it if the whole eraseblock is written or the update + * is finished. + */ + + len = vol->usable_leb_size - offs; + if (len > count) + len = count; + + err = copy_from_user(vol->upd_buf + offs, buf, len); + if (err) + return -EFAULT; + + if (offs + len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + int flush_len = offs + len; + + /* + * OK, we gathered either the whole eraseblock or this + * is the last chunk, it's time to flush the buffer. + */ + ubi_assert(flush_len <= vol->usable_leb_size); + err = write_leb(ubi, vol_id, lnum, vol->upd_buf, + flush_len, vol->upd_ebs); + if (err) + return err; + } + + vol->upd_received += len; + count -= len; + buf += len; + lnum += 1; + } + + /* + * If we've got more to write, let's continue. At this point we know we + * are starting from the beginning of an eraseblock. + */ + while (count) { + if (count > vol->usable_leb_size) + len = vol->usable_leb_size; + else + len = count; + + err = copy_from_user(vol->upd_buf, buf, len); + if (err) + return -EFAULT; + + if (len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len, + vol->upd_ebs); + if (err) + break; + } + + vol->upd_received += len; + count -= len; + lnum += 1; + buf += len; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + /* The update is finished, clear the update marker */ + err = clear_update_marker(ubi, vol_id, vol->upd_bytes); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (err == 0) { + err = to_write; + kfree(vol->upd_buf); + vol->updating = 0; + } + } + + return err; +} diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c new file mode 100644 index 0000000..622d0d1 --- /dev/null +++ b/drivers/mtd/ubi/vmt.c @@ -0,0 +1,809 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * This file contains implementation of volume creation, deletion, updating and + * resizing. + */ + +#include +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static void paranoid_check_volumes(struct ubi_device *ubi); +#else +#define paranoid_check_volumes(ubi) +#endif + +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* Device attributes corresponding to files in '//class/ubi/ubiX_Y' */ +static struct device_attribute vol_reserved_ebs = + __ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_type = + __ATTR(type, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_name = + __ATTR(name, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_corrupted = + __ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_alignment = + __ATTR(alignment, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_usable_eb_size = + __ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_data_bytes = + __ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_upd_marker = + __ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL); + +/* + * "Show" method for files in '//class/ubi/ubiX_Y/'. + * + * Consider a situation: + * A. process 1 opens a sysfs file related to volume Y, say + * //class/ubi/ubiX_Y/reserved_ebs; + * B. process 2 removes volume Y; + * C. process 1 starts reading the //class/ubi/ubiX_Y/reserved_ebs file; + * + * What we want to do in a situation like that is to return error when the file + * is read. This is done by means of the 'removed' flag and the 'vol_lock' of + * the UBI volume description object. + */ +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + + spin_lock(&vol->ubi->volumes_lock); + if (vol->removed) { + spin_unlock(&vol->ubi->volumes_lock); + return -ENODEV; + } + if (attr == &vol_reserved_ebs) + ret = sprintf(buf, "%d\n", vol->reserved_pebs); + else if (attr == &vol_type) { + const char *tp; + tp = vol->vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"; + ret = sprintf(buf, "%s\n", tp); + } else if (attr == &vol_name) + ret = sprintf(buf, "%s\n", vol->name); + else if (attr == &vol_corrupted) + ret = sprintf(buf, "%d\n", vol->corrupted); + else if (attr == &vol_alignment) + ret = sprintf(buf, "%d\n", vol->alignment); + else if (attr == &vol_usable_eb_size) { + ret = sprintf(buf, "%d\n", vol->usable_leb_size); + } else if (attr == &vol_data_bytes) + ret = sprintf(buf, "%lld\n", vol->used_bytes); + else if (attr == &vol_upd_marker) + ret = sprintf(buf, "%d\n", vol->upd_marker); + else + BUG(); + spin_unlock(&vol->ubi->volumes_lock); + return ret; +} + +/* Release method for volume devices */ +static void vol_release(struct device *dev) +{ + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + ubi_assert(vol->removed); + kfree(vol); +} + +/** + * volume_sysfs_init - initialize sysfs for new volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, this function does not free allocated resources in case of failure - + * the caller does it. This is because this would cause release() here and the + * caller would oops. + */ +static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int err; + + err = device_create_file(&vol->dev, &vol_reserved_ebs); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_type); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_name); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_corrupted); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_alignment); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_usable_eb_size); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_data_bytes); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_upd_marker); + if (err) + return err; + return 0; +} + +/** + * volume_sysfs_close - close sysfs for a volume. + * @vol: volume description object + */ +static void volume_sysfs_close(struct ubi_volume *vol) +{ + device_remove_file(&vol->dev, &vol_upd_marker); + device_remove_file(&vol->dev, &vol_data_bytes); + device_remove_file(&vol->dev, &vol_usable_eb_size); + device_remove_file(&vol->dev, &vol_alignment); + device_remove_file(&vol->dev, &vol_corrupted); + device_remove_file(&vol->dev, &vol_name); + device_remove_file(&vol->dev, &vol_type); + device_remove_file(&vol->dev, &vol_reserved_ebs); + device_unregister(&vol->dev); +} + +/** + * ubi_create_volume - create volume. + * @ubi: UBI device description object + * @req: volume creation request + * + * This function creates volume described by @req. If @req->vol_id id + * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume + * and saves it in @req->vol_id. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) +{ + int i, err, vol_id = req->vol_id; + struct ubi_volume *vol; + struct ubi_vtbl_record vtbl_rec; + uint64_t bytes; + + if (ubi->ro_mode) + return -EROFS; + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + spin_lock(&ubi->volumes_lock); + + if (vol_id == UBI_VOL_NUM_AUTO) { + /* Find unused volume ID */ + dbg_msg("search for vacant volume ID"); + for (i = 0; i < ubi->vtbl_slots; i++) + if (!ubi->volumes[i]) { + vol_id = i; + break; + } + + if (vol_id == UBI_VOL_NUM_AUTO) { + dbg_err("out of volume IDs"); + err = -ENFILE; + goto out_unlock; + } + req->vol_id = vol_id; + } + + dbg_msg("volume ID %d, %llu bytes, type %d, name %s", + vol_id, (unsigned long long)req->bytes, + (int)req->vol_type, req->name); + + /* Ensure that this volume does not exist */ + err = -EEXIST; + if (ubi->volumes[vol_id]) { + dbg_err("volume %d already exists", vol_id); + goto out_unlock; + } + + /* Ensure that the name is unique */ + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i] && + ubi->volumes[i]->name_len == req->name_len && + strcmp(ubi->volumes[i]->name, req->name) == 0) { + dbg_err("volume \"%s\" exists (ID %d)", req->name, i); + goto out_unlock; + } + + /* Calculate how many eraseblocks are requested */ + vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment; + bytes = req->bytes; + if (do_div(bytes, vol->usable_leb_size)) + vol->reserved_pebs = 1; + vol->reserved_pebs += bytes; + + /* Reserve physical eraseblocks */ + if (vol->reserved_pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs, only %d available", ubi->avail_pebs); + spin_unlock(&ubi->volumes_lock); + err = -ENOSPC; + goto out_unlock; + } + ubi->avail_pebs -= vol->reserved_pebs; + ubi->rsvd_pebs += vol->reserved_pebs; + + vol->vol_id = vol_id; + vol->alignment = req->alignment; + vol->data_pad = ubi->leb_size % vol->alignment; + vol->vol_type = req->vol_type; + vol->name_len = req->name_len; + memcpy(vol->name, req->name, vol->name_len + 1); + vol->exclusive = 1; + vol->ubi = ubi; + ubi->volumes[vol_id] = vol; + spin_unlock(&ubi->volumes_lock); + + /* + * Finish all pending erases because there may be some LEBs belonging + * to the same volume ID. + */ + err = ubi_wl_flush(ubi); + if (err) + goto out_acc; + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_acc; + } + + for (i = 0; i < vol->reserved_pebs; i++) + vol->eba_tbl[i] = UBI_LEB_UNMAPPED; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + } else { + bytes = vol->used_bytes; + vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size); + vol->used_ebs = bytes; + if (vol->last_eb_bytes) + vol->used_ebs += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; + err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol_id + 1), 1); + if (err) { + ubi_err("cannot add character device for volume %d", vol_id); + goto out_mapping; + } + + err = ubi_create_gluebi(ubi, vol); + if (err) + goto out_cdev; + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; + vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); + if (err) + goto out_gluebi; + + err = volume_sysfs_init(ubi, vol); + if (err) + goto out_sysfs; + + /* Fill volume table record */ + memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_ubi32(vol->reserved_pebs); + vtbl_rec.alignment = cpu_to_ubi32(vol->alignment); + vtbl_rec.data_pad = cpu_to_ubi32(vol->data_pad); + vtbl_rec.name_len = cpu_to_ubi16(vol->name_len); + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + vtbl_rec.vol_type = UBI_VID_DYNAMIC; + else + vtbl_rec.vol_type = UBI_VID_STATIC; + memcpy(vtbl_rec.name, vol->name, vol->name_len + 1); + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_sysfs; + + spin_lock(&ubi->volumes_lock); + ubi->vol_count += 1; + vol->exclusive = 0; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + return 0; + +out_gluebi: + err = ubi_destroy_gluebi(vol); +out_cdev: + cdev_del(&vol->cdev); +out_mapping: + kfree(vol->eba_tbl); +out_acc: + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= vol->reserved_pebs; + ubi->avail_pebs += vol->reserved_pebs; +out_unlock: + spin_unlock(&ubi->volumes_lock); + kfree(vol); + return err; + + /* + * We are registered, so @vol is destroyed in the release function and + * we have to de-initialize differently. + */ +out_sysfs: + err = ubi_destroy_gluebi(vol); + cdev_del(&vol->cdev); + kfree(vol->eba_tbl); + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= vol->reserved_pebs; + ubi->avail_pebs += vol->reserved_pebs; + spin_unlock(&ubi->volumes_lock); + volume_sysfs_close(vol); + return err; +} + +/** + * ubi_remove_volume - remove volume. + * @desc: volume descriptor + * + * This function removes volume described by @desc. The volume has to be opened + * in "exclusive" mode. Returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_remove_volume(struct ubi_volume_desc *desc) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs; + + dbg_msg("remove UBI volume %d", vol_id); + ubi_assert(desc->mode == UBI_EXCLUSIVE); + ubi_assert(vol == ubi->volumes[vol_id]); + + if (ubi->ro_mode) + return -EROFS; + + err = ubi_destroy_gluebi(vol); + if (err) + return err; + + err = ubi_change_vtbl_record(ubi, vol_id, NULL); + if (err) + return err; + + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol_id, i); + if (err) + return err; + } + + spin_lock(&ubi->volumes_lock); + vol->removed = 1; + ubi->volumes[vol_id] = NULL; + spin_unlock(&ubi->volumes_lock); + + kfree(vol->eba_tbl); + vol->eba_tbl = NULL; + cdev_del(&vol->cdev); + volume_sysfs_close(vol); + kfree(desc); + + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= reserved_pebs; + ubi->avail_pebs += reserved_pebs; + i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (i > 0) { + i = ubi->avail_pebs >= i ? i : ubi->avail_pebs; + ubi->avail_pebs -= i; + ubi->rsvd_pebs += i; + ubi->beb_rsvd_pebs += i; + if (i > 0) + ubi_msg("reserve more %d PEBs", i); + } + ubi->vol_count -= 1; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + module_put(THIS_MODULE); + return 0; +} + +/** + * ubi_resize_volume - re-size volume. + * @desc: volume descriptor + * @reserved_pebs: new size in physical eraseblocks + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) +{ + int i, err, pebs, *new_mapping; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + struct ubi_vtbl_record vtbl_rec; + int vol_id = vol->vol_id; + + if (ubi->ro_mode) + return -EROFS; + + dbg_msg("re-size volume %d to from %d to %d PEBs", + vol_id, vol->reserved_pebs, reserved_pebs); + ubi_assert(desc->mode == UBI_EXCLUSIVE); + ubi_assert(vol == ubi->volumes[vol_id]); + + if (vol->vol_type == UBI_STATIC_VOLUME && + reserved_pebs < vol->used_ebs) { + dbg_err("too small size %d, %d LEBs contain data", + reserved_pebs, vol->used_ebs); + return -EINVAL; + } + + /* If the size is the same, we have nothing to do */ + if (reserved_pebs == vol->reserved_pebs) + return 0; + + new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL); + if (!new_mapping) + return -ENOMEM; + + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = UBI_LEB_UNMAPPED; + + /* Reserve physical eraseblocks */ + pebs = reserved_pebs - vol->reserved_pebs; + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + if (pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs: requested %d, available %d", + pebs, ubi->avail_pebs); + spin_unlock(&ubi->volumes_lock); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= pebs; + ubi->rsvd_pebs += pebs; + for (i = 0; i < vol->reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + /* Change volume table record */ + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_ubi32(reserved_pebs); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_acc; + + if (pebs < 0) { + for (i = 0; i < -pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol_id, reserved_pebs + i); + if (err) + goto out_acc; + } + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs += pebs; + ubi->avail_pebs -= pebs; + pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (pebs > 0) { + pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs; + ubi->avail_pebs -= pebs; + ubi->rsvd_pebs += pebs; + ubi->beb_rsvd_pebs += pebs; + if (pebs > 0) + ubi_msg("reserve more %d PEBs", pebs); + } + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + vol->reserved_pebs = reserved_pebs; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + } + + paranoid_check_volumes(ubi); + return 0; + +out_acc: + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= pebs; + ubi->avail_pebs += pebs; + spin_unlock(&ubi->volumes_lock); + } +out_free: + kfree(new_mapping); + return err; +} + +/** + * ubi_add_volume - add volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function adds an existin volume and initializes all its data + * structures. Returnes zero in case of success and a negative error code in + * case of failure. + */ +int ubi_add_volume(struct ubi_device *ubi, int vol_id) +{ + int err; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("add volume %d", vol_id); + ubi_dbg_dump_vol_info(vol); + ubi_assert(vol); + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; + err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol->vol_id + 1), 1); + if (err) { + ubi_err("cannot add character device for volume %d", vol_id); + return err; + } + + err = ubi_create_gluebi(ubi, vol); + if (err) + goto out_cdev; + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; + vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); + if (err) + goto out_gluebi; + + err = volume_sysfs_init(ubi, vol); + if (err) { + cdev_del(&vol->cdev); + err = ubi_destroy_gluebi(vol); + volume_sysfs_close(vol); + return err; + } + + paranoid_check_volumes(ubi); + return 0; + +out_gluebi: + err = ubi_destroy_gluebi(vol); +out_cdev: + cdev_del(&vol->cdev); + return err; +} + +/** + * ubi_free_volume - free volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function frees all resources for volume @vol_id but does not remove it. + * Used only when the UBI device is detached. + */ +void ubi_free_volume(struct ubi_device *ubi, int vol_id) +{ + int err; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("free volume %d", vol_id); + ubi_assert(vol); + + vol->removed = 1; + err = ubi_destroy_gluebi(vol); + ubi->volumes[vol_id] = NULL; + cdev_del(&vol->cdev); + volume_sysfs_close(vol); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_volume - check volume information. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id) +{ + int idx = vol_id2idx(ubi, vol_id); + int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker; + const struct ubi_volume *vol = ubi->volumes[idx]; + long long n; + const char *name; + + reserved_pebs = ubi32_to_cpu(ubi->vtbl[vol_id].reserved_pebs); + + if (!vol) { + if (reserved_pebs) { + ubi_err("no volume info, but volume exists"); + goto fail; + } + return; + } + + if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 || + vol->name_len < 0) { + ubi_err("negative values"); + goto fail; + } + if (vol->alignment > ubi->leb_size || vol->alignment == 0) { + ubi_err("bad alignment"); + goto fail; + } + + n = vol->alignment % ubi->min_io_size; + if (vol->alignment != 1 && n) { + ubi_err("alignment is not multiple of min I/O unit"); + goto fail; + } + + n = ubi->leb_size % vol->alignment; + if (vol->data_pad != n) { + ubi_err("bad data_pad, has to be %lld", n); + goto fail; + } + + if (vol->vol_type != UBI_DYNAMIC_VOLUME && + vol->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto fail; + } + + if (vol->upd_marker != 0 && vol->upd_marker != 1) { + ubi_err("bad upd_marker"); + goto fail; + } + + if (vol->upd_marker && vol->corrupted) { + dbg_err("update marker and corrupted simultaneously"); + goto fail; + } + + if (vol->reserved_pebs > ubi->good_peb_count) { + ubi_err("too large reserved_pebs"); + goto fail; + } + + n = ubi->leb_size - vol->data_pad; + if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) { + ubi_err("bad usable_leb_size, has to be %lld", n); + goto fail; + } + + if (vol->name_len > UBI_VOL_NAME_MAX) { + ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX); + goto fail; + } + + if (!vol->name) { + ubi_err("NULL volume name"); + goto fail; + } + + n = strnlen(vol->name, vol->name_len + 1); + if (n != vol->name_len) { + ubi_err("bad name_len %lld", n); + goto fail; + } + + n = vol->used_ebs * vol->usable_leb_size; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + if (vol->corrupted != 0) { + ubi_err("corrupted dynamic volume"); + goto fail; + } + if (vol->used_ebs != vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes != vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes != n) { + ubi_err("bad used_bytes"); + goto fail; + } + } else { + if (vol->corrupted != 0 && vol->corrupted != 1) { + ubi_err("bad corrupted"); + goto fail; + } + if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes < 0 || + vol->last_eb_bytes > vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes < 0 || vol->used_bytes > n || + vol->used_bytes < n - vol->usable_leb_size) { + ubi_err("bad used_bytes"); + goto fail; + } + } + + alignment = ubi32_to_cpu(ubi->vtbl[vol_id].alignment); + data_pad = ubi32_to_cpu(ubi->vtbl[vol_id].data_pad); + name_len = ubi16_to_cpu(ubi->vtbl[vol_id].name_len); + upd_marker = ubi->vtbl[vol_id].upd_marker; + name = &ubi->vtbl[vol_id].name[0]; + if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC) + vol_type = UBI_DYNAMIC_VOLUME; + else + vol_type = UBI_STATIC_VOLUME; + + if (alignment != vol->alignment || data_pad != vol->data_pad || + upd_marker != vol->upd_marker || vol_type != vol->vol_type || + name_len!= vol->name_len || strncmp(name, vol->name, name_len)) { + ubi_err("volume info is different"); + goto fail; + } + + return; + +fail: + ubi_err("paranoid check failed"); + ubi_dbg_dump_vol_info(vol); + ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); + BUG(); +} + +/** + * paranoid_check_volumes - check information about all volumes. + * @ubi: UBI device description object + */ +static void paranoid_check_volumes(struct ubi_device *ubi) +{ + int i; + + mutex_lock(&ubi->vtbl_mutex); + spin_lock(&ubi->volumes_lock); + for (i = 0; i < ubi->vtbl_slots; i++) + paranoid_check_volume(ubi, i); + spin_unlock(&ubi->volumes_lock); + mutex_unlock(&ubi->vtbl_mutex); +} +#endif diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c new file mode 100644 index 0000000..b6fd6bb --- /dev/null +++ b/drivers/mtd/ubi/vtbl.c @@ -0,0 +1,809 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * This file includes volume table manipulation code. The volume table is an + * on-flash table containing volume meta-data like name, number of reserved + * physical eraseblocks, type, etc. The volume table is stored in the so-called + * "layout volume". + * + * The layout volume is an internal volume which is organized as follows. It + * consists of two logical eraseblocks - LEB 0 and LEB 1. Each logical + * eraseblock stores one volume table copy, i.e. LEB 0 and LEB 1 duplicate each + * other. This redundancy guarantees robustness to unclean reboots. The volume + * table is basically an array of volume table records. Each record contains + * full information about the volume and protected by a CRC checksum. + * + * The volume table is changed, it is first changed in RAM. Then LEB 0 is + * erased, and the updated volume table is written back to LEB 0. Then same for + * LEB 1. This scheme guarantees recoverability from unclean reboots. + * + * In this UBI implementation the on-flash volume table does not contain any + * information about how many data static volumes contain. This information may + * be found from the scanning data. + * + * But it would still be beneficial to store this information in the volume + * table. For example, suppose we have a static volume X, and all its physical + * eraseblocks became bad for some reasons. Suppose we are attaching the + * corresponding MTD device, the scanning has found no logical eraseblocks + * corresponding to the volume X. According to the volume table volume X does + * exist. So we don't know whether it is just empty or all its physical + * eraseblocks went bad. So we cannot alarm the user about this corruption. + * + * The volume table also stores so-called "update marker", which is used for + * volume updates. Before updating the volume, the update marker is set, and + * after the update operation is finished, the update marker is cleared. So if + * the update operation was interrupted (e.g. by an unclean reboot) - the + * update marker is still there and we know that the volume's contents is + * damaged. + */ + +#include +#include +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static void paranoid_vtbl_check(const struct ubi_device *ubi); +#else +#define paranoid_vtbl_check(ubi) +#endif + +/* Empty volume table record */ +static struct ubi_vtbl_record empty_vtbl_record; + +/** + * ubi_change_vtbl_record - change volume table record. + * @ubi: UBI device description object + * @idx: table index to change + * @vtbl_rec: new volume table record + * + * This function changes volume table record @idx. If @vtbl_rec is %NULL, empty + * volume table record is written. The caller does not have to calculate CRC of + * the record as it is done by this function. Returns zero in case of success + * and a negative error code in case of failure. + */ +int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + struct ubi_vtbl_record *vtbl_rec) +{ + int i, err; + uint32_t crc; + + ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); + + if (!vtbl_rec) + vtbl_rec = &empty_vtbl_record; + else { + crc = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_ubi32(crc); + } + + dbg_msg("change record %d", idx); + ubi_dbg_dump_vtbl_record(vtbl_rec, idx); + + mutex_lock(&ubi->vtbl_mutex); + memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + err = ubi_eba_unmap_leb(ubi, UBI_LAYOUT_VOL_ID, i); + if (err) { + mutex_unlock(&ubi->vtbl_mutex); + return err; + } + err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, ubi->vtbl, 0, + ubi->vtbl_size, UBI_LONGTERM); + if (err) { + mutex_unlock(&ubi->vtbl_mutex); + return err; + } + } + + paranoid_vtbl_check(ubi); + mutex_unlock(&ubi->vtbl_mutex); + return ubi_wl_flush(ubi); +} + +/** + * vol_til_check - check if volume table is not corrupted and contains sensible + * data. + * + * @ubi: UBI device description object + * @vtbl: volume table + * + * This function returns zero if @vtbl is all right, %1 if CRC is incorrect, + * and %-EINVAL if it contains inconsistent data. + */ +static int vtbl_check(const struct ubi_device *ubi, + const struct ubi_vtbl_record *vtbl) +{ + int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len; + int upd_marker; + uint32_t crc; + const char *name; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs); + alignment = ubi32_to_cpu(vtbl[i].alignment); + data_pad = ubi32_to_cpu(vtbl[i].data_pad); + upd_marker = vtbl[i].upd_marker; + vol_type = vtbl[i].vol_type; + name_len = ubi16_to_cpu(vtbl[i].name_len); + name = &vtbl[i].name[0]; + + crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); + if (ubi32_to_cpu(vtbl[i].crc) != crc) { + ubi_err("bad CRC at record %u: %#08x, not %#08x", + i, crc, ubi32_to_cpu(vtbl[i].crc)); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + return 1; + } + + if (reserved_pebs == 0) { + if (memcmp(&vtbl[i], &empty_vtbl_record, + UBI_VTBL_RECORD_SIZE)) { + dbg_err("bad empty record"); + goto bad; + } + continue; + } + + if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 || + name_len < 0) { + dbg_err("negative values"); + goto bad; + } + + if (alignment > ubi->leb_size || alignment == 0) { + dbg_err("bad alignment"); + goto bad; + } + + n = alignment % ubi->min_io_size; + if (alignment != 1 && n) { + dbg_err("alignment is not multiple of min I/O unit"); + goto bad; + } + + n = ubi->leb_size % alignment; + if (data_pad != n) { + dbg_err("bad data_pad, has to be %d", n); + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + dbg_err("bad vol_type"); + goto bad; + } + + if (upd_marker != 0 && upd_marker != 1) { + dbg_err("bad upd_marker"); + goto bad; + } + + if (reserved_pebs > ubi->good_peb_count) { + dbg_err("too large reserved_pebs, good PEBs %d", + ubi->good_peb_count); + goto bad; + } + + if (name_len > UBI_VOL_NAME_MAX) { + dbg_err("too long volume name, max %d", + UBI_VOL_NAME_MAX); + goto bad; + } + + if (name[0] == '\0') { + dbg_err("NULL volume name"); + goto bad; + } + + if (name_len != strnlen(name, name_len + 1)) { + dbg_err("bad name_len"); + goto bad; + } + } + + /* Checks that all names are unique */ + for (i = 0; i < ubi->vtbl_slots - 1; i++) { + for (n = i + 1; n < ubi->vtbl_slots; n++) { + int len1 = ubi16_to_cpu(vtbl[i].name_len); + int len2 = ubi16_to_cpu(vtbl[n].name_len); + + if (len1 > 0 && len1 == len2 && + !strncmp(vtbl[i].name, vtbl[n].name, len1)) { + ubi_err("volumes %d and %d have the same name" + " \"%s\"", i, n, vtbl[i].name); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + ubi_dbg_dump_vtbl_record(&vtbl[n], n); + return -EINVAL; + } + } + } + + return 0; + +bad: + ubi_err("volume table check failed, record %d", i); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + return -EINVAL; +} + +/** + * create_vtbl - create a copy of volume table. + * @ubi: UBI device description object + * @si: scanning information + * @copy: number of the volume table copy + * @vtbl: contents of the volume table + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int create_vtbl(const struct ubi_device *ubi, struct ubi_scan_info *si, + int copy, void *vtbl) +{ + int err, tries = 0; + static struct ubi_vid_hdr *vid_hdr; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *new_seb, *old_seb = NULL; + + ubi_msg("create volume table (copy #%d)", copy + 1); + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + /* + * Check if there is a logical eraseblock which would have to contain + * this volume table copy was found during scanning. It has to be wiped + * out. + */ + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + if (sv) + old_seb = ubi_scan_find_seb(sv, copy); + +retry: + new_seb = ubi_scan_get_free_peb(ubi, si); + if (IS_ERR(new_seb)) { + err = PTR_ERR(new_seb); + goto out_free; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->vol_id = cpu_to_ubi32(UBI_LAYOUT_VOL_ID); + vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; + vid_hdr->data_size = vid_hdr->used_ebs = + vid_hdr->data_pad = cpu_to_ubi32(0); + vid_hdr->lnum = cpu_to_ubi32(copy); + vid_hdr->sqnum = cpu_to_ubi64(++si->max_sqnum); + vid_hdr->leb_ver = cpu_to_ubi32(old_seb ? old_seb->leb_ver + 1: 0); + + /* The EC header is already there, write the VID header */ + err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr); + if (err) + goto write_error; + + /* Write the layout volume contents */ + err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size); + if (err) + goto write_error; + + /* + * And add it to the scanning information. Don't delete the old + * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'. + */ + err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec, + vid_hdr, 0); + kfree(new_seb); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + kfree(new_seb); + /* May be this physical eraseblock went bad, try to pick another one */ + if (++tries <= 5) { + err = ubi_scan_add_to_list(si, new_seb->pnum, new_seb->ec, + &si->corr); + if (!err) + goto retry; + } +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +} + +/** + * process_lvol - process the layout volume. + * @ubi: UBI device description object + * @si: scanning information + * @sv: layout volume scanning information + * + * This function is responsible for reading the layout volume, ensuring it is + * not corrupted, and recovering from corruptions if needed. Returns volume + * table in case of success and a negative error code in case of failure. + */ +static struct ubi_vtbl_record *process_lvol(const struct ubi_device *ubi, + struct ubi_scan_info *si, + struct ubi_scan_volume *sv) +{ + int err; + struct rb_node *rb; + struct ubi_scan_leb *seb; + struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL }; + int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1}; + + /* + * UBI goes through the following steps when it changes the layout + * volume: + * a. erase LEB 0; + * b. write new data to LEB 0; + * c. erase LEB 1; + * d. write new data to LEB 1. + * + * Before the change, both LEBs contain the same data. + * + * Due to unclean reboots, the contents of LEB 0 may be lost, but there + * should LEB 1. So it is OK if LEB 0 is corrupted while LEB 1 is not. + * Similarly, LEB 1 may be lost, but there should be LEB 0. And + * finally, unclean reboots may result in a situation when neither LEB + * 0 nor LEB 1 are corrupted, but they are different. In this case, LEB + * 0 contains more recent information. + * + * So the plan is to first check LEB 0. Then + * a. if LEB 0 is OK, it must be containing the most resent data; then + * we compare it with LEB 1, and if they are different, we copy LEB + * 0 to LEB 1; + * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1 + * to LEB 0. + */ + + dbg_msg("check layout volume"); + + /* Read both LEB 0 and LEB 1 into memory */ + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { + leb[seb->lnum] = kzalloc(ubi->vtbl_size, GFP_KERNEL); + if (!leb[seb->lnum]) { + err = -ENOMEM; + goto out_free; + } + + err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0, + ubi->vtbl_size); + if (err == UBI_IO_BITFLIPS || err == -EBADMSG) + /* Scrub the PEB later */ + seb->scrub = 1; + else if (err) + goto out_free; + } + + err = -EINVAL; + if (leb[0]) { + leb_corrupted[0] = vtbl_check(ubi, leb[0]); + if (leb_corrupted[0] < 0) + goto out_free; + } + + if (!leb_corrupted[0]) { + /* LEB 0 is OK */ + if (leb[1]) + leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size); + if (leb_corrupted[1]) { + ubi_warn("volume table copy #2 is corrupted"); + err = create_vtbl(ubi, si, 1, leb[0]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + } + + /* Both LEB 1 and LEB 2 are OK and consistent */ + kfree(leb[1]); + return leb[0]; + } else { + /* LEB 0 is corrupted or does not exist */ + if (leb[1]) { + leb_corrupted[1] = vtbl_check(ubi, leb[1]); + if (leb_corrupted[1] < 0) + goto out_free; + } + if (leb_corrupted[1]) { + /* Both LEB 0 and LEB 1 are corrupted */ + ubi_err("both volume tables are corrupted"); + goto out_free; + } + + ubi_warn("volume table copy #1 is corrupted"); + err = create_vtbl(ubi, si, 0, leb[1]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + + kfree(leb[0]); + return leb[1]; + } + +out_free: + kfree(leb[0]); + kfree(leb[1]); + return ERR_PTR(err); +} + +/** + * create_empty_lvol - create empty layout volume. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns volume table contents in case of success and a + * negative error code in case of failure. + */ +static struct ubi_vtbl_record *create_empty_lvol(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int i; + struct ubi_vtbl_record *vtbl; + + vtbl = kzalloc(ubi->vtbl_size, GFP_KERNEL); + if (!vtbl) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < ubi->vtbl_slots; i++) + memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE); + + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + int err; + + err = create_vtbl(ubi, si, i, vtbl); + if (err) { + kfree(vtbl); + return ERR_PTR(err); + } + } + + return vtbl; +} + +/** + * init_volumes - initialize volume information for existing volumes. + * @ubi: UBI device description object + * @si: scanning information + * @vtbl: volume table + * + * This function allocates volume description objects for existing volumes. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, + const struct ubi_vtbl_record *vtbl) +{ + int i, reserved_pebs = 0; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + if (ubi32_to_cpu(vtbl[i].reserved_pebs) == 0) + continue; /* Empty record */ + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs); + vol->alignment = ubi32_to_cpu(vtbl[i].alignment); + vol->data_pad = ubi32_to_cpu(vtbl[i].data_pad); + vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + vol->name_len = ubi16_to_cpu(vtbl[i].name_len); + vol->usable_leb_size = ubi->leb_size - vol->data_pad; + memcpy(vol->name, vtbl[i].name, vol->name_len); + vol->name[vol->name_len] = '\0'; + vol->vol_id = i; + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[i] = vol; + ubi->vol_count += 1; + vol->ubi = ubi; + reserved_pebs += vol->reserved_pebs; + + /* + * In case of dynamic volume UBI knows nothing about how many + * data is stored there. So assume the whole volume is used. + */ + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + continue; + } + + /* Static volumes only */ + sv = ubi_scan_find_sv(si, i); + if (!sv) { + /* + * No eraseblocks belonging to this volume found. We + * don't actually know whether this static volume is + * completely corrupted or just contains no data. And + * we cannot know this as long as data size is not + * stored on flash. So we just assume the volume is + * empty. FIXME: this should be handled. + */ + continue; + } + + if (sv->leb_count != sv->used_ebs) { + /* + * We found a static volume which misses several + * eraseblocks. Treat it as corrupted. + */ + ubi_warn("static volume %d misses %d LEBs - corrupted", + sv->vol_id, sv->used_ebs - sv->leb_count); + vol->corrupted = 1; + continue; + } + + vol->used_ebs = sv->used_ebs; + vol->used_bytes = (vol->used_ebs - 1) * vol->usable_leb_size; + vol->used_bytes += sv->last_data_size; + vol->last_eb_bytes = sv->last_data_size; + } + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS; + vol->alignment = 1; + vol->vol_type = UBI_DYNAMIC_VOLUME; + vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1; + memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1); + vol->usable_leb_size = ubi->leb_size; + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->reserved_pebs; + vol->used_bytes = vol->used_ebs * (ubi->leb_size - vol->data_pad); + vol->vol_id = UBI_LAYOUT_VOL_ID; + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol; + reserved_pebs += vol->reserved_pebs; + ubi->vol_count += 1; + vol->ubi = ubi; + + if (reserved_pebs > ubi->avail_pebs) + ubi_err("not enough PEBs, required %d, available %d", + reserved_pebs, ubi->avail_pebs); + ubi->rsvd_pebs += reserved_pebs; + ubi->avail_pebs -= reserved_pebs; + + return 0; +} + +/** + * check_sv - check volume scanning information. + * @vol: UBI volume description object + * @sv: volume scanning information + * + * This function returns zero if the volume scanning information is consistent + * to the data read from the volume tabla, and %-EINVAL if not. + */ +static int check_sv(const struct ubi_volume *vol, + const struct ubi_scan_volume *sv) +{ + if (sv->highest_lnum >= vol->reserved_pebs) { + dbg_err("bad highest_lnum"); + goto bad; + } + if (sv->leb_count > vol->reserved_pebs) { + dbg_err("bad leb_count"); + goto bad; + } + if (sv->vol_type != vol->vol_type) { + dbg_err("bad vol_type"); + goto bad; + } + if (sv->used_ebs > vol->reserved_pebs) { + dbg_err("bad used_ebs"); + goto bad; + } + if (sv->data_pad != vol->data_pad) { + dbg_err("bad data_pad"); + goto bad; + } + return 0; + +bad: + ubi_err("bad scanning information"); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vol_info(vol); + return -EINVAL; +} + +/** + * check_scanning_info - check that scanning information. + * @ubi: UBI device description object + * @si: scanning information + * + * Even though we protect on-flash data by CRC checksums, we still don't trust + * the media. This function ensures that scanning information is consistent to + * the information read from the volume table. Returns zero if the scanning + * information is OK and %-EINVAL if it is not. + */ +static int check_scanning_info(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int err, i; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + + if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) { + ubi_err("scanning found %d volumes, maximum is %d + %d", + si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots); + return -EINVAL; + } + + if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT&& + si->highest_vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("too large volume ID %d found by scanning", + si->highest_vol_id); + return -EINVAL; + } + + + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + cond_resched(); + + sv = ubi_scan_find_sv(si, i); + vol = ubi->volumes[i]; + if (!vol) { + if (sv) + ubi_scan_rm_volume(si, sv); + continue; + } + + if (vol->reserved_pebs == 0) { + ubi_assert(i < ubi->vtbl_slots); + + if (!sv) + continue; + + /* + * During scanning we found a volume which does not + * exist according to the information in the volume + * table. This must have happened due to an unclean + * reboot while the volume was being removed. Discard + * these eraseblocks. + */ + ubi_msg("finish volume %d removal", sv->vol_id); + ubi_scan_rm_volume(si, sv); + } else if (sv) { + err = check_sv(vol, sv); + if (err) + return err; + } + } + + return 0; +} + +/** + * ubi_read_volume_table - read volume table. + * information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function reads volume table, checks it, recover from errors if needed, + * or creates it if needed. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int i, err; + struct ubi_scan_volume *sv; + + empty_vtbl_record.crc = cpu_to_ubi32(0xf116c36b); + + /* + * The number of supported volumes is limited by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + ubi->vtbl_slots = ubi->leb_size / UBI_VTBL_RECORD_SIZE; + if (ubi->vtbl_slots > UBI_MAX_VOLUMES) + ubi->vtbl_slots = UBI_MAX_VOLUMES; + + ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE; + ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size); + + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + if (!sv) { + /* + * No logical eraseblocks belonging to the layout volume were + * found. This could mean that the flash is just empty. In + * this case we create empty layout volume. + * + * But if flash is not empty this must be a corruption or the + * MTD device just contains garbage. + */ + if (si->is_empty) { + ubi->vtbl = create_empty_lvol(ubi, si); + if (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } else { + ubi_err("the layout volume was not found"); + return -EINVAL; + } + } else { + if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) { + /* This must not happen with proper UBI images */ + dbg_err("too many LEBs (%d) in layout volume", + sv->leb_count); + return -EINVAL; + } + + ubi->vtbl = process_lvol(ubi, si, sv); + if (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } + + ubi->avail_pebs = ubi->good_peb_count; + + /* + * The layout volume is OK, initialize the corresponding in-RAM data + * structures. + */ + err = init_volumes(ubi, si, ubi->vtbl); + if (err) + goto out_free; + + /* + * Get sure that the scanning information is consistent to the + * information stored in the volume table. + */ + err = check_scanning_info(ubi, si); + if (err) + goto out_free; + + return 0; + +out_free: + kfree(ubi->vtbl); + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) + if (ubi->volumes[i]) { + kfree(ubi->volumes[i]); + ubi->volumes[i] = NULL; + } + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_vtbl_check - check volume table. + * @ubi: UBI device description object + */ +static void paranoid_vtbl_check(const struct ubi_device *ubi) +{ + if (vtbl_check(ubi, ubi->vtbl)) { + ubi_err("paranoid check failed"); + BUG(); + } +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c new file mode 100644 index 0000000..9ecaf77 --- /dev/null +++ b/drivers/mtd/ubi/wl.c @@ -0,0 +1,1671 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём), Thomas Gleixner + */ + +/* + * UBI wear-leveling unit. + * + * This unit is responsible for wear-leveling. It works in terms of physical + * eraseblocks and erase counters and knows nothing about logical eraseblocks, + * volumes, etc. From this unit's perspective all physical eraseblocks are of + * two types - used and free. Used physical eraseblocks are those that were + * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are + * those that were put by the 'ubi_wl_put_peb()' function. + * + * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter + * header. The rest of the physical eraseblock contains only 0xFF bytes. + * + * When physical eraseblocks are returned to the WL unit by means of the + * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is + * done asynchronously in context of the per-UBI device background thread, + * which is also managed by the WL unit. + * + * The wear-leveling is ensured by means of moving the contents of used + * physical eraseblocks with low erase counter to free physical eraseblocks + * with high erase counter. + * + * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick + * an "optimal" physical eraseblock. For example, when it is known that the + * physical eraseblock will be "put" soon because it contains short-term data, + * the WL unit may pick a free physical eraseblock with low erase counter, and + * so forth. + * + * If the WL unit fails to erase a physical eraseblock, it marks it as bad. + * + * This unit is also responsible for scrubbing. If a bit-flip is detected in a + * physical eraseblock, it has to be moved. Technically this is the same as + * moving it for wear-leveling reasons. + * + * As it was said, for the UBI unit all physical eraseblocks are either "free" + * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used + * eraseblocks are kept in a set of different RB-trees: @wl->used, + * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub. + * + * Note, in this implementation, we keep a small in-RAM object for each physical + * eraseblock. This is surely not a scalable solution. But it appears to be good + * enough for moderately large flashes and it is simple. In future, one may + * re-work this unit and make it more scalable. + * + * At the moment this unit does not utilize the sequence number, which was + * introduced relatively recently. But it would be wise to do this because the + * sequence number of a logical eraseblock characterizes how old is it. For + * example, when we move a PEB with low erase counter, and we need to pick the + * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we + * pick target PEB with an average EC if our PEB is not very "old". This is a + * room for future re-works of the WL unit. + * + * FIXME: looks too complex, should be simplified (later). + */ + +#include +#include +#include +#include +#include "ubi.h" + +/* Number of physical eraseblocks reserved for wear-leveling purposes */ +#define WL_RESERVED_PEBS 1 + +/* + * How many erase cycles are short term, unknown, and long term physical + * eraseblocks protected. + */ +#define ST_PROTECTION 16 +#define U_PROTECTION 10 +#define LT_PROTECTION 4 + +/* + * Maximum difference between two erase counters. If this threshold is + * exceeded, the WL unit starts moving data from used physical eraseblocks with + * low erase counter to free physical eraseblocks with high erase counter. + */ +#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD + +/* + * When a physical eraseblock is moved, the WL unit has to pick the target + * physical eraseblock to move to. The simplest way would be just to pick the + * one with the highest erase counter. But in certain workloads this could lead + * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a + * situation when the picked physical eraseblock is constantly erased after the + * data is written to it. So, we have a constant which limits the highest erase + * counter of the free physical eraseblock to pick. Namely, the WL unit does + * not pick eraseblocks with erase counter greater then the lowest erase + * counter plus %WL_FREE_MAX_DIFF. + */ +#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) + +/* + * Maximum number of consecutive background thread failures which is enough to + * switch to read-only mode. + */ +#define WL_MAX_FAILURES 32 + +/** + * struct ubi_wl_entry - wear-leveling entry. + * @rb: link in the corresponding RB-tree + * @ec: erase counter + * @pnum: physical eraseblock number + * + * Each physical eraseblock has a corresponding &struct wl_entry object which + * may be kept in different RB-trees. + */ +struct ubi_wl_entry { + struct rb_node rb; + int ec; + int pnum; +}; + +/** + * struct ubi_wl_prot_entry - PEB protection entry. + * @rb_pnum: link in the @wl->prot.pnum RB-tree + * @rb_aec: link in the @wl->prot.aec RB-tree + * @abs_ec: the absolute erase counter value when the protection ends + * @e: the wear-leveling entry of the physical eraseblock under protection + * + * When the WL unit returns a physical eraseblock, the physical eraseblock is + * protected from being moved for some "time". For this reason, the physical + * eraseblock is not directly moved from the @wl->free tree to the @wl->used + * tree. There is one more tree in between where this physical eraseblock is + * temporarily stored (@wl->prot). + * + * All this protection stuff is needed because: + * o we don't want to move physical eraseblocks just after we have given them + * to the user; instead, we first want to let users fill them up with data; + * + * o there is a chance that the user will put the physical eraseblock very + * soon, so it makes sense not to move it for some time, but wait; this is + * especially important in case of "short term" physical eraseblocks. + * + * Physical eraseblocks stay protected only for limited time. But the "time" is + * measured in erase cycles in this case. This is implemented with help of the + * absolute erase counter (@wl->abs_ec). When it reaches certain value, the + * physical eraseblocks are moved from the protection trees (@wl->prot.*) to + * the @wl->used tree. + * + * Protected physical eraseblocks are searched by physical eraseblock number + * (when they are put) and by the absolute erase counter (to check if it is + * time to move them to the @wl->used tree). So there are actually 2 RB-trees + * storing the protected physical eraseblocks: @wl->prot.pnum and + * @wl->prot.aec. They are referred to as the "protection" trees. The + * first one is indexed by the physical eraseblock number. The second one is + * indexed by the absolute erase counter. Both trees store + * &struct ubi_wl_prot_entry objects. + * + * Each physical eraseblock has 2 main states: free and used. The former state + * corresponds to the @wl->free tree. The latter state is split up on several + * sub-states: + * o the WL movement is allowed (@wl->used tree); + * o the WL movement is temporarily prohibited (@wl->prot.pnum and + * @wl->prot.aec trees); + * o scrubbing is needed (@wl->scrub tree). + * + * Depending on the sub-state, wear-leveling entries of the used physical + * eraseblocks may be kept in one of those trees. + */ +struct ubi_wl_prot_entry { + struct rb_node rb_pnum; + struct rb_node rb_aec; + unsigned long long abs_ec; + struct ubi_wl_entry *e; +}; + +/** + * struct ubi_work - UBI work description data structure. + * @list: a link in the list of pending works + * @func: worker function + * @priv: private data of the worker function + * + * @e: physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * The @func pointer points to the worker function. If the @cancel argument is + * not zero, the worker has to free the resources and exit immediately. The + * worker has to return zero in case of success and a negative error code in + * case of failure. + */ +struct ubi_work { + struct list_head list; + int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel); + /* The below fields are only relevant to erasure works */ + struct ubi_wl_entry *e; + int torture; +}; + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec); +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root); +#else +#define paranoid_check_ec(ubi, pnum, ec) 0 +#define paranoid_check_in_wl_tree(e, root) +#endif + +/* Slab cache for wear-leveling entries */ +static struct kmem_cache *wl_entries_slab; + +/** + * tree_empty - a helper function to check if an RB-tree is empty. + * @root: the root of the tree + * + * This function returns non-zero if the RB-tree is empty and zero if not. + */ +static inline int tree_empty(struct rb_root *root) +{ + return root->rb_node == NULL; +} + +/** + * wl_tree_add - add a wear-leveling entry to a WL RB-tree. + * @e: the wear-leveling entry to add + * @root: the root of the tree + * + * Note, we use (erase counter, physical eraseblock number) pairs as keys in + * the @ubi->used and @ubi->free RB-trees. + */ +static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node **p, *parent = NULL; + + p = &root->rb_node; + while (*p) { + struct ubi_wl_entry *e1; + + parent = *p; + e1 = rb_entry(parent, struct ubi_wl_entry, rb); + + if (e->ec < e1->ec) + p = &(*p)->rb_left; + else if (e->ec > e1->ec) + p = &(*p)->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&e->rb, parent, p); + rb_insert_color(&e->rb, root); +} + + +/* + * Helper functions to add and delete wear-leveling entries from different + * trees. + */ + +static void free_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e) +{ + wl_tree_add(e, &ubi->free); +} +static inline void used_tree_add(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + wl_tree_add(e, &ubi->used); +} +static inline void scrub_tree_add(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + wl_tree_add(e, &ubi->scrub); +} +static inline void free_tree_del(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &ubi->free); + rb_erase(&e->rb, &ubi->free); +} +static inline void used_tree_del(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &ubi->used); + rb_erase(&e->rb, &ubi->used); +} +static inline void scrub_tree_del(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &ubi->scrub); + rb_erase(&e->rb, &ubi->scrub); +} + +/** + * do_work - do one pending work. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int do_work(struct ubi_device *ubi) +{ + int err; + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + + if (list_empty(&ubi->works)) { + spin_unlock(&ubi->wl_lock); + return 0; + } + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + spin_unlock(&ubi->wl_lock); + + /* + * Call the worker function. Do not touch the work structure + * after this call as it will have been freed or reused by that + * time by the worker function. + */ + err = wrk->func(ubi, wrk, 0); + if (err) + ubi_err("work failed with error code %d", err); + + spin_lock(&ubi->wl_lock); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + spin_unlock(&ubi->wl_lock); + return err; +} + +/** + * produce_free_peb - produce a free physical eraseblock. + * @ubi: UBI device description object + * + * This function tries to make a free PEB by means of synchronous execution of + * pending works. This may be needed if, for example the background thread is + * disabled. Returns zero in case of success and a negative error code in case + * of failure. + */ +static int produce_free_peb(struct ubi_device *ubi) +{ + int err; + + spin_lock(&ubi->wl_lock); + while (tree_empty(&ubi->free)) { + spin_unlock(&ubi->wl_lock); + + dbg_wl("do one work synchronously"); + err = do_work(ubi); + if (err) + return err; + + spin_lock(&ubi->wl_lock); + } + spin_unlock(&ubi->wl_lock); + + return 0; +} + +/** + * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns non-zero if @e is in the @root RB-tree and zero if it + * is not. + */ +static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node *p; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + + if (e->pnum == e1->pnum) { + ubi_assert(e == e1); + return 1; + } + + if (e->ec < e1->ec) + p = p->rb_left; + else if (e->ec > e1->ec) + p = p->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + } + + return 0; +} + +/** + * prot_tree_add - add physical eraseblock to protection trees. + * @ubi: UBI device description object + * @e: the physical eraseblock to add + * @pe: protection entry object to use + * @abs_ec: absolute erase counter value when this physical eraseblock has + * to be removed from the protection trees. + * + * @wl->lock has to be locked. + */ +static void prot_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e, + struct ubi_wl_prot_entry *pe, int abs_ec) +{ + struct rb_node **p, *parent = NULL; + struct ubi_wl_prot_entry *pe1; + + pe->e = e; + pe->abs_ec = ubi->abs_ec + abs_ec; + + p = &ubi->prot.pnum.rb_node; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum); + + if (e->pnum < pe1->e->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_pnum, parent, p); + rb_insert_color(&pe->rb_pnum, &ubi->prot.pnum); + + p = &ubi->prot.aec.rb_node; + parent = NULL; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec < pe1->abs_ec) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_aec, parent, p); + rb_insert_color(&pe->rb_aec, &ubi->prot.aec); +} + +/** + * find_wl_entry - find wear-leveling entry closest to certain erase counter. + * @root: the RB-tree where to look for + * @max: highest possible erase counter + * + * This function looks for a wear leveling entry with erase counter closest to + * @max and less then @max. + */ +static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) +{ + struct rb_node *p; + struct ubi_wl_entry *e; + + e = rb_entry(rb_first(root), struct ubi_wl_entry, rb); + max += e->ec; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + if (e1->ec >= max) + p = p->rb_left; + else { + p = p->rb_right; + e = e1; + } + } + + return e; +} + +/** + * ubi_wl_get_peb - get a physical eraseblock. + * @ubi: UBI device description object + * @dtype: type of data which will be stored in this physical eraseblock + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. Might sleep. + */ +int ubi_wl_get_peb(struct ubi_device *ubi, int dtype) +{ + int err, protect, medium_ec; + struct ubi_wl_entry *e, *first, *last; + struct ubi_wl_prot_entry *pe; + + ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM || + dtype == UBI_UNKNOWN); + + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_KERNEL); + if (!pe) + return -ENOMEM; + +retry: + spin_lock(&ubi->wl_lock); + if (tree_empty(&ubi->free)) { + if (ubi->works_count == 0) { + ubi_assert(list_empty(&ubi->works)); + ubi_err("no free eraseblocks"); + spin_unlock(&ubi->wl_lock); + kfree(pe); + return -ENOSPC; + } + spin_unlock(&ubi->wl_lock); + + err = produce_free_peb(ubi); + if (err < 0) { + kfree(pe); + return err; + } + goto retry; + } + + switch (dtype) { + case UBI_LONGTERM: + /* + * For long term data we pick a physical eraseblock + * with high erase counter. But the highest erase + * counter we can pick is bounded by the the lowest + * erase counter plus %WL_FREE_MAX_DIFF. + */ + e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + protect = LT_PROTECTION; + break; + case UBI_UNKNOWN: + /* + * For unknown data we pick a physical eraseblock with + * medium erase counter. But we by no means can pick a + * physical eraseblock with erase counter greater or + * equivalent than the lowest erase counter plus + * %WL_FREE_MAX_DIFF. + */ + first = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + last = rb_entry(rb_last(&ubi->free), + struct ubi_wl_entry, rb); + + if (last->ec - first->ec < WL_FREE_MAX_DIFF) + e = rb_entry(ubi->free.rb_node, + struct ubi_wl_entry, rb); + else { + medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2; + e = find_wl_entry(&ubi->free, medium_ec); + } + protect = U_PROTECTION; + break; + case UBI_SHORTTERM: + /* + * For short term data we pick a physical eraseblock + * with the lowest erase counter as we expect it will + * be erased soon. + */ + e = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + protect = ST_PROTECTION; + break; + default: + protect = 0; + e = NULL; + BUG(); + } + + /* + * Move the physical eraseblock to the protection trees where it will + * be protected from being moved for some time. + */ + free_tree_del(ubi, e); + prot_tree_add(ubi, e, pe, protect); + + dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect); + spin_unlock(&ubi->wl_lock); + + return e->pnum; +} + +/** + * prot_tree_del - remove a physical eraseblock from the protection trees + * @ubi: UBI device description object + * @pnum: the physical eraseblock to remove + */ +static void prot_tree_del(struct ubi_device *ubi, int pnum) +{ + struct rb_node *p; + struct ubi_wl_prot_entry *pe = NULL; + + p = ubi->prot.pnum.rb_node; + while (p) { + + pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); + + if (pnum == pe->e->pnum) + break; + + if (pnum < pe->e->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + + ubi_assert(pe->e->pnum == pnum); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + kfree(pe); +} + +/** + * sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @e: the the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + unsigned long long ec = e->ec; + + dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec); + + err = paranoid_check_ec(ubi, e->pnum, e->ec); + if (err > 0) + return -EINVAL; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_sync_erase(ubi, e->pnum, torture); + if (err < 0) + goto out_free; + + ec += err; + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %llu", + e->pnum, ec); + err = -EINVAL; + goto out_free; + } + + dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec); + + ec_hdr->ec = cpu_to_ubi64(ec); + + err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); + if (err) + goto out_free; + + e->ec = ec; + spin_lock(&ubi->wl_lock); + if (e->ec > ubi->max_ec) + ubi->max_ec = e->ec; + spin_unlock(&ubi->wl_lock); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * check_protection_over - check if it is time to stop protecting some + * physical eraseblocks. + * @ubi: UBI device description object + * + * This function is called after each erase operation, when the absolute erase + * counter is incremented, to check if some physical eraseblock have not to be + * protected any longer. These physical eraseblocks are moved from the + * protection trees to the used tree. + */ +static void check_protection_over(struct ubi_device *ubi) +{ + struct ubi_wl_prot_entry *pe; + + /* + * There may be several protected physical eraseblock to remove, + * process them all. + */ + while (1) { + spin_lock(&ubi->wl_lock); + if (tree_empty(&ubi->prot.aec)) { + spin_unlock(&ubi->wl_lock); + break; + } + + pe = rb_entry(rb_first(&ubi->prot.aec), + struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec > ubi->abs_ec) { + spin_unlock(&ubi->wl_lock); + break; + } + + dbg_wl("PEB %d protection over, abs_ec %llu, PEB abs_ec %llu", + pe->e->pnum, ubi->abs_ec, pe->abs_ec); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + used_tree_add(ubi, pe->e); + spin_unlock(&ubi->wl_lock); + + kfree(pe); + cond_resched(); + } +} + +/** + * schedule_ubi_work - schedule a work. + * @ubi: UBI device description object + * @wrk: the work to schedule + * + * This function enqueues a work defined by @wrk to the tail of the pending + * works list. + */ +static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk) +{ + spin_lock(&ubi->wl_lock); + list_add_tail(&wrk->list, &ubi->works); + ubi_assert(ubi->works_count >= 0); + ubi->works_count += 1; + if (ubi->thread_enabled) + wake_up_process(ubi->bgt_thread); + spin_unlock(&ubi->wl_lock); +} + +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel); + +/** + * schedule_erase - schedule an erase work. + * @ubi: UBI device description object + * @e: the WL entry of the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a %-ENOMEM in case of + * failure. + */ +static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + int torture) +{ + struct ubi_work *wl_wrk; + + dbg_wl("schedule erasure of PEB %d, EC %d, torture %d", + e->pnum, e->ec, torture); + + wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL); + if (!wl_wrk) + return -ENOMEM; + + wl_wrk->func = &erase_worker; + wl_wrk->e = e; + wl_wrk->torture = torture; + + schedule_ubi_work(ubi, wl_wrk); + return 0; +} + +/** + * wear_leveling_worker - wear-leveling worker function. + * @ubi: UBI device description object + * @wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function copies a more worn out physical eraseblock to a less worn out + * one. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + int cancel) +{ + int err, put = 0; + struct ubi_wl_entry *e1, *e2; + struct ubi_vid_hdr *vid_hdr; + + kfree(wrk); + + if (cancel) + return 0; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + spin_lock(&ubi->wl_lock); + + /* + * Only one WL worker at a time is supported at this implementation, so + * make sure a PEB is not being moved already. + */ + if (ubi->move_to || tree_empty(&ubi->free) || + (tree_empty(&ubi->used) && tree_empty(&ubi->scrub))) { + /* + * Only one WL worker at a time is supported at this + * implementation, so if a LEB is already being moved, cancel. + * + * No free physical eraseblocks? Well, we cancel wear-leveling + * then. It will be triggered again when a free physical + * eraseblock appears. + * + * No used physical eraseblocks? They must be temporarily + * protected from being moved. They will be moved to the + * @ubi->used tree later and the wear-leveling will be + * triggered again. + */ + dbg_wl("cancel WL, a list is empty: free %d, used %d", + tree_empty(&ubi->free), tree_empty(&ubi->used)); + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + } + + if (tree_empty(&ubi->scrub)) { + /* + * Now pick the least worn-out used physical eraseblock and a + * highly worn-out free physical eraseblock. If the erase + * counters differ much enough, start wear-leveling. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { + dbg_wl("no WL needed: min used EC %d, max free EC %d", + e1->ec, e2->ec); + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + } + used_tree_del(ubi, e1); + dbg_wl("move PEB %d EC %d to PEB %d EC %d", + e1->pnum, e1->ec, e2->pnum, e2->ec); + } else { + e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + scrub_tree_del(ubi, e1); + dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); + } + + free_tree_del(ubi, e2); + ubi_assert(!ubi->move_from && !ubi->move_to); + ubi_assert(!ubi->move_to_put && !ubi->move_from_put); + ubi->move_from = e1; + ubi->move_to = e2; + spin_unlock(&ubi->wl_lock); + + /* + * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum. + * We so far do not know which logical eraseblock our physical + * eraseblock (@e1) belongs to. We have to read the volume identifier + * header first. + */ + + err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + if (err == UBI_IO_PEB_FREE) { + /* + * We are trying to move PEB without a VID header. UBI + * always write VID headers shortly after the PEB was + * given, so we have a situation when it did not have + * chance to write it down because it was preempted. + * Just re-schedule the work, so that next time it will + * likely have the VID header in place. + */ + dbg_wl("PEB %d has no VID header", e1->pnum); + err = 0; + } else { + ubi_err("error %d while reading VID header from PEB %d", + err, e1->pnum); + if (err > 0) + err = -EIO; + } + goto error; + } + + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); + if (err) { + if (err == UBI_IO_BITFLIPS) + err = 0; + goto error; + } + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + if (!ubi->move_to_put) + used_tree_add(ubi, e2); + else + put = 1; + ubi->move_from = ubi->move_to = NULL; + ubi->move_from_put = ubi->move_to_put = 0; + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + if (put) { + /* + * Well, the target PEB was put meanwhile, schedule it for + * erasure. + */ + dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); + err = schedule_erase(ubi, e2, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e2); + ubi_ro_mode(ubi); + } + } + + err = schedule_erase(ubi, e1, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e1); + ubi_ro_mode(ubi); + } + + dbg_wl("done"); + return err; + + /* + * Some error occurred. @e1 was not changed, so return it back. @e2 + * might be changed, schedule it for erasure. + */ +error: + if (err) + dbg_wl("error %d occurred, cancel operation", err); + ubi_assert(err <= 0); + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; + if (ubi->move_from_put) + put = 1; + else + used_tree_add(ubi, e1); + ubi->move_from = ubi->move_to = NULL; + ubi->move_from_put = ubi->move_to_put = 0; + spin_unlock(&ubi->wl_lock); + + if (put) { + /* + * Well, the target PEB was put meanwhile, schedule it for + * erasure. + */ + dbg_wl("PEB %d was put meanwhile, erase", e1->pnum); + err = schedule_erase(ubi, e1, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e1); + ubi_ro_mode(ubi); + } + } + + err = schedule_erase(ubi, e2, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e2); + ubi_ro_mode(ubi); + } + + yield(); + return err; +} + +/** + * ensure_wear_leveling - schedule wear-leveling if it is needed. + * @ubi: UBI device description object + * + * This function checks if it is time to start wear-leveling and schedules it + * if yes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +static int ensure_wear_leveling(struct ubi_device *ubi) +{ + int err = 0; + struct ubi_wl_entry *e1; + struct ubi_wl_entry *e2; + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + if (ubi->wl_scheduled) + /* Wear-leveling is already in the work queue */ + goto out_unlock; + + /* + * If the ubi->scrub tree is not empty, scrubbing is needed, and the + * the WL worker has to be scheduled anyway. + */ + if (tree_empty(&ubi->scrub)) { + if (tree_empty(&ubi->used) || tree_empty(&ubi->free)) + /* No physical eraseblocks - no deal */ + goto out_unlock; + + /* + * We schedule wear-leveling only if the difference between the + * lowest erase counter of used physical eraseblocks and a high + * erase counter of free physical eraseblocks is greater then + * %UBI_WL_THRESHOLD. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) + goto out_unlock; + dbg_wl("schedule wear-leveling"); + } else + dbg_wl("schedule scrubbing"); + + ubi->wl_scheduled = 1; + spin_unlock(&ubi->wl_lock); + + wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL); + if (!wrk) { + err = -ENOMEM; + goto out_cancel; + } + + wrk->func = &wear_leveling_worker; + schedule_ubi_work(ubi, wrk); + return err; + +out_cancel: + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; +out_unlock: + spin_unlock(&ubi->wl_lock); + return err; +} + +/** + * erase_worker - physical eraseblock erase worker function. + * @ubi: UBI device description object + * @wl_wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function erases a physical eraseblock and perform torture testing if + * needed. It also takes care about marking the physical eraseblock bad if + * needed. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel) +{ + int err; + struct ubi_wl_entry *e = wl_wrk->e; + int pnum = e->pnum; + + if (cancel) { + dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); + kfree(wl_wrk); + kmem_cache_free(wl_entries_slab, e); + return 0; + } + + dbg_wl("erase PEB %d EC %d", pnum, e->ec); + + err = sync_erase(ubi, e, wl_wrk->torture); + if (!err) { + /* Fine, we've erased it successfully */ + kfree(wl_wrk); + + spin_lock(&ubi->wl_lock); + ubi->abs_ec += 1; + free_tree_add(ubi, e); + spin_unlock(&ubi->wl_lock); + + /* + * One more erase operation has happened, take care about protected + * physical eraseblocks. + */ + check_protection_over(ubi); + + /* And take care about wear-leveling */ + err = ensure_wear_leveling(ubi); + return err; + } + + kfree(wl_wrk); + kmem_cache_free(wl_entries_slab, e); + + if (err != -EIO) { + /* + * If this is not %-EIO, we have no idea what to do. Scheduling + * this physical eraseblock for erasure again would cause + * errors again and again. Well, lets switch to RO mode. + */ + ubi_ro_mode(ubi); + return err; + } + + /* It is %-EIO, the PEB went bad */ + + if (!ubi->bad_allowed) { + ubi_err("bad physical eraseblock %d detected", pnum); + ubi_ro_mode(ubi); + err = -EIO; + } else { + int need; + + spin_lock(&ubi->volumes_lock); + need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1; + if (need > 0) { + need = ubi->avail_pebs >= need ? need : ubi->avail_pebs; + ubi->avail_pebs -= need; + ubi->rsvd_pebs += need; + ubi->beb_rsvd_pebs += need; + if (need > 0) + ubi_msg("reserve more %d PEBs", need); + } + + if (ubi->beb_rsvd_pebs == 0) { + spin_unlock(&ubi->volumes_lock); + ubi_err("no reserved physical eraseblocks"); + ubi_ro_mode(ubi); + return -EIO; + } + + spin_unlock(&ubi->volumes_lock); + ubi_msg("mark PEB %d as bad", pnum); + + err = ubi_io_mark_bad(ubi, pnum); + if (err) { + ubi_ro_mode(ubi); + return err; + } + + spin_lock(&ubi->volumes_lock); + ubi->beb_rsvd_pebs -= 1; + ubi->bad_peb_count += 1; + ubi->good_peb_count -= 1; + ubi_calculate_reserved(ubi); + if (ubi->beb_rsvd_pebs == 0) + ubi_warn("last PEB from the reserved pool was used"); + spin_unlock(&ubi->volumes_lock); + } + + return err; +} + +/** + * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling + * unit. + * @ubi: UBI device description object + * @pnum: physical eraseblock to return + * @torture: if this physical eraseblock has to be tortured + * + * This function is called to return physical eraseblock @pnum to the pool of + * free physical eraseblocks. The @torture flag has to be set if an I/O error + * occurred to this @pnum and it has to be tested. This function returns zero + * in case of success and a negative error code in case of failure. + */ +int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) +{ + int err; + struct ubi_wl_entry *e; + + dbg_wl("PEB %d", pnum); + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->peb_count); + + spin_lock(&ubi->wl_lock); + + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from) { + /* + * User is putting the physical eraseblock which was selected to + * be moved. It will be scheduled for erasure in the + * wear-leveling worker. + */ + dbg_wl("PEB %d is being moved", pnum); + ubi_assert(!ubi->move_from_put); + ubi->move_from_put = 1; + spin_unlock(&ubi->wl_lock); + return 0; + } else if (e == ubi->move_to) { + /* + * User is putting the physical eraseblock which was selected + * as the target the data is moved to. It may happen if the EBA + * unit already re-mapped the LEB but the WL unit did has not + * put the PEB to the "used" tree. + */ + dbg_wl("PEB %d is the target of data moving", pnum); + ubi_assert(!ubi->move_to_put); + ubi->move_to_put = 1; + spin_unlock(&ubi->wl_lock); + return 0; + } else { + if (in_wl_tree(e, &ubi->used)) + used_tree_del(ubi, e); + else if (in_wl_tree(e, &ubi->scrub)) + scrub_tree_del(ubi, e); + else + prot_tree_del(ubi, e->pnum); + } + spin_unlock(&ubi->wl_lock); + + err = schedule_erase(ubi, e, torture); + if (err) { + spin_lock(&ubi->wl_lock); + used_tree_add(ubi, e); + spin_unlock(&ubi->wl_lock); + } + + return err; +} + +/** + * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to schedule + * + * If a bit-flip in a physical eraseblock is detected, this physical eraseblock + * needs scrubbing. This function schedules a physical eraseblock for + * scrubbing which is done in background. This function returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum) +{ + struct ubi_wl_entry *e; + + ubi_msg("schedule PEB %d for scrubbing", pnum); + +retry: + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) { + spin_unlock(&ubi->wl_lock); + return 0; + } + + if (e == ubi->move_to) { + /* + * This physical eraseblock was used to move data to. The data + * was moved but the PEB was not yet inserted to the proper + * tree. We should just wait a little and let the WL worker + * proceed. + */ + spin_unlock(&ubi->wl_lock); + dbg_wl("the PEB %d is not in proper tree, retry", pnum); + yield(); + goto retry; + } + + if (in_wl_tree(e, &ubi->used)) + used_tree_del(ubi, e); + else + prot_tree_del(ubi, pnum); + + scrub_tree_add(ubi, e); + spin_unlock(&ubi->wl_lock); + + /* + * Technically scrubbing is the same as wear-leveling, so it is done + * by the WL worker. + */ + return ensure_wear_leveling(ubi); +} + +/** + * ubi_wl_flush - flush all pending works. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_wl_flush(struct ubi_device *ubi) +{ + int err, pending_count; + + pending_count = ubi->works_count; + + dbg_wl("flush (%d pending works)", pending_count); + + /* + * Erase while the pending works queue is not empty, but not more then + * the number of currently pending works. + */ + while (pending_count-- > 0) { + err = do_work(ubi); + if (err) + return err; + } + + return 0; +} + +/** + * tree_destroy - destroy an RB-tree. + * @root: the root of the tree to destroy + */ +static void tree_destroy(struct rb_root *root) +{ + struct rb_node *rb; + struct ubi_wl_entry *e; + + rb = root->rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + e = rb_entry(rb, struct ubi_wl_entry, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &e->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(wl_entries_slab, e); + } + } +} + +/** + * ubi_thread - UBI background thread. + * @u: the UBI device description object pointer + */ +static int ubi_thread(void *u) +{ + int failures = 0; + struct ubi_device *ubi = u; + + ubi_msg("background thread \"%s\" started, PID %d", + ubi->bgt_name, current->pid); + + for (;;) { + int err; + + if (kthread_should_stop()) + goto out; + + if (try_to_freeze()) + continue; + + spin_lock(&ubi->wl_lock); + if (list_empty(&ubi->works) || ubi->ro_mode || + !ubi->thread_enabled) { + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock(&ubi->wl_lock); + schedule(); + continue; + } + spin_unlock(&ubi->wl_lock); + + err = do_work(ubi); + if (err) { + ubi_err("%s: work failed with error code %d", + ubi->bgt_name, err); + if (failures++ > WL_MAX_FAILURES) { + /* + * Too many failures, disable the thread and + * switch to read-only mode. + */ + ubi_msg("%s: %d consecutive failures", + ubi->bgt_name, WL_MAX_FAILURES); + ubi_ro_mode(ubi); + break; + } + } else + failures = 0; + + cond_resched(); + } + +out: + dbg_wl("background thread \"%s\" is killed", ubi->bgt_name); + return 0; +} + +/** + * cancel_pending - cancel all pending works. + * @ubi: UBI device description object + */ +static void cancel_pending(struct ubi_device *ubi) +{ + while (!list_empty(&ubi->works)) { + struct ubi_work *wrk; + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + wrk->func(ubi, wrk, 1); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + } +} + +/** + * ubi_wl_init_scan - initialize the wear-leveling unit using scanning + * information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int err; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *tmp; + struct ubi_wl_entry *e; + + + ubi->used = ubi->free = ubi->scrub = RB_ROOT; + ubi->prot.pnum = ubi->prot.aec = RB_ROOT; + spin_lock_init(&ubi->wl_lock); + ubi->max_ec = si->max_ec; + INIT_LIST_HEAD(&ubi->works); + + sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); + + ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + if (IS_ERR(ubi->bgt_thread)) { + err = PTR_ERR(ubi->bgt_thread); + ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, + err); + return err; + } + + if (ubi_devices_cnt == 0) { + wl_entries_slab = kmem_cache_create("ubi_wl_entry_slab", + sizeof(struct ubi_wl_entry), + 0, 0, NULL, NULL); + if (!wl_entries_slab) + return -ENOMEM; + } + + err = -ENOMEM; + ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); + if (!ubi->lookuptbl) + goto out_free; + + list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(wl_entries_slab, e); + goto out_free; + } + } + + list_for_each_entry(seb, &si->free, u.list) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi_assert(e->ec >= 0); + free_tree_add(ubi, e); + ubi->lookuptbl[e->pnum] = e; + } + + list_for_each_entry(seb, &si->corr, u.list) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(wl_entries_slab, e); + goto out_free; + } + } + + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (!seb->scrub) { + dbg_wl("add PEB %d EC %d to the used tree", + e->pnum, e->ec); + used_tree_add(ubi, e); + } else { + dbg_wl("add PEB %d EC %d to the scrub tree", + e->pnum, e->ec); + scrub_tree_add(ubi, e); + } + } + } + + if (WL_RESERVED_PEBS > ubi->avail_pebs) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, WL_RESERVED_PEBS); + goto out_free; + } + ubi->avail_pebs -= WL_RESERVED_PEBS; + ubi->rsvd_pebs += WL_RESERVED_PEBS; + + /* Schedule wear-leveling if needed */ + err = ensure_wear_leveling(ubi); + if (err) + goto out_free; + + return 0; + +out_free: + cancel_pending(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); + if (ubi_devices_cnt == 0) + kmem_cache_destroy(wl_entries_slab); + return err; +} + +/** + * protection_trees_destroy - destroy the protection RB-trees. + * @ubi: UBI device description object + */ +static void protection_trees_destroy(struct ubi_device *ubi) +{ + struct rb_node *rb; + struct ubi_wl_prot_entry *pe; + + rb = ubi->prot.aec.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &pe->rb_aec) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(wl_entries_slab, pe->e); + kfree(pe); + } + } +} + +/** + * ubi_wl_close - close the wear-leveling unit. + * @ubi: UBI device description object + */ +void ubi_wl_close(struct ubi_device *ubi) +{ + dbg_wl("disable \"%s\"", ubi->bgt_name); + if (ubi->bgt_thread) + kthread_stop(ubi->bgt_thread); + + dbg_wl("close the UBI wear-leveling unit"); + + cancel_pending(ubi); + protection_trees_destroy(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); + if (ubi_devices_cnt == 1) + kmem_cache_destroy(wl_entries_slab); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_ec - make sure that the erase counter of a physical eraseblock + * is correct. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * @ec: the erase counter to check + * + * This function returns zero if the erase counter of physical eraseblock @pnum + * is equivalent to @ec, %1 if not, and a negative error code if an error + * occurred. + */ +static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec) +{ + int err; + long long read_ec; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + /* The header does not have to exist */ + err = 0; + goto out_free; + } + + read_ec = ubi64_to_cpu(ec_hdr->ec); + if (ec != read_ec) { + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_err("read EC is %lld, should be %d", read_ec, ec); + ubi_dbg_dump_stack(); + err = 1; + } else + err = 0; + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present + * in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns zero if @e is in the @root RB-tree and %1 if it + * is not. + */ +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root) +{ + if (in_wl_tree(e, root)) + return 0; + + ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ", + e->pnum, e->ec, root); + ubi_dbg_dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c index 06e3378..4bee99b 100644 --- a/drivers/net/3c501.c +++ b/drivers/net/3c501.c @@ -735,7 +735,6 @@ static void el_receive(struct net_device else { skb_reserve(skb,2); /* Force 16 byte alignment */ - skb->dev = dev; /* * The read increments through the bytes. The interrupt * handler will fix the pointer when it returns to diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index 702bfb2..e985a85 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -615,7 +615,6 @@ static void receive_packet(struct net_de if (test_and_set_bit(0, (void *) &adapter->dmaing)) printk(KERN_ERR "%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction); - skb->dev = dev; adapter->current_dma.direction = 0; adapter->current_dma.length = rlen; adapter->current_dma.skb = skb; @@ -1026,7 +1025,7 @@ static int send_packet(struct net_device adapter->current_dma.start_time = jiffies; if ((unsigned long)(skb->data + nlen) >= MAX_DMA_ADDRESS || nlen != skb->len) { - memcpy(adapter->dma_buffer, skb->data, nlen); + skb_copy_from_linear_data(skb, adapter->dma_buffer, nlen); memset(adapter->dma_buffer+skb->len, 0, nlen-skb->len); target = isa_virt_to_bus(adapter->dma_buffer); } diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c index 54e1d5a..eed4299 100644 --- a/drivers/net/3c507.c +++ b/drivers/net/3c507.c @@ -873,7 +873,6 @@ static void el16_rx(struct net_device *d } skb_reserve(skb,2); - skb->dev = dev; /* 'skb->data' points to the start of sk_buff data area. */ memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len); diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index f791bf0..9588da3 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -83,7 +83,6 @@ #include #include #include #include -#include #include #include /* for udelay() */ #include @@ -1091,7 +1090,6 @@ el3_rx(struct net_device *dev) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte */ /* 'skb->data' points to the start of sk_buff data area. */ diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c index c307ce6..290166d 100644 --- a/drivers/net/3c515.c +++ b/drivers/net/3c515.c @@ -1292,7 +1292,6 @@ static int corkscrew_rx(struct net_devic printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ insl(ioaddr + RX_FIFO, @@ -1363,7 +1362,6 @@ static int boomerang_rx(struct net_devic copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 4)) != 0) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ memcpy(skb_put(skb, pkt_len), diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c index 17d61eb..da1a22c 100644 --- a/drivers/net/3c523.c +++ b/drivers/net/3c523.c @@ -988,7 +988,6 @@ static void elmc_rcv_int(struct net_devi rbd->status = 0; skb = (struct sk_buff *) dev_alloc_skb(totlen + 2); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte alignment */ skb_put(skb,totlen); eth_copy_and_sum(skb, (char *) p->base+(unsigned long) rbd->buffer,totlen,0); @@ -1146,7 +1145,7 @@ #endif if (len != skb->len) memset((char *) p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN); - memcpy((char *) p->xmit_cbuffs[p->xmit_count], (char *) (skb->data), skb->len); + skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len); #if (NUM_XMIT_BUFFS == 1) #ifdef NO_NOPCOMMANDS diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index 6c7437e..c7b571b 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -1189,7 +1189,6 @@ static void mc32_rx_ring(struct net_devi } skb->protocol=eth_type_trans(skb,dev); - skb->dev=dev; dev->last_rx = jiffies; lp->net_stats.rx_packets++; lp->net_stats.rx_bytes += length; diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index b406ecf..80924f7 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -2414,7 +2414,6 @@ static int vortex_rx(struct net_device * printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ if (vp->bus_master && @@ -2491,7 +2490,6 @@ boomerang_rx(struct net_device *dev) /* Check if the packet is long enough to just accept without copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ pci_dma_sync_single_for_cpu(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); /* 'skb_put()' points to the start of sk_buff data area. */ diff --git a/drivers/net/7990.c b/drivers/net/7990.c index 1b3d11e..d396f99 100644 --- a/drivers/net/7990.c +++ b/drivers/net/7990.c @@ -331,7 +331,6 @@ #endif return 0; } - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align */ skb_put (skb, len); /* make room */ eth_copy_and_sum(skb, @@ -568,7 +567,7 @@ #endif if (skb->len < ETH_ZLEN) memset((char *)&ib->tx_buf[entry][0], 0, ETH_ZLEN); - memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen); + skb_copy_from_linear_data(skb, &ib->tx_buf[entry][0], skblen); /* Now, give the packet to the lance */ ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN); diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 12c8453..e8c9f27 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -573,7 +573,6 @@ rx_status_loop: } skb_reserve(new_skb, RX_OFFSET); - new_skb->dev = dev; pci_unmap_single(cp->pdev, mapping, buflen, PCI_DMA_FROMDEVICE); @@ -807,7 +806,7 @@ #endif if (mss) flags |= LargeSend | ((mss & MSSMask) << MSSShift); else if (skb->ip_summed == CHECKSUM_PARTIAL) { - const struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) flags |= IPCS | TCPCS; else if (ip->protocol == IPPROTO_UDP) @@ -826,7 +825,7 @@ #endif u32 first_len, first_eor; dma_addr_t first_mapping; int frag, first_entry = entry; - const struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); /* We must give this initial chunk to the device last. * Otherwise we could race with the device. @@ -1082,7 +1081,6 @@ static int cp_refill_rx (struct cp_priva if (!skb) goto err_out; - skb->dev = cp->dev; skb_reserve(skb, RX_OFFSET); mapping = pci_map_single(cp->pdev, skb->data, cp->rx_buf_sz, diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 99304b2..a844b1f 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -1904,10 +1904,10 @@ static __inline__ void wrap_copy(struct u32 left = RX_BUF_LEN - offset; if (size > left) { - memcpy(skb->data, ring + offset, left); - memcpy(skb->data+left, ring, size - left); + skb_copy_to_linear_data(skb, ring + offset, left); + skb_copy_to_linear_data_offset(skb, left, ring, size - left); } else - memcpy(skb->data, ring + offset, size); + skb_copy_to_linear_data(skb, ring + offset, size); } #endif @@ -2013,7 +2013,6 @@ no_early_rx: skb = dev_alloc_skb (pkt_size + 2); if (likely(skb)) { - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align the IP fields. */ #if RX_BUF_IDX == 3 wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); diff --git a/drivers/net/82596.c b/drivers/net/82596.c index 640d7ca..3ff1155 100644 --- a/drivers/net/82596.c +++ b/drivers/net/82596.c @@ -830,7 +830,6 @@ memory_squeeze: lp->stats.rx_dropped++; } else { - skb->dev = dev; if (!rx_in_place) { /* 16 byte align the data fields */ skb_reserve(skb, 2); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a3d46ea..dcdad21 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -486,8 +486,8 @@ config SGI_IOC3_ETH_HW_TX_CSUM enables offloading for checksums on transmit. If unsure, say Y. config MIPS_SIM_NET - tristate "MIPS simulator Network device (EXPERIMENTAL)" - depends on MIPS_SIM && EXPERIMENTAL + tristate "MIPS simulator Network device" + depends on NET_ETHERNET && MIPS_SIM help The MIPSNET device is a simple Ethernet network device which is emulated by the MIPS Simulator. @@ -1444,7 +1444,8 @@ config CS89x0 config TC35815 tristate "TOSHIBA TC35815 Ethernet support" - depends on NET_PCI && PCI && TOSHIBA_JMR3927 + depends on NET_PCI && PCI && MIPS + select MII config DGRS tristate "Digi Intl. RightSwitch SE-X support" @@ -2273,11 +2274,12 @@ config GFAR_NAPI depends on GIANFAR config UCC_GETH - tristate "Freescale QE UCC GETH" - depends on QUICC_ENGINE && UCC_FAST + tristate "Freescale QE Gigabit Ethernet" + depends on QUICC_ENGINE + select UCC_FAST help - This driver supports the Gigabit Ethernet mode of QE UCC. - QE can be found on MPC836x CPUs. + This driver supports the Gigabit Ethernet mode of the QUICC Engine, + which is available on some Freescale SOCs. config UGETH_NAPI bool "NAPI Support" @@ -2291,14 +2293,10 @@ config UGETH_FILTERING bool "Mac address filtering support" depends on UCC_GETH -config UGETH_TX_ON_DEMOND - bool "Transmit on Demond support" +config UGETH_TX_ON_DEMAND + bool "Transmit on Demand support" depends on UCC_GETH -config UGETH_HAS_GIGA - bool - depends on UCC_GETH && PPC_MPC836x - config MV643XX_ETH tristate "MV-643XX Ethernet support" depends on MOMENCO_OCELOT_C || MOMENCO_JAGUAR_ATX || MV64360 || MOMENCO_OCELOT_3 || (PPC_MULTIPLATFORM && PPC32) @@ -2929,11 +2927,6 @@ endif #NETDEVICES config NETPOLL def_bool NETCONSOLE -config NETPOLL_RX - bool "Netpoll support for trapping incoming packets" - default n - depends on NETPOLL - config NETPOLL_TRAP bool "Netpoll traffic trapping" default n diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 33af833..59c0459 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -18,7 +18,7 @@ gianfar_driver-objs := gianfar.o \ gianfar_sysfs.o obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o -ucc_geth_driver-objs := ucc_geth.o ucc_geth_phy.o +ucc_geth_driver-objs := ucc_geth.o ucc_geth_mii.o # # link order important here @@ -206,7 +206,7 @@ obj-$(CONFIG_TR) += tokenring/ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_ARCNET) += arcnet/ obj-$(CONFIG_NET_PCMCIA) += pcmcia/ -obj-$(CONFIG_NET_RADIO) += wireless/ +obj-y += wireless/ obj-$(CONFIG_NET_TULIP) += tulip/ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index d76548e..1226cbb 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -320,7 +320,6 @@ #endif return 0; } - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align */ skb_put (skb, len); /* make room */ eth_copy_and_sum(skb, @@ -599,7 +598,7 @@ #endif ib->btx_ring [entry].length = (-len) | 0xf000; ib->btx_ring [entry].misc = 0; - memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen); + skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen); /* Clear the slack of the packet, do I need this? */ if (len != skblen) diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 7138e0e..7122b7b 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -2027,7 +2027,6 @@ static void ace_rx_int(struct net_device */ csum = retdesc->tcp_udp_csum; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); /* diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index 962c954..675fe91 100644 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -798,9 +798,7 @@ #endif pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index], lp->rx_buff_len-2, PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); - skb->dev = dev; lp->rx_skbuff[rx_index] = new_skb; - new_skb->dev = dev; lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev, new_skb->data, lp->rx_buff_len-2, @@ -926,9 +924,7 @@ #endif pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index], lp->rx_buff_len-2, PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); - skb->dev = dev; lp->rx_skbuff[rx_index] = new_skb; - new_skb->dev = dev; lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev, new_skb->data, lp->rx_buff_len-2,PCI_DMA_FROMDEVICE); diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index dba5e51..da6ffa8 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -853,9 +853,9 @@ static void cops_rx(struct net_device *d return; } - skb->mac.raw = skb->data; /* Point to entire packet. */ + skb_reset_mac_header(skb); /* Point to entire packet. */ skb_pull(skb,3); - skb->h.raw = skb->data; /* Point to data (Skip header). */ + skb_reset_transport_header(skb); /* Point to data (Skip header). */ /* Update the counters. */ lp->stats.rx_packets++; diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c index 2ea44ce..6a6cbd3 100644 --- a/drivers/net/appletalk/ltpc.c +++ b/drivers/net/appletalk/ltpc.c @@ -770,13 +770,13 @@ static int sendup_buffer (struct net_dev skb->data[0] = dnode; skb->data[1] = snode; skb->data[2] = llaptype; - skb->mac.raw = skb->data; /* save pointer to llap header */ + skb_reset_mac_header(skb); /* save pointer to llap header */ skb_pull(skb,3); /* copy ddp(s,e)hdr + contents */ - memcpy(skb->data,(void*)ltdmabuf,len); + skb_copy_to_linear_data(skb, ltdmabuf, len); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); stats->rx_packets++; stats->rx_bytes+=skb->len; @@ -917,13 +917,14 @@ static int ltpc_xmit(struct sk_buff *skb int i; struct lt_sendlap cbuf; + unsigned char *hdr; cbuf.command = LT_SENDLAP; cbuf.dnode = skb->data[0]; cbuf.laptype = skb->data[2]; skb_pull(skb,3); /* skip past LLAP header */ cbuf.length = skb->len; /* this is host order */ - skb->h.raw=skb->data; + skb_reset_transport_header(skb); if(debug & DEBUG_UPPER) { printk("command "); @@ -932,11 +933,13 @@ static int ltpc_xmit(struct sk_buff *skb printk("\n"); } - do_write(dev,&cbuf,sizeof(cbuf),skb->h.raw,skb->len); + hdr = skb_transport_header(skb); + do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len); if(debug & DEBUG_UPPER) { printk("sent %d ddp bytes\n",skb->len); - for(i=0;ilen;i++) printk("%02x ",skb->h.raw[i]); + for (i = 0; i < skb->len; i++) + printk("%02x ", hdr[i]); printk("\n"); } diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c index 6318814..e0a18e7 100644 --- a/drivers/net/arcnet/arc-rawmode.c +++ b/drivers/net/arcnet/arc-rawmode.c @@ -110,7 +110,7 @@ static void rx(struct net_device *dev, i pkt = (struct archdr *) skb->data; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ARC_HDR_SIZE); /* up to sizeof(pkt->soft) has already been copied from the card */ diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 83004fd..681e20b 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -519,9 +519,12 @@ static int arcnet_header(struct sk_buff * real header when we do rebuild_header. */ *(uint16_t *) skb_push(skb, 2) = type; - if (skb->nh.raw - skb->mac.raw != 2) + /* + * XXX: Why not use skb->mac_len? + */ + if (skb->network_header - skb->mac_header != 2) BUGMSG(D_NORMAL, "arcnet_header: Yikes! diff (%d) is not 2!\n", - (int)(skb->nh.raw - skb->mac.raw)); + (int)(skb->network_header - skb->mac_header)); return -2; /* return error -- can't transmit yet! */ } else { @@ -554,11 +557,13 @@ static int arcnet_rebuild_header(struct unsigned short type; uint8_t daddr=0; struct ArcProto *proto; - - if (skb->nh.raw - skb->mac.raw != 2) { + /* + * XXX: Why not use skb->mac_len? + */ + if (skb->network_header - skb->mac_header != 2) { BUGMSG(D_NORMAL, - "rebuild_header: shouldn't be here! (hdrsize=%d)\n", - (int)(skb->nh.raw - skb->mac.raw)); + "rebuild_header: shouldn't be here! (hdrsize=%d)\n", + (int)(skb->network_header - skb->mac_header)); return 0; } type = *(uint16_t *) skb_pull(skb, 2); diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c index 6648558..cc4610d 100644 --- a/drivers/net/arcnet/capmode.c +++ b/drivers/net/arcnet/capmode.c @@ -122,10 +122,8 @@ static void rx(struct net_device *dev, i } skb_put(skb, length + ARC_HDR_SIZE + sizeof(int)); skb->dev = dev; - - pkt = (struct archdr *) skb->data; - - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); + pkt = (struct archdr *)skb_mac_header(skb); skb_pull(skb, ARC_HDR_SIZE); /* up to sizeof(pkt->soft) has already been copied from the card */ @@ -270,13 +268,13 @@ static int ack_tx(struct net_device *dev skb_put(ackskb, length + ARC_HDR_SIZE ); ackskb->dev = dev; - ackpkt = (struct archdr *) ackskb->data; - - ackskb->mac.raw = ackskb->data; + skb_reset_mac_header(ackskb); + ackpkt = (struct archdr *)skb_mac_header(ackskb); /* skb_pull(ackskb, ARC_HDR_SIZE); */ - memcpy(ackpkt, lp->outgoing.skb->data, ARC_HDR_SIZE+sizeof(struct arc_cap)); + skb_copy_from_linear_data(lp->outgoing.skb, ackpkt, + ARC_HDR_SIZE + sizeof(struct arc_cap)); ackpkt->soft.cap.proto=0; /* using protocol 0 for acknowledge */ ackpkt->soft.cap.mes.ack=acked; diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c index 6d6c69f..2de8877 100644 --- a/drivers/net/arcnet/rfc1051.c +++ b/drivers/net/arcnet/rfc1051.c @@ -94,7 +94,7 @@ static unsigned short type_trans(struct int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE; /* Pull off the arcnet header. */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, hdr_size); if (pkt->hard.dest == 0) diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c index bee3422..460a095 100644 --- a/drivers/net/arcnet/rfc1201.c +++ b/drivers/net/arcnet/rfc1201.c @@ -96,7 +96,7 @@ static unsigned short type_trans(struct int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE; /* Pull off the arcnet header. */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, hdr_size); if (pkt->hard.dest == 0) diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c index 9dfc09b..a0e68e7 100644 --- a/drivers/net/ariadne.c +++ b/drivers/net/ariadne.c @@ -743,7 +743,6 @@ static int ariadne_rx(struct net_device } - skb->dev = dev; skb_reserve(skb,2); /* 16 byte align */ skb_put(skb,pkt_len); /* Make room */ eth_copy_and_sum(skb, (char *)priv->rx_buff[entry], pkt_len,0); diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c index ddd12d4..8f0d7ce 100644 --- a/drivers/net/arm/am79c961a.c +++ b/drivers/net/arm/am79c961a.c @@ -526,7 +526,6 @@ am79c961_rx(struct net_device *dev, stru skb = dev_alloc_skb(len + 2); if (skb) { - skb->dev = dev; skb_reserve(skb, 2); am_readbuffer(dev, pktaddr, skb_put(skb, len), len); diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 1621b8f..152fa7a 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -858,7 +858,6 @@ static void at91ether_rx(struct net_devi skb_reserve(skb, 2); memcpy(skb_put(skb, pktlen), p_recv, pktlen); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; lp->stats.rx_bytes += pktlen; diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index dd698b0..2438c5b 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -255,7 +255,6 @@ static int ep93xx_rx(struct net_device * skb = dev_alloc_skb(length + 2); if (likely(skb != NULL)) { - skb->dev = dev; skb_reserve(skb, 2); dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr, length, DMA_FROM_DEVICE); diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c index a292188..f075ceb 100644 --- a/drivers/net/arm/ether1.c +++ b/drivers/net/arm/ether1.c @@ -875,7 +875,6 @@ ether1_recv_done (struct net_device *dev skb = dev_alloc_skb (length + 2); if (skb) { - skb->dev = dev; skb_reserve (skb, 2); ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c index 8411783..32da2eb 100644 --- a/drivers/net/arm/ether3.c +++ b/drivers/net/arm/ether3.c @@ -661,7 +661,6 @@ if (next_ptr < RX_START || next_ptr >= R if (skb) { unsigned char *buf; - skb->dev = dev; skb_reserve(skb, 2); buf = skb_put(skb, length); ether3_readbuffer(dev, buf + 12, length - 12); diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c index 56ae8ba..bed8e0e 100644 --- a/drivers/net/at1700.c +++ b/drivers/net/at1700.c @@ -768,7 +768,6 @@ #endif lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb,2); insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1); diff --git a/drivers/net/atari_bionet.c b/drivers/net/atari_bionet.c index 4e3bf6a..3d87bd2 100644 --- a/drivers/net/atari_bionet.c +++ b/drivers/net/atari_bionet.c @@ -453,7 +453,8 @@ bionet_send_packet(struct sk_buff *skb, stdma_lock(bionet_intr, NULL); local_irq_restore(flags); if( !STRAM_ADDR(buf+length-1) ) { - memcpy(nic_packet->buffer, skb->data, length); + skb_copy_from_linear_data(skb, nic_packet->buffer, + length); buf = (unsigned long)&((struct nic_pkt_s *)phys_nic_packet)->buffer; } @@ -544,13 +545,13 @@ bionet_poll_rx(struct net_device *dev) { break; } - skb->dev = dev; skb_reserve( skb, 2 ); /* 16 Byte align */ skb_put( skb, pkt_len ); /* make room */ /* 'skb->data' points to the start of sk_buff data area. */ - memcpy(skb->data, nic_packet->buffer, pkt_len); + skb_copy_to_linear_data(skb, nic_packet->buffer, + pkt_len); skb->protocol = eth_type_trans( skb, dev ); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/atari_pamsnet.c b/drivers/net/atari_pamsnet.c index 3b54361..5471440 100644 --- a/drivers/net/atari_pamsnet.c +++ b/drivers/net/atari_pamsnet.c @@ -717,7 +717,8 @@ pamsnet_send_packet(struct sk_buff *skb, local_irq_restore(flags); if( !STRAM_ADDR(buf+length-1) ) { - memcpy(nic_packet->buffer, skb->data, length); + skb_copy_from_linear_data(skb, nic_packet->buffer, + length); buf = (unsigned long)phys_nic_packet; } @@ -792,7 +793,8 @@ pamsnet_poll_rx(struct net_device *dev) /* 'skb->data' points to the start of sk_buff data area. */ - memcpy(skb->data, nic_packet->buffer, pkt_len); + skb_copy_to_linear_data(skb, nic_packet->buffer, + pkt_len); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c index 7e37ac8..dfa8b9b 100644 --- a/drivers/net/atarilance.c +++ b/drivers/net/atarilance.c @@ -1047,7 +1047,6 @@ static int lance_rx( struct net_device * pkt_len ); } - skb->dev = dev; skb_reserve( skb, 2 ); /* 16 byte align */ skb_put( skb, pkt_len ); /* Make room */ lp->memcpy_f( skb->data, PKTBUF_ADDR(head), pkt_len ); diff --git a/drivers/net/atl1/atl1_main.c b/drivers/net/atl1/atl1_main.c index 8606eac..4b1d4d1 100644 --- a/drivers/net/atl1/atl1_main.c +++ b/drivers/net/atl1/atl1_main.c @@ -408,7 +408,6 @@ static void atl1_rx_checksum(struct atl1 static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) { struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; - struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; struct page *page; unsigned long offset; @@ -444,7 +443,6 @@ static u16 atl1_alloc_rx_buffers(struct * the 14 byte MAC header is removed */ skb_reserve(skb, NET_IP_ALIGN); - skb->dev = netdev; buffer_info->alloced = 1; buffer_info->skb = skb; @@ -1296,19 +1294,21 @@ static int atl1_tso(struct atl1_adapter } if (skb->protocol == ntohs(ETH_P_IP)) { - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, 0, - IPPROTO_TCP, 0); - ipofst = skb->nh.raw - skb->data; + struct iphdr *iph = ip_hdr(skb); + + iph->tot_len = 0; + iph->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, + 0); + ipofst = skb_network_offset(skb); if (ipofst != ENET_HEADER_SIZE) /* 802.3 frame */ tso->tsopl |= 1 << TSO_PARAM_ETHTYPE_SHIFT; - tso->tsopl |= (skb->nh.iph->ihl & + tso->tsopl |= (iph->ihl & CSUM_PARAM_IPHL_MASK) << CSUM_PARAM_IPHL_SHIFT; - tso->tsopl |= ((skb->h.th->doff << 2) & + tso->tsopl |= (tcp_hdrlen(skb) & TSO_PARAM_TCPHDRLEN_MASK) << TSO_PARAM_TCPHDRLEN_SHIFT; tso->tsopl |= (skb_shinfo(skb)->gso_size & TSO_PARAM_MSS_MASK) << TSO_PARAM_MSS_SHIFT; @@ -1327,8 +1327,8 @@ static int atl1_tx_csum(struct atl1_adap u8 css, cso; if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { - cso = skb->h.raw - skb->data; - css = (skb->h.raw + skb->csum_offset) - skb->data; + cso = skb_transport_offset(skb); + css = cso + skb->csum_offset; if (unlikely(cso & 0x1)) { printk(KERN_DEBUG "%s: payload offset != even number\n", atl1_driver_name); @@ -1370,8 +1370,7 @@ static void atl1_tx_map(struct atl1_adap if (tcp_seg) { /* TSO/GSO */ - proto_hdr_len = - ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); buffer_info->length = proto_hdr_len; page = virt_to_page(skb->data); offset = (unsigned long)skb->data & ~PAGE_MASK; @@ -1563,8 +1562,8 @@ static int atl1_xmit_frame(struct sk_buf mss = skb_shinfo(skb)->gso_size; if (mss) { if (skb->protocol == htons(ETH_P_IP)) { - proto_hdr_len = ((skb->h.raw - skb->data) + - (skb->h.th->doff << 2)); + proto_hdr_len = (skb_transport_offset(skb) + + tcp_hdrlen(skb)); if (unlikely(proto_hdr_len > len)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; diff --git a/drivers/net/atp.c b/drivers/net/atp.c index 2d306fc..18aba83 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -793,7 +793,6 @@ static void net_rx(struct net_device *de lp->stats.rx_dropped++; goto done; } - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port); diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 69ae229..d10fb80 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -1125,7 +1125,7 @@ static int au1000_tx(struct sk_buff *skb } pDB = aup->tx_db_inuse[aup->tx_head]; - memcpy((void *)pDB->vaddr, skb->data, skb->len); + skb_copy_from_linear_data(skb, pDB->vaddr, skb->len); if (skb->len < ETH_ZLEN) { for (i=skb->len; ivaddr)[i] = 0; @@ -1205,7 +1205,6 @@ static int au1000_rx(struct net_device * aup->stats.rx_dropped++; continue; } - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte IP header align */ eth_copy_and_sum(skb, (unsigned char *)pDB->vaddr, frmlen, 0); diff --git a/drivers/net/b44.c b/drivers/net/b44.c index d742bfe..879a2ff 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -825,12 +825,11 @@ static int b44_rx(struct b44 *bp, int bu if (copy_skb == NULL) goto drop_it_no_recycle; - copy_skb->dev = bp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); /* DMA sync done above, copy just the actual packet */ - memcpy(copy_skb->data, skb->data+bp->rx_offset, len); - + skb_copy_from_linear_data_offset(skb, bp->rx_offset, + copy_skb->data, len); skb = copy_skb; } skb->ip_summed = CHECKSUM_NONE; @@ -1007,7 +1006,8 @@ static int b44_start_xmit(struct sk_buff goto err_out; } - memcpy(skb_put(bounce_skb, len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(bounce_skb, len), + skb->len); dev_kfree_skb_any(skb); skb = bounce_skb; } diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c index c143304..4612725 100644 --- a/drivers/net/bmac.c +++ b/drivers/net/bmac.c @@ -715,7 +715,6 @@ static irqreturn_t bmac_rxdma_intr(int i if (skb != NULL) { nb -= ETHERCRC; skb_put(skb, nb); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index e85f5ec..f98a220 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -1884,10 +1884,8 @@ bnx2_rx_int(struct bnx2 *bp, int budget) goto reuse_rx; /* aligned copy */ - memcpy(new_skb->data, - skb->data + bp->rx_offset - 2, - len + 2); - + skb_copy_from_linear_data_offset(skb, bp->rx_offset - 2, + new_skb->data, len + 2); skb_reserve(new_skb, 2); skb_put(new_skb, len); @@ -4513,6 +4511,7 @@ bnx2_start_xmit(struct sk_buff *skb, str if ((mss = skb_shinfo(skb)->gso_size) && (skb->len > (bp->dev->mtu + ETH_HLEN))) { u32 tcp_opt_len, ip_tcp_len; + struct iphdr *iph; if (skb_header_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { @@ -4520,25 +4519,23 @@ bnx2_start_xmit(struct sk_buff *skb, str return NETDEV_TX_OK; } - tcp_opt_len = ((skb->h.th->doff - 5) * 4); vlan_tag_flags |= TX_BD_FLAGS_SW_LSO; tcp_opt_len = 0; - if (skb->h.th->doff > 5) { - tcp_opt_len = (skb->h.th->doff - 5) << 2; - } - ip_tcp_len = (skb->nh.iph->ihl << 2) + sizeof(struct tcphdr); - - skb->nh.iph->check = 0; - skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, IPPROTO_TCP, 0); - - if (tcp_opt_len || (skb->nh.iph->ihl > 5)) { - vlan_tag_flags |= ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)) << 8; + if (tcp_hdr(skb)->doff > 5) + tcp_opt_len = tcp_optlen(skb); + + ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); + + iph = ip_hdr(skb); + iph->check = 0; + iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, 0); + if (tcp_opt_len || (iph->ihl > 5)) { + vlan_tag_flags |= ((iph->ihl - 5) + + (tcp_opt_len >> 2)) << 8; } } else diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 3fb354d..7e03f41 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -884,8 +884,8 @@ static int ad_lacpdu_send(struct port *p } skb->dev = slave->dev; - skb->mac.raw = skb->data; - skb->nh.raw = skb->data + ETH_HLEN; + skb_reset_mac_header(skb); + skb->network_header = skb->mac_header + ETH_HLEN; skb->protocol = PKT_TYPE_LACPDU; skb->priority = TC_PRIO_CONTROL; @@ -928,8 +928,8 @@ static int ad_marker_send(struct port *p skb_reserve(skb, 16); skb->dev = slave->dev; - skb->mac.raw = skb->data; - skb->nh.raw = skb->data + ETH_HLEN; + skb_reset_mac_header(skb); + skb->network_header = skb->mac_header + ETH_HLEN; skb->protocol = PKT_TYPE_LACPDU; marker_header = (struct marker_header *)skb_put(skb, length); diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 217a2ee..92c3b6f 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -104,10 +104,15 @@ struct arp_pkt { }; #pragma pack() +static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb) +{ + return (struct arp_pkt *)skb_network_header(skb); +} + /* Forward declaration */ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]); -static inline u8 _simple_hash(u8 *hash_start, int hash_size) +static inline u8 _simple_hash(const u8 *hash_start, int hash_size) { int i; u8 hash = 0; @@ -613,7 +618,7 @@ static void rlb_req_update_subnet_client static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bond) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); - struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw; + struct arp_pkt *arp = arp_pkt(skb); struct slave *assigned_slave; struct rlb_client_info *client_info; u32 hash_index = 0; @@ -701,7 +706,7 @@ static struct slave *rlb_choose_channel( */ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) { - struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw; + struct arp_pkt *arp = arp_pkt(skb); struct slave *tx_slave = NULL; if (arp->op_code == __constant_htons(ARPOP_REPLY)) { @@ -890,8 +895,8 @@ static void alb_send_learning_packets(st data = skb_put(skb, size); memcpy(data, &pkt, size); - skb->mac.raw = data; - skb->nh.raw = data + ETH_HLEN; + skb_reset_mac_header(skb); + skb->network_header = skb->mac_header + ETH_HLEN; skb->protocol = pkt.type; skb->priority = TC_PRIO_CONTROL; skb->dev = slave->dev; @@ -1263,10 +1268,10 @@ int bond_alb_xmit(struct sk_buff *skb, s int hash_size = 0; int do_tx_balance = 1; u32 hash_index = 0; - u8 *hash_start = NULL; + const u8 *hash_start = NULL; int res = 1; - skb->mac.raw = (unsigned char *)skb->data; + skb_reset_mac_header(skb); eth_data = eth_hdr(skb); /* make sure that the curr_active_slave and the slaves list do @@ -1280,15 +1285,18 @@ int bond_alb_xmit(struct sk_buff *skb, s } switch (ntohs(skb->protocol)) { - case ETH_P_IP: + case ETH_P_IP: { + const struct iphdr *iph = ip_hdr(skb); + if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) || - (skb->nh.iph->daddr == ip_bcast) || - (skb->nh.iph->protocol == IPPROTO_IGMP)) { + (iph->daddr == ip_bcast) || + (iph->protocol == IPPROTO_IGMP)) { do_tx_balance = 0; break; } - hash_start = (char*)&(skb->nh.iph->daddr); - hash_size = sizeof(skb->nh.iph->daddr); + hash_start = (char *)&(iph->daddr); + hash_size = sizeof(iph->daddr); + } break; case ETH_P_IPV6: if (memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) { @@ -1296,8 +1304,8 @@ int bond_alb_xmit(struct sk_buff *skb, s break; } - hash_start = (char*)&(skb->nh.ipv6h->daddr); - hash_size = sizeof(skb->nh.ipv6h->daddr); + hash_start = (char *)&(ipv6_hdr(skb)->daddr); + hash_size = sizeof(ipv6_hdr(skb)->daddr); break; case ETH_P_IPX: if (ipx_hdr(skb)->ipx_checksum != diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e4724d8..724bce5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1360,13 +1360,6 @@ int bond_enslave(struct net_device *bond goto err_undo_flags; } - if (slave_dev->get_stats == NULL) { - printk(KERN_NOTICE DRV_NAME - ": %s: the driver for slave device %s does not provide " - "get_stats function, network statistics will be " - "inaccurate.\n", bond_dev->name, slave_dev->name); - } - new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL); if (!new_slave) { res = -ENOMEM; @@ -2524,7 +2517,7 @@ static int bond_arp_rcv(struct sk_buff * (2 * sizeof(u32))))) goto out_unlock; - arp = skb->nh.arph; + arp = arp_hdr(skb); if (arp->ar_hln != dev->addr_len || skb->pkt_type == PACKET_OTHERHOST || skb->pkt_type == PACKET_LOOPBACK || @@ -3476,7 +3469,7 @@ static int bond_xmit_hash_policy_l34(str struct net_device *bond_dev, int count) { struct ethhdr *data = (struct ethhdr *)skb->data; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); u16 *layer4hdr = (u16 *)((u32 *)iph + iph->ihl); int layer4_xor = 0; @@ -3640,35 +3633,32 @@ static struct net_device_stats *bond_get read_lock_bh(&bond->lock); bond_for_each_slave(bond, slave, i) { - if (slave->dev->get_stats) { - sstats = slave->dev->get_stats(slave->dev); - - stats->rx_packets += sstats->rx_packets; - stats->rx_bytes += sstats->rx_bytes; - stats->rx_errors += sstats->rx_errors; - stats->rx_dropped += sstats->rx_dropped; - - stats->tx_packets += sstats->tx_packets; - stats->tx_bytes += sstats->tx_bytes; - stats->tx_errors += sstats->tx_errors; - stats->tx_dropped += sstats->tx_dropped; - - stats->multicast += sstats->multicast; - stats->collisions += sstats->collisions; - - stats->rx_length_errors += sstats->rx_length_errors; - stats->rx_over_errors += sstats->rx_over_errors; - stats->rx_crc_errors += sstats->rx_crc_errors; - stats->rx_frame_errors += sstats->rx_frame_errors; - stats->rx_fifo_errors += sstats->rx_fifo_errors; - stats->rx_missed_errors += sstats->rx_missed_errors; - - stats->tx_aborted_errors += sstats->tx_aborted_errors; - stats->tx_carrier_errors += sstats->tx_carrier_errors; - stats->tx_fifo_errors += sstats->tx_fifo_errors; - stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors; - stats->tx_window_errors += sstats->tx_window_errors; - } + sstats = slave->dev->get_stats(slave->dev); + stats->rx_packets += sstats->rx_packets; + stats->rx_bytes += sstats->rx_bytes; + stats->rx_errors += sstats->rx_errors; + stats->rx_dropped += sstats->rx_dropped; + + stats->tx_packets += sstats->tx_packets; + stats->tx_bytes += sstats->tx_bytes; + stats->tx_errors += sstats->tx_errors; + stats->tx_dropped += sstats->tx_dropped; + + stats->multicast += sstats->multicast; + stats->collisions += sstats->collisions; + + stats->rx_length_errors += sstats->rx_length_errors; + stats->rx_over_errors += sstats->rx_over_errors; + stats->rx_crc_errors += sstats->rx_crc_errors; + stats->rx_frame_errors += sstats->rx_frame_errors; + stats->rx_fifo_errors += sstats->rx_fifo_errors; + stats->rx_missed_errors += sstats->rx_missed_errors; + + stats->tx_aborted_errors += sstats->tx_aborted_errors; + stats->tx_carrier_errors += sstats->tx_carrier_errors; + stats->tx_fifo_errors += sstats->tx_fifo_errors; + stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors; + stats->tx_window_errors += sstats->tx_window_errors; } read_unlock_bh(&bond->lock); diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index c812648..4aec747 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -1995,7 +1995,6 @@ static int cas_rx_process_pkt(struct cas return -1; *skbref = skb; - skb->dev = cp->dev; skb_reserve(skb, swivel); p = skb->data; @@ -2822,10 +2821,8 @@ static inline int cas_xmit_tx_ringN(stru ctrl = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { - u64 csum_start_off, csum_stuff_off; - - csum_start_off = (u64) (skb->h.raw - skb->data); - csum_stuff_off = csum_start_off + skb->csum_offset; + const u64 csum_start_off = skb_transport_offset(skb); + const u64 csum_stuff_off = csum_start_off + skb->csum_offset; ctrl = TX_DESC_CSUM_EN | CAS_BASE(TX_DESC_CSUM_START, csum_start_off) | @@ -2849,8 +2846,8 @@ static inline int cas_xmit_tx_ringN(stru ctrl | TX_DESC_SOF, 0); entry = TX_DESC_NEXT(ring, entry); - memcpy(tx_tiny_buf(cp, ring, entry), skb->data + - len - tabort, tabort); + skb_copy_from_linear_data_offset(skb, len - tabort, + tx_tiny_buf(cp, ring, entry), tabort); mapping = tx_tiny_map(cp, ring, entry, tentry); cas_write_txd(cp, ring, entry, mapping, tabort, ctrl, (nr_frags == 0)); diff --git a/drivers/net/chelsio/Makefile b/drivers/net/chelsio/Makefile index 382d23f..743ad8b 100644 --- a/drivers/net/chelsio/Makefile +++ b/drivers/net/chelsio/Makefile @@ -4,8 +4,6 @@ # obj-$(CONFIG_CHELSIO_T1) += cxgb.o -cxgb-$(CONFIG_CHELSIO_T1_1G) += ixf1010.o mac.o mv88e1xxx.o vsc7326.o vsc8244.o +cxgb-$(CONFIG_CHELSIO_T1_1G) += mac.o mv88e1xxx.o vsc7326.o cxgb-objs := cxgb2.o espi.o tp.o pm3393.o sge.o subr.o \ mv88x201x.o my3126.o $(cxgb-y) - - diff --git a/drivers/net/chelsio/common.h b/drivers/net/chelsio/common.h index 787f2f2..8ba702c 100644 --- a/drivers/net/chelsio/common.h +++ b/drivers/net/chelsio/common.h @@ -322,9 +322,9 @@ struct board_info { unsigned char mdio_mdiinv; unsigned char mdio_mdc; unsigned char mdio_phybaseaddr; - struct gmac *gmac; - struct gphy *gphy; - struct mdio_ops *mdio_ops; + const struct gmac *gmac; + const struct gphy *gphy; + const struct mdio_ops *mdio_ops; const char *desc; }; diff --git a/drivers/net/chelsio/cphy.h b/drivers/net/chelsio/cphy.h index cf91434..79d855e 100644 --- a/drivers/net/chelsio/cphy.h +++ b/drivers/net/chelsio/cphy.h @@ -100,7 +100,7 @@ struct cphy { u32 elmer_gpo; - struct cphy_ops *ops; /* PHY operations */ + const struct cphy_ops *ops; /* PHY operations */ int (*mdio_read)(adapter_t *adapter, int phy_addr, int mmd_addr, int reg_addr, unsigned int *val); int (*mdio_write)(adapter_t *adapter, int phy_addr, int mmd_addr, @@ -136,7 +136,7 @@ static inline int simple_mdio_write(stru /* Convenience initializer */ static inline void cphy_init(struct cphy *phy, adapter_t *adapter, int phy_addr, struct cphy_ops *phy_ops, - struct mdio_ops *mdio_ops) + const struct mdio_ops *mdio_ops) { phy->adapter = adapter; phy->addr = phy_addr; @@ -151,7 +151,7 @@ static inline void cphy_init(struct cphy struct gphy { /* Construct a PHY instance with the given PHY address */ struct cphy *(*create)(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops); + const struct mdio_ops *mdio_ops); /* * Reset the PHY chip. This resets the whole PHY chip, not individual @@ -160,11 +160,9 @@ struct gphy { int (*reset)(adapter_t *adapter); }; -extern struct gphy t1_my3126_ops; -extern struct gphy t1_mv88e1xxx_ops; -extern struct gphy t1_vsc8244_ops; -extern struct gphy t1_xpak_ops; -extern struct gphy t1_mv88x201x_ops; -extern struct gphy t1_dummy_phy_ops; +extern const struct gphy t1_my3126_ops; +extern const struct gphy t1_mv88e1xxx_ops; +extern const struct gphy t1_vsc8244_ops; +extern const struct gphy t1_mv88x201x_ops; #endif /* _CXGB_CPHY_H_ */ diff --git a/drivers/net/chelsio/gmac.h b/drivers/net/chelsio/gmac.h index 006a2eb..d423374 100644 --- a/drivers/net/chelsio/gmac.h +++ b/drivers/net/chelsio/gmac.h @@ -126,7 +126,7 @@ typedef struct _cmac_instance cmac_insta struct cmac { struct cmac_statistics stats; adapter_t *adapter; - struct cmac_ops *ops; + const struct cmac_ops *ops; cmac_instance *instance; }; @@ -136,11 +136,7 @@ struct gmac { int (*reset)(adapter_t *); }; -extern struct gmac t1_pm3393_ops; -extern struct gmac t1_chelsio_mac_ops; -extern struct gmac t1_vsc7321_ops; -extern struct gmac t1_vsc7326_ops; -extern struct gmac t1_ixf1010_ops; -extern struct gmac t1_dummy_mac_ops; +extern const struct gmac t1_pm3393_ops; +extern const struct gmac t1_vsc7326_ops; #endif /* _CXGB_GMAC_H_ */ diff --git a/drivers/net/chelsio/ixf1010.c b/drivers/net/chelsio/ixf1010.c deleted file mode 100644 index 10b2a9a..0000000 --- a/drivers/net/chelsio/ixf1010.c +++ /dev/null @@ -1,505 +0,0 @@ -/* $Date: 2005/11/12 02:13:49 $ $RCSfile: ixf1010.c,v $ $Revision: 1.36 $ */ -#include "gmac.h" -#include "elmer0.h" - -/* Update fast changing statistics every 15 seconds */ -#define STATS_TICK_SECS 15 -/* 30 minutes for full statistics update */ -#define MAJOR_UPDATE_TICKS (1800 / STATS_TICK_SECS) - -/* - * The IXF1010 can handle frames up to 16383 bytes but it's optimized for - * frames up to 9831 (0x2667) bytes, so we limit jumbo frame size to this. - * This length includes ethernet header and FCS. - */ -#define MAX_FRAME_SIZE 0x2667 - -/* MAC registers */ -enum { - /* Per-port registers */ - REG_MACADDR_LOW = 0, - REG_MACADDR_HIGH = 0x4, - REG_FDFC_TYPE = 0xC, - REG_FC_TX_TIMER_VALUE = 0x1c, - REG_IPG_RX_TIME1 = 0x28, - REG_IPG_RX_TIME2 = 0x2c, - REG_IPG_TX_TIME = 0x30, - REG_PAUSE_THRES = 0x38, - REG_MAX_FRAME_SIZE = 0x3c, - REG_RGMII_SPEED = 0x40, - REG_FC_ENABLE = 0x48, - REG_DISCARD_CTRL_FRAMES = 0x54, - REG_DIVERSE_CONFIG = 0x60, - REG_RX_FILTER = 0x64, - REG_MC_ADDR_LOW = 0x68, - REG_MC_ADDR_HIGH = 0x6c, - - REG_RX_OCTETS_OK = 0x80, - REG_RX_OCTETS_BAD = 0x84, - REG_RX_UC_PKTS = 0x88, - REG_RX_MC_PKTS = 0x8c, - REG_RX_BC_PKTS = 0x90, - REG_RX_FCS_ERR = 0xb0, - REG_RX_TAGGED = 0xb4, - REG_RX_DATA_ERR = 0xb8, - REG_RX_ALIGN_ERR = 0xbc, - REG_RX_LONG_ERR = 0xc0, - REG_RX_JABBER_ERR = 0xc4, - REG_RX_PAUSE_FRAMES = 0xc8, - REG_RX_UNKNOWN_CTRL_FRAMES = 0xcc, - REG_RX_VERY_LONG_ERR = 0xd0, - REG_RX_RUNT_ERR = 0xd4, - REG_RX_SHORT_ERR = 0xd8, - REG_RX_SYMBOL_ERR = 0xe4, - - REG_TX_OCTETS_OK = 0x100, - REG_TX_OCTETS_BAD = 0x104, - REG_TX_UC_PKTS = 0x108, - REG_TX_MC_PKTS = 0x10c, - REG_TX_BC_PKTS = 0x110, - REG_TX_EXCESSIVE_LEN_DROP = 0x14c, - REG_TX_UNDERRUN = 0x150, - REG_TX_TAGGED = 0x154, - REG_TX_PAUSE_FRAMES = 0x15C, - - /* Global registers */ - REG_PORT_ENABLE = 0x1400, - - REG_JTAG_ID = 0x1430, - - RX_FIFO_HIGH_WATERMARK_BASE = 0x1600, - RX_FIFO_LOW_WATERMARK_BASE = 0x1628, - RX_FIFO_FRAMES_REMOVED_BASE = 0x1650, - - REG_RX_ERR_DROP = 0x167c, - REG_RX_FIFO_OVERFLOW_EVENT = 0x1680, - - TX_FIFO_HIGH_WATERMARK_BASE = 0x1800, - TX_FIFO_LOW_WATERMARK_BASE = 0x1828, - TX_FIFO_XFER_THRES_BASE = 0x1850, - - REG_TX_FIFO_OVERFLOW_EVENT = 0x1878, - REG_TX_FIFO_OOS_EVENT = 0x1884, - - TX_FIFO_FRAMES_REMOVED_BASE = 0x1888, - - REG_SPI_RX_BURST = 0x1c00, - REG_SPI_RX_TRAINING = 0x1c04, - REG_SPI_RX_CALENDAR = 0x1c08, - REG_SPI_TX_SYNC = 0x1c0c -}; - -enum { /* RMON registers */ - REG_RxOctetsTotalOK = 0x80, - REG_RxOctetsBad = 0x84, - REG_RxUCPkts = 0x88, - REG_RxMCPkts = 0x8c, - REG_RxBCPkts = 0x90, - REG_RxJumboPkts = 0xac, - REG_RxFCSErrors = 0xb0, - REG_RxDataErrors = 0xb8, - REG_RxAlignErrors = 0xbc, - REG_RxLongErrors = 0xc0, - REG_RxJabberErrors = 0xc4, - REG_RxPauseMacControlCounter = 0xc8, - REG_RxVeryLongErrors = 0xd0, - REG_RxRuntErrors = 0xd4, - REG_RxShortErrors = 0xd8, - REG_RxSequenceErrors = 0xe0, - REG_RxSymbolErrors = 0xe4, - - REG_TxOctetsTotalOK = 0x100, - REG_TxOctetsBad = 0x104, - REG_TxUCPkts = 0x108, - REG_TxMCPkts = 0x10c, - REG_TxBCPkts = 0x110, - REG_TxJumboPkts = 0x12C, - REG_TxTotalCollisions = 0x134, - REG_TxExcessiveLengthDrop = 0x14c, - REG_TxUnderrun = 0x150, - REG_TxCRCErrors = 0x158, - REG_TxPauseFrames = 0x15c -}; - -enum { - DIVERSE_CONFIG_PAD_ENABLE = 0x80, - DIVERSE_CONFIG_CRC_ADD = 0x40 -}; - -#define MACREG_BASE 0 -#define MACREG(mac, mac_reg) ((mac)->instance->mac_base + (mac_reg)) - -struct _cmac_instance { - u32 mac_base; - u32 index; - u32 version; - u32 ticks; -}; - -static void disable_port(struct cmac *mac) -{ - u32 val; - - t1_tpi_read(mac->adapter, REG_PORT_ENABLE, &val); - val &= ~(1 << mac->instance->index); - t1_tpi_write(mac->adapter, REG_PORT_ENABLE, val); -} - -/* - * Read the current values of the RMON counters and add them to the cumulative - * port statistics. The HW RMON counters are cleared by this operation. - */ -static void port_stats_update(struct cmac *mac) -{ - static struct { - unsigned int reg; - unsigned int offset; - } hw_stats[] = { - -#define HW_STAT(name, stat_name) \ - { REG_##name, \ - (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL } - - /* Rx stats */ - HW_STAT(RxOctetsTotalOK, RxOctetsOK), - HW_STAT(RxOctetsBad, RxOctetsBad), - HW_STAT(RxUCPkts, RxUnicastFramesOK), - HW_STAT(RxMCPkts, RxMulticastFramesOK), - HW_STAT(RxBCPkts, RxBroadcastFramesOK), - HW_STAT(RxJumboPkts, RxJumboFramesOK), - HW_STAT(RxFCSErrors, RxFCSErrors), - HW_STAT(RxAlignErrors, RxAlignErrors), - HW_STAT(RxLongErrors, RxFrameTooLongErrors), - HW_STAT(RxVeryLongErrors, RxFrameTooLongErrors), - HW_STAT(RxPauseMacControlCounter, RxPauseFrames), - HW_STAT(RxDataErrors, RxDataErrors), - HW_STAT(RxJabberErrors, RxJabberErrors), - HW_STAT(RxRuntErrors, RxRuntErrors), - HW_STAT(RxShortErrors, RxRuntErrors), - HW_STAT(RxSequenceErrors, RxSequenceErrors), - HW_STAT(RxSymbolErrors, RxSymbolErrors), - - /* Tx stats (skip collision stats as we are full-duplex only) */ - HW_STAT(TxOctetsTotalOK, TxOctetsOK), - HW_STAT(TxOctetsBad, TxOctetsBad), - HW_STAT(TxUCPkts, TxUnicastFramesOK), - HW_STAT(TxMCPkts, TxMulticastFramesOK), - HW_STAT(TxBCPkts, TxBroadcastFramesOK), - HW_STAT(TxJumboPkts, TxJumboFramesOK), - HW_STAT(TxPauseFrames, TxPauseFrames), - HW_STAT(TxExcessiveLengthDrop, TxLengthErrors), - HW_STAT(TxUnderrun, TxUnderrun), - HW_STAT(TxCRCErrors, TxFCSErrors) - }, *p = hw_stats; - u64 *stats = (u64 *) &mac->stats; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(hw_stats); i++) { - u32 val; - - t1_tpi_read(mac->adapter, MACREG(mac, p->reg), &val); - stats[p->offset] += val; - } -} - -/* No-op interrupt operation as this MAC does not support interrupts */ -static int mac_intr_op(struct cmac *mac) -{ - return 0; -} - -/* Expect MAC address to be in network byte order. */ -static int mac_set_address(struct cmac *mac, u8 addr[6]) -{ - u32 addr_lo, addr_hi; - - addr_lo = addr[2]; - addr_lo = (addr_lo << 8) | addr[3]; - addr_lo = (addr_lo << 8) | addr[4]; - addr_lo = (addr_lo << 8) | addr[5]; - - addr_hi = addr[0]; - addr_hi = (addr_hi << 8) | addr[1]; - - t1_tpi_write(mac->adapter, MACREG(mac, REG_MACADDR_LOW), addr_lo); - t1_tpi_write(mac->adapter, MACREG(mac, REG_MACADDR_HIGH), addr_hi); - return 0; -} - -static int mac_get_address(struct cmac *mac, u8 addr[6]) -{ - u32 addr_lo, addr_hi; - - t1_tpi_read(mac->adapter, MACREG(mac, REG_MACADDR_LOW), &addr_lo); - t1_tpi_read(mac->adapter, MACREG(mac, REG_MACADDR_HIGH), &addr_hi); - - addr[0] = (u8) (addr_hi >> 8); - addr[1] = (u8) addr_hi; - addr[2] = (u8) (addr_lo >> 24); - addr[3] = (u8) (addr_lo >> 16); - addr[4] = (u8) (addr_lo >> 8); - addr[5] = (u8) addr_lo; - return 0; -} - -/* This is intended to reset a port, not the whole MAC */ -static int mac_reset(struct cmac *mac) -{ - return 0; -} - -static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm) -{ - u32 val, new_mode; - adapter_t *adapter = mac->adapter; - u32 addr_lo, addr_hi; - u8 *addr; - - t1_tpi_read(adapter, MACREG(mac, REG_RX_FILTER), &val); - new_mode = val & ~7; - if (!t1_rx_mode_promisc(rm) && mac->instance->version > 0) - new_mode |= 1; /* only set if version > 0 due to erratum */ - if (!t1_rx_mode_promisc(rm) && !t1_rx_mode_allmulti(rm) - && t1_rx_mode_mc_cnt(rm) <= 1) - new_mode |= 2; - if (new_mode != val) - t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), new_mode); - switch (t1_rx_mode_mc_cnt(rm)) { - case 0: - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_LOW), 0); - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_HIGH), 0); - break; - case 1: - addr = t1_get_next_mcaddr(rm); - addr_lo = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | - addr[5]; - addr_hi = (addr[0] << 8) | addr[1]; - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_LOW), addr_lo); - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_HIGH), addr_hi); - break; - default: - break; - } - return 0; -} - -static int mac_set_mtu(struct cmac *mac, int mtu) -{ - /* MAX_FRAME_SIZE inludes header + FCS, mtu doesn't */ - if (mtu > (MAX_FRAME_SIZE - 14 - 4)) - return -EINVAL; - t1_tpi_write(mac->adapter, MACREG(mac, REG_MAX_FRAME_SIZE), - mtu + 14 + 4); - return 0; -} - -static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, - int fc) -{ - u32 val; - - if (speed >= 0 && speed != SPEED_100 && speed != SPEED_1000) - return -1; - if (duplex >= 0 && duplex != DUPLEX_FULL) - return -1; - - if (speed >= 0) { - val = speed == SPEED_100 ? 1 : 2; - t1_tpi_write(mac->adapter, MACREG(mac, REG_RGMII_SPEED), val); - } - - t1_tpi_read(mac->adapter, MACREG(mac, REG_FC_ENABLE), &val); - val &= ~3; - if (fc & PAUSE_RX) - val |= 1; - if (fc & PAUSE_TX) - val |= 2; - t1_tpi_write(mac->adapter, MACREG(mac, REG_FC_ENABLE), val); - return 0; -} - -static int mac_get_speed_duplex_fc(struct cmac *mac, int *speed, int *duplex, - int *fc) -{ - u32 val; - - if (duplex) - *duplex = DUPLEX_FULL; - if (speed) { - t1_tpi_read(mac->adapter, MACREG(mac, REG_RGMII_SPEED), - &val); - *speed = (val & 2) ? SPEED_1000 : SPEED_100; - } - if (fc) { - t1_tpi_read(mac->adapter, MACREG(mac, REG_FC_ENABLE), &val); - *fc = 0; - if (val & 1) - *fc |= PAUSE_RX; - if (val & 2) - *fc |= PAUSE_TX; - } - return 0; -} - -static void enable_port(struct cmac *mac) -{ - u32 val; - u32 index = mac->instance->index; - adapter_t *adapter = mac->adapter; - - t1_tpi_read(adapter, MACREG(mac, REG_DIVERSE_CONFIG), &val); - val |= DIVERSE_CONFIG_CRC_ADD | DIVERSE_CONFIG_PAD_ENABLE; - t1_tpi_write(adapter, MACREG(mac, REG_DIVERSE_CONFIG), val); - if (mac->instance->version > 0) - t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), 3); - else /* Don't enable unicast address filtering due to IXF1010 bug */ - t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), 2); - - t1_tpi_read(adapter, REG_RX_ERR_DROP, &val); - val |= (1 << index); - t1_tpi_write(adapter, REG_RX_ERR_DROP, val); - - /* - * Clear the port RMON registers by adding their current values to the - * cumulatice port stats and then clearing the stats. Really. - */ - port_stats_update(mac); - memset(&mac->stats, 0, sizeof(struct cmac_statistics)); - mac->instance->ticks = 0; - - t1_tpi_read(adapter, REG_PORT_ENABLE, &val); - val |= (1 << index); - t1_tpi_write(adapter, REG_PORT_ENABLE, val); - - index <<= 2; - if (is_T2(adapter)) { - /* T204: set the Fifo water level & threshold */ - t1_tpi_write(adapter, RX_FIFO_HIGH_WATERMARK_BASE + index, 0x740); - t1_tpi_write(adapter, RX_FIFO_LOW_WATERMARK_BASE + index, 0x730); - t1_tpi_write(adapter, TX_FIFO_HIGH_WATERMARK_BASE + index, 0x600); - t1_tpi_write(adapter, TX_FIFO_LOW_WATERMARK_BASE + index, 0x1d0); - t1_tpi_write(adapter, TX_FIFO_XFER_THRES_BASE + index, 0x1100); - } else { - /* - * Set the TX Fifo Threshold to 0x400 instead of 0x100 to work around - * Underrun problem. Intel has blessed this solution. - */ - t1_tpi_write(adapter, TX_FIFO_XFER_THRES_BASE + index, 0x400); - } -} - -/* IXF1010 ports do not have separate enables for TX and RX */ -static int mac_enable(struct cmac *mac, int which) -{ - if (which & (MAC_DIRECTION_RX | MAC_DIRECTION_TX)) - enable_port(mac); - return 0; -} - -static int mac_disable(struct cmac *mac, int which) -{ - if (which & (MAC_DIRECTION_RX | MAC_DIRECTION_TX)) - disable_port(mac); - return 0; -} - -#define RMON_UPDATE(mac, name, stat_name) \ - t1_tpi_read((mac)->adapter, MACREG(mac, REG_##name), &val); \ - (mac)->stats.stat_name += val; - -/* - * This function is called periodically to accumulate the current values of the - * RMON counters into the port statistics. Since the counters are only 32 bits - * some of them can overflow in less than a minute at GigE speeds, so this - * function should be called every 30 seconds or so. - * - * To cut down on reading costs we update only the octet counters at each tick - * and do a full update at major ticks, which can be every 30 minutes or more. - */ -static const struct cmac_statistics *mac_update_statistics(struct cmac *mac, - int flag) -{ - if (flag == MAC_STATS_UPDATE_FULL || - MAJOR_UPDATE_TICKS <= mac->instance->ticks) { - port_stats_update(mac); - mac->instance->ticks = 0; - } else { - u32 val; - - RMON_UPDATE(mac, RxOctetsTotalOK, RxOctetsOK); - RMON_UPDATE(mac, TxOctetsTotalOK, TxOctetsOK); - mac->instance->ticks++; - } - return &mac->stats; -} - -static void mac_destroy(struct cmac *mac) -{ - kfree(mac); -} - -static struct cmac_ops ixf1010_ops = { - .destroy = mac_destroy, - .reset = mac_reset, - .interrupt_enable = mac_intr_op, - .interrupt_disable = mac_intr_op, - .interrupt_clear = mac_intr_op, - .enable = mac_enable, - .disable = mac_disable, - .set_mtu = mac_set_mtu, - .set_rx_mode = mac_set_rx_mode, - .set_speed_duplex_fc = mac_set_speed_duplex_fc, - .get_speed_duplex_fc = mac_get_speed_duplex_fc, - .statistics_update = mac_update_statistics, - .macaddress_get = mac_get_address, - .macaddress_set = mac_set_address, -}; - -static int ixf1010_mac_reset(adapter_t *adapter) -{ - u32 val; - - t1_tpi_read(adapter, A_ELMER0_GPO, &val); - if ((val & 1) != 0) { - val &= ~1; - t1_tpi_write(adapter, A_ELMER0_GPO, val); - udelay(2); - } - val |= 1; - t1_tpi_write(adapter, A_ELMER0_GPO, val); - udelay(2); - - t1_tpi_write(adapter, REG_PORT_ENABLE, 0); - return 0; -} - -static struct cmac *ixf1010_mac_create(adapter_t *adapter, int index) -{ - struct cmac *mac; - u32 val; - - if (index > 9) - return NULL; - - mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL); - if (!mac) - return NULL; - - mac->ops = &ixf1010_ops; - mac->instance = (cmac_instance *)(mac + 1); - - mac->instance->mac_base = MACREG_BASE + (index * 0x200); - mac->instance->index = index; - mac->adapter = adapter; - mac->instance->ticks = 0; - - t1_tpi_read(adapter, REG_JTAG_ID, &val); - mac->instance->version = val >> 28; - return mac; -} - -struct gmac t1_ixf1010_ops = { - STATS_TICK_SECS, - ixf1010_mac_create, - ixf1010_mac_reset -}; diff --git a/drivers/net/chelsio/mac.c b/drivers/net/chelsio/mac.c index 6af39dc..1d97282 100644 --- a/drivers/net/chelsio/mac.c +++ b/drivers/net/chelsio/mac.c @@ -363,6 +363,6 @@ static struct cmac *mac_create(adapter_t return mac; } -struct gmac t1_chelsio_mac_ops = { +const struct gmac t1_chelsio_mac_ops = { .create = mac_create }; diff --git a/drivers/net/chelsio/mv88e1xxx.c b/drivers/net/chelsio/mv88e1xxx.c index 5867e3b..0632be0 100644 --- a/drivers/net/chelsio/mv88e1xxx.c +++ b/drivers/net/chelsio/mv88e1xxx.c @@ -354,7 +354,7 @@ static struct cphy_ops mv88e1xxx_ops = { }; static struct cphy *mv88e1xxx_phy_create(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops) + const struct mdio_ops *mdio_ops) { struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); @@ -390,7 +390,7 @@ static int mv88e1xxx_phy_reset(adapter_t return 0; } -struct gphy t1_mv88e1xxx_ops = { - mv88e1xxx_phy_create, - mv88e1xxx_phy_reset +const struct gphy t1_mv88e1xxx_ops = { + .create = mv88e1xxx_phy_create, + .reset = mv88e1xxx_phy_reset }; diff --git a/drivers/net/chelsio/mv88x201x.c b/drivers/net/chelsio/mv88x201x.c index c8e8948..cd85604 100644 --- a/drivers/net/chelsio/mv88x201x.c +++ b/drivers/net/chelsio/mv88x201x.c @@ -208,7 +208,7 @@ static struct cphy_ops mv88x201x_ops = { }; static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops) + const struct mdio_ops *mdio_ops) { u32 val; struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); @@ -252,7 +252,7 @@ static int mv88x201x_phy_reset(adapter_t return 0; } -struct gphy t1_mv88x201x_ops = { - mv88x201x_phy_create, - mv88x201x_phy_reset +const struct gphy t1_mv88x201x_ops = { + .create = mv88x201x_phy_create, + .reset = mv88x201x_phy_reset }; diff --git a/drivers/net/chelsio/my3126.c b/drivers/net/chelsio/my3126.c index 87dde3e..040acd2 100644 --- a/drivers/net/chelsio/my3126.c +++ b/drivers/net/chelsio/my3126.c @@ -166,7 +166,7 @@ static struct cphy_ops my3126_ops = { }; static struct cphy *my3126_phy_create(adapter_t *adapter, - int phy_addr, struct mdio_ops *mdio_ops) + int phy_addr, const struct mdio_ops *mdio_ops) { struct cphy *cphy = kzalloc(sizeof (*cphy), GFP_KERNEL); @@ -201,7 +201,7 @@ static int my3126_phy_reset(adapter_t * return 0; } -struct gphy t1_my3126_ops = { - my3126_phy_create, - my3126_phy_reset +const struct gphy t1_my3126_ops = { + .create = my3126_phy_create, + .reset = my3126_phy_reset }; diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/chelsio/pm3393.c index 69129ed..678778a 100644 --- a/drivers/net/chelsio/pm3393.c +++ b/drivers/net/chelsio/pm3393.c @@ -807,8 +807,8 @@ static int pm3393_mac_reset(adapter_t * return successful_reset ? 0 : 1; } -struct gmac t1_pm3393_ops = { - STATS_TICK_SECS, - pm3393_mac_create, - pm3393_mac_reset +const struct gmac t1_pm3393_ops = { + .stats_update_period = STATS_TICK_SECS, + .create = pm3393_mac_create, + .reset = pm3393_mac_reset, }; diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c index 326d4a6..e4f874a 100644 --- a/drivers/net/chelsio/sge.c +++ b/drivers/net/chelsio/sge.c @@ -1062,7 +1062,7 @@ static inline struct sk_buff *get_packet pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), PCI_DMA_FROMDEVICE); - memcpy(skb->data, ce->skb->data, len); + skb_copy_from_linear_data(ce->skb, skb->data, len); pci_dma_sync_single_for_device(pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), @@ -1379,12 +1379,11 @@ static void sge_rx(struct sge *sge, stru } __skb_pull(skb, sizeof(*p)); - skb->dev = adapter->port[p->iff].dev; skb->dev->last_rx = jiffies; st = per_cpu_ptr(sge->port_stats[p->iff], smp_processor_id()); st->rx_packets++; - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, adapter->port[p->iff].dev); if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff && skb->protocol == htons(ETH_P_IP) && (skb->data[9] == IPPROTO_TCP || skb->data[9] == IPPROTO_UDP)) { @@ -1866,14 +1865,14 @@ int t1_start_xmit(struct sk_buff *skb, s ++st->tx_tso; - eth_type = skb->nh.raw - skb->data == ETH_HLEN ? + eth_type = skb_network_offset(skb) == ETH_HLEN ? CPL_ETH_II : CPL_ETH_II_VLAN; hdr = (struct cpl_tx_pkt_lso *)skb_push(skb, sizeof(*hdr)); hdr->opcode = CPL_TX_PKT_LSO; hdr->ip_csum_dis = hdr->l4_csum_dis = 0; - hdr->ip_hdr_words = skb->nh.iph->ihl; - hdr->tcp_hdr_words = skb->h.th->doff; + hdr->ip_hdr_words = ip_hdr(skb)->ihl; + hdr->tcp_hdr_words = tcp_hdr(skb)->doff; hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type, skb_shinfo(skb)->gso_size)); hdr->len = htonl(skb->len - sizeof(*hdr)); @@ -1913,7 +1912,7 @@ int t1_start_xmit(struct sk_buff *skb, s if (!(adapter->flags & UDP_CSUM_CAPABLE) && skb->ip_summed == CHECKSUM_PARTIAL && - skb->nh.iph->protocol == IPPROTO_UDP) { + ip_hdr(skb)->protocol == IPPROTO_UDP) { if (unlikely(skb_checksum_help(skb))) { pr_debug("%s: unable to do udp checksum\n", dev->name); dev_kfree_skb_any(skb); @@ -1926,7 +1925,7 @@ int t1_start_xmit(struct sk_buff *skb, s */ if ((unlikely(!adapter->sge->espibug_skb[dev->if_port]))) { if (skb->protocol == htons(ETH_P_ARP) && - skb->nh.arph->ar_op == htons(ARPOP_REQUEST)) { + arp_hdr(skb)->ar_op == htons(ARPOP_REQUEST)) { adapter->sge->espibug_skb[dev->if_port] = skb; /* We want to re-use this skb later. We * simply bump the reference count and it @@ -2096,10 +2095,14 @@ static void espibug_workaround_t204(unsi 0x0, 0x7, 0x43, 0x0, 0x0, 0x0 }; - memcpy(skb->data + sizeof(struct cpl_tx_pkt), - ch_mac_addr, ETH_ALEN); - memcpy(skb->data + skb->len - 10, - ch_mac_addr, ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + sizeof(struct cpl_tx_pkt), + ch_mac_addr, + ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + skb->len - 10, + ch_mac_addr, + ETH_ALEN); skb->cb[0] = 0xff; } @@ -2126,10 +2129,14 @@ static void espibug_workaround(unsigned if (!skb->cb[0]) { u8 ch_mac_addr[ETH_ALEN] = {0x0, 0x7, 0x43, 0x0, 0x0, 0x0}; - memcpy(skb->data + sizeof(struct cpl_tx_pkt), - ch_mac_addr, ETH_ALEN); - memcpy(skb->data + skb->len - 10, ch_mac_addr, - ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + sizeof(struct cpl_tx_pkt), + ch_mac_addr, + ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + skb->len - 10, + ch_mac_addr, + ETH_ALEN); skb->cb[0] = 0xff; } diff --git a/drivers/net/chelsio/subr.c b/drivers/net/chelsio/subr.c index c2522cd..7de9a61 100644 --- a/drivers/net/chelsio/subr.c +++ b/drivers/net/chelsio/subr.c @@ -321,10 +321,10 @@ static int mi1_mdio_write(adapter_t *ada } #if defined(CONFIG_CHELSIO_T1_1G) || defined(CONFIG_CHELSIO_T1_COUGAR) -static struct mdio_ops mi1_mdio_ops = { - mi1_mdio_init, - mi1_mdio_read, - mi1_mdio_write +static const struct mdio_ops mi1_mdio_ops = { + .init = mi1_mdio_init, + .read = mi1_mdio_read, + .write = mi1_mdio_write }; #endif @@ -377,10 +377,10 @@ static int mi1_mdio_ext_write(adapter_t return 0; } -static struct mdio_ops mi1_mdio_ext_ops = { - mi1_mdio_init, - mi1_mdio_ext_read, - mi1_mdio_ext_write +static const struct mdio_ops mi1_mdio_ext_ops = { + .init = mi1_mdio_init, + .read = mi1_mdio_ext_read, + .write = mi1_mdio_ext_write }; enum { @@ -392,63 +392,136 @@ enum { CH_BRD_N204_4CU, }; -static struct board_info t1_board[] = { - -{ CHBT_BOARD_CHT110, 1/*ports#*/, - SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T1, - CHBT_MAC_PM3393, CHBT_PHY_MY3126, - 125000000/*clk-core*/, 150000000/*clk-mc3*/, 125000000/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/, - 1/*mdiinv*/, 1/*mdc*/, 1/*phybaseaddr*/, &t1_pm3393_ops, - &t1_my3126_ops, &mi1_mdio_ext_ops, - "Chelsio T110 1x10GBase-CX4 TOE" }, - -{ CHBT_BOARD_N110, 1/*ports#*/, - SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T1, - CHBT_MAC_PM3393, CHBT_PHY_88X2010, - 125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops, - &t1_mv88x201x_ops, &mi1_mdio_ext_ops, - "Chelsio N110 1x10GBaseX NIC" }, - -{ CHBT_BOARD_N210, 1/*ports#*/, - SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T2, - CHBT_MAC_PM3393, CHBT_PHY_88X2010, - 125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops, - &t1_mv88x201x_ops, &mi1_mdio_ext_ops, - "Chelsio N210 1x10GBaseX NIC" }, - -{ CHBT_BOARD_CHT210, 1/*ports#*/, - SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T2, - CHBT_MAC_PM3393, CHBT_PHY_88X2010, - 125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops, - &t1_mv88x201x_ops, &mi1_mdio_ext_ops, - "Chelsio T210 1x10GBaseX TOE" }, - -{ CHBT_BOARD_CHT210, 1/*ports#*/, - SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T2, - CHBT_MAC_PM3393, CHBT_PHY_MY3126, - 125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/, - 1/*mdiinv*/, 1/*mdc*/, 1/*phybaseaddr*/, &t1_pm3393_ops, - &t1_my3126_ops, &mi1_mdio_ext_ops, - "Chelsio T210 1x10GBase-CX4 TOE" }, +static const struct board_info t1_board[] = { + { + .board = CHBT_BOARD_CHT110, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full, + .chip_term = CHBT_TERM_T1, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_MY3126, + .clock_core = 125000000, + .clock_mc3 = 150000000, + .clock_mc4 = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 1, + .mdio_mdiinv = 1, + .mdio_mdc = 1, + .mdio_phybaseaddr = 1, + .gmac = &t1_pm3393_ops, + .gphy = &t1_my3126_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio T110 1x10GBase-CX4 TOE", + }, + + { + .board = CHBT_BOARD_N110, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE, + .chip_term = CHBT_TERM_T1, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_88X2010, + .clock_core = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 1, + .mdio_phybaseaddr = 0, + .gmac = &t1_pm3393_ops, + .gphy = &t1_mv88x201x_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio N110 1x10GBaseX NIC", + }, + + { + .board = CHBT_BOARD_N210, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_88X2010, + .clock_core = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 1, + .mdio_phybaseaddr = 0, + .gmac = &t1_pm3393_ops, + .gphy = &t1_mv88x201x_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio N210 1x10GBaseX NIC", + }, + + { + .board = CHBT_BOARD_CHT210, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_88X2010, + .clock_core = 125000000, + .clock_mc3 = 133000000, + .clock_mc4 = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 1, + .mdio_phybaseaddr = 0, + .gmac = &t1_pm3393_ops, + .gphy = &t1_mv88x201x_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio T210 1x10GBaseX TOE", + }, + + { + .board = CHBT_BOARD_CHT210, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_MY3126, + .clock_core = 125000000, + .clock_mc3 = 133000000, + .clock_mc4 = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 1, + .mdio_mdiinv = 1, + .mdio_mdc = 1, + .mdio_phybaseaddr = 1, + .gmac = &t1_pm3393_ops, + .gphy = &t1_my3126_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio T210 1x10GBase-CX4 TOE", + }, #ifdef CONFIG_CHELSIO_T1_1G -{ CHBT_BOARD_CHN204, 4/*ports#*/, - SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | - SUPPORTED_PAUSE | SUPPORTED_TP /*caps*/, CHBT_TERM_T2, CHBT_MAC_VSC7321, CHBT_PHY_88E1111, - 100000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/, - 4/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 4/*phybaseaddr*/, &t1_vsc7326_ops, - &t1_mv88e1xxx_ops, &mi1_mdio_ops, - "Chelsio N204 4x100/1000BaseT NIC" }, + { + .board = CHBT_BOARD_CHN204, + .port_number = 4, + .caps = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full + | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | + SUPPORTED_PAUSE | SUPPORTED_TP, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_VSC7321, + .chip_phy = CHBT_PHY_88E1111, + .clock_core = 100000000, + .espi_nports = 4, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 0, + .mdio_phybaseaddr = 4, + .gmac = &t1_vsc7326_ops, + .gphy = &t1_mv88e1xxx_ops, + .mdio_ops = &mi1_mdio_ops, + .desc = "Chelsio N204 4x100/1000BaseT NIC", + }, #endif }; diff --git a/drivers/net/chelsio/vsc7326.c b/drivers/net/chelsio/vsc7326.c index 534ffa0..99b51f6 100644 --- a/drivers/net/chelsio/vsc7326.c +++ b/drivers/net/chelsio/vsc7326.c @@ -723,7 +723,7 @@ static int vsc7326_mac_reset(adapter_t * return 0; } -struct gmac t1_vsc7326_ops = { +const struct gmac t1_vsc7326_ops = { .stats_update_period = STATS_TICK_SECS, .create = vsc7326_mac_create, .reset = vsc7326_mac_reset, diff --git a/drivers/net/chelsio/vsc8244.c b/drivers/net/chelsio/vsc8244.c deleted file mode 100644 index 251d485..0000000 --- a/drivers/net/chelsio/vsc8244.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * This file is part of the Chelsio T2 Ethernet driver. - * - * Copyright (C) 2005 Chelsio Communications. All rights reserved. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this - * release for licensing terms and conditions. - */ - -#include "common.h" -#include "cphy.h" -#include "elmer0.h" - -#ifndef ADVERTISE_PAUSE_CAP -# define ADVERTISE_PAUSE_CAP 0x400 -#endif -#ifndef ADVERTISE_PAUSE_ASYM -# define ADVERTISE_PAUSE_ASYM 0x800 -#endif - -/* Gigabit MII registers */ -#ifndef MII_CTRL1000 -# define MII_CTRL1000 9 -#endif - -#ifndef ADVERTISE_1000FULL -# define ADVERTISE_1000FULL 0x200 -# define ADVERTISE_1000HALF 0x100 -#endif - -/* VSC8244 PHY specific registers. */ -enum { - VSC8244_INTR_ENABLE = 25, - VSC8244_INTR_STATUS = 26, - VSC8244_AUX_CTRL_STAT = 28, -}; - -enum { - VSC_INTR_RX_ERR = 1 << 0, - VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ - VSC_INTR_CABLE = 1 << 2, /* cable impairment */ - VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ - VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ - VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ - VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ - VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ - VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ - VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ - VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ - VSC_INTR_LINK_CHG = 1 << 13, /* link change */ - VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ -}; - -#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ - VSC_INTR_NEG_DONE) -#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ - VSC_INTR_ENABLE) - -/* PHY specific auxiliary control & status register fields */ -#define S_ACSR_ACTIPHY_TMR 0 -#define M_ACSR_ACTIPHY_TMR 0x3 -#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR) - -#define S_ACSR_SPEED 3 -#define M_ACSR_SPEED 0x3 -#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED) - -#define S_ACSR_DUPLEX 5 -#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX) - -#define S_ACSR_ACTIPHY 6 -#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY) - -/* - * Reset the PHY. This PHY completes reset immediately so we never wait. - */ -static int vsc8244_reset(struct cphy *cphy, int wait) -{ - int err; - unsigned int ctl; - - err = simple_mdio_read(cphy, MII_BMCR, &ctl); - if (err) - return err; - - ctl &= ~BMCR_PDOWN; - ctl |= BMCR_RESET; - return simple_mdio_write(cphy, MII_BMCR, ctl); -} - -static int vsc8244_intr_enable(struct cphy *cphy) -{ - simple_mdio_write(cphy, VSC8244_INTR_ENABLE, INTR_MASK); - - /* Enable interrupts through Elmer */ - if (t1_is_asic(cphy->adapter)) { - u32 elmer; - - t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); - elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) - elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); - } - - return 0; -} - -static int vsc8244_intr_disable(struct cphy *cphy) -{ - simple_mdio_write(cphy, VSC8244_INTR_ENABLE, 0); - - if (t1_is_asic(cphy->adapter)) { - u32 elmer; - - t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); - elmer &= ~ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) - elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4); - t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); - } - - return 0; -} - -static int vsc8244_intr_clear(struct cphy *cphy) -{ - u32 val; - u32 elmer; - - /* Clear PHY interrupts by reading the register. */ - simple_mdio_read(cphy, VSC8244_INTR_ENABLE, &val); - - if (t1_is_asic(cphy->adapter)) { - t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); - elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) - elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); - } - - return 0; -} - -/* - * Force the PHY speed and duplex. This also disables auto-negotiation, except - * for 1Gb/s, where auto-negotiation is mandatory. - */ -static int vsc8244_set_speed_duplex(struct cphy *phy, int speed, int duplex) -{ - int err; - unsigned int ctl; - - err = simple_mdio_read(phy, MII_BMCR, &ctl); - if (err) - return err; - - if (speed >= 0) { - ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); - if (speed == SPEED_100) - ctl |= BMCR_SPEED100; - else if (speed == SPEED_1000) - ctl |= BMCR_SPEED1000; - } - if (duplex >= 0) { - ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE); - if (duplex == DUPLEX_FULL) - ctl |= BMCR_FULLDPLX; - } - if (ctl & BMCR_SPEED1000) /* auto-negotiation required for 1Gb/s */ - ctl |= BMCR_ANENABLE; - return simple_mdio_write(phy, MII_BMCR, ctl); -} - -int t1_mdio_set_bits(struct cphy *phy, int mmd, int reg, unsigned int bits) -{ - int ret; - unsigned int val; - - ret = mdio_read(phy, mmd, reg, &val); - if (!ret) - ret = mdio_write(phy, mmd, reg, val | bits); - return ret; -} - -static int vsc8244_autoneg_enable(struct cphy *cphy) -{ - return t1_mdio_set_bits(cphy, 0, MII_BMCR, - BMCR_ANENABLE | BMCR_ANRESTART); -} - -static int vsc8244_autoneg_restart(struct cphy *cphy) -{ - return t1_mdio_set_bits(cphy, 0, MII_BMCR, BMCR_ANRESTART); -} - -static int vsc8244_advertise(struct cphy *phy, unsigned int advertise_map) -{ - int err; - unsigned int val = 0; - - err = simple_mdio_read(phy, MII_CTRL1000, &val); - if (err) - return err; - - val &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); - if (advertise_map & ADVERTISED_1000baseT_Half) - val |= ADVERTISE_1000HALF; - if (advertise_map & ADVERTISED_1000baseT_Full) - val |= ADVERTISE_1000FULL; - - err = simple_mdio_write(phy, MII_CTRL1000, val); - if (err) - return err; - - val = 1; - if (advertise_map & ADVERTISED_10baseT_Half) - val |= ADVERTISE_10HALF; - if (advertise_map & ADVERTISED_10baseT_Full) - val |= ADVERTISE_10FULL; - if (advertise_map & ADVERTISED_100baseT_Half) - val |= ADVERTISE_100HALF; - if (advertise_map & ADVERTISED_100baseT_Full) - val |= ADVERTISE_100FULL; - if (advertise_map & ADVERTISED_PAUSE) - val |= ADVERTISE_PAUSE_CAP; - if (advertise_map & ADVERTISED_ASYM_PAUSE) - val |= ADVERTISE_PAUSE_ASYM; - return simple_mdio_write(phy, MII_ADVERTISE, val); -} - -static int vsc8244_get_link_status(struct cphy *cphy, int *link_ok, - int *speed, int *duplex, int *fc) -{ - unsigned int bmcr, status, lpa, adv; - int err, sp = -1, dplx = -1, pause = 0; - - err = simple_mdio_read(cphy, MII_BMCR, &bmcr); - if (!err) - err = simple_mdio_read(cphy, MII_BMSR, &status); - if (err) - return err; - - if (link_ok) { - /* - * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it - * once more to get the current link state. - */ - if (!(status & BMSR_LSTATUS)) - err = simple_mdio_read(cphy, MII_BMSR, &status); - if (err) - return err; - *link_ok = (status & BMSR_LSTATUS) != 0; - } - if (!(bmcr & BMCR_ANENABLE)) { - dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; - if (bmcr & BMCR_SPEED1000) - sp = SPEED_1000; - else if (bmcr & BMCR_SPEED100) - sp = SPEED_100; - else - sp = SPEED_10; - } else if (status & BMSR_ANEGCOMPLETE) { - err = simple_mdio_read(cphy, VSC8244_AUX_CTRL_STAT, &status); - if (err) - return err; - - dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; - sp = G_ACSR_SPEED(status); - if (sp == 0) - sp = SPEED_10; - else if (sp == 1) - sp = SPEED_100; - else - sp = SPEED_1000; - - if (fc && dplx == DUPLEX_FULL) { - err = simple_mdio_read(cphy, MII_LPA, &lpa); - if (!err) - err = simple_mdio_read(cphy, MII_ADVERTISE, - &adv); - if (err) - return err; - - if (lpa & adv & ADVERTISE_PAUSE_CAP) - pause = PAUSE_RX | PAUSE_TX; - else if ((lpa & ADVERTISE_PAUSE_CAP) && - (lpa & ADVERTISE_PAUSE_ASYM) && - (adv & ADVERTISE_PAUSE_ASYM)) - pause = PAUSE_TX; - else if ((lpa & ADVERTISE_PAUSE_ASYM) && - (adv & ADVERTISE_PAUSE_CAP)) - pause = PAUSE_RX; - } - } - if (speed) - *speed = sp; - if (duplex) - *duplex = dplx; - if (fc) - *fc = pause; - return 0; -} - -static int vsc8244_intr_handler(struct cphy *cphy) -{ - unsigned int cause; - int err, cphy_cause = 0; - - err = simple_mdio_read(cphy, VSC8244_INTR_STATUS, &cause); - if (err) - return err; - - cause &= INTR_MASK; - if (cause & CFG_CHG_INTR_MASK) - cphy_cause |= cphy_cause_link_change; - if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) - cphy_cause |= cphy_cause_fifo_error; - return cphy_cause; -} - -static void vsc8244_destroy(struct cphy *cphy) -{ - kfree(cphy); -} - -static struct cphy_ops vsc8244_ops = { - .destroy = vsc8244_destroy, - .reset = vsc8244_reset, - .interrupt_enable = vsc8244_intr_enable, - .interrupt_disable = vsc8244_intr_disable, - .interrupt_clear = vsc8244_intr_clear, - .interrupt_handler = vsc8244_intr_handler, - .autoneg_enable = vsc8244_autoneg_enable, - .autoneg_restart = vsc8244_autoneg_restart, - .advertise = vsc8244_advertise, - .set_speed_duplex = vsc8244_set_speed_duplex, - .get_link_status = vsc8244_get_link_status -}; - -static struct cphy* vsc8244_phy_create(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops) -{ - struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); - - if (!cphy) - return NULL; - - cphy_init(cphy, adapter, phy_addr, &vsc8244_ops, mdio_ops); - - return cphy; -} - - -static int vsc8244_phy_reset(adapter_t* adapter) -{ - return 0; -} - -struct gphy t1_vsc8244_ops = { - vsc8244_phy_create, - vsc8244_phy_reset -}; - - diff --git a/drivers/net/chelsio/vsc8244_reg.h b/drivers/net/chelsio/vsc8244_reg.h deleted file mode 100644 index d3c1829..0000000 --- a/drivers/net/chelsio/vsc8244_reg.h +++ /dev/null @@ -1,172 +0,0 @@ -/* $Date: 2005/11/23 16:28:53 $ $RCSfile: vsc8244_reg.h,v $ $Revision: 1.1 $ */ -#ifndef CHELSIO_MV8E1XXX_H -#define CHELSIO_MV8E1XXX_H - -#ifndef BMCR_SPEED1000 -# define BMCR_SPEED1000 0x40 -#endif - -#ifndef ADVERTISE_PAUSE -# define ADVERTISE_PAUSE 0x400 -#endif -#ifndef ADVERTISE_PAUSE_ASYM -# define ADVERTISE_PAUSE_ASYM 0x800 -#endif - -/* Gigabit MII registers */ -#define MII_GBMR 1 /* 1000Base-T mode register */ -#define MII_GBCR 9 /* 1000Base-T control register */ -#define MII_GBSR 10 /* 1000Base-T status register */ - -/* 1000Base-T control register fields */ -#define GBCR_ADV_1000HALF 0x100 -#define GBCR_ADV_1000FULL 0x200 -#define GBCR_PREFER_MASTER 0x400 -#define GBCR_MANUAL_AS_MASTER 0x800 -#define GBCR_MANUAL_CONFIG_ENABLE 0x1000 - -/* 1000Base-T status register fields */ -#define GBSR_LP_1000HALF 0x400 -#define GBSR_LP_1000FULL 0x800 -#define GBSR_REMOTE_OK 0x1000 -#define GBSR_LOCAL_OK 0x2000 -#define GBSR_LOCAL_MASTER 0x4000 -#define GBSR_MASTER_FAULT 0x8000 - -/* Vitesse PHY interrupt status bits. */ -#if 0 -#define VSC8244_INTR_JABBER 0x0001 -#define VSC8244_INTR_POLARITY_CHNG 0x0002 -#define VSC8244_INTR_ENG_DETECT_CHNG 0x0010 -#define VSC8244_INTR_DOWNSHIFT 0x0020 -#define VSC8244_INTR_MDI_XOVER_CHNG 0x0040 -#define VSC8244_INTR_FIFO_OVER_UNDER 0x0080 -#define VSC8244_INTR_FALSE_CARRIER 0x0100 -#define VSC8244_INTR_SYMBOL_ERROR 0x0200 -#define VSC8244_INTR_LINK_CHNG 0x0400 -#define VSC8244_INTR_AUTONEG_DONE 0x0800 -#define VSC8244_INTR_PAGE_RECV 0x1000 -#define VSC8244_INTR_DUPLEX_CHNG 0x2000 -#define VSC8244_INTR_SPEED_CHNG 0x4000 -#define VSC8244_INTR_AUTONEG_ERR 0x8000 -#else -//#define VSC8244_INTR_JABBER 0x0001 -//#define VSC8244_INTR_POLARITY_CHNG 0x0002 -//#define VSC8244_INTR_BIT2 0x0004 -//#define VSC8244_INTR_BIT3 0x0008 -#define VSC8244_INTR_RX_ERR 0x0001 -#define VSC8244_INTR_MASTER_SLAVE 0x0002 -#define VSC8244_INTR_CABLE_IMPAIRED 0x0004 -#define VSC8244_INTR_FALSE_CARRIER 0x0008 -//#define VSC8244_INTR_ENG_DETECT_CHNG 0x0010 -//#define VSC8244_INTR_DOWNSHIFT 0x0020 -//#define VSC8244_INTR_MDI_XOVER_CHNG 0x0040 -//#define VSC8244_INTR_FIFO_OVER_UNDER 0x0080 -#define VSC8244_INTR_BIT4 0x0010 -#define VSC8244_INTR_FIFO_RX 0x0020 -#define VSC8244_INTR_FIFO_OVER_UNDER 0x0040 -#define VSC8244_INTR_LOCK_LOST 0x0080 -//#define VSC8244_INTR_FALSE_CARRIER 0x0100 -//#define VSC8244_INTR_SYMBOL_ERROR 0x0200 -//#define VSC8244_INTR_LINK_CHNG 0x0400 -//#define VSC8244_INTR_AUTONEG_DONE 0x0800 -#define VSC8244_INTR_SYMBOL_ERROR 0x0100 -#define VSC8244_INTR_ENG_DETECT_CHNG 0x0200 -#define VSC8244_INTR_AUTONEG_DONE 0x0400 -#define VSC8244_INTR_AUTONEG_ERR 0x0800 -//#define VSC8244_INTR_PAGE_RECV 0x1000 -//#define VSC8244_INTR_DUPLEX_CHNG 0x2000 -//#define VSC8244_INTR_SPEED_CHNG 0x4000 -//#define VSC8244_INTR_AUTONEG_ERR 0x8000 -#define VSC8244_INTR_DUPLEX_CHNG 0x1000 -#define VSC8244_INTR_LINK_CHNG 0x2000 -#define VSC8244_INTR_SPEED_CHNG 0x4000 -#define VSC8244_INTR_STATUS 0x8000 -#endif - - -/* Vitesse PHY specific registers. */ -#define VSC8244_SPECIFIC_CNTRL_REGISTER 16 -#define VSC8244_SPECIFIC_STATUS_REGISTER 0x1c -#define VSC8244_INTERRUPT_ENABLE_REGISTER 0x19 -#define VSC8244_INTERRUPT_STATUS_REGISTER 0x1a -#define VSC8244_EXT_PHY_SPECIFIC_CNTRL_REGISTER 20 -#define VSC8244_RECV_ERR_CNTR_REGISTER 21 -#define VSC8244_RES_REGISTER 22 -#define VSC8244_GLOBAL_STATUS_REGISTER 23 -#define VSC8244_LED_CONTROL_REGISTER 24 -#define VSC8244_MANUAL_LED_OVERRIDE_REGISTER 25 -#define VSC8244_EXT_PHY_SPECIFIC_CNTRL_2_REGISTER 26 -#define VSC8244_EXT_PHY_SPECIFIC_STATUS_REGISTER 27 -#define VSC8244_VIRTUAL_CABLE_TESTER_REGISTER 28 -#define VSC8244_EXTENDED_ADDR_REGISTER 29 -#define VSC8244_EXTENDED_REGISTER 30 - -/* PHY specific control register fields */ -#define S_PSCR_MDI_XOVER_MODE 5 -#define M_PSCR_MDI_XOVER_MODE 0x3 -#define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE) -#define G_PSCR_MDI_XOVER_MODE(x) (((x) >> S_PSCR_MDI_XOVER_MODE) & M_PSCR_MDI_XOVER_MODE) - -/* Extended PHY specific control register fields */ -#define S_DOWNSHIFT_ENABLE 8 -#define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE) - -#define S_DOWNSHIFT_CNT 9 -#define M_DOWNSHIFT_CNT 0x7 -#define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT) -#define G_DOWNSHIFT_CNT(x) (((x) >> S_DOWNSHIFT_CNT) & M_DOWNSHIFT_CNT) - -/* PHY specific status register fields */ -#define S_PSSR_JABBER 0 -#define V_PSSR_JABBER (1 << S_PSSR_JABBER) - -#define S_PSSR_POLARITY 1 -#define V_PSSR_POLARITY (1 << S_PSSR_POLARITY) - -#define S_PSSR_RX_PAUSE 2 -#define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE) - -#define S_PSSR_TX_PAUSE 3 -#define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE) - -#define S_PSSR_ENERGY_DETECT 4 -#define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT) - -#define S_PSSR_DOWNSHIFT_STATUS 5 -#define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS) - -#define S_PSSR_MDI 6 -#define V_PSSR_MDI (1 << S_PSSR_MDI) - -#define S_PSSR_CABLE_LEN 7 -#define M_PSSR_CABLE_LEN 0x7 -#define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN) -#define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN) - -//#define S_PSSR_LINK 10 -//#define S_PSSR_LINK 13 -#define S_PSSR_LINK 2 -#define V_PSSR_LINK (1 << S_PSSR_LINK) - -//#define S_PSSR_STATUS_RESOLVED 11 -//#define S_PSSR_STATUS_RESOLVED 10 -#define S_PSSR_STATUS_RESOLVED 15 -#define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED) - -#define S_PSSR_PAGE_RECEIVED 12 -#define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED) - -//#define S_PSSR_DUPLEX 13 -//#define S_PSSR_DUPLEX 12 -#define S_PSSR_DUPLEX 5 -#define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX) - -//#define S_PSSR_SPEED 14 -//#define S_PSSR_SPEED 14 -#define S_PSSR_SPEED 3 -#define M_PSSR_SPEED 0x3 -#define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED) -#define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED) - -#endif diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index 8eb5712..5bdf5ca 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -1348,7 +1348,8 @@ #endif #ifdef ETHDEBUG printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n", - skb->head, skb->data, skb->tail, skb->end); + skb->head, skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb)); printk("copying packet to 0x%x.\n", skb_data_ptr); #endif @@ -1375,7 +1376,6 @@ #endif myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data)); } - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); /* Send the packet to the upper layers */ diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 4612f71..9774bb1 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -1004,7 +1004,6 @@ skip_this_frame: return; } skb_reserve(skb, 2); /* longword align L3 header */ - skb->dev = dev; if (bp + length > lp->end_dma_buff) { int semi_cnt = lp->end_dma_buff - bp; @@ -1702,7 +1701,6 @@ #endif return; } skb_reserve(skb, 2); /* longword align L3 header */ - skb->dev = dev; readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1); if (length & 1) diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index 199e506..ebcf35e 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -783,7 +783,7 @@ static int do_trace(struct t3cdev *dev, skb->protocol = htons(0xffff); skb->dev = dev->lldev; skb_pull(skb, sizeof(*p)); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_receive_skb(skb); return 0; } diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 027ab2c..3666586 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -661,7 +661,7 @@ static inline struct sk_buff *get_imm_pa if (skb) { __skb_put(skb, IMMED_PKT_SIZE); - memcpy(skb->data, resp->imm_data, IMMED_PKT_SIZE); + skb_copy_to_linear_data(skb, resp->imm_data, IMMED_PKT_SIZE); } return skb; } @@ -897,11 +897,11 @@ static void write_tx_pkt_wr(struct adapt d->flit[2] = 0; cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT_LSO); hdr->cntrl = htonl(cntrl); - eth_type = skb->nh.raw - skb->data == ETH_HLEN ? + eth_type = skb_network_offset(skb) == ETH_HLEN ? CPL_ETH_II : CPL_ETH_II_VLAN; tso_info |= V_LSO_ETH_TYPE(eth_type) | - V_LSO_IPHDR_WORDS(skb->nh.iph->ihl) | - V_LSO_TCPHDR_WORDS(skb->h.th->doff); + V_LSO_IPHDR_WORDS(ip_hdr(skb)->ihl) | + V_LSO_TCPHDR_WORDS(tcp_hdr(skb)->doff); hdr->lso_info = htonl(tso_info); flits = 3; } else { @@ -913,7 +913,8 @@ static void write_tx_pkt_wr(struct adapt if (skb->len <= WR_LEN - sizeof(*cpl)) { q->sdesc[pidx].skb = NULL; if (!skb->data_len) - memcpy(&d->flit[2], skb->data, skb->len); + skb_copy_from_linear_data(skb, &d->flit[2], + skb->len); else skb_copy_bits(skb, 0, &d->flit[2], skb->len); @@ -1319,16 +1320,19 @@ static void write_ofld_wr(struct adapter /* Only TX_DATA builds SGLs */ from = (struct work_request_hdr *)skb->data; - memcpy(&d->flit[1], &from[1], skb->h.raw - skb->data - sizeof(*from)); + memcpy(&d->flit[1], &from[1], + skb_transport_offset(skb) - sizeof(*from)); - flits = (skb->h.raw - skb->data) / 8; + flits = skb_transport_offset(skb) / 8; sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = make_sgl(skb, sgp, skb->h.raw, skb->tail - skb->h.raw, + sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb), + skb->tail - skb->transport_header, adap->pdev); if (need_skb_unmap()) { setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits); skb->destructor = deferred_unmap_destructor; - ((struct unmap_info *)skb->cb)->len = skb->tail - skb->h.raw; + ((struct unmap_info *)skb->cb)->len = (skb->tail - + skb->transport_header); } write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, @@ -1349,8 +1353,8 @@ static inline unsigned int calc_tx_descs if (skb->len <= WR_LEN && cnt == 0) return 1; /* packet fits as immediate data */ - flits = (skb->h.raw - skb->data) / 8; /* headers */ - if (skb->tail != skb->h.raw) + flits = skb_transport_offset(skb) / 8; /* headers */ + if (skb->tail != skb->transport_header) cnt++; return flits_to_desc(flits + sgl_len(cnt)); } @@ -1620,7 +1624,9 @@ static inline int rx_offload(struct t3cd unsigned int gather_idx) { rq->offload_pkts++; - skb->mac.raw = skb->nh.raw = skb->h.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); if (rq->polling) { rx_gather[gather_idx++] = skb; @@ -1684,9 +1690,8 @@ static void rx_eth(struct adapter *adap, struct port_info *pi; skb_pull(skb, sizeof(*p) + pad); - skb->dev = adap->port[p->iff]; skb->dev->last_rx = jiffies; - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, adap->port[p->iff]); pi = netdev_priv(skb->dev); if (pi->rx_csum_offload && p->csum_valid && p->csum == 0xffff && !p->fragment) { @@ -1717,11 +1722,11 @@ static void skb_data_init(struct sk_buff { skb->len = len; if (len <= SKB_DATA_SIZE) { - memcpy(skb->data, p->va, len); + skb_copy_to_linear_data(skb, p->va, len); skb->tail += len; put_page(p->frag.page); } else { - memcpy(skb->data, p->va, SKB_DATA_SIZE); + skb_copy_to_linear_data(skb, p->va, SKB_DATA_SIZE); skb_shinfo(skb)->frags[0].page = p->frag.page; skb_shinfo(skb)->frags[0].page_offset = p->frag.page_offset + SKB_DATA_SIZE; @@ -1767,7 +1772,7 @@ static struct sk_buff *get_packet(struct __skb_put(skb, len); pci_dma_sync_single_for_cpu(adap->pdev, mapping, len, PCI_DMA_FROMDEVICE); - memcpy(skb->data, sd->t.skb->data, len); + skb_copy_from_linear_data(sd->t.skb, skb->data, len); pci_dma_sync_single_for_device(adap->pdev, mapping, len, PCI_DMA_FROMDEVICE); } else if (!drop_thres) diff --git a/drivers/net/de600.c b/drivers/net/de600.c index e547ce1..dae97b8 100644 --- a/drivers/net/de600.c +++ b/drivers/net/de600.c @@ -359,7 +359,6 @@ static void de600_rx_intr(struct net_dev } /* else */ - skb->dev = dev; skb_reserve(skb,2); /* Align */ /* 'skb->data' points to the start of sk_buff data area. */ diff --git a/drivers/net/de620.c b/drivers/net/de620.c index b6ad0cb..dc48924 100644 --- a/drivers/net/de620.c +++ b/drivers/net/de620.c @@ -697,7 +697,6 @@ static int de620_rx_intr(struct net_devi } else { /* Yep! Go get it! */ skb_reserve(skb,2); /* Align */ - skb->dev = dev; /* skb->data points to the start of sk_buff data area */ buffer = skb_put(skb,size); /* copy the packet into the buffer */ diff --git a/drivers/net/declance.c b/drivers/net/declance.c index 9f7e1db..95d854e 100644 --- a/drivers/net/declance.c +++ b/drivers/net/declance.c @@ -616,7 +616,6 @@ #endif } lp->stats.rx_bytes += len; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c index 07d2731..571d82f 100644 --- a/drivers/net/defxx.c +++ b/drivers/net/defxx.c @@ -3091,13 +3091,13 @@ #endif { /* Receive buffer allocated, pass receive packet up */ - memcpy(skb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3); + skb_copy_to_linear_data(skb, + p_buff + RCV_BUFF_K_PADDING, + pkt_len + 3); } skb_reserve(skb,3); /* adjust data field so that it points to FC byte */ skb_put(skb, pkt_len); /* pass up packet length, NOT including CRC */ - skb->dev = bp->dev; /* pass up device pointer */ - skb->protocol = fddi_type_trans(skb, bp->dev); bp->rcv_total_bytes += skb->len; netif_rx(skb); diff --git a/drivers/net/depca.c b/drivers/net/depca.c index f3807aa..1834970 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -1044,7 +1044,6 @@ static int depca_rx(struct net_device *d unsigned char *buf; skb_reserve(skb, 2); /* 16 byte align the IP header */ buf = skb_put(skb, pkt_len); - skb->dev = dev; if (entry < lp->rx_old) { /* Wrapped buffer */ len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ; memcpy_fromio(buf, lp->rx_buff[lp->rx_old], len); diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c index a795202..df62c02 100644 --- a/drivers/net/dgrs.c +++ b/drivers/net/dgrs.c @@ -503,7 +503,6 @@ dgrs_rcv_frame( /* discarding the frame */ goto out; } - skb->dev = devN; skb_reserve(skb, 2); /* Align IP header */ again: @@ -742,7 +741,7 @@ static int dgrs_start_xmit(struct sk_buf } amt = min_t(unsigned int, len, rbdp->size - count); - memcpy( (char *) S2H(rbdp->buf) + count, skb->data + i, amt); + skb_copy_from_linear_data_offset(skb, i, S2H(rbdp->buf) + count, amt); i += amt; count += amt; len -= amt; diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 9d446a0..74ec64a 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -504,7 +504,6 @@ rio_timer (unsigned long data) break; } np->rx_skbuff[entry] = skb; - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve (skb, 2); np->rx_ring[entry].fraginfo = @@ -575,7 +574,6 @@ alloc_list (struct net_device *dev) dev->name); break; } - skb->dev = dev; /* Mark as being used by this device. */ skb_reserve (skb, 2); /* 16 byte align the IP header. */ /* Rubicon now supports 40 bits of addressing space. */ np->rx_ring[i].fraginfo = @@ -866,7 +864,6 @@ receive_packet (struct net_device *dev) DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve (skb, 2); eth_copy_and_sum (skb, @@ -910,7 +907,6 @@ #endif break; } np->rx_skbuff[entry] = skb; - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve (skb, 2); np->rx_ring[entry].fraginfo = diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 615d2b1..8cc1174 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -954,7 +954,6 @@ dm9000_rx(struct net_device *dev) /* Move data from DM9000 */ if (GoodPacket && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { - skb->dev = dev; skb_reserve(skb, 2); rdptr = (u8 *) skb_put(skb, RxLen - 4); diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 0cefef5..6169663 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -159,7 +159,7 @@ #include #define DRV_NAME "e100" #define DRV_EXT "-NAPI" -#define DRV_VERSION "3.5.17-k2"DRV_EXT +#define DRV_VERSION "3.5.17-k4"DRV_EXT #define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 1999-2006 Intel Corporation" #define PFX DRV_NAME ": " @@ -174,10 +174,13 @@ MODULE_VERSION(DRV_VERSION); static int debug = 3; static int eeprom_bad_csum_allow = 0; +static int use_io = 0; module_param(debug, int, 0); module_param(eeprom_bad_csum_allow, int, 0); +module_param(use_io, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); MODULE_PARM_DESC(eeprom_bad_csum_allow, "Allow bad eeprom checksums"); +MODULE_PARM_DESC(use_io, "Force use of i/o access mode"); #define DPRINTK(nlevel, klevel, fmt, args...) \ (void)((NETIF_MSG_##nlevel & nic->msg_enable) && \ printk(KERN_##klevel PFX "%s: %s: " fmt, nic->netdev->name, \ @@ -282,12 +285,6 @@ enum scb_status { rus_mask = 0x3C, }; -enum ru_state { - RU_SUSPENDED = 0, - RU_RUNNING = 1, - RU_UNINITIALIZED = -1, -}; - enum scb_stat_ack { stat_ack_not_ours = 0x00, stat_ack_sw_gen = 0x04, @@ -529,7 +526,6 @@ struct nic { struct rx *rx_to_use; struct rx *rx_to_clean; struct rfd blank_rfd; - enum ru_state ru_running; spinlock_t cb_lock ____cacheline_aligned; spinlock_t cmd_lock; @@ -591,7 +587,7 @@ static inline void e100_write_flush(stru { /* Flush previous PCI writes through intermediate bridges * by doing a benign read */ - (void)readb(&nic->csr->scb.status); + (void)ioread8(&nic->csr->scb.status); } static void e100_enable_irq(struct nic *nic) @@ -599,7 +595,7 @@ static void e100_enable_irq(struct nic * unsigned long flags; spin_lock_irqsave(&nic->cmd_lock, flags); - writeb(irq_mask_none, &nic->csr->scb.cmd_hi); + iowrite8(irq_mask_none, &nic->csr->scb.cmd_hi); e100_write_flush(nic); spin_unlock_irqrestore(&nic->cmd_lock, flags); } @@ -609,7 +605,7 @@ static void e100_disable_irq(struct nic unsigned long flags; spin_lock_irqsave(&nic->cmd_lock, flags); - writeb(irq_mask_all, &nic->csr->scb.cmd_hi); + iowrite8(irq_mask_all, &nic->csr->scb.cmd_hi); e100_write_flush(nic); spin_unlock_irqrestore(&nic->cmd_lock, flags); } @@ -618,11 +614,11 @@ static void e100_hw_reset(struct nic *ni { /* Put CU and RU into idle with a selective reset to get * device off of PCI bus */ - writel(selective_reset, &nic->csr->port); + iowrite32(selective_reset, &nic->csr->port); e100_write_flush(nic); udelay(20); /* Now fully reset device */ - writel(software_reset, &nic->csr->port); + iowrite32(software_reset, &nic->csr->port); e100_write_flush(nic); udelay(20); /* Mask off our interrupt line - it's unmasked after reset */ @@ -639,7 +635,7 @@ static int e100_self_test(struct nic *ni nic->mem->selftest.signature = 0; nic->mem->selftest.result = 0xFFFFFFFF; - writel(selftest | dma_addr, &nic->csr->port); + iowrite32(selftest | dma_addr, &nic->csr->port); e100_write_flush(nic); /* Wait 10 msec for self-test to complete */ msleep(10); @@ -677,23 +673,23 @@ static void e100_eeprom_write(struct nic for(j = 0; j < 3; j++) { /* Chip select */ - writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); for(i = 31; i >= 0; i--) { ctrl = (cmd_addr_data[j] & (1 << i)) ? eecs | eedi : eecs; - writeb(ctrl, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); - writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); } /* Wait 10 msec for cmd to complete */ msleep(10); /* Chip deselect */ - writeb(0, &nic->csr->eeprom_ctrl_lo); + iowrite8(0, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); } }; @@ -709,21 +705,21 @@ static u16 e100_eeprom_read(struct nic * cmd_addr_data = ((op_read << *addr_len) | addr) << 16; /* Chip select */ - writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); /* Bit-bang to read word from eeprom */ for(i = 31; i >= 0; i--) { ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs; - writeb(ctrl, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); - writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); /* Eeprom drives a dummy zero to EEDO after receiving * complete address. Use this to adjust addr_len. */ - ctrl = readb(&nic->csr->eeprom_ctrl_lo); + ctrl = ioread8(&nic->csr->eeprom_ctrl_lo); if(!(ctrl & eedo) && i > 16) { *addr_len -= (i - 16); i = 17; @@ -733,7 +729,7 @@ static u16 e100_eeprom_read(struct nic * } /* Chip deselect */ - writeb(0, &nic->csr->eeprom_ctrl_lo); + iowrite8(0, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); return le16_to_cpu(data); @@ -804,7 +800,7 @@ static int e100_exec_cmd(struct nic *nic /* Previous command is accepted when SCB clears */ for(i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) { - if(likely(!readb(&nic->csr->scb.cmd_lo))) + if(likely(!ioread8(&nic->csr->scb.cmd_lo))) break; cpu_relax(); if(unlikely(i > E100_WAIT_SCB_FAST)) @@ -816,8 +812,8 @@ static int e100_exec_cmd(struct nic *nic } if(unlikely(cmd != cuc_resume)) - writel(dma_addr, &nic->csr->scb.gen_ptr); - writeb(cmd, &nic->csr->scb.cmd_lo); + iowrite32(dma_addr, &nic->csr->scb.gen_ptr); + iowrite8(cmd, &nic->csr->scb.cmd_lo); err_unlock: spin_unlock_irqrestore(&nic->cmd_lock, flags); @@ -895,7 +891,7 @@ static u16 mdio_ctrl(struct nic *nic, u3 */ spin_lock_irqsave(&nic->mdio_lock, flags); for (i = 100; i; --i) { - if (readl(&nic->csr->mdi_ctrl) & mdi_ready) + if (ioread32(&nic->csr->mdi_ctrl) & mdi_ready) break; udelay(20); } @@ -905,11 +901,11 @@ static u16 mdio_ctrl(struct nic *nic, u3 spin_unlock_irqrestore(&nic->mdio_lock, flags); return 0; /* No way to indicate timeout error */ } - writel((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl); + iowrite32((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl); for (i = 0; i < 100; i++) { udelay(20); - if ((data_out = readl(&nic->csr->mdi_ctrl)) & mdi_ready) + if ((data_out = ioread32(&nic->csr->mdi_ctrl)) & mdi_ready) break; } spin_unlock_irqrestore(&nic->mdio_lock, flags); @@ -951,7 +947,7 @@ static void e100_get_defaults(struct nic ((nic->mac >= mac_82558_D101_A4) ? cb_cid : cb_i)); /* Template for a freshly allocated RFD */ - nic->blank_rfd.command = cpu_to_le16(cb_el); + nic->blank_rfd.command = cpu_to_le16(cb_el & cb_s); nic->blank_rfd.rbd = 0xFFFFFFFF; nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN); @@ -1318,7 +1314,7 @@ static inline int e100_exec_cb_wait(stru } /* ack any interupts, something could have been set */ - writeb(~0, &nic->csr->scb.stat_ack); + iowrite8(~0, &nic->csr->scb.stat_ack); /* if the command failed, or is not OK, notify and return */ if (!counter || !(cb->status & cpu_to_le16(cb_ok))) { @@ -1580,7 +1576,7 @@ static void e100_watchdog(unsigned long * accidentally, due to hardware that shares a register between the * interrupt mask bit and the SW Interrupt generation bit */ spin_lock_irq(&nic->cmd_lock); - writeb(readb(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi); + iowrite8(ioread8(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi); e100_write_flush(nic); spin_unlock_irq(&nic->cmd_lock); @@ -1746,19 +1742,11 @@ static int e100_alloc_cbs(struct nic *ni return 0; } -static inline void e100_start_receiver(struct nic *nic, struct rx *rx) +static inline void e100_start_receiver(struct nic *nic) { - if(!nic->rxs) return; - if(RU_SUSPENDED != nic->ru_running) return; - - /* handle init time starts */ - if(!rx) rx = nic->rxs; - - /* (Re)start RU if suspended or idle and RFA is non-NULL */ - if(rx->skb) { - e100_exec_cmd(nic, ruc_start, rx->dma_addr); - nic->ru_running = RU_RUNNING; - } + /* Start if RFA is non-NULL */ + if(nic->rx_to_clean->skb) + e100_exec_cmd(nic, ruc_start, nic->rx_to_clean->dma_addr); } #define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN) @@ -1769,7 +1757,7 @@ static int e100_rx_alloc_skb(struct nic /* Align, init, and map the RFD. */ skb_reserve(rx->skb, NET_IP_ALIGN); - memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd)); + skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd)); rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); @@ -1787,7 +1775,7 @@ static int e100_rx_alloc_skb(struct nic put_unaligned(cpu_to_le32(rx->dma_addr), (u32 *)&prev_rfd->link); wmb(); - prev_rfd->command &= ~cpu_to_le16(cb_el); + prev_rfd->command &= ~cpu_to_le16(cb_el & cb_s); pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, sizeof(struct rfd), PCI_DMA_TODEVICE); } @@ -1825,10 +1813,6 @@ static int e100_rx_indicate(struct nic * pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_FROMDEVICE); - /* this allows for a fast restart without re-enabling interrupts */ - if(le16_to_cpu(rfd->command) & cb_el) - nic->ru_running = RU_SUSPENDED; - /* Pull off the RFD and put the actual data (minus eth hdr) */ skb_reserve(skb, sizeof(struct rfd)); skb_put(skb, actual_size); @@ -1859,45 +1843,18 @@ static void e100_rx_clean(struct nic *ni unsigned int work_to_do) { struct rx *rx; - int restart_required = 0; - struct rx *rx_to_start = NULL; - - /* are we already rnr? then pay attention!!! this ensures that - * the state machine progression never allows a start with a - * partially cleaned list, avoiding a race between hardware - * and rx_to_clean when in NAPI mode */ - if(RU_SUSPENDED == nic->ru_running) - restart_required = 1; /* Indicate newly arrived packets */ for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { - int err = e100_rx_indicate(nic, rx, work_done, work_to_do); - if(-EAGAIN == err) { - /* hit quota so have more work to do, restart once - * cleanup is complete */ - restart_required = 0; - break; - } else if(-ENODATA == err) + if(e100_rx_indicate(nic, rx, work_done, work_to_do)) break; /* No more to clean */ } - /* save our starting point as the place we'll restart the receiver */ - if(restart_required) - rx_to_start = nic->rx_to_clean; - /* Alloc new skbs to refill list */ for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { if(unlikely(e100_rx_alloc_skb(nic, rx))) break; /* Better luck next time (see watchdog) */ } - - if(restart_required) { - // ack the rnr? - writeb(stat_ack_rnr, &nic->csr->scb.stat_ack); - e100_start_receiver(nic, rx_to_start); - if(work_done) - (*work_done)++; - } } static void e100_rx_clean_list(struct nic *nic) @@ -1905,8 +1862,6 @@ static void e100_rx_clean_list(struct ni struct rx *rx; unsigned int i, count = nic->params.rfds.count; - nic->ru_running = RU_UNINITIALIZED; - if(nic->rxs) { for(rx = nic->rxs, i = 0; i < count; rx++, i++) { if(rx->skb) { @@ -1928,7 +1883,6 @@ static int e100_rx_alloc_list(struct nic unsigned int i, count = nic->params.rfds.count; nic->rx_to_use = nic->rx_to_clean = NULL; - nic->ru_running = RU_UNINITIALIZED; if(!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_ATOMIC))) return -ENOMEM; @@ -1943,7 +1897,6 @@ static int e100_rx_alloc_list(struct nic } nic->rx_to_use = nic->rx_to_clean = nic->rxs; - nic->ru_running = RU_SUSPENDED; return 0; } @@ -1952,7 +1905,7 @@ static irqreturn_t e100_intr(int irq, vo { struct net_device *netdev = dev_id; struct nic *nic = netdev_priv(netdev); - u8 stat_ack = readb(&nic->csr->scb.stat_ack); + u8 stat_ack = ioread8(&nic->csr->scb.stat_ack); DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack); @@ -1961,11 +1914,7 @@ static irqreturn_t e100_intr(int irq, vo return IRQ_NONE; /* Ack interrupt(s) */ - writeb(stat_ack, &nic->csr->scb.stat_ack); - - /* We hit Receive No Resource (RNR); restart RU after cleaning */ - if(stat_ack & stat_ack_rnr) - nic->ru_running = RU_SUSPENDED; + iowrite8(stat_ack, &nic->csr->scb.stat_ack); if(likely(netif_rx_schedule_prep(netdev))) { e100_disable_irq(nic); @@ -2058,7 +2007,7 @@ static int e100_up(struct nic *nic) if((err = e100_hw_init(nic))) goto err_clean_cbs; e100_set_multicast_list(nic->netdev); - e100_start_receiver(nic, NULL); + e100_start_receiver(nic); mod_timer(&nic->watchdog, jiffies); if((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED, nic->netdev->name, nic->netdev))) @@ -2107,7 +2056,7 @@ static void e100_tx_timeout_task(struct struct net_device *netdev = nic->netdev; DPRINTK(TX_ERR, DEBUG, "scb.status=0x%02X\n", - readb(&nic->csr->scb.status)); + ioread8(&nic->csr->scb.status)); e100_down(netdev_priv(netdev)); e100_up(netdev_priv(netdev)); } @@ -2139,7 +2088,7 @@ static int e100_loopback_test(struct nic mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, BMCR_LOOPBACK); - e100_start_receiver(nic, NULL); + e100_start_receiver(nic); if(!(skb = netdev_alloc_skb(nic->netdev, ETH_DATA_LEN))) { err = -ENOMEM; @@ -2230,9 +2179,9 @@ static void e100_get_regs(struct net_dev int i; regs->version = (1 << 24) | nic->rev_id; - buff[0] = readb(&nic->csr->scb.cmd_hi) << 24 | - readb(&nic->csr->scb.cmd_lo) << 16 | - readw(&nic->csr->scb.status); + buff[0] = ioread8(&nic->csr->scb.cmd_hi) << 24 | + ioread8(&nic->csr->scb.cmd_lo) << 16 | + ioread16(&nic->csr->scb.status); for(i = E100_PHY_REGS; i >= 0; i--) buff[1 + E100_PHY_REGS - i] = mdio_read(netdev, nic->mii.phy_id, i); @@ -2604,7 +2553,10 @@ #endif SET_MODULE_OWNER(netdev); SET_NETDEV_DEV(netdev, &pdev->dev); - nic->csr = ioremap(pci_resource_start(pdev, 0), sizeof(struct csr)); + if (use_io) + DPRINTK(PROBE, INFO, "using i/o access mode\n"); + + nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr)); if(!nic->csr) { DPRINTK(PROBE, ERR, "Cannot map device registers, aborting.\n"); err = -ENOMEM; @@ -2651,11 +2603,16 @@ #endif memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN); memcpy(netdev->perm_addr, nic->eeprom, ETH_ALEN); - if(!is_valid_ether_addr(netdev->perm_addr)) { - DPRINTK(PROBE, ERR, "Invalid MAC address from " - "EEPROM, aborting.\n"); - err = -EAGAIN; - goto err_out_free; + if (!is_valid_ether_addr(netdev->perm_addr)) { + if (!eeprom_bad_csum_allow) { + DPRINTK(PROBE, ERR, "Invalid MAC address from " + "EEPROM, aborting.\n"); + err = -EAGAIN; + goto err_out_free; + } else { + DPRINTK(PROBE, ERR, "Invalid MAC address from EEPROM, " + "you MUST configure one.\n"); + } } /* Wol magic packet can be enabled from eeprom */ @@ -2676,7 +2633,7 @@ #endif DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, " "MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n", - (unsigned long long)pci_resource_start(pdev, 0), pdev->irq, + (unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0), pdev->irq, netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); @@ -2685,7 +2642,7 @@ #endif err_out_free: e100_free(nic); err_out_iounmap: - iounmap(nic->csr); + pci_iounmap(pdev, nic->csr); err_out_free_res: pci_release_regions(pdev); err_out_disable_pdev: diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index dd4b728..a9ea67e 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -155,9 +155,6 @@ #define E1000_MNG_VLAN_NONE -1 /* Number of packet split data buffers (not including the header buffer) */ #define PS_PAGE_BUFFERS MAX_PS_BUFFERS-1 -/* only works for sizes that are powers of 2 */ -#define E1000_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1))) - /* wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct e1000_buffer { diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 6777887..bb08375 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -654,14 +654,11 @@ e1000_set_ringparam(struct net_device *n e1000_mac_type mac_type = adapter->hw.mac_type; struct e1000_tx_ring *txdr, *tx_old; struct e1000_rx_ring *rxdr, *rx_old; - int i, err, tx_ring_size, rx_ring_size; + int i, err; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) return -EINVAL; - tx_ring_size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues; - rx_ring_size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues; - while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) msleep(1); @@ -672,11 +669,11 @@ e1000_set_ringparam(struct net_device *n rx_old = adapter->rx_ring; err = -ENOMEM; - txdr = kzalloc(tx_ring_size, GFP_KERNEL); + txdr = kcalloc(adapter->num_tx_queues, sizeof(struct e1000_tx_ring), GFP_KERNEL); if (!txdr) goto err_alloc_tx; - rxdr = kzalloc(rx_ring_size, GFP_KERNEL); + rxdr = kcalloc(adapter->num_rx_queues, sizeof(struct e1000_rx_ring), GFP_KERNEL); if (!rxdr) goto err_alloc_rx; @@ -686,12 +683,12 @@ e1000_set_ringparam(struct net_device *n rxdr->count = max(ring->rx_pending,(uint32_t)E1000_MIN_RXD); rxdr->count = min(rxdr->count,(uint32_t)(mac_type < e1000_82544 ? E1000_MAX_RXD : E1000_MAX_82544_RXD)); - E1000_ROUNDUP(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE); + rxdr->count = ALIGN(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE); txdr->count = max(ring->tx_pending,(uint32_t)E1000_MIN_TXD); txdr->count = min(txdr->count,(uint32_t)(mac_type < e1000_82544 ? E1000_MAX_TXD : E1000_MAX_82544_TXD)); - E1000_ROUNDUP(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE); + txdr->count = ALIGN(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE); for (i = 0; i < adapter->num_tx_queues; i++) txdr[i].count = txdr->count; @@ -742,7 +739,7 @@ #define REG_PATTERN_TEST(R, M, W) uint32_t pat, value; \ uint32_t test[] = \ {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF}; \ - for (pat = 0; pat < sizeof(test)/sizeof(test[0]); pat++) { \ + for (pat = 0; pat < ARRAY_SIZE(test); pat++) { \ E1000_WRITE_REG(&adapter->hw, R, (test[pat] & W)); \ value = E1000_READ_REG(&adapter->hw, R); \ if (value != (test[pat] & W & M)) { \ @@ -1053,23 +1050,24 @@ e1000_setup_desc_rings(struct e1000_adap struct e1000_rx_ring *rxdr = &adapter->test_rx_ring; struct pci_dev *pdev = adapter->pdev; uint32_t rctl; - int size, i, ret_val; + int i, ret_val; /* Setup Tx descriptor ring and Tx buffers */ if (!txdr->count) txdr->count = E1000_DEFAULT_TXD; - size = txdr->count * sizeof(struct e1000_buffer); - if (!(txdr->buffer_info = kmalloc(size, GFP_KERNEL))) { + if (!(txdr->buffer_info = kcalloc(txdr->count, + sizeof(struct e1000_buffer), + GFP_KERNEL))) { ret_val = 1; goto err_nomem; } - memset(txdr->buffer_info, 0, size); txdr->size = txdr->count * sizeof(struct e1000_tx_desc); - E1000_ROUNDUP(txdr->size, 4096); - if (!(txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma))) { + txdr->size = ALIGN(txdr->size, 4096); + if (!(txdr->desc = pci_alloc_consistent(pdev, txdr->size, + &txdr->dma))) { ret_val = 2; goto err_nomem; } @@ -1116,12 +1114,12 @@ e1000_setup_desc_rings(struct e1000_adap if (!rxdr->count) rxdr->count = E1000_DEFAULT_RXD; - size = rxdr->count * sizeof(struct e1000_buffer); - if (!(rxdr->buffer_info = kmalloc(size, GFP_KERNEL))) { + if (!(rxdr->buffer_info = kcalloc(rxdr->count, + sizeof(struct e1000_buffer), + GFP_KERNEL))) { ret_val = 4; goto err_nomem; } - memset(rxdr->buffer_info, 0, size); rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc); if (!(rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma))) { diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index b28a915..3a03a74 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -409,25 +409,21 @@ e1000_release_hw_control(struct e1000_ad { uint32_t ctrl_ext; uint32_t swsm; - uint32_t extcnf; /* Let firmware taken over control of h/w */ switch (adapter->hw.mac_type) { - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); - E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); - break; case e1000_82573: swsm = E1000_READ_REG(&adapter->hw, SWSM); E1000_WRITE_REG(&adapter->hw, SWSM, swsm & ~E1000_SWSM_DRV_LOAD); + break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: case e1000_ich8lan: - extcnf = E1000_READ_REG(&adapter->hw, CTRL_EXT); + ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - extcnf & ~E1000_CTRL_EXT_DRV_LOAD); + ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); break; default: break; @@ -450,26 +446,21 @@ e1000_get_hw_control(struct e1000_adapte { uint32_t ctrl_ext; uint32_t swsm; - uint32_t extcnf; /* Let firmware know the driver has taken over */ switch (adapter->hw.mac_type) { - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); - E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); - break; case e1000_82573: swsm = E1000_READ_REG(&adapter->hw, SWSM); E1000_WRITE_REG(&adapter->hw, SWSM, swsm | E1000_SWSM_DRV_LOAD); break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: case e1000_ich8lan: - extcnf = E1000_READ_REG(&adapter->hw, EXTCNF_CTRL); - E1000_WRITE_REG(&adapter->hw, EXTCNF_CTRL, - extcnf | E1000_EXTCNF_CTRL_SWFLAG); + ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); + E1000_WRITE_REG(&adapter->hw, CTRL_EXT, + ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); break; default: break; @@ -522,14 +513,15 @@ e1000_release_manageability(struct e1000 } } -int -e1000_up(struct e1000_adapter *adapter) +/** + * e1000_configure - configure the hardware for RX and TX + * @adapter = private board structure + **/ +static void e1000_configure(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; int i; - /* hardware has been reset, we need to reload some things */ - e1000_set_multi(netdev); e1000_restore_vlan(adapter); @@ -548,14 +540,20 @@ e1000_up(struct e1000_adapter *adapter) } adapter->tx_queue_len = netdev->tx_queue_len; +} + +int e1000_up(struct e1000_adapter *adapter) +{ + /* hardware has been reset, we need to reload some things */ + e1000_configure(adapter); + + clear_bit(__E1000_DOWN, &adapter->flags); #ifdef CONFIG_E1000_NAPI - netif_poll_enable(netdev); + netif_poll_enable(adapter->netdev); #endif e1000_irq_enable(adapter); - clear_bit(__E1000_DOWN, &adapter->flags); - /* fire a link change interrupt to start the watchdog */ E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC); return 0; @@ -640,15 +638,15 @@ e1000_down(struct e1000_adapter *adapter * reschedule our watchdog timer */ set_bit(__E1000_DOWN, &adapter->flags); +#ifdef CONFIG_E1000_NAPI + netif_poll_disable(netdev); +#endif e1000_irq_disable(adapter); del_timer_sync(&adapter->tx_fifo_stall_timer); del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_info_timer); -#ifdef CONFIG_E1000_NAPI - netif_poll_disable(netdev); -#endif netdev->tx_queue_len = adapter->tx_queue_len; adapter->link_speed = 0; adapter->link_duplex = 0; @@ -750,9 +748,9 @@ e1000_reset(struct e1000_adapter *adapte VLAN_TAG_SIZE; min_tx_space = min_rx_space; min_tx_space *= 2; - E1000_ROUNDUP(min_tx_space, 1024); + min_tx_space = ALIGN(min_tx_space, 1024); min_tx_space >>= 10; - E1000_ROUNDUP(min_rx_space, 1024); + min_rx_space = ALIGN(min_rx_space, 1024); min_rx_space >>= 10; /* If current Tx allocation is less than the min Tx FIFO size, @@ -1356,31 +1354,27 @@ #endif static int __devinit e1000_alloc_queues(struct e1000_adapter *adapter) { - int size; - - size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues; - adapter->tx_ring = kmalloc(size, GFP_KERNEL); + adapter->tx_ring = kcalloc(adapter->num_tx_queues, + sizeof(struct e1000_tx_ring), GFP_KERNEL); if (!adapter->tx_ring) return -ENOMEM; - memset(adapter->tx_ring, 0, size); - size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues; - adapter->rx_ring = kmalloc(size, GFP_KERNEL); + adapter->rx_ring = kcalloc(adapter->num_rx_queues, + sizeof(struct e1000_rx_ring), GFP_KERNEL); if (!adapter->rx_ring) { kfree(adapter->tx_ring); return -ENOMEM; } - memset(adapter->rx_ring, 0, size); #ifdef CONFIG_E1000_NAPI - size = sizeof(struct net_device) * adapter->num_rx_queues; - adapter->polling_netdev = kmalloc(size, GFP_KERNEL); + adapter->polling_netdev = kcalloc(adapter->num_rx_queues, + sizeof(struct net_device), + GFP_KERNEL); if (!adapter->polling_netdev) { kfree(adapter->tx_ring); kfree(adapter->rx_ring); return -ENOMEM; } - memset(adapter->polling_netdev, 0, size); #endif return E1000_SUCCESS; @@ -1410,21 +1404,17 @@ e1000_open(struct net_device *netdev) return -EBUSY; /* allocate transmit descriptors */ - if ((err = e1000_setup_all_tx_resources(adapter))) + err = e1000_setup_all_tx_resources(adapter); + if (err) goto err_setup_tx; /* allocate receive descriptors */ - if ((err = e1000_setup_all_rx_resources(adapter))) - goto err_setup_rx; - - err = e1000_request_irq(adapter); + err = e1000_setup_all_rx_resources(adapter); if (err) - goto err_req_irq; + goto err_setup_rx; e1000_power_up_phy(adapter); - if ((err = e1000_up(adapter))) - goto err_up; adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; if ((adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) { @@ -1437,12 +1427,33 @@ e1000_open(struct net_device *netdev) e1000_check_mng_mode(&adapter->hw)) e1000_get_hw_control(adapter); + /* before we allocate an interrupt, we must be ready to handle it. + * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt + * as soon as we call pci_request_irq, so we have to setup our + * clean_rx handler before we do so. */ + e1000_configure(adapter); + + err = e1000_request_irq(adapter); + if (err) + goto err_req_irq; + + /* From here on the code is the same as e1000_up() */ + clear_bit(__E1000_DOWN, &adapter->flags); + +#ifdef CONFIG_E1000_NAPI + netif_poll_enable(netdev); +#endif + + e1000_irq_enable(adapter); + + /* fire a link status change interrupt to start the watchdog */ + E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC); + return E1000_SUCCESS; -err_up: - e1000_power_down_phy(adapter); - e1000_free_irq(adapter); err_req_irq: + e1000_release_hw_control(adapter); + e1000_power_down_phy(adapter); e1000_free_all_rx_resources(adapter); err_setup_rx: e1000_free_all_tx_resources(adapter); @@ -1545,7 +1556,7 @@ e1000_setup_tx_resources(struct e1000_ad /* round up to nearest 4K */ txdr->size = txdr->count * sizeof(struct e1000_tx_desc); - E1000_ROUNDUP(txdr->size, 4096); + txdr->size = ALIGN(txdr->size, 4096); txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma); if (!txdr->desc) { @@ -1759,18 +1770,18 @@ e1000_setup_rx_resources(struct e1000_ad } memset(rxdr->buffer_info, 0, size); - size = sizeof(struct e1000_ps_page) * rxdr->count; - rxdr->ps_page = kmalloc(size, GFP_KERNEL); + rxdr->ps_page = kcalloc(rxdr->count, sizeof(struct e1000_ps_page), + GFP_KERNEL); if (!rxdr->ps_page) { vfree(rxdr->buffer_info); DPRINTK(PROBE, ERR, "Unable to allocate memory for the receive descriptor ring\n"); return -ENOMEM; } - memset(rxdr->ps_page, 0, size); - size = sizeof(struct e1000_ps_page_dma) * rxdr->count; - rxdr->ps_page_dma = kmalloc(size, GFP_KERNEL); + rxdr->ps_page_dma = kcalloc(rxdr->count, + sizeof(struct e1000_ps_page_dma), + GFP_KERNEL); if (!rxdr->ps_page_dma) { vfree(rxdr->buffer_info); kfree(rxdr->ps_page); @@ -1778,7 +1789,6 @@ e1000_setup_rx_resources(struct e1000_ad "Unable to allocate memory for the receive descriptor ring\n"); return -ENOMEM; } - memset(rxdr->ps_page_dma, 0, size); if (adapter->hw.mac_type <= e1000_82547_rev_2) desc_len = sizeof(struct e1000_rx_desc); @@ -1788,7 +1798,7 @@ e1000_setup_rx_resources(struct e1000_ad /* Round up to nearest 4K */ rxdr->size = rxdr->count * desc_len; - E1000_ROUNDUP(rxdr->size, 4096); + rxdr->size = ALIGN(rxdr->size, 4096); rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma); @@ -2652,7 +2662,7 @@ e1000_watchdog(unsigned long data) netif_carrier_on(netdev); netif_wake_queue(netdev); - mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ); + mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); adapter->smartspeed = 0; } else { /* make sure the receive unit is started */ @@ -2669,7 +2679,7 @@ e1000_watchdog(unsigned long data) DPRINTK(LINK, INFO, "NIC Link is Down\n"); netif_carrier_off(netdev); netif_stop_queue(netdev); - mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ); + mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); /* 80003ES2LAN workaround-- * For packet buffer work-around on link down event; @@ -2721,7 +2731,7 @@ e1000_watchdog(unsigned long data) e1000_rar_set(&adapter->hw, adapter->hw.mac_addr, 0); /* Reset the timer */ - mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); + mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 2 * HZ)); } enum latency_range { @@ -2887,33 +2897,30 @@ e1000_tso(struct e1000_adapter *adapter, return err; } - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); mss = skb_shinfo(skb)->gso_size; if (skb->protocol == htons(ETH_P_IP)) { - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, - IPPROTO_TCP, - 0); + struct iphdr *iph = ip_hdr(skb); + iph->tot_len = 0; + iph->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, + 0); cmd_length = E1000_TXD_CMD_IP; - ipcse = skb->h.raw - skb->data - 1; + ipcse = skb_transport_offset(skb) - 1; } else if (skb->protocol == htons(ETH_P_IPV6)) { - skb->nh.ipv6h->payload_len = 0; - skb->h.th->check = - ~csum_ipv6_magic(&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, - 0, - IPPROTO_TCP, - 0); + ipv6_hdr(skb)->payload_len = 0; + tcp_hdr(skb)->check = + ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); ipcse = 0; } - ipcss = skb->nh.raw - skb->data; - ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data; - tucss = skb->h.raw - skb->data; - tucso = (void *)&(skb->h.th->check) - (void *)skb->data; + ipcss = skb_network_offset(skb); + ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data; + tucss = skb_transport_offset(skb); + tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data; tucse = 0; cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE | @@ -2954,7 +2961,7 @@ e1000_tx_csum(struct e1000_adapter *adap uint8_t css; if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { - css = skb->h.raw - skb->data; + css = skb_transport_offset(skb); i = tx_ring->next_to_use; buffer_info = &tx_ring->buffer_info[i]; @@ -2962,7 +2969,8 @@ e1000_tx_csum(struct e1000_adapter *adap context_desc->lower_setup.ip_config = 0; context_desc->upper_setup.tcp_fields.tucss = css; - context_desc->upper_setup.tcp_fields.tucso = css + skb->csum; + context_desc->upper_setup.tcp_fields.tucso = + css + skb->csum_offset; context_desc->upper_setup.tcp_fields.tucse = 0; context_desc->tcp_seg_setup.data = 0; context_desc->cmd_and_length = cpu_to_le32(E1000_TXD_CMD_DEXT); @@ -3162,7 +3170,7 @@ e1000_82547_fifo_workaround(struct e1000 uint32_t fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head; uint32_t skb_fifo_len = skb->len + E1000_FIFO_HDR; - E1000_ROUNDUP(skb_fifo_len, E1000_FIFO_HDR); + skb_fifo_len = ALIGN(skb_fifo_len, E1000_FIFO_HDR); if (adapter->link_duplex != HALF_DUPLEX) goto no_fifo_stall_required; @@ -3296,7 +3304,7 @@ e1000_xmit_frame(struct sk_buff *skb, st /* TSO Workaround for 82571/2/3 Controllers -- if skb->data * points to just header, pull a few bytes of payload from * frags into skb->data */ - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (skb->data_len && (hdr_len == (skb->len - skb->data_len))) { switch (adapter->hw.mac_type) { unsigned int pull_size; @@ -3307,7 +3315,7 @@ e1000_xmit_frame(struct sk_buff *skb, st * NOTE: this is a TSO only workaround * if end byte alignment not correct move us * into the next dword */ - if ((unsigned long)(skb->tail - 1) & 4) + if ((unsigned long)(skb_tail_pointer(skb) - 1) & 4) break; /* fall through */ case e1000_82571: @@ -3363,12 +3371,9 @@ e1000_xmit_frame(struct sk_buff *skb, st (adapter->hw.mac_type == e1000_82573)) e1000_transfer_dhcp_info(adapter, skb); - local_irq_save(flags); - if (!spin_trylock(&tx_ring->tx_lock)) { + if (!spin_trylock_irqsave(&tx_ring->tx_lock, flags)) /* Collision - tell upper layer to requeue */ - local_irq_restore(flags); return NETDEV_TX_LOCKED; - } /* need: count + 2 desc gap to keep tail from touching * head, otherwise try next time */ @@ -4227,9 +4232,12 @@ #endif netdev_alloc_skb(netdev, length + NET_IP_ALIGN); if (new_skb) { skb_reserve(new_skb, NET_IP_ALIGN); - memcpy(new_skb->data - NET_IP_ALIGN, - skb->data - NET_IP_ALIGN, - length + NET_IP_ALIGN); + skb_copy_to_linear_data_offset(new_skb, + -NET_IP_ALIGN, + (skb->data - + NET_IP_ALIGN), + (length + + NET_IP_ALIGN)); /* save the skb in buffer_info as good */ buffer_info->skb = skb; skb = new_skb; @@ -4391,7 +4399,7 @@ #endif PCI_DMA_FROMDEVICE); vaddr = kmap_atomic(ps_page->ps_page[0], KM_SKB_DATA_SOFTIRQ); - memcpy(skb->tail, vaddr, l1); + memcpy(skb_tail_pointer(skb), vaddr, l1); kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ); pci_dma_sync_single_for_device(pdev, ps_page_dma->ps_page_dma[0], diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c index f8862e2..f485874 100644 --- a/drivers/net/e1000/e1000_param.c +++ b/drivers/net/e1000/e1000_param.c @@ -305,7 +305,7 @@ e1000_check_options(struct e1000_adapter if (num_TxDescriptors > bd) { tx_ring->count = TxDescriptors[bd]; e1000_validate_option(&tx_ring->count, &opt, adapter); - E1000_ROUNDUP(tx_ring->count, + tx_ring->count = ALIGN(tx_ring->count, REQ_TX_DESCRIPTOR_MULTIPLE); } else { tx_ring->count = opt.def; @@ -331,7 +331,7 @@ e1000_check_options(struct e1000_adapter if (num_RxDescriptors > bd) { rx_ring->count = RxDescriptors[bd]; e1000_validate_option(&rx_ring->count, &opt, adapter); - E1000_ROUNDUP(rx_ring->count, + rx_ring->count = ALIGN(rx_ring->count, REQ_RX_DESCRIPTOR_MULTIPLE); } else { rx_ring->count = opt.def; diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index b446309..39654e1 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -1591,7 +1591,6 @@ eepro_rx(struct net_device *dev) break; } - skb->dev = dev; skb_reserve(skb,2); if (lp->version == LAN595) diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index e28bb1e..6c267c3 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -1793,7 +1793,6 @@ speedo_rx(struct net_device *dev) copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry], @@ -1805,8 +1804,9 @@ #if 1 || USE_IP_CSUM eth_copy_and_sum(skb, sp->rx_skbuff[entry]->data, pkt_len, 0); skb_put(skb, pkt_len); #else - memcpy(skb_put(skb, pkt_len), sp->rx_skbuff[entry]->data, - pkt_len); + skb_copy_from_linear_data(sp->rx_skbuff[entry], + skb_put(skb, pkt_len), + pkt_len); #endif pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry], sizeof(struct RxFD) + pkt_len, diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 3868b80..7934ea3 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -115,6 +115,7 @@ #include #include #include #include +#include #include #include @@ -556,7 +557,7 @@ static void unstick_cu(struct net_device if (lp->started) { - if ((jiffies - dev->trans_start)>50) + if (time_after(jiffies, dev->trans_start + 50)) { if (lp->tx_link==lp->last_tx_restart) { @@ -612,7 +613,7 @@ static void unstick_cu(struct net_device } else { - if ((jiffies-lp->init_time)>10) + if (time_after(jiffies, lp->init_time + 10)) { unsigned short status = scb_status(dev); printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n", @@ -776,7 +777,7 @@ #endif static void eexp_cmd_clear(struct net_device *dev) { unsigned long int oldtime = jiffies; - while (scb_rdcmd(dev) && ((jiffies-oldtime)<10)); + while (scb_rdcmd(dev) && (time_before(jiffies, oldtime + 10))); if (scb_rdcmd(dev)) { printk("%s: command didn't clear\n", dev->name); } @@ -976,7 +977,6 @@ #endif lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb, 2); outw(pbuf+10, ioaddr+READ_PTR); insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1); @@ -1650,7 +1650,7 @@ #if 0 #endif oj = jiffies; while ((SCB_CUstat(scb_status(dev)) == 2) && - ((jiffies-oj) < 2000)); + (time_before(jiffies, oj + 2000))); if (SCB_CUstat(scb_status(dev)) == 2) printk("%s: warning, CU didn't stop\n", dev->name); lp->started &= ~(STARTED_CU); diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h index 42295d6..602872d 100644 --- a/drivers/net/ehea/ehea.h +++ b/drivers/net/ehea/ehea.h @@ -39,7 +39,7 @@ #include #include #define DRV_NAME "ehea" -#define DRV_VERSION "EHEA_0046" +#define DRV_VERSION "EHEA_0058" #define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \ | NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) @@ -78,10 +78,7 @@ #define EHEA_MAX_PACKET_SIZE 9022 /* #define EHEA_RQ2_PKT_SIZE 1522 #define EHEA_L_PKT_SIZE 256 /* low latency */ -#define EHEA_POLL_MAX_RWQE 1000 - /* Send completion signaling */ -#define EHEA_SIG_IV_LONG 1 /* Protection Domain Identifier */ #define EHEA_PD_ID 0xaabcdeff @@ -108,11 +105,7 @@ #define EHEA_BCMC_VLANID_SINGLE 0x00 #define EHEA_CACHE_LINE 128 /* Memory Regions */ -#define EHEA_MR_MAX_TX_PAGES 20 -#define EHEA_MR_TX_DATA_PN 3 #define EHEA_MR_ACC_CTRL 0x00800000 -#define EHEA_RWQES_PER_MR_RQ2 10 -#define EHEA_RWQES_PER_MR_RQ3 10 #define EHEA_WATCH_DOG_TIMEOUT 10*HZ @@ -311,6 +304,7 @@ struct ehea_cq { * Memory Region */ struct ehea_mr { + struct ehea_adapter *adapter; u64 handle; u64 vaddr; u32 lkey; @@ -319,17 +313,12 @@ struct ehea_mr { /* * Port state information */ -struct port_state { - int poll_max_processed; +struct port_stats { int poll_receive_errors; - int ehea_poll; int queue_stopped; - int min_swqe_avail; - u64 sqc_stop_sum; - int pkt_send; - int pkt_xmit; - int send_tasklet; - int nwqe; + int err_tcp_cksum; + int err_ip_cksum; + int err_frame_crc; }; #define EHEA_IRQ_NAME_SIZE 20 @@ -348,6 +337,7 @@ struct ehea_q_skb_arr { * Port resources */ struct ehea_port_res { + struct port_stats p_stats; struct ehea_mr send_mr; /* send memory region */ struct ehea_mr recv_mr; /* receive memory region */ spinlock_t xmit_lock; @@ -357,9 +347,8 @@ struct ehea_port_res { struct ehea_qp *qp; struct ehea_cq *send_cq; struct ehea_cq *recv_cq; - struct ehea_eq *send_eq; - struct ehea_eq *recv_eq; - spinlock_t send_lock; + struct ehea_eq *eq; + struct net_device *d_netdev; struct ehea_q_skb_arr rq1_skba; struct ehea_q_skb_arr rq2_skba; struct ehea_q_skb_arr rq3_skba; @@ -369,21 +358,18 @@ struct ehea_port_res { int swqe_refill_th; atomic_t swqe_avail; int swqe_ll_count; - int swqe_count; u32 swqe_id_counter; u64 tx_packets; - struct tasklet_struct send_comp_task; - spinlock_t recv_lock; - struct port_state p_state; u64 rx_packets; u32 poll_counter; }; +#define EHEA_MAX_PORTS 16 struct ehea_adapter { u64 handle; - u8 num_ports; - struct ehea_port *port[16]; + struct ibmebus_dev *ebus_dev; + struct ehea_port *port[EHEA_MAX_PORTS]; struct ehea_eq *neq; /* notification event queue */ struct workqueue_struct *ehea_wq; struct tasklet_struct neq_tasklet; @@ -406,7 +392,7 @@ struct ehea_port { struct net_device *netdev; struct net_device_stats stats; struct ehea_port_res port_res[EHEA_MAX_PORT_RES]; - struct device_node *of_dev_node; /* Open Firmware Device Node */ + struct of_device ofdev; /* Open Firmware Device */ struct ehea_mc_list *mc_list; /* Multicast MAC addresses */ struct vlan_group *vgrp; struct ehea_eq *qp_eq; @@ -415,7 +401,9 @@ struct ehea_port { char int_aff_name[EHEA_IRQ_NAME_SIZE]; int allmulti; /* Indicates IFF_ALLMULTI state */ int promisc; /* Indicates IFF_PROMISC state */ + int num_tx_qps; int num_add_tx_qps; + int num_mcs; int resets; u64 mac_addr; u32 logical_port_id; diff --git a/drivers/net/ehea/ehea_ethtool.c b/drivers/net/ehea/ehea_ethtool.c index 9f57c2e..decec8c 100644 --- a/drivers/net/ehea/ehea_ethtool.c +++ b/drivers/net/ehea/ehea_ethtool.c @@ -144,8 +144,8 @@ static int ehea_nway_reset(struct net_de static void ehea_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); - strlcpy(info->version, DRV_VERSION, sizeof(info->version) - 1); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } static u32 ehea_get_msglevel(struct net_device *dev) @@ -166,33 +166,23 @@ static u32 ehea_get_rx_csum(struct net_d } static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = { - {"poll_max_processed"}, - {"queue_stopped"}, - {"min_swqe_avail"}, - {"poll_receive_err"}, - {"pkt_send"}, - {"pkt_xmit"}, - {"send_tasklet"}, - {"ehea_poll"}, - {"nwqe"}, - {"swqe_available_0"}, {"sig_comp_iv"}, {"swqe_refill_th"}, {"port resets"}, - {"rxo"}, - {"rx64"}, - {"rx65"}, - {"rx128"}, - {"rx256"}, - {"rx512"}, - {"rx1024"}, - {"txo"}, - {"tx64"}, - {"tx65"}, - {"tx128"}, - {"tx256"}, - {"tx512"}, - {"tx1024"}, + {"Receive errors"}, + {"TCP cksum errors"}, + {"IP cksum errors"}, + {"Frame cksum errors"}, + {"num SQ stopped"}, + {"SQ stopped"}, + {"PR0 free_swqes"}, + {"PR1 free_swqes"}, + {"PR2 free_swqes"}, + {"PR3 free_swqes"}, + {"PR4 free_swqes"}, + {"PR5 free_swqes"}, + {"PR6 free_swqes"}, + {"PR7 free_swqes"}, }; static void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data) @@ -211,63 +201,44 @@ static int ehea_get_stats_count(struct n static void ehea_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { - u64 hret; - int i; + int i, k, tmp; struct ehea_port *port = netdev_priv(dev); - struct ehea_adapter *adapter = port->adapter; - struct ehea_port_res *pr = &port->port_res[0]; - struct port_state *p_state = &pr->p_state; - struct hcp_ehea_port_cb6 *cb6; for (i = 0; i < ehea_get_stats_count(dev); i++) data[i] = 0; - i = 0; - data[i++] = p_state->poll_max_processed; - data[i++] = p_state->queue_stopped; - data[i++] = p_state->min_swqe_avail; - data[i++] = p_state->poll_receive_errors; - data[i++] = p_state->pkt_send; - data[i++] = p_state->pkt_xmit; - data[i++] = p_state->send_tasklet; - data[i++] = p_state->ehea_poll; - data[i++] = p_state->nwqe; - data[i++] = atomic_read(&port->port_res[0].swqe_avail); data[i++] = port->sig_comp_iv; data[i++] = port->port_res[0].swqe_refill_th; data[i++] = port->resets; - cb6 = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!cb6) { - ehea_error("no mem for cb6"); - return; - } + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.poll_receive_errors; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.err_tcp_cksum; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.err_ip_cksum; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.err_frame_crc; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.queue_stopped; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp |= port->port_res[k].queue_stopped; + data[i++] = tmp; + + for (k = 0; k < 8; k++) + data[i++] = atomic_read(&port->port_res[k].swqe_avail); - hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id, - H_PORT_CB6, H_PORT_CB6_ALL, cb6); - if (netif_msg_hw(port)) - ehea_dump(cb6, sizeof(*cb6), "ehea_get_ethtool_stats"); - - if (hret == H_SUCCESS) { - data[i++] = cb6->rxo; - data[i++] = cb6->rx64; - data[i++] = cb6->rx65; - data[i++] = cb6->rx128; - data[i++] = cb6->rx256; - data[i++] = cb6->rx512; - data[i++] = cb6->rx1024; - data[i++] = cb6->txo; - data[i++] = cb6->tx64; - data[i++] = cb6->tx65; - data[i++] = cb6->tx128; - data[i++] = cb6->tx256; - data[i++] = cb6->tx512; - data[i++] = cb6->tx1024; - } else - ehea_error("query_ehea_port failed"); - - kfree(cb6); } const struct ethtool_ops ehea_ethtool_ops = { diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 0e4042b..c7a5614 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -51,13 +51,18 @@ static int rq1_entries = EHEA_DEF_ENTRIE static int rq2_entries = EHEA_DEF_ENTRIES_RQ2; static int rq3_entries = EHEA_DEF_ENTRIES_RQ3; static int sq_entries = EHEA_DEF_ENTRIES_SQ; +static int use_mcs = 0; +static int num_tx_qps = EHEA_NUM_TX_QP; module_param(msg_level, int, 0); module_param(rq1_entries, int, 0); module_param(rq2_entries, int, 0); module_param(rq3_entries, int, 0); module_param(sq_entries, int, 0); +module_param(use_mcs, int, 0); +module_param(num_tx_qps, int, 0); +MODULE_PARM_DESC(num_tx_qps, "Number of TX-QPS"); MODULE_PARM_DESC(msg_level, "msg_level"); MODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 " "[2^x - 1], x = [6..14]. Default = " @@ -71,6 +76,29 @@ MODULE_PARM_DESC(rq1_entries, "Number of MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue " "[2^x - 1], x = [6..14]. Default = " __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")"); +MODULE_PARM_DESC(use_mcs, " 0:NAPI, 1:Multiple receive queues, Default = 1 "); + +static int port_name_cnt = 0; + +static int __devinit ehea_probe_adapter(struct ibmebus_dev *dev, + const struct of_device_id *id); + +static int __devexit ehea_remove(struct ibmebus_dev *dev); + +static struct of_device_id ehea_device_table[] = { + { + .name = "lhea", + .compatible = "IBM,lhea", + }, + {}, +}; + +static struct ibmebus_driver ehea_driver = { + .name = "ehea", + .id_table = ehea_device_table, + .probe = ehea_probe_adapter, + .remove = ehea_remove, +}; void ehea_dump(void *adr, int len, char *msg) { int x; @@ -197,7 +225,7 @@ static int ehea_refill_rq_def(struct ehe struct sk_buff *skb = netdev_alloc_skb(dev, packet_size); if (!skb) { ehea_error("%s: no mem for skb/%d wqes filled", - dev->name, i); + pr->port->netdev->name, i); q_skba->os_skbs = fill_wqes - i; ret = -ENOMEM; break; @@ -321,6 +349,13 @@ static int ehea_treat_poll_error(struct { struct sk_buff *skb; + if (cqe->status & EHEA_CQE_STAT_ERR_TCP) + pr->p_stats.err_tcp_cksum++; + if (cqe->status & EHEA_CQE_STAT_ERR_IP) + pr->p_stats.err_ip_cksum++; + if (cqe->status & EHEA_CQE_STAT_ERR_CRC) + pr->p_stats.err_frame_crc++; + if (netif_msg_rx_err(pr->port)) { ehea_error("CQE Error for QP %d", pr->qp->init_attr.qp_nr); ehea_dump(cqe, sizeof(*cqe), "CQE"); @@ -345,10 +380,11 @@ static int ehea_treat_poll_error(struct return 0; } -static int ehea_poll(struct net_device *dev, int *budget) +static struct ehea_cqe *ehea_proc_rwqes(struct net_device *dev, + struct ehea_port_res *pr, + int *budget) { - struct ehea_port *port = netdev_priv(dev); - struct ehea_port_res *pr = &port->port_res[0]; + struct ehea_port *port = pr->port; struct ehea_qp *qp = pr->qp; struct ehea_cqe *cqe; struct sk_buff *skb; @@ -359,14 +395,12 @@ static int ehea_poll(struct net_device * int skb_arr_rq2_len = pr->rq2_skba.len; int skb_arr_rq3_len = pr->rq3_skba.len; int processed, processed_rq1, processed_rq2, processed_rq3; - int wqe_index, last_wqe_index, rq, intreq, my_quota, port_reset; + int wqe_index, last_wqe_index, rq, my_quota, port_reset; processed = processed_rq1 = processed_rq2 = processed_rq3 = 0; last_wqe_index = 0; my_quota = min(*budget, dev->quota); - my_quota = min(my_quota, EHEA_POLL_MAX_RWQE); - /* rq0 is low latency RQ */ cqe = ehea_poll_rq1(qp, &wqe_index); while ((my_quota > 0) && cqe) { ehea_inc_rq1(qp); @@ -386,13 +420,14 @@ static int ehea_poll(struct net_device * if (unlikely(!skb)) { if (netif_msg_rx_err(port)) ehea_error("LL rq1: skb=NULL"); - skb = netdev_alloc_skb(dev, + + skb = netdev_alloc_skb(port->netdev, EHEA_L_PKT_SIZE); if (!skb) break; } - memcpy(skb->data, ((char*)cqe) + 64, - cqe->num_bytes_transfered - 4); + skb_copy_to_linear_data(skb, ((char*)cqe) + 64, + cqe->num_bytes_transfered - 4); ehea_fill_skb(dev, skb, cqe); } else if (rq == 2) { /* RQ2 */ skb = get_skb_by_index(skb_arr_rq2, @@ -402,7 +437,7 @@ static int ehea_poll(struct net_device * ehea_error("rq2: skb=NULL"); break; } - ehea_fill_skb(dev, skb, cqe); + ehea_fill_skb(port->netdev, skb, cqe); processed_rq2++; } else { /* RQ3 */ skb = get_skb_by_index(skb_arr_rq3, @@ -412,7 +447,7 @@ static int ehea_poll(struct net_device * ehea_error("rq3: skb=NULL"); break; } - ehea_fill_skb(dev, skb, cqe); + ehea_fill_skb(port->netdev, skb, cqe); processed_rq3++; } @@ -421,9 +456,8 @@ static int ehea_poll(struct net_device * cqe->vlan_tag); else netif_receive_skb(skb); - - } else { /* Error occured */ - pr->p_state.poll_receive_errors++; + } else { + pr->p_stats.poll_receive_errors++; port_reset = ehea_treat_poll_error(pr, rq, cqe, &processed_rq2, &processed_rq3); @@ -433,72 +467,32 @@ static int ehea_poll(struct net_device * cqe = ehea_poll_rq1(qp, &wqe_index); } - dev->quota -= processed; - *budget -= processed; - - pr->p_state.ehea_poll += 1; pr->rx_packets += processed; + *budget -= processed; ehea_refill_rq1(pr, last_wqe_index, processed_rq1); ehea_refill_rq2(pr, processed_rq2); ehea_refill_rq3(pr, processed_rq3); - intreq = ((pr->p_state.ehea_poll & 0xF) == 0xF); - - if (!cqe || intreq) { - netif_rx_complete(dev); - ehea_reset_cq_ep(pr->recv_cq); - ehea_reset_cq_n1(pr->recv_cq); - cqe = hw_qeit_get_valid(&qp->hw_rqueue1); - if (!cqe || intreq) - return 0; - if (!netif_rx_reschedule(dev, my_quota)) - return 0; - } - return 1; + cqe = ehea_poll_rq1(qp, &wqe_index); + return cqe; } -void free_sent_skbs(struct ehea_cqe *cqe, struct ehea_port_res *pr) +static struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota) { struct sk_buff *skb; - int index, max_index_mask, i; - - index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id); - max_index_mask = pr->sq_skba.len - 1; - for (i = 0; i < EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id); i++) { - skb = pr->sq_skba.arr[index]; - if (likely(skb)) { - dev_kfree_skb(skb); - pr->sq_skba.arr[index] = NULL; - } else { - ehea_error("skb=NULL, wr_id=%lX, loop=%d, index=%d", - cqe->wr_id, i, index); - } - index--; - index &= max_index_mask; - } -} - -#define MAX_SENDCOMP_QUOTA 400 -void ehea_send_irq_tasklet(unsigned long data) -{ - struct ehea_port_res *pr = (struct ehea_port_res*)data; struct ehea_cq *send_cq = pr->send_cq; struct ehea_cqe *cqe; - int quota = MAX_SENDCOMP_QUOTA; + int quota = my_quota; int cqe_counter = 0; int swqe_av = 0; + int index; unsigned long flags; - do { - cqe = ehea_poll_cq(send_cq); - if (!cqe) { - ehea_reset_cq_ep(send_cq); - ehea_reset_cq_n1(send_cq); - cqe = ehea_poll_cq(send_cq); - if (!cqe) - break; - } + cqe = ehea_poll_cq(send_cq); + while(cqe && (quota > 0)) { + ehea_inc_cq(send_cq); + cqe_counter++; rmb(); if (cqe->status & EHEA_CQE_STAT_ERR_MASK) { @@ -514,17 +508,25 @@ void ehea_send_irq_tasklet(unsigned long ehea_dump(cqe, sizeof(*cqe), "CQE"); if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id) - == EHEA_SWQE2_TYPE)) - free_sent_skbs(cqe, pr); + == EHEA_SWQE2_TYPE)) { + + index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id); + skb = pr->sq_skba.arr[index]; + dev_kfree_skb(skb); + pr->sq_skba.arr[index] = NULL; + } swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id); quota--; - } while (quota > 0); + + cqe = ehea_poll_cq(send_cq); + }; ehea_update_feca(send_cq, cqe_counter); atomic_add(swqe_av, &pr->swqe_avail); spin_lock_irqsave(&pr->netif_queue, flags); + if (pr->queue_stopped && (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th)) { netif_wake_queue(pr->port->netdev); @@ -532,22 +534,55 @@ void ehea_send_irq_tasklet(unsigned long } spin_unlock_irqrestore(&pr->netif_queue, flags); - if (unlikely(cqe)) - tasklet_hi_schedule(&pr->send_comp_task); + return cqe; } -static irqreturn_t ehea_send_irq_handler(int irq, void *param) +#define EHEA_NAPI_POLL_NUM_BEFORE_IRQ 16 + +static int ehea_poll(struct net_device *dev, int *budget) { - struct ehea_port_res *pr = param; - tasklet_hi_schedule(&pr->send_comp_task); - return IRQ_HANDLED; + struct ehea_port_res *pr = dev->priv; + struct ehea_cqe *cqe; + struct ehea_cqe *cqe_skb = NULL; + int force_irq, wqe_index; + + cqe = ehea_poll_rq1(pr->qp, &wqe_index); + cqe_skb = ehea_poll_cq(pr->send_cq); + + force_irq = (pr->poll_counter > EHEA_NAPI_POLL_NUM_BEFORE_IRQ); + + if ((!cqe && !cqe_skb) || force_irq) { + pr->poll_counter = 0; + netif_rx_complete(dev); + ehea_reset_cq_ep(pr->recv_cq); + ehea_reset_cq_ep(pr->send_cq); + ehea_reset_cq_n1(pr->recv_cq); + ehea_reset_cq_n1(pr->send_cq); + cqe = ehea_poll_rq1(pr->qp, &wqe_index); + cqe_skb = ehea_poll_cq(pr->send_cq); + + if (!cqe && !cqe_skb) + return 0; + + if (!netif_rx_reschedule(dev, dev->quota)) + return 0; + } + + cqe = ehea_proc_rwqes(dev, pr, budget); + cqe_skb = ehea_proc_cqes(pr, 300); + + if (cqe || cqe_skb) + pr->poll_counter++; + + return 1; } static irqreturn_t ehea_recv_irq_handler(int irq, void *param) { struct ehea_port_res *pr = param; - struct ehea_port *port = pr->port; - netif_rx_schedule(port->netdev); + + netif_rx_schedule(pr->d_netdev); + return IRQ_HANDLED; } @@ -580,7 +615,7 @@ static struct ehea_port *ehea_get_port(s { int i; - for (i = 0; i < adapter->num_ports; i++) + for (i = 0; i < EHEA_MAX_PORTS; i++) if (adapter->port[i]) if (adapter->port[i]->logical_port_id == logical_port) return adapter->port[i]; @@ -650,19 +685,25 @@ int ehea_sense_port_attr(struct ehea_por } port->autoneg = 1; + port->num_mcs = cb0->num_default_qps; /* Number of default QPs */ - port->num_def_qps = cb0->num_default_qps; + if (use_mcs) + port->num_def_qps = cb0->num_default_qps; + else + port->num_def_qps = 1; if (!port->num_def_qps) { ret = -EINVAL; goto out_free; } - if (port->num_def_qps >= EHEA_NUM_TX_QP) + port->num_tx_qps = num_tx_qps; + + if (port->num_def_qps >= port->num_tx_qps) port->num_add_tx_qps = 0; else - port->num_add_tx_qps = EHEA_NUM_TX_QP - port->num_def_qps; + port->num_add_tx_qps = port->num_tx_qps - port->num_def_qps; ret = 0; out_free: @@ -882,23 +923,6 @@ static int ehea_reg_interrupts(struct ne struct ehea_port_res *pr; int i, ret; - for (i = 0; i < port->num_def_qps; i++) { - pr = &port->port_res[i]; - snprintf(pr->int_recv_name, EHEA_IRQ_NAME_SIZE - 1 - , "%s-recv%d", dev->name, i); - ret = ibmebus_request_irq(NULL, pr->recv_eq->attr.ist1, - ehea_recv_irq_handler, - IRQF_DISABLED, pr->int_recv_name, pr); - if (ret) { - ehea_error("failed registering irq for ehea_recv_int:" - "port_res_nr:%d, ist=%X", i, - pr->recv_eq->attr.ist1); - goto out_free_seq; - } - if (netif_msg_ifup(port)) - ehea_info("irq_handle 0x%X for funct ehea_recv_int %d " - "registered", pr->recv_eq->attr.ist1, i); - } snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1, "%s-aff", dev->name); @@ -916,41 +940,41 @@ static int ehea_reg_interrupts(struct ne ehea_info("irq_handle 0x%X for function qp_aff_irq_handler " "registered", port->qp_eq->attr.ist1); + for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) { pr = &port->port_res[i]; snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1, - "%s-send%d", dev->name, i); - ret = ibmebus_request_irq(NULL, pr->send_eq->attr.ist1, - ehea_send_irq_handler, + "%s-queue%d", dev->name, i); + ret = ibmebus_request_irq(NULL, pr->eq->attr.ist1, + ehea_recv_irq_handler, IRQF_DISABLED, pr->int_send_name, pr); if (ret) { - ehea_error("failed registering irq for ehea_send " + ehea_error("failed registering irq for ehea_queue " "port_res_nr:%d, ist=%X", i, - pr->send_eq->attr.ist1); + pr->eq->attr.ist1); goto out_free_req; } if (netif_msg_ifup(port)) - ehea_info("irq_handle 0x%X for function ehea_send_int " - "%d registered", pr->send_eq->attr.ist1, i); + ehea_info("irq_handle 0x%X for function ehea_queue_int " + "%d registered", pr->eq->attr.ist1, i); } out: return ret; + out_free_req: while (--i >= 0) { - u32 ist = port->port_res[i].send_eq->attr.ist1; + u32 ist = port->port_res[i].eq->attr.ist1; ibmebus_free_irq(NULL, ist, &port->port_res[i]); } + out_free_qpeq: ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port); i = port->num_def_qps; -out_free_seq: - while (--i >= 0) { - u32 ist = port->port_res[i].recv_eq->attr.ist1; - ibmebus_free_irq(NULL, ist, &port->port_res[i]); - } + goto out; + } static void ehea_free_interrupts(struct net_device *dev) @@ -960,21 +984,13 @@ static void ehea_free_interrupts(struct int i; /* send */ + for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) { pr = &port->port_res[i]; - ibmebus_free_irq(NULL, pr->send_eq->attr.ist1, pr); + ibmebus_free_irq(NULL, pr->eq->attr.ist1, pr); if (netif_msg_intr(port)) ehea_info("free send irq for res %d with handle 0x%X", - i, pr->send_eq->attr.ist1); - } - - /* receive */ - for (i = 0; i < port->num_def_qps; i++) { - pr = &port->port_res[i]; - ibmebus_free_irq(NULL, pr->recv_eq->attr.ist1, pr); - if (netif_msg_intr(port)) - ehea_info("free recv irq for res %d with handle 0x%X", - i, pr->recv_eq->attr.ist1); + i, pr->eq->attr.ist1); } /* associated events */ @@ -1003,8 +1019,13 @@ static int ehea_configure_port(struct eh PXLY_RC_VLAN_FILTER) | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1); - for (i = 0; i < port->num_def_qps; i++) - cb0->default_qpn_arr[i] = port->port_res[0].qp->init_attr.qp_nr; + for (i = 0; i < port->num_mcs; i++) + if (use_mcs) + cb0->default_qpn_arr[i] = + port->port_res[i].qp->init_attr.qp_nr; + else + cb0->default_qpn_arr[i] = + port->port_res[0].qp->init_attr.qp_nr; if (netif_msg_ifup(port)) ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port"); @@ -1027,52 +1048,35 @@ out: return ret; } -static int ehea_gen_smrs(struct ehea_port_res *pr) +int ehea_gen_smrs(struct ehea_port_res *pr) { - u64 hret; + int ret; struct ehea_adapter *adapter = pr->port->adapter; - hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle, - adapter->mr.vaddr, EHEA_MR_ACC_CTRL, - adapter->pd, &pr->send_mr); - if (hret != H_SUCCESS) + ret = ehea_gen_smr(adapter, &adapter->mr, &pr->send_mr); + if (ret) goto out; - hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle, - adapter->mr.vaddr, EHEA_MR_ACC_CTRL, - adapter->pd, &pr->recv_mr); - if (hret != H_SUCCESS) - goto out_freeres; + ret = ehea_gen_smr(adapter, &adapter->mr, &pr->recv_mr); + if (ret) + goto out_free; return 0; -out_freeres: - hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle); - if (hret != H_SUCCESS) - ehea_error("failed freeing SMR"); +out_free: + ehea_rem_mr(&pr->send_mr); out: + ehea_error("Generating SMRS failed\n"); return -EIO; } -static int ehea_rem_smrs(struct ehea_port_res *pr) +int ehea_rem_smrs(struct ehea_port_res *pr) { - struct ehea_adapter *adapter = pr->port->adapter; - int ret = 0; - u64 hret; - - hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle); - if (hret != H_SUCCESS) { - ret = -EIO; - ehea_error("failed freeing send SMR for pr=%p", pr); - } - - hret = ehea_h_free_resource(adapter->handle, pr->recv_mr.handle); - if (hret != H_SUCCESS) { - ret = -EIO; - ehea_error("failed freeing recv SMR for pr=%p", pr); - } - - return ret; + if ((ehea_rem_mr(&pr->send_mr)) + || (ehea_rem_mr(&pr->recv_mr))) + return -EIO; + else + return 0; } static int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries) @@ -1103,25 +1107,17 @@ static int ehea_init_port_res(struct ehe memset(pr, 0, sizeof(struct ehea_port_res)); pr->port = port; - spin_lock_init(&pr->send_lock); - spin_lock_init(&pr->recv_lock); spin_lock_init(&pr->xmit_lock); spin_lock_init(&pr->netif_queue); - pr->recv_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0); - if (!pr->recv_eq) { - ehea_error("create_eq failed (recv_eq)"); - goto out_free; - } - - pr->send_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0); - if (!pr->send_eq) { - ehea_error("create_eq failed (send_eq)"); + pr->eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0); + if (!pr->eq) { + ehea_error("create_eq failed (eq)"); goto out_free; } pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq, - pr->recv_eq->fw_handle, + pr->eq->fw_handle, port->logical_port_id); if (!pr->recv_cq) { ehea_error("create_cq failed (cq_recv)"); @@ -1129,7 +1125,7 @@ static int ehea_init_port_res(struct ehe } pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq, - pr->send_eq->fw_handle, + pr->eq->fw_handle, port->logical_port_id); if (!pr->send_cq) { ehea_error("create_cq failed (cq_send)"); @@ -1194,11 +1190,20 @@ static int ehea_init_port_res(struct ehe ret = -EIO; goto out_free; } - tasklet_init(&pr->send_comp_task, ehea_send_irq_tasklet, - (unsigned long)pr); + atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1); kfree(init_attr); + + pr->d_netdev = alloc_netdev(0, "", ether_setup); + if (!pr->d_netdev) + goto out_free; + pr->d_netdev->priv = pr; + pr->d_netdev->weight = 64; + pr->d_netdev->poll = ehea_poll; + set_bit(__LINK_STATE_START, &pr->d_netdev->state); + strcpy(pr->d_netdev->name, port->netdev->name); + ret = 0; goto out; @@ -1211,8 +1216,7 @@ out_free: ehea_destroy_qp(pr->qp); ehea_destroy_cq(pr->send_cq); ehea_destroy_cq(pr->recv_cq); - ehea_destroy_eq(pr->send_eq); - ehea_destroy_eq(pr->recv_eq); + ehea_destroy_eq(pr->eq); out: return ret; } @@ -1221,13 +1225,14 @@ static int ehea_clean_portres(struct ehe { int ret, i; + free_netdev(pr->d_netdev); + ret = ehea_destroy_qp(pr->qp); if (!ret) { ehea_destroy_cq(pr->send_cq); ehea_destroy_cq(pr->recv_cq); - ehea_destroy_eq(pr->send_eq); - ehea_destroy_eq(pr->recv_eq); + ehea_destroy_eq(pr->eq); for (i = 0; i < pr->rq1_skba.len; i++) if (pr->rq1_skba.arr[i]) @@ -1262,8 +1267,8 @@ static int ehea_clean_portres(struct ehe static inline void write_ip_start_end(struct ehea_swqe *swqe, const struct sk_buff *skb) { - swqe->ip_start = (u8)(((u64)skb->nh.iph) - ((u64)skb->data)); - swqe->ip_end = (u8)(swqe->ip_start + skb->nh.iph->ihl * 4 - 1); + swqe->ip_start = skb_network_offset(skb); + swqe->ip_end = (u8)(swqe->ip_start + ip_hdrlen(skb) - 1); } static inline void write_tcp_offset_end(struct ehea_swqe *swqe, @@ -1300,13 +1305,13 @@ static void write_swqe2_TSO(struct sk_bu /* copy only eth/ip/tcp headers to immediate data and * the rest of skb->data to sg1entry */ - headersize = ETH_HLEN + (skb->nh.iph->ihl * 4) + (skb->h.th->doff * 4); + headersize = ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb); skb_data_size = skb->len - skb->data_len; if (skb_data_size >= headersize) { /* copy immediate data */ - memcpy(imm_data, skb->data, headersize); + skb_copy_from_linear_data(skb, imm_data, headersize); swqe->immediate_data_length = headersize; if (skb_data_size > headersize) { @@ -1337,7 +1342,7 @@ static void write_swqe2_nonTSO(struct sk */ if (skb_data_size >= SWQE2_MAX_IMM) { /* copy immediate data */ - memcpy(imm_data, skb->data, SWQE2_MAX_IMM); + skb_copy_from_linear_data(skb, imm_data, SWQE2_MAX_IMM); swqe->immediate_data_length = SWQE2_MAX_IMM; @@ -1350,7 +1355,7 @@ static void write_swqe2_nonTSO(struct sk swqe->descriptors++; } } else { - memcpy(imm_data, skb->data, skb_data_size); + skb_copy_from_linear_data(skb, imm_data, skb_data_size); swqe->immediate_data_length = skb_data_size; } } @@ -1688,6 +1693,7 @@ static void ehea_xmit2(struct sk_buff *s struct ehea_swqe *swqe, u32 lkey) { if (skb->protocol == htons(ETH_P_IP)) { + const struct iphdr *iph = ip_hdr(skb); /* IPv4 */ swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IP_CHECKSUM @@ -1697,15 +1703,15 @@ static void ehea_xmit2(struct sk_buff *s write_ip_start_end(swqe, skb); - if (skb->nh.iph->protocol == IPPROTO_UDP) { - if ((skb->nh.iph->frag_off & IP_MF) || - (skb->nh.iph->frag_off & IP_OFFSET)) + if (iph->protocol == IPPROTO_UDP) { + if ((iph->frag_off & IP_MF) || + (iph->frag_off & IP_OFFSET)) /* IP fragment, so don't change cs */ swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM; else write_udp_offset_end(swqe, skb); - } else if (skb->nh.iph->protocol == IPPROTO_TCP) { + } else if (iph->protocol == IPPROTO_TCP) { write_tcp_offset_end(swqe, skb); } @@ -1731,10 +1737,11 @@ static void ehea_xmit3(struct sk_buff *s int i; if (skb->protocol == htons(ETH_P_IP)) { + const struct iphdr *iph = ip_hdr(skb); /* IPv4 */ write_ip_start_end(swqe, skb); - if (skb->nh.iph->protocol == IPPROTO_TCP) { + if (iph->protocol == IPPROTO_TCP) { swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IP_CHECKSUM | EHEA_SWQE_TCP_CHECKSUM @@ -1742,9 +1749,9 @@ static void ehea_xmit3(struct sk_buff *s write_tcp_offset_end(swqe, skb); - } else if (skb->nh.iph->protocol == IPPROTO_UDP) { - if ((skb->nh.iph->frag_off & IP_MF) || - (skb->nh.iph->frag_off & IP_OFFSET)) + } else if (iph->protocol == IPPROTO_UDP) { + if ((iph->frag_off & IP_MF) || + (iph->frag_off & IP_OFFSET)) /* IP fragment, so don't change cs */ swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IMM_DATA_PRESENT; @@ -1770,10 +1777,11 @@ static void ehea_xmit3(struct sk_buff *s /* copy (immediate) data */ if (nfrags == 0) { /* data is in a single piece */ - memcpy(imm_data, skb->data, skb->len); + skb_copy_from_linear_data(skb, imm_data, skb->len); } else { /* first copy data from the skb->data buffer ... */ - memcpy(imm_data, skb->data, skb->len - skb->data_len); + skb_copy_from_linear_data(skb, imm_data, + skb->len - skb->data_len); imm_data += skb->len - skb->data_len; /* ... then copy data from the fragments */ @@ -1789,6 +1797,22 @@ static void ehea_xmit3(struct sk_buff *s dev_kfree_skb(skb); } +static inline int ehea_hash_skb(struct sk_buff *skb, int num_qps) +{ + struct tcphdr *tcp; + u32 tmp; + + if ((skb->protocol == htons(ETH_P_IP)) && + (skb->nh.iph->protocol == IPPROTO_TCP)) { + tcp = (struct tcphdr*)(skb->nh.raw + (skb->nh.iph->ihl * 4)); + tmp = (tcp->source + (tcp->dest << 16)) % 31; + tmp += skb->nh.iph->daddr % 31; + return tmp % num_qps; + } + else + return 0; +} + static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ehea_port *port = netdev_priv(dev); @@ -1796,9 +1820,17 @@ static int ehea_start_xmit(struct sk_buf unsigned long flags; u32 lkey; int swqe_index; - struct ehea_port_res *pr = &port->port_res[0]; + struct ehea_port_res *pr; + + pr = &port->port_res[ehea_hash_skb(skb, port->num_tx_qps)]; - spin_lock(&pr->xmit_lock); + if (!spin_trylock(&pr->xmit_lock)) + return NETDEV_TX_BUSY; + + if (pr->queue_stopped) { + spin_unlock(&pr->xmit_lock); + return NETDEV_TX_BUSY; + } swqe = ehea_get_swqe(pr->qp, &swqe_index); memset(swqe, 0, SWQE_HEADER_SIZE); @@ -1821,6 +1853,7 @@ static int ehea_start_xmit(struct sk_buf swqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE) | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter) + | EHEA_BMASK_SET(EHEA_WR_ID_REFILL, 1) | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index); pr->sq_skba.arr[pr->sq_skba.index] = skb; @@ -1829,14 +1862,7 @@ static int ehea_start_xmit(struct sk_buf lkey = pr->send_mr.lkey; ehea_xmit2(skb, dev, swqe, lkey); - - if (pr->swqe_count >= (EHEA_SIG_IV_LONG - 1)) { - swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL, - EHEA_SIG_IV_LONG); - swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION; - pr->swqe_count = 0; - } else - pr->swqe_count += 1; + swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION; } pr->swqe_id_counter += 1; @@ -1856,6 +1882,7 @@ static int ehea_start_xmit(struct sk_buf if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) { spin_lock_irqsave(&pr->netif_queue, flags); if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) { + pr->p_stats.queue_stopped++; netif_stop_queue(dev); pr->queue_stopped = 1; } @@ -2057,7 +2084,7 @@ static int ehea_port_res_setup(struct eh } pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries; - pr_cfg.max_entries_scq = sq_entries; + pr_cfg.max_entries_scq = sq_entries * 2; pr_cfg.max_entries_sq = sq_entries; pr_cfg.max_entries_rq1 = rq1_entries; pr_cfg.max_entries_rq2 = rq2_entries; @@ -2106,6 +2133,28 @@ static int ehea_clean_all_portres(struct return ret; } +static void ehea_remove_adapter_mr (struct ehea_adapter *adapter) +{ + int i; + + for (i=0; i < EHEA_MAX_PORTS; i++) + if (adapter->port[i]) + return; + + ehea_rem_mr(&adapter->mr); +} + +static int ehea_add_adapter_mr (struct ehea_adapter *adapter) +{ + int i; + + for (i=0; i < EHEA_MAX_PORTS; i++) + if (adapter->port[i]) + return 0; + + return ehea_reg_kernel_mr(adapter, &adapter->mr); +} + static int ehea_up(struct net_device *dev) { int ret, i; @@ -2205,8 +2254,10 @@ static int ehea_down(struct net_device * ehea_drop_multicast_list(dev); ehea_free_interrupts(dev); - for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) - tasklet_kill(&port->port_res[i].send_comp_task); + for (i = 0; i < port->num_def_qps; i++) + while (test_bit(__LINK_STATE_RX_SCHED, + &port->port_res[i].d_netdev->state)) + msleep(1); ehea_broadcast_reg_helper(port, H_DEREG_BCMC); ret = ehea_clean_all_portres(port); @@ -2273,8 +2324,6 @@ static void ehea_tx_watchdog(struct net_ int ehea_sense_adapter_attr(struct ehea_adapter *adapter) { struct hcp_query_ehea *cb; - struct device_node *lhea_dn = NULL; - struct device_node *eth_dn = NULL; u64 hret; int ret; @@ -2291,18 +2340,6 @@ int ehea_sense_adapter_attr(struct ehea_ goto out_herr; } - /* Determine the number of available logical ports - * by counting the child nodes of the lhea OFDT entry - */ - adapter->num_ports = 0; - lhea_dn = of_find_node_by_name(lhea_dn, "lhea"); - do { - eth_dn = of_get_next_child(lhea_dn, eth_dn); - if (eth_dn) - adapter->num_ports++; - } while ( eth_dn ); - of_node_put(lhea_dn); - adapter->max_mc_mac = cb->max_mc_mac - 1; ret = 0; @@ -2312,79 +2349,188 @@ out: return ret; } -static int ehea_setup_single_port(struct ehea_port *port, - struct device_node *dn) +int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo) { - int ret; - u64 hret; - struct net_device *dev = port->netdev; - struct ehea_adapter *adapter = port->adapter; struct hcp_ehea_port_cb4 *cb4; - u32 *dn_log_port_id; - int jumbo = 0; - - sema_init(&port->port_lock, 1); - port->state = EHEA_PORT_DOWN; - port->sig_comp_iv = sq_entries / 10; - - if (!dn) { - ehea_error("bad device node: dn=%p", dn); - ret = -EINVAL; - goto out; - } - - port->of_dev_node = dn; - - /* Determine logical port id */ - dn_log_port_id = (u32*)get_property(dn, "ibm,hea-port-no", NULL); - - if (!dn_log_port_id) { - ehea_error("bad device node: dn_log_port_id=%p", - dn_log_port_id); - ret = -EINVAL; - goto out; - } - port->logical_port_id = *dn_log_port_id; - - port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL); - if (!port->mc_list) { - ret = -ENOMEM; - goto out; - } - - INIT_LIST_HEAD(&port->mc_list->list); + u64 hret; + int ret = 0; - ret = ehea_sense_port_attr(port); - if (ret) - goto out; + *jumbo = 0; - /* Enable Jumbo frames */ + /* (Try to) enable *jumbo frames */ cb4 = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!cb4) { ehea_error("no mem for cb4"); + ret = -ENOMEM; + goto out; } else { - hret = ehea_h_query_ehea_port(adapter->handle, + hret = ehea_h_query_ehea_port(port->adapter->handle, port->logical_port_id, H_PORT_CB4, H_PORT_CB4_JUMBO, cb4); - if (hret == H_SUCCESS) { if (cb4->jumbo_frame) - jumbo = 1; + *jumbo = 1; else { cb4->jumbo_frame = 1; - hret = ehea_h_modify_ehea_port(adapter->handle, + hret = ehea_h_modify_ehea_port(port->adapter-> + handle, port-> - logical_port_id, + logical_port_id, H_PORT_CB4, H_PORT_CB4_JUMBO, cb4); if (hret == H_SUCCESS) - jumbo = 1; + *jumbo = 1; } - } + } else + ret = -EINVAL; + kfree(cb4); } +out: + return ret; +} + +static ssize_t ehea_show_port_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev); + return sprintf(buf, "0x%X", port->logical_port_id); +} + +static DEVICE_ATTR(log_port_id, S_IRUSR | S_IRGRP | S_IROTH, ehea_show_port_id, + NULL); + +static void __devinit logical_port_release(struct device *dev) +{ + struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev); + of_node_put(port->ofdev.node); +} + +static int ehea_driver_sysfs_add(struct device *dev, + struct device_driver *driver) +{ + int ret; + + ret = sysfs_create_link(&driver->kobj, &dev->kobj, + kobject_name(&dev->kobj)); + if (ret == 0) { + ret = sysfs_create_link(&dev->kobj, &driver->kobj, + "driver"); + if (ret) + sysfs_remove_link(&driver->kobj, + kobject_name(&dev->kobj)); + } + return ret; +} + +static void ehea_driver_sysfs_remove(struct device *dev, + struct device_driver *driver) +{ + struct device_driver *drv = driver; + + if (drv) { + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + } +} + +static struct device *ehea_register_port(struct ehea_port *port, + struct device_node *dn) +{ + int ret; + + port->ofdev.node = of_node_get(dn); + port->ofdev.dev.parent = &port->adapter->ebus_dev->ofdev.dev; + port->ofdev.dev.bus = &ibmebus_bus_type; + + sprintf(port->ofdev.dev.bus_id, "port%d", port_name_cnt++); + port->ofdev.dev.release = logical_port_release; + + ret = of_device_register(&port->ofdev); + if (ret) { + ehea_error("failed to register device. ret=%d", ret); + goto out; + } + + ret = device_create_file(&port->ofdev.dev, &dev_attr_log_port_id); + if (ret) { + ehea_error("failed to register attributes, ret=%d", ret); + goto out_unreg_of_dev; + } + + ret = ehea_driver_sysfs_add(&port->ofdev.dev, &ehea_driver.driver); + if (ret) { + ehea_error("failed to register sysfs driver link"); + goto out_rem_dev_file; + } + + return &port->ofdev.dev; + +out_rem_dev_file: + device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id); +out_unreg_of_dev: + of_device_unregister(&port->ofdev); +out: + return NULL; +} + +static void ehea_unregister_port(struct ehea_port *port) +{ + ehea_driver_sysfs_remove(&port->ofdev.dev, &ehea_driver.driver); + device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id); + of_device_unregister(&port->ofdev); +} + +struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, + u32 logical_port_id, + struct device_node *dn) +{ + int ret; + struct net_device *dev; + struct ehea_port *port; + struct device *port_dev; + int jumbo; + + /* allocate memory for the port structures */ + dev = alloc_etherdev(sizeof(struct ehea_port)); + + if (!dev) { + ehea_error("no mem for net_device"); + ret = -ENOMEM; + goto out_err; + } + + port = netdev_priv(dev); + + sema_init(&port->port_lock, 1); + port->state = EHEA_PORT_DOWN; + port->sig_comp_iv = sq_entries / 10; + + port->adapter = adapter; + port->netdev = dev; + port->logical_port_id = logical_port_id; + + port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT); + + port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL); + if (!port->mc_list) { + ret = -ENOMEM; + goto out_free_ethdev; + } + + INIT_LIST_HEAD(&port->mc_list->list); + + ret = ehea_sense_port_attr(port); + if (ret) + goto out_free_mc_list; + + port_dev = ehea_register_port(port, dn); + if (!port_dev) + goto out_free_mc_list; + + SET_NETDEV_DEV(dev, port_dev); /* initialize net_device structure */ SET_MODULE_OWNER(dev); @@ -2417,84 +2563,225 @@ static int ehea_setup_single_port(struct ret = register_netdev(dev); if (ret) { ehea_error("register_netdev failed. ret=%d", ret); - goto out_free; + goto out_unreg_port; } + ret = ehea_get_jumboframe_status(port, &jumbo); + if (ret) + ehea_error("failed determining jumbo frame status for %s", + port->netdev->name); + ehea_info("%s: Jumbo frames are %sabled", dev->name, jumbo == 1 ? "en" : "dis"); - port->netdev = dev; - ret = 0; - goto out; + return port; -out_free: +out_unreg_port: + ehea_unregister_port(port); + +out_free_mc_list: kfree(port->mc_list); -out: - return ret; + +out_free_ethdev: + free_netdev(dev); + +out_err: + ehea_error("setting up logical port with id=%d failed, ret=%d", + logical_port_id, ret); + return NULL; +} + +static void ehea_shutdown_single_port(struct ehea_port *port) +{ + unregister_netdev(port->netdev); + ehea_unregister_port(port); + kfree(port->mc_list); + free_netdev(port->netdev); } static int ehea_setup_ports(struct ehea_adapter *adapter) { - int ret; - int port_setup_ok = 0; + struct device_node *lhea_dn; + struct device_node *eth_dn = NULL; + + u32 *dn_log_port_id; + int i = 0; + + lhea_dn = adapter->ebus_dev->ofdev.node; + while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) { + + dn_log_port_id = (u32*)get_property(eth_dn, "ibm,hea-port-no", + NULL); + if (!dn_log_port_id) { + ehea_error("bad device node: eth_dn name=%s", + eth_dn->full_name); + continue; + } + + if (ehea_add_adapter_mr(adapter)) { + ehea_error("creating MR failed"); + of_node_put(eth_dn); + return -EIO; + } + + adapter->port[i] = ehea_setup_single_port(adapter, + *dn_log_port_id, + eth_dn); + if (adapter->port[i]) + ehea_info("%s -> logical port id #%d", + adapter->port[i]->netdev->name, + *dn_log_port_id); + else + ehea_remove_adapter_mr(adapter); + + i++; + }; + + return 0; +} + +static struct device_node *ehea_get_eth_dn(struct ehea_adapter *adapter, + u32 logical_port_id) +{ + struct device_node *lhea_dn; + struct device_node *eth_dn = NULL; + u32 *dn_log_port_id; + + lhea_dn = adapter->ebus_dev->ofdev.node; + while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) { + + dn_log_port_id = (u32*)get_property(eth_dn, "ibm,hea-port-no", + NULL); + if (dn_log_port_id) + if (*dn_log_port_id == logical_port_id) + return eth_dn; + }; + + return NULL; +} + +static ssize_t ehea_probe_port(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ehea_adapter *adapter = dev->driver_data; struct ehea_port *port; - struct device_node *dn = NULL; - struct net_device *dev; + struct device_node *eth_dn = NULL; int i; - /* get port properties for all ports */ - for (i = 0; i < adapter->num_ports; i++) { + u32 logical_port_id; - if (adapter->port[i]) - continue; /* port already up and running */ + sscanf(buf, "%X", &logical_port_id); - /* allocate memory for the port structures */ - dev = alloc_etherdev(sizeof(struct ehea_port)); + port = ehea_get_port(adapter, logical_port_id); - if (!dev) { - ehea_error("no mem for net_device"); - break; - } + if (port) { + ehea_info("adding port with logical port id=%d failed. port " + "already configured as %s.", logical_port_id, + port->netdev->name); + return -EINVAL; + } - port = netdev_priv(dev); - port->adapter = adapter; - port->netdev = dev; - adapter->port[i] = port; - port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT); + eth_dn = ehea_get_eth_dn(adapter, logical_port_id); - dn = of_find_node_by_name(dn, "ethernet"); - ret = ehea_setup_single_port(port, dn); - if (ret) { - /* Free mem for this port struct. The others will be - processed on rollback */ - free_netdev(dev); - adapter->port[i] = NULL; - ehea_error("eHEA port %d setup failed, ret=%d", i, ret); - } + if (!eth_dn) { + ehea_info("no logical port with id %d found", logical_port_id); + return -EINVAL; } - of_node_put(dn); + if (ehea_add_adapter_mr(adapter)) { + ehea_error("creating MR failed"); + return -EIO; + } - /* Check for succesfully set up ports */ - for (i = 0; i < adapter->num_ports; i++) - if (adapter->port[i]) - port_setup_ok++; + port = ehea_setup_single_port(adapter, logical_port_id, eth_dn); - if (port_setup_ok) - ret = 0; /* At least some ports are setup correctly */ - else - ret = -EINVAL; + of_node_put(eth_dn); + + if (port) { + for (i=0; i < EHEA_MAX_PORTS; i++) + if (!adapter->port[i]) { + adapter->port[i] = port; + break; + } + + ehea_info("added %s (logical port id=%d)", port->netdev->name, + logical_port_id); + } else { + ehea_remove_adapter_mr(adapter); + return -EIO; + } + + return (ssize_t) count; +} + +static ssize_t ehea_remove_port(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ehea_adapter *adapter = dev->driver_data; + struct ehea_port *port; + int i; + u32 logical_port_id; + + sscanf(buf, "%X", &logical_port_id); + + port = ehea_get_port(adapter, logical_port_id); + + if (port) { + ehea_info("removed %s (logical port id=%d)", port->netdev->name, + logical_port_id); + + ehea_shutdown_single_port(port); + + for (i=0; i < EHEA_MAX_PORTS; i++) + if (adapter->port[i] == port) { + adapter->port[i] = NULL; + break; + } + } else { + ehea_error("removing port with logical port id=%d failed. port " + "not configured.", logical_port_id); + return -EINVAL; + } + + ehea_remove_adapter_mr(adapter); + + return (ssize_t) count; +} + +static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port); +static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port); +int ehea_create_device_sysfs(struct ibmebus_dev *dev) +{ + int ret = device_create_file(&dev->ofdev.dev, &dev_attr_probe_port); + if (ret) + goto out; + + ret = device_create_file(&dev->ofdev.dev, &dev_attr_remove_port); +out: return ret; } -static int __devinit ehea_probe(struct ibmebus_dev *dev, - const struct of_device_id *id) +void ehea_remove_device_sysfs(struct ibmebus_dev *dev) +{ + device_remove_file(&dev->ofdev.dev, &dev_attr_probe_port); + device_remove_file(&dev->ofdev.dev, &dev_attr_remove_port); +} + +static int __devinit ehea_probe_adapter(struct ibmebus_dev *dev, + const struct of_device_id *id) { struct ehea_adapter *adapter; u64 *adapter_handle; int ret; + if (!dev || !dev->ofdev.node) { + ehea_error("Invalid ibmebus device probed"); + return -EINVAL; + } + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) { ret = -ENOMEM; @@ -2502,6 +2789,8 @@ static int __devinit ehea_probe(struct i goto out; } + adapter->ebus_dev = dev; + adapter_handle = (u64*)get_property(dev->ofdev.node, "ibm,hea-handle", NULL); if (adapter_handle) @@ -2518,26 +2807,21 @@ static int __devinit ehea_probe(struct i dev->ofdev.dev.driver_data = adapter; - ret = ehea_reg_mr_adapter(adapter); - if (ret) { - dev_err(&dev->ofdev.dev, "reg_mr_adapter failed\n"); - goto out_free_ad; - } /* initialize adapter and ports */ /* get adapter properties */ ret = ehea_sense_adapter_attr(adapter); if (ret) { dev_err(&dev->ofdev.dev, "sense_adapter_attr failed: %d", ret); - goto out_free_res; + goto out_free_ad; } - dev_info(&dev->ofdev.dev, "%d eHEA ports found\n", adapter->num_ports); adapter->neq = ehea_create_eq(adapter, EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1); if (!adapter->neq) { + ret = -EIO; dev_err(&dev->ofdev.dev, "NEQ creation failed"); - goto out_free_res; + goto out_free_ad; } tasklet_init(&adapter->neq_tasklet, ehea_neq_tasklet, @@ -2552,18 +2836,27 @@ static int __devinit ehea_probe(struct i } adapter->ehea_wq = create_workqueue("ehea_wq"); - if (!adapter->ehea_wq) + if (!adapter->ehea_wq) { + ret = -EIO; goto out_free_irq; + } + + ret = ehea_create_device_sysfs(dev); + if (ret) + goto out_kill_wq; ret = ehea_setup_ports(adapter); if (ret) { dev_err(&dev->ofdev.dev, "setup_ports failed"); - goto out_kill_wq; + goto out_rem_dev_sysfs; } ret = 0; goto out; +out_rem_dev_sysfs: + ehea_remove_device_sysfs(dev); + out_kill_wq: destroy_workqueue(adapter->ehea_wq); @@ -2573,45 +2866,32 @@ out_free_irq: out_kill_eq: ehea_destroy_eq(adapter->neq); -out_free_res: - ehea_h_free_resource(adapter->handle, adapter->mr.handle); - out_free_ad: kfree(adapter); out: return ret; } -static void ehea_shutdown_single_port(struct ehea_port *port) -{ - unregister_netdev(port->netdev); - kfree(port->mc_list); - free_netdev(port->netdev); -} - static int __devexit ehea_remove(struct ibmebus_dev *dev) { struct ehea_adapter *adapter = dev->ofdev.dev.driver_data; - u64 hret; int i; - for (i = 0; i < adapter->num_ports; i++) + for (i = 0; i < EHEA_MAX_PORTS; i++) if (adapter->port[i]) { ehea_shutdown_single_port(adapter->port[i]); adapter->port[i] = NULL; } + + ehea_remove_device_sysfs(dev); + destroy_workqueue(adapter->ehea_wq); ibmebus_free_irq(NULL, adapter->neq->attr.ist1, adapter); tasklet_kill(&adapter->neq_tasklet); ehea_destroy_eq(adapter->neq); - - hret = ehea_h_free_resource(adapter->handle, adapter->mr.handle); - if (hret) { - dev_err(&dev->ofdev.dev, "free_resource_mr failed"); - return -EIO; - } + ehea_remove_adapter_mr(adapter); kfree(adapter); return 0; } @@ -2644,21 +2924,6 @@ static int check_module_parm(void) return ret; } -static struct of_device_id ehea_device_table[] = { - { - .name = "lhea", - .compatible = "IBM,lhea", - }, - {}, -}; - -static struct ibmebus_driver ehea_driver = { - .name = "ehea", - .id_table = ehea_device_table, - .probe = ehea_probe, - .remove = ehea_remove, -}; - int __init ehea_module_init(void) { int ret; diff --git a/drivers/net/ehea/ehea_phyp.c b/drivers/net/ehea/ehea_phyp.c index bc3c005..95c4a7f 100644 --- a/drivers/net/ehea/ehea_phyp.c +++ b/drivers/net/ehea/ehea_phyp.c @@ -478,12 +478,14 @@ u64 ehea_h_disable_and_get_hea(const u64 0, 0, 0, 0, 0, 0); /* R7-R12 */ } -u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle) +u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle, + u64 force_bit) { return ehea_plpar_hcall_norets(H_FREE_RESOURCE, adapter_handle, /* R4 */ res_handle, /* R5 */ - 0, 0, 0, 0, 0); /* R6-R10 */ + force_bit, + 0, 0, 0, 0); /* R7-R10 */ } u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr, diff --git a/drivers/net/ehea/ehea_phyp.h b/drivers/net/ehea/ehea_phyp.h index 90acddb..d17a45a 100644 --- a/drivers/net/ehea/ehea_phyp.h +++ b/drivers/net/ehea/ehea_phyp.h @@ -414,7 +414,11 @@ #define H_DISABLE_GET_RQC 3 u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle); -u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle); +#define FORCE_FREE 1 +#define NORMAL_FREE 0 + +u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle, + u64 force_bit); u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr, const u64 length, const u32 access_ctrl, diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ehea/ehea_qmr.c index 96ff3b6..f24a886 100644 --- a/drivers/net/ehea/ehea_qmr.c +++ b/drivers/net/ehea/ehea_qmr.c @@ -197,7 +197,7 @@ out_kill_hwq: hw_queue_dtor(&cq->hw_queue); out_freeres: - ehea_h_free_resource(adapter->handle, cq->fw_handle); + ehea_h_free_resource(adapter->handle, cq->fw_handle, FORCE_FREE); out_freemem: kfree(cq); @@ -206,25 +206,38 @@ out_nomem: return NULL; } -int ehea_destroy_cq(struct ehea_cq *cq) +u64 ehea_destroy_cq_res(struct ehea_cq *cq, u64 force) { - u64 adapter_handle, hret; + u64 hret; + u64 adapter_handle = cq->adapter->handle; + + /* deregister all previous registered pages */ + hret = ehea_h_free_resource(adapter_handle, cq->fw_handle, force); + if (hret != H_SUCCESS) + return hret; + + hw_queue_dtor(&cq->hw_queue); + kfree(cq); + + return hret; +} +int ehea_destroy_cq(struct ehea_cq *cq) +{ + u64 hret; if (!cq) return 0; - adapter_handle = cq->adapter->handle; + if ((hret = ehea_destroy_cq_res(cq, NORMAL_FREE)) == H_R_STATE) { + ehea_error_data(cq->adapter, cq->fw_handle); + hret = ehea_destroy_cq_res(cq, FORCE_FREE); + } - /* deregister all previous registered pages */ - hret = ehea_h_free_resource(adapter_handle, cq->fw_handle); if (hret != H_SUCCESS) { ehea_error("destroy CQ failed"); return -EIO; } - hw_queue_dtor(&cq->hw_queue); - kfree(cq); - return 0; } @@ -297,7 +310,7 @@ out_kill_hwq: hw_queue_dtor(&eq->hw_queue); out_freeres: - ehea_h_free_resource(adapter->handle, eq->fw_handle); + ehea_h_free_resource(adapter->handle, eq->fw_handle, FORCE_FREE); out_freemem: kfree(eq); @@ -316,27 +329,41 @@ struct ehea_eqe *ehea_poll_eq(struct ehe return eqe; } -int ehea_destroy_eq(struct ehea_eq *eq) +u64 ehea_destroy_eq_res(struct ehea_eq *eq, u64 force) { u64 hret; unsigned long flags; - if (!eq) - return 0; - spin_lock_irqsave(&eq->spinlock, flags); - hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle); + hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle, force); spin_unlock_irqrestore(&eq->spinlock, flags); - if (hret != H_SUCCESS) { - ehea_error("destroy_eq failed"); - return -EIO; - } + if (hret != H_SUCCESS) + return hret; hw_queue_dtor(&eq->hw_queue); kfree(eq); + return hret; +} + +int ehea_destroy_eq(struct ehea_eq *eq) +{ + u64 hret; + if (!eq) + return 0; + + if ((hret = ehea_destroy_eq_res(eq, NORMAL_FREE)) == H_R_STATE) { + ehea_error_data(eq->adapter, eq->fw_handle); + hret = ehea_destroy_eq_res(eq, FORCE_FREE); + } + + if (hret != H_SUCCESS) { + ehea_error("destroy EQ failed"); + return -EIO; + } + return 0; } @@ -471,41 +498,56 @@ out_kill_hwsq: out_freeres: ehea_h_disable_and_get_hea(adapter->handle, qp->fw_handle); - ehea_h_free_resource(adapter->handle, qp->fw_handle); + ehea_h_free_resource(adapter->handle, qp->fw_handle, FORCE_FREE); out_freemem: kfree(qp); return NULL; } -int ehea_destroy_qp(struct ehea_qp *qp) +u64 ehea_destroy_qp_res(struct ehea_qp *qp, u64 force) { - u64 hret; - struct ehea_qp_init_attr *qp_attr = &qp->init_attr; + u64 hret; + struct ehea_qp_init_attr *qp_attr = &qp->init_attr; - if (!qp) - return 0; - ehea_h_disable_and_get_hea(qp->adapter->handle, qp->fw_handle); - hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle); - if (hret != H_SUCCESS) { - ehea_error("destroy_qp failed"); - return -EIO; - } + ehea_h_disable_and_get_hea(qp->adapter->handle, qp->fw_handle); + hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle, force); + if (hret != H_SUCCESS) + return hret; - hw_queue_dtor(&qp->hw_squeue); - hw_queue_dtor(&qp->hw_rqueue1); + hw_queue_dtor(&qp->hw_squeue); + hw_queue_dtor(&qp->hw_rqueue1); - if (qp_attr->rq_count > 1) - hw_queue_dtor(&qp->hw_rqueue2); - if (qp_attr->rq_count > 2) - hw_queue_dtor(&qp->hw_rqueue3); - kfree(qp); + if (qp_attr->rq_count > 1) + hw_queue_dtor(&qp->hw_rqueue2); + if (qp_attr->rq_count > 2) + hw_queue_dtor(&qp->hw_rqueue3); + kfree(qp); - return 0; + return hret; } -int ehea_reg_mr_adapter(struct ehea_adapter *adapter) +int ehea_destroy_qp(struct ehea_qp *qp) +{ + u64 hret; + if (!qp) + return 0; + + if ((hret = ehea_destroy_qp_res(qp, NORMAL_FREE)) == H_R_STATE) { + ehea_error_data(qp->adapter, qp->fw_handle); + hret = ehea_destroy_qp_res(qp, FORCE_FREE); + } + + if (hret != H_SUCCESS) { + ehea_error("destroy QP failed"); + return -EIO; + } + + return 0; +} + +int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr) { int i, k, ret; u64 hret, pt_abs, start, end, nr_pages; @@ -526,14 +568,14 @@ int ehea_reg_mr_adapter(struct ehea_adap hret = ehea_h_alloc_resource_mr(adapter->handle, start, end - start, acc_ctrl, adapter->pd, - &adapter->mr.handle, &adapter->mr.lkey); + &mr->handle, &mr->lkey); if (hret != H_SUCCESS) { ehea_error("alloc_resource_mr failed"); ret = -EIO; goto out; } - adapter->mr.vaddr = KERNELBASE; + mr->vaddr = KERNELBASE; k = 0; while (nr_pages > 0) { @@ -545,7 +587,7 @@ int ehea_reg_mr_adapter(struct ehea_adap EHEA_PAGESIZE))); hret = ehea_h_register_rpage_mr(adapter->handle, - adapter->mr.handle, 0, + mr->handle, 0, 0, (u64)pt_abs, num_pages); nr_pages -= num_pages; @@ -554,34 +596,68 @@ int ehea_reg_mr_adapter(struct ehea_adap (k * EHEA_PAGESIZE))); hret = ehea_h_register_rpage_mr(adapter->handle, - adapter->mr.handle, 0, + mr->handle, 0, 0, abs_adr,1); nr_pages--; } if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED)) { ehea_h_free_resource(adapter->handle, - adapter->mr.handle); - ehea_error("register_rpage_mr failed: hret = %lX", - hret); + mr->handle, FORCE_FREE); + ehea_error("register_rpage_mr failed"); ret = -EIO; goto out; } } if (hret != H_SUCCESS) { - ehea_h_free_resource(adapter->handle, adapter->mr.handle); - ehea_error("register_rpage failed for last page: hret = %lX", - hret); + ehea_h_free_resource(adapter->handle, mr->handle, + FORCE_FREE); + ehea_error("register_rpage failed for last page"); ret = -EIO; goto out; } + + mr->adapter = adapter; ret = 0; out: kfree(pt); return ret; } +int ehea_rem_mr(struct ehea_mr *mr) +{ + u64 hret; + + if (!mr || !mr->adapter) + return -EINVAL; + + hret = ehea_h_free_resource(mr->adapter->handle, mr->handle, + FORCE_FREE); + if (hret != H_SUCCESS) { + ehea_error("destroy MR failed"); + return -EIO; + } + + return 0; +} + +int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr, + struct ehea_mr *shared_mr) +{ + u64 hret; + + hret = ehea_h_register_smr(adapter->handle, old_mr->handle, + old_mr->vaddr, EHEA_MR_ACC_CTRL, + adapter->pd, shared_mr); + if (hret != H_SUCCESS) + return -EIO; + + shared_mr->adapter = adapter; + + return 0; +} + void print_error_data(u64 *data) { int length; @@ -597,6 +673,14 @@ void print_error_data(u64 *data) ehea_error("QP (resource=%lX) state: AER=0x%lX, AERR=0x%lX, " "port=%lX", resource, data[6], data[12], data[22]); + if (type == 0x4) /* Completion Queue */ + ehea_error("CQ (resource=%lX) state: AER=0x%lX", resource, + data[6]); + + if (type == 0x3) /* Event Queue */ + ehea_error("EQ (resource=%lX) state: AER=0x%lX", resource, + data[6]); + ehea_dump(data, length, "error data"); } diff --git a/drivers/net/ehea/ehea_qmr.h b/drivers/net/ehea/ehea_qmr.h index 1ff6098..c0eb3e0 100644 --- a/drivers/net/ehea/ehea_qmr.h +++ b/drivers/net/ehea/ehea_qmr.h @@ -142,6 +142,8 @@ #define EHEA_CQE_TYPE_RQ 0x60 #define EHEA_CQE_STAT_ERR_MASK 0x721F #define EHEA_CQE_STAT_FAT_ERR_MASK 0x1F #define EHEA_CQE_STAT_ERR_TCP 0x4000 +#define EHEA_CQE_STAT_ERR_IP 0x2000 +#define EHEA_CQE_STAT_ERR_CRC 0x1000 struct ehea_cqe { u64 wr_id; /* work request ID from WQE */ @@ -320,6 +322,11 @@ static inline struct ehea_cqe *ehea_poll return hw_qeit_get_valid(queue); } +static inline void ehea_inc_cq(struct ehea_cq *cq) +{ + hw_qeit_inc(&cq->hw_queue); +} + static inline void ehea_inc_rq1(struct ehea_qp *qp) { hw_qeit_inc(&qp->hw_rqueue1); @@ -327,7 +334,7 @@ static inline void ehea_inc_rq1(struct e static inline struct ehea_cqe *ehea_poll_cq(struct ehea_cq *my_cq) { - return hw_qeit_get_inc_valid(&my_cq->hw_queue); + return hw_qeit_get_valid(&my_cq->hw_queue); } #define EHEA_CQ_REGISTER_ORIG 0 @@ -356,7 +363,12 @@ struct ehea_qp *ehea_create_qp(struct eh int ehea_destroy_qp(struct ehea_qp *qp); -int ehea_reg_mr_adapter(struct ehea_adapter *adapter); +int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr); + +int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr, + struct ehea_mr *shared_mr); + +int ehea_rem_mr(struct ehea_mr *mr); void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle); diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 3a6a83d..4e3f14c 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -934,7 +934,6 @@ static void epic_init_ring(struct net_de ep->rx_skbuff[i] = skb; if (skb == NULL) break; - skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* 16 byte align the IP header. */ ep->rx_ring[i].bufaddr = pci_map_single(ep->pci_dev, skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE); @@ -1199,7 +1198,6 @@ static int epic_rx(struct net_device *de to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(ep->pci_dev, ep->rx_ring[entry].bufaddr, @@ -1236,7 +1234,6 @@ static int epic_rx(struct net_device *de skb = ep->rx_skbuff[entry] = dev_alloc_skb(ep->rx_buf_sz); if (skb == NULL) break; - skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ ep->rx_ring[entry].bufaddr = pci_map_single(ep->pci_dev, skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c index 93283e3..04abf59 100644 --- a/drivers/net/eth16i.c +++ b/drivers/net/eth16i.c @@ -1175,7 +1175,6 @@ static void eth16i_rx(struct net_device break; } - skb->dev = dev; skb_reserve(skb,2); /* diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c index 714ea11..cb0792c 100644 --- a/drivers/net/ewrk3.c +++ b/drivers/net/ewrk3.c @@ -993,7 +993,6 @@ static int ewrk3_rx(struct net_device *d if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) { unsigned char *p; - skb->dev = dev; skb_reserve(skb, 2); /* Align to 16 bytes */ p = skb_put(skb, pkt_len); diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 38a13f4..abe9b08 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -1719,7 +1719,6 @@ #endif to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, np->cur_rx->buffer, diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 6764281..255b091 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -647,7 +647,6 @@ #endif printk("%s: Memory squeeze, dropping packet.\n", dev->name); fep->stats.rx_dropped++; } else { - skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, data, pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c index 77f747a..e824d5d 100644 --- a/drivers/net/fec_8xx/fec_main.c +++ b/drivers/net/fec_8xx/fec_main.c @@ -551,7 +551,9 @@ static int fec_enet_rx_common(struct net skbn = dev_alloc_skb(pkt_len + 2); if (skbn != NULL) { skb_reserve(skbn, 2); /* align IP header */ - memcpy(skbn->data, skb->data, pkt_len); + skb_copy_from_linear_data(skb + skbn->data, + pkt_len); /* swap */ skbt = skb; skb = skbn; @@ -561,7 +563,6 @@ static int fec_enet_rx_common(struct net skbn = dev_alloc_skb(ENET_RX_FRSIZE); if (skbn != NULL) { - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ skb->protocol = eth_type_trans(skb, dev); received++; diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index d04214e..7a01802 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -1385,11 +1385,12 @@ static int nv_alloc_rx(struct net_device while (np->put_rx.orig != less_rx) { struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD); if (skb) { - skb->dev = dev; np->put_rx_ctx->skb = skb; - np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data, - skb->end-skb->data, PCI_DMA_FROMDEVICE); - np->put_rx_ctx->dma_len = skb->end-skb->data; + np->put_rx_ctx->dma = pci_map_single(np->pci_dev, + skb->data, + skb_tailroom(skb), + PCI_DMA_FROMDEVICE); + np->put_rx_ctx->dma_len = skb_tailroom(skb); np->put_rx.orig->buf = cpu_to_le32(np->put_rx_ctx->dma); wmb(); np->put_rx.orig->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL); @@ -1416,11 +1417,12 @@ static int nv_alloc_rx_optimized(struct while (np->put_rx.ex != less_rx) { struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD); if (skb) { - skb->dev = dev; np->put_rx_ctx->skb = skb; - np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data, - skb->end-skb->data, PCI_DMA_FROMDEVICE); - np->put_rx_ctx->dma_len = skb->end-skb->data; + np->put_rx_ctx->dma = pci_map_single(np->pci_dev, + skb->data, + skb_tailroom(skb), + PCI_DMA_FROMDEVICE); + np->put_rx_ctx->dma_len = skb_tailroom(skb); np->put_rx.ex->bufhigh = cpu_to_le64(np->put_rx_ctx->dma) >> 32; np->put_rx.ex->buflow = cpu_to_le64(np->put_rx_ctx->dma) & 0x0FFFFFFFF; wmb(); @@ -1604,8 +1606,9 @@ static void nv_drain_rx(struct net_devic wmb(); if (np->rx_skb[i].skb) { pci_unmap_single(np->pci_dev, np->rx_skb[i].dma, - np->rx_skb[i].skb->end-np->rx_skb[i].skb->data, - PCI_DMA_FROMDEVICE); + (skb_end_pointer(np->rx_skb[i].skb) - + np->rx_skb[i].skb->data), + PCI_DMA_FROMDEVICE); dev_kfree_skb(np->rx_skb[i].skb); np->rx_skb[i].skb = NULL; } @@ -4376,11 +4379,12 @@ static int nv_loopback_test(struct net_d ret = 0; goto out; } + test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data, + skb_tailroom(tx_skb), + PCI_DMA_FROMDEVICE); pkt_data = skb_put(tx_skb, pkt_len); for (i = 0; i < pkt_len; i++) pkt_data[i] = (u8)(i & 0xff); - test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data, - tx_skb->end-tx_skb->data, PCI_DMA_FROMDEVICE); if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->tx_ring.orig[0].buf = cpu_to_le32(test_dma_addr); @@ -4437,7 +4441,7 @@ static int nv_loopback_test(struct net_d } pci_unmap_page(np->pci_dev, test_dma_addr, - tx_skb->end-tx_skb->data, + (skb_end_pointer(tx_skb) - tx_skb->data), PCI_DMA_TODEVICE); dev_kfree_skb_any(tx_skb); out: diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index 4a05c14..e2ddd61 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -160,7 +160,8 @@ static int fs_enet_rx_napi(struct net_de skbn = dev_alloc_skb(pkt_len + 2); if (skbn != NULL) { skb_reserve(skbn, 2); /* align IP header */ - memcpy(skbn->data, skb->data, pkt_len); + skb_copy_from_linear_data(skb, + skbn->data, pkt_len); /* swap */ skbt = skb; skb = skbn; @@ -170,7 +171,6 @@ static int fs_enet_rx_napi(struct net_de skbn = dev_alloc_skb(ENET_RX_FRSIZE); if (skbn != NULL) { - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ skb->protocol = eth_type_trans(skb, dev); received++; @@ -294,7 +294,8 @@ static int fs_enet_rx_non_napi(struct ne skbn = dev_alloc_skb(pkt_len + 2); if (skbn != NULL) { skb_reserve(skbn, 2); /* align IP header */ - memcpy(skbn->data, skb->data, pkt_len); + skb_copy_from_linear_data(skb, + skbn->data, pkt_len); /* swap */ skbt = skb; skb = skbn; @@ -304,7 +305,6 @@ static int fs_enet_rx_non_napi(struct ne skbn = dev_alloc_skb(ENET_RX_FRSIZE); if (skbn != NULL) { - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ skb->protocol = eth_type_trans(skb, dev); received++; @@ -516,7 +516,6 @@ void fs_init_bds(struct net_device *dev) break; } fep->rx_skbuff[i] = skb; - skb->dev = dev; CBDW_BUFADDR(bdp, dma_map_single(fep->dev, skb->data, L1_CACHE_ALIGN(PKT_MAXBUF_SIZE), diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index d981d4c..b666a0c 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -942,18 +942,18 @@ static inline void gfar_tx_checksum(stru /* Tell the controller what the protocol is */ /* And provide the already calculated phcs */ - if (skb->nh.iph->protocol == IPPROTO_UDP) { + if (ip_hdr(skb)->protocol == IPPROTO_UDP) { flags |= TXFCB_UDP; - fcb->phcs = skb->h.uh->check; + fcb->phcs = udp_hdr(skb)->check; } else - fcb->phcs = skb->h.th->check; + fcb->phcs = udp_hdr(skb)->check; /* l3os is the distance between the start of the * frame (skb->data) and the start of the IP hdr. * l4os is the distance between the start of the * l3 hdr and the l4 hdr */ - fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN); - fcb->l4os = (u16)(skb->h.raw - skb->nh.raw); + fcb->l3os = (u16)(skb_network_offset(skb) - GMAC_FCB_LEN); + fcb->l4os = skb_network_header_len(skb); fcb->flags = flags; } @@ -1295,8 +1295,6 @@ struct sk_buff * gfar_new_skb(struct net */ skb_reserve(skb, alignamount); - skb->dev = dev; - bdp->bufPtr = dma_map_single(NULL, skb->data, priv->rx_buffer_size, DMA_FROM_DEVICE); diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index c3c0d67..2521b11 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -1568,7 +1568,6 @@ #ifdef RX_CHECKSUM printk(KERN_ERR "%s: rx_copybreak non-zero " "not good with RX_CHECKSUM\n", dev->name); #endif - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(hmp->pci_dev, hmp->rx_ring[entry].addr, diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index 30baf6e..17ac697 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -415,11 +415,18 @@ static int ser12_open(struct net_device if (!dev || !bc) return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || - dev->irq < 2 || dev->irq > 15) + if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT || + dev->irq < 2 || dev->irq > NR_IRQS) { + printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) " + "or irq (2 <= irq <= %d)\n", + 0xffff-SER12_EXTENT, NR_IRQS); return -ENXIO; - if (bc->baud < 300 || bc->baud > 4800) + } + if (bc->baud < 300 || bc->baud > 4800) { + printk(KERN_INFO "baycom_ser_fdx: invalid baudrate " + "(300...4800)\n"); return -EINVAL; + } if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) { printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy \n", dev->base_addr); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index d254269..656f278 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -282,7 +282,7 @@ static int bpq_xmit(struct sk_buff *skb, } skb->protocol = ax25_type_trans(skb, dev); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); bpq->stats.tx_packets++; bpq->stats.tx_bytes+=skb->len; diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 0fbb414..3be8c50 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -930,7 +930,7 @@ static int scc_send_packet(struct sk_buf /* Transfer data to DMA buffer */ i = priv->tx_head; - memcpy(priv->tx_buf[i], skb->data + 1, skb->len - 1); + skb_copy_from_linear_data_offset(skb, 1, priv->tx_buf[i], skb->len - 1); priv->tx_len[i] = skb->len - 1; /* Clear interrupts while we touch our circular buffers */ diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index f5a17ad..b33adc6 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -317,7 +317,9 @@ void hdlcdrv_transmitter(struct net_devi dev_kfree_skb_irq(skb); break; } - memcpy(s->hdlctx.buffer, skb->data+1, pkt_len); + skb_copy_from_linear_data_offset(skb, 1, + s->hdlctx.buffer, + pkt_len); dev_kfree_skb_irq(skb); s->hdlctx.bp = s->hdlctx.buffer; append_crc_ccitt(s->hdlctx.buffer, pkt_len); diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index ee3ea4f..467559d 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -638,7 +638,9 @@ static void yam_tx_byte(struct net_devic dev_kfree_skb_any(skb); break; } - memcpy(yp->tx_buf, skb->data + 1, yp->tx_len); + skb_copy_from_linear_data_offset(skb, 1, + yp->tx_buf, + yp->tx_len); dev_kfree_skb_any(skb); yp->tx_count = 0; yp->tx_crcl = 0x21; diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 7dc5185..8118a67 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -1816,7 +1816,6 @@ #endif u_char *ptr; skb_reserve(skb,2); - skb->dev = dev; /* ptr to start of the sk_buff data area */ skb_put(skb, pkt_len); diff --git a/drivers/net/ibm_emac/ibm_emac_core.c b/drivers/net/ibm_emac/ibm_emac_core.c index dd8ad87..3d82d46 100644 --- a/drivers/net/ibm_emac/ibm_emac_core.c +++ b/drivers/net/ibm_emac/ibm_emac_core.c @@ -1338,7 +1338,7 @@ static inline int emac_rx_sg_append(stru dev_kfree_skb(dev->rx_sg_skb); dev->rx_sg_skb = NULL; } else { - cacheable_memcpy(dev->rx_sg_skb->tail, + cacheable_memcpy(skb_tail_pointer(dev->rx_sg_skb), dev->rx_skb[slot]->data, len); skb_put(dev->rx_sg_skb, len); emac_recycle_rx_skb(dev, slot, len); @@ -1398,7 +1398,6 @@ static int emac_poll_rx(void *param, int skb_put(skb, len); push_packet: - skb->dev = dev->ndev; skb->protocol = eth_type_trans(skb, dev->ndev); emac_rx_csum(dev, skb, ctrl); diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c index 3f946c8..fe85d6f 100644 --- a/drivers/net/ibmlana.c +++ b/drivers/net/ibmlana.c @@ -601,7 +601,6 @@ static void irqrx_handler(struct net_dev /* set up skb fields */ - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 458db05..3bec0f7 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -93,7 +93,7 @@ static void ibmveth_proc_unregister_driv static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter); static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter); static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance); -static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter); +static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter); static struct kobj_type ktype_veth_pool; #ifdef CONFIG_PROC_FS @@ -389,7 +389,7 @@ static void ibmveth_rxq_recycle_buffer(s } } -static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) +static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) { ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator); @@ -798,7 +798,6 @@ static int ibmveth_poll(struct net_devic skb_reserve(skb, offset); skb_put(skb, length); - skb->dev = netdev; skb->protocol = eth_type_trans(skb, netdev); netif_receive_skb(skb); /* send it up */ @@ -954,14 +953,16 @@ static int __devinit ibmveth_probe(struc ibmveth_debug_printk_no_adapter("entering ibmveth_probe for UA 0x%x\n", dev->unit_address); - mac_addr_p = (unsigned char *) vio_get_attribute(dev, VETH_MAC_ADDR, 0); + mac_addr_p = (unsigned char *) vio_get_attribute(dev, + VETH_MAC_ADDR, NULL); if(!mac_addr_p) { printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find VETH_MAC_ADDR " "attribute\n", __FILE__, __LINE__); return 0; } - mcastFilterSize_p= (unsigned int *) vio_get_attribute(dev, VETH_MCAST_FILTER_SIZE, 0); + mcastFilterSize_p = (unsigned int *) vio_get_attribute(dev, + VETH_MCAST_FILTER_SIZE, NULL); if(!mcastFilterSize_p) { printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find " "VETH_MCAST_FILTER_SIZE attribute\n", diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c index 4ad7807..f749e07 100644 --- a/drivers/net/ioc3-eth.c +++ b/drivers/net/ioc3-eth.c @@ -633,8 +633,6 @@ #endif ip->rx_skbs[rx_entry] = NULL; /* Poison */ - new_skb->dev = priv_netdev(ip); - /* Because we reserve afterwards. */ skb_put(new_skb, (1664 + RX_OFFSET)); rxb = (struct ioc3_erxbuf *) new_skb->data; @@ -940,7 +938,6 @@ static void ioc3_alloc_rings(struct net_ } ip->rx_skbs[i] = skb; - skb->dev = dev; /* Because we reserve afterwards. */ skb_put(skb, (1664 + RX_OFFSET)); @@ -1396,9 +1393,9 @@ #ifdef CONFIG_SGI_IOC3_ETH_HW_TX_CSUM * manually. */ if (skb->ip_summed == CHECKSUM_PARTIAL) { - int proto = ntohs(skb->nh.iph->protocol); + const struct iphdr *ih = ip_hdr(skb); + const int proto = ntohs(ih->protocol); unsigned int csoff; - struct iphdr *ih = skb->nh.iph; uint32_t csum, ehsum; uint16_t *eh; @@ -1425,11 +1422,11 @@ #ifdef CONFIG_SGI_IOC3_ETH_HW_TX_CSUM csoff = ETH_HLEN + (ih->ihl << 2); if (proto == IPPROTO_UDP) { csoff += offsetof(struct udphdr, check); - skb->h.uh->check = csum; + udp_hdr(skb)->check = csum; } if (proto == IPPROTO_TCP) { csoff += offsetof(struct tcphdr, check); - skb->h.th->check = csum; + tcp_hdr(skb)->check = csum; } w0 = ETXD_DOCHECKSUM | (csoff << ETXD_CHKOFF_SHIFT); @@ -1446,7 +1443,7 @@ #endif /* CONFIG_SGI_IOC3_ETH_HW_TX_CSUM if (len <= 104) { /* Short packet, let's copy it directly into the ring. */ - memcpy(desc->data, skb->data, skb->len); + skb_copy_from_linear_data(skb, desc->data, skb->len); if (len < ETH_ZLEN) { /* Very short packet, pad with zeros at the end. */ memset(desc->data + len, 0, ETH_ZLEN - len); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index cebf8c3..f9c889c 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -1472,9 +1472,8 @@ static int ali_ircc_fir_hard_xmit(struct self->stats.tx_bytes += skb->len; - memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, - skb->len); - + skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, + skb->len); self->tx_fifo.len++; self->tx_fifo.free++; @@ -1924,7 +1923,7 @@ static int ali_ircc_dma_receive_complet /* Copy frame without CRC, CRC is removed by hardware*/ skb_put(skb, len); - memcpy(skb->data, self->rx_buff.data, len); + skb_copy_to_linear_data(skb, self->rx_buff.data, len); /* Move to next frame */ self->rx_buff.data += len; @@ -1932,7 +1931,7 @@ static int ali_ircc_dma_receive_complet self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); self->netdev->last_rx = jiffies; diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index 37914dc..4dbdfaa 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -526,7 +526,7 @@ #endif if (aup->speed == 4000000) { /* FIR */ - memcpy((void *)pDB->vaddr, skb->data, skb->len); + skb_copy_from_linear_data(skb, pDB->vaddr, skb->len); ptxd->count_0 = skb->len & 0xff; ptxd->count_1 = (skb->len >> 8) & 0xff; @@ -604,9 +604,9 @@ static int au1k_irda_rx(struct net_devic skb_put(skb, count); else skb_put(skb, count-2); - memcpy(skb->data, (void *)pDB->vaddr, count-2); + skb_copy_to_linear_data(skb, pDB->vaddr, count - 2); skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); prxd->count_0 = 0; diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c index 11af0ae..3ca47bf 100644 --- a/drivers/net/irda/donauboe.c +++ b/drivers/net/irda/donauboe.c @@ -1119,7 +1119,7 @@ #endif else { len = skb->len; - memcpy (self->tx_bufs[self->txs], skb->data, len); + skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len); } self->ring->tx[self->txs].len = len & 0x0fff; @@ -1282,11 +1282,11 @@ #endif skb_reserve (skb, 1); skb_put (skb, len); - memcpy (skb->data, self->rx_bufs[self->rxs], len); - + skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs], + len); self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons (ETH_P_IRDA); } else diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 1d510bd..0ac240c 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -441,7 +441,7 @@ static int irda_usb_hard_xmit(struct sk_ goto drop; } - memcpy(self->tx_buff + self->header_length, skb->data, skb->len); + skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len); /* Change setting for next frame */ if (self->capability & IUC_STIR421X) { @@ -902,7 +902,7 @@ static void irda_usb_receive(struct urb if(docopy) { /* Copy packet, so we can recycle the original */ - memcpy(newskb->data, skb->data, urb->actual_length); + skb_copy_from_linear_data(skb, newskb->data, urb->actual_length); /* Deliver this new skb */ dataskb = newskb; /* And hook the old skb to the URB @@ -921,7 +921,7 @@ static void irda_usb_receive(struct urb /* Ask the networking layer to queue the packet for the IrDA stack */ dataskb->dev = self->netdev; - dataskb->mac.raw = dataskb->data; + skb_reset_mac_header(dataskb); dataskb->protocol = htons(ETH_P_IRDA); len = dataskb->len; netif_rx(dataskb); diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index f0c61f3..0de8672 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -200,14 +200,14 @@ static inline int mcs_setup_transceiver_ /* Setup a communication between mcs7780 and agilent chip. */ static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs) { - IRDA_WARNING("This transceiver type is not supported yet."); + IRDA_WARNING("This transceiver type is not supported yet.\n"); return 1; } /* Setup a communication between mcs7780 and sharp chip. */ static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs) { - IRDA_WARNING("This transceiver type is not supported yet."); + IRDA_WARNING("This transceiver type is not supported yet.\n"); return 1; } @@ -279,7 +279,7 @@ static inline int mcs_setup_transceiver( break; default: - IRDA_WARNING("Unknown transceiver type: %d", + IRDA_WARNING("Unknown transceiver type: %d\n", mcs->transceiver_type); ret = 1; } @@ -318,7 +318,7 @@ static inline int mcs_setup_transceiver( return ret; error: - IRDA_ERROR("%s", msg); + IRDA_ERROR("%s\n", msg); return ret; } @@ -353,7 +353,7 @@ static unsigned mcs_wrap_fir_skb(const s buf[0] = len & 0xff; buf[1] = (len >> 8) & 0xff; /* copy the data into the tx buffer. */ - memcpy(buf+2, skb->data, skb->len); + skb_copy_from_linear_data(skb, buf + 2, skb->len); /* put the fcs in the last four bytes in little endian order. */ buf[len - 4] = fcs & 0xff; buf[len - 3] = (fcs >> 8) & 0xff; @@ -377,7 +377,7 @@ static unsigned mcs_wrap_mir_skb(const s buf[0] = len & 0xff; buf[1] = (len >> 8) & 0xff; /* copy the data */ - memcpy(buf+2, skb->data, skb->len); + skb_copy_from_linear_data(skb, buf + 2, skb->len); /* put the fcs in last two bytes in little endian order. */ buf[len - 2] = fcs & 0xff; buf[len - 1] = (fcs >> 8) & 0xff; @@ -426,9 +426,9 @@ static void mcs_unwrap_mir(struct mcs_cb } skb_reserve(skb, 1); - memcpy(skb->data, buf, new_len); + skb_copy_to_linear_data(skb, buf, new_len); skb_put(skb, new_len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); skb->dev = mcs->netdev; @@ -479,9 +479,9 @@ static void mcs_unwrap_fir(struct mcs_cb } skb_reserve(skb, 1); - memcpy(skb->data, buf, new_len); + skb_copy_to_linear_data(skb, buf, new_len); skb_put(skb, new_len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); skb->dev = mcs->netdev; @@ -587,7 +587,7 @@ static int mcs_speed_change(struct mcs_c } while(cnt++ < 100 && (rval & MCS_IRINTX)); if(cnt >= 100) { - IRDA_ERROR("unable to change speed"); + IRDA_ERROR("unable to change speed\n"); ret = -EIO; goto error; } @@ -638,7 +638,7 @@ static int mcs_speed_change(struct mcs_c default: ret = 1; - IRDA_WARNING("Unknown transceiver type: %d", + IRDA_WARNING("Unknown transceiver type: %d\n", mcs->transceiver_type); } if (unlikely(ret)) @@ -733,7 +733,7 @@ static int mcs_net_open(struct net_devic sprintf(hwname, "usb#%d", mcs->usbdev->devnum); mcs->irlap = irlap_open(netdev, &mcs->qos, hwname); if (!mcs->irlap) { - IRDA_ERROR("mcs7780: irlap_open failed"); + IRDA_ERROR("mcs7780: irlap_open failed\n"); goto error2; } @@ -862,7 +862,7 @@ static int mcs_hard_xmit(struct sk_buff mcs->out_buf, wraplen, mcs_send_irq, mcs); if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) { - IRDA_ERROR("failed tx_urb: %d", ret); + IRDA_ERROR("failed tx_urb: %d\n", ret); switch (ret) { case -ENODEV: case -EPIPE: @@ -897,7 +897,7 @@ static int mcs_probe(struct usb_interfac if (!ndev) goto error1; - IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.", udev->devnum); + IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum); /* what is it realy for? */ SET_MODULE_OWNER(ndev); @@ -905,7 +905,7 @@ static int mcs_probe(struct usb_interfac ret = usb_reset_configuration(udev); if (ret != 0) { - IRDA_ERROR("mcs7780: usb reset configuration failed"); + IRDA_ERROR("mcs7780: usb reset configuration failed\n"); goto error2; } @@ -950,7 +950,7 @@ static int mcs_probe(struct usb_interfac if (ret != 0) goto error2; - IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s", + IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s\n", ndev->name); mcs->transceiver_type = transceiver_type; @@ -981,7 +981,7 @@ static void mcs_disconnect(struct usb_in free_netdev(mcs->netdev); usb_set_intfdata(intf, NULL); - IRDA_DEBUG(0, "MCS7780 now disconnected."); + IRDA_DEBUG(0, "MCS7780 now disconnected.\n"); } /* Module insertion */ @@ -992,7 +992,7 @@ static int __init mcs_init(void) /* register this driver with the USB subsystem */ result = usb_register(&mcs_driver); if (result) - IRDA_ERROR("usb_register failed. Error number %d", result); + IRDA_ERROR("usb_register failed. Error number %d\n", result); return result; } diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 29b5ccd..d96c897 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1466,9 +1466,8 @@ static int nsc_ircc_hard_xmit_fir(struct self->stats.tx_bytes += skb->len; - memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, - skb->len); - + skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, + skb->len); self->tx_fifo.len++; self->tx_fifo.free++; @@ -1869,10 +1868,14 @@ static int nsc_ircc_dma_receive_complete /* Copy frame without CRC */ if (self->io.speed < 4000000) { skb_put(skb, len-2); - memcpy(skb->data, self->rx_buff.data, len-2); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 2); } else { skb_put(skb, len-4); - memcpy(skb->data, self->rx_buff.data, len-4); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 4); } /* Move to next frame */ @@ -1881,7 +1884,7 @@ static int nsc_ircc_dma_receive_complete self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); self->netdev->last_rx = jiffies; diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 2272156..fb196fd 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -386,12 +386,12 @@ static void pxa_irda_fir_irq_eif(struct /* Align IP header to 20 bytes */ skb_reserve(skb, 1); - memcpy(skb->data, si->dma_rx_buff, len); + skb_copy_to_linear_data(skb, si->dma_rx_buff, len); skb_put(skb, len); /* Feed it to IrLAP */ skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); @@ -484,7 +484,7 @@ static int pxa_irda_hard_xmit(struct sk_ unsigned long mtt = irda_get_mtt(skb); si->dma_tx_buff_len = skb->len; - memcpy(si->dma_tx_buff, skb->data, skb->len); + skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len); if (mtt) while ((unsigned)(OSCR - si->last_oscr)/4 < mtt) diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index 937372d..056639f 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -504,7 +504,7 @@ static void sa1100_irda_fir_error(struct skb_put(skb, len); skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); si->stats.rx_packets++; si->stats.rx_bytes += len; diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index 31c6233..198bf3b 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -315,6 +315,7 @@ static struct smsc_chip __initdata lpc_c { /* Base address 0x2E or 0x4E */ { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 }, + { "47N227", KEY55_1|FIR|SERx4, 0x7a, 0x00 }, { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 }, { NULL } }; @@ -1161,7 +1162,7 @@ static int smsc_ircc_hard_xmit_fir(struc self->new_speed = speed; } - memcpy(self->tx_buff.head, skb->data, skb->len); + skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len); self->tx_buff.len = skb->len; self->tx_buff.data = self->tx_buff.head; @@ -1412,7 +1413,7 @@ #endif self->stats.rx_bytes += len; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); } diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 20d306f..755aa44 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -52,7 +52,6 @@ #include #include #include #include -#include #include #include #include @@ -349,7 +348,7 @@ static void fir_eof(struct stir_cb *stir } skb_reserve(nskb, 1); skb = nskb; - memcpy(nskb->data, rx_buff->data, len); + skb_copy_to_linear_data(nskb, rx_buff->data, len); } else { nskb = dev_alloc_skb(rx_buff->truesize); if (unlikely(!nskb)) { @@ -364,7 +363,7 @@ static void fir_eof(struct stir_cb *stir skb_put(skb, len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); skb->dev = stir->netdev; diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index c3ed9b3..ff53585 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -925,8 +925,8 @@ static int via_ircc_hard_xmit_fir(struct self->tx_fifo.tail += skb->len; self->stats.tx_bytes += skb->len; - memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, - skb->len); + skb_copy_from_linear_data(skb, + self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); self->tx_fifo.len++; self->tx_fifo.free++; //F01 if (self->tx_fifo.len == 1) { @@ -1125,7 +1125,7 @@ static int via_ircc_dma_receive_complete self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); return TRUE; @@ -1189,7 +1189,7 @@ F01_E */ skb_reserve(skb, 1); skb_put(skb, len - 4); - memcpy(skb->data, self->rx_buff.data, len - 4); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); IRDA_DEBUG(2, "%s(): len=%x.rx_buff=%p\n", __FUNCTION__, len - 4, self->rx_buff.data); @@ -1198,7 +1198,7 @@ F01_E */ self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); @@ -1234,7 +1234,7 @@ static int upload_rxdata(struct via_ircc } skb_reserve(skb, 1); skb_put(skb, len - 4 + 1); - memcpy(skb->data, self->rx_buff.data, len - 4 + 1); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1); st_fifo->tail++; st_fifo->len++; if (st_fifo->tail > MAX_RX_WINDOW) @@ -1244,7 +1244,7 @@ static int upload_rxdata(struct via_ircc self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); if (st_fifo->len < (MAX_RX_WINDOW + 2)) { @@ -1303,7 +1303,7 @@ static int RxTimerHandler(struct via_irc } skb_reserve(skb, 1); skb_put(skb, len - 4); - memcpy(skb->data, self->rx_buff.data, len - 4); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); IRDA_DEBUG(2, "%s(): len=%x.head=%x\n", __FUNCTION__, len - 4, st_fifo->head); @@ -1313,7 +1313,7 @@ static int RxTimerHandler(struct via_irc self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); } //while diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index 3457e9d..c4be973 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -595,7 +595,7 @@ static int vlsi_process_rx(struct vlsi_r rd->skb = NULL; skb->dev = ndev; memcpy(skb_put(skb,len), rd->buf, len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); if (in_interrupt()) netif_rx(skb); else @@ -993,7 +993,7 @@ static int vlsi_hard_start_xmit(struct s goto drop; } else - memcpy(rd->buf, skb->data, len); + skb_copy_from_linear_data(skb, rd->buf, len); } rd->skb = skb; /* remember skb for tx-complete stats */ diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 4212657..5182e80 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -529,7 +529,7 @@ int w83977af_hard_xmit(struct sk_buff *s /* Decide if we should use PIO or DMA transfer */ if (self->io.speed > PIO_MAX_SPEED) { self->tx_buff.data = self->tx_buff.head; - memcpy(self->tx_buff.data, skb->data, skb->len); + skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len); self->tx_buff.len = skb->len; mtt = irda_get_mtt(skb); @@ -908,10 +908,14 @@ #endif /* Copy frame without CRC */ if (self->io.speed < 4000000) { skb_put(skb, len-2); - memcpy(skb->data, self->rx_buff.data, len-2); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 2); } else { skb_put(skb, len-4); - memcpy(skb->data, self->rx_buff.data, len-4); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 4); } /* Move to next frame */ @@ -919,7 +923,7 @@ #endif self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); self->netdev->last_rx = jiffies; diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 0e9ba3c..347d50c 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -1540,7 +1540,6 @@ static void veth_receive(struct veth_lpa } skb_put(skb, length); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; netif_rx(skb); /* send it up */ diff --git a/drivers/net/ixgb/ixgb.h b/drivers/net/ixgb/ixgb.h index cf30a10..c8e9086 100644 --- a/drivers/net/ixgb/ixgb.h +++ b/drivers/net/ixgb/ixgb.h @@ -111,9 +111,6 @@ #define IXGB_RXBUFFER_16384 16384 /* How many Rx Buffers do we bundle into one write to the hardware ? */ #define IXGB_RX_BUFFER_WRITE 8 /* Must be power of 2 */ -/* only works for sizes that are powers of 2 */ -#define IXGB_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1))) - /* wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct ixgb_buffer { diff --git a/drivers/net/ixgb/ixgb_ethtool.c b/drivers/net/ixgb/ixgb_ethtool.c index d6628bd..afde848 100644 --- a/drivers/net/ixgb/ixgb_ethtool.c +++ b/drivers/net/ixgb/ixgb_ethtool.c @@ -577,11 +577,11 @@ ixgb_set_ringparam(struct net_device *ne rxdr->count = max(ring->rx_pending,(uint32_t)MIN_RXD); rxdr->count = min(rxdr->count,(uint32_t)MAX_RXD); - IXGB_ROUNDUP(rxdr->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); + rxdr->count = ALIGN(rxdr->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); txdr->count = max(ring->tx_pending,(uint32_t)MIN_TXD); txdr->count = min(txdr->count,(uint32_t)MAX_TXD); - IXGB_ROUNDUP(txdr->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); + txdr->count = ALIGN(txdr->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); if(netif_running(adapter->netdev)) { /* Try to get new resources before deleting old */ diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c index afc2ec7..6d2b059 100644 --- a/drivers/net/ixgb/ixgb_main.c +++ b/drivers/net/ixgb/ixgb_main.c @@ -685,7 +685,7 @@ ixgb_setup_tx_resources(struct ixgb_adap /* round up to nearest 4K */ txdr->size = txdr->count * sizeof(struct ixgb_tx_desc); - IXGB_ROUNDUP(txdr->size, 4096); + txdr->size = ALIGN(txdr->size, 4096); txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma); if(!txdr->desc) { @@ -774,7 +774,7 @@ ixgb_setup_rx_resources(struct ixgb_adap /* Round up to nearest 4K */ rxdr->size = rxdr->count * sizeof(struct ixgb_rx_desc); - IXGB_ROUNDUP(rxdr->size, 4096); + rxdr->size = ALIGN(rxdr->size, 4096); rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma); @@ -1182,24 +1182,27 @@ ixgb_tso(struct ixgb_adapter *adapter, s if (likely(skb_is_gso(skb))) { struct ixgb_buffer *buffer_info; + struct iphdr *iph; + if (skb_header_cloned(skb)) { err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (err) return err; } - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); mss = skb_shinfo(skb)->gso_size; - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; - skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, IPPROTO_TCP, 0); - ipcss = skb->nh.raw - skb->data; - ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data; - ipcse = skb->h.raw - skb->data - 1; - tucss = skb->h.raw - skb->data; - tucso = (void *)&(skb->h.th->check) - (void *)skb->data; + iph = ip_hdr(skb); + iph->tot_len = 0; + iph->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, 0); + ipcss = skb_network_offset(skb); + ipcso = (void *)&(iph->check) - (void *)skb->data; + ipcse = skb_transport_offset(skb) - 1; + tucss = skb_transport_offset(skb); + tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data; tucse = 0; i = adapter->tx_ring.next_to_use; @@ -1243,7 +1246,7 @@ ixgb_tx_csum(struct ixgb_adapter *adapte if(likely(skb->ip_summed == CHECKSUM_PARTIAL)) { struct ixgb_buffer *buffer_info; - css = skb->h.raw - skb->data; + css = skb_transport_offset(skb); cso = css + skb->csum_offset; i = adapter->tx_ring.next_to_use; @@ -2014,9 +2017,12 @@ #define IXGB_CB_LENGTH 256 netdev_alloc_skb(netdev, length + NET_IP_ALIGN); if (new_skb) { skb_reserve(new_skb, NET_IP_ALIGN); - memcpy(new_skb->data - NET_IP_ALIGN, - skb->data - NET_IP_ALIGN, - length + NET_IP_ALIGN); + skb_copy_to_linear_data_offset(new_skb, + -NET_IP_ALIGN, + (skb->data - + NET_IP_ALIGN), + (length + + NET_IP_ALIGN)); /* save the skb in buffer_info as good */ buffer_info->skb = skb; skb = new_skb; diff --git a/drivers/net/ixgb/ixgb_param.c b/drivers/net/ixgb/ixgb_param.c index b27442a..5d5ddab 100644 --- a/drivers/net/ixgb/ixgb_param.c +++ b/drivers/net/ixgb/ixgb_param.c @@ -245,8 +245,6 @@ ixgb_validate_option(int *value, struct return -1; } -#define LIST_LEN(l) (sizeof(l) / sizeof(l[0])) - /** * ixgb_check_options - Range Checking for Command Line Parameters * @adapter: board private structure @@ -284,7 +282,7 @@ ixgb_check_options(struct ixgb_adapter * } else { tx_ring->count = opt.def; } - IXGB_ROUNDUP(tx_ring->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); + tx_ring->count = ALIGN(tx_ring->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); } { /* Receive Descriptor Count */ struct ixgb_option opt = { @@ -303,7 +301,7 @@ ixgb_check_options(struct ixgb_adapter * } else { rx_ring->count = opt.def; } - IXGB_ROUNDUP(rx_ring->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); + rx_ring->count = ALIGN(rx_ring->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); } { /* Receive Checksum Offload Enable */ struct ixgb_option opt = { @@ -335,7 +333,7 @@ ixgb_check_options(struct ixgb_adapter * .name = "Flow Control", .err = "reading default settings from EEPROM", .def = ixgb_fc_tx_pause, - .arg = { .l = { .nr = LIST_LEN(fc_list), + .arg = { .l = { .nr = ARRAY_SIZE(fc_list), .p = fc_list }} }; diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c index a4eccb1..6683afc 100644 --- a/drivers/net/ixp2000/ixpdev.c +++ b/drivers/net/ixp2000/ixpdev.c @@ -110,11 +110,10 @@ static int ixpdev_rx(struct net_device * skb = dev_alloc_skb(desc->pkt_length + 2); if (likely(skb != NULL)) { - skb->dev = nds[desc->channel]; skb_reserve(skb, 2); eth_copy_and_sum(skb, buf, desc->pkt_length, 0); skb_put(skb, desc->pkt_length); - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, nds[desc->channel]); skb->dev->last_rx = jiffies; diff --git a/drivers/net/lance.c b/drivers/net/lance.c index a384332..0fe96c8 100644 --- a/drivers/net/lance.c +++ b/drivers/net/lance.c @@ -988,7 +988,7 @@ static int lance_start_xmit(struct sk_bu if (lance_debug > 5) printk("%s: bouncing a high-memory packet (%#x).\n", dev->name, (u32)isa_virt_to_bus(skb->data)); - memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len); + skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len); lp->tx_ring[entry].base = ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000; dev_kfree_skb(skb); @@ -1184,7 +1184,6 @@ lance_rx(struct net_device *dev) } break; } - skb->dev = dev; skb_reserve(skb,2); /* 16 byte align */ skb_put(skb,pkt_len); /* Make room */ eth_copy_and_sum(skb, diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c index 452863d..0edcd12 100644 --- a/drivers/net/lasi_82596.c +++ b/drivers/net/lasi_82596.c @@ -801,7 +801,6 @@ memory_squeeze: lp->stats.rx_dropped++; } else { - skb->dev = dev; if (!rx_in_place) { /* 16 byte align the data fields */ dma_sync_single_for_cpu(lp->dev, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE); diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c index e726c06..5c86e73 100644 --- a/drivers/net/lib8390.c +++ b/drivers/net/lib8390.c @@ -722,7 +722,6 @@ static void ei_receive(struct net_device else { skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); skb->protocol=eth_type_trans(skb,dev); diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 2b739fd..6ba6ed2 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -75,8 +75,9 @@ #define LOOPBACK_OVERHEAD (128 + MAX_HEA #ifdef LOOPBACK_TSO static void emulate_large_send_offload(struct sk_buff *skb) { - struct iphdr *iph = skb->nh.iph; - struct tcphdr *th = (struct tcphdr*)(skb->nh.raw + (iph->ihl * 4)); + struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = (struct tcphdr *)(skb_network_header(skb) + + (iph->ihl * 4)); unsigned int doffset = (iph->ihl + th->doff) * 4; unsigned int mtu = skb_shinfo(skb)->gso_size + doffset; unsigned int offset = 0; @@ -90,10 +91,11 @@ static void emulate_large_send_offload(s if (!nskb) break; skb_reserve(nskb, 32); - nskb->mac.raw = nskb->data - 14; - nskb->nh.raw = nskb->data; - iph = nskb->nh.iph; - memcpy(nskb->data, skb->nh.raw, doffset); + skb_set_mac_header(nskb, -ETH_HLEN); + skb_reset_network_header(nskb); + iph = ip_hdr(nskb); + skb_copy_to_linear_data(nskb, skb_network_header(skb), + doffset); if (skb_copy_bits(skb, doffset + offset, nskb->data + doffset, @@ -108,7 +110,7 @@ static void emulate_large_send_offload(s memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); nskb->pkt_type = skb->pkt_type; - th = (struct tcphdr*)(nskb->nh.raw + iph->ihl*4); + th = (struct tcphdr *)(skb_network_header(nskb) + iph->ihl * 4); iph->tot_len = htons(frag_size + doffset); iph->id = htons(id); iph->check = 0; @@ -137,7 +139,6 @@ static int loopback_xmit(struct sk_buff skb_orphan(skb); skb->protocol = eth_type_trans(skb,dev); - skb->dev = dev; #ifndef LOOPBACK_MUST_CHECKSUM skb->ip_summed = CHECKSUM_UNNECESSARY; #endif @@ -145,7 +146,7 @@ #endif #ifdef LOOPBACK_TSO if (skb_is_gso(skb)) { BUG_ON(skb->protocol != htons(ETH_P_IP)); - BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); + BUG_ON(ip_hdr(skb)->protocol != IPPROTO_TCP); emulate_large_send_offload(skb); return 0; @@ -163,11 +164,9 @@ #endif return 0; } -static struct net_device_stats loopback_stats; - static struct net_device_stats *get_stats(struct net_device *dev) { - struct net_device_stats *stats = &loopback_stats; + struct net_device_stats *stats = &dev->stats; unsigned long bytes = 0; unsigned long packets = 0; int i; @@ -207,7 +206,6 @@ static const struct ethtool_ops loopback struct net_device loopback_dev = { .name = "lo", .get_stats = &get_stats, - .priv = &loopback_stats, .mtu = (16 * 1024) + 20 + 20 + 12, .hard_start_xmit = loopback_xmit, .hard_header = eth_header, diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c index 177c502..5fc18da 100644 --- a/drivers/net/lp486e.c +++ b/drivers/net/lp486e.c @@ -676,7 +676,6 @@ i596_rx_one(struct net_device *dev, stru return 1; } - skb->dev = dev; memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len); skb->protocol = eth_type_trans(skb,dev); diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c index e960138..90e695d 100644 --- a/drivers/net/mac89x0.c +++ b/drivers/net/mac89x0.c @@ -530,7 +530,6 @@ net_rx(struct net_device *dev) return; } skb_put(skb, length); - skb->dev = dev; memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 2e9571b..0e04f7a 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -357,7 +357,6 @@ static int macb_rx_frame(struct macb *bp } skb_reserve(skb, RX_OFFSET); - skb->dev = bp->dev; skb->ip_summed = CHECKSUM_NONE; skb_put(skb, len); @@ -368,9 +367,10 @@ static int macb_rx_frame(struct macb *bp BUG_ON(frag != last_frag); frag_len = len - offset; } - memcpy(skb->data + offset, - bp->rx_buffers + (RX_BUFFER_SIZE * frag), - frag_len); + skb_copy_to_linear_data_offset(skb, offset, + (bp->rx_buffers + + (RX_BUFFER_SIZE * frag)), + frag_len); offset += RX_BUFFER_SIZE; bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED); wmb(); @@ -576,7 +576,8 @@ #ifdef DEBUG int i; dev_dbg(&bp->pdev->dev, "start_xmit: len %u head %p data %p tail %p end %p\n", - skb->len, skb->head, skb->data, skb->tail, skb->end); + skb->len, skb->head, skb->data, + skb_tail_pointer(skb), skb_end_pointer(skb)); dev_dbg(&bp->pdev->dev, "data:"); for (i = 0; i < 16; i++) diff --git a/drivers/net/mace.c b/drivers/net/mace.c index 9ec24f0..b3bd623 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -939,7 +939,6 @@ static irqreturn_t mace_rxdma_intr(int i else /* Ethernet header; mace includes FCS */ nb -= 8; skb_put(skb, nb); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); mp->stats.rx_bytes += skb->len; netif_rx(skb); diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c index 5d541e8..27911c0 100644 --- a/drivers/net/macmace.c +++ b/drivers/net/macmace.c @@ -420,8 +420,7 @@ static int mace_xmit_start(struct sk_buf mp->stats.tx_bytes += skb->len; /* We need to copy into our xmit buffer to take care of alignment and caching issues */ - - memcpy((void *) mp->tx_ring, skb->data, skb->len); + skb_copy_from_linear_data(skb, mp->tx_ring, skb->len); /* load the Tx DMA and fire it off */ @@ -621,7 +620,6 @@ static void mace_dma_rx_frame(struct net skb_reserve(skb,2); memcpy(skb_put(skb, mf->len), mf->data, mf->len); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/meth.c b/drivers/net/meth.c index 7e69ca6..0343ea1 100644 --- a/drivers/net/meth.c +++ b/drivers/net/meth.c @@ -421,7 +421,6 @@ #endif /* Write metadata, and then pass to the receive level */ skb_put(skb_c, len); priv->rx_skbs[priv->rx_write] = skb; - skb_c->dev = dev; skb_c->protocol = eth_type_trans(skb_c, dev); dev->last_rx = jiffies; priv->stats.rx_packets++; @@ -609,7 +608,7 @@ static void meth_tx_short_prepare(struct desc->header.raw = METH_TX_CMD_INT_EN | (len-1) | ((128-len) << 16); /* maybe I should set whole thing to 0 first... */ - memcpy(desc->data.dt + (120 - len), skb->data, skb->len); + skb_copy_from_linear_data(skb, desc->data.dt + (120 - len), skb->len); if (skb->len < len) memset(desc->data.dt + 120 - len + skb->len, 0, len-skb->len); } @@ -627,8 +626,8 @@ static void meth_tx_1page_prepare(struct /* unaligned part */ if (unaligned_len) { - memcpy(desc->data.dt + (120 - unaligned_len), - skb->data, unaligned_len); + skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len), + unaligned_len); desc->header.raw |= (128 - unaligned_len) << 16; } @@ -653,8 +652,8 @@ static void meth_tx_2page_prepare(struct desc->header.raw = METH_TX_CMD_INT_EN | TX_CATBUF1 | TX_CATBUF2| (skb->len - 1); /* unaligned part */ if (unaligned_len){ - memcpy(desc->data.dt + (120 - unaligned_len), - skb->data, unaligned_len); + skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len), + unaligned_len); desc->header.raw |= (128 - unaligned_len) << 16; } diff --git a/drivers/net/mii.c b/drivers/net/mii.c index 2912a34..9205605 100644 --- a/drivers/net/mii.c +++ b/drivers/net/mii.c @@ -33,6 +33,13 @@ #include #include #include +/** + * mii_ethtool_gset - get settings that are specified in @ecmd + * @mii: MII interface + * @ecmd: requested ethtool_cmd + * + * Returns 0 for success, negative on error. + */ int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) { struct net_device *dev = mii->dev; @@ -114,6 +121,13 @@ int mii_ethtool_gset(struct mii_if_info return 0; } +/** + * mii_ethtool_sset - set settings that are specified in @ecmd + * @mii: MII interface + * @ecmd: requested ethtool_cmd + * + * Returns 0 for success, negative on error. + */ int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) { struct net_device *dev = mii->dev; @@ -207,6 +221,10 @@ int mii_ethtool_sset(struct mii_if_info return 0; } +/** + * mii_check_gmii_support - check if the MII supports Gb interfaces + * @mii: the MII interface + */ int mii_check_gmii_support(struct mii_if_info *mii) { int reg; @@ -221,6 +239,12 @@ int mii_check_gmii_support(struct mii_if return 0; } +/** + * mii_link_ok - is link status up/ok + * @mii: the MII interface + * + * Returns 1 if the MII reports link status up/ok, 0 otherwise. + */ int mii_link_ok (struct mii_if_info *mii) { /* first, a dummy read, needed to latch some MII phys */ @@ -230,6 +254,12 @@ int mii_link_ok (struct mii_if_info *mii return 0; } +/** + * mii_nway_restart - restart NWay (autonegotiation) for this interface + * @mii: the MII interface + * + * Returns 0 on success, negative on error. + */ int mii_nway_restart (struct mii_if_info *mii) { int bmcr; @@ -247,6 +277,14 @@ int mii_nway_restart (struct mii_if_info return r; } +/** + * mii_check_link - check MII link status + * @mii: MII interface + * + * If the link status changed (previous != current), call + * netif_carrier_on() if current link status is Up or call + * netif_carrier_off() if current link status is Down. + */ void mii_check_link (struct mii_if_info *mii) { int cur_link = mii_link_ok(mii); @@ -258,6 +296,15 @@ void mii_check_link (struct mii_if_info netif_carrier_off(mii->dev); } +/** + * mii_check_media - check the MII interface for a duplex change + * @mii: the MII interface + * @ok_to_print: OK to print link up/down messages + * @init_media: OK to save duplex mode in @mii + * + * Returns 1 if the duplex mode changed, 0 if not. + * If the media type is forced, always returns 0. + */ unsigned int mii_check_media (struct mii_if_info *mii, unsigned int ok_to_print, unsigned int init_media) @@ -326,6 +373,16 @@ unsigned int mii_check_media (struct mii return 0; /* duplex did not change */ } +/** + * generic_mii_ioctl - main MII ioctl interface + * @mii_if: the MII interface + * @mii_data: MII ioctl data structure + * @cmd: MII ioctl command + * @duplex_chg_out: pointer to @duplex_changed status if there was no + * ioctl error + * + * Returns 0 on success, negative on error. + */ int generic_mii_ioctl(struct mii_if_info *mii_if, struct mii_ioctl_data *mii_data, int cmd, unsigned int *duplex_chg_out) diff --git a/drivers/net/mipsnet.c b/drivers/net/mipsnet.c index f42b9e2..638a279 100644 --- a/drivers/net/mipsnet.c +++ b/drivers/net/mipsnet.c @@ -26,8 +26,6 @@ struct mipsnet_priv { struct net_device_stats stats; }; -static struct platform_device *mips_plat_dev; - static char mipsnet_string[] = "mipsnet"; /* @@ -101,7 +99,6 @@ static inline ssize_t mipsnet_get_fromde if (ioiocpy_frommipsnet(dev, skb_put(skb, len), len)) return -EFAULT; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -298,64 +295,17 @@ static struct device_driver mipsnet_driv .remove = __devexit_p(mipsnet_device_remove), }; -static void mipsnet_platform_release(struct device *device) -{ - struct platform_device *pldev; - - /* free device */ - pldev = to_platform_device(device); - kfree(pldev); -} - static int __init mipsnet_init_module(void) { - struct platform_device *pldev; int err; printk(KERN_INFO "MIPSNet Ethernet driver. Version: %s. " "(c)2005 MIPS Technologies, Inc.\n", MIPSNET_VERSION); - if (driver_register(&mipsnet_driver)) { + err = driver_register(&mipsnet_driver); + if (err) printk(KERN_ERR "Driver registration failed\n"); - err = -ENODEV; - goto out; - } - - if (!(pldev = kmalloc (sizeof (*pldev), GFP_KERNEL))) { - err = -ENOMEM; - goto out_unregister_driver; - } - - memset (pldev, 0, sizeof (*pldev)); - pldev->name = mipsnet_string; - pldev->id = 0; - pldev->dev.release = mipsnet_platform_release; - if (platform_device_register(pldev)) { - err = -ENODEV; - goto out_free_pldev; - } - - if (!pldev->dev.driver) { - /* - * The driver was not bound to this device, there was - * no hardware at this address. Unregister it, as the - * release fuction will take care of freeing the - * allocated structure - */ - platform_device_unregister (pldev); - } - - mips_plat_dev = pldev; - - return 0; - -out_free_pldev: - kfree(pldev); - -out_unregister_driver: - driver_unregister(&mipsnet_driver); -out: return err; } diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 8015a7c..1799eee 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -51,8 +51,8 @@ #include #include "mv643xx_eth.h" /* Static function declarations */ -static void eth_port_uc_addr_get(struct net_device *dev, - unsigned char *MacAddr); +static void eth_port_uc_addr_get(unsigned int port_num, unsigned char *p_addr); +static void eth_port_uc_addr_set(unsigned int port_num, unsigned char *p_addr); static void eth_port_set_multicast_list(struct net_device *); static void mv643xx_eth_port_enable_tx(unsigned int port_num, unsigned int queues); @@ -434,7 +434,6 @@ static int mv643xx_eth_receive_queue(str * received packet */ skb_put(skb, pkt_info.byte_cnt - 4); - skb->dev = dev; if (pkt_info.cmd_sts & ETH_LAYER_4_CHECKSUM_OK) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1162,15 +1161,15 @@ static void eth_tx_submit_descs_for_skb( cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM | ETH_GEN_IP_V_4_CHECKSUM | - skb->nh.iph->ihl << ETH_TX_IHL_SHIFT; + ip_hdr(skb)->ihl << ETH_TX_IHL_SHIFT; - switch (skb->nh.iph->protocol) { + switch (ip_hdr(skb)->protocol) { case IPPROTO_UDP: cmd_sts |= ETH_UDP_FRAME; - desc->l4i_chk = skb->h.uh->check; + desc->l4i_chk = udp_hdr(skb)->check; break; case IPPROTO_TCP: - desc->l4i_chk = skb->h.th->check; + desc->l4i_chk = tcp_hdr(skb)->check; break; default: BUG(); @@ -1382,7 +1381,7 @@ #endif port_num = mp->port_num = pd->port_number; /* set default config values */ - eth_port_uc_addr_get(dev, dev->dev_addr); + eth_port_uc_addr_get(port_num, dev->dev_addr); mp->rx_ring_size = MV643XX_ETH_PORT_DEFAULT_RECEIVE_QUEUE_SIZE; mp->tx_ring_size = MV643XX_ETH_PORT_DEFAULT_TRANSMIT_QUEUE_SIZE; @@ -1840,26 +1839,9 @@ static void eth_port_start(struct net_de } /* - * eth_port_uc_addr_set - This function Set the port Unicast address. - * - * DESCRIPTION: - * This function Set the port Ethernet MAC address. - * - * INPUT: - * unsigned int eth_port_num Port number. - * char * p_addr Address to be set - * - * OUTPUT: - * Set MAC address low and high registers. also calls - * eth_port_set_filter_table_entry() to set the unicast - * table with the proper information. - * - * RETURN: - * N/A. - * + * eth_port_uc_addr_set - Write a MAC address into the port's hw registers */ -static void eth_port_uc_addr_set(unsigned int eth_port_num, - unsigned char *p_addr) +static void eth_port_uc_addr_set(unsigned int port_num, unsigned char *p_addr) { unsigned int mac_h; unsigned int mac_l; @@ -1869,40 +1851,24 @@ static void eth_port_uc_addr_set(unsigne mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) | (p_addr[2] << 8) | (p_addr[3] << 0); - mv_write(MV643XX_ETH_MAC_ADDR_LOW(eth_port_num), mac_l); - mv_write(MV643XX_ETH_MAC_ADDR_HIGH(eth_port_num), mac_h); + mv_write(MV643XX_ETH_MAC_ADDR_LOW(port_num), mac_l); + mv_write(MV643XX_ETH_MAC_ADDR_HIGH(port_num), mac_h); - /* Accept frames of this address */ - table = MV643XX_ETH_DA_FILTER_UNICAST_TABLE_BASE(eth_port_num); + /* Accept frames with this address */ + table = MV643XX_ETH_DA_FILTER_UNICAST_TABLE_BASE(port_num); eth_port_set_filter_table_entry(table, p_addr[5] & 0x0f); } /* - * eth_port_uc_addr_get - This function retrieves the port Unicast address - * (MAC address) from the ethernet hw registers. - * - * DESCRIPTION: - * This function retrieves the port Ethernet MAC address. - * - * INPUT: - * unsigned int eth_port_num Port number. - * char *MacAddr pointer where the MAC address is stored - * - * OUTPUT: - * Copy the MAC address to the location pointed to by MacAddr - * - * RETURN: - * N/A. - * + * eth_port_uc_addr_get - Read the MAC address from the port's hw registers */ -static void eth_port_uc_addr_get(struct net_device *dev, unsigned char *p_addr) +static void eth_port_uc_addr_get(unsigned int port_num, unsigned char *p_addr) { - struct mv643xx_private *mp = netdev_priv(dev); unsigned int mac_h; unsigned int mac_l; - mac_h = mv_read(MV643XX_ETH_MAC_ADDR_HIGH(mp->port_num)); - mac_l = mv_read(MV643XX_ETH_MAC_ADDR_LOW(mp->port_num)); + mac_h = mv_read(MV643XX_ETH_MAC_ADDR_HIGH(port_num)); + mac_l = mv_read(MV643XX_ETH_MAC_ADDR_LOW(port_num)); p_addr[0] = (mac_h >> 24) & 0xff; p_addr[1] = (mac_h >> 16) & 0xff; diff --git a/drivers/net/mv643xx_eth.h b/drivers/net/mv643xx_eth.h index 7d4e90c..82f8c0c 100644 --- a/drivers/net/mv643xx_eth.h +++ b/drivers/net/mv643xx_eth.h @@ -346,10 +346,6 @@ static void eth_port_init(struct mv643xx static void eth_port_reset(unsigned int eth_port_num); static void eth_port_start(struct net_device *dev); -/* Port MAC address routines */ -static void eth_port_uc_addr_set(unsigned int eth_port_num, - unsigned char *p_addr); - /* PHY and MIB routines */ static void ethernet_phy_reset(unsigned int eth_port_num); diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index f8efe0e..16e3c43 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -879,7 +879,7 @@ myri10ge_rx_skb_build(struct sk_buff *sk * skb_pull() (for ether_pad and eth_type_trans()) requires * the beginning of the packet in skb_headlen(), move it * manually */ - memcpy(skb->data, va, hlen); + skb_copy_to_linear_data(skb, va, hlen); skb_shinfo(skb)->frags[0].page_offset += hlen; skb_shinfo(skb)->frags[0].size -= hlen; skb->data_len -= hlen; @@ -1020,7 +1020,6 @@ myri10ge_rx_done(struct myri10ge_priv *m skb_shinfo(skb)->nr_frags = 0; } skb->protocol = eth_type_trans(skb, dev); - skb->dev = dev; if (mgp->csum_flag) { if ((skb->protocol == htons(ETH_P_IP)) || @@ -2030,7 +2029,7 @@ again: odd_flag = 0; flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST); if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { - cksum_offset = (skb->h.raw - skb->data); + cksum_offset = skb_transport_offset(skb); pseudo_hdr_offset = cksum_offset + skb->csum_offset; /* If the headers are excessively large, then we must * fall back to a software checksum */ @@ -2055,7 +2054,7 @@ again: * send loop that we are still in the * header portion of the TSO packet. * TSO header must be at most 134 bytes long */ - cum_len = -((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb)); /* for TSO, pseudo_hdr_offset holds mss. * The firmware figures out where to put diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c index ee26ef5..13444da 100644 --- a/drivers/net/myri_sbus.c +++ b/drivers/net/myri_sbus.c @@ -368,7 +368,7 @@ static __be16 myri_type_trans(struct sk_ struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw = (((unsigned char *)skb->data) + MYRI_PAD_LEN); + skb_set_mac_header(skb, MYRI_PAD_LEN); skb_pull(skb, dev->hard_header_len); eth = eth_hdr(skb); @@ -502,7 +502,7 @@ static void myri_rx(struct myri_eth *mp, copy_skb->dev = dev; DRX(("resv_and_put ")); skb_put(copy_skb, len); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); /* Reuse original ring buffer. */ DRX(("reuse ")); diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 349b96a..a8d7ff2 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -2289,7 +2289,6 @@ static void netdev_rx(struct net_device * without copying to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) { - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve(skb, RX_OFFSET); pci_dma_sync_single_for_cpu(np->pci_dev, diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c index a53644f..2b8da0a 100644 --- a/drivers/net/netx-eth.c +++ b/drivers/net/netx-eth.c @@ -168,7 +168,6 @@ static void netx_eth_receive(struct net_ FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno)); ndev->last_rx = jiffies; - skb->dev = ndev; skb->protocol = eth_type_trans(skb, ndev); netif_rx(skb); priv->stats.rx_packets++; diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index dd8ce35..ad6688e 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -64,9 +64,9 @@ #include #include "netxen_nic_hw.h" #define _NETXEN_NIC_LINUX_MAJOR 3 -#define _NETXEN_NIC_LINUX_MINOR 3 -#define _NETXEN_NIC_LINUX_SUBVERSION 3 -#define NETXEN_NIC_LINUX_VERSIONID "3.3.3" +#define _NETXEN_NIC_LINUX_MINOR 4 +#define _NETXEN_NIC_LINUX_SUBVERSION 2 +#define NETXEN_NIC_LINUX_VERSIONID "3.4.2" #define NUM_FLASH_SECTORS (64) #define FLASH_SECTOR_SIZE (64 * 1024) @@ -131,8 +131,8 @@ #define NX_P2_C1 0x25 #define FIRST_PAGE_GROUP_START 0 #define FIRST_PAGE_GROUP_END 0x100000 -#define SECOND_PAGE_GROUP_START 0x4000000 -#define SECOND_PAGE_GROUP_END 0x66BC000 +#define SECOND_PAGE_GROUP_START 0x6000000 +#define SECOND_PAGE_GROUP_END 0x68BC000 #define THIRD_PAGE_GROUP_START 0x70E4000 #define THIRD_PAGE_GROUP_END 0x8000000 @@ -205,6 +205,8 @@ #define RCV_DESC_TYPE(ID) \ #define MAX_CMD_DESCRIPTORS 1024 #define MAX_RCV_DESCRIPTORS 16384 +#define MAX_CMD_DESCRIPTORS_HOST (MAX_CMD_DESCRIPTORS / 4) +#define MAX_RCV_DESCRIPTORS_1G (MAX_RCV_DESCRIPTORS / 4) #define MAX_JUMBO_RCV_DESCRIPTORS 1024 #define MAX_LRO_RCV_DESCRIPTORS 64 #define MAX_RCVSTATUS_DESCRIPTORS MAX_RCV_DESCRIPTORS @@ -230,7 +232,9 @@ #define get_index_range(index,length,cou (((index) + (count)) & ((length) - 1)) #define MPORT_SINGLE_FUNCTION_MODE 0x1111 +#define MPORT_MULTI_FUNCTION_MODE 0x2222 +#include "netxen_nic_phan_reg.h" extern unsigned long long netxen_dma_mask; extern unsigned long last_schedule_time; @@ -300,6 +304,8 @@ #define FLAGS_VLAN_TAGGED 0x10 #define netxen_set_cmd_desc_port(cmd_desc, var) \ ((cmd_desc)->port_ctxid |= ((var) & 0x0F)) +#define netxen_set_cmd_desc_ctxid(cmd_desc, var) \ + ((cmd_desc)->port_ctxid |= ((var) & 0xF0)) #define netxen_set_cmd_desc_flags(cmd_desc, val) \ ((cmd_desc)->flags_opcode &= ~cpu_to_le16(0x7f), \ @@ -442,7 +448,7 @@ struct status_desc { /* Bit pattern: 0-6 lro_count indicates frag sequence, 7 last_frag indicates last frag */ u8 lro; -} __attribute__ ((aligned(8))); +} __attribute__ ((aligned(16))); enum { NETXEN_RCV_PEG_0 = 0, @@ -703,10 +709,8 @@ #define DPRINTK(klevel, fmt, args...) do #else #define DPRINTK(klevel, fmt, args...) do { \ printk(KERN_##klevel PFX "%s: %s: " fmt, __FUNCTION__,\ - (adapter != NULL && \ - adapter->port[0] != NULL && \ - adapter->port[0]->netdev != NULL) ? \ - adapter->port[0]->netdev->name : NULL, \ + (adapter != NULL && adapter->netdev != NULL) ? \ + adapter->netdev->name : NULL, \ ## args); } while(0) #endif @@ -722,6 +726,18 @@ struct netxen_skb_frag { u32 length; }; +#define _netxen_set_bits(config_word, start, bits, val) {\ + unsigned long long __tmask = (((1ULL << (bits)) - 1) << (start));\ + unsigned long long __tvalue = (val); \ + (config_word) &= ~__tmask; \ + (config_word) |= (((__tvalue) << (start)) & __tmask); \ +} + +#define _netxen_clear_bits(config_word, start, bits) {\ + unsigned long long __tmask = (((1ULL << (bits)) - 1) << (start)); \ + (config_word) &= ~__tmask; \ +} + /* Following defines are for the state of the buffers */ #define NETXEN_BUFFER_FREE 0 #define NETXEN_BUFFER_BUSY 1 @@ -766,6 +782,8 @@ struct netxen_hardware_context { void __iomem *pci_base0; void __iomem *pci_base1; void __iomem *pci_base2; + unsigned long first_page_group_end; + unsigned long first_page_group_start; void __iomem *db_base; unsigned long db_len; @@ -780,6 +798,7 @@ struct netxen_hardware_context { struct pci_dev *cmd_desc_pdev; dma_addr_t cmd_desc_phys_addr; struct netxen_adapter *adapter; + int pci_func; }; #define RCV_RING_LRO RCV_DESC_LRO @@ -788,17 +807,27 @@ #define MINIMUM_ETHERNET_FRAME_SIZE 64 / #define ETHERNET_FCS_SIZE 4 struct netxen_adapter_stats { - u64 ints; - u64 hostints; - u64 otherints; - u64 process_rcv; - u64 process_xmit; - u64 noxmitdone; - u64 xmitcsummed; - u64 post_called; - u64 posted; - u64 lastposted; - u64 goodskbposts; + u64 rcvdbadskb; + u64 xmitcalled; + u64 xmitedframes; + u64 xmitfinished; + u64 badskblen; + u64 nocmddescriptor; + u64 polled; + u64 uphappy; + u64 updropped; + u64 uplcong; + u64 uphcong; + u64 upmcong; + u64 updunno; + u64 skbfreed; + u64 txdropped; + u64 txnullskb; + u64 csummed; + u64 no_rcv; + u64 rxbytes; + u64 txbytes; + u64 ints; }; /* @@ -846,13 +875,20 @@ struct netxen_dummy_dma { struct netxen_adapter { struct netxen_hardware_context ahw; - int port_count; /* Number of configured ports */ - int active_ports; /* Number of open ports */ - struct netxen_port *port[NETXEN_MAX_PORTS]; /* ptr to each port */ + + struct netxen_adapter *master; + struct net_device *netdev; + struct pci_dev *pdev; + struct net_device_stats net_stats; + unsigned char mac_addr[ETH_ALEN]; + int mtu; + int portnum; + spinlock_t tx_lock; spinlock_t lock; struct work_struct watchdog_task; struct timer_list watchdog_timer; + struct work_struct tx_timeout_task; u32 curr_window; @@ -875,6 +911,15 @@ struct netxen_adapter { u32 temp; struct netxen_adapter_stats stats; + + u16 portno; + u16 link_speed; + u16 link_duplex; + u16 state; + u16 link_autoneg; + int rcsum; + int status; + spinlock_t stats_lock; struct netxen_cmd_buffer *cmd_buf_arr; /* Command buffers for xmit */ @@ -891,65 +936,23 @@ struct netxen_adapter { struct netxen_ring_ctx *ctx_desc; struct pci_dev *ctx_desc_pdev; dma_addr_t ctx_desc_phys_addr; - int (*enable_phy_interrupts) (struct netxen_adapter *, int); - int (*disable_phy_interrupts) (struct netxen_adapter *, int); + int (*enable_phy_interrupts) (struct netxen_adapter *); + int (*disable_phy_interrupts) (struct netxen_adapter *); void (*handle_phy_intr) (struct netxen_adapter *); - int (*macaddr_set) (struct netxen_port *, netxen_ethernet_macaddr_t); - int (*set_mtu) (struct netxen_port *, int); - int (*set_promisc) (struct netxen_adapter *, int, - netxen_niu_prom_mode_t); - int (*unset_promisc) (struct netxen_adapter *, int, - netxen_niu_prom_mode_t); - int (*phy_read) (struct netxen_adapter *, long phy, long reg, u32 *); - int (*phy_write) (struct netxen_adapter *, long phy, long reg, u32 val); + int (*macaddr_set) (struct netxen_adapter *, netxen_ethernet_macaddr_t); + int (*set_mtu) (struct netxen_adapter *, int); + int (*set_promisc) (struct netxen_adapter *, netxen_niu_prom_mode_t); + int (*unset_promisc) (struct netxen_adapter *, netxen_niu_prom_mode_t); + int (*phy_read) (struct netxen_adapter *, long reg, u32 *); + int (*phy_write) (struct netxen_adapter *, long reg, u32 val); int (*init_port) (struct netxen_adapter *, int); void (*init_niu) (struct netxen_adapter *); - int (*stop_port) (struct netxen_adapter *, int); + int (*stop_port) (struct netxen_adapter *); }; /* netxen_adapter structure */ /* Max number of xmit producer threads that can run simultaneously */ #define MAX_XMIT_PRODUCERS 16 -struct netxen_port_stats { - u64 rcvdbadskb; - u64 xmitcalled; - u64 xmitedframes; - u64 xmitfinished; - u64 badskblen; - u64 nocmddescriptor; - u64 polled; - u64 uphappy; - u64 updropped; - u64 uplcong; - u64 uphcong; - u64 upmcong; - u64 updunno; - u64 skbfreed; - u64 txdropped; - u64 txnullskb; - u64 csummed; - u64 no_rcv; - u64 rxbytes; - u64 txbytes; -}; - -struct netxen_port { - struct netxen_adapter *adapter; - - u16 portnum; /* GBE port number */ - u16 link_speed; - u16 link_duplex; - u16 link_autoneg; - - int flags; - - struct net_device *netdev; - struct pci_dev *pdev; - struct net_device_stats net_stats; - struct netxen_port_stats stats; - struct work_struct tx_timeout_task; -}; - #define PCI_OFFSET_FIRST_RANGE(adapter, off) \ ((adapter)->ahw.pci_base0 + (off)) #define PCI_OFFSET_SECOND_RANGE(adapter, off) \ @@ -987,32 +990,26 @@ static inline void __iomem *pci_base(str return NULL; } -int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port); +int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter); void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter); void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter); void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter, int port, long enable); void netxen_niu_gbe_set_gmii_mode(struct netxen_adapter *adapter, int port, long enable); -int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long phy, long reg, +int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, __u32 * readval); -int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long phy, +int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, __u32 val); /* Functions available from netxen_nic_hw.c */ -int netxen_nic_set_mtu_xgb(struct netxen_port *port, int new_mtu); -int netxen_nic_set_mtu_gb(struct netxen_port *port, int new_mtu); +int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu); +int netxen_nic_set_mtu_gb(struct netxen_adapter *adapter, int new_mtu); void netxen_nic_init_niu_gb(struct netxen_adapter *adapter); void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw); void netxen_nic_reg_write(struct netxen_adapter *adapter, u64 off, u32 val); @@ -1027,6 +1024,7 @@ int netxen_nic_hw_write_wx(struct netxen int len); void netxen_crb_writelit_adapter(struct netxen_adapter *adapter, unsigned long off, int data); +int netxen_nic_erase_pxe(struct netxen_adapter *adapter); /* Functions from netxen_nic_init.c */ void netxen_free_adapter_offload(struct netxen_adapter *adapter); @@ -1051,11 +1049,8 @@ int netxen_do_rom_se(struct netxen_adapt /* Functions from netxen_nic_isr.c */ void netxen_nic_isr_other(struct netxen_adapter *adapter); -void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 port, - u32 link); -void netxen_handle_port_int(struct netxen_adapter *adapter, u32 port, - u32 enable); -void netxen_nic_stop_all_ports(struct netxen_adapter *adapter); +void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link); +void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable); void netxen_initialize_adapter_sw(struct netxen_adapter *adapter); void netxen_initialize_adapter_hw(struct netxen_adapter *adapter); void *netxen_alloc(struct pci_dev *pdev, size_t sz, dma_addr_t * ptr, @@ -1110,6 +1105,7 @@ static inline void netxen_nic_enable_int if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) { mask = 0xbff; + writel(0X0, NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR)); writel(mask, PCI_OFFSET_SECOND_RANGE(adapter, ISR_INT_TARGET_MASK)); } @@ -1174,4 +1170,5 @@ extern int netxen_rom_fast_read(struct n extern struct ethtool_ops netxen_nic_ethtool_ops; +extern int physical_port[]; /* physical port # from virtual port.*/ #endif /* __NETXEN_NIC_H_ */ diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index ee1b5a2..16fabb3 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -40,8 +40,8 @@ #include #include #include -#include "netxen_nic_hw.h" #include "netxen_nic.h" +#include "netxen_nic_hw.h" #include "netxen_nic_phan_reg.h" struct netxen_nic_stats { @@ -50,8 +50,8 @@ struct netxen_nic_stats { int stat_offset; }; -#define NETXEN_NIC_STAT(m) sizeof(((struct netxen_port *)0)->m), \ - offsetof(struct netxen_port, m) +#define NETXEN_NIC_STAT(m) sizeof(((struct netxen_adapter *)0)->m), \ + offsetof(struct netxen_adapter, m) #define NETXEN_NIC_PORT_WINDOW 0x10000 #define NETXEN_NIC_INVALID_DATA 0xDEADBEEF @@ -100,8 +100,7 @@ static int netxen_nic_get_eeprom_len(str static void netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); u32 fw_major = 0; u32 fw_minor = 0; u32 fw_build = 0; @@ -115,7 +114,7 @@ netxen_nic_get_drvinfo(struct net_device fw_build = readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_FW_VERSION_SUB)); sprintf(drvinfo->fw_version, "%d.%d.%d", fw_major, fw_minor, fw_build); - strncpy(drvinfo->bus_info, pci_name(port->pdev), 32); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); drvinfo->n_stats = NETXEN_NIC_STATS_LEN; drvinfo->testinfo_len = NETXEN_NIC_TEST_LEN; drvinfo->regdump_len = NETXEN_NIC_REGS_LEN; @@ -125,8 +124,7 @@ netxen_nic_get_drvinfo(struct net_device static int netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); struct netxen_board_info *boardinfo = &adapter->ahw.boardcfg; /* read which mode */ @@ -146,8 +144,8 @@ netxen_nic_get_settings(struct net_devic ecmd->port = PORT_TP; if (netif_running(dev)) { - ecmd->speed = port->link_speed; - ecmd->duplex = port->link_duplex; + ecmd->speed = adapter->link_speed; + ecmd->duplex = adapter->link_duplex; } else return -EIO; /* link absent */ } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { @@ -165,7 +163,7 @@ netxen_nic_get_settings(struct net_devic } else return -EIO; - ecmd->phy_address = port->portnum; + ecmd->phy_address = adapter->portnum; ecmd->transceiver = XCVR_EXTERNAL; switch ((netxen_brdtype_t) boardinfo->board_type) { @@ -179,7 +177,7 @@ netxen_nic_get_settings(struct net_devic ecmd->port = PORT_TP; ecmd->autoneg = (boardinfo->board_type == NETXEN_BRDTYPE_P2_SB31_10G_CX4) ? - (AUTONEG_DISABLE) : (port->link_autoneg); + (AUTONEG_DISABLE) : (adapter->link_autoneg); break; case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ: case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ: @@ -206,23 +204,22 @@ netxen_nic_get_settings(struct net_devic static int netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 status; /* read which mode */ if (adapter->ahw.board_type == NETXEN_NIC_GBE) { /* autonegotiation */ if (adapter->phy_write - && adapter->phy_write(adapter, port->portnum, + && adapter->phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, ecmd->autoneg) != 0) return -EIO; else - port->link_autoneg = ecmd->autoneg; + adapter->link_autoneg = ecmd->autoneg; if (adapter->phy_read - && adapter->phy_read(adapter, port->portnum, + && adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) != 0) return -EIO; @@ -245,13 +242,13 @@ netxen_nic_set_settings(struct net_devic if (ecmd->duplex == DUPLEX_FULL) netxen_set_phy_duplex(status); if (adapter->phy_write - && adapter->phy_write(adapter, port->portnum, + && adapter->phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, *((int *)&status)) != 0) return -EIO; else { - port->link_speed = ecmd->speed; - port->link_duplex = ecmd->duplex; + adapter->link_speed = ecmd->speed; + adapter->link_duplex = ecmd->duplex; } } else return -EOPNOTSUPP; @@ -360,15 +357,14 @@ static struct netxen_niu_regs niu_regist static void netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 mode, *regs_buff = p; void __iomem *addr; int i, window; memset(p, 0, NETXEN_NIC_REGS_LEN); regs->version = (1 << 24) | (adapter->ahw.revision_id << 16) | - (port->pdev)->device; + (adapter->pdev)->device; /* which mode */ NETXEN_NIC_LOCKED_READ_REG(NETXEN_NIU_MODE, ®s_buff[0]); mode = regs_buff[0]; @@ -383,7 +379,8 @@ netxen_nic_get_regs(struct net_device *d for (i = 3; niu_registers[mode].reg[i - 3] != -1; i++) { /* GB: port specific registers */ if (mode == 0 && i >= 19) - window = port->portnum * NETXEN_NIC_PORT_WINDOW; + window = physical_port[adapter->portnum] * + NETXEN_NIC_PORT_WINDOW; NETXEN_NIC_LOCKED_READ_REG(niu_registers[mode]. reg[i - 3] + window, @@ -395,15 +392,14 @@ netxen_nic_get_regs(struct net_device *d static u32 netxen_nic_test_link(struct net_device *dev) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 status; int val; /* read which mode */ if (adapter->ahw.board_type == NETXEN_NIC_GBE) { if (adapter->phy_read - && adapter->phy_read(adapter, port->portnum, + && adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) != 0) return -EIO; @@ -422,15 +418,15 @@ static int netxen_nic_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); int offset; int ret; if (eeprom->len == 0) return -EINVAL; - eeprom->magic = (port->pdev)->vendor | ((port->pdev)->device << 16); + eeprom->magic = (adapter->pdev)->vendor | + ((adapter->pdev)->device << 16); offset = eeprom->offset; ret = netxen_rom_fast_read_words(adapter, offset, bytes, @@ -445,8 +441,7 @@ static int netxen_nic_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); int offset = eeprom->offset; static int flash_start; static int ready_to_flash; @@ -516,8 +511,7 @@ netxen_nic_set_eeprom(struct net_device static void netxen_nic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); int i; ring->rx_pending = 0; @@ -541,19 +535,45 @@ static void netxen_nic_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 val; + int port = physical_port[adapter->portnum]; if (adapter->ahw.board_type == NETXEN_NIC_GBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + return; /* get flow control settings */ - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum), - &val); + netxen_nic_read_w0(adapter,NETXEN_NIU_GB_MAC_CONFIG_0(port), + &val); pause->rx_pause = netxen_gb_get_rx_flowctl(val); - pause->tx_pause = netxen_gb_get_tx_flowctl(val); - /* get autoneg settings */ - pause->autoneg = port->link_autoneg; + netxen_nic_read_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, &val); + switch (port) { + case 0: + pause->tx_pause = !(netxen_gb_get_gb0_mask(val)); + break; + case 1: + pause->tx_pause = !(netxen_gb_get_gb1_mask(val)); + break; + case 2: + pause->tx_pause = !(netxen_gb_get_gb2_mask(val)); + break; + case 3: + default: + pause->tx_pause = !(netxen_gb_get_gb3_mask(val)); + break; + } + } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) + return; + pause->rx_pause = 1; + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, &val); + if (port == 0) + pause->tx_pause = !(netxen_xg_get_xg0_mask(val)); + else + pause->tx_pause = !(netxen_xg_get_xg1_mask(val)); + } else { + printk(KERN_ERR"%s: Unknown board type: %x\n", + netxen_nic_driver_name, adapter->ahw.board_type); } } @@ -561,42 +581,76 @@ static int netxen_nic_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 val; - unsigned int autoneg; - + int port = physical_port[adapter->portnum]; /* read mode */ if (adapter->ahw.board_type == NETXEN_NIC_GBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + return -EIO; /* set flow control */ netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum), - (u32 *) & val); - if (pause->tx_pause) - netxen_gb_tx_flowctl(val); - else - netxen_gb_unset_tx_flowctl(val); + NETXEN_NIU_GB_MAC_CONFIG_0(port), &val); + if (pause->rx_pause) netxen_gb_rx_flowctl(val); else netxen_gb_unset_rx_flowctl(val); - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum), - *&val); + netxen_nic_write_w0(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), + val); /* set autoneg */ - autoneg = pause->autoneg; - if (adapter->phy_write - && adapter->phy_write(adapter, port->portnum, - NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, - autoneg) != 0) + netxen_nic_read_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, &val); + switch (port) { + case 0: + if (pause->tx_pause) + netxen_gb_unset_gb0_mask(val); + else + netxen_gb_set_gb0_mask(val); + break; + case 1: + if (pause->tx_pause) + netxen_gb_unset_gb1_mask(val); + else + netxen_gb_set_gb1_mask(val); + break; + case 2: + if (pause->tx_pause) + netxen_gb_unset_gb2_mask(val); + else + netxen_gb_set_gb2_mask(val); + break; + case 3: + default: + if (pause->tx_pause) + netxen_gb_unset_gb3_mask(val); + else + netxen_gb_set_gb3_mask(val); + break; + } + netxen_nic_write_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, val); + } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) return -EIO; - else { - port->link_autoneg = pause->autoneg; - return 0; + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, &val); + if (port == 0) { + if (pause->tx_pause) + netxen_xg_unset_xg0_mask(val); + else + netxen_xg_set_xg0_mask(val); + } else { + if (pause->tx_pause) + netxen_xg_unset_xg1_mask(val); + else + netxen_xg_set_xg1_mask(val); } - } else - return -EOPNOTSUPP; + netxen_nic_write_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, val); + } else { + printk(KERN_ERR "%s: Unknown board type: %x\n", + netxen_nic_driver_name, + adapter->ahw.board_type); + } + return 0; } static int netxen_nic_reg_test(struct net_device *dev) @@ -627,23 +681,12 @@ static void netxen_nic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, u64 * data) { - if (eth_test->flags == ETH_TEST_FL_OFFLINE) { /* offline tests */ - /* link test */ - if ((data[1] = (u64) netxen_nic_test_link(dev))) - eth_test->flags |= ETH_TEST_FL_FAILED; - - /* register tests */ - if ((data[0] = netxen_nic_reg_test(dev))) - eth_test->flags |= ETH_TEST_FL_FAILED; - } else { /* online tests */ - /* register tests */ - if((data[0] = netxen_nic_reg_test(dev))) - eth_test->flags |= ETH_TEST_FL_FAILED; - - /* link test */ - if ((data[1] = (u64) netxen_nic_test_link(dev))) - eth_test->flags |= ETH_TEST_FL_FAILED; - } + memset(data, 0, sizeof(uint64_t) * NETXEN_NIC_TEST_LEN); + if ((data[0] = netxen_nic_reg_test(dev))) + eth_test->flags |= ETH_TEST_FL_FAILED; + /* link test */ + if ((data[1] = (u64) netxen_nic_test_link(dev))) + eth_test->flags |= ETH_TEST_FL_FAILED; } static void @@ -675,12 +718,13 @@ static void netxen_nic_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 * data) { - struct netxen_port *port = netdev_priv(dev); + struct netxen_adapter *adapter = netdev_priv(dev); int index; for (index = 0; index < NETXEN_NIC_STATS_LEN; index++) { char *p = - (char *)port + netxen_nic_gstrings_stats[index].stat_offset; + (char *)adapter + + netxen_nic_gstrings_stats[index].stat_offset; data[index] = (netxen_nic_gstrings_stats[index].sizeof_stat == sizeof(u64)) ? *(u64 *) p : *(u32 *) p; diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h index fe8b675..608e37b 100644 --- a/drivers/net/netxen/netxen_nic_hdr.h +++ b/drivers/net/netxen/netxen_nic_hdr.h @@ -467,6 +467,8 @@ #define NETXEN_PCI_OCM0_MAX (0x050fffffU #define NETXEN_PCI_OCM1 (0x05100000UL) #define NETXEN_PCI_OCM1_MAX (0x051fffffUL) #define NETXEN_PCI_CRBSPACE (0x06000000UL) +#define NETXEN_PCI_128MB_SIZE (0x08000000UL) +#define NETXEN_PCI_32MB_SIZE (0x02000000UL) #define NETXEN_CRB_CAM NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_CAM) @@ -484,6 +486,7 @@ #define NETXEN_NIU_PHY_WAITLEN 200000 /* 10 seconds before we give up */ #define NETXEN_NIU_PHY_WAITMAX 50 #define NETXEN_NIU_MAX_GBE_PORTS 4 +#define NETXEN_NIU_MAX_XG_PORTS 2 #define NETXEN_NIU_MODE (NETXEN_CRB_NIU + 0x00000) @@ -527,6 +530,7 @@ #define NETXEN_NIU_TEST_MUX_CTL (NETXEN #define NETXEN_NIU_XG_PAUSE_CTL (NETXEN_CRB_NIU + 0x00098) #define NETXEN_NIU_XG_PAUSE_LEVEL (NETXEN_CRB_NIU + 0x000dc) #define NETXEN_NIU_XG_SEL (NETXEN_CRB_NIU + 0x00128) +#define NETXEN_NIU_GB_PAUSE_CTL (NETXEN_CRB_NIU + 0x0030c) #define NETXEN_NIU_FULL_LEVEL_XG (NETXEN_CRB_NIU + 0x00450) @@ -649,11 +653,19 @@ #define PCIX_MN_WINDOW (0x10200) #define PCIX_MS_WINDOW (0x10204) #define PCIX_SN_WINDOW (0x10208) #define PCIX_CRB_WINDOW (0x10210) +#define PCIX_CRB_WINDOW_F0 (0x10210) +#define PCIX_CRB_WINDOW_F1 (0x10230) +#define PCIX_CRB_WINDOW_F2 (0x10250) +#define PCIX_CRB_WINDOW_F3 (0x10270) #define PCIX_TARGET_STATUS (0x10118) #define PCIX_TARGET_MASK (0x10128) #define PCIX_MSI_F0 (0x13000) +#define PCIX_MSI_F1 (0x13004) +#define PCIX_MSI_F2 (0x13008) +#define PCIX_MSI_F3 (0x1300c) +#define PCIX_MSI_F(i) (0x13000+((i)*4)) #define PCIX_PS_MEM_SPACE (0x90000) diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index 6537574..baff17a 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -33,8 +33,225 @@ #include "netxen_nic.h" #include "netxen_nic_hw.h" +#define DEFINE_GLOBAL_RECV_CRB #include "netxen_nic_phan_reg.h" + +#include + +struct netxen_recv_crb recv_crb_registers[] = { + /* + * Instance 0. + */ + { + /* rcv_desc_crb: */ + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x100), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x104), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x108), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x10c), + + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x110), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x114), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x118), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x11c), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x120), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x124), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x128), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x12c), + } + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x130), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x134), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x138), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x13c), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x140), + + }, + /* + * Instance 1, + */ + { + /* rcv_desc_crb: */ + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x144), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x148), + /* crb_globalrcv_ring: */ + NETXEN_NIC_REG(0x14c), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x150), + + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x154), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x158), + /* crb_globalrcv_ring: */ + NETXEN_NIC_REG(0x15c), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x160), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x164), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x168), + /* crb_globalrcv_ring: */ + NETXEN_NIC_REG(0x16c), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x170), + } + + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x174), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x178), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x17c), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x180), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x184), + }, + /* + * Instance 2, + */ + { + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x1d8), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x1dc), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x1f0), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x1f4), + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x1f8), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x1fc), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x200), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x204), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x208), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x20c), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x210), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x214), + } + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x218), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x21c), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x220), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x224), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x228), + }, + /* + * Instance 3, + */ + { + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x22c), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x230), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x234), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x238), + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x23c), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x240), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x244), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x248), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x24c), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x250), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x254), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x258), + } + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x25c), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x260), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x264), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x268), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x26c), + }, +}; + +u64 ctx_addr_sig_regs[][3] = { + {NETXEN_NIC_REG(0x188), NETXEN_NIC_REG(0x18c), NETXEN_NIC_REG(0x1c0)}, + {NETXEN_NIC_REG(0x190), NETXEN_NIC_REG(0x194), NETXEN_NIC_REG(0x1c4)}, + {NETXEN_NIC_REG(0x198), NETXEN_NIC_REG(0x19c), NETXEN_NIC_REG(0x1c8)}, + {NETXEN_NIC_REG(0x1a0), NETXEN_NIC_REG(0x1a4), NETXEN_NIC_REG(0x1cc)} +}; + + /* PCI Windowing for DDR regions. */ #define ADDR_IN_RANGE(addr, low, high) \ @@ -68,8 +285,7 @@ void netxen_free_hw_resources(struct net int netxen_nic_set_mac(struct net_device *netdev, void *p) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; if (netif_running(netdev)) @@ -82,7 +298,7 @@ int netxen_nic_set_mac(struct net_device memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); if (adapter->macaddr_set) - adapter->macaddr_set(port, addr->sa_data); + adapter->macaddr_set(adapter, addr->sa_data); return 0; } @@ -92,56 +308,19 @@ int netxen_nic_set_mac(struct net_device */ void netxen_nic_set_multi(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); struct dev_mc_list *mc_ptr; - __u32 netxen_mac_addr_cntl_data = 0; mc_ptr = netdev->mc_list; if (netdev->flags & IFF_PROMISC) { if (adapter->set_promisc) adapter->set_promisc(adapter, - port->portnum, NETXEN_NIU_PROMISC_MODE); } else { - if (adapter->unset_promisc && - adapter->ahw.boardcfg.board_type - != NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) + if (adapter->unset_promisc) adapter->unset_promisc(adapter, - port->portnum, NETXEN_NIU_NON_PROMISC_MODE); } - if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { - netxen_nic_mcr_set_mode_select(netxen_mac_addr_cntl_data, 0x03); - netxen_nic_mcr_set_id_pool0(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool1(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool2(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool3(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_enable_xtnd0(netxen_mac_addr_cntl_data); - netxen_nic_mcr_set_enable_xtnd1(netxen_mac_addr_cntl_data); - netxen_nic_mcr_set_enable_xtnd2(netxen_mac_addr_cntl_data); - netxen_nic_mcr_set_enable_xtnd3(netxen_mac_addr_cntl_data); - } else { - netxen_nic_mcr_set_mode_select(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool0(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool1(netxen_mac_addr_cntl_data, 0x01); - netxen_nic_mcr_set_id_pool2(netxen_mac_addr_cntl_data, 0x02); - netxen_nic_mcr_set_id_pool3(netxen_mac_addr_cntl_data, 0x03); - } - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_MAC_ADDR_CNTL_REG)); - if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, - NETXEN_MULTICAST_ADDR_HI_0)); - } else { - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, - NETXEN_MULTICAST_ADDR_HI_1)); - } - netxen_mac_addr_cntl_data = 0; - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_NIU_GB_DROP_WRONGADDR)); } /* @@ -150,8 +329,7 @@ void netxen_nic_set_multi(struct net_dev */ int netxen_nic_change_mtu(struct net_device *netdev, int mtu) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); int eff_mtu = mtu + NETXEN_ENET_HEADER_SIZE + NETXEN_ETH_FCS_SIZE; if ((eff_mtu > NETXEN_MAX_MTU) || (eff_mtu < NETXEN_MIN_MTU)) { @@ -161,7 +339,7 @@ int netxen_nic_change_mtu(struct net_dev } if (adapter->set_mtu) - adapter->set_mtu(port, mtu); + adapter->set_mtu(adapter, mtu); netdev->mtu = mtu; return 0; @@ -178,9 +356,9 @@ int netxen_nic_hw_resources(struct netxe void *addr; int loops = 0, err = 0; int ctx, ring; - u32 card_cmdring = 0; struct netxen_recv_context *recv_ctx; struct netxen_rcv_desc_ctx *rcv_desc; + int func_id = adapter->portnum; DPRINTK(INFO, "crb_base: %lx %x", NETXEN_PCI_CRBSPACE, PCI_OFFSET_SECOND_RANGE(adapter, NETXEN_PCI_CRBSPACE)); @@ -189,11 +367,6 @@ int netxen_nic_hw_resources(struct netxe DPRINTK(INFO, "cam RAM: %lx %x", NETXEN_CAM_RAM_BASE, pci_base_offset(adapter, NETXEN_CAM_RAM_BASE)); - /* Window 1 call */ - card_cmdring = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_CMDRING)); - - DPRINTK(INFO, "Command Peg sends 0x%x for cmdring base\n", - card_cmdring); for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { DPRINTK(INFO, "Command Peg ready..waiting for rcv peg\n"); @@ -227,7 +400,7 @@ int netxen_nic_hw_resources(struct netxe (dma_addr_t *) & adapter->ctx_desc_phys_addr, &adapter->ctx_desc_pdev); - printk("ctx_desc_phys_addr: 0x%llx\n", + printk(KERN_INFO "ctx_desc_phys_addr: 0x%llx\n", (unsigned long long) adapter->ctx_desc_phys_addr); if (addr == NULL) { DPRINTK(ERR, "bad return from pci_alloc_consistent\n"); @@ -236,6 +409,7 @@ int netxen_nic_hw_resources(struct netxe } memset(addr, 0, sizeof(struct netxen_ring_ctx)); adapter->ctx_desc = (struct netxen_ring_ctx *)addr; + adapter->ctx_desc->ctx_id = cpu_to_le32(adapter->portnum); adapter->ctx_desc->cmd_consumer_offset = cpu_to_le64(adapter->ctx_desc_phys_addr + sizeof(struct netxen_ring_ctx)); @@ -247,7 +421,7 @@ int netxen_nic_hw_resources(struct netxe adapter->max_tx_desc_count, (dma_addr_t *) & hw->cmd_desc_phys_addr, &adapter->ahw.cmd_desc_pdev); - printk("cmd_desc_phys_addr: 0x%llx\n", + printk(KERN_INFO "cmd_desc_phys_addr: 0x%llx\n", (unsigned long long) hw->cmd_desc_phys_addr); if (addr == NULL) { @@ -306,11 +480,11 @@ int netxen_nic_hw_resources(struct netxe /* Window = 1 */ writel(lower32(adapter->ctx_desc_phys_addr), - NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_LO)); + NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_LO(func_id))); writel(upper32(adapter->ctx_desc_phys_addr), - NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_HI)); - writel(NETXEN_CTX_SIGNATURE, - NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_SIGNATURE_REG)); + NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_HI(func_id))); + writel(NETXEN_CTX_SIGNATURE | func_id, + NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_SIGNATURE_REG(func_id))); return err; } @@ -337,10 +511,6 @@ void netxen_free_hw_resources(struct net adapter->ahw.cmd_desc_phys_addr); adapter->ahw.cmd_desc_head = NULL; } - /* Special handling: there are 2 ports on this board */ - if (adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) { - adapter->ahw.max_ports = 2; - } for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { recv_ctx = &adapter->recv_ctx[ctx]; @@ -371,22 +541,20 @@ void netxen_tso_check(struct netxen_adap struct cmd_desc_type0 *desc, struct sk_buff *skb) { if (desc->mss) { - desc->total_hdr_length = sizeof(struct ethhdr) + - ((skb->nh.iph)->ihl * sizeof(u32)) + - ((skb->h.th)->doff * sizeof(u32)); + desc->total_hdr_length = (sizeof(struct ethhdr) + + ip_hdrlen(skb) + tcp_hdrlen(skb)); netxen_set_cmd_desc_opcode(desc, TX_TCP_LSO); } else if (skb->ip_summed == CHECKSUM_PARTIAL) { - if (skb->nh.iph->protocol == IPPROTO_TCP) { + if (ip_hdr(skb)->protocol == IPPROTO_TCP) { netxen_set_cmd_desc_opcode(desc, TX_TCP_PKT); - } else if (skb->nh.iph->protocol == IPPROTO_UDP) { + } else if (ip_hdr(skb)->protocol == IPPROTO_UDP) { netxen_set_cmd_desc_opcode(desc, TX_UDP_PKT); } else { return; } } - adapter->stats.xmitcsummed++; - desc->tcp_hdr_offset = skb->h.raw - skb->data; - desc->ip_hdr_offset = skb->nh.raw - skb->data; + desc->tcp_hdr_offset = skb_transport_offset(skb); + desc->ip_hdr_offset = skb_network_offset(skb); } int netxen_is_flash_supported(struct netxen_adapter *adapter) @@ -474,7 +642,30 @@ void netxen_nic_pci_change_crbwindow(str if (adapter->curr_window == wndw) return; - + switch(adapter->ahw.pci_func) { + case 0: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW)); + break; + case 1: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F1)); + break; + case 2: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F2)); + break; + case 3: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F3)); + break; + default: + printk(KERN_INFO "Changing the window for PCI function" + "%d\n", adapter->ahw.pci_func); + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW)); + break; + } /* * Move the CRB window. * We need to write to the "direct access" region of PCI @@ -483,9 +674,6 @@ void netxen_nic_pci_change_crbwindow(str * register address is received by PCI. The direct region bypasses * the CRB bus. */ - offset = - PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW)); if (wndw & 0x1) wndw = NETXEN_WINDOW_ONE; @@ -503,7 +691,10 @@ void netxen_nic_pci_change_crbwindow(str count++; } - adapter->curr_window = wndw; + if (wndw == NETXEN_WINDOW_ONE) + adapter->curr_window = 1; + else + adapter->curr_window = 0; } void netxen_load_firmware(struct netxen_adapter *adapter) @@ -748,6 +939,17 @@ netxen_nic_pci_set_window(struct netxen_ return addr; } +int +netxen_nic_erase_pxe(struct netxen_adapter *adapter) +{ + if (netxen_rom_fast_write(adapter, PXE_START, 0) == -1) { + printk(KERN_ERR "%s: erase pxe failed\n", + netxen_nic_driver_name); + return -1; + } + return 0; +} + int netxen_nic_get_board_info(struct netxen_adapter *adapter) { int rv = 0; @@ -809,43 +1011,29 @@ int netxen_nic_get_board_info(struct net /* NIU access sections */ -int netxen_nic_set_mtu_gb(struct netxen_port *port, int new_mtu) +int netxen_nic_set_mtu_gb(struct netxen_adapter *adapter, int new_mtu) { - struct netxen_adapter *adapter = port->adapter; netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MAX_FRAME_SIZE(port->portnum), - new_mtu); + NETXEN_NIU_GB_MAX_FRAME_SIZE( + physical_port[adapter->portnum]), new_mtu); return 0; } -int netxen_nic_set_mtu_xgb(struct netxen_port *port, int new_mtu) +int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu) { - struct netxen_adapter *adapter = port->adapter; new_mtu += NETXEN_NIU_HDRSIZE + NETXEN_NIU_TLRSIZE; - if (port->portnum == 0) - netxen_nic_write_w0(adapter, NETXEN_NIU_XGE_MAX_FRAME_SIZE, new_mtu); - else if (port->portnum == 1) - netxen_nic_write_w0(adapter, NETXEN_NIU_XG1_MAX_FRAME_SIZE, new_mtu); + if (physical_port[adapter->portnum] == 0) + netxen_nic_write_w0(adapter, NETXEN_NIU_XGE_MAX_FRAME_SIZE, + new_mtu); + else + netxen_nic_write_w0(adapter, NETXEN_NIU_XG1_MAX_FRAME_SIZE, + new_mtu); return 0; } void netxen_nic_init_niu_gb(struct netxen_adapter *adapter) { - int portno; - for (portno = 0; portno < NETXEN_NIU_MAX_GBE_PORTS; portno++) - netxen_niu_gbe_init_port(adapter, portno); -} - -void netxen_nic_stop_all_ports(struct netxen_adapter *adapter) -{ - int port_nr; - struct netxen_port *port; - - for (port_nr = 0; port_nr < adapter->ahw.max_ports; port_nr++) { - port = adapter->port[port_nr]; - if (adapter->stop_port) - adapter->stop_port(adapter, port->portnum); - } + netxen_niu_gbe_init_port(adapter, physical_port[adapter->portnum]); } void @@ -864,9 +1052,8 @@ netxen_crb_writelit_adapter(struct netxe } } -void netxen_nic_set_link_parameters(struct netxen_port *port) +void netxen_nic_set_link_parameters(struct netxen_adapter *adapter) { - struct netxen_adapter *adapter = port->adapter; __u32 status; __u32 autoneg; __u32 mode; @@ -875,47 +1062,47 @@ void netxen_nic_set_link_parameters(stru if (netxen_get_niu_enable_ge(mode)) { /* Gb 10/100/1000 Mbps mode */ if (adapter->phy_read && adapter-> - phy_read(adapter, port->portnum, + phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) == 0) { if (netxen_get_phy_link(status)) { switch (netxen_get_phy_speed(status)) { case 0: - port->link_speed = SPEED_10; + adapter->link_speed = SPEED_10; break; case 1: - port->link_speed = SPEED_100; + adapter->link_speed = SPEED_100; break; case 2: - port->link_speed = SPEED_1000; + adapter->link_speed = SPEED_1000; break; default: - port->link_speed = -1; + adapter->link_speed = -1; break; } switch (netxen_get_phy_duplex(status)) { case 0: - port->link_duplex = DUPLEX_HALF; + adapter->link_duplex = DUPLEX_HALF; break; case 1: - port->link_duplex = DUPLEX_FULL; + adapter->link_duplex = DUPLEX_FULL; break; default: - port->link_duplex = -1; + adapter->link_duplex = -1; break; } if (adapter->phy_read && adapter-> - phy_read(adapter, port->portnum, + phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, &autoneg) != 0) - port->link_autoneg = autoneg; + adapter->link_autoneg = autoneg; } else goto link_down; } else { link_down: - port->link_speed = -1; - port->link_duplex = -1; + adapter->link_speed = -1; + adapter->link_duplex = -1; } } } @@ -929,7 +1116,7 @@ void netxen_nic_flash_print(struct netxe char brd_name[NETXEN_MAX_SHORT_NAME]; struct netxen_new_user_info user_info; int i, addr = USER_START; - u32 *ptr32; + __le32 *ptr32; struct netxen_board_info *board_info = &(adapter->ahw.boardcfg); if (board_info->magic != NETXEN_BDINFO_MAGIC) { @@ -955,7 +1142,6 @@ void netxen_nic_flash_print(struct netxe netxen_nic_driver_name); return; } - *ptr32 = le32_to_cpu(*ptr32); ptr32++; addr += sizeof(u32); } diff --git a/drivers/net/netxen/netxen_nic_hw.h b/drivers/net/netxen/netxen_nic_hw.h index ab1112e..245bf13 100644 --- a/drivers/net/netxen/netxen_nic_hw.h +++ b/drivers/net/netxen/netxen_nic_hw.h @@ -6,12 +6,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, @@ -87,7 +87,7 @@ #define NETXEN_NIC_LOCKED_READ_REG(X, Y) *(u32 *)Y = readl((void __iomem*) addr); struct netxen_port; -void netxen_nic_set_link_parameters(struct netxen_port *port); +void netxen_nic_set_link_parameters(struct netxen_adapter *adapter); void netxen_nic_flash_print(struct netxen_adapter *adapter); int netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, int len); @@ -220,6 +220,69 @@ #define netxen_get_gb_mii_mgmt_scanning( _netxen_crb_get_bit(config_word, 1) #define netxen_get_gb_mii_mgmt_notvalid(config_word) \ _netxen_crb_get_bit(config_word, 2) +/* + * NIU XG Pause Ctl Register + * + * Bit 0 : xg0_mask => 1:disable tx pause frames + * Bit 1 : xg0_request => 1:request single pause frame + * Bit 2 : xg0_on_off => 1:request is pause on, 0:off + * Bit 3 : xg1_mask => 1:disable tx pause frames + * Bit 4 : xg1_request => 1:request single pause frame + * Bit 5 : xg1_on_off => 1:request is pause on, 0:off + */ + +#define netxen_xg_set_xg0_mask(config_word) \ + ((config_word) |= 1 << 0) +#define netxen_xg_set_xg1_mask(config_word) \ + ((config_word) |= 1 << 3) + +#define netxen_xg_get_xg0_mask(config_word) \ + _netxen_crb_get_bit((config_word), 0) +#define netxen_xg_get_xg1_mask(config_word) \ + _netxen_crb_get_bit((config_word), 3) + +#define netxen_xg_unset_xg0_mask(config_word) \ + ((config_word) &= ~(1 << 0)) +#define netxen_xg_unset_xg1_mask(config_word) \ + ((config_word) &= ~(1 << 3)) + +/* + * NIU XG Pause Ctl Register + * + * Bit 0 : xg0_mask => 1:disable tx pause frames + * Bit 1 : xg0_request => 1:request single pause frame + * Bit 2 : xg0_on_off => 1:request is pause on, 0:off + * Bit 3 : xg1_mask => 1:disable tx pause frames + * Bit 4 : xg1_request => 1:request single pause frame + * Bit 5 : xg1_on_off => 1:request is pause on, 0:off + */ +#define netxen_gb_set_gb0_mask(config_word) \ + ((config_word) |= 1 << 0) +#define netxen_gb_set_gb1_mask(config_word) \ + ((config_word) |= 1 << 2) +#define netxen_gb_set_gb2_mask(config_word) \ + ((config_word) |= 1 << 4) +#define netxen_gb_set_gb3_mask(config_word) \ + ((config_word) |= 1 << 6) + +#define netxen_gb_get_gb0_mask(config_word) \ + _netxen_crb_get_bit((config_word), 0) +#define netxen_gb_get_gb1_mask(config_word) \ + _netxen_crb_get_bit((config_word), 2) +#define netxen_gb_get_gb2_mask(config_word) \ + _netxen_crb_get_bit((config_word), 4) +#define netxen_gb_get_gb3_mask(config_word) \ + _netxen_crb_get_bit((config_word), 6) + +#define netxen_gb_unset_gb0_mask(config_word) \ + ((config_word) &= ~(1 << 0)) +#define netxen_gb_unset_gb1_mask(config_word) \ + ((config_word) &= ~(1 << 2)) +#define netxen_gb_unset_gb2_mask(config_word) \ + ((config_word) &= ~(1 << 4)) +#define netxen_gb_unset_gb3_mask(config_word) \ + ((config_word) &= ~(1 << 6)) + /* * PHY-Specific MII control/status registers. @@ -452,21 +515,21 @@ #define netxen_nic_mcr_set_enable_pool(c ((config) |= (((val) & 0x0f) << 28)) /* Set promiscuous mode for a GbE interface */ -int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int port, +int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, netxen_niu_prom_mode_t mode); int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter, - int port, netxen_niu_prom_mode_t mode); + netxen_niu_prom_mode_t mode); /* get/set the MAC address for a given MAC */ -int netxen_niu_macaddr_get(struct netxen_adapter *adapter, int port, +int netxen_niu_macaddr_get(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t * addr); -int netxen_niu_macaddr_set(struct netxen_port *port, +int netxen_niu_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr); /* XG versons */ -int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, int port, +int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t * addr); -int netxen_niu_xg_macaddr_set(struct netxen_port *port, +int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr); /* Generic enable for GbE ports. Will detect the speed of the link. */ @@ -475,8 +538,8 @@ int netxen_niu_gbe_init_port(struct netx int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port); /* Disable a GbE interface */ -int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter, int port); +int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter); -int netxen_niu_disable_xg_port(struct netxen_adapter *adapter, int port); +int netxen_niu_disable_xg_port(struct netxen_adapter *adapter); #endif /* __NETXEN_NIC_HW_H_ */ diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index eff965d..cf0e96a 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -139,7 +139,7 @@ int netxen_init_firmware(struct netxen_a return err; } /* Window 1 call */ - writel(MPORT_SINGLE_FUNCTION_MODE, + writel(MPORT_MULTI_FUNCTION_MODE, NETXEN_CRB_NORMALIZE(adapter, CRB_MPORT_MODE)); writel(PHAN_INITIALIZE_ACK, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); @@ -226,7 +226,6 @@ void netxen_initialize_adapter_ops(struc adapter->unset_promisc = netxen_niu_set_promiscuous_mode; adapter->phy_read = netxen_niu_gbe_phy_read; adapter->phy_write = netxen_niu_gbe_phy_write; - adapter->init_port = netxen_niu_gbe_init_port; adapter->init_niu = netxen_nic_init_niu_gb; adapter->stop_port = netxen_niu_disable_gbe_port; break; @@ -277,8 +276,8 @@ u32 netxen_decode_crb_addr(u32 addr) return (pci_base + offset); } -static long rom_max_timeout = 10000; -static long rom_lock_timeout = 1000000; +static long rom_max_timeout = 100; +static long rom_lock_timeout = 10000; static long rom_write_timeout = 700; static inline int rom_lock(struct netxen_adapter *adapter) @@ -438,9 +437,9 @@ do_rom_fast_read_words(struct netxen_ada for (addridx = addr; addridx < (addr + size); addridx += 4) { ret = do_rom_fast_read(adapter, addridx, (int *)bytes); - *(int *)bytes = cpu_to_le32(*(int *)bytes); if (ret != 0) break; + *(int *)bytes = cpu_to_le32(*(int *)bytes); bytes += 4; } @@ -499,7 +498,6 @@ static inline int do_rom_fast_write_word int data; data = le32_to_cpu((*(u32*)bytes)); - ret = do_rom_fast_write(adapter, addridx, data); if (ret < 0) return ret; @@ -953,7 +951,8 @@ void netxen_phantom_init(struct netxen_a if (!pegtune_val) { val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); - while (val != PHAN_INITIALIZE_COMPLETE && loops < 200000) { + while (val != PHAN_INITIALIZE_COMPLETE && + val != PHAN_INITIALIZE_ACK && loops < 200000) { udelay(100); schedule(); val = @@ -990,9 +989,7 @@ int netxen_nic_rx_has_work(struct netxen static inline int netxen_nic_check_temp(struct netxen_adapter *adapter) { - int port_num; - struct netxen_port *port; - struct net_device *netdev; + struct net_device *netdev = adapter->netdev; uint32_t temp, temp_state, temp_val; int rv = 0; @@ -1006,14 +1003,9 @@ static inline int netxen_nic_check_temp( "%s: Device temperature %d degrees C exceeds" " maximum allowed. Hardware has been shut down.\n", netxen_nic_driver_name, temp_val); - for (port_num = 0; port_num < adapter->ahw.max_ports; - port_num++) { - port = adapter->port[port_num]; - netdev = port->netdev; - netif_carrier_off(netdev); - netif_stop_queue(netdev); - } + netif_carrier_off(netdev); + netif_stop_queue(netdev); rv = 1; } else if (temp_state == NX_TEMP_WARN) { if (adapter->temp == NX_TEMP_NORMAL) { @@ -1037,29 +1029,23 @@ static inline int netxen_nic_check_temp( void netxen_watchdog_task(struct work_struct *work) { - int port_num; - struct netxen_port *port; struct net_device *netdev; struct netxen_adapter *adapter = container_of(work, struct netxen_adapter, watchdog_task); - if (netxen_nic_check_temp(adapter)) + if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter)) return; - for (port_num = 0; port_num < adapter->ahw.max_ports; port_num++) { - port = adapter->port[port_num]; - netdev = port->netdev; - - if ((netif_running(netdev)) && !netif_carrier_ok(netdev)) { - printk(KERN_INFO "%s port %d, %s carrier is now ok\n", - netxen_nic_driver_name, port_num, netdev->name); - netif_carrier_on(netdev); - } - - if (netif_queue_stopped(netdev)) - netif_wake_queue(netdev); + netdev = adapter->netdev; + if ((netif_running(netdev)) && !netif_carrier_ok(netdev)) { + printk(KERN_INFO "%s port %d, %s carrier is now ok\n", + netxen_nic_driver_name, adapter->portnum, netdev->name); + netif_carrier_on(netdev); } + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + if (adapter->handle_phy_intr) adapter->handle_phy_intr(adapter); mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); @@ -1074,9 +1060,8 @@ void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, struct status_desc *desc) { - struct netxen_port *port = adapter->port[netxen_get_sts_port(desc)]; - struct pci_dev *pdev = port->pdev; - struct net_device *netdev = port->netdev; + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; int index = netxen_get_sts_refhandle(desc); struct netxen_recv_context *recv_ctx = &(adapter->recv_ctx[ctxid]); struct netxen_rx_buffer *buffer; @@ -1126,10 +1111,9 @@ netxen_process_rcv(struct netxen_adapter skb = (struct sk_buff *)buffer->skb; if (likely(netxen_get_sts_status(desc) == STATUS_CKSUM_OK)) { - port->stats.csummed++; + adapter->stats.csummed++; skb->ip_summed = CHECKSUM_UNNECESSARY; } - skb->dev = netdev; if (desc_ctx == RCV_DESC_LRO_CTXID) { /* True length was only available on the last pkt */ skb_put(skb, buffer->lro_length); @@ -1147,27 +1131,27 @@ netxen_process_rcv(struct netxen_adapter */ switch (ret) { case NET_RX_SUCCESS: - port->stats.uphappy++; + adapter->stats.uphappy++; break; case NET_RX_CN_LOW: - port->stats.uplcong++; + adapter->stats.uplcong++; break; case NET_RX_CN_MOD: - port->stats.upmcong++; + adapter->stats.upmcong++; break; case NET_RX_CN_HIGH: - port->stats.uphcong++; + adapter->stats.uphcong++; break; case NET_RX_DROP: - port->stats.updropped++; + adapter->stats.updropped++; break; default: - port->stats.updunno++; + adapter->stats.updunno++; break; } @@ -1179,14 +1163,13 @@ netxen_process_rcv(struct netxen_adapter /* * We just consumed one buffer so post a buffer. */ - adapter->stats.post_called++; buffer->skb = NULL; buffer->state = NETXEN_BUFFER_FREE; buffer->lro_current_frags = 0; buffer->lro_expected_frags = 0; - port->stats.no_rcv++; - port->stats.rxbytes += length; + adapter->stats.no_rcv++; + adapter->stats.rxbytes += length; } /* Process Receive status ring */ @@ -1227,7 +1210,6 @@ u32 netxen_process_rcv_ring(struct netxe /* update the consumer index in phantom */ if (count) { - adapter->stats.process_rcv++; recv_ctx->status_rx_consumer = consumer; recv_ctx->status_rx_producer = producer; @@ -1250,13 +1232,10 @@ int netxen_process_cmd_ring(unsigned lon int count1 = 0; int count2 = 0; struct netxen_cmd_buffer *buffer; - struct netxen_port *port; /* port #1 */ - struct netxen_port *nport; struct pci_dev *pdev; struct netxen_skb_frag *frag; u32 i; struct sk_buff *skb = NULL; - int p; int done; spin_lock(&adapter->tx_lock); @@ -1277,7 +1256,6 @@ int netxen_process_cmd_ring(unsigned lon } adapter->proc_cmd_buf_counter++; - adapter->stats.process_xmit++; /* * Not needed - does not seem to be used anywhere. * adapter->cmd_consumer = consumer; @@ -1286,8 +1264,7 @@ int netxen_process_cmd_ring(unsigned lon while ((last_consumer != consumer) && (count1 < MAX_STATUS_HANDLE)) { buffer = &adapter->cmd_buf_arr[last_consumer]; - port = adapter->port[buffer->port]; - pdev = port->pdev; + pdev = adapter->pdev; frag = &buffer->frag_array[0]; skb = buffer->skb; if (skb && (cmpxchg(&buffer->skb, skb, 0) == skb)) { @@ -1300,24 +1277,23 @@ int netxen_process_cmd_ring(unsigned lon PCI_DMA_TODEVICE); } - port->stats.skbfreed++; + adapter->stats.skbfreed++; dev_kfree_skb_any(skb); skb = NULL; } else if (adapter->proc_cmd_buf_counter == 1) { - port->stats.txnullskb++; + adapter->stats.txnullskb++; } - if (unlikely(netif_queue_stopped(port->netdev) - && netif_carrier_ok(port->netdev)) - && ((jiffies - port->netdev->trans_start) > - port->netdev->watchdog_timeo)) { - SCHEDULE_WORK(&port->tx_timeout_task); + if (unlikely(netif_queue_stopped(adapter->netdev) + && netif_carrier_ok(adapter->netdev)) + && ((jiffies - adapter->netdev->trans_start) > + adapter->netdev->watchdog_timeo)) { + SCHEDULE_WORK(&adapter->tx_timeout_task); } last_consumer = get_next_index(last_consumer, adapter->max_tx_desc_count); count1++; } - adapter->stats.noxmitdone += count1; count2 = 0; spin_lock(&adapter->tx_lock); @@ -1337,13 +1313,10 @@ int netxen_process_cmd_ring(unsigned lon } } if (count1 || count2) { - for (p = 0; p < adapter->ahw.max_ports; p++) { - nport = adapter->port[p]; - if (netif_queue_stopped(nport->netdev) - && (nport->flags & NETXEN_NETDEV_STATUS)) { - netif_wake_queue(nport->netdev); - nport->flags &= ~NETXEN_NETDEV_STATUS; - } + if (netif_queue_stopped(adapter->netdev) + && (adapter->flags & NETXEN_NETDEV_STATUS)) { + netif_wake_queue(adapter->netdev); + adapter->flags &= ~NETXEN_NETDEV_STATUS; } } /* @@ -1389,7 +1362,6 @@ void netxen_post_rx_buffers(struct netxe netxen_ctx_msg msg = 0; dma_addr_t dma; - adapter->stats.post_called++; rcv_desc = &recv_ctx->rcv_desc[ringid]; producer = rcv_desc->producer; @@ -1442,8 +1414,6 @@ #endif if (count) { rcv_desc->begin_alloc = index; rcv_desc->rcv_pending += count; - adapter->stats.lastposted = count; - adapter->stats.posted += count; rcv_desc->producer = producer; if (rcv_desc->rcv_free >= 32) { rcv_desc->rcv_free = 0; @@ -1451,7 +1421,8 @@ #endif writel((producer - 1) & (rcv_desc->max_rx_desc_count - 1), NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[0]. + recv_crb_registers[ + adapter->portnum]. rcv_desc_crb[ringid]. crb_rcv_producer_offset)); /* @@ -1464,7 +1435,7 @@ #endif ((producer - 1) & (rcv_desc-> max_rx_desc_count - 1))); - netxen_set_msg_ctxid(msg, 0); + netxen_set_msg_ctxid(msg, adapter->portnum); netxen_set_msg_opcode(msg, NETXEN_RCV_PRODUCER(ringid)); writel(msg, DB_NORMALIZE(adapter, @@ -1486,7 +1457,6 @@ void netxen_post_rx_buffers_nodb(struct int count = 0; int index = 0; - adapter->stats.post_called++; rcv_desc = &recv_ctx->rcv_desc[ringid]; producer = rcv_desc->producer; @@ -1533,8 +1503,6 @@ void netxen_post_rx_buffers_nodb(struct if (count) { rcv_desc->begin_alloc = index; rcv_desc->rcv_pending += count; - adapter->stats.lastposted = count; - adapter->stats.posted += count; rcv_desc->producer = producer; if (rcv_desc->rcv_free >= 32) { rcv_desc->rcv_free = 0; @@ -1542,7 +1510,8 @@ void netxen_post_rx_buffers_nodb(struct writel((producer - 1) & (rcv_desc->max_rx_desc_count - 1), NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[0]. + recv_crb_registers[ + adapter->portnum]. rcv_desc_crb[ringid]. crb_rcv_producer_offset)); wmb(); @@ -1563,13 +1532,7 @@ int netxen_nic_tx_has_work(struct netxen void netxen_nic_clear_stats(struct netxen_adapter *adapter) { - struct netxen_port *port; - int port_num; - memset(&adapter->stats, 0, sizeof(adapter->stats)); - for (port_num = 0; port_num < adapter->ahw.max_ports; port_num++) { - port = adapter->port[port_num]; - memset(&port->stats, 0, sizeof(port->stats)); - } + return; } diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c index be366e4..b213b06 100644 --- a/drivers/net/netxen/netxen_nic_isr.c +++ b/drivers/net/netxen/netxen_nic_isr.c @@ -6,12 +6,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, @@ -40,35 +40,35 @@ #include "netxen_nic_phan_reg.h" */ struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct net_device_stats *stats = &port->net_stats; + struct netxen_adapter *adapter = netdev_priv(netdev); + struct net_device_stats *stats = &adapter->net_stats; memset(stats, 0, sizeof(*stats)); /* total packets received */ - stats->rx_packets = port->stats.no_rcv; + stats->rx_packets = adapter->stats.no_rcv; /* total packets transmitted */ - stats->tx_packets = port->stats.xmitedframes + port->stats.xmitfinished; + stats->tx_packets = adapter->stats.xmitedframes + + adapter->stats.xmitfinished; /* total bytes received */ - stats->rx_bytes = port->stats.rxbytes; + stats->rx_bytes = adapter->stats.rxbytes; /* total bytes transmitted */ - stats->tx_bytes = port->stats.txbytes; + stats->tx_bytes = adapter->stats.txbytes; /* bad packets received */ - stats->rx_errors = port->stats.rcvdbadskb; + stats->rx_errors = adapter->stats.rcvdbadskb; /* packet transmit problems */ - stats->tx_errors = port->stats.nocmddescriptor; + stats->tx_errors = adapter->stats.nocmddescriptor; /* no space in linux buffers */ - stats->rx_dropped = port->stats.updropped; + stats->rx_dropped = adapter->stats.updropped; /* no space available in linux */ - stats->tx_dropped = port->stats.txdropped; + stats->tx_dropped = adapter->stats.txdropped; return stats; } -void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 portno, - u32 link) +void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link) { - struct net_device *netdev = (adapter->port[portno])->netdev; + struct net_device *netdev = adapter->netdev; if (link) netif_carrier_on(netdev); @@ -76,15 +76,13 @@ void netxen_indicate_link_status(struct netif_carrier_off(netdev); } -void netxen_handle_port_int(struct netxen_adapter *adapter, u32 portno, - u32 enable) +void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable) { __u32 int_src; - struct netxen_port *port; /* This should clear the interrupt source */ if (adapter->phy_read) - adapter->phy_read(adapter, portno, + adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS, &int_src); if (int_src == 0) { @@ -92,9 +90,7 @@ void netxen_handle_port_int(struct netxe return; } if (adapter->disable_phy_interrupts) - adapter->disable_phy_interrupts(adapter, portno); - - port = adapter->port[portno]; + adapter->disable_phy_interrupts(adapter); if (netxen_get_phy_int_jabber(int_src)) DPRINTK(INFO, "Jabber interrupt \n"); @@ -115,64 +111,57 @@ void netxen_handle_port_int(struct netxe DPRINTK(INFO, "SPEED CHANGED OR LINK STATUS CHANGED \n"); if (adapter->phy_read - && adapter->phy_read(adapter, portno, + && adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) == 0) { if (netxen_get_phy_int_link_status_changed(int_src)) { if (netxen_get_phy_link(status)) { - netxen_niu_gbe_init_port(adapter, - portno); - printk("%s: %s Link UP\n", + printk(KERN_INFO "%s: %s Link UP\n", netxen_nic_driver_name, - port->netdev->name); + adapter->netdev->name); } else { - printk("%s: %s Link DOWN\n", + printk(KERN_INFO "%s: %s Link DOWN\n", netxen_nic_driver_name, - port->netdev->name); + adapter->netdev->name); } - netxen_indicate_link_status(adapter, portno, + netxen_indicate_link_status(adapter, netxen_get_phy_link (status)); } } } if (adapter->enable_phy_interrupts) - adapter->enable_phy_interrupts(adapter, portno); + adapter->enable_phy_interrupts(adapter); } void netxen_nic_isr_other(struct netxen_adapter *adapter) { - u32 portno; + int portno = adapter->portnum; u32 val, linkup, qg_linksup; /* verify the offset */ val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); + val = val >> physical_port[adapter->portnum]; if (val == adapter->ahw.qg_linksup) return; qg_linksup = adapter->ahw.qg_linksup; adapter->ahw.qg_linksup = val; DPRINTK(INFO, "link update 0x%08x\n", val); - for (portno = 0; portno < NETXEN_NIU_MAX_GBE_PORTS; portno++) { - linkup = val & 1; - if (linkup != (qg_linksup & 1)) { - printk(KERN_INFO "%s: %s PORT %d link %s\n", - adapter->port[portno]->netdev->name, - netxen_nic_driver_name, portno, - ((linkup == 0) ? "down" : "up")); - netxen_indicate_link_status(adapter, portno, linkup); - if (linkup) - netxen_nic_set_link_parameters(adapter-> - port[portno]); - } - val = val >> 1; - qg_linksup = qg_linksup >> 1; - } + linkup = val & 1; - adapter->stats.otherints++; + if (linkup != (qg_linksup & 1)) { + printk(KERN_INFO "%s: %s PORT %d link %s\n", + adapter->netdev->name, + netxen_nic_driver_name, portno, + ((linkup == 0) ? "down" : "up")); + netxen_indicate_link_status(adapter, linkup); + if (linkup) + netxen_nic_set_link_parameters(adapter); + } } void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter) @@ -182,26 +171,28 @@ void netxen_nic_gbe_handle_phy_intr(stru void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter) { - struct net_device *netdev = adapter->port[0]->netdev; - u32 val; + struct net_device *netdev = adapter->netdev; + u32 val, val1; /* WINDOW = 1 */ val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); + val >>= (physical_port[adapter->portnum] * 8); + val1 = val & 0xff; - if (adapter->ahw.xg_linkup == 1 && val != XG_LINK_UP) { + if (adapter->ahw.xg_linkup == 1 && val1 != XG_LINK_UP) { printk(KERN_INFO "%s: %s NIC Link is down\n", netxen_nic_driver_name, netdev->name); adapter->ahw.xg_linkup = 0; /* read twice to clear sticky bits */ /* WINDOW = 0 */ - netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val); - netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val); + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val1); + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val1); if ((val & 0xffb) != 0xffb) { printk(KERN_INFO "%s ISR: Sync/Align BAD: 0x%08x\n", - netxen_nic_driver_name, val); + netxen_nic_driver_name, val1); } - } else if (adapter->ahw.xg_linkup == 0 && val == XG_LINK_UP) { + } else if (adapter->ahw.xg_linkup == 0 && val1 == XG_LINK_UP) { printk(KERN_INFO "%s: %s NIC Link is up\n", netxen_nic_driver_name, netdev->name); adapter->ahw.xg_linkup = 1; diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 7d2525e..4e32bb6 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -36,11 +36,11 @@ #include #include "netxen_nic_hw.h" #include "netxen_nic.h" -#define DEFINE_GLOBAL_RECV_CRB #include "netxen_nic_phan_reg.h" #include #include +#include MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver"); MODULE_LICENSE("GPL"); @@ -76,6 +76,8 @@ static void netxen_nic_poll_controller(s #endif static irqreturn_t netxen_intr(int irq, void *data); +int physical_port[] = {0, 1, 2, 3}; + /* PCI Device ID Table */ static struct pci_device_id netxen_pci_tbl[] __devinitdata = { {PCI_DEVICE(0x4040, 0x0001)}, @@ -93,6 +95,67 @@ MODULE_DEVICE_TABLE(pci, netxen_pci_tbl) struct workqueue_struct *netxen_workq; static void netxen_watchdog(unsigned long); +static inline void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter, + uint32_t crb_producer) +{ + switch (adapter->portnum) { + case 0: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET)); + return; + case 1: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET_1)); + return; + case 2: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET_2)); + return; + case 3: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET_3)); + return; + default: + printk(KERN_WARNING "We tried to update " + "CRB_CMD_PRODUCER_OFFSET for invalid " + "PCI function id %d\n", + adapter->portnum); + return; + } +} + +static inline void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter, + u32 crb_consumer) +{ + switch (adapter->portnum) { + case 0: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET)); + return; + case 1: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET_1)); + return; + case 2: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET_2)); + return; + case 3: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET_3)); + return; + default: + printk(KERN_WARNING "We tried to update " + "CRB_CMD_PRODUCER_OFFSET for invalid " + "PCI function id %d\n", + adapter->portnum); + return; + } +} + +#define ADAPTER_LIST_SIZE 12 +int netxen_cards_found; + /* * netxen_nic_probe() * @@ -110,26 +173,30 @@ netxen_nic_probe(struct pci_dev *pdev, c { struct net_device *netdev = NULL; struct netxen_adapter *adapter = NULL; - struct netxen_port *port = NULL; void __iomem *mem_ptr0 = NULL; void __iomem *mem_ptr1 = NULL; void __iomem *mem_ptr2 = NULL; + unsigned long first_page_group_end; + unsigned long first_page_group_start; + u8 __iomem *db_ptr = NULL; unsigned long mem_base, mem_len, db_base, db_len; - int pci_using_dac, i, err; + int pci_using_dac, i = 0, err; int ring; struct netxen_recv_context *recv_ctx = NULL; struct netxen_rcv_desc_ctx *rcv_desc = NULL; struct netxen_cmd_buffer *cmd_buf_arr = NULL; u64 mac_addr[FLASH_NUM_PORTS + 1]; int valid_mac = 0; + u32 val; + int pci_func_id = PCI_FUNC(pdev->devfn); printk(KERN_INFO "%s \n", netxen_nic_driver_string); - /* In current scheme, we use only PCI function 0 */ - if (PCI_FUNC(pdev->devfn) != 0) { - DPRINTK(ERR, "NetXen function %d will not be enabled.\n", - PCI_FUNC(pdev->devfn)); + + if (pdev->class != 0x020000) { + printk(KERN_ERR"NetXen function %d, class %x will not" + "be enabled.\n",pci_func_id, pdev->class); return -ENODEV; } if ((err = pci_enable_device(pdev))) @@ -156,18 +223,52 @@ netxen_nic_probe(struct pci_dev *pdev, c pci_using_dac = 0; } + + netdev = alloc_etherdev(sizeof(struct netxen_adapter)); + if(!netdev) { + printk(KERN_ERR"%s: Failed to allocate memory for the " + "device block.Check system memory resource" + " usage.\n", netxen_nic_driver_name); + goto err_out_free_res; + } + + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); + + adapter = netdev->priv; + memset(adapter, 0 , sizeof(struct netxen_adapter)); + + adapter->ahw.pdev = pdev; + adapter->ahw.pci_func = pci_func_id; + spin_lock_init(&adapter->tx_lock); + spin_lock_init(&adapter->lock); + /* remap phys address */ mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */ mem_len = pci_resource_len(pdev, 0); /* 128 Meg of memory */ - mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE); - mem_ptr1 = - ioremap(mem_base + SECOND_PAGE_GROUP_START, SECOND_PAGE_GROUP_SIZE); - mem_ptr2 = - ioremap(mem_base + THIRD_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE); + if (mem_len == NETXEN_PCI_128MB_SIZE) { + mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE); + mem_ptr1 = ioremap(mem_base + SECOND_PAGE_GROUP_START, + SECOND_PAGE_GROUP_SIZE); + mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START, + THIRD_PAGE_GROUP_SIZE); + first_page_group_start = FIRST_PAGE_GROUP_START; + first_page_group_end = FIRST_PAGE_GROUP_END; + } else if (mem_len == NETXEN_PCI_32MB_SIZE) { + mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE); + mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START - + SECOND_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE); + first_page_group_start = 0; + first_page_group_end = 0; + } else { + err = -EIO; + goto err_out_free_netdev; + } - if ((mem_ptr0 == 0UL) || (mem_ptr1 == 0UL) || (mem_ptr2 == 0UL)) { + if (((mem_ptr0 == 0UL) && (mem_len == NETXEN_PCI_128MB_SIZE)) || + (mem_ptr1 == 0UL) || (mem_ptr2 == 0UL)) { DPRINTK(ERR, "Cannot remap adapter memory aborting.:" "0 -> %p, 1 -> %p, 2 -> %p\n", @@ -197,30 +298,87 @@ netxen_nic_probe(struct pci_dev *pdev, c } DPRINTK(INFO, "doorbell ioremaped at %p\n", db_ptr); -/* - * Allocate a adapter structure which will manage all the initialization - * as well as the common resources for all ports... - * all the ports will have pointer to this adapter as well as Adapter - * will have pointers of all the ports structures. - */ + adapter->ahw.pci_base0 = mem_ptr0; + adapter->ahw.first_page_group_start = first_page_group_start; + adapter->ahw.first_page_group_end = first_page_group_end; + adapter->ahw.pci_base1 = mem_ptr1; + adapter->ahw.pci_base2 = mem_ptr2; + adapter->ahw.db_base = db_ptr; + adapter->ahw.db_len = db_len; - /* One adapter structure for all 4 ports.... */ - adapter = kzalloc(sizeof(struct netxen_adapter), GFP_KERNEL); - if (adapter == NULL) { - printk(KERN_ERR "%s: Could not allocate adapter memory:%d\n", - netxen_nic_driver_name, - (int)sizeof(struct netxen_adapter)); - err = -ENOMEM; - goto err_out_dbunmap; - } + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->portnum = pci_func_id; + + netdev->open = netxen_nic_open; + netdev->stop = netxen_nic_close; + netdev->hard_start_xmit = netxen_nic_xmit_frame; + netdev->get_stats = netxen_nic_get_stats; + netdev->set_multicast_list = netxen_nic_set_multi; + netdev->set_mac_address = netxen_nic_set_mac; + netdev->change_mtu = netxen_nic_change_mtu; + netdev->tx_timeout = netxen_tx_timeout; + netdev->watchdog_timeo = HZ; + + netxen_nic_change_mtu(netdev, netdev->mtu); + + SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops); + netdev->poll = netxen_nic_poll; + netdev->weight = NETXEN_NETDEV_WEIGHT; +#ifdef CONFIG_NET_POLL_CONTROLLER + netdev->poll_controller = netxen_nic_poll_controller; +#endif + /* ScatterGather support */ + netdev->features = NETIF_F_SG; + netdev->features |= NETIF_F_IP_CSUM; + netdev->features |= NETIF_F_TSO; + + if (pci_using_dac) + netdev->features |= NETIF_F_HIGHDMA; + + if (pci_enable_msi(pdev)) { + adapter->flags &= ~NETXEN_NIC_MSI_ENABLED; + printk(KERN_WARNING "%s: unable to allocate MSI interrupt" + " error\n", netxen_nic_driver_name); + } else + adapter->flags |= NETXEN_NIC_MSI_ENABLED; + + netdev->irq = pdev->irq; + INIT_WORK(&adapter->tx_timeout_task, netxen_tx_timeout_task); - adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS; - adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS; + /* + * Set the CRB window to invalid. If any register in window 0 is + * accessed it should set the window to 0 and then reset it to 1. + */ + adapter->curr_window = 255; + + /* initialize the adapter */ + netxen_initialize_adapter_hw(adapter); + +#ifdef CONFIG_PPC + if ((adapter->ahw.boardcfg.board_type == + NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) && + (pci_func_id == 2)) + goto err_out_free_adapter; +#endif /* CONFIG_PPC */ + + /* + * Adapter in our case is quad port so initialize it before + * initializing the ports + */ + + netxen_initialize_adapter_ops(adapter); + + adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS_HOST; + if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB35_4G) || + (adapter->ahw.boardcfg.board_type == + NETXEN_BRDTYPE_P2_SB31_2G)) + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G; + else + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS; adapter->max_jumbo_rx_desc_count = MAX_JUMBO_RCV_DESCRIPTORS; adapter->max_lro_rx_desc_count = MAX_LRO_RCV_DESCRIPTORS; - pci_set_drvdata(pdev, adapter); - cmd_buf_arr = (struct netxen_cmd_buffer *)vmalloc(TX_RINGSIZE); if (cmd_buf_arr == NULL) { printk(KERN_ERR @@ -230,6 +388,7 @@ netxen_nic_probe(struct pci_dev *pdev, c goto err_out_free_adapter; } memset(cmd_buf_arr, 0, TX_RINGSIZE); + adapter->cmd_buf_arr = cmd_buf_arr; for (i = 0; i < MAX_RCV_CTX; ++i) { recv_ctx = &adapter->recv_ctx[i]; @@ -277,33 +436,20 @@ netxen_nic_probe(struct pci_dev *pdev, c } - adapter->cmd_buf_arr = cmd_buf_arr; - adapter->ahw.pci_base0 = mem_ptr0; - adapter->ahw.pci_base1 = mem_ptr1; - adapter->ahw.pci_base2 = mem_ptr2; - adapter->ahw.db_base = db_ptr; - adapter->ahw.db_len = db_len; - spin_lock_init(&adapter->tx_lock); - spin_lock_init(&adapter->lock); netxen_initialize_adapter_sw(adapter); /* initialize the buffers in adapter */ -#ifdef CONFIG_IA64 - netxen_pinit_from_rom(adapter, 0); - udelay(500); - netxen_load_firmware(adapter); -#endif - /* - * Set the CRB window to invalid. If any register in window 0 is - * accessed it should set the window to 0 and then reset it to 1. - */ - adapter->curr_window = 255; - /* - * Adapter in our case is quad port so initialize it before - * initializing the ports - */ - netxen_initialize_adapter_hw(adapter); /* initialize the adapter */ + /* Mezz cards have PCI function 0,2,3 enabled */ + if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) + && (pci_func_id >= 2)) + adapter->portnum = pci_func_id - 2; - netxen_initialize_adapter_ops(adapter); +#ifdef CONFIG_IA64 + if(adapter->portnum == 0) { + netxen_pinit_from_rom(adapter, 0); + udelay(500); + netxen_load_firmware(adapter); + } +#endif init_timer(&adapter->watchdog_timer); adapter->ahw.xg_linkup = 0; @@ -314,12 +460,12 @@ #endif adapter->proc_cmd_buf_counter = 0; adapter->ahw.revision_id = nx_p2_id; - if (pci_enable_msi(pdev)) { - adapter->flags &= ~NETXEN_NIC_MSI_ENABLED; - printk(KERN_WARNING "%s: unable to allocate MSI interrupt" - " error\n", netxen_nic_driver_name); - } else - adapter->flags |= NETXEN_NIC_MSI_ENABLED; + /* make sure Window == 1 */ + netxen_nic_pci_change_crbwindow(adapter, 1); + + netxen_nic_update_cmd_producer(adapter, 0); + netxen_nic_update_cmd_consumer(adapter, 0); + writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_CMD_ADDR_LO)); if (netxen_is_flash_supported(adapter) == 0 && netxen_get_flash_mac_addr(adapter, mac_addr) == 0) @@ -327,153 +473,118 @@ #endif else valid_mac = 0; - /* - * Initialize all the CRB registers here. - */ - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_PRODUCER_OFFSET)); - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_CONSUMER_OFFSET)); - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_CMD_ADDR_LO)); - - /* do this before waking up pegs so that we have valid dummy dma addr */ - err = netxen_initialize_adapter_offload(adapter); - if (err) { - goto err_out_free_dev; + if (valid_mac) { + unsigned char *p = (unsigned char *)&mac_addr[adapter->portnum]; + netdev->dev_addr[0] = *(p + 5); + netdev->dev_addr[1] = *(p + 4); + netdev->dev_addr[2] = *(p + 3); + netdev->dev_addr[3] = *(p + 2); + netdev->dev_addr[4] = *(p + 1); + netdev->dev_addr[5] = *(p + 0); + + memcpy(netdev->perm_addr, netdev->dev_addr, + netdev->addr_len); + if (!is_valid_ether_addr(netdev->perm_addr)) { + printk(KERN_ERR "%s: Bad MAC address " + "%02x:%02x:%02x:%02x:%02x:%02x.\n", + netxen_nic_driver_name, + netdev->dev_addr[0], + netdev->dev_addr[1], + netdev->dev_addr[2], + netdev->dev_addr[3], + netdev->dev_addr[4], + netdev->dev_addr[5]); + } else { + if (adapter->macaddr_set) + adapter->macaddr_set(adapter, + netdev->dev_addr); + } } - /* Unlock the HW, prompting the boot sequence */ - writel(1, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_ROMUSB_GLB_PEGTUNE_DONE)); - - /* Handshake with the card before we register the devices. */ - netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); - - /* initialize the all the ports */ - adapter->active_ports = 0; - - for (i = 0; i < adapter->ahw.max_ports; i++) { - netdev = alloc_etherdev(sizeof(struct netxen_port)); - if (!netdev) { - printk(KERN_ERR "%s: could not allocate netdev for port" - " %d\n", netxen_nic_driver_name, i + 1); + if (adapter->portnum == 0) { + err = netxen_initialize_adapter_offload(adapter); + if (err) + goto err_out_free_rx_buffer; + val = readl(NETXEN_CRB_NORMALIZE(adapter, + NETXEN_CAM_RAM(0x1fc))); + if (val == 0x55555555) { + /* This is the first boot after power up */ + val = readl(NETXEN_CRB_NORMALIZE(adapter, + NETXEN_ROMUSB_GLB_SW_RESET)); + printk(KERN_INFO"NetXen: read 0x%08x for reset reg.\n",val); + if (val != 0x80000f) { + /* clear the register for future unloads/loads */ + writel(0, NETXEN_CRB_NORMALIZE(adapter, + NETXEN_CAM_RAM(0x1fc))); + printk(KERN_ERR "ERROR in NetXen HW init sequence.\n"); + err = -ENODEV; goto err_out_free_dev; - } + } - SET_MODULE_OWNER(netdev); - SET_NETDEV_DEV(netdev, &pdev->dev); - - port = netdev_priv(netdev); - port->netdev = netdev; - port->pdev = pdev; - port->adapter = adapter; - port->portnum = i; /* Gigabit port number from 0-3 */ - - netdev->open = netxen_nic_open; - netdev->stop = netxen_nic_close; - netdev->hard_start_xmit = netxen_nic_xmit_frame; - netdev->get_stats = netxen_nic_get_stats; - netdev->set_multicast_list = netxen_nic_set_multi; - netdev->set_mac_address = netxen_nic_set_mac; - netdev->change_mtu = netxen_nic_change_mtu; - netdev->tx_timeout = netxen_tx_timeout; - netdev->watchdog_timeo = HZ; - - netxen_nic_change_mtu(netdev, netdev->mtu); - - SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops); - netdev->poll = netxen_nic_poll; - netdev->weight = NETXEN_NETDEV_WEIGHT; -#ifdef CONFIG_NET_POLL_CONTROLLER - netdev->poll_controller = netxen_nic_poll_controller; -#endif - /* ScatterGather support */ - netdev->features = NETIF_F_SG; - netdev->features |= NETIF_F_IP_CSUM; - netdev->features |= NETIF_F_TSO; - - if (pci_using_dac) - netdev->features |= NETIF_F_HIGHDMA; - - if (valid_mac) { - unsigned char *p = (unsigned char *)&mac_addr[i]; - netdev->dev_addr[0] = *(p + 5); - netdev->dev_addr[1] = *(p + 4); - netdev->dev_addr[2] = *(p + 3); - netdev->dev_addr[3] = *(p + 2); - netdev->dev_addr[4] = *(p + 1); - netdev->dev_addr[5] = *(p + 0); - - memcpy(netdev->perm_addr, netdev->dev_addr, - netdev->addr_len); - if (!is_valid_ether_addr(netdev->perm_addr)) { - printk(KERN_ERR "%s: Bad MAC address " - "%02x:%02x:%02x:%02x:%02x:%02x.\n", - netxen_nic_driver_name, - netdev->dev_addr[0], - netdev->dev_addr[1], - netdev->dev_addr[2], - netdev->dev_addr[3], - netdev->dev_addr[4], - netdev->dev_addr[5]); - } else { - if (adapter->macaddr_set) - adapter->macaddr_set(port, - netdev->dev_addr); - } + /* clear the register for future unloads/loads */ + writel(0, NETXEN_CRB_NORMALIZE(adapter, + NETXEN_CAM_RAM(0x1fc))); } - INIT_WORK(&port->tx_timeout_task, netxen_tx_timeout_task); - netif_carrier_off(netdev); - netif_stop_queue(netdev); + printk(KERN_INFO "State: 0x%0x\n", + readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE))); - if ((err = register_netdev(netdev))) { - printk(KERN_ERR "%s: register_netdev failed port #%d" - " aborting\n", netxen_nic_driver_name, i + 1); - err = -EIO; - free_netdev(netdev); - goto err_out_free_dev; - } - adapter->port_count++; - adapter->port[i] = port; + /* + * Tell the hardware our version number. + */ + i = (_NETXEN_NIC_LINUX_MAJOR << 16) + | ((_NETXEN_NIC_LINUX_MINOR << 8)) + | (_NETXEN_NIC_LINUX_SUBVERSION); + writel(i, NETXEN_CRB_NORMALIZE(adapter, CRB_DRIVER_VERSION)); + + /* Unlock the HW, prompting the boot sequence */ + writel(1, + NETXEN_CRB_NORMALIZE(adapter, + NETXEN_ROMUSB_GLB_PEGTUNE_DONE)); + /* Handshake with the card before we register the devices. */ + netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); } - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); - netxen_pinit_from_rom(adapter, 0); - udelay(500); - netxen_load_firmware(adapter); - netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); + /* - * delay a while to ensure that the Pegs are up & running. - * Otherwise, we might see some flaky behaviour. + * See if the firmware gave us a virtual-physical port mapping. */ - udelay(100); + i = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_V2P(adapter->portnum))); + if (i != 0x55555555) + physical_port[adapter->portnum] = i; + + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + if ((err = register_netdev(netdev))) { + printk(KERN_ERR "%s: register_netdev failed port #%d" + " aborting\n", netxen_nic_driver_name, + adapter->portnum); + err = -EIO; + goto err_out_free_dev; + } + + pci_set_drvdata(pdev, adapter); switch (adapter->ahw.board_type) { - case NETXEN_NIC_GBE: - printk("%s: QUAD GbE board initialized\n", - netxen_nic_driver_name); - break; + case NETXEN_NIC_GBE: + printk(KERN_INFO "%s: QUAD GbE board initialized\n", + netxen_nic_driver_name); + break; - case NETXEN_NIC_XGBE: - printk("%s: XGbE board initialized\n", netxen_nic_driver_name); - break; + case NETXEN_NIC_XGBE: + printk(KERN_INFO "%s: XGbE board initialized\n", + netxen_nic_driver_name); + break; } adapter->driver_mismatch = 0; return 0; - err_out_free_dev: - if (adapter->flags & NETXEN_NIC_MSI_ENABLED) - pci_disable_msi(pdev); - for (i = 0; i < adapter->port_count; i++) { - port = adapter->port[i]; - if ((port) && (port->netdev)) { - unregister_netdev(port->netdev); - free_netdev(port->netdev); - } - } +err_out_free_dev: + if (adapter->portnum == 0) + netxen_free_adapter_offload(adapter); - netxen_free_adapter_offload(adapter); - - err_out_free_rx_buffer: +err_out_free_rx_buffer: for (i = 0; i < MAX_RCV_CTX; ++i) { recv_ctx = &adapter->recv_ctx[i]; for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) { @@ -486,15 +597,16 @@ #endif } vfree(cmd_buf_arr); - err_out_free_adapter: +err_out_free_adapter: + if (adapter->flags & NETXEN_NIC_MSI_ENABLED) + pci_disable_msi(pdev); + pci_set_drvdata(pdev, NULL); - kfree(adapter); - err_out_dbunmap: if (db_ptr) iounmap(db_ptr); - err_out_iounmap: +err_out_iounmap: if (mem_ptr0) iounmap(mem_ptr0); if (mem_ptr1) @@ -502,9 +614,13 @@ #endif if (mem_ptr2) iounmap(mem_ptr2); - err_out_free_res: +err_out_free_netdev: + free_netdev(netdev); + +err_out_free_res: pci_release_regions(pdev); - err_out_disable_pdev: + +err_out_disable_pdev: pci_disable_device(pdev); return err; } @@ -512,7 +628,7 @@ #endif static void __devexit netxen_nic_remove(struct pci_dev *pdev) { struct netxen_adapter *adapter; - struct netxen_port *port; + struct net_device *netdev; struct netxen_rx_buffer *buffer; struct netxen_recv_context *recv_ctx; struct netxen_rcv_desc_ctx *rcv_desc; @@ -523,38 +639,34 @@ static void __devexit netxen_nic_remove( if (adapter == NULL) return; + netdev = adapter->netdev; + + netxen_nic_disable_int(adapter); if (adapter->irq) free_irq(adapter->irq, adapter); - netxen_nic_stop_all_ports(adapter); - /* leave the hw in the same state as reboot */ - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); - netxen_pinit_from_rom(adapter, 0); - udelay(500); - netxen_load_firmware(adapter); - netxen_free_adapter_offload(adapter); - - mdelay(1000); /* Delay for a while to drain the DMA engines */ - for (i = 0; i < adapter->port_count; i++) { - port = adapter->port[i]; - if ((port) && (port->netdev)) { - unregister_netdev(port->netdev); - free_netdev(port->netdev); - } - } + + if (adapter->stop_port) + adapter->stop_port(adapter); if ((adapter->flags & NETXEN_NIC_MSI_ENABLED)) pci_disable_msi(pdev); - if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) - netxen_free_hw_resources(adapter); - iounmap(adapter->ahw.db_base); - iounmap(adapter->ahw.pci_base0); - iounmap(adapter->ahw.pci_base1); - iounmap(adapter->ahw.pci_base2); + if (adapter->portnum == 0) + netxen_free_adapter_offload(adapter); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); + if (adapter->irq) + free_irq(adapter->irq, adapter); + if(adapter->portnum == 0) { + /* leave the hw in the same state as reboot */ + writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); + netxen_pinit_from_rom(adapter, 0); + udelay(500); + netxen_load_firmware(adapter); + netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); + } + + if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) + netxen_free_hw_resources(adapter); for (ctxid = 0; ctxid < MAX_RCV_CTX; ++ctxid) { recv_ctx = &adapter->recv_ctx[ctxid]; @@ -574,8 +686,20 @@ static void __devexit netxen_nic_remove( } } + unregister_netdev(netdev); + vfree(adapter->cmd_buf_arr); - kfree(adapter); + + iounmap(adapter->ahw.db_base); + iounmap(adapter->ahw.pci_base0); + iounmap(adapter->ahw.pci_base1); + iounmap(adapter->ahw.pci_base2); + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + free_netdev(netdev); } /* @@ -584,8 +708,7 @@ static void __devexit netxen_nic_remove( */ static int netxen_nic_open(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = (struct netxen_adapter *)netdev->priv; int err = 0; int ctx, ring; @@ -596,8 +719,6 @@ static int netxen_nic_open(struct net_de return -EIO; } netxen_nic_flash_print(adapter); - if (adapter->init_niu) - adapter->init_niu(adapter); /* setup all the resources for the Phantom... */ /* this include the descriptors for rcv, tx, and status */ @@ -608,21 +729,14 @@ static int netxen_nic_open(struct net_de err); return err; } - if (adapter->init_port - && adapter->init_port(adapter, port->portnum) != 0) { - printk(KERN_ERR "%s: Failed to initialize port %d\n", - netxen_nic_driver_name, port->portnum); - netxen_free_hw_resources(adapter); - return -EIO; - } for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) netxen_post_rx_buffers(adapter, ctx, ring); } adapter->irq = adapter->ahw.pdev->irq; - err = request_irq(adapter->ahw.pdev->irq, &netxen_intr, - IRQF_SHARED | IRQF_SAMPLE_RANDOM, - netdev->name, adapter); + err = request_irq(adapter->ahw.pdev->irq, netxen_intr, + SA_SHIRQ | SA_SAMPLE_RANDOM, netdev->name, + adapter); if (err) { printk(KERN_ERR "request_irq failed with: %d\n", err); netxen_free_hw_resources(adapter); @@ -631,23 +745,28 @@ static int netxen_nic_open(struct net_de adapter->is_up = NETXEN_ADAPTER_UP_MAGIC; } - adapter->active_ports++; - if (adapter->active_ports == 1) { - if (!adapter->driver_mismatch) - mod_timer(&adapter->watchdog_timer, jiffies); + if (!adapter->driver_mismatch) + mod_timer(&adapter->watchdog_timer, jiffies); - netxen_nic_enable_int(adapter); - } + netxen_nic_enable_int(adapter); /* Done here again so that even if phantom sw overwrote it, * we set it */ if (adapter->macaddr_set) - adapter->macaddr_set(port, netdev->dev_addr); - netxen_nic_set_link_parameters(port); + adapter->macaddr_set(adapter, netdev->dev_addr); + if (adapter->init_port + && adapter->init_port(adapter, adapter->portnum) != 0) { + del_timer_sync(&adapter->watchdog_timer); + printk(KERN_ERR "%s: Failed to initialize port %d\n", + netxen_nic_driver_name, adapter->portnum); + return -EIO; + } + + netxen_nic_set_link_parameters(adapter); netxen_nic_set_multi(netdev); if (adapter->set_mtu) - adapter->set_mtu(port, netdev->mtu); + adapter->set_mtu(adapter, netdev->mtu); if (!adapter->driver_mismatch) netif_start_queue(netdev); @@ -660,8 +779,7 @@ static int netxen_nic_open(struct net_de */ static int netxen_nic_close(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); int i, j; struct netxen_cmd_buffer *cmd_buff; struct netxen_skb_frag *buffrag; @@ -669,47 +787,39 @@ static int netxen_nic_close(struct net_d netif_carrier_off(netdev); netif_stop_queue(netdev); - adapter->active_ports--; - - if (!adapter->active_ports) { - netxen_nic_disable_int(adapter); - cmd_buff = adapter->cmd_buf_arr; - for (i = 0; i < adapter->max_tx_desc_count; i++) { - buffrag = cmd_buff->frag_array; + cmd_buff = adapter->cmd_buf_arr; + for (i = 0; i < adapter->max_tx_desc_count; i++) { + buffrag = cmd_buff->frag_array; + if (buffrag->dma) { + pci_unmap_single(adapter->pdev, buffrag->dma, + buffrag->length, PCI_DMA_TODEVICE); + buffrag->dma = (u64) NULL; + } + for (j = 0; j < cmd_buff->frag_count; j++) { + buffrag++; if (buffrag->dma) { - pci_unmap_single(port->pdev, buffrag->dma, - buffrag->length, - PCI_DMA_TODEVICE); + pci_unmap_page(adapter->pdev, buffrag->dma, + buffrag->length, + PCI_DMA_TODEVICE); buffrag->dma = (u64) NULL; } - for (j = 0; j < cmd_buff->frag_count; j++) { - buffrag++; - if (buffrag->dma) { - pci_unmap_page(port->pdev, - buffrag->dma, - buffrag->length, - PCI_DMA_TODEVICE); - buffrag->dma = (u64) NULL; - } - } - /* Free the skb we received in netxen_nic_xmit_frame */ - if (cmd_buff->skb) { - dev_kfree_skb_any(cmd_buff->skb); - cmd_buff->skb = NULL; - } - cmd_buff++; } - FLUSH_SCHEDULED_WORK(); - del_timer_sync(&adapter->watchdog_timer); + /* Free the skb we received in netxen_nic_xmit_frame */ + if (cmd_buff->skb) { + dev_kfree_skb_any(cmd_buff->skb); + cmd_buff->skb = NULL; + } + cmd_buff++; } + FLUSH_SCHEDULED_WORK(); + del_timer_sync(&adapter->watchdog_timer); return 0; } static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); struct netxen_hardware_context *hw = &adapter->ahw; unsigned int first_seg_len = skb->len - skb->data_len; struct netxen_skb_frag *buffrag; @@ -727,12 +837,12 @@ static int netxen_nic_xmit_frame(struct u32 last_cmd_consumer = 0; int no_of_desc; - port->stats.xmitcalled++; + adapter->stats.xmitcalled++; frag_count = skb_shinfo(skb)->nr_frags + 1; if (unlikely(skb->len <= 0)) { dev_kfree_skb_any(skb); - port->stats.badskblen++; + adapter->stats.badskblen++; return NETDEV_TX_OK; } @@ -741,7 +851,7 @@ static int netxen_nic_xmit_frame(struct "too large, can handle only %d frags\n", netxen_nic_driver_name, netdev->name, frag_count, MAX_BUFFERS_PER_CMD); - port->stats.txdropped++; + adapter->stats.txdropped++; if ((++dropped_packet & 0xff) == 0xff) printk("%s: %s droppped packets = %d\n", netxen_nic_driver_name, netdev->name, @@ -758,7 +868,7 @@ static int netxen_nic_xmit_frame(struct */ retry_getting_window: spin_lock_bh(&adapter->tx_lock); - if (adapter->total_threads == MAX_XMIT_PRODUCERS) { + if (adapter->total_threads >= MAX_XMIT_PRODUCERS) { spin_unlock_bh(&adapter->tx_lock); /* * Yield CPU @@ -778,9 +888,8 @@ static int netxen_nic_xmit_frame(struct if (skb_shinfo(skb)->gso_size > 0) { no_of_desc++; - if (((skb->nh.iph)->ihl * sizeof(u32)) + - ((skb->h.th)->doff * sizeof(u32)) + - sizeof(struct ethhdr) > + if ((ip_hdrlen(skb) + tcp_hdrlen(skb) + + sizeof(struct ethhdr)) > (sizeof(struct cmd_desc_type0) - 2)) { no_of_desc++; } @@ -792,15 +901,8 @@ static int netxen_nic_xmit_frame(struct if ((k + no_of_desc) >= ((last_cmd_consumer <= k) ? last_cmd_consumer + max_tx_desc_count : last_cmd_consumer)) { - port->stats.nocmddescriptor++; - DPRINTK(ERR, "No command descriptors available," - " producer = %d, consumer = %d count=%llu," - " dropping packet\n", producer, - adapter->last_cmd_consumer, - port->stats.nocmddescriptor); - netif_stop_queue(netdev); - port->flags |= NETXEN_NETDEV_STATUS; + adapter->flags |= NETXEN_NETDEV_STATUS; spin_unlock_bh(&adapter->tx_lock); return NETDEV_TX_BUSY; } @@ -828,16 +930,17 @@ static int netxen_nic_xmit_frame(struct pbuf->skb = skb; pbuf->cmd = TX_ETHER_PKT; pbuf->frag_count = frag_count; - pbuf->port = port->portnum; + pbuf->port = adapter->portnum; buffrag = &pbuf->frag_array[0]; - buffrag->dma = pci_map_single(port->pdev, skb->data, first_seg_len, + buffrag->dma = pci_map_single(adapter->pdev, skb->data, first_seg_len, PCI_DMA_TODEVICE); buffrag->length = first_seg_len; netxen_set_cmd_desc_totallength(hwdesc, skb->len); netxen_set_cmd_desc_num_of_buff(hwdesc, frag_count); netxen_set_cmd_desc_opcode(hwdesc, TX_ETHER_PKT); - netxen_set_cmd_desc_port(hwdesc, port->portnum); + netxen_set_cmd_desc_port(hwdesc, adapter->portnum); + netxen_set_cmd_desc_ctxid(hwdesc, adapter->portnum); hwdesc->buffer1_length = cpu_to_le16(first_seg_len); hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma); @@ -860,7 +963,7 @@ static int netxen_nic_xmit_frame(struct offset = frag->page_offset; temp_len = len; - temp_dma = pci_map_page(port->pdev, frag->page, offset, + temp_dma = pci_map_page(adapter->pdev, frag->page, offset, len, PCI_DMA_TODEVICE); buffrag++; @@ -920,26 +1023,36 @@ static int netxen_nic_xmit_frame(struct /* copy the next 64 bytes - should be enough except * for pathological case */ - memcpy((void *)hwdesc, (void *)(skb->data) + - first_hdr_len, hdr_len - first_hdr_len); + skb_copy_from_linear_data_offset(skb, first_hdr_len, + hwdesc, + (hdr_len - + first_hdr_len)); producer = get_next_index(producer, max_tx_desc_count); } } + + i = netxen_get_cmd_desc_totallength(&hw->cmd_desc_head[saved_producer]); + + hw->cmd_desc_head[saved_producer].flags_opcode = + cpu_to_le16(hw->cmd_desc_head[saved_producer].flags_opcode); + hw->cmd_desc_head[saved_producer].num_of_buffers_total_length = + cpu_to_le32(hw->cmd_desc_head[saved_producer]. + num_of_buffers_total_length); + spin_lock_bh(&adapter->tx_lock); - port->stats.txbytes += - netxen_get_cmd_desc_totallength(&hw->cmd_desc_head[saved_producer]); + adapter->stats.txbytes += i; + /* Code to update the adapter considering how many producer threads are currently working */ if ((--adapter->num_threads) == 0) { /* This is the last thread */ u32 crb_producer = adapter->cmd_producer; - writel(crb_producer, - NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_PRODUCER_OFFSET)); + netxen_nic_update_cmd_producer(adapter, crb_producer); wmb(); adapter->total_threads = 0; } - port->stats.xmitfinished++; + adapter->stats.xmitfinished++; spin_unlock_bh(&adapter->tx_lock); netdev->trans_start = jiffies; @@ -959,27 +1072,26 @@ static void netxen_watchdog(unsigned lon static void netxen_tx_timeout(struct net_device *netdev) { - struct netxen_port *port = (struct netxen_port *)netdev_priv(netdev); - - SCHEDULE_WORK(&port->tx_timeout_task); + struct netxen_adapter *adapter = (struct netxen_adapter *) + netdev_priv(netdev); + SCHEDULE_WORK(&adapter->tx_timeout_task); } static void netxen_tx_timeout_task(struct work_struct *work) { - struct netxen_port *port = - container_of(work, struct netxen_port, tx_timeout_task); - struct net_device *netdev = port->netdev; + struct netxen_adapter *adapter = + container_of(work, struct netxen_adapter, tx_timeout_task); unsigned long flags; printk(KERN_ERR "%s %s: transmit timeout, resetting.\n", - netxen_nic_driver_name, netdev->name); - - spin_lock_irqsave(&port->adapter->lock, flags); - netxen_nic_close(netdev); - netxen_nic_open(netdev); - spin_unlock_irqrestore(&port->adapter->lock, flags); - netdev->trans_start = jiffies; - netif_wake_queue(netdev); + netxen_nic_driver_name, adapter->netdev->name); + + spin_lock_irqsave(&adapter->lock, flags); + netxen_nic_close(adapter->netdev); + netxen_nic_open(adapter->netdev); + spin_unlock_irqrestore(&adapter->lock, flags); + adapter->netdev->trans_start = jiffies; + netif_wake_queue(adapter->netdev); } static int @@ -988,17 +1100,16 @@ netxen_handle_int(struct netxen_adapter u32 ret = 0; DPRINTK(INFO, "Entered handle ISR\n"); - adapter->stats.ints++; if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) { int count = 0; u32 mask; - mask = readl(pci_base_offset(adapter, ISR_INT_VECTOR)); - if ((mask & 0x80) == 0) { - /* not our interrupt */ + u32 our_int = 0; + our_int = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR)); + /* not our interrupt */ + if ((our_int & (0x80 << adapter->portnum)) == 0) return ret; - } netxen_nic_disable_int(adapter); /* Window = 0 or 1 */ do { @@ -1010,7 +1121,6 @@ netxen_handle_int(struct netxen_adapter printk("Could not disable interrupt completely\n"); } - adapter->stats.hostints++; if (netxen_nic_rx_has_work(adapter) || netxen_nic_tx_has_work(adapter)) { if (netif_rx_schedule_prep(netdev)) { @@ -1044,33 +1154,24 @@ netxen_handle_int(struct netxen_adapter irqreturn_t netxen_intr(int irq, void *data) { struct netxen_adapter *adapter; - struct netxen_port *port; struct net_device *netdev; - int i; if (unlikely(!irq)) { return IRQ_NONE; /* Not our interrupt */ } adapter = (struct netxen_adapter *)data; - for (i = 0; i < adapter->ahw.max_ports; i++) { - port = adapter->port[i]; - netdev = port->netdev; - - /* process our status queue (for all 4 ports) */ - if (netif_running(netdev)) { - netxen_handle_int(adapter, netdev); - break; - } - } + netdev = adapter->netdev; + /* process our status queue (for all 4 ports) */ + if (netif_running(netdev)) + netxen_handle_int(adapter, netdev); return IRQ_HANDLED; } static int netxen_nic_poll(struct net_device *netdev, int *budget) { - struct netxen_port *port = (struct netxen_port *)netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); int work_to_do = min(*budget, netdev->quota); int done = 1; int ctx; @@ -1078,7 +1179,6 @@ static int netxen_nic_poll(struct net_de int work_done = 0; DPRINTK(INFO, "polling for %d descriptors\n", *budget); - port->stats.polled++; work_done = 0; for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { @@ -1122,8 +1222,7 @@ static int netxen_nic_poll(struct net_de #ifdef CONFIG_NET_POLL_CONTROLLER static void netxen_nic_poll_controller(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); disable_irq(adapter->irq); netxen_intr(adapter->irq, adapter); enable_irq(adapter->irq); diff --git a/drivers/net/netxen/netxen_nic_niu.c b/drivers/net/netxen/netxen_nic_niu.c index d5d9507..cef90a7 100644 --- a/drivers/net/netxen/netxen_nic_niu.c +++ b/drivers/net/netxen/netxen_nic_niu.c @@ -88,12 +88,13 @@ static inline int phy_unlock(struct netx * -1 on error * */ -int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long phy, - long reg, __u32 * readval) +int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, + __u32 * readval) { long timeout = 0; long result = 0; long restore = 0; + long phy = physical_port[adapter->portnum]; __u32 address; __u32 command; __u32 status; @@ -183,12 +184,13 @@ int netxen_niu_gbe_phy_read(struct netxe * -1 on error * */ -int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, - long phy, long reg, __u32 val) +int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, + __u32 val) { long timeout = 0; long result = 0; long restore = 0; + long phy = physical_port[adapter->portnum]; __u32 address; __u32 command; __u32 status; @@ -258,15 +260,13 @@ int netxen_niu_gbe_phy_write(struct netx return result; } -int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter) { netxen_crb_writelit_adapter(adapter, NETXEN_NIU_INT_MASK, 0x3f); return 0; } -int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter) { int result = 0; __u32 enable = 0; @@ -275,7 +275,7 @@ int netxen_niu_gbe_enable_phy_interrupts netxen_set_phy_int_speed_changed(enable); if (0 != - netxen_niu_gbe_phy_write(adapter, port, + netxen_niu_gbe_phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_ENABLE, enable)) result = -EIO; @@ -283,38 +283,34 @@ int netxen_niu_gbe_enable_phy_interrupts return result; } -int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter) { netxen_crb_writelit_adapter(adapter, NETXEN_NIU_INT_MASK, 0x7f); return 0; } -int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter) { int result = 0; if (0 != - netxen_niu_gbe_phy_write(adapter, port, + netxen_niu_gbe_phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_ENABLE, 0)) result = -EIO; return result; } -int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter) { netxen_crb_writelit_adapter(adapter, NETXEN_NIU_ACTIVE_INT, -1); return 0; } -int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter) { int result = 0; if (0 != - netxen_niu_gbe_phy_write(adapter, port, + netxen_niu_gbe_phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS, -EIO)) result = -EIO; @@ -355,9 +351,9 @@ void netxen_niu_gbe_set_mii_mode(struct 0x5); } - if (netxen_niu_gbe_enable_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_enable_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n"); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); } @@ -393,9 +389,9 @@ void netxen_niu_gbe_set_gmii_mode(struct 0x5); } - if (netxen_niu_gbe_enable_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_enable_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n"); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); } @@ -404,11 +400,11 @@ int netxen_niu_gbe_init_port(struct netx int result = 0; __u32 status; if (adapter->disable_phy_interrupts) - adapter->disable_phy_interrupts(adapter, port); + adapter->disable_phy_interrupts(adapter); mdelay(2); if (0 == - netxen_niu_gbe_phy_read(adapter, port, + netxen_niu_gbe_phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status)) { if (netxen_get_phy_link(status)) { @@ -439,13 +435,13 @@ int netxen_niu_gbe_init_port(struct netx | NETXEN_GB_MAC_ENABLE_TX_RX | NETXEN_GB_MAC_PAUSED_FRMS); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); - if (netxen_niu_gbe_enable_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_enable_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n"); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); result = -1; @@ -458,26 +454,18 @@ int netxen_niu_gbe_init_port(struct netx int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port) { - u32 reg = 0, ret = 0; + u32 reg; + u32 portnum = physical_port[adapter->portnum]; - if (adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) { - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XG1_CONFIG_0, 0x5); - /* XXX hack for Mez cards: both ports in promisc mode */ - netxen_nic_hw_read_wx(adapter, - NETXEN_NIU_XGE_CONFIG_1, ®, 4); - reg = (reg | 0x2000UL); - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XGE_CONFIG_1, reg); - reg = 0; - netxen_nic_hw_read_wx(adapter, - NETXEN_NIU_XG1_CONFIG_1, ®, 4); - reg = (reg | 0x2000UL); - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XG1_CONFIG_1, reg); - } + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_0+(0x10000*portnum), 0x5); + netxen_nic_hw_read_wx(adapter, + NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), ®, 4); + reg = (reg & ~0x2000UL); + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), reg); - return ret; + return 0; } /* @@ -498,7 +486,7 @@ int netxen_niu_gbe_handle_phy_interrupt( * The read of the PHY INT status will clear the pending * interrupt status */ - if (netxen_niu_gbe_phy_read(adapter, port, + if (netxen_niu_gbe_phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS, &int_src) != 0) result = -EINVAL; @@ -535,7 +523,7 @@ int netxen_niu_gbe_handle_phy_interrupt( printk(KERN_INFO PFX "speed_changed or link status changed"); if (netxen_niu_gbe_phy_read - (adapter, port, + (adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) == 0) { if (netxen_get_phy_speed(status) == 2) { @@ -581,10 +569,11 @@ int netxen_niu_gbe_handle_phy_interrupt( * Note that the passed-in value must already be in network byte order. */ int netxen_niu_macaddr_get(struct netxen_adapter *adapter, - int phy, netxen_ethernet_macaddr_t * addr) + netxen_ethernet_macaddr_t * addr) { u32 stationhigh; u32 stationlow; + int phy = physical_port[adapter->portnum]; u8 val[8]; if (addr == NULL) @@ -610,13 +599,12 @@ int netxen_niu_macaddr_get(struct netxen * Set the station MAC address. * Note that the passed-in value must already be in network byte order. */ -int netxen_niu_macaddr_set(struct netxen_port *port, +int netxen_niu_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr) { u8 temp[4]; u32 val; - struct netxen_adapter *adapter = port->adapter; - int phy = port->portnum; + int phy = physical_port[adapter->portnum]; unsigned char mac_addr[6]; int i; @@ -634,7 +622,7 @@ int netxen_niu_macaddr_set(struct netxen (adapter, NETXEN_NIU_GB_STATION_ADDR_0(phy), &val, 4)) return -2; - netxen_niu_macaddr_get(adapter, phy, + netxen_niu_macaddr_get(adapter, (netxen_ethernet_macaddr_t *) mac_addr); if (memcmp(mac_addr, addr, 6) == 0) break; @@ -642,7 +630,7 @@ int netxen_niu_macaddr_set(struct netxen if (i == 10) { printk(KERN_ERR "%s: cannot set Mac addr for %s\n", - netxen_nic_driver_name, port->netdev->name); + netxen_nic_driver_name, adapter->netdev->name); printk(KERN_ERR "MAC address set: " "%02x:%02x:%02x:%02x:%02x:%02x.\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); @@ -735,13 +723,13 @@ int netxen_niu_enable_gbe_port(struct ne } /* Disable a GbE interface */ -int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter, int port) +int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter) { __u32 mac_cfg0; + u32 port = physical_port[adapter->portnum]; if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) return -EINVAL; - mac_cfg0 = 0; netxen_gb_soft_reset(mac_cfg0); if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), @@ -751,13 +739,13 @@ int netxen_niu_disable_gbe_port(struct n } /* Disable an XG interface */ -int netxen_niu_disable_xg_port(struct netxen_adapter *adapter, int port) +int netxen_niu_disable_xg_port(struct netxen_adapter *adapter) { __u32 mac_cfg; + u32 port = physical_port[adapter->portnum]; if (port != 0) return -EINVAL; - mac_cfg = 0; netxen_xg_soft_reset(mac_cfg); if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_CONFIG_0, @@ -767,10 +755,11 @@ int netxen_niu_disable_xg_port(struct ne } /* Set promiscuous mode for a GbE interface */ -int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int port, +int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, netxen_niu_prom_mode_t mode) { __u32 reg; + u32 port = physical_port[adapter->portnum]; if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) return -EINVAL; @@ -824,25 +813,50 @@ int netxen_niu_set_promiscuous_mode(stru * Set the MAC address for an XG port * Note that the passed-in value must already be in network byte order. */ -int netxen_niu_xg_macaddr_set(struct netxen_port *port, +int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr) { + int phy = physical_port[adapter->portnum]; u8 temp[4]; u32 val; - struct netxen_adapter *adapter = port->adapter; + + if ((phy < 0) || (phy > NETXEN_NIU_MAX_XG_PORTS)) + return -EIO; temp[0] = temp[1] = 0; - memcpy(temp + 2, addr, 2); - val = le32_to_cpu(*(__le32 *)temp); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1, - &val, 4)) + switch (phy) { + case 0: + memcpy(temp + 2, addr, 2); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1, + &val, 4)) return -EIO; - memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32)); - val = le32_to_cpu(*(__le32 *)temp); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI, - &val, 4)) + memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32)); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI, + &val, 4)) + return -EIO; + break; + + case 1: + memcpy(temp + 2, addr, 2); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XG1_STATION_ADDR_0_1, + &val, 4)) + return -EIO; + + memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32)); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XG1_STATION_ADDR_0_HI, + &val, 4)) return -EIO; + break; + + default: + printk(KERN_ERR "Unknown port %d\n", phy); + break; + } return 0; } @@ -851,9 +865,10 @@ int netxen_niu_xg_macaddr_set(struct net * Return the current station MAC address. * Note that the passed-in value must already be in network byte order. */ -int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, int phy, +int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t * addr) { + int phy = physical_port[adapter->portnum]; u32 stationhigh; u32 stationlow; u8 val[8]; @@ -878,21 +893,24 @@ int netxen_niu_xg_macaddr_get(struct net } int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter, - int port, netxen_niu_prom_mode_t mode) + netxen_niu_prom_mode_t mode) { __u32 reg; + u32 port = physical_port[adapter->portnum]; - if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) return -EINVAL; - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_XGE_CONFIG_1, ®, 4)) - return -EIO; + if (netxen_nic_hw_read_wx(adapter, + NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), ®, 4)) + return -EIO; if (mode == NETXEN_NIU_PROMISC_MODE) reg = (reg | 0x2000UL); else reg = (reg & ~0x2000UL); - netxen_crb_writelit_adapter(adapter, NETXEN_NIU_XGE_CONFIG_1, reg); + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), reg); return 0; } diff --git a/drivers/net/netxen/netxen_nic_phan_reg.h b/drivers/net/netxen/netxen_nic_phan_reg.h index 0c7c943..9457fc7 100644 --- a/drivers/net/netxen/netxen_nic_phan_reg.h +++ b/drivers/net/netxen/netxen_nic_phan_reg.h @@ -100,8 +100,21 @@ #define CRB_JUMBO_BUFFER_CONS NETX #define CRB_CMD_PRODUCER_OFFSET_1 NETXEN_NIC_REG(0x1ac) #define CRB_CMD_CONSUMER_OFFSET_1 NETXEN_NIC_REG(0x1b0) +#define CRB_CMD_PRODUCER_OFFSET_2 NETXEN_NIC_REG(0x1b8) +#define CRB_CMD_CONSUMER_OFFSET_2 NETXEN_NIC_REG(0x1bc) + +// 1c0 to 1cc used for signature reg +#define CRB_CMD_PRODUCER_OFFSET_3 NETXEN_NIC_REG(0x1d0) +#define CRB_CMD_CONSUMER_OFFSET_3 NETXEN_NIC_REG(0x1d4) #define CRB_TEMP_STATE NETXEN_NIC_REG(0x1b4) +#define CRB_V2P_0 NETXEN_NIC_REG(0x290) +#define CRB_V2P_1 NETXEN_NIC_REG(0x294) +#define CRB_V2P_2 NETXEN_NIC_REG(0x298) +#define CRB_V2P_3 NETXEN_NIC_REG(0x29c) +#define CRB_V2P(port) (CRB_V2P_0+((port)*4)) +#define CRB_DRIVER_VERSION NETXEN_NIC_REG(0x2a0) + /* used for ethtool tests */ #define CRB_SCRATCHPAD_TEST NETXEN_NIC_REG(0x280) @@ -139,128 +152,13 @@ struct netxen_recv_crb { }; #if defined(DEFINE_GLOBAL_RECV_CRB) -struct netxen_recv_crb recv_crb_registers[] = { - /* - * Instance 0. - */ - { - /* rcv_desc_crb: */ - { - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x100), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x104), - /* crb_gloablrcv_ring: */ - NETXEN_NIC_REG(0x108), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x10c), - - }, - /* Jumbo frames */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x110), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x114), - /* crb_gloablrcv_ring: */ - NETXEN_NIC_REG(0x118), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x11c), - }, - /* LRO */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x120), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x124), - /* crb_gloablrcv_ring: */ - NETXEN_NIC_REG(0x128), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x12c), - } - }, - /* crb_rcvstatus_ring: */ - NETXEN_NIC_REG(0x130), - /* crb_rcv_status_producer: */ - NETXEN_NIC_REG(0x134), - /* crb_rcv_status_consumer: */ - NETXEN_NIC_REG(0x138), - /* crb_rcvpeg_state: */ - NETXEN_NIC_REG(0x13c), - /* crb_status_ring_size */ - NETXEN_NIC_REG(0x140), - - }, - /* - * Instance 1, - */ - { - /* rcv_desc_crb: */ - { - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x144), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x148), - /* crb_globalrcv_ring: */ - NETXEN_NIC_REG(0x14c), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x150), - - }, - /* Jumbo frames */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x154), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x158), - /* crb_globalrcv_ring: */ - NETXEN_NIC_REG(0x15c), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x160), - }, - /* LRO */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x164), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x168), - /* crb_globalrcv_ring: */ - NETXEN_NIC_REG(0x16c), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x170), - } - - }, - /* crb_rcvstatus_ring: */ - NETXEN_NIC_REG(0x174), - /* crb_rcv_status_producer: */ - NETXEN_NIC_REG(0x178), - /* crb_rcv_status_consumer: */ - NETXEN_NIC_REG(0x17c), - /* crb_rcvpeg_state: */ - NETXEN_NIC_REG(0x180), - /* crb_status_ring_size */ - NETXEN_NIC_REG(0x184), - - }, -}; - -u64 ctx_addr_sig_regs[][3] = { - {NETXEN_NIC_REG(0x188), NETXEN_NIC_REG(0x18c), NETXEN_NIC_REG(0x1c0)}, - {NETXEN_NIC_REG(0x190), NETXEN_NIC_REG(0x194), NETXEN_NIC_REG(0x1c4)}, - {NETXEN_NIC_REG(0x198), NETXEN_NIC_REG(0x19c), NETXEN_NIC_REG(0x1c8)}, - {NETXEN_NIC_REG(0x1a0), NETXEN_NIC_REG(0x1a4), NETXEN_NIC_REG(0x1cc)} -}; - #else extern struct netxen_recv_crb recv_crb_registers[]; extern u64 ctx_addr_sig_regs[][3]; -#define CRB_CTX_ADDR_REG_LO (ctx_addr_sig_regs[0][0]) -#define CRB_CTX_ADDR_REG_HI (ctx_addr_sig_regs[0][2]) -#define CRB_CTX_SIGNATURE_REG (ctx_addr_sig_regs[0][1]) #endif /* DEFINE_GLOBAL_RECEIVE_CRB */ +#define CRB_CTX_ADDR_REG_LO(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][0]) +#define CRB_CTX_ADDR_REG_HI(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][2]) +#define CRB_CTX_SIGNATURE_REG(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][1]) /* * Temperature control. diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c index 8be0d03..3d5b423 100644 --- a/drivers/net/ni5010.c +++ b/drivers/net/ni5010.c @@ -562,7 +562,6 @@ static void ni5010_rx(struct net_device return; } - skb->dev = dev; skb_reserve(skb, 2); /* Read packet into buffer */ diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c index a6f4b24..8dbd6d1 100644 --- a/drivers/net/ni52.c +++ b/drivers/net/ni52.c @@ -934,7 +934,6 @@ static void ni52_rcv_int(struct net_devi skb = (struct sk_buff *) dev_alloc_skb(totlen+2); if(skb != NULL) { - skb->dev = dev; skb_reserve(skb,2); skb_put(skb,totlen); eth_copy_and_sum(skb,(char *) p->base+(unsigned long) rbd->buffer,totlen,0); @@ -1183,7 +1182,7 @@ #if(NUM_XMIT_BUFFS > 1) else #endif { - memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len); + skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len); len = skb->len; if (len < ETH_ZLEN) { len = ETH_ZLEN; diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c index 1578f4d..3818edf 100644 --- a/drivers/net/ni65.c +++ b/drivers/net/ni65.c @@ -610,7 +610,6 @@ static void *ni65_alloc_mem(struct net_d printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what); return NULL; } - skb->dev = dev; skb_reserve(skb,2+16); skb_put(skb,R_BUF_SIZE); /* grab the whole space .. (not necessary) */ ptr = skb->data; @@ -1094,7 +1093,6 @@ #endif if(skb) { skb_reserve(skb,2); - skb->dev = dev; #ifdef RCV_VIA_SKB if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) { skb_put(skb,len); @@ -1178,8 +1176,9 @@ #ifdef XMT_VIA_SKB if( (unsigned long) (skb->data + skb->len) > 0x1000000) { #endif - memcpy((char *) p->tmdbounce[p->tmdbouncenum] ,(char *)skb->data, - (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len); + skb_copy_from_linear_data(skb, p->tmdbounce[p->tmdbouncenum], + skb->len > T_BUF_SIZE ? T_BUF_SIZE : + skb->len); if (len > skb->len) memset((char *)p->tmdbounce[p->tmdbouncenum]+skb->len, 0, len-skb->len); dev_kfree_skb (skb); diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index 9ec6e9e..6a32338 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -607,7 +607,6 @@ static inline int rx_refill(struct net_d res &= 0xf; skb_reserve(skb, res); - skb->dev = ndev; if (gfp != GFP_ATOMIC) spin_lock_irqsave(&dev->rx_info.lock, flags); res = ns83820_add_rx_skb(dev, skb); @@ -1157,9 +1156,9 @@ again: extsts = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { extsts |= EXTSTS_IPPKT; - if (IPPROTO_TCP == skb->nh.iph->protocol) + if (IPPROTO_TCP == ip_hdr(skb)->protocol) extsts |= EXTSTS_TCPPKT; - else if (IPPROTO_UDP == skb->nh.iph->protocol) + else if (IPPROTO_UDP == ip_hdr(skb)->protocol) extsts |= EXTSTS_UDPPKT; } diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index d670ac7..76fe9dd 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -334,8 +334,6 @@ static void pasemi_mac_replenish_rx_ring break; } - skb->dev = dev; - dma = pci_map_single(mac->dma_pdev, skb->data, skb->len, PCI_DMA_FROMDEVICE); @@ -731,16 +729,18 @@ static int pasemi_mac_start_tx(struct sk dflags = XCT_MACTX_O | XCT_MACTX_ST | XCT_MACTX_SS | XCT_MACTX_CRC_PAD; if (skb->ip_summed == CHECKSUM_PARTIAL) { - switch (skb->nh.iph->protocol) { + const unsigned char *nh = skb_network_header(skb); + + switch (ip_hdr(skb)->protocol) { case IPPROTO_TCP: dflags |= XCT_MACTX_CSUM_TCP; - dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2); - dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data); + dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2); + dflags |= XCT_MACTX_IPO(nh - skb->data); break; case IPPROTO_UDP: dflags |= XCT_MACTX_CSUM_UDP; - dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2); - dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data); + dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2); + dflags |= XCT_MACTX_IPO(nh - skb->data); break; } } diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c index 6ca4e4f..df8998b 100644 --- a/drivers/net/pci-skeleton.c +++ b/drivers/net/pci-skeleton.c @@ -1344,7 +1344,7 @@ static int netdrv_start_xmit (struct sk_ tp->tx_info[entry].skb = skb; /* tp->tx_info[entry].mapping = 0; */ - memcpy (tp->tx_buf[entry], skb->data, skb->len); + skb_copy_from_linear_data(skb, tp->tx_buf[entry], skb->len); /* Note: the chip doesn't have auto-pad! */ NETDRV_W32 (TxStatus0 + (entry * sizeof(u32)), @@ -1565,7 +1565,6 @@ #endif skb = dev_alloc_skb (pkt_size + 2); if (skb) { - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align the IP fields. */ eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index c7bd9c1..2b395ee 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -1056,7 +1056,6 @@ static int el3_rx(struct net_device *dev DEBUG(3, " Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), ((pkt_len+3)>>2)); diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 461e827..143ae2f 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -883,7 +883,6 @@ static int el3_rx(struct net_device *dev DEBUG(3, " Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), (pkt_len+3)>>2); diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 6139048..808fae1 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -1136,7 +1136,7 @@ static int ei_start_xmit(struct sk_buff ei_block_output(dev, length, skb->data, output_page); else { memset(packet, 0, ETH_ZLEN); - memcpy(packet, skb->data, skb->len); + skb_copy_from_linear_data(skb, packet, skb->len); ei_block_output(dev, length, packet, output_page); } @@ -1496,7 +1496,6 @@ static void ei_receive(struct net_device else { skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); skb->protocol=eth_type_trans(skb,dev); diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index 0d7de61..3f93d49 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -999,7 +999,6 @@ #endif lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb, 2); insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index 3b70774..73da611 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -1182,12 +1182,10 @@ static int mace_rx(struct net_device *de skb = dev_alloc_skb(pkt_len+2); if (skb != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1); if (pkt_len & 1) - *(skb->tail-1) = inb(ioaddr + AM2150_RCV); + *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); /* Send the packet to the upper (protocol) layers. */ diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 2561f76..7912dbd 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -1669,7 +1669,6 @@ static void smc_rx(struct net_device *de (packet_length+1)>>1); skb->protocol = eth_type_trans(skb, dev); - skb->dev = dev; netif_rx(skb); dev->last_rx = jiffies; smc->stats.rx_packets++; diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 5879e7c..809ec44 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -1226,7 +1226,6 @@ xirc2ps_interrupt(int irq, void *dev_id) (pktlen+1)>>1); } skb->protocol = eth_type_trans(skb, dev); - skb->dev = dev; netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 4d94ba7..9c171a7 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -253,12 +253,12 @@ struct pcnet32_access { * so the structure should be allocated using pci_alloc_consistent(). */ struct pcnet32_private { - struct pcnet32_init_block init_block; + struct pcnet32_init_block *init_block; /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */ struct pcnet32_rx_head *rx_ring; struct pcnet32_tx_head *tx_ring; - dma_addr_t dma_addr;/* DMA address of beginning of this - object, returned by pci_alloc_consistent */ + dma_addr_t init_dma_addr;/* DMA address of beginning of the init block, + returned by pci_alloc_consistent */ struct pci_dev *pci_dev; const char *name; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ @@ -653,7 +653,7 @@ static void pcnet32_realloc_rx_ring(stru static void pcnet32_purge_rx_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int i; /* free all allocated skbuffs */ @@ -681,7 +681,7 @@ #endif static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r = -EOPNOTSUPP; @@ -696,7 +696,7 @@ static int pcnet32_get_settings(struct n static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r = -EOPNOTSUPP; @@ -711,7 +711,7 @@ static int pcnet32_set_settings(struct n static void pcnet32_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); @@ -723,7 +723,7 @@ static void pcnet32_get_drvinfo(struct n static u32 pcnet32_get_link(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r; @@ -743,19 +743,19 @@ static u32 pcnet32_get_link(struct net_d static u32 pcnet32_get_msglevel(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); return lp->msg_enable; } static void pcnet32_set_msglevel(struct net_device *dev, u32 value) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); lp->msg_enable = value; } static int pcnet32_nway_reset(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r = -EOPNOTSUPP; @@ -770,7 +770,7 @@ static int pcnet32_nway_reset(struct net static void pcnet32_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); ering->tx_max_pending = TX_MAX_RING_SIZE; ering->tx_pending = lp->tx_ring_size; @@ -781,7 +781,7 @@ static void pcnet32_get_ringparam(struct static int pcnet32_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; unsigned int size; ulong ioaddr = dev->base_addr; @@ -847,7 +847,7 @@ static int pcnet32_self_test_count(struc static void pcnet32_ethtool_test(struct net_device *dev, struct ethtool_test *test, u64 * data) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int rc; if (test->flags == ETH_TEST_FL_OFFLINE) { @@ -868,7 +868,7 @@ static void pcnet32_ethtool_test(struct static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; /* access to registers */ ulong ioaddr = dev->base_addr; /* card base I/O address */ struct sk_buff *skb; /* sk buff */ @@ -1047,7 +1047,7 @@ #endif static void pcnet32_led_blink_callback(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; unsigned long flags; @@ -1064,7 +1064,7 @@ static void pcnet32_led_blink_callback(s static int pcnet32_phys_id(struct net_device *dev, u32 data) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; unsigned long flags; @@ -1109,7 +1109,7 @@ static int pcnet32_suspend(struct net_de int can_sleep) { int csr5; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; int ticks; @@ -1206,7 +1206,6 @@ static void pcnet32_rx_entry(struct net_ PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); lp->rx_skbuff[entry] = newskb; - newskb->dev = dev; lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->data, @@ -1258,7 +1257,7 @@ #endif static int pcnet32_rx(struct net_device *dev, int quota) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int entry = lp->cur_rx & lp->rx_mod_mask; struct pcnet32_rx_head *rxp = &lp->rx_ring[entry]; int npackets = 0; @@ -1283,7 +1282,7 @@ static int pcnet32_rx(struct net_device static int pcnet32_tx(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned int dirty_tx = lp->dirty_tx; int delta; int must_restart = 0; @@ -1382,7 +1381,7 @@ #endif #ifdef CONFIG_PCNET32_NAPI static int pcnet32_poll(struct net_device *dev, int *budget) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int quota = min(dev->quota, *budget); unsigned long ioaddr = dev->base_addr; unsigned long flags; @@ -1429,7 +1428,7 @@ #define PCNET32_REGS_PER_PHY 32 #define PCNET32_MAX_PHYS 32 static int pcnet32_get_regs_len(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int j = lp->phycount * PCNET32_REGS_PER_PHY; return ((PCNET32_NUM_REGS + j) * sizeof(u16)); @@ -1440,7 +1439,7 @@ static void pcnet32_get_regs(struct net_ { int i, csr0; u16 *buff = ptr; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; unsigned long flags; @@ -1593,7 +1592,6 @@ static int __devinit pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) { struct pcnet32_private *lp; - dma_addr_t lp_dma_addr; int i, media; int fdx, mii, fset, dxsuflo; int chip_version; @@ -1715,7 +1713,7 @@ pcnet32_probe1(unsigned long ioaddr, int dxsuflo = 1; } - dev = alloc_etherdev(0); + dev = alloc_etherdev(sizeof(*lp)); if (!dev) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Memory allocation failed.\n"); @@ -1806,25 +1804,22 @@ pcnet32_probe1(unsigned long ioaddr, int } dev->base_addr = ioaddr; + lp = netdev_priv(dev); /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ - if ((lp = - pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) { + if ((lp->init_block = + pci_alloc_consistent(pdev, sizeof(*lp->init_block), &lp->init_dma_addr)) == NULL) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Consistent memory allocation failed.\n"); ret = -ENOMEM; goto err_free_netdev; } - - memset(lp, 0, sizeof(*lp)); - lp->dma_addr = lp_dma_addr; lp->pci_dev = pdev; spin_lock_init(&lp->lock); SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); - dev->priv = lp; lp->name = chipname; lp->shared_irq = shared; lp->tx_ring_size = TX_RING_SIZE; /* default tx ring size */ @@ -1871,23 +1866,21 @@ pcnet32_probe1(unsigned long ioaddr, int && dev->dev_addr[2] == 0x75) lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; - lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ - lp->init_block.tlen_rlen = + lp->init_block->mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ + lp->init_block->tlen_rlen = le16_to_cpu(lp->tx_len_bits | lp->rx_len_bits); for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); - lp->init_block.tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); + lp->init_block->phys_addr[i] = dev->dev_addr[i]; + lp->init_block->filter[0] = 0x00000000; + lp->init_block->filter[1] = 0x00000000; + lp->init_block->rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); + lp->init_block->tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); /* switch pcnet32 to 32bit mode */ a->write_bcr(ioaddr, 20, 2); - a->write_csr(ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, - init_block)) & 0xffff); - a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, - init_block)) >> 16); + a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff)); + a->write_csr(ioaddr, 2, (lp->init_dma_addr >> 16)); if (pdev) { /* use the IRQ provided by PCI */ dev->irq = pdev->irq; @@ -1993,7 +1986,8 @@ #endif err_free_ring: pcnet32_free_ring(dev); err_free_consistent: - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), + lp->init_block, lp->init_dma_addr); err_free_netdev: free_netdev(dev); err_release_region: @@ -2004,7 +1998,7 @@ #endif /* if any allocation fails, caller must also call pcnet32_free_ring */ static int pcnet32_alloc_ring(struct net_device *dev, char *name) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); lp->tx_ring = pci_alloc_consistent(lp->pci_dev, sizeof(struct pcnet32_tx_head) * @@ -2071,7 +2065,7 @@ static int pcnet32_alloc_ring(struct net static void pcnet32_free_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); kfree(lp->tx_skbuff); lp->tx_skbuff = NULL; @@ -2104,7 +2098,7 @@ static void pcnet32_free_ring(struct net static int pcnet32_open(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; u16 val; int i; @@ -2135,8 +2129,7 @@ static int pcnet32_open(struct net_devic "%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n", dev->name, dev->irq, (u32) (lp->tx_ring_dma_addr), (u32) (lp->rx_ring_dma_addr), - (u32) (lp->dma_addr + - offsetof(struct pcnet32_private, init_block))); + (u32) (lp->init_dma_addr)); /* set/reset autoselect bit */ val = lp->a.read_bcr(ioaddr, 2) & ~2; @@ -2275,7 +2268,7 @@ #ifdef DO_DXSUFLO } #endif - lp->init_block.mode = + lp->init_block->mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); pcnet32_load_multicast(dev); @@ -2285,12 +2278,8 @@ #endif } /* Re-initialize the PCNET32, and start it when done. */ - lp->a.write_csr(ioaddr, 1, (lp->dma_addr + - offsetof(struct pcnet32_private, - init_block)) & 0xffff); - lp->a.write_csr(ioaddr, 2, - (lp->dma_addr + - offsetof(struct pcnet32_private, init_block)) >> 16); + lp->a.write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff)); + lp->a.write_csr(ioaddr, 2, (lp->init_dma_addr >> 16)); lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */ lp->a.write_csr(ioaddr, CSR0, CSR0_INIT); @@ -2317,8 +2306,7 @@ #endif printk(KERN_DEBUG "%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n", dev->name, i, - (u32) (lp->dma_addr + - offsetof(struct pcnet32_private, init_block)), + (u32) (lp->init_dma_addr), lp->a.read_csr(ioaddr, CSR0)); spin_unlock_irqrestore(&lp->lock, flags); @@ -2356,7 +2344,7 @@ #endif static void pcnet32_purge_tx_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int i; for (i = 0; i < lp->tx_ring_size; i++) { @@ -2376,7 +2364,7 @@ static void pcnet32_purge_tx_ring(struct /* Initialize the PCNET32 Rx and Tx rings. */ static int pcnet32_init_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int i; lp->tx_full = 0; @@ -2418,12 +2406,12 @@ static int pcnet32_init_ring(struct net_ lp->tx_dma_addr[i] = 0; } - lp->init_block.tlen_rlen = + lp->init_block->tlen_rlen = le16_to_cpu(lp->tx_len_bits | lp->rx_len_bits); for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); - lp->init_block.tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); + lp->init_block->phys_addr[i] = dev->dev_addr[i]; + lp->init_block->rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); + lp->init_block->tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); wmb(); /* Make sure all changes are visible */ return 0; } @@ -2434,7 +2422,7 @@ static int pcnet32_init_ring(struct net_ */ static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int i; @@ -2464,7 +2452,7 @@ static void pcnet32_restart(struct net_d static void pcnet32_tx_timeout(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr, flags; spin_lock_irqsave(&lp->lock, flags); @@ -2505,7 +2493,7 @@ static void pcnet32_tx_timeout(struct ne static int pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; u16 status; int entry; @@ -2570,7 +2558,7 @@ pcnet32_interrupt(int irq, void *dev_id) int boguscnt = max_interrupt_work; ioaddr = dev->base_addr; - lp = dev->priv; + lp = netdev_priv(dev); spin_lock(&lp->lock); @@ -2652,7 +2640,7 @@ #endif static int pcnet32_close(struct net_device *dev) { unsigned long ioaddr = dev->base_addr; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; del_timer_sync(&lp->watchdog_timer); @@ -2693,7 +2681,7 @@ static int pcnet32_close(struct net_devi static struct net_device_stats *pcnet32_get_stats(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; unsigned long flags; @@ -2707,8 +2695,8 @@ static struct net_device_stats *pcnet32_ /* taken from the sunlance driver, which it took from the depca driver */ static void pcnet32_load_multicast(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; - volatile struct pcnet32_init_block *ib = &lp->init_block; + struct pcnet32_private *lp = netdev_priv(dev); + volatile struct pcnet32_init_block *ib = lp->init_block; volatile u16 *mcast_table = (u16 *) & ib->filter; struct dev_mc_list *dmi = dev->mc_list; unsigned long ioaddr = dev->base_addr; @@ -2757,7 +2745,7 @@ static void pcnet32_load_multicast(struc static void pcnet32_set_multicast_list(struct net_device *dev) { unsigned long ioaddr = dev->base_addr, flags; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int csr15, suspended; spin_lock_irqsave(&lp->lock, flags); @@ -2768,12 +2756,12 @@ static void pcnet32_set_multicast_list(s if (netif_msg_hw(lp)) printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); - lp->init_block.mode = + lp->init_block->mode = le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) << 7); lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000); } else { - lp->init_block.mode = + lp->init_block->mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff); pcnet32_load_multicast(dev); @@ -2796,7 +2784,7 @@ static void pcnet32_set_multicast_list(s /* This routine assumes that the lp->lock is held */ static int mdio_read(struct net_device *dev, int phy_id, int reg_num) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; u16 val_out; @@ -2812,7 +2800,7 @@ static int mdio_read(struct net_device * /* This routine assumes that the lp->lock is held */ static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; if (!lp->mii) @@ -2824,7 +2812,7 @@ static void mdio_write(struct net_device static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int rc; unsigned long flags; @@ -2842,7 +2830,7 @@ static int pcnet32_ioctl(struct net_devi static int pcnet32_check_otherphy(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct mii_if_info mii = lp->mii_if; u16 bmcr; int i; @@ -2889,7 +2877,7 @@ static int pcnet32_check_otherphy(struct static void pcnet32_check_media(struct net_device *dev, int verbose) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int curr_link; int prev_link = netif_carrier_ok(dev) ? 1 : 0; u32 bcr9; @@ -2945,7 +2933,7 @@ static void pcnet32_check_media(struct n static void pcnet32_watchdog(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; /* Print the link status if it has changed */ @@ -2961,12 +2949,13 @@ static void __devexit pcnet32_remove_one struct net_device *dev = pci_get_drvdata(pdev); if (dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unregister_netdev(dev); pcnet32_free_ring(dev); release_region(dev->base_addr, PCNET32_TOTAL_SIZE); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), + lp->init_block, lp->init_dma_addr); free_netdev(dev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); @@ -3041,12 +3030,13 @@ static void __exit pcnet32_cleanup_modul struct net_device *next_dev; while (pcnet32_dev) { - struct pcnet32_private *lp = pcnet32_dev->priv; + struct pcnet32_private *lp = netdev_priv(pcnet32_dev); next_dev = lp->next; unregister_netdev(pcnet32_dev); pcnet32_free_ring(pcnet32_dev); release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), + lp->init_block, lp->init_dma_addr); free_netdev(pcnet32_dev); pcnet32_dev = next_dev; } diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 66da91b..68c99b4 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -276,21 +276,15 @@ static int fixed_mdio_register_device(in artificially, we are binding the driver here by hand; it will be the same for all the fixed phys anyway. */ - down_write(&phydev->dev.bus->subsys.rwsem); - phydev->dev.driver = &fixed_mdio_driver.driver; err = phydev->dev.driver->probe(&phydev->dev); if(err < 0) { printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id); - up_write(&phydev->dev.bus->subsys.rwsem); goto probe_fail; } err = device_bind_driver(&phydev->dev); - - up_write(&phydev->dev.bus->subsys.rwsem); - if (err) goto probe_fail; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index b31ce27..fc4aee9 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -35,10 +35,14 @@ #include #include #include -/* mdiobus_register +/** + * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus + * @bus: target mii_bus * - * description: Called by a bus driver to bring up all the PHYs - * on a given bus, and attach them to the bus + * Description: Called by a bus driver to bring up all the PHYs + * on a given bus, and attach them to the bus. + * + * Returns 0 on success or < 0 on error. */ int mdiobus_register(struct mii_bus *bus) { @@ -114,10 +118,13 @@ void mdiobus_unregister(struct mii_bus * } EXPORT_SYMBOL(mdiobus_unregister); -/* mdio_bus_match +/** + * mdio_bus_match - determine if given PHY driver supports the given PHY device + * @dev: target PHY device + * @drv: given PHY driver * - * description: Given a PHY device, and a PHY driver, return 1 if - * the driver supports the device. Otherwise, return 0 + * Description: Given a PHY device, and a PHY driver, return 1 if + * the driver supports the device. Otherwise, return 0. */ static int mdio_bus_match(struct device *dev, struct device_driver *drv) { diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c94a1fb..eed433d 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -39,7 +39,9 @@ #include #include #include -/* Convenience function to print out the current phy status +/** + * phy_print_status - Convenience function to print out the current phy status + * @phydev: the phy_device struct */ void phy_print_status(struct phy_device *phydev) { @@ -55,10 +57,15 @@ void phy_print_status(struct phy_device EXPORT_SYMBOL(phy_print_status); -/* Convenience functions for reading/writing a given PHY - * register. They MUST NOT be called from interrupt context, +/** + * phy_read - Convenience function for reading a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to read + * + * NOTE: MUST NOT be called from interrupt context, * because the bus read/write functions may wait for an interrupt - * to conclude the operation. */ + * to conclude the operation. + */ int phy_read(struct phy_device *phydev, u16 regnum) { int retval; @@ -72,6 +79,16 @@ int phy_read(struct phy_device *phydev, } EXPORT_SYMBOL(phy_read); +/** + * phy_write - Convenience function for writing a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ int phy_write(struct phy_device *phydev, u16 regnum, u16 val) { int err; @@ -85,7 +102,15 @@ int phy_write(struct phy_device *phydev, } EXPORT_SYMBOL(phy_write); - +/** + * phy_clear_interrupt - Ack the phy device's interrupt + * @phydev: the phy_device struct + * + * If the @phydev driver has an ack_interrupt function, call it to + * ack and clear the phy device's interrupt. + * + * Returns 0 on success on < 0 on error. + */ int phy_clear_interrupt(struct phy_device *phydev) { int err = 0; @@ -96,7 +121,13 @@ int phy_clear_interrupt(struct phy_devic return err; } - +/** + * phy_config_interrupt - configure the PHY device for the requested interrupts + * @phydev: the phy_device struct + * @interrupts: interrupt flags to configure for this @phydev + * + * Returns 0 on success on < 0 on error. + */ int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) { int err = 0; @@ -109,9 +140,11 @@ int phy_config_interrupt(struct phy_devi } -/* phy_aneg_done +/** + * phy_aneg_done - return auto-negotiation status + * @phydev: target phy_device struct * - * description: Reads the status register and returns 0 either if + * Description: Reads the status register and returns 0 either if * auto-negotiation is incomplete, or if there was an error. * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done. */ @@ -173,9 +206,12 @@ static const struct phy_setting settings #define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting)) -/* phy_find_setting +/** + * phy_find_setting - find a PHY settings array entry that matches speed & duplex + * @speed: speed to match + * @duplex: duplex to match * - * description: Searches the settings array for the setting which + * Description: Searches the settings array for the setting which * matches the desired speed and duplex, and returns the index * of that setting. Returns the index of the last setting if * none of the others match. @@ -192,11 +228,12 @@ static inline int phy_find_setting(int s return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; } -/* phy_find_valid - * idx: The first index in settings[] to search - * features: A mask of the valid settings +/** + * phy_find_valid - find a PHY setting that matches the requested features mask + * @idx: The first index in settings[] to search + * @features: A mask of the valid settings * - * description: Returns the index of the first valid setting less + * Description: Returns the index of the first valid setting less * than or equal to the one pointed to by idx, as determined by * the mask in features. Returns the index of the last setting * if nothing else matches. @@ -209,11 +246,13 @@ static inline int phy_find_valid(int idx return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; } -/* phy_sanitize_settings +/** + * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex + * @phydev: the target phy_device struct * - * description: Make sure the PHY is set to supported speeds and + * Description: Make sure the PHY is set to supported speeds and * duplexes. Drop down by one in this order: 1000/FULL, - * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF + * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. */ void phy_sanitize_settings(struct phy_device *phydev) { @@ -232,16 +271,17 @@ void phy_sanitize_settings(struct phy_de } EXPORT_SYMBOL(phy_sanitize_settings); -/* phy_ethtool_sset: - * A generic ethtool sset function. Handles all the details +/** + * phy_ethtool_sset - generic ethtool sset function, handles all the details + * @phydev: target phy_device struct + * @cmd: ethtool_cmd * * A few notes about parameter checking: * - We don't set port or transceiver, so we don't care what they * were set to. * - phy_start_aneg() will make sure forced settings are sane, and * choose the next best ones from the ones selected, so we don't - * care if ethtool tries to give us bad values - * + * care if ethtool tries to give us bad values. */ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) { @@ -304,9 +344,15 @@ int phy_ethtool_gset(struct phy_device * } EXPORT_SYMBOL(phy_ethtool_gset); -/* Note that this function is currently incompatible with the +/** + * phy_mii_ioctl - generic PHY MII ioctl interface + * @phydev: the phy_device struct + * @mii_data: MII ioctl data + * @cmd: ioctl cmd to execute + * + * Note that this function is currently incompatible with the * PHYCONTROL layer. It changes registers without regard to - * current state. Use at own risk + * current state. Use at own risk. */ int phy_mii_ioctl(struct phy_device *phydev, struct mii_ioctl_data *mii_data, int cmd) @@ -336,6 +382,12 @@ int phy_mii_ioctl(struct phy_device *phy phydev->duplex = DUPLEX_FULL; else phydev->duplex = DUPLEX_HALF; + if ((!phydev->autoneg) && + (val & BMCR_SPEED1000)) + phydev->speed = SPEED_1000; + else if ((!phydev->autoneg) && + (val & BMCR_SPEED100)) + phydev->speed = SPEED_100; break; case MII_ADVERTISE: phydev->advertising = val; @@ -358,13 +410,14 @@ int phy_mii_ioctl(struct phy_device *phy return 0; } -/* phy_start_aneg +/** + * phy_start_aneg - start auto-negotiation for this PHY device + * @phydev: the phy_device struct * - * description: Sanitizes the settings (if we're not - * autonegotiating them), and then calls the driver's - * config_aneg function. If the PHYCONTROL Layer is operating, - * we change the state to reflect the beginning of - * Auto-negotiation or forcing. + * Description: Sanitizes the settings (if we're not autonegotiating + * them), and then calls the driver's config_aneg function. + * If the PHYCONTROL Layer is operating, we change the state to + * reflect the beginning of Auto-negotiation or forcing. */ int phy_start_aneg(struct phy_device *phydev) { @@ -400,15 +453,19 @@ EXPORT_SYMBOL(phy_start_aneg); static void phy_change(struct work_struct *work); static void phy_timer(unsigned long data); -/* phy_start_machine: +/** + * phy_start_machine - start PHY state machine tracking + * @phydev: the phy_device struct + * @handler: callback function for state change notifications * - * description: The PHY infrastructure can run a state machine + * Description: The PHY infrastructure can run a state machine * which tracks whether the PHY is starting up, negotiating, * etc. This function starts the timer which tracks the state - * of the PHY. If you want to be notified when the state - * changes, pass in the callback, otherwise, pass NULL. If you + * of the PHY. If you want to be notified when the state changes, + * pass in the callback @handler, otherwise, pass NULL. If you * want to maintain your own state machine, do not call this - * function. */ + * function. + */ void phy_start_machine(struct phy_device *phydev, void (*handler)(struct net_device *)) { @@ -420,9 +477,11 @@ void phy_start_machine(struct phy_device mod_timer(&phydev->phy_timer, jiffies + HZ); } -/* phy_stop_machine +/** + * phy_stop_machine - stop the PHY state machine tracking + * @phydev: target phy_device struct * - * description: Stops the state machine timer, sets the state to UP + * Description: Stops the state machine timer, sets the state to UP * (unless it wasn't up yet). This function must be called BEFORE * phy_detach. */ @@ -438,12 +497,14 @@ void phy_stop_machine(struct phy_device phydev->adjust_state = NULL; } -/* phy_force_reduction +/** + * phy_force_reduction - reduce PHY speed/duplex settings by one step + * @phydev: target phy_device struct * - * description: Reduces the speed/duplex settings by - * one notch. The order is so: - * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF, - * 10/FULL, 10/HALF. The function bottoms out at 10/HALF. + * Description: Reduces the speed/duplex settings by one notch, + * in this order-- + * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. + * The function bottoms out at 10/HALF. */ static void phy_force_reduction(struct phy_device *phydev) { @@ -464,7 +525,9 @@ static void phy_force_reduction(struct p } -/* phy_error: +/** + * phy_error - enter HALTED state for this PHY device + * @phydev: target phy_device struct * * Moves the PHY to the HALTED state in response to a read * or write error, and tells the controller the link is down. @@ -478,9 +541,12 @@ void phy_error(struct phy_device *phydev spin_unlock(&phydev->lock); } -/* phy_interrupt +/** + * phy_interrupt - PHY interrupt handler + * @irq: interrupt line + * @phy_dat: phy_device pointer * - * description: When a PHY interrupt occurs, the handler disables + * Description: When a PHY interrupt occurs, the handler disables * interrupts, and schedules a work task to clear the interrupt. */ static irqreturn_t phy_interrupt(int irq, void *phy_dat) @@ -501,7 +567,10 @@ static irqreturn_t phy_interrupt(int irq return IRQ_HANDLED; } -/* Enable the interrupts from the PHY side */ +/** + * phy_enable_interrupts - Enable the interrupts from the PHY side + * @phydev: target phy_device struct + */ int phy_enable_interrupts(struct phy_device *phydev) { int err; @@ -517,7 +586,10 @@ int phy_enable_interrupts(struct phy_dev } EXPORT_SYMBOL(phy_enable_interrupts); -/* Disable the PHY interrupts from the PHY side */ +/** + * phy_disable_interrupts - Disable the PHY interrupts from the PHY side + * @phydev: target phy_device struct + */ int phy_disable_interrupts(struct phy_device *phydev) { int err; @@ -543,13 +615,15 @@ phy_err: } EXPORT_SYMBOL(phy_disable_interrupts); -/* phy_start_interrupts +/** + * phy_start_interrupts - request and enable interrupts for a PHY device + * @phydev: target phy_device struct * - * description: Request the interrupt for the given PHY. If - * this fails, then we set irq to PHY_POLL. + * Description: Request the interrupt for the given PHY. + * If this fails, then we set irq to PHY_POLL. * Otherwise, we enable the interrupts in the PHY. - * Returns 0 on success. * This should only be called with a valid IRQ number. + * Returns 0 on success or < 0 on error. */ int phy_start_interrupts(struct phy_device *phydev) { @@ -574,6 +648,10 @@ int phy_start_interrupts(struct phy_devi } EXPORT_SYMBOL(phy_start_interrupts); +/** + * phy_stop_interrupts - disable interrupts from a PHY device + * @phydev: target phy_device struct + */ int phy_stop_interrupts(struct phy_device *phydev) { int err; @@ -596,7 +674,10 @@ int phy_stop_interrupts(struct phy_devic EXPORT_SYMBOL(phy_stop_interrupts); -/* Scheduled by the phy_interrupt/timer to handle PHY changes */ +/** + * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes + * @work: work_struct that describes the work to be done + */ static void phy_change(struct work_struct *work) { int err; @@ -630,7 +711,10 @@ phy_err: phy_error(phydev); } -/* Bring down the PHY link, and stop checking the status. */ +/** + * phy_stop - Bring down the PHY link, and stop checking the status + * @phydev: target phy_device struct + */ void phy_stop(struct phy_device *phydev) { spin_lock(&phydev->lock); @@ -659,9 +743,11 @@ out_unlock: } -/* phy_start +/** + * phy_start - start or restart a PHY device + * @phydev: target phy_device struct * - * description: Indicates the attached device's readiness to + * Description: Indicates the attached device's readiness to * handle PHY-related work. Used during startup to start the * PHY, and after a call to phy_stop() to resume operation. * Also used to indicate the MDIO bus has cleared an error diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 7d5b6d1..a8b74cd 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -74,11 +74,13 @@ struct phy_device* phy_device_create(str } EXPORT_SYMBOL(phy_device_create); -/* get_phy_device +/** + * get_phy_device - reads the specified PHY device and returns its @phy_device struct + * @bus: the target MII bus + * @addr: PHY address on the MII bus * - * description: Reads the ID registers of the PHY at addr on the - * bus, then allocates and returns the phy_device to - * represent it. + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, then allocates and returns the phy_device to represent it. */ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) { @@ -112,23 +114,33 @@ struct phy_device * get_phy_device(struc return dev; } -/* phy_prepare_link: +/** + * phy_prepare_link - prepares the PHY layer to monitor link status + * @phydev: target phy_device struct + * @handler: callback function for link status change notifications * - * description: Tells the PHY infrastructure to handle the + * Description: Tells the PHY infrastructure to handle the * gory details on monitoring link status (whether through * polling or an interrupt), and to call back to the * connected device driver when the link status changes. * If you want to monitor your own link state, don't call - * this function */ + * this function. + */ void phy_prepare_link(struct phy_device *phydev, void (*handler)(struct net_device *)) { phydev->adjust_link = handler; } -/* phy_connect: +/** + * phy_connect - connect an ethernet device to a PHY device + * @dev: the network device to connect + * @phy_id: the PHY device to connect + * @handler: callback function for state change notifications + * @flags: PHY device's dev_flags + * @interface: PHY device's interface * - * description: Convenience function for connecting ethernet + * Description: Convenience function for connecting ethernet * devices to PHY devices. The default behavior is for * the PHY infrastructure to handle everything, and only notify * the connected driver when the link status changes. If you @@ -158,6 +170,10 @@ struct phy_device * phy_connect(struct n } EXPORT_SYMBOL(phy_connect); +/** + * phy_disconnect - disable interrupts, stop state machine, and detach a PHY device + * @phydev: target phy_device struct + */ void phy_disconnect(struct phy_device *phydev) { if (phydev->irq > 0) @@ -171,21 +187,25 @@ void phy_disconnect(struct phy_device *p } EXPORT_SYMBOL(phy_disconnect); -/* phy_attach: +static int phy_compare_id(struct device *dev, void *data) +{ + return strcmp((char *)data, dev->bus_id) ? 0 : 1; +} + +/** + * phy_attach - attach a network device to a particular PHY device + * @dev: network device to attach + * @phy_id: PHY device to attach + * @flags: PHY device's dev_flags + * @interface: PHY device's interface * - * description: Called by drivers to attach to a particular PHY + * Description: Called by drivers to attach to a particular PHY * device. The phy_device is found, and properly hooked up * to the phy_driver. If no driver is attached, then the * genphy_driver is used. The phy_device is given a ptr to * the attaching device, and given a callback for link status - * change. The phy_device is returned to the attaching - * driver. + * change. The phy_device is returned to the attaching driver. */ -static int phy_compare_id(struct device *dev, void *data) -{ - return strcmp((char *)data, dev->bus_id) ? 0 : 1; -} - struct phy_device *phy_attach(struct net_device *dev, const char *phy_id, u32 flags, phy_interface_t interface) { @@ -208,16 +228,12 @@ struct phy_device *phy_attach(struct net * exist, and we should use the genphy driver. */ if (NULL == d->driver) { int err; - down_write(&d->bus->subsys.rwsem); d->driver = &genphy_driver.driver; err = d->driver->probe(d); - if (err >= 0) err = device_bind_driver(d); - up_write(&d->bus->subsys.rwsem); - if (err) return ERR_PTR(err); } @@ -250,6 +266,10 @@ struct phy_device *phy_attach(struct net } EXPORT_SYMBOL(phy_attach); +/** + * phy_detach - detach a PHY device from its network device + * @phydev: target phy_device struct + */ void phy_detach(struct phy_device *phydev) { phydev->attached_dev = NULL; @@ -258,22 +278,21 @@ void phy_detach(struct phy_device *phyde * was using the generic driver), we unbind the device * from the generic driver so that there's a chance a * real driver could be loaded */ - if (phydev->dev.driver == &genphy_driver.driver) { - down_write(&phydev->dev.bus->subsys.rwsem); + if (phydev->dev.driver == &genphy_driver.driver) device_release_driver(&phydev->dev); - up_write(&phydev->dev.bus->subsys.rwsem); - } } EXPORT_SYMBOL(phy_detach); /* Generic PHY support and helper functions */ -/* genphy_config_advert +/** + * genphy_config_advert - sanitize and advertise auto-negotation parameters + * @phydev: target phy_device struct * - * description: Writes MII_ADVERTISE with the appropriate values, + * Description: Writes MII_ADVERTISE with the appropriate values, * after sanitizing the values to make sure we only advertise - * what is supported + * what is supported. */ int genphy_config_advert(struct phy_device *phydev) { @@ -335,11 +354,14 @@ int genphy_config_advert(struct phy_devi } EXPORT_SYMBOL(genphy_config_advert); -/* genphy_setup_forced +/** + * genphy_setup_forced - configures/forces speed/duplex from @phydev + * @phydev: target phy_device struct * - * description: Configures MII_BMCR to force speed/duplex + * Description: Configures MII_BMCR to force speed/duplex * to the values in phydev. Assumes that the values are valid. - * Please see phy_sanitize_settings() */ + * Please see phy_sanitize_settings(). + */ int genphy_setup_forced(struct phy_device *phydev) { int ctl = BMCR_RESET; @@ -368,7 +390,10 @@ int genphy_setup_forced(struct phy_devic } -/* Enable and Restart Autonegotiation */ +/** + * genphy_restart_aneg - Enable and Restart Autonegotiation + * @phydev: target phy_device struct + */ int genphy_restart_aneg(struct phy_device *phydev) { int ctl; @@ -389,11 +414,13 @@ int genphy_restart_aneg(struct phy_devic } -/* genphy_config_aneg +/** + * genphy_config_aneg - restart auto-negotiation or write BMCR + * @phydev: target phy_device struct * - * description: If auto-negotiation is enabled, we configure the + * Description: If auto-negotiation is enabled, we configure the * advertising, and then restart auto-negotiation. If it is not - * enabled, then we write the BMCR + * enabled, then we write the BMCR. */ int genphy_config_aneg(struct phy_device *phydev) { @@ -413,11 +440,13 @@ int genphy_config_aneg(struct phy_device } EXPORT_SYMBOL(genphy_config_aneg); -/* genphy_update_link +/** + * genphy_update_link - update link status in @phydev + * @phydev: target phy_device struct * - * description: Update the value in phydev->link to reflect the + * Description: Update the value in phydev->link to reflect the * current link value. In order to do this, we need to read - * the status register twice, keeping the second value + * the status register twice, keeping the second value. */ int genphy_update_link(struct phy_device *phydev) { @@ -444,9 +473,11 @@ int genphy_update_link(struct phy_device } EXPORT_SYMBOL(genphy_update_link); -/* genphy_read_status +/** + * genphy_read_status - check the link status and update current link state + * @phydev: target phy_device struct * - * description: Check the link, then figure out the current state + * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. Start by checking the gigabit possibilities, * then move on to 10/100. @@ -586,9 +617,11 @@ static int genphy_config_init(struct phy } -/* phy_probe +/** + * phy_probe - probe and init a PHY device + * @dev: device to probe and init * - * description: Take care of setting up the phy_device structure, + * Description: Take care of setting up the phy_device structure, * set the state to READY (the driver's init function should * set it to STARTING if needed). */ @@ -650,6 +683,10 @@ static int phy_remove(struct device *dev return 0; } +/** + * phy_driver_register - register a phy_driver with the PHY layer + * @new_driver: new phy_driver to register + */ int phy_driver_register(struct phy_driver *new_driver) { int retval; diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 6bb085f..8754cf3 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -546,7 +546,7 @@ static __be16 plip_type_trans(struct sk_ struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb_pull(skb,dev->hard_header_len); eth = eth_hdr(skb); diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index ef58e41..6d596ca 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -88,8 +88,6 @@ #define PF_TO_X(pf, X) container_of(pf, #define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp) #define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel) -#define ROUNDUP(n, x) (((n) + (x) - 1) / (x)) - /* * Data structure describing one ppp unit. * A ppp unit corresponds to a ppp network interface device @@ -1297,7 +1295,7 @@ static int ppp_mp_explode(struct ppp *pp */ fragsize = len; if (nfree > 1) - fragsize = ROUNDUP(fragsize, nfree); + fragsize = DIV_ROUND_UP(fragsize, nfree); /* nbigger channels get fragsize bytes, the rest get fragsize-1, except if nbigger==0, then they all get fragsize. */ nbigger = len % nfree; @@ -1685,7 +1683,7 @@ #endif /* CONFIG_PPP_FILTER */ skb_pull_rcsum(skb, 2); skb->dev = ppp->dev; skb->protocol = htons(npindex_to_ethertype[npi]); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_rx(skb); ppp->dev->last_rx = jiffies; } diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index b6f0e9a..5918fab 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -594,7 +594,8 @@ ppp_sync_txmunge(struct syncppp *ap, str return NULL; } skb_reserve(npkt,2); - memcpy(skb_put(npkt,skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, + skb_put(npkt, skb->len), skb->len); kfree_skb(skb); skb = npkt; } diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index ebfa296..6f98834 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -207,7 +207,7 @@ static inline struct pppox_sock *get_ite static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp) { - struct net_device *dev = NULL; + struct net_device *dev; int ifindex; dev = dev_get_by_name(sp->sa_addr.pppoe.dev); @@ -218,20 +218,6 @@ static inline struct pppox_sock *get_ite return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex); } -static inline int set_item(struct pppox_sock *po) -{ - int i; - - if (!po) - return -EINVAL; - - write_lock_bh(&pppoe_hash_lock); - i = __set_item(po); - write_unlock_bh(&pppoe_hash_lock); - - return i; -} - static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int ifindex) { struct pppox_sock *ret; @@ -255,54 +241,53 @@ static inline struct pppox_sock *delete_ static void pppoe_flush_dev(struct net_device *dev) { int hash; - BUG_ON(dev == NULL); - read_lock_bh(&pppoe_hash_lock); + write_lock_bh(&pppoe_hash_lock); for (hash = 0; hash < PPPOE_HASH_SIZE; hash++) { struct pppox_sock *po = item_hash_table[hash]; while (po != NULL) { - if (po->pppoe_dev == dev) { - struct sock *sk = sk_pppox(po); - - sock_hold(sk); - po->pppoe_dev = NULL; + struct sock *sk = sk_pppox(po); + if (po->pppoe_dev != dev) { + po = po->next; + continue; + } + po->pppoe_dev = NULL; + dev_put(dev); - /* We hold a reference to SK, now drop the - * hash table lock so that we may attempt - * to lock the socket (which can sleep). - */ - read_unlock_bh(&pppoe_hash_lock); - lock_sock(sk); + /* We always grab the socket lock, followed by the + * pppoe_hash_lock, in that order. Since we should + * hold the sock lock while doing any unbinding, + * we need to release the lock we're holding. + * Hold a reference to the sock so it doesn't disappear + * as we're jumping between locks. + */ - if (sk->sk_state & - (PPPOX_CONNECTED | PPPOX_BOUND)) { - pppox_unbind_sock(sk); - dev_put(dev); - sk->sk_state = PPPOX_ZOMBIE; - sk->sk_state_change(sk); - } + sock_hold(sk); - release_sock(sk); + write_unlock_bh(&pppoe_hash_lock); + lock_sock(sk); - sock_put(sk); + if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + pppox_unbind_sock(sk); + sk->sk_state = PPPOX_ZOMBIE; + sk->sk_state_change(sk); + } - read_lock_bh(&pppoe_hash_lock); + release_sock(sk); + sock_put(sk); - /* Now restart from the beginning of this - * hash chain. We always NULL out pppoe_dev - * so we are guaranteed to make forward - * progress. - */ - po = item_hash_table[hash]; - continue; - } - po = po->next; + /* Restart scan at the beginning of this hash chain. + * While the lock was dropped the chain contents may + * have changed. + */ + write_lock_bh(&pppoe_hash_lock); + po = item_hash_table[hash]; } } - read_unlock_bh(&pppoe_hash_lock); + write_unlock_bh(&pppoe_hash_lock); } static int pppoe_device_event(struct notifier_block *this, @@ -344,10 +329,10 @@ static struct notifier_block pppoe_notif static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) { struct pppox_sock *po = pppox_sk(sk); - struct pppox_sock *relay_po = NULL; + struct pppox_sock *relay_po; if (sk->sk_state & PPPOX_BOUND) { - struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw; + struct pppoe_hdr *ph = pppoe_hdr(skb); int len = ntohs(ph->length); skb_pull_rcsum(skb, sizeof(struct pppoe_hdr)); if (pskb_trim_rcsum(skb, len)) @@ -401,7 +386,7 @@ static int pppoe_rcv(struct sk_buff *skb if (!(skb = skb_share_check(skb, GFP_ATOMIC))) goto out; - ph = (struct pppoe_hdr *) skb->nh.raw; + ph = pppoe_hdr(skb); po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source, dev->ifindex); if (po != NULL) @@ -433,7 +418,7 @@ static int pppoe_disc_rcv(struct sk_buff if (!(skb = skb_share_check(skb, GFP_ATOMIC))) goto out; - ph = (struct pppoe_hdr *) skb->nh.raw; + ph = pppoe_hdr(skb); if (ph->code != PADT_CODE) goto abort; @@ -514,36 +499,49 @@ static int pppoe_release(struct socket * { struct sock *sk = sock->sk; struct pppox_sock *po; - int error = 0; if (!sk) return 0; - if (sock_flag(sk, SOCK_DEAD)) + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)){ + release_sock(sk); return -EBADF; + } pppox_unbind_sock(sk); /* Signal the death of the socket. */ sk->sk_state = PPPOX_DEAD; + + /* Write lock on hash lock protects the entire "po" struct from + * concurrent updates via pppoe_flush_dev. The "po" struct should + * be considered part of the hash table contents, thus protected + * by the hash table lock */ + write_lock_bh(&pppoe_hash_lock); + po = pppox_sk(sk); if (po->pppoe_pa.sid) { - delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote, po->pppoe_ifindex); + __delete_item(po->pppoe_pa.sid, + po->pppoe_pa.remote, po->pppoe_ifindex); } - if (po->pppoe_dev) + if (po->pppoe_dev) { dev_put(po->pppoe_dev); + po->pppoe_dev = NULL; + } - po->pppoe_dev = NULL; + write_unlock_bh(&pppoe_hash_lock); sock_orphan(sk); sock->sk = NULL; skb_queue_purge(&sk->sk_receive_queue); + release_sock(sk); sock_put(sk); - return error; + return 0; } @@ -599,14 +597,18 @@ static int pppoe_connect(struct socket * po->pppoe_dev = dev; po->pppoe_ifindex = dev->ifindex; - if (!(dev->flags & IFF_UP)) + write_lock_bh(&pppoe_hash_lock); + if (!(dev->flags & IFF_UP)){ + write_unlock_bh(&pppoe_hash_lock); goto err_put; + } memcpy(&po->pppoe_pa, &sp->sa_addr.pppoe, sizeof(struct pppoe_addr)); - error = set_item(po); + error = __set_item(po); + write_unlock_bh(&pppoe_hash_lock); if (error < 0) goto err_put; @@ -762,10 +764,10 @@ static int pppoe_ioctl(struct socket *so static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len) { - struct sk_buff *skb = NULL; + struct sk_buff *skb; struct sock *sk = sock->sk; struct pppox_sock *po = pppox_sk(sk); - int error = 0; + int error; struct pppoe_hdr hdr; struct pppoe_hdr *ph; struct net_device *dev; @@ -799,7 +801,7 @@ static int pppoe_sendmsg(struct kiocb *i /* Reserve space for headers. */ skb_reserve(skb, dev->hard_header_len); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); skb->dev = dev; @@ -869,7 +871,8 @@ static int __pppoe_xmit(struct sock *sk, goto abort; skb_reserve(skb2, dev->hard_header_len + sizeof(struct pppoe_hdr)); - memcpy(skb_put(skb2, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(skb2, skb->len), + skb->len); } else { /* Make a clone so as to not disturb the original skb, * give dev_queue_xmit something it can free. @@ -884,7 +887,7 @@ static int __pppoe_xmit(struct sock *sk, memcpy(ph, &hdr, sizeof(struct pppoe_hdr)); skb2->protocol = __constant_htons(ETH_P_PPP_SES); - skb2->nh.raw = skb2->data; + skb_reset_network_header(skb2); skb2->dev = dev; @@ -929,10 +932,8 @@ static int pppoe_recvmsg(struct kiocb *i struct msghdr *m, size_t total_len, int flags) { struct sock *sk = sock->sk; - struct sk_buff *skb = NULL; + struct sk_buff *skb; int error = 0; - int len; - struct pppoe_hdr *ph = NULL; if (sk->sk_state & PPPOX_BOUND) { error = -EIO; @@ -942,26 +943,21 @@ static int pppoe_recvmsg(struct kiocb *i skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &error); - if (error < 0) { + if (error < 0) goto end; - } m->msg_namelen = 0; if (skb) { - error = 0; - ph = (struct pppoe_hdr *) skb->nh.raw; - len = ntohs(ph->length); + struct pppoe_hdr *ph = pppoe_hdr(skb); + const int len = ntohs(ph->length); error = memcpy_toiovec(m->msg_iov, (unsigned char *) &ph->tag[0], len); - if (error < 0) - goto do_skb_free; - error = len; + if (error == 0) + error = len; } -do_skb_free: - if (skb) - kfree_skb(skb); + kfree_skb(skb); end: return error; } @@ -991,7 +987,7 @@ out: static __inline__ struct pppox_sock *pppoe_get_idx(loff_t pos) { - struct pppox_sock *po = NULL; + struct pppox_sock *po; int i = 0; for (; i < PPPOE_HASH_SIZE; i++) { diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c index 9315046..f3e47d0 100644 --- a/drivers/net/pppox.c +++ b/drivers/net/pppox.c @@ -31,6 +31,7 @@ #include #include #include #include +#include #include @@ -58,7 +59,7 @@ void pppox_unbind_sock(struct sock *sk) { /* Clear connection to ppp device, if attached. */ - if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE)) { + if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) { ppp_unregister_channel(&pppox_sk(sk)->chan); sk->sk_state = PPPOX_DEAD; } @@ -114,6 +115,13 @@ static int pppox_create(struct socket *s goto out; rc = -EPROTONOSUPPORT; +#ifdef CONFIG_KMOD + if (!pppox_protos[protocol]) { + char buffer[32]; + sprintf(buffer, "pppox-proto-%d", protocol); + request_module(buffer); + } +#endif if (!pppox_protos[protocol] || !try_module_get(pppox_protos[protocol]->owner)) goto out; diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index a8246eb..d8766c0 100755 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -39,7 +39,7 @@ #include "qla3xxx.h" #define DRV_NAME "qla3xxx" #define DRV_STRING "QLogic ISP3XXX Network Driver" -#define DRV_VERSION "v2.03.00-k3" +#define DRV_VERSION "v2.03.00-k4" #define PFX DRV_NAME " " static const char ql3xxx_driver_name[] = DRV_NAME; @@ -72,6 +72,30 @@ static struct pci_device_id ql3xxx_pci_t MODULE_DEVICE_TABLE(pci, ql3xxx_pci_tbl); /* + * These are the known PHY's which are used + */ +typedef enum { + PHY_TYPE_UNKNOWN = 0, + PHY_VITESSE_VSC8211, + PHY_AGERE_ET1011C, + MAX_PHY_DEV_TYPES +} PHY_DEVICE_et; + +typedef struct { + PHY_DEVICE_et phyDevice; + u32 phyIdOUI; + u16 phyIdModel; + char *name; +} PHY_DEVICE_INFO_t; + +static const PHY_DEVICE_INFO_t PHY_DEVICES[] = + {{PHY_TYPE_UNKNOWN, 0x000000, 0x0, "PHY_TYPE_UNKNOWN"}, + {PHY_VITESSE_VSC8211, 0x0003f1, 0xb, "PHY_VITESSE_VSC8211"}, + {PHY_AGERE_ET1011C, 0x00a0bc, 0x1, "PHY_AGERE_ET1011C"}, +}; + + +/* * Caller must take hw_lock. */ static int ql_sem_spinlock(struct ql3_adapter *qdev, @@ -662,7 +686,7 @@ static u8 ql_mii_disable_scan_mode(struc } static int ql_mii_write_reg_ex(struct ql3_adapter *qdev, - u16 regAddr, u16 value, u32 mac_index) + u16 regAddr, u16 value, u32 phyAddr) { struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; @@ -680,7 +704,7 @@ static int ql_mii_write_reg_ex(struct ql } ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, - PHYAddr[mac_index] | regAddr); + phyAddr | regAddr); ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value); @@ -701,7 +725,7 @@ static int ql_mii_write_reg_ex(struct ql } static int ql_mii_read_reg_ex(struct ql3_adapter *qdev, u16 regAddr, - u16 * value, u32 mac_index) + u16 * value, u32 phyAddr) { struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; @@ -720,7 +744,7 @@ static int ql_mii_read_reg_ex(struct ql3 } ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, - PHYAddr[mac_index] | regAddr); + phyAddr | regAddr); ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, (MAC_MII_CONTROL_RC << 16)); @@ -850,28 +874,31 @@ static void ql_petbi_start_neg(struct ql } -static void ql_petbi_reset_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_petbi_reset_ex(struct ql3_adapter *qdev) { ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET, - mac_index); + PHYAddr[qdev->mac_index]); } -static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev) { u16 reg; /* Enable Auto-negotiation sense */ - ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, ®, mac_index); + ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, ®, + PHYAddr[qdev->mac_index]); reg |= PETBI_TBI_AUTO_SENSE; - ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg, mac_index); + ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg, + PHYAddr[qdev->mac_index]); ql_mii_write_reg_ex(qdev, PETBI_NEG_ADVER, - PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX, mac_index); + PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX, + PHYAddr[qdev->mac_index]); ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG | PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000, - mac_index); + PHYAddr[qdev->mac_index]); } static void ql_petbi_init(struct ql3_adapter *qdev) @@ -880,10 +907,10 @@ static void ql_petbi_init(struct ql3_ada ql_petbi_start_neg(qdev); } -static void ql_petbi_init_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_petbi_init_ex(struct ql3_adapter *qdev) { - ql_petbi_reset_ex(qdev, mac_index); - ql_petbi_start_neg_ex(qdev, mac_index); + ql_petbi_reset_ex(qdev); + ql_petbi_start_neg_ex(qdev); } static int ql_is_petbi_neg_pause(struct ql3_adapter *qdev) @@ -896,33 +923,128 @@ static int ql_is_petbi_neg_pause(struct return (reg & PETBI_NEG_PAUSE_MASK) == PETBI_NEG_PAUSE; } +static void phyAgereSpecificInit(struct ql3_adapter *qdev, u32 miiAddr) +{ + printk(KERN_INFO "%s: enabling Agere specific PHY\n", qdev->ndev->name); + /* power down device bit 11 = 1 */ + ql_mii_write_reg_ex(qdev, 0x00, 0x1940, miiAddr); + /* enable diagnostic mode bit 2 = 1 */ + ql_mii_write_reg_ex(qdev, 0x12, 0x840e, miiAddr); + /* 1000MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x10, 0x8805, miiAddr); + /* 1000MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x11, 0xf03e, miiAddr); + /* 100MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x10, 0x8806, miiAddr); + /* 100MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x11, 0x003e, miiAddr); + /* 10MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x10, 0x8807, miiAddr); + /* 10MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x11, 0x1f00, miiAddr); + /* point to hidden reg 0x2806 */ + ql_mii_write_reg_ex(qdev, 0x10, 0x2806, miiAddr); + /* Write new PHYAD w/bit 5 set */ + ql_mii_write_reg_ex(qdev, 0x11, 0x0020 | (PHYAddr[qdev->mac_index] >> 8), miiAddr); + /* + * Disable diagnostic mode bit 2 = 0 + * Power up device bit 11 = 0 + * Link up (on) and activity (blink) + */ + ql_mii_write_reg(qdev, 0x12, 0x840a); + ql_mii_write_reg(qdev, 0x00, 0x1140); + ql_mii_write_reg(qdev, 0x1c, 0xfaf0); +} + +static PHY_DEVICE_et getPhyType (struct ql3_adapter *qdev, + u16 phyIdReg0, u16 phyIdReg1) +{ + PHY_DEVICE_et result = PHY_TYPE_UNKNOWN; + u32 oui; + u16 model; + int i; + + if (phyIdReg0 == 0xffff) { + return result; + } + + if (phyIdReg1 == 0xffff) { + return result; + } + + /* oui is split between two registers */ + oui = (phyIdReg0 << 6) | ((phyIdReg1 & PHY_OUI_1_MASK) >> 10); + + model = (phyIdReg1 & PHY_MODEL_MASK) >> 4; + + /* Scan table for this PHY */ + for(i = 0; i < MAX_PHY_DEV_TYPES; i++) { + if ((oui == PHY_DEVICES[i].phyIdOUI) && (model == PHY_DEVICES[i].phyIdModel)) + { + result = PHY_DEVICES[i].phyDevice; + + printk(KERN_INFO "%s: Phy: %s\n", + qdev->ndev->name, PHY_DEVICES[i].name); + + break; + } + } + + return result; +} + static int ql_phy_get_speed(struct ql3_adapter *qdev) { u16 reg; + switch(qdev->phyType) { + case PHY_AGERE_ET1011C: + { + if (ql_mii_read_reg(qdev, 0x1A, ®) < 0) + return 0; + + reg = (reg >> 8) & 3; + break; + } + default: if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) return 0; reg = (((reg & 0x18) >> 3) & 3); + } - if (reg == 2) + switch(reg) { + case 2: return SPEED_1000; - else if (reg == 1) + case 1: return SPEED_100; - else if (reg == 0) + case 0: return SPEED_10; - else + default: return -1; + } } static int ql_is_full_dup(struct ql3_adapter *qdev) { u16 reg; - if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) - return 0; - - return (reg & PHY_AUX_DUPLEX_STAT) != 0; + switch(qdev->phyType) { + case PHY_AGERE_ET1011C: + { + if (ql_mii_read_reg(qdev, 0x1A, ®)) + return 0; + + return ((reg & 0x0080) && (reg & 0x1000)) != 0; + } + case PHY_VITESSE_VSC8211: + default: + { + if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) + return 0; + return (reg & PHY_AUX_DUPLEX_STAT) != 0; + } + } } static int ql_is_phy_neg_pause(struct ql3_adapter *qdev) @@ -935,6 +1057,73 @@ static int ql_is_phy_neg_pause(struct ql return (reg & PHY_NEG_PAUSE) != 0; } +static int PHY_Setup(struct ql3_adapter *qdev) +{ + u16 reg1; + u16 reg2; + bool agereAddrChangeNeeded = false; + u32 miiAddr = 0; + int err; + + /* Determine the PHY we are using by reading the ID's */ + err = ql_mii_read_reg(qdev, PHY_ID_0_REG, ®1); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n", + qdev->ndev->name); + return err; + } + + err = ql_mii_read_reg(qdev, PHY_ID_1_REG, ®2); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n", + qdev->ndev->name); + return err; + } + + /* Check if we have a Agere PHY */ + if ((reg1 == 0xffff) || (reg2 == 0xffff)) { + + /* Determine which MII address we should be using + determined by the index of the card */ + if (qdev->mac_index == 0) { + miiAddr = MII_AGERE_ADDR_1; + } else { + miiAddr = MII_AGERE_ADDR_2; + } + + err =ql_mii_read_reg_ex(qdev, PHY_ID_0_REG, ®1, miiAddr); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n", + qdev->ndev->name); + return err; + } + + err = ql_mii_read_reg_ex(qdev, PHY_ID_1_REG, ®2, miiAddr); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n", + qdev->ndev->name); + return err; + } + + /* We need to remember to initialize the Agere PHY */ + agereAddrChangeNeeded = true; + } + + /* Determine the particular PHY we have on board to apply + PHY specific initializations */ + qdev->phyType = getPhyType(qdev, reg1, reg2); + + if ((qdev->phyType == PHY_AGERE_ET1011C) && agereAddrChangeNeeded) { + /* need this here so address gets changed */ + phyAgereSpecificInit(qdev, miiAddr); + } else if (qdev->phyType == PHY_TYPE_UNKNOWN) { + printk(KERN_ERR "%s: PHY is unknown\n", qdev->ndev->name); + return -EIO; + } + + return 0; +} + /* * Caller holds hw_lock. */ @@ -1205,15 +1394,14 @@ static int ql_link_down_detect_clear(str /* * Caller holds hw_lock. */ -static int ql_this_adapter_controls_port(struct ql3_adapter *qdev, - u32 mac_index) +static int ql_this_adapter_controls_port(struct ql3_adapter *qdev) { struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; u32 bitToCheck = 0; u32 temp; - switch (mac_index) { + switch (qdev->mac_index) { case 0: bitToCheck = PORT_STATUS_F1_ENABLED; break; @@ -1238,27 +1426,96 @@ static int ql_this_adapter_controls_port } } -static void ql_phy_reset_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_phy_reset_ex(struct ql3_adapter *qdev) { - ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET, mac_index); + ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET, + PHYAddr[qdev->mac_index]); } -static void ql_phy_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_phy_start_neg_ex(struct ql3_adapter *qdev) { u16 reg; + u16 portConfiguration; + + if(qdev->phyType == PHY_AGERE_ET1011C) { + /* turn off external loopback */ + ql_mii_write_reg(qdev, 0x13, 0x0000); + } + + if(qdev->mac_index == 0) + portConfiguration = qdev->nvram_data.macCfg_port0.portConfiguration; + else + portConfiguration = qdev->nvram_data.macCfg_port1.portConfiguration; + + /* Some HBA's in the field are set to 0 and they need to + be reinterpreted with a default value */ + if(portConfiguration == 0) + portConfiguration = PORT_CONFIG_DEFAULT; + + /* Set the 1000 advertisements */ + ql_mii_read_reg_ex(qdev, PHY_GIG_CONTROL, ®, + PHYAddr[qdev->mac_index]); + reg &= ~PHY_GIG_ALL_PARAMS; + + if(portConfiguration & + PORT_CONFIG_FULL_DUPLEX_ENABLED & + PORT_CONFIG_1000MB_SPEED) { + reg |= PHY_GIG_ADV_1000F; + } + + if(portConfiguration & + PORT_CONFIG_HALF_DUPLEX_ENABLED & + PORT_CONFIG_1000MB_SPEED) { + reg |= PHY_GIG_ADV_1000H; + } - ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, - PHY_NEG_PAUSE | PHY_NEG_ADV_SPEED | 1, mac_index); + ql_mii_write_reg_ex(qdev, PHY_GIG_CONTROL, reg, + PHYAddr[qdev->mac_index]); - ql_mii_read_reg_ex(qdev, CONTROL_REG, ®, mac_index); - ql_mii_write_reg_ex(qdev, CONTROL_REG, reg | PHY_CTRL_RESTART_NEG, - mac_index); + /* Set the 10/100 & pause negotiation advertisements */ + ql_mii_read_reg_ex(qdev, PHY_NEG_ADVER, ®, + PHYAddr[qdev->mac_index]); + reg &= ~PHY_NEG_ALL_PARAMS; + + if(portConfiguration & PORT_CONFIG_SYM_PAUSE_ENABLED) + reg |= PHY_NEG_ASY_PAUSE | PHY_NEG_SYM_PAUSE; + + if(portConfiguration & PORT_CONFIG_FULL_DUPLEX_ENABLED) { + if(portConfiguration & PORT_CONFIG_100MB_SPEED) + reg |= PHY_NEG_ADV_100F; + + if(portConfiguration & PORT_CONFIG_10MB_SPEED) + reg |= PHY_NEG_ADV_10F; + } + + if(portConfiguration & PORT_CONFIG_HALF_DUPLEX_ENABLED) { + if(portConfiguration & PORT_CONFIG_100MB_SPEED) + reg |= PHY_NEG_ADV_100H; + + if(portConfiguration & PORT_CONFIG_10MB_SPEED) + reg |= PHY_NEG_ADV_10H; + } + + if(portConfiguration & + PORT_CONFIG_1000MB_SPEED) { + reg |= 1; + } + + ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, reg, + PHYAddr[qdev->mac_index]); + + ql_mii_read_reg_ex(qdev, CONTROL_REG, ®, PHYAddr[qdev->mac_index]); + + ql_mii_write_reg_ex(qdev, CONTROL_REG, + reg | PHY_CTRL_RESTART_NEG | PHY_CTRL_AUTO_NEG, + PHYAddr[qdev->mac_index]); } -static void ql_phy_init_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_phy_init_ex(struct ql3_adapter *qdev) { - ql_phy_reset_ex(qdev, mac_index); - ql_phy_start_neg_ex(qdev, mac_index); + ql_phy_reset_ex(qdev); + PHY_Setup(qdev); + ql_phy_start_neg_ex(qdev); } /* @@ -1295,14 +1552,17 @@ static int ql_port_start(struct ql3_adap { if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * - 2) << 7)) + 2) << 7)) { + printk(KERN_ERR "%s: Could not get hw lock for GIO\n", + qdev->ndev->name); return -1; + } if (ql_is_fiber(qdev)) { ql_petbi_init(qdev); } else { /* Copper port */ - ql_phy_init_ex(qdev, qdev->mac_index); + ql_phy_init_ex(qdev); } ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); @@ -1453,7 +1713,7 @@ static void ql_link_state_machine(struct */ static void ql_get_phy_owner(struct ql3_adapter *qdev) { - if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) + if (ql_this_adapter_controls_port(qdev)) set_bit(QL_LINK_MASTER,&qdev->flags); else clear_bit(QL_LINK_MASTER,&qdev->flags); @@ -1467,11 +1727,11 @@ static void ql_init_scan_mode(struct ql3 ql_mii_enable_scan_mode(qdev); if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) { - if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) - ql_petbi_init_ex(qdev, qdev->mac_index); + if (ql_this_adapter_controls_port(qdev)) + ql_petbi_init_ex(qdev); } else { - if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) - ql_phy_init_ex(qdev, qdev->mac_index); + if (ql_this_adapter_controls_port(qdev)) + ql_phy_init_ex(qdev); } } @@ -1624,6 +1884,23 @@ static void ql_set_msglevel(struct net_d qdev->msg_enable = value; } +static void ql_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + + u32 reg; + if(qdev->mac_index == 0) + reg = ql_read_page0_reg(qdev, &port_regs->mac0ConfigReg); + else + reg = ql_read_page0_reg(qdev, &port_regs->mac1ConfigReg); + + pause->autoneg = ql_get_auto_cfg_status(qdev); + pause->rx_pause = (reg & MAC_CONFIG_REG_RF) >> 2; + pause->tx_pause = (reg & MAC_CONFIG_REG_TF) >> 1; +} + static const struct ethtool_ops ql3xxx_ethtool_ops = { .get_settings = ql_get_settings, .get_drvinfo = ql_get_drvinfo, @@ -1631,6 +1908,7 @@ static const struct ethtool_ops ql3xxx_e .get_link = ethtool_op_get_link, .get_msglevel = ql_get_msglevel, .set_msglevel = ql_set_msglevel, + .get_pauseparam = ql_get_pauseparam, }; static int ql_populate_free_queue(struct ql3_adapter *qdev) @@ -1815,14 +2093,14 @@ invalid_seg_count: atomic_inc(&qdev->tx_count); } -void ql_get_sbuf(struct ql3_adapter *qdev) +static void ql_get_sbuf(struct ql3_adapter *qdev) { if (++qdev->small_buf_index == NUM_SMALL_BUFFERS) qdev->small_buf_index = 0; qdev->small_buf_release_cnt++; } -struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev) +static struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev) { struct ql_rcv_buf_cb *lrg_buf_cb = NULL; lrg_buf_cb = &qdev->lrg_buf[qdev->lrg_buf_index]; @@ -1873,7 +2151,6 @@ static void ql_process_mac_rx_intr(struc pci_unmap_len(lrg_buf_cb2, maplen), PCI_DMA_FROMDEVICE); prefetch(skb->data); - skb->dev = qdev->ndev; skb->ip_summed = CHECKSUM_NONE; skb->protocol = eth_type_trans(skb, qdev->ndev); @@ -1928,7 +2205,8 @@ static void ql_process_macip_rx_intr(str * Copy the ethhdr from first buffer to second. This * is necessary for 3022 IP completions. */ - memcpy(skb_push(skb2, size), skb1->data + VLAN_ID_LEN, size); + skb_copy_from_linear_data_offset(skb1, VLAN_ID_LEN, + skb_push(skb2, size), size); } else { u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum); if (checksum & @@ -1946,7 +2224,6 @@ static void ql_process_macip_rx_intr(str skb2->ip_summed = CHECKSUM_UNNECESSARY; } } - skb2->dev = qdev->ndev; skb2->protocol = eth_type_trans(skb2, qdev->ndev); netif_receive_skb(skb2); @@ -3075,6 +3352,7 @@ static int ql_adapter_initialize(struct goto out; } + PHY_Setup(qdev); ql_init_scan_mode(qdev); ql_get_phy_owner(qdev); diff --git a/drivers/net/qla3xxx.h b/drivers/net/qla3xxx.h index 0203f88..4a832c4 100755 --- a/drivers/net/qla3xxx.h +++ b/drivers/net/qla3xxx.h @@ -293,6 +293,16 @@ #define ETHERNET_CRC_SIZE 4 #define MII_SCAN_REGISTER 0x00000001 +#define PHY_ID_0_REG 2 +#define PHY_ID_1_REG 3 + +#define PHY_OUI_1_MASK 0xfc00 +#define PHY_MODEL_MASK 0x03f0 + +/* Address for the Agere Phy */ +#define MII_AGERE_ADDR_1 0x00001000 +#define MII_AGERE_ADDR_2 0x00001100 + /* 32-bit ispControlStatus */ enum { ISP_CONTROL_NP_MASK = 0x0003, @@ -789,6 +799,7 @@ enum { PHY_CTRL_LOOPBACK = 0x4000, PETBI_CONTROL_REG = 0x00, + PETBI_CTRL_ALL_PARAMS = 0x7140, PETBI_CTRL_SOFT_RESET = 0x8000, PETBI_CTRL_AUTO_NEG = 0x1000, PETBI_CTRL_RESTART_NEG = 0x0200, @@ -811,6 +822,23 @@ enum { PETBI_EXPANSION_REG = 0x06, PETBI_EXP_PAGE_RX = 0x0002, + PHY_GIG_CONTROL = 9, + PHY_GIG_ENABLE_MAN = 0x1000, /* Enable Master/Slave Manual Config*/ + PHY_GIG_SET_MASTER = 0x0800, /* Set Master (slave if clear)*/ + PHY_GIG_ALL_PARAMS = 0x0300, + PHY_GIG_ADV_1000F = 0x0200, + PHY_GIG_ADV_1000H = 0x0100, + + PHY_NEG_ADVER = 4, + PHY_NEG_ALL_PARAMS = 0x0fe0, + PHY_NEG_ASY_PAUSE = 0x0800, + PHY_NEG_SYM_PAUSE = 0x0400, + PHY_NEG_ADV_SPEED = 0x01e0, + PHY_NEG_ADV_100F = 0x0100, + PHY_NEG_ADV_100H = 0x0080, + PHY_NEG_ADV_10F = 0x0040, + PHY_NEG_ADV_10H = 0x0020, + PETBI_TBI_CTRL = 0x11, PETBI_TBI_RESET = 0x8000, PETBI_TBI_AUTO_SENSE = 0x0100, @@ -826,8 +854,7 @@ enum { PHY_AUX_RESET_STICK = 0x0002, PHY_NEG_PAUSE = 0x0400, PHY_CTRL_SOFT_RESET = 0x8000, - PHY_NEG_ADVER = 4, - PHY_NEG_ADV_SPEED = 0x01e0, + PHY_CTRL_AUTO_NEG = 0x1000, PHY_CTRL_RESTART_NEG = 0x0200, }; enum { @@ -892,6 +919,7 @@ enum {EEPROM_SIZE = FM93C86A_SIZE_16, u16 pauseThreshold_mac; u16 resumeThreshold_mac; u16 portConfiguration; +#define PORT_CONFIG_DEFAULT 0xf700 #define PORT_CONFIG_AUTO_NEG_ENABLED 0x8000 #define PORT_CONFIG_SYM_PAUSE_ENABLED 0x4000 #define PORT_CONFIG_FULL_DUPLEX_ENABLED 0x2000 @@ -1259,6 +1287,7 @@ struct ql3_adapter { struct delayed_work tx_timeout_work; u32 max_frame_size; u32 device_id; + u16 phyType; }; #endif /* _QLA3XXX_H_ */ diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 6a77b8a..45876a8 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -2284,7 +2284,7 @@ static inline u32 rtl8169_tso_csum(struc return LargeSend | ((mss & MSSMask) << MSSShift); } if (skb->ip_summed == CHECKSUM_PARTIAL) { - const struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) return IPCS | TCPCS; @@ -2586,7 +2586,6 @@ rtl8169_rx_interrupt(struct net_device * pci_action(tp->pci_dev, le64_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb->dev = dev; skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index b7ff484..df6b738 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -115,7 +115,6 @@ static int rionet_rx_clean(struct net_de rnet->rx_skb[i]->data = data; skb_put(rnet->rx_skb[i], RIO_MAX_MSG_SIZE); - rnet->rx_skb[i]->dev = ndev; rnet->rx_skb[i]->protocol = eth_type_trans(rnet->rx_skb[i], ndev); error = netif_rx(rnet->rx_skb[i]); diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c index d81536f..25c73d4 100644 --- a/drivers/net/rrunner.c +++ b/drivers/net/rrunner.c @@ -1029,7 +1029,6 @@ #endif goto defer; } } - skb->dev = dev; skb->protocol = hippi_type_trans(skb, dev); netif_rx(skb); /* send it up */ @@ -1452,7 +1451,7 @@ static int rr_start_xmit(struct sk_buff } skb_reserve(new_skb, 8); skb_put(new_skb, len); - memcpy(new_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, new_skb->data, len); dev_kfree_skb(skb); skb = new_skb; } diff --git a/drivers/net/s2io-regs.h b/drivers/net/s2io-regs.h index 33fb7f3..4cb710b 100644 --- a/drivers/net/s2io-regs.h +++ b/drivers/net/s2io-regs.h @@ -1,6 +1,6 @@ /************************************************************************ * regs.h: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC - * Copyright(c) 2002-2005 Neterion Inc. + * Copyright(c) 2002-2007 Neterion Inc. * This software may be used and distributed according to the terms of * the GNU General Public License (GPL), incorporated herein by reference. diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 46ebf14..290e1c1 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -1,6 +1,6 @@ /************************************************************************ * s2io.c: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC - * Copyright(c) 2002-2005 Neterion Inc. + * Copyright(c) 2002-2007 Neterion Inc. * This software may be used and distributed according to the terms of * the GNU General Public License (GPL), incorporated herein by reference. @@ -84,7 +84,7 @@ #include #include "s2io.h" #include "s2io-regs.h" -#define DRV_VERSION "2.0.17.1" +#define DRV_VERSION "2.0.22.1" /* S2io Driver name & version. */ static char s2io_driver_name[] = "Neterion"; @@ -316,7 +316,7 @@ static void s2io_vlan_rx_register(struct } /* A flag indicating whether 'RX_PA_CFG_STRIP_VLAN_TAG' bit is set or not */ -int vlan_strip_flag; +static int vlan_strip_flag; /* Unregister the vlan */ static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned long vid) @@ -394,7 +394,6 @@ static const u64 fix_mac[] = { END_SIGN }; -MODULE_AUTHOR("Raghavendra Koushik "); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); @@ -516,7 +515,7 @@ static int init_shared_mem(struct s2io_n mac_control->fifos[i].list_info = kmalloc(list_holder_size, GFP_KERNEL); if (!mac_control->fifos[i].list_info) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "Malloc failed for list_info\n"); return -ENOMEM; } @@ -542,9 +541,9 @@ static int init_shared_mem(struct s2io_n tmp_v = pci_alloc_consistent(nic->pdev, PAGE_SIZE, &tmp_p); if (!tmp_v) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "pci_alloc_consistent "); - DBG_PRINT(ERR_DBG, "failed for TxDL\n"); + DBG_PRINT(INFO_DBG, "failed for TxDL\n"); return -ENOMEM; } /* If we got a zero DMA address(can happen on @@ -561,9 +560,9 @@ static int init_shared_mem(struct s2io_n tmp_v = pci_alloc_consistent(nic->pdev, PAGE_SIZE, &tmp_p); if (!tmp_v) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "pci_alloc_consistent "); - DBG_PRINT(ERR_DBG, "failed for TxDL\n"); + DBG_PRINT(INFO_DBG, "failed for TxDL\n"); return -ENOMEM; } } @@ -2187,7 +2186,7 @@ static int fill_rxd_3buf(struct s2io_nic /* skb_shinfo(skb)->frag_list will have L4 data payload */ skb_shinfo(skb)->frag_list = dev_alloc_skb(dev->mtu + ALIGN_SIZE); if (skb_shinfo(skb)->frag_list == NULL) { - DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n ", dev->name); + DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n ", dev->name); return -ENOMEM ; } frag_list = skb_shinfo(skb)->frag_list; @@ -2195,7 +2194,7 @@ static int fill_rxd_3buf(struct s2io_nic frag_list->next = NULL; tmp = (void *)ALIGN((long)frag_list->data, ALIGN_SIZE + 1); frag_list->data = tmp; - frag_list->tail = tmp; + skb_reset_tail_pointer(frag_list); /* Buffer-2 receives L4 data payload */ ((struct RxD3*)rxdp)->Buffer2_ptr = pci_map_single(nic->pdev, @@ -2242,6 +2241,7 @@ static int fill_rx_buffers(struct s2io_n struct buffAdd *ba; unsigned long flags; struct RxD_t *first_rxdp = NULL; + u64 Buffer0_ptr = 0, Buffer1_ptr = 0; mac_control = &nic->mac_control; config = &nic->config; @@ -2313,8 +2313,8 @@ static int fill_rx_buffers(struct s2io_n /* allocate skb */ skb = dev_alloc_skb(size); if(!skb) { - DBG_PRINT(ERR_DBG, "%s: Out of ", dev->name); - DBG_PRINT(ERR_DBG, "memory to allocate SKBs\n"); + DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name); + DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n"); if (first_rxdp) { wmb(); first_rxdp->Control_1 |= RXD_OWN_XENA; @@ -2342,14 +2342,21 @@ static int fill_rx_buffers(struct s2io_n * payload */ + /* save the buffer pointers to avoid frequent dma mapping */ + Buffer0_ptr = ((struct RxD3*)rxdp)->Buffer0_ptr; + Buffer1_ptr = ((struct RxD3*)rxdp)->Buffer1_ptr; memset(rxdp, 0, sizeof(struct RxD3)); + /* restore the buffer pointers for dma sync*/ + ((struct RxD3*)rxdp)->Buffer0_ptr = Buffer0_ptr; + ((struct RxD3*)rxdp)->Buffer1_ptr = Buffer1_ptr; + ba = &mac_control->rings[ring_no].ba[block_no][off]; skb_reserve(skb, BUF0_LEN); tmp = (u64)(unsigned long) skb->data; tmp += ALIGN_SIZE; tmp &= ~ALIGN_SIZE; skb->data = (void *) (unsigned long)tmp; - skb->tail = (void *) (unsigned long)tmp; + skb_reset_tail_pointer(skb); if (!(((struct RxD3*)rxdp)->Buffer0_ptr)) ((struct RxD3*)rxdp)->Buffer0_ptr = @@ -2573,8 +2580,8 @@ static int s2io_poll(struct net_device * for (i = 0; i < config->rx_ring_num; i++) { if (fill_rx_buffers(nic, i) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); - DBG_PRINT(ERR_DBG, " in Rx Poll!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); + DBG_PRINT(INFO_DBG, " in Rx Poll!!\n"); break; } } @@ -2590,8 +2597,8 @@ no_rx: for (i = 0; i < config->rx_ring_num; i++) { if (fill_rx_buffers(nic, i) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); - DBG_PRINT(ERR_DBG, " in Rx Poll!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); + DBG_PRINT(INFO_DBG, " in Rx Poll!!\n"); break; } } @@ -2640,8 +2647,8 @@ static void s2io_netpoll(struct net_devi for (i = 0; i < config->rx_ring_num; i++) { if (fill_rx_buffers(nic, i) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); - DBG_PRINT(ERR_DBG, " in Rx Netpoll!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); + DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n"); break; } } @@ -3307,6 +3314,7 @@ static void s2io_reset(struct s2io_nic * u16 subid, pci_cmd; int i; u16 val16; + unsigned long long reset_cnt = 0; DBG_PRINT(INIT_DBG,"%s - Resetting XFrame card %s\n", __FUNCTION__, sp->dev->name); @@ -3372,6 +3380,11 @@ new_way: /* Reset device statistics maintained by OS */ memset(&sp->stats, 0, sizeof (struct net_device_stats)); + /* save reset count */ + reset_cnt = sp->mac_control.stats_info->sw_stat.soft_reset_cnt; + memset(sp->mac_control.stats_info, 0, sizeof(struct stat_block)); + /* restore reset count */ + sp->mac_control.stats_info->sw_stat.soft_reset_cnt = reset_cnt; /* SXE-002: Configure link and activity LED to turn it off */ subid = sp->pdev->subsystem_device; @@ -3659,7 +3672,7 @@ static int s2io_enable_msi_x(struct s2io nic->entries = kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct msix_entry), GFP_KERNEL); if (nic->entries == NULL) { - DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__); + DBG_PRINT(INFO_DBG, "%s: Memory allocation failed\n", __FUNCTION__); return -ENOMEM; } memset(nic->entries, 0, MAX_REQUESTED_MSI_X * sizeof(struct msix_entry)); @@ -3668,7 +3681,7 @@ static int s2io_enable_msi_x(struct s2io kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct s2io_msix_entry), GFP_KERNEL); if (nic->s2io_entries == NULL) { - DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__); + DBG_PRINT(INFO_DBG, "%s: Memory allocation failed\n", __FUNCTION__); kfree(nic->entries); return -ENOMEM; } @@ -4019,7 +4032,7 @@ static int s2io_chk_rx_buffers(struct s2 DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__); DBG_PRINT(INTR_DBG, "PANIC levels\n"); if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "Out of memory in %s", + DBG_PRINT(INFO_DBG, "Out of memory in %s", __FUNCTION__); clear_bit(0, (&sp->tasklet_status)); return -1; @@ -4029,8 +4042,8 @@ static int s2io_chk_rx_buffers(struct s2 tasklet_schedule(&sp->task); } else if (fill_rx_buffers(sp, rng_n) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", sp->dev->name); - DBG_PRINT(ERR_DBG, " in Rx Intr!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", sp->dev->name); + DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); } return 0; } @@ -4279,9 +4292,7 @@ static void s2io_updt_stats(struct s2io_ if (cnt == 5) break; /* Updt failed */ } while(1); - } else { - memset(sp->mac_control.stats_info, 0, sizeof(struct stat_block)); - } + } } /** @@ -5949,12 +5960,12 @@ static void s2io_tasklet(unsigned long d for (i = 0; i < config->rx_ring_num; i++) { ret = fill_rx_buffers(sp, i); if (ret == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s: Out of ", + DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name); DBG_PRINT(ERR_DBG, "memory in tasklet\n"); break; } else if (ret == -EFILL) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "%s: Rx Ring %d is full\n", dev->name, i); break; @@ -6065,8 +6076,8 @@ static int set_rxd_buffer_pointer(struct } else { *skb = dev_alloc_skb(size); if (!(*skb)) { - DBG_PRINT(ERR_DBG, "%s: Out of ", dev->name); - DBG_PRINT(ERR_DBG, "memory to allocate SKBs\n"); + DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name); + DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n"); return -ENOMEM ; } /* storing the mapped addr in a temp variable @@ -6088,7 +6099,7 @@ static int set_rxd_buffer_pointer(struct } else { *skb = dev_alloc_skb(size); if (!(*skb)) { - DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n", + DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n", dev->name); return -ENOMEM; } @@ -6115,7 +6126,7 @@ static int set_rxd_buffer_pointer(struct } else { *skb = dev_alloc_skb(size); if (!(*skb)) { - DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n", + DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n", dev->name); return -ENOMEM; } @@ -6616,7 +6627,6 @@ static int rx_osm_handler(struct ring_in /* Updating statistics */ rxdp->Host_Control = 0; - sp->rx_pkt_count++; sp->stats.rx_packets++; if (sp->rxd_mode == RXD_MODE_1) { int len = RXD_GET_BUFFER0_SIZE_1(rxdp->Control_2); @@ -7252,7 +7262,7 @@ #endif goto register_failed; } s2io_vpd_read(sp); - DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2005 Neterion Inc.\n"); + DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2007 Neterion Inc.\n"); DBG_PRINT(ERR_DBG, "%s: Neterion %s (rev %d)\n",dev->name, sp->product_name, get_xena_rev_id(sp->pdev)); DBG_PRINT(ERR_DBG, "%s: Driver version %s\n", dev->name, diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h index 803137c..a656d18 100644 --- a/drivers/net/s2io.h +++ b/drivers/net/s2io.h @@ -1,6 +1,6 @@ /************************************************************************ * s2io.h: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC - * Copyright(c) 2002-2005 Neterion Inc. + * Copyright(c) 2002-2007 Neterion Inc. * This software may be used and distributed according to the terms of * the GNU General Public License (GPL), incorporated herein by reference. @@ -760,7 +760,6 @@ #define MAX_MAC_SUPPORTED 16 #define MAX_SUPPORTED_MULTICASTS MAX_MAC_SUPPORTED struct mac_addr def_mac_addr[MAX_MAC_SUPPORTED]; - struct mac_addr pre_mac_addr[MAX_MAC_SUPPORTED]; struct net_device_stats stats; int high_dma_flag; @@ -794,11 +793,6 @@ #define MAX_ADDRS_SUPPORTED 64 u16 all_multi_pos; u16 promisc_flg; - u16 tx_pkt_count; - u16 rx_pkt_count; - u16 tx_err_count; - u16 rx_err_count; - /* Id timer, used to blink NIC to physically identify NIC. */ struct timer_list id_timer; diff --git a/drivers/net/saa9730.c b/drivers/net/saa9730.c index 143958f..ad94358 100644 --- a/drivers/net/saa9730.c +++ b/drivers/net/saa9730.c @@ -688,7 +688,6 @@ static int lan_saa9730_rx(struct net_dev } else { lp->stats.rx_bytes += len; lp->stats.rx_packets++; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ eth_copy_and_sum(skb, diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index b9fa4fb..1de3eec 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -834,7 +834,7 @@ #endif /* XXXDEBUG */ goto dropped_frame; } skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = (unsigned short) buffer[NewDatagramHeaderSkip + 16]; insw(ioaddr, skb_put(skb, NewDatagramDataSize), NewDatagramDataSize / 2); diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 103c317..132e214 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -95,19 +95,28 @@ MODULE_PARM_DESC(full_duplex, "1-" __MOD #endif #ifdef CONFIG_SBMAC_COALESCE -static int int_pktcnt = 0; -module_param(int_pktcnt, int, S_IRUGO); -MODULE_PARM_DESC(int_pktcnt, "Packet count"); +static int int_pktcnt_tx = 255; +module_param(int_pktcnt_tx, int, S_IRUGO); +MODULE_PARM_DESC(int_pktcnt_tx, "TX packet count"); -static int int_timeout = 0; -module_param(int_timeout, int, S_IRUGO); -MODULE_PARM_DESC(int_timeout, "Timeout value"); +static int int_timeout_tx = 255; +module_param(int_timeout_tx, int, S_IRUGO); +MODULE_PARM_DESC(int_timeout_tx, "TX timeout value"); + +static int int_pktcnt_rx = 64; +module_param(int_pktcnt_rx, int, S_IRUGO); +MODULE_PARM_DESC(int_pktcnt_rx, "RX packet count"); + +static int int_timeout_rx = 64; +module_param(int_timeout_rx, int, S_IRUGO); +MODULE_PARM_DESC(int_timeout_rx, "RX timeout value"); #endif #include #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) #include #include +#define R_MAC_DMA_OODPKTLOST_RX R_MAC_DMA_OODPKTLOST #elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X) #include #include @@ -155,8 +164,8 @@ #define SBDMA_NEXTBUF(d,f) ((((d)->f+1) #define NUMCACHEBLKS(x) (((x)+SMP_CACHE_BYTES-1)/SMP_CACHE_BYTES) -#define SBMAC_MAX_TXDESCR 32 -#define SBMAC_MAX_RXDESCR 32 +#define SBMAC_MAX_TXDESCR 256 +#define SBMAC_MAX_RXDESCR 256 #define ETHER_ALIGN 2 #define ETHER_ADDR_LEN 6 @@ -185,10 +194,10 @@ typedef struct sbmacdma_s { * associated with it. */ - struct sbmac_softc *sbdma_eth; /* back pointer to associated MAC */ - int sbdma_channel; /* channel number */ + struct sbmac_softc *sbdma_eth; /* back pointer to associated MAC */ + int sbdma_channel; /* channel number */ int sbdma_txdir; /* direction (1=transmit) */ - int sbdma_maxdescr; /* total # of descriptors in ring */ + int sbdma_maxdescr; /* total # of descriptors in ring */ #ifdef CONFIG_SBMAC_COALESCE int sbdma_int_pktcnt; /* # descriptors rx/tx before interrupt*/ int sbdma_int_timeout; /* # usec rx/tx interrupt */ @@ -197,13 +206,16 @@ #endif volatile void __iomem *sbdma_config0; /* DMA config register 0 */ volatile void __iomem *sbdma_config1; /* DMA config register 1 */ volatile void __iomem *sbdma_dscrbase; /* Descriptor base address */ - volatile void __iomem *sbdma_dscrcnt; /* Descriptor count register */ + volatile void __iomem *sbdma_dscrcnt; /* Descriptor count register */ volatile void __iomem *sbdma_curdscr; /* current descriptor address */ + volatile void __iomem *sbdma_oodpktlost;/* pkt drop (rx only) */ + /* * This stuff is for maintenance of the ring */ + sbdmadscr_t *sbdma_dscrtable_unaligned; sbdmadscr_t *sbdma_dscrtable; /* base of descriptor table */ sbdmadscr_t *sbdma_dscrtable_end; /* end of descriptor table */ @@ -286,8 +298,8 @@ static int sbdma_add_rcvbuffer(sbmacdma_ static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *m); static void sbdma_emptyring(sbmacdma_t *d); static void sbdma_fillring(sbmacdma_t *d); -static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d); -static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d); +static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d, int work_to_do, int poll); +static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll); static int sbmac_initctx(struct sbmac_softc *s); static void sbmac_channel_start(struct sbmac_softc *s); static void sbmac_channel_stop(struct sbmac_softc *s); @@ -308,6 +320,8 @@ static struct net_device_stats *sbmac_ge static void sbmac_set_rx_mode(struct net_device *dev); static int sbmac_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int sbmac_close(struct net_device *dev); +static int sbmac_poll(struct net_device *poll_dev, int *budget); + static int sbmac_mii_poll(struct sbmac_softc *s,int noisy); static int sbmac_mii_probe(struct net_device *dev); @@ -679,6 +693,10 @@ static void sbdma_initctx(sbmacdma_t *d, int txrx, int maxdescr) { +#ifdef CONFIG_SBMAC_COALESCE + int int_pktcnt, int_timeout; +#endif + /* * Save away interesting stuff in the structure */ @@ -728,6 +746,11 @@ #endif s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_CNT); d->sbdma_curdscr = s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CUR_DSCRADDR); + if (d->sbdma_txdir) + d->sbdma_oodpktlost = NULL; + else + d->sbdma_oodpktlost = + s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_OODPKTLOST_RX); /* * Allocate memory for the ring @@ -735,6 +758,7 @@ #endif d->sbdma_maxdescr = maxdescr; + d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = (sbdmadscr_t *) kmalloc((d->sbdma_maxdescr+1)*sizeof(sbdmadscr_t), GFP_KERNEL); @@ -765,12 +789,14 @@ #ifdef CONFIG_SBMAC_COALESCE * Setup Rx/Tx DMA coalescing defaults */ + int_pktcnt = (txrx == DMA_TX) ? int_pktcnt_tx : int_pktcnt_rx; if ( int_pktcnt ) { d->sbdma_int_pktcnt = int_pktcnt; } else { d->sbdma_int_pktcnt = 1; } + int_timeout = (txrx == DMA_TX) ? int_timeout_tx : int_timeout_rx; if ( int_timeout ) { d->sbdma_int_timeout = int_timeout; } else { @@ -933,9 +959,6 @@ static int sbdma_add_rcvbuffer(sbmacdma_ } sbdma_align_skb(sb_new, SMP_CACHE_BYTES, ETHER_ALIGN); - - /* mark skbuff owned by our device */ - sb_new->dev = d->sbdma_eth->sbm_dev; } else { sb_new = sb; @@ -1128,32 +1151,63 @@ static void sbdma_fillring(sbmacdma_t *d } } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void sbmac_netpoll(struct net_device *netdev) +{ + struct sbmac_softc *sc = netdev_priv(netdev); + int irq = sc->sbm_dev->irq; + + __raw_writeq(0, sc->sbm_imr); + + sbmac_intr(irq, netdev, NULL); + +#ifdef CONFIG_SBMAC_COALESCE + __raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | + ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), + sc->sbm_imr); +#else + __raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | + (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), sc->sbm_imr); +#endif +} +#endif /********************************************************************** - * SBDMA_RX_PROCESS(sc,d) + * SBDMA_RX_PROCESS(sc,d,work_to_do,poll) * * Process "completed" receive buffers on the specified DMA channel. - * Note that this isn't really ideal for priority channels, since - * it processes all of the packets on a given channel before - * returning. * * Input parameters: - * sc - softc structure - * d - DMA channel context + * sc - softc structure + * d - DMA channel context + * work_to_do - no. of packets to process before enabling interrupt + * again (for NAPI) + * poll - 1: using polling (for NAPI) * * Return value: * nothing ********************************************************************* */ -static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) +static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d, + int work_to_do, int poll) { int curidx; int hwidx; sbdmadscr_t *dsc; struct sk_buff *sb; int len; + int work_done = 0; + int dropped = 0; - for (;;) { + prefetch(d); + +again: + /* Check if the HW dropped any frames */ + sc->sbm_stats.rx_fifo_errors + += __raw_readq(sc->sbm_rxdma.sbdma_oodpktlost) & 0xffff; + __raw_writeq(0, sc->sbm_rxdma.sbdma_oodpktlost); + + while (work_to_do-- > 0) { /* * figure out where we are (as an index) and where * the hardware is (also as an index) @@ -1165,7 +1219,12 @@ static void sbdma_rx_process(struct sbma * (sbdma_remptr) and the physical address (sbdma_curdscr CSR) */ - curidx = d->sbdma_remptr - d->sbdma_dscrtable; + dsc = d->sbdma_remptr; + curidx = dsc - d->sbdma_dscrtable; + + prefetch(dsc); + prefetch(&d->sbdma_ctxtable[curidx]); + hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) - d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t)); @@ -1176,13 +1235,12 @@ static void sbdma_rx_process(struct sbma */ if (curidx == hwidx) - break; + goto done; /* * Otherwise, get the packet's sk_buff ptr back */ - dsc = &(d->sbdma_dscrtable[curidx]); sb = d->sbdma_ctxtable[curidx]; d->sbdma_ctxtable[curidx] = NULL; @@ -1194,7 +1252,7 @@ static void sbdma_rx_process(struct sbma * receive ring. */ - if (!(dsc->dscr_a & M_DMA_ETHRX_BAD)) { + if (likely (!(dsc->dscr_a & M_DMA_ETHRX_BAD))) { /* * Add a new buffer to replace the old one. If we fail @@ -1202,9 +1260,14 @@ static void sbdma_rx_process(struct sbma * packet and put it right back on the receive ring. */ - if (sbdma_add_rcvbuffer(d,NULL) == -ENOBUFS) { - sc->sbm_stats.rx_dropped++; + if (unlikely (sbdma_add_rcvbuffer(d,NULL) == + -ENOBUFS)) { + sc->sbm_stats.rx_dropped++; sbdma_add_rcvbuffer(d,sb); /* re-add old buffer */ + /* No point in continuing at the moment */ + printk(KERN_ERR "dropped packet (1)\n"); + d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); + goto done; } else { /* * Set length into the packet @@ -1216,8 +1279,6 @@ static void sbdma_rx_process(struct sbma * receive ring. Pass the buffer to * the kernel */ - sc->sbm_stats.rx_bytes += len; - sc->sbm_stats.rx_packets++; sb->protocol = eth_type_trans(sb,d->sbdma_eth->sbm_dev); /* Check hw IPv4/TCP checksum if supported */ if (sc->rx_hw_checksum == ENABLE) { @@ -1229,8 +1290,22 @@ static void sbdma_rx_process(struct sbma sb->ip_summed = CHECKSUM_NONE; } } - - netif_rx(sb); + prefetch(sb->data); + prefetch((const void *)(((char *)sb->data)+32)); + if (poll) + dropped = netif_receive_skb(sb); + else + dropped = netif_rx(sb); + + if (dropped == NET_RX_DROP) { + sc->sbm_stats.rx_dropped++; + d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); + goto done; + } + else { + sc->sbm_stats.rx_bytes += len; + sc->sbm_stats.rx_packets++; + } } } else { /* @@ -1247,12 +1322,16 @@ static void sbdma_rx_process(struct sbma */ d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); - + work_done++; + } + if (!poll) { + work_to_do = 32; + goto again; /* collect fifo drop statistics again */ } +done: + return work_done; } - - /********************************************************************** * SBDMA_TX_PROCESS(sc,d) * @@ -1264,22 +1343,30 @@ static void sbdma_rx_process(struct sbma * * Input parameters: * sc - softc structure - * d - DMA channel context + * d - DMA channel context + * poll - 1: using polling (for NAPI) * * Return value: * nothing ********************************************************************* */ -static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d) +static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll) { int curidx; int hwidx; sbdmadscr_t *dsc; struct sk_buff *sb; unsigned long flags; + int packets_handled = 0; spin_lock_irqsave(&(sc->sbm_lock), flags); + if (d->sbdma_remptr == d->sbdma_addptr) + goto end_unlock; + + hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) - + d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t)); + for (;;) { /* * figure out where we are (as an index) and where @@ -1293,8 +1380,6 @@ static void sbdma_tx_process(struct sbma */ curidx = d->sbdma_remptr - d->sbdma_dscrtable; - hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) - - d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t)); /* * If they're the same, that means we've processed all @@ -1332,6 +1417,8 @@ static void sbdma_tx_process(struct sbma d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); + packets_handled++; + } /* @@ -1340,8 +1427,10 @@ static void sbdma_tx_process(struct sbma * watermark on the transmit queue. */ - netif_wake_queue(d->sbdma_eth->sbm_dev); + if (packets_handled) + netif_wake_queue(d->sbdma_eth->sbm_dev); +end_unlock: spin_unlock_irqrestore(&(sc->sbm_lock), flags); } @@ -1415,9 +1504,9 @@ static int sbmac_initctx(struct sbmac_so static void sbdma_uninitctx(struct sbmacdma_s *d) { - if (d->sbdma_dscrtable) { - kfree(d->sbdma_dscrtable); - d->sbdma_dscrtable = NULL; + if (d->sbdma_dscrtable_unaligned) { + kfree(d->sbdma_dscrtable_unaligned); + d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL; } if (d->sbdma_ctxtable) { @@ -1615,15 +1704,9 @@ #error invalid SiByte MAC configuation #endif #ifdef CONFIG_SBMAC_COALESCE - /* - * Accept any TX interrupt and EOP count/timer RX interrupts on ch 0 - */ __raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), s->sbm_imr); #else - /* - * Accept any kind of interrupt on TX and RX DMA channel 0 - */ __raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), s->sbm_imr); #endif @@ -2056,57 +2139,46 @@ static irqreturn_t sbmac_intr(int irq,vo uint64_t isr; int handled = 0; - for (;;) { - - /* - * Read the ISR (this clears the bits in the real - * register, except for counter addr) - */ + /* + * Read the ISR (this clears the bits in the real + * register, except for counter addr) + */ - isr = __raw_readq(sc->sbm_isr) & ~M_MAC_COUNTER_ADDR; + isr = __raw_readq(sc->sbm_isr) & ~M_MAC_COUNTER_ADDR; - if (isr == 0) - break; + if (isr == 0) + return IRQ_RETVAL(0); + handled = 1; - handled = 1; - - /* - * Transmits on channel 0 - */ + /* + * Transmits on channel 0 + */ - if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) { - sbdma_tx_process(sc,&(sc->sbm_txdma)); + if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) { + sbdma_tx_process(sc,&(sc->sbm_txdma), 0); +#ifdef CONFIG_NETPOLL_TRAP + if (netpoll_trap()) { + if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) + __netif_schedule(dev); } +#endif + } - /* - * Receives on channel 0 - */ - - /* - * It's important to test all the bits (or at least the - * EOP_SEEN bit) when deciding to do the RX process - * particularly when coalescing, to make sure we - * take care of the following: - * - * If you have some packets waiting (have been received - * but no interrupt) and get a TX interrupt before - * the RX timer or counter expires, reading the ISR - * above will clear the timer and counter, and you - * won't get another interrupt until a packet shows - * up to start the timer again. Testing - * EOP_SEEN here takes care of this case. - * (EOP_SEEN is part of M_MAC_INT_CHANNEL << S_MAC_RX_CH0) - */ - - - if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) { - sbdma_rx_process(sc,&(sc->sbm_rxdma)); + if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) { + if (netif_rx_schedule_prep(dev)) { + __raw_writeq(0, sc->sbm_imr); + __netif_rx_schedule(dev); + /* Depend on the exit from poll to reenable intr */ + } + else { + /* may leave some packets behind */ + sbdma_rx_process(sc,&(sc->sbm_rxdma), + SBMAC_MAX_RXDESCR * 2, 0); } } return IRQ_RETVAL(handled); } - /********************************************************************** * SBMAC_START_TX(skb,dev) * @@ -2236,8 +2308,6 @@ static void sbmac_setmulti(struct sbmac_ } } - - #if defined(SBMAC_ETH0_HWADDR) || defined(SBMAC_ETH1_HWADDR) || defined(SBMAC_ETH2_HWADDR) || defined(SBMAC_ETH3_HWADDR) /********************************************************************** * SBMAC_PARSE_XDIGIT(str) @@ -2400,8 +2470,13 @@ static int sbmac_init(struct net_device dev->do_ioctl = sbmac_mii_ioctl; dev->tx_timeout = sbmac_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; + dev->poll = sbmac_poll; + dev->weight = 16; dev->change_mtu = sb1250_change_mtu; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = sbmac_netpoll; +#endif /* This is needed for PASS2 for Rx H/W checksum feature */ sbmac_set_iphdr_offset(sc); @@ -2799,7 +2874,39 @@ static int sbmac_close(struct net_device return 0; } +static int sbmac_poll(struct net_device *dev, int *budget) +{ + int work_to_do; + int work_done; + struct sbmac_softc *sc = netdev_priv(dev); + + work_to_do = min(*budget, dev->quota); + work_done = sbdma_rx_process(sc, &(sc->sbm_rxdma), work_to_do, 1); + if (work_done > work_to_do) + printk(KERN_ERR "%s exceeded work_to_do budget=%d quota=%d work-done=%d\n", + sc->sbm_dev->name, *budget, dev->quota, work_done); + + sbdma_tx_process(sc, &(sc->sbm_txdma), 1); + + *budget -= work_done; + dev->quota -= work_done; + + if (work_done < work_to_do) { + netif_rx_complete(dev); + +#ifdef CONFIG_SBMAC_COALESCE + __raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | + ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), + sc->sbm_imr); +#else + __raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | + (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), sc->sbm_imr); +#endif + } + + return (work_done >= work_to_do); +} #if defined(SBMAC_ETH0_HWADDR) || defined(SBMAC_ETH1_HWADDR) || defined(SBMAC_ETH2_HWADDR) || defined(SBMAC_ETH3_HWADDR) static void @@ -2886,7 +2993,7 @@ #endif /* * The R_MAC_ETHERNET_ADDR register will be set to some nonzero - * value for us by the firmware if we're going to use this MAC. + * value for us by the firmware if we are going to use this MAC. * If we find a zero, skip this MAC. */ diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index c32c21a..5b7284c 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -814,7 +814,6 @@ static void _sc92031_rx_tasklet(struct n memcpy(skb_put(skb, pkt_size), rx_ring + rx_ring_offset, pkt_size); } - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; netif_rx(skb); diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c index 0d6c95c..4bce7c4 100644 --- a/drivers/net/seeq8005.c +++ b/drivers/net/seeq8005.c @@ -550,7 +550,6 @@ static void seeq8005_rx(struct net_devic lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb, 2); /* align data on 16 byte */ buf = skb_put(skb,pkt_len); diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index 52ed522..1fc7730 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -318,7 +318,6 @@ static inline void sgiseeq_rx(struct net skb = dev_alloc_skb(len + 2); if (skb) { - skb->dev = dev; skb_reserve(skb, 2); skb_put(skb, len); @@ -535,7 +534,7 @@ static int sgiseeq_start_xmit(struct sk_ * entry and the HPC got to the end of the chain before we * added this new entry and restarted it. */ - memcpy((char *)(long)td->buf_vaddr, skb->data, skblen); + skb_copy_from_linear_data(skb, (char *)(long)td->buf_vaddr, skblen); if (len != skblen) memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen); td->tdma.cntinfo = (len & HPCDMA_BCNT) | @@ -625,7 +624,7 @@ static inline void setup_rx_ring(struct #define ALIGNED(x) ((((unsigned long)(x)) + 0xf) & ~(0xf)) -static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq) +static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq, int has_eeprom) { struct sgiseeq_init_block *sr; struct sgiseeq_private *sp; @@ -651,7 +650,9 @@ static int sgiseeq_init(struct hpc3_regs #define EADDR_NVOFS 250 for (i = 0; i < 3; i++) { - unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i); + unsigned short tmp = has_eeprom ? + ip22_eeprom_read(&hpcregs->eeprom, EADDR_NVOFS / 2+i) : + ip22_nvram_read(EADDR_NVOFS / 2+i); dev->dev_addr[2 * i] = tmp >> 8; dev->dev_addr[2 * i + 1] = tmp & 0xff; @@ -684,6 +685,11 @@ #endif sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; + /* Setup PIO and DMA transfer timing */ + sp->hregs->pconfig = 0x161; + sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | + HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; + /* Reset the chip. */ hpc3_eth_reset(sp->hregs); @@ -730,8 +736,23 @@ err_out: static int __init sgiseeq_probe(void) { + unsigned int tmp, ret1, ret2 = 0; + /* On board adapter on 1st HPC is always present */ - return sgiseeq_init(hpc3c0, SGI_ENET_IRQ); + ret1 = sgiseeq_init(hpc3c0, SGI_ENET_IRQ, 0); + /* Let's see if second HPC is there */ + if (!(ip22_is_fullhouse()) && + get_dbe(tmp, (unsigned int *)&hpc3c1->pbdma[1]) == 0) { + sgimc->giopar |= SGIMC_GIOPAR_MASTEREXP1 | + SGIMC_GIOPAR_EXP164 | + SGIMC_GIOPAR_HPC264; + hpc3c1->pbus_piocfg[0][0] = 0x3ffff; + /* interrupt/config register on Challenge S Mezz board */ + hpc3c1->pbus_extregs[0][0] = 0x30; + ret2 = sgiseeq_init(hpc3c1, SGI_GIO_0_IRQ, 1); + } + + return (ret1 & ret2) ? ret1 : 0; } static void __exit sgiseeq_exit(void) diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index 34463ce..bc8de48 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -632,7 +632,6 @@ static int sis190_rx_interrupt(struct ne pci_action(tp->pci_dev, le32_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb->dev = dev; skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index b2a3b19..2cb2e15 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -1160,7 +1160,6 @@ sis900_init_rx_ring(struct net_device *n buffer */ break; } - skb->dev = net_dev; sis_priv->rx_skbuff[i] = skb; sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev, @@ -1754,6 +1753,7 @@ #endif sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; } else { struct sk_buff * skb; + struct sk_buff * rx_skb; pci_unmap_single(sis_priv->pci_dev, sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, @@ -1787,10 +1787,10 @@ #endif } /* give the socket buffer to upper layers */ - skb = sis_priv->rx_skbuff[entry]; - skb_put(skb, rx_size); - skb->protocol = eth_type_trans(skb, net_dev); - netif_rx(skb); + rx_skb = sis_priv->rx_skbuff[entry]; + skb_put(rx_skb, rx_size); + rx_skb->protocol = eth_type_trans(rx_skb, net_dev); + netif_rx(rx_skb); /* some network statistics */ if ((rx_status & BCAST) == MCAST) @@ -1800,7 +1800,6 @@ #endif sis_priv->stats.rx_packets++; sis_priv->dirty_rx++; refill_rx_ring: - skb->dev = net_dev; sis_priv->rx_skbuff[entry] = skb; sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[entry].bufptr = @@ -1832,7 +1831,6 @@ refill_rx_ring: sis_priv->stats.rx_dropped++; break; } - skb->dev = net_dev; sis_priv->rx_skbuff[entry] = skb; sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[entry].bufptr = diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c index e94ab25..bf21862 100644 --- a/drivers/net/sk98lin/skge.c +++ b/drivers/net/sk98lin/skge.c @@ -1562,10 +1562,10 @@ #endif pTxd->pMBuf = pMessage; if (pMessage->ip_summed == CHECKSUM_PARTIAL) { - u16 hdrlen = pMessage->h.raw - pMessage->data; + u16 hdrlen = skb_transport_offset(pMessage); u16 offset = hdrlen + pMessage->csum_offset; - if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) && + if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) && (pAC->GIni.GIChipRev == 0) && (pAC->GIni.GIChipId == CHIP_ID_YUKON)) { pTxd->TBControl = BMU_TCP_CHECK; @@ -1681,7 +1681,7 @@ #endif ** Does the HW need to evaluate checksum for TCP or UDP packets? */ if (pMessage->ip_summed == CHECKSUM_PARTIAL) { - u16 hdrlen = pMessage->h.raw - pMessage->data; + u16 hdrlen = skb_transport_offset(pMessage); u16 offset = hdrlen + pMessage->csum_offset; Control = BMU_STFWD; @@ -1691,7 +1691,7 @@ #endif ** opcode for udp is not working in the hardware yet ** (Revision 2.0) */ - if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) && + if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) && (pAC->GIni.GIChipRev == 0) && (pAC->GIni.GIChipId == CHIP_ID_YUKON)) { Control |= BMU_TCP_CHECK; @@ -2127,7 +2127,7 @@ #endif (dma_addr_t) PhysAddr, FrameLength, PCI_DMA_FROMDEVICE); - memcpy(pNewMsg->data, pMsg, FrameLength); + skb_copy_to_linear_data(pNewMsg, pMsg, FrameLength); pci_dma_sync_single_for_device(pAC->PciDev, (dma_addr_t) PhysAddr, @@ -2193,7 +2193,6 @@ #endif SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC, FrameLength, pRxPort->PortIndex); - pMsg->dev = pAC->dev[pRxPort->PortIndex]; pMsg->protocol = eth_type_trans(pMsg, pAC->dev[pRxPort->PortIndex]); netif_rx(pMsg); @@ -2246,7 +2245,6 @@ #endif (IFF_PROMISC | IFF_ALLMULTI)) != 0 || (ForRlmt & SK_RLMT_RX_PROTOCOL) == SK_RLMT_RX_PROTOCOL) { - pMsg->dev = pAC->dev[pRxPort->PortIndex]; pMsg->protocol = eth_type_trans(pMsg, pAC->dev[pRxPort->PortIndex]); netif_rx(pMsg); @@ -5125,7 +5123,12 @@ static int skge_resume(struct pci_dev *p pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_enable_device(pdev); + ret = pci_enable_device(pdev); + if (ret) { + printk(KERN_WARNING "sk98lin: unable to enable device %s " + "in resume\n", dev->name); + goto err_out; + } pci_set_master(pdev); if (pAC->GIni.GIMacsFound == 2) ret = request_irq(dev->irq, SkGeIsr, IRQF_SHARED, "sk98lin", dev); @@ -5133,10 +5136,8 @@ static int skge_resume(struct pci_dev *p ret = request_irq(dev->irq, SkGeIsrOnePort, IRQF_SHARED, "sk98lin", dev); if (ret) { printk(KERN_WARNING "sk98lin: unable to acquire IRQ %d\n", dev->irq); - pAC->AllocFlag &= ~SK_ALLOC_IRQ; - dev->irq = 0; - pci_disable_device(pdev); - return -EBUSY; + ret = -EBUSY; + goto err_out_disable_pdev; } netif_device_attach(dev); @@ -5153,6 +5154,13 @@ static int skge_resume(struct pci_dev *p } return 0; + +err_out_disable_pdev: + pci_disable_device(pdev); +err_out: + pAC->AllocFlag &= ~SK_ALLOC_IRQ; + dev->irq = 0; + return ret; } #else #define skge_suspend NULL diff --git a/drivers/net/skfp/h/lnkstat.h b/drivers/net/skfp/h/lnkstat.h deleted file mode 100644 index c73dcd9..0000000 --- a/drivers/net/skfp/h/lnkstat.h +++ /dev/null @@ -1,84 +0,0 @@ -/****************************************************************************** - * - * (C)Copyright 1998,1999 SysKonnect, - * a business unit of Schneider & Koch & Co. Datensysteme GmbH. - * - * 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. - * - * The information in this file is provided "AS IS" without warranty. - * - ******************************************************************************/ - -/* - * Definition of the Error Log Structure - * This structure will be copied into the Error Log buffer - * during the NDIS General Request ReadErrorLog by the MAC Driver - */ - -struct s_error_log { - - /* - * place holder for token ring adapter error log (zeros) - */ - u_char reserved_0 ; /* byte 0 inside Error Log */ - u_char reserved_1 ; /* byte 1 */ - u_char reserved_2 ; /* byte 2 */ - u_char reserved_3 ; /* byte 3 */ - u_char reserved_4 ; /* byte 4 */ - u_char reserved_5 ; /* byte 5 */ - u_char reserved_6 ; /* byte 6 */ - u_char reserved_7 ; /* byte 7 */ - u_char reserved_8 ; /* byte 8 */ - u_char reserved_9 ; /* byte 9 */ - u_char reserved_10 ; /* byte 10 */ - u_char reserved_11 ; /* byte 11 */ - u_char reserved_12 ; /* byte 12 */ - u_char reserved_13 ; /* byte 13 */ - - /* - * FDDI link statistics - */ -/* - * smt error low - */ -#define SMT_ERL_AEB (1<<15) /* A elast. buffer */ -#define SMT_ERL_BLC (1<<14) /* B link error condition */ -#define SMT_ERL_ALC (1<<13) /* A link error condition */ -#define SMT_ERL_NCC (1<<12) /* not copied condition */ -#define SMT_ERL_FEC (1<<11) /* frame error condition */ - -/* - * smt event low - */ -#define SMT_EVL_NCE (1<<5) - - u_short smt_error_low ; /* byte 14/15 */ - u_short smt_error_high ; /* byte 16/17 */ - u_short smt_event_low ; /* byte 18/19 */ - u_short smt_event_high ; /* byte 20/21 */ - u_short connection_policy_violation ; /* byte 22/23 */ - u_short port_event ; /* byte 24/25 */ - u_short set_count_low ; /* byte 26/27 */ - u_short set_count_high ; /* byte 28/29 */ - u_short aci_id_code ; /* byte 30/31 */ - u_short purge_frame_counter ; /* byte 32/33 */ - - /* - * CMT and RMT state machines - */ - u_short ecm_state ; /* byte 34/35 */ - u_short pcm_a_state ; /* byte 36/37 */ - u_short pcm_b_state ; /* byte 38/39 */ - u_short cfm_state ; /* byte 40/41 */ - u_short rmt_state ; /* byte 42/43 */ - - u_short not_used[30] ; /* byte 44-103 */ - - u_short ucode_version_level ; /* byte 104/105 */ - - u_short not_used_1 ; /* byte 106/107 */ - u_short not_used_2 ; /* byte 108/109 */ -} ; diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index 9733a11..a7ef6c8 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -1680,7 +1680,6 @@ void mac_drv_rx_complete(struct s_smc *s rxd->rxd_os.skb = NULL; skb_trim(skb, len); skb->protocol = fddi_type_trans(skb, bp->dev); - skb->dev = bp->dev; /* pass up device pointer */ netif_rx(skb); bp->dev->last_rx = jiffies; @@ -1938,7 +1937,7 @@ int mac_drv_rx_init(struct s_smc *smc, i } skb_reserve(skb, 3); skb_put(skb, len); - memcpy(skb->data, look_ahead, len); + skb_copy_to_linear_data(skb, look_ahead, len); // deliver frame to system skb->protocol = fddi_type_trans(skb, smc->os.dev); diff --git a/drivers/net/skge.c b/drivers/net/skge.c index d476a3c..21afe10 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -42,7 +42,7 @@ #include #include "skge.h" #define DRV_NAME "skge" -#define DRV_VERSION "1.10" +#define DRV_VERSION "1.11" #define PFX DRV_NAME " " #define DEFAULT_TX_RING_SIZE 128 @@ -2621,6 +2621,7 @@ static int skge_down(struct net_device * static inline int skge_avail(const struct skge_ring *ring) { + smp_mb(); return ((ring->to_clean > ring->to_use) ? 0 : ring->count) + (ring->to_clean - ring->to_use) - 1; } @@ -2654,12 +2655,12 @@ static int skge_xmit_frame(struct sk_buf td->dma_hi = map >> 32; if (skb->ip_summed == CHECKSUM_PARTIAL) { - int offset = skb->h.raw - skb->data; + const int offset = skb_transport_offset(skb); /* This seems backwards, but it is what the sk98lin * does. Looks like hardware is wrong? */ - if (skb->h.ipiph->protocol == IPPROTO_UDP + if (ipip_hdr(skb)->protocol == IPPROTO_UDP && hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON) control = BMU_TCP_CHECK; else @@ -2709,6 +2710,8 @@ static int skge_xmit_frame(struct sk_buf dev->name, e - skge->tx_ring.start, skb->len); skge->tx_ring.to_use = e->next; + smp_wmb(); + if (skge_avail(&skge->tx_ring) <= TX_LOW_WATER) { pr_debug("%s: transmit queue full\n", dev->name); netif_stop_queue(dev); @@ -2726,8 +2729,6 @@ static void skge_tx_free(struct skge_por { struct pci_dev *pdev = skge->hw->pdev; - BUG_ON(!e->skb); - /* skb header vs. fragment */ if (control & BMU_STF) pci_unmap_single(pdev, pci_unmap_addr(e, mapaddr), @@ -2745,7 +2746,6 @@ static void skge_tx_free(struct skge_por dev_kfree_skb(e->skb); } - e->skb = NULL; } /* Free all buffers in transmit ring */ @@ -2950,7 +2950,7 @@ static struct sk_buff *skge_rx_get(struc pci_dma_sync_single_for_cpu(skge->hw->pdev, pci_unmap_addr(e, mapaddr), len, PCI_DMA_FROMDEVICE); - memcpy(skb->data, e->skb->data, len); + skb_copy_from_linear_data(e->skb, skb->data, len); pci_dma_sync_single_for_device(skge->hw->pdev, pci_unmap_addr(e, mapaddr), len, PCI_DMA_FROMDEVICE); @@ -3017,21 +3017,29 @@ static void skge_tx_done(struct net_devi skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F); - netif_tx_lock(dev); for (e = ring->to_clean; e != ring->to_use; e = e->next) { - struct skge_tx_desc *td = e->desc; + u32 control = ((const struct skge_tx_desc *) e->desc)->control; - if (td->control & BMU_OWN) + if (control & BMU_OWN) break; - skge_tx_free(skge, e, td->control); + skge_tx_free(skge, e, control); } skge->tx_ring.to_clean = e; - if (skge_avail(&skge->tx_ring) > TX_LOW_WATER) - netif_wake_queue(dev); + /* Can run lockless until we need to synchronize to restart queue. */ + smp_mb(); + + if (unlikely(netif_queue_stopped(dev) && + skge_avail(&skge->tx_ring) > TX_LOW_WATER)) { + netif_tx_lock(dev); + if (unlikely(netif_queue_stopped(dev) && + skge_avail(&skge->tx_ring) > TX_LOW_WATER)) { + netif_wake_queue(dev); - netif_tx_unlock(dev); + } + netif_tx_unlock(dev); + } } static int skge_poll(struct net_device *dev, int *budget) diff --git a/drivers/net/skge.h b/drivers/net/skge.h index 86467ae..edd7146 100644 --- a/drivers/net/skge.h +++ b/drivers/net/skge.h @@ -232,7 +232,6 @@ enum { IS_R2_PAR_ERR = 1<<0, /* Queue R2 Parity Error */ IS_ERR_MSK = IS_IRQ_MST_ERR | IS_IRQ_STAT - | IS_NO_STAT_M1 | IS_NO_STAT_M2 | IS_RAM_RD_PAR | IS_RAM_WR_PAR | IS_M1_PAR_ERR | IS_M2_PAR_ERR | IS_R1_PAR_ERR | IS_R2_PAR_ERR, @@ -2447,15 +2446,15 @@ enum pause_status { struct skge_port { - u32 msg_enable; struct skge_hw *hw; struct net_device *netdev; int port; + u32 msg_enable; struct skge_ring tx_ring; - struct skge_ring rx_ring; - struct net_device_stats net_stats; + struct skge_ring rx_ring ____cacheline_aligned_in_smp; + unsigned int rx_buf_size; struct timer_list link_timer; enum pause_control flow_control; @@ -2471,7 +2470,8 @@ struct skge_port { void *mem; /* PCI memory for rings */ dma_addr_t dma; unsigned long mem_size; - unsigned int rx_buf_size; + + struct net_device_stats net_stats; }; diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index ac36152..238c2ca 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -32,6 +32,7 @@ #include #include #include #include +#include #include #include #include @@ -1391,8 +1392,8 @@ static int sky2_xmit_frame(struct sk_buf /* Check for TCP Segmentation Offload */ mss = skb_shinfo(skb)->gso_size; if (mss != 0) { - mss += ((skb->h.th->doff - 5) * 4); /* TCP options */ - mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); + mss += tcp_optlen(skb); /* TCP options */ + mss += ip_hdrlen(skb) + sizeof(struct tcphdr); mss += ETH_HLEN; if (mss != sky2->tx_last_mss) { @@ -1420,14 +1421,14 @@ #endif /* Handle TCP checksum offload */ if (skb->ip_summed == CHECKSUM_PARTIAL) { - unsigned offset = skb->h.raw - skb->data; + const unsigned offset = skb_transport_offset(skb); u32 tcpsum; tcpsum = offset << 16; /* sum start */ tcpsum |= offset + skb->csum_offset; /* sum write */ ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM; - if (skb->nh.iph->protocol == IPPROTO_UDP) + if (ip_hdr(skb)->protocol == IPPROTO_UDP) ctrl |= UDPTCP; if (tcpsum != sky2->tx_tcpsum) { @@ -1970,7 +1971,7 @@ static struct sk_buff *receive_copy(stru skb_reserve(skb, 2); pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr, length, PCI_DMA_FROMDEVICE); - memcpy(skb->data, re->skb->data, length); + skb_copy_from_linear_data(re->skb, skb->data, length); skb->ip_summed = re->skb->ip_summed; skb->csum = re->skb->csum; pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr, diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 2f4b1de..65bd20f 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -363,7 +363,7 @@ #endif /* SL_INCLUDE_CSLIP */ } skb->dev = sl->dev; memcpy(skb_put(skb,count), sl->rbuff, count); - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb->protocol=htons(ETH_P_IP); netif_rx(skb); sl->dev->last_rx = jiffies; diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index c956141..81f2484 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -499,10 +499,9 @@ #else SMC_SET_RX_CFG(RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_)); SMC_PULL_DATA(data, pkt_len+2+3); - DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name,); + DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name); PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; @@ -1307,7 +1306,6 @@ smc911x_rx_dma_irq(int dma, void *data) lp->current_rx_skb = NULL; PRINT_PKT(skb->data, skb->len); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c index bd6e845..36c1eba 100644 --- a/drivers/net/smc9194.c +++ b/drivers/net/smc9194.c @@ -1262,7 +1262,6 @@ static void smc_rcv(struct net_device *d skb_reserve( skb, 2 ); /* 16 bit alignment */ - skb->dev = dev; data = skb_put( skb, packet_length); #ifdef USE_32_BIT diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 49f4b77..01cc3c7 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -568,7 +568,6 @@ static inline void smc_rcv(struct net_d PRINT_PKT(data, packet_len - 4); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c index ed7aa0a..c6320c7 100644 --- a/drivers/net/sonic.c +++ b/drivers/net/sonic.c @@ -85,7 +85,6 @@ static int sonic_open(struct net_device dev->name); return -ENOMEM; } - skb->dev = dev; /* align IP header unless DMA requires otherwise */ if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2) skb_reserve(skb, 2); @@ -451,7 +450,6 @@ static void sonic_rx(struct net_device * lp->stats.rx_dropped++; break; } - new_skb->dev = dev; /* provide 16 byte IP header alignment unless DMA requires otherwise */ if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2) skb_reserve(new_skb, 2); diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index e3019d5..230da14 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -720,7 +720,7 @@ spider_net_prepare_tx_descr(struct spide spin_unlock_irqrestore(&chain->lock, flags); if (skb->protocol == htons(ETH_P_IP) && skb->ip_summed == CHECKSUM_PARTIAL) - switch (skb->nh.iph->protocol) { + switch (ip_hdr(skb)->protocol) { case IPPROTO_TCP: hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP; break; @@ -990,7 +990,6 @@ spider_net_pass_skb_up(struct spider_net netdev = card->netdev; skb = descr->skb; - skb->dev = netdev; skb_put(skb, hwdescr->valid_size); /* the card seems to add 2 bytes of junk in front diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 8bba2e3..9d6e454 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -1452,7 +1452,6 @@ static int __netdev_rx(struct net_device to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, np->rx_info[entry].mapping, diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c index 4757aa6..396c3d9 100644 --- a/drivers/net/sun3_82586.c +++ b/drivers/net/sun3_82586.c @@ -775,7 +775,6 @@ static void sun3_82586_rcv_int(struct ne skb = (struct sk_buff *) dev_alloc_skb(totlen+2); if(skb != NULL) { - skb->dev = dev; skb_reserve(skb,2); skb_put(skb,totlen); eth_copy_and_sum(skb,(char *) p->base+swab32((unsigned long) rbd->buffer),totlen,0); @@ -1027,7 +1026,7 @@ #endif memset((char *)p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN); len = ETH_ZLEN; } - memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len); + skb_copy_from_linear_data(skb, p->xmit_cbuffs[p->xmit_count], skb->len); #if (NUM_XMIT_BUFFS == 1) # ifdef NO_NOPCOMMANDS diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c index 7bee45b..791e081 100644 --- a/drivers/net/sun3lance.c +++ b/drivers/net/sun3lance.c @@ -629,7 +629,7 @@ #endif head->length = (-len) | 0xf000; head->misc = 0; - memcpy( PKTBUF_ADDR(head), (void *)skb->data, skb->len ); + skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len); if (len != skb->len) memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len); @@ -851,10 +851,9 @@ #endif } - skb->dev = dev; skb_reserve( skb, 2 ); /* 16 byte align */ skb_put( skb, pkt_len ); /* Make room */ -// memcpy( skb->data, PKTBUF_ADDR(head), pkt_len ); +// skb_copy_to_linear_data(skb, PKTBUF_ADDR(head), pkt_len); eth_copy_and_sum(skb, PKTBUF_ADDR(head), pkt_len, 0); diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index 18f8885..2ad8d58 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -855,7 +855,6 @@ static void bigmac_rx(struct bigmac *bp) drops++; goto drop_it; } - copy_skb->dev = bp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); sbus_dma_sync_single_for_cpu(bp->bigmac_sdev, diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index c06ecc8..f51ba31 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -1308,7 +1308,6 @@ #endif to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, desc->frag[0].addr, diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 08ea61d..5da7321 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -64,11 +64,9 @@ #include #include #include -#ifdef __sparc__ +#ifdef CONFIG_SPARC #include -#include -#include -#include +#include #endif #ifdef CONFIG_PPC_PMAC @@ -845,11 +843,10 @@ static int gem_rx(struct gem *gp, int wo goto drop_it; } - copy_skb->dev = gp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); pci_dma_sync_single_for_cpu(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); pci_dma_sync_single_for_device(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ @@ -1029,10 +1026,8 @@ static int gem_start_xmit(struct sk_buff ctrl = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { - u64 csum_start_off, csum_stuff_off; - - csum_start_off = (u64) (skb->h.raw - skb->data); - csum_stuff_off = csum_start_off + skb->csum_offset; + const u64 csum_start_off = skb_transport_offset(skb); + const u64 csum_stuff_off = csum_start_off + skb->csum_offset; ctrl = (TXDCTRL_CENAB | (csum_start_off << 15) | @@ -2849,7 +2844,7 @@ static int gem_ioctl(struct net_device * return rc; } -#if (!defined(__sparc__) && !defined(CONFIG_PPC_PMAC)) +#if (!defined(CONFIG_SPARC) && !defined(CONFIG_PPC_PMAC)) /* Fetch MAC address from vital product data of PCI ROM. */ static int find_eth_addr_in_vpd(void __iomem *rom_base, int len, unsigned char *dev_addr) { @@ -2904,36 +2899,19 @@ #endif /* not Sparc and not PPC */ static int __devinit gem_get_device_address(struct gem *gp) { -#if defined(__sparc__) || defined(CONFIG_PPC_PMAC) +#if defined(CONFIG_SPARC) || defined(CONFIG_PPC_PMAC) struct net_device *dev = gp->dev; -#endif - -#if defined(__sparc__) - struct pci_dev *pdev = gp->pdev; - struct pcidev_cookie *pcp = pdev->sysdata; - int use_idprom = 1; - - if (pcp != NULL) { - unsigned char *addr; - int len; - - addr = of_get_property(pcp->prom_node, "local-mac-address", - &len); - if (addr && len == 6) { - use_idprom = 0; - memcpy(dev->dev_addr, addr, 6); - } - } - if (use_idprom) - memcpy(dev->dev_addr, idprom->id_ethaddr, 6); -#elif defined(CONFIG_PPC_PMAC) const unsigned char *addr; addr = get_property(gp->of_node, "local-mac-address", NULL); if (addr == NULL) { +#ifdef CONFIG_SPARC + addr = idprom->id_ethaddr; +#else printk("\n"); printk(KERN_ERR "%s: can't get mac-address\n", dev->name); return -1; +#endif } memcpy(dev->dev_addr, addr, 6); #else @@ -3091,7 +3069,7 @@ static int __devinit gem_init_one(struct /* On Apple, we want a reference to the Open Firmware device-tree * node. We use it for clock control. */ -#ifdef CONFIG_PPC_PMAC +#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC) gp->of_node = pci_device_to_OF_node(pdev); #endif diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h index a70067c..58cf87c 100644 --- a/drivers/net/sungem.h +++ b/drivers/net/sungem.h @@ -1025,7 +1025,7 @@ struct gem { struct pci_dev *pdev; struct net_device *dev; -#ifdef CONFIG_PPC_PMAC +#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC) struct device_node *of_node; #endif }; diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 192bbc9..51c3fe2 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -55,9 +55,6 @@ #include #ifdef CONFIG_PCI #include -#ifdef CONFIG_SPARC -#include -#endif #endif #include "sunhme.h" @@ -2058,11 +2055,10 @@ static void happy_meal_rx(struct happy_m goto drop_it; } - copy_skb->dev = dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE); /* Reuse original ring buffer. */ @@ -2270,10 +2266,8 @@ static int happy_meal_start_xmit(struct tx_flags = TXFLAG_OWN; if (skb->ip_summed == CHECKSUM_PARTIAL) { - u32 csum_start_off, csum_stuff_off; - - csum_start_off = (u32) (skb->h.raw - skb->data); - csum_stuff_off = csum_start_off + skb->csum_offset; + const u32 csum_start_off = skb_transport_offset(skb); + const u32 csum_stuff_off = csum_start_off + skb->csum_offset; tx_flags = (TXFLAG_OWN | TXFLAG_CSENABLE | ((csum_start_off << 14) & TXFLAG_CSBUFBEGIN) | @@ -2704,7 +2698,7 @@ static int __devinit happy_meal_sbus_pro dev->dev_addr[i] = macaddr[i]; macaddr[5]++; } else { - unsigned char *addr; + const unsigned char *addr; int len; addr = of_get_property(dp, "local-mac-address", &len); @@ -2986,7 +2980,7 @@ static int __devinit happy_meal_pci_prob { struct quattro *qp = NULL; #ifdef CONFIG_SPARC - struct pcidev_cookie *pcp; + struct device_node *dp; #endif struct happy_meal *hp; struct net_device *dev; @@ -2998,13 +2992,8 @@ #endif /* Now make sure pci_dev cookie is there. */ #ifdef CONFIG_SPARC - pcp = pdev->sysdata; - if (pcp == NULL) { - printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n"); - return -ENODEV; - } - - strcpy(prom_name, pcp->prom_node->name); + dp = pci_device_to_OF_node(pdev); + strcpy(prom_name, dp->name); #else if (is_quattro_p(pdev)) strcpy(prom_name, "SUNW,qfe"); @@ -3081,11 +3070,11 @@ #endif macaddr[5]++; } else { #ifdef CONFIG_SPARC - unsigned char *addr; + const unsigned char *addr; int len; if (qfe_slot != -1 && - (addr = of_get_property(pcp->prom_node, + (addr = of_get_property(dp, "local-mac-address", &len)) != NULL && len == 6) { memcpy(dev->dev_addr, addr, 6); @@ -3105,7 +3094,7 @@ #endif hp->tcvregs = (hpreg_base + 0x7000UL); #ifdef CONFIG_SPARC - hp->hm_revision = of_getintprop_default(pcp->prom_node, "hm-rev", 0xff); + hp->hm_revision = of_getintprop_default(dp, "hm-rev", 0xff); if (hp->hm_revision == 0xff) { unsigned char prev; @@ -3300,7 +3289,7 @@ static int __devinit hme_sbus_probe(stru { struct sbus_dev *sdev = to_sbus_device(&dev->dev); struct device_node *dp = dev->node; - char *model = of_get_property(dp, "model", NULL); + const char *model = of_get_property(dp, "model", NULL); int is_qfe = (match->data != NULL); if (!is_qfe && model && !strcmp(model, "SUNW,sbus-qfe")) diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index b0929a4..4272253 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -547,7 +547,6 @@ static void lance_rx_dvma(struct net_dev lp->stats.rx_bytes += len; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ eth_copy_and_sum(skb, @@ -721,7 +720,6 @@ static void lance_rx_pio(struct net_devi lp->stats.rx_bytes += len; - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len); @@ -1145,7 +1143,7 @@ static int lance_start_xmit(struct sk_bu struct lance_init_block *ib = lp->init_block_mem; ib->btx_ring [entry].length = (-len) | 0xf000; ib->btx_ring [entry].misc = 0; - memcpy((char *)&ib->tx_buf [entry][0], skb->data, skblen); + skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen); if (len != skblen) memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen); ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN); diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index f3bad56..fa70e0b 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -437,7 +437,6 @@ static void qe_rx(struct sunqe *qep) drops++; qep->net_stats.rx_dropped++; } else { - skb->dev = qep->dev; skb_reserve(skb, 2); skb_put(skb, len); eth_copy_and_sum(skb, (unsigned char *) this_qbuf, @@ -593,7 +592,7 @@ static int qe_start_xmit(struct sk_buff /* Avoid a race... */ qep->qe_block->qe_txd[entry].tx_flags = TXD_UPDATE; - memcpy(txbuf, skb->data, len); + skb_copy_from_linear_data(skb, txbuf, len); qep->qe_block->qe_txd[entry].tx_addr = txbuf_dvma; qep->qe_block->qe_txd[entry].tx_flags = diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index e3a7e3c..f1e2dfc 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -1,35 +1,34 @@ -/* tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux. - * - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ahennessy@mvista.com +/* + * tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux. * * Based on skelton.c by Donald Becker. - * Copyright (C) 2000-2001 Toshiba Corporation * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This driver is a replacement of older and less maintained version. + * This is a header of the older version: + * ---------- + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ahennessy@mvista.com + * Copyright (C) 2000-2001 Toshiba Corporation + * static const char *version = + * "tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n"; + * ---------- * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. + * (C) Copyright TOSHIBA CORPORATION 2004-2005 + * All Rights Reserved. */ -static const char *version = - "tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n"; +#ifdef TC35815_NAPI +#define DRV_VERSION "1.35-NAPI" +#else +#define DRV_VERSION "1.35" +#endif +static const char *version = "tc35815.c:v" DRV_VERSION "\n"; +#define MODNAME "tc35815" #include #include @@ -40,6 +39,7 @@ #include #include #include #include +#include #include #include #include @@ -47,36 +47,47 @@ #include #include #include #include -#include -#include -#include - -#include +#include +#include #include -#include #include -/* - * The name of the card. Is used for messages and in the requests for - * io regions, irqs and dma channels - */ -static const char* cardname = "TC35815CF"; -#define TC35815_PROC_ENTRY "net/tc35815" - -#define TC35815_MODULE_NAME "TC35815CF" -#define TX_TIMEOUT (4*HZ) - /* First, a few definitions that the brave might change. */ -/* use 0 for production, 1 for verification, >2 for debug */ -#ifndef TC35815_DEBUG -#define TC35815_DEBUG 1 -#endif -static unsigned int tc35815_debug = TC35815_DEBUG; - #define GATHER_TXINT /* On-Demand Tx Interrupt */ +#define WORKAROUND_LOSTCAR +#define WORKAROUND_100HALF_PROMISC +/* #define TC35815_USE_PACKEDBUFFER */ + +typedef enum { + TC35815CF = 0, + TC35815_NWU, + TC35815_TX4939, +} board_t; + +/* indexed by board_t, above */ +static const struct { + const char *name; +} board_info[] __devinitdata = { + { "TOSHIBA TC35815CF 10/100BaseTX" }, + { "TOSHIBA TC35815 with Wake on LAN" }, + { "TOSHIBA TC35815/TX4939" }, +}; + +static const struct pci_device_id tc35815_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF), .driver_data = TC35815CF }, + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_NWU), .driver_data = TC35815_NWU }, + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939), .driver_data = TC35815_TX4939 }, + {0,} +}; +MODULE_DEVICE_TABLE (pci, tc35815_pci_tbl); -#define vtonocache(p) KSEG1ADDR(virt_to_phys(p)) +/* see MODULE_PARM_DESC */ +static struct tc35815_options { + int speed; + int duplex; + int doforce; +} options; /* * Registers @@ -119,6 +130,11 @@ struct tc35815_regs { * Bit assignments */ /* DMA_Ctl bit asign ------------------------------------------------------- */ +#define DMA_RxAlign 0x00c00000 /* 1:Reception Alignment */ +#define DMA_RxAlign_1 0x00400000 +#define DMA_RxAlign_2 0x00800000 +#define DMA_RxAlign_3 0x00c00000 +#define DMA_M66EnStat 0x00080000 /* 1:66MHz Enable State */ #define DMA_IntMask 0x00040000 /* 1:Interupt mask */ #define DMA_SWIntReq 0x00020000 /* 1:Software Interrupt request */ #define DMA_TxWakeUp 0x00010000 /* 1:Transmit Wake Up */ @@ -269,42 +285,6 @@ #define MD_CA_Busy 0x0000080 #define MD_CA_Wr 0x00000400 /* 1:Write 0:Read */ -/* MII register offsets */ -#define MII_CONTROL 0x0000 -#define MII_STATUS 0x0001 -#define MII_PHY_ID0 0x0002 -#define MII_PHY_ID1 0x0003 -#define MII_ANAR 0x0004 -#define MII_ANLPAR 0x0005 -#define MII_ANER 0x0006 -/* MII Control register bit definitions. */ -#define MIICNTL_FDX 0x0100 -#define MIICNTL_RST_AUTO 0x0200 -#define MIICNTL_ISOLATE 0x0400 -#define MIICNTL_PWRDWN 0x0800 -#define MIICNTL_AUTO 0x1000 -#define MIICNTL_SPEED 0x2000 -#define MIICNTL_LPBK 0x4000 -#define MIICNTL_RESET 0x8000 -/* MII Status register bit significance. */ -#define MIISTAT_EXT 0x0001 -#define MIISTAT_JAB 0x0002 -#define MIISTAT_LINK 0x0004 -#define MIISTAT_CAN_AUTO 0x0008 -#define MIISTAT_FAULT 0x0010 -#define MIISTAT_AUTO_DONE 0x0020 -#define MIISTAT_CAN_T 0x0800 -#define MIISTAT_CAN_T_FDX 0x1000 -#define MIISTAT_CAN_TX 0x2000 -#define MIISTAT_CAN_TX_FDX 0x4000 -#define MIISTAT_CAN_T4 0x8000 -/* MII Auto-Negotiation Expansion/RemoteEnd Register Bits */ -#define MII_AN_TX_FDX 0x0100 -#define MII_AN_TX_HDX 0x0080 -#define MII_AN_10_FDX 0x0040 -#define MII_AN_10_HDX 0x0020 - - /* * Descriptors */ @@ -352,32 +332,51 @@ #undef NO_CHECK_CARRIER /* Does not chec #ifdef NO_CHECK_CARRIER #define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \ - Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \ - Tx_En) /* maybe 0x7d01 */ + Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \ + Tx_En) /* maybe 0x7b01 */ #else #define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \ - Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \ - Tx_En) /* maybe 0x7f01 */ + Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \ + Tx_En) /* maybe 0x7b01 */ #endif #define RX_CTL_CMD (Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \ | Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn) /* maybe 0x6f01 */ - #define INT_EN_CMD (Int_NRAbtEn | \ - Int_DParDEn | Int_DParErrEn | \ + Int_DmParErrEn | Int_DParDEn | Int_DParErrEn | \ Int_SSysErrEn | Int_RMasAbtEn | Int_RTargAbtEn | \ Int_STargAbtEn | \ Int_BLExEn | Int_FDAExEn) /* maybe 0xb7f*/ +#define DMA_CTL_CMD DMA_BURST_SIZE +#define HAVE_DMA_RXALIGN(lp) likely((lp)->boardtype != TC35815CF) /* Tuning parameters */ #define DMA_BURST_SIZE 32 #define TX_THRESHOLD 1024 +#define TX_THRESHOLD_MAX 1536 /* used threshold with packet max byte for low pci transfer ability.*/ +#define TX_THRESHOLD_KEEP_LIMIT 10 /* setting threshold max value when overrun error occured this count. */ +/* 16 + RX_BUF_NUM * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*FD_PAGE_NUM */ +#ifdef TC35815_USE_PACKEDBUFFER #define FD_PAGE_NUM 2 -#define FD_PAGE_ORDER 1 -/* 16 + RX_BUF_PAGES * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*2 */ -#define RX_BUF_PAGES 8 /* >= 2 */ +#define RX_BUF_NUM 8 /* >= 2 */ #define RX_FD_NUM 250 /* >= 32 */ #define TX_FD_NUM 128 +#define RX_BUF_SIZE PAGE_SIZE +#else /* TC35815_USE_PACKEDBUFFER */ +#define FD_PAGE_NUM 4 +#define RX_BUF_NUM 128 /* < 256 */ +#define RX_FD_NUM 256 /* >= 32 */ +#define TX_FD_NUM 128 +#if RX_CTL_CMD & Rx_LongEn +#define RX_BUF_SIZE PAGE_SIZE +#elif RX_CTL_CMD & Rx_StripCRC +#define RX_BUF_SIZE ALIGN(ETH_FRAME_LEN + 4 + 2, 32) /* +2: reserve */ +#else +#define RX_BUF_SIZE ALIGN(ETH_FRAME_LEN + 2, 32) /* +2: reserve */ +#endif +#endif /* TC35815_USE_PACKEDBUFFER */ +#define RX_FD_RESERVE (2 / 2) /* max 2 BD per RxFD */ +#define NAPI_WEIGHT 16 struct TxFD { struct FDesc fd; @@ -392,18 +391,27 @@ struct RxFD { struct FrFD { struct FDesc fd; - struct BDesc bd[RX_BUF_PAGES]; + struct BDesc bd[RX_BUF_NUM]; }; -extern unsigned long tc_readl(volatile __u32 *addr); -extern void tc_writel(unsigned long data, volatile __u32 *addr); +#define tc_readl(addr) readl(addr) +#define tc_writel(d, addr) writel(d, addr) + +#define TC35815_TX_TIMEOUT msecs_to_jiffies(400) -dma_addr_t priv_dma_handle; +/* Timer state engine. */ +enum tc35815_timer_state { + arbwait = 0, /* Waiting for auto negotiation to complete. */ + lupwait = 1, /* Auto-neg complete, awaiting link-up status. */ + ltrywait = 2, /* Forcing try of all modes, from fastest to slowest. */ + asleep = 3, /* Time inactive. */ + lcheck = 4, /* Check link status. */ +}; /* Information that need to be kept for each board. */ struct tc35815_local { - struct net_device *next_module; + struct pci_dev *pci_dev; /* statistics */ struct net_device_stats stats; @@ -411,216 +419,372 @@ struct tc35815_local { int max_tx_qlen; int tx_ints; int rx_ints; + int tx_underrun; } lstats; - int tbusy; - int option; -#define TC35815_OPT_AUTO 0x00 -#define TC35815_OPT_10M 0x01 -#define TC35815_OPT_100M 0x02 -#define TC35815_OPT_FULLDUP 0x04 - int linkspeed; /* 10 or 100 */ + /* Tx control lock. This protects the transmit buffer ring + * state along with the "tx full" state of the driver. This + * means all netif_queue flow control actions are protected + * by this lock as well. + */ + spinlock_t lock; + + int phy_addr; int fullduplex; + unsigned short saved_lpa; + struct timer_list timer; + enum tc35815_timer_state timer_state; /* State of auto-neg timer. */ + unsigned int timer_ticks; /* Number of clicks at each state */ /* * Transmitting: Batch Mode. * 1 BD in 1 TxFD. - * Receiving: Packing Mode. + * Receiving: Packing Mode. (TC35815_USE_PACKEDBUFFER) * 1 circular FD for Free Buffer List. - * RX_BUG_PAGES BD in Free Buffer FD. + * RX_BUF_NUM BD in Free Buffer FD. * One Free Buffer BD has PAGE_SIZE data buffer. + * Or Non-Packing Mode. + * 1 circular FD for Free Buffer List. + * RX_BUF_NUM BD in Free Buffer FD. + * One Free Buffer BD has ETH_FRAME_LEN data buffer. */ - struct pci_dev *pdev; - dma_addr_t fd_buf_dma_handle; - void * fd_buf; /* for TxFD, TxFD, FrFD */ + void * fd_buf; /* for TxFD, RxFD, FrFD */ + dma_addr_t fd_buf_dma; struct TxFD *tfd_base; - int tfd_start; - int tfd_end; + unsigned int tfd_start; + unsigned int tfd_end; struct RxFD *rfd_base; struct RxFD *rfd_limit; struct RxFD *rfd_cur; struct FrFD *fbl_ptr; +#ifdef TC35815_USE_PACKEDBUFFER unsigned char fbl_curid; - dma_addr_t data_buf_dma_handle[RX_BUF_PAGES]; - void * data_buf[RX_BUF_PAGES]; /* packing */ - spinlock_t lock; + void * data_buf[RX_BUF_NUM]; /* packing */ + dma_addr_t data_buf_dma[RX_BUF_NUM]; + struct { + struct sk_buff *skb; + dma_addr_t skb_dma; + } tx_skbs[TX_FD_NUM]; +#else + unsigned int fbl_count; + struct { + struct sk_buff *skb; + dma_addr_t skb_dma; + } tx_skbs[TX_FD_NUM], rx_skbs[RX_BUF_NUM]; +#endif + struct mii_if_info mii; + unsigned short mii_id[2]; + u32 msg_enable; + board_t boardtype; }; -/* Index to functions, as function prototypes. */ +static inline dma_addr_t fd_virt_to_bus(struct tc35815_local *lp, void *virt) +{ + return lp->fd_buf_dma + ((u8 *)virt - (u8 *)lp->fd_buf); +} +#ifdef DEBUG +static inline void *fd_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) +{ + return (void *)((u8 *)lp->fd_buf + (bus - lp->fd_buf_dma)); +} +#endif +#ifdef TC35815_USE_PACKEDBUFFER +static inline void *rxbuf_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) +{ + int i; + for (i = 0; i < RX_BUF_NUM; i++) { + if (bus >= lp->data_buf_dma[i] && + bus < lp->data_buf_dma[i] + PAGE_SIZE) + return (void *)((u8 *)lp->data_buf[i] + + (bus - lp->data_buf_dma[i])); + } + return NULL; +} -static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq); +#define TC35815_DMA_SYNC_ONDEMAND +static void* alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle) +{ +#ifdef TC35815_DMA_SYNC_ONDEMAND + void *buf; + /* pci_map + pci_dma_sync will be more effective than + * pci_alloc_consistent on some archs. */ + if ((buf = (void *)__get_free_page(GFP_ATOMIC)) == NULL) + return NULL; + *dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE, + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(*dma_handle)) { + free_page((unsigned long)buf); + return NULL; + } + return buf; +#else + return pci_alloc_consistent(hwdev, PAGE_SIZE, dma_handle); +#endif +} + +static void free_rxbuf_page(struct pci_dev *hwdev, void *buf, dma_addr_t dma_handle) +{ +#ifdef TC35815_DMA_SYNC_ONDEMAND + pci_unmap_single(hwdev, dma_handle, PAGE_SIZE, PCI_DMA_FROMDEVICE); + free_page((unsigned long)buf); +#else + pci_free_consistent(hwdev, PAGE_SIZE, buf, dma_handle); +#endif +} +#else /* TC35815_USE_PACKEDBUFFER */ +static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, + struct pci_dev *hwdev, + dma_addr_t *dma_handle) +{ + struct sk_buff *skb; + skb = dev_alloc_skb(RX_BUF_SIZE); + if (!skb) + return NULL; + skb->dev = dev; + *dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(*dma_handle)) { + dev_kfree_skb_any(skb); + return NULL; + } + skb_reserve(skb, 2); /* make IP header 4byte aligned */ + return skb; +} + +static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_t dma_handle) +{ + pci_unmap_single(hwdev, dma_handle, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); +} +#endif /* TC35815_USE_PACKEDBUFFER */ + +/* Index to functions, as function prototypes. */ static int tc35815_open(struct net_device *dev); static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev); -static void tc35815_tx_timeout(struct net_device *dev); -static irqreturn_t tc35815_interrupt(int irq, void *dev_id); +static irqreturn_t tc35815_interrupt(int irq, void *dev_id); +#ifdef TC35815_NAPI +static int tc35815_rx(struct net_device *dev, int limit); +static int tc35815_poll(struct net_device *dev, int *budget); +#else static void tc35815_rx(struct net_device *dev); +#endif static void tc35815_txdone(struct net_device *dev); static int tc35815_close(struct net_device *dev); static struct net_device_stats *tc35815_get_stats(struct net_device *dev); static void tc35815_set_multicast_list(struct net_device *dev); +static void tc35815_tx_timeout(struct net_device *dev); +static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tc35815_poll_controller(struct net_device *dev); +#endif +static const struct ethtool_ops tc35815_ethtool_ops; +/* Example routines you must write ;->. */ static void tc35815_chip_reset(struct net_device *dev); static void tc35815_chip_init(struct net_device *dev); +static void tc35815_find_phy(struct net_device *dev); static void tc35815_phy_chip_init(struct net_device *dev); -/* A list of all installed tc35815 devices. */ -static struct net_device *root_tc35815_dev = NULL; +#ifdef DEBUG +static void panic_queues(struct net_device *dev); +#endif -/* - * PCI device identifiers for "new style" Linux PCI Device Drivers - */ -static struct pci_device_id tc35815_pci_tbl[] = { - { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, } -}; +static void tc35815_timer(unsigned long data); +static void tc35815_start_auto_negotiation(struct net_device *dev, + struct ethtool_cmd *ep); +static int tc_mdio_read(struct net_device *dev, int phy_id, int location); +static void tc_mdio_write(struct net_device *dev, int phy_id, int location, + int val); -MODULE_DEVICE_TABLE (pci, tc35815_pci_tbl); +static void __devinit tc35815_init_dev_addr (struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int i; -int -tc35815_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) + /* dev_addr will be overwritten on NETDEV_REGISTER event */ + while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) + ; + for (i = 0; i < 6; i += 2) { + unsigned short data; + tc_writel(PROM_Busy | PROM_Read | (i / 2 + 2), &tr->PROM_Ctl); + while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) + ; + data = tc_readl(&tr->PROM_Data); + dev->dev_addr[i] = data & 0xff; + dev->dev_addr[i+1] = data >> 8; + } +} + +static int __devinit tc35815_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { - int err = 0; - int ret; - unsigned long pci_memaddr; - unsigned int pci_irq_line; + void __iomem *ioaddr = NULL; + struct net_device *dev; + struct tc35815_local *lp; + int rc; + unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; + + static int printed_version; + if (!printed_version++) { + printk(version); + dev_printk(KERN_DEBUG, &pdev->dev, + "speed:%d duplex:%d doforce:%d\n", + options.speed, options.duplex, options.doforce); + } - printk(KERN_INFO "tc35815_probe: found device %#08x.%#08x\n", ent->vendor, ent->device); + if (!pdev->irq) { + dev_warn(&pdev->dev, "no IRQ assigned.\n"); + return -ENODEV; + } - err = pci_enable_device(pdev); - if (err) - return err; + /* dev zeroed in alloc_etherdev */ + dev = alloc_etherdev (sizeof (*lp)); + if (dev == NULL) { + dev_err(&pdev->dev, "unable to alloc new ethernet\n"); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); + lp = dev->priv; + + /* enable device (incl. PCI PM wakeup), and bus-mastering */ + rc = pci_enable_device (pdev); + if (rc) + goto err_out; - pci_memaddr = pci_resource_start (pdev, 1); + mmio_start = pci_resource_start (pdev, 1); + mmio_end = pci_resource_end (pdev, 1); + mmio_flags = pci_resource_flags (pdev, 1); + mmio_len = pci_resource_len (pdev, 1); - printk(KERN_INFO " pci_memaddr=%#08lx resource_flags=%#08lx\n", pci_memaddr, pci_resource_flags (pdev, 0)); + /* set this immediately, we need to know before + * we talk to the chip directly */ - if (!pci_memaddr) { - printk(KERN_WARNING "no PCI MEM resources, aborting\n"); - ret = -ENODEV; + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); + rc = -ENODEV; goto err_out; } - pci_irq_line = pdev->irq; - /* irq disabled. */ - if (pci_irq_line == 0) { - printk(KERN_WARNING "no PCI irq, aborting\n"); - ret = -ENODEV; + + /* check for weird/broken PCI region reporting */ + if ((mmio_len < sizeof(struct tc35815_regs))) { + dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n"); + rc = -ENODEV; goto err_out; } - ret = tc35815_probe1(pdev, pci_memaddr, pci_irq_line); - if (ret) + rc = pci_request_regions (pdev, MODNAME); + if (rc) goto err_out; - pci_set_master(pdev); - - return 0; + pci_set_master (pdev); -err_out: - pci_disable_device(pdev); - return ret; -} + /* ioremap MMIO region */ + ioaddr = ioremap (mmio_start, mmio_len); + if (ioaddr == NULL) { + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + rc = -EIO; + goto err_out_free_res; + } -static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq) -{ - static unsigned version_printed = 0; - int i, ret; - struct tc35815_local *lp; - struct tc35815_regs *tr; - struct net_device *dev; + /* Initialize the device structure. */ + dev->open = tc35815_open; + dev->hard_start_xmit = tc35815_send_packet; + dev->stop = tc35815_close; + dev->get_stats = tc35815_get_stats; + dev->set_multicast_list = tc35815_set_multicast_list; + dev->do_ioctl = tc35815_ioctl; + dev->ethtool_ops = &tc35815_ethtool_ops; + dev->tx_timeout = tc35815_tx_timeout; + dev->watchdog_timeo = TC35815_TX_TIMEOUT; +#ifdef TC35815_NAPI + dev->poll = tc35815_poll; + dev->weight = NAPI_WEIGHT; +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = tc35815_poll_controller; +#endif - /* Allocate a new 'dev' if needed. */ - dev = alloc_etherdev(sizeof(struct tc35815_local)); - if (dev == NULL) - return -ENOMEM; + dev->irq = pdev->irq; + dev->base_addr = (unsigned long) ioaddr; - /* - * alloc_etherdev allocs and zeros dev->priv - */ + /* dev->priv/lp zeroed and aligned in alloc_etherdev */ lp = dev->priv; + spin_lock_init(&lp->lock); + lp->pci_dev = pdev; + lp->boardtype = ent->driver_data; - if (tc35815_debug && version_printed++ == 0) - printk(KERN_DEBUG "%s", version); - - /* Fill in the 'dev' fields. */ - dev->irq = irq; - dev->base_addr = (unsigned long)ioremap(base_addr, - sizeof(struct tc35815_regs)); - if (!dev->base_addr) { - ret = -ENOMEM; - goto err_out; - } - tr = (struct tc35815_regs*)dev->base_addr; + lp->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV | NETIF_MSG_LINK; + pci_set_drvdata(pdev, dev); + /* Soft reset the chip. */ tc35815_chip_reset(dev); - /* Retrieve and print the ethernet address. */ - while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) - ; - for (i = 0; i < 6; i += 2) { - unsigned short data; - tc_writel(PROM_Busy | PROM_Read | (i / 2 + 2), &tr->PROM_Ctl); - while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) - ; - data = tc_readl(&tr->PROM_Data); - dev->dev_addr[i] = data & 0xff; - dev->dev_addr[i+1] = data >> 8; - } + /* Retrieve the ethernet address. */ + tc35815_init_dev_addr(dev); + + rc = register_netdev (dev); + if (rc) + goto err_out_unmap; + + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + printk(KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", + dev->name, + board_info[ent->driver_data].name, + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + setup_timer(&lp->timer, tc35815_timer, (unsigned long) dev); + lp->mii.dev = dev; + lp->mii.mdio_read = tc_mdio_read; + lp->mii.mdio_write = tc_mdio_write; + lp->mii.phy_id_mask = 0x1f; + lp->mii.reg_num_mask = 0x1f; + tc35815_find_phy(dev); + lp->mii.phy_id = lp->phy_addr; + lp->mii.full_duplex = 0; + lp->mii.force_media = 0; - /* Initialize the device structure. */ - lp->pdev = pdev; - lp->next_module = root_tc35815_dev; - root_tc35815_dev = dev; + return 0; - spin_lock_init(&lp->lock); +err_out_unmap: + iounmap(ioaddr); +err_out_free_res: + pci_release_regions (pdev); +err_out: + free_netdev (dev); + return rc; +} - if (dev->mem_start > 0) { - lp->option = dev->mem_start; - if ((lp->option & TC35815_OPT_10M) && - (lp->option & TC35815_OPT_100M)) { - /* if both speed speficied, auto select. */ - lp->option &= ~(TC35815_OPT_10M | TC35815_OPT_100M); - } - } - //XXX fixme - lp->option |= TC35815_OPT_10M; - /* do auto negotiation */ - tc35815_phy_chip_init(dev); +static void __devexit tc35815_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + unsigned long mmio_addr; - dev->open = tc35815_open; - dev->stop = tc35815_close; - dev->tx_timeout = tc35815_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - dev->hard_start_xmit = tc35815_send_packet; - dev->get_stats = tc35815_get_stats; - dev->set_multicast_list = tc35815_set_multicast_list; - SET_MODULE_OWNER(dev); - SET_NETDEV_DEV(dev, &pdev->dev); + mmio_addr = dev->base_addr; - ret = register_netdev(dev); - if (ret) - goto err_out_iounmap; + unregister_netdev (dev); - printk(KERN_INFO "%s: %s found at %#x, irq %d, MAC", - dev->name, cardname, base_addr, irq); - for (i = 0; i < 6; i++) - printk(" %2.2x", dev->dev_addr[i]); - printk("\n"); - printk(KERN_INFO "%s: linkspeed %dMbps, %s Duplex\n", - dev->name, lp->linkspeed, lp->fullduplex ? "Full" : "Half"); + if (mmio_addr) { + iounmap ((void __iomem *)mmio_addr); + pci_release_regions (pdev); + } - return 0; + free_netdev (dev); -err_out_iounmap: - iounmap((void *) dev->base_addr); -err_out: - free_netdev(dev); - return ret; + pci_set_drvdata (pdev, NULL); } - static int tc35815_init_queues(struct net_device *dev) { @@ -629,44 +793,64 @@ tc35815_init_queues(struct net_device *d unsigned long fd_addr; if (!lp->fd_buf) { - if (sizeof(struct FDesc) + - sizeof(struct BDesc) * RX_BUF_PAGES + - sizeof(struct FDesc) * RX_FD_NUM + - sizeof(struct TxFD) * TX_FD_NUM > PAGE_SIZE * FD_PAGE_NUM) { - printk(KERN_WARNING "%s: Invalid Queue Size.\n", dev->name); - return -ENOMEM; - } + BUG_ON(sizeof(struct FDesc) + + sizeof(struct BDesc) * RX_BUF_NUM + + sizeof(struct FDesc) * RX_FD_NUM + + sizeof(struct TxFD) * TX_FD_NUM > + PAGE_SIZE * FD_PAGE_NUM); - if ((lp->fd_buf = (void *)__get_free_pages(GFP_KERNEL, FD_PAGE_ORDER)) == 0) + if ((lp->fd_buf = pci_alloc_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, &lp->fd_buf_dma)) == 0) return -ENOMEM; - for (i = 0; i < RX_BUF_PAGES; i++) { - if ((lp->data_buf[i] = (void *)get_zeroed_page(GFP_KERNEL)) == 0) { + for (i = 0; i < RX_BUF_NUM; i++) { +#ifdef TC35815_USE_PACKEDBUFFER + if ((lp->data_buf[i] = alloc_rxbuf_page(lp->pci_dev, &lp->data_buf_dma[i])) == NULL) { while (--i >= 0) { - free_page((unsigned long)lp->data_buf[i]); - lp->data_buf[i] = 0; + free_rxbuf_page(lp->pci_dev, + lp->data_buf[i], + lp->data_buf_dma[i]); + lp->data_buf[i] = NULL; } - free_page((unsigned long)lp->fd_buf); - lp->fd_buf = 0; + pci_free_consistent(lp->pci_dev, + PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, + lp->fd_buf_dma); + lp->fd_buf = NULL; + return -ENOMEM; + } +#else + lp->rx_skbs[i].skb = + alloc_rxbuf_skb(dev, lp->pci_dev, + &lp->rx_skbs[i].skb_dma); + if (!lp->rx_skbs[i].skb) { + while (--i >= 0) { + free_rxbuf_skb(lp->pci_dev, + lp->rx_skbs[i].skb, + lp->rx_skbs[i].skb_dma); + lp->rx_skbs[i].skb = NULL; + } + pci_free_consistent(lp->pci_dev, + PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, + lp->fd_buf_dma); + lp->fd_buf = NULL; return -ENOMEM; } -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)lp->data_buf[i], PAGE_SIZE * FD_PAGE_NUM); #endif } -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM); + printk(KERN_DEBUG "%s: FD buf %p DataBuf", + dev->name, lp->fd_buf); +#ifdef TC35815_USE_PACKEDBUFFER + printk(" DataBuf"); + for (i = 0; i < RX_BUF_NUM; i++) + printk(" %p", lp->data_buf[i]); #endif + printk("\n"); } else { - memset(lp->fd_buf, 0, PAGE_SIZE * FD_PAGE_NUM); -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM); -#endif + for (i = 0; i < FD_PAGE_NUM; i++) { + clear_page((void *)((unsigned long)lp->fd_buf + i * PAGE_SIZE)); + } } -#ifdef __mips__ - fd_addr = (unsigned long)vtonocache(lp->fd_buf); -#else fd_addr = (unsigned long)lp->fd_buf; -#endif /* Free Descriptors (for Receive) */ lp->rfd_base = (struct RxFD *)fd_addr; @@ -675,34 +859,66 @@ #endif lp->rfd_base[i].fd.FDCtl = cpu_to_le32(FD_CownsFD); } lp->rfd_cur = lp->rfd_base; - lp->rfd_limit = (struct RxFD *)(fd_addr - - sizeof(struct FDesc) - - sizeof(struct BDesc) * 30); + lp->rfd_limit = (struct RxFD *)fd_addr - (RX_FD_RESERVE + 1); /* Transmit Descriptors */ lp->tfd_base = (struct TxFD *)fd_addr; fd_addr += sizeof(struct TxFD) * TX_FD_NUM; for (i = 0; i < TX_FD_NUM; i++) { - lp->tfd_base[i].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[i+1])); - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0); + lp->tfd_base[i].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[i+1])); + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); lp->tfd_base[i].fd.FDCtl = cpu_to_le32(0); } - lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[0])); + lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[0])); lp->tfd_start = 0; lp->tfd_end = 0; /* Buffer List (for Receive) */ lp->fbl_ptr = (struct FrFD *)fd_addr; - lp->fbl_ptr->fd.FDNext = cpu_to_le32(virt_to_bus(lp->fbl_ptr)); - lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_PAGES | FD_CownsFD); - for (i = 0; i < RX_BUF_PAGES; i++) { - lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(virt_to_bus(lp->data_buf[i])); + lp->fbl_ptr->fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, lp->fbl_ptr)); + lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_NUM | FD_CownsFD); +#ifndef TC35815_USE_PACKEDBUFFER + /* + * move all allocated skbs to head of rx_skbs[] array. + * fbl_count mighe not be RX_BUF_NUM if alloc_rxbuf_skb() in + * tc35815_rx() had failed. + */ + lp->fbl_count = 0; + for (i = 0; i < RX_BUF_NUM; i++) { + if (lp->rx_skbs[i].skb) { + if (i != lp->fbl_count) { + lp->rx_skbs[lp->fbl_count].skb = + lp->rx_skbs[i].skb; + lp->rx_skbs[lp->fbl_count].skb_dma = + lp->rx_skbs[i].skb_dma; + } + lp->fbl_count++; + } + } +#endif + for (i = 0; i < RX_BUF_NUM; i++) { +#ifdef TC35815_USE_PACKEDBUFFER + lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(lp->data_buf_dma[i]); +#else + if (i >= lp->fbl_count) { + lp->fbl_ptr->bd[i].BuffData = 0; + lp->fbl_ptr->bd[i].BDCtl = 0; + continue; + } + lp->fbl_ptr->bd[i].BuffData = + cpu_to_le32(lp->rx_skbs[i].skb_dma); +#endif /* BDID is index of FrFD.bd[] */ lp->fbl_ptr->bd[i].BDCtl = - cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | PAGE_SIZE); + cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | + RX_BUF_SIZE); } +#ifdef TC35815_USE_PACKEDBUFFER lp->fbl_curid = 0; +#endif + printk(KERN_DEBUG "%s: TxFD %p RxFD %p FrFD %p\n", + dev->name, lp->tfd_base, lp->rfd_base, lp->fbl_ptr); return 0; } @@ -713,11 +929,25 @@ tc35815_clear_queues(struct net_device * int i; for (i = 0; i < TX_FD_NUM; i++) { - struct sk_buff *skb = (struct sk_buff *) - le32_to_cpu(lp->tfd_base[i].fd.FDSystem); - if (skb) + u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); + struct sk_buff *skb = + fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[i].skb != skb) { + printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[i].skb != skb); +#endif + if (skb) { + pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[i].skb = NULL; + lp->tx_skbs[i].skb_dma = 0; dev_kfree_skb_any(skb); - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0); + } + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); } tc35815_init_queues(dev); @@ -731,28 +961,53 @@ tc35815_free_queues(struct net_device *d if (lp->tfd_base) { for (i = 0; i < TX_FD_NUM; i++) { - struct sk_buff *skb = (struct sk_buff *) - le32_to_cpu(lp->tfd_base[i].fd.FDSystem); - if (skb) - dev_kfree_skb_any(skb); - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0); + u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); + struct sk_buff *skb = + fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[i].skb != skb) { + printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[i].skb != skb); +#endif + if (skb) { + dev_kfree_skb(skb); + pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[i].skb = NULL; + lp->tx_skbs[i].skb_dma = 0; + } + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); } } lp->rfd_base = NULL; - lp->rfd_base = NULL; lp->rfd_limit = NULL; lp->rfd_cur = NULL; lp->fbl_ptr = NULL; - for (i = 0; i < RX_BUF_PAGES; i++) { - if (lp->data_buf[i]) - free_page((unsigned long)lp->data_buf[i]); - lp->data_buf[i] = 0; + for (i = 0; i < RX_BUF_NUM; i++) { +#ifdef TC35815_USE_PACKEDBUFFER + if (lp->data_buf[i]) { + free_rxbuf_page(lp->pci_dev, + lp->data_buf[i], lp->data_buf_dma[i]); + lp->data_buf[i] = NULL; + } +#else + if (lp->rx_skbs[i].skb) { + free_rxbuf_skb(lp->pci_dev, lp->rx_skbs[i].skb, + lp->rx_skbs[i].skb_dma); + lp->rx_skbs[i].skb = NULL; + } +#endif + } + if (lp->fd_buf) { + pci_free_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, lp->fd_buf_dma); + lp->fd_buf = NULL; } - if (lp->fd_buf) - __free_pages(lp->fd_buf, FD_PAGE_ORDER); - lp->fd_buf = NULL; } static void @@ -792,6 +1047,7 @@ dump_rxfd(struct RxFD *fd) return bd_count; } +#if defined(DEBUG) || defined(TC35815_USE_PACKEDBUFFER) static void dump_frfd(struct FrFD *fd) { @@ -802,20 +1058,22 @@ dump_frfd(struct FrFD *fd) le32_to_cpu(fd->fd.FDStat), le32_to_cpu(fd->fd.FDCtl)); printk("BD: "); - for (i = 0; i < RX_BUF_PAGES; i++) + for (i = 0; i < RX_BUF_NUM; i++) printk(" %08x %08x", le32_to_cpu(fd->bd[i].BuffData), le32_to_cpu(fd->bd[i].BDCtl)); printk("\n"); } +#endif +#ifdef DEBUG static void panic_queues(struct net_device *dev) { struct tc35815_local *lp = dev->priv; int i; - printk("TxFD base %p, start %d, end %d\n", + printk("TxFD base %p, start %u, end %u\n", lp->tfd_base, lp->tfd_start, lp->tfd_end); printk("RxFD base %p limit %p cur %p\n", lp->rfd_base, lp->rfd_limit, lp->rfd_cur); @@ -829,31 +1087,13 @@ panic_queues(struct net_device *dev) dump_frfd(lp->fbl_ptr); panic("%s: Illegal queue state.", dev->name); } - -#if 0 -static void print_buf(char *add, int length) -{ - int i; - int len = length; - - printk("print_buf(%08x)(%x)\n", (unsigned int) add,length); - - if (len > 100) - len = 100; - for (i = 0; i < len; i++) { - printk(" %2.2X", (unsigned char) add[i]); - if (!(i % 16)) - printk("\n"); - } - printk("\n"); -} #endif static void print_eth(char *add) { int i; - printk("print_eth(%08x)\n", (unsigned int) add); + printk("print_eth(%p)\n", add); for (i = 0; i < 6; i++) printk(" %2.2X", (unsigned char) add[i + 6]); printk(" =>"); @@ -862,6 +1102,73 @@ static void print_eth(char *add) printk(" : %2.2X%2.2X\n", (unsigned char) add[12], (unsigned char) add[13]); } +static int tc35815_tx_full(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + return ((lp->tfd_start + 1) % TX_FD_NUM == lp->tfd_end); +} + +static void tc35815_restart(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + int do_phy_reset = 1; + del_timer(&lp->timer); /* Kill if running */ + + if (lp->mii_id[0] == 0x0016 && (lp->mii_id[1] & 0xfc00) == 0xf800) { + /* Resetting PHY cause problem on some chip... (SEEQ 80221) */ + do_phy_reset = 0; + } + if (do_phy_reset) { + int timeout; + tc_mdio_write(dev, pid, MII_BMCR, BMCR_RESET); + timeout = 100; + while (--timeout) { + if (!(tc_mdio_read(dev, pid, MII_BMCR) & BMCR_RESET)) + break; + udelay(1); + } + if (!timeout) + printk(KERN_ERR "%s: BMCR reset failed.\n", dev->name); + } + + tc35815_chip_reset(dev); + tc35815_clear_queues(dev); + tc35815_chip_init(dev); + /* Reconfigure CAM again since tc35815_chip_init() initialize it. */ + tc35815_set_multicast_list(dev); +} + +static void tc35815_tx_timeout(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + + printk(KERN_WARNING "%s: transmit timed out, status %#x\n", + dev->name, tc_readl(&tr->Tx_Stat)); + + /* Try to restart the adaptor. */ + spin_lock_irq(&lp->lock); + tc35815_restart(dev); + spin_unlock_irq(&lp->lock); + + lp->stats.tx_errors++; + + /* If we have space available to accept new transmit + * requests, wake up the queueing layer. This would + * be the case if the chipset_init() call above just + * flushes out the tx queue and empties it. + * + * If instead, the tx queue is retained then the + * netif_wake_queue() call should be placed in the + * TX completion interrupt handler of the driver instead + * of here. + */ + if (!tc35815_tx_full(dev)) + netif_wake_queue(dev); +} + /* * Open/initialize the board. This is called (in the current kernel) * sometime after booting when the 'ifconfig' program is run. @@ -874,16 +1181,16 @@ static int tc35815_open(struct net_device *dev) { struct tc35815_local *lp = dev->priv; + /* * This is used if the interrupt line can turned off (shared). * See 3c503.c for an example of selecting the IRQ at config-time. */ - - if (dev->irq == 0 || - request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED, cardname, dev)) { + if (request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED, dev->name, dev)) { return -EAGAIN; } + del_timer(&lp->timer); /* Kill if running */ tc35815_chip_reset(dev); if (tc35815_init_queues(dev) != 0) { @@ -892,138 +1199,119 @@ tc35815_open(struct net_device *dev) } /* Reset the hardware here. Don't forget to set the station address. */ + spin_lock_irq(&lp->lock); tc35815_chip_init(dev); + spin_unlock_irq(&lp->lock); - lp->tbusy = 0; + /* We are now ready to accept transmit requeusts from + * the queueing layer of the networking. + */ netif_start_queue(dev); return 0; } -static void tc35815_tx_timeout(struct net_device *dev) -{ - struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs *)dev->base_addr; - unsigned long flags; - - spin_lock_irqsave(&lp->lock, flags); - printk(KERN_WARNING "%s: transmit timed out, status %#lx\n", - dev->name, tc_readl(&tr->Tx_Stat)); - /* Try to restart the adaptor. */ - tc35815_chip_reset(dev); - tc35815_clear_queues(dev); - tc35815_chip_init(dev); - lp->tbusy=0; - spin_unlock_irqrestore(&lp->lock, flags); - dev->trans_start = jiffies; - netif_wake_queue(dev); -} - +/* This will only be invoked if your driver is _not_ in XOFF state. + * What this means is that you need not check it, and that this + * invariant will hold if you make sure that the netif_*_queue() + * calls are done at the proper times. + */ static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs *)dev->base_addr; - - if (netif_queue_stopped(dev)) { - /* - * If we get here, some higher level has decided we are broken. - * There should really be a "kick me" function call instead. - */ - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 5) - return 1; - printk(KERN_WARNING "%s: transmit timed out, status %#lx\n", - dev->name, tc_readl(&tr->Tx_Stat)); - /* Try to restart the adaptor. */ - tc35815_chip_reset(dev); - tc35815_clear_queues(dev); - tc35815_chip_init(dev); - lp->tbusy=0; - dev->trans_start = jiffies; - netif_wake_queue(dev); - } + struct TxFD *txfd; + unsigned long flags; - /* - * Block a timer-based transmit from overlapping. This could better be - * done with atomic_swap(1, lp->tbusy), but set_bit() works as well. + /* If some error occurs while trying to transmit this + * packet, you should return '1' from this function. + * In such a case you _may not_ do anything to the + * SKB, it is still owned by the network queueing + * layer when an error is returned. This means you + * may not modify any SKB fields, you may not free + * the SKB, etc. */ - if (test_and_set_bit(0, (void*)&lp->tbusy) != 0) { - printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); - dev_kfree_skb_any(skb); - } else { - short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned char *buf = skb->data; - struct TxFD *txfd = &lp->tfd_base[lp->tfd_start]; - unsigned long flags; - lp->stats.tx_bytes += skb->len; + /* This is the most common case for modern hardware. + * The spinlock protects this code from the TX complete + * hardware interrupt handler. Queue flow control is + * thus managed under this lock as well. + */ + spin_lock_irqsave(&lp->lock, flags); -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)buf, length); + /* failsafe... (handle txdone now if half of FDs are used) */ + if ((lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM > + TX_FD_NUM / 2) + tc35815_txdone(dev); + + if (netif_msg_pktdata(lp)) + print_eth(skb->data); +#ifdef DEBUG + if (lp->tx_skbs[lp->tfd_start].skb) { + printk("%s: tx_skbs conflict.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[lp->tfd_start].skb); #endif - - spin_lock_irqsave(&lp->lock, flags); - - /* failsafe... */ - if (lp->tfd_start != lp->tfd_end) - tc35815_txdone(dev); - - - txfd->bd.BuffData = cpu_to_le32(virt_to_bus(buf)); - - txfd->bd.BDCtl = cpu_to_le32(length); - txfd->fd.FDSystem = cpu_to_le32((__u32)skb); - txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT)); - - if (lp->tfd_start == lp->tfd_end) { - /* Start DMA Transmitter. */ - txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL); + lp->tx_skbs[lp->tfd_start].skb = skb; + lp->tx_skbs[lp->tfd_start].skb_dma = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); + + /*add to ring */ + txfd = &lp->tfd_base[lp->tfd_start]; + txfd->bd.BuffData = cpu_to_le32(lp->tx_skbs[lp->tfd_start].skb_dma); + txfd->bd.BDCtl = cpu_to_le32(skb->len); + txfd->fd.FDSystem = cpu_to_le32(lp->tfd_start); + txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT)); + + if (lp->tfd_start == lp->tfd_end) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + /* Start DMA Transmitter. */ + txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL); #ifdef GATHER_TXINT - txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); + txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); #endif - if (tc35815_debug > 2) { - printk("%s: starting TxFD.\n", dev->name); - dump_txfd(txfd); - if (tc35815_debug > 3) - print_eth(buf); - } - tc_writel(virt_to_bus(txfd), &tr->TxFrmPtr); - } else { - txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL); - if (tc35815_debug > 2) { - printk("%s: queueing TxFD.\n", dev->name); - dump_txfd(txfd); - if (tc35815_debug > 3) - print_eth(buf); - } + if (netif_msg_tx_queued(lp)) { + printk("%s: starting TxFD.\n", dev->name); + dump_txfd(txfd); } - lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM; + tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); + } else { + txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL); + if (netif_msg_tx_queued(lp)) { + printk("%s: queueing TxFD.\n", dev->name); + dump_txfd(txfd); + } + } + lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM; - dev->trans_start = jiffies; + dev->trans_start = jiffies; - if ((lp->tfd_start + 1) % TX_FD_NUM != lp->tfd_end) { - /* we can send another packet */ - lp->tbusy = 0; - netif_start_queue(dev); - } else { - netif_stop_queue(dev); - if (tc35815_debug > 1) - printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name); - } - spin_unlock_irqrestore(&lp->lock, flags); + /* If we just used up the very last entry in the + * TX ring on this device, tell the queueing + * layer to send no more. + */ + if (tc35815_tx_full(dev)) { + if (netif_msg_tx_queued(lp)) + printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name); + netif_stop_queue(dev); } + /* When the TX completion hw interrupt arrives, this + * is when the transmit statistics are updated. + */ + + spin_unlock_irqrestore(&lp->lock, flags); return 0; } #define FATAL_ERROR_INT \ (Int_IntPCI | Int_DmParErr | Int_IntNRAbt) -static void tc35815_fatal_error_interrupt(struct net_device *dev, int status) +static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status) { static int count; printk(KERN_WARNING "%s: Fatal Error Intterrupt (%#x):", dev->name, status); - if (status & Int_IntPCI) printk(" IntPCI"); if (status & Int_DmParErr) @@ -1033,110 +1321,170 @@ static void tc35815_fatal_error_interrup printk("\n"); if (count++ > 100) panic("%s: Too many fatal errors.", dev->name); - printk(KERN_WARNING "%s: Resetting %s...\n", dev->name, cardname); + printk(KERN_WARNING "%s: Resetting ...\n", dev->name); /* Try to restart the adaptor. */ - tc35815_chip_reset(dev); - tc35815_clear_queues(dev); - tc35815_chip_init(dev); + tc35815_restart(dev); +} + +#ifdef TC35815_NAPI +static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit) +#else +static int tc35815_do_interrupt(struct net_device *dev, u32 status) +#endif +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int ret = -1; + + /* Fatal errors... */ + if (status & FATAL_ERROR_INT) { + tc35815_fatal_error_interrupt(dev, status); + return 0; + } + /* recoverable errors */ + if (status & Int_IntFDAEx) { + /* disable FDAEx int. (until we make rooms...) */ + tc_writel(tc_readl(&tr->Int_En) & ~Int_FDAExEn, &tr->Int_En); + printk(KERN_WARNING + "%s: Free Descriptor Area Exhausted (%#x).\n", + dev->name, status); + lp->stats.rx_dropped++; + ret = 0; + } + if (status & Int_IntBLEx) { + /* disable BLEx int. (until we make rooms...) */ + tc_writel(tc_readl(&tr->Int_En) & ~Int_BLExEn, &tr->Int_En); + printk(KERN_WARNING + "%s: Buffer List Exhausted (%#x).\n", + dev->name, status); + lp->stats.rx_dropped++; + ret = 0; + } + if (status & Int_IntExBD) { + printk(KERN_WARNING + "%s: Excessive Buffer Descriptiors (%#x).\n", + dev->name, status); + lp->stats.rx_length_errors++; + ret = 0; + } + + /* normal notification */ + if (status & Int_IntMacRx) { + /* Got a packet(s). */ +#ifdef TC35815_NAPI + ret = tc35815_rx(dev, limit); +#else + tc35815_rx(dev); + ret = 0; +#endif + lp->lstats.rx_ints++; + } + if (status & Int_IntMacTx) { + /* Transmit complete. */ + lp->lstats.tx_ints++; + tc35815_txdone(dev); + netif_wake_queue(dev); + ret = 0; + } + return ret; } /* * The typical workload of the driver: - * Handle the network interface interrupts. + * Handle the network interface interrupts. */ static irqreturn_t tc35815_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; - struct tc35815_regs *tr; - struct tc35815_local *lp; - int status, boguscount = 0; - int handled = 0; - - if (dev == NULL) { - printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); - return IRQ_NONE; - } - - tr = (struct tc35815_regs*)dev->base_addr; - lp = dev->priv; - - do { - status = tc_readl(&tr->Int_Src); - if (status == 0) - break; - handled = 1; - tc_writel(status, &tr->Int_Src); /* write to clear */ - - /* Fatal errors... */ - if (status & FATAL_ERROR_INT) { - tc35815_fatal_error_interrupt(dev, status); - break; - } - /* recoverable errors */ - if (status & Int_IntFDAEx) { - /* disable FDAEx int. (until we make rooms...) */ - tc_writel(tc_readl(&tr->Int_En) & ~Int_FDAExEn, &tr->Int_En); - printk(KERN_WARNING - "%s: Free Descriptor Area Exhausted (%#x).\n", - dev->name, status); - lp->stats.rx_dropped++; - } - if (status & Int_IntBLEx) { - /* disable BLEx int. (until we make rooms...) */ - tc_writel(tc_readl(&tr->Int_En) & ~Int_BLExEn, &tr->Int_En); - printk(KERN_WARNING - "%s: Buffer List Exhausted (%#x).\n", - dev->name, status); - lp->stats.rx_dropped++; - } - if (status & Int_IntExBD) { - printk(KERN_WARNING - "%s: Excessive Buffer Descriptiors (%#x).\n", - dev->name, status); - lp->stats.rx_length_errors++; - } - /* normal notification */ - if (status & Int_IntMacRx) { - /* Got a packet(s). */ - lp->lstats.rx_ints++; - tc35815_rx(dev); - } - if (status & Int_IntMacTx) { - lp->lstats.tx_ints++; - tc35815_txdone(dev); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; +#ifdef TC35815_NAPI + u32 dmactl = tc_readl(&tr->DMA_Ctl); + + if (!(dmactl & DMA_IntMask)) { + /* disable interrupts */ + tc_writel(dmactl | DMA_IntMask, &tr->DMA_Ctl); + if (netif_rx_schedule_prep(dev)) + __netif_rx_schedule(dev); + else { + printk(KERN_ERR "%s: interrupt taken in poll\n", + dev->name); + BUG(); } - } while (++boguscount < 20) ; + (void)tc_readl(&tr->Int_Src); /* flush */ + return IRQ_HANDLED; + } + return IRQ_NONE; +#else + struct tc35815_local *lp = dev->priv; + int handled; + u32 status; + + spin_lock(&lp->lock); + status = tc_readl(&tr->Int_Src); + tc_writel(status, &tr->Int_Src); /* write to clear */ + handled = tc35815_do_interrupt(dev, status); + (void)tc_readl(&tr->Int_Src); /* flush */ + spin_unlock(&lp->lock); + return IRQ_RETVAL(handled >= 0); +#endif /* TC35815_NAPI */ +} - return IRQ_RETVAL(handled); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tc35815_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + tc35815_interrupt(dev->irq, dev); + enable_irq(dev->irq); } +#endif /* We have a good packet(s), get it/them out of the buffers. */ +#ifdef TC35815_NAPI +static int +tc35815_rx(struct net_device *dev, int limit) +#else static void tc35815_rx(struct net_device *dev) +#endif { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; unsigned int fdctl; int i; int buf_free_count = 0; int fd_free_count = 0; +#ifdef TC35815_NAPI + int received = 0; +#endif while (!((fdctl = le32_to_cpu(lp->rfd_cur->fd.FDCtl)) & FD_CownsFD)) { int status = le32_to_cpu(lp->rfd_cur->fd.FDStat); int pkt_len = fdctl & FD_FDLength_MASK; - struct RxFD *next_rfd; int bd_count = (fdctl & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT; +#ifdef DEBUG + struct RxFD *next_rfd; +#endif +#if (RX_CTL_CMD & Rx_StripCRC) == 0 + pkt_len -= 4; +#endif - if (tc35815_debug > 2) + if (netif_msg_rx_status(lp)) dump_rxfd(lp->rfd_cur); if (status & Rx_Good) { - /* Malloc up new buffer. */ struct sk_buff *skb; unsigned char *data; - int cur_bd, offset; - - lp->stats.rx_bytes += pkt_len; + int cur_bd; +#ifdef TC35815_USE_PACKEDBUFFER + int offset; +#endif +#ifdef TC35815_NAPI + if (--limit < 0) + break; +#endif +#ifdef TC35815_USE_PACKEDBUFFER + BUG_ON(bd_count > 2); skb = dev_alloc_skb(pkt_len + 2); /* +2: for reserve */ if (skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", @@ -1145,7 +1493,6 @@ tc35815_rx(struct net_device *dev) break; } skb_reserve(skb, 2); /* 16 bit alignment */ - skb->dev = dev; data = skb_put(skb, pkt_len); @@ -1155,25 +1502,69 @@ tc35815_rx(struct net_device *dev) while (offset < pkt_len && cur_bd < bd_count) { int len = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BDCtl) & BD_BuffLength_MASK; - void *rxbuf = - bus_to_virt(le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData)); -#ifdef __mips__ - dma_cache_inv((unsigned long)rxbuf, len); + dma_addr_t dma = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData); + void *rxbuf = rxbuf_bus_to_virt(lp, dma); + if (offset + len > pkt_len) + len = pkt_len - offset; +#ifdef TC35815_DMA_SYNC_ONDEMAND + pci_dma_sync_single_for_cpu(lp->pci_dev, + dma, len, + PCI_DMA_FROMDEVICE); #endif memcpy(data + offset, rxbuf, len); +#ifdef TC35815_DMA_SYNC_ONDEMAND + pci_dma_sync_single_for_device(lp->pci_dev, + dma, len, + PCI_DMA_FROMDEVICE); +#endif offset += len; cur_bd++; } -#if 0 - print_buf(data,pkt_len); +#else /* TC35815_USE_PACKEDBUFFER */ + BUG_ON(bd_count > 1); + cur_bd = (le32_to_cpu(lp->rfd_cur->bd[0].BDCtl) + & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; +#ifdef DEBUG + if (cur_bd >= RX_BUF_NUM) { + printk("%s: invalid BDID.\n", dev->name); + panic_queues(dev); + } + BUG_ON(lp->rx_skbs[cur_bd].skb_dma != + (le32_to_cpu(lp->rfd_cur->bd[0].BuffData) & ~3)); + if (!lp->rx_skbs[cur_bd].skb) { + printk("%s: NULL skb.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(cur_bd >= RX_BUF_NUM); #endif - if (tc35815_debug > 3) + skb = lp->rx_skbs[cur_bd].skb; + prefetch(skb->data); + lp->rx_skbs[cur_bd].skb = NULL; + lp->fbl_count--; + pci_unmap_single(lp->pci_dev, + lp->rx_skbs[cur_bd].skb_dma, + RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (!HAVE_DMA_RXALIGN(lp)) + memmove(skb->data, skb->data - 2, pkt_len); + data = skb_put(skb, pkt_len); +#endif /* TC35815_USE_PACKEDBUFFER */ + if (netif_msg_pktdata(lp)) print_eth(data); skb->protocol = eth_type_trans(skb, dev); +#ifdef TC35815_NAPI + netif_receive_skb(skb); + received++; +#else netif_rx(skb); +#endif + dev->last_rx = jiffies; lp->stats.rx_packets++; + lp->stats.rx_bytes += pkt_len; } else { lp->stats.rx_errors++; + printk(KERN_DEBUG "%s: Rx error (status %x)\n", + dev->name, status & Rx_Stat_Mask); /* WORKAROUND: LongErr and CRCErr means Overflow. */ if ((status & Rx_LongErr) && (status & Rx_CRCErr)) { status &= ~(Rx_LongErr|Rx_CRCErr); @@ -1190,63 +1581,150 @@ #endif int bdctl = le32_to_cpu(lp->rfd_cur->bd[bd_count - 1].BDCtl); unsigned char id = (bdctl & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; - if (id >= RX_BUF_PAGES) { +#ifdef DEBUG + if (id >= RX_BUF_NUM) { printk("%s: invalid BDID.\n", dev->name); panic_queues(dev); } +#else + BUG_ON(id >= RX_BUF_NUM); +#endif /* free old buffers */ - while (lp->fbl_curid != id) { - bdctl = le32_to_cpu(lp->fbl_ptr->bd[lp->fbl_curid].BDCtl); +#ifdef TC35815_USE_PACKEDBUFFER + while (lp->fbl_curid != id) +#else + while (lp->fbl_count < RX_BUF_NUM) +#endif + { +#ifdef TC35815_USE_PACKEDBUFFER + unsigned char curid = lp->fbl_curid; +#else + unsigned char curid = + (id + 1 + lp->fbl_count) % RX_BUF_NUM; +#endif + struct BDesc *bd = &lp->fbl_ptr->bd[curid]; +#ifdef DEBUG + bdctl = le32_to_cpu(bd->BDCtl); if (bdctl & BD_CownsBD) { printk("%s: Freeing invalid BD.\n", dev->name); panic_queues(dev); } +#endif /* pass BD to controler */ +#ifndef TC35815_USE_PACKEDBUFFER + if (!lp->rx_skbs[curid].skb) { + lp->rx_skbs[curid].skb = + alloc_rxbuf_skb(dev, + lp->pci_dev, + &lp->rx_skbs[curid].skb_dma); + if (!lp->rx_skbs[curid].skb) + break; /* try on next reception */ + bd->BuffData = cpu_to_le32(lp->rx_skbs[curid].skb_dma); + } +#endif /* TC35815_USE_PACKEDBUFFER */ /* Note: BDLength was modified by chip. */ - lp->fbl_ptr->bd[lp->fbl_curid].BDCtl = - cpu_to_le32(BD_CownsBD | - (lp->fbl_curid << BD_RxBDID_SHIFT) | - PAGE_SIZE); - lp->fbl_curid = - (lp->fbl_curid + 1) % RX_BUF_PAGES; - if (tc35815_debug > 2) { + bd->BDCtl = cpu_to_le32(BD_CownsBD | + (curid << BD_RxBDID_SHIFT) | + RX_BUF_SIZE); +#ifdef TC35815_USE_PACKEDBUFFER + lp->fbl_curid = (curid + 1) % RX_BUF_NUM; + if (netif_msg_rx_status(lp)) { printk("%s: Entering new FBD %d\n", dev->name, lp->fbl_curid); dump_frfd(lp->fbl_ptr); } +#else + lp->fbl_count++; +#endif buf_free_count++; } } /* put RxFD back to controller */ - next_rfd = bus_to_virt(le32_to_cpu(lp->rfd_cur->fd.FDNext)); -#ifdef __mips__ - next_rfd = (struct RxFD *)vtonocache(next_rfd); -#endif +#ifdef DEBUG + next_rfd = fd_bus_to_virt(lp, + le32_to_cpu(lp->rfd_cur->fd.FDNext)); if (next_rfd < lp->rfd_base || next_rfd > lp->rfd_limit) { printk("%s: RxFD FDNext invalid.\n", dev->name); panic_queues(dev); } +#endif for (i = 0; i < (bd_count + 1) / 2 + 1; i++) { /* pass FD to controler */ - lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead); /* for debug */ +#ifdef DEBUG + lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead); +#else + lp->rfd_cur->fd.FDNext = cpu_to_le32(FD_Next_EOL); +#endif lp->rfd_cur->fd.FDCtl = cpu_to_le32(FD_CownsFD); lp->rfd_cur++; fd_free_count++; } - - lp->rfd_cur = next_rfd; + if (lp->rfd_cur > lp->rfd_limit) + lp->rfd_cur = lp->rfd_base; +#ifdef DEBUG + if (lp->rfd_cur != next_rfd) + printk("rfd_cur = %p, next_rfd %p\n", + lp->rfd_cur, next_rfd); +#endif } /* re-enable BL/FDA Exhaust interrupts. */ if (fd_free_count) { - tc_writel(tc_readl(&tr->Int_En) | Int_FDAExEn, &tr->Int_En); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + u32 en, en_old = tc_readl(&tr->Int_En); + en = en_old | Int_FDAExEn; if (buf_free_count) - tc_writel(tc_readl(&tr->Int_En) | Int_BLExEn, &tr->Int_En); + en |= Int_BLExEn; + if (en != en_old) + tc_writel(en, &tr->Int_En); } +#ifdef TC35815_NAPI + return received; +#endif } +#ifdef TC35815_NAPI +static int +tc35815_poll(struct net_device *dev, int *budget) +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int limit = min(*budget, dev->quota); + int received = 0, handled; + u32 status; + + spin_lock(&lp->lock); + status = tc_readl(&tr->Int_Src); + do { + tc_writel(status, &tr->Int_Src); /* write to clear */ + + handled = tc35815_do_interrupt(dev, status, limit); + if (handled >= 0) { + received += handled; + limit -= handled; + if (limit <= 0) + break; + } + status = tc_readl(&tr->Int_Src); + } while (status); + spin_unlock(&lp->lock); + + dev->quota -= received; + *budget -= received; + if (limit <= 0) + return 1; + + netif_rx_complete(dev); + /* enable interrupts */ + tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl); + return 0; +} +#endif + #ifdef NO_CHECK_CARRIER #define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_LateColl|Tx_TxPar|Tx_SQErr) #else @@ -1265,9 +1743,17 @@ tc35815_check_tx_stat(struct net_device if (status & Tx_TxColl_MASK) lp->stats.collisions += status & Tx_TxColl_MASK; +#ifndef NO_CHECK_CARRIER + /* TX4939 does not have NCarr */ + if (lp->boardtype == TC35815_TX4939) + status &= ~Tx_NCarr; +#ifdef WORKAROUND_LOSTCAR /* WORKAROUND: ignore LostCrS in full duplex operation */ - if (lp->fullduplex) + if ((lp->timer_state != asleep && lp->timer_state != lcheck) + || lp->fullduplex) status &= ~Tx_NCarr; +#endif +#endif if (!(status & TX_STA_ERR)) { /* no error. */ @@ -1283,6 +1769,15 @@ tc35815_check_tx_stat(struct net_device if (status & Tx_Under) { lp->stats.tx_fifo_errors++; msg = "Tx FIFO Underrun."; + if (lp->lstats.tx_underrun < TX_THRESHOLD_KEEP_LIMIT) { + lp->lstats.tx_underrun++; + if (lp->lstats.tx_underrun >= TX_THRESHOLD_KEEP_LIMIT) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + tc_writel(TX_THRESHOLD_MAX, &tr->TxThrsh); + msg = "Tx FIFO Underrun.Change Tx threshold to max."; + } + } } if (status & Tx_Defer) { lp->stats.tx_fifo_errors++; @@ -1306,18 +1801,19 @@ #endif lp->stats.tx_heartbeat_errors++; msg = "Signal Quality Error."; } - if (msg) + if (msg && netif_msg_tx_err(lp)) printk(KERN_WARNING "%s: %s (%#x)\n", dev->name, msg, status); } +/* This handles TX complete events posted by the device + * via interrupts. + */ static void tc35815_txdone(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; struct TxFD *txfd; unsigned int fdctl; - int num_done = 0; txfd = &lp->tfd_base[lp->tfd_end]; while (lp->tfd_start != lp->tfd_end && @@ -1325,38 +1821,61 @@ tc35815_txdone(struct net_device *dev) int status = le32_to_cpu(txfd->fd.FDStat); struct sk_buff *skb; unsigned long fdnext = le32_to_cpu(txfd->fd.FDNext); + u32 fdsystem = le32_to_cpu(txfd->fd.FDSystem); - if (tc35815_debug > 2) { + if (netif_msg_tx_done(lp)) { printk("%s: complete TxFD.\n", dev->name); dump_txfd(txfd); } tc35815_check_tx_stat(dev, status); - skb = (struct sk_buff *)le32_to_cpu(txfd->fd.FDSystem); + skb = fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[lp->tfd_end].skb != skb) { + printk("%s: tx_skbs mismatch.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[lp->tfd_end].skb != skb); +#endif if (skb) { + lp->stats.tx_bytes += skb->len; + pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[lp->tfd_end].skb = NULL; + lp->tx_skbs[lp->tfd_end].skb_dma = 0; +#ifdef TC35815_NAPI dev_kfree_skb_any(skb); +#else + dev_kfree_skb_irq(skb); +#endif } - txfd->fd.FDSystem = cpu_to_le32(0); + txfd->fd.FDSystem = cpu_to_le32(0xffffffff); - num_done++; lp->tfd_end = (lp->tfd_end + 1) % TX_FD_NUM; txfd = &lp->tfd_base[lp->tfd_end]; - if ((fdnext & ~FD_Next_EOL) != virt_to_bus(txfd)) { +#ifdef DEBUG + if ((fdnext & ~FD_Next_EOL) != fd_virt_to_bus(lp, txfd)) { printk("%s: TxFD FDNext invalid.\n", dev->name); panic_queues(dev); } +#endif if (fdnext & FD_Next_EOL) { /* DMA Transmitter has been stopping... */ if (lp->tfd_end != lp->tfd_start) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; int head = (lp->tfd_start + TX_FD_NUM - 1) % TX_FD_NUM; struct TxFD* txhead = &lp->tfd_base[head]; int qlen = (lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM; +#ifdef DEBUG if (!(le32_to_cpu(txfd->fd.FDCtl) & FD_CownsFD)) { printk("%s: TxFD FDCtl invalid.\n", dev->name); panic_queues(dev); } +#endif /* log max queue length */ if (lp->lstats.max_tx_qlen < qlen) lp->lstats.max_tx_qlen = qlen; @@ -1367,21 +1886,23 @@ tc35815_txdone(struct net_device *dev) #ifdef GATHER_TXINT txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); #endif - if (tc35815_debug > 2) { + if (netif_msg_tx_queued(lp)) { printk("%s: start TxFD on queue.\n", dev->name); dump_txfd(txfd); } - tc_writel(virt_to_bus(txfd), &tr->TxFrmPtr); + tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); } break; } } - if (num_done > 0 && lp->tbusy) { - lp->tbusy = 0; - netif_start_queue(dev); - } + /* If we had stopped the queue due to a "tx full" + * condition, and space has now been made available, + * wake up the queue. + */ + if (netif_queue_stopped(dev) && ! tc35815_tx_full(dev)) + netif_wake_queue(dev); } /* The inverse routine to tc35815_open(). */ @@ -1389,18 +1910,18 @@ static int tc35815_close(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - - lp->tbusy = 1; netif_stop_queue(dev); /* Flush the Tx and disable Rx here. */ + del_timer(&lp->timer); /* Kill if running */ tc35815_chip_reset(dev); free_irq(dev->irq, dev); tc35815_free_queues(dev); return 0; + } /* @@ -1410,29 +1931,29 @@ tc35815_close(struct net_device *dev) static struct net_device_stats *tc35815_get_stats(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; - unsigned long flags; - + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; if (netif_running(dev)) { - spin_lock_irqsave(&lp->lock, flags); /* Update the statistics from the device registers. */ lp->stats.rx_missed_errors = tc_readl(&tr->Miss_Cnt); - spin_unlock_irqrestore(&lp->lock, flags); } return &lp->stats; } -static void tc35815_set_cam_entry(struct tc35815_regs *tr, int index, unsigned char *addr) +static void tc35815_set_cam_entry(struct net_device *dev, int index, unsigned char *addr) { + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; int cam_index = index * 6; - unsigned long cam_data; - unsigned long saved_addr; + u32 cam_data; + u32 saved_addr; saved_addr = tc_readl(&tr->CAM_Adr); - if (tc35815_debug > 1) { + if (netif_msg_hw(lp)) { int i; - printk(KERN_DEBUG "%s: CAM %d:", cardname, index); + printk(KERN_DEBUG "%s: CAM %d:", dev->name, index); for (i = 0; i < 6; i++) printk(" %02x", addr[i]); printk("\n"); @@ -1459,14 +1980,6 @@ static void tc35815_set_cam_entry(struct tc_writel(cam_data, &tr->CAM_Data); } - if (tc35815_debug > 2) { - int i; - for (i = cam_index / 4; i < cam_index / 4 + 2; i++) { - tc_writel(i * 4, &tr->CAM_Adr); - printk("CAM 0x%x: %08lx", - i * 4, tc_readl(&tr->CAM_Data)); - } - } tc_writel(saved_addr, &tr->CAM_Adr); } @@ -1481,10 +1994,19 @@ static void tc35815_set_cam_entry(struct static void tc35815_set_multicast_list(struct net_device *dev) { - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; if (dev->flags&IFF_PROMISC) { +#ifdef WORKAROUND_100HALF_PROMISC + /* With some (all?) 100MHalf HUB, controller will hang + * if we enabled promiscuous mode before linkup... */ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + if (!(tc_mdio_read(dev, pid, MII_BMSR) & BMSR_LSTATUS)) + return; +#endif /* Enable promiscuous mode */ tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl); } @@ -1506,7 +2028,7 @@ tc35815_set_multicast_list(struct net_de if (!cur_addr) break; /* entry 0,1 is reserved. */ - tc35815_set_cam_entry(tr, i + 2, cur_addr->dmi_addr); + tc35815_set_cam_entry(dev, i + 2, cur_addr->dmi_addr); ena_bits |= CAM_Ena_Bit(i + 2); } tc_writel(ena_bits, &tr->CAM_Ena); @@ -1518,122 +2040,753 @@ tc35815_set_multicast_list(struct net_de } } -static unsigned long tc_phy_read(struct net_device *dev, struct tc35815_regs *tr, int phy, int phy_reg) +static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct tc35815_local *lp = dev->priv; - unsigned long data; - unsigned long flags; + strcpy(info->driver, MODNAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(lp->pci_dev)); +} - spin_lock_irqsave(&lp->lock, flags); +static int tc35815_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct tc35815_local *lp = dev->priv; + spin_lock_irq(&lp->lock); + mii_ethtool_gset(&lp->mii, cmd); + spin_unlock_irq(&lp->lock); + return 0; +} - tc_writel(MD_CA_Busy | (phy << 5) | phy_reg, &tr->MD_CA); +static int tc35815_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct tc35815_local *lp = dev->priv; + int rc; +#if 1 /* use our negotiation method... */ + /* Verify the settings we care about. */ + if (cmd->autoneg != AUTONEG_ENABLE && + cmd->autoneg != AUTONEG_DISABLE) + return -EINVAL; + if (cmd->autoneg == AUTONEG_DISABLE && + ((cmd->speed != SPEED_100 && + cmd->speed != SPEED_10) || + (cmd->duplex != DUPLEX_HALF && + cmd->duplex != DUPLEX_FULL))) + return -EINVAL; + + /* Ok, do it to it. */ + spin_lock_irq(&lp->lock); + del_timer(&lp->timer); + tc35815_start_auto_negotiation(dev, cmd); + spin_unlock_irq(&lp->lock); + rc = 0; +#else + spin_lock_irq(&lp->lock); + rc = mii_ethtool_sset(&lp->mii, cmd); + spin_unlock_irq(&lp->lock); +#endif + return rc; +} + +static int tc35815_nway_reset(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int rc; + spin_lock_irq(&lp->lock); + rc = mii_nway_restart(&lp->mii); + spin_unlock_irq(&lp->lock); + return rc; +} + +static u32 tc35815_get_link(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int rc; + spin_lock_irq(&lp->lock); + rc = mii_link_ok(&lp->mii); + spin_unlock_irq(&lp->lock); + return rc; +} + +static u32 tc35815_get_msglevel(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + return lp->msg_enable; +} + +static void tc35815_set_msglevel(struct net_device *dev, u32 datum) +{ + struct tc35815_local *lp = dev->priv; + lp->msg_enable = datum; +} + +static int tc35815_get_stats_count(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + return sizeof(lp->lstats) / sizeof(int); +} + +static void tc35815_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) +{ + struct tc35815_local *lp = dev->priv; + data[0] = lp->lstats.max_tx_qlen; + data[1] = lp->lstats.tx_ints; + data[2] = lp->lstats.rx_ints; + data[3] = lp->lstats.tx_underrun; +} + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "max_tx_qlen" }, + { "tx_ints" }, + { "rx_ints" }, + { "tx_underrun" }, +}; + +static void tc35815_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +static const struct ethtool_ops tc35815_ethtool_ops = { + .get_drvinfo = tc35815_get_drvinfo, + .get_settings = tc35815_get_settings, + .set_settings = tc35815_set_settings, + .nway_reset = tc35815_nway_reset, + .get_link = tc35815_get_link, + .get_msglevel = tc35815_get_msglevel, + .set_msglevel = tc35815_set_msglevel, + .get_strings = tc35815_get_strings, + .get_stats_count = tc35815_get_stats_count, + .get_ethtool_stats = tc35815_get_ethtool_stats, + .get_perm_addr = ethtool_op_get_perm_addr, +}; + +static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct tc35815_local *lp = dev->priv; + int rc; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&lp->lock); + rc = generic_mii_ioctl(&lp->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&lp->lock); + + return rc; +} + +static int tc_mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + u32 data; + tc_writel(MD_CA_Busy | (phy_id << 5) | location, &tr->MD_CA); while (tc_readl(&tr->MD_CA) & MD_CA_Busy) ; data = tc_readl(&tr->MD_Data); - spin_unlock_irqrestore(&lp->lock, flags); - return data; + return data & 0xffff; +} + +static void tc_mdio_write(struct net_device *dev, int phy_id, int location, + int val) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + tc_writel(val, &tr->MD_Data); + tc_writel(MD_CA_Busy | MD_CA_Wr | (phy_id << 5) | location, &tr->MD_CA); + while (tc_readl(&tr->MD_CA) & MD_CA_Busy) + ; } -static void tc_phy_write(struct net_device *dev, unsigned long d, struct tc35815_regs *tr, int phy, int phy_reg) +/* Auto negotiation. The scheme is very simple. We have a timer routine + * that keeps watching the auto negotiation process as it progresses. + * The DP83840 is first told to start doing it's thing, we set up the time + * and place the timer state machine in it's initial state. + * + * Here the timer peeks at the DP83840 status registers at each click to see + * if the auto negotiation has completed, we assume here that the DP83840 PHY + * will time out at some point and just tell us what (didn't) happen. For + * complete coverage we only allow so many of the ticks at this level to run, + * when this has expired we print a warning message and try another strategy. + * This "other" strategy is to force the interface into various speed/duplex + * configurations and we stop when we see a link-up condition before the + * maximum number of "peek" ticks have occurred. + * + * Once a valid link status has been detected we configure the BigMAC and + * the rest of the Happy Meal to speak the most efficient protocol we could + * get a clean link for. The priority for link configurations, highest first + * is: + * 100 Base-T Full Duplex + * 100 Base-T Half Duplex + * 10 Base-T Full Duplex + * 10 Base-T Half Duplex + * + * We start a new timer now, after a successful auto negotiation status has + * been detected. This timer just waits for the link-up bit to get set in + * the BMCR of the DP83840. When this occurs we print a kernel log message + * describing the link type in use and the fact that it is up. + * + * If a fatal error of some sort is signalled and detected in the interrupt + * service routine, and the chip is reset, or the link is ifconfig'd down + * and then back up, this entire process repeats itself all over again. + */ +/* Note: Above comments are come from sunhme driver. */ + +static int tc35815_try_next_permutation(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - unsigned long flags; + int pid = lp->phy_addr; + unsigned short bmcr; - spin_lock_irqsave(&lp->lock, flags); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); - tc_writel(d, &tr->MD_Data); - tc_writel(MD_CA_Busy | MD_CA_Wr | (phy << 5) | phy_reg, &tr->MD_CA); - while (tc_readl(&tr->MD_CA) & MD_CA_Busy) - ; - spin_unlock_irqrestore(&lp->lock, flags); + /* Downgrade from full to half duplex. Only possible via ethtool. */ + if (bmcr & BMCR_FULLDPLX) { + bmcr &= ~BMCR_FULLDPLX; + printk(KERN_DEBUG "%s: try next permutation (BMCR %x)\n", dev->name, bmcr); + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + return 0; + } + + /* Downgrade from 100 to 10. */ + if (bmcr & BMCR_SPEED100) { + bmcr &= ~BMCR_SPEED100; + printk(KERN_DEBUG "%s: try next permutation (BMCR %x)\n", dev->name, bmcr); + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + return 0; + } + + /* We've tried everything. */ + return -1; } -static void tc35815_phy_chip_init(struct net_device *dev) +static void +tc35815_display_link_mode(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; - static int first = 1; - unsigned short ctl; - - if (first) { - unsigned short id0, id1; - int count; - first = 0; - - /* first data written to the PHY will be an ID number */ - tc_phy_write(dev, 0, tr, 0, MII_CONTROL); /* ID:0 */ -#if 0 - tc_phy_write(dev, MIICNTL_RESET, tr, 0, MII_CONTROL); - printk(KERN_INFO "%s: Resetting PHY...", dev->name); - while (tc_phy_read(dev, tr, 0, MII_CONTROL) & MIICNTL_RESET) - ; - printk("\n"); - tc_phy_write(dev, MIICNTL_AUTO|MIICNTL_SPEED|MIICNTL_FDX, tr, 0, - MII_CONTROL); -#endif - id0 = tc_phy_read(dev, tr, 0, MII_PHY_ID0); - id1 = tc_phy_read(dev, tr, 0, MII_PHY_ID1); - printk(KERN_DEBUG "%s: PHY ID %04x %04x\n", dev->name, - id0, id1); - if (lp->option & TC35815_OPT_10M) { - lp->linkspeed = 10; - lp->fullduplex = (lp->option & TC35815_OPT_FULLDUP) != 0; - } else if (lp->option & TC35815_OPT_100M) { - lp->linkspeed = 100; - lp->fullduplex = (lp->option & TC35815_OPT_FULLDUP) != 0; + int pid = lp->phy_addr; + unsigned short lpa, bmcr; + char *speed = "", *duplex = ""; + + lpa = tc_mdio_read(dev, pid, MII_LPA); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (options.speed ? (bmcr & BMCR_SPEED100) : (lpa & (LPA_100HALF | LPA_100FULL))) + speed = "100Mb/s"; + else + speed = "10Mb/s"; + if (options.duplex ? (bmcr & BMCR_FULLDPLX) : (lpa & (LPA_100FULL | LPA_10FULL))) + duplex = "Full Duplex"; + else + duplex = "Half Duplex"; + + if (netif_msg_link(lp)) + printk(KERN_INFO "%s: Link is up at %s, %s.\n", + dev->name, speed, duplex); + printk(KERN_DEBUG "%s: MII BMCR %04x BMSR %04x LPA %04x\n", + dev->name, + bmcr, tc_mdio_read(dev, pid, MII_BMSR), lpa); +} + +static void tc35815_display_forced_link_mode(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmcr; + char *speed = "", *duplex = ""; + + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (bmcr & BMCR_SPEED100) + speed = "100Mb/s"; + else + speed = "10Mb/s"; + if (bmcr & BMCR_FULLDPLX) + duplex = "Full Duplex.\n"; + else + duplex = "Half Duplex.\n"; + + if (netif_msg_link(lp)) + printk(KERN_INFO "%s: Link has been forced up at %s, %s", + dev->name, speed, duplex); +} + +static void tc35815_set_link_modes(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int pid = lp->phy_addr; + unsigned short bmcr, lpa; + int speed; + + if (lp->timer_state == arbwait) { + lpa = tc_mdio_read(dev, pid, MII_LPA); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + printk(KERN_DEBUG "%s: MII BMCR %04x BMSR %04x LPA %04x\n", + dev->name, + bmcr, tc_mdio_read(dev, pid, MII_BMSR), lpa); + if (!(lpa & (LPA_10HALF | LPA_10FULL | + LPA_100HALF | LPA_100FULL))) { + /* fall back to 10HALF */ + printk(KERN_INFO "%s: bad ability %04x - falling back to 10HD.\n", + dev->name, lpa); + lpa = LPA_10HALF; + } + if (options.duplex ? (bmcr & BMCR_FULLDPLX) : (lpa & (LPA_100FULL | LPA_10FULL))) + lp->fullduplex = 1; + else + lp->fullduplex = 0; + if (options.speed ? (bmcr & BMCR_SPEED100) : (lpa & (LPA_100HALF | LPA_100FULL))) + speed = 100; + else + speed = 10; + } else { + /* Forcing a link mode. */ + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (bmcr & BMCR_FULLDPLX) + lp->fullduplex = 1; + else + lp->fullduplex = 0; + if (bmcr & BMCR_SPEED100) + speed = 100; + else + speed = 10; + } + + tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_HaltReq, &tr->MAC_Ctl); + if (lp->fullduplex) { + tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_FullDup, &tr->MAC_Ctl); + } else { + tc_writel(tc_readl(&tr->MAC_Ctl) & ~MAC_FullDup, &tr->MAC_Ctl); + } + tc_writel(tc_readl(&tr->MAC_Ctl) & ~MAC_HaltReq, &tr->MAC_Ctl); + + /* TX4939 PCFG.SPEEDn bit will be changed on NETDEV_CHANGE event. */ + +#ifndef NO_CHECK_CARRIER + /* TX4939 does not have EnLCarr */ + if (lp->boardtype != TC35815_TX4939) { +#ifdef WORKAROUND_LOSTCAR + /* WORKAROUND: enable LostCrS only if half duplex operation */ + if (!lp->fullduplex && lp->boardtype != TC35815_TX4939) + tc_writel(tc_readl(&tr->Tx_Ctl) | Tx_EnLCarr, &tr->Tx_Ctl); +#endif + } +#endif + lp->mii.full_duplex = lp->fullduplex; +} + +static void tc35815_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmsr, bmcr, lpa; + int restart_timer = 0; + + spin_lock_irq(&lp->lock); + + lp->timer_ticks++; + switch (lp->timer_state) { + case arbwait: + /* + * Only allow for 5 ticks, thats 10 seconds and much too + * long to wait for arbitration to complete. + */ + /* TC35815 need more times... */ + if (lp->timer_ticks >= 10) { + /* Enter force mode. */ + if (!options.doforce) { + printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful," + " cable probblem?\n", dev->name); + /* Try to restart the adaptor. */ + tc35815_restart(dev); + goto out; + } + printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful," + " trying force link mode\n", dev->name); + printk(KERN_DEBUG "%s: BMCR %x BMSR %x\n", dev->name, + tc_mdio_read(dev, pid, MII_BMCR), + tc_mdio_read(dev, pid, MII_BMSR)); + bmcr = BMCR_SPEED100; + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + + /* + * OK, seems we need do disable the transceiver + * for the first tick to make sure we get an + * accurate link state at the second tick. + */ + + lp->timer_state = ltrywait; + lp->timer_ticks = 0; + restart_timer = 1; } else { - /* auto negotiation */ - unsigned long neg_result; - tc_phy_write(dev, MIICNTL_AUTO | MIICNTL_RST_AUTO, tr, 0, MII_CONTROL); - printk(KERN_INFO "%s: Auto Negotiation...", dev->name); - count = 0; - while (!(tc_phy_read(dev, tr, 0, MII_STATUS) & MIISTAT_AUTO_DONE)) { - if (count++ > 5000) { - printk(" failed. Assume 10Mbps\n"); - lp->linkspeed = 10; - lp->fullduplex = 0; - goto done; + /* Anything interesting happen? */ + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + if (bmsr & BMSR_ANEGCOMPLETE) { + /* Just what we've been waiting for... */ + tc35815_set_link_modes(dev); + + /* + * Success, at least so far, advance our state + * engine. + */ + lp->timer_state = lupwait; + restart_timer = 1; + } else { + restart_timer = 1; + } + } + break; + + case lupwait: + /* + * Auto negotiation was successful and we are awaiting a + * link up status. I have decided to let this timer run + * forever until some sort of error is signalled, reporting + * a message to the user at 10 second intervals. + */ + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + if (bmsr & BMSR_LSTATUS) { + /* + * Wheee, it's up, display the link mode in use and put + * the timer to sleep. + */ + tc35815_display_link_mode(dev); + netif_carrier_on(dev); +#ifdef WORKAROUND_100HALF_PROMISC + /* delayed promiscuous enabling */ + if (dev->flags & IFF_PROMISC) + tc35815_set_multicast_list(dev); +#endif +#if 1 + lp->saved_lpa = tc_mdio_read(dev, pid, MII_LPA); + lp->timer_state = lcheck; + restart_timer = 1; +#else + lp->timer_state = asleep; + restart_timer = 0; +#endif + } else { + if (lp->timer_ticks >= 10) { + printk(KERN_NOTICE "%s: Auto negotiation successful, link still " + "not completely up.\n", dev->name); + lp->timer_ticks = 0; + restart_timer = 1; + } else { + restart_timer = 1; + } + } + break; + + case ltrywait: + /* + * Making the timeout here too long can make it take + * annoyingly long to attempt all of the link mode + * permutations, but then again this is essentially + * error recovery code for the most part. + */ + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (lp->timer_ticks == 1) { + /* + * Re-enable transceiver, we'll re-enable the + * transceiver next tick, then check link state + * on the following tick. + */ + restart_timer = 1; + break; + } + if (lp->timer_ticks == 2) { + restart_timer = 1; + break; + } + if (bmsr & BMSR_LSTATUS) { + /* Force mode selection success. */ + tc35815_display_forced_link_mode(dev); + netif_carrier_on(dev); + tc35815_set_link_modes(dev); +#ifdef WORKAROUND_100HALF_PROMISC + /* delayed promiscuous enabling */ + if (dev->flags & IFF_PROMISC) + tc35815_set_multicast_list(dev); +#endif +#if 1 + lp->saved_lpa = tc_mdio_read(dev, pid, MII_LPA); + lp->timer_state = lcheck; + restart_timer = 1; +#else + lp->timer_state = asleep; + restart_timer = 0; +#endif + } else { + if (lp->timer_ticks >= 4) { /* 6 seconds or so... */ + int ret; + + ret = tc35815_try_next_permutation(dev); + if (ret == -1) { + /* + * Aieee, tried them all, reset the + * chip and try all over again. + */ + printk(KERN_NOTICE "%s: Link down, " + "cable problem?\n", + dev->name); + + /* Try to restart the adaptor. */ + tc35815_restart(dev); + goto out; } - if (count % 512 == 0) - printk("."); - mdelay(1); + lp->timer_ticks = 0; + restart_timer = 1; + } else { + restart_timer = 1; + } + } + break; + + case lcheck: + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + lpa = tc_mdio_read(dev, pid, MII_LPA); + if (bmcr & (BMCR_PDOWN | BMCR_ISOLATE | BMCR_RESET)) { + printk(KERN_ERR "%s: PHY down? (BMCR %x)\n", dev->name, + bmcr); + } else if ((lp->saved_lpa ^ lpa) & + (LPA_100FULL|LPA_100HALF|LPA_10FULL|LPA_10HALF)) { + printk(KERN_NOTICE "%s: link status changed" + " (BMCR %x LPA %x->%x)\n", dev->name, + bmcr, lp->saved_lpa, lpa); + } else { + /* go on */ + restart_timer = 1; + break; + } + /* Try to restart the adaptor. */ + tc35815_restart(dev); + goto out; + + case asleep: + default: + /* Can't happens.... */ + printk(KERN_ERR "%s: Aieee, link timer is asleep but we got " + "one anyways!\n", dev->name); + restart_timer = 0; + lp->timer_ticks = 0; + lp->timer_state = asleep; /* foo on you */ + break; + } + + if (restart_timer) { + lp->timer.expires = jiffies + msecs_to_jiffies(1200); + add_timer(&lp->timer); + } +out: + spin_unlock_irq(&lp->lock); +} + +static void tc35815_start_auto_negotiation(struct net_device *dev, + struct ethtool_cmd *ep) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmsr, bmcr, advertize; + int timeout; + + netif_carrier_off(dev); + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + advertize = tc_mdio_read(dev, pid, MII_ADVERTISE); + + if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + if (options.speed || options.duplex) { + /* Advertise only specified configuration. */ + advertize &= ~(ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL); + if (options.speed != 10) { + if (options.duplex != 1) + advertize |= ADVERTISE_100FULL; + if (options.duplex != 2) + advertize |= ADVERTISE_100HALF; + } + if (options.speed != 100) { + if (options.duplex != 1) + advertize |= ADVERTISE_10FULL; + if (options.duplex != 2) + advertize |= ADVERTISE_10HALF; } - printk(" done.\n"); - neg_result = tc_phy_read(dev, tr, 0, MII_ANLPAR); - if (neg_result & (MII_AN_TX_FDX | MII_AN_TX_HDX)) - lp->linkspeed = 100; + if (options.speed == 100) + bmcr |= BMCR_SPEED100; + else if (options.speed == 10) + bmcr &= ~BMCR_SPEED100; + if (options.duplex == 2) + bmcr |= BMCR_FULLDPLX; + else if (options.duplex == 1) + bmcr &= ~BMCR_FULLDPLX; + } else { + /* Advertise everything we can support. */ + if (bmsr & BMSR_10HALF) + advertize |= ADVERTISE_10HALF; else - lp->linkspeed = 10; - if (neg_result & (MII_AN_TX_FDX | MII_AN_10_FDX)) - lp->fullduplex = 1; + advertize &= ~ADVERTISE_10HALF; + if (bmsr & BMSR_10FULL) + advertize |= ADVERTISE_10FULL; else - lp->fullduplex = 0; - done: - ; + advertize &= ~ADVERTISE_10FULL; + if (bmsr & BMSR_100HALF) + advertize |= ADVERTISE_100HALF; + else + advertize &= ~ADVERTISE_100HALF; + if (bmsr & BMSR_100FULL) + advertize |= ADVERTISE_100FULL; + else + advertize &= ~ADVERTISE_100FULL; + } + + tc_mdio_write(dev, pid, MII_ADVERTISE, advertize); + + /* Enable Auto-Negotiation, this is usually on already... */ + bmcr |= BMCR_ANENABLE; + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + + /* Restart it to make sure it is going. */ + bmcr |= BMCR_ANRESTART; + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + printk(KERN_DEBUG "%s: ADVERTISE %x BMCR %x\n", dev->name, advertize, bmcr); + + /* BMCR_ANRESTART self clears when the process has begun. */ + timeout = 64; /* More than enough. */ + while (--timeout) { + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (!(bmcr & BMCR_ANRESTART)) + break; /* got it. */ + udelay(10); } + if (!timeout) { + printk(KERN_ERR "%s: TC35815 would not start auto " + "negotiation BMCR=0x%04x\n", + dev->name, bmcr); + printk(KERN_NOTICE "%s: Performing force link " + "detection.\n", dev->name); + goto force_link; + } else { + printk(KERN_DEBUG "%s: auto negotiation started.\n", dev->name); + lp->timer_state = arbwait; + } + } else { +force_link: + /* Force the link up, trying first a particular mode. + * Either we are here at the request of ethtool or + * because the Happy Meal would not start to autoneg. + */ + + /* Disable auto-negotiation in BMCR, enable the duplex and + * speed setting, init the timer state machine, and fire it off. + */ + if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + bmcr = BMCR_SPEED100; + } else { + if (ep->speed == SPEED_100) + bmcr = BMCR_SPEED100; + else + bmcr = 0; + if (ep->duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + } + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + + /* OK, seems we need do disable the transceiver for the first + * tick to make sure we get an accurate link state at the + * second tick. + */ + lp->timer_state = ltrywait; } - ctl = 0; - if (lp->linkspeed == 100) - ctl |= MIICNTL_SPEED; - if (lp->fullduplex) - ctl |= MIICNTL_FDX; - tc_phy_write(dev, ctl, tr, 0, MII_CONTROL); + del_timer(&lp->timer); + lp->timer_ticks = 0; + lp->timer.expires = jiffies + msecs_to_jiffies(1200); + add_timer(&lp->timer); +} - if (lp->fullduplex) { - tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_FullDup, &tr->MAC_Ctl); +static void tc35815_find_phy(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short id0; + + /* find MII phy */ + for (pid = 31; pid >= 0; pid--) { + id0 = tc_mdio_read(dev, pid, MII_BMSR); + if (id0 != 0xffff && id0 != 0x0000 && + (id0 & BMSR_RESV) != (0xffff & BMSR_RESV) /* paranoia? */ + ) { + lp->phy_addr = pid; + break; + } } + if (pid < 0) { + printk(KERN_ERR "%s: No MII Phy found.\n", + dev->name); + lp->phy_addr = pid = 0; + } + + lp->mii_id[0] = tc_mdio_read(dev, pid, MII_PHYSID1); + lp->mii_id[1] = tc_mdio_read(dev, pid, MII_PHYSID2); + if (netif_msg_hw(lp)) + printk(KERN_INFO "%s: PHY(%02x) ID %04x %04x\n", dev->name, + pid, lp->mii_id[0], lp->mii_id[1]); } -static void tc35815_chip_reset(struct net_device *dev) +static void tc35815_phy_chip_init(struct net_device *dev) { - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmcr; + struct ethtool_cmd ecmd, *ep; + + /* dis-isolate if needed. */ + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (bmcr & BMCR_ISOLATE) { + int count = 32; + printk(KERN_DEBUG "%s: unisolating...", dev->name); + tc_mdio_write(dev, pid, MII_BMCR, bmcr & ~BMCR_ISOLATE); + while (--count) { + if (!(tc_mdio_read(dev, pid, MII_BMCR) & BMCR_ISOLATE)) + break; + udelay(20); + } + printk(" %s.\n", count ? "done" : "failed"); + } + + if (options.speed && options.duplex) { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = options.speed == 10 ? SPEED_10 : SPEED_100; + ecmd.duplex = options.duplex == 1 ? DUPLEX_HALF : DUPLEX_FULL; + ep = &ecmd; + } else { + ep = NULL; + } + tc35815_start_auto_negotiation(dev, ep); +} +static void tc35815_chip_reset(struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int i; /* reset the controller */ tc_writel(MAC_Reset, &tr->MAC_Ctl); - while (tc_readl(&tr->MAC_Ctl) & MAC_Reset) - ; - + udelay(4); /* 3200ns */ + i = 0; + while (tc_readl(&tr->MAC_Ctl) & MAC_Reset) { + if (i++ > 100) { + printk(KERN_ERR "%s: MAC reset failed.\n", dev->name); + break; + } + mdelay(1); + } tc_writel(0, &tr->MAC_Ctl); /* initialize registers to default value */ @@ -1651,90 +2804,142 @@ static void tc35815_chip_reset(struct ne tc_writel(0, &tr->CAM_Ena); (void)tc_readl(&tr->Miss_Cnt); /* Read to clear */ + /* initialize internal SRAM */ + tc_writel(DMA_TestMode, &tr->DMA_Ctl); + for (i = 0; i < 0x1000; i += 4) { + tc_writel(i, &tr->CAM_Adr); + tc_writel(0, &tr->CAM_Data); + } + tc_writel(0, &tr->DMA_Ctl); } static void tc35815_chip_init(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; - unsigned long flags; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; unsigned long txctl = TX_CTL_CMD; tc35815_phy_chip_init(dev); /* load station address to CAM */ - tc35815_set_cam_entry(tr, CAM_ENTRY_SOURCE, dev->dev_addr); + tc35815_set_cam_entry(dev, CAM_ENTRY_SOURCE, dev->dev_addr); /* Enable CAM (broadcast and unicast) */ tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena); tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); - spin_lock_irqsave(&lp->lock, flags); - - tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl); - + /* Use DMA_RxAlign_2 to make IP header 4-byte aligned. */ + if (HAVE_DMA_RXALIGN(lp)) + tc_writel(DMA_BURST_SIZE | DMA_RxAlign_2, &tr->DMA_Ctl); + else + tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl); +#ifdef TC35815_USE_PACKEDBUFFER tc_writel(RxFrag_EnPack | ETH_ZLEN, &tr->RxFragSize); /* Packing */ +#else + tc_writel(ETH_ZLEN, &tr->RxFragSize); +#endif tc_writel(0, &tr->TxPollCtr); /* Batch mode */ tc_writel(TX_THRESHOLD, &tr->TxThrsh); tc_writel(INT_EN_CMD, &tr->Int_En); /* set queues */ - tc_writel(virt_to_bus(lp->rfd_base), &tr->FDA_Bas); + tc_writel(fd_virt_to_bus(lp, lp->rfd_base), &tr->FDA_Bas); tc_writel((unsigned long)lp->rfd_limit - (unsigned long)lp->rfd_base, &tr->FDA_Lim); /* * Activation method: - * First, enable eht MAC Transmitter and the DMA Receive circuits. + * First, enable the MAC Transmitter and the DMA Receive circuits. * Then enable the DMA Transmitter and the MAC Receive circuits. */ - tc_writel(virt_to_bus(lp->fbl_ptr), &tr->BLFrmPtr); /* start DMA receiver */ + tc_writel(fd_virt_to_bus(lp, lp->fbl_ptr), &tr->BLFrmPtr); /* start DMA receiver */ tc_writel(RX_CTL_CMD, &tr->Rx_Ctl); /* start MAC receiver */ + /* start MAC transmitter */ +#ifndef NO_CHECK_CARRIER + /* TX4939 does not have EnLCarr */ + if (lp->boardtype == TC35815_TX4939) + txctl &= ~Tx_EnLCarr; +#ifdef WORKAROUND_LOSTCAR /* WORKAROUND: ignore LostCrS in full duplex operation */ - if (lp->fullduplex) - txctl = TX_CTL_CMD & ~Tx_EnLCarr; + if ((lp->timer_state != asleep && lp->timer_state != lcheck) || + lp->fullduplex) + txctl &= ~Tx_EnLCarr; +#endif +#endif /* !NO_CHECK_CARRIER */ #ifdef GATHER_TXINT txctl &= ~Tx_EnComp; /* disable global tx completion int. */ #endif tc_writel(txctl, &tr->Tx_Ctl); -#if 0 /* No need to polling */ - tc_writel(virt_to_bus(lp->tfd_base), &tr->TxFrmPtr); /* start DMA transmitter */ -#endif +} + +#ifdef CONFIG_PM +static int tc35815_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tc35815_local *lp = dev->priv; + unsigned long flags; + + pci_save_state(pdev); + if (!netif_running(dev)) + return 0; + netif_device_detach(dev); + spin_lock_irqsave(&lp->lock, flags); + del_timer(&lp->timer); /* Kill if running */ + tc35815_chip_reset(dev); spin_unlock_irqrestore(&lp->lock, flags); + pci_set_power_state(pdev, PCI_D3hot); + return 0; } -static struct pci_driver tc35815_driver = { - .name = TC35815_MODULE_NAME, - .probe = tc35815_probe, - .remove = NULL, - .id_table = tc35815_pci_tbl, +static int tc35815_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tc35815_local *lp = dev->priv; + unsigned long flags; + + pci_restore_state(pdev); + if (!netif_running(dev)) + return 0; + pci_set_power_state(pdev, PCI_D0); + spin_lock_irqsave(&lp->lock, flags); + tc35815_restart(dev); + spin_unlock_irqrestore(&lp->lock, flags); + netif_device_attach(dev); + return 0; +} +#endif /* CONFIG_PM */ + +static struct pci_driver tc35815_pci_driver = { + .name = MODNAME, + .id_table = tc35815_pci_tbl, + .probe = tc35815_init_one, + .remove = __devexit_p(tc35815_remove_one), +#ifdef CONFIG_PM + .suspend = tc35815_suspend, + .resume = tc35815_resume, +#endif }; +module_param_named(speed, options.speed, int, 0); +MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps"); +module_param_named(duplex, options.duplex, int, 0); +MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full"); +module_param_named(doforce, options.doforce, int, 0); +MODULE_PARM_DESC(doforce, "try force link mode if auto-negotiation failed"); + static int __init tc35815_init_module(void) { - return pci_register_driver(&tc35815_driver); + return pci_register_driver(&tc35815_pci_driver); } static void __exit tc35815_cleanup_module(void) { - struct net_device *next_dev; - - /* - * TODO: implement a tc35815_driver.remove hook, and - * move this code into that function. Then, delete - * all root_tc35815_dev list handling code. - */ - while (root_tc35815_dev) { - struct net_device *dev = root_tc35815_dev; - next_dev = ((struct tc35815_local *)dev->priv)->next_module; - iounmap((void *)(dev->base_addr)); - unregister_netdev(dev); - free_netdev(dev); - root_tc35815_dev = next_dev; - } - - pci_unregister_driver(&tc35815_driver); + pci_unregister_driver(&tc35815_pci_driver); } module_init(tc35815_init_module); module_exit(tc35815_cleanup_module); + +MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 256969e..9488f49 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -40,16 +40,16 @@ #include #include #include +#include #include #include #include #include -#ifdef CONFIG_SPARC64 +#ifdef CONFIG_SPARC #include -#include -#include +#include #endif #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) @@ -3349,7 +3349,7 @@ static int tg3_rx(struct tg3 *tp, int bu skb_reserve(copy_skb, 2); skb_put(copy_skb, len); pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); pci_dma_sync_single_for_device(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ @@ -3908,20 +3908,20 @@ static int tg3_start_xmit(struct sk_buff if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) mss |= (skb_headlen(skb) - ETH_HLEN) << 9; else { - tcp_opt_len = ((skb->h.th->doff - 5) * 4); - ip_tcp_len = (skb->nh.iph->ihl * 4) + - sizeof(struct tcphdr); + struct iphdr *iph = ip_hdr(skb); + + tcp_opt_len = tcp_optlen(skb); + ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); - skb->nh.iph->check = 0; - skb->nh.iph->tot_len = htons(mss + ip_tcp_len + - tcp_opt_len); + iph->check = 0; + iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); mss |= (ip_tcp_len + tcp_opt_len) << 9; } base_flags |= (TXD_FLAG_CPU_PRE_DMA | TXD_FLAG_CPU_POST_DMA); - skb->h.th->check = 0; + tcp_hdr(skb)->check = 0; } else if (skb->ip_summed == CHECKSUM_PARTIAL) @@ -4055,6 +4055,7 @@ static int tg3_start_xmit_dma_bug(struct mss = 0; if (skb->len > (tp->dev->mtu + ETH_HLEN) && (mss = skb_shinfo(skb)->gso_size) != 0) { + struct iphdr *iph; int tcp_opt_len, ip_tcp_len, hdr_len; if (skb_header_cloned(skb) && @@ -4063,8 +4064,8 @@ static int tg3_start_xmit_dma_bug(struct goto out_unlock; } - tcp_opt_len = ((skb->h.th->doff - 5) * 4); - ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); + tcp_opt_len = tcp_optlen(skb); + ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); hdr_len = ip_tcp_len + tcp_opt_len; if (unlikely((ETH_HLEN + hdr_len) > 80) && @@ -4074,34 +4075,31 @@ static int tg3_start_xmit_dma_bug(struct base_flags |= (TXD_FLAG_CPU_PRE_DMA | TXD_FLAG_CPU_POST_DMA); - skb->nh.iph->check = 0; - skb->nh.iph->tot_len = htons(mss + hdr_len); + iph = ip_hdr(skb); + iph->check = 0; + iph->tot_len = htons(mss + hdr_len); if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { - skb->h.th->check = 0; + tcp_hdr(skb)->check = 0; base_flags &= ~TXD_FLAG_TCPUDP_CSUM; - } - else { - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, IPPROTO_TCP, 0); - } + } else + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, + 0); if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) { - if (tcp_opt_len || skb->nh.iph->ihl > 5) { + if (tcp_opt_len || iph->ihl > 5) { int tsflags; - tsflags = ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)); + tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2); mss |= (tsflags << 11); } } else { - if (tcp_opt_len || skb->nh.iph->ihl > 5) { + if (tcp_opt_len || iph->ihl > 5) { int tsflags; - tsflags = ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)); + tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2); base_flags |= tsflags << 12; } } @@ -10988,24 +10986,20 @@ static int __devinit tg3_get_invariants( return err; } -#ifdef CONFIG_SPARC64 +#ifdef CONFIG_SPARC static int __devinit tg3_get_macaddr_sparc(struct tg3 *tp) { struct net_device *dev = tp->dev; struct pci_dev *pdev = tp->pdev; - struct pcidev_cookie *pcp = pdev->sysdata; - - if (pcp != NULL) { - unsigned char *addr; - int len; - - addr = of_get_property(pcp->prom_node, "local-mac-address", - &len); - if (addr && len == 6) { - memcpy(dev->dev_addr, addr, 6); - memcpy(dev->perm_addr, dev->dev_addr, 6); - return 0; - } + struct device_node *dp = pci_device_to_OF_node(pdev); + const unsigned char *addr; + int len; + + addr = of_get_property(dp, "local-mac-address", &len); + if (addr && len == 6) { + memcpy(dev->dev_addr, addr, 6); + memcpy(dev->perm_addr, dev->dev_addr, 6); + return 0; } return -ENODEV; } @@ -11026,7 +11020,7 @@ static int __devinit tg3_get_device_addr u32 hi, lo, mac_offset; int addr_ok = 0; -#ifdef CONFIG_SPARC64 +#ifdef CONFIG_SPARC if (!tg3_get_macaddr_sparc(tp)) return 0; #endif diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index f85f002..106dc1e 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -1112,7 +1112,7 @@ static int TLan_StartTx( struct sk_buff if ( bbuf ) { tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); - memcpy( tail_buffer, skb->data, skb->len ); + skb_copy_from_linear_data(skb, tail_buffer, skb->len); } else { tail_list->buffer[0].address = pci_map_single(priv->pciDev, skb->data, skb->len, PCI_DMA_TODEVICE); TLan_StoreSKB(tail_list, skb); @@ -1577,7 +1577,6 @@ u32 TLan_HandleRxEOF( struct net_device printk(KERN_INFO "TLAN: Couldn't allocate memory for received data.\n"); else { head_buffer = priv->rxBuffer + (priv->rxHead * TLAN_MAX_FRAME_SIZE); - skb->dev = dev; skb_reserve(skb, 2); t = (void *) skb_put(skb, frameSize); @@ -1608,7 +1607,6 @@ u32 TLan_HandleRxEOF( struct net_device skb->protocol = eth_type_trans( skb, dev ); netif_rx( skb ); - new_skb->dev = dev; skb_reserve( new_skb, 2 ); t = (void *) skb_put( new_skb, TLAN_MAX_FRAME_SIZE ); head_list->buffer[0].address = pci_map_single(priv->pciDev, new_skb->data, TLAN_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c index 7580bde..e22a3f5 100644 --- a/drivers/net/tokenring/3c359.c +++ b/drivers/net/tokenring/3c359.c @@ -933,20 +933,21 @@ static void xl_rx(struct net_device *dev return ; } - skb->dev = dev ; - while (xl_priv->rx_ring_tail != temp_ring_loc) { copy_len = xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfraglen & 0x7FFF ; frame_length -= copy_len ; pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; - memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, copy_len) ; + skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail], + skb_put(skb, copy_len), + copy_len); pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; adv_rx_ring(dev) ; } /* Now we have found the last fragment */ pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; - memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, frame_length) ; + skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail], + skb_put(skb,copy_len), frame_length); /* memcpy(skb_put(skb,frame_length), bus_to_virt(xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr), frame_length) ; */ pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; adv_rx_ring(dev) ; @@ -967,8 +968,6 @@ static void xl_rx(struct net_device *dev return ; } - skb->dev = dev ; - skb2 = xl_priv->rx_ring_skb[xl_priv->rx_ring_tail] ; pci_unmap_single(xl_priv->pdev, xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr, xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; skb_put(skb2, frame_length) ; diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 01d5531..1e8958e 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -1771,7 +1771,6 @@ #endif /*BMS again, if she comes in with few but leaves with many */ skb_reserve(skb, sizeof(struct trh_hdr) - lan_hdr_len); skb_put(skb, length); - skb->dev = dev; data = skb->data; rbuffer_len = ntohs(readw(rbuf + offsetof(struct rec_buf, buf_len))); rbufdata = rbuf + offsetof(struct rec_buf, data); diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c index e999feb..5d849c0 100644 --- a/drivers/net/tokenring/lanstreamer.c +++ b/drivers/net/tokenring/lanstreamer.c @@ -944,8 +944,6 @@ static void streamer_rx(struct net_devic printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n", dev->name); streamer_priv->streamer_stats.rx_dropped++; } else { /* we allocated an skb OK */ - skb->dev = dev; - if (buffer_cnt == 1) { /* release the DMA mapping */ pci_unmap_single(streamer_priv->pci_dev, @@ -1607,10 +1605,11 @@ #endif frame_data, buffer_len); } while (next_ptr && (buff_off = next_ptr)); + mac_frame->protocol = tr_type_trans(mac_frame, dev); #if STREAMER_NETWORK_MONITOR printk(KERN_WARNING "%s: Received MAC Frame, details: \n", dev->name); - mac_hdr = (struct trh_hdr *) mac_frame->data; + mac_hdr = tr_hdr(mac_frame); printk(KERN_WARNING "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1], @@ -1622,8 +1621,6 @@ #if STREAMER_NETWORK_MONITOR mac_hdr->saddr[2], mac_hdr->saddr[3], mac_hdr->saddr[4], mac_hdr->saddr[5]); #endif - mac_frame->dev = dev; - mac_frame->protocol = tr_type_trans(mac_frame, dev); netif_rx(mac_frame); /* Now tell the card we have dealt with the received frame */ diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c index 8f4ecc1..09b3cfb 100644 --- a/drivers/net/tokenring/olympic.c +++ b/drivers/net/tokenring/olympic.c @@ -814,8 +814,6 @@ #endif olympic_priv->rx_ring_last_received += i ; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1) ; } else { - skb->dev = dev ; - /* Optimise based upon number of buffers used. If only one buffer is used we can simply swap the buffers around. If more than one then we must use the new buffer and copy the information @@ -847,7 +845,9 @@ #endif pci_dma_sync_single_for_cpu(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; - memcpy(skb_put(skb,length-4),olympic_priv->rx_ring_skb[rx_ring_last_received]->data,length-4) ; + skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received], + skb_put(skb,length - 4), + length - 4); pci_dma_sync_single_for_device(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; @@ -864,7 +864,9 @@ #endif olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; rx_desc = &(olympic_priv->olympic_rx_ring[rx_ring_last_received]); cpy_length = (i == 1 ? frag_len : le32_to_cpu(rx_desc->res_length)); - memcpy(skb_put(skb, cpy_length), olympic_priv->rx_ring_skb[rx_ring_last_received]->data, cpy_length) ; + skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received], + skb_put(skb, cpy_length), + cpy_length); pci_dma_sync_single_for_device(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; @@ -1440,16 +1442,16 @@ #endif next_ptr=readw(buf_ptr+offsetof(struct mac_receive_buffer,next)); } while (next_ptr && (buf_ptr=olympic_priv->olympic_lap + ntohs(next_ptr))); + mac_frame->protocol = tr_type_trans(mac_frame, dev); + if (olympic_priv->olympic_network_monitor) { struct trh_hdr *mac_hdr ; printk(KERN_WARNING "%s: Received MAC Frame, details: \n",dev->name) ; - mac_hdr = (struct trh_hdr *)mac_frame->data ; + mac_hdr = tr_hdr(mac_frame); printk(KERN_WARNING "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->daddr[0], mac_hdr->daddr[1], mac_hdr->daddr[2], mac_hdr->daddr[3], mac_hdr->daddr[4], mac_hdr->daddr[5]) ; printk(KERN_WARNING "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->saddr[0], mac_hdr->saddr[1], mac_hdr->saddr[2], mac_hdr->saddr[3], mac_hdr->saddr[4], mac_hdr->saddr[5]) ; } - mac_frame->dev = dev ; - mac_frame->protocol = tr_type_trans(mac_frame,dev); - netif_rx(mac_frame) ; + netif_rx(mac_frame); dev->last_rx = jiffies; drop_frame: diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c index cec282a..9bbea5c 100644 --- a/drivers/net/tokenring/smctr.c +++ b/drivers/net/tokenring/smctr.c @@ -3889,14 +3889,13 @@ static int smctr_process_rx_packet(MAC_H /* Slide data into a sleek skb. */ skb_put(skb, skb->len); - memcpy(skb->data, rmf, skb->len); + skb_copy_to_linear_data(skb, rmf, skb->len); /* Update Counters */ tp->MacStat.rx_packets++; tp->MacStat.rx_bytes += skb->len; /* Kick the packet on up. */ - skb->dev = dev; skb->protocol = tr_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; @@ -4476,14 +4475,13 @@ static int smctr_rx_frame(struct net_dev if (skb) { skb_put(skb, rx_size); - memcpy(skb->data, pbuff, rx_size); + skb_copy_to_linear_data(skb, pbuff, rx_size); /* Update Counters */ tp->MacStat.rx_packets++; tp->MacStat.rx_bytes += skb->len; /* Kick the packet on up. */ - skb->dev = dev; skb->protocol = tr_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c index ea797ca..12bd294 100644 --- a/drivers/net/tokenring/tms380tr.c +++ b/drivers/net/tokenring/tms380tr.c @@ -644,7 +644,7 @@ static int tms380tr_hardware_send_packet dmabuf = 0; i = tp->TplFree->TPLIndex; buf = tp->LocalTxBuffers[i]; - memcpy(buf, skb->data, length); + skb_copy_from_linear_data(skb, buf, length); newbuf = ((char *)buf - (char *)tp) + tp->dmabuffer; } else { @@ -2168,7 +2168,6 @@ static void tms380tr_rcv_status_irq(stru } else { - skb->dev = dev; skb_put(skb, tp->MaxPacketSize); rpl->SkbStat = SKB_DATA_COPY; ReceiveDataPtr = rpl->MData; @@ -2179,7 +2178,8 @@ static void tms380tr_rcv_status_irq(stru || rpl->SkbStat == SKB_DMA_DIRECT)) { if(rpl->SkbStat == SKB_DATA_COPY) - memcpy(skb->data, ReceiveDataPtr, Length); + skb_copy_to_linear_data(skb, ReceiveDataPtr, + Length); /* Deliver frame to system */ rpl->Skb = NULL; diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index d92c5c5..0bfc2c9 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -788,7 +788,6 @@ static int tsi108_complete_rx(struct net printk(".\n"); } - skb->dev = dev; skb_put(skb, data->rxring[rx].len); skb->protocol = eth_type_trans(skb, dev); netif_receive_skb(skb); diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index c82befa..8617298 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -63,7 +63,7 @@ MODULE_PARM_DESC (debug, "de2104x bitmap /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(__sparc__) || defined(__ia64__) \ + || defined(CONFIG_SPARC) || defined(__ia64__) \ || defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else @@ -435,7 +435,6 @@ static void de_rx (struct de_private *de rx_work = 100; goto rx_next; } - copy_skb->dev = de->dev; if (!copying_skb) { pci_unmap_single(de->pdev, mapping, @@ -450,8 +449,8 @@ static void de_rx (struct de_private *de } else { pci_dma_sync_single_for_cpu(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); skb_reserve(copy_skb, RX_OFFSET); - memcpy(skb_put(copy_skb, len), skb->data, len); - + skb_copy_from_linear_data(skb, skb_put(copy_skb, len), + len); pci_dma_sync_single_for_device(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index 4b3cd3d..62143f9 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -1160,7 +1160,7 @@ de4x5_hw_init(struct net_device *dev, u_ sprintf(lp->adapter_name,"%s (%s)", name, gendev->bus_id); lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc); -#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY) +#if defined(__alpha__) || defined(__powerpc__) || defined(CONFIG_SPARC) || defined(DE4X5_DO_MEMCPY) lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + DE4X5_ALIGN; #endif lp->rx_ring = dma_alloc_coherent(gendev, lp->dma_size, @@ -1175,7 +1175,7 @@ #endif ** Set up the RX descriptor ring (Intels) ** Allocate contiguous receive buffers, long word aligned (Alphas) */ -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY) for (i=0; irx_ring[i].status = 0; lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); @@ -1252,11 +1252,7 @@ #endif mii_get_phy(dev); } -#ifndef __sparc_v9__ printk(" and requires IRQ%d (provided by %s).\n", dev->irq, -#else - printk(" and requires IRQ%x (provided by %s).\n", dev->irq, -#endif ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); } @@ -3627,14 +3623,13 @@ de4x5_alloc_rx_buff(struct net_device *d struct de4x5_private *lp = netdev_priv(dev); struct sk_buff *p; -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY) struct sk_buff *ret; u_long i=0, tmp; p = dev_alloc_skb(IEEE802_3_SZ + DE4X5_ALIGN + 2); if (!p) return NULL; - p->dev = dev; tmp = virt_to_bus(p->data); i = ((tmp + DE4X5_ALIGN) & ~DE4X5_ALIGN) - tmp; skb_reserve(p, i); @@ -3655,7 +3650,6 @@ #else p = dev_alloc_skb(len + 2); if (!p) return NULL; - p->dev = dev; skb_reserve(p, 2); /* Align */ if (index < lp->rx_old) { /* Wrapped buffer */ short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c index 9aeac76..4ed67ff 100644 --- a/drivers/net/tulip/dmfe.c +++ b/drivers/net/tulip/dmfe.c @@ -55,9 +55,6 @@ TODO - Implement pci_driver::suspend() and pci_driver::resume() - power management methods. - Check on 64 bit boxes. Check and fix on big endian boxes. @@ -125,6 +122,11 @@ #define MAX_CHECK_PACKET 0x8000 #define DM9801_NOISE_FLOOR 8 #define DM9802_NOISE_FLOOR 5 +#define DMFE_WOL_LINKCHANGE 0x20000000 +#define DMFE_WOL_SAMPLEPACKET 0x10000000 +#define DMFE_WOL_MAGICPACKET 0x08000000 + + #define DMFE_10MHF 0 #define DMFE_100MHF 1 #define DMFE_10MFD 4 @@ -251,6 +253,7 @@ struct dmfe_board_info { u8 wait_reset; /* Hardware failed, need to reset */ u8 dm910x_chk_mode; /* Operating mode check */ u8 first_in_callback; /* Flag to record state */ + u8 wol_mode; /* user WOL settings */ struct timer_list timer; /* System defined statistic counter */ @@ -431,6 +434,7 @@ #endif db->chip_id = ent->driver_data; db->ioaddr = pci_resource_start(pdev, 0); db->chip_revision = dev_rev; + db->wol_mode = 0; db->pdev = pdev; @@ -682,7 +686,7 @@ static int dmfe_start_xmit(struct sk_buf /* transmit this packet */ txptr = db->tx_insert_ptr; - memcpy(txptr->tx_buf_ptr, skb->data, skb->len); + skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len); txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); /* Point to next transmit free descriptor */ @@ -988,14 +992,14 @@ static void dmfe_rx_packet(struct DEVICE skb = newskb; /* size less than COPY_SIZE, allocate a rxlen SKB */ - skb->dev = dev; skb_reserve(skb, 2); /* 16byte align */ - memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->data, rxlen); + skb_copy_from_linear_data(rxptr->rx_skb_ptr, + skb_put(skb, rxlen), + rxlen); dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - skb->dev = dev; + } else skb_put(skb, rxlen); - } + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; @@ -1065,7 +1069,11 @@ static void dmfe_set_filter_mode(struct spin_unlock_irqrestore(&db->lock, flags); } -static void netdev_get_drvinfo(struct net_device *dev, +/* + * Ethtool interace + */ + +static void dmfe_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct dmfe_board_info *np = netdev_priv(dev); @@ -1079,9 +1087,35 @@ static void netdev_get_drvinfo(struct ne dev->base_addr, dev->irq); } +static int dmfe_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct dmfe_board_info *db = netdev_priv(dev); + + if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | + WAKE_ARP | WAKE_MAGICSECURE)) + return -EOPNOTSUPP; + + db->wol_mode = wolinfo->wolopts; + return 0; +} + +static void dmfe_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct dmfe_board_info *db = netdev_priv(dev); + + wolinfo->supported = WAKE_PHY | WAKE_MAGIC; + wolinfo->wolopts = db->wol_mode; + return; +} + + static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, + .get_drvinfo = dmfe_ethtool_get_drvinfo, .get_link = ethtool_op_get_link, + .set_wol = dmfe_ethtool_set_wol, + .get_wol = dmfe_ethtool_get_wol, }; /* @@ -2050,11 +2084,85 @@ static struct pci_device_id dmfe_pci_tbl MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); +#ifdef CONFIG_PM +static int dmfe_suspend(struct pci_dev *pci_dev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pci_dev); + struct dmfe_board_info *db = netdev_priv(dev); + u32 tmp; + + /* Disable upper layer interface */ + netif_device_detach(dev); + + /* Disable Tx/Rx */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); + update_cr6(db->cr6_data, dev->base_addr); + + /* Disable Interrupt */ + outl(0, dev->base_addr + DCR7); + outl(inl (dev->base_addr + DCR5), dev->base_addr + DCR5); + + /* Fre RX buffers */ + dmfe_free_rxbuffer(db); + + /* Enable WOL */ + pci_read_config_dword(pci_dev, 0x40, &tmp); + tmp &= ~(DMFE_WOL_LINKCHANGE|DMFE_WOL_MAGICPACKET); + + if (db->wol_mode & WAKE_PHY) + tmp |= DMFE_WOL_LINKCHANGE; + if (db->wol_mode & WAKE_MAGIC) + tmp |= DMFE_WOL_MAGICPACKET; + + pci_write_config_dword(pci_dev, 0x40, tmp); + + pci_enable_wake(pci_dev, PCI_D3hot, 1); + pci_enable_wake(pci_dev, PCI_D3cold, 1); + + /* Power down device*/ + pci_set_power_state(pci_dev, pci_choose_state (pci_dev,state)); + pci_save_state(pci_dev); + + return 0; +} + +static int dmfe_resume(struct pci_dev *pci_dev) +{ + struct net_device *dev = pci_get_drvdata(pci_dev); + u32 tmp; + + pci_restore_state(pci_dev); + pci_set_power_state(pci_dev, PCI_D0); + + /* Re-initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Disable WOL */ + pci_read_config_dword(pci_dev, 0x40, &tmp); + + tmp &= ~(DMFE_WOL_LINKCHANGE | DMFE_WOL_MAGICPACKET); + pci_write_config_dword(pci_dev, 0x40, tmp); + + pci_enable_wake(pci_dev, PCI_D3hot, 0); + pci_enable_wake(pci_dev, PCI_D3cold, 0); + + /* Restart upper layer interface */ + netif_device_attach(dev); + + return 0; +} +#else +#define dmfe_suspend NULL +#define dmfe_resume NULL +#endif + static struct pci_driver dmfe_driver = { .name = "dmfe", .id_table = dmfe_pci_tbl, .probe = dmfe_init_one, .remove = __devexit_p(dmfe_remove_one), + .suspend = dmfe_suspend, + .resume = dmfe_resume }; MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index e3488d7..9b08afb 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -192,7 +192,6 @@ #endif to a minimally-sized skbuff. */ if (pkt_len < tulip_rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(tp->pdev, tp->rx_buffers[entry].mapping, @@ -416,7 +415,6 @@ #endif to a minimally-sized skbuff. */ if (pkt_len < tulip_rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(tp->pdev, tp->rx_buffers[entry].mapping, @@ -675,7 +673,7 @@ #endif if (tp->link_change) (tp->link_change)(dev, csr5); } - if (csr5 & SytemError) { + if (csr5 & SystemError) { int error = (csr5 >> 23) & 7; /* oops, we hit a PCI error. The code produced corresponds * to the reason: @@ -745,7 +743,7 @@ #ifdef CONFIG_TULIP_NAPI TxFIFOUnderflow | TxJabber | TPLnkFail | - SytemError )) != 0); + SystemError )) != 0); #else } while ((csr5 & (NormalIntr|AbnormalIntr)) != 0); diff --git a/drivers/net/tulip/media.c b/drivers/net/tulip/media.c index 20bd52b..b562566 100644 --- a/drivers/net/tulip/media.c +++ b/drivers/net/tulip/media.c @@ -44,8 +44,10 @@ static const unsigned char comet_miireg2 /* MII transceiver control section. Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. */ + MDIO protocol. + See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management functions") + or DP83840A data sheet for more details. + */ int tulip_mdio_read(struct net_device *dev, int phy_id, int location) { @@ -261,24 +263,56 @@ void tulip_select_media(struct net_devic u16 *reset_sequence = &((u16*)(p+3))[init_length]; int reset_length = p[2 + init_length*2]; misc_info = reset_sequence + reset_length; - if (startup) + if (startup) { + int timeout = 10; /* max 1 ms */ for (i = 0; i < reset_length; i++) iowrite32(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + + /* flush posted writes */ + ioread32(ioaddr + CSR15); + + /* Sect 3.10.3 in DP83840A.pdf (p39) */ + udelay(500); + + /* Section 4.2 in DP83840A.pdf (p43) */ + /* and IEEE 802.3 "22.2.4.1.1 Reset" */ + while (timeout-- && + (tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET)) + udelay(100); + } for (i = 0; i < init_length; i++) iowrite32(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + + ioread32(ioaddr + CSR15); /* flush posted writes */ } else { u8 *init_sequence = p + 2; u8 *reset_sequence = p + 3 + init_length; int reset_length = p[2 + init_length]; misc_info = (u16*)(reset_sequence + reset_length); if (startup) { + int timeout = 10; /* max 1 ms */ iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12); for (i = 0; i < reset_length; i++) iowrite32(reset_sequence[i], ioaddr + CSR12); + + /* flush posted writes */ + ioread32(ioaddr + CSR12); + + /* Sect 3.10.3 in DP83840A.pdf (p39) */ + udelay(500); + + /* Section 4.2 in DP83840A.pdf (p43) */ + /* and IEEE 802.3 "22.2.4.1.1 Reset" */ + while (timeout-- && + (tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET)) + udelay(100); } for (i = 0; i < init_length; i++) iowrite32(init_sequence[i], ioaddr + CSR12); + + ioread32(ioaddr + CSR12); /* flush posted writes */ } + tmp_info = get_u16(&misc_info[1]); if (tmp_info) tp->advertising[phy_num] = tmp_info | 1; diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index 25f25da..c840d2e 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h @@ -132,7 +132,7 @@ #define RxPollInt (RxIntr|RxNoBuf|RxDied /* The bits in the CSR5 status registers, mostly interrupt sources. */ enum status_bits { TimerInt = 0x800, - SytemError = 0x2000, + SystemError = 0x2000, TPLnkFail = 0x1000, TPLnkPass = 0x10, NormalIntr = 0x10000, @@ -482,8 +482,11 @@ static inline void tulip_stop_rxtx(struc udelay(10); if (!i) - printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed\n", - pci_name(tp->pdev)); + printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed" + " (CSR5 0x%x CSR6 0x%x)\n", + pci_name(tp->pdev), + ioread32(ioaddr + CSR5), + ioread32(ioaddr + CSR6)); } } diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index e3774a5..041af63 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -17,11 +17,11 @@ #define DRV_NAME "tulip" #ifdef CONFIG_TULIP_NAPI -#define DRV_VERSION "1.1.14-NAPI" /* Keep at least for test */ +#define DRV_VERSION "1.1.15-NAPI" /* Keep at least for test */ #else -#define DRV_VERSION "1.1.14" +#define DRV_VERSION "1.1.15" #endif -#define DRV_RELDATE "May 11, 2002" +#define DRV_RELDATE "Feb 27, 2007" #include @@ -36,8 +36,8 @@ #include #include #include -#ifdef __sparc__ -#include +#ifdef CONFIG_SPARC +#include #endif static char version[] __devinitdata = @@ -67,7 +67,7 @@ const char * const medianame[32] = { /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(__sparc__) || defined(__ia64__) \ + || defined(CONFIG_SPARC) || defined(__ia64__) \ || defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else @@ -91,7 +91,7 @@ #if defined(__alpha__) || defined(__ia64 static int csr0 = 0x01A00000 | 0xE000; #elif defined(__i386__) || defined(__powerpc__) || defined(__x86_64__) static int csr0 = 0x01A00000 | 0x8000; -#elif defined(__sparc__) || defined(__hppa__) +#elif defined(CONFIG_SPARC) || defined(__hppa__) /* The UltraSparc PCI controllers will disconnect at every 64-byte * crossing anyways so it makes no sense to tell Tulip to burst * any more than that. @@ -1315,7 +1315,7 @@ #endif /* DM9102A has troubles with MRM & clear reserved bits 24:22, 20, 16, 7:1 */ if (tulip_uli_dm_quirk(pdev)) { csr0 &= ~0x01f100ff; -#if defined(__sparc__) +#if defined(CONFIG_SPARC) csr0 = (csr0 & ~0xff00) | 0xe000; #endif } @@ -1535,23 +1535,19 @@ #endif Many PCI BIOSes also incorrectly report the IRQ line, so we correct that here as well. */ if (sum == 0 || sum == 6*0xff) { -#if defined(__sparc__) - struct pcidev_cookie *pcp = pdev->sysdata; +#if defined(CONFIG_SPARC) + struct device_node *dp = pci_device_to_OF_node(pdev); + const unsigned char *addr; + int len; #endif eeprom_missing = 1; for (i = 0; i < 5; i++) dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1; -#if defined(__sparc__) - if (pcp) { - unsigned char *addr; - int len; - - addr = of_get_property(pcp->prom_node, - "local-mac-address", &len); - if (addr && len == 6) - memcpy(dev->dev_addr, addr, 6); - } +#if defined(CONFIG_SPARC) + addr = of_get_property(dp, "local-mac-address", &len); + if (addr && len == 6) + memcpy(dev->dev_addr, addr, 6); #endif #if defined(__i386__) || defined(__x86_64__) /* Patch up x86 BIOS bug. */ if (last_irq) diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c index 229158e..ca2548e 100644 --- a/drivers/net/tulip/uli526x.c +++ b/drivers/net/tulip/uli526x.c @@ -583,7 +583,7 @@ static int uli526x_start_xmit(struct sk_ /* transmit this packet */ txptr = db->tx_insert_ptr; - memcpy(txptr->tx_buf_ptr, skb->data, skb->len); + skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len); txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); /* Point to next transmit free descriptor */ @@ -828,14 +828,14 @@ static void uli526x_rx_packet(struct net ( (skb = dev_alloc_skb(rxlen + 2) ) != NULL) ) { /* size less than COPY_SIZE, allocate a rxlen SKB */ - skb->dev = dev; skb_reserve(skb, 2); /* 16byte align */ - memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen); + memcpy(skb_put(skb, rxlen), + skb_tail_pointer(rxptr->rx_skb_ptr), + rxlen); uli526x_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - skb->dev = dev; + } else skb_put(skb, rxlen); - } + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; @@ -1177,7 +1177,10 @@ static void uli526x_reuse_skb(struct uli if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { rxptr->rx_skb_ptr = skb; - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev, + skb_tail_pointer(skb), + RX_ALLOC_SIZE, + PCI_DMA_FROMDEVICE)); wmb(); rxptr->rdes0 = cpu_to_le32(0x80000000); db->rx_avail_cnt++; @@ -1341,7 +1344,10 @@ static void allocate_rx_buffer(struct ul if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) break; rxptr->rx_skb_ptr = skb; /* FIXME (?) */ - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev, + skb_tail_pointer(skb), + RX_ALLOC_SIZE, + PCI_DMA_FROMDEVICE)); wmb(); rxptr->rdes0 = cpu_to_le32(0x80000000); rxptr = rxptr->next_rx_desc; diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index 002a05e..fa44070 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -813,7 +813,6 @@ static void init_rxtx_rings(struct net_d np->rx_skbuff[i] = skb; if (skb == NULL) break; - skb->dev = dev; /* Mark as being used by this device. */ np->rx_addr[i] = pci_map_single(np->pci_dev,skb->data, np->rx_buf_sz,PCI_DMA_FROMDEVICE); @@ -903,7 +902,7 @@ #if defined (__i386__) && !defined(MODUL } #elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) i |= 0xE000; -#elif defined(__sparc__) || defined (CONFIG_PARISC) +#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) i |= 0x4800; #else #warning Processor architecture undefined @@ -1148,7 +1147,7 @@ static irqreturn_t intr_handler(int irq, } /* Abnormal error summary/uncommon events handlers. */ - if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SytemError | + if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SystemError | TimerInt | TxDied)) netdev_error(dev, intr_status); @@ -1229,7 +1228,6 @@ #endif to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev,np->rx_addr[entry], np->rx_skbuff[entry]->len, @@ -1278,7 +1276,6 @@ #endif np->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ - skb->dev = dev; /* Mark as being used by this device. */ np->rx_addr[entry] = pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c index 61d3130..985a181 100644 --- a/drivers/net/tulip/xircom_cb.c +++ b/drivers/net/tulip/xircom_cb.c @@ -411,9 +411,9 @@ static int xircom_start_xmit(struct sk_b sometimes sends more than you ask it to. */ memset(&card->tx_buffer[bufferoffsets[desc]/4],0,1536); - memcpy(&(card->tx_buffer[bufferoffsets[desc]/4]),skb->data,skb->len); - - + skb_copy_from_linear_data(skb, + &(card->tx_buffer[bufferoffsets[desc] / 4]), + skb->len); /* FIXME: The specification tells us that the length we send HAS to be a multiple of 4 bytes. */ @@ -1207,7 +1207,6 @@ static void investigate_read_descriptor( card->stats.rx_dropped++; goto out; } - skb->dev = dev; skb_reserve(skb, 2); eth_copy_and_sum(skb, (unsigned char*)&card->rx_buffer[bufferoffset / 4], pkt_len, 0); skb_put(skb, pkt_len); diff --git a/drivers/net/tulip/xircom_tulip_cb.c b/drivers/net/tulip/xircom_tulip_cb.c index a998c5d..f641729 100644 --- a/drivers/net/tulip/xircom_tulip_cb.c +++ b/drivers/net/tulip/xircom_tulip_cb.c @@ -65,7 +65,7 @@ #if defined(__alpha__) || defined(__ia64 static int csr0 = 0x01A00000 | 0xE000; #elif defined(__powerpc__) static int csr0 = 0x01B00000 | 0x8000; -#elif defined(__sparc__) +#elif defined(CONFIG_SPARC) static int csr0 = 0x01B00080 | 0x8000; #elif defined(__i386__) static int csr0 = 0x01A00000 | 0x8000; @@ -915,7 +915,9 @@ xircom_start_xmit(struct sk_buff *skb, s tp->tx_skbuff[entry] = skb; if (tp->chip_id == X3201_3) { - memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); + skb_copy_from_linear_data(skb, + tp->tx_aligned_skbuff[entry]->data, + skb->len); tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); } else tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); @@ -1238,7 +1240,6 @@ #endif to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ #if ! defined(__alpha__) eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 5643d1e..a2c6caa 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -18,6 +18,10 @@ /* * Changes: * + * Brian Braunstein 2007/03/23 + * Fixed hw address handling. Now net_device.dev_addr is kept consistent + * with tun.dev_addr when the address is set by this module. + * * Mike Kershaw 2005/08/14 * Add TUNSETLINK ioctl to set the link encapsulation * @@ -196,7 +200,10 @@ static void tun_net_init(struct net_devi dev->set_multicast_list = tun_net_mclist; ether_setup(dev); - random_ether_addr(dev->dev_addr); + + /* random address already created for us by tun_set_iff, use it */ + memcpy(dev->dev_addr, tun->dev_addr, min(sizeof(tun->dev_addr), sizeof(dev->dev_addr)) ); + dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; } @@ -254,11 +261,11 @@ static __inline__ ssize_t tun_get_user(s return -EFAULT; } - skb->dev = tun->dev; switch (tun->flags & TUN_TYPE_MASK) { case TUN_TUN_DEV: - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = pi.proto; + skb->dev = tun->dev; break; case TUN_TAP_DEV: skb->protocol = eth_type_trans(skb, tun->dev); @@ -386,8 +393,8 @@ static ssize_t tun_chr_aio_read(struct k * - we are multicast promiscous. * - we belong to the multicast group. */ - memcpy(addr, skb->data, - min_t(size_t, sizeof addr, skb->len)); + skb_copy_from_linear_data(skb, addr, min_t(size_t, sizeof addr, + skb->len)); bit_nr = ether_crc(sizeof addr, addr) >> 26; if ((tun->if_flags & IFF_PROMISC) || memcmp(addr, tun->dev_addr, sizeof addr) == 0 || @@ -636,6 +643,7 @@ #endif return 0; case SIOCGIFHWADDR: + /* Note: the actual net device's address may be different */ memcpy(ifr.ifr_hwaddr.sa_data, tun->dev_addr, min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr)); if (copy_to_user( argp, &ifr, sizeof ifr)) @@ -643,16 +651,24 @@ #endif return 0; case SIOCSIFHWADDR: - /** Set the character device's hardware address. This is used when - * filtering packets being sent from the network device to the character - * device. */ - memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data, - min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr)); - DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n", - tun->dev->name, - tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2], - tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]); - return 0; + { + /* try to set the actual net device's hw address */ + int ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); + + if (ret == 0) { + /** Set the character device's hardware address. This is used when + * filtering packets being sent from the network device to the character + * device. */ + memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data, + min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr)); + DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n", + tun->dev->name, + tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2], + tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]); + } + + return ret; + } case SIOCADDMULTI: /** Add the specified group to the character device's multicast filter diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 0d91d09..f2dd776 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -1708,7 +1708,6 @@ typhoon_rx(struct typhoon *tp, struct ba if(pkt_len < rx_copybreak && (new_skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - new_skb->dev = tp->dev; skb_reserve(new_skb, 2); pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, PKT_BUF_SZ, diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 639e1e6..16b9acd 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -29,6 +29,7 @@ #include #include #include #include +#include #include #include @@ -41,12 +42,13 @@ #include #include #include "ucc_geth.h" -#include "ucc_geth_phy.h" +#include "ucc_geth_mii.h" #undef DEBUG -#define DRV_DESC "QE UCC Gigabit Ethernet Controller version:Sept 11, 2006" +#define DRV_DESC "QE UCC Gigabit Ethernet Controller" #define DRV_NAME "ucc_geth" +#define DRV_VERSION "1.1" #define ugeth_printk(level, format, arg...) \ printk(level format "\n", ## arg) @@ -73,22 +75,13 @@ static struct ucc_geth_info ugeth_primar .bd_mem_part = MEM_PART_SYSTEM, .rtsm = UCC_FAST_SEND_IDLES_BETWEEN_FRAMES, .max_rx_buf_length = 1536, -/* FIXME: should be changed in run time for 1G and 100M */ -#ifdef CONFIG_UGETH_HAS_GIGA - .urfs = UCC_GETH_URFS_GIGA_INIT, - .urfet = UCC_GETH_URFET_GIGA_INIT, - .urfset = UCC_GETH_URFSET_GIGA_INIT, - .utfs = UCC_GETH_UTFS_GIGA_INIT, - .utfet = UCC_GETH_UTFET_GIGA_INIT, - .utftt = UCC_GETH_UTFTT_GIGA_INIT, -#else + /* adjusted at startup if max-speed 1000 */ .urfs = UCC_GETH_URFS_INIT, .urfet = UCC_GETH_URFET_INIT, .urfset = UCC_GETH_URFSET_INIT, .utfs = UCC_GETH_UTFS_INIT, .utfet = UCC_GETH_UTFET_INIT, .utftt = UCC_GETH_UTFTT_INIT, -#endif .ufpt = 256, .mode = UCC_FAST_PROTOCOL_MODE_ETHERNET, .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL, @@ -217,70 +210,6 @@ static struct list_head *dequeue(struct } } -static int get_interface_details(enum enet_interface enet_interface, - enum enet_speed *speed, - int *r10m, - int *rmm, - int *rpm, - int *tbi, int *limited_to_full_duplex) -{ - /* Analyze enet_interface according to Interface Mode - Configuration table */ - switch (enet_interface) { - case ENET_10_MII: - *speed = ENET_SPEED_10BT; - break; - case ENET_10_RMII: - *speed = ENET_SPEED_10BT; - *r10m = 1; - *rmm = 1; - break; - case ENET_10_RGMII: - *speed = ENET_SPEED_10BT; - *rpm = 1; - *r10m = 1; - *limited_to_full_duplex = 1; - break; - case ENET_100_MII: - *speed = ENET_SPEED_100BT; - break; - case ENET_100_RMII: - *speed = ENET_SPEED_100BT; - *rmm = 1; - break; - case ENET_100_RGMII: - *speed = ENET_SPEED_100BT; - *rpm = 1; - *limited_to_full_duplex = 1; - break; - case ENET_1000_GMII: - *speed = ENET_SPEED_1000BT; - *limited_to_full_duplex = 1; - break; - case ENET_1000_RGMII: - *speed = ENET_SPEED_1000BT; - *rpm = 1; - *limited_to_full_duplex = 1; - break; - case ENET_1000_TBI: - *speed = ENET_SPEED_1000BT; - *tbi = 1; - *limited_to_full_duplex = 1; - break; - case ENET_1000_RTBI: - *speed = ENET_SPEED_1000BT; - *rpm = 1; - *tbi = 1; - *limited_to_full_duplex = 1; - break; - default: - return -EINVAL; - break; - } - - return 0; -} - static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth, u8 *bd) { struct sk_buff *skb = NULL; @@ -758,24 +687,6 @@ static void dump_regs(struct ucc_geth_pr ugeth_info("hafdup : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->hafdup, in_be32(&ugeth->ug_regs->hafdup)); - ugeth_info("miimcfg : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimcfg, - in_be32(&ugeth->ug_regs->miimng.miimcfg)); - ugeth_info("miimcom : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimcom, - in_be32(&ugeth->ug_regs->miimng.miimcom)); - ugeth_info("miimadd : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimadd, - in_be32(&ugeth->ug_regs->miimng.miimadd)); - ugeth_info("miimcon : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimcon, - in_be32(&ugeth->ug_regs->miimng.miimcon)); - ugeth_info("miimstat : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimstat, - in_be32(&ugeth->ug_regs->miimng.miimstat)); - ugeth_info("miimmind : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimind, - in_be32(&ugeth->ug_regs->miimng.miimind)); ugeth_info("ifctl : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->ifctl, in_be32(&ugeth->ug_regs->ifctl)); @@ -1425,27 +1336,6 @@ static int init_mac_station_addr_regs(u8 return 0; } -static int init_mac_duplex_mode(int full_duplex, - int limited_to_full_duplex, - volatile u32 *maccfg2_register) -{ - u32 value = 0; - - /* some interfaces must work in full duplex mode */ - if ((full_duplex == 0) && (limited_to_full_duplex == 1)) - return -EINVAL; - - value = in_be32(maccfg2_register); - - if (full_duplex) - value |= MACCFG2_FDX; - else - value &= ~MACCFG2_FDX; - - out_be32(maccfg2_register, value); - return 0; -} - static int init_check_frame_length_mode(int length_check, volatile u32 *maccfg2_register) { @@ -1477,40 +1367,6 @@ static int init_preamble_length(u8 pream return 0; } -static int init_mii_management_configuration(int reset_mgmt, - int preamble_supress, - volatile u32 *miimcfg_register, - volatile u32 *miimind_register) -{ - unsigned int timeout = PHY_INIT_TIMEOUT; - u32 value = 0; - - value = in_be32(miimcfg_register); - if (reset_mgmt) { - value |= MIIMCFG_RESET_MANAGEMENT; - out_be32(miimcfg_register, value); - } - - value = 0; - - if (preamble_supress) - value |= MIIMCFG_NO_PREAMBLE; - - value |= UCC_GETH_MIIMCFG_MNGMNT_CLC_DIV_INIT; - out_be32(miimcfg_register, value); - - /* Wait until the bus is free */ - while ((in_be32(miimind_register) & MIIMIND_BUSY) && timeout--) - cpu_relax(); - - if (timeout <= 0) { - ugeth_err("%s: The MII Bus is stuck!", __FUNCTION__); - return -ETIMEDOUT; - } - - return 0; -} - static int init_rx_parameters(int reject_broadcast, int receive_short_frames, int promiscuous, volatile u32 *upsmr_register) @@ -1570,10 +1426,8 @@ static int adjust_enet_interface(struct struct ucc_geth_info *ug_info; struct ucc_geth *ug_regs; struct ucc_fast *uf_regs; - enum enet_speed speed; - int ret_val, rpm = 0, tbi = 0, r10m = 0, rmm = - 0, limited_to_full_duplex = 0; - u32 upsmr, maccfg2, utbipar, tbiBaseAddress; + int ret_val; + u32 upsmr, maccfg2, tbiBaseAddress; u16 value; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -1582,24 +1436,13 @@ static int adjust_enet_interface(struct ug_regs = ugeth->ug_regs; uf_regs = ugeth->uccf->uf_regs; - /* Analyze enet_interface according to Interface Mode Configuration - table */ - ret_val = - get_interface_details(ug_info->enet_interface, &speed, &r10m, &rmm, - &rpm, &tbi, &limited_to_full_duplex); - if (ret_val != 0) { - ugeth_err - ("%s: half duplex not supported in requested configuration.", - __FUNCTION__); - return ret_val; - } - /* Set MACCFG2 */ maccfg2 = in_be32(&ug_regs->maccfg2); maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK; - if ((speed == ENET_SPEED_10BT) || (speed == ENET_SPEED_100BT)) + if ((ugeth->max_speed == SPEED_10) || + (ugeth->max_speed == SPEED_100)) maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; - else if (speed == ENET_SPEED_1000BT) + else if (ugeth->max_speed == SPEED_1000) maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; maccfg2 |= ug_info->padAndCrc; out_be32(&ug_regs->maccfg2, maccfg2); @@ -1607,54 +1450,39 @@ static int adjust_enet_interface(struct /* Set UPSMR */ upsmr = in_be32(&uf_regs->upsmr); upsmr &= ~(UPSMR_RPM | UPSMR_R10M | UPSMR_TBIM | UPSMR_RMM); - if (rpm) + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UPSMR_RPM; - if (r10m) - upsmr |= UPSMR_R10M; - if (tbi) + switch (ugeth->max_speed) { + case SPEED_10: + upsmr |= UPSMR_R10M; + /* FALLTHROUGH */ + case SPEED_100: + if (ugeth->phy_interface != PHY_INTERFACE_MODE_RTBI) + upsmr |= UPSMR_RMM; + } + } + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UPSMR_TBIM; - if (rmm) - upsmr |= UPSMR_RMM; + } out_be32(&uf_regs->upsmr, upsmr); - /* Set UTBIPAR */ - utbipar = in_be32(&ug_regs->utbipar); - utbipar &= ~UTBIPAR_PHY_ADDRESS_MASK; - if (tbi) - utbipar |= - (ug_info->phy_address + - ugeth->ug_info->uf_info. - ucc_num) << UTBIPAR_PHY_ADDRESS_SHIFT; - else - utbipar |= - (0x10 + - ugeth->ug_info->uf_info. - ucc_num) << UTBIPAR_PHY_ADDRESS_SHIFT; - out_be32(&ug_regs->utbipar, utbipar); - /* Disable autonegotiation in tbi mode, because by default it comes up in autonegotiation mode. */ /* Note that this depends on proper setting in utbipar register. */ - if (tbi) { + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { tbiBaseAddress = in_be32(&ug_regs->utbipar); tbiBaseAddress &= UTBIPAR_PHY_ADDRESS_MASK; tbiBaseAddress >>= UTBIPAR_PHY_ADDRESS_SHIFT; - value = - ugeth->mii_info->mdio_read(ugeth->dev, (u8) tbiBaseAddress, - ENET_TBI_MII_CR); + value = ugeth->phydev->bus->read(ugeth->phydev->bus, + (u8) tbiBaseAddress, ENET_TBI_MII_CR); value &= ~0x1000; /* Turn off autonegotiation */ - ugeth->mii_info->mdio_write(ugeth->dev, (u8) tbiBaseAddress, - ENET_TBI_MII_CR, value); - } - - ret_val = init_mac_duplex_mode(1, - limited_to_full_duplex, - &ug_regs->maccfg2); - if (ret_val != 0) { - ugeth_err - ("%s: half duplex not supported in requested configuration.", - __FUNCTION__); - return ret_val; + ugeth->phydev->bus->write(ugeth->phydev->bus, + (u8) tbiBaseAddress, ENET_TBI_MII_CR, value); } init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2); @@ -1676,76 +1504,88 @@ static int adjust_enet_interface(struct * function converts those variables into the appropriate * register values, and can bring down the device if needed. */ + static void adjust_link(struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_geth *ug_regs; - u32 tempval; - struct ugeth_mii_info *mii_info = ugeth->mii_info; + struct ucc_fast *uf_regs; + struct phy_device *phydev = ugeth->phydev; + unsigned long flags; + int new_state = 0; ug_regs = ugeth->ug_regs; + uf_regs = ugeth->uccf->uf_regs; - if (mii_info->link) { + spin_lock_irqsave(&ugeth->lock, flags); + + if (phydev->link) { + u32 tempval = in_be32(&ug_regs->maccfg2); + u32 upsmr = in_be32(&uf_regs->upsmr); /* Now we make sure that we can be in full duplex mode. * If not, we operate in half-duplex mode. */ - if (mii_info->duplex != ugeth->oldduplex) { - if (!(mii_info->duplex)) { - tempval = in_be32(&ug_regs->maccfg2); + if (phydev->duplex != ugeth->oldduplex) { + new_state = 1; + if (!(phydev->duplex)) tempval &= ~(MACCFG2_FDX); - out_be32(&ug_regs->maccfg2, tempval); - - ugeth_info("%s: Half Duplex", dev->name); - } else { - tempval = in_be32(&ug_regs->maccfg2); + else tempval |= MACCFG2_FDX; - out_be32(&ug_regs->maccfg2, tempval); - - ugeth_info("%s: Full Duplex", dev->name); - } - - ugeth->oldduplex = mii_info->duplex; + ugeth->oldduplex = phydev->duplex; } - if (mii_info->speed != ugeth->oldspeed) { - switch (mii_info->speed) { - case 1000: - ugeth->ug_info->enet_interface = ENET_1000_RGMII; + if (phydev->speed != ugeth->oldspeed) { + new_state = 1; + switch (phydev->speed) { + case SPEED_1000: + tempval = ((tempval & + ~(MACCFG2_INTERFACE_MODE_MASK)) | + MACCFG2_INTERFACE_MODE_BYTE); break; - case 100: - ugeth->ug_info->enet_interface = ENET_100_RGMII; - break; - case 10: - ugeth->ug_info->enet_interface = ENET_10_RGMII; + case SPEED_100: + case SPEED_10: + tempval = ((tempval & + ~(MACCFG2_INTERFACE_MODE_MASK)) | + MACCFG2_INTERFACE_MODE_NIBBLE); + /* if reduced mode, re-set UPSMR.R10M */ + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { + if (phydev->speed == SPEED_10) + upsmr |= UPSMR_R10M; + else + upsmr &= ~(UPSMR_R10M); + } break; default: - ugeth_warn - ("%s: Ack! Speed (%d) is not 10/100/1000!", - dev->name, mii_info->speed); + if (netif_msg_link(ugeth)) + ugeth_warn( + "%s: Ack! Speed (%d) is not 10/100/1000!", + dev->name, phydev->speed); break; } - adjust_enet_interface(ugeth); - - ugeth_info("%s: Speed %dBT", dev->name, - mii_info->speed); - - ugeth->oldspeed = mii_info->speed; + ugeth->oldspeed = phydev->speed; } + out_be32(&ug_regs->maccfg2, tempval); + out_be32(&uf_regs->upsmr, upsmr); + if (!ugeth->oldlink) { - ugeth_info("%s: Link is up", dev->name); + new_state = 1; ugeth->oldlink = 1; - netif_carrier_on(dev); netif_schedule(dev); } - } else { - if (ugeth->oldlink) { - ugeth_info("%s: Link is down", dev->name); + } else if (ugeth->oldlink) { + new_state = 1; ugeth->oldlink = 0; ugeth->oldspeed = 0; ugeth->oldduplex = -1; - netif_carrier_off(dev); - } } + + if (new_state && netif_msg_link(ugeth)) + phy_print_status(phydev); + + spin_unlock_irqrestore(&ugeth->lock, flags); } /* Configure the PHY for dev. @@ -1753,102 +1593,40 @@ static void adjust_link(struct net_devic */ static int init_phy(struct net_device *dev) { - struct ucc_geth_private *ugeth = netdev_priv(dev); - struct phy_info *curphy; - struct ucc_mii_mng *mii_regs; - struct ugeth_mii_info *mii_info; - int err; + struct ucc_geth_private *priv = netdev_priv(dev); + struct phy_device *phydev; + char phy_id[BUS_ID_SIZE]; - mii_regs = &ugeth->ug_regs->miimng; + priv->oldlink = 0; + priv->oldspeed = 0; + priv->oldduplex = -1; - ugeth->oldlink = 0; - ugeth->oldspeed = 0; - ugeth->oldduplex = -1; + snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->ug_info->mdio_bus, + priv->ug_info->phy_address); - mii_info = kmalloc(sizeof(struct ugeth_mii_info), GFP_KERNEL); + phydev = phy_connect(dev, phy_id, &adjust_link, 0, priv->phy_interface); - if (NULL == mii_info) { - ugeth_err("%s: Could not allocate mii_info", dev->name); - return -ENOMEM; + if (IS_ERR(phydev)) { + printk("%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(phydev); } - mii_info->mii_regs = mii_regs; - mii_info->speed = SPEED_1000; - mii_info->duplex = DUPLEX_FULL; - mii_info->pause = 0; - mii_info->link = 0; - - mii_info->advertising = (ADVERTISED_10baseT_Half | + phydev->supported &= (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Full); - mii_info->autoneg = 1; + ADVERTISED_100baseT_Full); - mii_info->mii_id = ugeth->ug_info->phy_address; + if (priv->max_speed == SPEED_1000) + phydev->supported |= ADVERTISED_1000baseT_Full; - mii_info->dev = dev; + phydev->advertising = phydev->supported; - mii_info->mdio_read = &read_phy_reg; - mii_info->mdio_write = &write_phy_reg; - - spin_lock_init(&mii_info->mdio_lock); - - ugeth->mii_info = mii_info; - - spin_lock_irq(&ugeth->lock); - - /* Set this UCC to be the master of the MII managment */ - ucc_set_qe_mux_mii_mng(ugeth->ug_info->uf_info.ucc_num); - - if (init_mii_management_configuration(1, - ugeth->ug_info-> - miiPreambleSupress, - &mii_regs->miimcfg, - &mii_regs->miimind)) { - ugeth_err("%s: The MII Bus is stuck!", dev->name); - err = -1; - goto bus_fail; - } - - spin_unlock_irq(&ugeth->lock); - - /* get info for this PHY */ - curphy = get_phy_info(ugeth->mii_info); - - if (curphy == NULL) { - ugeth_err("%s: No PHY found", dev->name); - err = -1; - goto no_phy; - } - - mii_info->phyinfo = curphy; - - /* Run the commands which initialize the PHY */ - if (curphy->init) { - err = curphy->init(ugeth->mii_info); - if (err) - goto phy_init_fail; - } + priv->phydev = phydev; return 0; - - phy_init_fail: - no_phy: - bus_fail: - kfree(mii_info); - - return err; } -#ifdef CONFIG_UGETH_TX_ON_DEMOND -static int ugeth_transmit_on_demand(struct ucc_geth_private *ugeth) -{ - struct ucc_fastransmit_on_demand(ugeth->uccf); - return 0; -} -#endif static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth) { @@ -2356,6 +2134,8 @@ static void ucc_geth_memclean(struct ucc } for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { bd = ugeth->p_tx_bd_ring[i]; + if (!bd) + continue; for (j = 0; j < ugeth->ug_info->bdRingLenTx[i]; j++) { if (ugeth->tx_skbuff[i][j]) { dma_unmap_single(NULL, @@ -2487,6 +2267,7 @@ static void ucc_geth_set_multi(struct ne static void ucc_geth_stop(struct ucc_geth_private *ugeth) { struct ucc_geth *ug_regs = ugeth->ug_regs; + struct phy_device *phydev = ugeth->phydev; u32 tempval; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -2495,8 +2276,7 @@ static void ucc_geth_stop(struct ucc_get ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); /* Tell the kernel the link is down */ - ugeth->mii_info->link = 0; - adjust_link(ugeth->dev); + phy_stop(phydev); /* Mask all interrupts */ out_be32(ugeth->uccf->p_ucce, 0x00000000); @@ -2509,50 +2289,24 @@ static void ucc_geth_stop(struct ucc_get tempval &= ~(MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX); out_be32(&ug_regs->maccfg1, tempval); - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) { - /* Clear any pending interrupts */ - mii_clear_phy_interrupt(ugeth->mii_info); - - /* Disable PHY Interrupts */ - mii_configure_phy_interrupt(ugeth->mii_info, - MII_INTERRUPT_DISABLED); - } - free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev); - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) { - free_irq(ugeth->ug_info->phy_interrupt, ugeth->dev); - } else { - del_timer_sync(&ugeth->phy_info_timer); - } - ucc_geth_memclean(ugeth); } -static int ucc_geth_startup(struct ucc_geth_private *ugeth) +static int ucc_struct_init(struct ucc_geth_private *ugeth) { - struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt; - struct ucc_geth_init_pram *p_init_enet_pram; - struct ucc_fast_private *uccf; struct ucc_geth_info *ug_info; struct ucc_fast_info *uf_info; - struct ucc_fast *uf_regs; - struct ucc_geth *ug_regs; - int ret_val = -EINVAL; - u32 remoder = UCC_GETH_REMODER_INIT; - u32 init_enet_pram_offset, cecr_subblock, command, maccfg1; - u32 ifstat, i, j, size, l2qt, l3qt, length; - u16 temoder = UCC_GETH_TEMODER_INIT; - u16 test; - u8 function_code = 0; - u8 *bd, *endOfRing; - u8 numThreadsRxNumerical, numThreadsTxNumerical; - - ugeth_vdbg("%s: IN", __FUNCTION__); + int i; ug_info = ugeth->ug_info; uf_info = &ug_info->uf_info; + /* Create CQs for hash tables */ + INIT_LIST_HEAD(&ugeth->group_hash_q); + INIT_LIST_HEAD(&ugeth->ind_hash_q); + if (!((uf_info->bd_mem_part == MEM_PART_SYSTEM) || (uf_info->bd_mem_part == MEM_PART_MURAM))) { ugeth_err("%s: Bad memory partition value.", __FUNCTION__); @@ -2647,12 +2401,42 @@ static int ucc_geth_startup(struct ucc_g for (i = 0; i < ug_info->numQueuesTx; i++) uf_info->uccm_mask |= (UCCE_TXBF_SINGLE_MASK << i); /* Initialize the general fast UCC block. */ - if (ucc_fast_init(uf_info, &uccf)) { + if (ucc_fast_init(uf_info, &ugeth->uccf)) { ugeth_err("%s: Failed to init uccf.", __FUNCTION__); ucc_geth_memclean(ugeth); return -ENOMEM; } - ugeth->uccf = uccf; + + ugeth->ug_regs = (struct ucc_geth *) ioremap(uf_info->regs, sizeof(struct ucc_geth)); + + return 0; +} + +static int ucc_geth_startup(struct ucc_geth_private *ugeth) +{ + struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt; + struct ucc_geth_init_pram *p_init_enet_pram; + struct ucc_fast_private *uccf; + struct ucc_geth_info *ug_info; + struct ucc_fast_info *uf_info; + struct ucc_fast *uf_regs; + struct ucc_geth *ug_regs; + int ret_val = -EINVAL; + u32 remoder = UCC_GETH_REMODER_INIT; + u32 init_enet_pram_offset, cecr_subblock, command, maccfg1; + u32 ifstat, i, j, size, l2qt, l3qt, length; + u16 temoder = UCC_GETH_TEMODER_INIT; + u16 test; + u8 function_code = 0; + u8 *bd, *endOfRing; + u8 numThreadsRxNumerical, numThreadsTxNumerical; + + ugeth_vdbg("%s: IN", __FUNCTION__); + uccf = ugeth->uccf; + ug_info = ugeth->ug_info; + uf_info = &ug_info->uf_info; + uf_regs = uccf->uf_regs; + ug_regs = ugeth->ug_regs; switch (ug_info->numThreadsRx) { case UCC_GETH_NUM_OF_THREADS_1: @@ -2711,10 +2495,6 @@ static int ucc_geth_startup(struct ucc_g || (ug_info->vlanOperationNonTagged != UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP); - uf_regs = uccf->uf_regs; - ug_regs = (struct ucc_geth *) (uccf->uf_regs); - ugeth->ug_regs = ug_regs; - init_default_reg_vals(&uf_regs->upsmr, &ug_regs->maccfg1, &ug_regs->maccfg2); @@ -3177,8 +2957,8 @@ static int ucc_geth_startup(struct ucc_g /* Size varies with number of Rx queues */ ugeth->rx_irq_coalescing_tbl_offset = qe_muram_alloc(ug_info->numQueuesRx * - sizeof(struct ucc_geth_rx_interrupt_coalescing_entry), - UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT); + sizeof(struct ucc_geth_rx_interrupt_coalescing_entry) + + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT); if (IS_MURAM_ERR(ugeth->rx_irq_coalescing_tbl_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for" @@ -3359,13 +3139,6 @@ static int ucc_geth_startup(struct ucc_g for (j = 0; j < NUM_OF_PADDRS; j++) ugeth_82xx_filtering_clear_addr_in_paddr(ugeth, (u8) j); - /* Create CQs for hash tables */ - if (ug_info->maxGroupAddrInHash > 0) { - INIT_LIST_HEAD(&ugeth->group_hash_q); - } - if (ug_info->maxIndAddrInHash > 0) { - INIT_LIST_HEAD(&ugeth->ind_hash_q); - } p_82xx_addr_filt = (struct ucc_geth_82xx_address_filtering_pram *) ugeth-> p_rx_glbl_pram->addressfiltering; @@ -3562,6 +3335,9 @@ static void ucc_geth_timeout(struct net_ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); +#ifdef CONFIG_UGETH_TX_ON_DEMAND + struct ucc_fast_private *uccf; +#endif u8 *bd; /* BD pointer */ u32 bd_status; u8 txQ = 0; @@ -3620,6 +3396,10 @@ static int ucc_geth_start_xmit(struct sk out_be16(ugeth->p_cpucount[txQ], ugeth->cpucount[txQ]); } +#ifdef CONFIG_UGETH_TX_ON_DEMAND + uccf = ugeth->uccf; + out_be16(uccf->p_utodr, UCC_FAST_TOD); +#endif spin_unlock_irq(&ugeth->lock); return 0; @@ -3635,7 +3415,6 @@ static int ucc_geth_rx(struct ucc_geth_p ugeth_vdbg("%s: IN", __FUNCTION__); - spin_lock(&ugeth->lock); /* collect received buffers */ bd = ugeth->rxBd[rxQ]; @@ -3683,7 +3462,6 @@ #endif /* CONFIG_UGETH_NAPI */ skb = get_new_skb(ugeth, bd); if (!skb) { ugeth_warn("%s: No Rx Data Buffer", __FUNCTION__); - spin_unlock(&ugeth->lock); ugeth->stats.rx_dropped++; break; } @@ -3704,7 +3482,6 @@ #endif /* CONFIG_UGETH_NAPI */ } ugeth->rxBd[rxQ] = bd; - spin_unlock(&ugeth->lock); return howmany; } @@ -3756,23 +3533,38 @@ #ifdef CONFIG_UGETH_NAPI static int ucc_geth_poll(struct net_device *dev, int *budget) { struct ucc_geth_private *ugeth = netdev_priv(dev); + struct ucc_geth_info *ug_info; + struct ucc_fast_private *uccf; int howmany; - int rx_work_limit = *budget; - u8 rxQ = 0; + u8 i; + int rx_work_limit; + register u32 uccm; + + ug_info = ugeth->ug_info; + rx_work_limit = *budget; if (rx_work_limit > dev->quota) rx_work_limit = dev->quota; - howmany = ucc_geth_rx(ugeth, rxQ, rx_work_limit); + howmany = 0; + + for (i = 0; i < ug_info->numQueuesRx; i++) { + howmany += ucc_geth_rx(ugeth, i, rx_work_limit); + } dev->quota -= howmany; rx_work_limit -= howmany; *budget -= howmany; - if (rx_work_limit >= 0) + if (rx_work_limit > 0) { netif_rx_complete(dev); + uccf = ugeth->uccf; + uccm = in_be32(uccf->p_uccm); + uccm |= UCCE_RX_EVENTS; + out_be32(uccf->p_uccm, uccm); + } - return (rx_work_limit < 0) ? 1 : 0; + return (rx_work_limit > 0) ? 0 : 1; } #endif /* CONFIG_UGETH_NAPI */ @@ -3782,10 +3574,13 @@ static irqreturn_t ucc_geth_irq_handler( struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_fast_private *uccf; struct ucc_geth_info *ug_info; - register u32 ucce = 0; - register u32 bit_mask = UCCE_RXBF_SINGLE_MASK; - register u32 tx_mask = UCCE_TXBF_SINGLE_MASK; - register u8 i; + register u32 ucce; + register u32 uccm; +#ifndef CONFIG_UGETH_NAPI + register u32 rx_mask; +#endif + register u32 tx_mask; + u8 i; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -3795,174 +3590,57 @@ static irqreturn_t ucc_geth_irq_handler( uccf = ugeth->uccf; ug_info = ugeth->ug_info; - do { - ucce |= (u32) (in_be32(uccf->p_ucce) & in_be32(uccf->p_uccm)); - - /* clear event bits for next time */ - /* Side effect here is to mask ucce variable - for future processing below. */ - out_be32(uccf->p_ucce, ucce); /* Clear with ones, - but only bits in UCCM */ - - /* We ignore Tx interrupts because Tx confirmation is - done inside Tx routine */ + /* read and clear events */ + ucce = (u32) in_be32(uccf->p_ucce); + uccm = (u32) in_be32(uccf->p_uccm); + ucce &= uccm; + out_be32(uccf->p_ucce, ucce); + /* check for receive events that require processing */ + if (ucce & UCCE_RX_EVENTS) { +#ifdef CONFIG_UGETH_NAPI + if (netif_rx_schedule_prep(dev)) { + uccm &= ~UCCE_RX_EVENTS; + out_be32(uccf->p_uccm, uccm); + __netif_rx_schedule(dev); + } +#else + rx_mask = UCCE_RXBF_SINGLE_MASK; for (i = 0; i < ug_info->numQueuesRx; i++) { - if (ucce & bit_mask) - ucc_geth_rx(ugeth, i, - (int)ugeth->ug_info-> - bdRingLenRx[i]); - ucce &= ~bit_mask; - bit_mask <<= 1; + if (ucce & rx_mask) + ucc_geth_rx(ugeth, i, (int)ugeth->ug_info->bdRingLenRx[i]); + ucce &= ~rx_mask; + rx_mask <<= 1; } +#endif /* CONFIG_UGETH_NAPI */ + } + /* Tx event processing */ + if (ucce & UCCE_TX_EVENTS) { + spin_lock(&ugeth->lock); + tx_mask = UCCE_TXBF_SINGLE_MASK; for (i = 0; i < ug_info->numQueuesTx; i++) { if (ucce & tx_mask) ucc_geth_tx(dev, i); ucce &= ~tx_mask; tx_mask <<= 1; } + spin_unlock(&ugeth->lock); + } - /* Exceptions */ + /* Errors and other events */ + if (ucce & UCCE_OTHER) { if (ucce & UCCE_BSY) { - ugeth_vdbg("Got BUSY irq!!!!"); ugeth->stats.rx_errors++; - ucce &= ~UCCE_BSY; } - if (ucce & UCCE_OTHER) { - ugeth_vdbg("Got frame with error (ucce - 0x%08x)!!!!", - ucce); - ugeth->stats.rx_errors++; - ucce &= ~ucce; + if (ucce & UCCE_TXE) { + ugeth->stats.tx_errors++; } } - while (ucce); - - return IRQ_HANDLED; -} - -static irqreturn_t phy_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct ucc_geth_private *ugeth = netdev_priv(dev); - - ugeth_vdbg("%s: IN", __FUNCTION__); - - /* Clear the interrupt */ - mii_clear_phy_interrupt(ugeth->mii_info); - - /* Disable PHY interrupts */ - mii_configure_phy_interrupt(ugeth->mii_info, MII_INTERRUPT_DISABLED); - - /* Schedule the phy change */ - schedule_work(&ugeth->tq); return IRQ_HANDLED; } -/* Scheduled by the phy_interrupt/timer to handle PHY changes */ -static void ugeth_phy_change(struct work_struct *work) -{ - struct ucc_geth_private *ugeth = - container_of(work, struct ucc_geth_private, tq); - struct net_device *dev = ugeth->dev; - struct ucc_geth *ug_regs; - int result = 0; - - ugeth_vdbg("%s: IN", __FUNCTION__); - - ug_regs = ugeth->ug_regs; - - /* Delay to give the PHY a chance to change the - * register state */ - msleep(1); - - /* Update the link, speed, duplex */ - result = ugeth->mii_info->phyinfo->read_status(ugeth->mii_info); - - /* Adjust the known status as long as the link - * isn't still coming up */ - if ((0 == result) || (ugeth->mii_info->link == 0)) - adjust_link(dev); - - /* Reenable interrupts, if needed */ - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) - mii_configure_phy_interrupt(ugeth->mii_info, - MII_INTERRUPT_ENABLED); -} - -/* Called every so often on systems that don't interrupt - * the core for PHY changes */ -static void ugeth_phy_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct ucc_geth_private *ugeth = netdev_priv(dev); - - schedule_work(&ugeth->tq); - - mod_timer(&ugeth->phy_info_timer, jiffies + PHY_CHANGE_TIME * HZ); -} - -/* Keep trying aneg for some time - * If, after GFAR_AN_TIMEOUT seconds, it has not - * finished, we switch to forced. - * Either way, once the process has completed, we either - * request the interrupt, or switch the timer over to - * using ugeth_phy_timer to check status */ -static void ugeth_phy_startup_timer(unsigned long data) -{ - struct ugeth_mii_info *mii_info = (struct ugeth_mii_info *)data; - struct ucc_geth_private *ugeth = netdev_priv(mii_info->dev); - static int secondary = UGETH_AN_TIMEOUT; - int result; - - /* Configure the Auto-negotiation */ - result = mii_info->phyinfo->config_aneg(mii_info); - - /* If autonegotiation failed to start, and - * we haven't timed out, reset the timer, and return */ - if (result && secondary--) { - mod_timer(&ugeth->phy_info_timer, jiffies + HZ); - return; - } else if (result) { - /* Couldn't start autonegotiation. - * Try switching to forced */ - mii_info->autoneg = 0; - result = mii_info->phyinfo->config_aneg(mii_info); - - /* Forcing failed! Give up */ - if (result) { - ugeth_err("%s: Forcing failed!", mii_info->dev->name); - return; - } - } - - /* Kill the timer so it can be restarted */ - del_timer_sync(&ugeth->phy_info_timer); - - /* Grab the PHY interrupt, if necessary/possible */ - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) { - if (request_irq(ugeth->ug_info->phy_interrupt, - phy_interrupt, IRQF_SHARED, - "phy_interrupt", mii_info->dev) < 0) { - ugeth_err("%s: Can't get IRQ %d (PHY)", - mii_info->dev->name, - ugeth->ug_info->phy_interrupt); - } else { - mii_configure_phy_interrupt(ugeth->mii_info, - MII_INTERRUPT_ENABLED); - return; - } - } - - /* Start the timer again, this time in order to - * handle a change in status */ - init_timer(&ugeth->phy_info_timer); - ugeth->phy_info_timer.function = &ugeth_phy_timer; - ugeth->phy_info_timer.data = (unsigned long)mii_info->dev; - mod_timer(&ugeth->phy_info_timer, jiffies + PHY_CHANGE_TIME * HZ); -} - /* Called when something needs to use the ethernet device */ /* Returns 0 for success. */ static int ucc_geth_open(struct net_device *dev) @@ -3979,6 +3657,12 @@ static int ucc_geth_open(struct net_devi return -EINVAL; } + err = ucc_struct_init(ugeth); + if (err) { + ugeth_err("%s: Cannot configure internal struct, aborting.", dev->name); + return err; + } + err = ucc_geth_startup(ugeth); if (err) { ugeth_err("%s: Cannot configure net device, aborting.", @@ -4006,10 +3690,12 @@ static int ucc_geth_open(struct net_devi err = init_phy(dev); if (err) { - ugeth_err("%s: Cannot initialzie PHY, aborting.", dev->name); + ugeth_err("%s: Cannot initialize PHY, aborting.", dev->name); return err; } -#ifndef CONFIG_UGETH_NAPI + + phy_start(ugeth->phydev); + err = request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler, 0, "UCC Geth", dev); @@ -4019,15 +3705,6 @@ #ifndef CONFIG_UGETH_NAPI ucc_geth_stop(ugeth); return err; } -#endif /* CONFIG_UGETH_NAPI */ - - /* Set up the PHY change work queue */ - INIT_WORK(&ugeth->tq, ugeth_phy_change); - - init_timer(&ugeth->phy_info_timer); - ugeth->phy_info_timer.function = &ugeth_phy_startup_timer; - ugeth->phy_info_timer.data = (unsigned long)ugeth->mii_info; - mod_timer(&ugeth->phy_info_timer, jiffies + HZ); err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); if (err) { @@ -4050,11 +3727,8 @@ static int ucc_geth_close(struct net_dev ucc_geth_stop(ugeth); - /* Shutdown the PHY */ - if (ugeth->mii_info->phyinfo->close) - ugeth->mii_info->phyinfo->close(ugeth->mii_info); - - kfree(ugeth->mii_info); + phy_disconnect(ugeth->phydev); + ugeth->phydev = NULL; netif_stop_queue(dev); @@ -4063,20 +3737,53 @@ static int ucc_geth_close(struct net_dev const struct ethtool_ops ucc_geth_ethtool_ops = { }; +static phy_interface_t to_phy_interface(const char *interface_type) +{ + if (strcasecmp(interface_type, "mii") == 0) + return PHY_INTERFACE_MODE_MII; + if (strcasecmp(interface_type, "gmii") == 0) + return PHY_INTERFACE_MODE_GMII; + if (strcasecmp(interface_type, "tbi") == 0) + return PHY_INTERFACE_MODE_TBI; + if (strcasecmp(interface_type, "rmii") == 0) + return PHY_INTERFACE_MODE_RMII; + if (strcasecmp(interface_type, "rgmii") == 0) + return PHY_INTERFACE_MODE_RGMII; + if (strcasecmp(interface_type, "rgmii-id") == 0) + return PHY_INTERFACE_MODE_RGMII_ID; + if (strcasecmp(interface_type, "rtbi") == 0) + return PHY_INTERFACE_MODE_RTBI; + + return PHY_INTERFACE_MODE_MII; +} + static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; struct device_node *np = ofdev->node; + struct device_node *mdio; struct net_device *dev = NULL; struct ucc_geth_private *ugeth = NULL; struct ucc_geth_info *ug_info; struct resource res; struct device_node *phy; - int err, ucc_num, phy_interface; - static int mii_mng_configured = 0; + int err, ucc_num, max_speed = 0; const phandle *ph; const unsigned int *prop; const void *mac_addr; + phy_interface_t phy_interface; + static const int enet_to_speed[] = { + SPEED_10, SPEED_10, SPEED_10, + SPEED_100, SPEED_100, SPEED_100, + SPEED_1000, SPEED_1000, SPEED_1000, SPEED_1000, + }; + static const phy_interface_t enet_to_phy_interface[] = { + PHY_INTERFACE_MODE_MII, PHY_INTERFACE_MODE_RMII, + PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_MII, + PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, + PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_RGMII, + PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RTBI, + }; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -4087,6 +3794,7 @@ static int ucc_geth_probe(struct of_devi ug_info = &ugeth_info[ucc_num]; ug_info->uf_info.ucc_num = ucc_num; + prop = get_property(np, "rx-clock", NULL); ug_info->uf_info.rx_clock = *prop; prop = get_property(np, "tx-clock", NULL); @@ -4104,13 +3812,72 @@ static int ucc_geth_probe(struct of_devi if (phy == NULL) return -ENODEV; + /* set the PHY address */ prop = get_property(phy, "reg", NULL); + if (prop == NULL) + return -1; ug_info->phy_address = *prop; - prop = get_property(phy, "interface", NULL); - ug_info->enet_interface = *prop; - ug_info->phy_interrupt = irq_of_parse_and_map(phy, 0); - ug_info->board_flags = (ug_info->phy_interrupt == NO_IRQ)? - 0:FSL_UGETH_BRD_HAS_PHY_INTR; + + /* get the phy interface type, or default to MII */ + prop = get_property(np, "interface-type", NULL); + if (!prop) { + /* handle interface property present in old trees */ + prop = get_property(phy, "interface", NULL); + if (prop != NULL) + phy_interface = enet_to_phy_interface[*prop]; + else + phy_interface = PHY_INTERFACE_MODE_MII; + } else { + phy_interface = to_phy_interface((const char *)prop); + } + + /* get speed, or derive from interface */ + prop = get_property(np, "max-speed", NULL); + if (!prop) { + /* handle interface property present in old trees */ + prop = get_property(phy, "interface", NULL); + if (prop != NULL) + max_speed = enet_to_speed[*prop]; + } else { + max_speed = *prop; + } + if (!max_speed) { + switch (phy_interface) { + case PHY_INTERFACE_MODE_GMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_TBI: + case PHY_INTERFACE_MODE_RTBI: + max_speed = SPEED_1000; + break; + default: + max_speed = SPEED_100; + break; + } + } + + if (max_speed == SPEED_1000) { + ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT; + ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT; + ug_info->uf_info.urfset = UCC_GETH_URFSET_GIGA_INIT; + ug_info->uf_info.utfs = UCC_GETH_UTFS_GIGA_INIT; + ug_info->uf_info.utfet = UCC_GETH_UTFET_GIGA_INIT; + ug_info->uf_info.utftt = UCC_GETH_UTFTT_GIGA_INIT; + } + + /* Set the bus id */ + mdio = of_get_parent(phy); + + if (mdio == NULL) + return -1; + + err = of_address_to_resource(mdio, 0, &res); + of_node_put(mdio); + + if (err) + return -1; + + ug_info->mdio_bus = res.start; printk(KERN_INFO "ucc_geth: UCC%1d at 0x%8x (irq = %d) \n", ug_info->uf_info.ucc_num + 1, ug_info->uf_info.regs, @@ -4122,43 +3889,6 @@ static int ucc_geth_probe(struct of_devi return -ENODEV; } - /* FIXME: Work around for early chip rev. */ - /* There's a bug in initial chip rev(s) in the RGMII ac */ - /* timing. */ - /* The following compensates by writing to the reserved */ - /* QE Port Output Hold Registers (CPOH1?). */ - prop = get_property(phy, "interface", NULL); - phy_interface = *prop; - if ((phy_interface == ENET_1000_RGMII) || - (phy_interface == ENET_100_RGMII) || - (phy_interface == ENET_10_RGMII)) { - struct device_node *soc; - phys_addr_t immrbase = -1; - u32 *tmp_reg; - u32 tmp_val; - - soc = of_find_node_by_type(NULL, "soc"); - if (soc) { - unsigned int size; - const void *prop = get_property(soc, "reg", &size); - immrbase = of_translate_address(soc, prop); - of_node_put(soc); - }; - - tmp_reg = (u32 *) ioremap(immrbase + 0x14A8, 0x4); - tmp_val = in_be32(tmp_reg); - if (ucc_num == 1) - out_be32(tmp_reg, tmp_val | 0x00003000); - else if (ucc_num == 2) - out_be32(tmp_reg, tmp_val | 0x0c000000); - iounmap(tmp_reg); - } - - if (!mii_mng_configured) { - ucc_set_qe_mux_mii_mng(ucc_num); - mii_mng_configured = 1; - } - /* Create an ethernet device instance */ dev = alloc_etherdev(sizeof(*ugeth)); @@ -4192,6 +3922,10 @@ #endif /* CONFIG_UGETH_NAPI */ dev->set_multicast_list = ucc_geth_set_multi; dev->ethtool_ops = &ucc_geth_ethtool_ops; + ugeth->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; + ugeth->phy_interface = phy_interface; + ugeth->max_speed = max_speed; + err = register_netdev(dev); if (err) { ugeth_err("%s: Cannot register net device, aborting.", @@ -4200,13 +3934,13 @@ #endif /* CONFIG_UGETH_NAPI */ return err; } - ugeth->ug_info = ug_info; - ugeth->dev = dev; - mac_addr = of_get_mac_address(np); if (mac_addr) memcpy(dev->dev_addr, mac_addr, 6); + ugeth->ug_info = ug_info; + ugeth->dev = dev; + return 0; } @@ -4242,19 +3976,30 @@ static struct of_platform_driver ucc_get static int __init ucc_geth_init(void) { - int i; + int i, ret; + + ret = uec_mdio_init(); + + if (ret) + return ret; printk(KERN_INFO "ucc_geth: " DRV_DESC "\n"); for (i = 0; i < 8; i++) memcpy(&(ugeth_info[i]), &ugeth_primary_info, sizeof(ugeth_primary_info)); - return of_register_platform_driver(&ucc_geth_driver); + ret = of_register_platform_driver(&ucc_geth_driver); + + if (ret) + uec_mdio_exit(); + + return ret; } static void __exit ucc_geth_exit(void) { of_unregister_platform_driver(&ucc_geth_driver); + uec_mdio_exit(); } module_init(ucc_geth_init); @@ -4262,4 +4007,5 @@ module_exit(ucc_geth_exit); MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION(DRV_DESC); +MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h index a665612..a29e1c3 100644 --- a/drivers/net/ucc_geth.h +++ b/drivers/net/ucc_geth.h @@ -28,6 +28,8 @@ #include #include #include +#include "ucc_geth_mii.h" + #define NUM_TX_QUEUES 8 #define NUM_RX_QUEUES 8 #define NUM_BDS_IN_PREFETCHED_BDS 4 @@ -36,15 +38,6 @@ #define NUM_OF_PADDRS #define ENET_INIT_PARAM_MAX_ENTRIES_RX 9 #define ENET_INIT_PARAM_MAX_ENTRIES_TX 8 -struct ucc_mii_mng { - u32 miimcfg; /* MII management configuration reg */ - u32 miimcom; /* MII management command reg */ - u32 miimadd; /* MII management address reg */ - u32 miimcon; /* MII management control reg */ - u32 miimstat; /* MII management status reg */ - u32 miimind; /* MII management indication reg */ -} __attribute__ ((packed)); - struct ucc_geth { struct ucc_fast uccf; @@ -53,7 +46,7 @@ struct ucc_geth { u32 ipgifg; /* interframe gap reg. */ u32 hafdup; /* half-duplex reg. */ u8 res1[0x10]; - struct ucc_mii_mng miimng; /* MII management structure */ + u8 miimng[0x18]; /* MII management structure moved to _mii.h */ u32 ifctl; /* interface control reg */ u32 ifstat; /* interface statux reg */ u32 macstnaddr1; /* mac station address part 1 reg */ @@ -212,6 +205,9 @@ #define UCCE_RXF (UCCE_RXF7 | UC #define UCCE_OTHER (UCCE_SCAR | UCCE_GRA | UCCE_CBPR | UCCE_BSY |\ UCCE_RXC | UCCE_TXC | UCCE_TXE) +#define UCCE_RX_EVENTS (UCCE_RXF | UCCE_BSY) +#define UCCE_TX_EVENTS (UCCE_TXB | UCCE_TXE) + /* UCC GETH UPSMR (Protocol Specific Mode Register) */ #define UPSMR_ECM 0x04000000 /* Enable CAM Miss or @@ -381,66 +377,6 @@ #define UCCS_PAU #define UCCS_MPD 0x01 /* Magic Packet Detected */ -/* UCC GETH MIIMCFG (MII Management Configuration Register) */ -#define MIIMCFG_RESET_MANAGEMENT 0x80000000 /* Reset - management */ -#define MIIMCFG_NO_PREAMBLE 0x00000010 /* Preamble - suppress */ -#define MIIMCFG_CLOCK_DIVIDE_SHIFT (31 - 31) /* clock divide - << shift */ -#define MIIMCFG_CLOCK_DIVIDE_MAX 0xf /* clock divide max val - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_2 0x00000000 /* divide by 2 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4 0x00000001 /* divide by 4 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6 0x00000002 /* divide by 6 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8 0x00000003 /* divide by 8 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 0x00000004 /* divide by 10 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14 0x00000005 /* divide by 14 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_16 0x00000008 /* divide by 16 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20 0x00000006 /* divide by 20 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28 0x00000007 /* divide by 28 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_32 0x00000009 /* divide by 32 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_48 0x0000000a /* divide by 48 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_64 0x0000000b /* divide by 64 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_80 0x0000000c /* divide by 80 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112 0x0000000d /* divide by - 112 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_160 0x0000000e /* divide by - 160 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_224 0x0000000f /* divide by - 224 */ - -/* UCC GETH MIIMCOM (MII Management Command Register) */ -#define MIIMCOM_SCAN_CYCLE 0x00000002 /* Scan cycle */ -#define MIIMCOM_READ_CYCLE 0x00000001 /* Read cycle */ - -/* UCC GETH MIIMADD (MII Management Address Register) */ -#define MIIMADD_PHY_ADDRESS_SHIFT (31 - 23) /* PHY Address - << shift */ -#define MIIMADD_PHY_REGISTER_SHIFT (31 - 31) /* PHY Register - << shift */ - -/* UCC GETH MIIMCON (MII Management Control Register) */ -#define MIIMCON_PHY_CONTROL_SHIFT (31 - 31) /* PHY Control - << shift */ -#define MIIMCON_PHY_STATUS_SHIFT (31 - 31) /* PHY Status - << shift */ - -/* UCC GETH MIIMIND (MII Management Indicator Register) */ -#define MIIMIND_NOT_VALID 0x00000004 /* Not valid */ -#define MIIMIND_SCAN 0x00000002 /* Scan in - progress */ -#define MIIMIND_BUSY 0x00000001 - /* UCC GETH IFSTAT (Interface Status Register) */ #define IFSTAT_EXCESS_DEFER 0x00000200 /* Excessive transmission @@ -931,8 +867,7 @@ #define UCC_GETH_SEND_QUEUE_QUEUE_DESCRI #define UCC_GETH_SCHEDULER_ALIGNMENT 4 /* This is a guess */ #define UCC_GETH_TX_STATISTICS_ALIGNMENT 4 /* This is a guess */ #define UCC_GETH_RX_STATISTICS_ALIGNMENT 4 /* This is a guess */ -#define UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT 4 /* This is a - guess */ +#define UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT 64 #define UCC_GETH_RX_BD_QUEUES_ALIGNMENT 8 /* This is a guess */ #define UCC_GETH_RX_PREFETCHED_BDS_ALIGNMENT 128 /* This is a guess */ #define UCC_GETH_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT 4 /* This @@ -1009,15 +944,6 @@ #define UCC_GETH_UPSMR_INIT register */ #define UCC_GETH_MACCFG1_INIT 0 #define UCC_GETH_MACCFG2_INIT (MACCFG2_RESERVED_1) -#define UCC_GETH_MIIMCFG_MNGMNT_CLC_DIV_INIT \ - (MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112) - -/* Ethernet speed */ -enum enet_speed { - ENET_SPEED_10BT, /* 10 Base T */ - ENET_SPEED_100BT, /* 100 Base T */ - ENET_SPEED_1000BT /* 1000 Base T */ -}; /* Ethernet Address Type. */ enum enet_addr_type { @@ -1026,22 +952,6 @@ enum enet_addr_type { ENET_ADDR_TYPE_BROADCAST }; -/* TBI / MII Set Register */ -enum enet_tbi_mii_reg { - ENET_TBI_MII_CR = 0x00, /* Control (CR ) */ - ENET_TBI_MII_SR = 0x01, /* Status (SR ) */ - ENET_TBI_MII_ANA = 0x04, /* AN advertisement (ANA ) */ - ENET_TBI_MII_ANLPBPA = 0x05, /* AN link partner base page ability - (ANLPBPA) */ - ENET_TBI_MII_ANEX = 0x06, /* AN expansion (ANEX ) */ - ENET_TBI_MII_ANNPT = 0x07, /* AN next page transmit (ANNPT ) */ - ENET_TBI_MII_ANLPANP = 0x08, /* AN link partner ability next page - (ANLPANP) */ - ENET_TBI_MII_EXST = 0x0F, /* Extended status (EXST ) */ - ENET_TBI_MII_JD = 0x10, /* Jitter diagnostics (JD ) */ - ENET_TBI_MII_TBICON = 0x11 /* TBI control (TBICON ) */ -}; - /* UCC GETH 82xx Ethernet Address Recognition Location */ enum ucc_geth_enet_address_recognition_location { UCC_GETH_ENET_ADDRESS_RECOGNITION_LOCATION_STATION_ADDRESS,/* station @@ -1239,8 +1149,7 @@ struct ucc_geth_info { u16 pausePeriod; u16 extensionField; u8 phy_address; - u32 board_flags; - u32 phy_interrupt; + u32 mdio_bus; u8 weightfactor[NUM_TX_QUEUES]; u8 interruptcoalescingmaxvalue[NUM_RX_QUEUES]; u8 l2qt[UCC_GETH_VLAN_PRIORITY_MAX]; @@ -1249,7 +1158,6 @@ struct ucc_geth_info { u8 iphoffset[TX_IP_OFFSET_ENTRY_MAX]; u16 bdRingLenTx[NUM_TX_QUEUES]; u16 bdRingLenRx[NUM_RX_QUEUES]; - enum enet_interface enet_interface; enum ucc_geth_num_of_station_addresses numStationAddresses; enum qe_fltr_largest_external_tbl_lookup_key_size largestexternallookupkeysize; @@ -1326,9 +1234,11 @@ struct ucc_geth_private { /* index of the first skb which hasn't been transmitted yet. */ u16 skb_dirtytx[NUM_TX_QUEUES]; - struct work_struct tq; - struct timer_list phy_info_timer; struct ugeth_mii_info *mii_info; + struct phy_device *phydev; + phy_interface_t phy_interface; + int max_speed; + uint32_t msg_enable; int oldspeed; int oldduplex; int oldlink; diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c new file mode 100644 index 0000000..73b5a53 --- /dev/null +++ b/drivers/net/ucc_geth_mii.c @@ -0,0 +1,279 @@ +/* + * drivers/net/ucc_geth_mii.c + * + * Gianfar Ethernet Driver -- MIIM bus implementation + * Provides Bus interface for MIIM regs + * + * Author: Li Yang + * + * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ucc_geth_mii.h" +#include "ucc_geth.h" + +#define DEBUG +#ifdef DEBUG +#define vdbg(format, arg...) printk(KERN_DEBUG , format "\n" , ## arg) +#else +#define vdbg(format, arg...) do {} while(0) +#endif + +#define DRV_DESC "QE UCC Ethernet Controller MII Bus" +#define DRV_NAME "fsl-uec_mdio" + +/* Write value to the PHY for this device to the register at regnum, */ +/* waiting until the write is done before it returns. All PHY */ +/* configuration has to be done through the master UEC MIIM regs */ +int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) +{ + struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; + + /* Setting up the MII Mangement Address Register */ + out_be32(®s->miimadd, + (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum); + + /* Setting up the MII Mangement Control Register with the value */ + out_be32(®s->miimcon, value); + + /* Wait till MII management write is complete */ + while ((in_be32(®s->miimind)) & MIIMIND_BUSY) + cpu_relax(); + + return 0; +} + +/* Reads from register regnum in the PHY for device dev, */ +/* returning the value. Clears miimcom first. All PHY */ +/* configuration has to be done through the TSEC1 MIIM regs */ +int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; + u16 value; + + /* Setting up the MII Mangement Address Register */ + out_be32(®s->miimadd, + (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum); + + /* Clear miimcom, perform an MII management read cycle */ + out_be32(®s->miimcom, 0); + out_be32(®s->miimcom, MIIMCOM_READ_CYCLE); + + /* Wait till MII management write is complete */ + while ((in_be32(®s->miimind)) & (MIIMIND_BUSY | MIIMIND_NOT_VALID)) + cpu_relax(); + + /* Read MII management status */ + value = in_be32(®s->miimstat); + + return value; +} + +/* Reset the MIIM registers, and wait for the bus to free */ +int uec_mdio_reset(struct mii_bus *bus) +{ + struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; + unsigned int timeout = PHY_INIT_TIMEOUT; + + spin_lock_bh(&bus->mdio_lock); + + /* Reset the management interface */ + out_be32(®s->miimcfg, MIIMCFG_RESET_MANAGEMENT); + + /* Setup the MII Mgmt clock speed */ + out_be32(®s->miimcfg, MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112); + + /* Wait until the bus is free */ + while ((in_be32(®s->miimind) & MIIMIND_BUSY) && timeout--) + cpu_relax(); + + spin_unlock_bh(&bus->mdio_lock); + + if (timeout <= 0) { + printk(KERN_ERR "%s: The MII Bus is stuck!\n", bus->name); + return -EBUSY; + } + + return 0; +} + +static int uec_mdio_probe(struct of_device *ofdev, const struct of_device_id *match) +{ + struct device *device = &ofdev->dev; + struct device_node *np = ofdev->node, *tempnp = NULL; + struct device_node *child = NULL; + struct ucc_mii_mng __iomem *regs; + struct mii_bus *new_bus; + struct resource res; + int k, err = 0; + + new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + + if (NULL == new_bus) + return -ENOMEM; + + new_bus->name = "UCC Ethernet Controller MII Bus"; + new_bus->read = &uec_mdio_read; + new_bus->write = &uec_mdio_write; + new_bus->reset = &uec_mdio_reset; + + memset(&res, 0, sizeof(res)); + + err = of_address_to_resource(np, 0, &res); + if (err) + goto reg_map_fail; + + new_bus->id = res.start; + + new_bus->irq = kmalloc(32 * sizeof(int), GFP_KERNEL); + + if (NULL == new_bus->irq) { + err = -ENOMEM; + goto reg_map_fail; + } + + for (k = 0; k < 32; k++) + new_bus->irq[k] = PHY_POLL; + + while ((child = of_get_next_child(np, child)) != NULL) { + int irq = irq_of_parse_and_map(child, 0); + if (irq != NO_IRQ) { + const u32 *id = get_property(child, "reg", NULL); + new_bus->irq[*id] = irq; + } + } + + /* Set the base address */ + regs = ioremap(res.start, sizeof(struct ucc_mii_mng)); + + if (NULL == regs) { + err = -ENOMEM; + goto ioremap_fail; + } + + new_bus->priv = (void __force *)regs; + + new_bus->dev = device; + dev_set_drvdata(device, new_bus); + + /* Read MII management master from device tree */ + while ((tempnp = of_find_compatible_node(tempnp, "network", "ucc_geth")) + != NULL) { + struct resource tempres; + + err = of_address_to_resource(tempnp, 0, &tempres); + if (err) + goto bus_register_fail; + + /* if our mdio regs fall within this UCC regs range */ + if ((res.start >= tempres.start) && + (res.end <= tempres.end)) { + /* set this UCC to be the MII master */ + const u32 *id = get_property(tempnp, "device-id", NULL); + if (id == NULL) + goto bus_register_fail; + + ucc_set_qe_mux_mii_mng(*id - 1); + + /* assign the TBI an address which won't + * conflict with the PHYs */ + out_be32(®s->utbipar, UTBIPAR_INIT_TBIPA); + break; + } + } + + err = mdiobus_register(new_bus); + if (0 != err) { + printk(KERN_ERR "%s: Cannot register as MDIO bus\n", + new_bus->name); + goto bus_register_fail; + } + + return 0; + +bus_register_fail: + iounmap(regs); +ioremap_fail: + kfree(new_bus->irq); +reg_map_fail: + kfree(new_bus); + + return err; +} + +int uec_mdio_remove(struct of_device *ofdev) +{ + struct device *device = &ofdev->dev; + struct mii_bus *bus = dev_get_drvdata(device); + + mdiobus_unregister(bus); + + dev_set_drvdata(device, NULL); + + iounmap((void __iomem *)bus->priv); + bus->priv = NULL; + kfree(bus); + + return 0; +} + +static struct of_device_id uec_mdio_match[] = { + { + .type = "mdio", + .compatible = "ucc_geth_phy", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, uec_mdio_match); + +static struct of_platform_driver uec_mdio_driver = { + .name = DRV_NAME, + .probe = uec_mdio_probe, + .remove = uec_mdio_remove, + .match_table = uec_mdio_match, +}; + +int __init uec_mdio_init(void) +{ + return of_register_platform_driver(&uec_mdio_driver); +} + +void __exit uec_mdio_exit(void) +{ + of_unregister_platform_driver(&uec_mdio_driver); +} diff --git a/drivers/net/ucc_geth_mii.h b/drivers/net/ucc_geth_mii.h new file mode 100644 index 0000000..98430fe --- /dev/null +++ b/drivers/net/ucc_geth_mii.h @@ -0,0 +1,100 @@ +/* + * drivers/net/ucc_geth_mii.h + * + * Gianfar Ethernet Driver -- MII Management Bus Implementation + * Driver for the MDIO bus controller in the Gianfar register space + * + * Author: Andy Fleming + * Maintainer: Kumar Gala + * + * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. + * + * 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. + * + */ +#ifndef __UEC_MII_H +#define __UEC_MII_H + +/* UCC GETH MIIMCFG (MII Management Configuration Register) */ +#define MIIMCFG_RESET_MANAGEMENT 0x80000000 /* Reset + management */ +#define MIIMCFG_NO_PREAMBLE 0x00000010 /* Preamble + suppress */ +#define MIIMCFG_CLOCK_DIVIDE_SHIFT (31 - 31) /* clock divide + << shift */ +#define MIIMCFG_CLOCK_DIVIDE_MAX 0xf /* max clock divide */ +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_2 0x00000000 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4 0x00000001 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6 0x00000002 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8 0x00000003 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 0x00000004 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14 0x00000005 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_16 0x00000008 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20 0x00000006 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28 0x00000007 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_32 0x00000009 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_48 0x0000000a +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_64 0x0000000b +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_80 0x0000000c +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112 0x0000000d +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_160 0x0000000e +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_224 0x0000000f + +/* UCC GETH MIIMCOM (MII Management Command Register) */ +#define MIIMCOM_SCAN_CYCLE 0x00000002 /* Scan cycle */ +#define MIIMCOM_READ_CYCLE 0x00000001 /* Read cycle */ + +/* UCC GETH MIIMADD (MII Management Address Register) */ +#define MIIMADD_PHY_ADDRESS_SHIFT (31 - 23) /* PHY Address + << shift */ +#define MIIMADD_PHY_REGISTER_SHIFT (31 - 31) /* PHY Register + << shift */ + +/* UCC GETH MIIMCON (MII Management Control Register) */ +#define MIIMCON_PHY_CONTROL_SHIFT (31 - 31) /* PHY Control + << shift */ +#define MIIMCON_PHY_STATUS_SHIFT (31 - 31) /* PHY Status + << shift */ + +/* UCC GETH MIIMIND (MII Management Indicator Register) */ +#define MIIMIND_NOT_VALID 0x00000004 /* Not valid */ +#define MIIMIND_SCAN 0x00000002 /* Scan in + progress */ +#define MIIMIND_BUSY 0x00000001 + +/* Initial TBI Physical Address */ +#define UTBIPAR_INIT_TBIPA 0x1f + +struct ucc_mii_mng { + u32 miimcfg; /* MII management configuration reg */ + u32 miimcom; /* MII management command reg */ + u32 miimadd; /* MII management address reg */ + u32 miimcon; /* MII management control reg */ + u32 miimstat; /* MII management status reg */ + u32 miimind; /* MII management indication reg */ + u8 notcare[28]; /* Space holder */ + u32 utbipar; /* TBI phy address reg */ +} __attribute__ ((packed)); + +/* TBI / MII Set Register */ +enum enet_tbi_mii_reg { + ENET_TBI_MII_CR = 0x00, /* Control */ + ENET_TBI_MII_SR = 0x01, /* Status */ + ENET_TBI_MII_ANA = 0x04, /* AN advertisement */ + ENET_TBI_MII_ANLPBPA = 0x05, /* AN link partner base page ability */ + ENET_TBI_MII_ANEX = 0x06, /* AN expansion */ + ENET_TBI_MII_ANNPT = 0x07, /* AN next page transmit */ + ENET_TBI_MII_ANLPANP = 0x08, /* AN link partner ability next page */ + ENET_TBI_MII_EXST = 0x0F, /* Extended status */ + ENET_TBI_MII_JD = 0x10, /* Jitter diagnostics */ + ENET_TBI_MII_TBICON = 0x11 /* TBI control */ +}; + +int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum); +int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value); +int __init uec_mdio_init(void); +void __exit uec_mdio_exit(void); +#endif /* __UEC_MII_H */ diff --git a/drivers/net/ucc_geth_phy.c b/drivers/net/ucc_geth_phy.c deleted file mode 100644 index 9373d89..0000000 --- a/drivers/net/ucc_geth_phy.c +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Author: Shlomi Gridish - * - * Description: - * UCC GETH Driver -- PHY handling - * - * Changelog: - * Jun 28, 2006 Li Yang - * - Rearrange code and style fixes - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ucc_geth.h" -#include "ucc_geth_phy.h" - -#define ugphy_printk(level, format, arg...) \ - printk(level format "\n", ## arg) - -#define ugphy_dbg(format, arg...) \ - ugphy_printk(KERN_DEBUG, format , ## arg) -#define ugphy_err(format, arg...) \ - ugphy_printk(KERN_ERR, format , ## arg) -#define ugphy_info(format, arg...) \ - ugphy_printk(KERN_INFO, format , ## arg) -#define ugphy_warn(format, arg...) \ - ugphy_printk(KERN_WARNING, format , ## arg) - -#ifdef UGETH_VERBOSE_DEBUG -#define ugphy_vdbg ugphy_dbg -#else -#define ugphy_vdbg(fmt, args...) do { } while (0) -#endif /* UGETH_VERBOSE_DEBUG */ - -static void config_genmii_advert(struct ugeth_mii_info *mii_info); -static void genmii_setup_forced(struct ugeth_mii_info *mii_info); -static void genmii_restart_aneg(struct ugeth_mii_info *mii_info); -static int gbit_config_aneg(struct ugeth_mii_info *mii_info); -static int genmii_config_aneg(struct ugeth_mii_info *mii_info); -static int genmii_update_link(struct ugeth_mii_info *mii_info); -static int genmii_read_status(struct ugeth_mii_info *mii_info); - -static u16 ucc_geth_phy_read(struct ugeth_mii_info *mii_info, u16 regnum) -{ - u16 retval; - unsigned long flags; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irqsave(&mii_info->mdio_lock, flags); - retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum); - spin_unlock_irqrestore(&mii_info->mdio_lock, flags); - - return retval; -} - -static void ucc_geth_phy_write(struct ugeth_mii_info *mii_info, u16 regnum, u16 val) -{ - unsigned long flags; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irqsave(&mii_info->mdio_lock, flags); - mii_info->mdio_write(mii_info->dev, mii_info->mii_id, regnum, val); - spin_unlock_irqrestore(&mii_info->mdio_lock, flags); -} - -/* Write value to the PHY for this device to the register at regnum, */ -/* waiting until the write is done before it returns. All PHY */ -/* configuration has to be done through the TSEC1 MIIM regs */ -void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value) -{ - struct ucc_geth_private *ugeth = netdev_priv(dev); - struct ucc_mii_mng *mii_regs; - enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg) regnum; - u32 tmp_reg; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irq(&ugeth->lock); - - mii_regs = ugeth->mii_info->mii_regs; - - /* Set this UCC to be the master of the MII managment */ - ucc_set_qe_mux_mii_mng(ugeth->ug_info->uf_info.ucc_num); - - /* Stop the MII management read cycle */ - out_be32(&mii_regs->miimcom, 0); - /* Setting up the MII Mangement Address Register */ - tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; - out_be32(&mii_regs->miimadd, tmp_reg); - - /* Setting up the MII Mangement Control Register with the value */ - out_be32(&mii_regs->miimcon, (u32) value); - - /* Wait till MII management write is complete */ - while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY) - cpu_relax(); - - spin_unlock_irq(&ugeth->lock); - - udelay(10000); -} - -/* Reads from register regnum in the PHY for device dev, */ -/* returning the value. Clears miimcom first. All PHY */ -/* configuration has to be done through the TSEC1 MIIM regs */ -int read_phy_reg(struct net_device *dev, int mii_id, int regnum) -{ - struct ucc_geth_private *ugeth = netdev_priv(dev); - struct ucc_mii_mng *mii_regs; - enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg) regnum; - u32 tmp_reg; - u16 value; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irq(&ugeth->lock); - - mii_regs = ugeth->mii_info->mii_regs; - - /* Setting up the MII Mangement Address Register */ - tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; - out_be32(&mii_regs->miimadd, tmp_reg); - - /* Perform an MII management read cycle */ - out_be32(&mii_regs->miimcom, MIIMCOM_READ_CYCLE); - - /* Wait till MII management write is complete */ - while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY) - cpu_relax(); - - udelay(10000); - - /* Read MII management status */ - value = (u16) in_be32(&mii_regs->miimstat); - out_be32(&mii_regs->miimcom, 0); - if (value == 0xffff) - ugphy_warn("read wrong value : mii_id %d,mii_reg %d, base %08x", - mii_id, mii_reg, (u32) & (mii_regs->miimcfg)); - - spin_unlock_irq(&ugeth->lock); - - return (value); -} - -void mii_clear_phy_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->phyinfo->ack_interrupt) - mii_info->phyinfo->ack_interrupt(mii_info); -} - -void mii_configure_phy_interrupt(struct ugeth_mii_info *mii_info, - u32 interrupts) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - mii_info->interrupts = interrupts; - if (mii_info->phyinfo->config_intr) - mii_info->phyinfo->config_intr(mii_info); -} - -/* Writes MII_ADVERTISE with the appropriate values, after - * sanitizing advertise to make sure only supported features - * are advertised - */ -static void config_genmii_advert(struct ugeth_mii_info *mii_info) -{ - u32 advertise; - u16 adv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Only allow advertising what this PHY supports */ - mii_info->advertising &= mii_info->phyinfo->features; - advertise = mii_info->advertising; - - /* Setup standard advertisement */ - adv = ucc_geth_phy_read(mii_info, MII_ADVERTISE); - adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); - if (advertise & ADVERTISED_10baseT_Half) - adv |= ADVERTISE_10HALF; - if (advertise & ADVERTISED_10baseT_Full) - adv |= ADVERTISE_10FULL; - if (advertise & ADVERTISED_100baseT_Half) - adv |= ADVERTISE_100HALF; - if (advertise & ADVERTISED_100baseT_Full) - adv |= ADVERTISE_100FULL; - ucc_geth_phy_write(mii_info, MII_ADVERTISE, adv); -} - -static void genmii_setup_forced(struct ugeth_mii_info *mii_info) -{ - u16 ctrl; - u32 features = mii_info->phyinfo->features; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - ctrl = ucc_geth_phy_read(mii_info, MII_BMCR); - - ctrl &= - ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); - ctrl |= BMCR_RESET; - - switch (mii_info->speed) { - case SPEED_1000: - if (features & (SUPPORTED_1000baseT_Half - | SUPPORTED_1000baseT_Full)) { - ctrl |= BMCR_SPEED1000; - break; - } - mii_info->speed = SPEED_100; - case SPEED_100: - if (features & (SUPPORTED_100baseT_Half - | SUPPORTED_100baseT_Full)) { - ctrl |= BMCR_SPEED100; - break; - } - mii_info->speed = SPEED_10; - case SPEED_10: - if (features & (SUPPORTED_10baseT_Half - | SUPPORTED_10baseT_Full)) - break; - default: /* Unsupported speed! */ - ugphy_err("%s: Bad speed!", mii_info->dev->name); - break; - } - - ucc_geth_phy_write(mii_info, MII_BMCR, ctrl); -} - -/* Enable and Restart Autonegotiation */ -static void genmii_restart_aneg(struct ugeth_mii_info *mii_info) -{ - u16 ctl; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - ctl = ucc_geth_phy_read(mii_info, MII_BMCR); - ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); - ucc_geth_phy_write(mii_info, MII_BMCR, ctl); -} - -static int gbit_config_aneg(struct ugeth_mii_info *mii_info) -{ - u16 adv; - u32 advertise; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->autoneg) { - /* Configure the ADVERTISE register */ - config_genmii_advert(mii_info); - advertise = mii_info->advertising; - - adv = ucc_geth_phy_read(mii_info, MII_1000BASETCONTROL); - adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP | - MII_1000BASETCONTROL_HALFDUPLEXCAP); - if (advertise & SUPPORTED_1000baseT_Half) - adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP; - if (advertise & SUPPORTED_1000baseT_Full) - adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP; - ucc_geth_phy_write(mii_info, MII_1000BASETCONTROL, adv); - - /* Start/Restart aneg */ - genmii_restart_aneg(mii_info); - } else - genmii_setup_forced(mii_info); - - return 0; -} - -static int genmii_config_aneg(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->autoneg) { - config_genmii_advert(mii_info); - genmii_restart_aneg(mii_info); - } else - genmii_setup_forced(mii_info); - - return 0; -} - -static int genmii_update_link(struct ugeth_mii_info *mii_info) -{ - u16 status; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Do a fake read */ - ucc_geth_phy_read(mii_info, MII_BMSR); - - /* Read link and autonegotiation status */ - status = ucc_geth_phy_read(mii_info, MII_BMSR); - if ((status & BMSR_LSTATUS) == 0) - mii_info->link = 0; - else - mii_info->link = 1; - - /* If we are autonegotiating, and not done, - * return an error */ - if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE)) - return -EAGAIN; - - return 0; -} - -static int genmii_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - if (mii_info->autoneg) { - status = ucc_geth_phy_read(mii_info, MII_LPA); - - if (status & (LPA_10FULL | LPA_100FULL)) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - if (status & (LPA_100FULL | LPA_100HALF)) - mii_info->speed = SPEED_100; - else - mii_info->speed = SPEED_10; - mii_info->pause = 0; - } - /* On non-aneg, we assume what we put in BMCR is the speed, - * though magic-aneg shouldn't prevent this case from occurring - */ - - return 0; -} - -static int marvell_init(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - ucc_geth_phy_write(mii_info, 0x14, 0x0cd2); - ucc_geth_phy_write(mii_info, 0x1b, - (ucc_geth_phy_read(mii_info, 0x1b) & ~0x000f) | 0x000b); - ucc_geth_phy_write(mii_info, MII_BMCR, - ucc_geth_phy_read(mii_info, MII_BMCR) | BMCR_RESET); - msleep(4000); - - return 0; -} - -static int marvell_config_aneg(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* The Marvell PHY has an errata which requires - * that certain registers get written in order - * to restart autonegotiation */ - ucc_geth_phy_write(mii_info, MII_BMCR, BMCR_RESET); - - ucc_geth_phy_write(mii_info, 0x1d, 0x1f); - ucc_geth_phy_write(mii_info, 0x1e, 0x200c); - ucc_geth_phy_write(mii_info, 0x1d, 0x5); - ucc_geth_phy_write(mii_info, 0x1e, 0); - ucc_geth_phy_write(mii_info, 0x1e, 0x100); - - gbit_config_aneg(mii_info); - - return 0; -} - -static int marvell_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - /* If the link is up, read the speed and duplex */ - /* If we aren't autonegotiating, assume speeds - * are as set */ - if (mii_info->autoneg && mii_info->link) { - int speed; - status = ucc_geth_phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS); - - /* Get the duplexity */ - if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - - /* Get the speed */ - speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK; - switch (speed) { - case MII_M1011_PHY_SPEC_STATUS_1000: - mii_info->speed = SPEED_1000; - break; - case MII_M1011_PHY_SPEC_STATUS_100: - mii_info->speed = SPEED_100; - break; - default: - mii_info->speed = SPEED_10; - break; - } - mii_info->pause = 0; - } - - return 0; -} - -static int marvell_ack_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Clear the interrupts by reading the reg */ - ucc_geth_phy_read(mii_info, MII_M1011_IEVENT); - - return 0; -} - -static int marvell_config_intr(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->interrupts == MII_INTERRUPT_ENABLED) - ucc_geth_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT); - else - ucc_geth_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); - - return 0; -} - -static int cis820x_init(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - ucc_geth_phy_write(mii_info, MII_CIS8201_AUX_CONSTAT, - MII_CIS8201_AUXCONSTAT_INIT); - ucc_geth_phy_write(mii_info, MII_CIS8201_EXT_CON1, MII_CIS8201_EXTCON1_INIT); - - return 0; -} - -static int cis820x_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - /* If the link is up, read the speed and duplex */ - /* If we aren't autonegotiating, assume speeds - * are as set */ - if (mii_info->autoneg && mii_info->link) { - int speed; - - status = ucc_geth_phy_read(mii_info, MII_CIS8201_AUX_CONSTAT); - if (status & MII_CIS8201_AUXCONSTAT_DUPLEX) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - - speed = status & MII_CIS8201_AUXCONSTAT_SPEED; - - switch (speed) { - case MII_CIS8201_AUXCONSTAT_GBIT: - mii_info->speed = SPEED_1000; - break; - case MII_CIS8201_AUXCONSTAT_100: - mii_info->speed = SPEED_100; - break; - default: - mii_info->speed = SPEED_10; - break; - } - } - - return 0; -} - -static int cis820x_ack_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - ucc_geth_phy_read(mii_info, MII_CIS8201_ISTAT); - - return 0; -} - -static int cis820x_config_intr(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->interrupts == MII_INTERRUPT_ENABLED) - ucc_geth_phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK); - else - ucc_geth_phy_write(mii_info, MII_CIS8201_IMASK, 0); - - return 0; -} - -#define DM9161_DELAY 10 - -static int dm9161_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - /* If the link is up, read the speed and duplex */ - /* If we aren't autonegotiating, assume speeds - * are as set */ - if (mii_info->autoneg && mii_info->link) { - status = ucc_geth_phy_read(mii_info, MII_DM9161_SCSR); - if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H)) - mii_info->speed = SPEED_100; - else - mii_info->speed = SPEED_10; - - if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F)) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - } - - return 0; -} - -static int dm9161_config_aneg(struct ugeth_mii_info *mii_info) -{ - struct dm9161_private *priv = mii_info->priv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (0 == priv->resetdone) - return -EAGAIN; - - return 0; -} - -static void dm9161_timer(unsigned long data) -{ - struct ugeth_mii_info *mii_info = (struct ugeth_mii_info *)data; - struct dm9161_private *priv = mii_info->priv; - u16 status = ucc_geth_phy_read(mii_info, MII_BMSR); - - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (status & BMSR_ANEGCOMPLETE) { - priv->resetdone = 1; - } else - mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ); -} - -static int dm9161_init(struct ugeth_mii_info *mii_info) -{ - struct dm9161_private *priv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Allocate the private data structure */ - priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL); - - if (NULL == priv) - return -ENOMEM; - - mii_info->priv = priv; - - /* Reset is not done yet */ - priv->resetdone = 0; - - ucc_geth_phy_write(mii_info, MII_BMCR, - ucc_geth_phy_read(mii_info, MII_BMCR) | BMCR_RESET); - - ucc_geth_phy_write(mii_info, MII_BMCR, - ucc_geth_phy_read(mii_info, MII_BMCR) & ~BMCR_ISOLATE); - - config_genmii_advert(mii_info); - /* Start/Restart aneg */ - genmii_config_aneg(mii_info); - - /* Start a timer for DM9161_DELAY seconds to wait - * for the PHY to be ready */ - init_timer(&priv->timer); - priv->timer.function = &dm9161_timer; - priv->timer.data = (unsigned long)mii_info; - mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ); - - return 0; -} - -static void dm9161_close(struct ugeth_mii_info *mii_info) -{ - struct dm9161_private *priv = mii_info->priv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - del_timer_sync(&priv->timer); - kfree(priv); -} - -static int dm9161_ack_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Clear the interrupts by reading the reg */ - ucc_geth_phy_read(mii_info, MII_DM9161_INTR); - - - return 0; -} - -static int dm9161_config_intr(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->interrupts == MII_INTERRUPT_ENABLED) - ucc_geth_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT); - else - ucc_geth_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP); - - return 0; -} - -/* Cicada 820x */ -static struct phy_info phy_info_cis820x = { - .phy_id = 0x000fc440, - .name = "Cicada Cis8204", - .phy_id_mask = 0x000fffc0, - .features = MII_GBIT_FEATURES, - .init = &cis820x_init, - .config_aneg = &gbit_config_aneg, - .read_status = &cis820x_read_status, - .ack_interrupt = &cis820x_ack_interrupt, - .config_intr = &cis820x_config_intr, -}; - -static struct phy_info phy_info_dm9161 = { - .phy_id = 0x0181b880, - .phy_id_mask = 0x0ffffff0, - .name = "Davicom DM9161E", - .init = dm9161_init, - .config_aneg = dm9161_config_aneg, - .read_status = dm9161_read_status, - .close = dm9161_close, -}; - -static struct phy_info phy_info_dm9161a = { - .phy_id = 0x0181b8a0, - .phy_id_mask = 0x0ffffff0, - .name = "Davicom DM9161A", - .features = MII_BASIC_FEATURES, - .init = dm9161_init, - .config_aneg = dm9161_config_aneg, - .read_status = dm9161_read_status, - .ack_interrupt = dm9161_ack_interrupt, - .config_intr = dm9161_config_intr, - .close = dm9161_close, -}; - -static struct phy_info phy_info_marvell = { - .phy_id = 0x01410c00, - .phy_id_mask = 0xffffff00, - .name = "Marvell 88E11x1", - .features = MII_GBIT_FEATURES, - .init = &marvell_init, - .config_aneg = &marvell_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, -}; - -static struct phy_info phy_info_genmii = { - .phy_id = 0x00000000, - .phy_id_mask = 0x00000000, - .name = "Generic MII", - .features = MII_BASIC_FEATURES, - .config_aneg = genmii_config_aneg, - .read_status = genmii_read_status, -}; - -static struct phy_info *phy_info[] = { - &phy_info_cis820x, - &phy_info_marvell, - &phy_info_dm9161, - &phy_info_dm9161a, - &phy_info_genmii, - NULL -}; - -/* Use the PHY ID registers to determine what type of PHY is attached - * to device dev. return a struct phy_info structure describing that PHY - */ -struct phy_info *get_phy_info(struct ugeth_mii_info *mii_info) -{ - u16 phy_reg; - u32 phy_ID; - int i; - struct phy_info *theInfo = NULL; - struct net_device *dev = mii_info->dev; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Grab the bits from PHYIR1, and put them in the upper half */ - phy_reg = ucc_geth_phy_read(mii_info, MII_PHYSID1); - phy_ID = (phy_reg & 0xffff) << 16; - - /* Grab the bits from PHYIR2, and put them in the lower half */ - phy_reg = ucc_geth_phy_read(mii_info, MII_PHYSID2); - phy_ID |= (phy_reg & 0xffff); - - /* loop through all the known PHY types, and find one that */ - /* matches the ID we read from the PHY. */ - for (i = 0; phy_info[i]; i++) - if (phy_info[i]->phy_id == (phy_ID & phy_info[i]->phy_id_mask)){ - theInfo = phy_info[i]; - break; - } - - /* This shouldn't happen, as we have generic PHY support */ - if (theInfo == NULL) { - ugphy_info("%s: PHY id %x is not supported!", dev->name, - phy_ID); - return NULL; - } else { - ugphy_info("%s: PHY is %s (%x)", dev->name, theInfo->name, - phy_ID); - } - - return theInfo; -} diff --git a/drivers/net/ucc_geth_phy.h b/drivers/net/ucc_geth_phy.h deleted file mode 100644 index f574078..0000000 --- a/drivers/net/ucc_geth_phy.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Author: Shlomi Gridish - * - * Description: - * UCC GETH Driver -- PHY handling - * - * Changelog: - * Jun 28, 2006 Li Yang - * - Rearrange code and style fixes - * - * 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. - * - */ -#ifndef __UCC_GETH_PHY_H__ -#define __UCC_GETH_PHY_H__ - -#define MII_end ((u32)-2) -#define MII_read ((u32)-1) - -#define MIIMIND_BUSY 0x00000001 -#define MIIMIND_NOTVALID 0x00000004 - -#define UGETH_AN_TIMEOUT 2000 - -/* 1000BT control (Marvell & BCM54xx at least) */ -#define MII_1000BASETCONTROL 0x09 -#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200 -#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100 - -/* Cicada Extended Control Register 1 */ -#define MII_CIS8201_EXT_CON1 0x17 -#define MII_CIS8201_EXTCON1_INIT 0x0000 - -/* Cicada Interrupt Mask Register */ -#define MII_CIS8201_IMASK 0x19 -#define MII_CIS8201_IMASK_IEN 0x8000 -#define MII_CIS8201_IMASK_SPEED 0x4000 -#define MII_CIS8201_IMASK_LINK 0x2000 -#define MII_CIS8201_IMASK_DUPLEX 0x1000 -#define MII_CIS8201_IMASK_MASK 0xf000 - -/* Cicada Interrupt Status Register */ -#define MII_CIS8201_ISTAT 0x1a -#define MII_CIS8201_ISTAT_STATUS 0x8000 -#define MII_CIS8201_ISTAT_SPEED 0x4000 -#define MII_CIS8201_ISTAT_LINK 0x2000 -#define MII_CIS8201_ISTAT_DUPLEX 0x1000 - -/* Cicada Auxiliary Control/Status Register */ -#define MII_CIS8201_AUX_CONSTAT 0x1c -#define MII_CIS8201_AUXCONSTAT_INIT 0x0004 -#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020 -#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018 -#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010 -#define MII_CIS8201_AUXCONSTAT_100 0x0008 - -/* 88E1011 PHY Status Register */ -#define MII_M1011_PHY_SPEC_STATUS 0x11 -#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000 -#define MII_M1011_PHY_SPEC_STATUS_100 0x4000 -#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000 -#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000 -#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800 -#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400 - -#define MII_M1011_IEVENT 0x13 -#define MII_M1011_IEVENT_CLEAR 0x0000 - -#define MII_M1011_IMASK 0x12 -#define MII_M1011_IMASK_INIT 0x6400 -#define MII_M1011_IMASK_CLEAR 0x0000 - -#define MII_DM9161_SCR 0x10 -#define MII_DM9161_SCR_INIT 0x0610 - -/* DM9161 Specified Configuration and Status Register */ -#define MII_DM9161_SCSR 0x11 -#define MII_DM9161_SCSR_100F 0x8000 -#define MII_DM9161_SCSR_100H 0x4000 -#define MII_DM9161_SCSR_10F 0x2000 -#define MII_DM9161_SCSR_10H 0x1000 - -/* DM9161 Interrupt Register */ -#define MII_DM9161_INTR 0x15 -#define MII_DM9161_INTR_PEND 0x8000 -#define MII_DM9161_INTR_DPLX_MASK 0x0800 -#define MII_DM9161_INTR_SPD_MASK 0x0400 -#define MII_DM9161_INTR_LINK_MASK 0x0200 -#define MII_DM9161_INTR_MASK 0x0100 -#define MII_DM9161_INTR_DPLX_CHANGE 0x0010 -#define MII_DM9161_INTR_SPD_CHANGE 0x0008 -#define MII_DM9161_INTR_LINK_CHANGE 0x0004 -#define MII_DM9161_INTR_INIT 0x0000 -#define MII_DM9161_INTR_STOP \ -(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \ - | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK) - -/* DM9161 10BT Configuration/Status */ -#define MII_DM9161_10BTCSR 0x12 -#define MII_DM9161_10BTCSR_INIT 0x7800 - -#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \ - SUPPORTED_10baseT_Full | \ - SUPPORTED_100baseT_Half | \ - SUPPORTED_100baseT_Full | \ - SUPPORTED_Autoneg | \ - SUPPORTED_TP | \ - SUPPORTED_MII) - -#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \ - SUPPORTED_1000baseT_Half | \ - SUPPORTED_1000baseT_Full) - -#define MII_READ_COMMAND 0x00000001 - -#define MII_INTERRUPT_DISABLED 0x0 -#define MII_INTERRUPT_ENABLED 0x1 -/* Taken from mii_if_info and sungem_phy.h */ -struct ugeth_mii_info { - /* Information about the PHY type */ - /* And management functions */ - struct phy_info *phyinfo; - - struct ucc_mii_mng *mii_regs; - - /* forced speed & duplex (no autoneg) - * partner speed & duplex & pause (autoneg) - */ - int speed; - int duplex; - int pause; - - /* The most recently read link state */ - int link; - - /* Enabled Interrupts */ - u32 interrupts; - - u32 advertising; - int autoneg; - int mii_id; - - /* private data pointer */ - /* For use by PHYs to maintain extra state */ - void *priv; - - /* Provided by host chip */ - struct net_device *dev; - - /* A lock to ensure that only one thing can read/write - * the MDIO bus at a time */ - spinlock_t mdio_lock; - - /* Provided by ethernet driver */ - int (*mdio_read) (struct net_device * dev, int mii_id, int reg); - void (*mdio_write) (struct net_device * dev, int mii_id, int reg, - int val); -}; - -/* struct phy_info: a structure which defines attributes for a PHY - * - * id will contain a number which represents the PHY. During - * startup, the driver will poll the PHY to find out what its - * UID--as defined by registers 2 and 3--is. The 32-bit result - * gotten from the PHY will be ANDed with phy_id_mask to - * discard any bits which may change based on revision numbers - * unimportant to functionality - * - * There are 6 commands which take a ugeth_mii_info structure. - * Each PHY must declare config_aneg, and read_status. - */ -struct phy_info { - u32 phy_id; - char *name; - unsigned int phy_id_mask; - u32 features; - - /* Called to initialize the PHY */ - int (*init) (struct ugeth_mii_info * mii_info); - - /* Called to suspend the PHY for power */ - int (*suspend) (struct ugeth_mii_info * mii_info); - - /* Reconfigures autonegotiation (or disables it) */ - int (*config_aneg) (struct ugeth_mii_info * mii_info); - - /* Determines the negotiated speed and duplex */ - int (*read_status) (struct ugeth_mii_info * mii_info); - - /* Clears any pending interrupts */ - int (*ack_interrupt) (struct ugeth_mii_info * mii_info); - - /* Enables or disables interrupts */ - int (*config_intr) (struct ugeth_mii_info * mii_info); - - /* Clears up any memory if needed */ - void (*close) (struct ugeth_mii_info * mii_info); -}; - -struct phy_info *get_phy_info(struct ugeth_mii_info *mii_info); -void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value); -int read_phy_reg(struct net_device *dev, int mii_id, int regnum); -void mii_clear_phy_interrupt(struct ugeth_mii_info *mii_info); -void mii_configure_phy_interrupt(struct ugeth_mii_info *mii_info, - u32 interrupts); - -struct dm9161_private { - struct timer_list timer; - int resetdone; -}; - -#endif /* __UCC_GETH_PHY_H__ */ diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index f3a972e..adea290 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -1486,7 +1486,6 @@ static int rhine_rx(struct net_device *d copying to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(rp->pdev, rp->rx_skbuff_dma[entry], diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 8e5d820..25b75b6 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -1339,7 +1339,8 @@ static inline int velocity_rx_copy(struc if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) skb_reserve(new_skb, 2); - memcpy(new_skb->data, rx_skb[0]->data, pkt_size); + skb_copy_from_linear_data(rx_skb[0], new_skb->data, + pkt_size); *rx_skb = new_skb; ret = 0; } @@ -1398,7 +1399,6 @@ static int velocity_receive_frame(struct vptr->stats.multicast++; skb = rd_info->skb; - skb->dev = vptr->dev; pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma, vptr->rx_buf_sz, PCI_DMA_FROMDEVICE); @@ -1428,7 +1428,7 @@ static int velocity_receive_frame(struct PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len - 4); - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, vptr->dev); stats->rx_bytes += pkt_len; netif_rx(skb); @@ -1928,7 +1928,7 @@ #endif if (pktlen < ETH_ZLEN) { /* Cannot occur until ZC support */ pktlen = ETH_ZLEN; - memcpy(tdinfo->buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, tdinfo->buf, skb->len); memset(tdinfo->buf + skb->len, 0, ETH_ZLEN - skb->len); tdinfo->skb = skb; tdinfo->skb_dma[0] = tdinfo->buf_dma; @@ -1944,7 +1944,7 @@ #ifdef VELOCITY_ZERO_COPY_SUPPORT int nfrags = skb_shinfo(skb)->nr_frags; tdinfo->skb = skb; if (nfrags > 6) { - memcpy(tdinfo->buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, tdinfo->buf, skb->len); tdinfo->skb_dma[0] = tdinfo->buf_dma; td_ptr->tdesc0.pktsize = td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); @@ -2007,7 +2007,7 @@ #endif */ if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM) && (skb->ip_summed == CHECKSUM_PARTIAL)) { - struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) td_ptr->tdesc1.TCR |= TCR0_TCPCK; else if (ip->protocol == IPPROTO_UDP) diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 5b82e4f..2346473 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -773,7 +773,7 @@ static int sppp_rx_done(struct channel_d } chan->rx_skb->protocol = htons(ETH_P_WAN_PPP); chan->rx_skb->dev = chan->pppdev.dev; - chan->rx_skb->mac.raw = chan->rx_skb->data; + skb_reset_mac_header(chan->rx_skb); chan->stats.rx_packets++; chan->stats.rx_bytes += chan->cosa->rxsize; netif_rx(chan->rx_skb); diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c index a631d1c..016b3ff 100644 --- a/drivers/net/wan/cycx_x25.c +++ b/drivers/net/wan/cycx_x25.c @@ -834,7 +834,7 @@ static void cycx_x25_irq_rx(struct cycx_ ++chan->ifstats.rx_packets; chan->ifstats.rx_bytes += pktlen; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_rx(skb); dev->last_rx = jiffies; /* timestamp */ } diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c index 7369875..66be20c 100644 --- a/drivers/net/wan/dlci.c +++ b/drivers/net/wan/dlci.c @@ -176,7 +176,7 @@ static void dlci_receive(struct sk_buff if (process) { /* we've set up the protocol, so discard the header */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, header); dlp->stats.rx_bytes += skb->len; netif_rx(skb); diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 25021a7..dca0244 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -1904,7 +1904,8 @@ static struct sk_buff *dscc4_init_dummy_ struct TxFD *tx_fd = dpriv->tx_fd + last; skb->len = DUMMY_SKB_SIZE; - memcpy(skb->data, version, strlen(version)%DUMMY_SKB_SIZE); + skb_copy_to_linear_data(skb, version, + strlen(version) % DUMMY_SKB_SIZE); tx_fd->state = FrameEnd | TO_STATE_TX(DUMMY_SKB_SIZE); tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, DUMMY_SKB_SIZE, PCI_DMA_TODEVICE); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index c45d6a8..58a53b6 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -864,7 +864,7 @@ fst_tx_dma_complete(struct fst_card_info static __be16 farsync_type_trans(struct sk_buff *skb, struct net_device *dev) { skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; return htons(ETH_P_CUST); } diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index c9664fd..9ec6cf2 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -37,16 +37,16 @@ #define CISCO_KEEPALIVE_REQ 2 /* Cisco k struct hdlc_header { u8 address; u8 control; - u16 protocol; + __be16 protocol; }__attribute__ ((packed)); struct cisco_packet { - u32 type; /* code */ - u32 par1; - u32 par2; - u16 rel; /* reliability */ - u32 time; + __be32 type; /* code */ + __be32 par1; + __be32 par2; + __be16 rel; /* reliability */ + __be32 time; }__attribute__ ((packed)); #define CISCO_PACKET_LEN 18 #define CISCO_BIG_PACKET_LEN 20 @@ -97,7 +97,7 @@ #endif static void cisco_keepalive_send(struct net_device *dev, u32 type, - u32 par1, u32 par2) + __be32 par1, __be32 par2) { struct sk_buff *skb; struct cisco_packet *data; @@ -115,16 +115,16 @@ static void cisco_keepalive_send(struct data = (struct cisco_packet*)(skb->data + 4); data->type = htonl(type); - data->par1 = htonl(par1); - data->par2 = htonl(par2); - data->rel = 0xFFFF; + data->par1 = par1; + data->par2 = par2; + data->rel = __constant_htons(0xFFFF); /* we will need do_div here if 1000 % HZ != 0 */ data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ)); skb_put(skb, sizeof(struct cisco_packet)); skb->priority = TC_PRIO_CONTROL; skb->dev = dev; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); dev_queue_xmit(skb); } @@ -193,7 +193,7 @@ static int cisco_rx(struct sk_buff *skb) case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ in_dev = dev->ip_ptr; addr = 0; - mask = ~0; /* is the mask correct? */ + mask = __constant_htonl(~0); /* is the mask correct? */ if (in_dev != NULL) { struct in_ifaddr **ifap = &in_dev->ifa_list; @@ -245,7 +245,7 @@ static int cisco_rx(struct sk_buff *skb) } /* switch(protocol) */ printk(KERN_INFO "%s: Unsupported protocol %x\n", dev->name, - data->protocol); + ntohs(data->protocol)); dev_kfree_skb_any(skb); return NET_RX_DROP; @@ -270,8 +270,9 @@ static void cisco_timer(unsigned long ar netif_dormant_on(dev); } - cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, ++state(hdlc)->txseq, - state(hdlc)->rxseq); + cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, + htonl(++state(hdlc)->txseq), + htonl(state(hdlc)->rxseq)); state(hdlc)->request_sent = 1; state(hdlc)->timer.expires = jiffies + state(hdlc)->settings.interval * HZ; diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index c6c3c75..15b6e07 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -288,31 +288,31 @@ static int fr_hard_header(struct sk_buff struct sk_buff *skb = *skb_p; switch (skb->protocol) { - case __constant_ntohs(NLPID_CCITT_ANSI_LMI): + case __constant_htons(NLPID_CCITT_ANSI_LMI): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_CCITT_ANSI_LMI; break; - case __constant_ntohs(NLPID_CISCO_LMI): + case __constant_htons(NLPID_CISCO_LMI): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_CISCO_LMI; break; - case __constant_ntohs(ETH_P_IP): + case __constant_htons(ETH_P_IP): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IP; break; - case __constant_ntohs(ETH_P_IPV6): + case __constant_htons(ETH_P_IPV6): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IPV6; break; - case __constant_ntohs(ETH_P_802_3): + case __constant_htons(ETH_P_802_3): head_len = 10; if (skb_headroom(skb) < head_len) { struct sk_buff *skb2 = skb_realloc_headroom(skb, @@ -340,7 +340,7 @@ static int fr_hard_header(struct sk_buff skb->data[5] = FR_PAD; skb->data[6] = FR_PAD; skb->data[7] = FR_PAD; - *(u16*)(skb->data + 8) = skb->protocol; + *(__be16*)(skb->data + 8) = skb->protocol; } dlci_to_q922(skb->data, dlci); @@ -533,7 +533,7 @@ static void fr_lmi_send(struct net_devic skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI); fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI); } - data = skb->tail; + data = skb_tail_pointer(skb); data[i++] = LMI_CALLREF; data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY; if (lmi == LMI_ANSI) @@ -590,7 +590,7 @@ static void fr_lmi_send(struct net_devic skb_put(skb, i); skb->priority = TC_PRIO_CONTROL; skb->dev = dev; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); dev_queue_xmit(skb); } @@ -974,8 +974,8 @@ #endif } else if (skb->len > 10 && data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) { - u16 oui = ntohs(*(u16*)(data + 6)); - u16 pid = ntohs(*(u16*)(data + 8)); + u16 oui = ntohs(*(__be16*)(data + 6)); + u16 pid = ntohs(*(__be16*)(data + 8)); skb_pull(skb, 10); switch ((((u32)oui) << 16) | pid) { @@ -1011,7 +1011,6 @@ #endif stats->rx_bytes += skb->len; if (pvc->state.becn) stats->rx_compressed++; - skb->dev = dev; netif_rx(skb); return NET_RX_SUCCESS; } else { @@ -1128,7 +1127,7 @@ static int fr_add_pvc(struct net_device memcpy(dev->dev_addr, "\x00\x01", 2); get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); } else { - *(u16*)dev->dev_addr = htons(dlci); + *(__be16*)dev->dev_addr = htons(dlci); dlci_to_q922(dev->broadcast, dlci); } dev->hard_start_xmit = pvc_xmit; diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index a02c5fb..9ba3e4e 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -59,7 +59,7 @@ static void hostess_input(struct z8530_c /* Drop the CRC - it's not a good idea to try and negotiate it ;) */ skb_trim(skb, skb->len-2); skb->protocol=__constant_htons(ETH_P_WAN_PPP); - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb->dev=c->netdevice; /* * Send it to the PPP layer. We don't have time to process diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index 2b54f1b..ae132c1 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -1636,7 +1636,7 @@ static int lmc_rx (struct net_device *de if (nsb) { sc->lmc_rxq[i] = nsb; nsb->dev = dev; - sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail); + sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb)); } sc->failed_recv_alloc = 1; goto skip_packet; @@ -1667,8 +1667,8 @@ static int lmc_rx (struct net_device *de skb_put (skb, len); skb->protocol = lmc_proto_type(sc, skb); skb->protocol = htons(ETH_P_WAN_PPP); - skb->mac.raw = skb->data; -// skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + /* skb_reset_network_header(skb); */ skb->dev = dev; lmc_proto_netif(sc, skb); @@ -1679,7 +1679,7 @@ static int lmc_rx (struct net_device *de if (nsb) { sc->lmc_rxq[i] = nsb; nsb->dev = dev; - sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail); + sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb)); /* Transferred to 21140 below */ } else { @@ -1702,11 +1702,11 @@ static int lmc_rx (struct net_device *de if(!nsb) { goto give_it_anyways; } - memcpy(skb_put(nsb, len), skb->data, len); + skb_copy_from_linear_data(skb, skb_put(nsb, len), len); nsb->protocol = lmc_proto_type(sc, skb); - nsb->mac.raw = nsb->data; -// nsb->nh.raw = nsb->data; + skb_reset_mac_header(nsb); + /* skb_reset_network_header(nsb); */ nsb->dev = dev; lmc_proto_netif(sc, nsb); } @@ -1932,7 +1932,7 @@ static void lmc_softreset (lmc_softc_t * sc->lmc_rxring[i].status = 0x80000000; /* used to be PKT_BUF_SZ now uses skb since we lose some to head room */ - sc->lmc_rxring[i].length = skb->end - skb->data; + sc->lmc_rxring[i].length = skb_tailroom(skb); /* use to be tail which is dumb since you're thinking why write * to the end of the packj,et but since there's nothing there tail == data diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c index 62184de..999bf71 100644 --- a/drivers/net/wan/pc300_drv.c +++ b/drivers/net/wan/pc300_drv.c @@ -1755,17 +1755,17 @@ cpc_trace(struct net_device *dev, struct skb->dev = dev; skb->protocol = htons(ETH_P_CUST); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; skb->len = 10 + skb_main->len; - memcpy(skb->data, dev->name, 5); + skb_copy_to_linear_data(skb, dev->name, 5); skb->data[5] = '['; skb->data[6] = rx_tx; skb->data[7] = ']'; skb->data[8] = ':'; skb->data[9] = ' '; - memcpy(&skb->data[10], skb_main->data, skb_main->len); + skb_copy_from_linear_data(skb_main, &skb->data[10], skb_main->len); netif_rx(skb); } diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index 5873c34..07dbdfb 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -1003,17 +1003,17 @@ static void cpc_tty_trace(pc300dev_t *de skb_put (skb, 10 + len); skb->dev = dev->dev; skb->protocol = htons(ETH_P_CUST); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; skb->len = 10 + len; - memcpy(skb->data,dev->dev->name,5); + skb_copy_to_linear_data(skb, dev->dev->name, 5); skb->data[5] = '['; skb->data[6] = rxtx; skb->data[7] = ']'; skb->data[8] = ':'; skb->data[9] = ' '; - memcpy(&skb->data[10], buf, len); + skb_copy_to_linear_data_offset(skb, 10, buf, len); netif_rx(skb); } diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index fc5c0c6..35eded7 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -999,11 +999,6 @@ get_rx_buf( struct net_device *dev ) if( !skb ) return NULL; -#ifdef CONFIG_SBNI_MULTILINE - skb->dev = ((struct net_local *) dev->priv)->master; -#else - skb->dev = dev; -#endif skb_reserve( skb, 2 ); /* Align IP on longword boundaries */ return skb; } diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c index 70fb1b9..1313581 100644 --- a/drivers/net/wan/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -61,7 +61,7 @@ static void sealevel_input(struct z8530_ /* Drop the CRC - it's not a good idea to try and negotiate it ;) */ skb_trim(skb, skb->len-2); skb->protocol=htons(ETH_P_WAN_PPP); - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb->dev=c->netdevice; /* * Send it to the PPP layer. We don't have time to process diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c index 218f7b5..67fc67c 100644 --- a/drivers/net/wan/syncppp.c +++ b/drivers/net/wan/syncppp.c @@ -227,7 +227,7 @@ static void sppp_input (struct net_devic unsigned long flags; skb->dev=dev; - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); if (dev->flags & IFF_RUNNING) { diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c index 8b4540b..98ef400 100644 --- a/drivers/net/wan/z85230.c +++ b/drivers/net/wan/z85230.c @@ -1656,7 +1656,7 @@ static void z8530_rx_done(struct z8530_c else { skb_put(skb, ct); - memcpy(skb->data, rxb, ct); + skb_copy_to_linear_data(skb, rxb, ct); c->stats.rx_packets++; c->stats.rx_bytes+=ct; } @@ -1782,7 +1782,7 @@ int z8530_queue_xmit(struct z8530_channe */ c->tx_next_ptr=c->tx_dma_buf[c->tx_dma_used]; c->tx_dma_used^=1; /* Flip temp buffer */ - memcpy(c->tx_next_ptr, skb->data, skb->len); + skb_copy_from_linear_data(skb, c->tx_next_ptr, skb->len); } else c->tx_next_ptr=skb->data; diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index ece3d9c..c4b3dc2 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -2,47 +2,21 @@ # # Wireless LAN device configuration # -menu "Wireless LAN (non-hamradio)" - depends on NETDEVICES - -config NET_RADIO - bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions" - select WIRELESS_EXT - ---help--- - Support for wireless LANs and everything having to do with radio, - but not with amateur radio or FM broadcasting. - - Saying Y here also enables the Wireless Extensions (creates - /proc/net/wireless and enables iwconfig access). The Wireless - Extension is a generic API allowing a driver to expose to the user - space configuration and statistics specific to common Wireless LANs. - The beauty of it is that a single set of tool can support all the - variations of Wireless LANs, regardless of their type (as long as - the driver supports Wireless Extension). Another advantage is that - these parameters may be changed on the fly without restarting the - driver (or Linux). If you wish to use Wireless Extensions with - wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch - the tools from - . +menu "Wireless LAN" -config NET_WIRELESS_RTNETLINK - bool "Wireless Extension API over RtNetlink" - depends on NET_RADIO +config WLAN_PRE80211 + bool "Wireless LAN (pre-802.11)" + depends on NETDEVICES ---help--- - Support the Wireless Extension API over the RtNetlink socket - in addition to the traditional ioctl interface (selected above). - - For now, few tools use this facility, but it might grow in the - future. The only downside is that it adds 4.5 kB to your kernel. + Say Y if you have any pre-802.11 wireless LAN hardware. -# Note : the cards are obsolete (can't buy them anymore), but the drivers -# are not, as people are still using them... -comment "Obsolete Wireless cards support (pre-802.11)" - depends on NET_RADIO && (INET || ISA || PCMCIA) + This option does not affect the kernel build, it only + lets you choose drivers. config STRIP tristate "STRIP (Metricom starmode radio IP)" - depends on NET_RADIO && INET + depends on INET && WLAN_PRE80211 + select WIRELESS_EXT ---help--- Say Y if you have a Metricom radio and intend to use Starmode Radio IP. STRIP is a radio protocol developed for the MosquitoNet project @@ -65,7 +39,8 @@ config STRIP config ARLAN tristate "Aironet Arlan 655 & IC2200 DS support" - depends on NET_RADIO && ISA && !64BIT + depends on ISA && !64BIT && WLAN_PRE80211 + select WIRELESS_EXT ---help--- Aironet makes Arlan, a class of wireless LAN adapters. These use the www.Telxon.com chip, which is also used on several similar cards. @@ -80,7 +55,8 @@ config ARLAN config WAVELAN tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" - depends on NET_RADIO && ISA + depends on ISA && WLAN_PRE80211 + select WIRELESS_EXT ---help--- The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is a Radio LAN (wireless Ethernet-like Local Area Network) using the @@ -107,7 +83,8 @@ config WAVELAN config PCMCIA_WAVELAN tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_PRE80211 + select WIRELESS_EXT help Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA (PC-card) wireless Ethernet networking card to your computer. This @@ -118,7 +95,8 @@ config PCMCIA_WAVELAN config PCMCIA_NETWAVE tristate "Xircom Netwave AirSurfer Pcmcia wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_PRE80211 + select WIRELESS_EXT help Say Y here if you intend to attach this type of PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -126,12 +104,20 @@ config PCMCIA_NETWAVE To compile this driver as a module, choose M here: the module will be called netwave_cs. If unsure, say N. -comment "Wireless 802.11 Frequency Hopping cards support" - depends on NET_RADIO && PCMCIA + +config WLAN_80211 + bool "Wireless LAN (IEEE 802.11)" + depends on NETDEVICES + ---help--- + Say Y if you have any 802.11 wireless LAN hardware. + + This option does not affect the kernel build, it only + lets you choose drivers. config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4MHz wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_80211 + select WIRELESS_EXT ---help--- Say Y here if you intend to attach an Aviator/Raytheon PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -141,12 +127,10 @@ config PCMCIA_RAYCS To compile this driver as a module, choose M here: the module will be called ray_cs. If unsure, say N. -comment "Wireless 802.11b ISA/PCI cards support" - depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) - config IPW2100 tristate "Intel PRO/Wireless 2100 Network Connection" - depends on NET_RADIO && PCI + depends on PCI && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select IEEE80211 ---help--- @@ -200,7 +184,8 @@ config IPW2100_DEBUG config IPW2200 tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" - depends on NET_RADIO && PCI + depends on PCI && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select IEEE80211 ---help--- @@ -280,9 +265,23 @@ config IPW2200_DEBUG If you are not sure, say N here. +config LIBERTAS_USB + tristate "Marvell Libertas 8388 802.11a/b/g cards" + depends on NET_RADIO && USB + select FW_LOADER + ---help--- + A driver for Marvell Libertas 8388 USB devices. + +config LIBERTAS_USB_DEBUG + bool "Enable full debugging output in the Libertas USB module." + depends on LIBERTAS_USB + ---help--- + Debugging support. + config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" - depends on NET_RADIO && ISA_DMA_API && (PCI || BROKEN) + depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN) + select WIRELESS_EXT select CRYPTO ---help--- This is the standard Linux driver to support Cisco/Aironet ISA and @@ -299,7 +298,8 @@ config AIRO config HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" - depends on NET_RADIO && (PPC_PMAC || PCI || PCMCIA) + depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 + select WIRELESS_EXT ---help--- A driver for 802.11b wireless cards based on the "Hermes" or Intersil HFA384x (Prism 2) MAC controller. This includes the vast @@ -373,7 +373,8 @@ config PCI_HERMES config ATMEL tristate "Atmel at76c50x chipset 802.11b support" - depends on NET_RADIO && (PCI || PCMCIA) + depends on (PCI || PCMCIA) && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select CRC32 ---help--- @@ -394,13 +395,9 @@ config PCI_ATMEL Enable support for PCI and mini-PCI cards containing the Atmel at76c506 chip. -# If Pcmcia is compiled in, offer Pcmcia cards... -comment "Wireless 802.11b Pcmcia/Cardbus cards support" - depends on NET_RADIO && PCMCIA - config PCMCIA_HERMES tristate "Hermes PCMCIA card support" - depends on NET_RADIO && PCMCIA && HERMES + depends on PCMCIA && HERMES ---help--- A driver for "Hermes" chipset based PCMCIA wireless adaptors, such as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -420,7 +417,7 @@ config PCMCIA_HERMES config PCMCIA_SPECTRUM tristate "Symbol Spectrum24 Trilogy PCMCIA card support" - depends on NET_RADIO && PCMCIA && HERMES + depends on PCMCIA && HERMES select FW_LOADER ---help--- @@ -434,7 +431,8 @@ config PCMCIA_SPECTRUM config AIRO_CS tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" - depends on NET_RADIO && PCMCIA && (BROKEN || !M32R) + depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211 + select WIRELESS_EXT select CRYPTO select CRYPTO_AES ---help--- @@ -458,7 +456,8 @@ config AIRO_CS config PCMCIA_ATMEL tristate "Atmel at76c502/at76c504 PCMCIA cards" - depends on NET_RADIO && ATMEL && PCMCIA + depends on ATMEL && PCMCIA + select WIRELESS_EXT select FW_LOADER select CRC32 ---help--- @@ -467,17 +466,17 @@ config PCMCIA_ATMEL config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" - depends on NET_RADIO && EXPERIMENTAL && PCMCIA + depends on EXPERIMENTAL && PCMCIA && WLAN_80211 + select WIRELESS_EXT ---help--- A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. It has basic support for Linux wireless extensions and initial micro support for ethtool. -comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support" - depends on NET_RADIO && PCI config PRISM54 tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus' - depends on PCI && NET_RADIO && EXPERIMENTAL + depends on PCI && EXPERIMENTAL && WLAN_80211 + select WIRELESS_EXT select FW_LOADER ---help--- Enable PCI and Cardbus support for the following chipset based cards: @@ -523,7 +522,8 @@ config PRISM54 config USB_ZD1201 tristate "USB ZD1201 based Wireless device support" - depends on USB && NET_RADIO + depends on USB && WLAN_80211 + select WIRELESS_EXT select FW_LOADER ---help--- Say Y if you want to use wireless LAN adapters based on the ZyDAS @@ -542,11 +542,4 @@ source "drivers/net/wireless/hostap/Kcon source "drivers/net/wireless/bcm43xx/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" -# yes, this works even when no drivers are selected -config NET_WIRELESS - bool - depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) - default y - endmenu - diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index c613af1..d212460 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_ZD1201) += zd1201.o +obj-$(CONFIG_LIBERTAS_USB) += libertas/ diff --git a/drivers/net/wireless/README b/drivers/net/wireless/README deleted file mode 100644 index 0c274bf..0000000 --- a/drivers/net/wireless/README +++ /dev/null @@ -1,25 +0,0 @@ - README - ------ - - This directory is mostly for Wireless LAN drivers, in their -various incarnations (ISA, PCI, Pcmcia...). - This separate directory is needed because a lot of driver work -on different bus (typically PCI + Pcmcia) and share 95% of the -code. This allow the code and the config options to be in one single -place instead of scattered all over the driver tree, which is never -100% satisfactory. - - Note : if you want more info on the topic of Wireless LANs, -you are kindly invited to have a look at the Wireless Howto : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/ - Some Wireless LAN drivers, like orinoco_cs, require the use of -Wireless Tools to be configured : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html - - Special notes for distribution maintainers : - 1) wvlan_cs will be discontinued soon in favor of orinoco_cs - 2) Please add Wireless Tools support in your scripts - - Have fun... - - Jean diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 2ada76a..f21bbaf 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1145,6 +1145,7 @@ static void airo_networks_free(struct ai struct airo_info { struct net_device_stats stats; struct net_device *dev; + struct list_head dev_list; /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we use the high bit to mark whether it is in use. */ #define MAX_FIDS 6 @@ -2360,6 +2361,21 @@ static int airo_change_mtu(struct net_de return 0; } +static LIST_HEAD(airo_devices); + +static void add_airo_dev(struct airo_info *ai) +{ + /* Upper layers already keep track of PCI devices, + * so we only need to remember our non-PCI cards. */ + if (!ai->pci) + list_add_tail(&ai->dev_list, &airo_devices); +} + +static void del_airo_dev(struct airo_info *ai) +{ + if (!ai->pci) + list_del(&ai->dev_list); +} static int airo_close(struct net_device *dev) { struct airo_info *ai = dev->priv; @@ -2381,8 +2397,6 @@ #endif return 0; } -static void del_airo_dev( struct net_device *dev ); - void stop_airo_card( struct net_device *dev, int freeres ) { struct airo_info *ai = dev->priv; @@ -2434,17 +2448,15 @@ void stop_airo_card( struct net_device * } } crypto_free_cipher(ai->tfm); - del_airo_dev( dev ); + del_airo_dev(ai); free_netdev( dev ); } EXPORT_SYMBOL(stop_airo_card); -static int add_airo_dev( struct net_device *dev ); - static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr) { - memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); return ETH_ALEN; } @@ -2740,8 +2752,6 @@ static int airo_networks_allocate(struct static void airo_networks_free(struct airo_info *ai) { - if (!ai->networks) - return; kfree(ai->networks); ai->networks = NULL; } @@ -2816,12 +2826,10 @@ static struct net_device *_init_airo_car if (IS_ERR(ai->airo_thread_task)) goto err_out_free; ai->tfm = NULL; - rc = add_airo_dev( dev ); - if (rc) - goto err_out_thr; + add_airo_dev(ai); if (airo_networks_allocate (ai)) - goto err_out_unlink; + goto err_out_thr; airo_networks_initialize (ai); /* The Airo-specific entries in the device structure. */ @@ -2937,9 +2945,8 @@ err_out_irq: free_irq(dev->irq, dev); err_out_nets: airo_networks_free(ai); -err_out_unlink: - del_airo_dev(dev); err_out_thr: + del_airo_dev(ai); set_bit(JOB_DIE, &ai->jobs); kthread_stop(ai->airo_thread_task); err_out_free: @@ -3411,14 +3418,12 @@ #endif /* WIRELESS_SPY */ OUT4500( apriv, EVACK, EV_RX); if (test_bit(FLAG_802_11, &apriv->flags)) { - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; skb->dev = apriv->wifidev; skb->protocol = htons(ETH_P_802_2); - } else { - skb->dev = dev; + } else skb->protocol = eth_type_trans(skb,dev); - } skb->dev->last_rx = jiffies; skb->ip_summed = CHECKSUM_NONE; @@ -3641,7 +3646,6 @@ #ifdef WIRELESS_SPY } #endif /* WIRELESS_SPY */ - skb->dev = ai->dev; skb->ip_summed = CHECKSUM_NONE; skb->protocol = eth_type_trans(skb, ai->dev); skb->dev->last_rx = jiffies; @@ -3749,7 +3753,7 @@ #ifdef IW_WIRELESS_SPY /* defined in i wireless_spy_update(ai->dev, sa, &wstats); } #endif /* IW_WIRELESS_SPY */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; skb->dev = ai->wifidev; skb->protocol = htons(ETH_P_802_2); @@ -5538,11 +5542,6 @@ static int proc_close( struct inode *ino return 0; } -static struct net_device_list { - struct net_device *dev; - struct net_device_list *next; -} *airo_devices; - /* Since the card doesn't automatically switch to the right WEP mode, we will make it do it. If the card isn't associated, every secs we will switch WEP modes to see if that will help. If the card is @@ -5585,26 +5584,6 @@ static void timer_func( struct net_devic apriv->expires = RUN_AT(HZ*3); } -static int add_airo_dev( struct net_device *dev ) { - struct net_device_list *node = kmalloc( sizeof( *node ), GFP_KERNEL ); - if ( !node ) - return -ENOMEM; - - node->dev = dev; - node->next = airo_devices; - airo_devices = node; - - return 0; -} - -static void del_airo_dev( struct net_device *dev ) { - struct net_device_list **p = &airo_devices; - while( *p && ( (*p)->dev != dev ) ) - p = &(*p)->next; - if ( *p && (*p)->dev == dev ) - *p = (*p)->next; -} - #ifdef CONFIG_PCI static int __devinit airo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pent) @@ -5628,6 +5607,10 @@ static int __devinit airo_pci_probe(stru static void __devexit airo_pci_remove(struct pci_dev *pdev) { + struct net_device *dev = pci_get_drvdata(pdev); + + airo_print_info(dev->name, "Unregistering..."); + stop_airo_card(dev, 1); } static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) @@ -5753,9 +5736,11 @@ #endif static void __exit airo_cleanup_module( void ) { - while( airo_devices ) { - airo_print_info(airo_devices->dev->name, "Unregistering...\n"); - stop_airo_card( airo_devices->dev, 1 ); + struct airo_info *ai; + while(!list_empty(&airo_devices)) { + ai = list_entry(airo_devices.next, struct airo_info, dev_list); + airo_print_info(ai->dev->name, "Unregistering..."); + stop_airo_card(ai->dev, 1); } #ifdef CONFIG_PCI pci_unregister_driver(&airo_driver); diff --git a/drivers/net/wireless/arlan-main.c b/drivers/net/wireless/arlan-main.c index 4688e56..498e848 100644 --- a/drivers/net/wireless/arlan-main.c +++ b/drivers/net/wireless/arlan-main.c @@ -1500,7 +1500,6 @@ #endif // ARLAN_MULTICAST break; } skb_reserve(skb, 2); - skb->dev = dev; skbtmp = skb_put(skb, pkt_len); memcpy_fromio(skbtmp + ARLAN_FAKE_HDR_LEN, ((char __iomem *) arlan) + rxOffset, pkt_len - ARLAN_FAKE_HDR_LEN); diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 23eba69..51a7db5 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -827,14 +827,14 @@ static int start_tx(struct sk_buff *skb, if (priv->wep_is_on) frame_ctl |= IEEE80211_FCTL_PROTECTED; if (priv->operating_mode == IW_MODE_ADHOC) { - memcpy(&header.addr1, skb->data, 6); + skb_copy_from_linear_data(skb, &header.addr1, 6); memcpy(&header.addr2, dev->dev_addr, 6); memcpy(&header.addr3, priv->BSSID, 6); } else { frame_ctl |= IEEE80211_FCTL_TODS; memcpy(&header.addr1, priv->CurrentBSSID, 6); memcpy(&header.addr2, dev->dev_addr, 6); - memcpy(&header.addr3, skb->data, 6); + skb_copy_from_linear_data(skb, &header.addr3, 6); } if (priv->use_wpa) @@ -920,7 +920,6 @@ static void fast_rx_path(struct atmel_pr memcpy(&skbp[6], header->addr2, 6); /* source address */ priv->dev->last_rx = jiffies; - skb->dev = priv->dev; skb->protocol = eth_type_trans(skb, priv->dev); skb->ip_summed = CHECKSUM_NONE; netif_rx(skb); @@ -1028,7 +1027,6 @@ static void frag_rx_path(struct atmel_pr priv->rx_buf, priv->frag_len + 12); priv->dev->last_rx = jiffies; - skb->dev = priv->dev; skb->protocol = eth_type_trans(skb, priv->dev); skb->ip_summed = CHECKSUM_NONE; netif_rx(skb); diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig index 533993f..ce397e4 100644 --- a/drivers/net/wireless/bcm43xx/Kconfig +++ b/drivers/net/wireless/bcm43xx/Kconfig @@ -1,6 +1,7 @@ config BCM43XX tristate "Broadcom BCM43xx wireless support" - depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + select WIRELESS_EXT select FW_LOADER select HW_RANDOM ---help--- diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h index 95ff175..f8483c1 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -277,11 +277,14 @@ #define BCM43xx_SBTMSTATELOW_RESET 0x01 #define BCM43xx_SBTMSTATELOW_REJECT 0x02 #define BCM43xx_SBTMSTATELOW_CLOCK 0x10000 #define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000 +#define BCM43xx_SBTMSTATELOW_G_MODE_ENABLE 0x20000000 /* sbtmstatehigh state flags */ #define BCM43xx_SBTMSTATEHIGH_SERROR 0x00000001 #define BCM43xx_SBTMSTATEHIGH_BUSY 0x00000004 #define BCM43xx_SBTMSTATEHIGH_TIMEOUT 0x00000020 +#define BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000 +#define BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL 0x00020000 #define BCM43xx_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000 #define BCM43xx_SBTMSTATEHIGH_DMA64BIT 0x10000000 #define BCM43xx_SBTMSTATEHIGH_GATEDCLK 0x20000000 diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c index 6e0dc76..e3d2e61 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c @@ -998,7 +998,8 @@ static void dma_tx_fragment(struct bcm43 assert(0); return; } - memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(bounce_skb, skb->len), + skb->len); dev_kfree_skb_any(skb); skb = bounce_skb; } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c index c947025..d2df6a0 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c @@ -32,7 +32,7 @@ #include "bcm43xx_ethtool.h" #include #include #include -#include +#include static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) @@ -40,7 +40,7 @@ static void bcm43xx_get_drvinfo(struct n struct bcm43xx_private *bcm = bcm43xx_priv(dev); strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strncpy(info->version, UTS_RELEASE, sizeof(info->version)); + strncpy(info->version, utsname()->release, sizeof(info->version)); strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN); } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index a38e7ee..5e96bca 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -1407,7 +1407,7 @@ #endif & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002)); } else { if (connect_phy) - flags |= 0x20000000; + flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_phy_connect(bcm, connect_phy); bcm43xx_core_enable(bcm, flags); bcm43xx_write16(bcm, 0x03E6, 0x0000); @@ -3604,7 +3604,7 @@ int bcm43xx_select_wireless_core(struct u32 sbtmstatelow; sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow |= 0x20000000; + sbtmstatelow |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); } err = wireless_core_up(bcm, 1); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index 72529a4..b37f1e3 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -168,16 +168,16 @@ int bcm43xx_phy_connect(struct bcm43xx_p flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); if (connect) { - if (!(flags & 0x00010000)) + if (!(flags & BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL)) return -ENODEV; flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - flags |= (0x800 << 18); + flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); } else { - if (!(flags & 0x00020000)) + if (!(flags & BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL)) return -ENODEV; flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - flags &= ~(0x800 << 18); + flags &= ~BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); } out: @@ -300,16 +300,20 @@ static void bcm43xx_phy_agcsetup(struct if (phy->rev > 2) { bcm43xx_phy_write(bcm, 0x0422, 0x287A); - bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0x0FFF) | 0x3000); + bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) + & 0x0FFF) | 0x3000); } - bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874); + bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) + | 0x7874); bcm43xx_phy_write(bcm, 0x048E, 0x1C00); if (phy->rev == 1) { - bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) & 0xF0FF) | 0x0600); + bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) + & 0xF0FF) | 0x0600); bcm43xx_phy_write(bcm, 0x048B, 0x005E); - bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xFF00) | 0x001E); + bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) + & 0xFF00) | 0x001E); bcm43xx_phy_write(bcm, 0x048D, 0x0002); } @@ -335,7 +339,8 @@ static void bcm43xx_phy_setupg(struct bc if (phy->rev == 1) { bcm43xx_phy_write(bcm, 0x0406, 0x4F19); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, - (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340); + (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) + & 0xFC3F) | 0x0340); bcm43xx_phy_write(bcm, 0x042C, 0x005A); bcm43xx_phy_write(bcm, 0x0427, 0x001A); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h index 1f321ef..7311836 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h @@ -48,6 +48,10 @@ #define bcm43xx_phy_unlock(bcm, flags) \ local_irq_restore(flags); \ } while (0) +/* Card uses the loopback gain stuff */ +#define has_loopback_gain(phy) \ + (((phy)->rev > 1) || ((phy)->connected)) + u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset); void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c index 4025dd0..6a109f4 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c @@ -1343,11 +1343,110 @@ u16 bcm43xx_radio_calibrationvalue(struc return ret; } +#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) +static u16 bcm43xx_get_812_value(struct bcm43xx_private *bcm, u8 lpd) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 loop_or = 0; + u16 adj_loopback_gain = phy->loopback_gain[0]; + u8 loop; + u16 extern_lna_control; + + if (!phy->connected) + return 0; + if (!has_loopback_gain(phy)) { + if (phy->rev < 7 || !(bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA)) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0FB2; + case LPD(0, 0, 1): + return 0x00B2; + case LPD(1, 0, 1): + return 0x30B2; + case LPD(1, 0, 0): + return 0x30B3; + default: + assert(0); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x8FB2; + case LPD(0, 0, 1): + return 0x80B2; + case LPD(1, 0, 1): + return 0x20B2; + case LPD(1, 0, 0): + return 0x20B3; + default: + assert(0); + } + } + } else { + if (radio->revision == 8) + adj_loopback_gain += 0x003E; + else + adj_loopback_gain += 0x0026; + if (adj_loopback_gain >= 0x46) { + adj_loopback_gain -= 0x46; + extern_lna_control = 0x3000; + } else if (adj_loopback_gain >= 0x3A) { + adj_loopback_gain -= 0x3A; + extern_lna_control = 0x2000; + } else if (adj_loopback_gain >= 0x2E) { + adj_loopback_gain -= 0x2E; + extern_lna_control = 0x1000; + } else { + adj_loopback_gain -= 0x10; + extern_lna_control = 0x0000; + } + for (loop = 0; loop < 16; loop++) { + u16 tmp = adj_loopback_gain - 6 * loop; + if (tmp < 6) + break; + } + + loop_or = (loop << 8) | extern_lna_control; + if (phy->rev >= 7 && bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA) { + if (extern_lna_control) + loop_or |= 0x8000; + switch (lpd) { + case LPD(0, 1, 1): + return 0x8F92; + case LPD(0, 0, 1): + return (0x8092 | loop_or); + case LPD(1, 0, 1): + return (0x2092 | loop_or); + case LPD(1, 0, 0): + return (0x2093 | loop_or); + default: + assert(0); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0F92; + case LPD(0, 0, 1): + case LPD(1, 0, 1): + return (0x0092 | loop_or); + case LPD(1, 0, 0): + return (0x0093 | loop_or); + default: + assert(0); + } + } + } + return 0; +} + u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); - u16 backup[19] = { 0 }; + u16 backup[21] = { 0 }; u16 ret; u16 i, j; u32 tmp1 = 0, tmp2 = 0; @@ -1373,19 +1472,36 @@ u16 bcm43xx_radio_init2050(struct bcm43x backup[8] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS); backup[9] = bcm43xx_phy_read(bcm, 0x0802); bcm43xx_phy_write(bcm, 0x0814, - (bcm43xx_phy_read(bcm, 0x0814) | 0x0003)); + (bcm43xx_phy_read(bcm, 0x0814) + | 0x0003)); bcm43xx_phy_write(bcm, 0x0815, - (bcm43xx_phy_read(bcm, 0x0815) & 0xFFFC)); + (bcm43xx_phy_read(bcm, 0x0815) + & 0xFFFC)); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, - (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF)); + (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) + & 0x7FFF)); bcm43xx_phy_write(bcm, 0x0802, (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC)); - bcm43xx_phy_write(bcm, 0x0811, 0x01B3); - bcm43xx_phy_write(bcm, 0x0812, 0x0FB2); + if (phy->rev > 1) { /* loopback gain enabled */ + backup[19] = bcm43xx_phy_read(bcm, 0x080F); + backup[20] = bcm43xx_phy_read(bcm, 0x0810); + if (phy->rev >= 3) + bcm43xx_phy_write(bcm, 0x080F, 0xC020); + else + bcm43xx_phy_write(bcm, 0x080F, 0x8020); + bcm43xx_phy_write(bcm, 0x0810, 0x0000); + } + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 1, 1))); + if (phy->rev < 7 || !(bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA)) + bcm43xx_phy_write(bcm, 0x0811, 0x01B3); + else + bcm43xx_phy_write(bcm, 0x0811, 0x09B3); } - bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, - (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000)); } + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, + (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000)); backup[10] = bcm43xx_phy_read(bcm, 0x0035); bcm43xx_phy_write(bcm, 0x0035, (bcm43xx_phy_read(bcm, 0x0035) & 0xFF7F)); @@ -1397,10 +1513,12 @@ u16 bcm43xx_radio_init2050(struct bcm43x bcm43xx_write16(bcm, 0x03E6, 0x0122); } else { if (phy->analog >= 2) - bcm43xx_phy_write(bcm, 0x0003, (bcm43xx_phy_read(bcm, 0x0003) - & 0xFFBF) | 0x0040); + bcm43xx_phy_write(bcm, 0x0003, + (bcm43xx_phy_read(bcm, 0x0003) + & 0xFFBF) | 0x0040); bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, - (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000)); + (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) + | 0x2000)); } ret = bcm43xx_radio_calibrationvalue(bcm); @@ -1408,16 +1526,25 @@ u16 bcm43xx_radio_init2050(struct bcm43x if (phy->type == BCM43xx_PHYTYPE_B) bcm43xx_radio_write16(bcm, 0x0078, 0x0026); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 1, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xBFAF); bcm43xx_phy_write(bcm, 0x002B, 0x1403); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x00B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xBFA0); bcm43xx_radio_write16(bcm, 0x0051, (bcm43xx_radio_read16(bcm, 0x0051) | 0x0004)); - bcm43xx_radio_write16(bcm, 0x0052, 0x0000); - bcm43xx_radio_write16(bcm, 0x0043, - (bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0) | 0x0009); + if (radio->revision == 8) + bcm43xx_radio_write16(bcm, 0x0043, 0x001F); + else { + bcm43xx_radio_write16(bcm, 0x0052, 0x0000); + bcm43xx_radio_write16(bcm, 0x0043, + (bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0) + | 0x0009); + } bcm43xx_phy_write(bcm, 0x0058, 0x0000); for (i = 0; i < 16; i++) { @@ -1425,21 +1552,25 @@ u16 bcm43xx_radio_init2050(struct bcm43x bcm43xx_phy_write(bcm, 0x0059, 0xC810); bcm43xx_phy_write(bcm, 0x0058, 0x000D); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 0))); bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); - udelay(10); + udelay(20); tmp1 += bcm43xx_phy_read(bcm, 0x002D); bcm43xx_phy_write(bcm, 0x0058, 0x0000); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); } @@ -1457,21 +1588,29 @@ u16 bcm43xx_radio_init2050(struct bcm43x bcm43xx_phy_write(bcm, 0x0059, 0xC810); bcm43xx_phy_write(bcm, 0x0058, 0x000D); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B3); /* 0x30B3 is not a typo */ + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 0))); bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); udelay(10); tmp2 += bcm43xx_phy_read(bcm, 0x002D); bcm43xx_phy_write(bcm, 0x0058, 0x0000); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); } tmp2++; @@ -1497,15 +1636,20 @@ u16 bcm43xx_radio_init2050(struct bcm43x bcm43xx_phy_write(bcm, 0x0030, backup[2]); bcm43xx_write16(bcm, 0x03EC, backup[3]); } else { - bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, - (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) & 0x7FFF)); if (phy->connected) { + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, + (bcm43xx_read16(bcm, + BCM43xx_MMIO_PHY_RADIO) & 0x7FFF)); bcm43xx_phy_write(bcm, 0x0811, backup[4]); bcm43xx_phy_write(bcm, 0x0812, backup[5]); bcm43xx_phy_write(bcm, 0x0814, backup[6]); bcm43xx_phy_write(bcm, 0x0815, backup[7]); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, backup[8]); bcm43xx_phy_write(bcm, 0x0802, backup[9]); + if (phy->rev > 1) { + bcm43xx_phy_write(bcm, 0x080F, backup[19]); + bcm43xx_phy_write(bcm, 0x0810, backup[20]); + } } } if (i >= 15) diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig index 308f773..1fef331 100644 --- a/drivers/net/wireless/hostap/Kconfig +++ b/drivers/net/wireless/hostap/Kconfig @@ -1,6 +1,7 @@ config HOSTAP tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" - depends on NET_RADIO + depends on WLAN_80211 + select WIRELESS_EXT select IEEE80211 select IEEE80211_CRYPT_WEP ---help--- diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c index 7e04dc9..cbedc9e 100644 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c @@ -167,7 +167,7 @@ #undef LWNG_SETVAL ret = skb->len - phdrlen; skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, hdrlen); if (prism_header) skb_pull(skb, phdrlen); @@ -933,12 +933,14 @@ #endif if (frag == 0) { /* copy first fragment (including full headers) into * beginning of the fragment cache skb */ - memcpy(skb_put(frag_skb, flen), skb->data, flen); + skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), + flen); } else { /* append frame payload to the end of the fragment * cache skb */ - memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, - flen); + skb_copy_from_linear_data_offset(skb, hdrlen, + skb_put(frag_skb, + flen), flen); } dev_kfree_skb(skb); skb = NULL; @@ -1044,8 +1046,9 @@ #endif skb->len >= ETH_HLEN + ETH_ALEN) { /* Non-standard frame: get addr4 from its bogus location after * the payload */ - memcpy(skb->data + ETH_ALEN, - skb->data + skb->len - ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, skb->len - ETH_ALEN, + skb->data + ETH_ALEN, + ETH_ALEN); skb_trim(skb, skb->len - ETH_ALEN); } @@ -1073,17 +1076,17 @@ #endif if (skb2 != NULL) { /* send to wireless media */ - skb2->protocol = __constant_htons(ETH_P_802_3); - skb2->mac.raw = skb2->nh.raw = skb2->data; - /* skb2->nh.raw = skb2->data + ETH_HLEN; */ skb2->dev = dev; + skb2->protocol = __constant_htons(ETH_P_802_3); + skb_reset_mac_header(skb2); + skb_reset_network_header(skb2); + /* skb2->network_header += ETH_HLEN; */ dev_queue_xmit(skb2); } if (skb) { skb->protocol = eth_type_trans(skb, dev); memset(skb->cb, 0, sizeof(skb->cb)); - skb->dev = dev; netif_rx(skb); } diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c index 4a5be70..246fac0 100644 --- a/drivers/net/wireless/hostap/hostap_80211_tx.c +++ b/drivers/net/wireless/hostap/hostap_80211_tx.c @@ -146,7 +146,8 @@ int hostap_data_start_xmit(struct sk_buf fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA, * Addr4 = SA */ - memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, + &hdr.addr4, ETH_ALEN); hdr_len += ETH_ALEN; } else { /* bogus 4-addr format to workaround Prism2 station @@ -159,7 +160,8 @@ int hostap_data_start_xmit(struct sk_buf /* SA from skb->data + ETH_ALEN will be added after * frame payload; use hdr.addr4 as a temporary buffer */ - memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, + &hdr.addr4, ETH_ALEN); need_tailroom += ETH_ALEN; } @@ -174,24 +176,27 @@ int hostap_data_start_xmit(struct sk_buf else memcpy(&hdr.addr1, local->bssid, ETH_ALEN); memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(&hdr.addr3, skb->data, ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) { fc |= IEEE80211_FCTL_FROMDS; /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */ - memcpy(&hdr.addr1, skb->data, ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3, + ETH_ALEN); } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) { fc |= IEEE80211_FCTL_TODS; /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ memcpy(&hdr.addr1, to_assoc_ap ? local->assoc_ap_addr : local->bssid, ETH_ALEN); - memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(&hdr.addr3, skb->data, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, + ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); } else if (local->iw_mode == IW_MODE_ADHOC) { /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ - memcpy(&hdr.addr1, skb->data, ETH_ALEN); - memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, + ETH_ALEN); memcpy(&hdr.addr3, local->bssid, ETH_ALEN); } @@ -237,7 +242,7 @@ int hostap_data_start_xmit(struct sk_buf iface->stats.tx_packets++; iface->stats.tx_bytes += skb->len; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); meta = (struct hostap_skb_tx_data *) skb->cb; memset(meta, 0, sizeof(*meta)); meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index efb8cf3..5b3abd5 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -1,8 +1,8 @@ /* * Intersil Prism2 driver with Host AP (software access point) support * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2005, Jouni Malinen + * + * Copyright (c) 2002-2005, Jouni Malinen * * This file is to be included into hostap.c when S/W AP functionality is * compiled. @@ -982,7 +982,8 @@ static void prism2_send_mgmt(struct net_ meta->tx_cb_idx = tx_cb_idx; skb->dev = dev; - skb->mac.raw = skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); dev_queue_xmit(skb); } #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ @@ -1276,8 +1277,8 @@ static char * ap_auth_make_challenge(str return NULL; } - memcpy(tmpbuf, skb->data + ap->crypt->extra_mpdu_prefix_len, - WLAN_AUTH_CHALLENGE_LEN); + skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len, + tmpbuf, WLAN_AUTH_CHALLENGE_LEN); dev_kfree_skb(skb); return tmpbuf; diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h index 0162400..b31e6a0 100644 --- a/drivers/net/wireless/hostap/hostap_common.h +++ b/drivers/net/wireless/hostap/hostap_common.h @@ -368,9 +368,9 @@ enum { #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 #define PRISM2_HOSTAPD_RID_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +offsetof(struct prism2_hostapd_param, u.rid.data) #define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) +offsetof(struct prism2_hostapd_param, u.generic_elem.data) /* Maximum length for algorithm names (-1 for nul termination) used in ioctl() */ diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 8d8f4b9..ee1532b 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -22,7 +22,7 @@ #include #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *version = PRISM2_VERSION " (Jouni Malinen )"; static dev_info_t dev_info = "hostap_cs"; MODULE_AUTHOR("Jouni Malinen"); @@ -838,6 +838,8 @@ static struct pcmcia_device_id hostap_cs PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010), PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002), + PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF", + 0x2d858104), PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL", 0x74c5e40d), PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil", @@ -848,6 +850,11 @@ static struct pcmcia_device_id hostap_cs "Intersil", "PRISM 2_5 PCMCIA ADAPTER", "ISL37300P", "Eval-RevA", 0x4b801a17, 0x6345a0bf, 0xc9049a39, 0xc23adc0e), + /* D-Link DWL-650 Rev. P1; manfid 0x000b, 0x7110 */ + PCMCIA_DEVICE_PROD_ID1234( + "D-Link", "DWL-650 Wireless PC Card RevP", "ISL37101P-10", + "A3", + 0x1a424a1c, 0x6ea57632, 0xdd97a26b, 0x56b21f52), PCMCIA_DEVICE_PROD_ID123( "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", 0xe6ec52ce, 0x08649af2, 0x4b74baa0), diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 3079378..959887b 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3,8 +3,8 @@ * Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2005, Jouni Malinen + * + * Copyright (c) 2002-2005, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1838,13 +1838,14 @@ static int prism2_tx_80211(struct sk_buf /* skb->data starts with txdesc->frame_control */ hdr_len = 24; - memcpy(&txdesc.frame_control, skb->data, hdr_len); + skb_copy_from_linear_data(skb, &txdesc.frame_control, hdr_len); fc = le16_to_cpu(txdesc.frame_control); if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA && (fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) && skb->len >= 30) { /* Addr4 */ - memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4, + ETH_ALEN); hdr_len += ETH_ALEN; } @@ -2217,7 +2218,7 @@ static void hostap_tx_callback(local_inf memcpy(skb_put(skb, len), payload, len); skb->dev = local->dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); cb->func(skb, ok, cb->data); } diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 9077e6e..4743426 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -3,8 +3,8 @@ * Intersil Prism2/2.5/3 - hostap.o module, common routines * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2005, Jouni Malinen + * + * Copyright (c) 2002-2005, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -590,20 +590,20 @@ void hostap_dump_tx_header(const char *n int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr) { - memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */ + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ return ETH_ALEN; } int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr) { - if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) { - memcpy(haddr, skb->mac.raw + - sizeof(struct linux_wlan_ng_prism_hdr) + 10, + const unsigned char *mac = skb_mac_header(skb); + + if (*(u32 *)mac == LWNG_CAP_DID_BASE) { + memcpy(haddr, mac + sizeof(struct linux_wlan_ng_prism_hdr) + 10, ETH_ALEN); /* addr2 */ - } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */ - memcpy(haddr, skb->mac.raw + - sizeof(struct linux_wlan_ng_cap_hdr) + 10, + } else { /* (*(u32 *)mac == htonl(LWNG_CAPHDR_VERSION)) */ + memcpy(haddr, mac + sizeof(struct linux_wlan_ng_cap_hdr) + 10, ETH_ALEN); /* addr2 */ } return ETH_ALEN; @@ -1063,7 +1063,8 @@ int prism2_sta_send_mgmt(local_info_t *l meta->iface = netdev_priv(dev); skb->dev = dev; - skb->mac.raw = skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); dev_queue_xmit(skb); return 0; diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c index c4f6020..db4899e 100644 --- a/drivers/net/wireless/hostap/hostap_pci.c +++ b/drivers/net/wireless/hostap/hostap_pci.c @@ -20,7 +20,7 @@ #include #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *version = PRISM2_VERSION " (Jouni Malinen )"; static char *dev_info = "hostap_pci"; diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c index e235e06..f0fd5ec 100644 --- a/drivers/net/wireless/hostap/hostap_plx.c +++ b/drivers/net/wireless/hostap/hostap_plx.c @@ -23,7 +23,7 @@ #include #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *version = PRISM2_VERSION " (Jouni Malinen )"; static char *dev_info = "hostap_plx"; diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c index ad6e4a4..d51daf8 100644 --- a/drivers/net/wireless/ipw2100.c +++ b/drivers/net/wireless/ipw2100.c @@ -28,8 +28,8 @@ Portions of this file are based on the Host AP project, Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - - Copyright (c) 2002-2003, Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c @@ -2416,8 +2416,9 @@ static void isr_rx(struct ipw2100_priv * #ifdef IPW2100_RX_DEBUG /* Make a copy of the frame so we can dump it to the logs if * ieee80211_rx fails */ - memcpy(packet_data, packet->skb->data, - min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH)); + skb_copy_from_linear_data(packet->skb, packet_data, + min_t(u32, status->frame_size, + IPW_RX_NIC_BUFFER_LENGTH)); #endif if (!ieee80211_rx(priv->ieee, packet->skb, stats)) { diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index c878a2f..7cb2052 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -1847,6 +1847,52 @@ static ssize_t store_net_stats(struct de static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO, show_net_stats, store_net_stats); +static ssize_t show_channels(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + int len = 0, i; + + len = sprintf(&buf[len], + "Displaying %d channels in 2.4Ghz band " + "(802.11bg):\n", geo->bg_channels); + + for (i = 0; i < geo->bg_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n", + geo->bg[i].channel, + geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) || + (geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + "passive only" : "active/passive", + geo->bg[i].flags & IEEE80211_CH_B_ONLY ? + "B" : "B/G"); + } + + len += sprintf(&buf[len], + "Displaying %d channels in 5.2Ghz band " + "(802.11a):\n", geo->a_channels); + for (i = 0; i < geo->a_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n", + geo->a[i].channel, + geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) || + (geo->a[i].flags & IEEE80211_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + "passive only" : "active/passive"); + } + + return len; +} + +static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); + static void notify_wx_assoc_event(struct ipw_priv *priv) { union iwreq_data wrqu; @@ -8133,7 +8179,7 @@ static void ipw_handle_mgmt_packet(struc skb->dev = priv->ieee->dev; /* Point raw at the ieee80211_stats */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; skb->protocol = __constant_htons(ETH_P_80211_STATS); @@ -10355,7 +10401,7 @@ static void ipw_handle_promiscuous_tx(st rt_hdr->it_len = dst->len; - memcpy(skb_put(dst, len), src->data, len); + skb_copy_from_linear_data(src, skb_put(dst, len), len); if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats)) dev_kfree_skb_any(dst); @@ -11383,6 +11429,7 @@ static struct attribute *ipw_sysfs_entri &dev_attr_led.attr, &dev_attr_speed_scan.attr, &dev_attr_net_stats.attr, + &dev_attr_channels.attr, #ifdef CONFIG_IPW2200_PROMISCUOUS &dev_attr_rtap_iface.attr, &dev_attr_rtap_filter.attr, diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c new file mode 100644 index 0000000..e0ecc4d --- /dev/null +++ b/drivers/net/wireless/libertas/11d.c @@ -0,0 +1,754 @@ +/** + * This file contains functions for 802.11D. + */ +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "11d.h" +#include "dev.h" +#include "wext.h" + +#define TX_PWR_DEFAULT 10 + +static struct region_code_mapping region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x10}, /* IC Canada */ + {"SG ", 0x10}, /* Singapore */ + {"EU ", 0x30}, /* ETSI */ + {"AU ", 0x30}, /* Australia */ + {"KR ", 0x30}, /* Republic Of Korea */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ +}; + +/* Following 2 structure defines the supported channels */ +static struct chan_freq_power channel_freq_power_UN_BG[] = { + {1, 2412, TX_PWR_DEFAULT}, + {2, 2417, TX_PWR_DEFAULT}, + {3, 2422, TX_PWR_DEFAULT}, + {4, 2427, TX_PWR_DEFAULT}, + {5, 2432, TX_PWR_DEFAULT}, + {6, 2437, TX_PWR_DEFAULT}, + {7, 2442, TX_PWR_DEFAULT}, + {8, 2447, TX_PWR_DEFAULT}, + {9, 2452, TX_PWR_DEFAULT}, + {10, 2457, TX_PWR_DEFAULT}, + {11, 2462, TX_PWR_DEFAULT}, + {12, 2467, TX_PWR_DEFAULT}, + {13, 2472, TX_PWR_DEFAULT}, + {14, 2484, TX_PWR_DEFAULT} +}; + +static u8 wlan_region_2_code(u8 * region) +{ + u8 i; + u8 size = sizeof(region_code_mapping)/ + sizeof(struct region_code_mapping); + + for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++) + region[i] = toupper(region[i]); + + for (i = 0; i < size; i++) { + if (!memcmp(region, region_code_mapping[i].region, + COUNTRY_CODE_LEN)) + return (region_code_mapping[i].code); + } + + /* default is US */ + return (region_code_mapping[0].code); +} + +static u8 *wlan_code_2_region(u8 code) +{ + u8 i; + u8 size = sizeof(region_code_mapping) + / sizeof(struct region_code_mapping); + for (i = 0; i < size; i++) { + if (region_code_mapping[i].code == code) + return (region_code_mapping[i].region); + } + /* default is US */ + return (region_code_mapping[0].region); +} + +/** + * @brief This function finds the nrchan-th chan after the firstchan + * @param band band + * @param firstchan first channel number + * @param nrchan number of channels + * @return the nrchan-th chan number +*/ +static u8 wlan_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 * chan) +/*find the nrchan-th chan after the firstchan*/ +{ + u8 i; + struct chan_freq_power *cfp; + u8 cfp_no; + + cfp = channel_freq_power_UN_BG; + cfp_no = sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cfp_no; i++) { + if ((cfp + i)->channel == firstchan) { + lbs_pr_debug(1, "firstchan found\n"); + break; + } + } + + if (i < cfp_no) { + /*if beyond the boundary */ + if (i + nrchan < cfp_no) { + *chan = (cfp + i + nrchan)->channel; + return 1; + } + } + + return 0; +} + +/** + * @brief This function Checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return TRUE; FALSE +*/ +static u8 wlan_channel_known_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr; + u8 nr_chan = parsed_region_chan->nr_chan; + u8 i = 0; + + lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr, + sizeof(struct chan_power_11d) * nr_chan); + + for (i = 0; i < nr_chan; i++) { + if (chan == chanpwr[i].chan) { + lbs_pr_debug(1, "11D: Found Chan:%d\n", chan); + return 1; + } + } + + lbs_pr_debug(1, "11D: Not Find Chan:%d\n", chan); + return 0; +} + +u32 libertas_chan_2_freq(u8 chan, u8 band) +{ + struct chan_freq_power *cf; + u16 cnt; + u16 i; + u32 freq = 0; + + cf = channel_freq_power_UN_BG; + cnt = + sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cnt; i++) { + if (chan == cf[i].channel) + freq = cf[i].freq; + } + + return freq; +} + +static int generate_domain_info_11d(struct parsed_region_chan_11d + *parsed_region_chan, + struct wlan_802_11d_domain_reg * domaininfo) +{ + u8 nr_subband = 0; + + u8 nr_chan = parsed_region_chan->nr_chan; + u8 nr_parsedchan = 0; + + u8 firstchan = 0, nextchan = 0, maxpwr = 0; + + u8 i, flag = 0; + + memcpy(domaininfo->countrycode, parsed_region_chan->countrycode, + COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D:nrchan=%d\n", nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan, + sizeof(struct parsed_region_chan_11d)); + + for (i = 0; i < nr_chan; i++) { + if (!flag) { + flag = 1; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + nr_parsedchan = 1; + continue; + } + + if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 && + parsed_region_chan->chanpwr[i].pwr == maxpwr) { + nextchan++; + nr_parsedchan++; + } else { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = + nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + } + } + + if (flag) { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + } + domaininfo->nr_subband = nr_subband; + + lbs_pr_debug(1, "nr_subband=%x\n", domaininfo->nr_subband); + lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo, + COUNTRY_CODE_LEN + 1 + + sizeof(struct ieeetypes_subbandset) * nr_subband); + return 0; +} + +/** + * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS + * @param region_chan pointer to struct region_channel + * @param *parsed_region_chan pointer to parsed_region_chan_11d + * @return N/A +*/ +static void wlan_generate_parsed_region_chan_11d(struct region_channel * region_chan, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 i; + struct chan_freq_power *cfp; + + if (region_chan == NULL) { + lbs_pr_debug(1, "11D: region_chan is NULL\n"); + return; + } + + cfp = region_chan->CFP; + if (cfp == NULL) { + lbs_pr_debug(1, "11D: cfp equal NULL \n"); + return; + } + + parsed_region_chan->band = region_chan->band; + parsed_region_chan->region = region_chan->region; + memcpy(parsed_region_chan->countrycode, + wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D: region[0x%x] band[%d]\n", parsed_region_chan->region, + parsed_region_chan->band); + + for (i = 0; i < region_chan->nrcfp; i++, cfp++) { + parsed_region_chan->chanpwr[i].chan = cfp->channel; + parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower; + lbs_pr_debug(1, "11D: Chan[%d] Pwr[%d]\n", + parsed_region_chan->chanpwr[i].chan, + parsed_region_chan->chanpwr[i].pwr); + } + parsed_region_chan->nr_chan = region_chan->nrcfp; + + lbs_pr_debug(1, "11D: nrchan[%d]\n", parsed_region_chan->nr_chan); + + return; +} + +/** + * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS + * @param region region ID + * @param band band + * @param chan chan + * @return TRUE;FALSE +*/ +static u8 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan) +{ + struct chan_freq_power *cfp; + int cfp_no; + u8 idx; + + ENTER(); + + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp == NULL) + return 0; + + for (idx = 0; idx < cfp_no; idx++) { + if (chan == (cfp + idx)->channel) { + /* If Mrvl Chip Supported? */ + if ((cfp + idx)->unsupported) { + return 0; + } else { + return 1; + } + } + } + + /*chan is not in the region table */ + LEAVE(); + return 0; +} + +/** + * @brief This function checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return 0 +*/ +static int parse_domain_info_11d(struct ieeetypes_countryinfofullset* + countryinfo, + u8 band, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 nr_subband, nrchan; + u8 lastchan, firstchan; + u8 region; + u8 curchan = 0; + + u8 idx = 0; /*chan index in parsed_region_chan */ + + u8 j, i; + + ENTER(); + + /*validation Rules: + 1. valid region Code + 2. First Chan increment + 3. channel range no overlap + 4. channel is valid? + 5. channel is supported by region? + 6. Others + */ + + lbs_dbg_hex("CountryInfo:", (u8 *) countryinfo, 30); + + if ((*(countryinfo->countrycode)) == 0 + || (countryinfo->len <= COUNTRY_CODE_LEN)) { + /* No region Info or Wrong region info: treat as No 11D info */ + LEAVE(); + return 0; + } + + /*Step1: check region_code */ + parsed_region_chan->region = region = + wlan_region_2_code(countryinfo->countrycode); + + lbs_pr_debug(1, "regioncode=%x\n", (u8) parsed_region_chan->region); + lbs_dbg_hex("CountryCode:", (char *)countryinfo->countrycode, + COUNTRY_CODE_LEN); + + parsed_region_chan->band = band; + + memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, + COUNTRY_CODE_LEN); + + nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) / + sizeof(struct ieeetypes_subbandset); + + for (j = 0, lastchan = 0; j < nr_subband; j++) { + + if (countryinfo->subband[j].firstchan <= lastchan) { + /*Step2&3. Check First Chan Num increment and no overlap */ + lbs_pr_debug(1, "11D: Chan[%d>%d] Overlap\n", + countryinfo->subband[j].firstchan, lastchan); + continue; + } + + firstchan = countryinfo->subband[j].firstchan; + nrchan = countryinfo->subband[j].nrchan; + + for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { + /*step4: channel is supported? */ + + if (!wlan_get_chan_11d(band, firstchan, i, &curchan)) { + /* Chan is not found in UN table */ + lbs_pr_debug(1, "chan is not supported: %d \n", i); + break; + } + + lastchan = curchan; + + if (wlan_region_chan_supported_11d + (region, band, curchan)) { + /*step5: Check if curchan is supported by mrvl in region */ + parsed_region_chan->chanpwr[idx].chan = curchan; + parsed_region_chan->chanpwr[idx].pwr = + countryinfo->subband[j].maxtxpwr; + idx++; + } else { + /*not supported and ignore the chan */ + lbs_pr_debug(1, + "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n", + i, curchan, region, band); + } + } + + /*Step6: Add other checking if any */ + + } + + parsed_region_chan->nr_chan = idx; + + lbs_pr_debug(1, "nrchan=%x\n", parsed_region_chan->nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan, + 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); + + LEAVE(); + return 0; +} + +/** + * @brief This function calculates the scan type for channels + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return PASSIVE if chan is unknown; ACTIVE if chan is known +*/ +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + u8 scan_type = cmd_scan_type_passive; + + ENTER(); + + if (wlan_channel_known_11d(chan, parsed_region_chan)) { + lbs_pr_debug(1, "11D: Found and do Active Scan\n"); + scan_type = cmd_scan_type_active; + } else { + lbs_pr_debug(1, "11D: Not Find and do Passive Scan\n"); + } + + LEAVE(); + return scan_type; + +} + +void libertas_init_11d(wlan_private * priv) +{ + priv->adapter->enable11d = 0; + memset(&(priv->adapter->parsed_region_chan), 0, + sizeof(struct parsed_region_chan_11d)); + return; +} + +static int wlan_enable_11d(wlan_private * priv, u8 flag) +{ + int ret; + + priv->adapter->enable11d = flag; + + /* send cmd to FW to enable/disable 11D function in FW */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11D_ENABLE, + &priv->adapter->enable11d); + if (ret) + lbs_pr_debug(1, "11D: Fail to enable 11D \n"); + + return 0; +} + +/** + * @brief This function sets DOMAIN INFO to FW + * @param priv pointer to wlan_private + * @return 0; -1 +*/ +static int set_domain_info_11d(wlan_private * priv) +{ + int ret; + + if (!priv->adapter->enable11d) { + lbs_pr_debug(1, "11D: dnld domain Info with 11d disabled\n"); + return 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + if (ret) + lbs_pr_debug(1, "11D: Fail to dnld domain Info\n"); + + return ret; +} + +/** + * @brief This function setups scan channels + * @param priv pointer to wlan_private + * @param band band + * @return 0 +*/ +int libertas_set_universaltable(wlan_private * priv, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + u16 size = sizeof(struct chan_freq_power); + u16 i = 0; + + memset(adapter->universal_channel, 0, + sizeof(adapter->universal_channel)); + + adapter->universal_channel[i].nrcfp = + sizeof(channel_freq_power_UN_BG) / size; + lbs_pr_debug(1, "11D: BG-band nrcfp=%d\n", + adapter->universal_channel[i].nrcfp); + + adapter->universal_channel[i].CFP = channel_freq_power_UN_BG; + adapter->universal_channel[i].valid = 1; + adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + adapter->universal_channel[i].band = band; + i++; + + return 0; +} + +/** + * @brief This function implements command CMD_802_11D_DOMAIN_INFO + * @param priv pointer to wlan_private + * @param cmd pointer to cmd buffer + * @param cmdno cmd ID + * @param cmdOption cmd action + * @return 0 +*/ +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdoption) +{ + struct cmd_ds_802_11d_domain_info *pdomaininfo = + &cmd->params.domaininfo; + struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain; + wlan_adapter *adapter = priv->adapter; + u8 nr_subband = adapter->domainreg.nr_subband; + + ENTER(); + + lbs_pr_debug(1, "nr_subband=%x\n", nr_subband); + + cmd->command = cpu_to_le16(cmdno); + pdomaininfo->action = cpu_to_le16(cmdoption); + if (cmdoption == cmd_act_get) { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd, + (int)(cmd->size)); + LEAVE(); + return 0; + } + + domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); + memcpy(domain->countrycode, adapter->domainreg.countrycode, + sizeof(domain->countrycode)); + + domain->header.len = + cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) + + sizeof(domain->countrycode)); + + if (nr_subband) { + memcpy(domain->subband, adapter->domainreg.subband, + nr_subband * sizeof(struct ieeetypes_subbandset)); + + cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + + domain->header.len + + sizeof(struct mrvlietypesheader) + + S_DS_GEN); + } else { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + } + + lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size)); + + LEAVE(); + + return 0; +} + +/** + * @brief This function implements private cmd: enable/disable 11D + * @param priv pointer to wlan_private + * @param wrq pointer to user data + * @return 0 or -1 + */ +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq) +{ + int data = 0; + int *val; + + ENTER(); + data = SUBCMD_DATA(wrq); + + lbs_pr_debug(1, "enable 11D: %s\n", + (data == 1) ? "enable" : "Disable"); + + wlan_enable_11d(priv, data); + val = (int *)wrq->u.name; + *val = priv->adapter->enable11d; + + LEAVE(); + return 0; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @param resp pointer to command response buffer + * @return 0; -1 + */ +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11d_domain_info + *domaininfo = &resp->params.domaininforesp; + struct mrvlietypes_domainparamset *domain = &domaininfo->domain; + u16 action = le16_to_cpu(domaininfo->action); + s16 ret = 0; + u8 nr_subband = 0; + + ENTER(); + + lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp, + (int)le16_to_cpu(resp->size)); + + nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset); + /* countrycode 3 bytes */ + + lbs_pr_debug(1, "11D Domain Info Resp: nr_subband=%d\n", nr_subband); + + if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { + lbs_pr_debug(1, "Invalid Numrer of Subband returned!!\n"); + return -1; + } + + switch (action) { + case cmd_act_set: /*Proc Set action */ + break; + + case cmd_act_get: + break; + default: + lbs_pr_debug(1, "Invalid action:%d\n", domaininfo->action); + ret = -1; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + if (priv->adapter->enable11d) { + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + ret = parse_domain_info_11d(&adapter->pattemptedbssdesc-> + countryinfo, 0, + &adapter->parsed_region_chan); + + if (ret == -1) { + lbs_pr_debug(1, "11D: Err Parse domain_info from AP..\n"); + LEAVE(); + return ret; + } + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + } + LEAVE(); + return 0; +} + +/** + * @brief This function generates 11D info from user specified regioncode and download to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_create_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + struct region_channel *region_chan; + u8 j; + + ENTER(); + lbs_pr_debug(1, "11D:curbssparams.band[%d]\n", adapter->curbssparams.band); + + if (priv->adapter->enable11d) { + /* update parsed_region_chan_11; dnld domaininf to FW */ + + for (j = 0; j < sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); j++) { + region_chan = &adapter->region_channel[j]; + + lbs_pr_debug(1, "11D:[%d] region_chan->band[%d]\n", j, + region_chan->band); + + if (!region_chan || !region_chan->valid + || !region_chan->CFP) + continue; + if (region_chan->band != adapter->curbssparams.band) + continue; + break; + } + + if (j >= sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0])) { + lbs_pr_debug(1, "11D:region_chan not found. band[%d]\n", + adapter->curbssparams.band); + LEAVE(); + return -1; + } + + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + wlan_generate_parsed_region_chan_11d(region_chan, + &adapter-> + parsed_region_chan); + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + + } + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h new file mode 100644 index 0000000..db2ebea --- /dev/null +++ b/drivers/net/wireless/libertas/11d.h @@ -0,0 +1,105 @@ +/** + * This header file contains data structures and + * function declarations of 802.11d + */ +#ifndef _WLAN_11D_ +#define _WLAN_11D_ + +#include "types.h" +#include "defs.h" + +#define UNIVERSAL_REGION_CODE 0xff + +/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr) + */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +#define COUNTRY_CODE_LEN 3 +#define MAX_NO_OF_CHAN 40 + +struct cmd_ds_command; + +/** Data structure for Country IE*/ +struct ieeetypes_subbandset { + u8 firstchan; + u8 nrchan; + u8 maxtxpwr; +} __attribute__ ((packed)); + +struct ieeetypes_countryinfoset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +}; + +struct ieeetypes_countryinfofullset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +} __attribute__ ((packed)); + +struct mrvlietypes_domainparamset { + struct mrvlietypesheader header; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11d_domain_info { + u16 action; + struct mrvlietypes_domainparamset domain; +} __attribute__ ((packed)); + +/** domain regulatory information */ +struct wlan_802_11d_domain_reg { + /** country Code*/ + u8 countrycode[COUNTRY_CODE_LEN]; + /** No. of subband*/ + u8 nr_subband; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +}; + +struct chan_power_11d { + u8 chan; + u8 pwr; +} __attribute__ ((packed)); + +struct parsed_region_chan_11d { + u8 band; + u8 region; + s8 countrycode[COUNTRY_CODE_LEN]; + struct chan_power_11d chanpwr[MAX_NO_OF_CHAN]; + u8 nr_chan; +} __attribute__ ((packed)); + +struct region_code_mapping { + u8 region[COUNTRY_CODE_LEN]; + u8 code; +}; + +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d *parsed_region_chan); + +u32 libertas_chan_2_freq(u8 chan, u8 band); + +enum state_11d libertas_get_state_11d(wlan_private * priv); + +void libertas_init_11d(wlan_private * priv); + +int libertas_set_universaltable(wlan_private * priv, u8 band); + +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdOption); + +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq); + +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp); + +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv); + +int libertas_create_dnld_countryinfo_11d(wlan_private * priv); + +#endif /* _WLAN_11D_ */ diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE new file mode 100644 index 0000000..8862742 --- /dev/null +++ b/drivers/net/wireless/libertas/LICENSE @@ -0,0 +1,16 @@ + Copyright (c) 2003-2006, Marvell International Ltd. + All Rights Reserved + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile new file mode 100644 index 0000000..19c9350 --- /dev/null +++ b/drivers/net/wireless/libertas/Makefile @@ -0,0 +1,21 @@ +# EXTRA_CFLAGS += -Wpacked + +usb8xxx-objs := main.o fw.o wext.o \ + rx.o tx.o cmd.o \ + cmdresp.o scan.o \ + join.o 11d.o \ + ioctl.o debugfs.o \ + ethtool.o assoc.o + +ifeq ($(CONFIG_LIBERTAS_USB_DEBUG), y) +EXTRA_CFLAGS += -DDEBUG -DPROC_DEBUG +endif + + +# This is needed to support the newer boot2 bootloader (v >= 3104) +EXTRA_CFLAGS += -DSUPPORT_BOOT_COMMAND +usb8xxx-objs += if_bootcmd.o +usb8xxx-objs += if_usb.o + +obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o + diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README new file mode 100644 index 0000000..688da4c --- /dev/null +++ b/drivers/net/wireless/libertas/README @@ -0,0 +1,1044 @@ +================================================================================ + README for USB8388 + + (c) Copyright 2003-2006, Marvell International Ltd. + All Rights Reserved + + This software file (the "File") is distributed by Marvell International + Ltd. under the terms of the GNU General Public License Version 2, June 1991 + (the "License"). You may use, redistribute and/or modify this File in + accordance with the terms and conditions of the License, a copy of which + is available along with the File in the license.txt file or by writing to + the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + ARE EXPRESSLY DISCLAIMED. The License provides additional details about + this warranty disclaimer. +================================================================================ + +===================== +DRIVER LOADING +===================== + + o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ + + o. Load driver by using the following command: + + insmod usb8388.ko [fw_name=usb8388.bin] + +===================== +IWPRIV COMMAND +===================== + +NAME + This manual describes the usage of private commands used in Marvell WLAN + Linux Driver. All the commands available in Wlanconfig will not be available + in the iwpriv. + +SYNOPSIS + iwpriv [sub-command] ... + + iwpriv ethX version + iwpriv ethX scantype [sub-command] + iwpriv ethX getSNR + iwpriv ethX getNF + iwpriv ethX getRSSI + iwpriv ethX setrxant + iwpriv ethX getrxant + iwpriv ethX settxant + iwpriv ethX gettxant + iwpriv ethX authalgs + iwpriv ethX pre-TBTT + iwpriv ethX 8021xauthalgs + iwpriv ethX encryptionmode + iwpriv ethX setregioncode + iwpriv ethX getregioncode + iwpriv ethX setbcnavg + iwpriv ethX getbcnavg + iwpriv ethX setdataavg + iwpriv ethX setlisteninter + iwpriv ethX getlisteninter + iwpriv ethX setmultipledtim + iwpriv ethX getmultipledtim + iwpriv ethX atimwindow + iwpriv ethX deauth + iwpriv ethX adhocstop + iwpriv ethX radioon + iwpriv ethX radiooff + iwpriv ethX reasso-on + iwpriv ethX reasso-off + iwpriv ethX scanmode [sub-command] + iwpriv ethX setwpaie + iwpriv ethX wlanidle-off + iwpriv ethX wlanidle-on + iwpriv ethX getcis + iwpriv ethX getlog + iwpriv ethX getadhocstatus + iwpriv ethX adhocgrate + +Version 4 Command: + iwpriv ethX inactvityto + iwpriv ethX sleeppd + iwpriv ethX enable11d + iwpriv ethX tpccfg + iwpriv ethX powercfg + iwpriv ethX setafc + iwpriv ethX getafc + +Version 5 Command: + iwpriv ethX ledgpio + iwpriv ethX scanprobes + iwpriv ethX lolisteninter + iwpriv ethX rateadapt + iwpriv ethX txcontrol + iwpriv ethX psnullinterval + iwpriv ethX prescan + iwpriv ethX getrxinfo + iwpriv ethX gettxrate + iwpriv ethX beaconinterval + +BT Commands: + The blinding table (BT) contains a list of mac addresses that should be + ignored by the firmware. It is primarily used for debugging and + testing networks. It can be edited and inspected with the following + commands: + + iwpriv ethX bt_reset + iwpriv ethX bt_add + iwpriv ethX bt_del + iwpriv ethX bt_list + +FWT Commands: + The forwarding table (FWT) is a feature used to manage mesh network + routing in the firmware. The FWT is essentially a routing table that + associates a destination mac address (da) with a next hop receiver + address (ra). The FWT can be inspected and edited with the following + iwpriv commands, which are described in greater detail below. + Eventually, the table will be automatically maintained by a custom + routing protocol. + + NOTE: FWT commands replace the previous DFT commands. What were the DFT + commands?, you might ask. They were an earlier API to the firmware that + implemented a simple MAC-layer forwarding mechanism. In the unlikely + event that you were using these commands, you must migrate to the new + FWT commands which can be used to achieve the same functionality. + + iwpriv ethX fwt_add [parameters] + iwpriv ethX fwt_del [parameters] + iwpriv ethX fwt_lookup [parameters] + iwpriv ethX fwt_list [parameters] + iwpriv ethX fwt_list_route [parameters] + iwpriv ethX fwt_list_neigh [parameters] + iwpriv ethX fwt_reset [parameters] + iwpriv ethX fwt_cleanup + iwpriv ethX fwt_time + +MESH Commands: + + The MESH commands are used to configure various features of the mesh + routing protocol. The following commands are supported: + + iwpriv ethX mesh_get_ttl + iwpriv ethX mesh_set_ttl ttl + +DESCRIPTION + Those commands are used to send additional commands to the Marvell WLAN + card via the Linux device driver. + + The ethX parameter specifies the network device that is to be used to + perform this command on. it could be eth0, eth1 etc. + +version + This is used to get the current version of the driver and the firmware. + +scantype + This command is used to set the scan type to be used by the driver in + the scan command. This setting will not be used while performing a scan + for a specific SSID, as it is always done with scan type being active. + + where the sub-commands are: - + active -- to set the scan type to active + passive -- to set the scan type to passive + get -- to get the scan type set in the driver + +getSNR + This command gets the average and non average value of Signal to Noise + Ratio of Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + If no value is given, all four values are returned in the order mentioned + above. + + Note: This command is available only when STA is connected. + +getRSSI + This command gets the average and non average value os Receive Signal + Strength of Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + Note: This command is available only when STA is connected. + +getNF + This command gets the average and non average value of Noise Floor of + Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + Note: This command is available only when STA is connected. + +setrxant + This command is used to set the mode for Rx antenna. + + The options that can be sent are:- + 1 -- Antenna 1. + 2 -- Antenna 2. + 0xFFFF -- Diversity. + + Usage: + iwpriv ethX setrxant 0x01: select Antenna 1. + +getrxant + This command is used to get the mode for Rx antenna. + + +settxant + This command is used to set the mode for Tx antenna. + The options that can be sent are:- + 1 -- Antenna 1. + 2 -- Antenna 2. + 0xFFFF -- Diversity. + Usage: + iwpriv ethX settxant 0x01: select Antenna 1. + +gettxant + This command is used to get the mode for Tx antenna. + +authalgs + This command is used by the WPA supplicant to set the authentication + algorithms in the station. + +8021xauthalgs + This command is used by the WPA supplicant to set the 8021.x authentication algorithm type + station. + + where values can be:- + 1 -- None + 2 -- LEAP + 4 -- TLS + 8 -- TTLs + 16 -- MD5 + + +encryptionmode + This command is used by the WPA supplicant to set the encryption algorithm. + + where values can be:- + 0 -- NONE + 1 -- WEP40 + 2 -- TKIP + 3 -- CCMP + 4 -- WEP104 + +pre-TBTT + This command is used to set pre-TBTT time period where value is in microseconds. + +setregioncode + This command is used to set the region code in the station. + where value is 'region code' for various regions like + USA FCC, Canada IC, Spain, France, Europe ETSI, Japan ... + + Usage: + iwpriv ethX setregioncode 0x10: set region code to USA (0x10). + +getregioncode + This command is used to get the region code information set in the + station. + +setbcnavg + Set the weighting factor for calculating RSSI. + +getbcnavg + Get weighting factor for calculating RSSI. + +setdataavg + Set the weighting factor for calculating SNR. + +setlisteninter + This command is used to set the listen interval in the + station. + + where the value ranges between 1 - 255 + +getlisteninter + This command is used to get the listen interval value set in the + station. + +setmultipledtim + This command is used to set the multiple dtim value in the + station. + where the value is 1,2,3,4,5,0xfffe + 0xfffe means the firmware will use listen interval in association + command for waking up + +getmultipledtim + This command is used to get the multiple dtim value set in the station. + +atimwindow + This command is used to set the atim value in the + station. + + where the value ranges between 0 - 50 + +deauth + This command is used to send the de-authentication to the AP with which + the station is associated. This command is valid only when + station is in Infrastructure mode. + + Note: This command is available only when STA is connected. + +adhocstop + This command is used to stop beacon transmission from the station and + go into idle state in ad-hoc mode. + + Note: This command is available only when STA is connected. + +radioon + This command is used to turn on the RF antenna. + +radiooff + This command is sued to turn off the RF antenna. + +scanmode + This command is used to set the station to scan for either IBSS + networks or BSS networks or both BSS and IBSS networks. This + command can be used with sub commands, + + where the value for + bss -- Scan All the BSS networks. + ibss -- Scan All the IBSS networks. + any -- Scan both BSS and IBSS networks. + + + +setwpaie + This command is used by WPA supplicant to send the WPA-IE to the driver. + +wlanidle-off + This command is used to get into idle state. + + Note: This command is available only when STA is connected. + +wlanidle-on + This command is used to get off the idle state. + + Note: This command is available only when STA is connected. + + +getlog + This command is used to get the 802.11 statistics available in the + station. + + Note: This command is available only when STA is connected. + +getadhocstatus + This command is used to get the ad-hoc Network Status. + + The various status codes are: + AdhocStarted + AdhocJoined + AdhocIdle + InfraMode + AutoUnknownMode + + Note: This command is available only when STA is connected. + +adhocgrate + This command is used to enable(1) g_rate, Disable(0) g_rate + and request(2) the status which g_rate is disabled/enabled, + for Ad-hoc creator. + + where value is:- + 0 -- Disabled + 1 -- Enabled + 2 -- Get + +ledgpio + This command is used to set/get LEDs. + + iwpriv ethX ledgpio + will set the corresponding LED for the GPIO Line. + + iwpriv ethX ledgpio + will give u which LEDs are Enabled. + + Usage: + iwpriv eth1 ledgpio 1 0 2 1 3 4 + will enable + LED 1 -> GPIO 0 + LED 2 -> GPIO 1 + LED 3 -> GPIO 4 + + iwpriv eth1 ledgpio + shows LED information in the format as mentioned above. + + Note: LED0 is invalid + Note: Maximum Number of LEDs are 16. + +inactivityto + This command is used by the host to set/get the inactivity timeout value, + which specifies when WLAN device is put to sleep. + + Usage: + iwpriv ethX inactivityto [] + + where the parameter are: + timeout: timeout value in milliseconds. + + Example: + iwpriv eth1 inactivityto + "get the timeout value" + + iwpriv eth1 inactivityto X + "set timeout value to X ms" + + +sleeppd + This command is used to configure the sleep period of the WLAN device. + + Usage: + iwpriv ethX sleeppd [] + + where the parameter are: + Period: sleep period in milliseconds. Range 10~60. + + Example: + iwpriv eth1 sleeppd 10 + "set period as 10 ms" + iwpriv eth1 sleeppd + "get the sleep period configuration" + +enable11d + This command is used to control 11d + where value is:- + 1 -- Enabled + 0 -- Disabled + 2 -- Get + + + + +tpccfg + Enables or disables automatic transmit power control. + + The first parameter turns this feature on (1) or off (0). When turning + on, the user must also supply four more parameters in the following + order: + -UseSNR (Use SNR (in addition to PER) for TPC algorithm), + -P0 (P0 power level for TPC), + -P1 (P1 power level for TPC), + -P2 (P2 power level for TPC). + + Usage: + iwpriv ethX tpccfg: Get current configuration + iwpriv ethX tpccfg 0: disable auto TPC + iwpriv ethX tpccfg 0x01 0x00 0x05 0x0a 0x0d: enable auto TPC; do not use SNR; + P0=0x05; P1=0x0a; P2=0x0d; + iwpriv ethX tpccfg 0x01 0x01 0x05 0x0a 0x0d: enable auto TPC; use SNR; + P0=0x05; P1=0x0a; P2=0x0d. + +powercfg + Enables or disables power adaptation. + + The first parameter turns this feature on (1) or off (0). When turning + on, the user must also supply three more parameters in the following + order: + -P0 (P0 power level for Power Adaptation), + -P1 (P1 power level for Power Adaptation), + -P2 (P2 power level for Power Adaptation). + + Usage: + iwpriv ethX powercfg: Get current configuration + iwpriv ethX powercfg 0: disable power adaptation + iwpriv ethX powercfg 1 0x0d 0x0f 0x12: enable power adaptation; + P0=0x0d; P1=0x0f; P2=0x12. + +getafc + This command returns automatic frequency control parameters. It returns + three integers: + -P0: automatic is on (1), or off (0), + -P1: current timing offset in PPM (part per million), and + -P2: current frequency offset in PPM. + +setafc + Set automatic frequency control options. + + The first parameter turns automatic on (1) or off (0). + The user must supply two more parameters in either case, in the following + order: + + When auto is on: + + -P0 (automatic adjustment frequency threshold in PPM), + -P1 (automatic adjustment period in beacon period), + + When auto is off: + + -P0 (manual adjustment timing offset in PPM), and + -P1 (manual adjustment frequency offset in PPM). + + Usage: + iwpriv ethX setafc 0 10 10: manual adjustment, both timing and frequcncy + offset are 10 PPM. + + iwpriv ethX setafc 1 10 10 enable afc, automatic adjustment, + frequency threshold 10 PPM, for every 10 beacon periods. + + + +scanprobes + This command sets number of probe requests per channel. + + Usage: + iwpriv ethX scanprobes 3 (set scan probes to 3) + iwpriv ethX scanprobes (get scan probes) + +lolisteninter + This command sets the value of listen interval. + + Usage: + iwpriv ethX lolisteninter 234 (set the lolisteninter to 234) + iwpriv ethX lolisteninter (get the lolisteninter value) + +rateadapt + This command sets the data rates bitmap. + Where + 0: Disable auto rate adapt + 1: Enable auto rate adapt + + + data rate bitmap + Bit Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 12-15 Reserved + + Usage: + iwpriv ethX rateadapt + read the currect data rate setting + iwpriv ethX rateadapt 1 0x07 + enable auto data rate adapt and + data rates are 1Mbps, 2Mbsp and 5.5Mbps + + +txcontrol + This command is used to set the Tx rate, ack policy, and retry limit on a per packet basis. + + Where value is: + if bit[4] == 1: + bit[3:0] -- 0 1 2 3 4 5 6 7 8 9 10 11 12 13-16 + Data Rate(Mbps) -- 1 2 5.5 11 Rsv 6 9 12 18 24 36 48 54 Rsv + + bit[12:8] + if bit[12] == 1, bit[11:8] specifies the Tx retry limit. + + bit[14:13] specifies per packet ack policy: + bit[14:13] + 1 0 use immediate ack policy for this packet + 1 1 use no ack policy for this packet + 0 x use the per-packet ack policy setting + + Usage: + iwpriv ethX txcontrol 0x7513 + Use no-ack policy, 5 retires for Tx, 11Mbps rate + + + +psnullinterval + This command is used to set/request NULL package interval for Power Save + under infrastructure mode. + + where value is:- + -1 -- Disabled + n>0 -- Set interval as n (seconds) + +prescan + This command is used to enable (1)/disable(0) auto prescan before assoicate to the ap + + where value is:- + 0 -- Disabled + 1 -- Enabled + 2 -- Get + +getrxinfo + This command gets non average value of Signal to Noise Ratio of Data and rate index. + + The following table shows RateIndex and Rate + + RateIndex Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 13-15 Reserved + +gettxrate + This command gets current Tx rate index of the first packet associated with Rate Adaptation. + + The following table shows RateIndex and Rate + + RateIndex Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 13-15 Reserved + +bcninterval + This command is used to sets beacon interval in adhoc mode when an argument is given, and gets current adhoc + beacon interval when no argument is given. The valid beacon interval is between 20 - 1000, + default beacon interval is 100. + + Usage: + iwpriv ethX bcninterval 100 (set adhoc beacon interval to 100) + iwpriv ethX bcninterval (get adhoc beacon interval) + +fwt_add + This command is used to insert an entry into the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_add da ra [metric dir ssn dsn hopcount ttl expiration sleepmode snr] + + The parameters between brackets are optional, but they must appear in + the order specified. For example, if you want to specify the metric, + you must also specify the dir, ssn, and dsn but you need not specify the + hopcount, expiration, sleepmode, or snr. Any unspecified parameters + will be assigned the defaults specified below. + + The different parameters are:- + da -- DA MAC address in the form 00:11:22:33:44:55 + ra -- RA MAC address in the form 00:11:22:33:44:55 + metric -- route metric (cost: smaller-metric routes are + preferred, default is 0) + dir -- direction (1 for direct, 0 for reverse, + default is 1) + ssn -- Source Sequence Number (time at the RA for + reverse routes. Default is 0) + dsn -- Destination Sequence Number (time at the DA + for direct routes. Default is 0) + hopcount -- hop count (currently unused, default is 0) + ttl -- TTL (Only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is + 1024us, or ~ 1ms. Use 0 for an indefinite + entry, default is 0) + sleepmode -- RA's sleep mode (currently unused, default is + 0) + snr -- SNR in the link to RA (currently unused, + default is 0) + + The command does not return anything. + +fwt_del + This command is used to remove an entry to the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_del da ra [dir] + + where the different parameters are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + dir -- direction (1 for direct, 0 for reverse, + default is 1) + + The command does not return anything. + +fwt_lookup + This command is used to get the best route in the FWT table to a given + host. The only parameter is the MAC address of the host that is being + looked for. + + iwpriv ethX fwt_lookup da + + where:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + + The command returns an output string identical to the one returned by + fwt_list described below. + + +fwt_list + This command is used to list a route from the FWT table. The only + parameter is the index into the table. If you want to list all the + routes in a table, start with index=0, and keep listing until you get a + "(null)" string. Note that the indicies may change as the fwt is + updated. It is expected that most users will not use fwt_list directly, + but that a utility similar to the traditional route command will be used + to invoke fwt_list over and over. + + iwpriv ethX fwt_list index + + The output is a string of the following form: + + da ra metric dir ssn dsn hopcount ttl expiration sleepmode snr + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + +fwt_list_route + This command is used to list a route from the FWT table. The only + parameter is the route ID. If you want to list all the routes in a + table, start with rid=0, and keep incrementing rid until you get a + "(null)" string. This function is similar to fwt_list. The only + difference is the output format. Also note that this command is meant + for debugging. It is expected that users will use fwt_lookup and + fwt_list. One important reason for this is that the route id may change + as the route table is altered. + + iwpriv ethX fwt_list_route rid + + The output is a string of the following form: + + da metric dir nid ssn dsn hopcount ttl expiration + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + nid -- Next-hop (neighbor) host ID (nid) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL count (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + +fwt_list_neigh + This command is used to list a neighbor from the FWT table. The only + parameter is the neighbor ID. If you want to list all the neighbors in a + table, start with nid=0, and keep incrementing nid until you get a + "(null)" string. Note that the nid from a fwt_list_route command can be + used as an input to this command. Also note that this command is meant + mostly for debugging. It is expected that users will use fwt_lookup. + One important reason for this is that the neighbor id may change as the + neighbor table is altered. + + iwpriv ethX fwt_list_neigh nid + + The output is a string of the following form: + + ra sleepmode snr references + + where the different fields are:- + ra -- RA MAC address (in the form "00:11:22:33:44:55") + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + references -- RA's reference counter + +fwt_reset + This command is used to reset the FWT table, getting rid of all the + entries. There are no input parameters. + + iwpriv ethX fwt_reset + + The command does not return anything. + +fwt_cleanup + This command is used to perform user-based garbage recollection. The + FWT table is checked, and all the entries that are expired or invalid + are cleaned. Note that this is exported to the driver for debugging + purposes, as garbage collection is also fired by the firmware when in + space problems. There are no input parameters. + + iwpriv ethX fwt_cleanup + + The command does returns the number of invalid/expired routes deleted. + +fwt_time + This command returns a card's internal time representation. It is this + time that is used to represent the expiration times of FWT entries. The + number is not consistent from card to card; it is simply a timer count. + The fwt_time command is used to inspect the timer so that expiration + times reported by fwt_list can be properly interpreted. + + iwpriv ethX fwt_time + +mesh_get_ttl + + The mesh ttl is the number of hops a mesh packet can traverse before it + is dropped. This parameter is used to prevent infinite loops in the + mesh network. The value returned by this function is the ttl assigned + to all mesh packets. Currently there is no way to control the ttl on a + per packet or per socket basis. + + iwpriv ethX mesh_get_ttl + +mesh_set_ttl ttl + + Set the ttl. The argument must be between 0 and 255. + + iwpriv ethX mesh_set_ttl + +========================= +ETHTOOL +========================= + + +Use the -i option to retrieve version information from the driver. + +# ethtool -i eth0 +driver: libertas +version: COMM-USB8388-318.p4 +firmware-version: 5.110.7 +bus-info: + +Use the -e option to read the EEPROM contents of the card. + + Usage: + ethtool -e ethX [raw on|off] [offset N] [length N] + + -e retrieves and prints an EEPROM dump for the specified ethernet + device. When raw is enabled, then it dumps the raw EEPROM data + to stdout. The length and offset parameters allow dumping cer- + tain portions of the EEPROM. Default is to dump the entire EEP- + ROM. + +# ethtool -e eth0 offset 0 length 16 +Offset Values +------ ------ +0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 + +======================== +DEBUGFS COMMANDS +======================== + +those commands are used via debugfs interface + +=========== +rdmac +rdbbp +rdrf + These commands are used to read the MAC, BBP and RF registers from the + card. These commands take one parameter that specifies the offset + location that is to be read. This parameter must be specified in + hexadecimal (its possible to preceed preceding the number with a "0x"). + + Path: /debugfs/libertas_wireless/ethX/registers/ + + Usage: + echo "0xa123" > rdmac ; cat rdmac + echo "0xa123" > rdbbp ; cat rdbbp + echo "0xa123" > rdrf ; cat rdrf +wrmac +wrbbp +wrrf + These commands are used to write the MAC, BBP and RF registers in the + card. These commands take two parameters that specify the offset + location and the value that is to be written. This parameters must + be specified in hexadecimal (its possible to preceed the number + with a "0x"). + + Usage: + echo "0xa123 0xaa" > wrmac + echo "0xa123 0xaa" > wrbbp + echo "0xa123 0xaa" > wrrf + +sleepparams + This command is used to set the sleepclock configurations + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat sleepparams: reads the current sleepclock configuration + + echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + +subscribed_events + + The subscribed_events directory contains the interface for the + subscribed events API. + + Path: /debugfs/libertas_wireless/ethX/subscribed_events/ + + Each event is represented by a filename. Each filename consists of the + following three fields: + Value Frequency Subscribed + + To read the current values for a given event, do: + cat event + To set the current values, do: + echo "60 2 1" > event + + Frequency field specifies the reporting frequency for this event. + If it is set to 0, then the event is reported only once, and then + automatically unsubscribed. If it is set to 1, then the event is + reported every time it occurs. If it is set to N, then the event is + reported every Nth time it occurs. + + beacon_missed + Value field specifies the number of consecutive missing beacons which + triggers the LINK_LOSS event. This event is generated only once after + which the firmware resets its state. At initialization, the LINK_LOSS + event is subscribed by default. The default value of MissedBeacons is + 60. + + failure_count + Value field specifies the consecutive failure count threshold which + triggers the generation of the MAX_FAIL event. Once this event is + generated, the consecutive failure count is reset to 0. + At initialization, the MAX_FAIL event is NOT subscribed by + default. + + high_rssi + This event is generated when the average received RSSI in beacons goes + above a threshold, specified by Value. + + low_rssi + This event is generated when the average received RSSI in beacons goes + below a threshold, specified by Value. + + high_snr + This event is generated when the average received SNR in beacons goes + above a threshold, specified by Value. + + low_snr + This event is generated when the average received SNR in beacons goes + below a threshold, specified by Value. + +extscan + This command is used to do a specific scan. + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: echo "SSID" > extscan + + Example: + echo "LINKSYS-AP" > extscan + + To see the results of use getscantable command. + +getscantable + + Display the current contents of the driver scan table (ie. get the + scan results). + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat getscantable + +setuserscan + Initiate a customized scan and retrieve the results + + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + echo "[ARGS]" > setuserscan + + where [ARGS]: + + chan=[chan#][band][mode] where band is [a,b,g] and mode is + blank for active or 'p' for passive + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + ssid="[SSID]" specify a SSID filter for the scan + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + probes=[#] number of probe requests to send on each chan + type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + + Any combination of the above arguments can be supplied on the command line. + If the chan token is absent, a full channel scan will be completed by + the driver. If the dur or probes tokens are absent, the driver default + setting will be used. The bssid and ssid fields, if blank, + will produce an unfiltered scan. The type field will default to 3 (Any) + and the keep field will default to 0 (Discard). + + Examples: + 1) Perform an active scan on channels 1, 6, and 11 in the 'g' band: + echo "chan=1g,6g,11g" > setuserscan + + 2) Perform a passive scan on channel 11 for 20 ms: + echo "chan=11gp dur=20" > setuserscan + + 3) Perform an active scan on channels 1, 6, and 11; and a passive scan on + channel 36 in the 'a' band: + + echo "chan=1g,6g,11g,36ap" > setuserscan + + 4) Perform an active scan on channel 6 and 36 for a specific SSID: + echo "chan=6g,36a ssid="TestAP"" > setuserscan + + 5) Scan all available channels (B/G, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan + + 6) Scan channel 6, for all infrastructure networks, sending two probe + requests. Keep the previous scan table intact. Update any duplicate + BSSID/SSID matches with the new scan data: + echo "chan=6g type=1 probes=2 keep=1" > setuserscan + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. + +============================================================================== diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c new file mode 100644 index 0000000..b55c7f5 --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.c @@ -0,0 +1,588 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#include +#include + +#include "assoc.h" +#include "join.h" +#include "decl.h" +#include "hostcmd.h" +#include "host.h" + + +static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static int assoc_helper_essid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "New SSID requested: %s\n", assoc_req->ssid.ssid); + if (assoc_req->mode == wlan802_11infrastructure) { + if (adapter->prescan) { + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1); + } + + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, + NULL, wlan802_11infrastructure); + if (i >= 0) { + lbs_pr_debug(1, + "SSID found in scan list ... associating...\n"); + + ret = wlan_associate(priv, &adapter->scantable[i]); + if (ret == 0) { + memcpy(&assoc_req->bssid, + &adapter->scantable[i].macaddress, + ETH_ALEN); + } + } else { + lbs_pr_debug(1, "SSID '%s' not found; cannot associate\n", + assoc_req->ssid.ssid); + } + } else if (assoc_req->mode == wlan802_11ibss) { + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0); + + /* Search for the requested SSID in the scan table */ + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL, + wlan802_11ibss); + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List, so join\n", ret); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + /* else send START command */ + lbs_pr_debug(1, "SSID not found in list, so creating adhoc" + " with SSID '%s'\n", assoc_req->ssid.ssid); + libertas_start_adhoc_network(priv, &assoc_req->ssid); + } + memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN); + } + + LEAVE(); + return ret; +} + + +static int assoc_helper_bssid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i, ret = 0; + + ENTER(); + + lbs_pr_debug(1, "ASSOC: WAP: BSSID = " MAC_FMT "\n", + MAC_ARG(assoc_req->bssid)); + + /* Search for index position in list for requested MAC */ + i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid, + assoc_req->mode); + if (i < 0) { + lbs_pr_debug(1, "ASSOC: WAP: BSSID " MAC_FMT " not found, " + "cannot associate.\n", MAC_ARG(assoc_req->bssid)); + goto out; + } + + if (assoc_req->mode == wlan802_11infrastructure) { + ret = wlan_associate(priv, &adapter->scantable[i]); + lbs_pr_debug(1, "ASSOC: return from wlan_associate(bssd) was %d\n", ret); + } else if (assoc_req->mode == wlan802_11ibss) { + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } + memcpy(&assoc_req->ssid, &adapter->scantable[i].ssid, + sizeof(struct WLAN_802_11_SSID)); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_associate(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0, done = 0; + + /* If we're given and 'any' BSSID, try associating based on SSID */ + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN) + && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) { + ret = assoc_helper_bssid(priv, assoc_req); + done = 1; + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + } + + if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + ret = assoc_helper_essid(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + + return ret; +} + + +static int assoc_helper_mode(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->mode == adapter->inframode) { + LEAVE(); + return 0; + } + + if (assoc_req->mode == wlan802_11infrastructure) { + if (adapter->psstate != PS_STATE_FULL_POWER) + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + adapter->psmode = wlan802_11powermodecam; + } + + adapter->inframode = assoc_req->mode; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + 0, cmd_option_waitforrsp, + OID_802_11_INFRASTRUCTURE_MODE, + (void *) assoc_req->mode); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wep_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i; + int ret = 0; + + ENTER(); + + /* Set or remove WEP keys */ + if ( assoc_req->wep_keys[0].len + || assoc_req->wep_keys[1].len + || assoc_req->wep_keys[2].len + || assoc_req->wep_keys[3].len) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, assoc_req); + } else { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_remove, + cmd_option_waitforrsp, + 0, NULL); + } + + if (ret) + goto out; + + /* enable/disable the MAC's WEP packet filter */ + if (assoc_req->secinfo.WEPstatus == wlan802_11WEPenabled) + adapter->currentpacketfilter |= cmd_act_mac_wep_enable; + else + adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable; + ret = libertas_set_mac_packet_filter(priv); + if (ret) + goto out; + + mutex_lock(&adapter->lock); + + /* Copy WEP keys into adapter wep key fields */ + for (i = 0; i < 4; i++) { + memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx; + + mutex_unlock(&adapter->lock); + +out: + LEAVE(); + return ret; +} + +static int assoc_helper_secinfo(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + memcpy(&adapter->secinfo, &assoc_req->secinfo, + sizeof(struct wlan_802_11_security)); + + ret = libertas_set_mac_packet_filter(priv); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0; + + ENTER(); + + /* enable/Disable RSN */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_enable_rsn, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + if (ret) + goto out; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_key_material, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_ie(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { + memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); + adapter->wpa_ie_len = assoc_req->wpa_ie_len; + } else { + memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN); + adapter->wpa_ie_len = 0; + } + + LEAVE(); + return ret; +} + + +static int should_deauth_infrastructure(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new SSID in " + " configuration request.\n"); + return 1; + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + if (adapter->secinfo.authmode != + assoc_req->secinfo.authmode) { + lbs_pr_debug(1, "Deauthenticating due to updated security " + "info in configuration request.\n"); + return 1; + } + } + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new BSSID in " + " configuration request.\n"); + return 1; + } + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != wlan802_11infrastructure) + return 1; + } + + return 0; +} + + +static int should_stop_adhoc(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength) + return 1; + if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid, + sizeof(struct WLAN_802_11_SSID))) + return 1; + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != wlan802_11ibss) + return 1; + } + + return 0; +} + + +void wlan_association_worker(struct work_struct *work) +{ + wlan_private *priv = container_of(work, wlan_private, assoc_work.work); + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req = NULL; + int ret = 0; + int find_any_ssid = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = adapter->assoc_req; + adapter->assoc_req = NULL; + mutex_unlock(&adapter->lock); + + if (!assoc_req) { + LEAVE(); + return; + } + + lbs_pr_debug(1, "ASSOC: starting new association request: flags = 0x%lX\n", + assoc_req->flags); + + /* If 'any' SSID was specified, find an SSID to associate with */ + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) + && !assoc_req->ssid.ssidlength) + find_any_ssid = 1; + + /* But don't use 'any' SSID if there's a valid locked BSSID to use */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN) + && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN)) + find_any_ssid = 0; + } + + if (find_any_ssid) { + enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; + + ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid, + assoc_req->mode, &new_mode); + if (ret) { + lbs_pr_debug(1, "Could not find best network\n"); + ret = -ENETUNREACH; + goto out; + } + + /* Ensure we switch to the mode of the AP */ + if (assoc_req->mode == wlan802_11autounknown) { + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + assoc_req->mode = new_mode; + } + } + + /* + * Check if the attributes being changing require deauthentication + * from the currently associated infrastructure access point. + */ + if (adapter->inframode == wlan802_11infrastructure) { + if (should_deauth_infrastructure(adapter, assoc_req)) { + ret = libertas_send_deauthentication(priv); + if (ret) { + lbs_pr_debug(1, "Deauthentication due to new " + "configuration request failed: %d\n", + ret); + } + } + } else if (adapter->inframode == wlan802_11ibss) { + if (should_stop_adhoc(adapter, assoc_req)) { + ret = libertas_stop_adhoc_network(priv); + if (ret) { + lbs_pr_debug(1, "Teardown of AdHoc network due to " + "new configuration request failed: %d\n", + ret); + } + + } + } + + /* Send the various configuration bits to the firmware */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + ret = assoc_helper_mode(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) mode: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { + ret = assoc_helper_wep_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + ret = assoc_helper_secinfo(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + ret = assoc_helper_wpa_ie(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + ret = assoc_helper_wpa_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + /* SSID/BSSID should be the _last_ config option set, because they + * trigger the association attempt. + */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) + || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + int success = 1; + + ret = assoc_helper_associate(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: association attempt unsuccessful: %d\n", + ret); + success = 0; + } + + if (adapter->connect_status != libertas_connected) { + lbs_pr_debug(1, "ASSOC: assoication attempt unsuccessful, " + "not connected.\n"); + success = 0; + } + + if (success) { + lbs_pr_debug(1, "ASSOC: association attempt successful. " + "Associated to '%s' (" MAC_FMT ")\n", + assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid)); + libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, 0, NULL); + + libertas_prepare_and_send_command(priv, + cmd_802_11_get_log, + 0, cmd_option_waitforrsp, 0, NULL); + } else { + + ret = -1; + } + } + +out: + if (ret) { + lbs_pr_debug(1, "ASSOC: reconfiguration attempt unsuccessful: %d\n", + ret); + } + kfree(assoc_req); + LEAVE(); +} + + +/* + * Caller MUST hold any necessary locks + */ +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter) +{ + struct assoc_request * assoc_req; + + if (!adapter->assoc_req) { + adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL); + if (!adapter->assoc_req) { + lbs_pr_info("Not enough memory to allocate association" + " request!\n"); + return NULL; + } + } + + /* Copy current configuration attributes to the association request, + * but don't overwrite any that are already set. + */ + assoc_req = adapter->assoc_req; + if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + } + + if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) + assoc_req->channel = adapter->curbssparams.channel; + + if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) + assoc_req->mode = adapter->inframode; + + if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + memcpy(&assoc_req->bssid, adapter->curbssparams.bssid, + ETH_ALEN); + } + + if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { + int i; + for (i = 0; i < 4; i++) { + memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + } + + if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) + assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx; + + if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + memcpy(&assoc_req->secinfo, &adapter->secinfo, + sizeof(struct wlan_802_11_security)); + } + + if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie, + MAX_WPA_IE_LEN); + assoc_req->wpa_ie_len = adapter->wpa_ie_len; + } + + return assoc_req; +} + + diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h new file mode 100644 index 0000000..2ffd82d --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#ifndef _WLAN_ASSOC_H_ +#define _WLAN_ASSOC_H_ + +#include "dev.h" + +void wlan_association_worker(struct work_struct *work); + +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter); + +#define ASSOC_DELAY (HZ / 2) +static inline void wlan_postpone_association_work(wlan_private *priv) +{ + if (priv->adapter->surpriseremoved) + return; + cancel_delayed_work(&priv->assoc_work); + queue_delayed_work(priv->assoc_thread, &priv->assoc_work, ASSOC_DELAY); +} + +static inline void wlan_cancel_association_work(wlan_private *priv) +{ + cancel_delayed_work(&priv->assoc_work); + if (priv->adapter->assoc_req) { + kfree(priv->adapter->assoc_req); + priv->adapter->assoc_req = NULL; + } +} + +#endif /* _WLAN_ASSOC_H */ diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c new file mode 100644 index 0000000..bfdac58 --- /dev/null +++ b/drivers/net/wireless/libertas/cmd.c @@ -0,0 +1,1958 @@ +/** + * This file contains the handling of command. + * It prepares command and sends it to firmware when it is ready. + */ + +#include +#include "host.h" +#include "hostcmd.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode); + +static u16 commands_allowed_in_ps[] = { + cmd_802_11_rssi, +}; + +/** + * @brief This function checks if the commans is allowed + * in PS mode not. + * + * @param command the command ID + * @return TRUE or FALSE + */ +static u8 is_command_allowed_in_ps(u16 command) +{ + int count = sizeof(commands_allowed_in_ps) + / sizeof(commands_allowed_in_ps[0]); + int i; + + for (i = 0; i < count; i++) { + if (command == cpu_to_le16(commands_allowed_in_ps[i])) + return 1; + } + + return 0; +} + +static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd) +{ + struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_get_hw_spec); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN); + memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_ps_mode(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode; + u16 action = cmd_action; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_ps_mode); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + + S_DS_GEN); + psm->action = cpu_to_le16(cmd_action); + psm->multipledtim = 0; + switch (action) { + case cmd_subcmd_enter_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n"); + lbs_pr_debug(1, "locallisteninterval = %d\n", + adapter->locallisteninterval); + + psm->locallisteninterval = + cpu_to_le16(adapter->locallisteninterval); + psm->nullpktinterval = + cpu_to_le16(adapter->nullpktinterval); + psm->multipledtim = + cpu_to_le16(priv->adapter->multipledtim); + break; + + case cmd_subcmd_exit_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n"); + break; + + case cmd_subcmd_sleep_confirmed: + lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n"); + break; + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + u16 *timeout = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_inactivity_timeout); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout) + + S_DS_GEN); + + cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action); + + if (cmd_action) + cmd->params.inactivity_timeout.timeout = + cpu_to_le16(*timeout); + else + cmd->params.inactivity_timeout.timeout = 0; + + return 0; +} + +static int wlan_cmd_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_sleep_params); + + if (cmd_action == cmd_act_get) { + memset(&adapter->sp, 0, sizeof(struct sleep_params)); + memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params)); + sp->action = cpu_to_le16(cmd_action); + } else if (cmd_action == cmd_act_set) { + sp->action = cpu_to_le16(cmd_action); + sp->error = cpu_to_le16(adapter->sp.sp_error); + sp->offset = cpu_to_le16(adapter->sp.sp_offset); + sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime); + sp->calcontrol = (u8) adapter->sp.sp_calcontrol; + sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk; + sp->reserved = cpu_to_le16(adapter->sp.sp_reserved); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_set_wep(wlan_private * priv, + struct cmd_ds_command *cmd, + u32 cmd_act, + void * pdata_buf) +{ + struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_set_wep); + cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep)) + + S_DS_GEN); + + if (cmd_act == cmd_act_add) { + int i; + + if (!assoc_req) { + lbs_pr_debug(1, "Invalid association request!"); + ret = -1; + goto done; + } + + wep->action = cpu_to_le16(cmd_act_add); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (assoc_req->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + + lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex); + + /* Copy key types and material to host command structure */ + for (i = 0; i < 4; i++) { + struct WLAN_802_11_KEY * pkey = &assoc_req->wep_keys[i]; + + switch (pkey->len) { + case KEY_LEN_WEP_40: + wep->keytype[i] = cmd_type_wep_40_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case KEY_LEN_WEP_104: + wep->keytype[i] = cmd_type_wep_104_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case 0: + break; + default: + lbs_pr_debug(1, "Invalid WEP key %d length of %d\n", + i, pkey->len); + ret = -1; + goto done; + break; + } + } + } else if (cmd_act == cmd_act_remove) { + /* ACT_REMOVE clears _all_ WEP keys */ + wep->action = cpu_to_le16(cmd_act_remove); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (adapter->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + } + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_enable_rsn(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn; + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_enable_rsn); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) + + S_DS_GEN); + penableRSN->action = cpu_to_le16(cmd_action); + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + penableRSN->enable = cpu_to_le16(cmd_enable_rsn); + } else { + penableRSN->enable = cpu_to_le16(cmd_disable_rsn); + } + + return 0; +} + + +static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset, + struct WLAN_802_11_KEY * pkey) +{ + pkeyparamset->keytypeid = cpu_to_le16(pkey->type); + + if (pkey->flags & KEY_INFO_WPA_ENABLED) { + pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED); + } else { + pkeyparamset->keyinfo = cpu_to_le16(!KEY_INFO_WPA_ENABLED); + } + + if (pkey->flags & KEY_INFO_WPA_UNICAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); + } else if (pkey->flags & KEY_INFO_WPA_MCAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); + } + + pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkeyparamset->keylen = cpu_to_le16(pkey->len); + memcpy(pkeyparamset->key, pkey->key, pkey->len); + pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid) + + sizeof(pkeyparamset->keyinfo) + + sizeof(pkeyparamset->keylen) + + sizeof(pkeyparamset->key)); +} + +static int wlan_cmd_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, + u32 cmd_oid, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_key_material *pkeymaterial = + &cmd->params.keymaterial; + int ret = 0; + int index = 0; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_key_material); + pkeymaterial->action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_get) { + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action)); + ret = 0; + goto done; + } + + memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet)); + + if (adapter->wpa_unicast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_unicast_key); + index++; + } + + if (adapter->wpa_mcast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_mcast_key); + index++; + } + + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action) + + index * sizeof(struct MrvlIEtype_keyParamSet)); + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_reset(wlan_private * priv, + struct cmd_ds_command *cmd, int cmd_action) +{ + struct cmd_ds_802_11_reset *reset = &cmd->params.reset; + + cmd->command = cpu_to_le16(cmd_802_11_reset); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN); + reset->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_802_11_get_log(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_log); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_get_stat(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_stat); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, + int cmd_oid, void *pdata_buf) +{ + struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib; + wlan_adapter *adapter = priv->adapter; + u8 ucTemp; + + ENTER(); + + lbs_pr_debug(1, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + + cmd->command = cpu_to_le16(cmd_802_11_snmp_mib); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_snmp_mib) + + S_DS_GEN); + + switch (cmd_oid) { + case OID_802_11_INFRASTRUCTURE_MODE: + { + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode = + (enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf; + pSNMPMIB->querytype = cpu_to_le16(cmd_act_set); + pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i); + pSNMPMIB->bufsize = sizeof(u8); + if (mode == wlan802_11infrastructure) + ucTemp = SNMP_MIB_VALUE_INFRA; + else + ucTemp = SNMP_MIB_VALUE_ADHOC; + + memmove(pSNMPMIB->value, &ucTemp, sizeof(u8)); + + break; + } + + case OID_802_11D_ENABLE: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) dot11d_i); + + if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = cmd_act_set; + pSNMPMIB->bufsize = sizeof(u16); + ulTemp = *(u32 *)pdata_buf; + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + } + break; + } + + case OID_802_11_FRAGMENTATION_THRESHOLD: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) fragthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) pdata_buf); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + + } + + break; + } + + case OID_802_11_RTS_THRESHOLD: + { + + u32 ulTemp; + pSNMPMIB->oid = le16_to_cpu((u16) rtsthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) + pdata_buf); + *(unsigned short *)(pSNMPMIB->value) = + cpu_to_le16((u16) ulTemp); + + } + break; + } + case OID_802_11_TX_RETRYCOUNT: + pSNMPMIB->oid = cpu_to_le16((u16) short_retrylim_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16)); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) adapter->txretrycount); + } + + break; + default: + break; + } + + lbs_pr_debug(1, + "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n", + cmd->command, cmd->size, cmd->seqnum, cmd->result); + + lbs_pr_debug(1, + "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n", + pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize, + *(u16 *) pSNMPMIB->value); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_radio_control(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_radio_control *pradiocontrol = + &cmd->params.radio; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_radio_control); + + pradiocontrol->action = cpu_to_le16(cmd_action); + + switch (adapter->preamble) { + case cmd_type_short_preamble: + pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE); + break; + + case cmd_type_long_preamble: + pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE); + break; + + case cmd_type_auto_preamble: + default: + pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE); + break; + } + + if (adapter->radioon) + pradiocontrol->control |= cpu_to_le16(TURN_ON_RF); + else + pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + + struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rf_tx_power); + prtp->action = cmd_action; + + lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size, + cmd->command, prtp->action); + + switch (cmd_action) { + case cmd_act_tx_power_opt_get: + prtp->action = cpu_to_le16(cmd_act_get); + prtp->currentlevel = 0; + break; + + case cmd_act_tx_power_opt_set_high: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_high); + break; + + case cmd_act_tx_power_opt_set_mid: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_mid); + break; + + case cmd_act_tx_power_opt_set_low: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf)); + break; + } + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant; + + cmd->command = cpu_to_le16(cmd_802_11_rf_antenna); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) + + S_DS_GEN); + + rant->action = cpu_to_le16(cmd_action); + if ((cmd_action == cmd_act_set_rx) || + (cmd_action == cmd_act_set_tx)) { + rant->antennamode = + cpu_to_le16((u16) (*(u32 *) pdata_buf)); + } + + return 0; +} + +static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_rate_adapt_rateset + *rateadapt = &cmd->params.rateset; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rate_adapt_rateset); + + ENTER(); + + rateadapt->action = cmd_action; + rateadapt->enablehwauto = adapter->enablehwauto; + rateadapt->bitmap = adapter->ratebitmap; + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate; + wlan_adapter *adapter = priv->adapter; + u16 action = cmd_action; + + ENTER(); + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) + + S_DS_GEN); + + cmd->command = cpu_to_le16(cmd_802_11_data_rate); + + memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate)); + + pdatarate->action = cpu_to_le16(cmd_action); + + if (action == cmd_act_set_tx_fix_rate) { + pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate); + lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n", + adapter->datarate); + } else if (action == cmd_act_set_tx_auto) { + lbs_pr_debug(1, "Setting FW for AUTO rate\n"); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_mac_multicast_adr(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_mac_multicast_adr); + + pMCastAdr->action = cpu_to_le16(cmd_action); + pMCastAdr->nr_of_adrs = + cpu_to_le16((u16) adapter->nr_of_multicastmacaddr); + memcpy(pMCastAdr->maclist, adapter->multicastlist, + adapter->nr_of_multicastmacaddr * ETH_ALEN); + + return 0; +} + +static int wlan_cmd_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *cmd, + int option, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel; + + cmd->command = cpu_to_le16(cmd_802_11_rf_channel); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel) + + S_DS_GEN); + + if (option == cmd_opt_802_11_rf_channel_set) { + rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf)); + } + + rfchan->action = cpu_to_le16(option); + + return 0; +} + +static int wlan_cmd_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_rssi); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN); + cmd->params.rssi.N = priv->adapter->bcn_avg_factor; + + /* reset Beacon SNR/NF/RSSI values */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_AVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0; + + return 0; +} + +static int wlan_cmd_reg_access(wlan_private * priv, + struct cmd_ds_command *cmdptr, + u8 cmd_action, void *pdata_buf) +{ + struct wlan_offset_value *offval; + + ENTER(); + + offval = (struct wlan_offset_value *)pdata_buf; + + switch (cmdptr->command) { + case cmd_mac_reg_access: + { + struct cmd_ds_mac_reg_access *macreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_mac_reg_access) + + S_DS_GEN); + macreg = + (struct cmd_ds_mac_reg_access *)&cmdptr->params. + macreg; + + macreg->action = cpu_to_le16(cmd_action); + macreg->offset = cpu_to_le16((u16) offval->offset); + macreg->value = cpu_to_le32(offval->value); + + break; + } + + case cmd_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *bbpreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_bbp_reg_access) + + S_DS_GEN); + bbpreg = + (struct cmd_ds_bbp_reg_access *)&cmdptr->params. + bbpreg; + + bbpreg->action = cpu_to_le16(cmd_action); + bbpreg->offset = cpu_to_le16((u16) offval->offset); + bbpreg->value = (u8) offval->value; + + break; + } + + case cmd_rf_reg_access: + { + struct cmd_ds_rf_reg_access *rfreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_rf_reg_access) + + S_DS_GEN); + rfreg = + (struct cmd_ds_rf_reg_access *)&cmdptr->params. + rfreg; + + rfreg->action = cpu_to_le16(cmd_action); + rfreg->offset = cpu_to_le16((u16) offval->offset); + rfreg->value = (u8) offval->value; + + break; + } + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_mac_address); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.macadd.action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_set) { + memcpy(cmd->params.macadd.macadd, + adapter->current_addr, ETH_ALEN); + lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6); + } + + return 0; +} + +static int wlan_cmd_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, void *pdata_buf) +{ + struct wlan_ioctl_regrdwr *ea = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_eeprom_access); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.rdeeprom.action = cpu_to_le16(ea->action); + cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset); + cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB); + cmd->params.rdeeprom.value = 0; + + return 0; +} + +static int wlan_cmd_bt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_bt_access *bt_access = &cmd->params.bt; + lbs_pr_debug(1, "BT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_bt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + + S_DS_GEN); + cmd->result = 0; + bt_access->action = cpu_to_le16(cmd_action); + + switch (cmd_action) { + case cmd_act_bt_access_add: + memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); + lbs_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_del: + memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); + lbs_dbg_hex("BT_DEL: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_list: + bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); + break; + case cmd_act_bt_access_reset: + break; + default: + break; + } + return 0; +} + +static int wlan_cmd_fwt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_fwt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); + else + memset(fwt_access, 0, sizeof(*fwt_access)); + + fwt_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_mesh_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_mesh_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(mesh_access, pdata_buf, sizeof(*mesh_access)); + else + memset(mesh_access, 0, sizeof(*mesh_access)); + + mesh_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + + ENTER(); + + if (!cmdnode) { + lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n"); + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + if (!cmdptr) { + lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n"); + goto done; + } + + /* Exit_PS command needs to be queued in the header always. */ + if (cmdptr->command == cmd_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode; + if (psm->action == cmd_subcmd_exit_ps) { + if (adapter->psstate != PS_STATE_FULL_POWER) + addtail = 0; + } + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (addtail) + list_add_tail((struct list_head *)cmdnode, + &adapter->cmdpendingq); + else + list_add((struct list_head *)cmdnode, &adapter->cmdpendingq); + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "QUEUE_CMD: Inserted node=0x%x, cmd=0x%x in cmdpendingq\n", + (u32) cmdnode, + ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command); + +done: + LEAVE(); + return; +} + +/* + * TODO: Fix the issue when DownloadcommandToStation is being called the + * second time when the command timesout. All the cmdptr->xxx are in little + * endian and therefore all the comparissions will fail. + * For now - we are not performing the endian conversion the second time - but + * for PS and DEEP_SLEEP we need to worry + */ +static int DownloadcommandToStation(wlan_private * priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 cmdsize; + u16 command; + + ENTER(); + + if (!adapter || !cmdnode) { + lbs_pr_debug(1, "DNLD_CMD: adapter = %#x, cmdnode = %#x\n", + (int)adapter, (int)cmdnode); + if (cmdnode) { + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + } + ret = -1; + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!cmdptr || !cmdptr->size) { + lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, " + "Not sending\n"); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + adapter->cur_cmd = cmdnode; + adapter->cur_cmd_retcode = 0; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n", + cmdptr->size); + + cmdsize = cmdptr->size; + + command = cpu_to_le16(cmdptr->command); + + cmdnode->cmdwaitqwoken = 0; + cmdsize = cpu_to_le16(cmdsize); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize); + + if (ret != 0) { + lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n"); + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies); + lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize); + + /* Setup the timer after transmit command */ + if (command == cmd_802_11_scan + || command == cmd_802_11_authenticate + || command == cmd_802_11_associate) + mod_timer(&adapter->command_timer, jiffies + (10*HZ)); + else + mod_timer(&adapter->command_timer, jiffies + (5*HZ)); + + ret = 0; + + done: + LEAVE(); + return ret; +} + +static int wlan_cmd_mac_control(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + struct cmd_ds_mac_control *mac = &cmd->params.macctrl; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_mac_control); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN); + mac->action = cpu_to_le16(priv->adapter->currentpacketfilter); + + lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n", + mac->action, cmd->size); + + LEAVE(); + return 0; +} + +/** + * This function inserts command node to cmdfreeq + * after cleans it. Requires adapter->driver_lock held. + */ +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + wlan_adapter *adapter = priv->adapter; + + if (!ptempcmd) + goto done; + + cleanup_cmdnode(ptempcmd); + list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq); +done: + return; +} + +void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + +int libertas_set_radio_control(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n", + priv->adapter->radioon, priv->adapter->preamble); + + LEAVE(); + return ret; +} + +int libertas_set_mac_packet_filter(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n", + priv->adapter->currentpacketfilter); + + /* Send MAC control command to station */ + ret = libertas_prepare_and_send_command(priv, + cmd_mac_control, 0, 0, 0, NULL); + + LEAVE(); + return ret; +} + +/** + * @brief This function prepare the command before send to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd_no command number + * @param cmd_action command action: GET or SET + * @param wait_option wait option: wait response or not + * @param cmd_oid cmd oid: treated as sub command + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode; + struct cmd_ds_command *cmdptr; + unsigned long flags; + + ENTER(); + + if (!adapter) { + lbs_pr_debug(1, "PREP_CMD: adapter is Null\n"); + ret = -1; + goto done; + } + + if (adapter->surpriseremoved) { + lbs_pr_debug(1, "PREP_CMD: Card is Removed\n"); + ret = -1; + goto done; + } + + cmdnode = libertas_get_free_cmd_ctrl_node(priv); + + if (cmdnode == NULL) { + lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n"); + + /* Wake up main thread to execute next command */ + wake_up_interruptible(&priv->mainthread.waitq); + ret = -1; + goto done; + } + + libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf); + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr =0x%x, command=0x%X\n", + (u32) cmdptr, cmd_no); + + if (!cmdptr) { + lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + /* Set sequence number, command and INT option */ + adapter->seqnum++; + cmdptr->seqnum = cpu_to_le16(adapter->seqnum); + + cmdptr->command = cmd_no; + cmdptr->result = 0; + + switch (cmd_no) { + case cmd_get_hw_spec: + ret = wlan_cmd_hw_spec(priv, cmdptr); + break; + case cmd_802_11_ps_mode: + ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_scan: + ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf); + break; + + case cmd_mac_control: + ret = wlan_cmd_mac_control(priv, cmdptr); + break; + + case cmd_802_11_associate: + case cmd_802_11_reassociate: + ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_deauthenticate: + ret = libertas_cmd_80211_deauthenticate(priv, cmdptr); + break; + + case cmd_802_11_set_wep: + ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_ad_hoc_start: + ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf); + break; + case cmd_code_dnld: + break; + + case cmd_802_11_reset: + ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_get_log: + ret = wlan_cmd_802_11_get_log(priv, cmdptr); + break; + + case cmd_802_11_authenticate: + ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_get_stat: + ret = wlan_cmd_802_11_get_stat(priv, cmdptr); + break; + + case cmd_802_11_snmp_mib: + ret = wlan_cmd_802_11_snmp_mib(priv, cmdptr, + cmd_action, cmd_oid, pdata_buf); + break; + + case cmd_mac_reg_access: + case cmd_bbp_reg_access: + case cmd_rf_reg_access: + ret = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_channel: + ret = wlan_cmd_802_11_rf_channel(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_tx_power: + ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_radio_control: + ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_rf_antenna: + ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_data_rate: + ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action); + break; + case cmd_802_11_rate_adapt_rateset: + ret = wlan_cmd_802_11_rate_adapt_rateset(priv, + cmdptr, cmd_action); + break; + + case cmd_mac_multicast_adr: + ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_ad_hoc_join: + ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_rssi: + ret = wlan_cmd_802_11_rssi(priv, cmdptr); + break; + + case cmd_802_11_ad_hoc_stop: + ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr); + break; + + case cmd_802_11_enable_rsn: + ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_key_material: + ret = wlan_cmd_802_11_key_material(priv, cmdptr, + cmd_action, cmd_oid, + pdata_buf); + break; + + case cmd_802_11_pairwise_tsc: + break; + case cmd_802_11_group_tsc: + break; + + case cmd_802_11_mac_address: + ret = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_eeprom_access: + ret = wlan_cmd_802_11_eeprom_access(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_set_afc: + case cmd_802_11_get_afc: + + cmdptr->command = cpu_to_le16(cmd_no); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + + S_DS_GEN); + + memmove(&cmdptr->params.afc, + pdata_buf, sizeof(struct cmd_ds_802_11_afc)); + + ret = 0; + goto done; + + case cmd_802_11d_domain_info: + ret = libertas_cmd_802_11d_domain_info(priv, cmdptr, + cmd_no, cmd_action); + break; + + case cmd_802_11_sleep_params: + ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action); + break; + case cmd_802_11_inactivity_timeout: + ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr, + cmd_action, pdata_buf); + libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 0, pdata_buf); + break; + + case cmd_802_11_tpc_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_tpc_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + + S_DS_GEN); + + memmove(&cmdptr->params.tpccfg, + pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); + + ret = 0; + break; + case cmd_802_11_led_gpio_ctrl: + { + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio*) + cmdptr->params.ledgpio.data; + + memmove(&cmdptr->params.ledgpio, + pdata_buf, + sizeof(struct cmd_ds_802_11_led_ctrl)); + + cmdptr->command = + cpu_to_le16(cmd_802_11_led_gpio_ctrl); + +#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8 + cmdptr->size = + cpu_to_le16(gpio->header.len + S_DS_GEN + + ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN); + gpio->header.len = cpu_to_le16(gpio->header.len); + + ret = 0; + break; + } + case cmd_802_11_pwr_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_pwr_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) + + S_DS_GEN); + memmove(&cmdptr->params.pwrcfg, pdata_buf, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + + ret = 0; + break; + case cmd_bt_access: + ret = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_fwt_access: + ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_mesh_access: + ret = wlan_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_get_tsf: + cmdptr->command = cpu_to_le16(cmd_get_tsf); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_get_tsf) + + S_DS_GEN); + ret = 0; + break; + case cmd_802_11_tx_rate_query: + cmdptr->command = + cpu_to_le16(cmd_802_11_tx_rate_query); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_tx_rate_query) + + S_DS_GEN); + adapter->txrate = 0; + ret = 0; + break; + default: + lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no); + ret = -1; + break; + } + + /* return error, since the command preparation failed */ + if (ret != 0) { + lbs_pr_debug(1, "PREP_CMD: command preparation failed\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + cmdnode->cmdwaitqwoken = 0; + + libertas_queue_cmd(adapter, cmdnode, 1); + adapter->nr_cmd_pending++; + wake_up_interruptible(&priv->mainthread.waitq); + + if (wait_option & cmd_option_waitforrsp) { + lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n"); + might_sleep(); + wait_event_interruptible(cmdnode->cmdwait_q, + cmdnode->cmdwaitqwoken); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd_retcode) { + lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n", + adapter->cur_cmd_retcode); + adapter->cur_cmd_retcode = 0; + ret = -1; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function allocates the command buffer and link + * it to command free queue. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_allocate_cmd_buffer(wlan_private * priv) +{ + int ret = 0; + u32 ulbufsize; + u32 i; + struct cmd_ctrl_node *tempcmd_array; + u8 *ptempvirtualaddr; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate and initialize cmdCtrlNode */ + ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; + + if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n"); + ret = -1; + goto done; + } + + adapter->cmd_array = tempcmd_array; + memset(adapter->cmd_array, 0, ulbufsize); + + /* Allocate and initialize command buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n"); + ret = -1; + goto done; + } + + memset(ptempvirtualaddr, 0, ulbufsize); + + /* Update command buffer virtual */ + tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr; + } + + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + init_waitqueue_head(&tempcmd_array[i].cmdwait_q); + libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]); + } + + ret = 0; + done: + LEAVE(); + return ret; +} + +/** + * @brief This function frees the command buffer. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_free_cmd_buffer(wlan_private * priv) +{ + u32 ulbufsize; + unsigned int i; + struct cmd_ctrl_node *tempcmd_array; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* need to check if cmd array is allocated or not */ + if (adapter->cmd_array == NULL) { + lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n"); + goto done; + } + + tempcmd_array = adapter->cmd_array; + + /* Release shared memory buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (tempcmd_array[i].bufvirtualaddr) { + lbs_pr_debug(1, "Free all the array\n"); + kfree(tempcmd_array[i].bufvirtualaddr); + tempcmd_array[i].bufvirtualaddr = NULL; + } + } + + /* Release cmd_ctrl_node */ + if (adapter->cmd_array) { + lbs_pr_debug(1, "Free cmd_array\n"); + kfree(adapter->cmd_array); + adapter->cmd_array = NULL; + } + +done: + LEAVE(); + return 0; +} + +/** + * @brief This function gets a free command node if available in + * command free queue. + * + * @param priv A pointer to wlan_private structure + * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL + */ +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv) +{ + struct cmd_ctrl_node *tempnode; + wlan_adapter *adapter = priv->adapter; + unsigned long flags; + + if (!adapter) + return NULL; + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!list_empty(&adapter->cmdfreeq)) { + tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next; + list_del((struct list_head *)tempnode); + } else { + lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); + tempnode = NULL; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (tempnode) { + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n"); + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n", + tempnode); + cleanup_cmdnode(tempnode); + } + + return tempnode; +} + +/** + * @brief This function cleans command node. + * + * @param ptempnode A pointer to cmdCtrlNode structure + * @return n/a + */ +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode) +{ + if (!ptempnode) + return; + ptempnode->cmdwaitqwoken = 1; + wake_up_interruptible(&ptempnode->cmdwait_q); + ptempnode->status = 0; + ptempnode->cmd_oid = (u32) 0; + ptempnode->wait_option = 0; + ptempnode->pdata_buf = NULL; + + if (ptempnode->bufvirtualaddr != NULL) + memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + return; +} + +/** + * @brief This function initializes the command node. + * + * @param priv A pointer to wlan_private structure + * @param ptempnode A pointer to cmd_ctrl_node structure + * @param cmd_oid cmd oid: treated as sub command + * @param wait_option wait option: wait response or not + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf) +{ + ENTER(); + + if (!ptempnode) + return; + + ptempnode->cmd_oid = cmd_oid; + ptempnode->wait_option = wait_option; + ptempnode->pdata_buf = pdata_buf; + + LEAVE(); +} + +/** + * @brief This function executes next command in command + * pending queue. It will put fimware back to PS mode + * if applicable. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_execute_next_command(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_ds_command *cmdptr; + unsigned long flags; + int ret = 0; + + lbs_pr_debug(1, "libertas_execute_next_command\n"); + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (adapter->cur_cmd) { + lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n"); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + if (!list_empty(&adapter->cmdpendingq)) { + cmdnode = (struct cmd_ctrl_node *) + adapter->cmdpendingq.next; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (cmdnode) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Got next command from cmdpendingq\n"); + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + if (is_command_allowed_in_ps(cmdptr->command)) { + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + ret = -1; + goto done; + } + lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command " + "0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + } else if (adapter->psstate != PS_STATE_FULL_POWER) { + /* + * 1. Non-PS command: + * Queue it. set needtowakeup to TRUE if current state + * is SLEEP, otherwise call libertas_ps_wakeup to send Exit_PS. + * 2. PS command but not Exit_PS: + * Ignore it. + * 3. PS command Exit_PS: + * Set needtowakeup to TRUE if current state is SLEEP, + * otherwise send this command down to firmware + * immediately. + */ + if (cmdptr->command != + cpu_to_le16(cmd_802_11_ps_mode)) { + /* Prepare to send Exit PS, + * this non PS command will be sent later */ + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + /* w/ new scheme, it will not reach here. + since it is blocked in main_thread. */ + adapter->needtowakeup = 1; + } else + libertas_ps_wakeup(priv, 0); + + ret = 0; + goto done; + } else { + /* + * PS command. Ignore it if it is not Exit_PS. + * otherwise send it down immediately. + */ + struct cmd_ds_802_11_ps_mode *psm = + &cmdptr->params.psmode; + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: PS cmd- action=0x%x\n", + psm->action); + if (psm->action != + cpu_to_le16(cmd_subcmd_exit_ps)) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore Enter PS cmd\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + + ret = 0; + goto done; + } + + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + adapter->needtowakeup = 1; + + ret = 0; + goto done; + } + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Sending Exit_PS down...\n"); + } + } + list_del((struct list_head *)cmdnode); + lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n", + cmdptr->command); + DownloadcommandToStation(priv, cmdnode); + } else { + /* + * check if in power save mode, if yes, put the device back + * to PS mode + */ + if ((adapter->psmode != wlan802_11powermodecam) && + (adapter->psstate == PS_STATE_FULL_POWER) && + (adapter->connect_status == libertas_connected)) { + if (adapter->secinfo.WPAenabled + || adapter->secinfo.WPA2enabled) { + /* check for valid WPA group keys */ + if (adapter->wpa_mcast_key.len + || adapter->wpa_unicast_key.len) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: WPA enabled and GTK_SET" + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } else { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: command PendQ is empty," + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } + } + + ret = 0; +done: + return ret; +} + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str) +{ + union iwreq_data iwrq; + u8 buf[50]; + + ENTER(); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; + + /* Send Event to upper layer */ + lbs_pr_debug(1, "Event Indication string = %s\n", + (char *)buf); + lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length); + + lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str); + wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf); + + LEAVE(); + return; +} + +static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size) +{ + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n", + size); + + lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->intcounter || adapter->currenttxskb) + lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n", + adapter->intcounter, adapter->currenttxskb); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (ret) { + lbs_pr_alert( + "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n"); + } else { + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!adapter->intcounter) { + adapter->psstate = PS_STATE_SLEEP; + } else { + lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n", + adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n"); + lbs_pr_debug(1, "+"); + } + + LEAVE(); + return ret; +} + +void libertas_ps_sleep(wlan_private * priv, int wait_option) +{ + + ENTER(); + + /* + * PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_enter_ps, wait_option, 0, NULL); + + LEAVE(); + return; +} + +/** + * @brief This function sends Eixt_PS command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param wait_option wait response or not + * @return n/a + */ +void libertas_ps_wakeup(wlan_private * priv, int wait_option) +{ + enum WLAN_802_11_POWER_MODE Localpsmode; + + ENTER(); + + Localpsmode = wlan802_11powermodecam; + + lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode); + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_exit_ps, + wait_option, 0, &Localpsmode); + + LEAVE(); + return; +} + +/** + * @brief This function checks condition and prepares to + * send sleep confirm command to firmware if ok. + * + * @param priv A pointer to wlan_private structure + * @param psmode Power Saving mode + * @return n/a + */ +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode) +{ + unsigned long flags =0; + wlan_adapter *adapter = priv->adapter; + u8 allowed = 1; + + ENTER(); + + if (priv->wlan_dev.dnld_sent) { + allowed = 0; + lbs_pr_debug(1, "D"); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + allowed = 0; + lbs_pr_debug(1, "C"); + } + if (adapter->intcounter > 0) { + allowed = 0; + lbs_pr_debug(1, "I%d", adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (allowed) { + lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n"); + sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep, + sizeof(struct PS_CMD_ConfirmSleep)); + } else { + lbs_pr_debug(1, "Sleep Confirm has been delayed\n"); + } + + LEAVE(); +} diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c new file mode 100644 index 0000000..cdb012c --- /dev/null +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -0,0 +1,1031 @@ +/** + * This file contains the handling of command + * responses as well as events generated by firmware. + */ +#include +#include +#include + +#include + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +/** + * @brief This function handles disconnect event. it + * reports disconnect to upper layer, clean tx/rx packets, + * reset link state etc. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +void libertas_mac_event_disconnected(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + if (adapter->connect_status != libertas_connected) + return; + + lbs_pr_debug(1, "Handles disconnect event.\n"); + + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* + * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. + * It causes problem in the Supplicant + */ + + msleep_interruptible(1000); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + /* Free Tx and Rx packets */ + kfree_skb(priv->adapter->currenttxskb); + priv->adapter->currenttxskb = NULL; + + /* report disconnect to upper layer */ + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + /* reset SNR/NF/RSSI values */ + memset(adapter->SNR, 0x00, sizeof(adapter->SNR)); + memset(adapter->NF, 0x00, sizeof(adapter->NF)); + memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI)); + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + adapter->rxpd_rate = 0; + lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n", + adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n", + adapter->previousssid.ssid, adapter->previousssid.ssidlength); + + /* reset internal flags */ + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + adapter->wpa_ie_len = 0; + adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; + adapter->secinfo.Encryptionmode = CIPHER_NONE; + + adapter->connect_status = libertas_disconnected; + + /* + * memorize the previous SSID and BSSID + * it could be used for re-assoc + */ + memcpy(&adapter->previousssid, + &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID)); + memcpy(adapter->previousbssid, + adapter->curbssparams.bssid, ETH_ALEN); + + /* need to erase the current SSID and BSSID info */ + adapter->pattemptedbssdesc = NULL; + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + if (adapter->psstate != PS_STATE_FULL_POWER) { + /* make firmware to exit PS mode */ + lbs_pr_debug(1, "Disconnected, so exit PS mode.\n"); + libertas_ps_wakeup(priv, 0); + } +} + +/** + * @brief This function handles MIC failure event. + * + * @param priv A pointer to wlan_private structure + * @para event the event id + * @return n/a + */ +static void handle_mic_failureevent(wlan_private * priv, u32 event) +{ + char buf[50]; + + memset(buf, 0, sizeof(buf)); + + sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); + + if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { + strcat(buf, "unicast "); + } else { + strcat(buf, "multicast "); + } + + libertas_send_iwevcustom_event(priv, buf); +} + +static int wlan_ret_reg_access(wlan_private * priv, + u16 type, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (type) { + case cmd_ret_mac_reg_access: + { + struct cmd_ds_mac_reg_access *reg; + + reg = + (struct cmd_ds_mac_reg_access *)&resp->params. + macreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *reg; + reg = + (struct cmd_ds_bbp_reg_access *)&resp->params. + bbpreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_rf_reg_access: + { + struct cmd_ds_rf_reg_access *reg; + reg = + (struct cmd_ds_rf_reg_access *)&resp->params. + rfreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + default: + LEAVE(); + return -1; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_get_hw_spec(wlan_private * priv, + struct cmd_ds_command *resp) +{ + u32 i; + struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo); + + adapter->fwreleasenumber = hwspec->fwreleasenumber; + + lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n", + adapter->fwreleasenumber); + lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n", + hwspec->permanentaddr[0], hwspec->permanentaddr[1], + hwspec->permanentaddr[2], hwspec->permanentaddr[3], + hwspec->permanentaddr[4], hwspec->permanentaddr[5]); + lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X version=0x%X\n", + hwspec->hwifversion, hwspec->version); + + adapter->regioncode = le16_to_cpu(hwspec->regioncode); + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (adapter->regioncode == libertas_region_code_to_index[i]) { + adapter->regiontableindex = (u16) i; + break; + } + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + adapter->regioncode = 0x10; + adapter->regiontableindex = 0; + lbs_pr_info( + "unidentified region code, use the default (USA)\n"); + } + + if (adapter->current_addr[0] == 0xff) { + memmove(adapter->current_addr, hwspec->permanentaddr, + ETH_ALEN); + } + + memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + + if (libertas_set_regiontable(priv, adapter->regioncode, 0)) { + ret = -1; + goto done; + } + + if (libertas_set_universaltable(priv, 0)) { + ret = -1; + goto done; + } + + done: + LEAVE(); + return ret; +} + +static int wlan_ret_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n" + " extsleepclk=%x\n", sp->error, sp->offset, + sp->stabletime, sp->calcontrol, sp->externalsleepclk); + adapter->sp.sp_error = le16_to_cpu(sp->error); + adapter->sp.sp_offset = le16_to_cpu(sp->offset); + adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime); + adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol); + adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk); + adapter->sp.sp_reserved = le16_to_cpu(sp->reserved); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_stat(wlan_private * priv, + struct cmd_ds_command *resp) +{ +/* currently adapter->wlan802_11Stat is unused + + struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat; + wlan_adapter *adapter = priv->adapter; + + // TODO Convert it to Big endian befor copy + memcpy(&adapter->wlan802_11Stat, + p11Stat, sizeof(struct cmd_ds_802_11_get_stat)); +*/ + return 0; +} + +static int wlan_ret_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 querytype = le16_to_cpu(smib->querytype); + + ENTER(); + + lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid, + querytype); + lbs_pr_debug(1, "SNMP_RESP: Buf size = %x\n", + le16_to_cpu(smib->bufsize)); + + if (querytype == cmd_act_get) { + switch (oid) { + case fragthresh_i: + priv->adapter->fragthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n", + priv->adapter->fragthsd); + break; + case rtsthresh_i: + priv->adapter->rtsthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n", + priv->adapter->rtsthsd); + break; + case short_retrylim_i: + priv->adapter->txretrycount = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n", + priv->adapter->rtsthsd); + break; + default: + break; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_key_material *pkeymaterial = + &resp->params.keymaterial; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pkeymaterial->action); + + ENTER(); + + /* Copy the returned key to driver private data */ + if (action == cmd_act_get) { + u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet; + u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size)); + + while (buf_ptr < resp_end) { + struct MrvlIEtype_keyParamSet * pkeyparamset = + (struct MrvlIEtype_keyParamSet *) buf_ptr; + struct WLAN_802_11_KEY * pkey; + u16 key_info = le16_to_cpu(pkeyparamset->keyinfo); + u16 param_set_len = le16_to_cpu(pkeyparamset->length); + u8 * end; + u16 key_len = le16_to_cpu(pkeyparamset->keylen); + + end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type) + + sizeof (pkeyparamset->length) + + param_set_len; + /* Make sure we don't access past the end of the IEs */ + if (end > resp_end) + break; + + if (key_info & KEY_INFO_WPA_UNICAST) + pkey = &adapter->wpa_unicast_key; + else if (key_info & KEY_INFO_WPA_MCAST) + pkey = &adapter->wpa_mcast_key; + else + break; + + /* Copy returned key into driver */ + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + if (key_len > sizeof(pkey->key)) + break; + pkey->type = le16_to_cpu(pkeyparamset->keytypeid); + pkey->flags = le16_to_cpu(pkeyparamset->keyinfo); + pkey->len = le16_to_cpu(pkeyparamset->keylen); + memcpy(pkey->key, pkeyparamset->key, pkey->len); + + buf_ptr = end + 1; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel); + + lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pAntenna->action); + + if (action == cmd_act_get_rx) + adapter->rxantennamode = + le16_to_cpu(pAntenna->antennamode); + + if (action == cmd_act_get_tx) + adapter->txantennamode = + le16_to_cpu(pAntenna->antennamode); + + lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n", + action, le16_to_cpu(pAntenna->antennamode)); + + return 0; +} + +static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rate_adapt_rateset *rates = + &resp->params.rateset; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (rates->action == cmd_act_get) { + adapter->enablehwauto = rates->enablehwauto; + adapter->ratebitmap = rates->bitmap; + } + + LEAVE(); + + return 0; +} + +static int wlan_ret_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate; + wlan_adapter *adapter = priv->adapter; + u8 dot11datarate; + + ENTER(); + + lbs_dbg_hex("DATA_RATE_RESP: data_rate- ", + (u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate)); + + dot11datarate = pdatarate->datarate[0]; + if (pdatarate->action == cmd_act_get_tx_rate) { + memcpy(adapter->libertas_supported_rates, pdatarate->datarate, + sizeof(adapter->libertas_supported_rates)); + } + adapter->datarate = libertas_index_to_data_rate(dot11datarate); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_channel *rfchannel = + &resp->params.rfchannel; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(rfchannel->action); + u16 newchannel = le16_to_cpu(rfchannel->currentchannel); + + ENTER(); + + if (action == cmd_opt_802_11_rf_channel_get + && adapter->curbssparams.channel != newchannel) { + lbs_pr_debug(1, "channel Switch: %d to %d\n", + adapter->curbssparams.channel, newchannel); + + /* Update the channel again */ + adapter->curbssparams.channel = newchannel; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; + wlan_adapter *adapter = priv->adapter; + + /* store the non average value */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR); + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = + le16_to_cpu(rssirsp->noisefloor); + + adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR); + adapter->NF[TYPE_BEACON][TYPE_AVG] = + le16_to_cpu(rssirsp->avgnoisefloor); + + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); + + lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n", + adapter->RSSI[TYPE_BEACON][TYPE_AVG]); + + return 0; +} + +static int wlan_ret_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr *pbuf; + pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom; + + lbs_pr_debug(1, "eeprom read len=%x\n", + le16_to_cpu(resp->params.rdeeprom.bytecount)); + if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) { + pbuf->NOB = 0; + lbs_pr_debug(1, "eeprom read return length is too big\n"); + return -1; + } + pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount); + if (pbuf->NOB > 0) { + + memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + lbs_dbg_hex("adapter", (char *)&pbuf->value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + } + return 0; +} + +static int wlan_ret_get_log(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_get_log *logmessage = + (struct cmd_ds_802_11_get_log *)&resp->params.glog; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* TODO Convert it to Big Endian before copy */ + memcpy(&adapter->logmsg, logmessage, + sizeof(struct cmd_ds_802_11_get_log)); + + LEAVE(); + return 0; +} + +static inline int handle_cmd_response(u16 respcmd, + struct cmd_ds_command *resp, + wlan_private *priv) +{ + int ret = 0; + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + + switch (respcmd) { + case cmd_ret_mac_reg_access: + case cmd_ret_bbp_reg_access: + case cmd_ret_rf_reg_access: + ret = wlan_ret_reg_access(priv, respcmd, resp); + break; + + case cmd_ret_hw_spec_info: + ret = wlan_ret_get_hw_spec(priv, resp); + break; + + case cmd_ret_802_11_scan: + ret = libertas_ret_80211_scan(priv, resp); + break; + + case cmd_ret_802_11_get_log: + ret = wlan_ret_get_log(priv, resp); + break; + + case cmd_ret_802_11_associate: + case cmd_ret_802_11_reassociate: + ret = libertas_ret_80211_associate(priv, resp); + break; + + case cmd_ret_802_11_disassociate: + case cmd_ret_802_11_deauthenticate: + ret = libertas_ret_80211_disassociate(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_start: + case cmd_ret_802_11_ad_hoc_join: + ret = libertas_ret_80211_ad_hoc_start(priv, resp); + break; + + case cmd_ret_802_11_stat: + ret = wlan_ret_802_11_stat(priv, resp); + break; + + case cmd_ret_802_11_snmp_mib: + ret = wlan_ret_802_11_snmp_mib(priv, resp); + break; + + case cmd_ret_802_11_rf_tx_power: + ret = wlan_ret_802_11_rf_tx_power(priv, resp); + break; + + case cmd_ret_802_11_set_afc: + case cmd_ret_802_11_get_afc: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.afc, + sizeof(struct cmd_ds_802_11_afc)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + case cmd_ret_802_11_rf_antenna: + ret = wlan_ret_802_11_rf_antenna(priv, resp); + break; + + case cmd_ret_mac_multicast_adr: + case cmd_ret_mac_control: + case cmd_ret_802_11_set_wep: + case cmd_ret_802_11_reset: + case cmd_ret_802_11_authenticate: + case cmd_ret_802_11_radio_control: + case cmd_ret_802_11_beacon_stop: + case cmd_ret_802_11_enable_rsn: + break; + + case cmd_ret_802_11_data_rate: + ret = wlan_ret_802_11_data_rate(priv, resp); + break; + case cmd_ret_802_11_rate_adapt_rateset: + ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp); + break; + case cmd_ret_802_11_rf_channel: + ret = wlan_ret_802_11_rf_channel(priv, resp); + break; + + case cmd_ret_802_11_rssi: + ret = wlan_ret_802_11_rssi(priv, resp); + break; + + case cmd_ret_802_11_mac_address: + ret = wlan_ret_802_11_mac_address(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_stop: + ret = libertas_ret_80211_ad_hoc_stop(priv, resp); + break; + + case cmd_ret_802_11_key_material: + lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n"); + ret = wlan_ret_802_11_key_material(priv, resp); + break; + + case cmd_ret_802_11_eeprom_access: + ret = wlan_ret_802_11_eeprom_access(priv, resp); + break; + + case cmd_ret_802_11d_domain_info: + ret = libertas_ret_802_11d_domain_info(priv, resp); + break; + + case cmd_ret_802_11_sleep_params: + ret = wlan_ret_802_11_sleep_params(priv, resp); + break; + case cmd_ret_802_11_inactivity_timeout: + spin_lock_irqsave(&adapter->driver_lock, flags); + *((u16 *) adapter->cur_cmd->pdata_buf) = + le16_to_cpu(resp->params.inactivity_timeout.timeout); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + + case cmd_ret_802_11_tpc_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.tpccfg, + sizeof(struct cmd_ds_802_11_tpc_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_led_gpio_ctrl: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.ledgpio, + sizeof(struct cmd_ds_802_11_led_ctrl)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_pwr_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.pwrcfg, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + + case cmd_ret_get_tsf: + spin_lock_irqsave(&adapter->driver_lock, flags); + memcpy(priv->adapter->cur_cmd->pdata_buf, + &resp->params.gettsf.tsfvalue, sizeof(u64)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_bt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.bt.addr1, 2 * ETH_ALEN); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_fwt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.fwt, + sizeof(resp->params.fwt)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_mesh_access: + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.mesh, + sizeof(resp->params.mesh)); + break; + case cmd_rte_802_11_tx_rate_query: + priv->adapter->txrate = resp->params.txrate.txrate; + break; + default: + lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + return ret; +} + +int libertas_process_rx_command(wlan_private * priv) +{ + u16 respcmd; + struct cmd_ds_command *resp; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + ulong flags; + u16 result; + + ENTER(); + + lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies); + + /* Now we got response from FW, cancel the command timer */ + del_timer(&adapter->command_timer); + + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!adapter->cur_cmd) { + lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd); + ret = -1; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + goto done; + } + resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr); + + lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr, + priv->wlan_dev.upld_len); + + respcmd = le16_to_cpu(resp->command); + + result = le16_to_cpu(resp->result); + + lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd, + result, priv->wlan_dev.upld_len); + + if (!(respcmd & 0x8000)) { + lbs_pr_debug(1, "Invalid response to command!"); + adapter->cur_cmd_retcode = -1; + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + /* Store the response code to cur_cmd_retcode. */ + adapter->cur_cmd_retcode = le16_to_cpu(resp->result); + + if (respcmd == cmd_ret_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psmode; + + psmode = &resp->params.psmode; + lbs_pr_debug(1, + "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n", + resp->result, psmode->action); + psmode->action = cpu_to_le16(psmode->action); + + if (result) { + lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n", + resp->result); + if (adapter->inframode == wlan802_11ibss) { + /* + * We should not re-try enter-ps command in + * ad-hoc mode. It takes place in + * libertas_execute_next_command(). + */ + if (psmode->action == cmd_subcmd_enter_ps) + adapter->psmode = + wlan802_11powermodecam; + } + } else if (psmode->action == cmd_subcmd_enter_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_AWAKE; + + lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n"); + if (adapter->connect_status != libertas_connected) { + /* + * When Deauth Event received before Enter_PS command + * response, We need to wake up the firmware. + */ + lbs_pr_debug(1, + "Disconnected, Going to invoke libertas_ps_wakeup\n"); + + mutex_unlock(&adapter->lock); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + libertas_ps_wakeup(priv, 0); + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + } + } else if (psmode->action == cmd_subcmd_exit_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_FULL_POWER; + lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n"); + } else { + lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n", + psmode->action); + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = 0; + goto done; + } + + if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) { + /* Copy the response back to response buffer */ + memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size); + + adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD; + } + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n", + resp->command, resp->result); + /* + * Handling errors here + */ + switch (respcmd) { + case cmd_ret_hw_spec_info: + case cmd_ret_802_11_reset: + lbs_pr_debug(1, "CMD_RESP: Reset command failed\n"); + break; + + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = handle_cmd_response(respcmd, resp, priv); + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + WARN_ON(adapter->nr_cmd_pending > 128); + adapter->cur_cmd = NULL; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + mutex_unlock(&adapter->lock); + LEAVE(); + return ret; +} + +int libertas_process_event(wlan_private * priv) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + u32 eventcause; + + spin_lock_irq(&adapter->driver_lock); + eventcause = adapter->eventcause; + spin_unlock_irq(&adapter->driver_lock); + + ENTER(); + + lbs_pr_debug(1, "EVENT Cause %x\n", eventcause); + + switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) { + case MACREG_INT_CODE_LINK_SENSED: + lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n"); + break; + + case MACREG_INT_CODE_DEAUTHENTICATED: + lbs_pr_debug(1, "EVENT: Deauthenticated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_DISASSOCIATED: + lbs_pr_debug(1, "EVENT: Disassociated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_LINK_LOSE_NO_SCAN: + lbs_pr_debug(1, "EVENT: Link lost\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_PS_SLEEP: + lbs_pr_debug(1, "EVENT: SLEEP\n"); + lbs_pr_debug(1, "_"); + + /* handle unexpected PS SLEEP event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS SLEEP\n"); + break; + } + adapter->psstate = PS_STATE_PRE_SLEEP; + + libertas_ps_confirm_sleep(priv, (u16) adapter->psmode); + + break; + + case MACREG_INT_CODE_PS_AWAKE: + lbs_pr_debug(1, "EVENT: AWAKE \n"); + lbs_pr_debug(1, "|"); + + /* handle unexpected PS AWAKE event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); + break; + } + + adapter->psstate = PS_STATE_AWAKE; + + if (adapter->needtowakeup) { + /* + * wait for the command processing to finish + * before resuming sending + * adapter->needtowakeup will be set to FALSE + * in libertas_ps_wakeup() + */ + lbs_pr_debug(1, "Waking up...\n"); + libertas_ps_wakeup(priv, 0); + } + break; + + case MACREG_INT_CODE_MIC_ERR_UNICAST: + lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); + break; + + case MACREG_INT_CODE_MIC_ERR_MULTICAST: + lbs_pr_debug(1, "EVENT: MULTICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); + break; + case MACREG_INT_CODE_MIB_CHANGED: + case MACREG_INT_CODE_INIT_DONE: + break; + + case MACREG_INT_CODE_ADHOC_BCN_LOST: + lbs_pr_debug(1, "EVENT: HWAC - ADHOC BCN LOST\n"); + break; + + case MACREG_INT_CODE_RSSI_LOW: + lbs_pr_alert( "EVENT: RSSI_LOW\n"); + break; + case MACREG_INT_CODE_SNR_LOW: + lbs_pr_alert( "EVENT: SNR_LOW\n"); + break; + case MACREG_INT_CODE_MAX_FAIL: + lbs_pr_alert( "EVENT: MAX_FAIL\n"); + break; + case MACREG_INT_CODE_RSSI_HIGH: + lbs_pr_alert( "EVENT: RSSI_HIGH\n"); + break; + case MACREG_INT_CODE_SNR_HIGH: + lbs_pr_alert( "EVENT: SNR_HIGH\n"); + break; + + default: + lbs_pr_alert( "EVENT: unknown event id: %#x\n", + eventcause >> SBI_EVENT_CAUSE_SHIFT); + break; + } + + spin_lock_irq(&adapter->driver_lock); + adapter->eventcause = 0; + spin_unlock_irq(&adapter->driver_lock); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c new file mode 100644 index 0000000..51dfd20 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.c @@ -0,0 +1,1935 @@ +#include +#include +#include +#include +#include +#include +#include "dev.h" +#include "decl.h" +#include "host.h" + +static struct dentry *libertas_dir = NULL; +static char *szStates[] = { + "Connected", + "Disconnected" +}; + +void libertas_debug_init(wlan_private * priv, struct net_device *dev); + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static const size_t len = PAGE_SIZE; + +static ssize_t libertas_dev_info(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + ssize_t res; + + pos += snprintf(buf+pos, len-pos, "state = %s\n", + szStates[priv->adapter->connect_status]); + pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", + (u32) priv->adapter->regioncode); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + + +static ssize_t libertas_getscantable(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + int numscansdone = 0, res; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + pos += snprintf(buf+pos, len-pos, + "# | ch | ss | bssid | cap | TSF | Qual | SSID \n"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + + while (numscansdone < priv->adapter->numinscantable) { + struct bss_descriptor *pbssinfo; + u16 cap; + + pbssinfo = &priv->adapter->scantable[numscansdone]; + memcpy(&cap, &pbssinfo->cap, sizeof(cap)); + pos += snprintf(buf+pos, len-pos, + "%02u| %03d | %03ld | %02x:%02x:%02x:%02x:%02x:%02x |", + numscansdone, pbssinfo->channel, pbssinfo->rssi, + pbssinfo->macaddress[0], pbssinfo->macaddress[1], + pbssinfo->macaddress[2], pbssinfo->macaddress[3], + pbssinfo->macaddress[4], pbssinfo->macaddress[5]); + pos += snprintf(buf+pos, len-pos, " %04x-", cap); + pos += snprintf(buf+pos, len-pos, "%c%c%c |", + pbssinfo->cap.ibss ? 'A' : 'I', + pbssinfo->cap.privacy ? 'P' : ' ', + pbssinfo->cap.spectrummgmt ? 'S' : ' '); + pos += snprintf(buf+pos, len-pos, " %08llx |", pbssinfo->networktsf); + pos += snprintf(buf+pos, len-pos, " %d |", + SCAN_RSSI(priv->adapter->scantable[numscansdone].rssi)); + + pos += snprintf(buf+pos, len-pos, " %s\n", pbssinfo->ssid.ssid); + + numscansdone++; + } + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t buf_size, res; + int p1, p2, p3, p4, p5, p6; + struct sleep_params sp; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); + if (res != 6) { + res = -EFAULT; + goto out_unlock; + } + sp.sp_error = p1; + sp.sp_offset = p2; + sp.sp_stabletime = p3; + sp.sp_calcontrol = p4; + sp.sp_extsleepclk = p5; + sp.sp_reserved = p6; + + memcpy(&priv->adapter->sp, &sp, sizeof(struct sleep_params)); + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (!res) + res = count; + else + res = -EINVAL; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_get, + cmd_option_waitforrsp, 0, NULL); + if (res) { + res = -EFAULT; + goto out_unlock; + } + + pos += snprintf(buf, len, "%d %d %d %d %d %d\n", adapter->sp.sp_error, + adapter->sp.sp_offset, adapter->sp.sp_stabletime, + adapter->sp.sp_calcontrol, adapter->sp.sp_extsleepclk, + adapter->sp.sp_reserved); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_extscan(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct WLAN_802_11_SSID extscan_ssid; + union iwreq_data wrqu; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + memcpy(&extscan_ssid.ssid, buf, strlen(buf)-1); + extscan_ssid.ssidlength = strlen(buf)-1; + + libertas_send_specific_SSID_scan(priv, &extscan_ssid, 1); + + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + return count; +} + +static int libertas_parse_chan(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg, int dur) +{ + char *start, *end, *hold, *str; + int i = 0; + + start = strstr(buf, "chan="); + if (!start) + return -EINVAL; + start += 5; + end = strstr(start, " "); + if (!end) + end = buf + count; + hold = kzalloc((end - start)+1, GFP_KERNEL); + if (!hold) + return -ENOMEM; + strncpy(hold, start, end - start); + hold[(end-start)+1] = '\0'; + while(hold && (str = strsep(&hold, ","))) { + int chan; + char band, passive = 0; + sscanf(str, "%d%c%c", &chan, &band, &passive); + scan_cfg->chanlist[i].channumber = chan; + scan_cfg->chanlist[i].scantype = passive ? 1 : 0; + if (band == 'b' || band == 'g') + scan_cfg->chanlist[i].radiotype = 0; + else if (band == 'a') + scan_cfg->chanlist[i].radiotype = 1; + + scan_cfg->chanlist[i].scantime = dur; + i++; + } + + kfree(hold); + return i; +} + +static void libertas_parse_bssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + unsigned int mac[ETH_ALEN]; + int i; + + hold = strstr(buf, "bssid="); + if (!hold) + return; + hold += 6; + sscanf(hold, "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3, + mac+4, mac+5); + for(i=0;ispecificBSSID[i] = mac[i]; +} + +static void libertas_parse_ssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold, *end; + ssize_t size; + + hold = strstr(buf, "ssid="); + if (!hold) + return; + hold += 5; + end = strstr(hold, " "); + if (!end) + end = buf + count - 1; + + size = min(IW_ESSID_MAX_SIZE, end - hold); + strncpy(scan_cfg->specificSSID, hold, size); + + return; +} + +static void libertas_parse_keep(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "keep="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + if (val != 0) + val = 1; + + scan_cfg->keeppreviousscan = val; + return; +} + +static int libertas_parse_dur(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "dur="); + if (!hold) + return 0; + hold += 4; + sscanf(hold, "%d", &val); + + return val; +} + +static void libertas_parse_probes(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "probes="); + if (!hold) + return; + hold += 7; + sscanf(hold, "%d", &val); + + scan_cfg->numprobes = val; + + return; +} + +static void libertas_parse_type(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "type="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + /* type=1,2 or 3 */ + if (val < 1 || val > 3) + return; + + scan_cfg->bsstype = val; + + return; +} + +static ssize_t libertas_setuserscan(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct wlan_ioctl_user_scan_cfg *scan_cfg; + union iwreq_data wrqu; + int dur; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + scan_cfg = kzalloc(sizeof(struct wlan_ioctl_user_scan_cfg), GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + scan_cfg->bsstype = WLAN_SCAN_BSS_TYPE_ANY; + + dur = libertas_parse_dur(buf, count, scan_cfg); + libertas_parse_chan(buf, count, scan_cfg, dur); + libertas_parse_bssid(buf, count, scan_cfg); + libertas_parse_ssid(buf, count, scan_cfg); + libertas_parse_keep(buf, count, scan_cfg); + libertas_parse_probes(buf, count, scan_cfg); + libertas_parse_type(buf, count, scan_cfg); + + wlan_scan_networks(priv, scan_cfg); + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + memset(&wrqu, 0x00, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + kfree(scan_cfg); + return count; +} + +static int libertas_event_initcmd(wlan_private *priv, void **response_buf, + struct cmd_ctrl_node **cmdnode, + struct cmd_ds_command **cmd) +{ + u16 wait_option = cmd_option_waitforrsp; + + if (!(*cmdnode = libertas_get_free_cmd_ctrl_node(priv))) { + lbs_pr_debug(1, "failed libertas_get_free_cmd_ctrl_node\n"); + return -ENOMEM; + } + if (!(*response_buf = kmalloc(3000, GFP_KERNEL))) { + lbs_pr_debug(1, "failed to allocate response buffer!\n"); + return -ENOMEM; + } + libertas_set_cmd_ctrl_node(priv, *cmdnode, 0, wait_option, NULL); + init_waitqueue_head(&(*cmdnode)->cmdwait_q); + (*cmdnode)->pdata_buf = *response_buf; + (*cmdnode)->cmdflags |= CMD_F_HOSTCMD; + (*cmdnode)->cmdwaitqwoken = 0; + *cmd = (struct cmd_ds_command *)(*cmdnode)->bufvirtualaddr; + (*cmd)->command = cmd_802_11_subscribe_event; + (*cmd)->seqnum = ++priv->adapter->seqnum; + (*cmd)->result = 0; + return 0; +} + +static ssize_t libertas_lowrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Lowrssi; + case TLV_TYPE_RSSI_LOW: + Lowrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Lowrssi->rssivalue, + Lowrssi->rssifreq, + (event->events & 0x0001)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static u16 libertas_get_events_bitmap(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res; + u16 event_bitmap; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + return res; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + event_bitmap = event->events; + kfree(response_buf); + return event_bitmap; +} + +static ssize_t libertas_lowrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(0x0104); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0001 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *LowSnr; + case TLV_TYPE_SNR_LOW: + LowSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + LowSnr->snrvalue, + LowSnr->snrfreq, + (event->events & 0x0002)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0002 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_failurecount *failcount; + case TLV_TYPE_FAILCOUNT: + failcount = (struct mrvlietypes_failurecount *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + failcount->failvalue, + failcount->Failfreq, + (event->events & 0x0004)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_failurecount); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_failurecount *failcount; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_failurecount)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + failcount = (struct mrvlietypes_failurecount *)(ptr); + failcount->header.type = cpu_to_le16(TLV_TYPE_FAILCOUNT); + failcount->header.len = 2; + failcount->failvalue = cpu_to_le16(value); + failcount->Failfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0004 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = (struct cmd_ds_command *)response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + free_page(addr); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_beaconsmissed *bcnmiss; + case TLV_TYPE_BCNMISS: + bcnmiss = (struct mrvlietypes_beaconsmissed *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d N/A %d\n", + bcnmiss->beaconmissed, + (event->events & 0x0008)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_beaconsmissed); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_beaconsmissed *bcnmiss; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_beaconsmissed)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + bcnmiss = (struct mrvlietypes_beaconsmissed *)(ptr); + bcnmiss->header.type = cpu_to_le16(TLV_TYPE_BCNMISS); + bcnmiss->header.len = 2; + bcnmiss->beaconmissed = cpu_to_le16(value); + event_bitmap |= subscribed ? 0x0008 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Highrssi; + case TLV_TYPE_RSSI_HIGH: + Highrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Highrssi->rssivalue, + Highrssi->rssifreq, + (event->events & 0x0010)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0010 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *HighSnr; + case TLV_TYPE_SNR_HIGH: + HighSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + HighSnr->snrvalue, + HighSnr->snrfreq, + (event->events & 0x0020)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_HIGH); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0020 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdmac_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->mac_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", + priv->mac_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return ret; +} + +static ssize_t libertas_rdmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdbbp_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->bbp_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", + priv->bbp_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdrf_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->rf_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", + priv->rf_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->rf_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +#define FOPS(fread, fwrite) { \ + .owner = THIS_MODULE, \ + .open = open_file_generic, \ + .read = (fread), \ + .write = (fwrite), \ +} + +struct libertas_debugfs_files { + char *name; + int perm; + struct file_operations fops; +}; + +struct libertas_debugfs_files debugfs_files[] = { + { "info", 0444, FOPS(libertas_dev_info, write_file_dummy), }, + { "getscantable", 0444, FOPS(libertas_getscantable, + write_file_dummy), }, + { "sleepparams", 0644, FOPS(libertas_sleepparams_read, + libertas_sleepparams_write), }, + { "extscan", 0600, FOPS(NULL, libertas_extscan), }, + { "setuserscan", 0600, FOPS(NULL, libertas_setuserscan), }, +}; + +struct libertas_debugfs_files debugfs_events_files[] = { + {"low_rssi", 0644, FOPS(libertas_lowrssi_read, + libertas_lowrssi_write), }, + {"low_snr", 0644, FOPS(libertas_lowsnr_read, + libertas_lowsnr_write), }, + {"failure_count", 0644, FOPS(libertas_failcount_read, + libertas_failcount_write), }, + {"beacon_missed", 0644, FOPS(libertas_bcnmiss_read, + libertas_bcnmiss_write), }, + {"high_rssi", 0644, FOPS(libertas_highrssi_read, + libertas_highrssi_write), }, + {"high_snr", 0644, FOPS(libertas_highsnr_read, + libertas_highsnr_write), }, +}; + +struct libertas_debugfs_files debugfs_regs_files[] = { + {"rdmac", 0644, FOPS(libertas_rdmac_read, libertas_rdmac_write), }, + {"wrmac", 0600, FOPS(NULL, libertas_wrmac_write), }, + {"rdbbp", 0644, FOPS(libertas_rdbbp_read, libertas_rdbbp_write), }, + {"wrbbp", 0600, FOPS(NULL, libertas_wrbbp_write), }, + {"rdrf", 0644, FOPS(libertas_rdrf_read, libertas_rdrf_write), }, + {"wrrf", 0600, FOPS(NULL, libertas_wrrf_write), }, +}; + +void libertas_debugfs_init(void) +{ + if (!libertas_dir) + libertas_dir = debugfs_create_dir("libertas_wireless", NULL); + + return; +} + +void libertas_debugfs_remove(void) +{ + if (libertas_dir) + debugfs_remove(libertas_dir); + return; +} + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev) +{ + int i; + struct libertas_debugfs_files *files; + if (!libertas_dir) + goto exit; + + priv->debugfs_dir = debugfs_create_dir(dev->name, libertas_dir); + if (!priv->debugfs_dir) + goto exit; + + for (i=0; idebugfs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->debugfs_dir, + priv, + &files->fops); + } + + priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); + if (!priv->events_dir) + goto exit; + + for (i=0; idebugfs_events_files[i] = debugfs_create_file(files->name, + files->perm, + priv->events_dir, + priv, + &files->fops); + } + + priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); + if (!priv->regs_dir) + goto exit; + + for (i=0; idebugfs_regs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->regs_dir, + priv, + &files->fops); + } + +#ifdef PROC_DEBUG + libertas_debug_init(priv, dev); +#endif +exit: + return; +} + +void libertas_debugfs_remove_one(wlan_private *priv) +{ + int i; + + for(i=0; idebugfs_regs_files[i]); + + debugfs_remove(priv->regs_dir); + + for(i=0; idebugfs_events_files[i]); + + debugfs_remove(priv->events_dir); +#ifdef PROC_DEBUG + debugfs_remove(priv->debugfs_debug); +#endif + for(i=0; idebugfs_files[i]); +} + +/* debug entry */ + +#define item_size(n) (FIELD_SIZEOF(wlan_adapter, n)) +#define item_addr(n) (offsetof(wlan_adapter, n)) + +struct debug_data { + char name[32]; + u32 size; + u32 addr; +}; + +/* To debug any member of wlan_adapter, simply add one line here. + */ +static struct debug_data items[] = { + {"intcounter", item_size(intcounter), item_addr(intcounter)}, + {"psmode", item_size(psmode), item_addr(psmode)}, + {"psstate", item_size(psstate), item_addr(psstate)}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/** + * @brief proc read function + * + * @param page pointer to buffer + * @param s read data starting position + * @param off offset + * @param cnt counter + * @param eof end of file flag + * @param data data to output + * @return number of output data + */ +static ssize_t wlan_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int val = 0; + size_t pos = 0; + ssize_t res; + char *p; + int i; + struct debug_data *d; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + p = buf; + + d = (struct debug_data *)file->private_data; + + for (i = 0; i < num_of_items; i++) { + if (d[i].size == 1) + val = *((u8 *) d[i].addr); + else if (d[i].size == 2) + val = *((u16 *) d[i].addr); + else if (d[i].size == 4) + val = *((u32 *) d[i].addr); + + pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); + } + + res = simple_read_from_buffer(userbuf, count, ppos, p, pos); + + free_page(addr); + return res; +} + +/** + * @brief proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param cnt data number to write + * @param data data to write + * @return number of data + */ +static int wlan_debugfs_write(struct file *f, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = (struct debug_data *)f->private_data; + + pdata = (char *)kmalloc(cnt, GFP_KERNEL); + if (pdata == NULL) + return 0; + + if (copy_from_user(pdata, buf, cnt)) { + lbs_pr_debug(1, "Copy from user failed\n"); + kfree(pdata); + return 0; + } + + p0 = pdata; + for (i = 0; i < num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = simple_strtoul(p2, NULL, 0); + if (d[i].size == 1) + *((u8 *) d[i].addr) = (u8) r; + else if (d[i].size == 2) + *((u16 *) d[i].addr) = (u16) r; + else if (d[i].size == 4) + *((u32 *) d[i].addr) = (u32) r; + break; + } while (1); + } + kfree(pdata); + + return cnt; +} + +static struct file_operations libertas_debug_fops = { + .owner = THIS_MODULE, + .open = open_file_generic, + .write = wlan_debugfs_write, + .read = wlan_debugfs_read, +}; + +/** + * @brief create debug proc file + * + * @param priv pointer wlan_private + * @param dev pointer net_device + * @return N/A + */ +void libertas_debug_init(wlan_private * priv, struct net_device *dev) +{ + int i; + + if (!priv->debugfs_dir) + return; + + for (i = 0; i < num_of_items; i++) + items[i].addr += (u32) priv->adapter; + + priv->debugfs_debug = debugfs_create_file("debug", 0644, + priv->debugfs_dir, &items[0], + &libertas_debug_fops); +} + +/** + * @brief remove proc file + * + * @param priv pointer wlan_private + * @return N/A + */ +void libertas_debug_remove(wlan_private * priv) +{ + debugfs_remove(priv->debugfs_debug); +} diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h new file mode 100644 index 0000000..880a11b --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.h @@ -0,0 +1,6 @@ +void libertas_debugfs_init(void); +void libertas_debugfs_remove(void); + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev); +void libertas_debugfs_remove_one(wlan_private *priv); + diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h new file mode 100644 index 0000000..606bdd0 --- /dev/null +++ b/drivers/net/wireless/libertas/decl.h @@ -0,0 +1,83 @@ +/** + * This file contains declaration referring to + * functions defined in other source files + */ + +#ifndef _WLAN_DECL_H_ +#define _WLAN_DECL_H_ + +#include "defs.h" + +/** Function Prototype Declaration */ +struct wlan_private; +struct sk_buff; +struct net_device; + +extern char *libertas_fw_name; + +void libertas_free_adapter(wlan_private * priv); +int libertas_set_mac_packet_filter(wlan_private * priv); + +int libertas_send_null_packet(wlan_private * priv, u8 pwr_mgmt); +void libertas_send_tx_feedback(wlan_private * priv); +u8 libertas_check_last_packet_indication(wlan_private * priv); + +int libertas_free_cmd_buffer(wlan_private * priv); +struct cmd_ctrl_node; +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv); + +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf); + +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf); + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail); + +int libertas_allocate_cmd_buffer(wlan_private * priv); +int libertas_execute_next_command(wlan_private * priv); +int libertas_process_event(wlan_private * priv); +void libertas_interrupt(struct net_device *); +int libertas_set_radio_control(wlan_private * priv); +u32 libertas_index_to_data_rate(u8 index); +u8 libertas_data_rate_to_index(u32 rate); +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen); + +int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb); + +/** The proc fs interface */ +int libertas_process_rx_command(wlan_private * priv); +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb); +void libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band); + +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *); + +void libertas_ps_sleep(wlan_private * priv, int wait_option); +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode); +void libertas_ps_wakeup(wlan_private * priv, int wait_option); + +void libertas_tx_runqueue(wlan_private *priv); + +extern struct chan_freq_power *libertas_find_cfp_by_band_and_channel( + wlan_adapter * adapter, u8 band, u16 channel); + +extern void libertas_mac_event_disconnected(wlan_private * priv); + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str); + +int reset_device(wlan_private *priv); +/* main.c */ +extern struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, + int *cfp_no); +wlan_private *wlan_add_card(void *card); +int wlan_remove_card(void *card); + +#endif /* _WLAN_DECL_H_ */ diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h new file mode 100644 index 0000000..fb1478c --- /dev/null +++ b/drivers/net/wireless/libertas/defs.h @@ -0,0 +1,369 @@ +/** + * This header file contains global constant/enum definitions, + * global variable declaration. + */ +#ifndef _WLAN_DEFS_H_ +#define _WLAN_DEFS_H_ + +#include + +extern unsigned int libertas_debug; + +#define DRV_NAME "usb8xxx" + +#define lbs_pr_info(format, args...) \ + printk(KERN_INFO DRV_NAME": " format, ## args) +#define lbs_pr_err(format, args...) \ + printk(KERN_ERR DRV_NAME": " format, ## args) +#define lbs_pr_alert(format, args...) \ + printk(KERN_ALERT DRV_NAME": " format, ## args) + +#ifdef DEBUG +#define lbs_pr_debug(level, format, args...) \ + do { if (libertas_debug >= level) \ + printk(KERN_INFO DRV_NAME": " format, ##args); } while (0) +#define lbs_dev_dbg(level, device, format, args...) \ + lbs_pr_debug(level, "%s: " format, \ + (device)->bus_id , ## args) + +static inline void lbs_dbg_hex(char *prompt, u8 * buf, int len) +{ + int i = 0; + + if (!libertas_debug) + return; + + printk(KERN_DEBUG "%s: ", prompt); + for (i = 1; i <= len; i++) { + printk(KERN_DEBUG "%02x ", (u8) * buf); + buf++; + } + printk("\n"); +} +#else +#define lbs_pr_debug(level, format, args...) do {} while (0) +#define lbs_dev_dbg(level, device, format, args...) do {} while (0) +#define lbs_dbg_hex(x,y,z) do {} while (0) +#endif + +#define ENTER() lbs_pr_debug(1, "Enter: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) +#define LEAVE() lbs_pr_debug(1, "Leave: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) + +/** Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical +* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas +* driver has more local TxPDs. Each TxPD on the host memory is associated +* with a Tx control node. The driver maintains 8 RxPD descriptors for +* station firmware to store Rx packet information. +* +* Current version of MAC has a 32x6 multicast address buffer. +* +* 802.11b can have up to 14 channels, the driver keeps the +* BSSID(MAC address) of each APs or Ad hoc stations it has sensed. +*/ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define MRVDRV_NUM_OF_CMD_BUFFER 10 +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_MAX_BSSID_LIST 64 +#define MRVDRV_ASSOCIATION_TIME_OUT 255 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define WLAN_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/** Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_BSS_DESCRIPTS 16 +#define MRVDRV_MAX_REGION_CODE 6 + +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +#define MRVDRV_MIN_MULTIPLE_DTIM 1 +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +#define MRVDRV_DEFAULT_MULTIPLE_DTIM 1 + +#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 + +#define MRVDRV_CHANNELS_PER_SCAN 4 +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +#define MRVDRV_DEBUG_RX_PATH 0x00000001 +#define MRVDRV_DEBUG_TX_PATH 0x00000002 + +#define MRVDRV_MIN_BEACON_INTERVAL 20 +#define MRVDRV_MAX_BEACON_INTERVAL 1000 +#define MRVDRV_BEACON_INTERVAL 100 + +/** TxPD status */ + +/* Station firmware use TxPD status field to report final Tx transmit +* result, Bit masks are used to present combined situations. +*/ + +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/** Tx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define TxPD_CONTROL_WDS_FRAME (1<<17) +#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME + +/** RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + +/** RxPD status - Received packet types */ +/** Rx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define RxPD_CONTROL_WDS_FRAME (0x40) +#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME + +/** RSSI-related defines */ +/* RSSI constants are used to implement 802.11 RSSI threshold +* indication. if the Rx packet signal got too weak for 5 consecutive +* times, miniport driver (driver) will report this event to wrapper +*/ + +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +/** RTS/FRAG related defines */ +#define MRVDRV_RTS_MIN_VALUE 0 +#define MRVDRV_RTS_MAX_VALUE 2347 +#define MRVDRV_FRAG_MIN_VALUE 256 +#define MRVDRV_FRAG_MAX_VALUE 2346 + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) + +/** WPA key LENGTH*/ +#define MRVL_MAX_KEY_WPA_KEY_LENGTH 32 + +#define KEY_LEN_WPA_AES 16 +#define KEY_LEN_WPA_TKIP 32 +#define KEY_LEN_WEP_104 13 +#define KEY_LEN_WEP_40 5 + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define BAND_B (0x01) +#define BAND_G (0x02) +#define ALL_802_11_BANDS (BAND_B | BAND_G) + +/** MACRO DEFINITIONS */ +#define CAL_NF(NF) ((s32)(-(s32)(NF))) +#define CAL_RSSI(SNR, NF) ((s32)((s32)(SNR) + CAL_NF(NF))) +#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) + +#define DEFAULT_BCN_AVG_FACTOR 8 +#define DEFAULT_DATA_AVG_FACTOR 8 +#define AVG_SCALE 100 +#define CAL_AVG_SNR_NF(AVG, SNRNF, N) \ + (((AVG) == 0) ? ((u16)(SNRNF) * AVG_SCALE) : \ + ((((int)(AVG) * (N -1)) + ((u16)(SNRNF) * \ + AVG_SCALE)) / N)) + +#define B_SUPPORTED_RATES 8 +#define G_SUPPORTED_RATES 14 + +#define WLAN_SUPPORTED_RATES 14 + +#define MAX_LEDS 8 + +#define IS_MESH_FRAME(x) (x->cb[6]) +#define SET_MESH_FRAME(x) (x->cb[6]=1) +#define UNSET_MESH_FRAME(x) (x->cb[6]=0) + +/** Global Variable Declaration */ +typedef struct _wlan_private wlan_private; +typedef struct _wlan_adapter wlan_adapter; +extern const char libertas_driver_version[]; +extern u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE]; + +extern u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES]; + +extern u8 libertas_supported_rates[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_b[4]; + +/** ENUM definition*/ +/** SNRNF_TYPE */ +enum SNRNF_TYPE { + TYPE_BEACON = 0, + TYPE_RXPD, + MAX_TYPE_B +}; + +/** SNRNF_DATA*/ +enum SNRNF_DATA { + TYPE_NOAVG = 0, + TYPE_AVG, + MAX_TYPE_AVG +}; + +/** WLAN_802_11_AUTH_ALG*/ +enum WLAN_802_11_AUTH_ALG { + AUTH_ALG_OPEN_SYSTEM = 1, + AUTH_ALG_SHARED_KEY = 2, + AUTH_ALG_NETWORK_EAP = 8, +}; + +/** WLAN_802_1X_AUTH_ALG */ +enum WLAN_802_1X_AUTH_ALG { + WLAN_1X_AUTH_ALG_NONE = 1, + WLAN_1X_AUTH_ALG_LEAP = 2, + WLAN_1X_AUTH_ALG_TLS = 4, + WLAN_1X_AUTH_ALG_TTLS = 8, + WLAN_1X_AUTH_ALG_MD5 = 16, +}; + +/** WLAN_802_11_ENCRYPTION_MODE */ +enum WLAN_802_11_ENCRYPTION_MODE { + CIPHER_NONE, + CIPHER_WEP40, + CIPHER_TKIP, + CIPHER_CCMP, + CIPHER_WEP104, +}; + +/** WLAN_802_11_POWER_MODE */ +enum WLAN_802_11_POWER_MODE { + wlan802_11powermodecam, + wlan802_11powermodemax_psp, + wlan802_11Powermodefast_psp, + /*not a real mode, defined as an upper bound */ + wlan802_11powemodemax +}; + +/** PS_STATE */ +enum PS_STATE { + PS_STATE_FULL_POWER, + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP +}; + +/** DNLD_STATE */ +enum DNLD_STATE { + DNLD_RES_RECEIVED, + DNLD_DATA_SENT, + DNLD_CMD_SENT +}; + +/** WLAN_MEDIA_STATE */ +enum WLAN_MEDIA_STATE { + libertas_connected, + libertas_disconnected +}; + +/** WLAN_802_11_PRIVACY_FILTER */ +enum WLAN_802_11_PRIVACY_FILTER { + wlan802_11privfilteracceptall, + wlan802_11privfilter8021xWEP +}; + +/** mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +/** WLAN_802_11_NETWORK_INFRASTRUCTURE */ +enum WLAN_802_11_NETWORK_INFRASTRUCTURE { + wlan802_11ibss, + wlan802_11infrastructure, + wlan802_11autounknown, + /*defined as upper bound */ + wlan802_11infrastructuremax +}; + +/** WLAN_802_11_AUTHENTICATION_MODE */ +enum WLAN_802_11_AUTHENTICATION_MODE { + wlan802_11authmodeopen = 0x00, + wlan802_11authmodeshared = 0x01, + wlan802_11authmodenetworkEAP = 0x80, +}; + +/** WLAN_802_11_WEP_STATUS */ +enum WLAN_802_11_WEP_STATUS { + wlan802_11WEPenabled, + wlan802_11WEPdisabled, +}; + +/** SNMP_MIB_INDEX_e */ +enum SNMP_MIB_INDEX_e { + desired_bsstype_i = 0, + op_rateset_i, + bcnperiod_i, + dtimperiod_i, + assocrsp_timeout_i, + rtsthresh_i, + short_retrylim_i, + long_retrylim_i, + fragthresh_i, + dot11d_i, + dot11h_i, + manufid_i, + prodID_i, + manuf_oui_i, + manuf_name_i, + manuf_prodname_i, + manuf_prodver_i, +}; + +/** KEY_TYPE_ID */ +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES +}; + +/** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ +enum KEY_INFO_WPA { + KEY_INFO_WPA_MCAST = 0x01, + KEY_INFO_WPA_UNICAST = 0x02, + KEY_INFO_WPA_ENABLED = 0x04 +}; + +/** SNMP_MIB_VALUE_e */ +enum SNMP_MIB_VALUE_e { + SNMP_MIB_VALUE_INFRA = 1, + SNMP_MIB_VALUE_ADHOC +}; + +/* Default values for fwt commands. */ +#define FWT_DEFAULT_METRIC 0 +#define FWT_DEFAULT_DIR 1 +#define FWT_DEFAULT_SSN 0xffffffff +#define FWT_DEFAULT_DSN 0 +#define FWT_DEFAULT_HOPCOUNT 0 +#define FWT_DEFAULT_TTL 0 +#define FWT_DEFAULT_EXPIRATION 0 +#define FWT_DEFAULT_SLEEPMODE 0 +#define FWT_DEFAULT_SNR 0 + +#endif /* _WLAN_DEFS_H_ */ diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h new file mode 100644 index 0000000..b1f876f --- /dev/null +++ b/drivers/net/wireless/libertas/dev.h @@ -0,0 +1,403 @@ +/** + * This file contains definitions and data structures specific + * to Marvell 802.11 NIC. It contains the Device Information + * structure wlan_adapter. + */ +#ifndef _WLAN_DEV_H_ +#define _WLAN_DEV_H_ + +#include +#include +#include +#include + +#include "defs.h" +#include "scan.h" +#include "thread.h" + +extern struct ethtool_ops libertas_ethtool_ops; + +#define MAX_BSSID_PER_CHANNEL 16 + +#define NR_TX_QUEUE 3 + +/* For the extended Scan */ +#define MAX_EXTENDED_SCAN_BSSID_LIST MAX_BSSID_PER_CHANNEL * \ + MRVDRV_MAX_CHANNEL_SIZE + 1 + +#define MAX_REGION_CHANNEL_NUM 2 + +/** Chan-freq-TxPower mapping table*/ +struct chan_freq_power { + /** channel Number */ + u16 channel; + /** frequency of this channel */ + u32 freq; + /** Max allowed Tx power level */ + u16 maxtxpower; + /** TRUE:channel unsupported; FLASE:supported*/ + u8 unsupported; +}; + +/** region-band mapping table*/ +struct region_channel { + /** TRUE if this entry is valid */ + u8 valid; + /** region code for US, Japan ... */ + u8 region; + /** band B/G/A, used for BAND_CONFIG cmd */ + u8 band; + /** Actual No. of elements in the array below */ + u8 nrcfp; + /** chan-freq-txpower mapping table*/ + struct chan_freq_power *CFP; +}; + +struct wlan_802_11_security { + u8 WPAenabled; + u8 WPA2enabled; + enum WLAN_802_11_WEP_STATUS WEPstatus; + enum WLAN_802_11_AUTHENTICATION_MODE authmode; + enum WLAN_802_1X_AUTH_ALG auth1xalg; + enum WLAN_802_11_ENCRYPTION_MODE Encryptionmode; +}; + +/** Current Basic Service Set State Structure */ +struct current_bss_params { + struct bss_descriptor bssdescriptor; + /** bssid */ + u8 bssid[ETH_ALEN]; + /** ssid */ + struct WLAN_802_11_SSID ssid; + + /** band */ + u8 band; + /** channel */ + u8 channel; + /** number of rates supported */ + int numofrates; + /** supported rates*/ + u8 datarates[WLAN_SUPPORTED_RATES]; +}; + +/** sleep_params */ +struct sleep_params { + u16 sp_error; + u16 sp_offset; + u16 sp_stabletime; + u8 sp_calcontrol; + u8 sp_extsleepclk; + u16 sp_reserved; +}; + +/** Data structure for the Marvell WLAN device */ +typedef struct _wlan_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** Upload received */ + u32 upld_rcv; + /** Upload type */ + u32 upld_typ; + /** Upload length */ + u32 upld_len; + /** netdev pointer */ + struct net_device *netdev; + /* Upload buffer */ + u8 upld_buf[WLAN_UPLD_SIZE]; + /* Download sent: + bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + u8 dnld_sent; +} wlan_dev_t, *pwlan_dev_t; + +/* Mesh statistics */ +struct wlan_mesh_stats { + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ + u32 fwd_drop_ttl; /* Fwd: TTL zero */ + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ + u32 fwd_drop_noroute; /* Fwd: No route to Destination */ + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ + u32 drop_blind; /* Rx: Dropped by blinding table */ +}; + +/** Private structure for the MV device */ +struct _wlan_private { + int open; + int mesh_open; + int infra_open; + + wlan_adapter *adapter; + wlan_dev_t wlan_dev; + + struct net_device_stats stats; + struct net_device *mesh_dev ; /* Virtual device */ + + struct iw_statistics wstats; + struct wlan_mesh_stats mstats; + struct dentry *debugfs_dir; + struct dentry *debugfs_debug; + struct dentry *debugfs_files[6]; + + struct dentry *events_dir; + struct dentry *debugfs_events_files[6]; + + struct dentry *regs_dir; + struct dentry *debugfs_regs_files[6]; + + u32 mac_offset; + u32 bbp_offset; + u32 rf_offset; + + const struct firmware *firmware; + struct device *hotplug_device; + + /** thread to service interrupts */ + struct wlan_thread mainthread; + + struct delayed_work assoc_work; + struct workqueue_struct *assoc_thread; +}; + +/** Association request + * + * Encapsulates all the options that describe a specific assocation request + * or configuration of the wireless card's radio, mode, and security settings. + */ +struct assoc_request { +#define ASSOC_FLAG_SSID 1 +#define ASSOC_FLAG_CHANNEL 2 +#define ASSOC_FLAG_MODE 3 +#define ASSOC_FLAG_BSSID 4 +#define ASSOC_FLAG_WEP_KEYS 5 +#define ASSOC_FLAG_WEP_TX_KEYIDX 6 +#define ASSOC_FLAG_WPA_MCAST_KEY 7 +#define ASSOC_FLAG_WPA_UCAST_KEY 8 +#define ASSOC_FLAG_SECINFO 9 +#define ASSOC_FLAG_WPA_IE 10 + unsigned long flags; + + struct WLAN_802_11_SSID ssid; + u8 channel; + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode; + u8 bssid[ETH_ALEN]; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + struct wlan_802_11_security secinfo; + + /** WPA Information Elements*/ +#define MAX_WPA_IE_LEN 64 + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; +}; + +/** Wlan adapter data structure*/ +struct _wlan_adapter { + /** STATUS variables */ + u32 fwreleasenumber; + u32 fwcapinfo; + /* protected with big lock */ + + struct mutex lock; + + u8 tmptxbuf[WLAN_UPLD_SIZE]; + /* protected by hard_start_xmit serialization */ + + /** command-related variables */ + u16 seqnum; + /* protected by big lock */ + + struct cmd_ctrl_node *cmd_array; + /** Current command */ + struct cmd_ctrl_node *cur_cmd; + int cur_cmd_retcode; + /** command Queues */ + /** Free command buffers */ + struct list_head cmdfreeq; + /** Pending command buffers */ + struct list_head cmdpendingq; + + wait_queue_head_t cmd_pending; + u8 nr_cmd_pending; + /* command related variables protected by adapter->driver_lock */ + + /** Async and Sync Event variables */ + u32 intcounter; + u32 eventcause; + u8 nodename[16]; /* nickname */ + + /** spin locks */ + spinlock_t driver_lock; + + /** Timers */ + struct timer_list command_timer; + + /* TX queue used in PS mode */ + spinlock_t txqueue_lock; + struct sk_buff *tx_queue_ps[NR_TX_QUEUE]; + unsigned int tx_queue_idx; + + u8 hisregcpy; + + /** current ssid/bssid related parameters*/ + struct current_bss_params curbssparams; + + enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; + + struct bss_descriptor *pattemptedbssdesc; + + struct WLAN_802_11_SSID previousssid; + u8 previousbssid[ETH_ALEN]; + + struct bss_descriptor *scantable; + u32 numinscantable; + + u8 scantype; + u32 scanmode; + + u16 beaconperiod; + u8 adhoccreate; + + /** capability Info used in Association, start, join */ + struct ieeetypes_capinfo capinfo; + + /** MAC address information */ + u8 current_addr[ETH_ALEN]; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + u32 nr_of_multicastmacaddr; + + /** 802.11 statistics */ +// struct cmd_DS_802_11_GET_STAT wlan802_11Stat; + + u16 enablehwauto; + u16 ratebitmap; + /** control G rates */ + u8 adhoc_grate_enabled; + + u32 txantenna; + u32 rxantenna; + + u8 adhocchannel; + u32 fragthsd; + u32 rtsthsd; + + u32 datarate; + u8 is_datarate_auto; + + u16 listeninterval; + u16 prescan; + u8 txretrycount; + + /** Tx-related variables (for single packet tx) */ + struct sk_buff *currenttxskb; + u16 TxLockFlag; + + /** NIC Operation characteristics */ + u16 currentpacketfilter; + u32 connect_status; + u16 regioncode; + u16 regiontableindex; + u16 txpowerlevel; + + /** POWER MANAGEMENT AND PnP SUPPORT */ + u8 surpriseremoved; + u16 atimwindow; + + u16 psmode; /* Wlan802_11PowermodeCAM=disable + Wlan802_11PowermodeMAX_PSP=enable */ + u16 multipledtim; + u32 psstate; + u8 needtowakeup; + + struct PS_CMD_ConfirmSleep libertas_ps_confirm_sleep; + u16 locallisteninterval; + u16 nullpktinterval; + + struct assoc_request * assoc_req; + + /** Encryption parameter */ + struct wlan_802_11_security secinfo; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + /** WPA Information Elements*/ +#define MAX_WPA_IE_LEN 64 + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; + + u16 rxantennamode; + u16 txantennamode; + + /** Requested Signal Strength*/ + u16 bcn_avg_factor; + u16 data_avg_factor; + u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG]; + u16 NF[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 RSSI[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 rawSNR[DEFAULT_DATA_AVG_FACTOR]; + u8 rawNF[DEFAULT_DATA_AVG_FACTOR]; + u16 nextSNRNF; + u16 numSNRNF; + u16 rxpd_rate; + + u8 radioon; + u32 preamble; + + /** Multi bands Parameter*/ + u8 libertas_supported_rates[G_SUPPORTED_RATES]; + + /** Blue Tooth Co-existence Arbitration */ + + /** sleep_params */ + struct sleep_params sp; + + /** RF calibration data */ + +#define MAX_REGION_CHANNEL_NUM 2 + /** region channel data */ + struct region_channel region_channel[MAX_REGION_CHANNEL_NUM]; + + struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM]; + + /** 11D and Domain Regulatory Data */ + struct wlan_802_11d_domain_reg domainreg; + struct parsed_region_chan_11d parsed_region_chan; + + /** FSM variable for 11d support */ + u32 enable11d; + + /** MISCELLANEOUS */ + u8 *prdeeprom; + struct wlan_offset_value offsetvalue; + + struct cmd_ds_802_11_get_log logmsg; + u16 scanprobes; + + u32 pkttxctrl; + + u16 txrate; + u32 linkmode; + u32 radiomode; + u32 debugmode; + u8 fw_ready; +}; + +#endif /* _WLAN_DEV_H_ */ diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c new file mode 100644 index 0000000..0064de5 --- /dev/null +++ b/drivers/net/wireless/libertas/ethtool.c @@ -0,0 +1,184 @@ + +#include +#include +#include + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" +static const char * mesh_stat_strings[]= { + "drop_duplicate_bcast", + "drop_ttl_zero", + "drop_no_fwd_route", + "drop_no_buffers", + "fwded_unicast_cnt", + "fwded_bcast_cnt", + "drop_blind_table" +}; + +static void libertas_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + wlan_private *priv = (wlan_private *) dev->priv; + char fwver[32]; + + libertas_get_fwversion(priv->adapter, fwver, sizeof(fwver) - 1); + + strcpy(info->driver, "libertas"); + strcpy(info->version, libertas_driver_version); + strcpy(info->fw_version, fwver); +} + +/* All 8388 parts have 16KiB EEPROM size at the time of writing. + * In case that changes this needs fixing. + */ +#define LIBERTAS_EEPROM_LEN 16384 + +static int libertas_ethtool_get_eeprom_len(struct net_device *dev) +{ + return LIBERTAS_EEPROM_LEN; +} + +static int libertas_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr regctrl; + char *ptr; + int ret; + + regctrl.action = 0; + regctrl.offset = eeprom->offset; + regctrl.NOB = eeprom->len; + + if (eeprom->offset + eeprom->len > LIBERTAS_EEPROM_LEN) + return -EINVAL; + +// mutex_lock(&priv->mutex); + + adapter->prdeeprom = + (char *)kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL); + if (!adapter->prdeeprom) + return -ENOMEM; + memcpy(adapter->prdeeprom, ®ctrl, sizeof(regctrl)); + + /* +14 is for action, offset, and NOB in + * response */ + lbs_pr_debug(1, "action:%d offset: %x NOB: %02x\n", + regctrl.action, regctrl.offset, regctrl.NOB); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_eeprom_access, + regctrl.action, + cmd_option_waitforrsp, 0, + ®ctrl); + + if (ret) { + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); + LEAVE(); + return ret; + } + + mdelay(10); + + ptr = (char *)adapter->prdeeprom; + + /* skip the command header, but include the "value" u32 variable */ + ptr = ptr + sizeof(struct wlan_ioctl_regrdwr) - 4; + + /* + * Return the result back to the user + */ + memcpy(bytes, ptr, eeprom->len); + + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); +// mutex_unlock(&priv->mutex); + + return 0; +} + +static void libertas_ethtool_get_stats(struct net_device * dev, + struct ethtool_stats * stats, u64 * data) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + stats->cmd = ETHTOOL_GSTATS; + BUG_ON(stats->n_stats != MESH_STATS_NUM); + + data[0] = priv->mstats.fwd_drop_rbt; + data[1] = priv->mstats.fwd_drop_ttl; + data[2] = priv->mstats.fwd_drop_noroute; + data[3] = priv->mstats.fwd_drop_nobuf; + data[4] = priv->mstats.fwd_unicast_cnt; + data[5] = priv->mstats.fwd_bcast_cnt; + data[6] = priv->mstats.drop_blind; + + LEAVE(); +} + +static int libertas_ethtool_get_stats_count(struct net_device * dev) +{ + int ret; + wlan_private *priv = dev->priv; + struct cmd_ds_mesh_access mesh_access; + + ENTER(); + /* Get Mesh Statistics */ + ret = libertas_prepare_and_send_command(priv, + cmd_mesh_access, cmd_act_mesh_get_stats, + cmd_option_waitforrsp, 0, &mesh_access); + + if (ret) { + LEAVE(); + return 0; + } + + priv->mstats.fwd_drop_rbt = mesh_access.data[0]; + priv->mstats.fwd_drop_ttl = mesh_access.data[1]; + priv->mstats.fwd_drop_noroute = mesh_access.data[2]; + priv->mstats.fwd_drop_nobuf = mesh_access.data[3]; + priv->mstats.fwd_unicast_cnt = mesh_access.data[4]; + priv->mstats.fwd_bcast_cnt = mesh_access.data[5]; + priv->mstats.drop_blind = mesh_access.data[6]; + + LEAVE(); + return MESH_STATS_NUM; +} + +static void libertas_ethtool_get_strings (struct net_device * dev, + u32 stringset, + u8 * s) +{ + int i; + + ENTER(); + switch (stringset) { + case ETH_SS_STATS: + for (i=0; i < MESH_STATS_NUM; i++) { + memcpy(s + i * ETH_GSTRING_LEN, + mesh_stat_strings[i], + ETH_GSTRING_LEN); + } + break; + } + LEAVE(); +} + +struct ethtool_ops libertas_ethtool_ops = { + .get_drvinfo = libertas_ethtool_get_drvinfo, + .get_eeprom = libertas_ethtool_get_eeprom, + .get_eeprom_len = libertas_ethtool_get_eeprom_len, + .get_stats_count = libertas_ethtool_get_stats_count, + .get_ethtool_stats = libertas_ethtool_get_stats, + .get_strings = libertas_ethtool_get_strings, +}; + diff --git a/drivers/net/wireless/libertas/fw.c b/drivers/net/wireless/libertas/fw.c new file mode 100644 index 0000000..b194a45 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.c @@ -0,0 +1,361 @@ +/** + * This file contains the initialization for FW and HW + */ +#include +#include + +#include +#include +#include + +#include "host.h" +#include "sbi.h" +#include "defs.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "if_usb.h" + +char *libertas_fw_name = NULL; +module_param_named(fw_name, libertas_fw_name, charp, 0644); + +unsigned int libertas_debug = 0; +module_param(libertas_debug, int, 0); + +/** + * @brief This function checks the validity of Boot2/FW image. + * + * @param data pointer to image + * len image length + * @return 0 or -1 + */ +static int check_fwfile_format(u8 *data, u32 totlen) +{ + u8 bincmd, exit; + u32 blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + bincmd = *data; + blksize = *(u32*)(data + offsetof(struct fwheader, datalength)); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + lbs_pr_err("bin file format check FAIL...\n"); + else + lbs_pr_debug(1, "bin file format check PASS...\n"); + + return ret; +} + +/** + * @brief This function downloads firmware image, gets + * HW spec from firmware and set basic parameters to + * firmware. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +static int wlan_setup_station_hw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if ((ret = request_firmware(&priv->firmware, libertas_fw_name, + priv->hotplug_device)) < 0) { + lbs_pr_err("request_firmware() failed, error code = %#x\n", + ret); + lbs_pr_err("%s not found in /lib/firmware\n", libertas_fw_name); + goto done; + } + + if(check_fwfile_format(priv->firmware->data, priv->firmware->size)) { + release_firmware(priv->firmware); + goto done; + } + + ret = libertas_sbi_prog_firmware(priv); + + release_firmware(priv->firmware); + + if (ret) { + lbs_pr_debug(1, "Bootloader in invalid state!\n"); + ret = -1; + goto done; + } + + /* + * Read MAC address from HW + */ + memset(adapter->current_addr, 0xff, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_get_hw_spec, + 0, cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + libertas_set_mac_packet_filter(priv); + + /* Get the supported Data rates */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + cmd_act_get_tx_rate, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + ret = 0; +done: + LEAVE(); + + return (ret); +} + +static int wlan_allocate_adapter(wlan_private * priv) +{ + u32 ulbufsize; + wlan_adapter *adapter = priv->adapter; + + struct bss_descriptor *ptempscantable; + + /* Allocate buffer to store the BSSID list */ + ulbufsize = sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST; + if (!(ptempscantable = kmalloc(ulbufsize, GFP_KERNEL))) { + libertas_free_adapter(priv); + return -1; + } + + adapter->scantable = ptempscantable; + memset(adapter->scantable, 0, ulbufsize); + + /* Allocate the command buffers */ + libertas_allocate_cmd_buffer(priv); + + memset(&adapter->libertas_ps_confirm_sleep, 0, sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.seqnum = cpu_to_le16(++adapter->seqnum); + adapter->libertas_ps_confirm_sleep.command = + cpu_to_le16(cmd_802_11_ps_mode); + adapter->libertas_ps_confirm_sleep.size = + cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.result = 0; + adapter->libertas_ps_confirm_sleep.action = + cpu_to_le16(cmd_subcmd_sleep_confirmed); + + return 0; +} + +static void wlan_init_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + adapter->scanprobes = 0; + + adapter->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + adapter->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + /* ATIM params */ + adapter->atimwindow = 0; + + adapter->connect_status = libertas_disconnected; + memset(adapter->current_addr, 0xff, ETH_ALEN); + + /* scan type */ + adapter->scantype = cmd_scan_type_active; + + /* scan mode */ + adapter->scanmode = cmd_bss_type_any; + + /* 802.11 specific */ + adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; + for (i = 0; i < sizeof(adapter->wep_keys) / sizeof(adapter->wep_keys[0]); + i++) + memset(&adapter->wep_keys[i], 0, sizeof(struct WLAN_802_11_KEY)); + adapter->wep_tx_keyidx = 0; + adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; + adapter->secinfo.authmode = wlan802_11authmodeopen; + adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; + adapter->secinfo.Encryptionmode = CIPHER_NONE; + adapter->inframode = wlan802_11infrastructure; + + adapter->assoc_req = NULL; + + adapter->numinscantable = 0; + adapter->pattemptedbssdesc = NULL; + mutex_init(&adapter->lock); + + adapter->prescan = 1; + + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + /* PnP and power profile */ + adapter->surpriseremoved = 0; + + adapter->currentpacketfilter = + cmd_act_mac_rx_on | cmd_act_mac_tx_on; + + adapter->radioon = RADIO_ON; + adapter->txantenna = RF_ANTENNA_2; + adapter->rxantenna = RF_ANTENNA_AUTO; + + adapter->is_datarate_auto = 1; + adapter->beaconperiod = MRVDRV_BEACON_INTERVAL; + + // set default value of capinfo. +#define SHORT_PREAMBLE_ALLOWED 1 + memset(&adapter->capinfo, 0, sizeof(adapter->capinfo)); + adapter->capinfo.shortpreamble = SHORT_PREAMBLE_ALLOWED; + + adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; + + adapter->psmode = wlan802_11powermodecam; + adapter->multipledtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; + + adapter->listeninterval = MRVDRV_DEFAULT_LISTEN_INTERVAL; + + adapter->psstate = PS_STATE_FULL_POWER; + adapter->needtowakeup = 0; + adapter->locallisteninterval = 0; /* default value in firmware will be used */ + + adapter->datarate = 0; // Initially indicate the rate as auto + + adapter->adhoc_grate_enabled = 0; + + adapter->intcounter = 0; + + adapter->currenttxskb = NULL; + adapter->pkttxctrl = 0; + + memset(&adapter->tx_queue_ps, 0, NR_TX_QUEUE*sizeof(struct sk_buff*)); + adapter->tx_queue_idx = 0; + spin_lock_init(&adapter->txqueue_lock); + + return; +} + +static void command_timer_fn(unsigned long data); + +int libertas_init_fw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate adapter structure */ + if ((ret = wlan_allocate_adapter(priv)) != 0) + goto done; + + /* init adapter structure */ + wlan_init_adapter(priv); + + /* init timer etc. */ + setup_timer(&adapter->command_timer, command_timer_fn, + (unsigned long)priv); + + /* download fimrware etc. */ + if ((ret = wlan_setup_station_hw(priv)) != 0) { + del_timer_sync(&adapter->command_timer); + goto done; + } + + /* init 802.11d */ + libertas_init_11d(priv); + + ret = 0; +done: + LEAVE(); + return ret; +} + +void libertas_free_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + + if (!adapter) { + lbs_pr_debug(1, "Why double free adapter?:)\n"); + return; + } + + lbs_pr_debug(1, "Free command buffer\n"); + libertas_free_cmd_buffer(priv); + + lbs_pr_debug(1, "Free commandTimer\n"); + del_timer(&adapter->command_timer); + + lbs_pr_debug(1, "Free scantable\n"); + if (adapter->scantable) { + kfree(adapter->scantable); + adapter->scantable = NULL; + } + + lbs_pr_debug(1, "Free adapter\n"); + + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; +} + +/** + * This function handles the timeout of command sending. + * It will re-send the same command again. + */ +static void command_timer_fn(unsigned long data) +{ + wlan_private *priv = (wlan_private *)data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *ptempnode; + struct cmd_ds_command *cmd; + unsigned long flags; + + ptempnode = adapter->cur_cmd; + cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr; + + lbs_pr_info("command_timer_fn fired (%x)\n", cmd->command); + + if (!adapter->fw_ready) + return; + + if (ptempnode == NULL) { + lbs_pr_debug(1, "PTempnode Empty\n"); + return; + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "Re-sending same command as it timeout...!\n"); + libertas_queue_cmd(adapter, ptempnode, 0); + + wake_up_interruptible(&priv->mainthread.waitq); + + return; +} diff --git a/drivers/net/wireless/libertas/fw.h b/drivers/net/wireless/libertas/fw.h new file mode 100644 index 0000000..1f9ae26 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.h @@ -0,0 +1,13 @@ +/** + * This header file contains FW interface related definitions. + */ +#ifndef _WLAN_FW_H_ +#define _WLAN_FW_H_ + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +int libertas_init_fw(wlan_private * priv); + +#endif /* _WLAN_FW_H_ */ diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h new file mode 100644 index 0000000..c0faaec --- /dev/null +++ b/drivers/net/wireless/libertas/host.h @@ -0,0 +1,338 @@ +/** + * This file contains definitions of WLAN commands. + */ + +#ifndef _HOST_H_ +#define _HOST_H_ + +/** PUBLIC DEFINITIONS */ +#define DEFAULT_AD_HOC_CHANNEL 6 +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +/** IEEE 802.11 oids */ +#define OID_802_11_SSID 0x00008002 +#define OID_802_11_INFRASTRUCTURE_MODE 0x00008008 +#define OID_802_11_FRAGMENTATION_THRESHOLD 0x00008009 +#define OID_802_11_RTS_THRESHOLD 0x0000800A +#define OID_802_11_TX_ANTENNA_SELECTED 0x0000800D +#define OID_802_11_SUPPORTED_RATES 0x0000800E +#define OID_802_11_STATISTICS 0x00008012 +#define OID_802_11_TX_RETRYCOUNT 0x0000801D +#define OID_802_11D_ENABLE 0x00008020 + +#define cmd_option_waitforrsp 0x0002 + +/** Host command ID */ +#define cmd_code_dnld 0x0002 +#define cmd_get_hw_spec 0x0003 +#define cmd_eeprom_update 0x0004 +#define cmd_802_11_reset 0x0005 +#define cmd_802_11_scan 0x0006 +#define cmd_802_11_get_log 0x000b +#define cmd_mac_multicast_adr 0x0010 +#define cmd_802_11_authenticate 0x0011 +#define cmd_802_11_eeprom_access 0x0059 +#define cmd_802_11_associate 0x0050 +#define cmd_802_11_set_wep 0x0013 +#define cmd_802_11_get_stat 0x0014 +#define cmd_802_3_get_stat 0x0015 +#define cmd_802_11_snmp_mib 0x0016 +#define cmd_mac_reg_map 0x0017 +#define cmd_bbp_reg_map 0x0018 +#define cmd_mac_reg_access 0x0019 +#define cmd_bbp_reg_access 0x001a +#define cmd_rf_reg_access 0x001b +#define cmd_802_11_radio_control 0x001c +#define cmd_802_11_rf_channel 0x001d +#define cmd_802_11_rf_tx_power 0x001e +#define cmd_802_11_rssi 0x001f +#define cmd_802_11_rf_antenna 0x0020 + +#define cmd_802_11_ps_mode 0x0021 + +#define cmd_802_11_data_rate 0x0022 +#define cmd_rf_reg_map 0x0023 +#define cmd_802_11_deauthenticate 0x0024 +#define cmd_802_11_reassociate 0x0025 +#define cmd_802_11_disassociate 0x0026 +#define cmd_mac_control 0x0028 +#define cmd_802_11_ad_hoc_start 0x002b +#define cmd_802_11_ad_hoc_join 0x002c + +#define cmd_802_11_query_tkip_reply_cntrs 0x002e +#define cmd_802_11_enable_rsn 0x002f +#define cmd_802_11_pairwise_tsc 0x0036 +#define cmd_802_11_group_tsc 0x0037 +#define cmd_802_11_key_material 0x005e + +#define cmd_802_11_set_afc 0x003c +#define cmd_802_11_get_afc 0x003d + +#define cmd_802_11_ad_hoc_stop 0x0040 + +#define cmd_802_11_beacon_stop 0x0049 + +#define cmd_802_11_mac_address 0x004D +#define cmd_802_11_eeprom_access 0x0059 + +#define cmd_802_11_band_config 0x0058 + +#define cmd_802_11d_domain_info 0x005b + +#define cmd_802_11_sleep_params 0x0066 + +#define cmd_802_11_inactivity_timeout 0x0067 + +#define cmd_802_11_tpc_cfg 0x0072 +#define cmd_802_11_pwr_cfg 0x0073 + +#define cmd_802_11_led_gpio_ctrl 0x004e + +#define cmd_802_11_subscribe_event 0x0075 + +#define cmd_802_11_rate_adapt_rateset 0x0076 + +#define cmd_802_11_tx_rate_query 0x007f + +#define cmd_get_tsf 0x0080 + +#define cmd_bt_access 0x0087 +#define cmd_ret_bt_access 0x8087 + +#define cmd_fwt_access 0x0088 +#define cmd_ret_fwt_access 0x8088 + +#define cmd_mesh_access 0x0090 +#define cmd_ret_mesh_access 0x8090 + +/* For the IEEE Power Save */ +#define cmd_subcmd_enter_ps 0x0030 +#define cmd_subcmd_exit_ps 0x0031 +#define cmd_subcmd_sleep_confirmed 0x0034 +#define cmd_subcmd_full_powerdown 0x0035 +#define cmd_subcmd_full_powerup 0x0036 + +/* command RET code, MSB is set to 1 */ +#define cmd_ret_hw_spec_info 0x8003 +#define cmd_ret_eeprom_update 0x8004 +#define cmd_ret_802_11_reset 0x8005 +#define cmd_ret_802_11_scan 0x8006 +#define cmd_ret_802_11_get_log 0x800b +#define cmd_ret_mac_control 0x8028 +#define cmd_ret_mac_multicast_adr 0x8010 +#define cmd_ret_802_11_authenticate 0x8011 +#define cmd_ret_802_11_deauthenticate 0x8024 +#define cmd_ret_802_11_associate 0x8012 +#define cmd_ret_802_11_reassociate 0x8025 +#define cmd_ret_802_11_disassociate 0x8026 +#define cmd_ret_802_11_set_wep 0x8013 +#define cmd_ret_802_11_stat 0x8014 +#define cmd_ret_802_3_stat 0x8015 +#define cmd_ret_802_11_snmp_mib 0x8016 +#define cmd_ret_mac_reg_map 0x8017 +#define cmd_ret_bbp_reg_map 0x8018 +#define cmd_ret_rf_reg_map 0x8023 +#define cmd_ret_mac_reg_access 0x8019 +#define cmd_ret_bbp_reg_access 0x801a +#define cmd_ret_rf_reg_access 0x801b +#define cmd_ret_802_11_radio_control 0x801c +#define cmd_ret_802_11_rf_channel 0x801d +#define cmd_ret_802_11_rssi 0x801f +#define cmd_ret_802_11_rf_tx_power 0x801e +#define cmd_ret_802_11_rf_antenna 0x8020 +#define cmd_ret_802_11_ps_mode 0x8021 +#define cmd_ret_802_11_data_rate 0x8022 + +#define cmd_ret_802_11_ad_hoc_start 0x802B +#define cmd_ret_802_11_ad_hoc_join 0x802C + +#define cmd_ret_802_11_query_tkip_reply_cntrs 0x802e +#define cmd_ret_802_11_enable_rsn 0x802f +#define cmd_ret_802_11_pairwise_tsc 0x8036 +#define cmd_ret_802_11_group_tsc 0x8037 +#define cmd_ret_802_11_key_material 0x805e + +#define cmd_enable_rsn 0x0001 +#define cmd_disable_rsn 0x0000 + +#define cmd_act_set 0x0001 +#define cmd_act_get 0x0000 + +#define cmd_act_get_AES (cmd_act_get + 2) +#define cmd_act_set_AES (cmd_act_set + 2) +#define cmd_act_remove_aes (cmd_act_set + 3) + +#define cmd_ret_802_11_set_afc 0x803c +#define cmd_ret_802_11_get_afc 0x803d + +#define cmd_ret_802_11_ad_hoc_stop 0x8040 + +#define cmd_ret_802_11_beacon_stop 0x8049 + +#define cmd_ret_802_11_mac_address 0x804D +#define cmd_ret_802_11_eeprom_access 0x8059 + +#define cmd_ret_802_11_band_config 0x8058 + +#define cmd_ret_802_11_sleep_params 0x8066 + +#define cmd_ret_802_11_inactivity_timeout 0x8067 + +#define cmd_ret_802_11d_domain_info (0x8000 | \ + cmd_802_11d_domain_info) + +#define cmd_ret_802_11_tpc_cfg (cmd_802_11_tpc_cfg | 0x8000) +#define cmd_ret_802_11_pwr_cfg (cmd_802_11_pwr_cfg | 0x8000) + +#define cmd_ret_802_11_led_gpio_ctrl 0x804e + +#define cmd_ret_802_11_subscribe_event (cmd_802_11_subscribe_event | 0x8000) + +#define cmd_ret_802_11_rate_adapt_rateset (cmd_802_11_rate_adapt_rateset | 0x8000) + +#define cmd_rte_802_11_tx_rate_query (cmd_802_11_tx_rate_query | 0x8000) + +#define cmd_ret_get_tsf 0x8080 + +/* Define action or option for cmd_802_11_set_wep */ +#define cmd_act_add 0x0002 +#define cmd_act_remove 0x0004 +#define cmd_act_use_default 0x0008 + +#define cmd_type_wep_40_bit 0x0001 +#define cmd_type_wep_104_bit 0x0002 + +#define cmd_NUM_OF_WEP_KEYS 4 + +#define cmd_WEP_KEY_INDEX_MASK 0x3fff + +/* Define action or option for cmd_802_11_reset */ +#define cmd_act_halt 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_bss_type_bss 0x0001 +#define cmd_bss_type_ibss 0x0002 +#define cmd_bss_type_any 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_scan_type_active 0x0000 +#define cmd_scan_type_passive 0x0001 + +#define cmd_scan_radio_type_bg 0 + +#define cmd_scan_probe_delay_time 0 + +/* Define action or option for cmd_mac_control */ +#define cmd_act_mac_rx_on 0x0001 +#define cmd_act_mac_tx_on 0x0002 +#define cmd_act_mac_loopback_on 0x0004 +#define cmd_act_mac_wep_enable 0x0008 +#define cmd_act_mac_int_enable 0x0010 +#define cmd_act_mac_multicast_enable 0x0020 +#define cmd_act_mac_broadcast_enable 0x0040 +#define cmd_act_mac_promiscuous_enable 0x0080 +#define cmd_act_mac_all_multicast_enable 0x0100 +#define cmd_act_mac_strict_protection_enable 0x0400 + +/* Define action or option for cmd_802_11_radio_control */ +#define cmd_type_auto_preamble 0x0001 +#define cmd_type_short_preamble 0x0002 +#define cmd_type_long_preamble 0x0003 + +#define TURN_ON_RF 0x01 +#define RADIO_ON 0x01 +#define RADIO_OFF 0x00 + +#define SET_AUTO_PREAMBLE 0x05 +#define SET_SHORT_PREAMBLE 0x03 +#define SET_LONG_PREAMBLE 0x01 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define cmd_opt_802_11_rf_channel_get 0x00 +#define cmd_opt_802_11_rf_channel_set 0x01 + +/* Define action or option for cmd_802_11_rf_tx_power */ +#define cmd_act_tx_power_opt_get 0x0000 +#define cmd_act_tx_power_opt_set_high 0x8007 +#define cmd_act_tx_power_opt_set_mid 0x8004 +#define cmd_act_tx_power_opt_set_low 0x8000 + +#define cmd_act_tx_power_index_high 0x0007 +#define cmd_act_tx_power_index_mid 0x0004 +#define cmd_act_tx_power_index_low 0x0000 + +/* Define action or option for cmd_802_11_data_rate */ +#define cmd_act_set_tx_auto 0x0000 +#define cmd_act_set_tx_fix_rate 0x0001 +#define cmd_act_get_tx_rate 0x0002 + +#define cmd_act_set_rx 0x0001 +#define cmd_act_set_tx 0x0002 +#define cmd_act_set_both 0x0003 +#define cmd_act_get_rx 0x0004 +#define cmd_act_get_tx 0x0008 +#define cmd_act_get_both 0x000c + +/* Define action or option for cmd_802_11_ps_mode */ +#define cmd_type_cam 0x0000 +#define cmd_type_max_psp 0x0001 +#define cmd_type_fast_psp 0x0002 + +/* Define action or option for cmd_bt_access */ +enum cmd_bt_access_opts { + /* The bt commands start at 5 instead of 1 because the old dft commands + * are mapped to 1-4. These old commands are no longer maintained and + * should not be called. + */ + cmd_act_bt_access_add = 5, + cmd_act_bt_access_del, + cmd_act_bt_access_list, + cmd_act_bt_access_reset +}; + +/* Define action or option for cmd_fwt_access */ +enum cmd_fwt_access_opts { + cmd_act_fwt_access_add = 1, + cmd_act_fwt_access_del, + cmd_act_fwt_access_lookup, + cmd_act_fwt_access_list, + cmd_act_fwt_access_list_route, + cmd_act_fwt_access_list_neighbor, + cmd_act_fwt_access_reset, + cmd_act_fwt_access_cleanup, + cmd_act_fwt_access_time, +}; + +/* Define action or option for cmd_mesh_access */ +enum cmd_mesh_access_opts { + cmd_act_mesh_get_ttl = 1, + cmd_act_mesh_set_ttl, + cmd_act_mesh_get_stats, + cmd_act_mesh_get_mpp, + cmd_act_mesh_set_mpp, +}; + +/** Card Event definition */ +#define MACREG_INT_CODE_TX_PPA_FREE 0x00000000 +#define MACREG_INT_CODE_TX_DMA_DONE 0x00000001 +#define MACREG_INT_CODE_LINK_LOSE_W_SCAN 0x00000002 +#define MACREG_INT_CODE_LINK_LOSE_NO_SCAN 0x00000003 +#define MACREG_INT_CODE_LINK_SENSED 0x00000004 +#define MACREG_INT_CODE_CMD_FINISHED 0x00000005 +#define MACREG_INT_CODE_MIB_CHANGED 0x00000006 +#define MACREG_INT_CODE_INIT_DONE 0x00000007 +#define MACREG_INT_CODE_DEAUTHENTICATED 0x00000008 +#define MACREG_INT_CODE_DISASSOCIATED 0x00000009 +#define MACREG_INT_CODE_PS_AWAKE 0x0000000a +#define MACREG_INT_CODE_PS_SLEEP 0x0000000b +#define MACREG_INT_CODE_MIC_ERR_MULTICAST 0x0000000d +#define MACREG_INT_CODE_MIC_ERR_UNICAST 0x0000000e +#define MACREG_INT_CODE_WM_AWAKE 0x0000000f +#define MACREG_INT_CODE_ADHOC_BCN_LOST 0x00000011 +#define MACREG_INT_CODE_RSSI_LOW 0x00000019 +#define MACREG_INT_CODE_SNR_LOW 0x0000001a +#define MACREG_INT_CODE_MAX_FAIL 0x0000001b +#define MACREG_INT_CODE_RSSI_HIGH 0x0000001c +#define MACREG_INT_CODE_SNR_HIGH 0x0000001d + +#endif /* _HOST_H_ */ diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h new file mode 100644 index 0000000..f239e5d --- /dev/null +++ b/drivers/net/wireless/libertas/hostcmd.h @@ -0,0 +1,693 @@ +/* + * This file contains the function prototypes, data structure + * and defines for all the host/station commands + */ +#ifndef __HOSTCMD__H +#define __HOSTCMD__H + +#include +#include "11d.h" +#include "types.h" + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* Current Tx packet status */ + u32 tx_status; + /* Tx control */ + u32 tx_control; + u32 tx_packet_location; + /* Tx packet length */ + u16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Amount of time the packet has been queued in the driver (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +}; + +/* RxPD Descriptor */ +struct rxpd { + /* Current Rx packet status */ + u16 status; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + u16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + u32 pkt_ptr; + + /* Next Rx RxPD addr */ + u32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +}; + +struct cmd_ctrl_node { + /* CMD link list */ + struct list_head list; + u32 status; + /* CMD ID */ + u32 cmd_oid; + /*CMD wait option: wait for finish or no wait */ + u16 wait_option; + /* command parameter */ + void *pdata_buf; + /*command data */ + u8 *bufvirtualaddr; + u16 cmdflags; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + +/* WLAN_802_11_KEY + * + * Generic structure to hold all key types. key type (WEP40, WEP104, TKIP, AES) + * is determined from the keylength field. + */ +struct WLAN_802_11_KEY { + u32 len; + u32 flags; /* KEY_INFO_* from wlan_defs.h */ + u8 key[MRVL_MAX_KEY_WPA_KEY_LENGTH]; + u16 type; /* KEY_TYPE_* from wlan_defs.h */ +}; + +struct IE_WPA { + u8 elementid; + u8 len; + u8 oui[4]; + u16 version; +}; + +struct WLAN_802_11_SSID { + /* SSID length */ + u32 ssidlength; + + /* SSID information field */ + u8 ssid[IW_ESSID_MAX_SIZE]; +}; + +struct WPA_SUPPLICANT { + u8 wpa_ie[256]; + u8 wpa_ie_len; +}; + +/* wlan_offset_value */ +struct wlan_offset_value { + u32 offset; + u32 value; +}; + +struct WLAN_802_11_FIXED_IEs { + u8 timestamp[8]; + u16 beaconinterval; + u16 capabilities; +}; + +struct WLAN_802_11_VARIABLE_IEs { + u8 elementid; + u8 length; + u8 data[1]; +}; + +/* Define general data structure */ +/* cmd_DS_GEN */ +struct cmd_ds_gen { + u16 command; + u16 size; + u16 seqnum; + u16 result; +}; + +#define S_DS_GEN sizeof(struct cmd_ds_gen) +/* + * Define data structure for cmd_get_hw_spec + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + /* HW Interface version number */ + u16 hwifversion; + /* HW version number */ + u16 version; + /* Max number of TxPD FW can handle */ + u16 nr_txpd; + /* Max no of Multicast address */ + u16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + u16 regioncode; + + /* Number of antenna used */ + u16 nr_antenna; + + /* FW release number, example 0x1234=1.2.3.4 */ + u32 fwreleasenumber; + + /* Base Address of TxPD queue */ + u32 wcb_base; + /* Read Pointer of RxPd queue */ + u32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + u32 rxpd_wrptr; + + /*FW/HW capability */ + u32 fwcapinfo; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_reset { + u16 action; +}; + +struct cmd_ds_802_11_subscribe_event { + u16 action; + u16 events; +}; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for cmd_802_11_scan + */ +struct cmd_ds_802_11_scan { + u8 bsstype; + u8 BSSID[ETH_ALEN]; + u8 tlvbuffer[1]; +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_chanlistparamset_t ChanListParamSet; + mrvlietypes_ratesparamset_t OpRateSet; +#endif +}; + +struct cmd_ds_802_11_scan_rsp { + u16 bssdescriptsize; + u8 nr_sets; + u8 bssdesc_and_tlvbuffer[1]; +}; + +struct cmd_ds_802_11_get_log { + u32 mcasttxframe; + u32 failed; + u32 retry; + u32 multiretry; + u32 framedup; + u32 rtssuccess; + u32 rtsfailure; + u32 ackfailure; + u32 rxfrag; + u32 mcastrxframe; + u32 fcserror; + u32 txframe; + u32 wepundecryptable; +}; + +struct cmd_ds_mac_control { + u16 action; + u16 reserved; +}; + +struct cmd_ds_mac_multicast_adr { + u16 action; + u16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +}; + +struct cmd_ds_802_11_authenticate { + u8 macaddr[ETH_ALEN]; + u8 authtype; + u8 reserved[10]; +}; + +struct cmd_ds_802_11_deauthenticate { + u8 macaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate { + u8 peerstaaddr[6]; + struct ieeetypes_capinfo capinfo; + u16 listeninterval; + u16 bcnperiod; + u8 dtimperiod; + +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_phyparamset_t phyparamset; + mrvlietypes_ssparamset_t ssparamset; + mrvlietypes_ratesparamset_t ratesParamSet; +#endif +} __attribute__ ((packed)); + +struct cmd_ds_802_11_disassociate { + u8 destmacaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate_rsp { + struct ieeetypes_assocrsp assocRsp; +}; + +struct cmd_ds_802_11_ad_hoc_result { + u8 PAD[3]; + u8 BSSID[ETH_ALEN]; +}; + +struct cmd_ds_802_11_set_wep { + /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ + u16 action; + + /* key Index selected for Tx */ + u16 keyindex; + + /* 40, 128bit or TXWEP */ + u8 keytype[4]; + u8 keymaterial[4][16]; +}; + +struct cmd_ds_802_3_get_stat { + u32 xmitok; + u32 rcvok; + u32 xmiterror; + u32 rcverror; + u32 rcvnobuffer; + u32 rcvcrcerror; +}; + +struct cmd_ds_802_11_get_stat { + u32 txfragmentcnt; + u32 mcasttxframecnt; + u32 failedcnt; + u32 retrycnt; + u32 Multipleretrycnt; + u32 rtssuccesscnt; + u32 rtsfailurecnt; + u32 ackfailurecnt; + u32 frameduplicatecnt; + u32 rxfragmentcnt; + u32 mcastrxframecnt; + u32 fcserrorcnt; + u32 bcasttxframecnt; + u32 bcastrxframecnt; + u32 txbeacon; + u32 rxbeacon; + u32 wepundecryptable; +}; + +struct cmd_ds_802_11_snmp_mib { + u16 querytype; + u16 oid; + u16 bufsize; + u8 value[128]; +}; + +struct cmd_ds_mac_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_bbp_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_rf_reg_map { + u16 buffersize; + u8 regmap[64]; + u16 reserved; +}; + +struct cmd_ds_mac_reg_access { + u16 action; + u16 offset; + u32 value; +}; + +struct cmd_ds_bbp_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_rf_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_802_11_radio_control { + u16 action; + u16 control; +}; + +struct cmd_ds_802_11_sleep_params { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Sleep clock error in ppm */ + u16 error; + + /* Wakeup offset in usec */ + u16 offset; + + /* Clock stabilization time in usec */ + u16 stabletime; + + /* control periodic calibration */ + u8 calcontrol; + + /* control the use of external sleep clock */ + u8 externalsleepclk; + + /* reserved field, should be set to zero */ + u16 reserved; +}; + +struct cmd_ds_802_11_inactivity_timeout { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Inactivity timeout in msec */ + u16 timeout; +}; + +struct cmd_ds_802_11_rf_channel { + u16 action; + u16 currentchannel; + u16 rftype; + u16 reserved; + u8 channellist[32]; +}; + +struct cmd_ds_802_11_rssi { + /* weighting factor */ + u16 N; + + u16 reserved_0; + u16 reserved_1; + u16 reserved_2; +}; + +struct cmd_ds_802_11_rssi_rsp { + u16 SNR; + u16 noisefloor; + u16 avgSNR; + u16 avgnoisefloor; +}; + +struct cmd_ds_802_11_mac_address { + u16 action; + u8 macadd[ETH_ALEN]; +}; + +struct cmd_ds_802_11_rf_tx_power { + u16 action; + u16 currentlevel; +}; + +struct cmd_ds_802_11_rf_antenna { + u16 action; + + /* Number of antennas or 0xffff(diversity) */ + u16 antennamode; + +}; + +struct cmd_ds_802_11_ps_mode { + u16 action; + u16 nullpktinterval; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct PS_CMD_ConfirmSleep { + u16 command; + u16 size; + u16 seqnum; + u16 result; + + u16 action; + u16 reserved1; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct cmd_ds_802_11_data_rate { + u16 action; + u16 reserverd; + u8 datarate[G_SUPPORTED_RATES]; +}; + +struct cmd_ds_802_11_rate_adapt_rateset { + u16 action; + u16 enablehwauto; + u16 bitmap; +}; + +struct cmd_ds_802_11_ad_hoc_start { + u8 SSID[IW_ESSID_MAX_SIZE]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + union IEEEtypes_ssparamset ssparamset; + union ieeetypes_phyparamset phyparamset; + u16 probedelay; + struct ieeetypes_capinfo cap; + u8 datarate[G_SUPPORTED_RATES]; + u8 tlv_memory_size_pad[100]; +} __attribute__ ((packed)); + +struct adhoc_bssdesc { + u8 BSSID[6]; + u8 SSID[32]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + u8 timestamp[8]; + u8 localtime[8]; + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[G_SUPPORTED_RATES]; + + /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the + * Adhoc join command and will cause a binary layout mismatch with + * the firmware + */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_join { + struct adhoc_bssdesc bssdescriptor; + u16 failtimeout; + u16 probedelay; + +} __attribute__ ((packed)); + +struct cmd_ds_802_11_enable_rsn { + u16 action; + u16 enable; +}; + +struct MrvlIEtype_keyParamSet { + /* type ID */ + u16 type; + + /* length of Payload */ + u16 length; + + /* type of key: WEP=0, TKIP=1, AES=2 */ + u16 keytypeid; + + /* key control Info specific to a keytypeid */ + u16 keyinfo; + + /* length of key */ + u16 keylen; + + /* key material of size keylen */ + u8 key[32]; +}; + +struct cmd_ds_802_11_key_material { + u16 action; + struct MrvlIEtype_keyParamSet keyParamSet[2]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_eeprom_access { + u16 action; + + /* multiple 4 */ + u16 offset; + u16 bytecount; + u8 value; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_tpc_cfg { + u16 action; + u8 enable; + s8 P0; + s8 P1; + s8 P2; + u8 usesnr; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_led_ctrl { + u16 action; + u16 numled; + u8 data[256]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_pwr_cfg { + u16 action; + u8 enable; + s8 PA_P0; + s8 PA_P1; + s8 PA_P2; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_afc { + u16 afc_auto; + union { + struct { + u16 threshold; + u16 period; + }; + struct { + s16 timing_offset; + s16 carrier_offset; + }; + }; +} __attribute__ ((packed)); + +struct cmd_tx_rate_query { + u16 txrate; +} __attribute__ ((packed)); + +struct cmd_ds_get_tsf { + __le64 tsfvalue; +} __attribute__ ((packed)); + +struct cmd_ds_bt_access { + u16 action; + u32 id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; +} __attribute__ ((packed)); + +struct cmd_ds_fwt_access { + u16 action; + u32 id; + u8 da[ETH_ALEN]; + u8 dir; + u8 ra[ETH_ALEN]; + u32 ssn; + u32 dsn; + u32 metric; + u8 hopcount; + u8 ttl; + u32 expiration; + u8 sleepmode; + u32 snr; + u32 references; +} __attribute__ ((packed)); + +#define MESH_STATS_NUM 7 +struct cmd_ds_mesh_access { + u16 action; + u32 data[MESH_STATS_NUM + 1]; /* last position reserved */ +} __attribute__ ((packed)); + +struct cmd_ds_command { + /* command header */ + u16 command; + u16 size; + u16 seqnum; + u16 result; + + /* command Body */ + union { + struct cmd_ds_get_hw_spec hwspec; + struct cmd_ds_802_11_ps_mode psmode; + struct cmd_ds_802_11_scan scan; + struct cmd_ds_802_11_scan_rsp scanresp; + struct cmd_ds_mac_control macctrl; + struct cmd_ds_802_11_associate associate; + struct cmd_ds_802_11_deauthenticate deauth; + struct cmd_ds_802_11_set_wep wep; + struct cmd_ds_802_11_ad_hoc_start ads; + struct cmd_ds_802_11_reset reset; + struct cmd_ds_802_11_ad_hoc_result result; + struct cmd_ds_802_11_get_log glog; + struct cmd_ds_802_11_authenticate auth; + struct cmd_ds_802_11_get_stat gstat; + struct cmd_ds_802_3_get_stat gstat_8023; + struct cmd_ds_802_11_snmp_mib smib; + struct cmd_ds_802_11_rf_tx_power txp; + struct cmd_ds_802_11_rf_antenna rant; + struct cmd_ds_802_11_data_rate drate; + struct cmd_ds_802_11_rate_adapt_rateset rateset; + struct cmd_ds_mac_multicast_adr madr; + struct cmd_ds_802_11_ad_hoc_join adj; + struct cmd_ds_802_11_radio_control radio; + struct cmd_ds_802_11_rf_channel rfchannel; + struct cmd_ds_802_11_rssi rssi; + struct cmd_ds_802_11_rssi_rsp rssirsp; + struct cmd_ds_802_11_disassociate dassociate; + struct cmd_ds_802_11_mac_address macadd; + struct cmd_ds_802_11_enable_rsn enbrsn; + struct cmd_ds_802_11_key_material keymaterial; + struct cmd_ds_mac_reg_access macreg; + struct cmd_ds_bbp_reg_access bbpreg; + struct cmd_ds_rf_reg_access rfreg; + struct cmd_ds_802_11_eeprom_access rdeeprom; + + struct cmd_ds_802_11d_domain_info domaininfo; + struct cmd_ds_802_11d_domain_info domaininforesp; + + struct cmd_ds_802_11_sleep_params sleep_params; + struct cmd_ds_802_11_inactivity_timeout inactivity_timeout; + struct cmd_ds_802_11_tpc_cfg tpccfg; + struct cmd_ds_802_11_pwr_cfg pwrcfg; + struct cmd_ds_802_11_afc afc; + struct cmd_ds_802_11_led_ctrl ledgpio; + + struct cmd_tx_rate_query txrate; + struct cmd_ds_bt_access bt; + struct cmd_ds_fwt_access fwt; + struct cmd_ds_mesh_access mesh; + struct cmd_ds_get_tsf gettsf; + struct cmd_ds_802_11_subscribe_event subscribe_event; + } params; +} __attribute__ ((packed)); + +#endif diff --git a/drivers/net/wireless/libertas/if_bootcmd.c b/drivers/net/wireless/libertas/if_bootcmd.c new file mode 100644 index 0000000..567000c --- /dev/null +++ b/drivers/net/wireless/libertas/if_bootcmd.c @@ -0,0 +1,38 @@ +/** + * This file contains functions used in USB Boot command + * and Boot2/FW update + */ + +#include +#include +#include +#include + +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +/** + * @brief This function issues Boot command to the Boot2 code + * @param ivalue 1:Boot from FW by USB-Download + * 2:Boot from FW in EEPROM + * @return 0 + */ +int if_usb_issue_boot_command(wlan_private *priv, int ivalue) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct bootcmdstr sbootcmd; + int i; + + /* Prepare command */ + sbootcmd.u32magicnumber = BOOT_CMD_MAGIC_NUMBER; + sbootcmd.u8cmd_tag = ivalue; + for (i=0; i<11; i++) + sbootcmd.au8dumy[i]=0x00; + memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr)); + + /* Issue command */ + usb_tx_block(priv, cardp->bulk_out_buffer, sizeof(struct bootcmdstr)); + + return 0; +} diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c new file mode 100644 index 0000000..695fb6a --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.c @@ -0,0 +1,952 @@ +/** + * This file contains functions used in USB interface module. + */ +#include +#include +#include +#include + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +#define MESSAGE_HEADER_LEN 4 + +static const char usbdriver_name[] = "usb8xxx"; + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { + USB_DEVICE(USB8388_VID_1, USB8388_PID_1), + }, + { + USB_DEVICE(USB8388_VID_2, USB8388_PID_2), + }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); + +/** + * @brief call back function to handle the status of the URB + * @param urb pointer to urb structure + * @return N/A + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + wlan_private *priv = (wlan_private *) (urb->context); + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + /* handle the transmission complete validations */ + + if (urb->status != 0) { + /* print the failure status number for debug */ + lbs_pr_info("URB in failure status\n"); + } else { + lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n"); + lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n", + urb->actual_length); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + /* Wake main thread if commands are pending */ + if (!adapter->cur_cmd) + wake_up_interruptible(&priv->mainthread.waitq); + if ((adapter->connect_status == libertas_connected)) + netif_wake_queue(dev); + } + + return; +} + +/** + * @brief free tx/rx urb, skb and rx buffer + * @param cardp pointer usb_card_rec + * @return N/A + */ +void if_usb_free(struct usb_card_rec *cardp) +{ + ENTER(); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + kfree(cardp->bulk_out_buffer); + cardp->bulk_out_buffer = NULL; + + LEAVE(); + return; +} + +/** + * @brief sets the configuration values + * @param ifnum interface number + * @param id pointer to usb_device_id + * @return 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + wlan_private *pwlanpriv; + struct usb_card_rec *usb_cardp; + int i; + + udev = interface_to_usbdev(intf); + + usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + if (!usb_cardp) { + lbs_pr_err("Out of memory allocating private data.\n"); + goto error; + } + + usb_cardp->udev = udev; + iface_desc = intf->cur_altsetting; + + lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + udev->descriptor.bcdUSB, + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk in endpoint */ + lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n", + endpoint->wMaxPacketSize); + if (! + (usb_cardp->rx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1, &udev->dev, + "Rx URB allocation failed\n"); + goto dealloc; + } + usb_cardp->rx_urb_recall = 0; + + usb_cardp->bulk_in_size = + endpoint->wMaxPacketSize; + usb_cardp->bulk_in_endpointAddr = + (endpoint-> + bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n", + endpoint->bEndpointAddress); + } + + if (((endpoint-> + bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + USB_DIR_OUT) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* We found bulk out endpoint */ + if (! + (usb_cardp->tx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1,&udev->dev, + "Tx URB allocation failed\n"); + goto dealloc; + } + + usb_cardp->bulk_out_size = + endpoint->wMaxPacketSize; + lbs_dev_dbg(1, &udev->dev, + "Bulk out size is %d\n", + endpoint->wMaxPacketSize); + usb_cardp->bulk_out_endpointAddr = + endpoint->bEndpointAddress; + lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n", + endpoint->bEndpointAddress); + usb_cardp->bulk_out_buffer = + kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, + GFP_KERNEL); + + if (!usb_cardp->bulk_out_buffer) { + lbs_dev_dbg(1, &udev->dev, + "Could not allocate buffer\n"); + goto dealloc; + } + } + } + + + /* At this point wlan_add_card() will be called. Don't worry + * about keeping pwlanpriv around since it will be set on our + * usb device data in -> add() -> libertas_sbi_register_dev(). + */ + if (!(pwlanpriv = wlan_add_card(usb_cardp))) + goto dealloc; + + usb_get_dev(udev); + usb_set_intfdata(intf, usb_cardp); + + /* + * return card structure, which can be got back in the + * diconnect function as the ptr + * argument. + */ + return 0; + +dealloc: + if_usb_free(usb_cardp); + +error: + return -ENOMEM; +} + +/** + * @brief free resource and cleanup + * @param udev pointer to usb_device + * @param ptr pointer to usb_cardp + * @return N/A + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = (wlan_private *) cardp->priv; + wlan_adapter *adapter = NULL; + + adapter = priv->adapter; + + /* + * Update Surprise removed to TRUE + */ + adapter->surpriseremoved = 1; + + /* card is removed and we can call wlan_remove_card */ + lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n"); + wlan_remove_card(cardp); + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + + return; +} + +/** + * @brief This function download FW + * @param priv pointer to wlan_private + * @return 0 + */ +static int if_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct FWData *fwdata; + struct fwheader *fwheader; + u8 *firmware = priv->firmware->data; + + fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC); + + if (!fwdata) + return -1; + + fwheader = &fwdata->fwheader; + + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum = cardp->lastseqnum - 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n", + cardp->totalbytes); + + memcpy(fwheader, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n"); + memcpy(fwdata->data, &firmware[cardp->totalbytes], + fwdata->fwheader.datalength); + + lbs_dev_dbg(2, &cardp->udev->dev, + "Data length = %d\n", fwdata->fwheader.datalength); + + cardp->fwseqnum = cardp->fwseqnum + 1; + + fwdata->seqnum = cardp->fwseqnum; + cardp->lastseqnum = fwdata->seqnum; + cardp->totalbytes += fwdata->fwheader.datalength; + + if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) { + lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, + cardp->totalbytes); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + + } else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) { + lbs_dev_dbg(2, &cardp->udev->dev, + "Host has finished FW downloading\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "Donwloading FW JUMP BLOCK\n"); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + cardp->fwfinalblk = 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, + "The firmware download is done size is %d\n", + cardp->totalbytes); + + kfree(fwdata); + + return 0; +} + +static int libertas_do_reset(wlan_private *priv) +{ + int ret; + struct usb_card_rec *cardp = priv->wlan_dev.card; + + ret = usb_reset_device(cardp->udev); + if (!ret) { + msleep(10); + reset_device(priv); + msleep(10); + } + return ret; +} + +/** + * @brief This function transfer the data to the device. + * @param priv pointer to wlan_private + * @param payload pointer to payload data + * @param nb data length + * @return 0 or -1 + */ +int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb) +{ + /* pointer to card structure */ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int ret = -1; + + /* check if device is removed */ + if (priv->adapter->surpriseremoved) { + lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n"); + goto tx_ret; + } + + usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->bulk_out_endpointAddr), + payload, nb, if_usb_write_bulk_callback, priv); + + cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { + /* transfer failed */ + lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n"); + ret = 0; + } + +tx_ret: + return ret; +} + +static int __if_usb_submit_rx_urb(wlan_private * priv, + void (*callbackfn) + (struct urb *urb)) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct sk_buff *skb; + struct read_cb_info *rinfo = &cardp->rinfo; + int ret = -1; + + if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { + lbs_pr_err("No free skb\n"); + goto rx_ret; + } + + rinfo->skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, + cardp->bulk_in_endpointAddr), + skb->tail + IPFIELD_ALIGN_OFFSET, + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, + rinfo); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); + if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { + /* handle failure conditions */ + lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n"); + ret = 0; + } + +rx_ret: + return ret; +} + +static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload); +} + +static inline int if_usb_submit_rx_urb(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + struct fwsyncheader *syncfwheader; + struct bootcmdrespStr bootcmdresp; + + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed during fw load\n"); + kfree_skb(skb); + return; + } + + if (cardp->bootcmdresp == 0) { + memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(bootcmdresp)); + if (cardp->udev->descriptor.bcdDevice < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + return; + } + if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) { + lbs_pr_info( + "boot cmd response wrong magic number (0x%x)\n", + bootcmdresp.u32magicnumber); + } else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) { + lbs_pr_info( + "boot cmd response cmd_tag error (%d)\n", + bootcmdresp.u8cmd_tag); + } else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) { + lbs_pr_info( + "boot cmd response result error (%d)\n", + bootcmdresp.u8result); + } else { + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + } + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + return; + } + + syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); + if (!syncfwheader) { + lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n"); + kfree_skb(skb); + return; + } + + memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(struct fwsyncheader)); + + if (!syncfwheader->cmd) { + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk with correct CRC\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk seqnum = %d\n", + syncfwheader->seqnum); + cardp->CRC_OK = 1; + } else { + lbs_dev_dbg(1, &cardp->udev->dev, + "FW received Blk with CRC error\n"); + cardp->CRC_OK = 0; + } + + kfree_skb(skb); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_prog_firmware(priv); + + if_usb_submit_rx_urb_fwload(priv); +exit: + kfree(syncfwheader); + + return; + +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + + MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { + lbs_dev_dbg(1, &cardp->udev->dev, + "Packet length is Invalid\n"); + kfree_skb(skb); + return; + } + + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + libertas_process_rxed_packet(priv, skb); + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); +} + +static inline void process_cmdrequest(int recvlength, u8 *recvbuff, + struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + u8 *cmdbuf; + if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) { + lbs_dev_dbg(1, &cardp->udev->dev, + "The receive buffer is too large\n"); + kfree_skb(skb); + return; + } + + if (!in_interrupt()) + BUG(); + + spin_lock(&priv->adapter->driver_lock); + /* take care of cur_cmd = NULL case by reading the + * data to clear the interrupt */ + if (!priv->adapter->cur_cmd) { + cmdbuf = priv->wlan_dev.upld_buf; + priv->adapter->hisregcpy &= ~his_cmdupldrdy; + } else + cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr; + + cardp->usb_int_cause |= his_cmdupldrdy; + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); + memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN, + priv->wlan_dev.upld_len); + + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + + lbs_dev_dbg(1, &cardp->udev->dev, + "Wake up main thread to handle cmd response\n"); + + return; +} + +/** + * @brief This function reads of the packet into the upload buff, + * wake up the main thread and initialise the Rx callack. + * + * @param urb pointer to struct urb + * @return N/A + */ +static void if_usb_receive(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + int recvlength = urb->actual_length; + u8 *recvbuff = NULL; + u32 recvtype; + + ENTER(); + + if (recvlength) { + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed\n"); + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; + memcpy(&recvtype, recvbuff, sizeof(u32)); + lbs_dev_dbg(1, &cardp->udev->dev, + "Recv length = 0x%x\n", recvlength); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type = 0x%X\n", recvtype); + recvtype = le32_to_cpu(recvtype); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type after = 0x%X\n", recvtype); + } else if (urb->status) + goto rx_exit; + + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + /* Event cause handling */ + spin_lock(&priv->adapter->driver_lock); + cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN); + lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n", + cardp->usb_event_cause); + if (cardp->usb_event_cause & 0xffff0000) { + libertas_send_tx_feedback(priv); + break; + } + cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3; + cardp->usb_int_cause |= his_cardevent; + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + goto rx_exit; + default: + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(priv); +rx_exit: + LEAVE(); + return; +} + +/** + * @brief This function downloads data to FW + * @param priv pointer to wlan_private structure + * @param type type of data + * @param buf pointer to data buffer + * @param len number of bytes + * @return 0 or -1 + */ +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb) +{ + int ret = -1; + u32 tmp; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type); + lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb); + + if (type == MVMS_CMD) { + tmp = cpu_to_le32(CMD_TYPE_REQUEST); + priv->wlan_dev.dnld_sent = DNLD_CMD_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + + } else { + tmp = cpu_to_le32(CMD_TYPE_DATA); + priv->wlan_dev.dnld_sent = DNLD_DATA_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + } + + memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb); + + ret = + usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN); + + return ret; +} + +/* called with adapter->driver_lock held */ +int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + + *ireg = cardp->usb_int_cause; + cardp->usb_int_cause = 0; + + lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg); + + return 0; +} + +int libertas_sbi_read_event_cause(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + priv->adapter->eventcause = cardp->usb_event_cause; + /* Re-submit rx urb here to avoid event lost issue */ + if_usb_submit_rx_urb(priv); + return 0; +} + +int reset_device(wlan_private *priv) +{ + int ret; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset, + cmd_act_halt, 0, 0, NULL); + msleep_interruptible(10); + + return ret; +} + +int libertas_sbi_unregister_dev(wlan_private * priv) +{ + int ret = 0; + + /* Need to send a Reset command to device before USB resources freed + * and wlan_remove_card() called, then device can handle FW download + * again. + */ + if (priv) + reset_device(priv); + + return ret; +} + + +/** + * @brief This function register usb device and initialize parameter + * @param priv pointer to wlan_private + * @return 0 or -1 + */ +int libertas_sbi_register_dev(wlan_private * priv) +{ + + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + ENTER(); + + cardp->priv = priv; + cardp->eth_dev = priv->wlan_dev.netdev; + priv->hotplug_device = &(cardp->udev->dev); + + SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev)); + + lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n", + cardp->udev); + + LEAVE(); + return 0; +} + + + +int libertas_sbi_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int i = 0; + static int reset_count = 10; + + ENTER(); + + cardp->rinfo.priv = priv; + +restart: + if (if_usb_submit_rx_urb_fwload(priv) < 0) { + lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n"); + LEAVE(); + return -1; + } + +#ifdef SUPPORT_BOOT_COMMAND + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + /* Issue Boot command = 1, Boot from Download-FW */ + if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp == 0) { + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + return -1; + } +#endif + + i = 0; + priv->adapter->fw_ready = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + if_prog_firmware(priv); + + do { + lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n"); + i++; + msleep_interruptible(100); + if (priv->adapter->surpriseremoved || i >= 20) + break; + } while (!cardp->fwdnldover); + + if (!cardp->fwdnldover) { + lbs_pr_info("failed to load fw, resetting device!\n"); + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + + lbs_pr_info("FW download failure, time = %d ms\n", i * 100); + LEAVE(); + return -1; + } + + if_usb_submit_rx_urb(priv); + + /* Delay 200 ms to waiting for the FW ready */ + msleep_interruptible(200); + + priv->adapter->fw_ready = 1; + + LEAVE(); + return 0; +} + +/** + * @brief Given a usb_card_rec return its wlan_private + * @param card pointer to a usb_card_rec + * @return pointer to wlan_private + */ +wlan_private *libertas_sbi_get_priv(void *card) +{ + struct usb_card_rec *cardp = card; + return cardp->priv; +} + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private * priv) +{ + return 0; +} + +int libertas_sbi_resume(wlan_private * priv) +{ + return 0; +} +#endif + +#ifdef CONFIG_PM +static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = cardp->priv; + + ENTER(); + + if (priv->adapter->psstate != PS_STATE_FULL_POWER) + return -1; + + netif_device_detach(cardp->eth_dev); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + cardp->rx_urb_recall = 1; + + LEAVE(); + return 0; +} + +static int if_usb_resume(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + + ENTER(); + + cardp->rx_urb_recall = 0; + + if_usb_submit_rx_urb(cardp->priv); + + netif_device_attach(cardp->eth_dev); + + LEAVE(); + return 0; +} +#else +#define if_usb_suspend NULL +#define if_usb_resume NULL +#endif + +static struct usb_driver if_usb_driver = { + /* driver name */ + .name = usbdriver_name, + /* probe function name */ + .probe = if_usb_probe, + /* disconnect function name */ + .disconnect = if_usb_disconnect, + /* device signature table */ + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, +}; + +/** + * @brief This function registers driver. + * @param add pointer to add_card callback function + * @param remove pointer to remove card callback function + * @param arg pointer to call back function parameter + * @return dummy success variable + */ +int libertas_sbi_register(void) +{ + /* + * API registers the Marvell USB driver + * to the USB system + */ + usb_register(&if_usb_driver); + + /* Return success to wlan layer */ + return 0; +} + +/** + * @brief This function removes usb driver. + * @return N/A + */ +void libertas_sbi_unregister(void) +{ + /* API unregisters the driver from USB subsystem */ + usb_deregister(&if_usb_driver); + return; +} diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h new file mode 100644 index 0000000..7851167 --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.h @@ -0,0 +1,109 @@ +/** + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define IPFIELD_ALIGN_OFFSET 2 + +#define USB8388_VID_1 0x1286 +#define USB8388_PID_1 0x2001 +#define USB8388_VID_2 0x05a3 +#define USB8388_PID_2 0x8388 + +#ifdef SUPPORT_BOOT_COMMAND +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */ + +struct bootcmdstr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 au8dumy[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 + +struct bootcmdrespStr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 u8result; + u8 au8dumy[2]; +}; +#endif /* SUPPORT_BOOT_COMMAND */ + +/* read callback private data */ +struct read_cb_info { + wlan_private *priv; + struct sk_buff *skb; +}; + +/** USB card description structure*/ +struct usb_card_rec { + struct net_device *eth_dev; + struct usb_device *udev; + struct urb *rx_urb, *tx_urb; + void *priv; + struct read_cb_info rinfo; + + int bulk_in_size; + u8 bulk_in_endpointAddr; + + u8 *bulk_out_buffer; + int bulk_out_size; + u8 bulk_out_endpointAddr; + + u8 CRC_OK; + u32 fwseqnum; + u32 lastseqnum; + u32 totalbytes; + u32 fwlastblksent; + u8 fwdnldover; + u8 fwfinalblk; + + u32 usb_event_cause; + u8 usb_int_cause; + + u8 rx_urb_recall; + + u8 bootcmdresp; +}; + +/** fwheader */ +struct fwheader { + u32 dnldcmd; + u32 baseaddr; + u32 datalength; + u32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/** FWData */ +struct FWData { + struct fwheader fwheader; + u32 seqnum; + u8 data[FW_MAX_DATA_BLK_SIZE]; +}; + +/** fwsyncheader */ +struct fwsyncheader { + u32 cmd; + u32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 + +#define FW_DATA_XMIT_SIZE \ + sizeof(struct fwheader) + fwdata->fwheader.datalength + sizeof(u32) + +int usb_tx_block(wlan_private *priv, u8 *payload, u16 nb); +void if_usb_free(struct usb_card_rec *cardp); +int if_usb_issue_boot_command(wlan_private *priv, int ivalue); + diff --git a/drivers/net/wireless/libertas/ioctl.c b/drivers/net/wireless/libertas/ioctl.c new file mode 100644 index 0000000..82b3964 --- /dev/null +++ b/drivers/net/wireless/libertas/ioctl.c @@ -0,0 +1,2500 @@ +/** + * This file contains ioctl functions + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN + \ + IW_ESSID_MAX_SIZE + \ + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \ + IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \ + IW_EV_PARAM_LEN + 40) /* 40 for WPAIE */ + +#define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ) + +static int setrxantenna(wlan_private * priv, int mode) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if (mode != RF_ANTENNA_1 && mode != RF_ANTENNA_2 + && mode != RF_ANTENNA_AUTO) { + return -EINVAL; + } + + adapter->rxantennamode = mode; + + lbs_pr_debug(1, "SET RX Antenna mode to 0x%04x\n", adapter->rxantennamode); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_set_rx, + cmd_option_waitforrsp, 0, + &adapter->rxantennamode); + return ret; +} + +static int settxantenna(wlan_private * priv, int mode) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if ((mode != RF_ANTENNA_1) && (mode != RF_ANTENNA_2) + && (mode != RF_ANTENNA_AUTO)) { + return -EINVAL; + } + + adapter->txantennamode = mode; + + lbs_pr_debug(1, "SET TX Antenna mode to 0x%04x\n", adapter->txantennamode); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_set_tx, + cmd_option_waitforrsp, 0, + &adapter->txantennamode); + + return ret; +} + +static int getrxantenna(wlan_private * priv, char *buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + // clear it, so we will know if the value + // returned below is correct or not. + adapter->rxantennamode = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_get_rx, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "Get Rx Antenna mode:0x%04x\n", adapter->rxantennamode); + + return sprintf(buf, "0x%04x", adapter->rxantennamode) + 1; +} + +static int gettxantenna(wlan_private * priv, char *buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + // clear it, so we will know if the value + // returned below is correct or not. + adapter->txantennamode = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_get_tx, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "Get Tx Antenna mode:0x%04x\n", adapter->txantennamode); + + return sprintf(buf, "0x%04x", adapter->txantennamode) + 1; +} + +static int wlan_set_region(wlan_private * priv, u16 region_code) +{ + int i; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + // use the region code to search for the index + if (region_code == libertas_region_code_to_index[i]) { + priv->adapter->regiontableindex = (u16) i; + priv->adapter->regioncode = region_code; + break; + } + } + + // if it's unidentified region code + if (i >= MRVDRV_MAX_REGION_CODE) { + lbs_pr_debug(1, "region Code not identified\n"); + LEAVE(); + return -1; + } + + if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) { + LEAVE(); + return -EINVAL; + } + + return 0; +} + +/** + * @brief Get/Set Firmware wakeup method + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_txcontrol(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data; + ENTER(); + + if ((int)wrq->u.data.length == 0) { + if (copy_to_user + (wrq->u.data.pointer, &adapter->pkttxctrl, sizeof(u32))) { + lbs_pr_alert("copy_to_user failed!\n"); + return -EFAULT; + } + } else { + if ((int)wrq->u.data.length > 1) { + lbs_pr_alert("ioctl too many args!\n"); + return -EFAULT; + } + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_alert("Copy from user failed\n"); + return -EFAULT; + } + + adapter->pkttxctrl = (u32) data; + } + + wrq->u.data.length = 1; + + LEAVE(); + return 0; +} + +/** + * @brief Get/Set NULL Package generation interval + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_null_pkt_interval(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data; + ENTER(); + + if ((int)wrq->u.data.length == 0) { + data = adapter->nullpktinterval; + + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + lbs_pr_alert( "copy_to_user failed!\n"); + return -EFAULT; + } + } else { + if ((int)wrq->u.data.length > 1) { + lbs_pr_alert( "ioctl too many args!\n"); + return -EFAULT; + } + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + adapter->nullpktinterval = data; + } + + wrq->u.data.length = 1; + + LEAVE(); + return 0; +} + +static int wlan_get_rxinfo(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data[2]; + ENTER(); + data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + data[1] = adapter->rxpd_rate; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + wrq->u.data.length = 2; + LEAVE(); + return 0; +} + +static int wlan_get_snr(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int data[4]; + + ENTER(); + memset(data, 0, sizeof(data)); + if (wrq->u.data.length) { + if (copy_from_user(data, wrq->u.data.pointer, + min_t(size_t, wrq->u.data.length, 4) * sizeof(int))) + return -EFAULT; + } + if ((wrq->u.data.length == 0) || (data[0] == 0) || (data[0] == 1)) { + if (adapter->connect_status == libertas_connected) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, + cmd_option_waitforrsp, + 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } + } + + if (wrq->u.data.length == 0) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; + data[1] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; + data[2] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + data[3] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 4)) + return -EFAULT; + wrq->u.data.length = 4; + } else if (data[0] == 0) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 1) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 2) { + data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 3) { + data[0] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else + return -ENOTSUPP; + + LEAVE(); + return 0; +} + +static int wlan_beacon_interval(wlan_private * priv, struct iwreq *wrq) +{ + int data; + wlan_adapter *adapter = priv->adapter; + + if (wrq->u.data.length > 0) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) + return -EFAULT; + + lbs_pr_debug(1, "WLAN SET BEACON INTERVAL: %d\n", data); + if ((data > MRVDRV_MAX_BEACON_INTERVAL) + || (data < MRVDRV_MIN_BEACON_INTERVAL)) + return -ENOTSUPP; + adapter->beaconperiod = data; + } + data = adapter->beaconperiod; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) + return -EFAULT; + + wrq->u.data.length = 1; + + return 0; +} + +static int wlan_get_rssi(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int temp; + int data = 0; + int *val; + + ENTER(); + data = SUBCMD_DATA(wrq); + if ((data == 0) || (data == 1)) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, + 0, NULL); + if (ret) { + LEAVE(); + return ret; + } + } + + switch (data) { + case 0: + + temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + break; + case 1: + temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG], + adapter->NF[TYPE_BEACON][TYPE_AVG]); + break; + case 2: + temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], + adapter->NF[TYPE_RXPD][TYPE_NOAVG]); + break; + case 3: + temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + break; + default: + return -ENOTSUPP; + } + val = (int *)wrq->u.name; + *val = temp; + + LEAVE(); + return 0; +} + +static int wlan_get_nf(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int temp; + int data = 0; + int *val; + + data = SUBCMD_DATA(wrq); + if ((data == 0) || (data == 1)) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, + 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } + + switch (data) { + case 0: + temp = adapter->NF[TYPE_BEACON][TYPE_NOAVG]; + break; + case 1: + temp = adapter->NF[TYPE_BEACON][TYPE_AVG]; + break; + case 2: + temp = adapter->NF[TYPE_RXPD][TYPE_NOAVG]; + break; + case 3: + temp = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + break; + default: + return -ENOTSUPP; + } + + temp = CAL_NF(temp); + + lbs_pr_debug(1, "%s: temp = %d\n", __FUNCTION__, temp); + val = (int *)wrq->u.name; + *val = temp; + return 0; +} + +static int wlan_get_txrate_ioctl(wlan_private * priv, struct ifreq *req) +{ + wlan_adapter *adapter = priv->adapter; + int *pdata; + struct iwreq *wrq = (struct iwreq *)req; + int ret = 0; + adapter->txrate = 0; + lbs_pr_debug(1, "wlan_get_txrate_ioctl\n"); + ret = libertas_prepare_and_send_command(priv, cmd_802_11_tx_rate_query, + cmd_act_get, cmd_option_waitforrsp, + 0, NULL); + if (ret) + return ret; + + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->txrate; + return 0; +} + +static int wlan_get_adhoc_status_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + char status[64]; + wlan_adapter *adapter = priv->adapter; + + memset(status, 0, sizeof(status)); + + switch (adapter->inframode) { + case wlan802_11ibss: + if (adapter->connect_status == libertas_connected) { + if (adapter->adhoccreate) + memcpy(&status, "AdhocStarted", sizeof(status)); + else + memcpy(&status, "AdhocJoined", sizeof(status)); + } else { + memcpy(&status, "AdhocIdle", sizeof(status)); + } + break; + case wlan802_11infrastructure: + memcpy(&status, "Inframode", sizeof(status)); + break; + default: + memcpy(&status, "AutoUnknownmode", sizeof(status)); + break; + } + + lbs_pr_debug(1, "status = %s\n", status); + wrq->u.data.length = strlen(status) + 1; + + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + &status, wrq->u.data.length)) + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Set/Get WPA IE + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_setwpaie_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (wrq->u.data.length) { + if (wrq->u.data.length > sizeof(adapter->wpa_ie)) { + lbs_pr_debug(1, "failed to copy WPA IE, too big \n"); + return -EFAULT; + } + if (copy_from_user(adapter->wpa_ie, wrq->u.data.pointer, + wrq->u.data.length)) { + lbs_pr_debug(1, "failed to copy WPA IE \n"); + return -EFAULT; + } + adapter->wpa_ie_len = wrq->u.data.length; + lbs_pr_debug(1, "Set wpa_ie_len=%d IE=%#x\n", adapter->wpa_ie_len, + adapter->wpa_ie[0]); + lbs_dbg_hex("wpa_ie", adapter->wpa_ie, adapter->wpa_ie_len); + if (adapter->wpa_ie[0] == WPA_IE) + adapter->secinfo.WPAenabled = 1; + else if (adapter->wpa_ie[0] == WPA2_IE) + adapter->secinfo.WPA2enabled = 1; + else { + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + } + } else { + memset(adapter->wpa_ie, 0, sizeof(adapter->wpa_ie)); + adapter->wpa_ie_len = wrq->u.data.length; + lbs_pr_debug(1, "Reset wpa_ie_len=%d IE=%#x\n", + adapter->wpa_ie_len, adapter->wpa_ie[0]); + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + } + + // enable/disable RSN in firmware if WPA is enabled/disabled + // depending on variable adapter->secinfo.WPAenabled is set or not + ret = libertas_prepare_and_send_command(priv, cmd_802_11_enable_rsn, + cmd_act_set, cmd_option_waitforrsp, + 0, NULL); + + LEAVE(); + return ret; +} + +/** + * @brief Set Auto prescan + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_subcmd_setprescan_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + int data; + wlan_adapter *adapter = priv->adapter; + int *val; + + data = SUBCMD_DATA(wrq); + lbs_pr_debug(1, "WLAN_SUBCMD_SET_PRESCAN %d\n", data); + adapter->prescan = data; + + val = (int *)wrq->u.name; + *val = data; + return 0; +} + +static int wlan_set_multiple_dtim_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + u32 mdtim; + int idata; + int ret = -EINVAL; + + ENTER(); + + idata = SUBCMD_DATA(wrq); + mdtim = (u32) idata; + if (((mdtim >= MRVDRV_MIN_MULTIPLE_DTIM) + && (mdtim <= MRVDRV_MAX_MULTIPLE_DTIM)) + || (mdtim == MRVDRV_IGNORE_MULTIPLE_DTIM)) { + priv->adapter->multipledtim = mdtim; + ret = 0; + } + if (ret) + lbs_pr_debug(1, "Invalid parameter, multipledtim not changed.\n"); + + LEAVE(); + return ret; +} + +/** + * @brief Set authentication mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_setauthalg_ioctl(wlan_private * priv, struct ifreq *req) +{ + int alg; + struct iwreq *wrq = (struct iwreq *)req; + wlan_adapter *adapter = priv->adapter; + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + alg = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(alg))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + + lbs_pr_debug(1, "auth alg is %#x\n", alg); + + switch (alg) { + case AUTH_ALG_SHARED_KEY: + adapter->secinfo.authmode = wlan802_11authmodeshared; + break; + case AUTH_ALG_NETWORK_EAP: + adapter->secinfo.authmode = + wlan802_11authmodenetworkEAP; + break; + case AUTH_ALG_OPEN_SYSTEM: + default: + adapter->secinfo.authmode = wlan802_11authmodeopen; + break; + } + return 0; +} + +/** + * @brief Set 802.1x authentication mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set8021xauthalg_ioctl(wlan_private * priv, struct ifreq *req) +{ + int alg; + struct iwreq *wrq = (struct iwreq *)req; + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + alg = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + lbs_pr_debug(1, "802.1x auth alg is %#x\n", alg); + priv->adapter->secinfo.auth1xalg = alg; + return 0; +} + +static int wlan_setencryptionmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + struct iwreq *wrq = (struct iwreq *)req; + + ENTER(); + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + mode = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&mode, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + lbs_pr_debug(1, "encryption mode is %#x\n", mode); + priv->adapter->secinfo.Encryptionmode = mode; + + LEAVE(); + return 0; +} + +static void adjust_mtu(wlan_private * priv) +{ + int mtu_increment = 0; + + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + mtu_increment += sizeof(struct ieee80211_hdr_4addr); + + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) + mtu_increment += max(sizeof(struct tx_radiotap_hdr), + sizeof(struct rx_radiotap_hdr)); + priv->wlan_dev.netdev->mtu = ETH_FRAME_LEN + - sizeof(struct ethhdr) + + mtu_increment; +} + +/** + * @brief Set Link-Layer Layer mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_linkmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + + mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; + + switch (mode) { + case WLAN_LINKMODE_802_3: + priv->adapter->linkmode = mode; + break; + case WLAN_LINKMODE_802_11: + priv->adapter->linkmode = mode; + break; + default: + lbs_pr_info("usb8388-5: invalid link-layer mode (%#x)\n", + mode); + return -EINVAL; + break; + } + lbs_pr_debug(1, "usb8388-5: link-layer mode is %#x\n", mode); + + adjust_mtu(priv); + + return 0; +} + +/** + * @brief Set Radio header mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_radiomode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + + mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; + + switch (mode) { + case WLAN_RADIOMODE_NONE: + priv->adapter->radiomode = mode; + break; + case WLAN_RADIOMODE_RADIOTAP: + priv->adapter->radiomode = mode; + break; + default: + lbs_pr_debug(1, "usb8388-5: invalid radio header mode (%#x)\n", + mode); + return -EINVAL; + } + lbs_pr_debug(1, "usb8388-5: radio-header mode is %#x\n", mode); + + adjust_mtu(priv); + return 0; +} + +/** + * @brief Set Debug header mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_debugmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + priv->adapter->debugmode = (int)((struct ifreq *) + ((u8 *) req + 4))->ifr_data; + return 0; +} + +static int wlan_subcmd_getrxantenna_ioctl(wlan_private * priv, + struct ifreq *req) +{ + int len; + char buf[8]; + struct iwreq *wrq = (struct iwreq *)req; + + lbs_pr_debug(1, "WLAN_SUBCMD_GETRXANTENNA\n"); + len = getrxantenna(priv, buf); + + wrq->u.data.length = len; + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &buf, len)) { + lbs_pr_debug(1, "CopyToUser failed\n"); + return -EFAULT; + } + } + + return 0; +} + +static int wlan_subcmd_gettxantenna_ioctl(wlan_private * priv, + struct ifreq *req) +{ + int len; + char buf[8]; + struct iwreq *wrq = (struct iwreq *)req; + + lbs_pr_debug(1, "WLAN_SUBCMD_GETTXANTENNA\n"); + len = gettxantenna(priv, buf); + + wrq->u.data.length = len; + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &buf, len)) { + lbs_pr_debug(1, "CopyToUser failed\n"); + return -EFAULT; + } + } + return 0; +} + +/** + * @brief Get the MAC TSF value from the firmware + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure containing buffer + * space to store a TSF value retrieved from the firmware + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int wlan_get_tsf_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + u64 tsfval; + int ret; + + ret = libertas_prepare_and_send_command(priv, + cmd_get_tsf, + 0, cmd_option_waitforrsp, 0, &tsfval); + + lbs_pr_debug(1, "IOCTL: Get TSF = 0x%016llx\n", tsfval); + + if (ret != 0) { + lbs_pr_debug(1, "IOCTL: Get TSF; command exec failed\n"); + ret = -EFAULT; + } else { + if (copy_to_user(wrq->u.data.pointer, + &tsfval, + min_t(size_t, wrq->u.data.length, + sizeof(tsfval))) != 0) { + + lbs_pr_debug(1, "IOCTL: Get TSF; Copy to user failed\n"); + ret = -EFAULT; + } else { + ret = 0; + } + } + return ret; +} + +/** + * @brief Get/Set adapt rate + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_adapt_rateset(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + int data[2]; + + memset(data, 0, sizeof(data)); + if (!wrq->u.data.length) { + lbs_pr_debug(1, "Get ADAPT RATE SET\n"); + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rate_adapt_rateset, + cmd_act_get, + cmd_option_waitforrsp, 0, NULL); + data[0] = adapter->enablehwauto; + data[1] = adapter->ratebitmap; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } +#define GET_TWO_INT 2 + wrq->u.data.length = GET_TWO_INT; + } else { + lbs_pr_debug(1, "Set ADAPT RATE SET\n"); + if (wrq->u.data.length > 2) + return -EINVAL; + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * wrq->u.data.length)) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + adapter->enablehwauto = data[0]; + adapter->ratebitmap = data[1]; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rate_adapt_rateset, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + } + return ret; +} + +/** + * @brief Get/Set inactivity timeout + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_inactivity_timeout(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + int data = 0; + u16 timeout = 0; + + ENTER(); + if (wrq->u.data.length > 1) + return -ENOTSUPP; + + if (wrq->u.data.length == 0) { + /* Get */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_inactivity_timeout, + cmd_act_get, + cmd_option_waitforrsp, 0, + &timeout); + data = timeout; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + } else { + /* Set */ + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + timeout = data; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_inactivity_timeout, + cmd_act_set, + cmd_option_waitforrsp, 0, + &timeout); + } + + wrq->u.data.length = 1; + + LEAVE(); + return ret; +} + +static int wlan_do_getlog_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + char buf[GETLOG_BUFSIZE - 1]; + wlan_adapter *adapter = priv->adapter; + + lbs_pr_debug(1, " GET STATS\n"); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_get_log, + 0, cmd_option_waitforrsp, 0, NULL); + + if (ret) { + return ret; + } + + if (wrq->u.data.pointer) { + sprintf(buf, "\n mcasttxframe %u failed %u retry %u " + "multiretry %u framedup %u " + "rtssuccess %u rtsfailure %u ackfailure %u\n" + "rxfrag %u mcastrxframe %u fcserror %u " + "txframe %u wepundecryptable %u ", + adapter->logmsg.mcasttxframe, + adapter->logmsg.failed, + adapter->logmsg.retry, + adapter->logmsg.multiretry, + adapter->logmsg.framedup, + adapter->logmsg.rtssuccess, + adapter->logmsg.rtsfailure, + adapter->logmsg.ackfailure, + adapter->logmsg.rxfrag, + adapter->logmsg.mcastrxframe, + adapter->logmsg.fcserror, + adapter->logmsg.txframe, + adapter->logmsg.wepundecryptable); + wrq->u.data.length = strlen(buf) + 1; + if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + } + + return 0; +} + +static int wlan_scan_type_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + u8 buf[12]; + u8 *option[] = { "active", "passive", "get", }; + int i, max_options = (sizeof(option) / sizeof(option[0])); + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if (priv->adapter->enable11d) { + lbs_pr_debug(1, "11D: Cannot set scantype when 11D enabled\n"); + return -EFAULT; + } + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), + wrq->u.data.length))) + return -EFAULT; + + lbs_pr_debug(1, "Scan type Option = %s\n", buf); + + buf[sizeof(buf) - 1] = '\0'; + + for (i = 0; i < max_options; i++) { + if (!strcmp(buf, option[i])) + break; + } + + switch (i) { + case 0: + adapter->scantype = cmd_scan_type_active; + break; + case 1: + adapter->scantype = cmd_scan_type_passive; + break; + case 2: + wrq->u.data.length = strlen(option[adapter->scantype]) + 1; + + if (copy_to_user(wrq->u.data.pointer, + option[adapter->scantype], + wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + ret = -EFAULT; + } + + break; + default: + lbs_pr_debug(1, "Invalid Scan type Ioctl Option\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int wlan_scan_mode_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + u8 buf[12]; + u8 *option[] = { "bss", "ibss", "any", "get" }; + int i, max_options = (sizeof(option) / sizeof(option[0])); + int ret = 0; + + ENTER(); + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), + wrq->u.data.length))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + lbs_pr_debug(1, "Scan mode Option = %s\n", buf); + + buf[sizeof(buf) - 1] = '\0'; + + for (i = 0; i < max_options; i++) { + if (!strcmp(buf, option[i])) + break; + } + + switch (i) { + + case 0: + adapter->scanmode = cmd_bss_type_bss; + break; + case 1: + adapter->scanmode = cmd_bss_type_ibss; + break; + case 2: + adapter->scanmode = cmd_bss_type_any; + break; + case 3: + + wrq->u.data.length = strlen(option[adapter->scanmode - 1]) + 1; + + lbs_pr_debug(1, "Get Scan mode Option = %s\n", + option[adapter->scanmode - 1]); + + lbs_pr_debug(1, "Scan mode length %d\n", wrq->u.data.length); + + if (copy_to_user(wrq->u.data.pointer, + option[adapter->scanmode - 1], + wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + ret = -EFAULT; + } + lbs_pr_debug(1, "GET Scan type Option after copy = %s\n", + (char *)wrq->u.data.pointer); + + break; + + default: + lbs_pr_debug(1, "Invalid Scan mode Ioctl Option\n"); + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set Adhoc G Rate + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_do_set_grate_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data, data1; + int *val; + + ENTER(); + + data1 = SUBCMD_DATA(wrq); + switch (data1) { + case 0: + adapter->adhoc_grate_enabled = 0; + break; + case 1: + adapter->adhoc_grate_enabled = 1; + break; + case 2: + break; + default: + return -EINVAL; + } + data = adapter->adhoc_grate_enabled; + val = (int *)wrq->u.name; + *val = data; + LEAVE(); + return 0; +} + +static inline int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + return -1; +} + +/* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx") + into binary format (6 bytes). + + This function expects that each byte is represented with 2 characters + (e.g., 11:2:11:11:11:11 is invalid) + + */ +static char *eth_str2addr(char *ethstr, u8 * addr) +{ + int i, val, val2; + char *pos = ethstr; + + /* get rid of initial blanks */ + while (*pos == ' ' || *pos == '\t') + ++pos; + + for (i = 0; i < 6; i++) { + val = hex2int(*pos++); + if (val < 0) + return NULL; + val2 = hex2int(*pos++); + if (val2 < 0) + return NULL; + addr[i] = (val * 16 + val2) & 0xff; + + if (i < 5 && *pos++ != ':') + return NULL; + } + return pos; +} + +/* this writes xx:xx:xx:xx:xx:xx into ethstr + (ethstr must have space for 18 chars) */ +static int eth_addr2str(u8 * addr, char *ethstr) +{ + int i; + char *pos = ethstr; + + for (i = 0; i < 6; i++) { + sprintf(pos, "%02x", addr[i] & 0xff); + pos += 2; + if (i < 5) + *pos++ = ':'; + } + return 17; +} + +/** + * @brief Add an entry to the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + char *pos; + u8 ethaddr[ETH_ALEN]; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("BT_ADD: Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str); + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_add, + cmd_option_waitforrsp, 0, ethaddr)); +} + +/** + * @brief Delete an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + u8 ethaddr[ETH_ALEN]; + char *pos; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_del, + cmd_option_waitforrsp, 0, ethaddr)); + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the BT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_reset_ioctl(wlan_private * priv) +{ + ENTER(); + + lbs_pr_alert( "BT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_reset, + cmd_option_waitforrsp, 0, NULL)); + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + int pos; + char *addr1; + struct iwreq *wrq = (struct iwreq *)req; + /* used to pass id and store the bt entry returned by the FW */ + union { + int id; + char addr1addr2[2 * ETH_ALEN]; + } param; + static char outstr[64]; + char *pbuf = outstr; + int ret; + + ENTER(); + + if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -1; + } + param.id = simple_strtoul(outstr, NULL, 10); + pos = sprintf(pbuf, "%d: ", param.id); + pbuf += pos; + + ret = libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_list, + cmd_option_waitforrsp, 0, + (char *)¶m); + + if (ret == 0) { + addr1 = param.addr1addr2; + + pos = sprintf(pbuf, "ignoring traffic from "); + pbuf += pos; + pos = eth_addr2str(addr1, pbuf); + pbuf += pos; + } else { + sprintf(pbuf, "(null)"); + pbuf += pos; + } + + wrq->u.data.length = strlen(outstr); + if (copy_to_user(wrq->u.data.pointer, (char *)outstr, + wrq->u.data.length)) { + lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Find the next parameter in an input string + * @param ptr A pointer to the input parameter string + * @return A pointer to the next parameter, or 0 if no parameters left. + */ +static char * next_param(char * ptr) +{ + if (!ptr) return NULL; + while (*ptr == ' ' || *ptr == '\t') ++ptr; + return (*ptr == '\0') ? NULL : ptr; +} + +/** + * @brief Add an entry to the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[128]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.metric = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.metric = FWT_DEFAULT_METRIC; + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + + if ((ptr = next_param(ptr))) + fwt_access.ssn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.ssn = FWT_DEFAULT_SSN; + + if ((ptr = next_param(ptr))) + fwt_access.dsn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.dsn = FWT_DEFAULT_DSN; + + if ((ptr = next_param(ptr))) + fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT; + + if ((ptr = next_param(ptr))) + fwt_access.ttl = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.ttl = FWT_DEFAULT_TTL; + + if ((ptr = next_param(ptr))) + fwt_access.expiration = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.expiration = FWT_DEFAULT_EXPIRATION; + + if ((ptr = next_param(ptr))) + fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE; + + if ((ptr = next_param(ptr))) + fwt_access.snr = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.snr = FWT_DEFAULT_SNR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str, + fwt_access.dir, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n", + fwt_access.ssn, fwt_access.dsn, fwt_access.metric, + fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration, + fwt_access.sleepmode, fwt_access.snr); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_add, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + +/** + * @brief Delete an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str, + ethaddr2_str, fwt_access.dir); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_del, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + + +/** + * @brief Print route parameters + * @param fwt_access struct cmd_ds_fwt_access with route info + * @param buf destination buffer for route info + */ +static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf) +{ + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.da, buf); + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.ra, buf); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric)); + buf += sprintf(buf, " %u", fwt_access.dir); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn)); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn)); + buf += sprintf(buf, " %u", fwt_access.hopcount); + buf += sprintf(buf, " %u", fwt_access.ttl); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration)); + buf += sprintf(buf, " %u", fwt_access.sleepmode); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr)); +} + +/** + * @brief Lookup an entry in the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + char *ptr; + static struct cmd_ds_fwt_access fwt_access; + static char out_str[128]; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n"); + return -EINVAL; + } + +#ifdef DEBUG + { + char ethaddr1_str[18]; + lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str); + } +#endif + + ret = libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_lookup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, out_str); + else + sprintf(out_str, "(null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the FWT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_reset_ioctl(wlan_private * priv) +{ + lbs_pr_debug(1, "FWT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_reset, + cmd_option_waitforrsp, 0, NULL)); +} + +/** + * @brief List an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, pbuf); + else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FRT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_route, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " "); + pbuf += eth_addr2str(fwt_access.da, pbuf); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric)); + pbuf += sprintf(pbuf, " %u", fwt_access.dir); + /* note that the firmware returns the nid in the id field */ + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn)); + pbuf += sprintf(pbuf, " hop %u", fwt_access.hopcount); + pbuf += sprintf(pbuf, " ttl %u", fwt_access.ttl); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FNT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + memset(&fwt_access, 0, sizeof(fwt_access)); + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_neighbor, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " ra "); + pbuf += eth_addr2str(fwt_access.ra, pbuf); + pbuf += sprintf(pbuf, " slp %u", fwt_access.sleepmode); + pbuf += sprintf(pbuf, " snr %u", le32_to_cpu(fwt_access.snr)); + pbuf += sprintf(pbuf, " ref %u", le32_to_cpu(fwt_access.references)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Cleans up the route (FRT) and neighbor (FNT) tables + * (Garbage Collection) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req) +{ + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: cleaning up\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_cleanup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets firmware internal time (debug purposes) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req) +{ + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: getting time\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_time, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + memset(&mesh_access, 0, sizeof(mesh_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_get_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret == 0) { + req->ifr_data = (char *)(le32_to_cpu(mesh_access.data[0])); + } + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param ttl New ttl value + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl) +{ + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + if( (ttl > 0xff) || (ttl < 0) ) + return -EINVAL; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = ttl; + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_set_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret != 0) + ret = -EFAULT; + + LEAVE(); + return ret; +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd command + * @return 0--success, otherwise fail + */ +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int subcmd = 0; + int idata = 0; + int *pdata; + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iwreq *wrq = (struct iwreq *)req; + + ENTER(); + + lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WLANSCAN_TYPE: + lbs_pr_debug(1, "Scan type Ioctl\n"); + ret = wlan_scan_type_ioctl(priv, wrq); + break; + + case WLAN_SETNONE_GETNONE: /* set WPA mode on/off ioctl #20 */ + switch (wrq->u.data.flags) { + case WLANDEAUTH: + lbs_pr_debug(1, "Deauth\n"); + libertas_send_deauth(priv); + break; + + case WLANADHOCSTOP: + lbs_pr_debug(1, "Adhoc stop\n"); + ret = libertas_do_adhocstop_ioctl(priv); + break; + + case WLANRADIOON: + wlan_radio_ioctl(priv, 1); + break; + + case WLANRADIOOFF: + wlan_radio_ioctl(priv, 0); + break; + case WLANWLANIDLEON: + libertas_idle_on(priv); + break; + case WLANWLANIDLEOFF: + libertas_idle_off(priv); + break; + case WLAN_SUBCMD_BT_RESET: /* bt_reset */ + wlan_bt_reset_ioctl(priv); + break; + case WLAN_SUBCMD_FWT_RESET: /* fwt_reset */ + wlan_fwt_reset_ioctl(priv); + break; + } /* End of switch */ + break; + + case WLANSETWPAIE: + ret = wlan_setwpaie_ioctl(priv, req); + break; + case WLAN_SETINT_GETINT: + /* The first 4 bytes of req->ifr_data is sub-ioctl number + * after 4 bytes sits the payload. + */ + subcmd = (int)req->ifr_data; //from iwpriv subcmd + switch (subcmd) { + case WLANNF: + ret = wlan_get_nf(priv, wrq); + break; + case WLANRSSI: + ret = wlan_get_rssi(priv, wrq); + break; + case WLANENABLE11D: + ret = libertas_cmd_enable_11d(priv, wrq); + break; + case WLANADHOCGRATE: + ret = wlan_do_set_grate_ioctl(priv, wrq); + break; + case WLAN_SUBCMD_SET_PRESCAN: + ret = wlan_subcmd_setprescan_ioctl(priv, wrq); + break; + } + break; + + case WLAN_SETONEINT_GETONEINT: + switch (wrq->u.data.flags) { + case WLAN_BEACON_INTERVAL: + ret = wlan_beacon_interval(priv, wrq); + break; + + case WLAN_LISTENINTRVL: + if (!wrq->u.data.length) { + int data; + lbs_pr_debug(1, "Get locallisteninterval value\n"); +#define GET_ONE_INT 1 + data = adapter->locallisteninterval; + if (copy_to_user(wrq->u.data.pointer, + &data, sizeof(int))) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = GET_ONE_INT; + } else { + int data; + if (copy_from_user + (&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + lbs_pr_debug(1, "Set locallisteninterval = %d\n", + data); +#define MAX_U16_VAL 65535 + if (data > MAX_U16_VAL) { + lbs_pr_debug(1, "Exceeds U16 value\n"); + return -EINVAL; + } + adapter->locallisteninterval = data; + } + break; + case WLAN_TXCONTROL: + ret = wlan_txcontrol(priv, wrq); //adds for txcontrol ioctl + break; + + case WLAN_NULLPKTINTERVAL: + ret = wlan_null_pkt_interval(priv, wrq); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + break; + + case WLAN_SETONEINT_GETNONE: + /* The first 4 bytes of req->ifr_data is sub-ioctl number + * after 4 bytes sits the payload. + */ + subcmd = wrq->u.data.flags; //from wpa_supplicant subcmd + + if (!subcmd) + subcmd = (int)req->ifr_data; //from iwpriv subcmd + + switch (subcmd) { + case WLAN_SUBCMD_SETRXANTENNA: /* SETRXANTENNA */ + idata = SUBCMD_DATA(wrq); + ret = setrxantenna(priv, idata); + break; + case WLAN_SUBCMD_SETTXANTENNA: /* SETTXANTENNA */ + idata = SUBCMD_DATA(wrq); + ret = settxantenna(priv, idata); + break; + case WLAN_SET_ATIM_WINDOW: + adapter->atimwindow = SUBCMD_DATA(wrq); + adapter->atimwindow = min_t(__u16, adapter->atimwindow, 50); + break; + case WLANSETBCNAVG: + adapter->bcn_avg_factor = SUBCMD_DATA(wrq); + if (adapter->bcn_avg_factor == 0) + adapter->bcn_avg_factor = + DEFAULT_BCN_AVG_FACTOR; + if (adapter->bcn_avg_factor > DEFAULT_BCN_AVG_FACTOR) + adapter->bcn_avg_factor = + DEFAULT_BCN_AVG_FACTOR; + break; + case WLANSETDATAAVG: + adapter->data_avg_factor = SUBCMD_DATA(wrq); + if (adapter->data_avg_factor == 0) + adapter->data_avg_factor = + DEFAULT_DATA_AVG_FACTOR; + if (adapter->data_avg_factor > DEFAULT_DATA_AVG_FACTOR) + adapter->data_avg_factor = + DEFAULT_DATA_AVG_FACTOR; + break; + case WLANSETREGION: + idata = SUBCMD_DATA(wrq); + ret = wlan_set_region(priv, (u16) idata); + break; + + case WLAN_SET_LISTEN_INTERVAL: + idata = SUBCMD_DATA(wrq); + adapter->listeninterval = (u16) idata; + break; + + case WLAN_SET_MULTIPLE_DTIM: + ret = wlan_set_multiple_dtim_ioctl(priv, req); + break; + + case WLANSETAUTHALG: + ret = wlan_setauthalg_ioctl(priv, req); + break; + + case WLANSET8021XAUTHALG: + ret = wlan_set8021xauthalg_ioctl(priv, req); + break; + + case WLANSETENCRYPTIONMODE: + ret = wlan_setencryptionmode_ioctl(priv, req); + break; + + case WLAN_SET_LINKMODE: + ret = wlan_set_linkmode_ioctl(priv, req); + break; + + case WLAN_SET_RADIOMODE: + ret = wlan_set_radiomode_ioctl(priv, req); + break; + + case WLAN_SET_DEBUGMODE: + ret = wlan_set_debugmode_ioctl(priv, req); + break; + + case WLAN_SUBCMD_MESH_SET_TTL: + idata = SUBCMD_DATA(wrq); + ret = wlan_mesh_set_ttl_ioctl(priv, idata); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + break; + + case WLAN_SETNONE_GETTWELVE_CHAR: /* Get Antenna settings */ + /* + * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is + * in flags of iwreq structure, otherwise it will be in + * mode member of iwreq structure. + */ + switch ((int)wrq->u.data.flags) { + case WLAN_SUBCMD_GETRXANTENNA: /* Get Rx Antenna */ + ret = wlan_subcmd_getrxantenna_ioctl(priv, req); + break; + + case WLAN_SUBCMD_GETTXANTENNA: /* Get Tx Antenna */ + ret = wlan_subcmd_gettxantenna_ioctl(priv, req); + break; + + case WLAN_GET_TSF: + ret = wlan_get_tsf_ioctl(priv, wrq); + break; + } + break; + + case WLAN_SET128CHAR_GET128CHAR: + switch ((int)wrq->u.data.flags) { + + case WLANSCAN_MODE: + lbs_pr_debug(1, "Scan mode Ioctl\n"); + ret = wlan_scan_mode_ioctl(priv, wrq); + break; + + case WLAN_GET_ADHOC_STATUS: + ret = wlan_get_adhoc_status_ioctl(priv, wrq); + break; + case WLAN_SUBCMD_BT_ADD: + ret = wlan_bt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_DEL: + ret = wlan_bt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_LIST: + ret = wlan_bt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_ADD: + ret = wlan_fwt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_DEL: + ret = wlan_fwt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LOOKUP: + ret = wlan_fwt_lookup_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_NEIGHBOR: + ret = wlan_fwt_list_neighbor_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST: + ret = wlan_fwt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_ROUTE: + ret = wlan_fwt_list_route_ioctl(priv, req); + break; + } + break; + + case WLAN_SETNONE_GETONEINT: + switch ((int)req->ifr_data) { + case WLANGETBCNAVG: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->bcn_avg_factor; + break; + + case WLANGETREGION: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->regioncode; + break; + + case WLAN_GET_LISTEN_INTERVAL: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->listeninterval; + break; + + case WLAN_GET_LINKMODE: + req->ifr_data = (char *)((u32) adapter->linkmode); + break; + + case WLAN_GET_RADIOMODE: + req->ifr_data = (char *)((u32) adapter->radiomode); + break; + + case WLAN_GET_DEBUGMODE: + req->ifr_data = (char *)((u32) adapter->debugmode); + break; + + case WLAN_GET_MULTIPLE_DTIM: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->multipledtim; + break; + case WLAN_GET_TX_RATE: + ret = wlan_get_txrate_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_CLEANUP: /* fwt_cleanup */ + ret = wlan_fwt_cleanup_ioctl(priv, req); + break; + + case WLAN_SUBCMD_FWT_TIME: /* fwt_time */ + ret = wlan_fwt_time_ioctl(priv, req); + break; + + case WLAN_SUBCMD_MESH_GET_TTL: + ret = wlan_mesh_get_ttl_ioctl(priv, req); + break; + + default: + ret = -EOPNOTSUPP; + + } + + break; + + case WLANGETLOG: + ret = wlan_do_getlog_ioctl(priv, wrq); + break; + + case WLAN_SET_GET_SIXTEEN_INT: + switch ((int)wrq->u.data.flags) { + case WLAN_TPCCFG: + { + int data[5]; + struct cmd_ds_802_11_tpc_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); + if ((wrq->u.data.length > 1) + && (wrq->u.data.length != 5)) + return -1; + + if (wrq->u.data.length == 0) { + cfg.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 5)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + cfg.action = + cpu_to_le16 + (cmd_act_set); + cfg.enable = data[0]; + cfg.usesnr = data[1]; + cfg.P0 = data[2]; + cfg.P1 = data[3]; + cfg.P2 = data[4]; + } + + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_tpc_cfg, + 0, + cmd_option_waitforrsp, + 0, (void *)&cfg); + + data[0] = cfg.enable; + data[1] = cfg.usesnr; + data[2] = cfg.P0; + data[3] = cfg.P1; + data[4] = cfg.P2; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 5)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 5; + } + break; + + case WLAN_POWERCFG: + { + int data[4]; + struct cmd_ds_802_11_pwr_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); + if ((wrq->u.data.length > 1) + && (wrq->u.data.length != 4)) + return -1; + if (wrq->u.data.length == 0) { + cfg.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 4)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + cfg.action = + cpu_to_le16 + (cmd_act_set); + cfg.enable = data[0]; + cfg.PA_P0 = data[1]; + cfg.PA_P1 = data[2]; + cfg.PA_P2 = data[3]; + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_pwr_cfg, + 0, + cmd_option_waitforrsp, + 0, (void *)&cfg); + data[0] = cfg.enable; + data[1] = cfg.PA_P0; + data[2] = cfg.PA_P1; + data[3] = cfg.PA_P2; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 4)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 4; + } + break; + case WLAN_AUTO_FREQ_SET: + { + int data[3]; + struct cmd_ds_802_11_afc afc; + memset(&afc, 0, sizeof(afc)); + if (wrq->u.data.length != 3) + return -1; + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 3)) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + afc.afc_auto = data[0]; + + if (afc.afc_auto != 0) { + afc.threshold = data[1]; + afc.period = data[2]; + } else { + afc.timing_offset = data[1]; + afc.carrier_offset = data[2]; + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_set_afc, + 0, + cmd_option_waitforrsp, + 0, (void *)&afc); + } + break; + case WLAN_AUTO_FREQ_GET: + { + int data[3]; + struct cmd_ds_802_11_afc afc; + memset(&afc, 0, sizeof(afc)); + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_get_afc, + 0, + cmd_option_waitforrsp, + 0, (void *)&afc); + data[0] = afc.afc_auto; + data[1] = afc.timing_offset; + data[2] = afc.carrier_offset; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 3)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 3; + } + break; + case WLAN_SCANPROBES: + { + int data; + if (wrq->u.data.length > 0) { + if (copy_from_user + (&data, wrq->u.data.pointer, + sizeof(int))) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + adapter->scanprobes = data; + } else { + data = adapter->scanprobes; + if (copy_to_user + (wrq->u.data.pointer, &data, + sizeof(int))) { + lbs_pr_debug(1, + "Copy to user failed\n"); + return -EFAULT; + } + } + wrq->u.data.length = 1; + } + break; + case WLAN_LED_GPIO_CTRL: + { + int i; + int data[16]; + + struct cmd_ds_802_11_led_ctrl ctrl; + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio *) ctrl.data; + + memset(&ctrl, 0, sizeof(ctrl)); + if (wrq->u.data.length > MAX_LEDS * 2) + return -ENOTSUPP; + if ((wrq->u.data.length % 2) != 0) + return -ENOTSUPP; + if (wrq->u.data.length == 0) { + ctrl.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * + wrq->u.data.length)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + ctrl.action = + cpu_to_le16 + (cmd_act_set); + ctrl.numled = cpu_to_le16(0); + gpio->header.type = + cpu_to_le16(TLV_TYPE_LED_GPIO); + gpio->header.len = wrq->u.data.length; + for (i = 0; i < wrq->u.data.length; + i += 2) { + gpio->ledpin[i / 2].led = + data[i]; + gpio->ledpin[i / 2].pin = + data[i + 1]; + } + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_led_gpio_ctrl, + 0, + cmd_option_waitforrsp, + 0, (void *)&ctrl); + for (i = 0; i < gpio->header.len; i += 2) { + data[i] = gpio->ledpin[i / 2].led; + data[i + 1] = gpio->ledpin[i / 2].pin; + } + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * + gpio->header.len)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = gpio->header.len; + } + break; + case WLAN_ADAPT_RATESET: + ret = wlan_adapt_rateset(priv, wrq); + break; + case WLAN_INACTIVITY_TIMEOUT: + ret = wlan_inactivity_timeout(priv, wrq); + break; + case WLANSNR: + ret = wlan_get_snr(priv, wrq); + break; + case WLAN_GET_RXINFO: + ret = wlan_get_rxinfo(priv, wrq); + } + break; + + default: + ret = -EINVAL; + break; + } + LEAVE(); + return ret; +} + + diff --git a/drivers/net/wireless/libertas/join.c b/drivers/net/wireless/libertas/join.c new file mode 100644 index 0000000..11682cb --- /dev/null +++ b/drivers/net/wireless/libertas/join.c @@ -0,0 +1,1055 @@ +/** + * Functions implementing wlan infrastructure and adhoc join routines, + * IOCTL handlers as well as command preperation and response routines + * for sending adhoc start, adhoc join, and association commands + * to the firmware. + */ +#include +#include +#include + +#include + +#include "host.h" +#include "decl.h" +#include "join.h" +#include "dev.h" + +/** + * @brief This function finds out the common rates between rate1 and rate2. + * + * It will fill common rates in rate1 as output if found. + * + * NOTE: Setting the MSB of the basic rates need to be taken + * care, either before or after calling this function + * + * @param adapter A pointer to wlan_adapter structure + * @param rate1 the buffer which keeps input and output + * @param rate1_size the size of rate1 buffer + * @param rate2 the buffer which keeps rate2 + * @param rate2_size the size of rate2 buffer. + * + * @return 0 or -1 + */ +static int get_common_rates(wlan_adapter * adapter, u8 * rate1, + int rate1_size, u8 * rate2, int rate2_size) +{ + u8 *ptr = rate1; + int ret = 0; + u8 tmp[30]; + int i; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp, rate1, min_t(size_t, rate1_size, sizeof(tmp))); + memset(rate1, 0, rate1_size); + + /* Mask the top bit of the original values */ + for (i = 0; tmp[i] && i < sizeof(tmp); i++) + tmp[i] &= 0x7F; + + for (i = 0; rate2[i] && i < rate2_size; i++) { + /* Check for Card Rate in tmp, excluding the top bit */ + if (strchr(tmp, rate2[i] & 0x7F)) { + /* values match, so copy the Card Rate to rate1 */ + *rate1++ = rate2[i]; + } + } + + lbs_dbg_hex("rate1 (AP) rates:", tmp, sizeof(tmp)); + lbs_dbg_hex("rate2 (Card) rates:", rate2, rate2_size); + lbs_dbg_hex("Common rates:", ptr, rate1_size); + lbs_pr_debug(1, "Tx datarate is set to 0x%X\n", adapter->datarate); + + if (!adapter->is_datarate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == adapter->datarate) { + ret = 0; + goto done; + } + ptr++; + } + lbs_pr_alert( "Previously set fixed data rate %#x isn't " + "compatible with the network.\n", adapter->datarate); + + ret = -1; + goto done; + } + + ret = 0; +done: + return ret; +} + +int libertas_send_deauth(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->inframode == wlan802_11infrastructure && + adapter->connect_status == libertas_connected) + ret = libertas_send_deauthentication(priv); + else + ret = -ENOTSUPP; + + return ret; +} + +int libertas_do_adhocstop_ioctl(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->inframode == wlan802_11ibss && + adapter->connect_status == libertas_connected) + ret = libertas_stop_adhoc_network(priv); + else + ret = -ENOTSUPP; + + return ret; +} + +/** + * @brief Associate to a specific BSS discovered in a scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to the BSS descriptor to associate with. + * + * @return 0-success, otherwise fail + */ +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_authenticate, + 0, cmd_option_waitforrsp, + 0, pbssdesc->macaddress); + + if (ret) { + LEAVE(); + return ret; + } + + /* set preamble to firmware */ + if (adapter->capinfo.shortpreamble && pbssdesc->cap.shortpreamble) + adapter->preamble = cmd_type_short_preamble; + else + adapter->preamble = cmd_type_long_preamble; + + libertas_set_radio_control(priv); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_associate, + 0, cmd_option_waitforrsp, 0, pbssdesc); + + LEAVE(); + return ret; +} + +/** + * @brief Start an Adhoc Network + * + * @param priv A pointer to wlan_private structure + * @param adhocssid The ssid of the Adhoc Network + * @return 0--success, -1--fail + */ +int libertas_start_adhoc_network(wlan_private * priv, struct WLAN_802_11_SSID *adhocssid) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + adapter->adhoccreate = 1; + + if (!adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocStart: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocStart: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "Adhoc channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %d\n", adapter->curbssparams.band); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_start, + 0, cmd_option_waitforrsp, 0, adhocssid); + + return ret; +} + +/** + * @brief Join an adhoc network found in a previous scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to a BSS descriptor found in a previous scan + * to attempt to join + * + * @return 0--success, -1--fail + */ +int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid =%s\n", + adapter->curbssparams.ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid_len =%u\n", + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid =%s\n", pbssdesc->ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid len =%u\n", + pbssdesc->ssid.ssidlength); + + /* check if the requested SSID is already joined */ + if (adapter->curbssparams.ssid.ssidlength + && !libertas_SSID_cmp(&pbssdesc->ssid, &adapter->curbssparams.ssid) + && (adapter->curbssparams.bssdescriptor.inframode == + wlan802_11ibss)) { + + lbs_pr_debug(1, + "ADHOC_J_CMD: New ad-hoc SSID is the same as current, " + "not attempting to re-join"); + + return -1; + } + + /*Use shortpreamble only when both creator and card supports + short preamble */ + if (!pbssdesc->cap.shortpreamble || !adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocJoin: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocJoin: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %c\n", adapter->curbssparams.band); + + adapter->adhoccreate = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_join, + 0, cmd_option_waitforrsp, + OID_802_11_SSID, pbssdesc); + + return ret; +} + +int libertas_stop_adhoc_network(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_stop, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief Send Deauthentication Request + * + * @param priv A pointer to wlan_private structure + * @return 0--success, -1--fail + */ +int libertas_send_deauthentication(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_deauthenticate, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief Set Idle Off + * + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +int libertas_idle_off(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + const u8 zeromac[] = { 0, 0, 0, 0, 0, 0 }; + int i; + + ENTER(); + + if (adapter->connect_status == libertas_disconnected) { + if (adapter->inframode == wlan802_11infrastructure) { + if (memcmp(adapter->previousbssid, zeromac, + sizeof(zeromac)) != 0) { + + lbs_pr_debug(1, "Previous SSID = %s\n", + adapter->previousssid.ssid); + lbs_pr_debug(1, "Previous BSSID = " + "%02x:%02x:%02x:%02x:%02x:%02x:\n", + adapter->previousbssid[0], + adapter->previousbssid[1], + adapter->previousbssid[2], + adapter->previousbssid[3], + adapter->previousbssid[4], + adapter->previousbssid[5]); + + i = libertas_find_SSID_in_list(adapter, + &adapter->previousssid, + adapter->previousbssid, + adapter->inframode); + + if (i < 0) { + libertas_send_specific_BSSID_scan(priv, + adapter-> + previousbssid, + 1); + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, + adapter-> + previousbssid, + adapter-> + inframode); + } + + if (i < 0) { + /* If the BSSID could not be found, try just the SSID */ + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, NULL, + adapter-> + inframode); + } + + if (i < 0) { + libertas_send_specific_SSID_scan(priv, + &adapter-> + previousssid, + 1); + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, NULL, + adapter-> + inframode); + } + + if (i >= 0) { + ret = + wlan_associate(priv, + &adapter-> + scantable[i]); + } + } + } else if (adapter->inframode == wlan802_11ibss) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_ad_hoc_start, + 0, + cmd_option_waitforrsp, + 0, &adapter->previousssid); + } + } + /* else it is connected */ + + lbs_pr_debug(1, "\nwlanidle is off"); + LEAVE(); + return ret; +} + +/** + * @brief Set Idle On + * + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +int libertas_idle_on(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->connect_status == libertas_connected) { + if (adapter->inframode == wlan802_11infrastructure) { + lbs_pr_debug(1, "Previous SSID = %s\n", + adapter->previousssid.ssid); + memmove(&adapter->previousssid, + &adapter->curbssparams.ssid, + sizeof(struct WLAN_802_11_SSID)); + libertas_send_deauth(priv); + + } else if (adapter->inframode == wlan802_11ibss) { + ret = libertas_stop_adhoc_network(priv); + } + + } + + lbs_pr_debug(1, "\nwlanidle is on"); + + return ret; +} + +/** + * @brief This function prepares command of authenticate. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure + * @param pdata_buf Void cast of pointer to a BSSID to authenticate with + * + * @return 0 or -1 + */ +int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_authenticate *pauthenticate = + &cmd->params.auth; + u8 *bssid = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_authenticate); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate) + + S_DS_GEN); + + pauthenticate->authtype = adapter->secinfo.authmode; + memcpy(pauthenticate->macaddr, bssid, ETH_ALEN); + + lbs_pr_debug(1, "AUTH_CMD: Bssid is : %x:%x:%x:%x:%x:%x\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); + + return 0; +} + +int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_deauthenticate); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) + + S_DS_GEN); + + /* set AP MAC address */ + memmove(dauth->macaddr, adapter->curbssparams.bssid, + ETH_ALEN); + + /* Reason code 3 = Station is leaving */ +#define REASON_CODE_STA_LEAVING 3 + dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING); + + LEAVE(); + return 0; +} + +int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_associate *passo = &cmd->params.associate; + int ret = 0; + struct bss_descriptor *pbssdesc; + u8 *card_rates; + u8 *pos; + int card_rates_size; + u16 tmpcap; + struct mrvlietypes_ssidparamset *ssid; + struct mrvlietypes_phyparamset *phy; + struct mrvlietypes_ssparamset *ss; + struct mrvlietypes_ratesparamset *rates; + struct mrvlietypes_rsnparamset *rsn; + + ENTER(); + + pbssdesc = pdata_buf; + pos = (u8 *) passo; + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_associate); + + /* Save so we know which BSS Desc to use in the response handler */ + adapter->pattemptedbssdesc = pbssdesc; + + memcpy(passo->peerstaaddr, + pbssdesc->macaddress, sizeof(passo->peerstaaddr)); + pos += sizeof(passo->peerstaaddr); + + /* set the listen interval */ + passo->listeninterval = adapter->listeninterval; + + pos += sizeof(passo->capinfo); + pos += sizeof(passo->listeninterval); + pos += sizeof(passo->bcnperiod); + pos += sizeof(passo->dtimperiod); + + ssid = (struct mrvlietypes_ssidparamset *) pos; + ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid->header.len = pbssdesc->ssid.ssidlength; + memcpy(ssid->ssid, pbssdesc->ssid.ssid, ssid->header.len); + pos += sizeof(ssid->header) + ssid->header.len; + ssid->header.len = cpu_to_le16(ssid->header.len); + + phy = (struct mrvlietypes_phyparamset *) pos; + phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); + phy->header.len = sizeof(phy->fh_ds.dsparamset); + memcpy(&phy->fh_ds.dsparamset, + &pbssdesc->phyparamset.dsparamset.currentchan, + sizeof(phy->fh_ds.dsparamset)); + pos += sizeof(phy->header) + phy->header.len; + phy->header.len = cpu_to_le16(phy->header.len); + + ss = (struct mrvlietypes_ssparamset *) pos; + ss->header.type = cpu_to_le16(TLV_TYPE_CF); + ss->header.len = sizeof(ss->cf_ibss.cfparamset); + pos += sizeof(ss->header) + ss->header.len; + ss->header.len = cpu_to_le16(ss->header.len); + + rates = (struct mrvlietypes_ratesparamset *) pos; + rates->header.type = cpu_to_le16(TLV_TYPE_RATES); + + memcpy(&rates->rates, &pbssdesc->libertas_supported_rates, WLAN_SUPPORTED_RATES); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + if (get_common_rates(adapter, rates->rates, WLAN_SUPPORTED_RATES, + card_rates, card_rates_size)) { + ret = -1; + goto done; + } + + rates->header.len = min_t(size_t, strlen(rates->rates), WLAN_SUPPORTED_RATES); + adapter->curbssparams.numofrates = rates->header.len; + + pos += sizeof(rates->header) + rates->header.len; + rates->header.len = cpu_to_le16(rates->header.len); + + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + rsn = (struct mrvlietypes_rsnparamset *) pos; + rsn->header.type = (u16) adapter->wpa_ie[0]; /* WPA_IE or WPA2_IE */ + rsn->header.type = cpu_to_le16(rsn->header.type); + rsn->header.len = (u16) adapter->wpa_ie[1]; + memcpy(rsn->rsnie, &adapter->wpa_ie[2], rsn->header.len); + lbs_dbg_hex("ASSOC_CMD: RSN IE", (u8 *) rsn, + sizeof(rsn->header) + rsn->header.len); + pos += sizeof(rsn->header) + rsn->header.len; + rsn->header.len = cpu_to_le16(rsn->header.len); + } + + /* update curbssparams */ + adapter->curbssparams.channel = + (pbssdesc->phyparamset.dsparamset.currentchan); + + /* Copy the infra. association rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, &rates->rates, + min_t(size_t, sizeof(adapter->curbssparams.datarates), rates->header.len)); + + lbs_pr_debug(1, "ASSOC_CMD: rates->header.len = %d\n", rates->header.len); + + /* set IBSS field */ + if (pbssdesc->inframode == wlan802_11infrastructure) { +#define CAPINFO_ESS_MODE 1 + passo->capinfo.ess = CAPINFO_ESS_MODE; + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN); + + /* set the capability info at last */ + memcpy(&tmpcap, &pbssdesc->cap, sizeof(passo->capinfo)); + tmpcap &= CAPINFO_MASK; + lbs_pr_debug(1, "ASSOC_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&passo->capinfo, &tmpcap, sizeof(passo->capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, void *pssid) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads; + int ret = 0; + int cmdappendsize = 0; + int i; + u16 tmpcap; + struct bss_descriptor *pbssdesc; + struct WLAN_802_11_SSID *ssid = pssid; + + ENTER(); + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_start); + + pbssdesc = &adapter->curbssparams.bssdescriptor; + adapter->pattemptedbssdesc = pbssdesc; + + /* + * Fill in the parameters for 2 data structures: + * 1. cmd_ds_802_11_ad_hoc_start command + * 2. adapter->scantable[i] + * + * Driver will fill up SSID, bsstype,IBSS param, Physical Param, + * probe delay, and cap info. + * + * Firmware will fill up beacon period, DTIM, Basic rates + * and operational rates. + */ + + memset(adhs->SSID, 0, IW_ESSID_MAX_SIZE); + + memcpy(adhs->SSID, ssid->ssid, ssid->ssidlength); + + lbs_pr_debug(1, "ADHOC_S_CMD: SSID = %s\n", adhs->SSID); + + memset(pbssdesc->ssid.ssid, 0, IW_ESSID_MAX_SIZE); + memcpy(pbssdesc->ssid.ssid, ssid->ssid, ssid->ssidlength); + + pbssdesc->ssid.ssidlength = ssid->ssidlength; + + /* set the BSS type */ + adhs->bsstype = cmd_bss_type_ibss; + pbssdesc->inframode = wlan802_11ibss; + adhs->beaconperiod = adapter->beaconperiod; + + /* set Physical param set */ +#define DS_PARA_IE_ID 3 +#define DS_PARA_IE_LEN 1 + + adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID; + adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN; + + WARN_ON(!adapter->adhocchannel); + + lbs_pr_debug(1, "ADHOC_S_CMD: Creating ADHOC on channel %d\n", + adapter->adhocchannel); + + adapter->curbssparams.channel = adapter->adhocchannel; + + pbssdesc->channel = adapter->adhocchannel; + adhs->phyparamset.dsparamset.currentchan = adapter->adhocchannel; + + memcpy(&pbssdesc->phyparamset, + &adhs->phyparamset, sizeof(union ieeetypes_phyparamset)); + + /* set IBSS param set */ +#define IBSS_PARA_IE_ID 6 +#define IBSS_PARA_IE_LEN 2 + + adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID; + adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN; + adhs->ssparamset.ibssparamset.atimwindow = adapter->atimwindow; + memcpy(&pbssdesc->ssparamset, + &adhs->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + /* set capability info */ + adhs->cap.ess = 0; + adhs->cap.ibss = 1; + pbssdesc->cap.ibss = 1; + + /* probedelay */ + adhs->probedelay = cpu_to_le16(cmd_scan_probe_delay_time); + + /* set up privacy in adapter->scantable[i] */ + if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { + +#define AD_HOC_CAP_PRIVACY_ON 1 + lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus set, privacy to WEP\n"); + pbssdesc->privacy = wlan802_11privfilter8021xWEP; + adhs->cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } else { + lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus NOT set, Setting " + "privacy to ACCEPT ALL\n"); + pbssdesc->privacy = wlan802_11privfilteracceptall; + } + + memset(adhs->datarate, 0, sizeof(adhs->datarate)); + + if (adapter->adhoc_grate_enabled) { + memcpy(adhs->datarate, libertas_adhoc_rates_g, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_g))); + } else { + memcpy(adhs->datarate, libertas_adhoc_rates_b, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_b))); + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(adhs->datarate) && adhs->datarate[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* Copy the ad-hoc creating rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, + &adhs->datarate, adapter->curbssparams.numofrates); + + lbs_pr_debug(1, "ADHOC_S_CMD: rates=%02x %02x %02x %02x \n", + adhs->datarate[0], adhs->datarate[1], + adhs->datarate[2], adhs->datarate[3]); + + lbs_pr_debug(1, "ADHOC_S_CMD: AD HOC Start command is ready\n"); + + if (libertas_create_dnld_countryinfo_11d(priv)) { + lbs_pr_debug(1, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &adhs->cap, sizeof(u16)); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&adhs->cap, &tmpcap, sizeof(u16)); + + ret = 0; +done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_stop); + cmd->size = cpu_to_le16(S_DS_GEN); + + return 0; +} + +int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_join *padhocjoin = &cmd->params.adj; + struct bss_descriptor *pbssdesc = pdata_buf; + int cmdappendsize = 0; + int ret = 0; + u8 *card_rates; + int card_rates_size; + u16 tmpcap; + int i; + + ENTER(); + + adapter->pattemptedbssdesc = pbssdesc; + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_join); + + padhocjoin->bssdescriptor.bsstype = cmd_bss_type_ibss; + + padhocjoin->bssdescriptor.beaconperiod = pbssdesc->beaconperiod; + + memcpy(&padhocjoin->bssdescriptor.BSSID, + &pbssdesc->macaddress, ETH_ALEN); + + memcpy(&padhocjoin->bssdescriptor.SSID, + &pbssdesc->ssid.ssid, pbssdesc->ssid.ssidlength); + + memcpy(&padhocjoin->bssdescriptor.phyparamset, + &pbssdesc->phyparamset, sizeof(union ieeetypes_phyparamset)); + + memcpy(&padhocjoin->bssdescriptor.ssparamset, + &pbssdesc->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + memcpy(&tmpcap, &pbssdesc->cap, sizeof(struct ieeetypes_capinfo)); + tmpcap &= CAPINFO_MASK; + + lbs_pr_debug(1, "ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + memcpy(&padhocjoin->bssdescriptor.cap, &tmpcap, + sizeof(struct ieeetypes_capinfo)); + + /* information on BSSID descriptor passed to FW */ + lbs_pr_debug(1, + "ADHOC_J_CMD: BSSID = %2x-%2x-%2x-%2x-%2x-%2x, SSID = %s\n", + padhocjoin->bssdescriptor.BSSID[0], + padhocjoin->bssdescriptor.BSSID[1], + padhocjoin->bssdescriptor.BSSID[2], + padhocjoin->bssdescriptor.BSSID[3], + padhocjoin->bssdescriptor.BSSID[4], + padhocjoin->bssdescriptor.BSSID[5], + padhocjoin->bssdescriptor.SSID); + + lbs_pr_debug(1, "ADHOC_J_CMD: Data Rate = %x\n", + (u32) padhocjoin->bssdescriptor.datarates); + + /* failtimeout */ + padhocjoin->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); + + /* probedelay */ + padhocjoin->probedelay = + cpu_to_le16(cmd_scan_probe_delay_time); + + /* Copy Data rates from the rates recorded in scan response */ + memset(padhocjoin->bssdescriptor.datarates, 0, + sizeof(padhocjoin->bssdescriptor.datarates)); + memcpy(padhocjoin->bssdescriptor.datarates, pbssdesc->datarates, + min(sizeof(padhocjoin->bssdescriptor.datarates), + sizeof(pbssdesc->datarates))); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + adapter->curbssparams.channel = pbssdesc->channel; + + if (get_common_rates(adapter, padhocjoin->bssdescriptor.datarates, + sizeof(padhocjoin->bssdescriptor.datarates), + card_rates, card_rates_size)) { + lbs_pr_debug(1, "ADHOC_J_CMD: get_common_rates returns error.\n"); + ret = -1; + goto done; + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(padhocjoin->bssdescriptor.datarates) + && padhocjoin->bssdescriptor.datarates[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* + * Copy the adhoc joining rates to Current BSS State structure + */ + memcpy(adapter->curbssparams.datarates, + padhocjoin->bssdescriptor.datarates, + adapter->curbssparams.numofrates); + + padhocjoin->bssdescriptor.ssparamset.ibssparamset.atimwindow = + cpu_to_le16(pbssdesc->atimwindow); + + if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { + padhocjoin->bssdescriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } + + if (adapter->psmode == wlan802_11powermodemax_psp) { + /* wake up first */ + enum WLAN_802_11_POWER_MODE Localpsmode; + + Localpsmode = wlan802_11powermodecam; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_ps_mode, + cmd_act_set, + 0, 0, &Localpsmode); + + if (ret) { + ret = -1; + goto done; + } + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &padhocjoin->bssdescriptor.cap, + sizeof(struct ieeetypes_capinfo)); + tmpcap = cpu_to_le16(tmpcap); + + memcpy(&padhocjoin->bssdescriptor.cap, + &tmpcap, sizeof(struct ieeetypes_capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + union iwreq_data wrqu; + struct ieeetypes_assocrsp *passocrsp; + struct bss_descriptor *pbssdesc; + + ENTER(); + + passocrsp = (struct ieeetypes_assocrsp *) & resp->params; + + if (passocrsp->statuscode) { + + libertas_mac_event_disconnected(priv); + + lbs_pr_debug(1, + "ASSOC_RESP: Association failed, status code = %d\n", + passocrsp->statuscode); + + ret = -1; + goto done; + } + + lbs_dbg_hex("ASSOC_RESP:", (void *)&resp->params, + le16_to_cpu(resp->size) - S_DS_GEN); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + /* Set the attempted BSSID Index to current */ + pbssdesc = adapter->pattemptedbssdesc; + + lbs_pr_debug(1, "ASSOC_RESP: %s\n", pbssdesc->ssid.ssid); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + /* Set the new BSSID (AP's MAC address) to current BSSID */ + memcpy(adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + + lbs_pr_debug(1, "ASSOC_RESP: currentpacketfilter is %x\n", + adapter->currentpacketfilter); + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = 0; + adapter->NF[TYPE_RXPD][TYPE_AVG] = 0; + + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + lbs_pr_debug(1, "ASSOC_RESP: Associated \n"); + + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} + +int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 command = le16_to_cpu(resp->command); + u16 result = le16_to_cpu(resp->result); + struct cmd_ds_802_11_ad_hoc_result *padhocresult; + union iwreq_data wrqu; + struct bss_descriptor *pbssdesc; + + ENTER(); + + padhocresult = &resp->params.result; + + lbs_pr_debug(1, "ADHOC_S_RESP: size = %d\n", le16_to_cpu(resp->size)); + lbs_pr_debug(1, "ADHOC_S_RESP: command = %x\n", command); + lbs_pr_debug(1, "ADHOC_S_RESP: result = %x\n", result); + + pbssdesc = adapter->pattemptedbssdesc; + + /* + * Join result code 0 --> SUCCESS + */ + if (result) { + lbs_pr_debug(1, "ADHOC_RESP failed\n"); + if (adapter->connect_status == libertas_connected) { + libertas_mac_event_disconnected(priv); + } + + memset(&adapter->curbssparams.bssdescriptor, + 0x00, sizeof(adapter->curbssparams.bssdescriptor)); + + LEAVE(); + return -1; + } + + /* + * Now the join cmd should be successful + * If BSSID has changed use SSID to compare instead of BSSID + */ + lbs_pr_debug(1, "ADHOC_J_RESP %s\n", pbssdesc->ssid.ssid); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + if (command == cmd_ret_802_11_ad_hoc_start) { + /* Update the created network descriptor with the new BSSID */ + memcpy(pbssdesc->macaddress, + padhocresult->BSSID, ETH_ALEN); + } else { + + /* Make a copy of current BSSID descriptor, only needed for join since + * the current descriptor is already being used for adhoc start + */ + memmove(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + } + + /* Set the BSSID from the joined/started descriptor */ + memcpy(&adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + lbs_pr_debug(1, "ADHOC_RESP: - Joined/Started Ad Hoc\n"); + lbs_pr_debug(1, "ADHOC_RESP: channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + padhocresult->BSSID[0], padhocresult->BSSID[1], + padhocresult->BSSID[2], padhocresult->BSSID[3], + padhocresult->BSSID[4], padhocresult->BSSID[5]); + + LEAVE(); + return ret; +} + +int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/join.h b/drivers/net/wireless/libertas/join.h new file mode 100644 index 0000000..8efa245 --- /dev/null +++ b/drivers/net/wireless/libertas/join.h @@ -0,0 +1,64 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Interface for the wlan infrastructure and adhoc join routines + * + * Driver interface functions and type declarations for the join module + * implemented in wlan_join.c. Process all start/join requests for + * both adhoc and infrastructure networks + */ +#ifndef _WLAN_JOIN_H +#define _WLAN_JOIN_H + +#include "defs.h" + +struct cmd_ds_command; +extern int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pssid); +extern int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp); + +extern int libertas_idle_on(wlan_private * priv); +extern int libertas_idle_off(wlan_private * priv); + +extern int libertas_do_adhocstop_ioctl(wlan_private * priv); +extern int libertas_reassociation_thread(void *data); + +struct WLAN_802_11_SSID; +struct bss_descriptor; + +extern int libertas_start_adhoc_network(wlan_private * priv, + struct WLAN_802_11_SSID *adhocssid); +extern int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor *pbssdesc); +extern int libertas_stop_adhoc_network(wlan_private * priv); + +extern int libertas_send_deauthentication(wlan_private * priv); +extern int libertas_send_deauth(wlan_private * priv); + +extern int libertas_do_adhocstop_ioctl(wlan_private * priv); + +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc); + +#endif diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c new file mode 100644 index 0000000..dcbf102 --- /dev/null +++ b/drivers/net/wireless/libertas/main.c @@ -0,0 +1,1258 @@ +/** + * This file contains the major functions in WLAN + * driver. It includes init, exit, open, close and main + * thread etc.. + */ + +#include +#include +#include +#include +#include + +#include + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "debugfs.h" +#include "assoc.h" + +#ifdef ENABLE_PM +static struct pm_dev *wlan_pm_dev = NULL; +#endif + +#define WLAN_TX_PWR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_US_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_JP_DEFAULT 16 /*50mW */ +#define WLAN_TX_PWR_FR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_EMEA_DEFAULT 20 /*100mW */ + +/* Format { channel, frequency (MHz), maxtxpower } */ +/* band: 'B/G', region: USA FCC/Canada IC */ +static struct chan_freq_power channel_freq_power_US_BG[] = { + {1, 2412, WLAN_TX_PWR_US_DEFAULT}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT} +}; + +/* band: 'B/G', region: Europe ETSI */ +static struct chan_freq_power channel_freq_power_EU_BG[] = { + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT} +}; + +/* band: 'B/G', region: Spain */ +static struct chan_freq_power channel_freq_power_SPN_BG[] = { + {10, 2457, WLAN_TX_PWR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_DEFAULT} +}; + +/* band: 'B/G', region: France */ +static struct chan_freq_power channel_freq_power_FR_BG[] = { + {10, 2457, WLAN_TX_PWR_FR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_FR_DEFAULT}, + {12, 2467, WLAN_TX_PWR_FR_DEFAULT}, + {13, 2472, WLAN_TX_PWR_FR_DEFAULT} +}; + +/* band: 'B/G', region: Japan */ +static struct chan_freq_power channel_freq_power_JPN_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_DEFAULT}, + {2, 2417, WLAN_TX_PWR_JP_DEFAULT}, + {3, 2422, WLAN_TX_PWR_JP_DEFAULT}, + {4, 2427, WLAN_TX_PWR_JP_DEFAULT}, + {5, 2432, WLAN_TX_PWR_JP_DEFAULT}, + {6, 2437, WLAN_TX_PWR_JP_DEFAULT}, + {7, 2442, WLAN_TX_PWR_JP_DEFAULT}, + {8, 2447, WLAN_TX_PWR_JP_DEFAULT}, + {9, 2452, WLAN_TX_PWR_JP_DEFAULT}, + {10, 2457, WLAN_TX_PWR_JP_DEFAULT}, + {11, 2462, WLAN_TX_PWR_JP_DEFAULT}, + {12, 2467, WLAN_TX_PWR_JP_DEFAULT}, + {13, 2472, WLAN_TX_PWR_JP_DEFAULT}, + {14, 2484, WLAN_TX_PWR_JP_DEFAULT} +}; + +/** + * the structure for channel, frequency and power + */ +struct region_cfp_table { + u8 region; + struct chan_freq_power *cfp_BG; + int cfp_no_BG; +}; + +/** + * the structure for the mapping between region and CFP + */ +static struct region_cfp_table region_cfp_table[] = { + {0x10, /*US FCC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x20, /*CANADA IC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x30, /*EU*/ channel_freq_power_EU_BG, + sizeof(channel_freq_power_EU_BG) / sizeof(struct chan_freq_power), + } + , + {0x31, /*SPAIN*/ channel_freq_power_SPN_BG, + sizeof(channel_freq_power_SPN_BG) / sizeof(struct chan_freq_power), + } + , + {0x32, /*FRANCE*/ channel_freq_power_FR_BG, + sizeof(channel_freq_power_FR_BG) / sizeof(struct chan_freq_power), + } + , + {0x40, /*JAPAN*/ channel_freq_power_JPN_BG, + sizeof(channel_freq_power_JPN_BG) / sizeof(struct chan_freq_power), + } + , +/*Add new region here */ +}; + +/** + * the rates supported by the card + */ +u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES] = + { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 +}; + +/** + * the rates supported + */ +u8 libertas_supported_rates[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc G mode + */ +u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc B mode + */ +u8 libertas_adhoc_rates_b[4] = { 0x82, 0x84, 0x8b, 0x96 }; + +/** + * the global variable of a pointer to wlan_private + * structure variable + */ +static wlan_private *wlanpriv = NULL; + +#define MAX_DEVS 5 +static struct net_device *libertas_devs[MAX_DEVS]; +static int libertas_found = 0; + +/** + * the table to keep region code + */ +u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE] = + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; + +static u8 *default_fw_name = "usb8388.bin"; + +/** + * Attributes exported through sysfs + */ + +/** + * @brief Get function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_get(struct device * dev, + struct device_attribute *attr, char * buf) { + struct cmd_ds_mesh_access mesh_access; + + memset(&mesh_access, 0, sizeof(mesh_access)); + libertas_prepare_and_send_command(to_net_dev(dev)->priv, + cmd_mesh_access, + cmd_act_mesh_get_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + + return snprintf(buf, 3, "%d\n", mesh_access.data[0]); +} + +/** + * @brief Set function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_set(struct device * dev, + struct device_attribute *attr, const char * buf, size_t count) { + struct cmd_ds_mesh_access mesh_access; + + + memset(&mesh_access, 0, sizeof(mesh_access)); + sscanf(buf, "%d", &(mesh_access.data[0])); + libertas_prepare_and_send_command((to_net_dev(dev))->priv, + cmd_mesh_access, + cmd_act_mesh_set_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + return strlen(buf); +} + +/** + * libertas_mpp attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/libertas-mpp) + */ +static DEVICE_ATTR(libertas_mpp, 0644, libertas_mpp_get, + libertas_mpp_set ); + +/** + * @brief Check if the device can be open and wait if necessary. + * + * @param dev A pointer to net_device structure + * @return 0 + * + * For USB adapter, on some systems the device open handler will be + * called before FW ready. Use the following flag check and wait + * function to work around the issue. + * + */ +static int pre_open_check(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + int i = 0; + + while (!adapter->fw_ready && i < 20) { + i++; + msleep_interruptible(100); + } + if (!adapter->fw_ready) { + lbs_pr_info("FW not ready, pre_open_check() return failure\n"); + LEAVE(); + return -1; + } + + return 0; +} + +/** + * @brief This function opens the device + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_dev_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + + priv->open = 1; + + if (adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + } else + netif_carrier_off(priv->wlan_dev.netdev); + + LEAVE(); + return 0; +} +/** + * @brief This function opens the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->mesh_open = 1 ; + netif_start_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +/** + * @brief This function opens the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->infra_open = 1 ; + netif_wake_queue(priv->wlan_dev.netdev); + if (priv->open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +static int wlan_dev_close(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + netif_carrier_off(priv->wlan_dev.netdev); + priv->open = 0; + + LEAVE(); + return 0; +} + +/** + * @brief This function closes the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_close(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) (dev->priv); + + priv->mesh_open = 0; + netif_stop_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + +/** + * @brief This function closes the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_close(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + + netif_stop_queue(priv->wlan_dev.netdev); + priv->infra_open = 0; + if (priv->mesh_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + + +#ifdef ENABLE_PM + +/** + * @brief This function is a callback function. it is called by + * kernel to enter or exit power saving mode. + * + * @param pmdev A pointer to pm_dev + * @param pmreq pm_request_t + * @param pmdata A pointer to pmdata + * @return 0 or -1 + */ +static int wlan_pm_callback(struct pm_dev *pmdev, pm_request_t pmreq, + void *pmdata) +{ + wlan_private *priv = wlanpriv; + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + lbs_pr_debug(1, "WPRM_PM_CALLBACK: pmreq = %d.\n", pmreq); + + switch (pmreq) { + case PM_SUSPEND: + lbs_pr_debug(1, "WPRM_PM_CALLBACK: enter PM_SUSPEND.\n"); + + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + if ((adapter->psstate != PS_STATE_SLEEP) + ) { + lbs_pr_debug(1, + "wlan_pm_callback: can't enter sleep mode\n"); + return -1; + } else { + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_detach(dev); + lbs_pr_debug(1, + "netif_device_detach().\n"); + } + libertas_sbi_suspend(priv); + } + break; + } + + /* in non associated mode */ + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) + netif_device_detach(dev); + + /* + * Storing and restoring of the regs be taken care + * at the driver rest will be done at wlan driver + * this makes driver independent of the card + */ + + libertas_sbi_suspend(priv); + + break; + + case PM_RESUME: + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + { + /* + * Bring the inteface up first + * This case should not happen still ... + */ + libertas_sbi_resume(priv); + + /* + * Attach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_attach(dev); + lbs_pr_debug(1, + "after netif_device_attach().\n"); + } + lbs_pr_debug(1, + "After netif attach, in associated mode.\n"); + } + break; + } + + /* in non associated mode */ + + /* + * Bring the inteface up first + * This case should not happen still ... + */ + + libertas_sbi_resume(priv); + + if (netif_running(dev)) + netif_device_attach(dev); + + lbs_pr_debug(1, "after netif attach, in NON associated mode.\n"); + break; + } + + return 0; +} +#endif /* ENABLE_PM */ + +static int wlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int ret = 0; + wlan_private *priv = dev->priv; + + ENTER(); + + if (priv->wlan_dev.dnld_sent || priv->adapter->TxLockFlag) { + priv->stats.tx_dropped++; + goto done; + } + + netif_stop_queue(priv->wlan_dev.netdev); + + if (libertas_process_tx(priv, skb) == 0) + dev->trans_start = jiffies; +done: + LEAVE(); + return ret; +} + +/** + * @brief Mark mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int mesh_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + wlan_private *priv = dev->priv; + ENTER(); + SET_MESH_FRAME(skb); + LEAVE(); + + return wlan_hard_start_xmit(skb, priv->wlan_dev.netdev); +} + +/** + * @brief Mark non-mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int wlan_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) { + ENTER(); + UNSET_MESH_FRAME(skb); + LEAVE(); + return wlan_hard_start_xmit(skb, dev); +} + +static void wlan_tx_timeout(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + ENTER(); + + lbs_pr_err("tx watch dog timeout!\n"); + + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + dev->trans_start = jiffies; + + if (priv->adapter->currenttxskb) { + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* If we are here, we have not received feedback from + the previous packet. Assume TX_FAIL and move on. */ + priv->adapter->eventcause = 0x01000000; + libertas_send_tx_feedback(priv); + } else + wake_up_interruptible(&priv->mainthread.waitq); + } else if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); + + LEAVE(); +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to wlan_private structure + * @return A pointer to net_device_stats structure + */ +static struct net_device_stats *wlan_get_stats(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + return &priv->stats; +} + +static int wlan_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct sockaddr *phwaddr = addr; + + ENTER(); + + memset(adapter->current_addr, 0, ETH_ALEN); + + /* dev->dev_addr is 8 bytes */ + lbs_dbg_hex("dev->dev_addr:", dev->dev_addr, ETH_ALEN); + + lbs_dbg_hex("addr:", phwaddr->sa_data, ETH_ALEN); + memcpy(adapter->current_addr, phwaddr->sa_data, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_mac_address, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + lbs_pr_debug(1, "set mac address failed.\n"); + ret = -1; + goto done; + } + + lbs_dbg_hex("adapter->macaddr:", adapter->current_addr, ETH_ALEN); + memcpy(dev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(((wlan_private *) dev->priv)->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + +done: + LEAVE(); + return ret; +} + +static int wlan_copy_multicast_address(wlan_adapter * adapter, + struct net_device *dev) +{ + int i = 0; + struct dev_mc_list *mcptr = dev->mc_list; + + for (i = 0; i < dev->mc_count; i++) { + memcpy(&adapter->multicastlist[i], mcptr->dmi_addr, ETH_ALEN); + mcptr = mcptr->next; + } + + return i; + +} + +static void wlan_set_multicast_list(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int oldpacketfilter; + + ENTER(); + + oldpacketfilter = adapter->currentpacketfilter; + + if (dev->flags & IFF_PROMISC) { + lbs_pr_debug(1, "enable Promiscuous mode\n"); + adapter->currentpacketfilter |= + cmd_act_mac_promiscuous_enable; + adapter->currentpacketfilter &= + ~(cmd_act_mac_all_multicast_enable | + cmd_act_mac_multicast_enable); + } else { + /* Multicast */ + adapter->currentpacketfilter &= + ~cmd_act_mac_promiscuous_enable; + + if (dev->flags & IFF_ALLMULTI || dev->mc_count > + MRVDRV_MAX_MULTICAST_LIST_SIZE) { + lbs_pr_debug(1, "Enabling All Multicast!\n"); + adapter->currentpacketfilter |= + cmd_act_mac_all_multicast_enable; + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_all_multicast_enable; + + if (!dev->mc_count) { + lbs_pr_debug(1, "No multicast addresses - " + "disabling multicast!\n"); + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + int i; + + adapter->currentpacketfilter |= + cmd_act_mac_multicast_enable; + + adapter->nr_of_multicastmacaddr = + wlan_copy_multicast_address(adapter, dev); + + lbs_pr_debug(1, "Multicast addresses: %d\n", + dev->mc_count); + + for (i = 0; i < dev->mc_count; i++) { + lbs_pr_debug(1, "Multicast address %d:" + "%x %x %x %x %x %x\n", i, + adapter->multicastlist[i][0], + adapter->multicastlist[i][1], + adapter->multicastlist[i][2], + adapter->multicastlist[i][3], + adapter->multicastlist[i][4], + adapter->multicastlist[i][5]); + } + /* set multicast addresses to firmware */ + libertas_prepare_and_send_command(priv, + cmd_mac_multicast_adr, + cmd_act_set, 0, 0, + NULL); + } + } + } + + if (adapter->currentpacketfilter != oldpacketfilter) { + libertas_set_mac_packet_filter(priv); + } + + LEAVE(); +} + +/** + * @brief This function hanldes the major job in WLAN driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to wlan_thread structure + * @return 0 + */ +static int wlan_service_main_thread(void *data) +{ + struct wlan_thread *thread = data; + wlan_private *priv = thread->priv; + wlan_adapter *adapter = priv->adapter; + wait_queue_t wait; + u8 ireg = 0; + + ENTER(); + + wlan_activate_thread(thread); + + init_waitqueue_entry(&wait, current); + + for (;;) { + lbs_pr_debug(1, "main-thread 111: intcounter=%d " + "currenttxskb=%p dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + add_wait_queue(&thread->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&adapter->driver_lock); + if ((adapter->psstate == PS_STATE_SLEEP) || + (!adapter->intcounter + && (priv->wlan_dev.dnld_sent || adapter->cur_cmd || + list_empty(&adapter->cmdpendingq)))) { + lbs_pr_debug(1, + "main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n", + adapter->connect_status, adapter->intcounter, + adapter->psmode, adapter->psstate); + spin_unlock_irq(&adapter->driver_lock); + schedule(); + } else + spin_unlock_irq(&adapter->driver_lock); + + + lbs_pr_debug(1, + "main-thread 222 (waking up): intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&thread->waitq, &wait); + try_to_freeze(); + + lbs_pr_debug(1, "main-thread 333: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + if (kthread_should_stop() + || adapter->surpriseremoved) { + lbs_pr_debug(1, + "main-thread: break from main thread: surpriseremoved=0x%x\n", + adapter->surpriseremoved); + break; + } + + + spin_lock_irq(&adapter->driver_lock); + if (adapter->intcounter) { + u8 int_status; + adapter->intcounter = 0; + int_status = libertas_sbi_get_int_status(priv, &ireg); + + if (int_status) { + lbs_pr_debug(1, + "main-thread: reading HOST_INT_STATUS_REG failed\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + adapter->hisregcpy |= ireg; + } + + lbs_pr_debug(1, "main-thread 444: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + /* command response? */ + if (adapter->hisregcpy & his_cmdupldrdy) { + lbs_pr_debug(1, "main-thread: cmd response ready.\n"); + + adapter->hisregcpy &= ~his_cmdupldrdy; + spin_unlock_irq(&adapter->driver_lock); + libertas_process_rx_command(priv); + spin_lock_irq(&adapter->driver_lock); + } + + /* Any Card Event */ + if (adapter->hisregcpy & his_cardevent) { + lbs_pr_debug(1, "main-thread: Card Event Activity.\n"); + + adapter->hisregcpy &= ~his_cardevent; + + if (libertas_sbi_read_event_cause(priv)) { + lbs_pr_alert( + "main-thread: libertas_sbi_read_event_cause failed.\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + spin_unlock_irq(&adapter->driver_lock); + libertas_process_event(priv); + } else + spin_unlock_irq(&adapter->driver_lock); + + /* Check if we need to confirm Sleep Request received previously */ + if (adapter->psstate == PS_STATE_PRE_SLEEP) { + if (!priv->wlan_dev.dnld_sent && !adapter->cur_cmd) { + if (adapter->connect_status == + libertas_connected) { + lbs_pr_debug(1, + "main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p " + "dnld_sent=%d cur_cmd=%p, confirm now\n", + adapter->intcounter, + adapter->currenttxskb, + priv->wlan_dev.dnld_sent, + adapter->cur_cmd); + + libertas_ps_confirm_sleep(priv, + (u16) adapter->psmode); + } else { + /* workaround for firmware sending + * deauth/linkloss event immediately + * after sleep request, remove this + * after firmware fixes it + */ + adapter->psstate = PS_STATE_AWAKE; + lbs_pr_alert( + "main-thread: ignore PS_SleepConfirm in non-connected state\n"); + } + } + } + + /* The PS state is changed during processing of Sleep Request + * event above + */ + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) + continue; + + /* Execute the next command */ + if (!priv->wlan_dev.dnld_sent && !priv->adapter->cur_cmd) + libertas_execute_next_command(priv); + + /* Wake-up command waiters which can't sleep in + * libertas_prepare_and_send_command + */ + if (!adapter->nr_cmd_pending) + wake_up_all(&adapter->cmd_pending); + + libertas_tx_runqueue(priv); + } + + del_timer(&adapter->command_timer); + adapter->nr_cmd_pending = 0; + wake_up_all(&adapter->cmd_pending); + wlan_deactivate_thread(thread); + + LEAVE(); + return 0; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the wlan_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to wlan_private structure + */ +wlan_private *wlan_add_card(void *card) +{ + struct net_device *dev = NULL; + struct net_device *mesh_dev = NULL; + wlan_private *priv = NULL; + + ENTER(); + + /* Allocate an Ethernet device and register it */ + if (!(dev = alloc_etherdev(sizeof(wlan_private)))) { + lbs_pr_alert( "Init ethernet device failed!\n"); + return NULL; + } + + priv = dev->priv; + + /* allocate buffer for wlan_adapter */ + if (!(priv->adapter = kmalloc(sizeof(wlan_adapter), GFP_KERNEL))) { + lbs_pr_alert( "Allocate buffer for wlan_adapter failed!\n"); + goto err_kmalloc; + } + + /* Allocate a virtual mesh device */ + if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) { + lbs_pr_debug(1, "Init ethernet device failed!\n"); + return NULL; + } + + /* Both intervaces share the priv structure */ + mesh_dev->priv = priv; + + /* init wlan_adapter */ + memset(priv->adapter, 0, sizeof(wlan_adapter)); + + priv->wlan_dev.netdev = dev; + priv->wlan_dev.card = card; + priv->mesh_open = 0; + priv->infra_open = 0; + priv->mesh_dev = mesh_dev; + wlanpriv = priv; + + SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(mesh_dev); + + /* Setup the OS Interface to our functions */ + dev->open = wlan_open; + dev->hard_start_xmit = wlan_pre_start_xmit; + dev->stop = wlan_close; + dev->do_ioctl = libertas_do_ioctl; + dev->set_mac_address = wlan_set_mac_address; + mesh_dev->open = mesh_open; + mesh_dev->hard_start_xmit = mesh_pre_start_xmit; + mesh_dev->stop = mesh_close; + mesh_dev->do_ioctl = libertas_do_ioctl; + memcpy(mesh_dev->dev_addr, wlanpriv->wlan_dev.netdev->dev_addr, + sizeof(wlanpriv->wlan_dev.netdev->dev_addr)); + +#define WLAN_WATCHDOG_TIMEOUT (5 * HZ) + + dev->tx_timeout = wlan_tx_timeout; + dev->get_stats = wlan_get_stats; + dev->watchdog_timeo = WLAN_WATCHDOG_TIMEOUT; + dev->ethtool_ops = &libertas_ethtool_ops; + mesh_dev->get_stats = wlan_get_stats; + mesh_dev->ethtool_ops = &libertas_ethtool_ops; + +#ifdef WIRELESS_EXT + dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; + mesh_dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; +#endif +#define NETIF_F_DYNALLOC 16 + dev->features |= NETIF_F_DYNALLOC; + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->set_multicast_list = wlan_set_multicast_list; + + INIT_LIST_HEAD(&priv->adapter->cmdfreeq); + INIT_LIST_HEAD(&priv->adapter->cmdpendingq); + + spin_lock_init(&priv->adapter->driver_lock); + init_waitqueue_head(&priv->adapter->cmd_pending); + priv->adapter->nr_cmd_pending = 0; + + lbs_pr_debug(1, "Starting kthread...\n"); + priv->mainthread.priv = priv; + wlan_create_thread(wlan_service_main_thread, + &priv->mainthread, "wlan_main_service"); + + priv->assoc_thread = + create_singlethread_workqueue("libertas_assoc"); + INIT_DELAYED_WORK(&priv->assoc_work, wlan_association_worker); + + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (libertas_sbi_register_dev(priv) < 0) { + lbs_pr_info("failed to register wlan device!\n"); + goto err_registerdev; + } + + /* init FW and HW */ + if (libertas_init_fw(priv)) { + lbs_pr_debug(1, "Firmware Init failed\n"); + goto err_registerdev; + } + + if (register_netdev(dev)) { + lbs_pr_err("Cannot register network device!\n"); + goto err_init_fw; + } + + /* Register virtual mesh interface */ + if (register_netdev(mesh_dev)) { + lbs_pr_info("Cannot register mesh virtual interface!\n"); + goto err_init_fw; + } + + lbs_pr_info("%s: Marvell Wlan 802.11 adapter ", dev->name); + + libertas_debugfs_init_one(priv, dev); + + if (libertas_found == MAX_DEVS) + goto err_init_fw; + libertas_devs[libertas_found] = dev; + libertas_found++; +#ifdef ENABLE_PM + if (!(wlan_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, wlan_pm_callback))) + lbs_pr_alert( "failed to register PM callback\n"); +#endif + if (device_create_file(&(mesh_dev->dev), &dev_attr_libertas_mpp)) + goto err_create_file; + + LEAVE(); + return priv; + +err_create_file: + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); +err_init_fw: + libertas_sbi_unregister_dev(priv); +err_registerdev: + destroy_workqueue(priv->assoc_thread); + /* Stop the thread servicing the interrupts */ + wake_up_interruptible(&priv->mainthread.waitq); + wlan_terminate_thread(&priv->mainthread); + kfree(priv->adapter); +err_kmalloc: + free_netdev(dev); + free_netdev(mesh_dev); + wlanpriv = NULL; + + LEAVE(); + return NULL; +} + +static void wake_pending_cmdnodes(wlan_private *priv) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + list_for_each_entry(cmdnode, &priv->adapter->cmdpendingq, list) { + cmdnode->cmdwaitqwoken = 1; + wake_up_interruptible(&cmdnode->cmdwait_q); + } + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + + +int wlan_remove_card(void *card) +{ + wlan_private *priv = libertas_sbi_get_priv(card); + wlan_adapter *adapter; + struct net_device *dev; + struct net_device *mesh_dev; + union iwreq_data wrqu; + int i; + + ENTER(); + + if (!priv) { + LEAVE(); + return 0; + } + + adapter = priv->adapter; + + if (!adapter) { + LEAVE(); + return 0; + } + + dev = priv->wlan_dev.netdev; + mesh_dev = priv->mesh_dev; + + netif_stop_queue(mesh_dev); + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + wake_pending_cmdnodes(priv); + + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); + unregister_netdev(mesh_dev); + unregister_netdev(dev); + + cancel_delayed_work(&priv->assoc_work); + destroy_workqueue(priv->assoc_thread); + + if (adapter->psmode == wlan802_11powermodemax_psp) { + adapter->psmode = wlan802_11powermodecam; + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + +#ifdef ENABLE_PM + pm_unregister(wlan_pm_dev); +#endif + + adapter->surpriseremoved = 1; + + /* Stop the thread servicing the interrupts */ + wlan_terminate_thread(&priv->mainthread); + + libertas_debugfs_remove_one(priv); + + lbs_pr_debug(1, "Free adapter\n"); + libertas_free_adapter(priv); + + for (i = 0; iwlan_dev.netdev) { + libertas_devs[i] = libertas_devs[--libertas_found]; + libertas_devs[libertas_found] = NULL ; + break ; + } + } + + lbs_pr_debug(1, "Unregister finish\n"); + + priv->wlan_dev.netdev = NULL; + priv->mesh_dev = NULL ; + free_netdev(mesh_dev); + free_netdev(dev); + wlanpriv = NULL; + + LEAVE(); + return 0; +} + +/** + * @brief This function finds the CFP in + * region_cfp_table based on region and band parameter. + * + * @param region The region code + * @param band The band + * @param cfp_no A pointer to CFP number + * @return A pointer to CFP + */ +struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, int *cfp_no) +{ + int i, end; + + ENTER(); + + end = sizeof(region_cfp_table)/sizeof(struct region_cfp_table); + + for (i = 0; i < end ; i++) { + lbs_pr_debug(1, "region_cfp_table[i].region=%d\n", + region_cfp_table[i].region); + if (region_cfp_table[i].region == region) { + *cfp_no = region_cfp_table[i].cfp_no_BG; + LEAVE(); + return region_cfp_table[i].cfp_BG; + } + } + + LEAVE(); + return NULL; +} + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + int i = 0; + + struct chan_freq_power *cfp; + int cfp_no; + + ENTER(); + + memset(adapter->region_channel, 0, sizeof(adapter->region_channel)); + + { + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp != NULL) { + adapter->region_channel[i].nrcfp = cfp_no; + adapter->region_channel[i].CFP = cfp; + } else { + lbs_pr_debug(1, "wrong region code %#x in band B-G\n", + region); + return -1; + } + adapter->region_channel[i].valid = 1; + adapter->region_channel[i].region = region; + adapter->region_channel[i].band = band; + i++; + } + LEAVE(); + return 0; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param dev A pointer to net_device structure + * @return n/a + */ +void libertas_interrupt(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + lbs_pr_debug(1, "libertas_interrupt: intcounter=%d\n", + priv->adapter->intcounter); + + priv->adapter->intcounter++; + + if (priv->adapter->psstate == PS_STATE_SLEEP) { + priv->adapter->psstate = PS_STATE_AWAKE; + netif_wake_queue(dev); + } + + wake_up_interruptible(&priv->mainthread.waitq); + + LEAVE(); +} + +static int wlan_init_module(void) +{ + int ret = 0; + + ENTER(); + + if (libertas_fw_name == NULL) { + libertas_fw_name = default_fw_name; + } + + libertas_debugfs_init(); + + if (libertas_sbi_register()) { + ret = -1; + libertas_debugfs_remove(); + goto done; + } + +done: + LEAVE(); + return ret; +} + +static void wlan_cleanup_module(void) +{ + int i; + + ENTER(); + + for (i = 0; ipriv; + reset_device(priv); + } + + libertas_sbi_unregister(); + libertas_debugfs_remove(); + + LEAVE(); +} + +module_init(wlan_init_module); +module_exit(wlan_cleanup_module); + +MODULE_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h new file mode 100644 index 0000000..5d118f4 --- /dev/null +++ b/drivers/net/wireless/libertas/radiotap.h @@ -0,0 +1,57 @@ +#include + +struct tx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 rate; + u8 txpower; + u8 rts_retries; + u8 data_retries; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12]; +#endif +} __attribute__ ((packed)); + +#define TX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ + (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ + (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ + 0) + +#define IEEE80211_FC_VERSION_MASK 0x0003 +#define IEEE80211_FC_TYPE_MASK 0x000c +#define IEEE80211_FC_TYPE_MGT 0x0000 +#define IEEE80211_FC_TYPE_CTL 0x0004 +#define IEEE80211_FC_TYPE_DATA 0x0008 +#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 +#define IEEE80211_FC_TOFROMDS_MASK 0x0300 +#define IEEE80211_FC_TODS_MASK 0x0100 +#define IEEE80211_FC_FROMDS_MASK 0x0200 +#define IEEE80211_FC_NODS 0x0000 +#define IEEE80211_FC_TODS 0x0100 +#define IEEE80211_FC_FROMDS 0x0200 +#define IEEE80211_FC_DSTODS 0x0300 + +struct rx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + u16 chan_freq; + u16 chan_flags; + u8 antenna; + u8 antsignal; + u16 rx_flags; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18]; +#endif +} __attribute__ ((packed)); + +#define RX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ + (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \ + 0) + diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c new file mode 100644 index 0000000..7e3f78f --- /dev/null +++ b/drivers/net/wireless/libertas/rx.c @@ -0,0 +1,459 @@ +/** + * This file contains the handling of RX in wlan driver. + */ +#include +#include + +#include "hostcmd.h" +#include "radiotap.h" +#include "decl.h" +#include "dev.h" +#include "wext.h" + +struct eth803hdr { + u8 dest_addr[6]; + u8 src_addr[6]; + u16 h803_len; +} __attribute__ ((packed)); + +struct rfc1042hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + u16 snap_type; +} __attribute__ ((packed)); + +struct rxpackethdr { + struct rxpd rx_pd; + struct eth803hdr eth803_hdr; + struct rfc1042hdr rfc1042_hdr; +} __attribute__ ((packed)); + +struct rx80211packethdr { + struct rxpd rx_pd; + void *eth80211_hdr; +} __attribute__ ((packed)); + +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb); + +/** + * @brief This function computes the avgSNR . + * + * @param priv A pointer to wlan_private structure + * @return avgSNR + */ +static u8 wlan_getavgsnr(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawSNR[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function computes the AvgNF + * + * @param priv A pointer to wlan_private structure + * @return AvgNF + */ +static u8 wlan_getavgnf(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawNF[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function save the raw SNR/NF to our internel buffer + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF < adapter->data_avg_factor) + adapter->numSNRNF++; + adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr; + adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf; + adapter->nextSNRNF++; + if (adapter->nextSNRNF >= adapter->data_avg_factor) + adapter->nextSNRNF = 0; + return; +} + +/** + * @brief This function computes the RSSI in received packet. + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf); + lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr; + adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf; + wlan_save_rawSNRNF(priv, p_rx_pd); + + adapter->rxpd_rate = p_rx_pd->rx_rate; + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE; + adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE; + lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], + adapter->NF[TYPE_RXPD][TYPE_NOAVG]); + + adapter->RSSI[TYPE_RXPD][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + LEAVE(); +} + +int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb) +{ + lbs_pr_debug(1, "skb->data=%p\n", skb->data); + + if(IS_MESH_FRAME(skb)) + skb->dev = priv->mesh_dev; + else + skb->dev = priv->wlan_dev.netdev; + skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + netif_rx(skb); + + return 0; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rxpackethdr *p_rx_pkt; + struct rxpd *p_rx_pd; + + int hdrchop; + struct ethhdr *p_ethhdr; + + const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + + ENTER(); + + if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH) + lbs_dbg_hex("RX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + return process_rxed_802_11_packet(priv, skb); + + p_rx_pkt = (struct rxpackethdr *) skb->data; + p_rx_pd = &p_rx_pkt->rx_pd; + if (p_rx_pd->rx_control & RxPD_MESH_FRAME) + SET_MESH_FRAME(skb); + else + UNSET_MESH_FRAME(skb); + + lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) { + lbs_pr_debug(1, "RX error: frame received with bad status\n"); + lbs_pr_alert("rxpd Not OK\n"); + priv->stats.rx_errors++; + ret = 0; + goto done; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_rx_pkt->eth803_hdr.dest_addr)); + lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_rx_pkt->eth803_hdr.src_addr)); + + if (memcmp(&p_rx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type (ethertype) + * + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address right + * before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *) & p_rx_pkt->eth803_hdr + + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) + - sizeof(p_rx_pkt->eth803_hdr.dest_addr) + - sizeof(p_rx_pkt->eth803_hdr.src_addr) + - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); + + memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_ethhdr->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header + * that was removed + */ + hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt; + } else { + lbs_dbg_hex("RX Data: LLC/SNAP", + (u8 *) & p_rx_pkt->rfc1042_hdr, + sizeof(p_rx_pkt->rfc1042_hdr)); + + /* Chop off the rxpd */ + hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt; + } + + /* Chop off the leading header bytes so the skb points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + skb_pull(skb, hdrchop); + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) + adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate); + + wlan_compute_rssi(priv, p_rx_pd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + if (libertas_upload_rx_packet(priv, skb)) { + lbs_pr_debug(1, "RX error: libertas_upload_rx_packet" + " returns failure\n"); + ret = -1; + goto done; + } + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + ret = 0; +done: + LEAVE(); + + return ret; +} + +/** + * @brief This function converts Tx/Rx rates from the Marvell WLAN format + * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u8 convert_mv_rate_to_radiotap(u8 rate) +{ + switch (rate) { + case 0: /* 1 Mbps */ + return 2; + case 1: /* 2 Mbps */ + return 4; + case 2: /* 5.5 Mbps */ + return 11; + case 3: /* 11 Mbps */ + return 22; + case 4: /* 6 Mbps */ + return 12; + case 5: /* 9 Mbps */ + return 18; + case 6: /* 12 Mbps */ + return 24; + case 7: /* 18 Mbps */ + return 36; + case 8: /* 24 Mbps */ + return 48; + case 9: /* 36 Mbps */ + return 72; + case 10: /* 48 Mbps */ + return 96; + case 11: /* 54 Mbps */ + return 108; + } + lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate); + return 0; +} + +/** + * @brief This function processes a received 802.11 packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rx80211packethdr *p_rx_pkt; + struct rxpd *prxpd; + struct rx_radiotap_hdr radiotap_hdr; + struct rx_radiotap_hdr *pradiotap_hdr; + + ENTER(); + + p_rx_pkt = (struct rx80211packethdr *) skb->data; + prxpd = &p_rx_pkt->rx_pd; + + // lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) { + //lbs_pr_debug(1, "RX error: frame received with bad status\n"); + priv->stats.rx_errors++; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + /* create the exported radio header */ + switch (priv->adapter->radiomode) { + case WLAN_RADIOMODE_NONE: + /* no radio header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + + case WLAN_RADIOMODE_RADIOTAP: + /* radiotap header */ + radiotap_hdr.hdr.it_version = 0; + /* XXX must check this value for pad */ + radiotap_hdr.hdr.it_pad = 0; + radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr); + radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT; + /* unknown values */ + radiotap_hdr.flags = 0; + radiotap_hdr.chan_freq = 0; + radiotap_hdr.chan_flags = 0; + radiotap_hdr.antenna = 0; + /* known values */ + radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); + /* XXX must check no carryout */ + radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; + radiotap_hdr.rx_flags = 0; + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) + radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; + //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); + + // lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100)); + + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + + /* add space for the new radio header */ + if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && + pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, + GFP_ATOMIC)) { + lbs_pr_alert( "%s: couldn't pskb_expand_head\n", + __func__); + } + + pradiotap_hdr = + (struct rx_radiotap_hdr *)skb_push(skb, + sizeof(struct + rx_radiotap_hdr)); + memcpy(pradiotap_hdr, &radiotap_hdr, + sizeof(struct rx_radiotap_hdr)); + //lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100)); + break; + + default: + /* unknown header */ + lbs_pr_alert( "Unknown radiomode (%i)\n", + priv->adapter->radiomode); + /* don't export any header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + } + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) { + adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate); + } + + wlan_compute_rssi(priv, prxpd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + + if (libertas_upload_rx_packet(priv, skb)) { + lbs_pr_debug(1, "RX error: libertas_upload_rx_packet " + "returns failure\n"); + ret = -1; + goto done; + } + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + ret = 0; +done: + LEAVE(); + + skb->protocol = __constant_htons(0x0019); /* ETH_P_80211_RAW */ + + return (ret); +} diff --git a/drivers/net/wireless/libertas/sbi.h b/drivers/net/wireless/libertas/sbi.h new file mode 100644 index 0000000..59d3a59 --- /dev/null +++ b/drivers/net/wireless/libertas/sbi.h @@ -0,0 +1,40 @@ +/** + * This file contains IF layer definitions. + */ + +#ifndef _SBI_H_ +#define _SBI_H_ + +#include + +#include "defs.h" + +/** INT status Bit Definition*/ +#define his_cmddnldrdy 0x01 +#define his_cardevent 0x02 +#define his_cmdupldrdy 0x04 + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +#define SBI_EVENT_CAUSE_SHIFT 3 + +/* Probe and Check if the card is present*/ +int libertas_sbi_register_dev(wlan_private * priv); +int libertas_sbi_unregister_dev(wlan_private *); +int libertas_sbi_get_int_status(wlan_private * priv, u8 *); +int libertas_sbi_register(void); +void libertas_sbi_unregister(void); +int libertas_sbi_prog_firmware(wlan_private *); + +int libertas_sbi_read_event_cause(wlan_private *); +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb); +wlan_private *libertas_sbi_get_priv(void *card); + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private *); +int libertas_sbi_resume(wlan_private *); +#endif + +#endif /* _SBI_H */ diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c new file mode 100644 index 0000000..e187062 --- /dev/null +++ b/drivers/net/wireless/libertas/scan.c @@ -0,0 +1,2044 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Functions implementing wlan scan IOCTL and firmware command APIs + * + * IOCTL handlers as well as command preperation and response routines + * for sending scan commands to the firmware. + */ +#include +#include +#include +#include + +#include +#include + +#include "host.h" +#include "decl.h" +#include "dev.h" +#include "scan.h" + +//! Approximate amount of data needed to pass a scan result back to iwlist +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_UINT_LEN \ + + IW_EV_FREQ_LEN \ + + IW_EV_QUAL_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_PARAM_LEN \ + + 40) /* 40 for WPAIE */ + +//! Memory needed to store a max sized channel List TLV for a firmware scan +#define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ + + (MRVDRV_MAX_CHANNELS_PER_SCAN \ + * sizeof(struct chanscanparamset))) + +//! Memory needed to store a max number/size SSID TLV for a firmware scan +#define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) + +//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max +#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ + + sizeof(struct mrvlietypes_numprobes) \ + + CHAN_TLV_MAX_SIZE \ + + SSID_TLV_MAX_SIZE) + +//! The maximum number of channels the firmware can scan per command +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +/** + * @brief Number of channels to scan per firmware scan command issuance. + * + * Number restricted to prevent hitting the limit on the amount of scan data + * returned in a single firmware scan command. + */ +#define MRVDRV_CHANNELS_PER_SCAN_CMD 4 + +//! Scan time specified in the channel TLV for each channel for passive scans +#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100 + +//! Scan time specified in the channel TLV for each channel for active scans +#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 + +//! Macro to enable/disable SSID checking before storing a scan table +#ifdef DISCARD_BAD_SSID +#define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid) +#else +#define CHECK_SSID_IS_VALID(x) 1 +#endif + +/** + * @brief Check if a scanned network compatible with the driver settings + * + * WEP WPA WPA2 ad-hoc encrypt Network + * enabled enabled enabled AES mode privacy WPA WPA2 Compatible + * 0 0 0 0 NONE 0 0 0 yes No security + * 1 0 0 0 NONE 1 0 0 yes Static WEP + * 0 1 0 0 x 1x 1 x yes WPA + * 0 0 1 0 x 1x x 1 yes WPA2 + * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES + * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP + * + * + * @param adapter A pointer to wlan_adapter + * @param index Index in scantable to check against current driver settings + * @param mode Network mode: Infrastructure or IBSS + * + * @return Index in scantable, or error code if negative + */ +static int is_network_compatible(wlan_adapter * adapter, int index, int mode) +{ + ENTER(); + + if (adapter->scantable[index].inframode == mode) { + if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].wpa_supplicant.wpa_ie[0] != + WPA_IE + && adapter->scantable[index].wpa2_supplicant.wpa_ie[0] != + WPA2_IE && adapter->secinfo.Encryptionmode == CIPHER_NONE + && !adapter->scantable[index].privacy) { + /* no security */ + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].privacy) { + /* static WEP enabled */ + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_supplicant. + wpa_ie[0] + == WPA_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa2_supplicant. + wpa_ie[0] + == WPA2_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA2 enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA2: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_supplicant. + wpa_ie[0] + != WPA_IE) + && (adapter->scantable[index].wpa2_supplicant. + wpa_ie[0] + != WPA2_IE) + && adapter->secinfo.Encryptionmode != CIPHER_NONE + && adapter->scantable[index].privacy) { + /* dynamic WEP enabled */ + lbs_pr_debug(1, + "is_network_compatible() dynamic WEP: index=%d " + "wpa_ie=%#x wpa2_ie=%#x Encmode=%#x privacy=%#x\n", + index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } + + /* security doesn't match */ + lbs_pr_debug(1, + "is_network_compatible() FAILED: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x privacy=%#x\n", + index, + adapter->scantable[index].wpa_supplicant.wpa_ie[0], + adapter->scantable[index].wpa2_supplicant.wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return -ECONNREFUSED; + } + + /* mode doesn't match */ + LEAVE(); + return -ENETUNREACH; +} + +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return TRUE or FALSE + */ +static u8 ssid_valid(struct WLAN_802_11_SSID *pssid) +{ + int ssididx; + + for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) { + if (!isprint(pssid->ssid[ssididx])) { + return 0; + } + } + + return 1; +} + +/** + * @brief Post process the scan table after a new scan command has completed + * + * Inspect each entry of the scan table and try to find an entry that + * matches our current associated/joined network from the scan. If + * one is found, update the stored copy of the bssdescriptor for our + * current network. + * + * Debug dump the current scan table contents if compiled accordingly. + * + * @param priv A pointer to wlan_private structure + * + * @return void + */ +static void wlan_scan_process_results(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int foundcurrent; + int i; + + foundcurrent = 0; + + if (adapter->connect_status == libertas_connected) { + /* try to find the current BSSID in the new scan list */ + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, + &adapter->curbssparams.ssid) && + !memcmp(adapter->curbssparams.bssid, + adapter->scantable[i].macaddress, + ETH_ALEN)) { + foundcurrent = 1; + } + } + + if (foundcurrent) { + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + &adapter->scantable[i], + sizeof(adapter->curbssparams.bssdescriptor)); + } + } + + for (i = 0; i < adapter->numinscantable; i++) { + lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " + "RSSI[%03d], SSID[%s]\n", + i, + adapter->scantable[i].macaddress[0], + adapter->scantable[i].macaddress[1], + adapter->scantable[i].macaddress[2], + adapter->scantable[i].macaddress[3], + adapter->scantable[i].macaddress[4], + adapter->scantable[i].macaddress[5], + (s32) adapter->scantable[i].rssi, + adapter->scantable[i].ssid.ssid); + } +} + +/** + * @brief Create a channel list for the driver to scan based on region info + * + * Use the driver region/band information to construct a comprehensive list + * of channels to scan. This routine is used for any scan that is not + * provided a specific channel list to scan. + * + * @param priv A pointer to wlan_private structure + * @param scanchanlist Output parameter: resulting channel list to scan + * @param filteredscan Flag indicating whether or not a BSSID or SSID filter + * is being sent in the command to firmware. Used to + * increase the number of channels sent in a scan + * command and to disable the firmware channel scan + * filter. + * + * @return void + */ +static void wlan_scan_create_channel_list(wlan_private * priv, + struct chanscanparamset * scanchanlist, + u8 filteredscan) +{ + + wlan_adapter *adapter = priv->adapter; + struct region_channel *scanregion; + struct chan_freq_power *cfp; + int rgnidx; + int chanidx; + int nextchan; + u8 scantype; + + chanidx = 0; + + /* Set the default scan type to the user specified type, will later + * be changed to passive on a per channel basis if restricted by + * regulatory requirements (11d or 11h) + */ + scantype = adapter->scantype; + + for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { + if (priv->adapter->enable11d && + adapter->connect_status != libertas_connected) { + /* Scan all the supported chan for the first scan */ + if (!adapter->universal_channel[rgnidx].valid) + continue; + scanregion = &adapter->universal_channel[rgnidx]; + + /* clear the parsed_region_chan for the first scan */ + memset(&adapter->parsed_region_chan, 0x00, + sizeof(adapter->parsed_region_chan)); + } else { + if (!adapter->region_channel[rgnidx].valid) + continue; + scanregion = &adapter->region_channel[rgnidx]; + } + + for (nextchan = 0; + nextchan < scanregion->nrcfp; nextchan++, chanidx++) { + + cfp = scanregion->CFP + nextchan; + + if (priv->adapter->enable11d) { + scantype = + libertas_get_scan_type_11d(cfp->channel, + &adapter-> + parsed_region_chan); + } + + switch (scanregion->band) { + case BAND_B: + case BAND_G: + default: + scanchanlist[chanidx].radiotype = + cmd_scan_radio_type_bg; + break; + } + + if (scantype == cmd_scan_type_passive) { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_PASSIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 1; + } else { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_ACTIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 0; + } + + scanchanlist[chanidx].channumber = cfp->channel; + + if (filteredscan) { + scanchanlist[chanidx].chanscanmode. + disablechanfilt = 1; + } + } + } +} + +/** + * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds + * + * Application layer or other functions can invoke wlan_scan_networks + * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. + * This structure is used as the basis of one or many wlan_scan_cmd_config + * commands that are sent to the command processing module and sent to + * firmware. + * + * Create a wlan_scan_cmd_config based on the following user supplied + * parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - channel list + * + * If the SSID or BSSID filter is not present, disable/clear the filter. + * If the number of probes is not set, use the adapter default setting + * Qualify the channel + * + * @param priv A pointer to wlan_private structure + * @param puserscanin NULL or pointer to scan configuration parameters + * @param ppchantlvout Output parameter: Pointer to the start of the + * channel TLV portion of the output scan config + * @param pscanchanlist Output parameter: Pointer to the resulting channel + * list to scan + * @param pmaxchanperscan Output parameter: Number of channels to scan for + * each issuance of the firmware scan command + * @param pfilteredscan Output parameter: Flag indicating whether or not + * a BSSID or SSID filter is being sent in the + * command to firmware. Used to increase the number + * of channels sent in a scan command and to + * disable the firmware channel scan filter. + * @param pscancurrentonly Output parameter: Flag indicating whether or not + * we are only scanning our current active channel + * + * @return resulting scan configuration + */ +static struct wlan_scan_cmd_config * +wlan_scan_setup_scan_config(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin, + struct mrvlietypes_chanlistparamset ** ppchantlvout, + struct chanscanparamset * pscanchanlist, + int *pmaxchanperscan, + u8 * pfilteredscan, + u8 * pscancurrentonly) +{ + wlan_adapter *adapter = priv->adapter; + const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + struct mrvlietypes_numprobes *pnumprobestlv; + struct mrvlietypes_ssidparamset *pssidtlv; + struct wlan_scan_cmd_config * pscancfgout = NULL; + u8 *ptlvpos; + u16 numprobes; + u16 ssidlen; + int chanidx; + int scantype; + int scandur; + int channel; + int radiotype; + + pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); + if (pscancfgout == NULL) + goto out; + + /* The tlvbufferlen is calculated for each scan command. The TLVs added + * in this routine will be preserved since the routine that sends + * the command will append channelTLVs at *ppchantlvout. The difference + * between the *ppchantlvout and the tlvbuffer start will be used + * to calculate the size of anything we add in this routine. + */ + pscancfgout->tlvbufferlen = 0; + + /* Running tlv pointer. Assigned to ppchantlvout at end of function + * so later routines know where channels can be added to the command buf + */ + ptlvpos = pscancfgout->tlvbuffer; + + /* + * Set the initial scan paramters for progressive scanning. If a specific + * BSSID or SSID is used, the number of channels in the scan command + * will be increased to the absolute maximum + */ + *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; + + /* Initialize the scan as un-filtered by firmware, set to TRUE below if + * a SSID or BSSID filter is sent in the command + */ + *pfilteredscan = 0; + + /* Initialize the scan as not being only on the current channel. If + * the channel list is customized, only contains one channel, and + * is the active channel, this is set true and data flow is not halted. + */ + *pscancurrentonly = 0; + + if (puserscanin) { + + /* Set the bss type scan filter, use adapter setting if unset */ + pscancfgout->bsstype = + (puserscanin->bsstype ? puserscanin->bsstype : adapter-> + scanmode); + + /* Set the number of probes to send, use adapter setting if unset */ + numprobes = (puserscanin->numprobes ? puserscanin->numprobes : + adapter->scanprobes); + + /* + * Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled (all zeros). + */ + memcpy(pscancfgout->specificBSSID, + puserscanin->specificBSSID, + sizeof(pscancfgout->specificBSSID)); + + ssidlen = strlen(puserscanin->specificSSID); + + if (ssidlen) { + pssidtlv = + (struct mrvlietypes_ssidparamset *) pscancfgout-> + tlvbuffer; + pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + pssidtlv->header.len = cpu_to_le16(ssidlen); + memcpy(pssidtlv->ssid, puserscanin->specificSSID, + ssidlen); + ptlvpos += sizeof(pssidtlv->header) + ssidlen; + } + + /* + * The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not truncate + * scan results. That is not an issue with an SSID or BSSID + * filter applied to the scan results in the firmware. + */ + if (ssidlen || (memcmp(pscancfgout->specificBSSID, + &zeromac, sizeof(zeromac)) != 0)) { + *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; + *pfilteredscan = 1; + } + } else { + pscancfgout->bsstype = adapter->scanmode; + numprobes = adapter->scanprobes; + } + + /* If the input config or adapter has the number of Probes set, add tlv */ + if (numprobes) { + pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; + pnumprobestlv->header.type = + cpu_to_le16(TLV_TYPE_NUMPROBES); + pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes); + pnumprobestlv->numprobes = cpu_to_le16(numprobes); + + ptlvpos += + sizeof(pnumprobestlv->header) + pnumprobestlv->header.len; + + pnumprobestlv->header.len = + cpu_to_le16(pnumprobestlv->header.len); + } + + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this fuction (SSID, numprobes). + * channel TLVs will be added past this for each scan command, preserving + * the TLVs that were previously added. + */ + *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; + + if (puserscanin && puserscanin->chanlist[0].channumber) { + + lbs_pr_debug(1, "Scan: Using supplied channel list\n"); + + for (chanidx = 0; + chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX + && puserscanin->chanlist[chanidx].channumber; chanidx++) { + + channel = puserscanin->chanlist[chanidx].channumber; + (pscanchanlist + chanidx)->channumber = channel; + + radiotype = puserscanin->chanlist[chanidx].radiotype; + (pscanchanlist + chanidx)->radiotype = radiotype; + + scantype = puserscanin->chanlist[chanidx].scantype; + + if (scantype == cmd_scan_type_passive) { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 1; + } else { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 0; + } + + if (puserscanin->chanlist[chanidx].scantime) { + scandur = + puserscanin->chanlist[chanidx].scantime; + } else { + if (scantype == cmd_scan_type_passive) { + scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; + } else { + scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; + } + } + + (pscanchanlist + chanidx)->minscantime = + cpu_to_le16(scandur); + (pscanchanlist + chanidx)->maxscantime = + cpu_to_le16(scandur); + } + + /* Check if we are only scanning the current channel */ + if ((chanidx == 1) && (puserscanin->chanlist[0].channumber + == + priv->adapter->curbssparams.channel)) { + *pscancurrentonly = 1; + lbs_pr_debug(1, "Scan: Scanning current channel only"); + } + + } else { + lbs_pr_debug(1, "Scan: Creating full region channel list\n"); + wlan_scan_create_channel_list(priv, pscanchanlist, + *pfilteredscan); + } + +out: + return pscancfgout; +} + +/** + * @brief Construct and send multiple scan config commands to the firmware + * + * Previous routines have created a wlan_scan_cmd_config with any requested + * TLVs. This function splits the channel TLV into maxchanperscan lists + * and sends the portion of the channel TLV along with the other TLVs + * to the wlan_cmd routines for execution in the firmware. + * + * @param priv A pointer to wlan_private structure + * @param maxchanperscan Maximum number channels to be included in each + * scan command sent to firmware + * @param filteredscan Flag indicating whether or not a BSSID or SSID + * filter is being used for the firmware command + * scan command sent to firmware + * @param pscancfgout Scan configuration used for this scan. + * @param pchantlvout Pointer in the pscancfgout where the channel TLV + * should start. This is past any other TLVs that + * must be sent down in each firmware command. + * @param pscanchanlist List of channels to scan in maxchanperscan segments + * + * @return 0 or error return otherwise + */ +static int wlan_scan_channel_list(wlan_private * priv, + int maxchanperscan, + u8 filteredscan, + struct wlan_scan_cmd_config * pscancfgout, + struct mrvlietypes_chanlistparamset * pchantlvout, + struct chanscanparamset * pscanchanlist) +{ + struct chanscanparamset *ptmpchan; + struct chanscanparamset *pstartchan; + u8 scanband; + int doneearly; + int tlvidx; + int ret = 0; + + ENTER(); + + if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { + lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n", + pscancfgout, pchantlvout, pscanchanlist); + return -1; + } + + pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired list */ + ptmpchan = pscanchanlist; + + /* Loop through the desired channel list, sending a new firmware scan + * commands for each maxchanperscan channels (or for 1,6,11 individually + * if configured accordingly) + */ + while (ptmpchan->channumber) { + + tlvidx = 0; + pchantlvout->header.len = 0; + scanband = ptmpchan->radiotype; + pstartchan = ptmpchan; + doneearly = 0; + + /* Construct the channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlvidx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired channel list) + * - doneearly is set (controlling individual scanning of 1,6,11) + */ + while (tlvidx < maxchanperscan && ptmpchan->channumber + && !doneearly) { + + lbs_pr_debug(1, + "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n", + ptmpchan->channumber, ptmpchan->radiotype, + ptmpchan->chanscanmode.passivescan, + ptmpchan->chanscanmode.disablechanfilt, + ptmpchan->maxscantime); + + /* Copy the current channel TLV to the command being prepared */ + memcpy(pchantlvout->chanscanparam + tlvidx, + ptmpchan, sizeof(pchantlvout->chanscanparam)); + + /* Increment the TLV header length by the size appended */ + pchantlvout->header.len += + sizeof(pchantlvout->chanscanparam); + + /* + * The tlv buffer length is set to the number of bytes of the + * between the channel tlv pointer and the start of the + * tlv buffer. This compensates for any TLVs that were appended + * before the channel list. + */ + pscancfgout->tlvbufferlen = ((u8 *) pchantlvout + - pscancfgout->tlvbuffer); + + /* Add the size of the channel tlv header and the data length */ + pscancfgout->tlvbufferlen += + (sizeof(pchantlvout->header) + + pchantlvout->header.len); + + /* Increment the index to the channel tlv we are constructing */ + tlvidx++; + + doneearly = 0; + + /* Stop the loop if the *current* channel is in the 1,6,11 set + * and we are not filtering on a BSSID or SSID. + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + + /* Increment the tmp pointer to the next channel to be scanned */ + ptmpchan++; + + /* Stop the loop if the *next* channel is in the 1,6,11 set. + * This will cause it to be the only channel scanned on the next + * interation + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + } + + /* Send the scan command to the firmware with the specified cfg */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0, + 0, 0, pscancfgout); + } + + LEAVE(); + return ret; +} + +/** + * @brief Internal function used to start a scan based on an input config + * + * Use the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table + * + * @param priv A pointer to wlan_private structure + * @param puserscanin Pointer to the input configuration for the requested + * scan. + * + * @return 0 or < 0 if error + */ +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin) +{ + wlan_adapter *adapter = priv->adapter; + struct mrvlietypes_chanlistparamset *pchantlvout; + struct chanscanparamset * scan_chan_list = NULL; + struct wlan_scan_cmd_config * scan_cfg = NULL; + u8 keeppreviousscan; + u8 filteredscan; + u8 scancurrentchanonly; + int maxchanperscan; + int ret; + + ENTER(); + + scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * + WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); + if (scan_chan_list == NULL) { + ret = -ENOMEM; + goto out; + } + + scan_cfg = wlan_scan_setup_scan_config(priv, + puserscanin, + &pchantlvout, + scan_chan_list, + &maxchanperscan, + &filteredscan, + &scancurrentchanonly); + if (scan_cfg == NULL) { + ret = -ENOMEM; + goto out; + } + + keeppreviousscan = 0; + + if (puserscanin) { + keeppreviousscan = puserscanin->keeppreviousscan; + } + + if (!keeppreviousscan) { + memset(adapter->scantable, 0x00, + sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST); + adapter->numinscantable = 0; + } + + /* Keep the data path active if we are only scanning our current channel */ + if (!scancurrentchanonly) { + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + } + + ret = wlan_scan_channel_list(priv, + maxchanperscan, + filteredscan, + scan_cfg, + pchantlvout, + scan_chan_list); + + /* Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(priv); + + if (priv->adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + } + +out: + if (scan_cfg) + kfree(scan_cfg); + + if (scan_chan_list) + kfree(scan_chan_list); + + LEAVE(); + return ret; +} + +/** + * @brief Inspect the scan response buffer for pointers to expected TLVs + * + * TLVs can be included at the end of the scan response BSS information. + * Parse the data in the buffer for pointers to TLVs that can potentially + * be passed back in the response + * + * @param ptlv Pointer to the start of the TLV buffer to parse + * @param tlvbufsize size of the TLV buffer + * @param ptsftlv Output parameter: Pointer to the TSF TLV if found + * + * @return void + */ +static +void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv, + int tlvbufsize, + struct mrvlietypes_tsftimestamp ** ptsftlv) +{ + struct mrvlietypes_data *pcurrenttlv; + int tlvbufleft; + u16 tlvtype; + u16 tlvlen; + + pcurrenttlv = ptlv; + tlvbufleft = tlvbufsize; + *ptsftlv = NULL; + + lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize); + lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize); + + while (tlvbufleft >= sizeof(struct mrvlietypesheader)) { + tlvtype = le16_to_cpu(pcurrenttlv->header.type); + tlvlen = le16_to_cpu(pcurrenttlv->header.len); + + switch (tlvtype) { + case TLV_TYPE_TSFTIMESTAMP: + *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv; + break; + + default: + lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n", + tlvtype); + /* Give up, this seems corrupted */ + return; + } /* switch */ + + tlvbufleft -= (sizeof(ptlv->header) + tlvlen); + pcurrenttlv = + (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen); + } /* while */ +} + +/** + * @brief Interpret a BSS scan response returned from the firmware + * + * Parse the various fixed fields and IEs passed back for a a BSS probe + * response or beacon from the scan command. Record information as needed + * in the scan table struct bss_descriptor for that entry. + * + * @param pBSSIDEntry Output parameter: Pointer to the BSS Entry + * + * @return 0 or -1 + */ +static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, + u8 ** pbeaconinfo, int *bytesleft) +{ + enum ieeetypes_elementid elemID; + struct ieeetypes_fhparamset *pFH; + struct ieeetypes_dsparamset *pDS; + struct ieeetypes_cfparamset *pCF; + struct ieeetypes_ibssparamset *pibss; + struct ieeetypes_capinfo *pcap; + struct WLAN_802_11_FIXED_IEs fixedie; + u8 *pcurrentptr; + u8 *pRate; + u8 elemlen; + u8 bytestocopy; + u8 ratesize; + u16 beaconsize; + u8 founddatarateie; + int bytesleftforcurrentbeacon; + + struct WPA_SUPPLICANT *pwpa_supplicant; + struct WPA_SUPPLICANT *pwpa2_supplicant; + struct IE_WPA *pIe; + const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; + + struct ieeetypes_countryinfoset *pcountryinfo; + + ENTER(); + + founddatarateie = 0; + ratesize = 0; + beaconsize = 0; + + if (*bytesleft >= sizeof(beaconsize)) { + /* Extract & convert beacon size from the command buffer */ + memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize)); + beaconsize = le16_to_cpu(beaconsize); + *bytesleft -= sizeof(beaconsize); + *pbeaconinfo += sizeof(beaconsize); + } + + if (beaconsize == 0 || beaconsize > *bytesleft) { + + *pbeaconinfo += *bytesleft; + *bytesleft = 0; + + return -1; + } + + /* Initialize the current working beacon pointer for this BSS iteration */ + pcurrentptr = *pbeaconinfo; + + /* Advance the return beacon pointer past the current beacon */ + *pbeaconinfo += beaconsize; + *bytesleft -= beaconsize; + + bytesleftforcurrentbeacon = beaconsize; + + pwpa_supplicant = &pBSSEntry->wpa_supplicant; + pwpa2_supplicant = &pBSSEntry->wpa2_supplicant; + + memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN); + lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n", + pBSSEntry->macaddress[0], pBSSEntry->macaddress[1], + pBSSEntry->macaddress[2], pBSSEntry->macaddress[3], + pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]); + + pcurrentptr += ETH_ALEN; + bytesleftforcurrentbeacon -= ETH_ALEN; + + if (bytesleftforcurrentbeacon < 12) { + lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n"); + return -1; + } + + /* + * next 4 fields are RSSI, time stamp, beacon interval, + * and capability information + */ + + /* RSSI is 1 byte long */ + pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr)); + lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr); + pcurrentptr += 1; + bytesleftforcurrentbeacon -= 1; + + /* time stamp is 8 bytes long */ + memcpy(fixedie.timestamp, pcurrentptr, 8); + memcpy(pBSSEntry->timestamp, pcurrentptr, 8); + pcurrentptr += 8; + bytesleftforcurrentbeacon -= 8; + + /* beacon interval is 2 bytes long */ + memcpy(&fixedie.beaconinterval, pcurrentptr, 2); + pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* capability information is 2 bytes long */ + memcpy(&fixedie.capabilities, pcurrentptr, 2); + lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n", + fixedie.capabilities); + fixedie.capabilities = le16_to_cpu(fixedie.capabilities); + pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities; + memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo)); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* rest of the current buffer are IE's */ + lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n", + bytesleftforcurrentbeacon); + + lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr, + bytesleftforcurrentbeacon); + + if (pcap->privacy) { + lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n"); + pBSSEntry->privacy = wlan802_11privfilter8021xWEP; + } else { + pBSSEntry->privacy = wlan802_11privfilteracceptall; + } + + if (pcap->ibss == 1) { + pBSSEntry->inframode = wlan802_11ibss; + } else { + pBSSEntry->inframode = wlan802_11infrastructure; + } + + /* process variable IE */ + while (bytesleftforcurrentbeacon >= 2) { + elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr)); + elemlen = *((u8 *) pcurrentptr + 1); + + if (bytesleftforcurrentbeacon < elemlen) { + lbs_pr_debug(1, "InterpretIE: error in processing IE, " + "bytes left < IE length\n"); + bytesleftforcurrentbeacon = 0; + continue; + } + + switch (elemID) { + + case SSID: + pBSSEntry->ssid.ssidlength = elemlen; + memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2), + elemlen); + lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid); + break; + + case SUPPORTED_RATES: + memcpy(pBSSEntry->datarates, (pcurrentptr + 2), + elemlen); + memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2), + elemlen); + ratesize = elemlen; + founddatarateie = 1; + break; + + case EXTRA_IE: + lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n"); + pBSSEntry->extra_ie = 1; + break; + + case FH_PARAM_SET: + pFH = (struct ieeetypes_fhparamset *) pcurrentptr; + memmove(&pBSSEntry->phyparamset.fhparamset, pFH, + sizeof(struct ieeetypes_fhparamset)); + pBSSEntry->phyparamset.fhparamset.dwelltime + = + le16_to_cpu(pBSSEntry->phyparamset.fhparamset. + dwelltime); + break; + + case DS_PARAM_SET: + pDS = (struct ieeetypes_dsparamset *) pcurrentptr; + + pBSSEntry->channel = pDS->currentchan; + + memcpy(&pBSSEntry->phyparamset.dsparamset, pDS, + sizeof(struct ieeetypes_dsparamset)); + break; + + case CF_PARAM_SET: + pCF = (struct ieeetypes_cfparamset *) pcurrentptr; + + memcpy(&pBSSEntry->ssparamset.cfparamset, pCF, + sizeof(struct ieeetypes_cfparamset)); + break; + + case IBSS_PARAM_SET: + pibss = (struct ieeetypes_ibssparamset *) pcurrentptr; + pBSSEntry->atimwindow = + le32_to_cpu(pibss->atimwindow); + + memmove(&pBSSEntry->ssparamset.ibssparamset, pibss, + sizeof(struct ieeetypes_ibssparamset)); + + pBSSEntry->ssparamset.ibssparamset.atimwindow + = + le16_to_cpu(pBSSEntry->ssparamset.ibssparamset. + atimwindow); + break; + + /* Handle Country Info IE */ + case COUNTRY_INFO: + pcountryinfo = + (struct ieeetypes_countryinfoset *) pcurrentptr; + + if (pcountryinfo->len < + sizeof(pcountryinfo->countrycode) + || pcountryinfo->len > 254) { + lbs_pr_debug(1, "InterpretIE: 11D- Err " + "CountryInfo len =%d min=%d max=254\n", + pcountryinfo->len, + sizeof(pcountryinfo->countrycode)); + LEAVE(); + return -1; + } + + memcpy(&pBSSEntry->countryinfo, + pcountryinfo, pcountryinfo->len + 2); + lbs_dbg_hex("InterpretIE: 11D- CountryInfo:", + (u8 *) pcountryinfo, + (u32) (pcountryinfo->len + 2)); + break; + + case EXTENDED_SUPPORTED_RATES: + /* + * only process extended supported rate + * if data rate is already found. + * data rate IE should come before + * extended supported rate IE + */ + if (founddatarateie) { + if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) { + bytestocopy = + (WLAN_SUPPORTED_RATES - ratesize); + } else { + bytestocopy = elemlen; + } + + pRate = (u8 *) pBSSEntry->datarates; + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + + pRate = (u8 *) pBSSEntry->libertas_supported_rates; + + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + } + break; + + case VENDOR_SPECIFIC_221: +#define IE_ID_LEN_FIELDS_BYTES 2 + pIe = (struct IE_WPA *)pcurrentptr; + + if (!memcmp(pIe->oui, oui01, sizeof(oui01))) { + pwpa_supplicant->wpa_ie_len + = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pwpa_supplicant->wpa_ie)); + memcpy(pwpa_supplicant->wpa_ie, + pcurrentptr, + pwpa_supplicant->wpa_ie_len); + lbs_dbg_hex("InterpretIE: Resp WPA_IE", + pwpa_supplicant->wpa_ie, elemlen); + } + break; + case WPA2_IE: + pIe = (struct IE_WPA *)pcurrentptr; + pwpa2_supplicant->wpa_ie_len + = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pwpa2_supplicant->wpa_ie)); + memcpy(pwpa2_supplicant->wpa_ie, + pcurrentptr, pwpa2_supplicant->wpa_ie_len); + + lbs_dbg_hex("InterpretIE: Resp WPA2_IE", + pwpa2_supplicant->wpa_ie, elemlen); + break; + case TIM: + break; + + case CHALLENGE_TEXT: + break; + } + + pcurrentptr += elemlen + 2; + + /* need to account for IE ID and IE len */ + bytesleftforcurrentbeacon -= (elemlen + 2); + + } /* while (bytesleftforcurrentbeacon > 2) */ + + return 0; +} + +/** + * @brief Compare two SSIDs + * + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2) +{ + if (!ssid1 || !ssid2) + return -1; + + if (ssid1->ssidlength != ssid2->ssidlength) + return -1; + + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength); +} + +/** + * @brief This function finds a specific compatible BSSID in the scan list + * + * @param adapter A pointer to wlan_adapter + * @param bssid BSSID to find in the scan list + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list, or error return code (< 0) + */ +int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode) +{ + int ret = -ENETUNREACH; + int i; + + if (!bssid) + return -EFAULT; + + lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n", + adapter->numinscantable); + + /* Look through the scan table for a compatible match. The ret return + * variable will be equal to the index in the scan table (greater + * than zero) if the network is compatible. The loop will continue + * past a matched bssid that is not compatible in case there is an + * AP with multiple SSIDs assigned to the same BSSID + */ + for (i = 0; ret < 0 && i < adapter->numinscantable; i++) { + if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + ret = is_network_compatible(adapter, i, mode); + break; + default: + ret = i; + break; + } + } + } + + return ret; +} + +/** + * @brief This function finds ssid in ssid list. + * + * @param adapter A pointer to wlan_adapter + * @param ssid SSID to find in the list + * @param bssid BSSID to qualify the SSID selection (if provided) + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list + */ +int libertas_find_SSID_in_list(wlan_adapter * adapter, + struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode) +{ + int net = -ENETUNREACH; + u8 bestrssi = 0; + int i; + int j; + + lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) && + (!bssid || + !memcmp(adapter->scantable[i]. + macaddress, bssid, ETH_ALEN))) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + j = is_network_compatible(adapter, i, mode); + + if (j >= 0) { + if (bssid) { + return i; + } + + if (SCAN_RSSI + (adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter-> + scantable[i]. + rssi); + net = i; + } + } else { + if (net == -ENETUNREACH) { + net = j; + } + } + break; + case wlan802_11autounknown: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + net = i; + } + break; + } + } + } + + return net; +} + +/** + * @brief This function finds the best SSID in the Scan List + * + * Search the scan table for the best SSID that also matches the current + * adapter network preference (infrastructure or adhoc) + * + * @param adapter A pointer to wlan_adapter + * + * @return index in BSSID list + */ +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode) +{ + int bestnet = -ENETUNREACH; + u8 bestrssi = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + if (is_network_compatible(adapter, i, mode) >= 0) { + if (SCAN_RSSI(adapter->scantable[i].rssi) > + bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + bestnet = i; + } + } + break; + case wlan802_11autounknown: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i].rssi); + bestnet = i; + } + break; + } + } + + LEAVE(); + return bestnet; +} + +/** + * @brief Find the AP with specific ssid in the scan list + * + * @param priv A pointer to wlan_private structure + * @param pSSID A pointer to AP's ssid + * + * @return 0--success, otherwise--fail + */ +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct bss_descriptor *preqbssid; + int i; + + ENTER(); + + memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID)); + + wlan_scan_networks(priv, NULL); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + i = libertas_find_best_SSID_in_list(adapter, preferred_mode); + if (i < 0) { + ret = -1; + goto out; + } + + preqbssid = &adapter->scantable[i]; + memcpy(pSSID, &preqbssid->ssid, + sizeof(struct WLAN_802_11_SSID)); + *out_mode = preqbssid->inframode; + + if (!pSSID->ssidlength) { + ret = -1; + } + +out: + LEAVE(); + return ret; +} + +/** + * @brief Scan Network + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + ENTER(); + + if (!wlan_scan_networks(priv, NULL)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, + NULL); + } + + if (adapter->surpriseremoved) + return -1; + + LEAVE(); + return 0; +} + +/** + * @brief Send a scan command for all available channels filtered on a spec + * + * @param priv A pointer to wlan_private structure + * @param prequestedssid A pointer to AP's ssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (prequestedssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + + memcpy(scancfg.specificSSID, prequestedssid->ssid, + prequestedssid->ssidlength); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief scan an AP with specific BSSID + * + * @param priv A pointer to wlan_private structure + * @param bssid A pointer to AP's bssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan) +{ + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (bssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID)); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (priv->adapter->surpriseremoved) + return -1; + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief Retrieve the scan table entries via wireless tools IOCTL call + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + struct chan_freq_power *cfp; + struct bss_descriptor *pscantable; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + int i; + int j; + int rate; +#define PERFECT_RSSI ((u8)50) +#define WORST_RSSI ((u8)0) +#define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) + u8 rssi; + + u8 buf[16 + 256 * 2]; + u8 *ptr; + + ENTER(); + + /* + * if there's either commands in the queue or one being + * processed return -EAGAIN for iwlist to retry later. + */ + if (adapter->nr_cmd_pending) + return -EAGAIN; + + if (adapter->connect_status == libertas_connected) + lbs_pr_debug(1, "Current ssid: %32s\n", + adapter->curbssparams.ssid.ssid); + + lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n", + adapter->numinscantable); + + /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. + * The new API using SIOCGIWSCAN is only limited by buffer size + * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes + * which is 4096. + */ + for (i = 0; i < adapter->numinscantable; i++) { + if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { + lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p " + "MAX_SCAN_CELL_SIZE=%d\n", + i, current_ev, end_buf, MAX_SCAN_CELL_SIZE); + break; + } + + pscantable = &adapter->scantable[i]; + + lbs_pr_debug(1, "i=%d ssid: %32s\n", i, pscantable->ssid.ssid); + + cfp = + libertas_find_cfp_by_band_and_channel(adapter, 0, + pscantable->channel); + if (!cfp) { + lbs_pr_debug(1, "Invalid channel number %d\n", + pscantable->channel); + continue; + } + + if (!ssid_valid(&adapter->scantable[i].ssid)) { + continue; + } + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, + &adapter->scantable[i].macaddress, ETH_ALEN); + + iwe.len = IW_EV_ADDR_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //Add the ESSID + iwe.u.data.length = adapter->scantable[i].ssid.ssidlength; + + if (iwe.u.data.length > 32) { + iwe.u.data.length = 32; + } + + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable[i].ssid. + ssid); + + //Add mode + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = adapter->scantable[i].inframode + 1; + iwe.len = IW_EV_UINT_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //frequency + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long)cfp->freq * 100000; + iwe.u.freq.e = 1; + iwe.len = IW_EV_FREQ_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; + iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi); + + rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; + iwe.u.qual.qual = + (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * + (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / + (RSSI_DIFF * RSSI_DIFF); + if (iwe.u.qual.qual > 100) + iwe.u.qual.qual = 100; + else if (iwe.u.qual.qual < 1) + iwe.u.qual.qual = 0; + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + iwe.u.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + if ((adapter->inframode == wlan802_11ibss) && + !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, + cmd_option_waitforrsp, + 0, NULL); + + if (!ret) { + iwe.u.qual.level = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE); + } + } + iwe.len = IW_EV_QUAL_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (adapter->scantable[i].privacy) { + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + } else { + iwe.u.data.flags = IW_ENCODE_DISABLED; + } + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable->ssid. + ssid); + + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + + /* Bit rate given in 500 kb/s units (+ 0x80) */ + for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates); + j++) { + if (adapter->scantable[i].libertas_supported_rates[j] == 0) { + break; + } + rate = + (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) * + 500000; + if (rate > iwe.u.bitrate.value) { + iwe.u.bitrate.value = rate; + } + + iwe.u.bitrate.value = + (adapter->scantable[i].libertas_supported_rates[j] + & 0x7f) * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, + end_buf, &iwe, iwe.len); + + } + if ((adapter->scantable[i].inframode == wlan802_11ibss) + && !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + iwe.u.bitrate.value = 22 * 500000; + } + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, + iwe.len); + + /* Add new value to event */ + current_val = current_ev + IW_EV_LCP_LEN; + + if (adapter->scantable[i].wpa2_supplicant.wpa_ie[0] == WPA2_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i]. + wpa2_supplicant.wpa_ie, + adapter->scantable[i].wpa2_supplicant. + wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i]. + wpa2_supplicant.wpa_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + if (adapter->scantable[i].wpa_supplicant.wpa_ie[0] == WPA_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i]. + wpa_supplicant.wpa_ie, + adapter->scantable[i].wpa_supplicant. + wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i]. + wpa_supplicant.wpa_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + + + if (adapter->scantable[i].extra_ie != 0) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "extra_ie"); + iwe.u.data.length = strlen(buf); + + lbs_pr_debug(1, "iwe.u.data.length %d\n", + iwe.u.data.length); + lbs_pr_debug(1, "BUF: %s \n", buf); + + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + } + + current_val = current_ev + IW_EV_LCP_LEN; + + /* + * Check if we added any event + */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + + dwrq->length = (current_ev - extra); + dwrq->flags = 0; + + LEAVE(); + return 0; +} + +/** + * @brief Prepare a scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command + * struct to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure to be sent to + * firmware with the cmd_DS_801_11_SCAN structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return 0 or -1 + * + * @sa wlan_scan_create_channel_list + */ +int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; + struct wlan_scan_cmd_config *pscancfg; + + ENTER(); + + pscancfg = pdata_buf; + + /* Set fixed field variables in scan command */ + pscan->bsstype = pscancfg->bsstype; + memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID)); + memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); + + cmd->command = cpu_to_le16(cmd_802_11_scan); + + /* size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16(sizeof(pscan->bsstype) + + sizeof(pscan->BSSID) + + pscancfg->tlvbufferlen + S_DS_GEN); + + lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n", + cmd->command, cmd->size, cmd->seqnum); + LEAVE(); + return 0; +} + +/** + * @brief This function handles the command response of scan + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-----------------------------------------------------------. + * | header (4 * sizeof(u16)): Standard command response hdr | + * .-----------------------------------------------------------. + * | bufsize (u16) : sizeof the BSS Description data | + * .-----------------------------------------------------------. + * | NumOfSet (u8) : Number of BSS Descs returned | + * .-----------------------------------------------------------. + * | BSSDescription data (variable, size given in bufsize) | + * .-----------------------------------------------------------. + * | TLV data (variable, size calculated using header->size, | + * | bufsize and sizeof the fixed fields above) | + * .-----------------------------------------------------------. + * + * @param priv A pointer to wlan_private structure + * @param resp A pointer to cmd_ds_command + * + * @return 0 or -1 + */ +int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_scan_rsp *pscan; + struct bss_descriptor newbssentry; + struct mrvlietypes_data *ptlv; + struct mrvlietypes_tsftimestamp *ptsftlv; + u8 *pbssinfo; + u16 scanrespsize; + int bytesleft; + int numintable; + int bssIdx; + int idx; + int tlvbufsize; + u64 tsfval; + + ENTER(); + + pscan = &resp->params.scanresp; + + if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) { + lbs_pr_debug(1, + "SCAN_RESP: Invalid number of AP returned (%d)!!\n", + pscan->nr_sets); + LEAVE(); + return -1; + } + + bytesleft = le16_to_cpu(pscan->bssdescriptsize); + lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft); + + scanrespsize = le16_to_cpu(resp->size); + lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n", + pscan->nr_sets); + + numintable = adapter->numinscantable; + pbssinfo = pscan->bssdesc_and_tlvbuffer; + + /* The size of the TLV buffer is equal to the entire command response + * size (scanrespsize) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bssdescriptsize as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) + + sizeof(pscan->nr_sets) + + S_DS_GEN); + + ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft); + + /* Search the TLV buffer space in the scan response for any valid TLVs */ + wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv); + + /* + * Process each scan response returned (pscan->nr_sets). Save + * the information in the newbssentry and then insert into the + * driver scan table either as an update to an existing entry + * or as an addition at the end of the table + */ + for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { + /* Zero out the newbssentry we are about to store info in */ + memset(&newbssentry, 0x00, sizeof(newbssentry)); + + /* Process the data fields and IEs returned for this BSS */ + if ((InterpretBSSDescriptionWithIE(&newbssentry, + &pbssinfo, + &bytesleft) == + 0) + && CHECK_SSID_IS_VALID(&newbssentry.ssid)) { + + lbs_pr_debug(1, + "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + newbssentry.macaddress[0], + newbssentry.macaddress[1], + newbssentry.macaddress[2], + newbssentry.macaddress[3], + newbssentry.macaddress[4], + newbssentry.macaddress[5]); + + /* + * Search the scan table for the same bssid + */ + for (bssIdx = 0; bssIdx < numintable; bssIdx++) { + if (memcmp(newbssentry.macaddress, + adapter->scantable[bssIdx]. + macaddress, + sizeof(newbssentry.macaddress)) == + 0) { + /* + * If the SSID matches as well, it is a duplicate of + * this entry. Keep the bssIdx set to this + * entry so we replace the old contents in the table + */ + if ((newbssentry.ssid.ssidlength == + adapter->scantable[bssIdx].ssid. + ssidlength) + && + (memcmp + (newbssentry.ssid.ssid, + adapter->scantable[bssIdx].ssid. + ssid, + newbssentry.ssid.ssidlength) == + 0)) { + lbs_pr_debug(1, + "SCAN_RESP: Duplicate of index: %d\n", + bssIdx); + break; + } + } + } + /* + * If the bssIdx is equal to the number of entries in the table, + * the new entry was not a duplicate; append it to the scan + * table + */ + if (bssIdx == numintable) { + /* Range check the bssIdx, keep it limited to the last entry */ + if (bssIdx == MRVDRV_MAX_BSSID_LIST) { + bssIdx--; + } else { + numintable++; + } + } + + /* + * If the TSF TLV was appended to the scan results, save the + * this entries TSF value in the networktsf field. The + * networktsf is the firmware's TSF value at the time the + * beacon or probe response was received. + */ + if (ptsftlv) { + memcpy(&tsfval, &ptsftlv->tsftable[idx], + sizeof(tsfval)); + tsfval = le64_to_cpu(tsfval); + + memcpy(&newbssentry.networktsf, + &tsfval, sizeof(newbssentry.networktsf)); + } + + /* Copy the locally created newbssentry to the scan table */ + memcpy(&adapter->scantable[bssIdx], + &newbssentry, + sizeof(adapter->scantable[bssIdx])); + + } else { + + /* error parsing/interpreting the scan response, skipped */ + lbs_pr_debug(1, "SCAN_RESP: " + "InterpretBSSDescriptionWithIE returned ERROR\n"); + } + } + + lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", + pscan->nr_sets, numintable - adapter->numinscantable, + numintable); + + /* Update the total number of BSSIDs in the scan table */ + adapter->numinscantable = numintable; + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h new file mode 100644 index 0000000..d93aa7f --- /dev/null +++ b/drivers/net/wireless/libertas/scan.h @@ -0,0 +1,216 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Interface for the wlan network scan routines + * + * Driver interface functions and type declarations for the scan module + * implemented in wlan_scan.c. + */ +#ifndef _WLAN_SCAN_H +#define _WLAN_SCAN_H + +#include "hostcmd.h" + +/** + * @brief Maximum number of channels that can be sent in a setuserscan ioctl + * + * @sa wlan_ioctl_user_scan_cfg + */ +#define WLAN_IOCTL_USER_SCAN_CHAN_MAX 50 + +//! Infrastructure BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_BSS 1 + +//! Adhoc BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_IBSS 2 + +//! Adhoc or Infrastructure BSS scan type in wlan_scan_cmd_config, no filter +#define WLAN_SCAN_BSS_TYPE_ANY 3 + +/** + * @brief Structure used internally in the wlan driver to configure a scan. + * + * Sent to the command processing module to configure the firmware + * scan command prepared by libertas_cmd_80211_scan. + * + * @sa wlan_scan_networks + * + */ +struct wlan_scan_cmd_config { + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Specific BSSID used to filter scan results in the firmware + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief length of TLVs sent in command starting at tlvBuffer + */ + int tlvbufferlen; + + /** + * @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t + * @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t + */ + u8 tlvbuffer[1]; //!< SSID TLV(s) and ChanList TLVs are stored here +}; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +struct wlan_ioctl_user_scan_chan { + u8 channumber; //!< channel Number to scan + u8 radiotype; //!< Radio type: 'B/G' band = 0, 'A' band = 1 + u8 scantype; //!< Scan type: Active = 0, Passive = 1 + u16 scantime; //!< Scan duration in milliseconds; if 0 default used +}; + +/** + * @brief IOCTL input structure to configure an immediate scan cmd to firmware + * + * Used in the setuserscan (WLAN_SET_USER_SCAN) private ioctl. Specifies + * a number of parameters to be used in general for the scan as well + * as a channel list (wlan_ioctl_user_scan_chan) for each scan period + * desired. + * + * @sa libertas_set_user_scan_ioctl + */ +struct wlan_ioctl_user_scan_cfg { + + /** + * @brief Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + u8 keeppreviousscan; //!< Do not erase the existing scan results + + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Configure the number of probe requests for active chan scans + */ + u8 numprobes; + + /** + * @brief BSSID filter sent in the firmware command to limit the results + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief SSID filter sent in the firmware command to limit the results + */ + char specificSSID[IW_ESSID_MAX_SIZE + 1]; + + /** + * @brief Variable number (fixed maximum) of channels to scan up + */ + struct wlan_ioctl_user_scan_chan chanlist[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; +}; + +/** + * @brief Structure used to store information for each beacon/probe response + */ +struct bss_descriptor { + u8 macaddress[ETH_ALEN]; + + struct WLAN_802_11_SSID ssid; + + /* WEP encryption requirement */ + u32 privacy; + + /* receive signal strength in dBm */ + long rssi; + + u32 channel; + + u16 beaconperiod; + + u32 atimwindow; + + enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; + u8 libertas_supported_rates[WLAN_SUPPORTED_RATES]; + + int extra_ie; + + u8 timestamp[8]; //!< TSF value included in the beacon/probe response + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[WLAN_SUPPORTED_RATES]; + + __le64 networktsf; //!< TSF timestamp from the current firmware TSF + + struct ieeetypes_countryinfofullset countryinfo; + + struct WPA_SUPPLICANT wpa_supplicant; + struct WPA_SUPPLICANT wpa2_supplicant; + +}; + +extern int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, + struct WLAN_802_11_SSID *ssid2); +extern int libertas_find_SSID_in_list(wlan_adapter * adapter, struct WLAN_802_11_SSID *ssid, + u8 * bssid, int mode); +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode); +extern int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode); + +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode); + +extern int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan); +extern int libertas_send_specific_BSSID_scan(wlan_private * priv, + u8 * bssid, u8 keeppreviousscan); + +extern int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_scan(wlan_private * priv, + struct cmd_ds_command *resp); + +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin); + +struct ifreq; + +struct iw_point; +struct iw_param; +struct iw_request_info; +extern int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra); +extern int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra); + +#endif /* _WLAN_SCAN_H */ diff --git a/drivers/net/wireless/libertas/thread.h b/drivers/net/wireless/libertas/thread.h new file mode 100644 index 0000000..207b8a6 --- /dev/null +++ b/drivers/net/wireless/libertas/thread.h @@ -0,0 +1,52 @@ +#ifndef __WLAN_THREAD_H_ +#define __WLAN_THREAD_H_ + +#include + +struct wlan_thread { + struct task_struct *task; + wait_queue_head_t waitq; + pid_t pid; + void *priv; +}; + +static inline void wlan_activate_thread(struct wlan_thread * thr) +{ + /** Record the thread pid */ + thr->pid = current->pid; + + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitq); +} + +static inline void wlan_deactivate_thread(struct wlan_thread * thr) +{ + ENTER(); + + thr->pid = 0; + + LEAVE(); +} + +static inline void wlan_create_thread(int (*wlanfunc) (void *), + struct wlan_thread * thr, char *name) +{ + thr->task = kthread_run(wlanfunc, thr, "%s", name); +} + +static inline int wlan_terminate_thread(struct wlan_thread * thr) +{ + ENTER(); + + /* Check if the thread is active or not */ + if (!thr->pid) { + printk(KERN_ERR "Thread does not exist\n"); + return -1; + } + kthread_stop(thr->task); + + LEAVE(); + return 0; +} + +#endif diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c new file mode 100644 index 0000000..82d0622 --- /dev/null +++ b/drivers/net/wireless/libertas/tx.c @@ -0,0 +1,285 @@ +/** + * This file contains the handling of TX in wlan driver. + */ +#include + +#include "hostcmd.h" +#include "radiotap.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "wext.h" + +/** + * @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE + * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u32 convert_radiotap_rate_to_mv(u8 rate) +{ + switch (rate) { + case 2: /* 1 Mbps */ + return 0 | (1 << 4); + case 4: /* 2 Mbps */ + return 1 | (1 << 4); + case 11: /* 5.5 Mbps */ + return 2 | (1 << 4); + case 22: /* 11 Mbps */ + return 3 | (1 << 4); + case 12: /* 6 Mbps */ + return 4 | (1 << 4); + case 18: /* 9 Mbps */ + return 5 | (1 << 4); + case 24: /* 12 Mbps */ + return 6 | (1 << 4); + case 36: /* 18 Mbps */ + return 7 | (1 << 4); + case 48: /* 24 Mbps */ + return 8 | (1 << 4); + case 72: /* 36 Mbps */ + return 9 | (1 << 4); + case 96: /* 48 Mbps */ + return 10 | (1 << 4); + case 108: /* 54 Mbps */ + return 11 | (1 << 4); + } + return 0; +} + +/** + * @brief This function processes a single packet and sends + * to IF layer + * + * @param priv A pointer to wlan_private structure + * @param skb A pointer to skb which includes TX packet + * @return 0 or -1 + */ +static int SendSinglePacket(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct txpd localtxpd; + struct txpd *plocaltxpd = &localtxpd; + u8 *p802x_hdr; + struct tx_radiotap_hdr *pradiotap_hdr; + u32 new_rate; + u8 *ptr = priv->adapter->tmptxbuf; + + ENTER(); + + if (priv->adapter->surpriseremoved) + return -1; + + if ((priv->adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { + lbs_pr_debug(1, "Tx error: Bad skb length %d : %d\n", + skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); + ret = -1; + goto done; + } + + memset(plocaltxpd, 0, sizeof(struct txpd)); + + plocaltxpd->tx_packet_length = skb->len; + + /* offset of actual data */ + plocaltxpd->tx_packet_location = sizeof(struct txpd); + + /* TxCtrl set by user or default */ + plocaltxpd->tx_control = adapter->pkttxctrl; + + p802x_hdr = skb->data; + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + + /* locate radiotap header */ + pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data; + + /* set txpd fields from the radiotap header */ + new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate); + if (new_rate != 0) { + /* erase tx_control[4:0] */ + plocaltxpd->tx_control &= ~0x1f; + /* write new tx_control[4:0] */ + plocaltxpd->tx_control |= new_rate; + } + + /* skip the radiotap header */ + p802x_hdr += sizeof(struct tx_radiotap_hdr); + plocaltxpd->tx_packet_length -= sizeof(struct tx_radiotap_hdr); + + } + /* copy destination address from 802.3 or 802.11 header */ + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); + else + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); + + lbs_dbg_hex("txpd", (u8 *) plocaltxpd, sizeof(struct txpd)); + + if (IS_MESH_FRAME(skb)) { + plocaltxpd->tx_control |= TxPD_MESH_FRAME; + } + + memcpy(ptr, plocaltxpd, sizeof(struct txpd)); + + ptr += sizeof(struct txpd); + + lbs_dbg_hex("Tx Data", (u8 *) p802x_hdr, plocaltxpd->tx_packet_length); + memcpy(ptr, p802x_hdr, plocaltxpd->tx_packet_length); + ret = libertas_sbi_host_to_card(priv, MVMS_DAT, + priv->adapter->tmptxbuf, + plocaltxpd->tx_packet_length + + sizeof(struct txpd)); + + if (ret) { + lbs_pr_debug(1, "Tx error: libertas_sbi_host_to_card failed: 0x%X\n", ret); + goto done; + } + + lbs_pr_debug(1, "SendSinglePacket succeeds\n"); + + done: + if (!ret) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } else { + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + } + + if (!ret && priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* Keep the skb to echo it back once Tx feedback is + received from FW */ + skb_orphan(skb); + /* stop processing outgoing pkts */ + netif_stop_queue(priv->wlan_dev.netdev); + /* freeze any packets already in our queues */ + priv->adapter->TxLockFlag = 1; + } else { + dev_kfree_skb_any(skb); + priv->adapter->currenttxskb = NULL; + } + + LEAVE(); + return ret; +} + + +void libertas_tx_runqueue(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + spin_lock(&adapter->txqueue_lock); + for (i = 0; i < adapter->tx_queue_idx; i++) { + struct sk_buff *skb = adapter->tx_queue_ps[i]; + spin_unlock(&adapter->txqueue_lock); + SendSinglePacket(priv, skb); + spin_lock(&adapter->txqueue_lock); + } + adapter->tx_queue_idx = 0; + spin_unlock(&adapter->txqueue_lock); +} + +static void wlan_tx_queue(wlan_private *priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + + spin_lock(&adapter->txqueue_lock); + + WARN_ON(priv->adapter->tx_queue_idx >= NR_TX_QUEUE); + adapter->tx_queue_ps[adapter->tx_queue_idx++] = skb; + if (adapter->tx_queue_idx == NR_TX_QUEUE) + netif_stop_queue(priv->wlan_dev.netdev); + else + netif_start_queue(priv->wlan_dev.netdev); + + spin_unlock(&adapter->txqueue_lock); +} + +/** + * @brief This function checks the conditions and sends packet to IF + * layer if everything is ok. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb) +{ + int ret = -1; + + ENTER(); + + lbs_dbg_hex("TX Data", skb->data, min_t(unsigned int, skb->len, 100)); + + if (priv->wlan_dev.dnld_sent) { + lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n", + priv->wlan_dev.dnld_sent); + goto done; + } + + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) { + wlan_tx_queue(priv, skb); + return ret; + } + + priv->adapter->currenttxskb = skb; + + ret = SendSinglePacket(priv, skb); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends to the host the last transmitted packet, + * filling the radiotap headers with transmission information. + * + * @param priv A pointer to wlan_private structure + * @param status A 32 bit value containing transmission status. + * + * @returns void + */ +void libertas_send_tx_feedback(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct tx_radiotap_hdr *radiotap_hdr; + u32 status = adapter->eventcause; + int txfail; + int try_count; + + if (adapter->radiomode != WLAN_RADIOMODE_RADIOTAP || + adapter->currenttxskb == NULL) + return; + + radiotap_hdr = (struct tx_radiotap_hdr *)adapter->currenttxskb->data; + + if ((adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX feedback: ", (u8 *) radiotap_hdr, + min_t(unsigned int, adapter->currenttxskb->len, 100)); + + txfail = (status >> 24); + +#if 0 + /* The version of roofnet that we've tested does not use this yet + * But it may be used in the future. + */ + if (txfail) + radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL; +#endif + try_count = (status >> 16) & 0xff; + radiotap_hdr->data_retries = (try_count) ? + (1 + adapter->txretrycount - try_count) : 0; + libertas_upload_rx_packet(priv, adapter->currenttxskb); + adapter->currenttxskb = NULL; + priv->adapter->TxLockFlag = 0; + if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); +} diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h new file mode 100644 index 0000000..09d62f8 --- /dev/null +++ b/drivers/net/wireless/libertas/types.h @@ -0,0 +1,289 @@ +/** + * This header file contains definition for global types + */ +#ifndef _WLAN_TYPES_ +#define _WLAN_TYPES_ + +#include + +/** IEEE type definitions */ +enum ieeetypes_elementid { + SSID = 0, + SUPPORTED_RATES, + FH_PARAM_SET, + DS_PARAM_SET, + CF_PARAM_SET, + TIM, + IBSS_PARAM_SET, + COUNTRY_INFO = 7, + + CHALLENGE_TEXT = 16, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + + WPA_IE = 221, + WPA2_IE = 48, + + EXTRA_IE = 133, +} __attribute__ ((packed)); + +#define CAPINFO_MASK (~(0xda00)) + +struct ieeetypes_capinfo { + u8 ess:1; + u8 ibss:1; + u8 cfpollable:1; + u8 cfpollrqst:1; + u8 privacy:1; + u8 shortpreamble:1; + u8 pbcc:1; + u8 chanagility:1; + u8 spectrummgmt:1; + u8 rsrvd3:1; + u8 shortslottime:1; + u8 apsd:1; + u8 rsvrd2:1; + u8 dsssofdm:1; + u8 rsrvd1:2; +} __attribute__ ((packed)); + +struct ieeetypes_cfparamset { + u8 elementid; + u8 len; + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + + +struct ieeetypes_ibssparamset { + u8 elementid; + u8 len; + u16 atimwindow; +} __attribute__ ((packed)); + +union IEEEtypes_ssparamset { + struct ieeetypes_cfparamset cfparamset; + struct ieeetypes_ibssparamset ibssparamset; +} __attribute__ ((packed)); + +struct ieeetypes_fhparamset { + u8 elementid; + u8 len; + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct ieeetypes_dsparamset { + u8 elementid; + u8 len; + u8 currentchan; +} __attribute__ ((packed)); + +union ieeetypes_phyparamset { + struct ieeetypes_fhparamset fhparamset; + struct ieeetypes_dsparamset dsparamset; +} __attribute__ ((packed)); + +struct ieeetypes_assocrsp { + struct ieeetypes_capinfo capability; + u16 statuscode; + u16 aid; + u8 iebuffer[1]; +} __attribute__ ((packed)); + +/** TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/* Terminating TLV type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +#define TLV_TYPE_SSID 0x0000 +#define TLV_TYPE_RATES 0x0001 +#define TLV_TYPE_PHY_FH 0x0002 +#define TLV_TYPE_PHY_DS 0x0003 +#define TLV_TYPE_CF 0x0004 +#define TLV_TYPE_IBSS 0x0006 + +#define TLV_TYPE_DOMAIN 0x0007 + +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) +#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) +#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) +#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) +#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) + +/** TLV related data structures*/ +struct mrvlietypesheader { + u16 type; + u16 len; +} __attribute__ ((packed)); + +struct mrvlietypes_data { + struct mrvlietypesheader header; + u8 Data[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ratesparamset { + struct mrvlietypesheader header; + u8 rates[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ssidparamset { + struct mrvlietypesheader header; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_wildcardssidparamset { + struct mrvlietypesheader header; + u8 MaxSsidlength; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct chanscanmode { + u8 passivescan:1; + u8 disablechanfilt:1; + u8 reserved_2_7:6; +} __attribute__ ((packed)); + +struct chanscanparamset { + u8 radiotype; + u8 channumber; + struct chanscanmode chanscanmode; + u16 minscantime; + u16 maxscantime; +} __attribute__ ((packed)); + +struct mrvlietypes_chanlistparamset { + struct mrvlietypesheader header; + struct chanscanparamset chanscanparam[1]; +} __attribute__ ((packed)); + +struct cfparamset { + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + +struct ibssparamset { + u16 atimwindow; +} __attribute__ ((packed)); + +struct mrvlietypes_ssparamset { + struct mrvlietypesheader header; + union { + struct cfparamset cfparamset[1]; + struct ibssparamset ibssparamset[1]; + } cf_ibss; +} __attribute__ ((packed)); + +struct fhparamset { + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct dsparamset { + u8 currentchan; +} __attribute__ ((packed)); + +struct mrvlietypes_phyparamset { + struct mrvlietypesheader header; + union { + struct fhparamset fhparamset[1]; + struct dsparamset dsparamset[1]; + } fh_ds; +} __attribute__ ((packed)); + +struct mrvlietypes_rsnparamset { + struct mrvlietypesheader header; + u8 rsnie[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_tsftimestamp { + struct mrvlietypesheader header; + __le64 tsftable[1]; +} __attribute__ ((packed)); + +/** Local Power capability */ +struct mrvlietypes_powercapability { + struct mrvlietypesheader header; + s8 minpower; + s8 maxpower; +} __attribute__ ((packed)); + +struct mrvlietypes_rssithreshold { + struct mrvlietypesheader header; + u8 rssivalue; + u8 rssifreq; +} __attribute__ ((packed)); + +struct mrvlietypes_snrthreshold { + struct mrvlietypesheader header; + u8 snrvalue; + u8 snrfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_failurecount { + struct mrvlietypesheader header; + u8 failvalue; + u8 Failfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_beaconsmissed { + struct mrvlietypesheader header; + u8 beaconmissed; + u8 reserved; +} __attribute__ ((packed)); + +struct mrvlietypes_numprobes { + struct mrvlietypesheader header; + u16 numprobes; +} __attribute__ ((packed)); + +struct mrvlietypes_bcastprobe { + struct mrvlietypesheader header; + u16 bcastprobe; +} __attribute__ ((packed)); + +struct mrvlietypes_numssidprobe { + struct mrvlietypesheader header; + u16 numssidprobe; +} __attribute__ ((packed)); + +struct led_pin { + u8 led; + u8 pin; +} __attribute__ ((packed)); + +struct mrvlietypes_ledgpio { + struct mrvlietypesheader header; + struct led_pin ledpin[1]; +} __attribute__ ((packed)); + +#endif /* _WLAN_TYPES_ */ diff --git a/drivers/net/wireless/libertas/version.h b/drivers/net/wireless/libertas/version.h new file mode 100644 index 0000000..e86f65a --- /dev/null +++ b/drivers/net/wireless/libertas/version.h @@ -0,0 +1,8 @@ +#define DRIVER_RELEASE_VERSION "320.p0" +const char libertas_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION +#ifdef DEBUG + "-dbg" +#endif + ""; + + diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c new file mode 100644 index 0000000..4a52336 --- /dev/null +++ b/drivers/net/wireless/libertas/wext.c @@ -0,0 +1,2769 @@ +/** + * This file contains ioctl functions + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "version.h" +#include "wext.h" +#include "assoc.h" + + +/** + * @brief Convert mw value to dbm value + * + * @param mw the value of mw + * @return the value of dbm + */ +static int mw_to_dbm(int mw) +{ + if (mw < 2) + return 0; + else if (mw < 3) + return 3; + else if (mw < 4) + return 5; + else if (mw < 6) + return 7; + else if (mw < 7) + return 8; + else if (mw < 8) + return 9; + else if (mw < 10) + return 10; + else if (mw < 13) + return 11; + else if (mw < 16) + return 12; + else if (mw < 20) + return 13; + else if (mw < 25) + return 14; + else if (mw < 32) + return 15; + else if (mw < 40) + return 16; + else if (mw < 50) + return 17; + else if (mw < 63) + return 18; + else if (mw < 79) + return 19; + else if (mw < 100) + return 20; + else + return 21; +} + +/** + * @brief Find the channel frequency power info with specific channel + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param channel the channel for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +struct chan_freq_power *libertas_find_cfp_by_band_and_channel(wlan_adapter * adapter, + u8 band, u16 channel) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].channel == channel) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && channel) + lbs_pr_debug(1, "libertas_find_cfp_by_band_and_channel(): cannot find " + "cfp by band %d & channel %d\n", band, channel); + + return cfp; +} + +/** + * @brief Find the channel frequency power info with specific frequency + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param freq the frequency for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +static struct chan_freq_power *find_cfp_by_band_and_freq(wlan_adapter * adapter, + u8 band, u32 freq) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].freq == freq) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && freq) + lbs_pr_debug(1, "find_cfp_by_band_and_freql(): cannot find cfp by " + "band %d & freq %d\n", band, freq); + + return cfp; +} + +static int updatecurrentchannel(wlan_private * priv) +{ + int ret; + + /* + ** the channel in f/w could be out of sync, get the current channel + */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_get, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "Current channel = %d\n", + priv->adapter->curbssparams.channel); + + return ret; +} + +static int setcurrentchannel(wlan_private * priv, int channel) +{ + lbs_pr_debug(1, "Set channel = %d\n", channel); + + /* + ** Current channel is not set to adhocchannel requested, set channel + */ + return (libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_set, + cmd_option_waitforrsp, 0, &channel)); +} + +static int changeadhocchannel(wlan_private * priv, int channel) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + adapter->adhocchannel = channel; + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel == adapter->adhocchannel) { + /* adhocchannel is set to the current channel already */ + LEAVE(); + return 0; + } + + lbs_pr_debug(1, "Updating channel from %d to %d\n", + adapter->curbssparams.channel, adapter->adhocchannel); + + setcurrentchannel(priv, adapter->adhocchannel); + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel != adapter->adhocchannel) { + lbs_pr_debug(1, "failed to updated channel to %d, channel = %d\n", + adapter->adhocchannel, adapter->curbssparams.channel); + LEAVE(); + return -1; + } + + if (adapter->connect_status == libertas_connected) { + int i; + struct WLAN_802_11_SSID curadhocssid; + + lbs_pr_debug(1, "channel Changed while in an IBSS\n"); + + /* Copy the current ssid */ + memcpy(&curadhocssid, &adapter->curbssparams.ssid, + sizeof(struct WLAN_802_11_SSID)); + + /* Exit Adhoc mode */ + lbs_pr_debug(1, "In changeadhocchannel(): Sending Adhoc Stop\n"); + ret = libertas_stop_adhoc_network(priv); + + if (ret) { + LEAVE(); + return ret; + } + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &curadhocssid, 0); + + // find out the BSSID that matches the current SSID + i = libertas_find_SSID_in_list(adapter, &curadhocssid, NULL, + wlan802_11ibss); + + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List," + "so join\n", i); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + // else send START command + lbs_pr_debug(1, "SSID not found in list, " + "so creating adhoc with ssid = %s\n", + curadhocssid.ssid); + libertas_start_adhoc_network(priv, &curadhocssid); + } // end of else (START command) + } + + LEAVE(); + return 0; +} + +/** + * @brief Set Radio On/OFF + * + * @param priv A pointer to wlan_private structure + * @option Radio Option + * @return 0 --success, otherwise fail + */ +int wlan_radio_ioctl(wlan_private * priv, u8 option) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->radioon != option) { + lbs_pr_debug(1, "Switching %s the Radio\n", option ? "On" : "Off"); + adapter->radioon = option; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + } + + LEAVE(); + return ret; +} + +/** + * @brief Copy rates + * + * @param dest A pointer to Dest Buf + * @param src A pointer to Src Buf + * @param len The len of Src Buf + * @return Number of rates copyed + */ +static inline int copyrates(u8 * dest, int pos, u8 * src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= sizeof(u8) * WLAN_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/** + * @brief Get active data rates + * + * @param adapter A pointer to wlan_adapter structure + * @param rate The buf to return the active rates + * @return The number of rates + */ +static int get_active_data_rates(wlan_adapter * adapter, + u8* rates) +{ + int k = 0; + + ENTER(); + + if (adapter->connect_status != libertas_connected) { + if (adapter->inframode == wlan802_11infrastructure) { + //Infra. mode + lbs_pr_debug(1, "Infra\n"); + k = copyrates(rates, k, libertas_supported_rates, + sizeof(libertas_supported_rates)); + } else { + //ad-hoc mode + lbs_pr_debug(1, "Adhoc G\n"); + k = copyrates(rates, k, libertas_adhoc_rates_g, + sizeof(libertas_adhoc_rates_g)); + } + } else { + k = copyrates(rates, 0, adapter->curbssparams.datarates, + adapter->curbssparams.numofrates); + } + + LEAVE(); + + return k; +} + +static int wlan_get_name(struct net_device *dev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + const char *cp; + char comm[6] = { "COMM-" }; + char mrvl[6] = { "MRVL-" }; + int cnt; + + ENTER(); + + strcpy(cwrq, mrvl); + + cp = strstr(libertas_driver_version, comm); + if (cp == libertas_driver_version) //skip leading "COMM-" + cp = libertas_driver_version + strlen(comm); + else + cp = libertas_driver_version; + + cnt = strlen(mrvl); + cwrq += cnt; + while (cnt < 16 && (*cp != '-')) { + *cwrq++ = toupper(*cp++); + cnt++; + } + *cwrq = '\0'; + + LEAVE(); + + return 0; +} + +static int wlan_get_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct chan_freq_power *cfp; + + ENTER(); + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, + adapter->curbssparams.channel); + + if (!cfp) { + if (adapter->curbssparams.channel) + lbs_pr_debug(1, "Invalid channel=%d\n", + adapter->curbssparams.channel); + return -EINVAL; + } + + fwrq->m = (long)cfp->freq * 100000; + fwrq->e = 1; + + lbs_pr_debug(1, "freq=%u\n", fwrq->m); + + LEAVE(); + return 0; +} + +static int wlan_get_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->connect_status == libertas_connected) { + memcpy(awrq->sa_data, adapter->curbssparams.bssid, ETH_ALEN); + } else { + memset(awrq->sa_data, 0, ETH_ALEN); + } + awrq->sa_family = ARPHRD_ETHER; + + LEAVE(); + return 0; +} + +static int wlan_set_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Check the size of the string + */ + + if (dwrq->length > 16) { + return -E2BIG; + } + + mutex_lock(&adapter->lock); + memset(adapter->nodename, 0, sizeof(adapter->nodename)); + memcpy(adapter->nodename, extra, dwrq->length); + mutex_unlock(&adapter->lock); + + LEAVE(); + return 0; +} + +static int wlan_get_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Get the Nick Name saved + */ + + mutex_lock(&adapter->lock); + strncpy(extra, adapter->nodename, 16); + mutex_unlock(&adapter->lock); + + extra[16] = '\0'; + + /* + * If none, we may want to get the one that was set + */ + + /* + * Push it out ! + */ + dwrq->length = strlen(extra) + 1; + + LEAVE(); + return 0; +} + +static int wlan_set_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + adapter->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE; + } else { + if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE) + return -EINVAL; + adapter->rtsthsd = rthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, &rthr); + + LEAVE(); + return ret; +} + +static int wlan_get_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->rtsthsd = 0; + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->rtsthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE) + || (vwrq->value > MRVDRV_RTS_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return 0; +} + +static int wlan_set_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + int fthr = vwrq->value; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->disabled) { + adapter->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE; + } else { + if (fthr < MRVDRV_FRAG_MIN_VALUE + || fthr > MRVDRV_FRAG_MAX_VALUE) + return -EINVAL; + adapter->fragthsd = fthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, &fthr); + LEAVE(); + return ret; +} + +static int wlan_get_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->fragthsd = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->fragthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE) + || (vwrq->value > MRVDRV_FRAG_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return ret; +} + +static int wlan_get_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (adapter->inframode) { + case wlan802_11ibss: + *uwrq = IW_MODE_ADHOC; + break; + + case wlan802_11infrastructure: + *uwrq = IW_MODE_INFRA; + break; + + default: + case wlan802_11autounknown: + *uwrq = IW_MODE_AUTO; + break; + } + + LEAVE(); + return 0; +} + +static int wlan_get_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_get, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "TXPOWER GET %d dbm.\n", adapter->txpowerlevel); + vwrq->value = adapter->txpowerlevel; + vwrq->fixed = 1; + if (adapter->radioon) { + vwrq->disabled = 0; + vwrq->flags = IW_TXPOW_DBM; + } else { + vwrq->disabled = 1; + } + + LEAVE(); + return 0; +} + +static int wlan_set_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->flags == IW_RETRY_LIMIT) { + /* The MAC has a 4-bit Total_Tx_Count register + Total_Tx_Count = 1 + Tx_Retry_Count */ +#define TX_RETRY_MIN 0 +#define TX_RETRY_MAX 14 + if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX) + return -EINVAL; + + /* Adding 1 to convert retry count to try count */ + adapter->txretrycount = vwrq->value + 1; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } else { + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + +static int wlan_get_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + adapter->txretrycount = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + if (ret) { + LEAVE(); + return ret; + } + vwrq->disabled = 0; + if (!vwrq->flags) { + vwrq->flags = IW_RETRY_LIMIT; + /* Subtract 1 to convert try count to retry count */ + vwrq->value = adapter->txretrycount - 1; + } + + LEAVE(); + return 0; +} + +static inline void sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/* data rate listing + MULTI_BANDS: + abg a b b/g + Infra G(12) A(8) B(4) G(12) + Adhoc A+B(12) A(8) B(4) B(4) + + non-MULTI_BANDS: + b b/g + Infra B(4) G(12) + Adhoc B(4) B(4) + */ +/** + * @brief Get Range Info + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_range(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int i, j; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_range *range = (struct iw_range *)extra; + struct chan_freq_power *cfp; + u8 rates[WLAN_SUPPORTED_RATES]; + + u8 flag = 0; + + ENTER(); + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + memset(rates, 0, sizeof(rates)); + range->num_bitrates = get_active_data_rates(adapter, rates); + + for (i = 0; i < min_t(__u8, range->num_bitrates, IW_MAX_BITRATES) && rates[i]; + i++) { + range->bitrate[i] = (rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + lbs_pr_debug(1, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, + range->num_bitrates); + + range->num_frequency = 0; + if (priv->adapter->enable11d && + adapter->connect_status == libertas_connected) { + u8 chan_no; + u8 band; + + struct parsed_region_chan_11d *parsed_region_chan = + &adapter->parsed_region_chan; + + if (parsed_region_chan == NULL) { + lbs_pr_debug(1, "11D:parsed_region_chan is NULL\n"); + LEAVE(); + return 0; + } + band = parsed_region_chan->band; + lbs_pr_debug(1, "band=%d NoOfChan=%d\n", band, + parsed_region_chan->nr_chan); + + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (i < parsed_region_chan->nr_chan); i++) { + chan_no = parsed_region_chan->chanpwr[i].chan; + lbs_pr_debug(1, "chan_no=%d\n", chan_no); + range->freq[range->num_frequency].i = (long)chan_no; + range->freq[range->num_frequency].m = + (long)libertas_chan_2_freq(chan_no, band) * 100000; + range->freq[range->num_frequency].e = 1; + range->num_frequency++; + } + flag = 1; + } + if (!flag) { + for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (j < sizeof(adapter->region_channel) + / sizeof(adapter->region_channel[0])); j++) { + cfp = adapter->region_channel[j].CFP; + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && adapter->region_channel[j].valid + && cfp + && (i < adapter->region_channel[j].nrcfp); i++) { + range->freq[range->num_frequency].i = + (long)cfp->channel; + range->freq[range->num_frequency].m = + (long)cfp->freq * 100000; + range->freq[range->num_frequency].e = 1; + cfp++; + range->num_frequency++; + } + } + } + + lbs_pr_debug(1, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MRVDRV_RTS_MIN_VALUE; + range->max_rts = MRVDRV_RTS_MAX_VALUE; + range->min_frag = MRVDRV_FRAG_MIN_VALUE; + range->max_frag = MRVDRV_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + + range->min_pmp = 1000000; + range->max_pmp = 120000000; + range->min_pmt = 1000; + range->max_pmt = 1000000; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = TX_RETRY_MIN; + range->max_retry = TX_RETRY_MAX; + + /* + * Set the qual, level and noise range values + */ + range->max_qual.qual = 100; + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->avg_qual.qual = 70; + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->sensitivity = 0; + + /* + * Setup the supported power level ranges + */ + memset(range->txpower, 0, sizeof(range->txpower)); + range->txpower[0] = 5; + range->txpower[1] = 7; + range->txpower[2] = 9; + range->txpower[3] = 11; + range->txpower[4] = 13; + range->txpower[5] = 15; + range->txpower[6] = 17; + range->txpower[7] = 19; + + range->num_txpower = 8; + range->txpower_capa = IW_TXPOW_DBM; + range->txpower_capa |= IW_TXPOW_RANGE; + + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + if (adapter->fwcapinfo & FW_CAPINFO_WPA) { + range->enc_capa = IW_ENC_CAPA_WPA + | IW_ENC_CAPA_WPA2 + | IW_ENC_CAPA_CIPHER_TKIP + | IW_ENC_CAPA_CIPHER_CCMP; + } + + LEAVE(); + return 0; +} + +static int wlan_set_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + if (vwrq->disabled) { + adapter->psmode = wlan802_11powermodecam; + if (adapter->psstate != PS_STATE_FULL_POWER) { + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + return 0; + } + + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + lbs_pr_debug(1, + "Setting power timeout command is not supported\n"); + return -EINVAL; + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { + lbs_pr_debug(1, "Setting power period command is not supported\n"); + return -EINVAL; + } + + if (adapter->psmode != wlan802_11powermodecam) { + return 0; + } + + adapter->psmode = wlan802_11powermodemax_psp; + + if (adapter->connect_status == libertas_connected) { + libertas_ps_sleep(priv, cmd_option_waitforrsp); + } + + LEAVE(); + return 0; +} + +static int wlan_get_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int mode; + + ENTER(); + + mode = adapter->psmode; + + if ((vwrq->disabled = (mode == wlan802_11powermodecam)) + || adapter->connect_status == libertas_disconnected) { + LEAVE(); + return 0; + } + + vwrq->value = 0; + + LEAVE(); + return 0; +} + +/* + * iwpriv settable callbacks + */ + +static const iw_handler wlan_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; + +static const struct iw_priv_args wlan_private_args[] = { + /* + * { cmd, set_args, get_args, name } + */ + { + WLANSCAN_TYPE, + IW_PRIV_TYPE_CHAR | 8, + IW_PRIV_TYPE_CHAR | 8, + "scantype"}, + + { + WLAN_SETINT_GETINT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + { + WLANNF, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getNF"}, + { + WLANRSSI, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getRSSI"}, + { + WLANENABLE11D, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "enable11d"}, + { + WLANADHOCGRATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "adhocgrate"}, + + { + WLAN_SUBCMD_SET_PRESCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "prescan"}, + { + WLAN_SETONEINT_GETONEINT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + ""}, + { + WLAN_BEACON_INTERVAL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "bcninterval"}, + { + WLAN_LISTENINTRVL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "lolisteninter"}, + { + WLAN_TXCONTROL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "txcontrol"}, + { + WLAN_NULLPKTINTERVAL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "psnullinterval"}, + /* Using iwpriv sub-command feature */ + { + WLAN_SETONEINT_GETNONE, /* IOCTL: 24 */ + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + ""}, + + { + WLAN_SUBCMD_SETRXANTENNA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setrxant"}, + { + WLAN_SUBCMD_SETTXANTENNA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "settxant"}, + { + WLANSETAUTHALG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "authalgs", + }, + { + WLANSET8021XAUTHALG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "8021xauthalgs", + }, + { + WLANSETENCRYPTIONMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "encryptionmode", + }, + { + WLANSETREGION, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setregioncode"}, + { + WLAN_SET_LISTEN_INTERVAL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setlisteninter"}, + { + WLAN_SET_MULTIPLE_DTIM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setmultipledtim"}, + { + WLAN_SET_ATIM_WINDOW, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "atimwindow"}, + { + WLANSETBCNAVG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setbcnavg"}, + { + WLANSETDATAAVG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setdataavg"}, + { + WLAN_SET_LINKMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "linkmode"}, + { + WLAN_SET_RADIOMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "radiomode"}, + { + WLAN_SET_DEBUGMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "debugmode"}, + { + WLAN_SUBCMD_MESH_SET_TTL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "mesh_set_ttl"}, + { + WLAN_SETNONE_GETONEINT, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + { + WLANGETREGION, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getregioncode"}, + { + WLAN_GET_LISTEN_INTERVAL, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getlisteninter"}, + { + WLAN_GET_MULTIPLE_DTIM, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getmultipledtim"}, + { + WLAN_GET_TX_RATE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "gettxrate"}, + { + WLANGETBCNAVG, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getbcnavg"}, + { + WLAN_GET_LINKMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_linkmode"}, + { + WLAN_GET_RADIOMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_radiomode"}, + { + WLAN_GET_DEBUGMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_debugmode"}, + { + WLAN_SUBCMD_FWT_CLEANUP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_cleanup"}, + { + WLAN_SUBCMD_FWT_TIME, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_time"}, + { + WLAN_SUBCMD_MESH_GET_TTL, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "mesh_get_ttl"}, + { + WLAN_SETNONE_GETTWELVE_CHAR, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + ""}, + { + WLAN_SUBCMD_GETRXANTENNA, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "getrxant"}, + { + WLAN_SUBCMD_GETTXANTENNA, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "gettxant"}, + { + WLAN_GET_TSF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "gettsf"}, + { + WLAN_SETNONE_GETNONE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + ""}, + { + WLANDEAUTH, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "deauth"}, + { + WLANADHOCSTOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "adhocstop"}, + { + WLANRADIOON, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "radioon"}, + { + WLANRADIOOFF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "radiooff"}, + { + WLANWLANIDLEON, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "wlanidle-on"}, + { + WLANWLANIDLEOFF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "wlanidle-off"}, + { + WLAN_SUBCMD_FWT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "fwt_reset"}, + { + WLAN_SUBCMD_BT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "bt_reset"}, + { + WLAN_SET128CHAR_GET128CHAR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + ""}, + /* BT Management */ + { + WLAN_SUBCMD_BT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_add"}, + { + WLAN_SUBCMD_BT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_del"}, + { + WLAN_SUBCMD_BT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_list"}, + /* FWT Management */ + { + WLAN_SUBCMD_FWT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_add"}, + { + WLAN_SUBCMD_FWT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_del"}, + { + WLAN_SUBCMD_FWT_LOOKUP, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_lookup"}, + { + WLAN_SUBCMD_FWT_LIST_NEIGHBOR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_neigh"}, + { + WLAN_SUBCMD_FWT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list"}, + { + WLAN_SUBCMD_FWT_LIST_ROUTE, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_route"}, + { + WLANSCAN_MODE, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "scanmode"}, + { + WLAN_GET_ADHOC_STATUS, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "getadhocstatus"}, + { + WLAN_SETNONE_GETWORDCHAR, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 128, + ""}, + { + WLANSETWPAIE, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 24, + IW_PRIV_TYPE_NONE, + "setwpaie"}, + { + WLANGETLOG, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE, + "getlog"}, + { + WLAN_SET_GET_SIXTEEN_INT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + ""}, + { + WLAN_TPCCFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "tpccfg"}, + { + WLAN_POWERCFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "powercfg"}, + { + WLAN_AUTO_FREQ_SET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "setafc"}, + { + WLAN_AUTO_FREQ_GET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getafc"}, + { + WLAN_SCANPROBES, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "scanprobes"}, + { + WLAN_LED_GPIO_CTRL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "ledgpio"}, + { + WLAN_ADAPT_RATESET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "rateadapt"}, + { + WLAN_INACTIVITY_TIMEOUT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "inactivityto"}, + { + WLANSNR, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getSNR"}, + { + WLAN_GET_RATE, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getrate"}, + { + WLAN_GET_RXINFO, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getrxinfo"}, +}; + +static struct iw_statistics *wlan_get_wireless_stats(struct net_device *dev) +{ + enum { + POOR = 30, + FAIR = 60, + GOOD = 80, + VERY_GOOD = 90, + EXCELLENT = 95, + PERFECT = 100 + }; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 rssi_qual; + u32 tx_qual; + u32 quality = 0; + int stats_valid = 0; + u8 rssi; + u32 tx_retries; + + ENTER(); + + priv->wstats.status = adapter->inframode; + + /* If we're not associated, all quality values are meaningless */ + if (adapter->connect_status != libertas_connected) + goto out; + + /* Quality by RSSI */ + priv->wstats.qual.level = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + priv->wstats.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + + lbs_pr_debug(1, "Signal Level = %#x\n", priv->wstats.qual.level); + lbs_pr_debug(1, "Noise = %#x\n", priv->wstats.qual.noise); + + rssi = priv->wstats.qual.level - priv->wstats.qual.noise; + if (rssi < 15) + rssi_qual = rssi * POOR / 10; + else if (rssi < 20) + rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR; + else if (rssi < 30) + rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR; + else if (rssi < 40) + rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) / + 10 + GOOD; + else + rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) / + 10 + VERY_GOOD; + quality = rssi_qual; + + /* Quality by TX errors */ + priv->wstats.discard.retries = priv->stats.tx_errors; + + tx_retries = adapter->logmsg.retry; + + if (tx_retries > 75) + tx_qual = (90 - tx_retries) * POOR / 15; + else if (tx_retries > 70) + tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; + else if (tx_retries > 65) + tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; + else if (tx_retries > 50) + tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / + 15 + GOOD; + else + tx_qual = (50 - tx_retries) * + (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; + quality = min(quality, tx_qual); + + priv->wstats.discard.code = adapter->logmsg.wepundecryptable; + priv->wstats.discard.fragment = adapter->logmsg.fcserror; + priv->wstats.discard.retries = tx_retries; + priv->wstats.discard.misc = adapter->logmsg.ackfailure; + + /* Calculate quality */ + priv->wstats.qual.qual = max(quality, (u32)100); + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + stats_valid = 1; + + /* update stats asynchronously for future calls */ + libertas_prepare_and_send_command(priv, cmd_802_11_rssi, 0, + 0, 0, NULL); + libertas_prepare_and_send_command(priv, cmd_802_11_get_log, 0, + 0, 0, NULL); +out: + if (!stats_valid) { + priv->wstats.miss.beacon = 0; + priv->wstats.discard.retries = 0; + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED; + priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + } + + LEAVE (); + return &priv->wstats; + + +} + +static int wlan_set_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rc = -EINPROGRESS; /* Call commit handler */ + struct chan_freq_power *cfp; + + ENTER(); + + /* + * If setting by frequency, convert to a channel + */ + if (fwrq->e == 1) { + + long f = fwrq->m / 100000; + int c = 0; + + cfp = find_cfp_by_band_and_freq(adapter, 0, f); + if (!cfp) { + lbs_pr_debug(1, "Invalid freq=%ld\n", f); + return -EINVAL; + } + + c = (int)cfp->channel; + + if (c < 0) + return -EINVAL; + + fwrq->e = 0; + fwrq->m = c; + } + + /* + * Setting by channel number + */ + if (fwrq->m > 1000 || fwrq->e > 0) { + rc = -EOPNOTSUPP; + } else { + int channel = fwrq->m; + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, channel); + if (!cfp) { + rc = -EINVAL; + } else { + if (adapter->inframode == wlan802_11ibss) { + rc = changeadhocchannel(priv, channel); + /* If station is WEP enabled, send the + * command to set WEP in firmware + */ + if (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) { + lbs_pr_debug(1, "set_freq: WEP enabled\n"); + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, + NULL); + + if (ret) { + LEAVE(); + return ret; + } + + adapter->currentpacketfilter |= + cmd_act_mac_wep_enable; + + libertas_set_mac_packet_filter(priv); + } + } else { + rc = -EOPNOTSUPP; + } + } + } + + LEAVE(); + return rc; +} + +/** + * @brief use index to get the data rate + * + * @param index The index of data rate + * @return data rate or 0 + */ +u32 libertas_index_to_data_rate(u8 index) +{ + if (index >= sizeof(libertas_wlan_data_rates)) + index = 0; + + return libertas_wlan_data_rates[index]; +} + +/** + * @brief use rate to get the index + * + * @param rate data rate + * @return index or 0 + */ +u8 libertas_data_rate_to_index(u32 rate) +{ + u8 *ptr; + + if (rate) + if ((ptr = memchr(libertas_wlan_data_rates, (u8) rate, + sizeof(libertas_wlan_data_rates)))) + return (ptr - libertas_wlan_data_rates); + + return 0; +} + +static int wlan_set_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 data_rate; + u16 action; + int ret = 0; + u8 rates[WLAN_SUPPORTED_RATES]; + u8 *rate; + + ENTER(); + + lbs_pr_debug(1, "Vwrq->value = %d\n", vwrq->value); + + if (vwrq->value == -1) { + action = cmd_act_set_tx_auto; // Auto + adapter->is_datarate_auto = 1; + adapter->datarate = 0; + } else { + if (vwrq->value % 100000) { + return -EINVAL; + } + + data_rate = vwrq->value / 500000; + + memset(rates, 0, sizeof(rates)); + get_active_data_rates(adapter, rates); + rate = rates; + while (*rate) { + lbs_pr_debug(1, "Rate=0x%X Wanted=0x%X\n", *rate, + data_rate); + if ((*rate & 0x7f) == (data_rate & 0x7f)) + break; + rate++; + } + if (!*rate) { + lbs_pr_alert( "The fixed data rate 0x%X is out " + "of range.\n", data_rate); + return -EINVAL; + } + + adapter->datarate = data_rate; + action = cmd_act_set_tx_fix_rate; + adapter->is_datarate_auto = 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + action, cmd_option_waitforrsp, 0, NULL); + + LEAVE(); + return ret; +} + +static int wlan_get_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->is_datarate_auto) { + vwrq->fixed = 0; + } else { + vwrq->fixed = 1; + } + + vwrq->value = adapter->datarate * 500000; + + LEAVE(); + return 0; +} + +static int wlan_set_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; + + ENTER(); + + switch (*uwrq) { + case IW_MODE_ADHOC: + lbs_pr_debug(1, "Wanted mode is ad-hoc: current datarate=%#x\n", + adapter->datarate); + new_mode = wlan802_11ibss; + adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; + break; + + case IW_MODE_INFRA: + lbs_pr_debug(1, "Wanted mode is Infrastructure\n"); + new_mode = wlan802_11infrastructure; + break; + + case IW_MODE_AUTO: + lbs_pr_debug(1, "Wanted mode is Auto\n"); + new_mode = wlan802_11autounknown; + break; + + default: + lbs_pr_debug(1, "Wanted mode is Unknown: 0x%x\n", *uwrq); + return -EINVAL; + } + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + } else { + assoc_req->mode = new_mode; + } + + if (ret == 0) { + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + + +/** + * @brief Get Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, u8 * extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + ENTER(); + + lbs_pr_debug(1, "flags=0x%x index=%d length=%d wep_tx_keyidx=%d\n", + dwrq->flags, index, dwrq->length, adapter->wep_tx_keyidx); + + dwrq->flags = 0; + + /* Authentication method */ + switch (adapter->secinfo.authmode) { + case wlan802_11authmodeopen: + dwrq->flags = IW_ENCODE_OPEN; + break; + + case wlan802_11authmodeshared: + case wlan802_11authmodenetworkEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) + || adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + memset(extra, 0, 16); + + mutex_lock(&adapter->lock); + + /* Default to returning current transmit key */ + if (index < 0) + index = adapter->wep_tx_keyidx; + + if ((adapter->wep_keys[index].len) && + (adapter->secinfo.WEPstatus == wlan802_11WEPenabled)) { + memcpy(extra, adapter->wep_keys[index].key, + adapter->wep_keys[index].len); + dwrq->length = adapter->wep_keys[index].len; + + dwrq->flags |= (index + 1); + /* Return WEP enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else if ((adapter->secinfo.WPAenabled) + || (adapter->secinfo.WPA2enabled)) { + /* return WPA enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + mutex_unlock(&adapter->lock); + + dwrq->flags |= IW_ENCODE_NOKEY; + + lbs_pr_debug(1, "key:%02x:%02x:%02x:%02x:%02x:%02x keylen=%d\n", + extra[0], extra[1], extra[2], + extra[3], extra[4], extra[5], dwrq->length); + + lbs_pr_debug(1, "Return flags=0x%x\n", dwrq->flags); + + LEAVE(); + return 0; +} + +/** + * @brief Set Encryption key (internal) + * + * @param priv A pointer to private card structure + * @param key_material A pointer to key material + * @param key_length length of key material + * @param index key index to set + * @param set_tx_key Force set TX key (1 = yes, 0 = no) + * @return 0 --success, otherwise fail + */ +static int wlan_set_wep_key(struct assoc_request *assoc_req, + const char *key_material, + u16 key_length, + u16 index, + int set_tx_key) +{ + struct WLAN_802_11_KEY *pkey; + + ENTER(); + + /* Paranoid validation of key index */ + if (index > 3) { + LEAVE(); + return -EINVAL; + } + + /* validate max key length */ + if (key_length > KEY_LEN_WEP_104) { + LEAVE(); + return -EINVAL; + } + + pkey = &assoc_req->wep_keys[index]; + + if (key_length > 0) { + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + pkey->type = KEY_TYPE_ID_WEP; + + /* Standardize the key length */ + pkey->len = (key_length > KEY_LEN_WEP_40) ? + KEY_LEN_WEP_104 : KEY_LEN_WEP_40; + memcpy(pkey->key, key_material, key_length); + } + + if (set_tx_key) { + /* Ensure the chosen key is valid */ + if (!pkey->len) { + lbs_pr_debug(1, "key not set, so cannot enable it\n"); + LEAVE(); + return -EINVAL; + } + assoc_req->wep_tx_keyidx = index; + } + + assoc_req->secinfo.WEPstatus = wlan802_11WEPenabled; + + LEAVE(); + return 0; +} + +static int validate_key_index(u16 def_index, u16 raw_index, + u16 *out_index, u16 *is_default) +{ + if (!out_index || !is_default) + return -EINVAL; + + /* Verify index if present, otherwise use default TX key index */ + if (raw_index > 0) { + if (raw_index > 4) + return -EINVAL; + *out_index = raw_index - 1; + } else { + *out_index = def_index; + *is_default = 1; + } + return 0; +} + +static void disable_wep(struct assoc_request *assoc_req) +{ + int i; + + /* Set Open System auth mode */ + assoc_req->secinfo.authmode = wlan802_11authmodeopen; + + /* Clear WEP keys and mark WEP as disabled */ + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + for (i = 0; i < 4; i++) + assoc_req->wep_keys[i].len = 0; + + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); +} + +/** + * @brief Set Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + u16 is_default = 0, index = 0, set_tx_key = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->flags & IW_ENCODE_DISABLED) { + disable_wep (assoc_req); + goto out; + } + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) { + ret = -EINVAL; + goto out; + } + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, set the TX key. + */ + if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) + || (dwrq->length == 0 && !is_default)) + set_tx_key = 1; + + ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key); + if (ret) + goto out; + + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.authmode = wlan802_11authmodeshared; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.authmode = wlan802_11authmodeopen; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Get Extended Encryption key (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 on success, otherwise failure + */ +static int wlan_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = -EINVAL; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int index, max_key_len; + + ENTER(); + + max_key_len = dwrq->length - sizeof(*ext); + if (max_key_len < 0) + goto out; + + index = dwrq->flags & IW_ENCODE_INDEX; + if (index) { + if (index < 1 || index > 4) + goto out; + index--; + } else { + index = adapter->wep_tx_keyidx; + } + + if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY && + ext->alg != IW_ENCODE_ALG_WEP) { + if (index != 0 || adapter->inframode != wlan802_11infrastructure) + goto out; + } + + dwrq->flags = index + 1; + memset(ext, 0, sizeof(*ext)); + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) + && !adapter->secinfo.WPAenabled && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + dwrq->flags |= IW_ENCODE_DISABLED; + } else { + u8 *key = NULL; + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_WEP; + ext->key_len = adapter->wep_keys[index].len; + key = &adapter->wep_keys[index].key[0]; + } else if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) && + (adapter->secinfo.WPAenabled || + adapter->secinfo.WPA2enabled)) { + /* WPA */ + ext->alg = IW_ENCODE_ALG_TKIP; + ext->key_len = 0; + } else { + goto out; + } + + if (ext->key_len > max_key_len) { + ret = -E2BIG; + goto out; + } + + if (ext->key_len) + memcpy(ext->key, key, ext->key_len); + else + dwrq->flags |= IW_ENCODE_NOKEY; + dwrq->flags |= IW_ENCODE_ENABLED; + } + ret = 0; + +out: + LEAVE(); + return ret; +} + +/** + * @brief Set Encryption key Extended (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int alg = ext->alg; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) { + disable_wep (assoc_req); + } else if (alg == IW_ENCODE_ALG_WEP) { + u16 is_default = 0, index, set_tx_key = 0; + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) + goto out; + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, or if the set-TX-key flag was passed, set the TX key. + */ + if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) + || (dwrq->length == 0 && !is_default) + || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) + set_tx_key = 1; + + /* Copy key to driver */ + ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index, + set_tx_key); + if (ret) + goto out; + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.authmode = + wlan802_11authmodeshared; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + + /* Mark the various WEP bits as modified */ + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) { + struct WLAN_802_11_KEY * pkey; + + /* validate key length */ + if (((alg == IW_ENCODE_ALG_TKIP) + && (ext->key_len != KEY_LEN_WPA_TKIP)) + || ((alg == IW_ENCODE_ALG_CCMP) + && (ext->key_len != KEY_LEN_WPA_AES))) { + lbs_pr_debug(1, "Invalid size %d for key of alg" + "type %d.\n", + ext->key_len, + alg); + ret = -EINVAL; + goto out; + } + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) + pkey = &assoc_req->wpa_mcast_key; + else + pkey = &assoc_req->wpa_unicast_key; + + memset(pkey, 0, sizeof (struct WLAN_802_11_KEY)); + memcpy(pkey->key, ext->key, ext->key_len); + pkey->len = ext->key_len; + pkey->flags = KEY_INFO_WPA_ENABLED; + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { + pkey->flags |= KEY_INFO_WPA_MCAST; + set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); + } else { + pkey->flags |= KEY_INFO_WPA_UNICAST; + set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); + } + + if (alg == IW_ENCODE_ALG_TKIP) + pkey->type = KEY_TYPE_ID_TKIP; + else if (alg == IW_ENCODE_ALG_CCMP) + pkey->type = KEY_TYPE_ID_AES; + + /* If WPA isn't enabled yet, do that now */ + if ( assoc_req->secinfo.WPAenabled == 0 + && assoc_req->secinfo.WPA2enabled == 0) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + } + + disable_wep (assoc_req); + } + +out: + if (ret == 0) { + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + + +static int wlan_set_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->length > MAX_WPA_IE_LEN || + (dwrq->length && extra == NULL)) { + ret = -EINVAL; + goto out; + } + + if (dwrq->length) { + memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length); + assoc_req->wpa_ie_len = dwrq->length; + } else { + memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->wpa_ie)); + assoc_req->wpa_ie_len = 0; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->wpa_ie_len == 0) { + dwrq->length = 0; + LEAVE(); + return 0; + } + + if (dwrq->length < adapter->wpa_ie_len) { + LEAVE(); + return -E2BIG; + } + + dwrq->length = adapter->wpa_ie_len; + memcpy(extra, &adapter->wpa_ie[0], adapter->wpa_ie_len); + + LEAVE(); + return 0; +} + + +static int wlan_set_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + int updated = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * libertas does not use these parameters + */ + break; + + case IW_AUTH_WPA_VERSION: + if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) { + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + updated = 1; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + if (dwrq->value) { + adapter->currentpacketfilter |= + cmd_act_mac_strict_protection_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_strict_protection_enable; + } + updated = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) { + assoc_req->secinfo.authmode = + wlan802_11authmodeshared; + } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) { + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } else if (dwrq->value & IW_AUTH_ALG_LEAP) { + assoc_req->secinfo.authmode = + wlan802_11authmodenetworkEAP; + } else { + ret = -EINVAL; + } + updated = 1; + break; + + case IW_AUTH_WPA_ENABLED: + if (dwrq->value) { + if (!assoc_req->secinfo.WPAenabled && + !assoc_req->secinfo.WPA2enabled) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + } else { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + updated = 1; + break; + + default: + ret = -EOPNOTSUPP; + break; + } + +out: + if (ret == 0) { + if (updated) + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else if (ret != -EOPNOTSUPP) { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + dwrq->value = 0; + if (adapter->secinfo.WPAenabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA; + if (adapter->secinfo.WPA2enabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA2; + if (!dwrq->value) + dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dwrq->value = 0; + if (adapter->currentpacketfilter & + cmd_act_mac_strict_protection_enable) + dwrq->value = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + switch (adapter->secinfo.authmode) { + case wlan802_11authmodeshared: + dwrq->value = IW_AUTH_ALG_SHARED_KEY; + break; + case wlan802_11authmodeopen: + dwrq->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + case wlan802_11authmodenetworkEAP: + dwrq->value = IW_AUTH_ALG_LEAP; + break; + default: + break; + } + break; + + case IW_AUTH_WPA_ENABLED: + if (adapter->secinfo.WPAenabled && adapter->secinfo.WPA2enabled) + dwrq->value = 1; + break; + + default: + LEAVE(); + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + + +static int wlan_set_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + u16 dbm; + + ENTER(); + + if (vwrq->disabled) { + wlan_radio_ioctl(priv, RADIO_OFF); + return 0; + } + + adapter->preamble = cmd_type_auto_preamble; + + wlan_radio_ioctl(priv, RADIO_ON); + + if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) { + dbm = (u16) mw_to_dbm(vwrq->value); + } else + dbm = (u16) vwrq->value; + + /* auto tx power control */ + + if (vwrq->fixed == 0) + dbm = 0xffff; + + lbs_pr_debug(1, "<1>TXPOWER SET %d dbm.\n", dbm); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_set_low, + cmd_option_waitforrsp, 0, (void *)&dbm); + + LEAVE(); + return ret; +} + +static int wlan_get_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + /* + * Note : if dwrq->flags != 0, we should get the relevant SSID from + * the SSID list... + */ + + /* + * Get the current SSID + */ + if (adapter->connect_status == libertas_connected) { + memcpy(extra, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } else { + memset(extra, 0, 32); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } + /* + * If none, we may want to get the one that was set + */ + + /* To make the driver backward compatible with WPA supplicant v0.2.4 */ + if (dwrq->length == 32) /* check with WPA supplicant buffer size */ + dwrq->length = min_t(size_t, adapter->curbssparams.ssid.ssidlength, + IW_ESSID_MAX_SIZE); + else + dwrq->length = adapter->curbssparams.ssid.ssidlength + 1; + + dwrq->flags = 1; /* active */ + + LEAVE(); + return 0; +} + +static int wlan_set_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct WLAN_802_11_SSID ssid; + struct assoc_request * assoc_req; + int ssid_len = dwrq->length; + + ENTER(); + + /* + * WE-20 and earlier NULL pad the end of the SSID and increment + * SSID length so it can be used like a string. WE-21 and later don't, + * but some userspace tools aren't able to cope with the change. + */ + if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0')) + ssid_len--; + + /* Check the size of the string */ + if (ssid_len > IW_ESSID_MAX_SIZE) { + ret = -E2BIG; + goto out; + } + + memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID)); + + if (!dwrq->flags || !ssid_len) { + /* "any" SSID requested; leave SSID blank */ + } else { + /* Specific SSID requested */ + memcpy(&ssid.ssid, extra, ssid_len); + ssid.ssidlength = ssid_len; + } + + lbs_pr_debug(1, "Requested new SSID = %s\n", + (ssid.ssidlength > 0) ? (char *)ssid.ssid : "any"); + +out: + mutex_lock(&adapter->lock); + if (ret == 0) { + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + } else { + /* Copy the SSID to the association request */ + memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID)); + set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + } + + /* Cancel the association request if there was an error */ + if (ret != 0) { + wlan_cancel_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Connect to the AP or Ad-hoc Network with specific bssid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + + lbs_pr_debug(1, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data)); + + mutex_lock(&adapter->lock); + + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + wlan_cancel_association_work(priv); + ret = -ENOMEM; + } else { + /* Copy the BSSID to the association request */ + memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN); + set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + return ret; +} + +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen) +{ + union { + u32 l; + u8 c[4]; + } ver; + char fwver[32]; + + mutex_lock(&adapter->lock); + ver.l = adapter->fwreleasenumber; + mutex_unlock(&adapter->lock); + + if (ver.c[3] == 0) + sprintf(fwver, "%u.%u.%u", ver.c[2], ver.c[1], ver.c[0]); + else + sprintf(fwver, "%u.%u.%u.p%u", + ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + + snprintf(fwversion, maxlen, fwver); +} + + +/* + * iwconfig settable callbacks + */ +static const iw_handler wlan_handler[] = { + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) wlan_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) wlan_set_freq, /* SIOCSIWFREQ */ + (iw_handler) wlan_get_freq, /* SIOCGIWFREQ */ + (iw_handler) wlan_set_mode, /* SIOCSIWMODE */ + (iw_handler) wlan_get_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) wlan_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) wlan_set_wap, /* SIOCSIWAP */ + (iw_handler) wlan_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* SIOCSIWMLME */ + (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */ + (iw_handler) libertas_set_scan, /* SIOCSIWSCAN */ + (iw_handler) libertas_get_scan, /* SIOCGIWSCAN */ + (iw_handler) wlan_set_essid, /* SIOCSIWESSID */ + (iw_handler) wlan_get_essid, /* SIOCGIWESSID */ + (iw_handler) wlan_set_nick, /* SIOCSIWNICKN */ + (iw_handler) wlan_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_rate, /* SIOCSIWRATE */ + (iw_handler) wlan_get_rate, /* SIOCGIWRATE */ + (iw_handler) wlan_set_rts, /* SIOCSIWRTS */ + (iw_handler) wlan_get_rts, /* SIOCGIWRTS */ + (iw_handler) wlan_set_frag, /* SIOCSIWFRAG */ + (iw_handler) wlan_get_frag, /* SIOCGIWFRAG */ + (iw_handler) wlan_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) wlan_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) wlan_set_retry, /* SIOCSIWRETRY */ + (iw_handler) wlan_get_retry, /* SIOCGIWRETRY */ + (iw_handler) wlan_set_encode, /* SIOCSIWENCODE */ + (iw_handler) wlan_get_encode, /* SIOCGIWENCODE */ + (iw_handler) wlan_set_power, /* SIOCSIWPOWER */ + (iw_handler) wlan_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_genie, /* SIOCSIWGENIE */ + (iw_handler) wlan_get_genie, /* SIOCGIWGENIE */ + (iw_handler) wlan_set_auth, /* SIOCSIWAUTH */ + (iw_handler) wlan_get_auth, /* SIOCGIWAUTH */ + (iw_handler) wlan_set_encodeext,/* SIOCSIWENCODEEXT */ + (iw_handler) wlan_get_encodeext,/* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ +}; + +struct iw_handler_def libertas_handler_def = { + .num_standard = sizeof(wlan_handler) / sizeof(iw_handler), + .num_private = sizeof(wlan_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(wlan_private_args) / + sizeof(struct iw_priv_args), + .standard = (iw_handler *) wlan_handler, + .private = (iw_handler *) wlan_private_handler, + .private_args = (struct iw_priv_args *)wlan_private_args, + .get_wireless_stats = wlan_get_wireless_stats, +}; diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h new file mode 100644 index 0000000..39f367c --- /dev/null +++ b/drivers/net/wireless/libertas/wext.h @@ -0,0 +1,147 @@ +/** + * This file contains definition for IOCTL call. + */ +#ifndef _WLAN_WEXT_H_ +#define _WLAN_WEXT_H_ + +#define SUBCMD_OFFSET 4 +#define SUBCMD_DATA(x) *((int *)(x->u.name + SUBCMD_OFFSET)) + +/** PRIVATE CMD ID */ +#define WLANIOCTL SIOCIWFIRSTPRIV + +#define WLANSETWPAIE (WLANIOCTL + 0) + +#define WLAN_SETINT_GETINT (WLANIOCTL + 7) +#define WLANNF 1 +#define WLANRSSI 2 +#define WLANENABLE11D 5 +#define WLANADHOCGRATE 6 +#define WLAN_SUBCMD_SET_PRESCAN 11 + +#define WLAN_SETNONE_GETNONE (WLANIOCTL + 8) +#define WLANDEAUTH 1 +#define WLANRADIOON 2 +#define WLANRADIOOFF 3 +#define WLANREMOVEADHOCAES 4 +#define WLANADHOCSTOP 5 +#define WLANCIPHERTEST 6 +#define WLANCRYPTOTEST 7 + +#define WLANWLANIDLEON 10 +#define WLANWLANIDLEOFF 11 +#define WLAN_SUBCMD_BT_RESET 13 +#define WLAN_SUBCMD_FWT_RESET 14 + +#define WLANGETLOG (WLANIOCTL + 9) +#define GETLOG_BUFSIZE 300 + +#define WLANSCAN_TYPE (WLANIOCTL + 11) + +#define WLAN_SETNONE_GETONEINT (WLANIOCTL + 15) +#define WLANGETREGION 1 +#define WLAN_GET_LISTEN_INTERVAL 2 +#define WLAN_GET_MULTIPLE_DTIM 3 +#define WLAN_GET_TX_RATE 4 +#define WLANGETBCNAVG 5 + +#define WLAN_GET_LINKMODE 6 +#define WLAN_GET_RADIOMODE 7 +#define WLAN_GET_DEBUGMODE 8 +#define WLAN_SUBCMD_FWT_CLEANUP 15 +#define WLAN_SUBCMD_FWT_TIME 16 +#define WLAN_SUBCMD_MESH_GET_TTL 17 + +#define WLANREGCFRDWR (WLANIOCTL + 18) + +#define WLAN_SETNONE_GETTWELVE_CHAR (WLANIOCTL + 19) +#define WLAN_SUBCMD_GETRXANTENNA 1 +#define WLAN_SUBCMD_GETTXANTENNA 2 +#define WLAN_GET_TSF 3 + +#define WLAN_SETNONE_GETWORDCHAR (WLANIOCTL + 21) +#define WLANGETADHOCAES 1 + +#define WLAN_SETONEINT_GETONEINT (WLANIOCTL + 23) +#define WLAN_BEACON_INTERVAL 1 +#define WLAN_LISTENINTRVL 4 + +#define WLAN_TXCONTROL 6 +#define WLAN_NULLPKTINTERVAL 7 + +#define WLAN_SETONEINT_GETNONE (WLANIOCTL + 24) +#define WLAN_SUBCMD_SETRXANTENNA 1 +#define WLAN_SUBCMD_SETTXANTENNA 2 +#define WLANSETAUTHALG 5 +#define WLANSET8021XAUTHALG 6 +#define WLANSETENCRYPTIONMODE 7 +#define WLANSETREGION 8 +#define WLAN_SET_LISTEN_INTERVAL 9 + +#define WLAN_SET_MULTIPLE_DTIM 10 +#define WLAN_SET_ATIM_WINDOW 11 +#define WLANSETBCNAVG 13 +#define WLANSETDATAAVG 14 +#define WLAN_SET_LINKMODE 15 +#define WLAN_SET_RADIOMODE 16 +#define WLAN_SET_DEBUGMODE 17 +#define WLAN_SUBCMD_MESH_SET_TTL 18 + +#define WLAN_SET128CHAR_GET128CHAR (WLANIOCTL + 25) +#define WLANSCAN_MODE 6 + +#define WLAN_GET_ADHOC_STATUS 9 + +#define WLAN_SUBCMD_BT_ADD 18 +#define WLAN_SUBCMD_BT_DEL 19 +#define WLAN_SUBCMD_BT_LIST 20 +#define WLAN_SUBCMD_FWT_ADD 21 +#define WLAN_SUBCMD_FWT_DEL 22 +#define WLAN_SUBCMD_FWT_LOOKUP 23 +#define WLAN_SUBCMD_FWT_LIST_NEIGHBOR 24 +#define WLAN_SUBCMD_FWT_LIST 25 +#define WLAN_SUBCMD_FWT_LIST_ROUTE 26 + +#define WLAN_SET_GET_SIXTEEN_INT (WLANIOCTL + 29) +#define WLAN_TPCCFG 1 +#define WLAN_POWERCFG 2 + +#define WLAN_AUTO_FREQ_SET 3 +#define WLAN_AUTO_FREQ_GET 4 +#define WLAN_LED_GPIO_CTRL 5 +#define WLAN_SCANPROBES 6 +#define WLAN_ADAPT_RATESET 8 +#define WLAN_INACTIVITY_TIMEOUT 9 +#define WLANSNR 10 +#define WLAN_GET_RATE 11 +#define WLAN_GET_RXINFO 12 + +#define WLANCMD52RDWR (WLANIOCTL + 30) +#define WLANCMD53RDWR (WLANIOCTL + 31) +#define CMD53BUFLEN 32 + +#define REG_MAC 0x19 +#define REG_BBP 0x1a +#define REG_RF 0x1b +#define REG_EEPROM 0x59 +#define WLAN_LINKMODE_802_3 0 +#define WLAN_LINKMODE_802_11 2 +#define WLAN_RADIOMODE_NONE 0 +#define WLAN_RADIOMODE_RADIOTAP 2 + +/** wlan_ioctl_regrdwr */ +struct wlan_ioctl_regrdwr { + /** Which register to access */ + u16 whichreg; + /** Read or Write */ + u16 action; + u32 offset; + u16 NOB; + u32 value; +}; + +extern struct iw_handler_def libertas_handler_def; +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int i); +int wlan_radio_ioctl(wlan_private * priv, u8 option); + +#endif /* _WLAN_WEXT_H_ */ diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c index a009ab5..45b00e1 100644 --- a/drivers/net/wireless/netwave_cs.c +++ b/drivers/net/wireless/netwave_cs.c @@ -1283,7 +1283,6 @@ static int netwave_rx(struct net_device skb_reserve( skb, 2); /* Align IP on 16 byte */ skb_put( skb, rcvLen); - skb->dev = dev; /* Copy packet fragments to the skb data area */ ptr = (u_char*) skb->data; diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 4e7f6cf..062286d 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -689,7 +689,7 @@ static void orinoco_stat_gather(struct n /* Note : gcc will optimise the whole section away if * WIRELESS_SPY is not defined... - Jean II */ if (SPY_NUMBER(priv)) { - orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN, + orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN, desc->signal, desc->silence); } } @@ -770,7 +770,7 @@ static void orinoco_rx_monitor(struct ne /* Copy the 802.11 header to the skb */ memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); /* If any, copy the data from the card to the skb */ if (datalen > 0) { @@ -915,7 +915,6 @@ static void __orinoco_ev_rx(struct net_d memcpy(hdr->h_source, desc.addr2, ETH_ALEN); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; if (fc & IEEE80211_FCTL_TODS) diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c index b112291..dd070cc 100644 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ b/drivers/net/wireless/prism54/islpci_eth.c @@ -136,7 +136,7 @@ #ifdef ISLPCI_ETH_DEBUG printk("islpci_eth_transmit:wds_mac\n"); #endif memmove(skb->data + 6, src, skb->len); - memcpy(skb->data, wds_mac, 6); + skb_copy_to_linear_data(skb, wds_mac, 6); } else { memmove(skb->data, src, skb->len); } @@ -162,13 +162,16 @@ #endif skb_put(newskb, init_wds ? skb->len + 6 : skb->len); if (init_wds) { - memcpy(newskb->data + 6, skb->data, skb->len); - memcpy(newskb->data, wds_mac, 6); + skb_copy_from_linear_data(skb, + newskb->data + 6, + skb->len); + skb_copy_to_linear_data(newskb, wds_mac, 6); #ifdef ISLPCI_ETH_DEBUG printk("islpci_eth_transmit:wds_mac\n"); #endif } else - memcpy(newskb->data, skb->data, skb->len); + skb_copy_from_linear_data(skb, newskb->data, + skb->len); #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n", @@ -303,7 +306,7 @@ islpci_monitor_rx(islpci_private *priv, skb_pull(*skb, sizeof (struct rfmon_header)); (*skb)->protocol = htons(ETH_P_802_2); - (*skb)->mac.raw = (*skb)->data; + skb_reset_mac_header(*skb); (*skb)->pkt_type = PACKET_OTHERHOST; return 0; @@ -374,10 +377,6 @@ #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data); display_buffer((char *) skb->data, skb->len); #endif - - /* do some additional sk_buff and network layer parameters */ - skb->dev = ndev; - /* take care of monitor mode and spy monitoring. */ if (unlikely(priv->iw_mode == IW_MODE_MONITOR)) discard = islpci_monitor_rx(priv, &skb); @@ -398,8 +397,10 @@ #endif /* Update spy records */ wireless_spy_update(ndev, annex->addr2, &wstats); - memcpy(skb->data + sizeof (struct rfmon_header), - skb->data, 2 * ETH_ALEN); + skb_copy_from_linear_data(skb, + (skb->data + + sizeof(struct rfmon_header)), + 2 * ETH_ALEN); skb_pull(skb, sizeof (struct rfmon_header)); } skb->protocol = eth_type_trans(skb, ndev); diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 47b2ccb..3be6242 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -2232,7 +2232,6 @@ #endif return; } skb_reserve( skb, 2); /* Align IP on 16 byte (TBD check this)*/ - skb->dev = dev; DEBUG(4,"ray_cs rx_data total_len = %x, rx_len = %x\n",total_len,rx_len); @@ -2243,7 +2242,8 @@ #endif rx_ptr += copy_from_rx_buff(local, rx_ptr, pkt_addr & RX_BUFF_END, rx_len); /* Get source address */ #ifdef WIRELESS_SPY - memcpy(linksrcaddr, ((struct mac_header *)skb->data)->addr_2, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, offsetof(struct mac_header, addr_2), + linksrcaddr, ETH_ALEN); #endif /* Now, deal with encapsulation/translation/sniffer */ if (!sniffer) { diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c index f5ce1c6..2a299a0 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/net/wireless/strip.c @@ -2009,7 +2009,7 @@ static void deliver_packet(struct strip packetlen); skb->dev = get_strip_dev(strip_info); skb->protocol = header->protocol; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); /* Having put a fake header on the front of the sk_buff for the */ /* benefit of tools like tcpdump, skb_pull now 'consumes' that */ diff --git a/drivers/net/wireless/todo.txt b/drivers/net/wireless/todo.txt deleted file mode 100644 index 3223401..0000000 --- a/drivers/net/wireless/todo.txt +++ /dev/null @@ -1,15 +0,0 @@ - Wireless Todo - ------------- - -1) Bring other kernel Wireless LAN drivers here - Completed - -2) Bring new Wireless LAN driver not yet in the kernel there - See my web page for details - In particular : HostAP - -3) Misc - o Mark wavelan, wavelan_cs, netwave_cs drivers as obsolete - o Maybe arlan.c, ray_cs.c and strip.c also deserve to be obsolete - - Jean II diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index 2aa3c76..1cf090d 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -2512,14 +2512,13 @@ #endif return; } - skb->dev = dev; - /* Copy the packet to the buffer. */ obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize); skb->protocol = eth_type_trans(skb, dev); #ifdef DEBUG_RX_INFO - wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); + wv_packet_info(skb_mac_header(skb), sksize, dev->name, + "wv_packet_read"); #endif /* DEBUG_RX_INFO */ /* Statistics-gathering and associated stuff. @@ -2555,7 +2554,7 @@ #endif /* Spying stuff */ #ifdef IW_WIRELESS_SPY - wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, + wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats); #endif /* IW_WIRELESS_SPY */ #ifdef HISTOGRAM @@ -2939,7 +2938,7 @@ #endif * need to pad. Jean II */ if (skb->len < ETH_ZLEN) { memset(data, 0, ETH_ZLEN); - memcpy(data, skb->data, skb->len); + skb_copy_from_linear_data(skb, data, skb->len); /* Write packet on the card */ if(wv_packet_write(dev, data, ETH_ZLEN)) return 1; /* We failed */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index b042397..67b867f 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -2884,14 +2884,12 @@ #endif return; } - skb->dev = dev; - skb_reserve(skb, 2); fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize); skb->protocol = eth_type_trans(skb, dev); #ifdef DEBUG_RX_INFO - wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); + wv_packet_info(skb_mac_header(skb), sksize, dev->name, "wv_packet_read"); #endif /* DEBUG_RX_INFO */ /* Statistics gathering & stuff associated. @@ -2925,7 +2923,7 @@ #ifdef WAVELAN_ROAMING #endif /* WAVELAN_ROAMING */ #ifdef WIRELESS_SPY - wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats); + wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats); #endif /* WIRELESS_SPY */ #ifdef HISTOGRAM wl_his_gather(dev, stats); diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 6cb66a3..935b144 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -327,7 +327,6 @@ static void zd1201_usbrx(struct urb *urb memcpy(skb_put(skb, 6), &data[datalen-8], 6); memcpy(skb_put(skb, 2), &data[datalen-24], 2); memcpy(skb_put(skb, len), data, len); - skb->dev = zd->dev; skb->dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, zd->dev); zd->stats.rx_packets++; @@ -385,7 +384,6 @@ static void zd1201_usbrx(struct urb *urb memcpy(skb_put(skb, 2), &data[6], 2); memcpy(skb_put(skb, len), data+8, len); } - skb->dev = zd->dev; skb->dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, zd->dev); zd->stats.rx_packets++; @@ -809,10 +807,10 @@ static int zd1201_hard_start_xmit(struct txbuf[4] = 0x00; txbuf[5] = 0x00; - memcpy(txbuf+6, skb->data+12, skb->len-12); + skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12); if (pad) txbuf[skb->len-12+6]=0; - memcpy(txbuf+skb->len-12+6+pad, skb->data, 12); + skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12); *(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6); txbuf[txbuflen-1] = 0; diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig index 66ed55b..d1ab24a 100644 --- a/drivers/net/wireless/zd1211rw/Kconfig +++ b/drivers/net/wireless/zd1211rw/Kconfig @@ -1,6 +1,7 @@ config ZD1211RW tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" - depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + depends on USB && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + select WIRELESS_EXT select FW_LOADER ---help--- This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 87ee3ee..95b4a2a 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -67,11 +67,12 @@ static int scnprint_id(struct zd_chip *c i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i); i += scnprintf(buffer+i, size-i, " "); i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); - i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c", chip->pa_type, + i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type, chip->patch_cck_gain ? 'g' : '-', chip->patch_cr157 ? '7' : '-', chip->patch_6m_band_edge ? '6' : '-', - chip->new_phy_layout ? 'N' : '-'); + chip->new_phy_layout ? 'N' : '-', + chip->al2230s_bit ? 'S' : '-'); return i; } @@ -114,7 +115,7 @@ int zd_ioread32v_locked(struct zd_chip * /* Allocate a single memory block for values and addresses. */ count16 = 2*count; a16 = (zd_addr_t *) kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)), - GFP_NOFS); + GFP_KERNEL); if (!a16) { dev_dbg_f(zd_chip_dev(chip), "error ENOMEM in allocation of a16\n"); @@ -163,7 +164,7 @@ int _zd_iowrite32v_locked(struct zd_chip /* Allocate a single memory block for values and addresses. */ count16 = 2*count; - ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_NOFS); + ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_KERNEL); if (!ioreqs16) { r = -ENOMEM; dev_dbg_f(zd_chip_dev(chip), @@ -614,16 +615,24 @@ static int patch_cr157(struct zd_chip *c * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge * bit (for AL2230, AL2230S) */ -static int patch_6m_band_edge(struct zd_chip *chip, int channel) +static int patch_6m_band_edge(struct zd_chip *chip, u8 channel) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + if (!chip->patch_6m_band_edge) + return 0; + + return zd_rf_patch_6m_band_edge(&chip->rf, channel); +} + +/* Generic implementation of 6M band edge patching, used by most RFs via + * zd_rf_generic_patch_6m() */ +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel) { struct zd_ioreq16 ioreqs[] = { { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, { CR47, 0x1e }, }; - if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge) - return 0; - /* FIXME: Channel 11 is not the edge for all regulatory domains. */ if (channel == 1 || channel == 11) ioreqs[0].value = 0x12; @@ -683,17 +692,17 @@ static int zd1211_hw_reset_phy(struct zd { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, { CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f }, - { CR123, 0x27 }, { CR125, 0xaa }, { CR127, 0x03 }, - { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR131, 0x0C }, { CR136, 0xdf }, { CR137, 0x40 }, - { CR138, 0xa0 }, { CR139, 0xb0 }, { CR140, 0x99 }, - { CR141, 0x82 }, { CR142, 0x54 }, { CR143, 0x1c }, - { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x4c }, - { CR149, 0x50 }, { CR150, 0x0e }, { CR151, 0x18 }, - { CR160, 0xfe }, { CR161, 0xee }, { CR162, 0xaa }, - { CR163, 0xfa }, { CR164, 0xfa }, { CR165, 0xea }, - { CR166, 0xbe }, { CR167, 0xbe }, { CR168, 0x6a }, - { CR169, 0xba }, { CR170, 0xba }, { CR171, 0xba }, + { CR125, 0xaa }, { CR127, 0x03 }, { CR128, 0x14 }, + { CR129, 0x12 }, { CR130, 0x10 }, { CR131, 0x0C }, + { CR136, 0xdf }, { CR137, 0x40 }, { CR138, 0xa0 }, + { CR139, 0xb0 }, { CR140, 0x99 }, { CR141, 0x82 }, + { CR142, 0x54 }, { CR143, 0x1c }, { CR144, 0x6c }, + { CR147, 0x07 }, { CR148, 0x4c }, { CR149, 0x50 }, + { CR150, 0x0e }, { CR151, 0x18 }, { CR160, 0xfe }, + { CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa }, + { CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe }, + { CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba }, + { CR170, 0xba }, { CR171, 0xba }, /* Note: CR204 must lead the CR203 */ { CR204, 0x7d }, { }, diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index e57ed75..ce0a5f6 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -833,6 +833,7 @@ int zd_chip_enable_rx(struct zd_chip *ch void zd_chip_disable_rx(struct zd_chip *chip); int zd_chip_enable_hwint(struct zd_chip *chip); int zd_chip_disable_hwint(struct zd_chip *chip); +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel); int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, u8 rts_rate, int preamble); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 4c5f78e..6753d24 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -156,17 +156,8 @@ void zd_mac_clear(struct zd_mac *mac) static int reset_mode(struct zd_mac *mac) { struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); - struct zd_ioreq32 ioreqs[] = { - { CR_RX_FILTER, STA_RX_FILTER }, - { CR_SNIFFER_ON, 0U }, - }; - - if (ieee->iw_mode == IW_MODE_MONITOR) { - ioreqs[0].value = 0xffffffff; - ioreqs[1].value = 0x1; - } - - return zd_iowrite32a(&mac->chip, ioreqs, ARRAY_SIZE(ioreqs)); + u32 filter = (ieee->iw_mode == IW_MODE_MONITOR) ? ~0 : STA_RX_FILTER; + return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); } int zd_mac_open(struct net_device *netdev) @@ -974,14 +965,14 @@ static int is_data_packet_for_us(struct switch (ieee->iw_mode) { case IW_MODE_ADHOC: if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != 0 || - memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) != 0) + compare_ether_addr(hdr->addr3, ieee->bssid) != 0) return 0; break; case IW_MODE_AUTO: case IW_MODE_INFRA: if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != IEEE80211_FCTL_FROMDS || - memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) != 0) + compare_ether_addr(hdr->addr2, ieee->bssid) != 0) return 0; break; default: @@ -989,9 +980,9 @@ static int is_data_packet_for_us(struct return 0; } - return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 || + return compare_ether_addr(hdr->addr1, netdev->dev_addr) == 0 || (is_multicast_ether_addr(hdr->addr1) && - memcmp(hdr->addr3, netdev->dev_addr, ETH_ALEN) != 0) || + compare_ether_addr(hdr->addr3, netdev->dev_addr) != 0) || (netdev->flags & IFF_PROMISC); } @@ -1047,7 +1038,7 @@ static void update_qual_rssi(struct zd_m hdr = (struct ieee80211_hdr_3addr *)buffer; if (length < offsetof(struct ieee80211_hdr_3addr, addr3)) return; - if (memcmp(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid, ETH_ALEN) != 0) + if (compare_ether_addr(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid) != 0) return; spin_lock_irqsave(&mac->lock, flags); diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c index f50cff3..549c23b 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.c +++ b/drivers/net/wireless/zd1211rw/zd_rf.c @@ -23,7 +23,7 @@ #include "zd_rf.h" #include "zd_ieee80211.h" #include "zd_chip.h" -static const char *rfs[] = { +static const char * const rfs[] = { [0] = "unknown RF0", [1] = "unknown RF1", [UW2451_RF] = "UW2451_RF", @@ -34,7 +34,7 @@ static const char *rfs[] = { [AL2210_RF] = "AL2210_RF", [MAXIM_NEW_RF] = "MAXIM_NEW_RF", [UW2453_RF] = "UW2453_RF", - [AL2230S_RF] = "AL2230S_RF", + [UNKNOWN_A_RF] = "UNKNOWN_A_RF", [RALINK_RF] = "RALINK_RF", [INTERSIL_RF] = "INTERSIL_RF", [RF2959_RF] = "RF2959_RF", @@ -154,3 +154,17 @@ int zd_switch_radio_off(struct zd_rf *rf r = t; return r; } + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel) +{ + if (!rf->patch_6m_band_edge) + return 0; + + return rf->patch_6m_band_edge(rf, channel); +} + +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel) +{ + return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel); +} + diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h index a57732e..aa9cc10 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.h +++ b/drivers/net/wireless/zd1211rw/zd_rf.h @@ -26,7 +26,7 @@ #define THETA_RF 0x6 #define AL2210_RF 0x7 #define MAXIM_NEW_RF 0x8 #define UW2453_RF 0x9 -#define AL2230S_RF 0xa +#define UNKNOWN_A_RF 0xa #define RALINK_RF 0xb #define INTERSIL_RF 0xc #define RF2959_RF 0xd @@ -47,17 +47,13 @@ struct zd_rf { u8 type; u8 channel; - /* - * Whether this RF should patch the 6M band edge - * (assuming E2P_POD agrees) - */ - u8 patch_6m_band_edge:1; /* RF-specific functions */ int (*init_hw)(struct zd_rf *rf); int (*set_channel)(struct zd_rf *rf, u8 channel); int (*switch_radio_on)(struct zd_rf *rf); int (*switch_radio_off)(struct zd_rf *rf); + int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel); }; const char *zd_rf_name(u8 type); @@ -72,6 +68,9 @@ int zd_rf_set_channel(struct zd_rf *rf, int zd_switch_radio_on(struct zd_rf *rf); int zd_switch_radio_off(struct zd_rf *rf); +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); + /* Functions for individual RF chips */ int zd_rf_init_rf2959(struct zd_rf *rf); diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c index 5235a78..511392a 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c @@ -59,6 +59,18 @@ static const struct zd_ioreq16 zd1211b_i { CR240, 0x57 }, { CR9, 0xe0 }, }; +static const struct zd_ioreq16 ioreqs_init_al2230s[] = { + { CR47, 0x1e }, /* MARK_002 */ + { CR106, 0x22 }, + { CR107, 0x2a }, /* MARK_002 */ + { CR109, 0x13 }, /* MARK_002 */ + { CR118, 0xf8 }, /* MARK_002 */ + { CR119, 0x12 }, { CR122, 0xe0 }, + { CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */ + { CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */ + { CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */ +}; + static int zd1211b_al2230_finalize_rf(struct zd_chip *chip) { int r; @@ -90,7 +102,7 @@ static int zd1211_al2230_init_hw(struct int r; struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { + static const struct zd_ioreq16 ioreqs_init[] = { { CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 }, { CR44, 0x33 }, { CR106, 0x2a }, { CR107, 0x1a }, @@ -117,10 +129,9 @@ static int zd1211_al2230_init_hw(struct { CR119, 0x10 }, { CR120, 0x4f }, { CR121, 0x77 }, { CR122, 0xe0 }, { CR137, 0x88 }, { CR252, 0xff }, { CR253, 0xff }, + }; - /* These following happen separately in the vendor driver */ - { }, - + static const struct zd_ioreq16 ioreqs_pll[] = { /* shdnb(PLL_ON)=0 */ { CR251, 0x2f }, /* shdnb(PLL_ON)=1 */ @@ -128,7 +139,7 @@ static int zd1211_al2230_init_hw(struct { CR138, 0x28 }, { CR203, 0x06 }, }; - static const u32 rv[] = { + static const u32 rv1[] = { /* Channel 1 */ 0x03f790, 0x033331, @@ -137,6 +148,9 @@ static int zd1211_al2230_init_hw(struct 0x0b3331, 0x03b812, 0x00fff3, + }; + + static const u32 rv2[] = { 0x000da4, 0x0f4dc5, /* fix freq shift, 0x04edc5 */ 0x0805b6, @@ -148,8 +162,9 @@ static int zd1211_al2230_init_hw(struct 0x0bdffc, 0x00000d, 0x00500f, + }; - /* These writes happen separately in the vendor driver */ + static const u32 rv3[] = { 0x00d00f, 0x004c0f, 0x00540f, @@ -157,11 +172,38 @@ static int zd1211_al2230_init_hw(struct 0x00500f, }; - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init)); if (r) return r; - r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); + if (chip->al2230s_bit) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + + r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); + if (r) + return r; + + /* improve band edge for AL2230S */ + if (chip->al2230s_bit) + r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); if (r) return r; @@ -227,7 +269,9 @@ static int zd1211b_al2230_init_hw(struct 0x481dc0, 0xcfff00, 0x25a000, + }; + static const u32 rv2[] = { /* To improve AL2230 yield, improve phase noise, 4713 */ 0x25a000, 0xa3b2f0, @@ -250,7 +294,7 @@ static int zd1211b_al2230_init_hw(struct { CR251, 0x7f }, /* shdnb(PLL_ON)=1 */ }; - static const u32 rv2[] = { + static const u32 rv3[] = { /* To improve AL2230 yield, 4713 */ 0xf01b00, 0xf01e00, @@ -269,18 +313,37 @@ static int zd1211b_al2230_init_hw(struct r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); if (r) return r; + + if (chip->al2230s_bit) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1)); if (r) return r; - r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + + if (chip->al2230s_bit) + r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS); if (r) return r; + r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2)); if (r) return r; + r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3)); + if (r) + return r; r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); if (r) return r; @@ -358,12 +421,6 @@ int zd_rf_init_al2230(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); - if (chip->al2230s_bit) { - dev_err(zd_chip_dev(chip), "AL2230S devices are not yet " - "supported by this driver.\n"); - return -ENODEV; - } - rf->switch_radio_off = al2230_switch_radio_off; if (chip->is_zd1211b) { rf->init_hw = zd1211b_al2230_init_hw; @@ -374,6 +431,6 @@ int zd_rf_init_al2230(struct zd_rf *rf) rf->set_channel = zd1211_al2230_set_channel; rf->switch_radio_on = zd1211_al2230_switch_radio_on; } - rf->patch_6m_band_edge = 1; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; return 0; } diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c index a289f95..5e5e9dd 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c @@ -51,9 +51,52 @@ static const u32 std_rv[] = { 0xd8c010, }; -static int al7230b_init_hw(struct zd_rf *rf) +static const u32 rv_init1[] = { + 0x3c9000, + 0xbfffff, + 0x700000, + 0xf15d58, +}; + +static const u32 rv_init2[] = { + 0xf15d59, + 0xf15d5c, + 0xf15d58, +}; + +static const struct zd_ioreq16 ioreqs_sw[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, +}; + +static int zd1211b_al7230b_finalize(struct zd_chip *chip) { - int i, r; + int r; + static const struct zd_ioreq16 ioreqs[] = { + { CR80, 0x30 }, { CR81, 0x30 }, { CR79, 0x58 }, + { CR12, 0xf0 }, { CR77, 0x1b }, { CR78, 0x58 }, + { CR203, 0x04 }, + { }, + { CR240, 0x80 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + if (chip->new_phy_layout) { + /* antenna selection? */ + r = zd_iowrite16_locked(chip, 0xe5, CR9); + if (r) + return r; + } + + return zd_iowrite16_locked(chip, 0x04, CR203); +} + +static int zd1211_al7230b_init_hw(struct zd_rf *rf) +{ + int r; struct zd_chip *chip = zd_rf_to_chip(rf); /* All of these writes are identical to AL2230 unless otherwise @@ -117,39 +160,136 @@ static int al7230b_init_hw(struct zd_rf }; static const struct zd_ioreq16 ioreqs_2[] = { - /* PLL_ON */ - { CR251, 0x3f }, + { CR251, 0x3f }, /* PLL_ON */ { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR38, 0x38 }, { CR136, 0xdf }, + { CR38, 0x38 }, { CR136, 0xdf }, }; r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x09ec04); + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x8cccc8); + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; - for (i = 0; i < ARRAY_SIZE(std_rv); i++) { - r = zd_rfwrite_cr_locked(chip, std_rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); + if (r) + return r; - r = zd_rfwrite_cr_locked(chip, 0x3c9000); + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xbfffff); + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x700000); + + r = zd_iowrite16_locked(chip, 0x06, CR203); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); + r = zd_iowrite16_locked(chip, 0x80, CR240); + if (r) + return r; + + return 0; +} + +static int zd1211b_al7230b_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_1[] = { + { CR240, 0x57 }, { CR9, 0x9 }, + { }, + { CR10, 0x8b }, { CR15, 0x20 }, + { CR17, 0x2B }, /* for newest (3rd cut) AL2230 */ + { CR20, 0x10 }, /* 4N25->Stone Request */ + { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, /* 5613 */ + { CR34, 0x30 }, + { CR35, 0x3e }, /* for newest (3rd cut) AL2230 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x99 }, /* for newest (3rd cut) AL2230 */ + { CR47, 0x1e }, + + /* ZD1215 5610 */ + { CR48, 0x00 }, { CR49, 0x00 }, { CR51, 0x01 }, + { CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 }, + { CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 }, + { CR69, 0x28 }, + + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR87, 0x0A }, { CR89, 0x04 }, + { CR90, 0x58 }, /* 5112 */ + { CR91, 0x00 }, /* 5613 */ + { CR92, 0x0a }, + { CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { CR99, 0x00 }, { CR100, 0x02 }, { CR101, 0x13 }, + { CR102, 0x27 }, + { CR106, 0x20 }, /* change to 0x24 for AL7230B */ + { CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { CR112, 0x1f }, + }; + + static const struct zd_ioreq16 ioreqs_new_phy[] = { + { CR107, 0x28 }, + { CR110, 0x1f }, /* 5127, 0x13->0x1f */ + { CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x2a }, { CR118, 0xfa }, { CR119, 0x12 }, + { CR121, 0x6c }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_old_phy[] = { + { CR107, 0x24 }, + { CR110, 0x13 }, /* 5127, 0x13->0x1f */ + { CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x24 }, { CR118, 0xfc }, { CR119, 0x11 }, + { CR121, 0x6a }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x24 }, + { CR117, 0xfa }, { CR120, 0x4f }, + { CR122, 0xfc }, /* E0->FCh at 4901 */ + { CR123, 0x57 }, /* 5613 */ + { CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { CR126, 0x6c }, /* 5613 */ + { CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { CR130, 0x10 }, + { CR131, 0x00 }, /* 5112 */ + { CR137, 0x50 }, /* 5613 */ + { CR138, 0xa8 }, /* 5112 */ + { CR144, 0xac }, /* 5613 */ + { CR148, 0x40 }, /* 5112 */ + { CR149, 0x40 }, /* 4O07, 50->40 */ + { CR150, 0x1a }, /* 5112, 0C->1A */ + { CR252, 0x34 }, { CR253, 0x34 }, + { CR251, 0x2f }, /* PLL_OFF */ + }; + + static const struct zd_ioreq16 ioreqs_3[] = { + { CR251, 0x7f }, /* PLL_ON */ + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + if (chip->new_phy_layout) + r = zd_iowrite16a_locked(chip, ioreqs_new_phy, + ARRAY_SIZE(ioreqs_new_phy)); + else + r = zd_iowrite16a_locked(chip, ioreqs_old_phy, + ARRAY_SIZE(ioreqs_old_phy)); if (r) return r; @@ -157,38 +297,36 @@ static int al7230b_init_hw(struct zd_rf if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d59); + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d5c); + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); + + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); if (r) return r; - r = zd_iowrite16_locked(chip, 0x06, CR203); + r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3)); if (r) return r; - r = zd_iowrite16_locked(chip, 0x80, CR240); + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; - return 0; + return zd1211b_al7230b_finalize(chip); } -static int al7230b_set_channel(struct zd_rf *rf, u8 channel) +static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel) { - int i, r; + int r; const u32 *rv = chan_rv[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); - struct zd_ioreq16 ioreqs_1[] = { - { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR38, 0x38 }, { CR136, 0xdf }, - }; - - struct zd_ioreq16 ioreqs_2[] = { + static const struct zd_ioreq16 ioreqs[] = { /* PLL_ON */ { CR251, 0x3f }, { CR203, 0x06 }, { CR240, 0x08 }, @@ -203,11 +341,9 @@ static int al7230b_set_channel(struct zd if (r) return r; - for (i = 0; i < ARRAY_SIZE(std_rv); i++) { - r = zd_rfwrite_cr_locked(chip, std_rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) @@ -216,24 +352,69 @@ static int al7230b_set_channel(struct zd if (r) return r; - r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); if (r) return r; - for (i = 0; i < 2; i++) { - r = zd_rfwrite_cr_locked(chip, rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) return r; - return zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } -static int al7230b_switch_radio_on(struct zd_rf *rf) +static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + r = zd_iowrite16_locked(chip, 0x57, CR240); + if (r) + return r; + r = zd_iowrite16_locked(chip, 0xe4, CR9); + if (r) + return r; + + /* PLL_OFF */ + r = zd_iowrite16_locked(chip, 0x2f, CR251); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + r = zd_iowrite16_locked(chip, 0x7f, CR251); + if (r) + return r; + + return zd1211b_al7230b_finalize(chip); +} + +static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { @@ -244,6 +425,17 @@ static int al7230b_switch_radio_on(struc return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } +static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + static int al7230b_switch_radio_off(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); @@ -255,20 +447,45 @@ static int al7230b_switch_radio_off(stru return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } +/* ZD1211B+AL7230B 6m band edge patching differs slightly from other + * configurations */ +static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, + }; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1) { + ioreqs[0].value = 0x0e; + ioreqs[1].value = 0x10; + } else if (channel == 11) { + ioreqs[0].value = 0x10; + ioreqs[1].value = 0x10; + } + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + int zd_rf_init_al7230b(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); if (chip->is_zd1211b) { - dev_err(zd_chip_dev(chip), "AL7230B is currently not " - "supported for ZD1211B devices\n"); - return -ENODEV; + rf->init_hw = zd1211b_al7230b_init_hw; + rf->switch_radio_on = zd1211b_al7230b_switch_radio_on; + rf->set_channel = zd1211b_al7230b_set_channel; + rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m; + } else { + rf->init_hw = zd1211_al7230b_init_hw; + rf->switch_radio_on = zd1211_al7230b_switch_radio_on; + rf->set_channel = zd1211_al7230b_set_channel; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; } - rf->init_hw = al7230b_init_hw; - rf->set_channel = al7230b_set_channel; - rf->switch_radio_on = al7230b_switch_radio_on; rf->switch_radio_off = al7230b_switch_radio_off; - rf->patch_6m_band_edge = 1; + return 0; } diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c index 5824727..2d736bd 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c @@ -21,7 +21,7 @@ #include "zd_rf.h" #include "zd_usb.h" #include "zd_chip.h" -static u32 rf2959_table[][2] = { +static const u32 rf2959_table[][2] = { RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, @@ -228,7 +228,7 @@ static int rf2959_init_hw(struct zd_rf * static int rf2959_set_channel(struct zd_rf *rf, u8 channel) { int i, r; - u32 *rv = rf2959_table[channel-1]; + const u32 *rv = rf2959_table[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); for (i = 0; i < 2; i++) { diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index edaaad2..e04cffc 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -52,6 +52,7 @@ static struct usb_device_id usb_ids[] = { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, @@ -62,7 +63,10 @@ static struct usb_device_id usb_ids[] = { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, {} @@ -413,7 +417,7 @@ int zd_usb_enable_int(struct zd_usb *usb dev_dbg_f(zd_usb_dev(usb), "\n"); - urb = usb_alloc_urb(0, GFP_NOFS); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { r = -ENOMEM; goto out; @@ -431,7 +435,7 @@ int zd_usb_enable_int(struct zd_usb *usb /* TODO: make it a DMA buffer */ r = -ENOMEM; - transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS); + transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_KERNEL); if (!transfer_buffer) { dev_dbg_f(zd_usb_dev(usb), "couldn't allocate transfer_buffer\n"); @@ -445,7 +449,7 @@ int zd_usb_enable_int(struct zd_usb *usb intr->interval); dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); - r = usb_submit_urb(urb, GFP_NOFS); + r = usb_submit_urb(urb, GFP_KERNEL); if (r) { dev_dbg_f(zd_usb_dev(usb), "Couldn't submit urb. Error number %d\n", r); @@ -594,10 +598,10 @@ static struct urb *alloc_urb(struct zd_u struct urb *urb; void *buffer; - urb = usb_alloc_urb(0, GFP_NOFS); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return NULL; - buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS, + buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_KERNEL, &urb->transfer_dma); if (!buffer) { usb_free_urb(urb); @@ -630,7 +634,7 @@ int zd_usb_enable_rx(struct zd_usb *usb) dev_dbg_f(zd_usb_dev(usb), "\n"); r = -ENOMEM; - urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_NOFS); + urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); if (!urbs) goto error; for (i = 0; i < URBS_COUNT; i++) { @@ -651,7 +655,7 @@ int zd_usb_enable_rx(struct zd_usb *usb) spin_unlock_irq(&rx->lock); for (i = 0; i < URBS_COUNT; i++) { - r = usb_submit_urb(urbs[i], GFP_NOFS); + r = usb_submit_urb(urbs[i], GFP_KERNEL); if (r) goto error_submit; } @@ -1157,7 +1161,7 @@ int zd_usb_ioread16v(struct zd_usb *usb, } req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; req->id = cpu_to_le16(USB_REQ_READ_REGS); @@ -1220,7 +1224,7 @@ int zd_usb_iowrite16v(struct zd_usb *usb req_len = sizeof(struct usb_req_write_regs) + count * sizeof(struct reg_data); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; @@ -1300,7 +1304,7 @@ #endif /* DEBUG */ bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 2412ce4..3f4a7cf 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -1137,7 +1137,6 @@ #endif skb = dev_alloc_skb(pkt_len + 2); if (skb == NULL) break; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ eth_copy_and_sum(skb, rx_skb->data, pkt_len, 0); skb_put(skb, pkt_len); diff --git a/drivers/net/znet.c b/drivers/net/znet.c index b24b072..4032e9f 100644 --- a/drivers/net/znet.c +++ b/drivers/net/znet.c @@ -774,7 +774,6 @@ #endif znet->stats.rx_dropped++; break; } - skb->dev = dev; if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) { int semi_cnt = (znet->rx_end - znet->rx_cur)<<1; diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c index d190c05..3df82fe 100644 --- a/drivers/parisc/led.c +++ b/drivers/parisc/led.c @@ -372,8 +372,6 @@ #else continue; if (LOOPBACK(in_dev->ifa_list->ifa_local)) continue; - if (!dev->get_stats) - continue; stats = dev->get_stats(dev); rx_total += stats->rx_packets; tx_total += stats->tx_packets; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a064f36..b5ac810 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -317,6 +317,10 @@ static int __init acpi_pci_init(void) { int ret; + if (acpi_gbl_FADT.boot_flags & BAF_MSI_NOT_SUPPORTED) { + printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n"); + pci_no_msi(); + } ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index a3c1755..39e80fc 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -434,11 +434,6 @@ int __pci_register_driver(struct pci_dri drv->driver.mod_name = mod_name; drv->driver.kobj.ktype = &pci_driver_kobj_type; - if (pci_multithread_probe) - drv->driver.multithread_probe = pci_multithread_probe; - else - drv->driver.multithread_probe = drv->multithread_probe; - spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); @@ -574,6 +569,7 @@ struct bus_type pci_bus_type = { static int __init pci_driver_init(void) { + pci_bus_type.multithread_probe = pci_multithread_probe; return bus_register(&pci_bus_type); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d3eab05..2a45827 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -13,6 +13,7 @@ #include #include #include #include +#include #include #include #include @@ -891,31 +892,48 @@ pci_disable_device(struct pci_dev *dev) } /** - * pci_enable_wake - enable device to generate PME# when suspended - * @dev: - PCI device to operate on - * @state: - Current state of device. - * @enable: - Flag to enable or disable generation - * - * Set the bits in the device's PM Capabilities to generate PME# when - * the system is suspended. + * pci_enable_wake - enable PCI device as wakeup event source + * @dev: PCI device affected + * @state: PCI state from which device will issue wakeup events + * @enable: True to enable event generation; false to disable * - * -EIO is returned if device doesn't have PM Capabilities. - * -EINVAL is returned if device supports it, but can't generate wake events. - * 0 if operation is successful. - * + * This enables the device as a wakeup event source, or disables it. + * When such events involves platform-specific hooks, those hooks are + * called automatically by this routine. + * + * Devices with legacy power management (no standard PCI PM capabilities) + * always require such platform hooks. Depending on the platform, devices + * supporting the standard PCI PME# signal may require such platform hooks; + * they always update bits in config space to allow PME# generation. + * + * -EIO is returned if the device can't ever be a wakeup event source. + * -EINVAL is returned if the device can't generate wakeup events from + * the specified PCI state. Returns zero if the operation is successful. */ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { int pm; + int status; u16 value; + /* Note that drivers should verify device_may_wakeup(&dev->dev) + * before calling this function. Platform code should report + * errors when drivers try to enable wakeup on devices that + * can't issue wakeups, or on which wakeups were disabled by + * userspace updating the /sys/devices.../power/wakeup file. + */ + + status = call_platform_enable_wakeup(&dev->dev, enable); + /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); - /* If device doesn't support PM Capabilities, but request is to disable - * wake events, it's a nop; otherwise fail */ - if (!pm) - return enable ? -EIO : 0; + /* If device doesn't support PM Capabilities, but caller wants to + * disable wake events, it's a NOP. Otherwise fail unless the + * platform hooks handled this legacy device already. + */ + if (!pm) + return enable ? status : 0; /* Check device's ability to generate PME# */ pci_read_config_word(dev,pm+PCI_PM_PMC,&value); @@ -924,8 +942,14 @@ int pci_enable_wake(struct pci_dev *dev, value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ /* Check if it can generate PME# from requested state. */ - if (!value || !(value & (1 << state))) + if (!value || !(value & (1 << state))) { + /* if it can't, revert what the platform hook changed, + * always reporting the base "EINVAL, can't PME#" error + */ + if (enable) + call_platform_enable_wakeup(&dev->dev, 0); return enable ? -EINVAL : 0; + } pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); @@ -936,7 +960,7 @@ int pci_enable_wake(struct pci_dev *dev, value &= ~PCI_PM_CTRL_PME_ENABLE; pci_write_config_word(dev, pm + PCI_PM_CTRL, value); - + return 0; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 65d6f23..3411483 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1303,119 +1303,6 @@ static void __init quirk_alder_ioapic(st DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EESSC, quirk_alder_ioapic ); #endif -enum ide_combined_type { COMBINED = 0, IDE = 1, LIBATA = 2 }; -/* Defaults to combined */ -static enum ide_combined_type combined_mode; - -static int __init combined_setup(char *str) -{ - if (!strncmp(str, "ide", 3)) - combined_mode = IDE; - else if (!strncmp(str, "libata", 6)) - combined_mode = LIBATA; - else /* "combined" or anything else defaults to old behavior */ - combined_mode = COMBINED; - - return 1; -} -__setup("combined_mode=", combined_setup); - -#ifdef CONFIG_SATA_INTEL_COMBINED -static void __devinit quirk_intel_ide_combined(struct pci_dev *pdev) -{ - u8 prog, comb, tmp; - int ich = 0; - - /* - * Narrow down to Intel SATA PCI devices. - */ - switch (pdev->device) { - /* PCI ids taken from drivers/scsi/ata_piix.c */ - case 0x24d1: - case 0x24df: - case 0x25a3: - case 0x25b0: - ich = 5; - break; - case 0x2651: - case 0x2652: - case 0x2653: - case 0x2680: /* ESB2 */ - ich = 6; - break; - case 0x27c0: - case 0x27c4: - ich = 7; - break; - case 0x2828: /* ICH8M */ - ich = 8; - break; - default: - /* we do not handle this PCI device */ - return; - } - - /* - * Read combined mode register. - */ - pci_read_config_byte(pdev, 0x90, &tmp); /* combined mode reg */ - - if (ich == 5) { - tmp &= 0x6; /* interesting bits 2:1, PATA primary/secondary */ - if (tmp == 0x4) /* bits 10x */ - comb = (1 << 0); /* SATA port 0, PATA port 1 */ - else if (tmp == 0x6) /* bits 11x */ - comb = (1 << 2); /* PATA port 0, SATA port 1 */ - else - return; /* not in combined mode */ - } else { - WARN_ON((ich != 6) && (ich != 7) && (ich != 8)); - tmp &= 0x3; /* interesting bits 1:0 */ - if (tmp & (1 << 0)) - comb = (1 << 2); /* PATA port 0, SATA port 1 */ - else if (tmp & (1 << 1)) - comb = (1 << 0); /* SATA port 0, PATA port 1 */ - else - return; /* not in combined mode */ - } - - /* - * Read programming interface register. - * (Tells us if it's legacy or native mode) - */ - pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog); - - /* if SATA port is in native mode, we're ok. */ - if (prog & comb) - return; - - /* Don't reserve any so the IDE driver can get them (but only if - * combined_mode=ide). - */ - if (combined_mode == IDE) - return; - - /* Grab them both for libata if combined_mode=libata. */ - if (combined_mode == LIBATA) { - request_region(0x1f0, 8, "libata"); /* port 0 */ - request_region(0x170, 8, "libata"); /* port 1 */ - return; - } - - /* SATA port is in legacy mode. Reserve port so that - * IDE driver does not attempt to use it. If request_region - * fails, it will be obvious at boot time, so we don't bother - * checking return values. - */ - if (comb == (1 << 0)) - request_region(0x1f0, 8, "libata"); /* port 0 */ - else - request_region(0x170, 8, "libata"); /* port 1 */ -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_intel_ide_combined ); -#endif /* CONFIG_SATA_INTEL_COMBINED */ - - int pcie_mch_quirk; EXPORT_SYMBOL(pcie_mch_quirk); diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index 91c047a..dd6384b 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -311,7 +311,6 @@ done: return NULL; found: - down_write(&dev->dev.bus->subsys.rwsem); dev->card_link = clink; dev->dev.driver = &drv->link.driver; if (pnp_bus_type.probe(&dev->dev)) @@ -319,14 +318,11 @@ found: if (device_bind_driver(&dev->dev)) goto err_out; - up_write(&dev->dev.bus->subsys.rwsem); - return dev; err_out: dev->dev.driver = NULL; dev->card_link = NULL; - up_write(&dev->dev.bus->subsys.rwsem); return NULL; } @@ -340,11 +336,9 @@ void pnp_release_card_device(struct pnp_ struct pnp_card_driver * drv = dev->card_link->driver; if (!drv) return; - down_write(&dev->dev.bus->subsys.rwsem); drv->link.remove = &card_remove; device_release_driver(&dev->dev); drv->link.remove = &card_remove_first; - up_write(&dev->dev.bus->subsys.rwsem); } /* diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c index 6c12744..7d7cab1 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/vuart.c @@ -82,14 +82,6 @@ struct ports_bmp { u64 unused[3]; } __attribute__ ((aligned (32))); -/* redefine dev_dbg to do a syntax check */ - -#if !defined(DEBUG) -#undef dev_dbg -static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg( - const struct device *_dev, const char *fmt, ...) {return 0;} -#endif - #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) static void __attribute__ ((unused)) _dump_ports_bmp( const struct ports_bmp* bmp, const char* func, int line) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index eb5dc62..e71929d 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -398,6 +398,9 @@ dasd_change_state(struct dasd_device *de if (device->state == device->target) wake_up(&dasd_init_waitq); + + /* let user-space know that the device status changed */ + kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE); } /* diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index ed70852..6a89cef 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -19,6 +19,7 @@ #include #include #include +#include /* This is ugly... */ #define PRINTK_HEADER "dasd_devmap:" @@ -133,6 +134,8 @@ dasd_call_setup(char *str) __setup ("dasd=", dasd_call_setup); #endif /* #ifndef MODULE */ +#define DASD_IPLDEV "ipldev" + /* * Read a device busid/devno from a string. */ @@ -141,6 +144,20 @@ dasd_busid(char **str, int *id0, int *id { int val, old_style; + /* Interpret ipldev busid */ + if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) { + if (ipl_info.type != IPL_TYPE_CCW) { + MESSAGE(KERN_ERR, "%s", "ipl device is not a ccw " + "device"); + return -EINVAL; + } + *id0 = 0; + *id1 = ipl_info.data.ccw.dev_id.ssid; + *devno = ipl_info.data.ccw.dev_id.devno; + *str += strlen(DASD_IPLDEV); + + return 0; + } /* check for leading '0x' */ old_style = 0; if ((*str)[0] == '0' && (*str)[1] == 'x') { @@ -829,6 +846,46 @@ dasd_discipline_show(struct device *dev, static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); static ssize_t +dasd_device_status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dasd_device *device; + ssize_t len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (!IS_ERR(device)) { + switch (device->state) { + case DASD_STATE_NEW: + len = snprintf(buf, PAGE_SIZE, "new\n"); + break; + case DASD_STATE_KNOWN: + len = snprintf(buf, PAGE_SIZE, "detected\n"); + break; + case DASD_STATE_BASIC: + len = snprintf(buf, PAGE_SIZE, "basic\n"); + break; + case DASD_STATE_UNFMT: + len = snprintf(buf, PAGE_SIZE, "unformatted\n"); + break; + case DASD_STATE_READY: + len = snprintf(buf, PAGE_SIZE, "ready\n"); + break; + case DASD_STATE_ONLINE: + len = snprintf(buf, PAGE_SIZE, "online\n"); + break; + default: + len = snprintf(buf, PAGE_SIZE, "no stat\n"); + break; + } + dasd_put_device(device); + } else + len = snprintf(buf, PAGE_SIZE, "unknown\n"); + return len; +} + +static DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL); + +static ssize_t dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dasd_devmap *devmap; @@ -939,6 +996,7 @@ static DEVICE_ATTR(eer_enabled, 0644, da static struct attribute * dasd_attrs[] = { &dev_attr_readonly.attr, &dev_attr_discipline.attr, + &dev_attr_status.attr, &dev_attr_alias.attr, &dev_attr_vendor.attr, &dev_attr_uid.attr, diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 293e667..c210784 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # S/390 character devices # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_info.o + sclp_info.o sclp_config.o sclp_chp.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o @@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34x obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONWRITER) += monwriter.o + +zcore_mod-objs := sclp_sdias.o zcore.o +obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 9a328f1..6000bde 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -813,12 +813,6 @@ con3215_unblank(void) spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } -static int __init -con3215_consetup(struct console *co, char *options) -{ - return 0; -} - /* * The console structure for the 3215 console */ @@ -827,7 +821,6 @@ static struct console con3215 = { .write = con3215_write, .device = con3215_device, .unblank = con3215_unblank, - .setup = con3215_consetup, .flags = CON_PRINTBUFFER, }; diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 8e7f2d7..fd34791 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -555,12 +555,6 @@ con3270_unblank(void) spin_unlock_irqrestore(&cp->view.lock, flags); } -static int __init -con3270_consetup(struct console *co, char *options) -{ - return 0; -} - /* * The console structure for the 3270 console */ @@ -569,7 +563,6 @@ static struct console con3270 = { .write = con3270_write, .device = con3270_device, .unblank = con3270_unblank, - .setup = con3270_consetup, .flags = CON_PRINTBUFFER, }; diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index f171de3..fa62e69 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -15,6 +15,7 @@ #include #include #include #include +#include #include #include @@ -510,7 +511,7 @@ sclp_state_change_cb(struct evbuf_header } static struct sclp_register sclp_state_change_event = { - .receive_mask = EvTyp_StateChange_Mask, + .receive_mask = EVTYP_STATECHANGE_MASK, .receiver_fn = sclp_state_change_cb }; @@ -930,3 +931,10 @@ sclp_init(void) sclp_init_mask(1); return 0; } + +static __init int sclp_initcall(void) +{ + return sclp_init(); +} + +arch_initcall(sclp_initcall); diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 7d29ab4..87ac4a3 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -19,33 +19,37 @@ #include #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) #define MAX_CONSOLE_PAGES 4 -#define EvTyp_OpCmd 0x01 -#define EvTyp_Msg 0x02 -#define EvTyp_StateChange 0x08 -#define EvTyp_PMsgCmd 0x09 -#define EvTyp_CntlProgOpCmd 0x20 -#define EvTyp_CntlProgIdent 0x0B -#define EvTyp_SigQuiesce 0x1D -#define EvTyp_VT220Msg 0x1A - -#define EvTyp_OpCmd_Mask 0x80000000 -#define EvTyp_Msg_Mask 0x40000000 -#define EvTyp_StateChange_Mask 0x01000000 -#define EvTyp_PMsgCmd_Mask 0x00800000 -#define EvTyp_CtlProgOpCmd_Mask 0x00000001 -#define EvTyp_CtlProgIdent_Mask 0x00200000 -#define EvTyp_SigQuiesce_Mask 0x00000008 -#define EvTyp_VT220Msg_Mask 0x00000040 - -#define GnrlMsgFlgs_DOM 0x8000 -#define GnrlMsgFlgs_SndAlrm 0x4000 -#define GnrlMsgFlgs_HoldMsg 0x2000 - -#define LnTpFlgs_CntlText 0x8000 -#define LnTpFlgs_LabelText 0x4000 -#define LnTpFlgs_DataText 0x2000 -#define LnTpFlgs_EndText 0x1000 -#define LnTpFlgs_PromptText 0x0800 +#define EVTYP_OPCMD 0x01 +#define EVTYP_MSG 0x02 +#define EVTYP_STATECHANGE 0x08 +#define EVTYP_PMSGCMD 0x09 +#define EVTYP_CNTLPROGOPCMD 0x20 +#define EVTYP_CNTLPROGIDENT 0x0B +#define EVTYP_SIGQUIESCE 0x1D +#define EVTYP_VT220MSG 0x1A +#define EVTYP_CONFMGMDATA 0x04 +#define EVTYP_SDIAS 0x1C + +#define EVTYP_OPCMD_MASK 0x80000000 +#define EVTYP_MSG_MASK 0x40000000 +#define EVTYP_STATECHANGE_MASK 0x01000000 +#define EVTYP_PMSGCMD_MASK 0x00800000 +#define EVTYP_CTLPROGOPCMD_MASK 0x00000001 +#define EVTYP_CTLPROGIDENT_MASK 0x00200000 +#define EVTYP_SIGQUIESCE_MASK 0x00000008 +#define EVTYP_VT220MSG_MASK 0x00000040 +#define EVTYP_CONFMGMDATA_MASK 0x10000000 +#define EVTYP_SDIAS_MASK 0x00000010 + +#define GNRLMSGFLGS_DOM 0x8000 +#define GNRLMSGFLGS_SNDALRM 0x4000 +#define GNRLMSGFLGS_HOLDMSG 0x2000 + +#define LNTPFLGS_CNTLTEXT 0x8000 +#define LNTPFLGS_LABELTEXT 0x4000 +#define LNTPFLGS_DATATEXT 0x2000 +#define LNTPFLGS_ENDTEXT 0x1000 +#define LNTPFLGS_PROMPTTEXT 0x0800 typedef unsigned int sclp_cmdw_t; @@ -56,15 +60,15 @@ #define SCLP_CMDW_READ_SCP_INFO 0x00020 #define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 #define GDS_ID_MDSMU 0x1310 -#define GDS_ID_MDSRouteInfo 0x1311 -#define GDS_ID_AgUnWrkCorr 0x1549 -#define GDS_ID_SNACondReport 0x1532 +#define GDS_ID_MDSROUTEINFO 0x1311 +#define GDS_ID_AGUNWRKCORR 0x1549 +#define GDS_ID_SNACONDREPORT 0x1532 #define GDS_ID_CPMSU 0x1212 -#define GDS_ID_RoutTargInstr 0x154D -#define GDS_ID_OpReq 0x8070 -#define GDS_ID_TextCmd 0x1320 +#define GDS_ID_ROUTTARGINSTR 0x154D +#define GDS_ID_OPREQ 0x8070 +#define GDS_ID_TEXTCMD 0x1320 -#define GDS_KEY_SelfDefTextMsg 0x31 +#define GDS_KEY_SELFDEFTEXTMSG 0x31 typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c new file mode 100644 index 0000000..a66b914 --- /dev/null +++ b/drivers/s390/char/sclp_chp.c @@ -0,0 +1,196 @@ +/* + * drivers/s390/char/sclp_chp.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#include +#include +#include +#include +#include +#include + +#include "sclp.h" + +#define TAG "sclp_chp: " + +#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001 +#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001 +#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001 + +static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid) +{ + return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8; +} + +static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid) +{ + return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8; +} + +static void chp_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +struct chp_cfg_sccb { + struct sccb_header header; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +struct chp_cfg_data { + struct chp_cfg_sccb sccb; + struct sclp_req req; + struct completion completion; +} __attribute__((packed)); + +static int do_configure(sclp_cmdw_t cmd) +{ + struct chp_cfg_data *data; + int rc; + + /* Prepare sccb. */ + data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + data->sccb.header.length = sizeof(struct chp_cfg_sccb); + data->req.command = cmd; + data->req.sccb = &(data->sccb); + data->req.status = SCLP_REQ_FILLED; + data->req.callback = chp_callback; + data->req.callback_data = &(data->completion); + init_completion(&data->completion); + + /* Perform sclp request. */ + rc = sclp_add_request(&(data->req)); + if (rc) + goto out; + wait_for_completion(&data->completion); + + /* Check response .*/ + if (data->req.status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "configure channel-path request failed " + "(status=0x%02x)\n", data->req.status); + rc = -EIO; + goto out; + } + switch (data->sccb.header.response_code) { + case 0x0020: + case 0x0120: + case 0x0440: + case 0x0450: + break; + default: + printk(KERN_WARNING TAG "configure channel-path failed " + "(cmd=0x%08x, response=0x%04x)\n", cmd, + data->sccb.header.response_code); + rc = -EIO; + break; + } +out: + free_page((unsigned long) data); + + return rc; +} + +/** + * sclp_chp_configure - perform configure channel-path sclp command + * @chpid: channel-path ID + * + * Perform configure channel-path command sclp command for specified chpid. + * Return 0 after command successfully finished, non-zero otherwise. + */ +int sclp_chp_configure(struct chp_id chpid) +{ + return do_configure(get_configure_cmdw(chpid)); +} + +/** + * sclp_chp_deconfigure - perform deconfigure channel-path sclp command + * @chpid: channel-path ID + * + * Perform deconfigure channel-path command sclp command for specified chpid + * and wait for completion. On success return 0. Return non-zero otherwise. + */ +int sclp_chp_deconfigure(struct chp_id chpid) +{ + return do_configure(get_deconfigure_cmdw(chpid)); +} + +struct chp_info_sccb { + struct sccb_header header; + u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; + u8 standby[SCLP_CHP_INFO_MASK_SIZE]; + u8 configured[SCLP_CHP_INFO_MASK_SIZE]; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +struct chp_info_data { + struct chp_info_sccb sccb; + struct sclp_req req; + struct completion completion; +} __attribute__((packed)); + +/** + * sclp_chp_read_info - perform read channel-path information sclp command + * @info: resulting channel-path information data + * + * Perform read channel-path information sclp command and wait for completion. + * On success, store channel-path information in @info and return 0. Return + * non-zero otherwise. + */ +int sclp_chp_read_info(struct sclp_chp_info *info) +{ + struct chp_info_data *data; + int rc; + + /* Prepare sccb. */ + data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + data->sccb.header.length = sizeof(struct chp_info_sccb); + data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION; + data->req.sccb = &(data->sccb); + data->req.status = SCLP_REQ_FILLED; + data->req.callback = chp_callback; + data->req.callback_data = &(data->completion); + init_completion(&data->completion); + + /* Perform sclp request. */ + rc = sclp_add_request(&(data->req)); + if (rc) + goto out; + wait_for_completion(&data->completion); + + /* Check response .*/ + if (data->req.status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "read channel-path info request failed " + "(status=0x%02x)\n", data->req.status); + rc = -EIO; + goto out; + } + if (data->sccb.header.response_code != 0x0010) { + printk(KERN_WARNING TAG "read channel-path info failed " + "(response=0x%04x)\n", data->sccb.header.response_code); + rc = -EIO; + goto out; + } + memcpy(info->recognized, data->sccb.recognized, + SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->standby, data->sccb.standby, + SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->configured, data->sccb.configured, + SCLP_CHP_INFO_MASK_SIZE); +out: + free_page((unsigned long) data); + + return rc; +} diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c new file mode 100644 index 0000000..5322e5e --- /dev/null +++ b/drivers/s390/char/sclp_config.c @@ -0,0 +1,75 @@ +/* + * drivers/s390/char/sclp_config.c + * + * Copyright IBM Corp. 2007 + * Author(s): Heiko Carstens + */ + +#include +#include +#include +#include +#include +#include "sclp.h" + +#define TAG "sclp_config: " + +struct conf_mgm_data { + u8 reserved; + u8 ev_qualifier; +} __attribute__((packed)); + +#define EV_QUAL_CAP_CHANGE 3 + +static struct work_struct sclp_cpu_capability_work; + +static void sclp_cpu_capability_notify(struct work_struct *work) +{ + int cpu; + struct sys_device *sysdev; + + printk(KERN_WARNING TAG "cpu capability changed.\n"); + lock_cpu_hotplug(); + for_each_online_cpu(cpu) { + sysdev = get_cpu_sysdev(cpu); + kobject_uevent(&sysdev->kobj, KOBJ_CHANGE); + } + unlock_cpu_hotplug(); +} + +static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) +{ + struct conf_mgm_data *cdata; + + cdata = (struct conf_mgm_data *)(evbuf + 1); + if (cdata->ev_qualifier == EV_QUAL_CAP_CHANGE) + schedule_work(&sclp_cpu_capability_work); +} + +static struct sclp_register sclp_conf_register = +{ + .receive_mask = EVTYP_CONFMGMDATA_MASK, + .receiver_fn = sclp_conf_receiver_fn, +}; + +static int __init sclp_conf_init(void) +{ + int rc; + + INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify); + + rc = sclp_register(&sclp_conf_register); + if (rc) { + printk(KERN_ERR TAG "failed to register (%d).\n", rc); + return rc; + } + + if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK)) { + printk(KERN_WARNING TAG "no configuration management.\n"); + sclp_unregister(&sclp_conf_register); + rc = -ENOSYS; + } + return rc; +} + +__initcall(sclp_conf_init); diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c index 65aa2c8..29fe2a5 100644 --- a/drivers/s390/char/sclp_cpi.c +++ b/drivers/s390/char/sclp_cpi.c @@ -46,7 +46,7 @@ struct cpi_sccb { /* Event type structure for write message and write priority message */ static struct sclp_register sclp_cpi_event = { - .send_mask = EvTyp_CtlProgIdent_Mask + .send_mask = EVTYP_CTLPROGIDENT_MASK }; MODULE_LICENSE("GPL"); @@ -201,7 +201,7 @@ cpi_module_init(void) "console.\n"); return -EINVAL; } - if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) { + if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { printk(KERN_WARNING "cpi: no control program identification " "support\n"); sclp_unregister(&sclp_cpi_event); diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c index baa8fe6..45ff25e 100644 --- a/drivers/s390/char/sclp_quiesce.c +++ b/drivers/s390/char/sclp_quiesce.c @@ -43,7 +43,7 @@ sclp_quiesce_handler(struct evbuf_header } static struct sclp_register sclp_quiesce_event = { - .receive_mask = EvTyp_SigQuiesce_Mask, + .receive_mask = EVTYP_SIGQUIESCE_MASK, .receiver_fn = sclp_quiesce_handler }; diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 2486783..bbd5b8b 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -30,7 +30,7 @@ #define MAX_SCCB_ROOM (PAGE_SIZE - sizeo /* Event type structure for write message and write priority message */ static struct sclp_register sclp_rw_event = { - .send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask + .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK }; /* @@ -64,7 +64,7 @@ sclp_make_buffer(void *page, unsigned sh memset(sccb, 0, sizeof(struct write_sccb)); sccb->header.length = sizeof(struct write_sccb); sccb->msg_buf.header.length = sizeof(struct msg_buf); - sccb->msg_buf.header.type = EvTyp_Msg; + sccb->msg_buf.header.type = EVTYP_MSG; sccb->msg_buf.mdb.header.length = sizeof(struct mdb); sccb->msg_buf.mdb.header.type = 1; sccb->msg_buf.mdb.header.tag = 0xD4C4C240; /* ebcdic "MDB " */ @@ -114,7 +114,7 @@ sclp_initialize_mto(struct sclp_buffer * memset(mto, 0, sizeof(struct mto)); mto->length = sizeof(struct mto); mto->type = 4; /* message text object */ - mto->line_type_flags = LnTpFlgs_EndText; /* end text */ + mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */ /* set pointer to first byte after struct mto. */ buffer->current_line = (char *) (mto + 1); @@ -215,7 +215,7 @@ sclp_write(struct sclp_buffer *buffer, c case '\a': /* bell, one for several times */ /* set SCLP sound alarm bit in General Object */ buffer->sccb->msg_buf.mdb.go.general_msg_flags |= - GnrlMsgFlgs_SndAlrm; + GNRLMSGFLGS_SNDALRM; break; case '\t': /* horizontal tabulator */ /* check if new mto needs to be created */ @@ -452,12 +452,12 @@ sclp_emit_buffer(struct sclp_buffer *buf return -EIO; sccb = buffer->sccb; - if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask) + if (sclp_rw_event.sclp_send_mask & EVTYP_MSG_MASK) /* Use normal write message */ - sccb->msg_buf.header.type = EvTyp_Msg; - else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask) + sccb->msg_buf.header.type = EVTYP_MSG; + else if (sclp_rw_event.sclp_send_mask & EVTYP_PMSGCMD_MASK) /* Use write priority message */ - sccb->msg_buf.header.type = EvTyp_PMsgCmd; + sccb->msg_buf.header.type = EVTYP_PMSGCMD; else return -ENOSYS; buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA; diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c new file mode 100644 index 0000000..52283da --- /dev/null +++ b/drivers/s390/char/sclp_sdias.c @@ -0,0 +1,255 @@ +/* + * Sclp "store data in absolut storage" + * + * Copyright IBM Corp. 2003,2007 + * Author(s): Michael Holzheu + */ + +#include +#include +#include +#include +#include "sclp.h" +#include "sclp_rw.h" + +#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x) +#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x ) + +#define SDIAS_RETRIES 300 +#define SDIAS_SLEEP_TICKS 50 + +#define EQ_STORE_DATA 0x0 +#define EQ_SIZE 0x1 +#define DI_FCP_DUMP 0x0 +#define ASA_SIZE_32 0x0 +#define ASA_SIZE_64 0x1 +#define EVSTATE_ALL_STORED 0x0 +#define EVSTATE_NO_DATA 0x3 +#define EVSTATE_PART_STORED 0x10 + +static struct debug_info *sdias_dbf; + +static struct sclp_register sclp_sdias_register = { + .send_mask = EVTYP_SDIAS_MASK, +}; + +struct sdias_evbuf { + struct evbuf_header hdr; + u8 event_qual; + u8 data_id; + u64 reserved2; + u32 event_id; + u16 reserved3; + u8 asa_size; + u8 event_status; + u32 reserved4; + u32 blk_cnt; + u64 asa; + u32 reserved5; + u32 fbn; + u32 reserved6; + u32 lbn; + u16 reserved7; + u16 dbs; +} __attribute__((packed)); + +struct sdias_sccb { + struct sccb_header hdr; + struct sdias_evbuf evbuf; +} __attribute__((packed)); + +static struct sdias_sccb sccb __attribute__((aligned(4096))); + +static int sclp_req_done; +static wait_queue_head_t sdias_wq; +static DEFINE_MUTEX(sdias_mutex); + +static void sdias_callback(struct sclp_req *request, void *data) +{ + struct sdias_sccb *sccb; + + sccb = (struct sdias_sccb *) request->sccb; + sclp_req_done = 1; + wake_up(&sdias_wq); /* Inform caller, that request is complete */ + TRACE("callback done\n"); +} + +static int sdias_sclp_send(struct sclp_req *req) +{ + int retries; + int rc; + + for (retries = SDIAS_RETRIES; retries; retries--) { + sclp_req_done = 0; + TRACE("add request\n"); + rc = sclp_add_request(req); + if (rc) { + /* not initiated, wait some time and retry */ + set_current_state(TASK_INTERRUPTIBLE); + TRACE("add request failed: rc = %i\n",rc); + schedule_timeout(SDIAS_SLEEP_TICKS); + continue; + } + /* initiated, wait for completion of service call */ + wait_event(sdias_wq, (sclp_req_done == 1)); + if (req->status == SCLP_REQ_FAILED) { + TRACE("sclp request failed\n"); + rc = -EIO; + continue; + } + TRACE("request done\n"); + break; + } + return rc; +} + +/* + * Get number of blocks (4K) available in the HSA + */ +int sclp_sdias_blk_count(void) +{ + struct sclp_req request; + int rc; + + mutex_lock(&sdias_mutex); + + memset(&sccb, 0, sizeof(sccb)); + memset(&request, 0, sizeof(request)); + + sccb.hdr.length = sizeof(sccb); + sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb.evbuf.hdr.type = EVTYP_SDIAS; + sccb.evbuf.event_qual = EQ_SIZE; + sccb.evbuf.data_id = DI_FCP_DUMP; + sccb.evbuf.event_id = 4712; + sccb.evbuf.dbs = 1; + + request.sccb = &sccb; + request.command = SCLP_CMDW_WRITE_EVENT_DATA; + request.status = SCLP_REQ_FILLED; + request.callback = sdias_callback; + + rc = sdias_sclp_send(&request); + if (rc) { + ERROR_MSG("sclp_send failed for get_nr_blocks\n"); + goto out; + } + if (sccb.hdr.response_code != 0x0020) { + TRACE("send failed: %x\n", sccb.hdr.response_code); + rc = -EIO; + goto out; + } + + switch (sccb.evbuf.event_status) { + case 0: + rc = sccb.evbuf.blk_cnt; + break; + default: + ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status); + rc = -EIO; + goto out; + } + TRACE("%i blocks\n", rc); +out: + mutex_unlock(&sdias_mutex); + return rc; +} + +/* + * Copy from HSA to absolute storage (not reentrant): + * + * @dest : Address of buffer where data should be copied + * @start_blk: Start Block (beginning with 1) + * @nr_blks : Number of 4K blocks to copy + * + * Return Value: 0 : Requested 'number' of blocks of data copied + * <0: ERROR - negative event status + */ +int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) +{ + struct sclp_req request; + int rc; + + mutex_lock(&sdias_mutex); + + memset(&sccb, 0, sizeof(sccb)); + memset(&request, 0, sizeof(request)); + + sccb.hdr.length = sizeof(sccb); + sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb.evbuf.hdr.type = EVTYP_SDIAS; + sccb.evbuf.hdr.flags = 0; + sccb.evbuf.event_qual = EQ_STORE_DATA; + sccb.evbuf.data_id = DI_FCP_DUMP; + sccb.evbuf.event_id = 4712; +#ifdef __s390x__ + sccb.evbuf.asa_size = ASA_SIZE_64; +#else + sccb.evbuf.asa_size = ASA_SIZE_32; +#endif + sccb.evbuf.event_status = 0; + sccb.evbuf.blk_cnt = nr_blks; + sccb.evbuf.asa = (unsigned long)dest; + sccb.evbuf.fbn = start_blk; + sccb.evbuf.lbn = 0; + sccb.evbuf.dbs = 1; + + request.sccb = &sccb; + request.command = SCLP_CMDW_WRITE_EVENT_DATA; + request.status = SCLP_REQ_FILLED; + request.callback = sdias_callback; + + rc = sdias_sclp_send(&request); + if (rc) { + ERROR_MSG("sclp_send failed: %x\n", rc); + goto out; + } + if (sccb.hdr.response_code != 0x0020) { + TRACE("copy failed: %x\n", sccb.hdr.response_code); + rc = -EIO; + goto out; + } + + switch (sccb.evbuf.event_status) { + case EVSTATE_ALL_STORED: + TRACE("all stored\n"); + case EVSTATE_PART_STORED: + TRACE("part stored: %i\n", sccb.evbuf.blk_cnt); + break; + case EVSTATE_NO_DATA: + TRACE("no data\n"); + default: + ERROR_MSG("Error from SCLP while copying hsa. " + "Event status = %x\n", + sccb.evbuf.event_status); + rc = -EIO; + } +out: + mutex_unlock(&sdias_mutex); + return rc; +} + +int __init sdias_init(void) +{ + int rc; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return 0; + sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); + debug_register_view(sdias_dbf, &debug_sprintf_view); + debug_set_level(sdias_dbf, 6); + rc = sclp_register(&sclp_sdias_register); + if (rc) { + ERROR_MSG("sclp register failed\n"); + return rc; + } + init_waitqueue_head(&sdias_wq); + TRACE("init done\n"); + return 0; +} + +void __exit sdias_exit(void) +{ + debug_unregister(sdias_dbf); + sclp_unregister(&sclp_sdias_register); +} diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 076816b..e3b3d39 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -648,7 +648,7 @@ sclp_eval_textcmd(struct gds_subvector * subvec = start; while (subvec < end) { subvec = find_gds_subvector(subvec, end, - GDS_KEY_SelfDefTextMsg); + GDS_KEY_SELFDEFTEXTMSG); if (!subvec) break; sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), @@ -664,7 +664,7 @@ sclp_eval_cpmsu(struct gds_vector *start vec = start; while (vec < end) { - vec = find_gds_vector(vec, end, GDS_ID_TextCmd); + vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD); if (!vec) break; sclp_eval_textcmd((struct gds_subvector *)(vec + 1), @@ -703,7 +703,7 @@ sclp_tty_state_change(struct sclp_regist static struct sclp_register sclp_input_event = { - .receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask, + .receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK, .state_change_fn = sclp_tty_state_change, .receiver_fn = sclp_tty_receiver }; diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index f77dc33..7263347 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -99,8 +99,8 @@ static void sclp_vt220_emit_current(void /* Registration structure for our interest in SCLP event buffers */ static struct sclp_register sclp_vt220_register = { - .send_mask = EvTyp_VT220Msg_Mask, - .receive_mask = EvTyp_VT220Msg_Mask, + .send_mask = EVTYP_VT220MSG_MASK, + .receive_mask = EVTYP_VT220MSG_MASK, .state_change_fn = NULL, .receiver_fn = sclp_vt220_receiver_fn }; @@ -202,7 +202,7 @@ sclp_vt220_callback(struct sclp_req *req static int __sclp_vt220_emit(struct sclp_vt220_request *request) { - if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) { + if (!(sclp_vt220_register.sclp_send_mask & EVTYP_VT220MSG_MASK)) { request->sclp_req.status = SCLP_REQ_FAILED; return -EIO; } @@ -284,7 +284,7 @@ sclp_vt220_initialize_page(void *page) sccb->header.length = sizeof(struct sclp_vt220_sccb); sccb->header.function_code = SCLP_NORMAL_WRITE; sccb->header.response_code = 0x0000; - sccb->evbuf.type = EvTyp_VT220Msg; + sccb->evbuf.type = EVTYP_VT220MSG; sccb->evbuf.length = sizeof(struct evbuf_header); return request; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index b87d3b0..a5a00e9 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -125,7 +125,7 @@ static struct vmlogrdr_priv_t sys_ser[] .recording_name = "EREP", .minor_num = 0, .buffer_free = 1, - .priv_lock = SPIN_LOCK_UNLOCKED, + .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock), .autorecording = 1, .autopurge = 1, }, @@ -134,7 +134,7 @@ static struct vmlogrdr_priv_t sys_ser[] .recording_name = "ACCOUNT", .minor_num = 1, .buffer_free = 1, - .priv_lock = SPIN_LOCK_UNLOCKED, + .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock), .autorecording = 1, .autopurge = 1, }, @@ -143,7 +143,7 @@ static struct vmlogrdr_priv_t sys_ser[] .recording_name = "SYMPTOM", .minor_num = 2, .buffer_free = 1, - .priv_lock = SPIN_LOCK_UNLOCKED, + .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock), .autorecording = 1, .autopurge = 1, } @@ -385,6 +385,9 @@ static int vmlogrdr_release (struct inod struct vmlogrdr_priv_t * logptr = filp->private_data; + iucv_path_sever(logptr->path, NULL); + kfree(logptr->path); + logptr->path = NULL; if (logptr->autorecording) { ret = vmlogrdr_recording(logptr,0,logptr->autopurge); if (ret) diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c new file mode 100644 index 0000000..89d4393 --- /dev/null +++ b/drivers/s390/char/zcore.c @@ -0,0 +1,651 @@ +/* + * zcore module to export memory content and register sets for creating system + * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same + * dump format as s390 standalone dumps. + * + * For more information please refer to Documentation/s390/zfcpdump.txt + * + * Copyright IBM Corp. 2003,2007 + * Author(s): Michael Holzheu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) +#define MSG(x...) printk( KERN_ALERT x ) +#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x ) + +#define TO_USER 0 +#define TO_KERNEL 1 + +enum arch_id { + ARCH_S390 = 0, + ARCH_S390X = 1, +}; + +/* dump system info */ + +struct sys_info { + enum arch_id arch; + unsigned long sa_base; + u32 sa_size; + int cpu_map[NR_CPUS]; + unsigned long mem_size; + union save_area lc_mask; +}; + +static struct sys_info sys_info; +static struct debug_info *zcore_dbf; +static int hsa_available; +static struct dentry *zcore_dir; +static struct dentry *zcore_file; + +/* + * Copy memory from HSA to kernel or user memory (not reentrant): + * + * @dest: Kernel or user buffer where memory should be copied to + * @src: Start address within HSA where data should be copied + * @count: Size of buffer, which should be copied + * @mode: Either TO_KERNEL or TO_USER + */ +static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) +{ + int offs, blk_num; + static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); + + if (count == 0) + return 0; + + /* copy first block */ + offs = 0; + if ((src % PAGE_SIZE) != 0) { + blk_num = src / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count); + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest, + buf + (src % PAGE_SIZE), offs)) + return -EFAULT; + } else + memcpy(dest, buf + (src % PAGE_SIZE), offs); + } + if (offs == count) + goto out; + + /* copy middle */ + for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) { + blk_num = (src + offs) / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest + offs, + buf, PAGE_SIZE)) + return -EFAULT; + } else + memcpy(dest + offs, buf, PAGE_SIZE); + } + if (offs == count) + goto out; + + /* copy last block */ + blk_num = (src + offs) / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest + offs, buf, + PAGE_SIZE)) + return -EFAULT; + } else + memcpy(dest + offs, buf, count - offs); +out: + return 0; +} + +static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count) +{ + return memcpy_hsa((void __force *) dest, src, count, TO_USER); +} + +static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count) +{ + return memcpy_hsa(dest, src, count, TO_KERNEL); +} + +static int memcpy_real(void *dest, unsigned long src, size_t count) +{ + unsigned long flags; + int rc = -EFAULT; + register unsigned long _dest asm("2") = (unsigned long) dest; + register unsigned long _len1 asm("3") = (unsigned long) count; + register unsigned long _src asm("4") = src; + register unsigned long _len2 asm("5") = (unsigned long) count; + + if (count == 0) + return 0; + flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */ + asm volatile ( + "0: mvcle %1,%2,0x0\n" + "1: jo 0b\n" + " lhi %0,0x0\n" + "2:\n" + EX_TABLE(1b,2b) + : "+d" (rc) + : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2) + : "cc", "memory"); + __raw_local_irq_ssm(flags); + + return rc; +} + +static int memcpy_real_user(__user void *dest, unsigned long src, size_t count) +{ + static char buf[4096]; + int offs = 0, size; + + while (offs < count) { + size = min(sizeof(buf), count - offs); + if (memcpy_real(buf, src + offs, size)) + return -EFAULT; + if (copy_to_user(dest + offs, buf, size)) + return -EFAULT; + offs += size; + } + return 0; +} + +#ifdef __s390x__ +/* + * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info + */ +static void __init s390x_to_s390_regs(union save_area *out, union save_area *in, + int cpu) +{ + int i; + + for (i = 0; i < 16; i++) { + out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff; + out->s390.acc_regs[i] = in->s390x.acc_regs[i]; + out->s390.ctrl_regs[i] = + in->s390x.ctrl_regs[i] & 0x00000000ffffffff; + } + /* locore for 31 bit has only space for fpregs 0,2,4,6 */ + out->s390.fp_regs[0] = in->s390x.fp_regs[0]; + out->s390.fp_regs[1] = in->s390x.fp_regs[2]; + out->s390.fp_regs[2] = in->s390x.fp_regs[4]; + out->s390.fp_regs[3] = in->s390x.fp_regs[6]; + memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4); + out->s390.psw[1] |= 0x8; /* set bit 12 */ + memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4); + out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */ + out->s390.pref_reg = in->s390x.pref_reg; + out->s390.timer = in->s390x.timer; + out->s390.clk_cmp = in->s390x.clk_cmp; +} + +static void __init s390x_to_s390_save_areas(void) +{ + int i = 1; + static union save_area tmp; + + while (zfcpdump_save_areas[i]) { + s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i); + memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp)); + i++; + } +} + +#endif /* __s390x__ */ + +static int __init init_cpu_info(enum arch_id arch) +{ + union save_area *sa; + + /* get info for boot cpu from lowcore, stored in the HSA */ + + sa = kmalloc(sizeof(*sa), GFP_KERNEL); + if (!sa) { + ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__); + return -ENOMEM; + } + if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { + ERROR_MSG("could not copy from HSA\n"); + kfree(sa); + return -EIO; + } + zfcpdump_save_areas[0] = sa; + +#ifdef __s390x__ + /* convert s390x regs to s390, if we are dumping an s390 Linux */ + + if (arch == ARCH_S390) + s390x_to_s390_save_areas(); +#endif + + return 0; +} + +static DEFINE_MUTEX(zcore_mutex); + +#define DUMP_VERSION 0x3 +#define DUMP_MAGIC 0xa8190173618f23fdULL +#define DUMP_ARCH_S390X 2 +#define DUMP_ARCH_S390 1 +#define HEADER_SIZE 4096 + +/* dump header dumped according to s390 crash dump format */ + +struct zcore_header { + u64 magic; + u32 version; + u32 header_size; + u32 dump_level; + u32 page_size; + u64 mem_size; + u64 mem_start; + u64 mem_end; + u32 num_pages; + u32 pad1; + u64 tod; + cpuid_t cpu_id; + u32 arch_id; + u32 build_arch; + char pad2[4016]; +} __attribute__((packed,__aligned__(16))); + +static struct zcore_header zcore_header = { + .magic = DUMP_MAGIC, + .version = DUMP_VERSION, + .header_size = 4096, + .dump_level = 0, + .page_size = PAGE_SIZE, + .mem_start = 0, +#ifdef __s390x__ + .build_arch = DUMP_ARCH_S390X, +#else + .build_arch = DUMP_ARCH_S390, +#endif +}; + +/* + * Copy lowcore info to buffer. Use map in order to copy only register parts. + * + * @buf: User buffer + * @sa: Pointer to save area + * @sa_off: Offset in save area to copy + * @len: Number of bytes to copy + */ +static int copy_lc(void __user *buf, void *sa, int sa_off, int len) +{ + int i; + char *lc_mask = (char*)&sys_info.lc_mask; + + for (i = 0; i < len; i++) { + if (!lc_mask[i + sa_off]) + continue; + if (copy_to_user(buf + i, sa + sa_off + i, 1)) + return -EFAULT; + } + return 0; +} + +/* + * Copy lowcores info to memory, if necessary + * + * @buf: User buffer + * @addr: Start address of buffer in dump memory + * @count: Size of buffer + */ +static int zcore_add_lc(char __user *buf, unsigned long start, size_t count) +{ + unsigned long end; + int i = 0; + + if (count == 0) + return 0; + + end = start + count; + while (zfcpdump_save_areas[i]) { + unsigned long cp_start, cp_end; /* copy range */ + unsigned long sa_start, sa_end; /* save area range */ + unsigned long prefix; + unsigned long sa_off, len, buf_off; + + if (sys_info.arch == ARCH_S390) + prefix = zfcpdump_save_areas[i]->s390.pref_reg; + else + prefix = zfcpdump_save_areas[i]->s390x.pref_reg; + + sa_start = prefix + sys_info.sa_base; + sa_end = prefix + sys_info.sa_base + sys_info.sa_size; + + if ((end < sa_start) || (start > sa_end)) + goto next; + cp_start = max(start, sa_start); + cp_end = min(end, sa_end); + + buf_off = cp_start - start; + sa_off = cp_start - sa_start; + len = cp_end - cp_start; + + TRACE("copy_lc for: %lx\n", start); + if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len)) + return -EFAULT; +next: + i++; + } + return 0; +} + +/* + * Read routine for zcore character device + * First 4K are dump header + * Next 32MB are HSA Memory + * Rest is read from absolute Memory + */ +static ssize_t zcore_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + unsigned long mem_start; /* Start address in memory */ + size_t mem_offs; /* Offset in dump memory */ + size_t hdr_count; /* Size of header part of output buffer */ + size_t size; + int rc; + + mutex_lock(&zcore_mutex); + + if (*ppos > (sys_info.mem_size + HEADER_SIZE)) { + rc = -EINVAL; + goto fail; + } + + count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos)); + + /* Copy dump header */ + if (*ppos < HEADER_SIZE) { + size = min(count, (size_t) (HEADER_SIZE - *ppos)); + if (copy_to_user(buf, &zcore_header + *ppos, size)) { + rc = -EFAULT; + goto fail; + } + hdr_count = size; + mem_start = 0; + } else { + hdr_count = 0; + mem_start = *ppos - HEADER_SIZE; + } + + mem_offs = 0; + + /* Copy from HSA data */ + if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) { + size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE + - mem_start)); + rc = memcpy_hsa_user(buf + hdr_count, mem_start, size); + if (rc) + goto fail; + + mem_offs += size; + } + + /* Copy from real mem */ + size = count - mem_offs - hdr_count; + rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs, + size); + if (rc) + goto fail; + + /* + * Since s390 dump analysis tools like lcrash or crash + * expect register sets in the prefix pages of the cpus, + * we copy them into the read buffer, if necessary. + * buf + hdr_count: Start of memory part of output buffer + * mem_start: Start memory address to copy from + * count - hdr_count: Size of memory area to copy + */ + if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) { + rc = -EFAULT; + goto fail; + } + *ppos += count; +fail: + mutex_unlock(&zcore_mutex); + return (rc < 0) ? rc : count; +} + +static int zcore_open(struct inode *inode, struct file *filp) +{ + if (!hsa_available) + return -ENODATA; + else + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + +static int zcore_release(struct inode *inode, struct file *filep) +{ + diag308(DIAG308_REL_HSA, NULL); + hsa_available = 0; + return 0; +} + +static loff_t zcore_lseek(struct file *file, loff_t offset, int orig) +{ + loff_t rc; + + mutex_lock(&zcore_mutex); + switch (orig) { + case 0: + file->f_pos = offset; + rc = file->f_pos; + break; + case 1: + file->f_pos += offset; + rc = file->f_pos; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&zcore_mutex); + return rc; +} + +static struct file_operations zcore_fops = { + .owner = THIS_MODULE, + .llseek = zcore_lseek, + .read = zcore_read, + .open = zcore_open, + .release = zcore_release, +}; + + +static void __init set_s390_lc_mask(union save_area *map) +{ + memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save)); + memset(&map->s390.timer, 0xff, sizeof(map->s390.timer)); + memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp)); + memset(&map->s390.psw, 0xff, sizeof(map->s390.psw)); + memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg)); + memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs)); + memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs)); + memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs)); + memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs)); +} + +static void __init set_s390x_lc_mask(union save_area *map) +{ + memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs)); + memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs)); + memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw)); + memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg)); + memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg)); + memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg)); + memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer)); + memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp)); + memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs)); + memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs)); +} + +/* + * Initialize dump globals for a given architecture + */ +static int __init sys_info_init(enum arch_id arch) +{ + switch (arch) { + case ARCH_S390X: + MSG("DETECTED 'S390X (64 bit) OS'\n"); + sys_info.sa_base = SAVE_AREA_BASE_S390X; + sys_info.sa_size = sizeof(struct save_area_s390x); + set_s390x_lc_mask(&sys_info.lc_mask); + break; + case ARCH_S390: + MSG("DETECTED 'S390 (32 bit) OS'\n"); + sys_info.sa_base = SAVE_AREA_BASE_S390; + sys_info.sa_size = sizeof(struct save_area_s390); + set_s390_lc_mask(&sys_info.lc_mask); + break; + default: + ERROR_MSG("unknown architecture 0x%x.\n",arch); + return -EINVAL; + } + sys_info.arch = arch; + if (init_cpu_info(arch)) { + ERROR_MSG("get cpu info failed\n"); + return -ENOMEM; + } + sys_info.mem_size = real_memory_size; + + return 0; +} + +static int __init check_sdias(void) +{ + int rc, act_hsa_size; + + rc = sclp_sdias_blk_count(); + if (rc < 0) { + ERROR_MSG("Could not determine HSA size\n"); + return rc; + } + act_hsa_size = (rc - 1) * PAGE_SIZE; + if (act_hsa_size < ZFCPDUMP_HSA_SIZE) { + ERROR_MSG("HSA size too small: %i\n", act_hsa_size); + return -EINVAL; + } + return 0; +} + +static void __init zcore_header_init(int arch, struct zcore_header *hdr) +{ + if (arch == ARCH_S390X) + hdr->arch_id = DUMP_ARCH_S390X; + else + hdr->arch_id = DUMP_ARCH_S390; + hdr->mem_size = sys_info.mem_size; + hdr->mem_end = sys_info.mem_size; + hdr->num_pages = sys_info.mem_size / PAGE_SIZE; + hdr->tod = get_clock(); + get_cpu_id(&hdr->cpu_id); +} + +extern int sdias_init(void); + +static int __init zcore_init(void) +{ + unsigned char arch; + int rc; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return -ENODATA; + + zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long)); + debug_register_view(zcore_dbf, &debug_sprintf_view); + debug_set_level(zcore_dbf, 6); + + TRACE("devno: %x\n", ipl_info.data.fcp.dev_id.devno); + TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn); + TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun); + + rc = sdias_init(); + if (rc) + goto fail; + + rc = check_sdias(); + if (rc) { + ERROR_MSG("Dump initialization failed\n"); + goto fail; + } + + rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1); + if (rc) { + ERROR_MSG("sdial memcpy for arch id failed\n"); + goto fail; + } + +#ifndef __s390x__ + if (arch == ARCH_S390X) { + ERROR_MSG("32 bit dumper can't dump 64 bit system!\n"); + rc = -EINVAL; + goto fail; + } +#endif + + rc = sys_info_init(arch); + if (rc) { + ERROR_MSG("arch init failed\n"); + goto fail; + } + + zcore_header_init(arch, &zcore_header); + + zcore_dir = debugfs_create_dir("zcore" , NULL); + if (!zcore_dir) { + rc = -ENOMEM; + goto fail; + } + zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL, + &zcore_fops); + if (!zcore_file) { + debugfs_remove(zcore_dir); + rc = -ENOMEM; + goto fail; + } + hsa_available = 1; + return 0; + +fail: + diag308(DIAG308_REL_HSA, NULL); + return rc; +} + +extern void sdias_exit(void); + +static void __exit zcore_exit(void) +{ + debug_unregister(zcore_dbf); + sdias_exit(); + diag308(DIAG308_REL_HSA, NULL); +} + +MODULE_AUTHOR("Copyright IBM Corp. 2003,2007"); +MODULE_DESCRIPTION("zcore module for zfcpdump support"); +MODULE_LICENSE("GPL"); + +subsys_initcall(zcore_init); +module_exit(zcore_exit); diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index c490c2a..cfaf77b 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -2,7 +2,7 @@ # # Makefile for the S/390 common i/o drivers # -obj-y += airq.o blacklist.o chsc.o cio.o css.o +obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 5aeb68e..e5ccda6 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -75,8 +75,10 @@ static void ccwgroup_ungroup_callback(st { struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + mutex_lock(&gdev->reg_mutex); __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + mutex_unlock(&gdev->reg_mutex); } static ssize_t @@ -173,7 +175,8 @@ ccwgroup_create(struct device *root, return -ENOMEM; atomic_set(&gdev->onoff, 0); - + mutex_init(&gdev->reg_mutex); + mutex_lock(&gdev->reg_mutex); for (i = 0; i < argc; i++) { gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); @@ -183,12 +186,12 @@ ccwgroup_create(struct device *root, || gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { rc = -EINVAL; - goto free_dev; + goto error; } /* Don't allow a device to belong to more than one group. */ if (gdev->cdev[i]->dev.driver_data) { rc = -EINVAL; - goto free_dev; + goto error; } gdev->cdev[i]->dev.driver_data = gdev; } @@ -203,9 +206,8 @@ ccwgroup_create(struct device *root, gdev->cdev[0]->dev.bus_id); rc = device_register(&gdev->dev); - if (rc) - goto free_dev; + goto error; get_device(&gdev->dev); rc = device_create_file(&gdev->dev, &dev_attr_ungroup); @@ -216,6 +218,7 @@ ccwgroup_create(struct device *root, rc = __ccwgroup_create_symlinks(gdev); if (!rc) { + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); return 0; } @@ -224,19 +227,12 @@ ccwgroup_create(struct device *root, error: for (i = 0; i < argc; i++) if (gdev->cdev[i]) { - put_device(&gdev->cdev[i]->dev); - gdev->cdev[i]->dev.driver_data = NULL; - } - put_device(&gdev->dev); - return rc; -free_dev: - for (i = 0; i < argc; i++) - if (gdev->cdev[i]) { if (gdev->cdev[i]->dev.driver_data == gdev) gdev->cdev[i]->dev.driver_data = NULL; put_device(&gdev->cdev[i]->dev); } - kfree(gdev); + mutex_unlock(&gdev->reg_mutex); + put_device(&gdev->dev); return rc; } @@ -422,8 +418,12 @@ ccwgroup_driver_unregister (struct ccwgr get_driver(&cdriver->driver); while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, __ccwgroup_match_all))) { - __ccwgroup_remove_symlinks(to_ccwgroupdev(dev)); + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + + mutex_lock(&gdev->reg_mutex); + __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + mutex_unlock(&gdev->reg_mutex); put_device(dev); } put_driver(&cdriver->driver); @@ -444,8 +444,10 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_d if (cdev->dev.driver_data) { gdev = (struct ccwgroup_device *)cdev->dev.driver_data; if (get_device(&gdev->dev)) { + mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) return gdev; + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); } return NULL; @@ -465,6 +467,7 @@ ccwgroup_remove_ccwdev(struct ccw_device if (gdev) { __ccwgroup_remove_symlinks(gdev); device_unregister(&gdev->dev); + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); } } diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c new file mode 100644 index 0000000..ac289e6 --- /dev/null +++ b/drivers/s390/cio/chp.c @@ -0,0 +1,683 @@ +/* + * drivers/s390/cio/chp.c + * + * Copyright IBM Corp. 1999,2007 + * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) + * Arnd Bergmann (arndb@de.ibm.com) + * Peter Oberparleiter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cio.h" +#include "css.h" +#include "ioasm.h" +#include "cio_debug.h" +#include "chp.h" + +#define to_channelpath(device) container_of(device, struct channel_path, dev) +#define CHP_INFO_UPDATE_INTERVAL 1*HZ + +enum cfg_task_t { + cfg_none, + cfg_configure, + cfg_deconfigure +}; + +/* Map for pending configure tasks. */ +static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1]; +static DEFINE_MUTEX(cfg_lock); +static int cfg_busy; + +/* Map for channel-path status. */ +static struct sclp_chp_info chp_info; +static DEFINE_MUTEX(info_lock); + +/* Time after which channel-path status may be outdated. */ +static unsigned long chp_info_expires; + +/* Workqueue to perform pending configure tasks. */ +static struct workqueue_struct *chp_wq; +static struct work_struct cfg_work; + +/* Wait queue for configure completion events. */ +static wait_queue_head_t cfg_wait_queue; + +/* Return channel_path struct for given chpid. */ +static inline struct channel_path *chpid_to_chp(struct chp_id chpid) +{ + return css[chpid.cssid]->chps[chpid.id]; +} + +/* Set vary state for given chpid. */ +static void set_chp_logically_online(struct chp_id chpid, int onoff) +{ + chpid_to_chp(chpid)->state = onoff; +} + +/* On succes return 0 if channel-path is varied offline, 1 if it is varied + * online. Return -ENODEV if channel-path is not registered. */ +int chp_get_status(struct chp_id chpid) +{ + return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV); +} + +/** + * chp_get_sch_opm - return opm for subchannel + * @sch: subchannel + * + * Calculate and return the operational path mask (opm) based on the chpids + * used by the subchannel and the status of the associated channel-paths. + */ +u8 chp_get_sch_opm(struct subchannel *sch) +{ + struct chp_id chpid; + int opm; + int i; + + opm = 0; + chp_id_init(&chpid); + for (i=0; i < 8; i++) { + opm <<= 1; + chpid.id = sch->schib.pmcw.chpid[i]; + if (chp_get_status(chpid) != 0) + opm |= 1; + } + return opm; +} + +/** + * chp_is_registered - check if a channel-path is registered + * @chpid: channel-path ID + * + * Return non-zero if a channel-path with the given chpid is registered, + * zero otherwise. + */ +int chp_is_registered(struct chp_id chpid) +{ + return chpid_to_chp(chpid) != NULL; +} + +/* + * Function: s390_vary_chpid + * Varies the specified chpid online or offline + */ +static int s390_vary_chpid(struct chp_id chpid, int on) +{ + char dbf_text[15]; + int status; + + sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, + chpid.id); + CIO_TRACE_EVENT( 2, dbf_text); + + status = chp_get_status(chpid); + if (status < 0) { + printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n", + chpid.cssid, chpid.id); + return -EINVAL; + } + + if (!on && !status) { + printk(KERN_ERR "chpid %x.%02x is already offline\n", + chpid.cssid, chpid.id); + return -EINVAL; + } + + set_chp_logically_online(chpid, on); + chsc_chp_vary(chpid, on); + return 0; +} + +/* + * Channel measurement related functions + */ +static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct channel_path *chp; + unsigned int size; + + chp = to_channelpath(container_of(kobj, struct device, kobj)); + if (!chp->cmg_chars) + return 0; + + size = sizeof(struct cmg_chars); + + if (off > size) + return 0; + if (off + count > size) + count = size - off; + memcpy(buf, chp->cmg_chars + off, count); + return count; +} + +static struct bin_attribute chp_measurement_chars_attr = { + .attr = { + .name = "measurement_chars", + .mode = S_IRUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(struct cmg_chars), + .read = chp_measurement_chars_read, +}; + +static void chp_measurement_copy_block(struct cmg_entry *buf, + struct channel_subsystem *css, + struct chp_id chpid) +{ + void *area; + struct cmg_entry *entry, reference_buf; + int idx; + + if (chpid.id < 128) { + area = css->cub_addr1; + idx = chpid.id; + } else { + area = css->cub_addr2; + idx = chpid.id - 128; + } + entry = area + (idx * sizeof(struct cmg_entry)); + do { + memcpy(buf, entry, sizeof(*entry)); + memcpy(&reference_buf, entry, sizeof(*entry)); + } while (reference_buf.values[0] != buf->values[0]); +} + +static ssize_t chp_measurement_read(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct channel_path *chp; + struct channel_subsystem *css; + unsigned int size; + + chp = to_channelpath(container_of(kobj, struct device, kobj)); + css = to_css(chp->dev.parent); + + size = sizeof(struct cmg_entry); + + /* Only allow single reads. */ + if (off || count < size) + return 0; + chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid); + count = size; + return count; +} + +static struct bin_attribute chp_measurement_attr = { + .attr = { + .name = "measurement", + .mode = S_IRUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(struct cmg_entry), + .read = chp_measurement_read, +}; + +void chp_remove_cmg_attr(struct channel_path *chp) +{ + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); + device_remove_bin_file(&chp->dev, &chp_measurement_attr); +} + +int chp_add_cmg_attr(struct channel_path *chp) +{ + int ret; + + ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); + if (ret) + return ret; + ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); + if (ret) + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); + return ret; +} + +/* + * Files for the channel path entries. + */ +static ssize_t chp_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = container_of(dev, struct channel_path, dev); + + if (!chp) + return 0; + return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") : + sprintf(buf, "offline\n")); +} + +static ssize_t chp_status_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct channel_path *cp = container_of(dev, struct channel_path, dev); + char cmd[10]; + int num_args; + int error; + + num_args = sscanf(buf, "%5s", cmd); + if (!num_args) + return count; + + if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) + error = s390_vary_chpid(cp->chpid, 1); + else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) + error = s390_vary_chpid(cp->chpid, 0); + else + error = -EINVAL; + + return error < 0 ? error : count; + +} + +static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); + +static ssize_t chp_configure_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *cp; + int status; + + cp = container_of(dev, struct channel_path, dev); + status = chp_info_get_status(cp->chpid); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} + +static int cfg_wait_idle(void); + +static ssize_t chp_configure_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct channel_path *cp; + int val; + char delim; + + if (sscanf(buf, "%d %c", &val, &delim) != 1) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + cp = container_of(dev, struct channel_path, dev); + chp_cfg_schedule(cp->chpid, val); + cfg_wait_idle(); + + return count; +} + +static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write); + +static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct channel_path *chp = container_of(dev, struct channel_path, dev); + + if (!chp) + return 0; + return sprintf(buf, "%x\n", chp->desc.desc); +} + +static DEVICE_ATTR(type, 0444, chp_type_show, NULL); + +static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->cmg == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->cmg); +} + +static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); + +static ssize_t chp_shared_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->shared == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->shared); +} + +static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); + +static struct attribute * chp_attrs[] = { + &dev_attr_status.attr, + &dev_attr_configure.attr, + &dev_attr_type.attr, + &dev_attr_cmg.attr, + &dev_attr_shared.attr, + NULL, +}; + +static struct attribute_group chp_attr_group = { + .attrs = chp_attrs, +}; + +static void chp_release(struct device *dev) +{ + struct channel_path *cp; + + cp = container_of(dev, struct channel_path, dev); + kfree(cp); +} + +/** + * chp_new - register a new channel-path + * @chpid - channel-path ID + * + * Create and register data structure representing new channel-path. Return + * zero on success, non-zero otherwise. + */ +int chp_new(struct chp_id chpid) +{ + struct channel_path *chp; + int ret; + + if (chp_is_registered(chpid)) + return 0; + chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); + if (!chp) + return -ENOMEM; + + /* fill in status, etc. */ + chp->chpid = chpid; + chp->state = 1; + chp->dev.parent = &css[chpid.cssid]->device; + chp->dev.release = chp_release; + snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid, + chpid.id); + + /* Obtain channel path description and fill it in. */ + ret = chsc_determine_channel_path_description(chpid, &chp->desc); + if (ret) + goto out_free; + if ((chp->desc.flags & 0x80) == 0) { + ret = -ENODEV; + goto out_free; + } + /* Get channel-measurement characteristics. */ + if (css_characteristics_avail && css_chsc_characteristics.scmc + && css_chsc_characteristics.secm) { + ret = chsc_get_channel_measurement_chars(chp); + if (ret) + goto out_free; + } else { + static int msg_done; + + if (!msg_done) { + printk(KERN_WARNING "cio: Channel measurements not " + "available, continuing.\n"); + msg_done = 1; + } + chp->cmg = -1; + } + + /* make it known to the system */ + ret = device_register(&chp->dev); + if (ret) { + printk(KERN_WARNING "%s: could not register %x.%02x\n", + __func__, chpid.cssid, chpid.id); + goto out_free; + } + ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); + if (ret) { + device_unregister(&chp->dev); + goto out_free; + } + mutex_lock(&css[chpid.cssid]->mutex); + if (css[chpid.cssid]->cm_enabled) { + ret = chp_add_cmg_attr(chp); + if (ret) { + sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); + device_unregister(&chp->dev); + mutex_unlock(&css[chpid.cssid]->mutex); + goto out_free; + } + } + css[chpid.cssid]->chps[chpid.id] = chp; + mutex_unlock(&css[chpid.cssid]->mutex); + return ret; +out_free: + kfree(chp); + return ret; +} + +/** + * chp_get_chp_desc - return newly allocated channel-path description + * @chpid: channel-path ID + * + * On success return a newly allocated copy of the channel-path description + * data associated with the given channel-path ID. Return %NULL on error. + */ +void *chp_get_chp_desc(struct chp_id chpid) +{ + struct channel_path *chp; + struct channel_path_desc *desc; + + chp = chpid_to_chp(chpid); + if (!chp) + return NULL; + desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); + if (!desc) + return NULL; + memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); + return desc; +} + +/** + * chp_process_crw - process channel-path status change + * @id: channel-path ID number + * @status: non-zero if channel-path has become available, zero otherwise + * + * Handle channel-report-words indicating that the status of a channel-path + * has changed. + */ +void chp_process_crw(int id, int status) +{ + struct chp_id chpid; + + chp_id_init(&chpid); + chpid.id = id; + if (status) { + if (!chp_is_registered(chpid)) + chp_new(chpid); + chsc_chp_online(chpid); + } else + chsc_chp_offline(chpid); +} + +static inline int info_bit_num(struct chp_id id) +{ + return id.id + id.cssid * (__MAX_CHPID + 1); +} + +/* Force chp_info refresh on next call to info_validate(). */ +static void info_expire(void) +{ + mutex_lock(&info_lock); + chp_info_expires = jiffies - 1; + mutex_unlock(&info_lock); +} + +/* Ensure that chp_info is up-to-date. */ +static int info_update(void) +{ + int rc; + + mutex_lock(&info_lock); + rc = 0; + if (time_after(jiffies, chp_info_expires)) { + /* Data is too old, update. */ + rc = sclp_chp_read_info(&chp_info); + chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ; + } + mutex_unlock(&info_lock); + + return rc; +} + +/** + * chp_info_get_status - retrieve configure status of a channel-path + * @chpid: channel-path ID + * + * On success, return 0 for standby, 1 for configured, 2 for reserved, + * 3 for not recognized. Return negative error code on error. + */ +int chp_info_get_status(struct chp_id chpid) +{ + int rc; + int bit; + + rc = info_update(); + if (rc) + return rc; + + bit = info_bit_num(chpid); + mutex_lock(&info_lock); + if (!chp_test_bit(chp_info.recognized, bit)) + rc = CHP_STATUS_NOT_RECOGNIZED; + else if (chp_test_bit(chp_info.configured, bit)) + rc = CHP_STATUS_CONFIGURED; + else if (chp_test_bit(chp_info.standby, bit)) + rc = CHP_STATUS_STANDBY; + else + rc = CHP_STATUS_RESERVED; + mutex_unlock(&info_lock); + + return rc; +} + +/* Return configure task for chpid. */ +static enum cfg_task_t cfg_get_task(struct chp_id chpid) +{ + return chp_cfg_task[chpid.cssid][chpid.id]; +} + +/* Set configure task for chpid. */ +static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg) +{ + chp_cfg_task[chpid.cssid][chpid.id] = cfg; +} + +/* Perform one configure/deconfigure request. Reschedule work function until + * last request. */ +static void cfg_func(struct work_struct *work) +{ + struct chp_id chpid; + enum cfg_task_t t; + + mutex_lock(&cfg_lock); + t = cfg_none; + chp_id_for_each(&chpid) { + t = cfg_get_task(chpid); + if (t != cfg_none) { + cfg_set_task(chpid, cfg_none); + break; + } + } + mutex_unlock(&cfg_lock); + + switch (t) { + case cfg_configure: + sclp_chp_configure(chpid); + info_expire(); + chsc_chp_online(chpid); + break; + case cfg_deconfigure: + sclp_chp_deconfigure(chpid); + info_expire(); + chsc_chp_offline(chpid); + break; + case cfg_none: + /* Get updated information after last change. */ + info_update(); + mutex_lock(&cfg_lock); + cfg_busy = 0; + mutex_unlock(&cfg_lock); + wake_up_interruptible(&cfg_wait_queue); + return; + } + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_schedule - schedule chpid configuration request + * @chpid - channel-path ID + * @configure - Non-zero for configure, zero for deconfigure + * + * Schedule a channel-path configuration/deconfiguration request. + */ +void chp_cfg_schedule(struct chp_id chpid, int configure) +{ + CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id, + configure); + mutex_lock(&cfg_lock); + cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); + cfg_busy = 1; + mutex_unlock(&cfg_lock); + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request + * @chpid - channel-path ID + * + * Cancel an active channel-path deconfiguration request if it has not yet + * been performed. + */ +void chp_cfg_cancel_deconfigure(struct chp_id chpid) +{ + CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id); + mutex_lock(&cfg_lock); + if (cfg_get_task(chpid) == cfg_deconfigure) + cfg_set_task(chpid, cfg_none); + mutex_unlock(&cfg_lock); +} + +static int cfg_wait_idle(void) +{ + if (wait_event_interruptible(cfg_wait_queue, !cfg_busy)) + return -ERESTARTSYS; + return 0; +} + +static int __init chp_init(void) +{ + struct chp_id chpid; + + chp_wq = create_singlethread_workqueue("cio_chp"); + if (!chp_wq) + return -ENOMEM; + INIT_WORK(&cfg_work, cfg_func); + init_waitqueue_head(&cfg_wait_queue); + if (info_update()) + return 0; + /* Register available channel-paths. */ + chp_id_for_each(&chpid) { + if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED) + chp_new(chpid); + } + + return 0; +} + +subsys_initcall(chp_init); diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h new file mode 100644 index 0000000..6528656 --- /dev/null +++ b/drivers/s390/cio/chp.h @@ -0,0 +1,53 @@ +/* + * drivers/s390/cio/chp.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef S390_CHP_H +#define S390_CHP_H S390_CHP_H + +#include +#include +#include +#include "chsc.h" + +#define CHP_STATUS_STANDBY 0 +#define CHP_STATUS_CONFIGURED 1 +#define CHP_STATUS_RESERVED 2 +#define CHP_STATUS_NOT_RECOGNIZED 3 + +static inline int chp_test_bit(u8 *bitmap, int num) +{ + int byte = num >> 3; + int mask = 128 >> (num & 7); + + return (bitmap[byte] & mask) ? 1 : 0; +} + + +struct channel_path { + struct chp_id chpid; + int state; + struct channel_path_desc desc; + /* Channel-measurement related stuff: */ + int cmg; + int shared; + void *cmg_chars; + struct device dev; +}; + +int chp_get_status(struct chp_id chpid); +u8 chp_get_sch_opm(struct subchannel *sch); +int chp_is_registered(struct chp_id chpid); +void *chp_get_chp_desc(struct chp_id chpid); +void chp_process_crw(int id, int available); +void chp_remove_cmg_attr(struct channel_path *chp); +int chp_add_cmg_attr(struct channel_path *chp); +int chp_new(struct chp_id chpid); +void chp_cfg_schedule(struct chp_id chpid, int configure); +void chp_cfg_cancel_deconfigure(struct chp_id chpid); +int chp_info_get_status(struct chp_id chpid); + +#endif /* S390_CHP_H */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 6f05a44..ea92ac4 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -15,202 +15,124 @@ #include #include #include +#include #include "css.h" #include "cio.h" #include "cio_debug.h" #include "ioasm.h" +#include "chp.h" #include "chsc.h" static void *sei_page; -static int new_channel_path(int chpid); - -static inline void -set_chp_logically_online(int chp, int onoff) -{ - css[0]->chps[chp]->state = onoff; -} - -static int -get_chp_status(int chp) -{ - return (css[0]->chps[chp] ? css[0]->chps[chp]->state : -ENODEV); -} - -void -chsc_validate_chpids(struct subchannel *sch) -{ - int mask, chp; - - for (chp = 0; chp <= 7; chp++) { - mask = 0x80 >> chp; - if (!get_chp_status(sch->schib.pmcw.chpid[chp])) - /* disable using this path */ - sch->opm &= ~mask; - } -} - -void -chpid_is_actually_online(int chp) -{ - int state; - - state = get_chp_status(chp); - if (state < 0) { - need_rescan = 1; - queue_work(slow_path_wq, &slow_path_work); - } else - WARN_ON(!state); -} +struct chsc_ssd_area { + struct chsc_header request; + u16 :10; + u16 ssid:2; + u16 :4; + u16 f_sch; /* first subchannel */ + u16 :16; + u16 l_sch; /* last subchannel */ + u32 :32; + struct chsc_header response; + u32 :32; + u8 sch_valid : 1; + u8 dev_valid : 1; + u8 st : 3; /* subchannel type */ + u8 zeroes : 3; + u8 unit_addr; /* unit address */ + u16 devno; /* device number */ + u8 path_mask; + u8 fla_valid_mask; + u16 sch; /* subchannel */ + u8 chpid[8]; /* chpids 0-7 */ + u16 fla[8]; /* full link addresses 0-7 */ +} __attribute__ ((packed)); -/* FIXME: this is _always_ called for every subchannel. shouldn't we - * process more than one at a time? */ -static int -chsc_get_sch_desc_irq(struct subchannel *sch, void *page) +int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) { - int ccode, j; - - struct { - struct chsc_header request; - u16 reserved1a:10; - u16 ssid:2; - u16 reserved1b:4; - u16 f_sch; /* first subchannel */ - u16 reserved2; - u16 l_sch; /* last subchannel */ - u32 reserved3; - struct chsc_header response; - u32 reserved4; - u8 sch_valid : 1; - u8 dev_valid : 1; - u8 st : 3; /* subchannel type */ - u8 zeroes : 3; - u8 unit_addr; /* unit address */ - u16 devno; /* device number */ - u8 path_mask; - u8 fla_valid_mask; - u16 sch; /* subchannel */ - u8 chpid[8]; /* chpids 0-7 */ - u16 fla[8]; /* full link addresses 0-7 */ - } __attribute__ ((packed)) *ssd_area; - - ssd_area = page; + unsigned long page; + struct chsc_ssd_area *ssd_area; + int ccode; + int ret; + int i; + int mask; + page = get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!page) + return -ENOMEM; + ssd_area = (struct chsc_ssd_area *) page; ssd_area->request.length = 0x0010; ssd_area->request.code = 0x0004; - - ssd_area->ssid = sch->schid.ssid; - ssd_area->f_sch = sch->schid.sch_no; - ssd_area->l_sch = sch->schid.sch_no; + ssd_area->ssid = schid.ssid; + ssd_area->f_sch = schid.sch_no; + ssd_area->l_sch = schid.sch_no; ccode = chsc(ssd_area); + /* Check response. */ if (ccode > 0) { - pr_debug("chsc returned with ccode = %d\n", ccode); - return (ccode == 3) ? -ENODEV : -EBUSY; + ret = (ccode == 3) ? -ENODEV : -EBUSY; + goto out_free; } - - switch (ssd_area->response.code) { - case 0x0001: /* everything ok */ - break; - case 0x0002: - CIO_CRW_EVENT(2, "Invalid command!\n"); - return -EINVAL; - case 0x0003: - CIO_CRW_EVENT(2, "Error in chsc request block!\n"); - return -EINVAL; - case 0x0004: - CIO_CRW_EVENT(2, "Model does not provide ssd\n"); - return -EOPNOTSUPP; - default: - CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", + if (ssd_area->response.code != 0x0001) { + CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", + schid.ssid, schid.sch_no, ssd_area->response.code); - return -EIO; + ret = -EIO; + goto out_free; } - - /* - * ssd_area->st stores the type of the detected - * subchannel, with the following definitions: - * - * 0: I/O subchannel: All fields have meaning - * 1: CHSC subchannel: Only sch_val, st and sch - * have meaning - * 2: Message subchannel: All fields except unit_addr - * have meaning - * 3: ADM subchannel: Only sch_val, st and sch - * have meaning - * - * Other types are currently undefined. - */ - if (ssd_area->st > 3) { /* uhm, that looks strange... */ - CIO_CRW_EVENT(0, "Strange subchannel type %d" - " for sch 0.%x.%04x\n", ssd_area->st, - sch->schid.ssid, sch->schid.sch_no); - /* - * There may have been a new subchannel type defined in the - * time since this code was written; since we don't know which - * fields have meaning and what to do with it we just jump out - */ - return 0; - } else { - const char *type[4] = {"I/O", "chsc", "message", "ADM"}; - CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n", - sch->schid.ssid, sch->schid.sch_no, - type[ssd_area->st]); - - sch->ssd_info.valid = 1; - sch->ssd_info.type = ssd_area->st; + if (!ssd_area->sch_valid) { + ret = -ENODEV; + goto out_free; } - - if (ssd_area->st == 0 || ssd_area->st == 2) { - for (j = 0; j < 8; j++) { - if (!((0x80 >> j) & ssd_area->path_mask & - ssd_area->fla_valid_mask)) - continue; - sch->ssd_info.chpid[j] = ssd_area->chpid[j]; - sch->ssd_info.fla[j] = ssd_area->fla[j]; + /* Copy data */ + ret = 0; + memset(ssd, 0, sizeof(struct chsc_ssd_info)); + if ((ssd_area->st != 0) && (ssd_area->st != 2)) + goto out_free; + ssd->path_mask = ssd_area->path_mask; + ssd->fla_valid_mask = ssd_area->fla_valid_mask; + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (ssd_area->path_mask & mask) { + chp_id_init(&ssd->chpid[i]); + ssd->chpid[i].id = ssd_area->chpid[i]; } + if (ssd_area->fla_valid_mask & mask) + ssd->fla[i] = ssd_area->fla[i]; } - return 0; +out_free: + free_page(page); + return ret; } -int -css_get_ssd_info(struct subchannel *sch) +static int check_for_io_on_path(struct subchannel *sch, int mask) { - int ret; - void *page; + int cc; - page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!page) - return -ENOMEM; - spin_lock_irq(sch->lock); - ret = chsc_get_sch_desc_irq(sch, page); - if (ret) { - static int cio_chsc_err_msg; - - if (!cio_chsc_err_msg) { - printk(KERN_ERR - "chsc_get_sch_descriptions:" - " Error %d while doing chsc; " - "processing some machine checks may " - "not work\n", ret); - cio_chsc_err_msg = 1; - } - } - spin_unlock_irq(sch->lock); - free_page((unsigned long)page); - if (!ret) { - int j, chpid, mask; - /* Allocate channel path structures, if needed. */ - for (j = 0; j < 8; j++) { - mask = 0x80 >> j; - chpid = sch->ssd_info.chpid[j]; - if ((sch->schib.pmcw.pim & mask) && - (get_chp_status(chpid) < 0)) - new_channel_path(chpid); - } + cc = stsch(sch->schid, &sch->schib); + if (cc) + return 0; + if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask) + return 1; + return 0; +} + +static void terminate_internal_io(struct subchannel *sch) +{ + if (cio_clear(sch)) { + /* Recheck device in case clear failed. */ + sch->lpm = 0; + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); + return; } - return ret; + /* Request retry of internal operation. */ + device_set_intretry(sch); + /* Call handler. */ + if (sch->driver && sch->driver->termination) + sch->driver->termination(&sch->dev); } static int @@ -219,7 +141,7 @@ s390_subchannel_remove_chpid(struct devi int j; int mask; struct subchannel *sch; - struct channel_path *chpid; + struct chp_id *chpid; struct schib schib; sch = to_subchannel(dev); @@ -243,106 +165,50 @@ s390_subchannel_remove_chpid(struct devi if (sch->schib.pmcw.pim == 0x80) goto out_unreg; - if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && - (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && - (sch->schib.pmcw.lpum == mask)) { - int cc; - - cc = cio_clear(sch); - if (cc == -ENODEV) + if (check_for_io_on_path(sch, mask)) { + if (device_is_online(sch)) + device_kill_io(sch); + else { + terminate_internal_io(sch); + /* Re-start path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + } + } else { + /* trigger path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + else if (sch->lpm == mask) goto out_unreg; - /* Request retry of internal operation. */ - device_set_intretry(sch); - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); - goto out_unlock; } - /* trigger path verification. */ - if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); - else if (sch->lpm == mask) - goto out_unreg; -out_unlock: spin_unlock_irq(sch->lock); return 0; + out_unreg: - spin_unlock_irq(sch->lock); sch->lpm = 0; - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } + spin_unlock_irq(sch->lock); + css_schedule_eval(sch->schid); return 0; } -static void -s390_set_chpid_offline( __u8 chpid) +void chsc_chp_offline(struct chp_id chpid) { char dbf_txt[15]; - struct device *dev; - sprintf(dbf_txt, "chpr%x", chpid); + sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - if (get_chp_status(chpid) <= 0) + if (chp_get_status(chpid) <= 0) return; - dev = get_device(&css[0]->chps[chpid]->dev); - bus_for_each_dev(&css_bus_type, NULL, to_channelpath(dev), + bus_for_each_dev(&css_bus_type, NULL, &chpid, s390_subchannel_remove_chpid); - - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); - put_device(dev); -} - -struct res_acc_data { - struct channel_path *chp; - u32 fla_mask; - u16 fla; -}; - -static int -s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch) -{ - int found; - int chp; - int ccode; - - found = 0; - for (chp = 0; chp <= 7; chp++) - /* - * check if chpid is in information updated by ssd - */ - if (sch->ssd_info.valid && - sch->ssd_info.chpid[chp] == res_data->chp->id && - (sch->ssd_info.fla[chp] & res_data->fla_mask) - == res_data->fla) { - found = 1; - break; - } - - if (found == 0) - return 0; - - /* - * Do a stsch to update our subchannel structure with the - * new path information and eventually check for logically - * offline chpids. - */ - ccode = stsch(sch->schid, &sch->schib); - if (ccode > 0) - return 0; - - return 0x80 >> chp; } static int s390_process_res_acc_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; /* * We don't know the device yet, but since a path * may be available now to the device we'll have @@ -353,14 +219,35 @@ s390_process_res_acc_new_sch(struct subc */ if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; + css_schedule_eval(schid); + return 0; +} + +struct res_acc_data { + struct chp_id chpid; + u32 fla_mask; + u16 fla; +}; + +static int get_res_chpid_mask(struct chsc_ssd_info *ssd, + struct res_acc_data *data) +{ + int i; + int mask; + + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (!(ssd->path_mask & mask)) + continue; + if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid)) + continue; + if ((ssd->fla_valid_mask & mask) && + ((ssd->fla[i] & data->fla_mask) != data->fla)) + continue; + return mask; } return 0; } @@ -379,14 +266,11 @@ __s390_process_res_acc(struct subchannel return s390_process_res_acc_new_sch(schid); spin_lock_irq(sch->lock); - - chp_mask = s390_process_res_acc_sch(res_data, sch); - - if (chp_mask == 0) { - spin_unlock_irq(sch->lock); - put_device(&sch->dev); - return 0; - } + chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); + if (chp_mask == 0) + goto out; + if (stsch(sch->schid, &sch->schib)) + goto out; old_lpm = sch->lpm; sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & @@ -396,20 +280,18 @@ __s390_process_res_acc(struct subchannel device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - +out: spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } - -static int -s390_process_res_acc (struct res_acc_data *res_data) +static void s390_process_res_acc (struct res_acc_data *res_data) { - int rc; char dbf_txt[15]; - sprintf(dbf_txt, "accpr%x", res_data->chp->id); + sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, + res_data->chpid.id); CIO_TRACE_EVENT( 2, dbf_txt); if (res_data->fla != 0) { sprintf(dbf_txt, "fla%x", res_data->fla); @@ -423,12 +305,7 @@ s390_process_res_acc (struct res_acc_dat * The more information we have (info), the less scanning * will we have to do. */ - rc = for_each_subchannel(__s390_process_res_acc, res_data); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - else if (rc != -EAGAIN) - rc = 0; - return rc; + for_each_subchannel(__s390_process_res_acc, res_data); } static int @@ -480,43 +357,45 @@ struct chsc_sei_area { /* ccdf has to be big enough for a link-incident record */ } __attribute__ ((packed)); -static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) +static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) { - int chpid; + struct chp_id chpid; + int id; CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; - chpid = __get_chpid_from_lir(sei_area->ccdf); - if (chpid < 0) + return; + id = __get_chpid_from_lir(sei_area->ccdf); + if (id < 0) CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); - else - s390_set_chpid_offline(chpid); - - return 0; + else { + chp_id_init(&chpid); + chpid.id = id; + chsc_chp_offline(chpid); + } } -static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) +static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { struct res_acc_data res_data; - struct device *dev; + struct chp_id chpid; int status; - int rc; CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; + return; + chp_id_init(&chpid); + chpid.id = sei_area->rsid; /* allocate a new channel path structure, if needed */ - status = get_chp_status(sei_area->rsid); + status = chp_get_status(chpid); if (status < 0) - new_channel_path(sei_area->rsid); + chp_new(chpid); else if (!status) - return 0; - dev = get_device(&css[0]->chps[sei_area->rsid]->dev); + return; memset(&res_data, 0, sizeof(struct res_acc_data)); - res_data.chp = to_channelpath(dev); + res_data.chpid = chpid; if ((sei_area->vf & 0xc0) != 0) { res_data.fla = sei_area->fla; if ((sei_area->vf & 0xc0) == 0xc0) @@ -526,51 +405,82 @@ static int chsc_process_sei_res_acc(stru /* link address */ res_data.fla_mask = 0xff00; } - rc = s390_process_res_acc(&res_data); - put_device(dev); - - return rc; + s390_process_res_acc(&res_data); } -static int chsc_process_sei(struct chsc_sei_area *sei_area) +struct chp_config_data { + u8 map[32]; + u8 op; + u8 pc; +}; + +static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) { - int rc; + struct chp_config_data *data; + struct chp_id chpid; + int num; + + CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); + if (sei_area->rs != 0) + return; + data = (struct chp_config_data *) &(sei_area->ccdf); + chp_id_init(&chpid); + for (num = 0; num <= __MAX_CHPID; num++) { + if (!chp_test_bit(data->map, num)) + continue; + chpid.id = num; + printk(KERN_WARNING "cio: processing configure event %d for " + "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id); + switch (data->op) { + case 0: + chp_cfg_schedule(chpid, 1); + break; + case 1: + chp_cfg_schedule(chpid, 0); + break; + case 2: + chp_cfg_cancel_deconfigure(chpid); + break; + } + } +} +static void chsc_process_sei(struct chsc_sei_area *sei_area) +{ /* Check if we might have lost some information. */ - if (sei_area->flags & 0x40) + if (sei_area->flags & 0x40) { CIO_CRW_EVENT(2, "chsc: event overflow\n"); + css_schedule_eval_all(); + } /* which kind of information was stored? */ - rc = 0; switch (sei_area->cc) { case 1: /* link incident*/ - rc = chsc_process_sei_link_incident(sei_area); + chsc_process_sei_link_incident(sei_area); break; case 2: /* i/o resource accessibiliy */ - rc = chsc_process_sei_res_acc(sei_area); + chsc_process_sei_res_acc(sei_area); + break; + case 8: /* channel-path-configuration notification */ + chsc_process_sei_chp_config(sei_area); break; default: /* other stuff */ CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", sei_area->cc); break; } - - return rc; } -int chsc_process_crw(void) +void chsc_process_crw(void) { struct chsc_sei_area *sei_area; - int ret; - int rc; if (!sei_page) - return 0; + return; /* Access to sei_page is serialized through machine check handler * thread, so no need for locking. */ sei_area = sei_page; CIO_TRACE_EVENT( 2, "prcss"); - ret = 0; do { memset(sei_area, 0, sizeof(*sei_area)); sei_area->request.length = 0x0010; @@ -580,37 +490,26 @@ int chsc_process_crw(void) if (sei_area->response.code == 0x0001) { CIO_CRW_EVENT(4, "chsc: sei successful\n"); - rc = chsc_process_sei(sei_area); - if (rc) - ret = rc; + chsc_process_sei(sei_area); } else { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", sei_area->response.code); - ret = 0; break; } } while (sei_area->flags & 0x80); - - return ret; } static int __chp_add_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -619,10 +518,10 @@ static int __chp_add(struct subchannel_id schid, void *data) { int i, mask; - struct channel_path *chp; + struct chp_id *chpid; struct subchannel *sch; - chp = data; + chpid = data; sch = get_subchannel_by_schid(schid); if (!sch) /* Check if the subchannel is now available. */ @@ -631,7 +530,7 @@ __chp_add(struct subchannel_id schid, vo for (i=0; i<8; i++) { mask = 0x80 >> i; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[i] == chp->id)) { + (sch->schib.pmcw.chpid[i] == chpid->id)) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ spin_unlock_irq(sch->lock); @@ -657,122 +556,58 @@ __chp_add(struct subchannel_id schid, vo return 0; } -static int -chp_add(int chpid) +void chsc_chp_online(struct chp_id chpid) { - int rc; char dbf_txt[15]; - struct device *dev; - if (!get_chp_status(chpid)) - return 0; /* no need to do the rest */ - - sprintf(dbf_txt, "cadd%x", chpid); + sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - dev = get_device(&css[0]->chps[chpid]->dev); - rc = for_each_subchannel(__chp_add, to_channelpath(dev)); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - if (rc != -EAGAIN) - rc = 0; - put_device(dev); - return rc; + if (chp_get_status(chpid) != 0) + for_each_subchannel(__chp_add, &chpid); } -/* - * Handling of crw machine checks with channel path source. - */ -int -chp_process_crw(int chpid, int on) -{ - if (on == 0) { - /* Path has gone. We use the link incident routine.*/ - s390_set_chpid_offline(chpid); - return 0; /* De-register is async anyway. */ - } - /* - * Path has come. Allocate a new channel path structure, - * if needed. - */ - if (get_chp_status(chpid) < 0) - new_channel_path(chpid); - /* Avoid the extra overhead in process_rec_acc. */ - return chp_add(chpid); -} - -static int check_for_io_on_path(struct subchannel *sch, int index) -{ - int cc; - - cc = stsch(sch->schid, &sch->schib); - if (cc) - return 0; - if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) - return 1; - return 0; -} - -static void terminate_internal_io(struct subchannel *sch) -{ - if (cio_clear(sch)) { - /* Recheck device in case clear failed. */ - sch->lpm = 0; - if (device_trigger_verify(sch) != 0) { - if(css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } - return; - } - /* Request retry of internal operation. */ - device_set_intretry(sch); - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); -} - -static void -__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) +static void __s390_subchannel_vary_chpid(struct subchannel *sch, + struct chp_id chpid, int on) { int chp, old_lpm; + int mask; unsigned long flags; - if (!sch->ssd_info.valid) - return; - spin_lock_irqsave(sch->lock, flags); old_lpm = sch->lpm; for (chp = 0; chp < 8; chp++) { - if (sch->ssd_info.chpid[chp] != chpid) + mask = 0x80 >> chp; + if (!(sch->ssd_info.path_mask & mask)) + continue; + if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid)) continue; if (on) { - sch->opm |= (0x80 >> chp); - sch->lpm |= (0x80 >> chp); + sch->opm |= mask; + sch->lpm |= mask; if (!old_lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); break; } - sch->opm &= ~(0x80 >> chp); - sch->lpm &= ~(0x80 >> chp); - if (check_for_io_on_path(sch, chp)) { + sch->opm &= ~mask; + sch->lpm &= ~mask; + if (check_for_io_on_path(sch, mask)) { if (device_is_online(sch)) /* Path verification is done after killing. */ device_kill_io(sch); - else + else { /* Kill and retry internal I/O. */ terminate_internal_io(sch); - } else if (!sch->lpm) { - if (device_trigger_verify(sch) != 0) { - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } + /* Re-start path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); } + } else if (!sch->lpm) { + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); } else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); break; @@ -780,11 +615,10 @@ __s390_subchannel_vary_chpid(struct subc spin_unlock_irqrestore(sch->lock, flags); } -static int -s390_subchannel_vary_chpid_off(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_off(struct device *dev, void *data) { struct subchannel *sch; - __u8 *chpid; + struct chp_id *chpid; sch = to_subchannel(dev); chpid = data; @@ -793,11 +627,10 @@ s390_subchannel_vary_chpid_off(struct de return 0; } -static int -s390_subchannel_vary_chpid_on(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) { struct subchannel *sch; - __u8 *chpid; + struct chp_id *chpid; sch = to_subchannel(dev); chpid = data; @@ -821,40 +654,17 @@ __s390_vary_chpid_on(struct subchannel_i /* We're through */ return -ENXIO; /* Put it on the slow path. */ - if (css_enqueue_subchannel_slow(schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } -/* - * Function: s390_vary_chpid - * Varies the specified chpid online or offline +/** + * chsc_chp_vary - propagate channel-path vary operation to subchannels + * @chpid: channl-path ID + * @on: non-zero for vary online, zero for vary offline */ -static int -s390_vary_chpid( __u8 chpid, int on) +int chsc_chp_vary(struct chp_id chpid, int on) { - char dbf_text[15]; - int status; - - sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid); - CIO_TRACE_EVENT( 2, dbf_text); - - status = get_chp_status(chpid); - if (status < 0) { - printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid); - return -EINVAL; - } - - if (!on && !status) { - printk(KERN_ERR "chpid %x is already offline\n", chpid); - return -EINVAL; - } - - set_chp_logically_online(chpid, on); - /* * Redo PathVerification on the devices the chpid connects to */ @@ -865,118 +675,9 @@ s390_vary_chpid( __u8 chpid, int on) if (on) /* Scan for new devices on varied on path. */ for_each_subchannel(__s390_vary_chpid_on, NULL); - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); return 0; } -/* - * Channel measurement related functions - */ -static ssize_t -chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off, - size_t count) -{ - struct channel_path *chp; - unsigned int size; - - chp = to_channelpath(container_of(kobj, struct device, kobj)); - if (!chp->cmg_chars) - return 0; - - size = sizeof(struct cmg_chars); - - if (off > size) - return 0; - if (off + count > size) - count = size - off; - memcpy(buf, chp->cmg_chars + off, count); - return count; -} - -static struct bin_attribute chp_measurement_chars_attr = { - .attr = { - .name = "measurement_chars", - .mode = S_IRUSR, - .owner = THIS_MODULE, - }, - .size = sizeof(struct cmg_chars), - .read = chp_measurement_chars_read, -}; - -static void -chp_measurement_copy_block(struct cmg_entry *buf, - struct channel_subsystem *css, int chpid) -{ - void *area; - struct cmg_entry *entry, reference_buf; - int idx; - - if (chpid < 128) { - area = css->cub_addr1; - idx = chpid; - } else { - area = css->cub_addr2; - idx = chpid - 128; - } - entry = area + (idx * sizeof(struct cmg_entry)); - do { - memcpy(buf, entry, sizeof(*entry)); - memcpy(&reference_buf, entry, sizeof(*entry)); - } while (reference_buf.values[0] != buf->values[0]); -} - -static ssize_t -chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count) -{ - struct channel_path *chp; - struct channel_subsystem *css; - unsigned int size; - - chp = to_channelpath(container_of(kobj, struct device, kobj)); - css = to_css(chp->dev.parent); - - size = sizeof(struct cmg_entry); - - /* Only allow single reads. */ - if (off || count < size) - return 0; - chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); - count = size; - return count; -} - -static struct bin_attribute chp_measurement_attr = { - .attr = { - .name = "measurement", - .mode = S_IRUSR, - .owner = THIS_MODULE, - }, - .size = sizeof(struct cmg_entry), - .read = chp_measurement_read, -}; - -static void -chsc_remove_chp_cmg_attr(struct channel_path *chp) -{ - device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); - device_remove_bin_file(&chp->dev, &chp_measurement_attr); -} - -static int -chsc_add_chp_cmg_attr(struct channel_path *chp) -{ - int ret; - - ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); - if (ret) - return ret; - ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); - if (ret) - device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); - return ret; -} - static void chsc_remove_cmg_attr(struct channel_subsystem *css) { @@ -985,7 +686,7 @@ chsc_remove_cmg_attr(struct channel_subs for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; - chsc_remove_chp_cmg_attr(css->chps[i]); + chp_remove_cmg_attr(css->chps[i]); } } @@ -998,7 +699,7 @@ chsc_add_cmg_attr(struct channel_subsyst for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; - ret = chsc_add_chp_cmg_attr(css->chps[i]); + ret = chp_add_cmg_attr(css->chps[i]); if (ret) goto cleanup; } @@ -1007,12 +708,11 @@ cleanup: for (--i; i >= 0; i--) { if (!css->chps[i]) continue; - chsc_remove_chp_cmg_attr(css->chps[i]); + chp_remove_cmg_attr(css->chps[i]); } return ret; } - static int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) { @@ -1118,7 +818,7 @@ chsc_secm(struct channel_subsystem *css, } else chsc_remove_cmg_attr(css); } - if (enable && !css->cm_enabled) { + if (!css->cm_enabled) { free_page((unsigned long)css->cub_addr1); free_page((unsigned long)css->cub_addr2); } @@ -1127,109 +827,8 @@ chsc_secm(struct channel_subsystem *css, return ret; } -/* - * Files for the channel path entries. - */ -static ssize_t -chp_status_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = container_of(dev, struct channel_path, dev); - - if (!chp) - return 0; - return (get_chp_status(chp->id) ? sprintf(buf, "online\n") : - sprintf(buf, "offline\n")); -} - -static ssize_t -chp_status_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct channel_path *cp = container_of(dev, struct channel_path, dev); - char cmd[10]; - int num_args; - int error; - - num_args = sscanf(buf, "%5s", cmd); - if (!num_args) - return count; - - if (!strnicmp(cmd, "on", 2)) - error = s390_vary_chpid(cp->id, 1); - else if (!strnicmp(cmd, "off", 3)) - error = s390_vary_chpid(cp->id, 0); - else - error = -EINVAL; - - return error < 0 ? error : count; - -} - -static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); - -static ssize_t -chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = container_of(dev, struct channel_path, dev); - - if (!chp) - return 0; - return sprintf(buf, "%x\n", chp->desc.desc); -} - -static DEVICE_ATTR(type, 0444, chp_type_show, NULL); - -static ssize_t -chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = to_channelpath(dev); - - if (!chp) - return 0; - if (chp->cmg == -1) /* channel measurements not available */ - return sprintf(buf, "unknown\n"); - return sprintf(buf, "%x\n", chp->cmg); -} - -static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); - -static ssize_t -chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = to_channelpath(dev); - - if (!chp) - return 0; - if (chp->shared == -1) /* channel measurements not available */ - return sprintf(buf, "unknown\n"); - return sprintf(buf, "%x\n", chp->shared); -} - -static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); - -static struct attribute * chp_attrs[] = { - &dev_attr_status.attr, - &dev_attr_type.attr, - &dev_attr_cmg.attr, - &dev_attr_shared.attr, - NULL, -}; - -static struct attribute_group chp_attr_group = { - .attrs = chp_attrs, -}; - -static void -chp_release(struct device *dev) -{ - struct channel_path *cp; - - cp = container_of(dev, struct channel_path, dev); - kfree(cp); -} - -static int -chsc_determine_channel_path_description(int chpid, - struct channel_path_desc *desc) +int chsc_determine_channel_path_description(struct chp_id chpid, + struct channel_path_desc *desc) { int ccode, ret; @@ -1252,8 +851,8 @@ chsc_determine_channel_path_description( scpd_area->request.length = 0x0010; scpd_area->request.code = 0x0002; - scpd_area->first_chpid = chpid; - scpd_area->last_chpid = chpid; + scpd_area->first_chpid = chpid.id; + scpd_area->last_chpid = chpid.id; ccode = chsc(scpd_area); if (ccode > 0) { @@ -1316,8 +915,7 @@ chsc_initialize_cmg_chars(struct channel } } -static int -chsc_get_channel_measurement_chars(struct channel_path *chp) +int chsc_get_channel_measurement_chars(struct channel_path *chp) { int ccode, ret; @@ -1349,8 +947,8 @@ chsc_get_channel_measurement_chars(struc scmc_area->request.length = 0x0010; scmc_area->request.code = 0x0022; - scmc_area->first_chpid = chp->id; - scmc_area->last_chpid = chp->id; + scmc_area->first_chpid = chp->chpid.id; + scmc_area->last_chpid = chp->chpid.id; ccode = chsc(scmc_area); if (ccode > 0) { @@ -1392,94 +990,6 @@ out: return ret; } -/* - * Entries for chpids on the system bus. - * This replaces /proc/chpids. - */ -static int -new_channel_path(int chpid) -{ - struct channel_path *chp; - int ret; - - chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); - if (!chp) - return -ENOMEM; - - /* fill in status, etc. */ - chp->id = chpid; - chp->state = 1; - chp->dev.parent = &css[0]->device; - chp->dev.release = chp_release; - snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid); - - /* Obtain channel path description and fill it in. */ - ret = chsc_determine_channel_path_description(chpid, &chp->desc); - if (ret) - goto out_free; - /* Get channel-measurement characteristics. */ - if (css_characteristics_avail && css_chsc_characteristics.scmc - && css_chsc_characteristics.secm) { - ret = chsc_get_channel_measurement_chars(chp); - if (ret) - goto out_free; - } else { - static int msg_done; - - if (!msg_done) { - printk(KERN_WARNING "cio: Channel measurements not " - "available, continuing.\n"); - msg_done = 1; - } - chp->cmg = -1; - } - - /* make it known to the system */ - ret = device_register(&chp->dev); - if (ret) { - printk(KERN_WARNING "%s: could not register %02x\n", - __func__, chpid); - goto out_free; - } - ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); - if (ret) { - device_unregister(&chp->dev); - goto out_free; - } - mutex_lock(&css[0]->mutex); - if (css[0]->cm_enabled) { - ret = chsc_add_chp_cmg_attr(chp); - if (ret) { - sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); - device_unregister(&chp->dev); - mutex_unlock(&css[0]->mutex); - goto out_free; - } - } - css[0]->chps[chpid] = chp; - mutex_unlock(&css[0]->mutex); - return ret; -out_free: - kfree(chp); - return ret; -} - -void * -chsc_get_chp_desc(struct subchannel *sch, int chp_no) -{ - struct channel_path *chp; - struct channel_path_desc *desc; - - chp = css[0]->chps[sch->schib.pmcw.chpid[chp_no]]; - if (!chp) - return NULL; - desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); - if (!desc) - return NULL; - memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); - return desc; -} - static int __init chsc_alloc_sei_area(void) { diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 0fb2b02..2ad81d1 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -1,9 +1,10 @@ #ifndef S390_CHSC_H #define S390_CHSC_H -#define CHSC_SEI_ACC_CHPID 1 -#define CHSC_SEI_ACC_LINKADDR 2 -#define CHSC_SEI_ACC_FULLLINKADDR 3 +#include +#include +#include +#include "schid.h" #define CHSC_SDA_OC_MSS 0x2 @@ -33,23 +34,9 @@ struct channel_path_desc { u8 chpp; } __attribute__ ((packed)); -struct channel_path { - int id; - int state; - struct channel_path_desc desc; - /* Channel-measurement related stuff: */ - int cmg; - int shared; - void *cmg_chars; - struct device dev; -}; +struct channel_path; -extern void s390_process_css( void ); -extern void chsc_validate_chpids(struct subchannel *); -extern void chpid_is_actually_online(int); -extern int css_get_ssd_info(struct subchannel *); -extern int chsc_process_crw(void); -extern int chp_process_crw(int, int); +extern void chsc_process_crw(void); struct css_general_char { u64 : 41; @@ -82,15 +69,26 @@ struct css_chsc_char { extern struct css_general_char css_general_characteristics; extern struct css_chsc_char css_chsc_characteristics; +struct chsc_ssd_info { + u8 path_mask; + u8 fla_valid_mask; + struct chp_id chpid[8]; + u16 fla[8]; +}; +extern int chsc_get_ssd_info(struct subchannel_id schid, + struct chsc_ssd_info *ssd); extern int chsc_determine_css_characteristics(void); extern int css_characteristics_avail; -extern void *chsc_get_chp_desc(struct subchannel*, int); - extern int chsc_enable_facility(int); struct channel_subsystem; extern int chsc_secm(struct channel_subsystem *, int); -#define to_channelpath(device) container_of(device, struct channel_path, dev) +int chsc_chp_vary(struct chp_id chpid, int on); +int chsc_determine_channel_path_description(struct chp_id chpid, + struct channel_path_desc *desc); +void chsc_chp_online(struct chp_id chpid); +void chsc_chp_offline(struct chp_id chpid); +int chsc_get_channel_measurement_chars(struct channel_path *chp); #endif diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 9cb129a..ea1defb 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -22,6 +22,7 @@ #include #include #include #include +#include #include "airq.h" #include "cio.h" #include "css.h" @@ -29,6 +30,7 @@ #include "chsc.h" #include "ioasm.h" #include "blacklist.h" #include "cio_debug.h" +#include "chp.h" #include "../s390mach.h" debug_info_t *cio_debug_msg_id; @@ -592,9 +594,10 @@ cio_validate_subchannel (struct subchann err = -ENODEV; goto out; } - sch->opm = 0xff; - if (!cio_is_console(sch->schid)) - chsc_validate_chpids(sch); + if (cio_is_console(sch->schid)) + sch->opm = 0xff; + else + sch->opm = chp_get_sch_opm(sch); sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, @@ -954,6 +957,7 @@ static void css_reset(void) { int i, ret; unsigned long long timeout; + struct chp_id chpid; /* Reset subchannels. */ for_each_subchannel(__shutdown_subchannel_easy, NULL); @@ -963,8 +967,10 @@ static void css_reset(void) __ctl_set_bit(14, 28); /* Temporarily reenable machine checks. */ local_mcck_enable(); + chp_id_init(&chpid); for (i = 0; i <= __MAX_CHPID; i++) { - ret = rchp(i); + chpid.id = i; + ret = rchp(chpid); if ((ret == 0) || (ret == 2)) /* * rchp either succeeded, or another rchp is already @@ -1048,37 +1054,19 @@ void reipl_ccw_dev(struct ccw_dev_id *de do_reipl_asm(*((__u32*)&schid)); } -static struct schib __initdata ipl_schib; - -/* - * ipl_save_parameters gets called very early. It is not allowed to access - * anything in the bss section at all. The bss section is not cleared yet, - * but may contain some ipl parameters written by the firmware. - * These parameters (if present) are copied to 0x2000. - * To avoid corruption of the ipl parameters, all variables used by this - * function must reside on the stack or in the data section. - */ -void ipl_save_parameters(void) +int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) { struct subchannel_id schid; - unsigned int *ipl_ptr; - void *src, *dst; + struct schib schib; schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID; if (!schid.one) - return; - if (stsch(schid, &ipl_schib)) - return; - if (!ipl_schib.pmcw.dnv) - return; - ipl_devno = ipl_schib.pmcw.dev; - ipl_flags |= IPL_DEVNO_VALID; - if (!ipl_schib.pmcw.qf) - return; - ipl_flags |= IPL_PARMBLOCK_VALID; - ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR; - src = (void *)(unsigned long)*ipl_ptr; - dst = (void *)IPL_PARMBLOCK_ORIGIN; - memmove(dst, src, PAGE_SIZE); - *ipl_ptr = IPL_PARMBLOCK_ORIGIN; + return -ENODEV; + if (stsch(schid, &schib)) + return -ENODEV; + if (!schib.pmcw.dnv) + return -ENODEV; + iplinfo->devno = schib.pmcw.dev; + iplinfo->is_qdio = schib.pmcw.qf; + return 0; } diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 35154a2..7446c39 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -1,18 +1,11 @@ #ifndef S390_CIO_H #define S390_CIO_H -#include "schid.h" #include - -/* - * where we put the ssd info - */ -struct ssd_info { - __u8 valid:1; - __u8 type:7; /* subchannel type */ - __u8 chpid[8]; /* chpids */ - __u16 fla[8]; /* full link addresses */ -} __attribute__ ((packed)); +#include +#include +#include "chsc.h" +#include "schid.h" /* * path management control word @@ -108,7 +101,7 @@ struct subchannel { struct schib schib; /* subchannel information block */ struct orb orb; /* operation request block */ struct ccw1 sense_ccw; /* static ccw for sense command */ - struct ssd_info ssd_info; /* subchannel description */ + struct chsc_ssd_info ssd_info; /* subchannel description */ struct device dev; /* entry in device tree */ struct css_driver *driver; } __attribute__ ((aligned(8))); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 90b22fa..28abd69 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -476,7 +476,7 @@ struct cmb_area { }; static struct cmb_area cmb_area = { - .lock = SPIN_LOCK_UNLOCKED, + .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock), .list = LIST_HEAD_INIT(cmb_area.list), .num_channels = 1024, }; diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fe0ace7..27c6d9e 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -20,8 +20,9 @@ #include "cio_debug.h" #include "ioasm.h" #include "chsc.h" #include "device.h" +#include "idset.h" +#include "chp.h" -int need_rescan = 0; int css_init_done = 0; static int need_reprobe = 0; static int max_ssid = 0; @@ -125,8 +126,52 @@ void css_sch_device_unregister(struct su mutex_unlock(&sch->reg_mutex); } -static int -css_register_subchannel(struct subchannel *sch) +static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) +{ + int i; + int mask; + + memset(ssd, 0, sizeof(struct chsc_ssd_info)); + ssd->path_mask = pmcw->pim; + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (pmcw->pim & mask) { + chp_id_init(&ssd->chpid[i]); + ssd->chpid[i].id = pmcw->chpid[i]; + } + } +} + +static void ssd_register_chpids(struct chsc_ssd_info *ssd) +{ + int i; + int mask; + + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (ssd->path_mask & mask) + if (!chp_is_registered(ssd->chpid[i])) + chp_new(ssd->chpid[i]); + } +} + +void css_update_ssd_info(struct subchannel *sch) +{ + int ret; + + if (cio_is_console(sch->schid)) { + /* Console is initialized too early for functions requiring + * memory allocation. */ + ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); + } else { + ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); + if (ret) + ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); + ssd_register_chpids(&sch->ssd_info); + } +} + +static int css_register_subchannel(struct subchannel *sch) { int ret; @@ -135,9 +180,7 @@ css_register_subchannel(struct subchanne sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; sch->dev.groups = subch_attr_groups; - - css_get_ssd_info(sch); - + css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); if (ret) { @@ -306,7 +349,7 @@ static int css_evaluate_new_subchannel(s return css_probe_device(schid); } -static int css_evaluate_subchannel(struct subchannel_id schid, int slow) +static void css_evaluate_subchannel(struct subchannel_id schid, int slow) { struct subchannel *sch; int ret; @@ -317,53 +360,66 @@ static int css_evaluate_subchannel(struc put_device(&sch->dev); } else ret = css_evaluate_new_subchannel(schid, slow); - - return ret; + if (ret == -EAGAIN) + css_schedule_eval(schid); } -static int -css_rescan_devices(struct subchannel_id schid, void *data) +static struct idset *slow_subchannel_set; +static spinlock_t slow_subchannel_lock; + +static int __init slow_subchannel_init(void) { - return css_evaluate_subchannel(schid, 1); + spin_lock_init(&slow_subchannel_lock); + slow_subchannel_set = idset_sch_new(); + if (!slow_subchannel_set) { + printk(KERN_WARNING "cio: could not allocate slow subchannel " + "set\n"); + return -ENOMEM; + } + return 0; } -struct slow_subchannel { - struct list_head slow_list; - struct subchannel_id schid; -}; - -static LIST_HEAD(slow_subchannels_head); -static DEFINE_SPINLOCK(slow_subchannel_lock); +subsys_initcall(slow_subchannel_init); -static void -css_trigger_slow_path(struct work_struct *unused) +static void css_slow_path_func(struct work_struct *unused) { - CIO_TRACE_EVENT(4, "slowpath"); - - if (need_rescan) { - need_rescan = 0; - for_each_subchannel(css_rescan_devices, NULL); - return; - } + struct subchannel_id schid; + CIO_TRACE_EVENT(4, "slowpath"); spin_lock_irq(&slow_subchannel_lock); - while (!list_empty(&slow_subchannels_head)) { - struct slow_subchannel *slow_sch = - list_entry(slow_subchannels_head.next, - struct slow_subchannel, slow_list); - - list_del_init(slow_subchannels_head.next); + init_subchannel_id(&schid); + while (idset_sch_get_first(slow_subchannel_set, &schid)) { + idset_sch_del(slow_subchannel_set, schid); spin_unlock_irq(&slow_subchannel_lock); - css_evaluate_subchannel(slow_sch->schid, 1); + css_evaluate_subchannel(schid, 1); spin_lock_irq(&slow_subchannel_lock); - kfree(slow_sch); } spin_unlock_irq(&slow_subchannel_lock); } -DECLARE_WORK(slow_path_work, css_trigger_slow_path); +static DECLARE_WORK(slow_path_work, css_slow_path_func); struct workqueue_struct *slow_path_wq; +void css_schedule_eval(struct subchannel_id schid) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_sch_add(slow_subchannel_set, schid); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + +void css_schedule_eval_all(void) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_fill(slow_subchannel_set); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + /* Reprobe subchannel if unregistered. */ static int reprobe_subchannel(struct subchannel_id schid, void *data) { @@ -426,33 +482,14 @@ void css_schedule_reprobe(void) EXPORT_SYMBOL_GPL(css_schedule_reprobe); /* - * Rescan for new devices. FIXME: This is slow. - * This function is called when we have lost CRWs due to overflows and we have - * to do subchannel housekeeping. - */ -void -css_reiterate_subchannels(void) -{ - css_clear_subchannel_slow_list(); - need_rescan = 1; -} - -/* * Called from the machine check handler for subchannel report words. */ -int -css_process_crw(int rsid1, int rsid2) +void css_process_crw(int rsid1, int rsid2) { - int ret; struct subchannel_id mchk_schid; CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n", rsid1, rsid2); - - if (need_rescan) - /* We need to iterate all subchannels anyway. */ - return -EAGAIN; - init_subchannel_id(&mchk_schid); mchk_schid.sch_no = rsid1; if (rsid2 != 0) @@ -463,14 +500,7 @@ css_process_crw(int rsid1, int rsid2) * use stsch() to find out if the subchannel in question has come * or gone. */ - ret = css_evaluate_subchannel(mchk_schid, 0); - if (ret == -EAGAIN) { - if (css_enqueue_subchannel_slow(mchk_schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } - return ret; + css_evaluate_subchannel(mchk_schid, 0); } static int __init @@ -745,47 +775,6 @@ struct bus_type css_bus_type = { subsys_initcall(init_channel_subsystem); -int -css_enqueue_subchannel_slow(struct subchannel_id schid) -{ - struct slow_subchannel *new_slow_sch; - unsigned long flags; - - new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC); - if (!new_slow_sch) - return -ENOMEM; - new_slow_sch->schid = schid; - spin_lock_irqsave(&slow_subchannel_lock, flags); - list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head); - spin_unlock_irqrestore(&slow_subchannel_lock, flags); - return 0; -} - -void -css_clear_subchannel_slow_list(void) -{ - unsigned long flags; - - spin_lock_irqsave(&slow_subchannel_lock, flags); - while (!list_empty(&slow_subchannels_head)) { - struct slow_subchannel *slow_sch = - list_entry(slow_subchannels_head.next, - struct slow_subchannel, slow_list); - - list_del_init(slow_subchannels_head.next); - kfree(slow_sch); - } - spin_unlock_irqrestore(&slow_subchannel_lock, flags); -} - - - -int -css_slow_subchannels_exist(void) -{ - return (!list_empty(&slow_subchannels_head)); -} - MODULE_LICENSE("GPL"); EXPORT_SYMBOL(css_bus_type); EXPORT_SYMBOL_GPL(css_characteristics_avail); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index ca2bab9..71fcfdc 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -4,8 +4,11 @@ #define _CSS_H #include #include #include +#include +#include #include +#include #include "schid.h" @@ -143,13 +146,12 @@ extern void css_sch_device_unregister(st extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); -extern int css_process_crw(int, int); +extern void css_process_crw(int, int); extern void css_reiterate_subchannels(void); +void css_update_ssd_info(struct subchannel *sch); #define __MAX_SUBCHANNEL 65535 #define __MAX_SSID 3 -#define __MAX_CHPID 255 -#define __MAX_CSSID 0 struct channel_subsystem { u8 cssid; @@ -185,16 +187,12 @@ int device_trigger_verify(struct subchan void device_kill_pending_timer(struct subchannel *); /* Helper functions to build lists for the slow path. */ -extern int css_enqueue_subchannel_slow(struct subchannel_id schid); -void css_walk_subchannel_slow_list(void (*fn)(unsigned long)); -void css_clear_subchannel_slow_list(void); -int css_slow_subchannels_exist(void); -extern int need_rescan; +void css_schedule_eval(struct subchannel_id schid); +void css_schedule_eval_all(void); int sch_is_pseudo_sch(struct subchannel *); extern struct workqueue_struct *slow_path_wq; -extern struct work_struct slow_path_work; int subchannel_add_files (struct device *); extern struct attribute_group *subch_attr_groups[]; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e322111..a23ff58 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -56,13 +56,12 @@ ccw_bus_match (struct device * dev, stru /* Store modalias string delimited by prefix/suffix string into buffer with * specified size. Return length of resulting string (excluding trailing '\0') * even if string doesn't fit buffer (snprintf semantics). */ -static int snprint_alias(char *buf, size_t size, const char *prefix, +static int snprint_alias(char *buf, size_t size, struct ccw_device_id *id, const char *suffix) { int len; - len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type, - id->cu_model); + len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model); if (len > size) return len; buf += len; @@ -85,53 +84,40 @@ static int ccw_uevent(struct device *dev struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); int i = 0; - int len; + int len = 0; + int ret; + char modalias_buf[30]; /* CU_TYPE= */ - len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "CU_TYPE=%04X", id->cu_type); + if (ret) + return ret; /* CU_MODEL= */ - len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "CU_MODEL=%02X", id->cu_model); + if (ret) + return ret; /* The next two can be zero, that's ok for us */ /* DEV_TYPE= */ - len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "DEV_TYPE=%04X", id->dev_type); + if (ret) + return ret; /* DEV_MODEL= */ - len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X", - (unsigned char) id->dev_model) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "DEV_MODEL=%02X", id->dev_model); + if (ret) + return ret; /* MODALIAS= */ - len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; - - envp[i] = NULL; - - return 0; + snprint_alias(modalias_buf, sizeof(modalias_buf), id, ""); + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "MODALIAS=%s", modalias_buf); + return ret; } struct bus_type ccw_bus_type; @@ -230,12 +216,18 @@ static ssize_t chpids_show (struct device * dev, struct device_attribute *attr, char * buf) { struct subchannel *sch = to_subchannel(dev); - struct ssd_info *ssd = &sch->ssd_info; + struct chsc_ssd_info *ssd = &sch->ssd_info; ssize_t ret = 0; int chp; + int mask; - for (chp = 0; chp < 8; chp++) - ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]); + for (chp = 0; chp < 8; chp++) { + mask = 0x80 >> chp; + if (ssd->path_mask & mask) + ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id); + else + ret += sprintf(buf + ret, "00 "); + } ret += sprintf (buf+ret, "\n"); return min((ssize_t)PAGE_SIZE, ret); } @@ -280,7 +272,7 @@ modalias_show (struct device *dev, struc struct ccw_device_id *id = &(cdev->id); int len; - len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1; + len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1; return len > PAGE_SIZE ? PAGE_SIZE : len; } @@ -298,16 +290,10 @@ int ccw_device_is_orphan(struct ccw_devi return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent)); } -static void ccw_device_unregister(struct work_struct *work) +static void ccw_device_unregister(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; if (test_and_clear_bit(1, &cdev->private->registered)) - device_unregister(&cdev->dev); - put_device(&cdev->dev); + device_del(&cdev->dev); } static void @@ -324,11 +310,8 @@ ccw_device_remove_disconnected(struct cc spin_lock_irqsave(cdev->ccwlock, flags); cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } + ccw_device_unregister(cdev); + put_device(&cdev->dev); return ; } sch = to_subchannel(cdev->dev.parent); @@ -413,11 +396,60 @@ ccw_device_set_online(struct ccw_device return (ret == 0) ? -ENODEV : ret; } -static ssize_t -online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static void online_store_handle_offline(struct ccw_device *cdev) +{ + if (cdev->private->state == DEV_STATE_DISCONNECTED) + ccw_device_remove_disconnected(cdev); + else if (cdev->drv && cdev->drv->set_offline) + ccw_device_set_offline(cdev); +} + +static int online_store_recog_and_online(struct ccw_device *cdev) +{ + int ret; + + /* Do device recognition, if needed. */ + if (cdev->id.cu_type == 0) { + ret = ccw_device_recognition(cdev); + if (ret) { + printk(KERN_WARNING"Couldn't start recognition " + "for device %s (ret=%d)\n", + cdev->dev.bus_id, ret); + return ret; + } + wait_event(cdev->private->wait_q, + cdev->private->flags.recog_done); + } + if (cdev->drv && cdev->drv->set_online) + ccw_device_set_online(cdev); + return 0; +} +static void online_store_handle_online(struct ccw_device *cdev, int force) +{ + int ret; + + ret = online_store_recog_and_online(cdev); + if (ret) + return; + if (force && cdev->private->state == DEV_STATE_BOXED) { + ret = ccw_device_stlck(cdev); + if (ret) { + printk(KERN_WARNING"ccw_device_stlck for device %s " + "returned %d!\n", cdev->dev.bus_id, ret); + return; + } + if (cdev->id.cu_type == 0) + cdev->private->state = DEV_STATE_NOT_OPER; + online_store_recog_and_online(cdev); + } + +} + +static ssize_t online_store (struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct ccw_device *cdev = to_ccwdev(dev); - int i, force, ret; + int i, force; char *tmp; if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) @@ -434,51 +466,17 @@ online_store (struct device *dev, struct force = 0; i = simple_strtoul(buf, &tmp, 16); } - if (i == 1) { - /* Do device recognition, if needed. */ - if (cdev->id.cu_type == 0) { - ret = ccw_device_recognition(cdev); - if (ret) { - printk(KERN_WARNING"Couldn't start recognition " - "for device %s (ret=%d)\n", - cdev->dev.bus_id, ret); - goto out; - } - wait_event(cdev->private->wait_q, - cdev->private->flags.recog_done); - } - if (cdev->drv && cdev->drv->set_online) - ccw_device_set_online(cdev); - } else if (i == 0) { - if (cdev->private->state == DEV_STATE_DISCONNECTED) - ccw_device_remove_disconnected(cdev); - else if (cdev->drv && cdev->drv->set_offline) - ccw_device_set_offline(cdev); - } - if (force && cdev->private->state == DEV_STATE_BOXED) { - ret = ccw_device_stlck(cdev); - if (ret) { - printk(KERN_WARNING"ccw_device_stlck for device %s " - "returned %d!\n", cdev->dev.bus_id, ret); - goto out; - } - /* Do device recognition, if needed. */ - if (cdev->id.cu_type == 0) { - cdev->private->state = DEV_STATE_NOT_OPER; - ret = ccw_device_recognition(cdev); - if (ret) { - printk(KERN_WARNING"Couldn't start recognition " - "for device %s (ret=%d)\n", - cdev->dev.bus_id, ret); - goto out; - } - wait_event(cdev->private->wait_q, - cdev->private->flags.recog_done); - } - if (cdev->drv && cdev->drv->set_online) - ccw_device_set_online(cdev); + + switch (i) { + case 0: + online_store_handle_offline(cdev); + break; + case 1: + online_store_handle_online(cdev, force); + break; + default: + count = -EINVAL; } - out: if (cdev->drv) module_put(cdev->drv->owner); atomic_set(&cdev->private->onoff, 0); @@ -548,17 +546,10 @@ static struct attribute_group ccwdev_att .attrs = ccwdev_attrs, }; -static int -device_add_files (struct device *dev) -{ - return sysfs_create_group(&dev->kobj, &ccwdev_attr_group); -} - -static void -device_remove_files(struct device *dev) -{ - sysfs_remove_group(&dev->kobj, &ccwdev_attr_group); -} +struct attribute_group *ccwdev_attr_groups[] = { + &ccwdev_attr_group, + NULL, +}; /* this is a simple abstraction for device_register that sets the * correct bus type and adds the bus specific files */ @@ -573,10 +564,6 @@ static int ccw_device_register(struct cc return ret; set_bit(1, &cdev->private->registered); - if ((ret = device_add_files(dev))) { - if (test_and_clear_bit(1, &cdev->private->registered)) - device_del(dev); - } return ret; } @@ -648,10 +635,6 @@ ccw_device_add_changed(struct work_struc return; } set_bit(1, &cdev->private->registered); - if (device_add_files(&cdev->dev)) { - if (test_and_clear_bit(1, &cdev->private->registered)) - device_unregister(&cdev->dev); - } } void ccw_device_do_unreg_rereg(struct work_struct *work) @@ -664,9 +647,7 @@ void ccw_device_do_unreg_rereg(struct wo cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); - device_remove_files(&cdev->dev); - if (test_and_clear_bit(1, &cdev->private->registered)) - device_del(&cdev->dev); + ccw_device_unregister(cdev); PREPARE_WORK(&cdev->private->kick_work, ccw_device_add_changed); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -705,6 +686,7 @@ static int io_subchannel_initialize_dev( cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; INIT_LIST_HEAD(&cdev->private->kick_work.entry); + cdev->dev.groups = ccwdev_attr_groups; /* Do first half of device_register. */ device_initialize(&cdev->dev); if (!get_device(&sch->dev)) { @@ -736,6 +718,7 @@ static int io_subchannel_recog(struct cc static void sch_attach_device(struct subchannel *sch, struct ccw_device *cdev) { + css_update_ssd_info(sch); spin_lock_irq(sch->lock); sch->dev.driver_data = cdev; cdev->private->schid = sch->schid; @@ -871,7 +854,7 @@ io_subchannel_register(struct work_struc priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); - + css_update_ssd_info(sch); /* * io_subchannel_register() will also be called after device * recognition has been done for a boxed device (which will already @@ -888,6 +871,12 @@ io_subchannel_register(struct work_struc } goto out; } + /* + * Now we know this subchannel will stay, we can throw + * our delayed uevent. + */ + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); /* make it known to the system */ ret = ccw_device_register(cdev); if (ret) { @@ -1133,15 +1122,8 @@ io_subchannel_remove (struct subchannel sch->dev.driver_data = NULL; cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); - /* - * Put unregistration on workqueue to avoid livelocks on the css bus - * semaphore. - */ - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } + ccw_device_unregister(cdev); + put_device(&cdev->dev); return 0; } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 089a3dd..898ec3b 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "cio.h" #include "cio_debug.h" @@ -22,6 +23,7 @@ #include "css.h" #include "device.h" #include "chsc.h" #include "ioasm.h" +#include "chp.h" int device_is_online(struct subchannel *sch) @@ -210,14 +212,18 @@ static void __recover_lost_chpids(struct subchannel *sch, int old_lpm) { int mask, i; + struct chp_id chpid; + chp_id_init(&chpid); for (i = 0; i<8; i++) { mask = 0x80 >> i; if (!(sch->lpm & mask)) continue; if (old_lpm & mask) continue; - chpid_is_actually_online(sch->schib.pmcw.chpid[i]); + chpid.id = sch->schib.pmcw.chpid[i]; + if (!chp_is_registered(chpid)) + css_schedule_eval_all(); } } diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 7c7775a..16f59fc 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -16,12 +16,14 @@ #include #include #include +#include #include "cio.h" #include "cio_debug.h" #include "css.h" #include "chsc.h" #include "device.h" +#include "chp.h" int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) { @@ -606,9 +608,12 @@ void * ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) { struct subchannel *sch; + struct chp_id chpid; sch = to_subchannel(cdev->dev.parent); - return chsc_get_chp_desc(sch, chp_no); + chp_id_init(&chpid); + chpid.id = sch->schib.pmcw.chpid[chp_no]; + return chp_get_chp_desc(chpid); } // FIXME: these have to go: diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c new file mode 100644 index 0000000..16ea828 --- /dev/null +++ b/drivers/s390/cio/idset.c @@ -0,0 +1,112 @@ +/* + * drivers/s390/cio/idset.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#include +#include +#include "idset.h" +#include "css.h" + +struct idset { + int num_ssid; + int num_id; + unsigned long bitmap[0]; +}; + +static inline unsigned long bitmap_size(int num_ssid, int num_id) +{ + return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long); +} + +static struct idset *idset_new(int num_ssid, int num_id) +{ + struct idset *set; + + set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id), + GFP_KERNEL); + if (set) { + set->num_ssid = num_ssid; + set->num_id = num_id; + } + return set; +} + +void idset_free(struct idset *set) +{ + kfree(set); +} + +void idset_clear(struct idset *set) +{ + memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id)); +} + +void idset_fill(struct idset *set) +{ + memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id)); +} + +static inline void idset_add(struct idset *set, int ssid, int id) +{ + set_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline void idset_del(struct idset *set, int ssid, int id) +{ + clear_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_contains(struct idset *set, int ssid, int id) +{ + return test_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_get_first(struct idset *set, int *ssid, int *id) +{ + int bitnum; + + bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id); + if (bitnum >= set->num_ssid * set->num_id) + return 0; + *ssid = bitnum / set->num_id; + *id = bitnum % set->num_id; + return 1; +} + +struct idset *idset_sch_new(void) +{ + return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1); +} + +void idset_sch_add(struct idset *set, struct subchannel_id schid) +{ + idset_add(set, schid.ssid, schid.sch_no); +} + +void idset_sch_del(struct idset *set, struct subchannel_id schid) +{ + idset_del(set, schid.ssid, schid.sch_no); +} + +int idset_sch_contains(struct idset *set, struct subchannel_id schid) +{ + return idset_contains(set, schid.ssid, schid.sch_no); +} + +int idset_sch_get_first(struct idset *set, struct subchannel_id *schid) +{ + int ssid = 0; + int id = 0; + int rc; + + rc = idset_get_first(set, &ssid, &id); + if (rc) { + init_subchannel_id(schid); + schid->ssid = ssid; + schid->sch_no = id; + } + return rc; +} diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h new file mode 100644 index 0000000..144466a --- /dev/null +++ b/drivers/s390/cio/idset.h @@ -0,0 +1,25 @@ +/* + * drivers/s390/cio/idset.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef S390_IDSET_H +#define S390_IDSET_H S390_IDSET_H + +#include "schid.h" + +struct idset; + +void idset_free(struct idset *set); +void idset_clear(struct idset *set); +void idset_fill(struct idset *set); + +struct idset *idset_sch_new(void); +void idset_sch_add(struct idset *set, struct subchannel_id id); +void idset_sch_del(struct idset *set, struct subchannel_id id); +int idset_sch_contains(struct idset *set, struct subchannel_id id); +int idset_sch_get_first(struct idset *set, struct subchannel_id *id); + +#endif /* S390_IDSET_H */ diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index ad6d829..7153dd9 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -1,6 +1,7 @@ #ifndef S390_CIO_IOASM_H #define S390_CIO_IOASM_H +#include #include "schid.h" /* @@ -189,9 +190,9 @@ static inline int chsc(void *chsc_area) return cc; } -static inline int rchp(int chpid) +static inline int rchp(struct chp_id chpid) { - register unsigned int reg1 asm ("1") = chpid; + register struct chp_id reg1 asm ("1") = chpid; int ccode; asm volatile( diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index bf37cdf..5aac0ec 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -423,27 +423,25 @@ static int ap_uevent (struct device *dev char *buffer, int buffer_size) { struct ap_device *ap_dev = to_ap_dev(dev); - int length; + int retval = 0, length = 0, i = 0; if (!ap_dev) return -ENODEV; /* Set up DEV_TYPE environment variable. */ - envp[0] = buffer; - length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X", - ap_dev->device_type); - if (buffer_size - length <= 0) - return -ENOMEM; - buffer += length; - buffer_size -= length; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEV_TYPE=%04X", ap_dev->device_type); + if (retval) + return retval; + /* Add MODALIAS= */ - envp[1] = buffer; - length = scnprintf(buffer, buffer_size, "MODALIAS=ap:t%02X", - ap_dev->device_type); - if (buffer_size - length <= 0) - return -ENOMEM; - envp[2] = NULL; - return 0; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=ap:t%02X", ap_dev->device_type); + + envp[i] = NULL; + return retval; } static struct bus_type ap_bus_type = { diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 7809a79..6dd64d0 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -3525,8 +3525,8 @@ #endif memcpy(skb_put(skb,len_of_data), privptr->p_mtc_envelope, len_of_data); - skb->mac.raw=skb->data; skb->dev=dev; + skb_reset_mac_header(skb); skb->protocol=htons(ETH_P_IP); skb->ip_summed=CHECKSUM_UNNECESSARY; privptr->stats.rx_packets++; diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c index 0d6d5fc..b20fd06 100644 --- a/drivers/s390/net/ctcmain.c +++ b/drivers/s390/net/ctcmain.c @@ -455,7 +455,7 @@ #endif return; } skb_put(pskb, header->length); - pskb->mac.raw = pskb->data; + skb_reset_mac_header(pskb); len -= header->length; skb = dev_alloc_skb(pskb->len); if (!skb) { @@ -472,8 +472,9 @@ #endif privptr->stats.rx_dropped++; return; } - memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len); - skb->mac.raw = skb->data; + skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len), + pskb->len); + skb_reset_mac_header(skb); skb->dev = pskb->dev; skb->protocol = pskb->protocol; pskb->ip_summed = CHECKSUM_UNNECESSARY; @@ -706,7 +707,8 @@ ch_action_txdone(fsm_instance * fi, int spin_unlock(&ch->collect_lock); return; } - ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data; + ch->trans_skb->data = ch->trans_skb_data; + skb_reset_tail_pointer(ch->trans_skb); ch->trans_skb->len = 0; if (ch->prof.maxmulti < (ch->collect_len + 2)) ch->prof.maxmulti = ch->collect_len + 2; @@ -715,8 +717,9 @@ ch_action_txdone(fsm_instance * fi, int *((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2; i = 0; while ((skb = skb_dequeue(&ch->collect_queue))) { - memcpy(skb_put(ch->trans_skb, skb->len), skb->data, - skb->len); + skb_copy_from_linear_data(skb, skb_put(ch->trans_skb, + skb->len), + skb->len); privptr->stats.tx_packets++; privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH; atomic_dec(&skb->users); @@ -831,7 +834,8 @@ #endif ctc_unpack_skb(ch, skb); } again: - skb->data = skb->tail = ch->trans_skb_data; + skb->data = ch->trans_skb_data; + skb_reset_tail_pointer(skb); skb->len = 0; if (ctc_checkalloc_buffer(ch, 1)) return; @@ -1638,21 +1642,19 @@ add_channel(struct ccw_device *cdev, enu struct channel *ch; DBF_TEXT(trace, 2, __FUNCTION__); - if ((ch = - (struct channel *) kmalloc(sizeof (struct channel), - GFP_KERNEL)) == NULL) { + ch = kzalloc(sizeof(struct channel), GFP_KERNEL); + if (!ch) { ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } - memset(ch, 0, sizeof (struct channel)); - if ((ch->ccw = kmalloc(8*sizeof(struct ccw1), - GFP_KERNEL | GFP_DMA)) == NULL) { + /* assure all flags and counters are reset */ + ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); + if (!ch->ccw) { kfree(ch); ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } - memset(ch->ccw, 0, 8*sizeof(struct ccw1)); // assure all flags and counters are reset /** * "static" ccws are used in the following way: @@ -1692,15 +1694,14 @@ add_channel(struct ccw_device *cdev, enu return -1; } fsm_newstate(ch->fsm, CH_STATE_IDLE); - if ((ch->irb = kmalloc(sizeof (struct irb), - GFP_KERNEL)) == NULL) { + ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL); + if (!ch->irb) { ctc_pr_warn("ctc: Out of memory in add_channel\n"); kfree_fsm(ch->fsm); kfree(ch->ccw); kfree(ch); return -1; } - memset(ch->irb, 0, sizeof (struct irb)); while (*c && less_than((*c)->id, ch->id)) c = &(*c)->next; if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) { @@ -2226,7 +2227,8 @@ transmit_skb(struct channel *ch, struct * IDAL support in CTC is broken, so we have to * care about skb's above 2G ourselves. */ - hi = ((unsigned long) skb->tail + LL_HEADER_LENGTH) >> 31; + hi = ((unsigned long)skb_tail_pointer(skb) + + LL_HEADER_LENGTH) >> 31; if (hi) { nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!nskb) { @@ -2262,11 +2264,12 @@ transmit_skb(struct channel *ch, struct return -EBUSY; } - ch->trans_skb->tail = ch->trans_skb->data; + skb_reset_tail_pointer(ch->trans_skb); ch->trans_skb->len = 0; ch->ccw[1].count = skb->len; - memcpy(skb_put(ch->trans_skb, skb->len), skb->data, - skb->len); + skb_copy_from_linear_data(skb, skb_put(ch->trans_skb, + skb->len), + skb->len); atomic_dec(&skb->users); dev_kfree_skb_irq(skb); ccw_idx = 0; @@ -2745,14 +2748,13 @@ ctc_probe_device(struct ccwgroup_device if (!get_device(&cgdev->dev)) return -ENODEV; - priv = kmalloc(sizeof (struct ctc_priv), GFP_KERNEL); + priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL); if (!priv) { ctc_pr_err("%s: Out of memory\n", __func__); put_device(&cgdev->dev); return -ENOMEM; } - memset(priv, 0, sizeof (struct ctc_priv)); rc = ctc_add_files(&cgdev->dev); if (rc) { kfree(priv); @@ -2793,10 +2795,9 @@ ctc_init_netdevice(struct net_device * d DBF_TEXT(setup, 3, __FUNCTION__); if (alloc_device) { - dev = kmalloc(sizeof (struct net_device), GFP_KERNEL); + dev = kzalloc(sizeof(struct net_device), GFP_KERNEL); if (!dev) return NULL; - memset(dev, 0, sizeof (struct net_device)); } dev->priv = privptr; diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index ecca104..08a994f 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1576,7 +1576,7 @@ __lcs_start_xmit(struct lcs_card *card, header->offset = card->tx_buffer->count; header->type = card->lan_type; header->slot = card->portno; - memcpy(header + 1, skb->data, skb->len); + skb_copy_from_linear_data(skb, header + 1, skb->len); spin_unlock(&card->lock); card->stats.tx_bytes += skb->len; card->stats.tx_packets++; @@ -1784,7 +1784,6 @@ lcs_get_skb(struct lcs_card *card, char card->stats.rx_dropped++; return; } - skb->dev = card->dev; memcpy(skb_put(skb, skb_len), skb_data, skb_len); skb->protocol = card->lan_type_trans(skb, card->dev); card->stats.rx_bytes += skb_len; diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 594320c..e10e85e 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -635,7 +635,7 @@ static void netiucv_unpack_skb(struct iu return; } skb_put(pskb, header->next); - pskb->mac.raw = pskb->data; + skb_reset_mac_header(pskb); skb = dev_alloc_skb(pskb->len); if (!skb) { PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n", @@ -645,8 +645,9 @@ static void netiucv_unpack_skb(struct iu privptr->stats.rx_dropped++; return; } - memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len); - skb->mac.raw = skb->data; + skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len), + pskb->len); + skb_reset_mac_header(skb); skb->dev = pskb->dev; skb->protocol = pskb->protocol; pskb->ip_summed = CHECKSUM_UNNECESSARY; @@ -689,7 +690,8 @@ static void conn_action_rx(fsm_instance msg->length, conn->max_buffsize); return; } - conn->rx_buff->data = conn->rx_buff->tail = conn->rx_buff->head; + conn->rx_buff->data = conn->rx_buff->head; + skb_reset_tail_pointer(conn->rx_buff); conn->rx_buff->len = 0; rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data, msg->length, NULL); @@ -735,14 +737,17 @@ static void conn_action_txdone(fsm_insta } } } - conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head; + conn->tx_buff->data = conn->tx_buff->head; + skb_reset_tail_pointer(conn->tx_buff); conn->tx_buff->len = 0; spin_lock_irqsave(&conn->collect_lock, saveflags); while ((skb = skb_dequeue(&conn->collect_queue))) { header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN; memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN); - memcpy(skb_put(conn->tx_buff, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, + skb_put(conn->tx_buff, skb->len), + skb->len); txbytes += skb->len; txpackets++; stat_maxcq++; @@ -1164,8 +1169,8 @@ static int netiucv_transmit_skb(struct i * Copy the skb to a new allocated skb in lowmem only if the * data is located above 2G in memory or tailroom is < 2. */ - unsigned long hi = - ((unsigned long)(skb->tail + NETIUCV_HDRLEN)) >> 31; + unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) + + NETIUCV_HDRLEN)) >> 31; int copied = 0; if (hi || (skb_tailroom(skb) < 2)) { nskb = alloc_skb(skb->len + NETIUCV_HDRLEN + diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c index 7c735e1..dd7034f 100644 --- a/drivers/s390/net/qeth_eddp.c +++ b/drivers/s390/net/qeth_eddp.c @@ -267,7 +267,8 @@ qeth_eddp_copy_data_tcp(char *dst, struc QETH_DBF_TEXT(trace, 5, "eddpcdtc"); if (skb_shinfo(eddp->skb)->nr_frags == 0) { - memcpy(dst, eddp->skb->data + eddp->skb_offset, len); + skb_copy_from_linear_data_offset(eddp->skb, eddp->skb_offset, + dst, len); *hcsum = csum_partial(eddp->skb->data + eddp->skb_offset, len, *hcsum); eddp->skb_offset += len; @@ -416,7 +417,7 @@ #ifdef CONFIG_QETH_VLAN eddp->skb_offset += VLAN_HLEN; #endif /* CONFIG_QETH_VLAN */ } - tcph = eddp->skb->h.th; + tcph = tcp_hdr(eddp->skb); while (eddp->skb_offset < eddp->skb->len) { data_len = min((int)skb_shinfo(eddp->skb)->gso_size, (int)(eddp->skb->len - eddp->skb_offset)); @@ -473,20 +474,24 @@ qeth_eddp_fill_context_tcp(struct qeth_e QETH_DBF_TEXT(trace, 5, "eddpficx"); /* create our segmentation headers and copy original headers */ if (skb->protocol == htons(ETH_P_IP)) - eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.iph, - skb->nh.iph->ihl*4, - (u8 *)skb->h.th, skb->h.th->doff*4); + eddp = qeth_eddp_create_eddp_data(qhdr, + skb_network_header(skb), + ip_hdrlen(skb), + skb_transport_header(skb), + tcp_hdrlen(skb)); else - eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.ipv6h, - sizeof(struct ipv6hdr), - (u8 *)skb->h.th, skb->h.th->doff*4); + eddp = qeth_eddp_create_eddp_data(qhdr, + skb_network_header(skb), + sizeof(struct ipv6hdr), + skb_transport_header(skb), + tcp_hdrlen(skb)); if (eddp == NULL) { QETH_DBF_TEXT(trace, 2, "eddpfcnm"); return -ENOMEM; } if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) { - skb->mac.raw = (skb->data) + sizeof(struct qeth_hdr); + skb_set_mac_header(skb, sizeof(struct qeth_hdr)); memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN); #ifdef CONFIG_QETH_VLAN if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) { @@ -590,12 +595,13 @@ qeth_eddp_create_context_tcp(struct qeth QETH_DBF_TEXT(trace, 5, "creddpct"); if (skb->protocol == htons(ETH_P_IP)) ctx = qeth_eddp_create_context_generic(card, skb, - sizeof(struct qeth_hdr) + skb->nh.iph->ihl*4 + - skb->h.th->doff*4); + (sizeof(struct qeth_hdr) + + ip_hdrlen(skb) + + tcp_hdrlen(skb))); else if (skb->protocol == htons(ETH_P_IPV6)) ctx = qeth_eddp_create_context_generic(card, skb, sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) + - skb->h.th->doff*4); + tcp_hdrlen(skb)); else QETH_DBF_TEXT(trace, 2, "cetcpinv"); diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index d8a86f5..ad7792d 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -2278,7 +2278,7 @@ #ifdef CONFIG_TR (card->info.link_type == QETH_LINK_TYPE_LANE_TR)) return tr_type_trans(skb,dev); #endif /* CONFIG_TR */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN ); eth = eth_hdr(skb); @@ -2306,9 +2306,9 @@ qeth_rebuild_skb_fake_ll_tr(struct qeth_ struct iphdr *ip_hdr; QETH_DBF_TEXT(trace,5,"skbfktr"); - skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_TR; + skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_TR); /* this is a fake ethernet header */ - fake_hdr = (struct trh_hdr *) skb->mac.raw; + fake_hdr = tr_hdr(skb); /* the destination MAC address */ switch (skb->pkt_type){ @@ -2359,9 +2359,9 @@ qeth_rebuild_skb_fake_ll_eth(struct qeth struct iphdr *ip_hdr; QETH_DBF_TEXT(trace,5,"skbfketh"); - skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_ETH; + skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_ETH); /* this is a fake ethernet header */ - fake_hdr = (struct ethhdr *) skb->mac.raw; + fake_hdr = eth_hdr(skb); /* the destination MAC address */ switch (skb->pkt_type){ @@ -2461,7 +2461,7 @@ #endif /* CONFIG_QETH_IPV6 */ if (card->options.fake_ll) qeth_rebuild_skb_fake_ll(card, skb, hdr); else - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->ip_summed = card->options.checksum_type; if (card->options.checksum_type == HW_CHECKSUMMING){ if ( (hdr->hdr.l3.ext_flags & @@ -2501,7 +2501,8 @@ qeth_process_inbound_buffer(struct qeth_ vlan_tag = qeth_rebuild_skb(card, skb, hdr); else { /*in case of OSN*/ skb_push(skb, sizeof(struct qeth_hdr)); - memcpy(skb->data, hdr, sizeof(struct qeth_hdr)); + skb_copy_to_linear_data(skb, hdr, + sizeof(struct qeth_hdr)); } /* is device UP ? */ if (!(card->dev->flags & IFF_UP)){ @@ -3778,9 +3779,11 @@ qeth_get_cast_type(struct qeth_card *car } /* try something else */ if (skb->protocol == ETH_P_IPV6) - return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0; + return (skb_network_header(skb)[24] == 0xff) ? + RTN_MULTICAST : 0; else if (skb->protocol == ETH_P_IP) - return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0; + return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ? + RTN_MULTICAST : 0; /* ... */ if (!memcmp(skb->data, skb->dev->broadcast, 6)) return RTN_BROADCAST; @@ -3818,18 +3821,20 @@ qeth_get_priority_queue(struct qeth_card return card->info.is_multicast_different & (card->qdio.no_out_queues - 1); if (card->qdio.do_prio_queueing && (ipv == 4)) { + const u8 tos = ip_hdr(skb)->tos; + if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_TOS){ - if (skb->nh.iph->tos & IP_TOS_NOTIMPORTANT) + if (tos & IP_TOS_NOTIMPORTANT) return 3; - if (skb->nh.iph->tos & IP_TOS_HIGHRELIABILITY) + if (tos & IP_TOS_HIGHRELIABILITY) return 2; - if (skb->nh.iph->tos & IP_TOS_HIGHTHROUGHPUT) + if (tos & IP_TOS_HIGHTHROUGHPUT) return 1; - if (skb->nh.iph->tos & IP_TOS_LOWDELAY) + if (tos & IP_TOS_LOWDELAY) return 0; } if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_PREC) - return 3 - (skb->nh.iph->tos >> 6); + return 3 - (tos >> 6); } else if (card->qdio.do_prio_queueing && (ipv == 6)) { /* TODO: IPv6!!! */ } @@ -3866,9 +3871,9 @@ #ifdef CONFIG_QETH_VLAN * memcpys instead of one memmove to save cycles. */ skb_push(skb, VLAN_HLEN); - memcpy(skb->data, skb->data + 4, 4); - memcpy(skb->data + 4, skb->data + 8, 4); - memcpy(skb->data + 8, skb->data + 12, 4); + skb_copy_to_linear_data(skb, skb->data + 4, 4); + skb_copy_to_linear_data_offset(skb, 4, skb->data + 8, 4); + skb_copy_to_linear_data_offset(skb, 8, skb->data + 12, 4); tag = (u16 *)(skb->data + 12); /* * first two bytes = ETH_P_8021Q (0x8100) @@ -4039,7 +4044,8 @@ #endif /* CONFIG_QETH_VLAN */ *((u32 *) skb->dst->neighbour->primary_key); } else { /* fill in destination address used in ip header */ - *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = skb->nh.iph->daddr; + *((u32 *)(&hdr->hdr.l3.dest_addr[12])) = + ip_hdr(skb)->daddr; } } else if (ipv == 6) { /* IPv6 or passthru */ hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags6(cast_type); @@ -4048,7 +4054,8 @@ #endif /* CONFIG_QETH_VLAN */ skb->dst->neighbour->primary_key, 16); } else { /* fill in destination address used in ip header */ - memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16); + memcpy(hdr->hdr.l3.dest_addr, + &ipv6_hdr(skb)->daddr, 16); } } else { /* passthrough */ if((skb->dev->type == ARPHRD_IEEE802_TR) && diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c index 81f805c..89d56c8 100644 --- a/drivers/s390/net/qeth_proc.c +++ b/drivers/s390/net/qeth_proc.c @@ -37,7 +37,6 @@ qeth_procfile_seq_start(struct seq_file struct device *dev = NULL; loff_t nr = 0; - down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); if (*offset == 0) return SEQ_START_TOKEN; while (1) { @@ -53,7 +52,6 @@ qeth_procfile_seq_start(struct seq_file static void qeth_procfile_seq_stop(struct seq_file *s, void* it) { - up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); } static void * diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h index 14504af..c20e923 100644 --- a/drivers/s390/net/qeth_tso.h +++ b/drivers/s390/net/qeth_tso.h @@ -40,8 +40,8 @@ qeth_tso_fill_header(struct qeth_card *c QETH_DBF_TEXT(trace, 5, "tsofhdr"); hdr = (struct qeth_hdr_tso *) skb->data; - iph = skb->nh.iph; - tcph = skb->h.th; + iph = ip_hdr(skb); + tcph = tcp_hdr(skb); /*fix header to TSO values ...*/ hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; /*set values which are fix for the first approach ...*/ @@ -63,13 +63,9 @@ qeth_tso_fill_header(struct qeth_card *c static inline void qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb) { - struct iphdr *iph; - struct ipv6hdr *ip6h; - struct tcphdr *tcph; - - iph = skb->nh.iph; - ip6h = skb->nh.ipv6h; - tcph = skb->h.th; + struct iphdr *iph = ip_hdr(skb); + struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct tcphdr *tcph = tcp_hdr(skb); tcph->check = 0; if (skb->protocol == ETH_P_IPV6) { diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 806bb1a..644a06e 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -21,6 +21,7 @@ #include #include "cio/cio.h" #include "cio/chsc.h" #include "cio/css.h" +#include "cio/chp.h" #include "s390mach.h" static struct semaphore m_sem; @@ -44,14 +45,13 @@ static int s390_collect_crw_info(void *param) { struct crw crw[2]; - int ccode, ret, slow; + int ccode; struct semaphore *sem; unsigned int chain; sem = (struct semaphore *)param; repeat: down_interruptible(sem); - slow = 0; chain = 0; while (1) { if (unlikely(chain > 1)) { @@ -84,9 +84,8 @@ repeat: /* Check for overflows. */ if (crw[chain].oflw) { pr_debug("%s: crw overflow detected!\n", __FUNCTION__); - css_reiterate_subchannels(); + css_schedule_eval_all(); chain = 0; - slow = 1; continue; } switch (crw[chain].rsc) { @@ -94,10 +93,7 @@ repeat: if (crw[0].chn && !chain) break; pr_debug("source is subchannel %04X\n", crw[0].rsid); - ret = css_process_crw (crw[0].rsid, - chain ? crw[1].rsid : 0); - if (ret == -EAGAIN) - slow = 1; + css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0); break; case CRW_RSC_MONITOR: pr_debug("source is monitoring facility\n"); @@ -116,28 +112,23 @@ repeat: } switch (crw[0].erc) { case CRW_ERC_IPARM: /* Path has come. */ - ret = chp_process_crw(crw[0].rsid, 1); + chp_process_crw(crw[0].rsid, 1); break; case CRW_ERC_PERRI: /* Path has gone. */ case CRW_ERC_PERRN: - ret = chp_process_crw(crw[0].rsid, 0); + chp_process_crw(crw[0].rsid, 0); break; default: pr_debug("Don't know how to handle erc=%x\n", crw[0].erc); - ret = 0; } - if (ret == -EAGAIN) - slow = 1; break; case CRW_RSC_CONFIG: pr_debug("source is configuration-alert facility\n"); break; case CRW_RSC_CSS: pr_debug("source is channel subsystem\n"); - ret = chsc_process_crw(); - if (ret == -EAGAIN) - slow = 1; + chsc_process_crw(); break; default: pr_debug("unknown source\n"); @@ -146,8 +137,6 @@ repeat: /* chain is always 0 or 1 here. */ chain = crw[chain].chn ? chain + 1 : 0; } - if (slow) - queue_work(slow_path_wq, &slow_path_work); goto repeat; return 0; } diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c index 090743d..19343f9 100644 --- a/drivers/s390/sysinfo.c +++ b/drivers/s390/sysinfo.c @@ -357,6 +357,24 @@ static __init int create_proc_sysinfo(vo __initcall(create_proc_sysinfo); +int get_cpu_capability(unsigned int *capability) +{ + struct sysinfo_1_2_2 *info; + int rc; + + info = (void *) get_zeroed_page(GFP_KERNEL); + if (!info) + return -ENOMEM; + rc = stsi(info, 1, 2, 2); + if (rc == -ENOSYS) + goto out; + rc = 0; + *capability = info->capability; +out: + free_page((unsigned long) info); + return rc; +} + /* * CPU capability might have changed. Therefore recalculate loops_per_jiffy. */ diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index 2cea4f5..f2be2ea 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -726,7 +726,7 @@ static struct miscdevice envctrl_dev = { * Return: None. */ static void envctrl_set_mon(struct i2c_child_t *pchild, - char *chnl_desc, + const char *chnl_desc, int chnl_no) { /* Firmware only has temperature type. It does not distinguish @@ -763,8 +763,8 @@ static void envctrl_set_mon(struct i2c_c static void envctrl_init_adc(struct i2c_child_t *pchild, struct device_node *dp) { int i = 0, len; - char *pos; - unsigned int *pval; + const char *pos; + const unsigned int *pval; /* Firmware describe channels into a stream separated by a '\0'. */ pos = of_get_property(dp, "channels-description", &len); @@ -859,7 +859,7 @@ static void envctrl_init_i2c_child(struc { int len, i, tbls_size = 0; struct device_node *dp = edev_child->prom_node; - void *pval; + const void *pval; /* Get device address. */ pval = of_get_property(dp, "reg", &len); diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 6e99507..262f01e 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -190,7 +190,7 @@ #endif } if (!sdev) { #ifdef CONFIG_PCI - struct linux_prom_registers *ebus_regs; + const struct linux_prom_registers *ebus_regs; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index 5041c9d..fbfeb89 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c @@ -44,7 +44,6 @@ #include #include #ifdef CONFIG_PCI #include -#include #endif MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)"); @@ -141,7 +140,7 @@ static int copyout(void __user *info, st static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) { - void *pval; + const void *pval; int len; if (!dp || @@ -248,18 +247,17 @@ static int oprompci2node(void __user *ar if (bufsize >= 2*sizeof(int)) { #ifdef CONFIG_PCI struct pci_dev *pdev; - struct pcidev_cookie *pcp; + struct device_node *dp; + pdev = pci_get_bus_and_slot (((int *) op->oprom_array)[0], ((int *) op->oprom_array)[1]); - pcp = pdev->sysdata; - if (pcp != NULL) { - dp = pcp->prom_node; - data->current_node = dp; - *((int *)op->oprom_array) = dp->node; - op->oprom_size = sizeof(int); - err = copyout(argp, op, bufsize + sizeof(int)); - } + dp = pci_device_to_OF_node(pdev); + data->current_node = dp; + *((int *)op->oprom_array) = dp->node; + op->oprom_size = sizeof(int); + err = copyout(argp, op, bufsize + sizeof(int)); + pci_dev_put(pdev); #endif } @@ -410,7 +408,7 @@ static int opiocget(void __user *argp, D struct opiocdesc op; struct device_node *dp; char *str; - void *pval; + const void *pval; int err, len; if (copy_from_user(&op, argp, sizeof(op))) diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c index 6349dd6..eee590a 100644 --- a/drivers/sbus/sbus.c +++ b/drivers/sbus/sbus.c @@ -35,7 +35,7 @@ struct sbus_bus *sbus_root; static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev) { unsigned long base; - void *pval; + const void *pval; int len, err; sdev->prom_node = dp->node; @@ -86,7 +86,7 @@ static void __init fill_sbus_device(stru static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus) { - void *pval; + const void *pval; int len; pval = of_get_property(dp, "ranges", &len); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 4cd280e..fcc4cb6 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1763,9 +1763,15 @@ config SUN3X_ESP The ESP was an on-board SCSI controller used on Sun 3/80 machines. Say Y here to compile in support for it. +config SCSI_ESP_CORE + tristate "ESP Scsi Driver Core" + depends on SCSI + select SCSI_SPI_ATTRS + config SCSI_SUNESP tristate "Sparc ESP Scsi Driver" depends on SBUS && SCSI + select SCSI_ESP_CORE help This is the driver for the Sun ESP SCSI host adapter. The ESP chipset is present in most SPARC SBUS-based computers. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 79ecf4e..70cff4c 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -106,7 +106,8 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megarai obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_MEGARAID_SAS) += megaraid/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o -obj-$(CONFIG_SCSI_SUNESP) += esp.o +obj-$(CONFIG_SCSI_ESP_CORE) += esp_scsi.o +obj-$(CONFIG_SCSI_SUNESP) += sun_esp.o obj-$(CONFIG_SCSI_GDTH) += gdth.o obj-$(CONFIG_SCSI_INITIO) += initio.o obj-$(CONFIG_SCSI_INIA100) += a100u2w.o diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c deleted file mode 100644 index 2c2fe80..0000000 --- a/drivers/scsi/esp.c +++ /dev/null @@ -1,4394 +0,0 @@ -/* esp.c: ESP Sun SCSI driver. - * - * Copyright (C) 1995, 1998, 2006 David S. Miller (davem@davemloft.net) - */ - -/* TODO: - * - * 1) Maybe disable parity checking in config register one for SCSI1 - * targets. (Gilmore says parity error on the SBus can lock up - * old sun4c's) - * 2) Add support for DMA2 pipelining. - * 3) Add tagged queueing. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "esp.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef __sparc_v9__ -#include -#include -#endif - -#include -#include -#include -#include -#include -#include - -#define DRV_VERSION "1.101" - -#define DEBUG_ESP -/* #define DEBUG_ESP_HME */ -/* #define DEBUG_ESP_DATA */ -/* #define DEBUG_ESP_QUEUE */ -/* #define DEBUG_ESP_DISCONNECT */ -/* #define DEBUG_ESP_STATUS */ -/* #define DEBUG_ESP_PHASES */ -/* #define DEBUG_ESP_WORKBUS */ -/* #define DEBUG_STATE_MACHINE */ -/* #define DEBUG_ESP_CMDS */ -/* #define DEBUG_ESP_IRQS */ -/* #define DEBUG_SDTR */ -/* #define DEBUG_ESP_SG */ - -/* Use the following to sprinkle debugging messages in a way which - * suits you if combinations of the above become too verbose when - * trying to track down a specific problem. - */ -/* #define DEBUG_ESP_MISC */ - -#if defined(DEBUG_ESP) -#define ESPLOG(foo) printk foo -#else -#define ESPLOG(foo) -#endif /* (DEBUG_ESP) */ - -#if defined(DEBUG_ESP_HME) -#define ESPHME(foo) printk foo -#else -#define ESPHME(foo) -#endif - -#if defined(DEBUG_ESP_DATA) -#define ESPDATA(foo) printk foo -#else -#define ESPDATA(foo) -#endif - -#if defined(DEBUG_ESP_QUEUE) -#define ESPQUEUE(foo) printk foo -#else -#define ESPQUEUE(foo) -#endif - -#if defined(DEBUG_ESP_DISCONNECT) -#define ESPDISC(foo) printk foo -#else -#define ESPDISC(foo) -#endif - -#if defined(DEBUG_ESP_STATUS) -#define ESPSTAT(foo) printk foo -#else -#define ESPSTAT(foo) -#endif - -#if defined(DEBUG_ESP_PHASES) -#define ESPPHASE(foo) printk foo -#else -#define ESPPHASE(foo) -#endif - -#if defined(DEBUG_ESP_WORKBUS) -#define ESPBUS(foo) printk foo -#else -#define ESPBUS(foo) -#endif - -#if defined(DEBUG_ESP_IRQS) -#define ESPIRQ(foo) printk foo -#else -#define ESPIRQ(foo) -#endif - -#if defined(DEBUG_SDTR) -#define ESPSDTR(foo) printk foo -#else -#define ESPSDTR(foo) -#endif - -#if defined(DEBUG_ESP_MISC) -#define ESPMISC(foo) printk foo -#else -#define ESPMISC(foo) -#endif - -/* Command phase enumeration. */ -enum { - not_issued = 0x00, /* Still in the issue_SC queue. */ - - /* Various forms of selecting a target. */ -#define in_slct_mask 0x10 - in_slct_norm = 0x10, /* ESP is arbitrating, normal selection */ - in_slct_stop = 0x11, /* ESP will select, then stop with IRQ */ - in_slct_msg = 0x12, /* select, then send a message */ - in_slct_tag = 0x13, /* select and send tagged queue msg */ - in_slct_sneg = 0x14, /* select and acquire sync capabilities */ - - /* Any post selection activity. */ -#define in_phases_mask 0x20 - in_datain = 0x20, /* Data is transferring from the bus */ - in_dataout = 0x21, /* Data is transferring to the bus */ - in_data_done = 0x22, /* Last DMA data operation done (maybe) */ - in_msgin = 0x23, /* Eating message from target */ - in_msgincont = 0x24, /* Eating more msg bytes from target */ - in_msgindone = 0x25, /* Decide what to do with what we got */ - in_msgout = 0x26, /* Sending message to target */ - in_msgoutdone = 0x27, /* Done sending msg out */ - in_cmdbegin = 0x28, /* Sending cmd after abnormal selection */ - in_cmdend = 0x29, /* Done sending slow cmd */ - in_status = 0x2a, /* Was in status phase, finishing cmd */ - in_freeing = 0x2b, /* freeing the bus for cmd cmplt or disc */ - in_the_dark = 0x2c, /* Don't know what bus phase we are in */ - - /* Special states, ie. not normal bus transitions... */ -#define in_spec_mask 0x80 - in_abortone = 0x80, /* Aborting one command currently */ - in_abortall = 0x81, /* Blowing away all commands we have */ - in_resetdev = 0x82, /* SCSI target reset in progress */ - in_resetbus = 0x83, /* SCSI bus reset in progress */ - in_tgterror = 0x84, /* Target did something stupid */ -}; - -enum { - /* Zero has special meaning, see skipahead[12]. */ -/*0*/ do_never, - -/*1*/ do_phase_determine, -/*2*/ do_reset_bus, -/*3*/ do_reset_complete, -/*4*/ do_work_bus, -/*5*/ do_intr_end -}; - -/* Forward declarations. */ -static irqreturn_t esp_intr(int irq, void *dev_id); - -/* Debugging routines */ -struct esp_cmdstrings { - u8 cmdchar; - char *text; -} esp_cmd_strings[] = { - /* Miscellaneous */ - { ESP_CMD_NULL, "ESP_NOP", }, - { ESP_CMD_FLUSH, "FIFO_FLUSH", }, - { ESP_CMD_RC, "RSTESP", }, - { ESP_CMD_RS, "RSTSCSI", }, - /* Disconnected State Group */ - { ESP_CMD_RSEL, "RESLCTSEQ", }, - { ESP_CMD_SEL, "SLCTNATN", }, - { ESP_CMD_SELA, "SLCTATN", }, - { ESP_CMD_SELAS, "SLCTATNSTOP", }, - { ESP_CMD_ESEL, "ENSLCTRESEL", }, - { ESP_CMD_DSEL, "DISSELRESEL", }, - { ESP_CMD_SA3, "SLCTATN3", }, - { ESP_CMD_RSEL3, "RESLCTSEQ", }, - /* Target State Group */ - { ESP_CMD_SMSG, "SNDMSG", }, - { ESP_CMD_SSTAT, "SNDSTATUS", }, - { ESP_CMD_SDATA, "SNDDATA", }, - { ESP_CMD_DSEQ, "DISCSEQ", }, - { ESP_CMD_TSEQ, "TERMSEQ", }, - { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", }, - { ESP_CMD_DCNCT, "DISC", }, - { ESP_CMD_RMSG, "RCVMSG", }, - { ESP_CMD_RCMD, "RCVCMD", }, - { ESP_CMD_RDATA, "RCVDATA", }, - { ESP_CMD_RCSEQ, "RCVCMDSEQ", }, - /* Initiator State Group */ - { ESP_CMD_TI, "TRANSINFO", }, - { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", }, - { ESP_CMD_MOK, "MSGACCEPTED", }, - { ESP_CMD_TPAD, "TPAD", }, - { ESP_CMD_SATN, "SATN", }, - { ESP_CMD_RATN, "RATN", }, -}; -#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings))) - -/* Print textual representation of an ESP command */ -static inline void esp_print_cmd(u8 espcmd) -{ - u8 dma_bit = espcmd & ESP_CMD_DMA; - int i; - - espcmd &= ~dma_bit; - for (i = 0; i < NUM_ESP_COMMANDS; i++) - if (esp_cmd_strings[i].cmdchar == espcmd) - break; - if (i == NUM_ESP_COMMANDS) - printk("ESP_Unknown"); - else - printk("%s%s", esp_cmd_strings[i].text, - ((dma_bit) ? "+DMA" : "")); -} - -/* Print the status register's value */ -static inline void esp_print_statreg(u8 statreg) -{ - u8 phase; - - printk("STATUS<"); - phase = statreg & ESP_STAT_PMASK; - printk("%s,", (phase == ESP_DOP ? "DATA-OUT" : - (phase == ESP_DIP ? "DATA-IN" : - (phase == ESP_CMDP ? "COMMAND" : - (phase == ESP_STATP ? "STATUS" : - (phase == ESP_MOP ? "MSG-OUT" : - (phase == ESP_MIP ? "MSG_IN" : - "unknown"))))))); - if (statreg & ESP_STAT_TDONE) - printk("TRANS_DONE,"); - if (statreg & ESP_STAT_TCNT) - printk("TCOUNT_ZERO,"); - if (statreg & ESP_STAT_PERR) - printk("P_ERROR,"); - if (statreg & ESP_STAT_SPAM) - printk("SPAM,"); - if (statreg & ESP_STAT_INTR) - printk("IRQ,"); - printk(">"); -} - -/* Print the interrupt register's value */ -static inline void esp_print_ireg(u8 intreg) -{ - printk("INTREG< "); - if (intreg & ESP_INTR_S) - printk("SLCT_NATN "); - if (intreg & ESP_INTR_SATN) - printk("SLCT_ATN "); - if (intreg & ESP_INTR_RSEL) - printk("RSLCT "); - if (intreg & ESP_INTR_FDONE) - printk("FDONE "); - if (intreg & ESP_INTR_BSERV) - printk("BSERV "); - if (intreg & ESP_INTR_DC) - printk("DISCNCT "); - if (intreg & ESP_INTR_IC) - printk("ILL_CMD "); - if (intreg & ESP_INTR_SR) - printk("SCSI_BUS_RESET "); - printk(">"); -} - -/* Print the sequence step registers contents */ -static inline void esp_print_seqreg(u8 stepreg) -{ - stepreg &= ESP_STEP_VBITS; - printk("STEP<%s>", - (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" : - (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" : - (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" : - (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" : - (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" : - "UNKNOWN")))))); -} - -static char *phase_string(int phase) -{ - switch (phase) { - case not_issued: - return "UNISSUED"; - case in_slct_norm: - return "SLCTNORM"; - case in_slct_stop: - return "SLCTSTOP"; - case in_slct_msg: - return "SLCTMSG"; - case in_slct_tag: - return "SLCTTAG"; - case in_slct_sneg: - return "SLCTSNEG"; - case in_datain: - return "DATAIN"; - case in_dataout: - return "DATAOUT"; - case in_data_done: - return "DATADONE"; - case in_msgin: - return "MSGIN"; - case in_msgincont: - return "MSGINCONT"; - case in_msgindone: - return "MSGINDONE"; - case in_msgout: - return "MSGOUT"; - case in_msgoutdone: - return "MSGOUTDONE"; - case in_cmdbegin: - return "CMDBEGIN"; - case in_cmdend: - return "CMDEND"; - case in_status: - return "STATUS"; - case in_freeing: - return "FREEING"; - case in_the_dark: - return "CLUELESS"; - case in_abortone: - return "ABORTONE"; - case in_abortall: - return "ABORTALL"; - case in_resetdev: - return "RESETDEV"; - case in_resetbus: - return "RESETBUS"; - case in_tgterror: - return "TGTERROR"; - default: - return "UNKNOWN"; - }; -} - -#ifdef DEBUG_STATE_MACHINE -static inline void esp_advance_phase(struct scsi_cmnd *s, int newphase) -{ - ESPLOG(("<%s>", phase_string(newphase))); - s->SCp.sent_command = s->SCp.phase; - s->SCp.phase = newphase; -} -#else -#define esp_advance_phase(__s, __newphase) \ - (__s)->SCp.sent_command = (__s)->SCp.phase; \ - (__s)->SCp.phase = (__newphase); -#endif - -#ifdef DEBUG_ESP_CMDS -static inline void esp_cmd(struct esp *esp, u8 cmd) -{ - esp->espcmdlog[esp->espcmdent] = cmd; - esp->espcmdent = (esp->espcmdent + 1) & 31; - sbus_writeb(cmd, esp->eregs + ESP_CMD); -} -#else -#define esp_cmd(__esp, __cmd) \ - sbus_writeb((__cmd), ((__esp)->eregs) + ESP_CMD) -#endif - -#define ESP_INTSOFF(__dregs) \ - sbus_writel(sbus_readl((__dregs)+DMA_CSR)&~(DMA_INT_ENAB), (__dregs)+DMA_CSR) -#define ESP_INTSON(__dregs) \ - sbus_writel(sbus_readl((__dregs)+DMA_CSR)|DMA_INT_ENAB, (__dregs)+DMA_CSR) -#define ESP_IRQ_P(__dregs) \ - (sbus_readl((__dregs)+DMA_CSR) & (DMA_HNDL_INTR|DMA_HNDL_ERROR)) - -/* How we use the various Linux SCSI data structures for operation. - * - * struct scsi_cmnd: - * - * We keep track of the synchronous capabilities of a target - * in the device member, using sync_min_period and - * sync_max_offset. These are the values we directly write - * into the ESP registers while running a command. If offset - * is zero the ESP will use asynchronous transfers. - * If the borken flag is set we assume we shouldn't even bother - * trying to negotiate for synchronous transfer as this target - * is really stupid. If we notice the target is dropping the - * bus, and we have been allowing it to disconnect, we clear - * the disconnect flag. - */ - - -/* Manipulation of the ESP command queues. Thanks to the aha152x driver - * and its author, Juergen E. Fischer, for the methods used here. - * Note that these are per-ESP queues, not global queues like - * the aha152x driver uses. - */ -static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC) -{ - struct scsi_cmnd *end; - - new_SC->host_scribble = (unsigned char *) NULL; - if (!*SC) - *SC = new_SC; - else { - for (end=*SC;end->host_scribble;end=(struct scsi_cmnd *)end->host_scribble) - ; - end->host_scribble = (unsigned char *) new_SC; - } -} - -static inline void prepend_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC) -{ - new_SC->host_scribble = (unsigned char *) *SC; - *SC = new_SC; -} - -static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd **SC) -{ - struct scsi_cmnd *ptr; - ptr = *SC; - if (ptr) - *SC = (struct scsi_cmnd *) (*SC)->host_scribble; - return ptr; -} - -static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC, int target, int lun) -{ - struct scsi_cmnd *ptr, *prev; - - for (ptr = *SC, prev = NULL; - ptr && ((ptr->device->id != target) || (ptr->device->lun != lun)); - prev = ptr, ptr = (struct scsi_cmnd *) ptr->host_scribble) - ; - if (ptr) { - if (prev) - prev->host_scribble=ptr->host_scribble; - else - *SC=(struct scsi_cmnd *)ptr->host_scribble; - } - return ptr; -} - -/* Resetting various pieces of the ESP scsi driver chipset/buses. */ -static void esp_reset_dma(struct esp *esp) -{ - int can_do_burst16, can_do_burst32, can_do_burst64; - int can_do_sbus64; - u32 tmp; - - can_do_burst16 = (esp->bursts & DMA_BURST16) != 0; - can_do_burst32 = (esp->bursts & DMA_BURST32) != 0; - can_do_burst64 = 0; - can_do_sbus64 = 0; - if (sbus_can_dma_64bit(esp->sdev)) - can_do_sbus64 = 1; - if (sbus_can_burst64(esp->sdev)) - can_do_burst64 = (esp->bursts & DMA_BURST64) != 0; - - /* Punt the DVMA into a known state. */ - if (esp->dma->revision != dvmahme) { - tmp = sbus_readl(esp->dregs + DMA_CSR); - sbus_writel(tmp | DMA_RST_SCSI, esp->dregs + DMA_CSR); - sbus_writel(tmp & ~DMA_RST_SCSI, esp->dregs + DMA_CSR); - } - switch (esp->dma->revision) { - case dvmahme: - /* This is the HME DVMA gate array. */ - - sbus_writel(DMA_RESET_FAS366, esp->dregs + DMA_CSR); - sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR); - - esp->prev_hme_dmacsr = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB); - esp->prev_hme_dmacsr &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ); - - if (can_do_burst64) - esp->prev_hme_dmacsr |= DMA_BRST64; - else if (can_do_burst32) - esp->prev_hme_dmacsr |= DMA_BRST32; - - if (can_do_sbus64) { - esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64; - sbus_set_sbus64(esp->sdev, esp->bursts); - } - - /* This chip is horrible. */ - while (sbus_readl(esp->dregs + DMA_CSR) & DMA_PEND_READ) - udelay(1); - - sbus_writel(0, esp->dregs + DMA_CSR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - - /* This is necessary to avoid having the SCSI channel - * engine lock up on us. - */ - sbus_writel(0, esp->dregs + DMA_ADDR); - - break; - case dvmarev2: - /* This is the gate array found in the sun4m - * NCR SBUS I/O subsystem. - */ - if (esp->erev != esp100) { - tmp = sbus_readl(esp->dregs + DMA_CSR); - sbus_writel(tmp | DMA_3CLKS, esp->dregs + DMA_CSR); - } - break; - case dvmarev3: - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp &= ~DMA_3CLKS; - tmp |= DMA_2CLKS; - if (can_do_burst32) { - tmp &= ~DMA_BRST_SZ; - tmp |= DMA_BRST32; - } - sbus_writel(tmp, esp->dregs + DMA_CSR); - break; - case dvmaesc1: - /* This is the DMA unit found on SCSI/Ether cards. */ - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp |= DMA_ADD_ENABLE; - tmp &= ~DMA_BCNT_ENAB; - if (!can_do_burst32 && can_do_burst16) { - tmp |= DMA_ESC_BURST; - } else { - tmp &= ~(DMA_ESC_BURST); - } - sbus_writel(tmp, esp->dregs + DMA_CSR); - break; - default: - break; - }; - ESP_INTSON(esp->dregs); -} - -/* Reset the ESP chip, _not_ the SCSI bus. */ -static void __init esp_reset_esp(struct esp *esp) -{ - u8 family_code, version; - int i; - - /* Now reset the ESP chip */ - esp_cmd(esp, ESP_CMD_RC); - esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); - esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); - - /* Reload the configuration registers */ - sbus_writeb(esp->cfact, esp->eregs + ESP_CFACT); - esp->prev_stp = 0; - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - esp->prev_soff = 0; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->neg_defp, esp->eregs + ESP_TIMEO); - - /* This is the only point at which it is reliable to read - * the ID-code for a fast ESP chip variants. - */ - esp->max_period = ((35 * esp->ccycle) / 1000); - if (esp->erev == fast) { - version = sbus_readb(esp->eregs + ESP_UID); - family_code = (version & 0xf8) >> 3; - if (family_code == 0x02) - esp->erev = fas236; - else if (family_code == 0x0a) - esp->erev = fashme; /* Version is usually '5'. */ - else - esp->erev = fas100a; - ESPMISC(("esp%d: FAST chip is %s (family=%d, version=%d)\n", - esp->esp_id, - (esp->erev == fas236) ? "fas236" : - ((esp->erev == fas100a) ? "fas100a" : - "fasHME"), family_code, (version & 7))); - - esp->min_period = ((4 * esp->ccycle) / 1000); - } else { - esp->min_period = ((5 * esp->ccycle) / 1000); - } - esp->max_period = (esp->max_period + 3)>>2; - esp->min_period = (esp->min_period + 3)>>2; - - sbus_writeb(esp->config1, esp->eregs + ESP_CFG1); - switch (esp->erev) { - case esp100: - /* nothing to do */ - break; - case esp100a: - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - break; - case esp236: - /* Slow 236 */ - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - esp->prev_cfg3 = esp->config3[0]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - break; - case fashme: - esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); - /* fallthrough... */ - case fas236: - /* Fast 236 or HME */ - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - for (i = 0; i < 16; i++) { - if (esp->erev == fashme) { - u8 cfg3; - - cfg3 = ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH; - if (esp->scsi_id >= 8) - cfg3 |= ESP_CONFIG3_IDBIT3; - esp->config3[i] |= cfg3; - } else { - esp->config3[i] |= ESP_CONFIG3_FCLK; - } - } - esp->prev_cfg3 = esp->config3[0]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - if (esp->erev == fashme) { - esp->radelay = 80; - } else { - if (esp->diff) - esp->radelay = 0; - else - esp->radelay = 96; - } - break; - case fas100a: - /* Fast 100a */ - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - for (i = 0; i < 16; i++) - esp->config3[i] |= ESP_CONFIG3_FCLOCK; - esp->prev_cfg3 = esp->config3[0]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - esp->radelay = 32; - break; - default: - panic("esp: what could it be... I wonder..."); - break; - }; - - /* Eat any bitrot in the chip */ - sbus_readb(esp->eregs + ESP_INTRPT); - udelay(100); -} - -/* This places the ESP into a known state at boot time. */ -static void __init esp_bootup_reset(struct esp *esp) -{ - u8 tmp; - - /* Reset the DMA */ - esp_reset_dma(esp); - - /* Reset the ESP */ - esp_reset_esp(esp); - - /* Reset the SCSI bus, but tell ESP not to generate an irq */ - tmp = sbus_readb(esp->eregs + ESP_CFG1); - tmp |= ESP_CONFIG1_SRRDISAB; - sbus_writeb(tmp, esp->eregs + ESP_CFG1); - - esp_cmd(esp, ESP_CMD_RS); - udelay(400); - - sbus_writeb(esp->config1, esp->eregs + ESP_CFG1); - - /* Eat any bitrot in the chip and we are done... */ - sbus_readb(esp->eregs + ESP_INTRPT); -} - -static int __init esp_find_dvma(struct esp *esp, struct sbus_dev *dma_sdev) -{ - struct sbus_dev *sdev = esp->sdev; - struct sbus_dma *dma; - - if (dma_sdev != NULL) { - for_each_dvma(dma) { - if (dma->sdev == dma_sdev) - break; - } - } else { - for_each_dvma(dma) { - /* If allocated already, can't use it. */ - if (dma->allocated) - continue; - - if (dma->sdev == NULL) - break; - - /* If bus + slot are the same and it has the - * correct OBP name, it's ours. - */ - if (sdev->bus == dma->sdev->bus && - sdev->slot == dma->sdev->slot && - (!strcmp(dma->sdev->prom_name, "dma") || - !strcmp(dma->sdev->prom_name, "espdma"))) - break; - } - } - - /* If we don't know how to handle the dvma, - * do not use this device. - */ - if (dma == NULL) { - printk("Cannot find dvma for ESP%d's SCSI\n", esp->esp_id); - return -1; - } - if (dma->allocated) { - printk("esp%d: can't use my espdma\n", esp->esp_id); - return -1; - } - dma->allocated = 1; - esp->dma = dma; - esp->dregs = dma->regs; - - return 0; -} - -static int __init esp_map_regs(struct esp *esp, int hme) -{ - struct sbus_dev *sdev = esp->sdev; - struct resource *res; - - /* On HME, two reg sets exist, first is DVMA, - * second is ESP registers. - */ - if (hme) - res = &sdev->resource[1]; - else - res = &sdev->resource[0]; - - esp->eregs = sbus_ioremap(res, 0, ESP_REG_SIZE, "ESP Registers"); - - if (esp->eregs == 0) - return -1; - return 0; -} - -static int __init esp_map_cmdarea(struct esp *esp) -{ - struct sbus_dev *sdev = esp->sdev; - - esp->esp_command = sbus_alloc_consistent(sdev, 16, - &esp->esp_command_dvma); - if (esp->esp_command == NULL || - esp->esp_command_dvma == 0) - return -1; - return 0; -} - -static int __init esp_register_irq(struct esp *esp) -{ - esp->ehost->irq = esp->irq = esp->sdev->irqs[0]; - - /* We used to try various overly-clever things to - * reduce the interrupt processing overhead on - * sun4c/sun4m when multiple ESP's shared the - * same IRQ. It was too complex and messy to - * sanely maintain. - */ - if (request_irq(esp->ehost->irq, esp_intr, - IRQF_SHARED, "ESP SCSI", esp)) { - printk("esp%d: Cannot acquire irq line\n", - esp->esp_id); - return -1; - } - - printk("esp%d: IRQ %d ", esp->esp_id, - esp->ehost->irq); - - return 0; -} - -static void __init esp_get_scsi_id(struct esp *esp) -{ - struct sbus_dev *sdev = esp->sdev; - struct device_node *dp = sdev->ofdev.node; - - esp->scsi_id = of_getintprop_default(dp, - "initiator-id", - -1); - if (esp->scsi_id == -1) - esp->scsi_id = of_getintprop_default(dp, - "scsi-initiator-id", - -1); - if (esp->scsi_id == -1) - esp->scsi_id = (sdev->bus == NULL) ? 7 : - of_getintprop_default(sdev->bus->ofdev.node, - "scsi-initiator-id", - 7); - esp->ehost->this_id = esp->scsi_id; - esp->scsi_id_mask = (1 << esp->scsi_id); - -} - -static void __init esp_get_clock_params(struct esp *esp) -{ - struct sbus_dev *sdev = esp->sdev; - int prom_node = esp->prom_node; - int sbus_prom_node; - unsigned int fmhz; - u8 ccf; - - if (sdev != NULL && sdev->bus != NULL) - sbus_prom_node = sdev->bus->prom_node; - else - sbus_prom_node = 0; - - /* This is getting messy but it has to be done - * correctly or else you get weird behavior all - * over the place. We are trying to basically - * figure out three pieces of information. - * - * a) Clock Conversion Factor - * - * This is a representation of the input - * crystal clock frequency going into the - * ESP on this machine. Any operation whose - * timing is longer than 400ns depends on this - * value being correct. For example, you'll - * get blips for arbitration/selection during - * high load or with multiple targets if this - * is not set correctly. - * - * b) Selection Time-Out - * - * The ESP isn't very bright and will arbitrate - * for the bus and try to select a target - * forever if you let it. This value tells - * the ESP when it has taken too long to - * negotiate and that it should interrupt - * the CPU so we can see what happened. - * The value is computed as follows (from - * NCR/Symbios chip docs). - * - * (Time Out Period) * (Input Clock) - * STO = ---------------------------------- - * (8192) * (Clock Conversion Factor) - * - * You usually want the time out period to be - * around 250ms, I think we'll set it a little - * bit higher to account for fully loaded SCSI - * bus's and slow devices that don't respond so - * quickly to selection attempts. (yeah, I know - * this is out of spec. but there is a lot of - * buggy pieces of firmware out there so bite me) - * - * c) Imperical constants for synchronous offset - * and transfer period register values - * - * This entails the smallest and largest sync - * period we could ever handle on this ESP. - */ - - fmhz = prom_getintdefault(prom_node, "clock-frequency", -1); - if (fmhz == -1) - fmhz = (!sbus_prom_node) ? 0 : - prom_getintdefault(sbus_prom_node, "clock-frequency", -1); - - if (fmhz <= (5000000)) - ccf = 0; - else - ccf = (((5000000 - 1) + (fmhz))/(5000000)); - - if (!ccf || ccf > 8) { - /* If we can't find anything reasonable, - * just assume 20MHZ. This is the clock - * frequency of the older sun4c's where I've - * been unable to find the clock-frequency - * PROM property. All other machines provide - * useful values it seems. - */ - ccf = ESP_CCF_F4; - fmhz = (20000000); - } - - if (ccf == (ESP_CCF_F7 + 1)) - esp->cfact = ESP_CCF_F0; - else if (ccf == ESP_CCF_NEVER) - esp->cfact = ESP_CCF_F2; - else - esp->cfact = ccf; - esp->raw_cfact = ccf; - - esp->cfreq = fmhz; - esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz); - esp->ctick = ESP_TICK(ccf, esp->ccycle); - esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf); - esp->sync_defp = SYNC_DEFP_SLOW; - - printk("SCSI ID %d Clk %dMHz CCYC=%d CCF=%d TOut %d ", - esp->scsi_id, (fmhz / 1000000), - (int)esp->ccycle, (int)ccf, (int) esp->neg_defp); -} - -static void __init esp_get_bursts(struct esp *esp, struct sbus_dev *dma) -{ - struct sbus_dev *sdev = esp->sdev; - u8 bursts; - - bursts = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff); - - if (dma) { - u8 tmp = prom_getintdefault(dma->prom_node, - "burst-sizes", 0xff); - if (tmp != 0xff) - bursts &= tmp; - } - - if (sdev->bus) { - u8 tmp = prom_getintdefault(sdev->bus->prom_node, - "burst-sizes", 0xff); - if (tmp != 0xff) - bursts &= tmp; - } - - if (bursts == 0xff || - (bursts & DMA_BURST16) == 0 || - (bursts & DMA_BURST32) == 0) - bursts = (DMA_BURST32 - 1); - - esp->bursts = bursts; -} - -static void __init esp_get_revision(struct esp *esp) -{ - u8 tmp; - - esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); - esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - - tmp = sbus_readb(esp->eregs + ESP_CFG2); - tmp &= ~ESP_CONFIG2_MAGIC; - if (tmp != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { - /* If what we write to cfg2 does not come back, cfg2 - * is not implemented, therefore this must be a plain - * esp100. - */ - esp->erev = esp100; - printk("NCR53C90(esp100)\n"); - } else { - esp->config2 = 0; - esp->prev_cfg3 = esp->config3[0] = 5; - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - sbus_writeb(0, esp->eregs + ESP_CFG3); - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - - tmp = sbus_readb(esp->eregs + ESP_CFG3); - if (tmp != 5) { - /* The cfg2 register is implemented, however - * cfg3 is not, must be esp100a. - */ - esp->erev = esp100a; - printk("NCR53C90A(esp100a)\n"); - } else { - int target; - - for (target = 0; target < 16; target++) - esp->config3[target] = 0; - esp->prev_cfg3 = 0; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - - /* All of cfg{1,2,3} implemented, must be one of - * the fas variants, figure out which one. - */ - if (esp->raw_cfact > ESP_CCF_F5) { - esp->erev = fast; - esp->sync_defp = SYNC_DEFP_FAST; - printk("NCR53C9XF(espfast)\n"); - } else { - esp->erev = esp236; - printk("NCR53C9x(esp236)\n"); - } - esp->config2 = 0; - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - } - } -} - -static void __init esp_init_swstate(struct esp *esp) -{ - int i; - - /* Command queues... */ - esp->current_SC = NULL; - esp->disconnected_SC = NULL; - esp->issue_SC = NULL; - - /* Target and current command state... */ - esp->targets_present = 0; - esp->resetting_bus = 0; - esp->snip = 0; - - init_waitqueue_head(&esp->reset_queue); - - /* Debugging... */ - for(i = 0; i < 32; i++) - esp->espcmdlog[i] = 0; - esp->espcmdent = 0; - - /* MSG phase state... */ - for(i = 0; i < 16; i++) { - esp->cur_msgout[i] = 0; - esp->cur_msgin[i] = 0; - } - esp->prevmsgout = esp->prevmsgin = 0; - esp->msgout_len = esp->msgin_len = 0; - - /* Clear the one behind caches to hold unmatchable values. */ - esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff; - esp->prev_hme_dmacsr = 0xffffffff; -} - -static int __init detect_one_esp(struct scsi_host_template *tpnt, - struct device *dev, - struct sbus_dev *esp_dev, - struct sbus_dev *espdma, - struct sbus_bus *sbus, - int hme) -{ - static int instance; - struct Scsi_Host *esp_host = scsi_host_alloc(tpnt, sizeof(struct esp)); - struct esp *esp; - - if (!esp_host) - return -ENOMEM; - - if (hme) - esp_host->max_id = 16; - esp = (struct esp *) esp_host->hostdata; - esp->ehost = esp_host; - esp->sdev = esp_dev; - esp->esp_id = instance; - esp->prom_node = esp_dev->prom_node; - prom_getstring(esp->prom_node, "name", esp->prom_name, - sizeof(esp->prom_name)); - - if (esp_find_dvma(esp, espdma) < 0) - goto fail_unlink; - if (esp_map_regs(esp, hme) < 0) { - printk("ESP registers unmappable"); - goto fail_dvma_release; - } - if (esp_map_cmdarea(esp) < 0) { - printk("ESP DVMA transport area unmappable"); - goto fail_unmap_regs; - } - if (esp_register_irq(esp) < 0) - goto fail_unmap_cmdarea; - - esp_get_scsi_id(esp); - - esp->diff = prom_getbool(esp->prom_node, "differential"); - if (esp->diff) - printk("Differential "); - - esp_get_clock_params(esp); - esp_get_bursts(esp, espdma); - esp_get_revision(esp); - esp_init_swstate(esp); - - esp_bootup_reset(esp); - - if (scsi_add_host(esp_host, dev)) - goto fail_free_irq; - - dev_set_drvdata(&esp_dev->ofdev.dev, esp); - - scsi_scan_host(esp_host); - instance++; - - return 0; - -fail_free_irq: - free_irq(esp->ehost->irq, esp); - -fail_unmap_cmdarea: - sbus_free_consistent(esp->sdev, 16, - (void *) esp->esp_command, - esp->esp_command_dvma); - -fail_unmap_regs: - sbus_iounmap(esp->eregs, ESP_REG_SIZE); - -fail_dvma_release: - esp->dma->allocated = 0; - -fail_unlink: - scsi_host_put(esp_host); - return -1; -} - -/* Detecting ESP chips on the machine. This is the simple and easy - * version. - */ -static int __devexit esp_remove_common(struct esp *esp) -{ - unsigned int irq = esp->ehost->irq; - - scsi_remove_host(esp->ehost); - - ESP_INTSOFF(esp->dregs); -#if 0 - esp_reset_dma(esp); - esp_reset_esp(esp); -#endif - - free_irq(irq, esp); - sbus_free_consistent(esp->sdev, 16, - (void *) esp->esp_command, esp->esp_command_dvma); - sbus_iounmap(esp->eregs, ESP_REG_SIZE); - esp->dma->allocated = 0; - - scsi_host_put(esp->ehost); - - return 0; -} - - -#ifdef CONFIG_SUN4 - -#include - -static struct sbus_dev sun4_esp_dev; - -static int __init esp_sun4_probe(struct scsi_host_template *tpnt) -{ - if (sun4_esp_physaddr) { - memset(&sun4_esp_dev, 0, sizeof(sun4_esp_dev)); - sun4_esp_dev.reg_addrs[0].phys_addr = sun4_esp_physaddr; - sun4_esp_dev.irqs[0] = 4; - sun4_esp_dev.resource[0].start = sun4_esp_physaddr; - sun4_esp_dev.resource[0].end = - sun4_esp_physaddr + ESP_REG_SIZE - 1; - sun4_esp_dev.resource[0].flags = IORESOURCE_IO; - - return detect_one_esp(tpnt, NULL, - &sun4_esp_dev, NULL, NULL, 0); - } - return 0; -} - -static int __devexit esp_sun4_remove(void) -{ - struct of_device *dev = &sun4_esp_dev.ofdev; - struct esp *esp = dev_get_drvdata(&dev->dev); - - return esp_remove_common(esp); -} - -#else /* !CONFIG_SUN4 */ - -static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match) -{ - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - struct device_node *dp = dev->node; - struct sbus_dev *dma_sdev = NULL; - int hme = 0; - - if (dp->parent && - (!strcmp(dp->parent->name, "espdma") || - !strcmp(dp->parent->name, "dma"))) - dma_sdev = sdev->parent; - else if (!strcmp(dp->name, "SUNW,fas")) { - dma_sdev = sdev; - hme = 1; - } - - return detect_one_esp(match->data, &dev->dev, - sdev, dma_sdev, sdev->bus, hme); -} - -static int __devexit esp_sbus_remove(struct of_device *dev) -{ - struct esp *esp = dev_get_drvdata(&dev->dev); - - return esp_remove_common(esp); -} - -#endif /* !CONFIG_SUN4 */ - -/* The info function will return whatever useful - * information the developer sees fit. If not provided, then - * the name field will be used instead. - */ -static const char *esp_info(struct Scsi_Host *host) -{ - struct esp *esp; - - esp = (struct esp *) host->hostdata; - switch (esp->erev) { - case esp100: - return "Sparc ESP100 (NCR53C90)"; - case esp100a: - return "Sparc ESP100A (NCR53C90A)"; - case esp236: - return "Sparc ESP236"; - case fas236: - return "Sparc ESP236-FAST"; - case fashme: - return "Sparc ESP366-HME"; - case fas100a: - return "Sparc ESP100A-FAST"; - default: - return "Bogon ESP revision"; - }; -} - -/* From Wolfgang Stanglmeier's NCR scsi driver. */ -struct info_str -{ - char *buffer; - int length; - int offset; - int pos; -}; - -static void copy_mem_info(struct info_str *info, char *data, int len) -{ - if (info->pos + len > info->length) - len = info->length - info->pos; - - if (info->pos + len < info->offset) { - info->pos += len; - return; - } - if (info->pos < info->offset) { - data += (info->offset - info->pos); - len -= (info->offset - info->pos); - } - - if (len > 0) { - memcpy(info->buffer + info->pos, data, len); - info->pos += len; - } -} - -static int copy_info(struct info_str *info, char *fmt, ...) -{ - va_list args; - char buf[81]; - int len; - - va_start(args, fmt); - len = vsprintf(buf, fmt, args); - va_end(args); - - copy_mem_info(info, buf, len); - return len; -} - -static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len) -{ - struct scsi_device *sdev; - struct info_str info; - int i; - - info.buffer = ptr; - info.length = len; - info.offset = offset; - info.pos = 0; - - copy_info(&info, "Sparc ESP Host Adapter:\n"); - copy_info(&info, "\tPROM node\t\t%08x\n", (unsigned int) esp->prom_node); - copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name); - copy_info(&info, "\tESP Model\t\t"); - switch (esp->erev) { - case esp100: - copy_info(&info, "ESP100\n"); - break; - case esp100a: - copy_info(&info, "ESP100A\n"); - break; - case esp236: - copy_info(&info, "ESP236\n"); - break; - case fas236: - copy_info(&info, "FAS236\n"); - break; - case fas100a: - copy_info(&info, "FAS100A\n"); - break; - case fast: - copy_info(&info, "FAST\n"); - break; - case fashme: - copy_info(&info, "Happy Meal FAS\n"); - break; - case espunknown: - default: - copy_info(&info, "Unknown!\n"); - break; - }; - copy_info(&info, "\tDMA Revision\t\t"); - switch (esp->dma->revision) { - case dvmarev0: - copy_info(&info, "Rev 0\n"); - break; - case dvmaesc1: - copy_info(&info, "ESC Rev 1\n"); - break; - case dvmarev1: - copy_info(&info, "Rev 1\n"); - break; - case dvmarev2: - copy_info(&info, "Rev 2\n"); - break; - case dvmarev3: - copy_info(&info, "Rev 3\n"); - break; - case dvmarevplus: - copy_info(&info, "Rev 1+\n"); - break; - case dvmahme: - copy_info(&info, "Rev HME/FAS\n"); - break; - default: - copy_info(&info, "Unknown!\n"); - break; - }; - copy_info(&info, "\tLive Targets\t\t[ "); - for (i = 0; i < 15; i++) { - if (esp->targets_present & (1 << i)) - copy_info(&info, "%d ", i); - } - copy_info(&info, "]\n\n"); - - /* Now describe the state of each existing target. */ - copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n"); - - shost_for_each_device(sdev, esp->ehost) { - struct esp_device *esp_dev = sdev->hostdata; - uint id = sdev->id; - - if (!(esp->targets_present & (1 << id))) - continue; - - copy_info(&info, "%d\t\t", id); - copy_info(&info, "%08lx\t", esp->config3[id]); - copy_info(&info, "[%02lx,%02lx]\t\t\t", - esp_dev->sync_max_offset, - esp_dev->sync_min_period); - copy_info(&info, "%s\t\t", - esp_dev->disconnect ? "yes" : "no"); - copy_info(&info, "%s\n", - (esp->config3[id] & ESP_CONFIG3_EWIDE) ? "yes" : "no"); - } - return info.pos > info.offset? info.pos - info.offset : 0; -} - -/* ESP proc filesystem code. */ -static int esp_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, - int length, int inout) -{ - struct esp *esp = (struct esp *) host->hostdata; - - if (inout) - return -EINVAL; /* not yet */ - - if (start) - *start = buffer; - - return esp_host_info(esp, buffer, offset, length); -} - -static void esp_get_dmabufs(struct esp *esp, struct scsi_cmnd *sp) -{ - if (sp->use_sg == 0) { - sp->SCp.this_residual = sp->request_bufflen; - sp->SCp.buffer = (struct scatterlist *) sp->request_buffer; - sp->SCp.buffers_residual = 0; - if (sp->request_bufflen) { - sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer, - sp->SCp.this_residual, - sp->sc_data_direction); - sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in); - } else { - sp->SCp.ptr = NULL; - } - } else { - sp->SCp.buffer = (struct scatterlist *) sp->request_buffer; - sp->SCp.buffers_residual = sbus_map_sg(esp->sdev, - sp->SCp.buffer, - sp->use_sg, - sp->sc_data_direction); - sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer); - sp->SCp.ptr = (char *) ((unsigned long)sg_dma_address(sp->SCp.buffer)); - } -} - -static void esp_release_dmabufs(struct esp *esp, struct scsi_cmnd *sp) -{ - if (sp->use_sg) { - sbus_unmap_sg(esp->sdev, sp->request_buffer, sp->use_sg, - sp->sc_data_direction); - } else if (sp->request_bufflen) { - sbus_unmap_single(esp->sdev, - sp->SCp.have_data_in, - sp->request_bufflen, - sp->sc_data_direction); - } -} - -static void esp_restore_pointers(struct esp *esp, struct scsi_cmnd *sp) -{ - struct esp_pointers *ep = &esp->data_pointers[sp->device->id]; - - sp->SCp.ptr = ep->saved_ptr; - sp->SCp.buffer = ep->saved_buffer; - sp->SCp.this_residual = ep->saved_this_residual; - sp->SCp.buffers_residual = ep->saved_buffers_residual; -} - -static void esp_save_pointers(struct esp *esp, struct scsi_cmnd *sp) -{ - struct esp_pointers *ep = &esp->data_pointers[sp->device->id]; - - ep->saved_ptr = sp->SCp.ptr; - ep->saved_buffer = sp->SCp.buffer; - ep->saved_this_residual = sp->SCp.this_residual; - ep->saved_buffers_residual = sp->SCp.buffers_residual; -} - -/* Some rules: - * - * 1) Never ever panic while something is live on the bus. - * If there is to be any chance of syncing the disks this - * rule is to be obeyed. - * - * 2) Any target that causes a foul condition will no longer - * have synchronous transfers done to it, no questions - * asked. - * - * 3) Keep register accesses to a minimum. Think about some - * day when we have Xbus machines this is running on and - * the ESP chip is on the other end of the machine on a - * different board from the cpu where this is running. - */ - -/* Fire off a command. We assume the bus is free and that the only - * case where we could see an interrupt is where we have disconnected - * commands active and they are trying to reselect us. - */ -static inline void esp_check_cmd(struct esp *esp, struct scsi_cmnd *sp) -{ - switch (sp->cmd_len) { - case 6: - case 10: - case 12: - esp->esp_slowcmd = 0; - break; - - default: - esp->esp_slowcmd = 1; - esp->esp_scmdleft = sp->cmd_len; - esp->esp_scmdp = &sp->cmnd[0]; - break; - }; -} - -static inline void build_sync_nego_msg(struct esp *esp, int period, int offset) -{ - esp->cur_msgout[0] = EXTENDED_MESSAGE; - esp->cur_msgout[1] = 3; - esp->cur_msgout[2] = EXTENDED_SDTR; - esp->cur_msgout[3] = period; - esp->cur_msgout[4] = offset; - esp->msgout_len = 5; -} - -/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */ -static inline void build_wide_nego_msg(struct esp *esp, int size) -{ - esp->cur_msgout[0] = EXTENDED_MESSAGE; - esp->cur_msgout[1] = 2; - esp->cur_msgout[2] = EXTENDED_WDTR; - switch (size) { - case 32: - esp->cur_msgout[3] = 2; - break; - case 16: - esp->cur_msgout[3] = 1; - break; - case 8: - default: - esp->cur_msgout[3] = 0; - break; - }; - - esp->msgout_len = 4; -} - -static void esp_exec_cmd(struct esp *esp) -{ - struct scsi_cmnd *SCptr; - struct scsi_device *SDptr; - struct esp_device *esp_dev; - volatile u8 *cmdp = esp->esp_command; - u8 the_esp_command; - int lun, target; - int i; - - /* Hold off if we have disconnected commands and - * an IRQ is showing... - */ - if (esp->disconnected_SC && ESP_IRQ_P(esp->dregs)) - return; - - /* Grab first member of the issue queue. */ - SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC); - - /* Safe to panic here because current_SC is null. */ - if (!SCptr) - panic("esp: esp_exec_cmd and issue queue is NULL"); - - SDptr = SCptr->device; - esp_dev = SDptr->hostdata; - lun = SCptr->device->lun; - target = SCptr->device->id; - - esp->snip = 0; - esp->msgout_len = 0; - - /* Send it out whole, or piece by piece? The ESP - * only knows how to automatically send out 6, 10, - * and 12 byte commands. I used to think that the - * Linux SCSI code would never throw anything other - * than that to us, but then again there is the - * SCSI generic driver which can send us anything. - */ - esp_check_cmd(esp, SCptr); - - /* If arbitration/selection is successful, the ESP will leave - * ATN asserted, causing the target to go into message out - * phase. The ESP will feed the target the identify and then - * the target can only legally go to one of command, - * datain/out, status, or message in phase, or stay in message - * out phase (should we be trying to send a sync negotiation - * message after the identify). It is not allowed to drop - * BSY, but some buggy targets do and we check for this - * condition in the selection complete code. Most of the time - * we'll make the command bytes available to the ESP and it - * will not interrupt us until it finishes command phase, we - * cannot do this for command sizes the ESP does not - * understand and in this case we'll get interrupted right - * when the target goes into command phase. - * - * It is absolutely _illegal_ in the presence of SCSI-2 devices - * to use the ESP select w/o ATN command. When SCSI-2 devices are - * present on the bus we _must_ always go straight to message out - * phase with an identify message for the target. Being that - * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2 - * selections should not confuse SCSI-1 we hope. - */ - - if (esp_dev->sync) { - /* this targets sync is known */ -#ifndef __sparc_v9__ -do_sync_known: -#endif - if (esp_dev->disconnect) - *cmdp++ = IDENTIFY(1, lun); - else - *cmdp++ = IDENTIFY(0, lun); - - if (esp->esp_slowcmd) { - the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); - esp_advance_phase(SCptr, in_slct_stop); - } else { - the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); - esp_advance_phase(SCptr, in_slct_norm); - } - } else if (!(esp->targets_present & (1<disconnect)) { - /* After the bootup SCSI code sends both the - * TEST_UNIT_READY and INQUIRY commands we want - * to at least attempt allowing the device to - * disconnect. - */ - ESPMISC(("esp: Selecting device for first time. target=%d " - "lun=%d\n", target, SCptr->device->lun)); - if (!SDptr->borken && !esp_dev->disconnect) - esp_dev->disconnect = 1; - - *cmdp++ = IDENTIFY(0, lun); - esp->prevmsgout = NOP; - esp_advance_phase(SCptr, in_slct_norm); - the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); - - /* Take no chances... */ - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - } else { - /* Sorry, I have had way too many problems with - * various CDROM devices on ESP. -DaveM - */ - int cdrom_hwbug_wkaround = 0; - -#ifndef __sparc_v9__ - /* Never allow disconnects or synchronous transfers on - * SparcStation1 and SparcStation1+. Allowing those - * to be enabled seems to lockup the machine completely. - */ - if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* But we are nice and allow tapes and removable - * disks (but not CDROMs) to disconnect. - */ - if(SDptr->type == TYPE_TAPE || - (SDptr->type != TYPE_ROM && SDptr->removable)) - esp_dev->disconnect = 1; - else - esp_dev->disconnect = 0; - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp_dev->sync = 1; - esp->snip = 0; - goto do_sync_known; - } -#endif /* !(__sparc_v9__) */ - - /* We've talked to this guy before, - * but never negotiated. Let's try, - * need to attempt WIDE first, before - * sync nego, as per SCSI 2 standard. - */ - if (esp->erev == fashme && !esp_dev->wide) { - if (!SDptr->borken && - SDptr->type != TYPE_ROM && - SDptr->removable == 0) { - build_wide_nego_msg(esp, 16); - esp_dev->wide = 1; - esp->wnip = 1; - goto after_nego_msg_built; - } else { - esp_dev->wide = 1; - /* Fall through and try sync. */ - } - } - - if (!SDptr->borken) { - if ((SDptr->type == TYPE_ROM)) { - /* Nice try sucker... */ - ESPMISC(("esp%d: Disabling sync for buggy " - "CDROM.\n", esp->esp_id)); - cdrom_hwbug_wkaround = 1; - build_sync_nego_msg(esp, 0, 0); - } else if (SDptr->removable != 0) { - ESPMISC(("esp%d: Not negotiating sync/wide but " - "allowing disconnect for removable media.\n", - esp->esp_id)); - build_sync_nego_msg(esp, 0, 0); - } else { - build_sync_nego_msg(esp, esp->sync_defp, 15); - } - } else { - build_sync_nego_msg(esp, 0, 0); - } - esp_dev->sync = 1; - esp->snip = 1; - -after_nego_msg_built: - /* A fix for broken SCSI1 targets, when they disconnect - * they lock up the bus and confuse ESP. So disallow - * disconnects for SCSI1 targets for now until we - * find a better fix. - * - * Addendum: This is funny, I figured out what was going - * on. The blotzed SCSI1 target would disconnect, - * one of the other SCSI2 targets or both would be - * disconnected as well. The SCSI1 target would - * stay disconnected long enough that we start - * up a command on one of the SCSI2 targets. As - * the ESP is arbitrating for the bus the SCSI1 - * target begins to arbitrate as well to reselect - * the ESP. The SCSI1 target refuses to drop it's - * ID bit on the data bus even though the ESP is - * at ID 7 and is the obvious winner for any - * arbitration. The ESP is a poor sport and refuses - * to lose arbitration, it will continue indefinitely - * trying to arbitrate for the bus and can only be - * stopped via a chip reset or SCSI bus reset. - * Therefore _no_ disconnects for SCSI1 targets - * thank you very much. ;-) - */ - if(((SDptr->scsi_level < 3) && - (SDptr->type != TYPE_TAPE) && - SDptr->removable == 0) || - cdrom_hwbug_wkaround || SDptr->borken) { - ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d " - "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun)); - esp_dev->disconnect = 0; - *cmdp++ = IDENTIFY(0, lun); - } else { - *cmdp++ = IDENTIFY(1, lun); - } - - /* ESP fifo is only so big... - * Make this look like a slow command. - */ - esp->esp_slowcmd = 1; - esp->esp_scmdleft = SCptr->cmd_len; - esp->esp_scmdp = &SCptr->cmnd[0]; - - the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); - esp_advance_phase(SCptr, in_slct_msg); - } - - if (!esp->esp_slowcmd) - for (i = 0; i < SCptr->cmd_len; i++) - *cmdp++ = SCptr->cmnd[i]; - - /* HME sucks... */ - if (esp->erev == fashme) - sbus_writeb((target & 0xf) | (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT), - esp->eregs + ESP_BUSID); - else - sbus_writeb(target & 7, esp->eregs + ESP_BUSID); - if (esp->prev_soff != esp_dev->sync_max_offset || - esp->prev_stp != esp_dev->sync_min_period || - (esp->erev > esp100a && - esp->prev_cfg3 != esp->config3[target])) { - esp->prev_soff = esp_dev->sync_max_offset; - esp->prev_stp = esp_dev->sync_min_period; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - if (esp->erev > esp100a) { - esp->prev_cfg3 = esp->config3[target]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - } - i = (cmdp - esp->esp_command); - - if (esp->erev == fashme) { - esp_cmd(esp, ESP_CMD_FLUSH); /* Grrr! */ - - /* Set up the DMA and HME counters */ - sbus_writeb(i, esp->eregs + ESP_TCLOW); - sbus_writeb(0, esp->eregs + ESP_TCMED); - sbus_writeb(0, esp->eregs + FAS_RLO); - sbus_writeb(0, esp->eregs + FAS_RHI); - esp_cmd(esp, the_esp_command); - - /* Talk about touchy hardware... */ - esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | - (DMA_SCSI_DISAB | DMA_ENABLE)) & - ~(DMA_ST_WRITE)); - sbus_writel(16, esp->dregs + DMA_COUNT); - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - } else { - u32 tmp; - - /* Set up the DMA and ESP counters */ - sbus_writeb(i, esp->eregs + ESP_TCLOW); - sbus_writeb(0, esp->eregs + ESP_TCMED); - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp &= ~DMA_ST_WRITE; - tmp |= DMA_ENABLE; - sbus_writel(tmp, esp->dregs + DMA_CSR); - if (esp->dma->revision == dvmaesc1) { - if (i) /* Workaround ESC gate array SBUS rerun bug. */ - sbus_writel(PAGE_SIZE, esp->dregs + DMA_COUNT); - } - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - - /* Tell ESP to "go". */ - esp_cmd(esp, the_esp_command); - } -} - -/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */ -static int esp_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) -{ - struct esp *esp; - - /* Set up func ptr and initial driver cmd-phase. */ - SCpnt->scsi_done = done; - SCpnt->SCp.phase = not_issued; - - /* We use the scratch area. */ - ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->device->id, SCpnt->device->lun)); - ESPDISC(("N<%02x,%02x>", SCpnt->device->id, SCpnt->device->lun)); - - esp = (struct esp *) SCpnt->device->host->hostdata; - esp_get_dmabufs(esp, SCpnt); - esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */ - - SCpnt->SCp.Status = CHECK_CONDITION; - SCpnt->SCp.Message = 0xff; - SCpnt->SCp.sent_command = 0; - - /* Place into our queue. */ - if (SCpnt->cmnd[0] == REQUEST_SENSE) { - ESPQUEUE(("RQSENSE\n")); - prepend_SC(&esp->issue_SC, SCpnt); - } else { - ESPQUEUE(("\n")); - append_SC(&esp->issue_SC, SCpnt); - } - - /* Run it now if we can. */ - if (!esp->current_SC && !esp->resetting_bus) - esp_exec_cmd(esp); - - return 0; -} - -/* Dump driver state. */ -static void esp_dump_cmd(struct scsi_cmnd *SCptr) -{ - ESPLOG(("[tgt<%02x> lun<%02x> " - "pphase<%s> cphase<%s>]", - SCptr->device->id, SCptr->device->lun, - phase_string(SCptr->SCp.sent_command), - phase_string(SCptr->SCp.phase))); -} - -static void esp_dump_state(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; -#ifdef DEBUG_ESP_CMDS - int i; -#endif - - ESPLOG(("esp%d: dumping state\n", esp->esp_id)); - ESPLOG(("esp%d: dma -- cond_reg<%08x> addr<%08x>\n", - esp->esp_id, - sbus_readl(esp->dregs + DMA_CSR), - sbus_readl(esp->dregs + DMA_ADDR))); - ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n", - esp->esp_id, esp->sreg, esp->seqreg, esp->ireg)); - ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n", - esp->esp_id, - sbus_readb(esp->eregs + ESP_STATUS), - sbus_readb(esp->eregs + ESP_SSTEP), - sbus_readb(esp->eregs + ESP_INTRPT))); -#ifdef DEBUG_ESP_CMDS - printk("esp%d: last ESP cmds [", esp->esp_id); - i = (esp->espcmdent - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - i = (i - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - i = (i - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - i = (i - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - printk("]\n"); -#endif /* (DEBUG_ESP_CMDS) */ - - if (SCptr) { - ESPLOG(("esp%d: current command ", esp->esp_id)); - esp_dump_cmd(SCptr); - } - ESPLOG(("\n")); - SCptr = esp->disconnected_SC; - ESPLOG(("esp%d: disconnected ", esp->esp_id)); - while (SCptr) { - esp_dump_cmd(SCptr); - SCptr = (struct scsi_cmnd *) SCptr->host_scribble; - } - ESPLOG(("\n")); -} - -/* Abort a command. The host_lock is acquired by caller. */ -static int esp_abort(struct scsi_cmnd *SCptr) -{ - struct esp *esp = (struct esp *) SCptr->device->host->hostdata; - int don; - - ESPLOG(("esp%d: Aborting command\n", esp->esp_id)); - esp_dump_state(esp); - - /* Wheee, if this is the current command on the bus, the - * best we can do is assert ATN and wait for msgout phase. - * This should even fix a hung SCSI bus when we lose state - * in the driver and timeout because the eventual phase change - * will cause the ESP to (eventually) give an interrupt. - */ - if (esp->current_SC == SCptr) { - esp->cur_msgout[0] = ABORT; - esp->msgout_len = 1; - esp->msgout_ctr = 0; - esp_cmd(esp, ESP_CMD_SATN); - return SUCCESS; - } - - /* If it is still in the issue queue then we can safely - * call the completion routine and report abort success. - */ - don = (sbus_readl(esp->dregs + DMA_CSR) & DMA_INT_ENAB); - if (don) { - ESP_INTSOFF(esp->dregs); - } - if (esp->issue_SC) { - struct scsi_cmnd **prev, *this; - for (prev = (&esp->issue_SC), this = esp->issue_SC; - this != NULL; - prev = (struct scsi_cmnd **) &(this->host_scribble), - this = (struct scsi_cmnd *) this->host_scribble) { - - if (this == SCptr) { - *prev = (struct scsi_cmnd *) this->host_scribble; - this->host_scribble = NULL; - - esp_release_dmabufs(esp, this); - this->result = DID_ABORT << 16; - this->scsi_done(this); - - if (don) - ESP_INTSON(esp->dregs); - - return SUCCESS; - } - } - } - - /* Yuck, the command to abort is disconnected, it is not - * worth trying to abort it now if something else is live - * on the bus at this time. So, we let the SCSI code wait - * a little bit and try again later. - */ - if (esp->current_SC) { - if (don) - ESP_INTSON(esp->dregs); - return FAILED; - } - - /* It's disconnected, we have to reconnect to re-establish - * the nexus and tell the device to abort. However, we really - * cannot 'reconnect' per se. Don't try to be fancy, just - * indicate failure, which causes our caller to reset the whole - * bus. - */ - - if (don) - ESP_INTSON(esp->dregs); - - return FAILED; -} - -/* We've sent ESP_CMD_RS to the ESP, the interrupt had just - * arrived indicating the end of the SCSI bus reset. Our job - * is to clean out the command queues and begin re-execution - * of SCSI commands once more. - */ -static int esp_finish_reset(struct esp *esp) -{ - struct scsi_cmnd *sp = esp->current_SC; - - /* Clean up currently executing command, if any. */ - if (sp != NULL) { - esp->current_SC = NULL; - - esp_release_dmabufs(esp, sp); - sp->result = (DID_RESET << 16); - - sp->scsi_done(sp); - } - - /* Clean up disconnected queue, they have been invalidated - * by the bus reset. - */ - if (esp->disconnected_SC) { - while ((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) { - esp_release_dmabufs(esp, sp); - sp->result = (DID_RESET << 16); - - sp->scsi_done(sp); - } - } - - /* SCSI bus reset is complete. */ - esp->resetting_bus = 0; - wake_up(&esp->reset_queue); - - /* Ok, now it is safe to get commands going once more. */ - if (esp->issue_SC) - esp_exec_cmd(esp); - - return do_intr_end; -} - -static int esp_do_resetbus(struct esp *esp) -{ - ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id)); - esp->resetting_bus = 1; - esp_cmd(esp, ESP_CMD_RS); - - return do_intr_end; -} - -/* Reset ESP chip, reset hanging bus, then kill active and - * disconnected commands for targets without soft reset. - * - * The host_lock is acquired by caller. - */ -static int esp_reset(struct scsi_cmnd *SCptr) -{ - struct esp *esp = (struct esp *) SCptr->device->host->hostdata; - - spin_lock_irq(esp->ehost->host_lock); - (void) esp_do_resetbus(esp); - spin_unlock_irq(esp->ehost->host_lock); - - wait_event(esp->reset_queue, (esp->resetting_bus == 0)); - - return SUCCESS; -} - -/* Internal ESP done function. */ -static void esp_done(struct esp *esp, int error) -{ - struct scsi_cmnd *done_SC = esp->current_SC; - - esp->current_SC = NULL; - - esp_release_dmabufs(esp, done_SC); - done_SC->result = error; - - done_SC->scsi_done(done_SC); - - /* Bus is free, issue any commands in the queue. */ - if (esp->issue_SC && !esp->current_SC) - esp_exec_cmd(esp); - -} - -/* Wheee, ESP interrupt engine. */ - -/* Forward declarations. */ -static int esp_do_phase_determine(struct esp *esp); -static int esp_do_data_finale(struct esp *esp); -static int esp_select_complete(struct esp *esp); -static int esp_do_status(struct esp *esp); -static int esp_do_msgin(struct esp *esp); -static int esp_do_msgindone(struct esp *esp); -static int esp_do_msgout(struct esp *esp); -static int esp_do_cmdbegin(struct esp *esp); - -#define sreg_datainp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DIP) -#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP) - -/* Read any bytes found in the FAS366 fifo, storing them into - * the ESP driver software state structure. - */ -static void hme_fifo_read(struct esp *esp) -{ - u8 count = 0; - u8 status = esp->sreg; - - /* Cannot safely frob the fifo for these following cases, but - * we must always read the fifo when the reselect interrupt - * is pending. - */ - if (((esp->ireg & ESP_INTR_RSEL) == 0) && - (sreg_datainp(status) || - sreg_dataoutp(status) || - (esp->current_SC && - esp->current_SC->SCp.phase == in_data_done))) { - ESPHME(("")); - } else { - unsigned long fcnt = sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES; - - /* The HME stores bytes in multiples of 2 in the fifo. */ - ESPHME(("hme_fifo[fcnt=%d", (int)fcnt)); - while (fcnt) { - esp->hme_fifo_workaround_buffer[count++] = - sbus_readb(esp->eregs + ESP_FDATA); - esp->hme_fifo_workaround_buffer[count++] = - sbus_readb(esp->eregs + ESP_FDATA); - ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1])); - fcnt--; - } - if (sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_F1BYTE) { - ESPHME(("")); - sbus_writeb(0, esp->eregs + ESP_FDATA); - esp->hme_fifo_workaround_buffer[count++] = - sbus_readb(esp->eregs + ESP_FDATA); - ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1])); - ESPHME(("CMD_FLUSH")); - esp_cmd(esp, ESP_CMD_FLUSH); - } else { - ESPHME(("no_xtra_byte")); - } - } - ESPHME(("wkarnd_cnt=%d]", (int)count)); - esp->hme_fifo_workaround_count = count; -} - -static inline void hme_fifo_push(struct esp *esp, u8 *bytes, u8 count) -{ - esp_cmd(esp, ESP_CMD_FLUSH); - while (count) { - u8 tmp = *bytes++; - sbus_writeb(tmp, esp->eregs + ESP_FDATA); - sbus_writeb(0, esp->eregs + ESP_FDATA); - count--; - } -} - -/* We try to avoid some interrupts by jumping ahead and see if the ESP - * has gotten far enough yet. Hence the following. - */ -static inline int skipahead1(struct esp *esp, struct scsi_cmnd *scp, - int prev_phase, int new_phase) -{ - if (scp->SCp.sent_command != prev_phase) - return 0; - if (ESP_IRQ_P(esp->dregs)) { - /* Yes, we are able to save an interrupt. */ - if (esp->erev == fashme) - esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2); - esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR)); - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); - if (esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - * Happy Meal indeed.... - */ - ESPHME(("fifo_workaround]")); - if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || - (esp->sreg2 & ESP_STAT2_F1BYTE)) - hme_fifo_read(esp); - } - if (!(esp->ireg & ESP_INTR_SR)) - return 0; - else - return do_reset_complete; - } - /* Ho hum, target is taking forever... */ - scp->SCp.sent_command = new_phase; /* so we don't recurse... */ - return do_intr_end; -} - -static inline int skipahead2(struct esp *esp, struct scsi_cmnd *scp, - int prev_phase1, int prev_phase2, int new_phase) -{ - if (scp->SCp.sent_command != prev_phase1 && - scp->SCp.sent_command != prev_phase2) - return 0; - if (ESP_IRQ_P(esp->dregs)) { - /* Yes, we are able to save an interrupt. */ - if (esp->erev == fashme) - esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2); - esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR)); - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); - if (esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - * Happy Meal indeed.... - */ - ESPHME(("fifo_workaround]")); - if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || - (esp->sreg2 & ESP_STAT2_F1BYTE)) - hme_fifo_read(esp); - } - if (!(esp->ireg & ESP_INTR_SR)) - return 0; - else - return do_reset_complete; - } - /* Ho hum, target is taking forever... */ - scp->SCp.sent_command = new_phase; /* so we don't recurse... */ - return do_intr_end; -} - -/* Now some dma helpers. */ -static void dma_setup(struct esp *esp, __u32 addr, int count, int write) -{ - u32 nreg = sbus_readl(esp->dregs + DMA_CSR); - - if (write) - nreg |= DMA_ST_WRITE; - else - nreg &= ~(DMA_ST_WRITE); - nreg |= DMA_ENABLE; - sbus_writel(nreg, esp->dregs + DMA_CSR); - if (esp->dma->revision == dvmaesc1) { - /* This ESC gate array sucks! */ - __u32 src = addr; - __u32 dest = src + count; - - if (dest & (PAGE_SIZE - 1)) - count = PAGE_ALIGN(count); - sbus_writel(count, esp->dregs + DMA_COUNT); - } - sbus_writel(addr, esp->dregs + DMA_ADDR); -} - -static void dma_drain(struct esp *esp) -{ - u32 tmp; - - if (esp->dma->revision == dvmahme) - return; - if ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_FIFO_ISDRAIN) { - switch (esp->dma->revision) { - default: - tmp |= DMA_FIFO_STDRAIN; - sbus_writel(tmp, esp->dregs + DMA_CSR); - - case dvmarev3: - case dvmaesc1: - while (sbus_readl(esp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN) - udelay(1); - }; - } -} - -static void dma_invalidate(struct esp *esp) -{ - u32 tmp; - - if (esp->dma->revision == dvmahme) { - sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR); - - esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | - (DMA_PARITY_OFF | DMA_2CLKS | - DMA_SCSI_DISAB | DMA_INT_ENAB)) & - ~(DMA_ST_WRITE | DMA_ENABLE)); - - sbus_writel(0, esp->dregs + DMA_CSR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - - /* This is necessary to avoid having the SCSI channel - * engine lock up on us. - */ - sbus_writel(0, esp->dregs + DMA_ADDR); - } else { - while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ) - udelay(1); - - tmp &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); - tmp |= DMA_FIFO_INV; - sbus_writel(tmp, esp->dregs + DMA_CSR); - tmp &= ~DMA_FIFO_INV; - sbus_writel(tmp, esp->dregs + DMA_CSR); - } -} - -static inline void dma_flashclear(struct esp *esp) -{ - dma_drain(esp); - dma_invalidate(esp); -} - -static int dma_can_transfer(struct esp *esp, struct scsi_cmnd *sp) -{ - __u32 base, end, sz; - - if (esp->dma->revision == dvmarev3) { - sz = sp->SCp.this_residual; - if (sz > 0x1000000) - sz = 0x1000000; - } else { - base = ((__u32)((unsigned long)sp->SCp.ptr)); - base &= (0x1000000 - 1); - end = (base + sp->SCp.this_residual); - if (end > 0x1000000) - end = 0x1000000; - sz = (end - base); - } - return sz; -} - -/* Misc. esp helper macros. */ -#define esp_setcount(__eregs, __cnt, __hme) \ - sbus_writeb(((__cnt)&0xff), (__eregs) + ESP_TCLOW); \ - sbus_writeb((((__cnt)>>8)&0xff), (__eregs) + ESP_TCMED); \ - if (__hme) { \ - sbus_writeb((((__cnt)>>16)&0xff), (__eregs) + FAS_RLO); \ - sbus_writeb(0, (__eregs) + FAS_RHI); \ - } - -#define esp_getcount(__eregs, __hme) \ - ((sbus_readb((__eregs) + ESP_TCLOW)&0xff) | \ - ((sbus_readb((__eregs) + ESP_TCMED)&0xff) << 8) | \ - ((__hme) ? sbus_readb((__eregs) + FAS_RLO) << 16 : 0)) - -#define fcount(__esp) \ - (((__esp)->erev == fashme) ? \ - (__esp)->hme_fifo_workaround_count : \ - sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_FBYTES) - -#define fnzero(__esp) \ - (((__esp)->erev == fashme) ? 0 : \ - sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_ONOTZERO) - -/* XXX speculative nops unnecessary when continuing amidst a data phase - * XXX even on esp100!!! another case of flooding the bus with I/O reg - * XXX writes... - */ -#define esp_maybe_nop(__esp) \ - if ((__esp)->erev == esp100) \ - esp_cmd((__esp), ESP_CMD_NULL) - -#define sreg_to_dataphase(__sreg) \ - ((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain) - -/* The ESP100 when in synchronous data phase, can mistake a long final - * REQ pulse from the target as an extra byte, it places whatever is on - * the data lines into the fifo. For now, we will assume when this - * happens that the target is a bit quirky and we don't want to - * be talking synchronously to it anyways. Regardless, we need to - * tell the ESP to eat the extraneous byte so that we can proceed - * to the next phase. - */ -static int esp100_sync_hwbug(struct esp *esp, struct scsi_cmnd *sp, int fifocnt) -{ - /* Do not touch this piece of code. */ - if ((!(esp->erev == esp100)) || - (!(sreg_datainp((esp->sreg = sbus_readb(esp->eregs + ESP_STATUS))) && - !fifocnt) && - !(sreg_dataoutp(esp->sreg) && !fnzero(esp)))) { - if (sp->SCp.phase == in_dataout) - esp_cmd(esp, ESP_CMD_FLUSH); - return 0; - } else { - /* Async mode for this guy. */ - build_sync_nego_msg(esp, 0, 0); - - /* Ack the bogus byte, but set ATN first. */ - esp_cmd(esp, ESP_CMD_SATN); - esp_cmd(esp, ESP_CMD_MOK); - return 1; - } -} - -/* This closes the window during a selection with a reselect pending, because - * we use DMA for the selection process the FIFO should hold the correct - * contents if we get reselected during this process. So we just need to - * ack the possible illegal cmd interrupt pending on the esp100. - */ -static inline int esp100_reconnect_hwbug(struct esp *esp) -{ - u8 tmp; - - if (esp->erev != esp100) - return 0; - tmp = sbus_readb(esp->eregs + ESP_INTRPT); - if (tmp & ESP_INTR_SR) - return 1; - return 0; -} - -/* This verifies the BUSID bits during a reselection so that we know which - * target is talking to us. - */ -static inline int reconnect_target(struct esp *esp) -{ - int it, me = esp->scsi_id_mask, targ = 0; - - if (2 != fcount(esp)) - return -1; - if (esp->erev == fashme) { - /* HME does not latch it's own BUS ID bits during - * a reselection. Also the target number is given - * as an unsigned char, not as a sole bit number - * like the other ESP's do. - * Happy Meal indeed.... - */ - targ = esp->hme_fifo_workaround_buffer[0]; - } else { - it = sbus_readb(esp->eregs + ESP_FDATA); - if (!(it & me)) - return -1; - it &= ~me; - if (it & (it - 1)) - return -1; - while (!(it & 1)) - targ++, it >>= 1; - } - return targ; -} - -/* This verifies the identify from the target so that we know which lun is - * being reconnected. - */ -static inline int reconnect_lun(struct esp *esp) -{ - int lun; - - if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) - return -1; - if (esp->erev == fashme) - lun = esp->hme_fifo_workaround_buffer[1]; - else - lun = sbus_readb(esp->eregs + ESP_FDATA); - - /* Yes, you read this correctly. We report lun of zero - * if we see parity error. ESP reports parity error for - * the lun byte, and this is the only way to hope to recover - * because the target is connected. - */ - if (esp->sreg & ESP_STAT_PERR) - return 0; - - /* Check for illegal bits being set in the lun. */ - if ((lun & 0x40) || !(lun & 0x80)) - return -1; - - return lun & 7; -} - -/* This puts the driver in a state where it can revitalize a command that - * is being continued due to reselection. - */ -static inline void esp_connect(struct esp *esp, struct scsi_cmnd *sp) -{ - struct esp_device *esp_dev = sp->device->hostdata; - - if (esp->prev_soff != esp_dev->sync_max_offset || - esp->prev_stp != esp_dev->sync_min_period || - (esp->erev > esp100a && - esp->prev_cfg3 != esp->config3[sp->device->id])) { - esp->prev_soff = esp_dev->sync_max_offset; - esp->prev_stp = esp_dev->sync_min_period; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - if (esp->erev > esp100a) { - esp->prev_cfg3 = esp->config3[sp->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - } - esp->current_SC = sp; -} - -/* This will place the current working command back into the issue queue - * if we are to receive a reselection amidst a selection attempt. - */ -static inline void esp_reconnect(struct esp *esp, struct scsi_cmnd *sp) -{ - if (!esp->disconnected_SC) - ESPLOG(("esp%d: Weird, being reselected but disconnected " - "command queue is empty.\n", esp->esp_id)); - esp->snip = 0; - esp->current_SC = NULL; - sp->SCp.phase = not_issued; - append_SC(&esp->issue_SC, sp); -} - -/* Begin message in phase. */ -static int esp_do_msgin(struct esp *esp) -{ - /* Must be very careful with the fifo on the HME */ - if ((esp->erev != fashme) || - !(sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_FEMPTY)) - esp_cmd(esp, ESP_CMD_FLUSH); - esp_maybe_nop(esp); - esp_cmd(esp, ESP_CMD_TI); - esp->msgin_len = 1; - esp->msgin_ctr = 0; - esp_advance_phase(esp->current_SC, in_msgindone); - return do_work_bus; -} - -/* This uses various DMA csr fields and the fifo flags count value to - * determine how many bytes were successfully sent/received by the ESP. - */ -static inline int esp_bytes_sent(struct esp *esp, int fifo_count) -{ - int rval = sbus_readl(esp->dregs + DMA_ADDR) - esp->esp_command_dvma; - - if (esp->dma->revision == dvmarev1) - rval -= (4 - ((sbus_readl(esp->dregs + DMA_CSR) & DMA_READ_AHEAD)>>11)); - return rval - fifo_count; -} - -static inline void advance_sg(struct scsi_cmnd *sp) -{ - ++sp->SCp.buffer; - --sp->SCp.buffers_residual; - sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer); - sp->SCp.ptr = (char *)((unsigned long)sg_dma_address(sp->SCp.buffer)); -} - -/* Please note that the way I've coded these routines is that I _always_ - * check for a disconnect during any and all information transfer - * phases. The SCSI standard states that the target _can_ cause a BUS - * FREE condition by dropping all MSG/CD/IO/BSY signals. Also note - * that during information transfer phases the target controls every - * change in phase, the only thing the initiator can do is "ask" for - * a message out phase by driving ATN true. The target can, and sometimes - * will, completely ignore this request so we cannot assume anything when - * we try to force a message out phase to abort/reset a target. Most of - * the time the target will eventually be nice and go to message out, so - * we may have to hold on to our state about what we want to tell the target - * for some period of time. - */ - -/* I think I have things working here correctly. Even partial transfers - * within a buffer or sub-buffer should not upset us at all no matter - * how bad the target and/or ESP fucks things up. - */ -static int esp_do_data(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - int thisphase, hmuch; - - ESPDATA(("esp_do_data: ")); - esp_maybe_nop(esp); - thisphase = sreg_to_dataphase(esp->sreg); - esp_advance_phase(SCptr, thisphase); - ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT")); - hmuch = dma_can_transfer(esp, SCptr); - if (hmuch > (64 * 1024) && (esp->erev != fashme)) - hmuch = (64 * 1024); - ESPDATA(("hmuch<%d> ", hmuch)); - esp->current_transfer_size = hmuch; - - if (esp->erev == fashme) { - u32 tmp = esp->prev_hme_dmacsr; - - /* Always set the ESP count registers first. */ - esp_setcount(esp->eregs, hmuch, 1); - - /* Get the DMA csr computed. */ - tmp |= (DMA_SCSI_DISAB | DMA_ENABLE); - if (thisphase == in_datain) - tmp |= DMA_ST_WRITE; - else - tmp &= ~(DMA_ST_WRITE); - esp->prev_hme_dmacsr = tmp; - - ESPDATA(("DMA|TI --> do_intr_end\n")); - if (thisphase == in_datain) { - sbus_writel(hmuch, esp->dregs + DMA_COUNT); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } else { - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - sbus_writel(hmuch, esp->dregs + DMA_COUNT); - } - sbus_writel((__u32)((unsigned long)SCptr->SCp.ptr), esp->dregs+DMA_ADDR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - } else { - esp_setcount(esp->eregs, hmuch, 0); - dma_setup(esp, ((__u32)((unsigned long)SCptr->SCp.ptr)), - hmuch, (thisphase == in_datain)); - ESPDATA(("DMA|TI --> do_intr_end\n")); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - return do_intr_end; -} - -/* See how successful the data transfer was. */ -static int esp_do_data_finale(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0; - - ESPDATA(("esp_do_data_finale: ")); - - if (SCptr->SCp.phase == in_datain) { - if (esp->sreg & ESP_STAT_PERR) { - /* Yuck, parity error. The ESP asserts ATN - * so that we can go to message out phase - * immediately and inform the target that - * something bad happened. - */ - ESPLOG(("esp%d: data bad parity detected.\n", - esp->esp_id)); - esp->cur_msgout[0] = INITIATOR_ERROR; - esp->msgout_len = 1; - } - dma_drain(esp); - } - dma_invalidate(esp); - - /* This could happen for the above parity error case. */ - if (esp->ireg != ESP_INTR_BSERV) { - /* Please go to msgout phase, please please please... */ - ESPLOG(("esp%d: !BSERV after data, probably to msgout\n", - esp->esp_id)); - return esp_do_phase_determine(esp); - } - - /* Check for partial transfers and other horrible events. - * Note, here we read the real fifo flags register even - * on HME broken adapters because we skip the HME fifo - * workaround code in esp_handle() if we are doing data - * phase things. We don't want to fuck directly with - * the fifo like that, especially if doing synchronous - * transfers! Also, will need to double the count on - * HME if we are doing wide transfers, as the HME fifo - * will move and count 16-bit quantities during wide data. - * SMCC _and_ Qlogic can both bite me. - */ - fifocnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES); - if (esp->erev != fashme) - ecount = esp_getcount(esp->eregs, 0); - bytes_sent = esp->current_transfer_size; - - ESPDATA(("trans_sz(%d), ", bytes_sent)); - if (esp->erev == fashme) { - if (!(esp->sreg & ESP_STAT_TCNT)) { - ecount = esp_getcount(esp->eregs, 1); - bytes_sent -= ecount; - } - - /* Always subtract any cruft remaining in the FIFO. */ - if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE) - fifocnt <<= 1; - if (SCptr->SCp.phase == in_dataout) - bytes_sent -= fifocnt; - - /* I have an IBM disk which exhibits the following - * behavior during writes to it. It disconnects in - * the middle of a partial transfer, the current sglist - * buffer is 1024 bytes, the disk stops data transfer - * at 512 bytes. - * - * However the FAS366 reports that 32 more bytes were - * transferred than really were. This is precisely - * the size of a fully loaded FIFO in wide scsi mode. - * The FIFO state recorded indicates that it is empty. - * - * I have no idea if this is a bug in the FAS366 chip - * or a bug in the firmware on this IBM disk. In any - * event the following seems to be a good workaround. -DaveM - */ - if (bytes_sent != esp->current_transfer_size && - SCptr->SCp.phase == in_dataout) { - int mask = (64 - 1); - - if ((esp->prev_cfg3 & ESP_CONFIG3_EWIDE) == 0) - mask >>= 1; - - if (bytes_sent & mask) - bytes_sent -= (bytes_sent & mask); - } - } else { - if (!(esp->sreg & ESP_STAT_TCNT)) - bytes_sent -= ecount; - if (SCptr->SCp.phase == in_dataout) - bytes_sent -= fifocnt; - } - - ESPDATA(("bytes_sent(%d), ", bytes_sent)); - - /* If we were in synchronous mode, check for peculiarities. */ - if (esp->erev == fashme) { - if (esp_dev->sync_max_offset) { - if (SCptr->SCp.phase == in_dataout) - esp_cmd(esp, ESP_CMD_FLUSH); - } else { - esp_cmd(esp, ESP_CMD_FLUSH); - } - } else { - if (esp_dev->sync_max_offset) - bogus_data = esp100_sync_hwbug(esp, SCptr, fifocnt); - else - esp_cmd(esp, ESP_CMD_FLUSH); - } - - /* Until we are sure of what has happened, we are certainly - * in the dark. - */ - esp_advance_phase(SCptr, in_the_dark); - - if (bytes_sent < 0) { - /* I've seen this happen due to lost state in this - * driver. No idea why it happened, but allowing - * this value to be negative caused things to - * lock up. This allows greater chance of recovery. - * In fact every time I've seen this, it has been - * a driver bug without question. - */ - ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id)); - ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n", - esp->esp_id, - esp->current_transfer_size, fifocnt, ecount)); - ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n", - esp->esp_id, - SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual)); - ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id, - SCptr->device->id)); - SCptr->device->borken = 1; - esp_dev->sync = 0; - bytes_sent = 0; - } - - /* Update the state of our transfer. */ - SCptr->SCp.ptr += bytes_sent; - SCptr->SCp.this_residual -= bytes_sent; - if (SCptr->SCp.this_residual < 0) { - /* shit */ - ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id)); - SCptr->SCp.this_residual = 0; - } - - /* Maybe continue. */ - if (!bogus_data) { - ESPDATA(("!bogus_data, ")); - - /* NO MATTER WHAT, we advance the scatterlist, - * if the target should decide to disconnect - * in between scatter chunks (which is common) - * we could die horribly! I used to have the sg - * advance occur only if we are going back into - * (or are staying in) a data phase, you can - * imagine the hell I went through trying to - * figure this out. - */ - if (SCptr->use_sg && !SCptr->SCp.this_residual) - advance_sg(SCptr); - if (sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) { - ESPDATA(("to more data\n")); - return esp_do_data(esp); - } - ESPDATA(("to new phase\n")); - return esp_do_phase_determine(esp); - } - /* Bogus data, just wait for next interrupt. */ - ESPLOG(("esp%d: bogus_data during end of data phase\n", - esp->esp_id)); - return do_intr_end; -} - -/* We received a non-good status return at the end of - * running a SCSI command. This is used to decide if - * we should clear our synchronous transfer state for - * such a device when that happens. - * - * The idea is that when spinning up a disk or rewinding - * a tape, we don't want to go into a loop re-negotiating - * synchronous capabilities over and over. - */ -static int esp_should_clear_sync(struct scsi_cmnd *sp) -{ - u8 cmd = sp->cmnd[0]; - - /* These cases are for spinning up a disk and - * waiting for that spinup to complete. - */ - if (cmd == START_STOP) - return 0; - - if (cmd == TEST_UNIT_READY) - return 0; - - /* One more special case for SCSI tape drives, - * this is what is used to probe the device for - * completion of a rewind or tape load operation. - */ - if (sp->device->type == TYPE_TAPE) { - if (cmd == MODE_SENSE) - return 0; - } - - return 1; -} - -/* Either a command is completing or a target is dropping off the bus - * to continue the command in the background so we can do other work. - */ -static int esp_do_freebus(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - int rval; - - rval = skipahead2(esp, SCptr, in_status, in_msgindone, in_freeing); - if (rval) - return rval; - if (esp->ireg != ESP_INTR_DC) { - ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id)); - return do_reset_bus; /* target will not drop BSY... */ - } - esp->msgout_len = 0; - esp->prevmsgout = NOP; - if (esp->prevmsgin == COMMAND_COMPLETE) { - /* Normal end of nexus. */ - if (esp->disconnected_SC || (esp->erev == fashme)) - esp_cmd(esp, ESP_CMD_ESEL); - - if (SCptr->SCp.Status != GOOD && - SCptr->SCp.Status != CONDITION_GOOD && - ((1<device->id) & esp->targets_present) && - esp_dev->sync && - esp_dev->sync_max_offset) { - /* SCSI standard says that the synchronous capabilities - * should be renegotiated at this point. Most likely - * we are about to request sense from this target - * in which case we want to avoid using sync - * transfers until we are sure of the current target - * state. - */ - ESPMISC(("esp: Status <%d> for target %d lun %d\n", - SCptr->SCp.Status, SCptr->device->id, SCptr->device->lun)); - - /* But don't do this when spinning up a disk at - * boot time while we poll for completion as it - * fills up the console with messages. Also, tapes - * can report not ready many times right after - * loading up a tape. - */ - if (esp_should_clear_sync(SCptr) != 0) - esp_dev->sync = 0; - } - ESPDISC(("F<%02x,%02x>", SCptr->device->id, SCptr->device->lun)); - esp_done(esp, ((SCptr->SCp.Status & 0xff) | - ((SCptr->SCp.Message & 0xff)<<8) | - (DID_OK << 16))); - } else if (esp->prevmsgin == DISCONNECT) { - /* Normal disconnect. */ - esp_cmd(esp, ESP_CMD_ESEL); - ESPDISC(("D<%02x,%02x>", SCptr->device->id, SCptr->device->lun)); - append_SC(&esp->disconnected_SC, SCptr); - esp->current_SC = NULL; - if (esp->issue_SC) - esp_exec_cmd(esp); - } else { - /* Driver bug, we do not expect a disconnect here - * and should not have advanced the state engine - * to in_freeing. - */ - ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n", - esp->esp_id)); - return do_reset_bus; - } - return do_intr_end; -} - -/* When a reselect occurs, and we cannot find the command to - * reconnect to in our queues, we do this. - */ -static int esp_bad_reconnect(struct esp *esp) -{ - struct scsi_cmnd *sp; - - ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", - esp->esp_id)); - ESPLOG(("QUEUE DUMP\n")); - sp = esp->issue_SC; - ESPLOG(("esp%d: issue_SC[", esp->esp_id)); - while (sp) { - ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun)); - sp = (struct scsi_cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - sp = esp->current_SC; - ESPLOG(("esp%d: current_SC[", esp->esp_id)); - if (sp) - ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun)); - else - ESPLOG(("")); - ESPLOG(("]\n")); - sp = esp->disconnected_SC; - ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); - while (sp) { - ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun)); - sp = (struct scsi_cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - return do_reset_bus; -} - -/* Do the needy when a target tries to reconnect to us. */ -static int esp_do_reconnect(struct esp *esp) -{ - int lun, target; - struct scsi_cmnd *SCptr; - - /* Check for all bogus conditions first. */ - target = reconnect_target(esp); - if (target < 0) { - ESPDISC(("bad bus bits\n")); - return do_reset_bus; - } - lun = reconnect_lun(esp); - if (lun < 0) { - ESPDISC(("target=%2x, bad identify msg\n", target)); - return do_reset_bus; - } - - /* Things look ok... */ - ESPDISC(("R<%02x,%02x>", target, lun)); - - /* Must not flush FIFO or DVMA on HME. */ - if (esp->erev != fashme) { - esp_cmd(esp, ESP_CMD_FLUSH); - if (esp100_reconnect_hwbug(esp)) - return do_reset_bus; - esp_cmd(esp, ESP_CMD_NULL); - } - - SCptr = remove_SC(&esp->disconnected_SC, (u8) target, (u8) lun); - if (!SCptr) - return esp_bad_reconnect(esp); - - esp_connect(esp, SCptr); - esp_cmd(esp, ESP_CMD_MOK); - - if (esp->erev == fashme) - sbus_writeb(((SCptr->device->id & 0xf) | - (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT)), - esp->eregs + ESP_BUSID); - - /* Reconnect implies a restore pointers operation. */ - esp_restore_pointers(esp, SCptr); - - esp->snip = 0; - esp_advance_phase(SCptr, in_the_dark); - return do_intr_end; -} - -/* End of NEXUS (hopefully), pick up status + message byte then leave if - * all goes well. - */ -static int esp_do_status(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - int intr, rval; - - rval = skipahead1(esp, SCptr, in_the_dark, in_status); - if (rval) - return rval; - intr = esp->ireg; - ESPSTAT(("esp_do_status: ")); - if (intr != ESP_INTR_DC) { - int message_out = 0; /* for parity problems */ - - /* Ack the message. */ - ESPSTAT(("ack msg, ")); - esp_cmd(esp, ESP_CMD_MOK); - - if (esp->erev != fashme) { - dma_flashclear(esp); - - /* Wait till the first bits settle. */ - while (esp->esp_command[0] == 0xff) - udelay(1); - } else { - esp->esp_command[0] = esp->hme_fifo_workaround_buffer[0]; - esp->esp_command[1] = esp->hme_fifo_workaround_buffer[1]; - } - - ESPSTAT(("got something, ")); - /* ESP chimes in with one of - * - * 1) function done interrupt: - * both status and message in bytes - * are available - * - * 2) bus service interrupt: - * only status byte was acquired - * - * 3) Anything else: - * can't happen, but we test for it - * anyways - * - * ALSO: If bad parity was detected on either - * the status _or_ the message byte then - * the ESP has asserted ATN on the bus - * and we must therefore wait for the - * next phase change. - */ - if (intr & ESP_INTR_FDONE) { - /* We got it all, hallejulia. */ - ESPSTAT(("got both, ")); - SCptr->SCp.Status = esp->esp_command[0]; - SCptr->SCp.Message = esp->esp_command[1]; - esp->prevmsgin = SCptr->SCp.Message; - esp->cur_msgin[0] = SCptr->SCp.Message; - if (esp->sreg & ESP_STAT_PERR) { - /* There was bad parity for the - * message byte, the status byte - * was ok. - */ - message_out = MSG_PARITY_ERROR; - } - } else if (intr == ESP_INTR_BSERV) { - /* Only got status byte. */ - ESPLOG(("esp%d: got status only, ", esp->esp_id)); - if (!(esp->sreg & ESP_STAT_PERR)) { - SCptr->SCp.Status = esp->esp_command[0]; - SCptr->SCp.Message = 0xff; - } else { - /* The status byte had bad parity. - * we leave the scsi_pointer Status - * field alone as we set it to a default - * of CHECK_CONDITION in esp_queue. - */ - message_out = INITIATOR_ERROR; - } - } else { - /* This shouldn't happen ever. */ - ESPSTAT(("got bolixed\n")); - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } - - if (!message_out) { - ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status, - SCptr->SCp.Message)); - if (SCptr->SCp.Message == COMMAND_COMPLETE) { - ESPSTAT(("and was COMMAND_COMPLETE\n")); - esp_advance_phase(SCptr, in_freeing); - return esp_do_freebus(esp); - } else { - ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n", - esp->esp_id)); - esp->msgin_len = esp->msgin_ctr = 1; - esp_advance_phase(SCptr, in_msgindone); - return esp_do_msgindone(esp); - } - } else { - /* With luck we'll be able to let the target - * know that bad parity happened, it will know - * which byte caused the problems and send it - * again. For the case where the status byte - * receives bad parity, I do not believe most - * targets recover very well. We'll see. - */ - ESPLOG(("esp%d: bad parity somewhere mout=%2x\n", - esp->esp_id, message_out)); - esp->cur_msgout[0] = message_out; - esp->msgout_len = esp->msgout_ctr = 1; - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } - } else { - /* If we disconnect now, all hell breaks loose. */ - ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id)); - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } -} - -static int esp_enter_status(struct esp *esp) -{ - u8 thecmd = ESP_CMD_ICCSEQ; - - esp_cmd(esp, ESP_CMD_FLUSH); - if (esp->erev != fashme) { - u32 tmp; - - esp->esp_command[0] = esp->esp_command[1] = 0xff; - sbus_writeb(2, esp->eregs + ESP_TCLOW); - sbus_writeb(0, esp->eregs + ESP_TCMED); - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp |= (DMA_ST_WRITE | DMA_ENABLE); - sbus_writel(tmp, esp->dregs + DMA_CSR); - if (esp->dma->revision == dvmaesc1) - sbus_writel(0x100, esp->dregs + DMA_COUNT); - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - thecmd |= ESP_CMD_DMA; - } - esp_cmd(esp, thecmd); - esp_advance_phase(esp->current_SC, in_status); - - return esp_do_status(esp); -} - -static int esp_disconnect_amidst_phases(struct esp *esp) -{ - struct scsi_cmnd *sp = esp->current_SC; - struct esp_device *esp_dev = sp->device->hostdata; - - /* This means real problems if we see this - * here. Unless we were actually trying - * to force the device to abort/reset. - */ - ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id)); - ESPLOG(("pphase<%s> cphase<%s>, ", - phase_string(sp->SCp.phase), - phase_string(sp->SCp.sent_command))); - - if (esp->disconnected_SC != NULL || (esp->erev == fashme)) - esp_cmd(esp, ESP_CMD_ESEL); - - switch (esp->cur_msgout[0]) { - default: - /* We didn't expect this to happen at all. */ - ESPLOG(("device is bolixed\n")); - esp_advance_phase(sp, in_tgterror); - esp_done(esp, (DID_ERROR << 16)); - break; - - case BUS_DEVICE_RESET: - ESPLOG(("device reset successful\n")); - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp_dev->sync = 0; - esp_advance_phase(sp, in_resetdev); - esp_done(esp, (DID_RESET << 16)); - break; - - case ABORT: - ESPLOG(("device abort successful\n")); - esp_advance_phase(sp, in_abortone); - esp_done(esp, (DID_ABORT << 16)); - break; - - }; - return do_intr_end; -} - -static int esp_enter_msgout(struct esp *esp) -{ - esp_advance_phase(esp->current_SC, in_msgout); - return esp_do_msgout(esp); -} - -static int esp_enter_msgin(struct esp *esp) -{ - esp_advance_phase(esp->current_SC, in_msgin); - return esp_do_msgin(esp); -} - -static int esp_enter_cmd(struct esp *esp) -{ - esp_advance_phase(esp->current_SC, in_cmdbegin); - return esp_do_cmdbegin(esp); -} - -static int esp_enter_badphase(struct esp *esp) -{ - ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id, - esp->sreg & ESP_STAT_PMASK)); - return do_reset_bus; -} - -typedef int (*espfunc_t)(struct esp *); - -static espfunc_t phase_vector[] = { - esp_do_data, /* ESP_DOP */ - esp_do_data, /* ESP_DIP */ - esp_enter_cmd, /* ESP_CMDP */ - esp_enter_status, /* ESP_STATP */ - esp_enter_badphase, /* ESP_STAT_PMSG */ - esp_enter_badphase, /* ESP_STAT_PMSG | ESP_STAT_PIO */ - esp_enter_msgout, /* ESP_MOP */ - esp_enter_msgin, /* ESP_MIP */ -}; - -/* The target has control of the bus and we have to see where it has - * taken us. - */ -static int esp_do_phase_determine(struct esp *esp) -{ - if ((esp->ireg & ESP_INTR_DC) != 0) - return esp_disconnect_amidst_phases(esp); - return phase_vector[esp->sreg & ESP_STAT_PMASK](esp); -} - -/* First interrupt after exec'ing a cmd comes here. */ -static int esp_select_complete(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - int cmd_bytes_sent, fcnt; - - if (esp->erev != fashme) - esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS); - - if (esp->erev == fashme) - fcnt = esp->hme_fifo_workaround_count; - else - fcnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES); - - cmd_bytes_sent = esp_bytes_sent(esp, fcnt); - dma_invalidate(esp); - - /* Let's check to see if a reselect happened - * while we we're trying to select. This must - * be checked first. - */ - if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) { - esp_reconnect(esp, SCptr); - return esp_do_reconnect(esp); - } - - /* Looks like things worked, we should see a bus service & - * a function complete interrupt at this point. Note we - * are doing a direct comparison because we don't want to - * be fooled into thinking selection was successful if - * ESP_INTR_DC is set, see below. - */ - if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) { - /* target speaks... */ - esp->targets_present |= (1<device->id); - - /* What if the target ignores the sdtr? */ - if (esp->snip) - esp_dev->sync = 1; - - /* See how far, if at all, we got in getting - * the information out to the target. - */ - switch (esp->seqreg) { - default: - - case ESP_STEP_ASEL: - /* Arbitration won, target selected, but - * we are in some phase which is not command - * phase nor is it message out phase. - * - * XXX We've confused the target, obviously. - * XXX So clear it's state, but we also end - * XXX up clearing everyone elses. That isn't - * XXX so nice. I'd like to just reset this - * XXX target, but if I cannot even get it's - * XXX attention and finish selection to talk - * XXX to it, there is not much more I can do. - * XXX If we have a loaded bus we're going to - * XXX spend the next second or so renegotiating - * XXX for synchronous transfers. - */ - ESPLOG(("esp%d: STEP_ASEL for tgt %d\n", - esp->esp_id, SCptr->device->id)); - - case ESP_STEP_SID: - /* Arbitration won, target selected, went - * to message out phase, sent one message - * byte, then we stopped. ATN is asserted - * on the SCSI bus and the target is still - * there hanging on. This is a legal - * sequence step if we gave the ESP a select - * and stop command. - * - * XXX See above, I could set the borken flag - * XXX in the device struct and retry the - * XXX command. But would that help for - * XXX tagged capable targets? - */ - - case ESP_STEP_NCMD: - /* Arbitration won, target selected, maybe - * sent the one message byte in message out - * phase, but we did not go to command phase - * in the end. Actually, we could have sent - * only some of the message bytes if we tried - * to send out the entire identify and tag - * message using ESP_CMD_SA3. - */ - cmd_bytes_sent = 0; - break; - - case ESP_STEP_PPC: - /* No, not the powerPC pinhead. Arbitration - * won, all message bytes sent if we went to - * message out phase, went to command phase - * but only part of the command was sent. - * - * XXX I've seen this, but usually in conjunction - * XXX with a gross error which appears to have - * XXX occurred between the time I told the - * XXX ESP to arbitrate and when I got the - * XXX interrupt. Could I have misloaded the - * XXX command bytes into the fifo? Actually, - * XXX I most likely missed a phase, and therefore - * XXX went into never never land and didn't even - * XXX know it. That was the old driver though. - * XXX What is even more peculiar is that the ESP - * XXX showed the proper function complete and - * XXX bus service bits in the interrupt register. - */ - - case ESP_STEP_FINI4: - case ESP_STEP_FINI5: - case ESP_STEP_FINI6: - case ESP_STEP_FINI7: - /* Account for the identify message */ - if (SCptr->SCp.phase == in_slct_norm) - cmd_bytes_sent -= 1; - }; - - if (esp->erev != fashme) - esp_cmd(esp, ESP_CMD_NULL); - - /* Be careful, we could really get fucked during synchronous - * data transfers if we try to flush the fifo now. - */ - if ((esp->erev != fashme) && /* not a Happy Meal and... */ - !fcnt && /* Fifo is empty and... */ - /* either we are not doing synchronous transfers or... */ - (!esp_dev->sync_max_offset || - /* We are not going into data in phase. */ - ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP))) - esp_cmd(esp, ESP_CMD_FLUSH); /* flush is safe */ - - /* See how far we got if this is not a slow command. */ - if (!esp->esp_slowcmd) { - if (cmd_bytes_sent < 0) - cmd_bytes_sent = 0; - if (cmd_bytes_sent != SCptr->cmd_len) { - /* Crapola, mark it as a slowcmd - * so that we have some chance of - * keeping the command alive with - * good luck. - * - * XXX Actually, if we didn't send it all - * XXX this means either we didn't set things - * XXX up properly (driver bug) or the target - * XXX or the ESP detected parity on one of - * XXX the command bytes. This makes much - * XXX more sense, and therefore this code - * XXX should be changed to send out a - * XXX parity error message or if the status - * XXX register shows no parity error then - * XXX just expect the target to bring the - * XXX bus into message in phase so that it - * XXX can send us the parity error message. - * XXX SCSI sucks... - */ - esp->esp_slowcmd = 1; - esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]); - esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent); - } - } - - /* Now figure out where we went. */ - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } - - /* Did the target even make it? */ - if (esp->ireg == ESP_INTR_DC) { - /* wheee... nobody there or they didn't like - * what we told it to do, clean up. - */ - - /* If anyone is off the bus, but working on - * a command in the background for us, tell - * the ESP to listen for them. - */ - if (esp->disconnected_SC) - esp_cmd(esp, ESP_CMD_ESEL); - - if (((1<device->id) & esp->targets_present) && - esp->seqreg != 0 && - (esp->cur_msgout[0] == EXTENDED_MESSAGE) && - (SCptr->SCp.phase == in_slct_msg || - SCptr->SCp.phase == in_slct_stop)) { - /* shit */ - esp->snip = 0; - ESPLOG(("esp%d: Failed synchronous negotiation for target %d " - "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun)); - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp_dev->sync = 1; /* so we don't negotiate again */ - - /* Run the command again, this time though we - * won't try to negotiate for synchronous transfers. - * - * XXX I'd like to do something like send an - * XXX INITIATOR_ERROR or ABORT message to the - * XXX target to tell it, "Sorry I confused you, - * XXX please come back and I will be nicer next - * XXX time". But that requires having the target - * XXX on the bus, and it has dropped BSY on us. - */ - esp->current_SC = NULL; - esp_advance_phase(SCptr, not_issued); - prepend_SC(&esp->issue_SC, SCptr); - esp_exec_cmd(esp); - return do_intr_end; - } - - /* Ok, this is normal, this is what we see during boot - * or whenever when we are scanning the bus for targets. - * But first make sure that is really what is happening. - */ - if (((1<device->id) & esp->targets_present)) { - ESPLOG(("esp%d: Warning, live target %d not responding to " - "selection.\n", esp->esp_id, SCptr->device->id)); - - /* This _CAN_ happen. The SCSI standard states that - * the target is to _not_ respond to selection if - * _it_ detects bad parity on the bus for any reason. - * Therefore, we assume that if we've talked successfully - * to this target before, bad parity is the problem. - */ - esp_done(esp, (DID_PARITY << 16)); - } else { - /* Else, there really isn't anyone there. */ - ESPMISC(("esp: selection failure, maybe nobody there?\n")); - ESPMISC(("esp: target %d lun %d\n", - SCptr->device->id, SCptr->device->lun)); - esp_done(esp, (DID_BAD_TARGET << 16)); - } - return do_intr_end; - } - - ESPLOG(("esp%d: Selection failure.\n", esp->esp_id)); - printk("esp%d: Currently -- ", esp->esp_id); - esp_print_ireg(esp->ireg); printk(" "); - esp_print_statreg(esp->sreg); printk(" "); - esp_print_seqreg(esp->seqreg); printk("\n"); - printk("esp%d: New -- ", esp->esp_id); - esp->sreg = sbus_readb(esp->eregs + ESP_STATUS); - esp->seqreg = sbus_readb(esp->eregs + ESP_SSTEP); - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); - esp_print_ireg(esp->ireg); printk(" "); - esp_print_statreg(esp->sreg); printk(" "); - esp_print_seqreg(esp->seqreg); printk("\n"); - ESPLOG(("esp%d: resetting bus\n", esp->esp_id)); - return do_reset_bus; /* ugh... */ -} - -/* Continue reading bytes for msgin phase. */ -static int esp_do_msgincont(struct esp *esp) -{ - if (esp->ireg & ESP_INTR_BSERV) { - /* in the right phase too? */ - if ((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) { - /* phew... */ - esp_cmd(esp, ESP_CMD_TI); - esp_advance_phase(esp->current_SC, in_msgindone); - return do_intr_end; - } - - /* We changed phase but ESP shows bus service, - * in this case it is most likely that we, the - * hacker who has been up for 20hrs straight - * staring at the screen, drowned in coffee - * smelling like retched cigarette ashes - * have miscoded something..... so, try to - * recover as best we can. - */ - ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id)); - } - esp_advance_phase(esp->current_SC, in_the_dark); - return do_phase_determine; -} - -static int check_singlebyte_msg(struct esp *esp) -{ - esp->prevmsgin = esp->cur_msgin[0]; - if (esp->cur_msgin[0] & 0x80) { - /* wheee... */ - ESPLOG(("esp%d: target sends identify amidst phases\n", - esp->esp_id)); - esp_advance_phase(esp->current_SC, in_the_dark); - return 0; - } else if (((esp->cur_msgin[0] & 0xf0) == 0x20) || - (esp->cur_msgin[0] == EXTENDED_MESSAGE)) { - esp->msgin_len = 2; - esp_advance_phase(esp->current_SC, in_msgincont); - return 0; - } - esp_advance_phase(esp->current_SC, in_the_dark); - switch (esp->cur_msgin[0]) { - default: - /* We don't want to hear about it. */ - ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id, - esp->cur_msgin[0])); - return MESSAGE_REJECT; - - case NOP: - ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id, - esp->current_SC->device->id)); - return 0; - - case RESTORE_POINTERS: - /* In this case we might also have to backup the - * "slow command" pointer. It is rare to get such - * a save/restore pointer sequence so early in the - * bus transition sequences, but cover it. - */ - if (esp->esp_slowcmd) { - esp->esp_scmdleft = esp->current_SC->cmd_len; - esp->esp_scmdp = &esp->current_SC->cmnd[0]; - } - esp_restore_pointers(esp, esp->current_SC); - return 0; - - case SAVE_POINTERS: - esp_save_pointers(esp, esp->current_SC); - return 0; - - case COMMAND_COMPLETE: - case DISCONNECT: - /* Freeing the bus, let it go. */ - esp->current_SC->SCp.phase = in_freeing; - return 0; - - case MESSAGE_REJECT: - ESPMISC(("msg reject, ")); - if (esp->prevmsgout == EXTENDED_MESSAGE) { - struct esp_device *esp_dev = esp->current_SC->device->hostdata; - - /* Doesn't look like this target can - * do synchronous or WIDE transfers. - */ - ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n")); - esp_dev->sync = 1; - esp_dev->wide = 1; - esp_dev->sync_min_period = 0; - esp_dev->sync_max_offset = 0; - return 0; - } else { - ESPMISC(("not sync nego, sending ABORT\n")); - return ABORT; - } - }; -} - -/* Target negotiates for synchronous transfers before we do, this - * is legal although very strange. What is even funnier is that - * the SCSI2 standard specifically recommends against targets doing - * this because so many initiators cannot cope with this occurring. - */ -static int target_with_ants_in_pants(struct esp *esp, - struct scsi_cmnd *SCptr, - struct esp_device *esp_dev) -{ - if (esp_dev->sync || SCptr->device->borken) { - /* sorry, no can do */ - ESPSDTR(("forcing to async, ")); - build_sync_nego_msg(esp, 0, 0); - esp_dev->sync = 1; - esp->snip = 1; - ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id)); - esp_advance_phase(SCptr, in_the_dark); - return EXTENDED_MESSAGE; - } - - /* Ok, we'll check them out... */ - return 0; -} - -static void sync_report(struct esp *esp) -{ - int msg3, msg4; - char *type; - - msg3 = esp->cur_msgin[3]; - msg4 = esp->cur_msgin[4]; - if (msg4) { - int hz = 1000000000 / (msg3 * 4); - int integer = hz / 1000000; - int fraction = (hz - (integer * 1000000)) / 10000; - if ((esp->erev == fashme) && - (esp->config3[esp->current_SC->device->id] & ESP_CONFIG3_EWIDE)) { - type = "FAST-WIDE"; - integer <<= 1; - fraction <<= 1; - } else if ((msg3 * 4) < 200) { - type = "FAST"; - } else { - type = "synchronous"; - } - - /* Do not transform this back into one big printk - * again, it triggers a bug in our sparc64-gcc272 - * sibling call optimization. -DaveM - */ - ESPLOG((KERN_INFO "esp%d: target %d ", - esp->esp_id, esp->current_SC->device->id)); - ESPLOG(("[period %dns offset %d %d.%02dMHz ", - (int) msg3 * 4, (int) msg4, - integer, fraction)); - ESPLOG(("%s SCSI%s]\n", type, - (((msg3 * 4) < 200) ? "-II" : ""))); - } else { - ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n", - esp->esp_id, esp->current_SC->device->id)); - } -} - -static int check_multibyte_msg(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - u8 regval = 0; - int message_out = 0; - - ESPSDTR(("chk multibyte msg: ")); - if (esp->cur_msgin[2] == EXTENDED_SDTR) { - int period = esp->cur_msgin[3]; - int offset = esp->cur_msgin[4]; - - ESPSDTR(("is sync nego response, ")); - if (!esp->snip) { - int rval; - - /* Target negotiates first! */ - ESPSDTR(("target jumps the gun, ")); - message_out = EXTENDED_MESSAGE; /* we must respond */ - rval = target_with_ants_in_pants(esp, SCptr, esp_dev); - if (rval) - return rval; - } - - ESPSDTR(("examining sdtr, ")); - - /* Offset cannot be larger than ESP fifo size. */ - if (offset > 15) { - ESPSDTR(("offset too big %2x, ", offset)); - offset = 15; - ESPSDTR(("sending back new offset\n")); - build_sync_nego_msg(esp, period, offset); - return EXTENDED_MESSAGE; - } - - if (offset && period > esp->max_period) { - /* Yeee, async for this slow device. */ - ESPSDTR(("period too long %2x, ", period)); - build_sync_nego_msg(esp, 0, 0); - ESPSDTR(("hoping for msgout\n")); - esp_advance_phase(esp->current_SC, in_the_dark); - return EXTENDED_MESSAGE; - } else if (offset && period < esp->min_period) { - ESPSDTR(("period too short %2x, ", period)); - period = esp->min_period; - if (esp->erev > esp236) - regval = 4; - else - regval = 5; - } else if (offset) { - int tmp; - - ESPSDTR(("period is ok, ")); - tmp = esp->ccycle / 1000; - regval = (((period << 2) + tmp - 1) / tmp); - if (regval && ((esp->erev == fas100a || - esp->erev == fas236 || - esp->erev == fashme))) { - if (period >= 50) - regval--; - } - } - - if (offset) { - u8 bit; - - esp_dev->sync_min_period = (regval & 0x1f); - esp_dev->sync_max_offset = (offset | esp->radelay); - if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) { - if ((esp->erev == fas100a) || (esp->erev == fashme)) - bit = ESP_CONFIG3_FAST; - else - bit = ESP_CONFIG3_FSCSI; - if (period < 50) { - /* On FAS366, if using fast-20 synchronous transfers - * we need to make sure the REQ/ACK assert/deassert - * control bits are clear. - */ - if (esp->erev == fashme) - esp_dev->sync_max_offset &= ~esp->radelay; - esp->config3[SCptr->device->id] |= bit; - } else { - esp->config3[SCptr->device->id] &= ~bit; - } - esp->prev_cfg3 = esp->config3[SCptr->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - esp->prev_soff = esp_dev->sync_max_offset; - esp->prev_stp = esp_dev->sync_min_period; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n", - esp_dev->sync_max_offset, - esp_dev->sync_min_period, - esp->config3[SCptr->device->id])); - - esp->snip = 0; - } else if (esp_dev->sync_max_offset) { - u8 bit; - - /* back to async mode */ - ESPSDTR(("unaccaptable sync nego, forcing async\n")); - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp->prev_soff = 0; - esp->prev_stp = 0; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) { - if ((esp->erev == fas100a) || (esp->erev == fashme)) - bit = ESP_CONFIG3_FAST; - else - bit = ESP_CONFIG3_FSCSI; - esp->config3[SCptr->device->id] &= ~bit; - esp->prev_cfg3 = esp->config3[SCptr->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - } - - sync_report(esp); - - ESPSDTR(("chk multibyte msg: sync is known, ")); - esp_dev->sync = 1; - - if (message_out) { - ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n", - esp->esp_id)); - build_sync_nego_msg(esp, period, offset); - esp_advance_phase(SCptr, in_the_dark); - return EXTENDED_MESSAGE; - } - - ESPSDTR(("returning zero\n")); - esp_advance_phase(SCptr, in_the_dark); /* ...or else! */ - return 0; - } else if (esp->cur_msgin[2] == EXTENDED_WDTR) { - int size = 8 << esp->cur_msgin[3]; - - esp->wnip = 0; - if (esp->erev != fashme) { - ESPLOG(("esp%d: AIEEE wide msg received and not HME.\n", - esp->esp_id)); - message_out = MESSAGE_REJECT; - } else if (size > 16) { - ESPLOG(("esp%d: AIEEE wide transfer for %d size " - "not supported.\n", esp->esp_id, size)); - message_out = MESSAGE_REJECT; - } else { - /* Things look good; let's see what we got. */ - if (size == 16) { - /* Set config 3 register for this target. */ - esp->config3[SCptr->device->id] |= ESP_CONFIG3_EWIDE; - } else { - /* Just make sure it was one byte sized. */ - if (size != 8) { - ESPLOG(("esp%d: Aieee, wide nego of %d size.\n", - esp->esp_id, size)); - message_out = MESSAGE_REJECT; - goto finish; - } - /* Pure paranoia. */ - esp->config3[SCptr->device->id] &= ~(ESP_CONFIG3_EWIDE); - } - esp->prev_cfg3 = esp->config3[SCptr->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - - /* Regardless, next try for sync transfers. */ - build_sync_nego_msg(esp, esp->sync_defp, 15); - esp_dev->sync = 1; - esp->snip = 1; - message_out = EXTENDED_MESSAGE; - } - } else if (esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) { - ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id)); - message_out = MESSAGE_REJECT; - } -finish: - esp_advance_phase(SCptr, in_the_dark); - return message_out; -} - -static int esp_do_msgindone(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - int message_out = 0, it = 0, rval; - - rval = skipahead1(esp, SCptr, in_msgin, in_msgindone); - if (rval) - return rval; - if (SCptr->SCp.sent_command != in_status) { - if (!(esp->ireg & ESP_INTR_DC)) { - if (esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) { - message_out = MSG_PARITY_ERROR; - esp_cmd(esp, ESP_CMD_FLUSH); - } else if (esp->erev != fashme && - (it = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES)) != 1) { - /* We certainly dropped the ball somewhere. */ - message_out = INITIATOR_ERROR; - esp_cmd(esp, ESP_CMD_FLUSH); - } else if (!esp->msgin_len) { - if (esp->erev == fashme) - it = esp->hme_fifo_workaround_buffer[0]; - else - it = sbus_readb(esp->eregs + ESP_FDATA); - esp_advance_phase(SCptr, in_msgincont); - } else { - /* it is ok and we want it */ - if (esp->erev == fashme) - it = esp->cur_msgin[esp->msgin_ctr] = - esp->hme_fifo_workaround_buffer[0]; - else - it = esp->cur_msgin[esp->msgin_ctr] = - sbus_readb(esp->eregs + ESP_FDATA); - esp->msgin_ctr++; - } - } else { - esp_advance_phase(SCptr, in_the_dark); - return do_work_bus; - } - } else { - it = esp->cur_msgin[0]; - } - if (!message_out && esp->msgin_len) { - if (esp->msgin_ctr < esp->msgin_len) { - esp_advance_phase(SCptr, in_msgincont); - } else if (esp->msgin_len == 1) { - message_out = check_singlebyte_msg(esp); - } else if (esp->msgin_len == 2) { - if (esp->cur_msgin[0] == EXTENDED_MESSAGE) { - if ((it + 2) >= 15) { - message_out = MESSAGE_REJECT; - } else { - esp->msgin_len = (it + 2); - esp_advance_phase(SCptr, in_msgincont); - } - } else { - message_out = MESSAGE_REJECT; /* foo on you */ - } - } else { - message_out = check_multibyte_msg(esp); - } - } - if (message_out < 0) { - return -message_out; - } else if (message_out) { - if (((message_out != 1) && - ((message_out < 0x20) || (message_out & 0x80)))) - esp->msgout_len = 1; - esp->cur_msgout[0] = message_out; - esp_cmd(esp, ESP_CMD_SATN); - esp_advance_phase(SCptr, in_the_dark); - esp->msgin_len = 0; - } - esp->sreg = sbus_readb(esp->eregs + ESP_STATUS); - esp->sreg &= ~(ESP_STAT_INTR); - if ((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD)) - esp_cmd(esp, ESP_CMD_MOK); - if ((SCptr->SCp.sent_command == in_msgindone) && - (SCptr->SCp.phase == in_freeing)) - return esp_do_freebus(esp); - return do_intr_end; -} - -static int esp_do_cmdbegin(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - - esp_advance_phase(SCptr, in_cmdend); - if (esp->erev == fashme) { - u32 tmp = sbus_readl(esp->dregs + DMA_CSR); - int i; - - for (i = 0; i < esp->esp_scmdleft; i++) - esp->esp_command[i] = *esp->esp_scmdp++; - esp->esp_scmdleft = 0; - esp_cmd(esp, ESP_CMD_FLUSH); - esp_setcount(esp->eregs, i, 1); - esp_cmd(esp, (ESP_CMD_DMA | ESP_CMD_TI)); - tmp |= (DMA_SCSI_DISAB | DMA_ENABLE); - tmp &= ~(DMA_ST_WRITE); - sbus_writel(i, esp->dregs + DMA_COUNT); - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - sbus_writel(tmp, esp->dregs + DMA_CSR); - } else { - u8 tmp; - - esp_cmd(esp, ESP_CMD_FLUSH); - tmp = *esp->esp_scmdp++; - esp->esp_scmdleft--; - sbus_writeb(tmp, esp->eregs + ESP_FDATA); - esp_cmd(esp, ESP_CMD_TI); - } - return do_intr_end; -} - -static int esp_do_cmddone(struct esp *esp) -{ - if (esp->erev == fashme) - dma_invalidate(esp); - else - esp_cmd(esp, ESP_CMD_NULL); - - if (esp->ireg & ESP_INTR_BSERV) { - esp_advance_phase(esp->current_SC, in_the_dark); - return esp_do_phase_determine(esp); - } - - ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n", - esp->esp_id)); - return do_reset_bus; -} - -static int esp_do_msgout(struct esp *esp) -{ - esp_cmd(esp, ESP_CMD_FLUSH); - switch (esp->msgout_len) { - case 1: - if (esp->erev == fashme) - hme_fifo_push(esp, &esp->cur_msgout[0], 1); - else - sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA); - - esp_cmd(esp, ESP_CMD_TI); - break; - - case 2: - esp->esp_command[0] = esp->cur_msgout[0]; - esp->esp_command[1] = esp->cur_msgout[1]; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 2); - esp_cmd(esp, ESP_CMD_TI); - } else { - dma_setup(esp, esp->esp_command_dvma, 2, 0); - esp_setcount(esp->eregs, 2, 0); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - break; - - case 4: - esp->esp_command[0] = esp->cur_msgout[0]; - esp->esp_command[1] = esp->cur_msgout[1]; - esp->esp_command[2] = esp->cur_msgout[2]; - esp->esp_command[3] = esp->cur_msgout[3]; - esp->snip = 1; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 4); - esp_cmd(esp, ESP_CMD_TI); - } else { - dma_setup(esp, esp->esp_command_dvma, 4, 0); - esp_setcount(esp->eregs, 4, 0); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - break; - - case 5: - esp->esp_command[0] = esp->cur_msgout[0]; - esp->esp_command[1] = esp->cur_msgout[1]; - esp->esp_command[2] = esp->cur_msgout[2]; - esp->esp_command[3] = esp->cur_msgout[3]; - esp->esp_command[4] = esp->cur_msgout[4]; - esp->snip = 1; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 5); - esp_cmd(esp, ESP_CMD_TI); - } else { - dma_setup(esp, esp->esp_command_dvma, 5, 0); - esp_setcount(esp->eregs, 5, 0); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - break; - - default: - /* whoops */ - ESPMISC(("bogus msgout sending NOP\n")); - esp->cur_msgout[0] = NOP; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 1); - } else { - sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA); - } - - esp->msgout_len = 1; - esp_cmd(esp, ESP_CMD_TI); - break; - }; - - esp_advance_phase(esp->current_SC, in_msgoutdone); - return do_intr_end; -} - -static int esp_do_msgoutdone(struct esp *esp) -{ - if (esp->msgout_len > 1) { - /* XXX HME/FAS ATN deassert workaround required, - * XXX no DMA flushing, only possible ESP_CMD_FLUSH - * XXX to kill the fifo. - */ - if (esp->erev != fashme) { - u32 tmp; - - while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ) - udelay(1); - tmp &= ~DMA_ENABLE; - sbus_writel(tmp, esp->dregs + DMA_CSR); - dma_invalidate(esp); - } else { - esp_cmd(esp, ESP_CMD_FLUSH); - } - } - if (!(esp->ireg & ESP_INTR_DC)) { - if (esp->erev != fashme) - esp_cmd(esp, ESP_CMD_NULL); - switch (esp->sreg & ESP_STAT_PMASK) { - case ESP_MOP: - /* whoops, parity error */ - ESPLOG(("esp%d: still in msgout, parity error assumed\n", - esp->esp_id)); - if (esp->msgout_len > 1) - esp_cmd(esp, ESP_CMD_SATN); - esp_advance_phase(esp->current_SC, in_msgout); - return do_work_bus; - - case ESP_DIP: - break; - - default: - /* Happy Meal fifo is touchy... */ - if ((esp->erev != fashme) && - !fcount(esp) && - !(((struct esp_device *)esp->current_SC->device->hostdata)->sync_max_offset)) - esp_cmd(esp, ESP_CMD_FLUSH); - break; - - }; - } else { - ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id)); - return do_reset_bus; - } - - /* If we sent out a synchronous negotiation message, update - * our state. - */ - if (esp->cur_msgout[2] == EXTENDED_MESSAGE && - esp->cur_msgout[4] == EXTENDED_SDTR) { - esp->snip = 1; /* anal retentiveness... */ - } - - esp->prevmsgout = esp->cur_msgout[0]; - esp->msgout_len = 0; - esp_advance_phase(esp->current_SC, in_the_dark); - return esp_do_phase_determine(esp); -} - -static int esp_bus_unexpected(struct esp *esp) -{ - ESPLOG(("esp%d: command in weird state %2x\n", - esp->esp_id, esp->current_SC->SCp.phase)); - return do_reset_bus; -} - -static espfunc_t bus_vector[] = { - esp_do_data_finale, - esp_do_data_finale, - esp_bus_unexpected, - esp_do_msgin, - esp_do_msgincont, - esp_do_msgindone, - esp_do_msgout, - esp_do_msgoutdone, - esp_do_cmdbegin, - esp_do_cmddone, - esp_do_status, - esp_do_freebus, - esp_do_phase_determine, - esp_bus_unexpected, - esp_bus_unexpected, - esp_bus_unexpected, -}; - -/* This is the second tier in our dual-level SCSI state machine. */ -static int esp_work_bus(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - unsigned int phase; - - ESPBUS(("esp_work_bus: ")); - if (!SCptr) { - ESPBUS(("reconnect\n")); - return esp_do_reconnect(esp); - } - phase = SCptr->SCp.phase; - if ((phase & 0xf0) == in_phases_mask) - return bus_vector[(phase & 0x0f)](esp); - else if ((phase & 0xf0) == in_slct_mask) - return esp_select_complete(esp); - else - return esp_bus_unexpected(esp); -} - -static espfunc_t isvc_vector[] = { - NULL, - esp_do_phase_determine, - esp_do_resetbus, - esp_finish_reset, - esp_work_bus -}; - -/* Main interrupt handler for an esp adapter. */ -static void esp_handle(struct esp *esp) -{ - struct scsi_cmnd *SCptr; - int what_next = do_intr_end; - - SCptr = esp->current_SC; - - /* Check for errors. */ - esp->sreg = sbus_readb(esp->eregs + ESP_STATUS); - esp->sreg &= (~ESP_STAT_INTR); - if (esp->erev == fashme) { - esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2); - esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS); - } - - if (esp->sreg & (ESP_STAT_SPAM)) { - /* Gross error, could be due to one of: - * - * - top of fifo overwritten, could be because - * we tried to do a synchronous transfer with - * an offset greater than ESP fifo size - * - * - top of command register overwritten - * - * - DMA setup to go in one direction, SCSI - * bus points in the other, whoops - * - * - weird phase change during asynchronous - * data phase while we are initiator - */ - ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg)); - - /* If a command is live on the bus we cannot safely - * reset the bus, so we'll just let the pieces fall - * where they may. Here we are hoping that the - * target will be able to cleanly go away soon - * so we can safely reset things. - */ - if (!SCptr) { - ESPLOG(("esp%d: No current cmd during gross error, " - "resetting bus\n", esp->esp_id)); - what_next = do_reset_bus; - goto state_machine; - } - } - - if (sbus_readl(esp->dregs + DMA_CSR) & DMA_HNDL_ERROR) { - /* A DMA gate array error. Here we must - * be seeing one of two things. Either the - * virtual to physical address translation - * on the SBUS could not occur, else the - * translation it did get pointed to a bogus - * page. Ho hum... - */ - ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id, - sbus_readl(esp->dregs + DMA_CSR))); - - /* DMA gate array itself must be reset to clear the - * error condition. - */ - esp_reset_dma(esp); - - what_next = do_reset_bus; - goto state_machine; - } - - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); /* Unlatch intr reg */ - - if (esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - - ESPHME(("sreg2=%02x,", esp->sreg2)); - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - */ - if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || - (esp->sreg2 & ESP_STAT2_F1BYTE)) { - ESPHME(("fifo_workaround]")); - hme_fifo_read(esp); - } else { - ESPHME(("no_fifo_workaround]")); - } - } - - /* No current cmd is only valid at this point when there are - * commands off the bus or we are trying a reset. - */ - if (!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) { - /* Panic is safe, since current_SC is null. */ - ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id)); - panic("esp_handle: current_SC == penguin within interrupt!"); - } - - if (esp->ireg & (ESP_INTR_IC)) { - /* Illegal command fed to ESP. Outside of obvious - * software bugs that could cause this, there is - * a condition with esp100 where we can confuse the - * ESP into an erroneous illegal command interrupt - * because it does not scrape the FIFO properly - * for reselection. See esp100_reconnect_hwbug() - * to see how we try very hard to avoid this. - */ - ESPLOG(("esp%d: invalid command\n", esp->esp_id)); - - esp_dump_state(esp); - - if (SCptr != NULL) { - /* Devices with very buggy firmware can drop BSY - * during a scatter list interrupt when using sync - * mode transfers. We continue the transfer as - * expected, the target drops the bus, the ESP - * gets confused, and we get a illegal command - * interrupt because the bus is in the disconnected - * state now and ESP_CMD_TI is only allowed when - * a nexus is alive on the bus. - */ - ESPLOG(("esp%d: Forcing async and disabling disconnect for " - "target %d\n", esp->esp_id, SCptr->device->id)); - SCptr->device->borken = 1; /* foo on you */ - } - - what_next = do_reset_bus; - } else if (!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) { - if (SCptr) { - unsigned int phase = SCptr->SCp.phase; - - if (phase & in_phases_mask) { - what_next = esp_work_bus(esp); - } else if (phase & in_slct_mask) { - what_next = esp_select_complete(esp); - } else { - ESPLOG(("esp%d: interrupt for no good reason...\n", - esp->esp_id)); - what_next = do_intr_end; - } - } else { - ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n", - esp->esp_id)); - what_next = do_reset_bus; - } - } else if (esp->ireg & ESP_INTR_SR) { - ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id)); - what_next = do_reset_complete; - } else if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) { - ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n", - esp->esp_id)); - what_next = do_reset_bus; - } else if (esp->ireg & ESP_INTR_RSEL) { - if (SCptr == NULL) { - /* This is ok. */ - what_next = esp_do_reconnect(esp); - } else if (SCptr->SCp.phase & in_slct_mask) { - /* Only selection code knows how to clean - * up properly. - */ - ESPDISC(("Reselected during selection attempt\n")); - what_next = esp_select_complete(esp); - } else { - ESPLOG(("esp%d: Reselected while bus is busy\n", - esp->esp_id)); - what_next = do_reset_bus; - } - } - - /* This is tier-one in our dual level SCSI state machine. */ -state_machine: - while (what_next != do_intr_end) { - if (what_next >= do_phase_determine && - what_next < do_intr_end) { - what_next = isvc_vector[what_next](esp); - } else { - /* state is completely lost ;-( */ - ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n", - esp->esp_id)); - what_next = do_reset_bus; - } - } -} - -/* Service only the ESP described by dev_id. */ -static irqreturn_t esp_intr(int irq, void *dev_id) -{ - struct esp *esp = dev_id; - unsigned long flags; - - spin_lock_irqsave(esp->ehost->host_lock, flags); - if (ESP_IRQ_P(esp->dregs)) { - ESP_INTSOFF(esp->dregs); - - ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id)); - esp_handle(esp); - ESPIRQ((")")); - - ESP_INTSON(esp->dregs); - } - spin_unlock_irqrestore(esp->ehost->host_lock, flags); - - return IRQ_HANDLED; -} - -static int esp_slave_alloc(struct scsi_device *SDptr) -{ - struct esp_device *esp_dev = - kmalloc(sizeof(struct esp_device), GFP_ATOMIC); - - if (!esp_dev) - return -ENOMEM; - memset(esp_dev, 0, sizeof(struct esp_device)); - SDptr->hostdata = esp_dev; - return 0; -} - -static void esp_slave_destroy(struct scsi_device *SDptr) -{ - struct esp *esp = (struct esp *) SDptr->host->hostdata; - - esp->targets_present &= ~(1 << SDptr->id); - kfree(SDptr->hostdata); - SDptr->hostdata = NULL; -} - -static struct scsi_host_template esp_template = { - .module = THIS_MODULE, - .name = "esp", - .info = esp_info, - .slave_alloc = esp_slave_alloc, - .slave_destroy = esp_slave_destroy, - .queuecommand = esp_queue, - .eh_abort_handler = esp_abort, - .eh_bus_reset_handler = esp_reset, - .can_queue = 7, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = ENABLE_CLUSTERING, - .proc_name = "esp", - .proc_info = esp_proc_info, -}; - -#ifndef CONFIG_SUN4 -static struct of_device_id esp_match[] = { - { - .name = "SUNW,esp", - .data = &esp_template, - }, - { - .name = "SUNW,fas", - .data = &esp_template, - }, - { - .name = "esp", - .data = &esp_template, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, esp_match); - -static struct of_platform_driver esp_sbus_driver = { - .name = "esp", - .match_table = esp_match, - .probe = esp_sbus_probe, - .remove = __devexit_p(esp_sbus_remove), -}; -#endif - -static int __init esp_init(void) -{ -#ifdef CONFIG_SUN4 - return esp_sun4_probe(&esp_template); -#else - return of_register_driver(&esp_sbus_driver, &sbus_bus_type); -#endif -} - -static void __exit esp_exit(void) -{ -#ifdef CONFIG_SUN4 - esp_sun4_remove(); -#else - of_unregister_driver(&esp_sbus_driver); -#endif -} - -MODULE_DESCRIPTION("ESP Sun SCSI driver"); -MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - -module_init(esp_init); -module_exit(esp_exit); diff --git a/drivers/scsi/esp.h b/drivers/scsi/esp.h deleted file mode 100644 index a98cda9..0000000 --- a/drivers/scsi/esp.h +++ /dev/null @@ -1,406 +0,0 @@ -/* $Id: esp.h,v 1.29 2001/12/11 04:55:47 davem Exp $ - * esp.h: Defines and structures for the Sparc ESP (Enhanced SCSI - * Processor) driver under Linux. - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ - -#ifndef _SPARC_ESP_H -#define _SPARC_ESP_H - -/* For dvma controller register definitions. */ -#include - -/* The ESP SCSI controllers have their register sets in three - * "classes": - * - * 1) Registers which are both read and write. - * 2) Registers which are read only. - * 3) Registers which are write only. - * - * Yet, they all live within the same IO space. - */ - -/* All the ESP registers are one byte each and are accessed longwords - * apart with a big-endian ordering to the bytes. - */ - /* Access Description Offset */ -#define ESP_TCLOW 0x00UL /* rw Low bits of the transfer count 0x00 */ -#define ESP_TCMED 0x04UL /* rw Mid bits of the transfer count 0x04 */ -#define ESP_FDATA 0x08UL /* rw FIFO data bits 0x08 */ -#define ESP_CMD 0x0cUL /* rw SCSI command bits 0x0c */ -#define ESP_STATUS 0x10UL /* ro ESP status register 0x10 */ -#define ESP_BUSID ESP_STATUS /* wo Bus ID for select/reselect 0x10 */ -#define ESP_INTRPT 0x14UL /* ro Kind of interrupt 0x14 */ -#define ESP_TIMEO ESP_INTRPT /* wo Timeout value for select/resel 0x14 */ -#define ESP_SSTEP 0x18UL /* ro Sequence step register 0x18 */ -#define ESP_STP ESP_SSTEP /* wo Transfer period per sync 0x18 */ -#define ESP_FFLAGS 0x1cUL /* ro Bits of current FIFO info 0x1c */ -#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */ -#define ESP_CFG1 0x20UL /* rw First configuration register 0x20 */ -#define ESP_CFACT 0x24UL /* wo Clock conversion factor 0x24 */ -#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */ -#define ESP_CTEST 0x28UL /* wo Chip test register 0x28 */ -#define ESP_CFG2 0x2cUL /* rw Second configuration register 0x2c */ -#define ESP_CFG3 0x30UL /* rw Third configuration register 0x30 */ -#define ESP_TCHI 0x38UL /* rw High bits of transfer count 0x38 */ -#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */ -#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */ -#define ESP_FGRND 0x3cUL /* rw Data base for fifo 0x3c */ -#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */ -#define ESP_REG_SIZE 0x40UL - -/* Various revisions of the ESP board. */ -enum esp_rev { - esp100 = 0x00, /* NCR53C90 - very broken */ - esp100a = 0x01, /* NCR53C90A */ - esp236 = 0x02, - fas236 = 0x03, - fas100a = 0x04, - fast = 0x05, - fashme = 0x06, - espunknown = 0x07 -}; - -/* We allocate one of these for each scsi device and attach it to - * SDptr->hostdata for use in the driver - */ -struct esp_device { - unsigned char sync_min_period; - unsigned char sync_max_offset; - unsigned sync:1; - unsigned wide:1; - unsigned disconnect:1; -}; - -struct scsi_cmnd; - -/* We get one of these for each ESP probed. */ -struct esp { - void __iomem *eregs; /* ESP controller registers */ - void __iomem *dregs; /* DMA controller registers */ - struct sbus_dma *dma; /* DMA controller sw state */ - struct Scsi_Host *ehost; /* Backpointer to SCSI Host */ - struct sbus_dev *sdev; /* Pointer to SBus entry */ - - /* ESP Configuration Registers */ - u8 config1; /* Copy of the 1st config register */ - u8 config2; /* Copy of the 2nd config register */ - u8 config3[16]; /* Copy of the 3rd config register */ - - /* The current command we are sending to the ESP chip. This esp_command - * ptr needs to be mapped in DVMA area so we can send commands and read - * from the ESP fifo without burning precious CPU cycles. Programmed I/O - * sucks when we have the DVMA to do it for us. The ESP is stupid and will - * only send out 6, 10, and 12 byte SCSI commands, others we need to send - * one byte at a time. esp_slowcmd being set says that we are doing one - * of the command types ESP doesn't understand, esp_scmdp keeps track of - * which byte we are sending, esp_scmdleft says how many bytes to go. - */ - volatile u8 *esp_command; /* Location of command (CPU view) */ - __u32 esp_command_dvma;/* Location of command (DVMA view) */ - unsigned char esp_clen; /* Length of this command */ - unsigned char esp_slowcmd; - unsigned char *esp_scmdp; - unsigned char esp_scmdleft; - - /* The following are used to determine the cause of an IRQ. Upon every - * IRQ entry we synchronize these with the hardware registers. - */ - u8 ireg; /* Copy of ESP interrupt register */ - u8 sreg; /* Copy of ESP status register */ - u8 seqreg; /* Copy of ESP sequence step register */ - u8 sreg2; /* Copy of HME status2 register */ - - /* To save register writes to the ESP, which can be expensive, we - * keep track of the previous value that various registers had for - * the last target we connected to. If they are the same for the - * current target, we skip the register writes as they are not needed. - */ - u8 prev_soff, prev_stp; - u8 prev_cfg3, __cache_pad; - - /* We also keep a cache of the previous FAS/HME DMA CSR register value. */ - u32 prev_hme_dmacsr; - - /* The HME is the biggest piece of shit I have ever seen. */ - u8 hme_fifo_workaround_buffer[16 * 2]; - u8 hme_fifo_workaround_count; - - /* For each target we keep track of save/restore data - * pointer information. This needs to be updated majorly - * when we add support for tagged queueing. -DaveM - */ - struct esp_pointers { - char *saved_ptr; - struct scatterlist *saved_buffer; - int saved_this_residual; - int saved_buffers_residual; - } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/; - - /* Clock periods, frequencies, synchronization, etc. */ - unsigned int cfreq; /* Clock frequency in HZ */ - unsigned int cfact; /* Clock conversion factor */ - unsigned int raw_cfact; /* Raw copy from probing */ - unsigned int ccycle; /* One ESP clock cycle */ - unsigned int ctick; /* One ESP clock time */ - unsigned int radelay; /* FAST chip req/ack delay */ - unsigned int neg_defp; /* Default negotiation period */ - unsigned int sync_defp; /* Default sync transfer period */ - unsigned int max_period; /* longest our period can be */ - unsigned int min_period; /* shortest period we can withstand */ - - struct esp *next; /* Next ESP we probed or NULL */ - char prom_name[64]; /* Name of ESP device from prom */ - int prom_node; /* Prom node where ESP found */ - int esp_id; /* Unique per-ESP ID number */ - - /* For slow to medium speed input clock rates we shoot for 5mb/s, - * but for high input clock rates we try to do 10mb/s although I - * don't think a transfer can even run that fast with an ESP even - * with DMA2 scatter gather pipelining. - */ -#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */ -#define SYNC_DEFP_FAST 0x19 /* 10mb/s */ - - unsigned int snip; /* Sync. negotiation in progress */ - unsigned int wnip; /* WIDE negotiation in progress */ - unsigned int targets_present;/* targets spoken to before */ - - int current_transfer_size; /* Set at beginning of data dma */ - - u8 espcmdlog[32]; /* Log of current esp cmds sent. */ - u8 espcmdent; /* Current entry in esp cmd log. */ - - /* Misc. info about this ESP */ - enum esp_rev erev; /* ESP revision */ - int irq; /* SBus IRQ for this ESP */ - int scsi_id; /* Who am I as initiator? */ - int scsi_id_mask; /* Bitmask of 'me'. */ - int diff; /* Differential SCSI bus? */ - int bursts; /* Burst sizes our DVMA supports */ - - /* Our command queues, only one cmd lives in the current_SC queue. */ - struct scsi_cmnd *issue_SC; /* Commands to be issued */ - struct scsi_cmnd *current_SC; /* Who is currently working the bus */ - struct scsi_cmnd *disconnected_SC;/* Commands disconnected from the bus */ - - /* Message goo */ - u8 cur_msgout[16]; - u8 cur_msgin[16]; - u8 prevmsgout, prevmsgin; - u8 msgout_len, msgin_len; - u8 msgout_ctr, msgin_ctr; - - /* States that we cannot keep in the per cmd structure because they - * cannot be assosciated with any specific command. - */ - u8 resetting_bus; - wait_queue_head_t reset_queue; -}; - -/* Bitfield meanings for the above registers. */ - -/* ESP config reg 1, read-write, found on all ESP chips */ -#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */ -#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */ -#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */ -#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */ -#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */ -#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */ - -/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */ -#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */ -#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */ -#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */ -#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */ -#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */ -#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */ -#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */ -#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */ -#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */ -#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */ -#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */ -#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */ -#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */ - -/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */ -#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */ -#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */ -#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */ -#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */ -#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */ -#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */ -#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */ -#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */ -#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ -#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ -#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ -#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */ -#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ -#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ -#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ -#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ - -/* ESP command register read-write */ -/* Group 1 commands: These may be sent at any point in time to the ESP - * chip. None of them can generate interrupts 'cept - * the "SCSI bus reset" command if you have not disabled - * SCSI reset interrupts in the config1 ESP register. - */ -#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */ -#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */ -#define ESP_CMD_RC 0x02 /* Chip reset */ -#define ESP_CMD_RS 0x03 /* SCSI bus reset */ - -/* Group 2 commands: ESP must be an initiator and connected to a target - * for these commands to work. - */ -#define ESP_CMD_TI 0x10 /* Transfer Information */ -#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */ -#define ESP_CMD_MOK 0x12 /* Message okie-dokie */ -#define ESP_CMD_TPAD 0x18 /* Transfer Pad */ -#define ESP_CMD_SATN 0x1a /* Set ATN */ -#define ESP_CMD_RATN 0x1b /* De-assert ATN */ - -/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected - * to a target as the initiator for these commands to work. - */ -#define ESP_CMD_SMSG 0x20 /* Send message */ -#define ESP_CMD_SSTAT 0x21 /* Send status */ -#define ESP_CMD_SDATA 0x22 /* Send data */ -#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */ -#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */ -#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */ -#define ESP_CMD_DCNCT 0x27 /* Disconnect */ -#define ESP_CMD_RMSG 0x28 /* Receive Message */ -#define ESP_CMD_RCMD 0x29 /* Receive Command */ -#define ESP_CMD_RDATA 0x2a /* Receive Data */ -#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */ - -/* Group 4 commands: The ESP must be in the disconnected state and must - * not be connected to any targets as initiator for - * these commands to work. - */ -#define ESP_CMD_RSEL 0x40 /* Reselect */ -#define ESP_CMD_SEL 0x41 /* Select w/o ATN */ -#define ESP_CMD_SELA 0x42 /* Select w/ATN */ -#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */ -#define ESP_CMD_ESEL 0x44 /* Enable selection */ -#define ESP_CMD_DSEL 0x45 /* Disable selections */ -#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */ -#define ESP_CMD_RSEL3 0x47 /* Reselect3 */ - -/* This bit enables the ESP's DMA on the SBus */ -#define ESP_CMD_DMA 0x80 /* Do DMA? */ - - -/* ESP status register read-only */ -#define ESP_STAT_PIO 0x01 /* IO phase bit */ -#define ESP_STAT_PCD 0x02 /* CD phase bit */ -#define ESP_STAT_PMSG 0x04 /* MSG phase bit */ -#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */ -#define ESP_STAT_TDONE 0x08 /* Transfer Completed */ -#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */ -#define ESP_STAT_PERR 0x20 /* Parity error */ -#define ESP_STAT_SPAM 0x40 /* Real bad error */ -/* This indicates the 'interrupt pending' condition on esp236, it is a reserved - * bit on other revs of the ESP. - */ -#define ESP_STAT_INTR 0x80 /* Interrupt */ - -/* HME only: status 2 register */ -#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */ -#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */ -#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */ -#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */ -#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */ -#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */ -#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */ -#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */ - -/* The status register can be masked with ESP_STAT_PMASK and compared - * with the following values to determine the current phase the ESP - * (at least thinks it) is in. For our purposes we also add our own - * software 'done' bit for our phase management engine. - */ -#define ESP_DOP (0) /* Data Out */ -#define ESP_DIP (ESP_STAT_PIO) /* Data In */ -#define ESP_CMDP (ESP_STAT_PCD) /* Command */ -#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */ -#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */ -#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */ - -/* ESP interrupt register read-only */ -#define ESP_INTR_S 0x01 /* Select w/o ATN */ -#define ESP_INTR_SATN 0x02 /* Select w/ATN */ -#define ESP_INTR_RSEL 0x04 /* Reselected */ -#define ESP_INTR_FDONE 0x08 /* Function done */ -#define ESP_INTR_BSERV 0x10 /* Bus service */ -#define ESP_INTR_DC 0x20 /* Disconnect */ -#define ESP_INTR_IC 0x40 /* Illegal command given */ -#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */ - -/* Interrupt status macros */ -#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR)) -#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC)) -#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN)) -#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S)) -#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \ - (ESP_SELECT_WITHOUT_ATN_IRQ(esp))) -#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL)) - -/* ESP sequence step register read-only */ -#define ESP_STEP_VBITS 0x07 /* Valid bits */ -#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */ -#define ESP_STEP_SID 0x01 /* One msg byte sent */ -#define ESP_STEP_NCMD 0x02 /* Was not in command phase */ -#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd - * bytes to be lost - */ -#define ESP_STEP_FINI4 0x04 /* Command was sent ok */ - -/* Ho hum, some ESP's set the step register to this as well... */ -#define ESP_STEP_FINI5 0x05 -#define ESP_STEP_FINI6 0x06 -#define ESP_STEP_FINI7 0x07 - -/* ESP chip-test register read-write */ -#define ESP_TEST_TARG 0x01 /* Target test mode */ -#define ESP_TEST_INI 0x02 /* Initiator test mode */ -#define ESP_TEST_TS 0x04 /* Tristate test mode */ - -/* ESP unique ID register read-only, found on fas236+fas100a only */ -#define ESP_UID_F100A 0x00 /* ESP FAS100A */ -#define ESP_UID_F236 0x02 /* ESP FAS236 */ -#define ESP_UID_REV 0x07 /* ESP revision */ -#define ESP_UID_FAM 0xf8 /* ESP family */ - -/* ESP fifo flags register read-only */ -/* Note that the following implies a 16 byte FIFO on the ESP. */ -#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */ -#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */ -#define ESP_FF_SSTEP 0xe0 /* Sequence step */ - -/* ESP clock conversion factor register write-only */ -#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */ -#define ESP_CCF_NEVER 0x01 /* Set it to this and die */ -#define ESP_CCF_F2 0x02 /* 10MHz */ -#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */ -#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */ -#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */ -#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */ -#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */ - -/* HME only... */ -#define ESP_BUSID_RESELID 0x10 -#define ESP_BUSID_CTR32BIT 0x40 - -#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */ -#define ESP_TIMEO_CONST 8192 -#define ESP_NEG_DEFP(mhz, cfact) \ - ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) -#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000)) -#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000)) - -#endif /* !(_SPARC_ESP_H) */ diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c new file mode 100644 index 0000000..99ce033 --- /dev/null +++ b/drivers/scsi/esp_scsi.c @@ -0,0 +1,2711 @@ +/* esp_scsi.c: ESP SCSI driver. + * + * Copyright (C) 2007 David S. Miller (davem@davemloft.net) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "esp_scsi.h" + +#define DRV_MODULE_NAME "esp" +#define PFX DRV_MODULE_NAME ": " +#define DRV_VERSION "2.000" +#define DRV_MODULE_RELDATE "April 19, 2007" + +/* SCSI bus reset settle time in seconds. */ +static int esp_bus_reset_settle = 3; + +static u32 esp_debug; +#define ESP_DEBUG_INTR 0x00000001 +#define ESP_DEBUG_SCSICMD 0x00000002 +#define ESP_DEBUG_RESET 0x00000004 +#define ESP_DEBUG_MSGIN 0x00000008 +#define ESP_DEBUG_MSGOUT 0x00000010 +#define ESP_DEBUG_CMDDONE 0x00000020 +#define ESP_DEBUG_DISCONNECT 0x00000040 +#define ESP_DEBUG_DATASTART 0x00000080 +#define ESP_DEBUG_DATADONE 0x00000100 +#define ESP_DEBUG_RECONNECT 0x00000200 +#define ESP_DEBUG_AUTOSENSE 0x00000400 + +#define esp_log_intr(f, a...) \ +do { if (esp_debug & ESP_DEBUG_INTR) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_reset(f, a...) \ +do { if (esp_debug & ESP_DEBUG_RESET) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_msgin(f, a...) \ +do { if (esp_debug & ESP_DEBUG_MSGIN) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_msgout(f, a...) \ +do { if (esp_debug & ESP_DEBUG_MSGOUT) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_cmddone(f, a...) \ +do { if (esp_debug & ESP_DEBUG_CMDDONE) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_disconnect(f, a...) \ +do { if (esp_debug & ESP_DEBUG_DISCONNECT) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_datastart(f, a...) \ +do { if (esp_debug & ESP_DEBUG_DATASTART) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_datadone(f, a...) \ +do { if (esp_debug & ESP_DEBUG_DATADONE) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_reconnect(f, a...) \ +do { if (esp_debug & ESP_DEBUG_RECONNECT) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_autosense(f, a...) \ +do { if (esp_debug & ESP_DEBUG_AUTOSENSE) \ + printk(f, ## a); \ +} while (0) + +#define esp_read8(REG) esp->ops->esp_read8(esp, REG) +#define esp_write8(VAL,REG) esp->ops->esp_write8(esp, VAL, REG) + +static void esp_log_fill_regs(struct esp *esp, + struct esp_event_ent *p) +{ + p->sreg = esp->sreg; + p->seqreg = esp->seqreg; + p->sreg2 = esp->sreg2; + p->ireg = esp->ireg; + p->select_state = esp->select_state; + p->event = esp->event; +} + +void scsi_esp_cmd(struct esp *esp, u8 val) +{ + struct esp_event_ent *p; + int idx = esp->esp_event_cur; + + p = &esp->esp_event_log[idx]; + p->type = ESP_EVENT_TYPE_CMD; + p->val = val; + esp_log_fill_regs(esp, p); + + esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); + + esp_write8(val, ESP_CMD); +} +EXPORT_SYMBOL(scsi_esp_cmd); + +static void esp_event(struct esp *esp, u8 val) +{ + struct esp_event_ent *p; + int idx = esp->esp_event_cur; + + p = &esp->esp_event_log[idx]; + p->type = ESP_EVENT_TYPE_EVENT; + p->val = val; + esp_log_fill_regs(esp, p); + + esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); + + esp->event = val; +} + +static void esp_dump_cmd_log(struct esp *esp) +{ + int idx = esp->esp_event_cur; + int stop = idx; + + printk(KERN_INFO PFX "esp%d: Dumping command log\n", + esp->host->unique_id); + do { + struct esp_event_ent *p = &esp->esp_event_log[idx]; + + printk(KERN_INFO PFX "esp%d: ent[%d] %s ", + esp->host->unique_id, idx, + p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT"); + + printk("val[%02x] sreg[%02x] seqreg[%02x] " + "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n", + p->val, p->sreg, p->seqreg, + p->sreg2, p->ireg, p->select_state, p->event); + + idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); + } while (idx != stop); +} + +static void esp_flush_fifo(struct esp *esp) +{ + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + if (esp->rev == ESP236) { + int lim = 1000; + + while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: ESP_FF_BYTES " + "will not clear!\n", + esp->host->unique_id); + break; + } + udelay(1); + } + } +} + +static void hme_read_fifo(struct esp *esp) +{ + int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES; + int idx = 0; + + while (fcnt--) { + esp->fifo[idx++] = esp_read8(ESP_FDATA); + esp->fifo[idx++] = esp_read8(ESP_FDATA); + } + if (esp->sreg2 & ESP_STAT2_F1BYTE) { + esp_write8(0, ESP_FDATA); + esp->fifo[idx++] = esp_read8(ESP_FDATA); + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + } + esp->fifo_cnt = idx; +} + +static void esp_set_all_config3(struct esp *esp, u8 val) +{ + int i; + + for (i = 0; i < ESP_MAX_TARGET; i++) + esp->target[i].esp_config3 = val; +} + +/* Reset the ESP chip, _not_ the SCSI bus. */ +static void esp_reset_esp(struct esp *esp) +{ + u8 family_code, version; + + /* Now reset the ESP chip */ + scsi_esp_cmd(esp, ESP_CMD_RC); + scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); + scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); + + /* Reload the configuration registers */ + esp_write8(esp->cfact, ESP_CFACT); + + esp->prev_stp = 0; + esp_write8(esp->prev_stp, ESP_STP); + + esp->prev_soff = 0; + esp_write8(esp->prev_soff, ESP_SOFF); + + esp_write8(esp->neg_defp, ESP_TIMEO); + + /* This is the only point at which it is reliable to read + * the ID-code for a fast ESP chip variants. + */ + esp->max_period = ((35 * esp->ccycle) / 1000); + if (esp->rev == FAST) { + version = esp_read8(ESP_UID); + family_code = (version & 0xf8) >> 3; + if (family_code == 0x02) + esp->rev = FAS236; + else if (family_code == 0x0a) + esp->rev = FASHME; /* Version is usually '5'. */ + else + esp->rev = FAS100A; + esp->min_period = ((4 * esp->ccycle) / 1000); + } else { + esp->min_period = ((5 * esp->ccycle) / 1000); + } + esp->max_period = (esp->max_period + 3)>>2; + esp->min_period = (esp->min_period + 3)>>2; + + esp_write8(esp->config1, ESP_CFG1); + switch (esp->rev) { + case ESP100: + /* nothing to do */ + break; + + case ESP100A: + esp_write8(esp->config2, ESP_CFG2); + break; + + case ESP236: + /* Slow 236 */ + esp_write8(esp->config2, ESP_CFG2); + esp->prev_cfg3 = esp->target[0].esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + break; + + case FASHME: + esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); + /* fallthrough... */ + + case FAS236: + /* Fast 236 or HME */ + esp_write8(esp->config2, ESP_CFG2); + if (esp->rev == FASHME) { + u8 cfg3 = esp->target[0].esp_config3; + + cfg3 |= ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH; + if (esp->scsi_id >= 8) + cfg3 |= ESP_CONFIG3_IDBIT3; + esp_set_all_config3(esp, cfg3); + } else { + u32 cfg3 = esp->target[0].esp_config3; + + cfg3 |= ESP_CONFIG3_FCLK; + esp_set_all_config3(esp, cfg3); + } + esp->prev_cfg3 = esp->target[0].esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + if (esp->rev == FASHME) { + esp->radelay = 80; + } else { + if (esp->flags & ESP_FLAG_DIFFERENTIAL) + esp->radelay = 0; + else + esp->radelay = 96; + } + break; + + case FAS100A: + /* Fast 100a */ + esp_write8(esp->config2, ESP_CFG2); + esp_set_all_config3(esp, + (esp->target[0].esp_config3 | + ESP_CONFIG3_FCLOCK)); + esp->prev_cfg3 = esp->target[0].esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + esp->radelay = 32; + break; + + default: + break; + } + + /* Eat any bitrot in the chip */ + esp_read8(ESP_INTRPT); + udelay(100); +} + +static void esp_map_dma(struct esp *esp, struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + struct scatterlist *sg = cmd->request_buffer; + int dir = cmd->sc_data_direction; + int total, i; + + if (dir == DMA_NONE) + return; + + BUG_ON(cmd->use_sg == 0); + + spriv->u.num_sg = esp->ops->map_sg(esp, sg, + cmd->use_sg, dir); + spriv->cur_residue = sg_dma_len(sg); + spriv->cur_sg = sg; + + total = 0; + for (i = 0; i < spriv->u.num_sg; i++) + total += sg_dma_len(&sg[i]); + spriv->tot_residue = total; +} + +static dma_addr_t esp_cur_dma_addr(struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + return ent->sense_dma + + (ent->sense_ptr - cmd->sense_buffer); + } + + return sg_dma_address(p->cur_sg) + + (sg_dma_len(p->cur_sg) - + p->cur_residue); +} + +static unsigned int esp_cur_dma_len(struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + return SCSI_SENSE_BUFFERSIZE - + (ent->sense_ptr - cmd->sense_buffer); + } + return p->cur_residue; +} + +static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd, unsigned int len) +{ + struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->sense_ptr += len; + return; + } + + p->cur_residue -= len; + p->tot_residue -= len; + if (p->cur_residue < 0 || p->tot_residue < 0) { + printk(KERN_ERR PFX "esp%d: Data transfer overflow.\n", + esp->host->unique_id); + printk(KERN_ERR PFX "esp%d: cur_residue[%d] tot_residue[%d] " + "len[%u]\n", + esp->host->unique_id, + p->cur_residue, p->tot_residue, len); + p->cur_residue = 0; + p->tot_residue = 0; + } + if (!p->cur_residue && p->tot_residue) { + p->cur_sg++; + p->cur_residue = sg_dma_len(p->cur_sg); + } +} + +static void esp_unmap_dma(struct esp *esp, struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + int dir = cmd->sc_data_direction; + + if (dir == DMA_NONE) + return; + + esp->ops->unmap_sg(esp, cmd->request_buffer, + spriv->u.num_sg, dir); +} + +static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->saved_sense_ptr = ent->sense_ptr; + return; + } + ent->saved_cur_residue = spriv->cur_residue; + ent->saved_cur_sg = spriv->cur_sg; + ent->saved_tot_residue = spriv->tot_residue; +} + +static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->sense_ptr = ent->saved_sense_ptr; + return; + } + spriv->cur_residue = ent->saved_cur_residue; + spriv->cur_sg = ent->saved_cur_sg; + spriv->tot_residue = ent->saved_tot_residue; +} + +static void esp_check_command_len(struct esp *esp, struct scsi_cmnd *cmd) +{ + if (cmd->cmd_len == 6 || + cmd->cmd_len == 10 || + cmd->cmd_len == 12) { + esp->flags &= ~ESP_FLAG_DOING_SLOWCMD; + } else { + esp->flags |= ESP_FLAG_DOING_SLOWCMD; + } +} + +static void esp_write_tgt_config3(struct esp *esp, int tgt) +{ + if (esp->rev > ESP100A) { + u8 val = esp->target[tgt].esp_config3; + + if (val != esp->prev_cfg3) { + esp->prev_cfg3 = val; + esp_write8(val, ESP_CFG3); + } + } +} + +static void esp_write_tgt_sync(struct esp *esp, int tgt) +{ + u8 off = esp->target[tgt].esp_offset; + u8 per = esp->target[tgt].esp_period; + + if (off != esp->prev_soff) { + esp->prev_soff = off; + esp_write8(off, ESP_SOFF); + } + if (per != esp->prev_stp) { + esp->prev_stp = per; + esp_write8(per, ESP_STP); + } +} + +static u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len) +{ + if (esp->rev == FASHME) { + /* Arbitrary segment boundaries, 24-bit counts. */ + if (dma_len > (1U << 24)) + dma_len = (1U << 24); + } else { + u32 base, end; + + /* ESP chip limits other variants by 16-bits of transfer + * count. Actually on FAS100A and FAS236 we could get + * 24-bits of transfer count by enabling ESP_CONFIG2_FENAB + * in the ESP_CFG2 register but that causes other unwanted + * changes so we don't use it currently. + */ + if (dma_len > (1U << 16)) + dma_len = (1U << 16); + + /* All of the DMA variants hooked up to these chips + * cannot handle crossing a 24-bit address boundary. + */ + base = dma_addr & ((1U << 24) - 1U); + end = base + dma_len; + if (end > (1U << 24)) + end = (1U <<24); + dma_len = end - base; + } + return dma_len; +} + +static int esp_need_to_nego_wide(struct esp_target_data *tp) +{ + struct scsi_target *target = tp->starget; + + return spi_width(target) != tp->nego_goal_width; +} + +static int esp_need_to_nego_sync(struct esp_target_data *tp) +{ + struct scsi_target *target = tp->starget; + + /* When offset is zero, period is "don't care". */ + if (!spi_offset(target) && !tp->nego_goal_offset) + return 0; + + if (spi_offset(target) == tp->nego_goal_offset && + spi_period(target) == tp->nego_goal_period) + return 0; + + return 1; +} + +static int esp_alloc_lun_tag(struct esp_cmd_entry *ent, + struct esp_lun_data *lp) +{ + if (!ent->tag[0]) { + /* Non-tagged, slot already taken? */ + if (lp->non_tagged_cmd) + return -EBUSY; + + if (lp->hold) { + /* We are being held by active tagged + * commands. + */ + if (lp->num_tagged) + return -EBUSY; + + /* Tagged commands completed, we can unplug + * the queue and run this untagged command. + */ + lp->hold = 0; + } else if (lp->num_tagged) { + /* Plug the queue until num_tagged decreases + * to zero in esp_free_lun_tag. + */ + lp->hold = 1; + return -EBUSY; + } + + lp->non_tagged_cmd = ent; + return 0; + } else { + /* Tagged command, see if blocked by a + * non-tagged one. + */ + if (lp->non_tagged_cmd || lp->hold) + return -EBUSY; + } + + BUG_ON(lp->tagged_cmds[ent->tag[1]]); + + lp->tagged_cmds[ent->tag[1]] = ent; + lp->num_tagged++; + + return 0; +} + +static void esp_free_lun_tag(struct esp_cmd_entry *ent, + struct esp_lun_data *lp) +{ + if (ent->tag[0]) { + BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent); + lp->tagged_cmds[ent->tag[1]] = NULL; + lp->num_tagged--; + } else { + BUG_ON(lp->non_tagged_cmd != ent); + lp->non_tagged_cmd = NULL; + } +} + +/* When a contingent allegiance conditon is created, we force feed a + * REQUEST_SENSE command to the device to fetch the sense data. I + * tried many other schemes, relying on the scsi error handling layer + * to send out the REQUEST_SENSE automatically, but this was difficult + * to get right especially in the presence of applications like smartd + * which use SG_IO to send out their own REQUEST_SENSE commands. + */ +static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + struct scsi_device *dev = cmd->device; + int tgt, lun; + u8 *p, val; + + tgt = dev->id; + lun = dev->lun; + + + if (!ent->sense_ptr) { + esp_log_autosense("esp%d: Doing auto-sense for " + "tgt[%d] lun[%d]\n", + esp->host->unique_id, tgt, lun); + + ent->sense_ptr = cmd->sense_buffer; + ent->sense_dma = esp->ops->map_single(esp, + ent->sense_ptr, + SCSI_SENSE_BUFFERSIZE, + DMA_FROM_DEVICE); + } + ent->saved_sense_ptr = ent->sense_ptr; + + esp->active_cmd = ent; + + p = esp->command_block; + esp->msg_out_len = 0; + + *p++ = IDENTIFY(0, lun); + *p++ = REQUEST_SENSE; + *p++ = ((dev->scsi_level <= SCSI_2) ? + (lun << 5) : 0); + *p++ = 0; + *p++ = 0; + *p++ = SCSI_SENSE_BUFFERSIZE; + *p++ = 0; + + esp->select_state = ESP_SELECT_BASIC; + + val = tgt; + if (esp->rev == FASHME) + val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT; + esp_write8(val, ESP_BUSID); + + esp_write_tgt_sync(esp, tgt); + esp_write_tgt_config3(esp, tgt); + + val = (p - esp->command_block); + + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA); +} + +static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp) +{ + struct esp_cmd_entry *ent; + + list_for_each_entry(ent, &esp->queued_cmds, list) { + struct scsi_cmnd *cmd = ent->cmd; + struct scsi_device *dev = cmd->device; + struct esp_lun_data *lp = dev->hostdata; + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->tag[0] = 0; + ent->tag[1] = 0; + return ent; + } + + if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) { + ent->tag[0] = 0; + ent->tag[1] = 0; + } + + if (esp_alloc_lun_tag(ent, lp) < 0) + continue; + + return ent; + } + + return NULL; +} + +static void esp_maybe_execute_command(struct esp *esp) +{ + struct esp_target_data *tp; + struct esp_lun_data *lp; + struct scsi_device *dev; + struct scsi_cmnd *cmd; + struct esp_cmd_entry *ent; + int tgt, lun, i; + u32 val, start_cmd; + u8 *p; + + if (esp->active_cmd || + (esp->flags & ESP_FLAG_RESETTING)) + return; + + ent = find_and_prep_issuable_command(esp); + if (!ent) + return; + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + esp_autosense(esp, ent); + return; + } + + cmd = ent->cmd; + dev = cmd->device; + tgt = dev->id; + lun = dev->lun; + tp = &esp->target[tgt]; + lp = dev->hostdata; + + list_del(&ent->list); + list_add(&ent->list, &esp->active_cmds); + + esp->active_cmd = ent; + + esp_map_dma(esp, cmd); + esp_save_pointers(esp, ent); + + esp_check_command_len(esp, cmd); + + p = esp->command_block; + + esp->msg_out_len = 0; + if (tp->flags & ESP_TGT_CHECK_NEGO) { + /* Need to negotiate. If the target is broken + * go for synchronous transfers and non-wide. + */ + if (tp->flags & ESP_TGT_BROKEN) { + tp->flags &= ~ESP_TGT_DISCONNECT; + tp->nego_goal_period = 0; + tp->nego_goal_offset = 0; + tp->nego_goal_width = 0; + tp->nego_goal_tags = 0; + } + + /* If the settings are not changing, skip this. */ + if (spi_width(tp->starget) == tp->nego_goal_width && + spi_period(tp->starget) == tp->nego_goal_period && + spi_offset(tp->starget) == tp->nego_goal_offset) { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + goto build_identify; + } + + if (esp->rev == FASHME && esp_need_to_nego_wide(tp)) { + esp->msg_out_len = + spi_populate_width_msg(&esp->msg_out[0], + (tp->nego_goal_width ? + 1 : 0)); + tp->flags |= ESP_TGT_NEGO_WIDE; + } else if (esp_need_to_nego_sync(tp)) { + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + tp->flags |= ESP_TGT_NEGO_SYNC; + } else { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + } + + /* Process it like a slow command. */ + if (tp->flags & (ESP_TGT_NEGO_WIDE | ESP_TGT_NEGO_SYNC)) + esp->flags |= ESP_FLAG_DOING_SLOWCMD; + } + +build_identify: + /* If we don't have a lun-data struct yet, we're probing + * so do not disconnect. Also, do not disconnect unless + * we have a tag on this command. + */ + if (lp && (tp->flags & ESP_TGT_DISCONNECT) && ent->tag[0]) + *p++ = IDENTIFY(1, lun); + else + *p++ = IDENTIFY(0, lun); + + if (ent->tag[0] && esp->rev == ESP100) { + /* ESP100 lacks select w/atn3 command, use select + * and stop instead. + */ + esp->flags |= ESP_FLAG_DOING_SLOWCMD; + } + + if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) { + start_cmd = ESP_CMD_DMA | ESP_CMD_SELA; + if (ent->tag[0]) { + *p++ = ent->tag[0]; + *p++ = ent->tag[1]; + + start_cmd = ESP_CMD_DMA | ESP_CMD_SA3; + } + + for (i = 0; i < cmd->cmd_len; i++) + *p++ = cmd->cmnd[i]; + + esp->select_state = ESP_SELECT_BASIC; + } else { + esp->cmd_bytes_left = cmd->cmd_len; + esp->cmd_bytes_ptr = &cmd->cmnd[0]; + + if (ent->tag[0]) { + for (i = esp->msg_out_len - 1; + i >= 0; i--) + esp->msg_out[i + 2] = esp->msg_out[i]; + esp->msg_out[0] = ent->tag[0]; + esp->msg_out[1] = ent->tag[1]; + esp->msg_out_len += 2; + } + + start_cmd = ESP_CMD_DMA | ESP_CMD_SELAS; + esp->select_state = ESP_SELECT_MSGOUT; + } + val = tgt; + if (esp->rev == FASHME) + val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT; + esp_write8(val, ESP_BUSID); + + esp_write_tgt_sync(esp, tgt); + esp_write_tgt_config3(esp, tgt); + + val = (p - esp->command_block); + + if (esp_debug & ESP_DEBUG_SCSICMD) { + printk("ESP: tgt[%d] lun[%d] scsi_cmd [ ", tgt, lun); + for (i = 0; i < cmd->cmd_len; i++) + printk("%02x ", cmd->cmnd[i]); + printk("]\n"); + } + + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + val, 16, 0, start_cmd); +} + +static struct esp_cmd_entry *esp_get_ent(struct esp *esp) +{ + struct list_head *head = &esp->esp_cmd_pool; + struct esp_cmd_entry *ret; + + if (list_empty(head)) { + ret = kzalloc(sizeof(struct esp_cmd_entry), GFP_ATOMIC); + } else { + ret = list_entry(head->next, struct esp_cmd_entry, list); + list_del(&ret->list); + memset(ret, 0, sizeof(*ret)); + } + return ret; +} + +static void esp_put_ent(struct esp *esp, struct esp_cmd_entry *ent) +{ + list_add(&ent->list, &esp->esp_cmd_pool); +} + +static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd, unsigned int result) +{ + struct scsi_device *dev = cmd->device; + int tgt = dev->id; + int lun = dev->lun; + + esp->active_cmd = NULL; + esp_unmap_dma(esp, cmd); + esp_free_lun_tag(ent, dev->hostdata); + cmd->result = result; + + if (ent->eh_done) { + complete(ent->eh_done); + ent->eh_done = NULL; + } + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + esp->ops->unmap_single(esp, ent->sense_dma, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + ent->sense_ptr = NULL; + + /* Restore the message/status bytes to what we actually + * saw originally. Also, report that we are providing + * the sense data. + */ + cmd->result = ((DRIVER_SENSE << 24) | + (DID_OK << 16) | + (COMMAND_COMPLETE << 8) | + (SAM_STAT_CHECK_CONDITION << 0)); + + ent->flags &= ~ESP_CMD_FLAG_AUTOSENSE; + if (esp_debug & ESP_DEBUG_AUTOSENSE) { + int i; + + printk("esp%d: tgt[%d] lun[%d] AUTO SENSE[ ", + esp->host->unique_id, tgt, lun); + for (i = 0; i < 18; i++) + printk("%02x ", cmd->sense_buffer[i]); + printk("]\n"); + } + } + + cmd->scsi_done(cmd); + + list_del(&ent->list); + esp_put_ent(esp, ent); + + esp_maybe_execute_command(esp); +} + +static unsigned int compose_result(unsigned int status, unsigned int message, + unsigned int driver_code) +{ + return (status | (message << 8) | (driver_code << 16)); +} + +static void esp_event_queue_full(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_device *dev = ent->cmd->device; + struct esp_lun_data *lp = dev->hostdata; + + scsi_track_queue_full(dev, lp->num_tagged - 1); +} + +static int esp_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + struct scsi_device *dev = cmd->device; + struct esp *esp = host_to_esp(dev->host); + struct esp_cmd_priv *spriv; + struct esp_cmd_entry *ent; + + ent = esp_get_ent(esp); + if (!ent) + return SCSI_MLQUEUE_HOST_BUSY; + + ent->cmd = cmd; + + cmd->scsi_done = done; + + spriv = ESP_CMD_PRIV(cmd); + spriv->u.dma_addr = ~(dma_addr_t)0x0; + + list_add_tail(&ent->list, &esp->queued_cmds); + + esp_maybe_execute_command(esp); + + return 0; +} + +static int esp_check_gross_error(struct esp *esp) +{ + if (esp->sreg & ESP_STAT_SPAM) { + /* Gross Error, could be one of: + * - top of fifo overwritten + * - top of command register overwritten + * - DMA programmed with wrong direction + * - improper phase change + */ + printk(KERN_ERR PFX "esp%d: Gross error sreg[%02x]\n", + esp->host->unique_id, esp->sreg); + /* XXX Reset the chip. XXX */ + return 1; + } + return 0; +} + +static int esp_check_spur_intr(struct esp *esp) +{ + switch (esp->rev) { + case ESP100: + case ESP100A: + /* The interrupt pending bit of the status register cannot + * be trusted on these revisions. + */ + esp->sreg &= ~ESP_STAT_INTR; + break; + + default: + if (!(esp->sreg & ESP_STAT_INTR)) { + esp->ireg = esp_read8(ESP_INTRPT); + if (esp->ireg & ESP_INTR_SR) + return 1; + + /* If the DMA is indicating interrupt pending and the + * ESP is not, the only possibility is a DMA error. + */ + if (!esp->ops->dma_error(esp)) { + printk(KERN_ERR PFX "esp%d: Spurious irq, " + "sreg=%x.\n", + esp->host->unique_id, esp->sreg); + return -1; + } + + printk(KERN_ERR PFX "esp%d: DMA error\n", + esp->host->unique_id); + + /* XXX Reset the chip. XXX */ + return -1; + } + break; + } + + return 0; +} + +static void esp_schedule_reset(struct esp *esp) +{ + esp_log_reset("ESP: esp_schedule_reset() from %p\n", + __builtin_return_address(0)); + esp->flags |= ESP_FLAG_RESETTING; + esp_event(esp, ESP_EVENT_RESET); +} + +/* In order to avoid having to add a special half-reconnected state + * into the driver we just sit here and poll through the rest of + * the reselection process to get the tag message bytes. + */ +static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp, + struct esp_lun_data *lp) +{ + struct esp_cmd_entry *ent; + int i; + + if (!lp->num_tagged) { + printk(KERN_ERR PFX "esp%d: Reconnect w/num_tagged==0\n", + esp->host->unique_id); + return NULL; + } + + esp_log_reconnect("ESP: reconnect tag, "); + + for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) { + if (esp->ops->irq_pending(esp)) + break; + } + if (i == ESP_QUICKIRQ_LIMIT) { + printk(KERN_ERR PFX "esp%d: Reconnect IRQ1 timeout\n", + esp->host->unique_id); + return NULL; + } + + esp->sreg = esp_read8(ESP_STATUS); + esp->ireg = esp_read8(ESP_INTRPT); + + esp_log_reconnect("IRQ(%d:%x:%x), ", + i, esp->ireg, esp->sreg); + + if (esp->ireg & ESP_INTR_DC) { + printk(KERN_ERR PFX "esp%d: Reconnect, got disconnect.\n", + esp->host->unique_id); + return NULL; + } + + if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) { + printk(KERN_ERR PFX "esp%d: Reconnect, not MIP sreg[%02x].\n", + esp->host->unique_id, esp->sreg); + return NULL; + } + + /* DMA in the tag bytes... */ + esp->command_block[0] = 0xff; + esp->command_block[1] = 0xff; + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + 2, 2, 1, ESP_CMD_DMA | ESP_CMD_TI); + + /* ACK the msssage. */ + scsi_esp_cmd(esp, ESP_CMD_MOK); + + for (i = 0; i < ESP_RESELECT_TAG_LIMIT; i++) { + if (esp->ops->irq_pending(esp)) { + esp->sreg = esp_read8(ESP_STATUS); + esp->ireg = esp_read8(ESP_INTRPT); + if (esp->ireg & ESP_INTR_FDONE) + break; + } + udelay(1); + } + if (i == ESP_RESELECT_TAG_LIMIT) { + printk(KERN_ERR PFX "esp%d: Reconnect IRQ2 timeout\n", + esp->host->unique_id); + return NULL; + } + esp->ops->dma_drain(esp); + esp->ops->dma_invalidate(esp); + + esp_log_reconnect("IRQ2(%d:%x:%x) tag[%x:%x]\n", + i, esp->ireg, esp->sreg, + esp->command_block[0], + esp->command_block[1]); + + if (esp->command_block[0] < SIMPLE_QUEUE_TAG || + esp->command_block[0] > ORDERED_QUEUE_TAG) { + printk(KERN_ERR PFX "esp%d: Reconnect, bad tag " + "type %02x.\n", + esp->host->unique_id, esp->command_block[0]); + return NULL; + } + + ent = lp->tagged_cmds[esp->command_block[1]]; + if (!ent) { + printk(KERN_ERR PFX "esp%d: Reconnect, no entry for " + "tag %02x.\n", + esp->host->unique_id, esp->command_block[1]); + return NULL; + } + + return ent; +} + +static int esp_reconnect(struct esp *esp) +{ + struct esp_cmd_entry *ent; + struct esp_target_data *tp; + struct esp_lun_data *lp; + struct scsi_device *dev; + int target, lun; + + BUG_ON(esp->active_cmd); + if (esp->rev == FASHME) { + /* FASHME puts the target and lun numbers directly + * into the fifo. + */ + target = esp->fifo[0]; + lun = esp->fifo[1] & 0x7; + } else { + u8 bits = esp_read8(ESP_FDATA); + + /* Older chips put the lun directly into the fifo, but + * the target is given as a sample of the arbitration + * lines on the bus at reselection time. So we should + * see the ID of the ESP and the one reconnecting target + * set in the bitmap. + */ + if (!(bits & esp->scsi_id_mask)) + goto do_reset; + bits &= ~esp->scsi_id_mask; + if (!bits || (bits & (bits - 1))) + goto do_reset; + + target = ffs(bits) - 1; + lun = (esp_read8(ESP_FDATA) & 0x7); + + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + if (esp->rev == ESP100) { + u8 ireg = esp_read8(ESP_INTRPT); + /* This chip has a bug during reselection that can + * cause a spurious illegal-command interrupt, which + * we simply ACK here. Another possibility is a bus + * reset so we must check for that. + */ + if (ireg & ESP_INTR_SR) + goto do_reset; + } + scsi_esp_cmd(esp, ESP_CMD_NULL); + } + + esp_write_tgt_sync(esp, target); + esp_write_tgt_config3(esp, target); + + scsi_esp_cmd(esp, ESP_CMD_MOK); + + if (esp->rev == FASHME) + esp_write8(target | ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT, + ESP_BUSID); + + tp = &esp->target[target]; + dev = __scsi_device_lookup_by_target(tp->starget, lun); + if (!dev) { + printk(KERN_ERR PFX "esp%d: Reconnect, no lp " + "tgt[%u] lun[%u]\n", + esp->host->unique_id, target, lun); + goto do_reset; + } + lp = dev->hostdata; + + ent = lp->non_tagged_cmd; + if (!ent) { + ent = esp_reconnect_with_tag(esp, lp); + if (!ent) + goto do_reset; + } + + esp->active_cmd = ent; + + if (ent->flags & ESP_CMD_FLAG_ABORT) { + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); + } + + esp_event(esp, ESP_EVENT_CHECK_PHASE); + esp_restore_pointers(esp, ent); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 1; + +do_reset: + esp_schedule_reset(esp); + return 0; +} + +static int esp_finish_select(struct esp *esp) +{ + struct esp_cmd_entry *ent; + struct scsi_cmnd *cmd; + u8 orig_select_state; + + orig_select_state = esp->select_state; + + /* No longer selecting. */ + esp->select_state = ESP_SELECT_NONE; + + esp->seqreg = esp_read8(ESP_SSTEP) & ESP_STEP_VBITS; + ent = esp->active_cmd; + cmd = ent->cmd; + + if (esp->ops->dma_error(esp)) { + /* If we see a DMA error during or as a result of selection, + * all bets are off. + */ + esp_schedule_reset(esp); + esp_cmd_is_done(esp, ent, cmd, (DID_ERROR << 16)); + return 0; + } + + esp->ops->dma_invalidate(esp); + + if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) { + struct esp_target_data *tp = &esp->target[cmd->device->id]; + + /* Carefully back out of the selection attempt. Release + * resources (such as DMA mapping & TAG) and reset state (such + * as message out and command delivery variables). + */ + if (!(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) { + esp_unmap_dma(esp, cmd); + esp_free_lun_tag(ent, cmd->device->hostdata); + tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE); + esp->flags &= ~ESP_FLAG_DOING_SLOWCMD; + esp->cmd_bytes_ptr = NULL; + esp->cmd_bytes_left = 0; + } else { + esp->ops->unmap_single(esp, ent->sense_dma, + SCSI_SENSE_BUFFERSIZE, + DMA_FROM_DEVICE); + ent->sense_ptr = NULL; + } + + /* Now that the state is unwound properly, put back onto + * the issue queue. This command is no longer active. + */ + list_del(&ent->list); + list_add(&ent->list, &esp->queued_cmds); + esp->active_cmd = NULL; + + /* Return value ignored by caller, it directly invokes + * esp_reconnect(). + */ + return 0; + } + + if (esp->ireg == ESP_INTR_DC) { + struct scsi_device *dev = cmd->device; + + /* Disconnect. Make sure we re-negotiate sync and + * wide parameters if this target starts responding + * again in the future. + */ + esp->target[dev->id].flags |= ESP_TGT_CHECK_NEGO; + + scsi_esp_cmd(esp, ESP_CMD_ESEL); + esp_cmd_is_done(esp, ent, cmd, (DID_BAD_TARGET << 16)); + return 1; + } + + if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) { + /* Selection successful. On pre-FAST chips we have + * to do a NOP and possibly clean out the FIFO. + */ + if (esp->rev <= ESP236) { + int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES; + + scsi_esp_cmd(esp, ESP_CMD_NULL); + + if (!fcnt && + (!esp->prev_soff || + ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP))) + esp_flush_fifo(esp); + } + + /* If we are doing a slow command, negotiation, etc. + * we'll do the right thing as we transition to the + * next phase. + */ + esp_event(esp, ESP_EVENT_CHECK_PHASE); + return 0; + } + + printk("ESP: Unexpected selection completion ireg[%x].\n", + esp->ireg); + esp_schedule_reset(esp); + return 0; +} + +static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd) +{ + int fifo_cnt, ecount, bytes_sent, flush_fifo; + + fifo_cnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES; + if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE) + fifo_cnt <<= 1; + + ecount = 0; + if (!(esp->sreg & ESP_STAT_TCNT)) { + ecount = ((unsigned int)esp_read8(ESP_TCLOW) | + (((unsigned int)esp_read8(ESP_TCMED)) << 8)); + if (esp->rev == FASHME) + ecount |= ((unsigned int)esp_read8(FAS_RLO)) << 16; + } + + bytes_sent = esp->data_dma_len; + bytes_sent -= ecount; + + if (!(ent->flags & ESP_CMD_FLAG_WRITE)) + bytes_sent -= fifo_cnt; + + flush_fifo = 0; + if (!esp->prev_soff) { + /* Synchronous data transfer, always flush fifo. */ + flush_fifo = 1; + } else { + if (esp->rev == ESP100) { + u32 fflags, phase; + + /* ESP100 has a chip bug where in the synchronous data + * phase it can mistake a final long REQ pulse from the + * target as an extra data byte. Fun. + * + * To detect this case we resample the status register + * and fifo flags. If we're still in a data phase and + * we see spurious chunks in the fifo, we return error + * to the caller which should reset and set things up + * such that we only try future transfers to this + * target in synchronous mode. + */ + esp->sreg = esp_read8(ESP_STATUS); + phase = esp->sreg & ESP_STAT_PMASK; + fflags = esp_read8(ESP_FFLAGS); + + if ((phase == ESP_DOP && + (fflags & ESP_FF_ONOTZERO)) || + (phase == ESP_DIP && + (fflags & ESP_FF_FBYTES))) + return -1; + } + if (!(ent->flags & ESP_CMD_FLAG_WRITE)) + flush_fifo = 1; + } + + if (flush_fifo) + esp_flush_fifo(esp); + + return bytes_sent; +} + +static void esp_setsync(struct esp *esp, struct esp_target_data *tp, + u8 scsi_period, u8 scsi_offset, + u8 esp_stp, u8 esp_soff) +{ + spi_period(tp->starget) = scsi_period; + spi_offset(tp->starget) = scsi_offset; + spi_width(tp->starget) = (tp->flags & ESP_TGT_WIDE) ? 1 : 0; + + if (esp_soff) { + esp_stp &= 0x1f; + esp_soff |= esp->radelay; + if (esp->rev >= FAS236) { + u8 bit = ESP_CONFIG3_FSCSI; + if (esp->rev >= FAS100A) + bit = ESP_CONFIG3_FAST; + + if (scsi_period < 50) { + if (esp->rev == FASHME) + esp_soff &= ~esp->radelay; + tp->esp_config3 |= bit; + } else { + tp->esp_config3 &= ~bit; + } + esp->prev_cfg3 = tp->esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + } + } + + tp->esp_period = esp->prev_stp = esp_stp; + tp->esp_offset = esp->prev_soff = esp_soff; + + esp_write8(esp_soff, ESP_SOFF); + esp_write8(esp_stp, ESP_STP); + + tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO); + + spi_display_xfer_agreement(tp->starget); +} + +static void esp_msgin_reject(struct esp *esp) +{ + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + struct esp_target_data *tp; + int tgt; + + tgt = cmd->device->id; + tp = &esp->target[tgt]; + + if (tp->flags & ESP_TGT_NEGO_WIDE) { + tp->flags &= ~(ESP_TGT_NEGO_WIDE | ESP_TGT_WIDE); + + if (!esp_need_to_nego_sync(tp)) { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + scsi_esp_cmd(esp, ESP_CMD_RATN); + } else { + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + tp->flags |= ESP_TGT_NEGO_SYNC; + scsi_esp_cmd(esp, ESP_CMD_SATN); + } + return; + } + + if (tp->flags & ESP_TGT_NEGO_SYNC) { + tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO); + tp->esp_period = 0; + tp->esp_offset = 0; + esp_setsync(esp, tp, 0, 0, 0, 0); + scsi_esp_cmd(esp, ESP_CMD_RATN); + return; + } + + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp) +{ + u8 period = esp->msg_in[3]; + u8 offset = esp->msg_in[4]; + u8 stp; + + if (!(tp->flags & ESP_TGT_NEGO_SYNC)) + goto do_reject; + + if (offset > 15) + goto do_reject; + + if (offset) { + int rounded_up, one_clock; + + if (period > esp->max_period) { + period = offset = 0; + goto do_sdtr; + } + if (period < esp->min_period) + goto do_reject; + + one_clock = esp->ccycle / 1000; + rounded_up = (period << 2); + rounded_up = (rounded_up + one_clock - 1) / one_clock; + stp = rounded_up; + if (stp && esp->rev >= FAS236) { + if (stp >= 50) + stp--; + } + } else { + stp = 0; + } + + esp_setsync(esp, tp, period, offset, stp, offset); + return; + +do_reject: + esp->msg_out[0] = MESSAGE_REJECT; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); + return; + +do_sdtr: + tp->nego_goal_period = period; + tp->nego_goal_offset = offset; + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +static void esp_msgin_wdtr(struct esp *esp, struct esp_target_data *tp) +{ + int size = 8 << esp->msg_in[3]; + u8 cfg3; + + if (esp->rev != FASHME) + goto do_reject; + + if (size != 8 && size != 16) + goto do_reject; + + if (!(tp->flags & ESP_TGT_NEGO_WIDE)) + goto do_reject; + + cfg3 = tp->esp_config3; + if (size == 16) { + tp->flags |= ESP_TGT_WIDE; + cfg3 |= ESP_CONFIG3_EWIDE; + } else { + tp->flags &= ~ESP_TGT_WIDE; + cfg3 &= ~ESP_CONFIG3_EWIDE; + } + tp->esp_config3 = cfg3; + esp->prev_cfg3 = cfg3; + esp_write8(cfg3, ESP_CFG3); + + tp->flags &= ~ESP_TGT_NEGO_WIDE; + + spi_period(tp->starget) = 0; + spi_offset(tp->starget) = 0; + if (!esp_need_to_nego_sync(tp)) { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + scsi_esp_cmd(esp, ESP_CMD_RATN); + } else { + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + tp->flags |= ESP_TGT_NEGO_SYNC; + scsi_esp_cmd(esp, ESP_CMD_SATN); + } + return; + +do_reject: + esp->msg_out[0] = MESSAGE_REJECT; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +static void esp_msgin_extended(struct esp *esp) +{ + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + struct esp_target_data *tp; + int tgt = cmd->device->id; + + tp = &esp->target[tgt]; + if (esp->msg_in[2] == EXTENDED_SDTR) { + esp_msgin_sdtr(esp, tp); + return; + } + if (esp->msg_in[2] == EXTENDED_WDTR) { + esp_msgin_wdtr(esp, tp); + return; + } + + printk("ESP: Unexpected extended msg type %x\n", + esp->msg_in[2]); + + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +/* Analyze msgin bytes received from target so far. Return non-zero + * if there are more bytes needed to complete the message. + */ +static int esp_msgin_process(struct esp *esp) +{ + u8 msg0 = esp->msg_in[0]; + int len = esp->msg_in_len; + + if (msg0 & 0x80) { + /* Identify */ + printk("ESP: Unexpected msgin identify\n"); + return 0; + } + + switch (msg0) { + case EXTENDED_MESSAGE: + if (len == 1) + return 1; + if (len < esp->msg_in[1] + 2) + return 1; + esp_msgin_extended(esp); + return 0; + + case IGNORE_WIDE_RESIDUE: { + struct esp_cmd_entry *ent; + struct esp_cmd_priv *spriv; + if (len == 1) + return 1; + + if (esp->msg_in[1] != 1) + goto do_reject; + + ent = esp->active_cmd; + spriv = ESP_CMD_PRIV(ent->cmd); + + if (spriv->cur_residue == sg_dma_len(spriv->cur_sg)) { + spriv->cur_sg--; + spriv->cur_residue = 1; + } else + spriv->cur_residue++; + spriv->tot_residue++; + return 0; + } + case NOP: + return 0; + case RESTORE_POINTERS: + esp_restore_pointers(esp, esp->active_cmd); + return 0; + case SAVE_POINTERS: + esp_save_pointers(esp, esp->active_cmd); + return 0; + + case COMMAND_COMPLETE: + case DISCONNECT: { + struct esp_cmd_entry *ent = esp->active_cmd; + + ent->message = msg0; + esp_event(esp, ESP_EVENT_FREE_BUS); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 0; + } + case MESSAGE_REJECT: + esp_msgin_reject(esp); + return 0; + + default: + do_reject: + esp->msg_out[0] = MESSAGE_REJECT; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); + return 0; + } +} + +static int esp_process_event(struct esp *esp) +{ + int write; + +again: + write = 0; + switch (esp->event) { + case ESP_EVENT_CHECK_PHASE: + switch (esp->sreg & ESP_STAT_PMASK) { + case ESP_DOP: + esp_event(esp, ESP_EVENT_DATA_OUT); + break; + case ESP_DIP: + esp_event(esp, ESP_EVENT_DATA_IN); + break; + case ESP_STATP: + esp_flush_fifo(esp); + scsi_esp_cmd(esp, ESP_CMD_ICCSEQ); + esp_event(esp, ESP_EVENT_STATUS); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 1; + + case ESP_MOP: + esp_event(esp, ESP_EVENT_MSGOUT); + break; + + case ESP_MIP: + esp_event(esp, ESP_EVENT_MSGIN); + break; + + case ESP_CMDP: + esp_event(esp, ESP_EVENT_CMD_START); + break; + + default: + printk("ESP: Unexpected phase, sreg=%02x\n", + esp->sreg); + esp_schedule_reset(esp); + return 0; + } + goto again; + break; + + case ESP_EVENT_DATA_IN: + write = 1; + /* fallthru */ + + case ESP_EVENT_DATA_OUT: { + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + dma_addr_t dma_addr = esp_cur_dma_addr(ent, cmd); + unsigned int dma_len = esp_cur_dma_len(ent, cmd); + + if (esp->rev == ESP100) + scsi_esp_cmd(esp, ESP_CMD_NULL); + + if (write) + ent->flags |= ESP_CMD_FLAG_WRITE; + else + ent->flags &= ~ESP_CMD_FLAG_WRITE; + + dma_len = esp_dma_length_limit(esp, dma_addr, dma_len); + esp->data_dma_len = dma_len; + + if (!dma_len) { + printk(KERN_ERR PFX "esp%d: DMA length is zero!\n", + esp->host->unique_id); + printk(KERN_ERR PFX "esp%d: cur adr[%08llx] len[%08x]\n", + esp->host->unique_id, + (unsigned long long)esp_cur_dma_addr(ent, cmd), + esp_cur_dma_len(ent, cmd)); + esp_schedule_reset(esp); + return 0; + } + + esp_log_datastart("ESP: start data addr[%08llx] len[%u] " + "write(%d)\n", + (unsigned long long)dma_addr, dma_len, write); + + esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len, + write, ESP_CMD_DMA | ESP_CMD_TI); + esp_event(esp, ESP_EVENT_DATA_DONE); + break; + } + case ESP_EVENT_DATA_DONE: { + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + int bytes_sent; + + if (esp->ops->dma_error(esp)) { + printk("ESP: data done, DMA error, resetting\n"); + esp_schedule_reset(esp); + return 0; + } + + if (ent->flags & ESP_CMD_FLAG_WRITE) { + /* XXX parity errors, etc. XXX */ + + esp->ops->dma_drain(esp); + } + esp->ops->dma_invalidate(esp); + + if (esp->ireg != ESP_INTR_BSERV) { + /* We should always see exactly a bus-service + * interrupt at the end of a successful transfer. + */ + printk("ESP: data done, not BSERV, resetting\n"); + esp_schedule_reset(esp); + return 0; + } + + bytes_sent = esp_data_bytes_sent(esp, ent, cmd); + + esp_log_datadone("ESP: data done flgs[%x] sent[%d]\n", + ent->flags, bytes_sent); + + if (bytes_sent < 0) { + /* XXX force sync mode for this target XXX */ + esp_schedule_reset(esp); + return 0; + } + + esp_advance_dma(esp, ent, cmd, bytes_sent); + esp_event(esp, ESP_EVENT_CHECK_PHASE); + goto again; + break; + } + + case ESP_EVENT_STATUS: { + struct esp_cmd_entry *ent = esp->active_cmd; + + if (esp->ireg & ESP_INTR_FDONE) { + ent->status = esp_read8(ESP_FDATA); + ent->message = esp_read8(ESP_FDATA); + scsi_esp_cmd(esp, ESP_CMD_MOK); + } else if (esp->ireg == ESP_INTR_BSERV) { + ent->status = esp_read8(ESP_FDATA); + ent->message = 0xff; + esp_event(esp, ESP_EVENT_MSGIN); + return 0; + } + + if (ent->message != COMMAND_COMPLETE) { + printk("ESP: Unexpected message %x in status\n", + ent->message); + esp_schedule_reset(esp); + return 0; + } + + esp_event(esp, ESP_EVENT_FREE_BUS); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + break; + } + case ESP_EVENT_FREE_BUS: { + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + + if (ent->message == COMMAND_COMPLETE || + ent->message == DISCONNECT) + scsi_esp_cmd(esp, ESP_CMD_ESEL); + + if (ent->message == COMMAND_COMPLETE) { + esp_log_cmddone("ESP: Command done status[%x] " + "message[%x]\n", + ent->status, ent->message); + if (ent->status == SAM_STAT_TASK_SET_FULL) + esp_event_queue_full(esp, ent); + + if (ent->status == SAM_STAT_CHECK_CONDITION && + !(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) { + ent->flags |= ESP_CMD_FLAG_AUTOSENSE; + esp_autosense(esp, ent); + } else { + esp_cmd_is_done(esp, ent, cmd, + compose_result(ent->status, + ent->message, + DID_OK)); + } + } else if (ent->message == DISCONNECT) { + esp_log_disconnect("ESP: Disconnecting tgt[%d] " + "tag[%x:%x]\n", + cmd->device->id, + ent->tag[0], ent->tag[1]); + + esp->active_cmd = NULL; + esp_maybe_execute_command(esp); + } else { + printk("ESP: Unexpected message %x in freebus\n", + ent->message); + esp_schedule_reset(esp); + return 0; + } + if (esp->active_cmd) + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + break; + } + case ESP_EVENT_MSGOUT: { + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + + if (esp_debug & ESP_DEBUG_MSGOUT) { + int i; + printk("ESP: Sending message [ "); + for (i = 0; i < esp->msg_out_len; i++) + printk("%02x ", esp->msg_out[i]); + printk("]\n"); + } + + if (esp->rev == FASHME) { + int i; + + /* Always use the fifo. */ + for (i = 0; i < esp->msg_out_len; i++) { + esp_write8(esp->msg_out[i], ESP_FDATA); + esp_write8(0, ESP_FDATA); + } + scsi_esp_cmd(esp, ESP_CMD_TI); + } else { + if (esp->msg_out_len == 1) { + esp_write8(esp->msg_out[0], ESP_FDATA); + scsi_esp_cmd(esp, ESP_CMD_TI); + } else { + /* Use DMA. */ + memcpy(esp->command_block, + esp->msg_out, + esp->msg_out_len); + + esp->ops->send_dma_cmd(esp, + esp->command_block_dma, + esp->msg_out_len, + esp->msg_out_len, + 0, + ESP_CMD_DMA|ESP_CMD_TI); + } + } + esp_event(esp, ESP_EVENT_MSGOUT_DONE); + break; + } + case ESP_EVENT_MSGOUT_DONE: + if (esp->rev == FASHME) { + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + } else { + if (esp->msg_out_len > 1) + esp->ops->dma_invalidate(esp); + } + + if (!(esp->ireg & ESP_INTR_DC)) { + if (esp->rev != FASHME) + scsi_esp_cmd(esp, ESP_CMD_NULL); + } + esp_event(esp, ESP_EVENT_CHECK_PHASE); + goto again; + case ESP_EVENT_MSGIN: + if (esp->ireg & ESP_INTR_BSERV) { + if (esp->rev == FASHME) { + if (!(esp_read8(ESP_STATUS2) & + ESP_STAT2_FEMPTY)) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + } else { + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + if (esp->rev == ESP100) + scsi_esp_cmd(esp, ESP_CMD_NULL); + } + scsi_esp_cmd(esp, ESP_CMD_TI); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 1; + } + if (esp->ireg & ESP_INTR_FDONE) { + u8 val; + + if (esp->rev == FASHME) + val = esp->fifo[0]; + else + val = esp_read8(ESP_FDATA); + esp->msg_in[esp->msg_in_len++] = val; + + esp_log_msgin("ESP: Got msgin byte %x\n", val); + + if (!esp_msgin_process(esp)) + esp->msg_in_len = 0; + + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + + scsi_esp_cmd(esp, ESP_CMD_MOK); + + if (esp->event != ESP_EVENT_FREE_BUS) + esp_event(esp, ESP_EVENT_CHECK_PHASE); + } else { + printk("ESP: MSGIN neither BSERV not FDON, resetting"); + esp_schedule_reset(esp); + return 0; + } + break; + case ESP_EVENT_CMD_START: + memcpy(esp->command_block, esp->cmd_bytes_ptr, + esp->cmd_bytes_left); + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + esp->cmd_bytes_left, 16, 0, + ESP_CMD_DMA | ESP_CMD_TI); + esp_event(esp, ESP_EVENT_CMD_DONE); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + break; + case ESP_EVENT_CMD_DONE: + esp->ops->dma_invalidate(esp); + if (esp->ireg & ESP_INTR_BSERV) { + esp_event(esp, ESP_EVENT_CHECK_PHASE); + goto again; + } + esp_schedule_reset(esp); + return 0; + break; + + case ESP_EVENT_RESET: + scsi_esp_cmd(esp, ESP_CMD_RS); + break; + + default: + printk("ESP: Unexpected event %x, resetting\n", + esp->event); + esp_schedule_reset(esp); + return 0; + break; + } + return 1; +} + +static void esp_reset_cleanup_one(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + + esp_unmap_dma(esp, cmd); + esp_free_lun_tag(ent, cmd->device->hostdata); + cmd->result = DID_RESET << 16; + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + esp->ops->unmap_single(esp, ent->sense_dma, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + ent->sense_ptr = NULL; + } + + cmd->scsi_done(cmd); + list_del(&ent->list); + esp_put_ent(esp, ent); +} + +static void esp_clear_hold(struct scsi_device *dev, void *data) +{ + struct esp_lun_data *lp = dev->hostdata; + + BUG_ON(lp->num_tagged); + lp->hold = 0; +} + +static void esp_reset_cleanup(struct esp *esp) +{ + struct esp_cmd_entry *ent, *tmp; + int i; + + list_for_each_entry_safe(ent, tmp, &esp->queued_cmds, list) { + struct scsi_cmnd *cmd = ent->cmd; + + list_del(&ent->list); + cmd->result = DID_RESET << 16; + cmd->scsi_done(cmd); + esp_put_ent(esp, ent); + } + + list_for_each_entry_safe(ent, tmp, &esp->active_cmds, list) { + if (ent == esp->active_cmd) + esp->active_cmd = NULL; + esp_reset_cleanup_one(esp, ent); + } + + BUG_ON(esp->active_cmd != NULL); + + /* Force renegotiation of sync/wide transfers. */ + for (i = 0; i < ESP_MAX_TARGET; i++) { + struct esp_target_data *tp = &esp->target[i]; + + tp->esp_period = 0; + tp->esp_offset = 0; + tp->esp_config3 &= ~(ESP_CONFIG3_EWIDE | + ESP_CONFIG3_FSCSI | + ESP_CONFIG3_FAST); + tp->flags &= ~ESP_TGT_WIDE; + tp->flags |= ESP_TGT_CHECK_NEGO; + + if (tp->starget) + starget_for_each_device(tp->starget, NULL, + esp_clear_hold); + } +} + +/* Runs under host->lock */ +static void __esp_interrupt(struct esp *esp) +{ + int finish_reset, intr_done; + u8 phase; + + esp->sreg = esp_read8(ESP_STATUS); + + if (esp->flags & ESP_FLAG_RESETTING) { + finish_reset = 1; + } else { + if (esp_check_gross_error(esp)) + return; + + finish_reset = esp_check_spur_intr(esp); + if (finish_reset < 0) + return; + } + + esp->ireg = esp_read8(ESP_INTRPT); + + if (esp->ireg & ESP_INTR_SR) + finish_reset = 1; + + if (finish_reset) { + esp_reset_cleanup(esp); + if (esp->eh_reset) { + complete(esp->eh_reset); + esp->eh_reset = NULL; + } + return; + } + + phase = (esp->sreg & ESP_STAT_PMASK); + if (esp->rev == FASHME) { + if (((phase != ESP_DIP && phase != ESP_DOP) && + esp->select_state == ESP_SELECT_NONE && + esp->event != ESP_EVENT_STATUS && + esp->event != ESP_EVENT_DATA_DONE) || + (esp->ireg & ESP_INTR_RSEL)) { + esp->sreg2 = esp_read8(ESP_STATUS2); + if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || + (esp->sreg2 & ESP_STAT2_F1BYTE)) + hme_read_fifo(esp); + } + } + + esp_log_intr("ESP: intr sreg[%02x] seqreg[%02x] " + "sreg2[%02x] ireg[%02x]\n", + esp->sreg, esp->seqreg, esp->sreg2, esp->ireg); + + intr_done = 0; + + if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN | ESP_INTR_IC)) { + printk("ESP: unexpected IREG %02x\n", esp->ireg); + if (esp->ireg & ESP_INTR_IC) + esp_dump_cmd_log(esp); + + esp_schedule_reset(esp); + } else { + if (!(esp->ireg & ESP_INTR_RSEL)) { + /* Some combination of FDONE, BSERV, DC. */ + if (esp->select_state != ESP_SELECT_NONE) + intr_done = esp_finish_select(esp); + } else if (esp->ireg & ESP_INTR_RSEL) { + if (esp->active_cmd) + (void) esp_finish_select(esp); + intr_done = esp_reconnect(esp); + } + } + while (!intr_done) + intr_done = esp_process_event(esp); +} + +irqreturn_t scsi_esp_intr(int irq, void *dev_id) +{ + struct esp *esp = dev_id; + unsigned long flags; + irqreturn_t ret; + + spin_lock_irqsave(esp->host->host_lock, flags); + ret = IRQ_NONE; + if (esp->ops->irq_pending(esp)) { + ret = IRQ_HANDLED; + for (;;) { + int i; + + __esp_interrupt(esp); + if (!(esp->flags & ESP_FLAG_QUICKIRQ_CHECK)) + break; + esp->flags &= ~ESP_FLAG_QUICKIRQ_CHECK; + + for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) { + if (esp->ops->irq_pending(esp)) + break; + } + if (i == ESP_QUICKIRQ_LIMIT) + break; + } + } + spin_unlock_irqrestore(esp->host->host_lock, flags); + + return ret; +} +EXPORT_SYMBOL(scsi_esp_intr); + +static void __devinit esp_get_revision(struct esp *esp) +{ + u8 val; + + esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); + esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); + esp_write8(esp->config2, ESP_CFG2); + + val = esp_read8(ESP_CFG2); + val &= ~ESP_CONFIG2_MAGIC; + if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { + /* If what we write to cfg2 does not come back, cfg2 is not + * implemented, therefore this must be a plain esp100. + */ + esp->rev = ESP100; + } else { + esp->config2 = 0; + esp_set_all_config3(esp, 5); + esp->prev_cfg3 = 5; + esp_write8(esp->config2, ESP_CFG2); + esp_write8(0, ESP_CFG3); + esp_write8(esp->prev_cfg3, ESP_CFG3); + + val = esp_read8(ESP_CFG3); + if (val != 5) { + /* The cfg2 register is implemented, however + * cfg3 is not, must be esp100a. + */ + esp->rev = ESP100A; + } else { + esp_set_all_config3(esp, 0); + esp->prev_cfg3 = 0; + esp_write8(esp->prev_cfg3, ESP_CFG3); + + /* All of cfg{1,2,3} implemented, must be one of + * the fas variants, figure out which one. + */ + if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) { + esp->rev = FAST; + esp->sync_defp = SYNC_DEFP_FAST; + } else { + esp->rev = ESP236; + } + esp->config2 = 0; + esp_write8(esp->config2, ESP_CFG2); + } + } +} + +static void __devinit esp_init_swstate(struct esp *esp) +{ + int i; + + INIT_LIST_HEAD(&esp->queued_cmds); + INIT_LIST_HEAD(&esp->active_cmds); + INIT_LIST_HEAD(&esp->esp_cmd_pool); + + /* Start with a clear state, domain validation (via ->slave_configure, + * spi_dv_device()) will attempt to enable SYNC, WIDE, and tagged + * commands. + */ + for (i = 0 ; i < ESP_MAX_TARGET; i++) { + esp->target[i].flags = 0; + esp->target[i].nego_goal_period = 0; + esp->target[i].nego_goal_offset = 0; + esp->target[i].nego_goal_width = 0; + esp->target[i].nego_goal_tags = 0; + } +} + +/* This places the ESP into a known state at boot time. */ +static void __devinit esp_bootup_reset(struct esp *esp) +{ + u8 val; + + /* Reset the DMA */ + esp->ops->reset_dma(esp); + + /* Reset the ESP */ + esp_reset_esp(esp); + + /* Reset the SCSI bus, but tell ESP not to generate an irq */ + val = esp_read8(ESP_CFG1); + val |= ESP_CONFIG1_SRRDISAB; + esp_write8(val, ESP_CFG1); + + scsi_esp_cmd(esp, ESP_CMD_RS); + udelay(400); + + esp_write8(esp->config1, ESP_CFG1); + + /* Eat any bitrot in the chip and we are done... */ + esp_read8(ESP_INTRPT); +} + +static void __devinit esp_set_clock_params(struct esp *esp) +{ + int fmhz; + u8 ccf; + + /* This is getting messy but it has to be done correctly or else + * you get weird behavior all over the place. We are trying to + * basically figure out three pieces of information. + * + * a) Clock Conversion Factor + * + * This is a representation of the input crystal clock frequency + * going into the ESP on this machine. Any operation whose timing + * is longer than 400ns depends on this value being correct. For + * example, you'll get blips for arbitration/selection during high + * load or with multiple targets if this is not set correctly. + * + * b) Selection Time-Out + * + * The ESP isn't very bright and will arbitrate for the bus and try + * to select a target forever if you let it. This value tells the + * ESP when it has taken too long to negotiate and that it should + * interrupt the CPU so we can see what happened. The value is + * computed as follows (from NCR/Symbios chip docs). + * + * (Time Out Period) * (Input Clock) + * STO = ---------------------------------- + * (8192) * (Clock Conversion Factor) + * + * We use a time out period of 250ms (ESP_BUS_TIMEOUT). + * + * c) Imperical constants for synchronous offset and transfer period + * register values + * + * This entails the smallest and largest sync period we could ever + * handle on this ESP. + */ + fmhz = esp->cfreq; + + ccf = ((fmhz / 1000000) + 4) / 5; + if (ccf == 1) + ccf = 2; + + /* If we can't find anything reasonable, just assume 20MHZ. + * This is the clock frequency of the older sun4c's where I've + * been unable to find the clock-frequency PROM property. All + * other machines provide useful values it seems. + */ + if (fmhz <= 5000000 || ccf < 1 || ccf > 8) { + fmhz = 20000000; + ccf = 4; + } + + esp->cfact = (ccf == 8 ? 0 : ccf); + esp->cfreq = fmhz; + esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz); + esp->ctick = ESP_TICK(ccf, esp->ccycle); + esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf); + esp->sync_defp = SYNC_DEFP_SLOW; +} + +static const char *esp_chip_names[] = { + "ESP100", + "ESP100A", + "ESP236", + "FAS236", + "FAS100A", + "FAST", + "FASHME", +}; + +static struct scsi_transport_template *esp_transport_template; + +int __devinit scsi_esp_register(struct esp *esp, struct device *dev) +{ + static int instance; + int err; + + esp->host->transportt = esp_transport_template; + esp->host->max_lun = ESP_MAX_LUN; + esp->host->cmd_per_lun = 2; + + esp_set_clock_params(esp); + + esp_get_revision(esp); + + esp_init_swstate(esp); + + esp_bootup_reset(esp); + + printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n", + esp->host->unique_id, esp->regs, esp->dma_regs, + esp->host->irq); + printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n", + esp->host->unique_id, esp_chip_names[esp->rev], + esp->cfreq / 1000000, esp->cfact, esp->scsi_id); + + /* Let the SCSI bus reset settle. */ + ssleep(esp_bus_reset_settle); + + err = scsi_add_host(esp->host, dev); + if (err) + return err; + + esp->host->unique_id = instance++; + + scsi_scan_host(esp->host); + + return 0; +} +EXPORT_SYMBOL(scsi_esp_register); + +void __devexit scsi_esp_unregister(struct esp *esp) +{ + scsi_remove_host(esp->host); +} +EXPORT_SYMBOL(scsi_esp_unregister); + +static int esp_slave_alloc(struct scsi_device *dev) +{ + struct esp *esp = host_to_esp(dev->host); + struct esp_target_data *tp = &esp->target[dev->id]; + struct esp_lun_data *lp; + + lp = kzalloc(sizeof(*lp), GFP_KERNEL); + if (!lp) + return -ENOMEM; + dev->hostdata = lp; + + tp->starget = dev->sdev_target; + + spi_min_period(tp->starget) = esp->min_period; + spi_max_offset(tp->starget) = 15; + + if (esp->flags & ESP_FLAG_WIDE_CAPABLE) + spi_max_width(tp->starget) = 1; + else + spi_max_width(tp->starget) = 0; + + return 0; +} + +static int esp_slave_configure(struct scsi_device *dev) +{ + struct esp *esp = host_to_esp(dev->host); + struct esp_target_data *tp = &esp->target[dev->id]; + int goal_tags, queue_depth; + + goal_tags = 0; + + if (dev->tagged_supported) { + /* XXX make this configurable somehow XXX */ + goal_tags = ESP_DEFAULT_TAGS; + + if (goal_tags > ESP_MAX_TAG) + goal_tags = ESP_MAX_TAG; + } + + queue_depth = goal_tags; + if (queue_depth < dev->host->cmd_per_lun) + queue_depth = dev->host->cmd_per_lun; + + if (goal_tags) { + scsi_set_tag_type(dev, MSG_ORDERED_TAG); + scsi_activate_tcq(dev, queue_depth); + } else { + scsi_deactivate_tcq(dev, queue_depth); + } + tp->flags |= ESP_TGT_DISCONNECT; + + if (!spi_initial_dv(dev->sdev_target)) + spi_dv_device(dev); + + return 0; +} + +static void esp_slave_destroy(struct scsi_device *dev) +{ + struct esp_lun_data *lp = dev->hostdata; + + kfree(lp); + dev->hostdata = NULL; +} + +static int esp_eh_abort_handler(struct scsi_cmnd *cmd) +{ + struct esp *esp = host_to_esp(cmd->device->host); + struct esp_cmd_entry *ent, *tmp; + struct completion eh_done; + unsigned long flags; + + /* XXX This helps a lot with debugging but might be a bit + * XXX much for the final driver. + */ + spin_lock_irqsave(esp->host->host_lock, flags); + printk(KERN_ERR PFX "esp%d: Aborting command [%p:%02x]\n", + esp->host->unique_id, cmd, cmd->cmnd[0]); + ent = esp->active_cmd; + if (ent) + printk(KERN_ERR PFX "esp%d: Current command [%p:%02x]\n", + esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + list_for_each_entry(ent, &esp->queued_cmds, list) { + printk(KERN_ERR PFX "esp%d: Queued command [%p:%02x]\n", + esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + } + list_for_each_entry(ent, &esp->active_cmds, list) { + printk(KERN_ERR PFX "esp%d: Active command [%p:%02x]\n", + esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + } + esp_dump_cmd_log(esp); + spin_unlock_irqrestore(esp->host->host_lock, flags); + + spin_lock_irqsave(esp->host->host_lock, flags); + + ent = NULL; + list_for_each_entry(tmp, &esp->queued_cmds, list) { + if (tmp->cmd == cmd) { + ent = tmp; + break; + } + } + + if (ent) { + /* Easiest case, we didn't even issue the command + * yet so it is trivial to abort. + */ + list_del(&ent->list); + + cmd->result = DID_ABORT << 16; + cmd->scsi_done(cmd); + + esp_put_ent(esp, ent); + + goto out_success; + } + + init_completion(&eh_done); + + ent = esp->active_cmd; + if (ent && ent->cmd == cmd) { + /* Command is the currently active command on + * the bus. If we already have an output message + * pending, no dice. + */ + if (esp->msg_out_len) + goto out_failure; + + /* Send out an abort, encouraging the target to + * go to MSGOUT phase by asserting ATN. + */ + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + ent->eh_done = &eh_done; + + scsi_esp_cmd(esp, ESP_CMD_SATN); + } else { + /* The command is disconnected. This is not easy to + * abort. For now we fail and let the scsi error + * handling layer go try a scsi bus reset or host + * reset. + * + * What we could do is put together a scsi command + * solely for the purpose of sending an abort message + * to the target. Coming up with all the code to + * cook up scsi commands, special case them everywhere, + * etc. is for questionable gain and it would be better + * if the generic scsi error handling layer could do at + * least some of that for us. + * + * Anyways this is an area for potential future improvement + * in this driver. + */ + goto out_failure; + } + + spin_unlock_irqrestore(esp->host->host_lock, flags); + + if (!wait_for_completion_timeout(&eh_done, 5 * HZ)) { + spin_lock_irqsave(esp->host->host_lock, flags); + ent->eh_done = NULL; + spin_unlock_irqrestore(esp->host->host_lock, flags); + + return FAILED; + } + + return SUCCESS; + +out_success: + spin_unlock_irqrestore(esp->host->host_lock, flags); + return SUCCESS; + +out_failure: + /* XXX This might be a good location to set ESP_TGT_BROKEN + * XXX since we know which target/lun in particular is + * XXX causing trouble. + */ + spin_unlock_irqrestore(esp->host->host_lock, flags); + return FAILED; +} + +static int esp_eh_bus_reset_handler(struct scsi_cmnd *cmd) +{ + struct esp *esp = host_to_esp(cmd->device->host); + struct completion eh_reset; + unsigned long flags; + + init_completion(&eh_reset); + + spin_lock_irqsave(esp->host->host_lock, flags); + + esp->eh_reset = &eh_reset; + + /* XXX This is too simple... We should add lots of + * XXX checks here so that if we find that the chip is + * XXX very wedged we return failure immediately so + * XXX that we can perform a full chip reset. + */ + esp->flags |= ESP_FLAG_RESETTING; + scsi_esp_cmd(esp, ESP_CMD_RS); + + spin_unlock_irqrestore(esp->host->host_lock, flags); + + ssleep(esp_bus_reset_settle); + + if (!wait_for_completion_timeout(&eh_reset, 5 * HZ)) { + spin_lock_irqsave(esp->host->host_lock, flags); + esp->eh_reset = NULL; + spin_unlock_irqrestore(esp->host->host_lock, flags); + + return FAILED; + } + + return SUCCESS; +} + +/* All bets are off, reset the entire device. */ +static int esp_eh_host_reset_handler(struct scsi_cmnd *cmd) +{ + struct esp *esp = host_to_esp(cmd->device->host); + unsigned long flags; + + spin_lock_irqsave(esp->host->host_lock, flags); + esp_bootup_reset(esp); + esp_reset_cleanup(esp); + spin_unlock_irqrestore(esp->host->host_lock, flags); + + ssleep(esp_bus_reset_settle); + + return SUCCESS; +} + +static const char *esp_info(struct Scsi_Host *host) +{ + return "esp"; +} + +struct scsi_host_template scsi_esp_template = { + .module = THIS_MODULE, + .name = "esp", + .info = esp_info, + .queuecommand = esp_queuecommand, + .slave_alloc = esp_slave_alloc, + .slave_configure = esp_slave_configure, + .slave_destroy = esp_slave_destroy, + .eh_abort_handler = esp_eh_abort_handler, + .eh_bus_reset_handler = esp_eh_bus_reset_handler, + .eh_host_reset_handler = esp_eh_host_reset_handler, + .can_queue = 7, + .this_id = 7, + .sg_tablesize = SG_ALL, + .use_clustering = ENABLE_CLUSTERING, + .max_sectors = 0xffff, + .skip_settle_delay = 1, +}; +EXPORT_SYMBOL(scsi_esp_template); + +static void esp_get_signalling(struct Scsi_Host *host) +{ + struct esp *esp = host_to_esp(host); + enum spi_signal_type type; + + if (esp->flags & ESP_FLAG_DIFFERENTIAL) + type = SPI_SIGNAL_HVD; + else + type = SPI_SIGNAL_SE; + + spi_signalling(host) = type; +} + +static void esp_set_offset(struct scsi_target *target, int offset) +{ + struct Scsi_Host *host = dev_to_shost(target->dev.parent); + struct esp *esp = host_to_esp(host); + struct esp_target_data *tp = &esp->target[target->id]; + + tp->nego_goal_offset = offset; + tp->flags |= ESP_TGT_CHECK_NEGO; +} + +static void esp_set_period(struct scsi_target *target, int period) +{ + struct Scsi_Host *host = dev_to_shost(target->dev.parent); + struct esp *esp = host_to_esp(host); + struct esp_target_data *tp = &esp->target[target->id]; + + tp->nego_goal_period = period; + tp->flags |= ESP_TGT_CHECK_NEGO; +} + +static void esp_set_width(struct scsi_target *target, int width) +{ + struct Scsi_Host *host = dev_to_shost(target->dev.parent); + struct esp *esp = host_to_esp(host); + struct esp_target_data *tp = &esp->target[target->id]; + + tp->nego_goal_width = (width ? 1 : 0); + tp->flags |= ESP_TGT_CHECK_NEGO; +} + +static struct spi_function_template esp_transport_ops = { + .set_offset = esp_set_offset, + .show_offset = 1, + .set_period = esp_set_period, + .show_period = 1, + .set_width = esp_set_width, + .show_width = 1, + .get_signalling = esp_get_signalling, +}; + +static int __init esp_init(void) +{ + BUILD_BUG_ON(sizeof(struct scsi_pointer) < + sizeof(struct esp_cmd_priv)); + + esp_transport_template = spi_attach_transport(&esp_transport_ops); + if (!esp_transport_template) + return -ENODEV; + + return 0; +} + +static void __exit esp_exit(void) +{ + spi_release_transport(esp_transport_template); +} + +MODULE_DESCRIPTION("ESP SCSI driver core"); +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_param(esp_bus_reset_settle, int, 0); +MODULE_PARM_DESC(esp_bus_reset_settle, + "ESP scsi bus reset delay in seconds"); + +module_param(esp_debug, int, 0); +MODULE_PARM_DESC(esp_debug, +"ESP bitmapped debugging message enable value:\n" +" 0x00000001 Log interrupt events\n" +" 0x00000002 Log scsi commands\n" +" 0x00000004 Log resets\n" +" 0x00000008 Log message in events\n" +" 0x00000010 Log message out events\n" +" 0x00000020 Log command completion\n" +" 0x00000040 Log disconnects\n" +" 0x00000080 Log data start\n" +" 0x00000100 Log data done\n" +" 0x00000200 Log reconnects\n" +" 0x00000400 Log auto-sense data\n" +); + +module_init(esp_init); +module_exit(esp_exit); diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h new file mode 100644 index 0000000..8d4a669 --- /dev/null +++ b/drivers/scsi/esp_scsi.h @@ -0,0 +1,560 @@ +/* esp_scsi.h: Defines and structures for the ESP drier. + * + * Copyright (C) 2007 David S. Miller (davem@davemloft.net) + */ + +#ifndef _ESP_SCSI_H +#define _ESP_SCSI_H + + /* Access Description Offset */ +#define ESP_TCLOW 0x00UL /* rw Low bits transfer count 0x00 */ +#define ESP_TCMED 0x01UL /* rw Mid bits transfer count 0x04 */ +#define ESP_FDATA 0x02UL /* rw FIFO data bits 0x08 */ +#define ESP_CMD 0x03UL /* rw SCSI command bits 0x0c */ +#define ESP_STATUS 0x04UL /* ro ESP status register 0x10 */ +#define ESP_BUSID ESP_STATUS /* wo BusID for sel/resel 0x10 */ +#define ESP_INTRPT 0x05UL /* ro Kind of interrupt 0x14 */ +#define ESP_TIMEO ESP_INTRPT /* wo Timeout for sel/resel 0x14 */ +#define ESP_SSTEP 0x06UL /* ro Sequence step register 0x18 */ +#define ESP_STP ESP_SSTEP /* wo Transfer period/sync 0x18 */ +#define ESP_FFLAGS 0x07UL /* ro Bits current FIFO info 0x1c */ +#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */ +#define ESP_CFG1 0x08UL /* rw First cfg register 0x20 */ +#define ESP_CFACT 0x09UL /* wo Clock conv factor 0x24 */ +#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */ +#define ESP_CTEST 0x0aUL /* wo Chip test register 0x28 */ +#define ESP_CFG2 0x0bUL /* rw Second cfg register 0x2c */ +#define ESP_CFG3 0x0cUL /* rw Third cfg register 0x30 */ +#define ESP_TCHI 0x0eUL /* rw High bits transf count 0x38 */ +#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */ +#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */ +#define ESP_FGRND 0x0fUL /* rw Data base for fifo 0x3c */ +#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */ + +#define SBUS_ESP_REG_SIZE 0x40UL + +/* Bitfield meanings for the above registers. */ + +/* ESP config reg 1, read-write, found on all ESP chips */ +#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */ +#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */ +#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */ +#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */ +#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */ +#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */ + +/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */ +#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */ +#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */ +#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */ +#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tgtmode) */ +#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */ +#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */ +#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */ +#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */ +#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,216) */ +#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (236) */ +#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */ +#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */ +#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */ + +/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */ +#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */ +#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */ +#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */ +#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */ +#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */ +#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */ +#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */ +#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */ +#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ +#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ +#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ +#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */ +#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ +#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ +#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ +#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ + +/* ESP command register read-write */ +/* Group 1 commands: These may be sent at any point in time to the ESP + * chip. None of them can generate interrupts 'cept + * the "SCSI bus reset" command if you have not disabled + * SCSI reset interrupts in the config1 ESP register. + */ +#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */ +#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */ +#define ESP_CMD_RC 0x02 /* Chip reset */ +#define ESP_CMD_RS 0x03 /* SCSI bus reset */ + +/* Group 2 commands: ESP must be an initiator and connected to a target + * for these commands to work. + */ +#define ESP_CMD_TI 0x10 /* Transfer Information */ +#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */ +#define ESP_CMD_MOK 0x12 /* Message okie-dokie */ +#define ESP_CMD_TPAD 0x18 /* Transfer Pad */ +#define ESP_CMD_SATN 0x1a /* Set ATN */ +#define ESP_CMD_RATN 0x1b /* De-assert ATN */ + +/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected + * to a target as the initiator for these commands to work. + */ +#define ESP_CMD_SMSG 0x20 /* Send message */ +#define ESP_CMD_SSTAT 0x21 /* Send status */ +#define ESP_CMD_SDATA 0x22 /* Send data */ +#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */ +#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */ +#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */ +#define ESP_CMD_DCNCT 0x27 /* Disconnect */ +#define ESP_CMD_RMSG 0x28 /* Receive Message */ +#define ESP_CMD_RCMD 0x29 /* Receive Command */ +#define ESP_CMD_RDATA 0x2a /* Receive Data */ +#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */ + +/* Group 4 commands: The ESP must be in the disconnected state and must + * not be connected to any targets as initiator for + * these commands to work. + */ +#define ESP_CMD_RSEL 0x40 /* Reselect */ +#define ESP_CMD_SEL 0x41 /* Select w/o ATN */ +#define ESP_CMD_SELA 0x42 /* Select w/ATN */ +#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */ +#define ESP_CMD_ESEL 0x44 /* Enable selection */ +#define ESP_CMD_DSEL 0x45 /* Disable selections */ +#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */ +#define ESP_CMD_RSEL3 0x47 /* Reselect3 */ + +/* This bit enables the ESP's DMA on the SBus */ +#define ESP_CMD_DMA 0x80 /* Do DMA? */ + +/* ESP status register read-only */ +#define ESP_STAT_PIO 0x01 /* IO phase bit */ +#define ESP_STAT_PCD 0x02 /* CD phase bit */ +#define ESP_STAT_PMSG 0x04 /* MSG phase bit */ +#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */ +#define ESP_STAT_TDONE 0x08 /* Transfer Completed */ +#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */ +#define ESP_STAT_PERR 0x20 /* Parity error */ +#define ESP_STAT_SPAM 0x40 /* Real bad error */ +/* This indicates the 'interrupt pending' condition on esp236, it is a reserved + * bit on other revs of the ESP. + */ +#define ESP_STAT_INTR 0x80 /* Interrupt */ + +/* The status register can be masked with ESP_STAT_PMASK and compared + * with the following values to determine the current phase the ESP + * (at least thinks it) is in. For our purposes we also add our own + * software 'done' bit for our phase management engine. + */ +#define ESP_DOP (0) /* Data Out */ +#define ESP_DIP (ESP_STAT_PIO) /* Data In */ +#define ESP_CMDP (ESP_STAT_PCD) /* Command */ +#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */ +#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */ +#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */ + +/* HME only: status 2 register */ +#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */ +#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */ +#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */ +#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */ +#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */ +#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */ +#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */ +#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */ + +/* ESP interrupt register read-only */ +#define ESP_INTR_S 0x01 /* Select w/o ATN */ +#define ESP_INTR_SATN 0x02 /* Select w/ATN */ +#define ESP_INTR_RSEL 0x04 /* Reselected */ +#define ESP_INTR_FDONE 0x08 /* Function done */ +#define ESP_INTR_BSERV 0x10 /* Bus service */ +#define ESP_INTR_DC 0x20 /* Disconnect */ +#define ESP_INTR_IC 0x40 /* Illegal command given */ +#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */ + +/* ESP sequence step register read-only */ +#define ESP_STEP_VBITS 0x07 /* Valid bits */ +#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */ +#define ESP_STEP_SID 0x01 /* One msg byte sent */ +#define ESP_STEP_NCMD 0x02 /* Was not in command phase */ +#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd + * bytes to be lost + */ +#define ESP_STEP_FINI4 0x04 /* Command was sent ok */ + +/* Ho hum, some ESP's set the step register to this as well... */ +#define ESP_STEP_FINI5 0x05 +#define ESP_STEP_FINI6 0x06 +#define ESP_STEP_FINI7 0x07 + +/* ESP chip-test register read-write */ +#define ESP_TEST_TARG 0x01 /* Target test mode */ +#define ESP_TEST_INI 0x02 /* Initiator test mode */ +#define ESP_TEST_TS 0x04 /* Tristate test mode */ + +/* ESP unique ID register read-only, found on fas236+fas100a only */ +#define ESP_UID_F100A 0x00 /* ESP FAS100A */ +#define ESP_UID_F236 0x02 /* ESP FAS236 */ +#define ESP_UID_REV 0x07 /* ESP revision */ +#define ESP_UID_FAM 0xf8 /* ESP family */ + +/* ESP fifo flags register read-only */ +/* Note that the following implies a 16 byte FIFO on the ESP. */ +#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */ +#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */ +#define ESP_FF_SSTEP 0xe0 /* Sequence step */ + +/* ESP clock conversion factor register write-only */ +#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */ +#define ESP_CCF_NEVER 0x01 /* Set it to this and die */ +#define ESP_CCF_F2 0x02 /* 10MHz */ +#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */ +#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */ +#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */ +#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */ +#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */ + +/* HME only... */ +#define ESP_BUSID_RESELID 0x10 +#define ESP_BUSID_CTR32BIT 0x40 + +#define ESP_BUS_TIMEOUT 250 /* In milli-seconds */ +#define ESP_TIMEO_CONST 8192 +#define ESP_NEG_DEFP(mhz, cfact) \ + ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) +#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000)) +#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000)) + +/* For slow to medium speed input clock rates we shoot for 5mb/s, but for high + * input clock rates we try to do 10mb/s although I don't think a transfer can + * even run that fast with an ESP even with DMA2 scatter gather pipelining. + */ +#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */ +#define SYNC_DEFP_FAST 0x19 /* 10mb/s */ + +struct esp_cmd_priv { + union { + dma_addr_t dma_addr; + int num_sg; + } u; + + unsigned int cur_residue; + struct scatterlist *cur_sg; + unsigned int tot_residue; +}; +#define ESP_CMD_PRIV(CMD) ((struct esp_cmd_priv *)(&(CMD)->SCp)) + +enum esp_rev { + ESP100 = 0x00, /* NCR53C90 - very broken */ + ESP100A = 0x01, /* NCR53C90A */ + ESP236 = 0x02, + FAS236 = 0x03, + FAS100A = 0x04, + FAST = 0x05, + FASHME = 0x06, +}; + +struct esp_cmd_entry { + struct list_head list; + + struct scsi_cmnd *cmd; + + unsigned int saved_cur_residue; + struct scatterlist *saved_cur_sg; + unsigned int saved_tot_residue; + + u8 flags; +#define ESP_CMD_FLAG_WRITE 0x01 /* DMA is a write */ +#define ESP_CMD_FLAG_ABORT 0x02 /* being aborted */ +#define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */ + + u8 tag[2]; + + u8 status; + u8 message; + + unsigned char *sense_ptr; + unsigned char *saved_sense_ptr; + dma_addr_t sense_dma; + + struct completion *eh_done; +}; + +/* XXX make this configurable somehow XXX */ +#define ESP_DEFAULT_TAGS 16 + +#define ESP_MAX_TARGET 16 +#define ESP_MAX_LUN 8 +#define ESP_MAX_TAG 256 + +struct esp_lun_data { + struct esp_cmd_entry *non_tagged_cmd; + int num_tagged; + int hold; + struct esp_cmd_entry *tagged_cmds[ESP_MAX_TAG]; +}; + +struct esp_target_data { + /* These are the ESP_STP, ESP_SOFF, and ESP_CFG3 register values which + * match the currently negotiated settings for this target. The SCSI + * protocol values are maintained in spi_{offset,period,wide}(starget). + */ + u8 esp_period; + u8 esp_offset; + u8 esp_config3; + + u8 flags; +#define ESP_TGT_WIDE 0x01 +#define ESP_TGT_DISCONNECT 0x02 +#define ESP_TGT_NEGO_WIDE 0x04 +#define ESP_TGT_NEGO_SYNC 0x08 +#define ESP_TGT_CHECK_NEGO 0x40 +#define ESP_TGT_BROKEN 0x80 + + /* When ESP_TGT_CHECK_NEGO is set, on the next scsi command to this + * device we will try to negotiate the following parameters. + */ + u8 nego_goal_period; + u8 nego_goal_offset; + u8 nego_goal_width; + u8 nego_goal_tags; + + struct scsi_target *starget; +}; + +struct esp_event_ent { + u8 type; +#define ESP_EVENT_TYPE_EVENT 0x01 +#define ESP_EVENT_TYPE_CMD 0x02 + u8 val; + + u8 sreg; + u8 seqreg; + u8 sreg2; + u8 ireg; + u8 select_state; + u8 event; + u8 __pad; +}; + +struct esp; +struct esp_driver_ops { + /* Read and write the ESP 8-bit registers. On some + * applications of the ESP chip the registers are at 4-byte + * instead of 1-byte intervals. + */ + void (*esp_write8)(struct esp *esp, u8 val, unsigned long reg); + u8 (*esp_read8)(struct esp *esp, unsigned long reg); + + /* Map and unmap DMA memory. Eventually the driver will be + * converted to the generic DMA API as soon as SBUS is able to + * cope with that. At such time we can remove this. + */ + dma_addr_t (*map_single)(struct esp *esp, void *buf, + size_t sz, int dir); + int (*map_sg)(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir); + void (*unmap_single)(struct esp *esp, dma_addr_t addr, + size_t sz, int dir); + void (*unmap_sg)(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir); + + /* Return non-zero if there is an IRQ pending. Usually this + * status bit lives in the DMA controller sitting in front of + * the ESP. This has to be accurate or else the ESP interrupt + * handler will not run. + */ + int (*irq_pending)(struct esp *esp); + + /* Reset the DMA engine entirely. On return, ESP interrupts + * should be enabled. Often the interrupt enabling is + * controlled in the DMA engine. + */ + void (*reset_dma)(struct esp *esp); + + /* Drain any pending DMA in the DMA engine after a transfer. + * This is for writes to memory. + */ + void (*dma_drain)(struct esp *esp); + + /* Invalidate the DMA engine after a DMA transfer. */ + void (*dma_invalidate)(struct esp *esp); + + /* Setup an ESP command that will use a DMA transfer. + * The 'esp_count' specifies what transfer length should be + * programmed into the ESP transfer counter registers, whereas + * the 'dma_count' is the length that should be programmed into + * the DMA controller. Usually they are the same. If 'write' + * is non-zero, this transfer is a write into memory. 'cmd' + * holds the ESP command that should be issued by calling + * scsi_esp_cmd() at the appropriate time while programming + * the DMA hardware. + */ + void (*send_dma_cmd)(struct esp *esp, u32 dma_addr, u32 esp_count, + u32 dma_count, int write, u8 cmd); + + /* Return non-zero if the DMA engine is reporting an error + * currently. + */ + int (*dma_error)(struct esp *esp); +}; + +#define ESP_MAX_MSG_SZ 8 +#define ESP_EVENT_LOG_SZ 32 + +#define ESP_QUICKIRQ_LIMIT 100 +#define ESP_RESELECT_TAG_LIMIT 2500 + +struct esp { + void __iomem *regs; + void __iomem *dma_regs; + + const struct esp_driver_ops *ops; + + struct Scsi_Host *host; + void *dev; + + struct esp_cmd_entry *active_cmd; + + struct list_head queued_cmds; + struct list_head active_cmds; + + u8 *command_block; + dma_addr_t command_block_dma; + + unsigned int data_dma_len; + + /* The following are used to determine the cause of an IRQ. Upon every + * IRQ entry we synchronize these with the hardware registers. + */ + u8 sreg; + u8 seqreg; + u8 sreg2; + u8 ireg; + + u32 prev_hme_dmacsr; + u8 prev_soff; + u8 prev_stp; + u8 prev_cfg3; + u8 __pad; + + struct list_head esp_cmd_pool; + + struct esp_target_data target[ESP_MAX_TARGET]; + + int fifo_cnt; + u8 fifo[16]; + + struct esp_event_ent esp_event_log[ESP_EVENT_LOG_SZ]; + int esp_event_cur; + + u8 msg_out[ESP_MAX_MSG_SZ]; + int msg_out_len; + + u8 msg_in[ESP_MAX_MSG_SZ]; + int msg_in_len; + + u8 bursts; + u8 config1; + u8 config2; + + u8 scsi_id; + u32 scsi_id_mask; + + enum esp_rev rev; + + u32 flags; +#define ESP_FLAG_DIFFERENTIAL 0x00000001 +#define ESP_FLAG_RESETTING 0x00000002 +#define ESP_FLAG_DOING_SLOWCMD 0x00000004 +#define ESP_FLAG_WIDE_CAPABLE 0x00000008 +#define ESP_FLAG_QUICKIRQ_CHECK 0x00000010 + + u8 select_state; +#define ESP_SELECT_NONE 0x00 /* Not selecting */ +#define ESP_SELECT_BASIC 0x01 /* Select w/o MSGOUT phase */ +#define ESP_SELECT_MSGOUT 0x02 /* Select with MSGOUT */ + + /* When we are not selecting, we are expecting an event. */ + u8 event; +#define ESP_EVENT_NONE 0x00 +#define ESP_EVENT_CMD_START 0x01 +#define ESP_EVENT_CMD_DONE 0x02 +#define ESP_EVENT_DATA_IN 0x03 +#define ESP_EVENT_DATA_OUT 0x04 +#define ESP_EVENT_DATA_DONE 0x05 +#define ESP_EVENT_MSGIN 0x06 +#define ESP_EVENT_MSGIN_MORE 0x07 +#define ESP_EVENT_MSGIN_DONE 0x08 +#define ESP_EVENT_MSGOUT 0x09 +#define ESP_EVENT_MSGOUT_DONE 0x0a +#define ESP_EVENT_STATUS 0x0b +#define ESP_EVENT_FREE_BUS 0x0c +#define ESP_EVENT_CHECK_PHASE 0x0d +#define ESP_EVENT_RESET 0x10 + + /* Probed in esp_get_clock_params() */ + u32 cfact; + u32 cfreq; + u32 ccycle; + u32 ctick; + u32 neg_defp; + u32 sync_defp; + + /* Computed in esp_reset_esp() */ + u32 max_period; + u32 min_period; + u32 radelay; + + /* Slow command state. */ + u8 *cmd_bytes_ptr; + int cmd_bytes_left; + + struct completion *eh_reset; + + struct sbus_dma *dma; +}; + +#define host_to_esp(host) ((struct esp *)(host)->hostdata) + +/* A front-end driver for the ESP chip should do the following in + * it's device probe routine: + * 1) Allocate the host and private area using scsi_host_alloc() + * with size 'sizeof(struct esp)'. The first argument to + * scsi_host_alloc() should be &scsi_esp_template. + * 2) Set host->max_id as appropriate. + * 3) Set esp->host to the scsi_host itself, and esp->dev + * to the device object pointer. + * 4) Hook up esp->ops to the front-end implementation. + * 5) If the ESP chip supports wide transfers, set ESP_FLAG_WIDE_CAPABLE + * in esp->flags. + * 6) Map the DMA and ESP chip registers. + * 7) DMA map the ESP command block, store the DMA address + * in esp->command_block_dma. + * 8) Register the scsi_esp_intr() interrupt handler. + * 9) Probe for and provide the following chip properties: + * esp->scsi_id (assign to esp->host->this_id too) + * esp->scsi_id_mask + * If ESP bus is differential, set ESP_FLAG_DIFFERENTIAL + * esp->cfreq + * DMA burst bit mask in esp->bursts, if necessary + * 10) Perform any actions necessary before the ESP device can + * be programmed for the first time. On some configs, for + * example, the DMA engine has to be reset before ESP can + * be programmed. + * 11) If necessary, call dev_set_drvdata() as needed. + * 12) Call scsi_esp_register() with prepared 'esp' structure + * and a device pointer if possible. + * 13) Check scsi_esp_register() return value, release all resources + * if an error was returned. + */ +extern struct scsi_host_template scsi_esp_template; +extern int scsi_esp_register(struct esp *, struct device *); + +extern void scsi_esp_unregister(struct esp *); +extern irqreturn_t scsi_esp_intr(int, void *); +extern void scsi_esp_cmd(struct esp *, u8); + +#endif /* !(_ESP_SCSI_H) */ diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 38c3a29..bd8e7f3 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -435,7 +435,7 @@ struct Scsi_Host *scsi_host_lookup(unsig struct class_device *cdev; struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p; - down_read(&class->subsys.rwsem); + down(&class->sem); list_for_each_entry(cdev, &class->children, node) { p = class_to_shost(cdev); if (p->host_no == hostnum) { @@ -443,7 +443,7 @@ struct Scsi_Host *scsi_host_lookup(unsig break; } } - up_read(&class->subsys.rwsem); + up(&class->sem); return shost; } diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index 4368ca0..a39a478 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -897,9 +897,9 @@ static int get_system_info(void) { struct device_node *rootdn; const char *id, *model, *name; - unsigned int *num; + const unsigned int *num; - rootdn = find_path_device("/"); + rootdn = of_find_node_by_path("/"); if (!rootdn) return -ENOENT; @@ -912,10 +912,11 @@ static int get_system_info(void) if (name) strncpy(partition_name, name, sizeof(partition_name)); - num = (unsigned int *) get_property(rootdn, "ibm,partition-no", NULL); + num = get_property(rootdn, "ibm,partition-no", NULL); if (num) partition_number = *num; + of_node_put(rootdn); return 0; } diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 227c0f2..0a533f3 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -157,7 +157,7 @@ static void gather_partition_info(void) const unsigned int *p_number_ptr; /* Retrieve information about this partition */ - rootdn = find_path_device("/"); + rootdn = of_find_node_by_path("/"); if (!rootdn) { return; } @@ -169,6 +169,7 @@ static void gather_partition_info(void) p_number_ptr = get_property(rootdn, "ibm,partition-no", NULL); if (p_number_ptr) partition_number = *p_number_ptr; + of_node_put(rootdn); } static void set_adapter_info(struct ibmvscsi_host_data *hostdata) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 95045e3..e9bd299 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3770,7 +3770,8 @@ static int ipr_device_reset(struct ipr_i * Return value: * 0 on success / non-zero on failure **/ -static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes) +static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes, + unsigned long deadline) { struct ipr_sata_port *sata_port = ap->private_data; struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg; diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 9f10689..c4195ea 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1403,7 +1403,7 @@ static int __devinit qpti_sbus_probe(str struct scsi_host_template *tpnt = match->data; struct Scsi_Host *host; struct qlogicpti *qpti; - char *fcode; + const char *fcode; /* Sometimes Antares cards come up not completely * setup, and we get a report of a zero IRQ. diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9f7482d..05d79af 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -31,7 +31,7 @@ #include "scsi_logging.h" #define SG_MEMPOOL_NR ARRAY_SIZE(scsi_sg_pools) -#define SG_MEMPOOL_SIZE 32 +#define SG_MEMPOOL_SIZE 2 struct scsi_host_sg_pool { size_t size; diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index 1b59b27..4bf9aa5 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -50,7 +50,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb) while (skb->len >= NLMSG_SPACE(0)) { err = 0; - nlh = (struct nlmsghdr *) skb->data; + nlh = nlmsg_hdr(skb); if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) || (skb->len < nlh->nlmsg_len)) { printk(KERN_WARNING "%s: discarding partial skb\n", @@ -168,7 +168,8 @@ scsi_netlink_init(void) } scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT, - SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE); + SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL, + THIS_MODULE); if (!scsi_nl_sock) { printk(KERN_ERR "%s: register of recieve handler failed\n", __FUNCTION__); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index ce0d14a..aabaa05 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1081,7 +1081,7 @@ iscsi_if_rx(struct sock *sk, int len) struct nlmsghdr *nlh; struct iscsi_uevent *ev; - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) { break; @@ -1435,7 +1435,7 @@ static __init int iscsi_transport_init(v if (err) goto unregister_conn_class; - nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, + nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL, THIS_MODULE); if (!nls) { err = -ENOBUFS; diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c new file mode 100644 index 0000000..8c766bc --- /dev/null +++ b/drivers/scsi/sun_esp.c @@ -0,0 +1,634 @@ +/* sun_esp.c: ESP front-end for Sparc SBUS systems. + * + * Copyright (C) 2007 David S. Miller (davem@davemloft.net) + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "esp_scsi.h" + +#define DRV_MODULE_NAME "sun_esp" +#define PFX DRV_MODULE_NAME ": " +#define DRV_VERSION "1.000" +#define DRV_MODULE_RELDATE "April 19, 2007" + +#define dma_read32(REG) \ + sbus_readl(esp->dma_regs + (REG)) +#define dma_write32(VAL, REG) \ + sbus_writel((VAL), esp->dma_regs + (REG)) + +static int __devinit esp_sbus_find_dma(struct esp *esp, struct sbus_dev *dma_sdev) +{ + struct sbus_dev *sdev = esp->dev; + struct sbus_dma *dma; + + if (dma_sdev != NULL) { + for_each_dvma(dma) { + if (dma->sdev == dma_sdev) + break; + } + } else { + for_each_dvma(dma) { + if (dma->sdev == NULL) + break; + + /* If bus + slot are the same and it has the + * correct OBP name, it's ours. + */ + if (sdev->bus == dma->sdev->bus && + sdev->slot == dma->sdev->slot && + (!strcmp(dma->sdev->prom_name, "dma") || + !strcmp(dma->sdev->prom_name, "espdma"))) + break; + } + } + + if (dma == NULL) { + printk(KERN_ERR PFX "[%s] Cannot find dma.\n", + sdev->ofdev.node->full_name); + return -ENODEV; + } + esp->dma = dma; + esp->dma_regs = dma->regs; + + return 0; + +} + +static int __devinit esp_sbus_map_regs(struct esp *esp, int hme) +{ + struct sbus_dev *sdev = esp->dev; + struct resource *res; + + /* On HME, two reg sets exist, first is DVMA, + * second is ESP registers. + */ + if (hme) + res = &sdev->resource[1]; + else + res = &sdev->resource[0]; + + esp->regs = sbus_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP"); + if (!esp->regs) + return -ENOMEM; + + return 0; +} + +static int __devinit esp_sbus_map_command_block(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + + esp->command_block = sbus_alloc_consistent(sdev, 16, + &esp->command_block_dma); + if (!esp->command_block) + return -ENOMEM; + return 0; +} + +static int __devinit esp_sbus_register_irq(struct esp *esp) +{ + struct Scsi_Host *host = esp->host; + struct sbus_dev *sdev = esp->dev; + + host->irq = sdev->irqs[0]; + return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp); +} + +static void __devinit esp_get_scsi_id(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + + esp->scsi_id = of_getintprop_default(dp, "initiator-id", 0xff); + if (esp->scsi_id != 0xff) + goto done; + + esp->scsi_id = of_getintprop_default(dp, "scsi-initiator-id", 0xff); + if (esp->scsi_id != 0xff) + goto done; + + if (!sdev->bus) { + /* SUN4 */ + esp->scsi_id = 7; + goto done; + } + + esp->scsi_id = of_getintprop_default(sdev->bus->ofdev.node, + "scsi-initiator-id", 7); + +done: + esp->host->this_id = esp->scsi_id; + esp->scsi_id_mask = (1 << esp->scsi_id); +} + +static void __devinit esp_get_differential(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + + if (of_find_property(dp, "differential", NULL)) + esp->flags |= ESP_FLAG_DIFFERENTIAL; + else + esp->flags &= ~ESP_FLAG_DIFFERENTIAL; +} + +static void __devinit esp_get_clock_params(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + struct device_node *bus_dp; + int fmhz; + + bus_dp = NULL; + if (sdev != NULL && sdev->bus != NULL) + bus_dp = sdev->bus->ofdev.node; + + fmhz = of_getintprop_default(dp, "clock-frequency", 0); + if (fmhz == 0) + fmhz = (!bus_dp) ? 0 : + of_getintprop_default(bus_dp, "clock-frequency", 0); + + esp->cfreq = fmhz; +} + +static void __devinit esp_get_bursts(struct esp *esp, struct sbus_dev *dma) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + u8 bursts; + + bursts = of_getintprop_default(dp, "burst-sizes", 0xff); + if (dma) { + struct device_node *dma_dp = dma->ofdev.node; + u8 val = of_getintprop_default(dma_dp, "burst-sizes", 0xff); + if (val != 0xff) + bursts &= val; + } + + if (sdev->bus) { + u8 val = of_getintprop_default(sdev->bus->ofdev.node, + "burst-sizes", 0xff); + if (val != 0xff) + bursts &= val; + } + + if (bursts == 0xff || + (bursts & DMA_BURST16) == 0 || + (bursts & DMA_BURST32) == 0) + bursts = (DMA_BURST32 - 1); + + esp->bursts = bursts; +} + +static void __devinit esp_sbus_get_props(struct esp *esp, struct sbus_dev *espdma) +{ + esp_get_scsi_id(esp); + esp_get_differential(esp); + esp_get_clock_params(esp); + esp_get_bursts(esp, espdma); +} + +static void sbus_esp_write8(struct esp *esp, u8 val, unsigned long reg) +{ + sbus_writeb(val, esp->regs + (reg * 4UL)); +} + +static u8 sbus_esp_read8(struct esp *esp, unsigned long reg) +{ + return sbus_readb(esp->regs + (reg * 4UL)); +} + +static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf, + size_t sz, int dir) +{ + return sbus_map_single(esp->dev, buf, sz, dir); +} + +static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + return sbus_map_sg(esp->dev, sg, num_sg, dir); +} + +static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr, + size_t sz, int dir) +{ + sbus_unmap_single(esp->dev, addr, sz, dir); +} + +static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + sbus_unmap_sg(esp->dev, sg, num_sg, dir); +} + +static int sbus_esp_irq_pending(struct esp *esp) +{ + if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) + return 1; + return 0; +} + +static void sbus_esp_reset_dma(struct esp *esp) +{ + int can_do_burst16, can_do_burst32, can_do_burst64; + int can_do_sbus64, lim; + u32 val; + + can_do_burst16 = (esp->bursts & DMA_BURST16) != 0; + can_do_burst32 = (esp->bursts & DMA_BURST32) != 0; + can_do_burst64 = 0; + can_do_sbus64 = 0; + if (sbus_can_dma_64bit(esp->dev)) + can_do_sbus64 = 1; + if (sbus_can_burst64(esp->sdev)) + can_do_burst64 = (esp->bursts & DMA_BURST64) != 0; + + /* Put the DVMA into a known state. */ + if (esp->dma->revision != dvmahme) { + val = dma_read32(DMA_CSR); + dma_write32(val | DMA_RST_SCSI, DMA_CSR); + dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); + } + switch (esp->dma->revision) { + case dvmahme: + dma_write32(DMA_RESET_FAS366, DMA_CSR); + dma_write32(DMA_RST_SCSI, DMA_CSR); + + esp->prev_hme_dmacsr = (DMA_PARITY_OFF | DMA_2CLKS | + DMA_SCSI_DISAB | DMA_INT_ENAB); + + esp->prev_hme_dmacsr &= ~(DMA_ENABLE | DMA_ST_WRITE | + DMA_BRST_SZ); + + if (can_do_burst64) + esp->prev_hme_dmacsr |= DMA_BRST64; + else if (can_do_burst32) + esp->prev_hme_dmacsr |= DMA_BRST32; + + if (can_do_sbus64) { + esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64; + sbus_set_sbus64(esp->dev, esp->bursts); + } + + lim = 1000; + while (dma_read32(DMA_CSR) & DMA_PEND_READ) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: DMA_PEND_READ " + "will not clear!\n", + esp->host->unique_id); + break; + } + udelay(1); + } + + dma_write32(0, DMA_CSR); + dma_write32(esp->prev_hme_dmacsr, DMA_CSR); + + dma_write32(0, DMA_ADDR); + break; + + case dvmarev2: + if (esp->rev != ESP100) { + val = dma_read32(DMA_CSR); + dma_write32(val | DMA_3CLKS, DMA_CSR); + } + break; + + case dvmarev3: + val = dma_read32(DMA_CSR); + val &= ~DMA_3CLKS; + val |= DMA_2CLKS; + if (can_do_burst32) { + val &= ~DMA_BRST_SZ; + val |= DMA_BRST32; + } + dma_write32(val, DMA_CSR); + break; + + case dvmaesc1: + val = dma_read32(DMA_CSR); + val |= DMA_ADD_ENABLE; + val &= ~DMA_BCNT_ENAB; + if (!can_do_burst32 && can_do_burst16) { + val |= DMA_ESC_BURST; + } else { + val &= ~(DMA_ESC_BURST); + } + dma_write32(val, DMA_CSR); + break; + + default: + break; + } + + /* Enable interrupts. */ + val = dma_read32(DMA_CSR); + dma_write32(val | DMA_INT_ENAB, DMA_CSR); +} + +static void sbus_esp_dma_drain(struct esp *esp) +{ + u32 csr; + int lim; + + if (esp->dma->revision == dvmahme) + return; + + csr = dma_read32(DMA_CSR); + if (!(csr & DMA_FIFO_ISDRAIN)) + return; + + if (esp->dma->revision != dvmarev3 && esp->dma->revision != dvmaesc1) + dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR); + + lim = 1000; + while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n", + esp->host->unique_id); + break; + } + udelay(1); + } +} + +static void sbus_esp_dma_invalidate(struct esp *esp) +{ + if (esp->dma->revision == dvmahme) { + dma_write32(DMA_RST_SCSI, DMA_CSR); + + esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | + (DMA_PARITY_OFF | DMA_2CLKS | + DMA_SCSI_DISAB | DMA_INT_ENAB)) & + ~(DMA_ST_WRITE | DMA_ENABLE)); + + dma_write32(0, DMA_CSR); + dma_write32(esp->prev_hme_dmacsr, DMA_CSR); + + /* This is necessary to avoid having the SCSI channel + * engine lock up on us. + */ + dma_write32(0, DMA_ADDR); + } else { + u32 val; + int lim; + + lim = 1000; + while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: DMA will not " + "invalidate!\n", esp->host->unique_id); + break; + } + udelay(1); + } + + val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); + val |= DMA_FIFO_INV; + dma_write32(val, DMA_CSR); + val &= ~DMA_FIFO_INV; + dma_write32(val, DMA_CSR); + } +} + +static void sbus_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, + u32 dma_count, int write, u8 cmd) +{ + u32 csr; + + BUG_ON(!(cmd & ESP_CMD_DMA)); + + sbus_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + sbus_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + if (esp->rev == FASHME) { + sbus_esp_write8(esp, (esp_count >> 16) & 0xff, FAS_RLO); + sbus_esp_write8(esp, 0, FAS_RHI); + + scsi_esp_cmd(esp, cmd); + + csr = esp->prev_hme_dmacsr; + csr |= DMA_SCSI_DISAB | DMA_ENABLE; + if (write) + csr |= DMA_ST_WRITE; + else + csr &= ~DMA_ST_WRITE; + esp->prev_hme_dmacsr = csr; + + dma_write32(dma_count, DMA_COUNT); + dma_write32(addr, DMA_ADDR); + dma_write32(csr, DMA_CSR); + } else { + csr = dma_read32(DMA_CSR); + csr |= DMA_ENABLE; + if (write) + csr |= DMA_ST_WRITE; + else + csr &= ~DMA_ST_WRITE; + dma_write32(csr, DMA_CSR); + if (esp->dma->revision == dvmaesc1) { + u32 end = PAGE_ALIGN(addr + dma_count + 16U); + dma_write32(end - addr, DMA_COUNT); + } + dma_write32(addr, DMA_ADDR); + + scsi_esp_cmd(esp, cmd); + } + +} + +static int sbus_esp_dma_error(struct esp *esp) +{ + u32 csr = dma_read32(DMA_CSR); + + if (csr & DMA_HNDL_ERROR) + return 1; + + return 0; +} + +static const struct esp_driver_ops sbus_esp_ops = { + .esp_write8 = sbus_esp_write8, + .esp_read8 = sbus_esp_read8, + .map_single = sbus_esp_map_single, + .map_sg = sbus_esp_map_sg, + .unmap_single = sbus_esp_unmap_single, + .unmap_sg = sbus_esp_unmap_sg, + .irq_pending = sbus_esp_irq_pending, + .reset_dma = sbus_esp_reset_dma, + .dma_drain = sbus_esp_dma_drain, + .dma_invalidate = sbus_esp_dma_invalidate, + .send_dma_cmd = sbus_esp_send_dma_cmd, + .dma_error = sbus_esp_dma_error, +}; + +static int __devinit esp_sbus_probe_one(struct device *dev, + struct sbus_dev *esp_dev, + struct sbus_dev *espdma, + struct sbus_bus *sbus, + int hme) +{ + struct scsi_host_template *tpnt = &scsi_esp_template; + struct Scsi_Host *host; + struct esp *esp; + int err; + + host = scsi_host_alloc(tpnt, sizeof(struct esp)); + + err = -ENOMEM; + if (!host) + goto fail; + + host->max_id = (hme ? 16 : 8); + esp = host_to_esp(host); + + esp->host = host; + esp->dev = esp_dev; + esp->ops = &sbus_esp_ops; + + if (hme) + esp->flags |= ESP_FLAG_WIDE_CAPABLE; + + err = esp_sbus_find_dma(esp, espdma); + if (err < 0) + goto fail_unlink; + + err = esp_sbus_map_regs(esp, hme); + if (err < 0) + goto fail_unlink; + + err = esp_sbus_map_command_block(esp); + if (err < 0) + goto fail_unmap_regs; + + err = esp_sbus_register_irq(esp); + if (err < 0) + goto fail_unmap_command_block; + + esp_sbus_get_props(esp, espdma); + + /* Before we try to touch the ESP chip, ESC1 dma can + * come up with the reset bit set, so make sure that + * is clear first. + */ + if (esp->dma->revision == dvmaesc1) { + u32 val = dma_read32(DMA_CSR); + + dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); + } + + dev_set_drvdata(&esp_dev->ofdev.dev, esp); + + err = scsi_esp_register(esp, dev); + if (err) + goto fail_free_irq; + + return 0; + +fail_free_irq: + free_irq(host->irq, esp); +fail_unmap_command_block: + sbus_free_consistent(esp->dev, 16, + esp->command_block, + esp->command_block_dma); +fail_unmap_regs: + sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE); +fail_unlink: + scsi_host_put(host); +fail: + return err; +} + +static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match) +{ + struct sbus_dev *sdev = to_sbus_device(&dev->dev); + struct device_node *dp = dev->node; + struct sbus_dev *dma_sdev = NULL; + int hme = 0; + + if (dp->parent && + (!strcmp(dp->parent->name, "espdma") || + !strcmp(dp->parent->name, "dma"))) + dma_sdev = sdev->parent; + else if (!strcmp(dp->name, "SUNW,fas")) { + dma_sdev = sdev; + hme = 1; + } + + return esp_sbus_probe_one(&dev->dev, sdev, dma_sdev, + sdev->bus, hme); +} + +static int __devexit esp_sbus_remove(struct of_device *dev) +{ + struct esp *esp = dev_get_drvdata(&dev->dev); + unsigned int irq = esp->host->irq; + u32 val; + + scsi_esp_unregister(esp); + + /* Disable interrupts. */ + val = dma_read32(DMA_CSR); + dma_write32(val & ~DMA_INT_ENAB, DMA_CSR); + + free_irq(irq, esp); + sbus_free_consistent(esp->dev, 16, + esp->command_block, + esp->command_block_dma); + sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE); + + scsi_host_put(esp->host); + + return 0; +} + +static struct of_device_id esp_match[] = { + { + .name = "SUNW,esp", + }, + { + .name = "SUNW,fas", + }, + { + .name = "esp", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, esp_match); + +static struct of_platform_driver esp_sbus_driver = { + .name = "esp", + .match_table = esp_match, + .probe = esp_sbus_probe, + .remove = __devexit_p(esp_sbus_remove), +}; + +static int __init sunesp_init(void) +{ + return of_register_driver(&esp_sbus_driver, &sbus_bus_type); +} + +static void __exit sunesp_exit(void) +{ + of_unregister_driver(&esp_sbus_driver); +} + +MODULE_DESCRIPTION("Sun ESP SCSI driver"); +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(sunesp_init); +module_exit(sunesp_exit); diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 752ef07..be8d757 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -1467,7 +1467,8 @@ no_dma: if (ZS_IS_IRDA(uap)) uap->port_type = PMAC_SCC_IRDA; if (ZS_IS_INTMODEM(uap)) { - struct device_node* i2c_modem = find_devices("i2c-modem"); + struct device_node* i2c_modem = + of_find_node_by_name(NULL, "i2c-modem"); if (i2c_modem) { const char* mid = get_property(i2c_modem, "modem-id", NULL); @@ -1482,6 +1483,7 @@ no_dma: } printk(KERN_INFO "pmac_zilog: i2c-modem detected, id: %d\n", mid ? (*mid) : 0); + of_node_put(i2c_modem); } else { printk(KERN_INFO "pmac_zilog: serial modem detected\n"); } diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 96a852a..bfd4417 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -1387,8 +1387,8 @@ static enum su_type __devinit su_get_typ struct device_node *ap = of_find_node_by_path("/aliases"); if (ap) { - char *keyb = of_get_property(ap, "keyboard", NULL); - char *ms = of_get_property(ap, "mouse", NULL); + const char *keyb = of_get_property(ap, "keyboard", NULL); + const char *ms = of_get_property(ap, "mouse", NULL); if (keyb) { if (dp == of_find_node_by_path(keyb)) diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 8b7ff46..f5de58a 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ -obj-$(CONFIG_ETRAX_USB_HOST) += host/ obj-$(CONFIG_USB_OHCI_AT91) += host/ obj-$(CONFIG_USB_ACM) += class/ @@ -27,10 +26,7 @@ obj-$(CONFIG_USB) += storage/ obj-$(CONFIG_USB_ACECAD) += input/ obj-$(CONFIG_USB_AIPTEK) += input/ obj-$(CONFIG_USB_ATI_REMOTE) += input/ -obj-$(CONFIG_USB_HID) += input/ -obj-$(CONFIG_USB_KBD) += input/ obj-$(CONFIG_USB_KBTAB) += input/ -obj-$(CONFIG_USB_MOUSE) += input/ obj-$(CONFIG_USB_MTOUCH) += input/ obj-$(CONFIG_USB_POWERMATE) += input/ obj-$(CONFIG_USB_WACOM) += input/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 3dfa3e4..30b7bfb 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -4,6 +4,7 @@ * * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) + * Copyright (C) 2007 Simon Arlott * * 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 @@ -34,14 +35,14 @@ #include #include #include #include -#include /* FIXME: linux/firmware.h should include it itself */ +#include #include #include #include "usbatm.h" -#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands" -#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott" +#define DRIVER_VERSION "0.3" #define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver" static const char cxacru_driver_name[] = "cxacru"; @@ -64,7 +65,7 @@ #define BR_STACK_ADDR 0x00187f10 #define SDRAM_ENA 0x1 #define CMD_TIMEOUT 2000 /* msecs */ -#define POLL_INTERVAL 5000 /* msecs */ +#define POLL_INTERVAL 1 /* secs */ /* commands for interaction with the modem through the control channel before * firmware is loaded */ @@ -146,6 +147,13 @@ enum cxacru_info_idx { CXINF_MAX = 0x1c, }; +enum cxacru_poll_state { + CXPOLL_STOPPING, + CXPOLL_STOPPED, + CXPOLL_POLLING, + CXPOLL_SHUTDOWN +}; + struct cxacru_modem_type { u32 pll_f_clk; u32 pll_b_clk; @@ -158,7 +166,12 @@ struct cxacru_data { const struct cxacru_modem_type *modem_type; int line_status; + struct mutex adsl_state_serialize; + int adsl_status; struct delayed_work poll_work; + u32 card_info[CXINF_MAX]; + struct mutex poll_state_serialize; + int poll_state; /* contol handles */ struct mutex cm_serialize; @@ -170,6 +183,275 @@ struct cxacru_data { struct completion snd_done; }; +static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, + u8 *wdata, int wsize, u8 *rdata, int rsize); +static void cxacru_poll_status(struct work_struct *work); + +/* Card info exported through sysfs */ +#define CXACRU__ATTR_INIT(_name) \ +static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL) + +#define CXACRU_CMD_INIT(_name) \ +static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \ + cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name) + +#define CXACRU_ATTR_INIT(_value, _type, _name) \ +static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \ + struct cxacru_data *instance = usbatm_instance->driver_data; \ + return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \ +} \ +CXACRU__ATTR_INIT(_name) + +#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name) +#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) +#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) + +#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name) +#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) +#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) + +static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", value); +} + +static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf) +{ + if (unlikely(value < 0)) { + return snprintf(buf, PAGE_SIZE, "%d.%02u\n", + value / 100, -value % 100); + } else { + return snprintf(buf, PAGE_SIZE, "%d.%02u\n", + value / 100, value % 100); + } +} + +static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf) +{ + switch (value) { + case 0: return snprintf(buf, PAGE_SIZE, "no\n"); + case 1: return snprintf(buf, PAGE_SIZE, "yes\n"); + default: return 0; + } +} + +static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf) +{ + switch (value) { + case 1: return snprintf(buf, PAGE_SIZE, "not connected\n"); + case 2: return snprintf(buf, PAGE_SIZE, "connected\n"); + case 3: return snprintf(buf, PAGE_SIZE, "lost\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf) +{ + switch (value) { + case 0: return snprintf(buf, PAGE_SIZE, "down\n"); + case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n"); + case 2: return snprintf(buf, PAGE_SIZE, "training\n"); + case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n"); + case 4: return snprintf(buf, PAGE_SIZE, "exchange\n"); + case 5: return snprintf(buf, PAGE_SIZE, "up\n"); + case 6: return snprintf(buf, PAGE_SIZE, "waiting\n"); + case 7: return snprintf(buf, PAGE_SIZE, "initialising\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf) +{ + switch (value) { + case 0: return 0; + case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n"); + case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n"); + case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +/* + * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since + * this data is already in atm_dev there's no point. + * + * MAC_ADDRESS_HIGH = 0x????5544 + * MAC_ADDRESS_LOW = 0x33221100 + * Where 00-55 are bytes 0-5 of the MAC. + */ +static ssize_t cxacru_sysfs_show_mac_address(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); + struct atm_dev *atm_dev = usbatm_instance->atm_dev; + + return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", + atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2], + atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]); +} + +static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); + struct cxacru_data *instance = usbatm_instance->driver_data; + u32 value = instance->card_info[CXINF_LINE_STARTABLE]; + + switch (value) { + case 0: return snprintf(buf, PAGE_SIZE, "running\n"); + case 1: return snprintf(buf, PAGE_SIZE, "stopped\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); + struct cxacru_data *instance = usbatm_instance->driver_data; + int ret; + int poll = -1; + char str_cmd[8]; + int len = strlen(buf); + + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + + ret = sscanf(buf, "%7s", str_cmd); + if (ret != 1) + return -EINVAL; + ret = 0; + + if (mutex_lock_interruptible(&instance->adsl_state_serialize)) + return -ERESTARTSYS; + + if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) { + ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0); + if (ret < 0) { + atm_err(usbatm_instance, "change adsl state:" + " CHIP_ADSL_LINE_STOP returned %d\n", ret); + + ret = -EIO; + } else { + ret = len; + poll = CXPOLL_STOPPED; + } + } + + /* Line status is only updated every second + * and the device appears to only react to + * START/STOP every second too. Wait 1.5s to + * be sure that restart will have an effect. */ + if (!strcmp(str_cmd, "restart")) + msleep(1500); + + if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) { + ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); + if (ret < 0) { + atm_err(usbatm_instance, "change adsl state:" + " CHIP_ADSL_LINE_START returned %d\n", ret); + + ret = -EIO; + } else { + ret = len; + poll = CXPOLL_POLLING; + } + } + + if (!strcmp(str_cmd, "poll")) { + ret = len; + poll = CXPOLL_POLLING; + } + + if (ret == 0) { + ret = -EINVAL; + poll = -1; + } + + if (poll == CXPOLL_POLLING) { + mutex_lock(&instance->poll_state_serialize); + switch (instance->poll_state) { + case CXPOLL_STOPPED: + /* start polling */ + instance->poll_state = CXPOLL_POLLING; + break; + + case CXPOLL_STOPPING: + /* abort stop request */ + instance->poll_state = CXPOLL_POLLING; + case CXPOLL_POLLING: + case CXPOLL_SHUTDOWN: + /* don't start polling */ + poll = -1; + } + mutex_unlock(&instance->poll_state_serialize); + } else if (poll == CXPOLL_STOPPED) { + mutex_lock(&instance->poll_state_serialize); + /* request stop */ + if (instance->poll_state == CXPOLL_POLLING) + instance->poll_state = CXPOLL_STOPPING; + mutex_unlock(&instance->poll_state_serialize); + } + + mutex_unlock(&instance->adsl_state_serialize); + + if (poll == CXPOLL_POLLING) + cxacru_poll_status(&instance->poll_work.work); + + return ret; +} + +/* + * All device attributes are included in CXACRU_ALL_FILES + * so that the same list can be used multiple times: + * INIT (define the device attributes) + * CREATE (create all the device files) + * REMOVE (remove all the device files) + * + * With the last two being defined as needed in the functions + * they are used in before calling CXACRU_ALL_FILES() + */ +#define CXACRU_ALL_FILES(_action) \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \ +CXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \ +CXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \ +CXACRU__ATTR_##_action( mac_address); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \ +CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \ +CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \ +CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \ +CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \ +CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \ +CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \ +CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \ +CXACRU_CMD_##_action( adsl_state); + +CXACRU_ALL_FILES(INIT); + /* the following three functions are stolen from drivers/usb/core/message.c */ static void cxacru_blocking_completion(struct urb *urb) { @@ -347,8 +629,6 @@ static int cxacru_card_status(struct cxa return 0; } -static void cxacru_poll_status(struct work_struct *work); - static int cxacru_atm_start(struct usbatm_data *usbatm_instance, struct atm_dev *atm_dev) { @@ -357,6 +637,7 @@ static int cxacru_atm_start(struct usbat struct atm_dev *atm_dev = usbatm_instance->atm_dev; */ int ret; + int start_polling = 1; dbg("cxacru_atm_start"); @@ -369,14 +650,35 @@ static int cxacru_atm_start(struct usbat } /* start ADSL */ + mutex_lock(&instance->adsl_state_serialize); ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); if (ret < 0) { atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret); + mutex_unlock(&instance->adsl_state_serialize); return ret; } /* Start status polling */ - cxacru_poll_status(&instance->poll_work.work); + mutex_lock(&instance->poll_state_serialize); + switch (instance->poll_state) { + case CXPOLL_STOPPED: + /* start polling */ + instance->poll_state = CXPOLL_POLLING; + break; + + case CXPOLL_STOPPING: + /* abort stop request */ + instance->poll_state = CXPOLL_POLLING; + case CXPOLL_POLLING: + case CXPOLL_SHUTDOWN: + /* don't start polling */ + start_polling = 0; + } + mutex_unlock(&instance->poll_state_serialize); + mutex_unlock(&instance->adsl_state_serialize); + + if (start_polling) + cxacru_poll_status(&instance->poll_work.work); return 0; } @@ -387,14 +689,46 @@ static void cxacru_poll_status(struct wo u32 buf[CXINF_MAX] = {}; struct usbatm_data *usbatm = instance->usbatm; struct atm_dev *atm_dev = usbatm->atm_dev; + int keep_polling = 1; int ret; ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX); if (ret < 0) { - atm_warn(usbatm, "poll status: error %d\n", ret); + if (ret != -ESHUTDOWN) + atm_warn(usbatm, "poll status: error %d\n", ret); + + mutex_lock(&instance->poll_state_serialize); + if (instance->poll_state != CXPOLL_SHUTDOWN) { + instance->poll_state = CXPOLL_STOPPED; + + if (ret != -ESHUTDOWN) + atm_warn(usbatm, "polling disabled, set adsl_state" + " to 'start' or 'poll' to resume\n"); + } + mutex_unlock(&instance->poll_state_serialize); goto reschedule; } + memcpy(instance->card_info, buf, sizeof(instance->card_info)); + + if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) { + instance->adsl_status = buf[CXINF_LINE_STARTABLE]; + + switch (instance->adsl_status) { + case 0: + atm_printk(KERN_INFO, usbatm, "ADSL state: running\n"); + break; + + case 1: + atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n"); + break; + + default: + atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status); + break; + } + } + if (instance->line_status == buf[CXINF_LINE_STATUS]) goto reschedule; @@ -449,7 +783,20 @@ static void cxacru_poll_status(struct wo break; } reschedule: - schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL)); + + mutex_lock(&instance->poll_state_serialize); + if (instance->poll_state == CXPOLL_STOPPING && + instance->adsl_status == 1 && /* stopped */ + instance->line_status == 0) /* down */ + instance->poll_state = CXPOLL_STOPPED; + + if (instance->poll_state == CXPOLL_STOPPED) + keep_polling = 0; + mutex_unlock(&instance->poll_state_serialize); + + if (keep_polling) + schedule_delayed_work(&instance->poll_work, + round_jiffies_relative(POLL_INTERVAL*HZ)); } static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, @@ -684,6 +1031,14 @@ static int cxacru_bind(struct usbatm_dat instance->usbatm = usbatm_instance; instance->modem_type = (struct cxacru_modem_type *) id->driver_info; + memset(instance->card_info, 0, sizeof(instance->card_info)); + + mutex_init(&instance->poll_state_serialize); + instance->poll_state = CXPOLL_STOPPED; + instance->line_status = -1; + instance->adsl_status = -1; + + mutex_init(&instance->adsl_state_serialize); instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); if (!instance->rcv_buf) { @@ -710,6 +1065,13 @@ static int cxacru_bind(struct usbatm_dat goto fail; } + #define CXACRU_DEVICE_CREATE_FILE(_name) \ + ret = device_create_file(&intf->dev, &dev_attr_##_name); \ + if (unlikely(ret)) \ + goto fail_sysfs; + CXACRU_ALL_FILES(CREATE); + #undef CXACRU_DEVICE_CREATE_FILE + usb_fill_int_urb(instance->rcv_urb, usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD), instance->rcv_buf, PAGE_SIZE, @@ -730,6 +1092,14 @@ static int cxacru_bind(struct usbatm_dat return 0; + fail_sysfs: + dbg("cxacru_bind: device_create_file failed (%d)\n", ret); + + #define CXACRU_DEVICE_REMOVE_FILE(_name) \ + device_remove_file(&intf->dev, &dev_attr_##_name); + CXACRU_ALL_FILES(REMOVE); + #undef CXACRU_DEVICE_REVOVE_FILE + fail: free_page((unsigned long) instance->snd_buf); free_page((unsigned long) instance->rcv_buf); @@ -744,6 +1114,7 @@ static void cxacru_unbind(struct usbatm_ struct usb_interface *intf) { struct cxacru_data *instance = usbatm_instance->driver_data; + int is_polling = 1; dbg("cxacru_unbind entered"); @@ -752,8 +1123,20 @@ static void cxacru_unbind(struct usbatm_ return; } - while (!cancel_delayed_work(&instance->poll_work)) - flush_scheduled_work(); + mutex_lock(&instance->poll_state_serialize); + BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN); + + /* ensure that status polling continues unless + * it has already stopped */ + if (instance->poll_state == CXPOLL_STOPPED) + is_polling = 0; + + /* stop polling from being stopped or started */ + instance->poll_state = CXPOLL_SHUTDOWN; + mutex_unlock(&instance->poll_state_serialize); + + if (is_polling) + cancel_rearming_delayed_work(&instance->poll_work); usb_kill_urb(instance->snd_urb); usb_kill_urb(instance->rcv_urb); @@ -762,6 +1145,12 @@ static void cxacru_unbind(struct usbatm_ free_page((unsigned long) instance->snd_buf); free_page((unsigned long) instance->rcv_buf); + + #define CXACRU_DEVICE_REMOVE_FILE(_name) \ + device_remove_file(&intf->dev, &dev_attr_##_name); + CXACRU_ALL_FILES(REMOVE); + #undef CXACRU_DEVICE_REVOVE_FILE + kfree(instance); usbatm_instance->driver_data = NULL; diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index ec63b0e..b3f779f 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -274,6 +274,9 @@ static void usbatm_complete(struct urb * (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) || urb->status != -EILSEQ )) { + if (urb->status == -ESHUTDOWN) + return; + if (printk_ratelimit()) atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n", __func__, urb, urb->status); @@ -343,7 +346,7 @@ static void usbatm_extract_one_cell(stru UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end); } - memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); + memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); __skb_put(sarb, ATM_CELL_PAYLOAD); if (pti & 1) { @@ -370,7 +373,7 @@ static void usbatm_extract_one_cell(stru goto out; } - if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { + if (crc32_be(~0, skb_tail_pointer(sarb) - pdu_length, pdu_length) != 0xc704dd7b) { atm_rldbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n", __func__, vcc); atomic_inc(&vcc->stats->rx_err); @@ -396,7 +399,9 @@ static void usbatm_extract_one_cell(stru goto out; /* atm_charge increments rx_drop */ } - memcpy(skb->data, sarb->tail - pdu_length, length); + skb_copy_to_linear_data(skb, + skb_tail_pointer(sarb) - pdu_length, + length); __skb_put(skb, length); vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u", @@ -484,7 +489,7 @@ static unsigned int usbatm_write_cells(s ptr[4] = 0xec; ptr += ATM_CELL_HEADER; - memcpy(ptr, skb->data, data_len); + skb_copy_from_linear_data(skb, ptr, data_len); ptr += data_len; __skb_pull(skb, data_len); @@ -966,6 +971,14 @@ static int usbatm_atm_init(struct usbatm /* temp init ATM device, set to 128kbit */ atm_dev->link_rate = 128 * 1000 / 424; + ret = sysfs_create_link(&atm_dev->class_dev.kobj, + &instance->usb_intf->dev.kobj, "device"); + if (ret) { + atm_err(instance, "%s: sysfs_create_link failed: %d\n", + __func__, ret); + goto fail_sysfs; + } + if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) { atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret); goto fail; @@ -984,6 +997,8 @@ static int usbatm_atm_init(struct usbatm return 0; fail: + sysfs_remove_link(&atm_dev->class_dev.kobj, "device"); + fail_sysfs: instance->atm_dev = NULL; atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */ return ret; @@ -1316,8 +1331,10 @@ void usbatm_usb_disconnect(struct usb_in kfree(instance->cell_buf); /* ATM finalize */ - if (instance->atm_dev) + if (instance->atm_dev) { + sysfs_remove_link(&instance->atm_dev->class_dev.kobj, "device"); atm_dev_deregister(instance->atm_dev); + } usbatm_put_instance(instance); /* taken in usbatm_usb_probe */ } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 31ae661..14de3b1 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -212,7 +212,41 @@ static int acm_write_start(struct acm *a } return rc; } +/* + * attributes exported through sysfs + */ +static ssize_t show_caps +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + return sprintf(buf, "%d", acm->ctrl_caps); +} +static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); + +static ssize_t show_country_codes +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + memcpy(buf, acm->country_codes, acm->country_code_size); + return acm->country_code_size; +} + +static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); + +static ssize_t show_country_rel_date +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + return sprintf(buf, "%d", acm->country_rel_date); +} +static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); /* * Interrupt handlers for various ACM device responses */ @@ -514,6 +548,7 @@ static void acm_tty_unregister(struct ac usb_free_urb(acm->writeurb); for (i = 0; i < nr; i++) usb_free_urb(acm->ru[i].urb); + kfree(acm->country_codes); kfree(acm); } @@ -761,6 +796,7 @@ static int acm_probe (struct usb_interfa const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = NULL; + struct usb_cdc_country_functional_desc *cfd = NULL; char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; @@ -824,8 +860,9 @@ static int acm_probe (struct usb_interfa union_header = (struct usb_cdc_union_desc *) buffer; break; - case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ - break; /* for now we ignore it */ + case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + cfd = (struct usb_cdc_country_functional_desc *)buffer; + break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: @@ -983,6 +1020,34 @@ skip_normal_probe: goto alloc_fail7; } + usb_set_intfdata (intf, acm); + + i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); + if (i < 0) + goto alloc_fail8; + + if (cfd) { /* export the country data */ + acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); + if (!acm->country_codes) + goto skip_countries; + acm->country_code_size = cfd->bLength - 4; + memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); + acm->country_rel_date = cfd->iCountryCodeRelDate; + + i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); + if (i < 0) { + kfree(acm->country_codes); + goto skip_countries; + } + + i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); + if (i < 0) { + kfree(acm->country_codes); + goto skip_countries; + } + } + +skip_countries: usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -1006,9 +1071,10 @@ skip_normal_probe: tty_register_device(acm_tty_driver, minor, &control_interface->dev); acm_table[minor] = acm; - usb_set_intfdata (intf, acm); - return 0; + return 0; +alloc_fail8: + usb_free_urb(acm->writeurb); alloc_fail7: for (i = 0; i < num_rx_buf; i++) usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); @@ -1027,7 +1093,7 @@ alloc_fail: static void acm_disconnect(struct usb_interface *intf) { - struct acm *acm = usb_get_intfdata (intf); + struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); int i; @@ -1041,6 +1107,11 @@ static void acm_disconnect(struct usb_in mutex_unlock(&open_mutex); return; } + if (acm->country_codes){ + device_remove_file(&intf->dev, &dev_attr_wCountryCodes); + device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate); + } + device_remove_file(&intf->dev, &dev_attr_bmCapabilities); acm->dev = NULL; usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 1bcaea3..09f7765 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -91,6 +91,9 @@ struct acm { struct urb *ctrlurb, *writeurb; /* urbs */ u8 *ctrl_buffer; /* buffers of urbs */ dma_addr_t ctrl_dma; /* dma handles of buffers */ + u8 *country_codes; /* country codes from device */ + unsigned int country_code_size; /* size of this buffer */ + unsigned int country_rel_date; /* release date of version */ struct acm_wb wb[ACM_NW]; struct acm_ru ru[ACM_NR]; struct acm_rb rb[ACM_NR]; diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 2fc0f88..f493fb1 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -31,7 +31,30 @@ config USB_DEVICEFS For the format of the various /proc/bus/usb/ files, please read . - Most users want to say Y here. + Usbfs files can't handle Access Control Lists (ACL), which are the + default way to grant access to USB devices for untrusted users of a + desktop system. The usbfs functionality is replaced by real + device-nodes managed by udev. These nodes live in /dev/bus/usb and + are used by libusb. + +config USB_DEVICE_CLASS + bool "USB device class-devices (DEPRECATED)" + depends on USB + default n + ---help--- + Userspace access to USB devices is granted by device-nodes exported + directly from the usbdev in sysfs. Old versions of the driver + core and udev needed additional class devices to export device nodes. + + These additional devices are difficult to handle in userspace, if + information about USB interfaces must be available. One device contains + the device node, the other device contains the interface data. Both + devices are at the same level in sysfs (siblings) and one can't access + the other. The device node created directly by the usbdev is the parent + device of the interface and therefore easily accessible from the interface + event. + + This option provides backward compatibility if needed. config USB_DYNAMIC_MINORS bool "Dynamic USB minor allocation (EXPERIMENTAL)" diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index aefc798..6753ca0 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -246,7 +246,6 @@ static char *usb_dump_interface_descript if (start > end) return start; - down_read(&usb_bus_type.subsys.rwsem); if (iface) { driver_name = (iface->dev.driver ? iface->dev.driver->name @@ -263,7 +262,6 @@ static char *usb_dump_interface_descript desc->bInterfaceSubClass, desc->bInterfaceProtocol, driver_name); - up_read(&usb_bus_type.subsys.rwsem); return start; } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 36e7a84..927a181 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -57,7 +57,6 @@ #include "usb.h" #define USB_MAXBUS 64 #define USB_DEVICE_MAX USB_MAXBUS * 128 -static struct class *usb_device_class; /* Mutual exclusion for removal, open, and release */ DEFINE_MUTEX(usbfs_mutex); @@ -421,14 +420,11 @@ static int claimintf(struct dev_state *p if (test_bit(ifnum, &ps->ifclaimed)) return 0; - /* lock against other changes to driver bindings */ - down_write(&usb_bus_type.subsys.rwsem); intf = usb_ifnum_to_if(dev, ifnum); if (!intf) err = -ENOENT; else err = usb_driver_claim_interface(&usbfs_driver, intf, ps); - up_write(&usb_bus_type.subsys.rwsem); if (err == 0) set_bit(ifnum, &ps->ifclaimed); return err; @@ -444,8 +440,6 @@ static int releaseintf(struct dev_state if (ifnum >= 8*sizeof(ps->ifclaimed)) return err; dev = ps->dev; - /* lock against other changes to driver bindings */ - down_write(&usb_bus_type.subsys.rwsem); intf = usb_ifnum_to_if(dev, ifnum); if (!intf) err = -ENOENT; @@ -453,7 +447,6 @@ static int releaseintf(struct dev_state usb_driver_release_interface(&usbfs_driver, intf); err = 0; } - up_write(&usb_bus_type.subsys.rwsem); return err; } @@ -520,22 +513,25 @@ static int check_ctrlrecip(struct dev_st return ret; } -static struct usb_device *usbdev_lookup_minor(int minor) +static int __match_minor(struct device *dev, void *data) { - struct device *device; - struct usb_device *udev = NULL; + int minor = *((int *)data); - down(&usb_device_class->sem); - list_for_each_entry(device, &usb_device_class->devices, node) { - if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { - udev = device->platform_data; - break; - } - } - up(&usb_device_class->sem); + if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) + return 1; + return 0; +} - return udev; -}; +static struct usb_device *usbdev_lookup_by_minor(int minor) +{ + struct device *dev; + + dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor); + if (!dev) + return NULL; + put_device(dev); + return container_of(dev, struct usb_device, dev); +} /* * file operations @@ -554,11 +550,14 @@ static int usbdev_open(struct inode *ino goto out; ret = -ENOENT; - /* check if we are called from a real node or usbfs */ + /* usbdev device-node */ if (imajor(inode) == USB_DEVICE_MAJOR) - dev = usbdev_lookup_minor(iminor(inode)); + dev = usbdev_lookup_by_minor(iminor(inode)); +#ifdef CONFIG_USB_DEVICEFS + /* procfs file */ if (!dev) dev = inode->i_private; +#endif if (!dev) goto out; ret = usb_autoresume_device(dev); @@ -581,7 +580,7 @@ static int usbdev_open(struct inode *ino ps->disccontext = NULL; ps->ifclaimed = 0; security_task_getsecid(current, &ps->secid); - wmb(); + smp_wmb(); list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; out: @@ -813,7 +812,6 @@ static int proc_getdriver(struct dev_sta if (copy_from_user(&gd, arg, sizeof(gd))) return -EFAULT; - down_read(&usb_bus_type.subsys.rwsem); intf = usb_ifnum_to_if(ps->dev, gd.interface); if (!intf || !intf->dev.driver) ret = -ENODATA; @@ -822,7 +820,6 @@ static int proc_getdriver(struct dev_sta sizeof(gd.driver)); ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0); } - up_read(&usb_bus_type.subsys.rwsem); return ret; } @@ -1351,15 +1348,12 @@ static int proc_ioctl(struct dev_state * /* disconnect kernel driver from interface */ case USBDEVFS_DISCONNECT: - - down_write(&usb_bus_type.subsys.rwsem); if (intf->dev.driver) { driver = to_usb_driver(intf->dev.driver); dev_dbg (&intf->dev, "disconnect by usbfs\n"); usb_driver_release_interface(driver, intf); } else retval = -ENODATA; - up_write(&usb_bus_type.subsys.rwsem); break; /* let kernel drivers try to (re)bind to the interface */ @@ -1371,7 +1365,6 @@ static int proc_ioctl(struct dev_state * /* talk directly to the interface's driver */ default: - down_read(&usb_bus_type.subsys.rwsem); if (intf->dev.driver) driver = to_usb_driver(intf->dev.driver); if (driver == NULL || driver->ioctl == NULL) { @@ -1381,7 +1374,6 @@ static int proc_ioctl(struct dev_state * if (retval == -ENOIOCTLCMD) retval = -ENOTTY; } - up_read(&usb_bus_type.subsys.rwsem); } /* cleanup and return */ @@ -1583,7 +1575,7 @@ static unsigned int usbdev_poll(struct f return mask; } -const struct file_operations usbfs_device_file_operations = { +const struct file_operations usbdev_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, @@ -1592,50 +1584,53 @@ const struct file_operations usbfs_devic .release = usbdev_release, }; -static int usbdev_add(struct usb_device *dev) +#ifdef CONFIG_USB_DEVICE_CLASS +static struct class *usb_classdev_class; + +static int usb_classdev_add(struct usb_device *dev) { int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); - dev->usbfs_dev = device_create(usb_device_class, &dev->dev, + dev->usb_classdev = device_create(usb_classdev_class, &dev->dev, MKDEV(USB_DEVICE_MAJOR, minor), "usbdev%d.%d", dev->bus->busnum, dev->devnum); - if (IS_ERR(dev->usbfs_dev)) - return PTR_ERR(dev->usbfs_dev); + if (IS_ERR(dev->usb_classdev)) + return PTR_ERR(dev->usb_classdev); - dev->usbfs_dev->platform_data = dev; return 0; } -static void usbdev_remove(struct usb_device *dev) +static void usb_classdev_remove(struct usb_device *dev) { - device_unregister(dev->usbfs_dev); + device_unregister(dev->usb_classdev); } -static int usbdev_notify(struct notifier_block *self, unsigned long action, - void *dev) +static int usb_classdev_notify(struct notifier_block *self, + unsigned long action, void *dev) { switch (action) { case USB_DEVICE_ADD: - if (usbdev_add(dev)) + if (usb_classdev_add(dev)) return NOTIFY_BAD; break; case USB_DEVICE_REMOVE: - usbdev_remove(dev); + usb_classdev_remove(dev); break; } return NOTIFY_OK; } static struct notifier_block usbdev_nb = { - .notifier_call = usbdev_notify, + .notifier_call = usb_classdev_notify, }; +#endif static struct cdev usb_device_cdev = { .kobj = {.name = "usb_device", }, .owner = THIS_MODULE, }; -int __init usbdev_init(void) +int __init usb_devio_init(void) { int retval; @@ -1645,38 +1640,38 @@ int __init usbdev_init(void) err("unable to register minors for usb_device"); goto out; } - cdev_init(&usb_device_cdev, &usbfs_device_file_operations); + cdev_init(&usb_device_cdev, &usbdev_file_operations); retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX); if (retval) { err("unable to get usb_device major %d", USB_DEVICE_MAJOR); goto error_cdev; } - usb_device_class = class_create(THIS_MODULE, "usb_device"); - if (IS_ERR(usb_device_class)) { +#ifdef CONFIG_USB_DEVICE_CLASS + usb_classdev_class = class_create(THIS_MODULE, "usb_device"); + if (IS_ERR(usb_classdev_class)) { err("unable to register usb_device class"); - retval = PTR_ERR(usb_device_class); - goto error_class; + retval = PTR_ERR(usb_classdev_class); + cdev_del(&usb_device_cdev); + usb_classdev_class = NULL; + goto out; } usb_register_notify(&usbdev_nb); - +#endif out: return retval; -error_class: - usb_device_class = NULL; - cdev_del(&usb_device_cdev); - error_cdev: unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); goto out; } -void usbdev_cleanup(void) +void usb_devio_cleanup(void) { +#ifdef CONFIG_USB_DEVICE_CLASS usb_unregister_notify(&usbdev_nb); - class_destroy(usb_device_class); + class_destroy(usb_classdev_class); +#endif cdev_del(&usb_device_cdev); unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); } - diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 9e3e943..b9f7f90 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -287,9 +287,9 @@ static int usb_unbind_interface(struct d * way to bind to an interface is to return the private data from * the driver's probe() method. * - * Callers must own the device lock and the driver model's usb_bus_type.subsys - * writelock. So driver probe() entries don't need extra locking, - * but other call contexts may need to explicitly claim those locks. + * Callers must own the device lock, so driver probe() entries don't need + * extra locking, but other call contexts may need to explicitly claim that + * lock. */ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) @@ -330,9 +330,9 @@ EXPORT_SYMBOL(usb_driver_claim_interface * also causes the driver disconnect() method to be called. * * This call is synchronous, and may not be used in an interrupt context. - * Callers must own the device lock and the driver model's usb_bus_type.subsys - * writelock. So driver disconnect() entries don't need extra locking, - * but other call contexts may need to explicitly claim those locks. + * Callers must own the device lock, so driver disconnect() entries don't + * need extra locking, but other call contexts may need to explicitly claim + * that lock. */ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) @@ -574,23 +574,10 @@ static int usb_device_match(struct devic } #ifdef CONFIG_HOTPLUG - -/* - * This sends an uevent to userspace, 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. - * - * We're called either from khubd (the typical case) or from root hub - * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle - * delays in event delivery. Use sysfs (and DEVPATH) to make sure the - * device (and this configuration!) are still present. - */ static int usb_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { - struct usb_interface *intf; struct usb_device *usb_dev; - struct usb_host_interface *alt; int i = 0; int length = 0; @@ -600,13 +587,11 @@ static int usb_uevent(struct device *dev /* driver is often null here; dev_dbg() would oops */ pr_debug ("usb %s: uevent\n", dev->bus_id); - if (is_usb_device(dev)) { + if (is_usb_device(dev)) usb_dev = to_usb_device(dev); - alt = NULL; - } else { - intf = to_usb_interface(dev); + else { + struct usb_interface *intf = to_usb_interface(dev); usb_dev = interface_to_usbdev(intf); - alt = intf->cur_altsetting; } if (usb_dev->devnum < 0) { @@ -621,9 +606,7 @@ static int usb_uevent(struct device *dev #ifdef CONFIG_USB_DEVICEFS /* If this is available, userspace programs can directly read * all the device descriptors we don't tell them about. Or - * even act as usermode drivers. - * - * FIXME reduce hardwired intelligence here + * act as usermode drivers. */ if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, @@ -650,44 +633,29 @@ #endif usb_dev->descriptor.bDeviceProtocol)) return -ENOMEM; - if (!is_usb_device(dev)) { - - if (add_uevent_var(envp, num_envp, &i, + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "INTERFACE=%d/%d/%d", - alt->desc.bInterfaceClass, - alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) - return -ENOMEM; + "BUSNUM=%03d", + usb_dev->bus->busnum)) + return -ENOMEM; - if (add_uevent_var(envp, num_envp, &i, + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", - le16_to_cpu(usb_dev->descriptor.idVendor), - le16_to_cpu(usb_dev->descriptor.idProduct), - le16_to_cpu(usb_dev->descriptor.bcdDevice), - usb_dev->descriptor.bDeviceClass, - usb_dev->descriptor.bDeviceSubClass, - usb_dev->descriptor.bDeviceProtocol, - alt->desc.bInterfaceClass, - alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) - return -ENOMEM; - } + "DEVNUM=%03d", + usb_dev->devnum)) + return -ENOMEM; envp[i] = NULL; - return 0; } #else static int usb_uevent(struct device *dev, char **envp, - int num_envp, char *buffer, int buffer_size) + int num_envp, char *buffer, int buffer_size) { return -ENODEV; } - #endif /* CONFIG_HOTPLUG */ /** @@ -872,8 +840,10 @@ static int usb_resume_device(struct usb_ done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); - if (status == 0) + if (status == 0) { + udev->autoresume_disabled = 0; udev->dev.power.power_state.event = PM_EVENT_ON; + } return status; } @@ -962,6 +932,7 @@ static int autosuspend_check(struct usb_ { int i; struct usb_interface *intf; + unsigned long suspend_time; /* For autosuspend, fail fast if anything is in use or autosuspend * is disabled. Also fail if any interfaces require remote wakeup @@ -970,9 +941,10 @@ static int autosuspend_check(struct usb_ udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; - if (!udev->autosuspend_delay) + if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled) return -EPERM; + suspend_time = udev->last_busy + udev->autosuspend_delay; if (udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; @@ -988,6 +960,24 @@ static int autosuspend_check(struct usb_ } } } + + /* If everything is okay but the device hasn't been idle for long + * enough, queue a delayed autosuspend request. + */ + if (time_after(suspend_time, jiffies)) { + if (!timer_pending(&udev->autosuspend.timer)) { + + /* The value of jiffies may change between the + * time_after() comparison above and the subtraction + * below. That's okay; the system behaves sanely + * when a timer is registered for the present moment + * or for the past. + */ + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + suspend_time - jiffies); + } + return -EAGAIN; + } return 0; } @@ -1033,26 +1023,25 @@ #endif /* CONFIG_USB_SUSPEND */ * * This routine can run only in process context. */ -int usb_suspend_both(struct usb_device *udev, pm_message_t msg) +static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) { int status = 0; int i = 0; struct usb_interface *intf; struct usb_device *parent = udev->parent; - cancel_delayed_work(&udev->autosuspend); - if (udev->state == USB_STATE_NOTATTACHED) - return 0; - if (udev->state == USB_STATE_SUSPENDED) - return 0; + if (udev->state == USB_STATE_NOTATTACHED || + udev->state == USB_STATE_SUSPENDED) + goto done; udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->auto_pm) { status = autosuspend_check(udev); if (status < 0) - return status; + goto done; } + cancel_delayed_work(&udev->autosuspend); /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { @@ -1077,6 +1066,7 @@ int usb_suspend_both(struct usb_device * } else if (parent) usb_autosuspend_device(parent); + done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; } @@ -1109,7 +1099,7 @@ int usb_suspend_both(struct usb_device * * * This routine can run only in process context. */ -int usb_resume_both(struct usb_device *udev) +static int usb_resume_both(struct usb_device *udev) { int status = 0; int i; @@ -1117,11 +1107,17 @@ int usb_resume_both(struct usb_device *u struct usb_device *parent = udev->parent; cancel_delayed_work(&udev->autosuspend); - if (udev->state == USB_STATE_NOTATTACHED) - return -ENODEV; + if (udev->state == USB_STATE_NOTATTACHED) { + status = -ENODEV; + goto done; + } /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { + if (udev->auto_pm && udev->autoresume_disabled) { + status = -EPERM; + goto done; + } if (parent) { status = usb_autoresume_device(parent); if (status == 0) { @@ -1167,6 +1163,7 @@ int usb_resume_both(struct usb_device *u } } + done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; } @@ -1181,20 +1178,34 @@ static int usb_autopm_do_device(struct u int status = 0; usb_pm_lock(udev); + udev->auto_pm = 1; udev->pm_usage_cnt += inc_usage_cnt; WARN_ON(udev->pm_usage_cnt < 0); if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) { - udev->auto_pm = 1; - status = usb_resume_both(udev); + if (udev->state == USB_STATE_SUSPENDED) + status = usb_resume_both(udev); if (status != 0) udev->pm_usage_cnt -= inc_usage_cnt; - } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - udev->autosuspend_delay); + else if (inc_usage_cnt) + udev->last_busy = jiffies; + } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) { + if (inc_usage_cnt) + udev->last_busy = jiffies; + status = usb_suspend_both(udev, PMSG_SUSPEND); + } usb_pm_unlock(udev); return status; } +/* usb_autosuspend_work - callback routine to autosuspend a USB device */ +void usb_autosuspend_work(struct work_struct *work) +{ + struct usb_device *udev = + container_of(work, struct usb_device, autosuspend.work); + + usb_autopm_do_device(udev, 0); +} + /** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces * @udev: the usb_device to autosuspend @@ -1286,15 +1297,20 @@ static int usb_autopm_do_interface(struc if (intf->condition == USB_INTERFACE_UNBOUND) status = -ENODEV; else { + udev->auto_pm = 1; intf->pm_usage_cnt += inc_usage_cnt; if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { - udev->auto_pm = 1; - status = usb_resume_both(udev); + if (udev->state == USB_STATE_SUSPENDED) + status = usb_resume_both(udev); if (status != 0) intf->pm_usage_cnt -= inc_usage_cnt; - } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - udev->autosuspend_delay); + else if (inc_usage_cnt) + udev->last_busy = jiffies; + } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) { + if (inc_usage_cnt) + udev->last_busy = jiffies; + status = usb_suspend_both(udev, PMSG_SUSPEND); + } } usb_pm_unlock(udev); return status; @@ -1353,11 +1369,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interfa * or @intf is unbound. A typical example would be a character-device * driver when its device file is opened. * - * The routine increments @intf's usage counter. So long as the counter - * is greater than 0, autosuspend will not be allowed for @intf or its - * usb_device. When the driver is finished using @intf it should call - * usb_autopm_put_interface() to decrement the usage counter and queue - * a delayed autosuspend request (if the counter is <= 0). + * + * The routine increments @intf's usage counter. (However if the + * autoresume fails then the counter is re-decremented.) So long as the + * counter is greater than 0, autosuspend will not be allowed for @intf + * or its usb_device. When the driver is finished using @intf it should + * call usb_autopm_put_interface() to decrement the usage counter and + * queue a delayed autosuspend request (if the counter is <= 0). + * * * Note that @intf->pm_usage_cnt is owned by the interface driver. The * core will not change its value other than the increment and decrement @@ -1405,50 +1424,96 @@ int usb_autopm_set_interface(struct usb_ } EXPORT_SYMBOL_GPL(usb_autopm_set_interface); +#else + +void usb_autosuspend_work(struct work_struct *work) +{} + #endif /* CONFIG_USB_SUSPEND */ -static int usb_suspend(struct device *dev, pm_message_t message) +/** + * usb_external_suspend_device - external suspend of a USB device and its interfaces + * @udev: the usb_device to suspend + * @msg: Power Management message describing this state transition + * + * This routine handles external suspend requests: ones not generated + * internally by a USB driver (autosuspend) but rather coming from the user + * (via sysfs) or the PM core (system sleep). The suspend will be carried + * out regardless of @udev's usage counter or those of its interfaces, + * and regardless of whether or not remote wakeup is enabled. Of course, + * interface drivers still have the option of failing the suspend (if + * there are unsuspended children, for example). + * + * The caller must hold @udev's device lock. + */ +int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_suspend_both(udev, message); - usb_pm_unlock(udev); - } else - status = 0; + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_suspend_both(udev, msg); + usb_pm_unlock(udev); return status; } -static int usb_resume(struct device *dev) +/** + * usb_external_resume_device - external resume of a USB device and its interfaces + * @udev: the usb_device to resume + * + * This routine handles external resume requests: ones not generated + * internally by a USB driver (autoresume) but rather coming from the user + * (via sysfs), the PM core (system resume), or the device itself (remote + * wakeup). @udev's usage counter is unaffected. + * + * The caller must hold @udev's device lock. + */ +int usb_external_resume_device(struct usb_device *udev) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_resume_both(udev); - usb_pm_unlock(udev); + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_resume_both(udev); + usb_pm_unlock(udev); - /* Rebind drivers that had no suspend method? */ - } else - status = 0; + /* Now that the device is awake, we can start trying to autosuspend + * it again. */ + if (status == 0) + usb_try_autosuspend_device(udev); return status; } +static int usb_suspend(struct device *dev, pm_message_t message) +{ + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + return usb_external_suspend_device(to_usb_device(dev), message); +} + +static int usb_resume(struct device *dev) +{ + struct usb_device *udev; + + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + udev = to_usb_device(dev); + if (udev->autoresume_disabled) + return -EPERM; + return usb_external_resume_device(udev); +} + +#else + +#define usb_suspend NULL +#define usb_resume NULL + #endif /* CONFIG_PM */ struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, -#ifdef CONFIG_PM .suspend = usb_suspend, .resume = usb_resume, -#endif }; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index b26c19e..40cf882 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -37,6 +37,7 @@ #include #include #include #include +#include #include @@ -544,6 +545,8 @@ void usb_hcd_poll_rh_status(struct usb_h unsigned long flags; char buffer[4]; /* Any root hubs with > 31 ports? */ + if (unlikely(!hcd->rh_registered)) + return; if (!hcd->uses_new_polling && !hcd->status_urb) return; @@ -1296,14 +1299,26 @@ int hcd_bus_resume (struct usb_bus *bus) return status; } +/* Workqueue routine for root-hub remote wakeup */ +static void hcd_resume_work(struct work_struct *work) +{ + struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work); + struct usb_device *udev = hcd->self.root_hub; + + usb_lock_device(udev); + usb_mark_last_busy(udev); + usb_external_resume_device(udev); + usb_unlock_device(udev); +} + /** * usb_hcd_resume_root_hub - called by HCD to resume its root hub * @hcd: host controller for this root hub * * The USB host controller calls this function when its root hub is * suspended (with the remote wakeup feature enabled) and a remote - * wakeup request is received. It queues a request for khubd to - * resume the root hub (that is, manage its downstream ports again). + * wakeup request is received. The routine submits a workqueue request + * to resume the root hub (that is, manage its downstream ports again). */ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) { @@ -1311,7 +1326,7 @@ void usb_hcd_resume_root_hub (struct usb spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) - usb_resume_root_hub (hcd->self.root_hub); + queue_work(ksuspend_usb_wq, &hcd->wakeup_work); spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); @@ -1500,6 +1515,9 @@ struct usb_hcd *usb_create_hcd (const st init_timer(&hcd->rh_timer); hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long) hcd; +#ifdef CONFIG_PM + INIT_WORK(&hcd->wakeup_work, hcd_resume_work); +#endif hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -1666,16 +1684,20 @@ void usb_remove_hcd(struct usb_hcd *hcd) hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); +#ifdef CONFIG_PM + flush_workqueue(ksuspend_usb_wq); +#endif + mutex_lock(&usb_bus_list_lock); usb_disconnect(&hcd->self.root_hub); mutex_unlock(&usb_bus_list_lock); - hcd->poll_rh = 0; - del_timer_sync(&hcd->rh_timer); - hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); + if (hcd->irq >= 0) free_irq(hcd->irq, hcd); usb_deregister_bus(&hcd->self); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 2a269ca..ef50fa4 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -68,6 +68,9 @@ struct usb_hcd { struct timer_list rh_timer; /* drives root-hub polling */ struct urb *status_urb; /* the current status urb */ +#ifdef CONFIG_PM + struct work_struct wakeup_work; /* for remote wakeup */ +#endif /* * hardware info/state diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b89a98e..bde29ab 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -119,8 +119,7 @@ MODULE_PARM_DESC(use_both_schemes, "first one fails"); -#ifdef DEBUG -static inline char *portspeed (int portstatus) +static inline char *portspeed(int portstatus) { if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) return "480 Mb/s"; @@ -129,7 +128,6 @@ static inline char *portspeed (int ports else return "12 Mb/s"; } -#endif /* Note that hdev or one of its children must be locked! */ static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev) @@ -1369,11 +1367,15 @@ #ifdef CONFIG_USB_OTG } #endif + /* export the usbdev device-node for libusb */ + udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, + (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); + /* Register the device. The device driver is responsible - * for adding the device files to usbfs and sysfs and for - * configuring the device. + * for adding the device files to sysfs and for configuring + * the device. */ - err = device_add (&udev->dev); + err = device_add(&udev->dev); if (err) { dev_err(&udev->dev, "can't device_add, error %d\n", err); goto fail; @@ -1857,12 +1859,8 @@ static int remote_wakeup(struct usb_devi usb_lock_device(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); - status = usb_autoresume_device(udev); - - /* Give the interface drivers a chance to do something, - * then autosuspend the device again. */ - if (status == 0) - usb_autosuspend_device(udev); + usb_mark_last_busy(udev); + status = usb_external_resume_device(udev); } usb_unlock_device(udev); return status; @@ -1986,13 +1984,6 @@ #define hub_suspend NULL #define hub_resume NULL #endif -void usb_resume_root_hub(struct usb_device *hdev) -{ - struct usb_hub *hub = hdev_to_hub(hdev); - - kick_khubd(hub); -} - /* USB 2.0 spec, 7.1.7.3 / fig 7-29: * diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 11dad22..cddfc62 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -662,7 +662,7 @@ static void usbfs_add_device(struct usb_ sprintf (name, "%03d", dev->devnum); dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, dev->bus->usbfs_dentry, dev, - &usbfs_device_file_operations, + &usbdev_file_operations, devuid, devgid); if (dev->usbfs_dentry == NULL) { err ("error creating usbfs device entry"); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 217a3d6..b743478 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -412,10 +412,24 @@ int usb_sg_init ( io->urbs [i]->status = -EINPROGRESS; io->urbs [i]->actual_length = 0; + /* + * Some systems need to revert to PIO when DMA is temporarily + * unavailable. For their sakes, both transfer_buffer and + * transfer_dma are set when possible. However this can only + * work on systems without HIGHMEM, since DMA buffers located + * in high memory are not directly addressable by the CPU for + * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL + * to prevent stale pointers and to help spot bugs. + */ if (dma) { - /* hc may use _only_ transfer_dma */ io->urbs [i]->transfer_dma = sg_dma_address (sg + i); len = sg_dma_len (sg + i); +#ifdef CONFIG_HIGHMEM + io->urbs[i]->transfer_buffer = NULL; +#else + io->urbs[i]->transfer_buffer = + page_address(sg[i].page) + sg[i].offset; +#endif } else { /* hc may use _only_ transfer_buffer */ io->urbs [i]->transfer_buffer = @@ -1305,7 +1319,7 @@ int usb_reset_configuration(struct usb_d return 0; } -static void release_interface(struct device *dev) +void usb_release_interface(struct device *dev) { struct usb_interface *intf = to_usb_interface(dev); struct usb_interface_cache *intfc = @@ -1315,6 +1329,67 @@ static void release_interface(struct dev kfree(intf); } +#ifdef CONFIG_HOTPLUG +static int usb_if_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct usb_device *usb_dev; + struct usb_interface *intf; + struct usb_host_interface *alt; + int i = 0; + int length = 0; + + if (!dev) + return -ENODEV; + + /* driver is often null here; dev_dbg() would oops */ + pr_debug ("usb %s: uevent\n", dev->bus_id); + + intf = to_usb_interface(dev); + usb_dev = interface_to_usbdev(intf); + alt = intf->cur_altsetting; + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "INTERFACE=%d/%d/%d", + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice), + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol, + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + + envp[i] = NULL; + return 0; +} + +#else + +static int usb_if_uevent(struct device *dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + return -ENODEV; +} +#endif /* CONFIG_HOTPLUG */ + +struct device_type usb_if_device_type = { + .name = "usb_interface", + .release = usb_release_interface, + .uevent = usb_if_uevent, +}; + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1349,7 +1424,7 @@ static void release_interface(struct dev * * This call is synchronous. The calling context must be able to sleep, * must own the device lock, and must not hold the driver model's USB - * bus rwsem; usb device driver probe() methods cannot use this routine. + * bus mutex; usb device driver probe() methods cannot use this routine. * * Returns zero on success, or else the status code returned by the * underlying call that failed. On successful completion, each interface @@ -1478,8 +1553,8 @@ free_interfaces: intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; + intf->dev.type = &usb_if_device_type; intf->dev.dma_mask = dev->dev.dma_mask; - intf->dev.release = release_interface; device_initialize (&intf->dev); mark_quiesced(intf); sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d", diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index f08ec85..739f520 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -42,7 +42,7 @@ static void usb_autosuspend_quirk(struct { #ifdef CONFIG_USB_SUSPEND /* disable autosuspend, but allow the user to re-enable it via sysfs */ - udev->autosuspend_delay = 0; + udev->autosuspend_disabled = 1; #endif } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 311d5df..e7c9823 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -11,6 +11,7 @@ #include +#include #include #include "usb.h" @@ -117,6 +118,16 @@ show_speed(struct device *dev, struct de static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL); static ssize_t +show_busnum(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + return sprintf(buf, "%d\n", udev->bus->busnum); +} +static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL); + +static ssize_t show_devnum(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -165,7 +176,7 @@ show_autosuspend(struct device *dev, str { struct usb_device *udev = to_usb_device(dev); - return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ); + return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ); } static ssize_t @@ -173,39 +184,115 @@ set_autosuspend(struct device *dev, stru const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); - unsigned value, old; + int value; - if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ) + if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ || + value <= - INT_MAX/HZ) return -EINVAL; value *= HZ; - old = udev->autosuspend_delay; udev->autosuspend_delay = value; - if (value > 0 && old == 0) + if (value >= 0) usb_try_autosuspend_device(udev); - + else { + if (usb_autoresume_device(udev) == 0) + usb_autosuspend_device(udev); + } return count; } static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, show_autosuspend, set_autosuspend); +static const char on_string[] = "on"; +static const char auto_string[] = "auto"; +static const char suspend_string[] = "suspend"; + +static ssize_t +show_level(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + const char *p = auto_string; + + if (udev->state == USB_STATE_SUSPENDED) { + if (udev->autoresume_disabled) + p = suspend_string; + } else { + if (udev->autosuspend_disabled) + p = on_string; + } + return sprintf(buf, "%s\n", p); +} + +static ssize_t +set_level(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int len = count; + char *cp; + int rc = 0; + + cp = memchr(buf, '\n', count); + if (cp) + len = cp - buf; + + usb_lock_device(udev); + + /* Setting the flags without calling usb_pm_lock is a subject to + * races, but who cares... + */ + if (len == sizeof on_string - 1 && + strncmp(buf, on_string, len) == 0) { + udev->autosuspend_disabled = 1; + udev->autoresume_disabled = 0; + rc = usb_external_resume_device(udev); + + } else if (len == sizeof auto_string - 1 && + strncmp(buf, auto_string, len) == 0) { + udev->autosuspend_disabled = 0; + udev->autoresume_disabled = 0; + rc = usb_external_resume_device(udev); + + } else if (len == sizeof suspend_string - 1 && + strncmp(buf, suspend_string, len) == 0) { + udev->autosuspend_disabled = 0; + udev->autoresume_disabled = 1; + rc = usb_external_suspend_device(udev, PMSG_SUSPEND); + + } else + rc = -EINVAL; + + usb_unlock_device(udev); + return (rc < 0 ? rc : count); +} + +static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); + static char power_group[] = "power"; static int add_power_attributes(struct device *dev) { int rc = 0; - if (is_usb_device(dev)) + if (is_usb_device(dev)) { rc = sysfs_add_file_to_group(&dev->kobj, &dev_attr_autosuspend.attr, power_group); + if (rc == 0) + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_level.attr, + power_group); + } return rc; } static void remove_power_attributes(struct device *dev) { sysfs_remove_file_from_group(&dev->kobj, + &dev_attr_level.attr, + power_group); + sysfs_remove_file_from_group(&dev->kobj, &dev_attr_autosuspend.attr, power_group); } @@ -270,6 +357,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_bNumConfigurations.attr, &dev_attr_bMaxPacketSize0.attr, &dev_attr_speed.attr, + &dev_attr_busnum.attr, &dev_attr_devnum.attr, &dev_attr_version.attr, &dev_attr_maxchild.attr, diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 54b42ce..dfd1b5c 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -49,12 +49,13 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ -struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ +/* Workqueue for autosuspend and for remote wakeup of root hubs */ +struct workqueue_struct *ksuspend_usb_wq; #ifdef CONFIG_USB_SUSPEND static int usb_autosuspend_delay = 2; /* Default delay value, * in seconds */ -module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644); +module_param_named(autosuspend, usb_autosuspend_delay, int, 0644); MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); #else @@ -196,6 +197,11 @@ #endif kfree(udev); } +struct device_type usb_device_type = { + .name = "usb_device", + .release = usb_release_dev, +}; + #ifdef CONFIG_PM static int ksuspend_usb_init(void) @@ -211,27 +217,6 @@ static void ksuspend_usb_cleanup(void) destroy_workqueue(ksuspend_usb_wq); } -#ifdef CONFIG_USB_SUSPEND - -/* usb_autosuspend_work - callback routine to autosuspend a USB device */ -static void usb_autosuspend_work(struct work_struct *work) -{ - struct usb_device *udev = - container_of(work, struct usb_device, autosuspend.work); - - usb_pm_lock(udev); - udev->auto_pm = 1; - usb_suspend_both(udev, PMSG_SUSPEND); - usb_pm_unlock(udev); -} - -#else - -static void usb_autosuspend_work(struct work_struct *work) -{} - -#endif /* CONFIG_USB_SUSPEND */ - #else #define ksuspend_usb_init() 0 @@ -267,13 +252,10 @@ usb_alloc_dev(struct usb_device *parent, device_initialize(&dev->dev); dev->dev.bus = &usb_bus_type; + dev->dev.type = &usb_device_type; dev->dev.dma_mask = bus->controller->dma_mask; - dev->dev.release = usb_release_dev; dev->state = USB_STATE_ATTACHED; - /* This magic assignment distinguishes devices from interfaces */ - dev->dev.platform_data = &usb_generic_driver; - INIT_LIST_HEAD(&dev->ep0.urb_list); dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; @@ -902,9 +884,9 @@ static int __init usb_init(void) retval = usb_register(&usbfs_driver); if (retval) goto driver_register_failed; - retval = usbdev_init(); + retval = usb_devio_init(); if (retval) - goto usbdevice_init_failed; + goto usb_devio_init_failed; retval = usbfs_init(); if (retval) goto fs_init_failed; @@ -919,8 +901,8 @@ static int __init usb_init(void) hub_init_failed: usbfs_cleanup(); fs_init_failed: - usbdev_cleanup(); -usbdevice_init_failed: + usb_devio_cleanup(); +usb_devio_init_failed: usb_deregister(&usbfs_driver); driver_register_failed: usb_major_cleanup(); @@ -947,7 +929,7 @@ static void __exit usb_exit(void) usb_major_cleanup(); usbfs_cleanup(); usb_deregister(&usbfs_driver); - usbdev_cleanup(); + usb_devio_cleanup(); usb_hub_cleanup(); usb_host_cleanup(); bus_unregister(&usb_bus_type); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 08b5a04..bf2eb0d 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -21,7 +21,6 @@ extern char *usb_cache_string(struct usb extern int usb_set_configuration(struct usb_device *dev, int configuration); extern void usb_kick_khubd(struct usb_device *dev); -extern void usb_resume_root_hub(struct usb_device *dev); extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); @@ -34,10 +33,12 @@ extern void usb_host_cleanup(void); #ifdef CONFIG_PM -extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg); -extern int usb_resume_both(struct usb_device *udev); +extern void usb_autosuspend_work(struct work_struct *work); extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); +extern int usb_external_suspend_device(struct usb_device *udev, + pm_message_t msg); +extern int usb_external_resume_device(struct usb_device *udev); static inline void usb_pm_lock(struct usb_device *udev) { @@ -51,11 +52,6 @@ static inline void usb_pm_unlock(struct #else -#define usb_suspend_both(udev, msg) 0 -static inline int usb_resume_both(struct usb_device *udev) -{ - return 0; -} #define usb_port_suspend(dev) 0 #define usb_port_resume(dev) 0 static inline void usb_pm_lock(struct usb_device *udev) {} @@ -82,15 +78,13 @@ #endif extern struct workqueue_struct *ksuspend_usb_wq; extern struct bus_type usb_bus_type; +extern struct device_type usb_device_type; +extern struct device_type usb_if_device_type; extern struct usb_device_driver usb_generic_driver; -/* Here's how we tell apart devices and interfaces. Luckily there's - * no such thing as a platform USB device, so we can steal the use - * of the platform_data field. */ - static inline int is_usb_device(const struct device *dev) { - return dev->platform_data == &usb_generic_driver; + return dev->type == &usb_device_type; } /* Do the same for device drivers and interface drivers. */ @@ -126,11 +120,11 @@ extern const char *usbcore_name; extern struct mutex usbfs_mutex; extern struct usb_driver usbfs_driver; extern const struct file_operations usbfs_devices_fops; -extern const struct file_operations usbfs_device_file_operations; +extern const struct file_operations usbdev_file_operations; extern void usbfs_conn_disc_event(void); -extern int usbdev_init(void); -extern void usbdev_cleanup(void); +extern int usb_devio_init(void); +extern void usb_devio_cleanup(void); struct dev_state { struct list_head list; /* state list */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 4097a86..8065f2b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -68,6 +68,27 @@ choice Many controller drivers are platform-specific; these often need board-specific hooks. +config USB_GADGET_FSL_USB2 + boolean "Freescale Highspeed USB DR Peripheral Controller" + depends on MPC834x || PPC_MPC831x + select USB_GADGET_DUALSPEED + help + Some of Freescale PowerPC processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. + + The number of programmable endpoints is different through + SOC revisions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fsl_usb2_udc" and force + all gadget drivers to also be dynamically linked. + +config USB_FSL_USB2 + tristate + depends on USB_GADGET_FSL_USB2 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_NET2280 boolean "NetChip 228x" depends on PCI @@ -370,6 +391,7 @@ config USB_GADGETFS config USB_FILE_STORAGE tristate "File-backed Storage Gadget" + depends on BLOCK help The File-backed Storage Gadget acts as a USB Mass Storage disk drive. As its storage repository it can use a regular diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e71e086..5db1939 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o obj-$(CONFIG_USB_AT91) += at91_udc.o +obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 04e6b85..1dd8b57 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -282,6 +282,9 @@ #ifdef CONFIG_USB_GADGET_HUSB2DEV #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_FSL_USB2 +#define DEV_CONFIG_CDC +#endif /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -1735,7 +1738,8 @@ enomem: defer_kevent (dev, WORK_RX_MEMORY); if (retval) { DEBUG (dev, "rx submit --> %d\n", retval); - dev_kfree_skb_any (skb); + if (skb) + dev_kfree_skb_any(skb); spin_lock(&dev->req_lock); list_add (&req->list, &dev->rx_reqs); spin_unlock(&dev->req_lock); @@ -1766,7 +1770,6 @@ static void rx_complete (struct usb_ep * break; } - skb->dev = dev->net; skb->protocol = eth_type_trans (skb, dev->net); dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c new file mode 100644 index 0000000..157054e --- /dev/null +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -0,0 +1,2500 @@ +/* + * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved. + * + * Author: Li Yang + * Jiang Bo + * + * Description: + * Freescale high-speed USB SOC DR module device controller driver. + * This can be found on MPC8349E/MPC8313E cpus. + * The driver is previously named as mpc_udc. Based on bare board + * code from Dave Liu and Shlomi Gridish. + * + * 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. + */ + +#undef VERBOSE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_usb2_udc.h" + +#define DRIVER_DESC "Freescale High-Speed USB SOC Device Controller driver" +#define DRIVER_AUTHOR "Li Yang/Jiang Bo" +#define DRIVER_VERSION "Apr 20, 2007" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +static const char driver_name[] = "fsl-usb2-udc"; +static const char driver_desc[] = DRIVER_DESC; + +volatile static struct usb_dr_device *dr_regs = NULL; +volatile static struct usb_sys_interface *usb_sys_regs = NULL; + +/* it is initialized in probe() */ +static struct fsl_udc *udc_controller = NULL; + +static const struct usb_endpoint_descriptor +fsl_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD, +}; + +static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state); +static int fsl_udc_resume(struct platform_device *pdev); +static void fsl_ep_fifo_flush(struct usb_ep *_ep); + +#ifdef CONFIG_PPC32 +#define fsl_readl(addr) in_le32(addr) +#define fsl_writel(addr, val32) out_le32(val32, addr) +#else +#define fsl_readl(addr) readl(addr) +#define fsl_writel(addr, val32) writel(addr, val32) +#endif + +/******************************************************************** + * Internal Used Function +********************************************************************/ +/*----------------------------------------------------------------- + * done() - retire a request; caller blocked irqs + * @status : request status to be set, only works when + * request is still in progress. + *--------------------------------------------------------------*/ +static void done(struct fsl_ep *ep, struct fsl_req *req, int status) +{ + struct fsl_udc *udc = NULL; + unsigned char stopped = ep->stopped; + struct ep_td_struct *curr_td, *next_td; + int j; + + udc = (struct fsl_udc *)ep->udc; + /* Removed the req from fsl_ep->queue */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* Free dtd for the request */ + next_td = req->head; + for (j = 0; j < req->dtd_count; j++) { + curr_td = next_td; + if (j != req->dtd_count - 1) { + next_td = curr_td->next_td_virt; + } + dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma); + } + + if (req->mapped) { + dma_unmap_single(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else + dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + if (status && (status != -ESHUTDOWN)) + VDBG("complete %s req %p stat %d len %u/%u", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + ep->stopped = 1; + + spin_unlock(&ep->udc->lock); + /* complete() is from gadget layer, + * eg fsg->bulk_in_complete() */ + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + + spin_lock(&ep->udc->lock); + ep->stopped = stopped; +} + +/*----------------------------------------------------------------- + * nuke(): delete all requests related to this ep + * called with spinlock held + *--------------------------------------------------------------*/ +static void nuke(struct fsl_ep *ep, int status) +{ + ep->stopped = 1; + + /* Flush fifo */ + fsl_ep_fifo_flush(&ep->ep); + + /* Whether this eq has request linked */ + while (!list_empty(&ep->queue)) { + struct fsl_req *req = NULL; + + req = list_entry(ep->queue.next, struct fsl_req, queue); + done(ep, req, status); + } +} + +/*------------------------------------------------------------------ + Internal Hardware related function + ------------------------------------------------------------------*/ + +static int dr_controller_setup(struct fsl_udc *udc) +{ + unsigned int tmp = 0, portctrl = 0, ctrl = 0; + unsigned long timeout; +#define FSL_UDC_RESET_TIMEOUT 1000 + + /* before here, make sure dr_regs has been initialized */ + if (!udc) + return -EINVAL; + + /* Stop and reset the usb controller */ + tmp = fsl_readl(&dr_regs->usbcmd); + tmp &= ~USB_CMD_RUN_STOP; + fsl_writel(tmp, &dr_regs->usbcmd); + + tmp = fsl_readl(&dr_regs->usbcmd); + tmp |= USB_CMD_CTRL_RESET; + fsl_writel(tmp, &dr_regs->usbcmd); + + /* Wait for reset to complete */ + timeout = jiffies + FSL_UDC_RESET_TIMEOUT; + while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) { + if (time_after(jiffies, timeout)) { + ERR("udc reset timeout! \n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + /* Set the controller as device mode */ + tmp = fsl_readl(&dr_regs->usbmode); + tmp |= USB_MODE_CTRL_MODE_DEVICE; + /* Disable Setup Lockout */ + tmp |= USB_MODE_SETUP_LOCK_OFF; + fsl_writel(tmp, &dr_regs->usbmode); + + /* Clear the setup status */ + fsl_writel(0, &dr_regs->usbsts); + + tmp = udc->ep_qh_dma; + tmp &= USB_EP_LIST_ADDRESS_MASK; + fsl_writel(tmp, &dr_regs->endpointlistaddr); + + VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x", + (int)udc->ep_qh, (int)tmp, + fsl_readl(&dr_regs->endpointlistaddr)); + + /* Config PHY interface */ + portctrl = fsl_readl(&dr_regs->portsc1); + portctrl &= ~PORTSCX_PHY_TYPE_SEL; + switch (udc->phy_mode) { + case FSL_USB2_PHY_ULPI: + portctrl |= PORTSCX_PTS_ULPI; + break; + case FSL_USB2_PHY_UTMI: + case FSL_USB2_PHY_UTMI_WIDE: + portctrl |= PORTSCX_PTS_UTMI; + break; + case FSL_USB2_PHY_SERIAL: + portctrl |= PORTSCX_PTS_FSLS; + break; + default: + return -EINVAL; + } + fsl_writel(portctrl, &dr_regs->portsc1); + + /* Config control enable i/o output, cpu endian register */ + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= USB_CTRL_IOENB; + __raw_writel(ctrl, &usb_sys_regs->control); + +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + /* Turn on cache snooping hardware, since some PowerPC platforms + * wholly rely on hardware to deal with cache coherent. */ + + /* Setup Snooping for all the 4GB space */ + tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop1); + tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop2); +#endif + + return 0; +} + +/* Enable DR irq and set controller to run state */ +static void dr_controller_run(struct fsl_udc *udc) +{ + u32 temp; + + /* Enable DR irq reg */ + temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN + | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN + | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN; + + fsl_writel(temp, &dr_regs->usbintr); + + /* Clear stopped bit */ + udc->stopped = 0; + + /* Set the controller as device mode */ + temp = fsl_readl(&dr_regs->usbmode); + temp |= USB_MODE_CTRL_MODE_DEVICE; + fsl_writel(temp, &dr_regs->usbmode); + + /* Set controller to Run */ + temp = fsl_readl(&dr_regs->usbcmd); + temp |= USB_CMD_RUN_STOP; + fsl_writel(temp, &dr_regs->usbcmd); + + return; +} + +static void dr_controller_stop(struct fsl_udc *udc) +{ + unsigned int tmp; + + /* disable all INTR */ + fsl_writel(0, &dr_regs->usbintr); + + /* Set stopped bit for isr */ + udc->stopped = 1; + + /* disable IO output */ +/* usb_sys_regs->control = 0; */ + + /* set controller to Stop */ + tmp = fsl_readl(&dr_regs->usbcmd); + tmp &= ~USB_CMD_RUN_STOP; + fsl_writel(tmp, &dr_regs->usbcmd); + + return; +} + +void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type) +{ + unsigned int tmp_epctrl = 0; + + tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (dir) { + if (ep_num) + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_TX_EP_TYPE_SHIFT); + } else { + if (ep_num) + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_RX_EP_TYPE_SHIFT); + } + + fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]); +} + +static void +dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value) +{ + u32 tmp_epctrl = 0; + + tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + + if (value) { + /* set the stall bit */ + if (dir) + tmp_epctrl |= EPCTRL_TX_EP_STALL; + else + tmp_epctrl |= EPCTRL_RX_EP_STALL; + } else { + /* clear the stall bit and reset data toggle */ + if (dir) { + tmp_epctrl &= ~EPCTRL_TX_EP_STALL; + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + } else { + tmp_epctrl &= ~EPCTRL_RX_EP_STALL; + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + } + } + fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]); +} + +/* Get stall status of a specific ep + Return: 0: not stalled; 1:stalled */ +static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir) +{ + u32 epctrl; + + epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (dir) + return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0; + else + return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0; +} + +/******************************************************************** + Internal Structure Build up functions +********************************************************************/ + +/*------------------------------------------------------------------ +* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH + * @zlt: Zero Length Termination Select (1: disable; 0: enable) + * @mult: Mult field + ------------------------------------------------------------------*/ +static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, + unsigned char dir, unsigned char ep_type, + unsigned int max_pkt_len, + unsigned int zlt, unsigned char mult) +{ + struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir]; + unsigned int tmp = 0; + + /* set the Endpoint Capabilites in QH */ + switch (ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + /* Interrupt On Setup (IOS). for control ep */ + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | EP_QUEUE_HEAD_IOS; + break; + case USB_ENDPOINT_XFER_ISOC: + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | (mult << EP_QUEUE_HEAD_MULT_POS); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; + break; + default: + VDBG("error ep type is %d", ep_type); + return; + } + if (zlt) + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + p_QH->max_pkt_length = cpu_to_le32(tmp); + + return; +} + +/* Setup qh structure and ep register for ep0. */ +static void ep0_setup(struct fsl_udc *udc) +{ + /* the intialization of an ep includes: fields in QH, Regs, + * fsl_ep struct */ + struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 0, 0); + struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 0, 0); + dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL); + dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL); + + return; + +} + +/*********************************************************************** + Endpoint Management Functions +***********************************************************************/ + +/*------------------------------------------------------------------------- + * when configurations are set, or when interface settings change + * for example the do_set_interface() in gadget layer, + * the driver will enable or disable the relevant endpoints + * ep0 doesn't use this routine. It is always enabled. +-------------------------------------------------------------------------*/ +static int fsl_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fsl_udc *udc = NULL; + struct fsl_ep *ep = NULL; + unsigned short max = 0; + unsigned char mult = 0, zlt; + int retval = -EINVAL; + unsigned long flags = 0; + + ep = container_of(_ep, struct fsl_ep, ep); + + /* catch various bogus parameters */ + if (!_ep || !desc || ep->desc + || (desc->bDescriptorType != USB_DT_ENDPOINT)) + return -EINVAL; + + udc = ep->udc; + + if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize); + + /* Disable automatic zlp generation. Driver is reponsible to indicate + * explicitly through req->req.zero. This is needed to enable multi-td + * request. */ + zlt = 1; + + /* Assume the max packet size from gadget is always correct */ + switch (desc->bmAttributes & 0x03) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + /* mult = 0. Execute N Transactions as demonstrated by + * the USB variable length packet protocol where N is + * computed using the Maximum Packet Length (dQH) and + * the Total Bytes field (dTD) */ + mult = 0; + break; + case USB_ENDPOINT_XFER_ISOC: + /* Calculate transactions needed for high bandwidth iso */ + mult = (unsigned char)(1 + ((max >> 11) & 0x03)); + max = max & 0x8ff; /* bit 0~10 */ + /* 3 transactions at most */ + if (mult > 3) + goto en_done; + break; + default: + goto en_done; + } + + spin_lock_irqsave(&udc->lock, flags); + ep->ep.maxpacket = max; + ep->desc = desc; + ep->stopped = 0; + + /* Controller related setup */ + /* Init EPx Queue Head (Ep Capabilites field in QH + * according to max, zlt, mult) */ + struct_ep_qh_setup(udc, (unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK), + max, zlt, mult); + + /* Init endpoint ctrl register */ + dr_ep_setup((unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK)); + + spin_unlock_irqrestore(&udc->lock, flags); + retval = 0; + + VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name, + ep->desc->bEndpointAddress & 0x0f, + (desc->bEndpointAddress & USB_DIR_IN) + ? "in" : "out", max); +en_done: + return retval; +} + +/*--------------------------------------------------------------------- + * @ep : the ep being unconfigured. May not be ep0 + * Any pending and uncomplete req will complete with status (-ESHUTDOWN) +*---------------------------------------------------------------------*/ +static int fsl_ep_disable(struct usb_ep *_ep) +{ + struct fsl_udc *udc = NULL; + struct fsl_ep *ep = NULL; + unsigned long flags = 0; + u32 epctrl; + int ep_num; + + ep = container_of(_ep, struct fsl_ep, ep); + if (!_ep || !ep->desc) { + VDBG("%s not enabled", _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + /* disable ep on controller */ + ep_num = ep_index(ep); + epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); + + udc = (struct fsl_udc *)ep->udc; + spin_lock_irqsave(&udc->lock, flags); + + /* nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->desc = 0; + ep->stopped = 1; + spin_unlock_irqrestore(&udc->lock, flags); + + VDBG("disabled %s OK", _ep->name); + return 0; +} + +/*--------------------------------------------------------------------- + * allocate a request object used by this endpoint + * the main operation is to insert the req->queue to the eq->queue + * Returns the request, or null if one could not be allocated +*---------------------------------------------------------------------*/ +static struct usb_request * +fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct fsl_req *req = NULL; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fsl_req *req = NULL; + + req = container_of(_req, struct fsl_req, req); + + if (_req) + kfree(req); +} + +/*------------------------------------------------------------------ + * Allocate an I/O buffer +*---------------------------------------------------------------------*/ +static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t *dma, gfp_t gfp_flags) +{ + struct fsl_ep *ep; + + if (!_ep) + return NULL; + + ep = container_of(_ep, struct fsl_ep, ep); + + return dma_alloc_coherent(ep->udc->gadget.dev.parent, + bytes, dma, gfp_flags); +} + +/*------------------------------------------------------------------ + * frees an i/o buffer +*---------------------------------------------------------------------*/ +static void fsl_free_buffer(struct usb_ep *_ep, void *buf, + dma_addr_t dma, unsigned bytes) +{ + struct fsl_ep *ep; + + if (!_ep) + return NULL; + + ep = container_of(_ep, struct fsl_ep, ep); + + dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); +} + +/*-------------------------------------------------------------------------*/ +static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) +{ + int i = ep_index(ep) * 2 + ep_is_in(ep); + u32 temp, bitmask, tmp_stat; + struct ep_queue_head *dQH = &ep->udc->ep_qh[i]; + + /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr); + VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */ + + bitmask = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + /* Add td to the end */ + struct fsl_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct fsl_req, queue); + lastreq->tail->next_td_ptr = + cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); + /* Read prime bit, if 1 goto done */ + if (fsl_readl(&dr_regs->endpointprime) & bitmask) + goto out; + + do { + /* Set ATDTW bit in USBCMD */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd); + + /* Read correct status bit */ + tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask; + + } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW)); + + /* Write ATDTW bit to 0 */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd); + + if (tmp_stat) + goto out; + } + + /* Write dQH next pointer and terminate bit to 0 */ + temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + dQH->next_dtd_ptr = cpu_to_le32(temp); + + /* Clear active and halt bit */ + temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + | EP_QUEUE_HEAD_STATUS_HALT)); + dQH->size_ioc_int_sts &= temp; + + /* Prime endpoint by writing 1 to ENDPTPRIME */ + temp = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + fsl_writel(temp, &dr_regs->endpointprime); +out: + return 0; +} + +/* Fill in the dTD structure + * @req: request that the transfer belongs to + * @length: return actually data length of the dTD + * @dma: return dma address of the dTD + * @is_last: return flag if it is the last dTD of the request + * return: pointer to the built dTD */ +static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, + dma_addr_t *dma, int *is_last) +{ + u32 swap_temp; + struct ep_td_struct *dtd; + + /* how big will this transfer be? */ + *length = min(req->req.length - req->req.actual, + (unsigned)EP_MAX_LENGTH_TRANSFER); + + dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma); + if (dtd == NULL) + return dtd; + + dtd->td_dma = *dma; + /* Clear reserved field */ + swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp &= ~DTD_RESERVED_FIELDS; + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + /* Init all of buffer page pointers */ + swap_temp = (u32) (req->req.dma + req->req.actual); + dtd->buff_ptr0 = cpu_to_le32(swap_temp); + dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + + req->req.actual += *length; + + /* zlp is needed if req->req.zero is set */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) + *is_last = 1; + else + *is_last = 0; + + if ((*is_last) == 0) + VDBG("multi-dtd request!\n"); + /* Fill in the transfer size; set active bit */ + swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); + + /* Enable interrupt for the last dtd of a request */ + if (*is_last && !req->req.no_interrupt) + swap_temp |= DTD_IOC; + + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + mb(); + + VDBG("length = %d address= 0x%x", *length, (int)*dma); + + return dtd; +} + +/* Generate dtd chain for a request */ +static int fsl_req_to_dtd(struct fsl_req *req) +{ + unsigned count; + int is_last; + int is_first =1; + struct ep_td_struct *last_dtd = NULL, *dtd; + dma_addr_t dma; + + do { + dtd = fsl_build_dtd(req, &count, &dma, &is_last); + if (dtd == NULL) + return -ENOMEM; + + if (is_first) { + is_first = 0; + req->head = dtd; + } else { + last_dtd->next_td_ptr = cpu_to_le32(dma); + last_dtd->next_td_virt = dtd; + } + last_dtd = dtd; + + req->dtd_count++; + } while (!is_last); + + dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + + req->tail = dtd; + + return 0; +} + +/* queues (submits) an I/O request to an endpoint */ +static int +fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep); + struct fsl_req *req = container_of(_req, struct fsl_req, req); + struct fsl_udc *udc; + unsigned long flags; + int is_iso = 0; + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + VDBG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + if (!_ep || (!ep->desc && ep_index(ep))) { + VDBG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + is_iso = 1; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + req->ep = ep; + + /* map virtual address to hardware */ + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, + req->req.length, ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 1; + } else { + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 0; + } + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->dtd_count = 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* build dtds and push them to device queue */ + if (!fsl_req_to_dtd(req)) { + fsl_queue_td(ep, req); + } else { + spin_unlock_irqrestore(&udc->lock, flags); + return -ENOMEM; + } + + /* Update ep0 state */ + if ((ep_index(ep) == 0)) + udc->ep0_state = DATA_STATE_XMIT; + + /* irq handler advances the queue */ + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep); + struct fsl_req *req; + unsigned long flags; + int ep_num, stopped, ret = 0; + u32 epctrl; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + stopped = ep->stopped; + + /* Stop the ep before we deal with the queue */ + ep->stopped = 1; + ep_num = ep_index(ep); + epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + ret = -EINVAL; + goto out; + } + + /* The request is in progress, or completed but not dequeued */ + if (ep->queue.next == &req->queue) { + _req->status = -ECONNRESET; + fsl_ep_fifo_flush(_ep); /* flush current transfer */ + + /* The request isn't the last request in this ep queue */ + if (req->queue.next != &ep->queue) { + struct ep_queue_head *qh; + struct fsl_req *next_req; + + qh = ep->qh; + next_req = list_entry(req->queue.next, struct fsl_req, + queue); + + /* Point the QH to the first TD of next request */ + fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr); + } + + /* The request hasn't been processed, patch up the TD chain */ + } else { + struct fsl_req *prev_req; + + prev_req = list_entry(req->queue.prev, struct fsl_req, queue); + fsl_writel(fsl_readl(&req->tail->next_td_ptr), + &prev_req->tail->next_td_ptr); + + } + + done(ep, req, -ECONNRESET); + + /* Enable EP */ +out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (ep_is_in(ep)) + epctrl |= EPCTRL_TX_ENABLE; + else + epctrl |= EPCTRL_RX_ENABLE; + fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); + ep->stopped = stopped; + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return ret; +} + +/*-------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------- + * modify the endpoint halt feature + * @ep: the non-isochronous endpoint being stalled + * @value: 1--set halt 0--clear halt + * Returns zero, or a negative error code. +*----------------------------------------------------------------*/ +static int fsl_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct fsl_ep *ep = NULL; + unsigned long flags = 0; + int status = -EOPNOTSUPP; /* operation not supported */ + unsigned char ep_dir = 0, ep_num = 0; + struct fsl_udc *udc = NULL; + + ep = container_of(_ep, struct fsl_ep, ep); + udc = ep->udc; + if (!_ep || !ep->desc) { + status = -EINVAL; + goto out; + } + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* Attempt to halt IN ep will fail if any transfer requests + * are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + status = 0; + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + ep_num = (unsigned char)(ep_index(ep)); + spin_lock_irqsave(&ep->udc->lock, flags); + dr_ep_change_stall(ep_num, ep_dir, value); + spin_unlock_irqrestore(&ep->udc->lock, flags); + + if (ep_index(ep) == 0) { + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + } +out: + VDBG(" %s %s halt stat %d", ep->ep.name, + value ? "set" : "clear", status); + + return status; +} + +static void fsl_ep_fifo_flush(struct usb_ep *_ep) +{ + struct fsl_ep *ep; + int ep_num, ep_dir; + u32 bits; + unsigned long timeout; +#define FSL_UDC_FLUSH_TIMEOUT 1000 + + if (!_ep) { + return; + } else { + ep = container_of(_ep, struct fsl_ep, ep); + if (!ep->desc) + return; + } + ep_num = ep_index(ep); + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + + if (ep_num == 0) + bits = (1 << 16) | 1; + else if (ep_dir == USB_SEND) + bits = 1 << (16 + ep_num); + else + bits = 1 << ep_num; + + timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT; + do { + fsl_writel(bits, &dr_regs->endptflush); + + /* Wait until flush complete */ + while (fsl_readl(&dr_regs->endptflush)) { + if (time_after(jiffies, timeout)) { + ERR("ep flush timeout\n"); + return; + } + cpu_relax(); + } + /* See if we need to flush again */ + } while (fsl_readl(&dr_regs->endptstatus) & bits); +} + +static struct usb_ep_ops fsl_ep_ops = { + .enable = fsl_ep_enable, + .disable = fsl_ep_disable, + + .alloc_request = fsl_alloc_request, + .free_request = fsl_free_request, + + .alloc_buffer = fsl_alloc_buffer, + .free_buffer = fsl_free_buffer, + + .queue = fsl_ep_queue, + .dequeue = fsl_ep_dequeue, + + .set_halt = fsl_ep_set_halt, + .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */ +}; + +/*------------------------------------------------------------------------- + Gadget Driver Layer Operations +-------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------- + * Get the current frame number (from DR frame_index Reg ) + *----------------------------------------------------------------------*/ +static int fsl_get_frame(struct usb_gadget *gadget) +{ + return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS); +} + +/*----------------------------------------------------------------------- + * Tries to wake up the host connected to this gadget + -----------------------------------------------------------------------*/ +static int fsl_wakeup(struct usb_gadget *gadget) +{ + struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget); + u32 portsc; + + /* Remote wakeup feature not enabled by host */ + if (!udc->remote_wakeup) + return -ENOTSUPP; + + portsc = fsl_readl(&dr_regs->portsc1); + /* not suspended? */ + if (!(portsc & PORTSCX_PORT_SUSPEND)) + return 0; + /* trigger force resume */ + portsc |= PORTSCX_PORT_FORCE_RESUME; + fsl_writel(portsc, &dr_regs->portsc1); + return 0; +} + +static int can_pullup(struct fsl_udc *udc) +{ + return udc->driver && udc->softconnect && udc->vbus_active; +} + +/* Notify controller that VBUS is powered, Called by whatever + detects VBUS sessions */ +static int fsl_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct fsl_udc *udc; + unsigned long flags; + + udc = container_of(gadget, struct fsl_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + VDBG("VBUS %s\n", is_active ? "on" : "off"); + udc->vbus_active = (is_active != 0); + if (can_pullup(udc)) + fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + else + fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/* constrain controller's VBUS power usage + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ +#ifdef CONFIG_USB_OTG + struct fsl_udc *udc; + + udc = container_of(gadget, struct fsl_udc, gadget); + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); +#endif + return -ENOTSUPP; +} + +/* Change Data+ pullup status + * this func is used by usb_gadget_connect/disconnet + */ +static int fsl_pullup(struct usb_gadget *gadget, int is_on) +{ + struct fsl_udc *udc; + + udc = container_of(gadget, struct fsl_udc, gadget); + udc->softconnect = (is_on != 0); + if (can_pullup(udc)) + fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + else + fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + + return 0; +} + +/* defined in usb_gadget.h */ +static struct usb_gadget_ops fsl_gadget_ops = { + .get_frame = fsl_get_frame, + .wakeup = fsl_wakeup, +/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */ + .vbus_session = fsl_vbus_session, + .vbus_draw = fsl_vbus_draw, + .pullup = fsl_pullup, +}; + +/* Set protocol stall on ep0, protocol stall will automatically be cleared + on new transaction */ +static void ep0stall(struct fsl_udc *udc) +{ + u32 tmp; + + /* must set tx and rx to stall at the same time */ + tmp = fsl_readl(&dr_regs->endptctrl[0]); + tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL; + fsl_writel(tmp, &dr_regs->endptctrl[0]); + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; +} + +/* Prime a status phase for ep0 */ +static int ep0_prime_status(struct fsl_udc *udc, int direction) +{ + struct fsl_req *req = udc->status_req; + struct fsl_ep *ep; + int status = 0; + + if (direction == EP_DIR_IN) + udc->ep0_dir = USB_DIR_IN; + else + udc->ep0_dir = USB_DIR_OUT; + + ep = &udc->eps[0]; + udc->ep0_state = WAIT_FOR_OUT_STATUS; + + req->ep = ep; + req->req.length = 0; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + if (fsl_req_to_dtd(req) == 0) + status = fsl_queue_td(ep, req); + else + return -ENOMEM; + + if (status) + ERR("Can't queue ep0 status request \n"); + list_add_tail(&req->queue, &ep->queue); + + return status; +} + +static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe) +{ + struct fsl_ep *ep = get_ep_by_pipe(udc, pipe); + + if (!ep->name) + return 0; + + nuke(ep, -ESHUTDOWN); + + return 0; +} + +/* + * ch9 Set address + */ +static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length) +{ + /* Save the new address to device struct */ + udc->device_address = (u8) value; + /* Update usb state */ + udc->usb_state = USB_STATE_ADDRESS; + /* Status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); +} + +/* + * ch9 Get status + */ +static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, + u16 index, u16 length) +{ + u16 tmp = 0; /* Status, cpu endian */ + + struct fsl_req *req; + struct fsl_ep *ep; + int status = 0; + + ep = &udc->eps[0]; + + if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + /* Get device status */ + tmp = 1 << USB_DEVICE_SELF_POWERED; + tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { + /* Get interface status */ + /* We don't have interface information in udc driver */ + tmp = 0; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { + /* Get endpoint status */ + struct fsl_ep *target_ep; + + target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index)); + + /* stall if endpoint doesn't exist */ + if (!target_ep->desc) + goto stall; + tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep)) + << USB_ENDPOINT_HALT; + } + + udc->ep0_dir = USB_DIR_IN; + /* Borrow the per device status_req */ + req = udc->status_req; + /* Fill in the reqest structure */ + *((u16 *) req->req.buf) = cpu_to_le16(tmp); + req->ep = ep; + req->req.length = 2; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + /* prime the data phase */ + if ((fsl_req_to_dtd(req) == 0)) + status = fsl_queue_td(ep, req); + else /* no mem */ + goto stall; + + if (status) { + ERR("Can't respond to getstatus request \n"); + goto stall; + } + list_add_tail(&req->queue, &ep->queue); + udc->ep0_state = DATA_STATE_XMIT; + return; +stall: + ep0stall(udc); +} + +static void setup_received_irq(struct fsl_udc *udc, + struct usb_ctrlrequest *setup) +{ + u16 wValue = le16_to_cpu(setup->wValue); + u16 wIndex = le16_to_cpu(setup->wIndex); + u16 wLength = le16_to_cpu(setup->wLength); + + udc_reset_ep_queue(udc, 0); + + switch (setup->bRequest) { + /* Request that need Data+Status phase from udc */ + case USB_REQ_GET_STATUS: + if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD)) + != (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength); + break; + + /* Requests that need Status phase from udc */ + case USB_REQ_SET_ADDRESS: + if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD + | USB_RECIP_DEVICE)) + break; + ch9setaddress(udc, wValue, wIndex, wLength); + break; + + /* Handled by udc, no data, status by udc */ + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + { /* status transaction */ + int rc = -EOPNOTSUPP; + + if ((setup->bRequestType & USB_RECIP_MASK) + == USB_RECIP_ENDPOINT) { + int pipe = get_pipe_by_windex(wIndex); + struct fsl_ep *ep; + + if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) + break; + ep = get_ep_by_pipe(udc, pipe); + + spin_unlock(&udc->lock); + rc = fsl_ep_set_halt(&ep->ep, + (setup->bRequest == USB_REQ_SET_FEATURE) + ? 1 : 0); + spin_lock(&udc->lock); + + } else if ((setup->bRequestType & USB_RECIP_MASK) + == USB_RECIP_DEVICE) { + /* Note: The driver has not include OTG support yet. + * This will be set when OTG support is added */ + if (!udc->gadget.is_otg) + break; + else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + rc = 0; + } + if (rc == 0) { + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + } + break; + } + /* Requests handled by gadget */ + default: + if (wLength) { + /* Data phase from gadget, status phase from udc */ + udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? USB_DIR_IN : USB_DIR_OUT; + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = (setup->bRequestType & USB_DIR_IN) + ? DATA_STATE_XMIT : DATA_STATE_RECV; + + } else { + /* No data phase, IN status from gadget */ + udc->ep0_dir = USB_DIR_IN; + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = WAIT_FOR_OUT_STATUS; + } + break; + } +} + +/* Process request for Data or Status phase of ep0 + * prime status phase if needed */ +static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, + struct fsl_req *req) +{ + if (udc->usb_state == USB_STATE_ADDRESS) { + /* Set the new address */ + u32 new_address = (u32) udc->device_address; + fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS, + &dr_regs->deviceaddr); + } + + done(ep0, req, 0); + + switch (udc->ep0_state) { + case DATA_STATE_XMIT: + /* receive status phase */ + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + break; + case DATA_STATE_RECV: + /* send status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + break; + case WAIT_FOR_OUT_STATUS: + udc->ep0_state = WAIT_FOR_SETUP; + break; + case WAIT_FOR_SETUP: + ERR("Unexpect ep0 packets \n"); + break; + default: + ep0stall(udc); + break; + } +} + +/* Tripwire mechanism to ensure a setup packet payload is extracted without + * being corrupted by another incoming setup packet */ +static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) +{ + u32 temp; + struct ep_queue_head *qh; + + qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; + + /* Clear bit in ENDPTSETUPSTAT */ + temp = fsl_readl(&dr_regs->endptsetupstat); + fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat); + + /* while a hazard exists when setup package arrives */ + do { + /* Set Setup Tripwire */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd); + + /* Copy the setup packet to local buffer */ + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW)); + + /* Clear Setup Tripwire */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd); +} + +/* process-ep_req(): free the completed Tds for this req */ +static int process_ep_req(struct fsl_udc *udc, int pipe, + struct fsl_req *curr_req) +{ + struct ep_td_struct *curr_td; + int td_complete, actual, remaining_length, j, tmp; + int status = 0; + int errors = 0; + struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; + int direction = pipe % 2; + + curr_td = curr_req->head; + td_complete = 0; + actual = curr_req->req.length; + + for (j = 0; j < curr_req->dtd_count; j++) { + remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + actual -= remaining_length; + + if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & + DTD_ERROR_MASK)) { + if (errors & DTD_STATUS_HALTED) { + ERR("dTD error %08x QH=%d\n", errors, pipe); + /* Clear the errors and Halt condition */ + tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp &= ~errors; + curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + status = -EPIPE; + /* FIXME: continue with next queued TD? */ + + break; + } + if (errors & DTD_STATUS_DATA_BUFF_ERR) { + VDBG("Transfer overflow"); + status = -EPROTO; + break; + } else if (errors & DTD_STATUS_TRANSACTION_ERR) { + VDBG("ISO error"); + status = -EILSEQ; + break; + } else + ERR("Unknown error has occured (0x%x)!\r\n", + errors); + + } else if (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_STATUS_ACTIVE) { + VDBG("Request not complete"); + status = REQ_UNCOMPLETE; + return status; + } else if (remaining_length) { + if (direction) { + VDBG("Transmit dTD remaining length not zero"); + status = -EPROTO; + break; + } else { + td_complete++; + break; + } + } else { + td_complete++; + VDBG("dTD transmitted successful "); + } + + if (j != curr_req->dtd_count - 1) + curr_td = (struct ep_td_struct *)curr_td->next_td_virt; + } + + if (status) + return status; + + curr_req->req.actual = actual; + + return 0; +} + +/* Process a DTD completion interrupt */ +static void dtd_complete_irq(struct fsl_udc *udc) +{ + u32 bit_pos; + int i, ep_num, direction, bit_mask, status; + struct fsl_ep *curr_ep; + struct fsl_req *curr_req, *temp_req; + + /* Clear the bits in the register */ + bit_pos = fsl_readl(&dr_regs->endptcomplete); + fsl_writel(bit_pos, &dr_regs->endptcomplete); + + if (!bit_pos) + return; + + for (i = 0; i < udc->max_ep * 2; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_mask = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & bit_mask)) + continue; + + curr_ep = get_ep_by_pipe(udc, i); + + /* If the ep is configured */ + if (curr_ep->name == NULL) { + WARN("Invalid EP?"); + continue; + } + + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue, + queue) { + status = process_ep_req(udc, i, curr_req); + + VDBG("status of process_ep_req= %d, ep = %d", + status, ep_num); + if (status == REQ_UNCOMPLETE) + break; + /* write back status to req */ + curr_req->req.status = status; + + if (ep_num == 0) { + ep0_req_complete(udc, curr_ep, curr_req); + break; + } else + done(curr_ep, curr_req, status); + } + } +} + +/* Process a port change interrupt */ +static void port_change_irq(struct fsl_udc *udc) +{ + u32 speed; + + if (udc->bus_reset) + udc->bus_reset = 0; + + /* Bus resetting is finished */ + if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { + /* Get the speed */ + speed = (fsl_readl(&dr_regs->portsc1) + & PORTSCX_PORT_SPEED_MASK); + switch (speed) { + case PORTSCX_PORT_SPEED_HIGH: + udc->gadget.speed = USB_SPEED_HIGH; + break; + case PORTSCX_PORT_SPEED_FULL: + udc->gadget.speed = USB_SPEED_FULL; + break; + case PORTSCX_PORT_SPEED_LOW: + udc->gadget.speed = USB_SPEED_LOW; + break; + default: + udc->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + } + + /* Update USB state */ + if (!udc->resume_state) + udc->usb_state = USB_STATE_DEFAULT; +} + +/* Process suspend interrupt */ +static void suspend_irq(struct fsl_udc *udc) +{ + udc->resume_state = udc->usb_state; + udc->usb_state = USB_STATE_SUSPENDED; + + /* report suspend to the driver, serial.c does not support this */ + if (udc->driver->suspend) + udc->driver->suspend(&udc->gadget); +} + +static void bus_resume(struct fsl_udc *udc) +{ + udc->usb_state = udc->resume_state; + udc->resume_state = 0; + + /* report resume to the driver, serial.c does not support this */ + if (udc->driver->resume) + udc->driver->resume(&udc->gadget); +} + +/* Clear up all ep queues */ +static int reset_queues(struct fsl_udc *udc) +{ + u8 pipe; + + for (pipe = 0; pipe < udc->max_pipes; pipe++) + udc_reset_ep_queue(udc, pipe); + + /* report disconnect; the driver is already quiesced */ + udc->driver->disconnect(&udc->gadget); + + return 0; +} + +/* Process reset interrupt */ +static void reset_irq(struct fsl_udc *udc) +{ + u32 temp; + unsigned long timeout; + + /* Clear the device address */ + temp = fsl_readl(&dr_regs->deviceaddr); + fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr); + + udc->device_address = 0; + + /* Clear usb state */ + udc->resume_state = 0; + udc->ep0_dir = 0; + udc->ep0_state = WAIT_FOR_SETUP; + udc->remote_wakeup = 0; /* default to 0 on reset */ + udc->gadget.b_hnp_enable = 0; + udc->gadget.a_hnp_support = 0; + udc->gadget.a_alt_hnp_support = 0; + + /* Clear all the setup token semaphores */ + temp = fsl_readl(&dr_regs->endptsetupstat); + fsl_writel(temp, &dr_regs->endptsetupstat); + + /* Clear all the endpoint complete status bits */ + temp = fsl_readl(&dr_regs->endptcomplete); + fsl_writel(temp, &dr_regs->endptcomplete); + + timeout = jiffies + 100; + while (fsl_readl(&dr_regs->endpointprime)) { + /* Wait until all endptprime bits cleared */ + if (time_after(jiffies, timeout)) { + ERR("Timeout for reset\n"); + break; + } + cpu_relax(); + } + + /* Write 1s to the flush register */ + fsl_writel(0xffffffff, &dr_regs->endptflush); + + if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { + VDBG("Bus reset"); + /* Bus is reseting */ + udc->bus_reset = 1; + /* Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + reset_queues(udc); + udc->usb_state = USB_STATE_DEFAULT; + } else { + VDBG("Controller reset"); + /* initialize usb hw reg except for regs for EP, not + * touch usbintr reg */ + dr_controller_setup(udc); + + /* Reset all internal used Queues */ + reset_queues(udc); + + ep0_setup(udc); + + /* Enable DR IRQ reg, Set Run bit, change udc state */ + dr_controller_run(udc); + udc->usb_state = USB_STATE_ATTACHED; + } +} + +/* + * USB device controller interrupt handler + */ +static irqreturn_t fsl_udc_irq(int irq, void *_udc) +{ + struct fsl_udc *udc = _udc; + u32 irq_src; + irqreturn_t status = IRQ_NONE; + unsigned long flags; + + /* Disable ISR for OTG host mode */ + if (udc->stopped) + return IRQ_NONE; + spin_lock_irqsave(&udc->lock, flags); + irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr); + /* Clear notification bits */ + fsl_writel(irq_src, &dr_regs->usbsts); + + /* VDBG("irq_src [0x%8x]", irq_src); */ + + /* Need to resume? */ + if (udc->usb_state == USB_STATE_SUSPENDED) + if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0) + bus_resume(udc); + + /* USB Interrupt */ + if (irq_src & USB_STS_INT) { + VDBG("Packet int"); + /* Setup package, we only support ep0 as control ep */ + if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) { + tripwire_handler(udc, 0, + (u8 *) (&udc->local_setup_buff)); + setup_received_irq(udc, &udc->local_setup_buff); + status = IRQ_HANDLED; + } + + /* completion of dtd */ + if (fsl_readl(&dr_regs->endptcomplete)) { + dtd_complete_irq(udc); + status = IRQ_HANDLED; + } + } + + /* SOF (for ISO transfer) */ + if (irq_src & USB_STS_SOF) { + status = IRQ_HANDLED; + } + + /* Port Change */ + if (irq_src & USB_STS_PORT_CHANGE) { + port_change_irq(udc); + status = IRQ_HANDLED; + } + + /* Reset Received */ + if (irq_src & USB_STS_RESET) { + reset_irq(udc); + status = IRQ_HANDLED; + } + + /* Sleep Enable (Suspend) */ + if (irq_src & USB_STS_SUSPEND) { + suspend_irq(udc); + status = IRQ_HANDLED; + } + + if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { + VDBG("Error IRQ %x ", irq_src); + } + + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +/*----------------------------------------------------------------* + * Hook to gadget drivers + * Called by initialization code of gadget drivers +*----------------------------------------------------------------*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int retval = -ENODEV; + unsigned long flags = 0; + + if (!udc_controller) + return -ENODEV; + + if (!driver || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !driver->bind || !driver->disconnect + || !driver->setup) + return -EINVAL; + + if (udc_controller->driver) + return -EBUSY; + + /* lock is needed but whether should use this lock or another */ + spin_lock_irqsave(&udc_controller->lock, flags); + + driver->driver.bus = 0; + /* hook up the driver */ + udc_controller->driver = driver; + udc_controller->gadget.dev.driver = &driver->driver; + spin_unlock_irqrestore(&udc_controller->lock, flags); + + /* bind udc driver to gadget driver */ + retval = driver->bind(&udc_controller->gadget); + if (retval) { + VDBG("bind to %s --> %d", driver->driver.name, retval); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + goto out; + } + + /* Enable DR IRQ reg and Set usbcmd reg Run bit */ + dr_controller_run(udc_controller); + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + printk(KERN_INFO "%s: bind to driver %s \n", + udc_controller->gadget.name, driver->driver.name); + +out: + if (retval) + printk("retval %d \n", retval); + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +/* Disconnect from gadget driver */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct fsl_ep *loop_ep; + unsigned long flags; + + if (!udc_controller) + return -ENODEV; + + if (!driver || driver != udc_controller->driver || !driver->unbind) + return -EINVAL; + +#ifdef CONFIG_USB_OTG + if (udc_controller->transceiver) + (void)otg_set_peripheral(udc_controller->transceiver, 0); +#endif + + /* stop DR, disable intr */ + dr_controller_stop(udc_controller); + + /* in fact, no needed */ + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + + /* stand operation */ + spin_lock_irqsave(&udc_controller->lock, flags); + udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc_controller->eps[0], -ESHUTDOWN); + list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list, + ep.ep_list) + nuke(loop_ep, -ESHUTDOWN); + spin_unlock_irqrestore(&udc_controller->lock, flags); + + /* unbind gadget and unhook driver. */ + driver->unbind(&udc_controller->gadget); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + + printk("unregistered gadget driver '%s'\r\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*------------------------------------------------------------------------- + PROC File System Support +-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +#include + +static const char proc_filename[] = "driver/fsl_usb2_udc"; + +static int fsl_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + char *next = buf; + unsigned size = count; + unsigned long flags; + int t, i; + u32 tmp_reg; + struct fsl_ep *ep = NULL; + struct fsl_req *req; + + struct fsl_udc *udc = udc_controller; + if (off != 0) + return 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* ------basic driver infomation ---- */ + t = scnprintf(next, size, + DRIVER_DESC "\n" + "%s version: %s\n" + "Gadget driver: %s\n\n", + driver_name, DRIVER_VERSION, + udc->driver ? udc->driver->driver.name : "(none)"); + size -= t; + next += t; + + /* ------ DR Registers ----- */ + tmp_reg = fsl_readl(&dr_regs->usbcmd); + t = scnprintf(next, size, + "USBCMD reg:\n" + "SetupTW: %d\n" + "Run/Stop: %s\n\n", + (tmp_reg & USB_CMD_SUTW) ? 1 : 0, + (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop"); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->usbsts); + t = scnprintf(next, size, + "USB Status Reg:\n" + "Dr Suspend: %d" "Reset Received: %d" "System Error: %s" + "USB Error Interrupt: %s\n\n", + (tmp_reg & USB_STS_SUSPEND) ? 1 : 0, + (tmp_reg & USB_STS_RESET) ? 1 : 0, + (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal", + (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err"); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->usbintr); + t = scnprintf(next, size, + "USB Intrrupt Enable Reg:\n" + "Sleep Enable: %d" "SOF Received Enable: %d" + "Reset Enable: %d\n" + "System Error Enable: %d" + "Port Change Dectected Enable: %d\n" + "USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n", + (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0, + (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0, + (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0, + (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0, + (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0, + (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0, + (tmp_reg & USB_INTR_INT_EN) ? 1 : 0); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->frindex); + t = scnprintf(next, size, + "USB Frame Index Reg:" "Frame Number is 0x%x\n\n", + (tmp_reg & USB_FRINDEX_MASKS)); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->deviceaddr); + t = scnprintf(next, size, + "USB Device Address Reg:" "Device Addr is 0x%x\n\n", + (tmp_reg & USB_DEVICE_ADDRESS_MASK)); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->endpointlistaddr); + t = scnprintf(next, size, + "USB Endpoint List Address Reg:" + "Device Addr is 0x%x\n\n", + (tmp_reg & USB_EP_LIST_ADDRESS_MASK)); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->portsc1); + t = scnprintf(next, size, + "USB Port Status&Control Reg:\n" + "Port Transceiver Type : %s" "Port Speed: %s \n" + "PHY Low Power Suspend: %s" "Port Reset: %s" + "Port Suspend Mode: %s \n" "Over-current Change: %s" + "Port Enable/Disable Change: %s\n" + "Port Enabled/Disabled: %s" + "Current Connect Status: %s\n\n", ( { + char *s; + switch (tmp_reg & PORTSCX_PTS_FSLS) { + case PORTSCX_PTS_UTMI: + s = "UTMI"; break; + case PORTSCX_PTS_ULPI: + s = "ULPI "; break; + case PORTSCX_PTS_FSLS: + s = "FS/LS Serial"; break; + default: + s = "None"; break; + } + s;} ), ( { + char *s; + switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) { + case PORTSCX_PORT_SPEED_FULL: + s = "Full Speed"; break; + case PORTSCX_PORT_SPEED_LOW: + s = "Low Speed"; break; + case PORTSCX_PORT_SPEED_HIGH: + s = "High Speed"; break; + default: + s = "Undefined"; break; + } + s; + } ), + (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ? + "Normal PHY mode" : "Low power mode", + (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" : + "Not in Reset", + (tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in", + (tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" : + "No", + (tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" : + "Not change", + (tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" : + "Not correct", + (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ? + "Attached" : "Not-Att"); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->usbmode); + t = scnprintf(next, size, + "USB Mode Reg:" "Controller Mode is : %s\n\n", ( { + char *s; + switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) { + case USB_MODE_CTRL_MODE_IDLE: + s = "Idle"; break; + case USB_MODE_CTRL_MODE_DEVICE: + s = "Device Controller"; break; + case USB_MODE_CTRL_MODE_HOST: + s = "Host Controller"; break; + default: + s = "None"; break; + } + s; + } )); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->endptsetupstat); + t = scnprintf(next, size, + "Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n", + (tmp_reg & EP_SETUP_STATUS_MASK)); + size -= t; + next += t; + + for (i = 0; i < udc->max_ep / 2; i++) { + tmp_reg = fsl_readl(&dr_regs->endptctrl[i]); + t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n", + i, tmp_reg); + size -= t; + next += t; + } + tmp_reg = fsl_readl(&dr_regs->endpointprime); + t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = usb_sys_regs->snoop1; + t = scnprintf(next, size, "\nSnoop1 Reg : = [0x%x]\n\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = usb_sys_regs->control; + t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", + tmp_reg); + size -= t; + next += t; + + /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ + ep = &udc->eps[0]; + t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n", + ep->ep.name, ep_maxpacket(ep), ep_index(ep)); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length 0x%x buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + size -= t; + next += t; + } + } + /* other gadget->eplist ep */ + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + if (ep->desc) { + t = scnprintf(next, size, + "\nFor %s Maxpkt is 0x%x " + "index is 0x%x\n", + ep->ep.name, ep_maxpacket(ep), + ep_index(ep)); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, + "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length" + "0x%x buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + size -= t; + next += t; + } /* end for each_entry of ep req */ + } /* end for else */ + } /* end for if(ep->queue) */ + } /* end (ep->desc) */ + + spin_unlock_irqrestore(&udc->lock, flags); + + *eof = 1; + return count - size; +} + +#define create_proc_file() create_proc_read_entry(proc_filename, \ + 0, NULL, fsl_proc_read, NULL) + +#define remove_proc_file() remove_proc_entry(proc_filename, NULL) + +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + +#define create_proc_file() do {} while (0) +#define remove_proc_file() do {} while (0) + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +/*-------------------------------------------------------------------------*/ + +/* Release udc structures */ +static void fsl_udc_release(struct device *dev) +{ + complete(udc_controller->done); + dma_free_coherent(dev, udc_controller->ep_qh_size, + udc_controller->ep_qh, udc_controller->ep_qh_dma); + kfree(udc_controller); +} + +/****************************************************************** + Internal structure setup functions +*******************************************************************/ +/*------------------------------------------------------------------ + * init resource for globle controller + * Return the udc handle on success or NULL on failure + ------------------------------------------------------------------*/ +static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) +{ + struct fsl_udc *udc; + struct fsl_usb2_platform_data *pdata; + size_t size; + + udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); + if (udc == NULL) { + ERR("malloc udc failed\n"); + return NULL; + } + + pdata = pdev->dev.platform_data; + udc->phy_mode = pdata->phy_mode; + /* max_ep_nr is bidirectional ep number, max_ep doubles the number */ + udc->max_ep = pdata->max_ep_nr * 2; + + udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL); + if (!udc->eps) { + ERR("malloc fsl_ep failed\n"); + goto cleanup; + } + + /* initialized QHs, take care of alignment */ + size = udc->max_ep * sizeof(struct ep_queue_head); + if (size < QH_ALIGNMENT) + size = QH_ALIGNMENT; + else if ((size % QH_ALIGNMENT) != 0) { + size += QH_ALIGNMENT + 1; + size &= ~(QH_ALIGNMENT - 1); + } + udc->ep_qh = dma_alloc_coherent(&pdev->dev, size, + &udc->ep_qh_dma, GFP_KERNEL); + if (!udc->ep_qh) { + ERR("malloc QHs for udc failed\n"); + kfree(udc->eps); + goto cleanup; + } + + udc->ep_qh_size = size; + + /* Initialize ep0 status request structure */ + /* FIXME: fsl_alloc_request() ignores ep argument */ + udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL), + struct fsl_req, req); + /* allocate a small amount of memory to get valid address */ + udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); + udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf); + + udc->resume_state = USB_STATE_NOTATTACHED; + udc->usb_state = USB_STATE_POWERED; + udc->ep0_dir = 0; + udc->remote_wakeup = 0; /* default to 0 on reset */ + spin_lock_init(&udc->lock); + + return udc; + +cleanup: + kfree(udc); + return NULL; +} + +/*---------------------------------------------------------------- + * Setup the fsl_ep struct for eps + * Link fsl_ep->ep to gadget->ep_list + * ep0out is not used so do nothing here + * ep0in should be taken care + *--------------------------------------------------------------*/ +static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, + char *name, int link) +{ + struct fsl_ep *ep = &udc->eps[index]; + + ep->udc = udc; + strcpy(ep->name, name); + ep->ep.name = ep->name; + + ep->ep.ops = &fsl_ep_ops; + ep->stopped = 0; + + /* for ep0: maxP defined in desc + * for other eps, maxP is set by epautoconfig() called by gadget layer + */ + ep->ep.maxpacket = (unsigned short) ~0; + + /* the queue lists any req for this ep */ + INIT_LIST_HEAD(&ep->queue); + + /* gagdet.ep_list used for ep_autoconfig so no ep0 */ + if (link) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + ep->gadget = &udc->gadget; + ep->qh = &udc->ep_qh[index]; + + return 0; +} + +/* Driver probe function + * all intialize operations implemented here except enabling usb_intr reg + */ +static int __init fsl_udc_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = -ENODEV; + unsigned int i; + + if (strcmp(pdev->name, driver_name)) { + VDBG("Wrong device\n"); + return -ENODEV; + } + + /* board setup should have been done in the platform code */ + + /* Initialize the udc structure including QH member and other member */ + udc_controller = struct_udc_setup(pdev); + if (!udc_controller) { + VDBG("udc_controller is NULL \n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, res->end - res->start + 1, + driver_name)) { + ERR("request mem region for %s failed \n", pdev->name); + return -EBUSY; + } + + dr_regs = ioremap(res->start, res->end - res->start + 1); + if (!dr_regs) { + ret = -ENOMEM; + goto err1; + } + + usb_sys_regs = (struct usb_sys_interface *) + ((u32)dr_regs + USB_DR_SYS_OFFSET); + + udc_controller->irq = platform_get_irq(pdev, 0); + if (!udc_controller->irq) { + ret = -ENODEV; + goto err2; + } + + ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ, + driver_name, udc_controller); + if (ret != 0) { + ERR("cannot request irq %d err %d \n", + udc_controller->irq, ret); + goto err2; + } + + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + dr_controller_setup(udc_controller); + + /* Setup gadget structure */ + udc_controller->gadget.ops = &fsl_gadget_ops; + udc_controller->gadget.is_dualspeed = 1; + udc_controller->gadget.ep0 = &udc_controller->eps[0].ep; + INIT_LIST_HEAD(&udc_controller->gadget.ep_list); + udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + udc_controller->gadget.name = driver_name; + + /* Setup gadget.dev and register with kernel */ + strcpy(udc_controller->gadget.dev.bus_id, "gadget"); + udc_controller->gadget.dev.release = fsl_udc_release; + udc_controller->gadget.dev.parent = &pdev->dev; + ret = device_register(&udc_controller->gadget.dev); + if (ret < 0) + goto err3; + + /* setup QH and epctrl for ep0 */ + ep0_setup(udc_controller); + + /* setup udc->eps[] for ep0 */ + struct_ep_setup(udc_controller, 0, "ep0", 0); + /* for ep0: the desc defined here; + * for other eps, gadget layer called ep_enable with defined desc + */ + udc_controller->eps[0].desc = &fsl_ep0_desc; + udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + + /* setup the udc->eps[] for non-control endpoints and link + * to gadget.ep_list */ + for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) { + char name[14]; + + sprintf(name, "ep%dout", i); + struct_ep_setup(udc_controller, i * 2, name, 1); + sprintf(name, "ep%din", i); + struct_ep_setup(udc_controller, i * 2 + 1, name, 1); + } + + /* use dma_pool for TD management */ + udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev, + sizeof(struct ep_td_struct), + DTD_ALIGNMENT, UDC_DMA_BOUNDARY); + if (udc_controller->td_pool == NULL) { + ret = -ENOMEM; + goto err4; + } + create_proc_file(); + return 0; + +err4: + device_unregister(&udc_controller->gadget.dev); +err3: + free_irq(udc_controller->irq, udc_controller); +err2: + iounmap(dr_regs); +err1: + release_mem_region(res->start, res->end - res->start + 1); + return ret; +} + +/* Driver removal function + * Free resources and finish pending transactions + */ +static int __exit fsl_udc_remove(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + DECLARE_COMPLETION(done); + + if (!udc_controller) + return -ENODEV; + udc_controller->done = &done; + + /* DR has been stopped in usb_gadget_unregister_driver() */ + remove_proc_file(); + + /* Free allocated memory */ + kfree(udc_controller->status_req->req.buf); + kfree(udc_controller->status_req); + kfree(udc_controller->eps); + + dma_pool_destroy(udc_controller->td_pool); + free_irq(udc_controller->irq, udc_controller); + iounmap(dr_regs); + release_mem_region(res->start, res->end - res->start + 1); + + device_unregister(&udc_controller->gadget.dev); + /* free udc --wait for the release() finished */ + wait_for_completion(&done); + + return 0; +} + +/*----------------------------------------------------------------- + * Modify Power management attributes + * Used by OTG statemachine to disable gadget temporarily + -----------------------------------------------------------------*/ +static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state) +{ + dr_controller_stop(udc_controller); + return 0; +} + +/*----------------------------------------------------------------- + * Invoked on USB resume. May be called in_interrupt. + * Here we start the DR controller and enable the irq + *-----------------------------------------------------------------*/ +static int fsl_udc_resume(struct platform_device *pdev) +{ + /* Enable DR irq reg and set controller Run */ + if (udc_controller->stopped) { + dr_controller_setup(udc_controller); + dr_controller_run(udc_controller); + } + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + return 0; +} + +/*------------------------------------------------------------------------- + Register entry point for the peripheral controller driver +--------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { + .remove = __exit_p(fsl_udc_remove), + /* these suspend and resume are not usb suspend and resume */ + .suspend = fsl_udc_suspend, + .resume = fsl_udc_resume, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init udc_init(void) +{ + printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION); + return platform_driver_probe(&udc_driver, fsl_udc_probe); +} + +module_init(udc_init); + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver); + printk("%s unregistered \n", driver_desc); +} + +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h new file mode 100644 index 0000000..c6291e0 --- /dev/null +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -0,0 +1,579 @@ +/* + * Freescale USB device/endpoint management registers + */ +#ifndef __FSL_USB2_UDC_H +#define __FSL_USB2_UDC_H + +/* ### define USB registers here + */ +#define USB_MAX_CTRL_PAYLOAD 64 +#define USB_DR_SYS_OFFSET 0x400 + + /* USB DR device mode registers (Little Endian) */ +struct usb_dr_device { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[24]; + u32 configflag; /* Configure Flag Register */ + u32 portsc1; /* Port 1 Status and Control Register */ + u8 res7[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ +}; + + /* USB DR host mode registers (Little Endian) */ +struct usb_dr_host { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 periodiclistbase; /* Periodic Frame List Base Address Register */ + u32 asynclistaddr; /* Current Asynchronous List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[24]; + u32 configflag; /* Configure Flag Register */ + u32 portsc1; /* Port 1 Status and Control Register */ + u8 res7[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ +}; + + /* non-EHCI USB system interface registers (Big Endian) */ +struct usb_sys_interface { + u32 snoop1; + u32 snoop2; + u32 age_cnt_thresh; /* Age Count Threshold Register */ + u32 pri_ctrl; /* Priority Control Register */ + u32 si_ctrl; /* System Interface Control Register */ + u8 res[236]; + u32 control; /* General Purpose Control Register */ +}; + +/* ep0 transfer state */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +/* Frame Index Register Bit Masks */ +#define USB_FRINDEX_MASKS 0x3fff +/* USB CMD Register Bit Masks */ +#define USB_CMD_RUN_STOP 0x00000001 +#define USB_CMD_CTRL_RESET 0x00000002 +#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010 +#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020 +#define USB_CMD_INT_AA_DOORBELL 0x00000040 +#define USB_CMD_ASP 0x00000300 +#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800 +#define USB_CMD_SUTW 0x00002000 +#define USB_CMD_ATDTW 0x00004000 +#define USB_CMD_ITC 0x00FF0000 + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 0x00000000 +#define USB_CMD_FRAME_SIZE_512 0x00000004 +#define USB_CMD_FRAME_SIZE_256 0x00000008 +#define USB_CMD_FRAME_SIZE_128 0x0000000C +#define USB_CMD_FRAME_SIZE_64 0x00008000 +#define USB_CMD_FRAME_SIZE_32 0x00008004 +#define USB_CMD_FRAME_SIZE_16 0x00008008 +#define USB_CMD_FRAME_SIZE_8 0x0000800C + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 0x00000000 +#define USB_CMD_ASP_01 0x00000100 +#define USB_CMD_ASP_10 0x00000200 +#define USB_CMD_ASP_11 0x00000300 +#define USB_CMD_ASP_BIT_POS 8 + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD 0x00000000 +#define USB_CMD_ITC_1_MICRO_FRM 0x00010000 +#define USB_CMD_ITC_2_MICRO_FRM 0x00020000 +#define USB_CMD_ITC_4_MICRO_FRM 0x00040000 +#define USB_CMD_ITC_8_MICRO_FRM 0x00080000 +#define USB_CMD_ITC_16_MICRO_FRM 0x00100000 +#define USB_CMD_ITC_32_MICRO_FRM 0x00200000 +#define USB_CMD_ITC_64_MICRO_FRM 0x00400000 +#define USB_CMD_ITC_BIT_POS 16 + +/* USB STS Register Bit Masks */ +#define USB_STS_INT 0x00000001 +#define USB_STS_ERR 0x00000002 +#define USB_STS_PORT_CHANGE 0x00000004 +#define USB_STS_FRM_LST_ROLL 0x00000008 +#define USB_STS_SYS_ERR 0x00000010 +#define USB_STS_IAA 0x00000020 +#define USB_STS_RESET 0x00000040 +#define USB_STS_SOF 0x00000080 +#define USB_STS_SUSPEND 0x00000100 +#define USB_STS_HC_HALTED 0x00001000 +#define USB_STS_RCL 0x00002000 +#define USB_STS_PERIODIC_SCHEDULE 0x00004000 +#define USB_STS_ASYNC_SCHEDULE 0x00008000 + +/* USB INTR Register Bit Masks */ +#define USB_INTR_INT_EN 0x00000001 +#define USB_INTR_ERR_INT_EN 0x00000002 +#define USB_INTR_PTC_DETECT_EN 0x00000004 +#define USB_INTR_FRM_LST_ROLL_EN 0x00000008 +#define USB_INTR_SYS_ERR_EN 0x00000010 +#define USB_INTR_ASYN_ADV_EN 0x00000020 +#define USB_INTR_RESET_EN 0x00000040 +#define USB_INTR_SOF_EN 0x00000080 +#define USB_INTR_DEVICE_SUSPEND 0x00000100 + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK 0xFE000000 +#define USB_DEVICE_ADDRESS_BIT_POS 25 + +/* endpoint list address bit masks */ +#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 + +/* PORTSCX Register Bit Masks */ +#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001 +#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002 +#define PORTSCX_PORT_ENABLE 0x00000004 +#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008 +#define PORTSCX_OVER_CURRENT_ACT 0x00000010 +#define PORTSCX_OVER_CURRENT_CHG 0x00000020 +#define PORTSCX_PORT_FORCE_RESUME 0x00000040 +#define PORTSCX_PORT_SUSPEND 0x00000080 +#define PORTSCX_PORT_RESET 0x00000100 +#define PORTSCX_LINE_STATUS_BITS 0x00000C00 +#define PORTSCX_PORT_POWER 0x00001000 +#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000 +#define PORTSCX_PORT_TEST_CTRL 0x000F0000 +#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000 +#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000 +#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000 +#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000 +#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000 +#define PORTSCX_PORT_SPEED_MASK 0x0C000000 +#define PORTSCX_PORT_WIDTH 0x10000000 +#define PORTSCX_PHY_TYPE_SEL 0xC0000000 + +/* bit 11-10 are line status */ +#define PORTSCX_LINE_STATUS_SE0 0x00000000 +#define PORTSCX_LINE_STATUS_JSTATE 0x00000400 +#define PORTSCX_LINE_STATUS_KSTATE 0x00000800 +#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00 +#define PORTSCX_LINE_STATUS_BIT_POS 10 + +/* bit 15-14 are port indicator control */ +#define PORTSCX_PIC_OFF 0x00000000 +#define PORTSCX_PIC_AMBER 0x00004000 +#define PORTSCX_PIC_GREEN 0x00008000 +#define PORTSCX_PIC_UNDEF 0x0000C000 +#define PORTSCX_PIC_BIT_POS 14 + +/* bit 19-16 are port test control */ +#define PORTSCX_PTC_DISABLE 0x00000000 +#define PORTSCX_PTC_JSTATE 0x00010000 +#define PORTSCX_PTC_KSTATE 0x00020000 +#define PORTSCX_PTC_SEQNAK 0x00030000 +#define PORTSCX_PTC_PACKET 0x00040000 +#define PORTSCX_PTC_FORCE_EN 0x00050000 +#define PORTSCX_PTC_BIT_POS 16 + +/* bit 27-26 are port speed */ +#define PORTSCX_PORT_SPEED_FULL 0x00000000 +#define PORTSCX_PORT_SPEED_LOW 0x04000000 +#define PORTSCX_PORT_SPEED_HIGH 0x08000000 +#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000 +#define PORTSCX_SPEED_BIT_POS 26 + +/* bit 28 is parallel transceiver width for UTMI interface */ +#define PORTSCX_PTW 0x10000000 +#define PORTSCX_PTW_8BIT 0x00000000 +#define PORTSCX_PTW_16BIT 0x10000000 + +/* bit 31-30 are port transceiver select */ +#define PORTSCX_PTS_UTMI 0x00000000 +#define PORTSCX_PTS_ULPI 0x80000000 +#define PORTSCX_PTS_FSLS 0xC0000000 +#define PORTSCX_PTS_BIT_POS 30 + +/* otgsc Register Bit Masks */ +#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 +#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 +#define OTGSC_CTRL_OTG_TERM 0x00000008 +#define OTGSC_CTRL_DATA_PULSING 0x00000010 +#define OTGSC_STS_USB_ID 0x00000100 +#define OTGSC_STS_A_VBUS_VALID 0x00000200 +#define OTGSC_STS_A_SESSION_VALID 0x00000400 +#define OTGSC_STS_B_SESSION_VALID 0x00000800 +#define OTGSC_STS_B_SESSION_END 0x00001000 +#define OTGSC_STS_1MS_TOGGLE 0x00002000 +#define OTGSC_STS_DATA_PULSING 0x00004000 +#define OTGSC_INTSTS_USB_ID 0x00010000 +#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 +#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 +#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 +#define OTGSC_INTSTS_B_SESSION_END 0x00100000 +#define OTGSC_INTSTS_1MS 0x00200000 +#define OTGSC_INTSTS_DATA_PULSING 0x00400000 +#define OTGSC_INTR_USB_ID 0x01000000 +#define OTGSC_INTR_A_VBUS_VALID 0x02000000 +#define OTGSC_INTR_A_SESSION_VALID 0x04000000 +#define OTGSC_INTR_B_SESSION_VALID 0x08000000 +#define OTGSC_INTR_B_SESSION_END 0x10000000 +#define OTGSC_INTR_1MS_TIMER 0x20000000 +#define OTGSC_INTR_DATA_PULSING 0x40000000 + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE 0x00000000 +#define USB_MODE_CTRL_MODE_DEVICE 0x00000002 +#define USB_MODE_CTRL_MODE_HOST 0x00000003 +#define USB_MODE_CTRL_MODE_RSV 0x00000001 +#define USB_MODE_SETUP_LOCK_OFF 0x00000008 +#define USB_MODE_STREAM_DISABLE 0x00000010 +/* Endpoint Flush Register */ +#define EPFLUSH_TX_OFFSET 0x00010000 +#define EPFLUSH_RX_OFFSET 0x00000000 + +/* Endpoint Setup Status bit masks */ +#define EP_SETUP_STATUS_MASK 0x0000003F +#define EP_SETUP_STATUS_EP0 0x00000001 + +/* ENDPOINTCTRLx Register Bit Masks */ +#define EPCTRL_TX_ENABLE 0x00800000 +#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */ +#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */ +#define EPCTRL_TX_TYPE 0x000C0000 +#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */ +#define EPCTRL_TX_EP_STALL 0x00010000 +#define EPCTRL_RX_ENABLE 0x00000080 +#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */ +#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */ +#define EPCTRL_RX_TYPE 0x0000000C +#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */ +#define EPCTRL_RX_EP_STALL 0x00000001 + +/* bit 19-18 and 3-2 are endpoint type */ +#define EPCTRL_EP_TYPE_CONTROL 0 +#define EPCTRL_EP_TYPE_ISO 1 +#define EPCTRL_EP_TYPE_BULK 2 +#define EPCTRL_EP_TYPE_INTERRUPT 3 +#define EPCTRL_TX_EP_TYPE_SHIFT 18 +#define EPCTRL_RX_EP_TYPE_SHIFT 2 + +/* SNOOPn Register Bit Masks */ +#define SNOOP_ADDRESS_MASK 0xFFFFF000 +#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */ +#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */ +#define SNOOP_SIZE_8KB 0x0C +#define SNOOP_SIZE_16KB 0x0D +#define SNOOP_SIZE_32KB 0x0E +#define SNOOP_SIZE_64KB 0x0F +#define SNOOP_SIZE_128KB 0x10 +#define SNOOP_SIZE_256KB 0x11 +#define SNOOP_SIZE_512KB 0x12 +#define SNOOP_SIZE_1MB 0x13 +#define SNOOP_SIZE_2MB 0x14 +#define SNOOP_SIZE_4MB 0x15 +#define SNOOP_SIZE_8MB 0x16 +#define SNOOP_SIZE_16MB 0x17 +#define SNOOP_SIZE_32MB 0x18 +#define SNOOP_SIZE_64MB 0x19 +#define SNOOP_SIZE_128MB 0x1A +#define SNOOP_SIZE_256MB 0x1B +#define SNOOP_SIZE_512MB 0x1C +#define SNOOP_SIZE_1GB 0x1D +#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */ + +/* pri_ctrl Register Bit Masks */ +#define PRI_CTRL_PRI_LVL1 0x0000000C +#define PRI_CTRL_PRI_LVL0 0x00000003 + +/* si_ctrl Register Bit Masks */ +#define SI_CTRL_ERR_DISABLE 0x00000010 +#define SI_CTRL_IDRC_DISABLE 0x00000008 +#define SI_CTRL_RD_SAFE_EN 0x00000004 +#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002 +#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001 + +/* control Register Bit Masks */ +#define USB_CTRL_IOENB 0x00000004 +#define USB_CTRL_ULPI_INT0EN 0x00000001 + +/* Endpoint Queue Head data struct + * Rem: all the variables of qh are LittleEndian Mode + * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr + */ +struct ep_queue_head { + u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len + and IOS(15) */ + u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ + u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ + u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */ + u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */ + u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */ + u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */ + u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */ + u32 res1; + u8 setup_buffer[8]; /* Setup data 8 bytes */ + u32 res2[4]; +}; + +/* Endpoint Queue Head Bit Masks */ +#define EP_QUEUE_HEAD_MULT_POS 30 +#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 +#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 +#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_IOS 0x00008000 +#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 +#define EP_QUEUE_HEAD_IOC 0x00008000 +#define EP_QUEUE_HEAD_MULTO 0x00000C00 +#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 +#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 +#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF +#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 +#define EP_QUEUE_FRINDEX_MASK 0x000007FF +#define EP_MAX_LENGTH_TRANSFER 0x4000 + +/* Endpoint Transfer Descriptor data struct */ +/* Rem: all the variables of td are LittleEndian Mode */ +struct ep_td_struct { + u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set + indicate invalid */ + u32 size_ioc_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 */ + u32 buff_ptr1; /* Buffer pointer Page 1 */ + u32 buff_ptr2; /* Buffer pointer Page 2 */ + u32 buff_ptr3; /* Buffer pointer Page 3 */ + u32 buff_ptr4; /* Buffer pointer Page 4 */ + u32 res; + /* 32 bytes */ + dma_addr_t td_dma; /* dma address for this td */ + /* virtual address of next td specified in next_td_ptr */ + struct ep_td_struct *next_td_virt; +}; + +/* Endpoint Transfer Descriptor bit Masks */ +#define DTD_NEXT_TERMINATE 0x00000001 +#define DTD_IOC 0x00008000 +#define DTD_STATUS_ACTIVE 0x00000080 +#define DTD_STATUS_HALTED 0x00000040 +#define DTD_STATUS_DATA_BUFF_ERR 0x00000020 +#define DTD_STATUS_TRANSACTION_ERR 0x00000008 +#define DTD_RESERVED_FIELDS 0x80007300 +#define DTD_ADDR_MASK 0xFFFFFFE0 +#define DTD_PACKET_SIZE 0x7FFF0000 +#define DTD_LENGTH_BIT_POS 16 +#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ + DTD_STATUS_DATA_BUFF_ERR | \ + DTD_STATUS_TRANSACTION_ERR) +/* Alignment requirements; must be a power of two */ +#define DTD_ALIGNMENT 0x20 +#define QH_ALIGNMENT 2048 + +/* Controller dma boundary */ +#define UDC_DMA_BOUNDARY 0x1000 + +/* -----------------------------------------------------------------------*/ +/* ##### enum data +*/ +typedef enum { + e_ULPI, + e_UTMI_8BIT, + e_UTMI_16BIT, + e_SERIAL +} e_PhyInterface; + +/*-------------------------------------------------------------------------*/ + +/* ### driver private data + */ +struct fsl_req { + struct usb_request req; + struct list_head queue; + /* ep_queue() func will add + a request->queue into a udc_ep->queue 'd tail */ + struct fsl_ep *ep; + unsigned mapped:1; + + struct ep_td_struct *head, *tail; /* For dTD List + cpu endian Virtual addr */ + unsigned int dtd_count; +}; + +#define REQ_UNCOMPLETE 1 + +struct fsl_ep { + struct usb_ep ep; + struct list_head queue; + struct fsl_udc *udc; + struct ep_queue_head *qh; + const struct usb_endpoint_descriptor *desc; + struct usb_gadget *gadget; + + char name[14]; + unsigned stopped:1; +}; + +#define EP_DIR_IN 1 +#define EP_DIR_OUT 0 + +struct fsl_udc { + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct fsl_ep *eps; + unsigned int max_ep; + unsigned int irq; + + struct usb_ctrlrequest local_setup_buff; + spinlock_t lock; + struct otg_transceiver *transceiver; + unsigned softconnect:1; + unsigned vbus_active:1; + unsigned stopped:1; + unsigned remote_wakeup:1; + + struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ + struct fsl_req *status_req; /* ep0 status request */ + struct dma_pool *td_pool; /* dma pool for DTD */ + enum fsl_usb2_phy_modes phy_mode; + + size_t ep_qh_size; /* size after alignment adjustment*/ + dma_addr_t ep_qh_dma; /* dma address of QH */ + + u32 max_pipes; /* Device max pipes */ + u32 max_use_endpts; /* Max endpointes to be used */ + u32 bus_reset; /* Device is bus reseting */ + u32 resume_state; /* USB state to resume */ + u32 usb_state; /* USB current state */ + u32 usb_next_state; /* USB next state */ + u32 ep0_state; /* Endpoint zero state */ + u32 ep0_dir; /* Endpoint zero direction: can be + USB_DIR_IN or USB_DIR_OUT */ + u32 usb_sof_count; /* SOF count */ + u32 errors; /* USB ERRORs count */ + u8 device_address; /* Device USB address */ + + struct completion *done; /* to make sure release() is done */ +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \ + __FUNCTION__, ## args) +#else +#define DBG(fmt, args...) do{}while(0) +#endif + +#if 0 +static void dump_msg(const char *label, const u8 * buf, unsigned int length) +{ + unsigned int start, num, i; + char line[52], *p; + + if (length >= 512) + return; + DBG("%s, length %u:\n", label, length); + start = 0; + while (length > 0) { + num = min(length, 16u); + p = line; + for (i = 0; i < num; ++i) { + if (i == 8) + *p++ = ' '; + sprintf(p, " %02x", buf[i]); + p += 3; + } + *p = 0; + printk(KERN_DEBUG "%6x: %s\n", start, line); + buf += num; + start += num; + length -= num; + } +} +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "udc: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) + +/*-------------------------------------------------------------------------*/ + +/* ### Add board specific defines here + */ + +/* + * ### pipe direction macro from device view + */ +#define USB_RECV 0 /* OUT EP */ +#define USB_SEND 1 /* IN EP */ + +/* + * ### internal used help routines. + */ +#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) +#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ + USB_DIR_IN ):((EP)->desc->bEndpointAddress \ + & USB_DIR_IN)==USB_DIR_IN) +#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \ + &udc->eps[pipe]) +#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \ + * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) +#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) + +#endif diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 2e3d662..d041b91 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -99,6 +99,12 @@ #else #define gadget_is_imx(g) 0 #endif +#ifdef CONFIG_USB_GADGET_FSL_USB2 +#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name) +#else +#define gadget_is_fsl_usb2(g) 0 +#endif + /* Mentor high speed function controller */ #ifdef CONFIG_USB_GADGET_MUSBHSFC #define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name) @@ -177,5 +183,7 @@ static inline int usb_gadget_controller_ return 0x17; else if (gadget_is_husb2dev(gadget)) return 0x18; + else if (gadget_is_fsl_usb2(gadget)) + return 0x19; return -ENOENT; } diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index f01890d..2c043a1 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -71,7 +71,7 @@ #include * by the host to interact with this device, and allocates endpoints to * the different protocol interfaces. The controller driver virtualizes * usb hardware so that the gadget drivers will be more portable. - * + * * This UDC hardware wants to implement a bit too much USB protocol, so * it constrains the sorts of USB configuration change events that work. * The errata for these chips are misleading; some "fixed" bugs from @@ -141,7 +141,7 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc #endif /* --------------------------------------------------------------------------- - * endpoint related parts of the api to the usb controller hardware, + * endpoint related parts of the api to the usb controller hardware, * used by gadget driver; and the inner talker-to-hardware core. * --------------------------------------------------------------------------- */ @@ -293,7 +293,7 @@ static int pxa2xx_ep_enable (struct usb_ #ifdef USE_DMA /* for (some) bulk and ISO endpoints, try to get a DMA channel and - * bind it to the endpoint. otherwise use PIO. + * bind it to the endpoint. otherwise use PIO. */ switch (ep->bmAttributes) { case USB_ENDPOINT_XFER_ISOC: @@ -304,7 +304,7 @@ #ifdef USE_DMA if (!use_dma || !ep->reg_drcmr) break; ep->dma = pxa_request_dma ((char *)_ep->name, - (le16_to_cpu (desc->wMaxPacketSize) > 64) + (le16_to_cpu (desc->wMaxPacketSize) > 64) ? DMA_PRIO_MEDIUM /* some iso */ : DMA_PRIO_LOW, dma_nodesc_handler, ep); @@ -361,7 +361,7 @@ #endif */ /* - * pxa2xx_ep_alloc_request - allocate a request data structure + * pxa2xx_ep_alloc_request - allocate a request data structure */ static struct usb_request * pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) @@ -378,7 +378,7 @@ pxa2xx_ep_alloc_request (struct usb_ep * /* - * pxa2xx_ep_free_request - deallocate a request data structure + * pxa2xx_ep_free_request - deallocate a request data structure */ static void pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) @@ -1031,7 +1031,7 @@ #endif /* - * nuke - dequeue ALL requests + * nuke - dequeue ALL requests */ static void nuke(struct pxa2xx_ep *ep, int status) { @@ -1136,16 +1136,16 @@ static int pxa2xx_ep_set_halt(struct usb ep->dev->req_pending = 0; ep->dev->ep0state = EP0_STALL; - /* and bulk/intr endpoints like dropping stalls too */ - } else { - unsigned i; - for (i = 0; i < 1000; i += 20) { - if (*ep->reg_udccs & UDCCS_BI_SST) - break; - udelay(20); - } - } - local_irq_restore(flags); + /* and bulk/intr endpoints like dropping stalls too */ + } else { + unsigned i; + for (i = 0; i < 1000; i += 20) { + if (*ep->reg_udccs & UDCCS_BI_SST) + break; + udelay(20); + } + } + local_irq_restore(flags); DBG(DBG_VERBOSE, "%s halt\n", _ep->name); return 0; @@ -1216,7 +1216,7 @@ static struct usb_ep_ops pxa2xx_ep_ops = /* --------------------------------------------------------------------------- - * device-scoped parts of the api to the usb controller hardware + * device-scoped parts of the api to the usb controller hardware * --------------------------------------------------------------------------- */ @@ -1239,7 +1239,7 @@ static void udc_enable (struct pxa2xx_ud static void udc_disable(struct pxa2xx_udc *); /* We disable the UDC -- and its 48 MHz clock -- whenever it's not - * in active use. + * in active use. */ static int pullup(struct pxa2xx_udc *udc, int is_active) { @@ -1464,24 +1464,10 @@ #define remove_proc_files() do {} while #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ -/* "function" sysfs attribute */ -static ssize_t -show_function (struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct pxa2xx_udc *dev = dev_get_drvdata (_dev); - - if (!dev->driver - || !dev->driver->function - || strlen (dev->driver->function) > PAGE_SIZE) - return 0; - return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); -} -static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); - /*-------------------------------------------------------------------------*/ /* - * udc_disable - disable USB device controller + * udc_disable - disable USB device controller */ static void udc_disable(struct pxa2xx_udc *dev) { @@ -1507,7 +1493,7 @@ #endif /* - * udc_reinit - initialize software state + * udc_reinit - initialize software state */ static void udc_reinit(struct pxa2xx_udc *dev) { @@ -1635,18 +1621,20 @@ int usb_gadget_register_driver(struct us dev->gadget.dev.driver = &driver->driver; dev->pullup = 1; - device_add (&dev->gadget.dev); + retval = device_add (&dev->gadget.dev); + if (retval) { +fail: + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return retval; + } retval = driver->bind(&dev->gadget); if (retval) { DMSG("bind to driver %s --> error %d\n", driver->driver.name, retval); device_del (&dev->gadget.dev); - - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; + goto fail; } - device_create_file(dev->dev, &dev_attr_function); /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. @@ -1704,7 +1692,6 @@ int usb_gadget_unregister_driver(struct dev->driver = NULL; device_del (&dev->gadget.dev); - device_remove_file(dev->dev, &dev_attr_function); DMSG("unregistered gadget driver '%s'\n", driver->driver.name); dump_state(dev); @@ -2474,12 +2461,12 @@ #define IXP425_B0 0x000001f1 #define IXP465_AD 0x00000200 /* - * probe - binds to the platform device + * probe - binds to the platform device */ static int __init pxa2xx_udc_probe(struct platform_device *pdev) { struct pxa2xx_udc *dev = &memory; - int retval, out_dma = 1, vbus_irq; + int retval, out_dma = 1, vbus_irq, irq; u32 chiprev; /* insist on Intel/ARM/XScale */ @@ -2522,7 +2509,11 @@ #endif return -ENODEV; } - pr_debug("%s: IRQ %d%s%s%s\n", driver_name, IRQ_USB, + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + + pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", out_dma ? "" : " (broken dma-out)", SIZE_STR DMASTR @@ -2570,11 +2561,11 @@ #endif dev->vbus = is_vbus_present(); /* irq setup after old hardware state is cleaned up */ - retval = request_irq(IRQ_USB, pxa2xx_udc_irq, + retval = request_irq(irq, pxa2xx_udc_irq, IRQF_DISABLED, driver_name, dev); if (retval != 0) { - printk(KERN_ERR "%s: can't get irq %i, err %d\n", - driver_name, IRQ_USB, retval); + printk(KERN_ERR "%s: can't get irq %d, err %d\n", + driver_name, irq, retval); return -EBUSY; } dev->got_irq = 1; @@ -2589,7 +2580,7 @@ #ifdef CONFIG_ARCH_LUBBOCK printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); lubbock_fail0: - free_irq(IRQ_USB, dev); + free_irq(irq, dev); return -EBUSY; } retval = request_irq(LUBBOCK_USB_IRQ, @@ -2616,7 +2607,7 @@ #endif if (retval != 0) { printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, vbus_irq, retval); - free_irq(IRQ_USB, dev); + free_irq(irq, dev); return -EBUSY; } } @@ -2641,7 +2632,7 @@ static int __exit pxa2xx_udc_remove(stru remove_proc_files(); if (dev->got_irq) { - free_irq(IRQ_USB, dev); + free_irq(platform_get_irq(pdev, 0), dev); dev->got_irq = 0; } #ifdef CONFIG_ARCH_LUBBOCK @@ -2668,7 +2659,7 @@ #ifdef CONFIG_PM * * For now, we punt and forcibly disconnect from the USB host when PXA * enters any suspend state. While we're disconnected, we always disable - * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. + * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. * Boards without software pullup control shouldn't use those states. * VBUS IRQs should probably be ignored so that the PXA device just acts * "dead" to USB hosts until system resume. @@ -2701,7 +2692,6 @@ #endif /*-------------------------------------------------------------------------*/ static struct platform_driver udc_driver = { - .probe = pxa2xx_udc_probe, .shutdown = pxa2xx_udc_shutdown, .remove = __exit_p(pxa2xx_udc_remove), .suspend = pxa2xx_udc_suspend, @@ -2715,7 +2705,7 @@ static struct platform_driver udc_driver static int __init udc_init(void) { printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); - return platform_driver_register(&udc_driver); + return platform_driver_probe(&udc_driver, pxa2xx_udc_probe); } module_init(udc_init); diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 4c3c725..397b149 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -195,7 +195,7 @@ struct rndis_packet_msg_type __le32 PerPacketInfoLength; __le32 VcHandle; __le32 Reserved; -}; +} __attribute__ ((packed)); struct rndis_config_parameter { diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index a2e58c8..2ff396b 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -15,4 +15,3 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o -obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index caac0d1..f28736a 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -31,7 +31,7 @@ #define FSL_SOC_USB_USBMODE 0x1a8 #define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ #define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ #define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ -#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */ -#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */ +#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ +#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ #define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ #endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1813b7c..f4d301b 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -136,6 +136,10 @@ static int ehci_bus_resume (struct usb_h /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); + /* Some controller/firmware combinations need a delay during which + * they set up the port statuses. See Bugzilla #8190. */ + mdelay(8); + /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); while (i--) { diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c deleted file mode 100644 index 32f7caf..0000000 --- a/drivers/usb/host/hc_crisv10.c +++ /dev/null @@ -1,4550 +0,0 @@ -/* - * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD) - * - * Copyright (c) 2002, 2003 Axis Communications AB. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -/* Ugly include because we don't live with the other host drivers. */ -#include <../drivers/usb/core/hcd.h> -#include <../drivers/usb/core/usb.h> - -#include "hc_crisv10.h" - -#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR -#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR -#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR - -static const char *usb_hcd_version = "$Revision: 1.2 $"; - -#undef KERN_DEBUG -#define KERN_DEBUG "" - - -#undef USB_DEBUG_RH -#undef USB_DEBUG_EPID -#undef USB_DEBUG_SB -#undef USB_DEBUG_DESC -#undef USB_DEBUG_URB -#undef USB_DEBUG_TRACE -#undef USB_DEBUG_BULK -#undef USB_DEBUG_CTRL -#undef USB_DEBUG_INTR -#undef USB_DEBUG_ISOC - -#ifdef USB_DEBUG_RH -#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg) -#else -#define dbg_rh(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_EPID -#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg) -#else -#define dbg_epid(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_SB -#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg) -#else -#define dbg_sb(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_CTRL -#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg) -#else -#define dbg_ctrl(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_BULK -#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg) -#else -#define dbg_bulk(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_INTR -#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg) -#else -#define dbg_intr(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_ISOC -#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg) -#else -#define dbg_isoc(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_TRACE -#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__)) -#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__)) -#else -#define DBFENTER do {} while (0) -#define DBFEXIT do {} while (0) -#endif - -#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) - -/*------------------------------------------------------------------- - Virtual Root Hub - -------------------------------------------------------------------*/ - -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, /* __le16 bcdUSB; v1.0 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __le16 idVendor; */ - 0x00, - 0x00, /* __le16 idProduct; */ - 0x00, - 0x00, /* __le16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __le16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x08, /* __le16 ep_wMaxPacketSize; 8 Bytes */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static __u8 root_hub_hub_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x29, /* __u8 bDescriptorType; Hub-descriptor */ - 0x02, /* __u8 bNbrPorts; */ - 0x00, /* __u16 wHubCharacteristics; */ - 0x00, - 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ - 0x00, /* __u8 bHubContrCurrent; 0 mA */ - 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ - 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ -}; - -static DEFINE_TIMER(bulk_start_timer, NULL, 0, 0); -static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0); - -/* We want the start timer to expire before the eot timer, because the former might start - traffic, thus making it unnecessary for the latter to time out. */ -#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */ -#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */ - -#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break -#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \ -{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);} - -#define SLAB_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) -#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) - -/* Most helpful debugging aid */ -#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) - -/* Alternative assert define which stops after a failed assert. */ -/* -#define assert(expr) \ -{ \ - if (!(expr)) { \ - err("assert failed at line %d",__LINE__); \ - while (1); \ - } \ -} -*/ - - -/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically? - To adjust it dynamically we would have to get an interrupt when we reach the end - of the rx descriptor list, or when we get close to the end, and then allocate more - descriptors. */ - -#define NBR_OF_RX_DESC 512 -#define RX_DESC_BUF_SIZE 1024 -#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE) - -/* The number of epids is, among other things, used for pre-allocating - ctrl, bulk and isoc EP descriptors (one for each epid). - Assumed to be > 1 when initiating the DMA lists. */ -#define NBR_OF_EPIDS 32 - -/* Support interrupt traffic intervals up to 128 ms. */ -#define MAX_INTR_INTERVAL 128 - -/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table - must be "invalid". By this we mean that we shouldn't care about epid attentions - for this epid, or at least handle them differently from epid attentions for "valid" - epids. This define determines which one to use (don't change it). */ -#define INVALID_EPID 31 -/* A special epid for the bulk dummys. */ -#define DUMMY_EPID 30 - -/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */ -static __u32 epid_usage_bitmask; - -/* A bitfield to keep information on in/out traffic is needed to uniquely identify - an endpoint on a device, since the most significant bit which indicates traffic - direction is lacking in the ep_id field (ETRAX epids can handle both in and - out traffic on endpoints that are otherwise identical). The USB framework, however, - relies on them to be handled separately. For example, bulk IN and OUT urbs cannot - be queued in the same list, since they would block each other. */ -static __u32 epid_out_traffic; - -/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line. - Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */ -static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32))); -static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4))); - -/* Pointers into RxDescList. */ -static volatile USB_IN_Desc_t *myNextRxDesc; -static volatile USB_IN_Desc_t *myLastRxDesc; -static volatile USB_IN_Desc_t *myPrevRxDesc; - -/* EP descriptors must be 32-bit aligned. */ -static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); -static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); -/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set, - causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which - gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the - EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors - in each frame. */ -static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4))); - -static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); -static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4))); - -static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4))); -static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4))); - -/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting - this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0 - results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point - it to this buffer. */ -static int zout_buffer[4] __attribute__ ((aligned (4))); - -/* Cache for allocating new EP and SB descriptors. */ -static struct kmem_cache *usb_desc_cache; - -/* Cache for the registers allocated in the top half. */ -static struct kmem_cache *top_half_reg_cache; - -/* Cache for the data allocated in the isoc descr top half. */ -static struct kmem_cache *isoc_compl_cache; - -static struct usb_bus *etrax_usb_bus; - -/* This is a circular (double-linked) list of the active urbs for each epid. - The head is never removed, and new urbs are linked onto the list as - urb_entry_t elements. Don't reference urb_list directly; use the wrapper - functions instead. Note that working with these lists might require spinlock - protection. */ -static struct list_head urb_list[NBR_OF_EPIDS]; - -/* Read about the need and usage of this lock in submit_ctrl_urb. */ -static spinlock_t urb_list_lock; - -/* Used when unlinking asynchronously. */ -static struct list_head urb_unlink_list; - -/* for returning string descriptors in UTF-16LE */ -static int ascii2utf (char *ascii, __u8 *utf, int utfmax) -{ - int retval; - - for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *ascii++ & 0x7f; - *utf++ = 0; - } - return retval; -} - -static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) -{ - char buf [30]; - - // assert (len > (2 * (sizeof (buf) + 1))); - // assert (strlen (type) <= 8); - - // language ids - if (id == 0) { - *data++ = 4; *data++ = 3; /* 4 bytes data */ - *data++ = 0; *data++ = 0; /* some language id */ - return 4; - - // serial number - } else if (id == 1) { - sprintf (buf, "%x", serial); - - // product description - } else if (id == 2) { - sprintf (buf, "USB %s Root Hub", type); - - // id 3 == vendor description - - // unsupported IDs --> "stall" - } else - return 0; - - data [0] = 2 + ascii2utf (buf, data + 2, len - 2); - data [1] = 3; - return data [0]; -} - -/* Wrappers around the list functions (include/linux/list.h). */ - -static inline int urb_list_empty(int epid) -{ - return list_empty(&urb_list[epid]); -} - -/* Returns first urb for this epid, or NULL if list is empty. */ -static inline struct urb *urb_list_first(int epid) -{ - struct urb *first_urb = 0; - - if (!urb_list_empty(epid)) { - /* Get the first urb (i.e. head->next). */ - urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list); - first_urb = urb_entry->urb; - } - return first_urb; -} - -/* Adds an urb_entry last in the list for this epid. */ -static inline void urb_list_add(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG); - assert(urb_entry); - - urb_entry->urb = urb; - list_add_tail(&urb_entry->list, &urb_list[epid]); -} - -/* Search through the list for an element that contains this urb. (The list - is expected to be short and the one we are about to delete will often be - the first in the list.) */ -static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid) -{ - struct list_head *entry; - struct list_head *tmp; - urb_entry_t *urb_entry; - - list_for_each_safe(entry, tmp, &urb_list[epid]) { - urb_entry = list_entry(entry, urb_entry_t, list); - assert(urb_entry); - assert(urb_entry->urb); - - if (urb_entry->urb == urb) { - return urb_entry; - } - } - return 0; -} - -/* Delete an urb from the list. */ -static inline void urb_list_del(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = __urb_list_entry(urb, epid); - assert(urb_entry); - - /* Delete entry and free. */ - list_del(&urb_entry->list); - kfree(urb_entry); -} - -/* Move an urb to the end of the list. */ -static inline void urb_list_move_last(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = __urb_list_entry(urb, epid); - assert(urb_entry); - - list_move_tail(&urb_entry->list, &urb_list[epid]); -} - -/* Get the next urb in the list. */ -static inline struct urb *urb_list_next(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = __urb_list_entry(urb, epid); - - assert(urb_entry); - - if (urb_entry->list.next != &urb_list[epid]) { - struct list_head *elem = urb_entry->list.next; - urb_entry = list_entry(elem, urb_entry_t, list); - return urb_entry->urb; - } else { - return NULL; - } -} - - - -/* For debug purposes only. */ -static inline void urb_list_dump(int epid) -{ - struct list_head *entry; - struct list_head *tmp; - urb_entry_t *urb_entry; - int i = 0; - - info("Dumping urb list for epid %d", epid); - - list_for_each_safe(entry, tmp, &urb_list[epid]) { - urb_entry = list_entry(entry, urb_entry_t, list); - info(" entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb); - } -} - -static void init_rx_buffers(void); -static int etrax_rh_unlink_urb(struct urb *urb); -static void etrax_rh_send_irq(struct urb *urb); -static void etrax_rh_init_int_timer(struct urb *urb); -static void etrax_rh_int_timer_do(unsigned long ptr); - -static int etrax_usb_setup_epid(struct urb *urb); -static int etrax_usb_lookup_epid(struct urb *urb); -static int etrax_usb_allocate_epid(void); -static void etrax_usb_free_epid(int epid); - -static int etrax_remove_from_sb_list(struct urb *urb); - -static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, - unsigned mem_flags, dma_addr_t *dma); -static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma); - -static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid); -static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid); -static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid); -static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid); - -static int etrax_usb_submit_bulk_urb(struct urb *urb); -static int etrax_usb_submit_ctrl_urb(struct urb *urb); -static int etrax_usb_submit_intr_urb(struct urb *urb); -static int etrax_usb_submit_isoc_urb(struct urb *urb); - -static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags); -static int etrax_usb_unlink_urb(struct urb *urb, int status); -static int etrax_usb_get_frame_number(struct usb_device *usb_dev); - -static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc); -static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc); -static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc); -static void etrax_usb_hc_interrupt_bottom_half(void *data); - -static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data); - - -/* The following is a list of interrupt handlers for the host controller interrupts we use. - They are called from etrax_usb_hc_interrupt_bottom_half. */ -static void etrax_usb_hc_isoc_eof_interrupt(void); -static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced); -static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg); -static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg); -static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg); - -static int etrax_rh_submit_urb (struct urb *urb); - -/* Forward declaration needed because they are used in the rx interrupt routine. */ -static void etrax_usb_complete_urb(struct urb *urb, int status); -static void etrax_usb_complete_bulk_urb(struct urb *urb, int status); -static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status); -static void etrax_usb_complete_intr_urb(struct urb *urb, int status); -static void etrax_usb_complete_isoc_urb(struct urb *urb, int status); - -static int etrax_usb_hc_init(void); -static void etrax_usb_hc_cleanup(void); - -static struct usb_operations etrax_usb_device_operations = -{ - .get_frame_number = etrax_usb_get_frame_number, - .submit_urb = etrax_usb_submit_urb, - .unlink_urb = etrax_usb_unlink_urb, - .buffer_alloc = etrax_usb_buffer_alloc, - .buffer_free = etrax_usb_buffer_free -}; - -/* Note that these functions are always available in their "__" variants, for use in - error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/ - USB_DEBUG_URB macros. */ -static void __dump_urb(struct urb* purb) -{ - printk("\nurb :0x%08lx\n", (unsigned long)purb); - printk("dev :0x%08lx\n", (unsigned long)purb->dev); - printk("pipe :0x%08x\n", purb->pipe); - printk("status :%d\n", purb->status); - printk("transfer_flags :0x%08x\n", purb->transfer_flags); - printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer); - printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length); - printk("actual_length :%d\n", purb->actual_length); - printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet); - printk("start_frame :%d\n", purb->start_frame); - printk("number_of_packets :%d\n", purb->number_of_packets); - printk("interval :%d\n", purb->interval); - printk("error_count :%d\n", purb->error_count); - printk("context :0x%08lx\n", (unsigned long)purb->context); - printk("complete :0x%08lx\n\n", (unsigned long)purb->complete); -} - -static void __dump_in_desc(volatile USB_IN_Desc_t *in) -{ - printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in); - printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len); - printk(" command : 0x%04x\n", in->command); - printk(" next : 0x%08lx\n", in->next); - printk(" buf : 0x%08lx\n", in->buf); - printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len); - printk(" status : 0x%04x\n\n", in->status); -} - -static void __dump_sb_desc(volatile USB_SB_Desc_t *sb) -{ - char tt = (sb->command & 0x30) >> 4; - char *tt_string; - - switch (tt) { - case 0: - tt_string = "zout"; - break; - case 1: - tt_string = "in"; - break; - case 2: - tt_string = "out"; - break; - case 3: - tt_string = "setup"; - break; - default: - tt_string = "unknown (weird)"; - } - - printk("\n USB_SB_Desc at 0x%08lx\n", (unsigned long)sb); - printk(" command : 0x%04x\n", sb->command); - printk(" rem : %d\n", (sb->command & 0x3f00) >> 8); - printk(" full : %d\n", (sb->command & 0x40) >> 6); - printk(" tt : %d (%s)\n", tt, tt_string); - printk(" intr : %d\n", (sb->command & 0x8) >> 3); - printk(" eot : %d\n", (sb->command & 0x2) >> 1); - printk(" eol : %d\n", sb->command & 0x1); - printk(" sw_len : 0x%04x (%d)\n", sb->sw_len, sb->sw_len); - printk(" next : 0x%08lx\n", sb->next); - printk(" buf : 0x%08lx\n\n", sb->buf); -} - - -static void __dump_ep_desc(volatile USB_EP_Desc_t *ep) -{ - printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep); - printk(" command : 0x%04x\n", ep->command); - printk(" ep_id : %d\n", (ep->command & 0x1f00) >> 8); - printk(" enable : %d\n", (ep->command & 0x10) >> 4); - printk(" intr : %d\n", (ep->command & 0x8) >> 3); - printk(" eof : %d\n", (ep->command & 0x2) >> 1); - printk(" eol : %d\n", ep->command & 0x1); - printk(" hw_len : 0x%04x (%d)\n", ep->hw_len, ep->hw_len); - printk(" next : 0x%08lx\n", ep->next); - printk(" sub : 0x%08lx\n\n", ep->sub); -} - -static inline void __dump_ep_list(int pipe_type) -{ - volatile USB_EP_Desc_t *ep; - volatile USB_EP_Desc_t *first_ep; - volatile USB_SB_Desc_t *sb; - - switch (pipe_type) - { - case PIPE_BULK: - first_ep = &TxBulkEPList[0]; - break; - case PIPE_CONTROL: - first_ep = &TxCtrlEPList[0]; - break; - case PIPE_INTERRUPT: - first_ep = &TxIntrEPList[0]; - break; - case PIPE_ISOCHRONOUS: - first_ep = &TxIsocEPList[0]; - break; - default: - warn("Cannot dump unknown traffic type"); - return; - } - ep = first_ep; - - printk("\n\nDumping EP list...\n\n"); - - do { - __dump_ep_desc(ep); - /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */ - sb = ep->sub ? phys_to_virt(ep->sub) : 0; - while (sb) { - __dump_sb_desc(sb); - sb = sb->next ? phys_to_virt(sb->next) : 0; - } - ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next)); - - } while (ep != first_ep); -} - -static inline void __dump_ept_data(int epid) -{ - unsigned long flags; - __u32 r_usb_ept_data; - - if (epid < 0 || epid > 31) { - printk("Cannot dump ept data for invalid epid %d\n", epid); - return; - } - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid); - if (r_usb_ept_data == 0) { - /* No need for more detailed printing. */ - return; - } - printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31); - printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30); - printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28); - printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27); - printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26); - printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24); - printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22); - printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21); - printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19); - printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11); - printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7); - printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f)); -} - -static inline void __dump_ept_data_list(void) -{ - int i; - - printk("Dumping the whole R_USB_EPT_DATA list\n"); - - for (i = 0; i < 32; i++) { - __dump_ept_data(i); - } -} -#ifdef USB_DEBUG_DESC -#define dump_in_desc(...) __dump_in_desc(...) -#define dump_sb_desc(...) __dump_sb_desc(...) -#define dump_ep_desc(...) __dump_ep_desc(...) -#else -#define dump_in_desc(...) do {} while (0) -#define dump_sb_desc(...) do {} while (0) -#define dump_ep_desc(...) do {} while (0) -#endif - -#ifdef USB_DEBUG_URB -#define dump_urb(x) __dump_urb(x) -#else -#define dump_urb(x) do {} while (0) -#endif - -static void init_rx_buffers(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) { - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].command = 0; - RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); - RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); - RxDescList[i].hw_len = 0; - RxDescList[i].status = 0; - - /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc - for the relevant fields.) */ - prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]); - - } - - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes); - RxDescList[i].next = virt_to_phys(&RxDescList[0]); - RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); - RxDescList[i].hw_len = 0; - RxDescList[i].status = 0; - - myNextRxDesc = &RxDescList[0]; - myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - - *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc); - *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start); - - DBFEXIT; -} - -static void init_tx_bulk_ep(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { - CHECK_ALIGN(&TxBulkEPList[i]); - TxBulkEPList[i].hw_len = 0; - TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxBulkEPList[i].sub = 0; - TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]); - - /* Initiate two EPs, disabled and with the eol flag set. No need for any - preserved epid. */ - - /* The first one has the intr flag set so we get an interrupt when the DMA - channel is about to become disabled. */ - CHECK_ALIGN(&TxBulkDummyEPList[i][0]); - TxBulkDummyEPList[i][0].hw_len = 0; - TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | - IO_STATE(USB_EP_command, eol, yes) | - IO_STATE(USB_EP_command, intr, yes)); - TxBulkDummyEPList[i][0].sub = 0; - TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]); - - /* The second one. */ - CHECK_ALIGN(&TxBulkDummyEPList[i][1]); - TxBulkDummyEPList[i][1].hw_len = 0; - TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | - IO_STATE(USB_EP_command, eol, yes)); - TxBulkDummyEPList[i][1].sub = 0; - /* The last dummy's next pointer is the same as the current EP's next pointer. */ - TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]); - } - - /* Configure the last one. */ - CHECK_ALIGN(&TxBulkEPList[i]); - TxBulkEPList[i].hw_len = 0; - TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, i)); - TxBulkEPList[i].sub = 0; - TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]); - - /* No need configuring dummy EPs for the last one as it will never be used for - bulk traffic (i == INVALD_EPID at this point). */ - - /* Set up to start on the last EP so we will enable it when inserting traffic - for the first time (imitating the situation where the DMA has stopped - because there was no more traffic). */ - *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]); - /* No point in starting the bulk channel yet. - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */ - DBFEXIT; -} - -static void init_tx_ctrl_ep(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { - CHECK_ALIGN(&TxCtrlEPList[i]); - TxCtrlEPList[i].hw_len = 0; - TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxCtrlEPList[i].sub = 0; - TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]); - } - - CHECK_ALIGN(&TxCtrlEPList[i]); - TxCtrlEPList[i].hw_len = 0; - TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, i)); - - TxCtrlEPList[i].sub = 0; - TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]); - - *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]); - *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); - - DBFEXIT; -} - - -static void init_tx_intr_ep(void) -{ - int i; - - DBFENTER; - - /* Read comment at zout_buffer declaration for an explanation to this. */ - TxIntrSB_zout.sw_len = 1; - TxIntrSB_zout.next = 0; - TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]); - TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) { - CHECK_ALIGN(&TxIntrEPList[i]); - TxIntrEPList[i].hw_len = 0; - TxIntrEPList[i].command = - (IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, enable, yes) | - IO_FIELD(USB_EP_command, epid, INVALID_EPID)); - TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]); - } - - CHECK_ALIGN(&TxIntrEPList[i]); - TxIntrEPList[i].hw_len = 0; - TxIntrEPList[i].command = - (IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, eol, yes) | - IO_STATE(USB_EP_command, enable, yes) | - IO_FIELD(USB_EP_command, epid, INVALID_EPID)); - TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]); - - *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]); - *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); - DBFEXIT; -} - -static void init_tx_isoc_ep(void) -{ - int i; - - DBFENTER; - - /* Read comment at zout_buffer declaration for an explanation to this. */ - TxIsocSB_zout.sw_len = 1; - TxIsocSB_zout.next = 0; - TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]); - TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - /* The last isochronous EP descriptor is a dummy. */ - - for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { - CHECK_ALIGN(&TxIsocEPList[i]); - TxIsocEPList[i].hw_len = 0; - TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxIsocEPList[i].sub = 0; - TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]); - } - - CHECK_ALIGN(&TxIsocEPList[i]); - TxIsocEPList[i].hw_len = 0; - - /* Must enable the last EP descr to get eof interrupt. */ - TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) | - IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, INVALID_EPID)); - TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout); - TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]); - - *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]); - *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); - - DBFEXIT; -} - -static void etrax_usb_unlink_intr_urb(struct urb *urb) -{ - volatile USB_EP_Desc_t *first_ep; /* First EP in the list. */ - volatile USB_EP_Desc_t *curr_ep; /* Current EP, the iterator. */ - volatile USB_EP_Desc_t *next_ep; /* The EP after current. */ - volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */ - - int epid; - - /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */ - - DBFENTER; - - epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid; - - first_ep = &TxIntrEPList[0]; - curr_ep = first_ep; - - - /* Note that this loop removes all EP descriptors with this epid. This assumes - that all EP descriptors belong to the one and only urb for this epid. */ - - do { - next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next); - - if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) { - - dbg_intr("Found EP to unlink for epid %d", epid); - - /* This is the one we should unlink. */ - unlink_ep = next_ep; - - /* Actually unlink the EP from the DMA list. */ - curr_ep->next = unlink_ep->next; - - /* Wait until the DMA is no longer at this descriptor. */ - while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep)); - - /* Now we are free to remove it and its SB descriptor. - Note that it is assumed here that there is only one sb in the - sb list for this ep. */ - kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub)); - kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep); - } - - curr_ep = phys_to_virt(curr_ep->next); - - } while (curr_ep != first_ep); - urb->hcpriv = NULL; -} - -void etrax_usb_do_intr_recover(int epid) -{ - USB_EP_Desc_t *first_ep, *tmp_ep; - - DBFENTER; - - first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP); - tmp_ep = first_ep; - - /* What this does is simply to walk the list of interrupt - ep descriptors and enable those that are disabled. */ - - do { - if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid && - !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) { - tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes); - } - - tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next); - - } while (tmp_ep != first_ep); - - - DBFEXIT; -} - -static int etrax_rh_unlink_urb (struct urb *urb) -{ - etrax_hc_t *hc; - - DBFENTER; - - hc = urb->dev->bus->hcpriv; - - if (hc->rh.urb == urb) { - hc->rh.send = 0; - del_timer(&hc->rh.rh_int_timer); - } - - DBFEXIT; - return 0; -} - -static void etrax_rh_send_irq(struct urb *urb) -{ - __u16 data = 0; - etrax_hc_t *hc = urb->dev->bus->hcpriv; - DBFENTER; - -/* - dbg_rh("R_USB_FM_NUMBER : 0x%08X", *R_USB_FM_NUMBER); - dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING); -*/ - - data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0; - data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0; - - *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data); - /* FIXME: Why is actual_length set to 1 when data is 2 bytes? - Since only 1 byte is used, why not declare data as __u8? */ - urb->actual_length = 1; - urb->status = 0; - - if (hc->rh.send && urb->complete) { - dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1); - dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2); - - urb->complete(urb, NULL); - } - - DBFEXIT; -} - -static void etrax_rh_init_int_timer(struct urb *urb) -{ - etrax_hc_t *hc; - - DBFENTER; - - hc = urb->dev->bus->hcpriv; - hc->rh.interval = urb->interval; - init_timer(&hc->rh.rh_int_timer); - hc->rh.rh_int_timer.function = etrax_rh_int_timer_do; - hc->rh.rh_int_timer.data = (unsigned long)urb; - /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped - to 0, and the rest to the nearest lower 10 ms. */ - hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000); - add_timer(&hc->rh.rh_int_timer); - - DBFEXIT; -} - -static void etrax_rh_int_timer_do(unsigned long ptr) -{ - struct urb *urb; - etrax_hc_t *hc; - - DBFENTER; - - urb = (struct urb*)ptr; - hc = urb->dev->bus->hcpriv; - - if (hc->rh.send) { - etrax_rh_send_irq(urb); - } - - DBFEXIT; -} - -static int etrax_usb_setup_epid(struct urb *urb) -{ - int epid; - char devnum, endpoint, out_traffic, slow; - int maxlen; - unsigned long flags; - - DBFENTER; - - epid = etrax_usb_lookup_epid(urb); - if ((epid != -1)){ - /* An epid that fits this urb has been found. */ - DBFEXIT; - return epid; - } - - /* We must find and initiate a new epid for this urb. */ - epid = etrax_usb_allocate_epid(); - - if (epid == -1) { - /* Failed to allocate a new epid. */ - DBFEXIT; - return epid; - } - - /* We now have a new epid to use. Initiate it. */ - set_bit(epid, (void *)&epid_usage_bitmask); - - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - slow = usb_pipeslow(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - /* We want both IN and OUT control traffic to be put on the same EP/SB list. */ - out_traffic = 1; - } else { - out_traffic = usb_pipeout(urb->pipe); - } - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) | - /* FIXME: Change any to the actual port? */ - IO_STATE(R_USB_EPT_DATA_ISO, port, any) | - IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) | - IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) | - IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum); - } else { - *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) | - IO_FIELD(R_USB_EPT_DATA, low_speed, slow) | - /* FIXME: Change any to the actual port? */ - IO_STATE(R_USB_EPT_DATA, port, any) | - IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) | - IO_FIELD(R_USB_EPT_DATA, ep, endpoint) | - IO_FIELD(R_USB_EPT_DATA, dev, devnum); - } - - restore_flags(flags); - - if (out_traffic) { - set_bit(epid, (void *)&epid_out_traffic); - } else { - clear_bit(epid, (void *)&epid_out_traffic); - } - - dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)", - epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN"); - - DBFEXIT; - return epid; -} - -static void etrax_usb_free_epid(int epid) -{ - unsigned long flags; - - DBFENTER; - - if (!test_bit(epid, (void *)&epid_usage_bitmask)) { - warn("Trying to free unused epid %d", epid); - DBFEXIT; - return; - } - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)); - /* This will, among other things, set the valid field to 0. */ - *R_USB_EPT_DATA = 0; - restore_flags(flags); - - clear_bit(epid, (void *)&epid_usage_bitmask); - - - dbg_epid("Freed epid %d", epid); - - DBFEXIT; -} - -static int etrax_usb_lookup_epid(struct urb *urb) -{ - int i; - __u32 data; - char devnum, endpoint, slow, out_traffic; - int maxlen; - unsigned long flags; - - DBFENTER; - - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - slow = usb_pipeslow(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - /* We want both IN and OUT control traffic to be put on the same EP/SB list. */ - out_traffic = 1; - } else { - out_traffic = usb_pipeout(urb->pipe); - } - - /* Step through att epids. */ - for (i = 0; i < NBR_OF_EPIDS; i++) { - if (test_bit(i, (void *)&epid_usage_bitmask) && - test_bit(i, (void *)&epid_out_traffic) == out_traffic) { - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i); - nop(); - - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - data = *R_USB_EPT_DATA_ISO; - restore_flags(flags); - - if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) { - dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", - i, devnum, endpoint, out_traffic ? "OUT" : "IN"); - DBFEXIT; - return i; - } - } else { - data = *R_USB_EPT_DATA; - restore_flags(flags); - - if ((IO_MASK(R_USB_EPT_DATA, valid) & data) && - (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) && - (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) && - (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) && - (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) { - dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", - i, devnum, endpoint, out_traffic ? "OUT" : "IN"); - DBFEXIT; - return i; - } - } - } - } - - DBFEXIT; - return -1; -} - -static int etrax_usb_allocate_epid(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < NBR_OF_EPIDS; i++) { - if (!test_bit(i, (void *)&epid_usage_bitmask)) { - dbg_epid("Found free epid %d", i); - DBFEXIT; - return i; - } - } - - dbg_epid("Found no free epids"); - DBFEXIT; - return -1; -} - -static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags) -{ - etrax_hc_t *hc; - int ret = -EINVAL; - - DBFENTER; - - if (!urb->dev || !urb->dev->bus) { - return -ENODEV; - } - if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) { - info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe); - return -EMSGSIZE; - } - - if (urb->timeout) { - /* FIXME. */ - warn("urb->timeout specified, ignoring."); - } - - hc = (etrax_hc_t*)urb->dev->bus->hcpriv; - - if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { - /* This request is for the Virtual Root Hub. */ - ret = etrax_rh_submit_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { - - ret = etrax_usb_submit_bulk_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - - ret = etrax_usb_submit_ctrl_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - int bustime; - - if (urb->bandwidth == 0) { - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - ret = bustime; - } else { - ret = etrax_usb_submit_intr_urb(urb); - if (ret == 0) - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - } - } else { - /* Bandwidth already set. */ - ret = etrax_usb_submit_intr_urb(urb); - } - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - int bustime; - - if (urb->bandwidth == 0) { - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - ret = bustime; - } else { - ret = etrax_usb_submit_isoc_urb(urb); - if (ret == 0) - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - } - } else { - /* Bandwidth already set. */ - ret = etrax_usb_submit_isoc_urb(urb); - } - } - - DBFEXIT; - - if (ret != 0) - printk("Submit URB error %d\n", ret); - - return ret; -} - -static int etrax_usb_unlink_urb(struct urb *urb, int status) -{ - etrax_hc_t *hc; - etrax_urb_priv_t *urb_priv; - int epid; - unsigned int flags; - - DBFENTER; - - if (!urb) { - return -EINVAL; - } - - /* Disable interrupts here since a descriptor interrupt for the isoc epid - will modify the sb list. This could possibly be done more granular, but - unlink_urb should not be used frequently anyway. - */ - - save_flags(flags); - cli(); - - if (!urb->dev || !urb->dev->bus) { - restore_flags(flags); - return -ENODEV; - } - if (!urb->hcpriv) { - /* This happens if a device driver calls unlink on an urb that - was never submitted (lazy driver) or if the urb was completed - while unlink was being called. */ - restore_flags(flags); - return 0; - } - if (urb->transfer_flags & URB_ASYNC_UNLINK) { - /* FIXME. */ - /* If URB_ASYNC_UNLINK is set: - unlink - move to a separate urb list - call complete at next sof with ECONNRESET - - If not: - wait 1 ms - unlink - call complete with ENOENT - */ - warn("URB_ASYNC_UNLINK set, ignoring."); - } - - /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking, - but that doesn't work for interrupt and isochronous traffic since they are completed - repeatedly, and urb->status is set then. That may in itself be a bug though. */ - - hc = urb->dev->bus->hcpriv; - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - epid = urb_priv->epid; - - /* Set the urb status (synchronous unlink). */ - urb->status = -ENOENT; - urb_priv->urb_state = UNLINK; - - if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { - int ret; - ret = etrax_rh_unlink_urb(urb); - DBFEXIT; - restore_flags(flags); - return ret; - - } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { - - dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb); - - if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid])); - } - /* Kicking dummy list out of the party. */ - TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); - - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - - dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb); - - if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid])); - } - - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - - dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb); - - /* Separate function because it's a tad more complicated. */ - etrax_usb_unlink_intr_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - - dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb); - - if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])); - } - } - - /* Note that we need to remove the urb from the urb list *before* removing its SB - descriptors. (This means that the isoc eof handler might get a null urb when we - are unlinking the last urb.) */ - - if (usb_pipetype(urb->pipe) == PIPE_BULK) { - - urb_list_del(urb, epid); - TxBulkEPList[epid].sub = 0; - etrax_remove_from_sb_list(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - - urb_list_del(urb, epid); - TxCtrlEPList[epid].sub = 0; - etrax_remove_from_sb_list(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - - urb_list_del(urb, epid); - /* Sanity check (should never happen). */ - assert(urb_list_empty(epid)); - - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 0); - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - - if (usb_pipeout(urb->pipe)) { - - USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb; - - if (__urb_list_entry(urb, epid)) { - - urb_list_del(urb, epid); - iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0; - prev_sb = 0; - while (iter_sb && (iter_sb != urb_priv->first_sb)) { - prev_sb = iter_sb; - iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; - } - - if (iter_sb == 0) { - /* Unlink of the URB currently being transmitted. */ - prev_sb = 0; - iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0; - } - - while (iter_sb && (iter_sb != urb_priv->last_sb)) { - iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; - } - if (iter_sb) { - next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; - } else { - /* This should only happen if the DMA has completed - processing the SB list for this EP while interrupts - are disabled. */ - dbg_isoc("Isoc urb not found, already sent?"); - next_sb = 0; - } - if (prev_sb) { - prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0; - } else { - TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0; - } - - etrax_remove_from_sb_list(urb); - if (urb_list_empty(epid)) { - TxIsocEPList[epid].sub = 0; - dbg_isoc("Last isoc out urb epid %d", epid); - } else if (next_sb || prev_sb) { - dbg_isoc("Re-enable isoc out epid %d", epid); - - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - } else { - TxIsocEPList[epid].sub = 0; - dbg_isoc("URB list non-empty and no SB list, EP disabled"); - } - } else { - dbg_isoc("Urb 0x%p not found, completed already?", urb); - } - } else { - - urb_list_del(urb, epid); - - /* For in traffic there is only one SB descriptor for each EP even - though there may be several urbs (all urbs point at the same SB). */ - if (urb_list_empty(epid)) { - /* No more urbs, remove the SB. */ - TxIsocEPList[epid].sub = 0; - etrax_remove_from_sb_list(urb); - } else { - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - } - } - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 1); - } - /* Free the epid if urb list is empty. */ - if (urb_list_empty(epid)) { - etrax_usb_free_epid(epid); - } - restore_flags(flags); - - /* Must be done before calling completion handler. */ - kfree(urb_priv); - urb->hcpriv = 0; - - if (urb->complete) { - urb->complete(urb, NULL); - } - - DBFEXIT; - return 0; -} - -static int etrax_usb_get_frame_number(struct usb_device *usb_dev) -{ - DBFENTER; - DBFEXIT; - return (*R_USB_FM_NUMBER & 0x7ff); -} - -static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc) -{ - DBFENTER; - - /* This interrupt handler could be used when unlinking EP descriptors. */ - - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) { - USB_EP_Desc_t *ep; - - //dbg_bulk("dma8_sub0_descr (BULK) intr."); - - /* It should be safe clearing the interrupt here, since we don't expect to get a new - one until we restart the bulk channel. */ - *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do); - - /* Wait while the DMA is running (though we don't expect it to be). */ - while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd)); - - /* Advance the DMA to the next EP descriptor. */ - ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP); - - //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep); - - /* ep->next is already a physical address; no need for a virt_to_phys. */ - *R_DMA_CH8_SUB0_EP = ep->next; - - /* Start the DMA bulk channel again. */ - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) { - struct urb *urb; - int epid; - etrax_urb_priv_t *urb_priv; - unsigned long int flags; - - dbg_ctrl("dma8_sub1_descr (CTRL) intr."); - *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do); - - /* The complete callback gets called so we cli. */ - save_flags(flags); - cli(); - - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - if ((TxCtrlEPList[epid].sub == 0) || - (epid == DUMMY_EPID) || - (epid == INVALID_EPID)) { - /* Nothing here to see. */ - continue; - } - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - - if (urb) { - - /* Sanity check. */ - assert(usb_pipetype(urb->pipe) == PIPE_CONTROL); - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) { - assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); - - etrax_usb_complete_urb(urb, 0); - } - } - } - restore_flags(flags); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) { - dbg_intr("dma8_sub2_descr (INTR) intr."); - *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) { - struct urb *urb; - int epid; - int epid_done; - etrax_urb_priv_t *urb_priv; - USB_SB_Desc_t *sb_desc; - - usb_isoc_complete_data_t *comp_data = NULL; - - /* One or more isoc out transfers are done. */ - dbg_isoc("dma8_sub3_descr (ISOC) intr."); - - /* For each isoc out EP search for the first sb_desc with the intr flag - set. This descriptor must be the last packet from an URB. Then - traverse the URB list for the EP until the URB with urb_priv->last_sb - matching the intr-marked sb_desc is found. All URBs before this have - been sent. - */ - - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - /* Skip past epids with no SB lists, epids used for in traffic, - and special (dummy, invalid) epids. */ - if ((TxIsocEPList[epid].sub == 0) || - (test_bit(epid, (void *)&epid_out_traffic) == 0) || - (epid == DUMMY_EPID) || - (epid == INVALID_EPID)) { - /* Nothing here to see. */ - continue; - } - sb_desc = phys_to_virt(TxIsocEPList[epid].sub); - - /* Find the last descriptor of the currently active URB for this ep. - This is the first descriptor in the sub list marked for a descriptor - interrupt. */ - while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) { - sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0; - } - assert(sb_desc); - - dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p", - epid, - phys_to_virt(TxIsocEPList[epid].sub), - sb_desc); - - epid_done = 0; - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - assert(urb); - - while (urb && !epid_done) { - - /* Sanity check. */ - assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); - - if (!usb_pipeout(urb->pipe)) { - /* descr interrupts are generated only for out pipes. */ - epid_done = 1; - continue; - } - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (sb_desc != urb_priv->last_sb) { - - /* This urb has been sent. */ - dbg_isoc("out URB 0x%p sent", urb); - - urb_priv->urb_state = TRANSFER_DONE; - - } else if ((sb_desc == urb_priv->last_sb) && - !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) { - - assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes)); - assert(sb_desc->next == 0); - - dbg_isoc("out URB 0x%p last in list, epid disabled", urb); - TxIsocEPList[epid].sub = 0; - TxIsocEPList[epid].hw_len = 0; - urb_priv->urb_state = TRANSFER_DONE; - - epid_done = 1; - - } else { - epid_done = 1; - } - if (!epid_done) { - urb = urb_list_next(urb, epid); - } - } - - } - - *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do); - - comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, GFP_ATOMIC); - assert(comp_data != NULL); - - INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data); - schedule_work(&comp_data->usb_bh); - } - - DBFEXIT; - return IRQ_HANDLED; -} - -static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data) -{ - usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data; - - struct urb *urb; - int epid; - int epid_done; - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - dbg_isoc("dma8_sub3_descr (ISOC) bottom half."); - - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - unsigned long flags; - - save_flags(flags); - cli(); - - epid_done = 0; - - /* The descriptor interrupt handler has marked all transmitted isoch. out - URBs with TRANSFER_DONE. Now we traverse all epids and for all that - have isoch. out traffic traverse its URB list and complete the - transmitted URB. - */ - - while (!epid_done) { - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - if (urb == 0) { - epid_done = 1; - continue; - } - - if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) { - epid_done = 1; - continue; - } - - if (!usb_pipeout(urb->pipe)) { - /* descr interrupts are generated only for out pipes. */ - epid_done = 1; - continue; - } - - dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub); - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (urb_priv->urb_state == TRANSFER_DONE) { - int i; - struct usb_iso_packet_descriptor *packet; - - /* This urb has been sent. */ - dbg_isoc("Completing isoc out URB 0x%p", urb); - - for (i = 0; i < urb->number_of_packets; i++) { - packet = &urb->iso_frame_desc[i]; - packet->status = 0; - packet->actual_length = packet->length; - } - - etrax_usb_complete_isoc_urb(urb, 0); - - if (urb_list_empty(epid)) { - etrax_usb_free_epid(epid); - epid_done = 1; - } - } else { - epid_done = 1; - } - } - restore_flags(flags); - - } - kmem_cache_free(isoc_compl_cache, comp_data); - - DBFEXIT; -} - - - -static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc) -{ - struct urb *urb; - etrax_urb_priv_t *urb_priv; - int epid = 0; - unsigned long flags; - - /* Isoc diagnostics. */ - static int curr_fm = 0; - static int prev_fm = 0; - - DBFENTER; - - /* Clear this interrupt. */ - *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); - - /* Note that this while loop assumes that all packets span only - one rx descriptor. */ - - /* The reason we cli here is that we call the driver's callback functions. */ - save_flags(flags); - cli(); - - while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) { - - epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status); - urb = urb_list_first(epid); - - //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb); - - if (!urb) { - err("No urb for epid %d in rx interrupt", epid); - __dump_ept_data(epid); - goto skip_out; - } - - /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since - ctrl pipes are not. */ - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) { - __u32 r_usb_ept_data; - int no_error = 0; - - assert(test_bit(epid, (void *)&epid_usage_bitmask)); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - r_usb_ept_data = *R_USB_EPT_DATA_ISO; - - if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) && - (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) { - /* Not an error, just a failure to receive an expected iso - in packet in this frame. This is not documented - in the designers reference. - */ - no_error++; - } else { - warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data); - } - } else { - r_usb_ept_data = *R_USB_EPT_DATA; - warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data); - } - - if (!no_error){ - warn("error in rx desc->status, epid %d, first urb = 0x%lx", - epid, (unsigned long)urb); - __dump_in_desc(myNextRxDesc); - - warn("R_USB_STATUS = 0x%x", *R_USB_STATUS); - - /* Check that ept was disabled when error occurred. */ - switch (usb_pipetype(urb->pipe)) { - case PIPE_BULK: - assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - case PIPE_CONTROL: - assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - case PIPE_INTERRUPT: - assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - case PIPE_ISOCHRONOUS: - assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - default: - warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p", - usb_pipetype(urb->pipe), - urb); - } - etrax_usb_complete_urb(urb, -EPROTO); - goto skip_out; - } - } - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if ((usb_pipetype(urb->pipe) == PIPE_BULK) || - (usb_pipetype(urb->pipe) == PIPE_CONTROL) || - (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { - /* We get nodata for empty data transactions, and the rx descriptor's - hw_len field is not valid in that case. No data to copy in other - words. */ - } else { - /* Make sure the data fits in the buffer. */ - assert(urb_priv->rx_offset + myNextRxDesc->hw_len - <= urb->transfer_buffer_length); - - memcpy(urb->transfer_buffer + urb_priv->rx_offset, - phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len); - urb_priv->rx_offset += myNextRxDesc->hw_len; - } - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) { - if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) && - ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) == - IO_STATE(USB_EP_command, enable, yes))) { - /* The EP is still enabled, so the OUT packet used to ack - the in data is probably not processed yet. If the EP - sub pointer has not moved beyond urb_priv->last_sb mark - it for a descriptor interrupt and complete the urb in - the descriptor interrupt handler. - */ - USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0; - - while ((sub != NULL) && (sub != urb_priv->last_sb)) { - sub = sub->next ? phys_to_virt(sub->next) : 0; - } - if (sub != NULL) { - /* The urb has not been fully processed. */ - urb_priv->urb_state = WAITING_FOR_DESCR_INTR; - } else { - warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub); - etrax_usb_complete_urb(urb, 0); - } - } else { - etrax_usb_complete_urb(urb, 0); - } - } - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - - struct usb_iso_packet_descriptor *packet; - - if (urb_priv->urb_state == UNLINK) { - info("Ignoring rx data for urb being unlinked."); - goto skip_out; - } else if (urb_priv->urb_state == NOT_STARTED) { - info("What? Got rx data for urb that isn't started?"); - goto skip_out; - } - - packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter]; - packet->status = 0; - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { - /* We get nodata for empty data transactions, and the rx descriptor's - hw_len field is not valid in that case. We copy 0 bytes however to - stay in synch. */ - packet->actual_length = 0; - } else { - packet->actual_length = myNextRxDesc->hw_len; - /* Make sure the data fits in the buffer. */ - assert(packet->actual_length <= packet->length); - memcpy(urb->transfer_buffer + packet->offset, - phys_to_virt(myNextRxDesc->buf), packet->actual_length); - } - - /* Increment the packet counter. */ - urb_priv->isoc_packet_counter++; - - /* Note that we don't care about the eot field in the rx descriptor's status. - It will always be set for isoc traffic. */ - if (urb->number_of_packets == urb_priv->isoc_packet_counter) { - - /* Out-of-synch diagnostics. */ - curr_fm = (*R_USB_FM_NUMBER & 0x7ff); - if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) { - /* This test is wrong, if there is more than one isoc - in endpoint active it will always calculate wrong - since prev_fm is shared by all endpoints. - - FIXME Make this check per URB using urb->start_frame. - */ - dbg_isoc("Out of synch? Previous frame = %d, current frame = %d", - prev_fm, curr_fm); - - } - prev_fm = curr_fm; - - /* Complete the urb with status OK. */ - etrax_usb_complete_isoc_urb(urb, 0); - } - } - - skip_out: - - /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr - has the same layout as USB_IN_Desc for the relevant fields.) */ - prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc); - - myPrevRxDesc = myNextRxDesc; - myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol); - myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol); - myLastRxDesc = myPrevRxDesc; - - myNextRxDesc->status = 0; - myNextRxDesc = phys_to_virt(myNextRxDesc->next); - } - - restore_flags(flags); - - DBFEXIT; - - return IRQ_HANDLED; -} - - -/* This function will unlink the SB descriptors associated with this urb. */ -static int etrax_remove_from_sb_list(struct urb *urb) -{ - USB_SB_Desc_t *next_sb, *first_sb, *last_sb; - etrax_urb_priv_t *urb_priv; - int i = 0; - - DBFENTER; - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor - doesn't really need to be disabled, it's just that we expect it to be. */ - if (usb_pipetype(urb->pipe) == PIPE_BULK) { - assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); - } - - first_sb = urb_priv->first_sb; - last_sb = urb_priv->last_sb; - - assert(first_sb); - assert(last_sb); - - while (first_sb != last_sb) { - next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next); - kmem_cache_free(usb_desc_cache, first_sb); - first_sb = next_sb; - i++; - } - kmem_cache_free(usb_desc_cache, last_sb); - i++; - dbg_sb("%d SB descriptors freed", i); - /* Compare i with urb->number_of_packets for Isoc traffic. - Should be same when calling unlink_urb */ - - DBFEXIT; - - return i; -} - -static int etrax_usb_submit_bulk_urb(struct urb *urb) -{ - int epid; - int empty; - unsigned long flags; - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - /* Epid allocation, empty check and list add must be protected. - Read about this in etrax_usb_submit_ctrl_urb. */ - - spin_lock_irqsave(&urb_list_lock, flags); - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - DBFEXIT; - spin_unlock_irqrestore(&urb_list_lock, flags); - return -ENOMEM; - } - empty = urb_list_empty(epid); - urb_list_add(urb, epid); - spin_unlock_irqrestore(&urb_list_lock, flags); - - dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d", - usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid); - - /* Mark the urb as being in progress. */ - urb->status = -EINPROGRESS; - - /* Setup the hcpriv data. */ - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); - assert(urb_priv != NULL); - /* This sets rx_offset to 0. */ - urb_priv->urb_state = NOT_STARTED; - urb->hcpriv = urb_priv; - - if (empty) { - etrax_usb_add_to_bulk_sb_list(urb, epid); - } - - DBFEXIT; - - return 0; -} - -static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid) -{ - USB_SB_Desc_t *sb_desc; - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - unsigned long flags; - char maxlen; - - DBFENTER; - - dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb); - - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - - sb_desc = kmem_cache_zalloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc != NULL); - - - if (usb_pipeout(urb->pipe)) { - - dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid); - - /* This is probably a sanity check of the bulk transaction length - not being larger than 64 kB. */ - if (urb->transfer_buffer_length > 0xffff) { - panic("urb->transfer_buffer_length > 0xffff"); - } - - sb_desc->sw_len = urb->transfer_buffer_length; - - /* The rem field is don't care if it's not a full-length transfer, so setting - it shouldn't hurt. Also, rem isn't used for OUT traffic. */ - sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - /* The full field is set to yes, even if we don't actually check that this is - a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0). - Setting full prevents the USB controller from sending an empty packet in - that case. However, if URB_ZERO_PACKET was set we want that. */ - if (!(urb->transfer_flags & URB_ZERO_PACKET)) { - sb_desc->command |= IO_STATE(USB_SB_command, full, yes); - } - - sb_desc->buf = virt_to_phys(urb->transfer_buffer); - sb_desc->next = 0; - - } else if (usb_pipein(urb->pipe)) { - - dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid); - - sb_desc->sw_len = urb->transfer_buffer_length ? - (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - - /* The rem field is don't care if it's not a full-length transfer, so setting - it shouldn't hurt. */ - sb_desc->command = - (IO_FIELD(USB_SB_command, rem, - urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - sb_desc->buf = 0; - sb_desc->next = 0; - } - - urb_priv->first_sb = sb_desc; - urb_priv->last_sb = sb_desc; - urb_priv->epid = epid; - - urb->hcpriv = urb_priv; - - /* Reset toggle bits and reset error count. */ - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - - /* FIXME: Is this a special case since the hold field is checked, - or should we check hold in a lot of other cases as well? */ - if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) { - panic("Hold was set in %s", __FUNCTION__); - } - - /* Reset error counters (regardless of which direction this traffic is). */ - *R_USB_EPT_DATA &= - ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | - IO_MASK(R_USB_EPT_DATA, error_count_out)); - - /* Software must preset the toggle bits. */ - if (usb_pipeout(urb->pipe)) { - char toggle = - usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out); - *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle); - } else { - char toggle = - usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in); - *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle); - } - - /* Assert that the EP descriptor is disabled. */ - assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))); - - /* The reason we set the EP's sub pointer directly instead of - walking the SB list and linking it last in the list is that we only - have one active urb at a time (the rest are queued). */ - - /* Note that we cannot have interrupts running when we have set the SB descriptor - but the EP is not yet enabled. If a bulk eot happens for another EP, we will - find this EP disabled and with a SB != 0, which will make us think that it's done. */ - TxBulkEPList[epid].sub = virt_to_phys(sb_desc); - TxBulkEPList[epid].hw_len = 0; - /* Note that we don't have to fill in the ep_id field since this - was done when we allocated the EP descriptors in init_tx_bulk_ep. */ - - /* Check if the dummy list is already with us (if several urbs were queued). */ - if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) { - - dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d", - (unsigned long)urb, epid); - - /* The last EP in the dummy list already has its next pointer set to - TxBulkEPList[epid].next. */ - - /* We don't need to check if the DMA is at this EP or not before changing the - next pointer, since we will do it in one 32-bit write (EP descriptors are - 32-bit aligned). */ - TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]); - } - /* Enable the EP descr. */ - dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid); - TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - - /* Everything is set up, safe to enable interrupts again. */ - restore_flags(flags); - - /* If the DMA bulk channel isn't running, we need to restart it if it - has stopped at the last EP descriptor (DMA stopped because there was - no more traffic) or if it has stopped at a dummy EP with the intr flag - set (DMA stopped because we were too slow in inserting new traffic). */ - if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) { - - USB_EP_Desc_t *ep; - ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP); - dbg_bulk("DMA channel not running in add"); - dbg_bulk("DMA is at 0x%lx", (unsigned long)ep); - - if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) || - (ep->command & 0x8) >> 3) { - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); - /* Update/restart the bulk start timer since we just started the channel. */ - mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); - /* Update/restart the bulk eot timer since we just inserted traffic. */ - mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); - } - } - - DBFEXIT; -} - -static void etrax_usb_complete_bulk_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - unsigned long flags; - - DBFENTER; - - if (status) - warn("Completing bulk urb with status %d.", status); - - dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid); - - /* Update the urb list. */ - urb_list_del(urb, epid); - - /* For an IN pipe, we always set the actual length, regardless of whether there was - an error or not (which means the device driver can use the data if it wants to). */ - if (usb_pipein(urb->pipe)) { - urb->actual_length = urb_priv->rx_offset; - } else { - /* Set actual_length for OUT urbs also; the USB mass storage driver seems - to want that. We wouldn't know of any partial writes if there was an error. */ - if (status == 0) { - urb->actual_length = urb->transfer_buffer_length; - } else { - urb->actual_length = 0; - } - } - - /* FIXME: Is there something of the things below we shouldn't do if there was an error? - Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */ - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - - /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */ - if (usb_pipeout(urb->pipe)) { - char toggle = - IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA); - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - } else { - char toggle = - IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA); - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - } - restore_flags(flags); - - /* Remember to free the SBs. */ - etrax_remove_from_sb_list(urb); - kfree(urb_priv); - urb->hcpriv = 0; - - /* If there are any more urb's in the list we'd better start sending */ - if (!urb_list_empty(epid)) { - - struct urb *new_urb; - - /* Get the first urb. */ - new_urb = urb_list_first(epid); - assert(new_urb); - - dbg_bulk("More bulk for epid %d", epid); - - etrax_usb_add_to_bulk_sb_list(new_urb, epid); - } - - urb->status = status; - - /* We let any non-zero status from the layer above have precedence. */ - if (status == 0) { - /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length) - is to be treated as an error. */ - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - if (usb_pipein(urb->pipe) && - (urb->actual_length != - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) { - urb->status = -EREMOTEIO; - } - } - } - - if (urb->complete) { - urb->complete(urb, NULL); - } - - if (urb_list_empty(epid)) { - /* This means that this EP is now free, deconfigure it. */ - etrax_usb_free_epid(epid); - - /* No more traffic; time to clean up. - Must set sub pointer to 0, since we look at the sub pointer when handling - the bulk eot interrupt. */ - - dbg_bulk("No bulk for epid %d", epid); - - TxBulkEPList[epid].sub = 0; - - /* Unlink the dummy list. */ - - dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d", - (unsigned long)urb, epid); - - /* No need to wait for the DMA before changing the next pointer. - The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use - the last one (INVALID_EPID) for actual traffic. */ - TxBulkEPList[epid].next = - virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); - } - - DBFEXIT; -} - -static int etrax_usb_submit_ctrl_urb(struct urb *urb) -{ - int epid; - int empty; - unsigned long flags; - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */ - - /* Epid allocation, empty check and list add must be protected. - - Epid allocation because if we find an existing epid for this endpoint an urb might be - completed (emptying the list) before we add the new urb to the list, causing the epid - to be de-allocated. We would then start the transfer with an invalid epid -> epid attn. - - Empty check and add because otherwise we might conclude that the list is not empty, - after which it becomes empty before we add the new urb to the list, causing us not to - insert the new traffic into the SB list. */ - - spin_lock_irqsave(&urb_list_lock, flags); - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - spin_unlock_irqrestore(&urb_list_lock, flags); - DBFEXIT; - return -ENOMEM; - } - empty = urb_list_empty(epid); - urb_list_add(urb, epid); - spin_unlock_irqrestore(&urb_list_lock, flags); - - dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d", - (unsigned long)urb, empty ? "empty" : "", epid); - - /* Mark the urb as being in progress. */ - urb->status = -EINPROGRESS; - - /* Setup the hcpriv data. */ - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); - assert(urb_priv != NULL); - /* This sets rx_offset to 0. */ - urb_priv->urb_state = NOT_STARTED; - urb->hcpriv = urb_priv; - - if (empty) { - etrax_usb_add_to_ctrl_sb_list(urb, epid); - } - - DBFEXIT; - - return 0; -} - -static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid) -{ - USB_SB_Desc_t *sb_desc_setup; - USB_SB_Desc_t *sb_desc_data; - USB_SB_Desc_t *sb_desc_status; - - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - - unsigned long flags; - char maxlen; - - DBFENTER; - - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - - sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_setup != NULL); - sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_status != NULL); - - /* Initialize the mandatory setup SB descriptor (used only in control transfers) */ - sb_desc_setup->sw_len = 8; - sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, setup) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes)); - - sb_desc_setup->buf = virt_to_phys(urb->setup_packet); - - if (usb_pipeout(urb->pipe)) { - dbg_ctrl("Transfer for epid %d is OUT", epid); - - /* If this Control OUT transfer has an optional data stage we add an OUT token - before the mandatory IN (status) token, hence the reordered SB list */ - - sb_desc_setup->next = virt_to_phys(sb_desc_status); - if (urb->transfer_buffer) { - - dbg_ctrl("This OUT transfer has an extra data stage"); - - sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_data != NULL); - - sb_desc_setup->next = virt_to_phys(sb_desc_data); - - sb_desc_data->sw_len = urb->transfer_buffer_length; - sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes)); - sb_desc_data->buf = virt_to_phys(urb->transfer_buffer); - sb_desc_data->next = virt_to_phys(sb_desc_status); - } - - sb_desc_status->sw_len = 1; - sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, intr, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - sb_desc_status->buf = 0; - sb_desc_status->next = 0; - - } else if (usb_pipein(urb->pipe)) { - - dbg_ctrl("Transfer for epid %d is IN", epid); - dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length); - dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen); - - sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_data != NULL); - - sb_desc_setup->next = virt_to_phys(sb_desc_data); - - sb_desc_data->sw_len = urb->transfer_buffer_length ? - (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - dbg_ctrl("sw_len got %d", sb_desc_data->sw_len); - - sb_desc_data->command = - (IO_FIELD(USB_SB_command, rem, - urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes)); - - sb_desc_data->buf = 0; - sb_desc_data->next = virt_to_phys(sb_desc_status); - - /* Read comment at zout_buffer declaration for an explanation to this. */ - sb_desc_status->sw_len = 1; - sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, intr, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - sb_desc_status->buf = virt_to_phys(&zout_buffer[0]); - sb_desc_status->next = 0; - } - - urb_priv->first_sb = sb_desc_setup; - urb_priv->last_sb = sb_desc_status; - urb_priv->epid = epid; - - urb_priv->urb_state = STARTED; - - /* Reset toggle bits and reset error count, remember to di and ei */ - /* Warning: it is possible that this locking doesn't work with bottom-halves */ - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) { - panic("Hold was set in %s", __FUNCTION__); - } - - - /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits - are set to a specific value. Why the difference? Read "Transfer and Toggle Bits - in Designer's Reference, p. 8 - 11. */ - *R_USB_EPT_DATA &= - ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | - IO_MASK(R_USB_EPT_DATA, error_count_out) | - IO_MASK(R_USB_EPT_DATA, t_in) | - IO_MASK(R_USB_EPT_DATA, t_out)); - - /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now - (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */ - restore_flags(flags); - - /* Assert that the EP descriptor is disabled. */ - assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))); - - /* Set up and enable the EP descriptor. */ - TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup); - TxCtrlEPList[epid].hw_len = 0; - TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - - /* We start the DMA sub channel without checking if it's running or not, because: - 1) If it's already running, issuing the start command is a nop. - 2) We avoid a test-and-set race condition. */ - *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); - - DBFEXIT; -} - -static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - - DBFENTER; - - if (status) - warn("Completing ctrl urb with status %d.", status); - - dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb); - - /* Remove this urb from the list. */ - urb_list_del(urb, epid); - - /* For an IN pipe, we always set the actual length, regardless of whether there was - an error or not (which means the device driver can use the data if it wants to). */ - if (usb_pipein(urb->pipe)) { - urb->actual_length = urb_priv->rx_offset; - } - - /* FIXME: Is there something of the things below we shouldn't do if there was an error? - Like, maybe we shouldn't insert more traffic. */ - - /* Remember to free the SBs. */ - etrax_remove_from_sb_list(urb); - kfree(urb_priv); - urb->hcpriv = 0; - - /* If there are any more urbs in the list we'd better start sending. */ - if (!urb_list_empty(epid)) { - struct urb *new_urb; - - /* Get the first urb. */ - new_urb = urb_list_first(epid); - assert(new_urb); - - dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb); - - etrax_usb_add_to_ctrl_sb_list(new_urb, epid); - } - - urb->status = status; - - /* We let any non-zero status from the layer above have precedence. */ - if (status == 0) { - /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length) - is to be treated as an error. */ - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - if (usb_pipein(urb->pipe) && - (urb->actual_length != - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) { - urb->status = -EREMOTEIO; - } - } - } - - if (urb->complete) { - urb->complete(urb, NULL); - } - - if (urb_list_empty(epid)) { - /* No more traffic. Time to clean up. */ - etrax_usb_free_epid(epid); - /* Must set sub pointer to 0. */ - dbg_ctrl("No ctrl for epid %d", epid); - TxCtrlEPList[epid].sub = 0; - } - - DBFEXIT; -} - -static int etrax_usb_submit_intr_urb(struct urb *urb) -{ - - int epid; - - DBFENTER; - - if (usb_pipeout(urb->pipe)) { - /* Unsupported transfer type. - We don't support interrupt out traffic. (If we do, we can't support - intervals for neither in or out traffic, but are forced to schedule all - interrupt traffic in one frame.) */ - return -EINVAL; - } - - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - DBFEXIT; - return -ENOMEM; - } - - if (!urb_list_empty(epid)) { - /* There is already a queued urb for this endpoint. */ - etrax_usb_free_epid(epid); - return -ENXIO; - } - - urb->status = -EINPROGRESS; - - dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid); - - urb_list_add(urb, epid); - etrax_usb_add_to_intr_sb_list(urb, epid); - - return 0; - - DBFEXIT; -} - -static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid) -{ - - volatile USB_EP_Desc_t *tmp_ep; - volatile USB_EP_Desc_t *first_ep; - - char maxlen; - int interval; - int i; - - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - interval = urb->interval; - - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); - assert(urb_priv != NULL); - urb->hcpriv = urb_priv; - - first_ep = &TxIntrEPList[0]; - - /* Round of the interval to 2^n, it is obvious that this code favours - smaller numbers, but that is actually a good thing */ - /* FIXME: The "rounding error" for larger intervals will be quite - large. For in traffic this shouldn't be a problem since it will only - mean that we "poll" more often. */ - for (i = 0; interval; i++) { - interval = interval >> 1; - } - interval = 1 << (i - 1); - - dbg_intr("Interval rounded to %d", interval); - - tmp_ep = first_ep; - i = 0; - do { - if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) { - if ((i % interval) == 0) { - /* Insert the traffic ep after tmp_ep */ - USB_EP_Desc_t *ep_desc; - USB_SB_Desc_t *sb_desc; - - dbg_intr("Inserting EP for epid %d", epid); - - ep_desc = (USB_EP_Desc_t *) - kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - sb_desc = (USB_SB_Desc_t *) - kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(ep_desc != NULL); - CHECK_ALIGN(ep_desc); - assert(sb_desc != NULL); - - ep_desc->sub = virt_to_phys(sb_desc); - ep_desc->hw_len = 0; - ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) | - IO_STATE(USB_EP_command, enable, yes)); - - - /* Round upwards the number of packets of size maxlen - that this SB descriptor should receive. */ - sb_desc->sw_len = urb->transfer_buffer_length ? - (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - sb_desc->next = 0; - sb_desc->buf = 0; - sb_desc->command = - (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - ep_desc->next = tmp_ep->next; - tmp_ep->next = virt_to_phys(ep_desc); - } - i++; - } - tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next); - } while (tmp_ep != first_ep); - - - /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */ - urb_priv->epid = epid; - - /* We start the DMA sub channel without checking if it's running or not, because: - 1) If it's already running, issuing the start command is a nop. - 2) We avoid a test-and-set race condition. */ - *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); - - DBFEXIT; -} - - - -static void etrax_usb_complete_intr_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - - DBFENTER; - - if (status) - warn("Completing intr urb with status %d.", status); - - dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb); - - urb->status = status; - urb->actual_length = urb_priv->rx_offset; - - dbg_intr("interrupt urb->actual_length = %d", urb->actual_length); - - /* We let any non-zero status from the layer above have precedence. */ - if (status == 0) { - /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length) - is to be treated as an error. */ - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - if (urb->actual_length != - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) { - urb->status = -EREMOTEIO; - } - } - } - - /* The driver will resubmit the URB so we need to remove it first */ - etrax_usb_unlink_urb(urb, 0); - if (urb->complete) { - urb->complete(urb, NULL); - } - - DBFEXIT; -} - - -static int etrax_usb_submit_isoc_urb(struct urb *urb) -{ - int epid; - unsigned long flags; - - DBFENTER; - - dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb); - - /* Epid allocation, empty check and list add must be protected. - Read about this in etrax_usb_submit_ctrl_urb. */ - - spin_lock_irqsave(&urb_list_lock, flags); - /* Is there an active epid for this urb ? */ - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - DBFEXIT; - spin_unlock_irqrestore(&urb_list_lock, flags); - return -ENOMEM; - } - - /* Ok, now we got valid endpoint, lets insert some traffic */ - - urb->status = -EINPROGRESS; - - /* Find the last urb in the URB_List and add this urb after that one. - Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list. This - is important to make this in "real time" since isochronous traffic is - time sensitive. */ - - dbg_isoc("Adding isoc urb to (possibly empty) list"); - urb_list_add(urb, epid); - etrax_usb_add_to_isoc_sb_list(urb, epid); - spin_unlock_irqrestore(&urb_list_lock, flags); - - DBFEXIT; - - return 0; -} - -static void etrax_usb_check_error_isoc_ep(const int epid) -{ - unsigned long int flags; - int error_code; - __u32 r_usb_ept_data; - - /* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof, - bulk_eot and epid_attn interrupts. So we just check the status of - the epid without testing if for it in R_USB_EPID_ATTN. */ - - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO - registers, they are located at the same address and are of the same size. - In other words, this read should be ok for isoc also. */ - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data); - - if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) { - warn("Hold was set for epid %d.", epid); - return; - } - - if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) { - - /* This indicates that the SB list of the ept was completed before - new data was appended to it. This is not an error, but indicates - large system or USB load and could possibly cause trouble for - very timing sensitive USB device drivers so we log it. - */ - info("Isoc. epid %d disabled with no error", epid); - return; - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) { - /* Not really a protocol error, just says that the endpoint gave - a stall response. Note that error_code cannot be stall for isoc. */ - panic("Isoc traffic cannot stall"); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) { - /* Two devices responded to a transaction request. Must be resolved - by software. FIXME: Reset ports? */ - panic("Bus error for epid %d." - " Two devices responded to transaction request", - epid); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) { - /* DMA overrun or underrun. */ - warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid); - - /* It seems that error_code = buffer_error in - R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS - are the same error. */ - } -} - - -static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid) -{ - - int i = 0; - - etrax_urb_priv_t *urb_priv; - USB_SB_Desc_t *prev_sb_desc, *next_sb_desc, *temp_sb_desc; - - DBFENTER; - - prev_sb_desc = next_sb_desc = temp_sb_desc = NULL; - - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC); - assert(urb_priv != NULL); - - urb->hcpriv = urb_priv; - urb_priv->epid = epid; - - if (usb_pipeout(urb->pipe)) { - - if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n"); - - dbg_isoc("Transfer for epid %d is OUT", epid); - dbg_isoc("%d packets in URB", urb->number_of_packets); - - /* Create one SB descriptor for each packet and link them together. */ - for (i = 0; i < urb->number_of_packets; i++) { - if (!urb->iso_frame_desc[i].length) - continue; - - next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC); - assert(next_sb_desc != NULL); - - if (urb->iso_frame_desc[i].length > 0) { - - next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, eot, yes)); - - next_sb_desc->sw_len = urb->iso_frame_desc[i].length; - next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset); - - /* Check if full length transfer. */ - if (urb->iso_frame_desc[i].length == - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) { - next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes); - } - } else { - dbg_isoc("zero len packet"); - next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, full, yes)); - - next_sb_desc->sw_len = 1; - next_sb_desc->buf = virt_to_phys(&zout_buffer[0]); - } - - /* First SB descriptor that belongs to this urb */ - if (i == 0) - urb_priv->first_sb = next_sb_desc; - else - prev_sb_desc->next = virt_to_phys(next_sb_desc); - - prev_sb_desc = next_sb_desc; - } - - next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) | - IO_STATE(USB_SB_command, eol, yes)); - next_sb_desc->next = 0; - urb_priv->last_sb = next_sb_desc; - - } else if (usb_pipein(urb->pipe)) { - - dbg_isoc("Transfer for epid %d is IN", epid); - dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length); - dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length); - - /* Note that in descriptors for periodic traffic are not consumed. This means that - the USB controller never propagates in the SB list. In other words, if there already - is an SB descriptor in the list for this EP we don't have to do anything. */ - if (TxIsocEPList[epid].sub == 0) { - dbg_isoc("Isoc traffic not already running, allocating SB"); - - next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC); - assert(next_sb_desc != NULL); - - next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - next_sb_desc->next = 0; - next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant - for periodic in traffic as long as it is more - than zero. Set to 1 always. */ - next_sb_desc->buf = 0; - - /* The rem field is don't care for isoc traffic, so we don't set it. */ - - /* Only one SB descriptor that belongs to this urb. */ - urb_priv->first_sb = next_sb_desc; - urb_priv->last_sb = next_sb_desc; - - } else { - - dbg_isoc("Isoc traffic already running, just setting first/last_sb"); - - /* Each EP for isoc in will have only one SB descriptor, setup when submitting the - already active urb. Note that even though we may have several first_sb/last_sb - pointing at the same SB descriptor, they are freed only once (when the list has - become empty). */ - urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub); - urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub); - return; - } - - } - - /* Find the spot to insert this urb and add it. */ - if (TxIsocEPList[epid].sub == 0) { - /* First SB descriptor inserted in this list (in or out). */ - dbg_isoc("Inserting SB desc first in list"); - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb); - - } else { - /* Isochronous traffic is already running, insert new traffic last (only out). */ - dbg_isoc("Inserting SB desc last in list"); - temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub); - while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) != - IO_STATE(USB_SB_command, eol, yes)) { - assert(temp_sb_desc->next); - temp_sb_desc = phys_to_virt(temp_sb_desc->next); - } - dbg_isoc("Appending list on desc 0x%p", temp_sb_desc); - - /* Next pointer must be set before eol is removed. */ - temp_sb_desc->next = virt_to_phys(urb_priv->first_sb); - /* Clear the previous end of list flag since there is a new in the - added SB descriptor list. */ - temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol); - - if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) { - /* 8.8.5 in Designer's Reference says we should check for and correct - any errors in the EP here. That should not be necessary if epid_attn - is handled correctly, so we assume all is ok. */ - dbg_isoc("EP disabled"); - etrax_usb_check_error_isoc_ep(epid); - - /* The SB list was exhausted. */ - if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) { - /* The new sublist did not get processed before the EP was - disabled. Setup the EP again. */ - dbg_isoc("Set EP sub to new list"); - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb); - } - } - } - - if (urb->transfer_flags & URB_ISO_ASAP) { - /* The isoc transfer should be started as soon as possible. The start_frame - field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER - with a USB Chief trace shows that the first isoc IN token is sent 2 frames - later. I'm not sure how this affects usage of the start_frame field by the - device driver, or how it affects things when USB_ISO_ASAP is not set, so - therefore there's no compensation for the 2 frame "lag" here. */ - urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff); - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - urb_priv->urb_state = STARTED; - dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame); - } else { - /* Not started yet. */ - urb_priv->urb_state = NOT_STARTED; - dbg_isoc("urb_priv->urb_state set to NOT_STARTED"); - } - - /* We start the DMA sub channel without checking if it's running or not, because: - 1) If it's already running, issuing the start command is a nop. - 2) We avoid a test-and-set race condition. */ - *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); - - DBFEXIT; -} - -static void etrax_usb_complete_isoc_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - int auto_resubmit = 0; - - DBFENTER; - dbg_isoc("complete urb 0x%p, status %d", urb, status); - - if (status) - warn("Completing isoc urb with status %d.", status); - - if (usb_pipein(urb->pipe)) { - int i; - - /* Make that all isoc packets have status and length set before - completing the urb. */ - for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = -EPROTO; - } - - urb_list_del(urb, epid); - - if (!list_empty(&urb_list[epid])) { - ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED; - } else { - unsigned long int flags; - if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])); - } - - etrax_remove_from_sb_list(urb); - TxIsocEPList[epid].sub = 0; - TxIsocEPList[epid].hw_len = 0; - - save_flags(flags); - cli(); - etrax_usb_free_epid(epid); - restore_flags(flags); - } - - urb->hcpriv = 0; - kfree(urb_priv); - - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 0); - } else if (usb_pipeout(urb->pipe)) { - int freed_descr; - - dbg_isoc("Isoc out urb complete 0x%p", urb); - - /* Update the urb list. */ - urb_list_del(urb, epid); - - freed_descr = etrax_remove_from_sb_list(urb); - dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets); - assert(freed_descr == urb->number_of_packets); - urb->hcpriv = 0; - kfree(urb_priv); - - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 0); - } - - urb->status = status; - if (urb->complete) { - urb->complete(urb, NULL); - } - - if (auto_resubmit) { - /* Check that urb was not unlinked by the complete callback. */ - if (__urb_list_entry(urb, epid)) { - /* Move this one down the list. */ - urb_list_move_last(urb, epid); - - /* Mark the now first urb as started (may already be). */ - ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED; - - /* Must set this to 0 since this urb is still active after - completion. */ - urb_priv->isoc_packet_counter = 0; - } else { - warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb); - } - } - - DBFEXIT; -} - -static void etrax_usb_complete_urb(struct urb *urb, int status) -{ - switch (usb_pipetype(urb->pipe)) { - case PIPE_BULK: - etrax_usb_complete_bulk_urb(urb, status); - break; - case PIPE_CONTROL: - etrax_usb_complete_ctrl_urb(urb, status); - break; - case PIPE_INTERRUPT: - etrax_usb_complete_intr_urb(urb, status); - break; - case PIPE_ISOCHRONOUS: - etrax_usb_complete_isoc_urb(urb, status); - break; - default: - err("Unknown pipetype"); - } -} - - - -static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc) -{ - usb_interrupt_registers_t *reg; - unsigned long flags; - __u32 irq_mask; - __u8 status; - __u32 epid_attn; - __u16 port_status_1; - __u16 port_status_2; - __u32 fm_number; - - DBFENTER; - - /* Read critical registers into local variables, do kmalloc afterwards. */ - save_flags(flags); - cli(); - - irq_mask = *R_USB_IRQ_MASK_READ; - /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS - must be read before R_USB_EPID_ATTN since reading the latter clears the - ourun and perror fields of R_USB_STATUS. */ - status = *R_USB_STATUS; - - /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */ - epid_attn = *R_USB_EPID_ATTN; - - /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the - port_status interrupt. */ - port_status_1 = *R_USB_RH_PORT_STATUS_1; - port_status_2 = *R_USB_RH_PORT_STATUS_2; - - /* Reading R_USB_FM_NUMBER clears the sof interrupt. */ - /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */ - fm_number = *R_USB_FM_NUMBER; - - restore_flags(flags); - - reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, GFP_ATOMIC); - - assert(reg != NULL); - - reg->hc = (etrax_hc_t *)vhc; - - /* Now put register values into kmalloc'd area. */ - reg->r_usb_irq_mask_read = irq_mask; - reg->r_usb_status = status; - reg->r_usb_epid_attn = epid_attn; - reg->r_usb_rh_port_status_1 = port_status_1; - reg->r_usb_rh_port_status_2 = port_status_2; - reg->r_usb_fm_number = fm_number; - - INIT_WORK(®->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg); - schedule_work(®->usb_bh); - - DBFEXIT; - - return IRQ_HANDLED; -} - -static void etrax_usb_hc_interrupt_bottom_half(void *data) -{ - usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data; - __u32 irq_mask = reg->r_usb_irq_mask_read; - - DBFENTER; - - /* Interrupts are handled in order of priority. */ - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) { - etrax_usb_hc_epid_attn_interrupt(reg); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) { - etrax_usb_hc_port_status_interrupt(reg); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) { - etrax_usb_hc_ctl_status_interrupt(reg); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) { - etrax_usb_hc_isoc_eof_interrupt(); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) { - /* Update/restart the bulk start timer since obviously the channel is running. */ - mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); - /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */ - mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); - - etrax_usb_hc_bulk_eot_interrupt(0); - } - - kmem_cache_free(top_half_reg_cache, reg); - - DBFEXIT; -} - - -void etrax_usb_hc_isoc_eof_interrupt(void) -{ - struct urb *urb; - etrax_urb_priv_t *urb_priv; - int epid; - unsigned long flags; - - DBFENTER; - - /* Do not check the invalid epid (it has a valid sub pointer). */ - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - - /* Do not check the invalid epid (it has a valid sub pointer). */ - if ((epid == DUMMY_EPID) || (epid == INVALID_EPID)) - continue; - - /* Disable interrupts to block the isoc out descriptor interrupt handler - from being called while the isoc EPID list is being checked. - */ - save_flags(flags); - cli(); - - if (TxIsocEPList[epid].sub == 0) { - /* Nothing here to see. */ - restore_flags(flags); - continue; - } - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - if (urb == 0) { - warn("Ignoring NULL urb"); - restore_flags(flags); - continue; - } - if (usb_pipein(urb->pipe)) { - - /* Sanity check. */ - assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (urb_priv->urb_state == NOT_STARTED) { - - /* If ASAP is not set and urb->start_frame is the current frame, - start the transfer. */ - if (!(urb->transfer_flags & URB_ISO_ASAP) && - (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) { - - dbg_isoc("Enabling isoc IN EP descr for epid %d", epid); - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - - /* This urb is now active. */ - urb_priv->urb_state = STARTED; - continue; - } - } - } - restore_flags(flags); - } - - DBFEXIT; - -} - -void etrax_usb_hc_bulk_eot_interrupt(int timer_induced) -{ - int epid; - - /* The technique is to run one urb at a time, wait for the eot interrupt at which - point the EP descriptor has been disabled. */ - - DBFENTER; - dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : ""); - - for (epid = 0; epid < NBR_OF_EPIDS; epid++) { - - if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) && - (TxBulkEPList[epid].sub != 0)) { - - struct urb *urb; - etrax_urb_priv_t *urb_priv; - unsigned long flags; - __u32 r_usb_ept_data; - - /* Found a disabled EP descriptor which has a non-null sub pointer. - Verify that this ctrl EP descriptor got disabled no errors. - FIXME: Necessary to check error_code? */ - dbg_bulk("for epid %d?", epid); - - /* Get the first urb. */ - urb = urb_list_first(epid); - - /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of - wrong unlinking? */ - if (!urb) { - warn("NULL urb for epid %d", epid); - continue; - } - - assert(urb); - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - /* Sanity checks. */ - assert(usb_pipetype(urb->pipe) == PIPE_BULK); - if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) { - err("bulk endpoint got disabled before reaching last sb"); - } - - /* For bulk IN traffic, there seems to be a race condition between - between the bulk eot and eop interrupts, or rather an uncertainty regarding - the order in which they happen. Normally we expect the eop interrupt from - DMA channel 9 to happen before the eot interrupt. - - Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */ - - if (usb_pipein(urb->pipe)) { - dbg_bulk("in urb, continuing"); - continue; - } - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) == - IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - /* This means that the endpoint has no error, is disabled - and had inserted traffic, i.e. transfer successfully completed. */ - etrax_usb_complete_bulk_urb(urb, 0); - } else { - /* Shouldn't happen. We expect errors to be caught by epid attention. */ - err("Found disabled bulk EP desc, error_code != no_error"); - } - } - } - - /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer. - However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may - not. Also, we might find two disabled EPs when handling an eot interrupt, and then find - none the next time. */ - - DBFEXIT; - -} - -void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg) -{ - /* This function handles the epid attention interrupt. There are a variety of reasons - for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details): - - invalid ep_id - Invalid epid in an EP (EP disabled). - stall - Not strictly an error condition (EP disabled). - 3rd error - Three successive transaction errors (EP disabled). - buffer ourun - Buffer overrun or underrun (EP disabled). - past eof1 - Intr or isoc transaction proceeds past EOF1. - near eof - Intr or isoc transaction would not fit inside the frame. - zout transfer - If zout transfer for a bulk endpoint (EP disabled). - setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */ - - int epid; - - - DBFENTER; - - assert(reg != NULL); - - /* Note that we loop through all epids. We still want to catch errors for - the invalid one, even though we might handle them differently. */ - for (epid = 0; epid < NBR_OF_EPIDS; epid++) { - - if (test_bit(epid, (void *)®->r_usb_epid_attn)) { - - struct urb *urb; - __u32 r_usb_ept_data; - unsigned long flags; - int error_code; - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO - registers, they are located at the same address and are of the same size. - In other words, this read should be ok for isoc also. */ - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - /* First some sanity checks. */ - if (epid == INVALID_EPID) { - /* FIXME: What if it became disabled? Could seriously hurt interrupt - traffic. (Use do_intr_recover.) */ - warn("Got epid_attn for INVALID_EPID (%d).", epid); - err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data); - err("R_USB_STATUS = 0x%x", reg->r_usb_status); - continue; - } else if (epid == DUMMY_EPID) { - /* We definitely don't care about these ones. Besides, they are - always disabled, so any possible disabling caused by the - epid attention interrupt is irrelevant. */ - warn("Got epid_attn for DUMMY_EPID (%d).", epid); - continue; - } - - /* Get the first urb in the urb list for this epid. We blatantly assume - that only the first urb could have caused the epid attention. - (For bulk and ctrl, only one urb is active at any one time. For intr - and isoc we remove them once they are completed.) */ - urb = urb_list_first(epid); - - if (urb == NULL) { - err("Got epid_attn for epid %i with no urb.", epid); - err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data); - err("R_USB_STATUS = 0x%x", reg->r_usb_status); - continue; - } - - switch (usb_pipetype(urb->pipe)) { - case PIPE_BULK: - warn("Got epid attn for bulk endpoint, epid %d", epid); - break; - case PIPE_CONTROL: - warn("Got epid attn for control endpoint, epid %d", epid); - break; - case PIPE_INTERRUPT: - warn("Got epid attn for interrupt endpoint, epid %d", epid); - break; - case PIPE_ISOCHRONOUS: - warn("Got epid attn for isochronous endpoint, epid %d", epid); - break; - } - - if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) { - if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) { - warn("Hold was set for epid %d.", epid); - continue; - } - } - - /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and - R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data); - } else { - error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data); - } - - /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */ - if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - - /* Isoc traffic doesn't have error_count_in/error_count_out. */ - if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) && - (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 || - IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) { - /* 3rd error. */ - warn("3rd error for epid %i", epid); - etrax_usb_complete_urb(urb, -EPROTO); - - } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { - - warn("Perror for epid %d", epid); - - if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) { - /* invalid ep_id */ - panic("Perror because of invalid epid." - " Deconfigured too early?"); - } else { - /* past eof1, near eof, zout transfer, setup transfer */ - - /* Dump the urb and the relevant EP descriptor list. */ - - __dump_urb(urb); - __dump_ept_data(epid); - __dump_ep_list(usb_pipetype(urb->pipe)); - - panic("Something wrong with DMA descriptor contents." - " Too much traffic inserted?"); - } - } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { - /* buffer ourun */ - panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid); - } - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) { - /* Not really a protocol error, just says that the endpoint gave - a stall response. Note that error_code cannot be stall for isoc. */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - panic("Isoc traffic cannot stall"); - } - - warn("Stall for epid %d", epid); - etrax_usb_complete_urb(urb, -EPIPE); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) { - /* Two devices responded to a transaction request. Must be resolved - by software. FIXME: Reset ports? */ - panic("Bus error for epid %d." - " Two devices responded to transaction request", - epid); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) { - /* DMA overrun or underrun. */ - warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid); - - /* It seems that error_code = buffer_error in - R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS - are the same error. */ - etrax_usb_complete_urb(urb, -EPROTO); - } - } - } - - DBFEXIT; - -} - -void etrax_usb_bulk_start_timer_func(unsigned long dummy) -{ - - /* We might enable an EP descriptor behind the current DMA position when it's about - to decide that there are no more bulk traffic and it should stop the bulk channel. - Therefore we periodically check if the bulk channel is stopped and there is an - enabled bulk EP descriptor, in which case we start the bulk channel. */ - dbg_bulk("bulk_start_timer timed out."); - - if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) { - int epid; - - dbg_bulk("Bulk DMA channel not running."); - - for (epid = 0; epid < NBR_OF_EPIDS; epid++) { - if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n", - epid); - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); - - /* Restart the bulk eot timer since we just started the bulk channel. */ - mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); - - /* No need to search any further. */ - break; - } - } - } else { - dbg_bulk("Bulk DMA channel running."); - } -} - -void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg) -{ - etrax_hc_t *hc = reg->hc; - __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1; - __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2; - - DBFENTER; - - /* The Etrax RH does not include a wPortChange register, so this has to be handled in software - (by saving the old port status value for comparison when the port status interrupt happens). - See section 11.16.2.6.2 in the USB 1.1 spec for details. */ - - dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1); - dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2); - dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1); - dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2); - - /* C_PORT_CONNECTION is set on any transition. */ - hc->rh.wPortChange_1 |= - ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) != - (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ? - (1 << RH_PORT_CONNECTION) : 0; - - hc->rh.wPortChange_2 |= - ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) != - (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ? - (1 << RH_PORT_CONNECTION) : 0; - - /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when - the port is disabled, not when it's enabled. */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE)) - && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_ENABLE) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE)) - && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_ENABLE) : 0; - - /* C_PORT_SUSPEND is set to one when the device has transitioned out - of the suspended state, i.e. when suspend goes from one to zero. */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND)) - && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ? - (1 << RH_PORT_SUSPEND) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND)) - && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ? - (1 << RH_PORT_SUSPEND) : 0; - - - /* C_PORT_RESET is set when reset processing on this port is complete. */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET)) - && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ? - (1 << RH_PORT_RESET) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET)) - && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ? - (1 << RH_PORT_RESET) : 0; - - /* Save the new values for next port status change. */ - hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1; - hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2; - - dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1); - dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2); - - DBFEXIT; - -} - -void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg) -{ - DBFENTER; - - /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB - list for the corresponding epid? */ - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { - panic("USB controller got ourun."); - } - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { - - /* Before, etrax_usb_do_intr_recover was called on this epid if it was - an interrupt pipe. I don't see how re-enabling all EP descriptors - will help if there was a programming error. */ - panic("USB controller got perror."); - } - - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) { - /* We should never operate in device mode. */ - panic("USB controller in device mode."); - } - - /* These if-statements could probably be nested. */ - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) { - info("USB controller in host mode."); - } - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) { - info("USB controller started."); - } - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) { - info("USB controller running."); - } - - DBFEXIT; - -} - - -static int etrax_rh_submit_urb(struct urb *urb) -{ - struct usb_device *usb_dev = urb->dev; - etrax_hc_t *hc = usb_dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int stat = 0; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - DBFENTER; - - /* FIXME: What is this interrupt urb that is sent to the root hub? */ - if (usb_pipetype (pipe) == PIPE_INTERRUPT) { - dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval); - hc->rh.urb = urb; - hc->rh.send = 1; - /* FIXME: We could probably remove this line since it's done - in etrax_rh_init_int_timer. (Don't remove it from - etrax_rh_init_int_timer though.) */ - hc->rh.interval = urb->interval; - etrax_rh_init_int_timer(urb); - DBFEXIT; - - return 0; - } - - bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); - wValue = le16_to_cpu(cmd->wValue); - wIndex = le16_to_cpu(cmd->wIndex); - wLength = le16_to_cpu(cmd->wLength); - - dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq); - dbg_rh("wValue : 0x%04x (%d)", wValue, wValue); - dbg_rh("wIndex : 0x%04x (%d)", wIndex, wIndex); - dbg_rh("wLength : 0x%04x (%d)", wLength, wLength); - - switch (bmRType_bReq) { - - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data = cpu_to_le16 (1); - OK (2); - - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data = cpu_to_le32 (0); - OK (4); /* hub power ** */ - - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - if (wIndex == 1) { - *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1); - *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1); - } else if (wIndex == 2) { - *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2); - *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2); - } else { - dbg_rh("RH_GET_STATUS whith invalid wIndex!"); - OK(0); - } - - OK(4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case (RH_C_HUB_OVER_CURRENT): - OK (0); /* hub power over current ** */ - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - if (wIndex == 1) { - - dbg_rh("trying to do disable port 1"); - - *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes); - - while (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes)); - *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); - dbg_rh("Port 1 is disabled"); - - } else if (wIndex == 2) { - - dbg_rh("trying to do disable port 2"); - - *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes); - - while (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes)); - *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no); - dbg_rh("Port 2 is disabled"); - - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_PORT_SUSPEND): - /* Opposite to suspend should be resume, so we'll do a resume. */ - /* FIXME: USB 1.1, 11.16.2.2 says: - "Clearing the PORT_SUSPEND feature causes a host-initiated resume - on the specified port. If the port is not in the Suspended state, - the hub should treat this request as a functional no-operation." - Shouldn't we check if the port is in a suspended state before - resuming? */ - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - if (wIndex == 1) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, resume) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else if (wIndex == 2) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port2) | - IO_STATE(R_USB_COMMAND, port_cmd, resume) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_PORT_POWER): - OK (0); /* port power ** */ - case (RH_C_PORT_CONNECTION): - if (wIndex == 1) { - hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION); - } else if (wIndex == 2) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_C_PORT_ENABLE): - if (wIndex == 1) { - hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE); - } else if (wIndex == 2) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE " - "with invalid wIndex == %d!", wIndex); - } - OK (0); - case (RH_C_PORT_SUSPEND): -/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ - OK (0); - case (RH_C_PORT_OVER_CURRENT): - OK (0); /* port power over current ** */ - case (RH_C_PORT_RESET): - if (wIndex == 1) { - hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET); - } else if (wIndex == 2) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET " - "with invalid index == %d!", wIndex); - } - - OK (0); - - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - if (wIndex == 1) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, suspend) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else if (wIndex == 2) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port2) | - IO_STATE(R_USB_COMMAND, port_cmd, suspend) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else { - dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_PORT_RESET): - if (wIndex == 1) { - - port_1_reset: - dbg_rh("Doing reset of port 1"); - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - - /* We must wait at least 10 ms for the device to recover. - 15 ms should be enough. */ - udelay(15000); - - /* Wait for reset bit to go low (should be done by now). */ - while (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes)); - - /* If the port status is - 1) connected and enabled then there is a device and everything is fine - 2) neither connected nor enabled then there is no device, also fine - 3) connected and not enabled then we try again - (Yes, there are other port status combinations besides these.) */ - - if ((hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) && - (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) { - dbg_rh("Connected device on port 1, but port not enabled?" - " Trying reset again."); - goto port_2_reset; - } - - /* Diagnostic printouts. */ - if ((hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) && - (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) { - dbg_rh("No connected device on port 1"); - } else if ((hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) && - (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) { - dbg_rh("Connected device on port 1, port 1 enabled"); - } - - } else if (wIndex == 2) { - - port_2_reset: - dbg_rh("Doing reset of port 2"); - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Issue the reset command. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port2) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - - /* We must wait at least 10 ms for the device to recover. - 15 ms should be enough. */ - udelay(15000); - - /* Wait for reset bit to go low (should be done by now). */ - while (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes)); - - /* If the port status is - 1) connected and enabled then there is a device and everything is fine - 2) neither connected nor enabled then there is no device, also fine - 3) connected and not enabled then we try again - (Yes, there are other port status combinations besides these.) */ - - if ((hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) && - (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) { - dbg_rh("Connected device on port 2, but port not enabled?" - " Trying reset again."); - goto port_2_reset; - } - - /* Diagnostic printouts. */ - if ((hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) && - (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) { - dbg_rh("No connected device on port 2"); - } else if ((hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) && - (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) { - dbg_rh("Connected device on port 2, port 2 enabled"); - } - - } else { - dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex); - } - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* If all enabled ports were disabled the host controller goes down into - started mode, so we need to bring it back into the running state. - (This is safe even if it's already in the running state.) */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - - dbg_rh("...Done"); - OK(0); - - case (RH_PORT_POWER): - OK (0); /* port power ** */ - case (RH_PORT_ENABLE): - /* There is no port enable command in the host controller, so if the - port is already enabled, we do nothing. If not, we reset the port - (with an ugly goto). */ - - if (wIndex == 1) { - if (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) { - goto port_1_reset; - } - } else if (wIndex == 2) { - if (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) { - goto port_2_reset; - } - } else { - dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex); - } - OK (0); - } - break; - - case RH_SET_ADDRESS: - hc->rh.devnum = wValue; - dbg_rh("RH address set to: %d", hc->rh.devnum); - OK (0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength)); - memcpy (data, root_hub_dev_des, len); - OK (len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength)); - memcpy (data, root_hub_config_des, len); - OK (len); - case (0x03): /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, - 0xff, "ETRAX 100LX", - data, wLength); - if (len > 0) { - OK(min(leni, len)); - } else { - stat = -EPIPE; - } - - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - root_hub_hub_des[2] = hc->rh.numports; - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); - memcpy (data, root_hub_hub_des, len); - OK (len); - - case RH_GET_CONFIGURATION: - *(__u8 *) data = 0x01; - OK (1); - - case RH_SET_CONFIGURATION: - OK (0); - - default: - stat = -EPIPE; - } - - urb->actual_length = len; - urb->status = stat; - urb->dev = NULL; - if (urb->complete) { - urb->complete(urb, NULL); - } - DBFEXIT; - - return 0; -} - -static void -etrax_usb_bulk_eot_timer_func(unsigned long dummy) -{ - /* Because of a race condition in the top half, we might miss a bulk eot. - This timer "simulates" a bulk eot if we don't get one for a while, hopefully - correcting the situation. */ - dbg_bulk("bulk_eot_timer timed out."); - etrax_usb_hc_bulk_eot_interrupt(1); -} - -static void* -etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, - unsigned mem_flags, dma_addr_t *dma) -{ - return kmalloc(size, mem_flags); -} - -static void -etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma) -{ - kfree(addr); -} - - -static struct device fake_device; - -static int __init etrax_usb_hc_init(void) -{ - static etrax_hc_t *hc; - struct usb_bus *bus; - struct usb_device *usb_rh; - int i; - - DBFENTER; - - info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version); - - hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL); - assert(hc != NULL); - - /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */ - /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate - SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) == - sizeof(USB_SB_Desc_t). */ - - usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, - SLAB_HWCACHE_ALIGN, 0, 0); - assert(usb_desc_cache != NULL); - - top_half_reg_cache = kmem_cache_create("top_half_reg_cache", - sizeof(usb_interrupt_registers_t), - 0, SLAB_HWCACHE_ALIGN, 0, 0); - assert(top_half_reg_cache != NULL); - - isoc_compl_cache = kmem_cache_create("isoc_compl_cache", - sizeof(usb_isoc_complete_data_t), - 0, SLAB_HWCACHE_ALIGN, 0, 0); - assert(isoc_compl_cache != NULL); - - etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations); - hc->bus = bus; - bus->bus_name="ETRAX 100LX"; - bus->hcpriv = hc; - - /* Initialize RH to the default address. - And make sure that we have no status change indication */ - hc->rh.numports = 2; /* The RH has two ports */ - hc->rh.devnum = 1; - hc->rh.wPortChange_1 = 0; - hc->rh.wPortChange_2 = 0; - - /* Also initate the previous values to zero */ - hc->rh.prev_wPortStatus_1 = 0; - hc->rh.prev_wPortStatus_2 = 0; - - /* Initialize the intr-traffic flags */ - /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */ - hc->intr.sleeping = 0; - hc->intr.wq = NULL; - - epid_usage_bitmask = 0; - epid_out_traffic = 0; - - /* Mark the invalid epid as being used. */ - set_bit(INVALID_EPID, (void *)&epid_usage_bitmask); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID); - nop(); - /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */ - *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) | - IO_FIELD(R_USB_EPT_DATA, max_len, 1)); - - /* Mark the dummy epid as being used. */ - set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID); - nop(); - *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) | - IO_FIELD(R_USB_EPT_DATA, max_len, 1)); - - /* Initialize the urb list by initiating a head for each list. */ - for (i = 0; i < NBR_OF_EPIDS; i++) { - INIT_LIST_HEAD(&urb_list[i]); - } - spin_lock_init(&urb_list_lock); - - INIT_LIST_HEAD(&urb_unlink_list); - - - /* Initiate the bulk start timer. */ - init_timer(&bulk_start_timer); - bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL; - bulk_start_timer.function = etrax_usb_bulk_start_timer_func; - add_timer(&bulk_start_timer); - - - /* Initiate the bulk eot timer. */ - init_timer(&bulk_eot_timer); - bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL; - bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func; - add_timer(&bulk_eot_timer); - - /* Set up the data structures for USB traffic. Note that this must be done before - any interrupt that relies on sane DMA list occurrs. */ - init_rx_buffers(); - init_tx_bulk_ep(); - init_tx_ctrl_ep(); - init_tx_intr_ep(); - init_tx_isoc_ep(); - - device_initialize(&fake_device); - kobject_set_name(&fake_device.kobj, "etrax_usb"); - kobject_add(&fake_device.kobj); - kobject_uevent(&fake_device.kobj, KOBJ_ADD); - hc->bus->controller = &fake_device; - usb_register_bus(hc->bus); - - *R_IRQ_MASK2_SET = - /* Note that these interrupts are not used. */ - IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) | - /* Sub channel 1 (ctrl) descr. interrupts are used. */ - IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) | - IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) | - /* Sub channel 3 (isoc) descr. interrupts are used. */ - IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set); - - /* Note that the dma9_descr interrupt is not used. */ - *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) | - IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set); - - /* FIXME: Enable iso_eof only when isoc traffic is running. */ - *R_USB_IRQ_MASK_SET = - IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) | - IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) | - IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) | - IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) | - IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set); - - - if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0, - "ETRAX 100LX built-in USB (HC)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0, - "ETRAX 100LX built-in USB (Rx)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0, - "ETRAX 100LX built-in USB (Tx)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - /* R_USB_COMMAND: - USB commands in host mode. The fields in this register should all be - written to in one write. Do not read-modify-write one field at a time. A - write to this register will trigger events in the USB controller and an - incomplete command may lead to unpredictable results, and in worst case - even to a deadlock in the controller. - (Note however that the busy field is read-only, so no need to write to it.) */ - - /* Check the busy bit before writing to R_USB_COMMAND. */ - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Reset the USB interface. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, reset); - - /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800), - to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may - allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation. - - While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK - behaviour, it doesn't solve this problem. What happens is that a control transfer will not - be interrupted in its data stage when PSTART happens (the point at which periodic traffic - is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before - PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done, - there may be too little time left for an isochronous transfer, causing an epid attention - interrupt due to perror. The work-around for this is to let the control transfers run at the - end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't - fit into the frame. However, since there will *always* be a control transfer at the beginning - of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer - which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to - this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make - sure that the periodic transfers that are inserted will always fit in the frame. - - The idea was suggested that a control transfer could be split up into several 8 byte transfers, - so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this - hasn't been implemented. - - The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra - for possible bit stuffing. */ - - *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960); - -#ifdef CONFIG_ETRAX_USB_HOST_PORT1 - *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); -#endif - -#ifdef CONFIG_ETRAX_USB_HOST_PORT2 - *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no); -#endif - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Configure the USB interface as a host controller. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config); - - /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled - sequence of resetting the ports. If we reset both ports now, and there are devices - on both ports, we will get a bus error because both devices will answer the set address - request. */ - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Start processing of USB traffic. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - usb_rh = usb_alloc_dev(NULL, hc->bus, 0); - hc->bus->root_hub = usb_rh; - usb_rh->state = USB_STATE_ADDRESS; - usb_rh->speed = USB_SPEED_FULL; - usb_rh->devnum = 1; - hc->bus->devnum_next = 2; - usb_rh->ep0.desc.wMaxPacketSize = __const_cpu_to_le16(64); - usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE); - usb_new_device(usb_rh); - - DBFEXIT; - - return 0; -} - -static void etrax_usb_hc_cleanup(void) -{ - DBFENTER; - - free_irq(ETRAX_USB_HC_IRQ, NULL); - free_irq(ETRAX_USB_RX_IRQ, NULL); - free_irq(ETRAX_USB_TX_IRQ, NULL); - - usb_deregister_bus(etrax_usb_bus); - - /* FIXME: call kmem_cache_destroy here? */ - - DBFEXIT; -} - -module_init(etrax_usb_hc_init); -module_exit(etrax_usb_hc_cleanup); diff --git a/drivers/usb/host/hc_crisv10.h b/drivers/usb/host/hc_crisv10.h deleted file mode 100644 index 62f7711..0000000 --- a/drivers/usb/host/hc_crisv10.h +++ /dev/null @@ -1,289 +0,0 @@ -#ifndef __LINUX_ETRAX_USB_H -#define __LINUX_ETRAX_USB_H - -#include -#include - -typedef struct USB_IN_Desc { - volatile __u16 sw_len; - volatile __u16 command; - volatile unsigned long next; - volatile unsigned long buf; - volatile __u16 hw_len; - volatile __u16 status; -} USB_IN_Desc_t; - -typedef struct USB_SB_Desc { - volatile __u16 sw_len; - volatile __u16 command; - volatile unsigned long next; - volatile unsigned long buf; - __u32 dummy; -} USB_SB_Desc_t; - -typedef struct USB_EP_Desc { - volatile __u16 hw_len; - volatile __u16 command; - volatile unsigned long sub; - volatile unsigned long next; - __u32 dummy; -} USB_EP_Desc_t; - -struct virt_root_hub { - int devnum; - void *urb; - void *int_addr; - int send; - int interval; - int numports; - struct timer_list rh_int_timer; - volatile __u16 wPortChange_1; - volatile __u16 wPortChange_2; - volatile __u16 prev_wPortStatus_1; - volatile __u16 prev_wPortStatus_2; -}; - -struct etrax_usb_intr_traffic { - int sleeping; - int error; - struct wait_queue *wq; -}; - -typedef struct etrax_usb_hc { - struct usb_bus *bus; - struct virt_root_hub rh; - struct etrax_usb_intr_traffic intr; -} etrax_hc_t; - -typedef enum { - STARTED, - NOT_STARTED, - UNLINK, - TRANSFER_DONE, - WAITING_FOR_DESCR_INTR -} etrax_usb_urb_state_t; - - - -typedef struct etrax_usb_urb_priv { - /* The first_sb field is used for freeing all SB descriptors belonging - to an urb. The corresponding ep descriptor's sub pointer cannot be - used for this since the DMA advances the sub pointer as it processes - the sb list. */ - USB_SB_Desc_t *first_sb; - /* The last_sb field referes to the last SB descriptor that belongs to - this urb. This is important to know so we can free the SB descriptors - that ranges between first_sb and last_sb. */ - USB_SB_Desc_t *last_sb; - - /* The rx_offset field is used in ctrl and bulk traffic to keep track - of the offset in the urb's transfer_buffer where incoming data should be - copied to. */ - __u32 rx_offset; - - /* Counter used in isochronous transfers to keep track of the - number of packets received/transmitted. */ - __u32 isoc_packet_counter; - - /* This field is used to pass information about the urb's current state between - the various interrupt handlers (thus marked volatile). */ - volatile etrax_usb_urb_state_t urb_state; - - /* Connection between the submitted urb and ETRAX epid number */ - __u8 epid; - - /* The rx_data_list field is used for periodic traffic, to hold - received data for later processing in the the complete_urb functions, - where the data us copied to the urb's transfer_buffer. Basically, we - use this intermediate storage because we don't know when it's safe to - reuse the transfer_buffer (FIXME?). */ - struct list_head rx_data_list; -} etrax_urb_priv_t; - -/* This struct is for passing data from the top half to the bottom half. */ -typedef struct usb_interrupt_registers -{ - etrax_hc_t *hc; - __u32 r_usb_epid_attn; - __u8 r_usb_status; - __u16 r_usb_rh_port_status_1; - __u16 r_usb_rh_port_status_2; - __u32 r_usb_irq_mask_read; - __u32 r_usb_fm_number; - struct work_struct usb_bh; -} usb_interrupt_registers_t; - -/* This struct is for passing data from the isoc top half to the isoc bottom half. */ -typedef struct usb_isoc_complete_data -{ - struct urb *urb; - struct work_struct usb_bh; -} usb_isoc_complete_data_t; - -/* This struct holds data we get from the rx descriptors for DMA channel 9 - for periodic traffic (intr and isoc). */ -typedef struct rx_data -{ - void *data; - int length; - struct list_head list; -} rx_data_t; - -typedef struct urb_entry -{ - struct urb *urb; - struct list_head list; -} urb_entry_t; - -/* --------------------------------------------------------------------------- - Virtual Root HUB - ------------------------------------------------------------------------- */ -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -/* Our Vendor Specific feature */ -#define RH_REMOVE_EP 0x00 - - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -/* Field definitions for */ - -#define USB_IN_command__eol__BITNR 0 /* command macros */ -#define USB_IN_command__eol__WIDTH 1 -#define USB_IN_command__eol__no 0 -#define USB_IN_command__eol__yes 1 - -#define USB_IN_command__intr__BITNR 3 -#define USB_IN_command__intr__WIDTH 1 -#define USB_IN_command__intr__no 0 -#define USB_IN_command__intr__yes 1 - -#define USB_IN_status__eop__BITNR 1 /* status macros. */ -#define USB_IN_status__eop__WIDTH 1 -#define USB_IN_status__eop__no 0 -#define USB_IN_status__eop__yes 1 - -#define USB_IN_status__eot__BITNR 5 -#define USB_IN_status__eot__WIDTH 1 -#define USB_IN_status__eot__no 0 -#define USB_IN_status__eot__yes 1 - -#define USB_IN_status__error__BITNR 6 -#define USB_IN_status__error__WIDTH 1 -#define USB_IN_status__error__no 0 -#define USB_IN_status__error__yes 1 - -#define USB_IN_status__nodata__BITNR 7 -#define USB_IN_status__nodata__WIDTH 1 -#define USB_IN_status__nodata__no 0 -#define USB_IN_status__nodata__yes 1 - -#define USB_IN_status__epid__BITNR 8 -#define USB_IN_status__epid__WIDTH 5 - -#define USB_EP_command__eol__BITNR 0 -#define USB_EP_command__eol__WIDTH 1 -#define USB_EP_command__eol__no 0 -#define USB_EP_command__eol__yes 1 - -#define USB_EP_command__eof__BITNR 1 -#define USB_EP_command__eof__WIDTH 1 -#define USB_EP_command__eof__no 0 -#define USB_EP_command__eof__yes 1 - -#define USB_EP_command__intr__BITNR 3 -#define USB_EP_command__intr__WIDTH 1 -#define USB_EP_command__intr__no 0 -#define USB_EP_command__intr__yes 1 - -#define USB_EP_command__enable__BITNR 4 -#define USB_EP_command__enable__WIDTH 1 -#define USB_EP_command__enable__no 0 -#define USB_EP_command__enable__yes 1 - -#define USB_EP_command__hw_valid__BITNR 5 -#define USB_EP_command__hw_valid__WIDTH 1 -#define USB_EP_command__hw_valid__no 0 -#define USB_EP_command__hw_valid__yes 1 - -#define USB_EP_command__epid__BITNR 8 -#define USB_EP_command__epid__WIDTH 5 - -#define USB_SB_command__eol__BITNR 0 /* command macros. */ -#define USB_SB_command__eol__WIDTH 1 -#define USB_SB_command__eol__no 0 -#define USB_SB_command__eol__yes 1 - -#define USB_SB_command__eot__BITNR 1 -#define USB_SB_command__eot__WIDTH 1 -#define USB_SB_command__eot__no 0 -#define USB_SB_command__eot__yes 1 - -#define USB_SB_command__intr__BITNR 3 -#define USB_SB_command__intr__WIDTH 1 -#define USB_SB_command__intr__no 0 -#define USB_SB_command__intr__yes 1 - -#define USB_SB_command__tt__BITNR 4 -#define USB_SB_command__tt__WIDTH 2 -#define USB_SB_command__tt__zout 0 -#define USB_SB_command__tt__in 1 -#define USB_SB_command__tt__out 2 -#define USB_SB_command__tt__setup 3 - - -#define USB_SB_command__rem__BITNR 8 -#define USB_SB_command__rem__WIDTH 6 - -#define USB_SB_command__full__BITNR 6 -#define USB_SB_command__full__WIDTH 1 -#define USB_SB_command__full__no 0 -#define USB_SB_command__full__yes 1 - -#endif diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f0d29ed..e8bbe8b 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -486,9 +486,6 @@ static int ohci_run (struct ohci_hcd *oh * or if bus glue did the same (e.g. for PCI add-in cards with * PCI PM support). */ - ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - ohci_readl (ohci, &ohci->regs->control)); if ((ohci->hc_control & OHCI_CTRL_RWC) != 0 && !device_may_wakeup(hcd->self.controller)) device_init_wakeup(hcd->self.controller, 1); @@ -744,9 +741,6 @@ static void ohci_stop (struct usb_hcd *h { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - hcd->state); ohci_dump (ohci, 1); flush_scheduled_work(); diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index b331ac4..7970560 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -20,10 +20,16 @@ #endif /*-------------------------------------------------------------------------*/ +static int broken_suspend(struct usb_hcd *hcd) +{ + device_init_wakeup(&hcd->self.root_hub->dev, 0); + return 0; +} + /* AMD 756, for most chips (early revs), corrupts register * values on read ... so enable the vendor workaround. */ -static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd) +static int ohci_quirk_amd756(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -31,16 +37,14 @@ static int __devinit ohci_quirk_amd756(s ohci_dbg (ohci, "AMD756 erratum 4 workaround\n"); /* also erratum 10 (suspend/resume issues) */ - device_init_wakeup(&hcd->self.root_hub->dev, 0); - - return 0; + return broken_suspend(hcd); } /* Apple's OHCI driver has a lot of bizarre workarounds * for this chip. Evidently control and bulk lists * can get confused. (B&W G3 models, and ...) */ -static int __devinit ohci_quirk_opti(struct usb_hcd *hcd) +static int ohci_quirk_opti(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -53,7 +57,7 @@ static int __devinit ohci_quirk_opti(str * identify the USB (fn2). This quirk might apply to more or * even all NSC stuff. */ -static int __devinit ohci_quirk_ns(struct usb_hcd *hcd) +static int ohci_quirk_ns(struct usb_hcd *hcd) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct pci_dev *b; @@ -75,7 +79,7 @@ static int __devinit ohci_quirk_ns(struc * delays before control or bulk queues get re-activated * in finish_unlinks() */ -static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd) +static int ohci_quirk_zfmicro(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -88,7 +92,7 @@ static int __devinit ohci_quirk_zfmicro( /* Check for Toshiba SCC OHCI which has big endian registers * and little endian in memory data structures */ -static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd) +static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -129,6 +133,18 @@ static const struct pci_device_id ohci_p PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6), .driver_data = (unsigned long)ohci_quirk_toshiba_scc, }, + { + /* Toshiba portege 4000 */ + .vendor = PCI_VENDOR_ID_AL, + .device = 0x5237, + .subvendor = PCI_VENDOR_ID_TOSHIBA_2, + .subdevice = 0x0004, + .driver_data = (unsigned long) broken_suspend, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152), + .driver_data = (unsigned long) broken_suspend, + }, /* FIXME for some of the early AMD 760 southbridges, OHCI * won't work at all. blacklist them. */ diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 19a0cc0..4aed305 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -123,10 +123,14 @@ static struct uhci_td *uhci_alloc_td(str static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) { - if (!list_empty(&td->list)) + if (!list_empty(&td->list)) { dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); - if (!list_empty(&td->fl_list)) + WARN_ON(1); + } + if (!list_empty(&td->fl_list)) { dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); + WARN_ON(1); + } dma_pool_free(uhci->td_pool, td, td->dma_handle); } @@ -291,8 +295,10 @@ static struct uhci_qh *uhci_alloc_qh(str static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { WARN_ON(qh->state != QH_STATE_IDLE && qh->udev); - if (!list_empty(&qh->queue)) + if (!list_empty(&qh->queue)) { dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh); + WARN_ON(1); + } list_del(&qh->node); if (qh->udev) { @@ -740,9 +746,11 @@ static void uhci_free_urb_priv(struct uh { struct uhci_td *td, *tmp; - if (!list_empty(&urbp->node)) + if (!list_empty(&urbp->node)) { dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n", urbp->urb); + WARN_ON(1); + } list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { uhci_remove_td_from_urbp(td); diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 69a9f3b..a792e42 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -4,151 +4,6 @@ # comment "USB Input Devices" depends on USB -config USB_HID - tristate "USB Human Interface Device (full HID) support" - default y - depends on USB && INPUT - select HID - ---help--- - Say Y here if you want full HID support to connect USB keyboards, - mice, joysticks, graphic tablets, or any other HID based devices - to your computer via USB, as well as Uninterruptible Power Supply - (UPS) and monitor control devices. - - You can't use this driver and the HIDBP (Boot Protocol) keyboard - and mouse drivers at the same time. More information is available: - . - - If unsure, say Y. - - To compile this driver as a module, choose M here: the - module will be called usbhid. - -comment "Input core support is needed for USB HID input layer or HIDBP support" - depends on USB_HID && INPUT=n - -config USB_HIDINPUT_POWERBOOK - bool "Enable support for iBook/PowerBook special keys" - default n - depends on USB_HID - help - Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks and PowerBooks. - - If unsure, say N. - -config HID_FF - bool "Force feedback support (EXPERIMENTAL)" - depends on USB_HID && EXPERIMENTAL - help - Say Y here is you want force feedback support for a few HID devices. - See below for a list of supported devices. - - See for a description of the force - feedback API. - - If unsure, say N. - -config HID_PID - bool "PID device support" - depends on HID_FF - help - Say Y here if you have a PID-compliant device and wish to enable force - feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such - devices. - -config LOGITECH_FF - bool "Logitech devices support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have one of these devices: - - Logitech WingMan Cordless RumblePad - - Logitech WingMan Cordless RumblePad 2 - - Logitech WingMan Force 3D - - Logitech Formula Force EX - - Logitech MOMO Force wheel - - and if you want to enable force feedback for them. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config PANTHERLORD_FF - bool "PantherLord USB/PS2 2in1 Adapter support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want - to enable force feedback support for it. - -config THRUSTMASTER_FF - bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" - depends on HID_FF && EXPERIMENTAL - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, - and want to enable force feedback support for it. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config ZEROPLUS_FF - bool "Zeroplus based game controller support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a Zeroplus based game controller and want to - enable force feedback for it. - -config USB_HIDDEV - bool "/dev/hiddev raw HID device support" - depends on USB_HID - help - Say Y here if you want to support HID devices (from the USB - specification standpoint) that aren't strictly user interface - devices, like monitor controls and Uninterruptable Power Supplies. - - This module supports these devices separately using a separate - event interface on /dev/usb/hiddevX (char 180:96 to 180:111). - - If unsure, say Y. - -menu "USB HID Boot Protocol drivers" - depends on USB!=n && USB_HID!=y - -config USB_KBD - tristate "USB HIDBP Keyboard (simple Boot) support" - depends on USB && INPUT - ---help--- - Say Y here only if you are absolutely sure that you don't want - to use the generic HID driver for your USB keyboard and prefer - to use the keyboard in its limited Boot Protocol mode instead. - - This is almost certainly not what you want. This is mostly - useful for embedded applications or simple keyboards. - - To compile this driver as a module, choose M here: the - module will be called usbkbd. - - If even remotely unsure, say N. - -config USB_MOUSE - tristate "USB HIDBP Mouse (simple Boot) support" - depends on USB && INPUT - ---help--- - Say Y here only if you are absolutely sure that you don't want - to use the generic HID driver for your USB mouse and prefer - to use the mouse in its limited Boot Protocol mode instead. - - This is almost certainly not what you want. This is mostly - useful for embedded applications or simple mice. - - To compile this driver as a module, choose M here: the - module will be called usbmouse. - - If even remotely unsure, say N. - -endmenu - config USB_AIPTEK tristate "Aiptek 6000U/8000U tablet support" depends on USB && INPUT diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index a9d206c..9bf420e 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -4,40 +4,12 @@ # # Multipart objects. wacom-objs := wacom_wac.o wacom_sys.o -usbhid-objs := hid-core.o - -# Optional parts of multipart objects. - -ifeq ($(CONFIG_USB_HIDDEV),y) - usbhid-objs += hiddev.o -endif -ifeq ($(CONFIG_HID_PID),y) - usbhid-objs += hid-pidff.o -endif -ifeq ($(CONFIG_LOGITECH_FF),y) - usbhid-objs += hid-lgff.o -endif -ifeq ($(CONFIG_PANTHERLORD_FF),y) - usbhid-objs += hid-plff.o -endif -ifeq ($(CONFIG_THRUSTMASTER_FF),y) - usbhid-objs += hid-tmff.o -endif -ifeq ($(CONFIG_ZEROPLUS_FF),y) - usbhid-objs += hid-zpff.o -endif -ifeq ($(CONFIG_HID_FF),y) - usbhid-objs += hid-ff.o -endif obj-$(CONFIG_USB_AIPTEK) += aiptek.o obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o -obj-$(CONFIG_USB_HID) += usbhid.o -obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_KBTAB) += kbtab.o obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.o -obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o obj-$(CONFIG_USB_ITMTOUCH) += itmtouch.o obj-$(CONFIG_USB_EGALAX) += touchkitusb.o diff --git a/drivers/usb/input/ati_remote2.c b/drivers/usb/input/ati_remote2.c index 83f1f79..6459be9 100644 --- a/drivers/usb/input/ati_remote2.c +++ b/drivers/usb/input/ati_remote2.c @@ -2,6 +2,7 @@ * ati_remote2 - ATI/Philips USB RF remote driver * * Copyright (C) 2005 Ville Syrjala + * Copyright (C) 2007 Peter Stokes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -11,13 +12,29 @@ #include #define DRIVER_DESC "ATI/Philips USB RF remote driver" -#define DRIVER_VERSION "0.1" +#define DRIVER_VERSION "0.2" MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_AUTHOR("Ville Syrjala "); MODULE_LICENSE("GPL"); +/* + * ATI Remote Wonder II Channel Configuration + * + * The remote control can by assigned one of sixteen "channels" in order to facilitate + * the use of multiple remote controls within range of each other. + * A remote's "channel" may be altered by pressing and holding the "PC" button for + * approximately 3 seconds, after which the button will slowly flash the count of the + * currently configured "channel", using the numeric keypad enter a number between 1 and + * 16 and then the "PC" button again, the button will slowly flash the count of the + * newly configured "channel". + */ + +static unsigned int channel_mask = 0xFFFF; +module_param(channel_mask, uint, 0644); +MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>"); + static unsigned int mode_mask = 0x1F; module_param(mode_mask, uint, 0644); MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); @@ -146,15 +163,23 @@ static void ati_remote2_input_mouse(stru { struct input_dev *idev = ar2->idev; u8 *data = ar2->buf[0]; + int channel, mode; + + channel = data[0] >> 4; + + if (!((1 << channel) & channel_mask)) + return; - if (data[0] > 4) { + mode = data[0] & 0x0F; + + if (mode > 4) { dev_err(&ar2->intf[0]->dev, "Unknown mode byte (%02x %02x %02x %02x)\n", data[3], data[2], data[1], data[0]); return; } - if (!((1 << data[0]) & mode_mask)) + if (!((1 << mode) & mode_mask)) return; input_event(idev, EV_REL, REL_X, (s8) data[1]); @@ -177,9 +202,16 @@ static void ati_remote2_input_key(struct { struct input_dev *idev = ar2->idev; u8 *data = ar2->buf[1]; - int hw_code, index; + int channel, mode, hw_code, index; + + channel = data[0] >> 4; + + if (!((1 << channel) & channel_mask)) + return; - if (data[0] > 4) { + mode = data[0] & 0x0F; + + if (mode > 4) { dev_err(&ar2->intf[1]->dev, "Unknown mode byte (%02x %02x %02x %02x)\n", data[3], data[2], data[1], data[0]); @@ -199,16 +231,16 @@ static void ati_remote2_input_key(struct * events for the mouse pad so we filter out any subsequent * events from the same mode key. */ - if (ar2->mode == data[0]) + if (ar2->mode == mode) return; if (data[1] == 0) - ar2->mode = data[0]; + ar2->mode = mode; - hw_code |= data[0] << 8; + hw_code |= mode << 8; } - if (!((1 << data[0]) & mode_mask)) + if (!((1 << mode) & mode_mask)) return; index = ati_remote2_lookup(hw_code); @@ -379,6 +411,41 @@ static void ati_remote2_urb_cleanup(stru } } +static int ati_remote2_setup(struct ati_remote2 *ar2) +{ + int r, i, channel; + + /* + * Configure receiver to only accept input from remote "channel" + * channel == 0 -> Accept input from any remote channel + * channel == 1 -> Only accept input from remote channel 1 + * channel == 2 -> Only accept input from remote channel 2 + * ... + * channel == 16 -> Only accept input from remote channel 16 + */ + + channel = 0; + for (i = 0; i < 16; i++) { + if ((1 << i) & channel_mask) { + if (!(~(1 << i) & 0xFFFF & channel_mask)) + channel = i + 1; + break; + } + } + + r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0), + 0x20, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (r) { + dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n", + __FUNCTION__, r); + return r; + } + + return 0; +} + static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); @@ -409,6 +476,10 @@ static int ati_remote2_probe(struct usb_ if (r) goto fail2; + r = ati_remote2_setup(ar2); + if (r) + goto fail2; + usb_make_path(udev, ar2->phys, sizeof(ar2->phys)); strlcat(ar2->phys, "/input0", sizeof(ar2->phys)); diff --git a/drivers/usb/input/gtco.c b/drivers/usb/input/gtco.c index 203cdc1..ae756e0 100644 --- a/drivers/usb/input/gtco.c +++ b/drivers/usb/input/gtco.c @@ -1047,13 +1047,10 @@ static void gtco_disconnect(struct usb_i /* Grab private device ptr */ struct gtco *device = usb_get_intfdata (interface); - struct input_dev *inputdev; - - inputdev = device->inputdevice; /* Now reverse all the registration stuff */ if (device) { - input_unregister_device(inputdev); + input_unregister_device(device->inputdevice); usb_kill_urb(device->urbinfo); usb_free_urb(device->urbinfo); usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c deleted file mode 100644 index 827a75a..0000000 --- a/drivers/usb/input/hid-core.c +++ /dev/null @@ -1,1477 +0,0 @@ -/* - * USB HID support for Linux - * - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2005 Vojtech Pavlik - * Copyright (c) 2005 Michael Haboustak for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include "usbhid.h" - -/* - * Version Information - */ - -#define DRIVER_VERSION "v2.6" -#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik" -#define DRIVER_DESC "USB HID core driver" -#define DRIVER_LICENSE "GPL" - -static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", - "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; -/* - * Module parameters. - */ - -static unsigned int hid_mousepoll_interval; -module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); -MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); - -/* - * Input submission and I/O error handler. - */ - -static void hid_io_error(struct hid_device *hid); - -/* Start up the input URB */ -static int hid_start_in(struct hid_device *hid) -{ - unsigned long flags; - int rc = 0; - struct usbhid_device *usbhid = hid->driver_data; - - spin_lock_irqsave(&usbhid->inlock, flags); - if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) && - !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { - rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); - if (rc != 0) - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - } - spin_unlock_irqrestore(&usbhid->inlock, flags); - return rc; -} - -/* I/O retry timer routine */ -static void hid_retry_timeout(unsigned long _hid) -{ - struct hid_device *hid = (struct hid_device *) _hid; - struct usbhid_device *usbhid = hid->driver_data; - - dev_dbg(&usbhid->intf->dev, "retrying intr urb\n"); - if (hid_start_in(hid)) - hid_io_error(hid); -} - -/* Workqueue routine to reset the device or clear a halt */ -static void hid_reset(struct work_struct *work) -{ - struct usbhid_device *usbhid = - container_of(work, struct usbhid_device, reset_work); - struct hid_device *hid = usbhid->hid; - int rc_lock, rc = 0; - - if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { - dev_dbg(&usbhid->intf->dev, "clear halt\n"); - rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); - clear_bit(HID_CLEAR_HALT, &usbhid->iofl); - hid_start_in(hid); - } - - else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { - dev_dbg(&usbhid->intf->dev, "resetting device\n"); - rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); - if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); - if (rc_lock) - usb_unlock_device(hid_to_usb_dev(hid)); - } - clear_bit(HID_RESET_PENDING, &usbhid->iofl); - } - - switch (rc) { - case 0: - if (!test_bit(HID_IN_RUNNING, &usbhid->iofl)) - hid_io_error(hid); - break; - default: - err("can't reset device, %s-%s/input%d, status %d", - hid_to_usb_dev(hid)->bus->bus_name, - hid_to_usb_dev(hid)->devpath, - usbhid->ifnum, rc); - /* FALLTHROUGH */ - case -EHOSTUNREACH: - case -ENODEV: - case -EINTR: - break; - } -} - -/* Main I/O error handler */ -static void hid_io_error(struct hid_device *hid) -{ - unsigned long flags; - struct usbhid_device *usbhid = hid->driver_data; - - spin_lock_irqsave(&usbhid->inlock, flags); - - /* Stop when disconnected */ - if (usb_get_intfdata(usbhid->intf) == NULL) - goto done; - - /* When an error occurs, retry at increasing intervals */ - if (usbhid->retry_delay == 0) { - usbhid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */ - usbhid->stop_retry = jiffies + msecs_to_jiffies(1000); - } else if (usbhid->retry_delay < 100) - usbhid->retry_delay *= 2; - - if (time_after(jiffies, usbhid->stop_retry)) { - - /* Retries failed, so do a port reset */ - if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { - schedule_work(&usbhid->reset_work); - goto done; - } - } - - mod_timer(&usbhid->io_retry, - jiffies + msecs_to_jiffies(usbhid->retry_delay)); -done: - spin_unlock_irqrestore(&usbhid->inlock, flags); -} - -/* - * Input interrupt completion handler. - */ - -static void hid_irq_in(struct urb *urb) -{ - struct hid_device *hid = urb->context; - struct usbhid_device *usbhid = hid->driver_data; - int status; - - switch (urb->status) { - case 0: /* success */ - usbhid->retry_delay = 0; - hid_input_report(urb->context, HID_INPUT_REPORT, - urb->transfer_buffer, - urb->actual_length, 1); - break; - case -EPIPE: /* stall */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - set_bit(HID_CLEAR_HALT, &usbhid->iofl); - schedule_work(&usbhid->reset_work); - return; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: /* unplug */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - return; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ETIME: /* protocol error or unplug */ - case -ETIMEDOUT: /* Should never happen, but... */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - hid_io_error(hid); - return; - default: /* error */ - warn("input irq status %d received", urb->status); - } - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - if (status != -EPERM) { - err("can't resubmit intr, %s-%s/input%d, status %d", - hid_to_usb_dev(hid)->bus->bus_name, - hid_to_usb_dev(hid)->devpath, - usbhid->ifnum, status); - hid_io_error(hid); - } - } -} - -static int hid_submit_out(struct hid_device *hid) -{ - struct hid_report *report; - struct usbhid_device *usbhid = hid->driver_data; - - report = usbhid->out[usbhid->outtail]; - - hid_output_report(report, usbhid->outbuf); - usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); - usbhid->urbout->dev = hid_to_usb_dev(hid); - - dbg("submitting out urb"); - - if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { - err("usb_submit_urb(out) failed"); - return -1; - } - - return 0; -} - -static int hid_submit_ctrl(struct hid_device *hid) -{ - struct hid_report *report; - unsigned char dir; - int len; - struct usbhid_device *usbhid = hid->driver_data; - - report = usbhid->ctrl[usbhid->ctrltail].report; - dir = usbhid->ctrl[usbhid->ctrltail].dir; - - len = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (dir == USB_DIR_OUT) { - hid_output_report(report, usbhid->ctrlbuf); - usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); - usbhid->urbctrl->transfer_buffer_length = len; - } else { - int maxpacket, padlen; - - usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); - maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); - if (maxpacket > 0) { - padlen = (len + maxpacket - 1) / maxpacket; - padlen *= maxpacket; - if (padlen > usbhid->bufsize) - padlen = usbhid->bufsize; - } else - padlen = 0; - usbhid->urbctrl->transfer_buffer_length = padlen; - } - usbhid->urbctrl->dev = hid_to_usb_dev(hid); - - usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; - usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT; - usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id); - usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); - usbhid->cr->wLength = cpu_to_le16(len); - - dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u", - usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", - usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); - - if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { - err("usb_submit_urb(ctrl) failed"); - return -1; - } - - return 0; -} - -/* - * Output interrupt completion handler. - */ - -static void hid_irq_out(struct urb *urb) -{ - struct hid_device *hid = urb->context; - struct usbhid_device *usbhid = hid->driver_data; - unsigned long flags; - int unplug = 0; - - switch (urb->status) { - case 0: /* success */ - break; - case -ESHUTDOWN: /* unplug */ - unplug = 1; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ECONNRESET: /* unlink */ - case -ENOENT: - break; - default: /* error */ - warn("output irq status %d received", urb->status); - } - - spin_lock_irqsave(&usbhid->outlock, flags); - - if (unplug) - usbhid->outtail = usbhid->outhead; - else - usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); - - if (usbhid->outhead != usbhid->outtail) { - if (hid_submit_out(hid)) { - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - wake_up(&hid->wait); - } - spin_unlock_irqrestore(&usbhid->outlock, flags); - return; - } - - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - spin_unlock_irqrestore(&usbhid->outlock, flags); - wake_up(&hid->wait); -} - -/* - * Control pipe completion handler. - */ - -static void hid_ctrl(struct urb *urb) -{ - struct hid_device *hid = urb->context; - struct usbhid_device *usbhid = hid->driver_data; - unsigned long flags; - int unplug = 0; - - spin_lock_irqsave(&usbhid->ctrllock, flags); - - switch (urb->status) { - case 0: /* success */ - if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) - hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, - urb->transfer_buffer, urb->actual_length, 0); - break; - case -ESHUTDOWN: /* unplug */ - unplug = 1; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -EPIPE: /* report not available */ - break; - default: /* error */ - warn("ctrl urb status %d received", urb->status); - } - - if (unplug) - usbhid->ctrltail = usbhid->ctrlhead; - else - usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); - - if (usbhid->ctrlhead != usbhid->ctrltail) { - if (hid_submit_ctrl(hid)) { - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - wake_up(&hid->wait); - } - spin_unlock_irqrestore(&usbhid->ctrllock, flags); - return; - } - - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - spin_unlock_irqrestore(&usbhid->ctrllock, flags); - wake_up(&hid->wait); -} - -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) -{ - int head; - unsigned long flags; - struct usbhid_device *usbhid = hid->driver_data; - - if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) - return; - - if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { - - spin_lock_irqsave(&usbhid->outlock, flags); - - if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) { - spin_unlock_irqrestore(&usbhid->outlock, flags); - warn("output queue full"); - return; - } - - usbhid->out[usbhid->outhead] = report; - usbhid->outhead = head; - - if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) - if (hid_submit_out(hid)) - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - - spin_unlock_irqrestore(&usbhid->outlock, flags); - return; - } - - spin_lock_irqsave(&usbhid->ctrllock, flags); - - if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) { - spin_unlock_irqrestore(&usbhid->ctrllock, flags); - warn("control queue full"); - return; - } - - usbhid->ctrl[usbhid->ctrlhead].report = report; - usbhid->ctrl[usbhid->ctrlhead].dir = dir; - usbhid->ctrlhead = head; - - if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) - if (hid_submit_ctrl(hid)) - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - - spin_unlock_irqrestore(&usbhid->ctrllock, flags); -} - -static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct hid_device *hid = dev->private; - struct hid_field *field; - int offset; - - if (type == EV_FF) - return input_ff_event(dev, type, code, value); - - if (type != EV_LED) - return -1; - - if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { - warn("event field not found"); - return -1; - } - - hid_set_field(field, offset, value); - usbhid_submit_report(hid, field->report, USB_DIR_OUT); - - return 0; -} - -int usbhid_wait_io(struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) && - !test_bit(HID_OUT_RUNNING, &usbhid->iofl)), - 10*HZ)) { - dbg("timeout waiting for ctrl or out queue to clear"); - return -1; - } - - return 0; -} - -static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report, - ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); -} - -static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, - unsigned char type, void *buf, int size) -{ - int result, retries = 4; - - memset(buf, 0, size); - - do { - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, - (type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT); - retries--; - } while (result < size && retries); - return result; -} - -int usbhid_open(struct hid_device *hid) -{ - ++hid->open; - if (hid_start_in(hid)) - hid_io_error(hid); - return 0; -} - -void usbhid_close(struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - if (!--hid->open) - usb_kill_urb(usbhid->urbin); -} - -#define USB_VENDOR_ID_PANJIT 0x134c - -#define USB_VENDOR_ID_TURBOX 0x062a -#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 -#define USB_VENDOR_ID_CIDC 0x1677 - -/* - * Initialize all reports - */ - -void usbhid_init_reports(struct hid_device *hid) -{ - struct hid_report *report; - struct usbhid_device *usbhid = hid->driver_data; - int err, ret; - - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) - usbhid_submit_report(hid, report, USB_DIR_IN); - - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) - usbhid_submit_report(hid, report, USB_DIR_IN); - - err = 0; - ret = usbhid_wait_io(hid); - while (ret) { - err |= ret; - if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) - usb_kill_urb(usbhid->urbctrl); - if (test_bit(HID_OUT_RUNNING, &usbhid->iofl)) - usb_kill_urb(usbhid->urbout); - ret = usbhid_wait_io(hid); - } - - if (err) - warn("timeout initializing reports"); -} - -#define USB_VENDOR_ID_GTCO 0x078c -#define USB_DEVICE_ID_GTCO_90 0x0090 -#define USB_DEVICE_ID_GTCO_100 0x0100 -#define USB_DEVICE_ID_GTCO_101 0x0101 -#define USB_DEVICE_ID_GTCO_103 0x0103 -#define USB_DEVICE_ID_GTCO_104 0x0104 -#define USB_DEVICE_ID_GTCO_105 0x0105 -#define USB_DEVICE_ID_GTCO_106 0x0106 -#define USB_DEVICE_ID_GTCO_107 0x0107 -#define USB_DEVICE_ID_GTCO_108 0x0108 -#define USB_DEVICE_ID_GTCO_200 0x0200 -#define USB_DEVICE_ID_GTCO_201 0x0201 -#define USB_DEVICE_ID_GTCO_202 0x0202 -#define USB_DEVICE_ID_GTCO_203 0x0203 -#define USB_DEVICE_ID_GTCO_204 0x0204 -#define USB_DEVICE_ID_GTCO_205 0x0205 -#define USB_DEVICE_ID_GTCO_206 0x0206 -#define USB_DEVICE_ID_GTCO_207 0x0207 -#define USB_DEVICE_ID_GTCO_300 0x0300 -#define USB_DEVICE_ID_GTCO_301 0x0301 -#define USB_DEVICE_ID_GTCO_302 0x0302 -#define USB_DEVICE_ID_GTCO_303 0x0303 -#define USB_DEVICE_ID_GTCO_304 0x0304 -#define USB_DEVICE_ID_GTCO_305 0x0305 -#define USB_DEVICE_ID_GTCO_306 0x0306 -#define USB_DEVICE_ID_GTCO_307 0x0307 -#define USB_DEVICE_ID_GTCO_308 0x0308 -#define USB_DEVICE_ID_GTCO_309 0x0309 -#define USB_DEVICE_ID_GTCO_400 0x0400 -#define USB_DEVICE_ID_GTCO_401 0x0401 -#define USB_DEVICE_ID_GTCO_402 0x0402 -#define USB_DEVICE_ID_GTCO_403 0x0403 -#define USB_DEVICE_ID_GTCO_404 0x0404 -#define USB_DEVICE_ID_GTCO_405 0x0405 -#define USB_DEVICE_ID_GTCO_500 0x0500 -#define USB_DEVICE_ID_GTCO_501 0x0501 -#define USB_DEVICE_ID_GTCO_502 0x0502 -#define USB_DEVICE_ID_GTCO_503 0x0503 -#define USB_DEVICE_ID_GTCO_504 0x0504 -#define USB_DEVICE_ID_GTCO_1000 0x1000 -#define USB_DEVICE_ID_GTCO_1001 0x1001 -#define USB_DEVICE_ID_GTCO_1002 0x1002 -#define USB_DEVICE_ID_GTCO_1003 0x1003 -#define USB_DEVICE_ID_GTCO_1004 0x1004 -#define USB_DEVICE_ID_GTCO_1005 0x1005 -#define USB_DEVICE_ID_GTCO_1006 0x1006 - -#define USB_VENDOR_ID_WACOM 0x056a - -#define USB_VENDOR_ID_ACECAD 0x0460 -#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 -#define USB_DEVICE_ID_ACECAD_302 0x0008 - -#define USB_VENDOR_ID_KBGEAR 0x084e -#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 - -#define USB_VENDOR_ID_AIPTEK 0x08ca -#define USB_DEVICE_ID_AIPTEK_01 0x0001 -#define USB_DEVICE_ID_AIPTEK_10 0x0010 -#define USB_DEVICE_ID_AIPTEK_20 0x0020 -#define USB_DEVICE_ID_AIPTEK_21 0x0021 -#define USB_DEVICE_ID_AIPTEK_22 0x0022 -#define USB_DEVICE_ID_AIPTEK_23 0x0023 -#define USB_DEVICE_ID_AIPTEK_24 0x0024 - -#define USB_VENDOR_ID_GRIFFIN 0x077d -#define USB_DEVICE_ID_POWERMATE 0x0410 -#define USB_DEVICE_ID_SOUNDKNOB 0x04AA - -#define USB_VENDOR_ID_ATEN 0x0557 -#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 -#define USB_DEVICE_ID_ATEN_CS124U 0x2202 -#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 -#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 -#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 - -#define USB_VENDOR_ID_TOPMAX 0x0663 -#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 - -#define USB_VENDOR_ID_HAPP 0x078b -#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 -#define USB_DEVICE_ID_UGCI_FLYING 0x0020 -#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 - -#define USB_VENDOR_ID_MGE 0x0463 -#define USB_DEVICE_ID_MGE_UPS 0xffff -#define USB_DEVICE_ID_MGE_UPS1 0x0001 - -#define USB_VENDOR_ID_ONTRAK 0x0a07 -#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 - -#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f -#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 - -#define USB_VENDOR_ID_A4TECH 0x09da -#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 - -#define USB_VENDOR_ID_AASHIMA 0x06d6 -#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 -#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 - -#define USB_VENDOR_ID_CYPRESS 0x04b4 -#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 -#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 -#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 - -#define USB_VENDOR_ID_BERKSHIRE 0x0c98 -#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 - -#define USB_VENDOR_ID_ALPS 0x0433 -#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 - -#define USB_VENDOR_ID_SAITEK 0x06a3 -#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 - -#define USB_VENDOR_ID_NEC 0x073e -#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 - -#define USB_VENDOR_ID_CHIC 0x05fe -#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 - -#define USB_VENDOR_ID_GLAB 0x06c2 -#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 -#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 -#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 -#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044 -#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045 -#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051 -#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 -#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 - -#define USB_VENDOR_ID_WISEGROUP 0x0925 -#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 -#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 -#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 -#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 - -#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 -#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 - -#define USB_VENDOR_ID_CODEMERCS 0x07c0 -#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 -#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff - -#define USB_VENDOR_ID_DELORME 0x1163 -#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 -#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 - -#define USB_VENDOR_ID_MCC 0x09db -#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 -#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a - -#define USB_VENDOR_ID_VERNIER 0x08f7 -#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 -#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 -#define USB_DEVICE_ID_VERNIER_SKIP 0x0003 -#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 - -#define USB_VENDOR_ID_LD 0x0f11 -#define USB_DEVICE_ID_LD_CASSY 0x1000 -#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 -#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 -#define USB_DEVICE_ID_LD_JWM 0x1080 -#define USB_DEVICE_ID_LD_DMMP 0x1081 -#define USB_DEVICE_ID_LD_UMIP 0x1090 -#define USB_DEVICE_ID_LD_XRAY1 0x1100 -#define USB_DEVICE_ID_LD_XRAY2 0x1101 -#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 -#define USB_DEVICE_ID_LD_COM3LAB 0x2000 -#define USB_DEVICE_ID_LD_TELEPORT 0x2010 -#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 -#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 -#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 - -#define USB_VENDOR_ID_APPLE 0x05ac -#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 -#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e -#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f -#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 -#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 -#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 -#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 -#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 -#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 -#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a -#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b -#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c -#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a -#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b -#define USB_DEVICE_ID_APPLE_IR 0x8240 - -#define USB_VENDOR_ID_CHERRY 0x046a -#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 - -#define USB_VENDOR_ID_YEALINK 0x6993 -#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 - -#define USB_VENDOR_ID_ALCOR 0x058f -#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 - -#define USB_VENDOR_ID_SUN 0x0430 -#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab - -#define USB_VENDOR_ID_AIRCABLE 0x16CA -#define USB_DEVICE_ID_AIRCABLE1 0x1502 - -#define USB_VENDOR_ID_LOGITECH 0x046d -#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER 0xc101 -#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2 0xc517 -#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 - -#define USB_VENDOR_ID_IMATION 0x0718 -#define USB_DEVICE_ID_DISC_STAKKA 0xd000 - -#define USB_VENDOR_ID_PANTHERLORD 0x0810 -#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 - -#define USB_VENDOR_ID_SONY 0x054c -#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 - -/* - * Alphabetically sorted blacklist by quirk type. - */ - -static const struct hid_blacklist { - __u16 idVendor; - __u16 idProduct; - unsigned quirks; -} hid_blacklist[] = { - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, - - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, - { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 }, - - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, - - { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2, HID_QUIRK_LOGITECH_S510_DESCRIPTOR }, - - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, - - { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, - - { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, - - { 0, 0 } -}; - -/* - * Traverse the supplied list of reports and find the longest - */ -static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max) -{ - struct hid_report *report; - int size; - - list_for_each_entry(report, &hid->report_enum[type].report_list, list) { - size = ((report->size - 1) >> 3) + 1; - if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered) - size++; - if (*max < size) - *max = size; - } -} - -static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma))) - return -1; - if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma))) - return -1; - if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma))) - return -1; - if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma))) - return -1; - - return 0; -} - -static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - if (usbhid->inbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); - if (usbhid->outbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); - if (usbhid->cr) - usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); - if (usbhid->ctrlbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); -} - -/* - * Cherry Cymotion keyboard have an invalid HID report descriptor, - * that needs fixing before we can parse it. - */ - -static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize) -{ - if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { - info("Fixing up Cherry Cymotion report descriptor"); - rdesc[11] = rdesc[16] = 0xff; - rdesc[12] = rdesc[17] = 0x03; - } -} - -/* - * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller - * to "operational". Without this, the ps3 controller will not report any - * events. - */ -static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) -{ - int result; - char *buf = kmalloc(18, GFP_KERNEL); - - if (!buf) - return; - - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - HID_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, - (3 << 8) | 0xf2, ifnum, buf, 17, - USB_CTRL_GET_TIMEOUT); - - if (result < 0) - err("%s failed: %d\n", __func__, result); - - kfree(buf); -} - -/* - * Logitech S510 keyboard sends in report #3 keys which are far - * above the logical maximum described in descriptor. This extends - * the original value of 0x28c of logical maximum to 0x104d - */ -static void hid_fixup_s510_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 90 && rdesc[83] == 0x26 - && rdesc[84] == 0x8c - && rdesc[85] == 0x02) { - info("Fixing up Logitech S510 report descriptor"); - rdesc[84] = rdesc[89] = 0x4d; - rdesc[85] = rdesc[90] = 0x10; - } -} - -static struct hid_device *usb_hid_configure(struct usb_interface *intf) -{ - struct usb_host_interface *interface = intf->cur_altsetting; - struct usb_device *dev = interface_to_usbdev (intf); - struct hid_descriptor *hdesc; - struct hid_device *hid; - unsigned quirks = 0, rsize = 0; - char *rdesc; - int n, len, insize = 0; - struct usbhid_device *usbhid; - - /* Ignore all Wacom devices */ - if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM) - return NULL; - /* ignore all Code Mercenaries IOWarrior devices */ - if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_CODEMERCS) - if (le16_to_cpu(dev->descriptor.idProduct) >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && - le16_to_cpu(dev->descriptor.idProduct) <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) - return NULL; - - for (n = 0; hid_blacklist[n].idVendor; n++) - if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) && - (hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct))) - quirks = hid_blacklist[n].quirks; - - /* Many keyboards and mice don't like to be polled for reports, - * so we will always set the HID_QUIRK_NOGET flag for them. */ - if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) { - if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD || - interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) - quirks |= HID_QUIRK_NOGET; - } - - if (quirks & HID_QUIRK_IGNORE) - return NULL; - - if ((quirks & HID_QUIRK_IGNORE_MOUSE) && - (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) - return NULL; - - - if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && - (!interface->desc.bNumEndpoints || - usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { - dbg("class descriptor not present\n"); - return NULL; - } - - for (n = 0; n < hdesc->bNumDescriptors; n++) - if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) - rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); - - if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { - dbg("weird size of report descriptor (%u)", rsize); - return NULL; - } - - if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) { - dbg("couldn't allocate rdesc memory"); - return NULL; - } - - hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); - - if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { - dbg("reading report descriptor failed"); - kfree(rdesc); - return NULL; - } - - if ((quirks & HID_QUIRK_CYMOTION)) - hid_fixup_cymotion_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_LOGITECH_S510_DESCRIPTOR) - hid_fixup_s510_descriptor(rdesc, rsize); - -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); - for (n = 0; n < rsize; n++) - printk(" %02x", (unsigned char) rdesc[n]); - printk("\n"); -#endif - - if (!(hid = hid_parse_report(rdesc, n))) { - dbg("parsing report descriptor failed"); - kfree(rdesc); - return NULL; - } - - kfree(rdesc); - hid->quirks = quirks; - - if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL))) - goto fail; - - hid->driver_data = usbhid; - usbhid->hid = hid; - - usbhid->bufsize = HID_MIN_BUFFER_SIZE; - hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize); - hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize); - hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize); - - if (usbhid->bufsize > HID_MAX_BUFFER_SIZE) - usbhid->bufsize = HID_MAX_BUFFER_SIZE; - - hid_find_max_report(hid, HID_INPUT_REPORT, &insize); - - if (insize > HID_MAX_BUFFER_SIZE) - insize = HID_MAX_BUFFER_SIZE; - - if (hid_alloc_buffers(dev, hid)) { - hid_free_buffers(dev, hid); - goto fail; - } - - for (n = 0; n < interface->desc.bNumEndpoints; n++) { - - struct usb_endpoint_descriptor *endpoint; - int pipe; - int interval; - - endpoint = &interface->endpoint[n].desc; - if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ - continue; - - interval = endpoint->bInterval; - - /* Change the polling interval of mice. */ - if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) - interval = hid_mousepoll_interval; - - if (usb_endpoint_dir_in(endpoint)) { - if (usbhid->urbin) - continue; - if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL))) - goto fail; - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize, - hid_irq_in, hid, interval); - usbhid->urbin->transfer_dma = usbhid->inbuf_dma; - usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - } else { - if (usbhid->urbout) - continue; - if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL))) - goto fail; - pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress); - usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0, - hid_irq_out, hid, interval); - usbhid->urbout->transfer_dma = usbhid->outbuf_dma; - usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - } - } - - if (!usbhid->urbin) { - err("couldn't find an input interrupt endpoint"); - goto fail; - } - - init_waitqueue_head(&hid->wait); - - INIT_WORK(&usbhid->reset_work, hid_reset); - setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); - - spin_lock_init(&usbhid->inlock); - spin_lock_init(&usbhid->outlock); - spin_lock_init(&usbhid->ctrllock); - - hid->version = le16_to_cpu(hdesc->bcdHID); - hid->country = hdesc->bCountryCode; - hid->dev = &intf->dev; - usbhid->intf = intf; - usbhid->ifnum = interface->desc.bInterfaceNumber; - - hid->name[0] = 0; - - if (dev->manufacturer) - strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); - - if (dev->product) { - if (dev->manufacturer) - strlcat(hid->name, " ", sizeof(hid->name)); - strlcat(hid->name, dev->product, sizeof(hid->name)); - } - - if (!strlen(hid->name)) - snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - hid->bus = BUS_USB; - hid->vendor = le16_to_cpu(dev->descriptor.idVendor); - hid->product = le16_to_cpu(dev->descriptor.idProduct); - - usb_make_path(dev, hid->phys, sizeof(hid->phys)); - strlcat(hid->phys, "/input", sizeof(hid->phys)); - len = strlen(hid->phys); - if (len < sizeof(hid->phys) - 1) - snprintf(hid->phys + len, sizeof(hid->phys) - len, - "%d", intf->altsetting[0].desc.bInterfaceNumber); - - if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) - hid->uniq[0] = 0; - - usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); - if (!usbhid->urbctrl) - goto fail; - - usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr, - usbhid->ctrlbuf, 1, hid_ctrl, hid); - usbhid->urbctrl->setup_dma = usbhid->cr_dma; - usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma; - usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - hid->hidinput_input_event = usb_hidinput_input_event; - hid->hid_open = usbhid_open; - hid->hid_close = usbhid_close; -#ifdef CONFIG_USB_HIDDEV - hid->hiddev_hid_event = hiddev_hid_event; - hid->hiddev_report_event = hiddev_report_event; -#endif - return hid; - -fail: - usb_free_urb(usbhid->urbin); - usb_free_urb(usbhid->urbout); - usb_free_urb(usbhid->urbctrl); - hid_free_buffers(dev, hid); - hid_free_device(hid); - - return NULL; -} - -static void hid_disconnect(struct usb_interface *intf) -{ - struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid; - - if (!hid) - return; - - usbhid = hid->driver_data; - - spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ - usb_set_intfdata(intf, NULL); - spin_unlock_irq(&usbhid->inlock); - usb_kill_urb(usbhid->urbin); - usb_kill_urb(usbhid->urbout); - usb_kill_urb(usbhid->urbctrl); - - del_timer_sync(&usbhid->io_retry); - flush_scheduled_work(); - - if (hid->claimed & HID_CLAIMED_INPUT) - hidinput_disconnect(hid); - if (hid->claimed & HID_CLAIMED_HIDDEV) - hiddev_disconnect(hid); - - usb_free_urb(usbhid->urbin); - usb_free_urb(usbhid->urbctrl); - usb_free_urb(usbhid->urbout); - - hid_free_buffers(hid_to_usb_dev(hid), hid); - hid_free_device(hid); -} - -static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct hid_device *hid; - char path[64]; - int i; - char *c; - - dbg("HID probe called for ifnum %d", - intf->altsetting->desc.bInterfaceNumber); - - if (!(hid = usb_hid_configure(intf))) - return -ENODEV; - - usbhid_init_reports(hid); - hid_dump_device(hid); - - if (!hidinput_connect(hid)) - hid->claimed |= HID_CLAIMED_INPUT; - if (!hiddev_connect(hid)) - hid->claimed |= HID_CLAIMED_HIDDEV; - - usb_set_intfdata(intf, hid); - - if (!hid->claimed) { - printk ("HID device not claimed by input or hiddev\n"); - hid_disconnect(intf); - return -ENODEV; - } - - if ((hid->claimed & HID_CLAIMED_INPUT)) - hid_ff_init(hid); - - if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) - hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), - intf->cur_altsetting->desc.bInterfaceNumber); - - printk(KERN_INFO); - - if (hid->claimed & HID_CLAIMED_INPUT) - printk("input"); - if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV)) - printk(","); - if (hid->claimed & HID_CLAIMED_HIDDEV) - printk("hiddev%d", hid->minor); - - c = "Device"; - for (i = 0; i < hid->maxcollection; i++) { - if (hid->collection[i].type == HID_COLLECTION_APPLICATION && - (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK && - (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) { - c = hid_types[hid->collection[i].usage & 0xffff]; - break; - } - } - - usb_make_path(interface_to_usbdev(intf), path, 63); - - printk(": USB HID v%x.%02x %s [%s] on %s\n", - hid->version >> 8, hid->version & 0xff, c, hid->name, path); - - return 0; -} - -static int hid_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid = hid->driver_data; - - spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ - set_bit(HID_SUSPENDED, &usbhid->iofl); - spin_unlock_irq(&usbhid->inlock); - del_timer(&usbhid->io_retry); - usb_kill_urb(usbhid->urbin); - dev_dbg(&intf->dev, "suspend\n"); - return 0; -} - -static int hid_resume(struct usb_interface *intf) -{ - struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid = hid->driver_data; - int status; - - clear_bit(HID_SUSPENDED, &usbhid->iofl); - usbhid->retry_delay = 0; - status = hid_start_in(hid); - dev_dbg(&intf->dev, "resume status %d\n", status); - return status; -} - -/* Treat USB reset pretty much the same as suspend/resume */ -static void hid_pre_reset(struct usb_interface *intf) -{ - /* FIXME: What if the interface is already suspended? */ - hid_suspend(intf, PMSG_ON); -} - -static void hid_post_reset(struct usb_interface *intf) -{ - struct usb_device *dev = interface_to_usbdev (intf); - - hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); - /* FIXME: Any more reinitialization needed? */ - - hid_resume(intf); -} - -static struct usb_device_id hid_usb_ids [] = { - { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, - .bInterfaceClass = USB_INTERFACE_CLASS_HID }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, hid_usb_ids); - -static struct usb_driver hid_driver = { - .name = "usbhid", - .probe = hid_probe, - .disconnect = hid_disconnect, - .suspend = hid_suspend, - .resume = hid_resume, - .pre_reset = hid_pre_reset, - .post_reset = hid_post_reset, - .id_table = hid_usb_ids, -}; - -static int __init hid_init(void) -{ - int retval; - retval = hiddev_init(); - if (retval) - goto hiddev_init_fail; - retval = usb_register(&hid_driver); - if (retval) - goto usb_register_fail; - info(DRIVER_VERSION ":" DRIVER_DESC); - - return 0; -usb_register_fail: - hiddev_exit(); -hiddev_init_fail: - return retval; -} - -static void __exit hid_exit(void) -{ - usb_deregister(&hid_driver); - hiddev_exit(); -} - -module_init(hid_init); -module_exit(hid_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c deleted file mode 100644 index e431faa..0000000 --- a/drivers/usb/input/hid-ff.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $ - * - * Force feedback support for hid devices. - * Not all hid devices use the same protocol. For example, some use PID, - * other use their own proprietary procotol. - * - * Copyright (c) 2002-2004 Johann Deneux - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include - -#undef DEBUG -#include - -#include -#include "usbhid.h" - -/* - * This table contains pointers to initializers. To add support for new - * devices, you need to add the USB vendor and product ids here. - */ -struct hid_ff_initializer { - u16 idVendor; - u16 idProduct; - int (*init)(struct hid_device*); -}; - -/* - * We try pidff when no other driver is found because PID is the - * standards compliant way of implementing force feedback in HID. - * pidff_init() will quickly abort if the device doesn't appear to - * be a PID device - */ -static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_LOGITECH_FF - { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ - { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ - { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ - { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ - { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ - { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ -#endif -#ifdef CONFIG_PANTHERLORD_FF - { 0x810, 0x0001, hid_plff_init }, -#endif -#ifdef CONFIG_THRUSTMASTER_FF - { 0x44f, 0xb304, hid_tmff_init }, -#endif -#ifdef CONFIG_ZEROPLUS_FF - { 0xc12, 0x0005, hid_zpff_init }, - { 0xc12, 0x0030, hid_zpff_init }, -#endif - { 0, 0, hid_pidff_init} /* Matches anything */ -}; - -int hid_ff_init(struct hid_device* hid) -{ - struct hid_ff_initializer *init; - int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor); - int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct); - - for (init = inits; init->idVendor; init++) - if (init->idVendor == vendor && init->idProduct == product) - break; - - return init->init(hid); -} -EXPORT_SYMBOL_GPL(hid_ff_init); - diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c deleted file mode 100644 index e6f3af3..0000000 --- a/drivers/usb/input/hid-lgff.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Force feedback support for hid-compliant for some of the devices from - * Logitech, namely: - * - WingMan Cordless RumblePad - * - WingMan Force 3D - * - * Copyright (c) 2002-2004 Johann Deneux - * Copyright (c) 2006 Anssi Hannula - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include -#include -#include -#include "usbhid.h" - -struct dev_type { - u16 idVendor; - u16 idProduct; - const signed short *ff; -}; - -static const signed short ff_rumble[] = { - FF_RUMBLE, - -1 -}; - -static const signed short ff_joystick[] = { - FF_CONSTANT, - -1 -}; - -static const struct dev_type devices[] = { - { 0x046d, 0xc211, ff_rumble }, - { 0x046d, 0xc219, ff_rumble }, - { 0x046d, 0xc283, ff_joystick }, - { 0x046d, 0xc294, ff_joystick }, - { 0x046d, 0xc295, ff_joystick }, - { 0x046d, 0xca03, ff_joystick }, -}; - -static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct hid_report *report = list_entry(report_list->next, struct hid_report, list); - int x, y; - unsigned int left, right; - -#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff - - switch (effect->type) { - case FF_CONSTANT: - x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */ - y = effect->u.ramp.end_level + 0x7f; - CLAMP(x); - CLAMP(y); - report->field[0]->value[0] = 0x51; - report->field[0]->value[1] = 0x08; - report->field[0]->value[2] = x; - report->field[0]->value[3] = y; - dbg("(x, y)=(%04x, %04x)", x, y); - usbhid_submit_report(hid, report, USB_DIR_OUT); - break; - - case FF_RUMBLE: - right = effect->u.rumble.strong_magnitude; - left = effect->u.rumble.weak_magnitude; - right = right * 0xff / 0xffff; - left = left * 0xff / 0xffff; - CLAMP(left); - CLAMP(right); - report->field[0]->value[0] = 0x42; - report->field[0]->value[1] = 0x00; - report->field[0]->value[2] = left; - report->field[0]->value[3] = right; - dbg("(left, right)=(%04x, %04x)", left, right); - usbhid_submit_report(hid, report, USB_DIR_OUT); - break; - } - return 0; -} - -int hid_lgff_init(struct hid_device* hid) -{ - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; - const signed short *ff_bits = ff_joystick; - int error; - int i; - - /* Find the report to use */ - if (list_empty(report_list)) { - err("No output report found"); - return -1; - } - - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - err("NULL output report"); - return -1; - } - - field = report->field[0]; - if (!field) { - err("NULL field"); - return -1; - } - - for (i = 0; i < ARRAY_SIZE(devices); i++) { - if (dev->id.vendor == devices[i].idVendor && - dev->id.product == devices[i].idProduct) { - ff_bits = devices[i].ff; - break; - } - } - - for (i = 0; ff_bits[i] >= 0; i++) - set_bit(ff_bits[i], dev->ffbit); - - error = input_ff_create_memless(dev, NULL, hid_lgff_play); - if (error) - return error; - - printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); - - return 0; -} diff --git a/drivers/usb/input/hid-pidff.c b/drivers/usb/input/hid-pidff.c deleted file mode 100644 index f5a90e9..0000000 --- a/drivers/usb/input/hid-pidff.c +++ /dev/null @@ -1,1331 +0,0 @@ -/* - * Force feedback driver for USB HID PID compliant devices - * - * Copyright (c) 2005, 2006 Anssi Hannula - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg) - -#include -#include - -#include - -#include "usbhid.h" - -#define PID_EFFECTS_MAX 64 - -/* Report usage table used to put reports into an array */ - -#define PID_SET_EFFECT 0 -#define PID_EFFECT_OPERATION 1 -#define PID_DEVICE_GAIN 2 -#define PID_POOL 3 -#define PID_BLOCK_LOAD 4 -#define PID_BLOCK_FREE 5 -#define PID_DEVICE_CONTROL 6 -#define PID_CREATE_NEW_EFFECT 7 - -#define PID_REQUIRED_REPORTS 7 - -#define PID_SET_ENVELOPE 8 -#define PID_SET_CONDITION 9 -#define PID_SET_PERIODIC 10 -#define PID_SET_CONSTANT 11 -#define PID_SET_RAMP 12 -static const u8 pidff_reports[] = { - 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab, - 0x5a, 0x5f, 0x6e, 0x73, 0x74 -}; - -/* device_control is really 0x95, but 0x96 specified as it is the usage of -the only field in that report */ - -/* Value usage tables used to put fields and values into arrays */ - -#define PID_EFFECT_BLOCK_INDEX 0 - -#define PID_DURATION 1 -#define PID_GAIN 2 -#define PID_TRIGGER_BUTTON 3 -#define PID_TRIGGER_REPEAT_INT 4 -#define PID_DIRECTION_ENABLE 5 -#define PID_START_DELAY 6 -static const u8 pidff_set_effect[] = { - 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7 -}; - -#define PID_ATTACK_LEVEL 1 -#define PID_ATTACK_TIME 2 -#define PID_FADE_LEVEL 3 -#define PID_FADE_TIME 4 -static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e }; - -#define PID_PARAM_BLOCK_OFFSET 1 -#define PID_CP_OFFSET 2 -#define PID_POS_COEFFICIENT 3 -#define PID_NEG_COEFFICIENT 4 -#define PID_POS_SATURATION 5 -#define PID_NEG_SATURATION 6 -#define PID_DEAD_BAND 7 -static const u8 pidff_set_condition[] = { - 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65 -}; - -#define PID_MAGNITUDE 1 -#define PID_OFFSET 2 -#define PID_PHASE 3 -#define PID_PERIOD 4 -static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 }; -static const u8 pidff_set_constant[] = { 0x22, 0x70 }; - -#define PID_RAMP_START 1 -#define PID_RAMP_END 2 -static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 }; - -#define PID_RAM_POOL_AVAILABLE 1 -static const u8 pidff_block_load[] = { 0x22, 0xac }; - -#define PID_LOOP_COUNT 1 -static const u8 pidff_effect_operation[] = { 0x22, 0x7c }; - -static const u8 pidff_block_free[] = { 0x22 }; - -#define PID_DEVICE_GAIN_FIELD 0 -static const u8 pidff_device_gain[] = { 0x7e }; - -#define PID_RAM_POOL_SIZE 0 -#define PID_SIMULTANEOUS_MAX 1 -#define PID_DEVICE_MANAGED_POOL 2 -static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 }; - -/* Special field key tables used to put special field keys into arrays */ - -#define PID_ENABLE_ACTUATORS 0 -#define PID_RESET 1 -static const u8 pidff_device_control[] = { 0x97, 0x9a }; - -#define PID_CONSTANT 0 -#define PID_RAMP 1 -#define PID_SQUARE 2 -#define PID_SINE 3 -#define PID_TRIANGLE 4 -#define PID_SAW_UP 5 -#define PID_SAW_DOWN 6 -#define PID_SPRING 7 -#define PID_DAMPER 8 -#define PID_INERTIA 9 -#define PID_FRICTION 10 -static const u8 pidff_effect_types[] = { - 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, - 0x40, 0x41, 0x42, 0x43 -}; - -#define PID_BLOCK_LOAD_SUCCESS 0 -#define PID_BLOCK_LOAD_FULL 1 -static const u8 pidff_block_load_status[] = { 0x8c, 0x8d }; - -#define PID_EFFECT_START 0 -#define PID_EFFECT_STOP 1 -static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b }; - -struct pidff_usage { - struct hid_field *field; - s32 *value; -}; - -struct pidff_device { - struct hid_device *hid; - - struct hid_report *reports[sizeof(pidff_reports)]; - - struct pidff_usage set_effect[sizeof(pidff_set_effect)]; - struct pidff_usage set_envelope[sizeof(pidff_set_envelope)]; - struct pidff_usage set_condition[sizeof(pidff_set_condition)]; - struct pidff_usage set_periodic[sizeof(pidff_set_periodic)]; - struct pidff_usage set_constant[sizeof(pidff_set_constant)]; - struct pidff_usage set_ramp[sizeof(pidff_set_ramp)]; - - struct pidff_usage device_gain[sizeof(pidff_device_gain)]; - struct pidff_usage block_load[sizeof(pidff_block_load)]; - struct pidff_usage pool[sizeof(pidff_pool)]; - struct pidff_usage effect_operation[sizeof(pidff_effect_operation)]; - struct pidff_usage block_free[sizeof(pidff_block_free)]; - - /* Special field is a field that is not composed of - usage<->value pairs that pidff_usage values are */ - - /* Special field in create_new_effect */ - struct hid_field *create_new_effect_type; - - /* Special fields in set_effect */ - struct hid_field *set_effect_type; - struct hid_field *effect_direction; - - /* Special field in device_control */ - struct hid_field *device_control; - - /* Special field in block_load */ - struct hid_field *block_load_status; - - /* Special field in effect_operation */ - struct hid_field *effect_operation_status; - - int control_id[sizeof(pidff_device_control)]; - int type_id[sizeof(pidff_effect_types)]; - int status_id[sizeof(pidff_block_load_status)]; - int operation_id[sizeof(pidff_effect_operation_status)]; - - int pid_id[PID_EFFECTS_MAX]; -}; - -/* - * Scale an unsigned value with range 0..max for the given field - */ -static int pidff_rescale(int i, int max, struct hid_field *field) -{ - return i * (field->logical_maximum - field->logical_minimum) / max + - field->logical_minimum; -} - -/* - * Scale a signed value in range -0x8000..0x7fff for the given field - */ -static int pidff_rescale_signed(int i, struct hid_field *field) -{ - return i == 0 ? 0 : i > - 0 ? i * field->logical_maximum / 0x7fff : i * - field->logical_minimum / -0x8000; -} - -static void pidff_set(struct pidff_usage *usage, u16 value) -{ - usage->value[0] = pidff_rescale(value, 0xffff, usage->field); - debug("calculated from %d to %d", value, usage->value[0]); -} - -static void pidff_set_signed(struct pidff_usage *usage, s16 value) -{ - if (usage->field->logical_minimum < 0) - usage->value[0] = pidff_rescale_signed(value, usage->field); - else { - if (value < 0) - usage->value[0] = - pidff_rescale(-value, 0x8000, usage->field); - else - usage->value[0] = - pidff_rescale(value, 0x7fff, usage->field); - } - debug("calculated from %d to %d", value, usage->value[0]); -} - -/* - * Send envelope report to the device - */ -static void pidff_set_envelope_report(struct pidff_device *pidff, - struct ff_envelope *envelope) -{ - pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - - pidff->set_envelope[PID_ATTACK_LEVEL].value[0] = - pidff_rescale(envelope->attack_level > - 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff, - pidff->set_envelope[PID_ATTACK_LEVEL].field); - pidff->set_envelope[PID_FADE_LEVEL].value[0] = - pidff_rescale(envelope->fade_level > - 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff, - pidff->set_envelope[PID_FADE_LEVEL].field); - - pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; - pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; - - debug("attack %u => %d", envelope->attack_level, - pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], - USB_DIR_OUT); -} - -/* - * Test if the new envelope differs from old one - */ -static int pidff_needs_set_envelope(struct ff_envelope *envelope, - struct ff_envelope *old) -{ - return envelope->attack_level != old->attack_level || - envelope->fade_level != old->fade_level || - envelope->attack_length != old->attack_length || - envelope->fade_length != old->fade_length; -} - -/* - * Send constant force report to the device - */ -static void pidff_set_constant_force_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], - effect->u.constant.level); - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], - USB_DIR_OUT); -} - -/* - * Test if the constant parameters have changed between effects - */ -static int pidff_needs_set_constant(struct ff_effect *effect, - struct ff_effect *old) -{ - return effect->u.constant.level != old->u.constant.level; -} - -/* - * Send set effect report to the device - */ -static void pidff_set_effect_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff->set_effect_type->value[0] = - pidff->create_new_effect_type->value[0]; - pidff->set_effect[PID_DURATION].value[0] = effect->replay.length; - pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button; - pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = - effect->trigger.interval; - pidff->set_effect[PID_GAIN].value[0] = - pidff->set_effect[PID_GAIN].field->logical_maximum; - pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; - pidff->effect_direction->value[0] = - pidff_rescale(effect->direction, 0xffff, - pidff->effect_direction); - pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); -} - -/* - * Test if the values used in set_effect have changed - */ -static int pidff_needs_set_effect(struct ff_effect *effect, - struct ff_effect *old) -{ - return effect->replay.length != old->replay.length || - effect->trigger.interval != old->trigger.interval || - effect->trigger.button != old->trigger.button || - effect->direction != old->direction || - effect->replay.delay != old->replay.delay; -} - -/* - * Send periodic effect report to the device - */ -static void pidff_set_periodic_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE], - effect->u.periodic.magnitude); - pidff_set_signed(&pidff->set_periodic[PID_OFFSET], - effect->u.periodic.offset); - pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); - pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], - USB_DIR_OUT); - -} - -/* - * Test if periodic effect parameters have changed - */ -static int pidff_needs_set_periodic(struct ff_effect *effect, - struct ff_effect *old) -{ - return effect->u.periodic.magnitude != old->u.periodic.magnitude || - effect->u.periodic.offset != old->u.periodic.offset || - effect->u.periodic.phase != old->u.periodic.phase || - effect->u.periodic.period != old->u.periodic.period; -} - -/* - * Send condition effect reports to the device - */ -static void pidff_set_condition_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - int i; - - pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - - for (i = 0; i < 2; i++) { - pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i; - pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET], - effect->u.condition[i].center); - pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], - effect->u.condition[i].right_coeff); - pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], - effect->u.condition[i].left_coeff); - pidff_set(&pidff->set_condition[PID_POS_SATURATION], - effect->u.condition[i].right_saturation); - pidff_set(&pidff->set_condition[PID_NEG_SATURATION], - effect->u.condition[i].left_saturation); - pidff_set(&pidff->set_condition[PID_DEAD_BAND], - effect->u.condition[i].deadband); - usbhid_wait_io(pidff->hid); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], - USB_DIR_OUT); - } -} - -/* - * Test if condition effect parameters have changed - */ -static int pidff_needs_set_condition(struct ff_effect *effect, - struct ff_effect *old) -{ - int i; - int ret = 0; - - for (i = 0; i < 2; i++) { - struct ff_condition_effect *cond = &effect->u.condition[i]; - struct ff_condition_effect *old_cond = &old->u.condition[i]; - - ret |= cond->center != old_cond->center || - cond->right_coeff != old_cond->right_coeff || - cond->left_coeff != old_cond->left_coeff || - cond->right_saturation != old_cond->right_saturation || - cond->left_saturation != old_cond->left_saturation || - cond->deadband != old_cond->deadband; - } - - return ret; -} - -/* - * Send ramp force report to the device - */ -static void pidff_set_ramp_force_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff_set_signed(&pidff->set_ramp[PID_RAMP_START], - effect->u.ramp.start_level); - pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], - effect->u.ramp.end_level); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], - USB_DIR_OUT); -} - -/* - * Test if ramp force parameters have changed - */ -static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old) -{ - return effect->u.ramp.start_level != old->u.ramp.start_level || - effect->u.ramp.end_level != old->u.ramp.end_level; -} - -/* - * Send a request for effect upload to the device - * - * Returns 0 if device reported success, -ENOSPC if the device reported memory - * is full. Upon unknown response the function will retry for 60 times, if - * still unsuccessful -EIO is returned. - */ -static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) -{ - int j; - - pidff->create_new_effect_type->value[0] = efnum; - usbhid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], - USB_DIR_OUT); - debug("create_new_effect sent, type: %d", efnum); - - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; - pidff->block_load_status->value[0] = 0; - usbhid_wait_io(pidff->hid); - - for (j = 0; j < 60; j++) { - debug("pid_block_load requested"); - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD], - USB_DIR_IN); - usbhid_wait_io(pidff->hid); - if (pidff->block_load_status->value[0] == - pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { - debug("device reported free memory: %d bytes", - pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? - pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); - return 0; - } - if (pidff->block_load_status->value[0] == - pidff->status_id[PID_BLOCK_LOAD_FULL]) { - debug("not enough memory free: %d bytes", - pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? - pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); - return -ENOSPC; - } - } - printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n"); - return -EIO; -} - -/* - * Play the effect with PID id n times - */ -static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) -{ - pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; - - if (n == 0) { - pidff->effect_operation_status->value[0] = - pidff->operation_id[PID_EFFECT_STOP]; - } else { - pidff->effect_operation_status->value[0] = - pidff->operation_id[PID_EFFECT_START]; - pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; - } - - usbhid_wait_io(pidff->hid); - usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], - USB_DIR_OUT); -} - -/** - * Play the effect with effect id @effect_id for @value times - */ -static int pidff_playback(struct input_dev *dev, int effect_id, int value) -{ - struct pidff_device *pidff = dev->ff->private; - - pidff_playback_pid(pidff, pidff->pid_id[effect_id], value); - - return 0; -} - -/* - * Erase effect with PID id - */ -static void pidff_erase_pid(struct pidff_device *pidff, int pid_id) -{ - pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE], - USB_DIR_OUT); -} - -/* - * Stop and erase effect with effect_id - */ -static int pidff_erase_effect(struct input_dev *dev, int effect_id) -{ - struct pidff_device *pidff = dev->ff->private; - int pid_id = pidff->pid_id[effect_id]; - - debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); - pidff_playback_pid(pidff, pid_id, 0); - pidff_erase_pid(pidff, pid_id); - - return 0; -} - -/* - * Effect upload handler - */ -static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, - struct ff_effect *old) -{ - struct pidff_device *pidff = dev->ff->private; - int type_id; - int error; - - switch (effect->type) { - case FF_CONSTANT: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_CONSTANT]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_constant(effect, old)) - pidff_set_constant_force_report(pidff, effect); - if (!old || - pidff_needs_set_envelope(&effect->u.constant.envelope, - &old->u.constant.envelope)) - pidff_set_envelope_report(pidff, - &effect->u.constant.envelope); - break; - - case FF_PERIODIC: - if (!old) { - switch (effect->u.periodic.waveform) { - case FF_SQUARE: - type_id = PID_SQUARE; - break; - case FF_TRIANGLE: - type_id = PID_TRIANGLE; - break; - case FF_SINE: - type_id = PID_SINE; - break; - case FF_SAW_UP: - type_id = PID_SAW_UP; - break; - case FF_SAW_DOWN: - type_id = PID_SAW_DOWN; - break; - default: - printk(KERN_ERR - "hid-pidff: invalid waveform\n"); - return -EINVAL; - } - - error = pidff_request_effect_upload(pidff, - pidff->type_id[type_id]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_periodic(effect, old)) - pidff_set_periodic_report(pidff, effect); - if (!old || - pidff_needs_set_envelope(&effect->u.periodic.envelope, - &old->u.periodic.envelope)) - pidff_set_envelope_report(pidff, - &effect->u.periodic.envelope); - break; - - case FF_RAMP: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_RAMP]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_ramp(effect, old)) - pidff_set_ramp_force_report(pidff, effect); - if (!old || - pidff_needs_set_envelope(&effect->u.ramp.envelope, - &old->u.ramp.envelope)) - pidff_set_envelope_report(pidff, - &effect->u.ramp.envelope); - break; - - case FF_SPRING: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_SPRING]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - case FF_FRICTION: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_FRICTION]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - case FF_DAMPER: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_DAMPER]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - case FF_INERTIA: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_INERTIA]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - default: - printk(KERN_ERR "hid-pidff: invalid type\n"); - return -EINVAL; - } - - if (!old) - pidff->pid_id[effect->id] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - - debug("uploaded"); - - return 0; -} - -/* - * set_gain() handler - */ -static void pidff_set_gain(struct input_dev *dev, u16 gain) -{ - struct pidff_device *pidff = dev->ff->private; - - pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); - usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); -} - -static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) -{ - struct hid_field *field = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field; - - if (!magnitude) { - pidff_playback_pid(pidff, field->logical_minimum, 0); - return; - } - - pidff_playback_pid(pidff, field->logical_minimum, 1); - - pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum; - pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING]; - pidff->set_effect[PID_DURATION].value[0] = 0; - pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0; - pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; - pidff_set(&pidff->set_effect[PID_GAIN], magnitude); - pidff->set_effect[PID_START_DELAY].value[0] = 0; - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); -} - -/* - * pidff_set_autocenter() handler - */ -static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude) -{ - struct pidff_device *pidff = dev->ff->private; - - pidff_autocenter(pidff, magnitude); -} - -/* - * Find fields from a report and fill a pidff_usage - */ -static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, - struct hid_report *report, int count, int strict) -{ - int i, j, k, found; - - for (k = 0; k < count; k++) { - found = 0; - for (i = 0; i < report->maxfield; i++) { - if (report->field[i]->maxusage != - report->field[i]->report_count) { - debug("maxusage and report_count do not match, " - "skipping"); - continue; - } - for (j = 0; j < report->field[i]->maxusage; j++) { - if (report->field[i]->usage[j].hid == - (HID_UP_PID | table[k])) { - debug("found %d at %d->%d", k, i, j); - usage[k].field = report->field[i]; - usage[k].value = - &report->field[i]->value[j]; - found = 1; - break; - } - } - if (found) - break; - } - if (!found && strict) { - debug("failed to locate %d", k); - return -1; - } - } - return 0; -} - -/* - * Return index into pidff_reports for the given usage - */ -static int pidff_check_usage(int usage) -{ - int i; - - for (i = 0; i < sizeof(pidff_reports); i++) - if (usage == (HID_UP_PID | pidff_reports[i])) - return i; - - return -1; -} - -/* - * Find the reports and fill pidff->reports[] - * report_type specifies either OUTPUT or FEATURE reports - */ -static void pidff_find_reports(struct hid_device *hid, int report_type, - struct pidff_device *pidff) -{ - struct hid_report *report; - int i, ret; - - list_for_each_entry(report, - &hid->report_enum[report_type].report_list, list) { - if (report->maxfield < 1) - continue; - ret = pidff_check_usage(report->field[0]->logical); - if (ret != -1) { - debug("found usage 0x%02x from field->logical", - pidff_reports[ret]); - pidff->reports[ret] = report; - continue; - } - - /* - * Sometimes logical collections are stacked to indicate - * different usages for the report and the field, in which - * case we want the usage of the parent. However, Linux HID - * implementation hides this fact, so we have to dig it up - * ourselves - */ - i = report->field[0]->usage[0].collection_index; - if (i <= 0 || - hid->collection[i - 1].type != HID_COLLECTION_LOGICAL) - continue; - ret = pidff_check_usage(hid->collection[i - 1].usage); - if (ret != -1 && !pidff->reports[ret]) { - debug("found usage 0x%02x from collection array", - pidff_reports[ret]); - pidff->reports[ret] = report; - } - } -} - -/* - * Test if the required reports have been found - */ -static int pidff_reports_ok(struct pidff_device *pidff) -{ - int i; - - for (i = 0; i <= PID_REQUIRED_REPORTS; i++) { - if (!pidff->reports[i]) { - debug("%d missing", i); - return 0; - } - } - - return 1; -} - -/* - * Find a field with a specific usage within a report - */ -static struct hid_field *pidff_find_special_field(struct hid_report *report, - int usage, int enforce_min) -{ - int i; - - for (i = 0; i < report->maxfield; i++) { - if (report->field[i]->logical == (HID_UP_PID | usage) && - report->field[i]->report_count > 0) { - if (!enforce_min || - report->field[i]->logical_minimum == 1) - return report->field[i]; - else { - printk(KERN_ERR "hid-pidff: logical_minimum " - "is not 1 as it should be\n"); - return NULL; - } - } - } - return NULL; -} - -/* - * Fill a pidff->*_id struct table - */ -static int pidff_find_special_keys(int *keys, struct hid_field *fld, - const u8 *usagetable, int count) -{ - - int i, j; - int found = 0; - - for (i = 0; i < count; i++) { - for (j = 0; j < fld->maxusage; j++) { - if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) { - keys[i] = j + 1; - found++; - break; - } - } - } - return found; -} - -#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \ - pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \ - sizeof(pidff_ ## name)) - -/* - * Find and check the special fields - */ -static int pidff_find_special_fields(struct pidff_device *pidff) -{ - debug("finding special fields"); - - pidff->create_new_effect_type = - pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT], - 0x25, 1); - pidff->set_effect_type = - pidff_find_special_field(pidff->reports[PID_SET_EFFECT], - 0x25, 1); - pidff->effect_direction = - pidff_find_special_field(pidff->reports[PID_SET_EFFECT], - 0x57, 0); - pidff->device_control = - pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL], - 0x96, 1); - pidff->block_load_status = - pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD], - 0x8b, 1); - pidff->effect_operation_status = - pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION], - 0x78, 1); - - debug("search done"); - - if (!pidff->create_new_effect_type || !pidff->set_effect_type) { - printk(KERN_ERR "hid-pidff: effect lists not found\n"); - return -1; - } - - if (!pidff->effect_direction) { - printk(KERN_ERR "hid-pidff: direction field not found\n"); - return -1; - } - - if (!pidff->device_control) { - printk(KERN_ERR "hid-pidff: device control field not found\n"); - return -1; - } - - if (!pidff->block_load_status) { - printk(KERN_ERR - "hid-pidff: block load status field not found\n"); - return -1; - } - - if (!pidff->effect_operation_status) { - printk(KERN_ERR - "hid-pidff: effect operation field not found\n"); - return -1; - } - - pidff_find_special_keys(pidff->control_id, pidff->device_control, - pidff_device_control, - sizeof(pidff_device_control)); - - PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control); - - if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type, - effect_types)) { - printk(KERN_ERR "hid-pidff: no effect types found\n"); - return -1; - } - - if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status, - block_load_status) != - sizeof(pidff_block_load_status)) { - printk(KERN_ERR - "hidpidff: block load status identifiers not found\n"); - return -1; - } - - if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status, - effect_operation_status) != - sizeof(pidff_effect_operation_status)) { - printk(KERN_ERR - "hidpidff: effect operation identifiers not found\n"); - return -1; - } - - return 0; -} - -/** - * Find the implemented effect types - */ -static int pidff_find_effects(struct pidff_device *pidff, - struct input_dev *dev) -{ - int i; - - for (i = 0; i < sizeof(pidff_effect_types); i++) { - int pidff_type = pidff->type_id[i]; - if (pidff->set_effect_type->usage[pidff_type].hid != - pidff->create_new_effect_type->usage[pidff_type].hid) { - printk(KERN_ERR "hid-pidff: " - "effect type number %d is invalid\n", i); - return -1; - } - } - - if (pidff->type_id[PID_CONSTANT]) - set_bit(FF_CONSTANT, dev->ffbit); - if (pidff->type_id[PID_RAMP]) - set_bit(FF_RAMP, dev->ffbit); - if (pidff->type_id[PID_SQUARE]) { - set_bit(FF_SQUARE, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SINE]) { - set_bit(FF_SINE, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_TRIANGLE]) { - set_bit(FF_TRIANGLE, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SAW_UP]) { - set_bit(FF_SAW_UP, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SAW_DOWN]) { - set_bit(FF_SAW_DOWN, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SPRING]) - set_bit(FF_SPRING, dev->ffbit); - if (pidff->type_id[PID_DAMPER]) - set_bit(FF_DAMPER, dev->ffbit); - if (pidff->type_id[PID_INERTIA]) - set_bit(FF_INERTIA, dev->ffbit); - if (pidff->type_id[PID_FRICTION]) - set_bit(FF_FRICTION, dev->ffbit); - - return 0; - -} - -#define PIDFF_FIND_FIELDS(name, report, strict) \ - pidff_find_fields(pidff->name, pidff_ ## name, \ - pidff->reports[report], \ - sizeof(pidff_ ## name), strict) - -/* - * Fill and check the pidff_usages - */ -static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev) -{ - int envelope_ok = 0; - - if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) { - printk(KERN_ERR - "hid-pidff: unknown set_effect report layout\n"); - return -ENODEV; - } - - PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0); - if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) { - printk(KERN_ERR - "hid-pidff: unknown pid_block_load report layout\n"); - return -ENODEV; - } - - if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) { - printk(KERN_ERR - "hid-pidff: unknown effect_operation report layout\n"); - return -ENODEV; - } - - if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) { - printk(KERN_ERR - "hid-pidff: unknown pid_block_free report layout\n"); - return -ENODEV; - } - - if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1)) - envelope_ok = 1; - - if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev)) - return -ENODEV; - - if (!envelope_ok) { - if (test_and_clear_bit(FF_CONSTANT, dev->ffbit)) - printk(KERN_WARNING "hid-pidff: " - "has constant effect but no envelope\n"); - if (test_and_clear_bit(FF_RAMP, dev->ffbit)) - printk(KERN_WARNING "hid-pidff: " - "has ramp effect but no envelope\n"); - - if (test_and_clear_bit(FF_PERIODIC, dev->ffbit)) - printk(KERN_WARNING "hid-pidff: " - "has periodic effect but no envelope\n"); - } - - if (test_bit(FF_CONSTANT, dev->ffbit) && - PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) { - printk(KERN_WARNING - "hid-pidff: unknown constant effect layout\n"); - clear_bit(FF_CONSTANT, dev->ffbit); - } - - if (test_bit(FF_RAMP, dev->ffbit) && - PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) { - printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n"); - clear_bit(FF_RAMP, dev->ffbit); - } - - if ((test_bit(FF_SPRING, dev->ffbit) || - test_bit(FF_DAMPER, dev->ffbit) || - test_bit(FF_FRICTION, dev->ffbit) || - test_bit(FF_INERTIA, dev->ffbit)) && - PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) { - printk(KERN_WARNING - "hid-pidff: unknown condition effect layout\n"); - clear_bit(FF_SPRING, dev->ffbit); - clear_bit(FF_DAMPER, dev->ffbit); - clear_bit(FF_FRICTION, dev->ffbit); - clear_bit(FF_INERTIA, dev->ffbit); - } - - if (test_bit(FF_PERIODIC, dev->ffbit) && - PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) { - printk(KERN_WARNING - "hid-pidff: unknown periodic effect layout\n"); - clear_bit(FF_PERIODIC, dev->ffbit); - } - - PIDFF_FIND_FIELDS(pool, PID_POOL, 0); - - if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1)) - set_bit(FF_GAIN, dev->ffbit); - - return 0; -} - -/* - * Reset the device - */ -static void pidff_reset(struct pidff_device *pidff) -{ - struct hid_device *hid = pidff->hid; - int i = 0; - - pidff->device_control->value[0] = pidff->control_id[PID_RESET]; - /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); - - pidff->device_control->value[0] = - pidff->control_id[PID_ENABLE_ACTUATORS]; - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); - - /* pool report is sometimes messed up, refetch it */ - usbhid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN); - usbhid_wait_io(hid); - - if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { - int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0]; - while (sim_effects < 2) { - if (i++ > 20) { - printk(KERN_WARNING "hid-pidff: device reports " - "%d simultaneous effects\n", - sim_effects); - break; - } - debug("pid_pool requested again"); - usbhid_submit_report(hid, pidff->reports[PID_POOL], - USB_DIR_IN); - usbhid_wait_io(hid); - } - } -} - -/* - * Test if autocenter modification is using the supported method - */ -static int pidff_check_autocenter(struct pidff_device *pidff, - struct input_dev *dev) -{ - int error; - - /* - * Let's find out if autocenter modification is supported - * Specification doesn't specify anything, so we request an - * effect upload and cancel it immediately. If the approved - * effect id was one above the minimum, then we assume the first - * effect id is a built-in spring type effect used for autocenter - */ - - error = pidff_request_effect_upload(pidff, 1); - if (error) { - printk(KERN_ERR "hid-pidff: upload request failed\n"); - return error; - } - - if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] == - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) { - pidff_autocenter(pidff, 0xffff); - set_bit(FF_AUTOCENTER, dev->ffbit); - } else { - printk(KERN_NOTICE "hid-pidff: " - "device has unknown autocenter control method\n"); - } - - pidff_erase_pid(pidff, - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]); - - return 0; - -} - -/* - * Check if the device is PID and initialize it - */ -int hid_pidff_init(struct hid_device *hid) -{ - struct pidff_device *pidff; - struct hid_input *hidinput = list_entry(hid->inputs.next, - struct hid_input, list); - struct input_dev *dev = hidinput->input; - struct ff_device *ff; - int max_effects; - int error; - - debug("starting pid init"); - - if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { - debug("not a PID device, no output report"); - return -ENODEV; - } - - pidff = kzalloc(sizeof(*pidff), GFP_KERNEL); - if (!pidff) - return -ENOMEM; - - pidff->hid = hid; - - pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff); - pidff_find_reports(hid, HID_FEATURE_REPORT, pidff); - - if (!pidff_reports_ok(pidff)) { - debug("reports not ok, aborting"); - error = -ENODEV; - goto fail; - } - - error = pidff_init_fields(pidff, dev); - if (error) - goto fail; - - pidff_reset(pidff); - - if (test_bit(FF_GAIN, dev->ffbit)) { - pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); - usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); - } - - error = pidff_check_autocenter(pidff, dev); - if (error) - goto fail; - - max_effects = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum - - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + - 1; - debug("max effects is %d", max_effects); - - if (max_effects > PID_EFFECTS_MAX) - max_effects = PID_EFFECTS_MAX; - - if (pidff->pool[PID_SIMULTANEOUS_MAX].value) - debug("max simultaneous effects is %d", - pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); - - if (pidff->pool[PID_RAM_POOL_SIZE].value) - debug("device memory size is %d bytes", - pidff->pool[PID_RAM_POOL_SIZE].value[0]); - - if (pidff->pool[PID_DEVICE_MANAGED_POOL].value && - pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) { - printk(KERN_NOTICE "hid-pidff: " - "device does not support device managed pool\n"); - goto fail; - } - - error = input_ff_create(dev, max_effects); - if (error) - goto fail; - - ff = dev->ff; - ff->private = pidff; - ff->upload = pidff_upload_effect; - ff->erase = pidff_erase_effect; - ff->set_gain = pidff_set_gain; - ff->set_autocenter = pidff_set_autocenter; - ff->playback = pidff_playback; - - printk(KERN_INFO "Force feedback for USB HID PID devices by " - "Anssi Hannula \n"); - - return 0; - - fail: - kfree(pidff); - return error; -} diff --git a/drivers/usb/input/hid-plff.c b/drivers/usb/input/hid-plff.c deleted file mode 100644 index 76d2e6e..0000000 --- a/drivers/usb/input/hid-plff.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Force feedback support for PantherLord USB/PS2 2in1 Adapter devices - * - * Copyright (c) 2007 Anssi Hannula - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) - -#include -#include -#include -#include "usbhid.h" - -struct plff_device { - struct hid_report *report; -}; - -static int hid_plff_play(struct input_dev *dev, void *data, - struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct plff_device *plff = data; - int left, right; - - left = effect->u.rumble.strong_magnitude; - right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); - - left = left * 0x7f / 0xffff; - right = right * 0x7f / 0xffff; - - plff->report->field[0]->value[2] = left; - plff->report->field[0]->value[3] = right; - debug("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); - - return 0; -} - -int hid_plff_init(struct hid_device *hid) -{ - struct plff_device *plff; - struct hid_report *report; - struct hid_input *hidinput; - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct list_head *report_ptr = report_list; - struct input_dev *dev; - int error; - - /* The device contains 2 output reports (one for each - HID_QUIRK_MULTI_INPUT device), both containing 1 field, which - contains 4 ff00.0002 usages and 4 16bit absolute values. - - The 2 input reports also contain a field which contains - 8 ff00.0001 usages and 8 boolean values. Their meaning is - currently unknown. */ - - if (list_empty(report_list)) { - printk(KERN_ERR "hid-plff: no output reports found\n"); - return -ENODEV; - } - - list_for_each_entry(hidinput, &hid->inputs, list) { - - report_ptr = report_ptr->next; - - if (report_ptr == report_list) { - printk(KERN_ERR "hid-plff: required output report is missing\n"); - return -ENODEV; - } - - report = list_entry(report_ptr, struct hid_report, list); - if (report->maxfield < 1) { - printk(KERN_ERR "hid-plff: no fields in the report\n"); - return -ENODEV; - } - - if (report->field[0]->report_count < 4) { - printk(KERN_ERR "hid-plff: not enough values in the field\n"); - return -ENODEV; - } - - plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); - if (!plff) - return -ENOMEM; - - dev = hidinput->input; - - set_bit(FF_RUMBLE, dev->ffbit); - - error = input_ff_create_memless(dev, plff, hid_plff_play); - if (error) { - kfree(plff); - return error; - } - - plff->report = report; - plff->report->field[0]->value[0] = 0x00; - plff->report->field[0]->value[1] = 0x00; - plff->report->field[0]->value[2] = 0x00; - plff->report->field[0]->value[3] = 0x00; - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); - } - - printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 " - "2in1 Adapters by Anssi Hannula \n"); - - return 0; -} diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c deleted file mode 100644 index ab67331..0000000 --- a/drivers/usb/input/hid-tmff.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Force feedback support for various HID compliant devices by ThrustMaster: - * ThrustMaster FireStorm Dual Power 2 - * and possibly others whose device ids haven't been added. - * - * Modified to support ThrustMaster devices by Zinx Verituse - * on 2003-01-25 from the Logitech force feedback driver, - * which is by Johann Deneux. - * - * Copyright (c) 2003 Zinx Verituse - * Copyright (c) 2002 Johann Deneux - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#undef DEBUG -#include - -#include -#include "usbhid.h" - -/* Usages for thrustmaster devices I know about */ -#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) - - -struct tmff_device { - struct hid_report *report; - struct hid_field *rumble; -}; - -/* Changes values from 0 to 0xffff into values from minimum to maximum */ -static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) -{ - int ret; - - ret = (in * (maximum - minimum) / 0xffff) + minimum; - if (ret < minimum) - return minimum; - if (ret > maximum) - return maximum; - return ret; -} - -static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct tmff_device *tmff = data; - int left, right; /* Rumbling */ - - left = hid_tmff_scale(effect->u.rumble.weak_magnitude, - tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - right = hid_tmff_scale(effect->u.rumble.strong_magnitude, - tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - - tmff->rumble->value[0] = left; - tmff->rumble->value[1] = right; - dbg("(left,right)=(%08x, %08x)", left, right); - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); - - return 0; -} - -int hid_tmff_init(struct hid_device *hid) -{ - struct tmff_device *tmff; - struct list_head *pos; - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - int error; - - tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); - if (!tmff) - return -ENOMEM; - - /* Find the report to use */ - __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { - struct hid_report *report = (struct hid_report *)pos; - int fieldnum; - - for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { - struct hid_field *field = report->field[fieldnum]; - - if (field->maxusage <= 0) - continue; - - switch (field->usage[0].hid) { - case THRUSTMASTER_USAGE_RUMBLE_LR: - if (field->report_count < 2) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2"); - continue; - } - - if (field->logical_maximum == field->logical_minimum) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum"); - continue; - } - - if (tmff->report && tmff->report != report) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); - continue; - } - - if (tmff->rumble && tmff->rumble != field) { - warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); - continue; - } - - tmff->report = report; - tmff->rumble = field; - - set_bit(FF_RUMBLE, input_dev->ffbit); - break; - - default: - warn("ignoring unknown output usage %08x", field->usage[0].hid); - continue; - } - } - } - - error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); - if (error) { - kfree(tmff); - return error; - } - - info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse "); - - return 0; -} - diff --git a/drivers/usb/input/hid-zpff.c b/drivers/usb/input/hid-zpff.c deleted file mode 100644 index 7bd8238..0000000 --- a/drivers/usb/input/hid-zpff.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Force feedback support for Zeroplus based devices - * - * Copyright (c) 2005, 2006 Anssi Hannula - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg) - -#include -#include -#include -#include "usbhid.h" - -struct zpff_device { - struct hid_report *report; -}; - -static int hid_zpff_play(struct input_dev *dev, void *data, - struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct zpff_device *zpff = data; - int left, right; - - /* - * The following is specified the other way around in the Zeroplus - * datasheet but the order below is correct for the XFX Executioner; - * however it is possible that the XFX Executioner is an exception - */ - - left = effect->u.rumble.strong_magnitude; - right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); - - left = left * 0x7f / 0xffff; - right = right * 0x7f / 0xffff; - - zpff->report->field[2]->value[0] = left; - zpff->report->field[3]->value[0] = right; - debug("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); - - return 0; -} - -int hid_zpff_init(struct hid_device *hid) -{ - struct zpff_device *zpff; - struct hid_report *report; - struct hid_input *hidinput = list_entry(hid->inputs.next, - struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev = hidinput->input; - int error; - - if (list_empty(report_list)) { - printk(KERN_ERR "hid-zpff: no output report found\n"); - return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 4) { - printk(KERN_ERR "hid-zpff: not enough fields in report\n"); - return -ENODEV; - } - - zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); - if (!zpff) - return -ENOMEM; - - set_bit(FF_RUMBLE, dev->ffbit); - - error = input_ff_create_memless(dev, zpff, hid_zpff_play); - if (error) { - kfree(zpff); - return error; - } - - zpff->report = report; - zpff->report->field[0]->value[0] = 0x00; - zpff->report->field[1]->value[0] = 0x02; - zpff->report->field[2]->value[0] = 0x00; - zpff->report->field[3]->value[0] = 0x00; - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); - - printk(KERN_INFO "Force feedback for Zeroplus based devices by " - "Anssi Hannula \n"); - - return 0; -} diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c deleted file mode 100644 index a8b3d66..0000000 --- a/drivers/usb/input/hiddev.c +++ /dev/null @@ -1,847 +0,0 @@ -/* - * Copyright (c) 2001 Paul Stewart - * Copyright (c) 2001 Vojtech Pavlik - * - * HID char devices, giving access to raw HID device events. - * - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to Paul Stewart - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "usbhid.h" - -#ifdef CONFIG_USB_DYNAMIC_MINORS -#define HIDDEV_MINOR_BASE 0 -#define HIDDEV_MINORS 256 -#else -#define HIDDEV_MINOR_BASE 96 -#define HIDDEV_MINORS 16 -#endif -#define HIDDEV_BUFFER_SIZE 64 - -struct hiddev { - int exist; - int open; - wait_queue_head_t wait; - struct hid_device *hid; - struct list_head list; -}; - -struct hiddev_list { - struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE]; - int head; - int tail; - unsigned flags; - struct fasync_struct *fasync; - struct hiddev *hiddev; - struct list_head node; -}; - -static struct hiddev *hiddev_table[HIDDEV_MINORS]; - -/* - * Find a report, given the report's type and ID. The ID can be specified - * indirectly by REPORT_ID_FIRST (which returns the first report of the given - * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the - * given type which follows old_id. - */ -static struct hid_report * -hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) -{ - unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK; - unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK; - struct hid_report_enum *report_enum; - struct hid_report *report; - struct list_head *list; - - if (rinfo->report_type < HID_REPORT_TYPE_MIN || - rinfo->report_type > HID_REPORT_TYPE_MAX) - return NULL; - - report_enum = hid->report_enum + - (rinfo->report_type - HID_REPORT_TYPE_MIN); - - switch (flags) { - case 0: /* Nothing to do -- report_id is already set correctly */ - break; - - case HID_REPORT_ID_FIRST: - if (list_empty(&report_enum->report_list)) - return NULL; - - list = report_enum->report_list.next; - report = list_entry(list, struct hid_report, list); - rinfo->report_id = report->id; - break; - - case HID_REPORT_ID_NEXT: - report = report_enum->report_id_hash[rid]; - if (!report) - return NULL; - - list = report->list.next; - if (list == &report_enum->report_list) - return NULL; - - report = list_entry(list, struct hid_report, list); - rinfo->report_id = report->id; - break; - - default: - return NULL; - } - - return report_enum->report_id_hash[rinfo->report_id]; -} - -/* - * Perform an exhaustive search of the report table for a usage, given its - * type and usage id. - */ -static struct hid_field * -hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) -{ - int i, j; - struct hid_report *report; - struct hid_report_enum *report_enum; - struct hid_field *field; - - if (uref->report_type < HID_REPORT_TYPE_MIN || - uref->report_type > HID_REPORT_TYPE_MAX) - return NULL; - - report_enum = hid->report_enum + - (uref->report_type - HID_REPORT_TYPE_MIN); - - list_for_each_entry(report, &report_enum->report_list, list) { - for (i = 0; i < report->maxfield; i++) { - field = report->field[i]; - for (j = 0; j < field->maxusage; j++) { - if (field->usage[j].hid == uref->usage_code) { - uref->report_id = report->id; - uref->field_index = i; - uref->usage_index = j; - return field; - } - } - } - } - - return NULL; -} - -static void hiddev_send_event(struct hid_device *hid, - struct hiddev_usage_ref *uref) -{ - struct hiddev *hiddev = hid->hiddev; - struct hiddev_list *list; - - list_for_each_entry(list, &hiddev->list, node) { - if (uref->field_index != HID_FIELD_INDEX_NONE || - (list->flags & HIDDEV_FLAG_REPORT) != 0) { - list->buffer[list->head] = *uref; - list->head = (list->head + 1) & - (HIDDEV_BUFFER_SIZE - 1); - kill_fasync(&list->fasync, SIGIO, POLL_IN); - } - } - - wake_up_interruptible(&hiddev->wait); -} - -/* - * This is where hid.c calls into hiddev to pass an event that occurred over - * the interrupt pipe - */ -void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - unsigned type = field->report_type; - struct hiddev_usage_ref uref; - - uref.report_type = - (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); - uref.report_id = field->report->id; - uref.field_index = field->index; - uref.usage_index = (usage - field->usage); - uref.usage_code = usage->hid; - uref.value = value; - - hiddev_send_event(hid, &uref); -} -EXPORT_SYMBOL_GPL(hiddev_hid_event); - -void hiddev_report_event(struct hid_device *hid, struct hid_report *report) -{ - unsigned type = report->type; - struct hiddev_usage_ref uref; - - memset(&uref, 0, sizeof(uref)); - uref.report_type = - (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); - uref.report_id = report->id; - uref.field_index = HID_FIELD_INDEX_NONE; - - hiddev_send_event(hid, &uref); -} - -/* - * fasync file op - */ -static int hiddev_fasync(int fd, struct file *file, int on) -{ - int retval; - struct hiddev_list *list = file->private_data; - - retval = fasync_helper(fd, file, on, &list->fasync); - - return retval < 0 ? retval : 0; -} - - -/* - * release file op - */ -static int hiddev_release(struct inode * inode, struct file * file) -{ - struct hiddev_list *list = file->private_data; - - hiddev_fasync(-1, file, 0); - list_del(&list->node); - - if (!--list->hiddev->open) { - if (list->hiddev->exist) - usbhid_close(list->hiddev->hid); - else - kfree(list->hiddev); - } - - kfree(list); - - return 0; -} - -/* - * open file op - */ -static int hiddev_open(struct inode *inode, struct file *file) -{ - struct hiddev_list *list; - - int i = iminor(inode) - HIDDEV_MINOR_BASE; - - if (i >= HIDDEV_MINORS || !hiddev_table[i]) - return -ENODEV; - - if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) - return -ENOMEM; - - list->hiddev = hiddev_table[i]; - list_add_tail(&list->node, &hiddev_table[i]->list); - file->private_data = list; - - if (!list->hiddev->open++) - if (list->hiddev->exist) - usbhid_open(hiddev_table[i]->hid); - - return 0; -} - -/* - * "write" file op - */ -static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -/* - * "read" file op - */ -static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - struct hiddev_list *list = file->private_data; - int event_size; - int retval = 0; - - event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? - sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); - - if (count < event_size) - return 0; - - while (retval == 0) { - if (list->head == list->tail) { - add_wait_queue(&list->hiddev->wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - while (list->head == list->tail) { - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - if (!list->hiddev->exist) { - retval = -EIO; - break; - } - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&list->hiddev->wait, &wait); - } - - if (retval) - return retval; - - - while (list->head != list->tail && - retval + event_size <= count) { - if ((list->flags & HIDDEV_FLAG_UREF) == 0) { - if (list->buffer[list->tail].field_index != - HID_FIELD_INDEX_NONE) { - struct hiddev_event event; - event.hid = list->buffer[list->tail].usage_code; - event.value = list->buffer[list->tail].value; - if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) - return -EFAULT; - retval += sizeof(struct hiddev_event); - } - } else { - if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || - (list->flags & HIDDEV_FLAG_REPORT) != 0) { - if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) - return -EFAULT; - retval += sizeof(struct hiddev_usage_ref); - } - } - list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); - } - - } - - return retval; -} - -/* - * "poll" file op - * No kernel lock - fine - */ -static unsigned int hiddev_poll(struct file *file, poll_table *wait) -{ - struct hiddev_list *list = file->private_data; - - poll_wait(file, &list->hiddev->wait, wait); - if (list->head != list->tail) - return POLLIN | POLLRDNORM; - if (!list->hiddev->exist) - return POLLERR | POLLHUP; - return 0; -} - -/* - * "ioctl" file op - */ -static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct hiddev_list *list = file->private_data; - struct hiddev *hiddev = list->hiddev; - struct hid_device *hid = hiddev->hid; - struct usb_device *dev = hid_to_usb_dev(hid); - struct hiddev_collection_info cinfo; - struct hiddev_report_info rinfo; - struct hiddev_field_info finfo; - struct hiddev_usage_ref_multi *uref_multi = NULL; - struct hiddev_usage_ref *uref; - struct hiddev_devinfo dinfo; - struct hid_report *report; - struct hid_field *field; - struct usbhid_device *usbhid = hid->driver_data; - void __user *user_arg = (void __user *)arg; - int i; - - if (!hiddev->exist) - return -EIO; - - switch (cmd) { - - case HIDIOCGVERSION: - return put_user(HID_VERSION, (int __user *)arg); - - case HIDIOCAPPLICATION: - if (arg < 0 || arg >= hid->maxapplication) - return -EINVAL; - - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == - HID_COLLECTION_APPLICATION && arg-- == 0) - break; - - if (i == hid->maxcollection) - return -EINVAL; - - return hid->collection[i].usage; - - case HIDIOCGDEVINFO: - dinfo.bustype = BUS_USB; - dinfo.busnum = dev->bus->busnum; - dinfo.devnum = dev->devnum; - dinfo.ifnum = usbhid->ifnum; - dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor); - dinfo.product = le16_to_cpu(dev->descriptor.idProduct); - dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice); - dinfo.num_applications = hid->maxapplication; - if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) - return -EFAULT; - - return 0; - - case HIDIOCGFLAG: - if (put_user(list->flags, (int __user *)arg)) - return -EFAULT; - - return 0; - - case HIDIOCSFLAG: - { - int newflags; - if (get_user(newflags, (int __user *)arg)) - return -EFAULT; - - if ((newflags & ~HIDDEV_FLAGS) != 0 || - ((newflags & HIDDEV_FLAG_REPORT) != 0 && - (newflags & HIDDEV_FLAG_UREF) == 0)) - return -EINVAL; - - list->flags = newflags; - - return 0; - } - - case HIDIOCGSTRING: - { - int idx, len; - char *buf; - - if (get_user(idx, (int __user *)arg)) - return -EFAULT; - - if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL) - return -ENOMEM; - - if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) { - kfree(buf); - return -EINVAL; - } - - if (copy_to_user(user_arg+sizeof(int), buf, len+1)) { - kfree(buf); - return -EFAULT; - } - - kfree(buf); - - return len; - } - - case HIDIOCINITREPORT: - usbhid_init_reports(hid); - - return 0; - - case HIDIOCGREPORT: - if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) - return -EFAULT; - - if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT) - return -EINVAL; - - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - usbhid_submit_report(hid, report, USB_DIR_IN); - usbhid_wait_io(hid); - - return 0; - - case HIDIOCSREPORT: - if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) - return -EFAULT; - - if (rinfo.report_type == HID_REPORT_TYPE_INPUT) - return -EINVAL; - - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - usbhid_submit_report(hid, report, USB_DIR_OUT); - usbhid_wait_io(hid); - - return 0; - - case HIDIOCGREPORTINFO: - if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) - return -EFAULT; - - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - rinfo.num_fields = report->maxfield; - - if (copy_to_user(user_arg, &rinfo, sizeof(rinfo))) - return -EFAULT; - - return 0; - - case HIDIOCGFIELDINFO: - if (copy_from_user(&finfo, user_arg, sizeof(finfo))) - return -EFAULT; - rinfo.report_type = finfo.report_type; - rinfo.report_id = finfo.report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - if (finfo.field_index >= report->maxfield) - return -EINVAL; - - field = report->field[finfo.field_index]; - memset(&finfo, 0, sizeof(finfo)); - finfo.report_type = rinfo.report_type; - finfo.report_id = rinfo.report_id; - finfo.field_index = field->report_count - 1; - finfo.maxusage = field->maxusage; - finfo.flags = field->flags; - finfo.physical = field->physical; - finfo.logical = field->logical; - finfo.application = field->application; - finfo.logical_minimum = field->logical_minimum; - finfo.logical_maximum = field->logical_maximum; - finfo.physical_minimum = field->physical_minimum; - finfo.physical_maximum = field->physical_maximum; - finfo.unit_exponent = field->unit_exponent; - finfo.unit = field->unit; - - if (copy_to_user(user_arg, &finfo, sizeof(finfo))) - return -EFAULT; - - return 0; - - case HIDIOCGUCODE: - uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); - if (!uref_multi) - return -ENOMEM; - uref = &uref_multi->uref; - if (copy_from_user(uref, user_arg, sizeof(*uref))) - goto fault; - - rinfo.report_type = uref->report_type; - rinfo.report_id = uref->report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - goto inval; - - if (uref->field_index >= report->maxfield) - goto inval; - - field = report->field[uref->field_index]; - if (uref->usage_index >= field->maxusage) - goto inval; - - uref->usage_code = field->usage[uref->usage_index].hid; - - if (copy_to_user(user_arg, uref, sizeof(*uref))) - goto fault; - - kfree(uref_multi); - return 0; - - case HIDIOCGUSAGE: - case HIDIOCSUSAGE: - case HIDIOCGUSAGES: - case HIDIOCSUSAGES: - case HIDIOCGCOLLECTIONINDEX: - uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); - if (!uref_multi) - return -ENOMEM; - uref = &uref_multi->uref; - if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { - if (copy_from_user(uref_multi, user_arg, - sizeof(*uref_multi))) - goto fault; - } else { - if (copy_from_user(uref, user_arg, sizeof(*uref))) - goto fault; - } - - if (cmd != HIDIOCGUSAGE && - cmd != HIDIOCGUSAGES && - uref->report_type == HID_REPORT_TYPE_INPUT) - goto inval; - - if (uref->report_id == HID_REPORT_ID_UNKNOWN) { - field = hiddev_lookup_usage(hid, uref); - if (field == NULL) - goto inval; - } else { - rinfo.report_type = uref->report_type; - rinfo.report_id = uref->report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - goto inval; - - if (uref->field_index >= report->maxfield) - goto inval; - - field = report->field[uref->field_index]; - - if (cmd == HIDIOCGCOLLECTIONINDEX) { - if (uref->usage_index >= field->maxusage) - goto inval; - } else if (uref->usage_index >= field->report_count) - goto inval; - - else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && - (uref_multi->num_values > HID_MAX_MULTI_USAGES || - uref->usage_index + uref_multi->num_values > field->report_count)) - goto inval; - } - - switch (cmd) { - case HIDIOCGUSAGE: - uref->value = field->value[uref->usage_index]; - if (copy_to_user(user_arg, uref, sizeof(*uref))) - goto fault; - goto goodreturn; - - case HIDIOCSUSAGE: - field->value[uref->usage_index] = uref->value; - goto goodreturn; - - case HIDIOCGCOLLECTIONINDEX: - kfree(uref_multi); - return field->usage[uref->usage_index].collection_index; - case HIDIOCGUSAGES: - for (i = 0; i < uref_multi->num_values; i++) - uref_multi->values[i] = - field->value[uref->usage_index + i]; - if (copy_to_user(user_arg, uref_multi, - sizeof(*uref_multi))) - goto fault; - goto goodreturn; - case HIDIOCSUSAGES: - for (i = 0; i < uref_multi->num_values; i++) - field->value[uref->usage_index + i] = - uref_multi->values[i]; - goto goodreturn; - } - -goodreturn: - kfree(uref_multi); - return 0; -fault: - kfree(uref_multi); - return -EFAULT; -inval: - kfree(uref_multi); - return -EINVAL; - - case HIDIOCGCOLLECTIONINFO: - if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) - return -EFAULT; - - if (cinfo.index >= hid->maxcollection) - return -EINVAL; - - cinfo.type = hid->collection[cinfo.index].type; - cinfo.usage = hid->collection[cinfo.index].usage; - cinfo.level = hid->collection[cinfo.index].level; - - if (copy_to_user(user_arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - default: - - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) - return -EINVAL; - - if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) { - int len; - if (!hid->name) - return 0; - len = strlen(hid->name) + 1; - if (len > _IOC_SIZE(cmd)) - len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->name, len) ? - -EFAULT : len; - } - - if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) { - int len; - if (!hid->phys) - return 0; - len = strlen(hid->phys) + 1; - if (len > _IOC_SIZE(cmd)) - len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->phys, len) ? - -EFAULT : len; - } - } - return -EINVAL; -} - -static const struct file_operations hiddev_fops = { - .owner = THIS_MODULE, - .read = hiddev_read, - .write = hiddev_write, - .poll = hiddev_poll, - .open = hiddev_open, - .release = hiddev_release, - .ioctl = hiddev_ioctl, - .fasync = hiddev_fasync, -}; - -static struct usb_class_driver hiddev_class = { - .name = "hiddev%d", - .fops = &hiddev_fops, - .minor_base = HIDDEV_MINOR_BASE, -}; - -/* - * This is where hid.c calls us to connect a hid device to the hiddev driver - */ -int hiddev_connect(struct hid_device *hid) -{ - struct hiddev *hiddev; - struct usbhid_device *usbhid = hid->driver_data; - int i; - int retval; - - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == - HID_COLLECTION_APPLICATION && - !IS_INPUT_APPLICATION(hid->collection[i].usage)) - break; - - if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) - return -1; - - if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) - return -1; - - retval = usb_register_dev(usbhid->intf, &hiddev_class); - if (retval) { - err("Not able to get a minor for this device."); - kfree(hiddev); - return -1; - } - - init_waitqueue_head(&hiddev->wait); - INIT_LIST_HEAD(&hiddev->list); - hiddev->hid = hid; - hiddev->exist = 1; - - hid->minor = usbhid->intf->minor; - hid->hiddev = hiddev; - - hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; - - return 0; -} - -/* - * This is where hid.c calls us to disconnect a hiddev device from the - * corresponding hid device (usually because the usb device has disconnected) - */ -static struct usb_class_driver hiddev_class; -void hiddev_disconnect(struct hid_device *hid) -{ - struct hiddev *hiddev = hid->hiddev; - struct usbhid_device *usbhid = hid->driver_data; - - hiddev->exist = 0; - - hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; - usb_deregister_dev(usbhid->intf, &hiddev_class); - - if (hiddev->open) { - usbhid_close(hiddev->hid); - wake_up_interruptible(&hiddev->wait); - } else { - kfree(hiddev); - } -} - -/* Currently this driver is a USB driver. It's not a conventional one in - * the sense that it doesn't probe at the USB level. Instead it waits to - * be connected by HID through the hiddev_connect / hiddev_disconnect - * routines. The reason to register as a USB device is to gain part of the - * minor number space from the USB major. - * - * In theory, should the HID code be generalized to more than one physical - * medium (say, IEEE 1384), this driver will probably need to register its - * own major number, and in doing so, no longer need to register with USB. - * At that point the probe routine and hiddev_driver struct below will no - * longer be useful. - */ - - -/* We never attach in this manner, and rely on HID to connect us. This - * is why there is no disconnect routine defined in the usb_driver either. - */ -static int hiddev_usbd_probe(struct usb_interface *intf, - const struct usb_device_id *hiddev_info) -{ - return -ENODEV; -} - - -static /* const */ struct usb_driver hiddev_driver = { - .name = "hiddev", - .probe = hiddev_usbd_probe, -}; - -int __init hiddev_init(void) -{ - return usb_register(&hiddev_driver); -} - -void hiddev_exit(void) -{ - usb_deregister(&hiddev_driver); -} diff --git a/drivers/usb/input/usbhid.h b/drivers/usb/input/usbhid.h deleted file mode 100644 index 0023f96..0000000 --- a/drivers/usb/input/usbhid.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __USBHID_H -#define __USBHID_H - -/* - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2001 Vojtech Pavlik - * Copyright (c) 2006 Jiri Kosina - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include - -/* API provided by hid-core.c for USB HID drivers */ -int usbhid_wait_io(struct hid_device* hid); -void usbhid_close(struct hid_device *hid); -int usbhid_open(struct hid_device *hid); -void usbhid_init_reports(struct hid_device *hid); -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir); - -/* - * USB-specific HID struct, to be pointed to - * from struct hid_device->driver_data - */ - -struct usbhid_device { - struct hid_device *hid; /* pointer to corresponding HID dev */ - - struct usb_interface *intf; /* USB interface */ - int ifnum; /* USB interface number */ - - unsigned int bufsize; /* URB buffer size */ - - struct urb *urbin; /* Input URB */ - char *inbuf; /* Input buffer */ - dma_addr_t inbuf_dma; /* Input buffer dma */ - spinlock_t inlock; /* Input fifo spinlock */ - - struct urb *urbctrl; /* Control URB */ - struct usb_ctrlrequest *cr; /* Control request struct */ - dma_addr_t cr_dma; /* Control request struct dma */ - struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */ - unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ - char *ctrlbuf; /* Control buffer */ - dma_addr_t ctrlbuf_dma; /* Control buffer dma */ - spinlock_t ctrllock; /* Control fifo spinlock */ - - struct urb *urbout; /* Output URB */ - struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ - unsigned char outhead, outtail; /* Output pipe fifo head & tail */ - char *outbuf; /* Output buffer */ - dma_addr_t outbuf_dma; /* Output buffer dma */ - spinlock_t outlock; /* Output fifo spinlock */ - - unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ - struct timer_list io_retry; /* Retry timer */ - unsigned long stop_retry; /* Time to give up, in jiffies */ - unsigned int retry_delay; /* Delay length in ms */ - struct work_struct reset_work; /* Task context for resets */ - -}; - -#define hid_to_usb_dev(hid_dev) \ - container_of(hid_dev->dev->parent, struct usb_device, dev) - -#endif - diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c deleted file mode 100644 index 3749f4a..0000000 --- a/drivers/usb/input/usbkbd.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $ - * - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * USB HIDBP Keyboard support - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include - -/* - * Version Information - */ -#define DRIVER_VERSION "" -#define DRIVER_AUTHOR "Vojtech Pavlik " -#define DRIVER_DESC "USB HID Boot Protocol keyboard driver" -#define DRIVER_LICENSE "GPL" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - -static unsigned char usb_kbd_keycode[256] = { - 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, - 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, - 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, - 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, - 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, - 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, - 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0, - 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, - 150,158,159,128,136,177,178,176,142,152,173,140 -}; - -struct usb_kbd { - struct input_dev *dev; - struct usb_device *usbdev; - unsigned char old[8]; - struct urb *irq, *led; - unsigned char newleds; - char name[128]; - char phys[64]; - - unsigned char *new; - struct usb_ctrlrequest *cr; - unsigned char *leds; - dma_addr_t cr_dma; - dma_addr_t new_dma; - dma_addr_t leds_dma; -}; - -static void usb_kbd_irq(struct urb *urb) -{ - struct usb_kbd *kbd = urb->context; - int i; - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: - return; - /* -EPIPE: should clear the halt */ - default: /* error */ - goto resubmit; - } - - for (i = 0; i < 8; i++) - input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); - - for (i = 2; i < 8; i++) { - - if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) { - if (usb_kbd_keycode[kbd->old[i]]) - input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); - else - info("Unknown key (scancode %#x) released.", kbd->old[i]); - } - - if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) { - if (usb_kbd_keycode[kbd->new[i]]) - input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); - else - info("Unknown key (scancode %#x) pressed.", kbd->new[i]); - } - } - - input_sync(kbd->dev); - - memcpy(kbd->old, kbd->new, 8); - -resubmit: - i = usb_submit_urb (urb, GFP_ATOMIC); - if (i) - err ("can't resubmit intr, %s-%s/input0, status %d", - kbd->usbdev->bus->bus_name, - kbd->usbdev->devpath, i); -} - -static int usb_kbd_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) -{ - struct usb_kbd *kbd = dev->private; - - if (type != EV_LED) - return -1; - - - kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | - (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | - (!!test_bit(LED_NUML, dev->led)); - - if (kbd->led->status == -EINPROGRESS) - return 0; - - if (*(kbd->leds) == kbd->newleds) - return 0; - - *(kbd->leds) = kbd->newleds; - kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); - - return 0; -} - -static void usb_kbd_led(struct urb *urb) -{ - struct usb_kbd *kbd = urb->context; - - if (urb->status) - warn("led urb status %d received", urb->status); - - if (*(kbd->leds) == kbd->newleds) - return; - - *(kbd->leds) = kbd->newleds; - kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); -} - -static int usb_kbd_open(struct input_dev *dev) -{ - struct usb_kbd *kbd = dev->private; - - kbd->irq->dev = kbd->usbdev; - if (usb_submit_urb(kbd->irq, GFP_KERNEL)) - return -EIO; - - return 0; -} - -static void usb_kbd_close(struct input_dev *dev) -{ - struct usb_kbd *kbd = dev->private; - - usb_kill_urb(kbd->irq); -} - -static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) -{ - if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) - return -1; - if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) - return -1; - if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma))) - return -1; - if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma))) - return -1; - if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) - return -1; - - return 0; -} - -static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) -{ - usb_free_urb(kbd->irq); - usb_free_urb(kbd->led); - if (kbd->new) - usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); - if (kbd->cr) - usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); - if (kbd->leds) - usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); -} - -static int usb_kbd_probe(struct usb_interface *iface, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(iface); - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_kbd *kbd; - struct input_dev *input_dev; - int i, pipe, maxp; - - interface = iface->cur_altsetting; - - if (interface->desc.bNumEndpoints != 1) - return -ENODEV; - - endpoint = &interface->endpoint[0].desc; - if (!usb_endpoint_is_int_in(endpoint)) - return -ENODEV; - - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!kbd || !input_dev) - goto fail1; - - if (usb_kbd_alloc_mem(dev, kbd)) - goto fail2; - - kbd->usbdev = dev; - kbd->dev = input_dev; - - if (dev->manufacturer) - strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); - - if (dev->product) { - if (dev->manufacturer) - strlcat(kbd->name, " ", sizeof(kbd->name)); - strlcat(kbd->name, dev->product, sizeof(kbd->name)); - } - - if (!strlen(kbd->name)) - snprintf(kbd->name, sizeof(kbd->name), - "USB HIDBP Keyboard %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); - strlcpy(kbd->phys, "/input0", sizeof(kbd->phys)); - - input_dev->name = kbd->name; - input_dev->phys = kbd->phys; - usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &iface->dev; - input_dev->private = kbd; - - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); - input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); - - for (i = 0; i < 255; i++) - set_bit(usb_kbd_keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); - - input_dev->event = usb_kbd_event; - input_dev->open = usb_kbd_open; - input_dev->close = usb_kbd_close; - - usb_fill_int_urb(kbd->irq, dev, pipe, - kbd->new, (maxp > 8 ? 8 : maxp), - usb_kbd_irq, kbd, endpoint->bInterval); - kbd->irq->transfer_dma = kbd->new_dma; - kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kbd->cr->bRequest = 0x09; - kbd->cr->wValue = cpu_to_le16(0x200); - kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); - kbd->cr->wLength = cpu_to_le16(1); - - usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), - (void *) kbd->cr, kbd->leds, 1, - usb_kbd_led, kbd); - kbd->led->setup_dma = kbd->cr_dma; - kbd->led->transfer_dma = kbd->leds_dma; - kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - - input_register_device(kbd->dev); - - usb_set_intfdata(iface, kbd); - return 0; - -fail2: usb_kbd_free_mem(dev, kbd); -fail1: input_free_device(input_dev); - kfree(kbd); - return -ENOMEM; -} - -static void usb_kbd_disconnect(struct usb_interface *intf) -{ - struct usb_kbd *kbd = usb_get_intfdata (intf); - - usb_set_intfdata(intf, NULL); - if (kbd) { - usb_kill_urb(kbd->irq); - input_unregister_device(kbd->dev); - usb_kbd_free_mem(interface_to_usbdev(intf), kbd); - kfree(kbd); - } -} - -static struct usb_device_id usb_kbd_id_table [] = { - { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, - USB_INTERFACE_PROTOCOL_KEYBOARD) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_kbd_id_table); - -static struct usb_driver usb_kbd_driver = { - .name = "usbkbd", - .probe = usb_kbd_probe, - .disconnect = usb_kbd_disconnect, - .id_table = usb_kbd_id_table, -}; - -static int __init usb_kbd_init(void) -{ - int result = usb_register(&usb_kbd_driver); - if (result == 0) - info(DRIVER_VERSION ":" DRIVER_DESC); - return result; -} - -static void __exit usb_kbd_exit(void) -{ - usb_deregister(&usb_kbd_driver); -} - -module_init(usb_kbd_init); -module_exit(usb_kbd_exit); diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c deleted file mode 100644 index 692fd60..0000000 --- a/drivers/usb/input/usbmouse.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $ - * - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * USB HIDBP Mouse support - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.6" -#define DRIVER_AUTHOR "Vojtech Pavlik " -#define DRIVER_DESC "USB HID Boot Protocol mouse driver" -#define DRIVER_LICENSE "GPL" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - -struct usb_mouse { - char name[128]; - char phys[64]; - struct usb_device *usbdev; - struct input_dev *dev; - struct urb *irq; - - signed char *data; - dma_addr_t data_dma; -}; - -static void usb_mouse_irq(struct urb *urb) -{ - struct usb_mouse *mouse = urb->context; - signed char *data = mouse->data; - struct input_dev *dev = mouse->dev; - int status; - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: - return; - /* -EPIPE: should clear the halt */ - default: /* error */ - goto resubmit; - } - - input_report_key(dev, BTN_LEFT, data[0] & 0x01); - input_report_key(dev, BTN_RIGHT, data[0] & 0x02); - input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); - input_report_key(dev, BTN_SIDE, data[0] & 0x08); - input_report_key(dev, BTN_EXTRA, data[0] & 0x10); - - input_report_rel(dev, REL_X, data[1]); - input_report_rel(dev, REL_Y, data[2]); - input_report_rel(dev, REL_WHEEL, data[3]); - - input_sync(dev); -resubmit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) - err ("can't resubmit intr, %s-%s/input0, status %d", - mouse->usbdev->bus->bus_name, - mouse->usbdev->devpath, status); -} - -static int usb_mouse_open(struct input_dev *dev) -{ - struct usb_mouse *mouse = dev->private; - - mouse->irq->dev = mouse->usbdev; - if (usb_submit_urb(mouse->irq, GFP_KERNEL)) - return -EIO; - - return 0; -} - -static void usb_mouse_close(struct input_dev *dev) -{ - struct usb_mouse *mouse = dev->private; - - usb_kill_urb(mouse->irq); -} - -static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_mouse *mouse; - struct input_dev *input_dev; - int pipe, maxp; - - interface = intf->cur_altsetting; - - if (interface->desc.bNumEndpoints != 1) - return -ENODEV; - - endpoint = &interface->endpoint[0].desc; - if (!usb_endpoint_is_int_in(endpoint)) - return -ENODEV; - - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!mouse || !input_dev) - goto fail1; - - mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma); - if (!mouse->data) - goto fail1; - - mouse->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!mouse->irq) - goto fail2; - - mouse->usbdev = dev; - mouse->dev = input_dev; - - if (dev->manufacturer) - strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); - - if (dev->product) { - if (dev->manufacturer) - strlcat(mouse->name, " ", sizeof(mouse->name)); - strlcat(mouse->name, dev->product, sizeof(mouse->name)); - } - - if (!strlen(mouse->name)) - snprintf(mouse->name, sizeof(mouse->name), - "USB HIDBP Mouse %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); - strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); - - input_dev->name = mouse->name; - input_dev->phys = mouse->phys; - usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &intf->dev; - - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); - input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); - input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); - input_dev->relbit[0] |= BIT(REL_WHEEL); - - input_dev->private = mouse; - input_dev->open = usb_mouse_open; - input_dev->close = usb_mouse_close; - - usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, - (maxp > 8 ? 8 : maxp), - usb_mouse_irq, mouse, endpoint->bInterval); - mouse->irq->transfer_dma = mouse->data_dma; - mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - input_register_device(mouse->dev); - - usb_set_intfdata(intf, mouse); - return 0; - -fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma); -fail1: input_free_device(input_dev); - kfree(mouse); - return -ENOMEM; -} - -static void usb_mouse_disconnect(struct usb_interface *intf) -{ - struct usb_mouse *mouse = usb_get_intfdata (intf); - - usb_set_intfdata(intf, NULL); - if (mouse) { - usb_kill_urb(mouse->irq); - input_unregister_device(mouse->dev); - usb_free_urb(mouse->irq); - usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); - kfree(mouse); - } -} - -static struct usb_device_id usb_mouse_id_table [] = { - { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, - USB_INTERFACE_PROTOCOL_MOUSE) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); - -static struct usb_driver usb_mouse_driver = { - .name = "usbmouse", - .probe = usb_mouse_probe, - .disconnect = usb_mouse_disconnect, - .id_table = usb_mouse_id_table, -}; - -static int __init usb_mouse_init(void) -{ - int retval = usb_register(&usb_mouse_driver); - if (retval == 0) - info(DRIVER_VERSION ":" DRIVER_DESC); - return retval; -} - -static void __exit usb_mouse_exit(void) -{ - usb_deregister(&usb_mouse_driver); -} - -module_init(usb_mouse_init); -module_exit(usb_mouse_exit); diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 75bfab9..77145f9 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -285,23 +285,24 @@ static int adu_open(struct inode *inode, /* save device in the file's private structure */ file->private_data = dev; - /* initialize in direction */ - dev->read_buffer_length = 0; - - /* fixup first read by having urb waiting for it */ - usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, - usb_rcvintpipe(dev->udev, - dev->interrupt_in_endpoint->bEndpointAddress), - dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), - adu_interrupt_in_callback, dev, - dev->interrupt_in_endpoint->bInterval); - /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ - dev->read_urb_finished = 0; - usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); - /* we ignore failure */ - /* end of fixup for first read */ + if (dev->open_count == 1) { + /* initialize in direction */ + dev->read_buffer_length = 0; + /* fixup first read by having urb waiting for it */ + usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, + usb_rcvintpipe(dev->udev, + dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + adu_interrupt_in_callback, dev, + dev->interrupt_in_endpoint->bInterval); + /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ + dev->read_urb_finished = 0; + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); + if (retval) + --dev->open_count; + } up(&dev->sem); exit_no_device: @@ -469,7 +470,7 @@ static ssize_t adu_read(struct file *fil adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); - retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); if (!retval) { spin_unlock_irqrestore(&dev->buflock, flags); dbg(2," %s : submitted OK", __FUNCTION__); @@ -539,7 +540,7 @@ static ssize_t adu_write(struct file *fi size_t bytes_written = 0; size_t bytes_to_write; size_t buffer_size; - int retval = 0; + int retval; int timeout = 0; dbg(2," %s : enter, count = %Zd", __FUNCTION__, count); @@ -547,7 +548,9 @@ static ssize_t adu_write(struct file *fi dev = file->private_data; /* lock this object */ - down_interruptible(&dev->sem); + retval = down_interruptible(&dev->sem); + if (retval) + goto exit_nolock; /* verify that the device wasn't unplugged */ if (dev->udev == NULL || dev->minor == 0) { @@ -575,7 +578,11 @@ static ssize_t adu_write(struct file *fi } up(&dev->sem); timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout); - down_interruptible(&dev->sem); + retval = down_interruptible(&dev->sem); + if (retval) { + retval = bytes_written ? bytes_written : retval; + goto exit_nolock; + } if (timeout > 0) { break; } @@ -637,6 +644,7 @@ static ssize_t adu_write(struct file *fi exit: /* unlock the device */ up(&dev->sem); +exit_nolock: dbg(2," %s : leave, return value %d", __FUNCTION__, retval); diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index b63b5f3..d721380 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -246,11 +246,13 @@ static void cypress_disconnect(struct us struct cypress *dev; dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); /* remove device attribute files */ device_remove_file(&interface->dev, &dev_attr_port0); device_remove_file(&interface->dev, &dev_attr_port1); + /* the intfdata can be set to NULL only after the + * device files have been removed */ + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index bc3327e..e2172e5 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -2304,7 +2304,6 @@ #define OHCI_QUIRK_AMD756 0x01 #define OHCI_QUIRK_SUPERIO 0x02 #define OHCI_QUIRK_INITRESET 0x04 #define OHCI_BIG_ENDIAN 0x08 -#define OHCI_QUIRK_ZFMICRO 0x10 #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR #define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \ OHCI_INTR_WDH) @@ -2910,24 +2909,28 @@ static int __init ftdi_elan_init(void) INIT_LIST_HEAD(&ftdi_static_list); status_queue = create_singlethread_workqueue("ftdi-status-control"); if (!status_queue) - goto err1; + goto err_status_queue; command_queue = create_singlethread_workqueue("ftdi-command-engine"); if (!command_queue) - goto err2; + goto err_command_queue; respond_queue = create_singlethread_workqueue("ftdi-respond-engine"); if (!respond_queue) - goto err3; + goto err_respond_queue; result = usb_register(&ftdi_elan_driver); - if (result) + if (result) { + destroy_workqueue(status_queue); + destroy_workqueue(command_queue); + destroy_workqueue(respond_queue); printk(KERN_ERR "usb_register failed. Error number %d\n", result); + } return result; - err3: + err_respond_queue: destroy_workqueue(command_queue); - err2: + err_command_queue: destroy_workqueue(status_queue); - err1: + err_status_queue: printk(KERN_ERR "%s couldn't create workqueue\n", ftdi_elan_driver.name); return -ENOMEM; } diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index d69665c..fc51207 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -118,7 +118,7 @@ static int usb_get_report(struct usb_dev USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, (type << 8) + id, inter->desc.bInterfaceNumber, buf, size, - GET_TIMEOUT); + GET_TIMEOUT*HZ); } //#endif @@ -133,7 +133,7 @@ static int usb_set_report(struct usb_int USB_TYPE_CLASS | USB_RECIP_INTERFACE, (type << 8) + id, intf->cur_altsetting->desc.bInterfaceNumber, buf, - size, 1); + size, HZ); } /*---------------------*/ @@ -417,14 +417,14 @@ static ssize_t iowarrior_write(struct fi if (!int_out_urb) { retval = -ENOMEM; dbg("%s Unable to allocate urb ", __func__); - goto error; + goto error_no_urb; } buf = usb_buffer_alloc(dev->udev, dev->report_size, GFP_KERNEL, &int_out_urb->transfer_dma); if (!buf) { retval = -ENOMEM; dbg("%s Unable to allocate buffer ", __func__); - goto error; + goto error_no_buffer; } usb_fill_int_urb(int_out_urb, dev->udev, usb_sndintpipe(dev->udev, @@ -459,7 +459,9 @@ static ssize_t iowarrior_write(struct fi error: usb_buffer_free(dev->udev, dev->report_size, buf, int_out_urb->transfer_dma); +error_no_buffer: usb_free_urb(int_out_urb); +error_no_urb: atomic_dec(&dev->write_busy); wake_up_interruptible(&dev->write_wait); exit: @@ -748,7 +750,6 @@ static int iowarrior_probe(struct usb_in struct usb_endpoint_descriptor *endpoint; int i; int retval = -ENOMEM; - int idele = 0; /* allocate memory for our device state and intialize it */ dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL); @@ -824,11 +825,10 @@ static int iowarrior_probe(struct usb_in /* Set the idle timeout to 0, if this is interface 0 */ if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) { - idele = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x0A, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - dbg("idele = %d", idele); + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x0A, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } /* allow device read and ioctl */ dev->present = 1; diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 788a11e..11555bd 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -62,6 +62,8 @@ #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0 #define USB_DEVICE_ID_VERNIER_SKIP 0x0003 #define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 +#define USB_VENDOR_ID_MICROCHIP 0x04d8 +#define USB_DEVICE_ID_PICDEM 0x000c #ifdef CONFIG_USB_DYNAMIC_MINORS #define USB_LD_MINOR_BASE 0 @@ -89,6 +91,7 @@ static struct usb_device_id ld_usb_table { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, + { USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICDEM) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, ld_usb_table); diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index ada2ebc..887ef95 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -47,6 +47,7 @@ struct usb_lcd { #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) static struct usb_driver lcd_driver; +static DEFINE_MUTEX(usb_lcd_open_mutex); static void lcd_delete(struct kref *kref) @@ -68,6 +69,7 @@ static int lcd_open(struct inode *inode, subminor = iminor(inode); + mutex_lock(&usb_lcd_open_mutex); interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { err ("USBLCD: %s - error, can't find device for minor %d", @@ -89,6 +91,7 @@ static int lcd_open(struct inode *inode, file->private_data = dev; exit: + mutex_unlock(&usb_lcd_open_mutex); return retval; } @@ -347,7 +350,7 @@ static void lcd_disconnect(struct usb_in int minor = interface->minor; /* prevent skel_open() from racing skel_disconnect() */ - lock_kernel(); + mutex_lock(&usb_lcd_open_mutex); dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -355,7 +358,7 @@ static void lcd_disconnect(struct usb_in /* give back our minor */ usb_deregister_dev(interface, &lcd_class); - unlock_kernel(); + mutex_unlock(&usb_lcd_open_mutex); /* decrement our usage count */ kref_put(&dev->kref, lcd_delete); diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index b2bedd9..0af11a6 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -356,8 +356,10 @@ static inline char mon_bin_get_setup(uns if (!usb_pipecontrol(urb->pipe) || ev_type != 'S') return '-'; - if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP) + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN); + } if (urb->setup_packet == NULL) return 'Z'; @@ -369,7 +371,8 @@ static char mon_bin_get_data(const struc unsigned int offset, struct urb *urb, unsigned int length) { - if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) { + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { mon_dmapeek_vec(rp, offset, urb->transfer_dma, length); return 0; } @@ -440,7 +443,7 @@ static void mon_bin_event(struct mon_rea /* We use the fact that usb_pipein() returns 0x80 */ ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); ep->devnum = usb_pipedevice(urb->pipe); - ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->busnum = urb->dev->bus->busnum; ep->id = (unsigned long) urb; ep->ts_sec = ts.tv_sec; ep->ts_usec = ts.tv_usec; @@ -500,7 +503,7 @@ static void mon_bin_error(void *data, st /* We use the fact that usb_pipein() returns 0x80 */ ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); ep->devnum = usb_pipedevice(urb->pipe); - ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->busnum = urb->dev->bus->busnum; ep->id = (unsigned long) urb; ep->status = error; @@ -515,7 +518,6 @@ static void mon_bin_error(void *data, st static int mon_bin_open(struct inode *inode, struct file *file) { struct mon_bus *mbus; - struct usb_bus *ubus; struct mon_reader_bin *rp; size_t size; int rc; @@ -525,7 +527,7 @@ static int mon_bin_open(struct inode *in mutex_unlock(&mon_lock); return -ENODEV; } - if ((ubus = mbus->u_bus) == NULL) { + if (mbus != &mon_bus0 && mbus->u_bus == NULL) { printk(KERN_ERR TAG ": consistency error on open\n"); mutex_unlock(&mon_lock); return -ENODEV; diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index c9739e7..8a1df2c 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -16,8 +16,6 @@ #include #include "usb_mon.h" #include "../core/hcd.h" -static void mon_submit(struct usb_bus *ubus, struct urb *urb); -static void mon_complete(struct usb_bus *ubus, struct urb *urb); static void mon_stop(struct mon_bus *mbus); static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); static void mon_bus_drop(struct kref *r); @@ -25,6 +23,7 @@ static void mon_bus_init(struct usb_bus DEFINE_MUTEX(mon_lock); +struct mon_bus mon_bus0; /* Pseudo bus meaning "all buses" */ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ /* @@ -35,22 +34,19 @@ static LIST_HEAD(mon_buses); /* All bus void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) { unsigned long flags; - struct usb_bus *ubus; + struct list_head *p; spin_lock_irqsave(&mbus->lock, flags); if (mbus->nreaders == 0) { - ubus = mbus->u_bus; - if (ubus->monitored) { - /* - * Something is really broken, refuse to go on and - * possibly corrupt ops pointers or worse. - */ - printk(KERN_ERR TAG ": bus %d is already monitored\n", - ubus->busnum); - spin_unlock_irqrestore(&mbus->lock, flags); - return; + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + struct mon_bus *m1; + m1 = list_entry(p, struct mon_bus, bus_link); + m1->u_bus->monitored = 1; + } + } else { + mbus->u_bus->monitored = 1; } - ubus->monitored = 1; } mbus->nreaders++; list_add_tail(&r->r_link, &mbus->r_list); @@ -80,77 +76,79 @@ void mon_reader_del(struct mon_bus *mbus /* */ -static void mon_submit(struct usb_bus *ubus, struct urb *urb) +static void mon_bus_submit(struct mon_bus *mbus, struct urb *urb) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; - mbus = ubus->mon_bus; - if (mbus == NULL) - goto out_unlocked; - spin_lock_irqsave(&mbus->lock, flags); - if (mbus->nreaders == 0) - goto out_locked; - mbus->cnt_events++; list_for_each (pos, &mbus->r_list) { r = list_entry(pos, struct mon_reader, r_link); r->rnf_submit(r->r_data, urb); } - spin_unlock_irqrestore(&mbus->lock, flags); return; +} -out_locked: - spin_unlock_irqrestore(&mbus->lock, flags); -out_unlocked: - return; +static void mon_submit(struct usb_bus *ubus, struct urb *urb) +{ + struct mon_bus *mbus; + + if ((mbus = ubus->mon_bus) != NULL) + mon_bus_submit(mbus, urb); + mon_bus_submit(&mon_bus0, urb); } /* */ -static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) +static void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; - mbus = ubus->mon_bus; - if (mbus == NULL) - goto out_unlocked; - spin_lock_irqsave(&mbus->lock, flags); - if (mbus->nreaders == 0) - goto out_locked; - mbus->cnt_events++; list_for_each (pos, &mbus->r_list) { r = list_entry(pos, struct mon_reader, r_link); r->rnf_error(r->r_data, urb, error); } - spin_unlock_irqrestore(&mbus->lock, flags); return; +} -out_locked: - spin_unlock_irqrestore(&mbus->lock, flags); -out_unlocked: - return; +static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) +{ + struct mon_bus *mbus; + + if ((mbus = ubus->mon_bus) != NULL) + mon_bus_submit_error(mbus, urb, error); + mon_bus_submit_error(&mon_bus0, urb, error); } /* */ -static void mon_complete(struct usb_bus *ubus, struct urb *urb) +static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; + spin_lock_irqsave(&mbus->lock, flags); + mbus->cnt_events++; + list_for_each (pos, &mbus->r_list) { + r = list_entry(pos, struct mon_reader, r_link); + r->rnf_complete(r->r_data, urb); + } + spin_unlock_irqrestore(&mbus->lock, flags); +} + +static void mon_complete(struct usb_bus *ubus, struct urb *urb) +{ + struct mon_bus *mbus; + mbus = ubus->mon_bus; if (mbus == NULL) { /* @@ -162,13 +160,8 @@ static void mon_complete(struct usb_bus return; } - spin_lock_irqsave(&mbus->lock, flags); - mbus->cnt_events++; - list_for_each (pos, &mbus->r_list) { - r = list_entry(pos, struct mon_reader, r_link); - r->rnf_complete(r->r_data, urb); - } - spin_unlock_irqrestore(&mbus->lock, flags); + mon_bus_complete(mbus, urb); + mon_bus_complete(&mon_bus0, urb); } /* int (*unlink_urb) (struct urb *urb, int status); */ @@ -179,14 +172,26 @@ static void mon_complete(struct usb_bus static void mon_stop(struct mon_bus *mbus) { struct usb_bus *ubus = mbus->u_bus; + struct list_head *p; - /* - * A stop can be called for a dissolved mon_bus in case of - * a reader staying across an rmmod foo_hcd. - */ - if (ubus != NULL) { - ubus->monitored = 0; - mb(); + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + mbus = list_entry(p, struct mon_bus, bus_link); + /* + * We do not change nreaders here, so rely on mon_lock. + */ + if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL) + ubus->monitored = 0; + } + } else { + /* + * A stop can be called for a dissolved mon_bus in case of + * a reader staying across an rmmod foo_hcd, so test ->u_bus. + */ + if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) { + ubus->monitored = 0; + mb(); + } } } @@ -199,6 +204,10 @@ static void mon_stop(struct mon_bus *mbu static void mon_bus_add(struct usb_bus *ubus) { mon_bus_init(ubus); + mutex_lock(&mon_lock); + if (mon_bus0.nreaders != 0) + ubus->monitored = 1; + mutex_unlock(&mon_lock); } /* @@ -250,12 +259,7 @@ static struct usb_mon_operations mon_ops static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) { - /* - * Never happens, but... - */ if (ubus->monitored) { - printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n", - ubus->busnum); ubus->monitored = 0; mb(); } @@ -263,6 +267,8 @@ static void mon_dissolve(struct mon_bus ubus->mon_bus = NULL; mbus->u_bus = NULL; mb(); + + /* We want synchronize_irq() here, but that needs an argument. */ } /* @@ -295,9 +301,8 @@ static void mon_bus_init(struct usb_bus */ mbus->u_bus = ubus; ubus->mon_bus = mbus; - mbus->uses_dma = ubus->uses_dma; - mbus->text_inited = mon_text_add(mbus, ubus); + mbus->text_inited = mon_text_add(mbus, ubus->busnum); // mon_bin_add(...) mutex_lock(&mon_lock); @@ -309,6 +314,18 @@ err_alloc: return; } +static void mon_bus0_init(void) +{ + struct mon_bus *mbus = &mon_bus0; + + kref_init(&mbus->ref); + spin_lock_init(&mbus->lock); + INIT_LIST_HEAD(&mbus->r_list); + + mbus->text_inited = mon_text_add(mbus, 0); + // mbus->bin_inited = mon_bin_add(mbus, 0); +} + /* * Search a USB bus by number. Notice that USB bus numbers start from one, * which we may later use to identify "all" with zero. @@ -322,6 +339,9 @@ struct mon_bus *mon_bus_lookup(unsigned struct list_head *p; struct mon_bus *mbus; + if (num == 0) { + return &mon_bus0; + } list_for_each (p, &mon_buses) { mbus = list_entry(p, struct mon_bus, bus_link); if (mbus->u_bus->busnum == num) { @@ -341,6 +361,8 @@ static int __init mon_init(void) if ((rc = mon_bin_init()) != 0) goto err_bin; + mon_bus0_init(); + if (usb_mon_register(&mon_ops_0) != 0) { printk(KERN_NOTICE TAG ": unable to register with the core\n"); rc = -ENODEV; @@ -374,6 +396,7 @@ static void __exit mon_exit(void) usb_mon_deregister(); mutex_lock(&mon_lock); + while (!list_empty(&mon_buses)) { p = mon_buses.next; mbus = list_entry(p, struct mon_bus, bus_link); @@ -397,6 +420,11 @@ static void __exit mon_exit(void) mon_dissolve(mbus, mbus->u_bus); kref_put(&mbus->ref, mon_bus_drop); } + + mbus = &mon_bus0; + if (mbus->text_inited) + mon_text_del(mbus); + mutex_unlock(&mon_lock); mon_text_exit(); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 494ee3b..ec0cc51 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -31,9 +31,21 @@ #define SETUP_MAX 8 * to a local DoS. But we have to keep to root in order to prevent * password sniffing from HID devices. */ -#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text)) +#define EVENT_MAX (4*PAGE_SIZE / sizeof(struct mon_event_text)) -#define PRINTF_DFL 160 +/* + * Potentially unlimited number; we limit it for similar allocations. + * The usbfs limits this to 128, but we're not quite as generous. + */ +#define ISODESC_MAX 5 + +#define PRINTF_DFL 250 /* with 5 ISOs segs */ + +struct mon_iso_desc { + int status; + unsigned int offset; + unsigned int length; /* Unsigned here, signed in URB. Historic. */ +}; struct mon_event_text { struct list_head e_link; @@ -41,10 +53,16 @@ struct mon_event_text { unsigned int pipe; /* Pipe */ unsigned long id; /* From pointer, most of the time */ unsigned int tstamp; + int busnum; int length; /* Depends on type: xfer length or act length */ int status; + int interval; + int start_frame; + int error_count; char setup_flag; char data_flag; + int numdesc; /* Full number */ + struct mon_iso_desc isodesc[ISODESC_MAX]; unsigned char setup[SETUP_MAX]; unsigned char data[DATA_MAX]; }; @@ -68,6 +86,28 @@ static struct dentry *mon_dir; /* Usual static void mon_text_ctor(void *, struct kmem_cache *, unsigned long); +struct mon_text_ptr { + int cnt, limit; + char *pbuf; +}; + +static struct mon_event_text * + mon_text_read_wait(struct mon_reader_text *rp, struct file *file); +static void mon_text_read_head_t(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_head_u(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_statset(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_intstat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_isostat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_isodesc(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_data(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); + /* * mon_text_submit * mon_text_complete @@ -84,8 +124,10 @@ static inline char mon_text_get_setup(st if (!usb_pipecontrol(urb->pipe) || ev_type != 'S') return '-'; - if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX); + } if (urb->setup_packet == NULL) return 'Z'; /* '0' would be not as pretty. */ @@ -104,10 +146,10 @@ static inline char mon_text_get_data(str len = DATA_MAX; if (usb_pipein(pipe)) { - if (ev_type == 'S') + if (ev_type != 'C') return '<'; } else { - if (ev_type == 'C') + if (ev_type != 'S') return '>'; } @@ -120,8 +162,10 @@ static inline char mon_text_get_data(str * contain non-NULL garbage in case the upper level promised to * set DMA for the HCD. */ - if (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { return mon_dmapeek(ep->data, urb->transfer_dma, len); + } if (urb->transfer_buffer == NULL) return 'Z'; /* '0' would be not as pretty. */ @@ -146,6 +190,9 @@ static void mon_text_event(struct mon_re { struct mon_event_text *ep; unsigned int stamp; + struct usb_iso_packet_descriptor *fp; + struct mon_iso_desc *dp; + int i, ndesc; stamp = mon_get_timestamp(); @@ -158,12 +205,36 @@ static void mon_text_event(struct mon_re ep->type = ev_type; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; + ep->busnum = urb->dev->bus->busnum; ep->tstamp = stamp; ep->length = (ev_type == 'S') ? urb->transfer_buffer_length : urb->actual_length; /* Collecting status makes debugging sense for submits, too */ ep->status = urb->status; + if (usb_pipeint(urb->pipe)) { + ep->interval = urb->interval; + } else if (usb_pipeisoc(urb->pipe)) { + ep->interval = urb->interval; + ep->start_frame = urb->start_frame; + ep->error_count = urb->error_count; + } + ep->numdesc = urb->number_of_packets; + if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) { + if ((ndesc = urb->number_of_packets) > ISODESC_MAX) + ndesc = ISODESC_MAX; + fp = urb->iso_frame_desc; + dp = ep->isodesc; + for (i = 0; i < ndesc; i++) { + dp->status = fp->status; + dp->offset = fp->offset; + dp->length = (ev_type == 'S') ? + fp->length : fp->actual_length; + fp++; + dp++; + } + } + ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus); ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type, rp->r.m_bus); @@ -199,6 +270,7 @@ static void mon_text_error(void *data, s ep->type = 'E'; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; + ep->busnum = 0; ep->tstamp = 0; ep->length = 0; ep->status = error; @@ -237,13 +309,11 @@ static struct mon_event_text *mon_text_f static int mon_text_open(struct inode *inode, struct file *file) { struct mon_bus *mbus; - struct usb_bus *ubus; struct mon_reader_text *rp; int rc; mutex_lock(&mon_lock); mbus = inode->i_private; - ubus = mbus->u_bus; rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); if (rp == NULL) { @@ -267,8 +337,7 @@ static int mon_text_open(struct inode *i rp->r.rnf_error = mon_text_error; rp->r.rnf_complete = mon_text_complete; - snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum, - (long)rp); + snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); rp->e_slab = kmem_cache_create(rp->slab_name, sizeof(struct mon_event_text), sizeof(long), 0, mon_text_ctor, NULL); @@ -300,17 +369,75 @@ err_alloc: * dd if=/dbg/usbmon/0t bs=10 * Also, we do not allow seeks and do not bother advancing the offset. */ -static ssize_t mon_text_read(struct file *file, char __user *buf, +static ssize_t mon_text_read_t(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_t(rp, &ptr, ep); + mon_text_read_statset(rp, &ptr, ep); + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static ssize_t mon_text_read_u(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_u(rp, &ptr, ep); + if (ep->type == 'E') { + mon_text_read_statset(rp, &ptr, ep); + } else if (usb_pipeisoc(ep->pipe)) { + mon_text_read_isostat(rp, &ptr, ep); + mon_text_read_isodesc(rp, &ptr, ep); + } else if (usb_pipeint(ep->pipe)) { + mon_text_read_intstat(rp, &ptr, ep); + } else { + mon_text_read_statset(rp, &ptr, ep); + } + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, + struct file *file) +{ struct mon_bus *mbus = rp->r.m_bus; DECLARE_WAITQUEUE(waita, current); struct mon_event_text *ep; - int cnt, limit; - char *pbuf; - char udir, utype; - int data_len, i; add_wait_queue(&rp->wait, &waita); set_current_state(TASK_INTERRUPTIBLE); @@ -318,7 +445,7 @@ static ssize_t mon_text_read(struct file if (file->f_flags & O_NONBLOCK) { set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); - return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ + return ERR_PTR(-EWOULDBLOCK); } /* * We do not count nwaiters, because ->release is supposed @@ -327,17 +454,19 @@ static ssize_t mon_text_read(struct file schedule(); if (signal_pending(current)) { remove_wait_queue(&rp->wait, &waita); - return -EINTR; + return ERR_PTR(-EINTR); } set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); + return ep; +} - mutex_lock(&rp->printf_lock); - cnt = 0; - pbuf = rp->printf_buf; - limit = rp->printf_size; +static void mon_text_read_head_t(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; udir = usb_pipein(ep->pipe) ? 'i' : 'o'; switch (usb_pipetype(ep->pipe)) { @@ -346,13 +475,38 @@ static ssize_t mon_text_read(struct file case PIPE_CONTROL: utype = 'C'; break; default: /* PIPE_BULK */ utype = 'B'; } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "%lx %u %c %c%c:%03u:%02u", ep->id, ep->tstamp, ep->type, - utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); + utype, udir, + usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); +} + +static void mon_text_read_head_u(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; + + udir = usb_pipein(ep->pipe) ? 'i' : 'o'; + switch (usb_pipetype(ep->pipe)) { + case PIPE_ISOCHRONOUS: utype = 'Z'; break; + case PIPE_INTERRUPT: utype = 'I'; break; + case PIPE_CONTROL: utype = 'C'; break; + default: /* PIPE_BULK */ utype = 'B'; + } + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "%lx %u %c %c%c:%d:%03u:%u", + ep->id, ep->tstamp, ep->type, + utype, udir, + ep->busnum, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); +} + +static void mon_text_read_statset(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ if (ep->setup_flag == 0) { /* Setup packet is present and captured */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " s %02x %02x %04x %04x %04x", ep->setup[0], ep->setup[1], @@ -360,40 +514,86 @@ static ssize_t mon_text_read(struct file (ep->setup[5] << 8) | ep->setup[4], (ep->setup[7] << 8) | ep->setup[6]); } else if (ep->setup_flag != '-') { /* Unable to capture setup packet */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c __ __ ____ ____ ____", ep->setup_flag); } else { /* No setup for this kind of URB */ - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->status); } - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length); +} + +static void mon_text_read_intstat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d", ep->status, ep->interval); +} + +static void mon_text_read_isostat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + if (ep->type == 'S') { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d", ep->status, ep->interval, ep->start_frame); + } else { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d:%d", + ep->status, ep->interval, ep->start_frame, ep->error_count); + } +} + +static void mon_text_read_isodesc(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int ndesc; /* Display this many */ + int i; + const struct mon_iso_desc *dp; + + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->numdesc); + ndesc = ep->numdesc; + if (ndesc > ISODESC_MAX) + ndesc = ISODESC_MAX; + if (ndesc < 0) + ndesc = 0; + dp = ep->isodesc; + for (i = 0; i < ndesc; i++) { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%u:%u", dp->status, dp->offset, dp->length); + dp++; + } +} + +static void mon_text_read_data(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int data_len, i; if ((data_len = ep->length) > 0) { if (ep->data_flag == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, " ="); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " ="); if (data_len >= DATA_MAX) data_len = DATA_MAX; for (i = 0; i < data_len; i++) { if (i % 4 == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, " "); } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, "%02x", ep->data[i]); } - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "\n"); } else { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c\n", ep->data_flag); } } else { - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n"); } - - if (copy_to_user(buf, rp->printf_buf, cnt)) - cnt = -EFAULT; - mutex_unlock(&rp->printf_lock); - kmem_cache_free(rp->e_slab, ep); - return cnt; } static int mon_text_release(struct inode *inode, struct file *file) @@ -439,34 +639,46 @@ static int mon_text_release(struct inode return 0; } -static const struct file_operations mon_fops_text = { +static const struct file_operations mon_fops_text_t = { .owner = THIS_MODULE, .open = mon_text_open, .llseek = no_llseek, - .read = mon_text_read, - /* .write = mon_text_write, */ - /* .poll = mon_text_poll, */ - /* .ioctl = mon_text_ioctl, */ + .read = mon_text_read_t, .release = mon_text_release, }; -int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) +static const struct file_operations mon_fops_text_u = { + .owner = THIS_MODULE, + .open = mon_text_open, + .llseek = no_llseek, + .read = mon_text_read_u, + .release = mon_text_release, +}; + +int mon_text_add(struct mon_bus *mbus, int busnum) { struct dentry *d; enum { NAMESZ = 10 }; char name[NAMESZ]; int rc; - rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); + rc = snprintf(name, NAMESZ, "%dt", busnum); if (rc <= 0 || rc >= NAMESZ) goto err_print_t; - d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text); + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t); if (d == NULL) goto err_create_t; mbus->dent_t = d; - /* XXX The stats do not belong to here (text API), but oh well... */ - rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); + rc = snprintf(name, NAMESZ, "%du", busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_u; + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u); + if (d == NULL) + goto err_create_u; + mbus->dent_u = d; + + rc = snprintf(name, NAMESZ, "%ds", busnum); if (rc <= 0 || rc >= NAMESZ) goto err_print_s; d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat); @@ -478,6 +690,10 @@ int mon_text_add(struct mon_bus *mbus, c err_create_s: err_print_s: + debugfs_remove(mbus->dent_u); + mbus->dent_u = NULL; +err_create_u: +err_print_u: debugfs_remove(mbus->dent_t); mbus->dent_t = NULL; err_create_t: @@ -487,6 +703,7 @@ err_print_t: void mon_text_del(struct mon_bus *mbus) { + debugfs_remove(mbus->dent_u); debugfs_remove(mbus->dent_t); debugfs_remove(mbus->dent_s); } diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index efdfd89..13d6325 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -22,7 +22,7 @@ struct mon_bus { int text_inited; struct dentry *dent_s; /* Debugging file */ struct dentry *dent_t; /* Text interface file */ - int uses_dma; + struct dentry *dent_u; /* Second text interface file */ /* Ref */ int nreaders; /* Under mon_lock AND mbus->lock */ @@ -52,7 +52,7 @@ void mon_reader_del(struct mon_bus *mbus struct mon_bus *mon_bus_lookup(unsigned int num); -int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus); +int /*bool*/ mon_text_add(struct mon_bus *mbus, int busnum); void mon_text_del(struct mon_bus *mbus); // void mon_bin_add(struct mon_bus *); @@ -81,4 +81,6 @@ extern struct mutex mon_lock; extern const struct file_operations mon_fops_stat; +extern struct mon_bus mon_bus0; /* Only for redundant checks */ + #endif /* __USB_MON_H */ diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c index 5808ea0..d5ef97b 100644 --- a/drivers/usb/net/asix.c +++ b/drivers/usb/net/asix.c @@ -298,7 +298,7 @@ static int asix_rx_fixup(struct usbnet * if (ax_skb) { ax_skb->len = size; ax_skb->data = packet; - ax_skb->tail = packet + size; + skb_set_tail_pointer(ax_skb, size); usbnet_skb_return(dev, ax_skb); } else { return 0; @@ -338,7 +338,7 @@ static struct sk_buff *asix_tx_fixup(str && ((headroom + tailroom) >= (4 + padlen))) { if ((headroom < 4) || (tailroom < padlen)) { skb->data = memmove(skb->head + 4, skb->data, skb->len); - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); } } else { struct sk_buff *skb2; @@ -352,11 +352,11 @@ static struct sk_buff *asix_tx_fixup(str skb_push(skb, 4); packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); cpu_to_le32s(&packet_len); - memcpy(skb->data, &packet_len, sizeof(packet_len)); + skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); if ((skb->len % 512) == 0) { cpu_to_le32s(&padbytes); - memcpy( skb->tail, &padbytes, sizeof(padbytes)); + memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); skb_put(skb, sizeof(padbytes)); } return skb; diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index 4852012..86e90c5 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -255,7 +255,6 @@ static void catc_rx_done(struct urb *urb if (!(skb = dev_alloc_skb(pkt_len))) return; - skb->dev = catc->netdev; eth_copy_and_sum(skb, pkt_start + pkt_offset, pkt_len, 0); skb_put(skb, pkt_len); @@ -356,7 +355,7 @@ resubmit: * Transmit routines. */ -static void catc_tx_run(struct catc *catc) +static int catc_tx_run(struct catc *catc) { int status; @@ -374,12 +373,14 @@ static void catc_tx_run(struct catc *cat catc->tx_ptr = 0; catc->netdev->trans_start = jiffies; + return status; } static void catc_tx_done(struct urb *urb) { struct catc *catc = urb->context; unsigned long flags; + int r; if (urb->status == -ECONNRESET) { dbg("Tx Reset."); @@ -398,10 +399,13 @@ static void catc_tx_done(struct urb *urb spin_lock_irqsave(&catc->tx_lock, flags); - if (catc->tx_ptr) - catc_tx_run(catc); - else + if (catc->tx_ptr) { + r = catc_tx_run(catc); + if (unlikely(r < 0)) + clear_bit(TX_RUNNING, &catc->flags); + } else { clear_bit(TX_RUNNING, &catc->flags); + } netif_wake_queue(catc->netdev); @@ -412,6 +416,7 @@ static int catc_hard_start_xmit(struct s { struct catc *catc = netdev_priv(netdev); unsigned long flags; + int r = 0; char *tx_buf; spin_lock_irqsave(&catc->tx_lock, flags); @@ -419,11 +424,14 @@ static int catc_hard_start_xmit(struct s catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6; tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr; *((u16*)tx_buf) = (catc->is_f5u011) ? cpu_to_be16((u16)skb->len) : cpu_to_le16((u16)skb->len); - memcpy(tx_buf + 2, skb->data, skb->len); + skb_copy_from_linear_data(skb, tx_buf + 2, skb->len); catc->tx_ptr += skb->len + 2; - if (!test_and_set_bit(TX_RUNNING, &catc->flags)) - catc_tx_run(catc); + if (!test_and_set_bit(TX_RUNNING, &catc->flags)) { + r = catc_tx_run(catc); + if (r < 0) + clear_bit(TX_RUNNING, &catc->flags); + } if ((catc->is_f5u011 && catc->tx_ptr) || (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2)))) @@ -431,8 +439,10 @@ static int catc_hard_start_xmit(struct s spin_unlock_irqrestore(&catc->tx_lock, flags); - catc->stats.tx_bytes += skb->len; - catc->stats.tx_packets++; + if (r >= 0) { + catc->stats.tx_bytes += skb->len; + catc->stats.tx_packets++; + } dev_kfree_skb(skb); diff --git a/drivers/usb/net/dm9601.c b/drivers/usb/net/dm9601.c index 5130cc7..a676386 100644 --- a/drivers/usb/net/dm9601.c +++ b/drivers/usb/net/dm9601.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -85,7 +86,7 @@ static int dm_write_reg(struct usbnet *d usb_sndctrlpipe(dev->udev, 0), DM_WRITE_REG, USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE, - value, reg, 0, 0, USB_CTRL_SET_TIMEOUT); + value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT); } static void dm_write_async_callback(struct urb *urb) @@ -171,7 +172,7 @@ static void dm_write_reg_async(struct us usb_fill_control_urb(urb, dev->udev, usb_sndctrlpipe(dev->udev, 0), - (void *)req, 0, 0, dm_write_async_callback, req); + (void *)req, NULL, 0, dm_write_async_callback, req); status = usb_submit_urb(urb, GFP_ATOMIC); if (status < 0) { diff --git a/drivers/usb/net/gl620a.c b/drivers/usb/net/gl620a.c index d257a8e..031cf5c 100644 --- a/drivers/usb/net/gl620a.c +++ b/drivers/usb/net/gl620a.c @@ -157,7 +157,7 @@ genelink_tx_fixup(struct usbnet *dev, st if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { skb->data = memmove(skb->head + (4 + 4*1), skb->data, skb->len); - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); } } else { struct sk_buff *skb2; diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index de95268..a0cc05d 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -636,8 +636,6 @@ static void kaweth_usb_receive(struct ur skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - skb->dev = net; - eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0); skb_put(skb, pkt_len); diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c index ccebfde..19bf8da 100644 --- a/drivers/usb/net/net1080.c +++ b/drivers/usb/net/net1080.c @@ -520,7 +520,7 @@ net1080_tx_fixup(struct usbnet *dev, str skb->data = memmove(skb->head + sizeof (struct nc_header), skb->data, skb->len); - skb->tail = skb->data + len; + skb_set_tail_pointer(skb, len); goto encapsulate; } } diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index 6d12961..a05fd97 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -575,7 +575,6 @@ static void fill_skb_pool(pegasus_t * pe */ if (pegasus->rx_pool[i] == NULL) return; - pegasus->rx_pool[i]->dev = pegasus->net; skb_reserve(pegasus->rx_pool[i], 2); } } @@ -848,16 +847,6 @@ static void intr_callback(struct urb *ur * d[0].NO_CARRIER kicks in only with failed TX. * ... so monitoring with MII may be safest. */ - if (pegasus->features & TRUST_LINK_STATUS) { - if (d[5] & LINK_STATUS) - netif_carrier_on(net); - else - netif_carrier_off(net); - } else { - /* Never set carrier _on_ based on ! NO_CARRIER */ - if (d[0] & NO_CARRIER) - netif_carrier_off(net); - } /* bytes 3-4 == rx_lostpkt, reg 2E/2F */ pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; @@ -890,7 +879,7 @@ static int pegasus_start_xmit(struct sk_ netif_stop_queue(net); ((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); - memcpy(pegasus->tx_buff + 2, skb->data, skb->len); + skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len); usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb, usb_sndbulkpipe(pegasus->usb, 2), pegasus->tx_buff, count, @@ -1415,8 +1404,10 @@ static void pegasus_disconnect(struct us unlink_all_urbs(pegasus); free_all_urbs(pegasus); free_skb_pool(pegasus); - if (pegasus->rx_skb) + if (pegasus->rx_skb != NULL) { dev_kfree_skb(pegasus->rx_skb); + pegasus->rx_skb = NULL; + } free_netdev(pegasus->net); } diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h index c7aadb4..c746782 100644 --- a/drivers/usb/net/pegasus.h +++ b/drivers/usb/net/pegasus.h @@ -11,7 +11,6 @@ #ifndef PEGASUS_DEV #define PEGASUS_II 0x80000000 #define HAS_HOME_PNA 0x40000000 -#define TRUST_LINK_STATUS 0x20000000 #define PEGASUS_MTU 1536 #define RX_SKBS 4 @@ -204,7 +203,7 @@ PEGASUS_DEV( "AEI USB Fast Ethernet Adap PEGASUS_DEV( "Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121, - DEFAULT_GPIO_RESET | PEGASUS_II | TRUST_LINK_STATUS ) + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Billionton USB-100", VENDOR_BILLIONTON, 0x0986, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987, diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c index 39a21c7..980e4aa 100644 --- a/drivers/usb/net/rndis_host.c +++ b/drivers/usb/net/rndis_host.c @@ -253,6 +253,7 @@ struct rndis_keepalive_c { /* IN (option * of that mess as possible. */ #define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101) +#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106) #define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e) /* @@ -349,7 +350,7 @@ static int rndis_command(struct usbnet * case RNDIS_MSG_INDICATE: { /* fault */ // struct rndis_indicate *msg = (void *)buf; dev_info(&info->control->dev, - "rndis fault indication\n"); + "rndis fault indication\n"); } break; case RNDIS_MSG_KEEPALIVE: { /* ping */ @@ -387,6 +388,71 @@ static int rndis_command(struct usbnet * return -ETIMEDOUT; } +/* + * rndis_query: + * + * Performs a query for @oid along with 0 or more bytes of payload as + * specified by @in_len. If @reply_len is not set to -1 then the reply + * length is checked against this value, resulting in an error if it + * doesn't match. + * + * NOTE: Adding a payload exactly or greater than the size of the expected + * response payload is an evident requirement MSFT added for ActiveSync. + * + * The only exception is for OIDs that return a variably sized response, + * in which case no payload should be added. This undocumented (and + * nonsensical!) issue was found by sniffing protocol requests from the + * ActiveSync 4.1 Windows driver. + */ +static int rndis_query(struct usbnet *dev, struct usb_interface *intf, + void *buf, u32 oid, u32 in_len, + void **reply, int *reply_len) +{ + int retval; + union { + void *buf; + struct rndis_msg_hdr *header; + struct rndis_query *get; + struct rndis_query_c *get_c; + } u; + u32 off, len; + + u.buf = buf; + + memset(u.get, 0, sizeof *u.get + in_len); + u.get->msg_type = RNDIS_MSG_QUERY; + u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len); + u.get->oid = oid; + u.get->len = cpu_to_le32(in_len); + u.get->offset = ccpu2(20); + + retval = rndis_command(dev, u.header); + if (unlikely(retval < 0)) { + dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", + oid, retval); + return retval; + } + + off = le32_to_cpu(u.get_c->offset); + len = le32_to_cpu(u.get_c->len); + if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE)) + goto response_error; + + if (*reply_len != -1 && len != *reply_len) + goto response_error; + + *reply = (unsigned char *) &u.get_c->request_id + off; + *reply_len = len; + + return retval; + +response_error: + dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) " + "invalid response - off %d len %d\n", + oid, off, len); + return -EDOM; +} + static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) { int retval; @@ -403,6 +469,8 @@ static int rndis_bind(struct usbnet *dev struct rndis_set_c *set_c; } u; u32 tmp; + int reply_len; + unsigned char *bp; /* we can't rely on i/o from stack working, or stack allocation */ u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); @@ -421,6 +489,12 @@ static int rndis_bind(struct usbnet *dev * TX we'll stick to one Ethernet packet plus RNDIS framing. * For RX we handle drivers that zero-pad to end-of-packet. * Don't let userspace change these settings. + * + * NOTE: there still seems to be wierdness here, as if we need + * to do some more things to make sure WinCE targets accept this. + * They default to jumbograms of 8KB or 16KB, which is absurd + * for such low data rates and which is also more than Linux + * can usually expect to allocate for SKB data... */ net->hard_header_len += sizeof (struct rndis_data_hdr); dev->hard_mtu = net->mtu + net->hard_header_len; @@ -434,7 +508,7 @@ static int rndis_bind(struct usbnet *dev if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); - goto fail_and_release; + goto fail_and_release; } tmp = le32_to_cpu(u.init_c->max_transfer_size); if (tmp < dev->hard_mtu) { @@ -450,34 +524,15 @@ static int rndis_bind(struct usbnet *dev dev->hard_mtu, tmp, dev->rx_urb_size, 1 << le32_to_cpu(u.init_c->packet_alignment)); - /* Get designated host ethernet address. - * - * Adding a payload exactly the same size as the expected response - * payload is an evident requirement MSFT added for ActiveSync. - * This undocumented (and nonsensical) issue was found by sniffing - * protocol requests from the ActiveSync 4.1 Windows driver. - */ - memset(u.get, 0, sizeof *u.get + 48); - u.get->msg_type = RNDIS_MSG_QUERY; - u.get->msg_len = ccpu2(sizeof *u.get + 48); - u.get->oid = OID_802_3_PERMANENT_ADDRESS; - u.get->len = ccpu2(48); - u.get->offset = ccpu2(20); - - retval = rndis_command(dev, u.header); - if (unlikely(retval < 0)) { + /* Get designated host ethernet address */ + reply_len = ETH_ALEN; + retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS, + 48, (void **) &bp, &reply_len); + if (unlikely(retval< 0)) { dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); goto fail_and_release; } - tmp = le32_to_cpu(u.get_c->offset); - if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN) - || u.get_c->len != ccpu2(ETH_ALEN))) { - dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", - tmp, le32_to_cpu(u.get_c->len)); - retval = -EDOM; - goto fail_and_release; - } - memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN); + memcpy(net->dev_addr, bp, ETH_ALEN); /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); @@ -502,6 +557,7 @@ static int rndis_bind(struct usbnet *dev fail_and_release: usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); + info->data = NULL; fail: kfree(u.buf); return retval; @@ -588,7 +644,7 @@ rndis_tx_fixup(struct usbnet *dev, struc if (likely((sizeof *hdr) <= room)) { skb->data = memmove(skb->head + sizeof *hdr, skb->data, len); - skb->tail = skb->data + len; + skb_set_tail_pointer(skb, len); goto fill; } } @@ -618,7 +674,7 @@ fill: static const struct driver_info rndis_info = { .description = "RNDIS device", - .flags = FLAG_ETHER | FLAG_FRAMING_RN, + .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT, .bind = rndis_bind, .unbind = rndis_unbind, .status = rndis_status, diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index ea153dc..fa598f0 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -646,7 +646,6 @@ static void fill_skb_pool(rtl8150_t *dev if (!skb) { return; } - skb->dev = dev->netdev; skb_reserve(skb, 2); dev->rx_skb_pool[i] = skb; } diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index de69b18..f9cd42d 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -203,7 +203,6 @@ void usbnet_skb_return (struct usbnet *d { int status; - skb->dev = dev->net; skb->protocol = eth_type_trans (skb, dev->net); dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; @@ -735,8 +734,7 @@ void usbnet_get_drvinfo (struct net_devi { struct usbnet *dev = netdev_priv(net); - /* REVISIT don't always return "usbnet" */ - strncpy (info->driver, driver_name, sizeof info->driver); + strncpy (info->driver, dev->driver_name, sizeof info->driver); strncpy (info->version, DRIVER_VERSION, sizeof info->version); strncpy (info->fw_version, dev->driver_info->description, sizeof info->fw_version); @@ -1116,10 +1114,12 @@ usbnet_probe (struct usb_interface *udev struct driver_info *info; struct usb_device *xdev; int status; + const char *name; + name = udev->dev.driver->name; info = (struct driver_info *) prod->driver_info; if (!info) { - dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name); + dev_dbg (&udev->dev, "blacklisted by %s\n", name); return -ENODEV; } xdev = interface_to_usbdev (udev); @@ -1139,6 +1139,7 @@ usbnet_probe (struct usb_interface *udev dev = netdev_priv(net); dev->udev = xdev; dev->driver_info = info; + dev->driver_name = name; dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK); skb_queue_head_init (&dev->rxq); diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h index 07c70ab..cbb53e0 100644 --- a/drivers/usb/net/usbnet.h +++ b/drivers/usb/net/usbnet.h @@ -29,6 +29,7 @@ struct usbnet { /* housekeeping */ struct usb_device *udev; struct driver_info *driver_info; + const char *driver_name; wait_queue_head_t *wait; struct mutex phy_mutex; diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 2f4d303..ba5d1dc 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -423,11 +423,11 @@ config USB_SERIAL_MCT_U232 module will be called mct_u232. config USB_SERIAL_MOS7720 - tristate "USB Moschip 7720 Single Port Serial Driver" + tristate "USB Moschip 7720 Serial Driver" depends on USB_SERIAL ---help--- - Say Y here if you want to use a USB Serial single port adapter from - Moschip Semiconductor Tech. + Say Y here if you want to use USB Serial single and double + port adapters from Moschip Semiconductor Tech. To compile this driver as a module, choose M here: the module will be called mos7720. diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 11dad42..b675735 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -209,6 +209,7 @@ static void aircable_send(struct usb_ser int count, result; struct aircable_private *priv = usb_get_serial_port_data(port); unsigned char* buf; + u16 *dbuf; dbg("%s - port %d", __FUNCTION__, port->number); if (port->write_urb_busy) return; @@ -226,8 +227,8 @@ static void aircable_send(struct usb_ser buf[0] = TX_HEADER_0; buf[1] = TX_HEADER_1; - buf[2] = (unsigned char)count; - buf[3] = (unsigned char)(count >> 8); + dbuf = (u16 *)&buf[2]; + *dbuf = cpu_to_le16((u16)count); serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE); memcpy(port->write_urb->transfer_buffer, buf, @@ -434,7 +435,7 @@ static void aircable_write_bulk_callback __FUNCTION__, urb->status); port->write_urb->transfer_buffer_length = 1; port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_KERNEL); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index edd6857..ea2175b 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -341,7 +341,7 @@ static int ark3116_open(struct usb_seria result = usb_serial_generic_open(port, filp); if (result) - return result; + goto err_out; /* open */ ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf); @@ -372,6 +372,7 @@ static int ark3116_open(struct usb_seria if (port->tty) ark3116_set_termios(port, &tmp_termios); +err_out: kfree(buf); return result; diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index d7d0ba9..e831cb7 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -58,9 +58,11 @@ static struct usb_device_id id_table [] { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ + { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ + { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */ { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8ff9d54..95a1805 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -342,6 +342,7 @@ static struct usb_device_id id_table_com { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, @@ -1433,6 +1434,7 @@ static int ftdi_write (struct usb_serial dbg("%s - write limit hit\n", __FUNCTION__); return 0; } + priv->tx_outstanding_urbs++; spin_unlock_irqrestore(&priv->tx_lock, flags); data_offset = priv->write_offset; @@ -1450,14 +1452,15 @@ static int ftdi_write (struct usb_serial buffer = kmalloc (transfer_size, GFP_ATOMIC); if (!buffer) { err("%s ran out of kernel memory for urb ...", __FUNCTION__); - return -ENOMEM; + count = -ENOMEM; + goto error_no_buffer; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { err("%s - no more free urbs", __FUNCTION__); - kfree (buffer); - return -ENOMEM; + count = -ENOMEM; + goto error_no_urb; } /* Copy data */ @@ -1499,10 +1502,9 @@ static int ftdi_write (struct usb_serial if (status) { err("%s - failed submitting write urb, error %d", __FUNCTION__, status); count = status; - kfree (buffer); + goto error; } else { spin_lock_irqsave(&priv->tx_lock, flags); - ++priv->tx_outstanding_urbs; priv->tx_outstanding_bytes += count; priv->tx_bytes += count; spin_unlock_irqrestore(&priv->tx_lock, flags); @@ -1510,10 +1512,19 @@ static int ftdi_write (struct usb_serial /* we are done with this urb, so let the host driver * really free it when it is finished with it */ - usb_free_urb (urb); + usb_free_urb(urb); dbg("%s write returning: %d", __FUNCTION__, count); return count; +error: + usb_free_urb(urb); +error_no_urb: + kfree (buffer); +error_no_buffer: + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_outstanding_urbs--; + spin_unlock_irqrestore(&priv->tx_lock, flags); + return count; } /* ftdi_write */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 513cfe1..77ad0a0 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -31,6 +31,7 @@ #define FTDI_232RL_PID 0xFBFA /* Produ #define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ #define FTDI_NF_RIC_PID 0x0001 /* Product Id */ +#define FTDI_USBX_707_PID 0xF857 /* ADSTech IR Blaster USBX-707 */ /* www.canusb.com Lawicel CANUSB device */ diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 6a26a2e..18f74ac 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -111,7 +111,7 @@ struct edgeport_port { struct TxFifo txfifo; /* transmit fifo -- size will be maxTxCredits */ struct urb *write_urb; /* write URB for this port */ - char write_in_progress; /* TRUE while a write URB is outstanding */ + bool write_in_progress; /* 'true' while a write URB is outstanding */ spinlock_t ep_lock; __u8 shadowLCR; /* last LCR value received */ @@ -123,11 +123,11 @@ struct edgeport_port { __u8 validDataMask; __u32 baudRate; - char open; - char openPending; - char commandPending; - char closePending; - char chaseResponsePending; + bool open; + bool openPending; + bool commandPending; + bool closePending; + bool chaseResponsePending; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ wait_queue_head_t wait_open; /* for handling sleeping while waiting for open to finish */ @@ -156,7 +156,7 @@ struct edgeport_serial { __u8 bulk_in_endpoint; /* the bulk in endpoint handle */ unsigned char * bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ struct urb * read_urb; /* our bulk read urb */ - int read_in_progress; + bool read_in_progress; spinlock_t es_lock; __u8 bulk_out_endpoint; /* the bulk out endpoint handle */ @@ -212,7 +212,7 @@ static int debug; static int low_latency = 1; /* tty low latency flag, on by default */ -static int CmdUrbs = 0; /* Number of outstanding Command Write Urbs */ +static atomic_t CmdUrbs; /* Number of outstanding Command Write Urbs */ /* local function prototypes */ @@ -631,14 +631,14 @@ static void edge_interrupt_callback (str if (edge_serial->rxBytesAvail > 0 && !edge_serial->read_in_progress) { dbg("%s - posting a read", __FUNCTION__); - edge_serial->read_in_progress = TRUE; + edge_serial->read_in_progress = true; /* we have pending bytes on the bulk in pipe, send a request */ edge_serial->read_urb->dev = edge_serial->serial->dev; result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); if (result) { dev_err(&edge_serial->serial->dev->dev, "%s - usb_submit_urb(read bulk) failed with result = %d\n", __FUNCTION__, result); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; } } spin_unlock(&edge_serial->es_lock); @@ -695,13 +695,13 @@ static void edge_bulk_in_callback (struc if (urb->status) { dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; return; } if (urb->actual_length == 0) { dbg("%s - read bulk callback with no data", __FUNCTION__); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; return; } @@ -725,10 +725,10 @@ static void edge_bulk_in_callback (struc status = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); if (status) { dev_err(&urb->dev->dev, "%s - usb_submit_urb(read bulk) failed, status = %d\n", __FUNCTION__, status); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; } } else { - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; } spin_unlock(&edge_serial->es_lock); @@ -759,7 +759,7 @@ static void edge_bulk_out_data_callback } // Release the Write URB - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; // Check if more data needs to be sent send_more_port_data((struct edgeport_serial *)(usb_get_serial_data(edge_port->port->serial)), edge_port); @@ -779,8 +779,8 @@ static void edge_bulk_out_cmd_callback ( dbg("%s", __FUNCTION__); - CmdUrbs--; - dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs); + atomic_dec(&CmdUrbs); + dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs)); /* clean up the transfer buffer */ @@ -802,7 +802,7 @@ static void edge_bulk_out_cmd_callback ( tty_wakeup(tty); /* we have completed the command */ - edge_port->commandPending = FALSE; + edge_port->commandPending = false; wake_up(&edge_port->wait_command); } @@ -868,7 +868,7 @@ static int edge_open (struct usb_serial_ port0->bulk_in_buffer, edge_serial->read_urb->transfer_buffer_length, edge_bulk_in_callback, edge_serial); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; /* start interrupt read for this edgeport * this interrupt will continue as long as the edgeport is connected */ @@ -890,26 +890,26 @@ static int edge_open (struct usb_serial_ /* initialize our port settings */ edge_port->txCredits = 0; /* Can't send any data yet */ edge_port->shadowMCR = MCR_MASTER_IE; /* Must always set this bit to enable ints! */ - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; /* send a open port command */ - edge_port->openPending = TRUE; - edge_port->open = FALSE; + edge_port->openPending = true; + edge_port->open = false; response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0); if (response < 0) { dev_err(&port->dev, "%s - error sending open port command\n", __FUNCTION__); - edge_port->openPending = FALSE; + edge_port->openPending = false; return -ENODEV; } /* now wait for the port to be completely opened */ - wait_event_timeout(edge_port->wait_open, (edge_port->openPending != TRUE), OPEN_TIMEOUT); + wait_event_timeout(edge_port->wait_open, !edge_port->openPending, OPEN_TIMEOUT); - if (edge_port->open == FALSE) { + if (!edge_port->open) { /* open timed out */ dbg("%s - open timedout", __FUNCTION__); - edge_port->openPending = FALSE; + edge_port->openPending = false; return -ENODEV; } @@ -928,7 +928,7 @@ static int edge_open (struct usb_serial_ /* Allocate a URB for the write */ edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL); - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; if (!edge_port->write_urb) { dbg("%s - no memory", __FUNCTION__); @@ -966,7 +966,7 @@ static void block_until_chase_response(s lastCredits = edge_port->txCredits; // Did we get our Chase response - if (edge_port->chaseResponsePending == FALSE) { + if (!edge_port->chaseResponsePending) { dbg("%s - Got Chase Response", __FUNCTION__); // did we get all of our credit back? @@ -985,7 +985,7 @@ static void block_until_chase_response(s // No activity.. count down. loop--; if (loop == 0) { - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; dbg("%s - Chase TIMEOUT", __FUNCTION__); return; } @@ -1068,13 +1068,13 @@ static void edge_close (struct usb_seria // block until tx is empty block_until_tx_empty(edge_port); - edge_port->closePending = TRUE; + edge_port->closePending = true; if ((!edge_serial->is_epic) || ((edge_serial->is_epic) && (edge_serial->epic_descriptor.Supports.IOSPChase))) { /* flush and chase */ - edge_port->chaseResponsePending = TRUE; + edge_port->chaseResponsePending = true; dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); @@ -1082,7 +1082,7 @@ static void edge_close (struct usb_seria // block until chase finished block_until_chase_response(edge_port); } else { - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; } } @@ -1094,10 +1094,10 @@ static void edge_close (struct usb_seria send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); } - //port->close = TRUE; - edge_port->closePending = FALSE; - edge_port->open = FALSE; - edge_port->openPending = FALSE; + //port->close = true; + edge_port->closePending = false; + edge_port->open = false; + edge_port->openPending = false; usb_kill_urb(edge_port->write_urb); @@ -1247,7 +1247,7 @@ static void send_more_port_data(struct e } // lock this write - edge_port->write_in_progress = TRUE; + edge_port->write_in_progress = true; // get a pointer to the write_urb urb = edge_port->write_urb; @@ -1261,7 +1261,7 @@ static void send_more_port_data(struct e buffer = kmalloc (count+2, GFP_ATOMIC); if (buffer == NULL) { dev_err(&edge_port->port->dev, "%s - no more kernel memory...\n", __FUNCTION__); - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; goto exit_send; } buffer[0] = IOSP_BUILD_DATA_HDR1 (edge_port->port->number - edge_port->port->serial->minor, count); @@ -1301,7 +1301,7 @@ static void send_more_port_data(struct e if (status) { /* something went wrong */ dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", __FUNCTION__, status); - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; /* revert the credits as something bad happened. */ edge_port->txCredits += count; @@ -1332,7 +1332,7 @@ static int edge_write_room (struct usb_s if (edge_port == NULL) return -ENODEV; - if (edge_port->closePending == TRUE) + if (edge_port->closePending) return -ENODEV; dbg("%s - port %d", __FUNCTION__, port->number); @@ -1371,7 +1371,7 @@ static int edge_chars_in_buffer (struct if (edge_port == NULL) return -ENODEV; - if (edge_port->closePending == TRUE) + if (edge_port->closePending) return -ENODEV; if (!edge_port->open) { @@ -1762,7 +1762,7 @@ static void edge_break (struct usb_seria ((edge_serial->is_epic) && (edge_serial->epic_descriptor.Supports.IOSPChase))) { /* flush and chase */ - edge_port->chaseResponsePending = TRUE; + edge_port->chaseResponsePending = true; dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); @@ -1770,7 +1770,7 @@ static void edge_break (struct usb_seria // block until chase finished block_until_chase_response(edge_port); } else { - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; } } @@ -1952,13 +1952,13 @@ static void process_rcvd_status (struct // Also, we currently clear flag and close the port regardless of content of above's Byte3. // We could choose to do something else when Byte3 says Timeout on Chase from Edgeport, // like wait longer in block_until_chase_response, but for now we don't. - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; wake_up (&edge_port->wait_chase); return; case IOSP_EXT_STATUS_RX_CHECK_RSP: dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", __FUNCTION__, edge_serial->rxPort, byte3 ); - //Port->RxCheckRsp = TRUE; + //Port->RxCheckRsp = true; return; } } @@ -1974,8 +1974,8 @@ static void process_rcvd_status (struct change_port_settings (edge_port, edge_port->port->tty->termios); /* we have completed the open */ - edge_port->openPending = FALSE; - edge_port->open = TRUE; + edge_port->openPending = false; + edge_port->open = true; wake_up(&edge_port->wait_open); return; } @@ -1983,7 +1983,7 @@ static void process_rcvd_status (struct // If port is closed, silently discard all rcvd status. We can // have cases where buffered status is received AFTER the close // port command is sent to the Edgeport. - if ((!edge_port->open ) || (edge_port->closePending)) { + if (!edge_port->open || edge_port->closePending) { return; } @@ -1991,14 +1991,14 @@ static void process_rcvd_status (struct // Not currently sent by Edgeport case IOSP_STATUS_LSR: dbg("%s - Port %u LSR Status = %02x", __FUNCTION__, edge_serial->rxPort, byte2); - handle_new_lsr (edge_port, FALSE, byte2, 0); + handle_new_lsr(edge_port, false, byte2, 0); break; case IOSP_STATUS_LSR_DATA: dbg("%s - Port %u LSR Status = %02x, Data = %02x", __FUNCTION__, edge_serial->rxPort, byte2, byte3); // byte2 is LSR Register // byte3 is broken data byte - handle_new_lsr (edge_port, TRUE, byte2, byte3); + handle_new_lsr(edge_port, true, byte2, byte3); break; // // case IOSP_EXT_4_STATUS: @@ -2317,14 +2317,14 @@ static int write_cmd_usb (struct edgepor if (!urb) return -ENOMEM; - CmdUrbs++; - dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs); + atomic_inc(&CmdUrbs); + dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs)); usb_fill_bulk_urb (urb, edge_serial->serial->dev, usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint), buffer, length, edge_bulk_out_cmd_callback, edge_port); - edge_port->commandPending = TRUE; + edge_port->commandPending = true; status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { @@ -2332,16 +2332,16 @@ static int write_cmd_usb (struct edgepor dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __FUNCTION__, status); usb_kill_urb(urb); usb_free_urb(urb); - CmdUrbs--; + atomic_dec(&CmdUrbs); return status; } // wait for command to finish timeout = COMMAND_TIMEOUT; #if 0 - wait_event (&edge_port->wait_command, (edge_port->commandPending == FALSE)); + wait_event (&edge_port->wait_command, !edge_port->commandPending); - if (edge_port->commandPending == TRUE) { + if (edge_port->commandPending) { /* command timed out */ dbg("%s - command timed out", __FUNCTION__); status = -EINVAL; @@ -2524,8 +2524,8 @@ static void change_port_settings (struct dbg("%s - port %d", __FUNCTION__, edge_port->port->number); - if ((!edge_port->open) && - (!edge_port->openPending)) { + if (!edge_port->open && + !edge_port->openPending) { dbg("%s - port not opened", __FUNCTION__); return; } @@ -2836,9 +2836,9 @@ static int edge_startup (struct usb_seri struct usb_device *dev; int i, j; int response; - int interrupt_in_found; - int bulk_in_found; - int bulk_out_found; + bool interrupt_in_found; + bool bulk_in_found; + bool bulk_out_found; static __u32 descriptor[3] = { EDGE_COMPATIBILITY_MASK0, EDGE_COMPATIBILITY_MASK1, EDGE_COMPATIBILITY_MASK2 }; @@ -2936,14 +2936,14 @@ static int edge_startup (struct usb_seri if (edge_serial->is_epic) { /* EPIC thing, set up our interrupt polling now and our read urb, so * that the device knows it really is connected. */ - interrupt_in_found = bulk_in_found = bulk_out_found = FALSE; + interrupt_in_found = bulk_in_found = bulk_out_found = false; for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) { struct usb_endpoint_descriptor *endpoint; int buffer_size; endpoint = &serial->interface->altsetting[0].endpoint[i].desc; buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); - if ((!interrupt_in_found) && + if (!interrupt_in_found && (usb_endpoint_is_int_in(endpoint))) { /* we found a interrupt in endpoint */ dbg("found interrupt in"); @@ -2972,10 +2972,10 @@ static int edge_startup (struct usb_seri edge_serial, endpoint->bInterval); - interrupt_in_found = TRUE; + interrupt_in_found = true; } - if ((!bulk_in_found) && + if (!bulk_in_found && (usb_endpoint_is_bulk_in(endpoint))) { /* we found a bulk in endpoint */ dbg("found bulk in"); @@ -3001,19 +3001,19 @@ static int edge_startup (struct usb_seri endpoint->wMaxPacketSize, edge_bulk_in_callback, edge_serial); - bulk_in_found = TRUE; + bulk_in_found = true; } - if ((!bulk_out_found) && + if (!bulk_out_found && (usb_endpoint_is_bulk_out(endpoint))) { /* we found a bulk out endpoint */ dbg("found bulk out"); edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress; - bulk_out_found = TRUE; + bulk_out_found = true; } } - if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) { + if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) { err ("Error - the proper endpoints were not found!"); return -ENODEV; } @@ -3083,6 +3083,7 @@ static int __init edgeport_init(void) retval = usb_register(&io_driver); if (retval) goto failed_usb_register; + atomic_set(&CmdUrbs, 0); info(DRIVER_DESC " " DRIVER_VERSION); return 0; diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 29a913a..cb201c1 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -19,12 +19,6 @@ #define _IO_EDGEPORT_H_ #define MAX_RS232_PORTS 8 /* Max # of RS-232 ports per device */ /* typedefs that the insideout headers need */ -#ifndef TRUE - #define TRUE (1) -#endif -#ifndef FALSE - #define FALSE (0) -#endif #ifndef LOW8 #define LOW8(a) ((unsigned char)(a & 0xff)) #endif diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index d16e2e1..4df0ec7 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -255,6 +255,7 @@ static struct usb_device_id ipaq_id_tabl { USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */ { USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */ { USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */ + { USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */ { USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */ { USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */ { USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */ diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index b2097c4..7b085f3 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -238,7 +238,7 @@ static int klsi_105_get_line_state(struc if (rc < 0) err("Reading line status failed (error = %d)", rc); else { - status = status_buf[0] + (status_buf[1]<<8); + status = le16_to_cpu(*(u16 *)status_buf); info("%s - read status %x %x", __FUNCTION__, status_buf[0], status_buf[1]); @@ -257,7 +257,7 @@ static int klsi_105_get_line_state(struc static int klsi_105_startup (struct usb_serial *serial) { struct klsi_105_private *priv; - int i; + int i, j; /* check if we support the product id (see keyspan.c) * FIXME @@ -265,12 +265,12 @@ static int klsi_105_startup (struct usb_ /* allocate the private data structure */ for (i=0; inum_ports; i++) { - int j; priv = kmalloc(sizeof(struct klsi_105_private), GFP_KERNEL); if (!priv) { dbg("%skmalloc for klsi_105_private failed.", __FUNCTION__); - return -ENOMEM; + i--; + goto err_cleanup; } /* set initial values for control structures */ priv->cfg.pktlen = 5; @@ -292,15 +292,14 @@ static int klsi_105_startup (struct usb_ priv->write_urb_pool[j] = urb; if (urb == NULL) { err("No more urbs???"); - continue; + goto err_cleanup; } - urb->transfer_buffer = NULL; urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { err("%s - out of memory for urb buffers.", __FUNCTION__); - continue; + goto err_cleanup; } } @@ -308,7 +307,20 @@ static int klsi_105_startup (struct usb_ init_waitqueue_head(&serial->port[i]->write_wait); } - return (0); + return 0; + +err_cleanup: + for (; i >= 0; i--) { + priv = usb_get_serial_port_data(serial->port[i]); + for (j=0; j < NUM_URBS; j++) { + if (priv->write_urb_pool[j]) { + kfree(priv->write_urb_pool[j]->transfer_buffer); + usb_free_urb(priv->write_urb_pool[j]); + } + } + usb_set_serial_port_data(serial->port[i], NULL); + } + return -ENOMEM; } /* klsi_105_startup */ diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 4cd839b..3db1adc 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -438,17 +438,21 @@ static int mct_u232_open (struct usb_se if (retval) { err("usb_submit_urb(read bulk) failed pipe 0x%x err %d", port->read_urb->pipe, retval); - goto exit; + goto error; } port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (retval) + if (retval) { + usb_kill_urb(port->read_urb); err(" usb_submit_urb(read int) failed pipe 0x%x err %d", port->interrupt_in_urb->pipe, retval); - -exit: + goto error; + } return 0; + +error: + return retval; } /* mct_u232_open */ diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 19bf403..b563e2a 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -103,11 +103,9 @@ static void mos7720_interrupt_callback(s { int result; int length; - __u32 *data; - unsigned int status; + __u8 *data; __u8 sp1; __u8 sp2; - __u8 st; dbg("%s"," : Entering\n"); @@ -141,18 +139,19 @@ static void mos7720_interrupt_callback(s * Byte 2 IIR Port 2 (port.number is 1) * Byte 3 -------------- * Byte 4 FIFO status for both */ - if (length && length > 4) { + + /* the above description is inverted + * oneukum 2007-03-14 */ + + if (unlikely(length != 4)) { dbg("Wrong data !!!"); return; } - status = *data; - - sp1 = (status & 0xff000000)>>24; - sp2 = (status & 0x00ff0000)>>16; - st = status & 0x000000ff; + sp1 = data[3]; + sp2 = data[2]; - if ((sp1 & 0x01) || (sp2 & 0x01)) { + if ((sp1 | sp2) & 0x01) { /* No Interrupt Pending in both the ports */ dbg("No Interrupt !!!"); } else { @@ -333,6 +332,7 @@ static int mos7720_open(struct usb_seria int response; int port_number; char data; + int allocated_urbs = 0; int j; serial = port->serial; @@ -353,7 +353,7 @@ static int mos7720_open(struct usb_seria /* Initialising the write urb pool */ for (j = 0; j < NUM_URBS; ++j) { - urb = usb_alloc_urb(0,GFP_ATOMIC); + urb = usb_alloc_urb(0,GFP_KERNEL); mos7720_port->write_urb_pool[j] = urb; if (urb == NULL) { @@ -365,10 +365,16 @@ static int mos7720_open(struct usb_seria GFP_KERNEL); if (!urb->transfer_buffer) { err("%s-out of memory for urb buffers.", __FUNCTION__); + usb_free_urb(mos7720_port->write_urb_pool[j]); + mos7720_port->write_urb_pool[j] = NULL; continue; } + allocated_urbs++; } + if (!allocated_urbs) + return -ENOMEM; + /* Initialize MCS7720 -- Write Init values to corresponding Registers * * Register Index @@ -526,7 +532,7 @@ static int mos7720_chars_in_buffer(struc } for (i = 0; i < NUM_URBS; ++i) { - if (mos7720_port->write_urb_pool[i]->status == -EINPROGRESS) + if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status == -EINPROGRESS) chars += URB_TRANSFER_BUFFER_SIZE; } dbg("%s - returns %d", __FUNCTION__, chars); @@ -629,7 +635,7 @@ static int mos7720_write_room(struct usb } for (i = 0; i < NUM_URBS; ++i) { - if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) + if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) room += URB_TRANSFER_BUFFER_SIZE; } @@ -664,7 +670,7 @@ static int mos7720_write(struct usb_seri urb = NULL; for (i = 0; i < NUM_URBS; ++i) { - if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) { + if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) { urb = mos7720_port->write_urb_pool[i]; dbg("URB:%d",i); break; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c6cca85..2366e7b 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -176,9 +176,12 @@ struct moschip_port { int port_num; /*Actual port number in the device(1,2,etc) */ struct urb *write_urb; /* write URB for this port */ struct urb *read_urb; /* read URB for this port */ + struct urb *int_urb; __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ char open; + char open_ports; + char zombie; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */ int delta_msr_cond; @@ -191,17 +194,17 @@ struct moschip_port { __u8 DcrRegOffset; //for processing control URBS in interrupt context struct urb *control_urb; + struct usb_ctrlrequest *dr; char *ctrl_buf; int MsrLsr; + spinlock_t pool_lock; struct urb *write_urb_pool[NUM_URBS]; + char busy[NUM_URBS]; }; static int debug; -static int mos7840_num_ports; //this says the number of ports in the device -static int mos7840_num_open_ports; - /* * mos7840_set_reg_sync @@ -254,7 +257,7 @@ static int mos7840_set_uart_reg(struct u struct usb_device *dev = port->serial->dev; val = val & 0x00ff; // For the UART control registers, the application number need to be Or'ed - if (mos7840_num_ports == 4) { + if (port->serial->num_ports == 4) { val |= (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; @@ -294,7 +297,7 @@ static int mos7840_get_uart_reg(struct u //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); /*Wval is same as application number */ - if (mos7840_num_ports == 4) { + if (port->serial->num_ports == 4) { Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; @@ -352,7 +355,7 @@ static inline struct moschip_port *mos78 return (struct moschip_port *)usb_get_serial_port_data(port); } -static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) +static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) { struct moschip_port *mos7840_port; struct async_icount *icount; @@ -366,22 +369,24 @@ static int mos7840_handle_new_msr(struct /* update input line counters */ if (new_msr & MOS_MSR_DELTA_CTS) { icount->cts++; + smp_wmb(); } if (new_msr & MOS_MSR_DELTA_DSR) { icount->dsr++; + smp_wmb(); } if (new_msr & MOS_MSR_DELTA_CD) { icount->dcd++; + smp_wmb(); } if (new_msr & MOS_MSR_DELTA_RI) { icount->rng++; + smp_wmb(); } } - - return 0; } -static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) +static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) { struct async_icount *icount; @@ -400,18 +405,20 @@ static int mos7840_handle_new_lsr(struct icount = &port->icount; if (new_lsr & SERIAL_LSR_BI) { icount->brk++; + smp_wmb(); } if (new_lsr & SERIAL_LSR_OE) { icount->overrun++; + smp_wmb(); } if (new_lsr & SERIAL_LSR_PE) { icount->parity++; + smp_wmb(); } if (new_lsr & SERIAL_LSR_FE) { icount->frame++; + smp_wmb(); } - - return 0; } /************************************************************************/ @@ -426,12 +433,15 @@ static void mos7840_control_callback(str unsigned char *data; struct moschip_port *mos7840_port; __u8 regval = 0x0; + int result = 0; if (!urb) { dbg("%s", "Invalid Pointer !!!!:\n"); return; } + mos7840_port = (struct moschip_port *)urb->context; + switch (urb->status) { case 0: /* success */ @@ -449,8 +459,6 @@ static void mos7840_control_callback(str goto exit; } - mos7840_port = (struct moschip_port *)urb->context; - dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length); dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__, mos7840_port->MsrLsr, mos7840_port->port_num); @@ -462,21 +470,26 @@ static void mos7840_control_callback(str else if (mos7840_port->MsrLsr == 1) mos7840_handle_new_lsr(mos7840_port, regval); - exit: - return; +exit: + spin_lock(&mos7840_port->pool_lock); + if (!mos7840_port->zombie) + result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC); + spin_unlock(&mos7840_port->pool_lock); + if (result) { + dev_err(&urb->dev->dev, + "%s - Error %d submitting interrupt urb\n", + __FUNCTION__, result); + } } static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, __u16 * val) { struct usb_device *dev = mcs->port->serial->dev; - struct usb_ctrlrequest *dr = NULL; - unsigned char *buffer = NULL; - int ret = 0; - buffer = (__u8 *) mcs->ctrl_buf; + struct usb_ctrlrequest *dr = mcs->dr; + unsigned char *buffer = mcs->ctrl_buf; + int ret; -// dr=(struct usb_ctrlrequest *)(buffer); - dr = (void *)(buffer + 2); dr->bRequestType = MCS_RD_RTYPE; dr->bRequest = MCS_RDREQ; dr->wValue = cpu_to_le16(Wval); //0; @@ -506,8 +519,8 @@ static void mos7840_interrupt_callback(s __u16 Data; unsigned char *data; __u8 sp[5], st; - int i; - __u16 wval; + int i, rv = 0; + __u16 wval, wreg = 0; dbg("%s", " : Entering\n"); if (!urb) { @@ -569,31 +582,34 @@ static void mos7840_interrupt_callback(s dbg("Serial Port %d: Receiver status error or ", i); dbg("address bit detected in 9-bit mode\n"); mos7840_port->MsrLsr = 1; - mos7840_get_reg(mos7840_port, wval, - LINE_STATUS_REGISTER, - &Data); + wreg = LINE_STATUS_REGISTER; break; case SERIAL_IIR_MS: dbg("Serial Port %d: Modem status change\n", i); mos7840_port->MsrLsr = 0; - mos7840_get_reg(mos7840_port, wval, - MODEM_STATUS_REGISTER, - &Data); + wreg = MODEM_STATUS_REGISTER; break; } + spin_lock(&mos7840_port->pool_lock); + if (!mos7840_port->zombie) { + rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data); + } else { + spin_unlock(&mos7840_port->pool_lock); + return; + } + spin_unlock(&mos7840_port->pool_lock); } } } - exit: + if (!(rv < 0)) /* the completion handler for the control urb will resubmit */ + return; +exit: result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n", __FUNCTION__, result); } - - return; - } static int mos7840_port_paranoia_check(struct usb_serial_port *port, @@ -634,7 +650,8 @@ static struct usb_serial *mos7840_get_us if (!port || mos7840_port_paranoia_check(port, function) || mos7840_serial_paranoia_check(port->serial, function)) { - /* then say that we don't have a valid usb_serial thing, which will * end up genrating -ENODEV return values */ + /* then say that we don't have a valid usb_serial thing, which will + * end up genrating -ENODEV return values */ return NULL; } @@ -699,6 +716,7 @@ static void mos7840_bulk_in_callback(str tty_flip_buffer_push(tty); } mos7840_port->icount.rx += urb->actual_length; + smp_wmb(); dbg("mos7840_port->icount.rx is %d:\n", mos7840_port->icount.rx); } @@ -708,15 +726,14 @@ static void mos7840_bulk_in_callback(str return; } - if (mos7840_port->read_urb->status != -EINPROGRESS) { - mos7840_port->read_urb->dev = serial->dev; - status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + mos7840_port->read_urb->dev = serial->dev; - if (status) { - dbg(" usb_submit_urb(read bulk) failed, status = %d", - status); - } + status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + + if (status) { + dbg(" usb_submit_urb(read bulk) failed, status = %d", + status); } } @@ -730,17 +747,28 @@ static void mos7840_bulk_out_data_callba { struct moschip_port *mos7840_port; struct tty_struct *tty; + int i; + if (!urb) { dbg("%s", "Invalid Pointer !!!!:\n"); return; } + mos7840_port = (struct moschip_port *)urb->context; + spin_lock(&mos7840_port->pool_lock); + for (i = 0; i < NUM_URBS; i++) { + if (urb == mos7840_port->write_urb_pool[i]) { + mos7840_port->busy[i] = 0; + break; + } + } + spin_unlock(&mos7840_port->pool_lock); + if (urb->status) { dbg("nonzero write bulk status received:%d\n", urb->status); return; } - mos7840_port = (struct moschip_port *)urb->context; if (!mos7840_port) { dbg("%s", "NULL mos7840_port pointer \n"); return; @@ -792,13 +820,13 @@ static int mos7840_open(struct usb_seria __u16 Data; int status; struct moschip_port *mos7840_port; + struct moschip_port *port0; if (mos7840_port_paranoia_check(port, __FUNCTION__)) { dbg("%s", "Port Paranoia failed \n"); return -ENODEV; } - mos7840_num_open_ports++; serial = port->serial; if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) { @@ -807,16 +835,18 @@ static int mos7840_open(struct usb_seria } mos7840_port = mos7840_get_port_private(port); + port0 = mos7840_get_port_private(serial->port[0]); - if (mos7840_port == NULL) + if (mos7840_port == NULL || port0 == NULL) return -ENODEV; usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); + port0->open_ports++; /* Initialising the write urb pool */ for (j = 0; j < NUM_URBS; ++j) { - urb = usb_alloc_urb(0, GFP_ATOMIC); + urb = usb_alloc_urb(0, GFP_KERNEL); mos7840_port->write_urb_pool[j] = urb; if (urb == NULL) { @@ -824,10 +854,10 @@ static int mos7840_open(struct usb_seria continue; } - urb->transfer_buffer = NULL; - urb->transfer_buffer = - kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); + urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { + usb_free_urb(urb); + mos7840_port->write_urb_pool[j] = NULL; err("%s-out of memory for urb buffers.", __FUNCTION__); continue; } @@ -879,9 +909,7 @@ static int mos7840_open(struct usb_seria } Data |= 0x08; //Driver done bit Data |= 0x20; //rx_disable - status = 0; - status = - mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); if (status < 0) { dbg("writing Controlreg failed\n"); return -1; @@ -893,7 +921,6 @@ static int mos7840_open(struct usb_seria //////////////////////////////////// Data = 0x00; - status = 0; status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); if (status < 0) { dbg("disableing interrupts failed\n"); @@ -901,7 +928,6 @@ static int mos7840_open(struct usb_seria } // Set FIFO_CONTROL_REGISTER to the default value Data = 0x00; - status = 0; status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); if (status < 0) { dbg("Writing FIFO_CONTROL_REGISTER failed\n"); @@ -909,7 +935,6 @@ static int mos7840_open(struct usb_seria } Data = 0xcf; - status = 0; status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); if (status < 0) { dbg("Writing FIFO_CONTROL_REGISTER failed\n"); @@ -917,22 +942,18 @@ static int mos7840_open(struct usb_seria } Data = 0x03; - status = 0; status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); mos7840_port->shadowLCR = Data; Data = 0x0b; - status = 0; status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); mos7840_port->shadowMCR = Data; Data = 0x00; - status = 0; status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); mos7840_port->shadowLCR = Data; Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 - status = 0; status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); Data = 0x0c; @@ -999,7 +1020,7 @@ static int mos7840_open(struct usb_seria /* Check to see if we've set up our endpoint info yet * * (can't set it up in mos7840_startup as the structures * * were not set up at that time.) */ - if (mos7840_num_open_ports == 1) { + if (port0->open_ports == 1) { if (serial->port[0]->interrupt_in_buffer == NULL) { /* set up interrupt urb */ @@ -1097,6 +1118,7 @@ static int mos7840_chars_in_buffer(struc { int i; int chars = 0; + unsigned long flags; struct moschip_port *mos7840_port; dbg("%s \n", " mos7840_chars_in_buffer:entering ..........."); @@ -1112,13 +1134,15 @@ static int mos7840_chars_in_buffer(struc return -1; } + spin_lock_irqsave(&mos7840_port->pool_lock,flags); for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) { + if (mos7840_port->busy[i]) { chars += URB_TRANSFER_BUFFER_SIZE; } } + spin_unlock_irqrestore(&mos7840_port->pool_lock,flags); dbg("%s - returns %d", __FUNCTION__, chars); - return (chars); + return chars; } @@ -1172,6 +1196,7 @@ static void mos7840_close(struct usb_ser { struct usb_serial *serial; struct moschip_port *mos7840_port; + struct moschip_port *port0; int j; __u16 Data; @@ -1189,10 +1214,10 @@ static void mos7840_close(struct usb_ser } mos7840_port = mos7840_get_port_private(port); + port0 = mos7840_get_port_private(serial->port[0]); - if (mos7840_port == NULL) { + if (mos7840_port == NULL || port0 == NULL) return; - } for (j = 0; j < NUM_URBS; ++j) usb_kill_urb(mos7840_port->write_urb_pool[j]); @@ -1234,12 +1259,13 @@ static void mos7840_close(struct usb_ser } // if(mos7840_port->ctrl_buf != NULL) // kfree(mos7840_port->ctrl_buf); - mos7840_num_open_ports--; + port0->open_ports--; dbg("mos7840_num_open_ports in close%d:in port%d\n", - mos7840_num_open_ports, port->number); - if (mos7840_num_open_ports == 0) { + port0->open_ports, port->number); + if (port0->open_ports == 0) { if (serial->port[0]->interrupt_in_urb) { dbg("%s", "Shutdown interrupt_in_urb\n"); + usb_kill_urb(serial->port[0]->interrupt_in_urb); } } @@ -1368,6 +1394,7 @@ static int mos7840_write_room(struct usb { int i; int room = 0; + unsigned long flags; struct moschip_port *mos7840_port; dbg("%s \n", " mos7840_write_room:entering ..........."); @@ -1384,14 +1411,17 @@ static int mos7840_write_room(struct usb return -1; } + spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) { + if (!mos7840_port->busy[i]) { room += URB_TRANSFER_BUFFER_SIZE; } } + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); + room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1; dbg("%s - returns %d", __FUNCTION__, room); - return (room); + return room; } @@ -1410,6 +1440,7 @@ static int mos7840_write(struct usb_seri int i; int bytes_sent = 0; int transfer_size; + unsigned long flags; struct moschip_port *mos7840_port; struct usb_serial *serial; @@ -1476,13 +1507,16 @@ #endif /* try to find a free urb in the list */ urb = NULL; + spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) { + if (!mos7840_port->busy[i]) { + mos7840_port->busy[i] = 1; urb = mos7840_port->write_urb_pool[i]; dbg("\nURB:%d", i); break; } } + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); if (urb == NULL) { dbg("%s - no more free urbs", __FUNCTION__); @@ -1518,6 +1552,7 @@ #endif status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { + mos7840_port->busy[i] = 0; err("%s - usb_submit_urb(write bulk) failed with status = %d", __FUNCTION__, status); bytes_sent = status; @@ -1525,6 +1560,7 @@ #endif } bytes_sent = transfer_size; mos7840_port->icount.tx += transfer_size; + smp_wmb(); dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); exit: @@ -2490,6 +2526,7 @@ static int mos7840_ioctl(struct usb_seri if (signal_pending(current)) return -ERESTARTSYS; cnow = mos7840_port->icount; + smp_rmb(); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ @@ -2506,6 +2543,7 @@ static int mos7840_ioctl(struct usb_seri case TIOCGICOUNT: cnow = mos7840_port->icount; + smp_rmb(); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; @@ -2535,19 +2573,18 @@ static int mos7840_ioctl(struct usb_seri static int mos7840_calc_num_ports(struct usb_serial *serial) { + int mos7840_num_ports = 0; dbg("numberofendpoints: %d \n", (int)serial->interface->cur_altsetting->desc.bNumEndpoints); dbg("numberofendpoints: %d \n", (int)serial->interface->altsetting->desc.bNumEndpoints); if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) { - mos7840_num_ports = 2; - serial->type->num_ports = 2; + mos7840_num_ports = serial->num_ports = 2; } else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) { - mos7840_num_ports = 4; - serial->type->num_bulk_in = 4; - serial->type->num_bulk_out = 4; - serial->type->num_ports = 4; + serial->num_bulk_in = 4; + serial->num_bulk_out = 4; + mos7840_num_ports = serial->num_ports = 4; } return mos7840_num_ports; @@ -2583,7 +2620,9 @@ static int mos7840_startup(struct usb_se mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); if (mos7840_port == NULL) { err("%s - Out of memory", __FUNCTION__); - return -ENOMEM; + status = -ENOMEM; + i--; /* don't follow NULL pointer cleaning up */ + goto error; } /* Initialize all port interrupt end point to port 0 int endpoint * @@ -2591,6 +2630,7 @@ static int mos7840_startup(struct usb_se mos7840_port->port = serial->port[i]; mos7840_set_port_private(serial->port[i], mos7840_port); + spin_lock_init(&mos7840_port->pool_lock); mos7840_port->port_num = ((serial->port[i]->number - (serial->port[i]->serial->minor)) + @@ -2601,22 +2641,22 @@ static int mos7840_startup(struct usb_se mos7840_port->ControlRegOffset = 0x1; mos7840_port->DcrRegOffset = 0x4; } else if ((mos7840_port->port_num == 2) - && (mos7840_num_ports == 4)) { + && (serial->num_ports == 4)) { mos7840_port->SpRegOffset = 0x8; mos7840_port->ControlRegOffset = 0x9; mos7840_port->DcrRegOffset = 0x16; } else if ((mos7840_port->port_num == 2) - && (mos7840_num_ports == 2)) { + && (serial->num_ports == 2)) { mos7840_port->SpRegOffset = 0xa; mos7840_port->ControlRegOffset = 0xb; mos7840_port->DcrRegOffset = 0x19; } else if ((mos7840_port->port_num == 3) - && (mos7840_num_ports == 4)) { + && (serial->num_ports == 4)) { mos7840_port->SpRegOffset = 0xa; mos7840_port->ControlRegOffset = 0xb; mos7840_port->DcrRegOffset = 0x19; } else if ((mos7840_port->port_num == 4) - && (mos7840_num_ports == 4)) { + && (serial->num_ports == 4)) { mos7840_port->SpRegOffset = 0xc; mos7840_port->ControlRegOffset = 0xd; mos7840_port->DcrRegOffset = 0x1c; @@ -2701,21 +2741,19 @@ static int mos7840_startup(struct usb_se dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status); Data = 0x20; - status = 0; status = mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER, Data); if (status < 0) { dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); - break; + goto error; } else dbg("CLK_MULTI_REGISTER Writing success status%d\n", status); //write value 0x0 to scratchpad register Data = 0x00; - status = 0; status = mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER, Data); @@ -2729,7 +2767,7 @@ static int mos7840_startup(struct usb_se //Zero Length flag register if ((mos7840_port->port_num != 1) - && (mos7840_num_ports == 2)) { + && (serial->num_ports == 2)) { Data = 0xff; status = 0; @@ -2770,14 +2808,17 @@ static int mos7840_startup(struct usb_se i + 1, status); } - mos7840_port->control_urb = usb_alloc_urb(0, GFP_ATOMIC); + mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL); mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL); - + mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || !mos7840_port->dr) { + status = -ENOMEM; + goto error; + } } //Zero Length flag enable Data = 0x0f; - status = 0; status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data); if (status < 0) { dbg("Writing ZLP_REG5 failed status-0x%x\n", status); @@ -2789,6 +2830,17 @@ static int mos7840_startup(struct usb_se usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ); return 0; +error: + for (/* nothing */; i >= 0; i--) { + mos7840_port = mos7840_get_port_private(serial->port[i]); + + kfree(mos7840_port->dr); + kfree(mos7840_port->ctrl_buf); + usb_free_urb(mos7840_port->control_urb); + kfree(mos7840_port); + serial->port[i] = NULL; + } + return status; } /**************************************************************************** @@ -2799,6 +2851,7 @@ static int mos7840_startup(struct usb_se static void mos7840_shutdown(struct usb_serial *serial) { int i; + unsigned long flags; struct moschip_port *mos7840_port; dbg("%s \n", " shutdown :entering.........."); @@ -2814,8 +2867,12 @@ static void mos7840_shutdown(struct usb_ for (i = 0; i < serial->num_ports; ++i) { mos7840_port = mos7840_get_port_private(serial->port[i]); - kfree(mos7840_port->ctrl_buf); + spin_lock_irqsave(&mos7840_port->pool_lock, flags); + mos7840_port->zombie = 1; + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); usb_kill_urb(mos7840_port->control_urb); + kfree(mos7840_port->ctrl_buf); + kfree(mos7840_port->dr); kfree(mos7840_port); mos7840_set_port_private(serial->port[i], NULL); } diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 0216ac1..4adfab9 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -69,6 +69,7 @@ static void omninet_write_bulk_callback static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count); static int omninet_write_room (struct usb_serial_port *port); static void omninet_shutdown (struct usb_serial *serial); +static int omninet_attach (struct usb_serial *serial); static struct usb_device_id id_table [] = { { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) }, @@ -99,6 +100,7 @@ static struct usb_serial_driver zyxel_om .num_bulk_in = 1, .num_bulk_out = 2, .num_ports = 1, + .attach = omninet_attach, .open = omninet_open, .close = omninet_close, .write = omninet_write, @@ -145,22 +147,30 @@ struct omninet_data __u8 od_outseq; // Sequence number for bulk_out URBs }; +static int omninet_attach (struct usb_serial *serial) +{ + struct omninet_data *od; + struct usb_serial_port *port = serial->port[0]; + + od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); + if( !od ) { + err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data)); + return -ENOMEM; + } + usb_set_serial_port_data(port, od); + return 0; +} + static int omninet_open (struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport; - struct omninet_data *od; + struct omninet_data *od = usb_get_serial_port_data(port); int result = 0; dbg("%s - port %d", __FUNCTION__, port->number); od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); - if( !od ) { - err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data)); - return -ENOMEM; - } - - usb_set_serial_port_data(port, od); wport = serial->port[1]; wport->tty = port->tty; @@ -170,24 +180,17 @@ static int omninet_open (struct usb_seri port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, omninet_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) + if (result) { err("%s - failed submitting read urb, error %d", __FUNCTION__, result); + } return result; } static void omninet_close (struct usb_serial_port *port, struct file * filp) { - struct usb_serial *serial = port->serial; - struct usb_serial_port *wport; - dbg("%s - port %d", __FUNCTION__, port->number); - - wport = serial->port[1]; - usb_kill_urb(wport->write_urb); usb_kill_urb(port->read_urb); - - kfree(usb_get_serial_port_data(port)); } @@ -326,7 +329,12 @@ static void omninet_write_bulk_callback static void omninet_shutdown (struct usb_serial *serial) { + struct usb_serial_port *wport = serial->port[1]; + struct usb_serial_port *port = serial->port[0]; dbg ("%s", __FUNCTION__); + + usb_kill_urb(wport->write_urb); + kfree(usb_get_serial_port_data(port)); } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e178e6f..8c3f55b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -113,6 +113,12 @@ #define NOVATELWIRELESS_VENDOR_ID 0x141 #define ANYDATA_VENDOR_ID 0x16d5 #define ANYDATA_PRODUCT_ID 0x6501 +#define BANDRICH_VENDOR_ID 0x1A8D +#define BANDRICH_PRODUCT_C100_1 0x1002 +#define BANDRICH_PRODUCT_C100_2 0x1003 + +#define DELL_VENDOR_ID 0x413C + static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -165,6 +171,9 @@ static struct usb_device_id option_ids[] { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, + { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, + { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, + { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -591,12 +600,6 @@ static int option_open(struct usb_serial return (0); } -static inline void stop_urb(struct urb *urb) -{ - if (urb && urb->status == -EINPROGRESS) - usb_kill_urb(urb); -} - static void option_close(struct usb_serial_port *port, struct file *filp) { int i; @@ -614,9 +617,9 @@ static void option_close(struct usb_seri /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) - stop_urb(portdata->in_urbs[i]); + usb_kill_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) - stop_urb(portdata->out_urbs[i]); + usb_kill_urb(portdata->out_urbs[i]); } port->tty = NULL; } @@ -747,9 +750,9 @@ static void option_shutdown(struct usb_s port = serial->port[i]; portdata = usb_get_serial_port_data(port); for (j = 0; j < N_IN_URB; j++) - stop_urb(portdata->in_urbs[j]); + usb_kill_urb(portdata->in_urbs[j]); for (j = 0; j < N_OUT_URB; j++) - stop_urb(portdata->out_urbs[j]); + usb_kill_urb(portdata->out_urbs[j]); } /* Now free them */ diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ecedd83..644607d 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -456,12 +456,6 @@ static int sierra_open(struct usb_serial return (0); } -static inline void stop_urb(struct urb *urb) -{ - if (urb && urb->status == -EINPROGRESS) - usb_kill_urb(urb); -} - static void sierra_close(struct usb_serial_port *port, struct file *filp) { int i; @@ -479,9 +473,9 @@ static void sierra_close(struct usb_seri /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) - stop_urb(portdata->in_urbs[i]); + usb_unlink_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) - stop_urb(portdata->out_urbs[i]); + usb_unlink_urb(portdata->out_urbs[i]); } port->tty = NULL; } @@ -583,17 +577,26 @@ static void sierra_shutdown(struct usb_s /* Stop reading/writing urbs */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; + if (!port) + continue; portdata = usb_get_serial_port_data(port); + if (!portdata) + continue; + for (j = 0; j < N_IN_URB; j++) - stop_urb(portdata->in_urbs[j]); + usb_unlink_urb(portdata->in_urbs[j]); for (j = 0; j < N_OUT_URB; j++) - stop_urb(portdata->out_urbs[j]); + usb_unlink_urb(portdata->out_urbs[j]); } /* Now free them */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; + if (!port) + continue; portdata = usb_get_serial_port_data(port); + if (!portdata) + continue; for (j = 0; j < N_IN_URB; j++) { if (portdata->in_urbs[j]) { @@ -612,6 +615,8 @@ static void sierra_shutdown(struct usb_s /* Now free per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; + if (!port) + continue; kfree(usb_get_serial_port_data(port)); } } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 2f59ff2..ffbe601 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -384,19 +384,21 @@ static int visor_write (struct usb_seria dbg("%s - write limit hit\n", __FUNCTION__); return 0; } + priv->outstanding_urbs++; spin_unlock_irqrestore(&priv->lock, flags); buffer = kmalloc (count, GFP_ATOMIC); if (!buffer) { dev_err(&port->dev, "out of memory\n"); - return -ENOMEM; + count = -ENOMEM; + goto error_no_buffer; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { dev_err(&port->dev, "no more free urbs\n"); - kfree (buffer); - return -ENOMEM; + count = -ENOMEM; + goto error_no_urb; } memcpy (buffer, buf, count); @@ -415,19 +417,27 @@ static int visor_write (struct usb_seria dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __FUNCTION__, status); count = status; - kfree (buffer); + goto error; } else { spin_lock_irqsave(&priv->lock, flags); - ++priv->outstanding_urbs; priv->bytes_out += count; spin_unlock_irqrestore(&priv->lock, flags); } /* we are done with this urb, so let the host driver * really free it when it is finished with it */ - usb_free_urb (urb); + usb_free_urb(urb); return count; +error: + usb_free_urb(urb); +error_no_urb: + kfree(buffer); +error_no_buffer: + spin_lock_irqsave(&priv->lock, flags); + --priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + return count; } diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index bf16e9e..27c5f8f 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1109,7 +1109,7 @@ static int firm_send_command (struct usb command_port = port->serial->port[COMMAND_PORT]; command_info = usb_get_serial_port_data(command_port); spin_lock_irqsave(&command_info->lock, flags); - command_info->command_finished = FALSE; + command_info->command_finished = false; transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; transfer_buffer[0] = command; @@ -1124,12 +1124,12 @@ static int firm_send_command (struct usb spin_unlock_irqrestore(&command_info->lock, flags); /* wait for the command to complete */ - wait_event_interruptible_timeout(command_info->wait_command, - (command_info->command_finished != FALSE), COMMAND_TIMEOUT); + wait_event_interruptible_timeout(command_info->wait_command, + (bool)command_info->command_finished, COMMAND_TIMEOUT); spin_lock_irqsave(&command_info->lock, flags); - if (command_info->command_finished == FALSE) { + if (command_info->command_finished == false) { dbg("%s - command timed out.", __FUNCTION__); retval = -ETIMEDOUT; goto exit; diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h index d714eff..f160797 100644 --- a/drivers/usb/serial/whiteheat.h +++ b/drivers/usb/serial/whiteheat.h @@ -20,10 +20,6 @@ #ifndef __LINUX_USB_SERIAL_WHITEHEAT_H #define __LINUX_USB_SERIAL_WHITEHEAT_H -#define FALSE 0 -#define TRUE 1 - - /* WhiteHEAT commands */ #define WHITEHEAT_OPEN 1 /* open the port */ #define WHITEHEAT_CLOSE 2 /* close the port */ diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c index 599ad10..06d1107 100644 --- a/drivers/usb/storage/libusual.c +++ b/drivers/usb/storage/libusual.c @@ -117,6 +117,7 @@ EXPORT_SYMBOL_GPL(usb_usual_check_type); static int usu_probe(struct usb_interface *intf, const struct usb_device_id *id) { + int rc; unsigned long type; struct task_struct* task; unsigned long flags; @@ -135,7 +136,7 @@ static int usu_probe(struct usb_interfac task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type); if (IS_ERR(task)) { - int rc = PTR_ERR(task); + rc = PTR_ERR(task); printk(KERN_WARNING "libusual: " "Unable to start the thread for %s: %d\n", bias_names[type], rc); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 4a9d0d5..8b3145a 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1371,15 +1371,6 @@ UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), -/* This prevents the kernel from detecting the virtual cd-drive with the - * Windows drivers. -*/ -UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0xffff, - "HUAWEI", - "E220 USB-UMTS Install", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_DEVICE), - /* Reported by Vilius Bilinkevicius PAGE_SIZE and the number of packets in a page + is an integer 512 is the largest possible packet on EHCI */ #define WRITES_IN_FLIGHT 8 +/* arbitrarily chosen */ /* Structure to hold all of our device specific stuff */ struct usb_skel { - struct usb_device *dev; /* the usb device for this device */ - struct usb_interface *interface; /* the interface for this device */ + struct usb_device *udev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ @@ -76,8 +83,10 @@ static int skel_open(struct inode *inode subminor = iminor(inode); + mutex_lock(&skel_open_lock); interface = usb_find_interface(&skel_driver, subminor); if (!interface) { + mutex_unlock(&skel_open_lock); err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; @@ -86,12 +95,15 @@ static int skel_open(struct inode *inode dev = usb_get_intfdata(interface); if (!dev) { + mutex_unlock(&skel_open_lock); retval = -ENODEV; goto exit; } /* increment our usage count for the device */ kref_get(&dev->kref); + /* now we can drop the lock */ + mutex_unlock(&skel_open_lock); /* prevent the device from being autosuspended */ retval = usb_autopm_get_interface(interface); @@ -201,12 +213,6 @@ static ssize_t skel_write(struct file *f goto exit; } - mutex_lock(&dev->io_mutex); - if (!dev->interface) { /* disconnect() was called */ - retval = -ENODEV; - goto error; - } - /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { @@ -225,6 +231,14 @@ static ssize_t skel_write(struct file *f goto error; } + /* this lock makes sure we don't submit URBs to gone devices */ + mutex_lock(&dev->io_mutex); + if (!dev->interface) { /* disconnect() was called */ + mutex_unlock(&dev->io_mutex); + retval = -ENODEV; + goto error; + } + /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), @@ -233,6 +247,7 @@ static ssize_t skel_write(struct file *f /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); + mutex_unlock(&dev->io_mutex); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); goto error; @@ -241,7 +256,7 @@ static ssize_t skel_write(struct file *f /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); - mutex_unlock(&dev->io_mutex); + return writesize; error: @@ -249,7 +264,6 @@ error: usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); usb_free_urb(urb); } - mutex_unlock(&dev->io_mutex); up(&dev->limit_sem); exit: @@ -344,6 +358,7 @@ static int skel_probe(struct usb_interfa error: if (dev) + /* this frees allocated memory */ kref_put(&dev->kref, skel_delete); return retval; } @@ -354,20 +369,21 @@ static void skel_disconnect(struct usb_i int minor = interface->minor; /* prevent skel_open() from racing skel_disconnect() */ - lock_kernel(); + mutex_lock(&skel_open_lock); dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &skel_class); + mutex_unlock(&skel_open_lock); /* prevent more I/O from starting */ mutex_lock(&dev->io_mutex); dev->interface = NULL; mutex_unlock(&dev->io_mutex); - unlock_kernel(); + /* decrement our usage count */ kref_put(&dev->kref, skel_delete); @@ -380,6 +396,7 @@ static struct usb_driver skel_driver = { .probe = skel_probe, .disconnect = skel_disconnect, .id_table = skel_table, + .supports_autosuspend = 1, }; static int __init usb_skel_init(void) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8372ace..b1cb72c 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -396,7 +396,7 @@ config FB_ATARI config FB_OF bool "Open Firmware frame buffer device support" - depends on (FB = y) && (PPC64 || PPC_OF) + depends on (FB = y) && (PPC64 || PPC_OF) && (!PPC_PSERIES || PCI) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index d7627fc..8514f2a 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -2899,7 +2899,7 @@ static int __devinit atyfb_setup_sparc(s struct fb_info *info, unsigned long addr) { struct atyfb_par *par = info->par; - struct pcidev_cookie *pcp; + struct device_node *dp; char prop[128]; int node, len, i, j, ret; u32 mem, chip_id; @@ -3037,8 +3037,8 @@ static int __devinit atyfb_setup_sparc(s node = 0; } - pcp = pdev->sysdata; - if (node == pcp->prom_node->node) { + dp = pci_device_to_OF_node(pdev); + if (node == dp->node) { struct fb_var_screeninfo *var = &default_var; unsigned int N, P, Q, M, T, R; u32 v_total, h_total; diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 1bf6f42..a4b3fd1 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -410,7 +410,7 @@ static int __devinit radeon_find_mem_vb } #endif -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device * tree. Hopefully, ATI OF driver is kind enough to fill these @@ -440,7 +440,7 @@ static int __devinit radeon_read_xtal_OF return 0; } -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ /* * Read PLL infos from chip registers @@ -645,7 +645,7 @@ static void __devinit radeon_get_pllinfo rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* * Retrieve PLL infos from Open Firmware first */ @@ -653,7 +653,7 @@ #ifdef CONFIG_PPC_OF printk(KERN_INFO "radeonfb: Retrieved PLL infos from Open Firmware\n"); goto found; } -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ /* * Check out if we have an X86 which gave us some PLL informations @@ -2231,7 +2231,7 @@ static int __devinit radeonfb_pci_regist rinfo->family == CHIP_FAMILY_RS200) rinfo->errata |= CHIP_ERRATA_PLL_DELAY; -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* On PPC, we obtain the OF device-node pointer to the firmware * data for this chip */ @@ -2240,6 +2240,8 @@ #ifdef CONFIG_PPC_OF printk(KERN_WARNING "radeonfb (%s): Cannot match card to OF node !\n", pci_name(rinfo->pdev)); +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ +#ifdef CONFIG_PPC_OF /* On PPC, the firmware sets up a memory mapping that tends * to cause lockups when enabling the engine. We reconfigure * the card internal memory mappings properly diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c index 38c7dbf..737b5c0 100644 --- a/drivers/video/aty/radeon_monitor.c +++ b/drivers/video/aty/radeon_monitor.c @@ -52,7 +52,7 @@ static char *radeon_get_mon_name(int typ } -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* * Try to find monitor informations & EDID data out of the Open Firmware * device-tree. This also contains some "hacks" to work around a few machine @@ -156,7 +156,7 @@ static int __devinit radeon_probe_OF_hea } return MT_NONE; } -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo) @@ -495,11 +495,11 @@ #endif /* DEBUG */ * Old single head cards */ if (!rinfo->has_CRTC2) { -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, &rinfo->mon1_EDID); -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = @@ -544,11 +544,11 @@ #endif /* CONFIG_FB_RADEON_I2C */ /* * Probe primary head (DVI or laptop internal panel) */ -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, &rinfo->mon1_EDID); -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi, @@ -572,11 +572,11 @@ #endif /* CONFIG_FB_RADEON_I2C */ /* * Probe secondary head (mostly VGA, can be DVI) */ -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) if (rinfo->mon2_type == MT_NONE) rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1, &rinfo->mon2_EDID); -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || defined(CONFIG_SPARC) */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon2_type == MT_NONE) rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_vga, diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index 9a2b0d6..c411293 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -1262,7 +1262,7 @@ static void radeon_pm_full_reset_sdram(s /* This is the code for the Aluminium PowerBooks M10 / iBooks M11 */ if (rinfo->family == CHIP_FAMILY_RV350) { u32 sdram_mode_reg = rinfo->save_regs[35]; - static u32 default_mrtable[] = + static const u32 default_mrtable[] = { 0x21320032, 0x21321000, 0xa1321000, 0x21321000, 0xffffffff, 0x21320032, 0xa1320032, 0x21320032, 0xffffffff, diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index d5ff224..3190003 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -16,7 +16,7 @@ #endif #include -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) #include #endif @@ -292,7 +292,7 @@ struct radeonfb_info { unsigned long fb_local_base; struct pci_dev *pdev; -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) struct device_node *of_node; #endif diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index 767c850..f042428 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -266,7 +266,7 @@ static void __devinit cg3_init_fix(struc static void __devinit cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var, struct device_node *dp) { - char *params; + const char *params; char *p; int ww, hh; diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index fd60dba..8b76273 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -179,12 +179,14 @@ MODULE_LICENSE("GPL"); int init_module(void) { struct device_node *dp; + int ret = -ENXIO; - dp = find_devices("control"); + dp = of_find_node_by_name(NULL, "control"); if (dp != 0 && !control_of_init(dp)) - return 0; + ret = 0; + of_node_put(dp); - return -ENXIO; + return ret; } void cleanup_module(void) @@ -589,16 +591,18 @@ static int __init control_init(void) { struct device_node *dp; char *option = NULL; + int ret = -ENXIO; if (fb_get_options("controlfb", &option)) return -ENODEV; control_setup(option); - dp = find_devices("control"); + dp = of_find_node_by_name(NULL, "control"); if (dp != 0 && !control_of_init(dp)) - return 0; + ret = 0; + of_node_put(dp); - return -ENXIO; + return ret; } module_init(control_init); diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index 90592fb..eb1a481 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -44,8 +44,8 @@ #include #include -#ifdef __sparc__ -#include +#ifdef CONFIG_SPARC +#include #include #endif @@ -96,7 +96,7 @@ struct fb_var_screeninfo default_var = { .vmode = FB_VMODE_NONINTERLACED }; -#ifdef __sparc__ +#ifdef CONFIG_SPARC struct fb_var_screeninfo default_var_1024x768 __initdata = { /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ .xres = 1024, @@ -188,7 +188,7 @@ static inline void iga_outb(struct iga_p pci_outb(par, val, reg+1); } -#endif /* __sparc__ */ +#endif /* CONFIG_SPARC */ /* * Very important functionality for the JavaEngine1 computer: @@ -217,7 +217,7 @@ #endif iga_outb(par, 0, IGA_EXT_CNTRL, IGA_IDX_OVERSCAN_COLOR + i); } -#ifdef __sparc__ +#ifdef CONFIG_SPARC static int igafb_mmap(struct fb_info *info, struct vm_area_struct *vma) { @@ -271,7 +271,7 @@ static int igafb_mmap(struct fb_info *in vma->vm_flags |= VM_IO; return 0; } -#endif /* __sparc__ */ +#endif /* CONFIG_SPARC */ static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, @@ -323,7 +323,7 @@ static struct fb_ops igafb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, -#ifdef __sparc__ +#ifdef CONFIG_SPARC .fb_mmap = igafb_mmap, #endif }; @@ -424,7 +424,7 @@ int __init igafb_init(void) par->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK; -#ifdef __sparc__ +#ifdef CONFIG_SPARC /* * The following is sparc specific and this is why: * @@ -477,8 +477,8 @@ #ifdef __sparc__ * Set default vmode and cmode from PROM properties. */ { - struct pcidev_cookie *cookie = pdev->sysdata; - int node = cookie->prom_node; + struct device_node *dp = pci_device_to_OF_node(pdev); + int node = dp->node; int width = prom_getintdefault(node, "width", 1024); int height = prom_getintdefault(node, "height", 768); int depth = prom_getintdefault(node, "depth", 8); @@ -534,7 +534,7 @@ #endif kfree(info); } -#ifdef __sparc__ +#ifdef CONFIG_SPARC /* * Add /dev/fb mmap values. */ @@ -552,7 +552,7 @@ #ifdef __sparc__ par->mmap_map[1].size = PAGE_SIZE * 2; /* X wants 2 pages */ par->mmap_map[1].prot_mask = SRMMU_CACHE; par->mmap_map[1].prot_flag = SRMMU_WRITE; -#endif /* __sparc__ */ +#endif /* CONFIG_SPARC */ return 0; } diff --git a/fs/Kconfig b/fs/Kconfig index 3c4886b..a42f767 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -2019,7 +2019,8 @@ config CODA_FS_OLD_API config AFS_FS tristate "Andrew File System support (AFS) (EXPERIMENTAL)" depends on INET && EXPERIMENTAL - select RXRPC + select AF_RXRPC + select KEYS help If you say Y here, you will get an experimental Andrew File System driver. It currently only supports unsecured read-only AFS access. @@ -2028,8 +2029,15 @@ config AFS_FS If unsure, say N. -config RXRPC - tristate +config AFS_DEBUG + bool "AFS dynamic debugging" + depends on AFS_FS + help + Say Y here to make runtime controllable debugging messages appear. + + See for more information. + + If unsure, say N. config 9P_FS tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 4029c9d..01545eb 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -2,8 +2,6 @@ # # Makefile for Red Hat Linux AFS client. # -#CFLAGS += -finstrument-functions - kafs-objs := \ callback.o \ cell.o \ @@ -12,14 +10,15 @@ kafs-objs := \ file.o \ fsclient.o \ inode.o \ - kafsasyncd.o \ - kafstimod.o \ main.o \ misc.o \ mntpt.o \ proc.o \ + rxrpc.o \ + security.o \ server.o \ super.o \ + use-rtnetlink.o \ vlclient.o \ vlocation.o \ vnode.o \ diff --git a/fs/afs/afs.h b/fs/afs/afs.h new file mode 100644 index 0000000..52d0752 --- /dev/null +++ b/fs/afs/afs.h @@ -0,0 +1,146 @@ +/* AFS common types + * + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef AFS_H +#define AFS_H + +#include + +#define AFS_MAXCELLNAME 64 /* maximum length of a cell name */ +#define AFS_MAXVOLNAME 64 /* maximum length of a volume name */ + +typedef unsigned afs_volid_t; +typedef unsigned afs_vnodeid_t; +typedef unsigned long long afs_dataversion_t; + +typedef enum { + AFSVL_RWVOL, /* read/write volume */ + AFSVL_ROVOL, /* read-only volume */ + AFSVL_BACKVOL, /* backup volume */ +} __attribute__((packed)) afs_voltype_t; + +typedef enum { + AFS_FTYPE_INVALID = 0, + AFS_FTYPE_FILE = 1, + AFS_FTYPE_DIR = 2, + AFS_FTYPE_SYMLINK = 3, +} afs_file_type_t; + +/* + * AFS file identifier + */ +struct afs_fid { + afs_volid_t vid; /* volume ID */ + afs_vnodeid_t vnode; /* file index within volume */ + unsigned unique; /* unique ID number (file index version) */ +}; + +/* + * AFS callback notification + */ +typedef enum { + AFSCM_CB_UNTYPED = 0, /* no type set on CB break */ + AFSCM_CB_EXCLUSIVE = 1, /* CB exclusive to CM [not implemented] */ + AFSCM_CB_SHARED = 2, /* CB shared by other CM's */ + AFSCM_CB_DROPPED = 3, /* CB promise cancelled by file server */ +} afs_callback_type_t; + +struct afs_callback { + struct afs_fid fid; /* file identifier */ + unsigned version; /* callback version */ + unsigned expiry; /* time at which expires */ + afs_callback_type_t type; /* type of callback */ +}; + +#define AFSCBMAX 50 /* maximum callbacks transferred per bulk op */ + +/* + * AFS volume information + */ +struct afs_volume_info { + afs_volid_t vid; /* volume ID */ + afs_voltype_t type; /* type of this volume */ + afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */ + + /* list of fileservers serving this volume */ + size_t nservers; /* number of entries used in servers[] */ + struct { + struct in_addr addr; /* fileserver address */ + } servers[8]; +}; + +/* + * AFS security ACE access mask + */ +typedef u32 afs_access_t; +#define AFS_ACE_READ 0x00000001U /* - permission to read a file/dir */ +#define AFS_ACE_WRITE 0x00000002U /* - permission to write/chmod a file */ +#define AFS_ACE_INSERT 0x00000004U /* - permission to create dirent in a dir */ +#define AFS_ACE_LOOKUP 0x00000008U /* - permission to lookup a file/dir in a dir */ +#define AFS_ACE_DELETE 0x00000010U /* - permission to delete a dirent from a dir */ +#define AFS_ACE_LOCK 0x00000020U /* - permission to lock a file */ +#define AFS_ACE_ADMINISTER 0x00000040U /* - permission to change ACL */ +#define AFS_ACE_USER_A 0x01000000U /* - 'A' user-defined permission */ +#define AFS_ACE_USER_B 0x02000000U /* - 'B' user-defined permission */ +#define AFS_ACE_USER_C 0x04000000U /* - 'C' user-defined permission */ +#define AFS_ACE_USER_D 0x08000000U /* - 'D' user-defined permission */ +#define AFS_ACE_USER_E 0x10000000U /* - 'E' user-defined permission */ +#define AFS_ACE_USER_F 0x20000000U /* - 'F' user-defined permission */ +#define AFS_ACE_USER_G 0x40000000U /* - 'G' user-defined permission */ +#define AFS_ACE_USER_H 0x80000000U /* - 'H' user-defined permission */ + +/* + * AFS file status information + */ +struct afs_file_status { + unsigned if_version; /* interface version */ +#define AFS_FSTATUS_VERSION 1 + + afs_file_type_t type; /* file type */ + unsigned nlink; /* link count */ + u64 size; /* file size */ + afs_dataversion_t data_version; /* current data version */ + u32 author; /* author ID */ + u32 owner; /* owner ID */ + u32 group; /* group ID */ + afs_access_t caller_access; /* access rights for authenticated caller */ + afs_access_t anon_access; /* access rights for unauthenticated caller */ + umode_t mode; /* UNIX mode */ + struct afs_fid parent; /* parent dir ID for non-dirs only */ + time_t mtime_client; /* last time client changed data */ + time_t mtime_server; /* last time server changed data */ +}; + +/* + * AFS file status change request + */ +struct afs_store_status { + u32 mask; /* which bits of the struct are set */ + u32 mtime_client; /* last time client changed data */ + u32 owner; /* owner ID */ + u32 group; /* group ID */ + umode_t mode; /* UNIX mode */ +}; + +#define AFS_SET_MTIME 0x01 /* set the mtime */ +#define AFS_SET_OWNER 0x02 /* set the owner ID */ +#define AFS_SET_GROUP 0x04 /* set the group ID (unsupported?) */ +#define AFS_SET_MODE 0x08 /* set the UNIX mode */ +#define AFS_SET_SEG_SIZE 0x10 /* set the segment size (unsupported) */ + +/* + * AFS volume synchronisation information + */ +struct afs_volsync { + time_t creation; /* volume creation time */ +}; + +#endif /* AFS_H */ diff --git a/fs/afs/afs_cm.h b/fs/afs/afs_cm.h new file mode 100644 index 0000000..7b4d4fa --- /dev/null +++ b/fs/afs/afs_cm.h @@ -0,0 +1,32 @@ +/* AFS Cache Manager definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef AFS_CM_H +#define AFS_CM_H + +#define AFS_CM_PORT 7001 /* AFS file server port */ +#define CM_SERVICE 1 /* AFS File Service ID */ + +enum AFS_CM_Operations { + CBCallBack = 204, /* break callback promises */ + CBInitCallBackState = 205, /* initialise callback state */ + CBProbe = 206, /* probe client */ + CBGetLock = 207, /* get contents of CM lock table */ + CBGetCE = 208, /* get cache file description */ + CBGetXStatsVersion = 209, /* get version of extended statistics */ + CBGetXStats = 210, /* get contents of extended statistics data */ + CBInitCallBackState3 = 213, /* initialise callback state, version 3 */ + CBGetCapabilities = 65538, /* get client capabilities */ +}; + +#define AFS_CAP_ERROR_TRANSLATION 0x1 + +#endif /* AFS_FS_H */ diff --git a/fs/afs/afs_fs.h b/fs/afs/afs_fs.h new file mode 100644 index 0000000..89e0d16 --- /dev/null +++ b/fs/afs/afs_fs.h @@ -0,0 +1,48 @@ +/* AFS File Service definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef AFS_FS_H +#define AFS_FS_H + +#define AFS_FS_PORT 7000 /* AFS file server port */ +#define FS_SERVICE 1 /* AFS File Service ID */ + +enum AFS_FS_Operations { + FSFETCHDATA = 130, /* AFS Fetch file data */ + FSFETCHSTATUS = 132, /* AFS Fetch file status */ + FSREMOVEFILE = 136, /* AFS Remove a file */ + FSCREATEFILE = 137, /* AFS Create a file */ + FSRENAME = 138, /* AFS Rename or move a file or directory */ + FSSYMLINK = 139, /* AFS Create a symbolic link */ + FSLINK = 140, /* AFS Create a hard link */ + FSMAKEDIR = 141, /* AFS Create a directory */ + FSREMOVEDIR = 142, /* AFS Remove a directory */ + FSGIVEUPCALLBACKS = 147, /* AFS Discard callback promises */ + FSGETVOLUMEINFO = 148, /* AFS Get root volume information */ + FSGETROOTVOLUME = 151, /* AFS Get root volume name */ + FSLOOKUP = 161, /* AFS lookup file in directory */ +}; + +enum AFS_FS_Errors { + VSALVAGE = 101, /* volume needs salvaging */ + VNOVNODE = 102, /* no such file/dir (vnode) */ + VNOVOL = 103, /* no such volume or volume unavailable */ + VVOLEXISTS = 104, /* volume name already exists */ + VNOSERVICE = 105, /* volume not currently in service */ + VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */ + VONLINE = 107, /* volume is already online */ + VDISKFULL = 108, /* disk partition is full */ + VOVERQUOTA = 109, /* volume's maximum quota exceeded */ + VBUSY = 110, /* volume is temporarily unavailable */ + VMOVED = 111, /* volume moved to new server - ask this FS where */ +}; + +#endif /* AFS_FS_H */ diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h new file mode 100644 index 0000000..8bbefe0 --- /dev/null +++ b/fs/afs/afs_vl.h @@ -0,0 +1,84 @@ +/* AFS Volume Location Service client interface + * + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef AFS_VL_H +#define AFS_VL_H + +#include "afs.h" + +#define AFS_VL_PORT 7003 /* volume location service port */ +#define VL_SERVICE 52 /* RxRPC service ID for the Volume Location service */ + +enum AFSVL_Operations { + VLGETENTRYBYID = 503, /* AFS Get Cache Entry By ID operation ID */ + VLGETENTRYBYNAME = 504, /* AFS Get Cache Entry By Name operation ID */ + VLPROBE = 514, /* AFS Probe Volume Location Service operation ID */ +}; + +enum AFSVL_Errors { + AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */ + AFSVL_IO = 363521, /* I/O related error */ + AFSVL_NAMEEXIST = 363522, /* Volume name entry exists in vl database */ + AFSVL_CREATEFAIL = 363523, /* Internal creation failure */ + AFSVL_NOENT = 363524, /* No such entry */ + AFSVL_EMPTY = 363525, /* Vl database is empty */ + AFSVL_ENTDELETED = 363526, /* Entry is deleted (soft delete) */ + AFSVL_BADNAME = 363527, /* Volume name is illegal */ + AFSVL_BADINDEX = 363528, /* Index is out of range */ + AFSVL_BADVOLTYPE = 363529, /* Bad volume type */ + AFSVL_BADSERVER = 363530, /* Illegal server number (out of range) */ + AFSVL_BADPARTITION = 363531, /* Bad partition number */ + AFSVL_REPSFULL = 363532, /* Run out of space for Replication sites */ + AFSVL_NOREPSERVER = 363533, /* No such Replication server site exists */ + AFSVL_DUPREPSERVER = 363534, /* Replication site already exists */ + AFSVL_RWNOTFOUND = 363535, /* Parent R/W entry not found */ + AFSVL_BADREFCOUNT = 363536, /* Illegal Reference Count number */ + AFSVL_SIZEEXCEEDED = 363537, /* Vl size for attributes exceeded */ + AFSVL_BADENTRY = 363538, /* Bad incoming vl entry */ + AFSVL_BADVOLIDBUMP = 363539, /* Illegal max volid increment */ + AFSVL_IDALREADYHASHED = 363540, /* RO/BACK id already hashed */ + AFSVL_ENTRYLOCKED = 363541, /* Vl entry is already locked */ + AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */ + AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */ + AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */ + AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server °ag */ + AFSVL_PERM = 363546, /* No permission access */ + AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */ +}; + +/* + * maps to "struct vldbentry" in vvl-spec.pdf + */ +struct afs_vldbentry { + char name[65]; /* name of volume (with NUL char) */ + afs_voltype_t type; /* volume type */ + unsigned num_servers; /* num servers that hold instances of this vol */ + unsigned clone_id; /* cloning ID */ + + unsigned flags; +#define AFS_VLF_RWEXISTS 0x1000 /* R/W volume exists */ +#define AFS_VLF_ROEXISTS 0x2000 /* R/O volume exists */ +#define AFS_VLF_BACKEXISTS 0x4000 /* backup volume exists */ + + afs_volid_t volume_ids[3]; /* volume IDs */ + + struct { + struct in_addr addr; /* server address */ + unsigned partition; /* partition ID on this server */ + unsigned flags; /* server specific flags */ +#define AFS_VLSF_NEWREPSITE 0x0001 /* unused */ +#define AFS_VLSF_ROVOL 0x0002 /* this server holds a R/O instance of the volume */ +#define AFS_VLSF_RWVOL 0x0004 /* this server holds a R/W instance of the volume */ +#define AFS_VLSF_BACKVOL 0x0008 /* this server holds a backup instance of the volume */ + } servers[8]; +}; + +#endif /* AFS_VL_H */ diff --git a/fs/afs/cache.c b/fs/afs/cache.c new file mode 100644 index 0000000..de0d7de --- /dev/null +++ b/fs/afs/cache.c @@ -0,0 +1,256 @@ +/* AFS caching stuff + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_cell_cache_match(void *target, + const void *entry); +static void afs_cell_cache_update(void *source, void *entry); + +struct cachefs_index_def afs_cache_cell_index_def = { + .name = "cell_ix", + .data_size = sizeof(struct afs_cache_cell), + .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, + .match = afs_cell_cache_match, + .update = afs_cell_cache_update, +}; +#endif + +/* + * match a cell record obtained from the cache + */ +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_cell_cache_match(void *target, + const void *entry) +{ + const struct afs_cache_cell *ccell = entry; + struct afs_cell *cell = target; + + _enter("{%s},{%s}", ccell->name, cell->name); + + if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) { + _leave(" = SUCCESS"); + return CACHEFS_MATCH_SUCCESS; + } + + _leave(" = FAILED"); + return CACHEFS_MATCH_FAILED; +} +#endif + +/* + * update a cell record in the cache + */ +#ifdef AFS_CACHING_SUPPORT +static void afs_cell_cache_update(void *source, void *entry) +{ + struct afs_cache_cell *ccell = entry; + struct afs_cell *cell = source; + + _enter("%p,%p", source, entry); + + strncpy(ccell->name, cell->name, sizeof(ccell->name)); + + memcpy(ccell->vl_servers, + cell->vl_addrs, + min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs))); + +} +#endif + +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_vlocation_cache_match(void *target, + const void *entry); +static void afs_vlocation_cache_update(void *source, void *entry); + +struct cachefs_index_def afs_vlocation_cache_index_def = { + .name = "vldb", + .data_size = sizeof(struct afs_cache_vlocation), + .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, + .match = afs_vlocation_cache_match, + .update = afs_vlocation_cache_update, +}; +#endif + +/* + * match a VLDB record stored in the cache + * - may also load target from entry + */ +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_vlocation_cache_match(void *target, + const void *entry) +{ + const struct afs_cache_vlocation *vldb = entry; + struct afs_vlocation *vlocation = target; + + _enter("{%s},{%s}", vlocation->vldb.name, vldb->name); + + if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0 + ) { + if (!vlocation->valid || + vlocation->vldb.rtime == vldb->rtime + ) { + vlocation->vldb = *vldb; + vlocation->valid = 1; + _leave(" = SUCCESS [c->m]"); + return CACHEFS_MATCH_SUCCESS; + } else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) { + /* delete if VIDs for this name differ */ + if (memcmp(&vlocation->vldb.vid, + &vldb->vid, + sizeof(vldb->vid)) != 0) { + _leave(" = DELETE"); + return CACHEFS_MATCH_SUCCESS_DELETE; + } + + _leave(" = UPDATE"); + return CACHEFS_MATCH_SUCCESS_UPDATE; + } else { + _leave(" = SUCCESS"); + return CACHEFS_MATCH_SUCCESS; + } + } + + _leave(" = FAILED"); + return CACHEFS_MATCH_FAILED; +} +#endif + +/* + * update a VLDB record stored in the cache + */ +#ifdef AFS_CACHING_SUPPORT +static void afs_vlocation_cache_update(void *source, void *entry) +{ + struct afs_cache_vlocation *vldb = entry; + struct afs_vlocation *vlocation = source; + + _enter(""); + + *vldb = vlocation->vldb; +} +#endif + +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_volume_cache_match(void *target, + const void *entry); +static void afs_volume_cache_update(void *source, void *entry); + +struct cachefs_index_def afs_volume_cache_index_def = { + .name = "volume", + .data_size = sizeof(struct afs_cache_vhash), + .keys[0] = { CACHEFS_INDEX_KEYS_BIN, 1 }, + .keys[1] = { CACHEFS_INDEX_KEYS_BIN, 1 }, + .match = afs_volume_cache_match, + .update = afs_volume_cache_update, +}; +#endif + +/* + * match a volume hash record stored in the cache + */ +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_volume_cache_match(void *target, + const void *entry) +{ + const struct afs_cache_vhash *vhash = entry; + struct afs_volume *volume = target; + + _enter("{%u},{%u}", volume->type, vhash->vtype); + + if (volume->type == vhash->vtype) { + _leave(" = SUCCESS"); + return CACHEFS_MATCH_SUCCESS; + } + + _leave(" = FAILED"); + return CACHEFS_MATCH_FAILED; +} +#endif + +/* + * update a volume hash record stored in the cache + */ +#ifdef AFS_CACHING_SUPPORT +static void afs_volume_cache_update(void *source, void *entry) +{ + struct afs_cache_vhash *vhash = entry; + struct afs_volume *volume = source; + + _enter(""); + + vhash->vtype = volume->type; +} +#endif + +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_vnode_cache_match(void *target, + const void *entry); +static void afs_vnode_cache_update(void *source, void *entry); + +struct cachefs_index_def afs_vnode_cache_index_def = { + .name = "vnode", + .data_size = sizeof(struct afs_cache_vnode), + .keys[0] = { CACHEFS_INDEX_KEYS_BIN, 4 }, + .match = afs_vnode_cache_match, + .update = afs_vnode_cache_update, +}; +#endif + +/* + * match a vnode record stored in the cache + */ +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_vnode_cache_match(void *target, + const void *entry) +{ + const struct afs_cache_vnode *cvnode = entry; + struct afs_vnode *vnode = target; + + _enter("{%x,%x,%Lx},{%x,%x,%Lx}", + vnode->fid.vnode, + vnode->fid.unique, + vnode->status.version, + cvnode->vnode_id, + cvnode->vnode_unique, + cvnode->data_version); + + if (vnode->fid.vnode != cvnode->vnode_id) { + _leave(" = FAILED"); + return CACHEFS_MATCH_FAILED; + } + + if (vnode->fid.unique != cvnode->vnode_unique || + vnode->status.version != cvnode->data_version) { + _leave(" = DELETE"); + return CACHEFS_MATCH_SUCCESS_DELETE; + } + + _leave(" = SUCCESS"); + return CACHEFS_MATCH_SUCCESS; +} +#endif + +/* + * update a vnode record stored in the cache + */ +#ifdef AFS_CACHING_SUPPORT +static void afs_vnode_cache_update(void *source, void *entry) +{ + struct afs_cache_vnode *cvnode = entry; + struct afs_vnode *vnode = source; + + _enter(""); + + cvnode->vnode_id = vnode->fid.vnode; + cvnode->vnode_unique = vnode->fid.unique; + cvnode->data_version = vnode->status.version; +} +#endif diff --git a/fs/afs/cache.h b/fs/afs/cache.h index 9eb7722..36a3642 100644 --- a/fs/afs/cache.h +++ b/fs/afs/cache.h @@ -1,4 +1,4 @@ -/* cache.h: AFS local cache management interface +/* AFS local cache management interface * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -9,8 +9,8 @@ * 2 of the License, or (at your option) any later version. */ -#ifndef _LINUX_AFS_CACHE_H -#define _LINUX_AFS_CACHE_H +#ifndef AFS_CACHE_H +#define AFS_CACHE_H #undef AFS_CACHING_SUPPORT @@ -20,8 +20,4 @@ #include #endif #include "types.h" -#ifdef __KERNEL__ - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_AFS_CACHE_H */ +#endif /* AFS_CACHE_H */ diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 9cb206e..639399f 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved. * * This software may be freely redistributed under the terms of the * GNU General Public License. @@ -16,85 +16,187 @@ #include #include #include -#include "server.h" -#include "vnode.h" +#include #include "internal.h" -#include "cmservice.h" -/*****************************************************************************/ +unsigned afs_vnode_update_timeout = 10; + +#define afs_breakring_space(server) \ + CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail, \ + ARRAY_SIZE((server)->cb_break)) + +//static void afs_callback_updater(struct work_struct *); + +static struct workqueue_struct *afs_callback_update_worker; + /* * allow the fileserver to request callback state (re-)initialisation */ -int SRXAFSCM_InitCallBackState(struct afs_server *server) +void afs_init_callback_state(struct afs_server *server) { - struct list_head callbacks; + struct afs_vnode *vnode; - _enter("%p", server); + _enter("{%p}", server); - INIT_LIST_HEAD(&callbacks); - - /* transfer the callback list from the server to a temp holding area */ spin_lock(&server->cb_lock); - list_add(&callbacks, &server->cb_promises); - list_del_init(&server->cb_promises); + /* kill all the promises on record from this server */ + while (!RB_EMPTY_ROOT(&server->cb_promises)) { + vnode = rb_entry(server->cb_promises.rb_node, + struct afs_vnode, cb_promise); + _debug("UNPROMISE { vid=%x vn=%u uq=%u}", + vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); + rb_erase(&vnode->cb_promise, &server->cb_promises); + vnode->cb_promised = false; + } - /* munch our way through the list, grabbing the inode, dropping all the - * locks and regetting them in the right order - */ - while (!list_empty(&callbacks)) { - struct afs_vnode *vnode; - struct inode *inode; + spin_unlock(&server->cb_lock); + _leave(""); +} - vnode = list_entry(callbacks.next, struct afs_vnode, cb_link); - list_del_init(&vnode->cb_link); +/* + * handle the data invalidation side of a callback being broken + */ +void afs_broken_callback_work(struct work_struct *work) +{ + struct afs_vnode *vnode = + container_of(work, struct afs_vnode, cb_broken_work); - /* try and grab the inode - may fail */ - inode = igrab(AFS_VNODE_TO_I(vnode)); - if (inode) { - int release = 0; + _enter(""); - spin_unlock(&server->cb_lock); - spin_lock(&vnode->lock); + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) + return; - if (vnode->cb_server == server) { - vnode->cb_server = NULL; - afs_kafstimod_del_timer(&vnode->cb_timeout); - spin_lock(&afs_cb_hash_lock); - list_del_init(&vnode->cb_hash_link); - spin_unlock(&afs_cb_hash_lock); - release = 1; - } + /* we're only interested in dealing with a broken callback on *this* + * vnode and only if no-one else has dealt with it yet */ + if (!mutex_trylock(&vnode->validate_lock)) + return; /* someone else is dealing with it */ - spin_unlock(&vnode->lock); + if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { + if (S_ISDIR(vnode->vfs_inode.i_mode)) + afs_clear_permits(vnode); - iput(inode); - afs_put_server(server); + if (afs_vnode_fetch_status(vnode, NULL, NULL) < 0) + goto out; - spin_lock(&server->cb_lock); + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) + goto out; + + /* if the vnode's data version number changed then its contents + * are different */ + if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { + _debug("zap data {%x:%u}", + vnode->fid.vid, vnode->fid.vnode); + invalidate_remote_inode(&vnode->vfs_inode); } } - spin_unlock(&server->cb_lock); +out: + mutex_unlock(&vnode->validate_lock); - _leave(" = 0"); - return 0; -} /* end SRXAFSCM_InitCallBackState() */ + /* avoid the potential race whereby the mutex_trylock() in this + * function happens again between the clear_bit() and the + * mutex_unlock() */ + if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { + _debug("requeue"); + queue_work(afs_callback_update_worker, &vnode->cb_broken_work); + } + _leave(""); +} + +/* + * actually break a callback + */ +static void afs_break_callback(struct afs_server *server, + struct afs_vnode *vnode) +{ + _enter(""); + + set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); + + if (vnode->cb_promised) { + spin_lock(&vnode->lock); + + _debug("break callback"); + + spin_lock(&server->cb_lock); + if (vnode->cb_promised) { + rb_erase(&vnode->cb_promise, &server->cb_promises); + vnode->cb_promised = false; + } + spin_unlock(&server->cb_lock); + + queue_work(afs_callback_update_worker, &vnode->cb_broken_work); + spin_unlock(&vnode->lock); + } +} + +/* + * allow the fileserver to explicitly break one callback + * - happens when + * - the backing file is changed + * - a lock is released + */ +static void afs_break_one_callback(struct afs_server *server, + struct afs_fid *fid) +{ + struct afs_vnode *vnode; + struct rb_node *p; + + _debug("find"); + spin_lock(&server->fs_lock); + p = server->fs_vnodes.rb_node; + while (p) { + vnode = rb_entry(p, struct afs_vnode, server_rb); + if (fid->vid < vnode->fid.vid) + p = p->rb_left; + else if (fid->vid > vnode->fid.vid) + p = p->rb_right; + else if (fid->vnode < vnode->fid.vnode) + p = p->rb_left; + else if (fid->vnode > vnode->fid.vnode) + p = p->rb_right; + else if (fid->unique < vnode->fid.unique) + p = p->rb_left; + else if (fid->unique > vnode->fid.unique) + p = p->rb_right; + else + goto found; + } + + /* not found so we just ignore it (it may have moved to another + * server) */ +not_available: + _debug("not avail"); + spin_unlock(&server->fs_lock); + _leave(""); + return; + +found: + _debug("found"); + ASSERTCMP(server, ==, vnode->server); + + if (!igrab(AFS_VNODE_TO_I(vnode))) + goto not_available; + spin_unlock(&server->fs_lock); + + afs_break_callback(server, vnode); + iput(&vnode->vfs_inode); + _leave(""); +} -/*****************************************************************************/ /* * allow the fileserver to break callback promises */ -int SRXAFSCM_CallBack(struct afs_server *server, size_t count, - struct afs_callback callbacks[]) +void afs_break_callbacks(struct afs_server *server, size_t count, + struct afs_callback callbacks[]) { - _enter("%p,%u,", server, count); + _enter("%p,%zu,", server, count); - for (; count > 0; callbacks++, count--) { - struct afs_vnode *vnode = NULL; - struct inode *inode = NULL; - int valid = 0; + ASSERT(server != NULL); + ASSERTCMP(count, <=, AFSCBMAX); + for (; count > 0; callbacks++, count--) { _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", callbacks->fid.vid, callbacks->fid.vnode, @@ -103,67 +205,270 @@ int SRXAFSCM_CallBack(struct afs_server callbacks->expiry, callbacks->type ); + afs_break_one_callback(server, &callbacks->fid); + } - /* find the inode for this fid */ - spin_lock(&afs_cb_hash_lock); + _leave(""); + return; +} - list_for_each_entry(vnode, - &afs_cb_hash(server, &callbacks->fid), - cb_hash_link) { - if (memcmp(&vnode->fid, &callbacks->fid, - sizeof(struct afs_fid)) != 0) - continue; +/* + * record the callback for breaking + * - the caller must hold server->cb_lock + */ +static void afs_do_give_up_callback(struct afs_server *server, + struct afs_vnode *vnode) +{ + struct afs_callback *cb; - /* right vnode, but is it same server? */ - if (vnode->cb_server != server) - break; /* no */ + _enter("%p,%p", server, vnode); - /* try and nail the inode down */ - inode = igrab(AFS_VNODE_TO_I(vnode)); - break; + cb = &server->cb_break[server->cb_break_head]; + cb->fid = vnode->fid; + cb->version = vnode->cb_version; + cb->expiry = vnode->cb_expiry; + cb->type = vnode->cb_type; + smp_wmb(); + server->cb_break_head = + (server->cb_break_head + 1) & + (ARRAY_SIZE(server->cb_break) - 1); + + /* defer the breaking of callbacks to try and collect as many as + * possible to ship in one operation */ + switch (atomic_inc_return(&server->cb_break_n)) { + case 1 ... AFSCBMAX - 1: + queue_delayed_work(afs_callback_update_worker, + &server->cb_break_work, HZ * 2); + break; + case AFSCBMAX: + afs_flush_callback_breaks(server); + break; + default: + break; + } + + ASSERT(server->cb_promises.rb_node != NULL); + rb_erase(&vnode->cb_promise, &server->cb_promises); + vnode->cb_promised = false; + _leave(""); +} + +/* + * discard the callback on a deleted item + */ +void afs_discard_callback_on_delete(struct afs_vnode *vnode) +{ + struct afs_server *server = vnode->server; + + _enter("%d", vnode->cb_promised); + + if (!vnode->cb_promised) { + _leave(" [not promised]"); + return; + } + + ASSERT(server != NULL); + + spin_lock(&server->cb_lock); + if (vnode->cb_promised) { + ASSERT(server->cb_promises.rb_node != NULL); + rb_erase(&vnode->cb_promise, &server->cb_promises); + vnode->cb_promised = false; + } + spin_unlock(&server->cb_lock); + _leave(""); +} + +/* + * give up the callback registered for a vnode on the file server when the + * inode is being cleared + */ +void afs_give_up_callback(struct afs_vnode *vnode) +{ + struct afs_server *server = vnode->server; + + DECLARE_WAITQUEUE(myself, current); + + _enter("%d", vnode->cb_promised); + + _debug("GIVE UP INODE %p", &vnode->vfs_inode); + + if (!vnode->cb_promised) { + _leave(" [not promised]"); + return; + } + + ASSERT(server != NULL); + + spin_lock(&server->cb_lock); + if (vnode->cb_promised && afs_breakring_space(server) == 0) { + add_wait_queue(&server->cb_break_waitq, &myself); + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!vnode->cb_promised || + afs_breakring_space(server) != 0) + break; + spin_unlock(&server->cb_lock); + schedule(); + spin_lock(&server->cb_lock); } + remove_wait_queue(&server->cb_break_waitq, &myself); + __set_current_state(TASK_RUNNING); + } + + /* of course, it's always possible for the server to break this vnode's + * callback first... */ + if (vnode->cb_promised) + afs_do_give_up_callback(server, vnode); + + spin_unlock(&server->cb_lock); + _leave(""); +} + +/* + * dispatch a deferred give up callbacks operation + */ +void afs_dispatch_give_up_callbacks(struct work_struct *work) +{ + struct afs_server *server = + container_of(work, struct afs_server, cb_break_work.work); + + _enter(""); + + /* tell the fileserver to discard the callback promises it has + * - in the event of ENOMEM or some other error, we just forget that we + * had callbacks entirely, and the server will call us later to break + * them + */ + afs_fs_give_up_callbacks(server, &afs_async_call); +} + +/* + * flush the outstanding callback breaks on a server + */ +void afs_flush_callback_breaks(struct afs_server *server) +{ + cancel_delayed_work(&server->cb_break_work); + queue_delayed_work(afs_callback_update_worker, + &server->cb_break_work, 0); +} - spin_unlock(&afs_cb_hash_lock); - - if (inode) { - /* we've found the record for this vnode */ - spin_lock(&vnode->lock); - if (vnode->cb_server == server) { - /* the callback _is_ on the calling server */ - vnode->cb_server = NULL; - valid = 1; - - afs_kafstimod_del_timer(&vnode->cb_timeout); - vnode->flags |= AFS_VNODE_CHANGED; - - spin_lock(&server->cb_lock); - list_del_init(&vnode->cb_link); - spin_unlock(&server->cb_lock); - - spin_lock(&afs_cb_hash_lock); - list_del_init(&vnode->cb_hash_link); - spin_unlock(&afs_cb_hash_lock); - } - spin_unlock(&vnode->lock); - - if (valid) { - invalidate_remote_inode(inode); - afs_put_server(server); - } - iput(inode); +#if 0 +/* + * update a bunch of callbacks + */ +static void afs_callback_updater(struct work_struct *work) +{ + struct afs_server *server; + struct afs_vnode *vnode, *xvnode; + time_t now; + long timeout; + int ret; + + server = container_of(work, struct afs_server, updater); + + _enter(""); + + now = get_seconds(); + + /* find the first vnode to update */ + spin_lock(&server->cb_lock); + for (;;) { + if (RB_EMPTY_ROOT(&server->cb_promises)) { + spin_unlock(&server->cb_lock); + _leave(" [nothing]"); + return; } + + vnode = rb_entry(rb_first(&server->cb_promises), + struct afs_vnode, cb_promise); + if (atomic_read(&vnode->usage) > 0) + break; + rb_erase(&vnode->cb_promise, &server->cb_promises); + vnode->cb_promised = false; } - _leave(" = 0"); - return 0; -} /* end SRXAFSCM_CallBack() */ + timeout = vnode->update_at - now; + if (timeout > 0) { + queue_delayed_work(afs_vnode_update_worker, + &afs_vnode_update, timeout * HZ); + spin_unlock(&server->cb_lock); + _leave(" [nothing]"); + return; + } + + list_del_init(&vnode->update); + atomic_inc(&vnode->usage); + spin_unlock(&server->cb_lock); + + /* we can now perform the update */ + _debug("update %s", vnode->vldb.name); + vnode->state = AFS_VL_UPDATING; + vnode->upd_rej_cnt = 0; + vnode->upd_busy_cnt = 0; + + ret = afs_vnode_update_record(vl, &vldb); + switch (ret) { + case 0: + afs_vnode_apply_update(vl, &vldb); + vnode->state = AFS_VL_UPDATING; + break; + case -ENOMEDIUM: + vnode->state = AFS_VL_VOLUME_DELETED; + break; + default: + vnode->state = AFS_VL_UNCERTAIN; + break; + } + + /* and then reschedule */ + _debug("reschedule"); + vnode->update_at = get_seconds() + afs_vnode_update_timeout; + + spin_lock(&server->cb_lock); + + if (!list_empty(&server->cb_promises)) { + /* next update in 10 minutes, but wait at least 1 second more + * than the newest record already queued so that we don't spam + * the VL server suddenly with lots of requests + */ + xvnode = list_entry(server->cb_promises.prev, + struct afs_vnode, update); + if (vnode->update_at <= xvnode->update_at) + vnode->update_at = xvnode->update_at + 1; + xvnode = list_entry(server->cb_promises.next, + struct afs_vnode, update); + timeout = xvnode->update_at - now; + if (timeout < 0) + timeout = 0; + } else { + timeout = afs_vnode_update_timeout; + } + + list_add_tail(&vnode->update, &server->cb_promises); + + _debug("timeout %ld", timeout); + queue_delayed_work(afs_vnode_update_worker, + &afs_vnode_update, timeout * HZ); + spin_unlock(&server->cb_lock); + afs_put_vnode(vl); +} +#endif + +/* + * initialise the callback update process + */ +int __init afs_callback_update_init(void) +{ + afs_callback_update_worker = + create_singlethread_workqueue("kafs_callbackd"); + return afs_callback_update_worker ? 0 : -ENOMEM; +} -/*****************************************************************************/ /* - * allow the fileserver to see if the cache manager is still alive + * shut down the callback update process */ -int SRXAFSCM_Probe(struct afs_server *server) +void __exit afs_callback_update_kill(void) { - _debug("SRXAFSCM_Probe(%p)\n", server); - return 0; -} /* end SRXAFSCM_Probe() */ + destroy_workqueue(afs_callback_update_worker); +} diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 1fc5783..9b1311a 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -1,4 +1,4 @@ -/* cell.c: AFS cell and server record management +/* AFS cell and server record management * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -11,15 +11,9 @@ #include #include -#include -#include -#include "volume.h" -#include "cell.h" -#include "server.h" -#include "transport.h" -#include "vlclient.h" -#include "kafstimod.h" -#include "super.h" +#include +#include +#include #include "internal.h" DECLARE_RWSEM(afs_proc_cells_sem); @@ -28,66 +22,47 @@ LIST_HEAD(afs_proc_cells); static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells); static DEFINE_RWLOCK(afs_cells_lock); static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */ +static DECLARE_WAIT_QUEUE_HEAD(afs_cells_freeable_wq); static struct afs_cell *afs_cell_root; -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_cell_cache_match(void *target, - const void *entry); -static void afs_cell_cache_update(void *source, void *entry); - -struct cachefs_index_def afs_cache_cell_index_def = { - .name = "cell_ix", - .data_size = sizeof(struct afs_cache_cell), - .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, - .match = afs_cell_cache_match, - .update = afs_cell_cache_update, -}; -#endif - -/*****************************************************************************/ /* - * create a cell record - * - "name" is the name of the cell - * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format + * allocate a cell record and fill in its name, VL server address list and + * allocate an anonymous key */ -int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell) +static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) { struct afs_cell *cell; - char *next; + size_t namelen; + char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next; int ret; - _enter("%s", name); + _enter("%s,%s", name, vllist); BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */ + namelen = strlen(name); + if (namelen > AFS_MAXCELLNAME) + return ERR_PTR(-ENAMETOOLONG); + /* allocate and initialise a cell record */ - cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL); + cell = kzalloc(sizeof(struct afs_cell) + namelen + 1, GFP_KERNEL); if (!cell) { _leave(" = -ENOMEM"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } - down_write(&afs_cells_sem); - - memset(cell, 0, sizeof(struct afs_cell)); - atomic_set(&cell->usage, 0); + memcpy(cell->name, name, namelen); + cell->name[namelen] = 0; + atomic_set(&cell->usage, 1); INIT_LIST_HEAD(&cell->link); - - rwlock_init(&cell->sv_lock); - INIT_LIST_HEAD(&cell->sv_list); - INIT_LIST_HEAD(&cell->sv_graveyard); - spin_lock_init(&cell->sv_gylock); - + rwlock_init(&cell->servers_lock); + INIT_LIST_HEAD(&cell->servers); init_rwsem(&cell->vl_sem); INIT_LIST_HEAD(&cell->vl_list); - INIT_LIST_HEAD(&cell->vl_graveyard); - spin_lock_init(&cell->vl_gylock); - - strcpy(cell->name,name); + spin_lock_init(&cell->vl_lock); /* fill in the VL server list from the rest of the string */ - ret = -EINVAL; do { unsigned a, b, c, d; @@ -96,20 +71,75 @@ int afs_cell_create(const char *name, ch *next++ = 0; if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) - goto badaddr; + goto bad_address; if (a > 255 || b > 255 || c > 255 || d > 255) - goto badaddr; + goto bad_address; cell->vl_addrs[cell->vl_naddrs++].s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); - if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS) - break; + } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && (vllist = next)); + + /* create a key to represent an anonymous user */ + memcpy(keyname, "afs@", 4); + dp = keyname + 4; + cp = cell->name; + do { + *dp++ = toupper(*cp); + } while (*cp++); + cell->anonymous_key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current, + KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(cell->anonymous_key)) { + _debug("no key"); + ret = PTR_ERR(cell->anonymous_key); + goto error; + } + + ret = key_instantiate_and_link(cell->anonymous_key, NULL, 0, + NULL, NULL); + if (ret < 0) { + _debug("instantiate failed"); + goto error; + } + + _debug("anon key %p{%x}", + cell->anonymous_key, key_serial(cell->anonymous_key)); + + _leave(" = %p", cell); + return cell; + +bad_address: + printk(KERN_ERR "kAFS: bad VL server IP address\n"); + ret = -EINVAL; +error: + key_put(cell->anonymous_key); + kfree(cell); + _leave(" = %d", ret); + return ERR_PTR(ret); +} + +/* + * create a cell record + * - "name" is the name of the cell + * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format + */ +struct afs_cell *afs_cell_create(const char *name, char *vllist) +{ + struct afs_cell *cell; + int ret; + + _enter("%s,%s", name, vllist); - } while(vllist = next, vllist); + cell = afs_cell_alloc(name, vllist); + if (IS_ERR(cell)) { + _leave(" = %ld", PTR_ERR(cell)); + return cell; + } + + down_write(&afs_cells_sem); - /* add a proc dir for this cell */ + /* add a proc directory for this cell */ ret = afs_proc_cell_setup(cell); if (ret < 0) goto error; @@ -130,31 +160,28 @@ #endif down_write(&afs_proc_cells_sem); list_add_tail(&cell->proc_link, &afs_proc_cells); up_write(&afs_proc_cells_sem); - - *_cell = cell; up_write(&afs_cells_sem); - _leave(" = 0 (%p)", cell); - return 0; + _leave(" = %p", cell); + return cell; - badaddr: - printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist); - error: +error: up_write(&afs_cells_sem); + key_put(cell->anonymous_key); kfree(cell); _leave(" = %d", ret); - return ret; -} /* end afs_cell_create() */ + return ERR_PTR(ret); +} -/*****************************************************************************/ /* - * initialise the cell database from module parameters + * set the root cell information + * - can be called with a module parameter string + * - can be called from a write to /proc/fs/afs/rootcell */ int afs_cell_init(char *rootcell) { struct afs_cell *old_root, *new_root; char *cp; - int ret; _enter(""); @@ -162,82 +189,60 @@ int afs_cell_init(char *rootcell) /* module is loaded with no parameters, or built statically. * - in the future we might initialize cell DB here. */ - _leave(" = 0 (but no root)"); + _leave(" = 0 [no root]"); return 0; } cp = strchr(rootcell, ':'); if (!cp) { printk(KERN_ERR "kAFS: no VL server IP addresses specified\n"); - _leave(" = %d (no colon)", -EINVAL); + _leave(" = -EINVAL"); return -EINVAL; } /* allocate a cell record for the root cell */ *cp++ = 0; - ret = afs_cell_create(rootcell, cp, &new_root); - if (ret < 0) { - _leave(" = %d", ret); - return ret; + new_root = afs_cell_create(rootcell, cp); + if (IS_ERR(new_root)) { + _leave(" = %ld", PTR_ERR(new_root)); + return PTR_ERR(new_root); } - /* as afs_put_cell() takes locks by itself, we have to do - * a little gymnastics to be race-free. - */ - afs_get_cell(new_root); - + /* install the new cell */ write_lock(&afs_cells_lock); - while (afs_cell_root) { - old_root = afs_cell_root; - afs_cell_root = NULL; - write_unlock(&afs_cells_lock); - afs_put_cell(old_root); - write_lock(&afs_cells_lock); - } + old_root = afs_cell_root; afs_cell_root = new_root; write_unlock(&afs_cells_lock); + afs_put_cell(old_root); - _leave(" = %d", ret); - return ret; - -} /* end afs_cell_init() */ + _leave(" = 0"); + return 0; +} -/*****************************************************************************/ /* * lookup a cell record */ -int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell) +struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz) { struct afs_cell *cell; - int ret; _enter("\"%*.*s\",", namesz, namesz, name ? name : ""); - *_cell = NULL; + down_read(&afs_cells_sem); + read_lock(&afs_cells_lock); if (name) { /* if the cell was named, look for it in the cell record list */ - ret = -ENOENT; - cell = NULL; - read_lock(&afs_cells_lock); - list_for_each_entry(cell, &afs_cells, link) { if (strncmp(cell->name, name, namesz) == 0) { afs_get_cell(cell); goto found; } } - cell = NULL; + cell = ERR_PTR(-ENOENT); found: - - read_unlock(&afs_cells_lock); - - if (cell) - ret = 0; - } - else { - read_lock(&afs_cells_lock); - + ; + } else { cell = afs_cell_root; if (!cell) { /* this should not happen unless user tries to mount @@ -246,44 +251,35 @@ int afs_cell_lookup(const char *name, un * ENOENT might be "more appropriate" but they happen * for other reasons. */ - ret = -EDESTADDRREQ; - } - else { + cell = ERR_PTR(-EDESTADDRREQ); + } else { afs_get_cell(cell); - ret = 0; } - read_unlock(&afs_cells_lock); } - *_cell = cell; - _leave(" = %d (%p)", ret, cell); - return ret; - -} /* end afs_cell_lookup() */ + read_unlock(&afs_cells_lock); + up_read(&afs_cells_sem); + _leave(" = %p", cell); + return cell; +} -/*****************************************************************************/ /* * try and get a cell record */ -struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell) +struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell) { - struct afs_cell *cell; - write_lock(&afs_cells_lock); - cell = *_cell; if (cell && !list_empty(&cell->link)) afs_get_cell(cell); else cell = NULL; write_unlock(&afs_cells_lock); - return cell; -} /* end afs_get_cell_maybe() */ +} -/*****************************************************************************/ /* * destroy a cell record */ @@ -294,8 +290,7 @@ void afs_put_cell(struct afs_cell *cell) _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); - /* sanity check */ - BUG_ON(atomic_read(&cell->usage) <= 0); + ASSERTCMP(atomic_read(&cell->usage), >, 0); /* to prevent a race, the decrement and the dequeue must be effectively * atomic */ @@ -307,36 +302,49 @@ void afs_put_cell(struct afs_cell *cell) return; } + ASSERT(list_empty(&cell->servers)); + ASSERT(list_empty(&cell->vl_list)); + write_unlock(&afs_cells_lock); - BUG_ON(!list_empty(&cell->sv_list)); - BUG_ON(!list_empty(&cell->sv_graveyard)); - BUG_ON(!list_empty(&cell->vl_list)); - BUG_ON(!list_empty(&cell->vl_graveyard)); + wake_up(&afs_cells_freeable_wq); _leave(" [unused]"); -} /* end afs_put_cell() */ +} -/*****************************************************************************/ /* * destroy a cell record + * - must be called with the afs_cells_sem write-locked + * - cell->link should have been broken by the caller */ static void afs_cell_destroy(struct afs_cell *cell) { _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); - /* to prevent a race, the decrement and the dequeue must be effectively - * atomic */ - write_lock(&afs_cells_lock); + ASSERTCMP(atomic_read(&cell->usage), >=, 0); + ASSERT(list_empty(&cell->link)); - /* sanity check */ - BUG_ON(atomic_read(&cell->usage) != 0); + /* wait for everyone to stop using the cell */ + if (atomic_read(&cell->usage) > 0) { + DECLARE_WAITQUEUE(myself, current); - list_del_init(&cell->link); + _debug("wait for cell %s", cell->name); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&afs_cells_freeable_wq, &myself); - write_unlock(&afs_cells_lock); + while (atomic_read(&cell->usage) > 0) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } - down_write(&afs_cells_sem); + remove_wait_queue(&afs_cells_freeable_wq, &myself); + set_current_state(TASK_RUNNING); + } + + _debug("cell dead"); + ASSERTCMP(atomic_read(&cell->usage), ==, 0); + ASSERT(list_empty(&cell->servers)); + ASSERT(list_empty(&cell->vl_list)); afs_proc_cell_remove(cell); @@ -348,104 +356,26 @@ #ifdef AFS_CACHING_SUPPORT cachefs_relinquish_cookie(cell->cache, 0); #endif - up_write(&afs_cells_sem); - - BUG_ON(!list_empty(&cell->sv_list)); - BUG_ON(!list_empty(&cell->sv_graveyard)); - BUG_ON(!list_empty(&cell->vl_list)); - BUG_ON(!list_empty(&cell->vl_graveyard)); - - /* finish cleaning up the cell */ + key_put(cell->anonymous_key); kfree(cell); _leave(" [destroyed]"); -} /* end afs_cell_destroy() */ - -/*****************************************************************************/ -/* - * lookup the server record corresponding to an Rx RPC peer - */ -int afs_server_find_by_peer(const struct rxrpc_peer *peer, - struct afs_server **_server) -{ - struct afs_server *server; - struct afs_cell *cell; - - _enter("%p{a=%08x},", peer, ntohl(peer->addr.s_addr)); - - /* search the cell list */ - read_lock(&afs_cells_lock); - - list_for_each_entry(cell, &afs_cells, link) { - - _debug("? cell %s",cell->name); - - write_lock(&cell->sv_lock); - - /* check the active list */ - list_for_each_entry(server, &cell->sv_list, link) { - _debug("?? server %08x", ntohl(server->addr.s_addr)); - - if (memcmp(&server->addr, &peer->addr, - sizeof(struct in_addr)) == 0) - goto found_server; - } +} - /* check the inactive list */ - spin_lock(&cell->sv_gylock); - list_for_each_entry(server, &cell->sv_graveyard, link) { - _debug("?? dead server %08x", - ntohl(server->addr.s_addr)); - - if (memcmp(&server->addr, &peer->addr, - sizeof(struct in_addr)) == 0) - goto found_dead_server; - } - spin_unlock(&cell->sv_gylock); - - write_unlock(&cell->sv_lock); - } - read_unlock(&afs_cells_lock); - - _leave(" = -ENOENT"); - return -ENOENT; - - /* we found it in the graveyard - resurrect it */ - found_dead_server: - list_move_tail(&server->link, &cell->sv_list); - afs_get_server(server); - afs_kafstimod_del_timer(&server->timeout); - spin_unlock(&cell->sv_gylock); - goto success; - - /* we found it - increment its ref count and return it */ - found_server: - afs_get_server(server); - - success: - write_unlock(&cell->sv_lock); - read_unlock(&afs_cells_lock); - - *_server = server; - _leave(" = 0 (s=%p c=%p)", server, cell); - return 0; - -} /* end afs_server_find_by_peer() */ - -/*****************************************************************************/ /* * purge in-memory cell database on module unload or afs_init() failure * - the timeout daemon is stopped before calling this */ void afs_cell_purge(void) { - struct afs_vlocation *vlocation; struct afs_cell *cell; _enter(""); afs_put_cell(afs_cell_root); + down_write(&afs_cells_sem); + while (!list_empty(&afs_cells)) { cell = NULL; @@ -464,104 +394,11 @@ void afs_cell_purge(void) _debug("PURGING CELL %s (%d)", cell->name, atomic_read(&cell->usage)); - BUG_ON(!list_empty(&cell->sv_list)); - BUG_ON(!list_empty(&cell->vl_list)); - - /* purge the cell's VL graveyard list */ - _debug(" - clearing VL graveyard"); - - spin_lock(&cell->vl_gylock); - - while (!list_empty(&cell->vl_graveyard)) { - vlocation = list_entry(cell->vl_graveyard.next, - struct afs_vlocation, - link); - list_del_init(&vlocation->link); - - afs_kafstimod_del_timer(&vlocation->timeout); - - spin_unlock(&cell->vl_gylock); - - afs_vlocation_do_timeout(vlocation); - /* TODO: race if move to use krxtimod instead - * of kafstimod */ - - spin_lock(&cell->vl_gylock); - } - - spin_unlock(&cell->vl_gylock); - - /* purge the cell's server graveyard list */ - _debug(" - clearing server graveyard"); - - spin_lock(&cell->sv_gylock); - - while (!list_empty(&cell->sv_graveyard)) { - struct afs_server *server; - - server = list_entry(cell->sv_graveyard.next, - struct afs_server, link); - list_del_init(&server->link); - - afs_kafstimod_del_timer(&server->timeout); - - spin_unlock(&cell->sv_gylock); - - afs_server_do_timeout(server); - - spin_lock(&cell->sv_gylock); - } - - spin_unlock(&cell->sv_gylock); - /* now the cell should be left with no references */ afs_cell_destroy(cell); } } + up_write(&afs_cells_sem); _leave(""); -} /* end afs_cell_purge() */ - -/*****************************************************************************/ -/* - * match a cell record obtained from the cache - */ -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_cell_cache_match(void *target, - const void *entry) -{ - const struct afs_cache_cell *ccell = entry; - struct afs_cell *cell = target; - - _enter("{%s},{%s}", ccell->name, cell->name); - - if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) { - _leave(" = SUCCESS"); - return CACHEFS_MATCH_SUCCESS; - } - - _leave(" = FAILED"); - return CACHEFS_MATCH_FAILED; -} /* end afs_cell_cache_match() */ -#endif - -/*****************************************************************************/ -/* - * update a cell record in the cache - */ -#ifdef AFS_CACHING_SUPPORT -static void afs_cell_cache_update(void *source, void *entry) -{ - struct afs_cache_cell *ccell = entry; - struct afs_cell *cell = source; - - _enter("%p,%p", source, entry); - - strncpy(ccell->name, cell->name, sizeof(ccell->name)); - - memcpy(ccell->vl_servers, - cell->vl_addrs, - min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs))); - -} /* end afs_cell_cache_update() */ -#endif +} diff --git a/fs/afs/cell.h b/fs/afs/cell.h deleted file mode 100644 index 4834910..0000000 --- a/fs/afs/cell.h +++ /dev/null @@ -1,78 +0,0 @@ -/* cell.h: AFS cell record - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_CELL_H -#define _LINUX_AFS_CELL_H - -#include "types.h" -#include "cache.h" - -#define AFS_CELL_MAX_ADDRS 15 - -extern volatile int afs_cells_being_purged; /* T when cells are being purged by rmmod */ - -/*****************************************************************************/ -/* - * entry in the cached cell catalogue - */ -struct afs_cache_cell -{ - char name[64]; /* cell name (padded with NULs) */ - struct in_addr vl_servers[15]; /* cached cell VL servers */ -}; - -/*****************************************************************************/ -/* - * AFS cell record - */ -struct afs_cell -{ - atomic_t usage; - struct list_head link; /* main cell list link */ - struct list_head proc_link; /* /proc cell list link */ - struct proc_dir_entry *proc_dir; /* /proc dir for this cell */ -#ifdef AFS_CACHING_SUPPORT - struct cachefs_cookie *cache; /* caching cookie */ -#endif - - /* server record management */ - rwlock_t sv_lock; /* active server list lock */ - struct list_head sv_list; /* active server list */ - struct list_head sv_graveyard; /* inactive server list */ - spinlock_t sv_gylock; /* inactive server list lock */ - - /* volume location record management */ - struct rw_semaphore vl_sem; /* volume management serialisation semaphore */ - struct list_head vl_list; /* cell's active VL record list */ - struct list_head vl_graveyard; /* cell's inactive VL record list */ - spinlock_t vl_gylock; /* graveyard lock */ - unsigned short vl_naddrs; /* number of VL servers in addr list */ - unsigned short vl_curr_svix; /* current server index */ - struct in_addr vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */ - - char name[0]; /* cell name - must go last */ -}; - -extern int afs_cell_init(char *rootcell); - -extern int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell); - -extern int afs_cell_lookup(const char *name, unsigned nmsize, struct afs_cell **_cell); - -#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) - -extern struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell); - -extern void afs_put_cell(struct afs_cell *cell); - -extern void afs_cell_purge(void); - -#endif /* _LINUX_AFS_CELL_H */ diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 3d097fd..6685f4c 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -1,4 +1,4 @@ -/* cmservice.c: AFS Cache Manager Service +/* AFS Cache Manager Service * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -12,641 +12,463 @@ #include #include #include -#include -#include "server.h" -#include "cell.h" -#include "transport.h" -#include -#include -#include -#include -#include "cmservice.h" +#include #include "internal.h" +#include "afs_cm.h" -static unsigned afscm_usage; /* AFS cache manager usage count */ -static struct rw_semaphore afscm_sem; /* AFS cache manager start/stop semaphore */ - -static int afscm_new_call(struct rxrpc_call *call); -static void afscm_attention(struct rxrpc_call *call); -static void afscm_error(struct rxrpc_call *call); -static void afscm_aemap(struct rxrpc_call *call); - -static void _SRXAFSCM_CallBack(struct rxrpc_call *call); -static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call); -static void _SRXAFSCM_Probe(struct rxrpc_call *call); - -typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call); - -static const struct rxrpc_operation AFSCM_ops[] = { - { - .id = 204, - .asize = RXRPC_APP_MARK_EOF, - .name = "CallBack", - .user = _SRXAFSCM_CallBack, - }, - { - .id = 205, - .asize = RXRPC_APP_MARK_EOF, - .name = "InitCallBackState", - .user = _SRXAFSCM_InitCallBackState, - }, - { - .id = 206, - .asize = RXRPC_APP_MARK_EOF, - .name = "Probe", - .user = _SRXAFSCM_Probe, - }, -#if 0 - { - .id = 207, - .asize = RXRPC_APP_MARK_EOF, - .name = "GetLock", - .user = _SRXAFSCM_GetLock, - }, - { - .id = 208, - .asize = RXRPC_APP_MARK_EOF, - .name = "GetCE", - .user = _SRXAFSCM_GetCE, - }, - { - .id = 209, - .asize = RXRPC_APP_MARK_EOF, - .name = "GetXStatsVersion", - .user = _SRXAFSCM_GetXStatsVersion, - }, - { - .id = 210, - .asize = RXRPC_APP_MARK_EOF, - .name = "GetXStats", - .user = _SRXAFSCM_GetXStats, - } -#endif -}; +struct workqueue_struct *afs_cm_workqueue; -static struct rxrpc_service AFSCM_service = { - .name = "AFS/CM", - .owner = THIS_MODULE, - .link = LIST_HEAD_INIT(AFSCM_service.link), - .new_call = afscm_new_call, - .service_id = 1, - .attn_func = afscm_attention, - .error_func = afscm_error, - .aemap_func = afscm_aemap, - .ops_begin = &AFSCM_ops[0], - .ops_end = &AFSCM_ops[ARRAY_SIZE(AFSCM_ops)], -}; +static int afs_deliver_cb_init_call_back_state(struct afs_call *, + struct sk_buff *, bool); +static int afs_deliver_cb_init_call_back_state3(struct afs_call *, + struct sk_buff *, bool); +static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool); +static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool); +static int afs_deliver_cb_get_capabilities(struct afs_call *, struct sk_buff *, + bool); +static void afs_cm_destructor(struct afs_call *); -static DECLARE_COMPLETION(kafscmd_alive); -static DECLARE_COMPLETION(kafscmd_dead); -static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq); -static LIST_HEAD(kafscmd_attention_list); -static LIST_HEAD(afscm_calls); -static DEFINE_SPINLOCK(afscm_calls_lock); -static DEFINE_SPINLOCK(kafscmd_attention_lock); -static int kafscmd_die; - -/*****************************************************************************/ /* - * AFS Cache Manager kernel thread + * CB.CallBack operation type */ -static int kafscmd(void *arg) -{ - DECLARE_WAITQUEUE(myself, current); - - struct rxrpc_call *call; - _SRXAFSCM_xxxx_t func; - int die; - - printk(KERN_INFO "kAFS: Started kafscmd %d\n", current->pid); - - daemonize("kafscmd"); - - complete(&kafscmd_alive); - - /* loop around looking for things to attend to */ - do { - if (list_empty(&kafscmd_attention_list)) { - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&kafscmd_sleepq, &myself); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (!list_empty(&kafscmd_attention_list) || - signal_pending(current) || - kafscmd_die) - break; - - schedule(); - } - - remove_wait_queue(&kafscmd_sleepq, &myself); - set_current_state(TASK_RUNNING); - } - - die = kafscmd_die; - - /* dequeue the next call requiring attention */ - call = NULL; - spin_lock(&kafscmd_attention_lock); - - if (!list_empty(&kafscmd_attention_list)) { - call = list_entry(kafscmd_attention_list.next, - struct rxrpc_call, - app_attn_link); - list_del_init(&call->app_attn_link); - die = 0; - } - - spin_unlock(&kafscmd_attention_lock); - - if (call) { - /* act upon it */ - _debug("@@@ Begin Attend Call %p", call); - - func = call->app_user; - if (func) - func(call); - - rxrpc_put_call(call); - - _debug("@@@ End Attend Call %p", call); - } - - } while(!die); - - /* and that's all */ - complete_and_exit(&kafscmd_dead, 0); - -} /* end kafscmd() */ +static const struct afs_call_type afs_SRXCBCallBack = { + .name = "CB.CallBack", + .deliver = afs_deliver_cb_callback, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * handle a call coming in to the cache manager - * - if I want to keep the call, I must increment its usage count - * - the return value will be negated and passed back in an abort packet if - * non-zero - * - serialised by virtue of there only being one krxiod + * CB.InitCallBackState operation type */ -static int afscm_new_call(struct rxrpc_call *call) -{ - _enter("%p{cid=%u u=%d}", - call, ntohl(call->call_id), atomic_read(&call->usage)); - - rxrpc_get_call(call); - - /* add to my current call list */ - spin_lock(&afscm_calls_lock); - list_add(&call->app_link,&afscm_calls); - spin_unlock(&afscm_calls_lock); - - _leave(" = 0"); - return 0; - -} /* end afscm_new_call() */ +static const struct afs_call_type afs_SRXCBInitCallBackState = { + .name = "CB.InitCallBackState", + .deliver = afs_deliver_cb_init_call_back_state, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * queue on the kafscmd queue for attention + * CB.InitCallBackState3 operation type */ -static void afscm_attention(struct rxrpc_call *call) -{ - _enter("%p{cid=%u u=%d}", - call, ntohl(call->call_id), atomic_read(&call->usage)); - - spin_lock(&kafscmd_attention_lock); - - if (list_empty(&call->app_attn_link)) { - list_add_tail(&call->app_attn_link, &kafscmd_attention_list); - rxrpc_get_call(call); - } - - spin_unlock(&kafscmd_attention_lock); - - wake_up(&kafscmd_sleepq); - - _leave(" {u=%d}", atomic_read(&call->usage)); -} /* end afscm_attention() */ +static const struct afs_call_type afs_SRXCBInitCallBackState3 = { + .name = "CB.InitCallBackState3", + .deliver = afs_deliver_cb_init_call_back_state3, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * handle my call being aborted - * - clean up, dequeue and put my ref to the call + * CB.Probe operation type */ -static void afscm_error(struct rxrpc_call *call) -{ - int removed; - - _enter("%p{est=%s ac=%u er=%d}", - call, - rxrpc_call_error_states[call->app_err_state], - call->app_abort_code, - call->app_errno); - - spin_lock(&kafscmd_attention_lock); - - if (list_empty(&call->app_attn_link)) { - list_add_tail(&call->app_attn_link, &kafscmd_attention_list); - rxrpc_get_call(call); - } - - spin_unlock(&kafscmd_attention_lock); - - removed = 0; - spin_lock(&afscm_calls_lock); - if (!list_empty(&call->app_link)) { - list_del_init(&call->app_link); - removed = 1; - } - spin_unlock(&afscm_calls_lock); - - if (removed) - rxrpc_put_call(call); - - wake_up(&kafscmd_sleepq); +static const struct afs_call_type afs_SRXCBProbe = { + .name = "CB.Probe", + .deliver = afs_deliver_cb_probe, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; - _leave(""); -} /* end afscm_error() */ +/* + * CB.GetCapabilities operation type + */ +static const struct afs_call_type afs_SRXCBGetCapabilites = { + .name = "CB.GetCapabilities", + .deliver = afs_deliver_cb_get_capabilities, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * map afs abort codes to/from Linux error codes - * - called with call->lock held + * route an incoming cache manager call + * - return T if supported, F if not */ -static void afscm_aemap(struct rxrpc_call *call) +bool afs_cm_incoming_call(struct afs_call *call) { - switch (call->app_err_state) { - case RXRPC_ESTATE_LOCAL_ABORT: - call->app_abort_code = -call->app_errno; - break; - case RXRPC_ESTATE_PEER_ABORT: - call->app_errno = -ECONNABORTED; - break; + u32 operation_id = ntohl(call->operation_ID); + + _enter("{CB.OP %u}", operation_id); + + switch (operation_id) { + case CBCallBack: + call->type = &afs_SRXCBCallBack; + return true; + case CBInitCallBackState: + call->type = &afs_SRXCBInitCallBackState; + return true; + case CBInitCallBackState3: + call->type = &afs_SRXCBInitCallBackState3; + return true; + case CBProbe: + call->type = &afs_SRXCBProbe; + return true; + case CBGetCapabilities: + call->type = &afs_SRXCBGetCapabilites; + return true; default: - break; + return false; } -} /* end afscm_aemap() */ +} -/*****************************************************************************/ /* - * start the cache manager service if not already started + * clean up a cache manager call */ -int afscm_start(void) +static void afs_cm_destructor(struct afs_call *call) { - int ret; - - down_write(&afscm_sem); - if (!afscm_usage) { - ret = kernel_thread(kafscmd, NULL, 0); - if (ret < 0) - goto out; - - wait_for_completion(&kafscmd_alive); - - ret = rxrpc_add_service(afs_transport, &AFSCM_service); - if (ret < 0) - goto kill; - - afs_kafstimod_add_timer(&afs_mntpt_expiry_timer, - afs_mntpt_expiry_timeout * HZ); - } - - afscm_usage++; - up_write(&afscm_sem); - - return 0; - - kill: - kafscmd_die = 1; - wake_up(&kafscmd_sleepq); - wait_for_completion(&kafscmd_dead); - - out: - up_write(&afscm_sem); - return ret; + _enter(""); -} /* end afscm_start() */ + afs_put_server(call->server); + call->server = NULL; + kfree(call->buffer); + call->buffer = NULL; +} -/*****************************************************************************/ /* - * stop the cache manager service + * allow the fileserver to see if the cache manager is still alive */ -void afscm_stop(void) +static void SRXAFSCB_CallBack(struct work_struct *work) { - struct rxrpc_call *call; + struct afs_call *call = container_of(work, struct afs_call, work); - down_write(&afscm_sem); + _enter(""); - BUG_ON(afscm_usage == 0); - afscm_usage--; + /* be sure to send the reply *before* attempting to spam the AFS server + * with FSFetchStatus requests on the vnodes with broken callbacks lest + * the AFS server get into a vicious cycle of trying to break further + * callbacks because it hadn't received completion of the CBCallBack op + * yet */ + afs_send_empty_reply(call); - if (afscm_usage == 0) { - /* don't want more incoming calls */ - rxrpc_del_service(afs_transport, &AFSCM_service); - - /* abort any calls I've still got open (the afscm_error() will - * dequeue them) */ - spin_lock(&afscm_calls_lock); - while (!list_empty(&afscm_calls)) { - call = list_entry(afscm_calls.next, - struct rxrpc_call, - app_link); + afs_break_callbacks(call->server, call->count, call->request); + _leave(""); +} - list_del_init(&call->app_link); - rxrpc_get_call(call); - spin_unlock(&afscm_calls_lock); +/* + * deliver request data to a CB.CallBack call + */ +static int afs_deliver_cb_callback(struct afs_call *call, struct sk_buff *skb, + bool last) +{ + struct afs_callback *cb; + struct afs_server *server; + struct in_addr addr; + __be32 *bp; + u32 tmp; + int ret, loop; + + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); + + switch (call->unmarshall) { + case 0: + call->offset = 0; + call->unmarshall++; + + /* extract the FID array and its count in two steps */ + case 1: + _debug("extract FID count"); + ret = afs_extract_data(call, skb, last, &call->tmp, 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and - * put */ + call->count = ntohl(call->tmp); + _debug("FID count: %u", call->count); + if (call->count > AFSCBMAX) + return -EBADMSG; + + call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL); + if (!call->buffer) + return -ENOMEM; + call->offset = 0; + call->unmarshall++; + + case 2: + _debug("extract FID array"); + ret = afs_extract_data(call, skb, last, call->buffer, + call->count * 3 * 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - _debug("nuking active call %08x.%d", - ntohl(call->conn->conn_id), - ntohl(call->call_id)); - rxrpc_put_call(call); - rxrpc_put_call(call); + _debug("unmarshall FID array"); + call->request = kcalloc(call->count, + sizeof(struct afs_callback), + GFP_KERNEL); + if (!call->request) + return -ENOMEM; + + cb = call->request; + bp = call->buffer; + for (loop = call->count; loop > 0; loop--, cb++) { + cb->fid.vid = ntohl(*bp++); + cb->fid.vnode = ntohl(*bp++); + cb->fid.unique = ntohl(*bp++); + cb->type = AFSCM_CB_UNTYPED; + } - spin_lock(&afscm_calls_lock); + call->offset = 0; + call->unmarshall++; + + /* extract the callback array and its count in two steps */ + case 3: + _debug("extract CB count"); + ret = afs_extract_data(call, skb, last, &call->tmp, 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; } - spin_unlock(&afscm_calls_lock); - /* get rid of my daemon */ - kafscmd_die = 1; - wake_up(&kafscmd_sleepq); - wait_for_completion(&kafscmd_dead); + tmp = ntohl(call->tmp); + _debug("CB count: %u", tmp); + if (tmp != call->count && tmp != 0) + return -EBADMSG; + call->offset = 0; + call->unmarshall++; + if (tmp == 0) + goto empty_cb_array; + + case 4: + _debug("extract CB array"); + ret = afs_extract_data(call, skb, last, call->request, + call->count * 3 * 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - /* dispose of any calls waiting for attention */ - spin_lock(&kafscmd_attention_lock); - while (!list_empty(&kafscmd_attention_list)) { - call = list_entry(kafscmd_attention_list.next, - struct rxrpc_call, - app_attn_link); + _debug("unmarshall CB array"); + cb = call->request; + bp = call->buffer; + for (loop = call->count; loop > 0; loop--, cb++) { + cb->version = ntohl(*bp++); + cb->expiry = ntohl(*bp++); + cb->type = ntohl(*bp++); + } - list_del_init(&call->app_attn_link); - spin_unlock(&kafscmd_attention_lock); + empty_cb_array: + call->offset = 0; + call->unmarshall++; - rxrpc_put_call(call); + case 5: + _debug("trailer"); + if (skb->len != 0) + return -EBADMSG; + break; + } - spin_lock(&kafscmd_attention_lock); - } - spin_unlock(&kafscmd_attention_lock); + if (!last) + return 0; - afs_kafstimod_del_timer(&afs_mntpt_expiry_timer); - } + call->state = AFS_CALL_REPLYING; - up_write(&afscm_sem); + /* we'll need the file server record as that tells us which set of + * vnodes to operate upon */ + memcpy(&addr, &ip_hdr(skb)->saddr, 4); + server = afs_find_server(&addr); + if (!server) + return -ENOTCONN; + call->server = server; -} /* end afscm_stop() */ + INIT_WORK(&call->work, SRXAFSCB_CallBack); + schedule_work(&call->work); + return 0; +} -/*****************************************************************************/ /* - * handle the fileserver breaking a set of callbacks + * allow the fileserver to request callback state (re-)initialisation */ -static void _SRXAFSCM_CallBack(struct rxrpc_call *call) +static void SRXAFSCB_InitCallBackState(struct work_struct *work) { - struct afs_server *server; - size_t count, qty, tmp; - int ret = 0, removed; - - _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); - - server = afs_server_get_from_peer(call->conn->peer); - - switch (call->app_call_state) { - /* we've received the last packet - * - drain all the data from the call and send the reply - */ - case RXRPC_CSTATE_SRVR_GOT_ARGS: - ret = -EBADMSG; - qty = call->app_ready_qty; - if (qty < 8 || qty > 50 * (6 * 4) + 8) - break; - - { - struct afs_callback *cb, *pcb; - int loop; - __be32 *fp, *bp; - - fp = rxrpc_call_alloc_scratch(call, qty); - - /* drag the entire argument block out to the scratch - * space */ - ret = rxrpc_call_read_data(call, fp, qty, 0); - if (ret < 0) - break; - - /* and unmarshall the parameter block */ - ret = -EBADMSG; - count = ntohl(*fp++); - if (count>AFSCBMAX || - (count * (3 * 4) + 8 != qty && - count * (6 * 4) + 8 != qty)) - break; - - bp = fp + count*3; - tmp = ntohl(*bp++); - if (tmp > 0 && tmp != count) - break; - if (tmp == 0) - bp = NULL; - - pcb = cb = rxrpc_call_alloc_scratch_s( - call, struct afs_callback); - - for (loop = count - 1; loop >= 0; loop--) { - pcb->fid.vid = ntohl(*fp++); - pcb->fid.vnode = ntohl(*fp++); - pcb->fid.unique = ntohl(*fp++); - if (bp) { - pcb->version = ntohl(*bp++); - pcb->expiry = ntohl(*bp++); - pcb->type = ntohl(*bp++); - } - else { - pcb->version = 0; - pcb->expiry = 0; - pcb->type = AFSCM_CB_UNTYPED; - } - pcb++; - } - - /* invoke the actual service routine */ - ret = SRXAFSCM_CallBack(server, count, cb); - if (ret < 0) - break; - } + struct afs_call *call = container_of(work, struct afs_call, work); - /* send the reply */ - ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET, - GFP_KERNEL, 0, &count); - if (ret < 0) - break; - break; - - /* operation complete */ - case RXRPC_CSTATE_COMPLETE: - call->app_user = NULL; - removed = 0; - spin_lock(&afscm_calls_lock); - if (!list_empty(&call->app_link)) { - list_del_init(&call->app_link); - removed = 1; - } - spin_unlock(&afscm_calls_lock); + _enter("{%p}", call->server); - if (removed) - rxrpc_put_call(call); - break; + afs_init_callback_state(call->server); + afs_send_empty_reply(call); + _leave(""); +} - /* operation terminated on error */ - case RXRPC_CSTATE_ERROR: - call->app_user = NULL; - break; +/* + * deliver request data to a CB.InitCallBackState call + */ +static int afs_deliver_cb_init_call_back_state(struct afs_call *call, + struct sk_buff *skb, + bool last) +{ + struct afs_server *server; + struct in_addr addr; - default: - break; - } + _enter(",{%u},%d", skb->len, last); - if (ret < 0) - rxrpc_call_abort(call, ret); + if (skb->len > 0) + return -EBADMSG; + if (!last) + return 0; - afs_put_server(server); + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; - _leave(" = %d", ret); + /* we'll need the file server record as that tells us which set of + * vnodes to operate upon */ + memcpy(&addr, &ip_hdr(skb)->saddr, 4); + server = afs_find_server(&addr); + if (!server) + return -ENOTCONN; + call->server = server; -} /* end _SRXAFSCM_CallBack() */ + INIT_WORK(&call->work, SRXAFSCB_InitCallBackState); + schedule_work(&call->work); + return 0; +} -/*****************************************************************************/ /* - * handle the fileserver asking us to initialise our callback state + * deliver request data to a CB.InitCallBackState3 call */ -static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call) +static int afs_deliver_cb_init_call_back_state3(struct afs_call *call, + struct sk_buff *skb, + bool last) { struct afs_server *server; - size_t count; - int ret = 0, removed; + struct in_addr addr; - _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); + _enter(",{%u},%d", skb->len, last); - server = afs_server_get_from_peer(call->conn->peer); + if (!last) + return 0; - switch (call->app_call_state) { - /* we've received the last packet - drain all the data from the - * call */ - case RXRPC_CSTATE_SRVR_GOT_ARGS: - /* shouldn't be any args */ - ret = -EBADMSG; - break; - - /* send the reply when asked for it */ - case RXRPC_CSTATE_SRVR_SND_REPLY: - /* invoke the actual service routine */ - ret = SRXAFSCM_InitCallBackState(server); - if (ret < 0) - break; - - ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET, - GFP_KERNEL, 0, &count); - if (ret < 0) - break; - break; + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; - /* operation complete */ - case RXRPC_CSTATE_COMPLETE: - call->app_user = NULL; - removed = 0; - spin_lock(&afscm_calls_lock); - if (!list_empty(&call->app_link)) { - list_del_init(&call->app_link); - removed = 1; - } - spin_unlock(&afscm_calls_lock); + /* we'll need the file server record as that tells us which set of + * vnodes to operate upon */ + memcpy(&addr, &ip_hdr(skb)->saddr, 4); + server = afs_find_server(&addr); + if (!server) + return -ENOTCONN; + call->server = server; - if (removed) - rxrpc_put_call(call); - break; - - /* operation terminated on error */ - case RXRPC_CSTATE_ERROR: - call->app_user = NULL; - break; - - default: - break; - } - - if (ret < 0) - rxrpc_call_abort(call, ret); - - afs_put_server(server); + INIT_WORK(&call->work, SRXAFSCB_InitCallBackState); + schedule_work(&call->work); + return 0; +} - _leave(" = %d", ret); +/* + * allow the fileserver to see if the cache manager is still alive + */ +static void SRXAFSCB_Probe(struct work_struct *work) +{ + struct afs_call *call = container_of(work, struct afs_call, work); -} /* end _SRXAFSCM_InitCallBackState() */ + _enter(""); + afs_send_empty_reply(call); + _leave(""); +} -/*****************************************************************************/ /* - * handle a probe from a fileserver + * deliver request data to a CB.Probe call */ -static void _SRXAFSCM_Probe(struct rxrpc_call *call) +static int afs_deliver_cb_probe(struct afs_call *call, struct sk_buff *skb, + bool last) { - struct afs_server *server; - size_t count; - int ret = 0, removed; - - _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); + _enter(",{%u},%d", skb->len, last); - server = afs_server_get_from_peer(call->conn->peer); + if (skb->len > 0) + return -EBADMSG; + if (!last) + return 0; - switch (call->app_call_state) { - /* we've received the last packet - drain all the data from the - * call */ - case RXRPC_CSTATE_SRVR_GOT_ARGS: - /* shouldn't be any args */ - ret = -EBADMSG; - break; + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; - /* send the reply when asked for it */ - case RXRPC_CSTATE_SRVR_SND_REPLY: - /* invoke the actual service routine */ - ret = SRXAFSCM_Probe(server); - if (ret < 0) - break; - - ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET, - GFP_KERNEL, 0, &count); - if (ret < 0) - break; - break; + INIT_WORK(&call->work, SRXAFSCB_Probe); + schedule_work(&call->work); + return 0; +} - /* operation complete */ - case RXRPC_CSTATE_COMPLETE: - call->app_user = NULL; - removed = 0; - spin_lock(&afscm_calls_lock); - if (!list_empty(&call->app_link)) { - list_del_init(&call->app_link); - removed = 1; +/* + * allow the fileserver to ask about the cache manager's capabilities + */ +static void SRXAFSCB_GetCapabilities(struct work_struct *work) +{ + struct afs_interface *ifs; + struct afs_call *call = container_of(work, struct afs_call, work); + int loop, nifs; + + struct { + struct /* InterfaceAddr */ { + __be32 nifs; + __be32 uuid[11]; + __be32 ifaddr[32]; + __be32 netmask[32]; + __be32 mtu[32]; + } ia; + struct /* Capabilities */ { + __be32 capcount; + __be32 caps[1]; + } cap; + } reply; + + _enter(""); + + nifs = 0; + ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL); + if (ifs) { + nifs = afs_get_ipv4_interfaces(ifs, 32, false); + if (nifs < 0) { + kfree(ifs); + ifs = NULL; + nifs = 0; } - spin_unlock(&afscm_calls_lock); + } - if (removed) - rxrpc_put_call(call); - break; + memset(&reply, 0, sizeof(reply)); + reply.ia.nifs = htonl(nifs); + + reply.ia.uuid[0] = htonl(afs_uuid.time_low); + reply.ia.uuid[1] = htonl(afs_uuid.time_mid); + reply.ia.uuid[2] = htonl(afs_uuid.time_hi_and_version); + reply.ia.uuid[3] = htonl((s8) afs_uuid.clock_seq_hi_and_reserved); + reply.ia.uuid[4] = htonl((s8) afs_uuid.clock_seq_low); + for (loop = 0; loop < 6; loop++) + reply.ia.uuid[loop + 5] = htonl((s8) afs_uuid.node[loop]); + + if (ifs) { + for (loop = 0; loop < nifs; loop++) { + reply.ia.ifaddr[loop] = ifs[loop].address.s_addr; + reply.ia.netmask[loop] = ifs[loop].netmask.s_addr; + reply.ia.mtu[loop] = htonl(ifs[loop].mtu); + } + } - /* operation terminated on error */ - case RXRPC_CSTATE_ERROR: - call->app_user = NULL; - break; + reply.cap.capcount = htonl(1); + reply.cap.caps[0] = htonl(AFS_CAP_ERROR_TRANSLATION); + afs_send_simple_reply(call, &reply, sizeof(reply)); - default: - break; - } + _leave(""); +} - if (ret < 0) - rxrpc_call_abort(call, ret); +/* + * deliver request data to a CB.GetCapabilities call + */ +static int afs_deliver_cb_get_capabilities(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + _enter(",{%u},%d", skb->len, last); - afs_put_server(server); + if (skb->len > 0) + return -EBADMSG; + if (!last) + return 0; - _leave(" = %d", ret); + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; -} /* end _SRXAFSCM_Probe() */ + INIT_WORK(&call->work, SRXAFSCB_GetCapabilities); + schedule_work(&call->work); + return 0; +} diff --git a/fs/afs/cmservice.h b/fs/afs/cmservice.h deleted file mode 100644 index af8d4d6..0000000 --- a/fs/afs/cmservice.h +++ /dev/null @@ -1,29 +0,0 @@ -/* cmservice.h: AFS Cache Manager Service declarations - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_CMSERVICE_H -#define _LINUX_AFS_CMSERVICE_H - -#include -#include "types.h" - -/* cache manager start/stop */ -extern int afscm_start(void); -extern void afscm_stop(void); - -/* cache manager server functions */ -extern int SRXAFSCM_InitCallBackState(struct afs_server *server); -extern int SRXAFSCM_CallBack(struct afs_server *server, - size_t count, - struct afs_callback callbacks[]); -extern int SRXAFSCM_Probe(struct afs_server *server); - -#endif /* _LINUX_AFS_CMSERVICE_H */ diff --git a/fs/afs/dir.c b/fs/afs/dir.c index b6dc2eb..dac5b99 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -15,45 +15,53 @@ #include #include #include #include -#include -#include "vnode.h" -#include "volume.h" -#include -#include "super.h" +#include #include "internal.h" -static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd); +static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd); static int afs_dir_open(struct inode *inode, struct file *file); -static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir); +static int afs_readdir(struct file *file, void *dirent, filldir_t filldir); static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); static int afs_d_delete(struct dentry *dentry); -static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, +static void afs_d_release(struct dentry *dentry); +static int afs_lookup_filldir(void *_cookie, const char *name, int nlen, loff_t fpos, u64 ino, unsigned dtype); +static int afs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd); +static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode); +static int afs_rmdir(struct inode *dir, struct dentry *dentry); +static int afs_unlink(struct inode *dir, struct dentry *dentry); +static int afs_link(struct dentry *from, struct inode *dir, + struct dentry *dentry); +static int afs_symlink(struct inode *dir, struct dentry *dentry, + const char *content); +static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); const struct file_operations afs_dir_file_operations = { .open = afs_dir_open, - .readdir = afs_dir_readdir, + .release = afs_release, + .readdir = afs_readdir, }; const struct inode_operations afs_dir_inode_operations = { - .lookup = afs_dir_lookup, + .create = afs_create, + .lookup = afs_lookup, + .link = afs_link, + .unlink = afs_unlink, + .symlink = afs_symlink, + .mkdir = afs_mkdir, + .rmdir = afs_rmdir, + .rename = afs_rename, + .permission = afs_permission, .getattr = afs_inode_getattr, -#if 0 /* TODO */ - .create = afs_dir_create, - .link = afs_dir_link, - .unlink = afs_dir_unlink, - .symlink = afs_dir_symlink, - .mkdir = afs_dir_mkdir, - .rmdir = afs_dir_rmdir, - .mknod = afs_dir_mknod, - .rename = afs_dir_rename, -#endif }; static struct dentry_operations afs_fs_dentry_operations = { .d_revalidate = afs_d_revalidate, .d_delete = afs_d_delete, + .d_release = afs_d_release, }; #define AFS_DIR_HASHTBL_SIZE 128 @@ -105,14 +113,13 @@ struct afs_dir_page { union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; }; -struct afs_dir_lookup_cookie { +struct afs_lookup_cookie { struct afs_fid fid; const char *name; size_t nlen; int found; }; -/*****************************************************************************/ /* * check that a directory page is valid */ @@ -128,9 +135,10 @@ #if 0 if (qty == 0) goto error; - if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) { + if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) { printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n", - __FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages)); + __FUNCTION__, dir->i_ino, qty, + ntohs(dbuf->blocks[0].pagehdr.npages)); goto error; } #endif @@ -157,13 +165,11 @@ #endif SetPageChecked(page); return; - error: +error: SetPageChecked(page); SetPageError(page); +} -} /* end afs_dir_check_page() */ - -/*****************************************************************************/ /* * discard a page cached in the pagecache */ @@ -171,20 +177,22 @@ static inline void afs_dir_put_page(stru { kunmap(page); page_cache_release(page); +} -} /* end afs_dir_put_page() */ - -/*****************************************************************************/ /* * get a page into the pagecache */ -static struct page *afs_dir_get_page(struct inode *dir, unsigned long index) +static struct page *afs_dir_get_page(struct inode *dir, unsigned long index, + struct key *key) { struct page *page; + struct file file = { + .private_data = key, + }; _enter("{%lu},%lu", dir->i_ino, index); - page = read_mapping_page(dir->i_mapping, index, NULL); + page = read_mapping_page(dir->i_mapping, index, &file); if (!IS_ERR(page)) { wait_on_page_locked(page); kmap(page); @@ -197,12 +205,12 @@ static struct page *afs_dir_get_page(str } return page; - fail: +fail: afs_dir_put_page(page); + _leave(" = -EIO"); return ERR_PTR(-EIO); -} /* end afs_dir_get_page() */ +} -/*****************************************************************************/ /* * open an AFS directory file */ @@ -213,15 +221,12 @@ static int afs_dir_open(struct inode *in BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); BUILD_BUG_ON(sizeof(union afs_dirent) != 32); - if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) + if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags)) return -ENOENT; - _leave(" = 0"); - return 0; - -} /* end afs_dir_open() */ + return afs_open(inode, file); +} -/*****************************************************************************/ /* * deal with one block in an AFS directory */ @@ -250,7 +255,7 @@ static int afs_dir_iterate_block(unsigne /* skip entries marked unused in the bitmap */ if (!(block->pagehdr.bitmap[offset / 8] & (1 << (offset % 8)))) { - _debug("ENT[%Zu.%u]: unused\n", + _debug("ENT[%Zu.%u]: unused", blkoff / sizeof(union afs_dir_block), offset); if (offset >= curr) *fpos = blkoff + @@ -264,7 +269,7 @@ static int afs_dir_iterate_block(unsigne sizeof(*block) - offset * sizeof(union afs_dirent)); - _debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n", + _debug("ENT[%Zu.%u]: %s %Zu \"%s\"", blkoff / sizeof(union afs_dir_block), offset, (offset < curr ? "skip" : "fill"), nlen, dire->u.name); @@ -274,7 +279,7 @@ static int afs_dir_iterate_block(unsigne if (next >= AFS_DIRENT_PER_BLOCK) { _debug("ENT[%Zu.%u]:" " %u travelled beyond end dir block" - " (len %u/%Zu)\n", + " (len %u/%Zu)", blkoff / sizeof(union afs_dir_block), offset, next, tmp, nlen); return -EIO; @@ -282,13 +287,13 @@ static int afs_dir_iterate_block(unsigne if (!(block->pagehdr.bitmap[next / 8] & (1 << (next % 8)))) { _debug("ENT[%Zu.%u]:" - " %u unmarked extension (len %u/%Zu)\n", + " %u unmarked extension (len %u/%Zu)", blkoff / sizeof(union afs_dir_block), offset, next, tmp, nlen); return -EIO; } - _debug("ENT[%Zu.%u]: ext %u/%Zu\n", + _debug("ENT[%Zu.%u]: ext %u/%Zu", blkoff / sizeof(union afs_dir_block), next, tmp, nlen); next++; @@ -304,7 +309,7 @@ static int afs_dir_iterate_block(unsigne nlen, blkoff + offset * sizeof(union afs_dirent), ntohl(dire->u.vnode), - filldir == afs_dir_lookup_filldir ? + filldir == afs_lookup_filldir ? ntohl(dire->u.unique) : DT_UNKNOWN); if (ret < 0) { _leave(" = 0 [full]"); @@ -316,16 +321,15 @@ static int afs_dir_iterate_block(unsigne _leave(" = 1 [more]"); return 1; -} /* end afs_dir_iterate_block() */ +} -/*****************************************************************************/ /* - * read an AFS directory + * iterate through the data blob that lists the contents of an AFS directory */ static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie, - filldir_t filldir) + filldir_t filldir, struct key *key) { - union afs_dir_block *dblock; + union afs_dir_block *dblock; struct afs_dir_page *dbuf; struct page *page; unsigned blkoff, limit; @@ -333,7 +337,7 @@ static int afs_dir_iterate(struct inode _enter("{%lu},%u,,", dir->i_ino, *fpos); - if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { + if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) { _leave(" = -ESTALE"); return -ESTALE; } @@ -348,7 +352,7 @@ static int afs_dir_iterate(struct inode blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1); /* fetch the appropriate page from the directory */ - page = afs_dir_get_page(dir, blkoff / PAGE_SIZE); + page = afs_dir_get_page(dir, blkoff / PAGE_SIZE, key); if (IS_ERR(page)) { ret = PTR_ERR(page); break; @@ -377,43 +381,50 @@ static int afs_dir_iterate(struct inode ret = 0; } - out: +out: _leave(" = %d", ret); return ret; -} /* end afs_dir_iterate() */ +} -/*****************************************************************************/ /* * read an AFS directory */ -static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) +static int afs_readdir(struct file *file, void *cookie, filldir_t filldir) { unsigned fpos; int ret; - _enter("{%Ld,{%lu}}", file->f_pos, file->f_path.dentry->d_inode->i_ino); + _enter("{%Ld,{%lu}}", + file->f_pos, file->f_path.dentry->d_inode->i_ino); + + ASSERT(file->private_data != NULL); fpos = file->f_pos; - ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, cookie, filldir); + ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, + cookie, filldir, file->private_data); file->f_pos = fpos; _leave(" = %d", ret); return ret; -} /* end afs_dir_readdir() */ +} -/*****************************************************************************/ /* * search the directory for a name * - if afs_dir_iterate_block() spots this function, it'll pass the FID * uniquifier through dtype */ -static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, - loff_t fpos, u64 ino, unsigned dtype) +static int afs_lookup_filldir(void *_cookie, const char *name, int nlen, + loff_t fpos, u64 ino, unsigned dtype) { - struct afs_dir_lookup_cookie *cookie = _cookie; + struct afs_lookup_cookie *cookie = _cookie; - _enter("{%s,%Zu},%s,%u,,%lu,%u", - cookie->name, cookie->nlen, name, nlen, ino, dtype); + _enter("{%s,%Zu},%s,%u,,%llu,%u", + cookie->name, cookie->nlen, name, nlen, + (unsigned long long) ino, dtype); + + /* insanity checks first */ + BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); + BUILD_BUG_ON(sizeof(union afs_dirent) != 32); if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) { _leave(" = 0 [no]"); @@ -426,216 +437,254 @@ static int afs_dir_lookup_filldir(void * _leave(" = -1 [found]"); return -1; -} /* end afs_dir_lookup_filldir() */ +} -/*****************************************************************************/ /* - * look up an entry in a directory + * do a lookup in a directory + * - just returns the FID the dentry name maps to if found */ -static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) +static int afs_do_lookup(struct inode *dir, struct dentry *dentry, + struct afs_fid *fid, struct key *key) { - struct afs_dir_lookup_cookie cookie; + struct afs_lookup_cookie cookie; struct afs_super_info *as; + unsigned fpos; + int ret; + + _enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name); + + as = dir->i_sb->s_fs_info; + + /* search the directory */ + cookie.name = dentry->d_name.name; + cookie.nlen = dentry->d_name.len; + cookie.fid.vid = as->volume->vid; + cookie.found = 0; + + fpos = 0; + ret = afs_dir_iterate(dir, &fpos, &cookie, afs_lookup_filldir, + key); + if (ret < 0) { + _leave(" = %d [iter]", ret); + return ret; + } + + ret = -ENOENT; + if (!cookie.found) { + _leave(" = -ENOENT [not found]"); + return -ENOENT; + } + + *fid = cookie.fid; + _leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique); + return 0; +} + +/* + * look up an entry in a directory + */ +static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ struct afs_vnode *vnode; + struct afs_fid fid; struct inode *inode; - unsigned fpos; + struct key *key; int ret; - _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); + vnode = AFS_FS_I(dir); - /* insanity checks first */ - BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); - BUILD_BUG_ON(sizeof(union afs_dirent) != 32); + _enter("{%x:%d},%p{%s},", + vnode->fid.vid, vnode->fid.vnode, dentry, dentry->d_name.name); + + ASSERTCMP(dentry->d_inode, ==, NULL); if (dentry->d_name.len > 255) { _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); } - vnode = AFS_FS_I(dir); - if (vnode->flags & AFS_VNODE_DELETED) { + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { _leave(" = -ESTALE"); return ERR_PTR(-ESTALE); } - as = dir->i_sb->s_fs_info; - - /* search the directory */ - cookie.name = dentry->d_name.name; - cookie.nlen = dentry->d_name.len; - cookie.fid.vid = as->volume->vid; - cookie.found = 0; + key = afs_request_key(vnode->volume->cell); + if (IS_ERR(key)) { + _leave(" = %ld [key]", PTR_ERR(key)); + return ERR_PTR(PTR_ERR(key)); + } - fpos = 0; - ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir); + ret = afs_validate(vnode, key); if (ret < 0) { - _leave(" = %d", ret); + key_put(key); + _leave(" = %d [val]", ret); return ERR_PTR(ret); } - ret = -ENOENT; - if (!cookie.found) { - _leave(" = %d", ret); + ret = afs_do_lookup(dir, dentry, &fid, key); + if (ret < 0) { + key_put(key); + if (ret == -ENOENT) { + d_add(dentry, NULL); + _leave(" = NULL [negative]"); + return NULL; + } + _leave(" = %d [do]", ret); return ERR_PTR(ret); } + dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version; /* instantiate the dentry */ - ret = afs_iget(dir->i_sb, &cookie.fid, &inode); - if (ret < 0) { - _leave(" = %d", ret); - return ERR_PTR(ret); + inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL); + key_put(key); + if (IS_ERR(inode)) { + _leave(" = %ld", PTR_ERR(inode)); + return ERR_PTR(PTR_ERR(inode)); } dentry->d_op = &afs_fs_dentry_operations; - dentry->d_fsdata = (void *) (unsigned long) vnode->status.version; d_add(dentry, inode); _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }", - cookie.fid.vnode, - cookie.fid.unique, + fid.vnode, + fid.unique, dentry->d_inode->i_ino, dentry->d_inode->i_version); return NULL; -} /* end afs_dir_lookup() */ +} -/*****************************************************************************/ /* * check that a dentry lookup hit has found a valid entry * - NOTE! the hit can be a negative hit too, so we can't assume we have an * inode - * (derived from nfs_lookup_revalidate) */ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct afs_dir_lookup_cookie cookie; + struct afs_vnode *vnode, *dir; + struct afs_fid fid; struct dentry *parent; - struct inode *inode, *dir; - unsigned fpos; + struct key *key; + void *dir_version; int ret; - _enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name); + vnode = AFS_FS_I(dentry->d_inode); - /* lock down the parent dentry so we can peer at it */ - parent = dget_parent(dentry->d_parent); + if (dentry->d_inode) + _enter("{v={%x:%u} n=%s fl=%lx},", + vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name, + vnode->flags); + else + _enter("{neg n=%s}", dentry->d_name.name); - dir = parent->d_inode; - inode = dentry->d_inode; + key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell); + if (IS_ERR(key)) + key = NULL; - /* handle a negative dentry */ - if (!inode) + /* lock down the parent dentry so we can peer at it */ + parent = dget_parent(dentry); + if (!parent->d_inode) goto out_bad; - /* handle a bad inode */ - if (is_bad_inode(inode)) { - printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - goto out_bad; - } + dir = AFS_FS_I(parent->d_inode); - /* force a full look up if the parent directory changed since last the - * server was consulted - * - otherwise this inode must still exist, even if the inode details - * themselves have changed - */ - if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED) - afs_vnode_fetch_status(AFS_FS_I(dir)); + /* validate the parent directory */ + if (test_bit(AFS_VNODE_MODIFIED, &dir->flags)) + afs_validate(dir, key); - if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { + if (test_bit(AFS_VNODE_DELETED, &dir->flags)) { _debug("%s: parent dir deleted", dentry->d_name.name); goto out_bad; } - if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) { - _debug("%s: file already deleted", dentry->d_name.name); - goto out_bad; - } - - if ((unsigned long) dentry->d_fsdata != - (unsigned long) AFS_FS_I(dir)->status.version) { - _debug("%s: parent changed %lu -> %u", - dentry->d_name.name, - (unsigned long) dentry->d_fsdata, - (unsigned) AFS_FS_I(dir)->status.version); + dir_version = (void *) (unsigned long) dir->status.data_version; + if (dentry->d_fsdata == dir_version) + goto out_valid; /* the dir contents are unchanged */ - /* search the directory for this vnode */ - cookie.name = dentry->d_name.name; - cookie.nlen = dentry->d_name.len; - cookie.fid.vid = AFS_FS_I(inode)->volume->vid; - cookie.found = 0; + _debug("dir modified"); - fpos = 0; - ret = afs_dir_iterate(dir, &fpos, &cookie, - afs_dir_lookup_filldir); - if (ret < 0) { - _debug("failed to iterate dir %s: %d", - parent->d_name.name, ret); + /* search the directory for this vnode */ + ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key); + switch (ret) { + case 0: + /* the filename maps to something */ + if (!dentry->d_inode) + goto out_bad; + if (is_bad_inode(dentry->d_inode)) { + printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", + parent->d_name.name, dentry->d_name.name); goto out_bad; - } - - if (!cookie.found) { - _debug("%s: dirent not found", dentry->d_name.name); - goto not_found; } /* if the vnode ID has changed, then the dirent points to a * different file */ - if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) { - _debug("%s: dirent changed", dentry->d_name.name); + if (fid.vnode != vnode->fid.vnode) { + _debug("%s: dirent changed [%u != %u]", + dentry->d_name.name, fid.vnode, + vnode->fid.vnode); goto not_found; } /* if the vnode ID uniqifier has changed, then the file has - * been deleted */ - if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) { + * been deleted and replaced, and the original vnode ID has + * been reused */ + if (fid.unique != vnode->fid.unique) { _debug("%s: file deleted (uq %u -> %u I:%lu)", - dentry->d_name.name, - cookie.fid.unique, - AFS_FS_I(inode)->fid.unique, - inode->i_version); - spin_lock(&AFS_FS_I(inode)->lock); - AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED; - spin_unlock(&AFS_FS_I(inode)->lock); - invalidate_remote_inode(inode); - goto out_bad; + dentry->d_name.name, fid.unique, + vnode->fid.unique, dentry->d_inode->i_version); + spin_lock(&vnode->lock); + set_bit(AFS_VNODE_DELETED, &vnode->flags); + spin_unlock(&vnode->lock); + goto not_found; } + goto out_valid; + + case -ENOENT: + /* the filename is unknown */ + _debug("%s: dirent not found", dentry->d_name.name); + if (dentry->d_inode) + goto not_found; + goto out_valid; - dentry->d_fsdata = - (void *) (unsigned long) AFS_FS_I(dir)->status.version; + default: + _debug("failed to iterate dir %s: %d", + parent->d_name.name, ret); + goto out_bad; } - out_valid: +out_valid: + dentry->d_fsdata = dir_version; +out_skip: dput(parent); + key_put(key); _leave(" = 1 [valid]"); return 1; /* the dirent, if it exists, now points to a different vnode */ - not_found: +not_found: spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); - out_bad: - if (inode) { +out_bad: + if (dentry->d_inode) { /* don't unhash if we have submounts */ if (have_submounts(dentry)) - goto out_valid; + goto out_skip; } - shrink_dcache_parent(dentry); - _debug("dropping dentry %s/%s", - dentry->d_parent->d_name.name, dentry->d_name.name); + parent->d_name.name, dentry->d_name.name); + shrink_dcache_parent(dentry); d_drop(dentry); - dput(parent); + key_put(key); _leave(" = 0 [bad]"); return 0; -} /* end afs_d_revalidate() */ +} -/*****************************************************************************/ /* * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't * sleep) @@ -649,15 +698,444 @@ static int afs_d_delete(struct dentry *d if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto zap; - if (dentry->d_inode) { - if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED) + if (dentry->d_inode && + test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags)) goto zap; - } _leave(" = 0 [keep]"); return 0; - zap: +zap: _leave(" = 1 [zap]"); return 1; -} /* end afs_d_delete() */ +} + +/* + * handle dentry release + */ +static void afs_d_release(struct dentry *dentry) +{ + _enter("%s", dentry->d_name.name); +} + +/* + * create a directory on an AFS filesystem + */ +static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct afs_file_status status; + struct afs_callback cb; + struct afs_server *server; + struct afs_vnode *dvnode, *vnode; + struct afs_fid fid; + struct inode *inode; + struct key *key; + int ret; + + dvnode = AFS_FS_I(dir); + + _enter("{%x:%d},{%s},%o", + dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); + + ret = -ENAMETOOLONG; + if (dentry->d_name.len > 255) + goto error; + + key = afs_request_key(dvnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + mode |= S_IFDIR; + ret = afs_vnode_create(dvnode, key, dentry->d_name.name, + mode, &fid, &status, &cb, &server); + if (ret < 0) + goto mkdir_error; + + inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); + if (IS_ERR(inode)) { + /* ENOMEM at a really inconvenient time - just abandon the new + * directory on the server */ + ret = PTR_ERR(inode); + goto iget_error; + } + + /* apply the status report we've got for the new vnode */ + vnode = AFS_FS_I(inode); + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + afs_vnode_finalise_status_update(vnode, server); + afs_put_server(server); + + d_instantiate(dentry, inode); + if (d_unhashed(dentry)) { + _debug("not hashed"); + d_rehash(dentry); + } + key_put(key); + _leave(" = 0"); + return 0; + +iget_error: + afs_put_server(server); +mkdir_error: + key_put(key); +error: + d_drop(dentry); + _leave(" = %d", ret); + return ret; +} + +/* + * remove a directory from an AFS filesystem + */ +static int afs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct afs_vnode *dvnode, *vnode; + struct key *key; + int ret; + + dvnode = AFS_FS_I(dir); + + _enter("{%x:%d},{%s}", + dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); + + ret = -ENAMETOOLONG; + if (dentry->d_name.len > 255) + goto error; + + key = afs_request_key(dvnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, true); + if (ret < 0) + goto rmdir_error; + + if (dentry->d_inode) { + vnode = AFS_FS_I(dentry->d_inode); + clear_nlink(&vnode->vfs_inode); + set_bit(AFS_VNODE_DELETED, &vnode->flags); + afs_discard_callback_on_delete(vnode); + } + + key_put(key); + _leave(" = 0"); + return 0; + +rmdir_error: + key_put(key); +error: + _leave(" = %d", ret); + return ret; +} + +/* + * remove a file from an AFS filesystem + */ +static int afs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct afs_vnode *dvnode, *vnode; + struct key *key; + int ret; + + dvnode = AFS_FS_I(dir); + + _enter("{%x:%d},{%s}", + dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); + + ret = -ENAMETOOLONG; + if (dentry->d_name.len > 255) + goto error; + + key = afs_request_key(dvnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + if (dentry->d_inode) { + vnode = AFS_FS_I(dentry->d_inode); + + /* make sure we have a callback promise on the victim */ + ret = afs_validate(vnode, key); + if (ret < 0) + goto error; + } + + ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false); + if (ret < 0) + goto remove_error; + + if (dentry->d_inode) { + /* if the file wasn't deleted due to excess hard links, the + * fileserver will break the callback promise on the file - if + * it had one - before it returns to us, and if it was deleted, + * it won't + * + * however, if we didn't have a callback promise outstanding, + * or it was outstanding on a different server, then it won't + * break it either... + */ + vnode = AFS_FS_I(dentry->d_inode); + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) + _debug("AFS_VNODE_DELETED"); + if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) + _debug("AFS_VNODE_CB_BROKEN"); + set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); + ret = afs_validate(vnode, key); + _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); + } + + key_put(key); + _leave(" = 0"); + return 0; + +remove_error: + key_put(key); +error: + _leave(" = %d", ret); + return ret; +} + +/* + * create a regular file on an AFS filesystem + */ +static int afs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + struct afs_file_status status; + struct afs_callback cb; + struct afs_server *server; + struct afs_vnode *dvnode, *vnode; + struct afs_fid fid; + struct inode *inode; + struct key *key; + int ret; + + dvnode = AFS_FS_I(dir); + + _enter("{%x:%d},{%s},%o,", + dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); + + ret = -ENAMETOOLONG; + if (dentry->d_name.len > 255) + goto error; + + key = afs_request_key(dvnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + mode |= S_IFREG; + ret = afs_vnode_create(dvnode, key, dentry->d_name.name, + mode, &fid, &status, &cb, &server); + if (ret < 0) + goto create_error; + + inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); + if (IS_ERR(inode)) { + /* ENOMEM at a really inconvenient time - just abandon the new + * directory on the server */ + ret = PTR_ERR(inode); + goto iget_error; + } + + /* apply the status report we've got for the new vnode */ + vnode = AFS_FS_I(inode); + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + afs_vnode_finalise_status_update(vnode, server); + afs_put_server(server); + + d_instantiate(dentry, inode); + if (d_unhashed(dentry)) { + _debug("not hashed"); + d_rehash(dentry); + } + key_put(key); + _leave(" = 0"); + return 0; + +iget_error: + afs_put_server(server); +create_error: + key_put(key); +error: + d_drop(dentry); + _leave(" = %d", ret); + return ret; +} + +/* + * create a hard link between files in an AFS filesystem + */ +static int afs_link(struct dentry *from, struct inode *dir, + struct dentry *dentry) +{ + struct afs_vnode *dvnode, *vnode; + struct key *key; + int ret; + + vnode = AFS_FS_I(from->d_inode); + dvnode = AFS_FS_I(dir); + + _enter("{%x:%d},{%x:%d},{%s}", + vnode->fid.vid, vnode->fid.vnode, + dvnode->fid.vid, dvnode->fid.vnode, + dentry->d_name.name); + + ret = -ENAMETOOLONG; + if (dentry->d_name.len > 255) + goto error; + + key = afs_request_key(dvnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + ret = afs_vnode_link(dvnode, vnode, key, dentry->d_name.name); + if (ret < 0) + goto link_error; + + atomic_inc(&vnode->vfs_inode.i_count); + d_instantiate(dentry, &vnode->vfs_inode); + key_put(key); + _leave(" = 0"); + return 0; + +link_error: + key_put(key); +error: + d_drop(dentry); + _leave(" = %d", ret); + return ret; +} + +/* + * create a symlink in an AFS filesystem + */ +static int afs_symlink(struct inode *dir, struct dentry *dentry, + const char *content) +{ + struct afs_file_status status; + struct afs_server *server; + struct afs_vnode *dvnode, *vnode; + struct afs_fid fid; + struct inode *inode; + struct key *key; + int ret; + + dvnode = AFS_FS_I(dir); + + _enter("{%x:%d},{%s},%s", + dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, + content); + + ret = -ENAMETOOLONG; + if (dentry->d_name.len > 255) + goto error; + + ret = -EINVAL; + if (strlen(content) > 1023) + goto error; + + key = afs_request_key(dvnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content, + &fid, &status, &server); + if (ret < 0) + goto create_error; + + inode = afs_iget(dir->i_sb, key, &fid, &status, NULL); + if (IS_ERR(inode)) { + /* ENOMEM at a really inconvenient time - just abandon the new + * directory on the server */ + ret = PTR_ERR(inode); + goto iget_error; + } + + /* apply the status report we've got for the new vnode */ + vnode = AFS_FS_I(inode); + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + afs_vnode_finalise_status_update(vnode, server); + afs_put_server(server); + + d_instantiate(dentry, inode); + if (d_unhashed(dentry)) { + _debug("not hashed"); + d_rehash(dentry); + } + key_put(key); + _leave(" = 0"); + return 0; + +iget_error: + afs_put_server(server); +create_error: + key_put(key); +error: + d_drop(dentry); + _leave(" = %d", ret); + return ret; +} + +/* + * rename a file in an AFS filesystem and/or move it between directories + */ +static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; + struct key *key; + int ret; + + vnode = AFS_FS_I(old_dentry->d_inode); + orig_dvnode = AFS_FS_I(old_dir); + new_dvnode = AFS_FS_I(new_dir); + + _enter("{%x:%d},{%x:%d},{%x:%d},{%s}", + orig_dvnode->fid.vid, orig_dvnode->fid.vnode, + vnode->fid.vid, vnode->fid.vnode, + new_dvnode->fid.vid, new_dvnode->fid.vnode, + new_dentry->d_name.name); + + ret = -ENAMETOOLONG; + if (new_dentry->d_name.len > 255) + goto error; + + key = afs_request_key(orig_dvnode->volume->cell); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + ret = afs_vnode_rename(orig_dvnode, new_dvnode, key, + old_dentry->d_name.name, + new_dentry->d_name.name); + if (ret < 0) + goto rename_error; + key_put(key); + _leave(" = 0"); + return 0; + +rename_error: + key_put(key); +error: + d_drop(new_dentry); + _leave(" = %d", ret); + return ret; +} diff --git a/fs/afs/errors.h b/fs/afs/errors.h deleted file mode 100644 index 574d94a..0000000 --- a/fs/afs/errors.h +++ /dev/null @@ -1,34 +0,0 @@ -/* errors.h: AFS abort/error codes - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_ERRORS_H -#define _LINUX_AFS_ERRORS_H - -#include "types.h" - -/* file server abort codes */ -typedef enum { - VSALVAGE = 101, /* volume needs salvaging */ - VNOVNODE = 102, /* no such file/dir (vnode) */ - VNOVOL = 103, /* no such volume or volume unavailable */ - VVOLEXISTS = 104, /* volume name already exists */ - VNOSERVICE = 105, /* volume not currently in service */ - VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */ - VONLINE = 107, /* volume is already online */ - VDISKFULL = 108, /* disk partition is full */ - VOVERQUOTA = 109, /* volume's maximum quota exceeded */ - VBUSY = 110, /* volume is temporarily unavailable */ - VMOVED = 111, /* volume moved to new server - ask this FS where */ -} afs_rxfs_abort_t; - -extern int afs_abort_to_error(int abortcode); - -#endif /* _LINUX_AFS_ERRORS_H */ diff --git a/fs/afs/file.c b/fs/afs/file.c index b176345..ae25649 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -1,6 +1,6 @@ -/* file.c: AFS filesystem file handling +/* AFS filesystem file handling * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -15,22 +15,25 @@ #include #include #include #include -#include "volume.h" -#include "vnode.h" -#include #include "internal.h" -#if 0 -static int afs_file_open(struct inode *inode, struct file *file); -static int afs_file_release(struct inode *inode, struct file *file); -#endif - static int afs_file_readpage(struct file *file, struct page *page); static void afs_file_invalidatepage(struct page *page, unsigned long offset); static int afs_file_releasepage(struct page *page, gfp_t gfp_flags); +const struct file_operations afs_file_operations = { + .open = afs_open, + .release = afs_release, + .llseek = generic_file_llseek, + .read = do_sync_read, + .aio_read = generic_file_aio_read, + .mmap = generic_file_readonly_mmap, + .sendfile = generic_file_sendfile, +}; + const struct inode_operations afs_file_inode_operations = { .getattr = afs_inode_getattr, + .permission = afs_permission, }; const struct address_space_operations afs_fs_aops = { @@ -40,7 +43,48 @@ const struct address_space_operations af .invalidatepage = afs_file_invalidatepage, }; -/*****************************************************************************/ +/* + * open an AFS file or directory and attach a key to it + */ +int afs_open(struct inode *inode, struct file *file) +{ + struct afs_vnode *vnode = AFS_FS_I(inode); + struct key *key; + int ret; + + _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode); + + key = afs_request_key(vnode->volume->cell); + if (IS_ERR(key)) { + _leave(" = %ld [key]", PTR_ERR(key)); + return PTR_ERR(key); + } + + ret = afs_validate(vnode, key); + if (ret < 0) { + _leave(" = %d [val]", ret); + return ret; + } + + file->private_data = key; + _leave(" = 0"); + return 0; +} + +/* + * release an AFS file or directory and discard its key + */ +int afs_release(struct inode *inode, struct file *file) +{ + struct afs_vnode *vnode = AFS_FS_I(inode); + + _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode); + + key_put(file->private_data); + _leave(" = 0"); + return 0; +} + /* * deal with notification that a page was read from the cache */ @@ -58,10 +102,9 @@ static void afs_file_readpage_read_compl SetPageUptodate(page); unlock_page(page); -} /* end afs_file_readpage_read_complete() */ +} #endif -/*****************************************************************************/ /* * deal with notification that a page was written to the cache */ @@ -74,41 +117,38 @@ static void afs_file_readpage_write_comp _enter("%p,%p,%p,%d", cookie_data, page, data, error); unlock_page(page); - -} /* end afs_file_readpage_write_complete() */ +} #endif -/*****************************************************************************/ /* * AFS read page from file (or symlink) */ static int afs_file_readpage(struct file *file, struct page *page) { - struct afs_rxfs_fetch_descriptor desc; -#ifdef AFS_CACHING_SUPPORT - struct cachefs_page *pageio; -#endif struct afs_vnode *vnode; struct inode *inode; + struct key *key; + size_t len; + off_t offset; int ret; inode = page->mapping->host; - _enter("{%lu},{%lu}", inode->i_ino, page->index); + ASSERT(file != NULL); + key = file->private_data; + ASSERT(key != NULL); + + _enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index); vnode = AFS_FS_I(inode); BUG_ON(!PageLocked(page)); ret = -ESTALE; - if (vnode->flags & AFS_VNODE_DELETED) + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) goto error; #ifdef AFS_CACHING_SUPPORT - ret = cachefs_page_get_private(page, &pageio, GFP_NOIO); - if (ret < 0) - goto error; - /* is it cached? */ ret = cachefs_read_or_alloc_page(vnode->cache, page, @@ -132,26 +172,19 @@ #endif case -ENOBUFS: case -ENODATA: default: - desc.fid = vnode->fid; - desc.offset = page->index << PAGE_CACHE_SHIFT; - desc.size = min((size_t) (inode->i_size - desc.offset), - (size_t) PAGE_SIZE); - desc.buffer = kmap(page); - - clear_page(desc.buffer); + offset = page->index << PAGE_CACHE_SHIFT; + len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE); /* read the contents of the file from the server into the * page */ - ret = afs_vnode_fetch_data(vnode, &desc); - kunmap(page); + ret = afs_vnode_fetch_data(vnode, key, offset, len, page); if (ret < 0) { - if (ret==-ENOENT) { + if (ret == -ENOENT) { _debug("got NOENT from server" " - marking file deleted and stale"); - vnode->flags |= AFS_VNODE_DELETED; + set_bit(AFS_VNODE_DELETED, &vnode->flags); ret = -ESTALE; } - #ifdef AFS_CACHING_SUPPORT cachefs_uncache_page(vnode->cache, page); #endif @@ -178,16 +211,13 @@ #endif _leave(" = 0"); return 0; - error: +error: SetPageError(page); unlock_page(page); - _leave(" = %d", ret); return ret; +} -} /* end afs_file_readpage() */ - -/*****************************************************************************/ /* * get a page cookie for the specified page */ @@ -202,10 +232,9 @@ int afs_cache_get_page_cookie(struct pag _leave(" = %d", ret); return ret; -} /* end afs_cache_get_page_cookie() */ +} #endif -/*****************************************************************************/ /* * invalidate part or all of a page */ @@ -240,9 +269,8 @@ #endif } _leave(" = %d", ret); -} /* end afs_file_invalidatepage() */ +} -/*****************************************************************************/ /* * release a page and cleanup its private data */ @@ -267,4 +295,4 @@ #endif _leave(" = 0"); return 0; -} /* end afs_file_releasepage() */ +} diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 61bc371..2393d2a 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -1,6 +1,6 @@ -/* fsclient.c: AFS File Server client stubs +/* AFS File Server client stubs * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -11,827 +11,927 @@ #include #include -#include -#include -#include -#include -#include "fsclient.h" -#include "cmservice.h" -#include "vnode.h" -#include "server.h" -#include "errors.h" +#include #include "internal.h" +#include "afs_fs.h" -#define FSFETCHSTATUS 132 /* AFS Fetch file status */ -#define FSFETCHDATA 130 /* AFS Fetch file data */ -#define FSGIVEUPCALLBACKS 147 /* AFS Discard callback promises */ -#define FSGETVOLUMEINFO 148 /* AFS Get root volume information */ -#define FSGETROOTVOLUME 151 /* AFS Get root volume name */ -#define FSLOOKUP 161 /* AFS lookup file in directory */ - -/*****************************************************************************/ /* - * map afs abort codes to/from Linux error codes - * - called with call->lock held + * decode an AFSFid block */ -static void afs_rxfs_aemap(struct rxrpc_call *call) +static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid) { - switch (call->app_err_state) { - case RXRPC_ESTATE_LOCAL_ABORT: - call->app_abort_code = -call->app_errno; - break; - case RXRPC_ESTATE_PEER_ABORT: - call->app_errno = afs_abort_to_error(call->app_abort_code); - break; - default: - break; - } -} /* end afs_rxfs_aemap() */ + const __be32 *bp = *_bp; + + fid->vid = ntohl(*bp++); + fid->vnode = ntohl(*bp++); + fid->unique = ntohl(*bp++); + *_bp = bp; +} -/*****************************************************************************/ /* - * get the root volume name from a fileserver - * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2 + * decode an AFSFetchStatus block */ -#if 0 -int afs_rxfs_get_root_volume(struct afs_server *server, - char *buf, size_t *buflen) +static void xdr_decode_AFSFetchStatus(const __be32 **_bp, + struct afs_file_status *status, + struct afs_vnode *vnode) { - struct rxrpc_connection *conn; - struct rxrpc_call *call; - struct kvec piov[2]; - size_t sent; - int ret; - u32 param[1]; + const __be32 *bp = *_bp; + umode_t mode; + u64 data_version, size; + u32 changed = 0; /* becomes non-zero if ctime-type changes seen */ + +#define EXTRACT(DST) \ + do { \ + u32 x = ntohl(*bp++); \ + changed |= DST - x; \ + DST = x; \ + } while (0) + + status->if_version = ntohl(*bp++); + EXTRACT(status->type); + EXTRACT(status->nlink); + size = ntohl(*bp++); + data_version = ntohl(*bp++); + EXTRACT(status->author); + EXTRACT(status->owner); + EXTRACT(status->caller_access); /* call ticket dependent */ + EXTRACT(status->anon_access); + EXTRACT(status->mode); + EXTRACT(status->parent.vnode); + EXTRACT(status->parent.unique); + bp++; /* seg size */ + status->mtime_client = ntohl(*bp++); + status->mtime_server = ntohl(*bp++); + EXTRACT(status->group); + bp++; /* sync counter */ + data_version |= (u64) ntohl(*bp++) << 32; + bp++; /* lock count */ + size |= (u64) ntohl(*bp++) << 32; + bp++; /* spare 4 */ + *_bp = bp; + + if (size != status->size) { + status->size = size; + changed |= true; + } + status->mode &= S_IALLUGO; + + _debug("vnode time %lx, %lx", + status->mtime_client, status->mtime_server); + + if (vnode) { + status->parent.vid = vnode->fid.vid; + if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { + _debug("vnode changed"); + i_size_write(&vnode->vfs_inode, size); + vnode->vfs_inode.i_uid = status->owner; + vnode->vfs_inode.i_gid = status->group; + vnode->vfs_inode.i_version = vnode->fid.unique; + vnode->vfs_inode.i_nlink = status->nlink; + + mode = vnode->vfs_inode.i_mode; + mode &= ~S_IALLUGO; + mode |= status->mode; + barrier(); + vnode->vfs_inode.i_mode = mode; + } - DECLARE_WAITQUEUE(myself, current); + vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server; + vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; + vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; + } - kenter("%p,%p,%u",server, buf, *buflen); + if (status->data_version != data_version) { + status->data_version = data_version; + if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { + _debug("vnode modified %llx on {%x:%u}", + (unsigned long long) data_version, + vnode->fid.vid, vnode->fid.vnode); + set_bit(AFS_VNODE_MODIFIED, &vnode->flags); + set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); + } + } +} - /* get hold of the fileserver connection */ - ret = afs_server_get_fsconn(server, &conn); - if (ret < 0) - goto out; +/* + * decode an AFSCallBack block + */ +static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode) +{ + const __be32 *bp = *_bp; - /* create a call through that connection */ - ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; - } - call->app_opcode = FSGETROOTVOLUME; + vnode->cb_version = ntohl(*bp++); + vnode->cb_expiry = ntohl(*bp++); + vnode->cb_type = ntohl(*bp++); + vnode->cb_expires = vnode->cb_expiry + get_seconds(); + *_bp = bp; +} - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); +static void xdr_decode_AFSCallBack_raw(const __be32 **_bp, + struct afs_callback *cb) +{ + const __be32 *bp = *_bp; - /* marshall the parameters */ - param[0] = htonl(FSGETROOTVOLUME); - - piov[0].iov_len = sizeof(param); - piov[0].iov_base = param; - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY || - signal_pending(current)) - break; - schedule(); - } - set_current_state(TASK_RUNNING); + cb->version = ntohl(*bp++); + cb->expiry = ntohl(*bp++); + cb->type = ntohl(*bp++); + *_bp = bp; +} - ret = -EINTR; - if (signal_pending(current)) - goto abort; +/* + * decode an AFSVolSync block + */ +static void xdr_decode_AFSVolSync(const __be32 **_bp, + struct afs_volsync *volsync) +{ + const __be32 *bp = *_bp; - switch (call->app_call_state) { - case RXRPC_CSTATE_ERROR: - ret = call->app_errno; - kdebug("Got Error: %d", ret); - goto out_unwait; + volsync->creation = ntohl(*bp++); + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + bp++; /* spare5 */ + bp++; /* spare6 */ + *_bp = bp; +} - case RXRPC_CSTATE_CLNT_GOT_REPLY: - /* read the reply */ - kdebug("Got Reply: qty=%d", call->app_ready_qty); +/* + * deliver reply data to an FS.FetchStatus + */ +static int afs_deliver_fs_fetch_status(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + struct afs_vnode *vnode = call->reply; + const __be32 *bp; - ret = -EBADMSG; - if (call->app_ready_qty <= 4) - goto abort; + _enter(",,%u", last); - ret = rxrpc_call_read_data(call, NULL, call->app_ready_qty, 0); - if (ret < 0) - goto abort; + afs_transfer_reply(call, skb); + if (!last) + return 0; -#if 0 - /* unmarshall the reply */ - bp = buffer; - for (loop = 0; loop < 65; loop++) - entry->name[loop] = ntohl(*bp++); - entry->name[64] = 0; + if (call->reply_size != call->reply_max) + return -EBADMSG; - entry->type = ntohl(*bp++); - entry->num_servers = ntohl(*bp++); + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSCallBack(&bp, vnode); + if (call->reply2) + xdr_decode_AFSVolSync(&bp, call->reply2); - for (loop = 0; loop < 8; loop++) - entry->servers[loop].addr.s_addr = *bp++; + _leave(" = 0 [done]"); + return 0; +} - for (loop = 0; loop < 8; loop++) - entry->servers[loop].partition = ntohl(*bp++); +/* + * FS.FetchStatus operation type + */ +static const struct afs_call_type afs_RXFSFetchStatus = { + .name = "FS.FetchStatus", + .deliver = afs_deliver_fs_fetch_status, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; - for (loop = 0; loop < 8; loop++) - entry->servers[loop].flags = ntohl(*bp++); +/* + * fetch the status information for a file + */ +int afs_fs_fetch_file_status(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + struct afs_volsync *volsync, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + __be32 *bp; - for (loop = 0; loop < 3; loop++) - entry->volume_ids[loop] = ntohl(*bp++); + _enter(",%x,{%x:%d},,", + key_serial(key), vnode->fid.vid, vnode->fid.vnode); - entry->clone_id = ntohl(*bp++); - entry->flags = ntohl(*bp); -#endif + call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4); + if (!call) + return -ENOMEM; - /* success */ - ret = 0; - goto out_unwait; + call->key = key; + call->reply = vnode; + call->reply2 = volsync; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); - default: - BUG(); - } + /* marshall the parameters */ + bp = call->request; + bp[0] = htonl(FSFETCHSTATUS); + bp[1] = htonl(vnode->fid.vid); + bp[2] = htonl(vnode->fid.vnode); + bp[3] = htonl(vnode->fid.unique); + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - afs_server_release_fsconn(server, conn); - out: - kleave(""); - return ret; -} /* end afs_rxfs_get_root_volume() */ -#endif - -/*****************************************************************************/ /* - * get information about a volume + * deliver reply data to an FS.FetchData */ -#if 0 -int afs_rxfs_get_volume_info(struct afs_server *server, - const char *name, - struct afs_volume_info *vinfo) +static int afs_deliver_fs_fetch_data(struct afs_call *call, + struct sk_buff *skb, bool last) { - struct rxrpc_connection *conn; - struct rxrpc_call *call; - struct kvec piov[3]; - size_t sent; + struct afs_vnode *vnode = call->reply; + const __be32 *bp; + struct page *page; + void *buffer; int ret; - u32 param[2], *bp, zero; - DECLARE_WAITQUEUE(myself, current); + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); + + switch (call->unmarshall) { + case 0: + call->offset = 0; + call->unmarshall++; + + /* extract the returned data length */ + case 1: + _debug("extract data length"); + ret = afs_extract_data(call, skb, last, &call->tmp, 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - _enter("%p,%s,%p", server, name, vinfo); + call->count = ntohl(call->tmp); + _debug("DATA length: %u", call->count); + if (call->count > PAGE_SIZE) + return -EBADMSG; + call->offset = 0; + call->unmarshall++; + + if (call->count < PAGE_SIZE) { + buffer = kmap_atomic(call->reply3, KM_USER0); + memset(buffer + PAGE_SIZE - call->count, 0, + call->count); + kunmap_atomic(buffer, KM_USER0); + } - /* get hold of the fileserver connection */ - ret = afs_server_get_fsconn(server, &conn); - if (ret < 0) - goto out; + /* extract the returned data */ + case 2: + _debug("extract data"); + page = call->reply3; + buffer = kmap_atomic(page, KM_USER0); + ret = afs_extract_data(call, skb, last, buffer, call->count); + kunmap_atomic(buffer, KM_USER0); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - /* create a call through that connection */ - ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; - } - call->app_opcode = FSGETVOLUMEINFO; + call->offset = 0; + call->unmarshall++; + + /* extract the metadata */ + case 3: + ret = afs_extract_data(call, skb, last, call->buffer, + (21 + 3 + 6) * 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); + bp = call->buffer; + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSCallBack(&bp, vnode); + if (call->reply2) + xdr_decode_AFSVolSync(&bp, call->reply2); - /* marshall the parameters */ - piov[1].iov_len = strlen(name); - piov[1].iov_base = (char *) name; - - zero = 0; - piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; - piov[2].iov_base = &zero; - - param[0] = htonl(FSGETVOLUMEINFO); - param[1] = htonl(piov[1].iov_len); - - piov[0].iov_len = sizeof(param); - piov[0].iov_base = param; - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - bp = rxrpc_call_alloc_scratch(call, 64); - - ret = rxrpc_call_read_data(call, bp, 64, - RXRPC_CALL_READ_BLOCK | - RXRPC_CALL_READ_ALL); - if (ret < 0) { - if (ret == -ECONNABORTED) { - ret = call->app_errno; - goto out_unwait; - } - goto abort; + call->offset = 0; + call->unmarshall++; + + case 4: + _debug("trailer"); + if (skb->len != 0) + return -EBADMSG; + break; } - /* unmarshall the reply */ - vinfo->vid = ntohl(*bp++); - vinfo->type = ntohl(*bp++); - - vinfo->type_vids[0] = ntohl(*bp++); - vinfo->type_vids[1] = ntohl(*bp++); - vinfo->type_vids[2] = ntohl(*bp++); - vinfo->type_vids[3] = ntohl(*bp++); - vinfo->type_vids[4] = ntohl(*bp++); - - vinfo->nservers = ntohl(*bp++); - vinfo->servers[0].addr.s_addr = *bp++; - vinfo->servers[1].addr.s_addr = *bp++; - vinfo->servers[2].addr.s_addr = *bp++; - vinfo->servers[3].addr.s_addr = *bp++; - vinfo->servers[4].addr.s_addr = *bp++; - vinfo->servers[5].addr.s_addr = *bp++; - vinfo->servers[6].addr.s_addr = *bp++; - vinfo->servers[7].addr.s_addr = *bp++; - - ret = -EBADMSG; - if (vinfo->nservers > 8) - goto abort; - - /* success */ - ret = 0; - - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - afs_server_release_fsconn(server, conn); - out: - _leave(""); - return ret; - - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - goto out_unwait; - -} /* end afs_rxfs_get_volume_info() */ -#endif - -/*****************************************************************************/ + if (!last) + return 0; + + _leave(" = 0 [done]"); + return 0; +} + /* - * fetch the status information for a file + * FS.FetchData operation type + */ +static const struct afs_call_type afs_RXFSFetchData = { + .name = "FS.FetchData", + .deliver = afs_deliver_fs_fetch_data, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * fetch data from a file */ -int afs_rxfs_fetch_file_status(struct afs_server *server, - struct afs_vnode *vnode, - struct afs_volsync *volsync) +int afs_fs_fetch_data(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + off_t offset, size_t length, + struct page *buffer, + const struct afs_wait_mode *wait_mode) { - struct afs_server_callslot callslot; - struct rxrpc_call *call; - struct kvec piov[1]; - size_t sent; - int ret; + struct afs_call *call; __be32 *bp; - DECLARE_WAITQUEUE(myself, current); + _enter(""); - _enter("%p,{%u,%u,%u}", - server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); + call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4); + if (!call) + return -ENOMEM; - /* get hold of the fileserver connection */ - ret = afs_server_request_callslot(server, &callslot); - if (ret < 0) - goto out; - - /* create a call through that connection */ - ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, - &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; - } - call->app_opcode = FSFETCHSTATUS; - - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); + call->key = key; + call->reply = vnode; + call->reply2 = NULL; /* volsync */ + call->reply3 = buffer; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); /* marshall the parameters */ - bp = rxrpc_call_alloc_scratch(call, 16); - bp[0] = htonl(FSFETCHSTATUS); + bp = call->request; + bp[0] = htonl(FSFETCHDATA); bp[1] = htonl(vnode->fid.vid); bp[2] = htonl(vnode->fid.vnode); bp[3] = htonl(vnode->fid.unique); + bp[4] = htonl(offset); + bp[5] = htonl(length); - piov[0].iov_len = 16; - piov[0].iov_base = bp; - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - bp = rxrpc_call_alloc_scratch(call, 120); - - ret = rxrpc_call_read_data(call, bp, 120, - RXRPC_CALL_READ_BLOCK | - RXRPC_CALL_READ_ALL); - if (ret < 0) { - if (ret == -ECONNABORTED) { - ret = call->app_errno; - goto out_unwait; - } - goto abort; - } + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} - /* unmarshall the reply */ - vnode->status.if_version = ntohl(*bp++); - vnode->status.type = ntohl(*bp++); - vnode->status.nlink = ntohl(*bp++); - vnode->status.size = ntohl(*bp++); - vnode->status.version = ntohl(*bp++); - vnode->status.author = ntohl(*bp++); - vnode->status.owner = ntohl(*bp++); - vnode->status.caller_access = ntohl(*bp++); - vnode->status.anon_access = ntohl(*bp++); - vnode->status.mode = ntohl(*bp++); - vnode->status.parent.vid = vnode->fid.vid; - vnode->status.parent.vnode = ntohl(*bp++); - vnode->status.parent.unique = ntohl(*bp++); - bp++; /* seg size */ - vnode->status.mtime_client = ntohl(*bp++); - vnode->status.mtime_server = ntohl(*bp++); - bp++; /* group */ - bp++; /* sync counter */ - vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; - bp++; /* spare2 */ - bp++; /* spare3 */ - bp++; /* spare4 */ +/* + * deliver reply data to an FS.GiveUpCallBacks + */ +static int afs_deliver_fs_give_up_callbacks(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + _enter(",{%u},%d", skb->len, last); - vnode->cb_version = ntohl(*bp++); - vnode->cb_expiry = ntohl(*bp++); - vnode->cb_type = ntohl(*bp++); - - if (volsync) { - volsync->creation = ntohl(*bp++); - bp++; /* spare2 */ - bp++; /* spare3 */ - bp++; /* spare4 */ - bp++; /* spare5 */ - bp++; /* spare6 */ - } + if (skb->len > 0) + return -EBADMSG; /* shouldn't be any reply data */ + return 0; +} - /* success */ - ret = 0; - - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - afs_server_release_callslot(server, &callslot); - out: - _leave(""); - return ret; - - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - goto out_unwait; -} /* end afs_rxfs_fetch_file_status() */ - -/*****************************************************************************/ /* - * fetch the contents of a file or directory + * FS.GiveUpCallBacks operation type */ -int afs_rxfs_fetch_file_data(struct afs_server *server, - struct afs_vnode *vnode, - struct afs_rxfs_fetch_descriptor *desc, - struct afs_volsync *volsync) +static const struct afs_call_type afs_RXFSGiveUpCallBacks = { + .name = "FS.GiveUpCallBacks", + .deliver = afs_deliver_fs_give_up_callbacks, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * give up a set of callbacks + * - the callbacks are held in the server->cb_break ring + */ +int afs_fs_give_up_callbacks(struct afs_server *server, + const struct afs_wait_mode *wait_mode) { - struct afs_server_callslot callslot; - struct rxrpc_call *call; - struct kvec piov[1]; - size_t sent; - int ret; - __be32 *bp; + struct afs_call *call; + size_t ncallbacks; + __be32 *bp, *tp; + int loop; - DECLARE_WAITQUEUE(myself, current); - - _enter("%p,{fid={%u,%u,%u},sz=%Zu,of=%lu}", - server, - desc->fid.vid, - desc->fid.vnode, - desc->fid.unique, - desc->size, - desc->offset); - - /* get hold of the fileserver connection */ - ret = afs_server_request_callslot(server, &callslot); - if (ret < 0) - goto out; - - /* create a call through that connection */ - ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; - } - call->app_opcode = FSFETCHDATA; + ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail, + ARRAY_SIZE(server->cb_break)); + + _enter("{%zu},", ncallbacks); + + if (ncallbacks == 0) + return 0; + if (ncallbacks > AFSCBMAX) + ncallbacks = AFSCBMAX; + + _debug("break %zu callbacks", ncallbacks); - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); + call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks, + 12 + ncallbacks * 6 * 4, 0); + if (!call) + return -ENOMEM; + + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); /* marshall the parameters */ - bp = rxrpc_call_alloc_scratch(call, 24); - bp[0] = htonl(FSFETCHDATA); - bp[1] = htonl(desc->fid.vid); - bp[2] = htonl(desc->fid.vnode); - bp[3] = htonl(desc->fid.unique); - bp[4] = htonl(desc->offset); - bp[5] = htonl(desc->size); - - piov[0].iov_len = 24; - piov[0].iov_base = bp; - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the data count to arrive */ - ret = rxrpc_call_read_data(call, bp, 4, RXRPC_CALL_READ_BLOCK); - if (ret < 0) - goto read_failed; - - desc->actual = ntohl(bp[0]); - if (desc->actual != desc->size) { - ret = -EBADMSG; - goto abort; + bp = call->request; + tp = bp + 2 + ncallbacks * 3; + *bp++ = htonl(FSGIVEUPCALLBACKS); + *bp++ = htonl(ncallbacks); + *tp++ = htonl(ncallbacks); + + atomic_sub(ncallbacks, &server->cb_break_n); + for (loop = ncallbacks; loop > 0; loop--) { + struct afs_callback *cb = + &server->cb_break[server->cb_break_tail]; + + *bp++ = htonl(cb->fid.vid); + *bp++ = htonl(cb->fid.vnode); + *bp++ = htonl(cb->fid.unique); + *tp++ = htonl(cb->version); + *tp++ = htonl(cb->expiry); + *tp++ = htonl(cb->type); + smp_mb(); + server->cb_break_tail = + (server->cb_break_tail + 1) & + (ARRAY_SIZE(server->cb_break) - 1); } - /* call the app to read the actual data */ - rxrpc_call_reset_scratch(call); - - ret = rxrpc_call_read_data(call, desc->buffer, desc->actual, - RXRPC_CALL_READ_BLOCK); - if (ret < 0) - goto read_failed; - - /* wait for the rest of the reply to completely arrive */ - rxrpc_call_reset_scratch(call); - bp = rxrpc_call_alloc_scratch(call, 120); - - ret = rxrpc_call_read_data(call, bp, 120, - RXRPC_CALL_READ_BLOCK | - RXRPC_CALL_READ_ALL); - if (ret < 0) - goto read_failed; - - /* unmarshall the reply */ - vnode->status.if_version = ntohl(*bp++); - vnode->status.type = ntohl(*bp++); - vnode->status.nlink = ntohl(*bp++); - vnode->status.size = ntohl(*bp++); - vnode->status.version = ntohl(*bp++); - vnode->status.author = ntohl(*bp++); - vnode->status.owner = ntohl(*bp++); - vnode->status.caller_access = ntohl(*bp++); - vnode->status.anon_access = ntohl(*bp++); - vnode->status.mode = ntohl(*bp++); - vnode->status.parent.vid = desc->fid.vid; - vnode->status.parent.vnode = ntohl(*bp++); - vnode->status.parent.unique = ntohl(*bp++); - bp++; /* seg size */ - vnode->status.mtime_client = ntohl(*bp++); - vnode->status.mtime_server = ntohl(*bp++); - bp++; /* group */ - bp++; /* sync counter */ - vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; - bp++; /* spare2 */ - bp++; /* spare3 */ - bp++; /* spare4 */ + ASSERT(ncallbacks > 0); + wake_up_nr(&server->cb_break_waitq, ncallbacks); - vnode->cb_version = ntohl(*bp++); - vnode->cb_expiry = ntohl(*bp++); - vnode->cb_type = ntohl(*bp++); - - if (volsync) { - volsync->creation = ntohl(*bp++); - bp++; /* spare2 */ - bp++; /* spare3 */ - bp++; /* spare4 */ - bp++; /* spare5 */ - bp++; /* spare6 */ - } + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} - /* success */ - ret = 0; - - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq,&myself); - rxrpc_put_call(call); - out_put_conn: - afs_server_release_callslot(server, &callslot); - out: - _leave(" = %d", ret); - return ret; - - read_failed: - if (ret == -ECONNABORTED) { - ret = call->app_errno; - goto out_unwait; - } +/* + * deliver reply data to an FS.CreateFile or an FS.MakeDir + */ +static int afs_deliver_fs_create_vnode(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + struct afs_vnode *vnode = call->reply; + const __be32 *bp; + + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - goto out_unwait; + afs_transfer_reply(call, skb); + if (!last) + return 0; -} /* end afs_rxfs_fetch_file_data() */ + if (call->reply_size != call->reply_max) + return -EBADMSG; + + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + xdr_decode_AFSFid(&bp, call->reply2); + xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSCallBack_raw(&bp, call->reply4); + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.CreateFile and FS.MakeDir operation type + */ +static const struct afs_call_type afs_RXFSCreateXXXX = { + .name = "FS.CreateXXXX", + .deliver = afs_deliver_fs_create_vnode, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; -/*****************************************************************************/ /* - * ask the AFS fileserver to discard a callback request on a file + * create a file or make a directory */ -int afs_rxfs_give_up_callback(struct afs_server *server, - struct afs_vnode *vnode) +int afs_fs_create(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + const char *name, + umode_t mode, + struct afs_fid *newfid, + struct afs_file_status *newstatus, + struct afs_callback *newcb, + const struct afs_wait_mode *wait_mode) { - struct afs_server_callslot callslot; - struct rxrpc_call *call; - struct kvec piov[1]; - size_t sent; - int ret; + struct afs_call *call; + size_t namesz, reqsz, padsz; __be32 *bp; - DECLARE_WAITQUEUE(myself, current); + _enter(""); - _enter("%p,{%u,%u,%u}", - server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); + namesz = strlen(name); + padsz = (4 - (namesz & 3)) & 3; + reqsz = (5 * 4) + namesz + padsz + (6 * 4); - /* get hold of the fileserver connection */ - ret = afs_server_request_callslot(server, &callslot); - if (ret < 0) - goto out; + call = afs_alloc_flat_call(&afs_RXFSCreateXXXX, reqsz, + (3 + 21 + 21 + 3 + 6) * 4); + if (!call) + return -ENOMEM; - /* create a call through that connection */ - ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; + call->key = key; + call->reply = vnode; + call->reply2 = newfid; + call->reply3 = newstatus; + call->reply4 = newcb; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(namesz); + memcpy(bp, name, namesz); + bp = (void *) bp + namesz; + if (padsz > 0) { + memset(bp, 0, padsz); + bp = (void *) bp + padsz; } - call->app_opcode = FSGIVEUPCALLBACKS; + *bp++ = htonl(AFS_SET_MODE); + *bp++ = 0; /* mtime */ + *bp++ = 0; /* owner */ + *bp++ = 0; /* group */ + *bp++ = htonl(mode & S_IALLUGO); /* unix mode */ + *bp++ = 0; /* segment size */ - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} - /* marshall the parameters */ - bp = rxrpc_call_alloc_scratch(call, (1 + 4 + 4) * 4); +/* + * deliver reply data to an FS.RemoveFile or FS.RemoveDir + */ +static int afs_deliver_fs_remove(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + struct afs_vnode *vnode = call->reply; + const __be32 *bp; - piov[0].iov_len = (1 + 4 + 4) * 4; - piov[0].iov_base = bp; + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); - *bp++ = htonl(FSGIVEUPCALLBACKS); - *bp++ = htonl(1); + afs_transfer_reply(call, skb); + if (!last) + return 0; + + if (call->reply_size != call->reply_max) + return -EBADMSG; + + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.RemoveDir/FS.RemoveFile operation type + */ +static const struct afs_call_type afs_RXFSRemoveXXXX = { + .name = "FS.RemoveXXXX", + .deliver = afs_deliver_fs_remove, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * remove a file or directory + */ +int afs_fs_remove(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + const char *name, + bool isdir, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + size_t namesz, reqsz, padsz; + __be32 *bp; + + _enter(""); + + namesz = strlen(name); + padsz = (4 - (namesz & 3)) & 3; + reqsz = (5 * 4) + namesz + padsz; + + call = afs_alloc_flat_call(&afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE); *bp++ = htonl(vnode->fid.vid); *bp++ = htonl(vnode->fid.vnode); *bp++ = htonl(vnode->fid.unique); - *bp++ = htonl(1); - *bp++ = htonl(vnode->cb_version); - *bp++ = htonl(vnode->cb_expiry); - *bp++ = htonl(vnode->cb_type); - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY || - signal_pending(current)) - break; - schedule(); + *bp++ = htonl(namesz); + memcpy(bp, name, namesz); + bp = (void *) bp + namesz; + if (padsz > 0) { + memset(bp, 0, padsz); + bp = (void *) bp + padsz; } - set_current_state(TASK_RUNNING); - ret = -EINTR; - if (signal_pending(current)) - goto abort; + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} - switch (call->app_call_state) { - case RXRPC_CSTATE_ERROR: - ret = call->app_errno; - goto out_unwait; +/* + * deliver reply data to an FS.Link + */ +static int afs_deliver_fs_link(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + struct afs_vnode *dvnode = call->reply, *vnode = call->reply2; + const __be32 *bp; - case RXRPC_CSTATE_CLNT_GOT_REPLY: - ret = 0; - goto out_unwait; + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); - default: - BUG(); - } + afs_transfer_reply(call, skb); + if (!last) + return 0; + + if (call->reply_size != call->reply_max) + return -EBADMSG; + + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode); + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.Link operation type + */ +static const struct afs_call_type afs_RXFSLink = { + .name = "FS.Link", + .deliver = afs_deliver_fs_link, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - afs_server_release_callslot(server, &callslot); - out: - _leave(""); - return ret; - - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - goto out_unwait; -} /* end afs_rxfs_give_up_callback() */ - -/*****************************************************************************/ /* - * look a filename up in a directory - * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2 + * make a hard link */ -#if 0 -int afs_rxfs_lookup(struct afs_server *server, - struct afs_vnode *dir, - const char *filename, - struct afs_vnode *vnode, - struct afs_volsync *volsync) +int afs_fs_link(struct afs_server *server, + struct key *key, + struct afs_vnode *dvnode, + struct afs_vnode *vnode, + const char *name, + const struct afs_wait_mode *wait_mode) { - struct rxrpc_connection *conn; - struct rxrpc_call *call; - struct kvec piov[3]; - size_t sent; - int ret; - u32 *bp, zero; + struct afs_call *call; + size_t namesz, reqsz, padsz; + __be32 *bp; - DECLARE_WAITQUEUE(myself, current); + _enter(""); - kenter("%p,{%u,%u,%u},%s", - server, fid->vid, fid->vnode, fid->unique, filename); + namesz = strlen(name); + padsz = (4 - (namesz & 3)) & 3; + reqsz = (5 * 4) + namesz + padsz + (3 * 4); - /* get hold of the fileserver connection */ - ret = afs_server_get_fsconn(server, &conn); - if (ret < 0) - goto out; + call = afs_alloc_flat_call(&afs_RXFSLink, reqsz, (21 + 21 + 6) * 4); + if (!call) + return -ENOMEM; - /* create a call through that connection */ - ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; + call->key = key; + call->reply = dvnode; + call->reply2 = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSLINK); + *bp++ = htonl(dvnode->fid.vid); + *bp++ = htonl(dvnode->fid.vnode); + *bp++ = htonl(dvnode->fid.unique); + *bp++ = htonl(namesz); + memcpy(bp, name, namesz); + bp = (void *) bp + namesz; + if (padsz > 0) { + memset(bp, 0, padsz); + bp = (void *) bp + padsz; } - call->app_opcode = FSLOOKUP; + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} + +/* + * deliver reply data to an FS.Symlink + */ +static int afs_deliver_fs_symlink(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + struct afs_vnode *vnode = call->reply; + const __be32 *bp; - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq,&myself); + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); + + afs_transfer_reply(call, skb); + if (!last) + return 0; + + if (call->reply_size != call->reply_max) + return -EBADMSG; + + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + xdr_decode_AFSFid(&bp, call->reply2); + xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL); + xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.Symlink operation type + */ +static const struct afs_call_type afs_RXFSSymlink = { + .name = "FS.Symlink", + .deliver = afs_deliver_fs_symlink, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * create a symbolic link + */ +int afs_fs_symlink(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + const char *name, + const char *contents, + struct afs_fid *newfid, + struct afs_file_status *newstatus, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + size_t namesz, reqsz, padsz, c_namesz, c_padsz; + __be32 *bp; + + _enter(""); + + namesz = strlen(name); + padsz = (4 - (namesz & 3)) & 3; + + c_namesz = strlen(contents); + c_padsz = (4 - (c_namesz & 3)) & 3; + + reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4); + + call = afs_alloc_flat_call(&afs_RXFSSymlink, reqsz, + (3 + 21 + 21 + 6) * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = vnode; + call->reply2 = newfid; + call->reply3 = newstatus; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); /* marshall the parameters */ - bp = rxrpc_call_alloc_scratch(call, 20); - - zero = 0; - - piov[0].iov_len = 20; - piov[0].iov_base = bp; - piov[1].iov_len = strlen(filename); - piov[1].iov_base = (char *) filename; - piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; - piov[2].iov_base = &zero; - - *bp++ = htonl(FSLOOKUP); - *bp++ = htonl(dirfid->vid); - *bp++ = htonl(dirfid->vnode); - *bp++ = htonl(dirfid->unique); - *bp++ = htonl(piov[1].iov_len); - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - bp = rxrpc_call_alloc_scratch(call, 220); - - ret = rxrpc_call_read_data(call, bp, 220, - RXRPC_CALL_READ_BLOCK | - RXRPC_CALL_READ_ALL); - if (ret < 0) { - if (ret == -ECONNABORTED) { - ret = call->app_errno; - goto out_unwait; - } - goto abort; + bp = call->request; + *bp++ = htonl(FSSYMLINK); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(namesz); + memcpy(bp, name, namesz); + bp = (void *) bp + namesz; + if (padsz > 0) { + memset(bp, 0, padsz); + bp = (void *) bp + padsz; } + *bp++ = htonl(c_namesz); + memcpy(bp, contents, c_namesz); + bp = (void *) bp + c_namesz; + if (c_padsz > 0) { + memset(bp, 0, c_padsz); + bp = (void *) bp + c_padsz; + } + *bp++ = htonl(AFS_SET_MODE); + *bp++ = 0; /* mtime */ + *bp++ = 0; /* owner */ + *bp++ = 0; /* group */ + *bp++ = htonl(S_IRWXUGO); /* unix mode */ + *bp++ = 0; /* segment size */ - /* unmarshall the reply */ - fid->vid = ntohl(*bp++); - fid->vnode = ntohl(*bp++); - fid->unique = ntohl(*bp++); + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} - vnode->status.if_version = ntohl(*bp++); - vnode->status.type = ntohl(*bp++); - vnode->status.nlink = ntohl(*bp++); - vnode->status.size = ntohl(*bp++); - vnode->status.version = ntohl(*bp++); - vnode->status.author = ntohl(*bp++); - vnode->status.owner = ntohl(*bp++); - vnode->status.caller_access = ntohl(*bp++); - vnode->status.anon_access = ntohl(*bp++); - vnode->status.mode = ntohl(*bp++); - vnode->status.parent.vid = dirfid->vid; - vnode->status.parent.vnode = ntohl(*bp++); - vnode->status.parent.unique = ntohl(*bp++); - bp++; /* seg size */ - vnode->status.mtime_client = ntohl(*bp++); - vnode->status.mtime_server = ntohl(*bp++); - bp++; /* group */ - bp++; /* sync counter */ - vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; - bp++; /* spare2 */ - bp++; /* spare3 */ - bp++; /* spare4 */ +/* + * deliver reply data to an FS.Rename + */ +static int afs_deliver_fs_rename(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2; + const __be32 *bp; - dir->status.if_version = ntohl(*bp++); - dir->status.type = ntohl(*bp++); - dir->status.nlink = ntohl(*bp++); - dir->status.size = ntohl(*bp++); - dir->status.version = ntohl(*bp++); - dir->status.author = ntohl(*bp++); - dir->status.owner = ntohl(*bp++); - dir->status.caller_access = ntohl(*bp++); - dir->status.anon_access = ntohl(*bp++); - dir->status.mode = ntohl(*bp++); - dir->status.parent.vid = dirfid->vid; - dir->status.parent.vnode = ntohl(*bp++); - dir->status.parent.unique = ntohl(*bp++); - bp++; /* seg size */ - dir->status.mtime_client = ntohl(*bp++); - dir->status.mtime_server = ntohl(*bp++); - bp++; /* group */ - bp++; /* sync counter */ - dir->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; - bp++; /* spare2 */ - bp++; /* spare3 */ - bp++; /* spare4 */ + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); + + afs_transfer_reply(call, skb); + if (!last) + return 0; + + if (call->reply_size != call->reply_max) + return -EBADMSG; + + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode); + if (new_dvnode != orig_dvnode) + xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode); + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.Rename operation type + */ +static const struct afs_call_type afs_RXFSRename = { + .name = "FS.Rename", + .deliver = afs_deliver_fs_rename, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * create a symbolic link + */ +int afs_fs_rename(struct afs_server *server, + struct key *key, + struct afs_vnode *orig_dvnode, + const char *orig_name, + struct afs_vnode *new_dvnode, + const char *new_name, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz; + __be32 *bp; + + _enter(""); + + o_namesz = strlen(orig_name); + o_padsz = (4 - (o_namesz & 3)) & 3; + + n_namesz = strlen(new_name); + n_padsz = (4 - (n_namesz & 3)) & 3; + + reqsz = (4 * 4) + + 4 + o_namesz + o_padsz + + (3 * 4) + + 4 + n_namesz + n_padsz; + + call = afs_alloc_flat_call(&afs_RXFSRename, reqsz, (21 + 21 + 6) * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = orig_dvnode; + call->reply2 = new_dvnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSRENAME); + *bp++ = htonl(orig_dvnode->fid.vid); + *bp++ = htonl(orig_dvnode->fid.vnode); + *bp++ = htonl(orig_dvnode->fid.unique); + *bp++ = htonl(o_namesz); + memcpy(bp, orig_name, o_namesz); + bp = (void *) bp + o_namesz; + if (o_padsz > 0) { + memset(bp, 0, o_padsz); + bp = (void *) bp + o_padsz; + } - callback->fid = *fid; - callback->version = ntohl(*bp++); - callback->expiry = ntohl(*bp++); - callback->type = ntohl(*bp++); - - if (volsync) { - volsync->creation = ntohl(*bp++); - bp++; /* spare2 */ - bp++; /* spare3 */ - bp++; /* spare4 */ - bp++; /* spare5 */ - bp++; /* spare6 */ + *bp++ = htonl(new_dvnode->fid.vid); + *bp++ = htonl(new_dvnode->fid.vnode); + *bp++ = htonl(new_dvnode->fid.unique); + *bp++ = htonl(n_namesz); + memcpy(bp, new_name, n_namesz); + bp = (void *) bp + n_namesz; + if (n_padsz > 0) { + memset(bp, 0, n_padsz); + bp = (void *) bp + n_padsz; } - /* success */ - ret = 0; - - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - afs_server_release_fsconn(server, conn); - out: - kleave(""); - return ret; - - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - goto out_unwait; -} /* end afs_rxfs_lookup() */ -#endif + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} diff --git a/fs/afs/fsclient.h b/fs/afs/fsclient.h deleted file mode 100644 index 8ba3e74..0000000 --- a/fs/afs/fsclient.h +++ /dev/null @@ -1,54 +0,0 @@ -/* fsclient.h: AFS File Server client stub declarations - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_FSCLIENT_H -#define _LINUX_AFS_FSCLIENT_H - -#include "server.h" - -extern int afs_rxfs_get_volume_info(struct afs_server *server, - const char *name, - struct afs_volume_info *vinfo); - -extern int afs_rxfs_fetch_file_status(struct afs_server *server, - struct afs_vnode *vnode, - struct afs_volsync *volsync); - -struct afs_rxfs_fetch_descriptor { - struct afs_fid fid; /* file ID to fetch */ - size_t size; /* total number of bytes to fetch */ - off_t offset; /* offset in file to start from */ - void *buffer; /* read buffer */ - size_t actual; /* actual size sent back by server */ -}; - -extern int afs_rxfs_fetch_file_data(struct afs_server *server, - struct afs_vnode *vnode, - struct afs_rxfs_fetch_descriptor *desc, - struct afs_volsync *volsync); - -extern int afs_rxfs_give_up_callback(struct afs_server *server, - struct afs_vnode *vnode); - -/* this doesn't appear to work in OpenAFS server */ -extern int afs_rxfs_lookup(struct afs_server *server, - struct afs_vnode *dir, - const char *filename, - struct afs_vnode *vnode, - struct afs_volsync *volsync); - -/* this is apparently mis-implemented in OpenAFS server */ -extern int afs_rxfs_get_root_volume(struct afs_server *server, - char *buf, - size_t *buflen); - - -#endif /* _LINUX_AFS_FSCLIENT_H */ diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 9d9bca6..c184a4e 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -19,9 +19,6 @@ #include #include #include #include -#include "volume.h" -#include "vnode.h" -#include "super.h" #include "internal.h" struct afs_iget_data { @@ -29,26 +26,25 @@ struct afs_iget_data { struct afs_volume *volume; /* volume on which resides */ }; -/*****************************************************************************/ /* * map the AFS file status to the inode member variables */ -static int afs_inode_map_status(struct afs_vnode *vnode) +static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) { struct inode *inode = AFS_VNODE_TO_I(vnode); - _debug("FS: ft=%d lk=%d sz=%Zu ver=%Lu mod=%hu", + _debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu", vnode->status.type, vnode->status.nlink, - vnode->status.size, - vnode->status.version, + (unsigned long long) vnode->status.size, + vnode->status.data_version, vnode->status.mode); switch (vnode->status.type) { case AFS_FTYPE_FILE: inode->i_mode = S_IFREG | vnode->status.mode; inode->i_op = &afs_file_inode_operations; - inode->i_fop = &generic_ro_fops; + inode->i_fop = &afs_file_operations; break; case AFS_FTYPE_DIR: inode->i_mode = S_IFDIR | vnode->status.mode; @@ -77,9 +73,9 @@ static int afs_inode_map_status(struct a /* check to see whether a symbolic link is really a mountpoint */ if (vnode->status.type == AFS_FTYPE_SYMLINK) { - afs_mntpt_check_symlink(vnode); + afs_mntpt_check_symlink(vnode, key); - if (vnode->flags & AFS_VNODE_MOUNTPOINT) { + if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) { inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_op = &afs_mntpt_inode_operations; inode->i_fop = &afs_mntpt_file_operations; @@ -87,30 +83,8 @@ static int afs_inode_map_status(struct a } return 0; -} /* end afs_inode_map_status() */ +} -/*****************************************************************************/ -/* - * attempt to fetch the status of an inode, coelescing multiple simultaneous - * fetches - */ -static int afs_inode_fetch_status(struct inode *inode) -{ - struct afs_vnode *vnode; - int ret; - - vnode = AFS_FS_I(inode); - - ret = afs_vnode_fetch_status(vnode); - - if (ret == 0) - ret = afs_inode_map_status(vnode); - - return ret; - -} /* end afs_inode_fetch_status() */ - -/*****************************************************************************/ /* * iget5() comparator */ @@ -120,9 +94,8 @@ static int afs_iget5_test(struct inode * return inode->i_ino == data->fid.vnode && inode->i_version == data->fid.unique; -} /* end afs_iget5_test() */ +} -/*****************************************************************************/ /* * iget5() inode initialiser */ @@ -137,14 +110,14 @@ static int afs_iget5_set(struct inode *i vnode->volume = data->volume; return 0; -} /* end afs_iget5_set() */ +} -/*****************************************************************************/ /* * inode retrieval */ -inline int afs_iget(struct super_block *sb, struct afs_fid *fid, - struct inode **_inode) +struct inode *afs_iget(struct super_block *sb, struct key *key, + struct afs_fid *fid, struct afs_file_status *status, + struct afs_callback *cb) { struct afs_iget_data data = { .fid = *fid }; struct afs_super_info *as; @@ -161,20 +134,18 @@ inline int afs_iget(struct super_block * &data); if (!inode) { _leave(" = -ENOMEM"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } + _debug("GOT INODE %p { vl=%x vn=%x, u=%x }", + inode, fid->vid, fid->vnode, fid->unique); + vnode = AFS_FS_I(inode); /* deal with an existing inode */ if (!(inode->i_state & I_NEW)) { - ret = afs_vnode_fetch_status(vnode); - if (ret==0) - *_inode = inode; - else - iput(inode); - _leave(" = %d", ret); - return ret; + _leave(" = %p", inode); + return inode; } #ifdef AFS_CACHING_SUPPORT @@ -186,100 +157,185 @@ #ifdef AFS_CACHING_SUPPORT &vnode->cache); #endif - /* okay... it's a new inode */ - inode->i_flags |= S_NOATIME; - vnode->flags |= AFS_VNODE_CHANGED; - ret = afs_inode_fetch_status(inode); - if (ret<0) + if (!status) { + /* it's a remotely extant inode */ + set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); + ret = afs_vnode_fetch_status(vnode, NULL, key); + if (ret < 0) + goto bad_inode; + } else { + /* it's an inode we just created */ + memcpy(&vnode->status, status, sizeof(vnode->status)); + + if (!cb) { + /* it's a symlink we just created (the fileserver + * didn't give us a callback) */ + vnode->cb_version = 0; + vnode->cb_expiry = 0; + vnode->cb_type = 0; + vnode->cb_expires = get_seconds(); + } else { + vnode->cb_version = cb->version; + vnode->cb_expiry = cb->expiry; + vnode->cb_type = cb->type; + vnode->cb_expires = vnode->cb_expiry + get_seconds(); + } + } + + ret = afs_inode_map_status(vnode, key); + if (ret < 0) goto bad_inode; /* success */ + clear_bit(AFS_VNODE_UNSET, &vnode->flags); + inode->i_flags |= S_NOATIME; unlock_new_inode(inode); - - *_inode = inode; - _leave(" = 0 [CB { v=%u x=%lu t=%u }]", - vnode->cb_version, - vnode->cb_timeout.timo_jif, - vnode->cb_type); - return 0; + _leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type); + return inode; /* failure */ - bad_inode: +bad_inode: make_bad_inode(inode); unlock_new_inode(inode); iput(inode); _leave(" = %d [bad]", ret); + return ERR_PTR(ret); +} + +/* + * validate a vnode/inode + * - there are several things we need to check + * - parent dir data changes (rm, rmdir, rename, mkdir, create, link, + * symlink) + * - parent dir metadata changed (security changes) + * - dentry data changed (write, truncate) + * - dentry metadata changed (security changes) + */ +int afs_validate(struct afs_vnode *vnode, struct key *key) +{ + int ret; + + _enter("{v={%x:%u} fl=%lx},%x", + vnode->fid.vid, vnode->fid.vnode, vnode->flags, + key_serial(key)); + + if (vnode->cb_promised && + !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) && + !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) && + !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { + if (vnode->cb_expires < get_seconds() + 10) { + _debug("callback expired"); + set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); + } else { + goto valid; + } + } + + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) + goto valid; + + mutex_lock(&vnode->validate_lock); + + /* if the promise has expired, we need to check the server again to get + * a new promise - note that if the (parent) directory's metadata was + * changed then the security may be different and we may no longer have + * access */ + if (!vnode->cb_promised || + test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { + _debug("not promised"); + ret = afs_vnode_fetch_status(vnode, NULL, key); + if (ret < 0) + goto error_unlock; + _debug("new promise [fl=%lx]", vnode->flags); + } + + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { + _debug("file already deleted"); + ret = -ESTALE; + goto error_unlock; + } + + /* if the vnode's data version number changed then its contents are + * different */ + if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { + _debug("zap data {%x:%d}", vnode->fid.vid, vnode->fid.vnode); + invalidate_remote_inode(&vnode->vfs_inode); + } + + clear_bit(AFS_VNODE_MODIFIED, &vnode->flags); + mutex_unlock(&vnode->validate_lock); +valid: + _leave(" = 0"); + return 0; + +error_unlock: + mutex_unlock(&vnode->validate_lock); + _leave(" = %d", ret); return ret; -} /* end afs_iget() */ +} -/*****************************************************************************/ /* * read the attributes of an inode */ int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct afs_vnode *vnode; struct inode *inode; - int ret; inode = dentry->d_inode; _enter("{ ino=%lu v=%lu }", inode->i_ino, inode->i_version); - vnode = AFS_FS_I(inode); - - ret = afs_inode_fetch_status(inode); - if (ret == -ENOENT) { - _leave(" = %d [%d %p]", - ret, atomic_read(&dentry->d_count), dentry->d_inode); - return ret; - } - else if (ret < 0) { - make_bad_inode(inode); - _leave(" = %d", ret); - return ret; - } - - /* transfer attributes from the inode structure to the stat - * structure */ generic_fillattr(inode, stat); - - _leave(" = 0 CB { v=%u x=%u t=%u }", - vnode->cb_version, - vnode->cb_expiry, - vnode->cb_type); - return 0; -} /* end afs_inode_getattr() */ +} -/*****************************************************************************/ /* * clear an AFS inode */ void afs_clear_inode(struct inode *inode) { + struct afs_permits *permits; struct afs_vnode *vnode; vnode = AFS_FS_I(inode); - _enter("ino=%lu { vn=%08x v=%u x=%u t=%u }", - inode->i_ino, + _enter("{%x:%d.%d} v=%u x=%u t=%u }", + vnode->fid.vid, vnode->fid.vnode, + vnode->fid.unique, vnode->cb_version, vnode->cb_expiry, - vnode->cb_type - ); + vnode->cb_type); - BUG_ON(inode->i_ino != vnode->fid.vnode); + _debug("CLEAR INODE %p", inode); - afs_vnode_give_up_callback(vnode); + ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode); + + afs_give_up_callback(vnode); + + if (vnode->server) { + spin_lock(&vnode->server->fs_lock); + rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes); + spin_unlock(&vnode->server->fs_lock); + afs_put_server(vnode->server); + vnode->server = NULL; + } + + ASSERT(!vnode->cb_promised); #ifdef AFS_CACHING_SUPPORT cachefs_relinquish_cookie(vnode->cache, 0); vnode->cache = NULL; #endif + mutex_lock(&vnode->permits_lock); + permits = vnode->permits; + rcu_assign_pointer(vnode->permits, NULL); + mutex_unlock(&vnode->permits_lock); + if (permits) + call_rcu(&permits->rcu, afs_zap_permits); + _leave(""); -} /* end afs_clear_inode() */ +} diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 5151d5d..34665f7 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1,6 +1,6 @@ -/* internal.h: internal AFS stuff +/* internal AFS stuff * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -9,48 +9,391 @@ * 2 of the License, or (at your option) any later version. */ -#ifndef AFS_INTERNAL_H -#define AFS_INTERNAL_H - #include #include #include #include +#include +#include +#include +#include "afs.h" +#include "afs_vl.h" + +#define AFS_CELL_MAX_ADDRS 15 + +struct afs_call; + +typedef enum { + AFS_VL_NEW, /* new, uninitialised record */ + AFS_VL_CREATING, /* creating record */ + AFS_VL_VALID, /* record is pending */ + AFS_VL_NO_VOLUME, /* no such volume available */ + AFS_VL_UPDATING, /* update in progress */ + AFS_VL_VOLUME_DELETED, /* volume was deleted */ + AFS_VL_UNCERTAIN, /* uncertain state (update failed) */ +} __attribute__((packed)) afs_vlocation_state_t; + +struct afs_mount_params { + bool rwpath; /* T if the parent should be considered R/W */ + bool force; /* T to force cell type */ + afs_voltype_t type; /* type of volume requested */ + int volnamesz; /* size of volume name */ + const char *volname; /* name of volume to mount */ + struct afs_cell *cell; /* cell in which to find volume */ + struct afs_volume *volume; /* volume record */ + struct key *key; /* key to use for secure mounting */ +}; /* - * debug tracing + * definition of how to wait for the completion of an operation */ -#define kenter(FMT, a...) printk("==> %s("FMT")\n",__FUNCTION__ , ## a) -#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ## a) -#define kdebug(FMT, a...) printk(FMT"\n" , ## a) -#define kproto(FMT, a...) printk("### "FMT"\n" , ## a) -#define knet(FMT, a...) printk(FMT"\n" , ## a) - -#ifdef __KDEBUG -#define _enter(FMT, a...) kenter(FMT , ## a) -#define _leave(FMT, a...) kleave(FMT , ## a) -#define _debug(FMT, a...) kdebug(FMT , ## a) -#define _proto(FMT, a...) kproto(FMT , ## a) -#define _net(FMT, a...) knet(FMT , ## a) -#else -#define _enter(FMT, a...) do { } while(0) -#define _leave(FMT, a...) do { } while(0) -#define _debug(FMT, a...) do { } while(0) -#define _proto(FMT, a...) do { } while(0) -#define _net(FMT, a...) do { } while(0) -#endif +struct afs_wait_mode { + /* RxRPC received message notification */ + void (*rx_wakeup)(struct afs_call *call); -static inline void afs_discard_my_signals(void) -{ - while (signal_pending(current)) { - siginfo_t sinfo; + /* synchronous call waiter and call dispatched notification */ + int (*wait)(struct afs_call *call); + + /* asynchronous call completion */ + void (*async_complete)(void *reply, int error); +}; + +extern const struct afs_wait_mode afs_sync_call; +extern const struct afs_wait_mode afs_async_call; - spin_lock_irq(¤t->sighand->siglock); - dequeue_signal(current,¤t->blocked, &sinfo); - spin_unlock_irq(¤t->sighand->siglock); - } +/* + * a record of an in-progress RxRPC call + */ +struct afs_call { + const struct afs_call_type *type; /* type of call */ + const struct afs_wait_mode *wait_mode; /* completion wait mode */ + wait_queue_head_t waitq; /* processes awaiting completion */ + struct work_struct async_work; /* asynchronous work processor */ + struct work_struct work; /* actual work processor */ + struct sk_buff_head rx_queue; /* received packets */ + struct rxrpc_call *rxcall; /* RxRPC call handle */ + struct key *key; /* security for this call */ + struct afs_server *server; /* server affected by incoming CM call */ + void *request; /* request data (first part) */ + void *request2; /* request data (second part) */ + void *buffer; /* reply receive buffer */ + void *reply; /* reply buffer (first part) */ + void *reply2; /* reply buffer (second part) */ + void *reply3; /* reply buffer (third part) */ + void *reply4; /* reply buffer (fourth part) */ + enum { /* call state */ + AFS_CALL_REQUESTING, /* request is being sent for outgoing call */ + AFS_CALL_AWAIT_REPLY, /* awaiting reply to outgoing call */ + AFS_CALL_AWAIT_OP_ID, /* awaiting op ID on incoming call */ + AFS_CALL_AWAIT_REQUEST, /* awaiting request data on incoming call */ + AFS_CALL_REPLYING, /* replying to incoming call */ + AFS_CALL_AWAIT_ACK, /* awaiting final ACK of incoming call */ + AFS_CALL_COMPLETE, /* successfully completed */ + AFS_CALL_BUSY, /* server was busy */ + AFS_CALL_ABORTED, /* call was aborted */ + AFS_CALL_ERROR, /* call failed due to error */ + } state; + int error; /* error code */ + unsigned request_size; /* size of request data */ + unsigned reply_max; /* maximum size of reply */ + unsigned reply_size; /* current size of reply */ + unsigned short offset; /* offset into received data store */ + unsigned char unmarshall; /* unmarshalling phase */ + bool incoming; /* T if incoming call */ + u16 service_id; /* RxRPC service ID to call */ + __be16 port; /* target UDP port */ + __be32 operation_ID; /* operation ID for an incoming call */ + u32 count; /* count for use in unmarshalling */ + __be32 tmp; /* place to extract temporary data */ +}; + +struct afs_call_type { + const char *name; + + /* deliver request or reply data to an call + * - returning an error will cause the call to be aborted + */ + int (*deliver)(struct afs_call *call, struct sk_buff *skb, + bool last); + + /* map an abort code to an error number */ + int (*abort_to_error)(u32 abort_code); + + /* clean up a call */ + void (*destructor)(struct afs_call *call); +}; + +/* + * AFS superblock private data + * - there's one superblock per volume + */ +struct afs_super_info { + struct afs_volume *volume; /* volume record */ + char rwparent; /* T if parent is R/W AFS volume */ +}; + +static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) +{ + return sb->s_fs_info; } +extern struct file_system_type afs_fs_type; + +/* + * entry in the cached cell catalogue + */ +struct afs_cache_cell { + char name[AFS_MAXCELLNAME]; /* cell name (padded with NULs) */ + struct in_addr vl_servers[15]; /* cached cell VL servers */ +}; + +/* + * AFS cell record + */ +struct afs_cell { + atomic_t usage; + struct list_head link; /* main cell list link */ + struct key *anonymous_key; /* anonymous user key for this cell */ + struct list_head proc_link; /* /proc cell list link */ + struct proc_dir_entry *proc_dir; /* /proc dir for this cell */ +#ifdef AFS_CACHING_SUPPORT + struct cachefs_cookie *cache; /* caching cookie */ +#endif + + /* server record management */ + rwlock_t servers_lock; /* active server list lock */ + struct list_head servers; /* active server list */ + + /* volume location record management */ + struct rw_semaphore vl_sem; /* volume management serialisation semaphore */ + struct list_head vl_list; /* cell's active VL record list */ + spinlock_t vl_lock; /* vl_list lock */ + unsigned short vl_naddrs; /* number of VL servers in addr list */ + unsigned short vl_curr_svix; /* current server index */ + struct in_addr vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */ + + char name[0]; /* cell name - must go last */ +}; + +/* + * entry in the cached volume location catalogue + */ +struct afs_cache_vlocation { + /* volume name (lowercase, padded with NULs) */ + uint8_t name[AFS_MAXVOLNAME + 1]; + + uint8_t nservers; /* number of entries used in servers[] */ + uint8_t vidmask; /* voltype mask for vid[] */ + uint8_t srvtmask[8]; /* voltype masks for servers[] */ +#define AFS_VOL_VTM_RW 0x01 /* R/W version of the volume is available (on this server) */ +#define AFS_VOL_VTM_RO 0x02 /* R/O version of the volume is available (on this server) */ +#define AFS_VOL_VTM_BAK 0x04 /* backup version of the volume is available (on this server) */ + + afs_volid_t vid[3]; /* volume IDs for R/W, R/O and Bak volumes */ + struct in_addr servers[8]; /* fileserver addresses */ + time_t rtime; /* last retrieval time */ +}; + +/* + * volume -> vnode hash table entry + */ +struct afs_cache_vhash { + afs_voltype_t vtype; /* which volume variation */ + uint8_t hash_bucket; /* which hash bucket this represents */ +} __attribute__((packed)); + +/* + * AFS volume location record + */ +struct afs_vlocation { + atomic_t usage; + time_t time_of_death; /* time at which put reduced usage to 0 */ + struct list_head link; /* link in cell volume location list */ + struct list_head grave; /* link in master graveyard list */ + struct list_head update; /* link in master update list */ + struct afs_cell *cell; /* cell to which volume belongs */ +#ifdef AFS_CACHING_SUPPORT + struct cachefs_cookie *cache; /* caching cookie */ +#endif + struct afs_cache_vlocation vldb; /* volume information DB record */ + struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ + wait_queue_head_t waitq; /* status change waitqueue */ + time_t update_at; /* time at which record should be updated */ + spinlock_t lock; /* access lock */ + afs_vlocation_state_t state; /* volume location state */ + unsigned short upd_rej_cnt; /* ENOMEDIUM count during update */ + unsigned short upd_busy_cnt; /* EBUSY count during update */ + bool valid; /* T if valid */ +}; + +/* + * AFS fileserver record + */ +struct afs_server { + atomic_t usage; + time_t time_of_death; /* time at which put reduced usage to 0 */ + struct in_addr addr; /* server address */ + struct afs_cell *cell; /* cell in which server resides */ + struct list_head link; /* link in cell's server list */ + struct list_head grave; /* link in master graveyard list */ + struct rb_node master_rb; /* link in master by-addr tree */ + struct rw_semaphore sem; /* access lock */ + + /* file service access */ + struct rb_root fs_vnodes; /* vnodes backed by this server (ordered by FID) */ + unsigned long fs_act_jif; /* time at which last activity occurred */ + unsigned long fs_dead_jif; /* time at which no longer to be considered dead */ + spinlock_t fs_lock; /* access lock */ + int fs_state; /* 0 or reason FS currently marked dead (-errno) */ + + /* callback promise management */ + struct rb_root cb_promises; /* vnode expiration list (ordered earliest first) */ + struct delayed_work cb_updater; /* callback updater */ + struct delayed_work cb_break_work; /* collected break dispatcher */ + wait_queue_head_t cb_break_waitq; /* space available in cb_break waitqueue */ + spinlock_t cb_lock; /* access lock */ + struct afs_callback cb_break[64]; /* ring of callbacks awaiting breaking */ + atomic_t cb_break_n; /* number of pending breaks */ + u8 cb_break_head; /* head of callback breaking ring */ + u8 cb_break_tail; /* tail of callback breaking ring */ +}; + +/* + * AFS volume access record + */ +struct afs_volume { + atomic_t usage; + struct afs_cell *cell; /* cell to which belongs (unrefd ptr) */ + struct afs_vlocation *vlocation; /* volume location */ +#ifdef AFS_CACHING_SUPPORT + struct cachefs_cookie *cache; /* caching cookie */ +#endif + afs_volid_t vid; /* volume ID */ + afs_voltype_t type; /* type of volume */ + char type_force; /* force volume type (suppress R/O -> R/W) */ + unsigned short nservers; /* number of server slots filled */ + unsigned short rjservers; /* number of servers discarded due to -ENOMEDIUM */ + struct afs_server *servers[8]; /* servers on which volume resides (ordered) */ + struct rw_semaphore server_sem; /* lock for accessing current server */ +}; + +/* + * vnode catalogue entry + */ +struct afs_cache_vnode { + afs_vnodeid_t vnode_id; /* vnode ID */ + unsigned vnode_unique; /* vnode ID uniquifier */ + afs_dataversion_t data_version; /* data version */ +}; + +/* + * AFS inode private data + */ +struct afs_vnode { + struct inode vfs_inode; /* the VFS's inode record */ + + struct afs_volume *volume; /* volume on which vnode resides */ + struct afs_server *server; /* server currently supplying this file */ + struct afs_fid fid; /* the file identifier for this inode */ + struct afs_file_status status; /* AFS status info for this file */ +#ifdef AFS_CACHING_SUPPORT + struct cachefs_cookie *cache; /* caching cookie */ +#endif + struct afs_permits *permits; /* cache of permits so far obtained */ + struct mutex permits_lock; /* lock for altering permits list */ + struct mutex validate_lock; /* lock for validating this vnode */ + wait_queue_head_t update_waitq; /* status fetch waitqueue */ + int update_cnt; /* number of outstanding ops that will update the + * status */ + spinlock_t lock; /* waitqueue/flags lock */ + unsigned long flags; +#define AFS_VNODE_CB_BROKEN 0 /* set if vnode's callback was broken */ +#define AFS_VNODE_UNSET 1 /* set if vnode attributes not yet set */ +#define AFS_VNODE_MODIFIED 2 /* set if vnode's data modified */ +#define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ +#define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ +#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ + + long acl_order; /* ACL check count (callback break count) */ + + /* outstanding callback notification on this file */ + struct rb_node server_rb; /* link in server->fs_vnodes */ + struct rb_node cb_promise; /* link in server->cb_promises */ + struct work_struct cb_broken_work; /* work to be done on callback break */ + time_t cb_expires; /* time at which callback expires */ + time_t cb_expires_at; /* time used to order cb_promise */ + unsigned cb_version; /* callback version */ + unsigned cb_expiry; /* callback expiry time */ + afs_callback_type_t cb_type; /* type of callback */ + bool cb_promised; /* true if promise still holds */ +}; + +/* + * cached security record for one user's attempt to access a vnode + */ +struct afs_permit { + struct key *key; /* RxRPC ticket holding a security context */ + afs_access_t access_mask; /* access mask for this key */ +}; + +/* + * cache of security records from attempts to access a vnode + */ +struct afs_permits { + struct rcu_head rcu; /* disposal procedure */ + int count; /* number of records */ + struct afs_permit permits[0]; /* the permits so far examined */ +}; + +/* + * record of one of a system's set of network interfaces + */ +struct afs_interface { + unsigned index; /* interface index */ + struct in_addr address; /* IPv4 address bound to interface */ + struct in_addr netmask; /* netmask applied to address */ + unsigned mtu; /* MTU of interface */ +}; + +/* + * UUID definition [internet draft] + * - the timestamp is a 60-bit value, split 32/16/12, and goes in 100ns + * increments since midnight 15th October 1582 + * - add AFS_UUID_TO_UNIX_TIME to convert unix time in 100ns units to UUID + * time + * - the clock sequence is a 14-bit counter to avoid duplicate times + */ +struct afs_uuid { + u32 time_low; /* low part of timestamp */ + u16 time_mid; /* mid part of timestamp */ + u16 time_hi_and_version; /* high part of timestamp and version */ +#define AFS_UUID_TO_UNIX_TIME 0x01b21dd213814000ULL +#define AFS_UUID_TIMEHI_MASK 0x0fff +#define AFS_UUID_VERSION_TIME 0x1000 /* time-based UUID */ +#define AFS_UUID_VERSION_NAME 0x3000 /* name-based UUID */ +#define AFS_UUID_VERSION_RANDOM 0x4000 /* (pseudo-)random generated UUID */ + u8 clock_seq_hi_and_reserved; /* clock seq hi and variant */ +#define AFS_UUID_CLOCKHI_MASK 0x3f +#define AFS_UUID_VARIANT_STD 0x80 + u8 clock_seq_low; /* clock seq low */ + u8 node[6]; /* spatially unique node ID (MAC addr) */ +}; + +/*****************************************************************************/ +/* + * callback.c + */ +extern void afs_init_callback_state(struct afs_server *); +extern void afs_broken_callback_work(struct work_struct *); +extern void afs_break_callbacks(struct afs_server *, size_t, + struct afs_callback[]); +extern void afs_discard_callback_on_delete(struct afs_vnode *); +extern void afs_give_up_callback(struct afs_vnode *); +extern void afs_dispatch_give_up_callbacks(struct work_struct *); +extern void afs_flush_callback_breaks(struct afs_server *); +extern int __init afs_callback_update_init(void); +extern void __exit afs_callback_update_kill(void); + /* * cell.c */ @@ -60,57 +403,156 @@ #ifdef AFS_CACHING_SUPPORT extern struct cachefs_index_def afs_cache_cell_index_def; #endif +#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) +extern int afs_cell_init(char *); +extern struct afs_cell *afs_cell_create(const char *, char *); +extern struct afs_cell *afs_cell_lookup(const char *, unsigned); +extern struct afs_cell *afs_grab_cell(struct afs_cell *); +extern void afs_put_cell(struct afs_cell *); +extern void afs_cell_purge(void); + +/* + * cmservice.c + */ +extern bool afs_cm_incoming_call(struct afs_call *); + /* * dir.c */ extern const struct inode_operations afs_dir_inode_operations; extern const struct file_operations afs_dir_file_operations; +extern int afs_permission(struct inode *, int, struct nameidata *); + /* * file.c */ extern const struct address_space_operations afs_fs_aops; extern const struct inode_operations afs_file_inode_operations; +extern const struct file_operations afs_file_operations; + +extern int afs_open(struct inode *, struct file *); +extern int afs_release(struct inode *, struct file *); #ifdef AFS_CACHING_SUPPORT -extern int afs_cache_get_page_cookie(struct page *page, - struct cachefs_page **_page_cookie); +extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **); #endif /* - * inode.c + * fsclient.c */ -extern int afs_iget(struct super_block *sb, struct afs_fid *fid, - struct inode **_inode); -extern int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat); -extern void afs_clear_inode(struct inode *inode); +extern int afs_fs_fetch_file_status(struct afs_server *, struct key *, + struct afs_vnode *, struct afs_volsync *, + const struct afs_wait_mode *); +extern int afs_fs_give_up_callbacks(struct afs_server *, + const struct afs_wait_mode *); +extern int afs_fs_fetch_data(struct afs_server *, struct key *, + struct afs_vnode *, off_t, size_t, struct page *, + const struct afs_wait_mode *); +extern int afs_fs_create(struct afs_server *, struct key *, + struct afs_vnode *, const char *, umode_t, + struct afs_fid *, struct afs_file_status *, + struct afs_callback *, + const struct afs_wait_mode *); +extern int afs_fs_remove(struct afs_server *, struct key *, + struct afs_vnode *, const char *, bool, + const struct afs_wait_mode *); +extern int afs_fs_link(struct afs_server *, struct key *, struct afs_vnode *, + struct afs_vnode *, const char *, + const struct afs_wait_mode *); +extern int afs_fs_symlink(struct afs_server *, struct key *, + struct afs_vnode *, const char *, const char *, + struct afs_fid *, struct afs_file_status *, + const struct afs_wait_mode *); +extern int afs_fs_rename(struct afs_server *, struct key *, + struct afs_vnode *, const char *, + struct afs_vnode *, const char *, + const struct afs_wait_mode *); /* - * key_afs.c + * inode.c */ -#ifdef CONFIG_KEYS -extern int afs_key_register(void); -extern void afs_key_unregister(void); -#endif +extern struct inode *afs_iget(struct super_block *, struct key *, + struct afs_fid *, struct afs_file_status *, + struct afs_callback *); +extern int afs_validate(struct afs_vnode *, struct key *); +extern int afs_inode_getattr(struct vfsmount *, struct dentry *, + struct kstat *); +extern void afs_zap_permits(struct rcu_head *); +extern void afs_clear_inode(struct inode *); /* * main.c */ +extern struct afs_uuid afs_uuid; #ifdef AFS_CACHING_SUPPORT extern struct cachefs_netfs afs_cache_netfs; #endif /* + * misc.c + */ +extern int afs_abort_to_error(u32); + +/* * mntpt.c */ extern const struct inode_operations afs_mntpt_inode_operations; extern const struct file_operations afs_mntpt_file_operations; -extern struct afs_timer afs_mntpt_expiry_timer; -extern struct afs_timer_ops afs_mntpt_expiry_timer_ops; extern unsigned long afs_mntpt_expiry_timeout; -extern int afs_mntpt_check_symlink(struct afs_vnode *vnode); +extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *); +extern void afs_mntpt_kill_timer(void); +extern void afs_umount_begin(struct vfsmount *, int); + +/* + * proc.c + */ +extern int afs_proc_init(void); +extern void afs_proc_cleanup(void); +extern int afs_proc_cell_setup(struct afs_cell *); +extern void afs_proc_cell_remove(struct afs_cell *); + +/* + * rxrpc.c + */ +extern int afs_open_socket(void); +extern void afs_close_socket(void); +extern int afs_make_call(struct in_addr *, struct afs_call *, gfp_t, + const struct afs_wait_mode *); +extern struct afs_call *afs_alloc_flat_call(const struct afs_call_type *, + size_t, size_t); +extern void afs_flat_call_destructor(struct afs_call *); +extern void afs_transfer_reply(struct afs_call *, struct sk_buff *); +extern void afs_send_empty_reply(struct afs_call *); +extern void afs_send_simple_reply(struct afs_call *, const void *, size_t); +extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *, + size_t); + +/* + * security.c + */ +extern void afs_clear_permits(struct afs_vnode *); +extern void afs_cache_permit(struct afs_vnode *, struct key *, long); +extern struct key *afs_request_key(struct afs_cell *); +extern int afs_permission(struct inode *, int, struct nameidata *); + +/* + * server.c + */ +extern spinlock_t afs_server_peer_lock; + +#define afs_get_server(S) \ +do { \ + _debug("GET SERVER %d", atomic_read(&(S)->usage)); \ + atomic_inc(&(S)->usage); \ +} while(0) + +extern struct afs_server *afs_lookup_server(struct afs_cell *, + const struct in_addr *); +extern struct afs_server *afs_find_server(const struct in_addr *); +extern void afs_put_server(struct afs_server *); +extern void __exit afs_purge_servers(void); /* * super.c @@ -118,22 +560,211 @@ extern int afs_mntpt_check_symlink(struc extern int afs_fs_init(void); extern void afs_fs_exit(void); -#define AFS_CB_HASH_COUNT (PAGE_SIZE / sizeof(struct list_head)) +/* + * use-rtnetlink.c + */ +extern int afs_get_ipv4_interfaces(struct afs_interface *, size_t, bool); +extern int afs_get_MAC_address(u8 [6]); -extern struct list_head afs_cb_hash_tbl[]; -extern spinlock_t afs_cb_hash_lock; +/* + * vlclient.c + */ +#ifdef AFS_CACHING_SUPPORT +extern struct cachefs_index_def afs_vlocation_cache_index_def; +#endif -#define afs_cb_hash(SRV,FID) \ - afs_cb_hash_tbl[((unsigned long)(SRV) + \ - (FID)->vid + (FID)->vnode + (FID)->unique) % \ - AFS_CB_HASH_COUNT] +extern int afs_vl_get_entry_by_name(struct in_addr *, struct key *, + const char *, struct afs_cache_vlocation *, + const struct afs_wait_mode *); +extern int afs_vl_get_entry_by_id(struct in_addr *, struct key *, + afs_volid_t, afs_voltype_t, + struct afs_cache_vlocation *, + const struct afs_wait_mode *); /* - * proc.c + * vlocation.c */ -extern int afs_proc_init(void); -extern void afs_proc_cleanup(void); -extern int afs_proc_cell_setup(struct afs_cell *cell); -extern void afs_proc_cell_remove(struct afs_cell *cell); +#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0) + +extern int __init afs_vlocation_update_init(void); +extern struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *, + struct key *, + const char *, size_t); +extern void afs_put_vlocation(struct afs_vlocation *); +extern void __exit afs_vlocation_purge(void); + +/* + * vnode.c + */ +#ifdef AFS_CACHING_SUPPORT +extern struct cachefs_index_def afs_vnode_cache_index_def; +#endif + +extern struct afs_timer_ops afs_vnode_cb_timed_out_ops; + +static inline struct afs_vnode *AFS_FS_I(struct inode *inode) +{ + return container_of(inode, struct afs_vnode, vfs_inode); +} + +static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode) +{ + return &vnode->vfs_inode; +} + +extern void afs_vnode_finalise_status_update(struct afs_vnode *, + struct afs_server *); +extern int afs_vnode_fetch_status(struct afs_vnode *, struct afs_vnode *, + struct key *); +extern int afs_vnode_fetch_data(struct afs_vnode *, struct key *, + off_t, size_t, struct page *); +extern int afs_vnode_create(struct afs_vnode *, struct key *, const char *, + umode_t, struct afs_fid *, struct afs_file_status *, + struct afs_callback *, struct afs_server **); +extern int afs_vnode_remove(struct afs_vnode *, struct key *, const char *, + bool); +extern int afs_vnode_link(struct afs_vnode *, struct afs_vnode *, struct key *, + const char *); +extern int afs_vnode_symlink(struct afs_vnode *, struct key *, const char *, + const char *, struct afs_fid *, + struct afs_file_status *, struct afs_server **); +extern int afs_vnode_rename(struct afs_vnode *, struct afs_vnode *, + struct key *, const char *, const char *); + +/* + * volume.c + */ +#ifdef AFS_CACHING_SUPPORT +extern struct cachefs_index_def afs_volume_cache_index_def; +#endif + +#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0) + +extern void afs_put_volume(struct afs_volume *); +extern struct afs_volume *afs_volume_lookup(struct afs_mount_params *); +extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *); +extern int afs_volume_release_fileserver(struct afs_vnode *, + struct afs_server *, int); + +/*****************************************************************************/ +/* + * debug tracing + */ +extern unsigned afs_debug; + +#define dbgprintk(FMT,...) \ + printk("[%x%-6.6s] "FMT"\n", smp_processor_id(), current->comm ,##__VA_ARGS__) + +/* make sure we maintain the format strings, even when debugging is disabled */ +static inline __attribute__((format(printf,1,2))) +void _dbprintk(const char *fmt, ...) +{ +} + +#define kenter(FMT,...) dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__) +#define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__) +#define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__) + + +#if defined(__KDEBUG) +#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) +#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) +#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) + +#elif defined(CONFIG_AFS_DEBUG) +#define AFS_DEBUG_KENTER 0x01 +#define AFS_DEBUG_KLEAVE 0x02 +#define AFS_DEBUG_KDEBUG 0x04 + +#define _enter(FMT,...) \ +do { \ + if (unlikely(afs_debug & AFS_DEBUG_KENTER)) \ + kenter(FMT,##__VA_ARGS__); \ +} while (0) + +#define _leave(FMT,...) \ +do { \ + if (unlikely(afs_debug & AFS_DEBUG_KLEAVE)) \ + kleave(FMT,##__VA_ARGS__); \ +} while (0) + +#define _debug(FMT,...) \ +do { \ + if (unlikely(afs_debug & AFS_DEBUG_KDEBUG)) \ + kdebug(FMT,##__VA_ARGS__); \ +} while (0) + +#else +#define _enter(FMT,...) _dbprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__) +#define _leave(FMT,...) _dbprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__) +#define _debug(FMT,...) _dbprintk(" "FMT ,##__VA_ARGS__) +#endif + +/* + * debug assertion checking + */ +#if 1 // defined(__KDEBUGALL) + +#define ASSERT(X) \ +do { \ + if (unlikely(!(X))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "AFS: Assertion failed\n"); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ + if (unlikely(!((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "AFS: Assertion failed\n"); \ + printk(KERN_ERR "%lu " #OP " %lu is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTIF(C, X) \ +do { \ + if (unlikely((C) && !(X))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "AFS: Assertion failed\n"); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ + if (unlikely((C) && !((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "AFS: Assertion failed\n"); \ + printk(KERN_ERR "%lu " #OP " %lu is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +#else + +#define ASSERT(X) \ +do { \ +} while(0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ +} while(0) + +#define ASSERTIF(C, X) \ +do { \ +} while(0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ +} while(0) -#endif /* AFS_INTERNAL_H */ +#endif /* __KDEBUGALL */ diff --git a/fs/afs/kafsasyncd.c b/fs/afs/kafsasyncd.c deleted file mode 100644 index 615df24..0000000 --- a/fs/afs/kafsasyncd.c +++ /dev/null @@ -1,255 +0,0 @@ -/* kafsasyncd.c: AFS asynchronous operation daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - * - * - * The AFS async daemon is used to the following: - * - probe "dead" servers to see whether they've come back to life yet. - * - probe "live" servers that we haven't talked to for a while to see if they are better - * candidates for serving than what we're currently using - * - poll volume location servers to keep up to date volume location lists - */ - -#include -#include -#include -#include -#include -#include "cell.h" -#include "server.h" -#include "volume.h" -#include "kafsasyncd.h" -#include "kafstimod.h" -#include -#include -#include "internal.h" - -static DECLARE_COMPLETION(kafsasyncd_alive); -static DECLARE_COMPLETION(kafsasyncd_dead); -static DECLARE_WAIT_QUEUE_HEAD(kafsasyncd_sleepq); -static struct task_struct *kafsasyncd_task; -static int kafsasyncd_die; - -static int kafsasyncd(void *arg); - -static LIST_HEAD(kafsasyncd_async_attnq); -static LIST_HEAD(kafsasyncd_async_busyq); -static DEFINE_SPINLOCK(kafsasyncd_async_lock); - -static void kafsasyncd_null_call_attn_func(struct rxrpc_call *call) -{ -} - -static void kafsasyncd_null_call_error_func(struct rxrpc_call *call) -{ -} - -/*****************************************************************************/ -/* - * start the async daemon - */ -int afs_kafsasyncd_start(void) -{ - int ret; - - ret = kernel_thread(kafsasyncd, NULL, 0); - if (ret < 0) - return ret; - - wait_for_completion(&kafsasyncd_alive); - - return ret; -} /* end afs_kafsasyncd_start() */ - -/*****************************************************************************/ -/* - * stop the async daemon - */ -void afs_kafsasyncd_stop(void) -{ - /* get rid of my daemon */ - kafsasyncd_die = 1; - wake_up(&kafsasyncd_sleepq); - wait_for_completion(&kafsasyncd_dead); - -} /* end afs_kafsasyncd_stop() */ - -/*****************************************************************************/ -/* - * probing daemon - */ -static int kafsasyncd(void *arg) -{ - struct afs_async_op *op; - int die; - - DECLARE_WAITQUEUE(myself, current); - - kafsasyncd_task = current; - - printk("kAFS: Started kafsasyncd %d\n", current->pid); - - daemonize("kafsasyncd"); - - complete(&kafsasyncd_alive); - - /* loop around looking for things to attend to */ - do { - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&kafsasyncd_sleepq, &myself); - - for (;;) { - if (!list_empty(&kafsasyncd_async_attnq) || - signal_pending(current) || - kafsasyncd_die) - break; - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - - remove_wait_queue(&kafsasyncd_sleepq, &myself); - set_current_state(TASK_RUNNING); - - try_to_freeze(); - - /* discard pending signals */ - afs_discard_my_signals(); - - die = kafsasyncd_die; - - /* deal with the next asynchronous operation requiring - * attention */ - if (!list_empty(&kafsasyncd_async_attnq)) { - struct afs_async_op *op; - - _debug("@@@ Begin Asynchronous Operation"); - - op = NULL; - spin_lock(&kafsasyncd_async_lock); - - if (!list_empty(&kafsasyncd_async_attnq)) { - op = list_entry(kafsasyncd_async_attnq.next, - struct afs_async_op, link); - list_move_tail(&op->link, - &kafsasyncd_async_busyq); - } - - spin_unlock(&kafsasyncd_async_lock); - - _debug("@@@ Operation %p {%p}\n", - op, op ? op->ops : NULL); - - if (op) - op->ops->attend(op); - - _debug("@@@ End Asynchronous Operation"); - } - - } while(!die); - - /* need to kill all outstanding asynchronous operations before - * exiting */ - kafsasyncd_task = NULL; - spin_lock(&kafsasyncd_async_lock); - - /* fold the busy and attention queues together */ - list_splice_init(&kafsasyncd_async_busyq, - &kafsasyncd_async_attnq); - - /* dequeue kafsasyncd from all their wait queues */ - list_for_each_entry(op, &kafsasyncd_async_attnq, link) { - op->call->app_attn_func = kafsasyncd_null_call_attn_func; - op->call->app_error_func = kafsasyncd_null_call_error_func; - remove_wait_queue(&op->call->waitq, &op->waiter); - } - - spin_unlock(&kafsasyncd_async_lock); - - /* abort all the operations */ - while (!list_empty(&kafsasyncd_async_attnq)) { - op = list_entry(kafsasyncd_async_attnq.next, struct afs_async_op, link); - list_del_init(&op->link); - - rxrpc_call_abort(op->call, -EIO); - rxrpc_put_call(op->call); - op->call = NULL; - - op->ops->discard(op); - } - - /* and that's all */ - _leave(""); - complete_and_exit(&kafsasyncd_dead, 0); - -} /* end kafsasyncd() */ - -/*****************************************************************************/ -/* - * begin an operation - * - place operation on busy queue - */ -void afs_kafsasyncd_begin_op(struct afs_async_op *op) -{ - _enter(""); - - spin_lock(&kafsasyncd_async_lock); - - init_waitqueue_entry(&op->waiter, kafsasyncd_task); - add_wait_queue(&op->call->waitq, &op->waiter); - - list_move_tail(&op->link, &kafsasyncd_async_busyq); - - spin_unlock(&kafsasyncd_async_lock); - - _leave(""); -} /* end afs_kafsasyncd_begin_op() */ - -/*****************************************************************************/ -/* - * request attention for an operation - * - move to attention queue - */ -void afs_kafsasyncd_attend_op(struct afs_async_op *op) -{ - _enter(""); - - spin_lock(&kafsasyncd_async_lock); - - list_move_tail(&op->link, &kafsasyncd_async_attnq); - - spin_unlock(&kafsasyncd_async_lock); - - wake_up(&kafsasyncd_sleepq); - - _leave(""); -} /* end afs_kafsasyncd_attend_op() */ - -/*****************************************************************************/ -/* - * terminate an operation - * - remove from either queue - */ -void afs_kafsasyncd_terminate_op(struct afs_async_op *op) -{ - _enter(""); - - spin_lock(&kafsasyncd_async_lock); - - if (!list_empty(&op->link)) { - list_del_init(&op->link); - remove_wait_queue(&op->call->waitq, &op->waiter); - } - - spin_unlock(&kafsasyncd_async_lock); - - wake_up(&kafsasyncd_sleepq); - - _leave(""); -} /* end afs_kafsasyncd_terminate_op() */ diff --git a/fs/afs/kafsasyncd.h b/fs/afs/kafsasyncd.h deleted file mode 100644 index 791803f..0000000 --- a/fs/afs/kafsasyncd.h +++ /dev/null @@ -1,52 +0,0 @@ -/* kafsasyncd.h: AFS asynchronous operation daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_KAFSASYNCD_H -#define _LINUX_AFS_KAFSASYNCD_H - -#include "types.h" - -struct afs_async_op; - -struct afs_async_op_ops { - void (*attend)(struct afs_async_op *op); - void (*discard)(struct afs_async_op *op); -}; - -/*****************************************************************************/ -/* - * asynchronous operation record - */ -struct afs_async_op -{ - struct list_head link; - struct afs_server *server; /* server being contacted */ - struct rxrpc_call *call; /* RxRPC call performing op */ - wait_queue_t waiter; /* wait queue for kafsasyncd */ - const struct afs_async_op_ops *ops; /* operations */ -}; - -static inline void afs_async_op_init(struct afs_async_op *op, - const struct afs_async_op_ops *ops) -{ - INIT_LIST_HEAD(&op->link); - op->call = NULL; - op->ops = ops; -} - -extern int afs_kafsasyncd_start(void); -extern void afs_kafsasyncd_stop(void); - -extern void afs_kafsasyncd_begin_op(struct afs_async_op *op); -extern void afs_kafsasyncd_attend_op(struct afs_async_op *op); -extern void afs_kafsasyncd_terminate_op(struct afs_async_op *op); - -#endif /* _LINUX_AFS_KAFSASYNCD_H */ diff --git a/fs/afs/kafstimod.c b/fs/afs/kafstimod.c deleted file mode 100644 index 694344e..0000000 --- a/fs/afs/kafstimod.c +++ /dev/null @@ -1,205 +0,0 @@ -/* kafstimod.c: AFS timeout daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include "cell.h" -#include "volume.h" -#include "kafstimod.h" -#include -#include "internal.h" - -static DECLARE_COMPLETION(kafstimod_alive); -static DECLARE_COMPLETION(kafstimod_dead); -static DECLARE_WAIT_QUEUE_HEAD(kafstimod_sleepq); -static int kafstimod_die; - -static LIST_HEAD(kafstimod_list); -static DEFINE_SPINLOCK(kafstimod_lock); - -static int kafstimod(void *arg); - -/*****************************************************************************/ -/* - * start the timeout daemon - */ -int afs_kafstimod_start(void) -{ - int ret; - - ret = kernel_thread(kafstimod, NULL, 0); - if (ret < 0) - return ret; - - wait_for_completion(&kafstimod_alive); - - return ret; -} /* end afs_kafstimod_start() */ - -/*****************************************************************************/ -/* - * stop the timeout daemon - */ -void afs_kafstimod_stop(void) -{ - /* get rid of my daemon */ - kafstimod_die = 1; - wake_up(&kafstimod_sleepq); - wait_for_completion(&kafstimod_dead); - -} /* end afs_kafstimod_stop() */ - -/*****************************************************************************/ -/* - * timeout processing daemon - */ -static int kafstimod(void *arg) -{ - struct afs_timer *timer; - - DECLARE_WAITQUEUE(myself, current); - - printk("kAFS: Started kafstimod %d\n", current->pid); - - daemonize("kafstimod"); - - complete(&kafstimod_alive); - - /* loop around looking for things to attend to */ - loop: - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&kafstimod_sleepq, &myself); - - for (;;) { - unsigned long jif; - signed long timeout; - - /* deal with the server being asked to die */ - if (kafstimod_die) { - remove_wait_queue(&kafstimod_sleepq, &myself); - _leave(""); - complete_and_exit(&kafstimod_dead, 0); - } - - try_to_freeze(); - - /* discard pending signals */ - afs_discard_my_signals(); - - /* work out the time to elapse before the next event */ - spin_lock(&kafstimod_lock); - if (list_empty(&kafstimod_list)) { - timeout = MAX_SCHEDULE_TIMEOUT; - } - else { - timer = list_entry(kafstimod_list.next, - struct afs_timer, link); - timeout = timer->timo_jif; - jif = jiffies; - - if (time_before_eq((unsigned long) timeout, jif)) - goto immediate; - - else { - timeout = (long) timeout - (long) jiffies; - } - } - spin_unlock(&kafstimod_lock); - - schedule_timeout(timeout); - - set_current_state(TASK_INTERRUPTIBLE); - } - - /* the thing on the front of the queue needs processing - * - we come here with the lock held and timer pointing to the expired - * entry - */ - immediate: - remove_wait_queue(&kafstimod_sleepq, &myself); - set_current_state(TASK_RUNNING); - - _debug("@@@ Begin Timeout of %p", timer); - - /* dequeue the timer */ - list_del_init(&timer->link); - spin_unlock(&kafstimod_lock); - - /* call the timeout function */ - timer->ops->timed_out(timer); - - _debug("@@@ End Timeout"); - goto loop; - -} /* end kafstimod() */ - -/*****************************************************************************/ -/* - * (re-)queue a timer - */ -void afs_kafstimod_add_timer(struct afs_timer *timer, unsigned long timeout) -{ - struct afs_timer *ptimer; - struct list_head *_p; - - _enter("%p,%lu", timer, timeout); - - spin_lock(&kafstimod_lock); - - list_del(&timer->link); - - /* the timer was deferred or reset - put it back in the queue at the - * right place */ - timer->timo_jif = jiffies + timeout; - - list_for_each(_p, &kafstimod_list) { - ptimer = list_entry(_p, struct afs_timer, link); - if (time_before(timer->timo_jif, ptimer->timo_jif)) - break; - } - - list_add_tail(&timer->link, _p); /* insert before stopping point */ - - spin_unlock(&kafstimod_lock); - - wake_up(&kafstimod_sleepq); - - _leave(""); -} /* end afs_kafstimod_add_timer() */ - -/*****************************************************************************/ -/* - * dequeue a timer - * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued - */ -int afs_kafstimod_del_timer(struct afs_timer *timer) -{ - int ret = 0; - - _enter("%p", timer); - - spin_lock(&kafstimod_lock); - - if (list_empty(&timer->link)) - ret = -ENOENT; - else - list_del_init(&timer->link); - - spin_unlock(&kafstimod_lock); - - wake_up(&kafstimod_sleepq); - - _leave(" = %d", ret); - return ret; -} /* end afs_kafstimod_del_timer() */ diff --git a/fs/afs/kafstimod.h b/fs/afs/kafstimod.h deleted file mode 100644 index e312f1a..0000000 --- a/fs/afs/kafstimod.h +++ /dev/null @@ -1,49 +0,0 @@ -/* kafstimod.h: AFS timeout daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_KAFSTIMOD_H -#define _LINUX_AFS_KAFSTIMOD_H - -#include "types.h" - -struct afs_timer; - -struct afs_timer_ops { - /* called when the front of the timer queue has timed out */ - void (*timed_out)(struct afs_timer *timer); -}; - -/*****************************************************************************/ -/* - * AFS timer/timeout record - */ -struct afs_timer -{ - struct list_head link; /* link in timer queue */ - unsigned long timo_jif; /* timeout time */ - const struct afs_timer_ops *ops; /* timeout expiry function */ -}; - -static inline void afs_timer_init(struct afs_timer *timer, - const struct afs_timer_ops *ops) -{ - INIT_LIST_HEAD(&timer->link); - timer->ops = ops; -} - -extern int afs_kafstimod_start(void); -extern void afs_kafstimod_stop(void); - -extern void afs_kafstimod_add_timer(struct afs_timer *timer, - unsigned long timeout); -extern int afs_kafstimod_del_timer(struct afs_timer *timer); - -#endif /* _LINUX_AFS_KAFSTIMOD_H */ diff --git a/fs/afs/main.c b/fs/afs/main.c index f2704ba..40c2704 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c @@ -1,4 +1,4 @@ -/* main.c: AFS client file system +/* AFS client file system * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -13,43 +13,21 @@ #include #include #include #include -#include -#include -#include -#include -#include "cache.h" -#include "cell.h" -#include "server.h" -#include "fsclient.h" -#include "cmservice.h" -#include "kafstimod.h" -#include "kafsasyncd.h" #include "internal.h" -struct rxrpc_transport *afs_transport; - -static int afs_adding_peer(struct rxrpc_peer *peer); -static void afs_discarding_peer(struct rxrpc_peer *peer); - - MODULE_DESCRIPTION("AFS Client File System"); MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); +unsigned afs_debug; +module_param_named(debug, afs_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(afs_debug, "AFS debugging mask"); + static char *rootcell; module_param(rootcell, charp, 0); MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); - -static struct rxrpc_peer_ops afs_peer_ops = { - .adding = afs_adding_peer, - .discarding = afs_discarding_peer, -}; - -struct list_head afs_cb_hash_tbl[AFS_CB_HASH_COUNT]; -DEFINE_SPINLOCK(afs_cb_hash_lock); - #ifdef AFS_CACHING_SUPPORT static struct cachefs_netfs_operations afs_cache_ops = { .get_page_cookie = afs_cache_get_page_cookie, @@ -62,20 +40,63 @@ struct cachefs_netfs afs_cache_netfs = { }; #endif -/*****************************************************************************/ +struct afs_uuid afs_uuid; + +/* + * get a client UUID + */ +static int __init afs_get_client_UUID(void) +{ + struct timespec ts; + u64 uuidtime; + u16 clockseq; + int ret; + + /* read the MAC address of one of the external interfaces and construct + * a UUID from it */ + ret = afs_get_MAC_address(afs_uuid.node); + if (ret < 0) + return ret; + + getnstimeofday(&ts); + uuidtime = (u64) ts.tv_sec * 1000 * 1000 * 10; + uuidtime += ts.tv_nsec / 100; + uuidtime += AFS_UUID_TO_UNIX_TIME; + afs_uuid.time_low = uuidtime; + afs_uuid.time_mid = uuidtime >> 32; + afs_uuid.time_hi_and_version = (uuidtime >> 48) & AFS_UUID_TIMEHI_MASK; + afs_uuid.time_hi_and_version = AFS_UUID_VERSION_TIME; + + get_random_bytes(&clockseq, 2); + afs_uuid.clock_seq_low = clockseq; + afs_uuid.clock_seq_hi_and_reserved = + (clockseq >> 8) & AFS_UUID_CLOCKHI_MASK; + afs_uuid.clock_seq_hi_and_reserved = AFS_UUID_VARIANT_STD; + + _debug("AFS UUID: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + afs_uuid.time_low, + afs_uuid.time_mid, + afs_uuid.time_hi_and_version, + afs_uuid.clock_seq_hi_and_reserved, + afs_uuid.clock_seq_low, + afs_uuid.node[0], afs_uuid.node[1], afs_uuid.node[2], + afs_uuid.node[3], afs_uuid.node[4], afs_uuid.node[5]); + + return 0; +} + /* * initialise the AFS client FS module */ static int __init afs_init(void) { - int loop, ret; + int ret; printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n"); - /* initialise the callback hash table */ - spin_lock_init(&afs_cb_hash_lock); - for (loop = AFS_CB_HASH_COUNT - 1; loop >= 0; loop--) - INIT_LIST_HEAD(&afs_cb_hash_tbl[loop]); + ret = afs_get_client_UUID(); + if (ret < 0) + return ret; /* register the /proc stuff */ ret = afs_proc_init(); @@ -87,70 +108,56 @@ #ifdef AFS_CACHING_SUPPORT ret = cachefs_register_netfs(&afs_cache_netfs, &afs_cache_cell_index_def); if (ret < 0) - goto error; -#endif - -#ifdef CONFIG_KEYS_TURNED_OFF - ret = afs_key_register(); - if (ret < 0) goto error_cache; #endif /* initialise the cell DB */ ret = afs_cell_init(rootcell); if (ret < 0) - goto error_keys; + goto error_cell_init; - /* start the timeout daemon */ - ret = afs_kafstimod_start(); + /* initialise the VL update process */ + ret = afs_vlocation_update_init(); if (ret < 0) - goto error_keys; + goto error_vl_update_init; - /* start the async operation daemon */ - ret = afs_kafsasyncd_start(); - if (ret < 0) - goto error_kafstimod; + /* initialise the callback update process */ + ret = afs_callback_update_init(); /* create the RxRPC transport */ - ret = rxrpc_create_transport(7001, &afs_transport); + ret = afs_open_socket(); if (ret < 0) - goto error_kafsasyncd; - - afs_transport->peer_ops = &afs_peer_ops; + goto error_open_socket; /* register the filesystems */ ret = afs_fs_init(); if (ret < 0) - goto error_transport; + goto error_fs; return ret; - error_transport: - rxrpc_put_transport(afs_transport); - error_kafsasyncd: - afs_kafsasyncd_stop(); - error_kafstimod: - afs_kafstimod_stop(); - error_keys: -#ifdef CONFIG_KEYS_TURNED_OFF - afs_key_unregister(); - error_cache: -#endif +error_fs: + afs_close_socket(); +error_open_socket: +error_vl_update_init: +error_cell_init: #ifdef AFS_CACHING_SUPPORT cachefs_unregister_netfs(&afs_cache_netfs); - error: +error_cache: #endif + afs_callback_update_kill(); + afs_vlocation_purge(); afs_cell_purge(); afs_proc_cleanup(); printk(KERN_ERR "kAFS: failed to register: %d\n", ret); return ret; -} /* end afs_init() */ +} /* XXX late_initcall is kludgy, but the only alternative seems to create * a transport upon the first mount, which is worse. Or is it? */ late_initcall(afs_init); /* must be called after net/ to create socket */ -/*****************************************************************************/ + /* * clean up on module removal */ @@ -159,127 +166,16 @@ static void __exit afs_exit(void) printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n"); afs_fs_exit(); - rxrpc_put_transport(afs_transport); - afs_kafstimod_stop(); - afs_kafsasyncd_stop(); + afs_close_socket(); + afs_purge_servers(); + afs_callback_update_kill(); + afs_vlocation_purge(); + flush_scheduled_work(); afs_cell_purge(); -#ifdef CONFIG_KEYS_TURNED_OFF - afs_key_unregister(); -#endif #ifdef AFS_CACHING_SUPPORT cachefs_unregister_netfs(&afs_cache_netfs); #endif afs_proc_cleanup(); - -} /* end afs_exit() */ - -module_exit(afs_exit); - -/*****************************************************************************/ -/* - * notification that new peer record is being added - * - called from krxsecd - * - return an error to induce an abort - * - mustn't sleep (caller holds an rwlock) - */ -static int afs_adding_peer(struct rxrpc_peer *peer) -{ - struct afs_server *server; - int ret; - - _debug("kAFS: Adding new peer %08x\n", ntohl(peer->addr.s_addr)); - - /* determine which server the peer resides in (if any) */ - ret = afs_server_find_by_peer(peer, &server); - if (ret < 0) - return ret; /* none that we recognise, so abort */ - - _debug("Server %p{u=%d}\n", server, atomic_read(&server->usage)); - - _debug("Cell %p{u=%d}\n", - server->cell, atomic_read(&server->cell->usage)); - - /* cross-point the structs under a global lock */ - spin_lock(&afs_server_peer_lock); - peer->user = server; - server->peer = peer; - spin_unlock(&afs_server_peer_lock); - - afs_put_server(server); - - return 0; -} /* end afs_adding_peer() */ - -/*****************************************************************************/ -/* - * notification that a peer record is being discarded - * - called from krxiod or krxsecd - */ -static void afs_discarding_peer(struct rxrpc_peer *peer) -{ - struct afs_server *server; - - _enter("%p",peer); - - _debug("Discarding peer %08x (rtt=%lu.%lumS)\n", - ntohl(peer->addr.s_addr), - (long) (peer->rtt / 1000), - (long) (peer->rtt % 1000)); - - /* uncross-point the structs under a global lock */ - spin_lock(&afs_server_peer_lock); - server = peer->user; - if (server) { - peer->user = NULL; - server->peer = NULL; - } - spin_unlock(&afs_server_peer_lock); - - _leave(""); - -} /* end afs_discarding_peer() */ - -/*****************************************************************************/ -/* - * clear the dead space between task_struct and kernel stack - * - called by supplying -finstrument-functions to gcc - */ -#if 0 -void __cyg_profile_func_enter (void *this_fn, void *call_site) -__attribute__((no_instrument_function)); - -void __cyg_profile_func_enter (void *this_fn, void *call_site) -{ - asm volatile(" movl %%esp,%%edi \n" - " andl %0,%%edi \n" - " addl %1,%%edi \n" - " movl %%esp,%%ecx \n" - " subl %%edi,%%ecx \n" - " shrl $2,%%ecx \n" - " movl $0xedededed,%%eax \n" - " rep stosl \n" - : - : "i"(~(THREAD_SIZE - 1)), "i"(sizeof(struct thread_info)) - : "eax", "ecx", "edi", "memory", "cc" - ); } -void __cyg_profile_func_exit(void *this_fn, void *call_site) -__attribute__((no_instrument_function)); - -void __cyg_profile_func_exit(void *this_fn, void *call_site) -{ - asm volatile(" movl %%esp,%%edi \n" - " andl %0,%%edi \n" - " addl %1,%%edi \n" - " movl %%esp,%%ecx \n" - " subl %%edi,%%ecx \n" - " shrl $2,%%ecx \n" - " movl $0xdadadada,%%eax \n" - " rep stosl \n" - : - : "i"(~(THREAD_SIZE - 1)), "i"(sizeof(struct thread_info)) - : "eax", "ecx", "edi", "memory", "cc" - ); -} -#endif +module_exit(afs_exit); diff --git a/fs/afs/misc.c b/fs/afs/misc.c index e4fce66..cdb9792 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c @@ -1,6 +1,6 @@ -/* misc.c: miscellaneous bits +/* miscellaneous bits * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -12,19 +12,20 @@ #include #include #include -#include "errors.h" #include "internal.h" +#include "afs_fs.h" -/*****************************************************************************/ /* * convert an AFS abort code to a Linux error number */ -int afs_abort_to_error(int abortcode) +int afs_abort_to_error(u32 abort_code) { - switch (abortcode) { + switch (abort_code) { + case 13: return -EACCES; + case 30: return -EROFS; case VSALVAGE: return -EIO; case VNOVNODE: return -ENOENT; - case VNOVOL: return -ENXIO; + case VNOVOL: return -ENOMEDIUM; case VVOLEXISTS: return -EEXIST; case VNOSERVICE: return -EIO; case VOFFLINE: return -ENOENT; @@ -33,7 +34,24 @@ int afs_abort_to_error(int abortcode) case VOVERQUOTA: return -EDQUOT; case VBUSY: return -EBUSY; case VMOVED: return -ENXIO; - default: return -EIO; + case 0x2f6df0c: return -EACCES; + case 0x2f6df0f: return -EBUSY; + case 0x2f6df10: return -EEXIST; + case 0x2f6df11: return -EXDEV; + case 0x2f6df13: return -ENOTDIR; + case 0x2f6df14: return -EISDIR; + case 0x2f6df15: return -EINVAL; + case 0x2f6df1a: return -EFBIG; + case 0x2f6df1b: return -ENOSPC; + case 0x2f6df1d: return -EROFS; + case 0x2f6df1e: return -EMLINK; + case 0x2f6df20: return -EDOM; + case 0x2f6df21: return -ERANGE; + case 0x2f6df22: return -EDEADLK; + case 0x2f6df23: return -ENAMETOOLONG; + case 0x2f6df24: return -ENOLCK; + case 0x2f6df26: return -ENOTEMPTY; + case 0x2f6df78: return -EDQUOT; + default: return -EREMOTEIO; } - -} /* end afs_abort_to_error() */ +} diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 68495f0..b905ae3 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -1,4 +1,4 @@ -/* mntpt.c: mountpoint management +/* mountpoint management * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -18,10 +18,6 @@ #include #include #include #include -#include "super.h" -#include "cell.h" -#include "volume.h" -#include "vnode.h" #include "internal.h" @@ -30,6 +26,7 @@ static struct dentry *afs_mntpt_lookup(s struct nameidata *nd); static int afs_mntpt_open(struct inode *inode, struct file *file); static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd); +static void afs_mntpt_expiry_timed_out(struct work_struct *work); const struct file_operations afs_mntpt_file_operations = { .open = afs_mntpt_open, @@ -43,24 +40,19 @@ const struct inode_operations afs_mntpt_ }; static LIST_HEAD(afs_vfsmounts); +static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out); -static void afs_mntpt_expiry_timed_out(struct afs_timer *timer); +unsigned long afs_mntpt_expiry_timeout = 10 * 60; -struct afs_timer_ops afs_mntpt_expiry_timer_ops = { - .timed_out = afs_mntpt_expiry_timed_out, -}; - -struct afs_timer afs_mntpt_expiry_timer; - -unsigned long afs_mntpt_expiry_timeout = 20; - -/*****************************************************************************/ /* * check a symbolic link to see whether it actually encodes a mountpoint * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately */ -int afs_mntpt_check_symlink(struct afs_vnode *vnode) +int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key) { + struct file file = { + .private_data = key, + }; struct page *page; size_t size; char *buf; @@ -69,7 +61,7 @@ int afs_mntpt_check_symlink(struct afs_v _enter("{%u,%u}", vnode->fid.vnode, vnode->fid.unique); /* read the contents of the symlink into the pagecache */ - page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, NULL); + page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, &file); if (IS_ERR(page)) { ret = PTR_ERR(page); goto out; @@ -85,7 +77,7 @@ int afs_mntpt_check_symlink(struct afs_v /* examine the symlink's contents */ size = vnode->status.size; - _debug("symlink to %*.*s", size, (int) size, buf); + _debug("symlink to %*.*s", (int) size, (int) size, buf); if (size > 2 && (buf[0] == '%' || buf[0] == '#') && @@ -93,22 +85,20 @@ int afs_mntpt_check_symlink(struct afs_v ) { _debug("symlink is a mountpoint"); spin_lock(&vnode->lock); - vnode->flags |= AFS_VNODE_MOUNTPOINT; + set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); spin_unlock(&vnode->lock); } ret = 0; - out_free: +out_free: kunmap(page); page_cache_release(page); - out: +out: _leave(" = %d", ret); return ret; +} -} /* end afs_mntpt_check_symlink() */ - -/*****************************************************************************/ /* * no valid lookup procedure on this sort of dir */ @@ -116,7 +106,7 @@ static struct dentry *afs_mntpt_lookup(s struct dentry *dentry, struct nameidata *nd) { - kenter("%p,%p{%p{%s},%s}", + _enter("%p,%p{%p{%s},%s}", dir, dentry, dentry->d_parent, @@ -125,15 +115,14 @@ static struct dentry *afs_mntpt_lookup(s dentry->d_name.name); return ERR_PTR(-EREMOTE); -} /* end afs_mntpt_lookup() */ +} -/*****************************************************************************/ /* * no valid open procedure on this sort of dir */ static int afs_mntpt_open(struct inode *inode, struct file *file) { - kenter("%p,%p{%p{%s},%s}", + _enter("%p,%p{%p{%s},%s}", inode, file, file->f_path.dentry->d_parent, file->f_path.dentry->d_parent ? @@ -142,9 +131,8 @@ static int afs_mntpt_open(struct inode * file->f_path.dentry->d_name.name); return -EREMOTE; -} /* end afs_mntpt_open() */ +} -/*****************************************************************************/ /* * create a vfsmount to be automounted */ @@ -157,7 +145,7 @@ static struct vfsmount *afs_mntpt_do_aut char *buf, *devname = NULL, *options = NULL; int ret; - kenter("{%s}", mntpt->d_name.name); + _enter("{%s}", mntpt->d_name.name); BUG_ON(!mntpt->d_inode); @@ -201,79 +189,108 @@ static struct vfsmount *afs_mntpt_do_aut strcat(options, ",rwpath"); /* try and do the mount */ - kdebug("--- attempting mount %s -o %s ---", devname, options); + _debug("--- attempting mount %s -o %s ---", devname, options); mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options); - kdebug("--- mount result %p ---", mnt); + _debug("--- mount result %p ---", mnt); free_page((unsigned long) devname); free_page((unsigned long) options); - kleave(" = %p", mnt); + _leave(" = %p", mnt); return mnt; - error: +error: if (page) page_cache_release(page); if (devname) free_page((unsigned long) devname); if (options) free_page((unsigned long) options); - kleave(" = %d", ret); + _leave(" = %d", ret); return ERR_PTR(ret); -} /* end afs_mntpt_do_automount() */ +} -/*****************************************************************************/ /* * follow a link from a mountpoint directory, thus causing it to be mounted */ static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) { struct vfsmount *newmnt; - struct dentry *old_dentry; int err; - kenter("%p{%s},{%s:%p{%s}}", + _enter("%p{%s},{%s:%p{%s},}", dentry, dentry->d_name.name, nd->mnt->mnt_devname, dentry, nd->dentry->d_name.name); - newmnt = afs_mntpt_do_automount(dentry); + dput(nd->dentry); + nd->dentry = dget(dentry); + + newmnt = afs_mntpt_do_automount(nd->dentry); if (IS_ERR(newmnt)) { path_release(nd); return (void *)newmnt; } - old_dentry = nd->dentry; - nd->dentry = dentry; - err = do_add_mount(newmnt, nd, 0, &afs_vfsmounts); - nd->dentry = old_dentry; - - path_release(nd); - - if (!err) { - mntget(newmnt); + mntget(newmnt); + err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts); + switch (err) { + case 0: + mntput(nd->mnt); + dput(nd->dentry); nd->mnt = newmnt; - dget(newmnt->mnt_root); - nd->dentry = newmnt->mnt_root; + nd->dentry = dget(newmnt->mnt_root); + schedule_delayed_work(&afs_mntpt_expiry_timer, + afs_mntpt_expiry_timeout * HZ); + break; + case -EBUSY: + /* someone else made a mount here whilst we were busy */ + while (d_mountpoint(nd->dentry) && + follow_down(&nd->mnt, &nd->dentry)) + ; + err = 0; + default: + mntput(newmnt); + break; } - kleave(" = %d", err); + _leave(" = %d", err); return ERR_PTR(err); -} /* end afs_mntpt_follow_link() */ +} -/*****************************************************************************/ /* * handle mountpoint expiry timer going off */ -static void afs_mntpt_expiry_timed_out(struct afs_timer *timer) +static void afs_mntpt_expiry_timed_out(struct work_struct *work) { - kenter(""); + _enter(""); - mark_mounts_for_expiry(&afs_vfsmounts); + if (!list_empty(&afs_vfsmounts)) { + mark_mounts_for_expiry(&afs_vfsmounts); + schedule_delayed_work(&afs_mntpt_expiry_timer, + afs_mntpt_expiry_timeout * HZ); + } + + _leave(""); +} - afs_kafstimod_add_timer(&afs_mntpt_expiry_timer, - afs_mntpt_expiry_timeout * HZ); +/* + * kill the AFS mountpoint timer if it's still running + */ +void afs_mntpt_kill_timer(void) +{ + _enter(""); - kleave(""); -} /* end afs_mntpt_expiry_timed_out() */ + ASSERT(list_empty(&afs_vfsmounts)); + cancel_delayed_work(&afs_mntpt_expiry_timer); + flush_scheduled_work(); +} + +/* + * begin unmount by attempting to remove all automounted mountpoints we added + */ +void afs_umount_begin(struct vfsmount *vfsmnt, int flags) +{ + shrink_submounts(vfsmnt, &afs_vfsmounts); +} diff --git a/fs/afs/mount.h b/fs/afs/mount.h deleted file mode 100644 index 9d2f46e..0000000 --- a/fs/afs/mount.h +++ /dev/null @@ -1,23 +0,0 @@ -/* mount.h: mount parameters - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_MOUNT_H -#define _LINUX_AFS_MOUNT_H - -struct afs_mountdata { - const char *volume; /* name of volume */ - const char *cell; /* name of cell containing volume */ - const char *cache; /* name of cache block device */ - size_t nservers; /* number of server addresses listed */ - uint32_t servers[10]; /* IP addresses of servers in this cell */ -}; - -#endif /* _LINUX_AFS_MOUNT_H */ diff --git a/fs/afs/proc.c b/fs/afs/proc.c index ae6b85b..d5601f6 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -1,4 +1,4 @@ -/* proc.c: /proc interface for AFS +/* /proc interface for AFS * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -13,8 +13,6 @@ #include #include #include #include -#include "cell.h" -#include "volume.h" #include #include "internal.h" @@ -130,7 +128,6 @@ static const struct file_operations afs_ .release = afs_proc_cell_servers_release, }; -/*****************************************************************************/ /* * initialise the /proc/fs/afs/ directory */ @@ -142,47 +139,43 @@ int afs_proc_init(void) proc_afs = proc_mkdir("fs/afs", NULL); if (!proc_afs) - goto error; + goto error_dir; proc_afs->owner = THIS_MODULE; p = create_proc_entry("cells", 0, proc_afs); if (!p) - goto error_proc; + goto error_cells; p->proc_fops = &afs_proc_cells_fops; p->owner = THIS_MODULE; p = create_proc_entry("rootcell", 0, proc_afs); if (!p) - goto error_cells; + goto error_rootcell; p->proc_fops = &afs_proc_rootcell_fops; p->owner = THIS_MODULE; _leave(" = 0"); return 0; - error_cells: +error_rootcell: remove_proc_entry("cells", proc_afs); - error_proc: +error_cells: remove_proc_entry("fs/afs", NULL); - error: +error_dir: _leave(" = -ENOMEM"); return -ENOMEM; +} -} /* end afs_proc_init() */ - -/*****************************************************************************/ /* * clean up the /proc/fs/afs/ directory */ void afs_proc_cleanup(void) { + remove_proc_entry("rootcell", proc_afs); remove_proc_entry("cells", proc_afs); - remove_proc_entry("fs/afs", NULL); +} -} /* end afs_proc_cleanup() */ - -/*****************************************************************************/ /* * open "/proc/fs/afs/cells" which provides a summary of extant cells */ @@ -199,9 +192,8 @@ static int afs_proc_cells_open(struct in m->private = PDE(inode)->data; return 0; -} /* end afs_proc_cells_open() */ +} -/*****************************************************************************/ /* * set up the iterator to start reading from the cells list and return the * first item @@ -225,9 +217,8 @@ static void *afs_proc_cells_start(struct break; return _p != &afs_proc_cells ? _p : NULL; -} /* end afs_proc_cells_start() */ +} -/*****************************************************************************/ /* * move to next cell in cells list */ @@ -241,19 +232,16 @@ static void *afs_proc_cells_next(struct _p = v == (void *) 1 ? afs_proc_cells.next : _p->next; return _p != &afs_proc_cells ? _p : NULL; -} /* end afs_proc_cells_next() */ +} -/*****************************************************************************/ /* * clean up after reading from the cells list */ static void afs_proc_cells_stop(struct seq_file *p, void *v) { up_read(&afs_proc_cells_sem); +} -} /* end afs_proc_cells_stop() */ - -/*****************************************************************************/ /* * display a header line followed by a load of cell lines */ @@ -261,19 +249,18 @@ static int afs_proc_cells_show(struct se { struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link); - /* display header on line 1 */ if (v == (void *) 1) { + /* display header on line 1 */ seq_puts(m, "USE NAME\n"); return 0; } /* display one cell per line on subsequent lines */ - seq_printf(m, "%3d %s\n", atomic_read(&cell->usage), cell->name); - + seq_printf(m, "%3d %s\n", + atomic_read(&cell->usage), cell->name); return 0; -} /* end afs_proc_cells_show() */ +} -/*****************************************************************************/ /* * handle writes to /proc/fs/afs/cells * - to add cells: echo "add [:][:]" @@ -326,30 +313,32 @@ static ssize_t afs_proc_cells_write(stru if (strcmp(kbuf, "add") == 0) { struct afs_cell *cell; - ret = afs_cell_create(name, args, &cell); - if (ret < 0) + + cell = afs_cell_create(name, args); + if (IS_ERR(cell)) { + ret = PTR_ERR(cell); goto done; + } + afs_put_cell(cell); printk("kAFS: Added new cell '%s'\n", name); - } - else { + } else { goto inval; } ret = size; - done: +done: kfree(kbuf); _leave(" = %d", ret); return ret; - inval: +inval: ret = -EINVAL; printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); goto done; -} /* end afs_proc_cells_write() */ +} -/*****************************************************************************/ /* * Stubs for /proc/fs/afs/rootcell */ @@ -369,7 +358,6 @@ static ssize_t afs_proc_rootcell_read(st return 0; } -/*****************************************************************************/ /* * handle writes to /proc/fs/afs/rootcell * - to initialize rootcell: echo "cell.name:192.168.231.14" @@ -407,14 +395,13 @@ static ssize_t afs_proc_rootcell_write(s if (ret >= 0) ret = size; /* consume everything, always */ - infault: +infault: kfree(kbuf); - nomem: +nomem: _leave(" = %d", ret); return ret; -} /* end afs_proc_rootcell_write() */ +} -/*****************************************************************************/ /* * initialise /proc/fs/afs// */ @@ -426,25 +413,25 @@ int afs_proc_cell_setup(struct afs_cell cell->proc_dir = proc_mkdir(cell->name, proc_afs); if (!cell->proc_dir) - return -ENOMEM; + goto error_dir; p = create_proc_entry("servers", 0, cell->proc_dir); if (!p) - goto error_proc; + goto error_servers; p->proc_fops = &afs_proc_cell_servers_fops; p->owner = THIS_MODULE; p->data = cell; p = create_proc_entry("vlservers", 0, cell->proc_dir); if (!p) - goto error_servers; + goto error_vlservers; p->proc_fops = &afs_proc_cell_vlservers_fops; p->owner = THIS_MODULE; p->data = cell; p = create_proc_entry("volumes", 0, cell->proc_dir); if (!p) - goto error_vlservers; + goto error_volumes; p->proc_fops = &afs_proc_cell_volumes_fops; p->owner = THIS_MODULE; p->data = cell; @@ -452,17 +439,17 @@ int afs_proc_cell_setup(struct afs_cell _leave(" = 0"); return 0; - error_vlservers: +error_volumes: remove_proc_entry("vlservers", cell->proc_dir); - error_servers: +error_vlservers: remove_proc_entry("servers", cell->proc_dir); - error_proc: +error_servers: remove_proc_entry(cell->name, proc_afs); +error_dir: _leave(" = -ENOMEM"); return -ENOMEM; -} /* end afs_proc_cell_setup() */ +} -/*****************************************************************************/ /* * remove /proc/fs/afs// */ @@ -476,9 +463,8 @@ void afs_proc_cell_remove(struct afs_cel remove_proc_entry(cell->name, proc_afs); _leave(""); -} /* end afs_proc_cell_remove() */ +} -/*****************************************************************************/ /* * open "/proc/fs/afs//volumes" which provides a summary of extant cells */ @@ -488,7 +474,7 @@ static int afs_proc_cell_volumes_open(st struct seq_file *m; int ret; - cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data); + cell = PDE(inode)->data; if (!cell) return -ENOENT; @@ -500,25 +486,16 @@ static int afs_proc_cell_volumes_open(st m->private = cell; return 0; -} /* end afs_proc_cell_volumes_open() */ +} -/*****************************************************************************/ /* * close the file and release the ref to the cell */ static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file) { - struct afs_cell *cell = PDE(inode)->data; - int ret; - - ret = seq_release(inode,file); - - afs_put_cell(cell); - - return ret; -} /* end afs_proc_cell_volumes_release() */ + return seq_release(inode, file); +} -/*****************************************************************************/ /* * set up the iterator to start reading from the cells list and return the * first item @@ -545,9 +522,8 @@ static void *afs_proc_cell_volumes_start break; return _p != &cell->vl_list ? _p : NULL; -} /* end afs_proc_cell_volumes_start() */ +} -/*****************************************************************************/ /* * move to next cell in cells list */ @@ -562,12 +538,11 @@ static void *afs_proc_cell_volumes_next( (*_pos)++; _p = v; - _p = v == (void *) 1 ? cell->vl_list.next : _p->next; + _p = (v == (void *) 1) ? cell->vl_list.next : _p->next; - return _p != &cell->vl_list ? _p : NULL; -} /* end afs_proc_cell_volumes_next() */ + return (_p != &cell->vl_list) ? _p : NULL; +} -/*****************************************************************************/ /* * clean up after reading from the cells list */ @@ -576,10 +551,18 @@ static void afs_proc_cell_volumes_stop(s struct afs_cell *cell = p->private; up_read(&cell->vl_sem); +} -} /* end afs_proc_cell_volumes_stop() */ +const char afs_vlocation_states[][4] = { + [AFS_VL_NEW] = "New", + [AFS_VL_CREATING] = "Crt", + [AFS_VL_VALID] = "Val", + [AFS_VL_NO_VOLUME] = "NoV", + [AFS_VL_UPDATING] = "Upd", + [AFS_VL_VOLUME_DELETED] = "Del", + [AFS_VL_UNCERTAIN] = "Unc", +}; -/*****************************************************************************/ /* * display a header line followed by a load of volume lines */ @@ -590,23 +573,22 @@ static int afs_proc_cell_volumes_show(st /* display header on line 1 */ if (v == (void *) 1) { - seq_puts(m, "USE VLID[0] VLID[1] VLID[2] NAME\n"); + seq_puts(m, "USE STT VLID[0] VLID[1] VLID[2] NAME\n"); return 0; } /* display one cell per line on subsequent lines */ - seq_printf(m, "%3d %08x %08x %08x %s\n", + seq_printf(m, "%3d %s %08x %08x %08x %s\n", atomic_read(&vlocation->usage), + afs_vlocation_states[vlocation->state], vlocation->vldb.vid[0], vlocation->vldb.vid[1], vlocation->vldb.vid[2], - vlocation->vldb.name - ); + vlocation->vldb.name); return 0; -} /* end afs_proc_cell_volumes_show() */ +} -/*****************************************************************************/ /* * open "/proc/fs/afs//vlservers" which provides a list of volume * location server @@ -617,11 +599,11 @@ static int afs_proc_cell_vlservers_open( struct seq_file *m; int ret; - cell = afs_get_cell_maybe((struct afs_cell**)&PDE(inode)->data); + cell = PDE(inode)->data; if (!cell) return -ENOENT; - ret = seq_open(file,&afs_proc_cell_vlservers_ops); + ret = seq_open(file, &afs_proc_cell_vlservers_ops); if (ret<0) return ret; @@ -629,26 +611,17 @@ static int afs_proc_cell_vlservers_open( m->private = cell; return 0; -} /* end afs_proc_cell_vlservers_open() */ +} -/*****************************************************************************/ /* * close the file and release the ref to the cell */ static int afs_proc_cell_vlservers_release(struct inode *inode, struct file *file) { - struct afs_cell *cell = PDE(inode)->data; - int ret; - - ret = seq_release(inode,file); - - afs_put_cell(cell); - - return ret; -} /* end afs_proc_cell_vlservers_release() */ + return seq_release(inode, file); +} -/*****************************************************************************/ /* * set up the iterator to start reading from the cells list and return the * first item @@ -672,9 +645,8 @@ static void *afs_proc_cell_vlservers_sta return NULL; return &cell->vl_addrs[pos]; -} /* end afs_proc_cell_vlservers_start() */ +} -/*****************************************************************************/ /* * move to next cell in cells list */ @@ -692,9 +664,8 @@ static void *afs_proc_cell_vlservers_nex return NULL; return &cell->vl_addrs[pos]; -} /* end afs_proc_cell_vlservers_next() */ +} -/*****************************************************************************/ /* * clean up after reading from the cells list */ @@ -703,10 +674,8 @@ static void afs_proc_cell_vlservers_stop struct afs_cell *cell = p->private; up_read(&cell->vl_sem); +} -} /* end afs_proc_cell_vlservers_stop() */ - -/*****************************************************************************/ /* * display a header line followed by a load of volume lines */ @@ -722,11 +691,9 @@ static int afs_proc_cell_vlservers_show( /* display one cell per line on subsequent lines */ seq_printf(m, "%u.%u.%u.%u\n", NIPQUAD(addr->s_addr)); - return 0; -} /* end afs_proc_cell_vlservers_show() */ +} -/*****************************************************************************/ /* * open "/proc/fs/afs//servers" which provides a summary of active * servers @@ -737,7 +704,7 @@ static int afs_proc_cell_servers_open(st struct seq_file *m; int ret; - cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data); + cell = PDE(inode)->data; if (!cell) return -ENOENT; @@ -747,34 +714,24 @@ static int afs_proc_cell_servers_open(st m = file->private_data; m->private = cell; - return 0; -} /* end afs_proc_cell_servers_open() */ +} -/*****************************************************************************/ /* * close the file and release the ref to the cell */ static int afs_proc_cell_servers_release(struct inode *inode, struct file *file) { - struct afs_cell *cell = PDE(inode)->data; - int ret; - - ret = seq_release(inode, file); - - afs_put_cell(cell); - - return ret; -} /* end afs_proc_cell_servers_release() */ + return seq_release(inode, file); +} -/*****************************************************************************/ /* * set up the iterator to start reading from the cells list and return the * first item */ static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos) - __acquires(m->private->sv_lock) + __acquires(m->private->servers_lock) { struct list_head *_p; struct afs_cell *cell = m->private; @@ -783,7 +740,7 @@ static void *afs_proc_cell_servers_start _enter("cell=%p pos=%Ld", cell, *_pos); /* lock the list against modification */ - read_lock(&cell->sv_lock); + read_lock(&cell->servers_lock); /* allow for the header line */ if (!pos) @@ -791,14 +748,13 @@ static void *afs_proc_cell_servers_start pos--; /* find the n'th element in the list */ - list_for_each(_p, &cell->sv_list) + list_for_each(_p, &cell->servers) if (!pos--) break; - return _p != &cell->sv_list ? _p : NULL; -} /* end afs_proc_cell_servers_start() */ + return _p != &cell->servers ? _p : NULL; +} -/*****************************************************************************/ /* * move to next cell in cells list */ @@ -813,25 +769,22 @@ static void *afs_proc_cell_servers_next( (*_pos)++; _p = v; - _p = v == (void *) 1 ? cell->sv_list.next : _p->next; + _p = v == (void *) 1 ? cell->servers.next : _p->next; - return _p != &cell->sv_list ? _p : NULL; -} /* end afs_proc_cell_servers_next() */ + return _p != &cell->servers ? _p : NULL; +} -/*****************************************************************************/ /* * clean up after reading from the cells list */ static void afs_proc_cell_servers_stop(struct seq_file *p, void *v) - __releases(p->private->sv_lock) + __releases(p->private->servers_lock) { struct afs_cell *cell = p->private; - read_unlock(&cell->sv_lock); - -} /* end afs_proc_cell_servers_stop() */ + read_unlock(&cell->servers_lock); +} -/*****************************************************************************/ /* * display a header line followed by a load of volume lines */ @@ -849,10 +802,7 @@ static int afs_proc_cell_servers_show(st /* display one cell per line on subsequent lines */ sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(server->addr)); seq_printf(m, "%3d %-15.15s %5d\n", - atomic_read(&server->usage), - ipaddr, - server->fs_state - ); + atomic_read(&server->usage), ipaddr, server->fs_state); return 0; -} /* end afs_proc_cell_servers_show() */ +} diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c new file mode 100644 index 0000000..222c1a3 --- /dev/null +++ b/fs/afs/rxrpc.c @@ -0,0 +1,782 @@ +/* Maintain an RxRPC server socket to do AFS communications through + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include "internal.h" +#include "afs_cm.h" + +static struct socket *afs_socket; /* my RxRPC socket */ +static struct workqueue_struct *afs_async_calls; +static atomic_t afs_outstanding_calls; +static atomic_t afs_outstanding_skbs; + +static void afs_wake_up_call_waiter(struct afs_call *); +static int afs_wait_for_call_to_complete(struct afs_call *); +static void afs_wake_up_async_call(struct afs_call *); +static int afs_dont_wait_for_call_to_complete(struct afs_call *); +static void afs_process_async_call(struct work_struct *); +static void afs_rx_interceptor(struct sock *, unsigned long, struct sk_buff *); +static int afs_deliver_cm_op_id(struct afs_call *, struct sk_buff *, bool); + +/* synchronous call management */ +const struct afs_wait_mode afs_sync_call = { + .rx_wakeup = afs_wake_up_call_waiter, + .wait = afs_wait_for_call_to_complete, +}; + +/* asynchronous call management */ +const struct afs_wait_mode afs_async_call = { + .rx_wakeup = afs_wake_up_async_call, + .wait = afs_dont_wait_for_call_to_complete, +}; + +/* asynchronous incoming call management */ +static const struct afs_wait_mode afs_async_incoming_call = { + .rx_wakeup = afs_wake_up_async_call, +}; + +/* asynchronous incoming call initial processing */ +static const struct afs_call_type afs_RXCMxxxx = { + .name = "CB.xxxx", + .deliver = afs_deliver_cm_op_id, + .abort_to_error = afs_abort_to_error, +}; + +static void afs_collect_incoming_call(struct work_struct *); + +static struct sk_buff_head afs_incoming_calls; +static DECLARE_WORK(afs_collect_incoming_call_work, afs_collect_incoming_call); + +/* + * open an RxRPC socket and bind it to be a server for callback notifications + * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT + */ +int afs_open_socket(void) +{ + struct sockaddr_rxrpc srx; + struct socket *socket; + int ret; + + _enter(""); + + skb_queue_head_init(&afs_incoming_calls); + + afs_async_calls = create_singlethread_workqueue("kafsd"); + if (!afs_async_calls) { + _leave(" = -ENOMEM [wq]"); + return -ENOMEM; + } + + ret = sock_create_kern(AF_RXRPC, SOCK_DGRAM, PF_INET, &socket); + if (ret < 0) { + destroy_workqueue(afs_async_calls); + _leave(" = %d [socket]", ret); + return ret; + } + + socket->sk->sk_allocation = GFP_NOFS; + + /* bind the callback manager's address to make this a server socket */ + srx.srx_family = AF_RXRPC; + srx.srx_service = CM_SERVICE; + srx.transport_type = SOCK_DGRAM; + srx.transport_len = sizeof(srx.transport.sin); + srx.transport.sin.sin_family = AF_INET; + srx.transport.sin.sin_port = htons(AFS_CM_PORT); + memset(&srx.transport.sin.sin_addr, 0, + sizeof(srx.transport.sin.sin_addr)); + + ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx)); + if (ret < 0) { + sock_release(socket); + _leave(" = %d [bind]", ret); + return ret; + } + + rxrpc_kernel_intercept_rx_messages(socket, afs_rx_interceptor); + + afs_socket = socket; + _leave(" = 0"); + return 0; +} + +/* + * close the RxRPC socket AFS was using + */ +void afs_close_socket(void) +{ + _enter(""); + + sock_release(afs_socket); + + _debug("dework"); + destroy_workqueue(afs_async_calls); + + ASSERTCMP(atomic_read(&afs_outstanding_skbs), ==, 0); + ASSERTCMP(atomic_read(&afs_outstanding_calls), ==, 0); + _leave(""); +} + +/* + * note that the data in a socket buffer is now delivered and that the buffer + * should be freed + */ +static void afs_data_delivered(struct sk_buff *skb) +{ + if (!skb) { + _debug("DLVR NULL [%d]", atomic_read(&afs_outstanding_skbs)); + dump_stack(); + } else { + _debug("DLVR %p{%u} [%d]", + skb, skb->mark, atomic_read(&afs_outstanding_skbs)); + if (atomic_dec_return(&afs_outstanding_skbs) == -1) + BUG(); + rxrpc_kernel_data_delivered(skb); + } +} + +/* + * free a socket buffer + */ +static void afs_free_skb(struct sk_buff *skb) +{ + if (!skb) { + _debug("FREE NULL [%d]", atomic_read(&afs_outstanding_skbs)); + dump_stack(); + } else { + _debug("FREE %p{%u} [%d]", + skb, skb->mark, atomic_read(&afs_outstanding_skbs)); + if (atomic_dec_return(&afs_outstanding_skbs) == -1) + BUG(); + rxrpc_kernel_free_skb(skb); + } +} + +/* + * free a call + */ +static void afs_free_call(struct afs_call *call) +{ + _debug("DONE %p{%s} [%d]", + call, call->type->name, atomic_read(&afs_outstanding_calls)); + if (atomic_dec_return(&afs_outstanding_calls) == -1) + BUG(); + + ASSERTCMP(call->rxcall, ==, NULL); + ASSERT(!work_pending(&call->async_work)); + ASSERT(skb_queue_empty(&call->rx_queue)); + ASSERT(call->type->name != NULL); + + kfree(call->request); + kfree(call); +} + +/* + * allocate a call with flat request and reply buffers + */ +struct afs_call *afs_alloc_flat_call(const struct afs_call_type *type, + size_t request_size, size_t reply_size) +{ + struct afs_call *call; + + call = kzalloc(sizeof(*call), GFP_NOFS); + if (!call) + goto nomem_call; + + _debug("CALL %p{%s} [%d]", + call, type->name, atomic_read(&afs_outstanding_calls)); + atomic_inc(&afs_outstanding_calls); + + call->type = type; + call->request_size = request_size; + call->reply_max = reply_size; + + if (request_size) { + call->request = kmalloc(request_size, GFP_NOFS); + if (!call->request) + goto nomem_free; + } + + if (reply_size) { + call->buffer = kmalloc(reply_size, GFP_NOFS); + if (!call->buffer) + goto nomem_free; + } + + init_waitqueue_head(&call->waitq); + skb_queue_head_init(&call->rx_queue); + return call; + +nomem_free: + afs_free_call(call); +nomem_call: + return NULL; +} + +/* + * clean up a call with flat buffer + */ +void afs_flat_call_destructor(struct afs_call *call) +{ + _enter(""); + + kfree(call->request); + call->request = NULL; + kfree(call->buffer); + call->buffer = NULL; +} + +/* + * initiate a call + */ +int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, + const struct afs_wait_mode *wait_mode) +{ + struct sockaddr_rxrpc srx; + struct rxrpc_call *rxcall; + struct msghdr msg; + struct kvec iov[1]; + int ret; + + _enter("%x,{%d},", addr->s_addr, ntohs(call->port)); + + ASSERT(call->type != NULL); + ASSERT(call->type->name != NULL); + + _debug("MAKE %p{%s} [%d]", + call, call->type->name, atomic_read(&afs_outstanding_calls)); + + call->wait_mode = wait_mode; + INIT_WORK(&call->async_work, afs_process_async_call); + + memset(&srx, 0, sizeof(srx)); + srx.srx_family = AF_RXRPC; + srx.srx_service = call->service_id; + srx.transport_type = SOCK_DGRAM; + srx.transport_len = sizeof(srx.transport.sin); + srx.transport.sin.sin_family = AF_INET; + srx.transport.sin.sin_port = call->port; + memcpy(&srx.transport.sin.sin_addr, addr, 4); + + /* create a call */ + rxcall = rxrpc_kernel_begin_call(afs_socket, &srx, call->key, + (unsigned long) call, gfp); + call->key = NULL; + if (IS_ERR(rxcall)) { + ret = PTR_ERR(rxcall); + goto error_kill_call; + } + + call->rxcall = rxcall; + + /* send the request */ + iov[0].iov_base = call->request; + iov[0].iov_len = call->request_size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = (struct iovec *) iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + /* have to change the state *before* sending the last packet as RxRPC + * might give us the reply before it returns from sending the + * request */ + call->state = AFS_CALL_AWAIT_REPLY; + ret = rxrpc_kernel_send_data(rxcall, &msg, call->request_size); + if (ret < 0) + goto error_do_abort; + + /* at this point, an async call may no longer exist as it may have + * already completed */ + return wait_mode->wait(call); + +error_do_abort: + rxrpc_kernel_abort_call(rxcall, RX_USER_ABORT); + rxrpc_kernel_end_call(rxcall); + call->rxcall = NULL; +error_kill_call: + call->type->destructor(call); + afs_free_call(call); + _leave(" = %d", ret); + return ret; +} + +/* + * handles intercepted messages that were arriving in the socket's Rx queue + * - called with the socket receive queue lock held to ensure message ordering + * - called with softirqs disabled + */ +static void afs_rx_interceptor(struct sock *sk, unsigned long user_call_ID, + struct sk_buff *skb) +{ + struct afs_call *call = (struct afs_call *) user_call_ID; + + _enter("%p,,%u", call, skb->mark); + + _debug("ICPT %p{%u} [%d]", + skb, skb->mark, atomic_read(&afs_outstanding_skbs)); + + ASSERTCMP(sk, ==, afs_socket->sk); + atomic_inc(&afs_outstanding_skbs); + + if (!call) { + /* its an incoming call for our callback service */ + skb_queue_tail(&afs_incoming_calls, skb); + schedule_work(&afs_collect_incoming_call_work); + } else { + /* route the messages directly to the appropriate call */ + skb_queue_tail(&call->rx_queue, skb); + call->wait_mode->rx_wakeup(call); + } + + _leave(""); +} + +/* + * deliver messages to a call + */ +static void afs_deliver_to_call(struct afs_call *call) +{ + struct sk_buff *skb; + bool last; + u32 abort_code; + int ret; + + _enter(""); + + while ((call->state == AFS_CALL_AWAIT_REPLY || + call->state == AFS_CALL_AWAIT_OP_ID || + call->state == AFS_CALL_AWAIT_REQUEST || + call->state == AFS_CALL_AWAIT_ACK) && + (skb = skb_dequeue(&call->rx_queue))) { + switch (skb->mark) { + case RXRPC_SKB_MARK_DATA: + _debug("Rcv DATA"); + last = rxrpc_kernel_is_data_last(skb); + ret = call->type->deliver(call, skb, last); + switch (ret) { + case 0: + if (last && + call->state == AFS_CALL_AWAIT_REPLY) + call->state = AFS_CALL_COMPLETE; + break; + case -ENOTCONN: + abort_code = RX_CALL_DEAD; + goto do_abort; + case -ENOTSUPP: + abort_code = RX_INVALID_OPERATION; + goto do_abort; + default: + abort_code = RXGEN_CC_UNMARSHAL; + if (call->state != AFS_CALL_AWAIT_REPLY) + abort_code = RXGEN_SS_UNMARSHAL; + do_abort: + rxrpc_kernel_abort_call(call->rxcall, + abort_code); + call->error = ret; + call->state = AFS_CALL_ERROR; + break; + } + afs_data_delivered(skb); + skb = NULL; + continue; + case RXRPC_SKB_MARK_FINAL_ACK: + _debug("Rcv ACK"); + call->state = AFS_CALL_COMPLETE; + break; + case RXRPC_SKB_MARK_BUSY: + _debug("Rcv BUSY"); + call->error = -EBUSY; + call->state = AFS_CALL_BUSY; + break; + case RXRPC_SKB_MARK_REMOTE_ABORT: + abort_code = rxrpc_kernel_get_abort_code(skb); + call->error = call->type->abort_to_error(abort_code); + call->state = AFS_CALL_ABORTED; + _debug("Rcv ABORT %u -> %d", abort_code, call->error); + break; + case RXRPC_SKB_MARK_NET_ERROR: + call->error = -rxrpc_kernel_get_error_number(skb); + call->state = AFS_CALL_ERROR; + _debug("Rcv NET ERROR %d", call->error); + break; + case RXRPC_SKB_MARK_LOCAL_ERROR: + call->error = -rxrpc_kernel_get_error_number(skb); + call->state = AFS_CALL_ERROR; + _debug("Rcv LOCAL ERROR %d", call->error); + break; + default: + BUG(); + break; + } + + afs_free_skb(skb); + } + + /* make sure the queue is empty if the call is done with (we might have + * aborted the call early because of an unmarshalling error) */ + if (call->state >= AFS_CALL_COMPLETE) { + while ((skb = skb_dequeue(&call->rx_queue))) + afs_free_skb(skb); + if (call->incoming) { + rxrpc_kernel_end_call(call->rxcall); + call->rxcall = NULL; + call->type->destructor(call); + afs_free_call(call); + } + } + + _leave(""); +} + +/* + * wait synchronously for a call to complete + */ +static int afs_wait_for_call_to_complete(struct afs_call *call) +{ + struct sk_buff *skb; + int ret; + + DECLARE_WAITQUEUE(myself, current); + + _enter(""); + + add_wait_queue(&call->waitq, &myself); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + + /* deliver any messages that are in the queue */ + if (!skb_queue_empty(&call->rx_queue)) { + __set_current_state(TASK_RUNNING); + afs_deliver_to_call(call); + continue; + } + + ret = call->error; + if (call->state >= AFS_CALL_COMPLETE) + break; + ret = -EINTR; + if (signal_pending(current)) + break; + schedule(); + } + + remove_wait_queue(&call->waitq, &myself); + __set_current_state(TASK_RUNNING); + + /* kill the call */ + if (call->state < AFS_CALL_COMPLETE) { + _debug("call incomplete"); + rxrpc_kernel_abort_call(call->rxcall, RX_CALL_DEAD); + while ((skb = skb_dequeue(&call->rx_queue))) + afs_free_skb(skb); + } + + _debug("call complete"); + rxrpc_kernel_end_call(call->rxcall); + call->rxcall = NULL; + call->type->destructor(call); + afs_free_call(call); + _leave(" = %d", ret); + return ret; +} + +/* + * wake up a waiting call + */ +static void afs_wake_up_call_waiter(struct afs_call *call) +{ + wake_up(&call->waitq); +} + +/* + * wake up an asynchronous call + */ +static void afs_wake_up_async_call(struct afs_call *call) +{ + _enter(""); + queue_work(afs_async_calls, &call->async_work); +} + +/* + * put a call into asynchronous mode + * - mustn't touch the call descriptor as the call my have completed by the + * time we get here + */ +static int afs_dont_wait_for_call_to_complete(struct afs_call *call) +{ + _enter(""); + return -EINPROGRESS; +} + +/* + * delete an asynchronous call + */ +static void afs_delete_async_call(struct work_struct *work) +{ + struct afs_call *call = + container_of(work, struct afs_call, async_work); + + _enter(""); + + afs_free_call(call); + + _leave(""); +} + +/* + * perform processing on an asynchronous call + * - on a multiple-thread workqueue this work item may try to run on several + * CPUs at the same time + */ +static void afs_process_async_call(struct work_struct *work) +{ + struct afs_call *call = + container_of(work, struct afs_call, async_work); + + _enter(""); + + if (!skb_queue_empty(&call->rx_queue)) + afs_deliver_to_call(call); + + if (call->state >= AFS_CALL_COMPLETE && call->wait_mode) { + if (call->wait_mode->async_complete) + call->wait_mode->async_complete(call->reply, + call->error); + call->reply = NULL; + + /* kill the call */ + rxrpc_kernel_end_call(call->rxcall); + call->rxcall = NULL; + if (call->type->destructor) + call->type->destructor(call); + + /* we can't just delete the call because the work item may be + * queued */ + PREPARE_WORK(&call->async_work, afs_delete_async_call); + queue_work(afs_async_calls, &call->async_work); + } + + _leave(""); +} + +/* + * empty a socket buffer into a flat reply buffer + */ +void afs_transfer_reply(struct afs_call *call, struct sk_buff *skb) +{ + size_t len = skb->len; + + if (skb_copy_bits(skb, 0, call->buffer + call->reply_size, len) < 0) + BUG(); + call->reply_size += len; +} + +/* + * accept the backlog of incoming calls + */ +static void afs_collect_incoming_call(struct work_struct *work) +{ + struct rxrpc_call *rxcall; + struct afs_call *call = NULL; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&afs_incoming_calls))) { + _debug("new call"); + + /* don't need the notification */ + afs_free_skb(skb); + + if (!call) { + call = kzalloc(sizeof(struct afs_call), GFP_KERNEL); + if (!call) { + rxrpc_kernel_reject_call(afs_socket); + return; + } + + INIT_WORK(&call->async_work, afs_process_async_call); + call->wait_mode = &afs_async_incoming_call; + call->type = &afs_RXCMxxxx; + init_waitqueue_head(&call->waitq); + skb_queue_head_init(&call->rx_queue); + call->state = AFS_CALL_AWAIT_OP_ID; + + _debug("CALL %p{%s} [%d]", + call, call->type->name, + atomic_read(&afs_outstanding_calls)); + atomic_inc(&afs_outstanding_calls); + } + + rxcall = rxrpc_kernel_accept_call(afs_socket, + (unsigned long) call); + if (!IS_ERR(rxcall)) { + call->rxcall = rxcall; + call = NULL; + } + } + + if (call) + afs_free_call(call); +} + +/* + * grab the operation ID from an incoming cache manager call + */ +static int afs_deliver_cm_op_id(struct afs_call *call, struct sk_buff *skb, + bool last) +{ + size_t len = skb->len; + void *oibuf = (void *) &call->operation_ID; + + _enter("{%u},{%zu},%d", call->offset, len, last); + + ASSERTCMP(call->offset, <, 4); + + /* the operation ID forms the first four bytes of the request data */ + len = min_t(size_t, len, 4 - call->offset); + if (skb_copy_bits(skb, 0, oibuf + call->offset, len) < 0) + BUG(); + if (!pskb_pull(skb, len)) + BUG(); + call->offset += len; + + if (call->offset < 4) { + if (last) { + _leave(" = -EBADMSG [op ID short]"); + return -EBADMSG; + } + _leave(" = 0 [incomplete]"); + return 0; + } + + call->state = AFS_CALL_AWAIT_REQUEST; + + /* ask the cache manager to route the call (it'll change the call type + * if successful) */ + if (!afs_cm_incoming_call(call)) + return -ENOTSUPP; + + /* pass responsibility for the remainer of this message off to the + * cache manager op */ + return call->type->deliver(call, skb, last); +} + +/* + * send an empty reply + */ +void afs_send_empty_reply(struct afs_call *call) +{ + struct msghdr msg; + struct iovec iov[1]; + + _enter(""); + + iov[0].iov_base = NULL; + iov[0].iov_len = 0; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + call->state = AFS_CALL_AWAIT_ACK; + switch (rxrpc_kernel_send_data(call->rxcall, &msg, 0)) { + case 0: + _leave(" [replied]"); + return; + + case -ENOMEM: + _debug("oom"); + rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT); + default: + rxrpc_kernel_end_call(call->rxcall); + call->rxcall = NULL; + call->type->destructor(call); + afs_free_call(call); + _leave(" [error]"); + return; + } +} + +/* + * send a simple reply + */ +void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len) +{ + struct msghdr msg; + struct iovec iov[1]; + + _enter(""); + + iov[0].iov_base = (void *) buf; + iov[0].iov_len = len; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + call->state = AFS_CALL_AWAIT_ACK; + switch (rxrpc_kernel_send_data(call->rxcall, &msg, len)) { + case 0: + _leave(" [replied]"); + return; + + case -ENOMEM: + _debug("oom"); + rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT); + default: + rxrpc_kernel_end_call(call->rxcall); + call->rxcall = NULL; + call->type->destructor(call); + afs_free_call(call); + _leave(" [error]"); + return; + } +} + +/* + * extract a piece of data from the received data socket buffers + */ +int afs_extract_data(struct afs_call *call, struct sk_buff *skb, + bool last, void *buf, size_t count) +{ + size_t len = skb->len; + + _enter("{%u},{%zu},%d,,%zu", call->offset, len, last, count); + + ASSERTCMP(call->offset, <, count); + + len = min_t(size_t, len, count - call->offset); + if (skb_copy_bits(skb, 0, buf + call->offset, len) < 0 || + !pskb_pull(skb, len)) + BUG(); + call->offset += len; + + if (call->offset < count) { + if (last) { + _leave(" = -EBADMSG [%d < %zu]", call->offset, count); + return -EBADMSG; + } + _leave(" = -EAGAIN"); + return -EAGAIN; + } + return 0; +} diff --git a/fs/afs/security.c b/fs/afs/security.c new file mode 100644 index 0000000..f9f424d --- /dev/null +++ b/fs/afs/security.c @@ -0,0 +1,356 @@ +/* AFS security handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include "internal.h" + +/* + * get a key + */ +struct key *afs_request_key(struct afs_cell *cell) +{ + struct key *key; + + _enter("{%x}", key_serial(cell->anonymous_key)); + + _debug("key %s", cell->anonymous_key->description); + key = request_key(&key_type_rxrpc, cell->anonymous_key->description, + NULL); + if (IS_ERR(key)) { + if (PTR_ERR(key) != -ENOKEY) { + _leave(" = %ld", PTR_ERR(key)); + return key; + } + + /* act as anonymous user */ + _leave(" = {%x} [anon]", key_serial(cell->anonymous_key)); + return key_get(cell->anonymous_key); + } else { + /* act as authorised user */ + _leave(" = {%x} [auth]", key_serial(key)); + return key; + } +} + +/* + * dispose of a permits list + */ +void afs_zap_permits(struct rcu_head *rcu) +{ + struct afs_permits *permits = + container_of(rcu, struct afs_permits, rcu); + int loop; + + _enter("{%d}", permits->count); + + for (loop = permits->count - 1; loop >= 0; loop--) + key_put(permits->permits[loop].key); + kfree(permits); +} + +/* + * dispose of a permits list in which all the key pointers have been copied + */ +static void afs_dispose_of_permits(struct rcu_head *rcu) +{ + struct afs_permits *permits = + container_of(rcu, struct afs_permits, rcu); + + _enter("{%d}", permits->count); + + kfree(permits); +} + +/* + * get the authorising vnode - this is the specified inode itself if it's a + * directory or it's the parent directory if the specified inode is a file or + * symlink + * - the caller must release the ref on the inode + */ +static struct afs_vnode *afs_get_auth_inode(struct afs_vnode *vnode, + struct key *key) +{ + struct afs_vnode *auth_vnode; + struct inode *auth_inode; + + _enter(""); + + if (S_ISDIR(vnode->vfs_inode.i_mode)) { + auth_inode = igrab(&vnode->vfs_inode); + ASSERT(auth_inode != NULL); + } else { + auth_inode = afs_iget(vnode->vfs_inode.i_sb, key, + &vnode->status.parent, NULL, NULL); + if (IS_ERR(auth_inode)) + return ERR_PTR(PTR_ERR(auth_inode)); + } + + auth_vnode = AFS_FS_I(auth_inode); + _leave(" = {%x}", auth_vnode->fid.vnode); + return auth_vnode; +} + +/* + * clear the permit cache on a directory vnode + */ +void afs_clear_permits(struct afs_vnode *vnode) +{ + struct afs_permits *permits; + + _enter("{%x}", vnode->fid.vnode); + + mutex_lock(&vnode->permits_lock); + permits = vnode->permits; + rcu_assign_pointer(vnode->permits, NULL); + mutex_unlock(&vnode->permits_lock); + + if (permits) + call_rcu(&permits->rcu, afs_zap_permits); + _leave(""); +} + +/* + * add the result obtained for a vnode to its or its parent directory's cache + * for the key used to access it + */ +void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order) +{ + struct afs_permits *permits, *xpermits; + struct afs_permit *permit; + struct afs_vnode *auth_vnode; + int count, loop; + + _enter("{%x},%x,%lx", vnode->fid.vnode, key_serial(key), acl_order); + + auth_vnode = afs_get_auth_inode(vnode, key); + if (IS_ERR(auth_vnode)) { + _leave(" [get error %ld]", PTR_ERR(auth_vnode)); + return; + } + + mutex_lock(&auth_vnode->permits_lock); + + /* guard against a rename being detected whilst we waited for the + * lock */ + if (memcmp(&auth_vnode->fid, &vnode->status.parent, + sizeof(struct afs_fid)) != 0) { + _debug("renamed"); + goto out_unlock; + } + + /* have to be careful as the directory's callback may be broken between + * us receiving the status we're trying to cache and us getting the + * lock to update the cache for the status */ + if (auth_vnode->acl_order - acl_order > 0) { + _debug("ACL changed?"); + goto out_unlock; + } + + /* always update the anonymous mask */ + _debug("anon access %x", vnode->status.anon_access); + auth_vnode->status.anon_access = vnode->status.anon_access; + if (key == vnode->volume->cell->anonymous_key) + goto out_unlock; + + xpermits = auth_vnode->permits; + count = 0; + if (xpermits) { + /* see if the permit is already in the list + * - if it is then we just amend the list + */ + count = xpermits->count; + permit = xpermits->permits; + for (loop = count; loop > 0; loop--) { + if (permit->key == key) { + permit->access_mask = + vnode->status.caller_access; + goto out_unlock; + } + permit++; + } + } + + permits = kmalloc(sizeof(*permits) + sizeof(*permit) * (count + 1), + GFP_NOFS); + if (!permits) + goto out_unlock; + + memcpy(permits->permits, xpermits->permits, + count * sizeof(struct afs_permit)); + + _debug("key %x access %x", + key_serial(key), vnode->status.caller_access); + permits->permits[count].access_mask = vnode->status.caller_access; + permits->permits[count].key = key_get(key); + permits->count = count + 1; + + rcu_assign_pointer(auth_vnode->permits, permits); + if (xpermits) + call_rcu(&xpermits->rcu, afs_dispose_of_permits); + +out_unlock: + mutex_unlock(&auth_vnode->permits_lock); + iput(&auth_vnode->vfs_inode); + _leave(""); +} + +/* + * check with the fileserver to see if the directory or parent directory is + * permitted to be accessed with this authorisation, and if so, what access it + * is granted + */ +static int afs_check_permit(struct afs_vnode *vnode, struct key *key, + afs_access_t *_access) +{ + struct afs_permits *permits; + struct afs_permit *permit; + struct afs_vnode *auth_vnode; + bool valid; + int loop, ret; + + _enter(""); + + auth_vnode = afs_get_auth_inode(vnode, key); + if (IS_ERR(auth_vnode)) { + *_access = 0; + _leave(" = %ld", PTR_ERR(auth_vnode)); + return PTR_ERR(auth_vnode); + } + + ASSERT(S_ISDIR(auth_vnode->vfs_inode.i_mode)); + + /* check the permits to see if we've got one yet */ + if (key == auth_vnode->volume->cell->anonymous_key) { + _debug("anon"); + *_access = auth_vnode->status.anon_access; + valid = true; + } else { + valid = false; + rcu_read_lock(); + permits = rcu_dereference(auth_vnode->permits); + if (permits) { + permit = permits->permits; + for (loop = permits->count; loop > 0; loop--) { + if (permit->key == key) { + _debug("found in cache"); + *_access = permit->access_mask; + valid = true; + break; + } + permit++; + } + } + rcu_read_unlock(); + } + + if (!valid) { + /* check the status on the file we're actually interested in + * (the post-processing will cache the result on auth_vnode) */ + _debug("no valid permit"); + + set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); + ret = afs_vnode_fetch_status(vnode, auth_vnode, key); + if (ret < 0) { + iput(&auth_vnode->vfs_inode); + *_access = 0; + _leave(" = %d", ret); + return ret; + } + } + + *_access = vnode->status.caller_access; + iput(&auth_vnode->vfs_inode); + _leave(" = 0 [access %x]", *_access); + return 0; +} + +/* + * check the permissions on an AFS file + * - AFS ACLs are attached to directories only, and a file is controlled by its + * parent directory's ACL + */ +int afs_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + struct afs_vnode *vnode = AFS_FS_I(inode); + afs_access_t access; + struct key *key; + int ret; + + _enter("{{%x:%x},%lx},%x,", + vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); + + key = afs_request_key(vnode->volume->cell); + if (IS_ERR(key)) { + _leave(" = %ld [key]", PTR_ERR(key)); + return PTR_ERR(key); + } + + /* if the promise has expired, we need to check the server again */ + if (!vnode->cb_promised) { + _debug("not promised"); + ret = afs_vnode_fetch_status(vnode, NULL, key); + if (ret < 0) + goto error; + _debug("new promise [fl=%lx]", vnode->flags); + } + + /* check the permits to see if we've got one yet */ + ret = afs_check_permit(vnode, key, &access); + if (ret < 0) + goto error; + + /* interpret the access mask */ + _debug("REQ %x ACC %x on %s", + mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file"); + + if (S_ISDIR(inode->i_mode)) { + if (mask & MAY_EXEC) { + if (!(access & AFS_ACE_LOOKUP)) + goto permission_denied; + } else if (mask & MAY_READ) { + if (!(access & AFS_ACE_READ)) + goto permission_denied; + } else if (mask & MAY_WRITE) { + if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */ + AFS_ACE_INSERT | /* create, mkdir, symlink, rename to */ + AFS_ACE_WRITE))) /* chmod */ + goto permission_denied; + } else { + BUG(); + } + } else { + if (!(access & AFS_ACE_LOOKUP)) + goto permission_denied; + if (mask & (MAY_EXEC | MAY_READ)) { + if (!(access & AFS_ACE_READ)) + goto permission_denied; + } else if (mask & MAY_WRITE) { + if (!(access & AFS_ACE_WRITE)) + goto permission_denied; + } + } + + key_put(key); + ret = generic_permission(inode, mask, NULL); + _leave(" = %d", ret); + return ret; + +permission_denied: + ret = -EACCES; +error: + key_put(key); + _leave(" = %d", ret); + return ret; +} diff --git a/fs/afs/server.c b/fs/afs/server.c index 44aff81..96bb23b 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -1,6 +1,6 @@ -/* server.c: AFS server record management +/* AFS server record management * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -11,489 +11,314 @@ #include #include -#include -#include -#include "volume.h" -#include "cell.h" -#include "server.h" -#include "transport.h" -#include "vlclient.h" -#include "kafstimod.h" #include "internal.h" -DEFINE_SPINLOCK(afs_server_peer_lock); +unsigned afs_server_timeout = 10; /* server timeout in seconds */ -#define FS_SERVICE_ID 1 /* AFS Volume Location Service ID */ -#define VL_SERVICE_ID 52 /* AFS Volume Location Service ID */ +static void afs_reap_server(struct work_struct *); -static void __afs_server_timeout(struct afs_timer *timer) +/* tree of all the servers, indexed by IP address */ +static struct rb_root afs_servers = RB_ROOT; +static DEFINE_RWLOCK(afs_servers_lock); + +/* LRU list of all the servers not currently in use */ +static LIST_HEAD(afs_server_graveyard); +static DEFINE_SPINLOCK(afs_server_graveyard_lock); +static DECLARE_DELAYED_WORK(afs_server_reaper, afs_reap_server); + +/* + * install a server record in the master tree + */ +static int afs_install_server(struct afs_server *server) { - struct afs_server *server = - list_entry(timer, struct afs_server, timeout); + struct afs_server *xserver; + struct rb_node **pp, *p; + int ret; - _debug("SERVER TIMEOUT [%p{u=%d}]", - server, atomic_read(&server->usage)); + _enter("%p", server); - afs_server_do_timeout(server); -} + write_lock(&afs_servers_lock); + + ret = -EEXIST; + pp = &afs_servers.rb_node; + p = NULL; + while (*pp) { + p = *pp; + _debug("- consider %p", p); + xserver = rb_entry(p, struct afs_server, master_rb); + if (server->addr.s_addr < xserver->addr.s_addr) + pp = &(*pp)->rb_left; + else if (server->addr.s_addr > xserver->addr.s_addr) + pp = &(*pp)->rb_right; + else + goto error; + } -static const struct afs_timer_ops afs_server_timer_ops = { - .timed_out = __afs_server_timeout, -}; + rb_link_node(&server->master_rb, p, pp); + rb_insert_color(&server->master_rb, &afs_servers); + ret = 0; + +error: + write_unlock(&afs_servers_lock); + return ret; +} -/*****************************************************************************/ /* - * lookup a server record in a cell - * - TODO: search the cell's server list + * allocate a new server record */ -int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr, - struct afs_server **_server) +static struct afs_server *afs_alloc_server(struct afs_cell *cell, + const struct in_addr *addr) { - struct afs_server *server, *active, *zombie; - int loop; + struct afs_server *server; - _enter("%p,%08x,", cell, ntohl(addr->s_addr)); + _enter(""); - /* allocate and initialise a server record */ server = kzalloc(sizeof(struct afs_server), GFP_KERNEL); - if (!server) { - _leave(" = -ENOMEM"); - return -ENOMEM; + if (server) { + atomic_set(&server->usage, 1); + server->cell = cell; + + INIT_LIST_HEAD(&server->link); + INIT_LIST_HEAD(&server->grave); + init_rwsem(&server->sem); + spin_lock_init(&server->fs_lock); + server->fs_vnodes = RB_ROOT; + server->cb_promises = RB_ROOT; + spin_lock_init(&server->cb_lock); + init_waitqueue_head(&server->cb_break_waitq); + INIT_DELAYED_WORK(&server->cb_break_work, + afs_dispatch_give_up_callbacks); + + memcpy(&server->addr, addr, sizeof(struct in_addr)); + server->addr.s_addr = addr->s_addr; } - atomic_set(&server->usage, 1); - - INIT_LIST_HEAD(&server->link); - init_rwsem(&server->sem); - INIT_LIST_HEAD(&server->fs_callq); - spin_lock_init(&server->fs_lock); - INIT_LIST_HEAD(&server->cb_promises); - spin_lock_init(&server->cb_lock); - - for (loop = 0; loop < AFS_SERVER_CONN_LIST_SIZE; loop++) - server->fs_conn_cnt[loop] = 4; + _leave(" = %p{%d}", server, atomic_read(&server->usage)); + return server; +} - memcpy(&server->addr, addr, sizeof(struct in_addr)); - server->addr.s_addr = addr->s_addr; +/* + * get an FS-server record for a cell + */ +struct afs_server *afs_lookup_server(struct afs_cell *cell, + const struct in_addr *addr) +{ + struct afs_server *server, *candidate; - afs_timer_init(&server->timeout, &afs_server_timer_ops); + _enter("%p,"NIPQUAD_FMT, cell, NIPQUAD(addr->s_addr)); - /* add to the cell */ - write_lock(&cell->sv_lock); + /* quick scan of the list to see if we already have the server */ + read_lock(&cell->servers_lock); - /* check the active list */ - list_for_each_entry(active, &cell->sv_list, link) { - if (active->addr.s_addr == addr->s_addr) - goto use_active_server; + list_for_each_entry(server, &cell->servers, link) { + if (server->addr.s_addr == addr->s_addr) + goto found_server_quickly; } + read_unlock(&cell->servers_lock); - /* check the inactive list */ - spin_lock(&cell->sv_gylock); - list_for_each_entry(zombie, &cell->sv_graveyard, link) { - if (zombie->addr.s_addr == addr->s_addr) - goto resurrect_server; + candidate = afs_alloc_server(cell, addr); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); } - spin_unlock(&cell->sv_gylock); - afs_get_cell(cell); - server->cell = cell; - list_add_tail(&server->link, &cell->sv_list); + write_lock(&cell->servers_lock); - write_unlock(&cell->sv_lock); + /* check the cell's server list again */ + list_for_each_entry(server, &cell->servers, link) { + if (server->addr.s_addr == addr->s_addr) + goto found_server; + } - *_server = server; - _leave(" = 0 (%p)", server); - return 0; + _debug("new"); + server = candidate; + if (afs_install_server(server) < 0) + goto server_in_two_cells; - /* found a matching active server */ - use_active_server: - _debug("active server"); - afs_get_server(active); - write_unlock(&cell->sv_lock); + afs_get_cell(cell); + list_add_tail(&server->link, &cell->servers); + + write_unlock(&cell->servers_lock); + _leave(" = %p{%d}", server, atomic_read(&server->usage)); + return server; + + /* found a matching server quickly */ +found_server_quickly: + _debug("found quickly"); + afs_get_server(server); + read_unlock(&cell->servers_lock); +no_longer_unused: + if (!list_empty(&server->grave)) { + spin_lock(&afs_server_graveyard_lock); + list_del_init(&server->grave); + spin_unlock(&afs_server_graveyard_lock); + } + _leave(" = %p{%d}", server, atomic_read(&server->usage)); + return server; + + /* found a matching server on the second pass */ +found_server: + _debug("found"); + afs_get_server(server); + write_unlock(&cell->servers_lock); + kfree(candidate); + goto no_longer_unused; + + /* found a server that seems to be in two cells */ +server_in_two_cells: + write_unlock(&cell->servers_lock); + kfree(candidate); + printk(KERN_NOTICE "kAFS:" + " Server "NIPQUAD_FMT" appears to be in two cells\n", + NIPQUAD(*addr)); + _leave(" = -EEXIST"); + return ERR_PTR(-EEXIST); +} - kfree(server); +/* + * look up a server by its IP address + */ +struct afs_server *afs_find_server(const struct in_addr *_addr) +{ + struct afs_server *server = NULL; + struct rb_node *p; + struct in_addr addr = *_addr; - *_server = active; - _leave(" = 0 (%p)", active); - return 0; + _enter(NIPQUAD_FMT, NIPQUAD(addr.s_addr)); - /* found a matching server in the graveyard, so resurrect it and - * dispose of the new record */ - resurrect_server: - _debug("resurrecting server"); + read_lock(&afs_servers_lock); - list_move_tail(&zombie->link, &cell->sv_list); - afs_get_server(zombie); - afs_kafstimod_del_timer(&zombie->timeout); - spin_unlock(&cell->sv_gylock); - write_unlock(&cell->sv_lock); + p = afs_servers.rb_node; + while (p) { + server = rb_entry(p, struct afs_server, master_rb); - kfree(server); + _debug("- consider %p", p); - *_server = zombie; - _leave(" = 0 (%p)", zombie); - return 0; + if (addr.s_addr < server->addr.s_addr) { + p = p->rb_left; + } else if (addr.s_addr > server->addr.s_addr) { + p = p->rb_right; + } else { + afs_get_server(server); + goto found; + } + } -} /* end afs_server_lookup() */ + server = NULL; +found: + read_unlock(&afs_servers_lock); + ASSERTIFCMP(server, server->addr.s_addr, ==, addr.s_addr); + _leave(" = %p", server); + return server; +} -/*****************************************************************************/ /* * destroy a server record * - removes from the cell list */ void afs_put_server(struct afs_server *server) { - struct afs_cell *cell; - if (!server) return; - _enter("%p", server); - - cell = server->cell; + _enter("%p{%d}", server, atomic_read(&server->usage)); - /* sanity check */ - BUG_ON(atomic_read(&server->usage) <= 0); + _debug("PUT SERVER %d", atomic_read(&server->usage)); - /* to prevent a race, the decrement and the dequeue must be effectively - * atomic */ - write_lock(&cell->sv_lock); + ASSERTCMP(atomic_read(&server->usage), >, 0); if (likely(!atomic_dec_and_test(&server->usage))) { - write_unlock(&cell->sv_lock); _leave(""); return; } - spin_lock(&cell->sv_gylock); - list_move_tail(&server->link, &cell->sv_graveyard); + afs_flush_callback_breaks(server); - /* time out in 10 secs */ - afs_kafstimod_add_timer(&server->timeout, 10 * HZ); - - spin_unlock(&cell->sv_gylock); - write_unlock(&cell->sv_lock); - - _leave(" [killed]"); -} /* end afs_put_server() */ + spin_lock(&afs_server_graveyard_lock); + if (atomic_read(&server->usage) == 0) { + list_move_tail(&server->grave, &afs_server_graveyard); + server->time_of_death = get_seconds(); + schedule_delayed_work(&afs_server_reaper, + afs_server_timeout * HZ); + } + spin_unlock(&afs_server_graveyard_lock); + _leave(" [dead]"); +} -/*****************************************************************************/ /* - * timeout server record - * - removes from the cell's graveyard if the usage count is zero + * destroy a dead server */ -void afs_server_do_timeout(struct afs_server *server) +static void afs_destroy_server(struct afs_server *server) { - struct rxrpc_peer *peer; - struct afs_cell *cell; - int loop; - _enter("%p", server); - cell = server->cell; - - BUG_ON(atomic_read(&server->usage) < 0); - - /* remove from graveyard if still dead */ - spin_lock(&cell->vl_gylock); - if (atomic_read(&server->usage) == 0) - list_del_init(&server->link); - else - server = NULL; - spin_unlock(&cell->vl_gylock); - - if (!server) { - _leave(""); - return; /* resurrected */ - } - - /* we can now destroy it properly */ - afs_put_cell(cell); - - /* uncross-point the structs under a global lock */ - spin_lock(&afs_server_peer_lock); - peer = server->peer; - if (peer) { - server->peer = NULL; - peer->user = NULL; - } - spin_unlock(&afs_server_peer_lock); - - /* finish cleaning up the server */ - for (loop = AFS_SERVER_CONN_LIST_SIZE - 1; loop >= 0; loop--) - if (server->fs_conn[loop]) - rxrpc_put_connection(server->fs_conn[loop]); - - if (server->vlserver) - rxrpc_put_connection(server->vlserver); + ASSERTCMP(server->fs_vnodes.rb_node, ==, NULL); + ASSERTCMP(server->cb_promises.rb_node, ==, NULL); + ASSERTCMP(server->cb_break_head, ==, server->cb_break_tail); + ASSERTCMP(atomic_read(&server->cb_break_n), ==, 0); + afs_put_cell(server->cell); kfree(server); +} - _leave(" [destroyed]"); -} /* end afs_server_do_timeout() */ - -/*****************************************************************************/ /* - * get a callslot on a connection to the fileserver on the specified server + * reap dead server records */ -int afs_server_request_callslot(struct afs_server *server, - struct afs_server_callslot *callslot) +static void afs_reap_server(struct work_struct *work) { - struct afs_server_callslot *pcallslot; - struct rxrpc_connection *conn; - int nconn, ret; - - _enter("%p,",server); - - INIT_LIST_HEAD(&callslot->link); - callslot->task = current; - callslot->conn = NULL; - callslot->nconn = -1; - callslot->ready = 0; - - ret = 0; - conn = NULL; - - /* get hold of a callslot first */ - spin_lock(&server->fs_lock); - - /* resurrect the server if it's death timeout has expired */ - if (server->fs_state) { - if (time_before(jiffies, server->fs_dead_jif)) { - ret = server->fs_state; - spin_unlock(&server->fs_lock); - _leave(" = %d [still dead]", ret); - return ret; + LIST_HEAD(corpses); + struct afs_server *server; + unsigned long delay, expiry; + time_t now; + + now = get_seconds(); + spin_lock(&afs_server_graveyard_lock); + + while (!list_empty(&afs_server_graveyard)) { + server = list_entry(afs_server_graveyard.next, + struct afs_server, grave); + + /* the queue is ordered most dead first */ + expiry = server->time_of_death + afs_server_timeout; + if (expiry > now) { + delay = (expiry - now) * HZ; + if (!schedule_delayed_work(&afs_server_reaper, delay)) { + cancel_delayed_work(&afs_server_reaper); + schedule_delayed_work(&afs_server_reaper, + delay); + } + break; } - server->fs_state = 0; - } - - /* try and find a connection that has spare callslots */ - for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) { - if (server->fs_conn_cnt[nconn] > 0) { - server->fs_conn_cnt[nconn]--; - spin_unlock(&server->fs_lock); - callslot->nconn = nconn; - goto obtained_slot; + write_lock(&server->cell->servers_lock); + write_lock(&afs_servers_lock); + if (atomic_read(&server->usage) > 0) { + list_del_init(&server->grave); + } else { + list_move_tail(&server->grave, &corpses); + list_del_init(&server->link); + rb_erase(&server->master_rb, &afs_servers); } + write_unlock(&afs_servers_lock); + write_unlock(&server->cell->servers_lock); } - /* none were available - wait interruptibly for one to become - * available */ - set_current_state(TASK_INTERRUPTIBLE); - list_add_tail(&callslot->link, &server->fs_callq); - spin_unlock(&server->fs_lock); - - while (!callslot->ready && !signal_pending(current)) { - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - - set_current_state(TASK_RUNNING); - - /* even if we were interrupted we may still be queued */ - if (!callslot->ready) { - spin_lock(&server->fs_lock); - list_del_init(&callslot->link); - spin_unlock(&server->fs_lock); - } - - nconn = callslot->nconn; + spin_unlock(&afs_server_graveyard_lock); - /* if interrupted, we must release any slot we also got before - * returning an error */ - if (signal_pending(current)) { - ret = -EINTR; - goto error_release; + /* now reap the corpses we've extracted */ + while (!list_empty(&corpses)) { + server = list_entry(corpses.next, struct afs_server, grave); + list_del(&server->grave); + afs_destroy_server(server); } +} - /* if we were woken up with an error, then pass that error back to the - * called */ - if (nconn < 0) { - _leave(" = %d", callslot->errno); - return callslot->errno; - } - - /* were we given a connection directly? */ - if (callslot->conn) { - /* yes - use it */ - _leave(" = 0 (nc=%d)", nconn); - return 0; - } - - /* got a callslot, but no connection */ - obtained_slot: - - /* need to get hold of the RxRPC connection */ - down_write(&server->sem); - - /* quick check to see if there's an outstanding error */ - ret = server->fs_state; - if (ret) - goto error_release_upw; - - if (server->fs_conn[nconn]) { - /* reuse an existing connection */ - rxrpc_get_connection(server->fs_conn[nconn]); - callslot->conn = server->fs_conn[nconn]; - } - else { - /* create a new connection */ - ret = rxrpc_create_connection(afs_transport, - htons(7000), - server->addr.s_addr, - FS_SERVICE_ID, - NULL, - &server->fs_conn[nconn]); - - if (ret < 0) - goto error_release_upw; - - callslot->conn = server->fs_conn[0]; - rxrpc_get_connection(callslot->conn); - } - - up_write(&server->sem); - - _leave(" = 0"); - return 0; - - /* handle an error occurring */ - error_release_upw: - up_write(&server->sem); - - error_release: - /* either release the callslot or pass it along to another deserving - * task */ - spin_lock(&server->fs_lock); - - if (nconn < 0) { - /* no callslot allocated */ - } - else if (list_empty(&server->fs_callq)) { - /* no one waiting */ - server->fs_conn_cnt[nconn]++; - spin_unlock(&server->fs_lock); - } - else { - /* someone's waiting - dequeue them and wake them up */ - pcallslot = list_entry(server->fs_callq.next, - struct afs_server_callslot, link); - list_del_init(&pcallslot->link); - - pcallslot->errno = server->fs_state; - if (!pcallslot->errno) { - /* pass them out callslot details */ - callslot->conn = xchg(&pcallslot->conn, - callslot->conn); - pcallslot->nconn = nconn; - callslot->nconn = nconn = -1; - } - pcallslot->ready = 1; - wake_up_process(pcallslot->task); - spin_unlock(&server->fs_lock); - } - - rxrpc_put_connection(callslot->conn); - callslot->conn = NULL; - - _leave(" = %d", ret); - return ret; - -} /* end afs_server_request_callslot() */ - -/*****************************************************************************/ -/* - * release a callslot back to the server - * - transfers the RxRPC connection to the next pending callslot if possible - */ -void afs_server_release_callslot(struct afs_server *server, - struct afs_server_callslot *callslot) -{ - struct afs_server_callslot *pcallslot; - - _enter("{ad=%08x,cnt=%u},{%d}", - ntohl(server->addr.s_addr), - server->fs_conn_cnt[callslot->nconn], - callslot->nconn); - - BUG_ON(callslot->nconn < 0); - - spin_lock(&server->fs_lock); - - if (list_empty(&server->fs_callq)) { - /* no one waiting */ - server->fs_conn_cnt[callslot->nconn]++; - spin_unlock(&server->fs_lock); - } - else { - /* someone's waiting - dequeue them and wake them up */ - pcallslot = list_entry(server->fs_callq.next, - struct afs_server_callslot, link); - list_del_init(&pcallslot->link); - - pcallslot->errno = server->fs_state; - if (!pcallslot->errno) { - /* pass them out callslot details */ - callslot->conn = xchg(&pcallslot->conn, callslot->conn); - pcallslot->nconn = callslot->nconn; - callslot->nconn = -1; - } - - pcallslot->ready = 1; - wake_up_process(pcallslot->task); - spin_unlock(&server->fs_lock); - } - - rxrpc_put_connection(callslot->conn); - - _leave(""); -} /* end afs_server_release_callslot() */ - -/*****************************************************************************/ /* - * get a handle to a connection to the vlserver (volume location) on the - * specified server + * discard all the server records for rmmod */ -int afs_server_get_vlconn(struct afs_server *server, - struct rxrpc_connection **_conn) +void __exit afs_purge_servers(void) { - struct rxrpc_connection *conn; - int ret; - - _enter("%p,", server); - - ret = 0; - conn = NULL; - down_read(&server->sem); - - if (server->vlserver) { - /* reuse an existing connection */ - rxrpc_get_connection(server->vlserver); - conn = server->vlserver; - up_read(&server->sem); - } - else { - /* create a new connection */ - up_read(&server->sem); - down_write(&server->sem); - if (!server->vlserver) { - ret = rxrpc_create_connection(afs_transport, - htons(7003), - server->addr.s_addr, - VL_SERVICE_ID, - NULL, - &server->vlserver); - } - if (ret == 0) { - rxrpc_get_connection(server->vlserver); - conn = server->vlserver; - } - up_write(&server->sem); - } - - *_conn = conn; - _leave(" = %d", ret); - return ret; -} /* end afs_server_get_vlconn() */ + afs_server_timeout = 0; + cancel_delayed_work(&afs_server_reaper); + schedule_delayed_work(&afs_server_reaper, 0); +} diff --git a/fs/afs/server.h b/fs/afs/server.h deleted file mode 100644 index c3d2411..0000000 --- a/fs/afs/server.h +++ /dev/null @@ -1,102 +0,0 @@ -/* server.h: AFS server record - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_SERVER_H -#define _LINUX_AFS_SERVER_H - -#include "types.h" -#include "kafstimod.h" -#include -#include - -extern spinlock_t afs_server_peer_lock; - -/*****************************************************************************/ -/* - * AFS server record - */ -struct afs_server -{ - atomic_t usage; - struct afs_cell *cell; /* cell in which server resides */ - struct list_head link; /* link in cell's server list */ - struct rw_semaphore sem; /* access lock */ - struct afs_timer timeout; /* graveyard timeout */ - struct in_addr addr; /* server address */ - struct rxrpc_peer *peer; /* peer record for this server */ - struct rxrpc_connection *vlserver; /* connection to the volume location service */ - - /* file service access */ -#define AFS_SERVER_CONN_LIST_SIZE 2 - struct rxrpc_connection *fs_conn[AFS_SERVER_CONN_LIST_SIZE]; /* FS connections */ - unsigned fs_conn_cnt[AFS_SERVER_CONN_LIST_SIZE]; /* per conn call count */ - struct list_head fs_callq; /* queue of processes waiting to make a call */ - spinlock_t fs_lock; /* access lock */ - int fs_state; /* 0 or reason FS currently marked dead (-errno) */ - unsigned fs_rtt; /* FS round trip time */ - unsigned long fs_act_jif; /* time at which last activity occurred */ - unsigned long fs_dead_jif; /* time at which no longer to be considered dead */ - - /* callback promise management */ - struct list_head cb_promises; /* as yet unbroken promises from this server */ - spinlock_t cb_lock; /* access lock */ -}; - -extern int afs_server_lookup(struct afs_cell *cell, - const struct in_addr *addr, - struct afs_server **_server); - -#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0) - -extern void afs_put_server(struct afs_server *server); -extern void afs_server_do_timeout(struct afs_server *server); - -extern int afs_server_find_by_peer(const struct rxrpc_peer *peer, - struct afs_server **_server); - -extern int afs_server_get_vlconn(struct afs_server *server, - struct rxrpc_connection **_conn); - -static inline -struct afs_server *afs_server_get_from_peer(struct rxrpc_peer *peer) -{ - struct afs_server *server; - - spin_lock(&afs_server_peer_lock); - server = peer->user; - if (server) - afs_get_server(server); - spin_unlock(&afs_server_peer_lock); - - return server; -} - -/*****************************************************************************/ -/* - * AFS server callslot grant record - */ -struct afs_server_callslot -{ - struct list_head link; /* link in server's list */ - struct task_struct *task; /* process waiting to make call */ - struct rxrpc_connection *conn; /* connection to use (or NULL on error) */ - short nconn; /* connection slot number (-1 on error) */ - char ready; /* T when ready */ - int errno; /* error number if nconn==-1 */ -}; - -extern int afs_server_request_callslot(struct afs_server *server, - struct afs_server_callslot *callslot); - -extern void afs_server_release_callslot(struct afs_server *server, - struct afs_server_callslot *callslot); - -#endif /* _LINUX_AFS_SERVER_H */ diff --git a/fs/afs/super.c b/fs/afs/super.c index eb7e323..cebd03c 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -1,5 +1,6 @@ -/* - * Copyright (c) 2002 Red Hat, Inc. All rights reserved. +/* AFS superblock handling + * + * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved. * * This software may be freely redistributed under the terms of the * GNU General Public License. @@ -9,7 +10,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Authors: David Howells - * David Woodhouse + * David Woodhouse * */ @@ -19,22 +20,10 @@ #include #include #include #include -#include "vnode.h" -#include "volume.h" -#include "cell.h" -#include "cmservice.h" -#include "fsclient.h" -#include "super.h" #include "internal.h" #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ -struct afs_mount_params { - int rwpath; - struct afs_cell *default_cell; - struct afs_volume *volume; -}; - static void afs_i_init_once(void *foo, struct kmem_cache *cachep, unsigned long flags); @@ -62,13 +51,13 @@ static const struct super_operations afs .drop_inode = generic_delete_inode, .destroy_inode = afs_destroy_inode, .clear_inode = afs_clear_inode, + .umount_begin = afs_umount_begin, .put_super = afs_put_super, }; static struct kmem_cache *afs_inode_cachep; static atomic_t afs_count_active_inodes; -/*****************************************************************************/ /* * initialise the filesystem */ @@ -78,8 +67,6 @@ int __init afs_fs_init(void) _enter(""); - afs_timer_init(&afs_mntpt_expiry_timer, &afs_mntpt_expiry_timer_ops); - /* create ourselves an inode cache */ atomic_set(&afs_count_active_inodes, 0); @@ -99,20 +86,22 @@ int __init afs_fs_init(void) ret = register_filesystem(&afs_fs_type); if (ret < 0) { kmem_cache_destroy(afs_inode_cachep); - kleave(" = %d", ret); + _leave(" = %d", ret); return ret; } - kleave(" = 0"); + _leave(" = 0"); return 0; -} /* end afs_fs_init() */ +} -/*****************************************************************************/ /* * clean up the filesystem */ void __exit afs_fs_exit(void) { + _enter(""); + + afs_mntpt_kill_timer(); unregister_filesystem(&afs_fs_type); if (atomic_read(&afs_count_active_inodes) != 0) { @@ -122,10 +111,9 @@ void __exit afs_fs_exit(void) } kmem_cache_destroy(afs_inode_cachep); + _leave(""); +} -} /* end afs_fs_exit() */ - -/*****************************************************************************/ /* * check that an argument has a value */ @@ -136,9 +124,8 @@ static int want_arg(char **_value, const return 0; } return 1; -} /* end want_arg() */ +} -/*****************************************************************************/ /* * check that there's no subsequent value */ @@ -150,18 +137,17 @@ static int want_no_value(char *const *_v return 0; } return 1; -} /* end want_no_value() */ +} -/*****************************************************************************/ /* * parse the mount options * - this function has been shamelessly adapted from the ext3 fs which * shamelessly adapted it from the msdos fs */ -static int afs_super_parse_options(struct afs_mount_params *params, - char *options, - const char **devname) +static int afs_parse_options(struct afs_mount_params *params, + char *options, const char **devname) { + struct afs_cell *cell; char *key, *value; int ret; @@ -170,51 +156,135 @@ static int afs_super_parse_options(struc options[PAGE_SIZE - 1] = 0; ret = 0; - while ((key = strsep(&options, ",")) != 0) - { + while ((key = strsep(&options, ","))) { value = strchr(key, '='); if (value) *value++ = 0; - printk("kAFS: KEY: %s, VAL:%s\n", key, value ?: "-"); + _debug("kAFS: KEY: %s, VAL:%s", key, value ?: "-"); if (strcmp(key, "rwpath") == 0) { if (!want_no_value(&value, "rwpath")) return -EINVAL; params->rwpath = 1; - continue; - } - else if (strcmp(key, "vol") == 0) { + } else if (strcmp(key, "vol") == 0) { if (!want_arg(&value, "vol")) return -EINVAL; *devname = value; - continue; - } - else if (strcmp(key, "cell") == 0) { + } else if (strcmp(key, "cell") == 0) { if (!want_arg(&value, "cell")) return -EINVAL; - afs_put_cell(params->default_cell); - ret = afs_cell_lookup(value, - strlen(value), - ¶ms->default_cell); - if (ret < 0) - return -EINVAL; - continue; + cell = afs_cell_lookup(value, strlen(value)); + if (IS_ERR(cell)) + return PTR_ERR(cell); + afs_put_cell(params->cell); + params->cell = cell; + } else { + printk("kAFS: Unknown mount option: '%s'\n", key); + ret = -EINVAL; + goto error; } - - printk("kAFS: Unknown mount option: '%s'\n", key); - ret = -EINVAL; - goto error; } ret = 0; - - error: +error: _leave(" = %d", ret); return ret; -} /* end afs_super_parse_options() */ +} + +/* + * parse a device name to get cell name, volume name, volume type and R/W + * selector + * - this can be one of the following: + * "%[cell:]volume[.]" R/W volume + * "#[cell:]volume[.]" R/O or R/W volume (rwpath=0), + * or R/W (rwpath=1) volume + * "%[cell:]volume.readonly" R/O volume + * "#[cell:]volume.readonly" R/O volume + * "%[cell:]volume.backup" Backup volume + * "#[cell:]volume.backup" Backup volume + */ +static int afs_parse_device_name(struct afs_mount_params *params, + const char *name) +{ + struct afs_cell *cell; + const char *cellname, *suffix; + int cellnamesz; + + _enter(",%s", name); + + if (!name) { + printk(KERN_ERR "kAFS: no volume name specified\n"); + return -EINVAL; + } + + if ((name[0] != '%' && name[0] != '#') || !name[1]) { + printk(KERN_ERR "kAFS: unparsable volume name\n"); + return -EINVAL; + } + + /* determine the type of volume we're looking for */ + params->type = AFSVL_ROVOL; + params->force = false; + if (params->rwpath || name[0] == '%') { + params->type = AFSVL_RWVOL; + params->force = true; + } + name++; + + /* split the cell name out if there is one */ + params->volname = strchr(name, ':'); + if (params->volname) { + cellname = name; + cellnamesz = params->volname - name; + params->volname++; + } else { + params->volname = name; + cellname = NULL; + cellnamesz = 0; + } + + /* the volume type is further affected by a possible suffix */ + suffix = strrchr(params->volname, '.'); + if (suffix) { + if (strcmp(suffix, ".readonly") == 0) { + params->type = AFSVL_ROVOL; + params->force = true; + } else if (strcmp(suffix, ".backup") == 0) { + params->type = AFSVL_BACKVOL; + params->force = true; + } else if (suffix[1] == 0) { + } else { + suffix = NULL; + } + } + + params->volnamesz = suffix ? + suffix - params->volname : strlen(params->volname); + + _debug("cell %*.*s [%p]", + cellnamesz, cellnamesz, cellname ?: "", params->cell); + + /* lookup the cell record */ + if (cellname || !params->cell) { + cell = afs_cell_lookup(cellname, cellnamesz); + if (IS_ERR(cell)) { + printk(KERN_ERR "kAFS: unable to lookup cell '%s'\n", + cellname ?: ""); + return PTR_ERR(cell); + } + afs_put_cell(params->cell); + params->cell = cell; + } + + _debug("CELL:%s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s", + params->cell->name, params->cell, + params->volnamesz, params->volnamesz, params->volname, + suffix ?: "-", params->type, params->force ? " FORCE" : ""); + + return 0; +} -/*****************************************************************************/ /* * check a superblock to see if it's the one we're looking for */ @@ -224,13 +294,12 @@ static int afs_test_super(struct super_b struct afs_super_info *as = sb->s_fs_info; return as->volume == params->volume; -} /* end afs_test_super() */ +} -/*****************************************************************************/ /* * fill in the superblock */ -static int afs_fill_super(struct super_block *sb, void *data, int silent) +static int afs_fill_super(struct super_block *sb, void *data) { struct afs_mount_params *params = data; struct afs_super_info *as = NULL; @@ -239,7 +308,7 @@ static int afs_fill_super(struct super_b struct inode *inode = NULL; int ret; - kenter(""); + _enter(""); /* allocate a superblock info record */ as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); @@ -262,9 +331,9 @@ static int afs_fill_super(struct super_b fid.vid = as->volume->vid; fid.vnode = 1; fid.unique = 1; - ret = afs_iget(sb, &fid, &inode); - if (ret < 0) - goto error; + inode = afs_iget(sb, params->key, &fid, NULL, NULL); + if (IS_ERR(inode)) + goto error_inode; ret = -ENOMEM; root = d_alloc_root(inode); @@ -273,21 +342,23 @@ static int afs_fill_super(struct super_b sb->s_root = root; - kleave(" = 0"); + _leave(" = 0"); return 0; - error: +error_inode: + ret = PTR_ERR(inode); + inode = NULL; +error: iput(inode); afs_put_volume(as->volume); kfree(as); sb->s_fs_info = NULL; - kleave(" = %d", ret); + _leave(" = %d", ret); return ret; -} /* end afs_fill_super() */ +} -/*****************************************************************************/ /* * get an AFS superblock * - TODO: don't use get_sb_nodev(), but rather call sget() directly @@ -300,69 +371,80 @@ static int afs_get_sb(struct file_system { struct afs_mount_params params; struct super_block *sb; + struct afs_volume *vol; + struct key *key; int ret; _enter(",,%s,%p", dev_name, options); memset(¶ms, 0, sizeof(params)); - /* start the cache manager */ - ret = afscm_start(); - if (ret < 0) { - _leave(" = %d", ret); - return ret; - } - - /* parse the options */ + /* parse the options and device name */ if (options) { - ret = afs_super_parse_options(¶ms, options, &dev_name); + ret = afs_parse_options(¶ms, options, &dev_name); if (ret < 0) goto error; - if (!dev_name) { - printk("kAFS: no volume name specified\n"); - ret = -EINVAL; - goto error; - } } - /* parse the device name */ - ret = afs_volume_lookup(dev_name, - params.default_cell, - params.rwpath, - ¶ms.volume); + + ret = afs_parse_device_name(¶ms, dev_name); if (ret < 0) goto error; - /* allocate a deviceless superblock */ - sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms); - if (IS_ERR(sb)) + /* try and do the mount securely */ + key = afs_request_key(params.cell); + if (IS_ERR(key)) { + _leave(" = %ld [key]", PTR_ERR(key)); + ret = PTR_ERR(key); goto error; + } + params.key = key; - sb->s_flags = flags; + /* parse the device name */ + vol = afs_volume_lookup(¶ms); + if (IS_ERR(vol)) { + ret = PTR_ERR(vol); + goto error; + } + params.volume = vol; - ret = afs_fill_super(sb, ¶ms, flags & MS_SILENT ? 1 : 0); - if (ret < 0) { - up_write(&sb->s_umount); - deactivate_super(sb); + /* allocate a deviceless superblock */ + sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms); + if (IS_ERR(sb)) { + ret = PTR_ERR(sb); goto error; } - sb->s_flags |= MS_ACTIVE; - simple_set_mnt(mnt, sb); + if (!sb->s_root) { + /* initial superblock/root creation */ + _debug("create"); + sb->s_flags = flags; + ret = afs_fill_super(sb, ¶ms); + if (ret < 0) { + up_write(&sb->s_umount); + deactivate_super(sb); + goto error; + } + sb->s_flags |= MS_ACTIVE; + } else { + _debug("reuse"); + ASSERTCMP(sb->s_flags, &, MS_ACTIVE); + } + + simple_set_mnt(mnt, sb); afs_put_volume(params.volume); - afs_put_cell(params.default_cell); - _leave(" = 0 [%p]", 0, sb); + afs_put_cell(params.cell); + _leave(" = 0 [%p]", sb); return 0; - error: +error: afs_put_volume(params.volume); - afs_put_cell(params.default_cell); - afscm_stop(); + afs_put_cell(params.cell); + key_put(params.key); _leave(" = %d", ret); return ret; -} /* end afs_get_sb() */ +} -/*****************************************************************************/ /* * finish the unmounting process on the superblock */ @@ -373,35 +455,30 @@ static void afs_put_super(struct super_b _enter(""); afs_put_volume(as->volume); - afscm_stop(); _leave(""); -} /* end afs_put_super() */ +} -/*****************************************************************************/ /* * initialise an inode cache slab element prior to any use */ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep, unsigned long flags) { - struct afs_vnode *vnode = (struct afs_vnode *) _vnode; + struct afs_vnode *vnode = _vnode; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { memset(vnode, 0, sizeof(*vnode)); inode_init_once(&vnode->vfs_inode); init_waitqueue_head(&vnode->update_waitq); + mutex_init(&vnode->permits_lock); + mutex_init(&vnode->validate_lock); spin_lock_init(&vnode->lock); - INIT_LIST_HEAD(&vnode->cb_link); - INIT_LIST_HEAD(&vnode->cb_hash_link); - afs_timer_init(&vnode->cb_timeout, - &afs_vnode_cb_timed_out_ops); + INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); } +} -} /* end afs_i_init_once() */ - -/*****************************************************************************/ /* * allocate an AFS inode struct from our slab cache */ @@ -409,8 +486,7 @@ static struct inode *afs_alloc_inode(str { struct afs_vnode *vnode; - vnode = (struct afs_vnode *) - kmem_cache_alloc(afs_inode_cachep, GFP_KERNEL); + vnode = kmem_cache_alloc(afs_inode_cachep, GFP_KERNEL); if (!vnode) return NULL; @@ -421,21 +497,25 @@ static struct inode *afs_alloc_inode(str vnode->volume = NULL; vnode->update_cnt = 0; - vnode->flags = 0; + vnode->flags = 1 << AFS_VNODE_UNSET; + vnode->cb_promised = false; return &vnode->vfs_inode; -} /* end afs_alloc_inode() */ +} -/*****************************************************************************/ /* * destroy an AFS inode struct */ static void afs_destroy_inode(struct inode *inode) { + struct afs_vnode *vnode = AFS_FS_I(inode); + _enter("{%lu}", inode->i_ino); - kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode)); + _debug("DESTROY INODE %p", inode); - atomic_dec(&afs_count_active_inodes); + ASSERTCMP(vnode->server, ==, NULL); -} /* end afs_destroy_inode() */ + kmem_cache_free(afs_inode_cachep, vnode); + atomic_dec(&afs_count_active_inodes); +} diff --git a/fs/afs/super.h b/fs/afs/super.h deleted file mode 100644 index 32de8cc..0000000 --- a/fs/afs/super.h +++ /dev/null @@ -1,45 +0,0 @@ -/* super.h: AFS filesystem internal private data - * - * Copyright (c) 2002 Red Hat, Inc. All rights reserved. - * - * This software may be freely redistributed under the terms of the - * GNU General Public License. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Authors: David Woodhouse - * David Howells - * - */ - -#ifndef _LINUX_AFS_SUPER_H -#define _LINUX_AFS_SUPER_H - -#include -#include "server.h" - -#ifdef __KERNEL__ - -/*****************************************************************************/ -/* - * AFS superblock private data - * - there's one superblock per volume - */ -struct afs_super_info -{ - struct afs_volume *volume; /* volume record */ - char rwparent; /* T if parent is R/W AFS volume */ -}; - -static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) -{ - return sb->s_fs_info; -} - -extern struct file_system_type afs_fs_type; - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_AFS_SUPER_H */ diff --git a/fs/afs/transport.h b/fs/afs/transport.h deleted file mode 100644 index 7013ae6..0000000 --- a/fs/afs/transport.h +++ /dev/null @@ -1,21 +0,0 @@ -/* transport.h: AFS transport management - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_TRANSPORT_H -#define _LINUX_AFS_TRANSPORT_H - -#include "types.h" -#include - -/* the cache manager transport endpoint */ -extern struct rxrpc_transport *afs_transport; - -#endif /* _LINUX_AFS_TRANSPORT_H */ diff --git a/fs/afs/types.h b/fs/afs/types.h deleted file mode 100644 index b1a2367..0000000 --- a/fs/afs/types.h +++ /dev/null @@ -1,125 +0,0 @@ -/* types.h: AFS types - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_TYPES_H -#define _LINUX_AFS_TYPES_H - -#ifdef __KERNEL__ -#include -#endif /* __KERNEL__ */ - -typedef unsigned afs_volid_t; -typedef unsigned afs_vnodeid_t; -typedef unsigned long long afs_dataversion_t; - -typedef enum { - AFSVL_RWVOL, /* read/write volume */ - AFSVL_ROVOL, /* read-only volume */ - AFSVL_BACKVOL, /* backup volume */ -} __attribute__((packed)) afs_voltype_t; - -typedef enum { - AFS_FTYPE_INVALID = 0, - AFS_FTYPE_FILE = 1, - AFS_FTYPE_DIR = 2, - AFS_FTYPE_SYMLINK = 3, -} afs_file_type_t; - -#ifdef __KERNEL__ - -struct afs_cell; -struct afs_vnode; - -/*****************************************************************************/ -/* - * AFS file identifier - */ -struct afs_fid -{ - afs_volid_t vid; /* volume ID */ - afs_vnodeid_t vnode; /* file index within volume */ - unsigned unique; /* unique ID number (file index version) */ -}; - -/*****************************************************************************/ -/* - * AFS callback notification - */ -typedef enum { - AFSCM_CB_UNTYPED = 0, /* no type set on CB break */ - AFSCM_CB_EXCLUSIVE = 1, /* CB exclusive to CM [not implemented] */ - AFSCM_CB_SHARED = 2, /* CB shared by other CM's */ - AFSCM_CB_DROPPED = 3, /* CB promise cancelled by file server */ -} afs_callback_type_t; - -struct afs_callback -{ - struct afs_server *server; /* server that made the promise */ - struct afs_fid fid; /* file identifier */ - unsigned version; /* callback version */ - unsigned expiry; /* time at which expires */ - afs_callback_type_t type; /* type of callback */ -}; - -#define AFSCBMAX 50 - -/*****************************************************************************/ -/* - * AFS volume information - */ -struct afs_volume_info -{ - afs_volid_t vid; /* volume ID */ - afs_voltype_t type; /* type of this volume */ - afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */ - - /* list of fileservers serving this volume */ - size_t nservers; /* number of entries used in servers[] */ - struct { - struct in_addr addr; /* fileserver address */ - } servers[8]; -}; - -/*****************************************************************************/ -/* - * AFS file status information - */ -struct afs_file_status -{ - unsigned if_version; /* interface version */ -#define AFS_FSTATUS_VERSION 1 - - afs_file_type_t type; /* file type */ - unsigned nlink; /* link count */ - size_t size; /* file size */ - afs_dataversion_t version; /* current data version */ - unsigned author; /* author ID */ - unsigned owner; /* owner ID */ - unsigned caller_access; /* access rights for authenticated caller */ - unsigned anon_access; /* access rights for unauthenticated caller */ - umode_t mode; /* UNIX mode */ - struct afs_fid parent; /* parent file ID */ - time_t mtime_client; /* last time client changed data */ - time_t mtime_server; /* last time server changed data */ -}; - -/*****************************************************************************/ -/* - * AFS volume synchronisation information - */ -struct afs_volsync -{ - time_t creation; /* volume creation time */ -}; - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_AFS_TYPES_H */ diff --git a/fs/afs/use-rtnetlink.c b/fs/afs/use-rtnetlink.c new file mode 100644 index 0000000..f8991c7 --- /dev/null +++ b/fs/afs/use-rtnetlink.c @@ -0,0 +1,473 @@ +/* RTNETLINK client + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ +#include +#include +#include +#include +#include +#include +#include "internal.h" + +struct afs_rtm_desc { + struct socket *nlsock; + struct afs_interface *bufs; + u8 *mac; + size_t nbufs; + size_t maxbufs; + void *data; + ssize_t datalen; + size_t datamax; + int msg_seq; + unsigned mac_index; + bool wantloopback; + int (*parse)(struct afs_rtm_desc *, struct nlmsghdr *); +}; + +/* + * parse an RTM_GETADDR response + */ +static int afs_rtm_getaddr_parse(struct afs_rtm_desc *desc, + struct nlmsghdr *nlhdr) +{ + struct afs_interface *this; + struct ifaddrmsg *ifa; + struct rtattr *rtattr; + const char *name; + size_t len; + + ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr); + + _enter("{ix=%d,af=%d}", ifa->ifa_index, ifa->ifa_family); + + if (ifa->ifa_family != AF_INET) { + _leave(" = 0 [family %d]", ifa->ifa_family); + return 0; + } + if (desc->nbufs >= desc->maxbufs) { + _leave(" = 0 [max %zu/%zu]", desc->nbufs, desc->maxbufs); + return 0; + } + + this = &desc->bufs[desc->nbufs]; + + this->index = ifa->ifa_index; + this->netmask.s_addr = inet_make_mask(ifa->ifa_prefixlen); + this->mtu = 0; + + rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)); + len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifaddrmsg)); + + name = "unknown"; + for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) { + switch (rtattr->rta_type) { + case IFA_ADDRESS: + memcpy(&this->address, RTA_DATA(rtattr), 4); + break; + case IFA_LABEL: + name = RTA_DATA(rtattr); + break; + } + } + + _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT, + name, NIPQUAD(this->address), NIPQUAD(this->netmask)); + + desc->nbufs++; + _leave(" = 0"); + return 0; +} + +/* + * parse an RTM_GETLINK response for MTUs + */ +static int afs_rtm_getlink_if_parse(struct afs_rtm_desc *desc, + struct nlmsghdr *nlhdr) +{ + struct afs_interface *this; + struct ifinfomsg *ifi; + struct rtattr *rtattr; + const char *name; + size_t len, loop; + + ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr); + + _enter("{ix=%d}", ifi->ifi_index); + + for (loop = 0; loop < desc->nbufs; loop++) { + this = &desc->bufs[loop]; + if (this->index == ifi->ifi_index) + goto found; + } + + _leave(" = 0 [no match]"); + return 0; + +found: + if (ifi->ifi_type == ARPHRD_LOOPBACK && !desc->wantloopback) { + _leave(" = 0 [loopback]"); + return 0; + } + + rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg)); + len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg)); + + name = "unknown"; + for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) { + switch (rtattr->rta_type) { + case IFLA_MTU: + memcpy(&this->mtu, RTA_DATA(rtattr), 4); + break; + case IFLA_IFNAME: + name = RTA_DATA(rtattr); + break; + } + } + + _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u", + name, NIPQUAD(this->address), NIPQUAD(this->netmask), + this->mtu); + + _leave(" = 0"); + return 0; +} + +/* + * parse an RTM_GETLINK response for the MAC address belonging to the lowest + * non-internal interface + */ +static int afs_rtm_getlink_mac_parse(struct afs_rtm_desc *desc, + struct nlmsghdr *nlhdr) +{ + struct ifinfomsg *ifi; + struct rtattr *rtattr; + const char *name; + size_t remain, len; + bool set; + + ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr); + + _enter("{ix=%d}", ifi->ifi_index); + + if (ifi->ifi_index >= desc->mac_index) { + _leave(" = 0 [high]"); + return 0; + } + if (ifi->ifi_type == ARPHRD_LOOPBACK) { + _leave(" = 0 [loopback]"); + return 0; + } + + rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg)); + remain = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg)); + + name = "unknown"; + set = false; + for (; RTA_OK(rtattr, remain); rtattr = RTA_NEXT(rtattr, remain)) { + switch (rtattr->rta_type) { + case IFLA_ADDRESS: + len = RTA_PAYLOAD(rtattr); + memcpy(desc->mac, RTA_DATA(rtattr), + min_t(size_t, len, 6)); + desc->mac_index = ifi->ifi_index; + set = true; + break; + case IFLA_IFNAME: + name = RTA_DATA(rtattr); + break; + } + } + + if (set) + _debug("%s: %02x:%02x:%02x:%02x:%02x:%02x", + name, + desc->mac[0], desc->mac[1], desc->mac[2], + desc->mac[3], desc->mac[4], desc->mac[5]); + + _leave(" = 0"); + return 0; +} + +/* + * read the rtnetlink response and pass to parsing routine + */ +static int afs_read_rtm(struct afs_rtm_desc *desc) +{ + struct nlmsghdr *nlhdr, tmphdr; + struct msghdr msg; + struct kvec iov[1]; + void *data; + bool last = false; + int len, ret, remain; + + _enter(""); + + do { + /* first of all peek to see how big the packet is */ + memset(&msg, 0, sizeof(msg)); + iov[0].iov_base = &tmphdr; + iov[0].iov_len = sizeof(tmphdr); + len = kernel_recvmsg(desc->nlsock, &msg, iov, 1, + sizeof(tmphdr), MSG_PEEK | MSG_TRUNC); + if (len < 0) { + _leave(" = %d [peek]", len); + return len; + } + if (len == 0) + continue; + if (len < sizeof(tmphdr) || len < NLMSG_PAYLOAD(&tmphdr, 0)) { + _leave(" = -EMSGSIZE"); + return -EMSGSIZE; + } + + if (desc->datamax < len) { + kfree(desc->data); + desc->data = NULL; + data = kmalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + desc->data = data; + } + desc->datamax = len; + + /* read all the data from this packet */ + iov[0].iov_base = desc->data; + iov[0].iov_len = desc->datamax; + desc->datalen = kernel_recvmsg(desc->nlsock, &msg, iov, 1, + desc->datamax, 0); + if (desc->datalen < 0) { + _leave(" = %zd [recv]", desc->datalen); + return desc->datalen; + } + + nlhdr = desc->data; + + /* check if the header is valid */ + if (!NLMSG_OK(nlhdr, desc->datalen) || + nlhdr->nlmsg_type == NLMSG_ERROR) { + _leave(" = -EIO"); + return -EIO; + } + + /* see if this is the last message */ + if (nlhdr->nlmsg_type == NLMSG_DONE || + !(nlhdr->nlmsg_flags & NLM_F_MULTI)) + last = true; + + /* parse the bits we got this time */ + nlmsg_for_each_msg(nlhdr, desc->data, desc->datalen, remain) { + ret = desc->parse(desc, nlhdr); + if (ret < 0) { + _leave(" = %d [parse]", ret); + return ret; + } + } + + } while (!last); + + _leave(" = 0"); + return 0; +} + +/* + * list the interface bound addresses to get the address and netmask + */ +static int afs_rtm_getaddr(struct afs_rtm_desc *desc) +{ + struct msghdr msg; + struct kvec iov[1]; + int ret; + + struct { + struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO))); + struct ifaddrmsg addr_msg __attribute__((aligned(NLMSG_ALIGNTO))); + } request; + + _enter(""); + + memset(&request, 0, sizeof(request)); + + request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + request.nl_msg.nlmsg_type = RTM_GETADDR; + request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + request.nl_msg.nlmsg_seq = desc->msg_seq++; + request.nl_msg.nlmsg_pid = 0; + + memset(&msg, 0, sizeof(msg)); + iov[0].iov_base = &request; + iov[0].iov_len = sizeof(request); + + ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len); + _leave(" = %d", ret); + return ret; +} + +/* + * list the interface link statuses to get the MTUs + */ +static int afs_rtm_getlink(struct afs_rtm_desc *desc) +{ + struct msghdr msg; + struct kvec iov[1]; + int ret; + + struct { + struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO))); + struct ifinfomsg link_msg __attribute__((aligned(NLMSG_ALIGNTO))); + } request; + + _enter(""); + + memset(&request, 0, sizeof(request)); + + request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + request.nl_msg.nlmsg_type = RTM_GETLINK; + request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + request.nl_msg.nlmsg_seq = desc->msg_seq++; + request.nl_msg.nlmsg_pid = 0; + + memset(&msg, 0, sizeof(msg)); + iov[0].iov_base = &request; + iov[0].iov_len = sizeof(request); + + ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len); + _leave(" = %d", ret); + return ret; +} + +/* + * cull any interface records for which there isn't an MTU value + */ +static void afs_cull_interfaces(struct afs_rtm_desc *desc) +{ + struct afs_interface *bufs = desc->bufs; + size_t nbufs = desc->nbufs; + int loop, point = 0; + + _enter("{%zu}", nbufs); + + for (loop = 0; loop < nbufs; loop++) { + if (desc->bufs[loop].mtu != 0) { + if (loop != point) { + ASSERTCMP(loop, >, point); + bufs[point] = bufs[loop]; + } + point++; + } + } + + desc->nbufs = point; + _leave(" [%zu/%zu]", desc->nbufs, nbufs); +} + +/* + * get a list of this system's interface IPv4 addresses, netmasks and MTUs + * - returns the number of interface records in the buffer + */ +int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs, + bool wantloopback) +{ + struct afs_rtm_desc desc; + int ret, loop; + + _enter(""); + + memset(&desc, 0, sizeof(desc)); + desc.bufs = bufs; + desc.maxbufs = maxbufs; + desc.wantloopback = wantloopback; + + ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, + &desc.nlsock); + if (ret < 0) { + _leave(" = %d [sock]", ret); + return ret; + } + + /* issue RTM_GETADDR */ + desc.parse = afs_rtm_getaddr_parse; + ret = afs_rtm_getaddr(&desc); + if (ret < 0) + goto error; + ret = afs_read_rtm(&desc); + if (ret < 0) + goto error; + + /* issue RTM_GETLINK */ + desc.parse = afs_rtm_getlink_if_parse; + ret = afs_rtm_getlink(&desc); + if (ret < 0) + goto error; + ret = afs_read_rtm(&desc); + if (ret < 0) + goto error; + + afs_cull_interfaces(&desc); + ret = desc.nbufs; + + for (loop = 0; loop < ret; loop++) + _debug("[%d] "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u", + bufs[loop].index, + NIPQUAD(bufs[loop].address), + NIPQUAD(bufs[loop].netmask), + bufs[loop].mtu); + +error: + kfree(desc.data); + sock_release(desc.nlsock); + _leave(" = %d", ret); + return ret; +} + +/* + * get a MAC address from a random ethernet interface that has a real one + * - the buffer should be 6 bytes in size + */ +int afs_get_MAC_address(u8 mac[6]) +{ + struct afs_rtm_desc desc; + int ret; + + _enter(""); + + memset(&desc, 0, sizeof(desc)); + desc.mac = mac; + desc.mac_index = UINT_MAX; + + ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, + &desc.nlsock); + if (ret < 0) { + _leave(" = %d [sock]", ret); + return ret; + } + + /* issue RTM_GETLINK */ + desc.parse = afs_rtm_getlink_mac_parse; + ret = afs_rtm_getlink(&desc); + if (ret < 0) + goto error; + ret = afs_read_rtm(&desc); + if (ret < 0) + goto error; + + if (desc.mac_index < UINT_MAX) { + /* got a MAC address */ + _debug("[%d] %02x:%02x:%02x:%02x:%02x:%02x", + desc.mac_index, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } else { + ret = -ENONET; + } + +error: + sock_release(desc.nlsock); + _leave(" = %d", ret); + return ret; +} diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c index 7b0e319..36c1306 100644 --- a/fs/afs/vlclient.c +++ b/fs/afs/vlclient.c @@ -1,4 +1,4 @@ -/* vlclient.c: AFS Volume Location Service client +/* AFS Volume Location Service client * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -11,247 +11,76 @@ #include #include -#include -#include -#include -#include -#include "server.h" -#include "volume.h" -#include "vlclient.h" -#include "kafsasyncd.h" -#include "kafstimod.h" -#include "errors.h" #include "internal.h" -#define VLGETENTRYBYID 503 /* AFS Get Cache Entry By ID operation ID */ -#define VLGETENTRYBYNAME 504 /* AFS Get Cache Entry By Name operation ID */ -#define VLPROBE 514 /* AFS Probe Volume Location Service operation ID */ - -static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call); -static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call); - -/*****************************************************************************/ /* - * map afs VL abort codes to/from Linux error codes - * - called with call->lock held + * map volume locator abort codes to error codes */ -static void afs_rxvl_aemap(struct rxrpc_call *call) +static int afs_vl_abort_to_error(u32 abort_code) { - int err; - - _enter("{%u,%u,%d}", - call->app_err_state, call->app_abort_code, call->app_errno); - - switch (call->app_err_state) { - case RXRPC_ESTATE_LOCAL_ABORT: - call->app_abort_code = -call->app_errno; - return; - - case RXRPC_ESTATE_PEER_ABORT: - switch (call->app_abort_code) { - case AFSVL_IDEXIST: err = -EEXIST; break; - case AFSVL_IO: err = -EREMOTEIO; break; - case AFSVL_NAMEEXIST: err = -EEXIST; break; - case AFSVL_CREATEFAIL: err = -EREMOTEIO; break; - case AFSVL_NOENT: err = -ENOMEDIUM; break; - case AFSVL_EMPTY: err = -ENOMEDIUM; break; - case AFSVL_ENTDELETED: err = -ENOMEDIUM; break; - case AFSVL_BADNAME: err = -EINVAL; break; - case AFSVL_BADINDEX: err = -EINVAL; break; - case AFSVL_BADVOLTYPE: err = -EINVAL; break; - case AFSVL_BADSERVER: err = -EINVAL; break; - case AFSVL_BADPARTITION: err = -EINVAL; break; - case AFSVL_REPSFULL: err = -EFBIG; break; - case AFSVL_NOREPSERVER: err = -ENOENT; break; - case AFSVL_DUPREPSERVER: err = -EEXIST; break; - case AFSVL_RWNOTFOUND: err = -ENOENT; break; - case AFSVL_BADREFCOUNT: err = -EINVAL; break; - case AFSVL_SIZEEXCEEDED: err = -EINVAL; break; - case AFSVL_BADENTRY: err = -EINVAL; break; - case AFSVL_BADVOLIDBUMP: err = -EINVAL; break; - case AFSVL_IDALREADYHASHED: err = -EINVAL; break; - case AFSVL_ENTRYLOCKED: err = -EBUSY; break; - case AFSVL_BADVOLOPER: err = -EBADRQC; break; - case AFSVL_BADRELLOCKTYPE: err = -EINVAL; break; - case AFSVL_RERELEASE: err = -EREMOTEIO; break; - case AFSVL_BADSERVERFLAG: err = -EINVAL; break; - case AFSVL_PERM: err = -EACCES; break; - case AFSVL_NOMEM: err = -EREMOTEIO; break; - default: - err = afs_abort_to_error(call->app_abort_code); - break; - } - call->app_errno = err; - return; - + _enter("%u", abort_code); + + switch (abort_code) { + case AFSVL_IDEXIST: return -EEXIST; + case AFSVL_IO: return -EREMOTEIO; + case AFSVL_NAMEEXIST: return -EEXIST; + case AFSVL_CREATEFAIL: return -EREMOTEIO; + case AFSVL_NOENT: return -ENOMEDIUM; + case AFSVL_EMPTY: return -ENOMEDIUM; + case AFSVL_ENTDELETED: return -ENOMEDIUM; + case AFSVL_BADNAME: return -EINVAL; + case AFSVL_BADINDEX: return -EINVAL; + case AFSVL_BADVOLTYPE: return -EINVAL; + case AFSVL_BADSERVER: return -EINVAL; + case AFSVL_BADPARTITION: return -EINVAL; + case AFSVL_REPSFULL: return -EFBIG; + case AFSVL_NOREPSERVER: return -ENOENT; + case AFSVL_DUPREPSERVER: return -EEXIST; + case AFSVL_RWNOTFOUND: return -ENOENT; + case AFSVL_BADREFCOUNT: return -EINVAL; + case AFSVL_SIZEEXCEEDED: return -EINVAL; + case AFSVL_BADENTRY: return -EINVAL; + case AFSVL_BADVOLIDBUMP: return -EINVAL; + case AFSVL_IDALREADYHASHED: return -EINVAL; + case AFSVL_ENTRYLOCKED: return -EBUSY; + case AFSVL_BADVOLOPER: return -EBADRQC; + case AFSVL_BADRELLOCKTYPE: return -EINVAL; + case AFSVL_RERELEASE: return -EREMOTEIO; + case AFSVL_BADSERVERFLAG: return -EINVAL; + case AFSVL_PERM: return -EACCES; + case AFSVL_NOMEM: return -EREMOTEIO; default: - return; + return afs_abort_to_error(abort_code); } -} /* end afs_rxvl_aemap() */ +} -#if 0 -/*****************************************************************************/ /* - * probe a volume location server to see if it is still alive -- unused + * deliver reply data to a VL.GetEntryByXXX call */ -static int afs_rxvl_probe(struct afs_server *server, int alloc_flags) +static int afs_deliver_vl_get_entry_by_xxx(struct afs_call *call, + struct sk_buff *skb, bool last) { - struct rxrpc_connection *conn; - struct rxrpc_call *call; - struct kvec piov[1]; - size_t sent; - int ret; - __be32 param[1]; - - DECLARE_WAITQUEUE(myself, current); - - /* get hold of the vlserver connection */ - ret = afs_server_get_vlconn(server, &conn); - if (ret < 0) - goto out; - - /* create a call through that connection */ - ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; - } - call->app_opcode = VLPROBE; - - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); - - /* marshall the parameters */ - param[0] = htonl(VLPROBE); - piov[0].iov_len = sizeof(param); - piov[0].iov_base = param; - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, - alloc_flags, 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY || - signal_pending(current)) - break; - schedule(); - } - set_current_state(TASK_RUNNING); - - ret = -EINTR; - if (signal_pending(current)) - goto abort; - - switch (call->app_call_state) { - case RXRPC_CSTATE_ERROR: - ret = call->app_errno; - goto out_unwait; - - case RXRPC_CSTATE_CLNT_GOT_REPLY: - ret = 0; - goto out_unwait; - - default: - BUG(); - } - - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - rxrpc_put_connection(conn); - out: - return ret; + struct afs_cache_vlocation *entry; + __be32 *bp; + u32 tmp; + int loop; -} /* end afs_rxvl_probe() */ -#endif + _enter(",,%u", last); -/*****************************************************************************/ -/* - * look up a volume location database entry by name - */ -int afs_rxvl_get_entry_by_name(struct afs_server *server, - const char *volname, - unsigned volnamesz, - struct afs_cache_vlocation *entry) -{ - DECLARE_WAITQUEUE(myself, current); - - struct rxrpc_connection *conn; - struct rxrpc_call *call; - struct kvec piov[3]; - unsigned tmp; - size_t sent; - int ret, loop; - __be32 *bp, param[2], zero; - - _enter(",%*.*s,%u,", volnamesz, volnamesz, volname, volnamesz); - - memset(entry, 0, sizeof(*entry)); - - /* get hold of the vlserver connection */ - ret = afs_server_get_vlconn(server, &conn); - if (ret < 0) - goto out; - - /* create a call through that connection */ - ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; - } - call->app_opcode = VLGETENTRYBYNAME; + afs_transfer_reply(call, skb); + if (!last) + return 0; - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); + if (call->reply_size != call->reply_max) + return -EBADMSG; - /* marshall the parameters */ - piov[1].iov_len = volnamesz; - piov[1].iov_base = (char *) volname; - - zero = 0; - piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; - piov[2].iov_base = &zero; - - param[0] = htonl(VLGETENTRYBYNAME); - param[1] = htonl(piov[1].iov_len); - - piov[0].iov_len = sizeof(param); - piov[0].iov_base = param; - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - bp = rxrpc_call_alloc_scratch(call, 384); - - ret = rxrpc_call_read_data(call, bp, 384, - RXRPC_CALL_READ_BLOCK | - RXRPC_CALL_READ_ALL); - if (ret < 0) { - if (ret == -ECONNABORTED) { - ret = call->app_errno; - goto out_unwait; - } - goto abort; - } + /* unmarshall the reply once we've received all of it */ + entry = call->reply; + bp = call->buffer; - /* unmarshall the reply */ for (loop = 0; loop < 64; loop++) entry->name[loop] = ntohl(*bp++); + entry->name[loop] = 0; bp++; /* final NUL */ bp++; /* type */ @@ -264,6 +93,7 @@ int afs_rxvl_get_entry_by_name(struct af for (loop = 0; loop < 8; loop++) { tmp = ntohl(*bp++); + entry->srvtmask[loop] = 0; if (tmp & AFS_VLSF_RWVOL) entry->srvtmask[loop] |= AFS_VOL_VTM_RW; if (tmp & AFS_VLSF_ROVOL) @@ -279,417 +109,110 @@ int afs_rxvl_get_entry_by_name(struct af bp++; /* clone ID */ tmp = ntohl(*bp++); /* flags */ + entry->vidmask = 0; if (tmp & AFS_VLF_RWEXISTS) entry->vidmask |= AFS_VOL_VTM_RW; if (tmp & AFS_VLF_ROEXISTS) entry->vidmask |= AFS_VOL_VTM_RO; if (tmp & AFS_VLF_BACKEXISTS) entry->vidmask |= AFS_VOL_VTM_BAK; - - ret = -ENOMEDIUM; if (!entry->vidmask) - goto abort; - - /* success */ - entry->rtime = get_seconds(); - ret = 0; - - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - rxrpc_put_connection(conn); - out: - _leave(" = %d", ret); - return ret; - - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - goto out_unwait; -} /* end afs_rxvl_get_entry_by_name() */ - -/*****************************************************************************/ -/* - * look up a volume location database entry by ID - */ -int afs_rxvl_get_entry_by_id(struct afs_server *server, - afs_volid_t volid, - afs_voltype_t voltype, - struct afs_cache_vlocation *entry) -{ - DECLARE_WAITQUEUE(myself, current); - - struct rxrpc_connection *conn; - struct rxrpc_call *call; - struct kvec piov[1]; - unsigned tmp; - size_t sent; - int ret, loop; - __be32 *bp, param[3]; - - _enter(",%x,%d,", volid, voltype); - - memset(entry, 0, sizeof(*entry)); - - /* get hold of the vlserver connection */ - ret = afs_server_get_vlconn(server, &conn); - if (ret < 0) - goto out; - - /* create a call through that connection */ - ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call); - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - goto out_put_conn; - } - call->app_opcode = VLGETENTRYBYID; - - /* we want to get event notifications from the call */ - add_wait_queue(&call->waitq, &myself); - - /* marshall the parameters */ - param[0] = htonl(VLGETENTRYBYID); - param[1] = htonl(volid); - param[2] = htonl(voltype); - - piov[0].iov_len = sizeof(param); - piov[0].iov_base = param; - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) - goto abort; - - /* wait for the reply to completely arrive */ - bp = rxrpc_call_alloc_scratch(call, 384); - - ret = rxrpc_call_read_data(call, bp, 384, - RXRPC_CALL_READ_BLOCK | - RXRPC_CALL_READ_ALL); - if (ret < 0) { - if (ret == -ECONNABORTED) { - ret = call->app_errno; - goto out_unwait; - } - goto abort; - } - - /* unmarshall the reply */ - for (loop = 0; loop < 64; loop++) - entry->name[loop] = ntohl(*bp++); - bp++; /* final NUL */ + return -EBADMSG; - bp++; /* type */ - entry->nservers = ntohl(*bp++); - - for (loop = 0; loop < 8; loop++) - entry->servers[loop].s_addr = *bp++; - - bp += 8; /* partition IDs */ + _leave(" = 0 [done]"); + return 0; +} - for (loop = 0; loop < 8; loop++) { - tmp = ntohl(*bp++); - if (tmp & AFS_VLSF_RWVOL) - entry->srvtmask[loop] |= AFS_VOL_VTM_RW; - if (tmp & AFS_VLSF_ROVOL) - entry->srvtmask[loop] |= AFS_VOL_VTM_RO; - if (tmp & AFS_VLSF_BACKVOL) - entry->srvtmask[loop] |= AFS_VOL_VTM_BAK; - } - - entry->vid[0] = ntohl(*bp++); - entry->vid[1] = ntohl(*bp++); - entry->vid[2] = ntohl(*bp++); - - bp++; /* clone ID */ - - tmp = ntohl(*bp++); /* flags */ - if (tmp & AFS_VLF_RWEXISTS) - entry->vidmask |= AFS_VOL_VTM_RW; - if (tmp & AFS_VLF_ROEXISTS) - entry->vidmask |= AFS_VOL_VTM_RO; - if (tmp & AFS_VLF_BACKEXISTS) - entry->vidmask |= AFS_VOL_VTM_BAK; - - ret = -ENOMEDIUM; - if (!entry->vidmask) - goto abort; - -#if 0 /* TODO: remove */ - entry->nservers = 3; - entry->servers[0].s_addr = htonl(0xac101249); - entry->servers[1].s_addr = htonl(0xac101243); - entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/); - - entry->srvtmask[0] = AFS_VOL_VTM_RO; - entry->srvtmask[1] = AFS_VOL_VTM_RO; - entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW; -#endif - - /* success */ - entry->rtime = get_seconds(); - ret = 0; - - out_unwait: - set_current_state(TASK_RUNNING); - remove_wait_queue(&call->waitq, &myself); - rxrpc_put_call(call); - out_put_conn: - rxrpc_put_connection(conn); - out: - _leave(" = %d", ret); - return ret; - - abort: - set_current_state(TASK_UNINTERRUPTIBLE); - rxrpc_call_abort(call, ret); - schedule(); - goto out_unwait; -} /* end afs_rxvl_get_entry_by_id() */ - -/*****************************************************************************/ /* - * look up a volume location database entry by ID asynchronously + * VL.GetEntryByName operation type */ -int afs_rxvl_get_entry_by_id_async(struct afs_async_op *op, - afs_volid_t volid, - afs_voltype_t voltype) -{ - struct rxrpc_connection *conn; - struct rxrpc_call *call; - struct kvec piov[1]; - size_t sent; - int ret; - __be32 param[3]; - - _enter(",%x,%d,", volid, voltype); - - /* get hold of the vlserver connection */ - ret = afs_server_get_vlconn(op->server, &conn); - if (ret < 0) { - _leave(" = %d", ret); - return ret; - } - - /* create a call through that connection */ - ret = rxrpc_create_call(conn, - afs_rxvl_get_entry_by_id_attn, - afs_rxvl_get_entry_by_id_error, - afs_rxvl_aemap, - &op->call); - rxrpc_put_connection(conn); - - if (ret < 0) { - printk("kAFS: Unable to create call: %d\n", ret); - _leave(" = %d", ret); - return ret; - } +static const struct afs_call_type afs_RXVLGetEntryByName = { + .name = "VL.GetEntryByName", + .deliver = afs_deliver_vl_get_entry_by_xxx, + .abort_to_error = afs_vl_abort_to_error, + .destructor = afs_flat_call_destructor, +}; - op->call->app_opcode = VLGETENTRYBYID; - op->call->app_user = op; - - call = op->call; - rxrpc_get_call(call); - - /* send event notifications from the call to kafsasyncd */ - afs_kafsasyncd_begin_op(op); - - /* marshall the parameters */ - param[0] = htonl(VLGETENTRYBYID); - param[1] = htonl(volid); - param[2] = htonl(voltype); - - piov[0].iov_len = sizeof(param); - piov[0].iov_base = param; - - /* allocate result read buffer in scratch space */ - call->app_scr_ptr = rxrpc_call_alloc_scratch(op->call, 384); - - /* send the parameters to the server */ - ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, - 0, &sent); - if (ret < 0) { - rxrpc_call_abort(call, ret); /* handle from kafsasyncd */ - ret = 0; - goto out; - } - - /* wait for the reply to completely arrive */ - ret = rxrpc_call_read_data(call, call->app_scr_ptr, 384, 0); - switch (ret) { - case 0: - case -EAGAIN: - case -ECONNABORTED: - ret = 0; - break; /* all handled by kafsasyncd */ - - default: - rxrpc_call_abort(call, ret); /* make kafsasyncd handle it */ - ret = 0; - break; - } - - out: - rxrpc_put_call(call); - _leave(" = %d", ret); - return ret; - -} /* end afs_rxvl_get_entry_by_id_async() */ +/* + * VL.GetEntryById operation type + */ +static const struct afs_call_type afs_RXVLGetEntryById = { + .name = "VL.GetEntryById", + .deliver = afs_deliver_vl_get_entry_by_xxx, + .abort_to_error = afs_vl_abort_to_error, + .destructor = afs_flat_call_destructor, +}; -/*****************************************************************************/ /* - * attend to the asynchronous get VLDB entry by ID + * dispatch a get volume entry by name operation */ -int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *op, - struct afs_cache_vlocation *entry) +int afs_vl_get_entry_by_name(struct in_addr *addr, + struct key *key, + const char *volname, + struct afs_cache_vlocation *entry, + const struct afs_wait_mode *wait_mode) { + struct afs_call *call; + size_t volnamesz, reqsz, padsz; __be32 *bp; - __u32 tmp; - int loop, ret; - - _enter("{op=%p cst=%u}", op, op->call->app_call_state); - - memset(entry, 0, sizeof(*entry)); - - if (op->call->app_call_state == RXRPC_CSTATE_COMPLETE) { - /* operation finished */ - afs_kafsasyncd_terminate_op(op); - - bp = op->call->app_scr_ptr; - - /* unmarshall the reply */ - for (loop = 0; loop < 64; loop++) - entry->name[loop] = ntohl(*bp++); - bp++; /* final NUL */ - - bp++; /* type */ - entry->nservers = ntohl(*bp++); - - for (loop = 0; loop < 8; loop++) - entry->servers[loop].s_addr = *bp++; - - bp += 8; /* partition IDs */ - - for (loop = 0; loop < 8; loop++) { - tmp = ntohl(*bp++); - if (tmp & AFS_VLSF_RWVOL) - entry->srvtmask[loop] |= AFS_VOL_VTM_RW; - if (tmp & AFS_VLSF_ROVOL) - entry->srvtmask[loop] |= AFS_VOL_VTM_RO; - if (tmp & AFS_VLSF_BACKVOL) - entry->srvtmask[loop] |= AFS_VOL_VTM_BAK; - } - - entry->vid[0] = ntohl(*bp++); - entry->vid[1] = ntohl(*bp++); - entry->vid[2] = ntohl(*bp++); - - bp++; /* clone ID */ - - tmp = ntohl(*bp++); /* flags */ - if (tmp & AFS_VLF_RWEXISTS) - entry->vidmask |= AFS_VOL_VTM_RW; - if (tmp & AFS_VLF_ROEXISTS) - entry->vidmask |= AFS_VOL_VTM_RO; - if (tmp & AFS_VLF_BACKEXISTS) - entry->vidmask |= AFS_VOL_VTM_BAK; - - ret = -ENOMEDIUM; - if (!entry->vidmask) { - rxrpc_call_abort(op->call, ret); - goto done; - } - -#if 0 /* TODO: remove */ - entry->nservers = 3; - entry->servers[0].s_addr = htonl(0xac101249); - entry->servers[1].s_addr = htonl(0xac101243); - entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/); - - entry->srvtmask[0] = AFS_VOL_VTM_RO; - entry->srvtmask[1] = AFS_VOL_VTM_RO; - entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW; -#endif - - /* success */ - entry->rtime = get_seconds(); - ret = 0; - goto done; - } - if (op->call->app_call_state == RXRPC_CSTATE_ERROR) { - /* operation error */ - ret = op->call->app_errno; - goto done; - } + _enter(""); - _leave(" = -EAGAIN"); - return -EAGAIN; + volnamesz = strlen(volname); + padsz = (4 - (volnamesz & 3)) & 3; + reqsz = 8 + volnamesz + padsz; - done: - rxrpc_put_call(op->call); - op->call = NULL; - _leave(" = %d", ret); - return ret; -} /* end afs_rxvl_get_entry_by_id_async2() */ + call = afs_alloc_flat_call(&afs_RXVLGetEntryByName, reqsz, 384); + if (!call) + return -ENOMEM; -/*****************************************************************************/ -/* - * handle attention events on an async get-entry-by-ID op - * - called from krxiod - */ -static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call) -{ - struct afs_async_op *op = call->app_user; - - _enter("{op=%p cst=%u}", op, call->app_call_state); - - switch (call->app_call_state) { - case RXRPC_CSTATE_COMPLETE: - afs_kafsasyncd_attend_op(op); - break; - case RXRPC_CSTATE_CLNT_RCV_REPLY: - if (call->app_async_read) - break; - case RXRPC_CSTATE_CLNT_GOT_REPLY: - if (call->app_read_count == 0) - break; - printk("kAFS: Reply bigger than expected" - " {cst=%u asyn=%d mark=%Zu rdy=%Zu pr=%u%s}", - call->app_call_state, - call->app_async_read, - call->app_mark, - call->app_ready_qty, - call->pkt_rcv_count, - call->app_last_rcv ? " last" : ""); - - rxrpc_call_abort(call, -EBADMSG); - break; - default: - BUG(); - } + call->key = key; + call->reply = entry; + call->service_id = VL_SERVICE; + call->port = htons(AFS_VL_PORT); - _leave(""); + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(VLGETENTRYBYNAME); + *bp++ = htonl(volnamesz); + memcpy(bp, volname, volnamesz); + if (padsz > 0) + memset((void *) bp + volnamesz, 0, padsz); -} /* end afs_rxvl_get_entry_by_id_attn() */ + /* initiate the call */ + return afs_make_call(addr, call, GFP_KERNEL, wait_mode); +} -/*****************************************************************************/ /* - * handle error events on an async get-entry-by-ID op - * - called from krxiod + * dispatch a get volume entry by ID operation */ -static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call) +int afs_vl_get_entry_by_id(struct in_addr *addr, + struct key *key, + afs_volid_t volid, + afs_voltype_t voltype, + struct afs_cache_vlocation *entry, + const struct afs_wait_mode *wait_mode) { - struct afs_async_op *op = call->app_user; + struct afs_call *call; + __be32 *bp; - _enter("{op=%p cst=%u}", op, call->app_call_state); + _enter(""); - afs_kafsasyncd_attend_op(op); + call = afs_alloc_flat_call(&afs_RXVLGetEntryById, 12, 384); + if (!call) + return -ENOMEM; - _leave(""); + call->key = key; + call->reply = entry; + call->service_id = VL_SERVICE; + call->port = htons(AFS_VL_PORT); -} /* end afs_rxvl_get_entry_by_id_error() */ + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(VLGETENTRYBYID); + *bp++ = htonl(volid); + *bp = htonl(voltype); + + /* initiate the call */ + return afs_make_call(addr, call, GFP_KERNEL, wait_mode); +} diff --git a/fs/afs/vlclient.h b/fs/afs/vlclient.h deleted file mode 100644 index e3d6011..0000000 --- a/fs/afs/vlclient.h +++ /dev/null @@ -1,93 +0,0 @@ -/* vlclient.h: Volume Location Service client interface - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_VLCLIENT_H -#define _LINUX_AFS_VLCLIENT_H - -#include "types.h" - -enum AFSVL_Errors { - AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */ - AFSVL_IO = 363521, /* I/O related error */ - AFSVL_NAMEEXIST = 363522, /* Volume name entry exists in vl database */ - AFSVL_CREATEFAIL = 363523, /* Internal creation failure */ - AFSVL_NOENT = 363524, /* No such entry */ - AFSVL_EMPTY = 363525, /* Vl database is empty */ - AFSVL_ENTDELETED = 363526, /* Entry is deleted (soft delete) */ - AFSVL_BADNAME = 363527, /* Volume name is illegal */ - AFSVL_BADINDEX = 363528, /* Index is out of range */ - AFSVL_BADVOLTYPE = 363529, /* Bad volume type */ - AFSVL_BADSERVER = 363530, /* Illegal server number (out of range) */ - AFSVL_BADPARTITION = 363531, /* Bad partition number */ - AFSVL_REPSFULL = 363532, /* Run out of space for Replication sites */ - AFSVL_NOREPSERVER = 363533, /* No such Replication server site exists */ - AFSVL_DUPREPSERVER = 363534, /* Replication site already exists */ - AFSVL_RWNOTFOUND = 363535, /* Parent R/W entry not found */ - AFSVL_BADREFCOUNT = 363536, /* Illegal Reference Count number */ - AFSVL_SIZEEXCEEDED = 363537, /* Vl size for attributes exceeded */ - AFSVL_BADENTRY = 363538, /* Bad incoming vl entry */ - AFSVL_BADVOLIDBUMP = 363539, /* Illegal max volid increment */ - AFSVL_IDALREADYHASHED = 363540, /* RO/BACK id already hashed */ - AFSVL_ENTRYLOCKED = 363541, /* Vl entry is already locked */ - AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */ - AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */ - AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */ - AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server ag */ - AFSVL_PERM = 363546, /* No permission access */ - AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */ -}; - -/* maps to "struct vldbentry" in vvl-spec.pdf */ -struct afs_vldbentry { - char name[65]; /* name of volume (including NUL char) */ - afs_voltype_t type; /* volume type */ - unsigned num_servers; /* num servers that hold instances of this vol */ - unsigned clone_id; /* cloning ID */ - - unsigned flags; -#define AFS_VLF_RWEXISTS 0x1000 /* R/W volume exists */ -#define AFS_VLF_ROEXISTS 0x2000 /* R/O volume exists */ -#define AFS_VLF_BACKEXISTS 0x4000 /* backup volume exists */ - - afs_volid_t volume_ids[3]; /* volume IDs */ - - struct { - struct in_addr addr; /* server address */ - unsigned partition; /* partition ID on this server */ - unsigned flags; /* server specific flags */ -#define AFS_VLSF_NEWREPSITE 0x0001 /* unused */ -#define AFS_VLSF_ROVOL 0x0002 /* this server holds a R/O instance of the volume */ -#define AFS_VLSF_RWVOL 0x0004 /* this server holds a R/W instance of the volume */ -#define AFS_VLSF_BACKVOL 0x0008 /* this server holds a backup instance of the volume */ - } servers[8]; - -}; - -/* look up a volume location database entry by name */ -extern int afs_rxvl_get_entry_by_name(struct afs_server *server, - const char *volname, - unsigned volnamesz, - struct afs_cache_vlocation *entry); - -/* look up a volume location database entry by ID */ -extern int afs_rxvl_get_entry_by_id(struct afs_server *server, - afs_volid_t volid, - afs_voltype_t voltype, - struct afs_cache_vlocation *entry); - -extern int afs_rxvl_get_entry_by_id_async(struct afs_async_op *op, - afs_volid_t volid, - afs_voltype_t voltype); - -extern int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *op, - struct afs_cache_vlocation *entry); - -#endif /* _LINUX_AFS_VLCLIENT_H */ diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index 782ee7c..6c8e95a 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c @@ -1,6 +1,6 @@ -/* vlocation.c: volume location management +/* AFS volume location management * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -12,131 +12,61 @@ #include #include #include -#include -#include -#include -#include "volume.h" -#include "cell.h" -#include "cmservice.h" -#include "fsclient.h" -#include "vlclient.h" -#include "kafstimod.h" -#include #include "internal.h" -#define AFS_VLDB_TIMEOUT HZ*1000 +unsigned afs_vlocation_timeout = 10; /* volume location timeout in seconds */ +unsigned afs_vlocation_update_timeout = 10 * 60; -static void afs_vlocation_update_timer(struct afs_timer *timer); -static void afs_vlocation_update_attend(struct afs_async_op *op); -static void afs_vlocation_update_discard(struct afs_async_op *op); -static void __afs_put_vlocation(struct afs_vlocation *vlocation); +static void afs_vlocation_reaper(struct work_struct *); +static void afs_vlocation_updater(struct work_struct *); -static void __afs_vlocation_timeout(struct afs_timer *timer) -{ - struct afs_vlocation *vlocation = - list_entry(timer, struct afs_vlocation, timeout); - - _debug("VL TIMEOUT [%s{u=%d}]", - vlocation->vldb.name, atomic_read(&vlocation->usage)); - - afs_vlocation_do_timeout(vlocation); -} - -static const struct afs_timer_ops afs_vlocation_timer_ops = { - .timed_out = __afs_vlocation_timeout, -}; +static LIST_HEAD(afs_vlocation_updates); +static LIST_HEAD(afs_vlocation_graveyard); +static DEFINE_SPINLOCK(afs_vlocation_updates_lock); +static DEFINE_SPINLOCK(afs_vlocation_graveyard_lock); +static DECLARE_DELAYED_WORK(afs_vlocation_reap, afs_vlocation_reaper); +static DECLARE_DELAYED_WORK(afs_vlocation_update, afs_vlocation_updater); +static struct workqueue_struct *afs_vlocation_update_worker; -static const struct afs_timer_ops afs_vlocation_update_timer_ops = { - .timed_out = afs_vlocation_update_timer, -}; - -static const struct afs_async_op_ops afs_vlocation_update_op_ops = { - .attend = afs_vlocation_update_attend, - .discard = afs_vlocation_update_discard, -}; - -static LIST_HEAD(afs_vlocation_update_pendq); /* queue of VLs awaiting update */ -static struct afs_vlocation *afs_vlocation_update; /* VL currently being updated */ -static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */ - -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_vlocation_cache_match(void *target, - const void *entry); -static void afs_vlocation_cache_update(void *source, void *entry); - -struct cachefs_index_def afs_vlocation_cache_index_def = { - .name = "vldb", - .data_size = sizeof(struct afs_cache_vlocation), - .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, - .match = afs_vlocation_cache_match, - .update = afs_vlocation_cache_update, -}; -#endif - -/*****************************************************************************/ /* * iterate through the VL servers in a cell until one of them admits knowing * about the volume in question - * - caller must have cell->vl_sem write-locked */ -static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation, - const char *name, - unsigned namesz, +static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl, + struct key *key, struct afs_cache_vlocation *vldb) { - struct afs_server *server = NULL; - struct afs_cell *cell = vlocation->cell; + struct afs_cell *cell = vl->cell; + struct in_addr addr; int count, ret; - _enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz); + _enter("%s,%s", cell->name, vl->vldb.name); + down_write(&vl->cell->vl_sem); ret = -ENOMEDIUM; for (count = cell->vl_naddrs; count > 0; count--) { - _debug("CellServ[%hu]: %08x", - cell->vl_curr_svix, - cell->vl_addrs[cell->vl_curr_svix].s_addr); - - /* try and create a server */ - ret = afs_server_lookup(cell, - &cell->vl_addrs[cell->vl_curr_svix], - &server); - switch (ret) { - case 0: - break; - case -ENOMEM: - case -ENONET: - goto out; - default: - goto rotate; - } + addr = cell->vl_addrs[cell->vl_curr_svix]; + + _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr); /* attempt to access the VL server */ - ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb); + ret = afs_vl_get_entry_by_name(&addr, key, vl->vldb.name, vldb, + &afs_sync_call); switch (ret) { case 0: - afs_put_server(server); goto out; case -ENOMEM: case -ENONET: case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: - down_write(&server->sem); - if (server->vlserver) { - rxrpc_put_connection(server->vlserver); - server->vlserver = NULL; - } - up_write(&server->sem); - afs_put_server(server); if (ret == -ENOMEM || ret == -ENONET) goto out; goto rotate; case -ENOMEDIUM: - afs_put_server(server); goto out; default: - afs_put_server(server); - ret = -ENOMEDIUM; + ret = -EIO; goto rotate; } @@ -146,76 +76,66 @@ static int afs_vlocation_access_vl_by_na cell->vl_curr_svix %= cell->vl_naddrs; } - out: +out: + up_write(&vl->cell->vl_sem); _leave(" = %d", ret); return ret; +} -} /* end afs_vlocation_access_vl_by_name() */ - -/*****************************************************************************/ /* * iterate through the VL servers in a cell until one of them admits knowing * about the volume in question - * - caller must have cell->vl_sem write-locked */ -static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation, +static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl, + struct key *key, afs_volid_t volid, afs_voltype_t voltype, struct afs_cache_vlocation *vldb) { - struct afs_server *server = NULL; - struct afs_cell *cell = vlocation->cell; + struct afs_cell *cell = vl->cell; + struct in_addr addr; int count, ret; _enter("%s,%x,%d,", cell->name, volid, voltype); + down_write(&vl->cell->vl_sem); ret = -ENOMEDIUM; for (count = cell->vl_naddrs; count > 0; count--) { - _debug("CellServ[%hu]: %08x", - cell->vl_curr_svix, - cell->vl_addrs[cell->vl_curr_svix].s_addr); - - /* try and create a server */ - ret = afs_server_lookup(cell, - &cell->vl_addrs[cell->vl_curr_svix], - &server); - switch (ret) { - case 0: - break; - case -ENOMEM: - case -ENONET: - goto out; - default: - goto rotate; - } + addr = cell->vl_addrs[cell->vl_curr_svix]; + + _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr); /* attempt to access the VL server */ - ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb); + ret = afs_vl_get_entry_by_id(&addr, key, volid, voltype, vldb, + &afs_sync_call); switch (ret) { case 0: - afs_put_server(server); goto out; case -ENOMEM: case -ENONET: case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: - down_write(&server->sem); - if (server->vlserver) { - rxrpc_put_connection(server->vlserver); - server->vlserver = NULL; - } - up_write(&server->sem); - afs_put_server(server); if (ret == -ENOMEM || ret == -ENONET) goto out; goto rotate; + case -EBUSY: + vl->upd_busy_cnt++; + if (vl->upd_busy_cnt <= 3) { + if (vl->upd_busy_cnt > 1) { + /* second+ BUSY - sleep a little bit */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + __set_current_state(TASK_RUNNING); + } + continue; + } + break; case -ENOMEDIUM: - afs_put_server(server); - goto out; + vl->upd_rej_cnt++; + goto rotate; default: - afs_put_server(server); - ret = -ENOMEDIUM; + ret = -EIO; goto rotate; } @@ -223,729 +143,579 @@ static int afs_vlocation_access_vl_by_id rotate: cell->vl_curr_svix++; cell->vl_curr_svix %= cell->vl_naddrs; + vl->upd_busy_cnt = 0; } - out: +out: + if (ret < 0 && vl->upd_rej_cnt > 0) { + printk(KERN_NOTICE "kAFS:" + " Active volume no longer valid '%s'\n", + vl->vldb.name); + vl->valid = 0; + ret = -ENOMEDIUM; + } + + up_write(&vl->cell->vl_sem); _leave(" = %d", ret); return ret; +} -} /* end afs_vlocation_access_vl_by_id() */ - -/*****************************************************************************/ /* - * lookup volume location - * - caller must have cell->vol_sem write-locked - * - iterate through the VL servers in a cell until one of them admits knowing - * about the volume in question - * - lookup in the local cache if not able to find on the VL server - * - insert/update in the local cache if did get a VL response + * allocate a volume location record */ -int afs_vlocation_lookup(struct afs_cell *cell, - const char *name, - unsigned namesz, - struct afs_vlocation **_vlocation) +static struct afs_vlocation *afs_vlocation_alloc(struct afs_cell *cell, + const char *name, + size_t namesz) { - struct afs_cache_vlocation vldb; - struct afs_vlocation *vlocation; - afs_voltype_t voltype; - afs_volid_t vid; - int active = 0, ret; - - _enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz); - - if (namesz > sizeof(vlocation->vldb.name)) { - _leave(" = -ENAMETOOLONG"); - return -ENAMETOOLONG; - } - - /* search the cell's active list first */ - list_for_each_entry(vlocation, &cell->vl_list, link) { - if (namesz < sizeof(vlocation->vldb.name) && - vlocation->vldb.name[namesz] != '\0') - continue; - - if (memcmp(vlocation->vldb.name, name, namesz) == 0) - goto found_in_memory; - } - - /* search the cell's graveyard list second */ - spin_lock(&cell->vl_gylock); - list_for_each_entry(vlocation, &cell->vl_graveyard, link) { - if (namesz < sizeof(vlocation->vldb.name) && - vlocation->vldb.name[namesz] != '\0') - continue; - - if (memcmp(vlocation->vldb.name, name, namesz) == 0) - goto found_in_graveyard; - } - spin_unlock(&cell->vl_gylock); - - /* not in the cell's in-memory lists - create a new record */ - vlocation = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL); - if (!vlocation) - return -ENOMEM; - - atomic_set(&vlocation->usage, 1); - INIT_LIST_HEAD(&vlocation->link); - rwlock_init(&vlocation->lock); - memcpy(vlocation->vldb.name, name, namesz); - - afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops); - afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops); - afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops); - - afs_get_cell(cell); - vlocation->cell = cell; - - list_add_tail(&vlocation->link, &cell->vl_list); - -#ifdef AFS_CACHING_SUPPORT - /* we want to store it in the cache, plus it might already be - * encached */ - cachefs_acquire_cookie(cell->cache, - &afs_volume_cache_index_def, - vlocation, - &vlocation->cache); - - if (vlocation->valid) - goto found_in_cache; -#endif - - /* try to look up an unknown volume in the cell VL databases by name */ - ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb); - if (ret < 0) { - printk("kAFS: failed to locate '%*.*s' in cell '%s'\n", - namesz, namesz, name, cell->name); - goto error; + struct afs_vlocation *vl; + + vl = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL); + if (vl) { + vl->cell = cell; + vl->state = AFS_VL_NEW; + atomic_set(&vl->usage, 1); + INIT_LIST_HEAD(&vl->link); + INIT_LIST_HEAD(&vl->grave); + INIT_LIST_HEAD(&vl->update); + init_waitqueue_head(&vl->waitq); + spin_lock_init(&vl->lock); + memcpy(vl->vldb.name, name, namesz); } - goto found_on_vlserver; - - found_in_graveyard: - /* found in the graveyard - resurrect */ - _debug("found in graveyard"); - atomic_inc(&vlocation->usage); - list_move_tail(&vlocation->link, &cell->vl_list); - spin_unlock(&cell->vl_gylock); - - afs_kafstimod_del_timer(&vlocation->timeout); - goto active; - - found_in_memory: - /* found in memory - check to see if it's active */ - _debug("found in memory"); - atomic_inc(&vlocation->usage); + _leave(" = %p", vl); + return vl; +} - active: - active = 1; +/* + * update record if we found it in the cache + */ +static int afs_vlocation_update_record(struct afs_vlocation *vl, + struct key *key, + struct afs_cache_vlocation *vldb) +{ + afs_voltype_t voltype; + afs_volid_t vid; + int ret; -#ifdef AFS_CACHING_SUPPORT - found_in_cache: -#endif /* try to look up a cached volume in the cell VL databases by ID */ - _debug("found in cache"); - _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", - vlocation->vldb.name, - vlocation->vldb.vidmask, - ntohl(vlocation->vldb.servers[0].s_addr), - vlocation->vldb.srvtmask[0], - ntohl(vlocation->vldb.servers[1].s_addr), - vlocation->vldb.srvtmask[1], - ntohl(vlocation->vldb.servers[2].s_addr), - vlocation->vldb.srvtmask[2] - ); + vl->vldb.name, + vl->vldb.vidmask, + ntohl(vl->vldb.servers[0].s_addr), + vl->vldb.srvtmask[0], + ntohl(vl->vldb.servers[1].s_addr), + vl->vldb.srvtmask[1], + ntohl(vl->vldb.servers[2].s_addr), + vl->vldb.srvtmask[2]); _debug("Vids: %08x %08x %08x", - vlocation->vldb.vid[0], - vlocation->vldb.vid[1], - vlocation->vldb.vid[2]); + vl->vldb.vid[0], + vl->vldb.vid[1], + vl->vldb.vid[2]); - if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { - vid = vlocation->vldb.vid[0]; + if (vl->vldb.vidmask & AFS_VOL_VTM_RW) { + vid = vl->vldb.vid[0]; voltype = AFSVL_RWVOL; - } - else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { - vid = vlocation->vldb.vid[1]; + } else if (vl->vldb.vidmask & AFS_VOL_VTM_RO) { + vid = vl->vldb.vid[1]; voltype = AFSVL_ROVOL; - } - else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { - vid = vlocation->vldb.vid[2]; + } else if (vl->vldb.vidmask & AFS_VOL_VTM_BAK) { + vid = vl->vldb.vid[2]; voltype = AFSVL_BACKVOL; - } - else { + } else { BUG(); vid = 0; voltype = 0; } - ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb); + /* contact the server to make sure the volume is still available + * - TODO: need to handle disconnected operation here + */ + ret = afs_vlocation_access_vl_by_id(vl, key, vid, voltype, vldb); switch (ret) { /* net error */ default: - printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n", - namesz, namesz, name, vid, cell->name, ret); - goto error; + printk(KERN_WARNING "kAFS:" + " failed to update volume '%s' (%x) up in '%s': %d\n", + vl->vldb.name, vid, vl->cell->name, ret); + _leave(" = %d", ret); + return ret; /* pulled from local cache into memory */ case 0: - goto found_on_vlserver; + _leave(" = 0"); + return 0; /* uh oh... looks like the volume got deleted */ case -ENOMEDIUM: - printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n", - namesz, namesz, name, vid, cell->name); + printk(KERN_ERR "kAFS:" + " volume '%s' (%x) does not exist '%s'\n", + vl->vldb.name, vid, vl->cell->name); /* TODO: make existing record unavailable */ - goto error; + _leave(" = %d", ret); + return ret; } +} - found_on_vlserver: - _debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }", - namesz, namesz, name, - vldb.vidmask, - ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], - ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], - ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] - ); - - _debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]); +/* + * apply the update to a VL record + */ +static void afs_vlocation_apply_update(struct afs_vlocation *vl, + struct afs_cache_vlocation *vldb) +{ + _debug("Done VL Lookup: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", + vldb->name, vldb->vidmask, + ntohl(vldb->servers[0].s_addr), vldb->srvtmask[0], + ntohl(vldb->servers[1].s_addr), vldb->srvtmask[1], + ntohl(vldb->servers[2].s_addr), vldb->srvtmask[2]); - if ((namesz < sizeof(vlocation->vldb.name) && - vlocation->vldb.name[namesz] != '\0') || - memcmp(vldb.name, name, namesz) != 0) - printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n", - namesz, namesz, name, vldb.name); + _debug("Vids: %08x %08x %08x", + vldb->vid[0], vldb->vid[1], vldb->vid[2]); - memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); + if (strcmp(vldb->name, vl->vldb.name) != 0) + printk(KERN_NOTICE "kAFS:" + " name of volume '%s' changed to '%s' on server\n", + vl->vldb.name, vldb->name); - afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ); + vl->vldb = *vldb; #ifdef AFS_CACHING_SUPPORT /* update volume entry in local cache */ - cachefs_update_cookie(vlocation->cache); -#endif - - *_vlocation = vlocation; - _leave(" = 0 (%p)",vlocation); - return 0; - - error: - if (vlocation) { - if (active) { - __afs_put_vlocation(vlocation); - } - else { - list_del(&vlocation->link); -#ifdef AFS_CACHING_SUPPORT - cachefs_relinquish_cookie(vlocation->cache, 0); + cachefs_update_cookie(vl->cache); #endif - afs_put_cell(vlocation->cell); - kfree(vlocation); - } - } - - _leave(" = %d", ret); - return ret; -} /* end afs_vlocation_lookup() */ +} -/*****************************************************************************/ /* - * finish using a volume location record - * - caller must have cell->vol_sem write-locked + * fill in a volume location record, consulting the cache and the VL server + * both */ -static void __afs_put_vlocation(struct afs_vlocation *vlocation) +static int afs_vlocation_fill_in_record(struct afs_vlocation *vl, + struct key *key) { - struct afs_cell *cell; + struct afs_cache_vlocation vldb; + int ret; - if (!vlocation) - return; + _enter(""); - _enter("%s", vlocation->vldb.name); + ASSERTCMP(vl->valid, ==, 0); - cell = vlocation->cell; + memset(&vldb, 0, sizeof(vldb)); - /* sanity check */ - BUG_ON(atomic_read(&vlocation->usage) <= 0); + /* see if we have an in-cache copy (will set vl->valid if there is) */ +#ifdef AFS_CACHING_SUPPORT + cachefs_acquire_cookie(cell->cache, + &afs_volume_cache_index_def, + vlocation, + &vl->cache); +#endif - spin_lock(&cell->vl_gylock); - if (likely(!atomic_dec_and_test(&vlocation->usage))) { - spin_unlock(&cell->vl_gylock); - _leave(""); - return; + if (vl->valid) { + /* try to update a known volume in the cell VL databases by + * ID as the name may have changed */ + _debug("found in cache"); + ret = afs_vlocation_update_record(vl, key, &vldb); + } else { + /* try to look up an unknown volume in the cell VL databases by + * name */ + ret = afs_vlocation_access_vl_by_name(vl, key, &vldb); + if (ret < 0) { + printk("kAFS: failed to locate '%s' in cell '%s'\n", + vl->vldb.name, vl->cell->name); + return ret; + } } - /* move to graveyard queue */ - list_move_tail(&vlocation->link,&cell->vl_graveyard); - - /* remove from pending timeout queue (refcounted if actually being - * updated) */ - list_del_init(&vlocation->upd_op.link); - - /* time out in 10 secs */ - afs_kafstimod_del_timer(&vlocation->upd_timer); - afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ); - - spin_unlock(&cell->vl_gylock); - - _leave(" [killed]"); -} /* end __afs_put_vlocation() */ - -/*****************************************************************************/ -/* - * finish using a volume location record - */ -void afs_put_vlocation(struct afs_vlocation *vlocation) -{ - if (vlocation) { - struct afs_cell *cell = vlocation->cell; - - down_write(&cell->vl_sem); - __afs_put_vlocation(vlocation); - up_write(&cell->vl_sem); - } -} /* end afs_put_vlocation() */ + afs_vlocation_apply_update(vl, &vldb); + _leave(" = 0"); + return 0; +} -/*****************************************************************************/ /* - * timeout vlocation record - * - removes from the cell's graveyard if the usage count is zero + * queue a vlocation record for updates */ -void afs_vlocation_do_timeout(struct afs_vlocation *vlocation) +void afs_vlocation_queue_for_updates(struct afs_vlocation *vl) { - struct afs_cell *cell; + struct afs_vlocation *xvl; - _enter("%s", vlocation->vldb.name); + /* wait at least 10 minutes before updating... */ + vl->update_at = get_seconds() + afs_vlocation_update_timeout; - cell = vlocation->cell; + spin_lock(&afs_vlocation_updates_lock); - BUG_ON(atomic_read(&vlocation->usage) < 0); - - /* remove from graveyard if still dead */ - spin_lock(&cell->vl_gylock); - if (atomic_read(&vlocation->usage) == 0) - list_del_init(&vlocation->link); - else - vlocation = NULL; - spin_unlock(&cell->vl_gylock); - - if (!vlocation) { - _leave(""); - return; /* resurrected */ + if (!list_empty(&afs_vlocation_updates)) { + /* ... but wait at least 1 second more than the newest record + * already queued so that we don't spam the VL server suddenly + * with lots of requests + */ + xvl = list_entry(afs_vlocation_updates.prev, + struct afs_vlocation, update); + if (vl->update_at <= xvl->update_at) + vl->update_at = xvl->update_at + 1; + } else { + queue_delayed_work(afs_vlocation_update_worker, + &afs_vlocation_update, + afs_vlocation_update_timeout * HZ); } - /* we can now destroy it properly */ -#ifdef AFS_CACHING_SUPPORT - cachefs_relinquish_cookie(vlocation->cache, 0); -#endif - afs_put_cell(cell); - - kfree(vlocation); - - _leave(" [destroyed]"); -} /* end afs_vlocation_do_timeout() */ + list_add_tail(&vl->update, &afs_vlocation_updates); + spin_unlock(&afs_vlocation_updates_lock); +} -/*****************************************************************************/ /* - * send an update operation to the currently selected server + * lookup volume location + * - iterate through the VL servers in a cell until one of them admits knowing + * about the volume in question + * - lookup in the local cache if not able to find on the VL server + * - insert/update in the local cache if did get a VL response */ -static int afs_vlocation_update_begin(struct afs_vlocation *vlocation) +struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell, + struct key *key, + const char *name, + size_t namesz) { - afs_voltype_t voltype; - afs_volid_t vid; + struct afs_vlocation *vl; int ret; - _enter("%s{ufs=%u ucs=%u}", - vlocation->vldb.name, - vlocation->upd_first_svix, - vlocation->upd_curr_svix); + _enter("{%s},{%x},%*.*s,%zu", + cell->name, key_serial(key), + (int) namesz, (int) namesz, name, namesz); - /* try to look up a cached volume in the cell VL databases by ID */ - if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { - vid = vlocation->vldb.vid[0]; - voltype = AFSVL_RWVOL; - } - else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { - vid = vlocation->vldb.vid[1]; - voltype = AFSVL_ROVOL; + if (namesz > sizeof(vl->vldb.name)) { + _leave(" = -ENAMETOOLONG"); + return ERR_PTR(-ENAMETOOLONG); } - else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { - vid = vlocation->vldb.vid[2]; - voltype = AFSVL_BACKVOL; + + /* see if we have an in-memory copy first */ + down_write(&cell->vl_sem); + spin_lock(&cell->vl_lock); + list_for_each_entry(vl, &cell->vl_list, link) { + if (vl->vldb.name[namesz] != '\0') + continue; + if (memcmp(vl->vldb.name, name, namesz) == 0) + goto found_in_memory; } - else { - BUG(); - vid = 0; - voltype = 0; + spin_unlock(&cell->vl_lock); + + /* not in the cell's in-memory lists - create a new record */ + vl = afs_vlocation_alloc(cell, name, namesz); + if (!vl) { + up_write(&cell->vl_sem); + return ERR_PTR(-ENOMEM); } - /* contact the chosen server */ - ret = afs_server_lookup( - vlocation->cell, - &vlocation->cell->vl_addrs[vlocation->upd_curr_svix], - &vlocation->upd_op.server); + afs_get_cell(cell); - switch (ret) { - case 0: - break; - case -ENOMEM: - case -ENONET: - default: - _leave(" = %d", ret); - return ret; - } + list_add_tail(&vl->link, &cell->vl_list); + vl->state = AFS_VL_CREATING; + up_write(&cell->vl_sem); - /* initiate the update operation */ - ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype); - if (ret < 0) { - _leave(" = %d", ret); - return ret; +fill_in_record: + ret = afs_vlocation_fill_in_record(vl, key); + if (ret < 0) + goto error_abandon; + spin_lock(&vl->lock); + vl->state = AFS_VL_VALID; + spin_unlock(&vl->lock); + wake_up(&vl->waitq); + + /* schedule for regular updates */ + afs_vlocation_queue_for_updates(vl); + goto success; + +found_in_memory: + /* found in memory */ + _debug("found in memory"); + atomic_inc(&vl->usage); + spin_unlock(&cell->vl_lock); + if (!list_empty(&vl->grave)) { + spin_lock(&afs_vlocation_graveyard_lock); + list_del_init(&vl->grave); + spin_unlock(&afs_vlocation_graveyard_lock); } + up_write(&cell->vl_sem); + + /* see if it was an abandoned record that we might try filling in */ + spin_lock(&vl->lock); + while (vl->state != AFS_VL_VALID) { + afs_vlocation_state_t state = vl->state; + + _debug("invalid [state %d]", state); + + if (state == AFS_VL_NEW || state == AFS_VL_NO_VOLUME) { + vl->state = AFS_VL_CREATING; + spin_unlock(&vl->lock); + goto fill_in_record; + } + + /* must now wait for creation or update by someone else to + * complete */ + _debug("wait"); + spin_unlock(&vl->lock); + ret = wait_event_interruptible(vl->waitq, + vl->state == AFS_VL_NEW || + vl->state == AFS_VL_VALID || + vl->state == AFS_VL_NO_VOLUME); + if (ret < 0) + goto error; + spin_lock(&vl->lock); + } + spin_unlock(&vl->lock); + +success: + _leave(" = %p",vl); + return vl; + +error_abandon: + spin_lock(&vl->lock); + vl->state = AFS_VL_NEW; + spin_unlock(&vl->lock); + wake_up(&vl->waitq); +error: + ASSERT(vl != NULL); + afs_put_vlocation(vl); _leave(" = %d", ret); - return ret; -} /* end afs_vlocation_update_begin() */ + return ERR_PTR(ret); +} -/*****************************************************************************/ /* - * abandon updating a VL record - * - does not restart the update timer + * finish using a volume location record */ -static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation, - afs_vlocation_upd_t state, - int ret) +void afs_put_vlocation(struct afs_vlocation *vl) { - _enter("%s,%u", vlocation->vldb.name, state); - - if (ret < 0) - printk("kAFS: Abandoning VL update '%s': %d\n", - vlocation->vldb.name, ret); - - /* discard the server record */ - afs_put_server(vlocation->upd_op.server); - vlocation->upd_op.server = NULL; + if (!vl) + return; - spin_lock(&afs_vlocation_update_lock); - afs_vlocation_update = NULL; - vlocation->upd_state = state; + _enter("%s", vl->vldb.name); - /* TODO: start updating next VL record on pending list */ + ASSERTCMP(atomic_read(&vl->usage), >, 0); - spin_unlock(&afs_vlocation_update_lock); + if (likely(!atomic_dec_and_test(&vl->usage))) { + _leave(""); + return; + } - _leave(""); -} /* end afs_vlocation_update_abandon() */ + spin_lock(&afs_vlocation_graveyard_lock); + if (atomic_read(&vl->usage) == 0) { + _debug("buried"); + list_move_tail(&vl->grave, &afs_vlocation_graveyard); + vl->time_of_death = get_seconds(); + schedule_delayed_work(&afs_vlocation_reap, + afs_vlocation_timeout * HZ); + + /* suspend updates on this record */ + if (!list_empty(&vl->update)) { + spin_lock(&afs_vlocation_updates_lock); + list_del_init(&vl->update); + spin_unlock(&afs_vlocation_updates_lock); + } + } + spin_unlock(&afs_vlocation_graveyard_lock); + _leave(" [killed?]"); +} -/*****************************************************************************/ /* - * handle periodic update timeouts and busy retry timeouts - * - called from kafstimod + * destroy a dead volume location record */ -static void afs_vlocation_update_timer(struct afs_timer *timer) +static void afs_vlocation_destroy(struct afs_vlocation *vl) { - struct afs_vlocation *vlocation = - list_entry(timer, struct afs_vlocation, upd_timer); - int ret; + _enter("%p", vl); - _enter("%s", vlocation->vldb.name); +#ifdef AFS_CACHING_SUPPORT + cachefs_relinquish_cookie(vl->cache, 0); +#endif - /* only update if not in the graveyard (defend against putting too) */ - spin_lock(&vlocation->cell->vl_gylock); + afs_put_cell(vl->cell); + kfree(vl); +} - if (!atomic_read(&vlocation->usage)) - goto out_unlock1; +/* + * reap dead volume location records + */ +static void afs_vlocation_reaper(struct work_struct *work) +{ + LIST_HEAD(corpses); + struct afs_vlocation *vl; + unsigned long delay, expiry; + time_t now; - spin_lock(&afs_vlocation_update_lock); + _enter(""); - /* if we were woken up due to EBUSY sleep then restart immediately if - * possible or else jump to front of pending queue */ - if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) { - if (afs_vlocation_update) { - list_add(&vlocation->upd_op.link, - &afs_vlocation_update_pendq); + now = get_seconds(); + spin_lock(&afs_vlocation_graveyard_lock); + + while (!list_empty(&afs_vlocation_graveyard)) { + vl = list_entry(afs_vlocation_graveyard.next, + struct afs_vlocation, grave); + + _debug("check %p", vl); + + /* the queue is ordered most dead first */ + expiry = vl->time_of_death + afs_vlocation_timeout; + if (expiry > now) { + delay = (expiry - now) * HZ; + _debug("delay %lu", delay); + if (!schedule_delayed_work(&afs_vlocation_reap, + delay)) { + cancel_delayed_work(&afs_vlocation_reap); + schedule_delayed_work(&afs_vlocation_reap, + delay); + } + break; } - else { - afs_get_vlocation(vlocation); - afs_vlocation_update = vlocation; - vlocation->upd_state = AFS_VLUPD_INPROGRESS; + + spin_lock(&vl->cell->vl_lock); + if (atomic_read(&vl->usage) > 0) { + _debug("no reap"); + list_del_init(&vl->grave); + } else { + _debug("reap"); + list_move_tail(&vl->grave, &corpses); + list_del_init(&vl->link); } - goto out_unlock2; + spin_unlock(&vl->cell->vl_lock); } - /* put on pending queue if there's already another update in progress */ - if (afs_vlocation_update) { - vlocation->upd_state = AFS_VLUPD_PENDING; - list_add_tail(&vlocation->upd_op.link, - &afs_vlocation_update_pendq); - goto out_unlock2; - } + spin_unlock(&afs_vlocation_graveyard_lock); - /* hold a ref on it while actually updating */ - afs_get_vlocation(vlocation); - afs_vlocation_update = vlocation; - vlocation->upd_state = AFS_VLUPD_INPROGRESS; - - spin_unlock(&afs_vlocation_update_lock); - spin_unlock(&vlocation->cell->vl_gylock); - - /* okay... we can start the update */ - _debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name); - vlocation->upd_first_svix = vlocation->cell->vl_curr_svix; - vlocation->upd_curr_svix = vlocation->upd_first_svix; - vlocation->upd_rej_cnt = 0; - vlocation->upd_busy_cnt = 0; - - ret = afs_vlocation_update_begin(vlocation); - if (ret < 0) { - afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); - afs_kafstimod_add_timer(&vlocation->upd_timer, - AFS_VLDB_TIMEOUT); - afs_put_vlocation(vlocation); + /* now reap the corpses we've extracted */ + while (!list_empty(&corpses)) { + vl = list_entry(corpses.next, struct afs_vlocation, grave); + list_del(&vl->grave); + afs_vlocation_destroy(vl); } _leave(""); - return; +} - out_unlock2: - spin_unlock(&afs_vlocation_update_lock); - out_unlock1: - spin_unlock(&vlocation->cell->vl_gylock); - _leave(""); - return; +/* + * initialise the VL update process + */ +int __init afs_vlocation_update_init(void) +{ + afs_vlocation_update_worker = + create_singlethread_workqueue("kafs_vlupdated"); + return afs_vlocation_update_worker ? 0 : -ENOMEM; +} -} /* end afs_vlocation_update_timer() */ +/* + * discard all the volume location records for rmmod + */ +void __exit afs_vlocation_purge(void) +{ + afs_vlocation_timeout = 0; + + spin_lock(&afs_vlocation_updates_lock); + list_del_init(&afs_vlocation_updates); + spin_unlock(&afs_vlocation_updates_lock); + cancel_delayed_work(&afs_vlocation_update); + queue_delayed_work(afs_vlocation_update_worker, + &afs_vlocation_update, 0); + destroy_workqueue(afs_vlocation_update_worker); + + cancel_delayed_work(&afs_vlocation_reap); + schedule_delayed_work(&afs_vlocation_reap, 0); +} -/*****************************************************************************/ /* - * attend to an update operation upon which an event happened - * - called in kafsasyncd context + * update a volume location */ -static void afs_vlocation_update_attend(struct afs_async_op *op) +static void afs_vlocation_updater(struct work_struct *work) { struct afs_cache_vlocation vldb; - struct afs_vlocation *vlocation = - list_entry(op, struct afs_vlocation, upd_op); - unsigned tmp; + struct afs_vlocation *vl, *xvl; + time_t now; + long timeout; int ret; - _enter("%s", vlocation->vldb.name); - - ret = afs_rxvl_get_entry_by_id_async2(op, &vldb); - switch (ret) { - case -EAGAIN: - _leave(" [unfinished]"); - return; - - case 0: - _debug("END VL UPDATE: %d\n", ret); - vlocation->valid = 1; - - _debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }", - vldb.vidmask, - ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], - ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], - ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] - ); - - _debug("Vids: %08x %08x %08x", - vldb.vid[0], vldb.vid[1], vldb.vid[2]); - - afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); - - down_write(&vlocation->cell->vl_sem); - - /* actually update the cache */ - if (strncmp(vldb.name, vlocation->vldb.name, - sizeof(vlocation->vldb.name)) != 0) - printk("kAFS: name of volume '%s'" - " changed to '%s' on server\n", - vlocation->vldb.name, vldb.name); - - memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); - -#if 0 - /* TODO update volume entry in local cache */ -#endif - - up_write(&vlocation->cell->vl_sem); - - if (ret < 0) - printk("kAFS: failed to update local cache: %d\n", ret); - - afs_kafstimod_add_timer(&vlocation->upd_timer, - AFS_VLDB_TIMEOUT); - afs_put_vlocation(vlocation); - _leave(" [found]"); - return; - - case -ENOMEDIUM: - vlocation->upd_rej_cnt++; - goto try_next; - - /* the server is locked - retry in a very short while */ - case -EBUSY: - vlocation->upd_busy_cnt++; - if (vlocation->upd_busy_cnt > 3) - goto try_next; /* too many retries */ - - afs_vlocation_update_abandon(vlocation, - AFS_VLUPD_BUSYSLEEP, 0); - afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2); - afs_put_vlocation(vlocation); - _leave(" [busy]"); - return; - - case -ENETUNREACH: - case -EHOSTUNREACH: - case -ECONNREFUSED: - case -EREMOTEIO: - /* record bad vlserver info in the cell too - * - TODO: use down_write_trylock() if available - */ - if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix) - vlocation->cell->vl_curr_svix = - vlocation->cell->vl_curr_svix % - vlocation->cell->vl_naddrs; - - case -EBADRQC: - case -EINVAL: - case -EACCES: - case -EBADMSG: - goto try_next; - - default: - goto abandon; - } - - /* try contacting the next server */ - try_next: - vlocation->upd_busy_cnt = 0; - - /* discard the server record */ - afs_put_server(vlocation->upd_op.server); - vlocation->upd_op.server = NULL; + _enter(""); - tmp = vlocation->cell->vl_naddrs; - if (tmp == 0) - goto abandon; + now = get_seconds(); - vlocation->upd_curr_svix++; - if (vlocation->upd_curr_svix >= tmp) - vlocation->upd_curr_svix = 0; - if (vlocation->upd_first_svix >= tmp) - vlocation->upd_first_svix = tmp - 1; + /* find a record to update */ + spin_lock(&afs_vlocation_updates_lock); + for (;;) { + if (list_empty(&afs_vlocation_updates)) { + spin_unlock(&afs_vlocation_updates_lock); + _leave(" [nothing]"); + return; + } - /* move to the next server */ - if (vlocation->upd_curr_svix != vlocation->upd_first_svix) { - afs_vlocation_update_begin(vlocation); - _leave(" [next]"); - return; + vl = list_entry(afs_vlocation_updates.next, + struct afs_vlocation, update); + if (atomic_read(&vl->usage) > 0) + break; + list_del_init(&vl->update); } - /* run out of servers to try - was the volume rejected? */ - if (vlocation->upd_rej_cnt > 0) { - printk("kAFS: Active volume no longer valid '%s'\n", - vlocation->vldb.name); - vlocation->valid = 0; - afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); - afs_kafstimod_add_timer(&vlocation->upd_timer, - AFS_VLDB_TIMEOUT); - afs_put_vlocation(vlocation); - _leave(" [invalidated]"); + timeout = vl->update_at - now; + if (timeout > 0) { + queue_delayed_work(afs_vlocation_update_worker, + &afs_vlocation_update, timeout * HZ); + spin_unlock(&afs_vlocation_updates_lock); + _leave(" [nothing]"); return; } - /* abandon the update */ - abandon: - afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); - afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10); - afs_put_vlocation(vlocation); - _leave(" [abandoned]"); - -} /* end afs_vlocation_update_attend() */ - -/*****************************************************************************/ -/* - * deal with an update operation being discarded - * - called in kafsasyncd context when it's dying due to rmmod - * - the call has already been aborted and put()'d - */ -static void afs_vlocation_update_discard(struct afs_async_op *op) -{ - struct afs_vlocation *vlocation = - list_entry(op, struct afs_vlocation, upd_op); + list_del_init(&vl->update); + atomic_inc(&vl->usage); + spin_unlock(&afs_vlocation_updates_lock); - _enter("%s", vlocation->vldb.name); + /* we can now perform the update */ + _debug("update %s", vl->vldb.name); + vl->state = AFS_VL_UPDATING; + vl->upd_rej_cnt = 0; + vl->upd_busy_cnt = 0; - afs_put_server(op->server); - op->server = NULL; + ret = afs_vlocation_update_record(vl, NULL, &vldb); + spin_lock(&vl->lock); + switch (ret) { + case 0: + afs_vlocation_apply_update(vl, &vldb); + vl->state = AFS_VL_VALID; + break; + case -ENOMEDIUM: + vl->state = AFS_VL_VOLUME_DELETED; + break; + default: + vl->state = AFS_VL_UNCERTAIN; + break; + } + spin_unlock(&vl->lock); + wake_up(&vl->waitq); - afs_put_vlocation(vlocation); + /* and then reschedule */ + _debug("reschedule"); + vl->update_at = get_seconds() + afs_vlocation_update_timeout; - _leave(""); -} /* end afs_vlocation_update_discard() */ + spin_lock(&afs_vlocation_updates_lock); -/*****************************************************************************/ -/* - * match a VLDB record stored in the cache - * - may also load target from entry - */ -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_vlocation_cache_match(void *target, - const void *entry) -{ - const struct afs_cache_vlocation *vldb = entry; - struct afs_vlocation *vlocation = target; - - _enter("{%s},{%s}", vlocation->vldb.name, vldb->name); - - if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0 - ) { - if (!vlocation->valid || - vlocation->vldb.rtime == vldb->rtime - ) { - vlocation->vldb = *vldb; - vlocation->valid = 1; - _leave(" = SUCCESS [c->m]"); - return CACHEFS_MATCH_SUCCESS; - } - /* need to update cache if cached info differs */ - else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) { - /* delete if VIDs for this name differ */ - if (memcmp(&vlocation->vldb.vid, - &vldb->vid, - sizeof(vldb->vid)) != 0) { - _leave(" = DELETE"); - return CACHEFS_MATCH_SUCCESS_DELETE; - } - - _leave(" = UPDATE"); - return CACHEFS_MATCH_SUCCESS_UPDATE; - } - else { - _leave(" = SUCCESS"); - return CACHEFS_MATCH_SUCCESS; - } + if (!list_empty(&afs_vlocation_updates)) { + /* next update in 10 minutes, but wait at least 1 second more + * than the newest record already queued so that we don't spam + * the VL server suddenly with lots of requests + */ + xvl = list_entry(afs_vlocation_updates.prev, + struct afs_vlocation, update); + if (vl->update_at <= xvl->update_at) + vl->update_at = xvl->update_at + 1; + xvl = list_entry(afs_vlocation_updates.next, + struct afs_vlocation, update); + timeout = xvl->update_at - now; + if (timeout < 0) + timeout = 0; + } else { + timeout = afs_vlocation_update_timeout; } - _leave(" = FAILED"); - return CACHEFS_MATCH_FAILED; -} /* end afs_vlocation_cache_match() */ -#endif - -/*****************************************************************************/ -/* - * update a VLDB record stored in the cache - */ -#ifdef AFS_CACHING_SUPPORT -static void afs_vlocation_cache_update(void *source, void *entry) -{ - struct afs_cache_vlocation *vldb = entry; - struct afs_vlocation *vlocation = source; + ASSERT(list_empty(&vl->update)); - _enter(""); - - *vldb = vlocation->vldb; + list_add_tail(&vl->update, &afs_vlocation_updates); -} /* end afs_vlocation_cache_update() */ -#endif + _debug("timeout %ld", timeout); + queue_delayed_work(afs_vlocation_update_worker, + &afs_vlocation_update, timeout * HZ); + spin_unlock(&afs_vlocation_updates_lock); + afs_put_vlocation(vl); +} diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index cf62da5..a1904ab 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c @@ -1,6 +1,6 @@ -/* vnode.c: AFS vnode management +/* AFS vnode management * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -14,142 +14,237 @@ #include #include #include #include -#include -#include "volume.h" -#include "cell.h" -#include "cmservice.h" -#include "fsclient.h" -#include "vlclient.h" -#include "vnode.h" #include "internal.h" -static void afs_vnode_cb_timed_out(struct afs_timer *timer); +#if 0 +static noinline bool dump_tree_aux(struct rb_node *node, struct rb_node *parent, + int depth, char lr) +{ + struct afs_vnode *vnode; + bool bad = false; + + if (!node) + return false; + + if (node->rb_left) + bad = dump_tree_aux(node->rb_left, node, depth + 2, '/'); + + vnode = rb_entry(node, struct afs_vnode, cb_promise); + _debug("%c %*.*s%c%p {%d}", + rb_is_red(node) ? 'R' : 'B', + depth, depth, "", lr, + vnode, vnode->cb_expires_at); + if (rb_parent(node) != parent) { + printk("BAD: %p != %p\n", rb_parent(node), parent); + bad = true; + } -struct afs_timer_ops afs_vnode_cb_timed_out_ops = { - .timed_out = afs_vnode_cb_timed_out, -}; + if (node->rb_right) + bad |= dump_tree_aux(node->rb_right, node, depth + 2, '\\'); -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_vnode_cache_match(void *target, - const void *entry); -static void afs_vnode_cache_update(void *source, void *entry); + return bad; +} -struct cachefs_index_def afs_vnode_cache_index_def = { - .name = "vnode", - .data_size = sizeof(struct afs_cache_vnode), - .keys[0] = { CACHEFS_INDEX_KEYS_BIN, 4 }, - .match = afs_vnode_cache_match, - .update = afs_vnode_cache_update, -}; +static noinline void dump_tree(const char *name, struct afs_server *server) +{ + _enter("%s", name); + if (dump_tree_aux(server->cb_promises.rb_node, NULL, 0, '-')) + BUG(); +} #endif -/*****************************************************************************/ /* - * handle a callback timing out - * TODO: retain a ref to vnode struct for an outstanding callback timeout + * insert a vnode into the backing server's vnode tree */ -static void afs_vnode_cb_timed_out(struct afs_timer *timer) +static void afs_install_vnode(struct afs_vnode *vnode, + struct afs_server *server) { - struct afs_server *oldserver; - struct afs_vnode *vnode; + struct afs_server *old_server = vnode->server; + struct afs_vnode *xvnode; + struct rb_node *parent, **p; - vnode = list_entry(timer, struct afs_vnode, cb_timeout); + _enter("%p,%p", vnode, server); - _enter("%p", vnode); + if (old_server) { + spin_lock(&old_server->fs_lock); + rb_erase(&vnode->server_rb, &old_server->fs_vnodes); + spin_unlock(&old_server->fs_lock); + } - /* set the changed flag in the vnode and release the server */ - spin_lock(&vnode->lock); + afs_get_server(server); + vnode->server = server; + afs_put_server(old_server); + + /* insert into the server's vnode tree in FID order */ + spin_lock(&server->fs_lock); + + parent = NULL; + p = &server->fs_vnodes.rb_node; + while (*p) { + parent = *p; + xvnode = rb_entry(parent, struct afs_vnode, server_rb); + if (vnode->fid.vid < xvnode->fid.vid) + p = &(*p)->rb_left; + else if (vnode->fid.vid > xvnode->fid.vid) + p = &(*p)->rb_right; + else if (vnode->fid.vnode < xvnode->fid.vnode) + p = &(*p)->rb_left; + else if (vnode->fid.vnode > xvnode->fid.vnode) + p = &(*p)->rb_right; + else if (vnode->fid.unique < xvnode->fid.unique) + p = &(*p)->rb_left; + else if (vnode->fid.unique > xvnode->fid.unique) + p = &(*p)->rb_right; + else + BUG(); /* can't happen unless afs_iget() malfunctions */ + } + + rb_link_node(&vnode->server_rb, parent, p); + rb_insert_color(&vnode->server_rb, &server->fs_vnodes); - oldserver = xchg(&vnode->cb_server, NULL); - if (oldserver) { - vnode->flags |= AFS_VNODE_CHANGED; + spin_unlock(&server->fs_lock); + _leave(""); +} - spin_lock(&afs_cb_hash_lock); - list_del_init(&vnode->cb_hash_link); - spin_unlock(&afs_cb_hash_lock); +/* + * insert a vnode into the promising server's update/expiration tree + * - caller must hold vnode->lock + */ +static void afs_vnode_note_promise(struct afs_vnode *vnode, + struct afs_server *server) +{ + struct afs_server *old_server; + struct afs_vnode *xvnode; + struct rb_node *parent, **p; - spin_lock(&oldserver->cb_lock); - list_del_init(&vnode->cb_link); - spin_unlock(&oldserver->cb_lock); + _enter("%p,%p", vnode, server); + + ASSERT(server != NULL); + + old_server = vnode->server; + if (vnode->cb_promised) { + if (server == old_server && + vnode->cb_expires == vnode->cb_expires_at) { + _leave(" [no change]"); + return; + } + + spin_lock(&old_server->cb_lock); + if (vnode->cb_promised) { + _debug("delete"); + rb_erase(&vnode->cb_promise, &old_server->cb_promises); + vnode->cb_promised = false; + } + spin_unlock(&old_server->cb_lock); } - spin_unlock(&vnode->lock); + if (vnode->server != server) + afs_install_vnode(vnode, server); + + vnode->cb_expires_at = vnode->cb_expires; + _debug("PROMISE on %p {%lu}", + vnode, (unsigned long) vnode->cb_expires_at); + + /* abuse an RB-tree to hold the expiration order (we may have multiple + * items with the same expiration time) */ + spin_lock(&server->cb_lock); + + parent = NULL; + p = &server->cb_promises.rb_node; + while (*p) { + parent = *p; + xvnode = rb_entry(parent, struct afs_vnode, cb_promise); + if (vnode->cb_expires_at < xvnode->cb_expires_at) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } - afs_put_server(oldserver); + rb_link_node(&vnode->cb_promise, parent, p); + rb_insert_color(&vnode->cb_promise, &server->cb_promises); + vnode->cb_promised = true; + spin_unlock(&server->cb_lock); _leave(""); -} /* end afs_vnode_cb_timed_out() */ +} -/*****************************************************************************/ /* - * finish off updating the recorded status of a file + * handle remote file deletion by discarding the callback promise + */ +static void afs_vnode_deleted_remotely(struct afs_vnode *vnode) +{ + struct afs_server *server; + + set_bit(AFS_VNODE_DELETED, &vnode->flags); + + server = vnode->server; + if (vnode->cb_promised) { + spin_lock(&server->cb_lock); + if (vnode->cb_promised) { + rb_erase(&vnode->cb_promise, &server->cb_promises); + vnode->cb_promised = false; + } + spin_unlock(&server->cb_lock); + } + + spin_lock(&vnode->server->fs_lock); + rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes); + spin_unlock(&vnode->server->fs_lock); + + vnode->server = NULL; + afs_put_server(server); +} + +/* + * finish off updating the recorded status of a file after a successful + * operation completion * - starts callback expiry timer * - adds to server's callback list */ -static void afs_vnode_finalise_status_update(struct afs_vnode *vnode, - struct afs_server *server, - int ret) +void afs_vnode_finalise_status_update(struct afs_vnode *vnode, + struct afs_server *server) { struct afs_server *oldserver = NULL; - _enter("%p,%p,%d", vnode, server, ret); + _enter("%p,%p", vnode, server); spin_lock(&vnode->lock); + clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); + afs_vnode_note_promise(vnode, server); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + + wake_up_all(&vnode->update_waitq); + afs_put_server(oldserver); + _leave(""); +} - vnode->flags &= ~AFS_VNODE_CHANGED; +/* + * finish off updating the recorded status of a file after an operation failed + */ +static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret) +{ + _enter("%p,%d", vnode, ret); - if (ret == 0) { - /* adjust the callback timeout appropriately */ - afs_kafstimod_add_timer(&vnode->cb_timeout, - vnode->cb_expiry * HZ); - - spin_lock(&afs_cb_hash_lock); - list_move_tail(&vnode->cb_hash_link, - &afs_cb_hash(server, &vnode->fid)); - spin_unlock(&afs_cb_hash_lock); - - /* swap ref to old callback server with that for new callback - * server */ - oldserver = xchg(&vnode->cb_server, server); - if (oldserver != server) { - if (oldserver) { - spin_lock(&oldserver->cb_lock); - list_del_init(&vnode->cb_link); - spin_unlock(&oldserver->cb_lock); - } + spin_lock(&vnode->lock); - afs_get_server(server); - spin_lock(&server->cb_lock); - list_add_tail(&vnode->cb_link, &server->cb_promises); - spin_unlock(&server->cb_lock); - } - else { - /* same server */ - oldserver = NULL; - } - } - else if (ret == -ENOENT) { - /* the file was deleted - clear the callback timeout */ - oldserver = xchg(&vnode->cb_server, NULL); - afs_kafstimod_del_timer(&vnode->cb_timeout); + clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); + if (ret == -ENOENT) { + /* the file was deleted on the server */ _debug("got NOENT from server - marking file deleted"); - vnode->flags |= AFS_VNODE_DELETED; + afs_vnode_deleted_remotely(vnode); } vnode->update_cnt--; - + ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); wake_up_all(&vnode->update_waitq); - - afs_put_server(oldserver); - _leave(""); +} -} /* end afs_vnode_finalise_status_update() */ - -/*****************************************************************************/ /* * fetch file status from the volume * - don't issue a fetch if: @@ -157,9 +252,11 @@ static void afs_vnode_finalise_status_up * - there are any outstanding ops that will fetch the status * - TODO implement local caching */ -int afs_vnode_fetch_status(struct afs_vnode *vnode) +int afs_vnode_fetch_status(struct afs_vnode *vnode, + struct afs_vnode *auth_vnode, struct key *key) { struct afs_server *server; + unsigned long acl_order; int ret; DECLARE_WAITQUEUE(myself, current); @@ -168,38 +265,49 @@ int afs_vnode_fetch_status(struct afs_vn vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); - if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) { + if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) && + vnode->cb_promised) { _leave(" [unchanged]"); return 0; } - if (vnode->flags & AFS_VNODE_DELETED) { + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { _leave(" [deleted]"); return -ENOENT; } + acl_order = 0; + if (auth_vnode) + acl_order = auth_vnode->acl_order; + spin_lock(&vnode->lock); - if (!(vnode->flags & AFS_VNODE_CHANGED)) { + if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) && + vnode->cb_promised) { spin_unlock(&vnode->lock); _leave(" [unchanged]"); return 0; } + ASSERTCMP(vnode->update_cnt, >=, 0); + if (vnode->update_cnt > 0) { /* someone else started a fetch */ + _debug("wait on fetch %d", vnode->update_cnt); + set_current_state(TASK_UNINTERRUPTIBLE); + ASSERT(myself.func != NULL); add_wait_queue(&vnode->update_waitq, &myself); /* wait for the status to be updated */ for (;;) { - if (!(vnode->flags & AFS_VNODE_CHANGED)) + if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) break; - if (vnode->flags & AFS_VNODE_DELETED) + if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) break; - /* it got updated and invalidated all before we saw - * it */ + /* check to see if it got updated and invalidated all + * before we saw it */ if (vnode->update_cnt == 0) { remove_wait_queue(&vnode->update_waitq, &myself); @@ -219,10 +327,11 @@ int afs_vnode_fetch_status(struct afs_vn spin_unlock(&vnode->lock); set_current_state(TASK_RUNNING); - return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0; + return test_bit(AFS_VNODE_DELETED, &vnode->flags) ? + -ENOENT : 0; } - get_anyway: +get_anyway: /* okay... we're going to have to initiate the op */ vnode->update_cnt++; @@ -232,39 +341,60 @@ int afs_vnode_fetch_status(struct afs_vn * vnode */ do { /* pick a server to query */ - ret = afs_volume_pick_fileserver(vnode->volume, &server); - if (ret<0) - return ret; + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; - _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + _debug("USING SERVER: %p{%08x}", + server, ntohl(server->addr.s_addr)); - ret = afs_rxfs_fetch_file_status(server, vnode, NULL); + ret = afs_fs_fetch_file_status(server, key, vnode, NULL, + &afs_sync_call); - } while (!afs_volume_release_fileserver(vnode->volume, server, ret)); + } while (!afs_volume_release_fileserver(vnode, server, ret)); /* adjust the flags */ - afs_vnode_finalise_status_update(vnode, server, ret); + if (ret == 0) { + _debug("adjust"); + if (auth_vnode) + afs_cache_permit(vnode, key, acl_order); + afs_vnode_finalise_status_update(vnode, server); + afs_put_server(server); + } else { + _debug("failed [%d]", ret); + afs_vnode_status_update_failed(vnode, ret); + } - _leave(" = %d", ret); + ASSERTCMP(vnode->update_cnt, >=, 0); + + _leave(" = %d [cnt %d]", ret, vnode->update_cnt); return ret; -} /* end afs_vnode_fetch_status() */ -/*****************************************************************************/ +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); + return PTR_ERR(server); +} + /* * fetch file data from the volume - * - TODO implement caching and server failover + * - TODO implement caching */ -int afs_vnode_fetch_data(struct afs_vnode *vnode, - struct afs_rxfs_fetch_descriptor *desc) +int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, + off_t offset, size_t length, struct page *page) { struct afs_server *server; int ret; - _enter("%s,{%u,%u,%u}", + _enter("%s{%u,%u,%u},%x,,,", vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, - vnode->fid.unique); + vnode->fid.unique, + key_serial(key)); /* this op will fetch the status */ spin_lock(&vnode->lock); @@ -275,120 +405,351 @@ int afs_vnode_fetch_data(struct afs_vnod * vnode */ do { /* pick a server to query */ - ret = afs_volume_pick_fileserver(vnode->volume, &server); - if (ret < 0) - return ret; + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); - ret = afs_rxfs_fetch_file_data(server, vnode, desc, NULL); + ret = afs_fs_fetch_data(server, key, vnode, offset, length, + page, &afs_sync_call); - } while (!afs_volume_release_fileserver(vnode->volume, server, ret)); + } while (!afs_volume_release_fileserver(vnode, server, ret)); /* adjust the flags */ - afs_vnode_finalise_status_update(vnode, server, ret); + if (ret == 0) { + afs_vnode_finalise_status_update(vnode, server); + afs_put_server(server); + } else { + afs_vnode_status_update_failed(vnode, ret); + } _leave(" = %d", ret); return ret; -} /* end afs_vnode_fetch_data() */ +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + return PTR_ERR(server); +} -/*****************************************************************************/ /* - * break any outstanding callback on a vnode - * - only relevent to server that issued it + * make a file or a directory */ -int afs_vnode_give_up_callback(struct afs_vnode *vnode) +int afs_vnode_create(struct afs_vnode *vnode, struct key *key, + const char *name, umode_t mode, struct afs_fid *newfid, + struct afs_file_status *newstatus, + struct afs_callback *newcb, struct afs_server **_server) { struct afs_server *server; int ret; - _enter("%s,{%u,%u,%u}", + _enter("%s{%u,%u,%u},%x,%s,,", vnode->volume->vlocation->vldb.name, vnode->fid.vid, vnode->fid.vnode, - vnode->fid.unique); - - spin_lock(&afs_cb_hash_lock); - list_del_init(&vnode->cb_hash_link); - spin_unlock(&afs_cb_hash_lock); + vnode->fid.unique, + key_serial(key), + name); - /* set the changed flag in the vnode and release the server */ + /* this op will fetch the status on the directory we're creating in */ spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); - afs_kafstimod_del_timer(&vnode->cb_timeout); + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); - server = xchg(&vnode->cb_server, NULL); - if (server) { - vnode->flags |= AFS_VNODE_CHANGED; + ret = afs_fs_create(server, key, vnode, name, mode, newfid, + newstatus, newcb, &afs_sync_call); - spin_lock(&server->cb_lock); - list_del_init(&vnode->cb_link); - spin_unlock(&server->cb_lock); + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) { + afs_vnode_finalise_status_update(vnode, server); + *_server = server; + } else { + afs_vnode_status_update_failed(vnode, ret); + *_server = NULL; } + _leave(" = %d [cnt %d]", ret, vnode->update_cnt); + return ret; + +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); + _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); + return PTR_ERR(server); +} - ret = 0; - if (server) { - ret = afs_rxfs_give_up_callback(server, vnode); +/* + * remove a file or directory + */ +int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name, + bool isdir) +{ + struct afs_server *server; + int ret; + + _enter("%s{%u,%u,%u},%x,%s", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(key), + name); + + /* this op will fetch the status on the directory we're removing from */ + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_remove(server, key, vnode, name, isdir, + &afs_sync_call); + + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) { + afs_vnode_finalise_status_update(vnode, server); afs_put_server(server); + } else { + afs_vnode_status_update_failed(vnode, ret); } - _leave(" = %d", ret); + _leave(" = %d [cnt %d]", ret, vnode->update_cnt); return ret; -} /* end afs_vnode_give_up_callback() */ -/*****************************************************************************/ +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); + return PTR_ERR(server); +} + /* - * match a vnode record stored in the cache + * create a hard link */ -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_vnode_cache_match(void *target, - const void *entry) +extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, + struct key *key, const char *name) { - const struct afs_cache_vnode *cvnode = entry; - struct afs_vnode *vnode = target; + struct afs_server *server; + int ret; - _enter("{%x,%x,%Lx},{%x,%x,%Lx}", + _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s", + dvnode->volume->vlocation->vldb.name, + dvnode->fid.vid, + dvnode->fid.vnode, + dvnode->fid.unique, + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, - vnode->status.version, - cvnode->vnode_id, - cvnode->vnode_unique, - cvnode->data_version); - - if (vnode->fid.vnode != cvnode->vnode_id) { - _leave(" = FAILED"); - return CACHEFS_MATCH_FAILED; + key_serial(key), + name); + + /* this op will fetch the status on the directory we're removing from */ + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + spin_lock(&dvnode->lock); + dvnode->update_cnt++; + spin_unlock(&dvnode->lock); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(dvnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_link(server, key, dvnode, vnode, name, + &afs_sync_call); + + } while (!afs_volume_release_fileserver(dvnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) { + afs_vnode_finalise_status_update(vnode, server); + afs_vnode_finalise_status_update(dvnode, server); + afs_put_server(server); + } else { + afs_vnode_status_update_failed(vnode, ret); + afs_vnode_status_update_failed(dvnode, ret); } - if (vnode->fid.unique != cvnode->vnode_unique || - vnode->status.version != cvnode->data_version) { - _leave(" = DELETE"); - return CACHEFS_MATCH_SUCCESS_DELETE; + _leave(" = %d [cnt %d]", ret, vnode->update_cnt); + return ret; + +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + spin_lock(&dvnode->lock); + dvnode->update_cnt--; + ASSERTCMP(dvnode->update_cnt, >=, 0); + spin_unlock(&dvnode->lock); + _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); + return PTR_ERR(server); +} + +/* + * create a symbolic link + */ +int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key, + const char *name, const char *content, + struct afs_fid *newfid, + struct afs_file_status *newstatus, + struct afs_server **_server) +{ + struct afs_server *server; + int ret; + + _enter("%s{%u,%u,%u},%x,%s,%s,,,", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(key), + name, content); + + /* this op will fetch the status on the directory we're creating in */ + spin_lock(&vnode->lock); + vnode->update_cnt++; + spin_unlock(&vnode->lock); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_symlink(server, key, vnode, name, content, + newfid, newstatus, &afs_sync_call); + + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) { + afs_vnode_finalise_status_update(vnode, server); + *_server = server; + } else { + afs_vnode_status_update_failed(vnode, ret); + *_server = NULL; } - _leave(" = SUCCESS"); - return CACHEFS_MATCH_SUCCESS; -} /* end afs_vnode_cache_match() */ -#endif + _leave(" = %d [cnt %d]", ret, vnode->update_cnt); + return ret; + +no_server: + spin_lock(&vnode->lock); + vnode->update_cnt--; + ASSERTCMP(vnode->update_cnt, >=, 0); + spin_unlock(&vnode->lock); + _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); + return PTR_ERR(server); +} -/*****************************************************************************/ /* - * update a vnode record stored in the cache + * rename a file */ -#ifdef AFS_CACHING_SUPPORT -static void afs_vnode_cache_update(void *source, void *entry) +int afs_vnode_rename(struct afs_vnode *orig_dvnode, + struct afs_vnode *new_dvnode, + struct key *key, + const char *orig_name, + const char *new_name) { - struct afs_cache_vnode *cvnode = entry; - struct afs_vnode *vnode = source; + struct afs_server *server; + int ret; - _enter(""); + _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s,%s", + orig_dvnode->volume->vlocation->vldb.name, + orig_dvnode->fid.vid, + orig_dvnode->fid.vnode, + orig_dvnode->fid.unique, + new_dvnode->volume->vlocation->vldb.name, + new_dvnode->fid.vid, + new_dvnode->fid.vnode, + new_dvnode->fid.unique, + key_serial(key), + orig_name, + new_name); + + /* this op will fetch the status on both the directories we're dealing + * with */ + spin_lock(&orig_dvnode->lock); + orig_dvnode->update_cnt++; + spin_unlock(&orig_dvnode->lock); + if (new_dvnode != orig_dvnode) { + spin_lock(&new_dvnode->lock); + new_dvnode->update_cnt++; + spin_unlock(&new_dvnode->lock); + } - cvnode->vnode_id = vnode->fid.vnode; - cvnode->vnode_unique = vnode->fid.unique; - cvnode->data_version = vnode->status.version; + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(orig_dvnode); + if (IS_ERR(server)) + goto no_server; -} /* end afs_vnode_cache_update() */ -#endif + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_rename(server, key, orig_dvnode, orig_name, + new_dvnode, new_name, &afs_sync_call); + + } while (!afs_volume_release_fileserver(orig_dvnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) { + afs_vnode_finalise_status_update(orig_dvnode, server); + if (new_dvnode != orig_dvnode) + afs_vnode_finalise_status_update(new_dvnode, server); + afs_put_server(server); + } else { + afs_vnode_status_update_failed(orig_dvnode, ret); + if (new_dvnode != orig_dvnode) + afs_vnode_status_update_failed(new_dvnode, ret); + } + + _leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt); + return ret; + +no_server: + spin_lock(&orig_dvnode->lock); + orig_dvnode->update_cnt--; + ASSERTCMP(orig_dvnode->update_cnt, >=, 0); + spin_unlock(&orig_dvnode->lock); + if (new_dvnode != orig_dvnode) { + spin_lock(&new_dvnode->lock); + new_dvnode->update_cnt--; + ASSERTCMP(new_dvnode->update_cnt, >=, 0); + spin_unlock(&new_dvnode->lock); + } + _leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt); + return PTR_ERR(server); +} diff --git a/fs/afs/vnode.h b/fs/afs/vnode.h deleted file mode 100644 index b86a971..0000000 --- a/fs/afs/vnode.h +++ /dev/null @@ -1,94 +0,0 @@ -/* vnode.h: AFS vnode record - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_VNODE_H -#define _LINUX_AFS_VNODE_H - -#include -#include "server.h" -#include "kafstimod.h" -#include "cache.h" - -#ifdef __KERNEL__ - -struct afs_rxfs_fetch_descriptor; - -/*****************************************************************************/ -/* - * vnode catalogue entry - */ -struct afs_cache_vnode -{ - afs_vnodeid_t vnode_id; /* vnode ID */ - unsigned vnode_unique; /* vnode ID uniquifier */ - afs_dataversion_t data_version; /* data version */ -}; - -#ifdef AFS_CACHING_SUPPORT -extern struct cachefs_index_def afs_vnode_cache_index_def; -#endif - -/*****************************************************************************/ -/* - * AFS inode private data - */ -struct afs_vnode -{ - struct inode vfs_inode; /* the VFS's inode record */ - - struct afs_volume *volume; /* volume on which vnode resides */ - struct afs_fid fid; /* the file identifier for this inode */ - struct afs_file_status status; /* AFS status info for this file */ -#ifdef AFS_CACHING_SUPPORT - struct cachefs_cookie *cache; /* caching cookie */ -#endif - - wait_queue_head_t update_waitq; /* status fetch waitqueue */ - unsigned update_cnt; /* number of outstanding ops that will update the - * status */ - spinlock_t lock; /* waitqueue/flags lock */ - unsigned flags; -#define AFS_VNODE_CHANGED 0x00000001 /* set if vnode reported changed by callback */ -#define AFS_VNODE_DELETED 0x00000002 /* set if vnode deleted on server */ -#define AFS_VNODE_MOUNTPOINT 0x00000004 /* set if vnode is a mountpoint symlink */ - - /* outstanding callback notification on this file */ - struct afs_server *cb_server; /* server that made the current promise */ - struct list_head cb_link; /* link in server's promises list */ - struct list_head cb_hash_link; /* link in master callback hash */ - struct afs_timer cb_timeout; /* timeout on promise */ - unsigned cb_version; /* callback version */ - unsigned cb_expiry; /* callback expiry time */ - afs_callback_type_t cb_type; /* type of callback */ -}; - -static inline struct afs_vnode *AFS_FS_I(struct inode *inode) -{ - return container_of(inode,struct afs_vnode,vfs_inode); -} - -static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode) -{ - return &vnode->vfs_inode; -} - -extern int afs_vnode_fetch_status(struct afs_vnode *vnode); - -extern int afs_vnode_fetch_data(struct afs_vnode *vnode, - struct afs_rxfs_fetch_descriptor *desc); - -extern int afs_vnode_give_up_callback(struct afs_vnode *vnode); - -extern struct afs_timer_ops afs_vnode_cb_timed_out_ops; - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_AFS_VNODE_H */ diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 768c6db..dd160ca 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -1,6 +1,6 @@ -/* volume.c: AFS volume management +/* AFS volume management * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -15,35 +15,10 @@ #include #include #include #include -#include "volume.h" -#include "vnode.h" -#include "cell.h" -#include "cache.h" -#include "cmservice.h" -#include "fsclient.h" -#include "vlclient.h" #include "internal.h" -#ifdef __KDEBUG static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" }; -#endif - -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_volume_cache_match(void *target, - const void *entry); -static void afs_volume_cache_update(void *source, void *entry); - -struct cachefs_index_def afs_volume_cache_index_def = { - .name = "volume", - .data_size = sizeof(struct afs_cache_vhash), - .keys[0] = { CACHEFS_INDEX_KEYS_BIN, 1 }, - .keys[1] = { CACHEFS_INDEX_KEYS_BIN, 1 }, - .match = afs_volume_cache_match, - .update = afs_volume_cache_update, -}; -#endif -/*****************************************************************************/ /* * lookup a volume by name * - this can be one of the following: @@ -66,118 +41,52 @@ #endif * - Rule 3: If parent volume is R/W, then only mount R/W volume unless * explicitly told otherwise */ -int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath, - struct afs_volume **_volume) +struct afs_volume *afs_volume_lookup(struct afs_mount_params *params) { struct afs_vlocation *vlocation = NULL; struct afs_volume *volume = NULL; - afs_voltype_t type; - const char *cellname, *volname, *suffix; + struct afs_server *server = NULL; char srvtmask; - int force, ret, loop, cellnamesz, volnamesz; - - _enter("%s,,%d,", name, rwpath); - - if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) { - printk("kAFS: unparsable volume name\n"); - return -EINVAL; - } - - /* determine the type of volume we're looking for */ - force = 0; - type = AFSVL_ROVOL; - - if (rwpath || name[0] == '%') { - type = AFSVL_RWVOL; - force = 1; - } - - suffix = strrchr(name, '.'); - if (suffix) { - if (strcmp(suffix, ".readonly") == 0) { - type = AFSVL_ROVOL; - force = 1; - } - else if (strcmp(suffix, ".backup") == 0) { - type = AFSVL_BACKVOL; - force = 1; - } - else if (suffix[1] == 0) { - } - else { - suffix = NULL; - } - } + int ret, loop; - /* split the cell and volume names */ - name++; - volname = strchr(name, ':'); - if (volname) { - cellname = name; - cellnamesz = volname - name; - volname++; - } - else { - volname = name; - cellname = NULL; - cellnamesz = 0; - } - - volnamesz = suffix ? suffix - volname : strlen(volname); - - _debug("CELL:%*.*s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s", - cellnamesz, cellnamesz, cellname ?: "", cell, - volnamesz, volnamesz, volname, suffix ?: "-", - type, - force ? " FORCE" : ""); - - /* lookup the cell record */ - if (cellname || !cell) { - ret = afs_cell_lookup(cellname, cellnamesz, &cell); - if (ret<0) { - printk("kAFS: unable to lookup cell '%s'\n", - cellname ?: ""); - goto error; - } - } - else { - afs_get_cell(cell); - } + _enter("{%*.*s,%d}", + params->volnamesz, params->volnamesz, params->volname, params->rwpath); /* lookup the volume location record */ - ret = afs_vlocation_lookup(cell, volname, volnamesz, &vlocation); - if (ret < 0) + vlocation = afs_vlocation_lookup(params->cell, params->key, + params->volname, params->volnamesz); + if (IS_ERR(vlocation)) { + ret = PTR_ERR(vlocation); + vlocation = NULL; goto error; + } /* make the final decision on the type we want */ ret = -ENOMEDIUM; - if (force && !(vlocation->vldb.vidmask & (1 << type))) + if (params->force && !(vlocation->vldb.vidmask & (1 << params->type))) goto error; srvtmask = 0; for (loop = 0; loop < vlocation->vldb.nservers; loop++) srvtmask |= vlocation->vldb.srvtmask[loop]; - if (force) { - if (!(srvtmask & (1 << type))) + if (params->force) { + if (!(srvtmask & (1 << params->type))) goto error; - } - else if (srvtmask & AFS_VOL_VTM_RO) { - type = AFSVL_ROVOL; - } - else if (srvtmask & AFS_VOL_VTM_RW) { - type = AFSVL_RWVOL; - } - else { + } else if (srvtmask & AFS_VOL_VTM_RO) { + params->type = AFSVL_ROVOL; + } else if (srvtmask & AFS_VOL_VTM_RW) { + params->type = AFSVL_RWVOL; + } else { goto error; } - down_write(&cell->vl_sem); + down_write(¶ms->cell->vl_sem); /* is the volume already active? */ - if (vlocation->vols[type]) { + if (vlocation->vols[params->type]) { /* yes - re-use it */ - volume = vlocation->vols[type]; + volume = vlocation->vols[params->type]; afs_get_volume(volume); goto success; } @@ -191,23 +100,24 @@ int afs_volume_lookup(const char *name, goto error_up; atomic_set(&volume->usage, 1); - volume->type = type; - volume->type_force = force; - volume->cell = cell; - volume->vid = vlocation->vldb.vid[type]; + volume->type = params->type; + volume->type_force = params->force; + volume->cell = params->cell; + volume->vid = vlocation->vldb.vid[params->type]; init_rwsem(&volume->server_sem); /* look up all the applicable server records */ for (loop = 0; loop < 8; loop++) { if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) { - ret = afs_server_lookup( - volume->cell, - &vlocation->vldb.servers[loop], - &volume->servers[volume->nservers]); - if (ret < 0) + server = afs_lookup_server( + volume->cell, &vlocation->vldb.servers[loop]); + if (IS_ERR(server)) { + ret = PTR_ERR(server); goto error_discard; + } + volume->servers[volume->nservers] = server; volume->nservers++; } } @@ -223,35 +133,34 @@ #endif afs_get_vlocation(vlocation); volume->vlocation = vlocation; - vlocation->vols[type] = volume; + vlocation->vols[volume->type] = volume; - success: +success: _debug("kAFS selected %s volume %08x", afs_voltypes[volume->type], volume->vid); - *_volume = volume; - ret = 0; + up_write(¶ms->cell->vl_sem); + afs_put_vlocation(vlocation); + _leave(" = %p", volume); + return volume; /* clean up */ - error_up: - up_write(&cell->vl_sem); - error: +error_up: + up_write(¶ms->cell->vl_sem); +error: afs_put_vlocation(vlocation); - afs_put_cell(cell); - - _leave(" = %d (%p)", ret, volume); - return ret; + _leave(" = %d", ret); + return ERR_PTR(ret); - error_discard: - up_write(&cell->vl_sem); +error_discard: + up_write(¶ms->cell->vl_sem); for (loop = volume->nservers - 1; loop >= 0; loop--) afs_put_server(volume->servers[loop]); kfree(volume); goto error; -} /* end afs_volume_lookup() */ +} -/*****************************************************************************/ /* * destroy a volume record */ @@ -265,10 +174,9 @@ void afs_put_volume(struct afs_volume *v _enter("%p", volume); - vlocation = volume->vlocation; + ASSERTCMP(atomic_read(&volume->usage), >, 0); - /* sanity check */ - BUG_ON(atomic_read(&volume->usage) <= 0); + vlocation = volume->vlocation; /* to prevent a race, the decrement and the dequeue must be effectively * atomic */ @@ -296,21 +204,27 @@ #endif kfree(volume); _leave(" [destroyed]"); -} /* end afs_put_volume() */ +} -/*****************************************************************************/ /* * pick a server to use to try accessing this volume * - returns with an elevated usage count on the server chosen */ -int afs_volume_pick_fileserver(struct afs_volume *volume, - struct afs_server **_server) +struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode) { + struct afs_volume *volume = vnode->volume; struct afs_server *server; int ret, state, loop; _enter("%s", volume->vlocation->vldb.name); + /* stick with the server we're already using if we can */ + if (vnode->server && vnode->server->fs_state == 0) { + afs_get_server(vnode->server); + _leave(" = %p [current]", vnode->server); + return vnode->server; + } + down_read(&volume->server_sem); /* handle the no-server case */ @@ -318,7 +232,7 @@ int afs_volume_pick_fileserver(struct af ret = volume->rjservers ? -ENOMEDIUM : -ESTALE; up_read(&volume->server_sem); _leave(" = %d [no servers]", ret); - return ret; + return ERR_PTR(ret); } /* basically, just search the list for the first live server and use @@ -328,15 +242,16 @@ int afs_volume_pick_fileserver(struct af server = volume->servers[loop]; state = server->fs_state; + _debug("consider %d [%d]", loop, state); + switch (state) { /* found an apparently healthy server */ case 0: afs_get_server(server); up_read(&volume->server_sem); - *_server = server; - _leave(" = 0 (picked %08x)", - ntohl(server->addr.s_addr)); - return 0; + _leave(" = %p (picked %08x)", + server, ntohl(server->addr.s_addr)); + return server; case -ENETUNREACH: if (ret == 0) @@ -372,20 +287,21 @@ int afs_volume_pick_fileserver(struct af */ up_read(&volume->server_sem); _leave(" = %d", ret); - return ret; -} /* end afs_volume_pick_fileserver() */ + return ERR_PTR(ret); +} -/*****************************************************************************/ /* * release a server after use * - releases the ref on the server struct that was acquired by picking * - records result of using a particular server to access a volume * - return 0 to try again, 1 if okay or to issue error + * - the caller must release the server struct if result was 0 */ -int afs_volume_release_fileserver(struct afs_volume *volume, +int afs_volume_release_fileserver(struct afs_vnode *vnode, struct afs_server *server, int result) { + struct afs_volume *volume = vnode->volume; unsigned loop; _enter("%s,%08x,%d", @@ -396,14 +312,16 @@ int afs_volume_release_fileserver(struct /* success */ case 0: server->fs_act_jif = jiffies; - break; + server->fs_state = 0; + _leave(""); + return 1; /* the fileserver denied all knowledge of the volume */ case -ENOMEDIUM: server->fs_act_jif = jiffies; down_write(&volume->server_sem); - /* first, find where the server is in the active list (if it + /* firstly, find where the server is in the active list (if it * is) */ for (loop = 0; loop < volume->nservers; loop++) if (volume->servers[loop] == server) @@ -441,6 +359,7 @@ int afs_volume_release_fileserver(struct case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: + case -ETIME: case -ETIMEDOUT: case -EREMOTEIO: /* mark the server as dead @@ -460,60 +379,17 @@ int afs_volume_release_fileserver(struct server->fs_act_jif = jiffies; case -ENOMEM: case -ENONET: - break; + /* tell the caller to accept the result */ + afs_put_server(server); + _leave(" [local failure]"); + return 1; } - /* tell the caller to accept the result */ - afs_put_server(server); - _leave(""); - return 1; - /* tell the caller to loop around and try the next server */ - try_next_server_upw: +try_next_server_upw: up_write(&volume->server_sem); - try_next_server: +try_next_server: afs_put_server(server); _leave(" [try next server]"); return 0; - -} /* end afs_volume_release_fileserver() */ - -/*****************************************************************************/ -/* - * match a volume hash record stored in the cache - */ -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_volume_cache_match(void *target, - const void *entry) -{ - const struct afs_cache_vhash *vhash = entry; - struct afs_volume *volume = target; - - _enter("{%u},{%u}", volume->type, vhash->vtype); - - if (volume->type == vhash->vtype) { - _leave(" = SUCCESS"); - return CACHEFS_MATCH_SUCCESS; - } - - _leave(" = FAILED"); - return CACHEFS_MATCH_FAILED; -} /* end afs_volume_cache_match() */ -#endif - -/*****************************************************************************/ -/* - * update a volume hash record stored in the cache - */ -#ifdef AFS_CACHING_SUPPORT -static void afs_volume_cache_update(void *source, void *entry) -{ - struct afs_cache_vhash *vhash = entry; - struct afs_volume *volume = source; - - _enter(""); - - vhash->vtype = volume->type; - -} /* end afs_volume_cache_update() */ -#endif +} diff --git a/fs/afs/volume.h b/fs/afs/volume.h deleted file mode 100644 index bfdcf19..0000000 --- a/fs/afs/volume.h +++ /dev/null @@ -1,140 +0,0 @@ -/* volume.h: AFS volume management - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_AFS_VOLUME_H -#define _LINUX_AFS_VOLUME_H - -#include "types.h" -#include "fsclient.h" -#include "kafstimod.h" -#include "kafsasyncd.h" -#include "cache.h" - -typedef enum { - AFS_VLUPD_SLEEP, /* sleeping waiting for update timer to fire */ - AFS_VLUPD_PENDING, /* on pending queue */ - AFS_VLUPD_INPROGRESS, /* op in progress */ - AFS_VLUPD_BUSYSLEEP, /* sleeping because server returned EBUSY */ - -} __attribute__((packed)) afs_vlocation_upd_t; - -/*****************************************************************************/ -/* - * entry in the cached volume location catalogue - */ -struct afs_cache_vlocation -{ - uint8_t name[64]; /* volume name (lowercase, padded with NULs) */ - uint8_t nservers; /* number of entries used in servers[] */ - uint8_t vidmask; /* voltype mask for vid[] */ - uint8_t srvtmask[8]; /* voltype masks for servers[] */ -#define AFS_VOL_VTM_RW 0x01 /* R/W version of the volume is available (on this server) */ -#define AFS_VOL_VTM_RO 0x02 /* R/O version of the volume is available (on this server) */ -#define AFS_VOL_VTM_BAK 0x04 /* backup version of the volume is available (on this server) */ - - afs_volid_t vid[3]; /* volume IDs for R/W, R/O and Bak volumes */ - struct in_addr servers[8]; /* fileserver addresses */ - time_t rtime; /* last retrieval time */ -}; - -#ifdef AFS_CACHING_SUPPORT -extern struct cachefs_index_def afs_vlocation_cache_index_def; -#endif - -/*****************************************************************************/ -/* - * volume -> vnode hash table entry - */ -struct afs_cache_vhash -{ - afs_voltype_t vtype; /* which volume variation */ - uint8_t hash_bucket; /* which hash bucket this represents */ -} __attribute__((packed)); - -#ifdef AFS_CACHING_SUPPORT -extern struct cachefs_index_def afs_volume_cache_index_def; -#endif - -/*****************************************************************************/ -/* - * AFS volume location record - */ -struct afs_vlocation -{ - atomic_t usage; - struct list_head link; /* link in cell volume location list */ - struct afs_timer timeout; /* decaching timer */ - struct afs_cell *cell; /* cell to which volume belongs */ -#ifdef AFS_CACHING_SUPPORT - struct cachefs_cookie *cache; /* caching cookie */ -#endif - struct afs_cache_vlocation vldb; /* volume information DB record */ - struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ - rwlock_t lock; /* access lock */ - unsigned long read_jif; /* time at which last read from vlserver */ - struct afs_timer upd_timer; /* update timer */ - struct afs_async_op upd_op; /* update operation */ - afs_vlocation_upd_t upd_state; /* update state */ - unsigned short upd_first_svix; /* first server index during update */ - unsigned short upd_curr_svix; /* current server index during update */ - unsigned short upd_rej_cnt; /* ENOMEDIUM count during update */ - unsigned short upd_busy_cnt; /* EBUSY count during update */ - unsigned short valid; /* T if valid */ -}; - -extern int afs_vlocation_lookup(struct afs_cell *cell, - const char *name, - unsigned namesz, - struct afs_vlocation **_vlocation); - -#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0) - -extern void afs_put_vlocation(struct afs_vlocation *vlocation); -extern void afs_vlocation_do_timeout(struct afs_vlocation *vlocation); - -/*****************************************************************************/ -/* - * AFS volume access record - */ -struct afs_volume -{ - atomic_t usage; - struct afs_cell *cell; /* cell to which belongs (unrefd ptr) */ - struct afs_vlocation *vlocation; /* volume location */ -#ifdef AFS_CACHING_SUPPORT - struct cachefs_cookie *cache; /* caching cookie */ -#endif - afs_volid_t vid; /* volume ID */ - afs_voltype_t type; /* type of volume */ - char type_force; /* force volume type (suppress R/O -> R/W) */ - unsigned short nservers; /* number of server slots filled */ - unsigned short rjservers; /* number of servers discarded due to -ENOMEDIUM */ - struct afs_server *servers[8]; /* servers on which volume resides (ordered) */ - struct rw_semaphore server_sem; /* lock for accessing current server */ -}; - -extern int afs_volume_lookup(const char *name, - struct afs_cell *cell, - int rwpath, - struct afs_volume **_volume); - -#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0) - -extern void afs_put_volume(struct afs_volume *volume); - -extern int afs_volume_pick_fileserver(struct afs_volume *volume, - struct afs_server **_server); - -extern int afs_volume_release_fileserver(struct afs_volume *volume, - struct afs_server *server, - int result); - -#endif /* _LINUX_AFS_VOLUME_H */ diff --git a/fs/bio.c b/fs/bio.c index 7618bcb..693940d 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -28,7 +28,7 @@ #include #include #include /* for struct sg_iovec */ -#define BIO_POOL_SIZE 256 +#define BIO_POOL_SIZE 2 static struct kmem_cache *bio_slab __read_mostly; @@ -38,7 +38,7 @@ #define BIOVEC_NR_POOLS 6 * a small number of entries is fine, not going to be performance critical. * basically we just need to survive */ -#define BIO_SPLIT_ENTRIES 8 +#define BIO_SPLIT_ENTRIES 2 mempool_t *bio_split_pool __read_mostly; struct biovec_slab { @@ -1120,7 +1120,7 @@ struct bio_pair *bio_split(struct bio *b * create memory pools for biovec's in a bio_set. * use the global biovec slabs created for general use. */ -static int biovec_create_pools(struct bio_set *bs, int pool_entries, int scale) +static int biovec_create_pools(struct bio_set *bs, int pool_entries) { int i; @@ -1128,9 +1128,6 @@ static int biovec_create_pools(struct bi struct biovec_slab *bp = bvec_slabs + i; mempool_t **bvp = bs->bvec_pools + i; - if (pool_entries > 1 && i >= scale) - pool_entries >>= 1; - *bvp = mempool_create_slab_pool(pool_entries, bp->slab); if (!*bvp) return -ENOMEM; @@ -1161,7 +1158,7 @@ void bioset_free(struct bio_set *bs) kfree(bs); } -struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size, int scale) +struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size) { struct bio_set *bs = kzalloc(sizeof(*bs), GFP_KERNEL); @@ -1172,7 +1169,7 @@ struct bio_set *bioset_create(int bio_po if (!bs->bio_pool) goto bad; - if (!biovec_create_pools(bs, bvec_pool_size, scale)) + if (!biovec_create_pools(bs, bvec_pool_size)) return bs; bad: @@ -1196,38 +1193,12 @@ static void __init biovec_init_slabs(voi static int __init init_bio(void) { - int megabytes, bvec_pool_entries; - int scale = BIOVEC_NR_POOLS; - bio_slab = kmem_cache_create("bio", sizeof(struct bio), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); biovec_init_slabs(); - megabytes = nr_free_pages() >> (20 - PAGE_SHIFT); - - /* - * find out where to start scaling - */ - if (megabytes <= 16) - scale = 0; - else if (megabytes <= 32) - scale = 1; - else if (megabytes <= 64) - scale = 2; - else if (megabytes <= 96) - scale = 3; - else if (megabytes <= 128) - scale = 4; - - /* - * Limit number of entries reserved -- mempools are only used when - * the system is completely unable to allocate memory, so we only - * need enough to make progress. - */ - bvec_pool_entries = 1 + scale; - - fs_bio_set = bioset_create(BIO_POOL_SIZE, bvec_pool_entries, scale); + fs_bio_set = bioset_create(BIO_POOL_SIZE, 2); if (!fs_bio_set) panic("bio: can't allocate bios\n"); diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 8b1c5d8..c68b055 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -266,6 +266,23 @@ static int do_siocgstamp(unsigned int fd return err; } +static int do_siocgstampns(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct compat_timespec __user *up = compat_ptr(arg); + struct timespec kts; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&kts); + set_fs(old_fs); + if (!err) { + err = put_user(kts.tv_sec, &up->tv_sec); + err |= __put_user(kts.tv_nsec, &up->tv_nsec); + } + return err; +} + struct ifmap32 { compat_ulong_t mem_start; compat_ulong_t mem_end; @@ -2437,6 +2454,7 @@ HANDLE_IOCTL(SIOCBRDELIF, dev_ifsioc) /* Note SIOCRTMSG is no longer, so this is safe and * the user would have seen just an -EINVAL anyways. */ HANDLE_IOCTL(SIOCRTMSG, ret_einval) HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp) +HANDLE_IOCTL(SIOCGSTAMPNS, do_siocgstampns) #endif #ifdef CONFIG_BLOCK HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo) diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 682f928..2e124e0 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -179,6 +179,48 @@ struct dentry *debugfs_create_u32(const } EXPORT_SYMBOL_GPL(debugfs_create_u32); +static void debugfs_u64_set(void *data, u64 val) +{ + *(u64 *)data = val; +} + +static u64 debugfs_u64_get(void *data) +{ + return *(u64 *)data; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n"); + +/** + * debugfs_create_u64 - create a debugfs file that is used to read and write an unsigned 64-bit value + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is %NULL, then the + * file will be created in the root of the debugfs filesystem. + * @value: a pointer to the variable that the file should read to and write + * from. + * + * This function creates a file in debugfs with the given name that + * contains the value of the variable @value. If the @mode variable is so + * set, it can be read from, and written to. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the debugfs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, %NULL will be returned. + * + * If debugfs is not enabled in the kernel, the value -%ENODEV will be + * returned. It is not wise to check for this value, but rather, check for + * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling + * code. + */ +struct dentry *debugfs_create_u64(const char *name, mode_t mode, + struct dentry *parent, u64 *value) +{ + return debugfs_create_file(name, mode, parent, value, &fops_u64); +} +EXPORT_SYMBOL_GPL(debugfs_create_u64); + static ssize_t read_file_bool(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { diff --git a/fs/ecryptfs/netlink.c b/fs/ecryptfs/netlink.c index e3aa225..fe91863 100644 --- a/fs/ecryptfs/netlink.c +++ b/fs/ecryptfs/netlink.c @@ -97,7 +97,7 @@ out: */ static int ecryptfs_process_nl_response(struct sk_buff *skb) { - struct nlmsghdr *nlh = (struct nlmsghdr*)skb->data; + struct nlmsghdr *nlh = nlmsg_hdr(skb); struct ecryptfs_message *msg = NLMSG_DATA(nlh); int rc; @@ -181,7 +181,7 @@ receive: "rc = [%d]\n", rc); return; } - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (!NLMSG_OK(nlh, skb->len)) { ecryptfs_printk(KERN_ERR, "Received corrupt netlink " "message\n"); @@ -229,7 +229,7 @@ int ecryptfs_init_netlink(void) ecryptfs_nl_sock = netlink_kernel_create(NETLINK_ECRYPTFS, 0, ecryptfs_receive_nl_message, - THIS_MODULE); + NULL, THIS_MODULE); if (!ecryptfs_nl_sock) { rc = -EIO; ecryptfs_printk(KERN_ERR, "Failed to create netlink socket\n"); diff --git a/fs/jffs2/LICENCE b/fs/jffs2/LICENCE index cd81d83..5628859 100644 --- a/fs/jffs2/LICENCE +++ b/fs/jffs2/LICENCE @@ -1,7 +1,7 @@ The files in this directory and elsewhere which refer to this LICENCE file are part of JFFS2, the Journalling Flash File System v2. - Copyright (C) 2001, 2002 Red Hat, Inc. + Copyright © 2001-2007 Red Hat, Inc. and others JFFS2 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 @@ -28,8 +28,3 @@ of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. -For information on obtaining alternative licences for JFFS2, see -http://sources.redhat.com/jffs2/jffs2-licence.html - - - $Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $ diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index 7f28ee0..c32b241 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile @@ -1,7 +1,6 @@ # # Makefile for the Linux Journalling Flash File System v2 (JFFS2) # -# $Id: Makefile.common,v 1.11 2005/09/07 08:34:53 havasi Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking index c8f0bd6..d14d5a4 100644 --- a/fs/jffs2/README.Locking +++ b/fs/jffs2/README.Locking @@ -1,4 +1,3 @@ - $Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $ JFFS2 LOCKING DOCUMENTATION --------------------------- diff --git a/fs/jffs2/TODO b/fs/jffs2/TODO index d0e23b2..5d3ea40 100644 --- a/fs/jffs2/TODO +++ b/fs/jffs2/TODO @@ -1,4 +1,3 @@ -$Id: TODO,v 1.18 2005/09/22 11:24:56 dedekind Exp $ - support asynchronous operation -- add a per-fs 'reserved_space' count, let each outstanding write reserve the _maximum_ amount of physical @@ -30,8 +29,6 @@ the full dirent, we only need to go to the flash in lookup() when we think we've got a match, and in readdir(). - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately? - - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into - jffs2_mark_node_obsolete(). Can all callers work it out? - Remove size from jffs2_raw_node_frag. dedekind: diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 73f0d60..a46101e 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -1,13 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2006 NEC Corporation + * Copyright © 2006 NEC Corporation * * Created by KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * */ + #include #include #include diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index fa327db..c84378c 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -1,13 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2006 NEC Corporation + * Copyright © 2006 NEC Corporation * * Created by KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * */ + struct jffs2_acl_entry { jint16_t e_tag; jint16_t e_perm; diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 888f236..0c82dfc 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: background.c,v 1.54 2005/05/20 21:37:12 gleixner Exp $ - * */ #include diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 07119c4..0ca2fff 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.85 2005/11/07 11:14:38 gleixner Exp $ - * */ #include diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index 7001ba2..485d065 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -1,16 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * Created by Arjan van de Ven * - * Copyright (C) 2004 Ferenc Havasi , + * Copyright © 2004 Ferenc Havasi , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.46 2005/11/07 11:14:38 gleixner Exp $ - * */ #include "compr.h" @@ -268,144 +266,6 @@ int jffs2_unregister_compressor(struct j return 0; } -#ifdef CONFIG_JFFS2_PROC - -#define JFFS2_STAT_BUF_SIZE 16000 - -char *jffs2_list_compressors(void) -{ - struct jffs2_compressor *this; - char *buf, *act_buf; - - act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); - list_for_each_entry(this, &jffs2_compressor_list, list) { - act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); - if ((this->disabled)||(!this->compress)) - act_buf += sprintf(act_buf,"disabled"); - else - act_buf += sprintf(act_buf,"enabled"); - act_buf += sprintf(act_buf,"\n"); - } - return buf; -} - -char *jffs2_stats(void) -{ - struct jffs2_compressor *this; - char *buf, *act_buf; - - act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); - - act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n"); - act_buf += sprintf(act_buf,"%10s ","none"); - act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, - none_stat_compr_size, none_stat_decompr_blocks); - spin_lock(&jffs2_compressor_list_lock); - list_for_each_entry(this, &jffs2_compressor_list, list) { - act_buf += sprintf(act_buf,"%10s ",this->name); - if ((this->disabled)||(!this->compress)) - act_buf += sprintf(act_buf,"- "); - else - act_buf += sprintf(act_buf,"+ "); - act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, - this->stat_compr_new_size, this->stat_compr_orig_size, - this->stat_decompr_blocks); - act_buf += sprintf(act_buf,"\n"); - } - spin_unlock(&jffs2_compressor_list_lock); - - return buf; -} - -char *jffs2_get_compression_mode_name(void) -{ - switch (jffs2_compression_mode) { - case JFFS2_COMPR_MODE_NONE: - return "none"; - case JFFS2_COMPR_MODE_PRIORITY: - return "priority"; - case JFFS2_COMPR_MODE_SIZE: - return "size"; - } - return "unkown"; -} - -int jffs2_set_compression_mode_name(const char *name) -{ - if (!strcmp("none",name)) { - jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; - return 0; - } - if (!strcmp("priority",name)) { - jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; - return 0; - } - if (!strcmp("size",name)) { - jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; - return 0; - } - return 1; -} - -static int jffs2_compressor_Xable(const char *name, int disabled) -{ - struct jffs2_compressor *this; - spin_lock(&jffs2_compressor_list_lock); - list_for_each_entry(this, &jffs2_compressor_list, list) { - if (!strcmp(this->name, name)) { - this->disabled = disabled; - spin_unlock(&jffs2_compressor_list_lock); - return 0; - } - } - spin_unlock(&jffs2_compressor_list_lock); - printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); - return 1; -} - -int jffs2_enable_compressor_name(const char *name) -{ - return jffs2_compressor_Xable(name, 0); -} - -int jffs2_disable_compressor_name(const char *name) -{ - return jffs2_compressor_Xable(name, 1); -} - -int jffs2_set_compressor_priority(const char *name, int priority) -{ - struct jffs2_compressor *this,*comp; - spin_lock(&jffs2_compressor_list_lock); - list_for_each_entry(this, &jffs2_compressor_list, list) { - if (!strcmp(this->name, name)) { - this->priority = priority; - comp = this; - goto reinsert; - } - } - spin_unlock(&jffs2_compressor_list_lock); - printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); - return 1; -reinsert: - /* list is sorted in the order of priority, so if - we change it we have to reinsert it into the - good place */ - list_del(&comp->list); - list_for_each_entry(this, &jffs2_compressor_list, list) { - if (this->priority < comp->priority) { - list_add(&comp->list, this->list.prev); - spin_unlock(&jffs2_compressor_list_lock); - return 0; - } - } - list_add_tail(&comp->list, &jffs2_compressor_list); - spin_unlock(&jffs2_compressor_list_lock); - return 0; -} - -#endif - void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) { if (orig != comprbuf) diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h index 509b8b1..68cc701 100644 --- a/fs/jffs2/compr.h +++ b/fs/jffs2/compr.h @@ -1,13 +1,10 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2004 Ferenc Havasi , + * Copyright © 2004 Ferenc Havasi , * University of Szeged, Hungary * - * For licensing information, see the file 'LICENCE' in the - * jffs2 directory. - * - * $Id: compr.h,v 1.9 2005/11/07 11:14:38 gleixner Exp $ + * For licensing information, see the file 'LICENCE' in this directory. * */ @@ -76,16 +73,6 @@ int jffs2_decompress(struct jffs2_sb_inf void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); -#ifdef CONFIG_JFFS2_PROC -int jffs2_enable_compressor_name(const char *name); -int jffs2_disable_compressor_name(const char *name); -int jffs2_set_compression_mode_name(const char *mode_name); -char *jffs2_get_compression_mode_name(void); -int jffs2_set_compressor_priority(const char *mode_name, int priority); -char *jffs2_list_compressors(void); -char *jffs2_stats(void); -#endif - /* Compressor modules */ /* These functions will be called by jffs2_compressors_init/exit */ diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 2eb1b74..0d0bfd2 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -1,13 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by Arjan van de Ven * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ * * * Very simple lz77-ish encoder. diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index e792e67..ea0431e 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -1,23 +1,94 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by Arjan van de Ven * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $ - * */ #include #include #include -#include "compr_rubin.h" -#include "histo_mips.h" +#include #include "compr.h" + +#define RUBIN_REG_SIZE 16 +#define UPPER_BIT_RUBIN (((long) 1)<<(RUBIN_REG_SIZE-1)) +#define LOWER_BITS_RUBIN ((((long) 1)<<(RUBIN_REG_SIZE-1))-1) + + +#define BIT_DIVIDER_MIPS 1043 +static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */ + +#include + +struct pushpull { + unsigned char *buf; + unsigned int buflen; + unsigned int ofs; + unsigned int reserve; +}; + +struct rubin_state { + unsigned long p; + unsigned long q; + unsigned long rec_q; + long bit_number; + struct pushpull pp; + int bit_divider; + int bits[8]; +}; + +static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) +{ + pp->buf = buf; + pp->buflen = buflen; + pp->ofs = ofs; + pp->reserve = reserve; +} + +static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) +{ + if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { + return -ENOSPC; + } + + if (bit) { + pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); + } + else { + pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); + } + pp->ofs++; + + return 0; +} + +static inline int pushedbits(struct pushpull *pp) +{ + return pp->ofs; +} + +static inline int pullbit(struct pushpull *pp) +{ + int bit; + + bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1; + + pp->ofs++; + return bit; +} + +static inline int pulledbits(struct pushpull *pp) +{ + return pp->ofs; +} + + static void init_rubin(struct rubin_state *rs, int div, int *bits) { int c; diff --git a/fs/jffs2/compr_rubin.h b/fs/jffs2/compr_rubin.h deleted file mode 100644 index bf1a934..0000000 --- a/fs/jffs2/compr_rubin.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Rubin encoder/decoder header */ -/* work started at : aug 3, 1994 */ -/* last modification : aug 15, 1994 */ -/* $Id: compr_rubin.h,v 1.7 2005/11/07 11:14:38 gleixner Exp $ */ - -#include "pushpull.h" - -#define RUBIN_REG_SIZE 16 -#define UPPER_BIT_RUBIN (((long) 1)<<(RUBIN_REG_SIZE-1)) -#define LOWER_BITS_RUBIN ((((long) 1)<<(RUBIN_REG_SIZE-1))-1) - - -struct rubin_state { - unsigned long p; - unsigned long q; - unsigned long rec_q; - long bit_number; - struct pushpull pp; - int bit_divider; - int bits[8]; -}; diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index 0c1fc6e..2b87fcc 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.32 2005/11/07 11:14:38 gleixner Exp $ - * */ #if !defined(__KERNEL__) && !defined(__ECOS) diff --git a/fs/jffs2/comprtest.c b/fs/jffs2/comprtest.c deleted file mode 100644 index f0fb8be..0000000 --- a/fs/jffs2/comprtest.c +++ /dev/null @@ -1,307 +0,0 @@ -/* $Id: comprtest.c,v 1.6 2005/11/07 11:14:38 gleixner Exp $ */ - -#include -#include -#include -#include -#if 0 -#define TESTDATA_LEN 512 -static unsigned char testdata[TESTDATA_LEN] = { - 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, - 0xb0, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x06, 0x00, 0x28, 0x00, - 0x1e, 0x00, 0x1b, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x80, 0x04, 0x08, - 0x34, 0x80, 0x04, 0x08, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xf4, 0x80, 0x04, 0x08, - 0xf4, 0x80, 0x04, 0x08, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08, - 0x00, 0x80, 0x04, 0x08, 0x0d, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x10, 0x95, 0x04, 0x08, - 0x10, 0x95, 0x04, 0x08, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x58, 0x95, 0x04, 0x08, - 0x58, 0x95, 0x04, 0x08, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x81, 0x04, 0x08, - 0x08, 0x81, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x75, - 0x78, 0x2e, 0x73, 0x6f, 0x2e, 0x32, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x0c, 0x83, 0x04, 0x08, 0x81, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x1c, 0x83, 0x04, 0x08, 0xac, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x2c, 0x83, 0x04, 0x08, 0xdd, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x3c, 0x83, 0x04, 0x08, 0x2e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x4c, 0x83, 0x04, 0x08, 0x7d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x00, 0x85, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x67, - 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, 0x63, - 0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x5f, 0x5f, 0x63}; -#else -#define TESTDATA_LEN 3481 -static unsigned char testdata[TESTDATA_LEN] = { - 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x22, 0x64, 0x62, 0x65, 0x6e, 0x63, 0x68, - 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x41, 0x58, - 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x62, 0x75, 0x66, 0x5b, 0x37, 0x30, 0x30, - 0x30, 0x30, 0x5d, 0x3b, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x20, - 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x09, 0x69, 0x6e, - 0x74, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x3b, 0x0a, 0x7d, 0x20, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x4d, 0x41, 0x58, 0x5f, - 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5d, 0x3b, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, - 0x5f, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, - 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, - 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x75, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, - 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, - 0x25, 0x64, 0x29, 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, - 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, - 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, - 0x69, 0x6c, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, - 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x3b, 0x0a, - 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, - 0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x49, 0x4e, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, - 0x28, 0x62, 0x75, 0x66, 0x29, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x3d, 0x20, 0x73, 0x3b, 0x0a, - 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6f, 0x70, - 0x65, 0x6e, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, - 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, - 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, - 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d, - 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x7c, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x3b, - 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, - 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, - 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, - 0x7a, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x7c, - 0x3d, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x64, 0x20, - 0x3d, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x66, 0x6c, - 0x61, 0x67, 0x73, 0x2c, 0x20, 0x30, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, - 0x28, 0x66, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, - 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x6f, 0x70, 0x65, 0x6e, - 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, - 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x68, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, - 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x64, 0x2c, - 0x20, 0x26, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, - 0x20, 0x3e, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, - 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, - 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, - 0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, - 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, - 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65, - 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69, - 0x6c, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x74, - 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x20, 0x65, 0x6c, - 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3c, 0x20, 0x73, 0x74, - 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, - 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, - 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25, - 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, - 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, - 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, - 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, - 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, - 0x30, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, - 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, - 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x66, 0x69, - 0x6c, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x65, 0x78, 0x69, 0x74, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, - 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x20, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, - 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x20, 0x25, 0x20, 0x31, 0x30, - 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, - 0x74, 0x66, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, - 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74, - 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, - 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, - 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x62, - 0x75, 0x66, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6d, 0x65, 0x6d, 0x73, - 0x65, 0x74, 0x28, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, - 0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, - 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, - 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, - 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, - 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, - 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x31, 0x0a, - 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, - 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, - 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, - 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, - 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, - 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, - 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, - 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, - 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, - 0x28, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, - 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, - 0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, - 0x6e, 0x74, 0x66, 0x28, 0x22, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, - 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x5c, 0x6e, - 0x22, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, - 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, - 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, - 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, - 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, - 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, - 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, - 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, - 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, - 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, - 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, - 0x64, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, - 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, - 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, - 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, - 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, - 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x7d, - 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, - 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, - 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, - 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, - 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, - 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, - 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, - 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x3a, 0x20, 0x68, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, - 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, - 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x66, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20, - 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6d, 0x6b, - 0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, - 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, - 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x6b, 0x64, 0x69, 0x72, - 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x30, 0x37, 0x30, 0x30, 0x29, 0x20, 0x21, 0x3d, - 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, - 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20, - 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, - 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, - 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, - 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, - 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x6d, 0x64, 0x69, 0x72, - 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, - 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, - 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e, - 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, - 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20, - 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, - 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, - 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, - 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, - 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6f, 0x6c, - 0x64, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6e, 0x65, 0x77, 0x29, 0x0a, 0x7b, 0x0a, - 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6f, 0x6c, 0x64, 0x29, 0x3b, 0x0a, - 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6e, 0x65, 0x77, 0x29, 0x3b, 0x0a, - 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6f, 0x6c, 0x64, - 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, - 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, - 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, - 0x0a, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x28, - 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, - 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, - 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, - 0x66, 0x20, 0x28, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x26, - 0x73, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, - 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x25, - 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, - 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x53, 0x5f, 0x49, - 0x53, 0x44, 0x49, 0x52, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x29, - 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, - 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69, - 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, - 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, - 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x20, 0x25, - 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, - 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, - 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, - 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, - 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65, - 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x35, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x73, - 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, - 0x35, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a -}; -#endif -static unsigned char comprbuf[TESTDATA_LEN]; -static unsigned char decomprbuf[TESTDATA_LEN]; - -int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, - unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); -unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *datalen, uint32_t *cdatalen); - -int init_module(void ) { - unsigned char comprtype; - uint32_t c, d; - int ret; - - printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - testdata[0],testdata[1],testdata[2],testdata[3], - testdata[4],testdata[5],testdata[6],testdata[7], - testdata[8],testdata[9],testdata[10],testdata[11], - testdata[12],testdata[13],testdata[14],testdata[15]); - d = TESTDATA_LEN; - c = TESTDATA_LEN; - comprtype = jffs2_compress(testdata, comprbuf, &d, &c); - - printk("jffs2_compress used compression type %d. Compressed size %d, uncompressed size %d\n", - comprtype, c, d); - printk("Compressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - comprbuf[0],comprbuf[1],comprbuf[2],comprbuf[3], - comprbuf[4],comprbuf[5],comprbuf[6],comprbuf[7], - comprbuf[8],comprbuf[9],comprbuf[10],comprbuf[11], - comprbuf[12],comprbuf[13],comprbuf[14],comprbuf[15]); - - ret = jffs2_decompress(comprtype, comprbuf, decomprbuf, c, d); - printk("jffs2_decompress returned %d\n", ret); - printk("Decompressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - decomprbuf[0],decomprbuf[1],decomprbuf[2],decomprbuf[3], - decomprbuf[4],decomprbuf[5],decomprbuf[6],decomprbuf[7], - decomprbuf[8],decomprbuf[9],decomprbuf[10],decomprbuf[11], - decomprbuf[12],decomprbuf[13],decomprbuf[14],decomprbuf[15]); - if (memcmp(decomprbuf, testdata, d)) - printk("Compression and decompression corrupted data\n"); - else - printk("Compression good for %d bytes\n", d); - return 1; -} diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 4189e4a..3a32c64 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -1,15 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: debug.c,v 1.12 2005/11/07 11:14:39 gleixner Exp $ - * */ + #include #include #include diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index f89c85d..2a49f2c 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -1,15 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: debug.h,v 1.21 2005/11/07 11:14:39 gleixner Exp $ - * */ + #ifndef _JFFS2_DEBUG_H_ #define _JFFS2_DEBUG_H_ diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 9fa2e27..c1dfca3 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir.c,v 1.90 2005/11/07 11:14:39 gleixner Exp $ - * */ #include diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index ad01210..66e7c2f 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.85 2005/09/20 14:53:15 dedekind Exp $ - * */ #include @@ -333,7 +331,7 @@ static int jffs2_block_check_erase(struc *bad_offset = ofs; - ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); + ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); goto fail; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index e82eeaf..9987127 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.104 2005/10/18 23:29:35 tpoynor Exp $ - * */ #include diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index abb90c0..1d3b7a9 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.66 2005/09/27 13:17:29 dedekind Exp $ - * */ #include @@ -672,6 +670,13 @@ static int jffs2_flash_setup(struct jffs return ret; } + /* and an UBI volume */ + if (jffs2_ubivol(c)) { + ret = jffs2_ubivol_setup(c); + if (ret) + return ret; + } + return ret; } @@ -690,4 +695,9 @@ void jffs2_flash_cleanup(struct jffs2_sb if (jffs2_nor_wbuf_flash(c)) { jffs2_nor_wbuf_flash_cleanup(c); } + + /* and an UBI volume */ + if (jffs2_ubivol(c)) { + jffs2_ubivol_cleanup(c); + } } diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 3a3cf22..2d99e06 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.155 2005/11/07 11:14:39 gleixner Exp $ - * */ #include @@ -144,7 +142,8 @@ int jffs2_garbage_collect_pass(struct jf c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); - BUG(); + up(&c->alloc_sem); + return -ENOSPC; } spin_unlock(&c->erase_completion_lock); diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index 6909983..f4d525b 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: ioctl.c,v 1.10 2005/11/07 11:14:40 gleixner Exp $ - * */ #include diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index 3a56607..0b78fdc 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -1,4 +1,13 @@ -/* $Id: jffs2_fs_i.h,v 1.19 2005/11/07 11:14:52 gleixner Exp $ */ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright © 2001-2007 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index ea88f69..b13298a 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -1,4 +1,13 @@ -/* $Id: jffs2_fs_sb.h,v 1.54 2005/09/21 13:37:34 dedekind Exp $ */ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright © 2001-2007 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 83f9881..35c1a5e 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc.c,v 1.31 2005/11/07 11:14:40 gleixner Exp $ - * */ #include diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 5a6b4d6..4bf8608 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.115 2005/11/07 11:14:40 gleixner Exp $ - * */ #include @@ -54,7 +52,7 @@ void jffs2_add_fd_to_list(struct jffs2_s *prev = new; } -void jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) +uint32_t jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) { struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); @@ -76,18 +74,24 @@ void jffs2_truncate_fragtree(struct jffs } if (size == 0) - return; + return 0; - /* - * If the last fragment starts at the RAM page boundary, it is - * REF_PRISTINE irrespective of its size. - */ frag = frag_last(list); + + /* Sanity check for truncation to longer than we started with... */ + if (!frag) + return 0; + if (frag->ofs + frag->size < size) + return frag->ofs + frag->size; + + /* If the last fragment starts at the RAM page boundary, it is + * REF_PRISTINE irrespective of its size. */ if (frag->node && (frag->ofs & (PAGE_CACHE_SIZE - 1)) == 0) { dbg_fragtree2("marking the last fragment 0x%08x-0x%08x REF_PRISTINE.\n", frag->ofs, frag->ofs + frag->size); frag->node->raw->flash_offset = ref_offset(frag->node->raw) | REF_PRISTINE; } + return size; } static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, @@ -397,466 +401,6 @@ int jffs2_add_full_dnode_to_inode(struct return 0; } -/* - * Check the data CRC of the node. - * - * Returns: 0 if the data CRC is correct; - * 1 - if incorrect; - * error code if an error occured. - */ -static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) -{ - struct jffs2_raw_node_ref *ref = tn->fn->raw; - int err = 0, pointed = 0; - struct jffs2_eraseblock *jeb; - unsigned char *buffer; - uint32_t crc, ofs, len; - size_t retlen; - - BUG_ON(tn->csize == 0); - - if (!jffs2_is_writebuffered(c)) - goto adj_acc; - - /* Calculate how many bytes were already checked */ - ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode); - len = ofs % c->wbuf_pagesize; - if (likely(len)) - len = c->wbuf_pagesize - len; - - if (len >= tn->csize) { - dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", - ref_offset(ref), tn->csize, ofs); - goto adj_acc; - } - - ofs += len; - len = tn->csize - len; - - dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n", - ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len); - -#ifndef __ECOS - /* TODO: instead, incapsulate point() stuff to jffs2_flash_read(), - * adding and jffs2_flash_read_end() interface. */ - if (c->mtd->point) { - err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); - if (!err && retlen < tn->csize) { - JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); - c->mtd->unpoint(c->mtd, buffer, ofs, len); - } else if (err) - JFFS2_WARNING("MTD point failed: error code %d.\n", err); - else - pointed = 1; /* succefully pointed to device */ - } -#endif - - if (!pointed) { - buffer = kmalloc(len, GFP_KERNEL); - if (unlikely(!buffer)) - return -ENOMEM; - - /* TODO: this is very frequent pattern, make it a separate - * routine */ - err = jffs2_flash_read(c, ofs, len, &retlen, buffer); - if (err) { - JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err); - goto free_out; - } - - if (retlen != len) { - JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len); - err = -EIO; - goto free_out; - } - } - - /* Continue calculating CRC */ - crc = crc32(tn->partial_crc, buffer, len); - if(!pointed) - kfree(buffer); -#ifndef __ECOS - else - c->mtd->unpoint(c->mtd, buffer, ofs, len); -#endif - - if (crc != tn->data_crc) { - JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", - ofs, tn->data_crc, crc); - return 1; - } - -adj_acc: - jeb = &c->blocks[ref->flash_offset / c->sector_size]; - len = ref_totlen(c, jeb, ref); - - /* - * Mark the node as having been checked and fix the - * accounting accordingly. - */ - spin_lock(&c->erase_completion_lock); - jeb->used_size += len; - jeb->unchecked_size -= len; - c->used_size += len; - c->unchecked_size -= len; - spin_unlock(&c->erase_completion_lock); - - return 0; - -free_out: - if(!pointed) - kfree(buffer); -#ifndef __ECOS - else - c->mtd->unpoint(c->mtd, buffer, ofs, len); -#endif - return err; -} - -/* - * Helper function for jffs2_add_older_frag_to_fragtree(). - * - * Checks the node if we are in the checking stage. - */ -static int check_node(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn) -{ - int ret; - - BUG_ON(ref_obsolete(tn->fn->raw)); - - /* We only check the data CRC of unchecked nodes */ - if (ref_flags(tn->fn->raw) != REF_UNCHECKED) - return 0; - - dbg_fragtree2("check node %#04x-%#04x, phys offs %#08x.\n", - tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw)); - - ret = check_node_data(c, tn); - if (unlikely(ret < 0)) { - JFFS2_ERROR("check_node_data() returned error: %d.\n", - ret); - } else if (unlikely(ret > 0)) { - dbg_fragtree2("CRC error, mark it obsolete.\n"); - jffs2_mark_node_obsolete(c, tn->fn->raw); - } - - return ret; -} - -/* - * Helper function for jffs2_add_older_frag_to_fragtree(). - * - * Called when the new fragment that is being inserted - * splits a hole fragment. - */ -static int split_hole(struct jffs2_sb_info *c, struct rb_root *root, - struct jffs2_node_frag *newfrag, struct jffs2_node_frag *hole) -{ - dbg_fragtree2("fragment %#04x-%#04x splits the hole %#04x-%#04x\n", - newfrag->ofs, newfrag->ofs + newfrag->size, hole->ofs, hole->ofs + hole->size); - - if (hole->ofs == newfrag->ofs) { - /* - * Well, the new fragment actually starts at the same offset as - * the hole. - */ - if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { - /* - * We replace the overlapped left part of the hole by - * the new node. - */ - - dbg_fragtree2("insert fragment %#04x-%#04x and cut the left part of the hole\n", - newfrag->ofs, newfrag->ofs + newfrag->size); - rb_replace_node(&hole->rb, &newfrag->rb, root); - - hole->ofs += newfrag->size; - hole->size -= newfrag->size; - - /* - * We know that 'hole' should be the right hand - * fragment. - */ - jffs2_fragtree_insert(hole, newfrag); - rb_insert_color(&hole->rb, root); - } else { - /* - * Ah, the new fragment is of the same size as the hole. - * Relace the hole by it. - */ - dbg_fragtree2("insert fragment %#04x-%#04x and overwrite hole\n", - newfrag->ofs, newfrag->ofs + newfrag->size); - rb_replace_node(&hole->rb, &newfrag->rb, root); - jffs2_free_node_frag(hole); - } - } else { - /* The new fragment lefts some hole space at the left */ - - struct jffs2_node_frag * newfrag2 = NULL; - - if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { - /* The new frag also lefts some space at the right */ - newfrag2 = new_fragment(NULL, newfrag->ofs + - newfrag->size, hole->ofs + hole->size - - newfrag->ofs - newfrag->size); - if (unlikely(!newfrag2)) { - jffs2_free_node_frag(newfrag); - return -ENOMEM; - } - } - - hole->size = newfrag->ofs - hole->ofs; - dbg_fragtree2("left the hole %#04x-%#04x at the left and inserd fragment %#04x-%#04x\n", - hole->ofs, hole->ofs + hole->size, newfrag->ofs, newfrag->ofs + newfrag->size); - - jffs2_fragtree_insert(newfrag, hole); - rb_insert_color(&newfrag->rb, root); - - if (newfrag2) { - dbg_fragtree2("left the hole %#04x-%#04x at the right\n", - newfrag2->ofs, newfrag2->ofs + newfrag2->size); - jffs2_fragtree_insert(newfrag2, newfrag); - rb_insert_color(&newfrag2->rb, root); - } - } - - return 0; -} - -/* - * This function is used when we build inode. It expects the nodes are passed - * in the decreasing version order. The whole point of this is to improve the - * inodes checking on NAND: we check the nodes' data CRC only when they are not - * obsoleted. Previously, add_frag_to_fragtree() function was used and - * nodes were passed to it in the increasing version ordes and CRCs of all - * nodes were checked. - * - * Note: tn->fn->size shouldn't be zero. - * - * Returns 0 if the node was inserted - * 1 if it wasn't inserted (since it is obsolete) - * < 0 an if error occured - */ -int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - struct jffs2_tmp_dnode_info *tn) -{ - struct jffs2_node_frag *this, *newfrag; - uint32_t lastend; - struct jffs2_full_dnode *fn = tn->fn; - struct rb_root *root = &f->fragtree; - uint32_t fn_size = fn->size, fn_ofs = fn->ofs; - int err, checked = 0; - int ref_flag; - - dbg_fragtree("insert fragment %#04x-%#04x, ver %u\n", fn_ofs, fn_ofs + fn_size, tn->version); - - /* Skip all the nodes which are completed before this one starts */ - this = jffs2_lookup_node_frag(root, fn_ofs); - if (this) - dbg_fragtree2("'this' found %#04x-%#04x (%s)\n", this->ofs, this->ofs + this->size, this->node ? "data" : "hole"); - - if (this) - lastend = this->ofs + this->size; - else - lastend = 0; - - /* Detect the preliminary type of node */ - if (fn->size >= PAGE_CACHE_SIZE) - ref_flag = REF_PRISTINE; - else - ref_flag = REF_NORMAL; - - /* See if we ran off the end of the root */ - if (lastend <= fn_ofs) { - /* We did */ - - /* - * We are going to insert the new node into the - * fragment tree, so check it. - */ - err = check_node(c, f, tn); - if (err != 0) - return err; - - fn->frags = 1; - - newfrag = new_fragment(fn, fn_ofs, fn_size); - if (unlikely(!newfrag)) - return -ENOMEM; - - err = no_overlapping_node(c, root, newfrag, this, lastend); - if (unlikely(err != 0)) { - jffs2_free_node_frag(newfrag); - return err; - } - - goto out_ok; - } - - fn->frags = 0; - - while (1) { - /* - * Here we have: - * fn_ofs < this->ofs + this->size && fn_ofs >= this->ofs. - * - * Remember, 'this' has higher version, any non-hole node - * which is already in the fragtree is newer then the newly - * inserted. - */ - if (!this->node) { - /* - * 'this' is the hole fragment, so at least the - * beginning of the new fragment is valid. - */ - - /* - * We are going to insert the new node into the - * fragment tree, so check it. - */ - if (!checked) { - err = check_node(c, f, tn); - if (unlikely(err != 0)) - return err; - checked = 1; - } - - if (this->ofs + this->size >= fn_ofs + fn_size) { - /* We split the hole on two parts */ - - fn->frags += 1; - newfrag = new_fragment(fn, fn_ofs, fn_size); - if (unlikely(!newfrag)) - return -ENOMEM; - - err = split_hole(c, root, newfrag, this); - if (unlikely(err)) - return err; - goto out_ok; - } - - /* - * The beginning of the new fragment is valid since it - * overlaps the hole node. - */ - - ref_flag = REF_NORMAL; - - fn->frags += 1; - newfrag = new_fragment(fn, fn_ofs, - this->ofs + this->size - fn_ofs); - if (unlikely(!newfrag)) - return -ENOMEM; - - if (fn_ofs == this->ofs) { - /* - * The new node starts at the same offset as - * the hole and supersieds the hole. - */ - dbg_fragtree2("add the new fragment instead of hole %#04x-%#04x, refcnt %d\n", - fn_ofs, fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); - - rb_replace_node(&this->rb, &newfrag->rb, root); - jffs2_free_node_frag(this); - } else { - /* - * The hole becomes shorter as its right part - * is supersieded by the new fragment. - */ - dbg_fragtree2("reduce size of hole %#04x-%#04x to %#04x-%#04x\n", - this->ofs, this->ofs + this->size, this->ofs, this->ofs + this->size - newfrag->size); - - dbg_fragtree2("add new fragment %#04x-%#04x, refcnt %d\n", fn_ofs, - fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); - - this->size -= newfrag->size; - jffs2_fragtree_insert(newfrag, this); - rb_insert_color(&newfrag->rb, root); - } - - fn_ofs += newfrag->size; - fn_size -= newfrag->size; - this = rb_entry(rb_next(&newfrag->rb), - struct jffs2_node_frag, rb); - - dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", - this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); - } - - /* - * 'This' node is not the hole so it obsoletes the new fragment - * either fully or partially. - */ - if (this->ofs + this->size >= fn_ofs + fn_size) { - /* The new node is obsolete, drop it */ - if (fn->frags == 0) { - dbg_fragtree2("%#04x-%#04x is obsolete, mark it obsolete\n", fn_ofs, fn_ofs + fn_size); - ref_flag = REF_OBSOLETE; - } - goto out_ok; - } else { - struct jffs2_node_frag *new_this; - - /* 'This' node obsoletes the beginning of the new node */ - dbg_fragtree2("the beginning %#04x-%#04x is obsolete\n", fn_ofs, this->ofs + this->size); - - ref_flag = REF_NORMAL; - - fn_size -= this->ofs + this->size - fn_ofs; - fn_ofs = this->ofs + this->size; - dbg_fragtree2("now considering %#04x-%#04x\n", fn_ofs, fn_ofs + fn_size); - - new_this = rb_entry(rb_next(&this->rb), struct jffs2_node_frag, rb); - if (!new_this) { - /* - * There is no next fragment. Add the rest of - * the new node as the right-hand child. - */ - if (!checked) { - err = check_node(c, f, tn); - if (unlikely(err != 0)) - return err; - checked = 1; - } - - fn->frags += 1; - newfrag = new_fragment(fn, fn_ofs, fn_size); - if (unlikely(!newfrag)) - return -ENOMEM; - - dbg_fragtree2("there are no more fragments, insert %#04x-%#04x\n", - newfrag->ofs, newfrag->ofs + newfrag->size); - rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); - rb_insert_color(&newfrag->rb, root); - goto out_ok; - } else { - this = new_this; - dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", - this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); - } - } - } - -out_ok: - BUG_ON(fn->size < PAGE_CACHE_SIZE && ref_flag == REF_PRISTINE); - - if (ref_flag == REF_OBSOLETE) { - dbg_fragtree2("the node is obsolete now\n"); - /* jffs2_mark_node_obsolete() will adjust space accounting */ - jffs2_mark_node_obsolete(c, fn->raw); - return 1; - } - - dbg_fragtree2("the node is \"%s\" now\n", ref_flag == REF_NORMAL ? "REF_NORMAL" : "REF_PRISTINE"); - - /* Space accounting was adjusted at check_node_data() */ - spin_lock(&c->erase_completion_lock); - fn->raw->flash_offset = ref_offset(fn->raw) | ref_flag; - spin_unlock(&c->erase_completion_lock); - - return 0; -} - void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) { spin_lock(&c->inocache_lock); diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 4178b4b..25126a0 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.140 2005/09/07 08:34:54 havasi Exp $ - * */ #ifndef __JFFS2_NODELIST_H__ @@ -40,6 +38,9 @@ #define cpu_to_je16(x) ((jint16_t){x}) #define cpu_to_je32(x) ((jint32_t){x}) #define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) +#define constant_cpu_to_je16(x) ((jint16_t){x}) +#define constant_cpu_to_je32(x) ((jint32_t){x}) + #define je16_to_cpu(x) ((x).v16) #define je32_to_cpu(x) ((x).v32) #define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) @@ -48,6 +49,9 @@ #define cpu_to_je16(x) ((jint16_t){cpu_t #define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) #define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) +#define constant_cpu_to_je16(x) ((jint16_t){__constant_cpu_to_be16(x)}) +#define constant_cpu_to_je32(x) ((jint32_t){__constant_cpu_to_be32(x)}) + #define je16_to_cpu(x) (be16_to_cpu(x.v16)) #define je32_to_cpu(x) (be32_to_cpu(x.v32)) #define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) @@ -56,6 +60,9 @@ #define cpu_to_je16(x) ((jint16_t){cpu_t #define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) #define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) +#define constant_cpu_to_je16(x) ((jint16_t){__constant_cpu_to_le16(x)}) +#define constant_cpu_to_je32(x) ((jint32_t){__constant_cpu_to_le32(x)}) + #define je16_to_cpu(x) (le16_to_cpu(x.v16)) #define je32_to_cpu(x) (le32_to_cpu(x.v32)) #define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) @@ -216,7 +223,20 @@ struct jffs2_tmp_dnode_info uint32_t version; uint32_t data_crc; uint32_t partial_crc; - uint32_t csize; + uint16_t csize; + uint16_t overlapped; +}; + +/* Temporary data structure used during readinode. */ +struct jffs2_readinode_info +{ + struct rb_root tn_root; + struct jffs2_tmp_dnode_info *mdata_tn; + uint32_t highest_version; + uint32_t latest_mctime; + uint32_t mctime_ver; + struct jffs2_full_dirent *fds; + struct jffs2_raw_node_ref *latest_ref; }; struct jffs2_full_dirent @@ -319,6 +339,15 @@ #define frag_left(frag) rb_entry((frag)- #define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) #define frag_erase(frag, list) rb_erase(&frag->rb, list); +#define tn_next(tn) rb_entry(rb_next(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) +#define tn_prev(tn) rb_entry(rb_prev(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) +#define tn_parent(tn) rb_entry(rb_parent(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) +#define tn_left(tn) rb_entry((tn)->rb.rb_left, struct jffs2_tmp_dnode_info, rb) +#define tn_right(tn) rb_entry((tn)->rb.rb_right, struct jffs2_tmp_dnode_info, rb) +#define tn_erase(tn, list) rb_erase(&tn->rb, list); +#define tn_last(list) rb_entry(rb_last(list), struct jffs2_tmp_dnode_info, rb) +#define tn_first(list) rb_entry(rb_first(list), struct jffs2_tmp_dnode_info, rb) + /* nodelist.c */ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state); @@ -333,8 +362,7 @@ struct rb_node *rb_next(struct rb_node * struct rb_node *rb_prev(struct rb_node *); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); -void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); -int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn); +uint32_t jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t ofs, uint32_t len, diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index d883769..dbc908a 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.127 2005/09/20 15:49:12 dedekind Exp $ - * */ #include @@ -172,6 +170,11 @@ int jffs2_reserve_space_gc(struct jffs2_ static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { + if (c->nextblock == NULL) { + D1(printk(KERN_DEBUG "jffs2_close_nextblock: Erase block at 0x%08x has already been placed in a list\n", + jeb->offset)); + return; + } /* Check, if we have a dirty block now, or if it was dirty already */ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { c->dirty_size += jeb->wasted_size; diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index e07a0ed..80daea9 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2002-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.64 2005/09/30 13:59:13 dedekind Exp $ - * */ #ifndef __JFFS2_OS_LINUX_H__ @@ -98,6 +96,9 @@ #define jffs2_dataflash_cleanup(c) do {} #define jffs2_nor_wbuf_flash(c) (0) #define jffs2_nor_wbuf_flash_setup(c) (0) #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0) +#define jffs2_ubivol(c) (0) +#define jffs2_ubivol_setup(c) (0) +#define jffs2_ubivol_cleanup(c) do {} while (0) #else /* NAND and/or ECC'd NOR support present */ @@ -133,6 +134,9 @@ void jffs2_nand_flash_cleanup(struct jff #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH) int jffs2_dataflash_setup(struct jffs2_sb_info *c); void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); +#define jffs2_ubivol(c) (c->mtd->type == MTD_UBIVOLUME) +int jffs2_ubivol_setup(struct jffs2_sb_info *c); +void jffs2_ubivol_cleanup(struct jffs2_sb_info *c); #define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE)) int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); diff --git a/fs/jffs2/pushpull.h b/fs/jffs2/pushpull.h deleted file mode 100644 index c0c2a91..0000000 --- a/fs/jffs2/pushpull.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * JFFS2 -- Journalling Flash File System, Version 2. - * - * Copyright (C) 2001, 2002 Red Hat, Inc. - * - * Created by David Woodhouse - * - * For licensing information, see the file 'LICENCE' in this directory. - * - * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $ - * - */ - -#ifndef __PUSHPULL_H__ -#define __PUSHPULL_H__ - -#include - -struct pushpull { - unsigned char *buf; - unsigned int buflen; - unsigned int ofs; - unsigned int reserve; -}; - - -static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) -{ - pp->buf = buf; - pp->buflen = buflen; - pp->ofs = ofs; - pp->reserve = reserve; -} - -static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) -{ - if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { - return -ENOSPC; - } - - if (bit) { - pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); - } - else { - pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); - } - pp->ofs++; - - return 0; -} - -static inline int pushedbits(struct pushpull *pp) -{ - return pp->ofs; -} - -static inline int pullbit(struct pushpull *pp) -{ - int bit; - - bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1; - - pp->ofs++; - return bit; -} - -static inline int pulledbits(struct pushpull *pp) -{ - return pp->ofs; -} - -#endif /* __PUSHPULL_H__ */ diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c index f3b86da..cfe05c1 100644 --- a/fs/jffs2/read.c +++ b/fs/jffs2/read.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.42 2005/11/07 11:14:41 gleixner Exp $ - * */ #include diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 717a48c..6aff389 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.143 2005/11/07 11:14:41 gleixner Exp $ - * */ #include @@ -22,30 +20,510 @@ #include #include "nodelist.h" /* - * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in - * order of increasing version. + * Check the data CRC of the node. + * + * Returns: 0 if the data CRC is correct; + * 1 - if incorrect; + * error code if an error occured. */ -static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list) +static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) { - struct rb_node **p = &list->rb_node; - struct rb_node * parent = NULL; - struct jffs2_tmp_dnode_info *this; - - while (*p) { - parent = *p; - this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); - - /* There may actually be a collision here, but it doesn't - actually matter. As long as the two nodes with the same - version are together, it's all fine. */ - if (tn->version > this->version) - p = &(*p)->rb_left; + struct jffs2_raw_node_ref *ref = tn->fn->raw; + int err = 0, pointed = 0; + struct jffs2_eraseblock *jeb; + unsigned char *buffer; + uint32_t crc, ofs, len; + size_t retlen; + + BUG_ON(tn->csize == 0); + + if (!jffs2_is_writebuffered(c)) + goto adj_acc; + + /* Calculate how many bytes were already checked */ + ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode); + len = ofs % c->wbuf_pagesize; + if (likely(len)) + len = c->wbuf_pagesize - len; + + if (len >= tn->csize) { + dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", + ref_offset(ref), tn->csize, ofs); + goto adj_acc; + } + + ofs += len; + len = tn->csize - len; + + dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n", + ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len); + +#ifndef __ECOS + /* TODO: instead, incapsulate point() stuff to jffs2_flash_read(), + * adding and jffs2_flash_read_end() interface. */ + if (c->mtd->point) { + err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); + if (!err && retlen < tn->csize) { + JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); + c->mtd->unpoint(c->mtd, buffer, ofs, len); + } else if (err) + JFFS2_WARNING("MTD point failed: error code %d.\n", err); else - p = &(*p)->rb_right; + pointed = 1; /* succefully pointed to device */ + } +#endif + + if (!pointed) { + buffer = kmalloc(len, GFP_KERNEL); + if (unlikely(!buffer)) + return -ENOMEM; + + /* TODO: this is very frequent pattern, make it a separate + * routine */ + err = jffs2_flash_read(c, ofs, len, &retlen, buffer); + if (err) { + JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err); + goto free_out; + } + + if (retlen != len) { + JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len); + err = -EIO; + goto free_out; + } + } + + /* Continue calculating CRC */ + crc = crc32(tn->partial_crc, buffer, len); + if(!pointed) + kfree(buffer); +#ifndef __ECOS + else + c->mtd->unpoint(c->mtd, buffer, ofs, len); +#endif + + if (crc != tn->data_crc) { + JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", + ofs, tn->data_crc, crc); + return 1; } - rb_link_node(&tn->rb, parent, p); - rb_insert_color(&tn->rb, list); +adj_acc: + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, ref); + /* If it should be REF_NORMAL, it'll get marked as such when + we build the fragtree, shortly. No need to worry about GC + moving it while it's marked REF_PRISTINE -- GC won't happen + till we've finished checking every inode anyway. */ + ref->flash_offset |= REF_PRISTINE; + /* + * Mark the node as having been checked and fix the + * accounting accordingly. + */ + spin_lock(&c->erase_completion_lock); + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); + + return 0; + +free_out: + if(!pointed) + kfree(buffer); +#ifndef __ECOS + else + c->mtd->unpoint(c->mtd, buffer, ofs, len); +#endif + return err; +} + +/* + * Helper function for jffs2_add_older_frag_to_fragtree(). + * + * Checks the node if we are in the checking stage. + */ +static int check_tn_node(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) +{ + int ret; + + BUG_ON(ref_obsolete(tn->fn->raw)); + + /* We only check the data CRC of unchecked nodes */ + if (ref_flags(tn->fn->raw) != REF_UNCHECKED) + return 0; + + dbg_readinode("check node %#04x-%#04x, phys offs %#08x\n", + tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw)); + + ret = check_node_data(c, tn); + if (unlikely(ret < 0)) { + JFFS2_ERROR("check_node_data() returned error: %d.\n", + ret); + } else if (unlikely(ret > 0)) { + dbg_readinode("CRC error, mark it obsolete.\n"); + jffs2_mark_node_obsolete(c, tn->fn->raw); + } + + return ret; +} + +static struct jffs2_tmp_dnode_info *jffs2_lookup_tn(struct rb_root *tn_root, uint32_t offset) +{ + struct rb_node *next; + struct jffs2_tmp_dnode_info *tn = NULL; + + dbg_readinode("root %p, offset %d\n", tn_root, offset); + + next = tn_root->rb_node; + + while (next) { + tn = rb_entry(next, struct jffs2_tmp_dnode_info, rb); + + if (tn->fn->ofs < offset) + next = tn->rb.rb_right; + else if (tn->fn->ofs >= offset) + next = tn->rb.rb_left; + else + break; + } + + return tn; +} + + +static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) +{ + jffs2_mark_node_obsolete(c, tn->fn->raw); + jffs2_free_full_dnode(tn->fn); + jffs2_free_tmp_dnode_info(tn); +} +/* + * This function is used when we read an inode. Data nodes arrive in + * arbitrary order -- they may be older or newer than the nodes which + * are already in the tree. Where overlaps occur, the older node can + * be discarded as long as the newer passes the CRC check. We don't + * bother to keep track of holes in this rbtree, and neither do we deal + * with frags -- we can have multiple entries starting at the same + * offset, and the one with the smallest length will come first in the + * ordering. + * + * Returns 0 if the node was inserted + * 1 if the node is obsolete (because we can't mark it so yet) + * < 0 an if error occurred + */ +static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, + struct jffs2_readinode_info *rii, + struct jffs2_tmp_dnode_info *tn) +{ + uint32_t fn_end = tn->fn->ofs + tn->fn->size; + struct jffs2_tmp_dnode_info *insert_point = NULL, *this; + + dbg_readinode("insert fragment %#04x-%#04x, ver %u\n", tn->fn->ofs, fn_end, tn->version); + + /* If a node has zero dsize, we only have to keep if it if it might be the + node with highest version -- i.e. the one which will end up as f->metadata. + Note that such nodes won't be REF_UNCHECKED since there are no data to + check anyway. */ + if (!tn->fn->size) { + if (rii->mdata_tn) { + /* We had a candidate mdata node already */ + dbg_readinode("kill old mdata with ver %d\n", rii->mdata_tn->version); + jffs2_kill_tn(c, rii->mdata_tn); + } + rii->mdata_tn = tn; + dbg_readinode("keep new mdata with ver %d\n", tn->version); + return 0; + } + + /* Find the earliest node which _may_ be relevant to this one */ + this = jffs2_lookup_tn(&rii->tn_root, tn->fn->ofs); + if (!this) { + /* First addition to empty tree. $DEITY how I love the easy cases */ + rb_link_node(&tn->rb, NULL, &rii->tn_root.rb_node); + rb_insert_color(&tn->rb, &rii->tn_root); + dbg_readinode("keep new frag\n"); + return 0; + } + + /* If we add a new node it'll be somewhere under here. */ + insert_point = this; + + /* If the node is coincident with another at a lower address, + back up until the other node is found. It may be relevant */ + while (tn->overlapped) + tn = tn_prev(tn); + + dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole"); + + while (this) { + if (this->fn->ofs > fn_end) + break; + dbg_readinode("Ponder this ver %d, 0x%x-0x%x\n", + this->version, this->fn->ofs, this->fn->size); + + if (this->version == tn->version) { + /* Version number collision means REF_PRISTINE GC. Accept either of them + as long as the CRC is correct. Check the one we have already... */ + if (!check_tn_node(c, this)) { + /* The one we already had was OK. Keep it and throw away the new one */ + dbg_readinode("Like old node. Throw away new\n"); + jffs2_kill_tn(c, tn); + return 0; + } else { + /* Who cares if the new one is good; keep it for now anyway. */ + rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); + /* Same overlapping from in front and behind */ + tn->overlapped = this->overlapped; + jffs2_kill_tn(c, this); + dbg_readinode("Like new node. Throw away old\n"); + return 0; + } + } + if (this->version < tn->version && + this->fn->ofs >= tn->fn->ofs && + this->fn->ofs + this->fn->size <= fn_end) { + /* New node entirely overlaps 'this' */ + if (check_tn_node(c, tn)) { + dbg_readinode("new node bad CRC\n"); + jffs2_kill_tn(c, tn); + return 0; + } + /* ... and is good. Kill 'this'... */ + rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); + tn->overlapped = this->overlapped; + jffs2_kill_tn(c, this); + /* ... and any subsequent nodes which are also overlapped */ + this = tn_next(tn); + while (this && this->fn->ofs + this->fn->size < fn_end) { + struct jffs2_tmp_dnode_info *next = tn_next(this); + if (this->version < tn->version) { + tn_erase(this, &rii->tn_root); + dbg_readinode("Kill overlapped ver %d, 0x%x-0x%x\n", + this->version, this->fn->ofs, + this->fn->ofs+this->fn->size); + jffs2_kill_tn(c, this); + } + this = next; + } + dbg_readinode("Done inserting new\n"); + return 0; + } + if (this->version > tn->version && + this->fn->ofs <= tn->fn->ofs && + this->fn->ofs+this->fn->size >= fn_end) { + /* New node entirely overlapped by 'this' */ + if (!check_tn_node(c, this)) { + dbg_readinode("Good CRC on old node. Kill new\n"); + jffs2_kill_tn(c, tn); + return 0; + } + /* ... but 'this' was bad. Replace it... */ + rb_replace_node(&this->rb, &tn->rb, &rii->tn_root); + dbg_readinode("Bad CRC on old overlapping node. Kill it\n"); + jffs2_kill_tn(c, this); + return 0; + } + /* We want to be inserted under the last node which is + either at a lower offset _or_ has a smaller range */ + if (this->fn->ofs < tn->fn->ofs || + (this->fn->ofs == tn->fn->ofs && + this->fn->size <= tn->fn->size)) + insert_point = this; + + this = tn_next(this); + } + dbg_readinode("insert_point %p, ver %d, 0x%x-0x%x, ov %d\n", + insert_point, insert_point->version, insert_point->fn->ofs, + insert_point->fn->ofs+insert_point->fn->size, + insert_point->overlapped); + /* We neither completely obsoleted nor were completely + obsoleted by an earlier node. Insert under insert_point */ + { + struct rb_node *parent = &insert_point->rb; + struct rb_node **link = &parent; + + while (*link) { + parent = *link; + insert_point = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); + if (tn->fn->ofs > insert_point->fn->ofs) + link = &insert_point->rb.rb_right; + else if (tn->fn->ofs < insert_point->fn->ofs || + tn->fn->size < insert_point->fn->size) + link = &insert_point->rb.rb_left; + else + link = &insert_point->rb.rb_right; + } + rb_link_node(&tn->rb, &insert_point->rb, link); + rb_insert_color(&tn->rb, &rii->tn_root); + } + /* If there's anything behind that overlaps us, note it */ + this = tn_prev(tn); + if (this) { + while (1) { + if (this->fn->ofs + this->fn->size > tn->fn->ofs) { + dbg_readinode("Node is overlapped by %p (v %d, 0x%x-0x%x)\n", + this, this->version, this->fn->ofs, + this->fn->ofs+this->fn->size); + tn->overlapped = 1; + break; + } + if (!this->overlapped) + break; + this = tn_prev(this); + } + } + + /* If the new node overlaps anything ahead, note it */ + this = tn_next(tn); + while (this && this->fn->ofs < fn_end) { + this->overlapped = 1; + dbg_readinode("Node ver %d, 0x%x-0x%x is overlapped\n", + this->version, this->fn->ofs, + this->fn->ofs+this->fn->size); + this = tn_next(this); + } + return 0; +} + +/* Trivial function to remove the last node in the tree. Which by definition + has no right-hand -- so can be removed just by making its only child (if + any) take its place under its parent. */ +static void eat_last(struct rb_root *root, struct rb_node *node) +{ + struct rb_node *parent = rb_parent(node); + struct rb_node **link; + + /* LAST! */ + BUG_ON(node->rb_right); + + if (!parent) + link = &root->rb_node; + else if (node == parent->rb_left) + link = &parent->rb_left; + else + link = &parent->rb_right; + + *link = node->rb_left; + /* Colour doesn't matter now. Only the parent pointer. */ + if (node->rb_left) + node->rb_left->rb_parent_color = node->rb_parent_color; +} + +/* We put this in reverse order, so we can just use eat_last */ +static void ver_insert(struct rb_root *ver_root, struct jffs2_tmp_dnode_info *tn) +{ + struct rb_node **link = &ver_root->rb_node; + struct rb_node *parent = NULL; + struct jffs2_tmp_dnode_info *this_tn; + + while (*link) { + parent = *link; + this_tn = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); + + if (tn->version > this_tn->version) + link = &parent->rb_left; + else + link = &parent->rb_right; + } + dbg_readinode("Link new node at %p (root is %p)\n", link, ver_root); + rb_link_node(&tn->rb, parent, link); + rb_insert_color(&tn->rb, ver_root); +} + +/* Build final, normal fragtree from tn tree. It doesn't matter which order + we add nodes to the real fragtree, as long as they don't overlap. And + having thrown away the majority of overlapped nodes as we went, there + really shouldn't be many sets of nodes which do overlap. If we start at + the end, we can use the overlap markers -- we can just eat nodes which + aren't overlapped, and when we encounter nodes which _do_ overlap we + sort them all into a temporary tree in version order before replaying them. */ +static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + struct jffs2_readinode_info *rii) +{ + struct jffs2_tmp_dnode_info *pen, *last, *this; + struct rb_root ver_root = RB_ROOT; + uint32_t high_ver = 0; + + if (rii->mdata_tn) { + dbg_readinode("potential mdata is ver %d at %p\n", rii->mdata_tn->version, rii->mdata_tn); + high_ver = rii->mdata_tn->version; + rii->latest_ref = rii->mdata_tn->fn->raw; + } +#ifdef JFFS2_DBG_READINODE_MESSAGES + this = tn_last(&rii->tn_root); + while (this) { + dbg_readinode("tn %p ver %d range 0x%x-0x%x ov %d\n", this, this->version, this->fn->ofs, + this->fn->ofs+this->fn->size, this->overlapped); + this = tn_prev(this); + } +#endif + pen = tn_last(&rii->tn_root); + while ((last = pen)) { + pen = tn_prev(last); + + eat_last(&rii->tn_root, &last->rb); + ver_insert(&ver_root, last); + + if (unlikely(last->overlapped)) + continue; + + /* Now we have a bunch of nodes in reverse version + order, in the tree at ver_root. Most of the time, + there'll actually be only one node in the 'tree', + in fact. */ + this = tn_last(&ver_root); + + while (this) { + struct jffs2_tmp_dnode_info *vers_next; + int ret; + vers_next = tn_prev(this); + eat_last(&ver_root, &this->rb); + if (check_tn_node(c, this)) { + dbg_readinode("node ver %x, 0x%x-0x%x failed CRC\n", + this->version, this->fn->ofs, + this->fn->ofs+this->fn->size); + jffs2_kill_tn(c, this); + } else { + if (this->version > high_ver) { + /* Note that this is different from the other + highest_version, because this one is only + counting _valid_ nodes which could give the + latest inode metadata */ + high_ver = this->version; + rii->latest_ref = this->fn->raw; + } + dbg_readinode("Add %p (v %x, 0x%x-0x%x, ov %d) to fragtree\n", + this, this->version, this->fn->ofs, + this->fn->ofs+this->fn->size, this->overlapped); + + ret = jffs2_add_full_dnode_to_inode(c, f, this->fn); + if (ret) { + /* Free the nodes in vers_root; let the caller + deal with the rest */ + JFFS2_ERROR("Add node to tree failed %d\n", ret); + while (1) { + vers_next = tn_prev(this); + if (check_tn_node(c, this)) + jffs2_mark_node_obsolete(c, this->fn->raw); + jffs2_free_full_dnode(this->fn); + jffs2_free_tmp_dnode_info(this); + this = vers_next; + if (!this) + break; + eat_last(&ver_root, &vers_next->rb); + } + return ret; + } + jffs2_free_tmp_dnode_info(this); + } + this = vers_next; + } + } + return 0; } static void jffs2_free_tmp_dnode_info_list(struct rb_root *list) @@ -112,8 +590,8 @@ static struct jffs2_raw_node_ref *jffs2_ * negative error code on failure. */ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, - struct jffs2_raw_dirent *rd, size_t read, struct jffs2_full_dirent **fdp, - uint32_t *latest_mctime, uint32_t *mctime_ver) + struct jffs2_raw_dirent *rd, size_t read, + struct jffs2_readinode_info *rii) { struct jffs2_full_dirent *fd; uint32_t crc; @@ -125,7 +603,8 @@ static inline int read_direntry(struct j if (unlikely(crc != je32_to_cpu(rd->node_crc))) { JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n", ref_offset(ref), je32_to_cpu(rd->node_crc), crc); - return 1; + jffs2_mark_node_obsolete(c, ref); + return 0; } /* If we've never checked the CRCs on this node, check them now */ @@ -137,7 +616,8 @@ static inline int read_direntry(struct j if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); - return 1; + jffs2_mark_node_obsolete(c, ref); + return 0; } jeb = &c->blocks[ref->flash_offset / c->sector_size]; @@ -161,10 +641,13 @@ static inline int read_direntry(struct j fd->ino = je32_to_cpu(rd->ino); fd->type = rd->type; + if (fd->version > rii->highest_version) + rii->highest_version = fd->version; + /* Pick out the mctime of the latest dirent */ - if(fd->version > *mctime_ver && je32_to_cpu(rd->mctime)) { - *mctime_ver = fd->version; - *latest_mctime = je32_to_cpu(rd->mctime); + if(fd->version > rii->mctime_ver && je32_to_cpu(rd->mctime)) { + rii->mctime_ver = fd->version; + rii->latest_mctime = je32_to_cpu(rd->mctime); } /* @@ -201,7 +684,7 @@ static inline int read_direntry(struct j * Wheee. We now have a complete jffs2_full_dirent structure, with * the name in it and everything. Link it into the list */ - jffs2_add_fd_to_list(c, fd, fdp); + jffs2_add_fd_to_list(c, fd, &rii->fds); return 0; } @@ -210,13 +693,13 @@ static inline int read_direntry(struct j * Helper function for jffs2_get_inode_nodes(). * It is called every time an inode node is found. * - * Returns: 0 on succes; + * Returns: 0 on success; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, - struct jffs2_raw_inode *rd, struct rb_root *tnp, int rdlen, - uint32_t *latest_mctime, uint32_t *mctime_ver) + struct jffs2_raw_inode *rd, int rdlen, + struct jffs2_readinode_info *rii) { struct jffs2_tmp_dnode_info *tn; uint32_t len, csize; @@ -230,7 +713,8 @@ static inline int read_dnode(struct jffs if (unlikely(crc != je32_to_cpu(rd->node_crc))) { JFFS2_NOTICE("node CRC failed on dnode at %#08x: read %#08x, calculated %#08x\n", ref_offset(ref), je32_to_cpu(rd->node_crc), crc); - return 1; + jffs2_mark_node_obsolete(c, ref); + return 0; } tn = jffs2_alloc_tmp_dnode_info(); @@ -342,6 +826,10 @@ static inline int read_dnode(struct jffs tn->data_crc = je32_to_cpu(rd->data_crc); tn->csize = csize; tn->fn->raw = ref; + tn->overlapped = 0; + + if (tn->version > rii->highest_version) + rii->highest_version = tn->version; /* There was a bug where we wrote hole nodes out with csize/dsize swapped. Deal with it */ @@ -353,13 +841,25 @@ static inline int read_dnode(struct jffs dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); - jffs2_add_tn_to_tree(tn, tnp); + ret = jffs2_add_tn_to_tree(c, rii, tn); + if (ret) { + jffs2_free_full_dnode(tn->fn); + free_out: + jffs2_free_tmp_dnode_info(tn); + return ret; + } +#ifdef JFFS2_DBG_READINODE_MESSAGES + dbg_readinode("After adding ver %d:\n", tn->version); + tn = tn_first(&rii->tn_root); + while (tn) { + dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n", + tn, tn->version, tn->fn->ofs, + tn->fn->ofs+tn->fn->size, tn->overlapped); + tn = tn_next(tn); + } +#endif return 0; - -free_out: - jffs2_free_tmp_dnode_info(tn); - return ret; } /* @@ -379,7 +879,8 @@ static inline int read_unknown(struct jf JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n", je16_to_cpu(un->magic), je16_to_cpu(un->nodetype), je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)); - return 1; + jffs2_mark_node_obsolete(c, ref); + return 0; } un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); @@ -407,7 +908,8 @@ static inline int read_unknown(struct jf case JFFS2_FEATURE_RWCOMPAT_DELETE: JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); - return 1; + jffs2_mark_node_obsolete(c, ref); + return 0; } return 0; @@ -421,92 +923,62 @@ static inline int read_unknown(struct jf * negative error code on failure. */ static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, - int right_size, int *rdlen, unsigned char *buf, unsigned char *bufstart) + int needed_len, int *rdlen, unsigned char *buf) { - int right_len, err, len; + int err, to_read = needed_len - *rdlen; size_t retlen; uint32_t offs; if (jffs2_is_writebuffered(c)) { - right_len = c->wbuf_pagesize - (bufstart - buf); - if (right_size + (int)(bufstart - buf) > c->wbuf_pagesize) - right_len += c->wbuf_pagesize; - } else - right_len = right_size; + int rem = to_read % c->wbuf_pagesize; - if (*rdlen == right_len) - return 0; + if (rem) + to_read += c->wbuf_pagesize - rem; + } /* We need to read more data */ offs = ref_offset(ref) + *rdlen; - if (jffs2_is_writebuffered(c)) { - bufstart = buf + c->wbuf_pagesize; - len = c->wbuf_pagesize; - } else { - bufstart = buf + *rdlen; - len = right_size - *rdlen; - } - dbg_readinode("read more %d bytes\n", len); + dbg_readinode("read more %d bytes\n", to_read); - err = jffs2_flash_read(c, offs, len, &retlen, bufstart); + err = jffs2_flash_read(c, offs, to_read, &retlen, buf + *rdlen); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, " - "error code: %d.\n", len, offs, err); + "error code: %d.\n", to_read, offs, err); return err; } - if (retlen < len) { + if (retlen < to_read) { JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n", - offs, retlen, len); + offs, retlen, to_read); return -EIO; } - *rdlen = right_len; - + *rdlen += to_read; return 0; } /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated - with this ino, returning the former in order of version */ + with this ino. Perform a preliminary ordering on data nodes, throwing away + those which are completely obsoleted by newer ones. The naïve approach we + use to take of just returning them _all_ in version order will cause us to + run out of memory in certain degenerate cases. */ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - struct rb_root *tnp, struct jffs2_full_dirent **fdp, - uint32_t *highest_version, uint32_t *latest_mctime, - uint32_t *mctime_ver) + struct jffs2_readinode_info *rii) { struct jffs2_raw_node_ref *ref, *valid_ref; - struct rb_root ret_tn = RB_ROOT; - struct jffs2_full_dirent *ret_fd = NULL; unsigned char *buf = NULL; union jffs2_node_union *node; size_t retlen; int len, err; - *mctime_ver = 0; + rii->mctime_ver = 0; dbg_readinode("ino #%u\n", f->inocache->ino); - if (jffs2_is_writebuffered(c)) { - /* - * If we have the write buffer, we assume the minimal I/O unit - * is c->wbuf_pagesize. We implement some optimizations which in - * this case and we need a temporary buffer of size = - * 2*c->wbuf_pagesize bytes (see comments in read_dnode()). - * Basically, we want to read not only the node header, but the - * whole wbuf (NAND page in case of NAND) or 2, if the node - * header overlaps the border between the 2 wbufs. - */ - len = 2*c->wbuf_pagesize; - } else { - /* - * When there is no write buffer, the size of the temporary - * buffer is the size of the larges node header. - */ - len = sizeof(union jffs2_node_union); - } - /* FIXME: in case of NOR and available ->point() this * needs to be fixed. */ + len = sizeof(union jffs2_node_union) + c->wbuf_pagesize; buf = kmalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -516,8 +988,6 @@ static int jffs2_get_inode_nodes(struct if (!valid_ref && f->inocache->ino != 1) JFFS2_WARNING("Eep. No valid nodes for ino #%u.\n", f->inocache->ino); while (valid_ref) { - unsigned char *bufstart; - /* We can hold a pointer to a non-obsolete node without the spinlock, but _obsolete_ nodes may disappear at any time, if the block they're in gets erased. So if we mark 'ref' obsolete while we're @@ -533,32 +1003,31 @@ static int jffs2_get_inode_nodes(struct /* * At this point we don't know the type of the node we're going * to read, so we do not know the size of its header. In order - * to minimize the amount of flash IO we assume the node has - * size = JFFS2_MIN_NODE_HEADER. + * to minimize the amount of flash IO we assume the header is + * of size = JFFS2_MIN_NODE_HEADER. */ + len = JFFS2_MIN_NODE_HEADER; if (jffs2_is_writebuffered(c)) { + int end, rem; + /* - * We treat 'buf' as 2 adjacent wbufs. We want to - * adjust bufstart such as it points to the - * beginning of the node within this wbuf. + * We are about to read JFFS2_MIN_NODE_HEADER bytes, + * but this flash has some minimal I/O unit. It is + * possible that we'll need to read more soon, so read + * up to the next min. I/O unit, in order not to + * re-read the same min. I/O unit twice. */ - bufstart = buf + (ref_offset(ref) % c->wbuf_pagesize); - /* We will read either one wbuf or 2 wbufs. */ - len = c->wbuf_pagesize - (bufstart - buf); - if (JFFS2_MIN_NODE_HEADER + (int)(bufstart - buf) > c->wbuf_pagesize) { - /* The header spans the border of the first wbuf */ - len += c->wbuf_pagesize; - } - } else { - bufstart = buf; - len = JFFS2_MIN_NODE_HEADER; + end = ref_offset(ref) + len; + rem = end % c->wbuf_pagesize; + if (rem) + end += c->wbuf_pagesize - rem; + len = end - ref_offset(ref); } dbg_readinode("read %d bytes at %#08x(%d).\n", len, ref_offset(ref), ref_flags(ref)); /* FIXME: point() */ - err = jffs2_flash_read(c, ref_offset(ref), len, - &retlen, bufstart); + err = jffs2_flash_read(c, ref_offset(ref), len, &retlen, buf); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, ref_offset(ref), err); goto free_out; @@ -570,7 +1039,7 @@ static int jffs2_get_inode_nodes(struct goto free_out; } - node = (union jffs2_node_union *)bufstart; + node = (union jffs2_node_union *)buf; /* No need to mask in the valid bit; it shouldn't be invalid */ if (je32_to_cpu(node->u.hdr_crc) != crc32(0, node, sizeof(node->u)-4)) { @@ -583,10 +1052,10 @@ static int jffs2_get_inode_nodes(struct jffs2_mark_node_obsolete(c, ref); goto cont; } - /* Due to poor choice of crc32 seed, an all-zero node will have a correct CRC */ - if (!je32_to_cpu(node->u.hdr_crc) && !je16_to_cpu(node->u.nodetype) && - !je16_to_cpu(node->u.magic) && !je32_to_cpu(node->u.totlen)) { - JFFS2_NOTICE("All zero node header at %#08x.\n", ref_offset(ref)); + if (je16_to_cpu(node->u.magic) != JFFS2_MAGIC_BITMASK) { + /* Not a JFFS2 node, whinge and move on */ + JFFS2_NOTICE("Wrong magic bitmask 0x%04x in node header at %#08x.\n", + je16_to_cpu(node->u.magic), ref_offset(ref)); jffs2_mark_node_obsolete(c, ref); goto cont; } @@ -596,46 +1065,34 @@ static int jffs2_get_inode_nodes(struct case JFFS2_NODETYPE_DIRENT: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent)) { - err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf, bufstart); + err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf); if (unlikely(err)) goto free_out; } - err = read_direntry(c, ref, &node->d, retlen, &ret_fd, latest_mctime, mctime_ver); - if (err == 1) { - jffs2_mark_node_obsolete(c, ref); - break; - } else if (unlikely(err)) + err = read_direntry(c, ref, &node->d, retlen, rii); + if (unlikely(err)) goto free_out; - if (je32_to_cpu(node->d.version) > *highest_version) - *highest_version = je32_to_cpu(node->d.version); - break; case JFFS2_NODETYPE_INODE: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode)) { - err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf, bufstart); + err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf); if (unlikely(err)) goto free_out; } - err = read_dnode(c, ref, &node->i, &ret_tn, len, latest_mctime, mctime_ver); - if (err == 1) { - jffs2_mark_node_obsolete(c, ref); - break; - } else if (unlikely(err)) + err = read_dnode(c, ref, &node->i, len, rii); + if (unlikely(err)) goto free_out; - if (je32_to_cpu(node->i.version) > *highest_version) - *highest_version = je32_to_cpu(node->i.version); - break; default: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node)) { - err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf, bufstart); + err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf); if (unlikely(err)) goto free_out; } @@ -653,17 +1110,19 @@ static int jffs2_get_inode_nodes(struct } spin_unlock(&c->erase_completion_lock); - *tnp = ret_tn; - *fdp = ret_fd; kfree(buf); + f->highest_version = rii->highest_version; + dbg_readinode("nodes of inode #%u were read, the highest version is %u, latest_mctime %u, mctime_ver %u.\n", - f->inocache->ino, *highest_version, *latest_mctime, *mctime_ver); + f->inocache->ino, rii->highest_version, rii->latest_mctime, + rii->mctime_ver); return 0; free_out: - jffs2_free_tmp_dnode_info_list(&ret_tn); - jffs2_free_full_dirent_list(ret_fd); + jffs2_free_tmp_dnode_info_list(&rii->tn_root); + jffs2_free_full_dirent_list(rii->fds); + rii->fds = NULL; kfree(buf); return err; } @@ -672,20 +1131,17 @@ static int jffs2_do_read_inode_internal( struct jffs2_inode_info *f, struct jffs2_raw_inode *latest_node) { - struct jffs2_tmp_dnode_info *tn; - struct rb_root tn_list; - struct rb_node *rb, *repl_rb; - struct jffs2_full_dirent *fd_list; - struct jffs2_full_dnode *fn, *first_fn = NULL; - uint32_t crc; - uint32_t latest_mctime, mctime_ver; + struct jffs2_readinode_info rii; + uint32_t crc, new_size; size_t retlen; int ret; dbg_readinode("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink); + memset(&rii, 0, sizeof(rii)); + /* Grab all nodes relevant to this ino */ - ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); + ret = jffs2_get_inode_nodes(c, f, &rii); if (ret) { JFFS2_ERROR("cannot read nodes for ino %u, returned error is %d\n", f->inocache->ino, ret); @@ -693,74 +1149,42 @@ static int jffs2_do_read_inode_internal( jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return ret; } - f->dents = fd_list; - - rb = rb_first(&tn_list); - while (rb) { - cond_resched(); - tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb); - fn = tn->fn; - ret = 1; - dbg_readinode("consider node ver %u, phys offset " - "%#08x(%d), range %u-%u.\n", tn->version, - ref_offset(fn->raw), ref_flags(fn->raw), - fn->ofs, fn->ofs + fn->size); - - if (fn->size) { - ret = jffs2_add_older_frag_to_fragtree(c, f, tn); - /* TODO: the error code isn't checked, check it */ - jffs2_dbg_fragtree_paranoia_check_nolock(f); - BUG_ON(ret < 0); - if (!first_fn && ret == 0) - first_fn = fn; - } else if (!first_fn) { - first_fn = fn; - f->metadata = fn; - ret = 0; /* Prevent freeing the metadata update node */ - } else - jffs2_mark_node_obsolete(c, fn->raw); - - BUG_ON(rb->rb_left); - if (rb_parent(rb) && rb_parent(rb)->rb_left == rb) { - /* We were then left-hand child of our parent. We need - * to move our own right-hand child into our place. */ - repl_rb = rb->rb_right; - if (repl_rb) - rb_set_parent(repl_rb, rb_parent(rb)); - } else - repl_rb = NULL; - - rb = rb_next(rb); - - /* Remove the spent tn from the tree; don't bother rebalancing - * but put our right-hand child in our own place. */ - if (rb_parent(&tn->rb)) { - if (rb_parent(&tn->rb)->rb_left == &tn->rb) - rb_parent(&tn->rb)->rb_left = repl_rb; - else if (rb_parent(&tn->rb)->rb_right == &tn->rb) - rb_parent(&tn->rb)->rb_right = repl_rb; - else BUG(); - } else if (tn->rb.rb_right) - rb_set_parent(tn->rb.rb_right, NULL); + ret = jffs2_build_inode_fragtree(c, f, &rii); + if (ret) { + JFFS2_ERROR("Failed to build final fragtree for inode #%u: error %d\n", + f->inocache->ino, ret); + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + jffs2_free_tmp_dnode_info_list(&rii.tn_root); + /* FIXME: We could at least crc-check them all */ + if (rii.mdata_tn) { + jffs2_free_full_dnode(rii.mdata_tn->fn); + jffs2_free_tmp_dnode_info(rii.mdata_tn); + rii.mdata_tn = NULL; + } + return ret; + } - jffs2_free_tmp_dnode_info(tn); - if (ret) { - dbg_readinode("delete dnode %u-%u.\n", - fn->ofs, fn->ofs + fn->size); - jffs2_free_full_dnode(fn); + if (rii.mdata_tn) { + if (rii.mdata_tn->fn->raw == rii.latest_ref) { + f->metadata = rii.mdata_tn->fn; + jffs2_free_tmp_dnode_info(rii.mdata_tn); + } else { + jffs2_kill_tn(c, rii.mdata_tn); } + rii.mdata_tn = NULL; } - jffs2_dbg_fragtree_paranoia_check_nolock(f); - BUG_ON(first_fn && ref_obsolete(first_fn->raw)); + f->dents = rii.fds; - fn = first_fn; - if (unlikely(!first_fn)) { + jffs2_dbg_fragtree_paranoia_check_nolock(f); + + if (unlikely(!rii.latest_ref)) { /* No data nodes for this inode. */ if (f->inocache->ino != 1) { JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino); - if (!fd_list) { + if (!rii.fds) { if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return -EIO; @@ -778,7 +1202,7 @@ static int jffs2_do_read_inode_internal( return 0; } - ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); + ret = jffs2_flash_read(c, ref_offset(rii.latest_ref), sizeof(*latest_node), &retlen, (void *)latest_node); if (ret || retlen != sizeof(*latest_node)) { JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); @@ -791,7 +1215,7 @@ static int jffs2_do_read_inode_internal( crc = crc32(0, latest_node, sizeof(*latest_node)-8); if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", - f->inocache->ino, ref_offset(fn->raw)); + f->inocache->ino, ref_offset(rii.latest_ref)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; @@ -799,17 +1223,22 @@ static int jffs2_do_read_inode_internal( switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { case S_IFDIR: - if (mctime_ver > je32_to_cpu(latest_node->version)) { + if (rii.mctime_ver > je32_to_cpu(latest_node->version)) { /* The times in the latest_node are actually older than mctime in the latest dirent. Cheat. */ - latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); + latest_node->ctime = latest_node->mtime = cpu_to_je32(rii.latest_mctime); } break; case S_IFREG: /* If it was a regular file, truncate it to the latest node's isize */ - jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize)); + new_size = jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize)); + if (new_size != je32_to_cpu(latest_node->isize)) { + JFFS2_WARNING("Truncating ino #%u to %d bytes failed because it only had %d bytes to start with!\n", + f->inocache->ino, je32_to_cpu(latest_node->isize), new_size); + latest_node->isize = cpu_to_je32(new_size); + } break; case S_IFLNK: @@ -832,7 +1261,7 @@ static int jffs2_do_read_inode_internal( return -ENOMEM; } - ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), + ret = jffs2_flash_read(c, ref_offset(rii.latest_ref) + sizeof(*latest_node), je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); if (ret || retlen != je32_to_cpu(latest_node->csize)) { diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 7fb45bd..2a1c976 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -1,15 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.125 2005/09/30 13:59:13 dedekind Exp $ - * */ + #include #include #include @@ -636,16 +635,17 @@ scan_more: if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { uint32_t inbuf_ofs; - uint32_t empty_start; + uint32_t empty_start, scan_end; empty_start = ofs; ofs += 4; + scan_end = min_t(uint32_t, EMPTY_SCAN_SIZE(c->sector_size)/8, buf_len); D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); more_empty: inbuf_ofs = ofs - buf_ofs; - while (inbuf_ofs < buf_len) { - if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) { + while (inbuf_ofs < scan_end) { + if (unlikely(*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)) { printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start))) @@ -666,7 +666,11 @@ scan_more: D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); return BLK_STATE_CLEANMARKER; } - + if (!buf_size && (scan_end != buf_len)) {/* XIP/point case */ + scan_end = buf_len; + goto more_empty; + } + /* See how much more there is to read in this eraseblock... */ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); if (!buf_len) { @@ -676,6 +680,8 @@ scan_more: empty_start)); break; } + /* point never reaches here */ + scan_end = buf_len; D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) @@ -734,18 +740,8 @@ scan_more: ofs += 4; continue; } - /* Due to poor choice of crc32 seed, an all-zero node will have a correct CRC */ - if (!je32_to_cpu(node->hdr_crc) && !je16_to_cpu(node->nodetype) && - !je16_to_cpu(node->magic) && !je32_to_cpu(node->totlen)) { - noisy_printk(&noise, "jffs2_scan_eraseblock(): All zero node header at 0x%08x.\n", ofs); - if ((err = jffs2_scan_dirty_space(c, jeb, 4))) - return err; - ofs += 4; - continue; - } - if (ofs + je32_to_cpu(node->totlen) > - jeb->offset + c->sector_size) { + if (ofs + je32_to_cpu(node->totlen) > jeb->offset + c->sector_size) { /* Eep. Node goes over the end of the erase block. */ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", ofs, je32_to_cpu(node->totlen)); @@ -952,8 +948,7 @@ static int jffs2_scan_inode_node(struct struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_inode_cache *ic; - uint32_t ino = je32_to_cpu(ri->ino); - int err; + uint32_t crc, ino = je32_to_cpu(ri->ino); D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs)); @@ -966,21 +961,22 @@ static int jffs2_scan_inode_node(struct Which means that the _full_ amount of time to get to proper write mode with GC operational may actually be _longer_ than before. Sucks to be me. */ + /* Check the node CRC in any case. */ + crc = crc32(0, ri, sizeof(*ri)-8); + if (crc != je32_to_cpu(ri->node_crc)) { + printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on " + "node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(ri->node_crc), crc); + /* + * We believe totlen because the CRC on the node + * _header_ was OK, just the node itself failed. + */ + return jffs2_scan_dirty_space(c, jeb, + PAD(je32_to_cpu(ri->totlen))); + } + ic = jffs2_get_ino_cache(c, ino); if (!ic) { - /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the - first node we found for this inode. Do a CRC check to protect against the former - case */ - uint32_t crc = crc32(0, ri, sizeof(*ri)-8); - - if (crc != je32_to_cpu(ri->node_crc)) { - printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", - ofs, je32_to_cpu(ri->node_crc), crc); - /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ - if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(ri->totlen))))) - return err; - return 0; - } ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) return -ENOMEM; diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c index 52a9894..bc9f6ba 100644 --- a/fs/jffs2/security.c +++ b/fs/jffs2/security.c @@ -1,13 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2006 NEC Corporation + * Copyright © 2006 NEC Corporation * * Created by KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * */ + #include #include #include diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index 30f8884..d828b29 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -1,16 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2004 Ferenc Havasi , - * Zoltan Sogor , - * Patrik Kluba , - * University of Szeged, Hungary - * 2006 KaiGai Kohei + * Copyright © 2004 Ferenc Havasi , + * Zoltan Sogor , + * Patrik Kluba , + * University of Szeged, Hungary + * 2006 KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: summary.c,v 1.4 2005/09/26 11:37:21 havasi Exp $ - * */ #include diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h index 6bf1f6a..0c6669e 100644 --- a/fs/jffs2/summary.h +++ b/fs/jffs2/summary.h @@ -1,15 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2004 Ferenc Havasi , - * Zoltan Sogor , - * Patrik Kluba , - * University of Szeged, Hungary + * Copyright © 2004 Ferenc Havasi , + * Zoltan Sogor , + * Patrik Kluba , + * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: summary.h,v 1.2 2005/09/26 11:37:21 havasi Exp $ - * */ #ifndef JFFS2_SUMMARY_H diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index cc7e8e7..e51164a 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.110 2005/11/07 11:14:42 gleixner Exp $ - * */ #include @@ -347,7 +345,7 @@ #endif #ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY) " #endif - " (C) 2001-2006 Red Hat, Inc.\n"); + " © 2001-2006 Red Hat, Inc.\n"); jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 7e4882c..b7339c3 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -1,17 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: symlink.c,v 1.19 2005/11/07 11:14:42 gleixner Exp $ - * */ - #include #include #include diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 4fac6dd..c556e85 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1,16 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. - * Copyright (C) 2004 Thomas Gleixner + * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004 Thomas Gleixner * * Created by David Woodhouse * Modified debugged and enhanced by Thomas Gleixner * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.100 2005/09/30 13:59:13 dedekind Exp $ - * */ #include @@ -345,6 +343,9 @@ static void jffs2_wbuf_recover(struct jf return; } + /* The summary is not recovered, so it must be disabled for this erase block */ + jffs2_sum_disable_collecting(c->summary); + ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile); if (ret) { printk(KERN_WARNING "Failed to allocate node refs for wbuf recovery. Data loss ensues.\n"); @@ -967,9 +968,9 @@ #define OOB_CM_SIZE 12 static const struct jffs2_unknown_node oob_cleanmarker = { - .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), - .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), - .totlen = cpu_to_je32(8) + .magic = constant_cpu_to_je16(JFFS2_MAGIC_BITMASK), + .nodetype = constant_cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), + .totlen = constant_cpu_to_je32(8) }; /* @@ -1208,3 +1209,27 @@ int jffs2_nor_wbuf_flash_setup(struct jf void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } + +int jffs2_ubivol_setup(struct jffs2_sb_info *c) { + c->cleanmarker_size = 0; + + if (c->mtd->writesize == 1) + /* We do not need write-buffer */ + return 0; + + init_rwsem(&c->wbuf_sem); + + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); + + return 0; +} + +void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 6717679..c9fe0ab 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.97 2005/11/07 11:14:42 gleixner Exp $ - * */ #include @@ -507,8 +505,7 @@ int jffs2_do_unlink(struct jffs2_sb_info uint32_t alloclen; int ret; - if (1 /* alternative branch needs testing */ || - !jffs2_can_mark_obsolete(c)) { + if (!jffs2_can_mark_obsolete(c)) { /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */ rd = jffs2_alloc_raw_dirent(); diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c index c638ae1..b9276b1 100644 --- a/fs/jffs2/writev.c +++ b/fs/jffs2/writev.c @@ -1,14 +1,12 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: writev.c,v 1.8 2005/09/09 15:11:58 havasi Exp $ - * */ #include diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 4bb3f18..78fc088 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -1,13 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2006 NEC Corporation + * Copyright © 2006 NEC Corporation * * Created by KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * */ + #include #include #include diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 06a5c69..3b0ff29 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -1,13 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2006 NEC Corporation + * Copyright © 2006 NEC Corporation * * Created by KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * */ + #ifndef _JFFS2_FS_XATTR_H_ #define _JFFS2_FS_XATTR_H_ diff --git a/fs/jffs2/xattr_trusted.c b/fs/jffs2/xattr_trusted.c index ed046e1..8ec5765 100644 --- a/fs/jffs2/xattr_trusted.c +++ b/fs/jffs2/xattr_trusted.c @@ -1,13 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2006 NEC Corporation + * Copyright © 2006 NEC Corporation * * Created by KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * */ + #include #include #include diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c index 2f8e9aa..40942bc 100644 --- a/fs/jffs2/xattr_user.c +++ b/fs/jffs2/xattr_user.c @@ -1,13 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2006 NEC Corporation + * Copyright © 2006 NEC Corporation * * Created by KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * */ + #include #include #include diff --git a/fs/namei.c b/fs/namei.c index ee60cc4..880052c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1243,22 +1243,13 @@ int __user_path_lookup_open(const char _ return err; } -/* - * Restricted form of lookup. Doesn't follow links, single-component only, - * needs parent already locked. Doesn't follow mounts. - * SMP-safe. - */ -static struct dentry * __lookup_hash(struct qstr *name, struct dentry * base, struct nameidata *nd) +static inline struct dentry *__lookup_hash_kern(struct qstr *name, struct dentry *base, struct nameidata *nd) { - struct dentry * dentry; + struct dentry *dentry; struct inode *inode; int err; inode = base->d_inode; - err = permission(inode, MAY_EXEC, nd); - dentry = ERR_PTR(err); - if (err) - goto out; /* * See if the low-level filesystem might want @@ -1287,35 +1278,76 @@ out: return dentry; } +/* + * Restricted form of lookup. Doesn't follow links, single-component only, + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +static inline struct dentry * __lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd) +{ + struct dentry *dentry; + struct inode *inode; + int err; + + inode = base->d_inode; + + err = permission(inode, MAY_EXEC, nd); + dentry = ERR_PTR(err); + if (err) + goto out; + + dentry = __lookup_hash_kern(name, base, nd); +out: + return dentry; +} + static struct dentry *lookup_hash(struct nameidata *nd) { return __lookup_hash(&nd->last, nd->dentry, nd); } /* SMP-safe */ -struct dentry * lookup_one_len(const char * name, struct dentry * base, int len) +static inline int __lookup_one_len(const char *name, struct qstr *this, struct dentry *base, int len) { unsigned long hash; - struct qstr this; unsigned int c; - this.name = name; - this.len = len; + this->name = name; + this->len = len; if (!len) - goto access; + return -EACCES; hash = init_name_hash(); while (len--) { c = *(const unsigned char *)name++; if (c == '/' || c == '\0') - goto access; + return -EACCES; hash = partial_name_hash(c, hash); } - this.hash = end_name_hash(hash); + this->hash = end_name_hash(hash); + return 0; +} +struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +{ + int err; + struct qstr this; + + err = __lookup_one_len(name, &this, base, len); + if (err) + return ERR_PTR(err); return __lookup_hash(&this, base, NULL); -access: - return ERR_PTR(-EACCES); +} + +struct dentry *lookup_one_len_kern(const char *name, struct dentry *base, int len) +{ + int err; + struct qstr this; + + err = __lookup_one_len(name, &this, base, len); + if (err) + return ERR_PTR(err); + return __lookup_hash_kern(&this, base, NULL); } /* diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index f27e537..a0c8667 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -27,6 +27,7 @@ #include #include #include #include +#include #define MLOG_MASK_PREFIX ML_DISK_ALLOC #include @@ -34,6 +35,7 @@ #include #include "ocfs2.h" #include "alloc.h" +#include "aops.h" #include "dlmglue.h" #include "extent_map.h" #include "inode.h" @@ -47,63 +49,243 @@ #include "uptodate.h" #include "buffer_head_io.h" -static int ocfs2_extent_contig(struct inode *inode, - struct ocfs2_extent_rec *ext, - u64 blkno); +static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc); -static int ocfs2_create_new_meta_bhs(struct ocfs2_super *osb, - handle_t *handle, - struct inode *inode, - int wanted, - struct ocfs2_alloc_context *meta_ac, - struct buffer_head *bhs[]); +/* + * Structures which describe a path through a btree, and functions to + * manipulate them. + * + * The idea here is to be as generic as possible with the tree + * manipulation code. + */ +struct ocfs2_path_item { + struct buffer_head *bh; + struct ocfs2_extent_list *el; +}; -static int ocfs2_add_branch(struct ocfs2_super *osb, - handle_t *handle, - struct inode *inode, - struct buffer_head *fe_bh, - struct buffer_head *eb_bh, - struct buffer_head *last_eb_bh, - struct ocfs2_alloc_context *meta_ac); +#define OCFS2_MAX_PATH_DEPTH 5 -static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, - handle_t *handle, - struct inode *inode, - struct buffer_head *fe_bh, - struct ocfs2_alloc_context *meta_ac, - struct buffer_head **ret_new_eb_bh); +struct ocfs2_path { + int p_tree_depth; + struct ocfs2_path_item p_node[OCFS2_MAX_PATH_DEPTH]; +}; -static int ocfs2_do_insert_extent(struct ocfs2_super *osb, - handle_t *handle, - struct inode *inode, - struct buffer_head *fe_bh, - u64 blkno, - u32 new_clusters); +#define path_root_bh(_path) ((_path)->p_node[0].bh) +#define path_root_el(_path) ((_path)->p_node[0].el) +#define path_leaf_bh(_path) ((_path)->p_node[(_path)->p_tree_depth].bh) +#define path_leaf_el(_path) ((_path)->p_node[(_path)->p_tree_depth].el) +#define path_num_items(_path) ((_path)->p_tree_depth + 1) -static int ocfs2_find_branch_target(struct ocfs2_super *osb, - struct inode *inode, - struct buffer_head *fe_bh, - struct buffer_head **target_bh); +/* + * Reset the actual path elements so that we can re-use the structure + * to build another path. Generally, this involves freeing the buffer + * heads. + */ +static void ocfs2_reinit_path(struct ocfs2_path *path, int keep_root) +{ + int i, start = 0, depth = 0; + struct ocfs2_path_item *node; -static int ocfs2_find_new_last_ext_blk(struct ocfs2_super *osb, - struct inode *inode, - struct ocfs2_dinode *fe, - unsigned int new_i_clusters, - struct buffer_head *old_last_eb, - struct buffer_head **new_last_eb); + if (keep_root) + start = 1; + + for(i = start; i < path_num_items(path); i++) { + node = &path->p_node[i]; + + brelse(node->bh); + node->bh = NULL; + node->el = NULL; + } + + /* + * Tree depth may change during truncate, or insert. If we're + * keeping the root extent list, then make sure that our path + * structure reflects the proper depth. + */ + if (keep_root) + depth = le16_to_cpu(path_root_el(path)->l_tree_depth); + + path->p_tree_depth = depth; +} + +static void ocfs2_free_path(struct ocfs2_path *path) +{ + if (path) { + ocfs2_reinit_path(path, 0); + kfree(path); + } +} + +/* + * Make the *dest path the same as src and re-initialize src path to + * have a root only. + */ +static void ocfs2_mv_path(struct ocfs2_path *dest, struct ocfs2_path *src) +{ + int i; + + BUG_ON(path_root_bh(dest) != path_root_bh(src)); + + for(i = 1; i < OCFS2_MAX_PATH_DEPTH; i++) { + brelse(dest->p_node[i].bh); + + dest->p_node[i].bh = src->p_node[i].bh; + dest->p_node[i].el = src->p_node[i].el; + + src->p_node[i].bh = NULL; + src->p_node[i].el = NULL; + } +} + +/* + * Insert an extent block at given index. + * + * This will not take an additional reference on eb_bh. + */ +static inline void ocfs2_path_insert_eb(struct ocfs2_path *path, int index, + struct buffer_head *eb_bh) +{ + struct ocfs2_extent_block *eb = (struct ocfs2_extent_block *)eb_bh->b_data; + + /* + * Right now, no root bh is an extent block, so this helps + * catch code errors with dinode trees. The assertion can be + * safely removed if we ever need to insert extent block + * structures at the root. + */ + BUG_ON(index == 0); + + path->p_node[index].bh = eb_bh; + path->p_node[index].el = &eb->h_list; +} + +static struct ocfs2_path *ocfs2_new_path(struct buffer_head *root_bh, + struct ocfs2_extent_list *root_el) +{ + struct ocfs2_path *path; + + BUG_ON(le16_to_cpu(root_el->l_tree_depth) >= OCFS2_MAX_PATH_DEPTH); + + path = kzalloc(sizeof(*path), GFP_NOFS); + if (path) { + path->p_tree_depth = le16_to_cpu(root_el->l_tree_depth); + get_bh(root_bh); + path_root_bh(path) = root_bh; + path_root_el(path) = root_el; + } + + return path; +} + +/* + * Allocate and initialize a new path based on a disk inode tree. + */ +static struct ocfs2_path *ocfs2_new_inode_path(struct buffer_head *di_bh) +{ + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_extent_list *el = &di->id2.i_list; + + return ocfs2_new_path(di_bh, el); +} + +/* + * Convenience function to journal all components in a path. + */ +static int ocfs2_journal_access_path(struct inode *inode, handle_t *handle, + struct ocfs2_path *path) +{ + int i, ret = 0; + + if (!path) + goto out; + + for(i = 0; i < path_num_items(path); i++) { + ret = ocfs2_journal_access(handle, inode, path->p_node[i].bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + +out: + return ret; +} + +enum ocfs2_contig_type { + CONTIG_NONE = 0, + CONTIG_LEFT, + CONTIG_RIGHT +}; -static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc); -static int ocfs2_extent_contig(struct inode *inode, - struct ocfs2_extent_rec *ext, - u64 blkno) +/* + * NOTE: ocfs2_block_extent_contig(), ocfs2_extents_adjacent() and + * ocfs2_extent_contig only work properly against leaf nodes! + */ +static int ocfs2_block_extent_contig(struct super_block *sb, + struct ocfs2_extent_rec *ext, + u64 blkno) +{ + u64 blk_end = le64_to_cpu(ext->e_blkno); + + blk_end += ocfs2_clusters_to_blocks(sb, + le16_to_cpu(ext->e_leaf_clusters)); + + return blkno == blk_end; +} + +static int ocfs2_extents_adjacent(struct ocfs2_extent_rec *left, + struct ocfs2_extent_rec *right) +{ + u32 left_range; + + left_range = le32_to_cpu(left->e_cpos) + + le16_to_cpu(left->e_leaf_clusters); + + return (left_range == le32_to_cpu(right->e_cpos)); +} + +static enum ocfs2_contig_type + ocfs2_extent_contig(struct inode *inode, + struct ocfs2_extent_rec *ext, + struct ocfs2_extent_rec *insert_rec) { - return blkno == (le64_to_cpu(ext->e_blkno) + - ocfs2_clusters_to_blocks(inode->i_sb, - le32_to_cpu(ext->e_clusters))); + u64 blkno = le64_to_cpu(insert_rec->e_blkno); + + if (ocfs2_extents_adjacent(ext, insert_rec) && + ocfs2_block_extent_contig(inode->i_sb, ext, blkno)) + return CONTIG_RIGHT; + + blkno = le64_to_cpu(ext->e_blkno); + if (ocfs2_extents_adjacent(insert_rec, ext) && + ocfs2_block_extent_contig(inode->i_sb, insert_rec, blkno)) + return CONTIG_LEFT; + + return CONTIG_NONE; } /* + * NOTE: We can have pretty much any combination of contiguousness and + * appending. + * + * The usefulness of APPEND_TAIL is more in that it lets us know that + * we'll have to update the path to that leaf. + */ +enum ocfs2_append_type { + APPEND_NONE = 0, + APPEND_TAIL, +}; + +struct ocfs2_insert_type { + enum ocfs2_append_type ins_appending; + enum ocfs2_contig_type ins_contig; + int ins_contig_index; + int ins_free_records; + int ins_tree_depth; +}; + +/* * How many free extents have we got before we need more meta data? */ int ocfs2_num_free_extents(struct ocfs2_super *osb, @@ -242,6 +424,28 @@ bail: } /* + * Helper function for ocfs2_add_branch() and ocfs2_shift_tree_depth(). + * + * Returns the sum of the rightmost extent rec logical offset and + * cluster count. + * + * ocfs2_add_branch() uses this to determine what logical cluster + * value should be populated into the leftmost new branch records. + * + * ocfs2_shift_tree_depth() uses this to determine the # clusters + * value for the new topmost tree record. + */ +static inline u32 ocfs2_sum_rightmost_rec(struct ocfs2_extent_list *el) +{ + int i; + + i = le16_to_cpu(el->l_next_free_rec) - 1; + + return le32_to_cpu(el->l_recs[i].e_cpos) + + ocfs2_rec_clusters(el, &el->l_recs[i]); +} + +/* * Add an entire tree branch to our inode. eb_bh is the extent block * to start at, if we don't want to start the branch at the dinode * structure. @@ -250,7 +454,7 @@ bail: * for the new last extent block. * * the new branch will be 'empty' in the sense that every block will - * contain a single record with e_clusters == 0. + * contain a single record with cluster count == 0. */ static int ocfs2_add_branch(struct ocfs2_super *osb, handle_t *handle, @@ -268,6 +472,7 @@ static int ocfs2_add_branch(struct ocfs2 struct ocfs2_extent_block *eb; struct ocfs2_extent_list *eb_el; struct ocfs2_extent_list *el; + u32 new_cpos; mlog_entry_void(); @@ -302,6 +507,9 @@ static int ocfs2_add_branch(struct ocfs2 goto bail; } + eb = (struct ocfs2_extent_block *)last_eb_bh->b_data; + new_cpos = ocfs2_sum_rightmost_rec(&eb->h_list); + /* Note: new_eb_bhs[new_blocks - 1] is the guy which will be * linked with the rest of the tree. * conversly, new_eb_bhs[0] is the new bottommost leaf. @@ -330,9 +538,18 @@ static int ocfs2_add_branch(struct ocfs2 eb->h_next_leaf_blk = 0; eb_el->l_tree_depth = cpu_to_le16(i); eb_el->l_next_free_rec = cpu_to_le16(1); - eb_el->l_recs[0].e_cpos = fe->i_clusters; + /* + * This actually counts as an empty extent as + * c_clusters == 0 + */ + eb_el->l_recs[0].e_cpos = cpu_to_le32(new_cpos); eb_el->l_recs[0].e_blkno = cpu_to_le64(next_blkno); - eb_el->l_recs[0].e_clusters = cpu_to_le32(0); + /* + * eb_el isn't always an interior node, but even leaf + * nodes want a zero'd flags and reserved field so + * this gets the whole 32 bits regardless of use. + */ + eb_el->l_recs[0].e_int_clusters = cpu_to_le32(0); if (!eb_el->l_tree_depth) new_last_eb_blk = le64_to_cpu(eb->h_blkno); @@ -376,8 +593,8 @@ static int ocfs2_add_branch(struct ocfs2 * either be on the fe, or the extent block passed in. */ i = le16_to_cpu(el->l_next_free_rec); el->l_recs[i].e_blkno = cpu_to_le64(next_blkno); - el->l_recs[i].e_cpos = fe->i_clusters; - el->l_recs[i].e_clusters = 0; + el->l_recs[i].e_cpos = cpu_to_le32(new_cpos); + el->l_recs[i].e_int_clusters = 0; le16_add_cpu(&el->l_next_free_rec, 1); /* fe needs a new last extent block pointer, as does the @@ -425,6 +642,7 @@ static int ocfs2_shift_tree_depth(struct struct buffer_head **ret_new_eb_bh) { int status, i; + u32 new_clusters; struct buffer_head *new_eb_bh = NULL; struct ocfs2_dinode *fe; struct ocfs2_extent_block *eb; @@ -461,11 +679,8 @@ static int ocfs2_shift_tree_depth(struct /* copy the fe data into the new extent block */ eb_el->l_tree_depth = fe_el->l_tree_depth; eb_el->l_next_free_rec = fe_el->l_next_free_rec; - for(i = 0; i < le16_to_cpu(fe_el->l_next_free_rec); i++) { - eb_el->l_recs[i].e_cpos = fe_el->l_recs[i].e_cpos; - eb_el->l_recs[i].e_clusters = fe_el->l_recs[i].e_clusters; - eb_el->l_recs[i].e_blkno = fe_el->l_recs[i].e_blkno; - } + for(i = 0; i < le16_to_cpu(fe_el->l_next_free_rec); i++) + eb_el->l_recs[i] = fe_el->l_recs[i]; status = ocfs2_journal_dirty(handle, new_eb_bh); if (status < 0) { @@ -480,16 +695,15 @@ static int ocfs2_shift_tree_depth(struct goto bail; } + new_clusters = ocfs2_sum_rightmost_rec(eb_el); + /* update fe now */ le16_add_cpu(&fe_el->l_tree_depth, 1); fe_el->l_recs[0].e_cpos = 0; fe_el->l_recs[0].e_blkno = eb->h_blkno; - fe_el->l_recs[0].e_clusters = fe->i_clusters; - for(i = 1; i < le16_to_cpu(fe_el->l_next_free_rec); i++) { - fe_el->l_recs[i].e_cpos = 0; - fe_el->l_recs[i].e_clusters = 0; - fe_el->l_recs[i].e_blkno = 0; - } + fe_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters); + for(i = 1; i < le16_to_cpu(fe_el->l_next_free_rec); i++) + memset(&fe_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec)); fe_el->l_next_free_rec = cpu_to_le16(1); /* If this is our 1st tree depth shift, then last_eb_blk @@ -515,199 +729,6 @@ bail: } /* - * Expects the tree to already have room in the rightmost leaf for the - * extent. Updates all the extent blocks (and the dinode) on the way - * down. - */ -static int ocfs2_do_insert_extent(struct ocfs2_super *osb, - handle_t *handle, - struct inode *inode, - struct buffer_head *fe_bh, - u64 start_blk, - u32 new_clusters) -{ - int status, i, num_bhs = 0; - u64 next_blkno; - u16 next_free; - struct buffer_head **eb_bhs = NULL; - struct ocfs2_dinode *fe; - struct ocfs2_extent_block *eb; - struct ocfs2_extent_list *el; - - mlog_entry_void(); - - status = ocfs2_journal_access(handle, inode, fe_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - fe = (struct ocfs2_dinode *) fe_bh->b_data; - el = &fe->id2.i_list; - if (el->l_tree_depth) { - /* This is another operation where we want to be - * careful about our tree updates. An error here means - * none of the previous changes we made should roll - * forward. As a result, we have to record the buffers - * for this part of the tree in an array and reserve a - * journal write to them before making any changes. */ - num_bhs = le16_to_cpu(fe->id2.i_list.l_tree_depth); - eb_bhs = kcalloc(num_bhs, sizeof(struct buffer_head *), - GFP_KERNEL); - if (!eb_bhs) { - status = -ENOMEM; - mlog_errno(status); - goto bail; - } - - i = 0; - while(el->l_tree_depth) { - next_free = le16_to_cpu(el->l_next_free_rec); - if (next_free == 0) { - ocfs2_error(inode->i_sb, - "Dinode %llu has a bad extent list", - (unsigned long long)OCFS2_I(inode)->ip_blkno); - status = -EIO; - goto bail; - } - next_blkno = le64_to_cpu(el->l_recs[next_free - 1].e_blkno); - - BUG_ON(i >= num_bhs); - status = ocfs2_read_block(osb, next_blkno, &eb_bhs[i], - OCFS2_BH_CACHED, inode); - if (status < 0) { - mlog_errno(status); - goto bail; - } - eb = (struct ocfs2_extent_block *) eb_bhs[i]->b_data; - if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { - OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, - eb); - status = -EIO; - goto bail; - } - - status = ocfs2_journal_access(handle, inode, eb_bhs[i], - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - el = &eb->h_list; - i++; - /* When we leave this loop, eb_bhs[num_bhs - 1] will - * hold the bottom-most leaf extent block. */ - } - BUG_ON(el->l_tree_depth); - - el = &fe->id2.i_list; - /* If we have tree depth, then the fe update is - * trivial, and we want to switch el out for the - * bottom-most leaf in order to update it with the - * actual extent data below. */ - next_free = le16_to_cpu(el->l_next_free_rec); - if (next_free == 0) { - ocfs2_error(inode->i_sb, - "Dinode %llu has a bad extent list", - (unsigned long long)OCFS2_I(inode)->ip_blkno); - status = -EIO; - goto bail; - } - le32_add_cpu(&el->l_recs[next_free - 1].e_clusters, - new_clusters); - /* (num_bhs - 1) to avoid the leaf */ - for(i = 0; i < (num_bhs - 1); i++) { - eb = (struct ocfs2_extent_block *) eb_bhs[i]->b_data; - el = &eb->h_list; - - /* finally, make our actual change to the - * intermediate extent blocks. */ - next_free = le16_to_cpu(el->l_next_free_rec); - le32_add_cpu(&el->l_recs[next_free - 1].e_clusters, - new_clusters); - - status = ocfs2_journal_dirty(handle, eb_bhs[i]); - if (status < 0) - mlog_errno(status); - } - BUG_ON(i != (num_bhs - 1)); - /* note that the leaf block wasn't touched in - * the loop above */ - eb = (struct ocfs2_extent_block *) eb_bhs[num_bhs - 1]->b_data; - el = &eb->h_list; - BUG_ON(el->l_tree_depth); - } - - /* yay, we can finally add the actual extent now! */ - i = le16_to_cpu(el->l_next_free_rec) - 1; - if (le16_to_cpu(el->l_next_free_rec) && - ocfs2_extent_contig(inode, &el->l_recs[i], start_blk)) { - le32_add_cpu(&el->l_recs[i].e_clusters, new_clusters); - } else if (le16_to_cpu(el->l_next_free_rec) && - (le32_to_cpu(el->l_recs[i].e_clusters) == 0)) { - /* having an empty extent at eof is legal. */ - if (el->l_recs[i].e_cpos != fe->i_clusters) { - ocfs2_error(inode->i_sb, - "Dinode %llu trailing extent is bad: " - "cpos (%u) != number of clusters (%u)", - (unsigned long long)OCFS2_I(inode)->ip_blkno, - le32_to_cpu(el->l_recs[i].e_cpos), - le32_to_cpu(fe->i_clusters)); - status = -EIO; - goto bail; - } - el->l_recs[i].e_blkno = cpu_to_le64(start_blk); - el->l_recs[i].e_clusters = cpu_to_le32(new_clusters); - } else { - /* No contiguous record, or no empty record at eof, so - * we add a new one. */ - - BUG_ON(le16_to_cpu(el->l_next_free_rec) >= - le16_to_cpu(el->l_count)); - i = le16_to_cpu(el->l_next_free_rec); - - el->l_recs[i].e_blkno = cpu_to_le64(start_blk); - el->l_recs[i].e_clusters = cpu_to_le32(new_clusters); - el->l_recs[i].e_cpos = fe->i_clusters; - le16_add_cpu(&el->l_next_free_rec, 1); - } - - /* - * extent_map errors are not fatal, so they are ignored outside - * of flushing the thing. - */ - status = ocfs2_extent_map_append(inode, &el->l_recs[i], - new_clusters); - if (status) { - mlog_errno(status); - ocfs2_extent_map_drop(inode, le32_to_cpu(fe->i_clusters)); - } - - status = ocfs2_journal_dirty(handle, fe_bh); - if (status < 0) - mlog_errno(status); - if (fe->id2.i_list.l_tree_depth) { - status = ocfs2_journal_dirty(handle, eb_bhs[num_bhs - 1]); - if (status < 0) - mlog_errno(status); - } - - status = 0; -bail: - if (eb_bhs) { - for (i = 0; i < num_bhs; i++) - if (eb_bhs[i]) - brelse(eb_bhs[i]); - kfree(eb_bhs); - } - - mlog_exit(status); - return status; -} - -/* * Should only be called when there is no space left in any of the * leaf nodes. What we want to do is find the lowest tree depth * non-leaf extent block with room for new records. There are three @@ -807,53 +828,1548 @@ bail: return status; } -/* the caller needs to update fe->i_clusters */ -int ocfs2_insert_extent(struct ocfs2_super *osb, - handle_t *handle, - struct inode *inode, - struct buffer_head *fe_bh, - u64 start_blk, - u32 new_clusters, - struct ocfs2_alloc_context *meta_ac) +/* + * This is only valid for leaf nodes, which are the only ones that can + * have empty extents anyway. + */ +static inline int ocfs2_is_empty_extent(struct ocfs2_extent_rec *rec) { - int status, i, shift; - struct buffer_head *last_eb_bh = NULL; + return !rec->e_leaf_clusters; +} + +/* + * This function will discard the rightmost extent record. + */ +static void ocfs2_shift_records_right(struct ocfs2_extent_list *el) +{ + int next_free = le16_to_cpu(el->l_next_free_rec); + int count = le16_to_cpu(el->l_count); + unsigned int num_bytes; + + BUG_ON(!next_free); + /* This will cause us to go off the end of our extent list. */ + BUG_ON(next_free >= count); + + num_bytes = sizeof(struct ocfs2_extent_rec) * next_free; + + memmove(&el->l_recs[1], &el->l_recs[0], num_bytes); +} + +static void ocfs2_rotate_leaf(struct ocfs2_extent_list *el, + struct ocfs2_extent_rec *insert_rec) +{ + int i, insert_index, next_free, has_empty, num_bytes; + u32 insert_cpos = le32_to_cpu(insert_rec->e_cpos); + struct ocfs2_extent_rec *rec; + + next_free = le16_to_cpu(el->l_next_free_rec); + has_empty = ocfs2_is_empty_extent(&el->l_recs[0]); + + BUG_ON(!next_free); + + /* The tree code before us didn't allow enough room in the leaf. */ + if (el->l_next_free_rec == el->l_count && !has_empty) + BUG(); + + /* + * The easiest way to approach this is to just remove the + * empty extent and temporarily decrement next_free. + */ + if (has_empty) { + /* + * If next_free was 1 (only an empty extent), this + * loop won't execute, which is fine. We still want + * the decrement above to happen. + */ + for(i = 0; i < (next_free - 1); i++) + el->l_recs[i] = el->l_recs[i+1]; + + next_free--; + } + + /* + * Figure out what the new record index should be. + */ + for(i = 0; i < next_free; i++) { + rec = &el->l_recs[i]; + + if (insert_cpos < le32_to_cpu(rec->e_cpos)) + break; + } + insert_index = i; + + mlog(0, "ins %u: index %d, has_empty %d, next_free %d, count %d\n", + insert_cpos, insert_index, has_empty, next_free, le16_to_cpu(el->l_count)); + + BUG_ON(insert_index < 0); + BUG_ON(insert_index >= le16_to_cpu(el->l_count)); + BUG_ON(insert_index > next_free); + + /* + * No need to memmove if we're just adding to the tail. + */ + if (insert_index != next_free) { + BUG_ON(next_free >= le16_to_cpu(el->l_count)); + + num_bytes = next_free - insert_index; + num_bytes *= sizeof(struct ocfs2_extent_rec); + memmove(&el->l_recs[insert_index + 1], + &el->l_recs[insert_index], + num_bytes); + } + + /* + * Either we had an empty extent, and need to re-increment or + * there was no empty extent on a non full rightmost leaf node, + * in which case we still need to increment. + */ + next_free++; + el->l_next_free_rec = cpu_to_le16(next_free); + /* + * Make sure none of the math above just messed up our tree. + */ + BUG_ON(le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)); + + el->l_recs[insert_index] = *insert_rec; + +} + +/* + * Create an empty extent record . + * + * l_next_free_rec may be updated. + * + * If an empty extent already exists do nothing. + */ +static void ocfs2_create_empty_extent(struct ocfs2_extent_list *el) +{ + int next_free = le16_to_cpu(el->l_next_free_rec); + + BUG_ON(le16_to_cpu(el->l_tree_depth) != 0); + + if (next_free == 0) + goto set_and_inc; + + if (ocfs2_is_empty_extent(&el->l_recs[0])) + return; + + mlog_bug_on_msg(el->l_count == el->l_next_free_rec, + "Asked to create an empty extent in a full list:\n" + "count = %u, tree depth = %u", + le16_to_cpu(el->l_count), + le16_to_cpu(el->l_tree_depth)); + + ocfs2_shift_records_right(el); + +set_and_inc: + le16_add_cpu(&el->l_next_free_rec, 1); + memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); +} + +/* + * For a rotation which involves two leaf nodes, the "root node" is + * the lowest level tree node which contains a path to both leafs. This + * resulting set of information can be used to form a complete "subtree" + * + * This function is passed two full paths from the dinode down to a + * pair of adjacent leaves. It's task is to figure out which path + * index contains the subtree root - this can be the root index itself + * in a worst-case rotation. + * + * The array index of the subtree root is passed back. + */ +static int ocfs2_find_subtree_root(struct inode *inode, + struct ocfs2_path *left, + struct ocfs2_path *right) +{ + int i = 0; + + /* + * Check that the caller passed in two paths from the same tree. + */ + BUG_ON(path_root_bh(left) != path_root_bh(right)); + + do { + i++; + + /* + * The caller didn't pass two adjacent paths. + */ + mlog_bug_on_msg(i > left->p_tree_depth, + "Inode %lu, left depth %u, right depth %u\n" + "left leaf blk %llu, right leaf blk %llu\n", + inode->i_ino, left->p_tree_depth, + right->p_tree_depth, + (unsigned long long)path_leaf_bh(left)->b_blocknr, + (unsigned long long)path_leaf_bh(right)->b_blocknr); + } while (left->p_node[i].bh->b_blocknr == + right->p_node[i].bh->b_blocknr); + + return i - 1; +} + +typedef void (path_insert_t)(void *, struct buffer_head *); + +/* + * Traverse a btree path in search of cpos, starting at root_el. + * + * This code can be called with a cpos larger than the tree, in which + * case it will return the rightmost path. + */ +static int __ocfs2_find_path(struct inode *inode, + struct ocfs2_extent_list *root_el, u32 cpos, + path_insert_t *func, void *data) +{ + int i, ret = 0; + u32 range; + u64 blkno; struct buffer_head *bh = NULL; - struct ocfs2_dinode *fe; struct ocfs2_extent_block *eb; - struct ocfs2_extent_list *el; + struct ocfs2_extent_list *el; + struct ocfs2_extent_rec *rec; + struct ocfs2_inode_info *oi = OCFS2_I(inode); - mlog_entry_void(); + el = root_el; + while (el->l_tree_depth) { + if (le16_to_cpu(el->l_next_free_rec) == 0) { + ocfs2_error(inode->i_sb, + "Inode %llu has empty extent list at " + "depth %u\n", + (unsigned long long)oi->ip_blkno, + le16_to_cpu(el->l_tree_depth)); + ret = -EROFS; + goto out; - mlog(0, "add %u clusters starting at block %llu to inode %llu\n", - new_clusters, (unsigned long long)start_blk, - (unsigned long long)OCFS2_I(inode)->ip_blkno); + } - fe = (struct ocfs2_dinode *) fe_bh->b_data; - el = &fe->id2.i_list; + for(i = 0; i < le16_to_cpu(el->l_next_free_rec) - 1; i++) { + rec = &el->l_recs[i]; + + /* + * In the case that cpos is off the allocation + * tree, this should just wind up returning the + * rightmost record. + */ + range = le32_to_cpu(rec->e_cpos) + + ocfs2_rec_clusters(el, rec); + if (cpos >= le32_to_cpu(rec->e_cpos) && cpos < range) + break; + } - if (el->l_tree_depth) { - /* jump to end of tree */ - status = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk), - &last_eb_bh, OCFS2_BH_CACHED, inode); - if (status < 0) { - mlog_exit(status); - goto bail; + blkno = le64_to_cpu(el->l_recs[i].e_blkno); + if (blkno == 0) { + ocfs2_error(inode->i_sb, + "Inode %llu has bad blkno in extent list " + "at depth %u (index %d)\n", + (unsigned long long)oi->ip_blkno, + le16_to_cpu(el->l_tree_depth), i); + ret = -EROFS; + goto out; } - eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; + + brelse(bh); + bh = NULL; + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno, + &bh, OCFS2_BH_CACHED, inode); + if (ret) { + mlog_errno(ret); + goto out; + } + + eb = (struct ocfs2_extent_block *) bh->b_data; el = &eb->h_list; + if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { + OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); + ret = -EIO; + goto out; + } + + if (le16_to_cpu(el->l_next_free_rec) > + le16_to_cpu(el->l_count)) { + ocfs2_error(inode->i_sb, + "Inode %llu has bad count in extent list " + "at block %llu (next free=%u, count=%u)\n", + (unsigned long long)oi->ip_blkno, + (unsigned long long)bh->b_blocknr, + le16_to_cpu(el->l_next_free_rec), + le16_to_cpu(el->l_count)); + ret = -EROFS; + goto out; + } + + if (func) + func(data, bh); + } + +out: + /* + * Catch any trailing bh that the loop didn't handle. + */ + brelse(bh); + + return ret; +} + +/* + * Given an initialized path (that is, it has a valid root extent + * list), this function will traverse the btree in search of the path + * which would contain cpos. + * + * The path traveled is recorded in the path structure. + * + * Note that this will not do any comparisons on leaf node extent + * records, so it will work fine in the case that we just added a tree + * branch. + */ +struct find_path_data { + int index; + struct ocfs2_path *path; +}; +static void find_path_ins(void *data, struct buffer_head *bh) +{ + struct find_path_data *fp = data; + + get_bh(bh); + ocfs2_path_insert_eb(fp->path, fp->index, bh); + fp->index++; +} +static int ocfs2_find_path(struct inode *inode, struct ocfs2_path *path, + u32 cpos) +{ + struct find_path_data data; + + data.index = 1; + data.path = path; + return __ocfs2_find_path(inode, path_root_el(path), cpos, + find_path_ins, &data); +} + +static void find_leaf_ins(void *data, struct buffer_head *bh) +{ + struct ocfs2_extent_block *eb =(struct ocfs2_extent_block *)bh->b_data; + struct ocfs2_extent_list *el = &eb->h_list; + struct buffer_head **ret = data; + + /* We want to retain only the leaf block. */ + if (le16_to_cpu(el->l_tree_depth) == 0) { + get_bh(bh); + *ret = bh; + } +} +/* + * Find the leaf block in the tree which would contain cpos. No + * checking of the actual leaf is done. + * + * Some paths want to call this instead of allocating a path structure + * and calling ocfs2_find_path(). + * + * This function doesn't handle non btree extent lists. + */ +int ocfs2_find_leaf(struct inode *inode, struct ocfs2_extent_list *root_el, + u32 cpos, struct buffer_head **leaf_bh) +{ + int ret; + struct buffer_head *bh = NULL; + + ret = __ocfs2_find_path(inode, root_el, cpos, find_leaf_ins, &bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + *leaf_bh = bh; +out: + return ret; +} + +/* + * Adjust the adjacent records (left_rec, right_rec) involved in a rotation. + * + * Basically, we've moved stuff around at the bottom of the tree and + * we need to fix up the extent records above the changes to reflect + * the new changes. + * + * left_rec: the record on the left. + * left_child_el: is the child list pointed to by left_rec + * right_rec: the record to the right of left_rec + * right_child_el: is the child list pointed to by right_rec + * + * By definition, this only works on interior nodes. + */ +static void ocfs2_adjust_adjacent_records(struct ocfs2_extent_rec *left_rec, + struct ocfs2_extent_list *left_child_el, + struct ocfs2_extent_rec *right_rec, + struct ocfs2_extent_list *right_child_el) +{ + u32 left_clusters, right_end; + + /* + * Interior nodes never have holes. Their cpos is the cpos of + * the leftmost record in their child list. Their cluster + * count covers the full theoretical range of their child list + * - the range between their cpos and the cpos of the record + * immediately to their right. + */ + left_clusters = le32_to_cpu(right_child_el->l_recs[0].e_cpos); + left_clusters -= le32_to_cpu(left_rec->e_cpos); + left_rec->e_int_clusters = cpu_to_le32(left_clusters); + + /* + * Calculate the rightmost cluster count boundary before + * moving cpos - we will need to adjust clusters after + * updating e_cpos to keep the same highest cluster count. + */ + right_end = le32_to_cpu(right_rec->e_cpos); + right_end += le32_to_cpu(right_rec->e_int_clusters); + + right_rec->e_cpos = left_rec->e_cpos; + le32_add_cpu(&right_rec->e_cpos, left_clusters); + + right_end -= le32_to_cpu(right_rec->e_cpos); + right_rec->e_int_clusters = cpu_to_le32(right_end); +} + +/* + * Adjust the adjacent root node records involved in a + * rotation. left_el_blkno is passed in as a key so that we can easily + * find it's index in the root list. + */ +static void ocfs2_adjust_root_records(struct ocfs2_extent_list *root_el, + struct ocfs2_extent_list *left_el, + struct ocfs2_extent_list *right_el, + u64 left_el_blkno) +{ + int i; + + BUG_ON(le16_to_cpu(root_el->l_tree_depth) <= + le16_to_cpu(left_el->l_tree_depth)); + + for(i = 0; i < le16_to_cpu(root_el->l_next_free_rec) - 1; i++) { + if (le64_to_cpu(root_el->l_recs[i].e_blkno) == left_el_blkno) + break; + } + + /* + * The path walking code should have never returned a root and + * two paths which are not adjacent. + */ + BUG_ON(i >= (le16_to_cpu(root_el->l_next_free_rec) - 1)); + + ocfs2_adjust_adjacent_records(&root_el->l_recs[i], left_el, + &root_el->l_recs[i + 1], right_el); +} + +/* + * We've changed a leaf block (in right_path) and need to reflect that + * change back up the subtree. + * + * This happens in multiple places: + * - When we've moved an extent record from the left path leaf to the right + * path leaf to make room for an empty extent in the left path leaf. + * - When our insert into the right path leaf is at the leftmost edge + * and requires an update of the path immediately to it's left. This + * can occur at the end of some types of rotation and appending inserts. + */ +static void ocfs2_complete_edge_insert(struct inode *inode, handle_t *handle, + struct ocfs2_path *left_path, + struct ocfs2_path *right_path, + int subtree_index) +{ + int ret, i, idx; + struct ocfs2_extent_list *el, *left_el, *right_el; + struct ocfs2_extent_rec *left_rec, *right_rec; + struct buffer_head *root_bh = left_path->p_node[subtree_index].bh; + + /* + * Update the counts and position values within all the + * interior nodes to reflect the leaf rotation we just did. + * + * The root node is handled below the loop. + * + * We begin the loop with right_el and left_el pointing to the + * leaf lists and work our way up. + * + * NOTE: within this loop, left_el and right_el always refer + * to the *child* lists. + */ + left_el = path_leaf_el(left_path); + right_el = path_leaf_el(right_path); + for(i = left_path->p_tree_depth - 1; i > subtree_index; i--) { + mlog(0, "Adjust records at index %u\n", i); + + /* + * One nice property of knowing that all of these + * nodes are below the root is that we only deal with + * the leftmost right node record and the rightmost + * left node record. + */ + el = left_path->p_node[i].el; + idx = le16_to_cpu(left_el->l_next_free_rec) - 1; + left_rec = &el->l_recs[idx]; + + el = right_path->p_node[i].el; + right_rec = &el->l_recs[0]; + + ocfs2_adjust_adjacent_records(left_rec, left_el, right_rec, + right_el); + + ret = ocfs2_journal_dirty(handle, left_path->p_node[i].bh); + if (ret) + mlog_errno(ret); + + ret = ocfs2_journal_dirty(handle, right_path->p_node[i].bh); + if (ret) + mlog_errno(ret); + + /* + * Setup our list pointers now so that the current + * parents become children in the next iteration. + */ + left_el = left_path->p_node[i].el; + right_el = right_path->p_node[i].el; + } + + /* + * At the root node, adjust the two adjacent records which + * begin our path to the leaves. + */ + + el = left_path->p_node[subtree_index].el; + left_el = left_path->p_node[subtree_index + 1].el; + right_el = right_path->p_node[subtree_index + 1].el; + + ocfs2_adjust_root_records(el, left_el, right_el, + left_path->p_node[subtree_index + 1].bh->b_blocknr); + + root_bh = left_path->p_node[subtree_index].bh; + + ret = ocfs2_journal_dirty(handle, root_bh); + if (ret) + mlog_errno(ret); +} + +static int ocfs2_rotate_subtree_right(struct inode *inode, + handle_t *handle, + struct ocfs2_path *left_path, + struct ocfs2_path *right_path, + int subtree_index) +{ + int ret, i; + struct buffer_head *right_leaf_bh; + struct buffer_head *left_leaf_bh = NULL; + struct buffer_head *root_bh; + struct ocfs2_extent_list *right_el, *left_el; + struct ocfs2_extent_rec move_rec; + + left_leaf_bh = path_leaf_bh(left_path); + left_el = path_leaf_el(left_path); + + if (left_el->l_next_free_rec != left_el->l_count) { + ocfs2_error(inode->i_sb, + "Inode %llu has non-full interior leaf node %llu" + "(next free = %u)", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)left_leaf_bh->b_blocknr, + le16_to_cpu(left_el->l_next_free_rec)); + return -EROFS; + } + + /* + * This extent block may already have an empty record, so we + * return early if so. + */ + if (ocfs2_is_empty_extent(&left_el->l_recs[0])) + return 0; + + root_bh = left_path->p_node[subtree_index].bh; + BUG_ON(root_bh != right_path->p_node[subtree_index].bh); + + ret = ocfs2_journal_access(handle, inode, root_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + for(i = subtree_index + 1; i < path_num_items(right_path); i++) { + ret = ocfs2_journal_access(handle, inode, + right_path->p_node[i].bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, + left_path->p_node[i].bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + right_leaf_bh = path_leaf_bh(right_path); + right_el = path_leaf_el(right_path); + + /* This is a code error, not a disk corruption. */ + mlog_bug_on_msg(!right_el->l_next_free_rec, "Inode %llu: Rotate fails " + "because rightmost leaf block %llu is empty\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)right_leaf_bh->b_blocknr); + + ocfs2_create_empty_extent(right_el); + + ret = ocfs2_journal_dirty(handle, right_leaf_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* Do the copy now. */ + i = le16_to_cpu(left_el->l_next_free_rec) - 1; + move_rec = left_el->l_recs[i]; + right_el->l_recs[0] = move_rec; + + /* + * Clear out the record we just copied and shift everything + * over, leaving an empty extent in the left leaf. + * + * We temporarily subtract from next_free_rec so that the + * shift will lose the tail record (which is now defunct). + */ + le16_add_cpu(&left_el->l_next_free_rec, -1); + ocfs2_shift_records_right(left_el); + memset(&left_el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); + le16_add_cpu(&left_el->l_next_free_rec, 1); + + ret = ocfs2_journal_dirty(handle, left_leaf_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + ocfs2_complete_edge_insert(inode, handle, left_path, right_path, + subtree_index); + +out: + return ret; +} + +/* + * Given a full path, determine what cpos value would return us a path + * containing the leaf immediately to the left of the current one. + * + * Will return zero if the path passed in is already the leftmost path. + */ +static int ocfs2_find_cpos_for_left_leaf(struct super_block *sb, + struct ocfs2_path *path, u32 *cpos) +{ + int i, j, ret = 0; + u64 blkno; + struct ocfs2_extent_list *el; + + BUG_ON(path->p_tree_depth == 0); + + *cpos = 0; + + blkno = path_leaf_bh(path)->b_blocknr; + + /* Start at the tree node just above the leaf and work our way up. */ + i = path->p_tree_depth - 1; + while (i >= 0) { + el = path->p_node[i].el; + + /* + * Find the extent record just before the one in our + * path. + */ + for(j = 0; j < le16_to_cpu(el->l_next_free_rec); j++) { + if (le64_to_cpu(el->l_recs[j].e_blkno) == blkno) { + if (j == 0) { + if (i == 0) { + /* + * We've determined that the + * path specified is already + * the leftmost one - return a + * cpos of zero. + */ + goto out; + } + /* + * The leftmost record points to our + * leaf - we need to travel up the + * tree one level. + */ + goto next_node; + } + + *cpos = le32_to_cpu(el->l_recs[j - 1].e_cpos); + *cpos = *cpos + ocfs2_rec_clusters(el, + &el->l_recs[j - 1]); + *cpos = *cpos - 1; + goto out; + } + } + + /* + * If we got here, we never found a valid node where + * the tree indicated one should be. + */ + ocfs2_error(sb, + "Invalid extent tree at extent block %llu\n", + (unsigned long long)blkno); + ret = -EROFS; + goto out; + +next_node: + blkno = path->p_node[i].bh->b_blocknr; + i--; + } + +out: + return ret; +} + +static int ocfs2_extend_rotate_transaction(handle_t *handle, int subtree_depth, + struct ocfs2_path *path) +{ + int credits = (path->p_tree_depth - subtree_depth) * 2 + 1; + + if (handle->h_buffer_credits < credits) + return ocfs2_extend_trans(handle, credits); + + return 0; +} + +/* + * Trap the case where we're inserting into the theoretical range past + * the _actual_ left leaf range. Otherwise, we'll rotate a record + * whose cpos is less than ours into the right leaf. + * + * It's only necessary to look at the rightmost record of the left + * leaf because the logic that calls us should ensure that the + * theoretical ranges in the path components above the leaves are + * correct. + */ +static int ocfs2_rotate_requires_path_adjustment(struct ocfs2_path *left_path, + u32 insert_cpos) +{ + struct ocfs2_extent_list *left_el; + struct ocfs2_extent_rec *rec; + int next_free; + + left_el = path_leaf_el(left_path); + next_free = le16_to_cpu(left_el->l_next_free_rec); + rec = &left_el->l_recs[next_free - 1]; + + if (insert_cpos > le32_to_cpu(rec->e_cpos)) + return 1; + return 0; +} + +/* + * Rotate all the records in a btree right one record, starting at insert_cpos. + * + * The path to the rightmost leaf should be passed in. + * + * The array is assumed to be large enough to hold an entire path (tree depth). + * + * Upon succesful return from this function: + * + * - The 'right_path' array will contain a path to the leaf block + * whose range contains e_cpos. + * - That leaf block will have a single empty extent in list index 0. + * - In the case that the rotation requires a post-insert update, + * *ret_left_path will contain a valid path which can be passed to + * ocfs2_insert_path(). + */ +static int ocfs2_rotate_tree_right(struct inode *inode, + handle_t *handle, + u32 insert_cpos, + struct ocfs2_path *right_path, + struct ocfs2_path **ret_left_path) +{ + int ret, start; + u32 cpos; + struct ocfs2_path *left_path = NULL; + + *ret_left_path = NULL; + + left_path = ocfs2_new_path(path_root_bh(right_path), + path_root_el(right_path)); + if (!left_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, right_path, &cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + mlog(0, "Insert: %u, first left path cpos: %u\n", insert_cpos, cpos); + + /* + * What we want to do here is: + * + * 1) Start with the rightmost path. + * + * 2) Determine a path to the leaf block directly to the left + * of that leaf. + * + * 3) Determine the 'subtree root' - the lowest level tree node + * which contains a path to both leaves. + * + * 4) Rotate the subtree. + * + * 5) Find the next subtree by considering the left path to be + * the new right path. + * + * The check at the top of this while loop also accepts + * insert_cpos == cpos because cpos is only a _theoretical_ + * value to get us the left path - insert_cpos might very well + * be filling that hole. + * + * Stop at a cpos of '0' because we either started at the + * leftmost branch (i.e., a tree with one branch and a + * rotation inside of it), or we've gone as far as we can in + * rotating subtrees. + */ + while (cpos && insert_cpos <= cpos) { + mlog(0, "Rotating a tree: ins. cpos: %u, left path cpos: %u\n", + insert_cpos, cpos); + + ret = ocfs2_find_path(inode, left_path, cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + mlog_bug_on_msg(path_leaf_bh(left_path) == + path_leaf_bh(right_path), + "Inode %lu: error during insert of %u " + "(left path cpos %u) results in two identical " + "paths ending at %llu\n", + inode->i_ino, insert_cpos, cpos, + (unsigned long long) + path_leaf_bh(left_path)->b_blocknr); + + if (ocfs2_rotate_requires_path_adjustment(left_path, + insert_cpos)) { + mlog(0, "Path adjustment required\n"); + + /* + * We've rotated the tree as much as we + * should. The rest is up to + * ocfs2_insert_path() to complete, after the + * record insertion. We indicate this + * situation by returning the left path. + * + * The reason we don't adjust the records here + * before the record insert is that an error + * later might break the rule where a parent + * record e_cpos will reflect the actual + * e_cpos of the 1st nonempty record of the + * child list. + */ + *ret_left_path = left_path; + goto out_ret_path; + } + + start = ocfs2_find_subtree_root(inode, left_path, right_path); + + mlog(0, "Subtree root at index %d (blk %llu, depth %d)\n", + start, + (unsigned long long) right_path->p_node[start].bh->b_blocknr, + right_path->p_tree_depth); + + ret = ocfs2_extend_rotate_transaction(handle, start, + right_path); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_rotate_subtree_right(inode, handle, left_path, + right_path, start); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * There is no need to re-read the next right path + * as we know that it'll be our current left + * path. Optimize by copying values instead. + */ + ocfs2_mv_path(right_path, left_path); + + ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, right_path, + &cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + } + +out: + ocfs2_free_path(left_path); + +out_ret_path: + return ret; +} + +/* + * Do the final bits of extent record insertion at the target leaf + * list. If this leaf is part of an allocation tree, it is assumed + * that the tree above has been prepared. + */ +static void ocfs2_insert_at_leaf(struct ocfs2_extent_rec *insert_rec, + struct ocfs2_extent_list *el, + struct ocfs2_insert_type *insert, + struct inode *inode) +{ + int i = insert->ins_contig_index; + unsigned int range; + struct ocfs2_extent_rec *rec; + + BUG_ON(le16_to_cpu(el->l_tree_depth) != 0); + + /* + * Contiguous insert - either left or right. + */ + if (insert->ins_contig != CONTIG_NONE) { + rec = &el->l_recs[i]; + if (insert->ins_contig == CONTIG_LEFT) { + rec->e_blkno = insert_rec->e_blkno; + rec->e_cpos = insert_rec->e_cpos; + } + le16_add_cpu(&rec->e_leaf_clusters, + le16_to_cpu(insert_rec->e_leaf_clusters)); + return; + } + + /* + * Handle insert into an empty leaf. + */ + if (le16_to_cpu(el->l_next_free_rec) == 0 || + ((le16_to_cpu(el->l_next_free_rec) == 1) && + ocfs2_is_empty_extent(&el->l_recs[0]))) { + el->l_recs[0] = *insert_rec; + el->l_next_free_rec = cpu_to_le16(1); + return; + } + + /* + * Appending insert. + */ + if (insert->ins_appending == APPEND_TAIL) { + i = le16_to_cpu(el->l_next_free_rec) - 1; + rec = &el->l_recs[i]; + range = le32_to_cpu(rec->e_cpos) + + le16_to_cpu(rec->e_leaf_clusters); + BUG_ON(le32_to_cpu(insert_rec->e_cpos) < range); + + mlog_bug_on_msg(le16_to_cpu(el->l_next_free_rec) >= + le16_to_cpu(el->l_count), + "inode %lu, depth %u, count %u, next free %u, " + "rec.cpos %u, rec.clusters %u, " + "insert.cpos %u, insert.clusters %u\n", + inode->i_ino, + le16_to_cpu(el->l_tree_depth), + le16_to_cpu(el->l_count), + le16_to_cpu(el->l_next_free_rec), + le32_to_cpu(el->l_recs[i].e_cpos), + le16_to_cpu(el->l_recs[i].e_leaf_clusters), + le32_to_cpu(insert_rec->e_cpos), + le16_to_cpu(insert_rec->e_leaf_clusters)); + i++; + el->l_recs[i] = *insert_rec; + le16_add_cpu(&el->l_next_free_rec, 1); + return; + } + + /* + * Ok, we have to rotate. + * + * At this point, it is safe to assume that inserting into an + * empty leaf and appending to a leaf have both been handled + * above. + * + * This leaf needs to have space, either by the empty 1st + * extent record, or by virtue of an l_next_rec < l_count. + */ + ocfs2_rotate_leaf(el, insert_rec); +} + +static inline void ocfs2_update_dinode_clusters(struct inode *inode, + struct ocfs2_dinode *di, + u32 clusters) +{ + le32_add_cpu(&di->i_clusters, clusters); + spin_lock(&OCFS2_I(inode)->ip_lock); + OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters); + spin_unlock(&OCFS2_I(inode)->ip_lock); +} + +static int ocfs2_append_rec_to_path(struct inode *inode, handle_t *handle, + struct ocfs2_extent_rec *insert_rec, + struct ocfs2_path *right_path, + struct ocfs2_path **ret_left_path) +{ + int ret, i, next_free; + struct buffer_head *bh; + struct ocfs2_extent_list *el; + struct ocfs2_path *left_path = NULL; + + *ret_left_path = NULL; + + /* + * This shouldn't happen for non-trees. The extent rec cluster + * count manipulation below only works for interior nodes. + */ + BUG_ON(right_path->p_tree_depth == 0); + + /* + * If our appending insert is at the leftmost edge of a leaf, + * then we might need to update the rightmost records of the + * neighboring path. + */ + el = path_leaf_el(right_path); + next_free = le16_to_cpu(el->l_next_free_rec); + if (next_free == 0 || + (next_free == 1 && ocfs2_is_empty_extent(&el->l_recs[0]))) { + u32 left_cpos; + + ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, right_path, + &left_cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + mlog(0, "Append may need a left path update. cpos: %u, " + "left_cpos: %u\n", le32_to_cpu(insert_rec->e_cpos), + left_cpos); + + /* + * No need to worry if the append is already in the + * leftmost leaf. + */ + if (left_cpos) { + left_path = ocfs2_new_path(path_root_bh(right_path), + path_root_el(right_path)); + if (!left_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_path(inode, left_path, left_cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * ocfs2_insert_path() will pass the left_path to the + * journal for us. + */ + } + } + + ret = ocfs2_journal_access_path(inode, handle, right_path); + if (ret) { + mlog_errno(ret); + goto out; + } + + el = path_root_el(right_path); + bh = path_root_bh(right_path); + i = 0; + while (1) { + struct ocfs2_extent_rec *rec; + + next_free = le16_to_cpu(el->l_next_free_rec); + if (next_free == 0) { + ocfs2_error(inode->i_sb, + "Dinode %llu has a bad extent list", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + ret = -EIO; + goto out; + } + + rec = &el->l_recs[next_free - 1]; + + rec->e_int_clusters = insert_rec->e_cpos; + le32_add_cpu(&rec->e_int_clusters, + le16_to_cpu(insert_rec->e_leaf_clusters)); + le32_add_cpu(&rec->e_int_clusters, + -le32_to_cpu(rec->e_cpos)); + + ret = ocfs2_journal_dirty(handle, bh); + if (ret) + mlog_errno(ret); + + /* Don't touch the leaf node */ + if (++i >= right_path->p_tree_depth) + break; + + bh = right_path->p_node[i].bh; + el = right_path->p_node[i].el; + } + + *ret_left_path = left_path; + ret = 0; +out: + if (ret != 0) + ocfs2_free_path(left_path); + + return ret; +} + +/* + * This function only does inserts on an allocation b-tree. For dinode + * lists, ocfs2_insert_at_leaf() is called directly. + * + * right_path is the path we want to do the actual insert + * in. left_path should only be passed in if we need to update that + * portion of the tree after an edge insert. + */ +static int ocfs2_insert_path(struct inode *inode, + handle_t *handle, + struct ocfs2_path *left_path, + struct ocfs2_path *right_path, + struct ocfs2_extent_rec *insert_rec, + struct ocfs2_insert_type *insert) +{ + int ret, subtree_index; + struct buffer_head *leaf_bh = path_leaf_bh(right_path); + struct ocfs2_extent_list *el; + + /* + * Pass both paths to the journal. The majority of inserts + * will be touching all components anyway. + */ + ret = ocfs2_journal_access_path(inode, handle, right_path); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + if (left_path) { + int credits = handle->h_buffer_credits; + + /* + * There's a chance that left_path got passed back to + * us without being accounted for in the + * journal. Extend our transaction here to be sure we + * can change those blocks. + */ + credits += left_path->p_tree_depth; + + ret = ocfs2_extend_trans(handle, credits); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access_path(inode, handle, left_path); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + el = path_leaf_el(right_path); + + ocfs2_insert_at_leaf(insert_rec, el, insert, inode); + ret = ocfs2_journal_dirty(handle, leaf_bh); + if (ret) + mlog_errno(ret); + + if (left_path) { + /* + * The rotate code has indicated that we need to fix + * up portions of the tree after the insert. + * + * XXX: Should we extend the transaction here? + */ + subtree_index = ocfs2_find_subtree_root(inode, left_path, + right_path); + ocfs2_complete_edge_insert(inode, handle, left_path, + right_path, subtree_index); + } + + ret = 0; +out: + return ret; +} + +static int ocfs2_do_insert_extent(struct inode *inode, + handle_t *handle, + struct buffer_head *di_bh, + struct ocfs2_extent_rec *insert_rec, + struct ocfs2_insert_type *type) +{ + int ret, rotate = 0; + u32 cpos; + struct ocfs2_path *right_path = NULL; + struct ocfs2_path *left_path = NULL; + struct ocfs2_dinode *di; + struct ocfs2_extent_list *el; + + di = (struct ocfs2_dinode *) di_bh->b_data; + el = &di->id2.i_list; + + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (le16_to_cpu(el->l_tree_depth) == 0) { + ocfs2_insert_at_leaf(insert_rec, el, type, inode); + goto out_update_clusters; + } + + right_path = ocfs2_new_inode_path(di_bh); + if (!right_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + /* + * Determine the path to start with. Rotations need the + * rightmost path, everything else can go directly to the + * target leaf. + */ + cpos = le32_to_cpu(insert_rec->e_cpos); + if (type->ins_appending == APPEND_NONE && + type->ins_contig == CONTIG_NONE) { + rotate = 1; + cpos = UINT_MAX; + } + + ret = ocfs2_find_path(inode, right_path, cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * Rotations and appends need special treatment - they modify + * parts of the tree's above them. + * + * Both might pass back a path immediate to the left of the + * one being inserted to. This will be cause + * ocfs2_insert_path() to modify the rightmost records of + * left_path to account for an edge insert. + * + * XXX: When modifying this code, keep in mind that an insert + * can wind up skipping both of these two special cases... + */ + if (rotate) { + ret = ocfs2_rotate_tree_right(inode, handle, + le32_to_cpu(insert_rec->e_cpos), + right_path, &left_path); + if (ret) { + mlog_errno(ret); + goto out; + } + } else if (type->ins_appending == APPEND_TAIL + && type->ins_contig != CONTIG_LEFT) { + ret = ocfs2_append_rec_to_path(inode, handle, insert_rec, + right_path, &left_path); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + ret = ocfs2_insert_path(inode, handle, left_path, right_path, + insert_rec, type); + if (ret) { + mlog_errno(ret); + goto out; + } + +out_update_clusters: + ocfs2_update_dinode_clusters(inode, di, + le16_to_cpu(insert_rec->e_leaf_clusters)); + + ret = ocfs2_journal_dirty(handle, di_bh); + if (ret) + mlog_errno(ret); + +out: + ocfs2_free_path(left_path); + ocfs2_free_path(right_path); + + return ret; +} + +static void ocfs2_figure_contig_type(struct inode *inode, + struct ocfs2_insert_type *insert, + struct ocfs2_extent_list *el, + struct ocfs2_extent_rec *insert_rec) +{ + int i; + enum ocfs2_contig_type contig_type = CONTIG_NONE; + + BUG_ON(le16_to_cpu(el->l_tree_depth) != 0); + + for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { + contig_type = ocfs2_extent_contig(inode, &el->l_recs[i], + insert_rec); + if (contig_type != CONTIG_NONE) { + insert->ins_contig_index = i; + break; + } + } + insert->ins_contig = contig_type; +} + +/* + * This should only be called against the righmost leaf extent list. + * + * ocfs2_figure_appending_type() will figure out whether we'll have to + * insert at the tail of the rightmost leaf. + * + * This should also work against the dinode list for tree's with 0 + * depth. If we consider the dinode list to be the rightmost leaf node + * then the logic here makes sense. + */ +static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert, + struct ocfs2_extent_list *el, + struct ocfs2_extent_rec *insert_rec) +{ + int i; + u32 cpos = le32_to_cpu(insert_rec->e_cpos); + struct ocfs2_extent_rec *rec; + + insert->ins_appending = APPEND_NONE; + + BUG_ON(le16_to_cpu(el->l_tree_depth) != 0); + + if (!el->l_next_free_rec) + goto set_tail_append; + + if (ocfs2_is_empty_extent(&el->l_recs[0])) { + /* Were all records empty? */ + if (le16_to_cpu(el->l_next_free_rec) == 1) + goto set_tail_append; } - /* Can we allocate without adding/shifting tree bits? */ i = le16_to_cpu(el->l_next_free_rec) - 1; - if (le16_to_cpu(el->l_next_free_rec) == 0 - || (le16_to_cpu(el->l_next_free_rec) < le16_to_cpu(el->l_count)) - || le32_to_cpu(el->l_recs[i].e_clusters) == 0 - || ocfs2_extent_contig(inode, &el->l_recs[i], start_blk)) - goto out_add; + rec = &el->l_recs[i]; + + if (cpos >= + (le32_to_cpu(rec->e_cpos) + le16_to_cpu(rec->e_leaf_clusters))) + goto set_tail_append; + + return; + +set_tail_append: + insert->ins_appending = APPEND_TAIL; +} + +/* + * Helper function called at the begining of an insert. + * + * This computes a few things that are commonly used in the process of + * inserting into the btree: + * - Whether the new extent is contiguous with an existing one. + * - The current tree depth. + * - Whether the insert is an appending one. + * - The total # of free records in the tree. + * + * All of the information is stored on the ocfs2_insert_type + * structure. + */ +static int ocfs2_figure_insert_type(struct inode *inode, + struct buffer_head *di_bh, + struct buffer_head **last_eb_bh, + struct ocfs2_extent_rec *insert_rec, + struct ocfs2_insert_type *insert) +{ + int ret; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_list *el; + struct ocfs2_path *path = NULL; + struct buffer_head *bh = NULL; + + el = &di->id2.i_list; + insert->ins_tree_depth = le16_to_cpu(el->l_tree_depth); + + if (el->l_tree_depth) { + /* + * If we have tree depth, we read in the + * rightmost extent block ahead of time as + * ocfs2_figure_insert_type() and ocfs2_add_branch() + * may want it later. + */ + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), + le64_to_cpu(di->i_last_eb_blk), &bh, + OCFS2_BH_CACHED, inode); + if (ret) { + mlog_exit(ret); + goto out; + } + eb = (struct ocfs2_extent_block *) bh->b_data; + el = &eb->h_list; + } + + /* + * Unless we have a contiguous insert, we'll need to know if + * there is room left in our allocation tree for another + * extent record. + * + * XXX: This test is simplistic, we can search for empty + * extent records too. + */ + insert->ins_free_records = le16_to_cpu(el->l_count) - + le16_to_cpu(el->l_next_free_rec); + + if (!insert->ins_tree_depth) { + ocfs2_figure_contig_type(inode, insert, el, insert_rec); + ocfs2_figure_appending_type(insert, el, insert_rec); + return 0; + } + + path = ocfs2_new_inode_path(di_bh); + if (!path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + /* + * In the case that we're inserting past what the tree + * currently accounts for, ocfs2_find_path() will return for + * us the rightmost tree path. This is accounted for below in + * the appending code. + */ + ret = ocfs2_find_path(inode, path, le32_to_cpu(insert_rec->e_cpos)); + if (ret) { + mlog_errno(ret); + goto out; + } + + el = path_leaf_el(path); + + /* + * Now that we have the path, there's two things we want to determine: + * 1) Contiguousness (also set contig_index if this is so) + * + * 2) Are we doing an append? We can trivially break this up + * into two types of appends: simple record append, or a + * rotate inside the tail leaf. + */ + ocfs2_figure_contig_type(inode, insert, el, insert_rec); + + /* + * The insert code isn't quite ready to deal with all cases of + * left contiguousness. Specifically, if it's an insert into + * the 1st record in a leaf, it will require the adjustment of + * cluster count on the last record of the path directly to it's + * left. For now, just catch that case and fool the layers + * above us. This works just fine for tree_depth == 0, which + * is why we allow that above. + */ + if (insert->ins_contig == CONTIG_LEFT && + insert->ins_contig_index == 0) + insert->ins_contig = CONTIG_NONE; + + /* + * Ok, so we can simply compare against last_eb to figure out + * whether the path doesn't exist. This will only happen in + * the case that we're doing a tail append, so maybe we can + * take advantage of that information somehow. + */ + if (le64_to_cpu(di->i_last_eb_blk) == path_leaf_bh(path)->b_blocknr) { + /* + * Ok, ocfs2_find_path() returned us the rightmost + * tree path. This might be an appending insert. There are + * two cases: + * 1) We're doing a true append at the tail: + * -This might even be off the end of the leaf + * 2) We're "appending" by rotating in the tail + */ + ocfs2_figure_appending_type(insert, el, insert_rec); + } + +out: + ocfs2_free_path(path); + + if (ret == 0) + *last_eb_bh = bh; + else + brelse(bh); + return ret; +} + +/* + * Insert an extent into an inode btree. + * + * The caller needs to update fe->i_clusters + */ +int ocfs2_insert_extent(struct ocfs2_super *osb, + handle_t *handle, + struct inode *inode, + struct buffer_head *fe_bh, + u32 cpos, + u64 start_blk, + u32 new_clusters, + struct ocfs2_alloc_context *meta_ac) +{ + int status, shift; + struct buffer_head *last_eb_bh = NULL; + struct buffer_head *bh = NULL; + struct ocfs2_insert_type insert = {0, }; + struct ocfs2_extent_rec rec; + + mlog(0, "add %u clusters at position %u to inode %llu\n", + new_clusters, cpos, (unsigned long long)OCFS2_I(inode)->ip_blkno); + + mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) && + (OCFS2_I(inode)->ip_clusters != cpos), + "Device %s, asking for sparse allocation: inode %llu, " + "cpos %u, clusters %u\n", + osb->dev_str, + (unsigned long long)OCFS2_I(inode)->ip_blkno, cpos, + OCFS2_I(inode)->ip_clusters); + + memset(&rec, 0, sizeof(rec)); + rec.e_cpos = cpu_to_le32(cpos); + rec.e_blkno = cpu_to_le64(start_blk); + rec.e_leaf_clusters = cpu_to_le16(new_clusters); + + status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec, + &insert); + if (status < 0) { + mlog_errno(status); + goto bail; + } - mlog(0, "ocfs2_allocate_extent: couldn't do a simple add, traversing " - "tree now.\n"); + mlog(0, "Insert.appending: %u, Insert.Contig: %u, " + "Insert.contig_index: %d, Insert.free_records: %d, " + "Insert.tree_depth: %d\n", + insert.ins_appending, insert.ins_contig, insert.ins_contig_index, + insert.ins_free_records, insert.ins_tree_depth); + + /* + * Avoid growing the tree unless we're out of records and the + * insert type requres one. + */ + if (insert.ins_contig != CONTIG_NONE || insert.ins_free_records) + goto out_add; shift = ocfs2_find_branch_target(osb, inode, fe_bh, &bh); if (shift < 0) { @@ -866,13 +2382,9 @@ int ocfs2_insert_extent(struct ocfs2_sup * and didn't find room for any more extents - we need to add * another tree level */ if (shift) { - /* if we hit a leaf, we'd better be empty :) */ - BUG_ON(le16_to_cpu(el->l_next_free_rec) != - le16_to_cpu(el->l_count)); BUG_ON(bh); - mlog(0, "ocfs2_allocate_extent: need to shift tree depth " - "(current = %u)\n", - le16_to_cpu(fe->id2.i_list.l_tree_depth)); + mlog(0, "need to shift tree depth " + "(current = %d)\n", insert.ins_tree_depth); /* ocfs2_shift_tree_depth will return us a buffer with * the new extent block (so we can pass that to @@ -883,15 +2395,16 @@ int ocfs2_insert_extent(struct ocfs2_sup mlog_errno(status); goto bail; } + insert.ins_tree_depth++; /* Special case: we have room now if we shifted from * tree_depth 0 */ - if (fe->id2.i_list.l_tree_depth == cpu_to_le16(1)) + if (insert.ins_tree_depth == 1) goto out_add; } /* call ocfs2_add_branch to add the final part of the tree with * the new data. */ - mlog(0, "ocfs2_allocate_extent: add branch. bh = %p\n", bh); + mlog(0, "add branch. bh = %p\n", bh); status = ocfs2_add_branch(osb, handle, inode, fe_bh, bh, last_eb_bh, meta_ac); if (status < 0) { @@ -900,11 +2413,12 @@ int ocfs2_insert_extent(struct ocfs2_sup } out_add: - /* Finally, we can add clusters. */ - status = ocfs2_do_insert_extent(osb, handle, inode, fe_bh, - start_blk, new_clusters); + /* Finally, we can add clusters. This might rotate the tree for us. */ + status = ocfs2_do_insert_extent(inode, handle, fe_bh, &rec, &insert); if (status < 0) mlog_errno(status); + else + ocfs2_extent_map_insert_rec(inode, &rec); bail: if (bh) @@ -1447,168 +2961,389 @@ int ocfs2_truncate_log_init(struct ocfs2 * block will be deleted, and if it will, what the new last extent * block will be so we can update his h_next_leaf_blk field, as well * as the dinodes i_last_eb_blk */ -static int ocfs2_find_new_last_ext_blk(struct ocfs2_super *osb, - struct inode *inode, - struct ocfs2_dinode *fe, - u32 new_i_clusters, - struct buffer_head *old_last_eb, +static int ocfs2_find_new_last_ext_blk(struct inode *inode, + unsigned int clusters_to_del, + struct ocfs2_path *path, struct buffer_head **new_last_eb) { - int i, status = 0; - u64 block = 0; + int next_free, ret = 0; + u32 cpos; + struct ocfs2_extent_rec *rec; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; struct buffer_head *bh = NULL; *new_last_eb = NULL; - if (!OCFS2_IS_VALID_DINODE(fe)) { - OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, fe); - status = -EIO; - goto bail; - } - /* we have no tree, so of course, no last_eb. */ - if (!fe->id2.i_list.l_tree_depth) - goto bail; + if (!path->p_tree_depth) + goto out; /* trunc to zero special case - this makes tree_depth = 0 * regardless of what it is. */ - if (!new_i_clusters) - goto bail; + if (OCFS2_I(inode)->ip_clusters == clusters_to_del) + goto out; - eb = (struct ocfs2_extent_block *) old_last_eb->b_data; - el = &(eb->h_list); + el = path_leaf_el(path); BUG_ON(!el->l_next_free_rec); - /* Make sure that this guy will actually be empty after we - * clear away the data. */ - if (le32_to_cpu(el->l_recs[0].e_cpos) < new_i_clusters) - goto bail; + /* + * Make sure that this extent list will actually be empty + * after we clear away the data. We can shortcut out if + * there's more than one non-empty extent in the + * list. Otherwise, a check of the remaining extent is + * necessary. + */ + next_free = le16_to_cpu(el->l_next_free_rec); + rec = NULL; + if (ocfs2_is_empty_extent(&el->l_recs[0])) { + if (next_free > 2) + goto out; - /* Ok, at this point, we know that last_eb will definitely - * change, so lets traverse the tree and find the second to - * last extent block. */ - el = &(fe->id2.i_list); - /* go down the tree, */ - do { - for(i = (le16_to_cpu(el->l_next_free_rec) - 1); i >= 0; i--) { - if (le32_to_cpu(el->l_recs[i].e_cpos) < - new_i_clusters) { - block = le64_to_cpu(el->l_recs[i].e_blkno); - break; - } + /* We may have a valid extent in index 1, check it. */ + if (next_free == 2) + rec = &el->l_recs[1]; + + /* + * Fall through - no more nonempty extents, so we want + * to delete this leaf. + */ + } else { + if (next_free > 1) + goto out; + + rec = &el->l_recs[0]; + } + + if (rec) { + /* + * Check it we'll only be trimming off the end of this + * cluster. + */ + if (le16_to_cpu(rec->e_leaf_clusters) > clusters_to_del) + goto out; + } + + ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, path, &cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_leaf(inode, path_root_el(path), cpos, &bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + eb = (struct ocfs2_extent_block *) bh->b_data; + el = &eb->h_list; + if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { + OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); + ret = -EROFS; + goto out; + } + + *new_last_eb = bh; + get_bh(*new_last_eb); + mlog(0, "returning block %llu, (cpos: %u)\n", + (unsigned long long)le64_to_cpu(eb->h_blkno), cpos); +out: + brelse(bh); + + return ret; +} + +/* + * Trim some clusters off the rightmost edge of a tree. Only called + * during truncate. + * + * The caller needs to: + * - start journaling of each path component. + * - compute and fully set up any new last ext block + */ +static int ocfs2_trim_tree(struct inode *inode, struct ocfs2_path *path, + handle_t *handle, struct ocfs2_truncate_context *tc, + u32 clusters_to_del, u64 *delete_start) +{ + int ret, i, index = path->p_tree_depth; + u32 new_edge = 0; + u64 deleted_eb = 0; + struct buffer_head *bh; + struct ocfs2_extent_list *el; + struct ocfs2_extent_rec *rec; + + *delete_start = 0; + + while (index >= 0) { + bh = path->p_node[index].bh; + el = path->p_node[index].el; + + mlog(0, "traveling tree (index = %d, block = %llu)\n", + index, (unsigned long long)bh->b_blocknr); + + BUG_ON(le16_to_cpu(el->l_next_free_rec) == 0); + + if (index != + (path->p_tree_depth - le16_to_cpu(el->l_tree_depth))) { + ocfs2_error(inode->i_sb, + "Inode %lu has invalid ext. block %llu", + inode->i_ino, + (unsigned long long)bh->b_blocknr); + ret = -EROFS; + goto out; } - BUG_ON(i < 0); - if (bh) { - brelse(bh); - bh = NULL; +find_tail_record: + i = le16_to_cpu(el->l_next_free_rec) - 1; + rec = &el->l_recs[i]; + + mlog(0, "Extent list before: record %d: (%u, %u, %llu), " + "next = %u\n", i, le32_to_cpu(rec->e_cpos), + ocfs2_rec_clusters(el, rec), + (unsigned long long)le64_to_cpu(rec->e_blkno), + le16_to_cpu(el->l_next_free_rec)); + + BUG_ON(ocfs2_rec_clusters(el, rec) < clusters_to_del); + + if (le16_to_cpu(el->l_tree_depth) == 0) { + /* + * If the leaf block contains a single empty + * extent and no records, we can just remove + * the block. + */ + if (i == 0 && ocfs2_is_empty_extent(rec)) { + memset(rec, 0, + sizeof(struct ocfs2_extent_rec)); + el->l_next_free_rec = cpu_to_le16(0); + + goto delete; + } + + /* + * Remove any empty extents by shifting things + * left. That should make life much easier on + * the code below. This condition is rare + * enough that we shouldn't see a performance + * hit. + */ + if (ocfs2_is_empty_extent(&el->l_recs[0])) { + le16_add_cpu(&el->l_next_free_rec, -1); + + for(i = 0; + i < le16_to_cpu(el->l_next_free_rec); i++) + el->l_recs[i] = el->l_recs[i + 1]; + + memset(&el->l_recs[i], 0, + sizeof(struct ocfs2_extent_rec)); + + /* + * We've modified our extent list. The + * simplest way to handle this change + * is to being the search from the + * start again. + */ + goto find_tail_record; + } + + le16_add_cpu(&rec->e_leaf_clusters, -clusters_to_del); + + /* + * We'll use "new_edge" on our way back up the + * tree to know what our rightmost cpos is. + */ + new_edge = le16_to_cpu(rec->e_leaf_clusters); + new_edge += le32_to_cpu(rec->e_cpos); + + /* + * The caller will use this to delete data blocks. + */ + *delete_start = le64_to_cpu(rec->e_blkno) + + ocfs2_clusters_to_blocks(inode->i_sb, + le16_to_cpu(rec->e_leaf_clusters)); + + /* + * If it's now empty, remove this record. + */ + if (le16_to_cpu(rec->e_leaf_clusters) == 0) { + memset(rec, 0, + sizeof(struct ocfs2_extent_rec)); + le16_add_cpu(&el->l_next_free_rec, -1); + } + } else { + if (le64_to_cpu(rec->e_blkno) == deleted_eb) { + memset(rec, 0, + sizeof(struct ocfs2_extent_rec)); + le16_add_cpu(&el->l_next_free_rec, -1); + + goto delete; + } + + /* Can this actually happen? */ + if (le16_to_cpu(el->l_next_free_rec) == 0) + goto delete; + + /* + * We never actually deleted any clusters + * because our leaf was empty. There's no + * reason to adjust the rightmost edge then. + */ + if (new_edge == 0) + goto delete; + + rec->e_int_clusters = cpu_to_le32(new_edge); + le32_add_cpu(&rec->e_int_clusters, + -le32_to_cpu(rec->e_cpos)); + + /* + * A deleted child record should have been + * caught above. + */ + BUG_ON(le32_to_cpu(rec->e_int_clusters) == 0); } - status = ocfs2_read_block(osb, block, &bh, OCFS2_BH_CACHED, - inode); - if (status < 0) { - mlog_errno(status); - goto bail; +delete: + ret = ocfs2_journal_dirty(handle, bh); + if (ret) { + mlog_errno(ret); + goto out; } - eb = (struct ocfs2_extent_block *) bh->b_data; - el = &eb->h_list; - if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { - OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); - status = -EIO; - goto bail; + + mlog(0, "extent list container %llu, after: record %d: " + "(%u, %u, %llu), next = %u.\n", + (unsigned long long)bh->b_blocknr, i, + le32_to_cpu(rec->e_cpos), ocfs2_rec_clusters(el, rec), + (unsigned long long)le64_to_cpu(rec->e_blkno), + le16_to_cpu(el->l_next_free_rec)); + + /* + * We must be careful to only attempt delete of an + * extent block (and not the root inode block). + */ + if (index > 0 && le16_to_cpu(el->l_next_free_rec) == 0) { + struct ocfs2_extent_block *eb = + (struct ocfs2_extent_block *)bh->b_data; + + /* + * Save this for use when processing the + * parent block. + */ + deleted_eb = le64_to_cpu(eb->h_blkno); + + mlog(0, "deleting this extent block.\n"); + + ocfs2_remove_from_cache(inode, bh); + + BUG_ON(ocfs2_rec_clusters(el, &el->l_recs[0])); + BUG_ON(le32_to_cpu(el->l_recs[0].e_cpos)); + BUG_ON(le64_to_cpu(el->l_recs[0].e_blkno)); + + if (le16_to_cpu(eb->h_suballoc_slot) == 0) { + /* + * This code only understands how to + * lock the suballocator in slot 0, + * which is fine because allocation is + * only ever done out of that + * suballocator too. A future version + * might change that however, so avoid + * a free if we don't know how to + * handle it. This way an fs incompat + * bit will not be necessary. + */ + ret = ocfs2_free_extent_block(handle, + tc->tc_ext_alloc_inode, + tc->tc_ext_alloc_bh, + eb); + + /* An error here is not fatal. */ + if (ret < 0) + mlog_errno(ret); + } + } else { + deleted_eb = 0; } - } while (el->l_tree_depth); - *new_last_eb = bh; - get_bh(*new_last_eb); - mlog(0, "returning block %llu\n", - (unsigned long long)le64_to_cpu(eb->h_blkno)); -bail: - if (bh) - brelse(bh); + index--; + } - return status; + ret = 0; +out: + return ret; } static int ocfs2_do_truncate(struct ocfs2_super *osb, unsigned int clusters_to_del, struct inode *inode, struct buffer_head *fe_bh, - struct buffer_head *old_last_eb_bh, handle_t *handle, - struct ocfs2_truncate_context *tc) + struct ocfs2_truncate_context *tc, + struct ocfs2_path *path) { - int status, i, depth; + int status; struct ocfs2_dinode *fe; - struct ocfs2_extent_block *eb; struct ocfs2_extent_block *last_eb = NULL; struct ocfs2_extent_list *el; - struct buffer_head *eb_bh = NULL; struct buffer_head *last_eb_bh = NULL; - u64 next_eb = 0; u64 delete_blk = 0; fe = (struct ocfs2_dinode *) fe_bh->b_data; - status = ocfs2_find_new_last_ext_blk(osb, - inode, - fe, - le32_to_cpu(fe->i_clusters) - - clusters_to_del, - old_last_eb_bh, - &last_eb_bh); + status = ocfs2_find_new_last_ext_blk(inode, clusters_to_del, + path, &last_eb_bh); if (status < 0) { mlog_errno(status); goto bail; } - if (last_eb_bh) - last_eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; - status = ocfs2_journal_access(handle, inode, fe_bh, - OCFS2_JOURNAL_ACCESS_WRITE); + /* + * Each component will be touched, so we might as well journal + * here to avoid having to handle errors later. + */ + status = ocfs2_journal_access_path(inode, handle, path); if (status < 0) { mlog_errno(status); goto bail; } + + if (last_eb_bh) { + status = ocfs2_journal_access(handle, inode, last_eb_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + goto bail; + } + + last_eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; + } + el = &(fe->id2.i_list); + /* + * Lower levels depend on this never happening, but it's best + * to check it up here before changing the tree. + */ + if (el->l_tree_depth && el->l_recs[0].e_int_clusters == 0) { + ocfs2_error(inode->i_sb, + "Inode %lu has an empty extent record, depth %u\n", + inode->i_ino, le16_to_cpu(el->l_tree_depth)); + status = -EROFS; + goto bail; + } + spin_lock(&OCFS2_I(inode)->ip_lock); OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters) - clusters_to_del; spin_unlock(&OCFS2_I(inode)->ip_lock); le32_add_cpu(&fe->i_clusters, -clusters_to_del); - fe->i_mtime = cpu_to_le64(CURRENT_TIME.tv_sec); - fe->i_mtime_nsec = cpu_to_le32(CURRENT_TIME.tv_nsec); - - i = le16_to_cpu(el->l_next_free_rec) - 1; - - BUG_ON(le32_to_cpu(el->l_recs[i].e_clusters) < clusters_to_del); - le32_add_cpu(&el->l_recs[i].e_clusters, -clusters_to_del); - /* tree depth zero, we can just delete the clusters, otherwise - * we need to record the offset of the next level extent block - * as we may overwrite it. */ - if (!el->l_tree_depth) - delete_blk = le64_to_cpu(el->l_recs[i].e_blkno) - + ocfs2_clusters_to_blocks(osb->sb, - le32_to_cpu(el->l_recs[i].e_clusters)); - else - next_eb = le64_to_cpu(el->l_recs[i].e_blkno); - if (!el->l_recs[i].e_clusters) { - /* if we deleted the whole extent record, then clear - * out the other fields and update the extent - * list. For depth > 0 trees, we've already recorded - * the extent block in 'next_eb' */ - el->l_recs[i].e_cpos = 0; - el->l_recs[i].e_blkno = 0; - BUG_ON(!el->l_next_free_rec); - le16_add_cpu(&el->l_next_free_rec, -1); + status = ocfs2_trim_tree(inode, path, handle, tc, + clusters_to_del, &delete_blk); + if (status) { + mlog_errno(status); + goto bail; } - depth = le16_to_cpu(el->l_tree_depth); - if (!fe->i_clusters) { + if (le32_to_cpu(fe->i_clusters) == 0) { /* trunc to zero is a special case. */ el->l_tree_depth = 0; fe->i_last_eb_blk = 0; @@ -1625,12 +3360,6 @@ static int ocfs2_do_truncate(struct ocfs /* If there will be a new last extent block, then by * definition, there cannot be any leaves to the right of * him. */ - status = ocfs2_journal_access(handle, inode, last_eb_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto bail; - } last_eb->h_next_leaf_blk = 0; status = ocfs2_journal_dirty(handle, last_eb_bh); if (status < 0) { @@ -1639,123 +3368,247 @@ static int ocfs2_do_truncate(struct ocfs } } - /* if our tree depth > 0, update all the tree blocks below us. */ - while (depth) { - mlog(0, "traveling tree (depth = %d, next_eb = %llu)\n", - depth, (unsigned long long)next_eb); - status = ocfs2_read_block(osb, next_eb, &eb_bh, - OCFS2_BH_CACHED, inode); + if (delete_blk) { + status = ocfs2_truncate_log_append(osb, handle, delete_blk, + clusters_to_del); if (status < 0) { mlog_errno(status); goto bail; } - eb = (struct ocfs2_extent_block *)eb_bh->b_data; - if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { - OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); - status = -EIO; - goto bail; + } + status = 0; +bail: + + mlog_exit(status); + return status; +} + +static int ocfs2_writeback_zero_func(handle_t *handle, struct buffer_head *bh) +{ + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + return 0; +} + +static int ocfs2_ordered_zero_func(handle_t *handle, struct buffer_head *bh) +{ + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + return ocfs2_journal_dirty_data(handle, bh); +} + +static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t isize, + struct page **pages, int numpages, + u64 phys, handle_t *handle) +{ + int i, ret, partial = 0; + void *kaddr; + struct page *page; + unsigned int from, to = PAGE_CACHE_SIZE; + struct super_block *sb = inode->i_sb; + + BUG_ON(!ocfs2_sparse_alloc(OCFS2_SB(sb))); + + if (numpages == 0) + goto out; + + from = isize & (PAGE_CACHE_SIZE - 1); /* 1st page offset */ + if (PAGE_CACHE_SHIFT > OCFS2_SB(sb)->s_clustersize_bits) { + /* + * Since 'from' has been capped to a value below page + * size, this calculation won't be able to overflow + * 'to' + */ + to = ocfs2_align_bytes_to_clusters(sb, from); + + /* + * The truncate tail in this case should never contain + * more than one page at maximum. The loop below also + * assumes this. + */ + BUG_ON(numpages != 1); + } + + for(i = 0; i < numpages; i++) { + page = pages[i]; + + BUG_ON(from > PAGE_CACHE_SIZE); + BUG_ON(to > PAGE_CACHE_SIZE); + + ret = ocfs2_map_page_blocks(page, &phys, inode, from, to, 0); + if (ret) + mlog_errno(ret); + + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + from, 0, to - from); + kunmap_atomic(kaddr, KM_USER0); + + /* + * Need to set the buffers we zero'd into uptodate + * here if they aren't - ocfs2_map_page_blocks() + * might've skipped some + */ + if (ocfs2_should_order_data(inode)) { + ret = walk_page_buffers(handle, + page_buffers(page), + from, to, &partial, + ocfs2_ordered_zero_func); + if (ret < 0) + mlog_errno(ret); + } else { + ret = walk_page_buffers(handle, page_buffers(page), + from, to, &partial, + ocfs2_writeback_zero_func); + if (ret < 0) + mlog_errno(ret); } - el = &(eb->h_list); - status = ocfs2_journal_access(handle, inode, eb_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto bail; + if (!partial) + SetPageUptodate(page); + + flush_dcache_page(page); + + /* + * Every page after the 1st one should be completely zero'd. + */ + from = 0; + } +out: + if (pages) { + for (i = 0; i < numpages; i++) { + page = pages[i]; + unlock_page(page); + mark_page_accessed(page); + page_cache_release(page); } + } +} - BUG_ON(le16_to_cpu(el->l_next_free_rec) == 0); - BUG_ON(depth != (le16_to_cpu(el->l_tree_depth) + 1)); +static int ocfs2_grab_eof_pages(struct inode *inode, loff_t isize, struct page **pages, + int *num, u64 *phys) +{ + int i, numpages = 0, ret = 0; + unsigned int csize = OCFS2_SB(inode->i_sb)->s_clustersize; + unsigned int ext_flags; + struct super_block *sb = inode->i_sb; + struct address_space *mapping = inode->i_mapping; + unsigned long index; + u64 next_cluster_bytes; + + BUG_ON(!ocfs2_sparse_alloc(OCFS2_SB(sb))); + + /* Cluster boundary, so we don't need to grab any pages. */ + if ((isize & (csize - 1)) == 0) + goto out; - i = le16_to_cpu(el->l_next_free_rec) - 1; + ret = ocfs2_extent_map_get_blocks(inode, isize >> sb->s_blocksize_bits, + phys, NULL, &ext_flags); + if (ret) { + mlog_errno(ret); + goto out; + } - mlog(0, "extent block %llu, before: record %d: " - "(%u, %u, %llu), next = %u\n", - (unsigned long long)le64_to_cpu(eb->h_blkno), i, - le32_to_cpu(el->l_recs[i].e_cpos), - le32_to_cpu(el->l_recs[i].e_clusters), - (unsigned long long)le64_to_cpu(el->l_recs[i].e_blkno), - le16_to_cpu(el->l_next_free_rec)); + /* Tail is a hole. */ + if (*phys == 0) + goto out; - BUG_ON(le32_to_cpu(el->l_recs[i].e_clusters) < clusters_to_del); - le32_add_cpu(&el->l_recs[i].e_clusters, -clusters_to_del); - - next_eb = le64_to_cpu(el->l_recs[i].e_blkno); - /* bottom-most block requires us to delete data.*/ - if (!el->l_tree_depth) - delete_blk = le64_to_cpu(el->l_recs[i].e_blkno) - + ocfs2_clusters_to_blocks(osb->sb, - le32_to_cpu(el->l_recs[i].e_clusters)); - if (!el->l_recs[i].e_clusters) { - el->l_recs[i].e_cpos = 0; - el->l_recs[i].e_blkno = 0; - BUG_ON(!el->l_next_free_rec); - le16_add_cpu(&el->l_next_free_rec, -1); - } - mlog(0, "extent block %llu, after: record %d: " - "(%u, %u, %llu), next = %u\n", - (unsigned long long)le64_to_cpu(eb->h_blkno), i, - le32_to_cpu(el->l_recs[i].e_cpos), - le32_to_cpu(el->l_recs[i].e_clusters), - (unsigned long long)le64_to_cpu(el->l_recs[i].e_blkno), - le16_to_cpu(el->l_next_free_rec)); + /* Tail is marked as unwritten, we can count on write to zero + * in that case. */ + if (ext_flags & OCFS2_EXT_UNWRITTEN) + goto out; - status = ocfs2_journal_dirty(handle, eb_bh); - if (status < 0) { - mlog_errno(status); - goto bail; + next_cluster_bytes = ocfs2_align_bytes_to_clusters(inode->i_sb, isize); + index = isize >> PAGE_CACHE_SHIFT; + do { + pages[numpages] = grab_cache_page(mapping, index); + if (!pages[numpages]) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; } - if (!el->l_next_free_rec) { - mlog(0, "deleting this extent block.\n"); - - ocfs2_remove_from_cache(inode, eb_bh); + numpages++; + index++; + } while (index < (next_cluster_bytes >> PAGE_CACHE_SHIFT)); - BUG_ON(el->l_recs[0].e_clusters); - BUG_ON(el->l_recs[0].e_cpos); - BUG_ON(el->l_recs[0].e_blkno); - if (eb->h_suballoc_slot == 0) { - /* - * This code only understands how to - * lock the suballocator in slot 0, - * which is fine because allocation is - * only ever done out of that - * suballocator too. A future version - * might change that however, so avoid - * a free if we don't know how to - * handle it. This way an fs incompat - * bit will not be necessary. - */ - status = ocfs2_free_extent_block(handle, - tc->tc_ext_alloc_inode, - tc->tc_ext_alloc_bh, - eb); - if (status < 0) { - mlog_errno(status); - goto bail; +out: + if (ret != 0) { + if (pages) { + for (i = 0; i < numpages; i++) { + if (pages[i]) { + unlock_page(pages[i]); + page_cache_release(pages[i]); } } } - brelse(eb_bh); - eb_bh = NULL; - depth--; + numpages = 0; } - BUG_ON(!delete_blk); - status = ocfs2_truncate_log_append(osb, handle, delete_blk, - clusters_to_del); - if (status < 0) { - mlog_errno(status); - goto bail; + *num = numpages; + + return ret; +} + +/* + * Zero the area past i_size but still within an allocated + * cluster. This avoids exposing nonzero data on subsequent file + * extends. + * + * We need to call this before i_size is updated on the inode because + * otherwise block_write_full_page() will skip writeout of pages past + * i_size. The new_i_size parameter is passed for this reason. + */ +int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle, + u64 new_i_size) +{ + int ret, numpages; + loff_t endbyte; + struct page **pages = NULL; + u64 phys; + + /* + * File systems which don't support sparse files zero on every + * extend. + */ + if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + return 0; + + pages = kcalloc(ocfs2_pages_per_cluster(inode->i_sb), + sizeof(struct page *), GFP_NOFS); + if (pages == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; } - status = 0; -bail: - if (!status) - ocfs2_extent_map_trunc(inode, le32_to_cpu(fe->i_clusters)); - else - ocfs2_extent_map_drop(inode, 0); - mlog_exit(status); - return status; + + ret = ocfs2_grab_eof_pages(inode, new_i_size, pages, &numpages, &phys); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (numpages == 0) + goto out; + + ocfs2_zero_cluster_pages(inode, new_i_size, pages, numpages, phys, + handle); + + /* + * Initiate writeout of the pages we zero'd here. We don't + * wait on them - the truncate_inode_pages() call later will + * do that for us. + */ + endbyte = ocfs2_align_bytes_to_clusters(inode->i_sb, new_i_size); + ret = do_sync_mapping_range(inode->i_mapping, new_i_size, + endbyte - 1, SYNC_FILE_RANGE_WRITE); + if (ret) + mlog_errno(ret); + +out: + if (pages) + kfree(pages); + + return ret; } /* @@ -1770,82 +3623,90 @@ int ocfs2_commit_truncate(struct ocfs2_s struct ocfs2_truncate_context *tc) { int status, i, credits, tl_sem = 0; - u32 clusters_to_del, target_i_clusters; - u64 last_eb = 0; - struct ocfs2_dinode *fe; - struct ocfs2_extent_block *eb; + u32 clusters_to_del, new_highest_cpos, range; struct ocfs2_extent_list *el; - struct buffer_head *last_eb_bh; handle_t *handle = NULL; struct inode *tl_inode = osb->osb_tl_inode; + struct ocfs2_path *path = NULL; mlog_entry_void(); down_write(&OCFS2_I(inode)->ip_alloc_sem); - target_i_clusters = ocfs2_clusters_for_bytes(osb->sb, + new_highest_cpos = ocfs2_clusters_for_bytes(osb->sb, i_size_read(inode)); - last_eb_bh = tc->tc_last_eb_bh; - tc->tc_last_eb_bh = NULL; + path = ocfs2_new_inode_path(fe_bh); + if (!path) { + status = -ENOMEM; + mlog_errno(status); + goto bail; + } - fe = (struct ocfs2_dinode *) fe_bh->b_data; + ocfs2_extent_map_trunc(inode, new_highest_cpos); - if (fe->id2.i_list.l_tree_depth) { - eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; - el = &eb->h_list; - } else - el = &fe->id2.i_list; - last_eb = le64_to_cpu(fe->i_last_eb_blk); start: - mlog(0, "ocfs2_commit_truncate: fe->i_clusters = %u, " - "last_eb = %llu, fe->i_last_eb_blk = %llu, " - "fe->id2.i_list.l_tree_depth = %u last_eb_bh = %p\n", - le32_to_cpu(fe->i_clusters), (unsigned long long)last_eb, - (unsigned long long)le64_to_cpu(fe->i_last_eb_blk), - le16_to_cpu(fe->id2.i_list.l_tree_depth), last_eb_bh); - - if (last_eb != le64_to_cpu(fe->i_last_eb_blk)) { - mlog(0, "last_eb changed!\n"); - BUG_ON(!fe->id2.i_list.l_tree_depth); - last_eb = le64_to_cpu(fe->i_last_eb_blk); - /* i_last_eb_blk may have changed, read it if - * necessary. We don't have to worry about the - * truncate to zero case here (where there becomes no - * last_eb) because we never loop back after our work - * is done. */ - if (last_eb_bh) { - brelse(last_eb_bh); - last_eb_bh = NULL; - } + /* + * Check that we still have allocation to delete. + */ + if (OCFS2_I(inode)->ip_clusters == 0) { + status = 0; + goto bail; + } - status = ocfs2_read_block(osb, last_eb, - &last_eb_bh, OCFS2_BH_CACHED, - inode); - if (status < 0) { - mlog_errno(status); - goto bail; - } - eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; - if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { - OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); - status = -EIO; - goto bail; - } - el = &(eb->h_list); + /* + * Truncate always works against the rightmost tree branch. + */ + status = ocfs2_find_path(inode, path, UINT_MAX); + if (status) { + mlog_errno(status); + goto bail; + } + + mlog(0, "inode->ip_clusters = %u, tree_depth = %u\n", + OCFS2_I(inode)->ip_clusters, path->p_tree_depth); + + /* + * By now, el will point to the extent list on the bottom most + * portion of this tree. Only the tail record is considered in + * each pass. + * + * We handle the following cases, in order: + * - empty extent: delete the remaining branch + * - remove the entire record + * - remove a partial record + * - no record needs to be removed (truncate has completed) + */ + el = path_leaf_el(path); + if (le16_to_cpu(el->l_next_free_rec) == 0) { + ocfs2_error(inode->i_sb, + "Inode %llu has empty extent block at %llu\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)path_leaf_bh(path)->b_blocknr); + status = -EROFS; + goto bail; } - /* by now, el will point to the extent list on the bottom most - * portion of this tree. */ i = le16_to_cpu(el->l_next_free_rec) - 1; - if (le32_to_cpu(el->l_recs[i].e_cpos) >= target_i_clusters) - clusters_to_del = le32_to_cpu(el->l_recs[i].e_clusters); - else - clusters_to_del = (le32_to_cpu(el->l_recs[i].e_clusters) + + range = le32_to_cpu(el->l_recs[i].e_cpos) + + ocfs2_rec_clusters(el, &el->l_recs[i]); + if (i == 0 && ocfs2_is_empty_extent(&el->l_recs[i])) { + clusters_to_del = 0; + } else if (le32_to_cpu(el->l_recs[i].e_cpos) >= new_highest_cpos) { + clusters_to_del = ocfs2_rec_clusters(el, &el->l_recs[i]); + } else if (range > new_highest_cpos) { + clusters_to_del = (ocfs2_rec_clusters(el, &el->l_recs[i]) + le32_to_cpu(el->l_recs[i].e_cpos)) - - target_i_clusters; + new_highest_cpos; + } else { + status = 0; + goto bail; + } - mlog(0, "clusters_to_del = %u in this pass\n", clusters_to_del); + mlog(0, "clusters_to_del = %u in this pass, tail blk=%llu\n", + clusters_to_del, (unsigned long long)path_leaf_bh(path)->b_blocknr); + + BUG_ON(clusters_to_del == 0); mutex_lock(&tl_inode->i_mutex); tl_sem = 1; @@ -1861,7 +3722,8 @@ start: } credits = ocfs2_calc_tree_trunc_credits(osb->sb, clusters_to_del, - fe, el); + (struct ocfs2_dinode *)fe_bh->b_data, + el); handle = ocfs2_start_trans(osb, credits); if (IS_ERR(handle)) { status = PTR_ERR(handle); @@ -1870,13 +3732,8 @@ start: goto bail; } - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - status = ocfs2_mark_inode_dirty(handle, inode, fe_bh); - if (status < 0) - mlog_errno(status); - - status = ocfs2_do_truncate(osb, clusters_to_del, inode, fe_bh, - last_eb_bh, handle, tc); + status = ocfs2_do_truncate(osb, clusters_to_del, inode, fe_bh, handle, + tc, path); if (status < 0) { mlog_errno(status); goto bail; @@ -1888,9 +3745,14 @@ start: ocfs2_commit_trans(osb, handle); handle = NULL; - BUG_ON(le32_to_cpu(fe->i_clusters) < target_i_clusters); - if (le32_to_cpu(fe->i_clusters) > target_i_clusters) - goto start; + ocfs2_reinit_path(path, 1); + + /* + * The check above will catch the case where we've truncated + * away all allocation. + */ + goto start; + bail: up_write(&OCFS2_I(inode)->ip_alloc_sem); @@ -1902,8 +3764,7 @@ bail: if (handle) ocfs2_commit_trans(osb, handle); - if (last_eb_bh) - brelse(last_eb_bh); + ocfs2_free_path(path); /* This will drop the ext_alloc cluster lock for us */ ocfs2_free_truncate_context(tc); @@ -1912,7 +3773,6 @@ bail: return status; } - /* * Expects the inode to already be locked. This will figure out which * inodes need to be locked and will put them on the returned truncate @@ -1923,7 +3783,7 @@ int ocfs2_prepare_truncate(struct ocfs2_ struct buffer_head *fe_bh, struct ocfs2_truncate_context **tc) { - int status, metadata_delete; + int status, metadata_delete, i; unsigned int new_i_clusters; struct ocfs2_dinode *fe; struct ocfs2_extent_block *eb; @@ -1944,21 +3804,6 @@ int ocfs2_prepare_truncate(struct ocfs2_ "%llu\n", fe->i_clusters, new_i_clusters, (unsigned long long)fe->i_size); - if (le32_to_cpu(fe->i_clusters) <= new_i_clusters) { - ocfs2_error(inode->i_sb, "Dinode %llu has cluster count " - "%u and size %llu whereas struct inode has " - "cluster count %u and size %llu which caused an " - "invalid truncate to %u clusters.", - (unsigned long long)le64_to_cpu(fe->i_blkno), - le32_to_cpu(fe->i_clusters), - (unsigned long long)le64_to_cpu(fe->i_size), - OCFS2_I(inode)->ip_clusters, i_size_read(inode), - new_i_clusters); - mlog_meta_lvb(ML_ERROR, &OCFS2_I(inode)->ip_meta_lockres); - status = -EIO; - goto bail; - } - *tc = kzalloc(sizeof(struct ocfs2_truncate_context), GFP_KERNEL); if (!(*tc)) { status = -ENOMEM; @@ -1986,7 +3831,15 @@ int ocfs2_prepare_truncate(struct ocfs2_ goto bail; } el = &(eb->h_list); - if (le32_to_cpu(el->l_recs[0].e_cpos) >= new_i_clusters) + + i = 0; + if (ocfs2_is_empty_extent(&el->l_recs[0])) + i = 1; + /* + * XXX: Should we check that next_free_rec contains + * the extent? + */ + if (le32_to_cpu(el->l_recs[i].e_cpos) >= new_i_clusters) metadata_delete = 1; } diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h index 0b82e80..fbcb593 100644 --- a/fs/ocfs2/alloc.h +++ b/fs/ocfs2/alloc.h @@ -31,7 +31,8 @@ int ocfs2_insert_extent(struct ocfs2_sup handle_t *handle, struct inode *inode, struct buffer_head *fe_bh, - u64 blkno, + u32 cpos, + u64 start_blk, u32 new_clusters, struct ocfs2_alloc_context *meta_ac); int ocfs2_num_free_extents(struct ocfs2_super *osb, @@ -70,6 +71,8 @@ struct ocfs2_truncate_context { struct buffer_head *tc_last_eb_bh; }; +int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle, + u64 new_i_size); int ocfs2_prepare_truncate(struct ocfs2_super *osb, struct inode *inode, struct buffer_head *fe_bh, @@ -79,4 +82,26 @@ int ocfs2_commit_truncate(struct ocfs2_s struct buffer_head *fe_bh, struct ocfs2_truncate_context *tc); +int ocfs2_find_leaf(struct inode *inode, struct ocfs2_extent_list *root_el, + u32 cpos, struct buffer_head **leaf_bh); + +/* + * Helper function to look at the # of clusters in an extent record. + */ +static inline unsigned int ocfs2_rec_clusters(struct ocfs2_extent_list *el, + struct ocfs2_extent_rec *rec) +{ + /* + * Cluster count in extent records is slightly different + * between interior nodes and leaf nodes. This is to support + * unwritten extents which need a flags field in leaf node + * records, thus shrinking the available space for a clusters + * field. + */ + if (el->l_tree_depth) + return le32_to_cpu(rec->e_int_clusters); + else + return le16_to_cpu(rec->e_leaf_clusters); +} + #endif /* OCFS2_ALLOC_H */ diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 875c114..56963e6 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -24,6 +24,8 @@ #include #include #include #include +#include +#include #define MLOG_MASK_PREFIX ML_FILE_IO #include @@ -37,6 +39,7 @@ #include "extent_map.h" #include "file.h" #include "inode.h" #include "journal.h" +#include "suballoc.h" #include "super.h" #include "symlink.h" @@ -134,7 +137,9 @@ static int ocfs2_get_block(struct inode struct buffer_head *bh_result, int create) { int err = 0; + unsigned int ext_flags; u64 p_blkno, past_eof; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry("(0x%p, %llu, 0x%p, %d)\n", inode, (unsigned long long)iblock, bh_result, create); @@ -149,17 +154,8 @@ static int ocfs2_get_block(struct inode goto bail; } - /* this can happen if another node truncs after our extend! */ - spin_lock(&OCFS2_I(inode)->ip_lock); - if (iblock >= ocfs2_clusters_to_blocks(inode->i_sb, - OCFS2_I(inode)->ip_clusters)) - err = -EIO; - spin_unlock(&OCFS2_I(inode)->ip_lock); - if (err) - goto bail; - - err = ocfs2_extent_map_get_blocks(inode, iblock, 1, &p_blkno, - NULL); + err = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno, NULL, + &ext_flags); if (err) { mlog(ML_ERROR, "Error %d from get_blocks(0x%p, %llu, 1, " "%llu, NULL)\n", err, inode, (unsigned long long)iblock, @@ -167,22 +163,39 @@ static int ocfs2_get_block(struct inode goto bail; } - map_bh(bh_result, inode->i_sb, p_blkno); - - if (bh_result->b_blocknr == 0) { - err = -EIO; - mlog(ML_ERROR, "iblock = %llu p_blkno = %llu blkno=(%llu)\n", - (unsigned long long)iblock, - (unsigned long long)p_blkno, - (unsigned long long)OCFS2_I(inode)->ip_blkno); - } + /* + * ocfs2 never allocates in this function - the only time we + * need to use BH_New is when we're extending i_size on a file + * system which doesn't support holes, in which case BH_New + * allows block_prepare_write() to zero. + */ + mlog_bug_on_msg(create && p_blkno == 0 && ocfs2_sparse_alloc(osb), + "ino %lu, iblock %llu\n", inode->i_ino, + (unsigned long long)iblock); + + /* Treat the unwritten extent as a hole for zeroing purposes. */ + if (p_blkno && !(ext_flags & OCFS2_EXT_UNWRITTEN)) + map_bh(bh_result, inode->i_sb, p_blkno); + + if (!ocfs2_sparse_alloc(osb)) { + if (p_blkno == 0) { + err = -EIO; + mlog(ML_ERROR, + "iblock = %llu p_blkno = %llu blkno=(%llu)\n", + (unsigned long long)iblock, + (unsigned long long)p_blkno, + (unsigned long long)OCFS2_I(inode)->ip_blkno); + mlog(ML_ERROR, "Size %llu, clusters %u\n", (unsigned long long)i_size_read(inode), OCFS2_I(inode)->ip_clusters); + dump_stack(); + } - past_eof = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode)); - mlog(0, "Inode %lu, past_eof = %llu\n", inode->i_ino, - (unsigned long long)past_eof); + past_eof = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode)); + mlog(0, "Inode %lu, past_eof = %llu\n", inode->i_ino, + (unsigned long long)past_eof); - if (create && (iblock >= past_eof)) - set_buffer_new(bh_result); + if (create && (iblock >= past_eof)) + set_buffer_new(bh_result); + } bail: if (err < 0) @@ -276,8 +289,11 @@ static int ocfs2_writepage(struct page * return ret; } -/* This can also be called from ocfs2_write_zero_page() which has done - * it's own cluster locking. */ +/* + * This is called from ocfs2_write_zero_page() which has handled it's + * own cluster locking and has ensured allocation exists for those + * blocks to be written. + */ int ocfs2_prepare_write_nolock(struct inode *inode, struct page *page, unsigned from, unsigned to) { @@ -292,44 +308,17 @@ int ocfs2_prepare_write_nolock(struct in return ret; } -/* - * ocfs2_prepare_write() can be an outer-most ocfs2 call when it is called - * from loopback. It must be able to perform its own locking around - * ocfs2_get_block(). - */ -static int ocfs2_prepare_write(struct file *file, struct page *page, - unsigned from, unsigned to) -{ - struct inode *inode = page->mapping->host; - int ret; - - mlog_entry("(0x%p, 0x%p, %u, %u)\n", file, page, from, to); - - ret = ocfs2_meta_lock_with_page(inode, NULL, 0, page); - if (ret != 0) { - mlog_errno(ret); - goto out; - } - - ret = ocfs2_prepare_write_nolock(inode, page, from, to); - - ocfs2_meta_unlock(inode, 0); -out: - mlog_exit(ret); - return ret; -} - /* Taken from ext3. We don't necessarily need the full blown * functionality yet, but IMHO it's better to cut and paste the whole * thing so we can avoid introducing our own bugs (and easily pick up * their fixes when they happen) --Mark */ -static int walk_page_buffers( handle_t *handle, - struct buffer_head *head, - unsigned from, - unsigned to, - int *partial, - int (*fn)( handle_t *handle, - struct buffer_head *bh)) +int walk_page_buffers( handle_t *handle, + struct buffer_head *head, + unsigned from, + unsigned to, + int *partial, + int (*fn)( handle_t *handle, + struct buffer_head *bh)) { struct buffer_head *bh; unsigned block_start, block_end; @@ -388,95 +377,6 @@ out: return handle; } -static int ocfs2_commit_write(struct file *file, struct page *page, - unsigned from, unsigned to) -{ - int ret; - struct buffer_head *di_bh = NULL; - struct inode *inode = page->mapping->host; - handle_t *handle = NULL; - struct ocfs2_dinode *di; - - mlog_entry("(0x%p, 0x%p, %u, %u)\n", file, page, from, to); - - /* NOTE: ocfs2_file_aio_write has ensured that it's safe for - * us to continue here without rechecking the I/O against - * changed inode values. - * - * 1) We're currently holding the inode alloc lock, so no - * nodes can change it underneath us. - * - * 2) We've had to take the metadata lock at least once - * already to check for extending writes, suid removal, etc. - * The meta data update code then ensures that we don't get a - * stale inode allocation image (i_size, i_clusters, etc). - */ - - ret = ocfs2_meta_lock_with_page(inode, &di_bh, 1, page); - if (ret != 0) { - mlog_errno(ret); - goto out; - } - - ret = ocfs2_data_lock_with_page(inode, 1, page); - if (ret != 0) { - mlog_errno(ret); - goto out_unlock_meta; - } - - handle = ocfs2_start_walk_page_trans(inode, page, from, to); - if (IS_ERR(handle)) { - ret = PTR_ERR(handle); - goto out_unlock_data; - } - - /* Mark our buffer early. We'd rather catch this error up here - * as opposed to after a successful commit_write which would - * require us to set back inode->i_size. */ - ret = ocfs2_journal_access(handle, inode, di_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (ret < 0) { - mlog_errno(ret); - goto out_commit; - } - - /* might update i_size */ - ret = generic_commit_write(file, page, from, to); - if (ret < 0) { - mlog_errno(ret); - goto out_commit; - } - - di = (struct ocfs2_dinode *)di_bh->b_data; - - /* ocfs2_mark_inode_dirty() is too heavy to use here. */ - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec); - di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); - - inode->i_blocks = ocfs2_align_bytes_to_sectors((u64)(i_size_read(inode))); - di->i_size = cpu_to_le64((u64)i_size_read(inode)); - - ret = ocfs2_journal_dirty(handle, di_bh); - if (ret < 0) { - mlog_errno(ret); - goto out_commit; - } - -out_commit: - ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); -out_unlock_data: - ocfs2_data_unlock(inode, 1); -out_unlock_meta: - ocfs2_meta_unlock(inode, 1); -out: - if (di_bh) - brelse(di_bh); - - mlog_exit(ret); - return ret; -} - static sector_t ocfs2_bmap(struct address_space *mapping, sector_t block) { sector_t status; @@ -499,8 +399,7 @@ static sector_t ocfs2_bmap(struct addres down_read(&OCFS2_I(inode)->ip_alloc_sem); } - err = ocfs2_extent_map_get_blocks(inode, block, 1, &p_blkno, - NULL); + err = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, NULL); if (!INODE_JOURNAL(inode)) { up_read(&OCFS2_I(inode)->ip_alloc_sem); @@ -540,8 +439,8 @@ static int ocfs2_direct_IO_get_blocks(st struct buffer_head *bh_result, int create) { int ret; - u64 p_blkno, inode_blocks; - int contig_blocks; + u64 p_blkno, inode_blocks, contig_blocks; + unsigned int ext_flags; unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; @@ -549,33 +448,20 @@ static int ocfs2_direct_IO_get_blocks(st * nicely aligned and of the right size, so there's no need * for us to check any of that. */ - spin_lock(&OCFS2_I(inode)->ip_lock); - inode_blocks = ocfs2_clusters_to_blocks(inode->i_sb, - OCFS2_I(inode)->ip_clusters); - - /* - * For a read which begins past the end of file, we return a hole. - */ - if (!create && (iblock >= inode_blocks)) { - spin_unlock(&OCFS2_I(inode)->ip_lock); - ret = 0; - goto bail; - } + inode_blocks = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode)); /* * Any write past EOF is not allowed because we'd be extending. */ if (create && (iblock + max_blocks) > inode_blocks) { - spin_unlock(&OCFS2_I(inode)->ip_lock); ret = -EIO; goto bail; } - spin_unlock(&OCFS2_I(inode)->ip_lock); /* This figures out the size of the next contiguous block, and * our logical offset */ - ret = ocfs2_extent_map_get_blocks(inode, iblock, 1, &p_blkno, - &contig_blocks); + ret = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno, + &contig_blocks, &ext_flags); if (ret) { mlog(ML_ERROR, "get_blocks() failed iblock=%llu\n", (unsigned long long)iblock); @@ -583,7 +469,37 @@ static int ocfs2_direct_IO_get_blocks(st goto bail; } - map_bh(bh_result, inode->i_sb, p_blkno); + if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)) && !p_blkno) { + ocfs2_error(inode->i_sb, + "Inode %llu has a hole at block %llu\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)iblock); + ret = -EROFS; + goto bail; + } + + /* + * get_more_blocks() expects us to describe a hole by clearing + * the mapped bit on bh_result(). + * + * Consider an unwritten extent as a hole. + */ + if (p_blkno && !(ext_flags & OCFS2_EXT_UNWRITTEN)) + map_bh(bh_result, inode->i_sb, p_blkno); + else { + /* + * ocfs2_prepare_inode_for_write() should have caught + * the case where we'd be filling a hole and triggered + * a buffered write instead. + */ + if (create) { + ret = -EIO; + mlog_errno(ret); + goto bail; + } + + clear_buffer_mapped(bh_result); + } /* make sure we don't map more than max_blocks blocks here as that's all the kernel will handle at this point. */ @@ -606,12 +522,17 @@ static void ocfs2_dio_end_io(struct kioc void *private) { struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; + int level; /* this io's submitter should not have unlocked this before we could */ BUG_ON(!ocfs2_iocb_is_rw_locked(iocb)); + ocfs2_iocb_clear_rw_locked(iocb); - up_read(&inode->i_alloc_sem); - ocfs2_rw_unlock(inode, 0); + + level = ocfs2_iocb_rw_locked_level(iocb); + if (!level) + up_read(&inode->i_alloc_sem); + ocfs2_rw_unlock(inode, level); } /* @@ -647,23 +568,27 @@ static ssize_t ocfs2_direct_IO(int rw, mlog_entry_void(); - /* - * We get PR data locks even for O_DIRECT. This allows - * concurrent O_DIRECT I/O but doesn't let O_DIRECT with - * extending and buffered zeroing writes race. If they did - * race then the buffered zeroing could be written back after - * the O_DIRECT I/O. It's one thing to tell people not to mix - * buffered and O_DIRECT writes, but expecting them to - * understand that file extension is also an implicit buffered - * write is too much. By getting the PR we force writeback of - * the buffered zeroing before proceeding. - */ - ret = ocfs2_data_lock(inode, 0); - if (ret < 0) { - mlog_errno(ret); - goto out; + if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) { + /* + * We get PR data locks even for O_DIRECT. This + * allows concurrent O_DIRECT I/O but doesn't let + * O_DIRECT with extending and buffered zeroing writes + * race. If they did race then the buffered zeroing + * could be written back after the O_DIRECT I/O. It's + * one thing to tell people not to mix buffered and + * O_DIRECT writes, but expecting them to understand + * that file extension is also an implicit buffered + * write is too much. By getting the PR we force + * writeback of the buffered zeroing before + * proceeding. + */ + ret = ocfs2_data_lock(inode, 0); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + ocfs2_data_unlock(inode, 0); } - ocfs2_data_unlock(inode, 0); ret = blockdev_direct_IO_no_locking(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, @@ -675,11 +600,715 @@ out: return ret; } +static void ocfs2_figure_cluster_boundaries(struct ocfs2_super *osb, + u32 cpos, + unsigned int *start, + unsigned int *end) +{ + unsigned int cluster_start = 0, cluster_end = PAGE_CACHE_SIZE; + + if (unlikely(PAGE_CACHE_SHIFT > osb->s_clustersize_bits)) { + unsigned int cpp; + + cpp = 1 << (PAGE_CACHE_SHIFT - osb->s_clustersize_bits); + + cluster_start = cpos % cpp; + cluster_start = cluster_start << osb->s_clustersize_bits; + + cluster_end = cluster_start + osb->s_clustersize; + } + + BUG_ON(cluster_start > PAGE_SIZE); + BUG_ON(cluster_end > PAGE_SIZE); + + if (start) + *start = cluster_start; + if (end) + *end = cluster_end; +} + +/* + * 'from' and 'to' are the region in the page to avoid zeroing. + * + * If pagesize > clustersize, this function will avoid zeroing outside + * of the cluster boundary. + * + * from == to == 0 is code for "zero the entire cluster region" + */ +static void ocfs2_clear_page_regions(struct page *page, + struct ocfs2_super *osb, u32 cpos, + unsigned from, unsigned to) +{ + void *kaddr; + unsigned int cluster_start, cluster_end; + + ocfs2_figure_cluster_boundaries(osb, cpos, &cluster_start, &cluster_end); + + kaddr = kmap_atomic(page, KM_USER0); + + if (from || to) { + if (from > cluster_start) + memset(kaddr + cluster_start, 0, from - cluster_start); + if (to < cluster_end) + memset(kaddr + to, 0, cluster_end - to); + } else { + memset(kaddr + cluster_start, 0, cluster_end - cluster_start); + } + + kunmap_atomic(kaddr, KM_USER0); +} + +/* + * Some of this taken from block_prepare_write(). We already have our + * mapping by now though, and the entire write will be allocating or + * it won't, so not much need to use BH_New. + * + * This will also skip zeroing, which is handled externally. + */ +int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, + struct inode *inode, unsigned int from, + unsigned int to, int new) +{ + int ret = 0; + struct buffer_head *head, *bh, *wait[2], **wait_bh = wait; + unsigned int block_end, block_start; + unsigned int bsize = 1 << inode->i_blkbits; + + if (!page_has_buffers(page)) + create_empty_buffers(page, bsize, 0); + + head = page_buffers(page); + for (bh = head, block_start = 0; bh != head || !block_start; + bh = bh->b_this_page, block_start += bsize) { + block_end = block_start + bsize; + + /* + * Ignore blocks outside of our i/o range - + * they may belong to unallocated clusters. + */ + if (block_start >= to || block_end <= from) { + if (PageUptodate(page)) + set_buffer_uptodate(bh); + continue; + } + + /* + * For an allocating write with cluster size >= page + * size, we always write the entire page. + */ + + if (buffer_new(bh)) + clear_buffer_new(bh); + + if (!buffer_mapped(bh)) { + map_bh(bh, inode->i_sb, *p_blkno); + unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr); + } + + if (PageUptodate(page)) { + if (!buffer_uptodate(bh)) + set_buffer_uptodate(bh); + } else if (!buffer_uptodate(bh) && !buffer_delay(bh) && + (block_start < from || block_end > to)) { + ll_rw_block(READ, 1, &bh); + *wait_bh++=bh; + } + + *p_blkno = *p_blkno + 1; + } + + /* + * If we issued read requests - let them complete. + */ + while(wait_bh > wait) { + wait_on_buffer(*--wait_bh); + if (!buffer_uptodate(*wait_bh)) + ret = -EIO; + } + + if (ret == 0 || !new) + return ret; + + /* + * If we get -EIO above, zero out any newly allocated blocks + * to avoid exposing stale data. + */ + bh = head; + block_start = 0; + do { + void *kaddr; + + block_end = block_start + bsize; + if (block_end <= from) + goto next_bh; + if (block_start >= to) + break; + + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr+block_start, 0, bh->b_size); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + +next_bh: + block_start = block_end; + bh = bh->b_this_page; + } while (bh != head); + + return ret; +} + +/* + * This will copy user data from the buffer page in the splice + * context. + * + * For now, we ignore SPLICE_F_MOVE as that would require some extra + * communication out all the way to ocfs2_write(). + */ +int ocfs2_map_and_write_splice_data(struct inode *inode, + struct ocfs2_write_ctxt *wc, u64 *p_blkno, + unsigned int *ret_from, unsigned int *ret_to) +{ + int ret; + unsigned int to, from, cluster_start, cluster_end; + char *src, *dst; + struct ocfs2_splice_write_priv *sp = wc->w_private; + struct pipe_buffer *buf = sp->s_buf; + unsigned long bytes, src_from; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + ocfs2_figure_cluster_boundaries(osb, wc->w_cpos, &cluster_start, + &cluster_end); + + from = sp->s_offset; + src_from = sp->s_buf_offset; + bytes = wc->w_count; + + if (wc->w_large_pages) { + /* + * For cluster size < page size, we have to + * calculate pos within the cluster and obey + * the rightmost boundary. + */ + bytes = min(bytes, (unsigned long)(osb->s_clustersize + - (wc->w_pos & (osb->s_clustersize - 1)))); + } + to = from + bytes; + + if (wc->w_this_page_new) + ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, + cluster_start, cluster_end, 1); + else + ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, + from, to, 0); + if (ret) { + mlog_errno(ret); + goto out; + } + + BUG_ON(from > PAGE_CACHE_SIZE); + BUG_ON(to > PAGE_CACHE_SIZE); + BUG_ON(from > osb->s_clustersize); + BUG_ON(to > osb->s_clustersize); + + src = buf->ops->map(sp->s_pipe, buf, 1); + dst = kmap_atomic(wc->w_this_page, KM_USER1); + memcpy(dst + from, src + src_from, bytes); + kunmap_atomic(wc->w_this_page, KM_USER1); + buf->ops->unmap(sp->s_pipe, buf, src); + + wc->w_finished_copy = 1; + + *ret_from = from; + *ret_to = to; +out: + + return bytes ? (unsigned int)bytes : ret; +} + +/* + * This will copy user data from the iovec in the buffered write + * context. + */ +int ocfs2_map_and_write_user_data(struct inode *inode, + struct ocfs2_write_ctxt *wc, u64 *p_blkno, + unsigned int *ret_from, unsigned int *ret_to) +{ + int ret; + unsigned int to, from, cluster_start, cluster_end; + unsigned long bytes, src_from; + char *dst; + struct ocfs2_buffered_write_priv *bp = wc->w_private; + const struct iovec *cur_iov = bp->b_cur_iov; + char __user *buf; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + ocfs2_figure_cluster_boundaries(osb, wc->w_cpos, &cluster_start, + &cluster_end); + + buf = cur_iov->iov_base + bp->b_cur_off; + src_from = (unsigned long)buf & ~PAGE_CACHE_MASK; + + from = wc->w_pos & (PAGE_CACHE_SIZE - 1); + + /* + * This is a lot of comparisons, but it reads quite + * easily, which is important here. + */ + /* Stay within the src page */ + bytes = PAGE_SIZE - src_from; + /* Stay within the vector */ + bytes = min(bytes, + (unsigned long)(cur_iov->iov_len - bp->b_cur_off)); + /* Stay within count */ + bytes = min(bytes, (unsigned long)wc->w_count); + /* + * For clustersize > page size, just stay within + * target page, otherwise we have to calculate pos + * within the cluster and obey the rightmost + * boundary. + */ + if (wc->w_large_pages) { + /* + * For cluster size < page size, we have to + * calculate pos within the cluster and obey + * the rightmost boundary. + */ + bytes = min(bytes, (unsigned long)(osb->s_clustersize + - (wc->w_pos & (osb->s_clustersize - 1)))); + } else { + /* + * cluster size > page size is the most common + * case - we just stay within the target page + * boundary. + */ + bytes = min(bytes, PAGE_CACHE_SIZE - from); + } + + to = from + bytes; + + if (wc->w_this_page_new) + ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, + cluster_start, cluster_end, 1); + else + ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, + from, to, 0); + if (ret) { + mlog_errno(ret); + goto out; + } + + BUG_ON(from > PAGE_CACHE_SIZE); + BUG_ON(to > PAGE_CACHE_SIZE); + BUG_ON(from > osb->s_clustersize); + BUG_ON(to > osb->s_clustersize); + + dst = kmap(wc->w_this_page); + memcpy(dst + from, bp->b_src_buf + src_from, bytes); + kunmap(wc->w_this_page); + + /* + * XXX: This is slow, but simple. The caller of + * ocfs2_buffered_write_cluster() is responsible for + * passing through the iovecs, so it's difficult to + * predict what our next step is in here after our + * initial write. A future version should be pushing + * that iovec manipulation further down. + * + * By setting this, we indicate that a copy from user + * data was done, and subsequent calls for this + * cluster will skip copying more data. + */ + wc->w_finished_copy = 1; + + *ret_from = from; + *ret_to = to; +out: + + return bytes ? (unsigned int)bytes : ret; +} + +/* + * Map, fill and write a page to disk. + * + * The work of copying data is done via callback. Newly allocated + * pages which don't take user data will be zero'd (set 'new' to + * indicate an allocating write) + * + * Returns a negative error code or the number of bytes copied into + * the page. + */ +int ocfs2_write_data_page(struct inode *inode, handle_t *handle, + u64 *p_blkno, struct page *page, + struct ocfs2_write_ctxt *wc, int new) +{ + int ret, copied = 0; + unsigned int from = 0, to = 0; + unsigned int cluster_start, cluster_end; + unsigned int zero_from = 0, zero_to = 0; + + ocfs2_figure_cluster_boundaries(OCFS2_SB(inode->i_sb), wc->w_cpos, + &cluster_start, &cluster_end); + + if ((wc->w_pos >> PAGE_CACHE_SHIFT) == page->index + && !wc->w_finished_copy) { + + wc->w_this_page = page; + wc->w_this_page_new = new; + ret = wc->w_write_data_page(inode, wc, p_blkno, &from, &to); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + copied = ret; + + zero_from = from; + zero_to = to; + if (new) { + from = cluster_start; + to = cluster_end; + } + } else { + /* + * If we haven't allocated the new page yet, we + * shouldn't be writing it out without copying user + * data. This is likely a math error from the caller. + */ + BUG_ON(!new); + + from = cluster_start; + to = cluster_end; + + ret = ocfs2_map_page_blocks(page, p_blkno, inode, + cluster_start, cluster_end, 1); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + /* + * Parts of newly allocated pages need to be zero'd. + * + * Above, we have also rewritten 'to' and 'from' - as far as + * the rest of the function is concerned, the entire cluster + * range inside of a page needs to be written. + * + * We can skip this if the page is up to date - it's already + * been zero'd from being read in as a hole. + */ + if (new && !PageUptodate(page)) + ocfs2_clear_page_regions(page, OCFS2_SB(inode->i_sb), + wc->w_cpos, zero_from, zero_to); + + flush_dcache_page(page); + + if (ocfs2_should_order_data(inode)) { + ret = walk_page_buffers(handle, + page_buffers(page), + from, to, NULL, + ocfs2_journal_dirty_data); + if (ret < 0) + mlog_errno(ret); + } + + /* + * We don't use generic_commit_write() because we need to + * handle our own i_size update. + */ + ret = block_commit_write(page, from, to); + if (ret) + mlog_errno(ret); +out: + + return copied ? copied : ret; +} + +/* + * Do the actual write of some data into an inode. Optionally allocate + * in order to fulfill the write. + * + * cpos is the logical cluster offset within the file to write at + * + * 'phys' is the physical mapping of that offset. a 'phys' value of + * zero indicates that allocation is required. In this case, data_ac + * and meta_ac should be valid (meta_ac can be null if metadata + * allocation isn't required). + */ +static ssize_t ocfs2_write(struct file *file, u32 phys, handle_t *handle, + struct buffer_head *di_bh, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_write_ctxt *wc) +{ + int ret, i, numpages = 1, new; + unsigned int copied = 0; + u32 tmp_pos; + u64 v_blkno, p_blkno; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + unsigned long index, start; + struct page **cpages; + + new = phys == 0 ? 1 : 0; + + /* + * Figure out how many pages we'll be manipulating here. For + * non allocating write, we just change the one + * page. Otherwise, we'll need a whole clusters worth. + */ + if (new) + numpages = ocfs2_pages_per_cluster(inode->i_sb); + + cpages = kzalloc(sizeof(*cpages) * numpages, GFP_NOFS); + if (!cpages) { + ret = -ENOMEM; + mlog_errno(ret); + return ret; + } + + /* + * Fill our page array first. That way we've grabbed enough so + * that we can zero and flush if we error after adding the + * extent. + */ + if (new) { + start = ocfs2_align_clusters_to_page_index(inode->i_sb, + wc->w_cpos); + v_blkno = ocfs2_clusters_to_blocks(inode->i_sb, wc->w_cpos); + } else { + start = wc->w_pos >> PAGE_CACHE_SHIFT; + v_blkno = wc->w_pos >> inode->i_sb->s_blocksize_bits; + } + + for(i = 0; i < numpages; i++) { + index = start + i; + + cpages[i] = grab_cache_page(mapping, index); + if (!cpages[i]) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + } + + if (new) { + /* + * This is safe to call with the page locks - it won't take + * any additional semaphores or cluster locks. + */ + tmp_pos = wc->w_cpos; + ret = ocfs2_do_extend_allocation(OCFS2_SB(inode->i_sb), inode, + &tmp_pos, 1, di_bh, handle, + data_ac, meta_ac, NULL); + /* + * This shouldn't happen because we must have already + * calculated the correct meta data allocation required. The + * internal tree allocation code should know how to increase + * transaction credits itself. + * + * If need be, we could handle -EAGAIN for a + * RESTART_TRANS here. + */ + mlog_bug_on_msg(ret == -EAGAIN, + "Inode %llu: EAGAIN return during allocation.\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + ret = ocfs2_extent_map_get_blocks(inode, v_blkno, &p_blkno, NULL, + NULL); + if (ret < 0) { + + /* + * XXX: Should we go readonly here? + */ + + mlog_errno(ret); + goto out; + } + + BUG_ON(p_blkno == 0); + + for(i = 0; i < numpages; i++) { + ret = ocfs2_write_data_page(inode, handle, &p_blkno, cpages[i], + wc, new); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + copied += ret; + } + +out: + for(i = 0; i < numpages; i++) { + unlock_page(cpages[i]); + mark_page_accessed(cpages[i]); + page_cache_release(cpages[i]); + } + kfree(cpages); + + return copied ? copied : ret; +} + +static void ocfs2_write_ctxt_init(struct ocfs2_write_ctxt *wc, + struct ocfs2_super *osb, loff_t pos, + size_t count, ocfs2_page_writer *cb, + void *cb_priv) +{ + wc->w_count = count; + wc->w_pos = pos; + wc->w_cpos = wc->w_pos >> osb->s_clustersize_bits; + wc->w_finished_copy = 0; + + if (unlikely(PAGE_CACHE_SHIFT > osb->s_clustersize_bits)) + wc->w_large_pages = 1; + else + wc->w_large_pages = 0; + + wc->w_write_data_page = cb; + wc->w_private = cb_priv; +} + +/* + * Write a cluster to an inode. The cluster may not be allocated yet, + * in which case it will be. This only exists for buffered writes - + * O_DIRECT takes a more "traditional" path through the kernel. + * + * The caller is responsible for incrementing pos, written counts, etc + * + * For file systems that don't support sparse files, pre-allocation + * and page zeroing up until cpos should be done prior to this + * function call. + * + * Callers should be holding i_sem, and the rw cluster lock. + * + * Returns the number of user bytes written, or less than zero for + * error. + */ +ssize_t ocfs2_buffered_write_cluster(struct file *file, loff_t pos, + size_t count, ocfs2_page_writer *actor, + void *priv) +{ + int ret, credits = OCFS2_INODE_UPDATE_CREDITS; + ssize_t written = 0; + u32 phys; + struct inode *inode = file->f_mapping->host; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct buffer_head *di_bh = NULL; + struct ocfs2_dinode *di; + struct ocfs2_alloc_context *data_ac = NULL; + struct ocfs2_alloc_context *meta_ac = NULL; + handle_t *handle; + struct ocfs2_write_ctxt wc; + + ocfs2_write_ctxt_init(&wc, osb, pos, count, actor, priv); + + ret = ocfs2_meta_lock(inode, &di_bh, 1); + if (ret) { + mlog_errno(ret); + goto out; + } + di = (struct ocfs2_dinode *)di_bh->b_data; + + /* + * Take alloc sem here to prevent concurrent lookups. That way + * the mapping, zeroing and tree manipulation within + * ocfs2_write() will be safe against ->readpage(). This + * should also serve to lock out allocation from a shared + * writeable region. + */ + down_write(&OCFS2_I(inode)->ip_alloc_sem); + + ret = ocfs2_get_clusters(inode, wc.w_cpos, &phys, NULL, NULL); + if (ret) { + mlog_errno(ret); + goto out_meta; + } + + /* phys == 0 means that allocation is required. */ + if (phys == 0) { + ret = ocfs2_lock_allocators(inode, di, 1, &data_ac, &meta_ac); + if (ret) { + mlog_errno(ret); + goto out_meta; + } + + credits = ocfs2_calc_extend_credits(inode->i_sb, di, 1); + } + + ret = ocfs2_data_lock(inode, 1); + if (ret) { + mlog_errno(ret); + goto out_meta; + } + + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out_data; + } + + written = ocfs2_write(file, phys, handle, di_bh, data_ac, + meta_ac, &wc); + if (written < 0) { + ret = written; + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + pos += written; + if (pos > inode->i_size) { + i_size_write(inode, pos); + mark_inode_dirty(inode); + } + inode->i_blocks = ocfs2_inode_sector_count(inode); + di->i_size = cpu_to_le64((u64)i_size_read(inode)); + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec); + di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); + + ret = ocfs2_journal_dirty(handle, di_bh); + if (ret) + mlog_errno(ret); + +out_commit: + ocfs2_commit_trans(osb, handle); + +out_data: + ocfs2_data_unlock(inode, 1); + +out_meta: + up_write(&OCFS2_I(inode)->ip_alloc_sem); + ocfs2_meta_unlock(inode, 1); + +out: + brelse(di_bh); + if (data_ac) + ocfs2_free_alloc_context(data_ac); + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + + return written ? written : ret; +} + const struct address_space_operations ocfs2_aops = { .readpage = ocfs2_readpage, .writepage = ocfs2_writepage, - .prepare_write = ocfs2_prepare_write, - .commit_write = ocfs2_commit_write, .bmap = ocfs2_bmap, .sync_page = block_sync_page, .direct_IO = ocfs2_direct_IO, diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index f446a15..45821d4 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -30,12 +30,83 @@ handle_t *ocfs2_start_walk_page_trans(st unsigned from, unsigned to); +int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, + struct inode *inode, unsigned int from, + unsigned int to, int new); + +int walk_page_buffers( handle_t *handle, + struct buffer_head *head, + unsigned from, + unsigned to, + int *partial, + int (*fn)( handle_t *handle, + struct buffer_head *bh)); + +struct ocfs2_write_ctxt; +typedef int (ocfs2_page_writer)(struct inode *, struct ocfs2_write_ctxt *, + u64 *, unsigned int *, unsigned int *); + +ssize_t ocfs2_buffered_write_cluster(struct file *file, loff_t pos, + size_t count, ocfs2_page_writer *actor, + void *priv); + +struct ocfs2_write_ctxt { + size_t w_count; + loff_t w_pos; + u32 w_cpos; + unsigned int w_finished_copy; + + /* This is true if page_size > cluster_size */ + unsigned int w_large_pages; + + /* Filler callback and private data */ + ocfs2_page_writer *w_write_data_page; + void *w_private; + + /* Only valid for the filler callback */ + struct page *w_this_page; + unsigned int w_this_page_new; +}; + +struct ocfs2_buffered_write_priv { + char *b_src_buf; + const struct iovec *b_cur_iov; /* Current iovec */ + size_t b_cur_off; /* Offset in the + * current iovec */ +}; +int ocfs2_map_and_write_user_data(struct inode *inode, + struct ocfs2_write_ctxt *wc, + u64 *p_blkno, + unsigned int *ret_from, + unsigned int *ret_to); + +struct ocfs2_splice_write_priv { + struct splice_desc *s_sd; + struct pipe_buffer *s_buf; + struct pipe_inode_info *s_pipe; + /* Neither offset value is ever larger than one page */ + unsigned int s_offset; + unsigned int s_buf_offset; +}; +int ocfs2_map_and_write_splice_data(struct inode *inode, + struct ocfs2_write_ctxt *wc, + u64 *p_blkno, + unsigned int *ret_from, + unsigned int *ret_to); + /* all ocfs2_dio_end_io()'s fault */ #define ocfs2_iocb_is_rw_locked(iocb) \ test_bit(0, (unsigned long *)&iocb->private) -#define ocfs2_iocb_set_rw_locked(iocb) \ - set_bit(0, (unsigned long *)&iocb->private) +static inline void ocfs2_iocb_set_rw_locked(struct kiocb *iocb, int level) +{ + set_bit(0, (unsigned long *)&iocb->private); + if (level) + set_bit(1, (unsigned long *)&iocb->private); + else + clear_bit(1, (unsigned long *)&iocb->private); +} #define ocfs2_iocb_clear_rw_locked(iocb) \ clear_bit(0, (unsigned long *)&iocb->private) - +#define ocfs2_iocb_rw_locked_level(iocb) \ + test_bit(1, (unsigned long *)&iocb->private) #endif /* OCFS2_FILE_H */ diff --git a/fs/ocfs2/cluster/quorum.c b/fs/ocfs2/cluster/quorum.c index 4705d65..bbacf7d 100644 --- a/fs/ocfs2/cluster/quorum.c +++ b/fs/ocfs2/cluster/quorum.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "heartbeat.h" #include "nodemanager.h" @@ -72,7 +73,9 @@ static void o2quo_fence_self(void) /* panic spins with interrupts enabled. with preempt * threads can still schedule, etc, etc */ o2hb_stop_all_regions(); - panic("ocfs2 is very sorry to be fencing this system by panicing\n"); + + printk("ocfs2 is very sorry to be fencing this system by restarting\n"); + emergency_restart(); } /* Indicate that a timeout occured on a hearbeat region write. The diff --git a/fs/ocfs2/cluster/tcp_internal.h b/fs/ocfs2/cluster/tcp_internal.h index 4dae5df..9606111 100644 --- a/fs/ocfs2/cluster/tcp_internal.h +++ b/fs/ocfs2/cluster/tcp_internal.h @@ -38,6 +38,9 @@ #define O2NET_QUORUM_DELAY_MS ((o2hb_dea * locking semantics of the file system using the protocol. It should * be somewhere else, I'm sure, but right now it isn't. * + * New in version 8: + * - Replace delete inode votes with a cluster lock + * * New in version 7: * - DLM join domain includes the live nodemap * @@ -57,7 +60,7 @@ #define O2NET_QUORUM_DELAY_MS ((o2hb_dea * - full 64 bit i_size in the metadata lock lvbs * - introduction of "rw" lock and pushing meta/data locking down */ -#define O2NET_PROTOCOL_VERSION 7ULL +#define O2NET_PROTOCOL_VERSION 8ULL struct o2net_handshake { __be64 protocol_version; __be64 connector_id; diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 66821e1..67e6866 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -358,15 +358,17 @@ int ocfs2_do_extend_dir(struct super_blo { int status; int extend; - u64 p_blkno; + u64 p_blkno, v_blkno; spin_lock(&OCFS2_I(dir)->ip_lock); extend = (i_size_read(dir) == ocfs2_clusters_to_bytes(sb, OCFS2_I(dir)->ip_clusters)); spin_unlock(&OCFS2_I(dir)->ip_lock); if (extend) { - status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, 1, - parent_fe_bh, handle, + u32 offset = OCFS2_I(dir)->ip_clusters; + + status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, &offset, + 1, parent_fe_bh, handle, data_ac, meta_ac, NULL); BUG_ON(status == -EAGAIN); if (status < 0) { @@ -375,9 +377,8 @@ int ocfs2_do_extend_dir(struct super_blo } } - status = ocfs2_extent_map_get_blocks(dir, (dir->i_blocks >> - (sb->s_blocksize_bits - 9)), - 1, &p_blkno, NULL); + v_blkno = ocfs2_blocks_for_bytes(sb, i_size_read(dir)); + status = ocfs2_extent_map_get_blocks(dir, v_blkno, &p_blkno, NULL, NULL); if (status < 0) { mlog_errno(status); goto bail; @@ -486,7 +487,7 @@ static int ocfs2_extend_dir(struct ocfs2 dir_i_size += dir->i_sb->s_blocksize; i_size_write(dir, dir_i_size); - dir->i_blocks = ocfs2_align_bytes_to_sectors(dir_i_size); + dir->i_blocks = ocfs2_inode_sector_count(dir); status = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh); if (status < 0) { mlog_errno(status); diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index c558442..d836b98 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -430,11 +430,10 @@ redo_bucket: dlm_lockres_put(res); - cond_resched_lock(&dlm->spinlock); - if (dropped) goto redo_bucket; } + cond_resched_lock(&dlm->spinlock); num += n; mlog(0, "%s: touched %d lockreses in bucket %d " "(tot=%d)\n", dlm->name, n, i, num); @@ -1035,7 +1034,7 @@ static int dlm_try_to_join_domain(struct { int status = 0, tmpstat, node; struct domain_join_ctxt *ctxt; - enum dlm_query_join_response response; + enum dlm_query_join_response response = JOIN_DISALLOW; mlog_entry("%p", dlm); diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 6d4a83d..c1807a4 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -611,6 +611,7 @@ static int dlm_remaster_locks(struct dlm } } while (status != 0); + spin_lock(&dlm_reco_state_lock); switch (ndata->state) { case DLM_RECO_NODE_DATA_INIT: case DLM_RECO_NODE_DATA_FINALIZE_SENT: @@ -641,6 +642,7 @@ static int dlm_remaster_locks(struct dlm ndata->node_num, dead_node); break; } + spin_unlock(&dlm_reco_state_lock); } mlog(0, "done requesting all lock info\n"); diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index e335541..27e43b0 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -225,11 +225,17 @@ static struct ocfs2_lock_res_ops ocfs2_d .flags = 0, }; +static struct ocfs2_lock_res_ops ocfs2_inode_open_lops = { + .get_osb = ocfs2_get_inode_osb, + .flags = 0, +}; + static inline int ocfs2_is_inode_lock(struct ocfs2_lock_res *lockres) { return lockres->l_type == OCFS2_LOCK_TYPE_META || lockres->l_type == OCFS2_LOCK_TYPE_DATA || - lockres->l_type == OCFS2_LOCK_TYPE_RW; + lockres->l_type == OCFS2_LOCK_TYPE_RW || + lockres->l_type == OCFS2_LOCK_TYPE_OPEN; } static inline struct inode *ocfs2_lock_res_inode(struct ocfs2_lock_res *lockres) @@ -373,6 +379,9 @@ void ocfs2_inode_lock_res_init(struct oc case OCFS2_LOCK_TYPE_DATA: ops = &ocfs2_inode_data_lops; break; + case OCFS2_LOCK_TYPE_OPEN: + ops = &ocfs2_inode_open_lops; + break; default: mlog_bug_on_msg(1, "type: %d\n", type); ops = NULL; /* thanks, gcc */ @@ -1129,6 +1138,12 @@ int ocfs2_create_new_inode_locks(struct goto bail; } + ret = ocfs2_create_new_lock(osb, &OCFS2_I(inode)->ip_open_lockres, 0, 0); + if (ret) { + mlog_errno(ret); + goto bail; + } + bail: mlog_exit(ret); return ret; @@ -1182,6 +1197,99 @@ void ocfs2_rw_unlock(struct inode *inode mlog_exit_void(); } +/* + * ocfs2_open_lock always get PR mode lock. + */ +int ocfs2_open_lock(struct inode *inode) +{ + int status = 0; + struct ocfs2_lock_res *lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + BUG_ON(!inode); + + mlog_entry_void(); + + mlog(0, "inode %llu take PRMODE open lock\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + + if (ocfs2_mount_local(osb)) + goto out; + + lockres = &OCFS2_I(inode)->ip_open_lockres; + + status = ocfs2_cluster_lock(OCFS2_SB(inode->i_sb), lockres, + LKM_PRMODE, 0, 0); + if (status < 0) + mlog_errno(status); + +out: + mlog_exit(status); + return status; +} + +int ocfs2_try_open_lock(struct inode *inode, int write) +{ + int status = 0, level; + struct ocfs2_lock_res *lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + BUG_ON(!inode); + + mlog_entry_void(); + + mlog(0, "inode %llu try to take %s open lock\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + write ? "EXMODE" : "PRMODE"); + + if (ocfs2_mount_local(osb)) + goto out; + + lockres = &OCFS2_I(inode)->ip_open_lockres; + + level = write ? LKM_EXMODE : LKM_PRMODE; + + /* + * The file system may already holding a PRMODE/EXMODE open lock. + * Since we pass LKM_NOQUEUE, the request won't block waiting on + * other nodes and the -EAGAIN will indicate to the caller that + * this inode is still in use. + */ + status = ocfs2_cluster_lock(OCFS2_SB(inode->i_sb), lockres, + level, LKM_NOQUEUE, 0); + +out: + mlog_exit(status); + return status; +} + +/* + * ocfs2_open_unlock unlock PR and EX mode open locks. + */ +void ocfs2_open_unlock(struct inode *inode) +{ + struct ocfs2_lock_res *lockres = &OCFS2_I(inode)->ip_open_lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + mlog_entry_void(); + + mlog(0, "inode %llu drop open lock\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + + if (ocfs2_mount_local(osb)) + goto out; + + if(lockres->l_ro_holders) + ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, + LKM_PRMODE); + if(lockres->l_ex_holders) + ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, + LKM_EXMODE); + +out: + mlog_exit_void(); +} + int ocfs2_data_lock_full(struct inode *inode, int write, int arg_flags) @@ -1387,8 +1495,7 @@ static void ocfs2_refresh_inode_from_lvb if (S_ISLNK(inode->i_mode) && !oi->ip_clusters) inode->i_blocks = 0; else - inode->i_blocks = - ocfs2_align_bytes_to_sectors(i_size_read(inode)); + inode->i_blocks = ocfs2_inode_sector_count(inode); inode->i_uid = be32_to_cpu(lvb->lvb_iuid); inode->i_gid = be32_to_cpu(lvb->lvb_igid); @@ -1479,12 +1586,15 @@ static int ocfs2_meta_lock_update(struct { int status = 0; struct ocfs2_inode_info *oi = OCFS2_I(inode); - struct ocfs2_lock_res *lockres = NULL; + struct ocfs2_lock_res *lockres = &oi->ip_meta_lockres; struct ocfs2_dinode *fe; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry_void(); + if (ocfs2_mount_local(osb)) + goto bail; + spin_lock(&oi->ip_lock); if (oi->ip_flags & OCFS2_INODE_DELETED) { mlog(0, "Orphaned inode %llu was deleted while we " @@ -1496,22 +1606,16 @@ static int ocfs2_meta_lock_update(struct } spin_unlock(&oi->ip_lock); - if (!ocfs2_mount_local(osb)) { - lockres = &oi->ip_meta_lockres; - - if (!ocfs2_should_refresh_lock_res(lockres)) - goto bail; - } + if (!ocfs2_should_refresh_lock_res(lockres)) + goto bail; /* This will discard any caching information we might have had * for the inode metadata. */ ocfs2_metadata_cache_purge(inode); - /* will do nothing for inode types that don't use the extent - * map (directories, bitmap files, etc) */ ocfs2_extent_map_trunc(inode, 0); - if (lockres && ocfs2_meta_lvb_is_trustable(inode, lockres)) { + if (ocfs2_meta_lvb_is_trustable(inode, lockres)) { mlog(0, "Trusting LVB on inode %llu\n", (unsigned long long)oi->ip_blkno); ocfs2_refresh_inode_from_lvb(inode); @@ -1558,8 +1662,7 @@ static int ocfs2_meta_lock_update(struct status = 0; bail_refresh: - if (lockres) - ocfs2_complete_lock_res_refresh(lockres, status); + ocfs2_complete_lock_res_refresh(lockres, status); bail: mlog_exit(status); return status; @@ -1630,7 +1733,6 @@ int ocfs2_meta_lock_full(struct inode *i wait_event(osb->recovery_event, ocfs2_node_map_is_empty(osb, &osb->recovery_map)); - acquired = 0; lockres = &OCFS2_I(inode)->ip_meta_lockres; level = ex ? LKM_EXMODE : LKM_PRMODE; dlm_flags = 0; @@ -2458,13 +2560,20 @@ int ocfs2_drop_inode_locks(struct inode * ocfs2_clear_inode has done it for us. */ err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb), - &OCFS2_I(inode)->ip_data_lockres); + &OCFS2_I(inode)->ip_open_lockres); if (err < 0) mlog_errno(err); status = err; err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb), + &OCFS2_I(inode)->ip_data_lockres); + if (err < 0) + mlog_errno(err); + if (err < 0 && !status) + status = err; + + err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb), &OCFS2_I(inode)->ip_meta_lockres); if (err < 0) mlog_errno(err); diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h index c343fca..59cb566 100644 --- a/fs/ocfs2/dlmglue.h +++ b/fs/ocfs2/dlmglue.h @@ -80,6 +80,9 @@ void ocfs2_data_unlock(struct inode *ino int write); int ocfs2_rw_lock(struct inode *inode, int write); void ocfs2_rw_unlock(struct inode *inode, int write); +int ocfs2_open_lock(struct inode *inode); +int ocfs2_try_open_lock(struct inode *inode, int write); +void ocfs2_open_unlock(struct inode *inode); int ocfs2_meta_lock_atime(struct inode *inode, struct vfsmount *vfsmnt, int *level); diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c index 80ac69f..ba2b2ab 100644 --- a/fs/ocfs2/extent_map.c +++ b/fs/ocfs2/extent_map.c @@ -3,8 +3,7 @@ * * extent_map.c * - * In-memory extent map for OCFS2. Man, this code was prettier in - * the library. + * Block/Cluster mapping functions * * Copyright (C) 2004 Oracle. All rights reserved. * @@ -26,1016 +25,528 @@ #include #include #include -#include -#include #define MLOG_MASK_PREFIX ML_EXTENT_MAP #include #include "ocfs2.h" +#include "alloc.h" #include "extent_map.h" #include "inode.h" #include "super.h" #include "buffer_head_io.h" - /* - * SUCK SUCK SUCK - * Our headers are so bad that struct ocfs2_extent_map is in ocfs.h - */ - -struct ocfs2_extent_map_entry { - struct rb_node e_node; - int e_tree_depth; - struct ocfs2_extent_rec e_rec; -}; - -struct ocfs2_em_insert_context { - int need_left; - int need_right; - struct ocfs2_extent_map_entry *new_ent; - struct ocfs2_extent_map_entry *old_ent; - struct ocfs2_extent_map_entry *left_ent; - struct ocfs2_extent_map_entry *right_ent; -}; - -static struct kmem_cache *ocfs2_em_ent_cachep = NULL; - - -static struct ocfs2_extent_map_entry * -ocfs2_extent_map_lookup(struct ocfs2_extent_map *em, - u32 cpos, u32 clusters, - struct rb_node ***ret_p, - struct rb_node **ret_parent); -static int ocfs2_extent_map_insert(struct inode *inode, - struct ocfs2_extent_rec *rec, - int tree_depth); -static int ocfs2_extent_map_insert_entry(struct ocfs2_extent_map *em, - struct ocfs2_extent_map_entry *ent); -static int ocfs2_extent_map_find_leaf(struct inode *inode, - u32 cpos, u32 clusters, - struct ocfs2_extent_list *el); -static int ocfs2_extent_map_lookup_read(struct inode *inode, - u32 cpos, u32 clusters, - struct ocfs2_extent_map_entry **ret_ent); -static int ocfs2_extent_map_try_insert(struct inode *inode, - struct ocfs2_extent_rec *rec, - int tree_depth, - struct ocfs2_em_insert_context *ctxt); - -/* returns 1 only if the rec contains all the given clusters -- that is that - * rec's cpos is <= the cluster cpos and that the rec endpoint (cpos + - * clusters) is >= the argument's endpoint */ -static int ocfs2_extent_rec_contains_clusters(struct ocfs2_extent_rec *rec, - u32 cpos, u32 clusters) -{ - if (le32_to_cpu(rec->e_cpos) > cpos) - return 0; - if (cpos + clusters > le32_to_cpu(rec->e_cpos) + - le32_to_cpu(rec->e_clusters)) - return 0; - return 1; -} - - -/* - * Find an entry in the tree that intersects the region passed in. - * Note that this will find straddled intervals, it is up to the - * callers to enforce any boundary conditions. - * - * Callers must hold ip_lock. This lookup is not guaranteed to return - * a tree_depth 0 match, and as such can race inserts if the lock - * were not held. + * The extent caching implementation is intentionally trivial. * - * The rb_node garbage lets insertion share the search. Trivial - * callers pass NULL. + * We only cache a small number of extents stored directly on the + * inode, so linear order operations are acceptable. If we ever want + * to increase the size of the extent map, then these algorithms must + * get smarter. */ -static struct ocfs2_extent_map_entry * -ocfs2_extent_map_lookup(struct ocfs2_extent_map *em, - u32 cpos, u32 clusters, - struct rb_node ***ret_p, - struct rb_node **ret_parent) + +void ocfs2_extent_map_init(struct inode *inode) { - struct rb_node **p = &em->em_extents.rb_node; - struct rb_node *parent = NULL; - struct ocfs2_extent_map_entry *ent = NULL; - - while (*p) - { - parent = *p; - ent = rb_entry(parent, struct ocfs2_extent_map_entry, - e_node); - if ((cpos + clusters) <= le32_to_cpu(ent->e_rec.e_cpos)) { - p = &(*p)->rb_left; - ent = NULL; - } else if (cpos >= (le32_to_cpu(ent->e_rec.e_cpos) + - le32_to_cpu(ent->e_rec.e_clusters))) { - p = &(*p)->rb_right; - ent = NULL; - } else - break; - } + struct ocfs2_inode_info *oi = OCFS2_I(inode); - if (ret_p != NULL) - *ret_p = p; - if (ret_parent != NULL) - *ret_parent = parent; - return ent; + oi->ip_extent_map.em_num_items = 0; + INIT_LIST_HEAD(&oi->ip_extent_map.em_list); } -/* - * Find the leaf containing the interval we want. While we're on our - * way down the tree, fill in every record we see at any depth, because - * we might want it later. - * - * Note that this code is run without ip_lock. That's because it - * sleeps while reading. If someone is also filling the extent list at - * the same time we are, we might have to restart. - */ -static int ocfs2_extent_map_find_leaf(struct inode *inode, - u32 cpos, u32 clusters, - struct ocfs2_extent_list *el) +static void __ocfs2_extent_map_lookup(struct ocfs2_extent_map *em, + unsigned int cpos, + struct ocfs2_extent_map_item **ret_emi) { - int i, ret; - struct buffer_head *eb_bh = NULL; - u64 blkno; - u32 rec_end; - struct ocfs2_extent_block *eb; - struct ocfs2_extent_rec *rec; - - /* - * The bh data containing the el cannot change here, because - * we hold alloc_sem. So we can do this without other - * locks. - */ - while (el->l_tree_depth) - { - blkno = 0; - for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { - rec = &el->l_recs[i]; - rec_end = (le32_to_cpu(rec->e_cpos) + - le32_to_cpu(rec->e_clusters)); - - ret = -EBADR; - if (rec_end > OCFS2_I(inode)->ip_clusters) { - mlog_errno(ret); - ocfs2_error(inode->i_sb, - "Extent %d at e_blkno %llu of inode %llu goes past ip_clusters of %u\n", - i, - (unsigned long long)le64_to_cpu(rec->e_blkno), - (unsigned long long)OCFS2_I(inode)->ip_blkno, - OCFS2_I(inode)->ip_clusters); - goto out_free; - } - - if (rec_end <= cpos) { - ret = ocfs2_extent_map_insert(inode, rec, - le16_to_cpu(el->l_tree_depth)); - if (ret && (ret != -EEXIST)) { - mlog_errno(ret); - goto out_free; - } - continue; - } - if ((cpos + clusters) <= le32_to_cpu(rec->e_cpos)) { - ret = ocfs2_extent_map_insert(inode, rec, - le16_to_cpu(el->l_tree_depth)); - if (ret && (ret != -EEXIST)) { - mlog_errno(ret); - goto out_free; - } - continue; - } + unsigned int range; + struct ocfs2_extent_map_item *emi; - /* - * We've found a record that matches our - * interval. We don't insert it because we're - * about to traverse it. - */ - - /* Check to see if we're stradling */ - ret = -ESRCH; - if (!ocfs2_extent_rec_contains_clusters(rec, - cpos, - clusters)) { - mlog_errno(ret); - goto out_free; - } + *ret_emi = NULL; - /* - * If we've already found a record, the el has - * two records covering the same interval. - * EEEK! - */ - ret = -EBADR; - if (blkno) { - mlog_errno(ret); - ocfs2_error(inode->i_sb, - "Multiple extents for (cpos = %u, clusters = %u) on inode %llu; e_blkno %llu and rec %d at e_blkno %llu\n", - cpos, clusters, - (unsigned long long)OCFS2_I(inode)->ip_blkno, - (unsigned long long)blkno, i, - (unsigned long long)le64_to_cpu(rec->e_blkno)); - goto out_free; - } + list_for_each_entry(emi, &em->em_list, ei_list) { + range = emi->ei_cpos + emi->ei_clusters; - blkno = le64_to_cpu(rec->e_blkno); - } + if (cpos >= emi->ei_cpos && cpos < range) { + list_move(&emi->ei_list, &em->em_list); - /* - * We don't support holes, and we're still up - * in the branches, so we'd better have found someone - */ - ret = -EBADR; - if (!blkno) { - ocfs2_error(inode->i_sb, - "No record found for (cpos = %u, clusters = %u) on inode %llu\n", - cpos, clusters, - (unsigned long long)OCFS2_I(inode)->ip_blkno); - mlog_errno(ret); - goto out_free; - } - - if (eb_bh) { - brelse(eb_bh); - eb_bh = NULL; - } - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), - blkno, &eb_bh, OCFS2_BH_CACHED, - inode); - if (ret) { - mlog_errno(ret); - goto out_free; - } - eb = (struct ocfs2_extent_block *)eb_bh->b_data; - if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { - OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); - ret = -EIO; - goto out_free; + *ret_emi = emi; + break; } - el = &eb->h_list; } +} - BUG_ON(el->l_tree_depth); - - for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { - rec = &el->l_recs[i]; - - if ((le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)) > - OCFS2_I(inode)->ip_clusters) { - ret = -EBADR; - mlog_errno(ret); - ocfs2_error(inode->i_sb, - "Extent %d at e_blkno %llu of inode %llu goes past ip_clusters of %u\n", - i, - (unsigned long long)le64_to_cpu(rec->e_blkno), - (unsigned long long)OCFS2_I(inode)->ip_blkno, - OCFS2_I(inode)->ip_clusters); - return ret; - } - - ret = ocfs2_extent_map_insert(inode, rec, - le16_to_cpu(el->l_tree_depth)); - if (ret && (ret != -EEXIST)) { - mlog_errno(ret); - goto out_free; - } +static int ocfs2_extent_map_lookup(struct inode *inode, unsigned int cpos, + unsigned int *phys, unsigned int *len, + unsigned int *flags) +{ + unsigned int coff; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_extent_map_item *emi; + + spin_lock(&oi->ip_lock); + + __ocfs2_extent_map_lookup(&oi->ip_extent_map, cpos, &emi); + if (emi) { + coff = cpos - emi->ei_cpos; + *phys = emi->ei_phys + coff; + if (len) + *len = emi->ei_clusters - coff; + if (flags) + *flags = emi->ei_flags; } - ret = 0; + spin_unlock(&oi->ip_lock); -out_free: - if (eb_bh) - brelse(eb_bh); + if (emi == NULL) + return -ENOENT; - return ret; + return 0; } /* - * This lookup actually will read from disk. It has one invariant: - * It will never re-traverse blocks. This means that all inserts should - * be new regions or more granular regions (both allowed by insert). + * Forget about all clusters equal to or greater than cpos. */ -static int ocfs2_extent_map_lookup_read(struct inode *inode, - u32 cpos, - u32 clusters, - struct ocfs2_extent_map_entry **ret_ent) +void ocfs2_extent_map_trunc(struct inode *inode, unsigned int cpos) { - int ret; - u64 blkno; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - struct ocfs2_extent_map_entry *ent; - struct buffer_head *bh = NULL; - struct ocfs2_extent_block *eb; - struct ocfs2_dinode *di; - struct ocfs2_extent_list *el; - - spin_lock(&OCFS2_I(inode)->ip_lock); - ent = ocfs2_extent_map_lookup(em, cpos, clusters, NULL, NULL); - if (ent) { - if (!ent->e_tree_depth) { - spin_unlock(&OCFS2_I(inode)->ip_lock); - *ret_ent = ent; - return 0; - } - blkno = le64_to_cpu(ent->e_rec.e_blkno); - spin_unlock(&OCFS2_I(inode)->ip_lock); - - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno, &bh, - OCFS2_BH_CACHED, inode); - if (ret) { - mlog_errno(ret); - if (bh) - brelse(bh); - return ret; + struct list_head *p, *n; + struct ocfs2_extent_map_item *emi; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_extent_map *em = &oi->ip_extent_map; + LIST_HEAD(tmp_list); + unsigned int range; + + spin_lock(&oi->ip_lock); + list_for_each_safe(p, n, &em->em_list) { + emi = list_entry(p, struct ocfs2_extent_map_item, ei_list); + + if (emi->ei_cpos >= cpos) { + /* Full truncate of this record. */ + list_move(&emi->ei_list, &tmp_list); + BUG_ON(em->em_num_items == 0); + em->em_num_items--; + continue; } - eb = (struct ocfs2_extent_block *)bh->b_data; - if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { - OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); - brelse(bh); - return -EIO; - } - el = &eb->h_list; - } else { - spin_unlock(&OCFS2_I(inode)->ip_lock); - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), - OCFS2_I(inode)->ip_blkno, &bh, - OCFS2_BH_CACHED, inode); - if (ret) { - mlog_errno(ret); - if (bh) - brelse(bh); - return ret; + range = emi->ei_cpos + emi->ei_clusters; + if (range > cpos) { + /* Partial truncate */ + emi->ei_clusters = cpos - emi->ei_cpos; } - di = (struct ocfs2_dinode *)bh->b_data; - if (!OCFS2_IS_VALID_DINODE(di)) { - brelse(bh); - OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, di); - return -EIO; - } - el = &di->id2.i_list; - } - - ret = ocfs2_extent_map_find_leaf(inode, cpos, clusters, el); - brelse(bh); - if (ret) { - mlog_errno(ret); - return ret; } + spin_unlock(&oi->ip_lock); - ent = ocfs2_extent_map_lookup(em, cpos, clusters, NULL, NULL); - if (!ent) { - ret = -ESRCH; - mlog_errno(ret); - return ret; + list_for_each_safe(p, n, &tmp_list) { + emi = list_entry(p, struct ocfs2_extent_map_item, ei_list); + list_del(&emi->ei_list); + kfree(emi); } - - /* FIXME: Make sure this isn't a corruption */ - BUG_ON(ent->e_tree_depth); - - *ret_ent = ent; - - return 0; } /* - * Callers must hold ip_lock. This can insert pieces of the tree, - * thus racing lookup if the lock weren't held. + * Is any part of emi2 contained within emi1 */ -static int ocfs2_extent_map_insert_entry(struct ocfs2_extent_map *em, - struct ocfs2_extent_map_entry *ent) +static int ocfs2_ei_is_contained(struct ocfs2_extent_map_item *emi1, + struct ocfs2_extent_map_item *emi2) { - struct rb_node **p, *parent; - struct ocfs2_extent_map_entry *old_ent; + unsigned int range1, range2; - old_ent = ocfs2_extent_map_lookup(em, le32_to_cpu(ent->e_rec.e_cpos), - le32_to_cpu(ent->e_rec.e_clusters), - &p, &parent); - if (old_ent) - return -EEXIST; + /* + * Check if logical start of emi2 is inside emi1 + */ + range1 = emi1->ei_cpos + emi1->ei_clusters; + if (emi2->ei_cpos >= emi1->ei_cpos && emi2->ei_cpos < range1) + return 1; - rb_link_node(&ent->e_node, parent, p); - rb_insert_color(&ent->e_node, &em->em_extents); + /* + * Check if logical end of emi2 is inside emi1 + */ + range2 = emi2->ei_cpos + emi2->ei_clusters; + if (range2 > emi1->ei_cpos && range2 <= range1) + return 1; return 0; } +static void ocfs2_copy_emi_fields(struct ocfs2_extent_map_item *dest, + struct ocfs2_extent_map_item *src) +{ + dest->ei_cpos = src->ei_cpos; + dest->ei_phys = src->ei_phys; + dest->ei_clusters = src->ei_clusters; + dest->ei_flags = src->ei_flags; +} /* - * Simple rule: on any return code other than -EAGAIN, anything left - * in the insert_context will be freed. - * - * Simple rule #2: A return code of -EEXIST from this function or - * its calls to ocfs2_extent_map_insert_entry() signifies that another - * thread beat us to the insert. It is not an actual error, but it - * tells the caller we have no more work to do. + * Try to merge emi with ins. Returns 1 if merge succeeds, zero + * otherwise. */ -static int ocfs2_extent_map_try_insert(struct inode *inode, - struct ocfs2_extent_rec *rec, - int tree_depth, - struct ocfs2_em_insert_context *ctxt) +static int ocfs2_try_to_merge_extent_map(struct ocfs2_extent_map_item *emi, + struct ocfs2_extent_map_item *ins) { - int ret; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - struct ocfs2_extent_map_entry *old_ent; - - ctxt->need_left = 0; - ctxt->need_right = 0; - ctxt->old_ent = NULL; - - spin_lock(&OCFS2_I(inode)->ip_lock); - ret = ocfs2_extent_map_insert_entry(em, ctxt->new_ent); - if (!ret) { - ctxt->new_ent = NULL; - goto out_unlock; - } - - /* Since insert_entry failed, the map MUST have old_ent */ - old_ent = ocfs2_extent_map_lookup(em, le32_to_cpu(rec->e_cpos), - le32_to_cpu(rec->e_clusters), - NULL, NULL); - - BUG_ON(!old_ent); - - if (old_ent->e_tree_depth < tree_depth) { - /* Another thread beat us to the lower tree_depth */ - ret = -EEXIST; - goto out_unlock; - } - - if (old_ent->e_tree_depth == tree_depth) { - /* - * Another thread beat us to this tree_depth. - * Let's make sure we agree with that thread (the - * extent_rec should be identical). - */ - if (!memcmp(rec, &old_ent->e_rec, - sizeof(struct ocfs2_extent_rec))) - ret = 0; - else - /* FIXME: Should this be ESRCH/EBADR??? */ - ret = -EEXIST; - - goto out_unlock; - } - /* - * We do it in this order specifically so that no actual tree - * changes occur until we have all the pieces we need. We - * don't want malloc failures to leave an inconsistent tree. - * Whenever we drop the lock, another process could be - * inserting. Also note that, if another process just beat us - * to an insert, we might not need the same pieces we needed - * the first go round. In the end, the pieces we need will - * be used, and the pieces we don't will be freed. + * Handle contiguousness */ - ctxt->need_left = !!(le32_to_cpu(rec->e_cpos) > - le32_to_cpu(old_ent->e_rec.e_cpos)); - ctxt->need_right = !!((le32_to_cpu(old_ent->e_rec.e_cpos) + - le32_to_cpu(old_ent->e_rec.e_clusters)) > - (le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters))); - ret = -EAGAIN; - if (ctxt->need_left) { - if (!ctxt->left_ent) - goto out_unlock; - *(ctxt->left_ent) = *old_ent; - ctxt->left_ent->e_rec.e_clusters = - cpu_to_le32(le32_to_cpu(rec->e_cpos) - - le32_to_cpu(ctxt->left_ent->e_rec.e_cpos)); - } - if (ctxt->need_right) { - if (!ctxt->right_ent) - goto out_unlock; - *(ctxt->right_ent) = *old_ent; - ctxt->right_ent->e_rec.e_cpos = - cpu_to_le32(le32_to_cpu(rec->e_cpos) + - le32_to_cpu(rec->e_clusters)); - ctxt->right_ent->e_rec.e_clusters = - cpu_to_le32((le32_to_cpu(old_ent->e_rec.e_cpos) + - le32_to_cpu(old_ent->e_rec.e_clusters)) - - le32_to_cpu(ctxt->right_ent->e_rec.e_cpos)); - } - - rb_erase(&old_ent->e_node, &em->em_extents); - /* Now that he's erased, set him up for deletion */ - ctxt->old_ent = old_ent; - - if (ctxt->need_left) { - ret = ocfs2_extent_map_insert_entry(em, - ctxt->left_ent); - if (ret) - goto out_unlock; - ctxt->left_ent = NULL; + if (ins->ei_phys == (emi->ei_phys + emi->ei_clusters) && + ins->ei_cpos == (emi->ei_cpos + emi->ei_clusters) && + ins->ei_flags == emi->ei_flags) { + emi->ei_clusters += ins->ei_clusters; + return 1; + } else if ((ins->ei_phys + ins->ei_clusters) == emi->ei_phys && + (ins->ei_cpos + ins->ei_clusters) == emi->ei_phys && + ins->ei_flags == emi->ei_flags) { + emi->ei_phys = ins->ei_phys; + emi->ei_cpos = ins->ei_cpos; + emi->ei_clusters += ins->ei_clusters; + return 1; } - if (ctxt->need_right) { - ret = ocfs2_extent_map_insert_entry(em, - ctxt->right_ent); - if (ret) - goto out_unlock; - ctxt->right_ent = NULL; + /* + * Overlapping extents - this shouldn't happen unless we've + * split an extent to change it's flags. That is exceedingly + * rare, so there's no sense in trying to optimize it yet. + */ + if (ocfs2_ei_is_contained(emi, ins) || + ocfs2_ei_is_contained(ins, emi)) { + ocfs2_copy_emi_fields(emi, ins); + return 1; } - ret = ocfs2_extent_map_insert_entry(em, ctxt->new_ent); - - if (!ret) - ctxt->new_ent = NULL; - -out_unlock: - spin_unlock(&OCFS2_I(inode)->ip_lock); - - return ret; + /* No merge was possible. */ + return 0; } - -static int ocfs2_extent_map_insert(struct inode *inode, - struct ocfs2_extent_rec *rec, - int tree_depth) +/* + * In order to reduce complexity on the caller, this insert function + * is intentionally liberal in what it will accept. + * + * The only rule is that the truncate call *must* be used whenever + * records have been deleted. This avoids inserting overlapping + * records with different physical mappings. + */ +void ocfs2_extent_map_insert_rec(struct inode *inode, + struct ocfs2_extent_rec *rec) { - int ret; - struct ocfs2_em_insert_context ctxt = {0, }; - - if ((le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)) > - OCFS2_I(inode)->ip_map.em_clusters) { - ret = -EBADR; - mlog_errno(ret); - return ret; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_extent_map *em = &oi->ip_extent_map; + struct ocfs2_extent_map_item *emi, *new_emi = NULL; + struct ocfs2_extent_map_item ins; + + ins.ei_cpos = le32_to_cpu(rec->e_cpos); + ins.ei_phys = ocfs2_blocks_to_clusters(inode->i_sb, + le64_to_cpu(rec->e_blkno)); + ins.ei_clusters = le16_to_cpu(rec->e_leaf_clusters); + ins.ei_flags = rec->e_flags; + +search: + spin_lock(&oi->ip_lock); + + list_for_each_entry(emi, &em->em_list, ei_list) { + if (ocfs2_try_to_merge_extent_map(emi, &ins)) { + list_move(&emi->ei_list, &em->em_list); + spin_unlock(&oi->ip_lock); + goto out; + } } - /* Zero e_clusters means a truncated tail record. It better be EOF */ - if (!rec->e_clusters) { - if ((le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)) != - OCFS2_I(inode)->ip_map.em_clusters) { - ret = -EBADR; - mlog_errno(ret); - ocfs2_error(inode->i_sb, - "Zero e_clusters on non-tail extent record at e_blkno %llu on inode %llu\n", - (unsigned long long)le64_to_cpu(rec->e_blkno), - (unsigned long long)OCFS2_I(inode)->ip_blkno); - return ret; - } + /* + * No item could be merged. + * + * Either allocate and add a new item, or overwrite the last recently + * inserted. + */ - /* Ignore the truncated tail */ - return 0; - } + if (em->em_num_items < OCFS2_MAX_EXTENT_MAP_ITEMS) { + if (new_emi == NULL) { + spin_unlock(&oi->ip_lock); - ret = -ENOMEM; - ctxt.new_ent = kmem_cache_alloc(ocfs2_em_ent_cachep, - GFP_NOFS); - if (!ctxt.new_ent) { - mlog_errno(ret); - return ret; - } + new_emi = kmalloc(sizeof(*new_emi), GFP_NOFS); + if (new_emi == NULL) + goto out; - ctxt.new_ent->e_rec = *rec; - ctxt.new_ent->e_tree_depth = tree_depth; - - do { - ret = -ENOMEM; - if (ctxt.need_left && !ctxt.left_ent) { - ctxt.left_ent = - kmem_cache_alloc(ocfs2_em_ent_cachep, - GFP_NOFS); - if (!ctxt.left_ent) - break; - } - if (ctxt.need_right && !ctxt.right_ent) { - ctxt.right_ent = - kmem_cache_alloc(ocfs2_em_ent_cachep, - GFP_NOFS); - if (!ctxt.right_ent) - break; + goto search; } - ret = ocfs2_extent_map_try_insert(inode, rec, - tree_depth, &ctxt); - } while (ret == -EAGAIN); - - if ((ret < 0) && (ret != -EEXIST)) - mlog_errno(ret); + ocfs2_copy_emi_fields(new_emi, &ins); + list_add(&new_emi->ei_list, &em->em_list); + em->em_num_items++; + new_emi = NULL; + } else { + BUG_ON(list_empty(&em->em_list) || em->em_num_items == 0); + emi = list_entry(em->em_list.prev, + struct ocfs2_extent_map_item, ei_list); + list_move(&emi->ei_list, &em->em_list); + ocfs2_copy_emi_fields(emi, &ins); + } - if (ctxt.left_ent) - kmem_cache_free(ocfs2_em_ent_cachep, ctxt.left_ent); - if (ctxt.right_ent) - kmem_cache_free(ocfs2_em_ent_cachep, ctxt.right_ent); - if (ctxt.old_ent) - kmem_cache_free(ocfs2_em_ent_cachep, ctxt.old_ent); - if (ctxt.new_ent) - kmem_cache_free(ocfs2_em_ent_cachep, ctxt.new_ent); + spin_unlock(&oi->ip_lock); - return ret; +out: + if (new_emi) + kfree(new_emi); } /* - * Append this record to the tail of the extent map. It must be - * tree_depth 0. The record might be an extension of an existing - * record, and as such that needs to be handled. eg: - * - * Existing record in the extent map: - * - * cpos = 10, len = 10 - * |---------| - * - * New Record: - * - * cpos = 10, len = 20 - * |------------------| - * - * The passed record is the new on-disk record. The new_clusters value - * is how many clusters were added to the file. If the append is a - * contiguous append, the new_clusters has been added to - * rec->e_clusters. If the append is an entirely new extent, then - * rec->e_clusters is == new_clusters. + * Return the 1st index within el which contains an extent start + * larger than v_cluster. */ -int ocfs2_extent_map_append(struct inode *inode, - struct ocfs2_extent_rec *rec, - u32 new_clusters) +static int ocfs2_search_for_hole_index(struct ocfs2_extent_list *el, + u32 v_cluster) { - int ret; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - struct ocfs2_extent_map_entry *ent; - struct ocfs2_extent_rec *old; - - BUG_ON(!new_clusters); - BUG_ON(le32_to_cpu(rec->e_clusters) < new_clusters); + int i; + struct ocfs2_extent_rec *rec; - if (em->em_clusters < OCFS2_I(inode)->ip_clusters) { - /* - * Size changed underneath us on disk. Drop any - * straddling records and update our idea of - * i_clusters - */ - ocfs2_extent_map_drop(inode, em->em_clusters - 1); - em->em_clusters = OCFS2_I(inode)->ip_clusters; - } + for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { + rec = &el->l_recs[i]; - mlog_bug_on_msg((le32_to_cpu(rec->e_cpos) + - le32_to_cpu(rec->e_clusters)) != - (em->em_clusters + new_clusters), - "Inode %llu:\n" - "rec->e_cpos = %u + rec->e_clusters = %u = %u\n" - "em->em_clusters = %u + new_clusters = %u = %u\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, - le32_to_cpu(rec->e_cpos), le32_to_cpu(rec->e_clusters), - le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters), - em->em_clusters, new_clusters, - em->em_clusters + new_clusters); - - em->em_clusters += new_clusters; - - ret = -ENOENT; - if (le32_to_cpu(rec->e_clusters) > new_clusters) { - /* This is a contiguous append */ - ent = ocfs2_extent_map_lookup(em, le32_to_cpu(rec->e_cpos), 1, - NULL, NULL); - if (ent) { - old = &ent->e_rec; - BUG_ON((le32_to_cpu(rec->e_cpos) + - le32_to_cpu(rec->e_clusters)) != - (le32_to_cpu(old->e_cpos) + - le32_to_cpu(old->e_clusters) + - new_clusters)); - if (ent->e_tree_depth == 0) { - BUG_ON(le32_to_cpu(old->e_cpos) != - le32_to_cpu(rec->e_cpos)); - BUG_ON(le64_to_cpu(old->e_blkno) != - le64_to_cpu(rec->e_blkno)); - ret = 0; - } - /* - * Let non-leafs fall through as -ENOENT to - * force insertion of the new leaf. - */ - le32_add_cpu(&old->e_clusters, new_clusters); - } + if (v_cluster < le32_to_cpu(rec->e_cpos)) + break; } - if (ret == -ENOENT) - ret = ocfs2_extent_map_insert(inode, rec, 0); - if (ret < 0) - mlog_errno(ret); - return ret; + return i; } -#if 0 -/* Code here is included but defined out as it completes the extent - * map api and may be used in the future. */ - /* - * Look up the record containing this cluster offset. This record is - * part of the extent map. Do not free it. Any changes you make to - * it will reflect in the extent map. So, if your last extent - * is (cpos = 10, clusters = 10) and you truncate the file by 5 - * clusters, you can do: + * Figure out the size of a hole which starts at v_cluster within the given + * extent list. * - * ret = ocfs2_extent_map_get_rec(em, orig_size - 5, &rec); - * rec->e_clusters -= 5; + * If there is no more allocation past v_cluster, we return the maximum + * cluster size minus v_cluster. * - * The lookup does not read from disk. If the map isn't filled in for - * an entry, you won't find it. - * - * Also note that the returned record is valid until alloc_sem is - * dropped. After that, truncate and extend can happen. Caveat Emptor. + * If we have in-inode extents, then el points to the dinode list and + * eb_bh is NULL. Otherwise, eb_bh should point to the extent block + * containing el. */ -int ocfs2_extent_map_get_rec(struct inode *inode, u32 cpos, - struct ocfs2_extent_rec **rec, - int *tree_depth) +static int ocfs2_figure_hole_clusters(struct inode *inode, + struct ocfs2_extent_list *el, + struct buffer_head *eb_bh, + u32 v_cluster, + u32 *num_clusters) { - int ret = -ENOENT; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - struct ocfs2_extent_map_entry *ent; + int ret, i; + struct buffer_head *next_eb_bh = NULL; + struct ocfs2_extent_block *eb, *next_eb; - *rec = NULL; + i = ocfs2_search_for_hole_index(el, v_cluster); - if (cpos >= OCFS2_I(inode)->ip_clusters) - return -EINVAL; + if (i == le16_to_cpu(el->l_next_free_rec) && eb_bh) { + eb = (struct ocfs2_extent_block *)eb_bh->b_data; - if (cpos >= em->em_clusters) { /* - * Size changed underneath us on disk. Drop any - * straddling records and update our idea of - * i_clusters + * Check the next leaf for any extents. */ - ocfs2_extent_map_drop(inode, em->em_clusters - 1); - em->em_clusters = OCFS2_I(inode)->ip_clusters ; - } - - ent = ocfs2_extent_map_lookup(&OCFS2_I(inode)->ip_map, cpos, 1, - NULL, NULL); - if (ent) { - *rec = &ent->e_rec; - if (tree_depth) - *tree_depth = ent->e_tree_depth; - ret = 0; - } + if (le64_to_cpu(eb->h_next_leaf_blk) == 0ULL) + goto no_more_extents; - return ret; -} + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), + le64_to_cpu(eb->h_next_leaf_blk), + &next_eb_bh, OCFS2_BH_CACHED, inode); + if (ret) { + mlog_errno(ret); + goto out; + } + next_eb = (struct ocfs2_extent_block *)next_eb_bh->b_data; -int ocfs2_extent_map_get_clusters(struct inode *inode, - u32 v_cpos, int count, - u32 *p_cpos, int *ret_count) -{ - int ret; - u32 coff, ccount; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - struct ocfs2_extent_map_entry *ent = NULL; + if (!OCFS2_IS_VALID_EXTENT_BLOCK(next_eb)) { + ret = -EROFS; + OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, next_eb); + goto out; + } - *p_cpos = ccount = 0; + el = &next_eb->h_list; - if ((v_cpos + count) > OCFS2_I(inode)->ip_clusters) - return -EINVAL; + i = ocfs2_search_for_hole_index(el, v_cluster); + } - if ((v_cpos + count) > em->em_clusters) { +no_more_extents: + if (i == le16_to_cpu(el->l_next_free_rec)) { /* - * Size changed underneath us on disk. Drop any - * straddling records and update our idea of - * i_clusters + * We're at the end of our existing allocation. Just + * return the maximum number of clusters we could + * possibly allocate. */ - ocfs2_extent_map_drop(inode, em->em_clusters - 1); - em->em_clusters = OCFS2_I(inode)->ip_clusters; + *num_clusters = UINT_MAX - v_cluster; + } else { + *num_clusters = le32_to_cpu(el->l_recs[i].e_cpos) - v_cluster; } + ret = 0; +out: + brelse(next_eb_bh); + return ret; +} - ret = ocfs2_extent_map_lookup_read(inode, v_cpos, count, &ent); - if (ret) - return ret; +/* + * Return the index of the extent record which contains cluster #v_cluster. + * -1 is returned if it was not found. + * + * Should work fine on interior and exterior nodes. + */ +static int ocfs2_search_extent_list(struct ocfs2_extent_list *el, + u32 v_cluster) +{ + int ret = -1; + int i; + struct ocfs2_extent_rec *rec; + u32 rec_end, rec_start, clusters; - if (ent) { - /* We should never find ourselves straddling an interval */ - if (!ocfs2_extent_rec_contains_clusters(&ent->e_rec, - v_cpos, - count)) - return -ESRCH; + for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { + rec = &el->l_recs[i]; - coff = v_cpos - le32_to_cpu(ent->e_rec.e_cpos); - *p_cpos = ocfs2_blocks_to_clusters(inode->i_sb, - le64_to_cpu(ent->e_rec.e_blkno)) + - coff; + rec_start = le32_to_cpu(rec->e_cpos); + clusters = ocfs2_rec_clusters(el, rec); - if (ret_count) - *ret_count = le32_to_cpu(ent->e_rec.e_clusters) - coff; + rec_end = rec_start + clusters; - return 0; + if (v_cluster >= rec_start && v_cluster < rec_end) { + ret = i; + break; + } } - - return -ENOENT; + return ret; } -#endif /* 0 */ - -int ocfs2_extent_map_get_blocks(struct inode *inode, - u64 v_blkno, int count, - u64 *p_blkno, int *ret_count) +int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, + u32 *p_cluster, u32 *num_clusters, + unsigned int *extent_flags) { - int ret; - u64 boff; - u32 cpos, clusters; - int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); - struct ocfs2_extent_map_entry *ent = NULL; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; + int ret, i; + unsigned int flags = 0; + struct buffer_head *di_bh = NULL; + struct buffer_head *eb_bh = NULL; + struct ocfs2_dinode *di; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_list *el; struct ocfs2_extent_rec *rec; + u32 coff; - *p_blkno = 0; - - cpos = ocfs2_blocks_to_clusters(inode->i_sb, v_blkno); - clusters = ocfs2_blocks_to_clusters(inode->i_sb, - (u64)count + bpc - 1); - if ((cpos + clusters) > OCFS2_I(inode)->ip_clusters) { - ret = -EINVAL; - mlog_errno(ret); - return ret; - } - - if ((cpos + clusters) > em->em_clusters) { - /* - * Size changed underneath us on disk. Drop any - * straddling records and update our idea of - * i_clusters - */ - ocfs2_extent_map_drop(inode, em->em_clusters - 1); - em->em_clusters = OCFS2_I(inode)->ip_clusters; - } + ret = ocfs2_extent_map_lookup(inode, v_cluster, p_cluster, + num_clusters, extent_flags); + if (ret == 0) + goto out; - ret = ocfs2_extent_map_lookup_read(inode, cpos, clusters, &ent); + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno, + &di_bh, OCFS2_BH_CACHED, inode); if (ret) { mlog_errno(ret); - return ret; + goto out; } - if (ent) - { - rec = &ent->e_rec; + di = (struct ocfs2_dinode *) di_bh->b_data; + el = &di->id2.i_list; - /* We should never find ourselves straddling an interval */ - if (!ocfs2_extent_rec_contains_clusters(rec, cpos, clusters)) { - ret = -ESRCH; + if (el->l_tree_depth) { + ret = ocfs2_find_leaf(inode, el, v_cluster, &eb_bh); + if (ret) { mlog_errno(ret); - return ret; + goto out; } - boff = ocfs2_clusters_to_blocks(inode->i_sb, cpos - - le32_to_cpu(rec->e_cpos)); - boff += (v_blkno & (u64)(bpc - 1)); - *p_blkno = le64_to_cpu(rec->e_blkno) + boff; + eb = (struct ocfs2_extent_block *) eb_bh->b_data; + el = &eb->h_list; - if (ret_count) { - *ret_count = ocfs2_clusters_to_blocks(inode->i_sb, - le32_to_cpu(rec->e_clusters)) - boff; + if (el->l_tree_depth) { + ocfs2_error(inode->i_sb, + "Inode %lu has non zero tree depth in " + "leaf block %llu\n", inode->i_ino, + (unsigned long long)eb_bh->b_blocknr); + ret = -EROFS; + goto out; } - - return 0; } - return -ENOENT; -} - -int ocfs2_extent_map_init(struct inode *inode) -{ - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - - em->em_extents = RB_ROOT; - em->em_clusters = 0; - - return 0; -} - -/* Needs the lock */ -static void __ocfs2_extent_map_drop(struct inode *inode, - u32 new_clusters, - struct rb_node **free_head, - struct ocfs2_extent_map_entry **tail_ent) -{ - struct rb_node *node, *next; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - struct ocfs2_extent_map_entry *ent; + i = ocfs2_search_extent_list(el, v_cluster); + if (i == -1) { + /* + * A hole was found. Return some canned values that + * callers can key on. If asked for, num_clusters will + * be populated with the size of the hole. + */ + *p_cluster = 0; + if (num_clusters) { + ret = ocfs2_figure_hole_clusters(inode, el, eb_bh, + v_cluster, + num_clusters); + if (ret) { + mlog_errno(ret); + goto out; + } + } + } else { + rec = &el->l_recs[i]; - *free_head = NULL; + BUG_ON(v_cluster < le32_to_cpu(rec->e_cpos)); - ent = NULL; - node = rb_last(&em->em_extents); - while (node) - { - next = rb_prev(node); + if (!rec->e_blkno) { + ocfs2_error(inode->i_sb, "Inode %lu has bad extent " + "record (%u, %u, 0)", inode->i_ino, + le32_to_cpu(rec->e_cpos), + ocfs2_rec_clusters(el, rec)); + ret = -EROFS; + goto out; + } - ent = rb_entry(node, struct ocfs2_extent_map_entry, - e_node); - if (le32_to_cpu(ent->e_rec.e_cpos) < new_clusters) - break; + coff = v_cluster - le32_to_cpu(rec->e_cpos); - rb_erase(&ent->e_node, &em->em_extents); + *p_cluster = ocfs2_blocks_to_clusters(inode->i_sb, + le64_to_cpu(rec->e_blkno)); + *p_cluster = *p_cluster + coff; - node->rb_right = *free_head; - *free_head = node; + if (num_clusters) + *num_clusters = ocfs2_rec_clusters(el, rec) - coff; - ent = NULL; - node = next; - } + flags = rec->e_flags; - /* Do we have an entry straddling new_clusters? */ - if (tail_ent) { - if (ent && - ((le32_to_cpu(ent->e_rec.e_cpos) + - le32_to_cpu(ent->e_rec.e_clusters)) > new_clusters)) - *tail_ent = ent; - else - *tail_ent = NULL; + ocfs2_extent_map_insert_rec(inode, rec); } -} - -static void __ocfs2_extent_map_drop_cleanup(struct rb_node *free_head) -{ - struct rb_node *node; - struct ocfs2_extent_map_entry *ent; - while (free_head) { - node = free_head; - free_head = node->rb_right; + if (extent_flags) + *extent_flags = flags; - ent = rb_entry(node, struct ocfs2_extent_map_entry, - e_node); - kmem_cache_free(ocfs2_em_ent_cachep, ent); - } +out: + brelse(di_bh); + brelse(eb_bh); + return ret; } /* - * Remove all entries past new_clusters, inclusive of an entry that - * contains new_clusters. This is effectively a cache forget. - * - * If you want to also clip the last extent by some number of clusters, - * you need to call ocfs2_extent_map_trunc(). - * This code does not check or modify ip_clusters. + * This expects alloc_sem to be held. The allocation cannot change at + * all while the map is in the process of being updated. */ -int ocfs2_extent_map_drop(struct inode *inode, u32 new_clusters) +int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno, + u64 *ret_count, unsigned int *extent_flags) { - struct rb_node *free_head = NULL; - struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map; - struct ocfs2_extent_map_entry *ent; - - spin_lock(&OCFS2_I(inode)->ip_lock); + int ret; + int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); + u32 cpos, num_clusters, p_cluster; + u64 boff = 0; - __ocfs2_extent_map_drop(inode, new_clusters, &free_head, &ent); + cpos = ocfs2_blocks_to_clusters(inode->i_sb, v_blkno); - if (ent) { - rb_erase(&ent->e_node, &em->em_extents); - ent->e_node.rb_right = free_head; - free_head = &ent->e_node; + ret = ocfs2_get_clusters(inode, cpos, &p_cluster, &num_clusters, + extent_flags); + if (ret) { + mlog_errno(ret); + goto out; } - spin_unlock(&OCFS2_I(inode)->ip_lock); - - if (free_head) - __ocfs2_extent_map_drop_cleanup(free_head); - - return 0; -} - -/* - * Remove all entries past new_clusters and also clip any extent - * straddling new_clusters, if there is one. This does not check - * or modify ip_clusters - */ -int ocfs2_extent_map_trunc(struct inode *inode, u32 new_clusters) -{ - struct rb_node *free_head = NULL; - struct ocfs2_extent_map_entry *ent = NULL; - - spin_lock(&OCFS2_I(inode)->ip_lock); - - __ocfs2_extent_map_drop(inode, new_clusters, &free_head, &ent); - - if (ent) - ent->e_rec.e_clusters = cpu_to_le32(new_clusters - - le32_to_cpu(ent->e_rec.e_cpos)); - - OCFS2_I(inode)->ip_map.em_clusters = new_clusters; - - spin_unlock(&OCFS2_I(inode)->ip_lock); - - if (free_head) - __ocfs2_extent_map_drop_cleanup(free_head); - - return 0; -} + /* + * p_cluster == 0 indicates a hole. + */ + if (p_cluster) { + boff = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); + boff += (v_blkno & (u64)(bpc - 1)); + } -int __init init_ocfs2_extent_maps(void) -{ - ocfs2_em_ent_cachep = - kmem_cache_create("ocfs2_em_ent", - sizeof(struct ocfs2_extent_map_entry), - 0, SLAB_HWCACHE_ALIGN, NULL, NULL); - if (!ocfs2_em_ent_cachep) - return -ENOMEM; + *p_blkno = boff; - return 0; -} + if (ret_count) { + *ret_count = ocfs2_clusters_to_blocks(inode->i_sb, num_clusters); + *ret_count -= v_blkno & (u64)(bpc - 1); + } -void exit_ocfs2_extent_maps(void) -{ - kmem_cache_destroy(ocfs2_em_ent_cachep); +out: + return ret; } diff --git a/fs/ocfs2/extent_map.h b/fs/ocfs2/extent_map.h index fa3745e..de91e3e 100644 --- a/fs/ocfs2/extent_map.h +++ b/fs/ocfs2/extent_map.h @@ -25,22 +25,29 @@ #ifndef _EXTENT_MAP_H #define _EXTENT_MAP_H -int init_ocfs2_extent_maps(void); -void exit_ocfs2_extent_maps(void); +struct ocfs2_extent_map_item { + unsigned int ei_cpos; + unsigned int ei_phys; + unsigned int ei_clusters; + unsigned int ei_flags; -/* - * EVERY CALL here except _init, _trunc, and _drop expects alloc_sem - * to be held. The allocation cannot change at all while the map is - * in the process of being updated. - */ -int ocfs2_extent_map_init(struct inode *inode); -int ocfs2_extent_map_append(struct inode *inode, - struct ocfs2_extent_rec *rec, - u32 new_clusters); -int ocfs2_extent_map_get_blocks(struct inode *inode, - u64 v_blkno, int count, - u64 *p_blkno, int *ret_count); -int ocfs2_extent_map_drop(struct inode *inode, u32 new_clusters); -int ocfs2_extent_map_trunc(struct inode *inode, u32 new_clusters); + struct list_head ei_list; +}; + +#define OCFS2_MAX_EXTENT_MAP_ITEMS 3 +struct ocfs2_extent_map { + unsigned int em_num_items; + struct list_head em_list; +}; + +void ocfs2_extent_map_init(struct inode *inode); +void ocfs2_extent_map_trunc(struct inode *inode, unsigned int cluster); +void ocfs2_extent_map_insert_rec(struct inode *inode, + struct ocfs2_extent_rec *rec); + +int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, u32 *p_cluster, + u32 *num_clusters, unsigned int *extent_flags); +int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno, + u64 *ret_count, unsigned int *extent_flags); #endif /* _EXTENT_MAP_H */ diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index f2cd3bf..520a2a6 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -33,6 +33,7 @@ #include #include #include #include +#include #define MLOG_MASK_PREFIX ML_INODE #include @@ -215,7 +216,7 @@ int ocfs2_set_inode_size(handle_t *handl mlog_entry_void(); i_size_write(inode, new_i_size); - inode->i_blocks = ocfs2_align_bytes_to_sectors(new_i_size); + inode->i_blocks = ocfs2_inode_sector_count(inode); inode->i_ctime = inode->i_mtime = CURRENT_TIME; status = ocfs2_mark_inode_dirty(handle, inode, fe_bh); @@ -261,6 +262,7 @@ static int ocfs2_orphan_for_truncate(str { int status; handle_t *handle; + struct ocfs2_dinode *di; mlog_entry_void(); @@ -274,12 +276,39 @@ static int ocfs2_orphan_for_truncate(str goto out; } - status = ocfs2_set_inode_size(handle, inode, fe_bh, new_i_size); + status = ocfs2_journal_access(handle, inode, fe_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + goto out_commit; + } + + /* + * Do this before setting i_size. + */ + status = ocfs2_zero_tail_for_truncate(inode, handle, new_i_size); + if (status) { + mlog_errno(status); + goto out_commit; + } + + i_size_write(inode, new_i_size); + inode->i_blocks = ocfs2_align_bytes_to_sectors(new_i_size); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + + di = (struct ocfs2_dinode *) fe_bh->b_data; + di->i_size = cpu_to_le64(new_i_size); + di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec); + di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + + status = ocfs2_journal_dirty(handle, fe_bh); if (status < 0) mlog_errno(status); +out_commit: ocfs2_commit_trans(osb, handle); out: + mlog_exit(status); return status; } @@ -342,19 +371,6 @@ static int ocfs2_truncate_file(struct in mlog_errno(status); goto bail; } - ocfs2_data_unlock(inode, 1); - - if (le32_to_cpu(fe->i_clusters) == - ocfs2_clusters_for_bytes(osb->sb, new_i_size)) { - mlog(0, "fe->i_clusters = %u, so we do a simple truncate\n", - fe->i_clusters); - /* No allocation change is required, so lets fast path - * this truncate. */ - status = ocfs2_simple_size_update(inode, di_bh, new_i_size); - if (status < 0) - mlog_errno(status); - goto bail; - } /* alright, we're going to need to do a full blown alloc size * change. Orphan the inode so that recovery can complete the @@ -363,22 +379,25 @@ static int ocfs2_truncate_file(struct in status = ocfs2_orphan_for_truncate(osb, inode, di_bh, new_i_size); if (status < 0) { mlog_errno(status); - goto bail; + goto bail_unlock_data; } status = ocfs2_prepare_truncate(osb, inode, di_bh, &tc); if (status < 0) { mlog_errno(status); - goto bail; + goto bail_unlock_data; } status = ocfs2_commit_truncate(osb, inode, di_bh, tc); if (status < 0) { mlog_errno(status); - goto bail; + goto bail_unlock_data; } /* TODO: orphan dir cleanup here. */ +bail_unlock_data: + ocfs2_data_unlock(inode, 1); + bail: mlog_exit(status); @@ -397,6 +416,7 @@ bail: */ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, struct inode *inode, + u32 *logical_offset, u32 clusters_to_add, struct buffer_head *fe_bh, handle_t *handle, @@ -460,18 +480,14 @@ int ocfs2_do_extend_allocation(struct oc block = ocfs2_clusters_to_blocks(osb->sb, bit_off); mlog(0, "Allocating %u clusters at block %u for inode %llu\n", num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno); - status = ocfs2_insert_extent(osb, handle, inode, fe_bh, block, - num_bits, meta_ac); + status = ocfs2_insert_extent(osb, handle, inode, fe_bh, + *logical_offset, block, num_bits, + meta_ac); if (status < 0) { mlog_errno(status); goto leave; } - le32_add_cpu(&fe->i_clusters, num_bits); - spin_lock(&OCFS2_I(inode)->ip_lock); - OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters); - spin_unlock(&OCFS2_I(inode)->ip_lock); - status = ocfs2_journal_dirty(handle, fe_bh); if (status < 0) { mlog_errno(status); @@ -479,6 +495,7 @@ int ocfs2_do_extend_allocation(struct oc } clusters_to_add -= num_bits; + *logical_offset += num_bits; if (clusters_to_add) { mlog(0, "need to alloc once more, clusters = %u, wanted = " @@ -494,14 +511,87 @@ leave: return status; } +/* + * For a given allocation, determine which allocators will need to be + * accessed, and lock them, reserving the appropriate number of bits. + * + * Called from ocfs2_extend_allocation() for file systems which don't + * support holes, and from ocfs2_write() for file systems which + * understand sparse inodes. + */ +int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, + u32 clusters_to_add, + struct ocfs2_alloc_context **data_ac, + struct ocfs2_alloc_context **meta_ac) +{ + int ret, num_free_extents; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + *meta_ac = NULL; + *data_ac = NULL; + + mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, " + "clusters_to_add = %u\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, i_size_read(inode), + le32_to_cpu(di->i_clusters), clusters_to_add); + + num_free_extents = ocfs2_num_free_extents(osb, inode, di); + if (num_free_extents < 0) { + ret = num_free_extents; + mlog_errno(ret); + goto out; + } + + /* + * Sparse allocation file systems need to be more conservative + * with reserving room for expansion - the actual allocation + * happens while we've got a journal handle open so re-taking + * a cluster lock (because we ran out of room for another + * extent) will violate ordering rules. + * + * Most of the time we'll only be seeing this 1 cluster at a time + * anyway. + */ + if (!num_free_extents || + (ocfs2_sparse_alloc(osb) && num_free_extents < clusters_to_add)) { + ret = ocfs2_reserve_new_metadata(osb, di, meta_ac); + if (ret < 0) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + } + + ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac); + if (ret < 0) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + +out: + if (ret) { + if (*meta_ac) { + ocfs2_free_alloc_context(*meta_ac); + *meta_ac = NULL; + } + + /* + * We cannot have an error and a non null *data_ac. + */ + } + + return ret; +} + static int ocfs2_extend_allocation(struct inode *inode, u32 clusters_to_add) { int status = 0; int restart_func = 0; int drop_alloc_sem = 0; - int credits, num_free_extents; - u32 prev_clusters; + int credits; + u32 prev_clusters, logical_start; struct buffer_head *bh = NULL; struct ocfs2_dinode *fe = NULL; handle_t *handle = NULL; @@ -512,6 +602,12 @@ static int ocfs2_extend_allocation(struc mlog_entry("(clusters_to_add = %u)\n", clusters_to_add); + /* + * This function only exists for file systems which don't + * support holes. + */ + BUG_ON(ocfs2_sparse_alloc(osb)); + status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh, OCFS2_BH_CACHED, inode); if (status < 0) { @@ -526,39 +622,11 @@ static int ocfs2_extend_allocation(struc goto leave; } + logical_start = OCFS2_I(inode)->ip_clusters; + restart_all: BUG_ON(le32_to_cpu(fe->i_clusters) != OCFS2_I(inode)->ip_clusters); - mlog(0, "extend inode %llu, i_size = %lld, fe->i_clusters = %u, " - "clusters_to_add = %u\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, i_size_read(inode), - fe->i_clusters, clusters_to_add); - - num_free_extents = ocfs2_num_free_extents(osb, - inode, - fe); - if (num_free_extents < 0) { - status = num_free_extents; - mlog_errno(status); - goto leave; - } - - if (!num_free_extents) { - status = ocfs2_reserve_new_metadata(osb, fe, &meta_ac); - if (status < 0) { - if (status != -ENOSPC) - mlog_errno(status); - goto leave; - } - } - - status = ocfs2_reserve_clusters(osb, clusters_to_add, &data_ac); - if (status < 0) { - if (status != -ENOSPC) - mlog_errno(status); - goto leave; - } - /* blocks peope in read/write from reading our allocation * until we're done changing it. We depend on i_mutex to block * other extend/truncate calls while we're here. Ordering wrt @@ -566,6 +634,13 @@ restart_all: down_write(&OCFS2_I(inode)->ip_alloc_sem); drop_alloc_sem = 1; + status = ocfs2_lock_allocators(inode, fe, clusters_to_add, &data_ac, + &meta_ac); + if (status) { + mlog_errno(status); + goto leave; + } + credits = ocfs2_calc_extend_credits(osb->sb, fe, clusters_to_add); handle = ocfs2_start_trans(osb, credits); if (IS_ERR(handle)) { @@ -590,6 +665,7 @@ restarted_transaction: status = ocfs2_do_extend_allocation(osb, inode, + &logical_start, clusters_to_add, bh, handle, @@ -778,7 +854,7 @@ static int ocfs2_extend_file(struct inod size_t tail_to_skip) { int ret = 0; - u32 clusters_to_add; + u32 clusters_to_add = 0; BUG_ON(!tail_to_skip && !di_bh); @@ -790,6 +866,11 @@ static int ocfs2_extend_file(struct inod goto out; BUG_ON(new_i_size < i_size_read(inode)); + if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) { + BUG_ON(tail_to_skip != 0); + goto out_update_size; + } + clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size) - OCFS2_I(inode)->ip_clusters; @@ -825,6 +906,7 @@ static int ocfs2_extend_file(struct inod goto out_unlock; } +out_update_size: if (!tail_to_skip) { /* We're being called from ocfs2_setattr() which wants * us to update i_size */ @@ -834,7 +916,8 @@ static int ocfs2_extend_file(struct inod } out_unlock: - ocfs2_data_unlock(inode, 1); + if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + ocfs2_data_unlock(inode, 1); out: return ret; @@ -972,7 +1055,8 @@ int ocfs2_permission(struct inode *inode ret = ocfs2_meta_lock(inode, NULL, 0); if (ret) { - mlog_errno(ret); + if (ret != -ENOENT) + mlog_errno(ret); goto out; } @@ -1035,10 +1119,49 @@ out: return ret; } +/* + * Will look for holes and unwritten extents in the range starting at + * pos for count bytes (inclusive). + */ +static int ocfs2_check_range_for_holes(struct inode *inode, loff_t pos, + size_t count) +{ + int ret = 0; + unsigned int extent_flags; + u32 cpos, clusters, extent_len, phys_cpos; + struct super_block *sb = inode->i_sb; + + cpos = pos >> OCFS2_SB(sb)->s_clustersize_bits; + clusters = ocfs2_clusters_for_bytes(sb, pos + count) - cpos; + + while (clusters) { + ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, &extent_len, + &extent_flags); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + if (phys_cpos == 0 || (extent_flags & OCFS2_EXT_UNWRITTEN)) { + ret = 1; + break; + } + + if (extent_len > clusters) + extent_len = clusters; + + clusters -= extent_len; + cpos += extent_len; + } +out: + return ret; +} + static int ocfs2_prepare_inode_for_write(struct dentry *dentry, loff_t *ppos, size_t count, - int appending) + int appending, + int *direct_io) { int ret = 0, meta_level = appending; struct inode *inode = dentry->d_inode; @@ -1089,6 +1212,49 @@ static int ocfs2_prepare_inode_for_write } else { saved_pos = *ppos; } + + if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) { + loff_t end = saved_pos + count; + + /* + * Skip the O_DIRECT checks if we don't need + * them. + */ + if (!direct_io || !(*direct_io)) + break; + + /* + * Allowing concurrent direct writes means + * i_size changes wouldn't be synchronized, so + * one node could wind up truncating another + * nodes writes. + */ + if (end > i_size_read(inode)) { + *direct_io = 0; + break; + } + + /* + * We don't fill holes during direct io, so + * check for them here. If any are found, the + * caller will have to retake some cluster + * locks and initiate the io as buffered. + */ + ret = ocfs2_check_range_for_holes(inode, saved_pos, + count); + if (ret == 1) { + *direct_io = 0; + ret = 0; + } else if (ret < 0) + mlog_errno(ret); + break; + } + + /* + * The rest of this loop is concerned with legacy file + * systems which don't support sparse files. + */ + newsize = count + saved_pos; mlog(0, "pos=%lld newsize=%lld cursize=%lld\n", @@ -1141,55 +1307,264 @@ out: return ret; } +static inline void +ocfs2_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) +{ + const struct iovec *iov = *iovp; + size_t base = *basep; + + do { + int copy = min(bytes, iov->iov_len - base); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + base = 0; + } + } while (bytes); + *iovp = iov; + *basep = base; +} + +static struct page * ocfs2_get_write_source(struct ocfs2_buffered_write_priv *bp, + const struct iovec *cur_iov, + size_t iov_offset) +{ + int ret; + char *buf; + struct page *src_page = NULL; + + buf = cur_iov->iov_base + iov_offset; + + if (!segment_eq(get_fs(), KERNEL_DS)) { + /* + * Pull in the user page. We want to do this outside + * of the meta data locks in order to preserve locking + * order in case of page fault. + */ + ret = get_user_pages(current, current->mm, + (unsigned long)buf & PAGE_CACHE_MASK, 1, + 0, 0, &src_page, NULL); + if (ret == 1) + bp->b_src_buf = kmap(src_page); + else + src_page = ERR_PTR(-EFAULT); + } else { + bp->b_src_buf = buf; + } + + return src_page; +} + +static void ocfs2_put_write_source(struct ocfs2_buffered_write_priv *bp, + struct page *page) +{ + if (page) { + kunmap(page); + page_cache_release(page); + } +} + +static ssize_t ocfs2_file_buffered_write(struct file *file, loff_t *ppos, + const struct iovec *iov, + unsigned long nr_segs, + size_t count, + ssize_t o_direct_written) +{ + int ret = 0; + ssize_t copied, total = 0; + size_t iov_offset = 0; + const struct iovec *cur_iov = iov; + struct ocfs2_buffered_write_priv bp; + struct page *page; + + /* + * handle partial DIO write. Adjust cur_iov if needed. + */ + ocfs2_set_next_iovec(&cur_iov, &iov_offset, o_direct_written); + + do { + bp.b_cur_off = iov_offset; + bp.b_cur_iov = cur_iov; + + page = ocfs2_get_write_source(&bp, cur_iov, iov_offset); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + goto out; + } + + copied = ocfs2_buffered_write_cluster(file, *ppos, count, + ocfs2_map_and_write_user_data, + &bp); + + ocfs2_put_write_source(&bp, page); + + if (copied < 0) { + mlog_errno(copied); + ret = copied; + goto out; + } + + total += copied; + *ppos = *ppos + copied; + count -= copied; + + ocfs2_set_next_iovec(&cur_iov, &iov_offset, copied); + } while(count); + +out: + return total ? total : ret; +} + +static int ocfs2_check_iovec(const struct iovec *iov, size_t *counted, + unsigned long *nr_segs) +{ + size_t ocount; /* original count */ + unsigned long seg; + + ocount = 0; + for (seg = 0; seg < *nr_segs; seg++) { + const struct iovec *iv = &iov[seg]; + + /* + * If any segment has a negative length, or the cumulative + * length ever wraps negative then return -EINVAL. + */ + ocount += iv->iov_len; + if (unlikely((ssize_t)(ocount|iv->iov_len) < 0)) + return -EINVAL; + if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len)) + continue; + if (seg == 0) + return -EFAULT; + *nr_segs = seg; + ocount -= iv->iov_len; /* This segment is no good */ + break; + } + + *counted = ocount; + return 0; +} + static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { - int ret, rw_level, have_alloc_sem = 0; - struct file *filp = iocb->ki_filp; - struct inode *inode = filp->f_path.dentry->d_inode; - int appending = filp->f_flags & O_APPEND ? 1 : 0; - - mlog_entry("(0x%p, %u, '%.*s')\n", filp, + int ret, direct_io, appending, rw_level, have_alloc_sem = 0; + int can_do_direct, sync = 0; + ssize_t written = 0; + size_t ocount; /* original count */ + size_t count; /* after file limit checks */ + loff_t *ppos = &iocb->ki_pos; + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_path.dentry->d_inode; + + mlog_entry("(0x%p, %u, '%.*s')\n", file, (unsigned int)nr_segs, - filp->f_path.dentry->d_name.len, - filp->f_path.dentry->d_name.name); + file->f_path.dentry->d_name.len, + file->f_path.dentry->d_name.name); - /* happy write of zero bytes */ if (iocb->ki_left == 0) return 0; + ret = ocfs2_check_iovec(iov, &ocount, &nr_segs); + if (ret) + return ret; + + count = ocount; + + vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); + + appending = file->f_flags & O_APPEND ? 1 : 0; + direct_io = file->f_flags & O_DIRECT ? 1 : 0; + mutex_lock(&inode->i_mutex); + +relock: /* to match setattr's i_mutex -> i_alloc_sem -> rw_lock ordering */ - if (filp->f_flags & O_DIRECT) { - have_alloc_sem = 1; + if (direct_io) { down_read(&inode->i_alloc_sem); + have_alloc_sem = 1; } /* concurrent O_DIRECT writes are allowed */ - rw_level = (filp->f_flags & O_DIRECT) ? 0 : 1; + rw_level = !direct_io; ret = ocfs2_rw_lock(inode, rw_level); if (ret < 0) { - rw_level = -1; mlog_errno(ret); - goto out; + goto out_sems; } - ret = ocfs2_prepare_inode_for_write(filp->f_path.dentry, &iocb->ki_pos, - iocb->ki_left, appending); + can_do_direct = direct_io; + ret = ocfs2_prepare_inode_for_write(file->f_path.dentry, ppos, + iocb->ki_left, appending, + &can_do_direct); if (ret < 0) { mlog_errno(ret); goto out; } - /* communicate with ocfs2_dio_end_io */ - ocfs2_iocb_set_rw_locked(iocb); + /* + * We can't complete the direct I/O as requested, fall back to + * buffered I/O. + */ + if (direct_io && !can_do_direct) { + ocfs2_rw_unlock(inode, rw_level); + up_read(&inode->i_alloc_sem); + + have_alloc_sem = 0; + rw_level = -1; - ret = generic_file_aio_write_nolock(iocb, iov, nr_segs, iocb->ki_pos); + direct_io = 0; + sync = 1; + goto relock; + } + + if (!sync && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) + sync = 1; + + /* + * XXX: Is it ok to execute these checks a second time? + */ + ret = generic_write_checks(file, ppos, &count, S_ISBLK(inode->i_mode)); + if (ret) + goto out; + + /* + * Set pos so that sync_page_range_nolock() below understands + * where to start from. We might've moved it around via the + * calls above. The range we want to actually sync starts from + * *ppos here. + * + */ + pos = *ppos; + + /* communicate with ocfs2_dio_end_io */ + ocfs2_iocb_set_rw_locked(iocb, rw_level); + + if (direct_io) { + written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos, + ppos, count, ocount); + if (written < 0) { + ret = written; + goto out_dio; + } + } else { + written = ocfs2_file_buffered_write(file, ppos, iov, nr_segs, + count, written); + if (written < 0) { + ret = written; + if (ret != -EFAULT || ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + } +out_dio: /* buffered aio wouldn't have proper lock coverage today */ - BUG_ON(ret == -EIOCBQUEUED && !(filp->f_flags & O_DIRECT)); + BUG_ON(ret == -EIOCBQUEUED && !(file->f_flags & O_DIRECT)); /* * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io @@ -1207,13 +1582,102 @@ static ssize_t ocfs2_file_aio_write(stru } out: + if (rw_level != -1) + ocfs2_rw_unlock(inode, rw_level); + +out_sems: if (have_alloc_sem) up_read(&inode->i_alloc_sem); - if (rw_level != -1) - ocfs2_rw_unlock(inode, rw_level); + + if (written > 0 && sync) { + ssize_t err; + + err = sync_page_range_nolock(inode, file->f_mapping, pos, count); + if (err < 0) + written = err; + } + mutex_unlock(&inode->i_mutex); mlog_exit(ret); + return written ? written : ret; +} + +static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe, + struct pipe_buffer *buf, + struct splice_desc *sd) +{ + int ret, count, total = 0; + ssize_t copied = 0; + struct ocfs2_splice_write_priv sp; + + ret = buf->ops->pin(pipe, buf); + if (ret) + goto out; + + sp.s_sd = sd; + sp.s_buf = buf; + sp.s_pipe = pipe; + sp.s_offset = sd->pos & ~PAGE_CACHE_MASK; + sp.s_buf_offset = buf->offset; + + count = sd->len; + if (count + sp.s_offset > PAGE_CACHE_SIZE) + count = PAGE_CACHE_SIZE - sp.s_offset; + + do { + /* + * splice wants us to copy up to one page at a + * time. For pagesize > cluster size, this means we + * might enter ocfs2_buffered_write_cluster() more + * than once, so keep track of our progress here. + */ + copied = ocfs2_buffered_write_cluster(sd->file, + (loff_t)sd->pos + total, + count, + ocfs2_map_and_write_splice_data, + &sp); + if (copied < 0) { + mlog_errno(copied); + ret = copied; + goto out; + } + + count -= copied; + sp.s_offset += copied; + sp.s_buf_offset += copied; + total += copied; + } while (count); + + ret = 0; +out: + + return total ? total : ret; +} + +static ssize_t __ocfs2_file_splice_write(struct pipe_inode_info *pipe, + struct file *out, + loff_t *ppos, + size_t len, + unsigned int flags) +{ + int ret, err; + struct address_space *mapping = out->f_mapping; + struct inode *inode = mapping->host; + + ret = __splice_from_pipe(pipe, out, ppos, len, flags, + ocfs2_splice_write_actor); + if (ret > 0) { + *ppos += ret; + + if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) { + err = generic_osync_inode(inode, mapping, + OSYNC_METADATA|OSYNC_DATA); + if (err) + ret = err; + } + } + return ret; } @@ -1239,14 +1703,15 @@ static ssize_t ocfs2_file_splice_write(s goto out; } - ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0); + ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0, + NULL); if (ret < 0) { mlog_errno(ret); goto out_unlock; } /* ok, we're done with i_size and alloc work */ - ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags); + ret = __ocfs2_file_splice_write(pipe, out, ppos, len, flags); out_unlock: ocfs2_rw_unlock(inode, 1); @@ -1323,7 +1788,7 @@ static ssize_t ocfs2_file_aio_read(struc } rw_level = 0; /* communicate with ocfs2_dio_end_io */ - ocfs2_iocb_set_rw_locked(iocb); + ocfs2_iocb_set_rw_locked(iocb, rw_level); } /* diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index cc973f0..2c4460f 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -39,12 +39,17 @@ enum ocfs2_alloc_restarted { }; int ocfs2_do_extend_allocation(struct ocfs2_super *osb, struct inode *inode, + u32 *cluster_start, u32 clusters_to_add, struct buffer_head *fe_bh, handle_t *handle, struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, enum ocfs2_alloc_restarted *reason); +int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, + u32 clusters_to_add, + struct ocfs2_alloc_context **data_ac, + struct ocfs2_alloc_context **meta_ac); int ocfs2_setattr(struct dentry *dentry, struct iattr *attr); int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 28ab56f..21a6050 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -89,24 +89,6 @@ void ocfs2_set_inode_flags(struct inode inode->i_flags |= S_DIRSYNC; } -struct inode *ocfs2_ilookup_for_vote(struct ocfs2_super *osb, - u64 blkno, - int delete_vote) -{ - struct ocfs2_find_inode_args args; - - /* ocfs2_ilookup_for_vote should *only* be called from the - * vote thread */ - BUG_ON(current != osb->vote_task); - - args.fi_blkno = blkno; - args.fi_flags = OCFS2_FI_FLAG_NOWAIT; - if (delete_vote) - args.fi_flags |= OCFS2_FI_FLAG_DELETE; - args.fi_ino = ino_from_blkno(osb->sb, blkno); - return ilookup5(osb->sb, args.fi_ino, ocfs2_find_actor, &args); -} - struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, int flags) { struct inode *inode = NULL; @@ -182,28 +164,6 @@ static int ocfs2_find_actor(struct inode if (oi->ip_blkno != args->fi_blkno) goto bail; - /* OCFS2_FI_FLAG_NOWAIT is *only* set from - * ocfs2_ilookup_for_vote which won't create an inode for one - * that isn't found. The vote thread which doesn't want to get - * an inode which is in the process of going away - otherwise - * the call to __wait_on_freeing_inode in find_inode_fast will - * cause it to deadlock on an inode which may be waiting on a - * vote (or lock release) in delete_inode */ - if ((args->fi_flags & OCFS2_FI_FLAG_NOWAIT) && - (inode->i_state & (I_FREEING|I_CLEAR))) { - /* As stated above, we're not going to return an - * inode. In the case of a delete vote, the voting - * code is going to signal the other node to go - * ahead. Mark that state here, so this freeing inode - * has the state when it gets to delete_inode. */ - if (args->fi_flags & OCFS2_FI_FLAG_DELETE) { - spin_lock(&oi->ip_lock); - ocfs2_mark_inode_remotely_deleted(inode); - spin_unlock(&oi->ip_lock); - } - goto bail; - } - ret = 1; bail: mlog_exit(ret); @@ -261,6 +221,9 @@ int ocfs2_populate_inode(struct inode *i goto bail; } + OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters); + OCFS2_I(inode)->ip_attr = le32_to_cpu(fe->i_attr); + inode->i_version = 1; inode->i_generation = le32_to_cpu(fe->i_generation); inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); @@ -272,8 +235,7 @@ int ocfs2_populate_inode(struct inode *i if (S_ISLNK(inode->i_mode) && !fe->i_clusters) inode->i_blocks = 0; else - inode->i_blocks = - ocfs2_align_bytes_to_sectors(le64_to_cpu(fe->i_size)); + inode->i_blocks = ocfs2_inode_sector_count(inode); inode->i_mapping->a_ops = &ocfs2_aops; inode->i_atime.tv_sec = le64_to_cpu(fe->i_atime); inode->i_atime.tv_nsec = le32_to_cpu(fe->i_atime_nsec); @@ -288,10 +250,6 @@ int ocfs2_populate_inode(struct inode *i (unsigned long long)OCFS2_I(inode)->ip_blkno, (unsigned long long)fe->i_blkno); - OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters); - OCFS2_I(inode)->ip_orphaned_slot = OCFS2_INVALID_SLOT; - OCFS2_I(inode)->ip_attr = le32_to_cpu(fe->i_attr); - inode->i_nlink = le16_to_cpu(fe->i_links_count); if (fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) @@ -347,6 +305,9 @@ int ocfs2_populate_inode(struct inode *i ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_meta_lockres, OCFS2_LOCK_TYPE_META, 0, inode); + + ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_open_lockres, + OCFS2_LOCK_TYPE_OPEN, 0, inode); } ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_rw_lockres, @@ -421,7 +382,7 @@ static int ocfs2_read_locked_inode(struc * cluster lock before trusting anything anyway. */ can_lock = !(args->fi_flags & OCFS2_FI_FLAG_SYSFILE) - && !(args->fi_flags & OCFS2_FI_FLAG_NOLOCK) + && !(args->fi_flags & OCFS2_FI_FLAG_ORPHAN_RECOVERY) && !ocfs2_mount_local(osb); /* @@ -438,7 +399,17 @@ static int ocfs2_read_locked_inode(struc OCFS2_LOCK_TYPE_META, generation, inode); + ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_open_lockres, + OCFS2_LOCK_TYPE_OPEN, + 0, inode); + if (can_lock) { + status = ocfs2_open_lock(inode); + if (status) { + make_bad_inode(inode); + mlog_errno(status); + return status; + } status = ocfs2_meta_lock(inode, NULL, 0); if (status) { make_bad_inode(inode); @@ -447,6 +418,14 @@ static int ocfs2_read_locked_inode(struc } } + if (args->fi_flags & OCFS2_FI_FLAG_ORPHAN_RECOVERY) { + status = ocfs2_try_open_lock(inode, 0); + if (status) { + make_bad_inode(inode); + return status; + } + } + status = ocfs2_read_block(osb, args->fi_blkno, &bh, 0, can_lock ? inode : NULL); if (status < 0) { @@ -507,50 +486,56 @@ static int ocfs2_truncate_for_delete(str struct buffer_head *fe_bh) { int status = 0; - handle_t *handle = NULL; struct ocfs2_truncate_context *tc = NULL; struct ocfs2_dinode *fe; + handle_t *handle = NULL; mlog_entry_void(); fe = (struct ocfs2_dinode *) fe_bh->b_data; - /* zero allocation, zero truncate :) */ - if (!fe->i_clusters) - goto bail; + if (fe->i_clusters) { + handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); + if (IS_ERR(handle)) { + status = PTR_ERR(handle); + mlog_errno(status); + goto out; + } - handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); - if (IS_ERR(handle)) { - status = PTR_ERR(handle); - handle = NULL; - mlog_errno(status); - goto bail; - } + status = ocfs2_journal_access(handle, inode, fe_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + goto out; + } - status = ocfs2_set_inode_size(handle, inode, fe_bh, 0ULL); - if (status < 0) { - mlog_errno(status); - goto bail; - } + i_size_write(inode, 0); - ocfs2_commit_trans(osb, handle); - handle = NULL; + status = ocfs2_mark_inode_dirty(handle, inode, fe_bh); + if (status < 0) { + mlog_errno(status); + goto out; + } - status = ocfs2_prepare_truncate(osb, inode, fe_bh, &tc); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_commit_trans(osb, handle); + handle = NULL; - status = ocfs2_commit_truncate(osb, inode, fe_bh, tc); - if (status < 0) { - mlog_errno(status); - goto bail; + status = ocfs2_prepare_truncate(osb, inode, fe_bh, &tc); + if (status < 0) { + mlog_errno(status); + goto out; + } + + status = ocfs2_commit_truncate(osb, inode, fe_bh, tc); + if (status < 0) { + mlog_errno(status); + goto out; + } } -bail: + +out: if (handle) ocfs2_commit_trans(osb, handle); - mlog_exit(status); return status; } @@ -678,10 +663,10 @@ static int ocfs2_wipe_inode(struct inode struct inode *orphan_dir_inode = NULL; struct buffer_head *orphan_dir_bh = NULL; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_dinode *di; - /* We've already voted on this so it should be readonly - no - * spinlock needed. */ - orphaned_slot = OCFS2_I(inode)->ip_orphaned_slot; + di = (struct ocfs2_dinode *) di_bh->b_data; + orphaned_slot = le16_to_cpu(di->i_orphaned_slot); status = ocfs2_check_orphan_recovery_state(osb, orphaned_slot); if (status) @@ -839,11 +824,20 @@ static int ocfs2_query_inode_wipe(struct goto bail; } - status = ocfs2_request_delete_vote(inode); - /* -EBUSY means that other nodes are still using the - * inode. We're done here though, so avoid doing anything on - * disk and let them worry about deleting it. */ - if (status == -EBUSY) { + /* + * This is how ocfs2 determines whether an inode is still live + * within the cluster. Every node takes a shared read lock on + * the inode open lock in ocfs2_read_locked_inode(). When we + * get to ->delete_inode(), each node tries to convert it's + * lock to an exclusive. Trylocks are serialized by the inode + * meta data lock. If the upconvert suceeds, we know the inode + * is no longer live and can be deleted. + * + * Though we call this with the meta data lock held, the + * trylock keeps us from ABBA deadlock. + */ + status = ocfs2_try_open_lock(inode, 1); + if (status == -EAGAIN) { status = 0; mlog(0, "Skipping delete of %llu because it is in use on" "other nodes\n", (unsigned long long)oi->ip_blkno); @@ -854,21 +848,10 @@ static int ocfs2_query_inode_wipe(struct goto bail; } - spin_lock(&oi->ip_lock); - if (oi->ip_orphaned_slot == OCFS2_INVALID_SLOT) { - /* Nobody knew which slot this inode was orphaned - * into. This may happen during node death and - * recovery knows how to clean it up so we can safely - * ignore this inode for now on. */ - mlog(0, "Nobody knew where inode %llu was orphaned!\n", - (unsigned long long)oi->ip_blkno); - } else { - *wipe = 1; - - mlog(0, "Inode %llu is ok to wipe from orphan dir %d\n", - (unsigned long long)oi->ip_blkno, oi->ip_orphaned_slot); - } - spin_unlock(&oi->ip_lock); + *wipe = 1; + mlog(0, "Inode %llu is ok to wipe from orphan dir %u\n", + (unsigned long long)oi->ip_blkno, + le16_to_cpu(di->i_orphaned_slot)); bail: return status; @@ -1001,11 +984,16 @@ void ocfs2_clear_inode(struct inode *ino mlog_bug_on_msg(OCFS2_SB(inode->i_sb) == NULL, "Inode=%lu\n", inode->i_ino); + /* For remove delete_inode vote, we hold open lock before, + * now it is time to unlock PR and EX open locks. */ + ocfs2_open_unlock(inode); + /* Do these before all the other work so that we don't bounce * the vote thread while waiting to destroy the locks. */ ocfs2_mark_lockres_freeing(&oi->ip_rw_lockres); ocfs2_mark_lockres_freeing(&oi->ip_meta_lockres); ocfs2_mark_lockres_freeing(&oi->ip_data_lockres); + ocfs2_mark_lockres_freeing(&oi->ip_open_lockres); /* We very well may get a clear_inode before all an inodes * metadata has hit disk. Of course, we can't drop any cluster @@ -1020,8 +1008,7 @@ void ocfs2_clear_inode(struct inode *ino "Clear inode of %llu, inode has io markers\n", (unsigned long long)oi->ip_blkno); - ocfs2_extent_map_drop(inode, 0); - ocfs2_extent_map_init(inode); + ocfs2_extent_map_trunc(inode, 0); status = ocfs2_drop_inode_locks(inode); if (status < 0) @@ -1030,6 +1017,7 @@ void ocfs2_clear_inode(struct inode *ino ocfs2_lock_res_free(&oi->ip_rw_lockres); ocfs2_lock_res_free(&oi->ip_meta_lockres); ocfs2_lock_res_free(&oi->ip_data_lockres); + ocfs2_lock_res_free(&oi->ip_open_lockres); ocfs2_metadata_cache_purge(inode); @@ -1086,9 +1074,6 @@ void ocfs2_drop_inode(struct inode *inod mlog(0, "Drop inode %llu, nlink = %u, ip_flags = 0x%x\n", (unsigned long long)oi->ip_blkno, inode->i_nlink, oi->ip_flags); - /* Testing ip_orphaned_slot here wouldn't work because we may - * not have gotten a delete_inode vote from any other nodes - * yet. */ if (oi->ip_flags & OCFS2_INODE_MAYBE_ORPHANED) generic_delete_inode(inode); else @@ -1121,8 +1106,8 @@ struct buffer_head *ocfs2_bread(struct i return NULL; } - tmperr = ocfs2_extent_map_get_blocks(inode, block, 1, - &p_blkno, NULL); + tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, + NULL); if (tmperr < 0) { mlog_errno(tmperr); goto fail; @@ -1259,7 +1244,7 @@ void ocfs2_refresh_inode(struct inode *i if (S_ISLNK(inode->i_mode) && le32_to_cpu(fe->i_clusters) == 0) inode->i_blocks = 0; else - inode->i_blocks = ocfs2_align_bytes_to_sectors(i_size_read(inode)); + inode->i_blocks = ocfs2_inode_sector_count(inode); inode->i_atime.tv_sec = le64_to_cpu(fe->i_atime); inode->i_atime.tv_nsec = le32_to_cpu(fe->i_atime_nsec); inode->i_mtime.tv_sec = le64_to_cpu(fe->i_mtime); diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 1a7dd29..03ae075 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -26,6 +26,8 @@ #ifndef OCFS2_INODE_H #define OCFS2_INODE_H +#include "extent_map.h" + /* OCFS2 Inode Private Data */ struct ocfs2_inode_info { @@ -34,6 +36,7 @@ struct ocfs2_inode_info struct ocfs2_lock_res ip_rw_lockres; struct ocfs2_lock_res ip_meta_lockres; struct ocfs2_lock_res ip_data_lockres; + struct ocfs2_lock_res ip_open_lockres; /* protects allocation changes on this inode. */ struct rw_semaphore ip_alloc_sem; @@ -42,9 +45,7 @@ struct ocfs2_inode_info spinlock_t ip_lock; u32 ip_open_count; u32 ip_clusters; - struct ocfs2_extent_map ip_map; struct list_head ip_io_markers; - int ip_orphaned_slot; struct mutex ip_io_mutex; @@ -64,6 +65,8 @@ struct ocfs2_inode_info struct ocfs2_caching_info ip_metadata_cache; + struct ocfs2_extent_map ip_extent_map; + struct inode vfs_inode; }; @@ -117,14 +120,9 @@ void ocfs2_delete_inode(struct inode *in void ocfs2_drop_inode(struct inode *inode); /* Flags for ocfs2_iget() */ -#define OCFS2_FI_FLAG_NOWAIT 0x1 -#define OCFS2_FI_FLAG_DELETE 0x2 -#define OCFS2_FI_FLAG_SYSFILE 0x4 -#define OCFS2_FI_FLAG_NOLOCK 0x8 +#define OCFS2_FI_FLAG_SYSFILE 0x4 +#define OCFS2_FI_FLAG_ORPHAN_RECOVERY 0x8 struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 feoff, int flags); -struct inode *ocfs2_ilookup_for_vote(struct ocfs2_super *osb, - u64 blkno, - int delete_vote); int ocfs2_inode_init_private(struct inode *inode); int ocfs2_inode_revalidate(struct dentry *dentry); int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, @@ -144,4 +142,11 @@ int ocfs2_aio_write(struct file *file, s void ocfs2_set_inode_flags(struct inode *inode); +static inline blkcnt_t ocfs2_inode_sector_count(struct inode *inode) +{ + int c_to_s_bits = OCFS2_SB(inode->i_sb)->s_clustersize_bits - 9; + + return (blkcnt_t)(OCFS2_I(inode)->ip_clusters << c_to_s_bits); +} + #endif /* OCFS2_INODE_H */ diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 825cb0a..5a8a90d 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -649,29 +649,20 @@ bail: static int ocfs2_force_read_journal(struct inode *inode) { int status = 0; - int i, p_blocks; - u64 v_blkno, p_blkno; -#define CONCURRENT_JOURNAL_FILL 32 + int i; + u64 v_blkno, p_blkno, p_blocks, num_blocks; +#define CONCURRENT_JOURNAL_FILL 32ULL struct buffer_head *bhs[CONCURRENT_JOURNAL_FILL]; mlog_entry_void(); - BUG_ON(inode->i_blocks != - ocfs2_align_bytes_to_sectors(i_size_read(inode))); - memset(bhs, 0, sizeof(struct buffer_head *) * CONCURRENT_JOURNAL_FILL); - mlog(0, "Force reading %llu blocks\n", - (unsigned long long)(inode->i_blocks >> - (inode->i_sb->s_blocksize_bits - 9))); - + num_blocks = ocfs2_blocks_for_bytes(inode->i_sb, inode->i_size); v_blkno = 0; - while (v_blkno < - (inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9))) { - + while (v_blkno < num_blocks) { status = ocfs2_extent_map_get_blocks(inode, v_blkno, - 1, &p_blkno, - &p_blocks); + &p_blkno, &p_blocks, NULL); if (status < 0) { mlog_errno(status); goto bail; @@ -1306,7 +1297,7 @@ static int ocfs2_queue_orphans(struct oc continue; iter = ocfs2_iget(osb, le64_to_cpu(de->inode), - OCFS2_FI_FLAG_NOLOCK); + OCFS2_FI_FLAG_ORPHAN_RECOVERY); if (IS_ERR(iter)) continue; @@ -1418,7 +1409,6 @@ static int ocfs2_recover_orphans(struct /* Set the proper information to get us going into * ocfs2_delete_inode. */ oi->ip_flags |= OCFS2_INODE_MAYBE_ORPHANED; - oi->ip_orphaned_slot = slot; spin_unlock(&oi->ip_lock); iput(inode); diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index d026b4f..3db5de4 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -390,7 +390,7 @@ static inline int ocfs2_calc_tree_trunc_ /* We may be deleting metadata blocks, so metadata alloc dinode + one desc. block for each possible delete. */ if (tree_depth && next_free == 1 && - le32_to_cpu(last_el->l_recs[i].e_clusters) == clusters_to_del) + ocfs2_rec_clusters(last_el, &last_el->l_recs[i]) == clusters_to_del) credits += 1 + tree_depth; /* update to the truncate log. */ diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index 51b0204..af01158 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -85,8 +85,11 @@ int ocfs2_mmap(struct file *file, struct int ret = 0, lock_level = 0; struct ocfs2_super *osb = OCFS2_SB(file->f_dentry->d_inode->i_sb); - /* We don't want to support shared writable mappings yet. */ - if (!ocfs2_mount_local(osb) && + /* + * Only support shared writeable mmap for local mounts which + * don't know about holes. + */ + if ((!ocfs2_mount_local(osb) || ocfs2_sparse_alloc(osb)) && ((vma->vm_flags & VM_SHARED) || (vma->vm_flags & VM_MAYSHARE)) && ((vma->vm_flags & VM_WRITE) || (vma->vm_flags & VM_MAYWRITE))) { mlog(0, "disallow shared writable mmaps %lx\n", vma->vm_flags); diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 28dd757..2bcf353 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -175,8 +175,6 @@ static struct dentry *ocfs2_lookup(struc inode = ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0); if (IS_ERR(inode)) { - mlog(ML_ERROR, "Unable to create inode %llu\n", - (unsigned long long)blkno); ret = ERR_PTR(-EACCES); goto bail_unlock; } @@ -189,7 +187,6 @@ static struct dentry *ocfs2_lookup(struc * unlink. */ spin_lock(&oi->ip_lock); oi->ip_flags &= ~OCFS2_INODE_MAYBE_ORPHANED; - oi->ip_orphaned_slot = OCFS2_INVALID_SLOT; spin_unlock(&oi->ip_lock); bail_add: @@ -288,7 +285,7 @@ static int ocfs2_fill_new_dir(struct ocf i_size_write(inode, inode->i_sb->s_blocksize); inode->i_nlink = 2; - inode->i_blocks = ocfs2_align_bytes_to_sectors(inode->i_sb->s_blocksize); + inode->i_blocks = ocfs2_inode_sector_count(inode); status = ocfs2_mark_inode_dirty(handle, inode, fe_bh); if (status < 0) { mlog_errno(status); @@ -1486,8 +1483,7 @@ static int ocfs2_create_symlink_data(str struct buffer_head **bhs = NULL; const char *c; struct super_block *sb = osb->sb; - u64 p_blkno; - int p_blocks; + u64 p_blkno, p_blocks; int virtual, blocks, status, i, bytes_left; bytes_left = i_size_read(inode) + 1; @@ -1514,8 +1510,8 @@ static int ocfs2_create_symlink_data(str goto bail; } - status = ocfs2_extent_map_get_blocks(inode, 0, 1, &p_blkno, - &p_blocks); + status = ocfs2_extent_map_get_blocks(inode, 0, &p_blkno, &p_blocks, + NULL); if (status < 0) { mlog_errno(status); goto bail; @@ -1674,8 +1670,11 @@ static int ocfs2_symlink(struct inode *d inode->i_rdev = 0; newsize = l - 1; if (l > ocfs2_fast_symlink_chars(sb)) { + u32 offset = 0; + inode->i_op = &ocfs2_symlink_inode_operations; - status = ocfs2_do_extend_allocation(osb, inode, 1, new_fe_bh, + status = ocfs2_do_extend_allocation(osb, inode, &offset, 1, + new_fe_bh, handle, data_ac, NULL, NULL); if (status < 0) { @@ -1689,7 +1688,7 @@ static int ocfs2_symlink(struct inode *d goto bail; } i_size_write(inode, newsize); - inode->i_blocks = ocfs2_align_bytes_to_sectors(newsize); + inode->i_blocks = ocfs2_inode_sector_count(inode); } else { inode->i_op = &ocfs2_fast_symlink_inode_operations; memcpy((char *) fe->id2.i_symlink, symname, l); @@ -2222,9 +2221,7 @@ static int ocfs2_orphan_add(struct ocfs2 /* Record which orphan dir our inode now resides * in. delete_inode will use this to determine which orphan * dir to lock. */ - spin_lock(&OCFS2_I(inode)->ip_lock); - OCFS2_I(inode)->ip_orphaned_slot = osb->slot_num; - spin_unlock(&OCFS2_I(inode)->ip_lock); + fe->i_orphaned_slot = cpu_to_le16(osb->slot_num); mlog(0, "Inode %llu orphaned in slot %d\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, osb->slot_num); diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index db8e77c..82cc92d 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -46,11 +46,6 @@ #include "ocfs2_fs.h" #include "endian.h" #include "ocfs2_lockid.h" -struct ocfs2_extent_map { - u32 em_clusters; - struct rb_root em_extents; -}; - /* Most user visible OCFS2 inodes will have very few pieces of * metadata, but larger files (including bitmaps, etc) must be taken * into account when designing an access scheme. We allow a small @@ -303,6 +298,13 @@ static inline int ocfs2_should_order_dat return 1; } +static inline int ocfs2_sparse_alloc(struct ocfs2_super *osb) +{ + if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) + return 1; + return 0; +} + /* set / clear functions because cluster events can make these happen * in parallel so we want the transitions to be atomic. this also * means that any future flags osb_flags must be protected by spinlock @@ -461,6 +463,49 @@ static inline unsigned long ocfs2_align_ return (unsigned long)((bytes + 511) >> 9); } +static inline unsigned int ocfs2_page_index_to_clusters(struct super_block *sb, + unsigned long pg_index) +{ + u32 clusters = pg_index; + unsigned int cbits = OCFS2_SB(sb)->s_clustersize_bits; + + if (unlikely(PAGE_CACHE_SHIFT > cbits)) + clusters = pg_index << (PAGE_CACHE_SHIFT - cbits); + else if (PAGE_CACHE_SHIFT < cbits) + clusters = pg_index >> (cbits - PAGE_CACHE_SHIFT); + + return clusters; +} + +/* + * Find the 1st page index which covers the given clusters. + */ +static inline unsigned long ocfs2_align_clusters_to_page_index(struct super_block *sb, + u32 clusters) +{ + unsigned int cbits = OCFS2_SB(sb)->s_clustersize_bits; + unsigned long index = clusters; + + if (PAGE_CACHE_SHIFT > cbits) { + index = clusters >> (PAGE_CACHE_SHIFT - cbits); + } else if (PAGE_CACHE_SHIFT < cbits) { + index = clusters << (cbits - PAGE_CACHE_SHIFT); + } + + return index; +} + +static inline unsigned int ocfs2_pages_per_cluster(struct super_block *sb) +{ + unsigned int cbits = OCFS2_SB(sb)->s_clustersize_bits; + unsigned int pages_per_cluster = 1; + + if (PAGE_CACHE_SHIFT < cbits) + pages_per_cluster = 1 << (cbits - PAGE_CACHE_SHIFT); + + return pages_per_cluster; +} + #define ocfs2_set_bit ext2_set_bit #define ocfs2_clear_bit ext2_clear_bit #define ocfs2_test_bit ext2_test_bit diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index e61e218..7130647 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -86,7 +86,8 @@ #define OCFS2_CLEAR_INCOMPAT_FEATURE(sb, OCFS2_SB(sb)->s_feature_incompat &= ~(mask) #define OCFS2_FEATURE_COMPAT_SUPP OCFS2_FEATURE_COMPAT_BACKUP_SB -#define OCFS2_FEATURE_INCOMPAT_SUPP OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT +#define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \ + | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) #define OCFS2_FEATURE_RO_COMPAT_SUPP 0 /* @@ -155,6 +156,12 @@ #define OCFS2_FL_VISIBLE (0x000100FF) /* #define OCFS2_FL_MODIFIABLE (0x000100FF) /* User modifiable flags */ /* + * Extent record flags (e_node.leaf.flags) + */ +#define OCFS2_EXT_UNWRITTEN (0x01) /* Extent is allocated but + * unwritten */ + +/* * ioctl commands */ #define OCFS2_IOC_GETFLAGS _IOR('f', 1, long) @@ -282,10 +289,21 @@ #define OCFS2_RAW_SB(dinode) (&((dinode /* * On disk extent record for OCFS2 * It describes a range of clusters on disk. + * + * Length fields are divided into interior and leaf node versions. + * This leaves room for a flags field (OCFS2_EXT_*) in the leaf nodes. */ struct ocfs2_extent_rec { /*00*/ __le32 e_cpos; /* Offset into the file, in clusters */ - __le32 e_clusters; /* Clusters covered by this extent */ + union { + __le32 e_int_clusters; /* Clusters covered by all children */ + struct { + __le16 e_leaf_clusters; /* Clusters covered by this + extent */ + __u8 e_reserved1; + __u8 e_flags; /* Extent flags */ + }; + }; __le64 e_blkno; /* Physical disk offset, in blocks */ /*10*/ }; @@ -311,7 +329,10 @@ struct ocfs2_extent_list { /*00*/ __le16 l_tree_depth; /* Extent tree depth from this point. 0 means data extents hang directly off this - header (a leaf) */ + header (a leaf) + NOTE: The high 8 bits cannot be + used - tree_depth is never that big. + */ __le16 l_count; /* Number of extent records */ __le16 l_next_free_rec; /* Next unused extent slot */ __le16 l_reserved1; @@ -446,7 +467,9 @@ struct ocfs2_dinode { __le32 i_ctime_nsec; __le32 i_mtime_nsec; __le32 i_attr; - __le32 i_reserved1; + __le16 i_orphaned_slot; /* Only valid when OCFS2_ORPHANED_FL + was set in i_flags */ + __le16 i_reserved1; /*70*/ __le64 i_reserved2[8]; /*B8*/ union { __le64 i_pad1; /* Generic way to refer to this diff --git a/fs/ocfs2/ocfs2_lockid.h b/fs/ocfs2/ocfs2_lockid.h index 4d5d565..4ca02b1 100644 --- a/fs/ocfs2/ocfs2_lockid.h +++ b/fs/ocfs2/ocfs2_lockid.h @@ -44,6 +44,7 @@ enum ocfs2_lock_type { OCFS2_LOCK_TYPE_RENAME, OCFS2_LOCK_TYPE_RW, OCFS2_LOCK_TYPE_DENTRY, + OCFS2_LOCK_TYPE_OPEN, OCFS2_NUM_LOCK_TYPES }; @@ -69,6 +70,9 @@ static inline char ocfs2_lock_type_char( case OCFS2_LOCK_TYPE_DENTRY: c = 'N'; break; + case OCFS2_LOCK_TYPE_OPEN: + c = 'O'; + break; default: c = '\0'; } @@ -85,6 +89,7 @@ static char *ocfs2_lock_type_strings[] = * important job it does, anyway. */ [OCFS2_LOCK_TYPE_RW] = "Write/Read", [OCFS2_LOCK_TYPE_DENTRY] = "Dentry", + [OCFS2_LOCK_TYPE_OPEN] = "Open", }; static inline const char *ocfs2_lock_type_string(enum ocfs2_lock_type type) diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c index 2d3ac32..d921a28 100644 --- a/fs/ocfs2/slot_map.c +++ b/fs/ocfs2/slot_map.c @@ -197,7 +197,7 @@ int ocfs2_init_slot_info(struct ocfs2_su goto bail; } - status = ocfs2_extent_map_get_blocks(inode, 0ULL, 1, &blkno, NULL); + status = ocfs2_extent_map_get_blocks(inode, 0ULL, &blkno, NULL, NULL); if (status < 0) { mlog_errno(status); goto bail; diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 6dbb117..0da655a 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -381,8 +381,7 @@ static int ocfs2_block_group_alloc(struc le32_to_cpu(fe->i_clusters))); spin_unlock(&OCFS2_I(alloc_inode)->ip_lock); i_size_write(alloc_inode, le64_to_cpu(fe->i_size)); - alloc_inode->i_blocks = - ocfs2_align_bytes_to_sectors(i_size_read(alloc_inode)); + alloc_inode->i_blocks = ocfs2_inode_sector_count(alloc_inode); status = 0; bail: diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 6534f92..5c9e824 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -806,9 +806,6 @@ static int __init ocfs2_init(void) ocfs2_print_version(); - if (init_ocfs2_extent_maps()) - return -ENOMEM; - status = init_ocfs2_uptodate_cache(); if (status < 0) { mlog_errno(status); @@ -837,7 +834,6 @@ leave: if (status < 0) { ocfs2_free_mem_caches(); exit_ocfs2_uptodate_cache(); - exit_ocfs2_extent_maps(); } mlog_exit(status); @@ -863,8 +859,6 @@ static void __exit ocfs2_exit(void) unregister_filesystem(&ocfs2_fs_type); - exit_ocfs2_extent_maps(); - exit_ocfs2_uptodate_cache(); mlog_exit_void(); @@ -963,6 +957,7 @@ static void ocfs2_inode_init_once(void * ocfs2_lock_res_init_once(&oi->ip_rw_lockres); ocfs2_lock_res_init_once(&oi->ip_meta_lockres); ocfs2_lock_res_init_once(&oi->ip_data_lockres); + ocfs2_lock_res_init_once(&oi->ip_open_lockres); ocfs2_metadata_cache_init(&oi->vfs_inode); diff --git a/fs/ocfs2/vote.c b/fs/ocfs2/vote.c index f30e63b..4f82a2f 100644 --- a/fs/ocfs2/vote.c +++ b/fs/ocfs2/vote.c @@ -63,17 +63,10 @@ struct ocfs2_msg_hdr __be32 h_node_num; /* node sending this particular message. */ }; -/* OCFS2_MAX_FILENAME_LEN is 255 characters, but we want to align this - * for the network. */ -#define OCFS2_VOTE_FILENAME_LEN 256 struct ocfs2_vote_msg { struct ocfs2_msg_hdr v_hdr; - union { - __be32 v_generic1; - __be32 v_orphaned_slot; /* Used during delete votes */ - __be32 v_nlink; /* Used during unlink votes */ - } md1; /* Message type dependant 1 */ + __be32 v_reserved1; }; /* Responses are given these values to maintain backwards @@ -86,7 +79,6 @@ struct ocfs2_response_msg { struct ocfs2_msg_hdr r_hdr; __be32 r_response; - __be32 r_orphaned_slot; }; struct ocfs2_vote_work { @@ -96,7 +88,6 @@ struct ocfs2_vote_work { enum ocfs2_vote_request { OCFS2_VOTE_REQ_INVALID = 0, - OCFS2_VOTE_REQ_DELETE, OCFS2_VOTE_REQ_MOUNT, OCFS2_VOTE_REQ_UMOUNT, OCFS2_VOTE_REQ_LAST @@ -151,135 +142,23 @@ static void ocfs2_process_umount_request ocfs2_node_map_set_bit(osb, &osb->umount_map, node_num); } -void ocfs2_mark_inode_remotely_deleted(struct inode *inode) -{ - struct ocfs2_inode_info *oi = OCFS2_I(inode); - - assert_spin_locked(&oi->ip_lock); - /* We set the SKIP_DELETE flag on the inode so we don't try to - * delete it in delete_inode ourselves, thus avoiding - * unecessary lock pinging. If the other node failed to wipe - * the inode as a result of a crash, then recovery will pick - * up the slack. */ - oi->ip_flags |= OCFS2_INODE_DELETED|OCFS2_INODE_SKIP_DELETE; -} - -static int ocfs2_process_delete_request(struct inode *inode, - int *orphaned_slot) -{ - int response = OCFS2_RESPONSE_BUSY; - - mlog(0, "DELETE vote on inode %lu, read lnk_cnt = %u, slot = %d\n", - inode->i_ino, inode->i_nlink, *orphaned_slot); - - spin_lock(&OCFS2_I(inode)->ip_lock); - - /* Whatever our vote response is, we want to make sure that - * the orphaned slot is recorded properly on this node *and* - * on the requesting node. Technically, if the requesting node - * did not know which slot the inode is orphaned in but we - * respond with BUSY he doesn't actually need the orphaned - * slot, but it doesn't hurt to do it here anyway. */ - if ((*orphaned_slot) != OCFS2_INVALID_SLOT) { - mlog_bug_on_msg(OCFS2_I(inode)->ip_orphaned_slot != - OCFS2_INVALID_SLOT && - OCFS2_I(inode)->ip_orphaned_slot != - (*orphaned_slot), - "Inode %llu: This node thinks it's " - "orphaned in slot %d, messaged it's in %d\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, - OCFS2_I(inode)->ip_orphaned_slot, - *orphaned_slot); - - mlog(0, "Setting orphaned slot for inode %llu to %d\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, - *orphaned_slot); - - OCFS2_I(inode)->ip_orphaned_slot = *orphaned_slot; - } else { - mlog(0, "Sending back orphaned slot %d for inode %llu\n", - OCFS2_I(inode)->ip_orphaned_slot, - (unsigned long long)OCFS2_I(inode)->ip_blkno); - - *orphaned_slot = OCFS2_I(inode)->ip_orphaned_slot; - } - - /* vote no if the file is still open. */ - if (OCFS2_I(inode)->ip_open_count) { - mlog(0, "open count = %u\n", - OCFS2_I(inode)->ip_open_count); - spin_unlock(&OCFS2_I(inode)->ip_lock); - goto done; - } - spin_unlock(&OCFS2_I(inode)->ip_lock); - - /* directories are a bit ugly... What if someone is sitting in - * it? We want to make sure the inode is removed completely as - * a result of the iput in process_vote. */ - if (S_ISDIR(inode->i_mode) && (atomic_read(&inode->i_count) != 1)) { - mlog(0, "i_count = %u\n", atomic_read(&inode->i_count)); - goto done; - } - - if (filemap_fdatawrite(inode->i_mapping)) { - mlog(ML_ERROR, "Could not sync inode %llu for delete!\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno); - goto done; - } - sync_mapping_buffers(inode->i_mapping); - truncate_inode_pages(inode->i_mapping, 0); - ocfs2_extent_map_trunc(inode, 0); - - spin_lock(&OCFS2_I(inode)->ip_lock); - /* double check open count - someone might have raced this - * thread into ocfs2_file_open while we were writing out - * data. If we're to allow a wipe of this inode now, we *must* - * hold the spinlock until we've marked it. */ - if (OCFS2_I(inode)->ip_open_count) { - mlog(0, "Raced to wipe! open count = %u\n", - OCFS2_I(inode)->ip_open_count); - spin_unlock(&OCFS2_I(inode)->ip_lock); - goto done; - } - - /* Mark the inode as being wiped from disk. */ - ocfs2_mark_inode_remotely_deleted(inode); - spin_unlock(&OCFS2_I(inode)->ip_lock); - - /* Not sure this is necessary anymore. */ - d_prune_aliases(inode); - - /* If we get here, then we're voting 'yes', so commit the - * delete on our side. */ - response = OCFS2_RESPONSE_OK; -done: - return response; -} - static void ocfs2_process_vote(struct ocfs2_super *osb, struct ocfs2_vote_msg *msg) { int net_status, vote_response; - int orphaned_slot = 0; - unsigned int node_num, generation; + unsigned int node_num; u64 blkno; enum ocfs2_vote_request request; - struct inode *inode = NULL; struct ocfs2_msg_hdr *hdr = &msg->v_hdr; struct ocfs2_response_msg response; /* decode the network mumbo jumbo into local variables. */ request = be32_to_cpu(hdr->h_request); blkno = be64_to_cpu(hdr->h_blkno); - generation = be32_to_cpu(hdr->h_generation); node_num = be32_to_cpu(hdr->h_node_num); - if (request == OCFS2_VOTE_REQ_DELETE) - orphaned_slot = be32_to_cpu(msg->md1.v_orphaned_slot); - mlog(0, "processing vote: request = %u, blkno = %llu, " - "generation = %u, node_num = %u, priv1 = %u\n", request, - (unsigned long long)blkno, generation, node_num, - be32_to_cpu(msg->md1.v_generic1)); + mlog(0, "processing vote: request = %u, blkno = %llu, node_num = %u\n", + request, (unsigned long long)blkno, node_num); if (!ocfs2_is_valid_vote_request(request)) { mlog(ML_ERROR, "Invalid vote request %d from node %u\n", @@ -302,52 +181,6 @@ static void ocfs2_process_vote(struct oc break; } - /* We cannot process the remaining message types before we're - * fully mounted. It's perfectly safe however to send a 'yes' - * response as we can't possibly have any of the state they're - * asking us to modify yet. */ - if (atomic_read(&osb->vol_state) == VOLUME_INIT) - goto respond; - - /* If we get here, then the request is against an inode. */ - inode = ocfs2_ilookup_for_vote(osb, blkno, - request == OCFS2_VOTE_REQ_DELETE); - - /* Not finding the inode is perfectly valid - it means we're - * not interested in what the other node is about to do to it - * so in those cases we automatically respond with an - * affirmative. Cluster locking ensures that we won't race - * interest in the inode with this vote request. */ - if (!inode) - goto respond; - - /* Check generation values. It's possible for us to get a - * request against a stale inode. If so then we proceed as if - * we had not found an inode in the first place. */ - if (inode->i_generation != generation) { - mlog(0, "generation passed %u != inode generation = %u, " - "ip_flags = %x, ip_blkno = %llu, msg %llu, i_count = %u, " - "message type = %u\n", generation, inode->i_generation, - OCFS2_I(inode)->ip_flags, - (unsigned long long)OCFS2_I(inode)->ip_blkno, - (unsigned long long)blkno, atomic_read(&inode->i_count), - request); - iput(inode); - inode = NULL; - goto respond; - } - - switch (request) { - case OCFS2_VOTE_REQ_DELETE: - vote_response = ocfs2_process_delete_request(inode, - &orphaned_slot); - break; - default: - mlog(ML_ERROR, "node %u, invalid request: %u\n", - node_num, request); - vote_response = OCFS2_RESPONSE_BAD_MSG; - } - respond: /* Response struture is small so we just put it on the stack * and stuff it inline. */ @@ -357,7 +190,6 @@ respond: response.r_hdr.h_generation = hdr->h_generation; response.r_hdr.h_node_num = cpu_to_be32(osb->node_num); response.r_response = cpu_to_be32(vote_response); - response.r_orphaned_slot = cpu_to_be32(orphaned_slot); net_status = o2net_send_message(OCFS2_MESSAGE_TYPE_RESPONSE, osb->net_key, @@ -373,9 +205,6 @@ respond: && net_status != -ENOTCONN) mlog(ML_ERROR, "message to node %u fails with error %d!\n", node_num, net_status); - - if (inode) - iput(inode); } static void ocfs2_vote_thread_do_work(struct ocfs2_super *osb) @@ -634,8 +463,7 @@ bail: static struct ocfs2_vote_msg * ocfs2_new_vote_request(struct ocfs2_super *osb, u64 blkno, unsigned int generation, - enum ocfs2_vote_request type, - u32 priv) + enum ocfs2_vote_request type) { struct ocfs2_vote_msg *request; struct ocfs2_msg_hdr *hdr; @@ -651,8 +479,6 @@ static struct ocfs2_vote_msg * ocfs2_new hdr->h_request = cpu_to_be32(type); hdr->h_blkno = cpu_to_be64(blkno); hdr->h_generation = cpu_to_be32(generation); - - request->md1.v_generic1 = cpu_to_be32(priv); } return request; @@ -664,7 +490,7 @@ static int ocfs2_do_request_vote(struct struct ocfs2_vote_msg *request, struct ocfs2_net_response_cb *callback) { - int status, response; + int status, response = -EBUSY; unsigned int response_id; struct ocfs2_msg_hdr *hdr; @@ -686,109 +512,12 @@ bail: return status; } -static int ocfs2_request_vote(struct inode *inode, - struct ocfs2_vote_msg *request, - struct ocfs2_net_response_cb *callback) -{ - int status; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - - if (ocfs2_inode_is_new(inode)) - return 0; - - status = -EAGAIN; - while (status == -EAGAIN) { - if (!(osb->s_mount_opt & OCFS2_MOUNT_NOINTR) && - signal_pending(current)) - return -ERESTARTSYS; - - status = ocfs2_super_lock(osb, 0); - if (status < 0) { - mlog_errno(status); - break; - } - - status = 0; - if (!ocfs2_node_map_is_only(osb, &osb->mounted_map, - osb->node_num)) - status = ocfs2_do_request_vote(osb, request, callback); - - ocfs2_super_unlock(osb, 0); - } - return status; -} - -static void ocfs2_delete_response_cb(void *priv, - struct ocfs2_response_msg *resp) -{ - int orphaned_slot, node; - struct inode *inode = priv; - - orphaned_slot = be32_to_cpu(resp->r_orphaned_slot); - node = be32_to_cpu(resp->r_hdr.h_node_num); - mlog(0, "node %d tells us that inode %llu is orphaned in slot %d\n", - node, (unsigned long long)OCFS2_I(inode)->ip_blkno, - orphaned_slot); - - /* The other node may not actually know which slot the inode - * is orphaned in. */ - if (orphaned_slot == OCFS2_INVALID_SLOT) - return; - - /* Ok, the responding node knows which slot this inode is - * orphaned in. We verify that the information is correct and - * then record this in the inode. ocfs2_delete_inode will use - * this information to determine which lock to take. */ - spin_lock(&OCFS2_I(inode)->ip_lock); - mlog_bug_on_msg(OCFS2_I(inode)->ip_orphaned_slot != orphaned_slot && - OCFS2_I(inode)->ip_orphaned_slot - != OCFS2_INVALID_SLOT, "Inode %llu: Node %d says it's " - "orphaned in slot %d, we think it's in %d\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, - be32_to_cpu(resp->r_hdr.h_node_num), - orphaned_slot, OCFS2_I(inode)->ip_orphaned_slot); - - OCFS2_I(inode)->ip_orphaned_slot = orphaned_slot; - spin_unlock(&OCFS2_I(inode)->ip_lock); -} - -int ocfs2_request_delete_vote(struct inode *inode) -{ - int orphaned_slot, status; - struct ocfs2_net_response_cb delete_cb; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - struct ocfs2_vote_msg *request; - - spin_lock(&OCFS2_I(inode)->ip_lock); - orphaned_slot = OCFS2_I(inode)->ip_orphaned_slot; - spin_unlock(&OCFS2_I(inode)->ip_lock); - - delete_cb.rc_cb = ocfs2_delete_response_cb; - delete_cb.rc_priv = inode; - - mlog(0, "Inode %llu, we start thinking orphaned slot is %d\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, orphaned_slot); - - status = -ENOMEM; - request = ocfs2_new_vote_request(osb, OCFS2_I(inode)->ip_blkno, - inode->i_generation, - OCFS2_VOTE_REQ_DELETE, orphaned_slot); - if (request) { - status = ocfs2_request_vote(inode, request, &delete_cb); - - kfree(request); - } - - return status; -} - int ocfs2_request_mount_vote(struct ocfs2_super *osb) { int status; struct ocfs2_vote_msg *request = NULL; - request = ocfs2_new_vote_request(osb, 0ULL, 0, - OCFS2_VOTE_REQ_MOUNT, 0); + request = ocfs2_new_vote_request(osb, 0ULL, 0, OCFS2_VOTE_REQ_MOUNT); if (!request) { status = -ENOMEM; goto bail; @@ -821,8 +550,7 @@ int ocfs2_request_umount_vote(struct ocf int status; struct ocfs2_vote_msg *request = NULL; - request = ocfs2_new_vote_request(osb, 0ULL, 0, - OCFS2_VOTE_REQ_UMOUNT, 0); + request = ocfs2_new_vote_request(osb, 0ULL, 0, OCFS2_VOTE_REQ_UMOUNT); if (!request) { status = -ENOMEM; goto bail; @@ -969,7 +697,6 @@ static int ocfs2_handle_vote_message(str be32_to_cpu(work->w_msg.v_hdr.h_generation)); mlog(0, "h_node_num = %u\n", be32_to_cpu(work->w_msg.v_hdr.h_node_num)); - mlog(0, "v_generic1 = %u\n", be32_to_cpu(work->w_msg.md1.v_generic1)); spin_lock(&osb->vote_task_lock); list_add_tail(&work->w_list, &osb->vote_list); diff --git a/fs/ocfs2/vote.h b/fs/ocfs2/vote.h index 53ebc1c..9ea46f6 100644 --- a/fs/ocfs2/vote.h +++ b/fs/ocfs2/vote.h @@ -38,14 +38,11 @@ static inline void ocfs2_kick_vote_threa wake_up(&osb->vote_event); } -int ocfs2_request_delete_vote(struct inode *inode); int ocfs2_request_mount_vote(struct ocfs2_super *osb); int ocfs2_request_umount_vote(struct ocfs2_super *osb); int ocfs2_register_net_handlers(struct ocfs2_super *osb); void ocfs2_unregister_net_handlers(struct ocfs2_super *osb); -void ocfs2_mark_inode_remotely_deleted(struct inode *inode); - void ocfs2_remove_node_from_vote_queues(struct ocfs2_super *osb, int node_num); #endif diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index abdf068..eca471b 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -38,7 +38,7 @@ static int property_read_proc(char *page n = count; else *eof = 1; - memcpy(page, pp->value + off, n); + memcpy(page, (char *)pp->value + off, n); *start = page; return n; } diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index c8178b7..2cac562 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -68,7 +68,7 @@ static struct dentry *get_xa_root(struct if (!privroot) return ERR_PTR(-ENODATA); - mutex_lock(&privroot->d_inode->i_mutex); + mutex_lock_nested(&privroot->d_inode->i_mutex, I_MUTEX_XATTR); if (REISERFS_SB(sb)->xattr_root) { xaroot = dget(REISERFS_SB(sb)->xattr_root); goto out; diff --git a/fs/super.c b/fs/super.c index 60b1e50..8341e4e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -725,16 +725,6 @@ static int test_bdev_super(struct super_ return (void *)s->s_bdev == data; } -static void bdev_uevent(struct block_device *bdev, enum kobject_action action) -{ - if (bdev->bd_disk) { - if (bdev->bd_part) - kobject_uevent(&bdev->bd_part->kobj, action); - else - kobject_uevent(&bdev->bd_disk->kobj, action); - } -} - int get_sb_bdev(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, int (*fill_super)(struct super_block *, void *, int), @@ -782,7 +772,6 @@ int get_sb_bdev(struct file_system_type } s->s_flags |= MS_ACTIVE; - bdev_uevent(bdev, KOBJ_MOUNT); } return simple_set_mnt(mnt, s); @@ -801,7 +790,6 @@ void kill_block_super(struct super_block { struct block_device *bdev = sb->s_bdev; - bdev_uevent(bdev, KOBJ_UMOUNT); generic_shutdown_super(sb); sync_blockdev(bdev); close_bdev_excl(bdev); diff --git a/fs/sync.c b/fs/sync.c index d0feff6..5cb9e7e 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -239,13 +239,11 @@ out: /* * `endbyte' is inclusive */ -int do_sync_file_range(struct file *file, loff_t offset, loff_t endbyte, - unsigned int flags) +int do_sync_mapping_range(struct address_space *mapping, loff_t offset, + loff_t endbyte, unsigned int flags) { int ret; - struct address_space *mapping; - mapping = file->f_mapping; if (!mapping) { ret = -EINVAL; goto out; @@ -275,4 +273,4 @@ int do_sync_file_range(struct file *file out: return ret; } -EXPORT_SYMBOL_GPL(do_sync_file_range); +EXPORT_SYMBOL_GPL(do_sync_mapping_range); diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index d3b9f5f..8ea2a51 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -59,7 +59,7 @@ read(struct file * file, char __user * u if (copy_to_user(userbuf, buffer, count)) return -EFAULT; - pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count); + pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); *off = offs + count; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index fc46333..db0413a 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -633,6 +633,7 @@ struct sysfs_schedule_callback_struct { struct kobject *kobj; void (*func)(void *); void *data; + struct module *owner; struct work_struct work; }; @@ -643,6 +644,7 @@ static void sysfs_schedule_callback_work (ss->func)(ss->data); kobject_put(ss->kobj); + module_put(ss->owner); kfree(ss); } @@ -651,6 +653,7 @@ static void sysfs_schedule_callback_work * @kobj: object we're acting for. * @func: callback function to invoke later. * @data: argument to pass to @func. + * @owner: module owning the callback code * * sysfs attribute methods must not unregister themselves or their parent * kobject (which would amount to the same thing). Attempts to do so will @@ -663,20 +666,25 @@ static void sysfs_schedule_callback_work * until @func returns. * * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated. + * be allocated, -ENODEV if a reference to @owner isn't available. */ int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), - void *data) + void *data, struct module *owner) { struct sysfs_schedule_callback_struct *ss; + if (!try_module_get(owner)) + return -ENODEV; ss = kmalloc(sizeof(*ss), GFP_KERNEL); - if (!ss) + if (!ss) { + module_put(owner); return -ENOMEM; + } kobject_get(kobj); ss->kobj = kobj; ss->func = func; ss->data = data; + ss->owner = owner; INIT_WORK(&ss->work, sysfs_schedule_callback_work); schedule_work(&ss->work); return 0; diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index b20951c..52eed2a 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -70,9 +70,11 @@ void sysfs_remove_group(struct kobject * { struct dentry * dir; - if (grp->name) - dir = lookup_one_len(grp->name, kobj->dentry, + if (grp->name) { + dir = lookup_one_len_kern(grp->name, kobj->dentry, strlen(grp->name)); + BUG_ON(IS_ERR(dir)); + } else dir = dget(kobj->dentry); diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 09469e7..955adfb 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -276,6 +276,7 @@ enum acpi_prefered_pm_profiles { #define BAF_LEGACY_DEVICES 0x0001 #define BAF_8042_KEYBOARD_CONTROLLER 0x0002 +#define BAF_MSI_NOT_SUPPORTED 0x0008 #define FADT2_REVISION_ID 3 #define FADT2_MINUS_REVISION_ID 2 diff --git a/include/asm-alpha/socket.h b/include/asm-alpha/socket.h index d22ab97..1fede7f 100644 --- a/include/asm-alpha/socket.h +++ b/include/asm-alpha/socket.h @@ -52,6 +52,8 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_PEERSEC 30 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 19 diff --git a/include/asm-alpha/sockios.h b/include/asm-alpha/sockios.h index e4961a7..7932c7a 100644 --- a/include/asm-alpha/sockios.h +++ b/include/asm-alpha/sockios.h @@ -10,6 +10,7 @@ #define SIOCATMARK _IOR('s', 7, int) #define SIOCSPGRP _IOW('s', 8, pid_t) #define SIOCGPGRP _IOR('s', 9, pid_t) -#define SIOCGSTAMP 0x8906 /* Get stamp - linux-specific */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* _ASM_ALPHA_SOCKIOS_H */ diff --git a/include/asm-alpha/string.h b/include/asm-alpha/string.h index 9e44fea..b02b8a2 100644 --- a/include/asm-alpha/string.h +++ b/include/asm-alpha/string.h @@ -61,8 +61,6 @@ (__builtin_constant_p(c) \ ? __constant_c_memset((s),0x0001000100010001UL*(unsigned short)(c),(n)) \ : __memsetw((s),(c),(n))) -extern int strcasecmp(const char *, const char *); - #endif /* __KERNEL__ */ #endif /* __ALPHA_STRING_H__ */ diff --git a/include/asm-arm/div64.h b/include/asm-arm/div64.h index 37e0a96..0b5f881 100644 --- a/include/asm-arm/div64.h +++ b/include/asm-arm/div64.h @@ -2,6 +2,7 @@ #ifndef __ASM_ARM_DIV64 #define __ASM_ARM_DIV64 #include +#include /* * The semantics of do_div() are: @@ -223,4 +224,6 @@ ({ \ #endif +extern uint64_t div64_64(uint64_t dividend, uint64_t divisor); + #endif diff --git a/include/asm-arm/socket.h b/include/asm-arm/socket.h index 19f7df7..65a1a64 100644 --- a/include/asm-arm/socket.h +++ b/include/asm-arm/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-arm/sockios.h b/include/asm-arm/sockios.h index 77c3408..a2588a2 100644 --- a/include/asm-arm/sockios.h +++ b/include/asm-arm/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif diff --git a/include/asm-arm26/socket.h b/include/asm-arm26/socket.h index 19f7df7..65a1a64 100644 --- a/include/asm-arm26/socket.h +++ b/include/asm-arm26/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-arm26/sockios.h b/include/asm-arm26/sockios.h index 77c3408..a2588a2 100644 --- a/include/asm-arm26/sockios.h +++ b/include/asm-arm26/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif diff --git a/include/asm-avr32/arch-at32ap/io.h b/include/asm-avr32/arch-at32ap/io.h new file mode 100644 index 0000000..ee59e40 --- /dev/null +++ b/include/asm-avr32/arch-at32ap/io.h @@ -0,0 +1,39 @@ +#ifndef __ASM_AVR32_ARCH_AT32AP_IO_H +#define __ASM_AVR32_ARCH_AT32AP_IO_H + +/* For "bizarre" halfword swapping */ +#include + +#if defined(CONFIG_AP7000_32_BIT_SMC) +# define __swizzle_addr_b(addr) (addr ^ 3UL) +# define __swizzle_addr_w(addr) (addr ^ 2UL) +# define __swizzle_addr_l(addr) (addr) +# define ioswabb(a, x) (x) +# define ioswabw(a, x) (x) +# define ioswabl(a, x) (x) +# define __mem_ioswabb(a, x) (x) +# define __mem_ioswabw(a, x) swab16(x) +# define __mem_ioswabl(a, x) swab32(x) +#elif defined(CONFIG_AP7000_16_BIT_SMC) +# define __swizzle_addr_b(addr) (addr ^ 1UL) +# define __swizzle_addr_w(addr) (addr) +# define __swizzle_addr_l(addr) (addr) +# define ioswabb(a, x) (x) +# define ioswabw(a, x) (x) +# define ioswabl(a, x) swahw32(x) +# define __mem_ioswabb(a, x) (x) +# define __mem_ioswabw(a, x) swab16(x) +# define __mem_ioswabl(a, x) swahb32(x) +#else +# define __swizzle_addr_b(addr) (addr) +# define __swizzle_addr_w(addr) (addr) +# define __swizzle_addr_l(addr) (addr) +# define ioswabb(a, x) (x) +# define ioswabw(a, x) swab16(x) +# define ioswabl(a, x) swab32(x) +# define __mem_ioswabb(a, x) (x) +# define __mem_ioswabw(a, x) (x) +# define __mem_ioswabl(a, x) (x) +#endif + +#endif /* __ASM_AVR32_ARCH_AT32AP_IO_H */ diff --git a/include/asm-avr32/arch-at32ap/smc.h b/include/asm-avr32/arch-at32ap/smc.h index 3732b32..07152b7 100644 --- a/include/asm-avr32/arch-at32ap/smc.h +++ b/include/asm-avr32/arch-at32ap/smc.h @@ -48,10 +48,32 @@ struct smc_config { unsigned int nwe_controlled:1; /* + * 0: NWAIT is disabled + * 1: Reserved + * 2: NWAIT is frozen mode + * 3: NWAIT in ready mode + */ + unsigned int nwait_mode:2; + + /* * 0: Byte select access type * 1: Byte write access type */ unsigned int byte_write:1; + + /* + * Number of clock cycles before data is released after + * the rising edge of the read controlling signal + * + * Total cycles from SMC is tdf_cycles + 1 + */ + unsigned int tdf_cycles:4; + + /* + * 0: TDF optimization disabled + * 1: TDF optimization enabled + */ + unsigned int tdf_mode:1; }; extern int smc_set_configuration(int cs, const struct smc_config *config); diff --git a/include/asm-avr32/arch-at32ap/time.h b/include/asm-avr32/arch-at32ap/time.h new file mode 100644 index 0000000..cc8a434 --- /dev/null +++ b/include/asm-avr32/arch-at32ap/time.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ASM_AVR32_ARCH_AT32AP_TIME_H +#define _ASM_AVR32_ARCH_AT32AP_TIME_H + +#include + +extern struct irqaction timer_irqaction; +extern struct platform_device at32_systc0_device; +extern void local_timer_interrupt(int irq, void *dev_id); + +#define TIMER_BCR 0x000000c0 +#define TIMER_BCR_SYNC 0 +#define TIMER_BMR 0x000000c4 +#define TIMER_BMR_TC0XC0S 0 +#define TIMER_BMR_TC1XC1S 2 +#define TIMER_BMR_TC2XC2S 4 +#define TIMER_CCR 0x00000000 +#define TIMER_CCR_CLKDIS 1 +#define TIMER_CCR_CLKEN 0 +#define TIMER_CCR_SWTRG 2 +#define TIMER_CMR 0x00000004 +#define TIMER_CMR_ABETRG 10 +#define TIMER_CMR_ACPA 16 +#define TIMER_CMR_ACPC 18 +#define TIMER_CMR_AEEVT 20 +#define TIMER_CMR_ASWTRG 22 +#define TIMER_CMR_BCPB 24 +#define TIMER_CMR_BCPC 26 +#define TIMER_CMR_BEEVT 28 +#define TIMER_CMR_BSWTRG 30 +#define TIMER_CMR_BURST 4 +#define TIMER_CMR_CLKI 3 +#define TIMER_CMR_CPCDIS 7 +#define TIMER_CMR_CPCSTOP 6 +#define TIMER_CMR_CPCTRG 14 +#define TIMER_CMR_EEVT 10 +#define TIMER_CMR_EEVTEDG 8 +#define TIMER_CMR_ENETRG 12 +#define TIMER_CMR_ETRGEDG 8 +#define TIMER_CMR_LDBDIS 7 +#define TIMER_CMR_LDBSTOP 6 +#define TIMER_CMR_LDRA 16 +#define TIMER_CMR_LDRB 18 +#define TIMER_CMR_TCCLKS 0 +#define TIMER_CMR_WAVE 15 +#define TIMER_CMR_WAVSEL 13 +#define TIMER_CV 0x00000010 +#define TIMER_CV_CV 0 +#define TIMER_IDR 0x00000028 +#define TIMER_IDR_COVFS 0 +#define TIMER_IDR_CPAS 2 +#define TIMER_IDR_CPBS 3 +#define TIMER_IDR_CPCS 4 +#define TIMER_IDR_ETRGS 7 +#define TIMER_IDR_LDRAS 5 +#define TIMER_IDR_LDRBS 6 +#define TIMER_IDR_LOVRS 1 +#define TIMER_IER 0x00000024 +#define TIMER_IER_COVFS 0 +#define TIMER_IER_CPAS 2 +#define TIMER_IER_CPBS 3 +#define TIMER_IER_CPCS 4 +#define TIMER_IER_ETRGS 7 +#define TIMER_IER_LDRAS 5 +#define TIMER_IER_LDRBS 6 +#define TIMER_IER_LOVRS 1 +#define TIMER_IMR 0x0000002c +#define TIMER_IMR_COVFS 0 +#define TIMER_IMR_CPAS 2 +#define TIMER_IMR_CPBS 3 +#define TIMER_IMR_CPCS 4 +#define TIMER_IMR_ETRGS 7 +#define TIMER_IMR_LDRAS 5 +#define TIMER_IMR_LDRBS 6 +#define TIMER_IMR_LOVRS 1 +#define TIMER_RA 0x00000014 +#define TIMER_RA_RA 0 +#define TIMER_RB 0x00000018 +#define TIMER_RB_RB 0 +#define TIMER_RC 0x0000001c +#define TIMER_RC_RC 0 +#define TIMER_SR 0x00000020 +#define TIMER_SR_CLKSTA 16 +#define TIMER_SR_COVFS 0 +#define TIMER_SR_CPAS 2 +#define TIMER_SR_CPBS 3 +#define TIMER_SR_CPCS 4 +#define TIMER_SR_ETRGS 7 +#define TIMER_SR_LDRAS 5 +#define TIMER_SR_LDRBS 6 +#define TIMER_SR_LOVRS 1 +#define TIMER_SR_MTIOA 17 +#define TIMER_SR_MTIOB 18 + +/* Bit manipulation macros */ +#define TIMER_BIT(name) (1 << TIMER_##name) +#define TIMER_BF(name,value) ((value) << TIMER_##name) + +/* Register access macros */ +#define timer_read(port,instance,reg) \ + __raw_readl(port + (0x40 * instance) + TIMER_##reg) +#define timer_write(port,instance,reg,value) \ + __raw_writel((value), port + (0x40 * instance) + TIMER_##reg) + +#endif /* _ASM_AVR32_ARCH_AT32AP_TIME_H */ diff --git a/include/asm-avr32/atomic.h b/include/asm-avr32/atomic.h index c40b603..b9c2548 100644 --- a/include/asm-avr32/atomic.h +++ b/include/asm-avr32/atomic.h @@ -173,7 +173,7 @@ static inline int atomic_sub_if_positive } #define atomic_xchg(v, new) (xchg(&((v)->counter), new)) -#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +#define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) #define atomic_sub(i, v) (void)atomic_sub_return(i, v) #define atomic_add(i, v) (void)atomic_add_return(i, v) diff --git a/include/asm-avr32/bug.h b/include/asm-avr32/bug.h index 521766b..afdcd79 100644 --- a/include/asm-avr32/bug.h +++ b/include/asm-avr32/bug.h @@ -18,27 +18,53 @@ #define AVR32_BUG_OPCODE 0x5df0 #ifdef CONFIG_DEBUG_BUGVERBOSE -#define BUG() \ - do { \ - asm volatile(".hword %0\n\t" \ - ".hword %1\n\t" \ - ".long %2" \ - : \ - : "n"(AVR32_BUG_OPCODE), \ - "i"(__LINE__), "X"(__FILE__)); \ - } while (0) +#define _BUG_OR_WARN(flags) \ + asm volatile( \ + "1: .hword %0\n" \ + " .section __bug_table,\"a\",@progbits\n" \ + "2: .long 1b\n" \ + " .long %1\n" \ + " .short %2\n" \ + " .short %3\n" \ + " .org 2b + %4\n" \ + " .previous" \ + : \ + : "i"(AVR32_BUG_OPCODE), "i"(__FILE__), \ + "i"(__LINE__), "i"(flags), \ + "i"(sizeof(struct bug_entry))) #else +#define _BUG_OR_WARN(flags) \ + asm volatile( \ + "1: .hword %0\n" \ + " .section __bug_table,\"a\",@progbits\n" \ + "2: .long 1b\n" \ + " .short %1\n" \ + " .org 2b + %2\n" \ + " .previous" \ + : \ + : "i"(AVR32_BUG_OPCODE), "i"(flags), \ + "i"(sizeof(struct bug_entry))) + +#endif /* CONFIG_DEBUG_BUGVERBOSE */ + #define BUG() \ do { \ - asm volatile(".hword %0\n\t" \ - : : "n"(AVR32_BUG_OPCODE)); \ + _BUG_OR_WARN(0); \ + for (;;); \ } while (0) -#endif /* CONFIG_DEBUG_BUGVERBOSE */ +#define WARN_ON(condition) \ + ({ \ + typeof(condition) __ret_warn_on = (condition); \ + if (unlikely(__ret_warn_on)) \ + _BUG_OR_WARN(BUGFLAG_WARNING); \ + unlikely(__ret_warn_on); \ + }) #define HAVE_ARCH_BUG +#define HAVE_ARCH_WARN_ON #endif /* CONFIG_BUG */ diff --git a/include/asm-avr32/io.h b/include/asm-avr32/io.h index c08e810..e30d4b3 100644 --- a/include/asm-avr32/io.h +++ b/include/asm-avr32/io.h @@ -1,13 +1,15 @@ #ifndef __ASM_AVR32_IO_H #define __ASM_AVR32_IO_H +#include #include - -#ifdef __KERNEL__ +#include #include #include +#include + /* virt_to_phys will only work when address is in P1 or P2 */ static __inline__ unsigned long virt_to_phys(volatile void *address) { @@ -36,104 +38,215 @@ extern void __raw_readsb(const void __io extern void __raw_readsw(const void __iomem *addr, void *data, int wordlen); extern void __raw_readsl(const void __iomem *addr, void *data, int longlen); -static inline void writeb(unsigned char b, volatile void __iomem *addr) +static inline void __raw_writeb(u8 v, volatile void __iomem *addr) { - *(volatile unsigned char __force *)addr = b; + *(volatile u8 __force *)addr = v; } -static inline void writew(unsigned short b, volatile void __iomem *addr) +static inline void __raw_writew(u16 v, volatile void __iomem *addr) { - *(volatile unsigned short __force *)addr = b; + *(volatile u16 __force *)addr = v; } -static inline void writel(unsigned int b, volatile void __iomem *addr) +static inline void __raw_writel(u32 v, volatile void __iomem *addr) { - *(volatile unsigned int __force *)addr = b; + *(volatile u32 __force *)addr = v; } -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel -static inline unsigned char readb(const volatile void __iomem *addr) +static inline u8 __raw_readb(const volatile void __iomem *addr) { - return *(const volatile unsigned char __force *)addr; + return *(const volatile u8 __force *)addr; } -static inline unsigned short readw(const volatile void __iomem *addr) +static inline u16 __raw_readw(const volatile void __iomem *addr) { - return *(const volatile unsigned short __force *)addr; + return *(const volatile u16 __force *)addr; } -static inline unsigned int readl(const volatile void __iomem *addr) +static inline u32 __raw_readl(const volatile void __iomem *addr) { - return *(const volatile unsigned int __force *)addr; + return *(const volatile u32 __force *)addr; +} + +/* Convert I/O port address to virtual address */ +#ifndef __io +# define __io(p) ((void *)phys_to_uncached(p)) +#endif + +/* + * Not really sure about the best way to slow down I/O on + * AVR32. Defining it as a no-op until we have an actual test case. + */ +#define SLOW_DOWN_IO do { } while (0) + +#define __BUILD_MEMORY_SINGLE(pfx, bwl, type) \ +static inline void \ +pfx##write##bwl(type val, volatile void __iomem *addr) \ +{ \ + volatile type *__addr; \ + type __val; \ + \ + __addr = (void *)__swizzle_addr_##bwl((unsigned long)(addr)); \ + __val = pfx##ioswab##bwl(__addr, val); \ + \ + BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \ + \ + *__addr = __val; \ +} \ + \ +static inline type pfx##read##bwl(const volatile void __iomem *addr) \ +{ \ + volatile type *__addr; \ + type __val; \ + \ + __addr = (void *)__swizzle_addr_##bwl((unsigned long)(addr)); \ + \ + BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \ + \ + __val = *__addr; \ + return pfx##ioswab##bwl(__addr, __val); \ +} + +#define __BUILD_IOPORT_SINGLE(pfx, bwl, type, p, slow) \ +static inline void pfx##out##bwl##p(type val, unsigned long port) \ +{ \ + volatile type *__addr; \ + type __val; \ + \ + __addr = __io(__swizzle_addr_##bwl(port)); \ + __val = pfx##ioswab##bwl(__addr, val); \ + \ + BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \ + \ + *__addr = __val; \ + slow; \ +} \ + \ +static inline type pfx##in##bwl##p(unsigned long port) \ +{ \ + volatile type *__addr; \ + type __val; \ + \ + __addr = __io(__swizzle_addr_##bwl(port)); \ + \ + BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \ + \ + __val = *__addr; \ + slow; \ + \ + return pfx##ioswab##bwl(__addr, __val); \ +} + +#define __BUILD_MEMORY_PFX(bus, bwl, type) \ + __BUILD_MEMORY_SINGLE(bus, bwl, type) + +#define BUILDIO_MEM(bwl, type) \ + __BUILD_MEMORY_PFX(, bwl, type) \ + __BUILD_MEMORY_PFX(__mem_, bwl, type) + +#define __BUILD_IOPORT_PFX(bus, bwl, type) \ + __BUILD_IOPORT_SINGLE(bus, bwl, type, ,) \ + __BUILD_IOPORT_SINGLE(bus, bwl, type, _p, SLOW_DOWN_IO) + +#define BUILDIO_IOPORT(bwl, type) \ + __BUILD_IOPORT_PFX(, bwl, type) \ + __BUILD_IOPORT_PFX(__mem_, bwl, type) + +BUILDIO_MEM(b, u8) +BUILDIO_MEM(w, u16) +BUILDIO_MEM(l, u32) + +BUILDIO_IOPORT(b, u8) +BUILDIO_IOPORT(w, u16) +BUILDIO_IOPORT(l, u32) + +#define readb_relaxed readb +#define readw_relaxed readw +#define readl_relaxed readl + +#define __BUILD_MEMORY_STRING(bwl, type) \ +static inline void writes##bwl(volatile void __iomem *addr, \ + const void *data, unsigned int count) \ +{ \ + const type *__data = data; \ + \ + while (count--) \ + __mem_write##bwl(*__data++, addr); \ +} \ + \ +static inline void reads##bwl(const volatile void __iomem *addr, \ + void *data, unsigned int count) \ +{ \ + type *__data = data; \ + \ + while (count--) \ + *__data++ = __mem_read##bwl(addr); \ } -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl -#define writesb(p, d, l) __raw_writesb((unsigned int)p, d, l) -#define writesw(p, d, l) __raw_writesw((unsigned int)p, d, l) -#define writesl(p, d, l) __raw_writesl((unsigned int)p, d, l) +#define __BUILD_IOPORT_STRING(bwl, type) \ +static inline void outs##bwl(unsigned long port, const void *data, \ + unsigned int count) \ +{ \ + const type *__data = data; \ + \ + while (count--) \ + __mem_out##bwl(*__data++, port); \ +} \ + \ +static inline void ins##bwl(unsigned long port, void *data, \ + unsigned int count) \ +{ \ + type *__data = data; \ + \ + while (count--) \ + *__data++ = __mem_in##bwl(port); \ +} -#define readsb(p, d, l) __raw_readsb((unsigned int)p, d, l) -#define readsw(p, d, l) __raw_readsw((unsigned int)p, d, l) -#define readsl(p, d, l) __raw_readsl((unsigned int)p, d, l) +#define BUILDSTRING(bwl, type) \ + __BUILD_MEMORY_STRING(bwl, type) \ + __BUILD_IOPORT_STRING(bwl, type) +BUILDSTRING(b, u8) +BUILDSTRING(w, u16) +BUILDSTRING(l, u32) /* * io{read,write}{8,16,32} macros in both le (for PCI style consumers) and native be */ #ifndef ioread8 -#define ioread8(p) ({ unsigned int __v = __raw_readb(p); __v; }) +#define ioread8(p) ((unsigned int)readb(p)) -#define ioread16(p) ({ unsigned int __v = le16_to_cpu(__raw_readw(p)); __v; }) -#define ioread16be(p) ({ unsigned int __v = be16_to_cpu(__raw_readw(p)); __v; }) +#define ioread16(p) ((unsigned int)readw(p)) +#define ioread16be(p) ((unsigned int)__raw_readw(p)) -#define ioread32(p) ({ unsigned int __v = le32_to_cpu(__raw_readl(p)); __v; }) -#define ioread32be(p) ({ unsigned int __v = be32_to_cpu(__raw_readl(p)); __v; }) +#define ioread32(p) ((unsigned int)readl(p)) +#define ioread32be(p) ((unsigned int)__raw_readl(p)) -#define iowrite8(v,p) __raw_writeb(v, p) +#define iowrite8(v,p) writeb(v, p) -#define iowrite16(v,p) __raw_writew(cpu_to_le16(v), p) -#define iowrite16be(v,p) __raw_writew(cpu_to_be16(v), p) +#define iowrite16(v,p) writew(v, p) +#define iowrite16be(v,p) __raw_writew(v, p) -#define iowrite32(v,p) __raw_writel(cpu_to_le32(v), p) -#define iowrite32be(v,p) __raw_writel(cpu_to_be32(v), p) +#define iowrite32(v,p) writel(v, p) +#define iowrite32be(v,p) __raw_writel(v, p) -#define ioread8_rep(p,d,c) __raw_readsb(p,d,c) -#define ioread16_rep(p,d,c) __raw_readsw(p,d,c) -#define ioread32_rep(p,d,c) __raw_readsl(p,d,c) +#define ioread8_rep(p,d,c) readsb(p,d,c) +#define ioread16_rep(p,d,c) readsw(p,d,c) +#define ioread32_rep(p,d,c) readsl(p,d,c) -#define iowrite8_rep(p,s,c) __raw_writesb(p,s,c) -#define iowrite16_rep(p,s,c) __raw_writesw(p,s,c) -#define iowrite32_rep(p,s,c) __raw_writesl(p,s,c) +#define iowrite8_rep(p,s,c) writesb(p,s,c) +#define iowrite16_rep(p,s,c) writesw(p,s,c) +#define iowrite32_rep(p,s,c) writesl(p,s,c) #endif - -/* - * These two are only here because ALSA _thinks_ it needs them... - */ static inline void memcpy_fromio(void * to, const volatile void __iomem *from, unsigned long count) { - char *p = to; - while (count) { - count--; - *p = readb(from); - p++; - from++; - } + memcpy(to, (const void __force *)from, count); } static inline void memcpy_toio(volatile void __iomem *to, const void * from, unsigned long count) { - const char *p = from; - while (count) { - count--; - writeb(*p, to); - p++; - to++; - } + memcpy((void __force *)to, from, count); } static inline void memset_io(volatile void __iomem *addr, unsigned char val, @@ -142,99 +255,8 @@ static inline void memset_io(volatile vo memset((void __force *)addr, val, count); } -/* - * Bad read/write accesses... - */ -extern void __readwrite_bug(const char *fn); - #define IO_SPACE_LIMIT 0xffffffff -/* Convert I/O port address to virtual address */ -#define __io(p) ((void __iomem *)phys_to_uncached(p)) - -/* - * IO port access primitives - * ------------------------- - * - * The AVR32 doesn't have special IO access instructions; all IO is memory - * mapped. Note that these are defined to perform little endian accesses - * only. Their primary purpose is to access PCI and ISA peripherals. - * - * Note that for a big endian machine, this implies that the following - * big endian mode connectivity is in place. - * - * The machine specific io.h include defines __io to translate an "IO" - * address to a memory address. - * - * Note that we prevent GCC re-ordering or caching values in expressions - * by introducing sequence points into the in*() definitions. Note that - * __raw_* do not guarantee this behaviour. - * - * The {in,out}[bwl] macros are for emulating x86-style PCI/ISA IO space. - */ -#define outb(v, p) __raw_writeb(v, __io(p)) -#define outw(v, p) __raw_writew(cpu_to_le16(v), __io(p)) -#define outl(v, p) __raw_writel(cpu_to_le32(v), __io(p)) - -#define inb(p) __raw_readb(__io(p)) -#define inw(p) le16_to_cpu(__raw_readw(__io(p))) -#define inl(p) le32_to_cpu(__raw_readl(__io(p))) - -static inline void __outsb(unsigned long port, void *addr, unsigned int count) -{ - while (count--) { - outb(*(u8 *)addr, port); - addr++; - } -} - -static inline void __insb(unsigned long port, void *addr, unsigned int count) -{ - while (count--) { - *(u8 *)addr = inb(port); - addr++; - } -} - -static inline void __outsw(unsigned long port, void *addr, unsigned int count) -{ - while (count--) { - outw(*(u16 *)addr, port); - addr += 2; - } -} - -static inline void __insw(unsigned long port, void *addr, unsigned int count) -{ - while (count--) { - *(u16 *)addr = inw(port); - addr += 2; - } -} - -static inline void __outsl(unsigned long port, void *addr, unsigned int count) -{ - while (count--) { - outl(*(u32 *)addr, port); - addr += 4; - } -} - -static inline void __insl(unsigned long port, void *addr, unsigned int count) -{ - while (count--) { - *(u32 *)addr = inl(port); - addr += 4; - } -} - -#define outsb(port, addr, count) __outsb(port, addr, count) -#define insb(port, addr, count) __insb(port, addr, count) -#define outsw(port, addr, count) __outsw(port, addr, count) -#define insw(port, addr, count) __insw(port, addr, count) -#define outsl(port, addr, count) __outsl(port, addr, count) -#define insl(port, addr, count) __insl(port, addr, count) - extern void __iomem *__ioremap(unsigned long offset, size_t size, unsigned long flags); extern void __iounmap(void __iomem *addr); @@ -292,6 +314,4 @@ #define xlate_dev_mem_ptr(p) __va(p) */ #define xlate_dev_kmem_ptr(p) p -#endif /* __KERNEL__ */ - #endif /* __ASM_AVR32_IO_H */ diff --git a/include/asm-avr32/processor.h b/include/asm-avr32/processor.h index f691377..6a64833 100644 --- a/include/asm-avr32/processor.h +++ b/include/asm-avr32/processor.h @@ -40,6 +40,14 @@ enum tlb_config { TLB_INVALID }; +#define AVR32_FEATURE_RMW (1 << 0) +#define AVR32_FEATURE_DSP (1 << 1) +#define AVR32_FEATURE_SIMD (1 << 2) +#define AVR32_FEATURE_OCD (1 << 3) +#define AVR32_FEATURE_PCTR (1 << 4) +#define AVR32_FEATURE_JAVA (1 << 5) +#define AVR32_FEATURE_FPU (1 << 6) + struct avr32_cpuinfo { struct clk *clk; unsigned long loops_per_jiffy; @@ -48,6 +56,7 @@ struct avr32_cpuinfo { unsigned short arch_revision; unsigned short cpu_revision; enum tlb_config tlb_config; + unsigned long features; struct cache_info icache; struct cache_info dcache; @@ -125,10 +134,10 @@ #define prepare_to_copy(tsk) do { } whil #define thread_saved_pc(tsk) ((tsk)->thread.cpu_context.pc) struct pt_regs; -void show_trace(struct task_struct *task, unsigned long *stack, - struct pt_regs *regs); - extern unsigned long get_wchan(struct task_struct *p); +extern void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl); +extern void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp, + struct pt_regs *regs, const char *log_lvl); #define KSTK_EIP(tsk) ((tsk)->thread.cpu_context.pc) #define KSTK_ESP(tsk) ((tsk)->thread.cpu_context.ksp) diff --git a/include/asm-avr32/setup.h b/include/asm-avr32/setup.h index 0a52242..1ff1a21 100644 --- a/include/asm-avr32/setup.h +++ b/include/asm-avr32/setup.h @@ -124,19 +124,12 @@ #define tag_size(type) ((sizeof(struct t #define for_each_tag(t,base) \ for (t = base; t->hdr.size; t = tag_next(t)) -extern struct tag_mem_range *mem_phys; -extern struct tag_mem_range *mem_reserved; -extern struct tag_mem_range *mem_ramdisk; - extern struct tag *bootloader_tags; -extern void setup_bootmem(void); -extern void setup_processor(void); -extern void board_setup_fbmem(unsigned long fbmem_start, - unsigned long fbmem_size); +extern resource_size_t fbmem_start; +extern resource_size_t fbmem_size; -/* Chip-specific hook to enable the use of SDRAM */ -void chip_enable_sdram(void); +void setup_processor(void); #endif /* !__ASSEMBLY__ */ diff --git a/include/asm-avr32/socket.h b/include/asm-avr32/socket.h index 543229d..a0d0507 100644 --- a/include/asm-avr32/socket.h +++ b/include/asm-avr32/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/include/asm-avr32/sockios.h b/include/asm-avr32/sockios.h index 84f3d65..0802d74 100644 --- a/include/asm-avr32/sockios.h +++ b/include/asm-avr32/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* __ASM_AVR32_SOCKIOS_H */ diff --git a/include/asm-avr32/sysreg.h b/include/asm-avr32/sysreg.h index f91975f..c02bc83 100644 --- a/include/asm-avr32/sysreg.h +++ b/include/asm-avr32/sysreg.h @@ -7,326 +7,281 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#ifndef __ASM_AVR32_SYSREG_H__ -#define __ASM_AVR32_SYSREG_H__ +#ifndef __ASM_AVR32_SYSREG_H +#define __ASM_AVR32_SYSREG_H /* sysreg register offsets */ -#define SYSREG_SR 0x0000 -#define SYSREG_EVBA 0x0004 -#define SYSREG_ACBA 0x0008 -#define SYSREG_CPUCR 0x000c -#define SYSREG_ECR 0x0010 -#define SYSREG_RSR_SUP 0x0014 -#define SYSREG_RSR_INT0 0x0018 -#define SYSREG_RSR_INT1 0x001c -#define SYSREG_RSR_INT2 0x0020 -#define SYSREG_RSR_INT3 0x0024 -#define SYSREG_RSR_EX 0x0028 -#define SYSREG_RSR_NMI 0x002c -#define SYSREG_RSR_DBG 0x0030 -#define SYSREG_RAR_SUP 0x0034 -#define SYSREG_RAR_INT0 0x0038 -#define SYSREG_RAR_INT1 0x003c -#define SYSREG_RAR_INT2 0x0040 -#define SYSREG_RAR_INT3 0x0044 -#define SYSREG_RAR_EX 0x0048 -#define SYSREG_RAR_NMI 0x004c -#define SYSREG_RAR_DBG 0x0050 -#define SYSREG_JECR 0x0054 -#define SYSREG_JOSP 0x0058 -#define SYSREG_JAVA_LV0 0x005c -#define SYSREG_JAVA_LV1 0x0060 -#define SYSREG_JAVA_LV2 0x0064 -#define SYSREG_JAVA_LV3 0x0068 -#define SYSREG_JAVA_LV4 0x006c -#define SYSREG_JAVA_LV5 0x0070 -#define SYSREG_JAVA_LV6 0x0074 -#define SYSREG_JAVA_LV7 0x0078 -#define SYSREG_JTBA 0x007c -#define SYSREG_JBCR 0x0080 -#define SYSREG_CONFIG0 0x0100 -#define SYSREG_CONFIG1 0x0104 -#define SYSREG_COUNT 0x0108 -#define SYSREG_COMPARE 0x010c -#define SYSREG_TLBEHI 0x0110 -#define SYSREG_TLBELO 0x0114 -#define SYSREG_PTBR 0x0118 -#define SYSREG_TLBEAR 0x011c -#define SYSREG_MMUCR 0x0120 -#define SYSREG_TLBARLO 0x0124 -#define SYSREG_TLBARHI 0x0128 -#define SYSREG_PCCNT 0x012c -#define SYSREG_PCNT0 0x0130 -#define SYSREG_PCNT1 0x0134 -#define SYSREG_PCCR 0x0138 -#define SYSREG_BEAR 0x013c +#define SYSREG_SR 0x0000 +#define SYSREG_EVBA 0x0004 +#define SYSREG_ACBA 0x0008 +#define SYSREG_CPUCR 0x000c +#define SYSREG_ECR 0x0010 +#define SYSREG_RSR_SUP 0x0014 +#define SYSREG_RSR_INT0 0x0018 +#define SYSREG_RSR_INT1 0x001c +#define SYSREG_RSR_INT2 0x0020 +#define SYSREG_RSR_INT3 0x0024 +#define SYSREG_RSR_EX 0x0028 +#define SYSREG_RSR_NMI 0x002c +#define SYSREG_RSR_DBG 0x0030 +#define SYSREG_RAR_SUP 0x0034 +#define SYSREG_RAR_INT0 0x0038 +#define SYSREG_RAR_INT1 0x003c +#define SYSREG_RAR_INT2 0x0040 +#define SYSREG_RAR_INT3 0x0044 +#define SYSREG_RAR_EX 0x0048 +#define SYSREG_RAR_NMI 0x004c +#define SYSREG_RAR_DBG 0x0050 +#define SYSREG_JECR 0x0054 +#define SYSREG_JOSP 0x0058 +#define SYSREG_JAVA_LV0 0x005c +#define SYSREG_JAVA_LV1 0x0060 +#define SYSREG_JAVA_LV2 0x0064 +#define SYSREG_JAVA_LV3 0x0068 +#define SYSREG_JAVA_LV4 0x006c +#define SYSREG_JAVA_LV5 0x0070 +#define SYSREG_JAVA_LV6 0x0074 +#define SYSREG_JAVA_LV7 0x0078 +#define SYSREG_JTBA 0x007c +#define SYSREG_JBCR 0x0080 +#define SYSREG_CONFIG0 0x0100 +#define SYSREG_CONFIG1 0x0104 +#define SYSREG_COUNT 0x0108 +#define SYSREG_COMPARE 0x010c +#define SYSREG_TLBEHI 0x0110 +#define SYSREG_TLBELO 0x0114 +#define SYSREG_PTBR 0x0118 +#define SYSREG_TLBEAR 0x011c +#define SYSREG_MMUCR 0x0120 +#define SYSREG_TLBARLO 0x0124 +#define SYSREG_TLBARHI 0x0128 +#define SYSREG_PCCNT 0x012c +#define SYSREG_PCNT0 0x0130 +#define SYSREG_PCNT1 0x0134 +#define SYSREG_PCCR 0x0138 +#define SYSREG_BEAR 0x013c +#define SYSREG_SABAL 0x0300 +#define SYSREG_SABAH 0x0304 +#define SYSREG_SABD 0x0308 /* Bitfields in SR */ -#define SYSREG_SR_C_OFFSET 0 -#define SYSREG_SR_C_SIZE 1 -#define SYSREG_Z_OFFSET 1 -#define SYSREG_Z_SIZE 1 -#define SYSREG_SR_N_OFFSET 2 -#define SYSREG_SR_N_SIZE 1 -#define SYSREG_SR_V_OFFSET 3 -#define SYSREG_SR_V_SIZE 1 -#define SYSREG_Q_OFFSET 4 -#define SYSREG_Q_SIZE 1 -#define SYSREG_GM_OFFSET 16 -#define SYSREG_GM_SIZE 1 -#define SYSREG_I0M_OFFSET 17 -#define SYSREG_I0M_SIZE 1 -#define SYSREG_I1M_OFFSET 18 -#define SYSREG_I1M_SIZE 1 -#define SYSREG_I2M_OFFSET 19 -#define SYSREG_I2M_SIZE 1 -#define SYSREG_I3M_OFFSET 20 -#define SYSREG_I3M_SIZE 1 -#define SYSREG_EM_OFFSET 21 -#define SYSREG_EM_SIZE 1 -#define SYSREG_M0_OFFSET 22 -#define SYSREG_M0_SIZE 1 -#define SYSREG_M1_OFFSET 23 -#define SYSREG_M1_SIZE 1 -#define SYSREG_M2_OFFSET 24 -#define SYSREG_M2_SIZE 1 -#define SYSREG_SR_D_OFFSET 26 -#define SYSREG_SR_D_SIZE 1 -#define SYSREG_DM_OFFSET 27 -#define SYSREG_DM_SIZE 1 -#define SYSREG_SR_J_OFFSET 28 -#define SYSREG_SR_J_SIZE 1 -#define SYSREG_R_OFFSET 29 -#define SYSREG_R_SIZE 1 -#define SYSREG_H_OFFSET 30 -#define SYSREG_H_SIZE 1 - -/* Bitfields in EVBA */ - -/* Bitfields in ACBA */ +#define SYSREG_SR_C_OFFSET 0 +#define SYSREG_SR_C_SIZE 1 +#define SYSREG_Z_OFFSET 1 +#define SYSREG_Z_SIZE 1 +#define SYSREG_SR_N_OFFSET 2 +#define SYSREG_SR_N_SIZE 1 +#define SYSREG_SR_V_OFFSET 3 +#define SYSREG_SR_V_SIZE 1 +#define SYSREG_Q_OFFSET 4 +#define SYSREG_Q_SIZE 1 +#define SYSREG_L_OFFSET 5 +#define SYSREG_L_SIZE 1 +#define SYSREG_T_OFFSET 14 +#define SYSREG_T_SIZE 1 +#define SYSREG_SR_R_OFFSET 15 +#define SYSREG_SR_R_SIZE 1 +#define SYSREG_GM_OFFSET 16 +#define SYSREG_GM_SIZE 1 +#define SYSREG_I0M_OFFSET 17 +#define SYSREG_I0M_SIZE 1 +#define SYSREG_I1M_OFFSET 18 +#define SYSREG_I1M_SIZE 1 +#define SYSREG_I2M_OFFSET 19 +#define SYSREG_I2M_SIZE 1 +#define SYSREG_I3M_OFFSET 20 +#define SYSREG_I3M_SIZE 1 +#define SYSREG_EM_OFFSET 21 +#define SYSREG_EM_SIZE 1 +#define SYSREG_M0_OFFSET 22 +#define SYSREG_M0_SIZE 1 +#define SYSREG_M1_OFFSET 23 +#define SYSREG_M1_SIZE 1 +#define SYSREG_M2_OFFSET 24 +#define SYSREG_M2_SIZE 1 +#define SYSREG_SR_D_OFFSET 26 +#define SYSREG_SR_D_SIZE 1 +#define SYSREG_DM_OFFSET 27 +#define SYSREG_DM_SIZE 1 +#define SYSREG_SR_J_OFFSET 28 +#define SYSREG_SR_J_SIZE 1 +#define SYSREG_H_OFFSET 29 +#define SYSREG_H_SIZE 1 /* Bitfields in CPUCR */ -#define SYSREG_BI_OFFSET 0 -#define SYSREG_BI_SIZE 1 -#define SYSREG_BE_OFFSET 1 -#define SYSREG_BE_SIZE 1 -#define SYSREG_FE_OFFSET 2 -#define SYSREG_FE_SIZE 1 -#define SYSREG_RE_OFFSET 3 -#define SYSREG_RE_SIZE 1 -#define SYSREG_IBE_OFFSET 4 -#define SYSREG_IBE_SIZE 1 -#define SYSREG_IEE_OFFSET 5 -#define SYSREG_IEE_SIZE 1 - -/* Bitfields in ECR */ -#define SYSREG_ECR_OFFSET 0 -#define SYSREG_ECR_SIZE 32 - -/* Bitfields in RSR_SUP */ - -/* Bitfields in RSR_INT0 */ - -/* Bitfields in RSR_INT1 */ - -/* Bitfields in RSR_INT2 */ - -/* Bitfields in RSR_INT3 */ - -/* Bitfields in RSR_EX */ - -/* Bitfields in RSR_NMI */ - -/* Bitfields in RSR_DBG */ - -/* Bitfields in RAR_SUP */ - -/* Bitfields in RAR_INT0 */ - -/* Bitfields in RAR_INT1 */ - -/* Bitfields in RAR_INT2 */ - -/* Bitfields in RAR_INT3 */ - -/* Bitfields in RAR_EX */ - -/* Bitfields in RAR_NMI */ - -/* Bitfields in RAR_DBG */ - -/* Bitfields in JECR */ - -/* Bitfields in JOSP */ - -/* Bitfields in JAVA_LV0 */ - -/* Bitfields in JAVA_LV1 */ - -/* Bitfields in JAVA_LV2 */ - -/* Bitfields in JAVA_LV3 */ - -/* Bitfields in JAVA_LV4 */ - -/* Bitfields in JAVA_LV5 */ - -/* Bitfields in JAVA_LV6 */ - -/* Bitfields in JAVA_LV7 */ - -/* Bitfields in JTBA */ - -/* Bitfields in JBCR */ +#define SYSREG_BI_OFFSET 0 +#define SYSREG_BI_SIZE 1 +#define SYSREG_BE_OFFSET 1 +#define SYSREG_BE_SIZE 1 +#define SYSREG_FE_OFFSET 2 +#define SYSREG_FE_SIZE 1 +#define SYSREG_RE_OFFSET 3 +#define SYSREG_RE_SIZE 1 +#define SYSREG_IBE_OFFSET 4 +#define SYSREG_IBE_SIZE 1 +#define SYSREG_IEE_OFFSET 5 +#define SYSREG_IEE_SIZE 1 /* Bitfields in CONFIG0 */ -#define SYSREG_CONFIG0_D_OFFSET 1 -#define SYSREG_CONFIG0_D_SIZE 1 -#define SYSREG_CONFIG0_S_OFFSET 2 -#define SYSREG_CONFIG0_S_SIZE 1 -#define SYSREG_O_OFFSET 3 -#define SYSREG_O_SIZE 1 -#define SYSREG_P_OFFSET 4 -#define SYSREG_P_SIZE 1 -#define SYSREG_CONFIG0_J_OFFSET 5 -#define SYSREG_CONFIG0_J_SIZE 1 -#define SYSREG_F_OFFSET 6 -#define SYSREG_F_SIZE 1 -#define SYSREG_MMUT_OFFSET 7 -#define SYSREG_MMUT_SIZE 3 -#define SYSREG_AR_OFFSET 10 -#define SYSREG_AR_SIZE 3 -#define SYSREG_AT_OFFSET 13 -#define SYSREG_AT_SIZE 3 -#define SYSREG_PROCESSORREVISION_OFFSET 16 -#define SYSREG_PROCESSORREVISION_SIZE 8 -#define SYSREG_PROCESSORID_OFFSET 24 -#define SYSREG_PROCESSORID_SIZE 8 +#define SYSREG_CONFIG0_R_OFFSET 0 +#define SYSREG_CONFIG0_R_SIZE 1 +#define SYSREG_CONFIG0_D_OFFSET 1 +#define SYSREG_CONFIG0_D_SIZE 1 +#define SYSREG_CONFIG0_S_OFFSET 2 +#define SYSREG_CONFIG0_S_SIZE 1 +#define SYSREG_CONFIG0_O_OFFSET 3 +#define SYSREG_CONFIG0_O_SIZE 1 +#define SYSREG_CONFIG0_P_OFFSET 4 +#define SYSREG_CONFIG0_P_SIZE 1 +#define SYSREG_CONFIG0_J_OFFSET 5 +#define SYSREG_CONFIG0_J_SIZE 1 +#define SYSREG_CONFIG0_F_OFFSET 6 +#define SYSREG_CONFIG0_F_SIZE 1 +#define SYSREG_MMUT_OFFSET 7 +#define SYSREG_MMUT_SIZE 3 +#define SYSREG_AR_OFFSET 10 +#define SYSREG_AR_SIZE 3 +#define SYSREG_AT_OFFSET 13 +#define SYSREG_AT_SIZE 3 +#define SYSREG_PROCESSORREVISION_OFFSET 16 +#define SYSREG_PROCESSORREVISION_SIZE 8 +#define SYSREG_PROCESSORID_OFFSET 24 +#define SYSREG_PROCESSORID_SIZE 8 /* Bitfields in CONFIG1 */ -#define SYSREG_DASS_OFFSET 0 -#define SYSREG_DASS_SIZE 3 -#define SYSREG_DLSZ_OFFSET 3 -#define SYSREG_DLSZ_SIZE 3 -#define SYSREG_DSET_OFFSET 6 -#define SYSREG_DSET_SIZE 4 -#define SYSREG_IASS_OFFSET 10 -#define SYSREG_IASS_SIZE 2 -#define SYSREG_ILSZ_OFFSET 13 -#define SYSREG_ILSZ_SIZE 3 -#define SYSREG_ISET_OFFSET 16 -#define SYSREG_ISET_SIZE 4 -#define SYSREG_DMMUSZ_OFFSET 20 -#define SYSREG_DMMUSZ_SIZE 6 -#define SYSREG_IMMUSZ_OFFSET 26 -#define SYSREG_IMMUSZ_SIZE 6 - -/* Bitfields in COUNT */ - -/* Bitfields in COMPARE */ +#define SYSREG_DASS_OFFSET 0 +#define SYSREG_DASS_SIZE 3 +#define SYSREG_DLSZ_OFFSET 3 +#define SYSREG_DLSZ_SIZE 3 +#define SYSREG_DSET_OFFSET 6 +#define SYSREG_DSET_SIZE 4 +#define SYSREG_IASS_OFFSET 10 +#define SYSREG_IASS_SIZE 3 +#define SYSREG_ILSZ_OFFSET 13 +#define SYSREG_ILSZ_SIZE 3 +#define SYSREG_ISET_OFFSET 16 +#define SYSREG_ISET_SIZE 4 +#define SYSREG_DMMUSZ_OFFSET 20 +#define SYSREG_DMMUSZ_SIZE 6 +#define SYSREG_IMMUSZ_OFFSET 26 +#define SYSREG_IMMUSZ_SIZE 6 /* Bitfields in TLBEHI */ -#define SYSREG_ASID_OFFSET 0 -#define SYSREG_ASID_SIZE 8 -#define SYSREG_TLBEHI_I_OFFSET 8 -#define SYSREG_TLBEHI_I_SIZE 1 -#define SYSREG_TLBEHI_V_OFFSET 9 -#define SYSREG_TLBEHI_V_SIZE 1 -#define SYSREG_VPN_OFFSET 10 -#define SYSREG_VPN_SIZE 22 +#define SYSREG_ASID_OFFSET 0 +#define SYSREG_ASID_SIZE 8 +#define SYSREG_TLBEHI_I_OFFSET 8 +#define SYSREG_TLBEHI_I_SIZE 1 +#define SYSREG_TLBEHI_V_OFFSET 9 +#define SYSREG_TLBEHI_V_SIZE 1 +#define SYSREG_VPN_OFFSET 10 +#define SYSREG_VPN_SIZE 22 /* Bitfields in TLBELO */ -#define SYSREG_W_OFFSET 0 -#define SYSREG_W_SIZE 1 -#define SYSREG_TLBELO_D_OFFSET 1 -#define SYSREG_TLBELO_D_SIZE 1 -#define SYSREG_SZ_OFFSET 2 -#define SYSREG_SZ_SIZE 2 -#define SYSREG_AP_OFFSET 4 -#define SYSREG_AP_SIZE 3 -#define SYSREG_B_OFFSET 7 -#define SYSREG_B_SIZE 1 -#define SYSREG_G_OFFSET 8 -#define SYSREG_G_SIZE 1 -#define SYSREG_TLBELO_C_OFFSET 9 -#define SYSREG_TLBELO_C_SIZE 1 -#define SYSREG_PFN_OFFSET 10 -#define SYSREG_PFN_SIZE 22 - -/* Bitfields in PTBR */ - -/* Bitfields in TLBEAR */ +#define SYSREG_W_OFFSET 0 +#define SYSREG_W_SIZE 1 +#define SYSREG_TLBELO_D_OFFSET 1 +#define SYSREG_TLBELO_D_SIZE 1 +#define SYSREG_SZ_OFFSET 2 +#define SYSREG_SZ_SIZE 2 +#define SYSREG_AP_OFFSET 4 +#define SYSREG_AP_SIZE 3 +#define SYSREG_B_OFFSET 7 +#define SYSREG_B_SIZE 1 +#define SYSREG_G_OFFSET 8 +#define SYSREG_G_SIZE 1 +#define SYSREG_TLBELO_C_OFFSET 9 +#define SYSREG_TLBELO_C_SIZE 1 +#define SYSREG_PFN_OFFSET 10 +#define SYSREG_PFN_SIZE 22 /* Bitfields in MMUCR */ -#define SYSREG_E_OFFSET 0 -#define SYSREG_E_SIZE 1 -#define SYSREG_M_OFFSET 1 -#define SYSREG_M_SIZE 1 -#define SYSREG_MMUCR_I_OFFSET 2 -#define SYSREG_MMUCR_I_SIZE 1 -#define SYSREG_MMUCR_N_OFFSET 3 -#define SYSREG_MMUCR_N_SIZE 1 -#define SYSREG_MMUCR_S_OFFSET 4 -#define SYSREG_MMUCR_S_SIZE 1 -#define SYSREG_DLA_OFFSET 8 -#define SYSREG_DLA_SIZE 6 -#define SYSREG_DRP_OFFSET 14 -#define SYSREG_DRP_SIZE 6 -#define SYSREG_ILA_OFFSET 20 -#define SYSREG_ILA_SIZE 6 -#define SYSREG_IRP_OFFSET 26 -#define SYSREG_IRP_SIZE 6 - -/* Bitfields in TLBARLO */ - -/* Bitfields in TLBARHI */ - -/* Bitfields in PCCNT */ - -/* Bitfields in PCNT0 */ - -/* Bitfields in PCNT1 */ +#define SYSREG_E_OFFSET 0 +#define SYSREG_E_SIZE 1 +#define SYSREG_M_OFFSET 1 +#define SYSREG_M_SIZE 1 +#define SYSREG_MMUCR_I_OFFSET 2 +#define SYSREG_MMUCR_I_SIZE 1 +#define SYSREG_MMUCR_N_OFFSET 3 +#define SYSREG_MMUCR_N_SIZE 1 +#define SYSREG_MMUCR_S_OFFSET 4 +#define SYSREG_MMUCR_S_SIZE 1 +#define SYSREG_DLA_OFFSET 8 +#define SYSREG_DLA_SIZE 6 +#define SYSREG_DRP_OFFSET 14 +#define SYSREG_DRP_SIZE 6 +#define SYSREG_ILA_OFFSET 20 +#define SYSREG_ILA_SIZE 6 +#define SYSREG_IRP_OFFSET 26 +#define SYSREG_IRP_SIZE 6 /* Bitfields in PCCR */ - -/* Bitfields in BEAR */ +#define SYSREG_PCCR_R_OFFSET 1 +#define SYSREG_PCCR_R_SIZE 1 +#define SYSREG_PCCR_C_OFFSET 2 +#define SYSREG_PCCR_C_SIZE 1 +#define SYSREG_PCCR_S_OFFSET 3 +#define SYSREG_PCCR_S_SIZE 1 +#define SYSREG_IEC_OFFSET 4 +#define SYSREG_IEC_SIZE 1 +#define SYSREG_IE0_OFFSET 5 +#define SYSREG_IE0_SIZE 1 +#define SYSREG_IE1_OFFSET 6 +#define SYSREG_IE1_SIZE 1 +#define SYSREG_FC_OFFSET 8 +#define SYSREG_FC_SIZE 1 +#define SYSREG_F0_OFFSET 9 +#define SYSREG_F0_SIZE 1 +#define SYSREG_F1_OFFSET 10 +#define SYSREG_F1_SIZE 1 +#define SYSREG_CONF0_OFFSET 12 +#define SYSREG_CONF0_SIZE 6 +#define SYSREG_CONF1_OFFSET 18 +#define SYSREG_CONF1_SIZE 6 /* Constants for ECR */ -#define ECR_UNRECOVERABLE 0 -#define ECR_TLB_MULTIPLE 1 -#define ECR_BUS_ERROR_WRITE 2 -#define ECR_BUS_ERROR_READ 3 -#define ECR_NMI 4 -#define ECR_ADDR_ALIGN_X 5 -#define ECR_PROTECTION_X 6 -#define ECR_DEBUG 7 -#define ECR_ILLEGAL_OPCODE 8 -#define ECR_UNIMPL_INSTRUCTION 9 -#define ECR_PRIVILEGE_VIOLATION 10 -#define ECR_FPE 11 -#define ECR_COPROC_ABSENT 12 -#define ECR_ADDR_ALIGN_R 13 -#define ECR_ADDR_ALIGN_W 14 -#define ECR_PROTECTION_R 15 -#define ECR_PROTECTION_W 16 -#define ECR_DTLB_MODIFIED 17 -#define ECR_TLB_MISS_X 20 -#define ECR_TLB_MISS_R 24 -#define ECR_TLB_MISS_W 28 +#define ECR_UNRECOVERABLE 0 +#define ECR_TLB_MULTIPLE 1 +#define ECR_BUS_ERROR_WRITE 2 +#define ECR_BUS_ERROR_READ 3 +#define ECR_NMI 4 +#define ECR_ADDR_ALIGN_X 5 +#define ECR_PROTECTION_X 6 +#define ECR_DEBUG 7 +#define ECR_ILLEGAL_OPCODE 8 +#define ECR_UNIMPL_INSTRUCTION 9 +#define ECR_PRIVILEGE_VIOLATION 10 +#define ECR_FPE 11 +#define ECR_COPROC_ABSENT 12 +#define ECR_ADDR_ALIGN_R 13 +#define ECR_ADDR_ALIGN_W 14 +#define ECR_PROTECTION_R 15 +#define ECR_PROTECTION_W 16 +#define ECR_DTLB_MODIFIED 17 +#define ECR_TLB_MISS_X 20 +#define ECR_TLB_MISS_R 24 +#define ECR_TLB_MISS_W 28 /* Bit manipulation macros */ -#define SYSREG_BIT(name) (1 << SYSREG_##name##_OFFSET) -#define SYSREG_BF(name,value) (((value) & ((1 << SYSREG_##name##_SIZE) - 1)) << SYSREG_##name##_OFFSET) -#define SYSREG_BFEXT(name,value) (((value) >> SYSREG_##name##_OFFSET) & ((1 << SYSREG_##name##_SIZE) - 1)) -#define SYSREG_BFINS(name,value,old) (((old) & ~(((1 << SYSREG_##name##_SIZE) - 1) << SYSREG_##name##_OFFSET)) | SYSREG_BF(name,value)) +#define SYSREG_BIT(name) \ + (1 << SYSREG_##name##_OFFSET) +#define SYSREG_BF(name,value) \ + (((value) & ((1 << SYSREG_##name##_SIZE) - 1)) \ + << SYSREG_##name##_OFFSET) +#define SYSREG_BFEXT(name,value)\ + (((value) >> SYSREG_##name##_OFFSET) \ + & ((1 << SYSREG_##name##_SIZE) - 1)) +#define SYSREG_BFINS(name,value,old) \ + (((old) & ~(((1 << SYSREG_##name##_SIZE) - 1) \ + << SYSREG_##name##_OFFSET)) \ + | SYSREG_BF(name,value)) +/* Register access macros */ #ifdef __CHECKER__ extern unsigned long __builtin_mfsr(unsigned long reg); extern void __builtin_mtsr(unsigned long reg, unsigned long value); #endif -/* Register access macros */ -#define sysreg_read(reg) __builtin_mfsr(SYSREG_##reg) -#define sysreg_write(reg, value) __builtin_mtsr(SYSREG_##reg, value) +#define sysreg_read(reg) __builtin_mfsr(SYSREG_##reg) +#define sysreg_write(reg, value) __builtin_mtsr(SYSREG_##reg, value) -#endif /* __ASM_AVR32_SYSREG_H__ */ +#endif /* __ASM_AVR32_SYSREG_H */ diff --git a/include/asm-avr32/system.h b/include/asm-avr32/system.h index ac59605..a8236ba 100644 --- a/include/asm-avr32/system.h +++ b/include/asm-avr32/system.h @@ -9,6 +9,7 @@ #ifndef __ASM_AVR32_SYSTEM_H #define __ASM_AVR32_SYSTEM_H #include +#include #include #include @@ -140,15 +141,9 @@ #define cmpxchg(ptr, old, new) \ sizeof(*(ptr)))) struct pt_regs; -extern void __die(const char *, struct pt_regs *, unsigned long, - const char *, const char *, unsigned long); -extern void __die_if_kernel(const char *, struct pt_regs *, unsigned long, - const char *, const char *, unsigned long); - -#define die(msg, regs, err) \ - __die(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__) -#define die_if_kernel(msg, regs, err) \ - __die_if_kernel(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__) +void NORET_TYPE die(const char *str, struct pt_regs *regs, long err); +void _exception(long signr, struct pt_regs *regs, int code, + unsigned long addr); #define arch_align_stack(x) (x) diff --git a/include/asm-avr32/thread_info.h b/include/asm-avr32/thread_info.h index d1f5b35..a2e606d 100644 --- a/include/asm-avr32/thread_info.h +++ b/include/asm-avr32/thread_info.h @@ -83,6 +83,7 @@ #define TIF_BREAKPOINT 5 /* true if we #define TIF_SINGLE_STEP 6 /* single step after next break */ #define TIF_MEMDIE 7 #define TIF_RESTORE_SIGMASK 8 /* restore signal mask in do_signal */ +#define TIF_CPU_GOING_TO_SLEEP 9 /* CPU is entering sleep 0 mode */ #define TIF_USERSPACE 31 /* true if FS sets userspace */ #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) @@ -94,6 +95,7 @@ #define _TIF_BREAKPOINT (1 << TIF_BREAK #define _TIF_SINGLE_STEP (1 << TIF_SINGLE_STEP) #define _TIF_MEMDIE (1 << TIF_MEMDIE) #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) +#define _TIF_CPU_GOING_TO_SLEEP (1 << TIF_CPU_GOING_TO_SLEEP) /* XXX: These two masks must never span more than 16 bits! */ /* work to do on interrupt/exception return */ diff --git a/include/asm-avr32/uaccess.h b/include/asm-avr32/uaccess.h index 74a679e..ed09239 100644 --- a/include/asm-avr32/uaccess.h +++ b/include/asm-avr32/uaccess.h @@ -181,24 +181,23 @@ extern int __put_user_bad(void); #define __get_user_nocheck(x, ptr, size) \ ({ \ - typeof(*(ptr)) __gu_val = (typeof(*(ptr)) __force)0; \ + unsigned long __gu_val = 0; \ int __gu_err = 0; \ \ switch (size) { \ case 1: __get_user_asm("ub", __gu_val, ptr, __gu_err); break; \ case 2: __get_user_asm("uh", __gu_val, ptr, __gu_err); break; \ case 4: __get_user_asm("w", __gu_val, ptr, __gu_err); break; \ - case 8: __get_user_asm("d", __gu_val, ptr, __gu_err); break; \ default: __gu_err = __get_user_bad(); break; \ } \ \ - x = __gu_val; \ + x = (typeof(*(ptr)))__gu_val; \ __gu_err; \ }) #define __get_user_check(x, ptr, size) \ ({ \ - typeof(*(ptr)) __gu_val = (typeof(*(ptr)) __force)0; \ + unsigned long __gu_val = 0; \ const typeof(*(ptr)) __user * __gu_addr = (ptr); \ int __gu_err = 0; \ \ @@ -216,10 +215,6 @@ ({ \ __get_user_asm("w", __gu_val, __gu_addr, \ __gu_err); \ break; \ - case 8: \ - __get_user_asm("d", __gu_val, __gu_addr, \ - __gu_err); \ - break; \ default: \ __gu_err = __get_user_bad(); \ break; \ @@ -227,7 +222,7 @@ ({ \ } else { \ __gu_err = -EFAULT; \ } \ - x = __gu_val; \ + x = (typeof(*(ptr)))__gu_val; \ __gu_err; \ }) diff --git a/include/asm-cris/socket.h b/include/asm-cris/socket.h index 01cfdf1..5b18dfd 100644 --- a/include/asm-cris/socket.h +++ b/include/asm-cris/socket.h @@ -51,6 +51,8 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-cris/sockios.h b/include/asm-cris/sockios.h index 6c4012f..cfe7bfe 100644 --- a/include/asm-cris/sockios.h +++ b/include/asm-cris/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif diff --git a/include/asm-frv/socket.h b/include/asm-frv/socket.h index 31db18f..a823bef 100644 --- a/include/asm-frv/socket.h +++ b/include/asm-frv/socket.h @@ -49,6 +49,8 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-frv/sockios.h b/include/asm-frv/sockios.h index 8a6e4b2..5dbdd13 100644 --- a/include/asm-frv/sockios.h +++ b/include/asm-frv/sockios.h @@ -7,7 +7,8 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* _ASM_SOCKIOS__ */ diff --git a/include/asm-generic/div64.h b/include/asm-generic/div64.h index 8f4e319..a4a4937 100644 --- a/include/asm-generic/div64.h +++ b/include/asm-generic/div64.h @@ -30,6 +30,11 @@ # define do_div(n,base) ({ \ __rem; \ }) +static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor) +{ + return dividend / divisor; +} + #elif BITS_PER_LONG == 32 extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor); @@ -49,6 +54,8 @@ # define do_div(n,base) ({ \ __rem; \ }) +extern uint64_t div64_64(uint64_t dividend, uint64_t divisor); + #else /* BITS_PER_LONG == ?? */ # error do_div() does not yet support the C64 diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 6d7e279..dc8f99e 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -139,8 +139,15 @@ #ifndef __HAVE_ARCH_PTE_SAME #define pte_same(A,B) (pte_val(A) == pte_val(B)) #endif -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -#define page_test_and_clear_dirty(page) (0) +#ifndef __HAVE_ARCH_PAGE_TEST_DIRTY +#define page_test_dirty(page) (0) +#endif + +#ifndef __HAVE_ARCH_PAGE_CLEAR_DIRTY +#define page_clear_dirty(page) do { } while (0) +#endif + +#ifndef __HAVE_ARCH_PAGE_TEST_DIRTY #define pte_maybe_dirty(pte) pte_dirty(pte) #else #define pte_maybe_dirty(pte) (1) diff --git a/include/asm-h8300/socket.h b/include/asm-h8300/socket.h index ebc830f..39911d8 100644 --- a/include/asm-h8300/socket.h +++ b/include/asm-h8300/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-h8300/sockios.h b/include/asm-h8300/sockios.h index d005d95..e9c7ec8 100644 --- a/include/asm-h8300/sockios.h +++ b/include/asm-h8300/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* __ARCH_H8300_SOCKIOS__ */ diff --git a/include/asm-i386/div64.h b/include/asm-i386/div64.h index 75c67c7..438e980 100644 --- a/include/asm-i386/div64.h +++ b/include/asm-i386/div64.h @@ -1,6 +1,8 @@ #ifndef __I386_DIV64 #define __I386_DIV64 +#include + /* * do_div() is NOT a C function. It wants to return * two values (the quotient and the remainder), but @@ -45,4 +47,6 @@ div_ll_X_l_rem(long long divs, long div, return dum2; } + +extern uint64_t div64_64(uint64_t dividend, uint64_t divisor); #endif diff --git a/include/asm-i386/socket.h b/include/asm-i386/socket.h index 5755d57..99ca648 100644 --- a/include/asm-i386/socket.h +++ b/include/asm-i386/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-i386/sockios.h b/include/asm-i386/sockios.h index 6b747f8..ff528c7 100644 --- a/include/asm-i386/sockios.h +++ b/include/asm-i386/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif diff --git a/include/asm-ia64/socket.h b/include/asm-ia64/socket.h index d638ef3..9e42ce4 100644 --- a/include/asm-ia64/socket.h +++ b/include/asm-ia64/socket.h @@ -58,5 +58,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_IA64_SOCKET_H */ diff --git a/include/asm-ia64/sockios.h b/include/asm-ia64/sockios.h index cf94857..15c9246 100644 --- a/include/asm-ia64/sockios.h +++ b/include/asm-ia64/sockios.h @@ -14,6 +14,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* _ASM_IA64_SOCKIOS_H */ diff --git a/include/asm-m32r/socket.h b/include/asm-m32r/socket.h index acdf748..793d5d3 100644 --- a/include/asm-m32r/socket.h +++ b/include/asm-m32r/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_M32R_SOCKET_H */ diff --git a/include/asm-m32r/sockios.h b/include/asm-m32r/sockios.h index f89962e..6c1fb9b 100644 --- a/include/asm-m32r/sockios.h +++ b/include/asm-m32r/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* _ASM_M32R_SOCKIOS_H */ diff --git a/include/asm-m68k/div64.h b/include/asm-m68k/div64.h index 9f65de1..33caad1 100644 --- a/include/asm-m68k/div64.h +++ b/include/asm-m68k/div64.h @@ -1,6 +1,8 @@ #ifndef _M68K_DIV64_H #define _M68K_DIV64_H +#include + /* n = n / base; return rem; */ #define do_div(n, base) ({ \ @@ -23,4 +25,5 @@ #define do_div(n, base) ({ \ __rem; \ }) +extern uint64_t div64_64(uint64_t dividend, uint64_t divisor); #endif /* _M68K_DIV64_H */ diff --git a/include/asm-m68k/socket.h b/include/asm-m68k/socket.h index a5966ec..6d21b90 100644 --- a/include/asm-m68k/socket.h +++ b/include/asm-m68k/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-m68k/sockios.h b/include/asm-m68k/sockios.h index 9b9ed97..c04a239 100644 --- a/include/asm-m68k/sockios.h +++ b/include/asm-m68k/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* __ARCH_M68K_SOCKIOS__ */ diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index 28d907d..4933b49 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -96,6 +96,6 @@ #define ClearPageDcacheDirty(page) \ unsigned long __init run_uncached(void *func); extern void *kmap_coherent(struct page *page, unsigned long addr); -extern void kunmap_coherent(struct page *page); +extern void kunmap_coherent(void); #endif /* _ASM_CACHEFLUSH_H */ diff --git a/include/asm-mips/div64.h b/include/asm-mips/div64.h index d107832..66189f5 100644 --- a/include/asm-mips/div64.h +++ b/include/asm-mips/div64.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2000, 2004 Maciej W. Rozycki - * Copyright (C) 2003 Ralf Baechle + * Copyright (C) 2003, 07 Ralf Baechle (ralf@linux-mips.org) * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -9,6 +9,8 @@ #ifndef _ASM_DIV64_H #define _ASM_DIV64_H +#include + #if (_MIPS_SZLONG == 32) #include @@ -78,6 +80,8 @@ #define do_div(n, base) ({ \ __quot = __quot << 32 | __low; \ (n) = __quot; \ __mod; }) + +extern uint64_t div64_64(uint64_t dividend, uint64_t divisor); #endif /* (_MIPS_SZLONG == 32) */ #if (_MIPS_SZLONG == 64) @@ -101,6 +105,11 @@ #define do_div(n, base) ({ \ (n) = __quot; \ __mod; }) +static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor) +{ + return dividend / divisor; +} + #endif /* (_MIPS_SZLONG == 64) */ #endif /* _ASM_DIV64_H */ diff --git a/include/asm-mips/jmr3927/irq.h b/include/asm-mips/jmr3927/irq.h deleted file mode 100644 index e3e7ed3..0000000 --- a/include/asm-mips/jmr3927/irq.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * linux/include/asm-mips/tx3927/irq.h - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2001 Toshiba Corporation - */ -#ifndef __ASM_TX3927_IRQ_H -#define __ASM_TX3927_IRQ_H - -#ifndef __ASSEMBLY__ - -#include - -struct tb_irq_space { - struct tb_irq_space* next; - int start_irqno; - int nr_irqs; - void (*mask_func)(int irq_nr, int space_id); - void (*unmask_func)(int irq_no, int space_id); - const char *name; - int space_id; - int can_share; -}; -extern struct tb_irq_space* tb_irq_spaces; - -static __inline__ void add_tb_irq_space(struct tb_irq_space* sp) -{ - sp->next = tb_irq_spaces; - tb_irq_spaces = sp; -} - - -struct pt_regs; -extern void -toshibaboards_spurious(struct pt_regs *regs, int irq); -extern void -toshibaboards_irqdispatch(struct pt_regs *regs, int irq); - -extern struct irqaction * -toshibaboards_get_irq_action(int irq); -extern int -toshibaboards_setup_irq(int irq, struct irqaction * new); - - -extern int (*toshibaboards_gen_iack)(void); - -#endif /* !__ASSEMBLY__ */ - -#define NR_ISA_IRQS 16 -#define TB_IRQ_IS_ISA(irq) \ - (0 <= (irq) && (irq) < NR_ISA_IRQS) -#define TB_IRQ_TO_ISA_IRQ(irq) (irq) - -#endif /* __ASM_TX3927_IRQ_H */ diff --git a/include/asm-mips/jmr3927/jmr3927.h b/include/asm-mips/jmr3927/jmr3927.h index c50e68f..958e297 100644 --- a/include/asm-mips/jmr3927/jmr3927.h +++ b/include/asm-mips/jmr3927/jmr3927.h @@ -1,5 +1,5 @@ /* - * Defines for the TJSYS JMR-TX3927/JMI-3927IO2/JMY-1394IF. + * Defines for the TJSYS JMR-TX3927 * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -12,10 +12,7 @@ #define __ASM_TX3927_JMR3927_H #include #include -#include -#ifndef __ASSEMBLY__ #include -#endif /* CS */ #define JMR3927_ROMCE0 0x1fc00000 /* 4M */ @@ -35,28 +32,10 @@ #define JMR3927_PCIIO_SIZE 0x01000000 /* #define JMR3927_SDRAM_SIZE 0x02000000 /* 32M */ #define JMR3927_PORT_BASE KSEG1 -/* select indirect initiator access per errata */ -#define JMR3927_INIT_INDIRECT_PCI -#define PCI_ISTAT_IDICC 0x1000 -#define PCI_IPCIBE_IBE_LONG 0 -#define PCI_IPCIBE_ICMD_IOREAD 2 -#define PCI_IPCIBE_ICMD_IOWRITE 3 -#define PCI_IPCIBE_ICMD_MEMREAD 6 -#define PCI_IPCIBE_ICMD_MEMWRITE 7 -#define PCI_IPCIBE_ICMD_SHIFT 4 - /* Address map (virtual address) */ #define JMR3927_ROM0_BASE (KSEG1 + JMR3927_ROMCE0) #define JMR3927_ROM1_BASE (KSEG1 + JMR3927_ROMCE1) #define JMR3927_IOC_BASE (KSEG1 + JMR3927_ROMCE2) -#define JMR3927_IOB_BASE (KSEG1 + JMR3927_ROMCE3) -#define JMR3927_ISAMEM_BASE (JMR3927_IOB_BASE) -#define JMR3927_ISAIO_BASE (JMR3927_IOB_BASE + 0x01000000) -#define JMR3927_ISAC_BASE (JMR3927_IOB_BASE + 0x02000000) -#define JMR3927_LCDVGA_REG_BASE (JMR3927_IOB_BASE + 0x03000000) -#define JMR3927_LCDVGA_MEM_BASE (JMR3927_IOB_BASE + 0x03800000) -#define JMR3927_JMY1394_BASE (KSEG1 + JMR3927_ROMCE5) -#define JMR3927_PREMIER3_BASE (JMR3927_JMY1394_BASE + 0x00100000) #define JMR3927_PCIMEM_BASE (KSEG1 + JMR3927_PCIMEM) #define JMR3927_PCIIO_BASE (KSEG1 + JMR3927_PCIIO) @@ -72,25 +51,14 @@ #define JMR3927_IOC_INTM_ADDR (JMR3927_I #define JMR3927_IOC_INTP_ADDR (JMR3927_IOC_BASE + 0x000b0000) #define JMR3927_IOC_RESET_ADDR (JMR3927_IOC_BASE + 0x000f0000) -#define JMR3927_ISAC_REV_ADDR (JMR3927_ISAC_BASE + 0x00000000) -#define JMR3927_ISAC_EINTS_ADDR (JMR3927_ISAC_BASE + 0x00200000) -#define JMR3927_ISAC_EINTM_ADDR (JMR3927_ISAC_BASE + 0x00300000) -#define JMR3927_ISAC_NMI_ADDR (JMR3927_ISAC_BASE + 0x00400000) -#define JMR3927_ISAC_LED_ADDR (JMR3927_ISAC_BASE + 0x00500000) -#define JMR3927_ISAC_INTP_ADDR (JMR3927_ISAC_BASE + 0x00800000) -#define JMR3927_ISAC_INTS1_ADDR (JMR3927_ISAC_BASE + 0x00900000) -#define JMR3927_ISAC_INTS2_ADDR (JMR3927_ISAC_BASE + 0x00a00000) -#define JMR3927_ISAC_INTM_ADDR (JMR3927_ISAC_BASE + 0x00b00000) - /* Flash ROM */ #define JMR3927_FLASH_BASE (JMR3927_ROM0_BASE) #define JMR3927_FLASH_SIZE 0x00400000 -/* bits for IOC_REV/IOC_BREV/ISAC_REV (high byte) */ +/* bits for IOC_REV/IOC_BREV (high byte) */ #define JMR3927_IDT_MASK 0xfc #define JMR3927_REV_MASK 0x03 #define JMR3927_IOC_IDT 0xe0 -#define JMR3927_ISAC_IDT 0x20 /* bits for IOC_INTS1/IOC_INTS2/IOC_INTM/IOC_INTP (high byte) */ #define JMR3927_IOC_INTB_PCIA 0 @@ -114,40 +82,6 @@ #define JMR3927_IOC_INTF_SOFT (1 << JMR3 #define JMR3927_IOC_RESET_CPU 1 #define JMR3927_IOC_RESET_PCI 2 -/* bits for ISAC_EINTS/ISAC_EINTM (high byte) */ -#define JMR3927_ISAC_EINTB_IOCHK 2 -#define JMR3927_ISAC_EINTB_BWTH 4 -#define JMR3927_ISAC_EINTF_IOCHK (1 << JMR3927_ISAC_EINTB_IOCHK) -#define JMR3927_ISAC_EINTF_BWTH (1 << JMR3927_ISAC_EINTB_BWTH) - -/* bits for ISAC_LED (high byte) */ -#define JMR3927_ISAC_LED_ISALED 0x01 -#define JMR3927_ISAC_LED_USRLED 0x02 - -/* bits for ISAC_INTS/ISAC_INTM/ISAC_INTP (high byte) */ -#define JMR3927_ISAC_INTB_IRQ5 0 -#define JMR3927_ISAC_INTB_IRQKB 1 -#define JMR3927_ISAC_INTB_IRQMOUSE 2 -#define JMR3927_ISAC_INTB_IRQ4 3 -#define JMR3927_ISAC_INTB_IRQ12 4 -#define JMR3927_ISAC_INTB_IRQ3 5 -#define JMR3927_ISAC_INTB_IRQ10 6 -#define JMR3927_ISAC_INTB_ISAER 7 -#define JMR3927_ISAC_INTF_IRQ5 (1 << JMR3927_ISAC_INTB_IRQ5) -#define JMR3927_ISAC_INTF_IRQKB (1 << JMR3927_ISAC_INTB_IRQKB) -#define JMR3927_ISAC_INTF_IRQMOUSE (1 << JMR3927_ISAC_INTB_IRQMOUSE) -#define JMR3927_ISAC_INTF_IRQ4 (1 << JMR3927_ISAC_INTB_IRQ4) -#define JMR3927_ISAC_INTF_IRQ12 (1 << JMR3927_ISAC_INTB_IRQ12) -#define JMR3927_ISAC_INTF_IRQ3 (1 << JMR3927_ISAC_INTB_IRQ3) -#define JMR3927_ISAC_INTF_IRQ10 (1 << JMR3927_ISAC_INTB_IRQ10) -#define JMR3927_ISAC_INTF_ISAER (1 << JMR3927_ISAC_INTB_ISAER) - -#ifndef __ASSEMBLY__ - -#if 0 -#define jmr3927_ioc_reg_out(d, a) ((*(volatile unsigned short *)(a)) = (d) << 8) -#define jmr3927_ioc_reg_in(a) (((*(volatile unsigned short *)(a)) >> 8) & 0xff) -#else #if defined(__BIG_ENDIAN) #define jmr3927_ioc_reg_out(d, a) ((*(volatile unsigned char *)(a)) = (d)) #define jmr3927_ioc_reg_in(a) (*(volatile unsigned char *)(a)) @@ -157,31 +91,9 @@ #define jmr3927_ioc_reg_in(a) (*(volati #else #error "No Endian" #endif -#endif -#define jmr3927_isac_reg_out(d, a) ((*(volatile unsigned char *)(a)) = (d)) -#define jmr3927_isac_reg_in(a) (*(volatile unsigned char *)(a)) - -static inline int jmr3927_have_isac(void) -{ - unsigned char idt; - unsigned long flags; - unsigned long romcr3; - - local_irq_save(flags); - romcr3 = tx3927_romcptr->cr[3]; - tx3927_romcptr->cr[3] &= 0xffffefff; /* do not wait infinitely */ - idt = jmr3927_isac_reg_in(JMR3927_ISAC_REV_ADDR) & JMR3927_IDT_MASK; - tx3927_romcptr->cr[3] = romcr3; - local_irq_restore(flags); - - return idt == JMR3927_ISAC_IDT; -} -#define jmr3927_have_nvram() \ - ((jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR) & JMR3927_IDT_MASK) == JMR3927_IOC_IDT) /* LED macro */ #define jmr3927_led_set(n/*0-16*/) jmr3927_ioc_reg_out(~(n), JMR3927_IOC_LED_ADDR) -#define jmr3927_io_led_set(n/*0-3*/) jmr3927_isac_reg_out((n), JMR3927_ISAC_LED_ADDR) #define jmr3927_led_and_set(n/*0-16*/) jmr3927_ioc_reg_out((~(n)) & jmr3927_ioc_reg_in(JMR3927_IOC_LED_ADDR), JMR3927_IOC_LED_ADDR) @@ -190,10 +102,6 @@ #define jmr3927_dipsw1() ((tx3927_pioptr #define jmr3927_dipsw2() ((tx3927_pioptr->din & (1 << 10)) == 0) #define jmr3927_dipsw3() ((jmr3927_ioc_reg_in(JMR3927_IOC_DIPSW_ADDR) & 2) == 0) #define jmr3927_dipsw4() ((jmr3927_ioc_reg_in(JMR3927_IOC_DIPSW_ADDR) & 1) == 0) -#define jmr3927_io_dipsw() (jmr3927_isac_reg_in(JMR3927_ISAC_LED_ADDR) >> 4) - - -#endif /* !__ASSEMBLY__ */ /* * IRQ mappings @@ -206,16 +114,10 @@ #endif /* !__ASSEMBLY__ */ */ #define JMR3927_NR_IRQ_IRC 16 /* On-Chip IRC */ #define JMR3927_NR_IRQ_IOC 8 /* PCI/MODEM/INT[6:7] */ -#define JMR3927_NR_IRQ_ISAC 8 /* ISA */ - -#define JMR3927_IRQ_IRC NR_ISA_IRQS +#define JMR3927_IRQ_IRC 16 #define JMR3927_IRQ_IOC (JMR3927_IRQ_IRC + JMR3927_NR_IRQ_IRC) -#define JMR3927_IRQ_ISAC (JMR3927_IRQ_IOC + JMR3927_NR_IRQ_IOC) -#define JMR3927_IRQ_END (JMR3927_IRQ_ISAC + JMR3927_NR_IRQ_ISAC) -#define JMR3927_IRQ_IS_IRC(irq) (JMR3927_IRQ_IRC <= (irq) && (irq) < JMR3927_IRQ_IOC) -#define JMR3927_IRQ_IS_IOC(irq) (JMR3927_IRQ_IOC <= (irq) && (irq) < JMR3927_IRQ_ISAC) -#define JMR3927_IRQ_IS_ISAC(irq) (JMR3927_IRQ_ISAC <= (irq) && (irq) < JMR3927_IRQ_END) +#define JMR3927_IRQ_END (JMR3927_IRQ_IOC + JMR3927_NR_IRQ_IOC) #define JMR3927_IRQ_IRC_INT0 (JMR3927_IRQ_IRC + TX3927_IR_INT0) #define JMR3927_IRQ_IRC_INT1 (JMR3927_IRQ_IRC + TX3927_IR_INT1) @@ -240,37 +142,13 @@ #define JMR3927_IRQ_IOC_MODEM (JMR3927_I #define JMR3927_IRQ_IOC_INT6 (JMR3927_IRQ_IOC + JMR3927_IOC_INTB_INT6) #define JMR3927_IRQ_IOC_INT7 (JMR3927_IRQ_IOC + JMR3927_IOC_INTB_INT7) #define JMR3927_IRQ_IOC_SOFT (JMR3927_IRQ_IOC + JMR3927_IOC_INTB_SOFT) -#define JMR3927_IRQ_ISAC_IRQ5 (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ5) -#define JMR3927_IRQ_ISAC_IRQKB (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQKB) -#define JMR3927_IRQ_ISAC_IRQMOUSE (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQMOUSE) -#define JMR3927_IRQ_ISAC_IRQ4 (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ4) -#define JMR3927_IRQ_ISAC_IRQ12 (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ12) -#define JMR3927_IRQ_ISAC_IRQ3 (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ3) -#define JMR3927_IRQ_ISAC_IRQ10 (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ10) -#define JMR3927_IRQ_ISAC_ISAER (JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_ISAER) -#if 0 /* auto detect */ -/* RTL8019AS 10M Ether (JMI-3927IO2:JPW2:1-2 Short) */ -#define JMR3927_IRQ_ETHER1 JMR3927_IRQ_IRC_INT0 -#endif /* IOC (PCI, MODEM) */ #define JMR3927_IRQ_IOCINT JMR3927_IRQ_IRC_INT1 -/* ISAC (ISA, PCMCIA, KEYBOARD, MOUSE) */ -#define JMR3927_IRQ_ISACINT JMR3927_IRQ_IRC_INT2 /* TC35815 100M Ether (JMR-TX3912:JPW4:2-3 Short) */ #define JMR3927_IRQ_ETHER0 JMR3927_IRQ_IRC_INT3 /* Clock Tick (10ms) */ #define JMR3927_IRQ_TICK JMR3927_IRQ_IRC_TMR0 -#define JMR3927_IRQ_IDE JMR3927_IRQ_ISAC_IRQ12 - -/* IEEE1394 (Note that this may conflicts with RTL8019AS 10M Ether...) */ -#define JMR3927_IRQ_PREMIER3 JMR3927_IRQ_IRC_INT0 - -/* I/O Ports */ -/* RTL8019AS 10M Ether */ -#define JMR3927_ETHER1_PORT (JMR3927_ISAIO_BASE - JMR3927_PORT_BASE + 0x280) -#define JMR3927_KBD_PORT (JMR3927_ISAIO_BASE - JMR3927_PORT_BASE + 0x00800060) -#define JMR3927_IDE_PORT (JMR3927_ISAIO_BASE - JMR3927_PORT_BASE + 0x001001f0) /* Clocks */ #define JMR3927_CORECLK 132710400 /* 132.7MHz */ diff --git a/include/asm-mips/jmr3927/tx3927.h b/include/asm-mips/jmr3927/tx3927.h index b3d67c7..0b9073b 100644 --- a/include/asm-mips/jmr3927/tx3927.h +++ b/include/asm-mips/jmr3927/tx3927.h @@ -22,8 +22,6 @@ #define TX3927_NR_SIO 2 #define TX3927_SIO_REG(ch) (0xfffef300 + (ch) * 0x100) #define TX3927_PIO_REG 0xfffef500 -#ifndef __ASSEMBLY__ - struct tx3927_sdramc_reg { volatile unsigned long cr[8]; volatile unsigned long tr[3]; @@ -164,8 +162,6 @@ struct tx3927_ccfg_reg { volatile unsigned long pdcr; }; -#endif /* !__ASSEMBLY__ */ - /* * SDRAMC */ @@ -348,8 +344,6 @@ #define TX3927_PCFG_INTDMA(ch) (0x000000 #define TX3927_PCFG_SELDMA_ALL 0x0000000f #define TX3927_PCFG_SELDMA(ch) (0x00000001<<(ch)) -#ifndef __ASSEMBLY__ - #define tx3927_sdramcptr ((struct tx3927_sdramc_reg *)TX3927_SDRAMC_REG) #define tx3927_romcptr ((struct tx3927_romc_reg *)TX3927_ROMC_REG) #define tx3927_dmaptr ((struct tx3927_dma_reg *)TX3927_DMA_REG) @@ -360,6 +354,4 @@ #define tx3927_tmrptr(ch) ((struct txx92 #define tx3927_sioptr(ch) ((struct txx927_sio_reg *)TX3927_SIO_REG(ch)) #define tx3927_pioptr ((struct txx927_pio_reg *)TX3927_PIO_REG) -#endif /* !__ASSEMBLY__ */ - #endif /* __ASM_TX3927_H */ diff --git a/include/asm-mips/jmr3927/txx927.h b/include/asm-mips/jmr3927/txx927.h index 9d5792e..58a8ff6 100644 --- a/include/asm-mips/jmr3927/txx927.h +++ b/include/asm-mips/jmr3927/txx927.h @@ -10,8 +10,6 @@ #ifndef __ASM_TXX927_H #define __ASM_TXX927_H -#ifndef __ASSEMBLY__ - struct txx927_tmr_reg { volatile unsigned long tcr; volatile unsigned long tisr; @@ -52,9 +50,6 @@ struct txx927_pio_reg { volatile unsigned long maskext; }; -#endif /* !__ASSEMBLY__ */ - - /* * TMR */ diff --git a/include/asm-mips/paccess.h b/include/asm-mips/paccess.h index 147844e..8c08fa9 100644 --- a/include/asm-mips/paccess.h +++ b/include/asm-mips/paccess.h @@ -34,7 +34,7 @@ #define __mp(x) (*(struct __large_pstruc #define __get_dbe(x,ptr,size) \ ({ \ long __gu_err; \ - __typeof(*(ptr)) __gu_val; \ + __typeof__(*(ptr)) __gu_val; \ unsigned long __gu_addr; \ __asm__("":"=r" (__gu_val)); \ __gu_addr = (unsigned long) (ptr); \ diff --git a/include/asm-mips/sgi/hpc3.h b/include/asm-mips/sgi/hpc3.h index fcec52b..c4729f5 100644 --- a/include/asm-mips/sgi/hpc3.h +++ b/include/asm-mips/sgi/hpc3.h @@ -206,7 +206,7 @@ #define HPC3_ISTAT_SC1MASK 0x200 /* irq #define HPC3_GIOMISC_ERTIME 0x1 /* Enable external timer real time. */ #define HPC3_GIOMISC_DENDIAN 0x2 /* dma descriptor endian, 1=lit 0=big */ - volatile u32 eeprom; /* EEPROM data reg. */ + u32 eeprom; /* EEPROM data reg. */ #define HPC3_EEPROM_EPROT 0x01 /* Protect register enable */ #define HPC3_EEPROM_CSEL 0x02 /* Chip select */ #define HPC3_EEPROM_ECLK 0x04 /* EEPROM clock */ diff --git a/include/asm-mips/sgi/ip22.h b/include/asm-mips/sgi/ip22.h index 6592f3b..f4981c4 100644 --- a/include/asm-mips/sgi/ip22.h +++ b/include/asm-mips/sgi/ip22.h @@ -72,7 +72,7 @@ #define SGI_SERIAL_IRQ SGINT_LOCAL2 + 5 #define ip22_is_fullhouse() (sgioc->sysid & SGIOC_SYSID_FULLHOUSE) -extern unsigned short ip22_eeprom_read(volatile unsigned int *ctrl, int reg); +extern unsigned short ip22_eeprom_read(unsigned int *ctrl, int reg); extern unsigned short ip22_nvram_read(int reg); #endif diff --git a/include/asm-mips/sgi/mc.h b/include/asm-mips/sgi/mc.h index c52f783..1576c23 100644 --- a/include/asm-mips/sgi/mc.h +++ b/include/asm-mips/sgi/mc.h @@ -57,7 +57,7 @@ #define SGIMC_SYSID_EPRESENT 0x00000010 volatile u32 divider; /* Divider reg for RPSS */ u32 _unused5; - volatile u32 eeprom; /* EEPROM byte reg for r4k */ + u32 eeprom; /* EEPROM byte reg for r4k */ #define SGIMC_EEPROM_PRE 0x00000001 /* eeprom chip PRE pin assertion */ #define SGIMC_EEPROM_CSEL 0x00000002 /* Active high, eeprom chip select */ #define SGIMC_EEPROM_SECLOCK 0x00000004 /* EEPROM serial clock */ diff --git a/include/asm-mips/sibyte/bcm1480_int.h b/include/asm-mips/sibyte/bcm1480_int.h index 42d4cf0..c0d5206 100644 --- a/include/asm-mips/sibyte/bcm1480_int.h +++ b/include/asm-mips/sibyte/bcm1480_int.h @@ -157,6 +157,7 @@ #define K_BCM1480_INT_GPIO_15 * Mask values for each interrupt */ +#define _BCM1480_INT_MASK(w,n) _SB_MAKEMASK(w,((n) & 0x3F)) #define _BCM1480_INT_MASK1(n) _SB_MAKEMASK1(((n) & 0x3F)) #define _BCM1480_INT_OFFSET(n) (((n) & 0x40) << 6) @@ -195,6 +196,7 @@ #define M_BCM1480_INT_PMI_LOW #define M_BCM1480_INT_PMI_HIGH _BCM1480_INT_MASK1(K_BCM1480_INT_PMI_HIGH) #define M_BCM1480_INT_PMO_LOW _BCM1480_INT_MASK1(K_BCM1480_INT_PMO_LOW) #define M_BCM1480_INT_PMO_HIGH _BCM1480_INT_MASK1(K_BCM1480_INT_PMO_HIGH) +#define M_BCM1480_INT_MBOX_ALL _BCM1480_INT_MASK(8,K_BCM1480_INT_MBOX_0_0) #define M_BCM1480_INT_MBOX_0_0 _BCM1480_INT_MASK1(K_BCM1480_INT_MBOX_0_0) #define M_BCM1480_INT_MBOX_0_1 _BCM1480_INT_MASK1(K_BCM1480_INT_MBOX_0_1) #define M_BCM1480_INT_MBOX_0_2 _BCM1480_INT_MASK1(K_BCM1480_INT_MBOX_0_2) diff --git a/include/asm-mips/sibyte/bcm1480_mc.h b/include/asm-mips/sibyte/bcm1480_mc.h index 6bdc941..a6a4374 100644 --- a/include/asm-mips/sibyte/bcm1480_mc.h +++ b/include/asm-mips/sibyte/bcm1480_mc.h @@ -382,6 +382,10 @@ #define M_BCM1480_MC_CS5 #define M_BCM1480_MC_CS6 _SB_MAKEMASK1(10) #define M_BCM1480_MC_CS7 _SB_MAKEMASK1(11) +#define M_BCM1480_MC_CS _SB_MAKEMASK(8,S_BCM1480_MC_CS0) +#define V_BCM1480_MC_CS(x) _SB_MAKEVALUE(x,S_BCM1480_MC_CS0) +#define G_BCM1480_MC_CS(x) _SB_GETVALUE(x,S_BCM1480_MC_CS0,M_BCM1480_MC_CS0) + #define M_BCM1480_MC_CMD_ACTIVE _SB_MAKEMASK1(16) /* @@ -412,6 +416,8 @@ #if SIBYTE_HDR_FEATURE(1480, PASS2) #define K_BCM1480_MC_DRAM_TYPE_DDR2 2 #endif +#define K_BCM1480_MC_DRAM_TYPE_DDR2_PASS1 0 + #define V_BCM1480_MC_DRAM_TYPE_JEDEC V_BCM1480_MC_DRAM_TYPE(K_BCM1480_MC_DRAM_TYPE_JEDEC) #define V_BCM1480_MC_DRAM_TYPE_FCRAM V_BCM1480_MC_DRAM_TYPE(K_BCM1480_MC_DRAM_TYPE_FCRAM) @@ -511,6 +517,22 @@ #define M_BCM1480_MC_WR_ODT6_CS4 _SB #define M_BCM1480_MC_WR_ODT6_CS6 _SB_MAKEMASK1(31) #define M_BCM1480_MC_CS_ODD_ODT_EN _SB_MAKEMASK1(32) + +#define S_BCM1480_MC_ODT0 0 +#define M_BCM1480_MC_ODT0 _SB_MAKEMASK(8,S_BCM1480_MC_ODT0) +#define V_BCM1480_MC_ODT0(x) _SB_MAKEVALUE(x,S_BCM1480_MC_ODT0) + +#define S_BCM1480_MC_ODT2 8 +#define M_BCM1480_MC_ODT2 _SB_MAKEMASK(8,S_BCM1480_MC_ODT2) +#define V_BCM1480_MC_ODT2(x) _SB_MAKEVALUE(x,S_BCM1480_MC_ODT2) + +#define S_BCM1480_MC_ODT4 16 +#define M_BCM1480_MC_ODT4 _SB_MAKEMASK(8,S_BCM1480_MC_ODT4) +#define V_BCM1480_MC_ODT4(x) _SB_MAKEVALUE(x,S_BCM1480_MC_ODT4) + +#define S_BCM1480_MC_ODT6 24 +#define M_BCM1480_MC_ODT6 _SB_MAKEMASK(8,S_BCM1480_MC_ODT6) +#define V_BCM1480_MC_ODT6(x) _SB_MAKEVALUE(x,S_BCM1480_MC_ODT6) #endif /* @@ -588,11 +610,11 @@ #define M_BCM1480_MC_DLL_REGBYPASS #define M_BCM1480_MC_DQO_SHIFT _SB_MAKEMASK1(47) #endif -#define S_BCM1480_MC_DLL_DEFAULT 48 -#define M_BCM1480_MC_DLL_DEFAULT _SB_MAKEMASK(6,S_BCM1480_MC_DLL_DEFAULT) -#define V_BCM1480_MC_DLL_DEFAULT(x) _SB_MAKEVALUE(x,S_BCM1480_MC_DLL_DEFAULT) -#define G_BCM1480_MC_DLL_DEFAULT(x) _SB_GETVALUE(x,S_BCM1480_MC_DLL_DEFAULT,M_BCM1480_MC_DLL_DEFAULT) -#define V_BCM1480_MC_DLL_DEFAULT_DEFAULT V_BCM1480_MC_DLL_DEFAULT(0x10) +#define S_BCM1480_MC_DLL_DEFAULT 48 +#define M_BCM1480_MC_DLL_DEFAULT _SB_MAKEMASK(6,S_BCM1480_MC_DLL_DEFAULT) +#define V_BCM1480_MC_DLL_DEFAULT(x) _SB_MAKEVALUE(x,S_BCM1480_MC_DLL_DEFAULT) +#define G_BCM1480_MC_DLL_DEFAULT(x) _SB_GETVALUE(x,S_BCM1480_MC_DLL_DEFAULT,M_BCM1480_MC_DLL_DEFAULT) +#define V_BCM1480_MC_DLL_DEFAULT_DEFAULT V_BCM1480_MC_DLL_DEFAULT(0x10) #if SIBYTE_HDR_FEATURE(1480, PASS2) #define S_BCM1480_MC_DLL_REGCTRL 54 diff --git a/include/asm-mips/sibyte/bcm1480_regs.h b/include/asm-mips/sibyte/bcm1480_regs.h index c2dd2fe..bda391d 100644 --- a/include/asm-mips/sibyte/bcm1480_regs.h +++ b/include/asm-mips/sibyte/bcm1480_regs.h @@ -230,6 +230,7 @@ #define R_BCM1480_DUART_ISRREG(chan) #define A_BCM1480_DUART_IMRREG(chan) (A_BCM1480_DUART(chan) + R_BCM1480_DUART_IMRREG(chan)) #define A_BCM1480_DUART_ISRREG(chan) (A_BCM1480_DUART(chan) + R_BCM1480_DUART_ISRREG(chan)) +#define A_BCM1480_DUART_IN_PORT(chan) (A_BCM1480_DUART(chan) + R_DUART_INP_ORT) /* * These constants are the absolute addresses. @@ -404,6 +405,21 @@ #define A_BCM1480_IMR_ALIAS_MAILBOX_REGI #define R_BCM1480_IMR_ALIAS_MAILBOX_0 0x0000 /* 0x0x0 */ #define R_BCM1480_IMR_ALIAS_MAILBOX_0_SET 0x0008 /* 0x0x8 */ +/* + * these macros work together to build the address of a mailbox + * register, e.g., A_BCM1480_MAILBOX_REGISTER(0,R_BCM1480_IMR_MAILBOX_SET,2) + * for mbox_0_set_cpu2 returns 0x00100240C8 + */ +#define R_BCM1480_IMR_MAILBOX_CPU 0x00 +#define R_BCM1480_IMR_MAILBOX_SET 0x08 +#define R_BCM1480_IMR_MAILBOX_CLR 0x10 +#define R_BCM1480_IMR_MAILBOX_NUM_SPACING 0x20 +#define A_BCM1480_MAILBOX_REGISTER(num,reg,cpu) \ + (A_BCM1480_IMR_CPU0_BASE + \ + (num * R_BCM1480_IMR_MAILBOX_NUM_SPACING) + \ + (cpu * BCM1480_IMR_REGISTER_SPACING) + \ + (R_BCM1480_IMR_MAILBOX_0_CPU + reg)) + /* ********************************************************************* * System Performance Counter Registers (Section 4.7) ********************************************************************* */ @@ -428,6 +444,10 @@ #define A_BCM1480_SCD_PERF_CNT_5 #define A_BCM1480_SCD_PERF_CNT_6 0x0010020500 #define A_BCM1480_SCD_PERF_CNT_7 0x0010020508 +#define BCM1480_SCD_NUM_PERF_CNT 8 +#define BCM1480_SCD_PERF_CNT_SPACING 8 +#define A_BCM1480_SCD_PERF_CNT(n) (A_SCD_PERF_CNT_0+(n*BCM1480_SCD_PERF_CNT_SPACING)) + /* ********************************************************************* * System Bus Watcher Registers (Section 4.8) ********************************************************************* */ diff --git a/include/asm-mips/sibyte/bcm1480_scd.h b/include/asm-mips/sibyte/bcm1480_scd.h index 648bed9..6111d6d 100644 --- a/include/asm-mips/sibyte/bcm1480_scd.h +++ b/include/asm-mips/sibyte/bcm1480_scd.h @@ -10,7 +10,7 @@ * ********************************************************************* * - * Copyright 2000,2001,2002,2003 + * Copyright 2000,2001,2002,2003,2004,2005 * Broadcom Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or @@ -78,6 +78,7 @@ #define K_SYS_PART_BCM1480 0x14 #define K_SYS_PART_BCM1280 0x1206 #define K_SYS_PART_BCM1455 0x1407 #define K_SYS_PART_BCM1255 0x1257 +#define K_SYS_PART_BCM1158 0x1156 /* * Manufacturing Information Register (Table 14) @@ -237,58 +238,42 @@ #define M_BCM1480_SCD_WDOG_HAS_RESET * System Performance Counter Configuration Register (Table 31) * Register: PERF_CNT_CFG_0 * - * Since the clear/enable bits are moved compared to the - * 1250 and there are more fields, this register will be BCM1480 specific. + * SPC_CFG_SRC[0-3] is the same as the 1250. + * SPC_CFG_SRC[4-7] only exist on the 1480 + * The clear/enable bits are in different locations on the 1250 and 1480. */ -#define S_BCM1480_SPC_CFG_SRC0 0 -#define M_BCM1480_SPC_CFG_SRC0 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC0) -#define V_BCM1480_SPC_CFG_SRC0(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC0) -#define G_BCM1480_SPC_CFG_SRC0(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC0,M_BCM1480_SPC_CFG_SRC0) - -#define S_BCM1480_SPC_CFG_SRC1 8 -#define M_BCM1480_SPC_CFG_SRC1 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC1) -#define V_BCM1480_SPC_CFG_SRC1(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC1) -#define G_BCM1480_SPC_CFG_SRC1(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC1,M_BCM1480_SPC_CFG_SRC1) - -#define S_BCM1480_SPC_CFG_SRC2 16 -#define M_BCM1480_SPC_CFG_SRC2 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC2) -#define V_BCM1480_SPC_CFG_SRC2(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC2) -#define G_BCM1480_SPC_CFG_SRC2(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC2,M_BCM1480_SPC_CFG_SRC2) - -#define S_BCM1480_SPC_CFG_SRC3 24 -#define M_BCM1480_SPC_CFG_SRC3 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC3) -#define V_BCM1480_SPC_CFG_SRC3(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC3) -#define G_BCM1480_SPC_CFG_SRC3(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC3,M_BCM1480_SPC_CFG_SRC3) - -#define S_BCM1480_SPC_CFG_SRC4 32 -#define M_BCM1480_SPC_CFG_SRC4 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC4) -#define V_BCM1480_SPC_CFG_SRC4(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC4) -#define G_BCM1480_SPC_CFG_SRC4(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC4,M_BCM1480_SPC_CFG_SRC4) - -#define S_BCM1480_SPC_CFG_SRC5 40 -#define M_BCM1480_SPC_CFG_SRC5 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC5) -#define V_BCM1480_SPC_CFG_SRC5(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC5) -#define G_BCM1480_SPC_CFG_SRC5(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC5,M_BCM1480_SPC_CFG_SRC5) - -#define S_BCM1480_SPC_CFG_SRC6 48 -#define M_BCM1480_SPC_CFG_SRC6 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC6) -#define V_BCM1480_SPC_CFG_SRC6(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC6) -#define G_BCM1480_SPC_CFG_SRC6(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC6,M_BCM1480_SPC_CFG_SRC6) - -#define S_BCM1480_SPC_CFG_SRC7 56 -#define M_BCM1480_SPC_CFG_SRC7 _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC7) -#define V_BCM1480_SPC_CFG_SRC7(x) _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC7) -#define G_BCM1480_SPC_CFG_SRC7(x) _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC7,M_BCM1480_SPC_CFG_SRC7) +#define S_SPC_CFG_SRC4 32 +#define M_SPC_CFG_SRC4 _SB_MAKEMASK(8,S_SPC_CFG_SRC4) +#define V_SPC_CFG_SRC4(x) _SB_MAKEVALUE(x,S_SPC_CFG_SRC4) +#define G_SPC_CFG_SRC4(x) _SB_GETVALUE(x,S_SPC_CFG_SRC4,M_SPC_CFG_SRC4) + +#define S_SPC_CFG_SRC5 40 +#define M_SPC_CFG_SRC5 _SB_MAKEMASK(8,S_SPC_CFG_SRC5) +#define V_SPC_CFG_SRC5(x) _SB_MAKEVALUE(x,S_SPC_CFG_SRC5) +#define G_SPC_CFG_SRC5(x) _SB_GETVALUE(x,S_SPC_CFG_SRC5,M_SPC_CFG_SRC5) + +#define S_SPC_CFG_SRC6 48 +#define M_SPC_CFG_SRC6 _SB_MAKEMASK(8,S_SPC_CFG_SRC6) +#define V_SPC_CFG_SRC6(x) _SB_MAKEVALUE(x,S_SPC_CFG_SRC6) +#define G_SPC_CFG_SRC6(x) _SB_GETVALUE(x,S_SPC_CFG_SRC6,M_SPC_CFG_SRC6) + +#define S_SPC_CFG_SRC7 56 +#define M_SPC_CFG_SRC7 _SB_MAKEMASK(8,S_SPC_CFG_SRC7) +#define V_SPC_CFG_SRC7(x) _SB_MAKEVALUE(x,S_SPC_CFG_SRC7) +#define G_SPC_CFG_SRC7(x) _SB_GETVALUE(x,S_SPC_CFG_SRC7,M_SPC_CFG_SRC7) /* * System Performance Counter Control Register (Table 32) * Register: PERF_CNT_CFG_1 * BCM1480 specific */ - -#define M_BCM1480_SPC_CFG_CLEAR _SB_MAKEMASK1(0) -#define M_BCM1480_SPC_CFG_ENABLE _SB_MAKEMASK1(1) +#define M_BCM1480_SPC_CFG_CLEAR _SB_MAKEMASK1(0) +#define M_BCM1480_SPC_CFG_ENABLE _SB_MAKEMASK1(1) +#if SIBYTE_HDR_FEATURE_CHIP(1480) +#define M_SPC_CFG_CLEAR M_BCM1480_SPC_CFG_CLEAR +#define M_SPC_CFG_ENABLE M_BCM1480_SPC_CFG_ENABLE +#endif /* * System Performance Counters (Table 33) @@ -405,20 +390,10 @@ #define G_BCM1480_SCD_TRSEQ_SWFUNC(x) * Trace Control Register (Table 49) * Register: TRACE_CFG * - * Bits 0..8 are the same as the BCM1250, rest are different. - * Entire register is redefined below. + * BCM1480 changes to this register (other than location of the CUR_ADDR field) + * are defined below. */ -#define M_BCM1480_SCD_TRACE_CFG_RESET _SB_MAKEMASK1(0) -#define M_BCM1480_SCD_TRACE_CFG_START_READ _SB_MAKEMASK1(1) -#define M_BCM1480_SCD_TRACE_CFG_START _SB_MAKEMASK1(2) -#define M_BCM1480_SCD_TRACE_CFG_STOP _SB_MAKEMASK1(3) -#define M_BCM1480_SCD_TRACE_CFG_FREEZE _SB_MAKEMASK1(4) -#define M_BCM1480_SCD_TRACE_CFG_FREEZE_FULL _SB_MAKEMASK1(5) -#define M_BCM1480_SCD_TRACE_CFG_DEBUG_FULL _SB_MAKEMASK1(6) -#define M_BCM1480_SCD_TRACE_CFG_FULL _SB_MAKEMASK1(7) -#define M_BCM1480_SCD_TRACE_CFG_FORCE_CNT _SB_MAKEMASK1(8) - #define S_BCM1480_SCD_TRACE_CFG_MODE 16 #define M_BCM1480_SCD_TRACE_CFG_MODE _SB_MAKEMASK(2,S_BCM1480_SCD_TRACE_CFG_MODE) #define V_BCM1480_SCD_TRACE_CFG_MODE(x) _SB_MAKEVALUE(x,S_BCM1480_SCD_TRACE_CFG_MODE) @@ -428,9 +403,4 @@ #define K_BCM1480_SCD_TRACE_CFG_MODE_BLO #define K_BCM1480_SCD_TRACE_CFG_MODE_BYTEEN_INT 1 #define K_BCM1480_SCD_TRACE_CFG_MODE_FLOW_ID 2 -#define S_BCM1480_SCD_TRACE_CFG_CUR_ADDR 24 -#define M_BCM1480_SCD_TRACE_CFG_CUR_ADDR _SB_MAKEMASK(8,S_BCM1480_SCD_TRACE_CFG_CUR_ADDR) -#define V_BCM1480_SCD_TRACE_CFG_CUR_ADDR(x) _SB_MAKEVALUE(x,S_BCM1480_SCD_TRACE_CFG_CUR_ADDR) -#define G_BCM1480_SCD_TRACE_CFG_CUR_ADDR(x) _SB_GETVALUE(x,S_BCM1480_SCD_TRACE_CFG_CUR_ADDR,M_BCM1480_SCD_TRACE_CFG_CUR_ADDR) - #endif /* _BCM1480_SCD_H */ diff --git a/include/asm-mips/sibyte/board.h b/include/asm-mips/sibyte/board.h index 3dfe29e..73bce90 100644 --- a/include/asm-mips/sibyte/board.h +++ b/include/asm-mips/sibyte/board.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation + * Copyright (C) 2000,2001,2002,2003,2004 Broadcom Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,8 +19,8 @@ #ifndef _SIBYTE_BOARD_H #define _SIBYTE_BOARD_H - #if defined(CONFIG_SIBYTE_SWARM) || defined(CONFIG_SIBYTE_PTSWARM) || \ + defined(CONFIG_SIBYTE_PT1120) || defined(CONFIG_SIBYTE_PT1125) || \ defined(CONFIG_SIBYTE_CRHONE) || defined(CONFIG_SIBYTE_CRHINE) || \ defined(CONFIG_SIBYTE_LITTLESUR) #include @@ -55,6 +55,16 @@ #else #define setleds(t0,t1,c0,c1,c2,c3) #endif /* LEDS_PHYS */ +#else + +void swarm_setup(void); + +#ifdef LEDS_PHYS +extern void setleds(char *str); +#else +#define setleds(s) do { } while (0) +#endif /* LEDS_PHYS */ + #endif /* __ASSEMBLY__ */ #endif /* _SIBYTE_BOARD_H */ diff --git a/include/asm-mips/sibyte/carmel.h b/include/asm-mips/sibyte/carmel.h index 57c53e6..11cad71 100644 --- a/include/asm-mips/sibyte/carmel.h +++ b/include/asm-mips/sibyte/carmel.h @@ -18,7 +18,6 @@ #ifndef __ASM_SIBYTE_CARMEL_H #define __ASM_SIBYTE_CARMEL_H - #include #include diff --git a/include/asm-mips/sibyte/sb1250_int.h b/include/asm-mips/sibyte/sb1250_int.h index 05c7b39..94e8299 100644 --- a/include/asm-mips/sibyte/sb1250_int.h +++ b/include/asm-mips/sibyte/sb1250_int.h @@ -45,8 +45,6 @@ #include "sb1250_defs.h" * First, the interrupt numbers. */ -#if SIBYTE_HDR_FEATURE_1250_112x - #define K_INT_SOURCES 64 #define K_INT_WATCHDOG_TIMER_0 0 @@ -152,6 +150,7 @@ #define M_INT_MBOX_0 _SB_ #define M_INT_MBOX_1 _SB_MAKEMASK1(K_INT_MBOX_1) #define M_INT_MBOX_2 _SB_MAKEMASK1(K_INT_MBOX_2) #define M_INT_MBOX_3 _SB_MAKEMASK1(K_INT_MBOX_3) +#define M_INT_MBOX_ALL _SB_MAKEMASK(4,K_INT_MBOX_0) #if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) #define M_INT_CYCLE_CP0_INT _SB_MAKEMASK1(K_INT_CYCLE_CP0_INT) #define M_INT_CYCLE_CP1_INT _SB_MAKEMASK1(K_INT_CYCLE_CP1_INT) @@ -247,5 +246,3 @@ #define M_LDTVECT_RAISEMBOX #endif /* 1250/112x */ - -#endif diff --git a/include/asm-mips/sibyte/sb1250_mac.h b/include/asm-mips/sibyte/sb1250_mac.h index adfc688..833c8b5 100644 --- a/include/asm-mips/sibyte/sb1250_mac.h +++ b/include/asm-mips/sibyte/sb1250_mac.h @@ -129,9 +129,9 @@ #define K_MAC_BYPASS_EOP 3 #define M_MAC_BYPASS_16 _SB_MAKEMASK1(42) #define M_MAC_BYPASS_FCS_CHK _SB_MAKEMASK1(43) -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define M_MAC_RX_CH_SEL_MSB _SB_MAKEMASK1(44) -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480*/ #if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define M_MAC_SPLIT_CH_SEL _SB_MAKEMASK1(45) @@ -223,9 +223,9 @@ #if SIBYTE_HDR_FEATURE_UP_TO(1250, PASS1 /* XXX: Can't enable, as it has the same name as a pass2+ define below. */ /* #define M_MAC_TX_WR_THRSH _SB_MAKEMASK(6,S_MAC_TX_WR_THRSH) */ #endif /* up to 1250 PASS1 */ -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define M_MAC_TX_WR_THRSH _SB_MAKEMASK(7,S_MAC_TX_WR_THRSH) -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480 */ #define V_MAC_TX_WR_THRSH(x) _SB_MAKEVALUE(x,S_MAC_TX_WR_THRSH) #define G_MAC_TX_WR_THRSH(x) _SB_GETVALUE(x,S_MAC_TX_WR_THRSH,M_MAC_TX_WR_THRSH) @@ -234,9 +234,9 @@ #if SIBYTE_HDR_FEATURE_UP_TO(1250, PASS1 /* XXX: Can't enable, as it has the same name as a pass2+ define below. */ /* #define M_MAC_TX_RD_THRSH _SB_MAKEMASK(6,S_MAC_TX_RD_THRSH) */ #endif /* up to 1250 PASS1 */ -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define M_MAC_TX_RD_THRSH _SB_MAKEMASK(7,S_MAC_TX_RD_THRSH) -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480 */ #define V_MAC_TX_RD_THRSH(x) _SB_MAKEVALUE(x,S_MAC_TX_RD_THRSH) #define G_MAC_TX_RD_THRSH(x) _SB_GETVALUE(x,S_MAC_TX_RD_THRSH,M_MAC_TX_RD_THRSH) @@ -260,12 +260,12 @@ #define M_MAC_RX_RL_THRSH _SB_ #define V_MAC_RX_RL_THRSH(x) _SB_MAKEVALUE(x,S_MAC_RX_RL_THRSH) #define G_MAC_RX_RL_THRSH(x) _SB_GETVALUE(x,S_MAC_RX_RL_THRSH,M_MAC_RX_RL_THRSH) -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define S_MAC_ENC_FC_THRSH _SB_MAKE64(56) #define M_MAC_ENC_FC_THRSH _SB_MAKEMASK(6,S_MAC_ENC_FC_THRSH) #define V_MAC_ENC_FC_THRSH(x) _SB_MAKEVALUE(x,S_MAC_ENC_FC_THRSH) #define G_MAC_ENC_FC_THRSH(x) _SB_GETVALUE(x,S_MAC_ENC_FC_THRSH,M_MAC_ENC_FC_THRSH) -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480 */ /* * MAC Frame Configuration Registers (Table 9-15) @@ -462,9 +462,9 @@ #define M_MAC_TX_OVRFL _SB_ #define M_MAC_LTCOL_ERR _SB_MAKEMASK1(44) #define M_MAC_EXCOL_ERR _SB_MAKEMASK1(45) #define M_MAC_CNTR_OVRFL_ERR _SB_MAKEMASK1(46) -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define M_MAC_SPLIT_EN _SB_MAKEMASK1(47) /* interrupt mask only */ -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480 */ #define S_MAC_COUNTER_ADDR _SB_MAKE64(47) #define M_MAC_COUNTER_ADDR _SB_MAKEMASK(5,S_MAC_COUNTER_ADDR) @@ -598,9 +598,9 @@ #define M_MAC_MCAST_EN _SB_MAKE #define M_MAC_MCAST_INV _SB_MAKEMASK1(4) #define M_MAC_BCAST_EN _SB_MAKEMASK1(5) #define M_MAC_DIRECT_INV _SB_MAKEMASK1(6) -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define M_MAC_ALLMCAST_EN _SB_MAKEMASK1(7) -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480 */ #define S_MAC_IPHDR_OFFSET _SB_MAKE64(8) #define M_MAC_IPHDR_OFFSET _SB_MAKEMASK(8,S_MAC_IPHDR_OFFSET) diff --git a/include/asm-mips/sibyte/sb1250_mc.h b/include/asm-mips/sibyte/sb1250_mc.h index 26e4214..4fe848f 100644 --- a/include/asm-mips/sibyte/sb1250_mc.h +++ b/include/asm-mips/sibyte/sb1250_mc.h @@ -295,7 +295,7 @@ #define M_MC_EXTERNALDECODE _SB_MAKE #if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1) #define M_MC_PRE_ON_A8 _SB_MAKEMASK1(36) -#define M_MC_RAM_WITH_A13 _SB_MAKEMASK1(38) +#define M_MC_RAM_WITH_A13 _SB_MAKEMASK1(37) #endif /* 1250 PASS3 || 112x PASS1 */ diff --git a/include/asm-mips/sibyte/sb1250_regs.h b/include/asm-mips/sibyte/sb1250_regs.h index bab3a45..da7c188 100644 --- a/include/asm-mips/sibyte/sb1250_regs.h +++ b/include/asm-mips/sibyte/sb1250_regs.h @@ -131,6 +131,7 @@ #define A_L2_EEC_ADDRESS A_L2 #endif + /* ********************************************************************* * PCI Interface Registers ********************************************************************* */ @@ -239,14 +240,14 @@ #define R_MAC_THRSH_CFG #define R_MAC_VLANTAG 0x00000110 #define R_MAC_FRAMECFG 0x00000118 #define R_MAC_EOPCNT 0x00000120 -#define R_MAC_FIFO_PTRS 0x00000130 +#define R_MAC_FIFO_PTRS 0x00000128 #define R_MAC_ADFILTER_CFG 0x00000200 #define R_MAC_ETHERNET_ADDR 0x00000208 #define R_MAC_PKT_TYPE 0x00000210 -#if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define R_MAC_ADMASK0 0x00000218 #define R_MAC_ADMASK1 0x00000220 -#endif /* 1250 PASS3 || 112x PASS1 */ +#endif /* 1250 PASS3 || 112x PASS1 || 1480 */ #define R_MAC_HASH_BASE 0x00000240 #define R_MAC_ADDR_BASE 0x00000280 #define R_MAC_CHLO0_BASE 0x00000300 @@ -256,9 +257,9 @@ #define R_MAC_STATUS #define R_MAC_INT_MASK 0x00000410 #define R_MAC_TXD_CTL 0x00000420 #define R_MAC_MDIO 0x00000428 -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define R_MAC_STATUS1 0x00000430 -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480 */ #define R_MAC_DEBUG_STATUS 0x00000448 #define MAC_HASH_COUNT 8 @@ -289,11 +290,11 @@ #define R_DUART_CMD 0x15 #define R_DUART_RX_HOLD 0x160 #define R_DUART_TX_HOLD 0x170 -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define R_DUART_FULL_CTL 0x140 #define R_DUART_OPCR_X 0x180 #define R_DUART_AUXCTL_X 0x190 -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480*/ /* @@ -308,6 +309,7 @@ #define R_DUART_ISR_B 0x34 #define R_DUART_IMR_B 0x350 #define R_DUART_OUT_PORT 0x360 #define R_DUART_OPCR 0x370 +#define R_DUART_IN_PORT 0x380 #define R_DUART_SET_OPR 0x3B0 #define R_DUART_CLEAR_OPR 0x3C0 @@ -685,12 +687,17 @@ #if SIBYTE_HDR_FEATURE(1250, PASS2) || S #define A_ADDR_TRAP_REG_DEBUG 0x0010020460 #endif /* 1250 PASS2 || 112x PASS1 || 1480 */ +#define ADDR_TRAP_SPACING 8 +#define NUM_ADDR_TRAP 4 +#define A_ADDR_TRAP_UP(n) (A_ADDR_TRAP_UP_0 + ((n) * ADDR_TRAP_SPACING)) +#define A_ADDR_TRAP_DOWN(n) (A_ADDR_TRAP_DOWN_0 + ((n) * ADDR_TRAP_SPACING)) +#define A_ADDR_TRAP_CFG(n) (A_ADDR_TRAP_CFG_0 + ((n) * ADDR_TRAP_SPACING)) + /* ********************************************************************* * System Interrupt Mapper Registers ********************************************************************* */ -#if SIBYTE_HDR_FEATURE_1250_112x #define A_IMR_CPU0_BASE 0x0010020000 #define A_IMR_CPU1_BASE 0x0010022000 #define IMR_REGISTER_SPACING 0x2000 @@ -700,6 +707,7 @@ #define A_IMR_MAPPER(cpu) (A_IMR_CPU0_BA #define A_IMR_REGISTER(cpu,reg) (A_IMR_MAPPER(cpu)+(reg)) #define R_IMR_INTERRUPT_DIAG 0x0010 +#define R_IMR_INTERRUPT_LDT 0x0018 #define R_IMR_INTERRUPT_MASK 0x0028 #define R_IMR_INTERRUPT_TRACE 0x0038 #define R_IMR_INTERRUPT_SOURCE_STATUS 0x0040 @@ -715,7 +723,14 @@ #define R_IMR_INTERRUPT_STATUS_BASE #define R_IMR_INTERRUPT_STATUS_COUNT 7 #define R_IMR_INTERRUPT_MAP_BASE 0x0200 #define R_IMR_INTERRUPT_MAP_COUNT 64 -#endif /* 1250/112x */ + +/* + * these macros work together to build the address of a mailbox + * register, e.g., A_MAILBOX_REGISTER(R_IMR_MAILBOX_SET_CPU,1) + * for mbox_0_set_cpu2 returns 0x00100240C8 + */ +#define A_MAILBOX_REGISTER(reg,cpu) \ + (A_IMR_CPU0_BASE + (cpu * IMR_REGISTER_SPACING) + reg) /* ********************************************************************* * System Performance Counter Registers @@ -727,6 +742,10 @@ #define A_SCD_PERF_CNT_1 0x00 #define A_SCD_PERF_CNT_2 0x00100204E0 #define A_SCD_PERF_CNT_3 0x00100204E8 +#define SCD_NUM_PERF_CNT 4 +#define SCD_PERF_CNT_SPACING 8 +#define A_SCD_PERF_CNT(n) (A_SCD_PERF_CNT_0+(n*SCD_PERF_CNT_SPACING)) + /* ********************************************************************* * System Bus Watcher Registers ********************************************************************* */ @@ -772,6 +791,15 @@ #define A_SCD_TRACE_SEQUENCE_5 0x00 #define A_SCD_TRACE_SEQUENCE_6 0x0010020A90 #define A_SCD_TRACE_SEQUENCE_7 0x0010020A98 +#define TRACE_REGISTER_SPACING 8 +#define TRACE_NUM_REGISTERS 8 +#define A_SCD_TRACE_EVENT(n) (((n) & 4) ? \ + (A_SCD_TRACE_EVENT_4 + (((n) & 3) * TRACE_REGISTER_SPACING)) : \ + (A_SCD_TRACE_EVENT_0 + ((n) * TRACE_REGISTER_SPACING))) +#define A_SCD_TRACE_SEQUENCE(n) (((n) & 4) ? \ + (A_SCD_TRACE_SEQUENCE_4 + (((n) & 3) * TRACE_REGISTER_SPACING)) : \ + (A_SCD_TRACE_SEQUENCE_0 + ((n) * TRACE_REGISTER_SPACING))) + /* ********************************************************************* * System Generic DMA Registers ********************************************************************* */ diff --git a/include/asm-mips/sibyte/sb1250_scd.h b/include/asm-mips/sibyte/sb1250_scd.h index b6a7d8f..9ea3da3 100644 --- a/include/asm-mips/sibyte/sb1250_scd.h +++ b/include/asm-mips/sibyte/sb1250_scd.h @@ -10,7 +10,7 @@ * ********************************************************************* * - * Copyright 2000,2001,2002,2003 + * Copyright 2000,2001,2002,2003,2004,2005 * Broadcom Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or @@ -150,7 +150,7 @@ #define K_SYS_SOC_TYPE_BCM1x55 0x7 * (For the assembler version, sysrev and dest may be the same register. * Also, it clobbers AT.) */ -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define SYS_SOC_TYPE(dest, sysrev) \ .set push ; \ .set reorder ; \ @@ -214,6 +214,7 @@ #define V_SYS_YPOS(x) _SB_MA #define G_SYS_YPOS(x) _SB_GETVALUE(x,S_SYS_YPOS,M_SYS_YPOS) #endif + /* * System Config Register (Table 4-2) * Register: SCD_SYSTEM_CFG @@ -360,13 +361,13 @@ #endif */ #define V_SCD_TIMER_FREQ 1000000 -#define V_SCD_TIMER_WIDTH 23 #define S_SCD_TIMER_INIT 0 -#define M_SCD_TIMER_INIT _SB_MAKEMASK(V_SCD_TIMER_WIDTH,S_SCD_TIMER_INIT) +#define M_SCD_TIMER_INIT _SB_MAKEMASK(23,S_SCD_TIMER_INIT) #define V_SCD_TIMER_INIT(x) _SB_MAKEVALUE(x,S_SCD_TIMER_INIT) #define G_SCD_TIMER_INIT(x) _SB_GETVALUE(x,S_SCD_TIMER_INIT,M_SCD_TIMER_INIT) +#define V_SCD_TIMER_WIDTH 23 #define S_SCD_TIMER_CNT 0 #define M_SCD_TIMER_CNT _SB_MAKEMASK(V_SCD_TIMER_WIDTH,S_SCD_TIMER_CNT) #define V_SCD_TIMER_CNT(x) _SB_MAKEVALUE(x,S_SCD_TIMER_CNT) @@ -380,7 +381,6 @@ #define M_SCD_TIMER_MODE_CONTINUOUS M_SC * System Performance Counters */ -#if SIBYTE_HDR_FEATURE_1250_112x #define S_SPC_CFG_SRC0 0 #define M_SPC_CFG_SRC0 _SB_MAKEMASK(8,S_SPC_CFG_SRC0) #define V_SPC_CFG_SRC0(x) _SB_MAKEVALUE(x,S_SPC_CFG_SRC0) @@ -401,6 +401,7 @@ #define M_SPC_CFG_SRC3 _SB_MA #define V_SPC_CFG_SRC3(x) _SB_MAKEVALUE(x,S_SPC_CFG_SRC3) #define G_SPC_CFG_SRC3(x) _SB_GETVALUE(x,S_SPC_CFG_SRC3,M_SPC_CFG_SRC3) +#if SIBYTE_HDR_FEATURE_1250_112x #define M_SPC_CFG_CLEAR _SB_MAKEMASK1(32) #define M_SPC_CFG_ENABLE _SB_MAKEMASK1(33) #endif @@ -516,8 +517,6 @@ #endif /* 1250/112x */ * Trace Buffer Config register */ -#if SIBYTE_HDR_FEATURE_1250_112x - #define M_SCD_TRACE_CFG_RESET _SB_MAKEMASK1(0) #define M_SCD_TRACE_CFG_START_READ _SB_MAKEMASK1(1) #define M_SCD_TRACE_CFG_START _SB_MAKEMASK1(2) @@ -526,17 +525,26 @@ #define M_SCD_TRACE_CFG_FREEZE #define M_SCD_TRACE_CFG_FREEZE_FULL _SB_MAKEMASK1(5) #define M_SCD_TRACE_CFG_DEBUG_FULL _SB_MAKEMASK1(6) #define M_SCD_TRACE_CFG_FULL _SB_MAKEMASK1(7) -#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) +#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480) #define M_SCD_TRACE_CFG_FORCECNT _SB_MAKEMASK1(8) -#endif /* 1250 PASS2 || 112x PASS1 */ +#endif /* 1250 PASS2 || 112x PASS1 || 1480 */ +/* + * This field is the same on the 1250/112x and 1480, just located in + * a slightly different place in the register. + */ +#if SIBYTE_HDR_FEATURE_1250_112x #define S_SCD_TRACE_CFG_CUR_ADDR 10 +#else +#if SIBYTE_HDR_FEATURE_CHIP(1480) +#define S_SCD_TRACE_CFG_CUR_ADDR 24 +#endif /* 1480 */ +#endif /* 1250/112x */ + #define M_SCD_TRACE_CFG_CUR_ADDR _SB_MAKEMASK(8,S_SCD_TRACE_CFG_CUR_ADDR) #define V_SCD_TRACE_CFG_CUR_ADDR(x) _SB_MAKEVALUE(x,S_SCD_TRACE_CFG_CUR_ADDR) #define G_SCD_TRACE_CFG_CUR_ADDR(x) _SB_GETVALUE(x,S_SCD_TRACE_CFG_CUR_ADDR,M_SCD_TRACE_CFG_CUR_ADDR) -#endif /* 1250/112x */ - /* * Trace Event registers */ diff --git a/include/asm-mips/sibyte/swarm.h b/include/asm-mips/sibyte/swarm.h index 86db37e..540865f 100644 --- a/include/asm-mips/sibyte/swarm.h +++ b/include/asm-mips/sibyte/swarm.h @@ -32,6 +32,18 @@ #define SIBYTE_HAVE_PCMCIA 1 #define SIBYTE_HAVE_IDE 1 #define SIBYTE_DEFAULT_CONSOLE "ttyS0,115200" #endif +#ifdef CONFIG_SIBYTE_PT1120 +#define SIBYTE_BOARD_NAME "PT1120" +#define SIBYTE_HAVE_PCMCIA 1 +#define SIBYTE_HAVE_IDE 1 +#define SIBYTE_DEFAULT_CONSOLE "ttyS0,115200" +#endif +#ifdef CONFIG_SIBYTE_PT1125 +#define SIBYTE_BOARD_NAME "PT1125" +#define SIBYTE_HAVE_PCMCIA 1 +#define SIBYTE_HAVE_IDE 1 +#define SIBYTE_DEFAULT_CONSOLE "ttyS0,115200" +#endif #ifdef CONFIG_SIBYTE_LITTLESUR #define SIBYTE_BOARD_NAME "BCM91250C2 (LittleSur)" #define SIBYTE_HAVE_PCMCIA 0 diff --git a/include/asm-mips/socket.h b/include/asm-mips/socket.h index 36ebe4e..9594568 100644 --- a/include/asm-mips/socket.h +++ b/include/asm-mips/socket.h @@ -70,6 +70,8 @@ #define SO_PEERSEC 30 #define SO_SNDBUFFORCE 31 #define SO_RCVBUFFORCE 33 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #ifdef __KERNEL__ diff --git a/include/asm-mips/sockios.h b/include/asm-mips/sockios.h index 87a50bf..ed1a5f7 100644 --- a/include/asm-mips/sockios.h +++ b/include/asm-mips/sockios.h @@ -20,6 +20,7 @@ #define SIOCATMARK _IOR('s', 7, int) #define SIOCSPGRP _IOW('s', 8, pid_t) #define SIOCGPGRP _IOR('s', 9, pid_t) -#define SIOCGSTAMP 0x8906 /* Get stamp - linux-specific */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* _ASM_SOCKIOS_H */ diff --git a/include/asm-parisc/socket.h b/include/asm-parisc/socket.h index ce2eae1..99e868f 100644 --- a/include/asm-parisc/socket.h +++ b/include/asm-parisc/socket.h @@ -33,6 +33,8 @@ #define SO_PASSCRED 0x4010 #define SO_PEERCRED 0x4011 #define SO_TIMESTAMP 0x4012 #define SCM_TIMESTAMP SO_TIMESTAMP +#define SO_TIMESTAMPNS 0x4013 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x4016 diff --git a/include/asm-parisc/sockios.h b/include/asm-parisc/sockios.h index aace496..dabfbc7 100644 --- a/include/asm-parisc/sockios.h +++ b/include/asm-parisc/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif diff --git a/include/asm-powerpc/asm-compat.h b/include/asm-powerpc/asm-compat.h index c89bd58..c19e736 100644 --- a/include/asm-powerpc/asm-compat.h +++ b/include/asm-powerpc/asm-compat.h @@ -78,6 +78,15 @@ #define PPC_LLARX stringify_in_c(ldarx) #define PPC_STLCX stringify_in_c(stdcx.) #define PPC_CNTLZL stringify_in_c(cntlzd) +/* Move to CR, single-entry optimized version. Only available + * on POWER4 and later. + */ +#ifdef CONFIG_POWER4_ONLY +#define PPC_MTOCRF stringify_in_c(mtocrf) +#else +#define PPC_MTOCRF stringify_in_c(mtcrf) +#endif + #else /* 32-bit */ /* operations for longs and pointers */ @@ -89,6 +98,7 @@ #define PPC_TLNEI stringify_in_c(twnei) #define PPC_LLARX stringify_in_c(lwarx) #define PPC_STLCX stringify_in_c(stwcx.) #define PPC_CNTLZL stringify_in_c(cntlzw) +#define PPC_MTOCRF stringify_in_c(mtcrf) #endif diff --git a/include/asm-powerpc/cacheflush.h b/include/asm-powerpc/cacheflush.h index 08e93e7..ba667a3 100644 --- a/include/asm-powerpc/cacheflush.h +++ b/include/asm-powerpc/cacheflush.h @@ -64,6 +64,12 @@ #define copy_from_user_page(vma, page, v memcpy(dst, src, len) + +#ifdef CONFIG_DEBUG_PAGEALLOC +/* internal debugging function */ +void kernel_map_pages(struct page *page, int numpages, int enable); +#endif + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_CACHEFLUSH_H */ diff --git a/include/asm-powerpc/cell-pmu.h b/include/asm-powerpc/cell-pmu.h index 35b9577..8066eed 100644 --- a/include/asm-powerpc/cell-pmu.h +++ b/include/asm-powerpc/cell-pmu.h @@ -97,11 +97,6 @@ extern void cbe_disable_pm_interrupts(u3 extern u32 cbe_get_and_clear_pm_interrupts(u32 cpu); extern void cbe_sync_irq(int node); -/* Utility functions, macros */ -extern u32 cbe_get_hw_thread_id(int cpu); - -#define cbe_cpu_to_node(cpu) ((cpu) >> 1) - #define CBE_COUNT_SUPERVISOR_MODE 0 #define CBE_COUNT_HYPERVISOR_MODE 1 #define CBE_COUNT_PROBLEM_MODE 2 diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index e870b53..4345249 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -48,6 +48,7 @@ enum powerpc_oprofile_type { PPC_OPROFILE_G4 = 3, PPC_OPROFILE_BOOKE = 4, PPC_OPROFILE_CELL = 5, + PPC_OPROFILE_PA6T = 6, }; enum powerpc_pmc_type { @@ -223,6 +224,10 @@ #define CPU_FTRS_750 (CPU_FTR_COMMON | C CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ CPU_FTR_PPC_LE) +#define CPU_FTRS_750CL (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ + CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) #define CPU_FTRS_750FX1 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ @@ -235,9 +240,9 @@ #define CPU_FTRS_750FX (CPU_FTR_COMMON | CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) -#define CPU_FTRS_750GX (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ - CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | \ - CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ +#define CPU_FTRS_750GX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) #define CPU_FTRS_7400_NOTAU (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ diff --git a/include/asm-powerpc/current.h b/include/asm-powerpc/current.h index b8708ae..e2c7f06 100644 --- a/include/asm-powerpc/current.h +++ b/include/asm-powerpc/current.h @@ -12,6 +12,7 @@ #ifdef __KERNEL__ struct task_struct; #ifdef __powerpc64__ +#include #include static inline struct task_struct *get_current(void) diff --git a/include/asm-powerpc/edac.h b/include/asm-powerpc/edac.h new file mode 100644 index 0000000..6ead88b --- /dev/null +++ b/include/asm-powerpc/edac.h @@ -0,0 +1,40 @@ +/* + * PPC EDAC common defs + * + * Author: Dave Jiang + * + * 2007 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef ASM_EDAC_H +#define ASM_EDAC_H +/* + * ECC atomic, DMA, SMP and interrupt safe scrub function. + * Implements the per arch atomic_scrub() that EDAC use for software + * ECC scrubbing. It reads memory and then writes back the original + * value, allowing the hardware to detect and correct memory errors. + */ +static __inline__ void atomic_scrub(void *va, u32 size) +{ + unsigned int *virt_addr = va; + unsigned int temp; + unsigned int i; + + for (i = 0; i < size / sizeof(*virt_addr); i++, virt_addr++) { + /* Very carefully read and write to memory atomically + * so we are interrupt, DMA and SMP safe. + */ + __asm__ __volatile__ ("\n\ + 1: lwarx %0,0,%1\n\ + stwcx. %0,0,%1\n\ + bne- 1b\n\ + isync" + : "=&r"(temp) + : "r"(virt_addr) + : "cr0", "memory"); + } +} + +#endif diff --git a/include/asm-powerpc/eeh_event.h b/include/asm-powerpc/eeh_event.h index dc6bf0f..cc3cb04 100644 --- a/include/asm-powerpc/eeh_event.h +++ b/include/asm-powerpc/eeh_event.h @@ -30,8 +30,6 @@ struct eeh_event { struct list_head list; struct device_node *dn; /* struct device node */ struct pci_dev *dev; /* affected device */ - enum pci_channel_state state; /* PCI bus state for the affected device */ - int time_unavail; /* milliseconds until device might be available */ }; /** @@ -46,9 +44,7 @@ struct eeh_event { * (from a workqueue). */ int eeh_send_failure_event (struct device_node *dn, - struct pci_dev *dev, - enum pci_channel_state state, - int time_unavail); + struct pci_dev *dev); /* Main recovery function */ struct pci_dn * handle_eeh_events (struct eeh_event *); diff --git a/include/asm-powerpc/ibmebus.h b/include/asm-powerpc/ibmebus.h index 6611211..87d396e 100644 --- a/include/asm-powerpc/ibmebus.h +++ b/include/asm-powerpc/ibmebus.h @@ -2,36 +2,37 @@ * IBM PowerPC eBus Infrastructure Support. * * Copyright (c) 2005 IBM Corporation + * Joachim Fenkes * Heiko J Schick - * + * * All rights reserved. * - * This source code is distributed under a dual license of GPL v2.0 and OpenIB - * BSD. + * This source code is distributed under a dual license of GPL v2.0 and OpenIB + * BSD. * * OpenIB BSD License * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation * and/or other materials - * provided with the distribution. + * provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ @@ -46,12 +47,11 @@ #include extern struct bus_type ibmebus_bus_type; -struct ibmebus_dev { - const char *name; +struct ibmebus_dev { struct of_device ofdev; }; -struct ibmebus_driver { +struct ibmebus_driver { char *name; struct of_device_id *id_table; int (*probe) (struct ibmebus_dev *dev, const struct of_device_id *id); @@ -63,7 +63,7 @@ int ibmebus_register_driver(struct ibmeb void ibmebus_unregister_driver(struct ibmebus_driver *drv); int ibmebus_request_irq(struct ibmebus_dev *dev, - u32 ist, + u32 ist, irq_handler_t handler, unsigned long irq_flags, const char * devname, void *dev_id); diff --git a/include/asm-powerpc/immap_86xx.h b/include/asm-powerpc/immap_86xx.h index d905b66..59b9e07 100644 --- a/include/asm-powerpc/immap_86xx.h +++ b/include/asm-powerpc/immap_86xx.h @@ -85,81 +85,6 @@ typedef struct ccsr_pci { char res19[472]; } ccsr_pci_t; -/* PCI Express Registers */ -typedef struct ccsr_pex { - uint pex_config_addr; /* 0x.000 - PCI Express Configuration Address Register */ - uint pex_config_data; /* 0x.004 - PCI Express Configuration Data Register */ - char res1[4]; - uint pex_otb_cpl_tor; /* 0x.00c - PCI Express Outbound completion timeout register */ - uint pex_conf_tor; /* 0x.010 - PCI Express configuration timeout register */ - char res2[12]; - uint pex_pme_mes_dr; /* 0x.020 - PCI Express PME and message detect register */ - uint pex_pme_mes_disr; /* 0x.024 - PCI Express PME and message disable register */ - uint pex_pme_mes_ier; /* 0x.028 - PCI Express PME and message interrupt enable register */ - uint pex_pmcr; /* 0x.02c - PCI Express power management command register */ - char res3[3024]; - uint pexotar0; /* 0x.c00 - PCI Express outbound translation address register 0 */ - uint pexotear0; /* 0x.c04 - PCI Express outbound translation extended address register 0*/ - char res4[8]; - uint pexowar0; /* 0x.c10 - PCI Express outbound window attributes register 0*/ - char res5[12]; - uint pexotar1; /* 0x.c20 - PCI Express outbound translation address register 1 */ - uint pexotear1; /* 0x.c24 - PCI Express outbound translation extended address register 1*/ - uint pexowbar1; /* 0x.c28 - PCI Express outbound window base address register 1*/ - char res6[4]; - uint pexowar1; /* 0x.c30 - PCI Express outbound window attributes register 1*/ - char res7[12]; - uint pexotar2; /* 0x.c40 - PCI Express outbound translation address register 2 */ - uint pexotear2; /* 0x.c44 - PCI Express outbound translation extended address register 2*/ - uint pexowbar2; /* 0x.c48 - PCI Express outbound window base address register 2*/ - char res8[4]; - uint pexowar2; /* 0x.c50 - PCI Express outbound window attributes register 2*/ - char res9[12]; - uint pexotar3; /* 0x.c60 - PCI Express outbound translation address register 3 */ - uint pexotear3; /* 0x.c64 - PCI Express outbound translation extended address register 3*/ - uint pexowbar3; /* 0x.c68 - PCI Express outbound window base address register 3*/ - char res10[4]; - uint pexowar3; /* 0x.c70 - PCI Express outbound window attributes register 3*/ - char res11[12]; - uint pexotar4; /* 0x.c80 - PCI Express outbound translation address register 4 */ - uint pexotear4; /* 0x.c84 - PCI Express outbound translation extended address register 4*/ - uint pexowbar4; /* 0x.c88 - PCI Express outbound window base address register 4*/ - char res12[4]; - uint pexowar4; /* 0x.c90 - PCI Express outbound window attributes register 4*/ - char res13[12]; - char res14[256]; - uint pexitar3; /* 0x.da0 - PCI Express inbound translation address register 3 */ - char res15[4]; - uint pexiwbar3; /* 0x.da8 - PCI Express inbound window base address register 3 */ - uint pexiwbear3; /* 0x.dac - PCI Express inbound window base extended address register 3 */ - uint pexiwar3; /* 0x.db0 - PCI Express inbound window attributes register 3 */ - char res16[12]; - uint pexitar2; /* 0x.dc0 - PCI Express inbound translation address register 2 */ - char res17[4]; - uint pexiwbar2; /* 0x.dc8 - PCI Express inbound window base address register 2 */ - uint pexiwbear2; /* 0x.dcc - PCI Express inbound window base extended address register 2 */ - uint pexiwar2; /* 0x.dd0 - PCI Express inbound window attributes register 2 */ - char res18[12]; - uint pexitar1; /* 0x.de0 - PCI Express inbound translation address register 2 */ - char res19[4]; - uint pexiwbar1; /* 0x.de8 - PCI Express inbound window base address register 2 */ - uint pexiwbear1; /* 0x.dec - PCI Express inbound window base extended address register 2 */ - uint pexiwar1; /* 0x.df0 - PCI Express inbound window attributes register 2 */ - char res20[12]; - uint pex_err_dr; /* 0x.e00 - PCI Express error detect register */ - char res21[4]; - uint pex_err_en; /* 0x.e08 - PCI Express error interrupt enable register */ - char res22[4]; - uint pex_err_disr; /* 0x.e10 - PCI Express error disable register */ - char res23[12]; - uint pex_err_cap_stat; /* 0x.e20 - PCI Express error capture status register */ - char res24[4]; - uint pex_err_cap_r0; /* 0x.e28 - PCI Express error capture register 0 */ - uint pex_err_cap_r1; /* 0x.e2c - PCI Express error capture register 0 */ - uint pex_err_cap_r2; /* 0x.e30 - PCI Express error capture register 0 */ - uint pex_err_cap_r3; /* 0x.e34 - PCI Express error capture register 0 */ -} ccsr_pex_t; - /* Global Utility Registers */ typedef struct ccsr_guts { uint porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ diff --git a/include/asm-powerpc/io.h b/include/asm-powerpc/io.h index 301c9bb..350c9bd 100644 --- a/include/asm-powerpc/io.h +++ b/include/asm-powerpc/io.h @@ -11,7 +11,12 @@ #ifdef __KERNEL__ /* Check of existence of legacy devices */ extern int check_legacy_ioport(unsigned long base_port); -#define PNPBIOS_BASE 0xf000 /* only relevant for PReP */ +#define I8042_DATA_REG 0x60 +#define FDC_BASE 0x3f0 +/* only relevant for PReP */ +#define _PIDXR 0x279 +#define _PNPWRP 0xa79 +#define PNPBIOS_BASE 0xf000 #include #include diff --git a/include/asm-powerpc/kprobes.h b/include/asm-powerpc/kprobes.h index 3a5dd49..f850ca7 100644 --- a/include/asm-powerpc/kprobes.h +++ b/include/asm-powerpc/kprobes.h @@ -87,6 +87,11 @@ extern void arch_remove_kprobe(struct kp struct arch_specific_insn { /* copy of original instruction */ kprobe_opcode_t *insn; + /* + * Set in kprobes code, initially to 0. If the instruction can be + * eumulated, this is set to 1, if not, to -1. + */ + int boostable; }; struct prev_kprobe { diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index 1b04e57..b204926 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -153,9 +153,6 @@ #endif */ long (*feature_call)(unsigned int feature, ...); - /* Check availability of legacy devices like i8042 */ - int (*check_legacy_ioport)(unsigned int baseport); - /* Get legacy PCI/IDE interrupt mapping */ int (*pci_get_legacy_ide_irq)(struct pci_dev *dev, int channel); diff --git a/include/asm-powerpc/mmu-hash64.h b/include/asm-powerpc/mmu-hash64.h new file mode 100644 index 0000000..6739457 --- /dev/null +++ b/include/asm-powerpc/mmu-hash64.h @@ -0,0 +1,400 @@ +#ifndef _ASM_POWERPC_MMU_HASH64_H_ +#define _ASM_POWERPC_MMU_HASH64_H_ +/* + * PowerPC64 memory management structures + * + * Dave Engebretsen & Mike Corrigan <{engebret|mikejc}@us.ibm.com> + * PPC64 rework. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +/* + * Segment table + */ + +#define STE_ESID_V 0x80 +#define STE_ESID_KS 0x20 +#define STE_ESID_KP 0x10 +#define STE_ESID_N 0x08 + +#define STE_VSID_SHIFT 12 + +/* Location of cpu0's segment table */ +#define STAB0_PAGE 0x6 +#define STAB0_OFFSET (STAB0_PAGE << 12) +#define STAB0_PHYS_ADDR (STAB0_OFFSET + PHYSICAL_START) + +#ifndef __ASSEMBLY__ +extern char initial_stab[]; +#endif /* ! __ASSEMBLY */ + +/* + * SLB + */ + +#define SLB_NUM_BOLTED 3 +#define SLB_CACHE_ENTRIES 8 + +/* Bits in the SLB ESID word */ +#define SLB_ESID_V ASM_CONST(0x0000000008000000) /* valid */ + +/* Bits in the SLB VSID word */ +#define SLB_VSID_SHIFT 12 +#define SLB_VSID_B ASM_CONST(0xc000000000000000) +#define SLB_VSID_B_256M ASM_CONST(0x0000000000000000) +#define SLB_VSID_B_1T ASM_CONST(0x4000000000000000) +#define SLB_VSID_KS ASM_CONST(0x0000000000000800) +#define SLB_VSID_KP ASM_CONST(0x0000000000000400) +#define SLB_VSID_N ASM_CONST(0x0000000000000200) /* no-execute */ +#define SLB_VSID_L ASM_CONST(0x0000000000000100) +#define SLB_VSID_C ASM_CONST(0x0000000000000080) /* class */ +#define SLB_VSID_LP ASM_CONST(0x0000000000000030) +#define SLB_VSID_LP_00 ASM_CONST(0x0000000000000000) +#define SLB_VSID_LP_01 ASM_CONST(0x0000000000000010) +#define SLB_VSID_LP_10 ASM_CONST(0x0000000000000020) +#define SLB_VSID_LP_11 ASM_CONST(0x0000000000000030) +#define SLB_VSID_LLP (SLB_VSID_L|SLB_VSID_LP) + +#define SLB_VSID_KERNEL (SLB_VSID_KP) +#define SLB_VSID_USER (SLB_VSID_KP|SLB_VSID_KS|SLB_VSID_C) + +#define SLBIE_C (0x08000000) + +/* + * Hash table + */ + +#define HPTES_PER_GROUP 8 + +#define HPTE_V_AVPN_SHIFT 7 +#define HPTE_V_AVPN ASM_CONST(0xffffffffffffff80) +#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT) +#define HPTE_V_COMPARE(x,y) (!(((x) ^ (y)) & HPTE_V_AVPN)) +#define HPTE_V_BOLTED ASM_CONST(0x0000000000000010) +#define HPTE_V_LOCK ASM_CONST(0x0000000000000008) +#define HPTE_V_LARGE ASM_CONST(0x0000000000000004) +#define HPTE_V_SECONDARY ASM_CONST(0x0000000000000002) +#define HPTE_V_VALID ASM_CONST(0x0000000000000001) + +#define HPTE_R_PP0 ASM_CONST(0x8000000000000000) +#define HPTE_R_TS ASM_CONST(0x4000000000000000) +#define HPTE_R_RPN_SHIFT 12 +#define HPTE_R_RPN ASM_CONST(0x3ffffffffffff000) +#define HPTE_R_FLAGS ASM_CONST(0x00000000000003ff) +#define HPTE_R_PP ASM_CONST(0x0000000000000003) +#define HPTE_R_N ASM_CONST(0x0000000000000004) +#define HPTE_R_C ASM_CONST(0x0000000000000080) +#define HPTE_R_R ASM_CONST(0x0000000000000100) + +/* Values for PP (assumes Ks=0, Kp=1) */ +/* pp0 will always be 0 for linux */ +#define PP_RWXX 0 /* Supervisor read/write, User none */ +#define PP_RWRX 1 /* Supervisor read/write, User read */ +#define PP_RWRW 2 /* Supervisor read/write, User read/write */ +#define PP_RXRX 3 /* Supervisor read, User read */ + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned long v; + unsigned long r; +} hpte_t; + +extern hpte_t *htab_address; +extern unsigned long htab_size_bytes; +extern unsigned long htab_hash_mask; + +/* + * Page size definition + * + * shift : is the "PAGE_SHIFT" value for that page size + * sllp : is a bit mask with the value of SLB L || LP to be or'ed + * directly to a slbmte "vsid" value + * penc : is the HPTE encoding mask for the "LP" field: + * + */ +struct mmu_psize_def +{ + unsigned int shift; /* number of bits */ + unsigned int penc; /* HPTE encoding */ + unsigned int tlbiel; /* tlbiel supported for that page size */ + unsigned long avpnm; /* bits to mask out in AVPN in the HPTE */ + unsigned long sllp; /* SLB L||LP (exact mask to use in slbmte) */ +}; + +#endif /* __ASSEMBLY__ */ + +/* + * The kernel use the constants below to index in the page sizes array. + * The use of fixed constants for this purpose is better for performances + * of the low level hash refill handlers. + * + * A non supported page size has a "shift" field set to 0 + * + * Any new page size being implemented can get a new entry in here. Whether + * the kernel will use it or not is a different matter though. The actual page + * size used by hugetlbfs is not defined here and may be made variable + */ + +#define MMU_PAGE_4K 0 /* 4K */ +#define MMU_PAGE_64K 1 /* 64K */ +#define MMU_PAGE_64K_AP 2 /* 64K Admixed (in a 4K segment) */ +#define MMU_PAGE_1M 3 /* 1M */ +#define MMU_PAGE_16M 4 /* 16M */ +#define MMU_PAGE_16G 5 /* 16G */ +#define MMU_PAGE_COUNT 6 + +#ifndef __ASSEMBLY__ + +/* + * The current system page sizes + */ +extern struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT]; +extern int mmu_linear_psize; +extern int mmu_virtual_psize; +extern int mmu_vmalloc_psize; +extern int mmu_io_psize; + +/* + * If the processor supports 64k normal pages but not 64k cache + * inhibited pages, we have to be prepared to switch processes + * to use 4k pages when they create cache-inhibited mappings. + * If this is the case, mmu_ci_restrictions will be set to 1. + */ +extern int mmu_ci_restrictions; + +#ifdef CONFIG_HUGETLB_PAGE +/* + * The page size index of the huge pages for use by hugetlbfs + */ +extern int mmu_huge_psize; + +#endif /* CONFIG_HUGETLB_PAGE */ + +/* + * This function sets the AVPN and L fields of the HPTE appropriately + * for the page size + */ +static inline unsigned long hpte_encode_v(unsigned long va, int psize) +{ + unsigned long v = + v = (va >> 23) & ~(mmu_psize_defs[psize].avpnm); + v <<= HPTE_V_AVPN_SHIFT; + if (psize != MMU_PAGE_4K) + v |= HPTE_V_LARGE; + return v; +} + +/* + * This function sets the ARPN, and LP fields of the HPTE appropriately + * for the page size. We assume the pa is already "clean" that is properly + * aligned for the requested page size + */ +static inline unsigned long hpte_encode_r(unsigned long pa, int psize) +{ + unsigned long r; + + /* A 4K page needs no special encoding */ + if (psize == MMU_PAGE_4K) + return pa & HPTE_R_RPN; + else { + unsigned int penc = mmu_psize_defs[psize].penc; + unsigned int shift = mmu_psize_defs[psize].shift; + return (pa & ~((1ul << shift) - 1)) | (penc << 12); + } + return r; +} + +/* + * This hashes a virtual address for a 256Mb segment only for now + */ + +static inline unsigned long hpt_hash(unsigned long va, unsigned int shift) +{ + return ((va >> 28) & 0x7fffffffffUL) ^ ((va & 0x0fffffffUL) >> shift); +} + +extern int __hash_page_4K(unsigned long ea, unsigned long access, + unsigned long vsid, pte_t *ptep, unsigned long trap, + unsigned int local); +extern int __hash_page_64K(unsigned long ea, unsigned long access, + unsigned long vsid, pte_t *ptep, unsigned long trap, + unsigned int local); +struct mm_struct; +extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); +extern int hash_huge_page(struct mm_struct *mm, unsigned long access, + unsigned long ea, unsigned long vsid, int local, + unsigned long trap); + +extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend, + unsigned long pstart, unsigned long mode, + int psize); + +extern void htab_initialize(void); +extern void htab_initialize_secondary(void); +extern void hpte_init_native(void); +extern void hpte_init_lpar(void); +extern void hpte_init_iSeries(void); +extern void hpte_init_beat(void); + +extern void stabs_alloc(void); +extern void slb_initialize(void); +extern void slb_flush_and_rebolt(void); +extern void stab_initialize(unsigned long stab); + +#endif /* __ASSEMBLY__ */ + +/* + * VSID allocation + * + * We first generate a 36-bit "proto-VSID". For kernel addresses this + * is equal to the ESID, for user addresses it is: + * (context << 15) | (esid & 0x7fff) + * + * The two forms are distinguishable because the top bit is 0 for user + * addresses, whereas the top two bits are 1 for kernel addresses. + * Proto-VSIDs with the top two bits equal to 0b10 are reserved for + * now. + * + * The proto-VSIDs are then scrambled into real VSIDs with the + * multiplicative hash: + * + * VSID = (proto-VSID * VSID_MULTIPLIER) % VSID_MODULUS + * where VSID_MULTIPLIER = 268435399 = 0xFFFFFC7 + * VSID_MODULUS = 2^36-1 = 0xFFFFFFFFF + * + * This scramble is only well defined for proto-VSIDs below + * 0xFFFFFFFFF, so both proto-VSID and actual VSID 0xFFFFFFFFF are + * reserved. VSID_MULTIPLIER is prime, so in particular it is + * co-prime to VSID_MODULUS, making this a 1:1 scrambling function. + * Because the modulus is 2^n-1 we can compute it efficiently without + * a divide or extra multiply (see below). + * + * This scheme has several advantages over older methods: + * + * - We have VSIDs allocated for every kernel address + * (i.e. everything above 0xC000000000000000), except the very top + * segment, which simplifies several things. + * + * - We allow for 15 significant bits of ESID and 20 bits of + * context for user addresses. i.e. 8T (43 bits) of address space for + * up to 1M contexts (although the page table structure and context + * allocation will need changes to take advantage of this). + * + * - The scramble function gives robust scattering in the hash + * table (at least based on some initial results). The previous + * method was more susceptible to pathological cases giving excessive + * hash collisions. + */ +/* + * WARNING - If you change these you must make sure the asm + * implementations in slb_allocate (slb_low.S), do_stab_bolted + * (head.S) and ASM_VSID_SCRAMBLE (below) are changed accordingly. + * + * You'll also need to change the precomputed VSID values in head.S + * which are used by the iSeries firmware. + */ + +#define VSID_MULTIPLIER ASM_CONST(200730139) /* 28-bit prime */ +#define VSID_BITS 36 +#define VSID_MODULUS ((1UL<= \ + * 2^36-1, then r3+1 has the 2^36 bit set. So, if r3+1 has \ + * the bit clear, r3 already has the answer we want, if it \ + * doesn't, the answer is the low 36 bits of r3+1. So in all \ + * cases the answer is the low 36 bits of (r3 + ((r3+1) >> 36))*/\ + addi rx,rt,1; \ + srdi rx,rx,VSID_BITS; /* extract 2^36 bit */ \ + add rt,rt,rx + + +#ifndef __ASSEMBLY__ + +typedef unsigned long mm_context_id_t; + +typedef struct { + mm_context_id_t id; + u16 user_psize; /* page size index */ + u16 sllp; /* SLB entry page size encoding */ +#ifdef CONFIG_HUGETLB_PAGE + u16 low_htlb_areas, high_htlb_areas; +#endif + unsigned long vdso_base; +} mm_context_t; + + +static inline unsigned long vsid_scramble(unsigned long protovsid) +{ +#if 0 + /* The code below is equivalent to this function for arguments + * < 2^VSID_BITS, which is all this should ever be called + * with. However gcc is not clever enough to compute the + * modulus (2^n-1) without a second multiply. */ + return ((protovsid * VSID_MULTIPLIER) % VSID_MODULUS); +#else /* 1 */ + unsigned long x; + + x = protovsid * VSID_MULTIPLIER; + x = (x >> VSID_BITS) + (x & VSID_MODULUS); + return (x + ((x+1) >> VSID_BITS)) & VSID_MODULUS; +#endif /* 1 */ +} + +/* This is only valid for addresses >= KERNELBASE */ +static inline unsigned long get_kernel_vsid(unsigned long ea) +{ + return vsid_scramble(ea >> SID_SHIFT); +} + +/* This is only valid for user addresses (which are below 2^41) */ +static inline unsigned long get_vsid(unsigned long context, unsigned long ea) +{ + return vsid_scramble((context << USER_ESID_BITS) + | (ea >> SID_SHIFT)); +} + +#define VSID_SCRAMBLE(pvsid) (((pvsid) * VSID_MULTIPLIER) % VSID_MODULUS) +#define KERNEL_VSID(ea) VSID_SCRAMBLE(GET_ESID(ea)) + +/* Physical address used by some IO functions */ +typedef unsigned long phys_addr_t; + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_POWERPC_MMU_HASH64_H_ */ diff --git a/include/asm-powerpc/mmu.h b/include/asm-powerpc/mmu.h index 200055a..06b3e6d 100644 --- a/include/asm-powerpc/mmu.h +++ b/include/asm-powerpc/mmu.h @@ -2,407 +2,14 @@ #ifndef _ASM_POWERPC_MMU_H_ #define _ASM_POWERPC_MMU_H_ #ifdef __KERNEL__ -#ifndef CONFIG_PPC64 -#include +#ifdef CONFIG_PPC64 +/* 64-bit classic hash table MMU */ +# include #else - -/* - * PowerPC memory management structures - * - * Dave Engebretsen & Mike Corrigan <{engebret|mikejc}@us.ibm.com> - * PPC64 rework. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include - -/* - * Segment table - */ - -#define STE_ESID_V 0x80 -#define STE_ESID_KS 0x20 -#define STE_ESID_KP 0x10 -#define STE_ESID_N 0x08 - -#define STE_VSID_SHIFT 12 - -/* Location of cpu0's segment table */ -#define STAB0_PAGE 0x6 -#define STAB0_OFFSET (STAB0_PAGE << 12) -#define STAB0_PHYS_ADDR (STAB0_OFFSET + PHYSICAL_START) - -#ifndef __ASSEMBLY__ -extern char initial_stab[]; -#endif /* ! __ASSEMBLY */ - -/* - * SLB - */ - -#define SLB_NUM_BOLTED 3 -#define SLB_CACHE_ENTRIES 8 - -/* Bits in the SLB ESID word */ -#define SLB_ESID_V ASM_CONST(0x0000000008000000) /* valid */ - -/* Bits in the SLB VSID word */ -#define SLB_VSID_SHIFT 12 -#define SLB_VSID_B ASM_CONST(0xc000000000000000) -#define SLB_VSID_B_256M ASM_CONST(0x0000000000000000) -#define SLB_VSID_B_1T ASM_CONST(0x4000000000000000) -#define SLB_VSID_KS ASM_CONST(0x0000000000000800) -#define SLB_VSID_KP ASM_CONST(0x0000000000000400) -#define SLB_VSID_N ASM_CONST(0x0000000000000200) /* no-execute */ -#define SLB_VSID_L ASM_CONST(0x0000000000000100) -#define SLB_VSID_C ASM_CONST(0x0000000000000080) /* class */ -#define SLB_VSID_LP ASM_CONST(0x0000000000000030) -#define SLB_VSID_LP_00 ASM_CONST(0x0000000000000000) -#define SLB_VSID_LP_01 ASM_CONST(0x0000000000000010) -#define SLB_VSID_LP_10 ASM_CONST(0x0000000000000020) -#define SLB_VSID_LP_11 ASM_CONST(0x0000000000000030) -#define SLB_VSID_LLP (SLB_VSID_L|SLB_VSID_LP) - -#define SLB_VSID_KERNEL (SLB_VSID_KP) -#define SLB_VSID_USER (SLB_VSID_KP|SLB_VSID_KS|SLB_VSID_C) - -#define SLBIE_C (0x08000000) - -/* - * Hash table - */ - -#define HPTES_PER_GROUP 8 - -#define HPTE_V_AVPN_SHIFT 7 -#define HPTE_V_AVPN ASM_CONST(0xffffffffffffff80) -#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT) -#define HPTE_V_COMPARE(x,y) (!(((x) ^ (y)) & HPTE_V_AVPN)) -#define HPTE_V_BOLTED ASM_CONST(0x0000000000000010) -#define HPTE_V_LOCK ASM_CONST(0x0000000000000008) -#define HPTE_V_LARGE ASM_CONST(0x0000000000000004) -#define HPTE_V_SECONDARY ASM_CONST(0x0000000000000002) -#define HPTE_V_VALID ASM_CONST(0x0000000000000001) - -#define HPTE_R_PP0 ASM_CONST(0x8000000000000000) -#define HPTE_R_TS ASM_CONST(0x4000000000000000) -#define HPTE_R_RPN_SHIFT 12 -#define HPTE_R_RPN ASM_CONST(0x3ffffffffffff000) -#define HPTE_R_FLAGS ASM_CONST(0x00000000000003ff) -#define HPTE_R_PP ASM_CONST(0x0000000000000003) -#define HPTE_R_N ASM_CONST(0x0000000000000004) -#define HPTE_R_C ASM_CONST(0x0000000000000080) -#define HPTE_R_R ASM_CONST(0x0000000000000100) - -/* Values for PP (assumes Ks=0, Kp=1) */ -/* pp0 will always be 0 for linux */ -#define PP_RWXX 0 /* Supervisor read/write, User none */ -#define PP_RWRX 1 /* Supervisor read/write, User read */ -#define PP_RWRW 2 /* Supervisor read/write, User read/write */ -#define PP_RXRX 3 /* Supervisor read, User read */ - -#ifndef __ASSEMBLY__ - -typedef struct { - unsigned long v; - unsigned long r; -} hpte_t; - -extern hpte_t *htab_address; -extern unsigned long htab_size_bytes; -extern unsigned long htab_hash_mask; - -/* - * Page size definition - * - * shift : is the "PAGE_SHIFT" value for that page size - * sllp : is a bit mask with the value of SLB L || LP to be or'ed - * directly to a slbmte "vsid" value - * penc : is the HPTE encoding mask for the "LP" field: - * - */ -struct mmu_psize_def -{ - unsigned int shift; /* number of bits */ - unsigned int penc; /* HPTE encoding */ - unsigned int tlbiel; /* tlbiel supported for that page size */ - unsigned long avpnm; /* bits to mask out in AVPN in the HPTE */ - unsigned long sllp; /* SLB L||LP (exact mask to use in slbmte) */ -}; - -#endif /* __ASSEMBLY__ */ - -/* - * The kernel use the constants below to index in the page sizes array. - * The use of fixed constants for this purpose is better for performances - * of the low level hash refill handlers. - * - * A non supported page size has a "shift" field set to 0 - * - * Any new page size being implemented can get a new entry in here. Whether - * the kernel will use it or not is a different matter though. The actual page - * size used by hugetlbfs is not defined here and may be made variable - */ - -#define MMU_PAGE_4K 0 /* 4K */ -#define MMU_PAGE_64K 1 /* 64K */ -#define MMU_PAGE_64K_AP 2 /* 64K Admixed (in a 4K segment) */ -#define MMU_PAGE_1M 3 /* 1M */ -#define MMU_PAGE_16M 4 /* 16M */ -#define MMU_PAGE_16G 5 /* 16G */ -#define MMU_PAGE_COUNT 6 - -#ifndef __ASSEMBLY__ - -/* - * The current system page sizes - */ -extern struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT]; -extern int mmu_linear_psize; -extern int mmu_virtual_psize; -extern int mmu_vmalloc_psize; -extern int mmu_io_psize; - -/* - * If the processor supports 64k normal pages but not 64k cache - * inhibited pages, we have to be prepared to switch processes - * to use 4k pages when they create cache-inhibited mappings. - * If this is the case, mmu_ci_restrictions will be set to 1. - */ -extern int mmu_ci_restrictions; - -#ifdef CONFIG_HUGETLB_PAGE -/* - * The page size index of the huge pages for use by hugetlbfs - */ -extern int mmu_huge_psize; - -#endif /* CONFIG_HUGETLB_PAGE */ - -/* - * This function sets the AVPN and L fields of the HPTE appropriately - * for the page size - */ -static inline unsigned long hpte_encode_v(unsigned long va, int psize) -{ - unsigned long v = - v = (va >> 23) & ~(mmu_psize_defs[psize].avpnm); - v <<= HPTE_V_AVPN_SHIFT; - if (psize != MMU_PAGE_4K) - v |= HPTE_V_LARGE; - return v; -} - -/* - * This function sets the ARPN, and LP fields of the HPTE appropriately - * for the page size. We assume the pa is already "clean" that is properly - * aligned for the requested page size - */ -static inline unsigned long hpte_encode_r(unsigned long pa, int psize) -{ - unsigned long r; - - /* A 4K page needs no special encoding */ - if (psize == MMU_PAGE_4K) - return pa & HPTE_R_RPN; - else { - unsigned int penc = mmu_psize_defs[psize].penc; - unsigned int shift = mmu_psize_defs[psize].shift; - return (pa & ~((1ul << shift) - 1)) | (penc << 12); - } - return r; -} - -/* - * This hashes a virtual address for a 256Mb segment only for now - */ - -static inline unsigned long hpt_hash(unsigned long va, unsigned int shift) -{ - return ((va >> 28) & 0x7fffffffffUL) ^ ((va & 0x0fffffffUL) >> shift); -} - -extern int __hash_page_4K(unsigned long ea, unsigned long access, - unsigned long vsid, pte_t *ptep, unsigned long trap, - unsigned int local); -extern int __hash_page_64K(unsigned long ea, unsigned long access, - unsigned long vsid, pte_t *ptep, unsigned long trap, - unsigned int local); -struct mm_struct; -extern int hash_huge_page(struct mm_struct *mm, unsigned long access, - unsigned long ea, unsigned long vsid, int local, - unsigned long trap); - -extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend, - unsigned long pstart, unsigned long mode, - int psize); - -extern void htab_initialize(void); -extern void htab_initialize_secondary(void); -extern void hpte_init_native(void); -extern void hpte_init_lpar(void); -extern void hpte_init_iSeries(void); -extern void hpte_init_beat(void); - -extern void stabs_alloc(void); -extern void slb_initialize(void); -extern void slb_flush_and_rebolt(void); -extern void stab_initialize(unsigned long stab); - -#endif /* __ASSEMBLY__ */ - -/* - * VSID allocation - * - * We first generate a 36-bit "proto-VSID". For kernel addresses this - * is equal to the ESID, for user addresses it is: - * (context << 15) | (esid & 0x7fff) - * - * The two forms are distinguishable because the top bit is 0 for user - * addresses, whereas the top two bits are 1 for kernel addresses. - * Proto-VSIDs with the top two bits equal to 0b10 are reserved for - * now. - * - * The proto-VSIDs are then scrambled into real VSIDs with the - * multiplicative hash: - * - * VSID = (proto-VSID * VSID_MULTIPLIER) % VSID_MODULUS - * where VSID_MULTIPLIER = 268435399 = 0xFFFFFC7 - * VSID_MODULUS = 2^36-1 = 0xFFFFFFFFF - * - * This scramble is only well defined for proto-VSIDs below - * 0xFFFFFFFFF, so both proto-VSID and actual VSID 0xFFFFFFFFF are - * reserved. VSID_MULTIPLIER is prime, so in particular it is - * co-prime to VSID_MODULUS, making this a 1:1 scrambling function. - * Because the modulus is 2^n-1 we can compute it efficiently without - * a divide or extra multiply (see below). - * - * This scheme has several advantages over older methods: - * - * - We have VSIDs allocated for every kernel address - * (i.e. everything above 0xC000000000000000), except the very top - * segment, which simplifies several things. - * - * - We allow for 15 significant bits of ESID and 20 bits of - * context for user addresses. i.e. 8T (43 bits) of address space for - * up to 1M contexts (although the page table structure and context - * allocation will need changes to take advantage of this). - * - * - The scramble function gives robust scattering in the hash - * table (at least based on some initial results). The previous - * method was more susceptible to pathological cases giving excessive - * hash collisions. - */ -/* - * WARNING - If you change these you must make sure the asm - * implementations in slb_allocate (slb_low.S), do_stab_bolted - * (head.S) and ASM_VSID_SCRAMBLE (below) are changed accordingly. - * - * You'll also need to change the precomputed VSID values in head.S - * which are used by the iSeries firmware. - */ - -#define VSID_MULTIPLIER ASM_CONST(200730139) /* 28-bit prime */ -#define VSID_BITS 36 -#define VSID_MODULUS ((1UL<= \ - * 2^36-1, then r3+1 has the 2^36 bit set. So, if r3+1 has \ - * the bit clear, r3 already has the answer we want, if it \ - * doesn't, the answer is the low 36 bits of r3+1. So in all \ - * cases the answer is the low 36 bits of (r3 + ((r3+1) >> 36))*/\ - addi rx,rt,1; \ - srdi rx,rx,VSID_BITS; /* extract 2^36 bit */ \ - add rt,rt,rx - - -#ifndef __ASSEMBLY__ - -typedef unsigned long mm_context_id_t; - -typedef struct { - mm_context_id_t id; - u16 user_psize; /* page size index */ - u16 sllp; /* SLB entry page size encoding */ -#ifdef CONFIG_HUGETLB_PAGE - u16 low_htlb_areas, high_htlb_areas; +/* 32-bit. FIXME: split up the 32-bit MMU types, and revise for + * arch/powerpc */ +# include #endif - unsigned long vdso_base; -} mm_context_t; - - -static inline unsigned long vsid_scramble(unsigned long protovsid) -{ -#if 0 - /* The code below is equivalent to this function for arguments - * < 2^VSID_BITS, which is all this should ever be called - * with. However gcc is not clever enough to compute the - * modulus (2^n-1) without a second multiply. */ - return ((protovsid * VSID_MULTIPLIER) % VSID_MODULUS); -#else /* 1 */ - unsigned long x; - - x = protovsid * VSID_MULTIPLIER; - x = (x >> VSID_BITS) + (x & VSID_MODULUS); - return (x + ((x+1) >> VSID_BITS)) & VSID_MODULUS; -#endif /* 1 */ -} - -/* This is only valid for addresses >= KERNELBASE */ -static inline unsigned long get_kernel_vsid(unsigned long ea) -{ - return vsid_scramble(ea >> SID_SHIFT); -} - -/* This is only valid for user addresses (which are below 2^41) */ -static inline unsigned long get_vsid(unsigned long context, unsigned long ea) -{ - return vsid_scramble((context << USER_ESID_BITS) - | (ea >> SID_SHIFT)); -} - -#define VSID_SCRAMBLE(pvsid) (((pvsid) * VSID_MULTIPLIER) % VSID_MODULUS) -#define KERNEL_VSID(ea) VSID_SCRAMBLE(GET_ESID(ea)) - -/* Physical address used by some IO functions */ -typedef unsigned long phys_addr_t; - - -#endif /* __ASSEMBLY */ -#endif /* CONFIG_PPC64 */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_MMU_H_ */ diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h index cb204a7..e4d5fc5 100644 --- a/include/asm-powerpc/mpic.h +++ b/include/asm-powerpc/mpic.h @@ -199,7 +199,7 @@ enum { }; -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS /* Fixup table entry */ struct mpic_irq_fixup { @@ -208,7 +208,7 @@ struct mpic_irq_fixup u32 data; unsigned int index; }; -#endif /* CONFIG_MPIC_BROKEN_U3 */ +#endif /* CONFIG_MPIC_U3_HT_IRQS */ enum mpic_reg_type { @@ -239,7 +239,7 @@ struct mpic /* The "linux" controller struct */ struct irq_chip hc_irq; -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS struct irq_chip hc_ht_irq; #endif #ifdef CONFIG_SMP @@ -268,7 +268,7 @@ #endif /* Spurious vector to program into unused sources */ unsigned int spurious_vec; -#ifdef CONFIG_MPIC_BROKEN_U3 +#ifdef CONFIG_MPIC_U3_HT_IRQS /* The fixup table */ struct mpic_irq_fixup *fixups; spinlock_t fixup_lock; @@ -313,7 +313,7 @@ #define MPIC_PRIMARY 0x00000001 /* Set this for a big-endian MPIC */ #define MPIC_BIG_ENDIAN 0x00000002 /* Broken U3 MPIC */ -#define MPIC_BROKEN_U3 0x00000004 +#define MPIC_U3_HT_IRQS 0x00000004 /* Broken IPI registers (autodetected) */ #define MPIC_BROKEN_IPI 0x00000008 /* MPIC wants a reset */ @@ -352,7 +352,7 @@ #define MPIC_REGSET_TSI108 MPIC_REGSET( * @senses_num: number of entries in the array * * Note about the sense array. If none is passed, all interrupts are - * setup to be level negative unless MPIC_BROKEN_U3 is set in which + * setup to be level negative unless MPIC_U3_HT_IRQS is set in which * case they are edge positive (and the array is ignored anyway). * The values in the array start at the first source of the MPIC, * that is senses[0] correspond to linux irq "irq_offset". diff --git a/include/asm-powerpc/of_device.h b/include/asm-powerpc/of_device.h index a889b20..4f1aabe 100644 --- a/include/asm-powerpc/of_device.h +++ b/include/asm-powerpc/of_device.h @@ -32,5 +32,8 @@ extern int of_device_register(struct of_ extern void of_device_unregister(struct of_device *ofdev); extern void of_release_dev(struct device *dev); +extern int of_device_uevent(struct device *dev, + char **envp, int num_envp, char *buffer, int buffer_size); + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_OF_DEVICE_H */ diff --git a/include/asm-powerpc/oprofile_impl.h b/include/asm-powerpc/oprofile_impl.h index 94c0ad2..8d6b47f 100644 --- a/include/asm-powerpc/oprofile_impl.h +++ b/include/asm-powerpc/oprofile_impl.h @@ -57,6 +57,8 @@ extern struct op_powerpc_model op_model_ extern struct op_powerpc_model op_model_power4; extern struct op_powerpc_model op_model_7450; extern struct op_powerpc_model op_model_cell; +extern struct op_powerpc_model op_model_pa6t; + /* All the classic PPC parts use these */ static inline unsigned int classic_ctr_read(unsigned int i) diff --git a/include/asm-powerpc/paca.h b/include/asm-powerpc/paca.h index 0d3adc0..cf95274 100644 --- a/include/asm-powerpc/paca.h +++ b/include/asm-powerpc/paca.h @@ -70,6 +70,7 @@ #endif /* CONFIG_PPC_ISERIES */ s16 hw_cpu_id; /* Physical processor number */ u8 cpu_start; /* At startup, processor spins until */ /* this becomes non-zero. */ + struct slb_shadow *slb_shadow_ptr; /* * Now, starting in cacheline 2, the exception save areas @@ -93,6 +94,7 @@ #endif /* CONFIG_PPC_ISERIES */ u64 stab_rr; /* stab/slb round-robin counter */ u64 saved_r1; /* r1 save for RTAS calls */ u64 saved_msr; /* MSR saved here by enter_rtas */ + u16 trap_save; /* Used when bad stack is encountered */ u8 soft_enabled; /* irq soft-enable flag */ u8 hard_enabled; /* set if irqs are enabled in MSR */ u8 io_sync; /* writel() needs spin_unlock sync */ @@ -101,8 +103,6 @@ #endif /* CONFIG_PPC_ISERIES */ u64 user_time; /* accumulated usermode TB ticks */ u64 system_time; /* accumulated system TB ticks */ u64 startpurr; /* PURR/TB value snapshot */ - - struct slb_shadow *slb_shadow_ptr; }; extern struct paca_struct paca[]; diff --git a/include/asm-powerpc/parport.h b/include/asm-powerpc/parport.h index 3fca21d..b37b81e 100644 --- a/include/asm-powerpc/parport.h +++ b/include/asm-powerpc/parport.h @@ -20,18 +20,18 @@ extern struct parport *parport_pc_probe_ static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma) { struct device_node *np; - u32 *prop; + const u32 *prop; u32 io1, io2; int propsize; int count = 0; for (np = NULL; (np = of_find_compatible_node(np, "parallel", "pnpPNP,400")) != NULL;) { - prop = (u32 *)get_property(np, "reg", &propsize); + prop = of_get_property(np, "reg", &propsize); if (!prop || propsize > 6*sizeof(u32)) continue; io1 = prop[1]; io2 = prop[2]; - prop = (u32 *)get_property(np, "interrupts", NULL); + prop = of_get_property(np, "interrupts", NULL); if (!prop) continue; if (parport_pc_probe_port(io1, io2, prop[0], autodma, NULL) != NULL) diff --git a/include/asm-powerpc/pci.h b/include/asm-powerpc/pci.h index ac656ee..ce0f13e 100644 --- a/include/asm-powerpc/pci.h +++ b/include/asm-powerpc/pci.h @@ -70,19 +70,22 @@ #ifdef CONFIG_PPC64 */ #define PCI_DISABLE_MWI -extern struct dma_mapping_ops *pci_dma_ops; +#ifdef CONFIG_PCI +extern void set_pci_dma_ops(struct dma_mapping_ops *dma_ops); +extern struct dma_mapping_ops *get_pci_dma_ops(void); /* For DAC DMA, we currently don't support it by default, but * we let 64-bit platforms override this. */ static inline int pci_dac_dma_supported(struct pci_dev *hwdev,u64 mask) { - if (pci_dma_ops && pci_dma_ops->dac_dma_supported) - return pci_dma_ops->dac_dma_supported(&hwdev->dev, mask); + struct dma_mapping_ops *d = get_pci_dma_ops(); + + if (d && d->dac_dma_supported) + return d->dac_dma_supported(&hwdev->dev, mask); return 0; } -#ifdef CONFIG_PCI static inline void pci_dma_burst_advice(struct pci_dev *pdev, enum pci_dma_burst_strategy *strat, unsigned long *strategy_parameter) @@ -99,6 +102,9 @@ static inline void pci_dma_burst_advice( *strat = PCI_DMA_BURST_MULTIPLE; *strategy_parameter = cacheline_size; } +#else /* CONFIG_PCI */ +#define set_pci_dma_ops(d) +#define get_pci_dma_ops() NULL #endif extern int pci_domain_nr(struct pci_bus *bus); diff --git a/include/asm-powerpc/pgtable-4k.h b/include/asm-powerpc/pgtable-4k.h index 345d9b0..a28fa8b 100644 --- a/include/asm-powerpc/pgtable-4k.h +++ b/include/asm-powerpc/pgtable-4k.h @@ -97,3 +97,6 @@ #define pud_offset(pgdp, addr) \ #define pud_ERROR(e) \ printk("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pud_val(e)) + +#define remap_4k_pfn(vma, addr, pfn, prot) \ + remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, (prot)) diff --git a/include/asm-powerpc/pgtable-64k.h b/include/asm-powerpc/pgtable-64k.h index 4b7126c..5e84f07 100644 --- a/include/asm-powerpc/pgtable-64k.h +++ b/include/asm-powerpc/pgtable-64k.h @@ -35,6 +35,7 @@ #define PGDIR_MASK (~(PGDIR_SIZE-1)) #define _PAGE_HPTE_SUB 0x0ffff000 /* combo only: sub pages HPTE bits */ #define _PAGE_HPTE_SUB0 0x08000000 /* combo only: first sub page */ #define _PAGE_COMBO 0x10000000 /* this is a combo 4k page */ +#define _PAGE_4K_PFN 0x20000000 /* PFN is for a single 4k page */ #define _PAGE_F_SECOND 0x00008000 /* full page: hidx bits */ #define _PAGE_F_GIX 0x00007000 /* full page: hidx bits */ @@ -93,6 +94,10 @@ #define pte_iterate_hashed_end() } while #define pte_pagesize_index(pte) \ (((pte) & _PAGE_COMBO)? MMU_PAGE_4K: MMU_PAGE_64K) +#define remap_4k_pfn(vma, addr, pfn, prot) \ + remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, \ + __pgprot(pgprot_val((prot)) | _PAGE_4K_PFN)) + #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PGTABLE_64K_H */ diff --git a/include/asm-powerpc/pgtable.h b/include/asm-powerpc/pgtable.h index 10f5274..19edb69 100644 --- a/include/asm-powerpc/pgtable.h +++ b/include/asm-powerpc/pgtable.h @@ -272,7 +272,10 @@ static inline pte_t pte_mkhuge(pte_t pte return pte; } /* Atomic PTE updates */ -static inline unsigned long pte_update(pte_t *p, unsigned long clr) +static inline unsigned long pte_update(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep, unsigned long clr, + int huge) { unsigned long old, tmp; @@ -283,20 +286,15 @@ static inline unsigned long pte_update(p andc %1,%0,%4 \n\ stdcx. %1,0,%3 \n\ bne- 1b" - : "=&r" (old), "=&r" (tmp), "=m" (*p) - : "r" (p), "r" (clr), "m" (*p), "i" (_PAGE_BUSY) + : "=&r" (old), "=&r" (tmp), "=m" (*ptep) + : "r" (ptep), "r" (clr), "m" (*ptep), "i" (_PAGE_BUSY) : "cc" ); + + if (old & _PAGE_HASHPTE) + hpte_need_flush(mm, addr, ptep, old, huge); return old; } -/* PTE updating functions, this function puts the PTE in the - * batch, doesn't actually triggers the hash flush immediately, - * you need to call flush_tlb_pending() to do that. - * Pass -1 for "normal" size (4K or 64K) - */ -extern void hpte_update(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, unsigned long pte, int huge); - static inline int __ptep_test_and_clear_young(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { @@ -304,11 +302,7 @@ static inline int __ptep_test_and_clear_ if ((pte_val(*ptep) & (_PAGE_ACCESSED | _PAGE_HASHPTE)) == 0) return 0; - old = pte_update(ptep, _PAGE_ACCESSED); - if (old & _PAGE_HASHPTE) { - hpte_update(mm, addr, ptep, old, 0); - flush_tlb_pending(); - } + old = pte_update(mm, addr, ptep, _PAGE_ACCESSED, 0); return (old & _PAGE_ACCESSED) != 0; } #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG @@ -331,9 +325,7 @@ static inline int __ptep_test_and_clear_ if ((pte_val(*ptep) & _PAGE_DIRTY) == 0) return 0; - old = pte_update(ptep, _PAGE_DIRTY); - if (old & _PAGE_HASHPTE) - hpte_update(mm, addr, ptep, old, 0); + old = pte_update(mm, addr, ptep, _PAGE_DIRTY, 0); return (old & _PAGE_DIRTY) != 0; } #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY @@ -352,9 +344,7 @@ static inline void ptep_set_wrprotect(st if ((pte_val(*ptep) & _PAGE_RW) == 0) return; - old = pte_update(ptep, _PAGE_RW); - if (old & _PAGE_HASHPTE) - hpte_update(mm, addr, ptep, old, 0); + old = pte_update(mm, addr, ptep, _PAGE_RW, 0); } /* @@ -378,7 +368,6 @@ #define ptep_clear_flush_dirty(__vma, __ ({ \ int __dirty = __ptep_test_and_clear_dirty((__vma)->vm_mm, __address, \ __ptep); \ - flush_tlb_page(__vma, __address); \ __dirty; \ }) @@ -386,20 +375,14 @@ #define __HAVE_ARCH_PTEP_GET_AND_CLEAR static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - unsigned long old = pte_update(ptep, ~0UL); - - if (old & _PAGE_HASHPTE) - hpte_update(mm, addr, ptep, old, 0); + unsigned long old = pte_update(mm, addr, ptep, ~0UL, 0); return __pte(old); } static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t * ptep) { - unsigned long old = pte_update(ptep, ~0UL); - - if (old & _PAGE_HASHPTE) - hpte_update(mm, addr, ptep, old, 0); + pte_update(mm, addr, ptep, ~0UL, 0); } /* @@ -408,10 +391,8 @@ static inline void pte_clear(struct mm_s static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte) { - if (pte_present(*ptep)) { + if (pte_present(*ptep)) pte_clear(mm, addr, ptep); - flush_tlb_pending(); - } pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS); *ptep = pte; } @@ -467,16 +448,6 @@ extern pgd_t swapper_pg_dir[]; extern void paging_init(void); -/* - * This gets called at the end of handling a page fault, when - * the kernel has put a new PTE into the page table for the process. - * We use it to put a corresponding HPTE into the hash table - * ahead of time, instead of waiting for the inevitable extra - * hash-table miss exception. - */ -struct vm_area_struct; -extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t); - /* Encode and de-code a swap entry */ #define __swp_type(entry) (((entry).val >> 1) & 0x3f) #define __swp_offset(entry) ((entry).val >> 8) @@ -522,6 +493,7 @@ void pgtable_cache_init(void); return pt; } + #include #endif /* __ASSEMBLY__ */ diff --git a/include/asm-powerpc/pmc.h b/include/asm-powerpc/pmc.h index 8588be6..d6a616a 100644 --- a/include/asm-powerpc/pmc.h +++ b/include/asm-powerpc/pmc.h @@ -30,6 +30,7 @@ void release_pmc_hardware(void); #ifdef CONFIG_PPC64 void power4_enable_pmcs(void); +void pasemi_enable_pmcs(void); #endif #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h index ab6eddb..d74b296 100644 --- a/include/asm-powerpc/ppc-pci.h +++ b/include/asm-powerpc/ppc-pci.h @@ -10,6 +10,8 @@ #ifndef _ASM_POWERPC_PPC_PCI_H #define _ASM_POWERPC_PPC_PCI_H #ifdef __KERNEL__ +#ifdef CONFIG_PCI + #include #include @@ -22,7 +24,7 @@ extern void pci_setup_phb_io_dynamic(str extern struct list_head hose_list; extern int global_phb_number; -extern unsigned long find_and_init_phbs(void); +extern void find_and_init_phbs(void); extern struct pci_dev *ppc64_isabridge_dev; /* may be NULL if no ISA bus */ @@ -68,7 +70,7 @@ struct pci_dev *pci_get_device_by_addr(u void eeh_slot_error_detail (struct pci_dn *pdn, int severity); /** - * rtas_pci_enableo - enable IO transfers for this slot + * rtas_pci_enable - enable IO transfers for this slot * @pdn: pci device node * @function: either EEH_THAW_MMIO or EEH_THAW_DMA * @@ -89,6 +91,7 @@ int rtas_pci_enable(struct pci_dn *pdn, * Returns a non-zero value if the reset failed. */ int rtas_set_slot_reset (struct pci_dn *); +int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs); /** * eeh_restore_bars - Restore device configuration info. @@ -126,5 +129,10 @@ struct device_node * find_device_pe(stru #endif +#else /* CONFIG_PCI */ +static inline void find_and_init_phbs(void) { } +static inline void init_pci_config_tokens(void) { } +#endif /* !CONFIG_PCI */ + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PPC_PCI_H */ diff --git a/include/asm-powerpc/processor.h b/include/asm-powerpc/processor.h index a26c32e..d947b16 100644 --- a/include/asm-powerpc/processor.h +++ b/include/asm-powerpc/processor.h @@ -133,7 +133,6 @@ #endif mm_segment_t fs; /* for get_fs() validation */ #ifdef CONFIG_PPC32 void *pgdir; /* root of page-table tree */ - signed long last_syscall; #endif #if defined(CONFIG_4xx) || defined (CONFIG_BOOKE) unsigned long dbcr0; /* debug control register values */ diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index 020ed01..ec400f6 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -18,7 +18,9 @@ #ifdef __KERNEL__ #include #include #include +#include #include +#include /* Definitions used by the flattened device tree */ #define OF_DT_HEADER 0xd00dfeed /* marker */ @@ -58,6 +60,8 @@ struct boot_param_header u32 boot_cpuid_phys; /* Physical CPU id we're booting on */ /* version 3 fields below */ u32 dt_strings_size; /* size of the DT strings block */ + /* version 17 fields below */ + u32 dt_struct_size; /* size of the DT structure block */ }; @@ -68,7 +72,7 @@ typedef u32 ihandle; struct property { char *name; int length; - unsigned char *value; + void *value; struct property *next; }; @@ -108,14 +112,6 @@ static inline void set_node_proc_entry(s } -/* OBSOLETE: Old style node lookup */ -extern struct device_node *find_devices(const char *name); -extern struct device_node *find_type_devices(const char *type); -extern struct device_node *find_path_device(const char *path); -extern struct device_node *find_compatible_devices(const char *type, - const char *compat); -extern struct device_node *find_all_nodes(void); - /* New style node lookup */ extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name); @@ -159,15 +155,17 @@ extern void of_detach_node(const struct extern void finish_device_tree(void); extern void unflatten_device_tree(void); extern void early_init_devtree(void *); -extern int device_is_compatible(const struct device_node *device, +extern int of_device_is_compatible(const struct device_node *device, const char *); +#define device_is_compatible(d, c) of_device_is_compatible((d), (c)) extern int machine_is_compatible(const char *compat); -extern const void *get_property(const struct device_node *node, +extern const void *of_get_property(const struct device_node *node, const char *name, int *lenp); +#define get_property(a, b, c) of_get_property((a), (b), (c)) extern void print_properties(struct device_node *node); -extern int prom_n_addr_cells(struct device_node* np); -extern int prom_n_size_cells(struct device_node* np); +extern int of_n_addr_cells(struct device_node* np); +extern int of_n_size_cells(struct device_node* np); extern int prom_n_intr_cells(struct device_node* np); extern void prom_get_irq_senses(unsigned char *senses, int off, int max); extern int prom_add_property(struct device_node* np, struct property* prop); @@ -350,6 +348,16 @@ static inline int of_irq_to_resource(str return irq; } +static inline void __iomem *of_iomap(struct device_node *np, int index) +{ + struct resource res; + + if (of_address_to_resource(np, index, &res)) + return NULL; + + return ioremap(res.start, 1 + res.end - res.start); +} + #endif /* __KERNEL__ */ #endif /* _POWERPC_PROM_H */ diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index 0d7f016..749c7f9 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -469,12 +469,68 @@ #define SPRN_PMC8 794 #define SPRN_SIAR 780 #define SPRN_SDAR 781 -#define PA6T_SPRN_PMC0 787 -#define PA6T_SPRN_PMC1 788 -#define PA6T_SPRN_PMC2 789 -#define PA6T_SPRN_PMC3 790 -#define PA6T_SPRN_PMC4 791 -#define PA6T_SPRN_PMC5 792 +#define SPRN_PA6T_MMCR0 795 +#define PA6T_MMCR0_EN0 0x0000000000000001UL +#define PA6T_MMCR0_EN1 0x0000000000000002UL +#define PA6T_MMCR0_EN2 0x0000000000000004UL +#define PA6T_MMCR0_EN3 0x0000000000000008UL +#define PA6T_MMCR0_EN4 0x0000000000000010UL +#define PA6T_MMCR0_EN5 0x0000000000000020UL +#define PA6T_MMCR0_SUPEN 0x0000000000000040UL +#define PA6T_MMCR0_PREN 0x0000000000000080UL +#define PA6T_MMCR0_HYPEN 0x0000000000000100UL +#define PA6T_MMCR0_FCM0 0x0000000000000200UL +#define PA6T_MMCR0_FCM1 0x0000000000000400UL +#define PA6T_MMCR0_INTGEN 0x0000000000000800UL +#define PA6T_MMCR0_INTEN0 0x0000000000001000UL +#define PA6T_MMCR0_INTEN1 0x0000000000002000UL +#define PA6T_MMCR0_INTEN2 0x0000000000004000UL +#define PA6T_MMCR0_INTEN3 0x0000000000008000UL +#define PA6T_MMCR0_INTEN4 0x0000000000010000UL +#define PA6T_MMCR0_INTEN5 0x0000000000020000UL +#define PA6T_MMCR0_DISCNT 0x0000000000040000UL +#define PA6T_MMCR0_UOP 0x0000000000080000UL +#define PA6T_MMCR0_TRG 0x0000000000100000UL +#define PA6T_MMCR0_TRGEN 0x0000000000200000UL +#define PA6T_MMCR0_TRGREG 0x0000000001600000UL +#define PA6T_MMCR0_SIARLOG 0x0000000002000000UL +#define PA6T_MMCR0_SDARLOG 0x0000000004000000UL +#define PA6T_MMCR0_PROEN 0x0000000008000000UL +#define PA6T_MMCR0_PROLOG 0x0000000010000000UL +#define PA6T_MMCR0_DAMEN2 0x0000000020000000UL +#define PA6T_MMCR0_DAMEN3 0x0000000040000000UL +#define PA6T_MMCR0_DAMEN4 0x0000000080000000UL +#define PA6T_MMCR0_DAMEN5 0x0000000100000000UL +#define PA6T_MMCR0_DAMSEL2 0x0000000200000000UL +#define PA6T_MMCR0_DAMSEL3 0x0000000400000000UL +#define PA6T_MMCR0_DAMSEL4 0x0000000800000000UL +#define PA6T_MMCR0_DAMSEL5 0x0000001000000000UL +#define PA6T_MMCR0_HANDDIS 0x0000002000000000UL +#define PA6T_MMCR0_PCTEN 0x0000004000000000UL +#define PA6T_MMCR0_SOCEN 0x0000008000000000UL +#define PA6T_MMCR0_SOCMOD 0x0000010000000000UL + +#define SPRN_PA6T_MMCR1 798 +#define PA6T_MMCR1_ES2 0x00000000000000ffUL +#define PA6T_MMCR1_ES3 0x000000000000ff00UL +#define PA6T_MMCR1_ES4 0x0000000000ff0000UL +#define PA6T_MMCR1_ES5 0x00000000ff000000UL + +#define SPRN_PA6T_SIAR 780 +#define SPRN_PA6T_UPMC0 771 +#define SPRN_PA6T_UPMC1 772 +#define SPRN_PA6T_UPMC2 773 +#define SPRN_PA6T_UPMC3 774 +#define SPRN_PA6T_UPMC4 775 +#define SPRN_PA6T_UPMC5 776 +#define SPRN_PA6T_UMMCR0 779 +#define SPRN_PA6T_UMMCR1 782 +#define SPRN_PA6T_PMC0 787 +#define SPRN_PA6T_PMC1 788 +#define SPRN_PA6T_PMC2 789 +#define SPRN_PA6T_PMC3 790 +#define SPRN_PA6T_PMC4 791 +#define SPRN_PA6T_PMC5 792 #else /* 32-bit */ #define SPRN_MMCR0 952 /* Monitor Mode Control Register 0 */ diff --git a/include/asm-powerpc/socket.h b/include/asm-powerpc/socket.h index c8b1da5..403e9fd 100644 --- a/include/asm-powerpc/socket.h +++ b/include/asm-powerpc/socket.h @@ -56,5 +56,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/include/asm-powerpc/sockios.h b/include/asm-powerpc/sockios.h index 590078d..55cef76 100644 --- a/include/asm-powerpc/sockios.h +++ b/include/asm-powerpc/sockios.h @@ -14,6 +14,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* _ASM_POWERPC_SOCKIOS_H */ diff --git a/include/asm-powerpc/spu_csa.h b/include/asm-powerpc/spu_csa.h index 8aad061..02e56a6 100644 --- a/include/asm-powerpc/spu_csa.h +++ b/include/asm-powerpc/spu_csa.h @@ -242,6 +242,7 @@ struct spu_state { u64 spu_chnldata_RW[32]; u32 spu_mailbox_data[4]; u32 pu_mailbox_data[1]; + u64 dar, dsisr; unsigned long suspend_time; spinlock_t register_lock; }; diff --git a/include/asm-powerpc/string.h b/include/asm-powerpc/string.h index faa407f..aa40f92 100644 --- a/include/asm-powerpc/string.h +++ b/include/asm-powerpc/string.h @@ -14,8 +14,6 @@ #define __HAVE_ARCH_MEMMOVE #define __HAVE_ARCH_MEMCMP #define __HAVE_ARCH_MEMCHR -extern int strcasecmp(const char *, const char *); -extern int strncasecmp(const char *, const char *, __kernel_size_t); extern char * strcpy(char *,const char *); extern char * strncpy(char *,const char *, __kernel_size_t); extern __kernel_size_t strlen(const char *); diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index f7b1227..d3e0906 100644 --- a/include/asm-powerpc/system.h +++ b/include/asm-powerpc/system.h @@ -131,6 +131,7 @@ extern void enable_kernel_altivec(void); extern void giveup_altivec(struct task_struct *); extern void load_up_altivec(struct task_struct *); extern int emulate_altivec(struct pt_regs *); +extern void enable_kernel_spe(void); extern void giveup_spe(struct task_struct *); extern void load_up_spe(struct task_struct *); extern int fix_alignment(struct pt_regs *); diff --git a/include/asm-powerpc/tlb.h b/include/asm-powerpc/tlb.h index 4e2a834..0a17682 100644 --- a/include/asm-powerpc/tlb.h +++ b/include/asm-powerpc/tlb.h @@ -38,7 +38,6 @@ extern void pte_free_finish(void); static inline void tlb_flush(struct mmu_gather *tlb) { - flush_tlb_pending(); pte_free_finish(); } diff --git a/include/asm-powerpc/tlbflush.h b/include/asm-powerpc/tlbflush.h index 93c7d0c..86e6266 100644 --- a/include/asm-powerpc/tlbflush.h +++ b/include/asm-powerpc/tlbflush.h @@ -17,10 +17,73 @@ #define _ASM_POWERPC_TLBFLUSH_H */ #ifdef __KERNEL__ - struct mm_struct; +struct vm_area_struct; + +#if defined(CONFIG_4xx) || defined(CONFIG_8xx) || defined(CONFIG_FSL_BOOKE) +/* + * TLB flushing for software loaded TLB chips + * + * TODO: (CONFIG_FSL_BOOKE) determine if flush_tlb_range & + * flush_tlb_kernel_range are best implemented as tlbia vs + * specific tlbie's + */ + +extern void _tlbie(unsigned long address); + +#if defined(CONFIG_40x) || defined(CONFIG_8xx) +#define _tlbia() asm volatile ("tlbia; sync" : : : "memory") +#else /* CONFIG_44x || CONFIG_FSL_BOOKE */ +extern void _tlbia(void); +#endif + +static inline void flush_tlb_mm(struct mm_struct *mm) +{ + _tlbia(); +} + +static inline void flush_tlb_page(struct vm_area_struct *vma, + unsigned long vmaddr) +{ + _tlbie(vmaddr); +} + +static inline void flush_tlb_page_nohash(struct vm_area_struct *vma, + unsigned long vmaddr) +{ + _tlbie(vmaddr); +} + +static inline void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + _tlbia(); +} + +static inline void flush_tlb_kernel_range(unsigned long start, + unsigned long end) +{ + _tlbia(); +} -#ifdef CONFIG_PPC64 +#elif defined(CONFIG_PPC32) +/* + * TLB flushing for "classic" hash-MMMU 32-bit CPUs, 6xx, 7xx, 7xxx + */ +extern void _tlbie(unsigned long address); +extern void _tlbia(void); + +extern void flush_tlb_mm(struct mm_struct *mm); +extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr); +extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr); +extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end); +extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); + +#else +/* + * TLB flushing for 64-bit has-MMU CPUs + */ #include #include @@ -28,117 +91,90 @@ #include #define PPC64_TLB_BATCH_NR 192 struct ppc64_tlb_batch { - unsigned long index; - struct mm_struct *mm; - real_pte_t pte[PPC64_TLB_BATCH_NR]; - unsigned long vaddr[PPC64_TLB_BATCH_NR]; - unsigned int psize; + int active; + unsigned long index; + struct mm_struct *mm; + real_pte_t pte[PPC64_TLB_BATCH_NR]; + unsigned long vaddr[PPC64_TLB_BATCH_NR]; + unsigned int psize; }; DECLARE_PER_CPU(struct ppc64_tlb_batch, ppc64_tlb_batch); extern void __flush_tlb_pending(struct ppc64_tlb_batch *batch); -static inline void flush_tlb_pending(void) +extern void hpte_need_flush(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long pte, int huge); + +#define __HAVE_ARCH_ENTER_LAZY_MMU_MODE + +static inline void arch_enter_lazy_mmu_mode(void) +{ + struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); + + batch->active = 1; +} + +static inline void arch_leave_lazy_mmu_mode(void) { - struct ppc64_tlb_batch *batch = &get_cpu_var(ppc64_tlb_batch); + struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); if (batch->index) __flush_tlb_pending(batch); - put_cpu_var(ppc64_tlb_batch); + batch->active = 0; } +#define arch_flush_lazy_mmu_mode() do {} while (0) + + extern void flush_hash_page(unsigned long va, real_pte_t pte, int psize, int local); extern void flush_hash_range(unsigned long number, int local); -#else /* CONFIG_PPC64 */ - -#include - -extern void _tlbie(unsigned long address); -extern void _tlbia(void); - -/* - * TODO: (CONFIG_FSL_BOOKE) determine if flush_tlb_range & - * flush_tlb_kernel_range are best implemented as tlbia vs - * specific tlbie's - */ - -#if (defined(CONFIG_4xx) && !defined(CONFIG_44x)) || defined(CONFIG_8xx) -#define flush_tlb_pending() asm volatile ("tlbia; sync" : : : "memory") -#elif defined(CONFIG_4xx) || defined(CONFIG_FSL_BOOKE) -#define flush_tlb_pending() _tlbia() -#endif - -/* - * This gets called at the end of handling a page fault, when - * the kernel has put a new PTE into the page table for the process. - * We use it to ensure coherency between the i-cache and d-cache - * for the page which has just been mapped in. - * On machines which use an MMU hash table, we use this to put a - * corresponding HPTE into the hash table ahead of time, instead of - * waiting for the inevitable extra hash-table miss exception. - */ -extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t); - -#endif /* CONFIG_PPC64 */ - -#if defined(CONFIG_PPC64) || defined(CONFIG_4xx) || \ - defined(CONFIG_FSL_BOOKE) || defined(CONFIG_8xx) static inline void flush_tlb_mm(struct mm_struct *mm) { - flush_tlb_pending(); } static inline void flush_tlb_page(struct vm_area_struct *vma, - unsigned long vmaddr) + unsigned long vmaddr) { -#ifdef CONFIG_PPC64 - flush_tlb_pending(); -#else - _tlbie(vmaddr); -#endif } static inline void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long vmaddr) { -#ifndef CONFIG_PPC64 - _tlbie(vmaddr); -#endif } static inline void flush_tlb_range(struct vm_area_struct *vma, - unsigned long start, unsigned long end) + unsigned long start, unsigned long end) { - flush_tlb_pending(); } static inline void flush_tlb_kernel_range(unsigned long start, - unsigned long end) + unsigned long end) { - flush_tlb_pending(); } -#else /* 6xx, 7xx, 7xxx cpus */ - -extern void flush_tlb_mm(struct mm_struct *mm); -extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr); -extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr); -extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end); -extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); - #endif /* + * This gets called at the end of handling a page fault, when + * the kernel has put a new PTE into the page table for the process. + * We use it to ensure coherency between the i-cache and d-cache + * for the page which has just been mapped in. + * On machines which use an MMU hash table, we use this to put a + * corresponding HPTE into the hash table ahead of time, instead of + * waiting for the inevitable extra hash-table miss exception. + */ +extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t); + +/* * This is called in munmap when we have freed up some page-table * pages. We don't need to do anything here, there's nothing special * about our page-table pages. -- paulus */ static inline void flush_tlb_pgtables(struct mm_struct *mm, - unsigned long start, unsigned long end) + unsigned long start, unsigned long end) { } diff --git a/include/asm-powerpc/uaccess.h b/include/asm-powerpc/uaccess.h index adbf16b..8e798e3 100644 --- a/include/asm-powerpc/uaccess.h +++ b/include/asm-powerpc/uaccess.h @@ -110,12 +110,18 @@ #define __get_user(x, ptr) \ __get_user_nocheck((x), (ptr), sizeof(*(ptr))) #define __put_user(x, ptr) \ __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) + #ifndef __powerpc64__ #define __get_user64(x, ptr) \ __get_user64_nocheck((x), (ptr), sizeof(*(ptr))) #define __put_user64(x, ptr) __put_user(x, ptr) #endif +#define __get_user_inatomic(x, ptr) \ + __get_user_nosleep((x), (ptr), sizeof(*(ptr))) +#define __put_user_inatomic(x, ptr) \ + __put_user_nosleep((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) + #define __get_user_unaligned __get_user #define __put_user_unaligned __put_user @@ -198,6 +204,16 @@ ({ \ __pu_err; \ }) +#define __put_user_nosleep(x, ptr, size) \ +({ \ + long __pu_err; \ + __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ + __chk_user_ptr(ptr); \ + __put_user_size((x), __pu_addr, (size), __pu_err); \ + __pu_err; \ +}) + + extern long __get_user_bad(void); #define __get_user_asm(x, addr, err, op) \ @@ -297,6 +313,18 @@ ({ \ __gu_err; \ }) +#define __get_user_nosleep(x, ptr, size) \ +({ \ + long __gu_err; \ + unsigned long __gu_val; \ + const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ + __chk_user_ptr(ptr); \ + __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + + /* more complex routines */ extern unsigned long __copy_tofrom_user(void __user *to, diff --git a/include/asm-powerpc/ucc_fast.h b/include/asm-powerpc/ucc_fast.h index 39d1c90..f529f70 100644 --- a/include/asm-powerpc/ucc_fast.h +++ b/include/asm-powerpc/ucc_fast.h @@ -159,6 +159,9 @@ struct ucc_fast_private { struct ucc_fast *uf_regs; /* a pointer to memory map of UCC regs. */ u32 *p_ucce; /* a pointer to the event register in memory. */ u32 *p_uccm; /* a pointer to the mask register in memory. */ +#ifdef CONFIG_UGETH_TX_ON_DEMAND + u16 *p_utodr; /* pointer to the transmit on demand register */ +#endif int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ int stopped_tx; /* Whether channel has been stopped for Tx diff --git a/include/asm-powerpc/uic.h b/include/asm-powerpc/uic.h new file mode 100644 index 0000000..970eb7e --- /dev/null +++ b/include/asm-powerpc/uic.h @@ -0,0 +1,23 @@ +/* + * include/asm-powerpc/uic.h + * + * IBM PPC4xx UIC external definitions and structure. + * + * Maintainer: David Gibson + * Copyright 2007 IBM Corporation. + * + * 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. + */ +#ifndef _ASM_POWERPC_UIC_H +#define _ASM_POWERPC_UIC_H + +#ifdef __KERNEL__ + +extern void __init uic_init_tree(void); +extern unsigned int uic_get_irq(void); + +#endif /* __KERNEL__ */ +#endif /* _ASM_POWERPC_UIC_H */ diff --git a/include/asm-ppc/ibm4xx.h b/include/asm-ppc/ibm4xx.h index 92fd02d..ed6891a 100644 --- a/include/asm-ppc/ibm4xx.h +++ b/include/asm-ppc/ibm4xx.h @@ -47,12 +47,8 @@ #if defined(CONFIG_WALNUT) #include #endif -#if defined(CONFIG_XILINX_ML300) -#include -#endif - -#if defined(CONFIG_XILINX_ML403) -#include +#if defined(CONFIG_XILINX_VIRTEX) +#include #endif #ifndef __ASSEMBLY__ diff --git a/include/asm-ppc/ppc_sys.h b/include/asm-ppc/ppc_sys.h index 40f197a..de99e92 100644 --- a/include/asm-ppc/ppc_sys.h +++ b/include/asm-ppc/ppc_sys.h @@ -33,8 +33,6 @@ #elif defined(CONFIG_PPC_MPC52xx) #include #elif defined(CONFIG_MPC10X_BRIDGE) #include -#elif defined(CONFIG_XILINX_VIRTEX) -#include #else #error "need definition of ppc_sys_devices" #endif diff --git a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h index adc5ae7..901f7fa 100644 --- a/include/asm-ppc/prom.h +++ b/include/asm-ppc/prom.h @@ -34,7 +34,8 @@ #define PTRUNRELOC(x) ((typeof(x))sub_re */ #define machine_is_compatible(x) 0 #define of_find_compatible_node(f, t, c) NULL -#define get_property(p, n, l) NULL +#define of_get_property(p, n, l) NULL +#define get_property(a, b, c) of_get_property((a), (b), (c)) #endif /* _PPC_PROM_H */ #endif /* __KERNEL__ */ diff --git a/include/asm-s390/bug.h b/include/asm-s390/bug.h index 8768983..838684d 100644 --- a/include/asm-s390/bug.h +++ b/include/asm-s390/bug.h @@ -1,27 +1,70 @@ -#ifndef _S390_BUG_H -#define _S390_BUG_H +#ifndef _ASM_S390_BUG_H +#define _ASM_S390_BUG_H #include #ifdef CONFIG_BUG -static inline __attribute__((noreturn)) void __do_illegal_op(void) -{ -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) - __builtin_trap(); +#ifdef CONFIG_64BIT +#define S390_LONG ".quad" #else - asm volatile(".long 0"); +#define S390_LONG ".long" #endif -} -#define BUG() do { \ - printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ - __do_illegal_op(); \ +#ifdef CONFIG_DEBUG_BUGVERBOSE + +#define __EMIT_BUG(x) do { \ + asm volatile( \ + "0: j 0b+2\n" \ + "1:\n" \ + ".section .rodata.str,\"aMS\",@progbits,1\n" \ + "2: .asciz \""__FILE__"\"\n" \ + ".previous\n" \ + ".section __bug_table,\"a\"\n" \ + "3:\t" S390_LONG "\t1b,2b\n" \ + " .short %0,%1\n" \ + " .org 3b+%2\n" \ + ".previous\n" \ + : : "i" (__LINE__), \ + "i" (x), \ + "i" (sizeof(struct bug_entry))); \ } while (0) +#else /* CONFIG_DEBUG_BUGVERBOSE */ + +#define __EMIT_BUG(x) do { \ + asm volatile( \ + "0: j 0b+2\n" \ + "1:\n" \ + ".section __bug_table,\"a\"\n" \ + "2:\t" S390_LONG "\t1b\n" \ + " .short %0\n" \ + " .org 2b+%1\n" \ + ".previous\n" \ + : : "i" (x), \ + "i" (sizeof(struct bug_entry))); \ +} while (0) + +#endif /* CONFIG_DEBUG_BUGVERBOSE */ + +#define BUG() __EMIT_BUG(0) + +#define WARN_ON(x) ({ \ + typeof(x) __ret_warn_on = (x); \ + if (__builtin_constant_p(__ret_warn_on)) { \ + if (__ret_warn_on) \ + __EMIT_BUG(BUGFLAG_WARNING); \ + } else { \ + if (unlikely(__ret_warn_on)) \ + __EMIT_BUG(BUGFLAG_WARNING); \ + } \ + unlikely(__ret_warn_on); \ +}) + #define HAVE_ARCH_BUG -#endif +#define HAVE_ARCH_WARN_ON +#endif /* CONFIG_BUG */ #include -#endif +#endif /* _ASM_S390_BUG_H */ diff --git a/include/asm-s390/ccwgroup.h b/include/asm-s390/ccwgroup.h index d2f9c0d..925b3dd 100644 --- a/include/asm-s390/ccwgroup.h +++ b/include/asm-s390/ccwgroup.h @@ -11,6 +11,7 @@ struct ccwgroup_device { CCWGROUP_ONLINE, } state; atomic_t onoff; + struct mutex reg_mutex; unsigned int count; /* number of attached slave devices */ struct device dev; /* master device */ struct ccw_device *cdev[0]; /* variable number, allocate as needed */ diff --git a/include/asm-s390/chpid.h b/include/asm-s390/chpid.h new file mode 100644 index 0000000..b203336 --- /dev/null +++ b/include/asm-s390/chpid.h @@ -0,0 +1,53 @@ +/* + * drivers/s390/cio/chpid.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef _ASM_S390_CHPID_H +#define _ASM_S390_CHPID_H _ASM_S390_CHPID_H + +#include +#include +#include + +#define __MAX_CHPID 255 + +struct chp_id { + u8 reserved1; + u8 cssid; + u8 reserved2; + u8 id; +} __attribute__((packed)); + +static inline void chp_id_init(struct chp_id *chpid) +{ + memset(chpid, 0, sizeof(struct chp_id)); +} + +static inline int chp_id_is_equal(struct chp_id *a, struct chp_id *b) +{ + return (a->id == b->id) && (a->cssid == b->cssid); +} + +static inline void chp_id_next(struct chp_id *chpid) +{ + if (chpid->id < __MAX_CHPID) + chpid->id++; + else { + chpid->id = 0; + chpid->cssid++; + } +} + +static inline int chp_id_is_valid(struct chp_id *chpid) +{ + return (chpid->cssid <= __MAX_CSSID); +} + + +#define chp_id_for_each(c) \ + for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) + +#endif /* _ASM_S390_CHPID_H */ diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h index d927850..f738d28 100644 --- a/include/asm-s390/cio.h +++ b/include/asm-s390/cio.h @@ -13,6 +13,7 @@ #include #ifdef __KERNEL__ #define LPM_ANYPATH 0xff +#define __MAX_CSSID 0 /* * subchannel status word @@ -292,6 +293,13 @@ extern void css_schedule_reprobe(void); extern void reipl_ccw_dev(struct ccw_dev_id *id); +struct cio_iplinfo { + u16 devno; + int is_qdio; +}; + +extern int cio_get_iplinfo(struct cio_iplinfo *iplinfo); + #endif #endif diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h index 0eb6408..bdcd448 100644 --- a/include/asm-s390/ipl.h +++ b/include/asm-s390/ipl.h @@ -8,6 +8,8 @@ #ifndef _ASM_S390_IPL_H #define _ASM_S390_IPL_H #include +#include +#include #define IPL_PARMBLOCK_ORIGIN 0x2000 @@ -74,12 +76,12 @@ struct ipl_parameter_block { } __attribute__((packed)); /* - * IPL validity flags and parameters as detected in head.S + * IPL validity flags */ extern u32 ipl_flags; -extern u16 ipl_devno; extern u32 dump_prefix_page; + extern void do_reipl(void); extern void ipl_save_parameters(void); @@ -89,6 +91,35 @@ enum { IPL_NSS_VALID = 4, }; +enum ipl_type { + IPL_TYPE_UNKNOWN = 1, + IPL_TYPE_CCW = 2, + IPL_TYPE_FCP = 4, + IPL_TYPE_FCP_DUMP = 8, + IPL_TYPE_NSS = 16, +}; + +struct ipl_info +{ + enum ipl_type type; + union { + struct { + struct ccw_dev_id dev_id; + } ccw; + struct { + struct ccw_dev_id dev_id; + u64 wwpn; + u64 lun; + } fcp; + struct { + char name[NSS_NAME_SIZE + 1]; + } nss; + } data; +}; + +extern struct ipl_info ipl_info; +extern void setup_ipl_info(void); + /* * DIAG 308 support */ diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index 4a31d0a..ffc9788 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -147,6 +147,52 @@ void pgm_check_handler(void); void mcck_int_handler(void); void io_int_handler(void); +struct save_area_s390 { + u32 ext_save; + u64 timer; + u64 clk_cmp; + u8 pad1[24]; + u8 psw[8]; + u32 pref_reg; + u8 pad2[20]; + u32 acc_regs[16]; + u64 fp_regs[4]; + u32 gp_regs[16]; + u32 ctrl_regs[16]; +} __attribute__((packed)); + +struct save_area_s390x { + u64 fp_regs[16]; + u64 gp_regs[16]; + u8 psw[16]; + u8 pad1[8]; + u32 pref_reg; + u32 fp_ctrl_reg; + u8 pad2[4]; + u32 tod_reg; + u64 timer; + u64 clk_cmp; + u8 pad3[8]; + u32 acc_regs[16]; + u64 ctrl_regs[16]; +} __attribute__((packed)); + +union save_area { + struct save_area_s390 s390; + struct save_area_s390x s390x; +}; + +#define SAVE_AREA_BASE_S390 0xd4 +#define SAVE_AREA_BASE_S390X 0x1200 + +#ifndef __s390x__ +#define SAVE_AREA_SIZE sizeof(struct save_area_s390) +#define SAVE_AREA_BASE SAVE_AREA_BASE_S390 +#else +#define SAVE_AREA_SIZE sizeof(struct save_area_s390x) +#define SAVE_AREA_BASE SAVE_AREA_BASE_S390X +#endif + struct _lowcore { #ifndef __s390x__ diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index 13c1654..8fe8d42 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -753,14 +753,14 @@ #define ptep_set_access_flags(__vma, __a * should therefore only be called if it is not mapped in any * address space. */ -static inline int page_test_and_clear_dirty(struct page *page) +static inline int page_test_dirty(struct page *page) { - unsigned long physpage = page_to_phys(page); - int skey = page_get_storage_key(physpage); + return (page_get_storage_key(page_to_phys(page)) & _PAGE_CHANGED) != 0; +} - if (skey & _PAGE_CHANGED) - page_set_storage_key(physpage, skey & ~_PAGE_CHANGED); - return skey & _PAGE_CHANGED; +static inline void page_clear_dirty(struct page *page) +{ + page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY); } /* @@ -953,7 +953,8 @@ #define __HAVE_ARCH_PTEP_GET_AND_CLEAR #define __HAVE_ARCH_PTEP_CLEAR_FLUSH #define __HAVE_ARCH_PTEP_SET_WRPROTECT #define __HAVE_ARCH_PTE_SAME -#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY +#define __HAVE_ARCH_PAGE_TEST_DIRTY +#define __HAVE_ARCH_PAGE_CLEAR_DIRTY #define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG #include diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index 33b80ce..e0fcea8 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -57,6 +57,7 @@ #endif /* __s390x__ */ extern void s390_adjust_jiffies(void); extern void print_cpu_info(struct cpuinfo_S390 *); +extern int get_cpu_capability(unsigned int *); /* Lazy FPU handling on uni-processor */ extern struct task_struct *last_task_used_math; @@ -196,6 +197,7 @@ extern unsigned long thread_saved_pc(str extern char *task_show_regs(struct task_struct *task, char *buffer); extern void show_registers(struct pt_regs *regs); +extern void show_code(struct pt_regs *regs); extern void show_trace(struct task_struct *task, unsigned long *sp); unsigned long get_wchan(struct task_struct *p); diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index 468b970..21ed647 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h @@ -9,6 +9,7 @@ #ifndef _ASM_S390_SCLP_H #define _ASM_S390_SCLP_H #include +#include struct sccb_header { u16 length; @@ -33,7 +34,20 @@ struct sclp_readinfo_sccb { u8 _reserved3[4096 - 112]; /* 112-4095 */ } __attribute__((packed, aligned(4096))); +#define SCLP_CHP_INFO_MASK_SIZE 32 + +struct sclp_chp_info { + u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; + u8 standby[SCLP_CHP_INFO_MASK_SIZE]; + u8 configured[SCLP_CHP_INFO_MASK_SIZE]; +}; + extern struct sclp_readinfo_sccb s390_readinfo_sccb; extern void sclp_readinfo_early(void); +extern int sclp_sdias_blk_count(void); +extern int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); +extern int sclp_chp_configure(struct chp_id chpid); +extern int sclp_chp_deconfigure(struct chp_id chpid); +extern int sclp_chp_read_info(struct sclp_chp_info *info); #endif /* _ASM_S390_SCLP_H */ diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h index 44c7aee..a76a6b8 100644 --- a/include/asm-s390/setup.h +++ b/include/asm-s390/setup.h @@ -40,6 +40,7 @@ struct mem_chunk { }; extern struct mem_chunk memory_chunk[]; +extern unsigned long real_memory_size; #ifdef CONFIG_S390_SWITCH_AMODE extern unsigned int switch_amode; @@ -77,6 +78,7 @@ #define MACHINE_HAS_MVCOS (machine_flags #endif /* __s390x__ */ #define MACHINE_HAS_SCLP (!MACHINE_IS_P390) +#define ZFCPDUMP_HSA_SIZE (32UL<<20) /* * Console mode. Override with conmode= diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index b957e4c..0a28e6d 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -54,9 +54,6 @@ #define PROC_CHANGE_PENALTY 20 /* Sched #define raw_smp_processor_id() (S390_lowcore.cpu_data.cpu_nr) -extern int smp_get_cpu(cpumask_t cpu_map); -extern void smp_put_cpu(int cpu); - static inline __u16 hard_smp_processor_id(void) { __u16 cpu_address; @@ -114,9 +111,8 @@ static inline void smp_send_stop(void) } #define smp_cpu_not_running(cpu) 1 -#define smp_get_cpu(cpu) ({ 0; }) -#define smp_put_cpu(cpu) ({ 0; }) #define smp_setup_cpu_possible_map() do { } while (0) #endif +extern union save_area *zfcpdump_save_areas[NR_CPUS + 1]; #endif diff --git a/include/asm-s390/socket.h b/include/asm-s390/socket.h index 1778a49..1161ebe 100644 --- a/include/asm-s390/socket.h +++ b/include/asm-s390/socket.h @@ -57,5 +57,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-s390/sockios.h b/include/asm-s390/sockios.h index 412aeb4..f4fc16c 100644 --- a/include/asm-s390/sockios.h +++ b/include/asm-s390/sockios.h @@ -15,6 +15,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif diff --git a/include/asm-sh/socket.h b/include/asm-sh/socket.h index ca70362..c48d6fc 100644 --- a/include/asm-sh/socket.h +++ b/include/asm-sh/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* __ASM_SH_SOCKET_H */ diff --git a/include/asm-sh/sockios.h b/include/asm-sh/sockios.h index 08a71df..cf8b96b 100644 --- a/include/asm-sh/sockios.h +++ b/include/asm-sh/sockios.h @@ -9,5 +9,6 @@ #define SIOCATMARK _IOR('s', 7, int) #define SIOCSPGRP _IOW('s', 8, pid_t) #define SIOCGPGRP _IOR('s', 9, pid_t) -#define SIOCGSTAMP _IOR('s', 100, struct timeval) /* Get stamp - linux-specific */ +#define SIOCGSTAMP _IOR('s', 100, struct timeval) /* Get stamp (timeval) */ +#define SIOCGSTAMPNS _IOR('s', 101, struct timespec) /* Get stamp (timespec) */ #endif /* __ASM_SH_SOCKIOS_H */ diff --git a/include/asm-sh/string.h b/include/asm-sh/string.h index 95bc7db..55f8db6 100644 --- a/include/asm-sh/string.h +++ b/include/asm-sh/string.h @@ -126,9 +126,6 @@ extern void *memchr(const void *__s, int #define __HAVE_ARCH_STRLEN extern size_t strlen(const char *); -/* arch/sh/lib/strcasecmp.c */ -extern int strcasecmp(const char *, const char *); - #endif /* __KERNEL__ */ #endif /* __ASM_SH_STRING_H */ diff --git a/include/asm-sh64/sockios.h b/include/asm-sh64/sockios.h index 1ae23ae..419e76f 100644 --- a/include/asm-sh64/sockios.h +++ b/include/asm-sh64/sockios.h @@ -20,5 +20,6 @@ #define SIOCATMARK _IOR('s', 7, int) #define SIOCSPGRP _IOW('s', 8, pid_t) #define SIOCGPGRP _IOR('s', 9, pid_t) -#define SIOCGSTAMP _IOR('s', 100, struct timeval) /* Get stamp - linux-specific */ +#define SIOCGSTAMP _IOR('s', 100, struct timeval) /* Get stamp (timeval) */ +#define SIOCGSTAMPNS _IOR('s', 101, struct timespec) /* Get stamp (timespec) */ #endif /* __ASM_SH64_SOCKIOS_H */ diff --git a/include/asm-sparc/prom.h b/include/asm-sparc/prom.h index 274868d..9ea105e 100644 --- a/include/asm-sparc/prom.h +++ b/include/asm-sparc/prom.h @@ -35,8 +35,8 @@ struct property { }; struct device_node { - char *name; - char *type; + const char *name; + const char *type; phandle node; char *path_component_name; char *full_name; @@ -85,12 +85,14 @@ extern struct device_node *of_find_node_ extern struct device_node *of_get_parent(const struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); -extern struct property *of_find_property(struct device_node *np, +extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); -extern int of_device_is_compatible(struct device_node *device, const char *); -extern void *of_get_property(struct device_node *node, const char *name, - int *lenp); +extern int of_device_is_compatible(const struct device_node *device, + const char *); +extern const void *of_get_property(const struct device_node *node, + const char *name, + int *lenp); #define get_property(node,name,lenp) of_get_property(node,name,lenp) extern int of_set_property(struct device_node *node, const char *name, void *val, int len); extern int of_getintprop_default(struct device_node *np, diff --git a/include/asm-sparc/socket.h b/include/asm-sparc/socket.h index f6c4e5b..7c14239 100644 --- a/include/asm-sparc/socket.h +++ b/include/asm-sparc/socket.h @@ -49,6 +49,8 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_PEERSEC 0x001e #define SO_PASSSEC 0x001f +#define SO_TIMESTAMPNS 0x0021 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 diff --git a/include/asm-sparc/sockios.h b/include/asm-sparc/sockios.h index 0c01b59..990ea74 100644 --- a/include/asm-sparc/sockios.h +++ b/include/asm-sparc/sockios.h @@ -7,7 +7,8 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* !(_ASM_SPARC_SOCKIOS_H) */ diff --git a/include/asm-sparc64/cpudata.h b/include/asm-sparc64/cpudata.h index f2cc941..e89922d 100644 --- a/include/asm-sparc64/cpudata.h +++ b/include/asm-sparc64/cpudata.h @@ -17,8 +17,8 @@ #include typedef struct { /* Dcache line 1 */ unsigned int __softirq_pending; /* must be 1st, see rtrap.S */ - unsigned int multiplier; - unsigned int counter; + unsigned int __pad0_1; + unsigned int __pad0_2; unsigned int __pad1; unsigned long clock_tick; /* %tick's per second */ unsigned long udelay_val; diff --git a/include/asm-sparc64/device.h b/include/asm-sparc64/device.h index d8f9872..d5a4559 100644 --- a/include/asm-sparc64/device.h +++ b/include/asm-sparc64/device.h @@ -3,5 +3,21 @@ * * This file is released under the GPLv2 */ -#include +#ifndef _ASM_SPARC64_DEVICE_H +#define _ASM_SPARC64_DEVICE_H +struct device_node; +struct of_device; + +struct dev_archdata { + void *iommu; + void *stc; + void *host_controller; + + struct device_node *prom_node; + struct of_device *op; + + unsigned int msi_num; +}; + +#endif /* _ASM_SPARC64_DEVICE_H */ diff --git a/include/asm-sparc64/ebus.h b/include/asm-sparc64/ebus.h index a4afe9d..9c1c6db 100644 --- a/include/asm-sparc64/ebus.h +++ b/include/asm-sparc64/ebus.h @@ -8,7 +8,6 @@ #ifndef __SPARC64_EBUS_H #define __SPARC64_EBUS_H -#include #include #include #include @@ -41,7 +40,6 @@ struct linux_ebus { struct of_device ofdev; struct linux_ebus *next; struct linux_ebus_device *devices; - struct pci_pbm_info *parent; struct pci_dev *self; int index; int is_rio; diff --git a/include/asm-sparc64/floppy.h b/include/asm-sparc64/floppy.h index 331013a..4aa0925 100644 --- a/include/asm-sparc64/floppy.h +++ b/include/asm-sparc64/floppy.h @@ -549,7 +549,7 @@ static int __init ebus_fdthree_p(struct if (!strcmp(edev->prom_node->name, "fdthree")) return 1; if (!strcmp(edev->prom_node->name, "floppy")) { - char *compat; + const char *compat; compat = of_get_property(edev->prom_node, "compatible", NULL); @@ -661,7 +661,7 @@ #ifdef CONFIG_PCI struct linux_ebus_device *edev = NULL; unsigned long config = 0; void __iomem *auxio_reg; - char *state_prop; + const char *state_prop; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { diff --git a/include/asm-sparc64/io.h b/include/asm-sparc64/io.h index 30b912d..ad595b6 100644 --- a/include/asm-sparc64/io.h +++ b/include/asm-sparc64/io.h @@ -24,14 +24,6 @@ extern unsigned long kern_base, kern_siz #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) #define BIO_VMERGE_BOUNDARY 8192 -/* Different PCI controllers we support have their PCI MEM space - * mapped to an either 2GB (Psycho) or 4GB (Sabre) aligned area, - * so need to chop off the top 33 or 32 bits. - */ -extern unsigned long pci_memspace_mask; - -#define bus_dvma_to_mem(__vaddr) ((__vaddr) & pci_memspace_mask) - static __inline__ u8 _inb(unsigned long addr) { u8 ret; diff --git a/include/asm-sparc64/iommu.h b/include/asm-sparc64/iommu.h index 0de7a3d..e199594 100644 --- a/include/asm-sparc64/iommu.h +++ b/include/asm-sparc64/iommu.h @@ -7,15 +7,50 @@ #ifndef _SPARC64_IOMMU_H #define _SPARC64_IOMMU_H /* The format of an iopte in the page tables. */ -#define IOPTE_VALID 0x8000000000000000UL /* IOPTE is valid */ -#define IOPTE_64K 0x2000000000000000UL /* IOPTE is for 64k page */ -#define IOPTE_STBUF 0x1000000000000000UL /* DVMA can use streaming buffer */ -#define IOPTE_INTRA 0x0800000000000000UL /* SBUS slot-->slot direct transfer*/ -#define IOPTE_CONTEXT 0x07ff800000000000UL /* Context number */ -#define IOPTE_PAGE 0x00007fffffffe000UL /* Physical page number (PA[42:13])*/ -#define IOPTE_CACHE 0x0000000000000010UL /* Cached (in UPA E-cache) */ -#define IOPTE_WRITE 0x0000000000000002UL /* Writeable */ +#define IOPTE_VALID 0x8000000000000000UL +#define IOPTE_64K 0x2000000000000000UL +#define IOPTE_STBUF 0x1000000000000000UL +#define IOPTE_INTRA 0x0800000000000000UL +#define IOPTE_CONTEXT 0x07ff800000000000UL +#define IOPTE_PAGE 0x00007fffffffe000UL +#define IOPTE_CACHE 0x0000000000000010UL +#define IOPTE_WRITE 0x0000000000000002UL #define IOMMU_NUM_CTXS 4096 +struct iommu_arena { + unsigned long *map; + unsigned int hint; + unsigned int limit; +}; + +struct iommu { + spinlock_t lock; + struct iommu_arena arena; + iopte_t *page_table; + u32 page_table_map_base; + unsigned long iommu_control; + unsigned long iommu_tsbbase; + unsigned long iommu_flush; + unsigned long iommu_ctxflush; + unsigned long write_complete_reg; + unsigned long dummy_page; + unsigned long dummy_page_pa; + unsigned long ctx_lowest_free; + DECLARE_BITMAP(ctx_bitmap, IOMMU_NUM_CTXS); + u32 dma_addr_mask; +}; + +struct strbuf { + int strbuf_enabled; + unsigned long strbuf_control; + unsigned long strbuf_pflush; + unsigned long strbuf_fsync; + unsigned long strbuf_ctxflush; + unsigned long strbuf_ctxmatch_base; + unsigned long strbuf_flushflag_pa; + volatile unsigned long *strbuf_flushflag; + volatile unsigned long __flushflag_buf[(64+(64-1)) / sizeof(long)]; +}; + #endif /* !(_SPARC_IOMMU_H) */ diff --git a/include/asm-sparc64/isa.h b/include/asm-sparc64/isa.h index d9728b9..ecd9290 100644 --- a/include/asm-sparc64/isa.h +++ b/include/asm-sparc64/isa.h @@ -7,7 +7,6 @@ #ifndef __SPARC64_ISA_H #define __SPARC64_ISA_H -#include #include #include #include @@ -29,7 +28,6 @@ struct sparc_isa_bridge { struct of_device ofdev; struct sparc_isa_bridge *next; struct sparc_isa_device *devices; - struct pci_pbm_info *parent; struct pci_dev *self; int index; struct device_node *prom_node; diff --git a/include/asm-sparc64/parport.h b/include/asm-sparc64/parport.h index 284dfd0..6340a52 100644 --- a/include/asm-sparc64/parport.h +++ b/include/asm-sparc64/parport.h @@ -103,7 +103,7 @@ static int ebus_ecpp_p(struct linux_ebus if (!strcmp(edev->prom_node->name, "ecpp")) return 1; if (!strcmp(edev->prom_node->name, "parallel")) { - char *compat; + const char *compat; compat = of_get_property(edev->prom_node, "compatible", NULL); diff --git a/include/asm-sparc64/pbm.h b/include/asm-sparc64/pbm.h index 7a246d8..c008cec 100644 --- a/include/asm-sparc64/pbm.h +++ b/include/asm-sparc64/pbm.h @@ -1,7 +1,6 @@ -/* $Id: pbm.h,v 1.27 2001/08/12 13:18:23 davem Exp $ - * pbm.h: UltraSparc PCI controller software state. +/* pbm.h: UltraSparc PCI controller software state. * - * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) + * Copyright (C) 1997, 1998, 1999, 2007 David S. Miller (davem@davemloft.net) */ #ifndef __SPARC64_PBM_H @@ -30,90 +29,7 @@ #include * PCI bus. */ -struct pci_controller_info; - -/* This contains the software state necessary to drive a PCI - * controller's IOMMU. - */ -struct pci_iommu_arena { - unsigned long *map; - unsigned int hint; - unsigned int limit; -}; - -struct pci_iommu { - /* This protects the controller's IOMMU and all - * streaming buffers underneath. - */ - spinlock_t lock; - - struct pci_iommu_arena arena; - - /* IOMMU page table, a linear array of ioptes. */ - iopte_t *page_table; /* The page table itself. */ - - /* Base PCI memory space address where IOMMU mappings - * begin. - */ - u32 page_table_map_base; - - /* IOMMU Controller Registers */ - unsigned long iommu_control; /* IOMMU control register */ - unsigned long iommu_tsbbase; /* IOMMU page table base register */ - unsigned long iommu_flush; /* IOMMU page flush register */ - unsigned long iommu_ctxflush; /* IOMMU context flush register */ - - /* This is a register in the PCI controller, which if - * read will have no side-effects but will guarantee - * completion of all previous writes into IOMMU/STC. - */ - unsigned long write_complete_reg; - - /* In order to deal with some buggy third-party PCI bridges that - * do wrong prefetching, we never mark valid mappings as invalid. - * Instead we point them at this dummy page. - */ - unsigned long dummy_page; - unsigned long dummy_page_pa; - - /* CTX allocation. */ - unsigned long ctx_lowest_free; - unsigned long ctx_bitmap[IOMMU_NUM_CTXS / (sizeof(unsigned long) * 8)]; - - /* Here a PCI controller driver describes the areas of - * PCI memory space where DMA to/from physical memory - * are addressed. Drivers interrogate the PCI layer - * if their device has addressing limitations. They - * do so via pci_dma_supported, and pass in a mask of - * DMA address bits their device can actually drive. - * - * The test for being usable is: - * (device_mask & dma_addr_mask) == dma_addr_mask - */ - u32 dma_addr_mask; -}; - -extern void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask); - -/* This describes a PCI bus module's streaming buffer. */ -struct pci_strbuf { - int strbuf_enabled; /* Present and using it? */ - - /* Streaming Buffer Control Registers */ - unsigned long strbuf_control; /* STC control register */ - unsigned long strbuf_pflush; /* STC page flush register */ - unsigned long strbuf_fsync; /* STC flush synchronization reg */ - unsigned long strbuf_ctxflush; /* STC context flush register */ - unsigned long strbuf_ctxmatch_base; /* STC context flush match reg */ - unsigned long strbuf_flushflag_pa; /* Physical address of flush flag */ - volatile unsigned long *strbuf_flushflag; /* The flush flag itself */ - - /* And this is the actual flush flag area. - * We allocate extra because the chips require - * a 64-byte aligned area. - */ - volatile unsigned long __flushflag_buf[(64 + (64 - 1)) / sizeof(long)]; -}; +extern void pci_iommu_table_init(struct iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask); #define PCI_STC_FLUSHFLAG_INIT(STC) \ (*((STC)->strbuf_flushflag) = 0UL) @@ -126,6 +42,8 @@ #define PCI_STC_FLUSHFLAG_SET(STC) \ #define PROM_PCIRNG_MAX 64 #define PROM_PCIIMAP_MAX 64 +struct pci_controller_info; + struct pci_pbm_info { /* PCI controller we sit under. */ struct pci_controller_info *parent; @@ -160,11 +78,6 @@ #define PBM_CHIP_TYPE_TOMATILLO 5 /* OBP specific information. */ struct device_node *prom_node; - struct linux_prom_pci_ranges *pbm_ranges; - int num_pbm_ranges; - struct linux_prom_pci_intmap *pbm_intmap; - int num_pbm_intmap; - struct linux_prom_pci_intmask *pbm_intmask; u64 ino_bitmap; /* PBM I/O and Memory space resources. */ @@ -197,13 +110,10 @@ #ifdef CONFIG_PCI_MSI #endif /* !(CONFIG_PCI_MSI) */ /* This PBM's streaming buffer. */ - struct pci_strbuf stc; + struct strbuf stc; /* IOMMU state, potentially shared by both PBM segments. */ - struct pci_iommu *iommu; - - /* PCI slot mapping. */ - unsigned int pci_first_slot; + struct iommu *iommu; /* Now things for the actual PCI bus probes. */ unsigned int pci_first_busno; @@ -220,17 +130,12 @@ struct pci_controller_info { */ int index; - /* Do the PBMs both exist in the same PCI domain? */ - int pbms_same_domain; - /* The PCI bus modules controlled by us. */ struct pci_pbm_info pbm_A; struct pci_pbm_info pbm_B; /* Operations which are controller specific. */ void (*scan_bus)(struct pci_controller_info *); - void (*base_address_update)(struct pci_dev *, int); - void (*resource_adjust)(struct pci_dev *, struct resource *, struct resource *); #ifdef CONFIG_PCI_MSI int (*setup_msi_irq)(unsigned int *virt_irq_p, struct pci_dev *pdev, @@ -244,27 +149,4 @@ #endif unsigned int pci_last_busno; }; -/* PCI devices which are not bridges have this placed in their pci_dev - * sysdata member. This makes OBP aware PCI device drivers easier to - * code. - */ -struct pcidev_cookie { - struct pci_pbm_info *pbm; - struct device_node *prom_node; - struct of_device *op; - struct linux_prom_pci_registers prom_regs[PROMREG_MAX]; - int num_prom_regs; - struct linux_prom_pci_registers prom_assignments[PROMREG_MAX]; - int num_prom_assignments; -#ifdef CONFIG_PCI_MSI - unsigned int msi_num; -#endif -}; - -/* Currently these are the same across all PCI controllers - * we support. Someday they may not be... - */ -#define PCI_IRQ_IGN 0x000007c0 /* Interrupt Group Number */ -#define PCI_IRQ_INO 0x0000003f /* Interrupt Number */ - #endif /* !(__SPARC64_PBM_H) */ diff --git a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h index b14a725..47cea16 100644 --- a/include/asm-sparc64/pci.h +++ b/include/asm-sparc64/pci.h @@ -54,7 +54,7 @@ struct pci_iommu_ops { void (*dma_sync_sg_for_cpu)(struct pci_dev *, struct scatterlist *, int, int); }; -extern struct pci_iommu_ops *pci_iommu_ops; +extern const struct pci_iommu_ops *pci_iommu_ops; /* Allocate and map kernel buffer using consistent mode DMA for a device. * hwdev should be valid struct pci_dev pointer for PCI devices. diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h index b12be7a..46705ef 100644 --- a/include/asm-sparc64/pgtable.h +++ b/include/asm-sparc64/pgtable.h @@ -737,20 +737,6 @@ #define pte_to_pgoff(pte) (pte_val(pte) extern pte_t pgoff_to_pte(unsigned long); #define PTE_FILE_MAX_BITS (64UL - PAGE_SHIFT - 1UL) -extern unsigned long prom_virt_to_phys(unsigned long, int *); - -extern unsigned long sun4u_get_pte(unsigned long); - -static inline unsigned long __get_phys(unsigned long addr) -{ - return sun4u_get_pte(addr); -} - -static inline int __get_iospace(unsigned long addr) -{ - return ((sun4u_get_pte(addr) & 0xf0000000) >> 28); -} - extern unsigned long *sparc64_valid_addr_bitmap; /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ @@ -791,6 +777,8 @@ extern void pgtable_cache_init(void); extern void sun4v_register_fault_status(void); extern void sun4v_ktsb_register(void); +extern unsigned long cmdline_memory_size; + #endif /* !(__ASSEMBLY__) */ #endif /* !(_SPARC64_PGTABLE_H) */ diff --git a/include/asm-sparc64/prom.h b/include/asm-sparc64/prom.h index 0eca2d9..ddad5f9 100644 --- a/include/asm-sparc64/prom.h +++ b/include/asm-sparc64/prom.h @@ -36,8 +36,8 @@ struct property { struct of_irq_controller; struct device_node { - char *name; - char *type; + const char *name; + const char *type; phandle node; char *path_component_name; char *full_name; @@ -93,11 +93,13 @@ extern struct device_node *of_find_node_ extern struct device_node *of_get_parent(const struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); -extern struct property *of_find_property(struct device_node *np, +extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); -extern int of_device_is_compatible(struct device_node *device, const char *); -extern void *of_get_property(struct device_node *node, const char *name, +extern int of_device_is_compatible(const struct device_node *device, + const char *); +extern const void *of_get_property(const struct device_node *node, + const char *name, int *lenp); #define get_property(node,name,lenp) of_get_property(node,name,lenp) extern int of_set_property(struct device_node *node, const char *name, void *val, int len); diff --git a/include/asm-sparc64/smp.h b/include/asm-sparc64/smp.h index 388249b..cca5480 100644 --- a/include/asm-sparc64/smp.h +++ b/include/asm-sparc64/smp.h @@ -42,15 +42,15 @@ extern int hard_smp_processor_id(void); #define raw_smp_processor_id() (current_thread_info()->cpu) extern void smp_setup_cpu_possible_map(void); +extern unsigned char boot_cpu_id; #endif /* !(__ASSEMBLY__) */ #else #define smp_setup_cpu_possible_map() do { } while (0) +#define boot_cpu_id (0) #endif /* !(CONFIG_SMP) */ -#define NO_PROC_ID 0xFF - #endif /* !(_SPARC64_SMP_H) */ diff --git a/include/asm-sparc64/socket.h b/include/asm-sparc64/socket.h index 754d46a..986441d 100644 --- a/include/asm-sparc64/socket.h +++ b/include/asm-sparc64/socket.h @@ -49,6 +49,8 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_PEERSEC 0x001e #define SO_PASSSEC 0x001f +#define SO_TIMESTAMPNS 0x0021 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 diff --git a/include/asm-sparc64/sockios.h b/include/asm-sparc64/sockios.h index 6735bab..c7d9900 100644 --- a/include/asm-sparc64/sockios.h +++ b/include/asm-sparc64/sockios.h @@ -7,7 +7,8 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* !(_ASM_SPARC64_SOCKIOS_H) */ diff --git a/include/asm-sparc64/sparsemem.h b/include/asm-sparc64/sparsemem.h index ed5c9d8..77bcd2b 100644 --- a/include/asm-sparc64/sparsemem.h +++ b/include/asm-sparc64/sparsemem.h @@ -3,7 +3,7 @@ #define _SPARC64_SPARSEMEM_H #ifdef __KERNEL__ -#define SECTION_SIZE_BITS 26 +#define SECTION_SIZE_BITS 31 #define MAX_PHYSADDR_BITS 42 #define MAX_PHYSMEM_BITS 42 diff --git a/include/asm-sparc64/timer.h b/include/asm-sparc64/timer.h index d435594..ccbd694 100644 --- a/include/asm-sparc64/timer.h +++ b/include/asm-sparc64/timer.h @@ -11,22 +11,19 @@ #include struct sparc64_tick_ops { - void (*init_tick)(unsigned long); unsigned long (*get_tick)(void); - unsigned long (*get_compare)(void); - unsigned long (*add_tick)(unsigned long, unsigned long); - unsigned long (*add_compare)(unsigned long); + int (*add_compare)(unsigned long); unsigned long softint_mask; + void (*disable_irq)(void); + + void (*init_tick)(void); + unsigned long (*add_tick)(unsigned long); + + char *name; }; extern struct sparc64_tick_ops *tick_ops; -#ifdef CONFIG_SMP -extern unsigned long timer_tick_offset; -struct pt_regs; -extern void timer_tick_interrupt(struct pt_regs *); -#endif - extern unsigned long sparc64_get_clock_tick(unsigned int cpu); #endif /* _SPARC64_TIMER_H */ diff --git a/include/asm-sparc64/ttable.h b/include/asm-sparc64/ttable.h index c2a16e1..bbb9c8f 100644 --- a/include/asm-sparc64/ttable.h +++ b/include/asm-sparc64/ttable.h @@ -157,23 +157,6 @@ #define TRAP_IRQ(routine, level) \ ba,a,pt %xcc, rtrap_irq; \ .previous; -#define TICK_SMP_IRQ \ - rdpr %pil, %g2; \ - wrpr %g0, 15, %pil; \ - sethi %hi(1f-4), %g7; \ - ba,pt %xcc, etrap_irq; \ - or %g7, %lo(1f-4), %g7; \ - nop; \ - nop; \ - nop; \ - .subsection 2; \ -1: call trace_hardirqs_off; \ - nop; \ - call smp_percpu_timer_interrupt; \ - add %sp, PTREGS_OFF, %o0; \ - ba,a,pt %xcc, rtrap_irq; \ - .previous; - #else #define TRAP_IRQ(routine, level) \ @@ -186,16 +169,6 @@ #define TRAP_IRQ(routine, level) \ add %sp, PTREGS_OFF, %o1; \ ba,a,pt %xcc, rtrap_irq; -#define TICK_SMP_IRQ \ - rdpr %pil, %g2; \ - wrpr %g0, 15, %pil; \ - sethi %hi(109f), %g7; \ - ba,pt %xcc, etrap_irq; \ -109: or %g7, %lo(109b), %g7; \ - call smp_percpu_timer_interrupt; \ - add %sp, PTREGS_OFF, %o0; \ - ba,a,pt %xcc, rtrap_irq; - #endif #define TRAP_IVEC TRAP_NOSAVE(do_ivec) diff --git a/include/asm-um/div64.h b/include/asm-um/div64.h index 1e17f74..7b73b2c 100644 --- a/include/asm-um/div64.h +++ b/include/asm-um/div64.h @@ -3,4 +3,5 @@ #define _UM_DIV64_H #include "asm/arch/div64.h" +extern uint64_t div64_64(uint64_t dividend, uint64_t divisor); #endif diff --git a/include/asm-v850/socket.h b/include/asm-v850/socket.h index 0dfe55a..a4c2493 100644 --- a/include/asm-v850/socket.h +++ b/include/asm-v850/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* __V850_SOCKET_H__ */ diff --git a/include/asm-v850/sockios.h b/include/asm-v850/sockios.h index cf4874c..823e106 100644 --- a/include/asm-v850/sockios.h +++ b/include/asm-v850/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* __V850_SOCKIOS_H__ */ diff --git a/include/asm-x86_64/socket.h b/include/asm-x86_64/socket.h index b467026..90af60c 100644 --- a/include/asm-x86_64/socket.h +++ b/include/asm-x86_64/socket.h @@ -49,5 +49,7 @@ #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-x86_64/sockios.h b/include/asm-x86_64/sockios.h index 2eefd10..d726ba2 100644 --- a/include/asm-x86_64/sockios.h +++ b/include/asm-x86_64/sockios.h @@ -7,6 +7,7 @@ #define SIOCSPGRP 0x8902 #define FIOGETOWN 0x8903 #define SIOCGPGRP 0x8904 #define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif diff --git a/include/asm-xtensa/div64.h b/include/asm-xtensa/div64.h index c4a1057..20965e3 100644 --- a/include/asm-xtensa/div64.h +++ b/include/asm-xtensa/div64.h @@ -11,9 +11,15 @@ #ifndef _XTENSA_DIV64_H #define _XTENSA_DIV64_H +#include + #define do_div(n,base) ({ \ int __res = n % ((unsigned int) base); \ n /= (unsigned int) base; \ __res; }) +static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor) +{ + return dividend / divisor; +} #endif diff --git a/include/asm-xtensa/socket.h b/include/asm-xtensa/socket.h index 971d231..1f5aeac 100644 --- a/include/asm-xtensa/socket.h +++ b/include/asm-xtensa/socket.h @@ -60,5 +60,7 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 #define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS #endif /* _XTENSA_SOCKET_H */ diff --git a/include/asm-xtensa/sockios.h b/include/asm-xtensa/sockios.h index 20d2ba1..efe0af3 100644 --- a/include/asm-xtensa/sockios.h +++ b/include/asm-xtensa/sockios.h @@ -25,6 +25,7 @@ #define SIOCATMARK _IOR('s', 7, int) #define SIOCSPGRP _IOW('s', 8, pid_t) #define SIOCGPGRP _IOR('s', 9, pid_t) -#define SIOCGSTAMP 0x8906 /* Get stamp - linux-specific */ +#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ #endif /* _XTENSA_SOCKIOS_H */ diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h new file mode 100644 index 0000000..e2ee73a --- /dev/null +++ b/include/keys/rxrpc-type.h @@ -0,0 +1,22 @@ +/* RxRPC key type + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef _KEYS_RXRPC_TYPE_H +#define _KEYS_RXRPC_TYPE_H + +#include + +/* + * key type for AF_RXRPC keys + */ +extern struct key_type key_type_rxrpc; + +#endif /* _KEYS_USER_TYPE_H */ diff --git a/include/linux/Kbuild b/include/linux/Kbuild index e81e301..4ff0f57 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -69,9 +69,7 @@ header-y += hdsmart.h header-y += hysdn_if.h header-y += i2c-dev.h header-y += i8k.h -header-y += icmp.h header-y += if_arcnet.h -header-y += if_arp.h header-y += if_bonding.h header-y += if_cablemodem.h header-y += if_fc.h @@ -88,7 +86,6 @@ header-y += if_tunnel.h header-y += in6.h header-y += in_route.h header-y += ioctl.h -header-y += ip.h header-y += ipmi_msgdefs.h header-y += ip_mp_alg.h header-y += ipsec.h @@ -116,6 +113,7 @@ header-y += netrom.h header-y += nfs2.h header-y += nfs4_mount.h header-y += nfs_mount.h +header-y += nl80211.h header-y += oom.h header-y += param.h header-y += pci_regs.h @@ -210,8 +208,10 @@ unifdef-y += hiddev.h unifdef-y += hpet.h unifdef-y += i2c.h unifdef-y += i2o-dev.h +unifdef-y += icmp.h unifdef-y += icmpv6.h unifdef-y += if_addr.h +unifdef-y += if_arp.h unifdef-y += if_bridge.h unifdef-y += if_ec.h unifdef-y += if_eql.h @@ -231,6 +231,7 @@ unifdef-y += inet_diag.h unifdef-y += in.h unifdef-y += inotify.h unifdef-y += input.h +unifdef-y += ip.h unifdef-y += ipc.h unifdef-y += ipmi.h unifdef-y += ipv6.h diff --git a/include/linux/ata.h b/include/linux/ata.h index 6caeb98..edb31bf 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -159,11 +159,19 @@ enum { ATA_CMD_INIT_DEV_PARAMS = 0x91, ATA_CMD_READ_NATIVE_MAX = 0xF8, ATA_CMD_READ_NATIVE_MAX_EXT = 0x27, + ATA_CMD_SET_MAX = 0xF9, + ATA_CMD_SET_MAX_EXT = 0x37, ATA_CMD_READ_LOG_EXT = 0x2f, /* READ_LOG_EXT pages */ ATA_LOG_SATA_NCQ = 0x10, + /* READ/WRITE LONG (obsolete) */ + ATA_CMD_READ_LONG = 0x22, + ATA_CMD_READ_LONG_ONCE = 0x23, + ATA_CMD_WRITE_LONG = 0x32, + ATA_CMD_WRITE_LONG_ONCE = 0x33, + /* SETFEATURES stuff */ SETFEATURES_XFER = 0x03, XFER_UDMA_7 = 0x47, @@ -194,6 +202,8 @@ enum { SETFEATURES_WC_ON = 0x02, /* Enable write cache */ SETFEATURES_WC_OFF = 0x82, /* Disable write cache */ + SETFEATURES_SPINUP = 0x07, /* Spin-up drive */ + /* ATAPI stuff */ ATAPI_PKT_DMA = (1 << 0), ATAPI_DMADIR = (1 << 2), /* ATAPI data dir: diff --git a/include/linux/atalk.h b/include/linux/atalk.h index d12984d..ced8a1e 100644 --- a/include/linux/atalk.h +++ b/include/linux/atalk.h @@ -101,7 +101,7 @@ struct ddpehdr { static __inline__ struct ddpehdr *ddp_hdr(struct sk_buff *skb) { - return (struct ddpehdr *)skb->h.raw; + return (struct ddpehdr *)skb_transport_header(skb); } /* AppleTalk AARP headers */ @@ -129,7 +129,7 @@ #define AARP_PROBE 3 static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb) { - return (struct elapaarp *)skb->h.raw; + return (struct elapaarp *)skb_transport_header(skb); } /* Not specified - how long till we drop a resolved entry */ diff --git a/include/linux/bio.h b/include/linux/bio.h index 08daf32..4d85262 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -276,7 +276,7 @@ extern struct bio_pair *bio_split(struct extern mempool_t *bio_split_pool; extern void bio_pair_release(struct bio_pair *dbio); -extern struct bio_set *bioset_create(int, int, int); +extern struct bio_set *bioset_create(int, int); extern void bioset_free(struct bio_set *); extern struct bio *bio_alloc(gfp_t, int); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 83dcd8c..a686eab 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -116,6 +116,7 @@ struct io_context { struct as_io_context *aic; struct rb_root cic_root; + void *ioc_data; }; void put_io_context(struct io_context *ioc); diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 1cb054b..fda2148 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -260,19 +260,20 @@ enum { static inline struct dccp_hdr *dccp_hdr(const struct sk_buff *skb) { - return (struct dccp_hdr *)skb->h.raw; + return (struct dccp_hdr *)skb_transport_header(skb); } static inline struct dccp_hdr *dccp_zeroed_hdr(struct sk_buff *skb, int headlen) { - skb->h.raw = skb_push(skb, headlen); - memset(skb->h.raw, 0, headlen); - return dccp_hdr(skb); + skb_push(skb, headlen); + skb_reset_transport_header(skb); + return memset(skb_transport_header(skb), 0, headlen); } static inline struct dccp_hdr_ext *dccp_hdrx(const struct sk_buff *skb) { - return (struct dccp_hdr_ext *)(skb->h.raw + sizeof(struct dccp_hdr)); + return (struct dccp_hdr_ext *)(skb_transport_header(skb) + + sizeof(struct dccp_hdr)); } static inline unsigned int __dccp_basic_hdr_len(const struct dccp_hdr *dh) @@ -301,12 +302,14 @@ static inline __u64 dccp_hdr_seq(const s static inline struct dccp_hdr_request *dccp_hdr_request(struct sk_buff *skb) { - return (struct dccp_hdr_request *)(skb->h.raw + dccp_basic_hdr_len(skb)); + return (struct dccp_hdr_request *)(skb_transport_header(skb) + + dccp_basic_hdr_len(skb)); } static inline struct dccp_hdr_ack_bits *dccp_hdr_ack_bits(const struct sk_buff *skb) { - return (struct dccp_hdr_ack_bits *)(skb->h.raw + dccp_basic_hdr_len(skb)); + return (struct dccp_hdr_ack_bits *)(skb_transport_header(skb) + + dccp_basic_hdr_len(skb)); } static inline u64 dccp_hdr_ack_seq(const struct sk_buff *skb) @@ -317,12 +320,14 @@ static inline u64 dccp_hdr_ack_seq(const static inline struct dccp_hdr_response *dccp_hdr_response(struct sk_buff *skb) { - return (struct dccp_hdr_response *)(skb->h.raw + dccp_basic_hdr_len(skb)); + return (struct dccp_hdr_response *)(skb_transport_header(skb) + + dccp_basic_hdr_len(skb)); } static inline struct dccp_hdr_reset *dccp_hdr_reset(struct sk_buff *skb) { - return (struct dccp_hdr_reset *)(skb->h.raw + dccp_basic_hdr_len(skb)); + return (struct dccp_hdr_reset *)(skb_transport_header(skb) + + dccp_basic_hdr_len(skb)); } static inline unsigned int __dccp_hdr_len(const struct dccp_hdr *dh) @@ -460,26 +465,27 @@ struct dccp_ackvec; * @dccps_service_list - second .. last service code on passive socket * @dccps_timestamp_time - time of latest TIMESTAMP option * @dccps_timestamp_echo - latest timestamp received on a TIMESTAMP option - * @dccps_l_ack_ratio - - * @dccps_r_ack_ratio - + * @dccps_l_ack_ratio - feature-local Ack Ratio + * @dccps_r_ack_ratio - feature-remote Ack Ratio * @dccps_pcslen - sender partial checksum coverage (via sockopt) * @dccps_pcrlen - receiver partial checksum coverage (via sockopt) * @dccps_ndp_count - number of Non Data Packets since last data packet - * @dccps_mss_cache - - * @dccps_minisock - + * @dccps_mss_cache - current value of MSS (path MTU minus header sizes) + * @dccps_minisock - associated minisock (accessed via dccp_msk) * @dccps_hc_rx_ackvec - rx half connection ack vector - * @dccps_hc_rx_ccid - - * @dccps_hc_tx_ccid - - * @dccps_options_received - - * @dccps_epoch - - * @dccps_role - Role of this sock, one of %dccp_role - * @dccps_hc_rx_insert_options - - * @dccps_hc_tx_insert_options - + * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection) + * @dccps_hc_tx_ccid - CCID used for the sender (or sending half-connection) + * @dccps_options_received - parsed set of retrieved options + * @dccps_role - role of this sock, one of %dccp_role + * @dccps_hc_rx_insert_options - receiver wants to add options when acking + * @dccps_hc_tx_insert_options - sender wants to add options when sending * @dccps_xmit_timer - timer for when CCID is not ready to send + * @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs) */ struct dccp_sock { /* inet_connection_sock has to be the first member of dccp_sock */ struct inet_connection_sock dccps_inet_connection; +#define dccps_syn_rtt dccps_inet_connection.icsk_ack.lrcvtime __u64 dccps_swl; __u64 dccps_swh; __u64 dccps_awl; diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 9fa0983..5a9c495 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -44,6 +44,8 @@ struct dentry *debugfs_create_u16(const struct dentry *parent, u16 *value); struct dentry *debugfs_create_u32(const char *name, mode_t mode, struct dentry *parent, u32 *value); +struct dentry *debugfs_create_u64(const char *name, mode_t mode, + struct dentry *parent, u64 *value); struct dentry *debugfs_create_bool(const char *name, mode_t mode, struct dentry *parent, u32 *value); @@ -104,6 +106,13 @@ static inline struct dentry *debugfs_cre return ERR_PTR(-ENODEV); } +static inline struct dentry *debugfs_create_u64(const char *name, mode_t mode, + struct dentry *parent, + u64 *value) +{ + return ERR_PTR(-ENODEV); +} + static inline struct dentry *debugfs_create_bool(const char *name, mode_t mode, struct dentry *parent, u32 *value) diff --git a/include/linux/device.h b/include/linux/device.h index 5cf30e9..a0cd2ce 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -34,9 +34,24 @@ struct device; struct device_driver; struct class; struct class_device; +struct bus_type; + +struct bus_attribute { + struct attribute attr; + ssize_t (*show)(struct bus_type *, char * buf); + ssize_t (*store)(struct bus_type *, const char * buf, size_t count); +}; + +#define BUS_ATTR(_name,_mode,_show,_store) \ +struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store) + +extern int __must_check bus_create_file(struct bus_type *, + struct bus_attribute *); +extern void bus_remove_file(struct bus_type *, struct bus_attribute *); struct bus_type { const char * name; + struct module * owner; struct subsystem subsys; struct kset drivers; @@ -49,6 +64,8 @@ struct bus_type { struct bus_attribute * bus_attrs; struct device_attribute * dev_attrs; struct driver_attribute * drv_attrs; + struct bus_attribute drivers_autoprobe_attr; + struct bus_attribute drivers_probe_attr; int (*match)(struct device * dev, struct device_driver * drv); int (*uevent)(struct device *dev, char **envp, @@ -61,6 +78,9 @@ struct bus_type { int (*suspend_late)(struct device * dev, pm_message_t state); int (*resume_early)(struct device * dev); int (*resume)(struct device * dev); + + unsigned int drivers_autoprobe:1; + unsigned int multithread_probe:1; }; extern int __must_check bus_register(struct bus_type * bus); @@ -102,26 +122,10 @@ #define BUS_NOTIFY_BOUND_DRIVER 0x00000 #define BUS_NOTIFY_UNBIND_DRIVER 0x00000004 /* driver about to be unbound */ -/* sysfs interface for exporting bus attributes */ - -struct bus_attribute { - struct attribute attr; - ssize_t (*show)(struct bus_type *, char * buf); - ssize_t (*store)(struct bus_type *, const char * buf, size_t count); -}; - -#define BUS_ATTR(_name,_mode,_show,_store) \ -struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store) - -extern int __must_check bus_create_file(struct bus_type *, - struct bus_attribute *); -extern void bus_remove_file(struct bus_type *, struct bus_attribute *); - struct device_driver { const char * name; struct bus_type * bus; - struct completion unloaded; struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; @@ -135,8 +139,6 @@ struct device_driver { void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev, pm_message_t state); int (*resume) (struct device * dev); - - unsigned int multithread_probe:1; }; @@ -181,10 +183,9 @@ struct class { struct list_head children; struct list_head devices; struct list_head interfaces; + struct kset class_dirs; struct semaphore sem; /* locks both the children and interfaces lists */ - struct kobject *virtual_dir; - struct class_attribute * class_attrs; struct class_device_attribute * class_dev_attrs; struct device_attribute * dev_attrs; @@ -328,11 +329,23 @@ extern struct class_device *class_device __attribute__((format(printf,5,6))); extern void class_device_destroy(struct class *cls, dev_t devt); +/* + * The type of device, "struct device" is embedded in. A class + * or bus can contain devices of different types + * like "partitions" and "disks", "mouse" and "event". + * This identifies the device type and carries type-specific + * information, equivalent to the kobj_type of a kobject. + * If "name" is specified, the uevent will contain it in + * the DEVTYPE variable. + */ struct device_type { - struct device_attribute *attrs; + const char *name; + struct attribute_group **groups; int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); void (*release)(struct device *dev); + int (*suspend)(struct device * dev, pm_message_t state); + int (*resume)(struct device * dev); }; /* interface for exporting device attributes */ @@ -354,8 +367,12 @@ extern int __must_check device_create_bi struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, struct bin_attribute *attr); -extern int device_schedule_callback(struct device *dev, - void (*func)(struct device *)); +extern int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner); + +/* This is a macro to avoid include problems with THIS_MODULE */ +#define device_schedule_callback(dev, func) \ + device_schedule_callback_owner(dev, func, THIS_MODULE) /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); @@ -554,7 +571,11 @@ #ifdef DEBUG #define dev_dbg(dev, format, arg...) \ dev_printk(KERN_DEBUG , dev , format , ## arg) #else -#define dev_dbg(dev, format, arg...) do { (void)(dev); } while (0) +static inline int __attribute__ ((format (printf, 2, 3))) +dev_dbg(struct device * dev, const char * fmt, ...) +{ + return 0; +} #endif #define dev_err(dev, format, arg...) \ diff --git a/include/linux/dvb/audio.h b/include/linux/dvb/audio.h index 0874a67..89412e1 100644 --- a/include/linux/dvb/audio.h +++ b/include/linux/dvb/audio.h @@ -47,7 +47,9 @@ typedef enum { typedef enum { AUDIO_STEREO, AUDIO_MONO_LEFT, - AUDIO_MONO_RIGHT + AUDIO_MONO_RIGHT, + AUDIO_MONO, + AUDIO_STEREO_SWAPPED } audio_channel_select_t; @@ -133,5 +135,6 @@ #define AUDIO_SET_KARAOKE _IOW( * extracted by the PES parser. */ #define AUDIO_GET_PTS _IOR('o', 19, __u64) +#define AUDIO_BILINGUAL_CHANNEL_SELECT _IO('o', 20) #endif /* _DVBAUDIO_H_ */ diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h index 6183c9c..126e0c2 100644 --- a/include/linux/dvb/version.h +++ b/include/linux/dvb/version.h @@ -24,6 +24,6 @@ #ifndef _DVBVERSION_H_ #define _DVBVERSION_H_ #define DVB_API_VERSION 3 -#define DVB_API_VERSION_MINOR 1 +#define DVB_API_VERSION_MINOR 2 #endif /*_DVBVERSION_H_*/ diff --git a/include/linux/dvb/video.h b/include/linux/dvb/video.h index faebfda..93e4c3a 100644 --- a/include/linux/dvb/video.h +++ b/include/linux/dvb/video.h @@ -80,14 +80,70 @@ typedef enum { } video_play_state_t; +/* Decoder commands */ +#define VIDEO_CMD_PLAY (0) +#define VIDEO_CMD_STOP (1) +#define VIDEO_CMD_FREEZE (2) +#define VIDEO_CMD_CONTINUE (3) + +/* Flags for VIDEO_CMD_FREEZE */ +#define VIDEO_CMD_FREEZE_TO_BLACK (1 << 0) + +/* Flags for VIDEO_CMD_STOP */ +#define VIDEO_CMD_STOP_TO_BLACK (1 << 0) +#define VIDEO_CMD_STOP_IMMEDIATELY (1 << 1) + +/* Play input formats: */ +/* The decoder has no special format requirements */ +#define VIDEO_PLAY_FMT_NONE (0) +/* The decoder requires full GOPs */ +#define VIDEO_PLAY_FMT_GOP (1) + +/* The structure must be zeroed before use by the application + This ensures it can be extended safely in the future. */ +struct video_command { + __u32 cmd; + __u32 flags; + union { + struct { + __u64 pts; + } stop; + + struct { + /* 0 or 1000 specifies normal speed, + 1 specifies forward single stepping, + -1 specifies backward single stepping, + >1: playback at speed/1000 of the normal speed, + <-1: reverse playback at (-speed/1000) of the normal speed. */ + __s32 speed; + __u32 format; + } play; + + struct { + __u32 data[16]; + } raw; + }; +}; + +/* FIELD_UNKNOWN can be used if the hardware does not know whether + the Vsync is for an odd, even or progressive (i.e. non-interlaced) + field. */ +#define VIDEO_VSYNC_FIELD_UNKNOWN (0) +#define VIDEO_VSYNC_FIELD_ODD (1) +#define VIDEO_VSYNC_FIELD_EVEN (2) +#define VIDEO_VSYNC_FIELD_PROGRESSIVE (3) + struct video_event { int32_t type; #define VIDEO_EVENT_SIZE_CHANGED 1 #define VIDEO_EVENT_FRAME_RATE_CHANGED 2 +#define VIDEO_EVENT_DECODER_STOPPED 3 +#define VIDEO_EVENT_VSYNC 4 time_t timestamp; union { video_size_t size; unsigned int frame_rate; /* in frames per 1000sec */ + unsigned char vsync_field; /* unknown/odd/even/progressive */ } u; }; @@ -213,4 +269,10 @@ #define VIDEO_GET_FRAME_RATE _IOR( */ #define VIDEO_GET_PTS _IOR('o', 57, __u64) +/* Read the number of displayed frames since the decoder was started */ +#define VIDEO_GET_FRAME_COUNT _IOR('o', 58, __u64) + +#define VIDEO_COMMAND _IOWR('o', 59, struct video_command) +#define VIDEO_TRY_COMMAND _IOWR('o', 60, struct video_command) + #endif /*_DVBVIDEO_H_*/ diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 8270aac..87b606b 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -5,8 +5,13 @@ #include #include /* rule is permanent, and cannot be deleted */ -#define FIB_RULE_PERMANENT 1 -#define FIB_RULE_INVERT 2 +#define FIB_RULE_PERMANENT 0x00000001 +#define FIB_RULE_INVERT 0x00000002 +#define FIB_RULE_UNRESOLVED 0x00000004 +#define FIB_RULE_DEV_DETACHED 0x00000008 + +/* try to find source address in routing lookups */ +#define FIB_RULE_FIND_SADDR 0x00010000 struct fib_rule_hdr { @@ -29,7 +34,7 @@ enum FRA_DST, /* destination address */ FRA_SRC, /* source address */ FRA_IFNAME, /* interface name */ - FRA_UNUSED1, + FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */ FRA_UNUSED2, FRA_PRIORITY, /* priority/preference */ FRA_UNUSED3, @@ -51,8 +56,8 @@ enum { FR_ACT_UNSPEC, FR_ACT_TO_TBL, /* Pass to fixed table */ - FR_ACT_RES1, - FR_ACT_RES2, + FR_ACT_GOTO, /* Jump to another rule */ + FR_ACT_NOP, /* No operation */ FR_ACT_RES3, FR_ACT_RES4, FR_ACT_BLACKHOLE, /* Drop without notification */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 86ec3f4..095a9c9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -843,8 +843,13 @@ extern int fcntl_setlease(unsigned int f extern int fcntl_getlease(struct file *filp); /* fs/sync.c */ -extern int do_sync_file_range(struct file *file, loff_t offset, loff_t endbyte, - unsigned int flags); +extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset, + loff_t endbyte, unsigned int flags); +static inline int do_sync_file_range(struct file *file, loff_t offset, + loff_t endbyte, unsigned int flags) +{ + return do_sync_mapping_range(file->f_mapping, offset, endbyte, flags); +} /* fs/locks.c */ extern void locks_init_lock(struct file_lock *); diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index abb64c4..73710d6 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -120,44 +120,5 @@ struct fsl_spi_platform_data { u32 sysclk; }; -/* Ethernet interface (phy management and speed) -*/ -enum enet_interface { - ENET_10_MII, /* 10 Base T, MII interface */ - ENET_10_RMII, /* 10 Base T, RMII interface */ - ENET_10_RGMII, /* 10 Base T, RGMII interface */ - ENET_100_MII, /* 100 Base T, MII interface */ - ENET_100_RMII, /* 100 Base T, RMII interface */ - ENET_100_RGMII, /* 100 Base T, RGMII interface */ - ENET_1000_GMII, /* 1000 Base T, GMII interface */ - ENET_1000_RGMII, /* 1000 Base T, RGMII interface */ - ENET_1000_TBI, /* 1000 Base T, TBI interface */ - ENET_1000_RTBI /* 1000 Base T, RTBI interface */ -}; - -struct ucc_geth_platform_data { - /* device specific information */ - u32 device_flags; - u32 phy_reg_addr; - - /* board specific information */ - u32 board_flags; - u8 rx_clock; - u8 tx_clock; - u32 phy_id; - enum enet_interface phy_interface; - u32 phy_interrupt; - u8 mac_addr[6]; -}; - -/* Flags related to UCC Gigabit Ethernet device features */ -#define FSL_UGETH_DEV_HAS_GIGABIT 0x00000001 -#define FSL_UGETH_DEV_HAS_COALESCE 0x00000002 -#define FSL_UGETH_DEV_HAS_RMON 0x00000004 - -/* Flags in ucc_geth_platform_data */ -#define FSL_UGETH_BRD_HAS_PHY_INTR 0x00000001 - /* if not set use a timer */ - #endif /* _FSL_DEVICE_H_ */ #endif /* __KERNEL__ */ diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index d4b3339..db390c5 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -43,8 +43,7 @@ struct hdlc_proto { void (*stop)(struct net_device *dev); /* if open & !DCD */ void (*detach)(struct net_device *dev); int (*ioctl)(struct net_device *dev, struct ifreq *ifr); - unsigned short (*type_trans)(struct sk_buff *skb, - struct net_device *dev); + __be16 (*type_trans)(struct sk_buff *skb, struct net_device *dev); struct module *module; struct hdlc_proto *next; /* next protocol in the list */ }; @@ -132,8 +131,8 @@ static __inline__ __be16 hdlc_type_trans { hdlc_device *hdlc = dev_to_hdlc(dev); - skb->mac.raw = skb->data; - skb->dev = dev; + skb->dev = dev; + skb_reset_mac_header(skb); if (hdlc->proto->type_trans) return hdlc->proto->type_trans(skb, dev); diff --git a/include/linux/hid.h b/include/linux/hid.h index 8c97d4d..37076b1 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -247,6 +247,11 @@ #define HID_FEATURE_REPORT 2 * HID device quirks. */ +/* + * Increase this if you need to configure more HID quirks at module load time + */ +#define MAX_USBHID_BOOT_QUIRKS 4 + #define HID_QUIRK_INVERT 0x00000001 #define HID_QUIRK_NOTOUCH 0x00000002 #define HID_QUIRK_IGNORE 0x00000004 @@ -267,8 +272,9 @@ #define HID_QUIRK_BAD_RELATIVE_KEYS 0x0 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00020000 #define HID_QUIRK_IGNORE_MOUSE 0x00040000 #define HID_QUIRK_SONY_PS3_CONTROLLER 0x00080000 -#define HID_QUIRK_LOGITECH_S510_DESCRIPTOR 0x00100000 +#define HID_QUIRK_LOGITECH_DESCRIPTOR 0x00100000 #define HID_QUIRK_DUPLICATE_USAGES 0x00200000 +#define HID_QUIRK_RESET_LEDS 0x00400000 /* * This is the global environment of the parser. This information is @@ -494,6 +500,12 @@ void hid_output_report(struct hid_report void hid_free_device(struct hid_device *device); struct hid_device *hid_parse_report(__u8 *start, unsigned size); +/* HID quirks API */ +u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); +int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, const u32 quirks); +int usbhid_quirks_init(char **quirks_param); +void usbhid_quirks_exit(void); + #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); diff --git a/include/linux/icmp.h b/include/linux/icmp.h index 24da4fb..474f2a5 100644 --- a/include/linux/icmp.h +++ b/include/linux/icmp.h @@ -82,6 +82,15 @@ struct icmphdr { } un; }; +#ifdef __KERNEL__ +#include + +static inline struct icmphdr *icmp_hdr(const struct sk_buff *skb) +{ + return (struct icmphdr *)skb_transport_header(skb); +} +#endif + /* * constants for (set|get)sockopt */ diff --git a/include/linux/icmpv6.h b/include/linux/icmpv6.h index 68d3526..7c5e981 100644 --- a/include/linux/icmpv6.h +++ b/include/linux/icmpv6.h @@ -75,6 +75,15 @@ #define icmp6_rt_lifetime icmp6_dataun.u #define icmp6_router_pref icmp6_dataun.u_nd_ra.router_pref }; +#ifdef __KERNEL__ +#include + +static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb) +{ + return (struct icmp6hdr *)skb_transport_header(skb); +} +#endif + #define ICMPV6_ROUTER_PREF_LOW 0x3 #define ICMPV6_ROUTER_PREF_MEDIUM 0x0 #define ICMPV6_ROUTER_PREF_HIGH 0x1 diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h index d557e4c..43f3bed 100644 --- a/include/linux/if_addr.h +++ b/include/linux/if_addr.h @@ -39,6 +39,7 @@ #define IFA_F_SECONDARY 0x01 #define IFA_F_TEMPORARY IFA_F_SECONDARY #define IFA_F_NODAD 0x02 +#define IFA_F_OPTIMISTIC 0x04 #define IFA_F_HOMEADDRESS 0x10 #define IFA_F_DEPRECATED 0x20 #define IFA_F_TENTATIVE 0x40 diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index 7f57142..ed7b93c 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -148,4 +148,13 @@ #endif }; +#ifdef __KERNEL__ +#include + +static inline struct arphdr *arp_hdr(const struct sk_buff *skb) +{ + return (struct arphdr *)skb_network_header(skb); +} +#endif + #endif /* _LINUX_IF_ARP_H */ diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index fd1b6eb..4ff211d 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -105,7 +105,8 @@ #ifdef __KERNEL__ #include extern void brioctl_set(int (*ioctl_hook)(unsigned int, void __user *)); -extern int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb); +extern struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p, + struct sk_buff *skb); extern int (*br_should_route_hook)(struct sk_buff **pskb); #endif diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index ab08f35..1db774c 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -61,6 +61,7 @@ #define ETH_P_AARP 0x80F3 /* Appletalk #define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ #define ETH_P_IPX 0x8137 /* IPX over DIX */ #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ #define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ #define ETH_P_WCCP 0x883E /* Web-cache coordination protocol * defined in draft-wilson-wrec-wccp-v2-00.txt */ @@ -112,7 +113,7 @@ #include static inline struct ethhdr *eth_hdr(const struct sk_buff *skb) { - return (struct ethhdr *)skb->mac.raw; + return (struct ethhdr *)skb_mac_header(skb); } #ifdef CONFIG_SYSCTL diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 35ed3b5..604c243 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -126,6 +126,7 @@ enum IFLA_INET6_STATS, /* statistics */ IFLA_INET6_MCAST, /* MC things. What of them? */ IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ __IFLA_INET6_MAX }; diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index f3de05c..ad09609 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -42,6 +42,7 @@ #define PACKET_RX_RING 5 #define PACKET_STATISTICS 6 #define PACKET_COPY_THRESH 7 #define PACKET_AUXDATA 8 +#define PACKET_ORIGDEV 9 struct tpacket_stats { diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index e33ee76..6f987be 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -111,7 +111,17 @@ #endif struct pppoe_tag tag[0]; } __attribute__ ((packed)); +/* Length of entire PPPoE + PPP header */ +#define PPPOE_SES_HLEN 8 + #ifdef __KERNEL__ +#include + +static inline struct pppoe_hdr *pppoe_hdr(const struct sk_buff *skb) +{ + return (struct pppoe_hdr *)skb_network_header(skb); +} + struct pppoe_opt { struct net_device *dev; /* device associated with socket*/ int ifindex; /* ifindex of device associated with socket */ diff --git a/include/linux/if_tr.h b/include/linux/if_tr.h index 2f94cf2..046e9d9 100644 --- a/include/linux/if_tr.h +++ b/include/linux/if_tr.h @@ -47,7 +47,7 @@ #include static inline struct trh_hdr *tr_hdr(const struct sk_buff *skb) { - return (struct trh_hdr *)skb->mac.raw; + return (struct trh_hdr *)skb_mac_header(skb); } #ifdef CONFIG_SYSCTL extern struct ctl_table tr_table[]; diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index d103580..81e9bc9 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -51,7 +51,7 @@ #include static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb) { - return (struct vlan_ethhdr *)skb->mac.raw; + return (struct vlan_ethhdr *)skb_mac_header(skb); } struct vlan_hdr { @@ -275,8 +275,8 @@ static inline struct sk_buff *__vlan_put veth->h_vlan_TCI = htons(tag); skb->protocol = __constant_htons(ETH_P_8021Q); - skb->mac.raw -= VLAN_HLEN; - skb->nh.raw -= VLAN_HLEN; + skb->mac_header -= VLAN_HLEN; + skb->network_header -= VLAN_HLEN; return skb; } diff --git a/include/linux/if_wanpipe_common.h b/include/linux/if_wanpipe_common.h deleted file mode 100644 index 6e5461d..0000000 --- a/include/linux/if_wanpipe_common.h +++ /dev/null @@ -1,58 +0,0 @@ -/***************************************************************************** -* if_wanipe_common.h Sangoma Driver/Socket common area definitions. -* -* Author: Nenad Corbic -* -* Copyright: (c) 2000 Sangoma Technologies Inc. -* -* 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. -* ============================================================================ -* Jan 13, 2000 Nenad Corbic Initial version -*****************************************************************************/ - - -#ifndef _WANPIPE_SOCK_DRIVER_COMMON_H -#define _WANPIPE_SOCK_DRIVER_COMMON_H - -typedef struct { - struct net_device *slave; - atomic_t packet_sent; - atomic_t receive_block; - atomic_t command; - atomic_t disconnect; - atomic_t driver_busy; - long common_critical; - struct timer_list *tx_timer; - struct sock *sk; /* Wanpipe Sock bind's here */ - int (*func)(struct sk_buff *skb, struct net_device *dev, - struct sock *sk); - - struct work_struct wanpipe_work; /* deferred keventd work */ - unsigned char rw_bind; /* Sock bind state */ - unsigned char usedby; - unsigned char state; - unsigned char svc; - unsigned short lcn; - void *mbox; -} wanpipe_common_t; - - -enum { - WANSOCK_UNCONFIGURED, /* link/channel is not configured */ - WANSOCK_DISCONNECTED, /* link/channel is disconnected */ - WANSOCK_CONNECTING, /* connection is in progress */ - WANSOCK_CONNECTED, /* link/channel is operational */ - WANSOCK_LIMIT, /* for verification only */ - WANSOCK_DUALPORT, /* for Dual Port cards */ - WANSOCK_DISCONNECTING, - WANSOCK_BINDED, - WANSOCK_BIND_LISTEN, - WANSOCK_LISTEN -}; - -#endif - - diff --git a/include/linux/igmp.h b/include/linux/igmp.h index a113fe6..f510e7e 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -80,6 +80,27 @@ #endif __be32 srcs[0]; }; +#ifdef __KERNEL__ +#include + +static inline struct igmphdr *igmp_hdr(const struct sk_buff *skb) +{ + return (struct igmphdr *)skb_transport_header(skb); +} + +static inline struct igmpv3_report * + igmpv3_report_hdr(const struct sk_buff *skb) +{ + return (struct igmpv3_report *)skb_transport_header(skb); +} + +static inline struct igmpv3_query * + igmpv3_query_hdr(const struct sk_buff *skb) +{ + return (struct igmpv3_query *)skb_transport_header(skb); +} +#endif + #define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* From RFC1112 */ #define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Ditto */ #define IGMP_DVMRP 0x13 /* DVMRP routing */ diff --git a/include/linux/in.h b/include/linux/in.h index 1912e7c..3975cbf 100644 --- a/include/linux/in.h +++ b/include/linux/in.h @@ -83,6 +83,7 @@ #define IP_RECVRETOPTS IP_RETOPTS #define IP_PMTUDISC_DONT 0 /* Never send DF frames */ #define IP_PMTUDISC_WANT 1 /* Use per route hints */ #define IP_PMTUDISC_DO 2 /* Always DF */ +#define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu */ #define IP_MULTICAST_IF 32 #define IP_MULTICAST_TTL 33 diff --git a/include/linux/in6.h b/include/linux/in6.h index 4e8350a..2a61c82 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h @@ -44,10 +44,8 @@ #define s6_addr32 in6_u.u6_addr32 * NOTE: Be aware the IN6ADDR_* constants and in6addr_* externals are defined * in network byte order, not in host byte order as are the IPv4 equivalents */ -#if 0 extern const struct in6_addr in6addr_any; #define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } -#endif extern const struct in6_addr in6addr_loopback; #define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } @@ -179,6 +177,7 @@ #define IPV6_LEAVE_ANYCAST 28 #define IPV6_PMTUDISC_DONT 0 #define IPV6_PMTUDISC_WANT 1 #define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3 /* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32 diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 6859a3b..71ea923 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -99,7 +99,6 @@ extern struct resource ioport_resource; extern struct resource iomem_resource; extern int request_resource(struct resource *root, struct resource *new); -extern struct resource * ____request_resource(struct resource *root, struct resource *new); extern int release_resource(struct resource *new); extern int insert_resource(struct resource *parent, struct resource *new); extern int allocate_resource(struct resource *root, struct resource *new, diff --git a/include/linux/ip.h b/include/linux/ip.h index 1d36b97..bd0a2a8 100644 --- a/include/linux/ip.h +++ b/include/linux/ip.h @@ -104,6 +104,20 @@ #endif /*The options start here. */ }; +#ifdef __KERNEL__ +#include + +static inline struct iphdr *ip_hdr(const struct sk_buff *skb) +{ + return (struct iphdr *)skb_network_header(skb); +} + +static inline struct iphdr *ipip_hdr(const struct sk_buff *skb) +{ + return (struct iphdr *)skb_transport_header(skb); +} +#endif + struct ip_auth_hdr { __u8 nexthdr; __u8 hdrlen; /* This one is measured in 32 bit units! */ diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 713eb5e..09ea01a 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -178,6 +178,9 @@ #endif #endif __s32 proxy_ndp; __s32 accept_source_route; +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + __s32 optimistic_dad; +#endif void *sysctl; }; @@ -208,6 +211,7 @@ enum { DEVCONF_PROXY_NDP, __DEVCONF_OPTIMISTIC_DAD, DEVCONF_ACCEPT_SOURCE_ROUTE, + DEVCONF_OPTIMISTIC_DAD, DEVCONF_MAX }; @@ -219,6 +223,16 @@ #include #include /* struct ipv6_mc_socklist */ #include +static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb) +{ + return (struct ipv6hdr *)skb_network_header(skb); +} + +static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb) +{ + return (struct ipv6hdr *)skb_transport_header(skb); +} + /* This structure contains results of exthdrs parsing as offsets from skb->nh. diff --git a/include/linux/jhash.h b/include/linux/jhash.h index 82c7ae4..2a2f99f 100644 --- a/include/linux/jhash.h +++ b/include/linux/jhash.h @@ -84,7 +84,7 @@ static inline u32 jhash(const void *key, /* A special optimized version that handles 1 or more of u32s. * The length parameter here is the number of u32s in the key. */ -static inline u32 jhash2(u32 *k, u32 length, u32 initval) +static inline u32 jhash2(const u32 *k, u32 length, u32 initval) { u32 a, b, c, len; diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h index 1cebcbc..3e3b92d 100644 --- a/include/linux/kallsyms.h +++ b/include/linux/kallsyms.h @@ -7,6 +7,8 @@ #define _LINUX_KALLSYMS_H #define KSYM_NAME_LEN 127 +#define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN + \ + 2*(BITS_PER_LONG*3/10) + MODULE_NAME_LEN + 1) #ifdef CONFIG_KALLSYMS /* Lookup the address for a symbol. Returns 0 if not found. */ @@ -22,7 +24,10 @@ const char *kallsyms_lookup(unsigned lon unsigned long *offset, char **modname, char *namebuf); -/* Replace "%s" in format with address, if found */ +/* Look up a kernel symbol and return it in a text buffer. */ +extern int sprint_symbol(char *buffer, unsigned long address); + +/* Look up a kernel symbol and print it to the kernel messages. */ extern void __print_symbol(const char *fmt, unsigned long address); #else /* !CONFIG_KALLSYMS */ @@ -47,6 +52,12 @@ static inline const char *kallsyms_looku return NULL; } +static inline int sprint_symbol(char *buffer, unsigned long addr) +{ + *buffer = '\0'; + return 0; +} + /* Stupid that this does nothing, but I didn't create this mess. */ #define __print_symbol(fmt, addr) #endif /*CONFIG_KALLSYMS*/ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 9ddf25c..e2f41b0 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -121,6 +121,7 @@ extern int vscnprintf(char *buf, size_t __attribute__ ((format (printf, 3, 0))); extern char *kasprintf(gfp_t gfp, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +extern char *kvasprintf(gfp_t gfp, const char *fmt, va_list args); extern int sscanf(const char *, const char *, ...) __attribute__ ((format (scanf, 2, 3))); diff --git a/include/linux/key.h b/include/linux/key.h index 169f05e..a9220e7 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -160,6 +160,8 @@ #define KEY_FLAG_NEGATIVE 5 /* set if ke */ union { struct list_head link; + unsigned long x[2]; + void *p[2]; } type_data; /* key data diff --git a/include/linux/kobject.h b/include/linux/kobject.h index b850e03..eb0e63e 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -22,7 +22,6 @@ #include #include #include #include -#include #include #include #include @@ -43,11 +42,9 @@ enum kobject_action { KOBJ_ADD = (__force kobject_action_t) 0x01, /* exclusive to core */ KOBJ_REMOVE = (__force kobject_action_t) 0x02, /* exclusive to core */ KOBJ_CHANGE = (__force kobject_action_t) 0x03, /* device state change */ - KOBJ_MOUNT = (__force kobject_action_t) 0x04, /* mount event for block devices (broken) */ - KOBJ_UMOUNT = (__force kobject_action_t) 0x05, /* umount event for block devices (broken) */ - KOBJ_OFFLINE = (__force kobject_action_t) 0x06, /* device offline */ - KOBJ_ONLINE = (__force kobject_action_t) 0x07, /* device online */ - KOBJ_MOVE = (__force kobject_action_t) 0x08, /* device move */ + KOBJ_OFFLINE = (__force kobject_action_t) 0x04, /* device offline */ + KOBJ_ONLINE = (__force kobject_action_t) 0x05, /* device online */ + KOBJ_MOVE = (__force kobject_action_t) 0x06, /* device move */ }; struct kobject { @@ -89,6 +86,8 @@ extern void kobject_unregister(struct ko extern struct kobject * kobject_get(struct kobject *); extern void kobject_put(struct kobject *); +extern struct kobject *kobject_kset_add_dir(struct kset *kset, + struct kobject *, const char *); extern struct kobject *kobject_add_dir(struct kobject *, const char *); extern char * kobject_get_path(struct kobject *, gfp_t); @@ -175,7 +174,6 @@ #define set_kset_name(str) .kset = { .ko struct subsystem { struct kset kset; - struct rw_semaphore rwsem; }; #define decl_subsys(_name,_type,_uevent_ops) \ diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 248305b..81bb9c7 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -259,6 +259,12 @@ static inline s64 ktime_to_ns(const ktim #endif +static inline s64 ktime_to_us(const ktime_t kt) +{ + struct timeval tv = ktime_to_timeval(kt); + return (s64) tv.tv_sec * USEC_PER_SEC + tv.tv_usec; +} + /* * The resolution of the clocks. The resolution value is returned in * the clock_getres() system call to give application programmers an diff --git a/include/linux/libata.h b/include/linux/libata.h index 0cfbcb6..d8cfc72 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -210,6 +210,7 @@ enum { /* host set flags */ ATA_HOST_SIMPLEX = (1 << 0), /* Host is simplex, one DMA channel per host only */ + ATA_HOST_STARTED = (1 << 1), /* Host started */ /* various lengths of time */ ATA_TMOUT_BOOT = 30 * HZ, /* heuristic */ @@ -281,11 +282,13 @@ enum { ATA_EHI_NO_AUTOPSY = (1 << 2), /* no autopsy */ ATA_EHI_QUIET = (1 << 3), /* be quiet */ - ATA_EHI_DID_RESET = (1 << 16), /* already reset this port */ - ATA_EHI_PRINTINFO = (1 << 17), /* print configuration info */ - ATA_EHI_SETMODE = (1 << 18), /* configure transfer mode */ - ATA_EHI_POST_SETMODE = (1 << 19), /* revaildating after setmode */ + ATA_EHI_DID_SOFTRESET = (1 << 16), /* already soft-reset this port */ + ATA_EHI_DID_HARDRESET = (1 << 17), /* already soft-reset this port */ + ATA_EHI_PRINTINFO = (1 << 18), /* print configuration info */ + ATA_EHI_SETMODE = (1 << 19), /* configure transfer mode */ + ATA_EHI_POST_SETMODE = (1 << 20), /* revaildating after setmode */ + ATA_EHI_DID_RESET = ATA_EHI_DID_SOFTRESET | ATA_EHI_DID_HARDRESET, ATA_EHI_RESET_MODIFIER_MASK = ATA_EHI_RESUME_LINK, /* max repeat if error condition is still set after ->error_handler */ @@ -367,34 +370,6 @@ struct ata_ioports { void __iomem *scr_addr; }; -struct ata_probe_ent { - struct list_head node; - struct device *dev; - const struct ata_port_operations *port_ops; - struct scsi_host_template *sht; - struct ata_ioports port[ATA_MAX_PORTS]; - unsigned int n_ports; - unsigned int dummy_port_mask; - unsigned int pio_mask; - unsigned int mwdma_mask; - unsigned int udma_mask; - unsigned long irq; - unsigned long irq2; - unsigned int irq_flags; - unsigned long port_flags; - unsigned long _host_flags; - void __iomem * const *iomap; - void *private_data; - - /* port_info for the secondary port. Together with irq2, it's - * used to implement non-uniform secondary port. Currently, - * the only user is ata_piix combined mode. This workaround - * will be removed together with ata_probe_ent when init model - * is updated. - */ - const struct ata_port_info *pinfo2; -}; - struct ata_host { spinlock_t lock; struct device *dev; @@ -427,6 +402,7 @@ struct ata_queued_cmd { int dma_dir; unsigned int pad_len; + unsigned int sect_size; unsigned int nbytes; unsigned int curbytes; @@ -472,6 +448,7 @@ struct ata_device { struct scsi_device *sdev; /* attached SCSI device */ /* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */ u64 n_sectors; /* size of device, if ATA */ + u64 n_sectors_boot; /* size of ATA device at startup */ unsigned int class; /* ATA_DEV_xxx */ u16 id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */ u8 pio_mode; @@ -597,11 +574,11 @@ struct ata_port { struct ata_port_operations { void (*port_disable) (struct ata_port *); - void (*dev_config) (struct ata_port *, struct ata_device *); + void (*dev_config) (struct ata_device *); void (*set_piomode) (struct ata_port *, struct ata_device *); void (*set_dmamode) (struct ata_port *, struct ata_device *); - unsigned long (*mode_filter) (const struct ata_port *, struct ata_device *, unsigned long); + unsigned long (*mode_filter) (struct ata_device *, unsigned long); void (*tf_load) (struct ata_port *ap, const struct ata_taskfile *tf); void (*tf_read) (struct ata_port *ap, struct ata_taskfile *tf); @@ -616,6 +593,8 @@ struct ata_port_operations { void (*post_set_mode) (struct ata_port *ap); + int (*cable_detect) (struct ata_port *ap); + int (*check_atapi_dma) (struct ata_queued_cmd *qc); void (*bmdma_setup) (struct ata_queued_cmd *qc); @@ -664,6 +643,7 @@ struct ata_port_info { unsigned long mwdma_mask; unsigned long udma_mask; const struct ata_port_operations *port_ops; + irq_handler_t irq_handler; void *private_data; }; @@ -686,6 +666,7 @@ extern const unsigned long sata_deb_timi extern const unsigned long sata_deb_timing_long[]; extern const struct ata_port_operations ata_dummy_port_ops; +extern const struct ata_port_info ata_dummy_port_info; static inline const unsigned long * sata_ehc_deb_timing(struct ata_eh_context *ehc) @@ -701,6 +682,7 @@ static inline int ata_port_is_dummy(stru return ap->ops == &ata_dummy_port_ops; } +extern void sata_print_link_status(struct ata_port *ap); extern void ata_port_probe(struct ata_port *); extern void __sata_phy_reset(struct ata_port *ap); extern void sata_phy_reset(struct ata_port *ap); @@ -728,7 +710,15 @@ extern int ata_pci_device_resume(struct #endif extern int ata_pci_clear_simplex(struct pci_dev *pdev); #endif /* CONFIG_PCI */ -extern int ata_device_add(const struct ata_probe_ent *ent); +extern struct ata_host *ata_host_alloc(struct device *dev, int max_ports); +extern struct ata_host *ata_host_alloc_pinfo(struct device *dev, + const struct ata_port_info * const * ppi, int n_ports); +extern int ata_host_start(struct ata_host *host); +extern int ata_host_register(struct ata_host *host, + struct scsi_host_template *sht); +extern int ata_host_activate(struct ata_host *host, int irq, + irq_handler_t irq_handler, unsigned long irq_flags, + struct scsi_host_template *sht); extern void ata_host_detach(struct ata_host *host); extern void ata_host_init(struct ata_host *, struct device *, unsigned long, const struct ata_port_operations *); @@ -828,11 +818,17 @@ extern void ata_scsi_slave_destroy(struc extern int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth); extern struct ata_device *ata_dev_pair(struct ata_device *adev); +extern int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev); extern u8 ata_irq_on(struct ata_port *ap); extern u8 ata_dummy_irq_on(struct ata_port *ap); extern u8 ata_irq_ack(struct ata_port *ap, unsigned int chk_drq); extern u8 ata_dummy_irq_ack(struct ata_port *ap, unsigned int chk_drq); +extern int ata_cable_40wire(struct ata_port *ap); +extern int ata_cable_80wire(struct ata_port *ap); +extern int ata_cable_sata(struct ata_port *ap); +extern int ata_cable_unknown(struct ata_port *ap); + /* * Timing helpers */ @@ -870,10 +866,13 @@ struct pci_bits { unsigned long val; }; -extern struct ata_probe_ent * -ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int portmask); +extern int ata_pci_init_native_host(struct ata_host *host, + unsigned int port_mask); +extern int ata_pci_prepare_native_host(struct pci_dev *pdev, + const struct ata_port_info * const * ppi, + int n_ports, struct ata_host **r_host); extern int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits); -extern unsigned long ata_pci_default_filter(const struct ata_port *, struct ata_device *, unsigned long); +extern unsigned long ata_pci_default_filter(struct ata_device *, unsigned long); #endif /* CONFIG_PCI */ /* @@ -1173,6 +1172,7 @@ static inline void ata_qc_reinit(struct qc->n_elem = 0; qc->err_mask = 0; qc->pad_len = 0; + qc->sect_size = ATA_SECT_SIZE; ata_tf_init(qc->dev, &qc->tf); @@ -1220,7 +1220,7 @@ static inline void ata_pad_free(struct a static inline struct ata_port *ata_shost_to_port(struct Scsi_Host *host) { - return (struct ata_port *) &host->hostdata[0]; + return *(struct ata_port **)&host->hostdata[0]; } #endif /* __LINUX_LIBATA_H__ */ diff --git a/include/linux/mtd/iflash.h b/include/linux/mtd/iflash.h deleted file mode 100644 index 9aa5b4f..0000000 --- a/include/linux/mtd/iflash.h +++ /dev/null @@ -1,98 +0,0 @@ -/* $Id: iflash.h,v 1.2 2000/11/13 18:01:54 dwmw2 Exp $ */ - -#ifndef __MTD_IFLASH_H__ -#define __MTD_IFLASH_H__ - -/* Extended CIS registers for Series 2 and 2+ cards */ -/* The registers are all offsets from 0x4000 */ -#define CISREG_CSR 0x0100 -#define CISREG_WP 0x0104 -#define CISREG_RDYBSY 0x0140 - -/* Extended CIS registers for Series 2 cards */ -#define CISREG_SLEEP 0x0118 -#define CISREG_RDY_MASK 0x0120 -#define CISREG_RDY_STATUS 0x0130 - -/* Extended CIS registers for Series 2+ cards */ -#define CISREG_VCR 0x010c - -/* Card Status Register */ -#define CSR_SRESET 0x20 /* Soft reset */ -#define CSR_CMWP 0x10 /* Common memory write protect */ -#define CSR_PWRDOWN 0x08 /* Power down status */ -#define CSR_CISWP 0x04 /* Common memory CIS WP */ -#define CSR_WP 0x02 /* Mechanical write protect */ -#define CSR_READY 0x01 /* Ready/busy status */ - -/* Write Protection Register */ -#define WP_BLKEN 0x04 /* Enable block locking */ -#define WP_CMWP 0x02 /* Common memory write protect */ -#define WP_CISWP 0x01 /* Common memory CIS WP */ - -/* Voltage Control Register */ -#define VCR_VCC_LEVEL 0x80 /* 0 = 5V, 1 = 3.3V */ -#define VCR_VPP_VALID 0x02 /* Vpp Valid */ -#define VCR_VPP_GEN 0x01 /* Integrated Vpp generator */ - -/* Ready/Busy Mode Register */ -#define RDYBSY_RACK 0x02 /* Ready acknowledge */ -#define RDYBSY_MODE 0x01 /* 1 = high performance */ - -#define LOW(x) ((x) & 0xff) - -/* 28F008SA-Compatible Command Set */ -#define IF_READ_ARRAY 0xffff -#define IF_INTEL_ID 0x9090 -#define IF_READ_CSR 0x7070 -#define IF_CLEAR_CSR 0x5050 -#define IF_WRITE 0x4040 -#define IF_BLOCK_ERASE 0x2020 -#define IF_ERASE_SUSPEND 0xb0b0 -#define IF_CONFIRM 0xd0d0 - -/* 28F016SA Performance Enhancement Commands */ -#define IF_READ_PAGE 0x7575 -#define IF_PAGE_SWAP 0x7272 -#define IF_SINGLE_LOAD 0x7474 -#define IF_SEQ_LOAD 0xe0e0 -#define IF_PAGE_WRITE 0x0c0c -#define IF_RDY_MODE 0x9696 -#define IF_RDY_LEVEL 0x0101 -#define IF_RDY_PULSE_WRITE 0x0202 -#define IF_RDY_PULSE_ERASE 0x0303 -#define IF_RDY_DISABLE 0x0404 -#define IF_LOCK_BLOCK 0x7777 -#define IF_UPLOAD_STATUS 0x9797 -#define IF_READ_ESR 0x7171 -#define IF_ERASE_UNLOCKED 0xa7a7 -#define IF_SLEEP 0xf0f0 -#define IF_ABORT 0x8080 -#define IF_UPLOAD_DEVINFO 0x9999 - -/* Definitions for Compatible Status Register */ -#define CSR_WR_READY 0x8080 /* Write state machine status */ -#define CSR_ERA_SUSPEND 0x4040 /* Erase suspend status */ -#define CSR_ERA_ERR 0x2020 /* Erase status */ -#define CSR_WR_ERR 0x1010 /* Data write status */ -#define CSR_VPP_LOW 0x0808 /* Vpp status */ - -/* Definitions for Global Status Register */ -#define GSR_WR_READY 0x8080 /* Write state machine status */ -#define GSR_OP_SUSPEND 0x4040 /* Operation suspend status */ -#define GSR_OP_ERR 0x2020 /* Device operation status */ -#define GSR_SLEEP 0x1010 /* Device sleep status */ -#define GSR_QUEUE_FULL 0x0808 /* Queue status */ -#define GSR_PAGE_AVAIL 0x0404 /* Page buffer available status */ -#define GSR_PAGE_READY 0x0202 /* Page buffer status */ -#define GSR_PAGE_SELECT 0x0101 /* Page buffer select status */ - -/* Definitions for Block Status Register */ -#define BSR_READY 0x8080 /* Block status */ -#define BSR_UNLOCK 0x4040 /* Block lock status */ -#define BSR_FAILED 0x2020 /* Block operation status */ -#define BSR_ABORTED 0x1010 /* Operation abort status */ -#define BSR_QUEUE_FULL 0x0808 /* Queue status */ -#define BSR_VPP_LOW 0x0404 /* Vpp status */ - -#endif /* __MTD_IFLASH_H__ */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 3d956c3..45d482c 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -53,6 +53,7 @@ struct mtd_erase_region_info { u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ u_int32_t erasesize; /* For this region */ u_int32_t numblocks; /* Number of blocks of erasesize in this region */ + unsigned long *lockmap; /* If keeping bitmap of locks */ }; /* diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 9752388..cf197ad 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -431,6 +431,7 @@ #define NAND_MFR_NATIONAL 0x8f #define NAND_MFR_RENESAS 0x07 #define NAND_MFR_STMICRO 0x20 #define NAND_MFR_HYNIX 0xad +#define NAND_MFR_MICRON 0x2c /** * struct nand_flash_dev - NAND Flash Device ID Structure diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h new file mode 100644 index 0000000..3d967b6 --- /dev/null +++ b/include/linux/mtd/ubi.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __LINUX_UBI_H__ +#define __LINUX_UBI_H__ + +#include +#include +#include + +/* + * UBI data type hint constants. + * + * UBI_LONGTERM: long-term data + * UBI_SHORTTERM: short-term data + * UBI_UNKNOWN: data persistence is unknown + * + * These constants are used when data is written to UBI volumes in order to + * help the UBI wear-leveling unit to find more appropriate physical + * eraseblocks. + */ +enum { + UBI_LONGTERM = 1, + UBI_SHORTTERM, + UBI_UNKNOWN +}; + +/* + * enum ubi_open_mode - UBI volume open mode constants. + * + * UBI_READONLY: read-only mode + * UBI_READWRITE: read-write mode + * UBI_EXCLUSIVE: exclusive mode + */ +enum { + UBI_READONLY = 1, + UBI_READWRITE, + UBI_EXCLUSIVE +}; + +/** + * struct ubi_volume_info - UBI volume description data structure. + * @vol_id: volume ID + * @ubi_num: UBI device number this volume belongs to + * @size: how many physical eraseblocks are reserved for this volume + * @used_bytes: how many bytes of data this volume contains + * @used_ebs: how many physical eraseblocks of this volume actually contain any + * data + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @corrupted: non-zero if the volume is corrupted (static volumes only) + * @upd_marker: non-zero if the volume has update marker set + * @alignment: volume alignment + * @usable_leb_size: how many bytes are available in logical eraseblocks of + * this volume + * @name_len: volume name length + * @name: volume name + * @cdev: UBI volume character device major and minor numbers + * + * The @corrupted flag is only relevant to static volumes and is always zero + * for dynamic ones. This is because UBI does not care about dynamic volume + * data protection and only cares about protecting static volume data. + * + * The @upd_marker flag is set if the volume update operation was interrupted. + * Before touching the volume data during the update operation, UBI first sets + * the update marker flag for this volume. If the volume update operation was + * further interrupted, the update marker indicates this. If the update marker + * is set, the contents of the volume is certainly damaged and a new volume + * update operation has to be started. + * + * To put it differently, @corrupted and @upd_marker fields have different + * semantics: + * o the @corrupted flag means that this static volume is corrupted for some + * reasons, but not because an interrupted volume update + * o the @upd_marker field means that the volume is damaged because of an + * interrupted update operation. + * + * I.e., the @corrupted flag is never set if the @upd_marker flag is set. + * + * The @used_bytes and @used_ebs fields are only really needed for static + * volumes and contain the number of bytes stored in this static volume and how + * many eraseblock this data occupies. In case of dynamic volumes, the + * @used_bytes field is equivalent to @size*@usable_leb_size, and the @used_ebs + * field is equivalent to @size. + * + * In general, logical eraseblock size is a property of the UBI device, not + * of the UBI volume. Indeed, the logical eraseblock size depends on the + * physical eraseblock size and on how much bytes UBI headers consume. But + * because of the volume alignment (@alignment), the usable size of logical + * eraseblocks if a volume may be less. The following equation is true: + * @usable_leb_size = LEB size - (LEB size mod @alignment), + * where LEB size is the logical eraseblock size defined by the UBI device. + * + * The alignment is multiple to the minimal flash input/output unit size or %1 + * if all the available space is used. + * + * To put this differently, alignment may be considered is a way to change + * volume logical eraseblock sizes. + */ +struct ubi_volume_info { + int ubi_num; + int vol_id; + int size; + long long used_bytes; + int used_ebs; + int vol_type; + int corrupted; + int upd_marker; + int alignment; + int usable_leb_size; + int name_len; + const char *name; + dev_t cdev; +}; + +/** + * struct ubi_device_info - UBI device description data structure. + * @ubi_num: ubi device number + * @leb_size: logical eraseblock size on this UBI device + * @min_io_size: minimal I/O unit size + * @ro_mode: if this device is in read-only mode + * @cdev: UBI character device major and minor numbers + * + * Note, @leb_size is the logical eraseblock size offered by the UBI device. + * Volumes of this UBI device may have smaller logical eraseblock size if their + * alignment is not equivalent to %1. + */ +struct ubi_device_info { + int ubi_num; + int leb_size; + int min_io_size; + int ro_mode; + dev_t cdev; +}; + +/* UBI descriptor given to users when they open UBI volumes */ +struct ubi_volume_desc; + +int ubi_get_device_info(int ubi_num, struct ubi_device_info *di); +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi); +struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode); +struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + int mode); +void ubi_close_volume(struct ubi_volume_desc *desc); +int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, + int len, int check); +int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, + int offset, int len, int dtype); +int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, + int len, int dtype); +int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum); +int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum); +int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); + +/* + * This function is the same as the 'ubi_leb_read()' function, but it does not + * provide the checking capability. + */ +static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf, + int offset, int len) +{ + return ubi_leb_read(desc, lnum, buf, offset, len, 0); +} + +/* + * This function is the same as the 'ubi_leb_write()' functions, but it does + * not have the data type argument. + */ +static inline int ubi_write(struct ubi_volume_desc *desc, int lnum, + const void *buf, int offset, int len) +{ + return ubi_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN); +} + +/* + * This function is the same as the 'ubi_leb_change()' functions, but it does + * not have the data type argument. + */ +static inline int ubi_change(struct ubi_volume_desc *desc, int lnum, + const void *buf, int len) +{ + return ubi_leb_change(desc, lnum, buf, len, UBI_UNKNOWN); +} + +#endif /* !__LINUX_UBI_H__ */ diff --git a/include/linux/namei.h b/include/linux/namei.h index d39a5a6..b7dd249 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -82,6 +82,7 @@ extern struct file *nameidata_to_filp(st extern void release_open_intent(struct nameidata *); extern struct dentry * lookup_one_len(const char *, struct dentry *, int); +extern struct dentry *lookup_one_len_kern(const char *, struct dentry *, int); extern int follow_down(struct vfsmount **, struct dentry **); extern int follow_up(struct vfsmount **, struct dentry **); diff --git a/include/linux/net.h b/include/linux/net.h index 4db21e6..efc4517 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -24,7 +24,7 @@ #include struct poll_table_struct; struct inode; -#define NPROTO 33 /* should be enough for now.. */ +#define NPROTO 34 /* should be enough for now.. */ #define SYS_SOCKET 1 /* sys_socket(2) */ #define SYS_BIND 2 /* sys_bind(2) */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1a52854..ac0c92b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -42,6 +42,8 @@ #include struct vlan_group; struct ethtool_ops; struct netpoll_info; +/* 802.11 specific */ +struct wireless_dev; /* source back-compat hooks */ #define SET_ETHTOOL_OPS(netdev,ops) \ ( (netdev)->ethtool_ops = (ops) ) @@ -347,13 +349,15 @@ #define NETIF_F_ALL_CSUM (NETIF_F_IP_CSU struct net_device_stats* (*get_stats)(struct net_device *dev); + struct net_device_stats stats; +#ifdef CONFIG_WIRELESS_EXT /* List of functions to handle Wireless Extensions (instead of ioctl). * See for details. Jean II */ const struct iw_handler_def * wireless_handlers; /* Instance data managed by the core of Wireless Extensions. */ struct iw_public_data * wireless_data; - +#endif const struct ethtool_ops *ethtool_ops; /* @@ -398,6 +402,8 @@ #define NETIF_F_ALL_CSUM (NETIF_F_IP_CSU void *ip6_ptr; /* IPv6 specific data */ void *ec_ptr; /* Econet specific data */ void *ax25_ptr; /* AX.25 specific data */ + struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data, + assign before registering */ /* * Cache line mostly used on receive path (including eth_type_trans()) @@ -647,8 +653,10 @@ static inline void netif_start_queue(str static inline void netif_wake_queue(struct net_device *dev) { #ifdef CONFIG_NETPOLL_TRAP - if (netpoll_trap()) + if (netpoll_trap()) { + clear_bit(__LINK_STATE_XOFF, &dev->state); return; + } #endif if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) __netif_schedule(dev); @@ -656,10 +664,6 @@ #endif static inline void netif_stop_queue(struct net_device *dev) { -#ifdef CONFIG_NETPOLL_TRAP - if (netpoll_trap()) - return; -#endif set_bit(__LINK_STATE_XOFF, &dev->state); } diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 70d3b4f..10b5c62 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -281,9 +281,6 @@ extern void nf_reinject(struct sk_buff * struct nf_info *info, unsigned int verdict); -extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *); -extern void nf_ct_attach(struct sk_buff *, struct sk_buff *); - /* FIXME: Before cache is ever used, this must be implemented for real. */ extern void nf_invalidate_cache(int pf); @@ -388,11 +385,18 @@ static inline int nf_hook(int pf, unsign { return 1; } -static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} struct flowi; static inline void nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family) {} #endif /*CONFIG_NETFILTER*/ +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *); +extern void nf_ct_attach(struct sk_buff *, struct sk_buff *); +extern void (*nf_ct_destroy)(struct nf_conntrack *); +#else +static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} +#endif + #endif /*__KERNEL__*/ #endif /*__LINUX_NETFILTER_H*/ diff --git a/include/linux/netfilter/nf_conntrack_tcp.h b/include/linux/netfilter/nf_conntrack_tcp.h index 007af4c..22ce299 100644 --- a/include/linux/netfilter/nf_conntrack_tcp.h +++ b/include/linux/netfilter/nf_conntrack_tcp.h @@ -30,6 +30,11 @@ #define IP_CT_TCP_FLAG_CLOSE_INIT 0x04 /* Be liberal in window checking */ #define IP_CT_TCP_FLAG_BE_LIBERAL 0x08 +struct nf_ct_tcp_flags { + u_int8_t flags; + u_int8_t mask; +}; + #ifdef __KERNEL__ struct ip_ct_tcp_state { diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 1e9c821..0f9311d 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -62,11 +62,11 @@ #define NFA_SPACE(len) NFA_ALIGN(NFA_LEN #define NFA_DATA(nfa) ((void *)(((char *)(nfa)) + NFA_LENGTH(0))) #define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0)) #define NFA_NEST(skb, type) \ -({ struct nfattr *__start = (struct nfattr *) (skb)->tail; \ +({ struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \ NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \ __start; }) #define NFA_NEST_END(skb, start) \ -({ (start)->nfa_len = ((skb)->tail - (unsigned char *) (start)); \ +({ (start)->nfa_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ (skb)->len; }) #define NFA_NEST_CANCEL(skb, start) \ ({ if (start) \ @@ -111,7 +111,7 @@ #include struct nfnl_callback { int (*call)(struct sock *nl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp); + struct nlmsghdr *nlh, struct nfattr *cda[]); u_int16_t attr_count; /* number of nfattr's */ }; @@ -129,19 +129,6 @@ #define NFA_PUT(skb, attrtype, attrlen, ({ if (skb_tailroom(skb) < (int)NFA_SPACE(attrlen)) goto nfattr_failure; \ __nfa_fill(skb, attrtype, attrlen, data); }) -extern struct semaphore nfnl_sem; - -#define nfnl_shlock() down(&nfnl_sem) -#define nfnl_shlock_nowait() down_trylock(&nfnl_sem) - -#define nfnl_shunlock() do { up(&nfnl_sem); \ - if(nfnl && nfnl->sk_receive_queue.qlen) \ - nfnl->sk_data_ready(nfnl, 0); \ - } while(0) - -extern void nfnl_lock(void); -extern void nfnl_unlock(void); - extern int nfnetlink_subsys_register(struct nfnetlink_subsystem *n); extern int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n); diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index b5883cc..d7c3503 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -83,6 +83,10 @@ #define CTA_PROTOINFO_MAX (__CTA_PROTOIN enum ctattr_protoinfo_tcp { CTA_PROTOINFO_TCP_UNSPEC, CTA_PROTOINFO_TCP_STATE, + CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, + CTA_PROTOINFO_TCP_WSCALE_REPLY, + CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, + CTA_PROTOINFO_TCP_FLAGS_REPLY, __CTA_PROTOINFO_TCP_MAX }; #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 55689f3..1906003 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -7,6 +7,7 @@ #define __LINUX_BRIDGE_NETFILTER_H #include #include #include +#include /* Bridge Hooks */ /* After promisc drops, checksum checks. */ @@ -58,8 +59,14 @@ static inline int nf_bridge_maybe_copy_h * enough room for the encapsulating header (if there is one). */ static inline int nf_bridge_pad(const struct sk_buff *skb) { - return (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q)) - ? VLAN_HLEN : 0; + int padding = 0; + + if (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q)) + padding = VLAN_HLEN; + else if (skb->nf_bridge && skb->protocol == htons(ETH_P_PPP_SES)) + padding = PPPOE_SES_HLEN; + + return padding; } struct bridge_skb_cb { diff --git a/include/linux/netfilter_bridge/ebt_802_3.h b/include/linux/netfilter_bridge/ebt_802_3.h index 07f044f..a11b0c2 100644 --- a/include/linux/netfilter_bridge/ebt_802_3.h +++ b/include/linux/netfilter_bridge/ebt_802_3.h @@ -54,7 +54,7 @@ #include static inline struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb) { - return (struct ebt_802_3_hdr *)skb->mac.raw; + return (struct ebt_802_3_hdr *)skb_mac_header(skb); } #endif diff --git a/include/linux/netfilter_bridge/ebt_arp.h b/include/linux/netfilter_bridge/ebt_arp.h index 97e4dbd..cbf4843 100644 --- a/include/linux/netfilter_bridge/ebt_arp.h +++ b/include/linux/netfilter_bridge/ebt_arp.h @@ -8,8 +8,10 @@ #define EBT_ARP_SRC_IP 0x08 #define EBT_ARP_DST_IP 0x10 #define EBT_ARP_SRC_MAC 0x20 #define EBT_ARP_DST_MAC 0x40 +#define EBT_ARP_GRAT 0x80 #define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ - EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC) + EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC | \ + EBT_ARP_GRAT) #define EBT_ARP_MATCH "arp" struct ebt_arp_info diff --git a/include/linux/netfilter_ipv4/Kbuild b/include/linux/netfilter_ipv4/Kbuild index 1803378..7185792 100644 --- a/include/linux/netfilter_ipv4/Kbuild +++ b/include/linux/netfilter_ipv4/Kbuild @@ -1,9 +1,3 @@ -header-y += ip_conntrack_helper.h -header-y += ip_conntrack_protocol.h -header-y += ip_conntrack_sctp.h -header-y += ip_conntrack_tcp.h -header-y += ip_conntrack_tftp.h -header-y += ip_nat_pptp.h header-y += ipt_addrtype.h header-y += ipt_ah.h header-y += ipt_CLASSIFY.h @@ -49,13 +43,5 @@ header-y += ipt_ttl.h header-y += ipt_TTL.h header-y += ipt_ULOG.h -unifdef-y += ip_conntrack.h -unifdef-y += ip_conntrack_h323.h -unifdef-y += ip_conntrack_irc.h -unifdef-y += ip_conntrack_pptp.h -unifdef-y += ip_conntrack_proto_gre.h -unifdef-y += ip_conntrack_tuple.h -unifdef-y += ip_nat.h -unifdef-y += ip_nat_rule.h unifdef-y += ip_queue.h unifdef-y += ip_tables.h diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h deleted file mode 100644 index da9274e..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack.h +++ /dev/null @@ -1,402 +0,0 @@ -#ifndef _IP_CONNTRACK_H -#define _IP_CONNTRACK_H - -#include - -#ifdef __KERNEL__ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* per conntrack: protocol private data */ -union ip_conntrack_proto { - /* insert conntrack proto private data here */ - struct ip_ct_gre gre; - struct ip_ct_sctp sctp; - struct ip_ct_tcp tcp; - struct ip_ct_icmp icmp; -}; - -union ip_conntrack_expect_proto { - /* insert expect proto private data here */ -}; - -/* Add protocol helper include file here */ -#include -#include -#include -#include -#include - -/* per conntrack: application helper private data */ -union ip_conntrack_help { - /* insert conntrack helper private data (master) here */ - struct ip_ct_h323_master ct_h323_info; - struct ip_ct_pptp_master ct_pptp_info; - struct ip_ct_ftp_master ct_ftp_info; - struct ip_ct_irc_master ct_irc_info; -}; - -#ifdef CONFIG_IP_NF_NAT_NEEDED -#include -#include - -/* per conntrack: nat application helper private data */ -union ip_conntrack_nat_help { - /* insert nat helper private data here */ - struct ip_nat_pptp nat_pptp_info; -}; -#endif - -#include -#include - -#ifdef CONFIG_NETFILTER_DEBUG -#define IP_NF_ASSERT(x) \ -do { \ - if (!(x)) \ - /* Wooah! I'm tripping my conntrack in a frenzy of \ - netplay... */ \ - printk("NF_IP_ASSERT: %s:%i(%s)\n", \ - __FILE__, __LINE__, __FUNCTION__); \ -} while(0) -#else -#define IP_NF_ASSERT(x) -#endif - -struct ip_conntrack_helper; - -struct ip_conntrack -{ - /* Usage count in here is 1 for hash table/destruct timer, 1 per skb, - plus 1 for any connection(s) we are `master' for */ - struct nf_conntrack ct_general; - - /* Have we seen traffic both ways yet? (bitset) */ - unsigned long status; - - /* Timer function; drops refcnt when it goes off. */ - struct timer_list timeout; - -#ifdef CONFIG_IP_NF_CT_ACCT - /* Accounting Information (same cache line as other written members) */ - struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; -#endif - /* If we were expected by an expectation, this will be it */ - struct ip_conntrack *master; - - /* Current number of expected connections */ - unsigned int expecting; - - /* Unique ID that identifies this conntrack*/ - unsigned int id; - - /* Helper, if any. */ - struct ip_conntrack_helper *helper; - - /* Storage reserved for other modules: */ - union ip_conntrack_proto proto; - - union ip_conntrack_help help; - -#ifdef CONFIG_IP_NF_NAT_NEEDED - struct { - struct ip_nat_info info; - union ip_conntrack_nat_help help; -#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ - defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) - int masq_index; -#endif - } nat; -#endif /* CONFIG_IP_NF_NAT_NEEDED */ - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - u_int32_t mark; -#endif - -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK - u_int32_t secmark; -#endif - - /* Traversed often, so hopefully in different cacheline to top */ - /* These are my tuples; original and reply */ - struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; -}; - -struct ip_conntrack_expect -{ - /* Internal linked list (global expectation list) */ - struct list_head list; - - /* We expect this tuple, with the following mask */ - struct ip_conntrack_tuple tuple, mask; - - /* Function to call after setup and insertion */ - void (*expectfn)(struct ip_conntrack *new, - struct ip_conntrack_expect *this); - - /* The conntrack of the master connection */ - struct ip_conntrack *master; - - /* Timer function; deletes the expectation. */ - struct timer_list timeout; - - /* Usage count. */ - atomic_t use; - - /* Unique ID */ - unsigned int id; - - /* Flags */ - unsigned int flags; - -#ifdef CONFIG_IP_NF_NAT_NEEDED - __be32 saved_ip; - /* This is the original per-proto part, used to map the - * expected connection the way the recipient expects. */ - union ip_conntrack_manip_proto saved_proto; - /* Direction relative to the master connection. */ - enum ip_conntrack_dir dir; -#endif -}; - -#define IP_CT_EXPECT_PERMANENT 0x1 - -static inline struct ip_conntrack * -tuplehash_to_ctrack(const struct ip_conntrack_tuple_hash *hash) -{ - return container_of(hash, struct ip_conntrack, - tuplehash[hash->tuple.dst.dir]); -} - -/* get master conntrack via master expectation */ -#define master_ct(conntr) (conntr->master) - -/* Alter reply tuple (maybe alter helper). */ -extern void -ip_conntrack_alter_reply(struct ip_conntrack *conntrack, - const struct ip_conntrack_tuple *newreply); - -/* Is this tuple taken? (ignoring any belonging to the given - conntrack). */ -extern int -ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -/* Return conntrack_info and tuple hash for given skb. */ -static inline struct ip_conntrack * -ip_conntrack_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) -{ - *ctinfo = skb->nfctinfo; - return (struct ip_conntrack *)skb->nfct; -} - -/* decrement reference count on a conntrack */ -static inline void -ip_conntrack_put(struct ip_conntrack *ct) -{ - IP_NF_ASSERT(ct); - nf_conntrack_put(&ct->ct_general); -} - -extern int invert_tuplepr(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig); - -extern void __ip_ct_refresh_acct(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies, - int do_acct); - -/* Refresh conntrack for this many jiffies and do accounting */ -static inline void ip_ct_refresh_acct(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies) -{ - __ip_ct_refresh_acct(ct, ctinfo, skb, extra_jiffies, 1); -} - -/* Refresh conntrack for this many jiffies */ -static inline void ip_ct_refresh(struct ip_conntrack *ct, - const struct sk_buff *skb, - unsigned long extra_jiffies) -{ - __ip_ct_refresh_acct(ct, 0, skb, extra_jiffies, 0); -} - -/* These are for NAT. Icky. */ -/* Update TCP window tracking data when NAT mangles the packet */ -extern void ip_conntrack_tcp_update(struct sk_buff *skb, - struct ip_conntrack *conntrack, - enum ip_conntrack_dir dir); - -/* Call me when a conntrack is destroyed. */ -extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack); - -/* Fake conntrack entry for untracked connections */ -extern struct ip_conntrack ip_conntrack_untracked; - -/* Returns new sk_buff, or NULL */ -struct sk_buff * -ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user); - -/* Iterate over all conntracks: if iter returns true, it's deleted. */ -extern void -ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *data), - void *data); - -extern struct ip_conntrack_helper * -__ip_conntrack_helper_find_byname(const char *); -extern struct ip_conntrack_helper * -ip_conntrack_helper_find_get(const struct ip_conntrack_tuple *tuple); -extern void ip_conntrack_helper_put(struct ip_conntrack_helper *helper); - -extern struct ip_conntrack_protocol * -__ip_conntrack_proto_find(u_int8_t protocol); -extern struct ip_conntrack_protocol * -ip_conntrack_proto_find_get(u_int8_t protocol); -extern void ip_conntrack_proto_put(struct ip_conntrack_protocol *proto); - -extern void ip_ct_remove_expectations(struct ip_conntrack *ct); - -extern struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *, - struct ip_conntrack_tuple *); - -extern void ip_conntrack_free(struct ip_conntrack *ct); - -extern void ip_conntrack_hash_insert(struct ip_conntrack *ct); - -extern struct ip_conntrack_expect * -__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple); - -extern struct ip_conntrack_expect * -ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple); - -extern struct ip_conntrack_tuple_hash * -__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -extern void ip_conntrack_flush(void); - -/* It's confirmed if it is, or has been in the hash table. */ -static inline int is_confirmed(struct ip_conntrack *ct) -{ - return test_bit(IPS_CONFIRMED_BIT, &ct->status); -} - -static inline int is_dying(struct ip_conntrack *ct) -{ - return test_bit(IPS_DYING_BIT, &ct->status); -} - -extern unsigned int ip_conntrack_htable_size; -extern int ip_conntrack_checksum; - -#define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++) -#define CONNTRACK_STAT_INC_ATOMIC(count) \ -do { \ - local_bh_disable(); \ - __get_cpu_var(ip_conntrack_stat).count++; \ - local_bh_enable(); \ -} while (0) - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -#include -#include - -struct ip_conntrack_ecache { - struct ip_conntrack *ct; - unsigned int events; -}; -DECLARE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); - -#define CONNTRACK_ECACHE(x) (__get_cpu_var(ip_conntrack_ecache).x) - -extern struct atomic_notifier_head ip_conntrack_chain; -extern struct atomic_notifier_head ip_conntrack_expect_chain; - -static inline int ip_conntrack_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&ip_conntrack_chain, nb); -} - -static inline int ip_conntrack_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&ip_conntrack_chain, nb); -} - -static inline int -ip_conntrack_expect_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&ip_conntrack_expect_chain, nb); -} - -static inline int -ip_conntrack_expect_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&ip_conntrack_expect_chain, - nb); -} - -extern void ip_ct_deliver_cached_events(const struct ip_conntrack *ct); -extern void __ip_ct_event_cache_init(struct ip_conntrack *ct); - -static inline void -ip_conntrack_event_cache(enum ip_conntrack_events event, - const struct sk_buff *skb) -{ - struct ip_conntrack *ct = (struct ip_conntrack *)skb->nfct; - struct ip_conntrack_ecache *ecache; - - local_bh_disable(); - ecache = &__get_cpu_var(ip_conntrack_ecache); - if (ct != ecache->ct) - __ip_ct_event_cache_init(ct); - ecache->events |= event; - local_bh_enable(); -} - -static inline void ip_conntrack_event(enum ip_conntrack_events event, - struct ip_conntrack *ct) -{ - if (is_confirmed(ct) && !is_dying(ct)) - atomic_notifier_call_chain(&ip_conntrack_chain, event, ct); -} - -static inline void -ip_conntrack_expect_event(enum ip_conntrack_expect_events event, - struct ip_conntrack_expect *exp) -{ - atomic_notifier_call_chain(&ip_conntrack_expect_chain, event, exp); -} -#else /* CONFIG_IP_NF_CONNTRACK_EVENTS */ -static inline void ip_conntrack_event_cache(enum ip_conntrack_events event, - const struct sk_buff *skb) {} -static inline void ip_conntrack_event(enum ip_conntrack_events event, - struct ip_conntrack *ct) {} -static inline void ip_ct_deliver_cached_events(const struct ip_conntrack *ct) {} -static inline void -ip_conntrack_expect_event(enum ip_conntrack_expect_events event, - struct ip_conntrack_expect *exp) {} -#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ - -#ifdef CONFIG_IP_NF_NAT_NEEDED -static inline int ip_nat_initialized(struct ip_conntrack *conntrack, - enum ip_nat_manip_type manip) -{ - if (manip == IP_NAT_MANIP_SRC) - return test_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status); - return test_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status); -} -#endif /* CONFIG_IP_NF_NAT_NEEDED */ - -#endif /* __KERNEL__ */ -#endif /* _IP_CONNTRACK_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_amanda.h b/include/linux/netfilter_ipv4/ip_conntrack_amanda.h deleted file mode 100644 index de3e41f..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_amanda.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _IP_CONNTRACK_AMANDA_H -#define _IP_CONNTRACK_AMANDA_H -/* AMANDA tracking. */ - -struct ip_conntrack_expect; -extern unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); -#endif /* _IP_CONNTRACK_AMANDA_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_core.h b/include/linux/netfilter_ipv4/ip_conntrack_core.h deleted file mode 100644 index e3a6df0..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_core.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef _IP_CONNTRACK_CORE_H -#define _IP_CONNTRACK_CORE_H -#include - -#define MAX_IP_CT_PROTO 256 -extern struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO]; - -/* This header is used to share core functionality between the - standalone connection tracking module, and the compatibility layer's use - of connection tracking. */ -extern unsigned int ip_conntrack_in(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)); - -extern int ip_conntrack_init(void); -extern void ip_conntrack_cleanup(void); - -struct ip_conntrack_protocol; - -extern int -ip_ct_get_tuple(const struct iphdr *iph, - const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_protocol *protocol); - -extern int -ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig, - const struct ip_conntrack_protocol *protocol); - -/* Find a connection corresponding to a tuple. */ -struct ip_conntrack_tuple_hash * -ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -extern int __ip_conntrack_confirm(struct sk_buff **pskb); - -/* Confirm a connection: returns NF_DROP if packet must be dropped. */ -static inline int ip_conntrack_confirm(struct sk_buff **pskb) -{ - struct ip_conntrack *ct = (struct ip_conntrack *)(*pskb)->nfct; - int ret = NF_ACCEPT; - - if (ct) { - if (!is_confirmed(ct) && !is_dying(ct)) - ret = __ip_conntrack_confirm(pskb); - ip_ct_deliver_cached_events(ct); - } - return ret; -} - -extern void ip_ct_unlink_expect(struct ip_conntrack_expect *exp); - -extern struct list_head *ip_conntrack_hash; -extern struct list_head ip_conntrack_expect_list; -extern rwlock_t ip_conntrack_lock; -#endif /* _IP_CONNTRACK_CORE_H */ - diff --git a/include/linux/netfilter_ipv4/ip_conntrack_ftp.h b/include/linux/netfilter_ipv4/ip_conntrack_ftp.h deleted file mode 100644 index 2129fc3..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_ftp.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _IP_CONNTRACK_FTP_H -#define _IP_CONNTRACK_FTP_H -/* FTP tracking. */ - -/* This enum is exposed to userspace */ -enum ip_ct_ftp_type -{ - /* PORT command from client */ - IP_CT_FTP_PORT, - /* PASV response from server */ - IP_CT_FTP_PASV, - /* EPRT command from client */ - IP_CT_FTP_EPRT, - /* EPSV response from server */ - IP_CT_FTP_EPSV, -}; - -#ifdef __KERNEL__ - -#define FTP_PORT 21 - -#define NUM_SEQ_TO_REMEMBER 2 -/* This structure exists only once per master */ -struct ip_ct_ftp_master { - /* Valid seq positions for cmd matching after newline */ - u_int32_t seq_aft_nl[IP_CT_DIR_MAX][NUM_SEQ_TO_REMEMBER]; - /* 0 means seq_match_aft_nl not set */ - int seq_aft_nl_num[IP_CT_DIR_MAX]; -}; - -struct ip_conntrack_expect; - -/* For NAT to hook in when we find a packet which describes what other - * connection we should expect. */ -extern unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - enum ip_ct_ftp_type type, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp, - u32 *seq); -#endif /* __KERNEL__ */ - -#endif /* _IP_CONNTRACK_FTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_h323.h b/include/linux/netfilter_ipv4/ip_conntrack_h323.h deleted file mode 100644 index 18f7698..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_h323.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef _IP_CONNTRACK_H323_H -#define _IP_CONNTRACK_H323_H - -#ifdef __KERNEL__ - -#include - -#define RAS_PORT 1719 -#define Q931_PORT 1720 -#define H323_RTP_CHANNEL_MAX 4 /* Audio, video, FAX and other */ - -/* This structure exists only once per master */ -struct ip_ct_h323_master { - - /* Original and NATed Q.931 or H.245 signal ports */ - u_int16_t sig_port[IP_CT_DIR_MAX]; - - /* Original and NATed RTP ports */ - u_int16_t rtp_port[H323_RTP_CHANNEL_MAX][IP_CT_DIR_MAX]; - - union { - /* RAS connection timeout */ - u_int32_t timeout; - - /* Next TPKT length (for separate TPKT header and data) */ - u_int16_t tpkt_len[IP_CT_DIR_MAX]; - }; -}; - -struct ip_conntrack_expect; - -extern int get_h225_addr(unsigned char *data, TransportAddress * addr, - __be32 * ip, u_int16_t * port); -extern void ip_conntrack_h245_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); -extern void ip_conntrack_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); -extern int (*set_h245_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - __be32 ip, u_int16_t port); -extern int (*set_h225_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - TransportAddress * addr, - __be32 ip, u_int16_t port); -extern int (*set_sig_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -extern int (*set_ras_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -extern int (*nat_rtp_rtcp_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - u_int16_t port, u_int16_t rtp_port, - struct ip_conntrack_expect * rtp_exp, - struct ip_conntrack_expect * rtcp_exp); -extern int (*nat_t120_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -extern int (*nat_h245_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -extern int (*nat_callforwarding_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, - u_int16_t port, - struct ip_conntrack_expect * exp); -extern int (*nat_q931_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, TransportAddress * addr, - int idx, u_int16_t port, - struct ip_conntrack_expect * exp); - -#endif - -#endif diff --git a/include/linux/netfilter_ipv4/ip_conntrack_helper.h b/include/linux/netfilter_ipv4/ip_conntrack_helper.h deleted file mode 100644 index 77fe868..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_helper.h +++ /dev/null @@ -1,46 +0,0 @@ -/* IP connection tracking helpers. */ -#ifndef _IP_CONNTRACK_HELPER_H -#define _IP_CONNTRACK_HELPER_H -#include - -struct module; - -struct ip_conntrack_helper -{ - struct list_head list; /* Internal use. */ - - const char *name; /* name of the module */ - struct module *me; /* pointer to self */ - unsigned int max_expected; /* Maximum number of concurrent - * expected connections */ - unsigned int timeout; /* timeout for expecteds */ - - /* Mask of things we will help (compared against server response) */ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_tuple mask; - - /* Function to call when data passes; return verdict, or -1 to - invalidate. */ - int (*help)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info conntrackinfo); - - void (*destroy)(struct ip_conntrack *ct); - - int (*to_nfattr)(struct sk_buff *skb, const struct ip_conntrack *ct); -}; - -extern int ip_conntrack_helper_register(struct ip_conntrack_helper *); -extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *); - -/* Allocate space for an expectation: this is mandatory before calling - ip_conntrack_expect_related. You will have to call put afterwards. */ -extern struct ip_conntrack_expect * -ip_conntrack_expect_alloc(struct ip_conntrack *master); -extern void ip_conntrack_expect_put(struct ip_conntrack_expect *exp); - -/* Add an expected connection: can have more than one per connection */ -extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp); -extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp); - -#endif /*_IP_CONNTRACK_HELPER_H*/ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_icmp.h b/include/linux/netfilter_ipv4/ip_conntrack_icmp.h deleted file mode 100644 index eed5ee3..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_icmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _IP_CONNTRACK_ICMP_H -#define _IP_CONNTRACK_ICMP_H - -#include - -#endif /* _IP_CONNTRACK_ICMP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_irc.h b/include/linux/netfilter_ipv4/ip_conntrack_irc.h deleted file mode 100644 index 16601e0..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_irc.h +++ /dev/null @@ -1,32 +0,0 @@ -/* IRC extension for IP connection tracking. - * (C) 2000 by Harald Welte - * based on RR's ip_conntrack_ftp.h - * - * ip_conntrack_irc.h,v 1.6 2000/11/07 18:26:42 laforge Exp - * - * 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. - * - * - */ -#ifndef _IP_CONNTRACK_IRC_H -#define _IP_CONNTRACK_IRC_H - -/* This structure exists only once per master */ -struct ip_ct_irc_master { -}; - -#ifdef __KERNEL__ -extern unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); - -#define IRC_PORT 6667 - -#endif /* __KERNEL__ */ - -#endif /* _IP_CONNTRACK_IRC_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h deleted file mode 100644 index 2644b1f..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h +++ /dev/null @@ -1,326 +0,0 @@ -/* PPTP constants and structs */ -#ifndef _CONNTRACK_PPTP_H -#define _CONNTRACK_PPTP_H - -/* state of the control session */ -enum pptp_ctrlsess_state { - PPTP_SESSION_NONE, /* no session present */ - PPTP_SESSION_ERROR, /* some session error */ - PPTP_SESSION_STOPREQ, /* stop_sess request seen */ - PPTP_SESSION_REQUESTED, /* start_sess request seen */ - PPTP_SESSION_CONFIRMED, /* session established */ -}; - -/* state of the call inside the control session */ -enum pptp_ctrlcall_state { - PPTP_CALL_NONE, - PPTP_CALL_ERROR, - PPTP_CALL_OUT_REQ, - PPTP_CALL_OUT_CONF, - PPTP_CALL_IN_REQ, - PPTP_CALL_IN_REP, - PPTP_CALL_IN_CONF, - PPTP_CALL_CLEAR_REQ, -}; - - -/* conntrack private data */ -struct ip_ct_pptp_master { - enum pptp_ctrlsess_state sstate; /* session state */ - - /* everything below is going to be per-expectation in newnat, - * since there could be more than one call within one session */ - enum pptp_ctrlcall_state cstate; /* call state */ - __be16 pac_call_id; /* call id of PAC, host byte order */ - __be16 pns_call_id; /* call id of PNS, host byte order */ - - /* in pre-2.6.11 this used to be per-expect. Now it is per-conntrack - * and therefore imposes a fixed limit on the number of maps */ - struct ip_ct_gre_keymap *keymap_orig, *keymap_reply; -}; - -/* conntrack_expect private member */ -struct ip_ct_pptp_expect { - enum pptp_ctrlcall_state cstate; /* call state */ - __be16 pac_call_id; /* call id of PAC */ - __be16 pns_call_id; /* call id of PNS */ -}; - - -#ifdef __KERNEL__ - -#define IP_CONNTR_PPTP PPTP_CONTROL_PORT - -#define PPTP_CONTROL_PORT 1723 - -#define PPTP_PACKET_CONTROL 1 -#define PPTP_PACKET_MGMT 2 - -#define PPTP_MAGIC_COOKIE 0x1a2b3c4d - -struct pptp_pkt_hdr { - __u16 packetLength; - __be16 packetType; - __be32 magicCookie; -}; - -/* PptpControlMessageType values */ -#define PPTP_START_SESSION_REQUEST 1 -#define PPTP_START_SESSION_REPLY 2 -#define PPTP_STOP_SESSION_REQUEST 3 -#define PPTP_STOP_SESSION_REPLY 4 -#define PPTP_ECHO_REQUEST 5 -#define PPTP_ECHO_REPLY 6 -#define PPTP_OUT_CALL_REQUEST 7 -#define PPTP_OUT_CALL_REPLY 8 -#define PPTP_IN_CALL_REQUEST 9 -#define PPTP_IN_CALL_REPLY 10 -#define PPTP_IN_CALL_CONNECT 11 -#define PPTP_CALL_CLEAR_REQUEST 12 -#define PPTP_CALL_DISCONNECT_NOTIFY 13 -#define PPTP_WAN_ERROR_NOTIFY 14 -#define PPTP_SET_LINK_INFO 15 - -#define PPTP_MSG_MAX 15 - -/* PptpGeneralError values */ -#define PPTP_ERROR_CODE_NONE 0 -#define PPTP_NOT_CONNECTED 1 -#define PPTP_BAD_FORMAT 2 -#define PPTP_BAD_VALUE 3 -#define PPTP_NO_RESOURCE 4 -#define PPTP_BAD_CALLID 5 -#define PPTP_REMOVE_DEVICE_ERROR 6 - -struct PptpControlHeader { - __be16 messageType; - __u16 reserved; -}; - -/* FramingCapability Bitmap Values */ -#define PPTP_FRAME_CAP_ASYNC 0x1 -#define PPTP_FRAME_CAP_SYNC 0x2 - -/* BearerCapability Bitmap Values */ -#define PPTP_BEARER_CAP_ANALOG 0x1 -#define PPTP_BEARER_CAP_DIGITAL 0x2 - -struct PptpStartSessionRequest { - __be16 protocolVersion; - __u16 reserved1; - __be32 framingCapability; - __be32 bearerCapability; - __be16 maxChannels; - __be16 firmwareRevision; - __u8 hostName[64]; - __u8 vendorString[64]; -}; - -/* PptpStartSessionResultCode Values */ -#define PPTP_START_OK 1 -#define PPTP_START_GENERAL_ERROR 2 -#define PPTP_START_ALREADY_CONNECTED 3 -#define PPTP_START_NOT_AUTHORIZED 4 -#define PPTP_START_UNKNOWN_PROTOCOL 5 - -struct PptpStartSessionReply { - __be16 protocolVersion; - __u8 resultCode; - __u8 generalErrorCode; - __be32 framingCapability; - __be32 bearerCapability; - __be16 maxChannels; - __be16 firmwareRevision; - __u8 hostName[64]; - __u8 vendorString[64]; -}; - -/* PptpStopReasons */ -#define PPTP_STOP_NONE 1 -#define PPTP_STOP_PROTOCOL 2 -#define PPTP_STOP_LOCAL_SHUTDOWN 3 - -struct PptpStopSessionRequest { - __u8 reason; - __u8 reserved1; - __u16 reserved2; -}; - -/* PptpStopSessionResultCode */ -#define PPTP_STOP_OK 1 -#define PPTP_STOP_GENERAL_ERROR 2 - -struct PptpStopSessionReply { - __u8 resultCode; - __u8 generalErrorCode; - __u16 reserved1; -}; - -struct PptpEchoRequest { - __be32 identNumber; -}; - -/* PptpEchoReplyResultCode */ -#define PPTP_ECHO_OK 1 -#define PPTP_ECHO_GENERAL_ERROR 2 - -struct PptpEchoReply { - __be32 identNumber; - __u8 resultCode; - __u8 generalErrorCode; - __u16 reserved; -}; - -/* PptpFramingType */ -#define PPTP_ASYNC_FRAMING 1 -#define PPTP_SYNC_FRAMING 2 -#define PPTP_DONT_CARE_FRAMING 3 - -/* PptpCallBearerType */ -#define PPTP_ANALOG_TYPE 1 -#define PPTP_DIGITAL_TYPE 2 -#define PPTP_DONT_CARE_BEARER_TYPE 3 - -struct PptpOutCallRequest { - __be16 callID; - __be16 callSerialNumber; - __be32 minBPS; - __be32 maxBPS; - __be32 bearerType; - __be32 framingType; - __be16 packetWindow; - __be16 packetProcDelay; - __be16 phoneNumberLength; - __u16 reserved1; - __u8 phoneNumber[64]; - __u8 subAddress[64]; -}; - -/* PptpCallResultCode */ -#define PPTP_OUTCALL_CONNECT 1 -#define PPTP_OUTCALL_GENERAL_ERROR 2 -#define PPTP_OUTCALL_NO_CARRIER 3 -#define PPTP_OUTCALL_BUSY 4 -#define PPTP_OUTCALL_NO_DIAL_TONE 5 -#define PPTP_OUTCALL_TIMEOUT 6 -#define PPTP_OUTCALL_DONT_ACCEPT 7 - -struct PptpOutCallReply { - __be16 callID; - __be16 peersCallID; - __u8 resultCode; - __u8 generalErrorCode; - __be16 causeCode; - __be32 connectSpeed; - __be16 packetWindow; - __be16 packetProcDelay; - __be32 physChannelID; -}; - -struct PptpInCallRequest { - __be16 callID; - __be16 callSerialNumber; - __be32 callBearerType; - __be32 physChannelID; - __be16 dialedNumberLength; - __be16 dialingNumberLength; - __u8 dialedNumber[64]; - __u8 dialingNumber[64]; - __u8 subAddress[64]; -}; - -/* PptpInCallResultCode */ -#define PPTP_INCALL_ACCEPT 1 -#define PPTP_INCALL_GENERAL_ERROR 2 -#define PPTP_INCALL_DONT_ACCEPT 3 - -struct PptpInCallReply { - __be16 callID; - __be16 peersCallID; - __u8 resultCode; - __u8 generalErrorCode; - __be16 packetWindow; - __be16 packetProcDelay; - __u16 reserved; -}; - -struct PptpInCallConnected { - __be16 peersCallID; - __u16 reserved; - __be32 connectSpeed; - __be16 packetWindow; - __be16 packetProcDelay; - __be32 callFramingType; -}; - -struct PptpClearCallRequest { - __be16 callID; - __u16 reserved; -}; - -struct PptpCallDisconnectNotify { - __be16 callID; - __u8 resultCode; - __u8 generalErrorCode; - __be16 causeCode; - __u16 reserved; - __u8 callStatistics[128]; -}; - -struct PptpWanErrorNotify { - __be16 peersCallID; - __u16 reserved; - __be32 crcErrors; - __be32 framingErrors; - __be32 hardwareOverRuns; - __be32 bufferOverRuns; - __be32 timeoutErrors; - __be32 alignmentErrors; -}; - -struct PptpSetLinkInfo { - __be16 peersCallID; - __u16 reserved; - __be32 sendAccm; - __be32 recvAccm; -}; - -union pptp_ctrl_union { - struct PptpStartSessionRequest sreq; - struct PptpStartSessionReply srep; - struct PptpStopSessionRequest streq; - struct PptpStopSessionReply strep; - struct PptpOutCallRequest ocreq; - struct PptpOutCallReply ocack; - struct PptpInCallRequest icreq; - struct PptpInCallReply icack; - struct PptpInCallConnected iccon; - struct PptpClearCallRequest clrreq; - struct PptpCallDisconnectNotify disc; - struct PptpWanErrorNotify wanerr; - struct PptpSetLinkInfo setlink; -}; - -extern int -(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -extern int -(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -extern void -(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *exp_orig, - struct ip_conntrack_expect *exp_reply); - -extern void -(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp); -#endif /* __KERNEL__ */ -#endif /* _CONNTRACK_PPTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h deleted file mode 100644 index e371e0f..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef _CONNTRACK_PROTO_GRE_H -#define _CONNTRACK_PROTO_GRE_H -#include - -/* GRE PROTOCOL HEADER */ - -/* GRE Version field */ -#define GRE_VERSION_1701 0x0 -#define GRE_VERSION_PPTP 0x1 - -/* GRE Protocol field */ -#define GRE_PROTOCOL_PPTP 0x880B - -/* GRE Flags */ -#define GRE_FLAG_C 0x80 -#define GRE_FLAG_R 0x40 -#define GRE_FLAG_K 0x20 -#define GRE_FLAG_S 0x10 -#define GRE_FLAG_A 0x80 - -#define GRE_IS_C(f) ((f)&GRE_FLAG_C) -#define GRE_IS_R(f) ((f)&GRE_FLAG_R) -#define GRE_IS_K(f) ((f)&GRE_FLAG_K) -#define GRE_IS_S(f) ((f)&GRE_FLAG_S) -#define GRE_IS_A(f) ((f)&GRE_FLAG_A) - -/* GRE is a mess: Four different standards */ -struct gre_hdr { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u16 rec:3, - srr:1, - seq:1, - key:1, - routing:1, - csum:1, - version:3, - reserved:4, - ack:1; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u16 csum:1, - routing:1, - key:1, - seq:1, - srr:1, - rec:3, - ack:1, - reserved:4, - version:3; -#else -#error "Adjust your defines" -#endif - __be16 protocol; -}; - -/* modified GRE header for PPTP */ -struct gre_hdr_pptp { - __u8 flags; /* bitfield */ - __u8 version; /* should be GRE_VERSION_PPTP */ - __be16 protocol; /* should be GRE_PROTOCOL_PPTP */ - __be16 payload_len; /* size of ppp payload, not inc. gre header */ - __be16 call_id; /* peer's call_id for this session */ - __be32 seq; /* sequence number. Present if S==1 */ - __be32 ack; /* seq number of highest packet recieved by */ - /* sender in this session */ -}; - - -/* this is part of ip_conntrack */ -struct ip_ct_gre { - unsigned int stream_timeout; - unsigned int timeout; -}; - -#ifdef __KERNEL__ -struct ip_conntrack_expect; -struct ip_conntrack; - -/* structure for original <-> reply keymap */ -struct ip_ct_gre_keymap { - struct list_head list; - - struct ip_conntrack_tuple tuple; -}; - -/* add new tuple->key_reply pair to keymap */ -int ip_ct_gre_keymap_add(struct ip_conntrack *ct, - struct ip_conntrack_tuple *t, - int reply); - -/* delete keymap entries */ -void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct); - - -/* get pointer to gre key, if present */ -static inline __be32 *gre_key(struct gre_hdr *greh) -{ - if (!greh->key) - return NULL; - if (greh->csum || greh->routing) - return (__be32 *) (greh+sizeof(*greh)+4); - return (__be32 *) (greh+sizeof(*greh)); -} - -/* get pointer ot gre csum, if present */ -static inline __sum16 *gre_csum(struct gre_hdr *greh) -{ - if (!greh->csum) - return NULL; - return (__sum16 *) (greh+sizeof(*greh)); -} - -#endif /* __KERNEL__ */ - -#endif /* _CONNTRACK_PROTO_GRE_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_protocol.h b/include/linux/netfilter_ipv4/ip_conntrack_protocol.h deleted file mode 100644 index 2c76b87..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_protocol.h +++ /dev/null @@ -1,98 +0,0 @@ -/* Header for use in defining a given protocol for connection tracking. */ -#ifndef _IP_CONNTRACK_PROTOCOL_H -#define _IP_CONNTRACK_PROTOCOL_H -#include -#include - -struct seq_file; - -struct ip_conntrack_protocol -{ - /* Protocol number. */ - u_int8_t proto; - - /* Protocol name */ - const char *name; - - /* Try to fill in the third arg: dataoff is offset past IP - hdr. Return true if possible. */ - int (*pkt_to_tuple)(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple); - - /* Invert the per-proto part of the tuple: ie. turn xmit into reply. - * Some packets can't be inverted: return 0 in that case. - */ - int (*invert_tuple)(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig); - - /* Print out the per-protocol part of the tuple. Return like seq_* */ - int (*print_tuple)(struct seq_file *, - const struct ip_conntrack_tuple *); - - /* Print out the private part of the conntrack. */ - int (*print_conntrack)(struct seq_file *, const struct ip_conntrack *); - - /* Returns verdict for packet, or -1 for invalid. */ - int (*packet)(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo); - - /* Called when a new connection for this protocol found; - * returns TRUE if it's OK. If so, packet() called next. */ - int (*new)(struct ip_conntrack *conntrack, const struct sk_buff *skb); - - /* Called when a conntrack entry is destroyed */ - void (*destroy)(struct ip_conntrack *conntrack); - - int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, - unsigned int hooknum); - - /* convert protoinfo to nfnetink attributes */ - int (*to_nfattr)(struct sk_buff *skb, struct nfattr *nfa, - const struct ip_conntrack *ct); - - /* convert nfnetlink attributes to protoinfo */ - int (*from_nfattr)(struct nfattr *tb[], struct ip_conntrack *ct); - - int (*tuple_to_nfattr)(struct sk_buff *skb, - const struct ip_conntrack_tuple *t); - int (*nfattr_to_tuple)(struct nfattr *tb[], - struct ip_conntrack_tuple *t); - - /* Module (if any) which this is connected to. */ - struct module *me; -}; - -/* Protocol registration. */ -extern int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto); -extern void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto); -/* Existing built-in protocols */ -extern struct ip_conntrack_protocol ip_conntrack_protocol_tcp; -extern struct ip_conntrack_protocol ip_conntrack_protocol_udp; -extern struct ip_conntrack_protocol ip_conntrack_protocol_icmp; -extern struct ip_conntrack_protocol ip_conntrack_generic_protocol; -extern int ip_conntrack_protocol_tcp_init(void); - -/* Log invalid packets */ -extern unsigned int ip_ct_log_invalid; - -extern int ip_ct_port_tuple_to_nfattr(struct sk_buff *, - const struct ip_conntrack_tuple *); -extern int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[], - struct ip_conntrack_tuple *); - -#ifdef CONFIG_SYSCTL -#ifdef DEBUG_INVALID_PACKETS -#define LOG_INVALID(proto) \ - (ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW) -#else -#define LOG_INVALID(proto) \ - ((ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW) \ - && net_ratelimit()) -#endif -#else -#define LOG_INVALID(proto) 0 -#endif /* CONFIG_SYSCTL */ - -#endif /*_IP_CONNTRACK_PROTOCOL_H*/ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sctp.h b/include/linux/netfilter_ipv4/ip_conntrack_sctp.h deleted file mode 100644 index 4099a04..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_sctp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _IP_CONNTRACK_SCTP_H -#define _IP_CONNTRACK_SCTP_H - -#include - -#endif /* _IP_CONNTRACK_SCTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sip.h b/include/linux/netfilter_ipv4/ip_conntrack_sip.h deleted file mode 100644 index bef6c64..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_sip.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef __IP_CONNTRACK_SIP_H__ -#define __IP_CONNTRACK_SIP_H__ -#ifdef __KERNEL__ - -#define SIP_PORT 5060 -#define SIP_TIMEOUT 3600 - -enum sip_header_pos { - POS_REG_REQ_URI, - POS_REQ_URI, - POS_FROM, - POS_TO, - POS_VIA, - POS_CONTACT, - POS_CONTENT, - POS_MEDIA, - POS_OWNER, - POS_CONNECTION, - POS_SDP_HEADER, -}; - -extern unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr); -extern unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp, - const char *dptr); - -extern int ct_sip_get_info(const char *dptr, size_t dlen, - unsigned int *matchoff, - unsigned int *matchlen, - enum sip_header_pos pos); -extern int ct_sip_lnlen(const char *line, const char *limit); -extern const char *ct_sip_search(const char *needle, const char *haystack, - size_t needle_len, size_t haystack_len, - int case_sensitive); -#endif /* __KERNEL__ */ -#endif /* __IP_CONNTRACK_SIP_H__ */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h b/include/linux/netfilter_ipv4/ip_conntrack_tcp.h deleted file mode 100644 index 876b8fb..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _IP_CONNTRACK_TCP_H -#define _IP_CONNTRACK_TCP_H - -#include - -#endif /* _IP_CONNTRACK_TCP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tftp.h b/include/linux/netfilter_ipv4/ip_conntrack_tftp.h deleted file mode 100644 index a404fc0..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_tftp.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _IP_CT_TFTP -#define _IP_CT_TFTP - -#define TFTP_PORT 69 - -struct tftphdr { - __be16 opcode; -}; - -#define TFTP_OPCODE_READ 1 -#define TFTP_OPCODE_WRITE 2 -#define TFTP_OPCODE_DATA 3 -#define TFTP_OPCODE_ACK 4 -#define TFTP_OPCODE_ERROR 5 - -extern unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp); - -#endif /* _IP_CT_TFTP */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h deleted file mode 100644 index c228bde..0000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef _IP_CONNTRACK_TUPLE_H -#define _IP_CONNTRACK_TUPLE_H - -#include -#include - -/* A `tuple' is a structure containing the information to uniquely - identify a connection. ie. if two packets have the same tuple, they - are in the same connection; if not, they are not. - - We divide the structure along "manipulatable" and - "non-manipulatable" lines, for the benefit of the NAT code. -*/ - -/* The protocol-specific manipulable parts of the tuple: always in - network order! */ -union ip_conntrack_manip_proto -{ - /* Add other protocols here. */ - u_int16_t all; - - struct { - __be16 port; - } tcp; - struct { - __be16 port; - } udp; - struct { - __be16 id; - } icmp; - struct { - __be16 port; - } sctp; - struct { - __be16 key; /* key is 32bit, pptp only uses 16 */ - } gre; -}; - -/* The manipulable part of the tuple. */ -struct ip_conntrack_manip -{ - __be32 ip; - union ip_conntrack_manip_proto u; -}; - -/* This contains the information to distinguish a connection. */ -struct ip_conntrack_tuple -{ - struct ip_conntrack_manip src; - - /* These are the parts of the tuple which are fixed. */ - struct { - __be32 ip; - union { - /* Add other protocols here. */ - u_int16_t all; - - struct { - __be16 port; - } tcp; - struct { - __be16 port; - } udp; - struct { - u_int8_t type, code; - } icmp; - struct { - __be16 port; - } sctp; - struct { - __be16 key; /* key is 32bit, - * pptp only uses 16 */ - } gre; - } u; - - /* The protocol. */ - u_int8_t protonum; - - /* The direction (for tuplehash) */ - u_int8_t dir; - } dst; -}; - -/* This is optimized opposed to a memset of the whole structure. Everything we - * really care about is the source/destination unions */ -#define IP_CT_TUPLE_U_BLANK(tuple) \ - do { \ - (tuple)->src.u.all = 0; \ - (tuple)->dst.u.all = 0; \ - } while (0) - -#ifdef __KERNEL__ - -#define DUMP_TUPLE(tp) \ -DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n", \ - (tp), (tp)->dst.protonum, \ - NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all), \ - NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all)) - -/* If we're the first tuple, it's the original dir. */ -#define DIRECTION(h) ((enum ip_conntrack_dir)(h)->tuple.dst.dir) - -/* Connections have two entries in the hash table: one for each way */ -struct ip_conntrack_tuple_hash -{ - struct list_head list; - - struct ip_conntrack_tuple tuple; -}; - -#endif /* __KERNEL__ */ - -static inline int ip_ct_tuple_src_equal(const struct ip_conntrack_tuple *t1, - const struct ip_conntrack_tuple *t2) -{ - return t1->src.ip == t2->src.ip - && t1->src.u.all == t2->src.u.all; -} - -static inline int ip_ct_tuple_dst_equal(const struct ip_conntrack_tuple *t1, - const struct ip_conntrack_tuple *t2) -{ - return t1->dst.ip == t2->dst.ip - && t1->dst.u.all == t2->dst.u.all - && t1->dst.protonum == t2->dst.protonum; -} - -static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1, - const struct ip_conntrack_tuple *t2) -{ - return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2); -} - -static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask) -{ - return !(((t->src.ip ^ tuple->src.ip) & mask->src.ip) - || ((t->dst.ip ^ tuple->dst.ip) & mask->dst.ip) - || ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all) - || ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all) - || ((t->dst.protonum ^ tuple->dst.protonum) - & mask->dst.protonum)); -} - -#endif /* _IP_CONNTRACK_TUPLE_H */ diff --git a/include/linux/netfilter_ipv4/ip_nat.h b/include/linux/netfilter_ipv4/ip_nat.h deleted file mode 100644 index bbca89a..0000000 --- a/include/linux/netfilter_ipv4/ip_nat.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _IP_NAT_H -#define _IP_NAT_H -#include -#include - -#define IP_NAT_MAPPING_TYPE_MAX_NAMELEN 16 - -enum ip_nat_manip_type -{ - IP_NAT_MANIP_SRC, - IP_NAT_MANIP_DST -}; - -/* SRC manip occurs POST_ROUTING or LOCAL_IN */ -#define HOOK2MANIP(hooknum) ((hooknum) != NF_IP_POST_ROUTING && (hooknum) != NF_IP_LOCAL_IN) - -#define IP_NAT_RANGE_MAP_IPS 1 -#define IP_NAT_RANGE_PROTO_SPECIFIED 2 -#define IP_NAT_RANGE_PROTO_RANDOM 4 /* add randomness to "port" selection */ - -/* NAT sequence number modifications */ -struct ip_nat_seq { - /* position of the last TCP sequence number - * modification (if any) */ - u_int32_t correction_pos; - /* sequence number offset before and after last modification */ - int16_t offset_before, offset_after; -}; - -/* Single range specification. */ -struct ip_nat_range -{ - /* Set to OR of flags above. */ - unsigned int flags; - - /* Inclusive: network order. */ - __be32 min_ip, max_ip; - - /* Inclusive: network order */ - union ip_conntrack_manip_proto min, max; -}; - -/* For backwards compat: don't use in modern code. */ -struct ip_nat_multi_range_compat -{ - unsigned int rangesize; /* Must be 1. */ - - /* hangs off end. */ - struct ip_nat_range range[1]; -}; - -#ifdef __KERNEL__ -#include - -/* Protects NAT hash tables, and NAT-private part of conntracks. */ -extern rwlock_t ip_nat_lock; - -/* The structure embedded in the conntrack structure. */ -struct ip_nat_info -{ - struct list_head bysource; - struct ip_nat_seq seq[IP_CT_DIR_MAX]; -}; - -struct ip_conntrack; - -/* Set up the info structure to map into this range. */ -extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack, - const struct ip_nat_range *range, - unsigned int hooknum); - -/* Is this tuple already taken? (not by us)*/ -extern int ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -#else /* !__KERNEL__: iptables wants this to compile. */ -#define ip_nat_multi_range ip_nat_multi_range_compat -#endif /*__KERNEL__*/ -#endif diff --git a/include/linux/netfilter_ipv4/ip_nat_core.h b/include/linux/netfilter_ipv4/ip_nat_core.h deleted file mode 100644 index 60566f9..0000000 --- a/include/linux/netfilter_ipv4/ip_nat_core.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _IP_NAT_CORE_H -#define _IP_NAT_CORE_H -#include -#include - -/* This header used to share core functionality between the standalone - NAT module, and the compatibility layer's use of NAT for masquerading. */ - -extern unsigned int ip_nat_packet(struct ip_conntrack *ct, - enum ip_conntrack_info conntrackinfo, - unsigned int hooknum, - struct sk_buff **pskb); - -extern int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff **pskb); -#endif /* _IP_NAT_CORE_H */ diff --git a/include/linux/netfilter_ipv4/ip_nat_helper.h b/include/linux/netfilter_ipv4/ip_nat_helper.h deleted file mode 100644 index bf9cb10..0000000 --- a/include/linux/netfilter_ipv4/ip_nat_helper.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _IP_NAT_HELPER_H -#define _IP_NAT_HELPER_H -/* NAT protocol helper routines. */ - -#include -#include - -struct sk_buff; - -/* These return true or false. */ -extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len); -extern int ip_nat_mangle_udp_packet(struct sk_buff **skb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len); -extern int ip_nat_seq_adjust(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo); - -/* Setup NAT on this expected conntrack so it follows master, but goes - * to port ct->master->saved_proto. */ -extern void ip_nat_follow_master(struct ip_conntrack *ct, - struct ip_conntrack_expect *this); -#endif diff --git a/include/linux/netfilter_ipv4/ip_nat_pptp.h b/include/linux/netfilter_ipv4/ip_nat_pptp.h deleted file mode 100644 index 36668bf..0000000 --- a/include/linux/netfilter_ipv4/ip_nat_pptp.h +++ /dev/null @@ -1,11 +0,0 @@ -/* PPTP constants and structs */ -#ifndef _NAT_PPTP_H -#define _NAT_PPTP_H - -/* conntrack private data */ -struct ip_nat_pptp { - __be16 pns_call_id; /* NAT'ed PNS call id */ - __be16 pac_call_id; /* NAT'ed PAC call id */ -}; - -#endif /* _NAT_PPTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_nat_protocol.h b/include/linux/netfilter_ipv4/ip_nat_protocol.h deleted file mode 100644 index 612a436..0000000 --- a/include/linux/netfilter_ipv4/ip_nat_protocol.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Header for use in defining a given protocol. */ -#ifndef _IP_NAT_PROTOCOL_H -#define _IP_NAT_PROTOCOL_H -#include -#include - -#include -#include - -struct iphdr; -struct ip_nat_range; - -struct ip_nat_protocol -{ - /* Protocol name */ - const char *name; - - /* Protocol number. */ - unsigned int protonum; - - struct module *me; - - /* Translate a packet to the target according to manip type. - Return true if succeeded. */ - int (*manip_pkt)(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype); - - /* Is the manipable part of the tuple between min and max incl? */ - int (*in_range)(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max); - - /* Alter the per-proto part of the tuple (depending on - maniptype), to give a unique tuple in the given range if - possible; return false if not. Per-protocol part of tuple - is initialized to the incoming packet. */ - int (*unique_tuple)(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack); - - int (*range_to_nfattr)(struct sk_buff *skb, - const struct ip_nat_range *range); - - int (*nfattr_to_range)(struct nfattr *tb[], - struct ip_nat_range *range); -}; - -/* Protocol registration. */ -extern int ip_nat_protocol_register(struct ip_nat_protocol *proto); -extern void ip_nat_protocol_unregister(struct ip_nat_protocol *proto); - -extern struct ip_nat_protocol *ip_nat_proto_find_get(u_int8_t protocol); -extern void ip_nat_proto_put(struct ip_nat_protocol *proto); - -/* Built-in protocols. */ -extern struct ip_nat_protocol ip_nat_protocol_tcp; -extern struct ip_nat_protocol ip_nat_protocol_udp; -extern struct ip_nat_protocol ip_nat_protocol_icmp; -extern struct ip_nat_protocol ip_nat_unknown_protocol; - -extern int init_protocols(void) __init; -extern void cleanup_protocols(void); -extern struct ip_nat_protocol *find_nat_proto(u_int16_t protonum); - -extern int ip_nat_port_range_to_nfattr(struct sk_buff *skb, - const struct ip_nat_range *range); -extern int ip_nat_port_nfattr_to_range(struct nfattr *tb[], - struct ip_nat_range *range); - -#endif /*_IP_NAT_PROTO_H*/ diff --git a/include/linux/netfilter_ipv4/ip_nat_rule.h b/include/linux/netfilter_ipv4/ip_nat_rule.h deleted file mode 100644 index 73b9552..0000000 --- a/include/linux/netfilter_ipv4/ip_nat_rule.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _IP_NAT_RULE_H -#define _IP_NAT_RULE_H -#include -#include -#include - -#ifdef __KERNEL__ - -extern int ip_nat_rule_init(void) __init; -extern void ip_nat_rule_cleanup(void); -extern int ip_nat_rule_find(struct sk_buff **pskb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - struct ip_conntrack *ct, - struct ip_nat_info *info); - -extern unsigned int -alloc_null_binding(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum); - -extern unsigned int -alloc_null_binding_confirmed(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum); -#endif -#endif /* _IP_NAT_RULE_H */ diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h index cc4c0b2..be6e682 100644 --- a/include/linux/netfilter_ipv4/ipt_SAME.h +++ b/include/linux/netfilter_ipv4/ipt_SAME.h @@ -13,7 +13,7 @@ struct ipt_same_info u_int32_t *iparray; /* hangs off end. */ - struct ip_nat_range range[IPT_SAME_MAX_RANGE]; + struct nf_nat_range range[IPT_SAME_MAX_RANGE]; }; #endif /*_IPT_SAME_H*/ diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 2a20f48..f41688f 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -138,6 +138,11 @@ #ifdef __KERNEL__ #include #include +static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb) +{ + return (struct nlmsghdr *)skb->data; +} + struct netlink_skb_parms { struct ucred creds; /* Skb credentials */ @@ -152,7 +157,10 @@ #define NETLINK_CB(skb) (*(struct netli #define NETLINK_CREDS(skb) (&NETLINK_CB((skb)).creds) -extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module); +extern struct sock *netlink_kernel_create(int unit, unsigned int groups, + void (*input)(struct sock *sk, int len), + struct mutex *cb_mutex, + struct module *module); extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); extern int netlink_has_listeners(struct sock *sk, unsigned int group); extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); @@ -171,9 +179,16 @@ int netlink_sendskb(struct sock *sk, str /* * skb should fit one page. This choice is good for headerless malloc. + * But we should limit to 8K so that userspace does not have to + * use enormous buffer sizes on recvmsg() calls just to avoid + * MSG_TRUNC when PAGE_SIZE is very large. */ -#define NLMSG_GOODORDER 0 -#define NLMSG_GOODSIZE (SKB_MAX_ORDER(0, NLMSG_GOODORDER)) +#if PAGE_SIZE < 8192UL +#define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(PAGE_SIZE) +#else +#define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(8192UL) +#endif + #define NLMSG_DEFAULT_SIZE (NLMSG_GOODSIZE - NLMSG_HDRLEN) @@ -217,18 +232,6 @@ ({ if (skb_tailroom(skb) < (int)NLMSG_SP #define NLMSG_PUT(skb, pid, seq, type, len) \ NLMSG_NEW(skb, pid, seq, type, len, 0) -#define NLMSG_NEW_ANSWER(skb, cb, type, len, flags) \ - NLMSG_NEW(skb, NETLINK_CB((cb)->skb).pid, \ - (cb)->nlh->nlmsg_seq, type, len, flags) - -#define NLMSG_END(skb, nlh) \ -({ (nlh)->nlmsg_len = (skb)->tail - (unsigned char *) (nlh); \ - (skb)->len; }) - -#define NLMSG_CANCEL(skb, nlh) \ -({ skb_trim(skb, (unsigned char *) (nlh) - (skb)->data); \ - -1; }) - extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, struct nlmsghdr *nlh, int (*dump)(struct sk_buff *skb, struct netlink_callback*), diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h new file mode 100644 index 0000000..9a30ba2 --- /dev/null +++ b/include/linux/nl80211.h @@ -0,0 +1,38 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006, 2007 Johannes Berg + */ + +/** + * enum nl80211_iftype - (virtual) interface types + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @__NL80211_IFTYPE_AFTER_LAST: internal use + * + * These values are used with the NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + + /* keep last */ + __NL80211_IFTYPE_AFTER_LAST +}; +#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1) + +#endif /* __LINUX_NL80211_H */ diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 9cd0d0e..9632659 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -133,7 +133,7 @@ #ifdef CONFIG_S390 static inline void SetPageUptodate(struct page *page) { if (!test_and_set_bit(PG_uptodate, &page->flags)) - page_test_and_clear_dirty(page); + page_clear_dirty(page); } #else #define SetPageUptodate(page) set_bit(PG_uptodate, &(page)->flags) diff --git a/include/linux/pci.h b/include/linux/pci.h index 481ea06..9724910 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -361,8 +361,6 @@ struct pci_driver { struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; - - int multithread_probe; }; #define to_pci_driver(drv) container_of(drv,struct pci_driver, driver) @@ -840,6 +838,7 @@ void __iomem * pcim_iomap(struct pci_dev void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr); void __iomem * const * pcim_iomap_table(struct pci_dev *pdev); int pcim_iomap_regions(struct pci_dev *pdev, u16 mask, const char *name); +void pcim_iounmap_regions(struct pci_dev *pdev, u16 mask); extern int pci_pci_problems; #define PCIPCI_FAIL 1 /* No PCI PCI DMA */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 600308f..1b0ddbb 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -368,7 +368,6 @@ #define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4 #define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 #define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a #define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380 -#define PCI_DEVICE_ID_ATI_IXP600_SRAID 0x4381 #define PCI_DEVICE_ID_ATI_IXP600_SMBUS 0x4385 #define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c @@ -1459,6 +1458,8 @@ #define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0 #define PCI_VENDOR_ID_TOSHIBA_2 0x102f #define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU 0x0031 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939 0x0032 #define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE 0x0105 #define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108 #define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3 diff --git a/include/linux/phy.h b/include/linux/phy.h index edd4c88..2a65978 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -55,6 +55,7 @@ typedef enum { PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, + PHY_INTERFACE_MODE_RGMII_ID, PHY_INTERFACE_MODE_RTBI } phy_interface_t; diff --git a/include/linux/pm.h b/include/linux/pm.h index 21db05a..6e8fa30 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -112,21 +112,25 @@ #define PM_SUSPEND_MAX ((__force suspen typedef int __bitwise suspend_disk_method_t; -#define PM_DISK_FIRMWARE ((__force suspend_disk_method_t) 1) -#define PM_DISK_PLATFORM ((__force suspend_disk_method_t) 2) -#define PM_DISK_SHUTDOWN ((__force suspend_disk_method_t) 3) -#define PM_DISK_REBOOT ((__force suspend_disk_method_t) 4) -#define PM_DISK_TEST ((__force suspend_disk_method_t) 5) -#define PM_DISK_TESTPROC ((__force suspend_disk_method_t) 6) -#define PM_DISK_MAX ((__force suspend_disk_method_t) 7) +/* invalid must be 0 so struct pm_ops initialisers can leave it out */ +#define PM_DISK_INVALID ((__force suspend_disk_method_t) 0) +#define PM_DISK_PLATFORM ((__force suspend_disk_method_t) 1) +#define PM_DISK_SHUTDOWN ((__force suspend_disk_method_t) 2) +#define PM_DISK_REBOOT ((__force suspend_disk_method_t) 3) +#define PM_DISK_TEST ((__force suspend_disk_method_t) 4) +#define PM_DISK_TESTPROC ((__force suspend_disk_method_t) 5) +#define PM_DISK_MAX ((__force suspend_disk_method_t) 6) /** * struct pm_ops - Callbacks for managing platform dependent suspend states. * @valid: Callback to determine whether the given state can be entered. * If %CONFIG_SOFTWARE_SUSPEND is set then %PM_SUSPEND_DISK is - * always valid and never passed to this call. - * If not assigned, all suspend states are advertised as valid - * in /sys/power/state (but can still be rejected by prepare or enter.) + * always valid and never passed to this call. If not assigned, + * no suspend states are valid. + * Valid states are advertised in /sys/power/state but can still + * be rejected by prepare or enter if the conditions aren't right. + * There is a %pm_valid_only_mem function available that can be assigned + * to this if you only implement mem sleep. * * @prepare: Prepare the platform for the given suspend state. Can return a * negative error code if necessary. @@ -137,17 +141,16 @@ #define PM_DISK_MAX ((__force suspend_d * @finish: Called when the system has left the given state and all devices * are resumed. The return value is ignored. * - * @pm_disk_mode: Set to the disk method that the user should be able to - * configure for suspend-to-disk. Since %PM_DISK_SHUTDOWN, - * %PM_DISK_REBOOT, %PM_DISK_TEST and %PM_DISK_TESTPROC - * are always allowed, currently only %PM_DISK_PLATFORM - * makes sense. If the user then choses %PM_DISK_PLATFORM, - * the @prepare call will be called before suspending to disk - * (if present), the @enter call should be present and will - * be called after all state has been saved and the machine - * is ready to be shut down/suspended/..., and the @finish - * callback is called after state has been restored. All - * these calls are called with %PM_SUSPEND_DISK as the state. + * @pm_disk_mode: The generic code always allows one of the shutdown methods + * %PM_DISK_SHUTDOWN, %PM_DISK_REBOOT, %PM_DISK_TEST and + * %PM_DISK_TESTPROC. If this variable is set, the mode it is set + * to is allowed in addition to those modes and is also made default. + * When this mode is sent selected, the @prepare call will be called + * before suspending to disk (if present), the @enter call should be + * present and will be called after all state has been saved and the + * machine is ready to be powered off; the @finish callback is called + * after state has been restored. All these calls are called with + * %PM_SUSPEND_DISK as the state. */ struct pm_ops { int (*valid)(suspend_state_t state); @@ -165,6 +168,25 @@ extern void pm_set_ops(struct pm_ops *pm extern struct pm_ops *pm_ops; extern int pm_suspend(suspend_state_t state); +extern int pm_valid_only_mem(suspend_state_t state); + +/** + * arch_suspend_disable_irqs - disable IRQs for suspend + * + * Disables IRQs (in the default case). This is a weak symbol in the common + * code and thus allows architectures to override it if more needs to be + * done. Not called for suspend to disk. + */ +extern void arch_suspend_disable_irqs(void); + +/** + * arch_suspend_enable_irqs - enable IRQs after suspend + * + * Enables IRQs (in the default case). This is a weak symbol in the common + * code and thus allows architectures to override it if more needs to be + * done. Not called for suspend to disk. + */ +extern void arch_suspend_enable_irqs(void); /* * Device power management @@ -273,6 +295,20 @@ #define suspend_report_result(fn, ret) __suspend_report_result(__FUNCTION__, fn, ret); \ } while (0) +/* + * Platform hook to activate device wakeup capability, if that's not already + * handled by enable_irq_wake() etc. + * Returns zero on success, else negative errno + */ +extern int (*platform_enable_wakeup)(struct device *dev, int is_on); + +static inline int call_platform_enable_wakeup(struct device *dev, int is_on) +{ + if (platform_enable_wakeup) + return (*platform_enable_wakeup)(dev, is_on); + return 0; +} + #else /* !CONFIG_PM */ static inline int device_suspend(pm_message_t state) @@ -294,6 +330,11 @@ static inline void dpm_runtime_resume(st #define suspend_report_result(fn, ret) do { } while (0) +static inline int call_platform_enable_wakeup(struct device *dev, int is_on) +{ + return 0; +} + #endif /* changes to device_may_wakeup take effect on the next pm state change. diff --git a/include/linux/pmu.h b/include/linux/pmu.h index 7831773..b0952e5 100644 --- a/include/linux/pmu.h +++ b/include/linux/pmu.h @@ -168,24 +168,16 @@ #include struct pmu_sleep_notifier { - int (*notifier_call)(struct pmu_sleep_notifier *self, int when); + void (*notifier_call)(struct pmu_sleep_notifier *self, int when); int priority; struct list_head list; }; /* Code values for calling sleep/wakeup handlers - * - * Note: If a sleep request got cancelled, all drivers will get - * the PBOOK_SLEEP_REJECT, even those who didn't get the PBOOK_SLEEP_REQUEST. */ #define PBOOK_SLEEP_REQUEST 1 #define PBOOK_SLEEP_NOW 2 -#define PBOOK_SLEEP_REJECT 3 -#define PBOOK_WAKE 4 - -/* Result codes returned by the notifiers */ -#define PBOOK_SLEEP_OK 0 -#define PBOOK_SLEEP_REFUSE -1 +#define PBOOK_WAKE 3 /* priority levels in notifiers */ #define SLEEP_LEVEL_VIDEO 100 /* Video driver (first wake) */ diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 4a629ea..1fae30a 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -574,13 +574,6 @@ extern int rtattr_parse(struct rtattr *t #define rtattr_parse_nested(tb, max, rta) \ rtattr_parse((tb), (max), RTA_DATA((rta)), RTA_PAYLOAD((rta))) -struct rtnetlink_link -{ - int (*doit)(struct sk_buff *, struct nlmsghdr*, void *attr); - int (*dumpit)(struct sk_buff *, struct netlink_callback *cb); -}; - -extern struct rtnetlink_link * rtnetlink_links[NPROTO]; extern int rtnetlink_send(struct sk_buff *skb, u32 pid, u32 group, int echo); extern int rtnl_unicast(struct sk_buff *skb, u32 pid); extern int rtnl_notify(struct sk_buff *skb, u32 pid, u32 group, @@ -605,7 +598,7 @@ ({ if (unlikely(skb_tailroom(skb) < (int #define RTA_PUT_NOHDR(skb, attrlen, data) \ ({ RTA_APPEND(skb, RTA_ALIGN(attrlen), data); \ - memset(skb->tail - (RTA_ALIGN(attrlen) - attrlen), 0, \ + memset(skb_tail_pointer(skb) - (RTA_ALIGN(attrlen) - attrlen), 0, \ RTA_ALIGN(attrlen) - attrlen); }) #define RTA_PUT_U8(skb, attrtype, value) \ @@ -637,12 +630,12 @@ #define RTA_PUT_FLAG(skb, attrtype) \ RTA_PUT(skb, attrtype, 0, NULL); #define RTA_NEST(skb, type) \ -({ struct rtattr *__start = (struct rtattr *) (skb)->tail; \ +({ struct rtattr *__start = (struct rtattr *)skb_tail_pointer(skb); \ RTA_PUT(skb, type, 0, NULL); \ __start; }) #define RTA_NEST_END(skb, start) \ -({ (start)->rta_len = ((skb)->tail - (unsigned char *) (start)); \ +({ (start)->rta_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ (skb)->len; }) #define RTA_NEST_CANCEL(skb, start) \ diff --git a/include/linux/rxrpc.h b/include/linux/rxrpc.h new file mode 100644 index 0000000..f7b826b --- /dev/null +++ b/include/linux/rxrpc.h @@ -0,0 +1,62 @@ +/* AF_RXRPC parameters + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef _LINUX_RXRPC_H +#define _LINUX_RXRPC_H + +#include +#include + +/* + * RxRPC socket address + */ +struct sockaddr_rxrpc { + sa_family_t srx_family; /* address family */ + u16 srx_service; /* service desired */ + u16 transport_type; /* type of transport socket (SOCK_DGRAM) */ + u16 transport_len; /* length of transport address */ + union { + sa_family_t family; /* transport address family */ + struct sockaddr_in sin; /* IPv4 transport address */ + struct sockaddr_in6 sin6; /* IPv6 transport address */ + } transport; +}; + +/* + * RxRPC socket options + */ +#define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */ +#define RXRPC_SECURITY_KEYRING 2 /* [srvr] set ring of server security keys */ +#define RXRPC_EXCLUSIVE_CONNECTION 3 /* [clnt] use exclusive RxRPC connection */ +#define RXRPC_MIN_SECURITY_LEVEL 4 /* minimum security level */ + +/* + * RxRPC control messages + * - terminal messages mean that a user call ID tag can be recycled + */ +#define RXRPC_USER_CALL_ID 1 /* user call ID specifier */ +#define RXRPC_ABORT 2 /* abort request / notification [terminal] */ +#define RXRPC_ACK 3 /* [Server] RPC op final ACK received [terminal] */ +#define RXRPC_NET_ERROR 5 /* network error received [terminal] */ +#define RXRPC_BUSY 6 /* server busy received [terminal] */ +#define RXRPC_LOCAL_ERROR 7 /* local error generated [terminal] */ +#define RXRPC_NEW_CALL 8 /* [Server] new incoming call notification */ +#define RXRPC_ACCEPT 9 /* [Server] accept request */ + +/* + * RxRPC security levels + */ +#define RXRPC_SECURITY_PLAIN 0 /* plain secure-checksummed packets only */ +#define RXRPC_SECURITY_AUTH 1 /* authenticated packets */ +#define RXRPC_SECURITY_ENCRYPT 2 /* encrypted packets */ + + +#endif /* _LINUX_RXRPC_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 49fe299..a170758 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -196,13 +196,13 @@ extern void init_idle(struct task_struct extern cpumask_t nohz_cpu_mask; /* - * Only dump TASK_* tasks. (-1 for all tasks) + * Only dump TASK_* tasks. (0 for all tasks) */ extern void show_state_filter(unsigned long state_filter); static inline void show_state(void) { - show_state_filter(-1); + show_state_filter(0); } extern void show_regs(struct pt_regs *); diff --git a/include/linux/sctp.h b/include/linux/sctp.h index d4f8656..d70df61 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -63,6 +63,15 @@ typedef struct sctphdr { __be32 checksum; } __attribute__((packed)) sctp_sctphdr_t; +#ifdef __KERNEL__ +#include + +static inline struct sctphdr *sctp_hdr(const struct sk_buff *skb) +{ + return (struct sctphdr *)skb_transport_header(skb); +} +#endif + /* Section 3.2. Chunk Field Descriptions. */ typedef struct sctp_chunkhdr { __u8 type; diff --git a/include/linux/sdla_fr.h b/include/linux/sdla_fr.h deleted file mode 100644 index cdfa77f..0000000 --- a/include/linux/sdla_fr.h +++ /dev/null @@ -1,638 +0,0 @@ -/***************************************************************************** -* sdla_fr.h Sangoma frame relay firmware API definitions. -* -* Author: Gideon Hack -* Nenad Corbic -* -* Copyright: (c) 1995-2000 Sangoma Technologies Inc. -* -* 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. -* ============================================================================ -* Oct 04, 1999 Gideon Hack Updated API structures -* Jun 02, 1999 Gideon Hack Modifications for S514 support -* Oct 12, 1997 Jaspreet Singh Added FR_READ_DLCI_IB_MAPPING -* Jul 21, 1997 Jaspreet Singh Changed FRRES_TOO_LONG and FRRES_TOO_MANY to -* 0x05 and 0x06 respectively. -* Dec 23, 1996 Gene Kozin v2.0 -* Apr 29, 1996 Gene Kozin v1.0 (merged version S502 & S508 definitions). -* Sep 26, 1995 Gene Kozin Initial version. -*****************************************************************************/ -#ifndef _SDLA_FR_H -#define _SDLA_FR_H - -/*---------------------------------------------------------------------------- - * Notes: - * ------ - * 1. All structures defined in this file are byte-alined. - * - * Compiler Platform - * -------- -------- - * GNU C Linux - */ - -#ifndef PACKED -# define PACKED __attribute__((packed)) -#endif /* PACKED */ - -/* Adapter memory layout */ -#define FR_MB_VECTOR 0xE000 /* mailbox window vector */ -#define FR502_RX_VECTOR 0xA000 /* S502 direct receive window vector */ -#define FR502_MBOX_OFFS 0xF60 /* S502 mailbox offset */ -#define FR508_MBOX_OFFS 0 /* S508 mailbox offset */ -#define FR502_FLAG_OFFS 0x1FF0 /* S502 status flags offset */ -#define FR508_FLAG_OFFS 0x1000 /* S508 status flags offset */ -#define FR502_RXMB_OFFS 0x900 /* S502 direct receive mailbox offset */ -#define FR508_TXBC_OFFS 0x1100 /* S508 Tx buffer info offset */ -#define FR508_RXBC_OFFS 0x1120 /* S508 Rx buffer info offset */ - -/* Important constants */ -#define FR502_MAX_DATA 4096 /* maximum data buffer length */ -#define FR508_MAX_DATA 4080 /* maximum data buffer length */ -#define MIN_LGTH_FR_DATA_CFG 300 /* min Information frame length -(for configuration purposes) */ -#define FR_MAX_NO_DATA_BYTES_IN_FRAME 15354 /* max Information frame length */ - -#define HIGHEST_VALID_DLCI 991 - -/****** Data Structures *****************************************************/ - -/*---------------------------------------------------------------------------- - * Frame relay command block. - */ -typedef struct fr_cmd -{ - unsigned char command PACKED; /* command code */ - unsigned short length PACKED; /* length of data buffer */ - unsigned char result PACKED; /* return code */ - unsigned short dlci PACKED; /* DLCI number */ - unsigned char attr PACKED; /* FECN, BECN, DE and C/R bits */ - unsigned short rxlost1 PACKED; /* frames discarded at int. level */ - unsigned long rxlost2 PACKED; /* frames discarded at app. level */ - unsigned char rsrv[2] PACKED; /* reserved for future use */ -} fr_cmd_t; - -/* 'command' field defines */ -#define FR_WRITE 0x01 -#define FR_READ 0x02 -#define FR_ISSUE_IS_FRAME 0x03 -#define FR_SET_CONFIG 0x10 -#define FR_READ_CONFIG 0x11 -#define FR_COMM_DISABLE 0x12 -#define FR_COMM_ENABLE 0x13 -#define FR_READ_STATUS 0x14 -#define FR_READ_STATISTICS 0x15 -#define FR_FLUSH_STATISTICS 0x16 -#define FR_LIST_ACTIVE_DLCI 0x17 -#define FR_FLUSH_DATA_BUFFERS 0x18 -#define FR_READ_ADD_DLC_STATS 0x19 -#define FR_ADD_DLCI 0x20 -#define FR_DELETE_DLCI 0x21 -#define FR_ACTIVATE_DLCI 0x22 -#define FR_DEACTIVATE_DLCI 0x22 -#define FR_READ_MODEM_STATUS 0x30 -#define FR_SET_MODEM_STATUS 0x31 -#define FR_READ_ERROR_STATS 0x32 -#define FR_FLUSH_ERROR_STATS 0x33 -#define FR_READ_DLCI_IB_MAPPING 0x34 -#define FR_READ_CODE_VERSION 0x40 -#define FR_SET_INTR_MODE 0x50 -#define FR_READ_INTR_MODE 0x51 -#define FR_SET_TRACE_CONFIG 0x60 -#define FR_FT1_STATUS_CTRL 0x80 -#define FR_SET_FT1_MODE 0x81 - -/* Special UDP drivers management commands */ -#define FPIPE_ENABLE_TRACING 0x41 -#define FPIPE_DISABLE_TRACING 0x42 -#define FPIPE_GET_TRACE_INFO 0x43 -#define FPIPE_FT1_READ_STATUS 0x44 -#define FPIPE_DRIVER_STAT_IFSEND 0x45 -#define FPIPE_DRIVER_STAT_INTR 0x46 -#define FPIPE_DRIVER_STAT_GEN 0x47 -#define FPIPE_FLUSH_DRIVER_STATS 0x48 -#define FPIPE_ROUTER_UP_TIME 0x49 - -/* 'result' field defines */ -#define FRRES_OK 0x00 /* command executed successfully */ -#define FRRES_DISABLED 0x01 /* communications not enabled */ -#define FRRES_INOPERATIVE 0x02 /* channel inoperative */ -#define FRRES_DLCI_INACTIVE 0x03 /* DLCI is inactive */ -#define FRRES_DLCI_INVALID 0x04 /* DLCI is not configured */ -#define FRRES_TOO_LONG 0x05 -#define FRRES_TOO_MANY 0x06 -#define FRRES_CIR_OVERFLOW 0x07 /* Tx throughput has exceeded CIR */ -#define FRRES_BUFFER_OVERFLOW 0x08 -#define FRRES_MODEM_FAILURE 0x10 /* DCD and/or CTS dropped */ -#define FRRES_CHANNEL_DOWN 0x11 /* channel became inoperative */ -#define FRRES_CHANNEL_UP 0x12 /* channel became operative */ -#define FRRES_DLCI_CHANGE 0x13 /* DLCI status (or number) changed */ -#define FRRES_DLCI_MISMATCH 0x14 -#define FRRES_INVALID_CMD 0x1F /* invalid command */ - -/* 'attr' field defines */ -#define FRATTR_ - -/*---------------------------------------------------------------------------- - * Frame relay mailbox. - * This structure is located at offset FR50?_MBOX_OFFS into FR_MB_VECTOR. - * For S502 it is also located at offset FR502_RXMB_OFFS into - * FR502_RX_VECTOR. - */ -typedef struct fr_mbox -{ - unsigned char opflag PACKED; /* 00h: execution flag */ - fr_cmd_t cmd PACKED; /* 01h: command block */ - unsigned char data[1] PACKED; /* 10h: variable length data buffer */ -} fr_mbox_t; - -/*---------------------------------------------------------------------------- - * S502 frame relay status flags. - * This structure is located at offset FR502_FLAG_OFFS into FR_MB_VECTOR. - */ -typedef struct fr502_flags -{ - unsigned char rsrv1[1] PACKED; /* 00h: */ - unsigned char tx_ready PACKED; /* 01h: Tx buffer available */ - unsigned char rx_ready PACKED; /* 02h: Rx frame available */ - unsigned char event PACKED; /* 03h: asynchronous event */ - unsigned char mstatus PACKED; /* 04h: modem status */ - unsigned char rsrv2[8] PACKED; /* 05h: */ - unsigned char iflag PACKED; /* 0Dh: interrupt flag */ - unsigned char imask PACKED; /* 0Eh: interrupt mask */ -} fr502_flags_t; - -/*---------------------------------------------------------------------------- - * S508 frame relay status flags. - * This structure is located at offset FR508_FLAG_OFFS into FR_MB_VECTOR. - */ -typedef struct fr508_flags -{ - unsigned char rsrv1[3] PACKED; /* 00h: reserved */ - unsigned char event PACKED; /* 03h: asynchronous event */ - unsigned char mstatus PACKED; /* 04h: modem status */ - unsigned char rsrv2[11] PACKED; /* 05h: reserved */ - unsigned char iflag PACKED; /* 10h: interrupt flag */ - unsigned char imask PACKED; /* 11h: interrupt mask */ - unsigned long tse_offs PACKED; /* 12h: Tx status element */ - unsigned short dlci PACKED; /* 16h: DLCI NUMBER */ -} fr508_flags_t; - -/* 'event' field defines */ -#define FR_EVENT_STATUS 0x01 /* channel status change */ -#define FR_EVENT_DLC_STATUS 0x02 /* DLC status change */ -#define FR_EVENT_BAD_DLCI 0x04 /* FSR included wrong DLCI */ -#define FR_EVENT_LINK_DOWN 0x40 /* DCD or CTS low */ - -/* 'mstatus' field defines */ -#define FR_MDM_DCD 0x08 /* mdm_status: DCD */ -#define FR_MDM_CTS 0x20 /* mdm_status: CTS */ - -/* 'iflag' & 'imask' fields defines */ -#define FR_INTR_RXRDY 0x01 /* Rx ready */ -#define FR_INTR_TXRDY 0x02 /* Tx ready */ -#define FR_INTR_MODEM 0x04 /* modem status change (DCD, CTS) */ -#define FR_INTR_READY 0x08 /* interface command completed */ -#define FR_INTR_DLC 0x10 /* DLC status change */ -#define FR_INTR_TIMER 0x20 /* millisecond timer */ -#define FR_INTR_TX_MULT_DLCIs 0x80 /* Tx interrupt on multiple DLCIs */ - - -/*---------------------------------------------------------------------------- - * Receive Buffer Configuration Info. S508 only! - * This structure is located at offset FR508_RXBC_OFFS into FR_MB_VECTOR. - */ -typedef struct fr_buf_info -{ - unsigned short rse_num PACKED; /* 00h: number of status elements */ - unsigned long rse_base PACKED; /* 02h: receive status array base */ - unsigned long rse_next PACKED; /* 06h: next status element */ - unsigned long buf_base PACKED; /* 0Ah: rotational buffer base */ - unsigned short reserved PACKED; /* 0Eh: */ - unsigned long buf_top PACKED; /* 10h: rotational buffer top */ -} fr_buf_info_t; - -/*---------------------------------------------------------------------------- - * Buffer Status Element. S508 only! - * Array of structures of this type is located at offset defined by the - * 'rse_base' field of the frBufInfo_t structure into absolute adapter - * memory address space. - */ -typedef struct fr_rx_buf_ctl -{ - unsigned char flag PACKED; /* 00h: ready flag */ - unsigned short length PACKED; /* 01h: frame length */ - unsigned short dlci PACKED; /* 03h: DLCI */ - unsigned char attr PACKED; /* 05h: FECN/BECN/DE/CR */ - unsigned short tmstamp PACKED; /* 06h: time stamp */ - unsigned short rsrv[2] PACKED; /* 08h: */ - unsigned long offset PACKED; /* 0Ch: buffer absolute address */ -} fr_rx_buf_ctl_t; - -typedef struct fr_tx_buf_ctl -{ - unsigned char flag PACKED; /* 00h: ready flag */ - unsigned short rsrv0[2] PACKED; /* 01h: */ - unsigned short length PACKED; /* 05h: frame length */ - unsigned short dlci PACKED; /* 07h: DLCI */ - unsigned char attr PACKED; /* 09h: FECN/BECN/DE/CR */ - unsigned short rsrv1 PACKED; /* 0Ah: */ - unsigned long offset PACKED; /* 0Ch: buffer absolute address */ -} fr_tx_buf_ctl_t; - -/*---------------------------------------------------------------------------- - * Global Configuration Block. Passed to FR_SET_CONFIG command when dlci == 0. - */ -typedef struct fr_conf -{ - unsigned short station PACKED; /* 00h: CPE/Node */ - unsigned short options PACKED; /* 02h: configuration options */ - unsigned short kbps PACKED; /* 04h: baud rate in kbps */ - unsigned short port PACKED; /* 06h: RS-232/V.35 */ - unsigned short mtu PACKED; /* 08h: max. transmit length */ - unsigned short t391 PACKED; /* 0Ah: */ - unsigned short t392 PACKED; /* 0Ch: */ - unsigned short n391 PACKED; /* 0Eh: */ - unsigned short n392 PACKED; /* 10h: */ - unsigned short n393 PACKED; /* 12h: */ - unsigned short cir_fwd PACKED; /* 14h: */ - unsigned short bc_fwd PACKED; /* 16h: */ - unsigned short be_fwd PACKED; /* 18h: */ - unsigned short cir_bwd PACKED; /* 1Ah: */ - unsigned short bc_bwd PACKED; /* 1Ch: */ - unsigned short be_bwd PACKED; /* 1Eh: */ - unsigned short dlci[0] PACKED; /* 20h: */ -} fr_conf_t; - -/* 'station_type' defines */ -#define FRCFG_STATION_CPE 0 -#define FRCFG_STATION_NODE 1 - -/* 'conf_flags' defines */ -#define FRCFG_IGNORE_TX_CIR 0x0001 -#define FRCFG_IGNORE_RX_CIR 0x0002 -#define FRCFG_DONT_RETRANSMIT 0x0004 -#define FRCFG_IGNORE_CBS 0x0008 -#define FRCFG_THROUGHPUT 0x0010 /* enable throughput calculation */ -#define FRCFG_DIRECT_RX 0x0080 /* enable direct receive buffer */ -#define FRCFG_AUTO_CONFIG 0x8000 /* enable auto DLCI configuration */ - -/* 'baud_rate' defines */ -#define FRCFG_BAUD_1200 12 -#define FRCFG_BAUD_2400 24 -#define FRCFG_BAUD_4800 48 -#define FRCFG_BAUD_9600 96 -#define FRCFG_BAUD_19200 19 -#define FRCFG_BAUD_38400 38 -#define FRCFG_BAUD_56000 56 -#define FRCFG_BAUD_64000 64 -#define FRCFG_BAUD_128000 128 - -/* 'port_mode' defines */ -#define FRCFG_MODE_EXT_CLK 0x0000 -#define FRCFG_MODE_INT_CLK 0x0001 -#define FRCFG_MODE_V35 0x0000 /* S508 only */ -#define FRCFG_MODE_RS232 0x0002 /* S508 only */ - -/* defines for line tracing */ - -/* the line trace status element presented by the frame relay code */ -typedef struct { - unsigned char flag PACKED; /* ready flag */ - unsigned short length PACKED; /* trace length */ - unsigned char rsrv0[2] PACKED; /* reserved */ - unsigned char attr PACKED; /* trace attributes */ - unsigned short tmstamp PACKED; /* time stamp */ - unsigned char rsrv1[4] PACKED; /* reserved */ - unsigned long offset PACKED; /* buffer absolute address */ -} fr_trc_el_t; - -typedef struct { - unsigned char status PACKED; /* status flag */ - unsigned char data_passed PACKED; /* 0 if no data passed, 1 if */ - /* data passed */ - unsigned short length PACKED; /* frame length */ - unsigned short tmstamp PACKED; /* time stamp */ -} fpipemon_trc_hdr_t; - -typedef struct { - fpipemon_trc_hdr_t fpipemon_trc_hdr PACKED; - unsigned char data[FR_MAX_NO_DATA_BYTES_IN_FRAME] PACKED; -} fpipemon_trc_t; - -/* bit settings for the 'status' byte - note that bits 1, 2 and 3 are used */ -/* for returning the number of frames being passed to fpipemon */ -#define TRC_OUTGOING_FRM 0x01 -#define TRC_ABORT_ERROR 0x10 -#define TRC_CRC_ERROR 0x20 -#define TRC_OVERRUN_ERROR 0x40 -#define MORE_TRC_DATA 0x80 - -#define MAX_FRMS_TRACED 0x07 - -#define NO_TRC_ELEMENTS_OFF 0x9000 -#define BASE_TRC_ELEMENTS_OFF 0x9002 -#define TRC_ACTIVE 0x01 -#define FLUSH_TRC_BUFFERS 0x02 -#define FLUSH_TRC_STATISTICS 0x04 -#define TRC_SIGNALLING_FRMS 0x10 -#define TRC_INFO_FRMS 0x20 -#define ACTIVATE_TRC (TRC_ACTIVE | TRC_SIGNALLING_FRMS | TRC_INFO_FRMS) -#define RESET_TRC (FLUSH_TRC_BUFFERS | FLUSH_TRC_STATISTICS) - -/*---------------------------------------------------------------------------- - * Channel configuration. - * This structure is passed to the FR_SET_CONFIG command when dlci != 0. - */ -typedef struct fr_dlc_conf -{ - unsigned short conf_flags PACKED; /* 00h: configuration bits */ - unsigned short cir_fwd PACKED; /* 02h: */ - unsigned short bc_fwd PACKED; /* 04h: */ - unsigned short be_fwd PACKED; /* 06h: */ - unsigned short cir_bwd PACKED; /* 08h: */ - unsigned short bc_bwd PACKED; /* 0Ah: */ - unsigned short be_bwd PACKED; /* 0Ch: */ -} fr_dlc_conf_t; - -/*---------------------------------------------------------------------------- - * S502 interrupt mode control block. - * This structure is passed to the FR_SET_INTR_FLAGS and returned by the - * FR_READ_INTR_FLAGS commands. - */ -typedef struct fr502_intr_ctl -{ - unsigned char mode PACKED; /* 00h: interrupt enable flags */ - unsigned short tx_len PACKED; /* 01h: required Tx buffer size */ -} fr502_intr_ctl_t; - -/*---------------------------------------------------------------------------- - * S508 interrupt mode control block. - * This structure is passed to the FR_SET_INTR_FLAGS and returned by the - * FR_READ_INTR_FLAGS commands. - */ -typedef struct fr508_intr_ctl -{ - unsigned char mode PACKED; /* 00h: interrupt enable flags */ - unsigned short tx_len PACKED; /* 01h: required Tx buffer size */ - unsigned char irq PACKED; /* 03h: IRQ level to activate */ - unsigned char flags PACKED; /* 04h: ?? */ - unsigned short timeout PACKED; /* 05h: ms, for timer interrupt */ -} fr508_intr_ctl_t; - -/*---------------------------------------------------------------------------- - * Channel status. - * This structure is returned by the FR_READ_STATUS command. - */ -typedef struct fr_dlc_Status -{ - unsigned char status PACKED; /* 00h: link/DLCI status */ - struct - { - unsigned short dlci PACKED; /* 01h: DLCI number */ - unsigned char status PACKED; /* 03h: DLCI status */ - } circuit[1] PACKED; -} fr_dlc_status_t; - -/* 'status' defines */ -#define FR_LINK_INOPER 0x00 /* for global status (DLCI == 0) */ -#define FR_LINK_OPER 0x01 -#define FR_DLCI_DELETED 0x01 /* for circuit status (DLCI != 0) */ -#define FR_DLCI_ACTIVE 0x02 -#define FR_DLCI_WAITING 0x04 -#define FR_DLCI_NEW 0x08 -#define FR_DLCI_REPORT 0x40 - -/*---------------------------------------------------------------------------- - * Global Statistics Block. - * This structure is returned by the FR_READ_STATISTICS command when - * dcli == 0. - */ -typedef struct fr_link_stat -{ - unsigned short rx_too_long PACKED; /* 00h: */ - unsigned short rx_dropped PACKED; /* 02h: */ - unsigned short rx_dropped2 PACKED; /* 04h: */ - unsigned short rx_bad_dlci PACKED; /* 06h: */ - unsigned short rx_bad_format PACKED; /* 08h: */ - unsigned short retransmitted PACKED; /* 0Ah: */ - unsigned short cpe_tx_FSE PACKED; /* 0Ch: */ - unsigned short cpe_tx_LIV PACKED; /* 0Eh: */ - unsigned short cpe_rx_FSR PACKED; /* 10h: */ - unsigned short cpe_rx_LIV PACKED; /* 12h: */ - unsigned short node_rx_FSE PACKED; /* 14h: */ - unsigned short node_rx_LIV PACKED; /* 16h: */ - unsigned short node_tx_FSR PACKED; /* 18h: */ - unsigned short node_tx_LIV PACKED; /* 1Ah: */ - unsigned short rx_ISF_err PACKED; /* 1Ch: */ - unsigned short rx_unsolicited PACKED; /* 1Eh: */ - unsigned short rx_SSN_err PACKED; /* 20h: */ - unsigned short rx_RSN_err PACKED; /* 22h: */ - unsigned short T391_timeouts PACKED; /* 24h: */ - unsigned short T392_timeouts PACKED; /* 26h: */ - unsigned short N392_reached PACKED; /* 28h: */ - unsigned short cpe_SSN_RSN PACKED; /* 2Ah: */ - unsigned short current_SSN PACKED; /* 2Ch: */ - unsigned short current_RSN PACKED; /* 2Eh: */ - unsigned short curreny_T391 PACKED; /* 30h: */ - unsigned short current_T392 PACKED; /* 32h: */ - unsigned short current_N392 PACKED; /* 34h: */ - unsigned short current_N393 PACKED; /* 36h: */ -} fr_link_stat_t; - -/*---------------------------------------------------------------------------- - * DLCI statistics. - * This structure is returned by the FR_READ_STATISTICS command when - * dlci != 0. - */ -typedef struct fr_dlci_stat -{ - unsigned long tx_frames PACKED; /* 00h: */ - unsigned long tx_bytes PACKED; /* 04h: */ - unsigned long rx_frames PACKED; /* 08h: */ - unsigned long rx_bytes PACKED; /* 0Ch: */ - unsigned long rx_dropped PACKED; /* 10h: */ - unsigned long rx_inactive PACKED; /* 14h: */ - unsigned long rx_exceed_CIR PACKED; /* 18h: */ - unsigned long rx_DE_set PACKED; /* 1Ch: */ - unsigned long tx_throughput PACKED; /* 20h: */ - unsigned long tx_calc_timer PACKED; /* 24h: */ - unsigned long rx_throughput PACKED; /* 28h: */ - unsigned long rx_calc_timer PACKED; /* 2Ch: */ -} fr_dlci_stat_t; - -/*---------------------------------------------------------------------------- - * Communications error statistics. - * This structure is returned by the FR_READ_ERROR_STATS command. - */ -typedef struct fr_comm_stat -{ - unsigned char rx_overruns PACKED; /* 00h: */ - unsigned char rx_bad_crc PACKED; /* 01h: */ - unsigned char rx_aborts PACKED; /* 02h: */ - unsigned char rx_too_long PACKED; /* 03h: */ - unsigned char tx_aborts PACKED; /* 04h: */ - unsigned char tx_underruns PACKED; /* 05h: */ - unsigned char tx_missed_undr PACKED; /* 06h: */ - unsigned char dcd_dropped PACKED; /* 07h: */ - unsigned char cts_dropped PACKED; /* 08h: */ -} fr_comm_stat_t; - -/*---------------------------------------------------------------------------- - * Defines for the FR_ISSUE_IS_FRAME command. - */ -#define FR_ISF_LVE 2 /* issue Link Verification Enquiry */ -#define FR_ISF_FSE 3 /* issue Full Status Enquiry */ - -/*---------------------------------------------------------------------------- - * Frame Relay ARP Header -- Used for Dynamic route creation with InvARP - */ - -typedef struct arphdr_fr - { - unsigned short ar_hrd PACKED; /* format of hardware addr */ - unsigned short ar_pro PACKED; /* format of protocol addr */ - unsigned char ar_hln PACKED; /* length of hardware addr */ - unsigned char ar_pln PACKED; /* length of protocol addr */ - unsigned short ar_op PACKED; /* ARP opcode */ - unsigned short ar_sha PACKED; /* Sender DLCI addr 2 bytes */ - unsigned long ar_sip PACKED; /* Sender IP addr 4 bytes */ - unsigned short ar_tha PACKED; /* Target DLCI addr 2 bytes */ - unsigned long ar_tip PACKED; /* Target IP addr 4 bytes */ - } arphdr_fr_t; - -/*---------------------------------------------------------------------------- - * Frame Relay RFC 1490 SNAP Header -- Used to check for ARP packets - */ -typedef struct arphdr_1490 - { - unsigned char control PACKED; /* UI, etc... */ - unsigned char pad PACKED; /* Pad */ - unsigned char NLPID PACKED; /* SNAP */ - unsigned char OUI[3] PACKED; /* Ethertype, etc... */ - unsigned short PID PACKED; /* ARP, IP, etc... */ - } arphdr_1490_t; - -/* UDP/IP packet (for UDP management) layout */ - -/* The embedded control block for UDP mgmt - This is essentially a mailbox structure, without the large data field */ - -typedef struct { - unsigned char opp_flag PACKED; /* the opp flag */ - unsigned char command PACKED; /* command code */ - unsigned short length PACKED; /* length of data buffer */ - unsigned char result PACKED; /* return code */ - unsigned short dlci PACKED; /* DLCI number */ - unsigned char attr PACKED; /* FECN, BECN, DE and C/R bits */ - unsigned short rxlost1 PACKED; /* frames discarded at int. level */ - unsigned long rxlost2 PACKED; /* frames discarded at app. level */ - unsigned char rsrv[2] PACKED; /* reserved for future use */ -} cblock_t; - - -/* UDP management packet layout (data area of ip packet) */ - -typedef struct { - unsigned char control PACKED; - unsigned char NLPID PACKED; -} fr_encap_hdr_t; - -typedef struct { -// fr_encap_hdr_t fr_encap_hdr PACKED; - ip_pkt_t ip_pkt PACKED; - udp_pkt_t udp_pkt PACKED; - wp_mgmt_t wp_mgmt PACKED; - cblock_t cblock PACKED; - unsigned char data[4080] PACKED; -} fr_udp_pkt_t; - - -/* valid ip_protocol for UDP management */ -#define UDPMGMT_UDP_PROTOCOL 0x11 - -#define UDPMGMT_FPIPE_SIGNATURE "FPIPE8ND" -#define UDPMGMT_DRVRSTATS_SIGNATURE "DRVSTATS" - -/* values for request/reply byte */ -#define UDPMGMT_REQUEST 0x01 -#define UDPMGMT_REPLY 0x02 -#define UDP_OFFSET 12 - -typedef struct { - unsigned long if_send_entry; - unsigned long if_send_skb_null; - unsigned long if_send_broadcast; - unsigned long if_send_multicast; - unsigned long if_send_critical_ISR; - unsigned long if_send_critical_non_ISR; - unsigned long if_send_busy; - unsigned long if_send_busy_timeout; - unsigned long if_send_DRVSTATS_request; - unsigned long if_send_FPIPE_request; - unsigned long if_send_wan_disconnected; - unsigned long if_send_dlci_disconnected; - unsigned long if_send_no_bfrs; - unsigned long if_send_adptr_bfrs_full; - unsigned long if_send_bfrs_passed_to_adptr; - unsigned long if_send_consec_send_fail; -} drvstats_if_send_t; - -typedef struct { - unsigned long rx_intr_no_socket; - unsigned long rx_intr_dev_not_started; - unsigned long rx_intr_DRVSTATS_request; - unsigned long rx_intr_FPIPE_request; - unsigned long rx_intr_bfr_not_passed_to_stack; - unsigned long rx_intr_bfr_passed_to_stack; - } drvstats_rx_intr_t; - -typedef struct { - unsigned long UDP_FPIPE_mgmt_kmalloc_err; - unsigned long UDP_FPIPE_mgmt_direction_err; - unsigned long UDP_FPIPE_mgmt_adptr_type_err; - unsigned long UDP_FPIPE_mgmt_adptr_cmnd_OK; - unsigned long UDP_FPIPE_mgmt_adptr_cmnd_timeout; - unsigned long UDP_FPIPE_mgmt_adptr_send_passed; - unsigned long UDP_FPIPE_mgmt_adptr_send_failed; - unsigned long UDP_FPIPE_mgmt_not_passed_to_stack; - unsigned long UDP_FPIPE_mgmt_passed_to_stack; - unsigned long UDP_FPIPE_mgmt_no_socket; - unsigned long UDP_DRVSTATS_mgmt_kmalloc_err; - unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK; - unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; - unsigned long UDP_DRVSTATS_mgmt_adptr_send_passed; - unsigned long UDP_DRVSTATS_mgmt_adptr_send_failed; - unsigned long UDP_DRVSTATS_mgmt_not_passed_to_stack; - unsigned long UDP_DRVSTATS_mgmt_passed_to_stack; - unsigned long UDP_DRVSTATS_mgmt_no_socket; -} drvstats_gen_t; - -typedef struct { - unsigned char attr PACKED; - unsigned short time_stamp PACKED; - unsigned char reserved[13] PACKED; -} api_rx_hdr_t; - -typedef struct { - api_rx_hdr_t api_rx_hdr PACKED; - void * data PACKED; -} api_rx_element_t; - -typedef struct { - unsigned char attr PACKED; - unsigned char reserved[15] PACKED; -} api_tx_hdr_t; - -typedef struct { - api_tx_hdr_t api_tx_hdr PACKED; - void * data PACKED; -} api_tx_element_t; - -#ifdef _MSC_ -# pragma pack() -#endif -#endif /* _SDLA_FR_H */ - diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 52c9eb9..26e4925 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -61,10 +61,10 @@ static inline void write_seqlock(seqlock { spin_lock(&sl->lock); ++sl->sequence; - smp_wmb(); -} + smp_wmb(); +} -static inline void write_sequnlock(seqlock_t *sl) +static inline void write_sequnlock(seqlock_t *sl) { smp_wmb(); sl->sequence++; @@ -77,7 +77,7 @@ static inline int write_tryseqlock(seqlo if (ret) { ++sl->sequence; - smp_wmb(); + smp_wmb(); } return ret; } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5992f65..253a2b9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -27,20 +27,24 @@ #include #include #include #include +#include #define HAVE_ALLOC_SKB /* For the drivers to know */ #define HAVE_ALIGNABLE_SKB /* Ditto 8) */ +/* Don't change this without changing skb_csum_unnecessary! */ #define CHECKSUM_NONE 0 -#define CHECKSUM_PARTIAL 1 -#define CHECKSUM_UNNECESSARY 2 -#define CHECKSUM_COMPLETE 3 +#define CHECKSUM_UNNECESSARY 1 +#define CHECKSUM_COMPLETE 2 +#define CHECKSUM_PARTIAL 3 #define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES - 1)) & \ ~(SMP_CACHE_BYTES - 1)) -#define SKB_MAX_ORDER(X, ORDER) (((PAGE_SIZE << (ORDER)) - (X) - \ - sizeof(struct skb_shared_info)) & \ - ~(SMP_CACHE_BYTES - 1)) +#define SKB_WITH_OVERHEAD(X) \ + (((X) - sizeof(struct skb_shared_info)) & \ + ~(SMP_CACHE_BYTES - 1)) +#define SKB_MAX_ORDER(X, ORDER) \ + SKB_WITH_OVERHEAD((PAGE_SIZE << (ORDER)) - (X)) #define SKB_MAX_HEAD(X) (SKB_MAX_ORDER((X), 0)) #define SKB_MAX_ALLOC (SKB_MAX_ORDER(0, 2)) @@ -66,8 +70,8 @@ #define SKB_MAX_ALLOC (SKB_MAX_ORDER(0, * NONE: skb is checksummed by protocol or csum is not required. * * PARTIAL: device is required to csum packet as seen by hard_start_xmit - * from skb->h.raw to the end and to record the checksum - * at skb->h.raw+skb->csum. + * from skb->transport_header to the end and to record the checksum + * at skb->transport_header + skb->csum. * * Device must show its capabilities in dev->features, set * at device setup time. @@ -83,12 +87,13 @@ #define SKB_MAX_ALLOC (SKB_MAX_ORDER(0, */ struct net_device; +struct scatterlist; -#ifdef CONFIG_NETFILTER +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack { atomic_t use; - void (*destroy)(struct nf_conntrack *); }; +#endif #ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info { @@ -103,8 +108,6 @@ #endif }; #endif -#endif - struct sk_buff_head { /* These two members must be first. */ struct sk_buff *next; @@ -156,11 +159,6 @@ struct skb_shared_info { #define SKB_DATAREF_SHIFT 16 #define SKB_DATAREF_MASK ((1 << SKB_DATAREF_SHIFT) - 1) -struct skb_timeval { - u32 off_sec; - u32 off_usec; -}; - enum { SKB_FCLONE_UNAVAILABLE, @@ -181,6 +179,16 @@ enum { SKB_GSO_TCPV6 = 1 << 4, }; +#if BITS_PER_LONG > 32 +#define NET_SKBUFF_DATA_USES_OFFSET 1 +#endif + +#ifdef NET_SKBUFF_DATA_USES_OFFSET +typedef unsigned int sk_buff_data_t; +#else +typedef unsigned char *sk_buff_data_t; +#endif + /** * struct sk_buff - socket buffer * @next: Next buffer in list @@ -190,15 +198,17 @@ enum { * @dev: Device we arrived on/are leaving by * @iif: ifindex of device we arrived on * @h: Transport layer header - * @nh: Network layer header - * @mac: Link layer header + * @network_header: Network layer header + * @mac_header: Link layer header * @dst: destination entry * @sp: the security path, used for xfrm * @cb: Control buffer. Free for use by every layer. Put private vars here * @len: Length of actual data * @data_len: Data length * @mac_len: Length of link layer header - * @csum: Checksum + * @csum: Checksum (must include start/offset pair) + * @csum_start: Offset from skb->head where checksumming should start + * @csum_offset: Offset from csum_start where checksum should be stored * @local_df: allow local fragmentation * @cloned: Head may be cloned (check refcnt to be sure) * @nohdr: Payload reference only, must not modify header @@ -233,32 +243,11 @@ struct sk_buff { struct sk_buff *prev; struct sock *sk; - struct skb_timeval tstamp; + ktime_t tstamp; struct net_device *dev; int iif; /* 4 byte hole on 64 bit*/ - union { - struct tcphdr *th; - struct udphdr *uh; - struct icmphdr *icmph; - struct igmphdr *igmph; - struct iphdr *ipiph; - struct ipv6hdr *ipv6h; - unsigned char *raw; - } h; - - union { - struct iphdr *iph; - struct ipv6hdr *ipv6h; - struct arphdr *arph; - unsigned char *raw; - } nh; - - union { - unsigned char *raw; - } mac; - struct dst_entry *dst; struct sec_path *sp; @@ -275,7 +264,10 @@ struct sk_buff { mac_len; union { __wsum csum; - __u32 csum_offset; + struct { + __u16 csum_start; + __u16 csum_offset; + }; }; __u32 priority; __u8 local_df:1, @@ -289,15 +281,13 @@ struct sk_buff { __be16 protocol; void (*destructor)(struct sk_buff *skb); -#ifdef CONFIG_NETFILTER - struct nf_conntrack *nfct; #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) + struct nf_conntrack *nfct; struct sk_buff *nfct_reasm; #endif #ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info *nf_bridge; #endif -#endif /* CONFIG_NETFILTER */ #ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */ #ifdef CONFIG_NET_CLS_ACT @@ -313,13 +303,16 @@ #endif __u32 mark; + sk_buff_data_t transport_header; + sk_buff_data_t network_header; + sk_buff_data_t mac_header; /* These elements must be at the end, see alloc_skb() for details. */ + sk_buff_data_t tail; + sk_buff_data_t end; + unsigned char *head, + *data; unsigned int truesize; atomic_t users; - unsigned char *head, - *data, - *tail, - *end; }; #ifdef __KERNEL__ @@ -361,6 +354,11 @@ extern struct sk_buff *skb_realloc_headr extern struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom, int newtailroom, gfp_t priority); +extern int skb_to_sgvec(struct sk_buff *skb, + struct scatterlist *sg, int offset, + int len); +extern int skb_cow_data(struct sk_buff *skb, int tailbits, + struct sk_buff **trailer); extern int skb_pad(struct sk_buff *skb, int pad); #define dev_kfree_skb(a) kfree_skb(a) extern void skb_over_panic(struct sk_buff *skb, int len, @@ -402,8 +400,20 @@ extern unsigned int skb_find_text(stru unsigned int to, struct ts_config *config, struct ts_state *state); +#ifdef NET_SKBUFF_DATA_USES_OFFSET +static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) +{ + return skb->head + skb->end; +} +#else +static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) +{ + return skb->end; +} +#endif + /* Internal */ -#define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end)) +#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB))) /** * skb_queue_empty - check if a queue is empty @@ -822,12 +832,46 @@ #define SKB_PAGE_ASSERT(skb) BUG_ON(skb #define SKB_FRAG_ASSERT(skb) BUG_ON(skb_shinfo(skb)->frag_list) #define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb)) +#ifdef NET_SKBUFF_DATA_USES_OFFSET +static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) +{ + return skb->head + skb->tail; +} + +static inline void skb_reset_tail_pointer(struct sk_buff *skb) +{ + skb->tail = skb->data - skb->head; +} + +static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) +{ + skb_reset_tail_pointer(skb); + skb->tail += offset; +} +#else /* NET_SKBUFF_DATA_USES_OFFSET */ +static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) +{ + return skb->tail; +} + +static inline void skb_reset_tail_pointer(struct sk_buff *skb) +{ + skb->tail = skb->data; +} + +static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) +{ + skb->tail = skb->data + offset; +} + +#endif /* NET_SKBUFF_DATA_USES_OFFSET */ + /* * Add data to an sk_buff */ static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len) { - unsigned char *tmp = skb->tail; + unsigned char *tmp = skb_tail_pointer(skb); SKB_LINEAR_ASSERT(skb); skb->tail += len; skb->len += len; @@ -845,11 +889,11 @@ static inline unsigned char *__skb_put(s */ static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len) { - unsigned char *tmp = skb->tail; + unsigned char *tmp = skb_tail_pointer(skb); SKB_LINEAR_ASSERT(skb); skb->tail += len; skb->len += len; - if (unlikely(skb->tail>skb->end)) + if (unlikely(skb->tail > skb->end)) skb_over_panic(skb, len, current_text_addr()); return tmp; } @@ -962,6 +1006,130 @@ static inline void skb_reserve(struct sk skb->tail += len; } +#ifdef NET_SKBUFF_DATA_USES_OFFSET +static inline unsigned char *skb_transport_header(const struct sk_buff *skb) +{ + return skb->head + skb->transport_header; +} + +static inline void skb_reset_transport_header(struct sk_buff *skb) +{ + skb->transport_header = skb->data - skb->head; +} + +static inline void skb_set_transport_header(struct sk_buff *skb, + const int offset) +{ + skb_reset_transport_header(skb); + skb->transport_header += offset; +} + +static inline unsigned char *skb_network_header(const struct sk_buff *skb) +{ + return skb->head + skb->network_header; +} + +static inline void skb_reset_network_header(struct sk_buff *skb) +{ + skb->network_header = skb->data - skb->head; +} + +static inline void skb_set_network_header(struct sk_buff *skb, const int offset) +{ + skb_reset_network_header(skb); + skb->network_header += offset; +} + +static inline unsigned char *skb_mac_header(const struct sk_buff *skb) +{ + return skb->head + skb->mac_header; +} + +static inline int skb_mac_header_was_set(const struct sk_buff *skb) +{ + return skb->mac_header != ~0U; +} + +static inline void skb_reset_mac_header(struct sk_buff *skb) +{ + skb->mac_header = skb->data - skb->head; +} + +static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) +{ + skb_reset_mac_header(skb); + skb->mac_header += offset; +} + +#else /* NET_SKBUFF_DATA_USES_OFFSET */ + +static inline unsigned char *skb_transport_header(const struct sk_buff *skb) +{ + return skb->transport_header; +} + +static inline void skb_reset_transport_header(struct sk_buff *skb) +{ + skb->transport_header = skb->data; +} + +static inline void skb_set_transport_header(struct sk_buff *skb, + const int offset) +{ + skb->transport_header = skb->data + offset; +} + +static inline unsigned char *skb_network_header(const struct sk_buff *skb) +{ + return skb->network_header; +} + +static inline void skb_reset_network_header(struct sk_buff *skb) +{ + skb->network_header = skb->data; +} + +static inline void skb_set_network_header(struct sk_buff *skb, const int offset) +{ + skb->network_header = skb->data + offset; +} + +static inline unsigned char *skb_mac_header(const struct sk_buff *skb) +{ + return skb->mac_header; +} + +static inline int skb_mac_header_was_set(const struct sk_buff *skb) +{ + return skb->mac_header != NULL; +} + +static inline void skb_reset_mac_header(struct sk_buff *skb) +{ + skb->mac_header = skb->data; +} + +static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) +{ + skb->mac_header = skb->data + offset; +} +#endif /* NET_SKBUFF_DATA_USES_OFFSET */ + +static inline int skb_transport_offset(const struct sk_buff *skb) +{ + return skb_transport_header(skb) - skb->data; +} + +static inline u32 skb_network_header_len(const struct sk_buff *skb) +{ + return skb->transport_header - skb->network_header; +} + +static inline int skb_network_offset(const struct sk_buff *skb) +{ + return skb_network_header(skb) - skb->data; +} + /* * CPUs often take a performance hit when accessing unaligned memory * locations. The actual performance hit varies, it can be small if the @@ -1013,8 +1181,8 @@ static inline void __skb_trim(struct sk_ WARN_ON(1); return; } - skb->len = len; - skb->tail = skb->data + len; + skb->len = len; + skb_set_tail_pointer(skb, len); } /** @@ -1303,6 +1471,11 @@ #define skb_queue_walk(queue, skb) \ prefetch(skb->next), (skb != (struct sk_buff *)(queue)); \ skb = skb->next) +#define skb_queue_walk_safe(queue, skb, tmp) \ + for (skb = (queue)->next, tmp = skb->next; \ + skb != (struct sk_buff *)(queue); \ + skb = tmp, tmp = skb->next) + #define skb_queue_reverse_walk(queue, skb) \ for (skb = (queue)->prev; \ prefetch(skb->prev), (skb != (struct sk_buff *)(queue)); \ @@ -1326,8 +1499,8 @@ extern __wsum skb_checksum(const int len, __wsum csum); extern int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); -extern int skb_store_bits(const struct sk_buff *skb, int offset, - void *from, int len); +extern int skb_store_bits(struct sk_buff *skb, int offset, + const void *from, int len); extern __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, __wsum csum); @@ -1351,8 +1524,36 @@ static inline void *skb_header_pointer(c return buffer; } +static inline void skb_copy_from_linear_data(const struct sk_buff *skb, + void *to, + const unsigned int len) +{ + memcpy(to, skb->data, len); +} + +static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb, + const int offset, void *to, + const unsigned int len) +{ + memcpy(to, skb->data + offset, len); +} + +static inline void skb_copy_to_linear_data(struct sk_buff *skb, + const void *from, + const unsigned int len) +{ + memcpy(skb->data, from, len); +} + +static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb, + const int offset, + const void *from, + const unsigned int len) +{ + memcpy(skb->data + offset, from, len); +} + extern void skb_init(void); -extern void skb_add_mtu(int mtu); /** * skb_get_timestamp - get timestamp from a skb @@ -1365,29 +1566,28 @@ extern void skb_add_mtu(int mtu); */ static inline void skb_get_timestamp(const struct sk_buff *skb, struct timeval *stamp) { - stamp->tv_sec = skb->tstamp.off_sec; - stamp->tv_usec = skb->tstamp.off_usec; + *stamp = ktime_to_timeval(skb->tstamp); } -/** - * skb_set_timestamp - set timestamp of a skb - * @skb: skb to set stamp of - * @stamp: pointer to struct timeval to get stamp from - * - * Timestamps are stored in the skb as offsets to a base timestamp. - * This function converts a struct timeval to an offset and stores - * it in the skb. - */ -static inline void skb_set_timestamp(struct sk_buff *skb, const struct timeval *stamp) +static inline void __net_timestamp(struct sk_buff *skb) +{ + skb->tstamp = ktime_get_real(); +} + +static inline ktime_t net_timedelta(ktime_t t) { - skb->tstamp.off_sec = stamp->tv_sec; - skb->tstamp.off_usec = stamp->tv_usec; + return ktime_sub(ktime_get_real(), t); } -extern void __net_timestamp(struct sk_buff *skb); +extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); extern __sum16 __skb_checksum_complete(struct sk_buff *skb); +static inline int skb_csum_unnecessary(const struct sk_buff *skb) +{ + return skb->ip_summed & CHECKSUM_UNNECESSARY; +} + /** * skb_checksum_complete - Calculate checksum of an entire packet * @skb: packet to process @@ -1406,22 +1606,22 @@ extern __sum16 __skb_checksum_complete(s */ static inline unsigned int skb_checksum_complete(struct sk_buff *skb) { - return skb->ip_summed != CHECKSUM_UNNECESSARY && - __skb_checksum_complete(skb); + return skb_csum_unnecessary(skb) ? + 0 : __skb_checksum_complete(skb); } -#ifdef CONFIG_NETFILTER +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +extern void nf_conntrack_destroy(struct nf_conntrack *nfct); static inline void nf_conntrack_put(struct nf_conntrack *nfct) { if (nfct && atomic_dec_and_test(&nfct->use)) - nfct->destroy(nfct); + nf_conntrack_destroy(nfct); } static inline void nf_conntrack_get(struct nf_conntrack *nfct) { if (nfct) atomic_inc(&nfct->use); } -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) static inline void nf_conntrack_get_reasm(struct sk_buff *skb) { if (skb) @@ -1447,9 +1647,9 @@ static inline void nf_bridge_get(struct #endif /* CONFIG_BRIDGE_NETFILTER */ static inline void nf_reset(struct sk_buff *skb) { +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_conntrack_put(skb->nfct); skb->nfct = NULL; -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_conntrack_put_reasm(skb->nfct_reasm); skb->nfct_reasm = NULL; #endif @@ -1459,9 +1659,33 @@ #ifdef CONFIG_BRIDGE_NETFILTER #endif } -#else /* CONFIG_NETFILTER */ -static inline void nf_reset(struct sk_buff *skb) {} -#endif /* CONFIG_NETFILTER */ +/* Note: This doesn't put any conntrack and bridge info in dst. */ +static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src) +{ +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) + dst->nfct = src->nfct; + nf_conntrack_get(src->nfct); + dst->nfctinfo = src->nfctinfo; + dst->nfct_reasm = src->nfct_reasm; + nf_conntrack_get_reasm(src->nfct_reasm); +#endif +#ifdef CONFIG_BRIDGE_NETFILTER + dst->nf_bridge = src->nf_bridge; + nf_bridge_get(src->nf_bridge); +#endif +} + +static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src) +{ +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) + nf_conntrack_put(dst->nfct); + nf_conntrack_put_reasm(dst->nfct_reasm); +#endif +#ifdef CONFIG_BRIDGE_NETFILTER + nf_bridge_put(dst->nf_bridge); +#endif + __nf_copy(dst, src); +} #ifdef CONFIG_NETWORK_SECMARK static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from) @@ -1486,5 +1710,12 @@ static inline int skb_is_gso(const struc return skb_shinfo(skb)->gso_size; } +static inline void skb_forward_csum(struct sk_buff *skb) +{ + /* Unfortunately we don't support this one. Any brave souls? */ + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->ip_summed = CHECKSUM_NONE; +} + #endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */ diff --git a/include/linux/snmp.h b/include/linux/snmp.h index 854aa6b..802b3a3 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -40,6 +40,8 @@ enum IPSTATS_MIB_FRAGCREATES, /* FragCreates */ IPSTATS_MIB_INMCASTPKTS, /* InMcastPkts */ IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */ + IPSTATS_MIB_INBCASTPKTS, /* InBcastPkts */ + IPSTATS_MIB_OUTBCASTPKTS, /* OutBcastPkts */ __IPSTATS_MIB_MAX }; diff --git a/include/linux/socket.h b/include/linux/socket.h index fcd35a2..6e7c948 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -188,7 +188,8 @@ #define AF_LLC 26 /* Linux LLC */ #define AF_TIPC 30 /* TIPC sockets */ #define AF_BLUETOOTH 31 /* Bluetooth sockets */ #define AF_IUCV 32 /* IUCV sockets */ -#define AF_MAX 33 /* For now.. */ +#define AF_RXRPC 33 /* RxRPC sockets */ +#define AF_MAX 34 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -222,6 +223,7 @@ #define PF_LLC AF_LLC #define PF_TIPC AF_TIPC #define PF_BLUETOOTH AF_BLUETOOTH #define PF_IUCV AF_IUCV +#define PF_RXRPC AF_RXRPC #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ @@ -284,6 +286,7 @@ #define SOL_LLC 268 #define SOL_DCCP 269 #define SOL_NETLINK 270 #define SOL_TIPC 271 +#define SOL_RXRPC 272 /* IPX options */ #define IPX_TYPE 1 diff --git a/include/linux/sony-laptop.h b/include/linux/sony-laptop.h new file mode 100644 index 0000000..e2e036d --- /dev/null +++ b/include/linux/sony-laptop.h @@ -0,0 +1,34 @@ +#ifndef _SONYLAPTOP_H_ +#define _SONYLAPTOP_H_ + +#include + +#ifdef __KERNEL__ + +/* used only for communication between v4l and sony-laptop */ + +#define SONY_PIC_COMMAND_GETCAMERA 1 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERA 2 +#define SONY_PIC_COMMAND_GETCAMERABRIGHTNESS 3 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERABRIGHTNESS 4 +#define SONY_PIC_COMMAND_GETCAMERACONTRAST 5 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERACONTRAST 6 +#define SONY_PIC_COMMAND_GETCAMERAHUE 7 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERAHUE 8 +#define SONY_PIC_COMMAND_GETCAMERACOLOR 9 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERACOLOR 10 +#define SONY_PIC_COMMAND_GETCAMERASHARPNESS 11 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERASHARPNESS 12 +#define SONY_PIC_COMMAND_GETCAMERAPICTURE 13 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERAPICTURE 14 +#define SONY_PIC_COMMAND_GETCAMERAAGC 15 /* obsolete */ +#define SONY_PIC_COMMAND_SETCAMERAAGC 16 +#define SONY_PIC_COMMAND_GETCAMERADIRECTION 17 /* obsolete */ +#define SONY_PIC_COMMAND_GETCAMERAROMVERSION 18 /* obsolete */ +#define SONY_PIC_COMMAND_GETCAMERAREVISION 19 /* obsolete */ + +int sony_pic_camera_command(int command, u8 value); + +#endif /* __KERNEL__ */ + +#endif /* _SONYLAPTOP_H_ */ diff --git a/include/linux/string.h b/include/linux/string.h index 4f69ef9..7f2eb6a 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -47,6 +47,12 @@ #endif #ifndef __HAVE_ARCH_STRNICMP extern int strnicmp(const char *, const char *, __kernel_size_t); #endif +#ifndef __HAVE_ARCH_STRCASECMP +extern int strcasecmp(const char *s1, const char *s2); +#endif +#ifndef __HAVE_ARCH_STRNCASECMP +extern int strncasecmp(const char *s1, const char *s2, size_t n); +#endif #ifndef __HAVE_ARCH_STRCHR extern char * strchr(const char *,int); #endif diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 9a8970b..47f1c53 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -290,6 +290,7 @@ enum NET_CORE_BUDGET=19, NET_CORE_AEVENT_ETIME=20, NET_CORE_AEVENT_RSEQTH=21, + NET_CORE_WARNINGS=22, }; /* /proc/sys/net/ethernet */ @@ -438,6 +439,8 @@ enum NET_CIPSOV4_RBM_STRICTVALID=121, NET_TCP_AVAIL_CONG_CONTROL=122, NET_TCP_ALLOWED_CONG_CONTROL=123, + NET_TCP_MAX_SSTHRESH=124, + NET_TCP_FRTO_RESPONSE=125, }; enum { @@ -789,6 +792,7 @@ enum { NET_BRIDGE_NF_CALL_IPTABLES = 2, NET_BRIDGE_NF_CALL_IP6TABLES = 3, NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4, + NET_BRIDGE_NF_FILTER_PPPOE_TAGGED = 5, }; /* CTL_FS names: */ diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index fea9a6b..7d5d1ec 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -80,7 +80,7 @@ #define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATT #ifdef CONFIG_SYSFS extern int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data); + void (*func)(void *), void *data, struct module *owner); extern int __must_check sysfs_create_dir(struct kobject *, struct dentry *); @@ -137,7 +137,7 @@ extern int __must_check sysfs_init(void) #else /* CONFIG_SYSFS */ static inline int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data) + void (*func)(void *), void *data, struct module *owner) { return -ENOSYS; } diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 29d3089..c6b9f92 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -178,6 +178,21 @@ #include #include #include +static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb) +{ + return (struct tcphdr *)skb_transport_header(skb); +} + +static inline unsigned int tcp_hdrlen(const struct sk_buff *skb) +{ + return tcp_hdr(skb)->doff * 4; +} + +static inline unsigned int tcp_optlen(const struct sk_buff *skb) +{ + return (tcp_hdr(skb)->doff - 5) * 4; +} + /* This defines a selective acknowledgement block. */ struct tcp_sack_block_wire { __be32 start_seq; @@ -242,6 +257,8 @@ struct tcp_sock { * See RFC793 and RFC1122. The RFC writes these in capitals. */ u32 rcv_nxt; /* What we want to receive next */ + u32 copied_seq; /* Head of yet unread data */ + u32 rcv_wup; /* rcv_nxt on last window update sent */ u32 snd_nxt; /* Next sequence we send */ u32 snd_una; /* First byte we want an ack for */ @@ -300,17 +317,15 @@ #endif u32 snd_ssthresh; /* Slow start size threshold */ u32 snd_cwnd; /* Sending congestion window */ u16 snd_cwnd_cnt; /* Linear increase counter */ - u16 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ + u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ u32 snd_cwnd_used; u32 snd_cwnd_stamp; struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ u32 rcv_wnd; /* Current receiver window */ - u32 rcv_wup; /* rcv_nxt on last window update sent */ u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ u32 pushed_seq; /* Last pushed seq, required to talk to windows */ - u32 copied_seq; /* Head of yet unread data */ /* SACKs data */ struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */ diff --git a/include/linux/udp.h b/include/linux/udp.h index 7e08c07..6de445c 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -26,6 +26,15 @@ struct udphdr { __sum16 check; }; +#ifdef __KERNEL__ +#include + +static inline struct udphdr *udp_hdr(const struct sk_buff *skb) +{ + return (struct udphdr *)skb_transport_header(skb); +} +#endif + /* UDP socket options */ #define UDP_CORK 1 /* Never send partially complete segments */ #define UDP_ENCAP 100 /* Set the socket to accept encapsulated packets */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 87dc75a..cfbd2bb 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -299,8 +299,9 @@ struct usb_bus { int bandwidth_int_reqs; /* number of Interrupt requests */ int bandwidth_isoc_reqs; /* number of Isoc. requests */ +#ifdef CONFIG_USB_DEVICEFS struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */ - +#endif struct class_device *class_dev; /* class device for this bus */ #if defined(CONFIG_USB_MON) @@ -373,9 +374,12 @@ struct usb_device { char *serial; /* iSerialNumber string, if present */ struct list_head filelist; - struct device *usbfs_dev; +#ifdef CONFIG_USB_DEVICE_CLASS + struct device *usb_classdev; +#endif +#ifdef CONFIG_USB_DEVICEFS struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ - +#endif /* * Child devices - these can be either new devices * (if this is a hub device), or different instances @@ -394,10 +398,13 @@ #ifdef CONFIG_PM struct delayed_work autosuspend; /* for delayed autosuspends */ struct mutex pm_mutex; /* protects PM operations */ - unsigned autosuspend_delay; /* in jiffies */ + unsigned long last_busy; /* time of last use */ + int autosuspend_delay; /* in jiffies */ unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ + unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ + unsigned autoresume_disabled:1; /* disabled by the user */ #endif }; #define to_usb_device(d) container_of(d, struct usb_device, dev) @@ -437,6 +444,11 @@ static inline void usb_autopm_disable(st usb_autopm_set_interface(intf); } +static inline void usb_mark_last_busy(struct usb_device *udev) +{ + udev->last_busy = jiffies; +} + #else static inline int usb_autopm_set_interface(struct usb_interface *intf) @@ -451,6 +463,8 @@ static inline void usb_autopm_enable(str { } static inline void usb_autopm_disable(struct usb_interface *intf) { } +static inline void usb_mark_last_busy(struct usb_device *udev) +{ } #endif /*-------------------------------------------------------------------------*/ diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h index 956edf3..2204ae2 100644 --- a/include/linux/usb/cdc.h +++ b/include/linux/usb/cdc.h @@ -91,6 +91,17 @@ struct usb_cdc_union_desc { /* ... and there could be other slave interfaces */ } __attribute__ ((packed)); +/* "Country Selection Functional Descriptor" from CDC spec 5.2.3.9 */ +struct usb_cdc_country_functional_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 iCountryCodeRelDate; + __le16 wCountyCode0; + /* ... and there can be a lot of country codes */ +} __attribute__ ((packed)); + /* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */ struct usb_cdc_network_terminal_desc { __u8 bLength; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 1122a6c..6169438 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -181,12 +181,15 @@ #define USB_DT_WIRELESS_ENDPOINT_COMP 0x #define USB_DT_WIRE_ADAPTER 0x21 #define USB_DT_RPIPE 0x22 -/* conventional codes for class-specific descriptors */ -#define USB_DT_CS_DEVICE 0x21 -#define USB_DT_CS_CONFIG 0x22 -#define USB_DT_CS_STRING 0x23 -#define USB_DT_CS_INTERFACE 0x24 -#define USB_DT_CS_ENDPOINT 0x25 +/* Conventional codes for class-specific descriptors. The convention is + * defined in the USB "Common Class" Spec (3.11). Individual class specs + * are authoritative for their usage, not the "common class" writeup. + */ +#define USB_DT_CS_DEVICE (USB_TYPE_CLASS | USB_DT_DEVICE) +#define USB_DT_CS_CONFIG (USB_TYPE_CLASS | USB_DT_CONFIG) +#define USB_DT_CS_STRING (USB_TYPE_CLASS | USB_DT_STRING) +#define USB_DT_CS_INTERFACE (USB_TYPE_CLASS | USB_DT_INTERFACE) +#define USB_DT_CS_ENDPOINT (USB_TYPE_CLASS | USB_DT_ENDPOINT) /* All standard descriptors have these 2 fields at the beginning */ struct usb_descriptor_header { diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 441b877..a25c2af 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -96,44 +96,60 @@ #define v4l2_fourcc(a,b,c,d)\ * E N U M S */ enum v4l2_field { - V4L2_FIELD_ANY = 0, /* driver can choose from none, - top, bottom, interlaced - depending on whatever it thinks - is approximate ... */ - V4L2_FIELD_NONE = 1, /* this device has no fields ... */ - V4L2_FIELD_TOP = 2, /* top field only */ - V4L2_FIELD_BOTTOM = 3, /* bottom field only */ - V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */ - V4L2_FIELD_SEQ_TB = 5, /* both fields sequential into one - buffer, top-bottom order */ - V4L2_FIELD_SEQ_BT = 6, /* same as above + bottom-top order */ - V4L2_FIELD_ALTERNATE = 7, /* both fields alternating into - separate buffers */ + V4L2_FIELD_ANY = 0, /* driver can choose from none, + top, bottom, interlaced + depending on whatever it thinks + is approximate ... */ + V4L2_FIELD_NONE = 1, /* this device has no fields ... */ + V4L2_FIELD_TOP = 2, /* top field only */ + V4L2_FIELD_BOTTOM = 3, /* bottom field only */ + V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */ + V4L2_FIELD_SEQ_TB = 5, /* both fields sequential into one + buffer, top-bottom order */ + V4L2_FIELD_SEQ_BT = 6, /* same as above + bottom-top order */ + V4L2_FIELD_ALTERNATE = 7, /* both fields alternating into + separate buffers */ + V4L2_FIELD_INTERLACED_TB = 8, /* both fields interlaced, top field + first and the top field is + transmitted first */ + V4L2_FIELD_INTERLACED_BT = 9, /* both fields interlaced, top field + first and the bottom field is + transmitted first */ }; #define V4L2_FIELD_HAS_TOP(field) \ ((field) == V4L2_FIELD_TOP ||\ (field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT ||\ (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) #define V4L2_FIELD_HAS_BOTTOM(field) \ ((field) == V4L2_FIELD_BOTTOM ||\ (field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT ||\ (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) #define V4L2_FIELD_HAS_BOTH(field) \ ((field) == V4L2_FIELD_INTERLACED ||\ - (field) == V4L2_FIELD_SEQ_TB ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT ||\ + (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) enum v4l2_buf_type { - V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, - V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, - V4L2_BUF_TYPE_VIDEO_OVERLAY = 3, - V4L2_BUF_TYPE_VBI_CAPTURE = 4, - V4L2_BUF_TYPE_VBI_OUTPUT = 5, - V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, - V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, - V4L2_BUF_TYPE_PRIVATE = 0x80, + V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, + V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, + V4L2_BUF_TYPE_VIDEO_OVERLAY = 3, + V4L2_BUF_TYPE_VBI_CAPTURE = 4, + V4L2_BUF_TYPE_VBI_OUTPUT = 5, + V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, + V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, +#if 1 + /* Experimental */ + V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, +#endif + V4L2_BUF_TYPE_PRIVATE = 0x80, }; enum v4l2_ctrl_type { @@ -227,6 +243,8 @@ #define V4L2_CAP_VBI_OUTPUT 0x00000020 #define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040 /* Is a sliced VBI capture device */ #define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */ #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */ +#define V4L2_CAP_VIDEO_OUTPUT_POS 0x00000200 /* Video output can have x,y coords */ +#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000400 /* Can do video output overlay */ #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ @@ -249,6 +267,8 @@ struct v4l2_pix_format __u32 sizeimage; enum v4l2_colorspace colorspace; __u32 priv; /* private data, depends on pixelformat */ + __u32 left; /* only valid if V4L2_CAP_VIDEO_OUTPUT_POS is set */ + __u32 top; /* only valid if V4L2_CAP_VIDEO_OUTPUT_POS is set */ }; /* Pixel format FOURCC depth Description */ @@ -596,10 +616,14 @@ #define V4L2_FBUF_CAP_EXTERNOVERLAY 0x00 #define V4L2_FBUF_CAP_CHROMAKEY 0x0002 #define V4L2_FBUF_CAP_LIST_CLIPPING 0x0004 #define V4L2_FBUF_CAP_BITMAP_CLIPPING 0x0008 +#define V4L2_FBUF_CAP_LOCAL_ALPHA 0x0010 +#define V4L2_FBUF_CAP_GLOBAL_ALPHA 0x0020 /* Flags for the 'flags' field. */ #define V4L2_FBUF_FLAG_PRIMARY 0x0001 #define V4L2_FBUF_FLAG_OVERLAY 0x0002 #define V4L2_FBUF_FLAG_CHROMAKEY 0x0004 +#define V4L2_FBUF_FLAG_LOCAL_ALPHA 0x0008 +#define V4L2_FBUF_FLAG_GLOBAL_ALPHA 0x0010 struct v4l2_clip { @@ -615,6 +639,7 @@ struct v4l2_window struct v4l2_clip __user *clips; __u32 clipcount; void __user *bitmap; + __u8 global_alpha; }; /* @@ -1037,6 +1062,7 @@ enum v4l2_mpeg_audio_crc { V4L2_MPEG_AUDIO_CRC_NONE = 0, V4L2_MPEG_AUDIO_CRC_CRC16 = 1, }; +#define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109) /* MPEG video */ #define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) @@ -1063,6 +1089,8 @@ enum v4l2_mpeg_video_bitrate_mode { #define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_MPEG_BASE+207) #define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_MPEG_BASE+208) #define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209) +#define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210) +#define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211) /* MPEG-class control IDs specific to the CX2584x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) @@ -1103,6 +1131,7 @@ #define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA #define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+8) #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9) #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+10) +#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (V4L2_CID_MPEG_CX2341X_BASE+11) /* * T U N I N G @@ -1369,6 +1398,14 @@ struct v4l2_register { __u64 val; }; +/* VIDIOC_G_CHIP_IDENT */ +struct v4l2_chip_ident { + __u32 match_type; /* Match type */ + __u32 match_chip; /* Match this chip, meaning determined by match_type */ + __u32 ident; /* chip identifier as specified in */ + __u32 revision; /* chip revision, chip specific */ +}; + /* * I O C T L C O D E S F O R V I D E O D E V I C E S * @@ -1442,6 +1479,8 @@ #define VIDIOC_TRY_ENCODER_CMD _IOWR (' /* Experimental, only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ #define VIDIOC_DBG_S_REGISTER _IOW ('V', 79, struct v4l2_register) #define VIDIOC_DBG_G_REGISTER _IOWR ('V', 80, struct v4l2_register) + +#define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident) #endif #ifdef __OLD_VIDIOC_ diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 48759b2..0987aa7 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -186,7 +186,7 @@ #define WIRELESS_EXT 22 * - Wireless Event capability in struct iw_range * - Add support for relative TxPower (yick !) * - * V17 to V18 (From Jouni Malinen ) + * V17 to V18 (From Jouni Malinen ) * ---------- * - Add support for WPA/WPA2 * - Add extended encoding configuration (SIOCSIWENCODEEXT and diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 2a7b38d..b8abfc7 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -191,14 +191,15 @@ int execute_in_process_context(work_func /* * Kill off a pending schedule_delayed_work(). Note that the work callback - * function may still be running on return from cancel_delayed_work(). Run - * flush_scheduled_work() to wait on it. + * function may still be running on return from cancel_delayed_work(), unless + * it returns 1 and the work doesn't re-arm itself. Run flush_workqueue() or + * cancel_work_sync() to wait on it. */ static inline int cancel_delayed_work(struct delayed_work *work) { int ret; - ret = del_timer_sync(&work->timer); + ret = del_timer(&work->timer); if (ret) work_release(&work->work); return ret; diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 15ca89e..a5d53e0 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -181,6 +181,15 @@ #define XFRM_MSG_REPORT XFRM_MSG_REPORT XFRM_MSG_MIGRATE, #define XFRM_MSG_MIGRATE XFRM_MSG_MIGRATE + XFRM_MSG_NEWSADINFO, +#define XFRM_MSG_NEWSADINFO XFRM_MSG_NEWSADINFO + XFRM_MSG_GETSADINFO, +#define XFRM_MSG_GETSADINFO XFRM_MSG_GETSADINFO + + XFRM_MSG_NEWSPDINFO, +#define XFRM_MSG_NEWSPDINFO XFRM_MSG_NEWSPDINFO + XFRM_MSG_GETSPDINFO, +#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO __XFRM_MSG_MAX }; #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1) @@ -234,6 +243,17 @@ enum xfrm_ae_ftype_t { #define XFRM_AE_MAX (__XFRM_AE_MAX - 1) }; +/* SAD Table filter flags */ +enum xfrm_sad_ftype_t { + XFRM_SAD_UNSPEC, + XFRM_SAD_HMASK=1, + XFRM_SAD_HMAX=2, + XFRM_SAD_CNT=4, + __XFRM_SAD_MAX + +#define XFRM_SAD_MAX (__XFRM_SAD_MAX - 1) +}; + struct xfrm_userpolicy_type { __u8 type; __u16 reserved1; @@ -265,6 +285,46 @@ enum xfrm_attr_type_t { #define XFRMA_MAX (__XFRMA_MAX - 1) }; +enum xfrm_sadattr_type_t { + XFRMA_SAD_UNSPEC, + XFRMA_SADHMASK, + XFRMA_SADHMAX, + XFRMA_SADCNT, + __XFRMA_SAD_MAX + +#define XFRMA_SAD_MAX (__XFRMA_SAD_MAX - 1) +}; + +/* SPD Table filter flags */ +enum xfrm_spd_ftype_t { + XFRM_SPD_UNSPEC, + XFRM_SPD_HMASK=1, + XFRM_SPD_HMAX=2, + XFRM_SPD_ICNT=4, + XFRM_SPD_OCNT=8, + XFRM_SPD_FCNT=16, + XFRM_SPD_ISCNT=32, + XFRM_SPD_OSCNT=64, + XFRM_SPD_FSCNT=128, + __XFRM_SPD_MAX + +#define XFRM_SPD_MAX (__XFRM_SPD_MAX - 1) +}; +enum xfrm_spdattr_type_t { + XFRMA_SPD_UNSPEC, + XFRMA_SPDHMASK, + XFRMA_SPDHMAX, + XFRMA_SPDICNT, + XFRMA_SPDOCNT, + XFRMA_SPDFCNT, + XFRMA_SPDISCNT, + XFRMA_SPDOSCNT, + XFRMA_SPDFSCNT, + __XFRMA_SPD_MAX + +#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1) +}; + struct xfrm_usersa_info { struct xfrm_selector sel; struct xfrm_id id; diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h index d758a52..38c12fe 100644 --- a/include/media/cx2341x.h +++ b/include/media/cx2341x.h @@ -40,6 +40,7 @@ struct cx2341x_mpeg_params { /* stream */ enum v4l2_mpeg_stream_type stream_type; enum v4l2_mpeg_stream_vbi_fmt stream_vbi_fmt; + u16 stream_insert_nav_packets; /* audio */ enum v4l2_mpeg_audio_sampling_freq audio_sampling_freq; @@ -50,6 +51,7 @@ struct cx2341x_mpeg_params { enum v4l2_mpeg_audio_emphasis audio_emphasis; enum v4l2_mpeg_audio_crc audio_crc; u16 audio_properties; + u16 audio_mute; /* video */ enum v4l2_mpeg_video_encoding video_encoding; @@ -61,6 +63,8 @@ struct cx2341x_mpeg_params { u32 video_bitrate; u32 video_bitrate_peak; u16 video_temporal_decimation; + u16 video_mute; + u32 video_mute_yuv; /* encoding filters */ enum v4l2_mpeg_cx2341x_video_spatial_filter_mode video_spatial_filter_mode; @@ -162,7 +166,7 @@ #define CX2341X_ENC_SET_NUM_VSYNC_LINES #define CX2341X_ENC_SET_PLACEHOLDER 0xd7 #define CX2341X_ENC_MUTE_VIDEO 0xd9 #define CX2341X_ENC_MUTE_AUDIO 0xda -#define CX2341X_ENC_UNKNOWN 0xdb +#define CX2341X_ENC_SET_VERT_CROP_LINE 0xdb #define CX2341X_ENC_MISC 0xdc /* OSD API, specific to the cx23415 */ diff --git a/include/media/ivtv.h b/include/media/ivtv.h new file mode 100644 index 0000000..412b48e --- /dev/null +++ b/include/media/ivtv.h @@ -0,0 +1,65 @@ +/* + Public ivtv API header + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004-2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_IVTV_H +#define _LINUX_IVTV_H + +/* ivtv knows several distinct output modes: MPEG streaming, + YUV streaming, YUV updates through user DMA and the passthrough + mode. + + In order to clearly tell the driver that we are in user DMA + YUV mode you need to call IVTV_IOC_DMA_FRAME with y_source == NULL + first (althrough if you don't then the first time + DMA_FRAME is called the mode switch is done automatically). + + When you close the file handle the user DMA mode is exited again. + + While in one mode, you cannot use another mode (EBUSY is returned). + + All this means that if you want to change the YUV interlacing + for the user DMA YUV mode you first need to do call IVTV_IOC_DMA_FRAME + with y_source == NULL before you can set the correct format using + VIDIOC_S_FMT. + + Eventually all this should be replaced with a proper V4L2 API, + but for now we have to do it this way. */ + +struct ivtv_dma_frame { + enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_OUTPUT */ + __u32 pixelformat; /* 0 == same as destination */ + void __user *y_source; /* if NULL and type == V4L2_BUF_TYPE_VIDEO_OUTPUT, + then just switch to user DMA YUV output mode */ + void __user *uv_source; /* Unused for RGB pixelformats */ + struct v4l2_rect src; + struct v4l2_rect dst; + __u32 src_width; + __u32 src_height; +}; + +#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame) + +/* These are the VBI types as they appear in the embedded VBI private packets. */ +#define IVTV_SLICED_TYPE_TELETEXT_B (1) +#define IVTV_SLICED_TYPE_CAPTION_525 (4) +#define IVTV_SLICED_TYPE_WSS_625 (5) +#define IVTV_SLICED_TYPE_VPS (7) + +#endif /* _LINUX_IVTV_H */ diff --git a/include/media/tuner.h b/include/media/tuner.h index 99acf84..a41ac41 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -177,6 +177,8 @@ struct tuner_setup { unsigned short addr; /* I2C address */ unsigned int type; /* Tuner type */ unsigned int mode_mask; /* Allowed tuner modes */ + unsigned int config; /* configuraion for more complex tuners */ + int (*tuner_callback) (void *dev, int command,int arg); }; struct tuner { @@ -211,6 +213,9 @@ struct tuner { unsigned char tda827x_ver; unsigned int sgIF; + unsigned int config; + int (*tuner_callback) (void *dev, int command,int arg); + /* function ptrs */ void (*set_tv_freq)(struct i2c_client *c, unsigned int freq); void (*set_radio_freq)(struct i2c_client *c, unsigned int freq); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h new file mode 100644 index 0000000..09d16c4 --- /dev/null +++ b/include/media/v4l2-chip-ident.h @@ -0,0 +1,149 @@ +/* + v4l2 chip identifiers header + + This header provides a list of chip identifiers that can be returned + through the VIDIOC_G_CHIP_IDENT ioctl. + + Copyright (C) 2007 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef V4L2_CHIP_IDENT_H_ +#define V4L2_CHIP_IDENT_H_ + +/* VIDIOC_G_CHIP_IDENT: identifies the actual chip installed on the board */ +enum { + /* general idents: reserved range 0-49 */ + V4L2_IDENT_NONE = 0, /* No chip matched */ + V4L2_IDENT_AMBIGUOUS = 1, /* Match too general, multiple chips matched */ + V4L2_IDENT_UNKNOWN = 2, /* Chip found, but cannot identify */ + + /* module tvaudio: reserved range 50-99 */ + V4L2_IDENT_TVAUDIO = 50, /* A tvaudio chip, unknown which it is exactly */ + + /* module saa7110: just ident 100 */ + V4L2_IDENT_SAA7110 = 100, + + /* module saa7111: just ident 101 */ + V4L2_IDENT_SAA7111 = 101, + + /* module saa7115: reserved range 102-149 */ + V4L2_IDENT_SAA7113 = 103, + V4L2_IDENT_SAA7114 = 104, + V4L2_IDENT_SAA7115 = 105, + V4L2_IDENT_SAA7118 = 108, + + /* module saa7127: reserved range 150-199 */ + V4L2_IDENT_SAA7127 = 157, + V4L2_IDENT_SAA7129 = 159, + + /* module cx25840: reserved range 200-249 */ + V4L2_IDENT_CX25836 = 236, + V4L2_IDENT_CX25837 = 237, + V4L2_IDENT_CX25840 = 240, + V4L2_IDENT_CX25841 = 241, + V4L2_IDENT_CX25842 = 242, + V4L2_IDENT_CX25843 = 243, + + /* OmniVision sensors: reserved range 250-299 */ + V4L2_IDENT_OV7670 = 250, + + /* Conexant MPEG encoder/decoders: reserved range 410-420 */ + V4L2_IDENT_CX23415 = 415, + V4L2_IDENT_CX23416 = 416, + + /* module wm8739: just ident 8739 */ + V4L2_IDENT_WM8739 = 8739, + + /* module wm8775: just ident 8775 */ + V4L2_IDENT_WM8775 = 8775, + + /* module cs53132a: just ident 53132 */ + V4L2_IDENT_CS53l32A = 53132, + + /* module upd64031a: just ident 64031 */ + V4L2_IDENT_UPD64031A = 64031, + + /* module upd64083: just ident 64083 */ + V4L2_IDENT_UPD64083 = 64083, + + /* module msp34xx: reserved range 34000-34999 */ + V4L2_IDENT_MSP3400B = 34002, + V4L2_IDENT_MSP3410B = 34102, + + V4L2_IDENT_MSP3400C = 34003, + V4L2_IDENT_MSP3410C = 34103, + + V4L2_IDENT_MSP3400D = 34004, + V4L2_IDENT_MSP3410D = 34104, + V4L2_IDENT_MSP3405D = 34054, + V4L2_IDENT_MSP3415D = 34154, + V4L2_IDENT_MSP3407D = 34074, + V4L2_IDENT_MSP3417D = 34174, + + V4L2_IDENT_MSP3400G = 34007, + V4L2_IDENT_MSP3410G = 34107, + V4L2_IDENT_MSP3420G = 34207, + V4L2_IDENT_MSP3430G = 34307, + V4L2_IDENT_MSP3440G = 34407, + V4L2_IDENT_MSP3450G = 34507, + V4L2_IDENT_MSP3460G = 34607, + + V4L2_IDENT_MSP3401G = 34017, + V4L2_IDENT_MSP3411G = 34117, + V4L2_IDENT_MSP3421G = 34217, + V4L2_IDENT_MSP3431G = 34317, + V4L2_IDENT_MSP3441G = 34417, + V4L2_IDENT_MSP3451G = 34517, + V4L2_IDENT_MSP3461G = 34617, + + V4L2_IDENT_MSP3402G = 34027, + V4L2_IDENT_MSP3412G = 34127, + V4L2_IDENT_MSP3422G = 34227, + V4L2_IDENT_MSP3442G = 34427, + V4L2_IDENT_MSP3452G = 34527, + + V4L2_IDENT_MSP3405G = 34057, + V4L2_IDENT_MSP3415G = 34157, + V4L2_IDENT_MSP3425G = 34257, + V4L2_IDENT_MSP3435G = 34357, + V4L2_IDENT_MSP3445G = 34457, + V4L2_IDENT_MSP3455G = 34557, + V4L2_IDENT_MSP3465G = 34657, + + V4L2_IDENT_MSP3407G = 34077, + V4L2_IDENT_MSP3417G = 34177, + V4L2_IDENT_MSP3427G = 34277, + V4L2_IDENT_MSP3437G = 34377, + V4L2_IDENT_MSP3447G = 34477, + V4L2_IDENT_MSP3457G = 34577, + V4L2_IDENT_MSP3467G = 34677, + + /* module msp44xx: reserved range 44000-44999 */ + V4L2_IDENT_MSP4400G = 44007, + V4L2_IDENT_MSP4410G = 44107, + V4L2_IDENT_MSP4420G = 44207, + V4L2_IDENT_MSP4440G = 44407, + V4L2_IDENT_MSP4450G = 44507, + + V4L2_IDENT_MSP4408G = 44087, + V4L2_IDENT_MSP4418G = 44187, + V4L2_IDENT_MSP4428G = 44287, + V4L2_IDENT_MSP4448G = 44487, + V4L2_IDENT_MSP4458G = 44587, +}; + +#endif diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 6eaeec9..181a40c 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -98,6 +98,8 @@ u32 v4l2_ctrl_next(const u32 * const *ct struct i2c_client; /* forward reference */ int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 id_type, u32 chip_id); +int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip, + u32 ident, u32 revision); int v4l2_chip_match_host(u32 id_type, u32 chip_id); /* ------------------------------------------------------------------------- */ @@ -114,39 +116,6 @@ struct v4l2_decode_vbi_line { u32 type; /* VBI service type (V4L2_SLICED_*). 0 if no service found */ }; -/* VIDIOC_INT_G_CHIP_IDENT: identifies the actual chip installed on the board */ -enum v4l2_chip_ident { - /* general idents: reserved range 0-49 */ - V4L2_IDENT_UNKNOWN = 0, - - /* module saa7110: just ident= 100 */ - V4L2_IDENT_SAA7110 = 100, - - /* module saa7111: just ident= 101 */ - V4L2_IDENT_SAA7111 = 101, - - /* module saa7115: reserved range 102-149 */ - V4L2_IDENT_SAA7113 = 103, - V4L2_IDENT_SAA7114 = 104, - V4L2_IDENT_SAA7115 = 105, - V4L2_IDENT_SAA7118 = 108, - - /* module saa7127: reserved range 150-199 */ - V4L2_IDENT_SAA7127 = 157, - V4L2_IDENT_SAA7129 = 159, - - /* module cx25840: reserved range 200-249 */ - V4L2_IDENT_CX25836 = 236, - V4L2_IDENT_CX25837 = 237, - V4L2_IDENT_CX25840 = 240, - V4L2_IDENT_CX25841 = 241, - V4L2_IDENT_CX25842 = 242, - V4L2_IDENT_CX25843 = 243, - - /* OmniVision sensors - range 250-299 */ - V4L2_IDENT_OV7670 = 250, -}; - /* audio ioctls */ /* v4l device was opened in Radio mode, to be replaced by VIDIOC_INT_S_TUNER_MODE */ @@ -208,10 +177,6 @@ #define VIDIOC_INT_S_VBI_DATA _IOW ('d whether CC data from the first or second field should be obtained). */ #define VIDIOC_INT_G_VBI_DATA _IOWR('d', 106, struct v4l2_sliced_vbi_data) -/* Returns the chip identifier or V4L2_IDENT_UNKNOWN if no identification can - be made. */ -#define VIDIOC_INT_G_CHIP_IDENT _IOR ('d', 107, enum v4l2_chip_ident) - /* Sets I2S speed in bps. This is used to provide a standard way to select I2S clock used by driving digital audio streams at some board designs. Usual values for the frequency are 1024000 and 2048000. @@ -254,4 +219,12 @@ #define VIDIOC_INT_S_CRYSTAL_FREQ _IOW default values. */ #define VIDIOC_INT_INIT _IOW ('d', 114, u32) +/* Set v4l2_std_id for video OUTPUT devices. This is ignored by + video input devices. */ +#define VIDIOC_INT_S_STD_OUTPUT _IOW ('d', 115, v4l2_std_id) + +/* Get v4l2_std_id for video OUTPUT devices. This is ignored by + video input devices. */ +#define VIDIOC_INT_G_STD_OUTPUT _IOW ('d', 116, v4l2_std_id) + #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 1dd3d32..d62847f 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -127,6 +127,8 @@ struct video_device struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_video_output)(struct file *file, void *fh, struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_output_overlay) (struct file *file, void *fh, + struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_vbi_output) (struct file *file, void *fh, struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh, @@ -145,6 +147,8 @@ struct video_device struct v4l2_format *f); int (*vidioc_g_fmt_video_output)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_g_fmt_output_overlay) (struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_g_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); @@ -162,6 +166,8 @@ struct video_device struct v4l2_format *f); int (*vidioc_s_fmt_video_output)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_s_fmt_output_overlay) (struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_s_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); @@ -178,6 +184,8 @@ struct video_device struct v4l2_format *f); int (*vidioc_try_fmt_video_output)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_try_fmt_output_overlay)(struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_try_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); @@ -309,6 +317,8 @@ #ifdef CONFIG_VIDEO_ADV_DEBUG int (*vidioc_s_register) (struct file *file, void *fh, struct v4l2_register *reg); #endif + int (*vidioc_g_chip_ident) (struct file *file, void *fh, + struct v4l2_chip_ident *chip); #ifdef OBSOLETE_OWNER /* to be removed soon */ diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild index e0fe92b..4d46b3b 100644 --- a/include/mtd/Kbuild +++ b/include/mtd/Kbuild @@ -3,3 +3,5 @@ header-y += jffs2-user.h header-y += mtd-abi.h header-y += mtd-user.h header-y += nftl-user.h +header-y += ubi-header.h +header-y += ubi-user.h diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 8e501a7..f71dac4 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -24,6 +24,7 @@ #define MTD_ROM 2 #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 #define MTD_DATAFLASH 6 +#define MTD_UBIVOLUME 7 #define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h new file mode 100644 index 0000000..fa479c7 --- /dev/null +++ b/include/mtd/ubi-header.h @@ -0,0 +1,360 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. May be included by user-space. + */ + +#ifndef __UBI_HEADER_H__ +#define __UBI_HEADER_H__ + +#include + +/* The version of UBI images supported by this implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 + +/* + * Volume type constants used in the volume identifier header. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't allow the wear-leveling unit to move them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* + * ubi16_t/ubi32_t/ubi64_t - 16, 32, and 64-bit integers used in UBI on-flash + * data structures. + */ +typedef struct { + uint16_t int16; +} __attribute__ ((packed)) ubi16_t; + +typedef struct { + uint32_t int32; +} __attribute__ ((packed)) ubi32_t; + +typedef struct { + uint64_t int64; +} __attribute__ ((packed)) ubi64_t; + +/* + * In this implementation of UBI uses the big-endian format for on-flash + * integers. The below are the corresponding conversion macros. + */ +#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)}) +#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16)) + +#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)}) +#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32)) + +#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)}) +#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64)) + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(ubi32_t)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(ubi32_t)) + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: version of UBI implementation which is supposed to accept this + * UBI image + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start + * @padding2: reserved for future, zeroes + * @hdr_crc: erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater then the current UBI + * version, the image is rejected. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * physical eraseblock. These values have to be the same for all physical + * eraseblocks. + */ +struct ubi_ec_hdr { + ubi32_t magic; + uint8_t version; + uint8_t padding1[3]; + ubi64_t ec; /* Warning: the current limit is 31-bit anyway! */ + ubi32_t vid_hdr_offset; + ubi32_t data_offset; + uint8_t padding2[36]; + ubi32_t hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: if this logical eraseblock was copied from another physical + * eraseblock (for wear-leveling reasons) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: ID of this volume + * @lnum: logical eraseblock number + * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be + * removed, kept only for not breaking older UBI users) + * @data_size: how many bytes of data this logical eraseblock contains + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this physical eraseblock are not + * used + * @data_crc: CRC checksum of the data stored in this logical eraseblock + * @padding1: reserved for future, zeroes + * @sqnum: sequence number + * @padding2: reserved for future, zeroes + * @hdr_crc: volume identifier header CRC checksum + * + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (sequence number) is used to distinguish between older and newer versions of + * logical eraseblocks. + * + * There are 2 situations when there may be more then one physical eraseblock + * corresponding to the same logical eraseblock, i.e., having the same @vol_id + * and @lnum values in the volume identifier header. Suppose we have a logical + * eraseblock L and it is mapped to the physical eraseblock P. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. + * + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. + * + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * checksum is correct, this physical eraseblock is selected (P1). Otherwise + * the older one (P) is selected. + * + * Note, there is an obsolete @leb_ver field which was used instead of @sqnum + * in the past. But it is not used anymore and we keep it in order to be able + * to deal with old UBI images. It will be removed at some point. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for various internal + * UBI purposes. In this implementation there is only one internal volume - the + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. + * + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * data of the physical eraseblock was moved by the wear-leveling unit, then + * the wear-leveling unit calculates the data CRC and stores it in the + * @data_crc field. And of course, the @copy_flag is %in this case. + * + * The @data_size field is used only for static volumes because UBI has to know + * how many bytes of data are stored in this eraseblock. For dynamic volumes, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains data size. + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + */ +struct ubi_vid_hdr { + ubi32_t magic; + uint8_t version; + uint8_t vol_type; + uint8_t copy_flag; + uint8_t compat; + ubi32_t vol_id; + ubi32_t lnum; + ubi32_t leb_ver; /* obsolete, to be removed, don't use */ + ubi32_t data_size; + ubi32_t used_ebs; + ubi32_t data_pad; + ubi32_t data_crc; + uint8_t padding1[4]; + ubi64_t sqnum; + uint8_t padding2[12]; + ubi32_t hdr_crc; +} __attribute__ ((packed)); + +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 + +/* + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* The layout volume contains the volume table */ + +#define UBI_LAYOUT_VOL_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "layout volume" +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of the volume table record */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) + +/* Size of the volume table record without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(ubi32_t)) + +/** + * struct ubi_vtbl_record - a record in the volume table. + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length + * @name: the volume name + * @padding2: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vtbl_record + * objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * the physical eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vtbl_record { + ubi32_t reserved_pebs; + ubi32_t alignment; + ubi32_t data_pad; + uint8_t vol_type; + uint8_t upd_marker; + ubi16_t name_len; + uint8_t name[UBI_VOL_NAME_MAX+1]; + uint8_t padding2[24]; + ubi32_t crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_HEADER_H__ */ diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h new file mode 100644 index 0000000..fe06ded --- /dev/null +++ b/include/mtd/ubi-user.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_USER_H__ +#define __UBI_USER_H__ + +/* + * UBI volume creation + * ~~~~~~~~~~~~~~~~~~~ + * + * UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character + * device. A &struct ubi_mkvol_req object has to be properly filled and a + * pointer to it has to be passed to the IOCTL. + * + * UBI volume deletion + * ~~~~~~~~~~~~~~~~~~~ + * + * To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character + * device should be used. A pointer to the 32-bit volume ID hast to be passed + * to the IOCTL. + * + * UBI volume re-size + * ~~~~~~~~~~~~~~~~~~ + * + * To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character + * device should be used. A &struct ubi_rsvol_req object has to be properly + * filled and a pointer to it has to be passed to the IOCTL. + * + * UBI volume update + * ~~~~~~~~~~~~~~~~~ + * + * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the + * corresponding UBI volume character device. A pointer to a 64-bit update + * size should be passed to the IOCTL. After then, UBI expects user to write + * this number of bytes to the volume character device. The update is finished + * when the claimed number of bytes is passed. So, the volume update sequence + * is something like: + * + * fd = open("/dev/my_volume"); + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); + */ + +/* + * When a new volume is created, users may either specify the volume number they + * want to create or to let UBI automatically assign a volume number using this + * constant. + */ +#define UBI_VOL_NUM_AUTO (-1) + +/* Maximum volume name length */ +#define UBI_MAX_VOLUME_NAME 127 + +/* IOCTL commands of UBI character devices */ + +#define UBI_IOC_MAGIC 'o' + +/* Create an UBI volume */ +#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) +/* Remove an UBI volume */ +#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) +/* Re-size an UBI volume */ +#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) + +/* IOCTL commands of UBI volume character devices */ + +#define UBI_VOL_IOC_MAGIC 'O' + +/* Start UBI volume update */ +#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) +/* An eraseblock erasure command, used for debugging, disabled by default */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) + +/* + * UBI volume type constants. + * + * @UBI_DYNAMIC_VOLUME: dynamic volume + * @UBI_STATIC_VOLUME: static volume + */ +enum { + UBI_DYNAMIC_VOLUME = 3, + UBI_STATIC_VOLUME = 4 +}; + +/** + * struct ubi_mkvol_req - volume description data structure used in + * volume creation requests. + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding1: reserved for future, not used + * @name_len: volume name length + * @padding2: reserved for future, not used + * @name: volume name + * + * This structure is used by userspace programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical + * eraseblock. This means, that the size of logical eraseblocks will be aligned + * to this number, i.e., + * (UBI device logical eraseblock size) mod (@alignment) = 0. + * + * To put it differently, the logical eraseblock of this volume may be slightly + * shortened in order to make it properly aligned. The alignment has to be + * multiple of the flash minimal input/output unit, or %1 to utilize the entire + * available space of logical eraseblocks. + * + * The @alignment field may be useful, for example, when one wants to maintain + * a block device on top of an UBI volume. In this case, it is desirable to fit + * an integer number of blocks in logical eraseblocks of this UBI volume. With + * alignment it is possible to update this volume using plane UBI volume image + * BLOBs, without caring about how to properly align them. + */ +struct ubi_mkvol_req { + int32_t vol_id; + int32_t alignment; + int64_t bytes; + int8_t vol_type; + int8_t padding1; + int16_t name_len; + int8_t padding2[4]; + char name[UBI_MAX_VOLUME_NAME+1]; +} __attribute__ ((packed)); + +/** + * struct ubi_rsvol_req - a data structure used in volume re-size requests. + * @vol_id: ID of the volume to re-size + * @bytes: new size of the volume in bytes + * + * Re-sizing is possible for both dynamic and static volumes. But while dynamic + * volumes may be re-sized arbitrarily, static volumes cannot be made to be + * smaller then the number of bytes they bear. To arbitrarily shrink a static + * volume, it must be wiped out first (by means of volume update operation with + * zero number of bytes). + */ +struct ubi_rsvol_req { + int64_t bytes; + int32_t vol_id; +} __attribute__ ((packed)); + +#endif /* __UBI_USER_H__ */ diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 88df8fc..f3531d0 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -73,7 +73,9 @@ extern int ipv6_get_saddr(struct dst_e extern int ipv6_dev_get_saddr(struct net_device *dev, struct in6_addr *daddr, struct in6_addr *saddr); -extern int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *); +extern int ipv6_get_lladdr(struct net_device *dev, + struct in6_addr *addr, + unsigned char banned_flags); extern int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2); extern void addrconf_join_solict(struct net_device *dev, diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h new file mode 100644 index 0000000..00c2eaa --- /dev/null +++ b/include/net/af_rxrpc.h @@ -0,0 +1,57 @@ +/* RxRPC kernel service interface definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef _NET_RXRPC_H +#define _NET_RXRPC_H + +#ifdef __KERNEL__ + +#include + +struct rxrpc_call; + +/* + * the mark applied to socket buffers that may be intercepted + */ +enum { + RXRPC_SKB_MARK_DATA, /* data message */ + RXRPC_SKB_MARK_FINAL_ACK, /* final ACK received message */ + RXRPC_SKB_MARK_BUSY, /* server busy message */ + RXRPC_SKB_MARK_REMOTE_ABORT, /* remote abort message */ + RXRPC_SKB_MARK_NET_ERROR, /* network error message */ + RXRPC_SKB_MARK_LOCAL_ERROR, /* local error message */ + RXRPC_SKB_MARK_NEW_CALL, /* local error message */ +}; + +typedef void (*rxrpc_interceptor_t)(struct sock *, unsigned long, + struct sk_buff *); +extern void rxrpc_kernel_intercept_rx_messages(struct socket *, + rxrpc_interceptor_t); +extern struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *, + struct sockaddr_rxrpc *, + struct key *, + unsigned long, + gfp_t); +extern int rxrpc_kernel_send_data(struct rxrpc_call *, struct msghdr *, + size_t); +extern void rxrpc_kernel_abort_call(struct rxrpc_call *, u32); +extern void rxrpc_kernel_end_call(struct rxrpc_call *); +extern bool rxrpc_kernel_is_data_last(struct sk_buff *); +extern u32 rxrpc_kernel_get_abort_code(struct sk_buff *); +extern int rxrpc_kernel_get_error_number(struct sk_buff *); +extern void rxrpc_kernel_data_delivered(struct sk_buff *); +extern void rxrpc_kernel_free_skb(struct sk_buff *); +extern struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *, + unsigned long); +extern int rxrpc_kernel_reject_call(struct socket *); + +#endif /* __KERNEL__ */ +#endif /* _NET_RXRPC_H */ diff --git a/include/net/ax25.h b/include/net/ax25.h index 47ff2f4..99a4e36 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -263,8 +263,8 @@ static __inline__ void ax25_cb_put(ax25_ static inline __be16 ax25_type_trans(struct sk_buff *skb, struct net_device *dev) { skb->dev = dev; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; - skb->mac.raw = skb->data; return htons(ETH_P_AX25); } diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 41456c1..93ce272 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -709,6 +709,24 @@ struct hci_sco_hdr { __u8 dlen; } __attribute__ ((packed)); +#ifdef __KERNEL__ +#include +static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb) +{ + return (struct hci_event_hdr *)skb->data; +} + +static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb) +{ + return (struct hci_acl_hdr *)skb->data; +} + +static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb) +{ + return (struct hci_sco_hdr *)skb->data; +} +#endif + /* Command opcode pack/unpack */ #define hci_opcode_pack(ogf, ocf) (__u16) ((ocf & 0x03ff)|(ogf << 10)) #define hci_opcode_ogf(op) (op >> 10) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h new file mode 100644 index 0000000..88171f8 --- /dev/null +++ b/include/net/cfg80211.h @@ -0,0 +1,40 @@ +#ifndef __NET_CFG80211_H +#define __NET_CFG80211_H + +#include +#include +#include + +/* + * 802.11 configuration in-kernel interface + * + * Copyright 2006 Johannes Berg + */ + +/* from net/wireless.h */ +struct wiphy; + +/** + * struct cfg80211_ops - backend description for wireless configuration + * + * This struct is registered by fullmac card drivers and/or wireless stacks + * in order to handle configuration requests on their interfaces. + * + * All callbacks except where otherwise noted should return 0 + * on success or a negative error code. + * + * All operations are currently invoked under rtnl for consistency with the + * wireless extensions but this is subject to reevaluation as soon as this + * code is used more widely and we have a first user without wext. + * + * @add_virtual_intf: create a new virtual interface with the given name + * + * @del_virtual_intf: remove the virtual interface determined by ifindex. + */ +struct cfg80211_ops { + int (*add_virtual_intf)(struct wiphy *wiphy, char *name, + unsigned int type); + int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); +}; + +#endif /* __NET_CFG80211_H */ diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index 4c9522c..4f90f55 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -120,7 +120,7 @@ #endif */ #define CIPSO_V4_OPTEXIST(x) (IPCB(x)->opt.cipso != 0) -#define CIPSO_V4_OPTPTR(x) ((x)->nh.raw + IPCB(x)->opt.cipso) +#define CIPSO_V4_OPTPTR(x) (skb_network_header(x) + IPCB(x)->opt.cipso) /* * DOI List Functions diff --git a/include/net/compat.h b/include/net/compat.h index 9859b60..406db24 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -25,6 +25,7 @@ struct compat_cmsghdr { }; extern int compat_sock_get_timestamp(struct sock *, struct timeval __user *); +extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *); #else /* defined(CONFIG_COMPAT) */ #define compat_msghdr msghdr /* to avoid compiler warnings */ diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h index f01626c..3012511 100644 --- a/include/net/dn_fib.h +++ b/include/net/dn_fib.h @@ -148,17 +148,8 @@ extern void dn_fib_rules_cleanup(void); extern unsigned dnet_addr_type(__le16 addr); extern int dn_fib_lookup(struct flowi *fl, struct dn_fib_res *res); -/* - * rtnetlink interface - */ -extern int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); -extern int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); extern int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb); -extern int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); -extern int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); -extern int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb); - extern void dn_fib_free_info(struct dn_fib_info *fi); static inline void dn_fib_info_put(struct dn_fib_info *fi) diff --git a/include/net/dn_route.h b/include/net/dn_route.h index a566944..c10e8e7 100644 --- a/include/net/dn_route.h +++ b/include/net/dn_route.h @@ -18,7 +18,6 @@ #define _NET_DN_ROUTE_H extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri); extern int dn_route_output_sock(struct dst_entry **pprt, struct flowi *, struct sock *sk, int flags); extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb); -extern int dn_cache_getroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); extern void dn_rt_cache_flush(int delay); /* Masks for flags field */ diff --git a/include/net/esp.h b/include/net/esp.h index 713d039..d05d8d2 100644 --- a/include/net/esp.h +++ b/include/net/esp.h @@ -40,8 +40,6 @@ struct esp_data } auth; }; -extern int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len); -extern int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer); extern void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len); static inline int esp_mac_digest(struct esp_data *esp, struct sk_buff *skb, diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index d585ea9..ed3a887 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -5,7 +5,7 @@ #include #include #include #include -#include +#include struct fib_rule { @@ -19,6 +19,8 @@ struct fib_rule u32 flags; u32 table; u8 action; + u32 target; + struct fib_rule * ctarget; struct rcu_head rcu; }; @@ -35,6 +37,8 @@ struct fib_rules_ops struct list_head list; int rule_size; int addr_size; + int unresolved_rules; + int nr_goto_rules; int (*action)(struct fib_rule *, struct flowi *, int, @@ -55,6 +59,10 @@ struct fib_rules_ops u32 (*default_pref)(void); size_t (*nlmsg_payload)(struct fib_rule *); + /* Called after modifications to the rules set, must flush + * the route cache if one exists. */ + void (*flush_cache)(void); + int nlgroup; struct nla_policy *policy; struct list_head *rules_list; @@ -66,7 +74,8 @@ #define FRA_GENERIC_POLICY \ [FRA_PRIORITY] = { .type = NLA_U32 }, \ [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ - [FRA_TABLE] = { .type = NLA_U32 } + [FRA_TABLE] = { .type = NLA_U32 }, \ + [FRA_GOTO] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) { @@ -98,11 +107,4 @@ extern int fib_rules_unregister(struct extern int fib_rules_lookup(struct fib_rules_ops *, struct flowi *, int flags, struct fib_lookup_arg *); - -extern int fib_nl_newrule(struct sk_buff *, - struct nlmsghdr *, void *); -extern int fib_nl_delrule(struct sk_buff *, - struct nlmsghdr *, void *); -extern int fib_rules_dump(struct sk_buff *, - struct netlink_callback *, int); #endif diff --git a/include/net/flow.h b/include/net/flow.h index ce4b10d..f3cc1f8 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -97,4 +97,10 @@ extern void *flow_cache_lookup(struct fl extern void flow_cache_flush(void); extern atomic_t flow_cache_genid; +static inline int flow_cache_uli_match(struct flowi *fl1, struct flowi *fl2) +{ + return (fl1->proto == fl2->proto && + !memcmp(&fl1->uli_u, &fl2->uli_u, sizeof(fl1->uli_u))); +} + #endif diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h index e02d85f..d56b292 100644 --- a/include/net/ieee80211.h +++ b/include/net/ieee80211.h @@ -6,8 +6,8 @@ * LAN access point) driver for Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2003, Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen * * Adaption to a generic IEEE 802.11 stack by James Ketrenos * diff --git a/include/net/ieee80211_crypt.h b/include/net/ieee80211_crypt.h index eb47641..b3d65e0 100644 --- a/include/net/ieee80211_crypt.h +++ b/include/net/ieee80211_crypt.h @@ -3,8 +3,8 @@ * for Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2003, Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen * * Adaption to a generic IEEE 802.11 stack by James Ketrenos * diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 429b738..a0c2b41 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -66,7 +66,9 @@ #define PKTHDR_RADIOTAP_VERSION 0 */ #define IEEE80211_RADIOTAP_HDRLEN 64 -/* The radio capture header precedes the 802.11 header. */ +/* The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ struct ieee80211_radiotap_header { u8 it_version; /* Version 0. Only increases * for drastic changes, @@ -74,12 +76,12 @@ struct ieee80211_radiotap_header { * new fields does not count. */ u8 it_pad; - u16 it_len; /* length of the whole + __le16 it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ - u32 it_present; /* A bitmap telling which + __le32 it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. @@ -88,89 +90,102 @@ struct ieee80211_radiotap_header { */ }; -/* Name Data type Units - * ---- --------- ----- +/* Name Data type Units + * ---- --------- ----- * - * IEEE80211_RADIOTAP_TSFT u64 microseconds + * IEEE80211_RADIOTAP_TSFT __le64 microseconds * * Value in microseconds of the MAC's 64-bit 802.11 Time * Synchronization Function timer when the first bit of the * MPDU arrived at the MAC. For received frames, only. * - * IEEE80211_RADIOTAP_CHANNEL 2 x u16 MHz, bitmap + * IEEE80211_RADIOTAP_CHANNEL 2 x __le16 MHz, bitmap * * Tx/Rx frequency in MHz, followed by flags (see below). * - * IEEE80211_RADIOTAP_FHSS u16 see below + * IEEE80211_RADIOTAP_FHSS __le16 see below * * For frequency-hopping radios, the hop set (first byte) * and pattern (second byte). * - * IEEE80211_RADIOTAP_RATE u8 500kb/s + * IEEE80211_RADIOTAP_RATE u8 500kb/s * * Tx/Rx data rate * - * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from - * one milliwatt (dBm) + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from + * one milliwatt (dBm) * * RF signal power at the antenna, decibel difference from * one milliwatt. * - * IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from - * one milliwatt (dBm) + * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from + * one milliwatt (dBm) * * RF noise power at the antenna, decibel difference from one * milliwatt. * - * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) * * RF signal power at the antenna, decibel difference from an * arbitrary, fixed reference. * - * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) + * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) * * RF noise power at the antenna, decibel difference from an * arbitrary, fixed reference point. * - * IEEE80211_RADIOTAP_LOCK_QUALITY u16 unitless + * IEEE80211_RADIOTAP_LOCK_QUALITY __le16 unitless * * Quality of Barker code lock. Unitless. Monotonically * nondecreasing with "better" lock strength. Called "Signal * Quality" in datasheets. (Is there a standard way to measure * this?) * - * IEEE80211_RADIOTAP_TX_ATTENUATION u16 unitless + * IEEE80211_RADIOTAP_TX_ATTENUATION __le16 unitless * * Transmit power expressed as unitless distance from max * power set at factory calibration. 0 is max power. * Monotonically nondecreasing with lower power levels. * - * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u16 decibels (dB) + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION __le16 decibels (dB) * * Transmit power expressed as decibel distance from max power * set at factory calibration. 0 is max power. Monotonically * nondecreasing with lower power levels. * - * IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from - * one milliwatt (dBm) + * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from + * one milliwatt (dBm) * * Transmit power expressed as dBm (decibels from a 1 milliwatt * reference). This is the absolute power level measured at * the antenna port. * - * IEEE80211_RADIOTAP_FLAGS u8 bitmap + * IEEE80211_RADIOTAP_FLAGS u8 bitmap * * Properties of transmitted and received frames. See flags * defined below. * - * IEEE80211_RADIOTAP_ANTENNA u8 antenna index + * IEEE80211_RADIOTAP_ANTENNA u8 antenna index * * Unitless indication of the Rx/Tx antenna for this packet. * The first antenna is antenna 0. * - * IEEE80211_RADIOTAP_FCS u32 data + * IEEE80211_RADIOTAP_RX_FLAGS __le16 bitmap + * + * Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS __le16 bitmap + * + * Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES u8 data + * + * Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES u8 data + * + * Number of unicast retries a transmitted frame used. * - * FCS from frame in network byte order. */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, @@ -187,7 +202,11 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_ANTENNA = 11, IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, IEEE80211_RADIOTAP_DB_ANTNOISE = 13, - IEEE80211_RADIOTAP_EXT = 31, + IEEE80211_RADIOTAP_RX_FLAGS = 14, + IEEE80211_RADIOTAP_TX_FLAGS = 15, + IEEE80211_RADIOTAP_RTS_RETRIES = 16, + IEEE80211_RADIOTAP_DATA_RETRIES = 17, + IEEE80211_RADIOTAP_EXT = 31 }; /* Channel flags. */ @@ -219,6 +238,14 @@ #define IEEE80211_RADIOTAP_F_DATAPAD 0x2 * 802.11 header and payload * (to 32-bit boundary) */ +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive + * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ /* Ugly macro to convert literal channel numbers into their mhz equivalents * There are certianly some conditions that will break this (like feeding it '30') diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index c28e424..668056b 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -19,6 +19,9 @@ #if defined(CONFIG_IPV6) || defined (CON #include #include #include +#include + +#include #include @@ -28,12 +31,11 @@ struct inet_hashinfo; static inline unsigned int inet6_ehashfn(const struct in6_addr *laddr, const u16 lport, const struct in6_addr *faddr, const __be16 fport) { - unsigned int hashent = (lport ^ (__force u16)fport); + u32 ports = (lport ^ (__force u16)fport); - hashent ^= (__force u32)(laddr->s6_addr32[3] ^ faddr->s6_addr32[3]); - hashent ^= hashent >> 16; - hashent ^= hashent >> 8; - return hashent; + return jhash_3words((__force u32)laddr->s6_addr32[3], + (__force u32)faddr->s6_addr32[3], + ports, inet_ehash_secret); } static inline int inet6_sk_ehashfn(const struct sock *sk) diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index 10117c8..de8399a 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h @@ -114,13 +114,13 @@ static inline int INET_ECN_set_ce(struct { switch (skb->protocol) { case __constant_htons(ETH_P_IP): - if (skb->nh.raw + sizeof(struct iphdr) <= skb->tail) - return IP_ECN_set_ce(skb->nh.iph); + if (skb->network_header + sizeof(struct iphdr) <= skb->tail) + return IP_ECN_set_ce(ip_hdr(skb)); break; case __constant_htons(ETH_P_IPV6): - if (skb->nh.raw + sizeof(struct ipv6hdr) <= skb->tail) - return IP6_ECN_set_ce(skb->nh.ipv6h); + if (skb->network_header + sizeof(struct ipv6hdr) <= skb->tail) + return IP6_ECN_set_ce(ipv6_hdr(skb)); break; } diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index ce6da97..62daf21 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -19,6 +19,7 @@ #define _INET_SOCK_H #include #include +#include #include #include @@ -167,13 +168,15 @@ #endif extern int inet_sk_rebuild_header(struct sock *sk); +extern u32 inet_ehash_secret; +extern void build_ehash_secret(void); + static inline unsigned int inet_ehashfn(const __be32 laddr, const __u16 lport, const __be32 faddr, const __be16 fport) { - unsigned int h = ((__force __u32)laddr ^ lport) ^ ((__force __u32)faddr ^ (__force __u32)fport); - h ^= h >> 16; - h ^= h >> 8; - return h; + return jhash_2words((__force __u32) laddr ^ (__force __u32) faddr, + ((__u32) lport) << 16 | (__force __u32)fport, + inet_ehash_secret); } static inline int inet_sk_ehashfn(const struct sock *sk) diff --git a/include/net/ip.h b/include/net/ip.h index e79c3e3..bb207db 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -25,6 +25,7 @@ #define _IP_H #include #include #include +#include #include #include @@ -43,6 +44,11 @@ #define IPSKB_FRAG_COMPLETE 8 #define IPSKB_REROUTED 16 }; +static inline unsigned int ip_hdrlen(const struct sk_buff *skb) +{ + return ip_hdr(skb)->ihl * 4; +} + struct ipcm_cookie { __be32 addr; @@ -74,7 +80,6 @@ struct msghdr; struct net_device; struct packet_type; struct rtable; -struct sk_buff; struct sockaddr; extern void ip_mc_dropsocket(struct sock *); @@ -161,6 +166,10 @@ #define NET_INC_STATS_USER(field) SNMP_ #define NET_ADD_STATS_BH(field, adnd) SNMP_ADD_STATS_BH(net_statistics, field, adnd) #define NET_ADD_STATS_USER(field, adnd) SNMP_ADD_STATS_USER(net_statistics, field, adnd) +extern unsigned long snmp_fold_field(void *mib[], int offt); +extern int snmp_mib_init(void *ptr[2], size_t mibsize, size_t mibalign); +extern void snmp_mib_free(void *ptr[2]); + extern int sysctl_local_port_range[2]; extern int sysctl_ip_default_ttl; extern int sysctl_ip_nonlocal_bind; diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index cf355a3..c48ea87 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -219,8 +219,6 @@ extern void fib6_init(void); extern void fib6_rules_init(void); extern void fib6_rules_cleanup(void); -extern int fib6_rules_dump(struct sk_buff *, - struct netlink_callback *); #endif #endif diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 4e927eb..5456fdd 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -116,12 +116,7 @@ extern void rt6_pmtu_discovery(struct struct net_device *dev, u32 pmtu); -struct nlmsghdr; struct netlink_callback; -extern int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb); -extern int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); -extern int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); -extern int inet6_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); struct rt6_rtnl_dump_arg { diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 36c635c..5a4a036 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -215,10 +215,6 @@ #endif /* CONFIG_IP_MULTIPLE_TABLES */ /* Exported by fib_frontend.c */ extern struct nla_policy rtm_ipv4_policy[]; extern void ip_fib_init(void); -extern int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); -extern int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); -extern int inet_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); -extern int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb); extern int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif, struct net_device *dev, __be32 *spec_dst, u32 *itag); extern void fib_select_multipath(const struct flowi *flp, struct fib_result *res); @@ -235,8 +231,6 @@ extern __be32 __fib_res_prefsrc(struct extern struct fib_table *fib_hash_init(u32 id); #ifdef CONFIG_IP_MULTIPLE_TABLES -extern int fib4_rules_dump(struct sk_buff *skb, struct netlink_callback *cb); - extern void __init fib4_rules_init(void); #ifdef CONFIG_NET_CLS_ROUTE diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 00328b7..f70afef 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -166,13 +166,6 @@ #define UDP6_INC_STATS_USER(field, is_ud if (is_udplite) SNMP_INC_STATS_USER(udplite_stats_in6, field); \ else SNMP_INC_STATS_USER(udp_stats_in6, field); } while(0) -int snmp6_register_dev(struct inet6_dev *idev); -int snmp6_unregister_dev(struct inet6_dev *idev); -int snmp6_alloc_dev(struct inet6_dev *idev); -int snmp6_free_dev(struct inet6_dev *idev); -int snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign); -void snmp6_mib_free(void *ptr[2]); - struct ip6_ra_chain { struct ip6_ra_chain *next; @@ -605,8 +598,20 @@ extern int udplite6_proc_init(void); extern void udplite6_proc_exit(void); extern int ipv6_misc_proc_init(void); extern void ipv6_misc_proc_exit(void); +extern int snmp6_register_dev(struct inet6_dev *idev); +extern int snmp6_unregister_dev(struct inet6_dev *idev); extern struct rt6_statistics rt6_stats; +#else +static inline int snmp6_register_dev(struct inet6_dev *idev) +{ + return 0; +} + +static inline int snmp6_unregister_dev(struct inet6_dev *idev) +{ + return 0; +} #endif #ifdef CONFIG_SYSCTL diff --git a/include/net/ipx.h b/include/net/ipx.h index c6b2ee6..4cc0b4e 100644 --- a/include/net/ipx.h +++ b/include/net/ipx.h @@ -43,7 +43,7 @@ #define IPX_TYPE_PPROP 0x14 /* complica static __inline__ struct ipxhdr *ipx_hdr(struct sk_buff *skb) { - return (struct ipxhdr *)skb->h.raw; + return (struct ipxhdr *)skb_transport_header(skb); } struct ipx_interface { diff --git a/include/net/iucv/iucv.h b/include/net/iucv/iucv.h index 746e741..fd70adb 100644 --- a/include/net/iucv/iucv.h +++ b/include/net/iucv/iucv.h @@ -16,7 +16,7 @@ * completed a register, it can exploit the other functions. * For furthur reference on all IUCV functionality, refer to the * CP Programming Services book, also available on the web thru - * www.ibm.com/s390/vm/pubs, manual # SC24-5760 + * www.vm.ibm.com/pubs, manual # SC24-6084 * * Definition of Return Codes * - All positive return codes including zero are reflected back diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index 8a83018..f23d07c 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -431,26 +431,7 @@ struct iw_public_data { * Those may be called only within the kernel. */ -/* First : function strictly used inside the kernel */ - -/* Handle /proc/net/wireless, called in net/code/dev.c */ -extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, - int length); - -/* Handle IOCTLs, called in net/core/dev.c */ -extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); - -/* Handle RtNetlink requests, called in net/core/rtnetlink.c */ -extern int wireless_rtnetlink_set(struct net_device * dev, - char * data, - int len); -extern int wireless_rtnetlink_get(struct net_device * dev, - char * data, - int len, - char ** p_buf, - int * p_len); - -/* Second : functions that may be called by driver modules */ +/* functions that may be called by driver modules */ /* Send a single event to user space */ extern void wireless_send_event(struct net_device * dev, diff --git a/include/net/llc_pdu.h b/include/net/llc_pdu.h index aa33a47..4a8f58b 100644 --- a/include/net/llc_pdu.h +++ b/include/net/llc_pdu.h @@ -203,7 +203,7 @@ struct llc_pdu_sn { static inline struct llc_pdu_sn *llc_pdu_sn_hdr(struct sk_buff *skb) { - return (struct llc_pdu_sn *)skb->nh.raw; + return (struct llc_pdu_sn *)skb_network_header(skb); } /* Un-numbered PDU format (3 bytes in length) */ @@ -215,12 +215,7 @@ struct llc_pdu_un { static inline struct llc_pdu_un *llc_pdu_un_hdr(struct sk_buff *skb) { - return (struct llc_pdu_un *)skb->nh.raw; -} - -static inline void *llc_set_pdu_hdr(struct sk_buff *skb, void *ptr) -{ - return skb->nh.raw = ptr; + return (struct llc_pdu_un *)skb_network_header(skb); } /** @@ -237,7 +232,11 @@ static inline void llc_pdu_header_init(s u8 ssap, u8 dsap, u8 cr) { const int hlen = type == LLC_PDU_TYPE_U ? 3 : 4; - struct llc_pdu_un *pdu = llc_set_pdu_hdr(skb, skb_push(skb, hlen)); + struct llc_pdu_un *pdu; + + skb_push(skb, hlen); + skb_reset_network_header(skb); + pdu = llc_pdu_un_hdr(skb); pdu->dsap = dsap; pdu->ssap = ssap; pdu->ssap |= cr; diff --git a/include/net/neighbour.h b/include/net/neighbour.h index ad7fe11..a4f2618 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -24,6 +24,7 @@ #include #include #include +#include #define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_REACHABLE|NUD_DELAY|NUD_PROBE) #define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) @@ -213,16 +214,7 @@ extern void pneigh_enqueue(struct neig extern struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, const void *key, struct net_device *dev, int creat); extern int pneigh_delete(struct neigh_table *tbl, const void *key, struct net_device *dev); -struct netlink_callback; -struct nlmsghdr; -extern int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb); -extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); -extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); extern void neigh_app_ns(struct neighbour *n); - -extern int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb); -extern int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); - extern void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie); extern void __neigh_for_each_release(struct neigh_table *tbl, int (*cb)(struct neighbour *)); extern void pneigh_for_each(struct neigh_table *tbl, void (*cb)(struct pneigh_entry *)); diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 0e690e3..1c6b8bd 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -250,6 +250,11 @@ static inline int nf_ct_is_dying(struct return test_bit(IPS_DYING_BIT, &ct->status); } +static inline int nf_ct_is_untracked(const struct sk_buff *skb) +{ + return (skb->nfct == &nf_conntrack_untracked.ct_general); +} + extern unsigned int nf_conntrack_htable_size; extern int nf_conntrack_checksum; extern atomic_t nf_conntrack_count; diff --git a/include/net/netfilter/nf_conntrack_compat.h b/include/net/netfilter/nf_conntrack_compat.h deleted file mode 100644 index 6f84c1f..0000000 --- a/include/net/netfilter/nf_conntrack_compat.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef _NF_CONNTRACK_COMPAT_H -#define _NF_CONNTRACK_COMPAT_H - -#ifdef __KERNEL__ - -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - -#include -#include - -#ifdef CONFIG_IP_NF_CONNTRACK_MARK -static inline u_int32_t *nf_ct_get_mark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo); - - if (ct) - return &ct->mark; - else - return NULL; -} -#endif /* CONFIG_IP_NF_CONNTRACK_MARK */ - -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK -static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo); - - if (ct) - return &ct->secmark; - else - return NULL; -} -#endif /* CONFIG_IP_NF_CONNTRACK_SECMARK */ - -#ifdef CONFIG_IP_NF_CT_ACCT -static inline struct ip_conntrack_counter * -nf_ct_get_counters(const struct sk_buff *skb) -{ - enum ip_conntrack_info ctinfo; - struct ip_conntrack *ct = ip_conntrack_get(skb, &ctinfo); - - if (ct) - return ct->counters; - else - return NULL; -} -#endif /* CONFIG_IP_NF_CT_ACCT */ - -static inline int nf_ct_is_untracked(const struct sk_buff *skb) -{ - return (skb->nfct == &ip_conntrack_untracked.ct_general); -} - -static inline void nf_ct_untrack(struct sk_buff *skb) -{ - skb->nfct = &ip_conntrack_untracked.ct_general; -} - -static inline int nf_ct_get_ctinfo(const struct sk_buff *skb, - enum ip_conntrack_info *ctinfo) -{ - struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo); - return (ct != NULL); -} - -static inline int nf_ct_l3proto_try_module_get(unsigned short l3proto) -{ - need_conntrack(); - return l3proto == PF_INET ? 0 : -1; -} - -static inline void nf_ct_l3proto_module_put(unsigned short l3proto) -{ -} - -#else /* CONFIG_IP_NF_CONNTRACK */ - -#include -#include - -#ifdef CONFIG_NF_CONNTRACK_MARK - -static inline u_int32_t *nf_ct_get_mark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct nf_conn *ct = nf_ct_get(skb, ctinfo); - - if (ct) - return &ct->mark; - else - return NULL; -} -#endif /* CONFIG_NF_CONNTRACK_MARK */ - -#ifdef CONFIG_NF_CONNTRACK_SECMARK -static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct nf_conn *ct = nf_ct_get(skb, ctinfo); - - if (ct) - return &ct->secmark; - else - return NULL; -} -#endif /* CONFIG_NF_CONNTRACK_MARK */ - -#ifdef CONFIG_NF_CT_ACCT -static inline struct ip_conntrack_counter * -nf_ct_get_counters(const struct sk_buff *skb) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - - if (ct) - return ct->counters; - else - return NULL; -} -#endif /* CONFIG_NF_CT_ACCT */ - -static inline int nf_ct_is_untracked(const struct sk_buff *skb) -{ - return (skb->nfct == &nf_conntrack_untracked.ct_general); -} - -static inline void nf_ct_untrack(struct sk_buff *skb) -{ - skb->nfct = &nf_conntrack_untracked.ct_general; -} - -static inline int nf_ct_get_ctinfo(const struct sk_buff *skb, - enum ip_conntrack_info *ctinfo) -{ - struct nf_conn *ct = nf_ct_get(skb, ctinfo); - return (ct != NULL); -} - -#endif /* CONFIG_IP_NF_CONNTRACK */ - -#endif /* __KERNEL__ */ - -#endif /* _NF_CONNTRACK_COMPAT_H */ diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index 85634e1..9fb9066 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -27,6 +27,9 @@ extern unsigned int nf_conntrack_in(int extern int nf_conntrack_init(void); extern void nf_conntrack_cleanup(void); +extern int nf_conntrack_proto_init(void); +extern void nf_conntrack_proto_fini(void); + struct nf_conntrack_l3proto; extern struct nf_conntrack_l3proto *nf_ct_find_l3proto(u_int16_t pf); /* Like above, but you already have conntrack read lock. */ diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index b62a8a9..811c907 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -20,30 +20,8 @@ DECLARE_PER_CPU(struct nf_conntrack_ecac #define CONNTRACK_ECACHE(x) (__get_cpu_var(nf_conntrack_ecache).x) extern struct atomic_notifier_head nf_conntrack_chain; -extern struct atomic_notifier_head nf_conntrack_expect_chain; - -static inline int nf_conntrack_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&nf_conntrack_chain, nb); -} - -static inline int nf_conntrack_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb); -} - -static inline int -nf_conntrack_expect_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&nf_conntrack_expect_chain, nb); -} - -static inline int -nf_conntrack_expect_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&nf_conntrack_expect_chain, - nb); -} +extern int nf_conntrack_register_notifier(struct notifier_block *nb); +extern int nf_conntrack_unregister_notifier(struct notifier_block *nb); extern void nf_ct_deliver_cached_events(const struct nf_conn *ct); extern void __nf_ct_event_cache_init(struct nf_conn *ct); @@ -71,6 +49,10 @@ static inline void nf_conntrack_event(en atomic_notifier_call_chain(&nf_conntrack_chain, event, ct); } +extern struct atomic_notifier_head nf_conntrack_expect_chain; +extern int nf_conntrack_expect_register_notifier(struct notifier_block *nb); +extern int nf_conntrack_expect_unregister_notifier(struct notifier_block *nb); + static inline void nf_conntrack_expect_event(enum ip_conntrack_expect_events event, struct nf_conntrack_expect *exp) diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index eb575cb..f32f714 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -90,10 +90,7 @@ extern struct nf_conntrack_l3proto *nf_c /* Protocol registration. */ extern int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto); extern void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto); - -extern struct nf_conntrack_l3proto * -nf_ct_l3proto_find_get(u_int16_t l3proto); - +extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p); /* Existing built-in protocols */ diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 8415182..f46cb93 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -97,7 +97,6 @@ extern struct nf_conntrack_l4proto nf_co extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic; #define MAX_NF_CT_PROTO 256 -extern struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX]; extern struct nf_conntrack_l4proto * __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto); diff --git a/include/net/netfilter/nf_nat_rule.h b/include/net/netfilter/nf_nat_rule.h index f191c67..e765654 100644 --- a/include/net/netfilter/nf_nat_rule.h +++ b/include/net/netfilter/nf_nat_rule.h @@ -4,16 +4,6 @@ #include #include #include -/* Compatibility definitions for ipt_FOO modules */ -#define ip_nat_range nf_nat_range -#define ip_conntrack_tuple nf_conntrack_tuple -#define ip_conntrack_get nf_ct_get -#define ip_conntrack nf_conn -#define ip_nat_setup_info nf_nat_setup_info -#define ip_nat_multi_range_compat nf_nat_multi_range_compat -#define ip_ct_iterate_cleanup nf_ct_iterate_cleanup -#define IP_NF_ASSERT NF_CT_ASSERT - extern int nf_nat_rule_init(void) __init; extern void nf_nat_rule_cleanup(void); extern int nf_nat_rule_find(struct sk_buff **pskb, diff --git a/include/net/netlink.h b/include/net/netlink.h index bcaf67b..0bf325c 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -171,6 +171,7 @@ enum { NLA_MSECS, NLA_NESTED, NLA_NUL_STRING, + NLA_BINARY, __NLA_TYPE_MAX, }; @@ -188,12 +189,13 @@ #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1 * NLA_STRING Maximum length of string * NLA_NUL_STRING Maximum length of string (excluding NUL) * NLA_FLAG Unused + * NLA_BINARY Maximum length of attribute payload * All other Exact length of attribute payload * * Example: * static struct nla_policy my_policy[ATTR_MAX+1] __read_mostly = { * [ATTR_FOO] = { .type = NLA_U16 }, - * [ATTR_BAR] = { .type = NLA_STRING, len = BARSIZ }, + * [ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ }, * [ATTR_BAZ] = { .len = sizeof(struct mystruct) }, * }; */ @@ -214,9 +216,7 @@ struct nl_info { extern void netlink_run_queue(struct sock *sk, unsigned int *qlen, int (*cb)(struct sk_buff *, - struct nlmsghdr *, int *)); -extern void netlink_queue_skip(struct nlmsghdr *nlh, - struct sk_buff *skb); + struct nlmsghdr *)); extern int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 pid, unsigned int group, int report, gfp_t flags); @@ -525,7 +525,7 @@ static inline struct sk_buff *nlmsg_new( */ static inline int nlmsg_end(struct sk_buff *skb, struct nlmsghdr *nlh) { - nlh->nlmsg_len = skb->tail - (unsigned char *) nlh; + nlh->nlmsg_len = skb_tail_pointer(skb) - (unsigned char *)nlh; return skb->len; } @@ -538,7 +538,7 @@ static inline int nlmsg_end(struct sk_bu */ static inline void *nlmsg_get_pos(struct sk_buff *skb) { - return skb->tail; + return skb_tail_pointer(skb); } /** @@ -548,7 +548,7 @@ static inline void *nlmsg_get_pos(struct * * Trims the message to the provided mark. Returns -1. */ -static inline int nlmsg_trim(struct sk_buff *skb, void *mark) +static inline int nlmsg_trim(struct sk_buff *skb, const void *mark) { if (mark) skb_trim(skb, (unsigned char *) mark - skb->data); @@ -940,7 +940,7 @@ static inline unsigned long nla_get_msec */ static inline struct nlattr *nla_nest_start(struct sk_buff *skb, int attrtype) { - struct nlattr *start = (struct nlattr *) skb->tail; + struct nlattr *start = (struct nlattr *)skb_tail_pointer(skb); if (nla_put(skb, attrtype, 0, NULL) < 0) return NULL; @@ -960,7 +960,7 @@ static inline struct nlattr *nla_nest_st */ static inline int nla_nest_end(struct sk_buff *skb, struct nlattr *start) { - start->nla_len = skb->tail - (unsigned char *) start; + start->nla_len = skb_tail_pointer(skb) - (unsigned char *)start; return skb->len; } diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 02647fe..4129df7 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -326,18 +326,18 @@ static inline unsigned char * tcf_get_ba case TCF_LAYER_LINK: return skb->data; case TCF_LAYER_NETWORK: - return skb->nh.raw; + return skb_network_header(skb); case TCF_LAYER_TRANSPORT: - return skb->h.raw; + return skb_transport_header(skb); } return NULL; } -static inline int tcf_valid_offset(struct sk_buff *skb, unsigned char *ptr, - int len) +static inline int tcf_valid_offset(const struct sk_buff *skb, + const unsigned char *ptr, const int len) { - return unlikely((ptr + len) < skb->tail && ptr > skb->head); + return unlikely((ptr + len) < skb_tail_pointer(skb) && ptr > skb->head); } #ifdef CONFIG_NET_CLS_IND diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index f6afee7..5754d53 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -2,6 +2,7 @@ #ifndef __NET_PKT_SCHED_H #define __NET_PKT_SCHED_H #include +#include #include struct qdisc_walker @@ -12,8 +13,6 @@ struct qdisc_walker int (*fn)(struct Qdisc *, unsigned long cl, struct qdisc_walker *); }; -extern rwlock_t qdisc_tree_lock; - #define QDISC_ALIGNTO 32 #define QDISC_ALIGN(len) (((len) + QDISC_ALIGNTO-1) & ~(QDISC_ALIGNTO-1)) @@ -37,175 +36,38 @@ static inline void *qdisc_priv(struct Qd The things are not so bad, because we may use artifical clock evaluated by integration of network data flow in the most critical places. - - Note: we do not use fastgettimeofday. - The reason is that, when it is not the same thing as - gettimeofday, it returns invalid timestamp, which is - not updated, when net_bh is active. - */ - -/* General note about internal clock. - - Any clock source returns time intervals, measured in units - close to 1usec. With source CONFIG_NET_SCH_CLK_GETTIMEOFDAY it is precisely - microseconds, otherwise something close but different chosen to minimize - arithmetic cost. Ratio usec/internal untis in form nominator/denominator - may be read from /proc/net/psched. */ - -#ifdef CONFIG_NET_SCH_CLK_GETTIMEOFDAY - -typedef struct timeval psched_time_t; -typedef long psched_tdiff_t; - -#define PSCHED_GET_TIME(stamp) do_gettimeofday(&(stamp)) -#define PSCHED_US2JIFFIE(usecs) usecs_to_jiffies(usecs) -#define PSCHED_JIFFIE2US(delay) jiffies_to_usecs(delay) - -#else /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */ - typedef u64 psched_time_t; typedef long psched_tdiff_t; -#ifdef CONFIG_NET_SCH_CLK_JIFFIES - -#if HZ < 96 -#define PSCHED_JSCALE 14 -#elif HZ >= 96 && HZ < 192 -#define PSCHED_JSCALE 13 -#elif HZ >= 192 && HZ < 384 -#define PSCHED_JSCALE 12 -#elif HZ >= 384 && HZ < 768 -#define PSCHED_JSCALE 11 -#elif HZ >= 768 -#define PSCHED_JSCALE 10 -#endif +/* Avoid doing 64 bit divide by 1000 */ +#define PSCHED_US2NS(x) ((s64)(x) << 10) +#define PSCHED_NS2US(x) ((x) >> 10) -#define PSCHED_GET_TIME(stamp) ((stamp) = (get_jiffies_64()<>PSCHED_JSCALE) -#define PSCHED_JIFFIE2US(delay) ((delay)< - -extern psched_tdiff_t psched_clock_per_hz; -extern int psched_clock_scale; -extern psched_time_t psched_time_base; -extern cycles_t psched_time_mark; - -#define PSCHED_GET_TIME(stamp) \ -do { \ - cycles_t cur = get_cycles(); \ - if (sizeof(cycles_t) == sizeof(u32)) { \ - if (cur <= psched_time_mark) \ - psched_time_base += 0x100000000ULL; \ - psched_time_mark = cur; \ - (stamp) = (psched_time_base + cur)>>psched_clock_scale; \ - } else { \ - (stamp) = cur>>psched_clock_scale; \ - } \ -} while (0) -#define PSCHED_US2JIFFIE(delay) (((delay)+psched_clock_per_hz-1)/psched_clock_per_hz) -#define PSCHED_JIFFIE2US(delay) ((delay)*psched_clock_per_hz) - -#endif /* CONFIG_NET_SCH_CLK_CPU */ - -#endif /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */ - -#ifdef CONFIG_NET_SCH_CLK_GETTIMEOFDAY -#define PSCHED_TDIFF(tv1, tv2) \ -({ \ - int __delta_sec = (tv1).tv_sec - (tv2).tv_sec; \ - int __delta = (tv1).tv_usec - (tv2).tv_usec; \ - if (__delta_sec) { \ - switch (__delta_sec) { \ - default: \ - __delta = 0; \ - case 2: \ - __delta += USEC_PER_SEC; \ - case 1: \ - __delta += USEC_PER_SEC; \ - } \ - } \ - __delta; \ -}) - -static inline int -psched_tod_diff(int delta_sec, int bound) +#define PSCHED_TICKS_PER_SEC PSCHED_NS2US(NSEC_PER_SEC) +#define PSCHED_PASTPERFECT 0 + +static inline psched_time_t psched_get_time(void) { - int delta; - - if (bound <= USEC_PER_SEC || delta_sec > (0x7FFFFFFF/USEC_PER_SEC)-1) - return bound; - delta = delta_sec * USEC_PER_SEC; - if (delta > bound || delta < 0) - delta = bound; - return delta; + return PSCHED_NS2US(ktime_to_ns(ktime_get())); } -#define PSCHED_TDIFF_SAFE(tv1, tv2, bound) \ -({ \ - int __delta_sec = (tv1).tv_sec - (tv2).tv_sec; \ - int __delta = (tv1).tv_usec - (tv2).tv_usec; \ - switch (__delta_sec) { \ - default: \ - __delta = psched_tod_diff(__delta_sec, bound); break; \ - case 2: \ - __delta += USEC_PER_SEC; \ - case 1: \ - __delta += USEC_PER_SEC; \ - case 0: \ - if (__delta > bound || __delta < 0) \ - __delta = bound; \ - } \ - __delta; \ -}) - -#define PSCHED_TLESS(tv1, tv2) (((tv1).tv_usec < (tv2).tv_usec && \ - (tv1).tv_sec <= (tv2).tv_sec) || \ - (tv1).tv_sec < (tv2).tv_sec) - -#define PSCHED_TADD2(tv, delta, tv_res) \ -({ \ - int __delta = (tv).tv_usec + (delta); \ - (tv_res).tv_sec = (tv).tv_sec; \ - while (__delta >= USEC_PER_SEC) { (tv_res).tv_sec++; __delta -= USEC_PER_SEC; } \ - (tv_res).tv_usec = __delta; \ -}) - -#define PSCHED_TADD(tv, delta) \ -({ \ - (tv).tv_usec += (delta); \ - while ((tv).tv_usec >= USEC_PER_SEC) { (tv).tv_sec++; \ - (tv).tv_usec -= USEC_PER_SEC; } \ -}) - -/* Set/check that time is in the "past perfect"; - it depends on concrete representation of system time - */ - -#define PSCHED_SET_PASTPERFECT(t) ((t).tv_sec = 0) -#define PSCHED_IS_PASTPERFECT(t) ((t).tv_sec == 0) - -#define PSCHED_AUDIT_TDIFF(t) ({ if ((t) > 2000000) (t) = 2000000; }) - -#else /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */ - -#define PSCHED_TDIFF(tv1, tv2) (long)((tv1) - (tv2)) -#define PSCHED_TDIFF_SAFE(tv1, tv2, bound) \ - min_t(long long, (tv1) - (tv2), bound) - +static inline psched_tdiff_t +psched_tdiff_bounded(psched_time_t tv1, psched_time_t tv2, psched_time_t bound) +{ + return min(tv1 - tv2, bound); +} -#define PSCHED_TLESS(tv1, tv2) ((tv1) < (tv2)) -#define PSCHED_TADD2(tv, delta, tv_res) ((tv_res) = (tv) + (delta)) -#define PSCHED_TADD(tv, delta) ((tv) += (delta)) -#define PSCHED_SET_PASTPERFECT(t) ((t) = 0) -#define PSCHED_IS_PASTPERFECT(t) ((t) == 0) -#define PSCHED_AUDIT_TDIFF(t) +struct qdisc_watchdog { + struct hrtimer timer; + struct Qdisc *qdisc; +}; -#endif /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */ +extern void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc); +extern void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, + psched_time_t expires); +extern void qdisc_watchdog_cancel(struct qdisc_watchdog *wd); extern struct Qdisc_ops pfifo_qdisc_ops; extern struct Qdisc_ops bfifo_qdisc_ops; diff --git a/include/net/red.h b/include/net/red.h index a4eb379..3cf31d4 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -151,17 +151,17 @@ static inline void red_set_parms(struct static inline int red_is_idling(struct red_parms *p) { - return !PSCHED_IS_PASTPERFECT(p->qidlestart); + return p->qidlestart != PSCHED_PASTPERFECT; } static inline void red_start_of_idle_period(struct red_parms *p) { - PSCHED_GET_TIME(p->qidlestart); + p->qidlestart = psched_get_time(); } static inline void red_end_of_idle_period(struct red_parms *p) { - PSCHED_SET_PASTPERFECT(p->qidlestart); + p->qidlestart = PSCHED_PASTPERFECT; } static inline void red_restart(struct red_parms *p) @@ -177,8 +177,8 @@ static inline unsigned long red_calc_qav long us_idle; int shift; - PSCHED_GET_TIME(now); - us_idle = PSCHED_TDIFF_SAFE(now, p->qidlestart, p->Scell_max); + now = psched_get_time(); + us_idle = psched_tdiff_bounded(now, p->qidlestart, p->Scell_max); /* * The problem: ideally, average length queue recalcultion should diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h new file mode 100644 index 0000000..3b3d474 --- /dev/null +++ b/include/net/rtnetlink.h @@ -0,0 +1,25 @@ +#ifndef __NET_RTNETLINK_H +#define __NET_RTNETLINK_H + +#include +#include + +typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, void *); +typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *); + +extern int __rtnl_register(int protocol, int msgtype, + rtnl_doit_func, rtnl_dumpit_func); +extern void rtnl_register(int protocol, int msgtype, + rtnl_doit_func, rtnl_dumpit_func); +extern int rtnl_unregister(int protocol, int msgtype); +extern void rtnl_unregister_all(int protocol); + +static inline int rtnl_msg_family(struct nlmsghdr *nlh) +{ + if (nlmsg_len(nlh) >= sizeof(struct rtgenmsg)) + return ((struct rtgenmsg *) nlmsg_data(nlh))->rtgen_family; + else + return AF_UNSPEC; +} + +#endif diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 8208639..1b8e351 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -5,10 +5,10 @@ #include #include #include #include -#include #include #include #include +#include struct Qdisc_ops; struct qdisc_walker; @@ -177,14 +177,8 @@ extern void qdisc_tree_decrease_qlen(str extern struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops); extern struct Qdisc *qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops, u32 parentid); - -static inline void -tcf_destroy(struct tcf_proto *tp) -{ - tp->ops->destroy(tp); - module_put(tp->ops->owner); - kfree(tp); -} +extern void tcf_destroy(struct tcf_proto *tp); +extern void tcf_destroy_chain(struct tcf_proto *fl); static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff_head *list) diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 5ddb855..bb37724 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -283,7 +283,7 @@ #define SCTP_RTO_ALPHA 3 /* 1 #define SCTP_RTO_BETA 2 /* 1/4 when converted to right shifts. */ /* Maximum number of new data packets that can be sent in a burst. */ -#define SCTP_MAX_BURST 4 +#define SCTP_DEFAULT_MAX_BURST 4 #define SCTP_CLOCK_GRANULARITY 1 /* 1 jiffy */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index f431acf..7b4fff9 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -276,6 +276,7 @@ struct sctp_sock { __u32 default_context; __u32 default_timetolive; __u32 default_rcv_context; + int max_burst; /* Heartbeat interval: The endpoint sends out a Heartbeat chunk to * the destination address every heartbeat interval. This value @@ -304,10 +305,12 @@ struct sctp_sock { __u32 autoclose; __u8 nodelay; __u8 disable_fragments; - __u8 pd_mode; __u8 v4mapped; + __u8 frag_interleave; __u32 adaptation_ind; + __u32 pd_point; + atomic_t pd_mode; /* Receive to here while partial delivery is in effect. */ struct sk_buff_head pd_lobby; }; diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index 2923e3d..de88ed5 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -89,6 +89,7 @@ struct sctp_ulpevent *sctp_ulpevent_make __u16 error, __u16 outbound, __u16 inbound, + struct sctp_chunk *chunk, gfp_t gfp); struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change( diff --git a/include/net/sctp/ulpqueue.h b/include/net/sctp/ulpqueue.h index ab26ab3..39ea3f4 100644 --- a/include/net/sctp/ulpqueue.h +++ b/include/net/sctp/ulpqueue.h @@ -78,7 +78,7 @@ void sctp_ulpq_partial_delivery(struct s void sctp_ulpq_abort_pd(struct sctp_ulpq *, gfp_t); /* Clear the partial data delivery condition on this socket. */ -int sctp_clear_pd(struct sock *sk); +int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc); /* Skip over an SSN. */ void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn); diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index 67a30eb..6d2b577 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h @@ -97,6 +97,12 @@ #define SCTP_GET_PEER_ADDR_INFO SCTP_GET #define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME SCTP_CONTEXT, /* Receive Context */ #define SCTP_CONTEXT SCTP_CONTEXT + SCTP_FRAGMENT_INTERLEAVE, +#define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE + SCTP_PARTIAL_DELIVERY_POINT, /* Set/Get partial delivery point */ +#define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT + SCTP_MAX_BURST, /* Set/Get max burst */ +#define SCTP_MAX_BURST SCTP_MAX_BURST /* Internal Socket Options. Some of the sctp library functions are * implemented using these socket options. @@ -213,6 +219,7 @@ struct sctp_assoc_change { __u16 sac_outbound_streams; __u16 sac_inbound_streams; sctp_assoc_t sac_assoc_id; + __u8 sac_info[0]; }; /* @@ -261,6 +268,7 @@ enum sctp_spc_state { SCTP_ADDR_REMOVED, SCTP_ADDR_ADDED, SCTP_ADDR_MADE_PRIM, + SCTP_ADDR_CONFIRMED, }; @@ -508,16 +516,17 @@ struct sctp_setadaptation { * address's parameters: */ enum sctp_spp_flags { - SPP_HB_ENABLE = 1, /*Enable heartbeats*/ - SPP_HB_DISABLE = 2, /*Disable heartbeats*/ + SPP_HB_ENABLE = 1<<0, /*Enable heartbeats*/ + SPP_HB_DISABLE = 1<<1, /*Disable heartbeats*/ SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE, - SPP_HB_DEMAND = 4, /*Send heartbeat immediately*/ - SPP_PMTUD_ENABLE = 8, /*Enable PMTU discovery*/ - SPP_PMTUD_DISABLE = 16, /*Disable PMTU discovery*/ + SPP_HB_DEMAND = 1<<2, /*Send heartbeat immediately*/ + SPP_PMTUD_ENABLE = 1<<3, /*Enable PMTU discovery*/ + SPP_PMTUD_DISABLE = 1<<4, /*Disable PMTU discovery*/ SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE, - SPP_SACKDELAY_ENABLE = 32, /*Enable SACK*/ - SPP_SACKDELAY_DISABLE = 64, /*Disable SACK*/ + SPP_SACKDELAY_ENABLE = 1<<5, /*Enable SACK*/ + SPP_SACKDELAY_DISABLE = 1<<6, /*Disable SACK*/ SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE, + SPP_HB_TIME_IS_ZERO = 1<<7, /* Set HB delay to 0 */ }; struct sctp_paddrparams { @@ -530,7 +539,7 @@ struct sctp_paddrparams { __u32 spp_flags; } __attribute__((packed, aligned(4))); -/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) +/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) * * This options will get or set the delayed ack timer. The time is set * in milliseconds. If the assoc_id is 0, then this sets or gets the diff --git a/include/net/sock.h b/include/net/sock.h index 2c7d60c..25c37e3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -202,6 +202,15 @@ #define sk_prot __sk_common.skc_prot unsigned short sk_type; int sk_rcvbuf; socket_lock_t sk_lock; + /* + * The backlog queue is special, it is always used with + * the per-socket spinlock held and requires low latency + * access. Therefore we special case it's implementation. + */ + struct { + struct sk_buff *head; + struct sk_buff *tail; + } sk_backlog; wait_queue_head_t *sk_sleep; struct dst_entry *sk_dst_cache; struct xfrm_policy *sk_policy[2]; @@ -221,15 +230,6 @@ #define sk_prot __sk_common.skc_prot int sk_rcvlowat; unsigned long sk_flags; unsigned long sk_lingertime; - /* - * The backlog queue is special, it is always used with - * the per-socket spinlock held and requires low latency - * access. Therefore we special case it's implementation. - */ - struct { - struct sk_buff *head; - struct sk_buff *tail; - } sk_backlog; struct sk_buff_head sk_error_queue; struct proto *sk_prot_creator; rwlock_t sk_callback_lock; @@ -244,7 +244,7 @@ #define sk_prot __sk_common.skc_prot struct sk_filter *sk_filter; void *sk_protinfo; struct timer_list sk_timer; - struct timeval sk_stamp; + ktime_t sk_stamp; struct socket *sk_socket; void *sk_user_data; struct page *sk_sndmsg_page; @@ -390,6 +390,7 @@ enum sock_flags { SOCK_USE_WRITE_QUEUE, /* whether to call sk->sk_write_space in sock_wfree */ SOCK_DBG, /* %SO_DEBUG setting */ SOCK_RCVTSTAMP, /* %SO_TIMESTAMP setting */ + SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */ SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ }; @@ -710,15 +711,6 @@ static inline void sk_stream_mem_reclaim __sk_stream_mem_reclaim(sk); } -static inline void sk_stream_writequeue_purge(struct sock *sk) -{ - struct sk_buff *skb; - - while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) - sk_stream_free_skb(sk, skb); - sk_stream_mem_reclaim(sk); -} - static inline int sk_stream_rmem_schedule(struct sock *sk, struct sk_buff *skb) { return (int)skb->truesize <= sk->sk_forward_alloc || @@ -1083,19 +1075,7 @@ static inline int sk_can_gso(const struc return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type); } -static inline void sk_setup_caps(struct sock *sk, struct dst_entry *dst) -{ - __sk_dst_set(sk, dst); - sk->sk_route_caps = dst->dev->features; - if (sk->sk_route_caps & NETIF_F_GSO) - sk->sk_route_caps |= NETIF_F_GSO_MASK; - if (sk_can_gso(sk)) { - if (dst->header_len) - sk->sk_route_caps &= ~NETIF_F_GSO_MASK; - else - sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; - } -} +extern void sk_setup_caps(struct sock *sk, struct dst_entry *dst); static inline void sk_charge_skb(struct sock *sk, struct sk_buff *skb) { @@ -1256,18 +1236,6 @@ static inline struct page *sk_stream_all return page; } -#define sk_stream_for_retrans_queue(skb, sk) \ - for (skb = (sk)->sk_write_queue.next; \ - (skb != (sk)->sk_send_head) && \ - (skb != (struct sk_buff *)&(sk)->sk_write_queue); \ - skb = skb->next) - -/*from STCP for fast SACK Process*/ -#define sk_stream_for_retrans_queue_from(skb, sk) \ - for (; (skb != (sk)->sk_send_head) && \ - (skb != (struct sk_buff *)&(sk)->sk_write_queue); \ - skb = skb->next) - /* * Default write policy as shown to user space via poll/select/SIGIO */ @@ -1304,22 +1272,18 @@ static inline int sock_intr_errno(long t return timeo == MAX_SCHEDULE_TIMEOUT ? -ERESTARTSYS : -EINTR; } +extern void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb); + static __inline__ void sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { - struct timeval stamp; + ktime_t kt = skb->tstamp; - skb_get_timestamp(skb, &stamp); - if (sock_flag(sk, SOCK_RCVTSTAMP)) { - /* Race occurred between timestamp enabling and packet - receiving. Fill in the current time for now. */ - if (stamp.tv_sec == 0) - do_gettimeofday(&stamp); - skb_set_timestamp(skb, &stamp); - put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP, sizeof(struct timeval), - &stamp); - } else - sk->sk_stamp = stamp; + if (sock_flag(sk, SOCK_RCVTSTAMP)) + __sock_recv_timestamp(msg, sk, skb); + else + sk->sk_stamp = kt; } /** @@ -1350,18 +1314,17 @@ #endif extern void sock_enable_timestamp(struct sock *sk); extern int sock_get_timestamp(struct sock *, struct timeval __user *); +extern int sock_get_timestampns(struct sock *, struct timespec __user *); /* * Enable debug/info messages */ +extern int net_msg_warn; +#define NETDEBUG(fmt, args...) \ + do { if (net_msg_warn) printk(fmt,##args); } while (0) -#ifdef CONFIG_NETDEBUG -#define NETDEBUG(fmt, args...) printk(fmt,##args) -#define LIMIT_NETDEBUG(fmt, args...) do { if (net_ratelimit()) printk(fmt,##args); } while(0) -#else -#define NETDEBUG(fmt, args...) do { } while (0) -#define LIMIT_NETDEBUG(fmt, args...) do { } while(0) -#endif +#define LIMIT_NETDEBUG(fmt, args...) \ + do { if (net_msg_warn && net_ratelimit()) printk(fmt,##args); } while(0) /* * Macros for sleeping on a socket. Use them like this: diff --git a/include/net/tcp.h b/include/net/tcp.h index 5c472f2..ef8f9d4 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -220,6 +220,7 @@ extern int sysctl_tcp_app_win; extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_tw_reuse; extern int sysctl_tcp_frto; +extern int sysctl_tcp_frto_response; extern int sysctl_tcp_low_latency; extern int sysctl_tcp_dma_copybreak; extern int sysctl_tcp_nometrics_save; @@ -230,6 +231,7 @@ extern int sysctl_tcp_mtu_probing; extern int sysctl_tcp_base_mss; extern int sysctl_tcp_workaround_signed_windows; extern int sysctl_tcp_slow_start_after_idle; +extern int sysctl_tcp_max_ssthresh; extern atomic_t tcp_memory_allocated; extern atomic_t tcp_sockets_allocated; @@ -341,6 +343,7 @@ extern struct sock * tcp_check_req(stru extern int tcp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb); +extern int tcp_use_frto(struct sock *sk); extern void tcp_enter_frto(struct sock *sk); extern void tcp_enter_loss(struct sock *sk, int how); extern void tcp_clear_retrans(struct tcp_sock *tp); @@ -417,9 +420,9 @@ extern __u32 cookie_v4_init_sequence(str /* tcp_output.c */ -extern void __tcp_push_pending_frames(struct sock *sk, struct tcp_sock *tp, - unsigned int cur_mss, int nonagle); -extern int tcp_may_send_now(struct sock *sk, struct tcp_sock *tp); +extern void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss, + int nonagle); +extern int tcp_may_send_now(struct sock *sk); extern int tcp_retransmit_skb(struct sock *, struct sk_buff *); extern void tcp_xmit_retransmit_queue(struct sock *); extern void tcp_simple_retransmit(struct sock *); @@ -476,8 +479,10 @@ static inline void tcp_fast_path_on(stru __tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale); } -static inline void tcp_fast_path_check(struct sock *sk, struct tcp_sock *tp) +static inline void tcp_fast_path_check(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (skb_queue_empty(&tp->out_of_order_queue) && tp->rcv_wnd && atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && @@ -588,10 +593,10 @@ static inline void tcp_dec_pcount_approx } } -static inline void tcp_packets_out_inc(struct sock *sk, - struct tcp_sock *tp, +static inline void tcp_packets_out_inc(struct sock *sk, const struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); int orig = tp->packets_out; tp->packets_out += tcp_skb_pcount(skb); @@ -624,9 +629,12 @@ #define TCP_CA_NAME_MAX 16 #define TCP_CA_MAX 128 #define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX) +#define TCP_CONG_NON_RESTRICTED 0x1 +#define TCP_CONG_RTT_STAMP 0x2 + struct tcp_congestion_ops { struct list_head list; - int non_restricted; + unsigned long flags; /* initialize private data (optional) */ void (*init)(struct sock *sk); @@ -640,8 +648,6 @@ struct tcp_congestion_ops { /* do new cwnd calculation (required) */ void (*cong_avoid)(struct sock *sk, u32 ack, u32 rtt, u32 in_flight, int good_ack); - /* round trip time sample per acked packet (optional) */ - void (*rtt_sample)(struct sock *sk, u32 usrtt); /* call before changing ca_state (optional) */ void (*set_state)(struct sock *sk, u8 new_state); /* call when cwnd event occurs (optional) */ @@ -649,7 +655,7 @@ struct tcp_congestion_ops { /* new value of cwnd after loss (optional) */ u32 (*undo_cwnd)(struct sock *sk); /* hook for packet ack accounting (optional) */ - void (*pkts_acked)(struct sock *sk, u32 num_acked); + void (*pkts_acked)(struct sock *sk, u32 num_acked, ktime_t last); /* get info for inet_diag (optional) */ void (*get_info)(struct sock *sk, u32 ext, struct sk_buff *skb); @@ -730,13 +736,11 @@ static inline __u32 tcp_current_ssthresh static inline void tcp_sync_left_out(struct tcp_sock *tp) { - if (tp->rx_opt.sack_ok && - (tp->sacked_out >= tp->packets_out - tp->lost_out)) - tp->sacked_out = tp->packets_out - tp->lost_out; + BUG_ON(tp->sacked_out + tp->lost_out > tp->packets_out); tp->left_out = tp->sacked_out + tp->lost_out; } -extern void tcp_enter_cwr(struct sock *sk); +extern void tcp_enter_cwr(struct sock *sk, const int set_ssthresh); extern __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst); /* Slow start with delack produces 3 packets of burst, so that @@ -775,18 +779,21 @@ static inline void tcp_minshall_update(s tp->snd_sml = TCP_SKB_CB(skb)->end_seq; } -static inline void tcp_check_probe_timer(struct sock *sk, struct tcp_sock *tp) +static inline void tcp_check_probe_timer(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); + if (!tp->packets_out && !icsk->icsk_pending) inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0, icsk->icsk_rto, TCP_RTO_MAX); } -static inline void tcp_push_pending_frames(struct sock *sk, - struct tcp_sock *tp) +static inline void tcp_push_pending_frames(struct sock *sk) { - __tcp_push_pending_frames(sk, tp, tcp_current_mss(sk, 1), tp->nonagle); + struct tcp_sock *tp = tcp_sk(sk); + + __tcp_push_pending_frames(sk, tcp_current_mss(sk, 1), tp->nonagle); } static inline void tcp_init_wl(struct tcp_sock *tp, u32 ack, u32 seq) @@ -815,7 +822,7 @@ static inline __sum16 __tcp_checksum_com static inline int tcp_checksum_complete(struct sk_buff *skb) { - return skb->ip_summed != CHECKSUM_UNNECESSARY && + return !skb_csum_unnecessary(skb) && __tcp_checksum_complete(skb); } @@ -918,21 +925,7 @@ #ifdef STATE_TRACE #endif } -static inline void tcp_done(struct sock *sk) -{ - if(sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) - TCP_INC_STATS_BH(TCP_MIB_ATTEMPTFAILS); - - tcp_set_state(sk, TCP_CLOSE); - tcp_clear_xmit_timers(sk); - - sk->sk_shutdown = SHUTDOWN_MASK; - - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_state_change(sk); - else - inet_csk_destroy_sock(sk); -} +extern void tcp_done(struct sock *sk); static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) { @@ -981,7 +974,7 @@ static inline void tcp_openreq_init(stru ireq->wscale_ok = rx_opt->wscale_ok; ireq->acked = 0; ireq->ecn_ok = 0; - ireq->rmt_port = skb->h.th->source; + ireq->rmt_port = tcp_hdr(skb)->source; } extern void tcp_enter_memory_pressure(void); @@ -1011,7 +1004,7 @@ static inline int tcp_paws_check(const s { if ((s32)(rx_opt->rcv_tsval - rx_opt->ts_recent) >= 0) return 0; - if (xtime.tv_sec >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS) + if (get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS) return 0; /* RST segments are not recommended to carry timestamp, @@ -1026,26 +1019,13 @@ static inline int tcp_paws_check(const s However, we can relax time bounds for RST segments to MSL. */ - if (rst && xtime.tv_sec >= rx_opt->ts_recent_stamp + TCP_PAWS_MSL) + if (rst && get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_MSL) return 0; return 1; } #define TCP_CHECK_TIMER(sk) do { } while (0) -static inline int tcp_use_frto(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - - /* F-RTO must be activated in sysctl and there must be some - * unsent new data, and the advertised window should allow - * sending it. - */ - return (sysctl_tcp_frto && sk->sk_send_head && - !after(TCP_SKB_CB(sk->sk_send_head)->end_seq, - tp->snd_una + tp->snd_wnd)); -} - static inline void tcp_mib_init(void) { /* See RFC 2012 */ @@ -1172,6 +1152,125 @@ static inline void tcp_put_md5sig_pool( put_cpu(); } +/* write queue abstraction */ +static inline void tcp_write_queue_purge(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) + sk_stream_free_skb(sk, skb); + sk_stream_mem_reclaim(sk); +} + +static inline struct sk_buff *tcp_write_queue_head(struct sock *sk) +{ + struct sk_buff *skb = sk->sk_write_queue.next; + if (skb == (struct sk_buff *) &sk->sk_write_queue) + return NULL; + return skb; +} + +static inline struct sk_buff *tcp_write_queue_tail(struct sock *sk) +{ + struct sk_buff *skb = sk->sk_write_queue.prev; + if (skb == (struct sk_buff *) &sk->sk_write_queue) + return NULL; + return skb; +} + +static inline struct sk_buff *tcp_write_queue_next(struct sock *sk, struct sk_buff *skb) +{ + return skb->next; +} + +#define tcp_for_write_queue(skb, sk) \ + for (skb = (sk)->sk_write_queue.next; \ + (skb != (struct sk_buff *)&(sk)->sk_write_queue); \ + skb = skb->next) + +#define tcp_for_write_queue_from(skb, sk) \ + for (; (skb != (struct sk_buff *)&(sk)->sk_write_queue);\ + skb = skb->next) + +static inline struct sk_buff *tcp_send_head(struct sock *sk) +{ + return sk->sk_send_head; +} + +static inline void tcp_advance_send_head(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_sock *tp = tcp_sk(sk); + + sk->sk_send_head = skb->next; + if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue) + sk->sk_send_head = NULL; + /* Don't override Nagle indefinately with F-RTO */ + if (tp->frto_counter == 2) + tp->frto_counter = 3; +} + +static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unlinked) +{ + if (sk->sk_send_head == skb_unlinked) + sk->sk_send_head = NULL; +} + +static inline void tcp_init_send_head(struct sock *sk) +{ + sk->sk_send_head = NULL; +} + +static inline void __tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb) +{ + __skb_queue_tail(&sk->sk_write_queue, skb); +} + +static inline void tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb) +{ + __tcp_add_write_queue_tail(sk, skb); + + /* Queue it, remembering where we must start sending. */ + if (sk->sk_send_head == NULL) + sk->sk_send_head = skb; +} + +static inline void __tcp_add_write_queue_head(struct sock *sk, struct sk_buff *skb) +{ + __skb_queue_head(&sk->sk_write_queue, skb); +} + +/* Insert buff after skb on the write queue of sk. */ +static inline void tcp_insert_write_queue_after(struct sk_buff *skb, + struct sk_buff *buff, + struct sock *sk) +{ + __skb_append(skb, buff, &sk->sk_write_queue); +} + +/* Insert skb between prev and next on the write queue of sk. */ +static inline void tcp_insert_write_queue_before(struct sk_buff *new, + struct sk_buff *skb, + struct sock *sk) +{ + __skb_insert(new, skb->prev, skb, &sk->sk_write_queue); +} + +static inline void tcp_unlink_write_queue(struct sk_buff *skb, struct sock *sk) +{ + __skb_unlink(skb, &sk->sk_write_queue); +} + +static inline int tcp_skb_is_last(const struct sock *sk, + const struct sk_buff *skb) +{ + return skb->next == (struct sk_buff *)&sk->sk_write_queue; +} + +static inline int tcp_write_queue_empty(struct sock *sk) +{ + return skb_queue_empty(&sk->sk_write_queue); +} + /* /proc */ enum tcp_seq_states { TCP_SEQ_STATE_LISTENING, diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h index 4629d77..89eb3e0 100644 --- a/include/net/tcp_ecn.h +++ b/include/net/tcp_ecn.h @@ -27,9 +27,10 @@ static inline void TCP_ECN_send_synack(s TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_ECE; } -static inline void TCP_ECN_send_syn(struct sock *sk, struct tcp_sock *tp, - struct sk_buff *skb) +static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); + tp->ecn_flags = 0; if (sysctl_tcp_ecn) { TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_ECE|TCPCB_FLAG_CWR; @@ -44,9 +45,11 @@ TCP_ECN_make_synack(struct request_sock th->ece = 1; } -static inline void TCP_ECN_send(struct sock *sk, struct tcp_sock *tp, - struct sk_buff *skb, int tcp_header_len) +static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb, + int tcp_header_len) { + struct tcp_sock *tp = tcp_sk(sk); + if (tp->ecn_flags & TCP_ECN_OK) { /* Not-retransmitted data segment: set ECT and inject CWR. */ if (skb->len != tcp_header_len && @@ -54,7 +57,7 @@ static inline void TCP_ECN_send(struct s INET_ECN_xmit(sk); if (tp->ecn_flags&TCP_ECN_QUEUE_CWR) { tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR; - skb->h.th->cwr = 1; + tcp_hdr(skb)->cwr = 1; skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; } } else { @@ -62,7 +65,7 @@ static inline void TCP_ECN_send(struct s INET_ECN_dontxmit(sk); } if (tp->ecn_flags & TCP_ECN_DEMAND_CWR) - skb->h.th->ece = 1; + tcp_hdr(skb)->ece = 1; } } @@ -70,7 +73,7 @@ static inline void TCP_ECN_send(struct s static inline void TCP_ECN_accept_cwr(struct tcp_sock *tp, struct sk_buff *skb) { - if (skb->h.th->cwr) + if (tcp_hdr(skb)->cwr) tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR; } diff --git a/include/net/udp.h b/include/net/udp.h index 1b921fa..98755eb 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -72,15 +72,12 @@ struct sk_buff; */ static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb) { - if (! UDP_SKB_CB(skb)->partial_cov) - return __skb_checksum_complete(skb); - return csum_fold(skb_checksum(skb, 0, UDP_SKB_CB(skb)->cscov, - skb->csum)); + return __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov); } static inline int udp_lib_checksum_complete(struct sk_buff *skb) { - return skb->ip_summed != CHECKSUM_UNNECESSARY && + return !skb_csum_unnecessary(skb) && __udp_lib_checksum_complete(skb); } @@ -92,8 +89,8 @@ static inline int udp_lib_checksum_compl */ static inline __wsum udp_csum_outgoing(struct sock *sk, struct sk_buff *skb) { - __wsum csum = csum_partial(skb->h.raw, sizeof(struct udphdr), 0); - + __wsum csum = csum_partial(skb_transport_header(skb), + sizeof(struct udphdr), 0); skb_queue_walk(&sk->sk_write_queue, skb) { csum = csum_add(csum, skb->csum); } diff --git a/include/net/udplite.h b/include/net/udplite.h index 67ac514..635b0ea 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -47,11 +47,10 @@ static inline int udplite_checksum_init( return 1; } - UDP_SKB_CB(skb)->partial_cov = 0; cscov = ntohs(uh->len); if (cscov == 0) /* Indicates that full coverage is required. */ - cscov = skb->len; + ; else if (cscov < 8 || cscov > skb->len) { /* * Coverage length violates RFC 3828: log and discard silently. @@ -60,42 +59,16 @@ static inline int udplite_checksum_init( cscov, skb->len); return 1; - } else if (cscov < skb->len) + } else if (cscov < skb->len) { UDP_SKB_CB(skb)->partial_cov = 1; - - UDP_SKB_CB(skb)->cscov = cscov; - - /* - * There is no known NIC manufacturer supporting UDP-Lite yet, - * hence ip_summed is always (re-)set to CHECKSUM_NONE. - */ - skb->ip_summed = CHECKSUM_NONE; + UDP_SKB_CB(skb)->cscov = cscov; + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->ip_summed = CHECKSUM_NONE; + } return 0; } -static __inline__ int udplite4_csum_init(struct sk_buff *skb, struct udphdr *uh) -{ - int rc = udplite_checksum_init(skb, uh); - - if (!rc) - skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, - skb->nh.iph->daddr, - skb->len, IPPROTO_UDPLITE, 0); - return rc; -} - -static __inline__ int udplite6_csum_init(struct sk_buff *skb, struct udphdr *uh) -{ - int rc = udplite_checksum_init(skb, uh); - - if (!rc) - skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, - skb->len, IPPROTO_UDPLITE, 0)); - return rc; -} - static inline int udplite_sender_cscov(struct udp_sock *up, struct udphdr *uh) { int cscov = up->len; @@ -128,14 +101,14 @@ static inline int udplite_sender_cscov(s static inline __wsum udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb) { - int off, len, cscov = udplite_sender_cscov(udp_sk(sk), skb->h.uh); + int cscov = udplite_sender_cscov(udp_sk(sk), udp_hdr(skb)); __wsum csum = 0; skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */ skb_queue_walk(&sk->sk_write_queue, skb) { - off = skb->h.raw - skb->data; - len = skb->len - off; + const int off = skb_transport_offset(skb); + const int len = skb->len - off; csum = skb_checksum(skb, off, (cscov > len)? len : cscov, csum); diff --git a/include/net/wext.h b/include/net/wext.h new file mode 100644 index 0000000..c02b8de --- /dev/null +++ b/include/net/wext.h @@ -0,0 +1,24 @@ +#ifndef __NET_WEXT_H +#define __NET_WEXT_H + +/* + * wireless extensions interface to the core code + */ + +#ifdef CONFIG_WIRELESS_EXT +extern int wext_proc_init(void); +extern int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd, + void __user *arg); +#else +static inline int wext_proc_init(void) +{ + return 0; +} +static inline int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd, + void __user *arg) +{ + return -EINVAL; +} +#endif + +#endif /* __NET_WEXT_H */ diff --git a/include/net/wireless.h b/include/net/wireless.h new file mode 100644 index 0000000..d30c4ba --- /dev/null +++ b/include/net/wireless.h @@ -0,0 +1,139 @@ +#ifndef __NET_WIRELESS_H +#define __NET_WIRELESS_H + +/* + * 802.11 device management + * + * Copyright 2007 Johannes Berg + */ + +#include +#include +#include +#include + +/** + * struct wiphy - wireless hardware description + * @idx: the wiphy index assigned to this item + * @class_dev: the class device representing /sys/class/ieee80211/ + */ +struct wiphy { + /* assign these fields before you register the wiphy */ + + /* permanent MAC address */ + u8 perm_addr[ETH_ALEN]; + + /* If multiple wiphys are registered and you're handed e.g. + * a regular netdev with assigned ieee80211_ptr, you won't + * know whether it points to a wiphy your driver has registered + * or not. Assign this to something global to your driver to + * help determine whether you own this wiphy or not. */ + void *privid; + + /* fields below are read-only, assigned by cfg80211 */ + + /* the item in /sys/class/ieee80211/ points to this, + * you need use set_wiphy_dev() (see below) */ + struct device dev; + + /* dir in debugfs: ieee80211/ */ + struct dentry *debugfsdir; + + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); +}; + +/** struct wireless_dev - wireless per-netdev state + * + * This structure must be allocated by the driver/stack + * that uses the ieee80211_ptr field in struct net_device + * (this is intentional so it can be allocated along with + * the netdev.) + * + * @wiphy: pointer to hardware description + */ +struct wireless_dev { + struct wiphy *wiphy; + + /* private to the generic wireless code */ + struct list_head list; + struct net_device *netdev; +}; + +/** + * wiphy_priv - return priv from wiphy + */ +static inline void *wiphy_priv(struct wiphy *wiphy) +{ + BUG_ON(!wiphy); + return &wiphy->priv; +} + +/** + * set_wiphy_dev - set device pointer for wiphy + */ +static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev) +{ + wiphy->dev.parent = dev; +} + +/** + * wiphy_dev - get wiphy dev pointer + */ +static inline struct device *wiphy_dev(struct wiphy *wiphy) +{ + return wiphy->dev.parent; +} + +/** + * wiphy_name - get wiphy name + */ +static inline char *wiphy_name(struct wiphy *wiphy) +{ + return wiphy->dev.bus_id; +} + +/** + * wdev_priv - return wiphy priv from wireless_dev + */ +static inline void *wdev_priv(struct wireless_dev *wdev) +{ + BUG_ON(!wdev); + return wiphy_priv(wdev->wiphy); +} + +/** + * wiphy_new - create a new wiphy for use with cfg80211 + * + * create a new wiphy and associate the given operations with it. + * @sizeof_priv bytes are allocated for private use. + * + * the returned pointer must be assigned to each netdev's + * ieee80211_ptr for proper operation. + */ +struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv); + +/** + * wiphy_register - register a wiphy with cfg80211 + * + * register the given wiphy + * + * Returns a non-negative wiphy index or a negative error code. + */ +extern int wiphy_register(struct wiphy *wiphy); + +/** + * wiphy_unregister - deregister a wiphy from cfg80211 + * + * unregister a device with the given priv pointer. + * After this call, no more requests can be made with this priv + * pointer, but the call may sleep to wait for an outstanding + * request that is being handled. + */ +extern void wiphy_unregister(struct wiphy *wiphy); + +/** + * wiphy_free - free wiphy + */ +extern void wiphy_free(struct wiphy *wiphy); + +#endif /* __NET_WIRELESS_H */ diff --git a/include/net/x25device.h b/include/net/x25device.h index 1d10c87..1415bcf 100644 --- a/include/net/x25device.h +++ b/include/net/x25device.h @@ -7,8 +7,8 @@ #include static inline __be16 x25_type_trans(struct sk_buff *skb, struct net_device *dev) { - skb->mac.raw = skb->data; skb->dev = dev; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; return htons(ETH_P_X25); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5a00aa8..66c2d3e 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -279,7 +279,7 @@ #define XFRM_TYPE_NON_FRAGMENT 1 xfrm_address_t *(*local_addr)(struct xfrm_state *, xfrm_address_t *); xfrm_address_t *(*remote_addr)(struct xfrm_state *, xfrm_address_t *); /* Estimate maximal size of result of transformation of a dgram */ - u32 (*get_max_size)(struct xfrm_state *, int size); + u32 (*get_mtu)(struct xfrm_state *, int size); }; extern int xfrm_register_type(struct xfrm_type *type, unsigned short family); @@ -416,6 +416,25 @@ struct xfrm_audit u32 secid; }; +/* SAD metadata, add more later */ +struct xfrm_sadinfo +{ + u32 sadhcnt; /* current hash bkts */ + u32 sadhmcnt; /* max allowed hash bkts */ + u32 sadcnt; /* current running count */ +}; + +struct xfrm_spdinfo +{ + u32 incnt; + u32 outcnt; + u32 fwdcnt; + u32 inscnt; + u32 outscnt; + u32 fwdscnt; + u32 spdhcnt; + u32 spdhmcnt; +}; #ifdef CONFIG_AUDITSYSCALL extern void xfrm_audit_log(uid_t auid, u32 secid, int type, int result, struct xfrm_policy *xp, struct xfrm_state *x); @@ -584,6 +603,10 @@ struct xfrm_dst struct rt6_info rt6; } u; struct dst_entry *route; +#ifdef CONFIG_XFRM_SUB_POLICY + struct flowi *origin; + struct xfrm_selector *partner; +#endif u32 genid; u32 route_mtu_cached; u32 child_mtu_cached; @@ -596,6 +619,12 @@ static inline void xfrm_dst_destroy(stru dst_release(xdst->route); if (likely(xdst->u.dst.xfrm)) xfrm_state_put(xdst->u.dst.xfrm); +#ifdef CONFIG_XFRM_SUB_POLICY + kfree(xdst->origin); + xdst->origin = NULL; + kfree(xdst->partner); + xdst->partner = NULL; +#endif } extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev); @@ -938,6 +967,8 @@ #endif extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq); extern int xfrm_state_delete(struct xfrm_state *x); extern void xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info); +extern void xfrm_sad_getinfo(struct xfrm_sadinfo *si); +extern void xfrm_spd_getinfo(struct xfrm_spdinfo *si); extern int xfrm_replay_check(struct xfrm_state *x, __be32 seq); extern void xfrm_replay_advance(struct xfrm_state *x, __be32 seq); extern void xfrm_replay_notify(struct xfrm_state *x, int event); diff --git a/include/rxrpc/call.h b/include/rxrpc/call.h deleted file mode 100644 index b86f837..0000000 --- a/include/rxrpc/call.h +++ /dev/null @@ -1,212 +0,0 @@ -/* call.h: Rx call record - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_CALL_H -#define _LINUX_RXRPC_CALL_H - -#include -#include -#include -#include - -#define RXRPC_CALL_ACK_WINDOW_SIZE 16 - -extern unsigned rxrpc_call_rcv_timeout; /* receive activity timeout (secs) */ - -/* application call state - * - only state 0 and ffff are reserved, the state is set to 1 after an opid is received - */ -enum rxrpc_app_cstate { - RXRPC_CSTATE_COMPLETE = 0, /* operation complete */ - RXRPC_CSTATE_ERROR, /* operation ICMP error or aborted */ - RXRPC_CSTATE_SRVR_RCV_OPID, /* [SERVER] receiving operation ID */ - RXRPC_CSTATE_SRVR_RCV_ARGS, /* [SERVER] receiving operation data */ - RXRPC_CSTATE_SRVR_GOT_ARGS, /* [SERVER] completely received operation data */ - RXRPC_CSTATE_SRVR_SND_REPLY, /* [SERVER] sending operation reply */ - RXRPC_CSTATE_SRVR_RCV_FINAL_ACK, /* [SERVER] receiving final ACK */ - RXRPC_CSTATE_CLNT_SND_ARGS, /* [CLIENT] sending operation args */ - RXRPC_CSTATE_CLNT_RCV_REPLY, /* [CLIENT] receiving operation reply */ - RXRPC_CSTATE_CLNT_GOT_REPLY, /* [CLIENT] completely received operation reply */ -} __attribute__((packed)); - -extern const char *rxrpc_call_states[]; - -enum rxrpc_app_estate { - RXRPC_ESTATE_NO_ERROR = 0, /* no error */ - RXRPC_ESTATE_LOCAL_ABORT, /* aborted locally by application layer */ - RXRPC_ESTATE_PEER_ABORT, /* aborted remotely by peer */ - RXRPC_ESTATE_LOCAL_ERROR, /* local ICMP network error */ - RXRPC_ESTATE_REMOTE_ERROR, /* remote ICMP network error */ -} __attribute__((packed)); - -extern const char *rxrpc_call_error_states[]; - -/*****************************************************************************/ -/* - * Rx call record and application scratch buffer - * - the call record occupies the bottom of a complete page - * - the application scratch buffer occupies the rest - */ -struct rxrpc_call -{ - atomic_t usage; - struct rxrpc_connection *conn; /* connection upon which active */ - spinlock_t lock; /* access lock */ - struct module *owner; /* owner module */ - wait_queue_head_t waitq; /* wait queue for events to happen */ - struct list_head link; /* general internal list link */ - struct list_head call_link; /* master call list link */ - __be32 chan_ix; /* connection channel index */ - __be32 call_id; /* call ID on connection */ - unsigned long cjif; /* jiffies at call creation */ - unsigned long flags; /* control flags */ -#define RXRPC_CALL_ACKS_TIMO 0x00000001 /* ACKS timeout reached */ -#define RXRPC_CALL_ACKR_TIMO 0x00000002 /* ACKR timeout reached */ -#define RXRPC_CALL_RCV_TIMO 0x00000004 /* RCV timeout reached */ -#define RXRPC_CALL_RCV_PKT 0x00000008 /* received packet */ - - /* transmission */ - rxrpc_seq_t snd_seq_count; /* outgoing packet sequence number counter */ - struct rxrpc_message *snd_nextmsg; /* next message being constructed for sending */ - struct rxrpc_message *snd_ping; /* last ping message sent */ - unsigned short snd_resend_cnt; /* count of resends since last ACK */ - - /* transmission ACK tracking */ - struct list_head acks_pendq; /* messages pending ACK (ordered by seq) */ - unsigned acks_pend_cnt; /* number of un-ACK'd packets */ - rxrpc_seq_t acks_dftv_seq; /* highest definitively ACK'd msg seq */ - struct timer_list acks_timeout; /* timeout on expected ACK */ - - /* reception */ - struct list_head rcv_receiveq; /* messages pending reception (ordered by seq) */ - struct list_head rcv_krxiodq_lk; /* krxiod queue for new inbound packets */ - struct timer_list rcv_timeout; /* call receive activity timeout */ - - /* reception ACK'ing */ - rxrpc_seq_t ackr_win_bot; /* bottom of ACK window */ - rxrpc_seq_t ackr_win_top; /* top of ACK window */ - rxrpc_seq_t ackr_high_seq; /* highest seqno yet received */ - rxrpc_seq_net_t ackr_prev_seq; /* previous seqno received */ - unsigned ackr_pend_cnt; /* number of pending ACKs */ - struct timer_list ackr_dfr_timo; /* timeout on deferred ACK */ - char ackr_dfr_perm; /* request for deferred ACKs permitted */ - rxrpc_seq_t ackr_dfr_seq; /* seqno for deferred ACK */ - struct rxrpc_ackpacket ackr; /* pending normal ACK packet */ - uint8_t ackr_array[RXRPC_CALL_ACK_WINDOW_SIZE]; /* ACK records */ - - /* presentation layer */ - char app_last_rcv; /* T if received last packet from remote end */ - enum rxrpc_app_cstate app_call_state; /* call state */ - enum rxrpc_app_estate app_err_state; /* abort/error state */ - struct list_head app_readyq; /* ordered ready received packet queue */ - struct list_head app_unreadyq; /* ordered post-hole recv'd packet queue */ - rxrpc_seq_t app_ready_seq; /* last seq number dropped into readyq */ - size_t app_ready_qty; /* amount of data ready in readyq */ - unsigned app_opcode; /* operation ID */ - unsigned app_abort_code; /* abort code (when aborted) */ - int app_errno; /* error number (when ICMP error received) */ - - /* statisics */ - unsigned pkt_rcv_count; /* count of received packets on this call */ - unsigned pkt_snd_count; /* count of sent packets on this call */ - unsigned app_read_count; /* number of reads issued */ - - /* bits for the application to use */ - rxrpc_call_attn_func_t app_attn_func; /* callback when attention required */ - rxrpc_call_error_func_t app_error_func; /* callback when abort sent (cleanup and put) */ - rxrpc_call_aemap_func_t app_aemap_func; /* callback to map abort code to/from errno */ - void *app_user; /* application data */ - struct list_head app_link; /* application list linkage */ - struct list_head app_attn_link; /* application attention list linkage */ - size_t app_mark; /* trigger callback when app_ready_qty>=app_mark */ - char app_async_read; /* T if in async-read mode */ - uint8_t *app_read_buf; /* application async read buffer (app_mark size) */ - uint8_t *app_scr_alloc; /* application scratch allocation pointer */ - void *app_scr_ptr; /* application pointer into scratch buffer */ - -#define RXRPC_APP_MARK_EOF 0xFFFFFFFFU /* mark at end of input */ - - /* application scratch buffer */ - uint8_t app_scratch[0] __attribute__((aligned(sizeof(long)))); -}; - -#define RXRPC_CALL_SCRATCH_SIZE (PAGE_SIZE - sizeof(struct rxrpc_call)) - -#define rxrpc_call_reset_scratch(CALL) \ -do { (CALL)->app_scr_alloc = (CALL)->app_scratch; } while(0) - -#define rxrpc_call_alloc_scratch(CALL,SIZE) \ -({ \ - void *ptr; \ - ptr = (CALL)->app_scr_alloc; \ - (CALL)->app_scr_alloc += (SIZE); \ - if ((SIZE)>RXRPC_CALL_SCRATCH_SIZE || \ - (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) { \ - printk("rxrpc_call_alloc_scratch(%p,%Zu)\n",(CALL),(size_t)(SIZE)); \ - BUG(); \ - } \ - ptr; \ -}) - -#define rxrpc_call_alloc_scratch_s(CALL,TYPE) \ -({ \ - size_t size = sizeof(TYPE); \ - TYPE *ptr; \ - ptr = (TYPE*)(CALL)->app_scr_alloc; \ - (CALL)->app_scr_alloc += size; \ - if (size>RXRPC_CALL_SCRATCH_SIZE || \ - (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) { \ - printk("rxrpc_call_alloc_scratch(%p,%Zu)\n",(CALL),size); \ - BUG(); \ - } \ - ptr; \ -}) - -#define rxrpc_call_is_ack_pending(CALL) ((CALL)->ackr.reason != 0) - -extern int rxrpc_create_call(struct rxrpc_connection *conn, - rxrpc_call_attn_func_t attn, - rxrpc_call_error_func_t error, - rxrpc_call_aemap_func_t aemap, - struct rxrpc_call **_call); - -extern int rxrpc_incoming_call(struct rxrpc_connection *conn, - struct rxrpc_message *msg, - struct rxrpc_call **_call); - -static inline void rxrpc_get_call(struct rxrpc_call *call) -{ - BUG_ON(atomic_read(&call->usage)<=0); - atomic_inc(&call->usage); - /*printk("rxrpc_get_call(%p{u=%d})\n",(C),atomic_read(&(C)->usage));*/ -} - -extern void rxrpc_put_call(struct rxrpc_call *call); - -extern void rxrpc_call_do_stuff(struct rxrpc_call *call); - -extern int rxrpc_call_abort(struct rxrpc_call *call, int error); - -#define RXRPC_CALL_READ_BLOCK 0x0001 /* block if not enough data and not yet EOF */ -#define RXRPC_CALL_READ_ALL 0x0002 /* error if insufficient data received */ -extern int rxrpc_call_read_data(struct rxrpc_call *call, void *buffer, size_t size, int flags); - -extern int rxrpc_call_write_data(struct rxrpc_call *call, - size_t sioc, - struct kvec *siov, - uint8_t rxhdr_flags, - gfp_t alloc_flags, - int dup_data, - size_t *size_sent); - -extern void rxrpc_call_handle_error(struct rxrpc_call *conn, int local, int errno); - -#endif /* _LINUX_RXRPC_CALL_H */ diff --git a/include/rxrpc/connection.h b/include/rxrpc/connection.h deleted file mode 100644 index 41e6781..0000000 --- a/include/rxrpc/connection.h +++ /dev/null @@ -1,83 +0,0 @@ -/* connection.h: Rx connection record - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_CONNECTION_H -#define _LINUX_RXRPC_CONNECTION_H - -#include -#include - -struct sk_buff; - -/*****************************************************************************/ -/* - * Rx connection - * - connections are matched by (rmt_port,rmt_addr,service_id,conn_id,clientflag) - * - connections only retain a refcount on the peer when they are active - * - connections with refcount==0 are inactive and reside in the peer's graveyard - */ -struct rxrpc_connection -{ - atomic_t usage; - struct rxrpc_transport *trans; /* transport endpoint */ - struct rxrpc_peer *peer; /* peer from/to which connected */ - struct rxrpc_service *service; /* responsible service (inbound conns) */ - struct rxrpc_timer timeout; /* decaching timer */ - struct list_head link; /* link in peer's list */ - struct list_head proc_link; /* link in proc list */ - struct list_head err_link; /* link in ICMP error processing list */ - struct list_head id_link; /* link in ID grant list */ - struct sockaddr_in addr; /* remote address */ - struct rxrpc_call *channels[4]; /* channels (active calls) */ - wait_queue_head_t chanwait; /* wait for channel to become available */ - spinlock_t lock; /* access lock */ - struct timeval atime; /* last access time */ - size_t mtu_size; /* MTU size for outbound messages */ - unsigned call_counter; /* call ID counter */ - rxrpc_serial_t serial_counter; /* packet serial number counter */ - - /* the following should all be in net order */ - __be32 in_epoch; /* peer's epoch */ - __be32 out_epoch; /* my epoch */ - __be32 conn_id; /* connection ID, appropriately shifted */ - __be16 service_id; /* service ID */ - uint8_t security_ix; /* security ID */ - uint8_t in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */ - uint8_t out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */ -}; - -extern int rxrpc_create_connection(struct rxrpc_transport *trans, - __be16 port, - __be32 addr, - uint16_t service_id, - void *security, - struct rxrpc_connection **_conn); - -extern int rxrpc_connection_lookup(struct rxrpc_peer *peer, - struct rxrpc_message *msg, - struct rxrpc_connection **_conn); - -static inline void rxrpc_get_connection(struct rxrpc_connection *conn) -{ - BUG_ON(atomic_read(&conn->usage)<0); - atomic_inc(&conn->usage); - //printk("rxrpc_get_conn(%p{u=%d})\n",conn,atomic_read(&conn->usage)); -} - -extern void rxrpc_put_connection(struct rxrpc_connection *conn); - -extern int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn, - struct rxrpc_call *call, - struct rxrpc_message *msg); - -extern void rxrpc_conn_handle_error(struct rxrpc_connection *conn, int local, int errno); - -#endif /* _LINUX_RXRPC_CONNECTION_H */ diff --git a/include/rxrpc/krxiod.h b/include/rxrpc/krxiod.h deleted file mode 100644 index c0e0e82..0000000 --- a/include/rxrpc/krxiod.h +++ /dev/null @@ -1,27 +0,0 @@ -/* krxiod.h: Rx RPC I/O kernel thread interface - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_KRXIOD_H -#define _LINUX_RXRPC_KRXIOD_H - -#include - -extern int rxrpc_krxiod_init(void); -extern void rxrpc_krxiod_kill(void); -extern void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans); -extern void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans); -extern void rxrpc_krxiod_queue_peer(struct rxrpc_peer *peer); -extern void rxrpc_krxiod_dequeue_peer(struct rxrpc_peer *peer); -extern void rxrpc_krxiod_clear_peers(struct rxrpc_transport *trans); -extern void rxrpc_krxiod_queue_call(struct rxrpc_call *call); -extern void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call); - -#endif /* _LINUX_RXRPC_KRXIOD_H */ diff --git a/include/rxrpc/krxsecd.h b/include/rxrpc/krxsecd.h deleted file mode 100644 index 55ce43a..0000000 --- a/include/rxrpc/krxsecd.h +++ /dev/null @@ -1,22 +0,0 @@ -/* krxsecd.h: Rx RPC security kernel thread interface - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_KRXSECD_H -#define _LINUX_RXRPC_KRXSECD_H - -#include - -extern int rxrpc_krxsecd_init(void); -extern void rxrpc_krxsecd_kill(void); -extern void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans); -extern void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg); - -#endif /* _LINUX_RXRPC_KRXSECD_H */ diff --git a/include/rxrpc/krxtimod.h b/include/rxrpc/krxtimod.h deleted file mode 100644 index b3d298b..0000000 --- a/include/rxrpc/krxtimod.h +++ /dev/null @@ -1,45 +0,0 @@ -/* krxtimod.h: RxRPC timeout daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_KRXTIMOD_H -#define _LINUX_RXRPC_KRXTIMOD_H - -#include - -struct rxrpc_timer_ops { - /* called when the front of the timer queue has timed out */ - void (*timed_out)(struct rxrpc_timer *timer); -}; - -/*****************************************************************************/ -/* - * RXRPC timer/timeout record - */ -struct rxrpc_timer -{ - struct list_head link; /* link in timer queue */ - unsigned long timo_jif; /* timeout time */ - const struct rxrpc_timer_ops *ops; /* timeout expiry function */ -}; - -static inline void rxrpc_timer_init(rxrpc_timer_t *timer, const struct rxrpc_timer_ops *ops) -{ - INIT_LIST_HEAD(&timer->link); - timer->ops = ops; -} - -extern int rxrpc_krxtimod_start(void); -extern void rxrpc_krxtimod_kill(void); - -extern void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout); -extern int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer); - -#endif /* _LINUX_RXRPC_KRXTIMOD_H */ diff --git a/include/rxrpc/message.h b/include/rxrpc/message.h deleted file mode 100644 index b318f27..0000000 --- a/include/rxrpc/message.h +++ /dev/null @@ -1,71 +0,0 @@ -/* message.h: Rx message caching - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_MESSAGE_H -#define _LINUX_RXRPC_MESSAGE_H - -#include - -/*****************************************************************************/ -/* - * Rx message record - */ -struct rxrpc_message -{ - atomic_t usage; - struct list_head link; /* list link */ - struct timeval stamp; /* time received or last sent */ - rxrpc_seq_t seq; /* message sequence number */ - - int state; /* the state the message is currently in */ -#define RXRPC_MSG_PREPARED 0 -#define RXRPC_MSG_SENT 1 -#define RXRPC_MSG_ACKED 2 /* provisionally ACK'd */ -#define RXRPC_MSG_DONE 3 /* definitively ACK'd (msg->sequsage); } while(0) - -extern void __rxrpc_put_message(struct rxrpc_message *msg); -static inline void rxrpc_put_message(struct rxrpc_message *msg) -{ - BUG_ON(atomic_read(&msg->usage)<=0); - if (atomic_dec_and_test(&msg->usage)) - __rxrpc_put_message(msg); -} - -extern int rxrpc_conn_newmsg(struct rxrpc_connection *conn, - struct rxrpc_call *call, - uint8_t type, - int count, - struct kvec *diov, - gfp_t alloc_flags, - struct rxrpc_message **_msg); - -extern int rxrpc_conn_sendmsg(struct rxrpc_connection *conn, struct rxrpc_message *msg); - -#endif /* _LINUX_RXRPC_MESSAGE_H */ diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h index 1447f0a..b69e6e1 100644 --- a/include/rxrpc/packet.h +++ b/include/rxrpc/packet.h @@ -1,6 +1,6 @@ /* packet.h: Rx packet layout and definitions * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -12,28 +12,25 @@ #ifndef _LINUX_RXRPC_PACKET_H #define _LINUX_RXRPC_PACKET_H -#include - -#define RXRPC_IPUDP_SIZE 28 -extern size_t RXRPC_MAX_PACKET_SIZE; -#define RXRPC_MAX_PACKET_DATA_SIZE (RXRPC_MAX_PACKET_SIZE - sizeof(struct rxrpc_header)) -#define RXRPC_LOCAL_PACKET_SIZE RXRPC_MAX_PACKET_SIZE -#define RXRPC_REMOTE_PACKET_SIZE (576 - RXRPC_IPUDP_SIZE) +typedef u32 rxrpc_seq_t; /* Rx message sequence number */ +typedef u32 rxrpc_serial_t; /* Rx message serial number */ +typedef __be32 rxrpc_seq_net_t; /* on-the-wire Rx message sequence number */ +typedef __be32 rxrpc_serial_net_t; /* on-the-wire Rx message serial number */ /*****************************************************************************/ /* * on-the-wire Rx packet header * - all multibyte fields should be in network byte order */ -struct rxrpc_header -{ +struct rxrpc_header { __be32 epoch; /* client boot timestamp */ __be32 cid; /* connection and channel ID */ #define RXRPC_MAXCALLS 4 /* max active calls per conn */ #define RXRPC_CHANNELMASK (RXRPC_MAXCALLS-1) /* mask for channel ID */ #define RXRPC_CIDMASK (~RXRPC_CHANNELMASK) /* mask for connection ID */ -#define RXRPC_CIDSHIFT 2 /* shift for connection ID */ +#define RXRPC_CIDSHIFT ilog2(RXRPC_MAXCALLS) /* shift for connection ID */ +#define RXRPC_CID_INC (1 << RXRPC_CIDSHIFT) /* connection ID increment */ __be32 callNumber; /* call ID (0 for connection-level packets) */ #define RXRPC_PROCESS_MAXCALLS (1<<2) /* maximum number of active calls per conn (power of 2) */ @@ -62,7 +59,10 @@ #define RXRPC_SLOW_START_OK 0x20 /* [AC uint8_t userStatus; /* app-layer defined status */ uint8_t securityIndex; /* security protocol ID */ - __be16 _rsvd; /* reserved (used by kerberos security as cksum) */ + union { + __be16 _rsvd; /* reserved */ + __be16 cksum; /* kerberos security checksum */ + }; __be16 serviceId; /* service ID */ } __attribute__((packed)); @@ -81,8 +81,7 @@ extern const char *rxrpc_pkts[]; * - new__rsvd = j__rsvd * - duplicating all other fields */ -struct rxrpc_jumbo_header -{ +struct rxrpc_jumbo_header { uint8_t flags; /* packet flags (as per rxrpc_header) */ uint8_t pad; __be16 _rsvd; /* reserved (used by kerberos security as cksum) */ @@ -95,8 +94,7 @@ #define RXRPC_JUMBO_DATALEN 1412 /* non- * on-the-wire Rx ACK packet data payload * - all multibyte fields should be in network byte order */ -struct rxrpc_ackpacket -{ +struct rxrpc_ackpacket { __be16 bufferSpace; /* number of packet buffers available */ __be16 maxSkew; /* diff between serno being ACK'd and highest serial no * received */ @@ -124,4 +122,93 @@ #define RXRPC_ACK_TYPE_ACK 1 } __attribute__((packed)); +/* + * ACK packets can have a further piece of information tagged on the end + */ +struct rxrpc_ackinfo { + __be32 rxMTU; /* maximum Rx MTU size (bytes) [AFS 3.3] */ + __be32 maxMTU; /* maximum interface MTU size (bytes) [AFS 3.3] */ + __be32 rwind; /* Rx window size (packets) [AFS 3.4] */ + __be32 jumbo_max; /* max packets to stick into a jumbo packet [AFS 3.5] */ +}; + +/*****************************************************************************/ +/* + * Kerberos security type-2 challenge packet + */ +struct rxkad_challenge { + __be32 version; /* version of this challenge type */ + __be32 nonce; /* encrypted random number */ + __be32 min_level; /* minimum security level */ + __be32 __padding; /* padding to 8-byte boundary */ +} __attribute__((packed)); + +/*****************************************************************************/ +/* + * Kerberos security type-2 response packet + */ +struct rxkad_response { + __be32 version; /* version of this reponse type */ + __be32 __pad; + + /* encrypted bit of the response */ + struct { + __be32 epoch; /* current epoch */ + __be32 cid; /* parent connection ID */ + __be32 checksum; /* checksum */ + __be32 securityIndex; /* security type */ + __be32 call_id[4]; /* encrypted call IDs */ + __be32 inc_nonce; /* challenge nonce + 1 */ + __be32 level; /* desired level */ + } encrypted; + + __be32 kvno; /* Kerberos key version number */ + __be32 ticket_len; /* Kerberos ticket length */ +} __attribute__((packed)); + +/*****************************************************************************/ +/* + * RxRPC-level abort codes + */ +#define RX_CALL_DEAD -1 /* call/conn has been inactive and is shut down */ +#define RX_INVALID_OPERATION -2 /* invalid operation requested / attempted */ +#define RX_CALL_TIMEOUT -3 /* call timeout exceeded */ +#define RX_EOF -4 /* unexpected end of data on read op */ +#define RX_PROTOCOL_ERROR -5 /* low-level protocol error */ +#define RX_USER_ABORT -6 /* generic user abort */ +#define RX_ADDRINUSE -7 /* UDP port in use */ +#define RX_DEBUGI_BADTYPE -8 /* bad debugging packet type */ + +/* + * (un)marshalling abort codes (rxgen) + */ +#define RXGEN_CC_MARSHAL -450 +#define RXGEN_CC_UNMARSHAL -451 +#define RXGEN_SS_MARSHAL -452 +#define RXGEN_SS_UNMARSHAL -453 +#define RXGEN_DECODE -454 +#define RXGEN_OPCODE -455 +#define RXGEN_SS_XDRFREE -456 +#define RXGEN_CC_XDRFREE -457 + +/* + * Rx kerberos security abort codes + * - unfortunately we have no generalised security abort codes to say things + * like "unsupported security", so we have to use these instead and hope the + * other side understands + */ +#define RXKADINCONSISTENCY 19270400 /* security module structure inconsistent */ +#define RXKADPACKETSHORT 19270401 /* packet too short for security challenge */ +#define RXKADLEVELFAIL 19270402 /* security level negotiation failed */ +#define RXKADTICKETLEN 19270403 /* ticket length too short or too long */ +#define RXKADOUTOFSEQUENCE 19270404 /* packet had bad sequence number */ +#define RXKADNOAUTH 19270405 /* caller not authorised */ +#define RXKADBADKEY 19270406 /* illegal key: bad parity or weak */ +#define RXKADBADTICKET 19270407 /* security object was passed a bad ticket */ +#define RXKADUNKNOWNKEY 19270408 /* ticket contained unknown key version number */ +#define RXKADEXPIRED 19270409 /* authentication expired */ +#define RXKADSEALEDINCON 19270410 /* sealed data inconsistent */ +#define RXKADDATALEN 19270411 /* user data too long */ +#define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */ + #endif /* _LINUX_RXRPC_PACKET_H */ diff --git a/include/rxrpc/peer.h b/include/rxrpc/peer.h deleted file mode 100644 index 8b8fe97..0000000 --- a/include/rxrpc/peer.h +++ /dev/null @@ -1,82 +0,0 @@ -/* peer.h: Rx RPC per-transport peer record - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_PEER_H -#define _LINUX_RXRPC_PEER_H - -#include -#include -#include - -struct rxrpc_peer_ops -{ - /* peer record being added */ - int (*adding)(struct rxrpc_peer *peer); - - /* peer record being discarded from graveyard */ - void (*discarding)(struct rxrpc_peer *peer); - - /* change of epoch detected on connection */ - void (*change_of_epoch)(struct rxrpc_connection *conn); -}; - -/*****************************************************************************/ -/* - * Rx RPC per-transport peer record - * - peers only retain a refcount on the transport when they are active - * - peers with refcount==0 are inactive and reside in the transport's graveyard - */ -struct rxrpc_peer -{ - atomic_t usage; - struct rxrpc_peer_ops *ops; /* operations on this peer */ - struct rxrpc_transport *trans; /* owner transport */ - struct rxrpc_timer timeout; /* timeout for grave destruction */ - struct list_head link; /* link in transport's peer list */ - struct list_head proc_link; /* link in /proc list */ - rwlock_t conn_idlock; /* lock for connection IDs */ - struct list_head conn_idlist; /* list of connections granted IDs */ - uint32_t conn_idcounter; /* connection ID counter */ - rwlock_t conn_lock; /* lock for active/dead connections */ - struct list_head conn_active; /* active connections to/from this peer */ - struct list_head conn_graveyard; /* graveyard for inactive connections */ - spinlock_t conn_gylock; /* lock for conn_graveyard */ - wait_queue_head_t conn_gy_waitq; /* wait queue hit when graveyard is empty */ - atomic_t conn_count; /* number of attached connections */ - struct in_addr addr; /* remote address */ - size_t if_mtu; /* interface MTU for this peer */ - spinlock_t lock; /* access lock */ - - void *user; /* application layer data */ - - /* calculated RTT cache */ -#define RXRPC_RTT_CACHE_SIZE 32 - suseconds_t rtt; /* current RTT estimate (in uS) */ - unsigned rtt_point; /* next entry at which to insert */ - unsigned rtt_usage; /* amount of cache actually used */ - suseconds_t rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */ -}; - - -extern int rxrpc_peer_lookup(struct rxrpc_transport *trans, - __be32 addr, - struct rxrpc_peer **_peer); - -static inline void rxrpc_get_peer(struct rxrpc_peer *peer) -{ - BUG_ON(atomic_read(&peer->usage)<0); - atomic_inc(&peer->usage); - //printk("rxrpc_get_peer(%p{u=%d})\n",peer,atomic_read(&peer->usage)); -} - -extern void rxrpc_put_peer(struct rxrpc_peer *peer); - -#endif /* _LINUX_RXRPC_PEER_H */ diff --git a/include/rxrpc/rxrpc.h b/include/rxrpc/rxrpc.h deleted file mode 100644 index 8d9874c..0000000 --- a/include/rxrpc/rxrpc.h +++ /dev/null @@ -1,36 +0,0 @@ -/* rx.h: Rx RPC interface - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_RXRPC_H -#define _LINUX_RXRPC_RXRPC_H - -#ifdef __KERNEL__ - -extern __be32 rxrpc_epoch; - -#ifdef CONFIG_SYSCTL -extern int rxrpc_ktrace; -extern int rxrpc_kdebug; -extern int rxrpc_kproto; -extern int rxrpc_knet; -#else -#define rxrpc_ktrace 0 -#define rxrpc_kdebug 0 -#define rxrpc_kproto 0 -#define rxrpc_knet 0 -#endif - -extern int rxrpc_sysctl_init(void); -extern void rxrpc_sysctl_cleanup(void); - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_RXRPC_RXRPC_H */ diff --git a/include/rxrpc/transport.h b/include/rxrpc/transport.h deleted file mode 100644 index 7c7b968..0000000 --- a/include/rxrpc/transport.h +++ /dev/null @@ -1,106 +0,0 @@ -/* transport.h: Rx transport management - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#ifndef _LINUX_RXRPC_TRANSPORT_H -#define _LINUX_RXRPC_TRANSPORT_H - -#include -#include -#include -#include -#include - -typedef int (*rxrpc_newcall_fnx_t)(struct rxrpc_call *call); - -extern wait_queue_head_t rxrpc_krxiod_wq; - -/*****************************************************************************/ -/* - * Rx operation specification - * - tables of these must be sorted by op ID so that they can be binary-chop searched - */ -struct rxrpc_operation -{ - unsigned id; /* operation ID */ - size_t asize; /* minimum size of argument block */ - const char *name; /* name of operation */ - void *user; /* initial user data */ -}; - -/*****************************************************************************/ -/* - * Rx transport service record - */ -struct rxrpc_service -{ - struct list_head link; /* link in services list on transport */ - struct module *owner; /* owner module */ - rxrpc_newcall_fnx_t new_call; /* new call handler function */ - const char *name; /* name of service */ - unsigned short service_id; /* Rx service ID */ - rxrpc_call_attn_func_t attn_func; /* call requires attention callback */ - rxrpc_call_error_func_t error_func; /* call error callback */ - rxrpc_call_aemap_func_t aemap_func; /* abort -> errno mapping callback */ - - const struct rxrpc_operation *ops_begin; /* beginning of operations table */ - const struct rxrpc_operation *ops_end; /* end of operations table */ -}; - -/*****************************************************************************/ -/* - * Rx transport endpoint record - */ -struct rxrpc_transport -{ - atomic_t usage; - struct socket *socket; /* my UDP socket */ - struct list_head services; /* services listening on this socket */ - struct list_head link; /* link in transport list */ - struct list_head proc_link; /* link in transport proc list */ - struct list_head krxiodq_link; /* krxiod attention queue link */ - spinlock_t lock; /* access lock */ - struct list_head peer_active; /* active peers connected to over this socket */ - struct list_head peer_graveyard; /* inactive peer list */ - spinlock_t peer_gylock; /* peer graveyard lock */ - wait_queue_head_t peer_gy_waitq; /* wait queue hit when peer graveyard is empty */ - rwlock_t peer_lock; /* peer list access lock */ - atomic_t peer_count; /* number of peers */ - struct rxrpc_peer_ops *peer_ops; /* default peer operations */ - unsigned short port; /* port upon which listening */ - volatile char error_rcvd; /* T if received ICMP error outstanding */ -}; - -extern int rxrpc_create_transport(unsigned short port, - struct rxrpc_transport **_trans); - -static inline void rxrpc_get_transport(struct rxrpc_transport *trans) -{ - BUG_ON(atomic_read(&trans->usage) <= 0); - atomic_inc(&trans->usage); - //printk("rxrpc_get_transport(%p{u=%d})\n", - // trans, atomic_read(&trans->usage)); -} - -extern void rxrpc_put_transport(struct rxrpc_transport *trans); - -extern int rxrpc_add_service(struct rxrpc_transport *trans, - struct rxrpc_service *srv); - -extern void rxrpc_del_service(struct rxrpc_transport *trans, - struct rxrpc_service *srv); - -extern void rxrpc_trans_receive_packet(struct rxrpc_transport *trans); - -extern int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans, - struct rxrpc_message *msg, - int error); - -#endif /* _LINUX_RXRPC_TRANSPORT_H */ diff --git a/kernel/audit.c b/kernel/audit.c index 76c9a11..4e9d208 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -151,7 +151,7 @@ struct audit_buffer { static void audit_set_pid(struct audit_buffer *ab, pid_t pid) { - struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; + struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); nlh->nlmsg_pid = pid; } @@ -750,7 +750,7 @@ static void audit_receive_skb(struct sk_ u32 rlen; while (skb->len >= NLMSG_SPACE(0)) { - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) return; rlen = NLMSG_ALIGN(nlh->nlmsg_len); @@ -795,7 +795,7 @@ static int __init audit_init(void) printk(KERN_INFO "audit: initializing netlink socket (%s)\n", audit_default ? "enabled" : "disabled"); audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive, - THIS_MODULE); + NULL, THIS_MODULE); if (!audit_sock) audit_panic("cannot initialize netlink socket"); else @@ -1073,7 +1073,7 @@ static void audit_log_vformat(struct aud goto out; } va_copy(args2, args); - len = vsnprintf(skb->tail, avail, fmt, args); + len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args); if (len >= avail) { /* The printk buffer is 1024 bytes long, so if we get * here and AUDIT_BUFSIZ is at least 1024, then we can @@ -1082,7 +1082,7 @@ static void audit_log_vformat(struct aud max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail)); if (!avail) goto out; - len = vsnprintf(skb->tail, avail, fmt, args2); + len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2); } if (len > 0) skb_put(skb, len); @@ -1143,7 +1143,7 @@ void audit_log_hex(struct audit_buffer * return; } - ptr = skb->tail; + ptr = skb_tail_pointer(skb); for (i=0; i>4]; /* Upper nibble */ *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ @@ -1175,7 +1175,7 @@ static void audit_log_n_string(struct au if (!avail) return; } - ptr = skb->tail; + ptr = skb_tail_pointer(skb); *ptr++ = '"'; memcpy(ptr, string, slen); ptr += slen; @@ -1268,7 +1268,7 @@ void audit_log_end(struct audit_buffer * audit_log_lost("rate limit exceeded"); } else { if (audit_pid) { - struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; + struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0); skb_queue_tail(&audit_skb_queue, ab->skb); ab->skb = NULL; diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index b74860a..1b30331 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -59,6 +59,7 @@ ktime_t ktime_get(void) return timespec_to_ktime(now); } +EXPORT_SYMBOL_GPL(ktime_get); /** * ktime_get_real - get the real (wall-) time in ktime_t format @@ -278,6 +279,8 @@ ktime_t ktime_add_ns(const ktime_t kt, u return ktime_add(kt, tmp); } + +EXPORT_SYMBOL_GPL(ktime_add_ns); # endif /* !CONFIG_KTIME_SCALAR */ /* diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 6f294ff..5a0de84 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -267,27 +267,33 @@ const char *kallsyms_lookup(unsigned lon return NULL; } -/* Replace "%s" in format with address, or returns -errno. */ -void __print_symbol(const char *fmt, unsigned long address) +/* Look up a kernel symbol and return it in a text buffer. */ +int sprint_symbol(char *buffer, unsigned long address) { char *modname; const char *name; unsigned long offset, size; char namebuf[KSYM_NAME_LEN+1]; - char buffer[sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN + - 2*(BITS_PER_LONG*3/10) + MODULE_NAME_LEN + 1]; name = kallsyms_lookup(address, &size, &offset, &modname, namebuf); - if (!name) - sprintf(buffer, "0x%lx", address); + return sprintf(buffer, "0x%lx", address); else { if (modname) - sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset, + return sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset, size, modname); else - sprintf(buffer, "%s+%#lx/%#lx", name, offset, size); + return sprintf(buffer, "%s+%#lx/%#lx", name, offset, size); } +} + +/* Look up a kernel symbol and print it to the kernel messages. */ +void __print_symbol(const char *fmt, unsigned long address) +{ + char buffer[KSYM_SYMBOL_LEN]; + + sprint_symbol(buffer, address); + printk(fmt, buffer); } @@ -452,3 +458,4 @@ static int __init kallsyms_init(void) __initcall(kallsyms_init); EXPORT_SYMBOL(__print_symbol); +EXPORT_SYMBOL_GPL(sprint_symbol); diff --git a/kernel/module.c b/kernel/module.c index dcdb32b..9da5af6 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1148,8 +1148,10 @@ int mod_sysfs_setup(struct module *mod, goto out; mod->holders_dir = kobject_add_dir(&mod->mkobj.kobj, "holders"); - if (!mod->holders_dir) + if (!mod->holders_dir) { + err = -ENOMEM; goto out_unreg; + } err = module_param_sysfs_setup(mod, kparam, num_params); if (err) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index aec19b0..02e4fb6 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -39,7 +39,13 @@ static inline int platform_prepare(void) { int error = 0; - if (pm_disk_mode == PM_DISK_PLATFORM) { + switch (pm_disk_mode) { + case PM_DISK_TEST: + case PM_DISK_TESTPROC: + case PM_DISK_SHUTDOWN: + case PM_DISK_REBOOT: + break; + default: if (pm_ops && pm_ops->prepare) error = pm_ops->prepare(PM_SUSPEND_DISK); } @@ -48,40 +54,48 @@ static inline int platform_prepare(void) /** * power_down - Shut machine down for hibernate. - * @mode: Suspend-to-disk mode * - * Use the platform driver, if configured so, and return gracefully if it - * fails. - * Otherwise, try to power off and reboot. If they fail, halt the machine, - * there ain't no turning back. + * Use the platform driver, if configured so; otherwise try + * to power off or reboot. */ -static void power_down(suspend_disk_method_t mode) +static void power_down(void) { - switch(mode) { - case PM_DISK_PLATFORM: - if (pm_ops && pm_ops->enter) { - kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); - pm_ops->enter(PM_SUSPEND_DISK); - break; - } + switch (pm_disk_mode) { + case PM_DISK_TEST: + case PM_DISK_TESTPROC: + break; case PM_DISK_SHUTDOWN: kernel_power_off(); break; case PM_DISK_REBOOT: kernel_restart(NULL); break; + default: + if (pm_ops && pm_ops->enter) { + kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); + pm_ops->enter(PM_SUSPEND_DISK); + break; + } } kernel_halt(); - /* Valid image is on the disk, if we continue we risk serious data corruption - after resume. */ + /* + * Valid image is on the disk, if we continue we risk serious data + * corruption after resume. + */ printk(KERN_CRIT "Please power me down manually\n"); while(1); } static inline void platform_finish(void) { - if (pm_disk_mode == PM_DISK_PLATFORM) { + switch (pm_disk_mode) { + case PM_DISK_TEST: + case PM_DISK_TESTPROC: + case PM_DISK_SHUTDOWN: + case PM_DISK_REBOOT: + break; + default: if (pm_ops && pm_ops->finish) pm_ops->finish(PM_SUSPEND_DISK); } @@ -108,8 +122,6 @@ static int prepare_processes(void) /** * pm_suspend_disk - The granpappy of hibernation power management. * - * If we're going through the firmware, then get it over with quickly. - * * If not, then call swsusp to do its thing, then figure out how * to power down the system. */ @@ -166,7 +178,7 @@ int pm_suspend_disk(void) pr_debug("PM: writing image.\n"); error = swsusp_write(); if (!error) - power_down(pm_disk_mode); + power_down(); else { swsusp_free(); goto Thaw; @@ -278,7 +290,6 @@ late_initcall(software_resume); static const char * const pm_disk_modes[] = { - [PM_DISK_FIRMWARE] = "firmware", [PM_DISK_PLATFORM] = "platform", [PM_DISK_SHUTDOWN] = "shutdown", [PM_DISK_REBOOT] = "reboot", @@ -289,27 +300,25 @@ static const char * const pm_disk_modes[ /** * disk - Control suspend-to-disk mode * - * Suspend-to-disk can be handled in several ways. The greatest - * distinction is who writes memory to disk - the firmware or the OS. - * If the firmware does it, we assume that it also handles suspending - * the system. - * If the OS does it, then we have three options for putting the system - * to sleep - using the platform driver (e.g. ACPI or other PM registers), - * powering off the system or rebooting the system (for testing). + * Suspend-to-disk can be handled in several ways. We have a few options + * for putting the system to sleep - using the platform driver (e.g. ACPI + * or other pm_ops), powering off the system or rebooting the system + * (for testing) as well as the two test modes. * - * The system will support either 'firmware' or 'platform', and that is - * known a priori (and encoded in pm_ops). But, the user may choose - * 'shutdown' or 'reboot' as alternatives. + * The system can support 'platform', and that is known a priori (and + * encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot' + * as alternatives, as well as the test modes 'test' and 'testproc'. * * show() will display what the mode is currently set to. * store() will accept one of * - * 'firmware' * 'platform' * 'shutdown' * 'reboot' + * 'test' + * 'testproc' * - * It will only change to 'firmware' or 'platform' if the system + * It will only change to 'platform' if the system * supports it (as determined from pm_ops->pm_disk_mode). */ @@ -331,17 +340,21 @@ static ssize_t disk_store(struct subsyst len = p ? p - buf : n; mutex_lock(&pm_mutex); - for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) { + for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { if (!strncmp(buf, pm_disk_modes[i], len)) { mode = i; break; } } if (mode) { - if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT || - mode == PM_DISK_TEST || mode == PM_DISK_TESTPROC) { + switch (mode) { + case PM_DISK_SHUTDOWN: + case PM_DISK_REBOOT: + case PM_DISK_TEST: + case PM_DISK_TESTPROC: pm_disk_mode = mode; - } else { + break; + default: if (pm_ops && pm_ops->enter && (mode == pm_ops->pm_disk_mode)) pm_disk_mode = mode; diff --git a/kernel/power/main.c b/kernel/power/main.c index a064dfd..72419a3 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -30,7 +30,7 @@ #define FREE_PAGE_NUMBER (100) DEFINE_MUTEX(pm_mutex); struct pm_ops *pm_ops; -suspend_disk_method_t pm_disk_mode = PM_DISK_PLATFORM; +suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN; /** * pm_set_ops - Set the global power method table. @@ -41,9 +41,26 @@ void pm_set_ops(struct pm_ops * ops) { mutex_lock(&pm_mutex); pm_ops = ops; + if (ops && ops->pm_disk_mode != PM_DISK_INVALID) { + pm_disk_mode = ops->pm_disk_mode; + } else + pm_disk_mode = PM_DISK_SHUTDOWN; mutex_unlock(&pm_mutex); } +/** + * pm_valid_only_mem - generic memory-only valid callback + * + * pm_ops drivers that implement mem suspend only and only need + * to check for that in their .valid callback can use this instead + * of rolling their own .valid callback. + */ +int pm_valid_only_mem(suspend_state_t state) +{ + return state == PM_SUSPEND_MEM; +} + + static inline void pm_finish(suspend_state_t state) { if (pm_ops->finish) @@ -111,13 +128,24 @@ static int suspend_prepare(suspend_state return error; } +/* default implementation */ +void __attribute__ ((weak)) arch_suspend_disable_irqs(void) +{ + local_irq_disable(); +} + +/* default implementation */ +void __attribute__ ((weak)) arch_suspend_enable_irqs(void) +{ + local_irq_enable(); +} int suspend_enter(suspend_state_t state) { int error = 0; - unsigned long flags; - local_irq_save(flags); + arch_suspend_disable_irqs(); + BUG_ON(!irqs_disabled()); if ((error = device_power_down(PMSG_SUSPEND))) { printk(KERN_ERR "Some devices failed to power down\n"); @@ -126,7 +154,8 @@ int suspend_enter(suspend_state_t state) error = pm_ops->enter(state); device_power_up(); Done: - local_irq_restore(flags); + arch_suspend_enable_irqs(); + BUG_ON(irqs_disabled()); return error; } @@ -169,8 +198,8 @@ static inline int valid_state(suspend_st /* all other states need lowlevel support and need to be * valid to the lowlevel implementation, no valid callback - * implies that all are valid. */ - if (!pm_ops || (pm_ops->valid && !pm_ops->valid(state))) + * implies that none are valid. */ + if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state)) return 0; return 1; } diff --git a/kernel/resource.c b/kernel/resource.c index bdb55a3..9bd14fd 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -213,27 +213,6 @@ int request_resource(struct resource *ro EXPORT_SYMBOL(request_resource); /** - * ____request_resource - reserve a resource, with resource conflict returned - * @root: root resource descriptor - * @new: resource descriptor desired by caller - * - * Returns: - * On success, NULL is returned. - * On error, a pointer to the conflicting resource is returned. - */ -struct resource *____request_resource(struct resource *root, struct resource *new) -{ - struct resource *conflict; - - write_lock(&resource_lock); - conflict = __request_resource(root, new); - write_unlock(&resource_lock); - return conflict; -} - -EXPORT_SYMBOL(____request_resource); - -/** * release_resource - release a previously reserved resource * @old: resource pointer */ diff --git a/kernel/sched.c b/kernel/sched.c index b9a6837..960d7c5 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4746,7 +4746,7 @@ #endif * console might take alot of time: */ touch_nmi_watchdog(); - if (p->state & state_filter) + if (!state_filter || (p->state & state_filter)) show_task(p); } while_each_thread(g, p); diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 4c3476f..ad7d239 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -102,7 +102,7 @@ static int prepare_reply(struct genl_inf */ static int send_reply(struct sk_buff *skb, pid_t pid) { - struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data); + struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); void *reply = genlmsg_data(genlhdr); int rc; @@ -121,7 +121,7 @@ static int send_reply(struct sk_buff *sk static void send_cpu_listeners(struct sk_buff *skb, struct listener_list *listeners) { - struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data); + struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); struct listener *s, *tmp; struct sk_buff *skb_next, *skb_cur = skb; void *reply = genlmsg_data(genlhdr); diff --git a/kernel/time.c b/kernel/time.c index 2f47888..ba18ec4 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -452,6 +452,7 @@ struct timespec ns_to_timespec(const s64 return ts; } +EXPORT_SYMBOL(ns_to_timespec); /** * ns_to_timeval - Convert nanoseconds to timeval @@ -469,6 +470,7 @@ struct timeval ns_to_timeval(const s64 n return tv; } +EXPORT_SYMBOL(ns_to_timeval); /* * Convert jiffies to milliseconds and back. diff --git a/kernel/timer.c b/kernel/timer.c index dd6c2c1..b22bd39 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -505,6 +505,8 @@ out: return ret; } +EXPORT_SYMBOL(try_to_del_timer_sync); + /** * del_timer_sync - deactivate a timer and wait for the handler to finish. * @timer: the timer to be deactivated diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 3f3e740..79afd00 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -261,7 +261,7 @@ config LOCKDEP bool depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT select STACKTRACE - select FRAME_POINTER if !X86 + select FRAME_POINTER if !X86 && !MIPS select KALLSYMS select KALLSYMS_ALL diff --git a/lib/Makefile b/lib/Makefile index 992a39e..ae57f35 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,7 +4,7 @@ # lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o dump_stack.o \ - idr.o div64.o int_sqrt.o bitmap.o extable.o prio_tree.o \ + idr.o int_sqrt.o bitmap.o extable.o prio_tree.o \ sha1.o irq_regs.o reciprocal_div.o lib-$(CONFIG_MMU) += ioremap.o @@ -12,7 +12,8 @@ lib-$(CONFIG_SMP) += cpumask.o lib-y += kobject.o kref.o kobject_uevent.o klist.o -obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o +obj-y += div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ + bust_spinlocks.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/devres.c b/lib/devres.c index eb38849..b1d336c 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -296,5 +296,31 @@ int pcim_iomap_regions(struct pci_dev *p return rc; } EXPORT_SYMBOL(pcim_iomap_regions); + +/** + * pcim_iounmap_regions - Unmap and release PCI BARs + * @pdev: PCI device to map IO resources for + * @mask: Mask of BARs to unmap and release + * + * Unamp and release regions specified by @mask. + */ +void pcim_iounmap_regions(struct pci_dev *pdev, u16 mask) +{ + void __iomem * const *iomap; + int i; + + iomap = pcim_iomap_table(pdev); + if (!iomap) + return; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + if (!(mask & (1 << i))) + continue; + + pcim_iounmap(pdev, iomap[i]); + pci_release_region(pdev, i); + } +} +EXPORT_SYMBOL(pcim_iounmap_regions); #endif #endif diff --git a/lib/div64.c b/lib/div64.c index 365719f..b71cf93 100644 --- a/lib/div64.c +++ b/lib/div64.c @@ -23,7 +23,7 @@ #include /* Not needed on 64bit architectures */ #if BITS_PER_LONG == 32 -uint32_t __div64_32(uint64_t *n, uint32_t base) +uint32_t __attribute__((weak)) __div64_32(uint64_t *n, uint32_t base) { uint64_t rem = *n; uint64_t b = base; @@ -58,4 +58,24 @@ uint32_t __div64_32(uint64_t *n, uint32_ EXPORT_SYMBOL(__div64_32); +/* 64bit divisor, dividend and result. dynamic precision */ +uint64_t div64_64(uint64_t dividend, uint64_t divisor) +{ + uint32_t high, d; + + high = divisor >> 32; + if (high) { + unsigned int shift = fls(high); + + d = divisor >> shift; + dividend >>= shift; + } else + d = divisor; + + do_div(dividend, d); + + return dividend; +} +EXPORT_SYMBOL(div64_64); + #endif /* BITS_PER_LONG == 32 */ diff --git a/lib/kobject.c b/lib/kobject.c index 057921c..cecf2fb 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -157,7 +157,7 @@ static void unlink(struct kobject * kobj } /** - * kobject_add - add an object to the hierarchy. + * kobject_shadow_add - add an object to the hierarchy. * @kobj: object. * @shadow_parent: sysfs directory to add to. */ @@ -174,6 +174,7 @@ int kobject_shadow_add(struct kobject * if (!*kobj->k_name) { pr_debug("kobject attempted to be registered with no name!\n"); WARN_ON(1); + kobject_put(kobj); return -EINVAL; } parent = kobject_get(kobj->parent); @@ -190,8 +191,8 @@ int kobject_shadow_add(struct kobject * list_add_tail(&kobj->entry,&kobj->kset->list); spin_unlock(&kobj->kset->list_lock); + kobj->parent = parent; } - kobj->parent = parent; error = create_dir(kobj, shadow_parent); if (error) { @@ -311,13 +312,43 @@ EXPORT_SYMBOL(kobject_set_name); int kobject_rename(struct kobject * kobj, const char *new_name) { int error = 0; + const char *devpath = NULL; + char *devpath_string = NULL; + char *envp[2]; kobj = kobject_get(kobj); if (!kobj) return -EINVAL; if (!kobj->parent) return -EINVAL; + + devpath = kobject_get_path(kobj, GFP_KERNEL); + if (!devpath) { + error = -ENOMEM; + goto out; + } + devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL); + if (!devpath_string) { + error = -ENOMEM; + goto out; + } + sprintf(devpath_string, "DEVPATH_OLD=%s", devpath); + envp[0] = devpath_string; + envp[1] = NULL; + /* Note : if we want to send the new name alone, not the full path, + * we could probably use kobject_name(kobj); */ + error = sysfs_rename_dir(kobj, kobj->parent->dentry, new_name); + + /* This function is mostly/only used for network interface. + * Some hotplug package track interfaces by their name and + * therefore want to know when the name is changed by the user. */ + if (!error) + kobject_uevent_env(kobj, KOBJ_MOVE, envp); + +out: + kfree(devpath_string); + kfree(devpath); kobject_put(kobj); return error; @@ -488,13 +519,15 @@ static struct kobj_type dir_ktype = { }; /** - * kobject_add_dir - add sub directory of object. + * kobject_kset_add_dir - add sub directory of object. + * @kset: kset the directory is belongs to. * @parent: object in which a directory is created. * @name: directory name. * * Add a plain directory object as child of given object. */ -struct kobject *kobject_add_dir(struct kobject *parent, const char *name) +struct kobject *kobject_kset_add_dir(struct kset *kset, + struct kobject *parent, const char *name) { struct kobject *k; int ret; @@ -506,13 +539,14 @@ struct kobject *kobject_add_dir(struct k if (!k) return NULL; + k->kset = kset; k->parent = parent; k->ktype = &dir_ktype; kobject_set_name(k, name); ret = kobject_register(k); if (ret < 0) { - printk(KERN_WARNING "kobject_add_dir: " - "kobject_register error: %d\n", ret); + printk(KERN_WARNING "%s: kobject_register error: %d\n", + __func__, ret); kobject_del(k); return NULL; } @@ -521,6 +555,18 @@ struct kobject *kobject_add_dir(struct k } /** + * kobject_add_dir - add sub directory of object. + * @parent: object in which a directory is created. + * @name: directory name. + * + * Add a plain directory object as child of given object. + */ +struct kobject *kobject_add_dir(struct kobject *parent, const char *name) +{ + return kobject_kset_add_dir(NULL, parent, name); +} + +/** * kset_init - initialize a kset for use * @k: kset */ @@ -613,7 +659,6 @@ struct kobject * kset_find_obj(struct ks void subsystem_init(struct subsystem * s) { - init_rwsem(&s->rwsem); kset_init(&s->kset); } @@ -622,8 +667,7 @@ void subsystem_init(struct subsystem * s * @s: the subsystem we're registering. * * Once we register the subsystem, we want to make sure that - * the kset points back to this subsystem for correct usage of - * the rwsem. + * the kset points back to this subsystem. */ int subsystem_register(struct subsystem * s) diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 84272ed..12e311d 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -42,10 +42,6 @@ static char *action_to_string(enum kobje return "remove"; case KOBJ_CHANGE: return "change"; - case KOBJ_MOUNT: - return "mount"; - case KOBJ_UMOUNT: - return "umount"; case KOBJ_OFFLINE: return "offline"; case KOBJ_ONLINE: @@ -95,10 +91,8 @@ int kobject_uevent_env(struct kobject *k /* search the kset we belong to */ top_kobj = kobj; - if (!top_kobj->kset && top_kobj->parent) { - do { - top_kobj = top_kobj->parent; - } while (!top_kobj->kset && top_kobj->parent); + while (!top_kobj->kset && top_kobj->parent) { + top_kobj = top_kobj->parent; } if (!top_kobj->kset) { pr_debug("kobject attempted to send uevent without kset!\n"); @@ -115,6 +109,16 @@ int kobject_uevent_env(struct kobject *k return 0; } + /* originating subsystem */ + if (uevent_ops && uevent_ops->name) + subsystem = uevent_ops->name(kset, kobj); + else + subsystem = kobject_name(&kset->kobj); + if (!subsystem) { + pr_debug("unset subsytem caused the event to drop!\n"); + return 0; + } + /* environment index */ envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); if (!envp) @@ -134,12 +138,6 @@ int kobject_uevent_env(struct kobject *k goto exit; } - /* originating subsystem */ - if (uevent_ops && uevent_ops->name) - subsystem = uevent_ops->name(kset, kobj); - else - subsystem = kobject_name(&kset->kobj); - /* event environemnt for helper process only */ envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; @@ -293,7 +291,7 @@ #if defined(CONFIG_NET) static int __init kobject_uevent_init(void) { uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, 1, NULL, - THIS_MODULE); + NULL, THIS_MODULE); if (!uevent_sock) { printk(KERN_ERR diff --git a/lib/kref.c b/lib/kref.c index 0d07cc3..a6dc3ec 100644 --- a/lib/kref.c +++ b/lib/kref.c @@ -21,6 +21,7 @@ #include void kref_init(struct kref *kref) { atomic_set(&kref->refcount,1); + smp_mb(); } /** @@ -31,6 +32,7 @@ void kref_get(struct kref *kref) { WARN_ON(!atomic_read(&kref->refcount)); atomic_inc(&kref->refcount); + smp_mb__after_atomic_inc(); } /** diff --git a/lib/string.c b/lib/string.c index bab440f..5efafed 100644 --- a/lib/string.c +++ b/lib/string.c @@ -60,6 +60,34 @@ int strnicmp(const char *s1, const char EXPORT_SYMBOL(strnicmp); #endif +#ifndef __HAVE_ARCH_STRCASECMP +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} +EXPORT_SYMBOL(strcasecmp); +#endif + +#ifndef __HAVE_ARCH_STRNCASECMP +int strncasecmp(const char *s1, const char *s2, size_t n) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while ((--n > 0) && c1 == c2 && c1 != 0); + return c1 - c2; +} +EXPORT_SYMBOL(strncasecmp); +#endif + #ifndef __HAVE_ARCH_STRCPY /** * strcpy - Copy a %NUL terminated string diff --git a/lib/vsprintf.c b/lib/vsprintf.c index b025864..cbab1df 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -851,23 +851,35 @@ EXPORT_SYMBOL(sscanf); /* Simplified asprintf. */ -char *kasprintf(gfp_t gfp, const char *fmt, ...) +char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap) { - va_list ap; unsigned int len; char *p; + va_list aq; - va_start(ap, fmt); - len = vsnprintf(NULL, 0, fmt, ap); - va_end(ap); + va_copy(aq, ap); + len = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); p = kmalloc(len+1, gfp); if (!p) return NULL; - va_start(ap, fmt); + vsnprintf(p, len+1, fmt, ap); - va_end(ap); + return p; } +EXPORT_SYMBOL(kvasprintf); + +char *kasprintf(gfp_t gfp, const char *fmt, ...) +{ + va_list ap; + char *p; + va_start(ap, fmt); + p = kvasprintf(gfp, fmt, ap); + va_end(ap); + + return p; +} EXPORT_SYMBOL(kasprintf); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index f469e3c..a794945 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -67,12 +67,12 @@ static inline long sync_writeback_pages( /* * Start background writeback (via pdflush) at this percentage */ -int dirty_background_ratio = 10; +int dirty_background_ratio = 5; /* * The generator of dirty data starts writeback at this percentage */ -int vm_dirty_ratio = 40; +int vm_dirty_ratio = 10; /* * The interval between `kupdate'-style writebacks, in jiffies diff --git a/mm/rmap.c b/mm/rmap.c index b82146e..59da5b7 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -498,8 +498,10 @@ int page_mkclean(struct page *page) struct address_space *mapping = page_mapping(page); if (mapping) ret = page_mkclean_file(mapping, page); - if (page_test_and_clear_dirty(page)) + if (page_test_dirty(page)) { + page_clear_dirty(page); ret = 1; + } } return ret; @@ -605,8 +607,10 @@ void page_remove_rmap(struct page *page, * Leaving it set also helps swapoff to reinstate ptes * faster for those pages still in swapcache. */ - if (page_test_and_clear_dirty(page)) + if (page_test_dirty(page)) { + page_clear_dirty(page); set_page_dirty(page); + } __dec_zone_page_state(page, PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED); } diff --git a/net/802/fddi.c b/net/802/fddi.c index ace6386..91dde41 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -100,7 +100,7 @@ static int fddi_rebuild_header(struct sk struct fddihdr *fddi = (struct fddihdr *)skb->data; #ifdef CONFIG_INET - if (fddi->hdr.llc_snap.ethertype == __constant_htons(ETH_P_IP)) + if (fddi->hdr.llc_snap.ethertype == htons(ETH_P_IP)) /* Try to get ARP to resolve the header and fill destination address */ return arp_find(fddi->daddr, skb); else @@ -130,12 +130,13 @@ __be16 fddi_type_trans(struct sk_buff *s * to start of packet data. Assume 802.2 SNAP frames for now. */ - skb->mac.raw = skb->data; /* point to frame control (FC) */ + skb->dev = dev; + skb_reset_mac_header(skb); /* point to frame control (FC) */ if(fddi->hdr.llc_8022_1.dsap==0xe0) { skb_pull(skb, FDDI_K_8022_HLEN-3); - type = __constant_htons(ETH_P_802_2); + type = htons(ETH_P_802_2); } else { diff --git a/net/802/hippi.c b/net/802/hippi.c index 578f2a3..87ffc12 100644 --- a/net/802/hippi.c +++ b/net/802/hippi.c @@ -60,7 +60,7 @@ static int hippi_header(struct sk_buff * * Due to the stupidity of the little endian byte-order we * have to set the fp field this way. */ - hip->fp.fixed = __constant_htonl(0x04800018); + hip->fp.fixed = htonl(0x04800018); hip->fp.d2_size = htonl(len + 8); hip->le.fc = 0; hip->le.double_wide = 0; /* only HIPPI 800 for the time being */ @@ -104,7 +104,7 @@ static int hippi_rebuild_header(struct s * Only IP is currently supported */ - if(hip->snap.ethertype != __constant_htons(ETH_P_IP)) + if(hip->snap.ethertype != htons(ETH_P_IP)) { printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",skb->dev->name,ntohs(hip->snap.ethertype)); return 0; @@ -126,14 +126,14 @@ __be16 hippi_type_trans(struct sk_buff * { struct hippi_hdr *hip; - hip = (struct hippi_hdr *) skb->data; - /* * This is actually wrong ... question is if we really should * set the raw address here. */ - skb->mac.raw = skb->data; - skb_pull(skb, HIPPI_HLEN); + skb->dev = dev; + skb_reset_mac_header(skb); + hip = (struct hippi_hdr *)skb_mac_header(skb); + skb_pull(skb, HIPPI_HLEN); /* * No fancy promisc stuff here now. diff --git a/net/802/psnap.c b/net/802/psnap.c index 6e7c212..04ee43e 100644 --- a/net/802/psnap.c +++ b/net/802/psnap.c @@ -56,10 +56,10 @@ static int snap_rcv(struct sk_buff *skb, }; rcu_read_lock(); - proto = find_snap_client(skb->h.raw); + proto = find_snap_client(skb_transport_header(skb)); if (proto) { /* Pass the frame on. */ - skb->h.raw += 5; + skb->transport_header += 5; skb_pull_rcsum(skb, 5); rc = proto->rcvfunc(skb, dev, &snap_packet_type, orig_dev); } else { diff --git a/net/802/tr.c b/net/802/tr.c index 96bd144..0ba1946 100644 --- a/net/802/tr.c +++ b/net/802/tr.c @@ -189,11 +189,13 @@ #endif __be16 tr_type_trans(struct sk_buff *skb, struct net_device *dev) { - struct trh_hdr *trh=(struct trh_hdr *)skb->data; + struct trh_hdr *trh; struct trllc *trllc; unsigned riflen=0; - skb->mac.raw = skb->data; + skb->dev = dev; + skb_reset_mac_header(skb); + trh = tr_hdr(skb); if(trh->saddr[0] & TR_RII) riflen = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; @@ -552,7 +554,8 @@ static int rif_seq_show(struct seq_file if(j==1) { segment=ntohs(entry->rseg[j-1])>>4; seq_printf(seq," %03X",segment); - }; + } + segment=ntohs(entry->rseg[j])>>4; brdgnmb=ntohs(entry->rseg[j-1])&0x00f; seq_printf(seq,"-%01X-%03X",brdgnmb,segment); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index eb1c71e..c0c7bb8 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -470,7 +470,7 @@ #endif */ default: snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID); - }; + } new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name, vlan_setup); @@ -685,7 +685,7 @@ static int vlan_device_event(struct noti break; } break; - }; + } out: return NOTIFY_DONE; @@ -819,7 +819,7 @@ #endif printk(VLAN_DBG "%s: Unknown VLAN CMD: %x \n", __FUNCTION__, args.cmd); return -EINVAL; - }; + } out: return err; } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index b6e0eea..ec46084 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -66,7 +66,7 @@ #endif memcpy(veth->h_source, dev->dev_addr, ETH_ALEN); break; - }; + } return 0; } @@ -83,7 +83,7 @@ static inline struct sk_buff *vlan_check /* Lifted from Gleb's VLAN code... */ memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 12); - skb->mac.raw += VLAN_HLEN; + skb->mac_header += VLAN_HLEN; } } @@ -219,7 +219,7 @@ #endif break; default: break; - }; + } /* Was a VLAN packet, grab the encapsulated protocol, which the layer * three protocols care about. @@ -258,7 +258,7 @@ #endif * won't work for fault tolerant netware but does for the rest. */ if (*(unsigned short *)rawp == 0xFFFF) { - skb->protocol = __constant_htons(ETH_P_802_3); + skb->protocol = htons(ETH_P_802_3); /* place it back on the queue to be handled by true layer 3 protocols. */ @@ -281,7 +281,7 @@ #endif /* * Real 802.2 LLC */ - skb->protocol = __constant_htons(ETH_P_802_2); + skb->protocol = htons(ETH_P_802_2); /* place it back on the queue to be handled by upper layer protocols. */ @@ -382,7 +382,7 @@ #endif } skb->protocol = htons(ETH_P_8021Q); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); } /* Before delegating work to the lower layer, enter our MAC-address */ @@ -448,7 +448,7 @@ int vlan_dev_hard_start_xmit(struct sk_b * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs... */ - if (veth->h_vlan_proto != __constant_htons(ETH_P_8021Q)) { + if (veth->h_vlan_proto != htons(ETH_P_8021Q)) { int orig_headroom = skb_headroom(skb); unsigned short veth_TCI; diff --git a/net/Kconfig b/net/Kconfig index 9156578..2fc8e77 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -27,13 +27,6 @@ if NET menu "Networking options" -config NETDEBUG - bool "Network packet debugging" - help - You can say Y here if you want to get additional messages useful in - debugging bad packets, but can overwhelm logs under denial of service - attacks. - source "net/packet/Kconfig" source "net/unix/Kconfig" source "net/xfrm/Kconfig" @@ -219,14 +212,18 @@ endmenu source "net/ax25/Kconfig" source "net/irda/Kconfig" source "net/bluetooth/Kconfig" -source "net/ieee80211/Kconfig" - -config WIRELESS_EXT - bool +source "net/rxrpc/Kconfig" config FIB_RULES bool +menu "Wireless" + +source "net/wireless/Kconfig" +source "net/ieee80211/Kconfig" + +endmenu + endif # if NET endmenu # Networking diff --git a/net/Makefile b/net/Makefile index 4854ac5..6b74d41 100644 --- a/net/Makefile +++ b/net/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_IRDA) += irda/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_RXRPC) += rxrpc/ +obj-$(CONFIG_AF_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ @@ -52,3 +53,5 @@ obj-$(CONFIG_IUCV) += iucv/ ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o endif + +obj-y += wireless/ diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index d89d62f..5ef6a23 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -118,7 +118,9 @@ static void __aarp_send_query(struct aar /* Set up the buffer */ skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb->nh.raw = skb->h.raw = skb_put(skb, sizeof(*eah)); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); skb->protocol = htons(ETH_P_ATALK); skb->dev = dev; eah = aarp_hdr(skb); @@ -163,7 +165,9 @@ static void aarp_send_reply(struct net_d /* Set up the buffer */ skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb->nh.raw = skb->h.raw = skb_put(skb, sizeof(*eah)); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); skb->protocol = htons(ETH_P_ATALK); skb->dev = dev; eah = aarp_hdr(skb); @@ -212,7 +216,9 @@ static void aarp_send_probe(struct net_d /* Set up the buffer */ skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb->nh.raw = skb->h.raw = skb_put(skb, sizeof(*eah)); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); skb->protocol = htons(ETH_P_ATALK); skb->dev = dev; eah = aarp_hdr(skb); @@ -539,7 +545,7 @@ int aarp_send_ddp(struct net_device *dev int hash; struct aarp_entry *a; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); /* Check for LocalTalk first */ if (dev->type == ARPHRD_LOCALTLK) { diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index c8b7dc2..f6a92a0 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1275,7 +1275,7 @@ static int handle_ip_over_ddp(struct sk_ skb->protocol = htons(ETH_P_IP); skb_pull(skb, 13); skb->dev = dev; - skb->h.raw = skb->data; + skb_reset_transport_header(skb); stats = dev->priv; stats->rx_packets++; @@ -1383,10 +1383,10 @@ free_it: * @pt - packet type * * Receive a packet (in skb) from device dev. This has come from the SNAP - * decoder, and on entry skb->h.raw is the DDP header, skb->len is the DDP - * header, skb->len is the DDP length. The physical headers have been - * extracted. PPP should probably pass frames marked as for this layer. - * [ie ARPHRD_ETHERTALK] + * decoder, and on entry skb->transport_header is the DDP header, skb->len + * is the DDP header, skb->len is the DDP length. The physical headers + * have been extracted. PPP should probably pass frames marked as for this + * layer. [ie ARPHRD_ETHERTALK] */ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) @@ -1484,7 +1484,7 @@ static int ltalk_rcv(struct sk_buff *skb struct packet_type *pt, struct net_device *orig_dev) { /* Expand any short form frames */ - if (skb->mac.raw[2] == 1) { + if (skb_mac_header(skb)[2] == 1) { struct ddpehdr *ddp; /* Find our address */ struct atalk_addr *ap = atalk_find_dev_addr(dev); @@ -1510,8 +1510,8 @@ static int ltalk_rcv(struct sk_buff *skb * we write the network numbers ! */ - ddp->deh_dnode = skb->mac.raw[0]; /* From physical header */ - ddp->deh_snode = skb->mac.raw[1]; /* From physical header */ + ddp->deh_dnode = skb_mac_header(skb)[0]; /* From physical header */ + ddp->deh_snode = skb_mac_header(skb)[1]; /* From physical header */ ddp->deh_dnet = ap->s_net; /* Network number */ ddp->deh_snet = ap->s_net; @@ -1522,7 +1522,7 @@ static int ltalk_rcv(struct sk_buff *skb /* Non routable, so force a drop if we slip up later */ ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); return atalk_rcv(skb, dev, pt, orig_dev); freeit: @@ -1771,6 +1771,9 @@ static int atalk_ioctl(struct socket *so case SIOCGSTAMP: rc = sock_get_timestamp(sk, argp); break; + case SIOCGSTAMPNS: + rc = sock_get_timestampns(sk, argp); + break; /* Routing */ case SIOCADDRT: case SIOCDELRT: diff --git a/net/atm/br2684.c b/net/atm/br2684.c index ec4ebd3..0e9f00c 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -173,7 +173,7 @@ #else } skb_push(skb, minheadroom); if (brvcc->encaps == e_llc) - memcpy(skb->data, llc_oui_pid_pad, 10); + skb_copy_to_linear_data(skb, llc_oui_pid_pad, 10); else memset(skb->data, 0, 2); #endif /* FASTER_VERSION */ @@ -375,11 +375,11 @@ packet_fails_filter(__be16 type, struct { if (brvcc->filter.netmask == 0) return 0; /* no filter in place */ - if (type == __constant_htons(ETH_P_IP) && + if (type == htons(ETH_P_IP) && (((struct iphdr *) (skb->data))->daddr & brvcc->filter. netmask) == brvcc->filter.prefix) return 0; - if (type == __constant_htons(ETH_P_ARP)) + if (type == htons(ETH_P_ARP)) return 0; /* TODO: we should probably filter ARPs too.. don't want to have * them returning values that don't make sense, or is that ok? @@ -458,7 +458,7 @@ #ifdef FASTER_VERSION /* FIXME: tcpdump shows that pointer to mac header is 2 bytes earlier, than should be. What else should I set? */ skb_pull(skb, plen); - skb->mac.raw = ((char *) (skb->data)) - ETH_HLEN; + skb_set_mac_header(skb, -ETH_HLEN); skb->pkt_type = PACKET_HOST; #ifdef CONFIG_BR2684_FAST_TRANS skb->protocol = ((u16 *) skb->data)[-1]; diff --git a/net/atm/clip.c b/net/atm/clip.c index 8c38258..876b77f 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -213,7 +213,7 @@ static void clip_push(struct atm_vcc *vc return; } ATM_SKB(skb)->vcc = vcc; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); if (!clip_vcc->encap || skb->len < RFC1483LLC_LEN || memcmp(skb->data, llc_oui, sizeof (llc_oui))) @@ -702,7 +702,7 @@ static struct atm_dev atmarpd_dev = { .ops = &atmarpd_dev_ops, .type = "arpd", .number = 999, - .lock = SPIN_LOCK_UNLOCKED + .lock = __SPIN_LOCK_UNLOCKED(atmarpd_dev.lock) }; diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c index 8ccee45..7afd8e7 100644 --- a/net/atm/ioctl.c +++ b/net/atm/ioctl.c @@ -82,6 +82,9 @@ int vcc_ioctl(struct socket *sock, unsig case SIOCGSTAMP: /* borrowed from IP */ error = sock_get_timestamp(sk, argp); goto done; + case SIOCGSTAMPNS: /* borrowed from IP */ + error = sock_get_timestampns(sk, argp); + goto done; case ATM_SETSC: printk(KERN_WARNING "ATM_SETSC is obsolete\n"); error = 0; diff --git a/net/atm/lec.c b/net/atm/lec.c index 3d804d6..4dc5f2b 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -283,8 +283,8 @@ #endif /* DUMP_PACKETS >0 */ } DPRINTK("skbuff head:%lx data:%lx tail:%lx end:%lx\n", - (long)skb->head, (long)skb->data, (long)skb->tail, - (long)skb->end); + (long)skb->head, (long)skb->data, (long)skb_tail_pointer(skb), + (long)skb_end_pointer(skb)); #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) if (memcmp(skb->data, bridge_ula_lec, sizeof(bridge_ula_lec)) == 0) lec_handle_bridge(skb, dev); @@ -576,8 +576,8 @@ #if defined(CONFIG_BRIDGE) || defined(CO break; } skb2->len = sizeof(struct atmlec_msg); - memcpy(skb2->data, mesg, - sizeof(struct atmlec_msg)); + skb_copy_to_linear_data(skb2, mesg, + sizeof(*mesg)); atm_force_charge(priv->lecd, skb2->truesize); sk = sk_atm(priv->lecd); skb_queue_tail(&sk->sk_receive_queue, skb2); @@ -630,7 +630,7 @@ static struct atm_dev lecatm_dev = { .ops = &lecdev_ops, .type = "lec", .number = 999, /* dummy device number */ - .lock = SPIN_LOCK_UNLOCKED + .lock = __SPIN_LOCK_UNLOCKED(lecatm_dev.lock) }; /* @@ -825,7 +825,6 @@ #endif if (!hlist_empty(&priv->lec_arp_empty_ones)) { lec_arp_check_empties(priv, vcc, skb); } - skb->dev = dev; skb_pull(skb, 2); /* skip lec_id */ #ifdef CONFIG_TR if (priv->is_trdev) @@ -1338,7 +1337,7 @@ static int lane2_resolve(struct net_devi if (skb == NULL) return -1; skb->len = *sizeoftlvs; - memcpy(skb->data, *tlvs, *sizeoftlvs); + skb_copy_to_linear_data(skb, *tlvs, *sizeoftlvs); retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb); } return retval; @@ -1372,7 +1371,7 @@ static int lane2_associate_req(struct ne if (skb == NULL) return 0; skb->len = sizeoftlvs; - memcpy(skb->data, tlvs, sizeoftlvs); + skb_copy_to_linear_data(skb, tlvs, sizeoftlvs); retval = send_to_lecd(priv, l_associate_req, NULL, NULL, skb); if (retval != 0) printk("lec.c: lane2_associate_req() failed\n"); diff --git a/net/atm/mpc.c b/net/atm/mpc.c index cb3c004..7c85aa5 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -504,11 +504,13 @@ static int send_via_shortcut(struct sk_b tagged_llc_snap_hdr.tag = entry->ctrl_info.tag; skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ skb_push(skb, sizeof(tagged_llc_snap_hdr)); /* add LLC/SNAP header */ - memcpy(skb->data, &tagged_llc_snap_hdr, sizeof(tagged_llc_snap_hdr)); + skb_copy_to_linear_data(skb, &tagged_llc_snap_hdr, + sizeof(tagged_llc_snap_hdr)); } else { skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ skb_push(skb, sizeof(struct llc_snap_hdr)); /* add LLC/SNAP header + tag */ - memcpy(skb->data, &llc_snap_mpoa_data, sizeof(struct llc_snap_hdr)); + skb_copy_to_linear_data(skb, &llc_snap_mpoa_data, + sizeof(struct llc_snap_hdr)); } atomic_add(skb->truesize, &sk_atm(entry->shortcut)->sk_wmem_alloc); @@ -711,11 +713,12 @@ static void mpc_push(struct atm_vcc *vcc return; } skb_push(new_skb, eg->ctrl_info.DH_length); /* add MAC header */ - memcpy(new_skb->data, eg->ctrl_info.DLL_header, eg->ctrl_info.DH_length); + skb_copy_to_linear_data(new_skb, eg->ctrl_info.DLL_header, + eg->ctrl_info.DH_length); new_skb->protocol = eth_type_trans(new_skb, dev); - new_skb->nh.raw = new_skb->data; + skb_reset_network_header(new_skb); - eg->latest_ip_addr = new_skb->nh.iph->saddr; + eg->latest_ip_addr = ip_hdr(new_skb)->saddr; eg->packets_rcvd++; mpc->eg_ops->put(eg); @@ -734,7 +737,7 @@ static struct atm_dev mpc_dev = { .ops = &mpc_ops, .type = "mpc", .number = 42, - .lock = SPIN_LOCK_UNLOCKED + .lock = __SPIN_LOCK_UNLOCKED(mpc_dev.lock) /* members not explicitly initialised will be 0 */ }; @@ -936,7 +939,7 @@ int msg_to_mpoad(struct k_message *mesg, if (skb == NULL) return -ENOMEM; skb_put(skb, sizeof(struct k_message)); - memcpy(skb->data, mesg, sizeof(struct k_message)); + skb_copy_to_linear_data(skb, mesg, sizeof(*mesg)); atm_force_charge(mpc->mpoad_vcc, skb->truesize); sk = sk_atm(mpc->mpoad_vcc); diff --git a/net/atm/signaling.c b/net/atm/signaling.c index 31d98b5..d14baaf 100644 --- a/net/atm/signaling.c +++ b/net/atm/signaling.c @@ -256,7 +256,7 @@ static struct atm_dev sigd_dev = { .ops = &sigd_dev_ops, .type = "sig", .number = 999, - .lock = SPIN_LOCK_UNLOCKED + .lock = __SPIN_LOCK_UNLOCKED(sigd_dev.lock) }; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 1c07c6a..6ded952 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1127,22 +1127,22 @@ static int __must_check ax25_connect(str switch (sk->sk_state) { case TCP_SYN_SENT: /* still trying */ err = -EINPROGRESS; - goto out; + goto out_release; case TCP_ESTABLISHED: /* connection established */ sock->state = SS_CONNECTED; - goto out; + goto out_release; case TCP_CLOSE: /* connection refused */ sock->state = SS_UNCONNECTED; err = -ECONNREFUSED; - goto out; + goto out_release; } } if (sk->sk_state == TCP_ESTABLISHED && sk->sk_type == SOCK_SEQPACKET) { err = -EISCONN; /* No reconnect on a seqpacket socket */ - goto out; + goto out_release; } sk->sk_state = TCP_CLOSE; @@ -1159,12 +1159,12 @@ static int __must_check ax25_connect(str /* Valid number of digipeaters ? */ if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) { err = -EINVAL; - goto out; + goto out_release; } if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { err = -ENOBUFS; - goto out; + goto out_release; } digi->ndigi = fsa->fsa_ax25.sax25_ndigis; @@ -1194,7 +1194,7 @@ static int __must_check ax25_connect(str current->comm); if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0) { kfree(digi); - goto out; + goto out_release; } ax25_fillin_cb(ax25, ax25->ax25_dev); @@ -1203,7 +1203,7 @@ static int __must_check ax25_connect(str if (ax25->ax25_dev == NULL) { kfree(digi); err = -EHOSTUNREACH; - goto out; + goto out_release; } } @@ -1213,7 +1213,7 @@ static int __must_check ax25_connect(str kfree(digi); err = -EADDRINUSE; /* Already such a connection */ ax25_cb_put(ax25t); - goto out; + goto out_release; } ax25->dest_addr = fsa->fsa_ax25.sax25_call; @@ -1223,7 +1223,7 @@ static int __must_check ax25_connect(str if (sk->sk_type != SOCK_SEQPACKET) { sock->state = SS_CONNECTED; sk->sk_state = TCP_ESTABLISHED; - goto out; + goto out_release; } /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ @@ -1255,55 +1255,53 @@ #endif /* Now the loop */ if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { err = -EINPROGRESS; - goto out; + goto out_release; } if (sk->sk_state == TCP_SYN_SENT) { - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); + DEFINE_WAIT(wait); - add_wait_queue(sk->sk_sleep, &wait); for (;;) { + prepare_to_wait(sk->sk_sleep, &wait, + TASK_INTERRUPTIBLE); if (sk->sk_state != TCP_SYN_SENT) break; - set_current_state(TASK_INTERRUPTIBLE); - release_sock(sk); - if (!signal_pending(tsk)) { + if (!signal_pending(current)) { + release_sock(sk); schedule(); lock_sock(sk); continue; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -ERESTARTSYS; + err = -ERESTARTSYS; + break; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); + finish_wait(sk->sk_sleep, &wait); + + if (err) + goto out_release; } if (sk->sk_state != TCP_ESTABLISHED) { /* Not in ABM, not in WAIT_UA -> failed */ sock->state = SS_UNCONNECTED; err = sock_error(sk); /* Always set at this point */ - goto out; + goto out_release; } sock->state = SS_CONNECTED; - err=0; -out: + err = 0; +out_release: release_sock(sk); return err; } - static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) { - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); struct sk_buff *skb; struct sock *newsk; + DEFINE_WAIT(wait); struct sock *sk; int err = 0; @@ -1328,30 +1326,29 @@ static int ax25_accept(struct socket *so * The read queue this time is holding sockets ready to use * hooked into the SABM we saved */ - add_wait_queue(sk->sk_sleep, &wait); for (;;) { + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); skb = skb_dequeue(&sk->sk_receive_queue); if (skb) break; - release_sock(sk); - current->state = TASK_INTERRUPTIBLE; if (flags & O_NONBLOCK) { - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -EWOULDBLOCK; + err = -EWOULDBLOCK; + break; } - if (!signal_pending(tsk)) { + if (!signal_pending(current)) { + release_sock(sk); schedule(); lock_sock(sk); continue; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -ERESTARTSYS; + err = -ERESTARTSYS; + break; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); + finish_wait(sk->sk_sleep, &wait); + + if (err) + goto out; newsk = skb->sk; newsk->sk_socket = newsock; @@ -1425,7 +1422,6 @@ static int ax25_sendmsg(struct kiocb *io struct sockaddr_ax25 sax; struct sk_buff *skb; ax25_digi dtmp, *dp; - unsigned char *asmptr; ax25_cb *ax25; size_t size; int lv, err, addr_len = msg->msg_namelen; @@ -1548,13 +1544,11 @@ static int ax25_sendmsg(struct kiocb *io goto out; } - skb->nh.raw = skb->data; + skb_reset_network_header(skb); /* Add the PID if one is not supplied by the user in the skb */ - if (!ax25->pidincl) { - asmptr = skb_push(skb, 1); - *asmptr = sk->sk_protocol; - } + if (!ax25->pidincl) + *skb_push(skb, 1) = sk->sk_protocol; SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n"); @@ -1573,7 +1567,7 @@ static int ax25_sendmsg(struct kiocb *io goto out; } - asmptr = skb_push(skb, 1 + ax25_addr_size(dp)); + skb_push(skb, 1 + ax25_addr_size(dp)); SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp); @@ -1581,17 +1575,17 @@ static int ax25_sendmsg(struct kiocb *io SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi); /* Build an AX.25 header */ - asmptr += (lv = ax25_addr_build(asmptr, &ax25->source_addr, - &sax.sax25_call, dp, - AX25_COMMAND, AX25_MODULUS)); + lv = ax25_addr_build(skb->data, &ax25->source_addr, &sax.sax25_call, + dp, AX25_COMMAND, AX25_MODULUS); SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); - skb->h.raw = asmptr; + skb_set_transport_header(skb, lv); - SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); + SOCK_DEBUG(sk, "base=%p pos=%p\n", + skb->data, skb_transport_header(skb)); - *asmptr = AX25_UI; + *skb_transport_header(skb) = AX25_UI; /* Datagram frames go straight out of the door as UI */ ax25_queue_xmit(skb, ax25->ax25_dev->dev); @@ -1631,8 +1625,8 @@ static int ax25_recvmsg(struct kiocb *io if (!ax25_sk(sk)->pidincl) skb_pull(skb, 1); /* Remove PID */ - skb->h.raw = skb->data; - copied = skb->len; + skb_reset_transport_header(skb); + copied = skb->len; if (copied > size) { copied = size; @@ -1645,9 +1639,10 @@ static int ax25_recvmsg(struct kiocb *io struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name; ax25_digi digi; ax25_address src; + const unsigned char *mac = skb_mac_header(skb); - ax25_addr_parse(skb->mac.raw+1, skb->data-skb->mac.raw-1, &src, NULL, &digi, NULL, NULL); - + ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL, + &digi, NULL, NULL); sax->sax25_family = AF_AX25; /* We set this correctly, even though we may not let the application know the digi calls further down (because it @@ -1711,6 +1706,10 @@ static int ax25_ioctl(struct socket *soc res = sock_get_timestamp(sk, argp); break; + case SIOCGSTAMPNS: + res = sock_get_timestampns(sk, argp); + break; + case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ case SIOCAX25GETUID: { diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c index 9569dd3..a49773f 100644 --- a/net/ax25/ax25_ds_subr.c +++ b/net/ax25/ax25_ds_subr.c @@ -136,7 +136,7 @@ static void ax25_kiss_cmd(ax25_dev *ax25 if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) return; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); p = skb_put(skb, 2); *p++ = cmd; diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index 4a6b26b..0ddaff0 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -61,12 +61,14 @@ static int ax25_rx_fragment(ax25_cb *ax2 skb_reserve(skbn, AX25_MAX_HEADER_LEN); skbn->dev = ax25->ax25_dev->dev; - skbn->h.raw = skbn->data; - skbn->nh.raw = skbn->data; + skb_reset_network_header(skbn); + skb_reset_transport_header(skbn); /* Copy data from the fragments */ while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) { - memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + skb_copy_from_linear_data(skbo, + skb_put(skbn, skbo->len), + skbo->len); kfree_skb(skbo); } @@ -122,8 +124,8 @@ int ax25_rx_iframe(ax25_cb *ax25, struct } skb_pull(skb, 1); /* Remove PID */ - skb->mac.raw = skb->nh.raw; - skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); skb->dev = ax25->ax25_dev->dev; skb->pkt_type = PACKET_HOST; skb->protocol = htons(ETH_P_IP); @@ -196,7 +198,7 @@ static int ax25_rcv(struct sk_buff *skb, * Process the AX.25/LAPB frame. */ - skb->h.raw = skb->data; + skb_reset_transport_header(skb); if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { kfree_skb(skb); @@ -233,7 +235,7 @@ static int ax25_rcv(struct sk_buff *skb, /* UI frame - bypass LAPB processing */ if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) { - skb->h.raw = skb->data + 2; /* skip control and pid */ + skb_set_transport_header(skb, 2); /* skip control and pid */ ax25_send_to_raw(&dest, skb, skb->data[1]); @@ -246,8 +248,8 @@ static int ax25_rcv(struct sk_buff *skb, switch (skb->data[1]) { case AX25_P_IP: skb_pull(skb,2); /* drop PID/CTRL */ - skb->h.raw = skb->data; - skb->nh.raw = skb->data; + skb_reset_transport_header(skb); + skb_reset_network_header(skb); skb->dev = dev; skb->pkt_type = PACKET_HOST; skb->protocol = htons(ETH_P_IP); @@ -256,8 +258,8 @@ static int ax25_rcv(struct sk_buff *skb, case AX25_P_ARP: skb_pull(skb,2); - skb->h.raw = skb->data; - skb->nh.raw = skb->data; + skb_reset_transport_header(skb); + skb_reset_network_header(skb); skb->dev = dev; skb->pkt_type = PACKET_HOST; skb->protocol = htons(ETH_P_ARP); diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index 7f818bb..930e491 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -121,7 +121,7 @@ int ax25_rebuild_header(struct sk_buff * digipeat = route->digipeat; dev = route->dev; ip_mode = route->ip_mode; - }; + } if (dev == NULL) dev = skb->dev; @@ -171,7 +171,7 @@ int ax25_rebuild_header(struct sk_buff * src_c = *(ax25_address *)(bp + 8); skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ - ourskb->nh.raw = ourskb->data; + skb_reset_network_header(ourskb); ax25=ax25_send_frame( ourskb, diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index 2238350..92b517a 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -148,8 +148,9 @@ void ax25_output(ax25_cb *ax25, int pacl if (ka9qfrag == 1) { skb_reserve(skbn, frontlen + 2); - skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data); - memcpy(skb_put(skbn, len), skb->data, len); + skb_set_network_header(skbn, + skb_network_offset(skb)); + skb_copy_from_linear_data(skb, skb_put(skbn, len), len); p = skb_push(skbn, 2); *p++ = AX25_P_SEGMENT; @@ -161,8 +162,9 @@ void ax25_output(ax25_cb *ax25, int pacl } } else { skb_reserve(skbn, frontlen + 1); - skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data); - memcpy(skb_put(skbn, len), skb->data, len); + skb_set_network_header(skbn, + skb_network_offset(skb)); + skb_copy_from_linear_data(skb, skb_put(skbn, len), len); p = skb_push(skbn, 1); *p = AX25_P_TEXT; } @@ -205,7 +207,7 @@ static void ax25_send_iframe(ax25_cb *ax if (skb == NULL) return; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); if (ax25->modulus == AX25_MODULUS) { frame = skb_push(skb, 1); diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index b6c577e..5fe9b2a 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -162,7 +162,7 @@ void ax25_send_control(ax25_cb *ax25, in skb_reserve(skb, ax25->ax25_dev->dev->hard_header_len); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); /* Assume a response - address structure for DTE */ if (ax25->modulus == AX25_MODULUS) { @@ -205,7 +205,7 @@ void ax25_return_dm(struct net_device *d return; /* Next SABM will get DM'd */ skb_reserve(skb, dev->hard_header_len); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); ax25_digi_invert(digi, &retdigi); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index c7228cf..d942b94 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -221,7 +221,7 @@ int bt_sock_recvmsg(struct kiocb *iocb, copied = len; } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); skb_free_datagram(sk, skb); diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index b85d149..ab2db55 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -326,7 +326,7 @@ static inline int bnep_rx_frame(struct b return 0; } - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); /* Verify and pull out header */ if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) @@ -364,26 +364,28 @@ static inline int bnep_rx_frame(struct b case BNEP_COMPRESSED_SRC_ONLY: memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN); - memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN); + memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN); put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); break; case BNEP_COMPRESSED_DST_ONLY: - memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN); - memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2); + memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), + ETH_ALEN); + memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, + ETH_ALEN + 2); break; case BNEP_GENERAL: - memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2); + memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb), + ETH_ALEN * 2); put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); break; } - memcpy(__skb_put(nskb, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len); kfree_skb(skb); s->stats.rx_packets++; - nskb->dev = dev; nskb->ip_summed = CHECKSUM_NONE; nskb->protocol = eth_type_trans(nskb, dev); netif_rx_ni(nskb); diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 3933608..66bef1c 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -124,7 +124,7 @@ static inline void cmtp_add_msgpart(stru } if (skb && (skb->len > 0)) - memcpy(skb_put(nskb, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len); memcpy(skb_put(nskb, count), buf, count); @@ -256,7 +256,7 @@ static void cmtp_process_transmit(struct hdr[2] = size >> 8; } - memcpy(skb_put(nskb, size), skb->data, size); + skb_copy_from_linear_data(skb, skb_put(nskb, size), size); skb_pull(skb, size); if (skb->len > 0) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index f3403fd..63980bd 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -72,11 +72,11 @@ void hci_acl_connect(struct hci_conn *co inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { cp.pscan_rep_mode = ie->data.pscan_rep_mode; cp.pscan_mode = ie->data.pscan_mode; - cp.clock_offset = ie->data.clock_offset | __cpu_to_le16(0x8000); + cp.clock_offset = ie->data.clock_offset | cpu_to_le16(0x8000); memcpy(conn->dev_class, ie->data.dev_class, 3); } - cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK); + cp.pkt_type = cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK); if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) cp.role_switch = 0x01; else @@ -107,7 +107,7 @@ void hci_acl_disconn(struct hci_conn *co conn->state = BT_DISCONN; - cp.handle = __cpu_to_le16(conn->handle); + cp.handle = cpu_to_le16(conn->handle); cp.reason = reason; hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, sizeof(cp), &cp); @@ -123,8 +123,8 @@ void hci_add_sco(struct hci_conn *conn, conn->state = BT_CONNECT; conn->out = 1; - cp.pkt_type = __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); - cp.handle = __cpu_to_le16(handle); + cp.pkt_type = cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); + cp.handle = cpu_to_le16(handle); hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, sizeof(cp), &cp); } @@ -348,7 +348,7 @@ int hci_conn_auth(struct hci_conn *conn) if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_auth_requested cp; - cp.handle = __cpu_to_le16(conn->handle); + cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED, sizeof(cp), &cp); } return 0; @@ -368,7 +368,7 @@ int hci_conn_encrypt(struct hci_conn *co if (hci_conn_auth(conn)) { struct hci_cp_set_conn_encrypt cp; - cp.handle = __cpu_to_le16(conn->handle); + cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp); } @@ -383,7 +383,7 @@ int hci_conn_change_link_key(struct hci_ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_change_conn_link_key cp; - cp.handle = __cpu_to_le16(conn->handle); + cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp); } return 0; @@ -423,7 +423,7 @@ void hci_conn_enter_active_mode(struct h if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { struct hci_cp_exit_sniff_mode cp; - cp.handle = __cpu_to_le16(conn->handle); + cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp); } @@ -452,21 +452,21 @@ void hci_conn_enter_sniff_mode(struct hc if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { struct hci_cp_sniff_subrate cp; - cp.handle = __cpu_to_le16(conn->handle); - cp.max_latency = __constant_cpu_to_le16(0); - cp.min_remote_timeout = __constant_cpu_to_le16(0); - cp.min_local_timeout = __constant_cpu_to_le16(0); + cp.handle = cpu_to_le16(conn->handle); + cp.max_latency = cpu_to_le16(0); + cp.min_remote_timeout = cpu_to_le16(0); + cp.min_local_timeout = cpu_to_le16(0); hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_SNIFF_SUBRATE, sizeof(cp), &cp); } if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { struct hci_cp_sniff_mode cp; - cp.handle = __cpu_to_le16(conn->handle); - cp.max_interval = __cpu_to_le16(hdev->sniff_max_interval); - cp.min_interval = __cpu_to_le16(hdev->sniff_min_interval); - cp.attempt = __constant_cpu_to_le16(4); - cp.timeout = __constant_cpu_to_le16(1); + cp.handle = cpu_to_le16(conn->handle); + cp.max_interval = cpu_to_le16(hdev->sniff_max_interval); + cp.min_interval = cpu_to_le16(hdev->sniff_min_interval); + cp.attempt = cpu_to_le16(4); + cp.timeout = cpu_to_le16(1); hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_SNIFF_MODE, sizeof(cp), &cp); } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4917919..aa4b56a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -149,7 +149,7 @@ static int __hci_request(struct hci_dev default: err = -ETIMEDOUT; break; - }; + } hdev->req_status = hdev->req_result = 0; @@ -216,10 +216,10 @@ #if 0 /* Host buffer size */ { struct hci_cp_host_buffer_size cp; - cp.acl_mtu = __cpu_to_le16(HCI_MAX_ACL_SIZE); + cp.acl_mtu = cpu_to_le16(HCI_MAX_ACL_SIZE); cp.sco_mtu = HCI_MAX_SCO_SIZE; - cp.acl_max_pkt = __cpu_to_le16(0xffff); - cp.sco_max_pkt = __cpu_to_le16(0xffff); + cp.acl_max_pkt = cpu_to_le16(0xffff); + cp.sco_max_pkt = cpu_to_le16(0xffff); hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE, sizeof(cp), &cp); } #endif @@ -240,11 +240,11 @@ #endif } /* Page timeout ~20 secs */ - param = __cpu_to_le16(0x8000); + param = cpu_to_le16(0x8000); hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_PG_TIMEOUT, 2, ¶m); /* Connection accept timeout ~20 secs */ - param = __cpu_to_le16(0x7d00); + param = cpu_to_le16(0x7d00); hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_CA_TIMEOUT, 2, ¶m); } @@ -1034,7 +1034,7 @@ int hci_send_cmd(struct hci_dev *hdev, _ } hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); - hdr->opcode = __cpu_to_le16(hci_opcode_pack(ogf, ocf)); + hdr->opcode = cpu_to_le16(hci_opcode_pack(ogf, ocf)); hdr->plen = plen; if (plen) @@ -1060,7 +1060,7 @@ void *hci_sent_cmd_data(struct hci_dev * hdr = (void *) hdev->sent_cmd->data; - if (hdr->opcode != __cpu_to_le16(hci_opcode_pack(ogf, ocf))) + if (hdr->opcode != cpu_to_le16(hci_opcode_pack(ogf, ocf))) return NULL; BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf); @@ -1074,11 +1074,11 @@ static void hci_add_acl_hdr(struct sk_bu struct hci_acl_hdr *hdr; int len = skb->len; - hdr = (struct hci_acl_hdr *) skb_push(skb, HCI_ACL_HDR_SIZE); - hdr->handle = __cpu_to_le16(hci_handle_pack(handle, flags)); - hdr->dlen = __cpu_to_le16(len); - - skb->h.raw = (void *) hdr; + skb_push(skb, HCI_ACL_HDR_SIZE); + skb_reset_transport_header(skb); + hdr = (struct hci_acl_hdr *)skb_transport_header(skb); + hdr->handle = cpu_to_le16(hci_handle_pack(handle, flags)); + hdr->dlen = cpu_to_le16(len); } int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) @@ -1140,11 +1140,12 @@ int hci_send_sco(struct hci_conn *conn, return -EINVAL; } - hdr.handle = __cpu_to_le16(conn->handle); + hdr.handle = cpu_to_le16(conn->handle); hdr.dlen = skb->len; - skb->h.raw = skb_push(skb, HCI_SCO_HDR_SIZE); - memcpy(skb->h.raw, &hdr, HCI_SCO_HDR_SIZE); + skb_push(skb, HCI_SCO_HDR_SIZE); + skb_reset_transport_header(skb); + memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE); skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; @@ -1387,7 +1388,7 @@ static void hci_rx_task(unsigned long ar case HCI_SCODATA_PKT: kfree_skb(skb); continue; - }; + } } /* Process frame */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 936d3fc..447ba71 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -783,7 +783,7 @@ static inline void hci_conn_complete_evt if (conn->type == ACL_LINK && hdev->link_policy) { struct hci_cp_write_link_policy cp; cp.handle = ev->handle; - cp.policy = __cpu_to_le16(hdev->link_policy); + cp.policy = cpu_to_le16(hdev->link_policy); hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, sizeof(cp), &cp); } @@ -793,8 +793,8 @@ static inline void hci_conn_complete_evt struct hci_cp_change_conn_ptype cp; cp.handle = ev->handle; cp.pkt_type = (conn->type == ACL_LINK) ? - __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): - __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); + cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): + cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp); @@ -970,7 +970,7 @@ static inline void hci_auth_complete_evt if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { if (!ev->status) { struct hci_cp_set_conn_encrypt cp; - cp.handle = __cpu_to_le16(conn->handle); + cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 71f5cfb..832b5f4 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -375,7 +375,7 @@ static int hci_sock_recvmsg(struct kiocb copied = len; } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); hci_sock_cmsg(sk, msg, skb); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e83ee82..a586787 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -459,8 +459,8 @@ static void __l2cap_sock_close(struct so sk->sk_state = BT_DISCONN; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); + req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); } else { @@ -652,7 +652,7 @@ static int l2cap_do_connect(struct sock if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn_req req; l2cap_pi(sk)->ident = l2cap_get_ident(conn); - req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); @@ -868,8 +868,8 @@ static inline int l2cap_do_send(struct s /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid); - lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); if (sk->sk_type == SOCK_DGRAM) put_unaligned(l2cap_pi(sk)->psm, (u16 *) skb_put(skb, 2)); @@ -1096,7 +1096,7 @@ static void l2cap_conn_ready(struct l2ca } else if (sk->sk_state == BT_CONNECT) { struct l2cap_conn_req req; l2cap_pi(sk)->ident = l2cap_get_ident(conn); - req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); } @@ -1192,13 +1192,13 @@ static struct sk_buff *l2cap_build_cmd(s return NULL; lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); - lh->cid = __cpu_to_le16(0x0001); + lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); + lh->cid = cpu_to_le16(0x0001); cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); cmd->code = code; cmd->ident = ident; - cmd->len = __cpu_to_le16(dlen); + cmd->len = cpu_to_le16(dlen); if (dlen) { count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE; @@ -1316,11 +1316,11 @@ static void l2cap_add_conf_opt(void **pt break; case 2: - *((u16 *) opt->val) = __cpu_to_le16(val); + *((u16 *) opt->val) = cpu_to_le16(val); break; case 4: - *((u32 *) opt->val) = __cpu_to_le32(val); + *((u32 *) opt->val) = cpu_to_le32(val); break; default: @@ -1346,8 +1346,8 @@ static int l2cap_build_conf_req(struct s //if (flush_to != L2CAP_DEFAULT_FLUSH_TO) // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to); - req->dcid = __cpu_to_le16(pi->dcid); - req->flags = __cpu_to_le16(0); + req->dcid = cpu_to_le16(pi->dcid); + req->flags = cpu_to_le16(0); return ptr - data; } @@ -1383,9 +1383,9 @@ static int l2cap_build_conf_rsp(struct s else flags = 0x0001; - rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid); - rsp->result = __cpu_to_le16(result ? *result : 0); - rsp->flags = __cpu_to_le16(flags); + rsp->scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp->result = cpu_to_le16(result ? *result : 0); + rsp->flags = cpu_to_le16(flags); return ptr - data; } @@ -1470,10 +1470,10 @@ response: bh_unlock_sock(parent); sendresp: - rsp.scid = __cpu_to_le16(scid); - rsp.dcid = __cpu_to_le16(dcid); - rsp.result = __cpu_to_le16(result); - rsp.status = __cpu_to_le16(status); + rsp.scid = cpu_to_le16(scid); + rsp.dcid = cpu_to_le16(dcid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(status); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); return 0; } @@ -1613,8 +1613,8 @@ static inline int l2cap_config_rsp(struc l2cap_sock_set_timer(sk, HZ * 5); { struct l2cap_disconn_req req; - req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); + req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); } @@ -1652,8 +1652,8 @@ static inline int l2cap_disconnect_req(s if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid))) return 0; - rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); - rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); sk->sk_shutdown = SHUTDOWN_MASK; @@ -1696,8 +1696,8 @@ static inline int l2cap_information_req( BT_DBG("type 0x%4.4x", type); - rsp.type = __cpu_to_le16(type); - rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP); + rsp.type = cpu_to_le16(type); + rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp); return 0; @@ -1794,7 +1794,7 @@ static inline void l2cap_sig_channel(str BT_DBG("error %d", err); /* FIXME: Map err to a valid reason */ - rej.reason = __cpu_to_le16(0); + rej.reason = cpu_to_le16(0); l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); } @@ -1993,10 +1993,10 @@ static int l2cap_auth_cfm(struct hci_con result = L2CAP_CR_SEC_BLOCK; } - rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = __cpu_to_le16(result); - rsp.status = __cpu_to_le16(0); + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(0); l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); @@ -2041,10 +2041,10 @@ static int l2cap_encrypt_cfm(struct hci_ result = L2CAP_CR_SEC_BLOCK; } - rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = __cpu_to_le16(result); - rsp.status = __cpu_to_le16(0); + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(0); l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); @@ -2107,7 +2107,8 @@ static int l2cap_recv_acldata(struct hci if (!(conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC))) goto drop; - memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), + skb->len); conn->rx_len = len - skb->len; } else { BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); @@ -2128,7 +2129,8 @@ static int l2cap_recv_acldata(struct hci goto drop; } - memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), + skb->len); conn->rx_len -= skb->len; if (!conn->rx_len) { diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 94f4573..fe7df90 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1567,7 +1567,7 @@ static int rfcomm_recv_frame(struct rfco /* Trim FCS */ skb->len--; skb->tail--; - fcs = *(u8 *) skb->tail; + fcs = *(u8 *)skb_tail_pointer(skb); if (__check_fcs(skb->data, type, fcs)) { BT_ERR("bad checksum in packet"); @@ -1851,18 +1851,18 @@ static void rfcomm_worker(void) BT_DBG(""); while (!atomic_read(&terminate)) { + set_current_state(TASK_INTERRUPTIBLE); if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) { /* No pending events. Let's sleep. * Incoming connections and data will wake us up. */ - set_current_state(TASK_INTERRUPTIBLE); schedule(); } + set_current_state(TASK_RUNNING); /* Process stuff */ clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); rfcomm_process_sessions(); } - set_current_state(TASK_RUNNING); return; } diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index ae43914..3f5163e 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -393,7 +393,7 @@ static void sco_sock_close(struct sock * default: sock_set_flag(sk, SOCK_ZAPPED); break; - }; + } release_sock(sk); diff --git a/net/bridge/br.c b/net/bridge/br.c index 2994387..848b8fa 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -37,7 +37,9 @@ static int __init br_init(void) return -EADDRINUSE; } - br_fdb_init(); + err = br_fdb_init(); + if (err) + goto err_out1; err = br_netfilter_init(); if (err) @@ -47,7 +49,10 @@ static int __init br_init(void) if (err) goto err_out2; - br_netlink_init(); + err = br_netlink_init(); + if (err) + goto err_out3; + brioctl_set(br_ioctl_deviceless_stub); br_handle_frame_hook = br_handle_frame; @@ -55,7 +60,8 @@ static int __init br_init(void) br_fdb_put_hook = br_fdb_put; return 0; - +err_out3: + unregister_netdevice_notifier(&br_device_notifier); err_out2: br_netfilter_fini(); err_out1: diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 905a39c..5e1892d 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -37,7 +37,7 @@ int br_dev_xmit(struct sk_buff *skb, str br->statistics.tx_packets++; br->statistics.tx_bytes += skb->len; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); if (dest[0] & 1) @@ -83,27 +83,21 @@ static int br_change_mtu(struct net_devi return 0; } -/* Allow setting mac address of pseudo-bridge to be same as - * any of the bound interfaces - */ +/* Allow setting mac address to any valid ethernet address. */ static int br_set_mac_address(struct net_device *dev, void *p) { struct net_bridge *br = netdev_priv(dev); struct sockaddr *addr = p; - struct net_bridge_port *port; - int err = -EADDRNOTAVAIL; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EINVAL; spin_lock_bh(&br->lock); - list_for_each_entry(port, &br->port_list, list) { - if (!compare_ether_addr(port->dev->dev_addr, addr->sa_data)) { - br_stp_change_bridge_id(br, addr->sa_data); - err = 0; - break; - } - } + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + br_stp_change_bridge_id(br, addr->sa_data); spin_unlock_bh(&br->lock); - return err; + return 0; } static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 8d566c1..91b0170 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -20,19 +20,28 @@ #include #include #include #include +#include #include +#include #include "br_private.h" static struct kmem_cache *br_fdb_cache __read_mostly; static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr); -void __init br_fdb_init(void) +static u32 fdb_salt __read_mostly; + +int __init br_fdb_init(void) { br_fdb_cache = kmem_cache_create("bridge_fdb_cache", sizeof(struct net_bridge_fdb_entry), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!br_fdb_cache) + return -ENOMEM; + + get_random_bytes(&fdb_salt, sizeof(fdb_salt)); + return 0; } void __exit br_fdb_fini(void) @@ -44,24 +53,26 @@ void __exit br_fdb_fini(void) /* if topology_changing then use forward_delay (default 15 sec) * otherwise keep longer (default 5 minutes) */ -static __inline__ unsigned long hold_time(const struct net_bridge *br) +static inline unsigned long hold_time(const struct net_bridge *br) { return br->topology_change ? br->forward_delay : br->ageing_time; } -static __inline__ int has_expired(const struct net_bridge *br, +static inline int has_expired(const struct net_bridge *br, const struct net_bridge_fdb_entry *fdb) { return !fdb->is_static && time_before_eq(fdb->ageing_timer + hold_time(br), jiffies); } -static __inline__ int br_mac_hash(const unsigned char *mac) +static inline int br_mac_hash(const unsigned char *mac) { - return jhash(mac, ETH_ALEN, 0) & (BR_HASH_SIZE - 1); + /* use 1 byte of OUI cnd 3 bytes of NIC */ + u32 key = get_unaligned((u32 *)(mac + 2)); + return jhash_1word(key, fdb_salt) & (BR_HASH_SIZE - 1); } -static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f) +static inline void fdb_delete(struct net_bridge_fdb_entry *f) { hlist_del_rcu(&f->hlist); br_fdb_put(f); @@ -128,7 +139,26 @@ void br_fdb_cleanup(unsigned long _data) mod_timer(&br->gc_timer, jiffies + HZ/10); } +/* Completely flush all dynamic entries in forwarding database.*/ +void br_fdb_flush(struct net_bridge *br) +{ + int i; + spin_lock_bh(&br->hash_lock); + for (i = 0; i < BR_HASH_SIZE; i++) { + struct net_bridge_fdb_entry *f; + struct hlist_node *h, *n; + hlist_for_each_entry_safe(f, h, n, &br->hash[i], hlist) { + if (!f->is_static) + fdb_delete(f); + } + } + spin_unlock_bh(&br->hash_lock); +} + +/* Flush all entries refering to a specific port. + * if do_all is set also flush static entries + */ void br_fdb_delete_by_port(struct net_bridge *br, const struct net_bridge_port *p, int do_all) diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 3e45c1a..ada7f49 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -71,7 +71,7 @@ static void __br_forward(const struct ne indev = skb->dev; skb->dev = to->dev; - skb->ip_summed = CHECKSUM_NONE; + skb_forward_csum(skb); NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev, br_forward_finish); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index f3a2e29..690573b 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -152,6 +152,8 @@ static void del_nbp(struct net_bridge_po br_stp_disable_port(p); spin_unlock_bh(&br->lock); + br_ifinfo_notify(RTM_DELLINK, p); + br_fdb_delete_by_port(br, p, 1); list_del_rcu(&p->list); @@ -203,7 +205,7 @@ static struct net_device *new_bridge_dev memcpy(br->group_addr, br_group_address, ETH_ALEN); br->feature_mask = dev->features; - br->stp_enabled = 0; + br->stp_enabled = BR_NO_STP; br->designated_root = br->bridge_id; br->root_path_cost = 0; br->root_port = 0; @@ -434,6 +436,8 @@ int br_add_if(struct net_bridge *br, str br_stp_enable_port(p); spin_unlock_bh(&br->lock); + br_ifinfo_notify(RTM_NEWLINK, p); + dev_set_mtu(br->dev, br_min_mtu(br)); kobject_uevent(&p->kobj, KOBJ_ADD); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 35b94f9..420bbb9 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -112,46 +112,59 @@ static int br_handle_local_finish(struct */ static inline int is_link_local(const unsigned char *dest) { - return memcmp(dest, br_group_address, 5) == 0 && (dest[5] & 0xf0) == 0; + const u16 *a = (const u16 *) dest; + static const u16 *const b = (const u16 *const ) br_group_address; + static const u16 m = __constant_cpu_to_be16(0xfff0); + + return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0; } /* * Called via br_handle_frame_hook. - * Return 0 if *pskb should be processed furthur - * 1 if *pskb is handled + * Return NULL if skb is handled * note: already called with rcu_read_lock (preempt_disabled) */ -int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) +struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb) { - struct sk_buff *skb = *pskb; const unsigned char *dest = eth_hdr(skb)->h_dest; if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) - goto err; + goto drop; if (unlikely(is_link_local(dest))) { - skb->pkt_type = PACKET_HOST; - return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, - NULL, br_handle_local_finish) != 0; + /* Pause frames shouldn't be passed up by driver anyway */ + if (skb->protocol == htons(ETH_P_PAUSE)) + goto drop; + + /* Process STP BPDU's through normal netif_receive_skb() path */ + if (p->br->stp_enabled != BR_NO_STP) { + if (NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, + NULL, br_handle_local_finish)) + return NULL; + else + return skb; + } } - if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) { + switch (p->state) { + case BR_STATE_FORWARDING: + if (br_should_route_hook) { - if (br_should_route_hook(pskb)) - return 0; - skb = *pskb; + if (br_should_route_hook(&skb)) + return skb; dest = eth_hdr(skb)->h_dest; } - + /* fall through */ + case BR_STATE_LEARNING: if (!compare_ether_addr(p->br->dev->dev_addr, dest)) skb->pkt_type = PACKET_HOST; NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish); - return 1; + break; + default: +drop: + kfree_skb(skb); } - -err: - kfree_skb(skb); - return 1; + return NULL; } diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 147015f..eda0fbf 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -137,7 +137,8 @@ static int old_dev_ioctl(struct net_devi b.topology_change = br->topology_change; b.topology_change_detected = br->topology_change_detected; b.root_port = br->root_port; - b.stp_enabled = br->stp_enabled; + + b.stp_enabled = (br->stp_enabled != BR_NO_STP); b.ageing_time = jiffies_to_clock_t(br->ageing_time); b.hello_timer_value = br_timer_value(&br->hello_timer); b.tcn_timer_value = br_timer_value(&br->tcn_timer); @@ -251,7 +252,7 @@ static int old_dev_ioctl(struct net_devi if (!capable(CAP_NET_ADMIN)) return -EPERM; - br->stp_enabled = args[1]?1:0; + br_stp_set_enabled(br, args[1]); return 0; case BRCTL_SET_BRIDGE_PRIORITY: diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 5439a3c..9b2986b 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -29,6 +29,8 @@ #include #include #include #include +#include +#include #include #include #include @@ -48,8 +50,8 @@ #endif #define skb_origaddr(skb) (((struct bridge_skb_cb *) \ (skb->nf_bridge->data))->daddr.ipv4) -#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr) -#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr) +#define store_orig_dstaddr(skb) (skb_origaddr(skb) = ip_hdr(skb)->daddr) +#define dnat_took_place(skb) (skb_origaddr(skb) != ip_hdr(skb)->daddr) #ifdef CONFIG_SYSCTL static struct ctl_table_header *brnf_sysctl_header; @@ -57,8 +59,10 @@ static int brnf_call_iptables __read_mos static int brnf_call_ip6tables __read_mostly = 1; static int brnf_call_arptables __read_mostly = 1; static int brnf_filter_vlan_tagged __read_mostly = 1; +static int brnf_filter_pppoe_tagged __read_mostly = 1; #else #define brnf_filter_vlan_tagged 1 +#define brnf_filter_pppoe_tagged 1 #endif static inline __be16 vlan_proto(const struct sk_buff *skb) @@ -81,6 +85,22 @@ #define IS_VLAN_ARP(skb) \ vlan_proto(skb) == htons(ETH_P_ARP) && \ brnf_filter_vlan_tagged) +static inline __be16 pppoe_proto(const struct sk_buff *skb) +{ + return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN + + sizeof(struct pppoe_hdr))); +} + +#define IS_PPPOE_IP(skb) \ + (skb->protocol == htons(ETH_P_PPP_SES) && \ + pppoe_proto(skb) == htons(PPP_IP) && \ + brnf_filter_pppoe_tagged) + +#define IS_PPPOE_IPV6(skb) \ + (skb->protocol == htons(ETH_P_PPP_SES) && \ + pppoe_proto(skb) == htons(PPP_IPV6) && \ + brnf_filter_pppoe_tagged) + /* We need these fake structures to make netfilter happy -- * lots of places assume that skb->dst != NULL, which isn't * all that unreasonable. @@ -128,8 +148,11 @@ static inline void nf_bridge_save_header if (skb->protocol == htons(ETH_P_8021Q)) header_size += VLAN_HLEN; + else if (skb->protocol == htons(ETH_P_PPP_SES)) + header_size += PPPOE_SES_HLEN; - memcpy(skb->nf_bridge->data, skb->data - header_size, header_size); + skb_copy_from_linear_data_offset(skb, -header_size, + skb->nf_bridge->data, header_size); } /* @@ -143,15 +166,20 @@ int nf_bridge_copy_header(struct sk_buff if (skb->protocol == htons(ETH_P_8021Q)) header_size += VLAN_HLEN; + else if (skb->protocol == htons(ETH_P_PPP_SES)) + header_size += PPPOE_SES_HLEN; err = skb_cow(skb, header_size); if (err) return err; - memcpy(skb->data - header_size, skb->nf_bridge->data, header_size); + skb_copy_to_linear_data_offset(skb, -header_size, + skb->nf_bridge->data, header_size); if (skb->protocol == htons(ETH_P_8021Q)) __skb_push(skb, VLAN_HLEN); + else if (skb->protocol == htons(ETH_P_PPP_SES)) + __skb_push(skb, PPPOE_SES_HLEN); return 0; } @@ -174,7 +202,10 @@ static int br_nf_pre_routing_finish_ipv6 skb->dev = nf_bridge->physindev; if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); - skb->nh.raw -= VLAN_HLEN; + skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish, 1); @@ -255,7 +286,10 @@ static int br_nf_pre_routing_finish_brid else { if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull(skb, VLAN_HLEN); - skb->nh.raw += VLAN_HLEN; + skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } skb->dst->output(skb); } @@ -265,7 +299,7 @@ static int br_nf_pre_routing_finish_brid static int br_nf_pre_routing_finish(struct sk_buff *skb) { struct net_device *dev = skb->dev; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); struct nf_bridge_info *nf_bridge = skb->nf_bridge; int err; @@ -325,7 +359,11 @@ bridged_dnat: if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); - skb->nh.raw -= VLAN_HLEN; + skb->network_header -= VLAN_HLEN; + } else if(skb->protocol == + htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, @@ -344,7 +382,10 @@ bridged_dnat: skb->dev = nf_bridge->physindev; if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); - skb->nh.raw -= VLAN_HLEN; + skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish, 1); @@ -372,9 +413,10 @@ static struct net_device *setup_pre_rout /* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */ static int check_hbh_len(struct sk_buff *skb) { - unsigned char *raw = (u8 *) (skb->nh.ipv6h + 1); + unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1); u32 pkt_len; - int off = raw - skb->nh.raw; + const unsigned char *nh = skb_network_header(skb); + int off = raw - nh; int len = (raw[1] + 1) << 3; if ((raw + len) - skb->data > skb_headlen(skb)) @@ -384,9 +426,9 @@ static int check_hbh_len(struct sk_buff len -= 2; while (len > 0) { - int optlen = skb->nh.raw[off + 1] + 2; + int optlen = nh[off + 1] + 2; - switch (skb->nh.raw[off]) { + switch (nh[off]) { case IPV6_TLV_PAD0: optlen = 1; break; @@ -395,17 +437,18 @@ static int check_hbh_len(struct sk_buff break; case IPV6_TLV_JUMBO: - if (skb->nh.raw[off + 1] != 4 || (off & 3) != 2) + if (nh[off + 1] != 4 || (off & 3) != 2) goto bad; - pkt_len = ntohl(*(__be32 *) (skb->nh.raw + off + 2)); + pkt_len = ntohl(*(__be32 *) (nh + off + 2)); if (pkt_len <= IPV6_MAXPLEN || - skb->nh.ipv6h->payload_len) + ipv6_hdr(skb)->payload_len) goto bad; if (pkt_len > skb->len - sizeof(struct ipv6hdr)) goto bad; if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) goto bad; + nh = skb_network_header(skb); break; default: if (optlen > len) @@ -439,7 +482,7 @@ static unsigned int br_nf_pre_routing_ip if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto inhdr_error; - hdr = skb->nh.ipv6h; + hdr = ipv6_hdr(skb); if (hdr->version != 6) goto inhdr_error; @@ -485,7 +528,8 @@ static unsigned int br_nf_pre_routing(un __u32 len; struct sk_buff *skb = *pskb; - if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) { + if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) || + IS_PPPOE_IPV6(skb)) { #ifdef CONFIG_SYSCTL if (!brnf_call_ip6tables) return NF_ACCEPT; @@ -495,7 +539,10 @@ #endif if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull_rcsum(skb, VLAN_HLEN); - skb->nh.raw += VLAN_HLEN; + skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull_rcsum(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); } @@ -504,7 +551,8 @@ #ifdef CONFIG_SYSCTL return NF_ACCEPT; #endif - if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb)) + if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb) && + !IS_PPPOE_IP(skb)) return NF_ACCEPT; if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) @@ -512,20 +560,23 @@ #endif if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull_rcsum(skb, VLAN_HLEN); - skb->nh.raw += VLAN_HLEN; + skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull_rcsum(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto inhdr_error; - iph = skb->nh.iph; + iph = ip_hdr(skb); if (iph->ihl < 5 || iph->version != 4) goto inhdr_error; if (!pskb_may_pull(skb, 4 * iph->ihl)) goto inhdr_error; - iph = skb->nh.iph; + iph = ip_hdr(skb); if (ip_fast_csum((__u8 *) iph, iph->ihl) != 0) goto inhdr_error; @@ -593,7 +644,10 @@ static int br_nf_forward_finish(struct s } if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); - skb->nh.raw -= VLAN_HLEN; + skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in, skb->dev, br_forward_finish, 1); @@ -622,14 +676,18 @@ static unsigned int br_nf_forward_ip(uns if (!parent) return NF_DROP; - if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb)) + if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) || + IS_PPPOE_IP(skb)) pf = PF_INET; else pf = PF_INET6; if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull(*pskb, VLAN_HLEN); - (*pskb)->nh.raw += VLAN_HLEN; + (*pskb)->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull(*pskb, PPPOE_SES_HLEN); + (*pskb)->network_header += PPPOE_SES_HLEN; } nf_bridge = skb->nf_bridge; @@ -665,13 +723,13 @@ #endif if (!IS_VLAN_ARP(skb)) return NF_ACCEPT; skb_pull(*pskb, VLAN_HLEN); - (*pskb)->nh.raw += VLAN_HLEN; + (*pskb)->network_header += VLAN_HLEN; } - if (skb->nh.arph->ar_pln != 4) { + if (arp_hdr(skb)->ar_pln != 4) { if (IS_VLAN_ARP(skb)) { skb_push(*pskb, VLAN_HLEN); - (*pskb)->nh.raw -= VLAN_HLEN; + (*pskb)->network_header -= VLAN_HLEN; } return NF_ACCEPT; } @@ -721,7 +779,10 @@ static unsigned int br_nf_local_out(unsi } if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); - skb->nh.raw -= VLAN_HLEN; + skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev, @@ -753,7 +814,8 @@ static unsigned int br_nf_post_routing(u #ifdef CONFIG_NETFILTER_DEBUG /* Be very paranoid. This probably won't happen anymore, but let's * keep the check just to be sure... */ - if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) { + if (skb_mac_header(skb) < skb->head || + skb_mac_header(skb) + ETH_HLEN > skb->data) { printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: " "bad mac.raw pointer.\n"); goto print_error; @@ -766,7 +828,8 @@ #endif if (!realoutdev) return NF_DROP; - if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb)) + if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) || + IS_PPPOE_IP(skb)) pf = PF_INET; else pf = PF_INET6; @@ -787,7 +850,10 @@ #endif if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull(skb, VLAN_HLEN); - skb->nh.raw += VLAN_HLEN; + skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } nf_bridge_save_header(skb); @@ -808,7 +874,7 @@ print_error: if (realoutdev) printk("[%s]", realoutdev->name); } - printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw, + printk(" head:%p, raw:%p, data:%p\n", skb->head, skb_mac_header(skb), skb->data); dump_stack(); return NF_ACCEPT; @@ -925,6 +991,14 @@ static ctl_table brnf_table[] = { .mode = 0644, .proc_handler = &brnf_sysctl_call_tables, }, + { + .ctl_name = NET_BRIDGE_NF_FILTER_PPPOE_TAGGED, + .procname = "bridge-nf-filter-pppoe-tagged", + .data = &brnf_filter_pppoe_tagged, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &brnf_sysctl_call_tables, + }, { .ctl_name = 0 } }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 7d68b24..35facc0 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -11,8 +11,7 @@ */ #include -#include -#include +#include #include "br_private.h" static inline size_t br_nlmsg_size(void) @@ -110,7 +109,6 @@ static int br_dump_ifinfo(struct sk_buff struct net_device *dev; int idx; - read_lock(&dev_base_lock); for (dev = dev_base, idx = 0; dev; dev = dev->next) { /* not a bridge port */ if (dev->br_port == NULL || idx < cb->args[0]) @@ -123,7 +121,6 @@ static int br_dump_ifinfo(struct sk_buff skip: ++idx; } - read_unlock(&dev_base_lock); cb->args[0] = idx; @@ -166,7 +163,7 @@ static int br_rtm_setlink(struct sk_buff return -EINVAL; /* if kernel STP is running, don't allow changes */ - if (p->br->stp_enabled) + if (p->br->stp_enabled == BR_KERNEL_STP) return -EBUSY; if (!netif_running(dev) || @@ -179,18 +176,19 @@ static int br_rtm_setlink(struct sk_buff } -static struct rtnetlink_link bridge_rtnetlink_table[RTM_NR_MSGTYPES] = { - [RTM_GETLINK - RTM_BASE] = { .dumpit = br_dump_ifinfo, }, - [RTM_SETLINK - RTM_BASE] = { .doit = br_rtm_setlink, }, -}; - -void __init br_netlink_init(void) +int __init br_netlink_init(void) { - rtnetlink_links[PF_BRIDGE] = bridge_rtnetlink_table; + if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo)) + return -ENOBUFS; + + /* Only the first call to __rtnl_register can fail */ + __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); + + return 0; } void __exit br_netlink_fini(void) { - rtnetlink_links[PF_BRIDGE] = NULL; + rtnl_unregister_all(PF_BRIDGE); } diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 37357ed..c8451d3 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -50,7 +50,6 @@ static int br_device_event(struct notifi case NETDEV_CHANGEADDR: spin_lock_bh(&br->lock); br_fdb_changeaddr(p, dev->dev_addr); - br_ifinfo_notify(RTM_NEWLINK, p); br_stp_recalculate_bridge_id(br); spin_unlock_bh(&br->lock); break; @@ -74,10 +73,11 @@ static int br_device_event(struct notifi break; case NETDEV_UP: - spin_lock_bh(&br->lock); - if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) + if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) { + spin_lock_bh(&br->lock); br_stp_enable_port(p); - spin_unlock_bh(&br->lock); + spin_unlock_bh(&br->lock); + } break; case NETDEV_UNREGISTER: @@ -85,5 +85,10 @@ static int br_device_event(struct notifi break; } + /* Events that may cause spanning tree to refresh */ + if (event == NETDEV_CHANGEADDR || event == NETDEV_UP || + event == NETDEV_CHANGE || event == NETDEV_DOWN) + br_ifinfo_notify(RTM_NEWLINK, p); + return NOTIFY_DONE; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index cc3f1c9..21bf3a9 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -26,7 +26,10 @@ #define BR_HOLD_TIME (1*HZ) #define BR_PORT_BITS 10 #define BR_MAX_PORTS (1<bridge_id, &br->designated_root, 8); } - /* br_device.c */ extern void br_dev_setup(struct net_device *dev); extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); /* br_fdb.c */ -extern void br_fdb_init(void); +extern int br_fdb_init(void); extern void br_fdb_fini(void); +extern void br_fdb_flush(struct net_bridge *br); extern void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr); extern void br_fdb_cleanup(unsigned long arg); @@ -182,7 +191,8 @@ extern void br_features_recompute(struct /* br_input.c */ extern int br_handle_frame_finish(struct sk_buff *skb); -extern int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb); +extern struct sk_buff *br_handle_frame(struct net_bridge_port *p, + struct sk_buff *skb); /* br_ioctl.c */ extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); @@ -207,6 +217,7 @@ extern void br_become_designated_port(st /* br_stp_if.c */ extern void br_stp_enable_bridge(struct net_bridge *br); extern void br_stp_disable_bridge(struct net_bridge *br); +extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val); extern void br_stp_enable_port(struct net_bridge_port *p); extern void br_stp_disable_port(struct net_bridge_port *p); extern void br_stp_recalculate_bridge_id(struct net_bridge *br); @@ -235,7 +246,7 @@ extern void (*br_fdb_put_hook)(struct ne /* br_netlink.c */ -extern void br_netlink_init(void); +extern int br_netlink_init(void); extern void br_netlink_fini(void); extern void br_ifinfo_notify(int event, struct net_bridge_port *port); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index f9ff4d5..ebb0861 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -370,11 +370,11 @@ static void br_make_blocking(struct net_ static void br_make_forwarding(struct net_bridge_port *p) { if (p->state == BR_STATE_BLOCKING) { - if (p->br->stp_enabled) { + if (p->br->stp_enabled == BR_KERNEL_STP) p->state = BR_STATE_LISTENING; - } else { + else p->state = BR_STATE_LEARNING; - } + br_log_state(p); mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); } } @@ -384,6 +384,10 @@ void br_port_state_selection(struct net_ { struct net_bridge_port *p; + /* Don't change port states if userspace is handling STP */ + if (br->stp_enabled == BR_USER_STP) + return; + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED) { if (p->port_no == br->root_port) { diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index b9fb0dc..60112bc 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -33,9 +33,6 @@ static void br_send_bpdu(struct net_brid { struct sk_buff *skb; - if (!p->br->stp_enabled) - return; - skb = dev_alloc_skb(length+LLC_RESERVE); if (!skb) return; @@ -75,6 +72,9 @@ void br_send_config_bpdu(struct net_brid { unsigned char buf[35]; + if (p->br->stp_enabled != BR_KERNEL_STP) + return; + buf[0] = 0; buf[1] = 0; buf[2] = 0; @@ -117,6 +117,9 @@ void br_send_tcn_bpdu(struct net_bridge_ { unsigned char buf[4]; + if (p->br->stp_enabled != BR_KERNEL_STP) + return; + buf[0] = 0; buf[1] = 0; buf[2] = 0; @@ -157,9 +160,13 @@ int br_stp_rcv(struct sk_buff *skb, stru br = p->br; spin_lock(&br->lock); - if (p->state == BR_STATE_DISABLED - || !br->stp_enabled - || !(br->dev->flags & IFF_UP)) + if (br->stp_enabled != BR_KERNEL_STP) + goto out; + + if (!(br->dev->flags & IFF_UP)) + goto out; + + if (p->state == BR_STATE_DISABLED) goto out; if (compare_ether_addr(dest, br->group_addr) != 0) diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index a285897..3e246b3 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -87,7 +87,6 @@ void br_stp_disable_bridge(struct net_br void br_stp_enable_port(struct net_bridge_port *p) { br_init_port(p); - br_ifinfo_notify(RTM_NEWLINK, p); br_port_state_selection(p->br); } @@ -101,8 +100,6 @@ void br_stp_disable_port(struct net_brid printk(KERN_INFO "%s: port %i(%s) entering %s state\n", br->dev->name, p->port_no, p->dev->name, "disabled"); - br_ifinfo_notify(RTM_DELLINK, p); - wasroot = br_is_root_bridge(br); br_become_designated_port(p); p->state = BR_STATE_DISABLED; @@ -123,6 +120,62 @@ void br_stp_disable_port(struct net_brid br_become_root_bridge(br); } +static void br_stp_start(struct net_bridge *br) +{ + int r; + char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL }; + char *envp[] = { NULL }; + + r = call_usermodehelper(BR_STP_PROG, argv, envp, 1); + if (r == 0) { + br->stp_enabled = BR_USER_STP; + printk(KERN_INFO "%s: userspace STP started\n", br->dev->name); + } else { + br->stp_enabled = BR_KERNEL_STP; + printk(KERN_INFO "%s: starting userspace STP failed, " + "staring kernel STP\n", br->dev->name); + + /* To start timers on any ports left in blocking */ + spin_lock_bh(&br->lock); + br_port_state_selection(br); + spin_unlock_bh(&br->lock); + } +} + +static void br_stp_stop(struct net_bridge *br) +{ + int r; + char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL }; + char *envp[] = { NULL }; + + if (br->stp_enabled == BR_USER_STP) { + r = call_usermodehelper(BR_STP_PROG, argv, envp, 1); + printk(KERN_INFO "%s: userspace STP stopped, return code %d\n", + br->dev->name, r); + + + /* To start timers on any ports left in blocking */ + spin_lock_bh(&br->lock); + br_port_state_selection(br); + spin_unlock_bh(&br->lock); + } + + br->stp_enabled = BR_NO_STP; +} + +void br_stp_set_enabled(struct net_bridge *br, unsigned long val) +{ + ASSERT_RTNL(); + + if (val) { + if (br->stp_enabled == BR_NO_STP) + br_stp_start(br); + } else { + if (br->stp_enabled != BR_NO_STP) + br_stp_stop(br); + } +} + /* called under bridge lock */ void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr) { diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 01a22ad..33c6c4a 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -149,7 +149,11 @@ static ssize_t show_stp_state(struct dev static void set_stp_state(struct net_bridge *br, unsigned long val) { - br->stp_enabled = val; + rtnl_lock(); + spin_unlock_bh(&br->lock); + br_stp_set_enabled(br, val); + spin_lock_bh(&br->lock); + rtnl_unlock(); } static ssize_t store_stp_state(struct device *d, @@ -309,6 +313,19 @@ static ssize_t store_group_addr(struct d static DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR, show_group_addr, store_group_addr); +static ssize_t store_flush(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct net_bridge *br = to_bridge(d); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + br_fdb_flush(br); + return len; +} +static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush); static struct attribute *bridge_attrs[] = { &dev_attr_forward_delay.attr, @@ -328,6 +345,7 @@ static struct attribute *bridge_attrs[] &dev_attr_topology_change_timer.attr, &dev_attr_gc_timer.attr, &dev_attr_group_addr.attr, + &dev_attr_flush.attr, NULL }; diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 0bc2aef..2da2292 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -137,6 +137,13 @@ static ssize_t show_hold_timer(struct ne } static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL); +static ssize_t store_flush(struct net_bridge_port *p, unsigned long v) +{ + br_fdb_delete_by_port(p->br, p, 0); // Don't delete local entry + return 0; +} +static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush); + static struct brport_attribute *brport_attrs[] = { &brport_attr_path_cost, &brport_attr_priority, @@ -152,6 +159,7 @@ static struct brport_attribute *brport_a &brport_attr_message_age_timer, &brport_attr_forward_delay_timer, &brport_attr_hold_timer, + &brport_attr_flush, NULL }; diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 9c59980..1a46952 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -35,40 +35,36 @@ static int ebt_filter_arp(const struct s return EBT_NOMATCH; if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) { - __be32 _addr, *ap; + __be32 saddr, daddr, *sap, *dap; - /* IPv4 addresses are always 4 bytes */ - if (ah->ar_pln != sizeof(__be32)) + if (ah->ar_pln != sizeof(__be32) || ah->ar_pro != htons(ETH_P_IP)) + return EBT_NOMATCH; + sap = skb_header_pointer(skb, sizeof(struct arphdr) + + ah->ar_hln, sizeof(saddr), + &saddr); + if (sap == NULL) + return EBT_NOMATCH; + dap = skb_header_pointer(skb, sizeof(struct arphdr) + + 2*ah->ar_hln+sizeof(saddr), + sizeof(daddr), &daddr); + if (dap == NULL) + return EBT_NOMATCH; + if (info->bitmask & EBT_ARP_SRC_IP && + FWINV(info->saddr != (*sap & info->smsk), EBT_ARP_SRC_IP)) + return EBT_NOMATCH; + if (info->bitmask & EBT_ARP_DST_IP && + FWINV(info->daddr != (*dap & info->dmsk), EBT_ARP_DST_IP)) + return EBT_NOMATCH; + if (info->bitmask & EBT_ARP_GRAT && + FWINV(*dap != *sap, EBT_ARP_GRAT)) return EBT_NOMATCH; - if (info->bitmask & EBT_ARP_SRC_IP) { - ap = skb_header_pointer(skb, sizeof(struct arphdr) + - ah->ar_hln, sizeof(_addr), - &_addr); - if (ap == NULL) - return EBT_NOMATCH; - if (FWINV(info->saddr != (*ap & info->smsk), - EBT_ARP_SRC_IP)) - return EBT_NOMATCH; - } - - if (info->bitmask & EBT_ARP_DST_IP) { - ap = skb_header_pointer(skb, sizeof(struct arphdr) + - 2*ah->ar_hln+sizeof(__be32), - sizeof(_addr), &_addr); - if (ap == NULL) - return EBT_NOMATCH; - if (FWINV(info->daddr != (*ap & info->dmsk), - EBT_ARP_DST_IP)) - return EBT_NOMATCH; - } } if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) { unsigned char _mac[ETH_ALEN], *mp; uint8_t verdict, i; - /* MAC addresses are 6 bytes */ - if (ah->ar_hln != ETH_ALEN) + if (ah->ar_hln != ETH_ALEN || ah->ar_hrd != htons(ARPHRD_ETHER)) return EBT_NOMATCH; if (info->bitmask & EBT_ARP_SRC_MAC) { mp = skb_header_pointer(skb, sizeof(struct arphdr), diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 45712ae..031bfa4 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -196,14 +196,10 @@ static int __init ebt_log_init(void) ret = ebt_register_watcher(&log); if (ret < 0) return ret; - if (nf_log_register(PF_BRIDGE, &ebt_log_logger) < 0) { - printk(KERN_WARNING "ebt_log: not logging via system console " - "since somebody else already registered for PF_INET\n"); - /* we cannot make module load fail here, since otherwise - * ebtables userspace would abort */ - } - - return 0; + ret = nf_log_register(PF_BRIDGE, &ebt_log_logger); + if (ret < 0 && ret != -EEXIST) + ebt_unregister_watcher(&log); + return ret; } static void __exit ebt_log_fini(void) diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 8e15cc4..9411db6 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -130,6 +130,7 @@ static void ebt_ulog_packet(unsigned int unsigned int group = uloginfo->nlgroup; ebt_ulog_buff_t *ub = &ulog_buffers[group]; spinlock_t *lock = &ub->lock; + ktime_t kt; if ((uloginfo->cprange == 0) || (uloginfo->cprange > skb->len + ETH_HLEN)) @@ -164,9 +165,10 @@ static void ebt_ulog_packet(unsigned int /* Fill in the ulog data */ pm->version = EBT_ULOG_VERSION; - do_gettimeofday(&pm->stamp); + kt = ktime_get_real(); + pm->stamp = ktime_to_timeval(kt); if (ub->qlen == 1) - skb_set_timestamp(ub->skb, &pm->stamp); + ub->skb->tstamp = kt; pm->data_len = copy_len; pm->mark = skb->mark; pm->hook = hooknr; @@ -295,14 +297,12 @@ static int __init ebt_ulog_init(void) /* initialize ulog_buffers */ for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) { - init_timer(&ulog_buffers[i].timer); - ulog_buffers[i].timer.function = ulog_timer; - ulog_buffers[i].timer.data = i; + setup_timer(&ulog_buffers[i].timer, ulog_timer, i); spin_lock_init(&ulog_buffers[i].lock); } ebtulognl = netlink_kernel_create(NETLINK_NFLOG, EBT_ULOG_MAXNLGROUPS, - NULL, THIS_MODULE); + NULL, NULL, THIS_MODULE); if (!ebtulognl) ret = -ENOMEM; else if ((ret = ebt_register_watcher(&ulog))) diff --git a/net/compat.c b/net/compat.c index 1f32866..9a0f5f2 100644 --- a/net/compat.c +++ b/net/compat.c @@ -34,11 +34,11 @@ static inline int iov_from_user_compat_t { int tot_len = 0; - while(niov > 0) { + while (niov > 0) { compat_uptr_t buf; compat_size_t len; - if(get_user(len, &uiov32->iov_len) || + if (get_user(len, &uiov32->iov_len) || get_user(buf, &uiov32->iov_base)) { tot_len = -EFAULT; break; @@ -78,12 +78,12 @@ int verify_compat_iovec(struct msghdr *k { int tot_len; - if(kern_msg->msg_namelen) { - if(mode==VERIFY_READ) { + if (kern_msg->msg_namelen) { + if (mode==VERIFY_READ) { int err = move_addr_to_kernel(kern_msg->msg_name, kern_msg->msg_namelen, kern_address); - if(err < 0) + if (err < 0) return err; } kern_msg->msg_name = kern_address; @@ -93,7 +93,7 @@ int verify_compat_iovec(struct msghdr *k tot_len = iov_from_user_compat_to_kern(kern_iov, (struct compat_iovec __user *)kern_msg->msg_iov, kern_msg->msg_iovlen); - if(tot_len >= 0) + if (tot_len >= 0) kern_msg->msg_iov = kern_iov; return tot_len; @@ -146,8 +146,8 @@ int cmsghdr_from_user_compat_to_kern(str kcmlen = 0; kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); - while(ucmsg != NULL) { - if(get_user(ucmlen, &ucmsg->cmsg_len)) + while (ucmsg != NULL) { + if (get_user(ucmlen, &ucmsg->cmsg_len)) return -EFAULT; /* Catch bogons. */ @@ -160,7 +160,7 @@ int cmsghdr_from_user_compat_to_kern(str kcmlen += tmp; ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); } - if(kcmlen == 0) + if (kcmlen == 0) return -EINVAL; /* The kcmlen holds the 64-bit version of the control length. @@ -176,7 +176,7 @@ int cmsghdr_from_user_compat_to_kern(str /* Now copy them over neatly. */ memset(kcmsg, 0, kcmlen); ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); - while(ucmsg != NULL) { + while (ucmsg != NULL) { if (__get_user(ucmlen, &ucmsg->cmsg_len)) goto Efault; if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg)) @@ -215,11 +215,12 @@ Efault: int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) { struct compat_timeval ctv; + struct compat_timespec cts; struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; struct compat_cmsghdr cmhdr; int cmlen; - if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + if (cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { kmsg->msg_flags |= MSG_CTRUNC; return 0; /* XXX: return error? check spec. */ } @@ -229,11 +230,18 @@ int put_cmsg_compat(struct msghdr *kmsg, ctv.tv_sec = tv->tv_sec; ctv.tv_usec = tv->tv_usec; data = &ctv; - len = sizeof(struct compat_timeval); + len = sizeof(ctv); + } + if (level == SOL_SOCKET && type == SO_TIMESTAMPNS) { + struct timespec *ts = (struct timespec *)data; + cts.tv_sec = ts->tv_sec; + cts.tv_nsec = ts->tv_nsec; + data = &cts; + len = sizeof(cts); } cmlen = CMSG_COMPAT_LEN(len); - if(kmsg->msg_controllen < cmlen) { + if (kmsg->msg_controllen < cmlen) { kmsg->msg_flags |= MSG_CTRUNC; cmlen = kmsg->msg_controllen; } @@ -241,9 +249,9 @@ int put_cmsg_compat(struct msghdr *kmsg, cmhdr.cmsg_type = type; cmhdr.cmsg_len = cmlen; - if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + if (copy_to_user(cm, &cmhdr, sizeof cmhdr)) return -EFAULT; - if(copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr))) + if (copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr))) return -EFAULT; cmlen = CMSG_COMPAT_SPACE(len); kmsg->msg_control += cmlen; @@ -545,20 +553,49 @@ int compat_sock_get_timestamp(struct soc struct compat_timeval __user *ctv = (struct compat_timeval __user*) userstamp; int err = -ENOENT; + struct timeval tv; if (!sock_flag(sk, SOCK_TIMESTAMP)) sock_enable_timestamp(sk); - if (sk->sk_stamp.tv_sec == -1) + tv = ktime_to_timeval(sk->sk_stamp); + if (tv.tv_sec == -1) return err; - if (sk->sk_stamp.tv_sec == 0) - do_gettimeofday(&sk->sk_stamp); - if (put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec) || - put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec)) + if (tv.tv_sec == 0) { + sk->sk_stamp = ktime_get_real(); + tv = ktime_to_timeval(sk->sk_stamp); + } + err = 0; + if (put_user(tv.tv_sec, &ctv->tv_sec) || + put_user(tv.tv_usec, &ctv->tv_usec)) err = -EFAULT; return err; } EXPORT_SYMBOL(compat_sock_get_timestamp); +int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp) +{ + struct compat_timespec __user *ctv = + (struct compat_timespec __user*) userstamp; + int err = -ENOENT; + struct timespec ts; + + if (!sock_flag(sk, SOCK_TIMESTAMP)) + sock_enable_timestamp(sk); + ts = ktime_to_timespec(sk->sk_stamp); + if (ts.tv_sec == -1) + return err; + if (ts.tv_sec == 0) { + sk->sk_stamp = ktime_get_real(); + ts = ktime_to_timespec(sk->sk_stamp); + } + err = 0; + if (put_user(ts.tv_sec, &ctv->tv_sec) || + put_user(ts.tv_nsec, &ctv->tv_nsec)) + err = -EFAULT; + return err; +} +EXPORT_SYMBOL(compat_sock_get_timestampns); + asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, char __user *optval, int __user *optlen) { @@ -617,7 +654,7 @@ asmlinkage long compat_sys_socketcall(in a0 = a[0]; a1 = a[1]; - switch(call) { + switch (call) { case SYS_SOCKET: ret = sys_socket(a0, a1, a[2]); break; diff --git a/net/core/Makefile b/net/core/Makefile index 73272d5..4751613 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -13,7 +13,6 @@ obj-y += dev.o ethtool.o dev_mcast obj-$(CONFIG_XFRM) += flow.o obj-$(CONFIG_SYSFS) += net-sysfs.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o -obj-$(CONFIG_WIRELESS_EXT) += wireless.o obj-$(CONFIG_NETPOLL) += netpoll.o obj-$(CONFIG_NET_DMA) += user_dma.o obj-$(CONFIG_FIB_RULES) += fib_rules.o diff --git a/net/core/datagram.c b/net/core/datagram.c index 186212b..cb056f4 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -411,11 +411,11 @@ fault: return -EFAULT; } -__sum16 __skb_checksum_complete(struct sk_buff *skb) +__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len) { __sum16 sum; - sum = csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)); + sum = csum_fold(skb_checksum(skb, 0, len, skb->csum)); if (likely(!sum)) { if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) netdev_rx_csum_fault(skb->dev); @@ -423,6 +423,12 @@ __sum16 __skb_checksum_complete(struct s } return sum; } +EXPORT_SYMBOL(__skb_checksum_complete_head); + +__sum16 __skb_checksum_complete(struct sk_buff *skb) +{ + return __skb_checksum_complete_head(skb, skb->len); +} EXPORT_SYMBOL(__skb_checksum_complete); /** diff --git a/net/core/dev.c b/net/core/dev.c index 4dc93cc..eb99900 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -109,7 +109,7 @@ #include #include #include #include -#include +#include #include #include #include @@ -146,8 +146,8 @@ #include */ static DEFINE_SPINLOCK(ptype_lock); -static struct list_head ptype_base[16]; /* 16 way hashed list */ -static struct list_head ptype_all; /* Taps */ +static struct list_head ptype_base[16] __read_mostly; /* 16 way hashed list */ +static struct list_head ptype_all __read_mostly; /* Taps */ #ifdef CONFIG_NET_DMA static struct dma_client *net_dma_client; @@ -226,12 +226,6 @@ #endif *******************************************************************************/ /* - * For efficiency - */ - -static int netdev_nit; - -/* * Add a protocol ID to the list. Now that the input handler is * smarter we can dispense with all the messy stuff that used to be * here. @@ -265,10 +259,9 @@ void dev_add_pack(struct packet_type *pt int hash; spin_lock_bh(&ptype_lock); - if (pt->type == htons(ETH_P_ALL)) { - netdev_nit++; + if (pt->type == htons(ETH_P_ALL)) list_add_rcu(&pt->list, &ptype_all); - } else { + else { hash = ntohs(pt->type) & 15; list_add_rcu(&pt->list, &ptype_base[hash]); } @@ -295,10 +288,9 @@ void __dev_remove_pack(struct packet_typ spin_lock_bh(&ptype_lock); - if (pt->type == htons(ETH_P_ALL)) { - netdev_nit--; + if (pt->type == htons(ETH_P_ALL)) head = &ptype_all; - } else + else head = &ptype_base[ntohs(pt->type) & 15]; list_for_each_entry(pt1, head, list) { @@ -817,7 +809,6 @@ static int default_rebuild_header(struct return 1; } - /** * dev_open - prepare an interface for use. * @dev: device to open @@ -1031,23 +1022,12 @@ void net_disable_timestamp(void) atomic_dec(&netstamp_needed); } -void __net_timestamp(struct sk_buff *skb) -{ - struct timeval tv; - - do_gettimeofday(&tv); - skb_set_timestamp(skb, &tv); -} -EXPORT_SYMBOL(__net_timestamp); - static inline void net_timestamp(struct sk_buff *skb) { if (atomic_read(&netstamp_needed)) __net_timestamp(skb); - else { - skb->tstamp.off_sec = 0; - skb->tstamp.off_usec = 0; - } + else + skb->tstamp.tv64 = 0; } /* @@ -1077,18 +1057,18 @@ static void dev_queue_xmit_nit(struct sk set by sender, so that the second statement is just protection against buggy protocols. */ - skb2->mac.raw = skb2->data; + skb_reset_mac_header(skb2); - if (skb2->nh.raw < skb2->data || - skb2->nh.raw > skb2->tail) { + if (skb_network_header(skb2) < skb2->data || + skb2->network_header > skb2->tail) { if (net_ratelimit()) printk(KERN_CRIT "protocol %04x is " "buggy, dev %s\n", skb2->protocol, dev->name); - skb2->nh.raw = skb2->data; + skb_reset_network_header(skb2); } - skb2->h.raw = skb2->nh.raw; + skb2->transport_header = skb2->network_header; skb2->pkt_type = PACKET_OUTGOING; ptype->func(skb2, skb->dev, ptype, skb->dev); } @@ -1167,7 +1147,7 @@ EXPORT_SYMBOL(netif_device_attach); int skb_checksum_help(struct sk_buff *skb) { __wsum csum; - int ret = 0, offset = skb->h.raw - skb->data; + int ret = 0, offset; if (skb->ip_summed == CHECKSUM_COMPLETE) goto out_set_summed; @@ -1183,15 +1163,16 @@ int skb_checksum_help(struct sk_buff *sk goto out; } + offset = skb->csum_start - skb_headroom(skb); BUG_ON(offset > (int)skb->len); csum = skb_checksum(skb, offset, skb->len-offset, 0); - offset = skb->tail - skb->h.raw; + offset = skb_headlen(skb) - offset; BUG_ON(offset <= 0); BUG_ON(skb->csum_offset + 2 > offset); - *(__sum16*)(skb->h.raw + skb->csum_offset) = csum_fold(csum); - + *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = + csum_fold(csum); out_set_summed: skb->ip_summed = CHECKSUM_NONE; out: @@ -1217,11 +1198,11 @@ struct sk_buff *skb_gso_segment(struct s BUG_ON(skb_shinfo(skb)->frag_list); - skb->mac.raw = skb->data; - skb->mac_len = skb->nh.raw - skb->data; + skb_reset_mac_header(skb); + skb->mac_len = skb->network_header - skb->mac_header; __skb_pull(skb, skb->mac_len); - if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + if (WARN_ON(skb->ip_summed != CHECKSUM_PARTIAL)) { if (skb_header_cloned(skb) && (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) return ERR_PTR(err); @@ -1235,7 +1216,8 @@ struct sk_buff *skb_gso_segment(struct s segs = ERR_PTR(err); if (err || skb_gso_ok(skb, features)) break; - __skb_push(skb, skb->data - skb->nh.raw); + __skb_push(skb, (skb->data - + skb_network_header(skb))); } segs = ptype->gso_segment(skb, features); break; @@ -1243,7 +1225,7 @@ struct sk_buff *skb_gso_segment(struct s } rcu_read_unlock(); - __skb_push(skb, skb->data - skb->mac.raw); + __skb_push(skb, skb->data - skb_mac_header(skb)); return segs; } @@ -1340,7 +1322,7 @@ static int dev_gso_segment(struct sk_buf int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { if (likely(!skb->next)) { - if (netdev_nit) + if (!list_empty(&ptype_all)) dev_queue_xmit_nit(skb, dev); if (netif_needs_gso(dev, skb)) { @@ -1442,12 +1424,16 @@ int dev_queue_xmit(struct sk_buff *skb) /* If packet is not checksummed and device does not support * checksumming for this protocol, complete checksumming here. */ - if (skb->ip_summed == CHECKSUM_PARTIAL && - (!(dev->features & NETIF_F_GEN_CSUM) && - (!(dev->features & NETIF_F_IP_CSUM) || - skb->protocol != htons(ETH_P_IP)))) - if (skb_checksum_help(skb)) - goto out_kfree_skb; + if (skb->ip_summed == CHECKSUM_PARTIAL) { + skb_set_transport_header(skb, skb->csum_start - + skb_headroom(skb)); + + if (!(dev->features & NETIF_F_GEN_CSUM) && + (!(dev->features & NETIF_F_IP_CSUM) || + skb->protocol != htons(ETH_P_IP))) + if (skb_checksum_help(skb)) + goto out_kfree_skb; + } gso: spin_lock_prefetch(&dev->queue_lock); @@ -1543,9 +1529,9 @@ out: Receiver routines =======================================================================*/ -int netdev_max_backlog = 1000; -int netdev_budget = 300; -int weight_p = 64; /* old backlog weight */ +int netdev_max_backlog __read_mostly = 1000; +int netdev_budget __read_mostly = 300; +int weight_p __read_mostly = 64; /* old backlog weight */ DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; @@ -1577,7 +1563,7 @@ int netif_rx(struct sk_buff *skb) if (netpoll_rx(skb)) return NET_RX_DROP; - if (!skb->tstamp.off_sec) + if (!skb->tstamp.tv64) net_timestamp(skb); /* @@ -1684,40 +1670,46 @@ static void net_tx_action(struct softirq } } -static __inline__ int deliver_skb(struct sk_buff *skb, - struct packet_type *pt_prev, - struct net_device *orig_dev) +static inline int deliver_skb(struct sk_buff *skb, + struct packet_type *pt_prev, + struct net_device *orig_dev) { atomic_inc(&skb->users); return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); } #if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE) -int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb); +/* These hooks defined here for ATM */ struct net_bridge; struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br, unsigned char *addr); -void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent); +void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent) __read_mostly; -static __inline__ int handle_bridge(struct sk_buff **pskb, - struct packet_type **pt_prev, int *ret, - struct net_device *orig_dev) +/* + * If bridge module is loaded call bridging hook. + * returns NULL if packet was consumed. + */ +struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p, + struct sk_buff *skb) __read_mostly; +static inline struct sk_buff *handle_bridge(struct sk_buff *skb, + struct packet_type **pt_prev, int *ret, + struct net_device *orig_dev) { struct net_bridge_port *port; - if ((*pskb)->pkt_type == PACKET_LOOPBACK || - (port = rcu_dereference((*pskb)->dev->br_port)) == NULL) - return 0; + if (skb->pkt_type == PACKET_LOOPBACK || + (port = rcu_dereference(skb->dev->br_port)) == NULL) + return skb; if (*pt_prev) { - *ret = deliver_skb(*pskb, *pt_prev, orig_dev); + *ret = deliver_skb(skb, *pt_prev, orig_dev); *pt_prev = NULL; } - return br_handle_frame_hook(port, pskb); + return br_handle_frame_hook(port, skb); } #else -#define handle_bridge(skb, pt_prev, ret, orig_dev) (0) +#define handle_bridge(skb, pt_prev, ret, orig_dev) (skb) #endif #ifdef CONFIG_NET_CLS_ACT @@ -1747,10 +1739,10 @@ static int ing_filter(struct sk_buff *sk skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_INGRESS); - spin_lock(&dev->queue_lock); + spin_lock(&dev->ingress_lock); if ((q = dev->qdisc_ingress) != NULL) result = q->enqueue(skb, q); - spin_unlock(&dev->queue_lock); + spin_unlock(&dev->ingress_lock); } @@ -1769,7 +1761,7 @@ int netif_receive_skb(struct sk_buff *sk if (skb->dev->poll && netpoll_rx(skb)) return NET_RX_DROP; - if (!skb->tstamp.off_sec) + if (!skb->tstamp.tv64) net_timestamp(skb); if (!skb->iif) @@ -1782,8 +1774,9 @@ int netif_receive_skb(struct sk_buff *sk __get_cpu_var(netdev_rx_stat).total++; - skb->h.raw = skb->nh.raw = skb->data; - skb->mac_len = skb->nh.raw - skb->mac.raw; + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->mac_len = skb->network_header - skb->mac_header; pt_prev = NULL; @@ -1823,7 +1816,8 @@ #ifdef CONFIG_NET_CLS_ACT ncls: #endif - if (handle_bridge(&skb, &pt_prev, &ret, orig_dev)) + skb = handle_bridge(skb, &pt_prev, &ret, orig_dev); + if (!skb) goto out; type = skb->protocol; @@ -2076,7 +2070,7 @@ #ifdef CONFIG_PROC_FS * This is invoked by the /proc filesystem handler to display a device * in detail. */ -static __inline__ struct net_device *dev_get_idx(loff_t pos) +static struct net_device *dev_get_idx(loff_t pos) { struct net_device *dev; loff_t i; @@ -2105,28 +2099,25 @@ void dev_seq_stop(struct seq_file *seq, static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) { - if (dev->get_stats) { - struct net_device_stats *stats = dev->get_stats(dev); - - seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu " - "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n", - dev->name, stats->rx_bytes, stats->rx_packets, - stats->rx_errors, - stats->rx_dropped + stats->rx_missed_errors, - stats->rx_fifo_errors, - stats->rx_length_errors + stats->rx_over_errors + - stats->rx_crc_errors + stats->rx_frame_errors, - stats->rx_compressed, stats->multicast, - stats->tx_bytes, stats->tx_packets, - stats->tx_errors, stats->tx_dropped, - stats->tx_fifo_errors, stats->collisions, - stats->tx_carrier_errors + - stats->tx_aborted_errors + - stats->tx_window_errors + - stats->tx_heartbeat_errors, - stats->tx_compressed); - } else - seq_printf(seq, "%6s: No statistics available.\n", dev->name); + struct net_device_stats *stats = dev->get_stats(dev); + + seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu " + "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n", + dev->name, stats->rx_bytes, stats->rx_packets, + stats->rx_errors, + stats->rx_dropped + stats->rx_missed_errors, + stats->rx_fifo_errors, + stats->rx_length_errors + stats->rx_over_errors + + stats->rx_crc_errors + stats->rx_frame_errors, + stats->rx_compressed, stats->multicast, + stats->tx_bytes, stats->tx_packets, + stats->tx_errors, stats->tx_dropped, + stats->tx_fifo_errors, stats->collisions, + stats->tx_carrier_errors + + stats->tx_aborted_errors + + stats->tx_window_errors + + stats->tx_heartbeat_errors, + stats->tx_compressed); } /* @@ -2185,7 +2176,7 @@ static int softnet_seq_show(struct seq_f return 0; } -static struct seq_operations dev_seq_ops = { +static const struct seq_operations dev_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, @@ -2205,7 +2196,7 @@ static const struct file_operations dev_ .release = seq_release, }; -static struct seq_operations softnet_seq_ops = { +static const struct seq_operations softnet_seq_ops = { .start = softnet_seq_start, .next = softnet_seq_next, .stop = softnet_seq_stop, @@ -2225,12 +2216,135 @@ static const struct file_operations soft .release = seq_release, }; -#ifdef CONFIG_WIRELESS_EXT -extern int wireless_proc_init(void); -#else -#define wireless_proc_init() 0 +static void *ptype_get_idx(loff_t pos) +{ + struct packet_type *pt = NULL; + loff_t i = 0; + int t; + + list_for_each_entry_rcu(pt, &ptype_all, list) { + if (i == pos) + return pt; + ++i; + } + + for (t = 0; t < 16; t++) { + list_for_each_entry_rcu(pt, &ptype_base[t], list) { + if (i == pos) + return pt; + ++i; + } + } + return NULL; +} + +static void *ptype_seq_start(struct seq_file *seq, loff_t *pos) +{ + rcu_read_lock(); + return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN; +} + +static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct packet_type *pt; + struct list_head *nxt; + int hash; + + ++*pos; + if (v == SEQ_START_TOKEN) + return ptype_get_idx(0); + + pt = v; + nxt = pt->list.next; + if (pt->type == htons(ETH_P_ALL)) { + if (nxt != &ptype_all) + goto found; + hash = 0; + nxt = ptype_base[0].next; + } else + hash = ntohs(pt->type) & 15; + + while (nxt == &ptype_base[hash]) { + if (++hash >= 16) + return NULL; + nxt = ptype_base[hash].next; + } +found: + return list_entry(nxt, struct packet_type, list); +} + +static void ptype_seq_stop(struct seq_file *seq, void *v) +{ + rcu_read_unlock(); +} + +static void ptype_seq_decode(struct seq_file *seq, void *sym) +{ +#ifdef CONFIG_KALLSYMS + unsigned long offset = 0, symsize; + const char *symname; + char *modname; + char namebuf[128]; + + symname = kallsyms_lookup((unsigned long)sym, &symsize, &offset, + &modname, namebuf); + + if (symname) { + char *delim = ":"; + + if (!modname) + modname = delim = ""; + seq_printf(seq, "%s%s%s%s+0x%lx", delim, modname, delim, + symname, offset); + return; + } #endif + seq_printf(seq, "[%p]", sym); +} + +static int ptype_seq_show(struct seq_file *seq, void *v) +{ + struct packet_type *pt = v; + + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Type Device Function\n"); + else { + if (pt->type == htons(ETH_P_ALL)) + seq_puts(seq, "ALL "); + else + seq_printf(seq, "%04x", ntohs(pt->type)); + + seq_printf(seq, " %-8s ", + pt->dev ? pt->dev->name : ""); + ptype_seq_decode(seq, pt->func); + seq_putc(seq, '\n'); + } + + return 0; +} + +static const struct seq_operations ptype_seq_ops = { + .start = ptype_seq_start, + .next = ptype_seq_next, + .stop = ptype_seq_stop, + .show = ptype_seq_show, +}; + +static int ptype_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ptype_seq_ops); +} + +static const struct file_operations ptype_seq_fops = { + .owner = THIS_MODULE, + .open = ptype_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + static int __init dev_proc_init(void) { int rc = -ENOMEM; @@ -2239,13 +2353,18 @@ static int __init dev_proc_init(void) goto out; if (!proc_net_fops_create("softnet_stat", S_IRUGO, &softnet_seq_fops)) goto out_dev; - if (wireless_proc_init()) + if (!proc_net_fops_create("ptype", S_IRUGO, &ptype_seq_fops)) + goto out_dev2; + + if (wext_proc_init()) goto out_softnet; rc = 0; out: return rc; out_softnet: proc_net_remove("softnet_stat"); +out_dev2: + proc_net_remove("ptype"); out_dev: proc_net_remove("dev"); goto out; @@ -2795,29 +2914,9 @@ int dev_ioctl(unsigned int cmd, void __u ret = -EFAULT; return ret; } -#ifdef CONFIG_WIRELESS_EXT /* Take care of Wireless Extensions */ - if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { - /* If command is `set a parameter', or - * `get the encoding parameters', check if - * the user has the right to do it */ - if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE - || cmd == SIOCGIWENCODEEXT) { - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - } - dev_load(ifr.ifr_name); - rtnl_lock(); - /* Follow me in net/core/wireless.c */ - ret = wireless_process_ioctl(&ifr, cmd); - rtnl_unlock(); - if (IW_IS_GET(cmd) && - copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - return ret; - } -#endif /* CONFIG_WIRELESS_EXT */ + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) + return wext_handle_ioctl(&ifr, cmd, arg); return -EINVAL; } } @@ -2847,7 +2946,7 @@ static int dev_boot_phase = 1; static DEFINE_SPINLOCK(net_todo_list_lock); static struct list_head net_todo_list = LIST_HEAD_INIT(net_todo_list); -static inline void net_set_todo(struct net_device *dev) +static void net_set_todo(struct net_device *dev) { spin_lock(&net_todo_list_lock); list_add_tail(&dev->todo_list, &net_todo_list); @@ -2888,9 +2987,7 @@ int register_netdevice(struct net_device spin_lock_init(&dev->queue_lock); spin_lock_init(&dev->_xmit_lock); dev->xmit_lock_owner = -1; -#ifdef CONFIG_NET_CLS_ACT spin_lock_init(&dev->ingress_lock); -#endif dev->iflink = -1; @@ -3002,7 +3099,7 @@ out: * chain. 0 is returned on success. A negative errno code is returned * on a failure to set up the device, or if the name is a duplicate. * - * This is a wrapper around register_netdev that takes the rtnl semaphore + * This is a wrapper around register_netdevice that takes the rtnl semaphore * and expands the device name if you passed a format string to * alloc_netdev. */ @@ -3157,6 +3254,11 @@ out: mutex_unlock(&net_todo_run_mutex); } +static struct net_device_stats *internal_stats(struct net_device *dev) +{ + return &dev->stats; +} + /** * alloc_netdev - allocate network device * @sizeof_priv: size of private data to allocate space for @@ -3192,6 +3294,7 @@ struct net_device *alloc_netdev(int size if (sizeof_priv) dev->priv = netdev_priv(dev); + dev->get_stats = internal_stats; setup(dev); strcpy(dev->name, name); return dev; diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 56b310c..7d57bf7 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -264,7 +264,7 @@ static int dev_mc_seq_show(struct seq_fi return 0; } -static struct seq_operations dev_mc_seq_ops = { +static const struct seq_operations dev_mc_seq_ops = { .start = dev_mc_seq_start, .next = dev_mc_seq_next, .stop = dev_mc_seq_stop, diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 6168edd..8d5e5a0 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -836,7 +836,7 @@ int dev_ethtool(struct ifreq *ifr) return -EPERM; } - if(dev->ethtool_ops->begin) + if (dev->ethtool_ops->begin) if ((rc = dev->ethtool_ops->begin(dev)) < 0) return rc; @@ -952,7 +952,7 @@ int dev_ethtool(struct ifreq *ifr) rc = -EOPNOTSUPP; } - if(dev->ethtool_ops->complete) + if (dev->ethtool_ops->complete) dev->ethtool_ops->complete(dev); if (old_features != dev->features) diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 7174ced..8c5474e 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -44,6 +44,12 @@ static void rules_ops_put(struct fib_rul module_put(ops->owner); } +static void flush_route_cache(struct fib_rules_ops *ops) +{ + if (ops->flush_cache) + ops->flush_cache(); +} + int fib_rules_register(struct fib_rules_ops *ops) { int err = -EEXIST; @@ -132,10 +138,25 @@ int fib_rules_lookup(struct fib_rules_op rcu_read_lock(); list_for_each_entry_rcu(rule, ops->rules_list, list) { +jumped: if (!fib_rule_match(rule, ops, fl, flags)) continue; - err = ops->action(rule, fl, flags, arg); + if (rule->action == FR_ACT_GOTO) { + struct fib_rule *target; + + target = rcu_dereference(rule->ctarget); + if (target == NULL) { + continue; + } else { + rule = target; + goto jumped; + } + } else if (rule->action == FR_ACT_NOP) + continue; + else + err = ops->action(rule, fl, flags, arg); + if (err != -EAGAIN) { fib_rule_get(rule); arg->rule = rule; @@ -174,13 +195,13 @@ errout: return err; } -int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) +static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct fib_rule_hdr *frh = nlmsg_data(nlh); struct fib_rules_ops *ops = NULL; struct fib_rule *rule, *r, *last = NULL; struct nlattr *tb[FRA_MAX+1]; - int err = -EINVAL; + int err = -EINVAL, unresolved = 0; if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) goto errout; @@ -237,6 +258,28 @@ int fib_nl_newrule(struct sk_buff *skb, if (!rule->pref && ops->default_pref) rule->pref = ops->default_pref(); + err = -EINVAL; + if (tb[FRA_GOTO]) { + if (rule->action != FR_ACT_GOTO) + goto errout_free; + + rule->target = nla_get_u32(tb[FRA_GOTO]); + /* Backward jumps are prohibited to avoid endless loops */ + if (rule->target <= rule->pref) + goto errout_free; + + list_for_each_entry(r, ops->rules_list, list) { + if (r->pref == rule->target) { + rule->ctarget = r; + break; + } + } + + if (rule->ctarget == NULL) + unresolved = 1; + } else if (rule->action == FR_ACT_GOTO) + goto errout_free; + err = ops->configure(rule, skb, nlh, frh, tb); if (err < 0) goto errout_free; @@ -249,12 +292,35 @@ int fib_nl_newrule(struct sk_buff *skb, fib_rule_get(rule); + if (ops->unresolved_rules) { + /* + * There are unresolved goto rules in the list, check if + * any of them are pointing to this new rule. + */ + list_for_each_entry(r, ops->rules_list, list) { + if (r->action == FR_ACT_GOTO && + r->target == rule->pref) { + BUG_ON(r->ctarget != NULL); + rcu_assign_pointer(r->ctarget, rule); + if (--ops->unresolved_rules == 0) + break; + } + } + } + + if (rule->action == FR_ACT_GOTO) + ops->nr_goto_rules++; + + if (unresolved) + ops->unresolved_rules++; + if (last) list_add_rcu(&rule->list, &last->list); else list_add_rcu(&rule->list, ops->rules_list); notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid); + flush_route_cache(ops); rules_ops_put(ops); return 0; @@ -265,11 +331,11 @@ errout: return err; } -int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) +static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct fib_rule_hdr *frh = nlmsg_data(nlh); struct fib_rules_ops *ops = NULL; - struct fib_rule *rule; + struct fib_rule *rule, *tmp; struct nlattr *tb[FRA_MAX+1]; int err = -EINVAL; @@ -322,10 +388,30 @@ int fib_nl_delrule(struct sk_buff *skb, } list_del_rcu(&rule->list); + + if (rule->action == FR_ACT_GOTO) + ops->nr_goto_rules--; + + /* + * Check if this rule is a target to any of them. If so, + * disable them. As this operation is eventually very + * expensive, it is only performed if goto rules have + * actually been added. + */ + if (ops->nr_goto_rules > 0) { + list_for_each_entry(tmp, ops->rules_list, list) { + if (tmp->ctarget == rule) { + rcu_assign_pointer(tmp->ctarget, NULL); + ops->unresolved_rules++; + } + } + } + synchronize_rcu(); notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).pid); fib_rule_put(rule); + flush_route_cache(ops); rules_ops_put(ops); return 0; } @@ -371,9 +457,16 @@ static int fib_nl_fill_rule(struct sk_bu frh->action = rule->action; frh->flags = rule->flags; - if (rule->ifname[0]) + if (rule->action == FR_ACT_GOTO && rule->ctarget == NULL) + frh->flags |= FIB_RULE_UNRESOLVED; + + if (rule->ifname[0]) { NLA_PUT_STRING(skb, FRA_IFNAME, rule->ifname); + if (rule->ifindex == -1) + frh->flags |= FIB_RULE_DEV_DETACHED; + } + if (rule->pref) NLA_PUT_U32(skb, FRA_PRIORITY, rule->pref); @@ -383,6 +476,9 @@ static int fib_nl_fill_rule(struct sk_bu if (rule->mark_mask || rule->mark) NLA_PUT_U32(skb, FRA_FWMASK, rule->mark_mask); + if (rule->target) + NLA_PUT_U32(skb, FRA_GOTO, rule->target); + if (ops->fill(rule, skb, nlh, frh) < 0) goto nla_put_failure; @@ -393,19 +489,14 @@ nla_put_failure: return -EMSGSIZE; } -int fib_rules_dump(struct sk_buff *skb, struct netlink_callback *cb, int family) +static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, + struct fib_rules_ops *ops) { int idx = 0; struct fib_rule *rule; - struct fib_rules_ops *ops; - - ops = lookup_rules_ops(family); - if (ops == NULL) - return -EAFNOSUPPORT; - rcu_read_lock(); - list_for_each_entry_rcu(rule, ops->rules_list, list) { - if (idx < cb->args[0]) + list_for_each_entry(rule, ops->rules_list, list) { + if (idx < cb->args[1]) goto skip; if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).pid, @@ -415,14 +506,44 @@ int fib_rules_dump(struct sk_buff *skb, skip: idx++; } - rcu_read_unlock(); - cb->args[0] = idx; + cb->args[1] = idx; rules_ops_put(ops); return skb->len; } -EXPORT_SYMBOL_GPL(fib_rules_dump); +static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct fib_rules_ops *ops; + int idx = 0, family; + + family = rtnl_msg_family(cb->nlh); + if (family != AF_UNSPEC) { + /* Protocol specific dump request */ + ops = lookup_rules_ops(family); + if (ops == NULL) + return -EAFNOSUPPORT; + + return dump_rules(skb, cb, ops); + } + + rcu_read_lock(); + list_for_each_entry_rcu(ops, &rules_ops, list) { + if (idx < cb->args[0] || !try_module_get(ops->owner)) + goto skip; + + if (dump_rules(skb, cb, ops) < 0) + break; + + cb->args[1] = 0; + skip: + idx++; + } + rcu_read_unlock(); + cb->args[0] = idx; + + return skb->len; +} static void notify_rule_change(int event, struct fib_rule *rule, struct fib_rules_ops *ops, struct nlmsghdr *nlh, @@ -501,6 +622,10 @@ static struct notifier_block fib_rules_n static int __init fib_rules_init(void) { + rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL); + rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL); + rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule); + return register_netdevice_notifier(&fib_rules_notifier); } diff --git a/net/core/filter.c b/net/core/filter.c index 8d185a0..bd903aa 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -42,11 +42,11 @@ static void *__load_pointer(struct sk_bu u8 *ptr = NULL; if (k >= SKF_NET_OFF) - ptr = skb->nh.raw + k - SKF_NET_OFF; + ptr = skb_network_header(skb) + k - SKF_NET_OFF; else if (k >= SKF_LL_OFF) - ptr = skb->mac.raw + k - SKF_LL_OFF; + ptr = skb_mac_header(skb) + k - SKF_LL_OFF; - if (ptr >= skb->head && ptr < skb->tail) + if (ptr >= skb->head && ptr < skb_tail_pointer(skb)) return ptr; return NULL; } diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index 259473d..bcc2559 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -61,7 +61,7 @@ gnet_stats_start_copy_compat(struct sk_b spin_lock_bh(lock); d->lock = lock; if (type) - d->tail = (struct rtattr *) skb->tail; + d->tail = (struct rtattr *)skb_tail_pointer(skb); d->skb = skb; d->compat_tc_stats = tc_stats_type; d->compat_xstats = xstats_type; @@ -212,7 +212,7 @@ int gnet_stats_finish_copy(struct gnet_dump *d) { if (d->tail) - d->tail->rta_len = d->skb->tail - (u8 *) d->tail; + d->tail->rta_len = skb_tail_pointer(d->skb) - (u8 *)d->tail; if (d->compat_tc_stats) if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats, diff --git a/net/core/link_watch.c b/net/core/link_watch.c index 8b45c9d..e3c26a9 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -79,7 +79,7 @@ static void rfc2863_policy(struct net_de case IF_LINK_MODE_DEFAULT: default: break; - }; + } dev->operstate = operstate; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 841e3f3..6f3bb73 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1125,7 +1125,7 @@ int neigh_compat_output(struct sk_buff * { struct net_device *dev = skb->dev; - __skb_pull(skb, skb->nh.raw - skb->data); + __skb_pull(skb, skb_network_offset(skb)); if (dev->hard_header && dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, @@ -1147,7 +1147,7 @@ int neigh_resolve_output(struct sk_buff if (!dst || !(neigh = dst->neighbour)) goto discard; - __skb_pull(skb, skb->nh.raw - skb->data); + __skb_pull(skb, skb_network_offset(skb)); if (!neigh_event_send(neigh, skb)) { int err; @@ -1190,7 +1190,7 @@ int neigh_connected_output(struct sk_buf struct neighbour *neigh = dst->neighbour; struct net_device *dev = neigh->dev; - __skb_pull(skb, skb->nh.raw - skb->data); + __skb_pull(skb, skb_network_offset(skb)); read_lock_bh(&neigh->lock); err = dev->hard_header(skb, dev, ntohs(skb->protocol), @@ -1441,7 +1441,7 @@ int neigh_table_clear(struct neigh_table return 0; } -int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct ndmsg *ndm; struct nlattr *dst_attr; @@ -1506,7 +1506,7 @@ out: return err; } -int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct ndmsg *ndm; struct nlattr *tb[NDA_MAX+1]; @@ -1786,7 +1786,7 @@ static struct nla_policy nl_ntbl_parm_po [NDTPA_LOCKTIME] = { .type = NLA_U64 }, }; -int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct neigh_table *tbl; struct ndtmsg *ndtmsg; @@ -1910,7 +1910,7 @@ errout: return err; } -int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) +static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) { int family, tidx, nidx = 0; int tbl_skip = cb->args[0]; @@ -2034,7 +2034,7 @@ out: return rc; } -int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) +static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) { struct neigh_table *tbl; int t, family, s_t; @@ -2393,7 +2393,7 @@ static int neigh_stat_seq_show(struct se return 0; } -static struct seq_operations neigh_stat_seq_ops = { +static const struct seq_operations neigh_stat_seq_ops = { .start = neigh_stat_seq_start, .next = neigh_stat_seq_next, .stop = neigh_stat_seq_stop, @@ -2746,14 +2746,26 @@ void neigh_sysctl_unregister(struct neig #endif /* CONFIG_SYSCTL */ +static int __init neigh_init(void) +{ + rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL); + rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info); + + rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info); + rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL); + + return 0; +} + +subsys_initcall(neigh_init); + EXPORT_SYMBOL(__neigh_event_send); EXPORT_SYMBOL(neigh_changeaddr); EXPORT_SYMBOL(neigh_compat_output); EXPORT_SYMBOL(neigh_connected_output); EXPORT_SYMBOL(neigh_create); -EXPORT_SYMBOL(neigh_delete); EXPORT_SYMBOL(neigh_destroy); -EXPORT_SYMBOL(neigh_dump_info); EXPORT_SYMBOL(neigh_event_ns); EXPORT_SYMBOL(neigh_ifdown); EXPORT_SYMBOL(neigh_lookup); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 4cbb129..b21307b 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -352,8 +352,8 @@ static ssize_t wireless_show(struct devi read_lock(&dev_base_lock); if (dev_isalive(dev)) { - if(dev->wireless_handlers && - dev->wireless_handlers->get_wireless_stats) + if (dev->wireless_handlers && + dev->wireless_handlers->get_wireless_stats) iw = dev->wireless_handlers->get_wireless_stats(dev); if (iw != NULL) ret = (*format)(iw, buf); @@ -412,20 +412,25 @@ static int netdev_uevent(struct device * int num_envp, char *buf, int size) { struct net_device *dev = to_net_dev(d); - int i = 0; - int n; + int retval, len = 0, i = 0; /* pass interface to uevent. */ - envp[i++] = buf; - n = snprintf(buf, size, "INTERFACE=%s", dev->name) + 1; - buf += n; - size -= n; - - if ((size <= 0) || (i >= num_envp)) - return -ENOMEM; - + retval = add_uevent_var(envp, num_envp, &i, + buf, size, &len, + "INTERFACE=%s", dev->name); + if (retval) + goto exit; + + /* pass ifindex to uevent. + * ifindex is useful as it won't change (interface name may change) + * and is what RtNetlink uses natively. */ + retval = add_uevent_var(envp, num_envp, &i, + buf, size, &len, + "IFINDEX=%d", dev->ifindex); + +exit: envp[i] = NULL; - return 0; + return retval; } #endif diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 4581ece..b316435 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -86,7 +86,7 @@ static __sum16 checksum_udp(struct sk_bu { __wsum psum; - if (uh->check == 0 || skb->ip_summed == CHECKSUM_UNNECESSARY) + if (uh->check == 0 || skb_csum_unnecessary(skb)) return 0; psum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0); @@ -293,10 +293,12 @@ void netpoll_send_udp(struct netpoll *np if (!skb) return; - memcpy(skb->data, msg, len); + skb_copy_to_linear_data(skb, msg, len); skb->len += len; - skb->h.uh = udph = (struct udphdr *) skb_push(skb, sizeof(*udph)); + skb_push(skb, sizeof(*udph)); + skb_reset_transport_header(skb); + udph = udp_hdr(skb); udph->source = htons(np->local_port); udph->dest = htons(np->remote_port); udph->len = htons(udp_len); @@ -308,7 +310,9 @@ void netpoll_send_udp(struct netpoll *np if (udph->check == 0) udph->check = CSUM_MANGLED_0; - skb->nh.iph = iph = (struct iphdr *)skb_push(skb, sizeof(*iph)); + skb_push(skb, sizeof(*iph)); + skb_reset_network_header(skb); + iph = ip_hdr(skb); /* iph->version = 4; iph->ihl = 5; */ put_unaligned(0x45, (unsigned char *)iph); @@ -324,7 +328,7 @@ void netpoll_send_udp(struct netpoll *np iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = eth->h_proto = htons(ETH_P_IP); memcpy(eth->h_source, np->local_mac, 6); memcpy(eth->h_dest, np->remote_mac, 6); @@ -359,8 +363,9 @@ static void arp_reply(struct sk_buff *sk (2 * sizeof(u32))))) return; - skb->h.raw = skb->nh.raw = skb->data; - arp = skb->nh.arph; + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + arp = arp_hdr(skb); if ((arp->ar_hrd != htons(ARPHRD_ETHER) && arp->ar_hrd != htons(ARPHRD_IEEE802)) || @@ -389,7 +394,7 @@ static void arp_reply(struct sk_buff *sk if (!send_skb) return; - send_skb->nh.raw = send_skb->data; + skb_reset_network_header(send_skb); arp = (struct arphdr *) skb_put(send_skb, size); send_skb->dev = skb->dev; send_skb->protocol = htons(ETH_P_ARP); @@ -443,7 +448,7 @@ int __netpoll_rx(struct sk_buff *skb) goto out; /* check if netpoll clients need ARP */ - if (skb->protocol == __constant_htons(ETH_P_ARP) && + if (skb->protocol == htons(ETH_P_ARP) && atomic_read(&trapped)) { skb_queue_tail(&npi->arp_tx, skb); return 1; diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 4b01496..b92a322 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -164,14 +164,11 @@ #include #define VERSION "pktgen v2.68: Packet Generator for packet performance testing.\n" -/* #define PG_DEBUG(a) a */ -#define PG_DEBUG(a) - /* The buckets are exponential in 'width' */ #define LAT_BUCKETS_MAX 32 #define IP_NAME_SZ 32 #define MAX_MPLS_LABELS 16 /* This is the max label stack depth */ -#define MPLS_STACK_BOTTOM __constant_htonl(0x00000100) +#define MPLS_STACK_BOTTOM htonl(0x00000100) /* Device flag bits */ #define F_IPSRC_RND (1<<0) /* IP-Src Random */ @@ -214,15 +211,11 @@ struct flow_state { }; struct pktgen_dev { - /* * Try to keep frequent/infrequent used vars. separated. */ - - char ifname[IFNAMSIZ]; - char result[512]; - - struct pktgen_thread *pg_thread; /* the owner */ + struct proc_dir_entry *entry; /* proc file */ + struct pktgen_thread *pg_thread;/* the owner */ struct list_head list; /* Used for chaining in the thread's run-queue */ int running; /* if this changes to false, the test will stop */ @@ -349,6 +342,8 @@ struct pktgen_dev { unsigned cflows; /* Concurrent flows (config) */ unsigned lflow; /* Flow length (config) */ unsigned nflows; /* accumulated flows (stats) */ + + char result[512]; }; struct pktgen_hdr { @@ -468,17 +463,6 @@ #endif return tmp; } -static inline u32 pktgen_random(void) -{ -#if 0 - __u32 n; - get_random_bytes(&n, 4); - return n; -#else - return net_random(); -#endif -} - static inline __u64 getCurMs(void) { struct timeval tv; @@ -512,7 +496,7 @@ static void pktgen_stop_all_threads_ifs( static int pktgen_stop_device(struct pktgen_dev *pkt_dev); static void pktgen_stop(struct pktgen_thread *t); static void pktgen_clear_counters(struct pktgen_dev *pkt_dev); -static int pktgen_mark_device(const char *ifname); + static unsigned int scan_ip6(const char *s, char ip[16]); static unsigned int fmt_ip6(char *s, const char ip[16]); @@ -606,7 +590,7 @@ static int pktgen_if_show(struct seq_fil " frags: %d delay: %u clone_skb: %d ifname: %s\n", pkt_dev->nfrags, 1000 * pkt_dev->delay_us + pkt_dev->delay_ns, - pkt_dev->clone_skb, pkt_dev->ifname); + pkt_dev->clone_skb, pkt_dev->odev->name); seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows, pkt_dev->lflow); @@ -661,7 +645,7 @@ static int pktgen_if_show(struct seq_fil if (pkt_dev->nr_labels) { unsigned i; seq_printf(seq, " mpls: "); - for(i = 0; i < pkt_dev->nr_labels; i++) + for (i = 0; i < pkt_dev->nr_labels; i++) seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]), i == pkt_dev->nr_labels-1 ? "\n" : ", "); } @@ -766,7 +750,7 @@ static int hex32_arg(const char __user * int i = 0; *num = 0; - for(; i < maxlen; i++) { + for (; i < maxlen; i++) { char c; *num <<= 4; if (get_user(c, &user_buffer[i])) @@ -802,7 +786,7 @@ static int count_trail_chars(const char break; default: goto done; - }; + } } done: return i; @@ -845,7 +829,7 @@ static int strn_len(const char __user * break; default: break; - }; + } } done_str: return i; @@ -874,7 +858,7 @@ static ssize_t get_labels(const char __u n++; if (n >= MAX_MPLS_LABELS) return -E2BIG; - } while(c == ','); + } while (c == ','); pkt_dev->nr_labels = n; return i; @@ -1503,7 +1487,7 @@ static ssize_t pktgen_if_write(struct fi if (len < 0) { return len; } i += len; offset = sprintf(pg_result, "OK: mpls="); - for(n = 0; n < pkt_dev->nr_labels; n++) + for (n = 0; n < pkt_dev->nr_labels; n++) offset += sprintf(pg_result + offset, "%08x%s", ntohl(pkt_dev->labels[n]), n == pkt_dev->nr_labels-1 ? "" : ","); @@ -1697,13 +1681,13 @@ static int pktgen_thread_show(struct seq if_lock(t); list_for_each_entry(pkt_dev, &t->if_list, list) if (pkt_dev->running) - seq_printf(seq, "%s ", pkt_dev->ifname); + seq_printf(seq, "%s ", pkt_dev->odev->name); seq_printf(seq, "\nStopped: "); list_for_each_entry(pkt_dev, &t->if_list, list) if (!pkt_dev->running) - seq_printf(seq, "%s ", pkt_dev->ifname); + seq_printf(seq, "%s ", pkt_dev->odev->name); if (t->result[0]) seq_printf(seq, "\nResult: %s\n", t->result); @@ -1849,16 +1833,14 @@ static struct pktgen_dev *__pktgen_NN_th /* * mark a device for removal */ -static int pktgen_mark_device(const char *ifname) +static void pktgen_mark_device(const char *ifname) { struct pktgen_dev *pkt_dev = NULL; const int max_tries = 10, msec_per_try = 125; int i = 0; - int ret = 0; mutex_lock(&pktgen_thread_lock); - PG_DEBUG(printk("pktgen: pktgen_mark_device marking %s for removal\n", - ifname)); + pr_debug("pktgen: pktgen_mark_device marking %s for removal\n", ifname); while (1) { @@ -1867,8 +1849,8 @@ static int pktgen_mark_device(const char break; /* success */ mutex_unlock(&pktgen_thread_lock); - PG_DEBUG(printk("pktgen: pktgen_mark_device waiting for %s " - "to disappear....\n", ifname)); + pr_debug("pktgen: pktgen_mark_device waiting for %s " + "to disappear....\n", ifname); schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try)); mutex_lock(&pktgen_thread_lock); @@ -1876,79 +1858,91 @@ static int pktgen_mark_device(const char printk("pktgen_mark_device: timed out after waiting " "%d msec for device %s to be removed\n", msec_per_try * i, ifname); - ret = 1; break; } } mutex_unlock(&pktgen_thread_lock); +} - return ret; +static void pktgen_change_name(struct net_device *dev) +{ + struct pktgen_thread *t; + + list_for_each_entry(t, &pktgen_threads, th_list) { + struct pktgen_dev *pkt_dev; + + list_for_each_entry(pkt_dev, &t->if_list, list) { + if (pkt_dev->odev != dev) + continue; + + remove_proc_entry(pkt_dev->entry->name, pg_proc_dir); + + pkt_dev->entry = create_proc_entry(dev->name, 0600, + pg_proc_dir); + if (!pkt_dev->entry) + printk(KERN_ERR "pktgen: can't move proc " + " entry for '%s'\n", dev->name); + break; + } + } } static int pktgen_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)(ptr); + struct net_device *dev = ptr; /* It is OK that we do not hold the group lock right now, * as we run under the RTNL lock. */ switch (event) { - case NETDEV_CHANGEADDR: - case NETDEV_GOING_DOWN: - case NETDEV_DOWN: - case NETDEV_UP: - /* Ignore for now */ + case NETDEV_CHANGENAME: + pktgen_change_name(dev); break; case NETDEV_UNREGISTER: pktgen_mark_device(dev->name); break; - }; + } return NOTIFY_DONE; } /* Associate pktgen_dev with a device. */ -static struct net_device *pktgen_setup_dev(struct pktgen_dev *pkt_dev) +static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname) { struct net_device *odev; + int err; /* Clean old setups */ - if (pkt_dev->odev) { dev_put(pkt_dev->odev); pkt_dev->odev = NULL; } - odev = dev_get_by_name(pkt_dev->ifname); - + odev = dev_get_by_name(ifname); if (!odev) { - printk("pktgen: no such netdevice: \"%s\"\n", pkt_dev->ifname); - goto out; + printk("pktgen: no such netdevice: \"%s\"\n", ifname); + return -ENODEV; } + if (odev->type != ARPHRD_ETHER) { - printk("pktgen: not an ethernet device: \"%s\"\n", - pkt_dev->ifname); - goto out_put; - } - if (!netif_running(odev)) { - printk("pktgen: device is down: \"%s\"\n", pkt_dev->ifname); - goto out_put; + printk("pktgen: not an ethernet device: \"%s\"\n", ifname); + err = -EINVAL; + } else if (!netif_running(odev)) { + printk("pktgen: device is down: \"%s\"\n", ifname); + err = -ENETDOWN; + } else { + pkt_dev->odev = odev; + return 0; } - pkt_dev->odev = odev; - return pkt_dev->odev; - -out_put: dev_put(odev); -out: - return NULL; - + return err; } /* Read pkt_dev from the interface and set up internal pktgen_dev @@ -1956,10 +1950,6 @@ out: */ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) { - /* Try once more, just in case it works now. */ - if (!pkt_dev->odev) - pktgen_setup_dev(pkt_dev); - if (!pkt_dev->odev) { printk("pktgen: ERROR: pkt_dev->odev == NULL in setup_inject.\n"); sprintf(pkt_dev->result, @@ -2096,7 +2086,7 @@ static void mod_cur_headers(struct pktge int flow = 0; if (pkt_dev->cflows) { - flow = pktgen_random() % pkt_dev->cflows; + flow = random32() % pkt_dev->cflows; if (pkt_dev->flows[flow].count > pkt_dev->lflow) pkt_dev->flows[flow].count = 0; @@ -2108,7 +2098,7 @@ static void mod_cur_headers(struct pktge __u32 tmp; if (pkt_dev->flags & F_MACSRC_RND) - mc = pktgen_random() % (pkt_dev->src_mac_count); + mc = random32() % pkt_dev->src_mac_count; else { mc = pkt_dev->cur_src_mac_offset++; if (pkt_dev->cur_src_mac_offset > @@ -2134,7 +2124,7 @@ static void mod_cur_headers(struct pktge __u32 tmp; if (pkt_dev->flags & F_MACDST_RND) - mc = pktgen_random() % (pkt_dev->dst_mac_count); + mc = random32() % pkt_dev->dst_mac_count; else { mc = pkt_dev->cur_dst_mac_offset++; @@ -2158,27 +2148,26 @@ static void mod_cur_headers(struct pktge if (pkt_dev->flags & F_MPLS_RND) { unsigned i; - for(i = 0; i < pkt_dev->nr_labels; i++) + for (i = 0; i < pkt_dev->nr_labels; i++) if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM) pkt_dev->labels[i] = MPLS_STACK_BOTTOM | - ((__force __be32)pktgen_random() & + ((__force __be32)random32() & htonl(0x000fffff)); } if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) { - pkt_dev->vlan_id = pktgen_random() % 4096; + pkt_dev->vlan_id = random32() & (4096-1); } if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) { - pkt_dev->svlan_id = pktgen_random() % 4096; + pkt_dev->svlan_id = random32() & (4096 - 1); } if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) { if (pkt_dev->flags & F_UDPSRC_RND) - pkt_dev->cur_udp_src = - ((pktgen_random() % - (pkt_dev->udp_src_max - pkt_dev->udp_src_min)) + - pkt_dev->udp_src_min); + pkt_dev->cur_udp_src = random32() % + (pkt_dev->udp_src_max - pkt_dev->udp_src_min) + + pkt_dev->udp_src_min; else { pkt_dev->cur_udp_src++; @@ -2189,10 +2178,9 @@ static void mod_cur_headers(struct pktge if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) { if (pkt_dev->flags & F_UDPDST_RND) { - pkt_dev->cur_udp_dst = - ((pktgen_random() % - (pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)) + - pkt_dev->udp_dst_min); + pkt_dev->cur_udp_dst = random32() % + (pkt_dev->udp_dst_max - pkt_dev->udp_dst_min) + + pkt_dev->udp_dst_min; } else { pkt_dev->cur_udp_dst++; if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max) @@ -2207,7 +2195,7 @@ static void mod_cur_headers(struct pktge saddr_max))) { __u32 t; if (pkt_dev->flags & F_IPSRC_RND) - t = ((pktgen_random() % (imx - imn)) + imn); + t = random32() % (imx - imn) + imn; else { t = ntohl(pkt_dev->cur_saddr); t++; @@ -2228,14 +2216,13 @@ static void mod_cur_headers(struct pktge __be32 s; if (pkt_dev->flags & F_IPDST_RND) { - t = pktgen_random() % (imx - imn) + imn; + t = random32() % (imx - imn) + imn; s = htonl(t); while (LOOPBACK(s) || MULTICAST(s) || BADCLASS(s) || ZERONET(s) || LOCAL_MCAST(s)) { - t = (pktgen_random() % - (imx - imn)) + imn; + t = random32() % (imx - imn) + imn; s = htonl(t); } pkt_dev->cur_daddr = s; @@ -2267,7 +2254,7 @@ static void mod_cur_headers(struct pktge for (i = 0; i < 4; i++) { pkt_dev->cur_in6_daddr.s6_addr32[i] = - (((__force __be32)pktgen_random() | + (((__force __be32)random32() | pkt_dev->min_in6_daddr.s6_addr32[i]) & pkt_dev->max_in6_daddr.s6_addr32[i]); } @@ -2277,9 +2264,9 @@ static void mod_cur_headers(struct pktge if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) { __u32 t; if (pkt_dev->flags & F_TXSIZE_RND) { - t = ((pktgen_random() % - (pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)) - + pkt_dev->min_pkt_size); + t = random32() % + (pkt_dev->max_pkt_size - pkt_dev->min_pkt_size) + + pkt_dev->min_pkt_size; } else { t = pkt_dev->cur_pkt_size + 1; if (t > pkt_dev->max_pkt_size) @@ -2294,7 +2281,7 @@ static void mod_cur_headers(struct pktge static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev) { unsigned i; - for(i = 0; i < pkt_dev->nr_labels; i++) { + for (i = 0; i < pkt_dev->nr_labels; i++) { *mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM; } mpls--; @@ -2316,7 +2303,7 @@ static struct sk_buff *fill_packet_ipv4( int datalen, iplen; struct iphdr *iph; struct pktgen_hdr *pgh = NULL; - __be16 protocol = __constant_htons(ETH_P_IP); + __be16 protocol = htons(ETH_P_IP); __be32 *mpls; __be16 *vlan_tci = NULL; /* Encapsulates priority and VLAN ID */ __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */ @@ -2325,10 +2312,10 @@ static struct sk_buff *fill_packet_ipv4( if (pkt_dev->nr_labels) - protocol = __constant_htons(ETH_P_MPLS_UC); + protocol = htons(ETH_P_MPLS_UC); if (pkt_dev->vlan_id != 0xffff) - protocol = __constant_htons(ETH_P_8021Q); + protocol = htons(ETH_P_8021Q); /* Update any of the values, used when we're incrementing various * fields. @@ -2354,24 +2341,28 @@ static struct sk_buff *fill_packet_ipv4( mpls_push(mpls, pkt_dev); if (pkt_dev->vlan_id != 0xffff) { - if(pkt_dev->svlan_id != 0xffff) { + if (pkt_dev->svlan_id != 0xffff) { svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16)); *svlan_tci = build_tci(pkt_dev->svlan_id, pkt_dev->svlan_cfi, pkt_dev->svlan_p); svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16)); - *svlan_encapsulated_proto = __constant_htons(ETH_P_8021Q); + *svlan_encapsulated_proto = htons(ETH_P_8021Q); } vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16)); *vlan_tci = build_tci(pkt_dev->vlan_id, pkt_dev->vlan_cfi, pkt_dev->vlan_p); vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16)); - *vlan_encapsulated_proto = __constant_htons(ETH_P_IP); + *vlan_encapsulated_proto = htons(ETH_P_IP); } - iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)); - udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr)); + skb->network_header = skb->tail; + skb->transport_header = skb->network_header + sizeof(struct iphdr); + skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr)); + + iph = ip_hdr(skb); + udph = udp_hdr(skb); memcpy(eth, pkt_dev->hh, 12); *(__be16 *) & eth[12] = protocol; @@ -2400,12 +2391,11 @@ static struct sk_buff *fill_packet_ipv4( iph->check = 0; iph->check = ip_fast_csum((void *)iph, iph->ihl); skb->protocol = protocol; - skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32) - - VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev); + skb->mac_header = (skb->network_header - ETH_HLEN - + pkt_dev->nr_labels * sizeof(u32) - + VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev)); skb->dev = odev; skb->pkt_type = PACKET_HOST; - skb->nh.iph = iph; - skb->h.uh = udph; if (pkt_dev->nfrags <= 0) pgh = (struct pktgen_hdr *)skb_put(skb, datalen); @@ -2654,7 +2644,7 @@ static struct sk_buff *fill_packet_ipv6( int datalen; struct ipv6hdr *iph; struct pktgen_hdr *pgh = NULL; - __be16 protocol = __constant_htons(ETH_P_IPV6); + __be16 protocol = htons(ETH_P_IPV6); __be32 *mpls; __be16 *vlan_tci = NULL; /* Encapsulates priority and VLAN ID */ __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */ @@ -2662,10 +2652,10 @@ static struct sk_buff *fill_packet_ipv6( __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */ if (pkt_dev->nr_labels) - protocol = __constant_htons(ETH_P_MPLS_UC); + protocol = htons(ETH_P_MPLS_UC); if (pkt_dev->vlan_id != 0xffff) - protocol = __constant_htons(ETH_P_8021Q); + protocol = htons(ETH_P_8021Q); /* Update any of the values, used when we're incrementing various * fields. @@ -2690,24 +2680,28 @@ static struct sk_buff *fill_packet_ipv6( mpls_push(mpls, pkt_dev); if (pkt_dev->vlan_id != 0xffff) { - if(pkt_dev->svlan_id != 0xffff) { + if (pkt_dev->svlan_id != 0xffff) { svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16)); *svlan_tci = build_tci(pkt_dev->svlan_id, pkt_dev->svlan_cfi, pkt_dev->svlan_p); svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16)); - *svlan_encapsulated_proto = __constant_htons(ETH_P_8021Q); + *svlan_encapsulated_proto = htons(ETH_P_8021Q); } vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16)); *vlan_tci = build_tci(pkt_dev->vlan_id, pkt_dev->vlan_cfi, pkt_dev->vlan_p); vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16)); - *vlan_encapsulated_proto = __constant_htons(ETH_P_IPV6); + *vlan_encapsulated_proto = htons(ETH_P_IPV6); } - iph = (struct ipv6hdr *)skb_put(skb, sizeof(struct ipv6hdr)); - udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr)); + skb->network_header = skb->tail; + skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); + skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr)); + + iph = ipv6_hdr(skb); + udph = udp_hdr(skb); memcpy(eth, pkt_dev->hh, 12); *(__be16 *) & eth[12] = protocol; @@ -2729,7 +2723,7 @@ static struct sk_buff *fill_packet_ipv6( udph->len = htons(datalen + sizeof(struct udphdr)); udph->check = 0; /* No checksum */ - *(__be32 *) iph = __constant_htonl(0x60000000); /* Version + flow */ + *(__be32 *) iph = htonl(0x60000000); /* Version + flow */ if (pkt_dev->traffic_class) { /* Version + traffic class + flow (0) */ @@ -2744,13 +2738,12 @@ static struct sk_buff *fill_packet_ipv6( ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr); ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr); - skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32) - - VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev); + skb->mac_header = (skb->network_header - ETH_HLEN - + pkt_dev->nr_labels * sizeof(u32) - + VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev)); skb->protocol = protocol; skb->dev = odev; skb->pkt_type = PACKET_HOST; - skb->nh.ipv6h = iph; - skb->h.uh = udph; if (pkt_dev->nfrags <= 0) pgh = (struct pktgen_hdr *)skb_put(skb, datalen); @@ -2848,7 +2841,7 @@ static void pktgen_run(struct pktgen_thr struct pktgen_dev *pkt_dev; int started = 0; - PG_DEBUG(printk("pktgen: entering pktgen_run. %p\n", t)); + pr_debug("pktgen: entering pktgen_run. %p\n", t); if_lock(t); list_for_each_entry(pkt_dev, &t->if_list, list) { @@ -2880,7 +2873,7 @@ static void pktgen_stop_all_threads_ifs( { struct pktgen_thread *t; - PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads_ifs.\n")); + pr_debug("pktgen: entering pktgen_stop_all_threads_ifs.\n"); mutex_lock(&pktgen_thread_lock); @@ -2948,7 +2941,7 @@ static void pktgen_run_all_threads(void) { struct pktgen_thread *t; - PG_DEBUG(printk("pktgen: entering pktgen_run_all_threads.\n")); + pr_debug("pktgen: entering pktgen_run_all_threads.\n"); mutex_lock(&pktgen_thread_lock); @@ -3006,7 +2999,7 @@ static int pktgen_stop_device(struct pkt if (!pkt_dev->running) { printk("pktgen: interface: %s is already stopped\n", - pkt_dev->ifname); + pkt_dev->odev->name); return -EINVAL; } @@ -3040,7 +3033,7 @@ static void pktgen_stop(struct pktgen_th { struct pktgen_dev *pkt_dev; - PG_DEBUG(printk("pktgen: entering pktgen_stop\n")); + pr_debug("pktgen: entering pktgen_stop\n"); if_lock(t); @@ -3064,7 +3057,7 @@ static void pktgen_rem_one_if(struct pkt struct list_head *q, *n; struct pktgen_dev *cur; - PG_DEBUG(printk("pktgen: entering pktgen_rem_one_if\n")); + pr_debug("pktgen: entering pktgen_rem_one_if\n"); if_lock(t); @@ -3093,7 +3086,7 @@ static void pktgen_rem_all_ifs(struct pk /* Remove all devices, free mem */ - PG_DEBUG(printk("pktgen: entering pktgen_rem_all_ifs\n")); + pr_debug("pktgen: entering pktgen_rem_all_ifs\n"); if_lock(t); list_for_each_safe(q, n, &t->if_list) { @@ -3276,7 +3269,7 @@ static int pktgen_thread_worker(void *ar t->pid = current->pid; - PG_DEBUG(printk("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid)); + pr_debug("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid); max_before_softirq = t->max_before_softirq; @@ -3339,13 +3332,13 @@ static int pktgen_thread_worker(void *ar set_current_state(TASK_INTERRUPTIBLE); } - PG_DEBUG(printk("pktgen: %s stopping all device\n", t->tsk->comm)); + pr_debug("pktgen: %s stopping all device\n", t->tsk->comm); pktgen_stop(t); - PG_DEBUG(printk("pktgen: %s removing all device\n", t->tsk->comm)); + pr_debug("pktgen: %s removing all device\n", t->tsk->comm); pktgen_rem_all_ifs(t); - PG_DEBUG(printk("pktgen: %s removing thread.\n", t->tsk->comm)); + pr_debug("pktgen: %s removing thread.\n", t->tsk->comm); pktgen_rem_thread(t); return 0; @@ -3358,13 +3351,13 @@ static struct pktgen_dev *pktgen_find_de if_lock(t); list_for_each_entry(p, &t->if_list, list) - if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) { + if (strncmp(p->odev->name, ifname, IFNAMSIZ) == 0) { pkt_dev = p; break; } if_unlock(t); - PG_DEBUG(printk("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev)); + pr_debug("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev); return pkt_dev; } @@ -3399,7 +3392,7 @@ out: static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) { struct pktgen_dev *pkt_dev; - struct proc_dir_entry *pe; + int err; /* We don't allow a device to be on several threads */ @@ -3441,29 +3434,28 @@ static int pktgen_add_device(struct pktg pkt_dev->svlan_cfi = 0; pkt_dev->svlan_id = 0xffff; - strncpy(pkt_dev->ifname, ifname, IFNAMSIZ); + err = pktgen_setup_dev(pkt_dev, ifname); + if (err) + goto out1; - if (!pktgen_setup_dev(pkt_dev)) { - printk("pktgen: ERROR: pktgen_setup_dev failed.\n"); - if (pkt_dev->flows) - vfree(pkt_dev->flows); - kfree(pkt_dev); - return -ENODEV; - } - - pe = create_proc_entry(ifname, 0600, pg_proc_dir); - if (!pe) { + pkt_dev->entry = create_proc_entry(ifname, 0600, pg_proc_dir); + if (!pkt_dev->entry) { printk("pktgen: cannot create %s/%s procfs entry.\n", PG_PROC_DIR, ifname); - if (pkt_dev->flows) - vfree(pkt_dev->flows); - kfree(pkt_dev); - return -EINVAL; + err = -EINVAL; + goto out2; } - pe->proc_fops = &pktgen_if_fops; - pe->data = pkt_dev; + pkt_dev->entry->proc_fops = &pktgen_if_fops; + pkt_dev->entry->data = pkt_dev; return add_dev_to_thread(t, pkt_dev); +out2: + dev_put(pkt_dev->odev); +out1: + if (pkt_dev->flows) + vfree(pkt_dev->flows); + kfree(pkt_dev); + return err; } static int __init pktgen_create_thread(int cpu) @@ -3533,7 +3525,7 @@ static int pktgen_remove_device(struct p struct pktgen_dev *pkt_dev) { - PG_DEBUG(printk("pktgen: remove_device pkt_dev=%p\n", pkt_dev)); + pr_debug("pktgen: remove_device pkt_dev=%p\n", pkt_dev); if (pkt_dev->running) { printk("pktgen:WARNING: trying to remove a running interface, stopping it now.\n"); @@ -3551,9 +3543,8 @@ static int pktgen_remove_device(struct p _rem_dev_from_if_list(t, pkt_dev); - /* Clean up proc file system */ - - remove_proc_entry(pkt_dev->ifname, pg_proc_dir); + if (pkt_dev->entry) + remove_proc_entry(pkt_dev->entry->name, pg_proc_dir); if (pkt_dev->flows) vfree(pkt_dev->flows); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 33ea8ea..cec1111 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -50,11 +50,13 @@ #include #include #include #include -#include -#ifdef CONFIG_NET_WIRELESS_RTNETLINK -#include -#include -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ +#include + +struct rtnl_link +{ + rtnl_doit_func doit; + rtnl_dumpit_func dumpit; +}; static DEFINE_MUTEX(rtnl_mutex); static struct sock *rtnl; @@ -95,7 +97,151 @@ int rtattr_parse(struct rtattr *tb[], in return 0; } -struct rtnetlink_link * rtnetlink_links[NPROTO]; +static struct rtnl_link *rtnl_msg_handlers[NPROTO]; + +static inline int rtm_msgindex(int msgtype) +{ + int msgindex = msgtype - RTM_BASE; + + /* + * msgindex < 0 implies someone tried to register a netlink + * control code. msgindex >= RTM_NR_MSGTYPES may indicate that + * the message type has not been added to linux/rtnetlink.h + */ + BUG_ON(msgindex < 0 || msgindex >= RTM_NR_MSGTYPES); + + return msgindex; +} + +static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex) +{ + struct rtnl_link *tab; + + tab = rtnl_msg_handlers[protocol]; + if (tab == NULL || tab[msgindex].doit == NULL) + tab = rtnl_msg_handlers[PF_UNSPEC]; + + return tab ? tab[msgindex].doit : NULL; +} + +static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) +{ + struct rtnl_link *tab; + + tab = rtnl_msg_handlers[protocol]; + if (tab == NULL || tab[msgindex].dumpit == NULL) + tab = rtnl_msg_handlers[PF_UNSPEC]; + + return tab ? tab[msgindex].dumpit : NULL; +} + +/** + * __rtnl_register - Register a rtnetlink message type + * @protocol: Protocol family or PF_UNSPEC + * @msgtype: rtnetlink message type + * @doit: Function pointer called for each request message + * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message + * + * Registers the specified function pointers (at least one of them has + * to be non-NULL) to be called whenever a request message for the + * specified protocol family and message type is received. + * + * The special protocol family PF_UNSPEC may be used to define fallback + * function pointers for the case when no entry for the specific protocol + * family exists. + * + * Returns 0 on success or a negative error code. + */ +int __rtnl_register(int protocol, int msgtype, + rtnl_doit_func doit, rtnl_dumpit_func dumpit) +{ + struct rtnl_link *tab; + int msgindex; + + BUG_ON(protocol < 0 || protocol >= NPROTO); + msgindex = rtm_msgindex(msgtype); + + tab = rtnl_msg_handlers[protocol]; + if (tab == NULL) { + tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); + if (tab == NULL) + return -ENOBUFS; + + rtnl_msg_handlers[protocol] = tab; + } + + if (doit) + tab[msgindex].doit = doit; + + if (dumpit) + tab[msgindex].dumpit = dumpit; + + return 0; +} + +EXPORT_SYMBOL_GPL(__rtnl_register); + +/** + * rtnl_register - Register a rtnetlink message type + * + * Identical to __rtnl_register() but panics on failure. This is useful + * as failure of this function is very unlikely, it can only happen due + * to lack of memory when allocating the chain to store all message + * handlers for a protocol. Meant for use in init functions where lack + * of memory implies no sense in continueing. + */ +void rtnl_register(int protocol, int msgtype, + rtnl_doit_func doit, rtnl_dumpit_func dumpit) +{ + if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0) + panic("Unable to register rtnetlink message handler, " + "protocol = %d, message type = %d\n", + protocol, msgtype); +} + +EXPORT_SYMBOL_GPL(rtnl_register); + +/** + * rtnl_unregister - Unregister a rtnetlink message type + * @protocol: Protocol family or PF_UNSPEC + * @msgtype: rtnetlink message type + * + * Returns 0 on success or a negative error code. + */ +int rtnl_unregister(int protocol, int msgtype) +{ + int msgindex; + + BUG_ON(protocol < 0 || protocol >= NPROTO); + msgindex = rtm_msgindex(msgtype); + + if (rtnl_msg_handlers[protocol] == NULL) + return -ENOENT; + + rtnl_msg_handlers[protocol][msgindex].doit = NULL; + rtnl_msg_handlers[protocol][msgindex].dumpit = NULL; + + return 0; +} + +EXPORT_SYMBOL_GPL(rtnl_unregister); + +/** + * rtnl_unregister_all - Unregister all rtnetlink message type of a protocol + * @protocol : Protocol family or PF_UNSPEC + * + * Identical to calling rtnl_unregster() for all registered message types + * of a certain protocol family. + */ +void rtnl_unregister_all(int protocol) +{ + BUG_ON(protocol < 0 || protocol >= NPROTO); + + kfree(rtnl_msg_handlers[protocol]); + rtnl_msg_handlers[protocol] = NULL; +} + +EXPORT_SYMBOL_GPL(rtnl_unregister_all); static const int rtm_min[RTM_NR_FAMILIES] = { @@ -249,7 +395,7 @@ static void set_operstate(struct net_dev operstate == IF_OPER_UNKNOWN) operstate = IF_OPER_DORMANT; break; - }; + } if (dev->operstate != operstate) { write_lock_bh(&dev_base_lock); @@ -393,7 +539,6 @@ static int rtnl_dump_ifinfo(struct sk_bu int s_idx = cb->args[0]; struct net_device *dev; - read_lock(&dev_base_lock); for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { if (idx < s_idx) continue; @@ -402,7 +547,6 @@ static int rtnl_dump_ifinfo(struct sk_bu cb->nlh->nlmsg_seq, 0, NLM_F_MULTI) <= 0) break; } - read_unlock(&dev_base_lock); cb->args[0] = idx; return skb->len; @@ -536,17 +680,6 @@ static int rtnl_setlink(struct sk_buff * modified = 1; } -#ifdef CONFIG_NET_WIRELESS_RTNETLINK - if (tb[IFLA_WIRELESS]) { - /* Call Wireless Extensions. - * Various stuff checked in there... */ - err = wireless_rtnetlink_set(dev, nla_data(tb[IFLA_WIRELESS]), - nla_len(tb[IFLA_WIRELESS])); - if (err < 0) - goto errout_dev; - } -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - if (tb[IFLA_BROADCAST]) { nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len); send_addr_notify = 1; @@ -610,22 +743,6 @@ static int rtnl_getlink(struct sk_buff * } else return -EINVAL; - -#ifdef CONFIG_NET_WIRELESS_RTNETLINK - if (tb[IFLA_WIRELESS]) { - /* Call Wireless Extensions. We need to know the size before - * we can alloc. Various stuff checked in there... */ - err = wireless_rtnetlink_get(dev, nla_data(tb[IFLA_WIRELESS]), - nla_len(tb[IFLA_WIRELESS]), - &iw_buf, &iw_buf_len); - if (err < 0) - goto errout; - - /* Payload is at an offset in buffer */ - iw = iw_buf + IW_EV_POINT_OFF; - } -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - nskb = nlmsg_new(if_nlmsg_size(iw_buf_len), GFP_KERNEL); if (nskb == NULL) { err = -ENOBUFS; @@ -659,12 +776,12 @@ static int rtnl_dump_all(struct sk_buff int type = cb->nlh->nlmsg_type-RTM_BASE; if (idx < s_idx || idx == PF_PACKET) continue; - if (rtnetlink_links[idx] == NULL || - rtnetlink_links[idx][type].dumpit == NULL) + if (rtnl_msg_handlers[idx] == NULL || + rtnl_msg_handlers[idx][type].dumpit == NULL) continue; if (idx > s_idx) memset(&cb->args[0], 0, sizeof(cb->args)); - if (rtnetlink_links[idx][type].dumpit(skb, cb)) + if (rtnl_msg_handlers[idx][type].dumpit(skb, cb)) break; } cb->family = idx; @@ -700,30 +817,18 @@ static int rtattr_max; /* Process one rtnetlink message. */ -static __inline__ int -rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) +static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { - struct rtnetlink_link *link; - struct rtnetlink_link *link_tab; + rtnl_doit_func doit; int sz_idx, kind; int min_len; int family; int type; int err; - /* Only requests are handled by kernel now */ - if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) - return 0; - type = nlh->nlmsg_type; - - /* A control message: ignore them */ - if (type < RTM_BASE) - return 0; - - /* Unknown message: reply with EINVAL */ if (type > RTM_MAX) - goto err_inval; + return -EOPNOTSUPP; type -= RTM_BASE; @@ -732,45 +837,33 @@ rtnetlink_rcv_msg(struct sk_buff *skb, s return 0; family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family; - if (family >= NPROTO) { - *errp = -EAFNOSUPPORT; - return -1; - } - - link_tab = rtnetlink_links[family]; - if (link_tab == NULL) - link_tab = rtnetlink_links[PF_UNSPEC]; - link = &link_tab[type]; + if (family >= NPROTO) + return -EAFNOSUPPORT; sz_idx = type>>2; kind = type&3; - if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN)) { - *errp = -EPERM; - return -1; - } + if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN)) + return -EPERM; if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { - if (link->dumpit == NULL) - link = &(rtnetlink_links[PF_UNSPEC][type]); - - if (link->dumpit == NULL) - goto err_inval; + rtnl_dumpit_func dumpit; - if ((*errp = netlink_dump_start(rtnl, skb, nlh, - link->dumpit, NULL)) != 0) { - return -1; - } + dumpit = rtnl_get_dumpit(family, type); + if (dumpit == NULL) + return -EOPNOTSUPP; - netlink_queue_skip(nlh, skb); - return -1; + __rtnl_unlock(); + err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL); + rtnl_lock(); + return err; } memset(rta_buf, 0, (rtattr_max * sizeof(struct rtattr *))); min_len = rtm_min[sz_idx]; if (nlh->nlmsg_len < min_len) - goto err_inval; + return -EINVAL; if (nlh->nlmsg_len > min_len) { int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); @@ -780,25 +873,18 @@ rtnetlink_rcv_msg(struct sk_buff *skb, s unsigned flavor = attr->rta_type; if (flavor) { if (flavor > rta_max[sz_idx]) - goto err_inval; + return -EINVAL; rta_buf[flavor-1] = attr; } attr = RTA_NEXT(attr, attrlen); } } - if (link->doit == NULL) - link = &(rtnetlink_links[PF_UNSPEC][type]); - if (link->doit == NULL) - goto err_inval; - err = link->doit(skb, nlh, (void *)&rta_buf[0]); + doit = rtnl_get_doit(family, type); + if (doit == NULL) + return -EOPNOTSUPP; - *errp = err; - return err; - -err_inval: - *errp = -EINVAL; - return -1; + return doit(skb, nlh, (void *)&rta_buf[0]); } static void rtnetlink_rcv(struct sock *sk, int len) @@ -814,25 +900,6 @@ static void rtnetlink_rcv(struct sock *s } while (qlen); } -static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] = -{ - [RTM_GETLINK - RTM_BASE] = { .doit = rtnl_getlink, - .dumpit = rtnl_dump_ifinfo }, - [RTM_SETLINK - RTM_BASE] = { .doit = rtnl_setlink }, - [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnl_dump_all }, - [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnl_dump_all }, - [RTM_NEWNEIGH - RTM_BASE] = { .doit = neigh_add }, - [RTM_DELNEIGH - RTM_BASE] = { .doit = neigh_delete }, - [RTM_GETNEIGH - RTM_BASE] = { .dumpit = neigh_dump_info }, -#ifdef CONFIG_FIB_RULES - [RTM_NEWRULE - RTM_BASE] = { .doit = fib_nl_newrule }, - [RTM_DELRULE - RTM_BASE] = { .doit = fib_nl_delrule }, -#endif - [RTM_GETRULE - RTM_BASE] = { .dumpit = rtnl_dump_all }, - [RTM_GETNEIGHTBL - RTM_BASE] = { .dumpit = neightbl_dump_info }, - [RTM_SETNEIGHTBL - RTM_BASE] = { .doit = neightbl_set }, -}; - static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = ptr; @@ -874,19 +941,22 @@ void __init rtnetlink_init(void) panic("rtnetlink_init: cannot allocate rta_buf\n"); rtnl = netlink_kernel_create(NETLINK_ROUTE, RTNLGRP_MAX, rtnetlink_rcv, - THIS_MODULE); + &rtnl_mutex, THIS_MODULE); if (rtnl == NULL) panic("rtnetlink_init: cannot initialize rtnetlink\n"); netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV); register_netdevice_notifier(&rtnetlink_dev_notifier); - rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table; - rtnetlink_links[PF_PACKET] = link_rtnetlink_table; + + rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo); + rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL); + + rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all); + rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all); } EXPORT_SYMBOL(__rta_fill); EXPORT_SYMBOL(rtattr_strlcpy); EXPORT_SYMBOL(rtattr_parse); -EXPORT_SYMBOL(rtnetlink_links); EXPORT_SYMBOL(rtnetlink_put_metrics); EXPORT_SYMBOL(rtnl_lock); EXPORT_SYMBOL(rtnl_trylock); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 336958f..1422573 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -55,6 +55,7 @@ #include #include #include #include +#include #include #include @@ -87,8 +88,9 @@ static struct kmem_cache *skbuff_fclone_ void skb_over_panic(struct sk_buff *skb, int sz, void *here) { printk(KERN_EMERG "skb_over_panic: text:%p len:%d put:%d head:%p " - "data:%p tail:%p end:%p dev:%s\n", - here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end, + "data:%p tail:%#lx end:%#lx dev:%s\n", + here, skb->len, sz, skb->head, skb->data, + (unsigned long)skb->tail, (unsigned long)skb->end, skb->dev ? skb->dev->name : ""); BUG(); } @@ -105,8 +107,9 @@ void skb_over_panic(struct sk_buff *skb, void skb_under_panic(struct sk_buff *skb, int sz, void *here) { printk(KERN_EMERG "skb_under_panic: text:%p len:%d put:%d head:%p " - "data:%p tail:%p end:%p dev:%s\n", - here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end, + "data:%p tail:%#lx end:%#lx dev:%s\n", + here, skb->len, sz, skb->head, skb->data, + (unsigned long)skb->tail, (unsigned long)skb->end, skb->dev ? skb->dev->name : ""); BUG(); } @@ -155,20 +158,22 @@ struct sk_buff *__alloc_skb(unsigned int if (!skb) goto out; - /* Get the DATA. Size must match skb_add_mtu(). */ size = SKB_DATA_ALIGN(size); data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info), gfp_mask, node); if (!data) goto nodata; - memset(skb, 0, offsetof(struct sk_buff, truesize)); + /* + * See comment in sk_buff definition, just before the 'tail' member + */ + memset(skb, 0, offsetof(struct sk_buff, tail)); skb->truesize = size + sizeof(struct sk_buff); atomic_set(&skb->users, 1); skb->head = data; skb->data = data; - skb->tail = data; - skb->end = data + size; + skb_reset_tail_pointer(skb); + skb->end = skb->tail + size; /* make sure we initialize shinfo sequentially */ shinfo = skb_shinfo(skb); atomic_set(&shinfo->dataref, 1); @@ -299,7 +304,7 @@ void kfree_skbmem(struct sk_buff *skb) if (atomic_dec_and_test(fclone_ref)) kmem_cache_free(skbuff_fclone_cache, other); break; - }; + } } /** @@ -321,15 +326,13 @@ #endif WARN_ON(in_irq()); skb->destructor(skb); } -#ifdef CONFIG_NETFILTER - nf_conntrack_put(skb->nfct); #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) + nf_conntrack_put(skb->nfct); nf_conntrack_put_reasm(skb->nfct_reasm); #endif #ifdef CONFIG_BRIDGE_NETFILTER nf_bridge_put(skb->nf_bridge); #endif -#endif /* XXX: IS this still necessary? - JHS */ #ifdef CONFIG_NET_SCHED skb->tc_index = 0; @@ -396,9 +399,9 @@ #define C(x) n->x = skb->x n->sk = NULL; C(tstamp); C(dev); - C(h); - C(nh); - C(mac); + C(transport_header); + C(network_header); + C(mac_header); C(dst); dst_clone(skb->dst); C(sp); @@ -422,19 +425,7 @@ #endif C(protocol); n->destructor = NULL; C(mark); -#ifdef CONFIG_NETFILTER - C(nfct); - nf_conntrack_get(skb->nfct); - C(nfctinfo); -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) - C(nfct_reasm); - nf_conntrack_get_reasm(skb->nfct_reasm); -#endif -#ifdef CONFIG_BRIDGE_NETFILTER - C(nf_bridge); - nf_bridge_get(skb->nf_bridge); -#endif -#endif /*CONFIG_NETFILTER*/ + __nf_copy(n, skb); #ifdef CONFIG_NET_SCHED C(tc_index); #ifdef CONFIG_NET_CLS_ACT @@ -460,11 +451,12 @@ #endif static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) { +#ifndef NET_SKBUFF_DATA_USES_OFFSET /* * Shift between the two data areas in bytes */ unsigned long offset = new->data - old->data; - +#endif new->sk = NULL; new->dev = old->dev; new->priority = old->priority; @@ -473,9 +465,15 @@ static void copy_skb_header(struct sk_bu #ifdef CONFIG_INET new->sp = secpath_get(old->sp); #endif - new->h.raw = old->h.raw + offset; - new->nh.raw = old->nh.raw + offset; - new->mac.raw = old->mac.raw + offset; + new->transport_header = old->transport_header; + new->network_header = old->network_header; + new->mac_header = old->mac_header; +#ifndef NET_SKBUFF_DATA_USES_OFFSET + /* {transport,network,mac}_header are relative to skb->head */ + new->transport_header += offset; + new->network_header += offset; + new->mac_header += offset; +#endif memcpy(new->cb, old->cb, sizeof(old->cb)); new->local_df = old->local_df; new->fclone = SKB_FCLONE_UNAVAILABLE; @@ -483,22 +481,10 @@ #endif new->tstamp = old->tstamp; new->destructor = NULL; new->mark = old->mark; -#ifdef CONFIG_NETFILTER - new->nfct = old->nfct; - nf_conntrack_get(old->nfct); - new->nfctinfo = old->nfctinfo; -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) - new->nfct_reasm = old->nfct_reasm; - nf_conntrack_get_reasm(old->nfct_reasm); -#endif + __nf_copy(new, old); #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE) new->ipvs_property = old->ipvs_property; #endif -#ifdef CONFIG_BRIDGE_NETFILTER - new->nf_bridge = old->nf_bridge; - nf_bridge_get(old->nf_bridge); -#endif -#endif #ifdef CONFIG_NET_SCHED #ifdef CONFIG_NET_CLS_ACT new->tc_verd = old->tc_verd; @@ -535,8 +521,12 @@ struct sk_buff *skb_copy(const struct sk /* * Allocate the copy buffer */ - struct sk_buff *n = alloc_skb(skb->end - skb->head + skb->data_len, - gfp_mask); + struct sk_buff *n; +#ifdef NET_SKBUFF_DATA_USES_OFFSET + n = alloc_skb(skb->end + skb->data_len, gfp_mask); +#else + n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask); +#endif if (!n) return NULL; @@ -573,8 +563,12 @@ struct sk_buff *pskb_copy(struct sk_buff /* * Allocate the copy buffer */ - struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask); - + struct sk_buff *n; +#ifdef NET_SKBUFF_DATA_USES_OFFSET + n = alloc_skb(skb->end, gfp_mask); +#else + n = alloc_skb(skb->end - skb->head, gfp_mask); +#endif if (!n) goto out; @@ -583,7 +577,7 @@ struct sk_buff *pskb_copy(struct sk_buff /* Set the tail pointer and length */ skb_put(n, skb_headlen(skb)); /* Copy the bytes */ - memcpy(n->data, skb->data, n->len); + skb_copy_from_linear_data(skb, n->data, n->len); n->csum = skb->csum; n->ip_summed = skb->ip_summed; @@ -632,7 +626,11 @@ int pskb_expand_head(struct sk_buff *skb { int i; u8 *data; +#ifdef NET_SKBUFF_DATA_USES_OFFSET + int size = nhead + skb->end + ntail; +#else int size = nhead + (skb->end - skb->head) + ntail; +#endif long off; if (skb_shared(skb)) @@ -646,8 +644,14 @@ int pskb_expand_head(struct sk_buff *skb /* Copy only real data... and, alas, header. This should be * optimized for the cases when header is void. */ - memcpy(data + nhead, skb->head, skb->tail - skb->head); - memcpy(data + size, skb->end, sizeof(struct skb_shared_info)); + memcpy(data + nhead, skb->head, +#ifdef NET_SKBUFF_DATA_USES_OFFSET + skb->tail); +#else + skb->tail - skb->head); +#endif + memcpy(data + size, skb_end_pointer(skb), + sizeof(struct skb_shared_info)); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) get_page(skb_shinfo(skb)->frags[i].page); @@ -660,12 +664,18 @@ int pskb_expand_head(struct sk_buff *skb off = (data + nhead) - skb->head; skb->head = data; - skb->end = data + size; skb->data += off; - skb->tail += off; - skb->mac.raw += off; - skb->h.raw += off; - skb->nh.raw += off; +#ifdef NET_SKBUFF_DATA_USES_OFFSET + skb->end = size; + off = nhead; +#else + skb->end = skb->head + size; +#endif + /* {transport,network,mac}_header and tail are relative to skb->head */ + skb->tail += off; + skb->transport_header += off; + skb->network_header += off; + skb->mac_header += off; skb->cloned = 0; skb->nohdr = 0; atomic_set(&skb_shinfo(skb)->dataref, 1); @@ -726,7 +736,9 @@ struct sk_buff *skb_copy_expand(const st */ struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom, gfp_mask); + int oldheadroom = skb_headroom(skb); int head_copy_len, head_copy_off; + int off = 0; if (!n) return NULL; @@ -736,7 +748,7 @@ struct sk_buff *skb_copy_expand(const st /* Set the tail pointer and length */ skb_put(n, skb->len); - head_copy_len = skb_headroom(skb); + head_copy_len = oldheadroom; head_copy_off = 0; if (newheadroom <= head_copy_len) head_copy_len = newheadroom; @@ -750,6 +762,13 @@ struct sk_buff *skb_copy_expand(const st copy_skb_header(n, skb); +#ifdef NET_SKBUFF_DATA_USES_OFFSET + off = newheadroom - oldheadroom; +#endif + n->transport_header += off; + n->network_header += off; + n->mac_header += off; + return n; } @@ -877,7 +896,7 @@ done: } else { skb->len = len; skb->data_len = 0; - skb->tail = skb->data + len; + skb_set_tail_pointer(skb, len); } return 0; @@ -922,7 +941,7 @@ unsigned char *__pskb_pull_tail(struct s return NULL; } - if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta)) + if (skb_copy_bits(skb, skb_headlen(skb), skb_tail_pointer(skb), delta)) BUG(); /* Optimization: no fragments, no reasons to preestimate @@ -1018,7 +1037,7 @@ pull_pages: skb->tail += delta; skb->data_len -= delta; - return skb->tail; + return skb_tail_pointer(skb); } /* Copy some data bits from skb to kernel buffer. */ @@ -1035,7 +1054,7 @@ int skb_copy_bits(const struct sk_buff * if ((copy = start - offset) > 0) { if (copy > len) copy = len; - memcpy(to, skb->data + offset, copy); + skb_copy_from_linear_data_offset(skb, offset, to, copy); if ((len -= copy) == 0) return 0; offset += copy; @@ -1110,7 +1129,7 @@ fault: * traversing fragment lists and such. */ -int skb_store_bits(const struct sk_buff *skb, int offset, void *from, int len) +int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len) { int i, copy; int start = skb_headlen(skb); @@ -1121,7 +1140,7 @@ int skb_store_bits(const struct sk_buff if ((copy = start - offset) > 0) { if (copy > len) copy = len; - memcpy(skb->data + offset, from, copy); + skb_copy_to_linear_data_offset(skb, offset, from, copy); if ((len -= copy) == 0) return 0; offset += copy; @@ -1348,13 +1367,13 @@ void skb_copy_and_csum_dev(const struct long csstart; if (skb->ip_summed == CHECKSUM_PARTIAL) - csstart = skb->h.raw - skb->data; + csstart = skb->csum_start - skb_headroom(skb); else csstart = skb_headlen(skb); BUG_ON(csstart > skb_headlen(skb)); - memcpy(to, skb->data, csstart); + skb_copy_from_linear_data(skb, to, csstart); csum = 0; if (csstart != skb->len) @@ -1522,27 +1541,14 @@ void skb_insert(struct sk_buff *old, str spin_unlock_irqrestore(&list->lock, flags); } -#if 0 -/* - * Tune the memory allocator for a new MTU size. - */ -void skb_add_mtu(int mtu) -{ - /* Must match allocation in alloc_skb */ - mtu = SKB_DATA_ALIGN(mtu) + sizeof(struct skb_shared_info); - - kmem_add_cache_size(mtu); -} -#endif - static inline void skb_split_inside_header(struct sk_buff *skb, struct sk_buff* skb1, const u32 len, const int pos) { int i; - memcpy(skb_put(skb1, pos - len), skb->data + len, pos - len); - + skb_copy_from_linear_data_offset(skb, len, skb_put(skb1, pos - len), + pos - len); /* And move data appendix as is. */ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i]; @@ -1553,7 +1559,7 @@ static inline void skb_split_inside_head skb1->len += skb1->data_len; skb->data_len = 0; skb->len = len; - skb->tail = skb->data + len; + skb_set_tail_pointer(skb, len); } static inline void skb_split_no_header(struct sk_buff *skb, @@ -1878,7 +1884,7 @@ struct sk_buff *skb_segment(struct sk_bu struct sk_buff *segs = NULL; struct sk_buff *tail = NULL; unsigned int mss = skb_shinfo(skb)->gso_size; - unsigned int doffset = skb->data - skb->mac.raw; + unsigned int doffset = skb->data - skb_mac_header(skb); unsigned int offset = doffset; unsigned int headroom; unsigned int len; @@ -1928,11 +1934,12 @@ struct sk_buff *skb_segment(struct sk_bu nskb->mac_len = skb->mac_len; skb_reserve(nskb, headroom); - nskb->mac.raw = nskb->data; - nskb->nh.raw = nskb->data + skb->mac_len; - nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw); - memcpy(skb_put(nskb, doffset), skb->data, doffset); - + skb_reset_mac_header(nskb); + skb_set_network_header(nskb, skb->mac_len); + nskb->transport_header = (nskb->network_header + + skb_network_header_len(skb)); + skb_copy_from_linear_data(skb, skb_put(nskb, doffset), + doffset); if (!sg) { nskb->csum = skb_copy_and_csum_bits(skb, offset, skb_put(nskb, len), @@ -1945,7 +1952,8 @@ struct sk_buff *skb_segment(struct sk_bu nskb->ip_summed = CHECKSUM_PARTIAL; nskb->csum = skb->csum; - memcpy(skb_put(nskb, hsize), skb->data + offset, hsize); + skb_copy_from_linear_data_offset(skb, offset, + skb_put(nskb, hsize), hsize); while (pos < offset + len) { BUG_ON(i >= nfrags); @@ -2005,6 +2013,190 @@ void __init skb_init(void) NULL, NULL); } +/** + * skb_to_sgvec - Fill a scatter-gather list from a socket buffer + * @skb: Socket buffer containing the buffers to be mapped + * @sg: The scatter-gather list to map into + * @offset: The offset into the buffer's contents to start mapping + * @len: Length of buffer space to be mapped + * + * Fill the specified scatter-gather list with mappings/pointers into a + * region of the buffer space attached to a socket buffer. + */ +int +skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) +{ + int start = skb_headlen(skb); + int i, copy = start - offset; + int elt = 0; + + if (copy > 0) { + if (copy > len) + copy = len; + sg[elt].page = virt_to_page(skb->data + offset); + sg[elt].offset = (unsigned long)(skb->data + offset) % PAGE_SIZE; + sg[elt].length = copy; + elt++; + if ((len -= copy) == 0) + return elt; + offset += copy; + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + int end; + + BUG_TRAP(start <= offset + len); + + end = start + skb_shinfo(skb)->frags[i].size; + if ((copy = end - offset) > 0) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + if (copy > len) + copy = len; + sg[elt].page = frag->page; + sg[elt].offset = frag->page_offset+offset-start; + sg[elt].length = copy; + elt++; + if (!(len -= copy)) + return elt; + offset += copy; + } + start = end; + } + + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *list = skb_shinfo(skb)->frag_list; + + for (; list; list = list->next) { + int end; + + BUG_TRAP(start <= offset + len); + + end = start + list->len; + if ((copy = end - offset) > 0) { + if (copy > len) + copy = len; + elt += skb_to_sgvec(list, sg+elt, offset - start, copy); + if ((len -= copy) == 0) + return elt; + offset += copy; + } + start = end; + } + } + BUG_ON(len); + return elt; +} + +/** + * skb_cow_data - Check that a socket buffer's data buffers are writable + * @skb: The socket buffer to check. + * @tailbits: Amount of trailing space to be added + * @trailer: Returned pointer to the skb where the @tailbits space begins + * + * Make sure that the data buffers attached to a socket buffer are + * writable. If they are not, private copies are made of the data buffers + * and the socket buffer is set to use these instead. + * + * If @tailbits is given, make sure that there is space to write @tailbits + * bytes of data beyond current end of socket buffer. @trailer will be + * set to point to the skb in which this space begins. + * + * The number of scatterlist elements required to completely map the + * COW'd and extended socket buffer will be returned. + */ +int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer) +{ + int copyflag; + int elt; + struct sk_buff *skb1, **skb_p; + + /* If skb is cloned or its head is paged, reallocate + * head pulling out all the pages (pages are considered not writable + * at the moment even if they are anonymous). + */ + if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) && + __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL) + return -ENOMEM; + + /* Easy case. Most of packets will go this way. */ + if (!skb_shinfo(skb)->frag_list) { + /* A little of trouble, not enough of space for trailer. + * This should not happen, when stack is tuned to generate + * good frames. OK, on miss we reallocate and reserve even more + * space, 128 bytes is fair. */ + + if (skb_tailroom(skb) < tailbits && + pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC)) + return -ENOMEM; + + /* Voila! */ + *trailer = skb; + return 1; + } + + /* Misery. We are in troubles, going to mincer fragments... */ + + elt = 1; + skb_p = &skb_shinfo(skb)->frag_list; + copyflag = 0; + + while ((skb1 = *skb_p) != NULL) { + int ntail = 0; + + /* The fragment is partially pulled by someone, + * this can happen on input. Copy it and everything + * after it. */ + + if (skb_shared(skb1)) + copyflag = 1; + + /* If the skb is the last, worry about trailer. */ + + if (skb1->next == NULL && tailbits) { + if (skb_shinfo(skb1)->nr_frags || + skb_shinfo(skb1)->frag_list || + skb_tailroom(skb1) < tailbits) + ntail = tailbits + 128; + } + + if (copyflag || + skb_cloned(skb1) || + ntail || + skb_shinfo(skb1)->nr_frags || + skb_shinfo(skb1)->frag_list) { + struct sk_buff *skb2; + + /* Fuck, we are miserable poor guys... */ + if (ntail == 0) + skb2 = skb_copy(skb1, GFP_ATOMIC); + else + skb2 = skb_copy_expand(skb1, + skb_headroom(skb1), + ntail, + GFP_ATOMIC); + if (unlikely(skb2 == NULL)) + return -ENOMEM; + + if (skb1->sk) + skb_set_owner_w(skb2, skb1->sk); + + /* Looking around. Are we still alive? + * OK, link new skb, drop old one */ + + skb2->next = skb1->next; + *skb_p = skb2; + kfree_skb(skb1); + skb1 = skb2; + } + elt++; + *trailer = skb1; + skb_p = &skb1->next; + } + + return elt; +} + EXPORT_SYMBOL(___pskb_trim); EXPORT_SYMBOL(__kfree_skb); EXPORT_SYMBOL(kfree_skb); @@ -2039,3 +2231,6 @@ EXPORT_SYMBOL(skb_seq_read); EXPORT_SYMBOL(skb_abort_seq_read); EXPORT_SYMBOL(skb_find_text); EXPORT_SYMBOL(skb_append_datato_frags); + +EXPORT_SYMBOL_GPL(skb_to_sgvec); +EXPORT_SYMBOL_GPL(skb_cow_data); diff --git a/net/core/sock.c b/net/core/sock.c index 27c4f62..22183c2 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -154,7 +154,8 @@ static const char *af_family_key_strings "sk_lock-21" , "sk_lock-AF_SNA" , "sk_lock-AF_IRDA" , "sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE" , "sk_lock-AF_LLC" , "sk_lock-27" , "sk_lock-28" , "sk_lock-29" , - "sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-AF_MAX" + "sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" , + "sk_lock-AF_RXRPC" , "sk_lock-AF_MAX" }; static const char *af_family_slock_key_strings[AF_MAX+1] = { "slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" , @@ -167,7 +168,8 @@ static const char *af_family_slock_key_s "slock-21" , "slock-AF_SNA" , "slock-AF_IRDA" , "slock-AF_PPPOX" , "slock-AF_WANPIPE" , "slock-AF_LLC" , "slock-27" , "slock-28" , "slock-29" , - "slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_MAX" + "slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" , + "slock-AF_RXRPC" , "slock-AF_MAX" }; #endif @@ -361,8 +363,8 @@ #ifdef SO_DONTLINGER /* Compatibility i } #endif - if(optlensk_reuse = valbool; - break; - case SO_TYPE: - case SO_ERROR: - ret = -ENOPROTOOPT; - break; - case SO_DONTROUTE: - if (valbool) - sock_set_flag(sk, SOCK_LOCALROUTE); - else - sock_reset_flag(sk, SOCK_LOCALROUTE); - break; - case SO_BROADCAST: - sock_valbool_flag(sk, SOCK_BROADCAST, valbool); - break; - case SO_SNDBUF: - /* Don't error on this BSD doesn't and if you think - about it this is right. Otherwise apps have to - play 'guess the biggest size' games. RCVBUF/SNDBUF - are treated in BSD as hints */ - - if (val > sysctl_wmem_max) - val = sysctl_wmem_max; + switch(optname) { + case SO_DEBUG: + if (val && !capable(CAP_NET_ADMIN)) { + ret = -EACCES; + } + else if (valbool) + sock_set_flag(sk, SOCK_DBG); + else + sock_reset_flag(sk, SOCK_DBG); + break; + case SO_REUSEADDR: + sk->sk_reuse = valbool; + break; + case SO_TYPE: + case SO_ERROR: + ret = -ENOPROTOOPT; + break; + case SO_DONTROUTE: + if (valbool) + sock_set_flag(sk, SOCK_LOCALROUTE); + else + sock_reset_flag(sk, SOCK_LOCALROUTE); + break; + case SO_BROADCAST: + sock_valbool_flag(sk, SOCK_BROADCAST, valbool); + break; + case SO_SNDBUF: + /* Don't error on this BSD doesn't and if you think + about it this is right. Otherwise apps have to + play 'guess the biggest size' games. RCVBUF/SNDBUF + are treated in BSD as hints */ + + if (val > sysctl_wmem_max) + val = sysctl_wmem_max; set_sndbuf: - sk->sk_userlocks |= SOCK_SNDBUF_LOCK; - if ((val * 2) < SOCK_MIN_SNDBUF) - sk->sk_sndbuf = SOCK_MIN_SNDBUF; - else - sk->sk_sndbuf = val * 2; + sk->sk_userlocks |= SOCK_SNDBUF_LOCK; + if ((val * 2) < SOCK_MIN_SNDBUF) + sk->sk_sndbuf = SOCK_MIN_SNDBUF; + else + sk->sk_sndbuf = val * 2; - /* - * Wake up sending tasks if we - * upped the value. - */ - sk->sk_write_space(sk); - break; + /* + * Wake up sending tasks if we + * upped the value. + */ + sk->sk_write_space(sk); + break; - case SO_SNDBUFFORCE: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - goto set_sndbuf; + case SO_SNDBUFFORCE: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + goto set_sndbuf; - case SO_RCVBUF: - /* Don't error on this BSD doesn't and if you think - about it this is right. Otherwise apps have to - play 'guess the biggest size' games. RCVBUF/SNDBUF - are treated in BSD as hints */ + case SO_RCVBUF: + /* Don't error on this BSD doesn't and if you think + about it this is right. Otherwise apps have to + play 'guess the biggest size' games. RCVBUF/SNDBUF + are treated in BSD as hints */ - if (val > sysctl_rmem_max) - val = sysctl_rmem_max; + if (val > sysctl_rmem_max) + val = sysctl_rmem_max; set_rcvbuf: - sk->sk_userlocks |= SOCK_RCVBUF_LOCK; - /* - * We double it on the way in to account for - * "struct sk_buff" etc. overhead. Applications - * assume that the SO_RCVBUF setting they make will - * allow that much actual data to be received on that - * socket. - * - * Applications are unaware that "struct sk_buff" and - * other overheads allocate from the receive buffer - * during socket buffer allocation. - * - * And after considering the possible alternatives, - * returning the value we actually used in getsockopt - * is the most desirable behavior. - */ - if ((val * 2) < SOCK_MIN_RCVBUF) - sk->sk_rcvbuf = SOCK_MIN_RCVBUF; - else - sk->sk_rcvbuf = val * 2; + sk->sk_userlocks |= SOCK_RCVBUF_LOCK; + /* + * We double it on the way in to account for + * "struct sk_buff" etc. overhead. Applications + * assume that the SO_RCVBUF setting they make will + * allow that much actual data to be received on that + * socket. + * + * Applications are unaware that "struct sk_buff" and + * other overheads allocate from the receive buffer + * during socket buffer allocation. + * + * And after considering the possible alternatives, + * returning the value we actually used in getsockopt + * is the most desirable behavior. + */ + if ((val * 2) < SOCK_MIN_RCVBUF) + sk->sk_rcvbuf = SOCK_MIN_RCVBUF; + else + sk->sk_rcvbuf = val * 2; + break; + + case SO_RCVBUFFORCE: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; break; + } + goto set_rcvbuf; - case SO_RCVBUFFORCE: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - goto set_rcvbuf; - - case SO_KEEPALIVE: + case SO_KEEPALIVE: #ifdef CONFIG_INET - if (sk->sk_protocol == IPPROTO_TCP) - tcp_set_keepalive(sk, valbool); + if (sk->sk_protocol == IPPROTO_TCP) + tcp_set_keepalive(sk, valbool); #endif - sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool); - break; - - case SO_OOBINLINE: - sock_valbool_flag(sk, SOCK_URGINLINE, valbool); + sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool); + break; + + case SO_OOBINLINE: + sock_valbool_flag(sk, SOCK_URGINLINE, valbool); + break; + + case SO_NO_CHECK: + sk->sk_no_check = valbool; + break; + + case SO_PRIORITY: + if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN)) + sk->sk_priority = val; + else + ret = -EPERM; + break; + + case SO_LINGER: + if (optlen < sizeof(ling)) { + ret = -EINVAL; /* 1003.1g */ break; - - case SO_NO_CHECK: - sk->sk_no_check = valbool; - break; - - case SO_PRIORITY: - if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN)) - sk->sk_priority = val; - else - ret = -EPERM; + } + if (copy_from_user(&ling,optval,sizeof(ling))) { + ret = -EFAULT; break; - - case SO_LINGER: - if(optlen= MAX_SCHEDULE_TIMEOUT/HZ) - sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT; - else + if ((unsigned int)ling.l_linger >= MAX_SCHEDULE_TIMEOUT/HZ) + sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT; + else #endif - sk->sk_lingertime = (unsigned int)ling.l_linger * HZ; - sock_set_flag(sk, SOCK_LINGER); - } - break; - - case SO_BSDCOMPAT: - sock_warn_obsolete_bsdism("setsockopt"); - break; - - case SO_PASSCRED: - if (valbool) - set_bit(SOCK_PASSCRED, &sock->flags); + sk->sk_lingertime = (unsigned int)ling.l_linger * HZ; + sock_set_flag(sk, SOCK_LINGER); + } + break; + + case SO_BSDCOMPAT: + sock_warn_obsolete_bsdism("setsockopt"); + break; + + case SO_PASSCRED: + if (valbool) + set_bit(SOCK_PASSCRED, &sock->flags); + else + clear_bit(SOCK_PASSCRED, &sock->flags); + break; + + case SO_TIMESTAMP: + case SO_TIMESTAMPNS: + if (valbool) { + if (optname == SO_TIMESTAMP) + sock_reset_flag(sk, SOCK_RCVTSTAMPNS); else - clear_bit(SOCK_PASSCRED, &sock->flags); - break; + sock_set_flag(sk, SOCK_RCVTSTAMPNS); + sock_set_flag(sk, SOCK_RCVTSTAMP); + sock_enable_timestamp(sk); + } else { + sock_reset_flag(sk, SOCK_RCVTSTAMP); + sock_reset_flag(sk, SOCK_RCVTSTAMPNS); + } + break; - case SO_TIMESTAMP: - if (valbool) { - sock_set_flag(sk, SOCK_RCVTSTAMP); - sock_enable_timestamp(sk); - } else - sock_reset_flag(sk, SOCK_RCVTSTAMP); - break; + case SO_RCVLOWAT: + if (val < 0) + val = INT_MAX; + sk->sk_rcvlowat = val ? : 1; + break; - case SO_RCVLOWAT: - if (val < 0) - val = INT_MAX; - sk->sk_rcvlowat = val ? : 1; - break; + case SO_RCVTIMEO: + ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen); + break; - case SO_RCVTIMEO: - ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen); - break; + case SO_SNDTIMEO: + ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen); + break; - case SO_SNDTIMEO: - ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen); +#ifdef CONFIG_NETDEVICES + case SO_BINDTODEVICE: + { + char devname[IFNAMSIZ]; + + /* Sorry... */ + if (!capable(CAP_NET_RAW)) { + ret = -EPERM; break; + } -#ifdef CONFIG_NETDEVICES - case SO_BINDTODEVICE: - { - char devname[IFNAMSIZ]; + /* Bind this socket to a particular device like "eth0", + * as specified in the passed interface name. If the + * name is "" or the option length is zero the socket + * is not bound. + */ - /* Sorry... */ - if (!capable(CAP_NET_RAW)) { - ret = -EPERM; + if (!valbool) { + sk->sk_bound_dev_if = 0; + } else { + if (optlen > IFNAMSIZ - 1) + optlen = IFNAMSIZ - 1; + memset(devname, 0, sizeof(devname)); + if (copy_from_user(devname, optval, optlen)) { + ret = -EFAULT; break; } - /* Bind this socket to a particular device like "eth0", - * as specified in the passed interface name. If the - * name is "" or the option length is zero the socket - * is not bound. - */ + /* Remove any cached route for this socket. */ + sk_dst_reset(sk); - if (!valbool) { + if (devname[0] == '\0') { sk->sk_bound_dev_if = 0; } else { - if (optlen > IFNAMSIZ - 1) - optlen = IFNAMSIZ - 1; - memset(devname, 0, sizeof(devname)); - if (copy_from_user(devname, optval, optlen)) { - ret = -EFAULT; + struct net_device *dev = dev_get_by_name(devname); + if (!dev) { + ret = -ENODEV; break; } - - /* Remove any cached route for this socket. */ - sk_dst_reset(sk); - - if (devname[0] == '\0') { - sk->sk_bound_dev_if = 0; - } else { - struct net_device *dev = dev_get_by_name(devname); - if (!dev) { - ret = -ENODEV; - break; - } - sk->sk_bound_dev_if = dev->ifindex; - dev_put(dev); - } + sk->sk_bound_dev_if = dev->ifindex; + dev_put(dev); } - break; } + break; + } #endif - case SO_ATTACH_FILTER: - ret = -EINVAL; - if (optlen == sizeof(struct sock_fprog)) { - struct sock_fprog fprog; + case SO_ATTACH_FILTER: + ret = -EINVAL; + if (optlen == sizeof(struct sock_fprog)) { + struct sock_fprog fprog; - ret = -EFAULT; - if (copy_from_user(&fprog, optval, sizeof(fprog))) - break; - - ret = sk_attach_filter(&fprog, sk); - } - break; - - case SO_DETACH_FILTER: - rcu_read_lock_bh(); - filter = rcu_dereference(sk->sk_filter); - if (filter) { - rcu_assign_pointer(sk->sk_filter, NULL); - sk_filter_release(sk, filter); - rcu_read_unlock_bh(); + ret = -EFAULT; + if (copy_from_user(&fprog, optval, sizeof(fprog))) break; - } + + ret = sk_attach_filter(&fprog, sk); + } + break; + + case SO_DETACH_FILTER: + rcu_read_lock_bh(); + filter = rcu_dereference(sk->sk_filter); + if (filter) { + rcu_assign_pointer(sk->sk_filter, NULL); + sk_filter_release(sk, filter); rcu_read_unlock_bh(); - ret = -ENONET; break; + } + rcu_read_unlock_bh(); + ret = -ENONET; + break; - case SO_PASSSEC: - if (valbool) - set_bit(SOCK_PASSSEC, &sock->flags); - else - clear_bit(SOCK_PASSSEC, &sock->flags); - break; + case SO_PASSSEC: + if (valbool) + set_bit(SOCK_PASSSEC, &sock->flags); + else + clear_bit(SOCK_PASSSEC, &sock->flags); + break; /* We implement the SO_SNDLOWAT etc to not be settable (1003.1g 5.3) */ - default: - ret = -ENOPROTOOPT; - break; + default: + ret = -ENOPROTOOPT; + break; } release_sock(sk); return ret; @@ -641,8 +648,7 @@ int sock_getsockopt(struct socket *sock, { struct sock *sk = sock->sk; - union - { + union { int val; struct linger ling; struct timeval tm; @@ -651,148 +657,153 @@ int sock_getsockopt(struct socket *sock, unsigned int lv = sizeof(int); int len; - if(get_user(len,optlen)) + if (get_user(len, optlen)) return -EFAULT; - if(len < 0) + if (len < 0) return -EINVAL; - switch(optname) - { - case SO_DEBUG: - v.val = sock_flag(sk, SOCK_DBG); - break; - - case SO_DONTROUTE: - v.val = sock_flag(sk, SOCK_LOCALROUTE); - break; - - case SO_BROADCAST: - v.val = !!sock_flag(sk, SOCK_BROADCAST); - break; - - case SO_SNDBUF: - v.val = sk->sk_sndbuf; - break; - - case SO_RCVBUF: - v.val = sk->sk_rcvbuf; - break; - - case SO_REUSEADDR: - v.val = sk->sk_reuse; - break; - - case SO_KEEPALIVE: - v.val = !!sock_flag(sk, SOCK_KEEPOPEN); - break; - - case SO_TYPE: - v.val = sk->sk_type; - break; - - case SO_ERROR: - v.val = -sock_error(sk); - if(v.val==0) - v.val = xchg(&sk->sk_err_soft, 0); - break; - - case SO_OOBINLINE: - v.val = !!sock_flag(sk, SOCK_URGINLINE); - break; - - case SO_NO_CHECK: - v.val = sk->sk_no_check; - break; - - case SO_PRIORITY: - v.val = sk->sk_priority; - break; - - case SO_LINGER: - lv = sizeof(v.ling); - v.ling.l_onoff = !!sock_flag(sk, SOCK_LINGER); - v.ling.l_linger = sk->sk_lingertime / HZ; - break; - - case SO_BSDCOMPAT: - sock_warn_obsolete_bsdism("getsockopt"); - break; - - case SO_TIMESTAMP: - v.val = sock_flag(sk, SOCK_RCVTSTAMP); - break; + switch(optname) { + case SO_DEBUG: + v.val = sock_flag(sk, SOCK_DBG); + break; + + case SO_DONTROUTE: + v.val = sock_flag(sk, SOCK_LOCALROUTE); + break; + + case SO_BROADCAST: + v.val = !!sock_flag(sk, SOCK_BROADCAST); + break; + + case SO_SNDBUF: + v.val = sk->sk_sndbuf; + break; + + case SO_RCVBUF: + v.val = sk->sk_rcvbuf; + break; + + case SO_REUSEADDR: + v.val = sk->sk_reuse; + break; + + case SO_KEEPALIVE: + v.val = !!sock_flag(sk, SOCK_KEEPOPEN); + break; + + case SO_TYPE: + v.val = sk->sk_type; + break; + + case SO_ERROR: + v.val = -sock_error(sk); + if (v.val==0) + v.val = xchg(&sk->sk_err_soft, 0); + break; + + case SO_OOBINLINE: + v.val = !!sock_flag(sk, SOCK_URGINLINE); + break; + + case SO_NO_CHECK: + v.val = sk->sk_no_check; + break; + + case SO_PRIORITY: + v.val = sk->sk_priority; + break; + + case SO_LINGER: + lv = sizeof(v.ling); + v.ling.l_onoff = !!sock_flag(sk, SOCK_LINGER); + v.ling.l_linger = sk->sk_lingertime / HZ; + break; + + case SO_BSDCOMPAT: + sock_warn_obsolete_bsdism("getsockopt"); + break; + + case SO_TIMESTAMP: + v.val = sock_flag(sk, SOCK_RCVTSTAMP) && + !sock_flag(sk, SOCK_RCVTSTAMPNS); + break; + + case SO_TIMESTAMPNS: + v.val = sock_flag(sk, SOCK_RCVTSTAMPNS); + break; + + case SO_RCVTIMEO: + lv=sizeof(struct timeval); + if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) { + v.tm.tv_sec = 0; + v.tm.tv_usec = 0; + } else { + v.tm.tv_sec = sk->sk_rcvtimeo / HZ; + v.tm.tv_usec = ((sk->sk_rcvtimeo % HZ) * 1000000) / HZ; + } + break; + + case SO_SNDTIMEO: + lv=sizeof(struct timeval); + if (sk->sk_sndtimeo == MAX_SCHEDULE_TIMEOUT) { + v.tm.tv_sec = 0; + v.tm.tv_usec = 0; + } else { + v.tm.tv_sec = sk->sk_sndtimeo / HZ; + v.tm.tv_usec = ((sk->sk_sndtimeo % HZ) * 1000000) / HZ; + } + break; - case SO_RCVTIMEO: - lv=sizeof(struct timeval); - if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) { - v.tm.tv_sec = 0; - v.tm.tv_usec = 0; - } else { - v.tm.tv_sec = sk->sk_rcvtimeo / HZ; - v.tm.tv_usec = ((sk->sk_rcvtimeo % HZ) * 1000000) / HZ; - } - break; + case SO_RCVLOWAT: + v.val = sk->sk_rcvlowat; + break; - case SO_SNDTIMEO: - lv=sizeof(struct timeval); - if (sk->sk_sndtimeo == MAX_SCHEDULE_TIMEOUT) { - v.tm.tv_sec = 0; - v.tm.tv_usec = 0; - } else { - v.tm.tv_sec = sk->sk_sndtimeo / HZ; - v.tm.tv_usec = ((sk->sk_sndtimeo % HZ) * 1000000) / HZ; - } - break; + case SO_SNDLOWAT: + v.val=1; + break; - case SO_RCVLOWAT: - v.val = sk->sk_rcvlowat; - break; + case SO_PASSCRED: + v.val = test_bit(SOCK_PASSCRED, &sock->flags) ? 1 : 0; + break; - case SO_SNDLOWAT: - v.val=1; - break; + case SO_PEERCRED: + if (len > sizeof(sk->sk_peercred)) + len = sizeof(sk->sk_peercred); + if (copy_to_user(optval, &sk->sk_peercred, len)) + return -EFAULT; + goto lenout; - case SO_PASSCRED: - v.val = test_bit(SOCK_PASSCRED, &sock->flags) ? 1 : 0; - break; - - case SO_PEERCRED: - if (len > sizeof(sk->sk_peercred)) - len = sizeof(sk->sk_peercred); - if (copy_to_user(optval, &sk->sk_peercred, len)) - return -EFAULT; - goto lenout; - - case SO_PEERNAME: - { - char address[128]; - - if (sock->ops->getname(sock, (struct sockaddr *)address, &lv, 2)) - return -ENOTCONN; - if (lv < len) - return -EINVAL; - if (copy_to_user(optval, address, len)) - return -EFAULT; - goto lenout; - } + case SO_PEERNAME: + { + char address[128]; + + if (sock->ops->getname(sock, (struct sockaddr *)address, &lv, 2)) + return -ENOTCONN; + if (lv < len) + return -EINVAL; + if (copy_to_user(optval, address, len)) + return -EFAULT; + goto lenout; + } - /* Dubious BSD thing... Probably nobody even uses it, but - * the UNIX standard wants it for whatever reason... -DaveM - */ - case SO_ACCEPTCONN: - v.val = sk->sk_state == TCP_LISTEN; - break; + /* Dubious BSD thing... Probably nobody even uses it, but + * the UNIX standard wants it for whatever reason... -DaveM + */ + case SO_ACCEPTCONN: + v.val = sk->sk_state == TCP_LISTEN; + break; - case SO_PASSSEC: - v.val = test_bit(SOCK_PASSSEC, &sock->flags) ? 1 : 0; - break; + case SO_PASSSEC: + v.val = test_bit(SOCK_PASSSEC, &sock->flags) ? 1 : 0; + break; - case SO_PEERSEC: - return security_socket_getpeersec_stream(sock, optval, optlen, len); + case SO_PEERSEC: + return security_socket_getpeersec_stream(sock, optval, optlen, len); - default: - return(-ENOPROTOOPT); + default: + return -ENOPROTOOPT; } + if (len > lv) len = lv; if (copy_to_user(optval, &v, len)) @@ -904,6 +915,7 @@ struct sock *sk_clone(const struct sock sk_node_init(&newsk->sk_node); sock_lock_init(newsk); bh_lock_sock(newsk); + newsk->sk_backlog.head = newsk->sk_backlog.tail = NULL; atomic_set(&newsk->sk_rmem_alloc, 0); atomic_set(&newsk->sk_wmem_alloc, 0); @@ -923,7 +935,6 @@ #endif newsk->sk_wmem_queued = 0; newsk->sk_forward_alloc = 0; newsk->sk_send_head = NULL; - newsk->sk_backlog.head = newsk->sk_backlog.tail = NULL; newsk->sk_userlocks = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; sock_reset_flag(newsk, SOCK_DONE); @@ -970,6 +981,21 @@ out: EXPORT_SYMBOL_GPL(sk_clone); +void sk_setup_caps(struct sock *sk, struct dst_entry *dst) +{ + __sk_dst_set(sk, dst); + sk->sk_route_caps = dst->dev->features; + if (sk->sk_route_caps & NETIF_F_GSO) + sk->sk_route_caps |= NETIF_F_GSO_MASK; + if (sk_can_gso(sk)) { + if (dst->header_len) + sk->sk_route_caps &= ~NETIF_F_GSO_MASK; + else + sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; + } +} +EXPORT_SYMBOL_GPL(sk_setup_caps); + void __init sk_init(void) { if (num_physpages <= 4096) { @@ -1220,13 +1246,13 @@ static void __lock_sock(struct sock *sk) { DEFINE_WAIT(wait); - for(;;) { + for (;;) { prepare_to_wait_exclusive(&sk->sk_lock.wq, &wait, TASK_UNINTERRUPTIBLE); spin_unlock_bh(&sk->sk_lock.slock); schedule(); spin_lock_bh(&sk->sk_lock.slock); - if(!sock_owned_by_user(sk)) + if (!sock_owned_by_user(sk)) break; } finish_wait(&sk->sk_lock.wq, &wait); @@ -1258,7 +1284,7 @@ static void __release_sock(struct sock * } while (skb != NULL); bh_lock_sock(sk); - } while((skb = sk->sk_backlog.head) != NULL); + } while ((skb = sk->sk_backlog.head) != NULL); } /** @@ -1420,7 +1446,7 @@ static void sock_def_write_space(struct /* Do not wake up a writer until he can make "significant" * progress. --DaveM */ - if((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) { + if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) { if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) wake_up_interruptible(sk->sk_sleep); @@ -1482,8 +1508,7 @@ #endif sock_set_flag(sk, SOCK_ZAPPED); - if(sock) - { + if (sock) { sk->sk_type = sock->type; sk->sk_sleep = &sock->wait; sock->sk = sk; @@ -1512,8 +1537,7 @@ #endif sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; - sk->sk_stamp.tv_sec = -1L; - sk->sk_stamp.tv_usec = -1L; + sk->sk_stamp = ktime_set(-1L, -1L); atomic_set(&sk->sk_refcnt, 1); } @@ -1554,17 +1578,36 @@ EXPORT_SYMBOL(release_sock); int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp) { + struct timeval tv; if (!sock_flag(sk, SOCK_TIMESTAMP)) sock_enable_timestamp(sk); - if (sk->sk_stamp.tv_sec == -1) + tv = ktime_to_timeval(sk->sk_stamp); + if (tv.tv_sec == -1) return -ENOENT; - if (sk->sk_stamp.tv_sec == 0) - do_gettimeofday(&sk->sk_stamp); - return copy_to_user(userstamp, &sk->sk_stamp, sizeof(struct timeval)) ? - -EFAULT : 0; + if (tv.tv_sec == 0) { + sk->sk_stamp = ktime_get_real(); + tv = ktime_to_timeval(sk->sk_stamp); + } + return copy_to_user(userstamp, &tv, sizeof(tv)) ? -EFAULT : 0; } EXPORT_SYMBOL(sock_get_timestamp); +int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp) +{ + struct timespec ts; + if (!sock_flag(sk, SOCK_TIMESTAMP)) + sock_enable_timestamp(sk); + ts = ktime_to_timespec(sk->sk_stamp); + if (ts.tv_sec == -1) + return -ENOENT; + if (ts.tv_sec == 0) { + sk->sk_stamp = ktime_get_real(); + ts = ktime_to_timespec(sk->sk_stamp); + } + return copy_to_user(userstamp, &ts, sizeof(ts)) ? -EFAULT : 0; +} +EXPORT_SYMBOL(sock_get_timestampns); + void sock_enable_timestamp(struct sock *sk) { if (!sock_flag(sk, SOCK_TIMESTAMP)) { @@ -1899,7 +1942,7 @@ static int proto_seq_show(struct seq_fil return 0; } -static struct seq_operations proto_seq_ops = { +static const struct seq_operations proto_seq_ops = { .start = proto_seq_start, .next = proto_seq_next, .stop = proto_seq_stop, diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 1e75b15..b297120 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -136,6 +136,14 @@ #endif /* CONFIG_NET */ .mode = 0644, .proc_handler = &proc_dointvec }, + { + .ctl_name = NET_CORE_WARNINGS, + .procname = "warnings", + .data = &net_msg_warn, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .ctl_name = 0 } }; diff --git a/net/core/utils.c b/net/core/utils.c index 07236c1..adecfd2 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -30,8 +30,10 @@ #include #include #include -int net_msg_cost = 5*HZ; -int net_msg_burst = 10; +int net_msg_cost __read_mostly = 5*HZ; +int net_msg_burst __read_mostly = 10; +int net_msg_warn __read_mostly = 1; +EXPORT_SYMBOL(net_msg_warn); /* * All net warning printk()s should be guarded by this function. diff --git a/net/core/wireless.c b/net/core/wireless.c deleted file mode 100644 index b07fe27..0000000 --- a/net/core/wireless.c +++ /dev/null @@ -1,2371 +0,0 @@ -/* - * This file implement the Wireless Extensions APIs. - * - * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ - -/************************** DOCUMENTATION **************************/ -/* - * API definition : - * -------------- - * See for details of the APIs and the rest. - * - * History : - * ------- - * - * v1 - 5.12.01 - Jean II - * o Created this file. - * - * v2 - 13.12.01 - Jean II - * o Move /proc/net/wireless stuff from net/core/dev.c to here - * o Make Wireless Extension IOCTLs go through here - * o Added iw_handler handling ;-) - * o Added standard ioctl description - * o Initial dumb commit strategy based on orinoco.c - * - * v3 - 19.12.01 - Jean II - * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call - * o Add event dispatcher function - * o Add event description - * o Propagate events as rtnetlink IFLA_WIRELESS option - * o Generate event on selected SET requests - * - * v4 - 18.04.02 - Jean II - * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 - * - * v5 - 21.06.02 - Jean II - * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup) - * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes - * o Add IWEVCUSTOM for driver specific event/scanning token - * o Turn on WE_STRICT_WRITE by default + kernel warning - * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) - * o Fix off-by-one in test (extra_size <= IFNAMSIZ) - * - * v6 - 9.01.03 - Jean II - * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() - * o Add enhanced spy support : iw_handler_set_thrspy() and event. - * o Add WIRELESS_EXT version display in /proc/net/wireless - * - * v6 - 18.06.04 - Jean II - * o Change get_spydata() method for added safety - * o Remove spy #ifdef, they are always on -> cleaner code - * o Allow any size GET request if user specifies length > max - * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV - * o Start migrating get_wireless_stats to struct iw_handler_def - * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus - * Based on patch from Pavel Roskin : - * o Fix kernel data leak to user space in private handler handling - * - * v7 - 18.3.05 - Jean II - * o Remove (struct iw_point *)->pointer from events and streams - * o Remove spy_offset from struct iw_handler_def - * o Start deprecating dev->get_wireless_stats, output a warning - * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless - * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats) - * - * v8 - 17.02.06 - Jean II - * o RtNetlink requests support (SET/GET) - * - * v8b - 03.08.06 - Herbert Xu - * o Fix Wireless Event locking issues. - * - * v9 - 14.3.06 - Jean II - * o Change length in ESSID and NICK to strlen() instead of strlen()+1 - * o Make standard_ioctl_num and standard_event_num unsigned - * o Remove (struct net_device *)->get_wireless_stats() - * - * v10 - 16.3.07 - Jean II - * o Prevent leaking of kernel space in stream on 64 bits. - */ - -/***************************** INCLUDES *****************************/ - -#include -#include /* off_t */ -#include /* struct ifreq, dev_get_by_name() */ -#include -#include /* rtnetlink stuff */ -#include -#include /* for __init */ -#include /* ARPHRD_ETHER */ -#include /* compare_ether_addr */ -#include - -#include /* Pretty obvious */ -#include /* New driver API */ -#include - -#include /* copy_to_user() */ - -/**************************** CONSTANTS ****************************/ - -/* Debugging stuff */ -#undef WE_IOCTL_DEBUG /* Debug IOCTL API */ -#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */ -#undef WE_EVENT_DEBUG /* Debug Event dispatcher */ -#undef WE_SPY_DEBUG /* Debug enhanced spy support */ - -/* Options */ -//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */ -#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */ -#define WE_SET_EVENT /* Generate an event on some set commands */ - -/************************* GLOBAL VARIABLES *************************/ -/* - * You should not use global variables, because of re-entrancy. - * On our case, it's only const, so it's OK... - */ -/* - * Meta-data about all the standard Wireless Extension request we - * know about. - */ -static const struct iw_ioctl_description standard_ioctl[] = { - [SIOCSIWCOMMIT - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWNAME - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_CHAR, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWNWID - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_EVENT, - }, - [SIOCGIWNWID - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWFREQ - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_EVENT, - }, - [SIOCGIWFREQ - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWMODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_EVENT, - }, - [SIOCGIWMODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWSENS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWSENS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWRANGE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWRANGE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_range), - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWPRIV - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_priv_args), - .max_tokens = 16, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [SIOCSIWSTATS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_statistics), - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr), - .max_tokens = IW_MAX_SPY, - }, - [SIOCGIWSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr) + - sizeof(struct iw_quality), - .max_tokens = IW_MAX_SPY, - }, - [SIOCSIWTHRSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [SIOCGIWTHRSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [SIOCSIWAP - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [SIOCGIWAP - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWMLME - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_mlme), - .max_tokens = sizeof(struct iw_mlme), - }, - [SIOCGIWAPLIST - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr) + - sizeof(struct iw_quality), - .max_tokens = IW_MAX_AP, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [SIOCSIWSCAN - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = 0, - .max_tokens = sizeof(struct iw_scan_req), - }, - [SIOCGIWSCAN - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_SCAN_MAX_DATA, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [SIOCSIWESSID - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - .flags = IW_DESCR_FLAG_EVENT, - }, - [SIOCGIWESSID - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWNICKN - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - }, - [SIOCGIWNICKN - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - }, - [SIOCSIWRATE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWRATE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWRTS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWRTS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWFRAG - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWFRAG - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWTXPOW - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWTXPOW - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWRETRY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWRETRY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWENCODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, - }, - [SIOCGIWENCODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, - }, - [SIOCSIWPOWER - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWPOWER - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWGENIE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [SIOCGIWGENIE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [SIOCSIWAUTH - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWAUTH - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWENCODEEXT - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_encode_ext), - .max_tokens = sizeof(struct iw_encode_ext) + - IW_ENCODING_TOKEN_MAX, - }, - [SIOCGIWENCODEEXT - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_encode_ext), - .max_tokens = sizeof(struct iw_encode_ext) + - IW_ENCODING_TOKEN_MAX, - }, - [SIOCSIWPMKSA - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_pmksa), - .max_tokens = sizeof(struct iw_pmksa), - }, -}; -static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) / - sizeof(struct iw_ioctl_description)); - -/* - * Meta-data about all the additional standard Wireless Extension events - * we know about. - */ -static const struct iw_ioctl_description standard_event[] = { - [IWEVTXDROP - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IWEVQUAL - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_QUAL, - }, - [IWEVCUSTOM - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_CUSTOM_MAX, - }, - [IWEVREGISTERED - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IWEVEXPIRED - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IWEVGENIE - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IWEVMICHAELMICFAILURE - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_michaelmicfailure), - }, - [IWEVASSOCREQIE - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IWEVASSOCRESPIE - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IWEVPMKIDCAND - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_pmkid_cand), - }, -}; -static const unsigned standard_event_num = (sizeof(standard_event) / - sizeof(struct iw_ioctl_description)); - -/* Size (in bytes) of the various private data types */ -static const char iw_priv_type_size[] = { - 0, /* IW_PRIV_TYPE_NONE */ - 1, /* IW_PRIV_TYPE_BYTE */ - 1, /* IW_PRIV_TYPE_CHAR */ - 0, /* Not defined */ - sizeof(__u32), /* IW_PRIV_TYPE_INT */ - sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ - sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ - 0, /* Not defined */ -}; - -/* Size (in bytes) of various events */ -static const int event_type_size[] = { - IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ - 0, - IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, - IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ - IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ - IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, - IW_EV_POINT_LEN, /* Without variable payload */ - IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ - IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ -}; - -/* Size (in bytes) of various events, as packed */ -static const int event_type_pk_size[] = { - IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */ - 0, - IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, - IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */ - IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */ - IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, - IW_EV_POINT_PK_LEN, /* Without variable payload */ - IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */ - IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */ -}; - -/************************ COMMON SUBROUTINES ************************/ -/* - * Stuff that may be used in various place or doesn't fit in one - * of the section below. - */ - -/* ---------------------------------------------------------------- */ -/* - * Return the driver handler associated with a specific Wireless Extension. - * Called from various place, so make sure it remains efficient. - */ -static inline iw_handler get_handler(struct net_device *dev, - unsigned int cmd) -{ - /* Don't "optimise" the following variable, it will crash */ - unsigned int index; /* *MUST* be unsigned */ - - /* Check if we have some wireless handlers defined */ - if(dev->wireless_handlers == NULL) - return NULL; - - /* Try as a standard command */ - index = cmd - SIOCIWFIRST; - if(index < dev->wireless_handlers->num_standard) - return dev->wireless_handlers->standard[index]; - - /* Try as a private command */ - index = cmd - SIOCIWFIRSTPRIV; - if(index < dev->wireless_handlers->num_private) - return dev->wireless_handlers->private[index]; - - /* Not found */ - return NULL; -} - -/* ---------------------------------------------------------------- */ -/* - * Get statistics out of the driver - */ -static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) -{ - /* New location */ - if((dev->wireless_handlers != NULL) && - (dev->wireless_handlers->get_wireless_stats != NULL)) - return dev->wireless_handlers->get_wireless_stats(dev); - - /* Not found */ - return (struct iw_statistics *) NULL; -} - -/* ---------------------------------------------------------------- */ -/* - * Call the commit handler in the driver - * (if exist and if conditions are right) - * - * Note : our current commit strategy is currently pretty dumb, - * but we will be able to improve on that... - * The goal is to try to agreagate as many changes as possible - * before doing the commit. Drivers that will define a commit handler - * are usually those that need a reset after changing parameters, so - * we want to minimise the number of reset. - * A cool idea is to use a timer : at each "set" command, we re-set the - * timer, when the timer eventually fires, we call the driver. - * Hopefully, more on that later. - * - * Also, I'm waiting to see how many people will complain about the - * netif_running(dev) test. I'm open on that one... - * Hopefully, the driver will remember to do a commit in "open()" ;-) - */ -static inline int call_commit_handler(struct net_device * dev) -{ - if((netif_running(dev)) && - (dev->wireless_handlers->standard[0] != NULL)) { - /* Call the commit handler on the driver */ - return dev->wireless_handlers->standard[0](dev, NULL, - NULL, NULL); - } else - return 0; /* Command completed successfully */ -} - -/* ---------------------------------------------------------------- */ -/* - * Calculate size of private arguments - */ -static inline int get_priv_size(__u16 args) -{ - int num = args & IW_PRIV_SIZE_MASK; - int type = (args & IW_PRIV_TYPE_MASK) >> 12; - - return num * iw_priv_type_size[type]; -} - -/* ---------------------------------------------------------------- */ -/* - * Re-calculate the size of private arguments - */ -static inline int adjust_priv_size(__u16 args, - union iwreq_data * wrqu) -{ - int num = wrqu->data.length; - int max = args & IW_PRIV_SIZE_MASK; - int type = (args & IW_PRIV_TYPE_MASK) >> 12; - - /* Make sure the driver doesn't goof up */ - if (max < num) - num = max; - - return num * iw_priv_type_size[type]; -} - -/* ---------------------------------------------------------------- */ -/* - * Standard Wireless Handler : get wireless stats - * Allow programatic access to /proc/net/wireless even if /proc - * doesn't exist... Also more efficient... - */ -static int iw_handler_get_iwstats(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - /* Get stats from the driver */ - struct iw_statistics *stats; - - stats = get_wireless_stats(dev); - if (stats != (struct iw_statistics *) NULL) { - - /* Copy statistics to extra */ - memcpy(extra, stats, sizeof(struct iw_statistics)); - wrqu->data.length = sizeof(struct iw_statistics); - - /* Check if we need to clear the updated flag */ - if(wrqu->data.flags != 0) - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; - return 0; - } else - return -EOPNOTSUPP; -} - -/* ---------------------------------------------------------------- */ -/* - * Standard Wireless Handler : get iwpriv definitions - * Export the driver private handler definition - * They will be picked up by tools like iwpriv... - */ -static int iw_handler_get_private(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - /* Check if the driver has something to export */ - if((dev->wireless_handlers->num_private_args == 0) || - (dev->wireless_handlers->private_args == NULL)) - return -EOPNOTSUPP; - - /* Check if there is enough buffer up there */ - if(wrqu->data.length < dev->wireless_handlers->num_private_args) { - /* User space can't know in advance how large the buffer - * needs to be. Give it a hint, so that we can support - * any size buffer we want somewhat efficiently... */ - wrqu->data.length = dev->wireless_handlers->num_private_args; - return -E2BIG; - } - - /* Set the number of available ioctls. */ - wrqu->data.length = dev->wireless_handlers->num_private_args; - - /* Copy structure to the user buffer. */ - memcpy(extra, dev->wireless_handlers->private_args, - sizeof(struct iw_priv_args) * wrqu->data.length); - - return 0; -} - - -/******************** /proc/net/wireless SUPPORT ********************/ -/* - * The /proc/net/wireless file is a human readable user-space interface - * exporting various wireless specific statistics from the wireless devices. - * This is the most popular part of the Wireless Extensions ;-) - * - * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). - * The content of the file is basically the content of "struct iw_statistics". - */ - -#ifdef CONFIG_PROC_FS - -/* ---------------------------------------------------------------- */ -/* - * Print one entry (line) of /proc/net/wireless - */ -static __inline__ void wireless_seq_printf_stats(struct seq_file *seq, - struct net_device *dev) -{ - /* Get stats from the driver */ - struct iw_statistics *stats = get_wireless_stats(dev); - - if (stats) { - seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " - "%6d %6d %6d\n", - dev->name, stats->status, stats->qual.qual, - stats->qual.updated & IW_QUAL_QUAL_UPDATED - ? '.' : ' ', - ((__s32) stats->qual.level) - - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), - stats->qual.updated & IW_QUAL_LEVEL_UPDATED - ? '.' : ' ', - ((__s32) stats->qual.noise) - - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), - stats->qual.updated & IW_QUAL_NOISE_UPDATED - ? '.' : ' ', - stats->discard.nwid, stats->discard.code, - stats->discard.fragment, stats->discard.retries, - stats->discard.misc, stats->miss.beacon); - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; - } -} - -/* ---------------------------------------------------------------- */ -/* - * Print info for /proc/net/wireless (print all entries) - */ -static int wireless_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_printf(seq, "Inter-| sta-| Quality | Discarded " - "packets | Missed | WE\n" - " face | tus | link level noise | nwid " - "crypt frag retry misc | beacon | %d\n", - WIRELESS_EXT); - else - wireless_seq_printf_stats(seq, v); - return 0; -} - -static struct seq_operations wireless_seq_ops = { - .start = dev_seq_start, - .next = dev_seq_next, - .stop = dev_seq_stop, - .show = wireless_seq_show, -}; - -static int wireless_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &wireless_seq_ops); -} - -static const struct file_operations wireless_seq_fops = { - .owner = THIS_MODULE, - .open = wireless_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -int __init wireless_proc_init(void) -{ - /* Create /proc/net/wireless entry */ - if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops)) - return -ENOMEM; - - return 0; -} -#endif /* CONFIG_PROC_FS */ - -/************************** IOCTL SUPPORT **************************/ -/* - * The original user space API to configure all those Wireless Extensions - * is through IOCTLs. - * In there, we check if we need to call the new driver API (iw_handler) - * or just call the driver ioctl handler. - */ - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a standard Wireless Extension handler. - * We do various checks and also take care of moving data between - * user space and kernel space. - */ -static int ioctl_standard_call(struct net_device * dev, - struct ifreq * ifr, - unsigned int cmd, - iw_handler handler) -{ - struct iwreq * iwr = (struct iwreq *) ifr; - const struct iw_ioctl_description * descr; - struct iw_request_info info; - int ret = -EINVAL; - - /* Get the description of the IOCTL */ - if((cmd - SIOCIWFIRST) >= standard_ioctl_num) - return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n", - ifr->ifr_name, cmd); - printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -#endif /* WE_IOCTL_DEBUG */ - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have a pointer to user space data or not */ - if(descr->header_type != IW_HEADER_TYPE_POINT) { - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, &(iwr->u), NULL); - -#ifdef WE_SET_EVENT - /* Generate an event to notify listeners of the change */ - if((descr->flags & IW_DESCR_FLAG_EVENT) && - ((ret == 0) || (ret == -EIWCOMMIT))) - wireless_send_event(dev, cmd, &(iwr->u), NULL); -#endif /* WE_SET_EVENT */ - } else { - char * extra; - int extra_size; - int user_length = 0; - int err; - int essid_compat = 0; - - /* Calculate space needed by arguments. Always allocate - * for max space. Easier, and won't last long... */ - extra_size = descr->max_tokens * descr->token_size; - - /* Check need for ESSID compatibility for WE < 21 */ - switch (cmd) { - case SIOCSIWESSID: - case SIOCGIWESSID: - case SIOCSIWNICKN: - case SIOCGIWNICKN: - if (iwr->u.data.length == descr->max_tokens + 1) - essid_compat = 1; - else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { - char essid[IW_ESSID_MAX_SIZE + 1]; - - err = copy_from_user(essid, iwr->u.data.pointer, - iwr->u.data.length * - descr->token_size); - if (err) - return -EFAULT; - - if (essid[iwr->u.data.length - 1] == '\0') - essid_compat = 1; - } - break; - default: - break; - } - - iwr->u.data.length -= essid_compat; - - /* Check what user space is giving us */ - if(IW_IS_SET(cmd)) { - /* Check NULL pointer */ - if((iwr->u.data.pointer == NULL) && - (iwr->u.data.length != 0)) - return -EFAULT; - /* Check if number of token fits within bounds */ - if(iwr->u.data.length > descr->max_tokens) - return -E2BIG; - if(iwr->u.data.length < descr->min_tokens) - return -EINVAL; - } else { - /* Check NULL pointer */ - if(iwr->u.data.pointer == NULL) - return -EFAULT; - /* Save user space buffer size for checking */ - user_length = iwr->u.data.length; - - /* Don't check if user_length > max to allow forward - * compatibility. The test user_length < min is - * implied by the test at the end. */ - - /* Support for very large requests */ - if((descr->flags & IW_DESCR_FLAG_NOMAX) && - (user_length > descr->max_tokens)) { - /* Allow userspace to GET more than max so - * we can support any size GET requests. - * There is still a limit : -ENOMEM. */ - extra_size = user_length * descr->token_size; - /* Note : user_length is originally a __u16, - * and token_size is controlled by us, - * so extra_size won't get negative and - * won't overflow... */ - } - } - -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", - dev->name, extra_size); -#endif /* WE_IOCTL_DEBUG */ - - /* Create the kernel buffer */ - /* kzalloc ensures NULL-termination for essid_compat */ - extra = kzalloc(extra_size, GFP_KERNEL); - if (extra == NULL) { - return -ENOMEM; - } - - /* If it is a SET, get all the extra data in here */ - if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { - err = copy_from_user(extra, iwr->u.data.pointer, - iwr->u.data.length * - descr->token_size); - if (err) { - kfree(extra); - return -EFAULT; - } -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Got %d bytes\n", - dev->name, - iwr->u.data.length * descr->token_size); -#endif /* WE_IOCTL_DEBUG */ - } - - /* Call the handler */ - ret = handler(dev, &info, &(iwr->u), extra); - - iwr->u.data.length += essid_compat; - - /* If we have something to return to the user */ - if (!ret && IW_IS_GET(cmd)) { - /* Check if there is enough buffer up there */ - if(user_length < iwr->u.data.length) { - kfree(extra); - return -E2BIG; - } - - err = copy_to_user(iwr->u.data.pointer, extra, - iwr->u.data.length * - descr->token_size); - if (err) - ret = -EFAULT; -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n", - dev->name, - iwr->u.data.length * descr->token_size); -#endif /* WE_IOCTL_DEBUG */ - } - -#ifdef WE_SET_EVENT - /* Generate an event to notify listeners of the change */ - if((descr->flags & IW_DESCR_FLAG_EVENT) && - ((ret == 0) || (ret == -EIWCOMMIT))) { - if(descr->flags & IW_DESCR_FLAG_RESTRICT) - /* If the event is restricted, don't - * export the payload */ - wireless_send_event(dev, cmd, &(iwr->u), NULL); - else - wireless_send_event(dev, cmd, &(iwr->u), - extra); - } -#endif /* WE_SET_EVENT */ - - /* Cleanup - I told you it wasn't that long ;-) */ - kfree(extra); - } - - /* Call commit handler if needed and defined */ - if(ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - /* Here, we will generate the appropriate event if needed */ - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a private Wireless Extension handler. - * We do various checks and also take care of moving data between - * user space and kernel space. - * It's not as nice and slimline as the standard wrapper. The cause - * is struct iw_priv_args, which was not really designed for the - * job we are going here. - * - * IMPORTANT : This function prevent to set and get data on the same - * IOCTL and enforce the SET/GET convention. Not doing it would be - * far too hairy... - * If you need to set and get data at the same time, please don't use - * a iw_handler but process it in your ioctl handler (i.e. use the - * old driver API). - */ -static inline int ioctl_private_call(struct net_device * dev, - struct ifreq * ifr, - unsigned int cmd, - iw_handler handler) -{ - struct iwreq * iwr = (struct iwreq *) ifr; - const struct iw_priv_args * descr = NULL; - struct iw_request_info info; - int extra_size = 0; - int i; - int ret = -EINVAL; - - /* Get the description of the IOCTL */ - for(i = 0; i < dev->wireless_handlers->num_private_args; i++) - if(cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &(dev->wireless_handlers->private_args[i]); - break; - } - -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n", - ifr->ifr_name, cmd); - if(descr) { - printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n", - dev->name, descr->name, - descr->set_args, descr->get_args); - } -#endif /* WE_IOCTL_DEBUG */ - - /* Compute the size of the set/get arguments */ - if(descr != NULL) { - if(IW_IS_SET(cmd)) { - int offset = 0; /* For sub-ioctls */ - /* Check for sub-ioctl handler */ - if(descr->name[0] == '\0') - /* Reserve one int for sub-ioctl index */ - offset = sizeof(__u32); - - /* Size of set arguments */ - extra_size = get_priv_size(descr->set_args); - - /* Does it fits in iwr ? */ - if((descr->set_args & IW_PRIV_SIZE_FIXED) && - ((extra_size + offset) <= IFNAMSIZ)) - extra_size = 0; - } else { - /* Size of get arguments */ - extra_size = get_priv_size(descr->get_args); - - /* Does it fits in iwr ? */ - if((descr->get_args & IW_PRIV_SIZE_FIXED) && - (extra_size <= IFNAMSIZ)) - extra_size = 0; - } - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have a pointer to user space data or not. */ - if(extra_size == 0) { - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); - } else { - char * extra; - int err; - - /* Check what user space is giving us */ - if(IW_IS_SET(cmd)) { - /* Check NULL pointer */ - if((iwr->u.data.pointer == NULL) && - (iwr->u.data.length != 0)) - return -EFAULT; - - /* Does it fits within bounds ? */ - if(iwr->u.data.length > (descr->set_args & - IW_PRIV_SIZE_MASK)) - return -E2BIG; - } else { - /* Check NULL pointer */ - if(iwr->u.data.pointer == NULL) - return -EFAULT; - } - -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", - dev->name, extra_size); -#endif /* WE_IOCTL_DEBUG */ - - /* Always allocate for max space. Easier, and won't last - * long... */ - extra = kmalloc(extra_size, GFP_KERNEL); - if (extra == NULL) { - return -ENOMEM; - } - - /* If it is a SET, get all the extra data in here */ - if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { - err = copy_from_user(extra, iwr->u.data.pointer, - extra_size); - if (err) { - kfree(extra); - return -EFAULT; - } -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Got %d elem\n", - dev->name, iwr->u.data.length); -#endif /* WE_IOCTL_DEBUG */ - } - - /* Call the handler */ - ret = handler(dev, &info, &(iwr->u), extra); - - /* If we have something to return to the user */ - if (!ret && IW_IS_GET(cmd)) { - - /* Adjust for the actual length if it's variable, - * avoid leaking kernel bits outside. */ - if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { - extra_size = adjust_priv_size(descr->get_args, - &(iwr->u)); - } - - err = copy_to_user(iwr->u.data.pointer, extra, - extra_size); - if (err) - ret = -EFAULT; -#ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n", - dev->name, iwr->u.data.length); -#endif /* WE_IOCTL_DEBUG */ - } - - /* Cleanup - I told you it wasn't that long ;-) */ - kfree(extra); - } - - - /* Call commit handler if needed and defined */ - if(ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Main IOCTl dispatcher. Called from the main networking code - * (dev_ioctl() in net/core/dev.c). - * Check the type of IOCTL and call the appropriate wrapper... - */ -int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd) -{ - struct net_device *dev; - iw_handler handler; - - /* Permissions are already checked in dev_ioctl() before calling us. - * The copy_to/from_user() of ifr is also dealt with in there */ - - /* Make sure the device exist */ - if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) - return -ENODEV; - - /* A bunch of special cases, then the generic case... - * Note that 'cmd' is already filtered in dev_ioctl() with - * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ - switch(cmd) - { - case SIOCGIWSTATS: - /* Get Wireless Stats */ - return ioctl_standard_call(dev, - ifr, - cmd, - &iw_handler_get_iwstats); - - case SIOCGIWPRIV: - /* Check if we have some wireless handlers defined */ - if(dev->wireless_handlers != NULL) { - /* We export to user space the definition of - * the private handler ourselves */ - return ioctl_standard_call(dev, - ifr, - cmd, - &iw_handler_get_private); - } - // ## Fall-through for old API ## - default: - /* Generic IOCTL */ - /* Basic check */ - if (!netif_device_present(dev)) - return -ENODEV; - /* New driver API : try to find the handler */ - handler = get_handler(dev, cmd); - if(handler != NULL) { - /* Standard and private are not the same */ - if(cmd < SIOCIWFIRSTPRIV) - return ioctl_standard_call(dev, - ifr, - cmd, - handler); - else - return ioctl_private_call(dev, - ifr, - cmd, - handler); - } - /* Old driver API : call driver ioctl handler */ - if (dev->do_ioctl) { - return dev->do_ioctl(dev, ifr, cmd); - } - return -EOPNOTSUPP; - } - /* Not reached */ - return -EINVAL; -} - -/********************** RTNETLINK REQUEST API **********************/ -/* - * The alternate user space API to configure all those Wireless Extensions - * is through RtNetlink. - * This API support only the new driver API (iw_handler). - * - * This RtNetlink API use the same query/reply model as the ioctl API. - * Maximum effort has been done to fit in the RtNetlink model, and - * we support both RtNetlink Set and RtNelink Get operations. - * On the other hand, we don't offer Dump operations because of the - * following reasons : - * o Large number of parameters, most optional - * o Large size of some parameters (> 100 bytes) - * o Each parameters need to be extracted from hardware - * o Scan requests can take seconds and disable network activity. - * Because of this high cost/overhead, we want to return only the - * parameters the user application is really interested in. - * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag. - * - * The API uses the standard RtNetlink socket. When the RtNetlink code - * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request, - * it calls here. - */ - -#ifdef CONFIG_NET_WIRELESS_RTNETLINK -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a standard Wireless Extension GET handler. - * We do various checks and call the handler with the proper args. - */ -static int rtnetlink_standard_get(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler, - char ** p_buf, - int * p_len) -{ - const struct iw_ioctl_description * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - int hdr_len; - struct iw_request_info info; - char * buffer = NULL; - int buffer_size = 0; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - if((cmd - SIOCIWFIRST) >= standard_ioctl_num) - return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Check if wrqu is complete */ - hdr_len = event_type_size[descr->header_type]; - if(request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have extra data in the reply or not */ - if(descr->header_type != IW_HEADER_TYPE_POINT) { - - /* Create the kernel buffer that we will return. - * It's at an offset to match the TYPE_POINT case... */ - buffer_size = request_len + IW_EV_POINT_OFF; - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - /* Copy event data */ - memcpy(buffer + IW_EV_POINT_OFF, request, request_len); - /* Use our own copy of wrqu */ - wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF - + IW_EV_LCP_PK_LEN); - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, NULL); - - } else { - union iwreq_data wrqu_point; - char * extra = NULL; - int extra_size = 0; - - /* Get a temp copy of wrqu (skip pointer) */ - memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, - ((char *) request) + IW_EV_LCP_PK_LEN, - IW_EV_POINT_LEN - IW_EV_LCP_PK_LEN); - - /* Calculate space needed by arguments. Always allocate - * for max space. Easier, and won't last long... */ - extra_size = descr->max_tokens * descr->token_size; - /* Support for very large requests */ - if((descr->flags & IW_DESCR_FLAG_NOMAX) && - (wrqu_point.data.length > descr->max_tokens)) - extra_size = (wrqu_point.data.length - * descr->token_size); - buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF; -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n", - dev->name, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Create the kernel buffer that we will return */ - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - - /* Put wrqu in the right place (just before extra). - * Leave space for IWE header and dummy pointer... - * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned. - */ - memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF, - ((char *) &wrqu_point) + IW_EV_POINT_OFF, - IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN); - - /* Extra comes logically after that. Offset +12 bytes. */ - extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN; - - /* Call the handler */ - ret = handler(dev, &info, wrqu, extra); - - /* Calculate real returned length */ - extra_size = (wrqu->data.length * descr->token_size); - /* Re-adjust reply size */ - request->len = extra_size + IW_EV_POINT_PK_LEN; - - /* Put the iwe header where it should, i.e. scrap the - * dummy pointer. */ - memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Check if there is enough buffer up there */ - if(wrqu_point.data.length < wrqu->data.length) - ret = -E2BIG; - } - - /* Return the buffer to the caller */ - if (!ret) { - *p_buf = buffer; - *p_len = request->len; - } else { - /* Cleanup */ - if(buffer) - kfree(buffer); - } - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a standard Wireless Extension SET handler. - * We do various checks and call the handler with the proper args. - */ -static inline int rtnetlink_standard_set(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler) -{ - const struct iw_ioctl_description * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - union iwreq_data wrqu_point; - int hdr_len; - char * extra = NULL; - int extra_size = 0; - struct iw_request_info info; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - if((cmd - SIOCIWFIRST) >= standard_ioctl_num) - return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Extract fixed header from request. This is properly aligned. */ - wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN); - - /* Check if wrqu is complete */ - hdr_len = event_type_pk_size[descr->header_type]; - if(request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have extra data in the request or not */ - if(descr->header_type != IW_HEADER_TYPE_POINT) { - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, NULL); - - } else { - int extra_len; - - /* Put wrqu in the right place (skip pointer) */ - memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, - wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - /* Don't forget about the event code... */ - wrqu = &wrqu_point; - - /* Check if number of token fits within bounds */ - if(wrqu_point.data.length > descr->max_tokens) - return -E2BIG; - if(wrqu_point.data.length < descr->min_tokens) - return -EINVAL; - - /* Real length of payload */ - extra_len = wrqu_point.data.length * descr->token_size; - - /* Check if request is self consistent */ - if((request_len - hdr_len) < extra_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Always allocate for max space. Easier, and won't last - * long... */ - extra_size = descr->max_tokens * descr->token_size; - extra = kmalloc(extra_size, GFP_KERNEL); - if (extra == NULL) - return -ENOMEM; - - /* Copy extra in aligned buffer */ - memcpy(extra, ((char *) request) + hdr_len, extra_len); - - /* Call the handler */ - ret = handler(dev, &info, &wrqu_point, extra); - } - -#ifdef WE_SET_EVENT - /* Generate an event to notify listeners of the change */ - if((descr->flags & IW_DESCR_FLAG_EVENT) && - ((ret == 0) || (ret == -EIWCOMMIT))) { - if(descr->flags & IW_DESCR_FLAG_RESTRICT) - /* If the event is restricted, don't - * export the payload */ - wireless_send_event(dev, cmd, wrqu, NULL); - else - wireless_send_event(dev, cmd, wrqu, extra); - } -#endif /* WE_SET_EVENT */ - - /* Cleanup - I told you it wasn't that long ;-) */ - if(extra) - kfree(extra); - - /* Call commit handler if needed and defined */ - if(ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a private Wireless Extension GET handler. - * Same as above... - * It's not as nice and slimline as the standard wrapper. The cause - * is struct iw_priv_args, which was not really designed for the - * job we are going here. - * - * IMPORTANT : This function prevent to set and get data on the same - * IOCTL and enforce the SET/GET convention. Not doing it would be - * far too hairy... - * If you need to set and get data at the same time, please don't use - * a iw_handler but process it in your ioctl handler (i.e. use the - * old driver API). - */ -static inline int rtnetlink_private_get(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler, - char ** p_buf, - int * p_len) -{ - const struct iw_priv_args * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - int hdr_len; - struct iw_request_info info; - int extra_size = 0; - int i; - char * buffer = NULL; - int buffer_size = 0; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - for(i = 0; i < dev->wireless_handlers->num_private_args; i++) - if(cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &(dev->wireless_handlers->private_args[i]); - break; - } - if(descr == NULL) - return -EOPNOTSUPP; - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n", - dev->name, descr->name, descr->set_args, descr->get_args); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Compute the max size of the get arguments */ - extra_size = get_priv_size(descr->get_args); - - /* Does it fits in wrqu ? */ - if((descr->get_args & IW_PRIV_SIZE_FIXED) && - (extra_size <= IFNAMSIZ)) { - hdr_len = extra_size; - extra_size = 0; - } else { - hdr_len = IW_EV_POINT_PK_LEN; - } - - /* Check if wrqu is complete */ - if(request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have a pointer to user space data or not. */ - if(extra_size == 0) { - - /* Create the kernel buffer that we will return. - * It's at an offset to match the TYPE_POINT case... */ - buffer_size = request_len + IW_EV_POINT_OFF; - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - /* Copy event data */ - memcpy(buffer + IW_EV_POINT_OFF, request, request_len); - /* Use our own copy of wrqu */ - wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF - + IW_EV_LCP_PK_LEN); - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, (char *) wrqu); - - } else { - char * extra; - - /* Buffer for full reply */ - buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF; - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n", - dev->name, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Create the kernel buffer that we will return */ - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - - /* Put wrqu in the right place (just before extra). - * Leave space for IWE header and dummy pointer... - * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned. - */ - memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF, - ((char *) request) + IW_EV_LCP_PK_LEN, - IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN); - - /* Extra comes logically after that. Offset +12 bytes. */ - extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN; - - /* Call the handler */ - ret = handler(dev, &info, wrqu, extra); - - /* Adjust for the actual length if it's variable, - * avoid leaking kernel bits outside. */ - if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) - extra_size = adjust_priv_size(descr->get_args, wrqu); - /* Re-adjust reply size */ - request->len = extra_size + IW_EV_POINT_PK_LEN; - - /* Put the iwe header where it should, i.e. scrap the - * dummy pointer. */ - memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - } - - /* Return the buffer to the caller */ - if (!ret) { - *p_buf = buffer; - *p_len = request->len; - } else { - /* Cleanup */ - if(buffer) - kfree(buffer); - } - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a private Wireless Extension SET handler. - * Same as above... - * It's not as nice and slimline as the standard wrapper. The cause - * is struct iw_priv_args, which was not really designed for the - * job we are going here. - * - * IMPORTANT : This function prevent to set and get data on the same - * IOCTL and enforce the SET/GET convention. Not doing it would be - * far too hairy... - * If you need to set and get data at the same time, please don't use - * a iw_handler but process it in your ioctl handler (i.e. use the - * old driver API). - */ -static inline int rtnetlink_private_set(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler) -{ - const struct iw_priv_args * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - union iwreq_data wrqu_point; - int hdr_len; - char * extra = NULL; - int extra_size = 0; - int offset = 0; /* For sub-ioctls */ - struct iw_request_info info; - int i; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - for(i = 0; i < dev->wireless_handlers->num_private_args; i++) - if(cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &(dev->wireless_handlers->private_args[i]); - break; - } - if(descr == NULL) - return -EOPNOTSUPP; - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n", - ifr->ifr_name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n", - dev->name, descr->name, descr->set_args, descr->get_args); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Compute the size of the set arguments */ - /* Check for sub-ioctl handler */ - if(descr->name[0] == '\0') - /* Reserve one int for sub-ioctl index */ - offset = sizeof(__u32); - - /* Size of set arguments */ - extra_size = get_priv_size(descr->set_args); - - /* Does it fits in wrqu ? */ - if((descr->set_args & IW_PRIV_SIZE_FIXED) && - (extra_size <= IFNAMSIZ)) { - hdr_len = IW_EV_LCP_PK_LEN + extra_size; - extra_size = 0; - } else { - hdr_len = IW_EV_POINT_PK_LEN; - } - - /* Extract fixed header from request. This is properly aligned. */ - wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN); - - /* Check if wrqu is complete */ - if(request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have a pointer to user space data or not. */ - if(extra_size == 0) { - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, (char *) wrqu); - - } else { - int extra_len; - - /* Put wrqu in the right place (skip pointer) */ - memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, - wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - - /* Does it fits within bounds ? */ - if(wrqu_point.data.length > (descr->set_args & - IW_PRIV_SIZE_MASK)) - return -E2BIG; - - /* Real length of payload */ - extra_len = adjust_priv_size(descr->set_args, &wrqu_point); - - /* Check if request is self consistent */ - if((request_len - hdr_len) < extra_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Always allocate for max space. Easier, and won't last - * long... */ - extra = kmalloc(extra_size, GFP_KERNEL); - if (extra == NULL) - return -ENOMEM; - - /* Copy extra in aligned buffer */ - memcpy(extra, ((char *) request) + hdr_len, extra_len); - - /* Call the handler */ - ret = handler(dev, &info, &wrqu_point, extra); - - /* Cleanup - I told you it wasn't that long ;-) */ - kfree(extra); - } - - /* Call commit handler if needed and defined */ - if(ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Main RtNetlink dispatcher. Called from the main networking code - * (do_getlink() in net/core/rtnetlink.c). - * Check the type of Request and call the appropriate wrapper... - */ -int wireless_rtnetlink_get(struct net_device * dev, - char * data, - int len, - char ** p_buf, - int * p_len) -{ - struct iw_event * request = (struct iw_event *) data; - iw_handler handler; - - /* Check length */ - if(len < IW_EV_LCP_PK_LEN) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n", - dev->name, len); - return -EINVAL; - } - - /* ReCheck length (len may have padding) */ - if(request->len > len) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n", - dev->name, request->len, len); - return -EINVAL; - } - - /* Only accept GET requests in here */ - if(!IW_IS_GET(request->cmd)) - return -EOPNOTSUPP; - - /* If command is `get the encoding parameters', check if - * the user has the right to do it */ - if (request->cmd == SIOCGIWENCODE || - request->cmd == SIOCGIWENCODEEXT) { - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - } - - /* Special cases */ - if(request->cmd == SIOCGIWSTATS) - /* Get Wireless Stats */ - return rtnetlink_standard_get(dev, - request, - request->len, - &iw_handler_get_iwstats, - p_buf, p_len); - if(request->cmd == SIOCGIWPRIV) { - /* Check if we have some wireless handlers defined */ - if(dev->wireless_handlers == NULL) - return -EOPNOTSUPP; - /* Get Wireless Stats */ - return rtnetlink_standard_get(dev, - request, - request->len, - &iw_handler_get_private, - p_buf, p_len); - } - - /* Basic check */ - if (!netif_device_present(dev)) - return -ENODEV; - - /* Try to find the handler */ - handler = get_handler(dev, request->cmd); - if(handler != NULL) { - /* Standard and private are not the same */ - if(request->cmd < SIOCIWFIRSTPRIV) - return rtnetlink_standard_get(dev, - request, - request->len, - handler, - p_buf, p_len); - else - return rtnetlink_private_get(dev, - request, - request->len, - handler, - p_buf, p_len); - } - - return -EOPNOTSUPP; -} - -/* ---------------------------------------------------------------- */ -/* - * Main RtNetlink dispatcher. Called from the main networking code - * (do_setlink() in net/core/rtnetlink.c). - * Check the type of Request and call the appropriate wrapper... - */ -int wireless_rtnetlink_set(struct net_device * dev, - char * data, - int len) -{ - struct iw_event * request = (struct iw_event *) data; - iw_handler handler; - - /* Check length */ - if(len < IW_EV_LCP_PK_LEN) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n", - dev->name, len); - return -EINVAL; - } - - /* ReCheck length (len may have padding) */ - if(request->len > len) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n", - dev->name, request->len, len); - return -EINVAL; - } - - /* Only accept SET requests in here */ - if(!IW_IS_SET(request->cmd)) - return -EOPNOTSUPP; - - /* Basic check */ - if (!netif_device_present(dev)) - return -ENODEV; - - /* New driver API : try to find the handler */ - handler = get_handler(dev, request->cmd); - if(handler != NULL) { - /* Standard and private are not the same */ - if(request->cmd < SIOCIWFIRSTPRIV) - return rtnetlink_standard_set(dev, - request, - request->len, - handler); - else - return rtnetlink_private_set(dev, - request, - request->len, - handler); - } - - return -EOPNOTSUPP; -} -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - - -/************************* EVENT PROCESSING *************************/ -/* - * Process events generated by the wireless layer or the driver. - * Most often, the event will be propagated through rtnetlink - */ - -#ifdef WE_EVENT_RTNETLINK -/* ---------------------------------------------------------------- */ -/* - * Locking... - * ---------- - * - * Thanks to Herbert Xu for fixing - * the locking issue in here and implementing this code ! - * - * The issue : wireless_send_event() is often called in interrupt context, - * while the Netlink layer can never be called in interrupt context. - * The fully formed RtNetlink events are queued, and then a tasklet is run - * to feed those to Netlink. - * The skb_queue is interrupt safe, and its lock is not held while calling - * Netlink, so there is no possibility of dealock. - * Jean II - */ - -static struct sk_buff_head wireless_nlevent_queue; - -static int __init wireless_nlevent_init(void) -{ - skb_queue_head_init(&wireless_nlevent_queue); - return 0; -} - -subsys_initcall(wireless_nlevent_init); - -static void wireless_nlevent_process(unsigned long data) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(&wireless_nlevent_queue))) - rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); -} - -static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); - -/* ---------------------------------------------------------------- */ -/* - * Fill a rtnetlink message with our event data. - * Note that we propage only the specified event and don't dump the - * current wireless config. Dumping the wireless config is far too - * expensive (for each parameter, the driver need to query the hardware). - */ -static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb, - struct net_device * dev, - int type, - char * event, - int event_len) -{ - struct ifinfomsg *r; - struct nlmsghdr *nlh; - unsigned char *b = skb->tail; - - nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r)); - r = NLMSG_DATA(nlh); - r->ifi_family = AF_UNSPEC; - r->__ifi_pad = 0; - r->ifi_type = dev->type; - r->ifi_index = dev->ifindex; - r->ifi_flags = dev_get_flags(dev); - r->ifi_change = 0; /* Wireless changes don't affect those flags */ - - /* Add the wireless events in the netlink packet */ - RTA_PUT(skb, IFLA_WIRELESS, event_len, event); - - nlh->nlmsg_len = skb->tail - b; - return skb->len; - -nlmsg_failure: -rtattr_failure: - skb_trim(skb, b - skb->data); - return -1; -} - -/* ---------------------------------------------------------------- */ -/* - * Create and broadcast and send it on the standard rtnetlink socket - * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c - * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field - * within a RTM_NEWLINK event. - */ -static inline void rtmsg_iwinfo(struct net_device * dev, - char * event, - int event_len) -{ - struct sk_buff *skb; - int size = NLMSG_GOODSIZE; - - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) - return; - - if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, - event, event_len) < 0) { - kfree_skb(skb); - return; - } - NETLINK_CB(skb).dst_group = RTNLGRP_LINK; - skb_queue_tail(&wireless_nlevent_queue, skb); - tasklet_schedule(&wireless_nlevent_tasklet); -} - -#endif /* WE_EVENT_RTNETLINK */ - -/* ---------------------------------------------------------------- */ -/* - * Main event dispatcher. Called from other parts and drivers. - * Send the event on the appropriate channels. - * May be called from interrupt context. - */ -void wireless_send_event(struct net_device * dev, - unsigned int cmd, - union iwreq_data * wrqu, - char * extra) -{ - const struct iw_ioctl_description * descr = NULL; - int extra_len = 0; - struct iw_event *event; /* Mallocated whole event */ - int event_len; /* Its size */ - int hdr_len; /* Size of the event header */ - int wrqu_off = 0; /* Offset in wrqu */ - /* Don't "optimise" the following variable, it will crash */ - unsigned cmd_index; /* *MUST* be unsigned */ - - /* Get the description of the Event */ - if(cmd <= SIOCIWLAST) { - cmd_index = cmd - SIOCIWFIRST; - if(cmd_index < standard_ioctl_num) - descr = &(standard_ioctl[cmd_index]); - } else { - cmd_index = cmd - IWEVFIRST; - if(cmd_index < standard_event_num) - descr = &(standard_event[cmd_index]); - } - /* Don't accept unknown events */ - if(descr == NULL) { - /* Note : we don't return an error to the driver, because - * the driver would not know what to do about it. It can't - * return an error to the user, because the event is not - * initiated by a user request. - * The best the driver could do is to log an error message. - * We will do it ourselves instead... - */ - printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", - dev->name, cmd); - return; - } -#ifdef WE_EVENT_DEBUG - printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -#endif /* WE_EVENT_DEBUG */ - - /* Check extra parameters and set extra_len */ - if(descr->header_type == IW_HEADER_TYPE_POINT) { - /* Check if number of token fits within bounds */ - if(wrqu->data.length > descr->max_tokens) { - printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); - return; - } - if(wrqu->data.length < descr->min_tokens) { - printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); - return; - } - /* Calculate extra_len - extra is NULL for restricted events */ - if(extra != NULL) - extra_len = wrqu->data.length * descr->token_size; - /* Always at an offset in wrqu */ - wrqu_off = IW_EV_POINT_OFF; -#ifdef WE_EVENT_DEBUG - printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len); -#endif /* WE_EVENT_DEBUG */ - } - - /* Total length of the event */ - hdr_len = event_type_size[descr->header_type]; - event_len = hdr_len + extra_len; - -#ifdef WE_EVENT_DEBUG - printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len); -#endif /* WE_EVENT_DEBUG */ - - /* Create temporary buffer to hold the event */ - event = kmalloc(event_len, GFP_ATOMIC); - if(event == NULL) - return; - - /* Fill event */ - event->len = event_len; - event->cmd = cmd; - memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); - if(extra != NULL) - memcpy(((char *) event) + hdr_len, extra, extra_len); - -#ifdef WE_EVENT_RTNETLINK - /* Send via the RtNetlink event channel */ - rtmsg_iwinfo(dev, (char *) event, event_len); -#endif /* WE_EVENT_RTNETLINK */ - - /* Cleanup */ - kfree(event); - - return; /* Always success, I guess ;-) */ -} - -/********************** ENHANCED IWSPY SUPPORT **********************/ -/* - * In the old days, the driver was handling spy support all by itself. - * Now, the driver can delegate this task to Wireless Extensions. - * It needs to use those standard spy iw_handler in struct iw_handler_def, - * push data to us via wireless_spy_update() and include struct iw_spy_data - * in its private part (and export it in net_device->wireless_data->spy_data). - * One of the main advantage of centralising spy support here is that - * it becomes much easier to improve and extend it without having to touch - * the drivers. One example is the addition of the Spy-Threshold events. - */ - -/* ---------------------------------------------------------------- */ -/* - * Return the pointer to the spy data in the driver. - * Because this is called on the Rx path via wireless_spy_update(), - * we want it to be efficient... - */ -static inline struct iw_spy_data * get_spydata(struct net_device *dev) -{ - /* This is the new way */ - if(dev->wireless_data) - return(dev->wireless_data->spy_data); - return NULL; -} - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : set Spy List - */ -int iw_handler_set_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - - /* Make sure driver is not buggy or using the old API */ - if(!spydata) - return -EOPNOTSUPP; - - /* Disable spy collection while we copy the addresses. - * While we copy addresses, any call to wireless_spy_update() - * will NOP. This is OK, as anyway the addresses are changing. */ - spydata->spy_number = 0; - - /* We want to operate without locking, because wireless_spy_update() - * most likely will happen in the interrupt handler, and therefore - * have its own locking constraints and needs performance. - * The rtnl_lock() make sure we don't race with the other iw_handlers. - * This make sure wireless_spy_update() "see" that the spy list - * is temporarily disabled. */ - smp_wmb(); - - /* Are there are addresses to copy? */ - if(wrqu->data.length > 0) { - int i; - - /* Copy addresses */ - for(i = 0; i < wrqu->data.length; i++) - memcpy(spydata->spy_address[i], address[i].sa_data, - ETH_ALEN); - /* Reset stats */ - memset(spydata->spy_stat, 0, - sizeof(struct iw_quality) * IW_MAX_SPY); - -#ifdef WE_SPY_DEBUG - printk(KERN_DEBUG "iw_handler_set_spy() : wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length); - for (i = 0; i < wrqu->data.length; i++) - printk(KERN_DEBUG - "%02X:%02X:%02X:%02X:%02X:%02X \n", - spydata->spy_address[i][0], - spydata->spy_address[i][1], - spydata->spy_address[i][2], - spydata->spy_address[i][3], - spydata->spy_address[i][4], - spydata->spy_address[i][5]); -#endif /* WE_SPY_DEBUG */ - } - - /* Make sure above is updated before re-enabling */ - smp_wmb(); - - /* Enable addresses */ - spydata->spy_number = wrqu->data.length; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : get Spy List - */ -int iw_handler_get_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - int i; - - /* Make sure driver is not buggy or using the old API */ - if(!spydata) - return -EOPNOTSUPP; - - wrqu->data.length = spydata->spy_number; - - /* Copy addresses. */ - for(i = 0; i < spydata->spy_number; i++) { - memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); - address[i].sa_family = AF_UNIX; - } - /* Copy stats to the user buffer (just after). */ - if(spydata->spy_number > 0) - memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), - spydata->spy_stat, - sizeof(struct iw_quality) * spydata->spy_number); - /* Reset updated flags. */ - for(i = 0; i < spydata->spy_number; i++) - spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : set spy threshold - */ -int iw_handler_set_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - - /* Make sure driver is not buggy or using the old API */ - if(!spydata) - return -EOPNOTSUPP; - - /* Just do it */ - memcpy(&(spydata->spy_thr_low), &(threshold->low), - 2 * sizeof(struct iw_quality)); - - /* Clear flag */ - memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); - -#ifdef WE_SPY_DEBUG - printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level); -#endif /* WE_SPY_DEBUG */ - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : get spy threshold - */ -int iw_handler_get_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - - /* Make sure driver is not buggy or using the old API */ - if(!spydata) - return -EOPNOTSUPP; - - /* Just do it */ - memcpy(&(threshold->low), &(spydata->spy_thr_low), - 2 * sizeof(struct iw_quality)); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Prepare and send a Spy Threshold event - */ -static void iw_send_thrspy_event(struct net_device * dev, - struct iw_spy_data * spydata, - unsigned char * address, - struct iw_quality * wstats) -{ - union iwreq_data wrqu; - struct iw_thrspy threshold; - - /* Init */ - wrqu.data.length = 1; - wrqu.data.flags = 0; - /* Copy address */ - memcpy(threshold.addr.sa_data, address, ETH_ALEN); - threshold.addr.sa_family = ARPHRD_ETHER; - /* Copy stats */ - memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); - /* Copy also thresholds */ - memcpy(&(threshold.low), &(spydata->spy_thr_low), - 2 * sizeof(struct iw_quality)); - -#ifdef WE_SPY_DEBUG - printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n", - threshold.addr.sa_data[0], - threshold.addr.sa_data[1], - threshold.addr.sa_data[2], - threshold.addr.sa_data[3], - threshold.addr.sa_data[4], - threshold.addr.sa_data[5], threshold.qual.level); -#endif /* WE_SPY_DEBUG */ - - /* Send event to user space */ - wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); -} - -/* ---------------------------------------------------------------- */ -/* - * Call for the driver to update the spy data. - * For now, the spy data is a simple array. As the size of the array is - * small, this is good enough. If we wanted to support larger number of - * spy addresses, we should use something more efficient... - */ -void wireless_spy_update(struct net_device * dev, - unsigned char * address, - struct iw_quality * wstats) -{ - struct iw_spy_data * spydata = get_spydata(dev); - int i; - int match = -1; - - /* Make sure driver is not buggy or using the old API */ - if(!spydata) - return; - -#ifdef WE_SPY_DEBUG - printk(KERN_DEBUG "wireless_spy_update() : wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]); -#endif /* WE_SPY_DEBUG */ - - /* Update all records that match */ - for(i = 0; i < spydata->spy_number; i++) - if(!compare_ether_addr(address, spydata->spy_address[i])) { - memcpy(&(spydata->spy_stat[i]), wstats, - sizeof(struct iw_quality)); - match = i; - } - - /* Generate an event if we cross the spy threshold. - * To avoid event storms, we have a simple hysteresis : we generate - * event only when we go under the low threshold or above the - * high threshold. */ - if(match >= 0) { - if(spydata->spy_thr_under[match]) { - if(wstats->level > spydata->spy_thr_high.level) { - spydata->spy_thr_under[match] = 0; - iw_send_thrspy_event(dev, spydata, - address, wstats); - } - } else { - if(wstats->level < spydata->spy_thr_low.level) { - spydata->spy_thr_under[match] = 1; - iw_send_thrspy_event(dev, spydata, - address, wstats); - } - } - } -} - -EXPORT_SYMBOL(iw_handler_get_spy); -EXPORT_SYMBOL(iw_handler_get_thrspy); -EXPORT_SYMBOL(iw_handler_set_spy); -EXPORT_SYMBOL(iw_handler_set_thrspy); -EXPORT_SYMBOL(wireless_send_event); -EXPORT_SYMBOL(wireless_spy_update); diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c index a086c63..01030f3 100644 --- a/net/dccp/ackvec.c +++ b/net/dccp/ackvec.c @@ -157,7 +157,7 @@ struct dccp_ackvec *dccp_ackvec_alloc(co if (av != NULL) { av->dccpav_buf_head = DCCP_MAX_ACKVEC_LEN - 1; - av->dccpav_buf_ackno = DCCP_MAX_SEQNO + 1; + av->dccpav_buf_ackno = UINT48_MAX + 1; av->dccpav_buf_nonce = av->dccpav_buf_nonce = 0; av->dccpav_time.tv_sec = 0; av->dccpav_time.tv_usec = 0; diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index 746f79d..d7d9ce7 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -33,7 +33,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - #include "../ccid.h" #include "../dccp.h" #include "lib/packet_history.h" @@ -52,6 +51,9 @@ static struct dccp_tx_hist *ccid3_tx_his static struct dccp_rx_hist *ccid3_rx_hist; static struct dccp_li_hist *ccid3_li_hist; +/* + * Transmitter Half-Connection Routines + */ #ifdef CONFIG_IP_DCCP_CCID3_DEBUG static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state) { @@ -80,23 +82,37 @@ static void ccid3_hc_tx_set_state(struct } /* - * Recalculate scheduled nominal send time t_nom, inter-packet interval - * t_ipi, and delta value. Should be called after each change to X. + * Compute the initial sending rate X_init according to RFC 3390: + * w_init = min(4 * MSS, max(2 * MSS, 4380 bytes)) + * X_init = w_init / RTT + * For consistency with other parts of the code, X_init is scaled by 2^6. */ -static inline void ccid3_update_send_time(struct ccid3_hc_tx_sock *hctx) +static inline u64 rfc3390_initial_rate(struct sock *sk) { - timeval_sub_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi); + const struct dccp_sock *dp = dccp_sk(sk); + const __u32 w_init = min(4 * dp->dccps_mss_cache, + max(2 * dp->dccps_mss_cache, 4380U)); - /* Calculate new t_ipi = s / X_inst (X_inst is in 64 * bytes/second) */ - hctx->ccid3hctx_t_ipi = scaled_div(hctx->ccid3hctx_s, - hctx->ccid3hctx_x >> 6); + return scaled_div(w_init << 6, ccid3_hc_tx_sk(sk)->ccid3hctx_rtt); +} - /* Update nominal send time with regard to the new t_ipi */ - timeval_add_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi); +/* + * Recalculate t_ipi and delta (should be called whenever X changes) + */ +static inline void ccid3_update_send_interval(struct ccid3_hc_tx_sock *hctx) +{ + /* Calculate new t_ipi = s / X_inst (X_inst is in 64 * bytes/second) */ + hctx->ccid3hctx_t_ipi = scaled_div32(((u64)hctx->ccid3hctx_s) << 6, + hctx->ccid3hctx_x); /* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */ hctx->ccid3hctx_delta = min_t(u32, hctx->ccid3hctx_t_ipi / 2, TFRC_OPSYS_HALF_TIME_GRAN); + + ccid3_pr_debug("t_ipi=%u, delta=%u, s=%u, X=%u\n", + hctx->ccid3hctx_t_ipi, hctx->ccid3hctx_delta, + hctx->ccid3hctx_s, (unsigned)(hctx->ccid3hctx_x >> 6)); + } /* * Update X by @@ -112,19 +128,28 @@ static inline void ccid3_update_send_tim * fine-grained resolution of sending rates. This requires scaling by 2^6 * throughout the code. Only X_calc is unscaled (in bytes/second). * - * If X has changed, we also update the scheduled send time t_now, - * the inter-packet interval t_ipi, and the delta value. */ static void ccid3_hc_tx_update_x(struct sock *sk, struct timeval *now) { struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); + __u64 min_rate = 2 * hctx->ccid3hctx_x_recv; const __u64 old_x = hctx->ccid3hctx_x; + /* + * Handle IDLE periods: do not reduce below RFC3390 initial sending rate + * when idling [RFC 4342, 5.1]. See also draft-ietf-dccp-rfc3448bis. + * For consistency with X and X_recv, min_rate is also scaled by 2^6. + */ + if (unlikely(hctx->ccid3hctx_idle)) { + min_rate = rfc3390_initial_rate(sk); + min_rate = max(min_rate, 2 * hctx->ccid3hctx_x_recv); + } + if (hctx->ccid3hctx_p > 0) { hctx->ccid3hctx_x = min(((__u64)hctx->ccid3hctx_x_calc) << 6, - hctx->ccid3hctx_x_recv * 2); + min_rate); hctx->ccid3hctx_x = max(hctx->ccid3hctx_x, (((__u64)hctx->ccid3hctx_s) << 6) / TFRC_T_MBI); @@ -133,14 +158,21 @@ static void ccid3_hc_tx_update_x(struct (suseconds_t)hctx->ccid3hctx_rtt >= 0) { hctx->ccid3hctx_x = - max(2 * min(hctx->ccid3hctx_x, hctx->ccid3hctx_x_recv), + max(min(2 * hctx->ccid3hctx_x, min_rate), scaled_div(((__u64)hctx->ccid3hctx_s) << 6, hctx->ccid3hctx_rtt)); hctx->ccid3hctx_t_ld = *now; } - if (hctx->ccid3hctx_x != old_x) - ccid3_update_send_time(hctx); + if (hctx->ccid3hctx_x != old_x) { + ccid3_pr_debug("X_prev=%u, X_now=%u, X_calc=%u, " + "X_recv=%u\n", (unsigned)(old_x >> 6), + (unsigned)(hctx->ccid3hctx_x >> 6), + hctx->ccid3hctx_x_calc, + (unsigned)(hctx->ccid3hctx_x_recv >> 6)); + + ccid3_update_send_interval(hctx); + } } /* @@ -149,17 +181,12 @@ static void ccid3_hc_tx_update_x(struct */ static inline void ccid3_hc_tx_update_s(struct ccid3_hc_tx_sock *hctx, int len) { - if (unlikely(len == 0)) - ccid3_pr_debug("Packet payload length is 0 - not updating\n"); - else - hctx->ccid3hctx_s = hctx->ccid3hctx_s == 0 ? len : - (9 * hctx->ccid3hctx_s + len) / 10; - /* - * Note: We could do a potential optimisation here - when `s' changes, - * recalculate sending rate and consequently t_ipi, t_delta, and - * t_now. This is however non-standard, and the benefits are not - * clear, so it is currently left out. - */ + const u16 old_s = hctx->ccid3hctx_s; + + hctx->ccid3hctx_s = old_s == 0 ? len : (9 * old_s + len) / 10; + + if (hctx->ccid3hctx_s != old_s) + ccid3_update_send_interval(hctx); } /* @@ -193,6 +220,7 @@ static void ccid3_hc_tx_no_feedback_time { struct sock *sk = (struct sock *)data; struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); + struct timeval now; unsigned long t_nfb = USEC_PER_SEC / 5; bh_lock_sock(sk); @@ -205,6 +233,8 @@ static void ccid3_hc_tx_no_feedback_time ccid3_pr_debug("%s(%p, state=%s) - entry \n", dccp_role(sk), sk, ccid3_tx_state_name(hctx->ccid3hctx_state)); + hctx->ccid3hctx_idle = 1; + switch (hctx->ccid3hctx_state) { case TFRC_SSTATE_NO_FBACK: /* RFC 3448, 4.4: Halve send rate directly */ @@ -219,53 +249,37 @@ static void ccid3_hc_tx_no_feedback_time /* The value of R is still undefined and so we can not recompute * the timout value. Keep initial value as per [RFC 4342, 5]. */ t_nfb = TFRC_INITIAL_TIMEOUT; - ccid3_update_send_time(hctx); + ccid3_update_send_interval(hctx); break; case TFRC_SSTATE_FBACK: /* - * Check if IDLE since last timeout and recv rate is less than - * 4 packets (in units of 64*bytes/sec) per RTT + * Modify the cached value of X_recv [RFC 3448, 4.4] + * + * If (p == 0 || X_calc > 2 * X_recv) + * X_recv = max(X_recv / 2, s / (2 * t_mbi)); + * Else + * X_recv = X_calc / 4; + * + * Note that X_recv is scaled by 2^6 while X_calc is not */ - if (!hctx->ccid3hctx_idle || - (hctx->ccid3hctx_x_recv >= 4 * - scaled_div(((__u64)hctx->ccid3hctx_s) << 6, - hctx->ccid3hctx_rtt))) { - struct timeval now; + BUG_ON(hctx->ccid3hctx_p && !hctx->ccid3hctx_x_calc); - ccid3_pr_debug("%s(%p, state=%s), not idle\n", - dccp_role(sk), sk, - ccid3_tx_state_name(hctx->ccid3hctx_state)); + if (hctx->ccid3hctx_p == 0 || + (hctx->ccid3hctx_x_calc > (hctx->ccid3hctx_x_recv >> 5))) { - /* - * Modify the cached value of X_recv [RFC 3448, 4.4] - * - * If (p == 0 || X_calc > 2 * X_recv) - * X_recv = max(X_recv / 2, s / (2 * t_mbi)); - * Else - * X_recv = X_calc / 4; - * - * Note that X_recv is scaled by 2^6 while X_calc is not - */ - BUG_ON(hctx->ccid3hctx_p && !hctx->ccid3hctx_x_calc); - - if (hctx->ccid3hctx_p == 0 || - (hctx->ccid3hctx_x_calc > - (hctx->ccid3hctx_x_recv >> 5))) { - - hctx->ccid3hctx_x_recv = - max(hctx->ccid3hctx_x_recv / 2, - (((__u64)hctx->ccid3hctx_s) << 6) / - (2 * TFRC_T_MBI)); - - if (hctx->ccid3hctx_p == 0) - dccp_timestamp(sk, &now); - } else { - hctx->ccid3hctx_x_recv = hctx->ccid3hctx_x_calc; - hctx->ccid3hctx_x_recv <<= 4; - } - /* Now recalculate X [RFC 3448, 4.3, step (4)] */ - ccid3_hc_tx_update_x(sk, &now); + hctx->ccid3hctx_x_recv = + max(hctx->ccid3hctx_x_recv / 2, + (((__u64)hctx->ccid3hctx_s) << 6) / + (2 * TFRC_T_MBI)); + + if (hctx->ccid3hctx_p == 0) + dccp_timestamp(sk, &now); + } else { + hctx->ccid3hctx_x_recv = hctx->ccid3hctx_x_calc; + hctx->ccid3hctx_x_recv <<= 4; } + /* Now recalculate X [RFC 3448, 4.3, step (4)] */ + ccid3_hc_tx_update_x(sk, &now); /* * Schedule no feedback timer to expire in * max(t_RTO, 2 * s/X) = max(t_RTO, 2 * t_ipi) @@ -280,8 +294,6 @@ static void ccid3_hc_tx_no_feedback_time goto out; } - hctx->ccid3hctx_idle = 1; - restart_timer: sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer, jiffies + usecs_to_jiffies(t_nfb)); @@ -322,24 +334,35 @@ static int ccid3_hc_tx_send_packet(struc usecs_to_jiffies(TFRC_INITIAL_TIMEOUT))); hctx->ccid3hctx_last_win_count = 0; hctx->ccid3hctx_t_last_win_count = now; - ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK); - - /* Set initial sending rate X/s to 1pps (X is scaled by 2^6) */ - ccid3_hc_tx_update_s(hctx, skb->len); - hctx->ccid3hctx_x = hctx->ccid3hctx_s; - hctx->ccid3hctx_x <<= 6; - - /* First timeout, according to [RFC 3448, 4.2], is 1 second */ - hctx->ccid3hctx_t_ipi = USEC_PER_SEC; - /* Initial delta: minimum of 0.5 sec and t_gran/2 */ - hctx->ccid3hctx_delta = TFRC_OPSYS_HALF_TIME_GRAN; /* Set t_0 for initial packet */ hctx->ccid3hctx_t_nom = now; + + hctx->ccid3hctx_s = skb->len; + + /* + * Use initial RTT sample when available: recommended by erratum + * to RFC 4342. This implements the initialisation procedure of + * draft rfc3448bis, section 4.2. Remember, X is scaled by 2^6. + */ + if (dp->dccps_syn_rtt) { + ccid3_pr_debug("SYN RTT = %uus\n", dp->dccps_syn_rtt); + hctx->ccid3hctx_rtt = dp->dccps_syn_rtt; + hctx->ccid3hctx_x = rfc3390_initial_rate(sk); + hctx->ccid3hctx_t_ld = now; + } else { + /* Sender does not have RTT sample: X = MSS/second */ + hctx->ccid3hctx_x = dp->dccps_mss_cache; + hctx->ccid3hctx_x <<= 6; + } + ccid3_update_send_interval(hctx); + + ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK); break; case TFRC_SSTATE_NO_FBACK: case TFRC_SSTATE_FBACK: delay = timeval_delta(&hctx->ccid3hctx_t_nom, &now); + ccid3_pr_debug("delay=%ld\n", (long)delay); /* * Scheduling of packet transmissions [RFC 3448, 4.6] * @@ -361,6 +384,7 @@ static int ccid3_hc_tx_send_packet(struc /* prepare to send now (add options etc.) */ dp->dccps_hc_tx_insert_options = 1; DCCP_SKB_CB(skb)->dccpd_ccval = hctx->ccid3hctx_last_win_count; + hctx->ccid3hctx_idle = 0; /* set the nominal send time for the next following packet */ timeval_add_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi); @@ -391,7 +415,6 @@ static void ccid3_hc_tx_packet_sent(stru packet->dccphtx_seqno = dccp_sk(sk)->dccps_gss; packet->dccphtx_rtt = hctx->ccid3hctx_rtt; packet->dccphtx_sent = 1; - hctx->ccid3hctx_idle = 0; } static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) @@ -402,8 +425,7 @@ static void ccid3_hc_tx_packet_recv(stru struct dccp_tx_hist_entry *packet; struct timeval now; unsigned long t_nfb; - u32 pinv; - suseconds_t r_sample, t_elapsed; + u32 pinv, r_sample; BUG_ON(hctx == NULL); @@ -445,18 +467,10 @@ static void ccid3_hc_tx_packet_recv(stru * Calculate new round trip sample as per [RFC 3448, 4.3] by * R_sample = (now - t_recvdata) - t_elapsed */ - r_sample = timeval_delta(&now, &packet->dccphtx_tstamp); - t_elapsed = dp->dccps_options_received.dccpor_elapsed_time * 10; - - DCCP_BUG_ON(r_sample < 0); - if (unlikely(r_sample <= t_elapsed)) - DCCP_WARN("WARNING: r_sample=%dus <= t_elapsed=%dus\n", - (int)r_sample, (int)t_elapsed); - else - r_sample -= t_elapsed; - CCID3_RTT_SANITY_CHECK(r_sample); + r_sample = dccp_sample_rtt(sk, &now, &packet->dccphtx_tstamp); - /* Update RTT estimate by + /* + * Update RTT estimate by * If (No feedback recv) * R = R_sample; * Else @@ -467,27 +481,23 @@ static void ccid3_hc_tx_packet_recv(stru if (hctx->ccid3hctx_state == TFRC_SSTATE_NO_FBACK) { /* * Larger Initial Windows [RFC 4342, sec. 5] - * We deviate in that we use `s' instead of `MSS'. */ - __u64 w_init = min(4 * hctx->ccid3hctx_s, - max(2 * hctx->ccid3hctx_s, 4380)); hctx->ccid3hctx_rtt = r_sample; - hctx->ccid3hctx_x = scaled_div(w_init << 6, r_sample); + hctx->ccid3hctx_x = rfc3390_initial_rate(sk); hctx->ccid3hctx_t_ld = now; - ccid3_update_send_time(hctx); + ccid3_update_send_interval(hctx); - ccid3_pr_debug("%s(%p), s=%u, w_init=%llu, " - "R_sample=%dus, X=%u\n", dccp_role(sk), + ccid3_pr_debug("%s(%p), s=%u, MSS=%u, " + "R_sample=%uus, X=%u\n", dccp_role(sk), sk, hctx->ccid3hctx_s, - (unsigned long long)w_init, - (int)r_sample, + dp->dccps_mss_cache, r_sample, (unsigned)(hctx->ccid3hctx_x >> 6)); ccid3_hc_tx_set_state(sk, TFRC_SSTATE_FBACK); } else { hctx->ccid3hctx_rtt = (9 * hctx->ccid3hctx_rtt + - (u32)r_sample) / 10; + r_sample) / 10; /* Update sending rate (step 4 of [RFC 3448, 4.3]) */ if (hctx->ccid3hctx_p > 0) @@ -497,10 +507,10 @@ static void ccid3_hc_tx_packet_recv(stru hctx->ccid3hctx_p); ccid3_hc_tx_update_x(sk, &now); - ccid3_pr_debug("%s(%p), RTT=%uus (sample=%dus), s=%u, " + ccid3_pr_debug("%s(%p), RTT=%uus (sample=%uus), s=%u, " "p=%u, X_calc=%u, X_recv=%u, X=%u\n", dccp_role(sk), - sk, hctx->ccid3hctx_rtt, (int)r_sample, + sk, hctx->ccid3hctx_rtt, r_sample, hctx->ccid3hctx_s, hctx->ccid3hctx_p, hctx->ccid3hctx_x_calc, (unsigned)(hctx->ccid3hctx_x_recv >> 6), @@ -644,10 +654,50 @@ static void ccid3_hc_tx_exit(struct sock dccp_tx_hist_purge(ccid3_tx_hist, &hctx->ccid3hctx_hist); } +static void ccid3_hc_tx_get_info(struct sock *sk, struct tcp_info *info) +{ + const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); + + /* Listen socks doesn't have a private CCID block */ + if (sk->sk_state == DCCP_LISTEN) + return; + + BUG_ON(hctx == NULL); + + info->tcpi_rto = hctx->ccid3hctx_t_rto; + info->tcpi_rtt = hctx->ccid3hctx_rtt; +} + +static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len, + u32 __user *optval, int __user *optlen) +{ + const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); + const void *val; + + /* Listen socks doesn't have a private CCID block */ + if (sk->sk_state == DCCP_LISTEN) + return -EINVAL; + + switch (optname) { + case DCCP_SOCKOPT_CCID_TX_INFO: + if (len < sizeof(hctx->ccid3hctx_tfrc)) + return -EINVAL; + len = sizeof(hctx->ccid3hctx_tfrc); + val = &hctx->ccid3hctx_tfrc; + break; + default: + return -ENOPROTOOPT; + } + + if (put_user(len, optlen) || copy_to_user(optval, val, len)) + return -EFAULT; + + return 0; +} + /* - * RX Half Connection methods + * Receiver Half-Connection Routines */ - #ifdef CONFIG_IP_DCCP_CCID3_DEBUG static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state) { @@ -977,8 +1027,7 @@ static void ccid3_hc_rx_packet_recv(stru const struct dccp_options_received *opt_recv; struct dccp_rx_hist_entry *packet; struct timeval now; - u32 p_prev, rtt_prev; - suseconds_t r_sample, t_elapsed; + u32 p_prev, r_sample, rtt_prev; int loss, payload_size; BUG_ON(hcrx == NULL); @@ -994,17 +1043,7 @@ static void ccid3_hc_rx_packet_recv(stru break; rtt_prev = hcrx->ccid3hcrx_rtt; dccp_timestamp(sk, &now); - timeval_sub_usecs(&now, opt_recv->dccpor_timestamp_echo * 10); - r_sample = timeval_usecs(&now); - t_elapsed = opt_recv->dccpor_elapsed_time * 10; - - DCCP_BUG_ON(r_sample < 0); - if (unlikely(r_sample <= t_elapsed)) - DCCP_WARN("r_sample=%ldus, t_elapsed=%ldus\n", - (long)r_sample, (long)t_elapsed); - else - r_sample -= t_elapsed; - CCID3_RTT_SANITY_CHECK(r_sample); + r_sample = dccp_sample_rtt(sk, &now, NULL); if (hcrx->ccid3hcrx_state == TFRC_RSTATE_NO_DATA) hcrx->ccid3hcrx_rtt = r_sample; @@ -1132,20 +1171,6 @@ static void ccid3_hc_rx_get_info(struct info->tcpi_rcv_rtt = hcrx->ccid3hcrx_rtt; } -static void ccid3_hc_tx_get_info(struct sock *sk, struct tcp_info *info) -{ - const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); - - /* Listen socks doesn't have a private CCID block */ - if (sk->sk_state == DCCP_LISTEN) - return; - - BUG_ON(hctx == NULL); - - info->tcpi_rto = hctx->ccid3hctx_t_rto; - info->tcpi_rtt = hctx->ccid3hctx_rtt; -} - static int ccid3_hc_rx_getsockopt(struct sock *sk, const int optname, int len, u32 __user *optval, int __user *optlen) { @@ -1173,33 +1198,6 @@ static int ccid3_hc_rx_getsockopt(struct return 0; } -static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len, - u32 __user *optval, int __user *optlen) -{ - const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); - const void *val; - - /* Listen socks doesn't have a private CCID block */ - if (sk->sk_state == DCCP_LISTEN) - return -EINVAL; - - switch (optname) { - case DCCP_SOCKOPT_CCID_TX_INFO: - if (len < sizeof(hctx->ccid3hctx_tfrc)) - return -EINVAL; - len = sizeof(hctx->ccid3hctx_tfrc); - val = &hctx->ccid3hctx_tfrc; - break; - default: - return -ENOPROTOOPT; - } - - if (put_user(len, optlen) || copy_to_user(optval, val, len)) - return -EFAULT; - - return 0; -} - static struct ccid_operations ccid3 = { .ccid_id = DCCPC_CCID3, .ccid_name = "ccid3", diff --git a/net/dccp/ccids/ccid3.h b/net/dccp/ccids/ccid3.h index 15776a8..8d31b38 100644 --- a/net/dccp/ccids/ccid3.h +++ b/net/dccp/ccids/ccid3.h @@ -51,16 +51,6 @@ #define TFRC_OPSYS_HALF_TIME_GRAN (USEC /* Parameter t_mbi from [RFC 3448, 4.3]: backoff interval in seconds */ #define TFRC_T_MBI 64 -/* What we think is a reasonable upper limit on RTT values */ -#define CCID3_SANE_RTT_MAX ((suseconds_t)(4 * USEC_PER_SEC)) - -#define CCID3_RTT_SANITY_CHECK(rtt) do { \ - if (rtt > CCID3_SANE_RTT_MAX) { \ - DCCP_CRIT("RTT (%d) too large, substituting %d", \ - (int)rtt, (int)CCID3_SANE_RTT_MAX); \ - rtt = CCID3_SANE_RTT_MAX; \ - } } while (0) - enum ccid3_options { TFRC_OPT_LOSS_EVENT_RATE = 192, TFRC_OPT_LOSS_INTERVALS = 193, diff --git a/net/dccp/ccids/lib/loss_interval.c b/net/dccp/ccids/lib/loss_interval.c index 0a0baef..372d7e7 100644 --- a/net/dccp/ccids/lib/loss_interval.c +++ b/net/dccp/ccids/lib/loss_interval.c @@ -91,7 +91,7 @@ u32 dccp_li_hist_calc_i_mean(struct list u32 w_tot = 0; list_for_each_entry_safe(li_entry, li_next, list, dccplih_node) { - if (li_entry->dccplih_interval != ~0) { + if (li_entry->dccplih_interval != ~0U) { i_tot0 += li_entry->dccplih_interval * dccp_li_hist_w[i]; w_tot += dccp_li_hist_w[i]; if (i != 0) diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index e33a9ed..d8ad27b 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -31,13 +31,9 @@ #define DCCP_BUG_ON(cond) do { if (un __stringify(cond)); \ } while (0) -#ifdef MODULE #define DCCP_PRINTK(enable, fmt, args...) do { if (enable) \ printk(fmt, ##args); \ } while(0) -#else -#define DCCP_PRINTK(enable, fmt, args...) printk(fmt, ##args) -#endif #define DCCP_PR_DEBUG(enable, fmt, a...) DCCP_PRINTK(enable, KERN_DEBUG \ "%s: " fmt, __FUNCTION__, ##a) @@ -75,11 +71,15 @@ #define DCCP_TIMEWAIT_LEN (60 * HZ) /* h /* RFC 1122, 4.2.3.1 initial RTO value */ #define DCCP_TIMEOUT_INIT ((unsigned)(3 * HZ)) +#define DCCP_RTO_MAX ((unsigned)(120 * HZ)) /* FIXME: using TCP value */ + +/* bounds for sampled RTT values from packet exchanges (in usec) */ +#define DCCP_SANE_RTT_MIN 100 +#define DCCP_SANE_RTT_MAX (4 * USEC_PER_SEC) + /* Maximal interval between probes for local resources. */ #define DCCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ / 2U)) -#define DCCP_RTO_MAX ((unsigned)(120 * HZ)) /* FIXME: using TCP value */ - /* sysctl variables for DCCP */ extern int sysctl_dccp_request_retries; extern int sysctl_dccp_retries1; @@ -92,17 +92,43 @@ extern int sysctl_dccp_feat_send_ack_ve extern int sysctl_dccp_feat_send_ndp_count; extern int sysctl_dccp_tx_qlen; +/* + * 48-bit sequence number arithmetic (signed and unsigned) + */ +#define INT48_MIN 0x800000000000LL /* 2^47 */ +#define UINT48_MAX 0xFFFFFFFFFFFFLL /* 2^48 - 1 */ +#define COMPLEMENT48(x) (0x1000000000000LL - (x)) /* 2^48 - x */ +#define TO_SIGNED48(x) (((x) < INT48_MIN)? (x) : -COMPLEMENT48( (x))) +#define TO_UNSIGNED48(x) (((x) >= 0)? (x) : COMPLEMENT48(-(x))) +#define ADD48(a, b) (((a) + (b)) & UINT48_MAX) +#define SUB48(a, b) ADD48((a), COMPLEMENT48(b)) + +static inline void dccp_set_seqno(u64 *seqno, u64 value) +{ + *seqno = value & UINT48_MAX; +} + +static inline void dccp_inc_seqno(u64 *seqno) +{ + *seqno = ADD48(*seqno, 1); +} + +/* signed mod-2^48 distance: pos. if seqno1 < seqno2, neg. if seqno1 > seqno2 */ +static inline s64 dccp_delta_seqno(const u64 seqno1, const u64 seqno2) +{ + u64 delta = SUB48(seqno2, seqno1); + + return TO_SIGNED48(delta); +} + /* is seq1 < seq2 ? */ static inline int before48(const u64 seq1, const u64 seq2) { - return (s64)((seq1 << 16) - (seq2 << 16)) < 0; + return (s64)((seq2 << 16) - (seq1 << 16)) > 0; } /* is seq1 > seq2 ? */ -static inline int after48(const u64 seq1, const u64 seq2) -{ - return (s64)((seq2 << 16) - (seq1 << 16)) < 0; -} +#define after48(seq1, seq2) before48(seq2, seq1) /* is seq2 <= seq1 <= seq3 ? */ static inline int between48(const u64 seq1, const u64 seq2, const u64 seq3) @@ -118,9 +144,7 @@ static inline u64 max48(const u64 seq1, /* is seq1 next seqno after seq2 */ static inline int follows48(const u64 seq1, const u64 seq2) { - int diff = (seq1 & 0xFFFF) - (seq2 & 0xFFFF); - - return diff==1; + return dccp_delta_seqno(seq2, seq1) == 1; } enum { @@ -272,6 +296,8 @@ extern int dccp_v4_connect(struct soc extern int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code); extern void dccp_send_close(struct sock *sk, const int active); extern int dccp_invalid_packet(struct sk_buff *skb); +extern u32 dccp_sample_rtt(struct sock *sk, struct timeval *t_recv, + struct timeval *t_history); static inline int dccp_bad_service_code(const struct sock *sk, const __be32 service) @@ -313,26 +339,7 @@ static inline int dccp_packet_without_ac return type == DCCP_PKT_DATA || type == DCCP_PKT_REQUEST; } -#define DCCP_MAX_SEQNO ((((u64)1) << 48) - 1) -#define DCCP_PKT_WITHOUT_ACK_SEQ (DCCP_MAX_SEQNO << 2) - -static inline void dccp_set_seqno(u64 *seqno, u64 value) -{ - if (value > DCCP_MAX_SEQNO) - value -= DCCP_MAX_SEQNO + 1; - *seqno = value; -} - -static inline u64 dccp_delta_seqno(u64 seqno1, u64 seqno2) -{ - return ((seqno2 << 16) - (seqno1 << 16)) >> 16; -} - -static inline void dccp_inc_seqno(u64 *seqno) -{ - if (++*seqno > DCCP_MAX_SEQNO) - *seqno = 0; -} +#define DCCP_PKT_WITHOUT_ACK_SEQ (UINT48_MAX << 2) static inline void dccp_hdr_set_seq(struct dccp_hdr *dh, const u64 gss) { diff --git a/net/dccp/input.c b/net/dccp/input.c index 78b043c..da6ec18 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -86,7 +86,8 @@ static int dccp_check_seqno(struct sock dh->dccph_type == DCCP_PKT_SYNCACK) { if (between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, dp->dccps_awl, dp->dccps_awh) && - !before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_swl)) + dccp_delta_seqno(dp->dccps_swl, + DCCP_SKB_CB(skb)->dccpd_seq) >= 0) dccp_update_gsr(sk, DCCP_SKB_CB(skb)->dccpd_seq); else return -1; @@ -203,7 +204,8 @@ static int __dccp_rcv_established(struct if (dp->dccps_role != DCCP_ROLE_CLIENT) goto send_sync; check_seq: - if (!before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_osr)) { + if (dccp_delta_seqno(dp->dccps_osr, + DCCP_SKB_CB(skb)->dccpd_seq) >= 0) { send_sync: dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq, DCCP_PKT_SYNC); @@ -298,6 +300,14 @@ static int dccp_rcv_request_sent_state_p if (dccp_parse_options(sk, skb)) goto out_invalid_packet; + /* Obtain RTT sample from SYN exchange (used by CCID 3) */ + if (dp->dccps_options_received.dccpor_timestamp_echo) { + struct timeval now; + + dccp_timestamp(sk, &now); + dp->dccps_syn_rtt = dccp_sample_rtt(sk, &now, NULL); + } + if (dccp_msk(sk)->dccpms_send_ack_vector && dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, DCCP_SKB_CB(skb)->dccpd_seq, @@ -575,3 +585,43 @@ discard: } EXPORT_SYMBOL_GPL(dccp_rcv_state_process); + +/** + * dccp_sample_rtt - Sample RTT from packet exchange + * + * @sk: connected dccp_sock + * @t_recv: receive timestamp of packet with timestamp echo + * @t_hist: packet history timestamp or NULL + */ +u32 dccp_sample_rtt(struct sock *sk, struct timeval *t_recv, + struct timeval *t_hist) +{ + struct dccp_sock *dp = dccp_sk(sk); + struct dccp_options_received *or = &dp->dccps_options_received; + suseconds_t delta; + + if (t_hist == NULL) { + if (!or->dccpor_timestamp_echo) { + DCCP_WARN("packet without timestamp echo\n"); + return DCCP_SANE_RTT_MAX; + } + timeval_sub_usecs(t_recv, or->dccpor_timestamp_echo * 10); + delta = timeval_usecs(t_recv); + } else + delta = timeval_delta(t_recv, t_hist); + + delta -= or->dccpor_elapsed_time * 10; /* either set or 0 */ + + if (unlikely(delta <= 0)) { + DCCP_WARN("unusable RTT sample %ld, using min\n", (long)delta); + return DCCP_SANE_RTT_MIN; + } + if (unlikely(delta - (suseconds_t)DCCP_SANE_RTT_MAX > 0)) { + DCCP_WARN("RTT sample %ld too large, using max\n", (long)delta); + return DCCP_SANE_RTT_MAX; + } + + return delta; +} + +EXPORT_SYMBOL_GPL(dccp_sample_rtt); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 4a83978..718f2fa 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -207,8 +207,8 @@ static void dccp_v4_err(struct sk_buff * (iph->ihl << 2)); struct dccp_sock *dp; struct inet_sock *inet; - const int type = skb->h.icmph->type; - const int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; struct sock *sk; __u64 seq; int err; @@ -363,8 +363,8 @@ EXPORT_SYMBOL_GPL(dccp_v4_send_check); static inline u64 dccp_v4_init_sequence(const struct sk_buff *skb) { - return secure_dccp_sequence_number(skb->nh.iph->daddr, - skb->nh.iph->saddr, + return secure_dccp_sequence_number(ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, dccp_hdr(skb)->dccph_dport, dccp_hdr(skb)->dccph_sport); } @@ -405,7 +405,7 @@ struct sock *dccp_v4_request_recv_sock(s newinet->opt = ireq->opt; ireq->opt = NULL; newinet->mc_index = inet_iif(skb); - newinet->mc_ttl = skb->nh.iph->ttl; + newinet->mc_ttl = ip_hdr(skb)->ttl; newinet->id = jiffies; dccp_sync_mss(newsk, dst_mtu(dst)); @@ -428,7 +428,7 @@ EXPORT_SYMBOL_GPL(dccp_v4_request_recv_s static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) { const struct dccp_hdr *dh = dccp_hdr(skb); - const struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); struct sock *nsk; struct request_sock **prev; /* Find possible connection requests. */ @@ -460,8 +460,8 @@ static struct dst_entry* dccp_v4_route_s struct rtable *rt; struct flowi fl = { .oif = ((struct rtable *)skb->dst)->rt_iif, .nl_u = { .ip4_u = - { .daddr = skb->nh.iph->saddr, - .saddr = skb->nh.iph->daddr, + { .daddr = ip_hdr(skb)->saddr, + .saddr = ip_hdr(skb)->daddr, .tos = RT_CONN_FLAGS(sk) } }, .proto = sk->sk_protocol, .uli_u = { .ports = @@ -513,6 +513,7 @@ static void dccp_v4_ctl_send_reset(struc { int err; struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh; + const struct iphdr *rxiph; const int dccp_hdr_reset_len = sizeof(struct dccp_hdr) + sizeof(struct dccp_hdr_ext) + sizeof(struct dccp_hdr_reset); @@ -559,13 +560,13 @@ static void dccp_v4_ctl_send_reset(struc dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), DCCP_SKB_CB(rxskb)->dccpd_seq); dccp_csum_outgoing(skb); - dh->dccph_checksum = dccp_v4_csum_finish(skb, rxskb->nh.iph->saddr, - rxskb->nh.iph->daddr); + rxiph = ip_hdr(rxskb); + dh->dccph_checksum = dccp_v4_csum_finish(skb, rxiph->saddr, + rxiph->daddr); bh_lock_sock(dccp_v4_ctl_socket->sk); err = ip_build_and_send_pkt(skb, dccp_v4_ctl_socket->sk, - rxskb->nh.iph->daddr, - rxskb->nh.iph->saddr, NULL); + rxiph->daddr, rxiph->saddr, NULL); bh_unlock_sock(dccp_v4_ctl_socket->sk); if (net_xmit_eval(err) == 0) { @@ -640,8 +641,8 @@ int dccp_v4_conn_request(struct sock *sk goto drop_and_free; ireq = inet_rsk(req); - ireq->loc_addr = skb->nh.iph->daddr; - ireq->rmt_addr = skb->nh.iph->saddr; + ireq->loc_addr = ip_hdr(skb)->daddr; + ireq->rmt_addr = ip_hdr(skb)->saddr; ireq->opt = NULL; /* @@ -809,6 +810,7 @@ EXPORT_SYMBOL_GPL(dccp_invalid_packet); static int dccp_v4_rcv(struct sk_buff *skb) { const struct dccp_hdr *dh; + const struct iphdr *iph; struct sock *sk; int min_cov; @@ -817,8 +819,9 @@ static int dccp_v4_rcv(struct sk_buff *s if (dccp_invalid_packet(skb)) goto discard_it; + iph = ip_hdr(skb); /* Step 1: If header checksum is incorrect, drop packet and return */ - if (dccp_v4_csum_finish(skb, skb->nh.iph->saddr, skb->nh.iph->daddr)) { + if (dccp_v4_csum_finish(skb, iph->saddr, iph->daddr)) { DCCP_WARN("dropped packet with invalid checksum\n"); goto discard_it; } @@ -832,8 +835,8 @@ static int dccp_v4_rcv(struct sk_buff *s "src=%u.%u.%u.%u@%-5d " "dst=%u.%u.%u.%u@%-5d seq=%llu", dccp_packet_name(dh->dccph_type), - NIPQUAD(skb->nh.iph->saddr), ntohs(dh->dccph_sport), - NIPQUAD(skb->nh.iph->daddr), ntohs(dh->dccph_dport), + NIPQUAD(iph->saddr), ntohs(dh->dccph_sport), + NIPQUAD(iph->daddr), ntohs(dh->dccph_dport), (unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq); if (dccp_packet_without_ack(skb)) { @@ -848,10 +851,8 @@ static int dccp_v4_rcv(struct sk_buff *s /* Step 2: * Look up flow ID in table and get corresponding socket */ sk = __inet_lookup(&dccp_hashinfo, - skb->nh.iph->saddr, dh->dccph_sport, - skb->nh.iph->daddr, dh->dccph_dport, - inet_iif(skb)); - + iph->saddr, dh->dccph_sport, + iph->daddr, dh->dccph_dport, inet_iif(skb)); /* * Step 2: * If no socket ... diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 7f51e8d..64eac25 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -84,8 +84,8 @@ static inline __u32 secure_dccpv6_sequen static inline __u32 dccp_v6_init_sequence(struct sk_buff *skb) { - return secure_dccpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32, - skb->nh.ipv6h->saddr.s6_addr32, + return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, + ipv6_hdr(skb)->saddr.s6_addr32, dccp_hdr(skb)->dccph_dport, dccp_hdr(skb)->dccph_sport ); @@ -261,8 +261,8 @@ static int dccp_v6_send_response(struct if (rxopt->srcrt) opt = ipv6_invert_rthdr(sk, - (struct ipv6_rt_hdr *)(pktopts->nh.raw + - rxopt->srcrt)); + (struct ipv6_rt_hdr *)(skb_network_header(pktopts) + + rxopt->srcrt)); } if (opt != NULL && opt->srcrt != NULL) { @@ -313,6 +313,7 @@ static void dccp_v6_reqsk_destructor(str static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) { struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh; + struct ipv6hdr *rxip6h; const u32 dccp_hdr_reset_len = sizeof(struct dccp_hdr) + sizeof(struct dccp_hdr_ext) + sizeof(struct dccp_hdr_reset); @@ -352,12 +353,13 @@ static void dccp_v6_ctl_send_reset(struc dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), DCCP_SKB_CB(rxskb)->dccpd_seq); dccp_csum_outgoing(skb); - dh->dccph_checksum = dccp_v6_csum_finish(skb, &rxskb->nh.ipv6h->saddr, - &rxskb->nh.ipv6h->daddr); + rxip6h = ipv6_hdr(rxskb); + dh->dccph_checksum = dccp_v6_csum_finish(skb, &rxip6h->saddr, + &rxip6h->daddr); memset(&fl, 0, sizeof(fl)); - ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr); - ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr); + ipv6_addr_copy(&fl.fl6_dst, &rxip6h->saddr); + ipv6_addr_copy(&fl.fl6_src, &rxip6h->daddr); fl.proto = IPPROTO_DCCP; fl.oif = inet6_iif(rxskb); @@ -390,7 +392,7 @@ static struct request_sock_ops dccp6_req static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) { const struct dccp_hdr *dh = dccp_hdr(skb); - const struct ipv6hdr *iph = skb->nh.ipv6h; + const struct ipv6hdr *iph = ipv6_hdr(skb); struct sock *nsk; struct request_sock **prev; /* Find possible connection requests. */ @@ -460,8 +462,8 @@ static int dccp_v6_conn_request(struct s goto drop_and_free; ireq6 = inet6_rsk(req); - ipv6_addr_copy(&ireq6->rmt_addr, &skb->nh.ipv6h->saddr); - ipv6_addr_copy(&ireq6->loc_addr, &skb->nh.ipv6h->daddr); + ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr); + ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr); ireq6->pktopts = NULL; if (ipv6_opt_accepted(sk, skb) || @@ -546,7 +548,7 @@ static struct sock *dccp_v6_request_recv newnp->pktoptions = NULL; newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); - newnp->mcast_hops = skb->nh.ipv6h->hop_limit; + newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; /* * No need to charge this sock to the relevant IPv6 refcnt debug socks count @@ -573,8 +575,8 @@ static struct sock *dccp_v6_request_recv if (rxopt->srcrt) opt = ipv6_invert_rthdr(sk, - (struct ipv6_rt_hdr *)(ireq6->pktopts->nh.raw + - rxopt->srcrt)); + (struct ipv6_rt_hdr *)(skb_network_header(ireq6->pktopts) + + rxopt->srcrt)); } if (dst == NULL) { @@ -653,7 +655,7 @@ static struct sock *dccp_v6_request_recv } newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); - newnp->mcast_hops = skb->nh.ipv6h->hop_limit; + newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; /* * Clone native IPv6 options from listening socket (if any) @@ -826,8 +828,8 @@ static int dccp_v6_rcv(struct sk_buff ** goto discard_it; /* Step 1: If header checksum is incorrect, drop packet and return. */ - if (dccp_v6_csum_finish(skb, &skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr)) { + if (dccp_v6_csum_finish(skb, &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr)) { DCCP_WARN("dropped packet with invalid checksum\n"); goto discard_it; } @@ -844,9 +846,9 @@ static int dccp_v6_rcv(struct sk_buff ** /* Step 2: * Look up flow ID in table and get corresponding socket */ - sk = __inet6_lookup(&dccp_hashinfo, &skb->nh.ipv6h->saddr, + sk = __inet6_lookup(&dccp_hashinfo, &ipv6_hdr(skb)->saddr, dh->dccph_sport, - &skb->nh.ipv6h->daddr, ntohs(dh->dccph_dport), + &ipv6_hdr(skb)->daddr, ntohs(dh->dccph_dport), inet6_iif(skb)); /* * Step 2: diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 6d235b3..e18e249 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -27,7 +27,7 @@ #include "feat.h" struct inet_timewait_death_row dccp_death_row = { .sysctl_max_tw_buckets = NR_FILE * 2, .period = DCCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS, - .death_lock = SPIN_LOCK_UNLOCKED, + .death_lock = __SPIN_LOCK_UNLOCKED(dccp_death_row.death_lock), .hashinfo = &dccp_hashinfo, .tw_timer = TIMER_INITIALIZER(inet_twdr_hangman, 0, (unsigned long)&dccp_death_row), diff --git a/net/dccp/options.c b/net/dccp/options.c index ca13f77..34d536d 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -29,8 +29,6 @@ int sysctl_dccp_feat_ack_ratio = D int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR; int sysctl_dccp_feat_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT; -EXPORT_SYMBOL_GPL(sysctl_dccp_feat_sequence_window); - void dccp_minisock_init(struct dccp_minisock *dmsk) { dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; @@ -174,21 +172,25 @@ int dccp_parse_options(struct sock *sk, opt_recv->dccpor_timestamp_echo = ntohl(*(__be32 *)value); dccp_pr_debug("%s rx opt: TIMESTAMP_ECHO=%u, len=%d, " - "ackno=%llu, ", dccp_role(sk), + "ackno=%llu", dccp_role(sk), opt_recv->dccpor_timestamp_echo, len + 2, (unsigned long long) DCCP_SKB_CB(skb)->dccpd_ack_seq); - if (len == 4) + if (len == 4) { + dccp_pr_debug_cat("\n"); break; + } if (len == 6) elapsed_time = ntohs(*(__be16 *)(value + 4)); else elapsed_time = ntohl(*(__be32 *)(value + 4)); + dccp_pr_debug_cat(", ELAPSED_TIME=%d\n", elapsed_time); + /* Give precedence to the biggest ELAPSED_TIME */ if (elapsed_time > opt_recv->dccpor_elapsed_time) opt_recv->dccpor_elapsed_time = elapsed_time; @@ -565,6 +567,14 @@ int dccp_insert_options(struct sock *sk, dccp_insert_options_feat(sk, skb)) return -1; + /* + * Obtain RTT sample from Request/Response exchange. + * This is currently used in CCID 3 initialisation. + */ + if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST && + dccp_insert_option_timestamp(sk, skb)) + return -1; + /* XXX: insert other options when appropriate */ if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) { diff --git a/net/dccp/output.c b/net/dccp/output.c index aa21cc4..c8d843e 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -194,6 +194,7 @@ static int dccp_wait_for_ccid(struct soc rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); if (rc <= 0) break; + dccp_pr_debug("delayed send by %d msec\n", rc); delay = msecs_to_jiffies(rc); sk->sk_write_pending++; release_sock(sk); @@ -255,7 +256,7 @@ void dccp_write_xmit(struct sock *sk, in DCCP_BUG("err=%d after ccid_hc_tx_packet_sent", err); } else { - dccp_pr_debug("packet discarded\n"); + dccp_pr_debug("packet discarded due to err=%d\n", err); kfree_skb(skb); } } diff --git a/net/dccp/probe.c b/net/dccp/probe.c index 3b1f509..1f5e3ba 100644 --- a/net/dccp/probe.c +++ b/net/dccp/probe.c @@ -90,15 +90,18 @@ static int jdccp_sendmsg(struct kiocb *i if (port == 0 || ntohs(inet->dport) == port || ntohs(inet->sport) == port) { if (hctx) - printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %d %d %d %d\n", - NIPQUAD(inet->saddr), ntohs(inet->sport), - NIPQUAD(inet->daddr), ntohs(inet->dport), size, - hctx->ccid3hctx_s, hctx->ccid3hctx_rtt, - hctx->ccid3hctx_p, hctx->ccid3hctx_t_ipi); + printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %d %d %d %u " + "%llu %llu %d\n", + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport), size, + hctx->ccid3hctx_s, hctx->ccid3hctx_rtt, + hctx->ccid3hctx_p, hctx->ccid3hctx_x_calc, + hctx->ccid3hctx_x_recv >> 6, + hctx->ccid3hctx_x >> 6, hctx->ccid3hctx_t_ipi); else printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d\n", - NIPQUAD(inet->saddr), ntohs(inet->sport), - NIPQUAD(inet->daddr), ntohs(inet->dport), size); + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport), size); } jprobe_return(); diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index c6568d6..a205eaa 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -2413,6 +2413,7 @@ #if 0 static void __exit decnet_exit(void) { sock_unregister(AF_DECnet); + rtnl_unregister_all(PF_DECnet); dev_remove_pack(&dn_dix_packet_type); dn_unregister_sysctl(); diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 060d725..5c2a995 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -799,7 +799,6 @@ static int dn_nl_dump_ifaddr(struct sk_b skip_ndevs = cb->args[0]; skip_naddr = cb->args[1]; - read_lock(&dev_base_lock); for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) { if (idx < skip_ndevs) continue; @@ -824,8 +823,6 @@ static int dn_nl_dump_ifaddr(struct sk_b } } done: - read_unlock(&dev_base_lock); - cb->args[0] = idx; cb->args[1] = dn_idx; @@ -913,7 +910,7 @@ static void dn_send_endnode_hello(struct pktlen = (__le16 *)skb_push(skb,2); *pktlen = dn_htons(skb->len - 2); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); dn_rt_finish_output(skb, dn_rt_all_rt_mcast, msg->id); } @@ -1005,7 +1002,7 @@ static void dn_send_router_hello(struct pktlen = (__le16 *)skb_push(skb, 2); *pktlen = dn_htons(skb->len - 2); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); if (dn_am_i_a_router(dn, dn_db, ifa)) { struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC); @@ -1447,24 +1444,6 @@ static const struct file_operations dn_d #endif /* CONFIG_PROC_FS */ -static struct rtnetlink_link dnet_rtnetlink_table[RTM_NR_MSGTYPES] = -{ - [RTM_NEWADDR - RTM_BASE] = { .doit = dn_nl_newaddr, }, - [RTM_DELADDR - RTM_BASE] = { .doit = dn_nl_deladdr, }, - [RTM_GETADDR - RTM_BASE] = { .dumpit = dn_nl_dump_ifaddr, }, -#ifdef CONFIG_DECNET_ROUTER - [RTM_NEWROUTE - RTM_BASE] = { .doit = dn_fib_rtm_newroute, }, - [RTM_DELROUTE - RTM_BASE] = { .doit = dn_fib_rtm_delroute, }, - [RTM_GETROUTE - RTM_BASE] = { .doit = dn_cache_getroute, - .dumpit = dn_fib_dump, }, - [RTM_GETRULE - RTM_BASE] = { .dumpit = dn_fib_dump_rules, }, -#else - [RTM_GETROUTE - RTM_BASE] = { .doit = dn_cache_getroute, - .dumpit = dn_cache_dump, }, -#endif - -}; - static int __initdata addr[2]; module_param_array(addr, int, NULL, 0444); MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node"); @@ -1485,7 +1464,9 @@ void __init dn_dev_init(void) dn_dev_devices_on(); - rtnetlink_links[PF_DECnet] = dnet_rtnetlink_table; + rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL); + rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL); + rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr); proc_net_fops_create("decnet_dev", S_IRUGO, &dn_dev_seq_fops); @@ -1500,8 +1481,6 @@ #endif /* CONFIG_SYSCTL */ void __exit dn_dev_cleanup(void) { - rtnetlink_links[PF_DECnet] = NULL; - #ifdef CONFIG_SYSCTL { int i; diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 82d58a9..310a862 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -504,7 +504,7 @@ static int dn_fib_check_attr(struct rtms return 0; } -int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct dn_fib_table *tb; struct rtattr **rta = arg; @@ -520,7 +520,7 @@ int dn_fib_rtm_delroute(struct sk_buff * return -ESRCH; } -int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct dn_fib_table *tb; struct rtattr **rta = arg; @@ -748,11 +748,13 @@ void __exit dn_fib_cleanup(void) void __init dn_fib_init(void) { - dn_fib_table_init(); dn_fib_rules_init(); register_dnaddr_notifier(&dn_fib_dnaddr_notifier); + + rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL); + rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL); } diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index bf701cf..4bf066c 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -261,7 +261,7 @@ static int dn_long_output(struct sk_buff lp->s_class = 0; lp->pt = 0; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); } @@ -300,7 +300,7 @@ static int dn_short_output(struct sk_buf sp->srcnode = cb->src; sp->forward = cb->hops & 0x3f; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); } @@ -342,7 +342,7 @@ static int dn_phase3_output(struct sk_bu sp->srcnode = cb->src & dn_htons(0x03ff); sp->forward = cb->hops & 0x3f; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); } diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index 9d20904..4074a6e 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -362,7 +362,8 @@ static void dn_nsp_conn_conf(struct sock u16 dlen = *skb->data; if ((dlen <= 16) && (dlen <= skb->len)) { scp->conndata_in.opt_optl = dn_htons(dlen); - memcpy(scp->conndata_in.opt_data, skb->data + 1, dlen); + skb_copy_from_linear_data_offset(skb, 1, + scp->conndata_in.opt_data, dlen); } } dn_nsp_send_link(sk, DN_NOCHANGE, 0); @@ -406,7 +407,7 @@ static void dn_nsp_disc_init(struct sock u16 dlen = *skb->data; if ((dlen <= 16) && (dlen <= skb->len)) { scp->discdata_in.opt_optl = dn_htons(dlen); - memcpy(scp->discdata_in.opt_data, skb->data + 1, dlen); + skb_copy_from_linear_data_offset(skb, 1, scp->discdata_in.opt_data, dlen); } } @@ -725,7 +726,7 @@ static int dn_nsp_rx_packet(struct sk_bu if (!pskb_may_pull(skb, 2)) goto free_out; - skb->h.raw = skb->data; + skb_reset_transport_header(skb); cb->nsp_flags = *ptr++; if (decnet_debug_level & 2) diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index 2d2cda8..7404653 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -79,7 +79,7 @@ static void dn_nsp_send(struct sk_buff * struct dst_entry *dst; struct flowi fl; - skb->h.raw = skb->data; + skb_reset_transport_header(skb); scp->stamp = jiffies; dst = sk_dst_check(sk, 0); @@ -681,8 +681,10 @@ void dn_nsp_send_conninit(struct sock *s if (scp->peer.sdn_objnum) type = 0; - skb_put(skb, dn_sockaddr2username(&scp->peer, skb->tail, type)); - skb_put(skb, dn_sockaddr2username(&scp->addr, skb->tail, 2)); + skb_put(skb, dn_sockaddr2username(&scp->peer, + skb_tail_pointer(skb), type)); + skb_put(skb, dn_sockaddr2username(&scp->addr, + skb_tail_pointer(skb), 2)); menuver = DN_MENUVER_ACC | DN_MENUVER_USR; if (scp->peer.sdn_flags & SDF_PROXY) diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index c1b5502..5d7337b 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -77,6 +77,7 @@ #include #include #include #include +#include #include #include #include @@ -386,7 +387,7 @@ static int dn_return_short(struct sk_buf __le16 tmp; /* Add back headers */ - skb_push(skb, skb->data - skb->nh.raw); + skb_push(skb, skb->data - skb_network_header(skb)); if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL) return NET_RX_DROP; @@ -425,7 +426,7 @@ static int dn_return_long(struct sk_buff unsigned char tmp[ETH_ALEN]; /* Add back all headers */ - skb_push(skb, skb->data - skb->nh.raw); + skb_push(skb, skb->data - skb_network_header(skb)); if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL) return NET_RX_DROP; @@ -504,7 +505,7 @@ static int dn_route_rx_long(struct sk_bu goto drop_it; skb_pull(skb, 20); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); /* Destination info */ ptr += 2; @@ -542,7 +543,7 @@ static int dn_route_rx_short(struct sk_b goto drop_it; skb_pull(skb, 5); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); cb->dst = *(__le16 *)ptr; ptr += 2; @@ -615,7 +616,7 @@ int dn_route_rcv(struct sk_buff *skb, st flags = *skb->data; } - skb->nh.raw = skb->data; + skb_reset_network_header(skb); /* * Weed out future version DECnet @@ -1468,7 +1469,7 @@ static int dn_rt_fill_info(struct sk_buf struct dn_route *rt = (struct dn_route *)skb->dst; struct rtmsg *r; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); long expires; nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags); @@ -1509,19 +1510,19 @@ static int dn_rt_fill_info(struct sk_buf if (rt->fl.iif) RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->fl.iif); - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } /* * This is called by both endnodes and routers now. */ -int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg) +static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg) { struct rtattr **rta = arg; struct rtmsg *rtm = NLMSG_DATA(nlh); @@ -1537,7 +1538,7 @@ int dn_cache_getroute(struct sk_buff *in skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (skb == NULL) return -ENOBUFS; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); cb = DN_SKB_CB(skb); if (rta[RTA_SRC-1]) @@ -1812,6 +1813,13 @@ void __init dn_route_init(void) dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1); proc_net_fops_create("decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops); + +#ifdef CONFIG_DECNET_ROUTER + rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, dn_fib_dump); +#else + rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, + dn_cache_dump); +#endif } void __exit dn_route_cleanup(void) diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index 5e86dd5..17a1932 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -31,6 +31,7 @@ #include #include #include #include +#include static struct fib_rules_ops dn_fib_rules_ops; @@ -239,9 +240,9 @@ static u32 dn_fib_rule_default_pref(void return 0; } -int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) +static void dn_fib_rule_flush_cache(void) { - return fib_rules_dump(skb, cb, AF_DECnet); + dn_rt_cache_flush(-1); } static struct fib_rules_ops dn_fib_rules_ops = { @@ -254,6 +255,7 @@ static struct fib_rules_ops dn_fib_rules .compare = dn_fib_rule_compare, .fill = dn_fib_rule_fill, .default_pref = dn_fib_rule_default_pref, + .flush_cache = dn_fib_rule_flush_cache, .nlgroup = RTNLGRP_DECnet_RULE, .policy = dn_fib_rule_policy, .rules_list = &dn_fib_rules, diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c index 780a141..d6615c9 100644 --- a/net/decnet/dn_table.c +++ b/net/decnet/dn_table.c @@ -28,6 +28,7 @@ #include #include #include /* RTF_xxx */ #include +#include #include #include #include @@ -295,7 +296,7 @@ static int dn_fib_dump_info(struct sk_bu { struct rtmsg *rtm; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*rtm), flags); rtm = NLMSG_DATA(nlh); @@ -337,19 +338,19 @@ static int dn_fib_dump_info(struct sk_bu nhp->rtnh_ifindex = nh->nh_oif; if (nh->nh_gw) RTA_PUT(skb, RTA_GATEWAY, 2, &nh->nh_gw); - nhp->rtnh_len = skb->tail - (unsigned char *)nhp; + nhp->rtnh_len = skb_tail_pointer(skb) - (unsigned char *)nhp; } endfor_nexthops(fi); mp_head->rta_type = RTA_MULTIPATH; - mp_head->rta_len = skb->tail - (u8*)mp_head; + mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head; } - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -EMSGSIZE; } diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index 0e62def..6962346 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -33,7 +33,7 @@ static struct sk_buff *dnrmg_build_messa { struct sk_buff *skb = NULL; size_t size; - unsigned char *old_tail; + sk_buff_data_t old_tail; struct nlmsghdr *nlh; unsigned char *ptr; struct nf_dn_rtmsg *rtm; @@ -48,7 +48,7 @@ static struct sk_buff *dnrmg_build_messa rtm = (struct nf_dn_rtmsg *)NLMSG_DATA(nlh); rtm->nfdn_ifindex = rt_skb->dev->ifindex; ptr = NFDN_RTMSG(rtm); - memcpy(ptr, rt_skb->data, rt_skb->len); + skb_copy_from_linear_data(rt_skb, ptr, rt_skb->len); nlh->nlmsg_len = skb->tail - old_tail; return skb; @@ -102,7 +102,7 @@ #define RCV_SKB_FAIL(err) do { netlink_a static inline void dnrmg_receive_user_skb(struct sk_buff *skb) { - struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; + struct nlmsghdr *nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) return; @@ -138,7 +138,7 @@ static int __init dn_rtmsg_init(void) int rv = 0; dnrmg = netlink_kernel_create(NETLINK_DNRTMSG, DNRNG_NLGRP_MAX, - dnrmg_receive_user_sk, THIS_MODULE); + dnrmg_receive_user_sk, NULL, THIS_MODULE); if (dnrmg == NULL) { printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket"); return -ENOMEM; diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index bc12e36..b5524f3 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -162,7 +162,7 @@ static int econet_recvmsg(struct kiocb * err = memcpy_toiovec(msg->msg_iov, skb->data, copied); if (err) goto out_free; - skb_get_timestamp(skb, &sk->sk_stamp); + sk->sk_stamp = skb->tstamp; if (msg->msg_name) memcpy(msg->msg_name, skb->cb, msg->msg_namelen); @@ -345,7 +345,7 @@ #ifdef CONFIG_ECONET_NATIVE goto out_unlock; skb_reserve(skb, LL_RESERVED_SPACE(dev)); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); eb = (struct ec_cb *)&skb->cb; @@ -366,7 +366,7 @@ #ifdef CONFIG_ECONET_NATIVE fh->cb = cb; fh->port = port; if (sock->type != SOCK_DGRAM) { - skb->tail = skb->data; + skb_reset_tail_pointer(skb); skb->len = 0; } else if (res < 0) goto out_free; @@ -727,6 +727,9 @@ static int econet_ioctl(struct socket *s case SIOCGSTAMP: return sock_get_timestamp(sk, argp); + case SIOCGSTAMPNS: + return sock_get_timestampns(sk, argp); + case SIOCSIFADDR: case SIOCGIFADDR: return ec_dev_ioctl(sock, cmd, argp); @@ -845,7 +848,7 @@ static void aun_send_response(__u32 addr static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len) { - struct iphdr *ip = skb->nh.iph; + struct iphdr *ip = ip_hdr(skb); unsigned char stn = ntohl(ip->saddr) & 0xff; struct sock *sk; struct sk_buff *newskb; @@ -940,10 +943,10 @@ static void aun_data_available(struct so printk(KERN_DEBUG "AUN: recvfrom() error %d\n", -err); } - data = skb->h.raw + sizeof(struct udphdr); + data = skb_transport_header(skb) + sizeof(struct udphdr); ah = (struct aunhdr *)data; len = skb->len - sizeof(struct udphdr); - ip = skb->nh.iph; + ip = ip_hdr(skb); switch (ah->code) { diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 7391f55..0ac2524 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -156,7 +156,8 @@ __be16 eth_type_trans(struct sk_buff *sk struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw = skb->data; + skb->dev = dev; + skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); eth = eth_hdr(skb); @@ -228,7 +229,7 @@ int eth_header_cache(struct neighbour *n eth = (struct ethhdr *) (((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth)))); - if (type == __constant_htons(ETH_P_802_3)) + if (type == htons(ETH_P_802_3)) return -1; eth->h_proto = type; diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig index 6ef766e..1438ade 100644 --- a/net/ieee80211/Kconfig +++ b/net/ieee80211/Kconfig @@ -56,7 +56,8 @@ config IEEE80211_CRYPT_CCMP config IEEE80211_CRYPT_TKIP tristate "IEEE 802.11i TKIP encryption" - depends on IEEE80211 && NET_RADIO + depends on IEEE80211 + select WIRELESS_EXT select CRYPTO select CRYPTO_MICHAEL_MIC select CRYPTO_ECB diff --git a/net/ieee80211/ieee80211_crypt.c b/net/ieee80211/ieee80211_crypt.c index 5ed0a98..df5592c 100644 --- a/net/ieee80211/ieee80211_crypt.c +++ b/net/ieee80211/ieee80211_crypt.c @@ -1,7 +1,7 @@ /* * Host AP crypto routines * - * Copyright (c) 2002-2003, Jouni Malinen + * Copyright (c) 2002-2003, Jouni Malinen * Portions Copyright (C) 2004, Intel Corporation * * This program is free software; you can redistribute it and/or modify diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c index 35aa342..b016b41 100644 --- a/net/ieee80211/ieee80211_crypt_ccmp.c +++ b/net/ieee80211/ieee80211_crypt_ccmp.c @@ -1,7 +1,7 @@ /* * Host AP crypt: host-based CCMP encryption implementation for Host AP driver * - * Copyright (c) 2003-2004, Jouni Malinen + * Copyright (c) 2003-2004, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -338,7 +338,7 @@ static int ieee80211_ccmp_decrypt(struct if (ccmp_replay_check(pn, key->rx_pn)) { if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: replay detected: STA=" MAC_FMT + IEEE80211_DEBUG_DROP("CCMP: replay detected: STA=" MAC_FMT " previous PN %02x%02x%02x%02x%02x%02x " "received PN %02x%02x%02x%02x%02x%02x\n", MAC_ARG(hdr->addr2), MAC_ARG(key->rx_pn), diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c index fc1f99a..5a48d8e 100644 --- a/net/ieee80211/ieee80211_crypt_tkip.c +++ b/net/ieee80211/ieee80211_crypt_tkip.c @@ -1,7 +1,7 @@ /* * Host AP crypt: host-based TKIP encryption implementation for Host AP driver * - * Copyright (c) 2003-2004, Jouni Malinen + * Copyright (c) 2003-2004, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -465,7 +465,7 @@ static int ieee80211_tkip_decrypt(struct if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: replay detected: STA=" MAC_FMT + IEEE80211_DEBUG_DROP("TKIP: replay detected: STA=" MAC_FMT " previous TSC %08x%04x received TSC " "%08x%04x\n", MAC_ARG(hdr->addr2), tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); @@ -507,7 +507,7 @@ static int ieee80211_tkip_decrypt(struct tkey->rx_phase1_done = 0; } if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: ICV error detected: STA=" + IEEE80211_DEBUG_DROP("TKIP: ICV error detected: STA=" MAC_FMT "\n", MAC_ARG(hdr->addr2)); } tkey->dot11RSNAStatsTKIPICVErrors++; diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c index ec6d885..8d18245 100644 --- a/net/ieee80211/ieee80211_crypt_wep.c +++ b/net/ieee80211/ieee80211_crypt_wep.c @@ -1,7 +1,7 @@ /* * Host AP crypt: host-based WEP encryption implementation for Host AP driver * - * Copyright (c) 2002-2004, Jouni Malinen + * Copyright (c) 2002-2004, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -152,7 +152,7 @@ static int prism2_wep_encrypt(struct sk_ return -1; /* Copy the IV into the first 3 bytes of the key */ - memcpy(key, skb->data + hdr_len, 3); + skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); /* Copy rest of the WEP key (the secret part) */ memcpy(key + 3, wep->key, wep->key_len); diff --git a/net/ieee80211/ieee80211_module.c b/net/ieee80211/ieee80211_module.c index b1c6d1f..7ec6610 100644 --- a/net/ieee80211/ieee80211_module.c +++ b/net/ieee80211/ieee80211_module.c @@ -5,8 +5,8 @@ Portions of this file are based on the WEP enablement code provided by the Host AP project hostap-drivers v0.1.3 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - - Copyright (c) 2002-2003, Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -229,6 +229,7 @@ #ifdef CONFIG_IEEE80211_DEBUG static int debug = 0; u32 ieee80211_debug_level = 0; +EXPORT_SYMBOL_GPL(ieee80211_debug_level); static struct proc_dir_entry *ieee80211_proc = NULL; static int show_debug_level(char *page, char **start, off_t offset, diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index 4084909..f2de2e4 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -3,8 +3,8 @@ * for Intersil Prism2/2.5/3 - hostap.o module, common routines * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2003, Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen * Copyright (c) 2004-2005, Intel Corporation * * This program is free software; you can redistribute it and/or modify @@ -42,7 +42,7 @@ static void ieee80211_monitor_rx(struct u16 fc = le16_to_cpu(hdr->frame_ctl); skb->dev = ieee->dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ieee80211_get_hdrlen(fc)); skb->pkt_type = PACKET_OTHERHOST; skb->protocol = __constant_htons(ETH_P_80211_RAW); @@ -606,12 +606,12 @@ #endif if (frag == 0) { /* copy first fragment (including full headers) into * beginning of the fragment cache skb */ - memcpy(skb_put(frag_skb, flen), skb->data, flen); + skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen); } else { /* append frame payload to the end of the fragment * cache skb */ - memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, - flen); + skb_copy_from_linear_data_offset(skb, hdrlen, + skb_put(frag_skb, flen), flen); } dev_kfree_skb_any(skb); skb = NULL; @@ -759,8 +759,9 @@ #ifdef NOT_YET IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) { /* Non-standard frame: get addr4 from its bogus location after * the payload */ - memcpy(skb->data + ETH_ALEN, - skb->data + skb->len - ETH_ALEN, ETH_ALEN); + skb_copy_to_linear_data_offset(skb, ETH_ALEN, + skb->data + skb->len - ETH_ALEN, + ETH_ALEN); skb_trim(skb, skb->len - ETH_ALEN); } #endif @@ -789,10 +790,11 @@ #ifdef NOT_YET if (skb2 != NULL) { /* send to wireless media */ - skb2->protocol = __constant_htons(ETH_P_802_3); - skb2->mac.raw = skb2->nh.raw = skb2->data; - /* skb2->nh.raw = skb2->data + ETH_HLEN; */ skb2->dev = dev; + skb2->protocol = __constant_htons(ETH_P_802_3); + skb_reset_mac_header(skb2); + skb_reset_network_header(skb2); + /* skb2->network_header += ETH_HLEN; */ dev_queue_xmit(skb2); } #endif @@ -800,7 +802,6 @@ #endif if (skb) { skb->protocol = eth_type_trans(skb, dev); memset(skb->cb, 0, sizeof(skb->cb)); - skb->dev = dev; skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ if (netif_rx(skb) == NET_RX_DROP) { /* netif_rx always succeeds, but it might drop diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c index 0292d63..a4c3c51 100644 --- a/net/ieee80211/ieee80211_tx.c +++ b/net/ieee80211/ieee80211_tx.c @@ -225,10 +225,10 @@ static int ieee80211_classify(struct sk_ struct iphdr *ip; eth = (struct ethhdr *)skb->data; - if (eth->h_proto != __constant_htons(ETH_P_IP)) + if (eth->h_proto != htons(ETH_P_IP)) return 0; - ip = skb->nh.iph; + ip = ip_hdr(skb); switch (ip->tos & 0xfc) { case 0x20: return 2; @@ -309,8 +309,8 @@ int ieee80211_xmit(struct sk_buff *skb, } /* Save source and destination addresses */ - memcpy(dest, skb->data, ETH_ALEN); - memcpy(src, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data(skb, dest, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN); if (host_encrypt || host_build_iv) fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | @@ -363,7 +363,7 @@ int ieee80211_xmit(struct sk_buff *skb, snapped = 1; ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)), ether_type); - memcpy(skb_put(skb_new, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len); res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv); if (res < 0) { IEEE80211_ERROR("msdu encryption failed\n"); @@ -492,7 +492,7 @@ int ieee80211_xmit(struct sk_buff *skb, bytes -= SNAP_SIZE + sizeof(u16); } - memcpy(skb_put(skb_frag, bytes), skb->data, bytes); + skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes); /* Advance the SKB... */ skb_pull(skb, bytes); diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c index 40d7a55..cee5e13 100644 --- a/net/ieee80211/ieee80211_wx.c +++ b/net/ieee80211/ieee80211_wx.c @@ -5,8 +5,8 @@ Portions of this file are based on the WEP enablement code provided by the Host AP project hostap-drivers v0.1.3 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - - Copyright (c) 2002-2003, Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 9e8ef50..e62aee0 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -574,6 +574,33 @@ config TCP_CONG_VENO loss packets. See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf +config TCP_CONG_YEAH + tristate "YeAH TCP" + depends on EXPERIMENTAL + default n + ---help--- + YeAH-TCP is a sender-side high-speed enabled TCP congestion control + algorithm, which uses a mixed loss/delay approach to compute the + congestion window. It's design goals target high efficiency, + internal, RTT and Reno fairness, resilience to link loss while + keeping network elements load as low as possible. + + For further details look here: + http://wil.cs.caltech.edu/pfldnet2007/paper/YeAH_TCP.pdf + +config TCP_CONG_ILLINOIS + tristate "TCP Illinois" + depends on EXPERIMENTAL + default n + ---help--- + TCP-Illinois is a sender-side modificatio of TCP Reno for + high speed long delay links. It uses round-trip-time to + adjust the alpha and beta parameters to achieve a higher average + throughput and maintain fairness. + + For further details see: + http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html + choice prompt "Default TCP congestion control" default DEFAULT_CUBIC diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 7a06862..4ff6c15 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -49,6 +49,8 @@ obj-$(CONFIG_TCP_CONG_VEGAS) += tcp_vega obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o +obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o +obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index cf358c8..16aae8e 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -87,6 +87,7 @@ #include #include #include #include +#include #include #include @@ -217,6 +218,26 @@ out: return err; } +u32 inet_ehash_secret __read_mostly; +EXPORT_SYMBOL(inet_ehash_secret); + +/* + * inet_ehash_secret must be set exactly once + * Instead of using a dedicated spinlock, we (ab)use inetsw_lock + */ +void build_ehash_secret(void) +{ + u32 rnd; + do { + get_random_bytes(&rnd, sizeof(rnd)); + } while (rnd == 0); + spin_lock_bh(&inetsw_lock); + if (!inet_ehash_secret) + inet_ehash_secret = rnd; + spin_unlock_bh(&inetsw_lock); +} +EXPORT_SYMBOL(build_ehash_secret); + /* * Create an inet socket. */ @@ -233,6 +254,11 @@ static int inet_create(struct socket *so int try_loading_module = 0; int err; + if (sock->type != SOCK_RAW && + sock->type != SOCK_DGRAM && + !inet_ehash_secret) + build_ehash_secret(); + sock->state = SS_UNCONNECTED; /* Look for the requested type/protocol pair. */ @@ -755,6 +781,9 @@ int inet_ioctl(struct socket *sock, unsi case SIOCGSTAMP: err = sock_get_timestamp(sk, (struct timeval __user *)arg); break; + case SIOCGSTAMPNS: + err = sock_get_timestampns(sk, (struct timespec __user *)arg); + break; case SIOCADDRT: case SIOCDELRT: case SIOCRTMSG: @@ -1109,7 +1138,7 @@ static int inet_gso_send_check(struct sk if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) goto out; - iph = skb->nh.iph; + iph = ip_hdr(skb); ihl = iph->ihl * 4; if (ihl < sizeof(*iph)) goto out; @@ -1117,8 +1146,9 @@ static int inet_gso_send_check(struct sk if (unlikely(!pskb_may_pull(skb, ihl))) goto out; - skb->h.raw = __skb_pull(skb, ihl); - iph = skb->nh.iph; + __skb_pull(skb, ihl); + skb_reset_transport_header(skb); + iph = ip_hdr(skb); proto = iph->protocol & (MAX_INET_PROTOS - 1); err = -EPROTONOSUPPORT; @@ -1152,7 +1182,7 @@ static struct sk_buff *inet_gso_segment( if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) goto out; - iph = skb->nh.iph; + iph = ip_hdr(skb); ihl = iph->ihl * 4; if (ihl < sizeof(*iph)) goto out; @@ -1160,8 +1190,9 @@ static struct sk_buff *inet_gso_segment( if (unlikely(!pskb_may_pull(skb, ihl))) goto out; - skb->h.raw = __skb_pull(skb, ihl); - iph = skb->nh.iph; + __skb_pull(skb, ihl); + skb_reset_transport_header(skb); + iph = ip_hdr(skb); id = ntohs(iph->id); proto = iph->protocol & (MAX_INET_PROTOS - 1); segs = ERR_PTR(-EPROTONOSUPPORT); @@ -1177,17 +1208,57 @@ static struct sk_buff *inet_gso_segment( skb = segs; do { - iph = skb->nh.iph; + iph = ip_hdr(skb); iph->id = htons(id++); iph->tot_len = htons(skb->len - skb->mac_len); iph->check = 0; - iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); + iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); } while ((skb = skb->next)); out: return segs; } +unsigned long snmp_fold_field(void *mib[], int offt) +{ + unsigned long res = 0; + int i; + + for_each_possible_cpu(i) { + res += *(((unsigned long *) per_cpu_ptr(mib[0], i)) + offt); + res += *(((unsigned long *) per_cpu_ptr(mib[1], i)) + offt); + } + return res; +} +EXPORT_SYMBOL_GPL(snmp_fold_field); + +int snmp_mib_init(void *ptr[2], size_t mibsize, size_t mibalign) +{ + BUG_ON(ptr == NULL); + ptr[0] = __alloc_percpu(mibsize); + if (!ptr[0]) + goto err0; + ptr[1] = __alloc_percpu(mibsize); + if (!ptr[1]) + goto err1; + return 0; +err1: + free_percpu(ptr[0]); + ptr[0] = NULL; +err0: + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(snmp_mib_init); + +void snmp_mib_free(void *ptr[2]) +{ + BUG_ON(ptr == NULL); + free_percpu(ptr[0]); + free_percpu(ptr[1]); + ptr[0] = ptr[1] = NULL; +} +EXPORT_SYMBOL_GPL(snmp_mib_free); + #ifdef CONFIG_IP_MULTICAST static struct net_protocol igmp_protocol = { .handler = igmp_rcv, @@ -1214,28 +1285,47 @@ static struct net_protocol icmp_protocol static int __init init_ipv4_mibs(void) { - net_statistics[0] = alloc_percpu(struct linux_mib); - net_statistics[1] = alloc_percpu(struct linux_mib); - ip_statistics[0] = alloc_percpu(struct ipstats_mib); - ip_statistics[1] = alloc_percpu(struct ipstats_mib); - icmp_statistics[0] = alloc_percpu(struct icmp_mib); - icmp_statistics[1] = alloc_percpu(struct icmp_mib); - tcp_statistics[0] = alloc_percpu(struct tcp_mib); - tcp_statistics[1] = alloc_percpu(struct tcp_mib); - udp_statistics[0] = alloc_percpu(struct udp_mib); - udp_statistics[1] = alloc_percpu(struct udp_mib); - udplite_statistics[0] = alloc_percpu(struct udp_mib); - udplite_statistics[1] = alloc_percpu(struct udp_mib); - if (! - (net_statistics[0] && net_statistics[1] && ip_statistics[0] - && ip_statistics[1] && tcp_statistics[0] && tcp_statistics[1] - && udp_statistics[0] && udp_statistics[1] - && udplite_statistics[0] && udplite_statistics[1] ) ) - return -ENOMEM; - - (void) tcp_mib_init(); + if (snmp_mib_init((void **)net_statistics, + sizeof(struct linux_mib), + __alignof__(struct linux_mib)) < 0) + goto err_net_mib; + if (snmp_mib_init((void **)ip_statistics, + sizeof(struct ipstats_mib), + __alignof__(struct ipstats_mib)) < 0) + goto err_ip_mib; + if (snmp_mib_init((void **)icmp_statistics, + sizeof(struct icmp_mib), + __alignof__(struct icmp_mib)) < 0) + goto err_icmp_mib; + if (snmp_mib_init((void **)tcp_statistics, + sizeof(struct tcp_mib), + __alignof__(struct tcp_mib)) < 0) + goto err_tcp_mib; + if (snmp_mib_init((void **)udp_statistics, + sizeof(struct udp_mib), + __alignof__(struct udp_mib)) < 0) + goto err_udp_mib; + if (snmp_mib_init((void **)udplite_statistics, + sizeof(struct udp_mib), + __alignof__(struct udp_mib)) < 0) + goto err_udplite_mib; + + tcp_mib_init(); return 0; + +err_udplite_mib: + snmp_mib_free((void **)udp_statistics); +err_udp_mib: + snmp_mib_free((void **)tcp_statistics); +err_tcp_mib: + snmp_mib_free((void **)icmp_statistics); +err_icmp_mib: + snmp_mib_free((void **)ip_statistics); +err_ip_mib: + snmp_mib_free((void **)net_statistics); +err_net_mib: + return -ENOMEM; } static int ipv4_proc_init(void); @@ -1336,7 +1426,7 @@ #endif * Initialise per-cpu ipv4 mibs */ - if(init_ipv4_mibs()) + if (init_ipv4_mibs()) printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ; ipv4_proc_init(); diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 7194eb4..6da8ff5 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -65,7 +65,7 @@ static int ah_output(struct xfrm_state * char buf[60]; } tmp_iph; - top_iph = skb->nh.iph; + top_iph = ip_hdr(skb); iph = &tmp_iph.iph; iph->tos = top_iph->tos; @@ -152,9 +152,9 @@ static int ah_input(struct xfrm_state *x skb->ip_summed = CHECKSUM_NONE; ah = (struct ip_auth_hdr*)skb->data; - iph = skb->nh.iph; + iph = ip_hdr(skb); - ihl = skb->data - skb->nh.raw; + ihl = skb->data - skb_network_header(skb); memcpy(work_buf, iph, ihl); iph->ttl = 0; @@ -181,7 +181,9 @@ static int ah_input(struct xfrm_state *x } } ((struct iphdr*)work_buf)->protocol = ah->nexthdr; - skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl); + skb->network_header += ah_hlen; + memcpy(skb_network_header(skb), work_buf, ihl); + skb->transport_header = skb->network_header; __skb_pull(skb, ah_hlen + ihl); return 0; @@ -196,8 +198,8 @@ static void ah4_err(struct sk_buff *skb, struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+(iph->ihl<<2)); struct xfrm_state *x; - if (skb->h.icmph->type != ICMP_DEST_UNREACH || - skb->h.icmph->code != ICMP_FRAG_NEEDED) + if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH || + icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) return; x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 1a3488a..7110779 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -342,13 +342,13 @@ static void arp_solicit(struct neighbour switch (IN_DEV_ARP_ANNOUNCE(in_dev)) { default: case 0: /* By default announce any local IP */ - if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) - saddr = skb->nh.iph->saddr; + if (skb && inet_addr_type(ip_hdr(skb)->saddr) == RTN_LOCAL) + saddr = ip_hdr(skb)->saddr; break; case 1: /* Restrict announcements of saddr in same subnet */ if (!skb) break; - saddr = skb->nh.iph->saddr; + saddr = ip_hdr(skb)->saddr; if (inet_addr_type(saddr) == RTN_LOCAL) { /* saddr should be known to target */ if (inet_addr_onlink(in_dev, target, saddr)) @@ -578,7 +578,7 @@ struct sk_buff *arp_create(int type, int return NULL; skb_reserve(skb, LL_RESERVED_SPACE(dev)); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4)); skb->dev = dev; skb->protocol = htons(ETH_P_ARP); @@ -721,7 +721,7 @@ static int arp_process(struct sk_buff *s if (in_dev == NULL) goto out; - arp = skb->nh.arph; + arp = arp_hdr(skb); switch (dev_type) { default: @@ -937,7 +937,7 @@ static int arp_rcv(struct sk_buff *skb, (2 * sizeof(u32))))) goto freeskb; - arp = skb->nh.arph; + arp = arp_hdr(skb); if (arp->ar_hln != dev->addr_len || dev->flags & IFF_NOARP || skb->pkt_type == PACKET_OTHERHOST || @@ -1178,7 +1178,7 @@ int arp_ioctl(unsigned int cmd, void __u goto out; } - switch(cmd) { + switch (cmd) { case SIOCDARP: err = arp_req_delete(&r, dev); break; @@ -1360,7 +1360,7 @@ static void *arp_seq_start(struct seq_fi /* ------------------------------------------------------------------------ */ -static struct seq_operations arp_seq_ops = { +static const struct seq_operations arp_seq_ops = { .start = arp_seq_start, .next = neigh_seq_next, .stop = neigh_seq_stop, diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 2ce5b69..e1f1848 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -92,6 +92,33 @@ int cipso_v4_rbm_optfmt = 0; int cipso_v4_rbm_strictvalid = 1; /* + * Protocol Constants + */ + +/* Maximum size of the CIPSO IP option, derived from the fact that the maximum + * IPv4 header size is 60 bytes and the base IPv4 header is 20 bytes long. */ +#define CIPSO_V4_OPT_LEN_MAX 40 + +/* Length of the base CIPSO option, this includes the option type (1 byte), the + * option length (1 byte), and the DOI (4 bytes). */ +#define CIPSO_V4_HDR_LEN 6 + +/* Base length of the restrictive category bitmap tag (tag #1). */ +#define CIPSO_V4_TAG_RBM_BLEN 4 + +/* Base length of the enumerated category tag (tag #2). */ +#define CIPSO_V4_TAG_ENUM_BLEN 4 + +/* Base length of the ranged categories bitmap tag (tag #5). */ +#define CIPSO_V4_TAG_RNG_BLEN 4 +/* The maximum number of category ranges permitted in the ranged category tag + * (tag #5). You may note that the IETF draft states that the maximum number + * of category ranges is 7, but if the low end of the last category range is + * zero then it is possibile to fit 8 category ranges because the zero should + * be omitted. */ +#define CIPSO_V4_TAG_RNG_CAT_MAX 8 + +/* * Helper Functions */ @@ -1109,16 +1136,15 @@ static int cipso_v4_map_cat_rng_hton(con unsigned char *net_cat, u32 net_cat_len) { - /* The constant '16' is not random, it is the maximum number of - * high/low category range pairs as permitted by the CIPSO draft based - * on a maximum IPv4 header length of 60 bytes - the BUG_ON() assertion - * does a sanity check to make sure we don't overflow the array. */ int iter = -1; - u16 array[16]; + u16 array[CIPSO_V4_TAG_RNG_CAT_MAX * 2]; u32 array_cnt = 0; u32 cat_size = 0; - BUG_ON(net_cat_len > 30); + /* make sure we don't overflow the 'array[]' variable */ + if (net_cat_len > + (CIPSO_V4_OPT_LEN_MAX - CIPSO_V4_HDR_LEN - CIPSO_V4_TAG_RNG_BLEN)) + return -ENOSPC; for (;;) { iter = netlbl_secattr_catmap_walk(secattr->mls_cat, iter + 1); @@ -1174,7 +1200,7 @@ static int cipso_v4_map_cat_rng_ntoh(con u16 cat_low; u16 cat_high; - for(net_iter = 0; net_iter < net_cat_len; net_iter += 4) { + for (net_iter = 0; net_iter < net_cat_len; net_iter += 4) { cat_high = ntohs(*((__be16 *)&net_cat[net_iter])); if ((net_iter + 4) <= net_cat_len) cat_low = ntohs(*((__be16 *)&net_cat[net_iter + 2])); @@ -1196,9 +1222,6 @@ static int cipso_v4_map_cat_rng_ntoh(con * Protocol Handling Functions */ -#define CIPSO_V4_OPT_LEN_MAX 40 -#define CIPSO_V4_HDR_LEN 6 - /** * cipso_v4_gentag_hdr - Generate a CIPSO option header * @doi_def: the DOI definition @@ -1676,7 +1699,7 @@ validate_return: */ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway) { - if (skb->nh.iph->protocol == IPPROTO_ICMP || error != -EACCES) + if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES) return; if (gateway) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 98a00d0..088888d 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -48,7 +48,6 @@ #include #include #include #include -#include #include #include #include @@ -62,7 +61,7 @@ #include #include #include #include -#include +#include struct ipv4_devconf ipv4_devconf = { .accept_redirects = 1, @@ -633,7 +632,7 @@ #ifdef CONFIG_KMOD dev_load(ifr.ifr_name); #endif - switch(cmd) { + switch (cmd) { case SIOCGIFADDR: /* Get interface address */ case SIOCGIFBRDADDR: /* Get the broadcast address */ case SIOCGIFDSTADDR: /* Get the destination address */ @@ -708,7 +707,7 @@ #endif if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) goto done; - switch(cmd) { + switch (cmd) { case SIOCGIFADDR: /* Get interface address */ sin->sin_addr.s_addr = ifa->ifa_local; goto rarok; @@ -1183,17 +1182,13 @@ static int inet_dump_ifaddr(struct sk_bu int s_ip_idx, s_idx = cb->args[0]; s_ip_idx = ip_idx = cb->args[1]; - read_lock(&dev_base_lock); for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) { if (idx < s_idx) continue; if (idx > s_idx) s_ip_idx = 0; - rcu_read_lock(); - if ((in_dev = __in_dev_get_rcu(dev)) == NULL) { - rcu_read_unlock(); + if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) continue; - } for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; ifa = ifa->ifa_next, ip_idx++) { @@ -1201,16 +1196,12 @@ static int inet_dump_ifaddr(struct sk_bu continue; if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, - RTM_NEWADDR, NLM_F_MULTI) <= 0) { - rcu_read_unlock(); + RTM_NEWADDR, NLM_F_MULTI) <= 0) goto done; - } } - rcu_read_unlock(); } done: - read_unlock(&dev_base_lock); cb->args[0] = idx; cb->args[1] = ip_idx; @@ -1241,19 +1232,6 @@ errout: rtnl_set_sk_err(RTNLGRP_IPV4_IFADDR, err); } -static struct rtnetlink_link inet_rtnetlink_table[RTM_NR_MSGTYPES] = { - [RTM_NEWADDR - RTM_BASE] = { .doit = inet_rtm_newaddr, }, - [RTM_DELADDR - RTM_BASE] = { .doit = inet_rtm_deladdr, }, - [RTM_GETADDR - RTM_BASE] = { .dumpit = inet_dump_ifaddr, }, - [RTM_NEWROUTE - RTM_BASE] = { .doit = inet_rtm_newroute, }, - [RTM_DELROUTE - RTM_BASE] = { .doit = inet_rtm_delroute, }, - [RTM_GETROUTE - RTM_BASE] = { .doit = inet_rtm_getroute, - .dumpit = inet_dump_fib, }, -#ifdef CONFIG_IP_MULTIPLE_TABLES - [RTM_GETRULE - RTM_BASE] = { .dumpit = fib4_rules_dump, }, -#endif -}; - #ifdef CONFIG_SYSCTL void inet_forward_change(void) @@ -1636,7 +1614,10 @@ void __init devinet_init(void) { register_gifconf(PF_INET, inet_gifconf); register_netdevice_notifier(&ip_netdev_notifier); - rtnetlink_links[PF_INET] = inet_rtnetlink_table; + + rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); + rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); + rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); #ifdef CONFIG_SYSCTL devinet_sysctl.sysctl_header = register_sysctl_table(devinet_sysctl.devinet_root_dir); diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 3104112..47c95e8 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -21,13 +21,14 @@ static int esp_output(struct xfrm_state struct blkcipher_desc desc; struct esp_data *esp; struct sk_buff *trailer; + u8 *tail; int blksize; int clen; int alen; int nfrags; /* Strip IP+ESP header. */ - __skb_pull(skb, skb->h.raw - skb->data); + __skb_pull(skb, skb_transport_offset(skb)); /* Now skb is pure payload to encrypt */ err = -ENOMEM; @@ -49,19 +50,21 @@ static int esp_output(struct xfrm_state goto error; /* Fill padding... */ + tail = skb_tail_pointer(trailer); do { int i; for (i=0; ilen - 2; i++) - *(u8*)(trailer->tail + i) = i+1; + tail[i] = i + 1; } while (0); - *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2; + tail[clen - skb->len - 2] = (clen - skb->len) - 2; pskb_put(skb, trailer, clen - skb->len); - __skb_push(skb, skb->data - skb->nh.raw); - top_iph = skb->nh.iph; - esph = (struct ip_esp_hdr *)(skb->nh.raw + top_iph->ihl*4); + __skb_push(skb, skb->data - skb_network_header(skb)); + top_iph = ip_hdr(skb); + esph = (struct ip_esp_hdr *)(skb_network_header(skb) + + top_iph->ihl * 4); top_iph->tot_len = htons(skb->len + alen); - *(u8*)(trailer->tail - 1) = top_iph->protocol; + *(skb_tail_pointer(trailer) - 1) = top_iph->protocol; /* this is non-NULL only with UDP Encapsulation */ if (x->encap) { @@ -217,12 +220,12 @@ static int esp_input(struct xfrm_state * /* ... check padding bits here. Silly. :-) */ - iph = skb->nh.iph; + iph = ip_hdr(skb); ihl = iph->ihl * 4; if (x->encap) { struct xfrm_encap_tmpl *encap = x->encap; - struct udphdr *uh = (void *)(skb->nh.raw + ihl); + struct udphdr *uh = (void *)(skb_network_header(skb) + ihl); /* * 1) if the NAT-T peer's IP or port changed then @@ -260,7 +263,8 @@ static int esp_input(struct xfrm_state * iph->protocol = nexthdr[1]; pskb_trim(skb, skb->len - alen - padlen - 2); - skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl; + __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen); + skb_set_transport_header(skb, -ihl); return 0; @@ -268,32 +272,33 @@ out: return -EINVAL; } -static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) +static u32 esp4_get_mtu(struct xfrm_state *x, int mtu) { struct esp_data *esp = x->data; u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); - int enclen = 0; + u32 align = max_t(u32, blksize, esp->conf.padlen); + u32 rem; + + mtu -= x->props.header_len + esp->auth.icv_trunc_len; + rem = mtu & (align - 1); + mtu &= ~(align - 1); switch (x->props.mode) { case XFRM_MODE_TUNNEL: - mtu = ALIGN(mtu +2, blksize); break; default: case XFRM_MODE_TRANSPORT: /* The worst case */ - mtu = ALIGN(mtu + 2, 4) + blksize - 4; + mtu -= blksize - 4; + mtu += min_t(u32, blksize - 4, rem); break; case XFRM_MODE_BEET: /* The worst case. */ - enclen = IPV4_BEET_PHMAXLEN; - mtu = ALIGN(mtu + enclen + 2, blksize); + mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem); break; } - if (esp->conf.padlen) - mtu = ALIGN(mtu, esp->conf.padlen); - - return mtu + x->props.header_len + esp->auth.icv_trunc_len - enclen; + return mtu - 2; } static void esp4_err(struct sk_buff *skb, u32 info) @@ -302,8 +307,8 @@ static void esp4_err(struct sk_buff *skb struct ip_esp_hdr *esph = (struct ip_esp_hdr*)(skb->data+(iph->ihl<<2)); struct xfrm_state *x; - if (skb->h.icmph->type != ICMP_DEST_UNREACH || - skb->h.icmph->code != ICMP_FRAG_NEEDED) + if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH || + icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) return; x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET); @@ -336,6 +341,7 @@ static int esp_init_state(struct xfrm_st { struct esp_data *esp = NULL; struct crypto_blkcipher *tfm; + u32 align; /* null auth and encryption can have zero length keys */ if (x->aalg) { @@ -402,6 +408,8 @@ static int esp_init_state(struct xfrm_st x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen; if (x->props.mode == XFRM_MODE_TUNNEL) x->props.header_len += sizeof(struct iphdr); + else if (x->props.mode == XFRM_MODE_BEET) + x->props.header_len += IPV4_BEET_PHMAXLEN; if (x->encap) { struct xfrm_encap_tmpl *encap = x->encap; @@ -417,7 +425,10 @@ static int esp_init_state(struct xfrm_st } } x->data = esp; - x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len; + align = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); + if (esp->conf.padlen) + align = max_t(u32, align, esp->conf.padlen); + x->props.trailer_len = align + 1 + esp->auth.icv_trunc_len; return 0; error: @@ -434,7 +445,7 @@ static struct xfrm_type esp_type = .proto = IPPROTO_ESP, .init_state = esp_init_state, .destructor = esp_destroy, - .get_max_size = esp4_get_max_size, + .get_mtu = esp4_get_mtu, .input = esp_input, .output = esp_output }; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index cac06c4..837f295 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -34,7 +34,6 @@ #include #include #include #include -#include #include #include @@ -46,6 +45,7 @@ #include #include #include #include +#include #define FFprint(a...) printk(KERN_DEBUG a) @@ -540,7 +540,7 @@ errout: return err; } -int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) +static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct fib_config cfg; struct fib_table *tb; @@ -561,7 +561,7 @@ errout: return err; } -int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) +static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct fib_config cfg; struct fib_table *tb; @@ -582,7 +582,7 @@ errout: return err; } -int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) +static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) { unsigned int h, s_h; unsigned int e = 0, s_e; @@ -777,6 +777,10 @@ static void nl_fib_lookup(struct fib_res .tos = frn->fl_tos, .scope = frn->fl_scope } } }; +#ifdef CONFIG_IP_MULTIPLE_TABLES + res.r = NULL; +#endif + frn->err = -ENOENT; if (tb) { local_bh_disable(); @@ -807,7 +811,7 @@ static void nl_fib_input(struct sock *sk if (skb == NULL) return; - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len || nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*frn))) { kfree_skb(skb); @@ -827,7 +831,8 @@ static void nl_fib_input(struct sock *sk static void nl_fib_lookup_init(void) { - netlink_kernel_create(NETLINK_FIB_LOOKUP, 0, nl_fib_input, THIS_MODULE); + netlink_kernel_create(NETLINK_FIB_LOOKUP, 0, nl_fib_input, NULL, + THIS_MODULE); } static void fib_disable_ip(struct net_device *dev, int force) @@ -925,6 +930,10 @@ #endif register_netdevice_notifier(&fib_netdev_notifier); register_inetaddr_notifier(&fib_inetaddr_notifier); nl_fib_lookup_init(); + + rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL); + rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL); + rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib); } EXPORT_SYMBOL(inet_addr_type); diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index a4949f9..9cfecf1 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -1027,7 +1027,7 @@ out: return 0; } -static struct seq_operations fib_seq_ops = { +static const struct seq_operations fib_seq_ops = { .start = fib_seq_start, .next = fib_seq_next, .stop = fib_seq_stop, diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index c660c07..33083ad 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -274,11 +274,6 @@ nla_put_failure: return -ENOBUFS; } -int fib4_rules_dump(struct sk_buff *skb, struct netlink_callback *cb) -{ - return fib_rules_dump(skb, cb, AF_INET); -} - static u32 fib4_rule_default_pref(void) { struct list_head *pos; @@ -303,6 +298,11 @@ static size_t fib4_rule_nlmsg_payload(st + nla_total_size(4); /* flow */ } +static void fib4_rule_flush_cache(void) +{ + rt_cache_flush(-1); +} + static struct fib_rules_ops fib4_rules_ops = { .family = AF_INET, .rule_size = sizeof(struct fib4_rule), @@ -314,6 +314,7 @@ static struct fib_rules_ops fib4_rules_o .fill = fib4_rule_fill, .default_pref = fib4_rule_default_pref, .nlmsg_payload = fib4_rule_nlmsg_payload, + .flush_cache = fib4_rule_flush_cache, .nlgroup = RTNLGRP_IPV4_RULE, .policy = fib4_rule_policy, .rules_list = &fib4_rules, diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 3dad12e..406ea70 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -927,7 +927,7 @@ #endif default: printk(KERN_DEBUG "impossible 102\n"); return -EINVAL; - }; + } } return err; } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 214c347..9be7da7 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -50,7 +50,7 @@ * Patrick McHardy */ -#define VERSION "0.407" +#define VERSION "0.408" #include #include @@ -292,8 +292,8 @@ static inline void check_tnode(const str static int halve_threshold = 25; static int inflate_threshold = 50; -static int halve_threshold_root = 15; -static int inflate_threshold_root = 25; +static int halve_threshold_root = 8; +static int inflate_threshold_root = 15; static void __alias_free_mem(struct rcu_head *head) @@ -350,11 +350,10 @@ static void __tnode_free_rcu(struct rcu_ static inline void tnode_free(struct tnode *tn) { - if(IS_LEAF(tn)) { + if (IS_LEAF(tn)) { struct leaf *l = (struct leaf *) tn; call_rcu_bh(&l->rcu, __leaf_free_rcu); - } - else + } else call_rcu(&tn->rcu, __tnode_free_rcu); } @@ -459,6 +458,7 @@ static struct node *resize(struct trie * struct tnode *old_tn; int inflate_threshold_use; int halve_threshold_use; + int max_resize; if (!tn) return NULL; @@ -553,13 +553,14 @@ static struct node *resize(struct trie * /* Keep root node larger */ - if(!tn->parent) + if (!tn->parent) inflate_threshold_use = inflate_threshold_root; else inflate_threshold_use = inflate_threshold; err = 0; - while ((tn->full_children > 0 && + max_resize = 10; + while ((tn->full_children > 0 && max_resize-- && 50 * (tn->full_children + tnode_child_length(tn) - tn->empty_children) >= inflate_threshold_use * tnode_child_length(tn))) { @@ -574,6 +575,15 @@ #endif } } + if (max_resize < 0) { + if (!tn->parent) + printk(KERN_WARNING "Fix inflate_threshold_root. Now=%d size=%d bits\n", + inflate_threshold_root, tn->bits); + else + printk(KERN_WARNING "Fix inflate_threshold. Now=%d size=%d bits\n", + inflate_threshold, tn->bits); + } + check_tnode(tn); /* @@ -584,13 +594,14 @@ #endif /* Keep root node larger */ - if(!tn->parent) + if (!tn->parent) halve_threshold_use = halve_threshold_root; else halve_threshold_use = halve_threshold; err = 0; - while (tn->bits > 1 && + max_resize = 10; + while (tn->bits > 1 && max_resize-- && 100 * (tnode_child_length(tn) - tn->empty_children) < halve_threshold_use * tnode_child_length(tn)) { @@ -605,6 +616,14 @@ #endif } } + if (max_resize < 0) { + if (!tn->parent) + printk(KERN_WARNING "Fix halve_threshold_root. Now=%d size=%d bits\n", + halve_threshold_root, tn->bits); + else + printk(KERN_WARNING "Fix halve_threshold. Now=%d size=%d bits\n", + halve_threshold, tn->bits); + } /* Only one child remains */ if (tn->empty_children == tnode_child_length(tn) - 1) @@ -2039,12 +2058,12 @@ static struct node *fib_trie_get_first(s { struct node *n ; - if(!t) + if (!t) return NULL; n = rcu_dereference(t->trie); - if(!iter) + if (!iter) return NULL; if (n) { @@ -2084,7 +2103,7 @@ static void trie_collect_stats(struct tr int i; s->tnodes++; - if(tn->bits < MAX_STAT_DEPTH) + if (tn->bits < MAX_STAT_DEPTH) s->nodesizes[tn->bits]++; for (i = 0; i < (1<bits); i++) @@ -2250,7 +2269,7 @@ static inline const char *rtn_scope(enum { static char buf[32]; - switch(s) { + switch (s) { case RT_SCOPE_UNIVERSE: return "universe"; case RT_SCOPE_SITE: return "site"; case RT_SCOPE_LINK: return "link"; @@ -2340,7 +2359,7 @@ static int fib_trie_seq_show(struct seq_ return 0; } -static struct seq_operations fib_trie_seq_ops = { +static const struct seq_operations fib_trie_seq_ops = { .start = fib_trie_seq_start, .next = fib_trie_seq_next, .stop = fib_trie_seq_stop, @@ -2461,7 +2480,7 @@ static int fib_route_seq_show(struct seq return 0; } -static struct seq_operations fib_route_seq_ops = { +static const struct seq_operations fib_route_seq_ops = { .start = fib_trie_seq_start, .next = fib_trie_seq_next, .stop = fib_trie_seq_stop, diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 4b7a0d9..d38cbba 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -355,7 +355,7 @@ static void icmp_push_reply(struct icmp_ ipc, rt, MSG_DONTWAIT) < 0) ip_flush_pending_frames(icmp_socket->sk); else if ((skb = skb_peek(&icmp_socket->sk->sk_write_queue)) != NULL) { - struct icmphdr *icmph = skb->h.icmph; + struct icmphdr *icmph = icmp_hdr(skb); __wsum csum = 0; struct sk_buff *skb1; @@ -392,7 +392,7 @@ static void icmp_reply(struct icmp_bxm * icmp_param->data.icmph.checksum = 0; icmp_out_count(icmp_param->data.icmph.type); - inet->tos = skb->nh.iph->tos; + inet->tos = ip_hdr(skb)->tos; daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; if (icmp_param->replyopts.optlen) { @@ -404,7 +404,7 @@ static void icmp_reply(struct icmp_bxm * struct flowi fl = { .nl_u = { .ip4_u = { .daddr = daddr, .saddr = rt->rt_spec_dst, - .tos = RT_TOS(skb->nh.iph->tos) } }, + .tos = RT_TOS(ip_hdr(skb)->tos) } }, .proto = IPPROTO_ICMP }; security_skb_classify_flow(skb, &fl); if (ip_route_output_key(&rt, &fl)) @@ -448,9 +448,10 @@ void icmp_send(struct sk_buff *skb_in, i * Check this, icmp_send is called from the most obscure devices * sometimes. */ - iph = skb_in->nh.iph; + iph = ip_hdr(skb_in); - if ((u8 *)iph < skb_in->head || (u8 *)(iph + 1) > skb_in->tail) + if ((u8 *)iph < skb_in->head || + (skb_in->network_header + sizeof(*iph)) > skb_in->tail) goto out; /* @@ -484,7 +485,7 @@ void icmp_send(struct sk_buff *skb_in, i u8 _inner_type, *itp; itp = skb_header_pointer(skb_in, - skb_in->nh.raw + + skb_network_header(skb_in) + (iph->ihl << 2) + offsetof(struct icmphdr, type) - @@ -536,7 +537,7 @@ void icmp_send(struct sk_buff *skb_in, i icmp_param.data.icmph.un.gateway = info; icmp_param.data.icmph.checksum = 0; icmp_param.skb = skb_in; - icmp_param.offset = skb_in->nh.raw - skb_in->data; + icmp_param.offset = skb_network_offset(skb_in); icmp_out_count(icmp_param.data.icmph.type); inet_sk(icmp_socket->sk)->tos = tos; ipc.addr = iph->saddr; @@ -613,7 +614,7 @@ static void icmp_unreach(struct sk_buff if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto out_err; - icmph = skb->h.icmph; + icmph = icmp_hdr(skb); iph = (struct iphdr *)skb->data; if (iph->ihl < 5) /* Mangled header, drop. */ @@ -676,7 +677,7 @@ static void icmp_unreach(struct sk_buff printk(KERN_WARNING "%u.%u.%u.%u sent an invalid ICMP " "type %u, code %u " "error to a broadcast: %u.%u.%u.%u on %s\n", - NIPQUAD(skb->nh.iph->saddr), + NIPQUAD(ip_hdr(skb)->saddr), icmph->type, icmph->code, NIPQUAD(iph->daddr), skb->dev->name); @@ -743,7 +744,7 @@ static void icmp_redirect(struct sk_buff iph = (struct iphdr *)skb->data; - switch (skb->h.icmph->code & 7) { + switch (icmp_hdr(skb)->code & 7) { case ICMP_REDIR_NET: case ICMP_REDIR_NETTOS: /* @@ -751,8 +752,8 @@ static void icmp_redirect(struct sk_buff */ case ICMP_REDIR_HOST: case ICMP_REDIR_HOSTTOS: - ip_rt_redirect(skb->nh.iph->saddr, iph->daddr, - skb->h.icmph->un.gateway, + ip_rt_redirect(ip_hdr(skb)->saddr, iph->daddr, + icmp_hdr(skb)->un.gateway, iph->saddr, skb->dev); break; } @@ -780,7 +781,7 @@ static void icmp_echo(struct sk_buff *sk if (!sysctl_icmp_echo_ignore_all) { struct icmp_bxm icmp_param; - icmp_param.data.icmph = *skb->h.icmph; + icmp_param.data.icmph = *icmp_hdr(skb); icmp_param.data.icmph.type = ICMP_ECHOREPLY; icmp_param.skb = skb; icmp_param.offset = 0; @@ -816,7 +817,7 @@ static void icmp_timestamp(struct sk_buf icmp_param.data.times[2] = icmp_param.data.times[1]; if (skb_copy_bits(skb, 0, &icmp_param.data.times[0], 4)) BUG(); - icmp_param.data.icmph = *skb->h.icmph; + icmp_param.data.icmph = *icmp_hdr(skb); icmp_param.data.icmph.type = ICMP_TIMESTAMPREPLY; icmp_param.data.icmph.code = 0; icmp_param.skb = skb; @@ -943,7 +944,7 @@ int icmp_rcv(struct sk_buff *skb) if (!pskb_pull(skb, sizeof(struct icmphdr))) goto error; - icmph = skb->h.icmph; + icmph = icmp_hdr(skb); /* * 18 is the highest 'known' ICMP type. Anything else is a mystery diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 8cedb2a..2506021 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -314,7 +314,9 @@ static struct sk_buff *igmpv3_newpack(st skb_reserve(skb, LL_RESERVED_SPACE(dev)); - skb->nh.iph = pip =(struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4); + skb_reset_network_header(skb); + pip = ip_hdr(skb); + skb_put(skb, sizeof(struct iphdr) + 4); pip->version = 4; pip->ihl = (sizeof(struct iphdr)+4)>>2; @@ -331,8 +333,9 @@ static struct sk_buff *igmpv3_newpack(st ((u8*)&pip[1])[2] = 0; ((u8*)&pip[1])[3] = 0; - pig =(struct igmpv3_report *)skb_put(skb, sizeof(*pig)); - skb->h.igmph = (struct igmphdr *)pig; + skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4; + skb_put(skb, sizeof(*pig)); + pig = igmpv3_report_hdr(skb); pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT; pig->resv1 = 0; pig->csum = 0; @@ -343,16 +346,14 @@ static struct sk_buff *igmpv3_newpack(st static int igmpv3_sendpack(struct sk_buff *skb) { - struct iphdr *pip = skb->nh.iph; - struct igmphdr *pig = skb->h.igmph; - int iplen, igmplen; + struct iphdr *pip = ip_hdr(skb); + struct igmphdr *pig = igmp_hdr(skb); + const int iplen = skb->tail - skb->network_header; + const int igmplen = skb->tail - skb->transport_header; - iplen = skb->tail - (unsigned char *)skb->nh.iph; pip->tot_len = htons(iplen); ip_send_check(pip); - - igmplen = skb->tail - (unsigned char *)skb->h.igmph; - pig->csum = ip_compute_csum((void *)skb->h.igmph, igmplen); + pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev, dst_output); @@ -379,7 +380,7 @@ static struct sk_buff *add_grhead(struct pgr->grec_auxwords = 0; pgr->grec_nsrcs = 0; pgr->grec_mca = pmc->multiaddr; - pih = (struct igmpv3_report *)skb->h.igmph; + pih = igmpv3_report_hdr(skb); pih->ngrec = htons(ntohs(pih->ngrec)+1); *ppgr = pgr; return skb; @@ -412,7 +413,7 @@ static struct sk_buff *add_grec(struct s if (!*psf_list) goto empty_source; - pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL; + pih = skb ? igmpv3_report_hdr(skb) : NULL; /* EX and TO_EX get a fresh packet, if needed */ if (truncate) { @@ -664,7 +665,9 @@ static int igmp_send_report(struct in_de skb_reserve(skb, LL_RESERVED_SPACE(dev)); - skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4); + skb_reset_network_header(skb); + iph = ip_hdr(skb); + skb_put(skb, sizeof(struct iphdr) + 4); iph->version = 4; iph->ihl = (sizeof(struct iphdr)+4)>>2; @@ -827,8 +830,8 @@ static void igmp_heard_report(struct in_ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, int len) { - struct igmphdr *ih = skb->h.igmph; - struct igmpv3_query *ih3 = (struct igmpv3_query *)ih; + struct igmphdr *ih = igmp_hdr(skb); + struct igmpv3_query *ih3 = igmpv3_query_hdr(skb); struct ip_mc_list *im; __be32 group = ih->group; int max_delay; @@ -861,12 +864,12 @@ static void igmp_heard_query(struct in_d if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) return; - ih3 = (struct igmpv3_query *) skb->h.raw; + ih3 = igmpv3_query_hdr(skb); if (ih3->nsrcs) { if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) + ntohs(ih3->nsrcs)*sizeof(__be32))) return; - ih3 = (struct igmpv3_query *) skb->h.raw; + ih3 = igmpv3_query_hdr(skb); } max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); @@ -943,7 +946,7 @@ int igmp_rcv(struct sk_buff *skb) goto drop; } - ih = skb->h.igmph; + ih = igmp_hdr(skb); switch (ih->type) { case IGMP_HOST_MEMBERSHIP_QUERY: igmp_heard_query(in_dev, skb, len); @@ -2397,7 +2400,7 @@ #endif return 0; } -static struct seq_operations igmp_mc_seq_ops = { +static const struct seq_operations igmp_mc_seq_ops = { .start = igmp_mc_seq_start, .next = igmp_mc_seq_next, .stop = igmp_mc_seq_stop, @@ -2571,7 +2574,7 @@ static int igmp_mcf_seq_show(struct seq_ return 0; } -static struct seq_operations igmp_mcf_seq_ops = { +static const struct seq_operations igmp_mcf_seq_ops = { .start = igmp_mcf_seq_start, .next = igmp_mcf_seq_next, .stop = igmp_mcf_seq_stop, diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 5df71cd..dbeacd8 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -27,6 +27,7 @@ #include #include #include #include +#include #include #include @@ -60,7 +61,7 @@ static int inet_csk_diag_fill(struct soc struct nlmsghdr *nlh; void *info = NULL; struct inet_diag_meminfo *minfo = NULL; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); const struct inet_diag_handler *handler; handler = inet_diag_table[unlh->nlmsg_type]; @@ -147,12 +148,12 @@ #undef EXPIRES_IN_MS icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) icsk->icsk_ca_ops->get_info(sk, ext, skb); - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -EMSGSIZE; } @@ -163,7 +164,7 @@ static int inet_twsk_diag_fill(struct in { long tmo; struct inet_diag_msg *r; - const unsigned char *previous_tail = skb->tail; + const unsigned char *previous_tail = skb_tail_pointer(skb); struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); @@ -205,10 +206,10 @@ #if defined(CONFIG_IPV6) || defined (CON &tw6->tw_v6_daddr); } #endif - nlh->nlmsg_len = skb->tail - previous_tail; + nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail; return skb->len; nlmsg_failure: - skb_trim(skb, previous_tail - skb->data); + nlmsg_trim(skb, previous_tail); return -EMSGSIZE; } @@ -535,7 +536,7 @@ static int inet_diag_fill_req(struct sk_ { const struct inet_request_sock *ireq = inet_rsk(req); struct inet_sock *inet = inet_sk(sk); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct inet_diag_msg *r; struct nlmsghdr *nlh; long tmo; @@ -574,12 +575,12 @@ #if defined(CONFIG_IPV6) || defined (CON &inet6_rsk(req)->rmt_addr); } #endif - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -805,68 +806,43 @@ done: return skb->len; } -static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { - if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) - return 0; + int hdrlen = sizeof(struct inet_diag_req); - if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) - goto err_inval; + if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || + nlmsg_len(nlh) < hdrlen) + return -EINVAL; if (inet_diag_table[nlh->nlmsg_type] == NULL) return -ENOENT; - if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len) - goto err_inval; - - if (nlh->nlmsg_flags&NLM_F_DUMP) { - if (nlh->nlmsg_len > - (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) { - struct rtattr *rta = (void *)(NLMSG_DATA(nlh) + - sizeof(struct inet_diag_req)); - if (rta->rta_type != INET_DIAG_REQ_BYTECODE || - rta->rta_len < 8 || - rta->rta_len > - (nlh->nlmsg_len - - NLMSG_SPACE(sizeof(struct inet_diag_req)))) - goto err_inval; - if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta))) - goto err_inval; + if (nlh->nlmsg_flags & NLM_F_DUMP) { + if (nlmsg_attrlen(nlh, hdrlen)) { + struct nlattr *attr; + + attr = nlmsg_find_attr(nlh, hdrlen, + INET_DIAG_REQ_BYTECODE); + if (attr == NULL || + nla_len(attr) < sizeof(struct inet_diag_bc_op) || + inet_diag_bc_audit(nla_data(attr), nla_len(attr))) + return -EINVAL; } + return netlink_dump_start(idiagnl, skb, nlh, inet_diag_dump, NULL); - } else - return inet_diag_get_exact(skb, nlh); - -err_inval: - return -EINVAL; -} - - -static inline void inet_diag_rcv_skb(struct sk_buff *skb) -{ - if (skb->len >= NLMSG_SPACE(0)) { - int err; - struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; - - if (nlh->nlmsg_len < sizeof(*nlh) || - skb->len < nlh->nlmsg_len) - return; - err = inet_diag_rcv_msg(skb, nlh); - if (err || nlh->nlmsg_flags & NLM_F_ACK) - netlink_ack(skb, nlh, err); } + + return inet_diag_get_exact(skb, nlh); } static void inet_diag_rcv(struct sock *sk, int len) { - struct sk_buff *skb; - unsigned int qlen = skb_queue_len(&sk->sk_receive_queue); + unsigned int qlen = 0; - while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) { - inet_diag_rcv_skb(skb); - kfree_skb(skb); - } + do { + netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg); + } while (qlen); } static DEFINE_SPINLOCK(inet_diag_register_lock); @@ -917,7 +893,7 @@ static int __init inet_diag_init(void) goto out; idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv, - THIS_MODULE); + NULL, THIS_MODULE); if (idiagnl == NULL) goto out_free_table; err = 0; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index db3ef96..2f44e61 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -87,10 +87,12 @@ #define PEER_MAXDEPTH 40 /* sufficient f static int peer_total; /* Exported for sysctl_net_ipv4. */ -int inet_peer_threshold = 65536 + 128; /* start to throw entries more +int inet_peer_threshold __read_mostly = 65536 + 128; /* start to throw entries more * aggressively at this stage */ -int inet_peer_minttl = 120 * HZ; /* TTL under high load: 120 sec */ -int inet_peer_maxttl = 10 * 60 * HZ; /* usual time to live: 10 min */ +int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */ +int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */ +int inet_peer_gc_mintime __read_mostly = 10 * HZ; +int inet_peer_gc_maxtime __read_mostly = 120 * HZ; static struct inet_peer *inet_peer_unused_head; static struct inet_peer **inet_peer_unused_tailp = &inet_peer_unused_head; @@ -99,9 +101,6 @@ static DEFINE_SPINLOCK(inet_peer_unused_ static void peer_check_expire(unsigned long dummy); static DEFINE_TIMER(peer_periodic_timer, peer_check_expire, 0, 0); -/* Exported for sysctl_net_ipv4. */ -int inet_peer_gc_mintime = 10 * HZ, - inet_peer_gc_maxtime = 120 * HZ; /* Called from ip_output.c:ip_init */ void __init inet_initpeers(void) @@ -151,20 +150,27 @@ static void unlink_from_unused(struct in spin_unlock_bh(&inet_peer_unused_lock); } -/* Called with local BH disabled and the pool lock held. */ -#define lookup(daddr) \ +/* + * Called with local BH disabled and the pool lock held. + * _stack is known to be NULL or not at compile time, + * so compiler will optimize the if (_stack) tests. + */ +#define lookup(_daddr,_stack) \ ({ \ struct inet_peer *u, **v; \ - stackptr = stack; \ - *stackptr++ = &peer_root; \ + if (_stack) { \ + stackptr = _stack; \ + *stackptr++ = &peer_root; \ + } \ for (u = peer_root; u != peer_avl_empty; ) { \ - if (daddr == u->v4daddr) \ + if (_daddr == u->v4daddr) \ break; \ - if ((__force __u32)daddr < (__force __u32)u->v4daddr) \ + if ((__force __u32)_daddr < (__force __u32)u->v4daddr) \ v = &u->avl_left; \ else \ v = &u->avl_right; \ - *stackptr++ = v; \ + if (_stack) \ + *stackptr++ = v; \ u = *v; \ } \ u; \ @@ -288,7 +294,7 @@ static void unlink_from_pool(struct inet if (atomic_read(&p->refcnt) == 1) { struct inet_peer **stack[PEER_MAXDEPTH]; struct inet_peer ***stackptr, ***delp; - if (lookup(p->v4daddr) != p) + if (lookup(p->v4daddr, stack) != p) BUG(); delp = stackptr - 1; /* *delp[0] == p */ if (p->avl_left == peer_avl_empty) { @@ -373,7 +379,7 @@ struct inet_peer *inet_getpeer(__be32 da /* Look up for the address quickly. */ read_lock_bh(&peer_pool_lock); - p = lookup(daddr); + p = lookup(daddr, NULL); if (p != peer_avl_empty) atomic_inc(&p->refcnt); read_unlock_bh(&peer_pool_lock); @@ -400,7 +406,7 @@ struct inet_peer *inet_getpeer(__be32 da write_lock_bh(&peer_pool_lock); /* Check if an entry has suddenly appeared. */ - p = lookup(daddr); + p = lookup(daddr, stack); if (p != peer_avl_empty) goto out_free; diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 369e721..9cb04df 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -67,14 +67,14 @@ int ip_forward(struct sk_buff *skb) if (skb->pkt_type != PACKET_HOST) goto drop; - skb->ip_summed = CHECKSUM_NONE; + skb_forward_csum(skb); /* * According to the RFC, we must first decrease the TTL field. If * that reaches zero, we must reply an ICMP control message telling * that the packet's lifetime expired. */ - if (skb->nh.iph->ttl <= 1) + if (ip_hdr(skb)->ttl <= 1) goto too_many_hops; if (!xfrm4_route_forward(skb)) @@ -85,10 +85,18 @@ int ip_forward(struct sk_buff *skb) if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway) goto sr_failed; + if (unlikely(skb->len > dst_mtu(&rt->u.dst) && + (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) { + IP_INC_STATS(IPSTATS_MIB_FRAGFAILS); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(dst_mtu(&rt->u.dst))); + goto drop; + } + /* We are about to mangle packet. Copy it! */ if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len)) goto drop; - iph = skb->nh.iph; + iph = ip_hdr(skb); /* Decrease ttl after skb cow done */ ip_decrease_ttl(iph); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index b6f0553..0231bdc 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -92,7 +92,7 @@ #define LAST_IN 1 spinlock_t lock; atomic_t refcnt; struct timer_list timer; /* when will this queue expire? */ - struct timeval stamp; + ktime_t stamp; int iif; unsigned int rid; struct inet_peer *peer; @@ -184,7 +184,7 @@ static __inline__ struct ipq *frag_alloc { struct ipq *qp = kmalloc(sizeof(struct ipq), GFP_ATOMIC); - if(!qp) + if (!qp) return NULL; atomic_add(sizeof(struct ipq), &ip_frag_mem); return qp; @@ -321,11 +321,11 @@ #ifdef CONFIG_SMP * promoted read lock to write lock. */ hlist_for_each_entry(qp, n, &ipq_hash[hash], list) { - if(qp->id == qp_in->id && - qp->saddr == qp_in->saddr && - qp->daddr == qp_in->daddr && - qp->protocol == qp_in->protocol && - qp->user == qp_in->user) { + if (qp->id == qp_in->id && + qp->saddr == qp_in->saddr && + qp->daddr == qp_in->daddr && + qp->protocol == qp_in->protocol && + qp->user == qp_in->user) { atomic_inc(&qp->refcnt); write_unlock(&ipfrag_lock); qp_in->last_in |= COMPLETE; @@ -398,11 +398,11 @@ static inline struct ipq *ip_find(struct read_lock(&ipfrag_lock); hash = ipqhashfn(id, saddr, daddr, protocol); hlist_for_each_entry(qp, n, &ipq_hash[hash], list) { - if(qp->id == id && - qp->saddr == saddr && - qp->daddr == daddr && - qp->protocol == protocol && - qp->user == user) { + if (qp->id == id && + qp->saddr == saddr && + qp->daddr == daddr && + qp->protocol == protocol && + qp->user == user) { atomic_inc(&qp->refcnt); read_unlock(&ipfrag_lock); return qp; @@ -479,11 +479,11 @@ static void ip_frag_queue(struct ipq *qp goto err; } - offset = ntohs(skb->nh.iph->frag_off); + offset = ntohs(ip_hdr(skb)->frag_off); flags = offset & ~IP_OFFSET; offset &= IP_OFFSET; offset <<= 3; /* offset is in 8-byte chunks */ - ihl = skb->nh.iph->ihl * 4; + ihl = ip_hdrlen(skb); /* Determine the position of this fragment. */ end = offset + skb->len - ihl; @@ -524,7 +524,7 @@ static void ip_frag_queue(struct ipq *qp * this fragment, right? */ prev = NULL; - for(next = qp->fragments; next != NULL; next = next->next) { + for (next = qp->fragments; next != NULL; next = next->next) { if (FRAG_CB(next)->offset >= offset) break; /* bingo! */ prev = next; @@ -592,7 +592,7 @@ static void ip_frag_queue(struct ipq *qp if (skb->dev) qp->iif = skb->dev->ifindex; skb->dev = NULL; - skb_get_timestamp(skb, &qp->stamp); + qp->stamp = skb->tstamp; qp->meat += skb->len; atomic_add(skb->truesize, &ip_frag_mem); if (offset == 0) @@ -624,10 +624,10 @@ static struct sk_buff *ip_frag_reasm(str BUG_TRAP(FRAG_CB(head)->offset == 0); /* Allocate a new buffer for the datagram. */ - ihlen = head->nh.iph->ihl*4; + ihlen = ip_hdrlen(head); len = ihlen + qp->len; - if(len > 65535) + if (len > 65535) goto out_oversize; /* Head of list must not be cloned. */ @@ -658,7 +658,7 @@ static struct sk_buff *ip_frag_reasm(str } skb_shinfo(head)->frag_list = head->next; - skb_push(head, head->data - head->nh.raw); + skb_push(head, head->data - skb_network_header(head)); atomic_sub(head->truesize, &ip_frag_mem); for (fp=head->next; fp; fp = fp->next) { @@ -674,9 +674,9 @@ static struct sk_buff *ip_frag_reasm(str head->next = NULL; head->dev = dev; - skb_set_timestamp(head, &qp->stamp); + head->tstamp = qp->stamp; - iph = head->nh.iph; + iph = ip_hdr(head); iph->frag_off = 0; iph->tot_len = htons(len); IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS); @@ -700,7 +700,6 @@ out_fail: /* Process an incoming IP datagram fragment. */ struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user) { - struct iphdr *iph = skb->nh.iph; struct ipq *qp; struct net_device *dev; @@ -713,7 +712,7 @@ struct sk_buff *ip_defrag(struct sk_buff dev = skb->dev; /* Lookup (or create) queue header */ - if ((qp = ip_find(iph, user)) != NULL) { + if ((qp = ip_find(ip_hdr(skb), user)) != NULL) { struct sk_buff *ret = NULL; spin_lock(&qp->lock); @@ -734,7 +733,7 @@ struct sk_buff *ip_defrag(struct sk_buff return NULL; } -void ipfrag_init(void) +void __init ipfrag_init(void) { ipfrag_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ (jiffies ^ (jiffies >> 6))); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 9151da6..6328293 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -191,11 +191,11 @@ static struct ip_tunnel * ipgre_tunnel_l return NULL; } -static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t) +static struct ip_tunnel **__ipgre_bucket(struct ip_tunnel_parm *parms) { - __be32 remote = t->parms.iph.daddr; - __be32 local = t->parms.iph.saddr; - __be32 key = t->parms.i_key; + __be32 remote = parms->iph.daddr; + __be32 local = parms->iph.saddr; + __be32 key = parms->i_key; unsigned h = HASH(key); int prio = 0; @@ -209,6 +209,11 @@ static struct ip_tunnel **ipgre_bucket(s return &tunnels[prio][h]; } +static inline struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t) +{ + return __ipgre_bucket(&t->parms); +} + static void ipgre_tunnel_link(struct ip_tunnel *t) { struct ip_tunnel **tp = ipgre_bucket(t); @@ -240,17 +245,9 @@ static struct ip_tunnel * ipgre_tunnel_l __be32 key = parms->i_key; struct ip_tunnel *t, **tp, *nt; struct net_device *dev; - unsigned h = HASH(key); - int prio = 0; char name[IFNAMSIZ]; - if (local) - prio |= 1; - if (remote && !MULTICAST(remote)) { - prio |= 2; - h ^= HASH(remote); - } - for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) { + for (tp = __ipgre_bucket(parms); (t = *tp) != NULL; tp = &t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) { if (key == t->parms.i_key) return t; @@ -320,8 +317,8 @@ #ifndef I_WISH_WORLD_WERE_PERFECT struct iphdr *iph = (struct iphdr*)skb->data; __be16 *p = (__be16*)(skb->data+(iph->ihl<<2)); int grehlen = (iph->ihl<<2) + 4; - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; struct ip_tunnel *t; __be16 flags; @@ -388,8 +385,8 @@ #else struct iphdr *iph = (struct iphdr*)dp; struct iphdr *eiph; __be16 *p = (__be16*)(dp+(iph->ihl<<2)); - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; int rel_type = 0; int rel_code = 0; __be32 rel_info = 0; @@ -422,7 +419,7 @@ #else default: return; case ICMP_PARAMETERPROB: - n = ntohl(skb->h.icmph->un.gateway) >> 24; + n = ntohl(icmp_hdr(skb)->un.gateway) >> 24; if (n < (iph->ihl<<2)) return; @@ -442,7 +439,7 @@ #else return; case ICMP_FRAG_NEEDED: /* And it is the only really necessary thing :-) */ - n = ntohs(skb->h.icmph->un.frag.mtu); + n = ntohs(icmp_hdr(skb)->un.frag.mtu); if (n < grehlen+68) return; n -= grehlen; @@ -474,7 +471,7 @@ #else dst_release(skb2->dst); skb2->dst = NULL; skb_pull(skb2, skb->data - (u8*)eiph); - skb2->nh.raw = skb2->data; + skb_reset_network_header(skb2); /* Try to guess incoming interface */ memset(&fl, 0, sizeof(fl)); @@ -533,9 +530,9 @@ static inline void ipgre_ecn_decapsulate { if (INET_ECN_is_ce(iph->tos)) { if (skb->protocol == htons(ETH_P_IP)) { - IP_ECN_set_ce(skb->nh.iph); + IP_ECN_set_ce(ip_hdr(skb)); } else if (skb->protocol == htons(ETH_P_IPV6)) { - IP6_ECN_set_ce(skb->nh.ipv6h); + IP6_ECN_set_ce(ipv6_hdr(skb)); } } } @@ -565,7 +562,7 @@ static int ipgre_rcv(struct sk_buff *skb if (!pskb_may_pull(skb, 16)) goto drop_nolock; - iph = skb->nh.iph; + iph = ip_hdr(skb); h = skb->data; flags = *(__be16*)h; @@ -616,9 +613,10 @@ static int ipgre_rcv(struct sk_buff *skb offset += 4; } - skb->mac.raw = skb->nh.raw; - skb->nh.raw = __pskb_pull(skb, offset); - skb_postpull_rcsum(skb, skb->h.raw, offset); + skb_reset_mac_header(skb); + __pskb_pull(skb, offset); + skb_reset_network_header(skb); + skb_postpull_rcsum(skb, skb_transport_header(skb), offset); skb->pkt_type = PACKET_HOST; #ifdef CONFIG_NET_IPGRE_BROADCAST if (MULTICAST(iph->daddr)) { @@ -669,7 +667,7 @@ static int ipgre_tunnel_xmit(struct sk_b { struct ip_tunnel *tunnel = netdev_priv(dev); struct net_device_stats *stats = &tunnel->stat; - struct iphdr *old_iph = skb->nh.iph; + struct iphdr *old_iph = ip_hdr(skb); struct iphdr *tiph; u8 tos; __be16 df; @@ -720,7 +718,7 @@ #ifdef CONFIG_IPV6 addr_type = ipv6_addr_type(addr6); if (addr_type == IPV6_ADDR_ANY) { - addr6 = &skb->nh.ipv6h->daddr; + addr6 = &ipv6_hdr(skb)->daddr; addr_type = ipv6_addr_type(addr6); } @@ -824,11 +822,12 @@ #endif skb_set_owner_w(new_skb, skb->sk); dev_kfree_skb(skb); skb = new_skb; - old_iph = skb->nh.iph; + old_iph = ip_hdr(skb); } - skb->h.raw = skb->nh.raw; - skb->nh.raw = skb_push(skb, gre_hlen); + skb->transport_header = skb->network_header; + skb_push(skb, gre_hlen); + skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); @@ -839,7 +838,7 @@ #endif * Push down and install the IPIP header. */ - iph = skb->nh.iph; + iph = ip_hdr(skb); iph->version = 4; iph->ihl = sizeof(struct iphdr) >> 2; iph->frag_off = df; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index f38e976..9706939 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -158,7 +158,7 @@ DEFINE_SNMP_STAT(struct ipstats_mib, ip_ int ip_call_ra_chain(struct sk_buff *skb) { struct ip_ra_chain *ra; - u8 protocol = skb->nh.iph->protocol; + u8 protocol = ip_hdr(skb)->protocol; struct sock *last = NULL; read_lock(&ip_ra_lock); @@ -171,7 +171,7 @@ int ip_call_ra_chain(struct sk_buff *skb if (sk && inet_sk(sk)->num == protocol && (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == skb->dev->ifindex)) { - if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { + if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { skb = ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN); if (skb == NULL) { read_unlock(&ip_ra_lock); @@ -198,17 +198,15 @@ int ip_call_ra_chain(struct sk_buff *skb static inline int ip_local_deliver_finish(struct sk_buff *skb) { - int ihl = skb->nh.iph->ihl*4; - - __skb_pull(skb, ihl); + __skb_pull(skb, ip_hdrlen(skb)); /* Point into the IP datagram, just past the header. */ - skb->h.raw = skb->data; + skb_reset_transport_header(skb); rcu_read_lock(); { /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ - int protocol = skb->nh.iph->protocol; + int protocol = ip_hdr(skb)->protocol; int hash; struct sock *raw_sk; struct net_protocol *ipprot; @@ -220,7 +218,7 @@ static inline int ip_local_deliver_finis /* If there maybe a raw socket we must check - if not we * don't care less */ - if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash)) + if (raw_sk && !raw_v4_input(skb, ip_hdr(skb), hash)) raw_sk = NULL; if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) { @@ -266,7 +264,7 @@ int ip_local_deliver(struct sk_buff *skb * Reassemble IP fragments. */ - if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { + if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER); if (!skb) return 0; @@ -294,7 +292,7 @@ static inline int ip_rcv_options(struct goto drop; } - iph = skb->nh.iph; + iph = ip_hdr(skb); if (ip_options_compile(NULL, skb)) { IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); @@ -330,7 +328,8 @@ drop: static inline int ip_rcv_finish(struct sk_buff *skb) { - struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); + struct rtable *rt; /* * Initialise the virtual path cache for the packet. It describes @@ -342,6 +341,8 @@ static inline int ip_rcv_finish(struct s if (unlikely(err)) { if (err == -EHOSTUNREACH) IP_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); + else if (err == -ENETUNREACH) + IP_INC_STATS_BH(IPSTATS_MIB_INNOROUTES); goto drop; } } @@ -360,6 +361,12 @@ #endif if (iph->ihl > 5 && ip_rcv_options(skb)) goto drop; + rt = (struct rtable*)skb->dst; + if (rt->rt_type == RTN_MULTICAST) + IP_INC_STATS_BH(IPSTATS_MIB_INMCASTPKTS); + else if (rt->rt_type == RTN_BROADCAST) + IP_INC_STATS_BH(IPSTATS_MIB_INBCASTPKTS); + return dst_input(skb); drop: @@ -391,7 +398,7 @@ int ip_rcv(struct sk_buff *skb, struct n if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto inhdr_error; - iph = skb->nh.iph; + iph = ip_hdr(skb); /* * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. @@ -410,13 +417,16 @@ int ip_rcv(struct sk_buff *skb, struct n if (!pskb_may_pull(skb, iph->ihl*4)) goto inhdr_error; - iph = skb->nh.iph; + iph = ip_hdr(skb); if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) goto inhdr_error; len = ntohs(iph->tot_len); - if (skb->len < len || len < (iph->ihl*4)) + if (skb->len < len) { + IP_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS); + goto drop; + } else if (len < (iph->ihl*4)) goto inhdr_error; /* Our transport medium may have padded the buffer out. Now we know it diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index f906a80..2513468 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -40,7 +40,7 @@ #include void ip_options_build(struct sk_buff * skb, struct ip_options * opt, __be32 daddr, struct rtable *rt, int is_frag) { - unsigned char * iph = skb->nh.raw; + unsigned char *iph = skb_network_header(skb); memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options)); memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen); @@ -104,13 +104,13 @@ int ip_options_echo(struct ip_options * return 0; } - sptr = skb->nh.raw; + sptr = skb_network_header(skb); dptr = dopt->__data; if (skb->dst) daddr = ((struct rtable*)skb->dst)->rt_spec_dst; else - daddr = skb->nh.iph->daddr; + daddr = ip_hdr(skb)->daddr; if (sopt->rr) { optlen = sptr[sopt->rr+1]; @@ -180,7 +180,8 @@ int ip_options_echo(struct ip_options * /* * RFC1812 requires to fix illegal source routes. */ - if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0) + if (memcmp(&ip_hdr(skb)->saddr, + &start[soffset + 3], 4) == 0) doffset -= 4; } if (doffset > 3) { @@ -217,7 +218,7 @@ int ip_options_echo(struct ip_options * void ip_options_fragment(struct sk_buff * skb) { - unsigned char * optptr = skb->nh.raw + sizeof(struct iphdr); + unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr); struct ip_options * opt = &(IPCB(skb)->opt); int l = opt->optlen; int optlen; @@ -264,12 +265,13 @@ int ip_options_compile(struct ip_options if (!opt) { opt = &(IPCB(skb)->opt); - iph = skb->nh.raw; + iph = skb_network_header(skb); opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr); optptr = iph + sizeof(struct iphdr); opt->is_data = 0; } else { - optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]); + optptr = opt->is_data ? opt->__data : + (unsigned char *)&(ip_hdr(skb)[1]); iph = optptr - sizeof(struct iphdr); } @@ -563,7 +565,7 @@ void ip_forward_options(struct sk_buff * struct ip_options * opt = &(IPCB(skb)->opt); unsigned char * optptr; struct rtable *rt = (struct rtable*)skb->dst; - unsigned char *raw = skb->nh.raw; + unsigned char *raw = skb_network_header(skb); if (opt->rr_needaddr) { optptr = (unsigned char *)raw + opt->rr; @@ -587,7 +589,7 @@ void ip_forward_options(struct sk_buff * if (srrptr + 3 <= srrspace) { opt->is_changed = 1; ip_rt_get_source(&optptr[srrptr-1], rt); - skb->nh.iph->daddr = rt->rt_dst; + ip_hdr(skb)->daddr = rt->rt_dst; optptr[2] = srrptr+4; } else if (net_ratelimit()) printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n"); @@ -599,7 +601,7 @@ void ip_forward_options(struct sk_buff * } if (opt->is_changed) { opt->is_changed = 0; - ip_send_check(skb->nh.iph); + ip_send_check(ip_hdr(skb)); } } @@ -608,8 +610,8 @@ int ip_options_rcv_srr(struct sk_buff *s struct ip_options *opt = &(IPCB(skb)->opt); int srrspace, srrptr; __be32 nexthop; - struct iphdr *iph = skb->nh.iph; - unsigned char * optptr = skb->nh.raw + opt->srr; + struct iphdr *iph = ip_hdr(skb); + unsigned char *optptr = skb_network_header(skb) + opt->srr; struct rtable *rt = (struct rtable*)skb->dst; struct rtable *rt2; int err; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index d096332..d6427d9 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -95,8 +95,8 @@ __inline__ void ip_send_check(struct iph /* dev_loopback_xmit for use with netfilter. */ static int ip_dev_loopback_xmit(struct sk_buff *newskb) { - newskb->mac.raw = newskb->data; - __skb_pull(newskb, newskb->nh.raw - newskb->data); + skb_reset_mac_header(newskb); + __skb_pull(newskb, skb_network_offset(newskb)); newskb->pkt_type = PACKET_LOOPBACK; newskb->ip_summed = CHECKSUM_UNNECESSARY; BUG_TRAP(newskb->dst); @@ -125,11 +125,9 @@ int ip_build_and_send_pkt(struct sk_buff struct iphdr *iph; /* Build the IP header. */ - if (opt) - iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen); - else - iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr)); - + skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); + skb_reset_network_header(skb); + iph = ip_hdr(skb); iph->version = 4; iph->ihl = 5; iph->tos = inet->tos; @@ -143,7 +141,6 @@ int ip_build_and_send_pkt(struct sk_buff iph->protocol = sk->sk_protocol; iph->tot_len = htons(skb->len); ip_select_ident(iph, &rt->u.dst, sk); - skb->nh.iph = iph; if (opt && opt->optlen) { iph->ihl += opt->optlen>>2; @@ -163,9 +160,15 @@ EXPORT_SYMBOL_GPL(ip_build_and_send_pkt) static inline int ip_finish_output2(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; + struct rtable *rt = (struct rtable *)dst; struct net_device *dev = dst->dev; int hh_len = LL_RESERVED_SPACE(dev); + if (rt->rt_type == RTN_MULTICAST) + IP_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS); + else if (rt->rt_type == RTN_BROADCAST) + IP_INC_STATS(IPSTATS_MIB_OUTBCASTPKTS); + /* Be paranoid, rather than too clever. */ if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) { struct sk_buff *skb2; @@ -192,6 +195,14 @@ static inline int ip_finish_output2(stru return -EINVAL; } +static inline int ip_skb_dst_mtu(struct sk_buff *skb) +{ + struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL; + + return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ? + skb->dst->dev->mtu : dst_mtu(skb->dst); +} + static inline int ip_finish_output(struct sk_buff *skb) { #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM) @@ -201,7 +212,7 @@ #if defined(CONFIG_NETFILTER) && defined return dst_output(skb); } #endif - if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) + if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb)) return ip_fragment(skb, ip_finish_output2); else return ip_finish_output2(skb); @@ -248,7 +259,7 @@ #endif /* Multicasts with ttl 0 must not go beyond the host */ - if (skb->nh.iph->ttl == 0) { + if (ip_hdr(skb)->ttl == 0) { kfree_skb(skb); return 0; } @@ -333,7 +344,9 @@ packet_routed: goto no_route; /* OK, we know where to send it, allocate and build IP header. */ - iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); + skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); + skb_reset_network_header(skb); + iph = ip_hdr(skb); *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); iph->tot_len = htons(skb->len); if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok) @@ -344,7 +357,6 @@ packet_routed: iph->protocol = sk->sk_protocol; iph->saddr = rt->rt_src; iph->daddr = rt->rt_dst; - skb->nh.iph = iph; /* Transport layer set skb->h.foo itself. */ if (opt && opt->optlen) { @@ -386,21 +398,10 @@ static void ip_copy_metadata(struct sk_b #ifdef CONFIG_NET_SCHED to->tc_index = from->tc_index; #endif -#ifdef CONFIG_NETFILTER - /* Connection association is same as pre-frag packet */ - nf_conntrack_put(to->nfct); - to->nfct = from->nfct; - nf_conntrack_get(to->nfct); - to->nfctinfo = from->nfctinfo; + nf_copy(to, from); #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE) to->ipvs_property = from->ipvs_property; #endif -#ifdef CONFIG_BRIDGE_NETFILTER - nf_bridge_put(to->nf_bridge); - to->nf_bridge = from->nf_bridge; - nf_bridge_get(to->nf_bridge); -#endif -#endif skb_copy_secmark(to, from); } @@ -430,12 +431,12 @@ int ip_fragment(struct sk_buff *skb, int * Point into the IP datagram header. */ - iph = skb->nh.iph; + iph = ip_hdr(skb); if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) { IP_INC_STATS(IPSTATS_MIB_FRAGFAILS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, - htonl(dst_mtu(&rt->u.dst))); + htonl(ip_skb_dst_mtu(skb))); kfree_skb(skb); return -EMSGSIZE; } @@ -502,10 +503,11 @@ int ip_fragment(struct sk_buff *skb, int * before previous one went down. */ if (frag) { frag->ip_summed = CHECKSUM_NONE; - frag->h.raw = frag->data; - frag->nh.raw = __skb_push(frag, hlen); - memcpy(frag->nh.raw, iph, hlen); - iph = frag->nh.iph; + skb_reset_transport_header(frag); + __skb_push(frag, hlen); + skb_reset_network_header(frag); + memcpy(skb_network_header(frag), iph, hlen); + iph = ip_hdr(frag); iph->tot_len = htons(frag->len); ip_copy_metadata(frag, skb); if (offset == 0) @@ -566,7 +568,7 @@ slow_path: * Keep copying data until we run out. */ - while(left > 0) { + while (left > 0) { len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) @@ -593,8 +595,8 @@ slow_path: ip_copy_metadata(skb2, skb); skb_reserve(skb2, ll_rs); skb_put(skb2, len + hlen); - skb2->nh.raw = skb2->data; - skb2->h.raw = skb2->data + hlen; + skb_reset_network_header(skb2); + skb2->transport_header = skb2->network_header + hlen; /* * Charge the memory for the fragment to any owner @@ -608,19 +610,19 @@ slow_path: * Copy the packet header into the new buffer. */ - memcpy(skb2->nh.raw, skb->data, hlen); + skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen); /* * Copy a block of the IP datagram. */ - if (skb_copy_bits(skb, ptr, skb2->h.raw, len)) + if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len)) BUG(); left -= len; /* * Fill in the new header fields. */ - iph = skb2->nh.iph; + iph = ip_hdr(skb2); iph->frag_off = htons((offset >> 3)); /* ANK: dirty, but effective trick. Upgrade options only if @@ -722,10 +724,10 @@ static inline int ip_ufo_append_data(str skb_put(skb,fragheaderlen + transhdrlen); /* initialize network header pointer */ - skb->nh.raw = skb->data; + skb_reset_network_header(skb); /* initialize protocol header pointer */ - skb->h.raw = skb->data + fragheaderlen; + skb->transport_header = skb->network_header + fragheaderlen; skb->ip_summed = CHECKSUM_PARTIAL; skb->csum = 0; @@ -799,7 +801,9 @@ int ip_append_data(struct sock *sk, inet->cork.addr = ipc->addr; } dst_hold(&rt->u.dst); - inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path); + inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu : + dst_mtu(rt->u.dst.path); inet->cork.rt = rt; inet->cork.length = 0; sk->sk_sndmsg_page = NULL; @@ -929,9 +933,10 @@ alloc_new_skb: * Find where to start putting bytes. */ data = skb_put(skb, fraglen); - skb->nh.raw = data + exthdrlen; + skb_set_network_header(skb, exthdrlen); + skb->transport_header = (skb->network_header + + fragheaderlen); data += fragheaderlen; - skb->h.raw = data + exthdrlen; if (fraggap) { skb->csum = skb_copy_and_csum_bits( @@ -1100,8 +1105,6 @@ ssize_t ip_append_page(struct sock *sk, } if (len <= 0) { struct sk_buff *skb_prev; - char *data; - struct iphdr *iph; int alloclen; skb_prev = skb; @@ -1124,15 +1127,15 @@ ssize_t ip_append_page(struct sock *sk, /* * Find where to start putting bytes. */ - data = skb_put(skb, fragheaderlen + fraggap); - skb->nh.iph = iph = (struct iphdr *)data; - data += fragheaderlen; - skb->h.raw = data; - + skb_put(skb, fragheaderlen + fraggap); + skb_reset_network_header(skb); + skb->transport_header = (skb->network_header + + fragheaderlen); if (fraggap) { - skb->csum = skb_copy_and_csum_bits( - skb_prev, maxfraglen, - data, fraggap, 0); + skb->csum = skb_copy_and_csum_bits(skb_prev, + maxfraglen, + skb_transport_header(skb), + fraggap, 0); skb_prev->csum = csum_sub(skb_prev->csum, skb->csum); pskb_trim_unique(skb_prev, maxfraglen); @@ -1198,10 +1201,10 @@ int ip_push_pending_frames(struct sock * tail_skb = &(skb_shinfo(skb)->frag_list); /* move skb->data to ip header from ext header */ - if (skb->data < skb->nh.raw) - __skb_pull(skb, skb->nh.raw - skb->data); + if (skb->data < skb_network_header(skb)) + __skb_pull(skb, skb_network_offset(skb)); while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { - __skb_pull(tmp_skb, skb->h.raw - skb->nh.raw); + __skb_pull(tmp_skb, skb_network_header_len(skb)); *tail_skb = tmp_skb; tail_skb = &(tmp_skb->next); skb->len += tmp_skb->len; @@ -1216,13 +1219,13 @@ int ip_push_pending_frames(struct sock * * to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ - if (inet->pmtudisc != IP_PMTUDISC_DO) + if (inet->pmtudisc < IP_PMTUDISC_DO) skb->local_df = 1; /* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ - if (inet->pmtudisc == IP_PMTUDISC_DO || + if (inet->pmtudisc >= IP_PMTUDISC_DO || (skb->len <= dst_mtu(&rt->u.dst) && ip_dont_fragment(sk, &rt->u.dst))) df = htons(IP_DF); @@ -1352,11 +1355,11 @@ void ip_send_reply(struct sock *sk, stru struct flowi fl = { .nl_u = { .ip4_u = { .daddr = daddr, .saddr = rt->rt_spec_dst, - .tos = RT_TOS(skb->nh.iph->tos) } }, + .tos = RT_TOS(ip_hdr(skb)->tos) } }, /* Not quite clean, but right. */ .uli_u = { .ports = - { .sport = skb->h.th->dest, - .dport = skb->h.th->source } }, + { .sport = tcp_hdr(skb)->dest, + .dport = tcp_hdr(skb)->source } }, .proto = sk->sk_protocol }; security_skb_classify_flow(skb, &fl); if (ip_route_output_key(&rt, &fl)) @@ -1370,14 +1373,16 @@ void ip_send_reply(struct sock *sk, stru with locally disabled BH and that sk cannot be already spinlocked. */ bh_lock_sock(sk); - inet->tos = skb->nh.iph->tos; + inet->tos = ip_hdr(skb)->tos; sk->sk_priority = skb->priority; - sk->sk_protocol = skb->nh.iph->protocol; + sk->sk_protocol = ip_hdr(skb)->protocol; ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0, &ipc, rt, MSG_DONTWAIT); if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) { if (arg->csumoffset >= 0) - *((__sum16 *)skb->h.raw + arg->csumoffset) = csum_fold(csum_add(skb->csum, arg->csum)); + *((__sum16 *)skb_transport_header(skb) + + arg->csumoffset) = csum_fold(csum_add(skb->csum, + arg->csum)); skb->ip_summed = CHECKSUM_NONE; ip_push_pending_frames(sk); } diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 23048d9..4d54457 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -59,7 +59,7 @@ static void ip_cmsg_recv_pktinfo(struct struct in_pktinfo info; struct rtable *rt = (struct rtable *)skb->dst; - info.ipi_addr.s_addr = skb->nh.iph->daddr; + info.ipi_addr.s_addr = ip_hdr(skb)->daddr; if (rt) { info.ipi_ifindex = rt->rt_iif; info.ipi_spec_dst.s_addr = rt->rt_spec_dst; @@ -73,13 +73,13 @@ static void ip_cmsg_recv_pktinfo(struct static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb) { - int ttl = skb->nh.iph->ttl; + int ttl = ip_hdr(skb)->ttl; put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl); } static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb) { - put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos); + put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos); } static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) @@ -87,7 +87,8 @@ static void ip_cmsg_recv_opts(struct msg if (IPCB(skb)->opt.optlen == 0) return; - put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1); + put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, + ip_hdr(skb) + 1); } @@ -268,18 +269,21 @@ void ip_icmp_error(struct sock *sk, stru serr = SKB_EXT_ERR(skb); serr->ee.ee_errno = err; serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; - serr->ee.ee_type = skb->h.icmph->type; - serr->ee.ee_code = skb->h.icmph->code; + serr->ee.ee_type = icmp_hdr(skb)->type; + serr->ee.ee_code = icmp_hdr(skb)->code; serr->ee.ee_pad = 0; serr->ee.ee_info = info; serr->ee.ee_data = 0; - serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb->nh.raw; + serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) - + skb_network_header(skb); serr->port = port; - skb->h.raw = payload; - if (!skb_pull(skb, payload - skb->data) || - sock_queue_err_skb(sk, skb)) - kfree_skb(skb); + if (skb_pull(skb, payload - skb->data) != NULL) { + skb_reset_transport_header(skb); + if (sock_queue_err_skb(sk, skb) == 0) + return; + } + kfree_skb(skb); } void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) @@ -296,8 +300,9 @@ void ip_local_error(struct sock *sk, int if (!skb) return; - iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr)); - skb->nh.iph = iph; + skb_put(skb, sizeof(struct iphdr)); + skb_reset_network_header(skb); + iph = ip_hdr(skb); iph->daddr = daddr; serr = SKB_EXT_ERR(skb); @@ -308,11 +313,11 @@ void ip_local_error(struct sock *sk, int serr->ee.ee_pad = 0; serr->ee.ee_info = info; serr->ee.ee_data = 0; - serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw; + serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb); serr->port = port; - skb->h.raw = skb->tail; - __skb_pull(skb, skb->tail - skb->data); + __skb_pull(skb, skb_tail_pointer(skb) - skb->data); + skb_reset_transport_header(skb); if (sock_queue_err_skb(sk, skb)) kfree_skb(skb); @@ -354,7 +359,8 @@ int ip_recv_error(struct sock *sk, struc sin = (struct sockaddr_in *)msg->msg_name; if (sin) { sin->sin_family = AF_INET; - sin->sin_addr.s_addr = *(__be32*)(skb->nh.raw + serr->addr_offset); + sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) + + serr->addr_offset); sin->sin_port = serr->port; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -366,7 +372,7 @@ int ip_recv_error(struct sock *sk, struc struct inet_sock *inet = inet_sk(sk); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = skb->nh.iph->saddr; + sin->sin_addr.s_addr = ip_hdr(skb)->saddr; sin->sin_port = 0; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); if (inet->cmsg_flags) @@ -403,20 +409,20 @@ out: */ static int do_ip_setsockopt(struct sock *sk, int level, - int optname, char __user *optval, int optlen) + int optname, char __user *optval, int optlen) { struct inet_sock *inet = inet_sk(sk); int val=0,err; if (((1<= sizeof(int)) { if (get_user(val, (int __user *) optval)) return -EFAULT; @@ -440,444 +446,444 @@ #endif lock_sock(sk); switch (optname) { - case IP_OPTIONS: - { - struct ip_options * opt = NULL; - if (optlen > 40 || optlen < 0) - goto e_inval; - err = ip_options_get_from_user(&opt, optval, optlen); - if (err) - break; - if (inet->is_icsk) { - struct inet_connection_sock *icsk = inet_csk(sk); + case IP_OPTIONS: + { + struct ip_options * opt = NULL; + if (optlen > 40 || optlen < 0) + goto e_inval; + err = ip_options_get_from_user(&opt, optval, optlen); + if (err) + break; + if (inet->is_icsk) { + struct inet_connection_sock *icsk = inet_csk(sk); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - if (sk->sk_family == PF_INET || - (!((1 << sk->sk_state) & - (TCPF_LISTEN | TCPF_CLOSE)) && - inet->daddr != LOOPBACK4_IPV6)) { + if (sk->sk_family == PF_INET || + (!((1 << sk->sk_state) & + (TCPF_LISTEN | TCPF_CLOSE)) && + inet->daddr != LOOPBACK4_IPV6)) { #endif - if (inet->opt) - icsk->icsk_ext_hdr_len -= inet->opt->optlen; - if (opt) - icsk->icsk_ext_hdr_len += opt->optlen; - icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); + if (inet->opt) + icsk->icsk_ext_hdr_len -= inet->opt->optlen; + if (opt) + icsk->icsk_ext_hdr_len += opt->optlen; + icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - } -#endif } - opt = xchg(&inet->opt, opt); - kfree(opt); - break; +#endif } - case IP_PKTINFO: - if (val) - inet->cmsg_flags |= IP_CMSG_PKTINFO; - else - inet->cmsg_flags &= ~IP_CMSG_PKTINFO; - break; - case IP_RECVTTL: - if (val) - inet->cmsg_flags |= IP_CMSG_TTL; - else - inet->cmsg_flags &= ~IP_CMSG_TTL; - break; - case IP_RECVTOS: - if (val) - inet->cmsg_flags |= IP_CMSG_TOS; - else - inet->cmsg_flags &= ~IP_CMSG_TOS; - break; - case IP_RECVOPTS: - if (val) - inet->cmsg_flags |= IP_CMSG_RECVOPTS; - else - inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; - break; - case IP_RETOPTS: - if (val) - inet->cmsg_flags |= IP_CMSG_RETOPTS; - else - inet->cmsg_flags &= ~IP_CMSG_RETOPTS; + opt = xchg(&inet->opt, opt); + kfree(opt); + break; + } + case IP_PKTINFO: + if (val) + inet->cmsg_flags |= IP_CMSG_PKTINFO; + else + inet->cmsg_flags &= ~IP_CMSG_PKTINFO; + break; + case IP_RECVTTL: + if (val) + inet->cmsg_flags |= IP_CMSG_TTL; + else + inet->cmsg_flags &= ~IP_CMSG_TTL; + break; + case IP_RECVTOS: + if (val) + inet->cmsg_flags |= IP_CMSG_TOS; + else + inet->cmsg_flags &= ~IP_CMSG_TOS; + break; + case IP_RECVOPTS: + if (val) + inet->cmsg_flags |= IP_CMSG_RECVOPTS; + else + inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; + break; + case IP_RETOPTS: + if (val) + inet->cmsg_flags |= IP_CMSG_RETOPTS; + else + inet->cmsg_flags &= ~IP_CMSG_RETOPTS; + break; + case IP_PASSSEC: + if (val) + inet->cmsg_flags |= IP_CMSG_PASSSEC; + else + inet->cmsg_flags &= ~IP_CMSG_PASSSEC; + break; + case IP_TOS: /* This sets both TOS and Precedence */ + if (sk->sk_type == SOCK_STREAM) { + val &= ~3; + val |= inet->tos & 3; + } + if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && + !capable(CAP_NET_ADMIN)) { + err = -EPERM; break; - case IP_PASSSEC: - if (val) - inet->cmsg_flags |= IP_CMSG_PASSSEC; - else - inet->cmsg_flags &= ~IP_CMSG_PASSSEC; + } + if (inet->tos != val) { + inet->tos = val; + sk->sk_priority = rt_tos2priority(val); + sk_dst_reset(sk); + } + break; + case IP_TTL: + if (optlen<1) + goto e_inval; + if (val != -1 && (val < 1 || val>255)) + goto e_inval; + inet->uc_ttl = val; + break; + case IP_HDRINCL: + if (sk->sk_type != SOCK_RAW) { + err = -ENOPROTOOPT; break; - case IP_TOS: /* This sets both TOS and Precedence */ - if (sk->sk_type == SOCK_STREAM) { - val &= ~3; - val |= inet->tos & 3; - } - if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && - !capable(CAP_NET_ADMIN)) { - err = -EPERM; + } + inet->hdrincl = val ? 1 : 0; + break; + case IP_MTU_DISCOVER: + if (val<0 || val>3) + goto e_inval; + inet->pmtudisc = val; + break; + case IP_RECVERR: + inet->recverr = !!val; + if (!val) + skb_queue_purge(&sk->sk_error_queue); + break; + case IP_MULTICAST_TTL: + if (sk->sk_type == SOCK_STREAM) + goto e_inval; + if (optlen<1) + goto e_inval; + if (val==-1) + val = 1; + if (val < 0 || val > 255) + goto e_inval; + inet->mc_ttl = val; + break; + case IP_MULTICAST_LOOP: + if (optlen<1) + goto e_inval; + inet->mc_loop = !!val; + break; + case IP_MULTICAST_IF: + { + struct ip_mreqn mreq; + struct net_device *dev = NULL; + + if (sk->sk_type == SOCK_STREAM) + goto e_inval; + /* + * Check the arguments are allowable + */ + + err = -EFAULT; + if (optlen >= sizeof(struct ip_mreqn)) { + if (copy_from_user(&mreq,optval,sizeof(mreq))) break; - } - if (inet->tos != val) { - inet->tos = val; - sk->sk_priority = rt_tos2priority(val); - sk_dst_reset(sk); - } - break; - case IP_TTL: - if (optlen<1) - goto e_inval; - if (val != -1 && (val < 1 || val>255)) - goto e_inval; - inet->uc_ttl = val; - break; - case IP_HDRINCL: - if (sk->sk_type != SOCK_RAW) { - err = -ENOPROTOOPT; + } else { + memset(&mreq, 0, sizeof(mreq)); + if (optlen >= sizeof(struct in_addr) && + copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) + break; + } + + if (!mreq.imr_ifindex) { + if (mreq.imr_address.s_addr == INADDR_ANY) { + inet->mc_index = 0; + inet->mc_addr = 0; + err = 0; break; } - inet->hdrincl = val ? 1 : 0; - break; - case IP_MTU_DISCOVER: - if (val<0 || val>2) - goto e_inval; - inet->pmtudisc = val; - break; - case IP_RECVERR: - inet->recverr = !!val; - if (!val) - skb_queue_purge(&sk->sk_error_queue); - break; - case IP_MULTICAST_TTL: - if (sk->sk_type == SOCK_STREAM) - goto e_inval; - if (optlen<1) - goto e_inval; - if (val==-1) - val = 1; - if (val < 0 || val > 255) - goto e_inval; - inet->mc_ttl = val; - break; - case IP_MULTICAST_LOOP: - if (optlen<1) - goto e_inval; - inet->mc_loop = !!val; - break; - case IP_MULTICAST_IF: - { - struct ip_mreqn mreq; - struct net_device *dev = NULL; + dev = ip_dev_find(mreq.imr_address.s_addr); + if (dev) { + mreq.imr_ifindex = dev->ifindex; + dev_put(dev); + } + } else + dev = __dev_get_by_index(mreq.imr_ifindex); - if (sk->sk_type == SOCK_STREAM) - goto e_inval; - /* - * Check the arguments are allowable - */ - err = -EFAULT; - if (optlen >= sizeof(struct ip_mreqn)) { - if (copy_from_user(&mreq,optval,sizeof(mreq))) - break; - } else { - memset(&mreq, 0, sizeof(mreq)); - if (optlen >= sizeof(struct in_addr) && - copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) - break; - } + err = -EADDRNOTAVAIL; + if (!dev) + break; - if (!mreq.imr_ifindex) { - if (mreq.imr_address.s_addr == INADDR_ANY) { - inet->mc_index = 0; - inet->mc_addr = 0; - err = 0; - break; - } - dev = ip_dev_find(mreq.imr_address.s_addr); - if (dev) { - mreq.imr_ifindex = dev->ifindex; - dev_put(dev); - } - } else - dev = __dev_get_by_index(mreq.imr_ifindex); + err = -EINVAL; + if (sk->sk_bound_dev_if && + mreq.imr_ifindex != sk->sk_bound_dev_if) + break; + inet->mc_index = mreq.imr_ifindex; + inet->mc_addr = mreq.imr_address.s_addr; + err = 0; + break; + } - err = -EADDRNOTAVAIL; - if (!dev) - break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + struct ip_mreqn mreq; - err = -EINVAL; - if (sk->sk_bound_dev_if && - mreq.imr_ifindex != sk->sk_bound_dev_if) + if (optlen < sizeof(struct ip_mreq)) + goto e_inval; + err = -EFAULT; + if (optlen >= sizeof(struct ip_mreqn)) { + if (copy_from_user(&mreq,optval,sizeof(mreq))) break; + } else { + memset(&mreq, 0, sizeof(mreq)); + if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) + break; + } - inet->mc_index = mreq.imr_ifindex; - inet->mc_addr = mreq.imr_address.s_addr; - err = 0; + if (optname == IP_ADD_MEMBERSHIP) + err = ip_mc_join_group(sk, &mreq); + else + err = ip_mc_leave_group(sk, &mreq); + break; + } + case IP_MSFILTER: + { + extern int sysctl_igmp_max_msf; + struct ip_msfilter *msf; + + if (optlen < IP_MSFILTER_SIZE(0)) + goto e_inval; + if (optlen > sysctl_optmem_max) { + err = -ENOBUFS; break; } + msf = kmalloc(optlen, GFP_KERNEL); + if (msf == 0) { + err = -ENOBUFS; + break; + } + err = -EFAULT; + if (copy_from_user(msf, optval, optlen)) { + kfree(msf); + break; + } + /* numsrc >= (1G-4) overflow in 32 bits */ + if (msf->imsf_numsrc >= 0x3ffffffcU || + msf->imsf_numsrc > sysctl_igmp_max_msf) { + kfree(msf); + err = -ENOBUFS; + break; + } + if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { + kfree(msf); + err = -EINVAL; + break; + } + err = ip_mc_msfilter(sk, msf, 0); + kfree(msf); + break; + } + case IP_BLOCK_SOURCE: + case IP_UNBLOCK_SOURCE: + case IP_ADD_SOURCE_MEMBERSHIP: + case IP_DROP_SOURCE_MEMBERSHIP: + { + struct ip_mreq_source mreqs; + int omode, add; - case IP_ADD_MEMBERSHIP: - case IP_DROP_MEMBERSHIP: - { - struct ip_mreqn mreq; - - if (optlen < sizeof(struct ip_mreq)) - goto e_inval; + if (optlen != sizeof(struct ip_mreq_source)) + goto e_inval; + if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { err = -EFAULT; - if (optlen >= sizeof(struct ip_mreqn)) { - if(copy_from_user(&mreq,optval,sizeof(mreq))) - break; - } else { - memset(&mreq, 0, sizeof(mreq)); - if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) - break; - } - - if (optname == IP_ADD_MEMBERSHIP) - err = ip_mc_join_group(sk, &mreq); - else - err = ip_mc_leave_group(sk, &mreq); break; } - case IP_MSFILTER: - { - extern int sysctl_igmp_max_msf; - struct ip_msfilter *msf; + if (optname == IP_BLOCK_SOURCE) { + omode = MCAST_EXCLUDE; + add = 1; + } else if (optname == IP_UNBLOCK_SOURCE) { + omode = MCAST_EXCLUDE; + add = 0; + } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { + struct ip_mreqn mreq; - if (optlen < IP_MSFILTER_SIZE(0)) - goto e_inval; - if (optlen > sysctl_optmem_max) { - err = -ENOBUFS; - break; - } - msf = kmalloc(optlen, GFP_KERNEL); - if (msf == 0) { - err = -ENOBUFS; + mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; + mreq.imr_address.s_addr = mreqs.imr_interface; + mreq.imr_ifindex = 0; + err = ip_mc_join_group(sk, &mreq); + if (err && err != -EADDRINUSE) break; - } + omode = MCAST_INCLUDE; + add = 1; + } else /* IP_DROP_SOURCE_MEMBERSHIP */ { + omode = MCAST_INCLUDE; + add = 0; + } + err = ip_mc_source(add, omode, sk, &mreqs, 0); + break; + } + case MCAST_JOIN_GROUP: + case MCAST_LEAVE_GROUP: + { + struct group_req greq; + struct sockaddr_in *psin; + struct ip_mreqn mreq; + + if (optlen < sizeof(struct group_req)) + goto e_inval; + err = -EFAULT; + if (copy_from_user(&greq, optval, sizeof(greq))) + break; + psin = (struct sockaddr_in *)&greq.gr_group; + if (psin->sin_family != AF_INET) + goto e_inval; + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = psin->sin_addr; + mreq.imr_ifindex = greq.gr_interface; + + if (optname == MCAST_JOIN_GROUP) + err = ip_mc_join_group(sk, &mreq); + else + err = ip_mc_leave_group(sk, &mreq); + break; + } + case MCAST_JOIN_SOURCE_GROUP: + case MCAST_LEAVE_SOURCE_GROUP: + case MCAST_BLOCK_SOURCE: + case MCAST_UNBLOCK_SOURCE: + { + struct group_source_req greqs; + struct ip_mreq_source mreqs; + struct sockaddr_in *psin; + int omode, add; + + if (optlen != sizeof(struct group_source_req)) + goto e_inval; + if (copy_from_user(&greqs, optval, sizeof(greqs))) { err = -EFAULT; - if (copy_from_user(msf, optval, optlen)) { - kfree(msf); - break; - } - /* numsrc >= (1G-4) overflow in 32 bits */ - if (msf->imsf_numsrc >= 0x3ffffffcU || - msf->imsf_numsrc > sysctl_igmp_max_msf) { - kfree(msf); - err = -ENOBUFS; - break; - } - if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { - kfree(msf); - err = -EINVAL; - break; - } - err = ip_mc_msfilter(sk, msf, 0); - kfree(msf); break; } - case IP_BLOCK_SOURCE: - case IP_UNBLOCK_SOURCE: - case IP_ADD_SOURCE_MEMBERSHIP: - case IP_DROP_SOURCE_MEMBERSHIP: - { - struct ip_mreq_source mreqs; - int omode, add; - - if (optlen != sizeof(struct ip_mreq_source)) - goto e_inval; - if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { - err = -EFAULT; - break; - } - if (optname == IP_BLOCK_SOURCE) { - omode = MCAST_EXCLUDE; - add = 1; - } else if (optname == IP_UNBLOCK_SOURCE) { - omode = MCAST_EXCLUDE; - add = 0; - } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { - struct ip_mreqn mreq; - - mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; - mreq.imr_address.s_addr = mreqs.imr_interface; - mreq.imr_ifindex = 0; - err = ip_mc_join_group(sk, &mreq); - if (err && err != -EADDRINUSE) - break; - omode = MCAST_INCLUDE; - add = 1; - } else /* IP_DROP_SOURCE_MEMBERSHIP */ { - omode = MCAST_INCLUDE; - add = 0; - } - err = ip_mc_source(add, omode, sk, &mreqs, 0); + if (greqs.gsr_group.ss_family != AF_INET || + greqs.gsr_source.ss_family != AF_INET) { + err = -EADDRNOTAVAIL; break; } - case MCAST_JOIN_GROUP: - case MCAST_LEAVE_GROUP: - { - struct group_req greq; - struct sockaddr_in *psin; + psin = (struct sockaddr_in *)&greqs.gsr_group; + mreqs.imr_multiaddr = psin->sin_addr.s_addr; + psin = (struct sockaddr_in *)&greqs.gsr_source; + mreqs.imr_sourceaddr = psin->sin_addr.s_addr; + mreqs.imr_interface = 0; /* use index for mc_source */ + + if (optname == MCAST_BLOCK_SOURCE) { + omode = MCAST_EXCLUDE; + add = 1; + } else if (optname == MCAST_UNBLOCK_SOURCE) { + omode = MCAST_EXCLUDE; + add = 0; + } else if (optname == MCAST_JOIN_SOURCE_GROUP) { struct ip_mreqn mreq; - if (optlen < sizeof(struct group_req)) - goto e_inval; - err = -EFAULT; - if(copy_from_user(&greq, optval, sizeof(greq))) - break; - psin = (struct sockaddr_in *)&greq.gr_group; - if (psin->sin_family != AF_INET) - goto e_inval; - memset(&mreq, 0, sizeof(mreq)); + psin = (struct sockaddr_in *)&greqs.gsr_group; mreq.imr_multiaddr = psin->sin_addr; - mreq.imr_ifindex = greq.gr_interface; - - if (optname == MCAST_JOIN_GROUP) - err = ip_mc_join_group(sk, &mreq); - else - err = ip_mc_leave_group(sk, &mreq); + mreq.imr_address.s_addr = 0; + mreq.imr_ifindex = greqs.gsr_interface; + err = ip_mc_join_group(sk, &mreq); + if (err && err != -EADDRINUSE) + break; + greqs.gsr_interface = mreq.imr_ifindex; + omode = MCAST_INCLUDE; + add = 1; + } else /* MCAST_LEAVE_SOURCE_GROUP */ { + omode = MCAST_INCLUDE; + add = 0; + } + err = ip_mc_source(add, omode, sk, &mreqs, + greqs.gsr_interface); + break; + } + case MCAST_MSFILTER: + { + extern int sysctl_igmp_max_msf; + struct sockaddr_in *psin; + struct ip_msfilter *msf = NULL; + struct group_filter *gsf = NULL; + int msize, i, ifindex; + + if (optlen < GROUP_FILTER_SIZE(0)) + goto e_inval; + if (optlen > sysctl_optmem_max) { + err = -ENOBUFS; break; } - case MCAST_JOIN_SOURCE_GROUP: - case MCAST_LEAVE_SOURCE_GROUP: - case MCAST_BLOCK_SOURCE: - case MCAST_UNBLOCK_SOURCE: - { - struct group_source_req greqs; - struct ip_mreq_source mreqs; - struct sockaddr_in *psin; - int omode, add; - - if (optlen != sizeof(struct group_source_req)) - goto e_inval; - if (copy_from_user(&greqs, optval, sizeof(greqs))) { - err = -EFAULT; - break; - } - if (greqs.gsr_group.ss_family != AF_INET || - greqs.gsr_source.ss_family != AF_INET) { - err = -EADDRNOTAVAIL; - break; - } - psin = (struct sockaddr_in *)&greqs.gsr_group; - mreqs.imr_multiaddr = psin->sin_addr.s_addr; - psin = (struct sockaddr_in *)&greqs.gsr_source; - mreqs.imr_sourceaddr = psin->sin_addr.s_addr; - mreqs.imr_interface = 0; /* use index for mc_source */ - - if (optname == MCAST_BLOCK_SOURCE) { - omode = MCAST_EXCLUDE; - add = 1; - } else if (optname == MCAST_UNBLOCK_SOURCE) { - omode = MCAST_EXCLUDE; - add = 0; - } else if (optname == MCAST_JOIN_SOURCE_GROUP) { - struct ip_mreqn mreq; - - psin = (struct sockaddr_in *)&greqs.gsr_group; - mreq.imr_multiaddr = psin->sin_addr; - mreq.imr_address.s_addr = 0; - mreq.imr_ifindex = greqs.gsr_interface; - err = ip_mc_join_group(sk, &mreq); - if (err && err != -EADDRINUSE) - break; - greqs.gsr_interface = mreq.imr_ifindex; - omode = MCAST_INCLUDE; - add = 1; - } else /* MCAST_LEAVE_SOURCE_GROUP */ { - omode = MCAST_INCLUDE; - add = 0; - } - err = ip_mc_source(add, omode, sk, &mreqs, - greqs.gsr_interface); + gsf = kmalloc(optlen,GFP_KERNEL); + if (gsf == 0) { + err = -ENOBUFS; break; } - case MCAST_MSFILTER: - { - extern int sysctl_igmp_max_msf; - struct sockaddr_in *psin; - struct ip_msfilter *msf = NULL; - struct group_filter *gsf = NULL; - int msize, i, ifindex; - - if (optlen < GROUP_FILTER_SIZE(0)) - goto e_inval; - if (optlen > sysctl_optmem_max) { - err = -ENOBUFS; - break; - } - gsf = kmalloc(optlen,GFP_KERNEL); - if (gsf == 0) { - err = -ENOBUFS; - break; - } - err = -EFAULT; - if (copy_from_user(gsf, optval, optlen)) { - goto mc_msf_out; - } - /* numsrc >= (4G-140)/128 overflow in 32 bits */ - if (gsf->gf_numsrc >= 0x1ffffff || - gsf->gf_numsrc > sysctl_igmp_max_msf) { - err = -ENOBUFS; - goto mc_msf_out; - } - if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { - err = -EINVAL; - goto mc_msf_out; - } - msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); - msf = kmalloc(msize,GFP_KERNEL); - if (msf == 0) { - err = -ENOBUFS; - goto mc_msf_out; - } - ifindex = gsf->gf_interface; - psin = (struct sockaddr_in *)&gsf->gf_group; - if (psin->sin_family != AF_INET) { - err = -EADDRNOTAVAIL; - goto mc_msf_out; - } - msf->imsf_multiaddr = psin->sin_addr.s_addr; - msf->imsf_interface = 0; - msf->imsf_fmode = gsf->gf_fmode; - msf->imsf_numsrc = gsf->gf_numsrc; + err = -EFAULT; + if (copy_from_user(gsf, optval, optlen)) { + goto mc_msf_out; + } + /* numsrc >= (4G-140)/128 overflow in 32 bits */ + if (gsf->gf_numsrc >= 0x1ffffff || + gsf->gf_numsrc > sysctl_igmp_max_msf) { + err = -ENOBUFS; + goto mc_msf_out; + } + if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { + err = -EINVAL; + goto mc_msf_out; + } + msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); + msf = kmalloc(msize,GFP_KERNEL); + if (msf == 0) { + err = -ENOBUFS; + goto mc_msf_out; + } + ifindex = gsf->gf_interface; + psin = (struct sockaddr_in *)&gsf->gf_group; + if (psin->sin_family != AF_INET) { err = -EADDRNOTAVAIL; - for (i=0; igf_numsrc; ++i) { - psin = (struct sockaddr_in *)&gsf->gf_slist[i]; - - if (psin->sin_family != AF_INET) - goto mc_msf_out; - msf->imsf_slist[i] = psin->sin_addr.s_addr; - } - kfree(gsf); - gsf = NULL; - - err = ip_mc_msfilter(sk, msf, ifindex); -mc_msf_out: - kfree(msf); - kfree(gsf); - break; + goto mc_msf_out; } - case IP_ROUTER_ALERT: - err = ip_ra_control(sk, val ? 1 : 0, NULL); - break; - - case IP_FREEBIND: - if (optlen<1) - goto e_inval; - inet->freebind = !!val; - break; + msf->imsf_multiaddr = psin->sin_addr.s_addr; + msf->imsf_interface = 0; + msf->imsf_fmode = gsf->gf_fmode; + msf->imsf_numsrc = gsf->gf_numsrc; + err = -EADDRNOTAVAIL; + for (i=0; igf_numsrc; ++i) { + psin = (struct sockaddr_in *)&gsf->gf_slist[i]; - case IP_IPSEC_POLICY: - case IP_XFRM_POLICY: - err = -EPERM; - if (!capable(CAP_NET_ADMIN)) - break; - err = xfrm_user_policy(sk, optname, optval, optlen); + if (psin->sin_family != AF_INET) + goto mc_msf_out; + msf->imsf_slist[i] = psin->sin_addr.s_addr; + } + kfree(gsf); + gsf = NULL; + + err = ip_mc_msfilter(sk, msf, ifindex); + mc_msf_out: + kfree(msf); + kfree(gsf); + break; + } + case IP_ROUTER_ALERT: + err = ip_ra_control(sk, val ? 1 : 0, NULL); + break; + + case IP_FREEBIND: + if (optlen<1) + goto e_inval; + inet->freebind = !!val; + break; + + case IP_IPSEC_POLICY: + case IP_XFRM_POLICY: + err = -EPERM; + if (!capable(CAP_NET_ADMIN)) break; + err = xfrm_user_policy(sk, optname, optval, optlen); + break; - default: - err = -ENOPROTOOPT; - break; + default: + err = -ENOPROTOOPT; + break; } release_sock(sk); return err; @@ -948,214 +954,213 @@ #endif */ static int do_ip_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) + char __user *optval, int __user *optlen) { struct inet_sock *inet = inet_sk(sk); int val; int len; - if(level!=SOL_IP) + if (level != SOL_IP) return -EOPNOTSUPP; #ifdef CONFIG_IP_MROUTE - if(optname>=MRT_BASE && optname <=MRT_BASE+10) - { + if (optname >= MRT_BASE && optname <= MRT_BASE+10) { return ip_mroute_getsockopt(sk,optname,optval,optlen); } #endif - if(get_user(len,optlen)) + if (get_user(len,optlen)) return -EFAULT; - if(len < 0) + if (len < 0) return -EINVAL; lock_sock(sk); - switch(optname) { - case IP_OPTIONS: - { - unsigned char optbuf[sizeof(struct ip_options)+40]; - struct ip_options * opt = (struct ip_options*)optbuf; - opt->optlen = 0; - if (inet->opt) - memcpy(optbuf, inet->opt, - sizeof(struct ip_options)+ - inet->opt->optlen); - release_sock(sk); - - if (opt->optlen == 0) - return put_user(0, optlen); - - ip_options_undo(opt); - - len = min_t(unsigned int, len, opt->optlen); - if(put_user(len, optlen)) - return -EFAULT; - if(copy_to_user(optval, opt->__data, len)) - return -EFAULT; - return 0; - } - case IP_PKTINFO: - val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; - break; - case IP_RECVTTL: - val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; - break; - case IP_RECVTOS: - val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; - break; - case IP_RECVOPTS: - val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; - break; - case IP_RETOPTS: - val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; - break; - case IP_PASSSEC: - val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; - break; - case IP_TOS: - val = inet->tos; - break; - case IP_TTL: - val = (inet->uc_ttl == -1 ? - sysctl_ip_default_ttl : - inet->uc_ttl); - break; - case IP_HDRINCL: - val = inet->hdrincl; - break; - case IP_MTU_DISCOVER: - val = inet->pmtudisc; - break; - case IP_MTU: - { - struct dst_entry *dst; - val = 0; - dst = sk_dst_get(sk); - if (dst) { - val = dst_mtu(dst); - dst_release(dst); - } - if (!val) { - release_sock(sk); - return -ENOTCONN; - } - break; + switch (optname) { + case IP_OPTIONS: + { + unsigned char optbuf[sizeof(struct ip_options)+40]; + struct ip_options * opt = (struct ip_options*)optbuf; + opt->optlen = 0; + if (inet->opt) + memcpy(optbuf, inet->opt, + sizeof(struct ip_options)+ + inet->opt->optlen); + release_sock(sk); + + if (opt->optlen == 0) + return put_user(0, optlen); + + ip_options_undo(opt); + + len = min_t(unsigned int, len, opt->optlen); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, opt->__data, len)) + return -EFAULT; + return 0; + } + case IP_PKTINFO: + val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; + break; + case IP_RECVTTL: + val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; + break; + case IP_RECVTOS: + val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; + break; + case IP_RECVOPTS: + val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; + break; + case IP_RETOPTS: + val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; + break; + case IP_PASSSEC: + val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; + break; + case IP_TOS: + val = inet->tos; + break; + case IP_TTL: + val = (inet->uc_ttl == -1 ? + sysctl_ip_default_ttl : + inet->uc_ttl); + break; + case IP_HDRINCL: + val = inet->hdrincl; + break; + case IP_MTU_DISCOVER: + val = inet->pmtudisc; + break; + case IP_MTU: + { + struct dst_entry *dst; + val = 0; + dst = sk_dst_get(sk); + if (dst) { + val = dst_mtu(dst); + dst_release(dst); } - case IP_RECVERR: - val = inet->recverr; - break; - case IP_MULTICAST_TTL: - val = inet->mc_ttl; - break; - case IP_MULTICAST_LOOP: - val = inet->mc_loop; - break; - case IP_MULTICAST_IF: - { - struct in_addr addr; - len = min_t(unsigned int, len, sizeof(struct in_addr)); - addr.s_addr = inet->mc_addr; + if (!val) { release_sock(sk); - - if(put_user(len, optlen)) - return -EFAULT; - if(copy_to_user(optval, &addr, len)) - return -EFAULT; - return 0; + return -ENOTCONN; } - case IP_MSFILTER: - { - struct ip_msfilter msf; - int err; + break; + } + case IP_RECVERR: + val = inet->recverr; + break; + case IP_MULTICAST_TTL: + val = inet->mc_ttl; + break; + case IP_MULTICAST_LOOP: + val = inet->mc_loop; + break; + case IP_MULTICAST_IF: + { + struct in_addr addr; + len = min_t(unsigned int, len, sizeof(struct in_addr)); + addr.s_addr = inet->mc_addr; + release_sock(sk); - if (len < IP_MSFILTER_SIZE(0)) { - release_sock(sk); - return -EINVAL; - } - if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { - release_sock(sk); - return -EFAULT; - } - err = ip_mc_msfget(sk, &msf, - (struct ip_msfilter __user *)optval, optlen); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &addr, len)) + return -EFAULT; + return 0; + } + case IP_MSFILTER: + { + struct ip_msfilter msf; + int err; + + if (len < IP_MSFILTER_SIZE(0)) { release_sock(sk); - return err; + return -EINVAL; } - case MCAST_MSFILTER: - { - struct group_filter gsf; - int err; - - if (len < GROUP_FILTER_SIZE(0)) { - release_sock(sk); - return -EINVAL; - } - if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { - release_sock(sk); - return -EFAULT; - } - err = ip_mc_gsfget(sk, &gsf, - (struct group_filter __user *)optval, optlen); + if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { release_sock(sk); - return err; + return -EFAULT; } - case IP_PKTOPTIONS: - { - struct msghdr msg; + err = ip_mc_msfget(sk, &msf, + (struct ip_msfilter __user *)optval, optlen); + release_sock(sk); + return err; + } + case MCAST_MSFILTER: + { + struct group_filter gsf; + int err; + if (len < GROUP_FILTER_SIZE(0)) { release_sock(sk); + return -EINVAL; + } + if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { + release_sock(sk); + return -EFAULT; + } + err = ip_mc_gsfget(sk, &gsf, + (struct group_filter __user *)optval, optlen); + release_sock(sk); + return err; + } + case IP_PKTOPTIONS: + { + struct msghdr msg; + + release_sock(sk); - if (sk->sk_type != SOCK_STREAM) - return -ENOPROTOOPT; + if (sk->sk_type != SOCK_STREAM) + return -ENOPROTOOPT; - msg.msg_control = optval; - msg.msg_controllen = len; - msg.msg_flags = 0; + msg.msg_control = optval; + msg.msg_controllen = len; + msg.msg_flags = 0; - if (inet->cmsg_flags & IP_CMSG_PKTINFO) { - struct in_pktinfo info; + if (inet->cmsg_flags & IP_CMSG_PKTINFO) { + struct in_pktinfo info; - info.ipi_addr.s_addr = inet->rcv_saddr; - info.ipi_spec_dst.s_addr = inet->rcv_saddr; - info.ipi_ifindex = inet->mc_index; - put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); - } - if (inet->cmsg_flags & IP_CMSG_TTL) { - int hlim = inet->mc_ttl; - put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); - } - len -= msg.msg_controllen; - return put_user(len, optlen); + info.ipi_addr.s_addr = inet->rcv_saddr; + info.ipi_spec_dst.s_addr = inet->rcv_saddr; + info.ipi_ifindex = inet->mc_index; + put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); } - case IP_FREEBIND: - val = inet->freebind; - break; - default: - release_sock(sk); - return -ENOPROTOOPT; + if (inet->cmsg_flags & IP_CMSG_TTL) { + int hlim = inet->mc_ttl; + put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); + } + len -= msg.msg_controllen; + return put_user(len, optlen); + } + case IP_FREEBIND: + val = inet->freebind; + break; + default: + release_sock(sk); + return -ENOPROTOOPT; } release_sock(sk); if (len < sizeof(int) && len > 0 && val>=0 && val<255) { unsigned char ucval = (unsigned char)val; len = 1; - if(put_user(len, optlen)) + if (put_user(len, optlen)) return -EFAULT; - if(copy_to_user(optval,&ucval,1)) + if (copy_to_user(optval,&ucval,1)) return -EFAULT; } else { len = min_t(unsigned int, sizeof(int), len); - if(put_user(len, optlen)) + if (put_user(len, optlen)) return -EFAULT; - if(copy_to_user(optval,&val,len)) + if (copy_to_user(optval,&val,len)) return -EFAULT; } return 0; } int ip_getsockopt(struct sock *sk, int level, - int optname, char __user *optval, int __user *optlen) + int optname, char __user *optval, int __user *optlen) { int err; @@ -1169,7 +1174,7 @@ #endif ) { int len; - if(get_user(len,optlen)) + if (get_user(len,optlen)) return -EFAULT; lock_sock(sk); diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index aa704b8..ab86137 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -43,21 +43,15 @@ static LIST_HEAD(ipcomp_tfms_list); static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb) { - int err, plen, dlen; struct ipcomp_data *ipcd = x->data; - u8 *start, *scratch; - struct crypto_comp *tfm; - int cpu; - - plen = skb->len; - dlen = IPCOMP_SCRATCH_SIZE; - start = skb->data; + const int plen = skb->len; + int dlen = IPCOMP_SCRATCH_SIZE; + const u8 *start = skb->data; + const int cpu = get_cpu(); + u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); + struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); + int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen); - cpu = get_cpu(); - scratch = *per_cpu_ptr(ipcomp_scratches, cpu); - tfm = *per_cpu_ptr(ipcd->tfms, cpu); - - err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen); if (err) goto out; @@ -72,7 +66,7 @@ static int ipcomp_decompress(struct xfrm skb->truesize += dlen - plen; __skb_put(skb, dlen - plen); - memcpy(skb->data, scratch, dlen); + skb_copy_to_linear_data(skb, scratch, dlen); out: put_cpu(); return err; @@ -90,10 +84,10 @@ static int ipcomp_input(struct xfrm_stat skb->ip_summed = CHECKSUM_NONE; /* Remove ipcomp header and decompress original payload */ - iph = skb->nh.iph; + iph = ip_hdr(skb); ipch = (void *)skb->data; iph->protocol = ipch->nexthdr; - skb->h.raw = skb->nh.raw + sizeof(*ipch); + skb->transport_header = skb->network_header + sizeof(*ipch); __skb_pull(skb, sizeof(*ipch)); err = ipcomp_decompress(x, skb); @@ -103,23 +97,16 @@ out: static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb) { - int err, plen, dlen, ihlen; - struct iphdr *iph = skb->nh.iph; struct ipcomp_data *ipcd = x->data; - u8 *start, *scratch; - struct crypto_comp *tfm; - int cpu; + const int ihlen = ip_hdrlen(skb); + const int plen = skb->len - ihlen; + int dlen = IPCOMP_SCRATCH_SIZE; + u8 *start = skb->data + ihlen; + const int cpu = get_cpu(); + u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); + struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); + int err = crypto_comp_compress(tfm, start, plen, scratch, &dlen); - ihlen = iph->ihl * 4; - plen = skb->len - ihlen; - dlen = IPCOMP_SCRATCH_SIZE; - start = skb->data + ihlen; - - cpu = get_cpu(); - scratch = *per_cpu_ptr(ipcomp_scratches, cpu); - tfm = *per_cpu_ptr(ipcd->tfms, cpu); - - err = crypto_comp_compress(tfm, start, plen, scratch, &dlen); if (err) goto out; @@ -142,12 +129,11 @@ out: static int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb) { int err; - struct iphdr *iph; struct ip_comp_hdr *ipch; struct ipcomp_data *ipcd = x->data; int hdr_len = 0; + struct iphdr *iph = ip_hdr(skb); - iph = skb->nh.iph; iph->tot_len = htons(skb->len); hdr_len = iph->ihl * 4; if ((skb->len - hdr_len) < ipcd->threshold) { @@ -159,7 +145,7 @@ static int ipcomp_output(struct xfrm_sta goto out_ok; err = ipcomp_compress(x, skb); - iph = skb->nh.iph; + iph = ip_hdr(skb); if (err) { goto out_ok; @@ -188,8 +174,8 @@ static void ipcomp4_err(struct sk_buff * struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); struct xfrm_state *x; - if (skb->h.icmph->type != ICMP_DEST_UNREACH || - skb->h.icmph->code != ICMP_FRAG_NEEDED) + if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH || + icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) return; spi = htonl(ntohs(ipch->cpi)); diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index cf49de1..597c800 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -432,7 +432,7 @@ ic_rarp_recv(struct sk_buff *skb, struct goto drop; /* Basic sanity checks can be done without the lock. */ - rarp = (struct arphdr *)skb->h.raw; + rarp = (struct arphdr *)skb_transport_header(skb); /* If this test doesn't pass, it's not IP, or we should * ignore it anyway. @@ -455,7 +455,7 @@ ic_rarp_recv(struct sk_buff *skb, struct goto drop; /* OK, it is all there and looks valid, process... */ - rarp = (struct arphdr *)skb->h.raw; + rarp = (struct arphdr *)skb_transport_header(skb); rarp_ptr = (unsigned char *) (rarp + 1); /* One reply at a time, please. */ @@ -702,7 +702,8 @@ static void __init ic_bootp_send_if(stru memset(b, 0, sizeof(struct bootp_pkt)); /* Construct IP header */ - skb->nh.iph = h = &b->iph; + skb_reset_network_header(skb); + h = ip_hdr(skb); h->version = 4; h->ihl = 5; h->tot_len = htons(sizeof(struct bootp_pkt)); @@ -782,7 +783,7 @@ #ifdef IPCONFIG_DEBUG u8 *c; printk("DHCP/BOOTP: Got extension %d:",*ext); - for(c=ext+2; cnh.iph; + b = (struct bootp_pkt *)skb_network_header(skb); h = &b->iph; if (h->ihl != 5 || h->version != 4 || h->protocol != IPPROTO_UDP) @@ -883,7 +884,7 @@ static int __init ic_bootp_recv(struct s if (!pskb_may_pull(skb, skb->len)) goto drop; - b = (struct bootp_pkt *) skb->nh.iph; + b = (struct bootp_pkt *)skb_network_header(skb); h = &b->iph; /* One reply at a time, please. */ @@ -938,7 +939,7 @@ #ifdef IPCONFIG_DHCP if (opt[1] >= 4) memcpy(&server_id, opt + 2, 4); break; - }; + } } #ifdef IPCONFIG_DEBUG @@ -983,7 +984,7 @@ #endif ic_myaddr = NONE; ic_servaddr = NONE; goto drop_unlock; - }; + } ic_dhcp_msgtype = mt; @@ -1094,7 +1095,7 @@ #endif retries = CONF_SEND_RETRIES; get_random_bytes(&timeout, sizeof(timeout)); timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM); - for(;;) { + for (;;) { #ifdef IPCONFIG_BOOTP if (do_bootp && (d->able & IC_BOOTP)) ic_bootp_send_if(d, jiffies - start_jiffies); diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 3ec5ce0..ebd2f2d 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -157,10 +157,10 @@ static struct ip_tunnel * ipip_tunnel_lo return NULL; } -static struct ip_tunnel **ipip_bucket(struct ip_tunnel *t) +static struct ip_tunnel **__ipip_bucket(struct ip_tunnel_parm *parms) { - __be32 remote = t->parms.iph.daddr; - __be32 local = t->parms.iph.saddr; + __be32 remote = parms->iph.daddr; + __be32 local = parms->iph.saddr; unsigned h = 0; int prio = 0; @@ -175,6 +175,10 @@ static struct ip_tunnel **ipip_bucket(st return &tunnels[prio][h]; } +static inline struct ip_tunnel **ipip_bucket(struct ip_tunnel *t) +{ + return __ipip_bucket(&t->parms); +} static void ipip_tunnel_unlink(struct ip_tunnel *t) { @@ -206,19 +210,9 @@ static struct ip_tunnel * ipip_tunnel_lo __be32 local = parms->iph.saddr; struct ip_tunnel *t, **tp, *nt; struct net_device *dev; - unsigned h = 0; - int prio = 0; char name[IFNAMSIZ]; - if (remote) { - prio |= 2; - h ^= HASH(remote); - } - if (local) { - prio |= 1; - h ^= HASH(local); - } - for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) { + for (tp = __ipip_bucket(parms); (t = *tp) != NULL; tp = &t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) return t; } @@ -280,8 +274,8 @@ #ifndef I_WISH_WORLD_WERE_PERFECT ICMP in the real Internet is absolutely infeasible. */ struct iphdr *iph = (struct iphdr*)skb->data; - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; struct ip_tunnel *t; int err; @@ -336,8 +330,8 @@ #else struct iphdr *iph = (struct iphdr*)dp; int hlen = iph->ihl<<2; struct iphdr *eiph; - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; int rel_type = 0; int rel_code = 0; __be32 rel_info = 0; @@ -354,7 +348,7 @@ #else default: return 0; case ICMP_PARAMETERPROB: - n = ntohl(skb->h.icmph->un.gateway) >> 24; + n = ntohl(icmp_hdr(skb)->un.gateway) >> 24; if (n < hlen) return 0; @@ -373,7 +367,7 @@ #else return 0; case ICMP_FRAG_NEEDED: /* And it is the only really necessary thing :-) */ - n = ntohs(skb->h.icmph->un.frag.mtu); + n = ntohs(icmp_hdr(skb)->un.frag.mtu); if (n < hlen+68) return 0; n -= hlen; @@ -405,7 +399,7 @@ #else dst_release(skb2->dst); skb2->dst = NULL; skb_pull(skb2, skb->data - (u8*)eiph); - skb2->nh.raw = skb2->data; + skb_reset_network_header(skb2); /* Try to guess incoming interface */ memset(&fl, 0, sizeof(fl)); @@ -461,9 +455,10 @@ #else #endif } -static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff *skb) +static inline void ipip_ecn_decapsulate(const struct iphdr *outer_iph, + struct sk_buff *skb) { - struct iphdr *inner_iph = skb->nh.iph; + struct iphdr *inner_iph = ip_hdr(skb); if (INET_ECN_is_ce(outer_iph->tos)) IP_ECN_set_ce(inner_iph); @@ -471,10 +466,8 @@ static inline void ipip_ecn_decapsulate( static int ipip_rcv(struct sk_buff *skb) { - struct iphdr *iph; struct ip_tunnel *tunnel; - - iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); read_lock(&ipip_lock); if ((tunnel = ipip_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) { @@ -486,8 +479,8 @@ static int ipip_rcv(struct sk_buff *skb) secpath_reset(skb); - skb->mac.raw = skb->nh.raw; - skb->nh.raw = skb->data; + skb->mac_header = skb->network_header; + skb_reset_network_header(skb); skb->protocol = htons(ETH_P_IP); skb->pkt_type = PACKET_HOST; @@ -521,7 +514,7 @@ static int ipip_tunnel_xmit(struct sk_bu __be16 df = tiph->frag_off; struct rtable *rt; /* Route to the other host */ struct net_device *tdev; /* Device to other host */ - struct iphdr *old_iph = skb->nh.iph; + struct iphdr *old_iph = ip_hdr(skb); struct iphdr *iph; /* Our new IP header */ int max_headroom; /* The extra header space needed */ __be32 dst = tiph->daddr; @@ -615,11 +608,12 @@ static int ipip_tunnel_xmit(struct sk_bu skb_set_owner_w(new_skb, skb->sk); dev_kfree_skb(skb); skb = new_skb; - old_iph = skb->nh.iph; + old_iph = ip_hdr(skb); } - skb->h.raw = skb->nh.raw; - skb->nh.raw = skb_push(skb, sizeof(struct iphdr)); + skb->transport_header = skb->network_header; + skb_push(skb, sizeof(struct iphdr)); + skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); @@ -630,7 +624,7 @@ static int ipip_tunnel_xmit(struct sk_bu * Push down and install the IPIP header. */ - iph = skb->nh.iph; + iph = ip_hdr(skb); iph->version = 4; iph->ihl = sizeof(struct iphdr)>>2; iph->frag_off = df; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 601e3df..0ebae41 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -62,6 +62,7 @@ #include #include #include #include +#include #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) #define CONFIG_IP_PIMSM 1 @@ -302,8 +303,8 @@ static void ipmr_destroy_unres(struct mf atomic_dec(&cache_resolve_queue_len); - while((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) { - if (skb->nh.iph->version == 0) { + while ((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) { + if (ip_hdr(skb)->version == 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); nlh->nlmsg_type = NLMSG_ERROR; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); @@ -479,7 +480,7 @@ static struct mfc_cache *ipmr_cache_find static struct mfc_cache *ipmr_cache_alloc(void) { struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); - if(c==NULL) + if (c==NULL) return NULL; c->mfc_un.res.minvif = MAXVIFS; return c; @@ -488,7 +489,7 @@ static struct mfc_cache *ipmr_cache_allo static struct mfc_cache *ipmr_cache_alloc_unres(void) { struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); - if(c==NULL) + if (c==NULL) return NULL; skb_queue_head_init(&c->mfc_un.unres.unresolved); c->mfc_un.unres.expires = jiffies + 10*HZ; @@ -508,12 +509,13 @@ static void ipmr_cache_resolve(struct mf * Play the pending entries through our router */ - while((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) { - if (skb->nh.iph->version == 0) { + while ((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) { + if (ip_hdr(skb)->version == 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); if (ipmr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) { - nlh->nlmsg_len = skb->tail - (u8*)nlh; + nlh->nlmsg_len = (skb_tail_pointer(skb) - + (u8 *)nlh); } else { nlh->nlmsg_type = NLMSG_ERROR; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); @@ -539,7 +541,7 @@ static void ipmr_cache_resolve(struct mf static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) { struct sk_buff *skb; - int ihl = pkt->nh.iph->ihl<<2; + const int ihl = ip_hdrlen(pkt); struct igmphdr *igmp; struct igmpmsg *msg; int ret; @@ -551,7 +553,7 @@ #ifdef CONFIG_IP_PIMSM #endif skb = alloc_skb(128, GFP_ATOMIC); - if(!skb) + if (!skb) return -ENOBUFS; #ifdef CONFIG_IP_PIMSM @@ -561,14 +563,17 @@ #ifdef CONFIG_IP_PIMSM And all this only to mangle msg->im_msgtype and to set msg->im_mbz to "mbz" :-) */ - msg = (struct igmpmsg*)skb_push(skb, sizeof(struct iphdr)); - skb->nh.raw = skb->h.raw = (u8*)msg; - memcpy(msg, pkt->nh.raw, sizeof(struct iphdr)); + skb_push(skb, sizeof(struct iphdr)); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + msg = (struct igmpmsg *)skb_network_header(skb); + memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); msg->im_msgtype = IGMPMSG_WHOLEPKT; msg->im_mbz = 0; msg->im_vif = reg_vif_num; - skb->nh.iph->ihl = sizeof(struct iphdr) >> 2; - skb->nh.iph->tot_len = htons(ntohs(pkt->nh.iph->tot_len) + sizeof(struct iphdr)); + ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; + ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + + sizeof(struct iphdr)); } else #endif { @@ -577,10 +582,11 @@ #endif * Copy the IP header */ - skb->nh.iph = (struct iphdr *)skb_put(skb, ihl); - memcpy(skb->data,pkt->data,ihl); - skb->nh.iph->protocol = 0; /* Flag to the kernel this is a route add */ - msg = (struct igmpmsg*)skb->nh.iph; + skb->network_header = skb->tail; + skb_put(skb, ihl); + skb_copy_to_linear_data(skb, pkt->data, ihl); + ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */ + msg = (struct igmpmsg *)skb_network_header(skb); msg->im_vif = vifi; skb->dst = dst_clone(pkt->dst); @@ -592,8 +598,8 @@ #endif igmp->type = msg->im_msgtype = assert; igmp->code = 0; - skb->nh.iph->tot_len=htons(skb->len); /* Fix the length */ - skb->h.raw = skb->nh.raw; + ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ + skb->transport_header = skb->network_header; } if (mroute_socket == NULL) { @@ -622,11 +628,12 @@ ipmr_cache_unresolved(vifi_t vifi, struc { int err; struct mfc_cache *c; + const struct iphdr *iph = ip_hdr(skb); spin_lock_bh(&mfc_unres_lock); for (c=mfc_unres_queue; c; c=c->next) { - if (c->mfc_mcastgrp == skb->nh.iph->daddr && - c->mfc_origin == skb->nh.iph->saddr) + if (c->mfc_mcastgrp == iph->daddr && + c->mfc_origin == iph->saddr) break; } @@ -646,9 +653,9 @@ ipmr_cache_unresolved(vifi_t vifi, struc /* * Fill in the new cache entry */ - c->mfc_parent=-1; - c->mfc_origin=skb->nh.iph->saddr; - c->mfc_mcastgrp=skb->nh.iph->daddr; + c->mfc_parent = -1; + c->mfc_origin = iph->saddr; + c->mfc_mcastgrp = iph->daddr; /* * Reflect first query at mrouted. @@ -734,7 +741,7 @@ static int ipmr_mfc_add(struct mfcctl *m return 0; } - if(!MULTICAST(mfc->mfcc_mcastgrp.s_addr)) + if (!MULTICAST(mfc->mfcc_mcastgrp.s_addr)) return -EINVAL; c=ipmr_cache_alloc(); @@ -788,7 +795,7 @@ static void mroute_clean_tables(struct s /* * Shut down all active vif entries */ - for(i=0; isk_type != SOCK_RAW || - inet_sk(sk)->num != IPPROTO_IGMP) - return -EOPNOTSUPP; - if(optlen!=sizeof(int)) - return -ENOPROTOOPT; - - rtnl_lock(); - if (mroute_socket) { - rtnl_unlock(); - return -EADDRINUSE; - } - - ret = ip_ra_control(sk, 1, mrtsock_destruct); - if (ret == 0) { - write_lock_bh(&mrt_lock); - mroute_socket=sk; - write_unlock_bh(&mrt_lock); + switch (optname) { + case MRT_INIT: + if (sk->sk_type != SOCK_RAW || + inet_sk(sk)->num != IPPROTO_IGMP) + return -EOPNOTSUPP; + if (optlen!=sizeof(int)) + return -ENOPROTOOPT; - ipv4_devconf.mc_forwarding++; - } + rtnl_lock(); + if (mroute_socket) { rtnl_unlock(); - return ret; - case MRT_DONE: - if (sk!=mroute_socket) - return -EACCES; - return ip_ra_control(sk, 0, NULL); - case MRT_ADD_VIF: - case MRT_DEL_VIF: - if(optlen!=sizeof(vif)) - return -EINVAL; - if (copy_from_user(&vif,optval,sizeof(vif))) - return -EFAULT; - if(vif.vifc_vifi >= MAXVIFS) - return -ENFILE; - rtnl_lock(); - if (optname==MRT_ADD_VIF) { - ret = vif_add(&vif, sk==mroute_socket); - } else { - ret = vif_delete(vif.vifc_vifi); - } - rtnl_unlock(); - return ret; + return -EADDRINUSE; + } + + ret = ip_ra_control(sk, 1, mrtsock_destruct); + if (ret == 0) { + write_lock_bh(&mrt_lock); + mroute_socket=sk; + write_unlock_bh(&mrt_lock); + + ipv4_devconf.mc_forwarding++; + } + rtnl_unlock(); + return ret; + case MRT_DONE: + if (sk!=mroute_socket) + return -EACCES; + return ip_ra_control(sk, 0, NULL); + case MRT_ADD_VIF: + case MRT_DEL_VIF: + if (optlen!=sizeof(vif)) + return -EINVAL; + if (copy_from_user(&vif,optval,sizeof(vif))) + return -EFAULT; + if (vif.vifc_vifi >= MAXVIFS) + return -ENFILE; + rtnl_lock(); + if (optname==MRT_ADD_VIF) { + ret = vif_add(&vif, sk==mroute_socket); + } else { + ret = vif_delete(vif.vifc_vifi); + } + rtnl_unlock(); + return ret; /* * Manipulate the forwarding caches. These live * in a sort of kernel/user symbiosis. */ - case MRT_ADD_MFC: - case MRT_DEL_MFC: - if(optlen!=sizeof(mfc)) - return -EINVAL; - if (copy_from_user(&mfc,optval, sizeof(mfc))) - return -EFAULT; - rtnl_lock(); - if (optname==MRT_DEL_MFC) - ret = ipmr_mfc_delete(&mfc); - else - ret = ipmr_mfc_add(&mfc, sk==mroute_socket); - rtnl_unlock(); - return ret; + case MRT_ADD_MFC: + case MRT_DEL_MFC: + if (optlen!=sizeof(mfc)) + return -EINVAL; + if (copy_from_user(&mfc,optval, sizeof(mfc))) + return -EFAULT; + rtnl_lock(); + if (optname==MRT_DEL_MFC) + ret = ipmr_mfc_delete(&mfc); + else + ret = ipmr_mfc_add(&mfc, sk==mroute_socket); + rtnl_unlock(); + return ret; /* * Control PIM assert. */ - case MRT_ASSERT: - { - int v; - if(get_user(v,(int __user *)optval)) - return -EFAULT; - mroute_do_assert=(v)?1:0; - return 0; - } + case MRT_ASSERT: + { + int v; + if (get_user(v,(int __user *)optval)) + return -EFAULT; + mroute_do_assert=(v)?1:0; + return 0; + } #ifdef CONFIG_IP_PIMSM - case MRT_PIM: - { - int v, ret; - if(get_user(v,(int __user *)optval)) - return -EFAULT; - v = (v)?1:0; - rtnl_lock(); - ret = 0; - if (v != mroute_do_pim) { - mroute_do_pim = v; - mroute_do_assert = v; + case MRT_PIM: + { + int v, ret; + if (get_user(v,(int __user *)optval)) + return -EFAULT; + v = (v)?1:0; + rtnl_lock(); + ret = 0; + if (v != mroute_do_pim) { + mroute_do_pim = v; + mroute_do_assert = v; #ifdef CONFIG_IP_PIMSM_V2 - if (mroute_do_pim) - ret = inet_add_protocol(&pim_protocol, - IPPROTO_PIM); - else - ret = inet_del_protocol(&pim_protocol, - IPPROTO_PIM); - if (ret < 0) - ret = -EAGAIN; + if (mroute_do_pim) + ret = inet_add_protocol(&pim_protocol, + IPPROTO_PIM); + else + ret = inet_del_protocol(&pim_protocol, + IPPROTO_PIM); + if (ret < 0) + ret = -EAGAIN; #endif - } - rtnl_unlock(); - return ret; } + rtnl_unlock(); + return ret; + } #endif - /* - * Spurious command, or MRT_VERSION which you cannot - * set. - */ - default: - return -ENOPROTOOPT; + /* + * Spurious command, or MRT_VERSION which you cannot + * set. + */ + default: + return -ENOPROTOOPT; } } @@ -983,7 +988,7 @@ int ip_mroute_getsockopt(struct sock *sk int olr; int val; - if(optname!=MRT_VERSION && + if (optname!=MRT_VERSION && #ifdef CONFIG_IP_PIMSM optname!=MRT_PIM && #endif @@ -997,17 +1002,17 @@ #endif if (olr < 0) return -EINVAL; - if(put_user(olr,optlen)) + if (put_user(olr,optlen)) return -EFAULT; - if(optname==MRT_VERSION) + if (optname==MRT_VERSION) val=0x0305; #ifdef CONFIG_IP_PIMSM - else if(optname==MRT_PIM) + else if (optname==MRT_PIM) val=mroute_do_pim; #endif else val=mroute_do_assert; - if(copy_to_user(optval,&val,olr)) + if (copy_to_user(optval,&val,olr)) return -EFAULT; return 0; } @@ -1023,48 +1028,47 @@ int ipmr_ioctl(struct sock *sk, int cmd, struct vif_device *vif; struct mfc_cache *c; - switch(cmd) - { - case SIOCGETVIFCNT: - if (copy_from_user(&vr,arg,sizeof(vr))) - return -EFAULT; - if(vr.vifi>=maxvif) - return -EINVAL; - read_lock(&mrt_lock); - vif=&vif_table[vr.vifi]; - if(VIF_EXISTS(vr.vifi)) { - vr.icount=vif->pkt_in; - vr.ocount=vif->pkt_out; - vr.ibytes=vif->bytes_in; - vr.obytes=vif->bytes_out; - read_unlock(&mrt_lock); - - if (copy_to_user(arg,&vr,sizeof(vr))) - return -EFAULT; - return 0; - } + switch (cmd) { + case SIOCGETVIFCNT: + if (copy_from_user(&vr,arg,sizeof(vr))) + return -EFAULT; + if (vr.vifi>=maxvif) + return -EINVAL; + read_lock(&mrt_lock); + vif=&vif_table[vr.vifi]; + if (VIF_EXISTS(vr.vifi)) { + vr.icount=vif->pkt_in; + vr.ocount=vif->pkt_out; + vr.ibytes=vif->bytes_in; + vr.obytes=vif->bytes_out; read_unlock(&mrt_lock); - return -EADDRNOTAVAIL; - case SIOCGETSGCNT: - if (copy_from_user(&sr,arg,sizeof(sr))) - return -EFAULT; - read_lock(&mrt_lock); - c = ipmr_cache_find(sr.src.s_addr, sr.grp.s_addr); - if (c) { - sr.pktcnt = c->mfc_un.res.pkt; - sr.bytecnt = c->mfc_un.res.bytes; - sr.wrong_if = c->mfc_un.res.wrong_if; - read_unlock(&mrt_lock); - - if (copy_to_user(arg,&sr,sizeof(sr))) - return -EFAULT; - return 0; - } + if (copy_to_user(arg,&vr,sizeof(vr))) + return -EFAULT; + return 0; + } + read_unlock(&mrt_lock); + return -EADDRNOTAVAIL; + case SIOCGETSGCNT: + if (copy_from_user(&sr,arg,sizeof(sr))) + return -EFAULT; + + read_lock(&mrt_lock); + c = ipmr_cache_find(sr.src.s_addr, sr.grp.s_addr); + if (c) { + sr.pktcnt = c->mfc_un.res.pkt; + sr.bytecnt = c->mfc_un.res.bytes; + sr.wrong_if = c->mfc_un.res.wrong_if; read_unlock(&mrt_lock); - return -EADDRNOTAVAIL; - default: - return -ENOIOCTLCMD; + + if (copy_to_user(arg,&sr,sizeof(sr))) + return -EFAULT; + return 0; + } + read_unlock(&mrt_lock); + return -EADDRNOTAVAIL; + default: + return -ENOIOCTLCMD; } } @@ -1076,7 +1080,7 @@ static int ipmr_device_event(struct noti if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; v=&vif_table[0]; - for(ct=0;ctdev==ptr) vif_delete(ct); } @@ -1096,11 +1100,17 @@ static struct notifier_block ip_mr_notif static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr) { - struct iphdr *iph = (struct iphdr *)skb_push(skb,sizeof(struct iphdr)); + struct iphdr *iph; + struct iphdr *old_iph = ip_hdr(skb); + + skb_push(skb, sizeof(struct iphdr)); + skb->transport_header = skb->network_header; + skb_reset_network_header(skb); + iph = ip_hdr(skb); iph->version = 4; - iph->tos = skb->nh.iph->tos; - iph->ttl = skb->nh.iph->ttl; + iph->tos = old_iph->tos; + iph->ttl = old_iph->ttl; iph->frag_off = 0; iph->daddr = daddr; iph->saddr = saddr; @@ -1110,8 +1120,6 @@ static void ip_encap(struct sk_buff *skb ip_select_ident(iph, skb->dst, NULL); ip_send_check(iph); - skb->h.ipiph = skb->nh.iph; - skb->nh.iph = iph; memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); nf_reset(skb); } @@ -1134,7 +1142,7 @@ static inline int ipmr_forward_finish(st static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi) { - struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); struct vif_device *vif = &vif_table[vifi]; struct net_device *dev; struct rtable *rt; @@ -1200,8 +1208,7 @@ #endif dst_release(skb->dst); skb->dst = &rt->u.dst; - iph = skb->nh.iph; - ip_decrease_ttl(iph); + ip_decrease_ttl(ip_hdr(skb)); /* FIXME: forward and output firewalls used to be called here. * What do we do with netfilter? -- RR */ @@ -1301,7 +1308,7 @@ static int ip_mr_forward(struct sk_buff * Forward the frame */ for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) { - if (skb->nh.iph->ttl > cache->mfc_un.res.ttls[ct]) { + if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) @@ -1347,7 +1354,7 @@ int ip_mr_input(struct sk_buff *skb) if (IPCB(skb)->opt.router_alert) { if (ip_call_ra_chain(skb)) return 0; - } else if (skb->nh.iph->protocol == IPPROTO_IGMP){ + } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP){ /* IGMPv1 (and broken IGMPv2 implementations sort of Cisco IOS <= 11.2(8)) do not put router alert option to IGMP packets destined to routable @@ -1366,7 +1373,7 @@ int ip_mr_input(struct sk_buff *skb) } read_lock(&mrt_lock); - cache = ipmr_cache_find(skb->nh.iph->saddr, skb->nh.iph->daddr); + cache = ipmr_cache_find(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); /* * No usable cache entry @@ -1426,14 +1433,15 @@ int pim_rcv_v1(struct sk_buff * skb) if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) goto drop; - pim = (struct igmphdr*)skb->h.raw; + pim = igmp_hdr(skb); if (!mroute_do_pim || skb->len < sizeof(*pim) + sizeof(*encap) || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) goto drop; - encap = (struct iphdr*)(skb->h.raw + sizeof(struct igmphdr)); + encap = (struct iphdr *)(skb_transport_header(skb) + + sizeof(struct igmphdr)); /* Check that: a. packet is really destinted to a multicast group @@ -1455,9 +1463,9 @@ int pim_rcv_v1(struct sk_buff * skb) if (reg_dev == NULL) goto drop; - skb->mac.raw = skb->nh.raw; + skb->mac_header = skb->network_header; skb_pull(skb, (u8*)encap - skb->data); - skb->nh.iph = (struct iphdr *)skb->data; + skb_reset_network_header(skb); skb->dev = reg_dev; skb->protocol = htons(ETH_P_IP); skb->ip_summed = 0; @@ -1486,7 +1494,7 @@ static int pim_rcv(struct sk_buff * skb) if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) goto drop; - pim = (struct pimreghdr*)skb->h.raw; + pim = (struct pimreghdr *)skb_transport_header(skb); if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) || (pim->flags&PIM_NULL_REGISTER) || (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && @@ -1494,7 +1502,8 @@ static int pim_rcv(struct sk_buff * skb) goto drop; /* check if the inner packet is destined to mcast group */ - encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr)); + encap = (struct iphdr *)(skb_transport_header(skb) + + sizeof(struct pimreghdr)); if (!MULTICAST(encap->daddr) || encap->tot_len == 0 || ntohs(encap->tot_len) + sizeof(*pim) > skb->len) @@ -1510,9 +1519,9 @@ static int pim_rcv(struct sk_buff * skb) if (reg_dev == NULL) goto drop; - skb->mac.raw = skb->nh.raw; + skb->mac_header = skb->network_header; skb_pull(skb, (u8*)encap - skb->data); - skb->nh.iph = (struct iphdr *)skb->data; + skb_reset_network_header(skb); skb->dev = reg_dev; skb->protocol = htons(ETH_P_IP); skb->ip_summed = 0; @@ -1537,7 +1546,7 @@ ipmr_fill_mroute(struct sk_buff *skb, st int ct; struct rtnexthop *nhp; struct net_device *dev = vif_table[c->mfc_parent].dev; - u8 *b = skb->tail; + u8 *b = skb_tail_pointer(skb); struct rtattr *mp_head; if (dev) @@ -1557,12 +1566,12 @@ ipmr_fill_mroute(struct sk_buff *skb, st } } mp_head->rta_type = RTA_MULTIPATH; - mp_head->rta_len = skb->tail - (u8*)mp_head; + mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head; rtm->rtm_type = RTN_MULTICAST; return 1; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -EMSGSIZE; } @@ -1577,6 +1586,7 @@ int ipmr_get_route(struct sk_buff *skb, if (cache==NULL) { struct sk_buff *skb2; + struct iphdr *iph; struct net_device *dev; int vif; @@ -1596,11 +1606,13 @@ int ipmr_get_route(struct sk_buff *skb, return -ENOMEM; } - skb2->nh.raw = skb_push(skb2, sizeof(struct iphdr)); - skb2->nh.iph->ihl = sizeof(struct iphdr)>>2; - skb2->nh.iph->saddr = rt->rt_src; - skb2->nh.iph->daddr = rt->rt_dst; - skb2->nh.iph->version = 0; + skb_push(skb2, sizeof(struct iphdr)); + skb_reset_network_header(skb2); + iph = ip_hdr(skb2); + iph->ihl = sizeof(struct iphdr) >> 2; + iph->saddr = rt->rt_src; + iph->daddr = rt->rt_dst; + iph->version = 0; err = ipmr_cache_unresolved(vif, skb2); read_unlock(&mrt_lock); return err; @@ -1625,7 +1637,7 @@ static struct vif_device *ipmr_vif_seq_i loff_t pos) { for (iter->ct = 0; iter->ct < maxvif; ++iter->ct) { - if(!VIF_EXISTS(iter->ct)) + if (!VIF_EXISTS(iter->ct)) continue; if (pos-- == 0) return &vif_table[iter->ct]; @@ -1649,7 +1661,7 @@ static void *ipmr_vif_seq_next(struct se return ipmr_vif_seq_idx(iter, 0); while (++iter->ct < maxvif) { - if(!VIF_EXISTS(iter->ct)) + if (!VIF_EXISTS(iter->ct)) continue; return &vif_table[iter->ct]; } @@ -1680,7 +1692,7 @@ static int ipmr_vif_seq_show(struct seq_ return 0; } -static struct seq_operations ipmr_vif_seq_ops = { +static const struct seq_operations ipmr_vif_seq_ops = { .start = ipmr_vif_seq_start, .next = ipmr_vif_seq_next, .stop = ipmr_vif_seq_stop, @@ -1732,14 +1744,14 @@ static struct mfc_cache *ipmr_mfc_seq_id it->cache = mfc_cache_array; read_lock(&mrt_lock); for (it->ct = 0; it->ct < MFC_LINES; it->ct++) - for(mfc = mfc_cache_array[it->ct]; mfc; mfc = mfc->next) + for (mfc = mfc_cache_array[it->ct]; mfc; mfc = mfc->next) if (pos-- == 0) return mfc; read_unlock(&mrt_lock); it->cache = &mfc_unres_queue; spin_lock_bh(&mfc_unres_lock); - for(mfc = mfc_unres_queue; mfc; mfc = mfc->next) + for (mfc = mfc_unres_queue; mfc; mfc = mfc->next) if (pos-- == 0) return mfc; spin_unlock_bh(&mfc_unres_lock); @@ -1829,9 +1841,9 @@ static int ipmr_mfc_seq_show(struct seq_ mfc->mfc_un.res.wrong_if); if (it->cache != &mfc_unres_queue) { - for(n = mfc->mfc_un.res.minvif; - n < mfc->mfc_un.res.maxvif; n++ ) { - if(VIF_EXISTS(n) + for (n = mfc->mfc_un.res.minvif; + n < mfc->mfc_un.res.maxvif; n++ ) { + if (VIF_EXISTS(n) && mfc->mfc_un.res.ttls[n] < 255) seq_printf(seq, " %2d:%-3d", @@ -1843,7 +1855,7 @@ static int ipmr_mfc_seq_show(struct seq_ return 0; } -static struct seq_operations ipmr_mfc_seq_ops = { +static const struct seq_operations ipmr_mfc_seq_ops = { .start = ipmr_mfc_seq_start, .next = ipmr_mfc_seq_next, .stop = ipmr_mfc_seq_stop, diff --git a/net/ipv4/ipvs/ip_vs_app.c b/net/ipv4/ipvs/ip_vs_app.c index 22e104c..15ad5dd 100644 --- a/net/ipv4/ipvs/ip_vs_app.c +++ b/net/ipv4/ipvs/ip_vs_app.c @@ -331,14 +331,14 @@ static inline int app_tcp_pkt_out(struct struct ip_vs_app *app) { int diff; - unsigned int tcp_offset = (*pskb)->nh.iph->ihl*4; + const unsigned int tcp_offset = ip_hdrlen(*pskb); struct tcphdr *th; __u32 seq; if (!ip_vs_make_skb_writable(pskb, tcp_offset + sizeof(*th))) return 0; - th = (struct tcphdr *)((*pskb)->nh.raw + tcp_offset); + th = (struct tcphdr *)(skb_network_header(*pskb) + tcp_offset); /* * Remember seq number in case this pkt gets resized @@ -406,14 +406,14 @@ static inline int app_tcp_pkt_in(struct struct ip_vs_app *app) { int diff; - unsigned int tcp_offset = (*pskb)->nh.iph->ihl*4; + const unsigned int tcp_offset = ip_hdrlen(*pskb); struct tcphdr *th; __u32 seq; if (!ip_vs_make_skb_writable(pskb, tcp_offset + sizeof(*th))) return 0; - th = (struct tcphdr *)((*pskb)->nh.raw + tcp_offset); + th = (struct tcphdr *)(skb_network_header(*pskb) + tcp_offset); /* * Remember seq number in case this pkt gets resized @@ -577,7 +577,6 @@ #endif int ip_vs_skb_replace(struct sk_buff *skb, gfp_t pri, char *o_buf, int o_len, char *n_buf, int n_len) { - struct iphdr *iph; int diff; int o_offset; int o_left; @@ -603,12 +602,11 @@ int ip_vs_skb_replace(struct sk_buff *sk skb_put(skb, diff); memmove(skb->data + o_offset + n_len, skb->data + o_offset + o_len, o_left); - memcpy(skb->data + o_offset, n_buf, n_len); + skb_copy_to_linear_data_offset(skb, o_offset, n_buf, n_len); } /* must update the iph total length here */ - iph = skb->nh.iph; - iph->tot_len = htons(skb->len); + ip_hdr(skb)->tot_len = htons(skb->len); LeaveFunction(9); return 0; diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c index 24d7b66..f005a2f 100644 --- a/net/ipv4/ipvs/ip_vs_core.c +++ b/net/ipv4/ipvs/ip_vs_core.c @@ -212,7 +212,7 @@ ip_vs_sched_persist(struct ip_vs_service __be16 ports[2]) { struct ip_vs_conn *cp = NULL; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); struct ip_vs_dest *dest; struct ip_vs_conn *ct; __be16 dport; /* destination port to forward */ @@ -381,7 +381,7 @@ struct ip_vs_conn * ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) { struct ip_vs_conn *cp = NULL; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); struct ip_vs_dest *dest; __be16 _ports[2], *pptr; @@ -447,7 +447,7 @@ int ip_vs_leave(struct ip_vs_service *sv struct ip_vs_protocol *pp) { __be16 _ports[2], *pptr; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); pptr = skb_header_pointer(skb, iph->ihl*4, sizeof(_ports), _ports); @@ -546,7 +546,7 @@ ip_vs_gather_frags(struct sk_buff *skb, { skb = ip_defrag(skb, user); if (skb) - ip_send_check(skb->nh.iph); + ip_send_check(ip_hdr(skb)); return skb; } @@ -557,9 +557,10 @@ ip_vs_gather_frags(struct sk_buff *skb, void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int inout) { - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); unsigned int icmp_offset = iph->ihl*4; - struct icmphdr *icmph = (struct icmphdr *)(skb->nh.raw + icmp_offset); + struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + + icmp_offset); struct iphdr *ciph = (struct iphdr *)(icmph + 1); if (inout) { @@ -617,14 +618,14 @@ static int ip_vs_out_icmp(struct sk_buff *related = 1; /* reassemble IP fragments */ - if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { + if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { skb = ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT); if (!skb) return NF_STOLEN; *pskb = skb; } - iph = skb->nh.iph; + iph = ip_hdr(skb); offset = ihl = iph->ihl * 4; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) @@ -659,7 +660,7 @@ static int ip_vs_out_icmp(struct sk_buff return NF_ACCEPT; /* Is the embedded protocol header present? */ - if (unlikely(cih->frag_off & __constant_htons(IP_OFFSET) && + if (unlikely(cih->frag_off & htons(IP_OFFSET) && pp->dont_defrag)) return NF_ACCEPT; @@ -680,8 +681,7 @@ static int ip_vs_out_icmp(struct sk_buff } /* Ensure the checksum is correct */ - if (skb->ip_summed != CHECKSUM_UNNECESSARY && - ip_vs_checksum_complete(skb, ihl)) { + if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { /* Failed checksum! */ IP_VS_DBG(1, "Forward ICMP: failed checksum from %d.%d.%d.%d!\n", NIPQUAD(iph->saddr)); @@ -712,8 +712,7 @@ static inline int is_tcp_reset(const str { struct tcphdr _tcph, *th; - th = skb_header_pointer(skb, skb->nh.iph->ihl * 4, - sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); if (th == NULL) return 0; return th->rst; @@ -740,14 +739,14 @@ ip_vs_out(unsigned int hooknum, struct s if (skb->ipvs_property) return NF_ACCEPT; - iph = skb->nh.iph; + iph = ip_hdr(skb); if (unlikely(iph->protocol == IPPROTO_ICMP)) { int related, verdict = ip_vs_out_icmp(pskb, &related); if (related) return verdict; skb = *pskb; - iph = skb->nh.iph; + iph = ip_hdr(skb); } pp = ip_vs_proto_get(iph->protocol); @@ -755,12 +754,12 @@ ip_vs_out(unsigned int hooknum, struct s return NF_ACCEPT; /* reassemble IP fragments */ - if (unlikely(iph->frag_off & __constant_htons(IP_MF|IP_OFFSET) && + if (unlikely(iph->frag_off & htons(IP_MF|IP_OFFSET) && !pp->dont_defrag)) { skb = ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT); if (!skb) return NF_STOLEN; - iph = skb->nh.iph; + iph = ip_hdr(skb); *pskb = skb; } @@ -810,8 +809,8 @@ ip_vs_out(unsigned int hooknum, struct s if (pp->snat_handler && !pp->snat_handler(pskb, pp, cp)) goto drop; skb = *pskb; - skb->nh.iph->saddr = cp->vaddr; - ip_send_check(skb->nh.iph); + ip_hdr(skb)->saddr = cp->vaddr; + ip_send_check(ip_hdr(skb)); /* For policy routing, packets originating from this * machine itself may be routed differently to packets @@ -861,7 +860,7 @@ ip_vs_in_icmp(struct sk_buff **pskb, int *related = 1; /* reassemble IP fragments */ - if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { + if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { skb = ip_vs_gather_frags(skb, hooknum == NF_IP_LOCAL_IN ? IP_DEFRAG_VS_IN : IP_DEFRAG_VS_FWD); @@ -870,7 +869,7 @@ ip_vs_in_icmp(struct sk_buff **pskb, int *pskb = skb; } - iph = skb->nh.iph; + iph = ip_hdr(skb); offset = ihl = iph->ihl * 4; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) @@ -905,7 +904,7 @@ ip_vs_in_icmp(struct sk_buff **pskb, int return NF_ACCEPT; /* Is the embedded protocol header present? */ - if (unlikely(cih->frag_off & __constant_htons(IP_OFFSET) && + if (unlikely(cih->frag_off & htons(IP_OFFSET) && pp->dont_defrag)) return NF_ACCEPT; @@ -921,8 +920,7 @@ ip_vs_in_icmp(struct sk_buff **pskb, int verdict = NF_DROP; /* Ensure the checksum is correct */ - if (skb->ip_summed != CHECKSUM_UNNECESSARY && - ip_vs_checksum_complete(skb, ihl)) { + if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { /* Failed checksum! */ IP_VS_DBG(1, "Incoming ICMP: failed checksum from %d.%d.%d.%d!\n", NIPQUAD(iph->saddr)); @@ -966,19 +964,19 @@ ip_vs_in(unsigned int hooknum, struct sk || skb->dev == &loopback_dev || skb->sk)) { IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n", skb->pkt_type, - skb->nh.iph->protocol, - NIPQUAD(skb->nh.iph->daddr)); + ip_hdr(skb)->protocol, + NIPQUAD(ip_hdr(skb)->daddr)); return NF_ACCEPT; } - iph = skb->nh.iph; + iph = ip_hdr(skb); if (unlikely(iph->protocol == IPPROTO_ICMP)) { int related, verdict = ip_vs_in_icmp(pskb, &related, hooknum); if (related) return verdict; skb = *pskb; - iph = skb->nh.iph; + iph = ip_hdr(skb); } /* Protocol supported? */ @@ -1064,7 +1062,7 @@ ip_vs_forward_icmp(unsigned int hooknum, { int r; - if ((*pskb)->nh.iph->protocol != IPPROTO_ICMP) + if (ip_hdr(*pskb)->protocol != IPPROTO_ICMP) return NF_ACCEPT; return ip_vs_in_icmp(pskb, &r, hooknum); diff --git a/net/ipv4/ipvs/ip_vs_dh.c b/net/ipv4/ipvs/ip_vs_dh.c index 502111f..dcf5d46 100644 --- a/net/ipv4/ipvs/ip_vs_dh.c +++ b/net/ipv4/ipvs/ip_vs_dh.c @@ -204,7 +204,7 @@ ip_vs_dh_schedule(struct ip_vs_service * { struct ip_vs_dest *dest; struct ip_vs_dh_bucket *tbl; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); IP_VS_DBG(6, "ip_vs_dh_schedule(): Scheduling...\n"); diff --git a/net/ipv4/ipvs/ip_vs_ftp.c b/net/ipv4/ipvs/ip_vs_ftp.c index 847c47a..344ddbb 100644 --- a/net/ipv4/ipvs/ip_vs_ftp.c +++ b/net/ipv4/ipvs/ip_vs_ftp.c @@ -159,10 +159,10 @@ static int ip_vs_ftp_out(struct ip_vs_ap return 0; if (cp->app_data == &ip_vs_ftp_pasv) { - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)th + (th->doff << 2); - data_limit = (*pskb)->tail; + data_limit = skb_tail_pointer(*pskb); if (ip_vs_ftp_get_addrport(data, data_limit, SERVER_STRING, @@ -262,14 +262,14 @@ static int ip_vs_ftp_in(struct ip_vs_app /* * Detecting whether it is passive */ - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); /* Since there may be OPTIONS in the TCP packet and the HLEN is the length of the header in 32-bit multiples, it is accurate to calculate data address by th+HLEN*4 */ data = data_start = (char *)th + (th->doff << 2); - data_limit = (*pskb)->tail; + data_limit = skb_tail_pointer(*pskb); while (data <= data_limit - 6) { if (strnicmp(data, "PASV\r\n", 6) == 0) { diff --git a/net/ipv4/ipvs/ip_vs_lblc.c b/net/ipv4/ipvs/ip_vs_lblc.c index c801273..052f4ed 100644 --- a/net/ipv4/ipvs/ip_vs_lblc.c +++ b/net/ipv4/ipvs/ip_vs_lblc.c @@ -521,7 +521,7 @@ ip_vs_lblc_schedule(struct ip_vs_service struct ip_vs_dest *dest; struct ip_vs_lblc_table *tbl; struct ip_vs_lblc_entry *en; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); IP_VS_DBG(6, "ip_vs_lblc_schedule(): Scheduling...\n"); diff --git a/net/ipv4/ipvs/ip_vs_lblcr.c b/net/ipv4/ipvs/ip_vs_lblcr.c index 23f9b9e..6225aca 100644 --- a/net/ipv4/ipvs/ip_vs_lblcr.c +++ b/net/ipv4/ipvs/ip_vs_lblcr.c @@ -775,7 +775,7 @@ ip_vs_lblcr_schedule(struct ip_vs_servic struct ip_vs_dest *dest; struct ip_vs_lblcr_table *tbl; struct ip_vs_lblcr_entry *en; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); IP_VS_DBG(6, "ip_vs_lblcr_schedule(): Scheduling...\n"); diff --git a/net/ipv4/ipvs/ip_vs_proto_ah.c b/net/ipv4/ipvs/ip_vs_proto_ah.c index 8b0505b..a842676 100644 --- a/net/ipv4/ipvs/ip_vs_proto_ah.c +++ b/net/ipv4/ipvs/ip_vs_proto_ah.c @@ -52,15 +52,15 @@ ah_conn_in_get(const struct sk_buff *skb if (likely(!inverse)) { cp = ip_vs_conn_in_get(IPPROTO_UDP, iph->saddr, - __constant_htons(PORT_ISAKMP), + htons(PORT_ISAKMP), iph->daddr, - __constant_htons(PORT_ISAKMP)); + htons(PORT_ISAKMP)); } else { cp = ip_vs_conn_in_get(IPPROTO_UDP, iph->daddr, - __constant_htons(PORT_ISAKMP), + htons(PORT_ISAKMP), iph->saddr, - __constant_htons(PORT_ISAKMP)); + htons(PORT_ISAKMP)); } if (!cp) { @@ -89,15 +89,15 @@ ah_conn_out_get(const struct sk_buff *sk if (likely(!inverse)) { cp = ip_vs_conn_out_get(IPPROTO_UDP, iph->saddr, - __constant_htons(PORT_ISAKMP), + htons(PORT_ISAKMP), iph->daddr, - __constant_htons(PORT_ISAKMP)); + htons(PORT_ISAKMP)); } else { cp = ip_vs_conn_out_get(IPPROTO_UDP, iph->daddr, - __constant_htons(PORT_ISAKMP), + htons(PORT_ISAKMP), iph->saddr, - __constant_htons(PORT_ISAKMP)); + htons(PORT_ISAKMP)); } if (!cp) { diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index 16a9ebe..e65577a 100644 --- a/net/ipv4/ipvs/ip_vs_proto_tcp.c +++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c @@ -76,16 +76,15 @@ tcp_conn_schedule(struct sk_buff *skb, struct ip_vs_service *svc; struct tcphdr _tcph, *th; - th = skb_header_pointer(skb, skb->nh.iph->ihl*4, - sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); if (th == NULL) { *verdict = NF_DROP; return 0; } if (th->syn && - (svc = ip_vs_service_get(skb->mark, skb->nh.iph->protocol, - skb->nh.iph->daddr, th->dest))) { + (svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol, + ip_hdr(skb)->daddr, th->dest))) { if (ip_vs_todrop()) { /* * It seems that we are very loaded. @@ -127,7 +126,7 @@ tcp_snat_handler(struct sk_buff **pskb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp) { struct tcphdr *tcph; - unsigned int tcphoff = (*pskb)->nh.iph->ihl * 4; + const unsigned int tcphoff = ip_hdrlen(*pskb); /* csum_check requires unshared skb */ if (!ip_vs_make_skb_writable(pskb, tcphoff+sizeof(*tcph))) @@ -143,7 +142,7 @@ tcp_snat_handler(struct sk_buff **pskb, return 0; } - tcph = (void *)(*pskb)->nh.iph + tcphoff; + tcph = (void *)ip_hdr(*pskb) + tcphoff; tcph->source = cp->vport; /* Adjust TCP checksums */ @@ -175,7 +174,7 @@ tcp_dnat_handler(struct sk_buff **pskb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp) { struct tcphdr *tcph; - unsigned int tcphoff = (*pskb)->nh.iph->ihl * 4; + const unsigned int tcphoff = ip_hdrlen(*pskb); /* csum_check requires unshared skb */ if (!ip_vs_make_skb_writable(pskb, tcphoff+sizeof(*tcph))) @@ -194,7 +193,7 @@ tcp_dnat_handler(struct sk_buff **pskb, return 0; } - tcph = (void *)(*pskb)->nh.iph + tcphoff; + tcph = (void *)ip_hdr(*pskb) + tcphoff; tcph->dest = cp->dport; /* @@ -224,15 +223,15 @@ tcp_dnat_handler(struct sk_buff **pskb, static int tcp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp) { - unsigned int tcphoff = skb->nh.iph->ihl*4; + const unsigned int tcphoff = ip_hdrlen(skb); switch (skb->ip_summed) { case CHECKSUM_NONE: skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); case CHECKSUM_COMPLETE: - if (csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr, + if (csum_tcpudp_magic(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, skb->len - tcphoff, - skb->nh.iph->protocol, skb->csum)) { + ip_hdr(skb)->protocol, skb->csum)) { IP_VS_DBG_RL_PKT(0, pp, skb, 0, "Failed checksum for"); return 0; @@ -467,8 +466,7 @@ tcp_state_transition(struct ip_vs_conn * { struct tcphdr _tcph, *th; - th = skb_header_pointer(skb, skb->nh.iph->ihl*4, - sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); if (th == NULL) return 0; diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c index 03f0a41..8ee5fe6 100644 --- a/net/ipv4/ipvs/ip_vs_proto_udp.c +++ b/net/ipv4/ipvs/ip_vs_proto_udp.c @@ -22,7 +22,7 @@ #include #include #include - +#include static struct ip_vs_conn * udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, @@ -56,7 +56,7 @@ udp_conn_out_get(const struct sk_buff *s struct ip_vs_conn *cp; __be16 _ports[2], *pptr; - pptr = skb_header_pointer(skb, skb->nh.iph->ihl*4, + pptr = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ports), _ports); if (pptr == NULL) return NULL; @@ -82,15 +82,15 @@ udp_conn_schedule(struct sk_buff *skb, s struct ip_vs_service *svc; struct udphdr _udph, *uh; - uh = skb_header_pointer(skb, skb->nh.iph->ihl*4, + uh = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_udph), &_udph); if (uh == NULL) { *verdict = NF_DROP; return 0; } - if ((svc = ip_vs_service_get(skb->mark, skb->nh.iph->protocol, - skb->nh.iph->daddr, uh->dest))) { + if ((svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol, + ip_hdr(skb)->daddr, uh->dest))) { if (ip_vs_todrop()) { /* * It seems that we are very loaded. @@ -133,7 +133,7 @@ udp_snat_handler(struct sk_buff **pskb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp) { struct udphdr *udph; - unsigned int udphoff = (*pskb)->nh.iph->ihl * 4; + const unsigned int udphoff = ip_hdrlen(*pskb); /* csum_check requires unshared skb */ if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph))) @@ -151,7 +151,7 @@ udp_snat_handler(struct sk_buff **pskb, return 0; } - udph = (void *)(*pskb)->nh.iph + udphoff; + udph = (void *)ip_hdr(*pskb) + udphoff; udph->source = cp->vport; /* @@ -187,7 +187,7 @@ udp_dnat_handler(struct sk_buff **pskb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp) { struct udphdr *udph; - unsigned int udphoff = (*pskb)->nh.iph->ihl * 4; + unsigned int udphoff = ip_hdrlen(*pskb); /* csum_check requires unshared skb */ if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph))) @@ -206,7 +206,7 @@ udp_dnat_handler(struct sk_buff **pskb, return 0; } - udph = (void *)(*pskb)->nh.iph + udphoff; + udph = (void *)ip_hdr(*pskb) + udphoff; udph->dest = cp->dport; /* @@ -239,7 +239,7 @@ static int udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp) { struct udphdr _udph, *uh; - unsigned int udphoff = skb->nh.iph->ihl*4; + const unsigned int udphoff = ip_hdrlen(skb); uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); if (uh == NULL) @@ -251,10 +251,10 @@ udp_csum_check(struct sk_buff *skb, stru skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); case CHECKSUM_COMPLETE: - if (csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, + if (csum_tcpudp_magic(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, skb->len - udphoff, - skb->nh.iph->protocol, + ip_hdr(skb)->protocol, skb->csum)) { IP_VS_DBG_RL_PKT(0, pp, skb, 0, "Failed checksum for"); diff --git a/net/ipv4/ipvs/ip_vs_sh.c b/net/ipv4/ipvs/ip_vs_sh.c index 338668f..1b25b00 100644 --- a/net/ipv4/ipvs/ip_vs_sh.c +++ b/net/ipv4/ipvs/ip_vs_sh.c @@ -201,7 +201,7 @@ ip_vs_sh_schedule(struct ip_vs_service * { struct ip_vs_dest *dest; struct ip_vs_sh_bucket *tbl; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n"); diff --git a/net/ipv4/ipvs/ip_vs_xmit.c b/net/ipv4/ipvs/ip_vs_xmit.c index e1f77bd..900ce29 100644 --- a/net/ipv4/ipvs/ip_vs_xmit.c +++ b/net/ipv4/ipvs/ip_vs_xmit.c @@ -156,7 +156,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, s struct ip_vs_protocol *pp) { struct rtable *rt; /* Route to the other host */ - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); u8 tos = iph->tos; int mtu; struct flowi fl = { @@ -178,7 +178,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, s /* MTU checking */ mtu = dst_mtu(&rt->u.dst); - if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) { + if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF))) { ip_rt_put(rt); icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); IP_VS_DBG_RL("ip_vs_bypass_xmit(): frag needed\n"); @@ -193,7 +193,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, s ip_rt_put(rt); return NF_STOLEN; } - ip_send_check(skb->nh.iph); + ip_send_check(ip_hdr(skb)); /* drop old route */ dst_release(skb->dst); @@ -226,7 +226,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, stru { struct rtable *rt; /* Route to the other host */ int mtu; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); EnterFunction(10); @@ -245,7 +245,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, stru /* MTU checking */ mtu = dst_mtu(&rt->u.dst); - if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) { + if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF))) { ip_rt_put(rt); icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); IP_VS_DBG_RL_PKT(0, pp, skb, 0, "ip_vs_nat_xmit(): frag needed for"); @@ -266,8 +266,8 @@ ip_vs_nat_xmit(struct sk_buff *skb, stru /* mangle the packet */ if (pp->dnat_handler && !pp->dnat_handler(&skb, pp, cp)) goto tx_error; - skb->nh.iph->daddr = cp->daddr; - ip_send_check(skb->nh.iph); + ip_hdr(skb)->daddr = cp->daddr; + ip_send_check(ip_hdr(skb)); IP_VS_DBG_PKT(10, pp, skb, 0, "After DNAT"); @@ -320,19 +320,20 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, s { struct rtable *rt; /* Route to the other host */ struct net_device *tdev; /* Device to other host */ - struct iphdr *old_iph = skb->nh.iph; + struct iphdr *old_iph = ip_hdr(skb); u8 tos = old_iph->tos; __be16 df = old_iph->frag_off; + sk_buff_data_t old_transport_header = skb->transport_header; struct iphdr *iph; /* Our new IP header */ int max_headroom; /* The extra header space needed */ int mtu; EnterFunction(10); - if (skb->protocol != __constant_htons(ETH_P_IP)) { + if (skb->protocol != htons(ETH_P_IP)) { IP_VS_DBG_RL("ip_vs_tunnel_xmit(): protocol error, " "ETH_P_IP: %d, skb protocol: %d\n", - __constant_htons(ETH_P_IP), skb->protocol); + htons(ETH_P_IP), skb->protocol); goto tx_error; } @@ -350,9 +351,9 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, s if (skb->dst) skb->dst->ops->update_pmtu(skb->dst, mtu); - df |= (old_iph->frag_off&__constant_htons(IP_DF)); + df |= (old_iph->frag_off & htons(IP_DF)); - if ((old_iph->frag_off&__constant_htons(IP_DF)) + if ((old_iph->frag_off & htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) { icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); ip_rt_put(rt); @@ -377,15 +378,16 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, s } kfree_skb(skb); skb = new_skb; - old_iph = skb->nh.iph; + old_iph = ip_hdr(skb); } - skb->h.raw = (void *) old_iph; + skb->transport_header = old_transport_header; /* fix old IP header checksum */ ip_send_check(old_iph); - skb->nh.raw = skb_push(skb, sizeof(struct iphdr)); + skb_push(skb, sizeof(struct iphdr)); + skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); /* drop old route */ @@ -395,7 +397,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, s /* * Push down and install the IPIP header. */ - iph = skb->nh.iph; + iph = ip_hdr(skb); iph->version = 4; iph->ihl = sizeof(struct iphdr)>>2; iph->frag_off = df; @@ -435,7 +437,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struc struct ip_vs_protocol *pp) { struct rtable *rt; /* Route to the other host */ - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); int mtu; EnterFunction(10); @@ -445,7 +447,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struc /* MTU checking */ mtu = dst_mtu(&rt->u.dst); - if ((iph->frag_off&__constant_htons(IP_DF)) && skb->len > mtu) { + if ((iph->frag_off & htons(IP_DF)) && skb->len > mtu) { icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); ip_rt_put(rt); IP_VS_DBG_RL("ip_vs_dr_xmit(): frag needed\n"); @@ -460,7 +462,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struc ip_rt_put(rt); return NF_STOLEN; } - ip_send_check(skb->nh.iph); + ip_send_check(ip_hdr(skb)); /* drop old route */ dst_release(skb->dst); @@ -514,12 +516,12 @@ ip_vs_icmp_xmit(struct sk_buff *skb, str * mangle and send the packet here (only for VS/NAT) */ - if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(skb->nh.iph->tos)))) + if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(ip_hdr(skb)->tos)))) goto tx_error_icmp; /* MTU checking */ mtu = dst_mtu(&rt->u.dst); - if ((skb->len > mtu) && (skb->nh.iph->frag_off&__constant_htons(IP_DF))) { + if ((skb->len > mtu) && (ip_hdr(skb)->frag_off & htons(IP_DF))) { ip_rt_put(rt); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n"); diff --git a/net/ipv4/multipath_drr.c b/net/ipv4/multipath_drr.c index 574c735..b03c5ca 100644 --- a/net/ipv4/multipath_drr.c +++ b/net/ipv4/multipath_drr.c @@ -100,7 +100,7 @@ static int drr_dev_event(struct notifier spin_unlock_bh(&state_lock); break; - }; + } return NOTIFY_DONE; } diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 6069a11..b441929 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -10,7 +10,7 @@ #include /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */ int ip_route_me_harder(struct sk_buff **pskb, unsigned addr_type) { - struct iphdr *iph = (*pskb)->nh.iph; + const struct iphdr *iph = ip_hdr(*pskb); struct rtable *rt; struct flowi fl = {}; struct dst_entry *odst; @@ -142,7 +142,7 @@ static void nf_ip_saveroute(const struct struct ip_rt_info *rt_info = nf_info_reroute(info); if (info->hook == NF_IP_LOCAL_OUT) { - const struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); rt_info->tos = iph->tos; rt_info->daddr = iph->daddr; @@ -155,7 +155,7 @@ static int nf_ip_reroute(struct sk_buff const struct ip_rt_info *rt_info = nf_info_reroute(info); if (info->hook == NF_IP_LOCAL_OUT) { - struct iphdr *iph = (*pskb)->nh.iph; + const struct iphdr *iph = ip_hdr(*pskb); if (!(iph->tos == rt_info->tos && iph->daddr == rt_info->daddr @@ -168,7 +168,7 @@ static int nf_ip_reroute(struct sk_buff __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, u_int8_t protocol) { - struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); __sum16 csum = 0; switch (skb->ip_summed) { diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 601808c..46509fa 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -30,188 +30,6 @@ config NF_CONNTRACK_PROC_COMPAT If unsure, say Y. -# connection tracking, helpers and protocols -config IP_NF_CT_ACCT - bool "Connection tracking flow accounting" - depends on IP_NF_CONNTRACK - help - If this option is enabled, the connection tracking code will - keep per-flow packet and byte counters. - - Those counters can be used for flow-based accounting or the - `connbytes' match. - - If unsure, say `N'. - -config IP_NF_CONNTRACK_MARK - bool 'Connection mark tracking support' - depends on IP_NF_CONNTRACK - help - This option enables support for connection marks, used by the - `CONNMARK' target and `connmark' match. Similar to the mark value - of packets, but this mark value is kept in the conntrack session - instead of the individual packets. - -config IP_NF_CONNTRACK_SECMARK - bool 'Connection tracking security mark support' - depends on IP_NF_CONNTRACK && NETWORK_SECMARK - help - This option enables security markings to be applied to - connections. Typically they are copied to connections from - packets using the CONNSECMARK target and copied back from - connections to packets with the same target, with the packets - being originally labeled via SECMARK. - - If unsure, say 'N'. - -config IP_NF_CONNTRACK_EVENTS - bool "Connection tracking events (EXPERIMENTAL)" - depends on EXPERIMENTAL && IP_NF_CONNTRACK - help - If this option is enabled, the connection tracking code will - provide a notifier chain that can be used by other kernel code - to get notified about changes in the connection tracking state. - - IF unsure, say `N'. - -config IP_NF_CONNTRACK_NETLINK - tristate 'Connection tracking netlink interface (EXPERIMENTAL)' - depends on EXPERIMENTAL && IP_NF_CONNTRACK && NETFILTER_NETLINK - depends on IP_NF_CONNTRACK!=y || NETFILTER_NETLINK!=m - depends on IP_NF_NAT=n || IP_NF_NAT - help - This option enables support for a netlink-based userspace interface - - -config IP_NF_CT_PROTO_SCTP - tristate 'SCTP protocol connection tracking support (EXPERIMENTAL)' - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - With this option enabled, the connection tracking code will - be able to do state tracking on SCTP connections. - - If you want to compile it as a module, say M here and read - . If unsure, say `N'. - -config IP_NF_FTP - tristate "FTP protocol support" - depends on IP_NF_CONNTRACK - help - Tracking FTP connections is problematic: special helpers are - required for tracking them, and doing masquerading and other forms - of Network Address Translation on them. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_IRC - tristate "IRC protocol support" - depends on IP_NF_CONNTRACK - ---help--- - There is a commonly-used extension to IRC called - Direct Client-to-Client Protocol (DCC). This enables users to send - files to each other, and also chat to each other without the need - of a server. DCC Sending is used anywhere you send files over IRC, - and DCC Chat is most commonly used by Eggdrop bots. If you are - using NAT, this extension will enable you to send files and initiate - chats. Note that you do NOT need this extension to get files or - have others initiate chats, or everything else in IRC. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_NETBIOS_NS - tristate "NetBIOS name service protocol support (EXPERIMENTAL)" - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - NetBIOS name service requests are sent as broadcast messages from an - unprivileged port and responded to with unicast messages to the - same port. This make them hard to firewall properly because connection - tracking doesn't deal with broadcasts. This helper tracks locally - originating NetBIOS name service requests and the corresponding - responses. It relies on correct IP address configuration, specifically - netmask and broadcast address. When properly configured, the output - of "ip address show" should look similar to this: - - $ ip -4 address show eth0 - 4: eth0: mtu 1500 qdisc pfifo_fast qlen 1000 - inet 172.16.2.252/24 brd 172.16.2.255 scope global eth0 - - To compile it as a module, choose M here. If unsure, say N. - -config IP_NF_TFTP - tristate "TFTP protocol support" - depends on IP_NF_CONNTRACK - help - TFTP connection tracking helper, this is required depending - on how restrictive your ruleset is. - If you are using a tftp client behind -j SNAT or -j MASQUERADING - you will need this. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_AMANDA - tristate "Amanda backup protocol support" - depends on IP_NF_CONNTRACK - select TEXTSEARCH - select TEXTSEARCH_KMP - help - If you are running the Amanda backup package - on this machine or machines that will be MASQUERADED through this - machine, then you may want to enable this feature. This allows the - connection tracking and natting code to allow the sub-channels that - Amanda requires for communication of the backup data, messages and - index. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_PPTP - tristate 'PPTP protocol support' - depends on IP_NF_CONNTRACK - help - This module adds support for PPTP (Point to Point Tunnelling - Protocol, RFC2637) connection tracking and NAT. - - If you are running PPTP sessions over a stateful firewall or NAT - box, you may want to enable this feature. - - Please note that not all PPTP modes of operation are supported yet. - For more info, read top of the file - net/ipv4/netfilter/ip_conntrack_pptp.c - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -config IP_NF_H323 - tristate 'H.323 protocol support (EXPERIMENTAL)' - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - H.323 is a VoIP signalling protocol from ITU-T. As one of the most - important VoIP protocols, it is widely used by voice hardware and - software including voice gateways, IP phones, Netmeeting, OpenPhone, - Gnomemeeting, etc. - - With this module you can support H.323 on a connection tracking/NAT - firewall. - - This module supports RAS, Fast Start, H.245 Tunnelling, Call - Forwarding, RTP/RTCP and T.120 based audio, video, fax, chat, - whiteboard, file transfer, etc. For more information, please - visit http://nath323.sourceforge.net/. - - If you want to compile it as a module, say 'M' here and read - Documentation/modules.txt. If unsure, say 'N'. - -config IP_NF_SIP - tristate "SIP protocol support (EXPERIMENTAL)" - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - SIP is an application-layer control protocol that can establish, - modify, and terminate multimedia sessions (conferences) such as - Internet telephony calls. With the ip_conntrack_sip and - the ip_nat_sip modules you can support the protocol on a connection - tracking/NATing firewall. - - To compile it as a module, choose M here. If unsure, say Y. - config IP_NF_QUEUE tristate "IP Userspace queueing via NETLINK (OBSOLETE)" help @@ -361,17 +179,6 @@ config IP_NF_TARGET_ULOG To compile it as a module, choose M here. If unsure, say N. -# NAT + specific targets: ip_conntrack -config IP_NF_NAT - tristate "Full NAT" - depends on IP_NF_IPTABLES && IP_NF_CONNTRACK - help - The Full NAT option allows masquerading, port forwarding and other - forms of full Network Address Port Translation. It is controlled by - the `nat' table in iptables: see the man page for iptables(8). - - To compile it as a module, choose M here. If unsure, say N. - # NAT + specific targets: nf_conntrack config NF_NAT tristate "Full NAT" @@ -383,11 +190,6 @@ config NF_NAT To compile it as a module, choose M here. If unsure, say N. -config IP_NF_NAT_NEEDED - bool - depends on IP_NF_NAT - default y - config NF_NAT_NEEDED bool depends on NF_NAT @@ -395,7 +197,7 @@ config NF_NAT_NEEDED config IP_NF_TARGET_MASQUERADE tristate "MASQUERADE target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help Masquerading is a special case of NAT: all outgoing connections are changed to seem to come from a particular interface's address, and @@ -407,7 +209,7 @@ config IP_NF_TARGET_MASQUERADE config IP_NF_TARGET_REDIRECT tristate "REDIRECT target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help REDIRECT is a special case of NAT: all incoming connections are mapped onto the incoming interface's address, causing the packets to @@ -418,7 +220,7 @@ config IP_NF_TARGET_REDIRECT config IP_NF_TARGET_NETMAP tristate "NETMAP target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help NETMAP is an implementation of static 1:1 NAT mapping of network addresses. It maps the network address part, while keeping the host @@ -429,28 +231,13 @@ config IP_NF_TARGET_NETMAP config IP_NF_TARGET_SAME tristate "SAME target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help This option adds a `SAME' target, which works like the standard SNAT target, but attempts to give clients the same IP for all connections. To compile it as a module, choose M here. If unsure, say N. -config IP_NF_NAT_SNMP_BASIC - tristate "Basic SNMP-ALG support (EXPERIMENTAL)" - depends on EXPERIMENTAL && IP_NF_NAT - ---help--- - - This module implements an Application Layer Gateway (ALG) for - SNMP payloads. In conjunction with NAT, it allows a network - management system to access multiple private networks with - conflicting addresses. It works by modifying IP addresses - inside SNMP payloads to match IP-layer NAT mapping. - - This is the "basic" form of SNMP-ALG, as described in RFC 2962 - - To compile it as a module, choose M here. If unsure, say N. - config NF_NAT_SNMP_BASIC tristate "Basic SNMP-ALG support (EXPERIMENTAL)" depends on EXPERIMENTAL && NF_NAT @@ -477,78 +264,37 @@ config NF_NAT_PROTO_GRE tristate depends on NF_NAT && NF_CT_PROTO_GRE -config IP_NF_NAT_FTP - tristate - depends on IP_NF_IPTABLES && IP_NF_CONNTRACK && IP_NF_NAT - default IP_NF_NAT && IP_NF_FTP - config NF_NAT_FTP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_FTP -config IP_NF_NAT_IRC - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_IRC=y - default m if IP_NF_IRC=m - config NF_NAT_IRC tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_IRC -config IP_NF_NAT_TFTP - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_TFTP=y - default m if IP_NF_TFTP=m - config NF_NAT_TFTP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_TFTP -config IP_NF_NAT_AMANDA - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_AMANDA=y - default m if IP_NF_AMANDA=m - config NF_NAT_AMANDA tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_AMANDA -config IP_NF_NAT_PPTP - tristate - depends on IP_NF_NAT!=n && IP_NF_PPTP!=n - default IP_NF_NAT if IP_NF_PPTP=y - default m if IP_NF_PPTP=m - config NF_NAT_PPTP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_PPTP select NF_NAT_PROTO_GRE -config IP_NF_NAT_H323 - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_H323=y - default m if IP_NF_H323=m - config NF_NAT_H323 tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_H323 -config IP_NF_NAT_SIP - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_SIP=y - default m if IP_NF_SIP=m - config NF_NAT_SIP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT @@ -606,9 +352,8 @@ config IP_NF_TARGET_TTL config IP_NF_TARGET_CLUSTERIP tristate "CLUSTERIP target support (EXPERIMENTAL)" depends on IP_NF_MANGLE && EXPERIMENTAL - depends on IP_NF_CONNTRACK || NF_CONNTRACK_IPV4 - select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK - select NF_CONNTRACK_MARK if NF_CONNTRACK_IPV4 + depends on NF_CONNTRACK_IPV4 + select NF_CONNTRACK_MARK help The CLUSTERIP target allows you to build load-balancing clusters of network servers without having a dedicated load-balancing diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 6625ec6..409d273 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -2,8 +2,6 @@ # # Makefile for the netfilter modules on top of IPv4. # -# objects for the standalone - connection tracking / NAT -ip_conntrack-objs := ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o # objects for l3 independent conntrack nf_conntrack_ipv4-objs := nf_conntrack_l3proto_ipv4.o nf_conntrack_proto_icmp.o ifeq ($(CONFIG_NF_CONNTRACK_PROC_COMPAT),y) @@ -12,53 +10,14 @@ nf_conntrack_ipv4-objs += nf_conntrack_l endif endif -ip_nat-objs := ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o -nf_nat-objs := nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o -ifneq ($(CONFIG_NF_NAT),) +nf_nat-objs := nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o iptable_nat-objs := nf_nat_rule.o nf_nat_standalone.o -else -iptable_nat-objs := ip_nat_rule.o ip_nat_standalone.o -endif - -ip_conntrack_pptp-objs := ip_conntrack_helper_pptp.o ip_conntrack_proto_gre.o -ip_nat_pptp-objs := ip_nat_helper_pptp.o ip_nat_proto_gre.o - -ip_conntrack_h323-objs := ip_conntrack_helper_h323.o ../../netfilter/nf_conntrack_h323_asn1.o -ip_nat_h323-objs := ip_nat_helper_h323.o # connection tracking -obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o -obj-$(CONFIG_IP_NF_NAT) += ip_nat.o obj-$(CONFIG_NF_NAT) += nf_nat.o -# conntrack netlink interface -obj-$(CONFIG_IP_NF_CONNTRACK_NETLINK) += ip_conntrack_netlink.o - - -# SCTP protocol connection tracking -obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o - -# connection tracking helpers -obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o -obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o -obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o -obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o -obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o -obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o -obj-$(CONFIG_IP_NF_SIP) += ip_conntrack_sip.o -obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o - -# NAT helpers (ip_conntrack) -obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o -obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o -obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o -obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o -obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o -obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o -obj-$(CONFIG_IP_NF_NAT_SIP) += ip_nat_sip.o - # NAT helpers (nf_conntrack) obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o @@ -78,7 +37,6 @@ obj-$(CONFIG_IP_NF_IPTABLES) += ip_table # the three instances of ip_tables obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o -obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o obj-$(CONFIG_NF_NAT) += iptable_nat.o obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o @@ -100,7 +58,6 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o -obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 57b0221..cae4121 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -245,7 +245,7 @@ unsigned int arpt_do_table(struct sk_buf e = get_entry(table_base, private->hook_entry[hook]); back = get_entry(table_base, private->underflow[hook]); - arp = (*pskb)->nh.arph; + arp = arp_hdr(*pskb); do { if (arp_packet_match(arp, (*pskb)->dev, indev, outdev, &e->arp)) { struct arpt_entry_target *t; @@ -297,7 +297,7 @@ unsigned int arpt_do_table(struct sk_buf t->data); /* Target might have changed stuff. */ - arp = (*pskb)->nh.arph; + arp = arp_hdr(*pskb); if (verdict == ARPT_CONTINUE) e = (void *)e + e->next_offset; diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c index 709db4d..6298d40 100644 --- a/net/ipv4/netfilter/arpt_mangle.c +++ b/net/ipv4/netfilter/arpt_mangle.c @@ -30,35 +30,35 @@ target(struct sk_buff **pskb, *pskb = nskb; } - arp = (*pskb)->nh.arph; - arpptr = (*pskb)->nh.raw + sizeof(*arp); + arp = arp_hdr(*pskb); + arpptr = skb_network_header(*pskb) + sizeof(*arp); pln = arp->ar_pln; hln = arp->ar_hln; /* We assume that pln and hln were checked in the match */ if (mangle->flags & ARPT_MANGLE_SDEV) { if (ARPT_DEV_ADDR_LEN_MAX < hln || - (arpptr + hln > (**pskb).tail)) + (arpptr + hln > skb_tail_pointer(*pskb))) return NF_DROP; memcpy(arpptr, mangle->src_devaddr, hln); } arpptr += hln; if (mangle->flags & ARPT_MANGLE_SIP) { if (ARPT_MANGLE_ADDR_LEN_MAX < pln || - (arpptr + pln > (**pskb).tail)) + (arpptr + pln > skb_tail_pointer(*pskb))) return NF_DROP; memcpy(arpptr, &mangle->u_s.src_ip, pln); } arpptr += pln; if (mangle->flags & ARPT_MANGLE_TDEV) { if (ARPT_DEV_ADDR_LEN_MAX < hln || - (arpptr + hln > (**pskb).tail)) + (arpptr + hln > skb_tail_pointer(*pskb))) return NF_DROP; memcpy(arpptr, mangle->tgt_devaddr, hln); } arpptr += hln; if (mangle->flags & ARPT_MANGLE_TIP) { if (ARPT_MANGLE_ADDR_LEN_MAX < pln || - (arpptr + pln > (**pskb).tail)) + (arpptr + pln > skb_tail_pointer(*pskb))) return NF_DROP; memcpy(arpptr, &mangle->u_t.tgt_ip, pln); } diff --git a/net/ipv4/netfilter/ip_conntrack_amanda.c b/net/ipv4/netfilter/ip_conntrack_amanda.c deleted file mode 100644 index 4f561f5..0000000 --- a/net/ipv4/netfilter/ip_conntrack_amanda.c +++ /dev/null @@ -1,229 +0,0 @@ -/* Amanda extension for IP connection tracking, Version 0.2 - * (C) 2002 by Brian J. Murrell - * based on HW's ip_conntrack_irc.c as well as other modules - * - * 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. - * - * Module load syntax: - * insmod ip_conntrack_amanda.o [master_timeout=n] - * - * Where master_timeout is the timeout (in seconds) of the master - * connection (port 10080). This defaults to 5 minutes but if - * your clients take longer than 5 minutes to do their work - * before getting back to the Amanda server, you can increase - * this value. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static unsigned int master_timeout = 300; -static char *ts_algo = "kmp"; - -MODULE_AUTHOR("Brian J. Murrell "); -MODULE_DESCRIPTION("Amanda connection tracking module"); -MODULE_LICENSE("GPL"); -module_param(master_timeout, uint, 0600); -MODULE_PARM_DESC(master_timeout, "timeout for the master connection"); -module_param(ts_algo, charp, 0400); -MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)"); - -unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); -EXPORT_SYMBOL_GPL(ip_nat_amanda_hook); - -enum amanda_strings { - SEARCH_CONNECT, - SEARCH_NEWLINE, - SEARCH_DATA, - SEARCH_MESG, - SEARCH_INDEX, -}; - -static struct { - char *string; - size_t len; - struct ts_config *ts; -} search[] = { - [SEARCH_CONNECT] = { - .string = "CONNECT ", - .len = 8, - }, - [SEARCH_NEWLINE] = { - .string = "\n", - .len = 1, - }, - [SEARCH_DATA] = { - .string = "DATA ", - .len = 5, - }, - [SEARCH_MESG] = { - .string = "MESG ", - .len = 5, - }, - [SEARCH_INDEX] = { - .string = "INDEX ", - .len = 6, - }, -}; - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) -{ - struct ts_state ts; - struct ip_conntrack_expect *exp; - unsigned int dataoff, start, stop, off, i; - char pbuf[sizeof("65535")], *tmp; - u_int16_t port, len; - int ret = NF_ACCEPT; - typeof(ip_nat_amanda_hook) ip_nat_amanda; - - /* Only look at packets from the Amanda server */ - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) - return NF_ACCEPT; - - /* increase the UDP timeout of the master connection as replies from - * Amanda clients to the server can be quite delayed */ - ip_ct_refresh(ct, *pskb, master_timeout * HZ); - - /* No data? */ - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); - if (dataoff >= (*pskb)->len) { - if (net_ratelimit()) - printk("amanda_help: skblen = %u\n", (*pskb)->len); - return NF_ACCEPT; - } - - memset(&ts, 0, sizeof(ts)); - start = skb_find_text(*pskb, dataoff, (*pskb)->len, - search[SEARCH_CONNECT].ts, &ts); - if (start == UINT_MAX) - goto out; - start += dataoff + search[SEARCH_CONNECT].len; - - memset(&ts, 0, sizeof(ts)); - stop = skb_find_text(*pskb, start, (*pskb)->len, - search[SEARCH_NEWLINE].ts, &ts); - if (stop == UINT_MAX) - goto out; - stop += start; - - for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) { - memset(&ts, 0, sizeof(ts)); - off = skb_find_text(*pskb, start, stop, search[i].ts, &ts); - if (off == UINT_MAX) - continue; - off += start + search[i].len; - - len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off); - if (skb_copy_bits(*pskb, off, pbuf, len)) - break; - pbuf[len] = '\0'; - - port = simple_strtoul(pbuf, &tmp, 10); - len = tmp - pbuf; - if (port == 0 || len > 5) - break; - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) { - ret = NF_DROP; - goto out; - } - - exp->expectfn = NULL; - exp->flags = 0; - - exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->tuple.dst.u.tcp.port = htons(port); - - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.protonum = 0xFF; - exp->mask.dst.u.tcp.port = htons(0xFFFF); - - /* RCU read locked by nf_hook_slow */ - ip_nat_amanda = rcu_dereference(ip_nat_amanda_hook); - if (ip_nat_amanda) - ret = ip_nat_amanda(pskb, ctinfo, off - dataoff, - len, exp); - else if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - ip_conntrack_expect_put(exp); - } - -out: - return ret; -} - -static struct ip_conntrack_helper amanda_helper = { - .max_expected = 3, - .timeout = 180, - .me = THIS_MODULE, - .help = help, - .name = "amanda", - - .tuple = { .src = { .u = { .udp = {.port = __constant_htons(10080) } } }, - .dst = { .protonum = IPPROTO_UDP }, - }, - .mask = { .src = { .u = { 0xFFFF } }, - .dst = { .protonum = 0xFF }, - }, -}; - -static void __exit ip_conntrack_amanda_fini(void) -{ - int i; - - ip_conntrack_helper_unregister(&amanda_helper); - for (i = 0; i < ARRAY_SIZE(search); i++) - textsearch_destroy(search[i].ts); -} - -static int __init ip_conntrack_amanda_init(void) -{ - int ret, i; - - ret = -ENOMEM; - for (i = 0; i < ARRAY_SIZE(search); i++) { - search[i].ts = textsearch_prepare(ts_algo, search[i].string, - search[i].len, - GFP_KERNEL, TS_AUTOLOAD); - if (search[i].ts == NULL) - goto err; - } - ret = ip_conntrack_helper_register(&amanda_helper); - if (ret < 0) - goto err; - return 0; - -err: - for (; i >= 0; i--) { - if (search[i].ts) - textsearch_destroy(search[i].ts); - } - return ret; -} - -module_init(ip_conntrack_amanda_init); -module_exit(ip_conntrack_amanda_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c deleted file mode 100644 index 23b99ae..0000000 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ /dev/null @@ -1,1550 +0,0 @@ -/* Connection state tracking for netfilter. This is separated from, - but required by, the NAT layer; it can also be used by an iptables - extension. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 23 Apr 2001: Harald Welte - * - new API and handling of conntrack/nat helpers - * - now capable of multiple expectations for one master - * 16 Jul 2002: Harald Welte - * - add usage/reference counts to ip_conntrack_expect - * - export ip_conntrack[_expect]_{find_get,put} functions - * */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* ip_conntrack_lock protects the main hash table, protocol/helper/expected - registrations, conntrack timers*/ -#include -#include -#include -#include - -#define IP_CONNTRACK_VERSION "2.4" - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -DEFINE_RWLOCK(ip_conntrack_lock); - -/* ip_conntrack_standalone needs this */ -atomic_t ip_conntrack_count = ATOMIC_INIT(0); - -void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; -LIST_HEAD(ip_conntrack_expect_list); -struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO] __read_mostly; -static LIST_HEAD(helpers); -unsigned int ip_conntrack_htable_size __read_mostly = 0; -int ip_conntrack_max __read_mostly; -struct list_head *ip_conntrack_hash __read_mostly; -static struct kmem_cache *ip_conntrack_cachep __read_mostly; -static struct kmem_cache *ip_conntrack_expect_cachep __read_mostly; -struct ip_conntrack ip_conntrack_untracked; -unsigned int ip_ct_log_invalid __read_mostly; -static LIST_HEAD(unconfirmed); -static int ip_conntrack_vmalloc __read_mostly; - -static unsigned int ip_conntrack_next_id; -static unsigned int ip_conntrack_expect_next_id; -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -ATOMIC_NOTIFIER_HEAD(ip_conntrack_chain); -ATOMIC_NOTIFIER_HEAD(ip_conntrack_expect_chain); - -DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); - -/* deliver cached events and clear cache entry - must be called with locally - * disabled softirqs */ -static inline void -__ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ecache) -{ - DEBUGP("ecache: delivering events for %p\n", ecache->ct); - if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events) - atomic_notifier_call_chain(&ip_conntrack_chain, ecache->events, - ecache->ct); - ecache->events = 0; - ip_conntrack_put(ecache->ct); - ecache->ct = NULL; -} - -/* Deliver all cached events for a particular conntrack. This is called - * by code prior to async packet handling or freeing the skb */ -void ip_ct_deliver_cached_events(const struct ip_conntrack *ct) -{ - struct ip_conntrack_ecache *ecache; - - local_bh_disable(); - ecache = &__get_cpu_var(ip_conntrack_ecache); - if (ecache->ct == ct) - __ip_ct_deliver_cached_events(ecache); - local_bh_enable(); -} - -void __ip_ct_event_cache_init(struct ip_conntrack *ct) -{ - struct ip_conntrack_ecache *ecache; - - /* take care of delivering potentially old events */ - ecache = &__get_cpu_var(ip_conntrack_ecache); - BUG_ON(ecache->ct == ct); - if (ecache->ct) - __ip_ct_deliver_cached_events(ecache); - /* initialize for this conntrack/packet */ - ecache->ct = ct; - nf_conntrack_get(&ct->ct_general); -} - -/* flush the event cache - touches other CPU's data and must not be called while - * packets are still passing through the code */ -static void ip_ct_event_cache_flush(void) -{ - struct ip_conntrack_ecache *ecache; - int cpu; - - for_each_possible_cpu(cpu) { - ecache = &per_cpu(ip_conntrack_ecache, cpu); - if (ecache->ct) - ip_conntrack_put(ecache->ct); - } -} -#else -static inline void ip_ct_event_cache_flush(void) {} -#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ - -DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat); - -static int ip_conntrack_hash_rnd_initted; -static unsigned int ip_conntrack_hash_rnd; - -static u_int32_t __hash_conntrack(const struct ip_conntrack_tuple *tuple, - unsigned int size, unsigned int rnd) -{ - return (jhash_3words((__force u32)tuple->src.ip, - ((__force u32)tuple->dst.ip ^ tuple->dst.protonum), - (tuple->src.u.all | (tuple->dst.u.all << 16)), - rnd) % size); -} - -static u_int32_t -hash_conntrack(const struct ip_conntrack_tuple *tuple) -{ - return __hash_conntrack(tuple, ip_conntrack_htable_size, - ip_conntrack_hash_rnd); -} - -int -ip_ct_get_tuple(const struct iphdr *iph, - const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_protocol *protocol) -{ - /* Never happen */ - if (iph->frag_off & htons(IP_OFFSET)) { - printk("ip_conntrack_core: Frag of proto %u.\n", - iph->protocol); - return 0; - } - - tuple->src.ip = iph->saddr; - tuple->dst.ip = iph->daddr; - tuple->dst.protonum = iph->protocol; - tuple->dst.dir = IP_CT_DIR_ORIGINAL; - - return protocol->pkt_to_tuple(skb, dataoff, tuple); -} - -int -ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig, - const struct ip_conntrack_protocol *protocol) -{ - inverse->src.ip = orig->dst.ip; - inverse->dst.ip = orig->src.ip; - inverse->dst.protonum = orig->dst.protonum; - inverse->dst.dir = !orig->dst.dir; - - return protocol->invert_tuple(inverse, orig); -} - - -/* ip_conntrack_expect helper functions */ -void ip_ct_unlink_expect(struct ip_conntrack_expect *exp) -{ - IP_NF_ASSERT(!timer_pending(&exp->timeout)); - list_del(&exp->list); - CONNTRACK_STAT_INC(expect_delete); - exp->master->expecting--; - ip_conntrack_expect_put(exp); -} - -static void expectation_timed_out(unsigned long ul_expect) -{ - struct ip_conntrack_expect *exp = (void *)ul_expect; - - write_lock_bh(&ip_conntrack_lock); - ip_ct_unlink_expect(exp); - write_unlock_bh(&ip_conntrack_lock); - ip_conntrack_expect_put(exp); -} - -struct ip_conntrack_expect * -__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_expect *i; - - list_for_each_entry(i, &ip_conntrack_expect_list, list) { - if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) - return i; - } - return NULL; -} - -/* Just find a expectation corresponding to a tuple. */ -struct ip_conntrack_expect * -ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_expect *i; - - read_lock_bh(&ip_conntrack_lock); - i = __ip_conntrack_expect_find(tuple); - if (i) - atomic_inc(&i->use); - read_unlock_bh(&ip_conntrack_lock); - - return i; -} - -/* If an expectation for this connection is found, it gets delete from - * global list then returned. */ -static struct ip_conntrack_expect * -find_expectation(const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_expect *i; - - list_for_each_entry(i, &ip_conntrack_expect_list, list) { - /* If master is not in hash table yet (ie. packet hasn't left - this machine yet), how can other end know about expected? - Hence these are not the droids you are looking for (if - master ct never got confirmed, we'd hold a reference to it - and weird things would happen to future packets). */ - if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) - && is_confirmed(i->master)) { - if (i->flags & IP_CT_EXPECT_PERMANENT) { - atomic_inc(&i->use); - return i; - } else if (del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - return i; - } - } - } - return NULL; -} - -/* delete all expectations for this conntrack */ -void ip_ct_remove_expectations(struct ip_conntrack *ct) -{ - struct ip_conntrack_expect *i, *tmp; - - /* Optimization: most connection never expect any others. */ - if (ct->expecting == 0) - return; - - list_for_each_entry_safe(i, tmp, &ip_conntrack_expect_list, list) { - if (i->master == ct && del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - ip_conntrack_expect_put(i); - } - } -} - -static void -clean_from_lists(struct ip_conntrack *ct) -{ - DEBUGP("clean_from_lists(%p)\n", ct); - list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); - list_del(&ct->tuplehash[IP_CT_DIR_REPLY].list); - - /* Destroy all pending expectations */ - ip_ct_remove_expectations(ct); -} - -static void -destroy_conntrack(struct nf_conntrack *nfct) -{ - struct ip_conntrack *ct = (struct ip_conntrack *)nfct; - struct ip_conntrack_protocol *proto; - struct ip_conntrack_helper *helper; - typeof(ip_conntrack_destroyed) destroyed; - - DEBUGP("destroy_conntrack(%p)\n", ct); - IP_NF_ASSERT(atomic_read(&nfct->use) == 0); - IP_NF_ASSERT(!timer_pending(&ct->timeout)); - - ip_conntrack_event(IPCT_DESTROY, ct); - set_bit(IPS_DYING_BIT, &ct->status); - - helper = ct->helper; - if (helper && helper->destroy) - helper->destroy(ct); - - /* To make sure we don't get any weird locking issues here: - * destroy_conntrack() MUST NOT be called with a write lock - * to ip_conntrack_lock!!! -HW */ - rcu_read_lock(); - proto = __ip_conntrack_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); - if (proto && proto->destroy) - proto->destroy(ct); - - destroyed = rcu_dereference(ip_conntrack_destroyed); - if (destroyed) - destroyed(ct); - - rcu_read_unlock(); - - write_lock_bh(&ip_conntrack_lock); - /* Expectations will have been removed in clean_from_lists, - * except TFTP can create an expectation on the first packet, - * before connection is in the list, so we need to clean here, - * too. */ - ip_ct_remove_expectations(ct); - - /* We overload first tuple to link into unconfirmed list. */ - if (!is_confirmed(ct)) { - BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list)); - list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); - } - - CONNTRACK_STAT_INC(delete); - write_unlock_bh(&ip_conntrack_lock); - - if (ct->master) - ip_conntrack_put(ct->master); - - DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); - ip_conntrack_free(ct); -} - -static void death_by_timeout(unsigned long ul_conntrack) -{ - struct ip_conntrack *ct = (void *)ul_conntrack; - - write_lock_bh(&ip_conntrack_lock); - /* Inside lock so preempt is disabled on module removal path. - * Otherwise we can get spurious warnings. */ - CONNTRACK_STAT_INC(delete_list); - clean_from_lists(ct); - write_unlock_bh(&ip_conntrack_lock); - ip_conntrack_put(ct); -} - -struct ip_conntrack_tuple_hash * -__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - struct ip_conntrack_tuple_hash *h; - unsigned int hash = hash_conntrack(tuple); - - list_for_each_entry(h, &ip_conntrack_hash[hash], list) { - if (tuplehash_to_ctrack(h) != ignored_conntrack && - ip_ct_tuple_equal(tuple, &h->tuple)) { - CONNTRACK_STAT_INC(found); - return h; - } - CONNTRACK_STAT_INC(searched); - } - - return NULL; -} - -/* Find a connection corresponding to a tuple. */ -struct ip_conntrack_tuple_hash * -ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - struct ip_conntrack_tuple_hash *h; - - read_lock_bh(&ip_conntrack_lock); - h = __ip_conntrack_find(tuple, ignored_conntrack); - if (h) - atomic_inc(&tuplehash_to_ctrack(h)->ct_general.use); - read_unlock_bh(&ip_conntrack_lock); - - return h; -} - -static void __ip_conntrack_hash_insert(struct ip_conntrack *ct, - unsigned int hash, - unsigned int repl_hash) -{ - ct->id = ++ip_conntrack_next_id; - list_add(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list, - &ip_conntrack_hash[hash]); - list_add(&ct->tuplehash[IP_CT_DIR_REPLY].list, - &ip_conntrack_hash[repl_hash]); -} - -void ip_conntrack_hash_insert(struct ip_conntrack *ct) -{ - unsigned int hash, repl_hash; - - hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - write_lock_bh(&ip_conntrack_lock); - __ip_conntrack_hash_insert(ct, hash, repl_hash); - write_unlock_bh(&ip_conntrack_lock); -} - -/* Confirm a connection given skb; places it in hash table */ -int -__ip_conntrack_confirm(struct sk_buff **pskb) -{ - unsigned int hash, repl_hash; - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - ct = ip_conntrack_get(*pskb, &ctinfo); - - /* ipt_REJECT uses ip_conntrack_attach to attach related - ICMP/TCP RST packets in other direction. Actual packet - which created connection will be IP_CT_NEW or for an - expected connection, IP_CT_RELATED. */ - if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) - return NF_ACCEPT; - - hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - /* We're not in hash table, and we refuse to set up related - connections for unconfirmed conns. But packet copies and - REJECT will give spurious warnings here. */ - /* IP_NF_ASSERT(atomic_read(&ct->ct_general.use) == 1); */ - - /* No external references means noone else could have - confirmed us. */ - IP_NF_ASSERT(!is_confirmed(ct)); - DEBUGP("Confirming conntrack %p\n", ct); - - write_lock_bh(&ip_conntrack_lock); - - /* See if there's one in the list already, including reverse: - NAT could have grabbed it without realizing, since we're - not in the hash. If there is, we lost race. */ - list_for_each_entry(h, &ip_conntrack_hash[hash], list) - if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - &h->tuple)) - goto out; - list_for_each_entry(h, &ip_conntrack_hash[repl_hash], list) - if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, - &h->tuple)) - goto out; - - /* Remove from unconfirmed list */ - list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); - - __ip_conntrack_hash_insert(ct, hash, repl_hash); - /* Timer relative to confirmation time, not original - setting time, otherwise we'd get timer wrap in - weird delay cases. */ - ct->timeout.expires += jiffies; - add_timer(&ct->timeout); - atomic_inc(&ct->ct_general.use); - set_bit(IPS_CONFIRMED_BIT, &ct->status); - CONNTRACK_STAT_INC(insert); - write_unlock_bh(&ip_conntrack_lock); - if (ct->helper) - ip_conntrack_event_cache(IPCT_HELPER, *pskb); -#ifdef CONFIG_IP_NF_NAT_NEEDED - if (test_bit(IPS_SRC_NAT_DONE_BIT, &ct->status) || - test_bit(IPS_DST_NAT_DONE_BIT, &ct->status)) - ip_conntrack_event_cache(IPCT_NATINFO, *pskb); -#endif - ip_conntrack_event_cache(master_ct(ct) ? - IPCT_RELATED : IPCT_NEW, *pskb); - - return NF_ACCEPT; - -out: - CONNTRACK_STAT_INC(insert_failed); - write_unlock_bh(&ip_conntrack_lock); - return NF_DROP; -} - -/* Returns true if a connection correspondings to the tuple (required - for NAT). */ -int -ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - struct ip_conntrack_tuple_hash *h; - - read_lock_bh(&ip_conntrack_lock); - h = __ip_conntrack_find(tuple, ignored_conntrack); - read_unlock_bh(&ip_conntrack_lock); - - return h != NULL; -} - -/* There's a small race here where we may free a just-assured - connection. Too bad: we're in trouble anyway. */ -static int early_drop(struct list_head *chain) -{ - /* Traverse backwards: gives us oldest, which is roughly LRU */ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct = NULL, *tmp; - int dropped = 0; - - read_lock_bh(&ip_conntrack_lock); - list_for_each_entry_reverse(h, chain, list) { - tmp = tuplehash_to_ctrack(h); - if (!test_bit(IPS_ASSURED_BIT, &tmp->status)) { - ct = tmp; - atomic_inc(&ct->ct_general.use); - break; - } - } - read_unlock_bh(&ip_conntrack_lock); - - if (!ct) - return dropped; - - if (del_timer(&ct->timeout)) { - death_by_timeout((unsigned long)ct); - dropped = 1; - CONNTRACK_STAT_INC_ATOMIC(early_drop); - } - ip_conntrack_put(ct); - return dropped; -} - -static struct ip_conntrack_helper * -__ip_conntrack_helper_find( const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_helper *h; - - list_for_each_entry(h, &helpers, list) { - if (ip_ct_tuple_mask_cmp(tuple, &h->tuple, &h->mask)) - return h; - } - return NULL; -} - -struct ip_conntrack_helper * -ip_conntrack_helper_find_get( const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_helper *helper; - - /* need ip_conntrack_lock to assure that helper exists until - * try_module_get() is called */ - read_lock_bh(&ip_conntrack_lock); - - helper = __ip_conntrack_helper_find(tuple); - if (helper) { - /* need to increase module usage count to assure helper will - * not go away while the caller is e.g. busy putting a - * conntrack in the hash that uses the helper */ - if (!try_module_get(helper->me)) - helper = NULL; - } - - read_unlock_bh(&ip_conntrack_lock); - - return helper; -} - -void ip_conntrack_helper_put(struct ip_conntrack_helper *helper) -{ - module_put(helper->me); -} - -struct ip_conntrack_protocol * -__ip_conntrack_proto_find(u_int8_t protocol) -{ - return ip_ct_protos[protocol]; -} - -/* this is guaranteed to always return a valid protocol helper, since - * it falls back to generic_protocol */ -struct ip_conntrack_protocol * -ip_conntrack_proto_find_get(u_int8_t protocol) -{ - struct ip_conntrack_protocol *p; - - rcu_read_lock(); - p = __ip_conntrack_proto_find(protocol); - if (p) { - if (!try_module_get(p->me)) - p = &ip_conntrack_generic_protocol; - } - rcu_read_unlock(); - - return p; -} - -void ip_conntrack_proto_put(struct ip_conntrack_protocol *p) -{ - module_put(p->me); -} - -struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *repl) -{ - struct ip_conntrack *conntrack; - - if (!ip_conntrack_hash_rnd_initted) { - get_random_bytes(&ip_conntrack_hash_rnd, 4); - ip_conntrack_hash_rnd_initted = 1; - } - - /* We don't want any race condition at early drop stage */ - atomic_inc(&ip_conntrack_count); - - if (ip_conntrack_max - && atomic_read(&ip_conntrack_count) > ip_conntrack_max) { - unsigned int hash = hash_conntrack(orig); - /* Try dropping from this hash chain. */ - if (!early_drop(&ip_conntrack_hash[hash])) { - atomic_dec(&ip_conntrack_count); - if (net_ratelimit()) - printk(KERN_WARNING - "ip_conntrack: table full, dropping" - " packet.\n"); - return ERR_PTR(-ENOMEM); - } - } - - conntrack = kmem_cache_zalloc(ip_conntrack_cachep, GFP_ATOMIC); - if (!conntrack) { - DEBUGP("Can't allocate conntrack.\n"); - atomic_dec(&ip_conntrack_count); - return ERR_PTR(-ENOMEM); - } - - atomic_set(&conntrack->ct_general.use, 1); - conntrack->ct_general.destroy = destroy_conntrack; - conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig; - conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *repl; - /* Don't set timer yet: wait for confirmation */ - init_timer(&conntrack->timeout); - conntrack->timeout.data = (unsigned long)conntrack; - conntrack->timeout.function = death_by_timeout; - - return conntrack; -} - -void -ip_conntrack_free(struct ip_conntrack *conntrack) -{ - atomic_dec(&ip_conntrack_count); - kmem_cache_free(ip_conntrack_cachep, conntrack); -} - -/* Allocate a new conntrack: we return -ENOMEM if classification - * failed due to stress. Otherwise it really is unclassifiable */ -static struct ip_conntrack_tuple_hash * -init_conntrack(struct ip_conntrack_tuple *tuple, - struct ip_conntrack_protocol *protocol, - struct sk_buff *skb) -{ - struct ip_conntrack *conntrack; - struct ip_conntrack_tuple repl_tuple; - struct ip_conntrack_expect *exp; - - if (!ip_ct_invert_tuple(&repl_tuple, tuple, protocol)) { - DEBUGP("Can't invert tuple.\n"); - return NULL; - } - - conntrack = ip_conntrack_alloc(tuple, &repl_tuple); - if (conntrack == NULL || IS_ERR(conntrack)) - return (struct ip_conntrack_tuple_hash *)conntrack; - - if (!protocol->new(conntrack, skb)) { - ip_conntrack_free(conntrack); - return NULL; - } - - write_lock_bh(&ip_conntrack_lock); - exp = find_expectation(tuple); - - if (exp) { - DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", - conntrack, exp); - /* Welcome, Mr. Bond. We've been expecting you... */ - __set_bit(IPS_EXPECTED_BIT, &conntrack->status); - conntrack->master = exp->master; -#ifdef CONFIG_IP_NF_CONNTRACK_MARK - conntrack->mark = exp->master->mark; -#endif -#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ - defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) - /* this is ugly, but there is no other place where to put it */ - conntrack->nat.masq_index = exp->master->nat.masq_index; -#endif -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK - conntrack->secmark = exp->master->secmark; -#endif - nf_conntrack_get(&conntrack->master->ct_general); - CONNTRACK_STAT_INC(expect_new); - } else { - conntrack->helper = __ip_conntrack_helper_find(&repl_tuple); - - CONNTRACK_STAT_INC(new); - } - - /* Overload tuple linked list to put us in unconfirmed list. */ - list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed); - - write_unlock_bh(&ip_conntrack_lock); - - if (exp) { - if (exp->expectfn) - exp->expectfn(conntrack, exp); - ip_conntrack_expect_put(exp); - } - - return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; -} - -/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ -static inline struct ip_conntrack * -resolve_normal_ct(struct sk_buff *skb, - struct ip_conntrack_protocol *proto, - int *set_reply, - unsigned int hooknum, - enum ip_conntrack_info *ctinfo) -{ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct; - - IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0); - - if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4, - &tuple,proto)) - return NULL; - - /* look for tuple match */ - h = ip_conntrack_find_get(&tuple, NULL); - if (!h) { - h = init_conntrack(&tuple, proto, skb); - if (!h) - return NULL; - if (IS_ERR(h)) - return (void *)h; - } - ct = tuplehash_to_ctrack(h); - - /* It exists; we have (non-exclusive) reference. */ - if (DIRECTION(h) == IP_CT_DIR_REPLY) { - *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; - /* Please set reply bit if this packet OK */ - *set_reply = 1; - } else { - /* Once we've had two way comms, always ESTABLISHED. */ - if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { - DEBUGP("ip_conntrack_in: normal packet for %p\n", - ct); - *ctinfo = IP_CT_ESTABLISHED; - } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { - DEBUGP("ip_conntrack_in: related packet for %p\n", - ct); - *ctinfo = IP_CT_RELATED; - } else { - DEBUGP("ip_conntrack_in: new packet for %p\n", - ct); - *ctinfo = IP_CT_NEW; - } - *set_reply = 0; - } - skb->nfct = &ct->ct_general; - skb->nfctinfo = *ctinfo; - return ct; -} - -/* Netfilter hook itself. */ -unsigned int ip_conntrack_in(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - struct ip_conntrack_protocol *proto; - int set_reply = 0; - int ret; - - /* Previously seen (loopback or untracked)? Ignore. */ - if ((*pskb)->nfct) { - CONNTRACK_STAT_INC_ATOMIC(ignore); - return NF_ACCEPT; - } - - /* Never happen */ - if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) { - if (net_ratelimit()) { - printk(KERN_ERR "ip_conntrack_in: Frag of proto %u (hook=%u)\n", - (*pskb)->nh.iph->protocol, hooknum); - } - return NF_DROP; - } - -/* Doesn't cover locally-generated broadcast, so not worth it. */ -#if 0 - /* Ignore broadcast: no `connection'. */ - if ((*pskb)->pkt_type == PACKET_BROADCAST) { - printk("Broadcast packet!\n"); - return NF_ACCEPT; - } else if (((*pskb)->nh.iph->daddr & htonl(0x000000FF)) - == htonl(0x000000FF)) { - printk("Should bcast: %u.%u.%u.%u->%u.%u.%u.%u (sk=%p, ptype=%u)\n", - NIPQUAD((*pskb)->nh.iph->saddr), - NIPQUAD((*pskb)->nh.iph->daddr), - (*pskb)->sk, (*pskb)->pkt_type); - } -#endif - - /* rcu_read_lock()ed by nf_hook_slow */ - proto = __ip_conntrack_proto_find((*pskb)->nh.iph->protocol); - - /* It may be an special packet, error, unclean... - * inverse of the return code tells to the netfilter - * core what to do with the packet. */ - if (proto->error != NULL - && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) { - CONNTRACK_STAT_INC_ATOMIC(error); - CONNTRACK_STAT_INC_ATOMIC(invalid); - return -ret; - } - - if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) { - /* Not valid part of a connection */ - CONNTRACK_STAT_INC_ATOMIC(invalid); - return NF_ACCEPT; - } - - if (IS_ERR(ct)) { - /* Too stressed to deal. */ - CONNTRACK_STAT_INC_ATOMIC(drop); - return NF_DROP; - } - - IP_NF_ASSERT((*pskb)->nfct); - - ret = proto->packet(ct, *pskb, ctinfo); - if (ret < 0) { - /* Invalid: inverse of the return code tells - * the netfilter core what to do*/ - nf_conntrack_put((*pskb)->nfct); - (*pskb)->nfct = NULL; - CONNTRACK_STAT_INC_ATOMIC(invalid); - return -ret; - } - - if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) - ip_conntrack_event_cache(IPCT_STATUS, *pskb); - - return ret; -} - -int invert_tuplepr(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig) -{ - struct ip_conntrack_protocol *proto; - int ret; - - rcu_read_lock(); - proto = __ip_conntrack_proto_find(orig->dst.protonum); - ret = ip_ct_invert_tuple(inverse, orig, proto); - rcu_read_unlock(); - - return ret; -} - -/* Would two expected things clash? */ -static inline int expect_clash(const struct ip_conntrack_expect *a, - const struct ip_conntrack_expect *b) -{ - /* Part covered by intersection of masks must be unequal, - otherwise they clash */ - struct ip_conntrack_tuple intersect_mask - = { { a->mask.src.ip & b->mask.src.ip, - { a->mask.src.u.all & b->mask.src.u.all } }, - { a->mask.dst.ip & b->mask.dst.ip, - { a->mask.dst.u.all & b->mask.dst.u.all }, - a->mask.dst.protonum & b->mask.dst.protonum } }; - - return ip_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask); -} - -static inline int expect_matches(const struct ip_conntrack_expect *a, - const struct ip_conntrack_expect *b) -{ - return a->master == b->master - && ip_ct_tuple_equal(&a->tuple, &b->tuple) - && ip_ct_tuple_equal(&a->mask, &b->mask); -} - -/* Generally a bad idea to call this: could have matched already. */ -void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp) -{ - struct ip_conntrack_expect *i; - - write_lock_bh(&ip_conntrack_lock); - /* choose the the oldest expectation to evict */ - list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) { - if (expect_matches(i, exp) && del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - write_unlock_bh(&ip_conntrack_lock); - ip_conntrack_expect_put(i); - return; - } - } - write_unlock_bh(&ip_conntrack_lock); -} - -/* We don't increase the master conntrack refcount for non-fulfilled - * conntracks. During the conntrack destruction, the expectations are - * always killed before the conntrack itself */ -struct ip_conntrack_expect *ip_conntrack_expect_alloc(struct ip_conntrack *me) -{ - struct ip_conntrack_expect *new; - - new = kmem_cache_alloc(ip_conntrack_expect_cachep, GFP_ATOMIC); - if (!new) { - DEBUGP("expect_related: OOM allocating expect\n"); - return NULL; - } - new->master = me; - atomic_set(&new->use, 1); - return new; -} - -void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) -{ - if (atomic_dec_and_test(&exp->use)) - kmem_cache_free(ip_conntrack_expect_cachep, exp); -} - -static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp) -{ - atomic_inc(&exp->use); - exp->master->expecting++; - list_add(&exp->list, &ip_conntrack_expect_list); - - init_timer(&exp->timeout); - exp->timeout.data = (unsigned long)exp; - exp->timeout.function = expectation_timed_out; - exp->timeout.expires = jiffies + exp->master->helper->timeout * HZ; - add_timer(&exp->timeout); - - exp->id = ++ip_conntrack_expect_next_id; - atomic_inc(&exp->use); - CONNTRACK_STAT_INC(expect_create); -} - -/* Race with expectations being used means we could have none to find; OK. */ -static void evict_oldest_expect(struct ip_conntrack *master) -{ - struct ip_conntrack_expect *i; - - list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) { - if (i->master == master) { - if (del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - ip_conntrack_expect_put(i); - } - break; - } - } -} - -static inline int refresh_timer(struct ip_conntrack_expect *i) -{ - if (!del_timer(&i->timeout)) - return 0; - - i->timeout.expires = jiffies + i->master->helper->timeout*HZ; - add_timer(&i->timeout); - return 1; -} - -int ip_conntrack_expect_related(struct ip_conntrack_expect *expect) -{ - struct ip_conntrack_expect *i; - int ret; - - DEBUGP("ip_conntrack_expect_related %p\n", related_to); - DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); - DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); - - write_lock_bh(&ip_conntrack_lock); - list_for_each_entry(i, &ip_conntrack_expect_list, list) { - if (expect_matches(i, expect)) { - /* Refresh timer: if it's dying, ignore.. */ - if (refresh_timer(i)) { - ret = 0; - goto out; - } - } else if (expect_clash(i, expect)) { - ret = -EBUSY; - goto out; - } - } - - /* Will be over limit? */ - if (expect->master->helper->max_expected && - expect->master->expecting >= expect->master->helper->max_expected) - evict_oldest_expect(expect->master); - - ip_conntrack_expect_insert(expect); - ip_conntrack_expect_event(IPEXP_NEW, expect); - ret = 0; -out: - write_unlock_bh(&ip_conntrack_lock); - return ret; -} - -/* Alter reply tuple (maybe alter helper). This is for NAT, and is - implicitly racy: see __ip_conntrack_confirm */ -void ip_conntrack_alter_reply(struct ip_conntrack *conntrack, - const struct ip_conntrack_tuple *newreply) -{ - write_lock_bh(&ip_conntrack_lock); - /* Should be unconfirmed, so not in hash table yet */ - IP_NF_ASSERT(!is_confirmed(conntrack)); - - DEBUGP("Altering reply tuple of %p to ", conntrack); - DUMP_TUPLE(newreply); - - conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; - if (!conntrack->master && conntrack->expecting == 0) - conntrack->helper = __ip_conntrack_helper_find(newreply); - write_unlock_bh(&ip_conntrack_lock); -} - -int ip_conntrack_helper_register(struct ip_conntrack_helper *me) -{ - BUG_ON(me->timeout == 0); - write_lock_bh(&ip_conntrack_lock); - list_add(&me->list, &helpers); - write_unlock_bh(&ip_conntrack_lock); - - return 0; -} - -struct ip_conntrack_helper * -__ip_conntrack_helper_find_byname(const char *name) -{ - struct ip_conntrack_helper *h; - - list_for_each_entry(h, &helpers, list) { - if (!strcmp(h->name, name)) - return h; - } - - return NULL; -} - -static inline void unhelp(struct ip_conntrack_tuple_hash *i, - const struct ip_conntrack_helper *me) -{ - if (tuplehash_to_ctrack(i)->helper == me) { - ip_conntrack_event(IPCT_HELPER, tuplehash_to_ctrack(i)); - tuplehash_to_ctrack(i)->helper = NULL; - } -} - -void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me) -{ - unsigned int i; - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_expect *exp, *tmp; - - /* Need write lock here, to delete helper. */ - write_lock_bh(&ip_conntrack_lock); - list_del(&me->list); - - /* Get rid of expectations */ - list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) { - if (exp->master->helper == me && del_timer(&exp->timeout)) { - ip_ct_unlink_expect(exp); - ip_conntrack_expect_put(exp); - } - } - /* Get rid of expecteds, set helpers to NULL. */ - list_for_each_entry(h, &unconfirmed, list) - unhelp(h, me); - for (i = 0; i < ip_conntrack_htable_size; i++) { - list_for_each_entry(h, &ip_conntrack_hash[i], list) - unhelp(h, me); - } - write_unlock_bh(&ip_conntrack_lock); - - /* Someone could be still looking at the helper in a bh. */ - synchronize_net(); -} - -/* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */ -void __ip_ct_refresh_acct(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies, - int do_acct) -{ - int event = 0; - - IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); - IP_NF_ASSERT(skb); - - write_lock_bh(&ip_conntrack_lock); - - /* Only update if this is not a fixed timeout */ - if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) { - write_unlock_bh(&ip_conntrack_lock); - return; - } - - /* If not in hash table, timer will not be active yet */ - if (!is_confirmed(ct)) { - ct->timeout.expires = extra_jiffies; - event = IPCT_REFRESH; - } else { - /* Need del_timer for race avoidance (may already be dying). */ - if (del_timer(&ct->timeout)) { - ct->timeout.expires = jiffies + extra_jiffies; - add_timer(&ct->timeout); - event = IPCT_REFRESH; - } - } - -#ifdef CONFIG_IP_NF_CT_ACCT - if (do_acct) { - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - ntohs(skb->nh.iph->tot_len); - if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000) - || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000)) - event |= IPCT_COUNTER_FILLING; - } -#endif - - write_unlock_bh(&ip_conntrack_lock); - - /* must be unlocked when calling event cache */ - if (event) - ip_conntrack_event_cache(event, skb); -} - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -/* Generic function for tcp/udp/sctp/dccp and alike. This needs to be - * in ip_conntrack_core, since we don't want the protocols to autoload - * or depend on ctnetlink */ -int ip_ct_port_tuple_to_nfattr(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple) -{ - NFA_PUT(skb, CTA_PROTO_SRC_PORT, sizeof(__be16), - &tuple->src.u.tcp.port); - NFA_PUT(skb, CTA_PROTO_DST_PORT, sizeof(__be16), - &tuple->dst.u.tcp.port); - return 0; - -nfattr_failure: - return -1; -} - -int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[], - struct ip_conntrack_tuple *t) -{ - if (!tb[CTA_PROTO_SRC_PORT-1] || !tb[CTA_PROTO_DST_PORT-1]) - return -EINVAL; - - t->src.u.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]); - t->dst.u.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]); - - return 0; -} -#endif - -/* Returns new sk_buff, or NULL */ -struct sk_buff * -ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user) -{ - skb_orphan(skb); - - local_bh_disable(); - skb = ip_defrag(skb, user); - local_bh_enable(); - - if (skb) - ip_send_check(skb->nh.iph); - return skb; -} - -/* Used by ipt_REJECT. */ -static void ip_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - /* This ICMP is in reverse direction to the packet which caused it */ - ct = ip_conntrack_get(skb, &ctinfo); - - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) - ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY; - else - ctinfo = IP_CT_RELATED; - - /* Attach to new skbuff, and increment count */ - nskb->nfct = &ct->ct_general; - nskb->nfctinfo = ctinfo; - nf_conntrack_get(nskb->nfct); -} - -/* Bring out ya dead! */ -static struct ip_conntrack * -get_next_corpse(int (*iter)(struct ip_conntrack *i, void *data), - void *data, unsigned int *bucket) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct; - - write_lock_bh(&ip_conntrack_lock); - for (; *bucket < ip_conntrack_htable_size; (*bucket)++) { - list_for_each_entry(h, &ip_conntrack_hash[*bucket], list) { - ct = tuplehash_to_ctrack(h); - if (iter(ct, data)) - goto found; - } - } - list_for_each_entry(h, &unconfirmed, list) { - ct = tuplehash_to_ctrack(h); - if (iter(ct, data)) - set_bit(IPS_DYING_BIT, &ct->status); - } - write_unlock_bh(&ip_conntrack_lock); - return NULL; - -found: - atomic_inc(&ct->ct_general.use); - write_unlock_bh(&ip_conntrack_lock); - return ct; -} - -void -ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *), void *data) -{ - struct ip_conntrack *ct; - unsigned int bucket = 0; - - while ((ct = get_next_corpse(iter, data, &bucket)) != NULL) { - /* Time to push up daises... */ - if (del_timer(&ct->timeout)) - death_by_timeout((unsigned long)ct); - /* ... else the timer will get him soon. */ - - ip_conntrack_put(ct); - } -} - -/* Fast function for those who don't want to parse /proc (and I don't - blame them). */ -/* Reversing the socket's dst/src point of view gives us the reply - mapping. */ -static int -getorigdst(struct sock *sk, int optval, void __user *user, int *len) -{ - struct inet_sock *inet = inet_sk(sk); - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_tuple tuple; - - IP_CT_TUPLE_U_BLANK(&tuple); - tuple.src.ip = inet->rcv_saddr; - tuple.src.u.tcp.port = inet->sport; - tuple.dst.ip = inet->daddr; - tuple.dst.u.tcp.port = inet->dport; - tuple.dst.protonum = IPPROTO_TCP; - - /* We only do TCP at the moment: is there a better way? */ - if (strcmp(sk->sk_prot->name, "TCP")) { - DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n"); - return -ENOPROTOOPT; - } - - if ((unsigned int) *len < sizeof(struct sockaddr_in)) { - DEBUGP("SO_ORIGINAL_DST: len %u not %u\n", - *len, sizeof(struct sockaddr_in)); - return -EINVAL; - } - - h = ip_conntrack_find_get(&tuple, NULL); - if (h) { - struct sockaddr_in sin; - struct ip_conntrack *ct = tuplehash_to_ctrack(h); - - sin.sin_family = AF_INET; - sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple.dst.u.tcp.port; - sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple.dst.ip; - memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); - - DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n", - NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port)); - ip_conntrack_put(ct); - if (copy_to_user(user, &sin, sizeof(sin)) != 0) - return -EFAULT; - else - return 0; - } - DEBUGP("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n", - NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), - NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); - return -ENOENT; -} - -static struct nf_sockopt_ops so_getorigdst = { - .pf = PF_INET, - .get_optmin = SO_ORIGINAL_DST, - .get_optmax = SO_ORIGINAL_DST+1, - .get = &getorigdst, -}; - -static int kill_all(struct ip_conntrack *i, void *data) -{ - return 1; -} - -void ip_conntrack_flush(void) -{ - ip_ct_iterate_cleanup(kill_all, NULL); -} - -static void free_conntrack_hash(struct list_head *hash, int vmalloced,int size) -{ - if (vmalloced) - vfree(hash); - else - free_pages((unsigned long)hash, - get_order(sizeof(struct list_head) * size)); -} - -/* Mishearing the voices in his head, our hero wonders how he's - supposed to kill the mall. */ -void ip_conntrack_cleanup(void) -{ - rcu_assign_pointer(ip_ct_attach, NULL); - - /* This makes sure all current packets have passed through - netfilter framework. Roll on, two-stage module - delete... */ - synchronize_net(); - - ip_ct_event_cache_flush(); - i_see_dead_people: - ip_conntrack_flush(); - if (atomic_read(&ip_conntrack_count) != 0) { - schedule(); - goto i_see_dead_people; - } - /* wait until all references to ip_conntrack_untracked are dropped */ - while (atomic_read(&ip_conntrack_untracked.ct_general.use) > 1) - schedule(); - - kmem_cache_destroy(ip_conntrack_cachep); - kmem_cache_destroy(ip_conntrack_expect_cachep); - free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc, - ip_conntrack_htable_size); - nf_unregister_sockopt(&so_getorigdst); -} - -static struct list_head *alloc_hashtable(int size, int *vmalloced) -{ - struct list_head *hash; - unsigned int i; - - *vmalloced = 0; - hash = (void*)__get_free_pages(GFP_KERNEL, - get_order(sizeof(struct list_head) - * size)); - if (!hash) { - *vmalloced = 1; - printk(KERN_WARNING"ip_conntrack: falling back to vmalloc.\n"); - hash = vmalloc(sizeof(struct list_head) * size); - } - - if (hash) - for (i = 0; i < size; i++) - INIT_LIST_HEAD(&hash[i]); - - return hash; -} - -static int set_hashsize(const char *val, struct kernel_param *kp) -{ - int i, bucket, hashsize, vmalloced; - int old_vmalloced, old_size; - int rnd; - struct list_head *hash, *old_hash; - struct ip_conntrack_tuple_hash *h; - - /* On boot, we can set this without any fancy locking. */ - if (!ip_conntrack_htable_size) - return param_set_int(val, kp); - - hashsize = simple_strtol(val, NULL, 0); - if (!hashsize) - return -EINVAL; - - hash = alloc_hashtable(hashsize, &vmalloced); - if (!hash) - return -ENOMEM; - - /* We have to rehash for the new table anyway, so we also can - * use a new random seed */ - get_random_bytes(&rnd, 4); - - write_lock_bh(&ip_conntrack_lock); - for (i = 0; i < ip_conntrack_htable_size; i++) { - while (!list_empty(&ip_conntrack_hash[i])) { - h = list_entry(ip_conntrack_hash[i].next, - struct ip_conntrack_tuple_hash, list); - list_del(&h->list); - bucket = __hash_conntrack(&h->tuple, hashsize, rnd); - list_add_tail(&h->list, &hash[bucket]); - } - } - old_size = ip_conntrack_htable_size; - old_vmalloced = ip_conntrack_vmalloc; - old_hash = ip_conntrack_hash; - - ip_conntrack_htable_size = hashsize; - ip_conntrack_vmalloc = vmalloced; - ip_conntrack_hash = hash; - ip_conntrack_hash_rnd = rnd; - write_unlock_bh(&ip_conntrack_lock); - - free_conntrack_hash(old_hash, old_vmalloced, old_size); - return 0; -} - -module_param_call(hashsize, set_hashsize, param_get_uint, - &ip_conntrack_htable_size, 0600); - -int __init ip_conntrack_init(void) -{ - unsigned int i; - int ret; - - /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB - * machine has 256 buckets. >= 1GB machines have 8192 buckets. */ - if (!ip_conntrack_htable_size) { - ip_conntrack_htable_size - = (((num_physpages << PAGE_SHIFT) / 16384) - / sizeof(struct list_head)); - if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) - ip_conntrack_htable_size = 8192; - if (ip_conntrack_htable_size < 16) - ip_conntrack_htable_size = 16; - } - ip_conntrack_max = 8 * ip_conntrack_htable_size; - - printk("ip_conntrack version %s (%u buckets, %d max)" - " - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION, - ip_conntrack_htable_size, ip_conntrack_max, - sizeof(struct ip_conntrack)); - - ret = nf_register_sockopt(&so_getorigdst); - if (ret != 0) { - printk(KERN_ERR "Unable to register netfilter socket option\n"); - return ret; - } - - ip_conntrack_hash = alloc_hashtable(ip_conntrack_htable_size, - &ip_conntrack_vmalloc); - if (!ip_conntrack_hash) { - printk(KERN_ERR "Unable to create ip_conntrack_hash\n"); - goto err_unreg_sockopt; - } - - ip_conntrack_cachep = kmem_cache_create("ip_conntrack", - sizeof(struct ip_conntrack), 0, - 0, NULL, NULL); - if (!ip_conntrack_cachep) { - printk(KERN_ERR "Unable to create ip_conntrack slab cache\n"); - goto err_free_hash; - } - - ip_conntrack_expect_cachep = kmem_cache_create("ip_conntrack_expect", - sizeof(struct ip_conntrack_expect), - 0, 0, NULL, NULL); - if (!ip_conntrack_expect_cachep) { - printk(KERN_ERR "Unable to create ip_expect slab cache\n"); - goto err_free_conntrack_slab; - } - - /* Don't NEED lock here, but good form anyway. */ - write_lock_bh(&ip_conntrack_lock); - for (i = 0; i < MAX_IP_CT_PROTO; i++) - rcu_assign_pointer(ip_ct_protos[i], &ip_conntrack_generic_protocol); - /* Sew in builtin protocols. */ - rcu_assign_pointer(ip_ct_protos[IPPROTO_TCP], &ip_conntrack_protocol_tcp); - rcu_assign_pointer(ip_ct_protos[IPPROTO_UDP], &ip_conntrack_protocol_udp); - rcu_assign_pointer(ip_ct_protos[IPPROTO_ICMP], &ip_conntrack_protocol_icmp); - write_unlock_bh(&ip_conntrack_lock); - - /* For use by ipt_REJECT */ - rcu_assign_pointer(ip_ct_attach, ip_conntrack_attach); - - /* Set up fake conntrack: - - to never be deleted, not in any hashes */ - atomic_set(&ip_conntrack_untracked.ct_general.use, 1); - /* - and look it like as a confirmed connection */ - set_bit(IPS_CONFIRMED_BIT, &ip_conntrack_untracked.status); - - return ret; - -err_free_conntrack_slab: - kmem_cache_destroy(ip_conntrack_cachep); -err_free_hash: - free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc, - ip_conntrack_htable_size); -err_unreg_sockopt: - nf_unregister_sockopt(&so_getorigdst); - - return -ENOMEM; -} diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c deleted file mode 100644 index 1faa68a..0000000 --- a/net/ipv4/netfilter/ip_conntrack_ftp.c +++ /dev/null @@ -1,520 +0,0 @@ -/* FTP extension for IP connection tracking. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Rusty Russell "); -MODULE_DESCRIPTION("ftp connection tracking helper"); - -/* This is slow, but it's simple. --RR */ -static char *ftp_buffer; -static DEFINE_SPINLOCK(ip_ftp_lock); - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); - -static int loose; -module_param(loose, bool, 0600); - -unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - enum ip_ct_ftp_type type, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp, - u32 *seq); -EXPORT_SYMBOL_GPL(ip_nat_ftp_hook); - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -static int try_rfc959(const char *, size_t, u_int32_t [], char); -static int try_eprt(const char *, size_t, u_int32_t [], char); -static int try_epsv_response(const char *, size_t, u_int32_t [], char); - -static const struct ftp_search { - const char *pattern; - size_t plen; - char skip; - char term; - enum ip_ct_ftp_type ftptype; - int (*getnum)(const char *, size_t, u_int32_t[], char); -} search[IP_CT_DIR_MAX][2] = { - [IP_CT_DIR_ORIGINAL] = { - { - .pattern = "PORT", - .plen = sizeof("PORT") - 1, - .skip = ' ', - .term = '\r', - .ftptype = IP_CT_FTP_PORT, - .getnum = try_rfc959, - }, - { - .pattern = "EPRT", - .plen = sizeof("EPRT") - 1, - .skip = ' ', - .term = '\r', - .ftptype = IP_CT_FTP_EPRT, - .getnum = try_eprt, - }, - }, - [IP_CT_DIR_REPLY] = { - { - .pattern = "227 ", - .plen = sizeof("227 ") - 1, - .skip = '(', - .term = ')', - .ftptype = IP_CT_FTP_PASV, - .getnum = try_rfc959, - }, - { - .pattern = "229 ", - .plen = sizeof("229 ") - 1, - .skip = '(', - .term = ')', - .ftptype = IP_CT_FTP_EPSV, - .getnum = try_epsv_response, - }, - }, -}; - -static int try_number(const char *data, size_t dlen, u_int32_t array[], - int array_size, char sep, char term) -{ - u_int32_t i, len; - - memset(array, 0, sizeof(array[0])*array_size); - - /* Keep data pointing at next char. */ - for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { - if (*data >= '0' && *data <= '9') { - array[i] = array[i]*10 + *data - '0'; - } - else if (*data == sep) - i++; - else { - /* Unexpected character; true if it's the - terminator and we're finished. */ - if (*data == term && i == array_size - 1) - return len; - - DEBUGP("Char %u (got %u nums) `%u' unexpected\n", - len, i, *data); - return 0; - } - } - DEBUGP("Failed to fill %u numbers separated by %c\n", array_size, sep); - - return 0; -} - -/* Returns 0, or length of numbers: 192,168,1,1,5,6 */ -static int try_rfc959(const char *data, size_t dlen, u_int32_t array[6], - char term) -{ - return try_number(data, dlen, array, 6, ',', term); -} - -/* Grab port: number up to delimiter */ -static int get_port(const char *data, int start, size_t dlen, char delim, - u_int32_t array[2]) -{ - u_int16_t port = 0; - int i; - - for (i = start; i < dlen; i++) { - /* Finished? */ - if (data[i] == delim) { - if (port == 0) - break; - array[0] = port >> 8; - array[1] = port; - return i + 1; - } - else if (data[i] >= '0' && data[i] <= '9') - port = port*10 + data[i] - '0'; - else /* Some other crap */ - break; - } - return 0; -} - -/* Returns 0, or length of numbers: |1|132.235.1.2|6275| */ -static int try_eprt(const char *data, size_t dlen, u_int32_t array[6], - char term) -{ - char delim; - int length; - - /* First character is delimiter, then "1" for IPv4, then - delimiter again. */ - if (dlen <= 3) return 0; - delim = data[0]; - if (isdigit(delim) || delim < 33 || delim > 126 - || data[1] != '1' || data[2] != delim) - return 0; - - DEBUGP("EPRT: Got |1|!\n"); - /* Now we have IP address. */ - length = try_number(data + 3, dlen - 3, array, 4, '.', delim); - if (length == 0) - return 0; - - DEBUGP("EPRT: Got IP address!\n"); - /* Start offset includes initial "|1|", and trailing delimiter */ - return get_port(data, 3 + length + 1, dlen, delim, array+4); -} - -/* Returns 0, or length of numbers: |||6446| */ -static int try_epsv_response(const char *data, size_t dlen, u_int32_t array[6], - char term) -{ - char delim; - - /* Three delimiters. */ - if (dlen <= 3) return 0; - delim = data[0]; - if (isdigit(delim) || delim < 33 || delim > 126 - || data[1] != delim || data[2] != delim) - return 0; - - return get_port(data, 3, dlen, delim, array+4); -} - -/* Return 1 for match, 0 for accept, -1 for partial. */ -static int find_pattern(const char *data, size_t dlen, - const char *pattern, size_t plen, - char skip, char term, - unsigned int *numoff, - unsigned int *numlen, - u_int32_t array[6], - int (*getnum)(const char *, size_t, u_int32_t[], char)) -{ - size_t i; - - DEBUGP("find_pattern `%s': dlen = %u\n", pattern, dlen); - if (dlen == 0) - return 0; - - if (dlen <= plen) { - /* Short packet: try for partial? */ - if (strnicmp(data, pattern, dlen) == 0) - return -1; - else return 0; - } - - if (strnicmp(data, pattern, plen) != 0) { -#if 0 - size_t i; - - DEBUGP("ftp: string mismatch\n"); - for (i = 0; i < plen; i++) { - DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n", - i, data[i], data[i], - pattern[i], pattern[i]); - } -#endif - return 0; - } - - DEBUGP("Pattern matches!\n"); - /* Now we've found the constant string, try to skip - to the 'skip' character */ - for (i = plen; data[i] != skip; i++) - if (i == dlen - 1) return -1; - - /* Skip over the last character */ - i++; - - DEBUGP("Skipped up to `%c'!\n", skip); - - *numoff = i; - *numlen = getnum(data + i, dlen - i, array, term); - if (!*numlen) - return -1; - - DEBUGP("Match succeeded!\n"); - return 1; -} - -/* Look up to see if we're just after a \n. */ -static int find_nl_seq(u32 seq, const struct ip_ct_ftp_master *info, int dir) -{ - unsigned int i; - - for (i = 0; i < info->seq_aft_nl_num[dir]; i++) - if (info->seq_aft_nl[dir][i] == seq) - return 1; - return 0; -} - -/* We don't update if it's older than what we have. */ -static void update_nl_seq(u32 nl_seq, struct ip_ct_ftp_master *info, int dir, - struct sk_buff *skb) -{ - unsigned int i, oldest = NUM_SEQ_TO_REMEMBER; - - /* Look for oldest: if we find exact match, we're done. */ - for (i = 0; i < info->seq_aft_nl_num[dir]; i++) { - if (info->seq_aft_nl[dir][i] == nl_seq) - return; - - if (oldest == info->seq_aft_nl_num[dir] - || before(info->seq_aft_nl[dir][i], oldest)) - oldest = i; - } - - if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) { - info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq; - ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb); - } else if (oldest != NUM_SEQ_TO_REMEMBER) { - info->seq_aft_nl[dir][oldest] = nl_seq; - ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb); - } -} - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dataoff, datalen; - struct tcphdr _tcph, *th; - char *fb_ptr; - int ret; - u32 seq, array[6] = { 0 }; - int dir = CTINFO2DIR(ctinfo); - unsigned int matchlen, matchoff; - struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; - struct ip_conntrack_expect *exp; - unsigned int i; - int found = 0, ends_in_nl; - typeof(ip_nat_ftp_hook) ip_nat_ftp; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { - DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo); - return NF_ACCEPT; - } - - th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, - sizeof(_tcph), &_tcph); - if (th == NULL) - return NF_ACCEPT; - - dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4; - /* No data? */ - if (dataoff >= (*pskb)->len) { - DEBUGP("ftp: pskblen = %u\n", (*pskb)->len); - return NF_ACCEPT; - } - datalen = (*pskb)->len - dataoff; - - spin_lock_bh(&ip_ftp_lock); - fb_ptr = skb_header_pointer(*pskb, dataoff, - (*pskb)->len - dataoff, ftp_buffer); - BUG_ON(fb_ptr == NULL); - - ends_in_nl = (fb_ptr[datalen - 1] == '\n'); - seq = ntohl(th->seq) + datalen; - - /* Look up to see if we're just after a \n. */ - if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) { - /* Now if this ends in \n, update ftp info. */ - DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n", - ct_ftp_info->seq_aft_nl[0][dir] - old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl); - ret = NF_ACCEPT; - goto out_update_nl; - } - - /* Initialize IP array to expected address (it's not mentioned - in EPSV responses) */ - array[0] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 24) & 0xFF; - array[1] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 16) & 0xFF; - array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF; - array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF; - - for (i = 0; i < ARRAY_SIZE(search[dir]); i++) { - found = find_pattern(fb_ptr, (*pskb)->len - dataoff, - search[dir][i].pattern, - search[dir][i].plen, - search[dir][i].skip, - search[dir][i].term, - &matchoff, &matchlen, - array, - search[dir][i].getnum); - if (found) break; - } - if (found == -1) { - /* We don't usually drop packets. After all, this is - connection tracking, not packet filtering. - However, it is necessary for accurate tracking in - this case. */ - if (net_ratelimit()) - printk("conntrack_ftp: partial %s %u+%u\n", - search[dir][i].pattern, - ntohl(th->seq), datalen); - ret = NF_DROP; - goto out; - } else if (found == 0) { /* No match */ - ret = NF_ACCEPT; - goto out_update_nl; - } - - DEBUGP("conntrack_ftp: match `%s' (%u bytes at %u)\n", - fb_ptr + matchoff, matchlen, ntohl(th->seq) + matchoff); - - /* Allocate expectation which will be inserted */ - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) { - ret = NF_DROP; - goto out; - } - - /* We refer to the reverse direction ("!dir") tuples here, - * because we're expecting something in the other direction. - * Doesn't matter unless NAT is happening. */ - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - - if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) - != ct->tuplehash[dir].tuple.src.ip) { - /* Enrico Scholz's passive FTP to partially RNAT'd ftp - server: it really wants us to connect to a - different IP address. Simply don't record it for - NAT. */ - DEBUGP("conntrack_ftp: NOT RECORDING: %u,%u,%u,%u != %u.%u.%u.%u\n", - array[0], array[1], array[2], array[3], - NIPQUAD(ct->tuplehash[dir].tuple.src.ip)); - - /* Thanks to Cristiano Lincoln Mattos - for reporting this potential - problem (DMZ machines opening holes to internal - networks, or the packet filter itself). */ - if (!loose) { - ret = NF_ACCEPT; - goto out_put_expect; - } - exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16) - | (array[2] << 8) | array[3]); - } - - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.dst.u.tcp.port = htons(array[4] << 8 | array[5]); - exp->tuple.src.u.tcp.port = 0; /* Don't care. */ - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask = ((struct ip_conntrack_tuple) - { { htonl(0xFFFFFFFF), { 0 } }, - { htonl(0xFFFFFFFF), { .tcp = { htons(0xFFFF) } }, 0xFF }}); - - exp->expectfn = NULL; - exp->flags = 0; - - /* Now, NAT might want to mangle the packet, and register the - * (possibly changed) expectation itself. */ - ip_nat_ftp = rcu_dereference(ip_nat_ftp_hook); - if (ip_nat_ftp) - ret = ip_nat_ftp(pskb, ctinfo, search[dir][i].ftptype, - matchoff, matchlen, exp, &seq); - else { - /* Can't expect this? Best to drop packet now. */ - if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - else - ret = NF_ACCEPT; - } - -out_put_expect: - ip_conntrack_expect_put(exp); - -out_update_nl: - /* Now if this ends in \n, update ftp info. Seq may have been - * adjusted by NAT code. */ - if (ends_in_nl) - update_nl_seq(seq, ct_ftp_info,dir, *pskb); - out: - spin_unlock_bh(&ip_ftp_lock); - return ret; -} - -static struct ip_conntrack_helper ftp[MAX_PORTS]; -static char ftp_names[MAX_PORTS][sizeof("ftp-65535")]; - -/* Not __exit: called from init() */ -static void ip_conntrack_ftp_fini(void) -{ - int i; - for (i = 0; i < ports_c; i++) { - DEBUGP("ip_ct_ftp: unregistering helper for port %d\n", - ports[i]); - ip_conntrack_helper_unregister(&ftp[i]); - } - - kfree(ftp_buffer); -} - -static int __init ip_conntrack_ftp_init(void) -{ - int i, ret; - char *tmpname; - - ftp_buffer = kmalloc(65536, GFP_KERNEL); - if (!ftp_buffer) - return -ENOMEM; - - if (ports_c == 0) - ports[ports_c++] = FTP_PORT; - - for (i = 0; i < ports_c; i++) { - ftp[i].tuple.src.u.tcp.port = htons(ports[i]); - ftp[i].tuple.dst.protonum = IPPROTO_TCP; - ftp[i].mask.src.u.tcp.port = htons(0xFFFF); - ftp[i].mask.dst.protonum = 0xFF; - ftp[i].max_expected = 1; - ftp[i].timeout = 5 * 60; /* 5 minutes */ - ftp[i].me = THIS_MODULE; - ftp[i].help = help; - - tmpname = &ftp_names[i][0]; - if (ports[i] == FTP_PORT) - sprintf(tmpname, "ftp"); - else - sprintf(tmpname, "ftp-%d", ports[i]); - ftp[i].name = tmpname; - - DEBUGP("ip_ct_ftp: registering helper for port %d\n", - ports[i]); - ret = ip_conntrack_helper_register(&ftp[i]); - - if (ret) { - ip_conntrack_ftp_fini(); - return ret; - } - } - return 0; -} - -module_init(ip_conntrack_ftp_init); -module_exit(ip_conntrack_ftp_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_helper_h323.c b/net/ipv4/netfilter/ip_conntrack_helper_h323.c deleted file mode 100644 index 53eb365..0000000 --- a/net/ipv4/netfilter/ip_conntrack_helper_h323.c +++ /dev/null @@ -1,1841 +0,0 @@ -/* - * H.323 connection tracking helper - * - * Copyright (c) 2006 Jing Min Zhao - * - * This source code is licensed under General Public License version 2. - * - * Based on the 'brute force' H.323 connection tracking module by - * Jozsef Kadlecsik - * - * For more information, please see http://nath323.sourceforge.net/ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -/* Parameters */ -static unsigned int default_rrq_ttl = 300; -module_param(default_rrq_ttl, uint, 0600); -MODULE_PARM_DESC(default_rrq_ttl, "use this TTL if it's missing in RRQ"); - -static int gkrouted_only = 1; -module_param(gkrouted_only, int, 0600); -MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper"); - -static int callforward_filter = 1; -module_param(callforward_filter, bool, 0600); -MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations " - "if both endpoints are on different sides " - "(determined by routing information)"); - -/* Hooks for NAT */ -int (*set_h245_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - __be32 ip, u_int16_t port); -int (*set_h225_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - TransportAddress * addr, - __be32 ip, u_int16_t port); -int (*set_sig_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -int (*set_ras_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -int (*nat_rtp_rtcp_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - u_int16_t port, u_int16_t rtp_port, - struct ip_conntrack_expect * rtp_exp, - struct ip_conntrack_expect * rtcp_exp); -int (*nat_t120_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -int (*nat_h245_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -int (*nat_callforwarding_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -int (*nat_q931_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, TransportAddress * addr, int idx, - u_int16_t port, struct ip_conntrack_expect * exp); - - -static DEFINE_SPINLOCK(ip_h323_lock); -static char *h323_buffer; - -/****************************************************************************/ -static int get_tpkt_data(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int *datalen, int *dataoff) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - struct tcphdr _tcph, *th; - int tcpdatalen; - int tcpdataoff; - unsigned char *tpkt; - int tpktlen; - int tpktoff; - - /* Get TCP header */ - th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4, - sizeof(_tcph), &_tcph); - if (th == NULL) - return 0; - - /* Get TCP data offset */ - tcpdataoff = (*pskb)->nh.iph->ihl * 4 + th->doff * 4; - - /* Get TCP data length */ - tcpdatalen = (*pskb)->len - tcpdataoff; - if (tcpdatalen <= 0) /* No TCP data */ - goto clear_out; - - if (*data == NULL) { /* first TPKT */ - /* Get first TPKT pointer */ - tpkt = skb_header_pointer(*pskb, tcpdataoff, tcpdatalen, - h323_buffer); - BUG_ON(tpkt == NULL); - - /* Validate TPKT identifier */ - if (tcpdatalen < 4 || tpkt[0] != 0x03 || tpkt[1] != 0) { - /* Netmeeting sends TPKT header and data separately */ - if (info->tpkt_len[dir] > 0) { - DEBUGP("ip_ct_h323: previous packet " - "indicated separate TPKT data of %hu " - "bytes\n", info->tpkt_len[dir]); - if (info->tpkt_len[dir] <= tcpdatalen) { - /* Yes, there was a TPKT header - * received */ - *data = tpkt; - *datalen = info->tpkt_len[dir]; - *dataoff = 0; - goto out; - } - - /* Fragmented TPKT */ - if (net_ratelimit()) - printk("ip_ct_h323: " - "fragmented TPKT\n"); - goto clear_out; - } - - /* It is not even a TPKT */ - return 0; - } - tpktoff = 0; - } else { /* Next TPKT */ - tpktoff = *dataoff + *datalen; - tcpdatalen -= tpktoff; - if (tcpdatalen <= 4) /* No more TPKT */ - goto clear_out; - tpkt = *data + *datalen; - - /* Validate TPKT identifier */ - if (tpkt[0] != 0x03 || tpkt[1] != 0) - goto clear_out; - } - - /* Validate TPKT length */ - tpktlen = tpkt[2] * 256 + tpkt[3]; - if (tpktlen < 4) - goto clear_out; - if (tpktlen > tcpdatalen) { - if (tcpdatalen == 4) { /* Separate TPKT header */ - /* Netmeeting sends TPKT header and data separately */ - DEBUGP("ip_ct_h323: separate TPKT header indicates " - "there will be TPKT data of %hu bytes\n", - tpktlen - 4); - info->tpkt_len[dir] = tpktlen - 4; - return 0; - } - - if (net_ratelimit()) - printk("ip_ct_h323: incomplete TPKT (fragmented?)\n"); - goto clear_out; - } - - /* This is the encapsulated data */ - *data = tpkt + 4; - *datalen = tpktlen - 4; - *dataoff = tpktoff + 4; - - out: - /* Clear TPKT length */ - info->tpkt_len[dir] = 0; - return 1; - - clear_out: - info->tpkt_len[dir] = 0; - return 0; -} - -/****************************************************************************/ -static int get_h245_addr(unsigned char *data, H245_TransportAddress * addr, - __be32 * ip, u_int16_t * port) -{ - unsigned char *p; - - if (addr->choice != eH245_TransportAddress_unicastAddress || - addr->unicastAddress.choice != eUnicastAddress_iPAddress) - return 0; - - p = data + addr->unicastAddress.iPAddress.network; - *ip = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); - *port = (p[4] << 8) | (p[5]); - - return 1; -} - -/****************************************************************************/ -static int expect_rtp_rtcp(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - u_int16_t rtp_port; - struct ip_conntrack_expect *rtp_exp; - struct ip_conntrack_expect *rtcp_exp; - typeof(nat_rtp_rtcp_hook) nat_rtp_rtcp; - - /* Read RTP or RTCP address */ - if (!get_h245_addr(*data, addr, &ip, &port) || - ip != ct->tuplehash[dir].tuple.src.ip || port == 0) - return 0; - - /* RTP port is even */ - rtp_port = port & (~1); - - /* Create expect for RTP */ - if ((rtp_exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - rtp_exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - rtp_exp->tuple.src.u.udp.port = 0; - rtp_exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - rtp_exp->tuple.dst.u.udp.port = htons(rtp_port); - rtp_exp->tuple.dst.protonum = IPPROTO_UDP; - rtp_exp->mask.src.ip = htonl(0xFFFFFFFF); - rtp_exp->mask.src.u.udp.port = 0; - rtp_exp->mask.dst.ip = htonl(0xFFFFFFFF); - rtp_exp->mask.dst.u.udp.port = htons(0xFFFF); - rtp_exp->mask.dst.protonum = 0xFF; - rtp_exp->flags = 0; - - /* Create expect for RTCP */ - if ((rtcp_exp = ip_conntrack_expect_alloc(ct)) == NULL) { - ip_conntrack_expect_put(rtp_exp); - return -1; - } - rtcp_exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - rtcp_exp->tuple.src.u.udp.port = 0; - rtcp_exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - rtcp_exp->tuple.dst.u.udp.port = htons(rtp_port + 1); - rtcp_exp->tuple.dst.protonum = IPPROTO_UDP; - rtcp_exp->mask.src.ip = htonl(0xFFFFFFFF); - rtcp_exp->mask.src.u.udp.port = 0; - rtcp_exp->mask.dst.ip = htonl(0xFFFFFFFF); - rtcp_exp->mask.dst.u.udp.port = htons(0xFFFF); - rtcp_exp->mask.dst.protonum = 0xFF; - rtcp_exp->flags = 0; - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_rtp_rtcp = rcu_dereference(nat_rtp_rtcp_hook))) { - /* NAT needed */ - ret = nat_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - addr, port, rtp_port, rtp_exp, rtcp_exp); - } else { /* Conntrack only */ - rtp_exp->expectfn = NULL; - rtcp_exp->expectfn = NULL; - - if (ip_conntrack_expect_related(rtp_exp) == 0) { - if (ip_conntrack_expect_related(rtcp_exp) == 0) { - DEBUGP("ip_ct_h323: expect RTP " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtp_exp->tuple.src.ip), - ntohs(rtp_exp->tuple.src.u.udp.port), - NIPQUAD(rtp_exp->tuple.dst.ip), - ntohs(rtp_exp->tuple.dst.u.udp.port)); - DEBUGP("ip_ct_h323: expect RTCP " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtcp_exp->tuple.src.ip), - ntohs(rtcp_exp->tuple.src.u.udp.port), - NIPQUAD(rtcp_exp->tuple.dst.ip), - ntohs(rtcp_exp->tuple.dst.u.udp.port)); - } else { - ip_conntrack_unexpect_related(rtp_exp); - ret = -1; - } - } else - ret = -1; - } - - ip_conntrack_expect_put(rtp_exp); - ip_conntrack_expect_put(rtcp_exp); - - return ret; -} - -/****************************************************************************/ -static int expect_t120(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - typeof(nat_t120_hook) nat_t120; - - /* Read T.120 address */ - if (!get_h245_addr(*data, addr, &ip, &port) || - ip != ct->tuplehash[dir].tuple.src.ip || port == 0) - return 0; - - /* Create expect for T.120 connections */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; /* Accept multiple channels */ - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_t120 = rcu_dereference(nat_t120_hook))) { - /* NAT needed */ - ret = nat_t120(pskb, ct, ctinfo, data, dataoff, addr, - port, exp); - } else { /* Conntrack only */ - exp->expectfn = NULL; - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_h323: expect T.120 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_h245_channel(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H2250LogicalChannelParameters * channel) -{ - int ret; - - if (channel->options & eH2250LogicalChannelParameters_mediaChannel) { - /* RTP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &channel->mediaChannel); - if (ret < 0) - return -1; - } - - if (channel-> - options & eH2250LogicalChannelParameters_mediaControlChannel) { - /* RTCP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &channel->mediaControlChannel); - if (ret < 0) - return -1; - } - - return 0; -} - -/****************************************************************************/ -static int process_olc(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - OpenLogicalChannel * olc) -{ - int ret; - - DEBUGP("ip_ct_h323: OpenLogicalChannel\n"); - - if (olc->forwardLogicalChannelParameters.multiplexParameters.choice == - eOpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters) - { - ret = process_h245_channel(pskb, ct, ctinfo, data, dataoff, - &olc-> - forwardLogicalChannelParameters. - multiplexParameters. - h2250LogicalChannelParameters); - if (ret < 0) - return -1; - } - - if ((olc->options & - eOpenLogicalChannel_reverseLogicalChannelParameters) && - (olc->reverseLogicalChannelParameters.options & - eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters) - && (olc->reverseLogicalChannelParameters.multiplexParameters. - choice == - eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) - { - ret = - process_h245_channel(pskb, ct, ctinfo, data, dataoff, - &olc-> - reverseLogicalChannelParameters. - multiplexParameters. - h2250LogicalChannelParameters); - if (ret < 0) - return -1; - } - - if ((olc->options & eOpenLogicalChannel_separateStack) && - olc->forwardLogicalChannelParameters.dataType.choice == - eDataType_data && - olc->forwardLogicalChannelParameters.dataType.data.application. - choice == eDataApplicationCapability_application_t120 && - olc->forwardLogicalChannelParameters.dataType.data.application. - t120.choice == eDataProtocolCapability_separateLANStack && - olc->separateStack.networkAddress.choice == - eNetworkAccessParameters_networkAddress_localAreaAddress) { - ret = expect_t120(pskb, ct, ctinfo, data, dataoff, - &olc->separateStack.networkAddress. - localAreaAddress); - if (ret < 0) - return -1; - } - - return 0; -} - -/****************************************************************************/ -static int process_olca(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - OpenLogicalChannelAck * olca) -{ - H2250LogicalChannelAckParameters *ack; - int ret; - - DEBUGP("ip_ct_h323: OpenLogicalChannelAck\n"); - - if ((olca->options & - eOpenLogicalChannelAck_reverseLogicalChannelParameters) && - (olca->reverseLogicalChannelParameters.options & - eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters) - && (olca->reverseLogicalChannelParameters.multiplexParameters. - choice == - eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) - { - ret = process_h245_channel(pskb, ct, ctinfo, data, dataoff, - &olca-> - reverseLogicalChannelParameters. - multiplexParameters. - h2250LogicalChannelParameters); - if (ret < 0) - return -1; - } - - if ((olca->options & - eOpenLogicalChannelAck_forwardMultiplexAckParameters) && - (olca->forwardMultiplexAckParameters.choice == - eOpenLogicalChannelAck_forwardMultiplexAckParameters_h2250LogicalChannelAckParameters)) - { - ack = &olca->forwardMultiplexAckParameters. - h2250LogicalChannelAckParameters; - if (ack->options & - eH2250LogicalChannelAckParameters_mediaChannel) { - /* RTP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &ack->mediaChannel); - if (ret < 0) - return -1; - } - - if (ack->options & - eH2250LogicalChannelAckParameters_mediaControlChannel) { - /* RTCP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &ack->mediaControlChannel); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_h245(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - MultimediaSystemControlMessage * mscm) -{ - switch (mscm->choice) { - case eMultimediaSystemControlMessage_request: - if (mscm->request.choice == - eRequestMessage_openLogicalChannel) { - return process_olc(pskb, ct, ctinfo, data, dataoff, - &mscm->request.openLogicalChannel); - } - DEBUGP("ip_ct_h323: H.245 Request %d\n", - mscm->request.choice); - break; - case eMultimediaSystemControlMessage_response: - if (mscm->response.choice == - eResponseMessage_openLogicalChannelAck) { - return process_olca(pskb, ct, ctinfo, data, dataoff, - &mscm->response. - openLogicalChannelAck); - } - DEBUGP("ip_ct_h323: H.245 Response %d\n", - mscm->response.choice); - break; - default: - DEBUGP("ip_ct_h323: H.245 signal %d\n", mscm->choice); - break; - } - - return 0; -} - -/****************************************************************************/ -static int h245_help(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - static MultimediaSystemControlMessage mscm; - unsigned char *data = NULL; - int datalen; - int dataoff; - int ret; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { - return NF_ACCEPT; - } - DEBUGP("ip_ct_h245: skblen = %u\n", (*pskb)->len); - - spin_lock_bh(&ip_h323_lock); - - /* Process each TPKT */ - while (get_tpkt_data(pskb, ct, ctinfo, &data, &datalen, &dataoff)) { - DEBUGP("ip_ct_h245: TPKT %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n", - NIPQUAD((*pskb)->nh.iph->saddr), - NIPQUAD((*pskb)->nh.iph->daddr), datalen); - - /* Decode H.245 signal */ - ret = DecodeMultimediaSystemControlMessage(data, datalen, - &mscm); - if (ret < 0) { - if (net_ratelimit()) - printk("ip_ct_h245: decoding error: %s\n", - ret == H323_ERROR_BOUND ? - "out of bound" : "out of range"); - /* We don't drop when decoding error */ - break; - } - - /* Process H.245 signal */ - if (process_h245(pskb, ct, ctinfo, &data, dataoff, &mscm) < 0) - goto drop; - } - - spin_unlock_bh(&ip_h323_lock); - return NF_ACCEPT; - - drop: - spin_unlock_bh(&ip_h323_lock); - if (net_ratelimit()) - printk("ip_ct_h245: packet dropped\n"); - return NF_DROP; -} - -/****************************************************************************/ -static struct ip_conntrack_helper ip_conntrack_helper_h245 = { - .name = "H.245", - .me = THIS_MODULE, - .max_expected = H323_RTP_CHANNEL_MAX * 4 + 2 /* T.120 */ , - .timeout = 240, - .tuple = {.dst = {.protonum = IPPROTO_TCP}}, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}}, - .help = h245_help -}; - -/****************************************************************************/ -void ip_conntrack_h245_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - write_lock_bh(&ip_conntrack_lock); - new->helper = &ip_conntrack_helper_h245; - write_unlock_bh(&ip_conntrack_lock); -} - -/****************************************************************************/ -int get_h225_addr(unsigned char *data, TransportAddress * addr, - __be32 * ip, u_int16_t * port) -{ - unsigned char *p; - - if (addr->choice != eTransportAddress_ipAddress) - return 0; - - p = data + addr->ipAddress.ip; - *ip = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); - *port = (p[4] << 8) | (p[5]); - - return 1; -} - -/****************************************************************************/ -static int expect_h245(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - typeof(nat_h245_hook) nat_h245; - - /* Read h245Address */ - if (!get_h225_addr(*data, addr, &ip, &port) || - ip != ct->tuplehash[dir].tuple.src.ip || port == 0) - return 0; - - /* Create expect for h245 connection */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = 0; - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_h245 = rcu_dereference(nat_h245_hook))) { - /* NAT needed */ - ret = nat_h245(pskb, ct, ctinfo, data, dataoff, addr, - port, exp); - } else { /* Conntrack only */ - exp->expectfn = ip_conntrack_h245_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_q931: expect H.245 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/* Forwarding declaration */ -void ip_conntrack_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); - -/****************************************************************************/ -static int expect_callforwarding(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - typeof(nat_callforwarding_hook) nat_callforwarding; - - /* Read alternativeAddress */ - if (!get_h225_addr(*data, addr, &ip, &port) || port == 0) - return 0; - - /* If the calling party is on the same side of the forward-to party, - * we don't need to track the second call */ - if (callforward_filter) { - struct rtable *rt1, *rt2; - struct flowi fl1 = { - .fl4_dst = ip, - }; - struct flowi fl2 = { - .fl4_dst = ct->tuplehash[!dir].tuple.src.ip, - }; - - if (ip_route_output_key(&rt1, &fl1) == 0) { - if (ip_route_output_key(&rt2, &fl2) == 0) { - if (rt1->rt_gateway == rt2->rt_gateway && - rt1->u.dst.dev == rt2->u.dst.dev) - ret = 1; - dst_release(&rt2->u.dst); - } - dst_release(&rt1->u.dst); - } - if (ret) { - DEBUGP("ip_ct_q931: Call Forwarding not tracked\n"); - return 0; - } - } - - /* Create expect for the second call leg */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = 0; - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_callforwarding = rcu_dereference(nat_callforwarding_hook))) { - /* Need NAT */ - ret = nat_callforwarding(pskb, ct, ctinfo, data, dataoff, - addr, port, exp); - } else { /* Conntrack only */ - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_q931: expect Call Forwarding " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_setup(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Setup_UUIE * setup) -{ - int dir = CTINFO2DIR(ctinfo); - int ret; - int i; - __be32 ip; - u_int16_t port; - typeof(set_h225_addr_hook) set_h225_addr; - - DEBUGP("ip_ct_q931: Setup\n"); - - if (setup->options & eSetup_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &setup->h245Address); - if (ret < 0) - return -1; - } - - set_h225_addr = rcu_dereference(set_h225_addr_hook); - - if ((setup->options & eSetup_UUIE_destCallSignalAddress) && - (set_h225_addr) && - get_h225_addr(*data, &setup->destCallSignalAddress, &ip, &port) && - ip != ct->tuplehash[!dir].tuple.src.ip) { - DEBUGP("ip_ct_q931: set destCallSignalAddress " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), - ntohs(ct->tuplehash[!dir].tuple.src.u.tcp.port)); - ret = set_h225_addr(pskb, data, dataoff, - &setup->destCallSignalAddress, - ct->tuplehash[!dir].tuple.src.ip, - ntohs(ct->tuplehash[!dir].tuple.src. - u.tcp.port)); - if (ret < 0) - return -1; - } - - if ((setup->options & eSetup_UUIE_sourceCallSignalAddress) && - (set_h225_addr) && - get_h225_addr(*data, &setup->sourceCallSignalAddress, &ip, &port) - && ip != ct->tuplehash[!dir].tuple.dst.ip) { - DEBUGP("ip_ct_q931: set sourceCallSignalAddress " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.dst.ip), - ntohs(ct->tuplehash[!dir].tuple.dst.u.tcp.port)); - ret = set_h225_addr(pskb, data, dataoff, - &setup->sourceCallSignalAddress, - ct->tuplehash[!dir].tuple.dst.ip, - ntohs(ct->tuplehash[!dir].tuple.dst. - u.tcp.port)); - if (ret < 0) - return -1; - } - - if (setup->options & eSetup_UUIE_fastStart) { - for (i = 0; i < setup->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &setup->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_callproceeding(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - CallProceeding_UUIE * callproc) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: CallProceeding\n"); - - if (callproc->options & eCallProceeding_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &callproc->h245Address); - if (ret < 0) - return -1; - } - - if (callproc->options & eCallProceeding_UUIE_fastStart) { - for (i = 0; i < callproc->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &callproc->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_connect(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Connect_UUIE * connect) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Connect\n"); - - if (connect->options & eConnect_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &connect->h245Address); - if (ret < 0) - return -1; - } - - if (connect->options & eConnect_UUIE_fastStart) { - for (i = 0; i < connect->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &connect->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_alerting(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Alerting_UUIE * alert) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Alerting\n"); - - if (alert->options & eAlerting_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &alert->h245Address); - if (ret < 0) - return -1; - } - - if (alert->options & eAlerting_UUIE_fastStart) { - for (i = 0; i < alert->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &alert->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_information(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Information_UUIE * info) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Information\n"); - - if (info->options & eInformation_UUIE_fastStart) { - for (i = 0; i < info->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &info->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_facility(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Facility_UUIE * facility) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Facility\n"); - - if (facility->reason.choice == eFacilityReason_callForwarded) { - if (facility->options & eFacility_UUIE_alternativeAddress) - return expect_callforwarding(pskb, ct, ctinfo, data, - dataoff, - &facility-> - alternativeAddress); - return 0; - } - - if (facility->options & eFacility_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &facility->h245Address); - if (ret < 0) - return -1; - } - - if (facility->options & eFacility_UUIE_fastStart) { - for (i = 0; i < facility->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &facility->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_progress(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Progress_UUIE * progress) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Progress\n"); - - if (progress->options & eProgress_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &progress->h245Address); - if (ret < 0) - return -1; - } - - if (progress->options & eProgress_UUIE_fastStart) { - for (i = 0; i < progress->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &progress->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_q931(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, Q931 * q931) -{ - H323_UU_PDU *pdu = &q931->UUIE.h323_uu_pdu; - int i; - int ret = 0; - - switch (pdu->h323_message_body.choice) { - case eH323_UU_PDU_h323_message_body_setup: - ret = process_setup(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.setup); - break; - case eH323_UU_PDU_h323_message_body_callProceeding: - ret = process_callproceeding(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body. - callProceeding); - break; - case eH323_UU_PDU_h323_message_body_connect: - ret = process_connect(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.connect); - break; - case eH323_UU_PDU_h323_message_body_alerting: - ret = process_alerting(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.alerting); - break; - case eH323_UU_PDU_h323_message_body_information: - ret = process_information(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body. - information); - break; - case eH323_UU_PDU_h323_message_body_facility: - ret = process_facility(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.facility); - break; - case eH323_UU_PDU_h323_message_body_progress: - ret = process_progress(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.progress); - break; - default: - DEBUGP("ip_ct_q931: Q.931 signal %d\n", - pdu->h323_message_body.choice); - break; - } - - if (ret < 0) - return -1; - - if (pdu->options & eH323_UU_PDU_h245Control) { - for (i = 0; i < pdu->h245Control.count; i++) { - ret = process_h245(pskb, ct, ctinfo, data, dataoff, - &pdu->h245Control.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int q931_help(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - static Q931 q931; - unsigned char *data = NULL; - int datalen; - int dataoff; - int ret; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { - return NF_ACCEPT; - } - DEBUGP("ip_ct_q931: skblen = %u\n", (*pskb)->len); - - spin_lock_bh(&ip_h323_lock); - - /* Process each TPKT */ - while (get_tpkt_data(pskb, ct, ctinfo, &data, &datalen, &dataoff)) { - DEBUGP("ip_ct_q931: TPKT %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n", - NIPQUAD((*pskb)->nh.iph->saddr), - NIPQUAD((*pskb)->nh.iph->daddr), datalen); - - /* Decode Q.931 signal */ - ret = DecodeQ931(data, datalen, &q931); - if (ret < 0) { - if (net_ratelimit()) - printk("ip_ct_q931: decoding error: %s\n", - ret == H323_ERROR_BOUND ? - "out of bound" : "out of range"); - /* We don't drop when decoding error */ - break; - } - - /* Process Q.931 signal */ - if (process_q931(pskb, ct, ctinfo, &data, dataoff, &q931) < 0) - goto drop; - } - - spin_unlock_bh(&ip_h323_lock); - return NF_ACCEPT; - - drop: - spin_unlock_bh(&ip_h323_lock); - if (net_ratelimit()) - printk("ip_ct_q931: packet dropped\n"); - return NF_DROP; -} - -/****************************************************************************/ -static struct ip_conntrack_helper ip_conntrack_helper_q931 = { - .name = "Q.931", - .me = THIS_MODULE, - .max_expected = H323_RTP_CHANNEL_MAX * 4 + 4 /* T.120 and H.245 */ , - .timeout = 240, - .tuple = {.src = {.u = {.tcp = {.port = __constant_htons(Q931_PORT)}}}, - .dst = {.protonum = IPPROTO_TCP}}, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}}, - .help = q931_help -}; - -/****************************************************************************/ -void ip_conntrack_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - write_lock_bh(&ip_conntrack_lock); - new->helper = &ip_conntrack_helper_q931; - write_unlock_bh(&ip_conntrack_lock); -} - -/****************************************************************************/ -static unsigned char *get_udp_data(struct sk_buff **pskb, int *datalen) -{ - struct udphdr _uh, *uh; - int dataoff; - - uh = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4, sizeof(_uh), - &_uh); - if (uh == NULL) - return NULL; - dataoff = (*pskb)->nh.iph->ihl * 4 + sizeof(_uh); - if (dataoff >= (*pskb)->len) - return NULL; - *datalen = (*pskb)->len - dataoff; - return skb_header_pointer(*pskb, dataoff, *datalen, h323_buffer); -} - -/****************************************************************************/ -static struct ip_conntrack_expect *find_expect(struct ip_conntrack *ct, - __be32 ip, u_int16_t port) -{ - struct ip_conntrack_expect *exp; - struct ip_conntrack_tuple tuple; - - tuple.src.ip = 0; - tuple.src.u.tcp.port = 0; - tuple.dst.ip = ip; - tuple.dst.u.tcp.port = htons(port); - tuple.dst.protonum = IPPROTO_TCP; - - exp = __ip_conntrack_expect_find(&tuple); - if (exp && exp->master == ct) - return exp; - return NULL; -} - -/****************************************************************************/ -static int set_expect_timeout(struct ip_conntrack_expect *exp, - unsigned timeout) -{ - if (!exp || !del_timer(&exp->timeout)) - return 0; - - exp->timeout.expires = jiffies + timeout * HZ; - add_timer(&exp->timeout); - - return 1; -} - -/****************************************************************************/ -static int expect_q931(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - int i; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp; - typeof(nat_q931_hook) nat_q931; - - /* Look for the first related address */ - for (i = 0; i < count; i++) { - if (get_h225_addr(*data, &addr[i], &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && port != 0) - break; - } - - if (i >= count) /* Not found */ - return 0; - - /* Create expect for Q.931 */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = gkrouted_only ? /* only accept calls from GK? */ - ct->tuplehash[!dir].tuple.src.ip : 0; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = gkrouted_only ? htonl(0xFFFFFFFF) : 0; - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; /* Accept multiple calls */ - - nat_q931 = rcu_dereference(nat_q931_hook); - if (nat_q931) { /* Need NAT */ - ret = nat_q931(pskb, ct, ctinfo, data, addr, i, port, exp); - } else { /* Conntrack only */ - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect Q.931 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - - /* Save port for looking up expect in processing RCF */ - info->sig_port[dir] = port; - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_grq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, GatekeeperRequest * grq) -{ - typeof(set_ras_addr_hook) set_ras_addr; - - DEBUGP("ip_ct_ras: GRQ\n"); - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) /* NATed */ - return set_ras_addr(pskb, ct, ctinfo, data, - &grq->rasAddress, 1); - return 0; -} - -/* Declare before using */ -static void ip_conntrack_ras_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); - -/****************************************************************************/ -static int process_gcf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, GatekeeperConfirm * gcf) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp; - - DEBUGP("ip_ct_ras: GCF\n"); - - if (!get_h225_addr(*data, &gcf->rasAddress, &ip, &port)) - return 0; - - /* Registration port is the same as discovery port */ - if (ip == ct->tuplehash[dir].tuple.src.ip && - port == ntohs(ct->tuplehash[dir].tuple.src.u.udp.port)) - return 0; - - /* Avoid RAS expectation loops. A GCF is never expected. */ - if (test_bit(IPS_EXPECTED_BIT, &ct->status)) - return 0; - - /* Need new expect */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_UDP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = 0; - exp->expectfn = ip_conntrack_ras_expect; - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect RAS " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_rrq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, RegistrationRequest * rrq) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int ret; - typeof(set_ras_addr_hook) set_ras_addr; - - DEBUGP("ip_ct_ras: RRQ\n"); - - ret = expect_q931(pskb, ct, ctinfo, data, - rrq->callSignalAddress.item, - rrq->callSignalAddress.count); - if (ret < 0) - return -1; - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) { - ret = set_ras_addr(pskb, ct, ctinfo, data, - rrq->rasAddress.item, - rrq->rasAddress.count); - if (ret < 0) - return -1; - } - - if (rrq->options & eRegistrationRequest_timeToLive) { - DEBUGP("ip_ct_ras: RRQ TTL = %u seconds\n", rrq->timeToLive); - info->timeout = rrq->timeToLive; - } else - info->timeout = default_rrq_ttl; - - return 0; -} - -/****************************************************************************/ -static int process_rcf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, RegistrationConfirm * rcf) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int ret; - struct ip_conntrack_expect *exp; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: RCF\n"); - - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) { - ret = set_sig_addr(pskb, ct, ctinfo, data, - rcf->callSignalAddress.item, - rcf->callSignalAddress.count); - if (ret < 0) - return -1; - } - - if (rcf->options & eRegistrationConfirm_timeToLive) { - DEBUGP("ip_ct_ras: RCF TTL = %u seconds\n", rcf->timeToLive); - info->timeout = rcf->timeToLive; - } - - if (info->timeout > 0) { - DEBUGP - ("ip_ct_ras: set RAS connection timeout to %u seconds\n", - info->timeout); - ip_ct_refresh(ct, *pskb, info->timeout * HZ); - - /* Set expect timeout */ - read_lock_bh(&ip_conntrack_lock); - exp = find_expect(ct, ct->tuplehash[dir].tuple.dst.ip, - info->sig_port[!dir]); - if (exp) { - DEBUGP("ip_ct_ras: set Q.931 expect " - "(%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu) " - "timeout to %u seconds\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port), - info->timeout); - set_expect_timeout(exp, info->timeout); - } - read_unlock_bh(&ip_conntrack_lock); - } - - return 0; -} - -/****************************************************************************/ -static int process_urq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, UnregistrationRequest * urq) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int ret; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: URQ\n"); - - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) { - ret = set_sig_addr(pskb, ct, ctinfo, data, - urq->callSignalAddress.item, - urq->callSignalAddress.count); - if (ret < 0) - return -1; - } - - /* Clear old expect */ - ip_ct_remove_expectations(ct); - info->sig_port[dir] = 0; - info->sig_port[!dir] = 0; - - /* Give it 30 seconds for UCF or URJ */ - ip_ct_refresh(ct, *pskb, 30 * HZ); - - return 0; -} - -/****************************************************************************/ -static int process_arq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, AdmissionRequest * arq) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - __be32 ip; - u_int16_t port; - typeof(set_h225_addr_hook) set_h225_addr; - - DEBUGP("ip_ct_ras: ARQ\n"); - - set_h225_addr = rcu_dereference(set_h225_addr_hook); - if ((arq->options & eAdmissionRequest_destCallSignalAddress) && - get_h225_addr(*data, &arq->destCallSignalAddress, &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && - port == info->sig_port[dir] && set_h225_addr) { - /* Answering ARQ */ - return set_h225_addr(pskb, data, 0, - &arq->destCallSignalAddress, - ct->tuplehash[!dir].tuple.dst.ip, - info->sig_port[!dir]); - } - - if ((arq->options & eAdmissionRequest_srcCallSignalAddress) && - get_h225_addr(*data, &arq->srcCallSignalAddress, &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && set_h225_addr) { - /* Calling ARQ */ - return set_h225_addr(pskb, data, 0, - &arq->srcCallSignalAddress, - ct->tuplehash[!dir].tuple.dst.ip, - port); - } - - return 0; -} - -/****************************************************************************/ -static int process_acf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, AdmissionConfirm * acf) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: ACF\n"); - - if (!get_h225_addr(*data, &acf->destCallSignalAddress, &ip, &port)) - return 0; - - if (ip == ct->tuplehash[dir].tuple.dst.ip) { /* Answering ACF */ - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) - return set_sig_addr(pskb, ct, ctinfo, data, - &acf->destCallSignalAddress, 1); - return 0; - } - - /* Need new expect */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect Q.931 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_lrq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, LocationRequest * lrq) -{ - typeof(set_ras_addr_hook) set_ras_addr; - - DEBUGP("ip_ct_ras: LRQ\n"); - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) - return set_ras_addr(pskb, ct, ctinfo, data, - &lrq->replyAddress, 1); - return 0; -} - -/****************************************************************************/ -static int process_lcf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, LocationConfirm * lcf) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - - DEBUGP("ip_ct_ras: LCF\n"); - - if (!get_h225_addr(*data, &lcf->callSignalAddress, &ip, &port)) - return 0; - - /* Need new expect for call signal */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect Q.931 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - - ip_conntrack_expect_put(exp); - - /* Ignore rasAddress */ - - return ret; -} - -/****************************************************************************/ -static int process_irr(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, InfoRequestResponse * irr) -{ - int ret; - typeof(set_ras_addr_hook) set_ras_addr; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: IRR\n"); - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) { - ret = set_ras_addr(pskb, ct, ctinfo, data, - &irr->rasAddress, 1); - if (ret < 0) - return -1; - } - - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) { - ret = set_sig_addr(pskb, ct, ctinfo, data, - irr->callSignalAddress.item, - irr->callSignalAddress.count); - if (ret < 0) - return -1; - } - - return 0; -} - -/****************************************************************************/ -static int process_ras(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, RasMessage * ras) -{ - switch (ras->choice) { - case eRasMessage_gatekeeperRequest: - return process_grq(pskb, ct, ctinfo, data, - &ras->gatekeeperRequest); - case eRasMessage_gatekeeperConfirm: - return process_gcf(pskb, ct, ctinfo, data, - &ras->gatekeeperConfirm); - case eRasMessage_registrationRequest: - return process_rrq(pskb, ct, ctinfo, data, - &ras->registrationRequest); - case eRasMessage_registrationConfirm: - return process_rcf(pskb, ct, ctinfo, data, - &ras->registrationConfirm); - case eRasMessage_unregistrationRequest: - return process_urq(pskb, ct, ctinfo, data, - &ras->unregistrationRequest); - case eRasMessage_admissionRequest: - return process_arq(pskb, ct, ctinfo, data, - &ras->admissionRequest); - case eRasMessage_admissionConfirm: - return process_acf(pskb, ct, ctinfo, data, - &ras->admissionConfirm); - case eRasMessage_locationRequest: - return process_lrq(pskb, ct, ctinfo, data, - &ras->locationRequest); - case eRasMessage_locationConfirm: - return process_lcf(pskb, ct, ctinfo, data, - &ras->locationConfirm); - case eRasMessage_infoRequestResponse: - return process_irr(pskb, ct, ctinfo, data, - &ras->infoRequestResponse); - default: - DEBUGP("ip_ct_ras: RAS message %d\n", ras->choice); - break; - } - - return 0; -} - -/****************************************************************************/ -static int ras_help(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - static RasMessage ras; - unsigned char *data; - int datalen = 0; - int ret; - - DEBUGP("ip_ct_ras: skblen = %u\n", (*pskb)->len); - - spin_lock_bh(&ip_h323_lock); - - /* Get UDP data */ - data = get_udp_data(pskb, &datalen); - if (data == NULL) - goto accept; - DEBUGP("ip_ct_ras: RAS message %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n", - NIPQUAD((*pskb)->nh.iph->saddr), - NIPQUAD((*pskb)->nh.iph->daddr), datalen); - - /* Decode RAS message */ - ret = DecodeRasMessage(data, datalen, &ras); - if (ret < 0) { - if (net_ratelimit()) - printk("ip_ct_ras: decoding error: %s\n", - ret == H323_ERROR_BOUND ? - "out of bound" : "out of range"); - goto accept; - } - - /* Process RAS message */ - if (process_ras(pskb, ct, ctinfo, &data, &ras) < 0) - goto drop; - - accept: - spin_unlock_bh(&ip_h323_lock); - return NF_ACCEPT; - - drop: - spin_unlock_bh(&ip_h323_lock); - if (net_ratelimit()) - printk("ip_ct_ras: packet dropped\n"); - return NF_DROP; -} - -/****************************************************************************/ -static struct ip_conntrack_helper ip_conntrack_helper_ras = { - .name = "RAS", - .me = THIS_MODULE, - .max_expected = 32, - .timeout = 240, - .tuple = {.src = {.u = {.tcp = {.port = __constant_htons(RAS_PORT)}}}, - .dst = {.protonum = IPPROTO_UDP}}, - .mask = {.src = {.u = {0xFFFE}}, - .dst = {.protonum = 0xFF}}, - .help = ras_help, -}; - -/****************************************************************************/ -static void ip_conntrack_ras_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - write_lock_bh(&ip_conntrack_lock); - new->helper = &ip_conntrack_helper_ras; - write_unlock_bh(&ip_conntrack_lock); -} - -/****************************************************************************/ -/* Not __exit - called from init() */ -static void fini(void) -{ - ip_conntrack_helper_unregister(&ip_conntrack_helper_ras); - ip_conntrack_helper_unregister(&ip_conntrack_helper_q931); - kfree(h323_buffer); - DEBUGP("ip_ct_h323: fini\n"); -} - -/****************************************************************************/ -static int __init init(void) -{ - int ret; - - h323_buffer = kmalloc(65536, GFP_KERNEL); - if (!h323_buffer) - return -ENOMEM; - if ((ret = ip_conntrack_helper_register(&ip_conntrack_helper_q931)) || - (ret = ip_conntrack_helper_register(&ip_conntrack_helper_ras))) { - fini(); - return ret; - } - DEBUGP("ip_ct_h323: init success\n"); - return 0; -} - -/****************************************************************************/ -module_init(init); -module_exit(fini); - -EXPORT_SYMBOL_GPL(get_h225_addr); -EXPORT_SYMBOL_GPL(ip_conntrack_h245_expect); -EXPORT_SYMBOL_GPL(ip_conntrack_q931_expect); -EXPORT_SYMBOL_GPL(set_h245_addr_hook); -EXPORT_SYMBOL_GPL(set_h225_addr_hook); -EXPORT_SYMBOL_GPL(set_sig_addr_hook); -EXPORT_SYMBOL_GPL(set_ras_addr_hook); -EXPORT_SYMBOL_GPL(nat_rtp_rtcp_hook); -EXPORT_SYMBOL_GPL(nat_t120_hook); -EXPORT_SYMBOL_GPL(nat_h245_hook); -EXPORT_SYMBOL_GPL(nat_callforwarding_hook); -EXPORT_SYMBOL_GPL(nat_q931_hook); - -MODULE_AUTHOR("Jing Min Zhao "); -MODULE_DESCRIPTION("H.323 connection tracking helper"); -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c deleted file mode 100644 index 2b760c5..0000000 --- a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c +++ /dev/null @@ -1,684 +0,0 @@ -/* - * ip_conntrack_pptp.c - Version 3.0 - * - * Connection tracking support for PPTP (Point to Point Tunneling Protocol). - * PPTP is a a protocol for creating virtual private networks. - * It is a specification defined by Microsoft and some vendors - * working with Microsoft. PPTP is built on top of a modified - * version of the Internet Generic Routing Encapsulation Protocol. - * GRE is defined in RFC 1701 and RFC 1702. Documentation of - * PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - * Limitations: - * - We blindly assume that control connections are always - * established in PNS->PAC direction. This is a violation - * of RFFC2673 - * - We can only support one single call within each session - * - * TODO: - * - testing of incoming PPTP calls - * - * Changes: - * 2002-02-05 - Version 1.3 - * - Call ip_conntrack_unexpect_related() from - * pptp_destroy_siblings() to destroy expectations in case - * CALL_DISCONNECT_NOTIFY or tcp fin packet was seen - * (Philip Craig ) - * - Add Version information at module loadtime - * 2002-02-10 - Version 1.6 - * - move to C99 style initializers - * - remove second expectation if first arrives - * 2004-10-22 - Version 2.0 - * - merge Mandrake's 2.6.x port with recent 2.6.x API changes - * - fix lots of linear skb assumptions from Mandrake's port - * 2005-06-10 - Version 2.1 - * - use ip_conntrack_expect_free() instead of kfree() on the - * expect's (which are from the slab for quite some time) - * 2005-06-10 - Version 3.0 - * - port helper to post-2.6.11 API changes, - * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/) - * 2005-07-30 - Version 3.1 - * - port helper to 2.6.13 API changes - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define IP_CT_PPTP_VERSION "3.1" - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); - -static DEFINE_SPINLOCK(ip_pptp_lock); - -int -(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -int -(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -void -(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *expect_orig, - struct ip_conntrack_expect *expect_reply); - -void -(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp); - -#if 0 -/* PptpControlMessageType names */ -const char *pptp_msg_name[] = { - "UNKNOWN_MESSAGE", - "START_SESSION_REQUEST", - "START_SESSION_REPLY", - "STOP_SESSION_REQUEST", - "STOP_SESSION_REPLY", - "ECHO_REQUEST", - "ECHO_REPLY", - "OUT_CALL_REQUEST", - "OUT_CALL_REPLY", - "IN_CALL_REQUEST", - "IN_CALL_REPLY", - "IN_CALL_CONNECT", - "CALL_CLEAR_REQUEST", - "CALL_DISCONNECT_NOTIFY", - "WAN_ERROR_NOTIFY", - "SET_LINK_INFO" -}; -EXPORT_SYMBOL(pptp_msg_name); -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) -#else -#define DEBUGP(format, args...) -#endif - -#define SECS *HZ -#define MINS * 60 SECS -#define HOURS * 60 MINS - -#define PPTP_GRE_TIMEOUT (10 MINS) -#define PPTP_GRE_STREAM_TIMEOUT (5 HOURS) - -static void pptp_expectfn(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp) -{ - typeof(ip_nat_pptp_hook_expectfn) ip_nat_pptp_expectfn; - - DEBUGP("increasing timeouts\n"); - - /* increase timeout of GRE data channel conntrack entry */ - ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; - ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; - - /* Can you see how rusty this code is, compared with the pre-2.6.11 - * one? That's what happened to my shiny newnat of 2002 ;( -HW */ - - rcu_read_lock(); - ip_nat_pptp_expectfn = rcu_dereference(ip_nat_pptp_hook_expectfn); - if (!ip_nat_pptp_expectfn) { - struct ip_conntrack_tuple inv_t; - struct ip_conntrack_expect *exp_other; - - /* obviously this tuple inversion only works until you do NAT */ - invert_tuplepr(&inv_t, &exp->tuple); - DEBUGP("trying to unexpect other dir: "); - DUMP_TUPLE(&inv_t); - - exp_other = ip_conntrack_expect_find_get(&inv_t); - if (exp_other) { - /* delete other expectation. */ - DEBUGP("found\n"); - ip_conntrack_unexpect_related(exp_other); - ip_conntrack_expect_put(exp_other); - } else { - DEBUGP("not found\n"); - } - } else { - /* we need more than simple inversion */ - ip_nat_pptp_expectfn(ct, exp); - } - rcu_read_unlock(); -} - -static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_expect *exp; - - DEBUGP("trying to timeout ct or exp for tuple "); - DUMP_TUPLE(t); - - h = ip_conntrack_find_get(t, NULL); - if (h) { - struct ip_conntrack *sibling = tuplehash_to_ctrack(h); - DEBUGP("setting timeout of conntrack %p to 0\n", sibling); - sibling->proto.gre.timeout = 0; - sibling->proto.gre.stream_timeout = 0; - if (del_timer(&sibling->timeout)) - sibling->timeout.function((unsigned long)sibling); - ip_conntrack_put(sibling); - return 1; - } else { - exp = ip_conntrack_expect_find_get(t); - if (exp) { - DEBUGP("unexpect_related of expect %p\n", exp); - ip_conntrack_unexpect_related(exp); - ip_conntrack_expect_put(exp); - return 1; - } - } - - return 0; -} - - -/* timeout GRE data connections */ -static void pptp_destroy_siblings(struct ip_conntrack *ct) -{ - struct ip_conntrack_tuple t; - - ip_ct_gre_keymap_destroy(ct); - /* Since ct->sibling_list has literally rusted away in 2.6.11, - * we now need another way to find out about our sibling - * contrack and expects... -HW */ - - /* try original (pns->pac) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct->help.ct_pptp_info.pns_call_id; - t.dst.u.gre.key = ct->help.ct_pptp_info.pac_call_id; - - if (!destroy_sibling_or_exp(&t)) - DEBUGP("failed to timeout original pns->pac ct/exp\n"); - - /* try reply (pac->pns) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct->help.ct_pptp_info.pac_call_id; - t.dst.u.gre.key = ct->help.ct_pptp_info.pns_call_id; - - if (!destroy_sibling_or_exp(&t)) - DEBUGP("failed to timeout reply pac->pns ct/exp\n"); -} - -/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ -static inline int -exp_gre(struct ip_conntrack *ct, - __be16 callid, - __be16 peer_callid) -{ - struct ip_conntrack_expect *exp_orig, *exp_reply; - int ret = 1; - typeof(ip_nat_pptp_hook_exp_gre) ip_nat_pptp_exp_gre; - - exp_orig = ip_conntrack_expect_alloc(ct); - if (exp_orig == NULL) - goto out; - - exp_reply = ip_conntrack_expect_alloc(ct); - if (exp_reply == NULL) - goto out_put_orig; - - /* original direction, PNS->PAC */ - exp_orig->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - exp_orig->tuple.src.u.gre.key = peer_callid; - exp_orig->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; - exp_orig->tuple.dst.u.gre.key = callid; - exp_orig->tuple.dst.protonum = IPPROTO_GRE; - - exp_orig->mask.src.ip = htonl(0xffffffff); - exp_orig->mask.src.u.all = 0; - exp_orig->mask.dst.u.gre.key = htons(0xffff); - exp_orig->mask.dst.ip = htonl(0xffffffff); - exp_orig->mask.dst.protonum = 0xff; - - exp_orig->master = ct; - exp_orig->expectfn = pptp_expectfn; - exp_orig->flags = 0; - - /* both expectations are identical apart from tuple */ - memcpy(exp_reply, exp_orig, sizeof(*exp_reply)); - - /* reply direction, PAC->PNS */ - exp_reply->tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - exp_reply->tuple.src.u.gre.key = callid; - exp_reply->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; - exp_reply->tuple.dst.u.gre.key = peer_callid; - exp_reply->tuple.dst.protonum = IPPROTO_GRE; - - ip_nat_pptp_exp_gre = rcu_dereference(ip_nat_pptp_hook_exp_gre); - if (ip_nat_pptp_exp_gre) - ip_nat_pptp_exp_gre(exp_orig, exp_reply); - if (ip_conntrack_expect_related(exp_orig) != 0) - goto out_put_both; - if (ip_conntrack_expect_related(exp_reply) != 0) - goto out_unexpect_orig; - - /* Add GRE keymap entries */ - if (ip_ct_gre_keymap_add(ct, &exp_orig->tuple, 0) != 0) - goto out_unexpect_both; - if (ip_ct_gre_keymap_add(ct, &exp_reply->tuple, 1) != 0) { - ip_ct_gre_keymap_destroy(ct); - goto out_unexpect_both; - } - ret = 0; - -out_put_both: - ip_conntrack_expect_put(exp_reply); -out_put_orig: - ip_conntrack_expect_put(exp_orig); -out: - return ret; - -out_unexpect_both: - ip_conntrack_unexpect_related(exp_reply); -out_unexpect_orig: - ip_conntrack_unexpect_related(exp_orig); - goto out_put_both; -} - -static inline int -pptp_inbound_pkt(struct sk_buff **pskb, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq, - unsigned int reqlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; - u_int16_t msg; - __be16 cid = 0, pcid = 0; - typeof(ip_nat_pptp_hook_inbound) ip_nat_pptp_inbound; - - msg = ntohs(ctlh->messageType); - DEBUGP("inbound control message %s\n", pptp_msg_name[msg]); - - switch (msg) { - case PPTP_START_SESSION_REPLY: - /* server confirms new control session */ - if (info->sstate < PPTP_SESSION_REQUESTED) - goto invalid; - if (pptpReq->srep.resultCode == PPTP_START_OK) - info->sstate = PPTP_SESSION_CONFIRMED; - else - info->sstate = PPTP_SESSION_ERROR; - break; - - case PPTP_STOP_SESSION_REPLY: - /* server confirms end of control session */ - if (info->sstate > PPTP_SESSION_STOPREQ) - goto invalid; - if (pptpReq->strep.resultCode == PPTP_STOP_OK) - info->sstate = PPTP_SESSION_NONE; - else - info->sstate = PPTP_SESSION_ERROR; - break; - - case PPTP_OUT_CALL_REPLY: - /* server accepted call, we now expect GRE frames */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - if (info->cstate != PPTP_CALL_OUT_REQ && - info->cstate != PPTP_CALL_OUT_CONF) - goto invalid; - - cid = pptpReq->ocack.callID; - pcid = pptpReq->ocack.peersCallID; - if (info->pns_call_id != pcid) - goto invalid; - DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg], - ntohs(cid), ntohs(pcid)); - - if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) { - info->cstate = PPTP_CALL_OUT_CONF; - info->pac_call_id = cid; - exp_gre(ct, cid, pcid); - } else - info->cstate = PPTP_CALL_NONE; - break; - - case PPTP_IN_CALL_REQUEST: - /* server tells us about incoming call request */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - - cid = pptpReq->icreq.callID; - DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); - info->cstate = PPTP_CALL_IN_REQ; - info->pac_call_id = cid; - break; - - case PPTP_IN_CALL_CONNECT: - /* server tells us about incoming call established */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - if (info->cstate != PPTP_CALL_IN_REP && - info->cstate != PPTP_CALL_IN_CONF) - goto invalid; - - pcid = pptpReq->iccon.peersCallID; - cid = info->pac_call_id; - - if (info->pns_call_id != pcid) - goto invalid; - - DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid)); - info->cstate = PPTP_CALL_IN_CONF; - - /* we expect a GRE connection from PAC to PNS */ - exp_gre(ct, cid, pcid); - break; - - case PPTP_CALL_DISCONNECT_NOTIFY: - /* server confirms disconnect */ - cid = pptpReq->disc.callID; - DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); - info->cstate = PPTP_CALL_NONE; - - /* untrack this call id, unexpect GRE packets */ - pptp_destroy_siblings(ct); - break; - - case PPTP_WAN_ERROR_NOTIFY: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* I don't have to explain these ;) */ - break; - default: - goto invalid; - } - - ip_nat_pptp_inbound = rcu_dereference(ip_nat_pptp_hook_inbound); - if (ip_nat_pptp_inbound) - return ip_nat_pptp_inbound(pskb, ct, ctinfo, ctlh, pptpReq); - return NF_ACCEPT; - -invalid: - DEBUGP("invalid %s: type=%d cid=%u pcid=%u " - "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", - msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], - msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, - ntohs(info->pns_call_id), ntohs(info->pac_call_id)); - return NF_ACCEPT; -} - -static inline int -pptp_outbound_pkt(struct sk_buff **pskb, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq, - unsigned int reqlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; - u_int16_t msg; - __be16 cid = 0, pcid = 0; - typeof(ip_nat_pptp_hook_outbound) ip_nat_pptp_outbound; - - msg = ntohs(ctlh->messageType); - DEBUGP("outbound control message %s\n", pptp_msg_name[msg]); - - switch (msg) { - case PPTP_START_SESSION_REQUEST: - /* client requests for new control session */ - if (info->sstate != PPTP_SESSION_NONE) - goto invalid; - info->sstate = PPTP_SESSION_REQUESTED; - break; - case PPTP_STOP_SESSION_REQUEST: - /* client requests end of control session */ - info->sstate = PPTP_SESSION_STOPREQ; - break; - - case PPTP_OUT_CALL_REQUEST: - /* client initiating connection to server */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - info->cstate = PPTP_CALL_OUT_REQ; - /* track PNS call id */ - cid = pptpReq->ocreq.callID; - DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); - info->pns_call_id = cid; - break; - case PPTP_IN_CALL_REPLY: - /* client answers incoming call */ - if (info->cstate != PPTP_CALL_IN_REQ && - info->cstate != PPTP_CALL_IN_REP) - goto invalid; - - cid = pptpReq->icack.callID; - pcid = pptpReq->icack.peersCallID; - if (info->pac_call_id != pcid) - goto invalid; - DEBUGP("%s, CID=%X PCID=%X\n", pptp_msg_name[msg], - ntohs(cid), ntohs(pcid)); - - if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) { - /* part two of the three-way handshake */ - info->cstate = PPTP_CALL_IN_REP; - info->pns_call_id = cid; - } else - info->cstate = PPTP_CALL_NONE; - break; - - case PPTP_CALL_CLEAR_REQUEST: - /* client requests hangup of call */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - /* FUTURE: iterate over all calls and check if - * call ID is valid. We don't do this without newnat, - * because we only know about last call */ - info->cstate = PPTP_CALL_CLEAR_REQ; - break; - case PPTP_SET_LINK_INFO: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* I don't have to explain these ;) */ - break; - default: - goto invalid; - } - - ip_nat_pptp_outbound = rcu_dereference(ip_nat_pptp_hook_outbound); - if (ip_nat_pptp_outbound) - return ip_nat_pptp_outbound(pskb, ct, ctinfo, ctlh, pptpReq); - return NF_ACCEPT; - -invalid: - DEBUGP("invalid %s: type=%d cid=%u pcid=%u " - "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", - msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], - msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, - ntohs(info->pns_call_id), ntohs(info->pac_call_id)); - return NF_ACCEPT; -} - -static const unsigned int pptp_msg_size[] = { - [PPTP_START_SESSION_REQUEST] = sizeof(struct PptpStartSessionRequest), - [PPTP_START_SESSION_REPLY] = sizeof(struct PptpStartSessionReply), - [PPTP_STOP_SESSION_REQUEST] = sizeof(struct PptpStopSessionRequest), - [PPTP_STOP_SESSION_REPLY] = sizeof(struct PptpStopSessionReply), - [PPTP_OUT_CALL_REQUEST] = sizeof(struct PptpOutCallRequest), - [PPTP_OUT_CALL_REPLY] = sizeof(struct PptpOutCallReply), - [PPTP_IN_CALL_REQUEST] = sizeof(struct PptpInCallRequest), - [PPTP_IN_CALL_REPLY] = sizeof(struct PptpInCallReply), - [PPTP_IN_CALL_CONNECT] = sizeof(struct PptpInCallConnected), - [PPTP_CALL_CLEAR_REQUEST] = sizeof(struct PptpClearCallRequest), - [PPTP_CALL_DISCONNECT_NOTIFY] = sizeof(struct PptpCallDisconnectNotify), - [PPTP_WAN_ERROR_NOTIFY] = sizeof(struct PptpWanErrorNotify), - [PPTP_SET_LINK_INFO] = sizeof(struct PptpSetLinkInfo), -}; - -/* track caller id inside control connection, call expect_related */ -static int -conntrack_pptp_help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) - -{ - int dir = CTINFO2DIR(ctinfo); - struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; - struct tcphdr _tcph, *tcph; - struct pptp_pkt_hdr _pptph, *pptph; - struct PptpControlHeader _ctlh, *ctlh; - union pptp_ctrl_union _pptpReq, *pptpReq; - unsigned int tcplen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4; - unsigned int datalen, reqlen, nexthdr_off; - int oldsstate, oldcstate; - int ret; - u_int16_t msg; - - /* don't do any tracking before tcp handshake complete */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { - DEBUGP("ctinfo = %u, skipping\n", ctinfo); - return NF_ACCEPT; - } - - nexthdr_off = (*pskb)->nh.iph->ihl*4; - tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph); - BUG_ON(!tcph); - nexthdr_off += tcph->doff * 4; - datalen = tcplen - tcph->doff * 4; - - pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph); - if (!pptph) { - DEBUGP("no full PPTP header, can't track\n"); - return NF_ACCEPT; - } - nexthdr_off += sizeof(_pptph); - datalen -= sizeof(_pptph); - - /* if it's not a control message we can't do anything with it */ - if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || - ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { - DEBUGP("not a control packet\n"); - return NF_ACCEPT; - } - - ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh); - if (!ctlh) - return NF_ACCEPT; - nexthdr_off += sizeof(_ctlh); - datalen -= sizeof(_ctlh); - - reqlen = datalen; - msg = ntohs(ctlh->messageType); - if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg]) - return NF_ACCEPT; - if (reqlen > sizeof(*pptpReq)) - reqlen = sizeof(*pptpReq); - - pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq); - if (!pptpReq) - return NF_ACCEPT; - - oldsstate = info->sstate; - oldcstate = info->cstate; - - spin_lock_bh(&ip_pptp_lock); - - /* FIXME: We just blindly assume that the control connection is always - * established from PNS->PAC. However, RFC makes no guarantee */ - if (dir == IP_CT_DIR_ORIGINAL) - /* client -> server (PNS -> PAC) */ - ret = pptp_outbound_pkt(pskb, ctlh, pptpReq, reqlen, ct, - ctinfo); - else - /* server -> client (PAC -> PNS) */ - ret = pptp_inbound_pkt(pskb, ctlh, pptpReq, reqlen, ct, - ctinfo); - DEBUGP("sstate: %d->%d, cstate: %d->%d\n", - oldsstate, info->sstate, oldcstate, info->cstate); - spin_unlock_bh(&ip_pptp_lock); - - return ret; -} - -/* control protocol helper */ -static struct ip_conntrack_helper pptp = { - .list = { NULL, NULL }, - .name = "pptp", - .me = THIS_MODULE, - .max_expected = 2, - .timeout = 5 * 60, - .tuple = { .src = { .ip = 0, - .u = { .tcp = { .port = - __constant_htons(PPTP_CONTROL_PORT) } } - }, - .dst = { .ip = 0, - .u = { .all = 0 }, - .protonum = IPPROTO_TCP - } - }, - .mask = { .src = { .ip = 0, - .u = { .tcp = { .port = __constant_htons(0xffff) } } - }, - .dst = { .ip = 0, - .u = { .all = 0 }, - .protonum = 0xff - } - }, - .help = conntrack_pptp_help, - .destroy = pptp_destroy_siblings, -}; - -extern void ip_ct_proto_gre_fini(void); -extern int __init ip_ct_proto_gre_init(void); - -/* ip_conntrack_pptp initialization */ -static int __init ip_conntrack_helper_pptp_init(void) -{ - int retcode; - - retcode = ip_ct_proto_gre_init(); - if (retcode < 0) - return retcode; - - DEBUGP(" registering helper\n"); - if ((retcode = ip_conntrack_helper_register(&pptp))) { - printk(KERN_ERR "Unable to register conntrack application " - "helper for pptp: %d\n", retcode); - ip_ct_proto_gre_fini(); - return retcode; - } - - printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION); - return 0; -} - -static void __exit ip_conntrack_helper_pptp_fini(void) -{ - ip_conntrack_helper_unregister(&pptp); - ip_ct_proto_gre_fini(); - printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION); -} - -module_init(ip_conntrack_helper_pptp_init); -module_exit(ip_conntrack_helper_pptp_fini); - -EXPORT_SYMBOL(ip_nat_pptp_hook_outbound); -EXPORT_SYMBOL(ip_nat_pptp_hook_inbound); -EXPORT_SYMBOL(ip_nat_pptp_hook_exp_gre); -EXPORT_SYMBOL(ip_nat_pptp_hook_expectfn); diff --git a/net/ipv4/netfilter/ip_conntrack_irc.c b/net/ipv4/netfilter/ip_conntrack_irc.c deleted file mode 100644 index 053e591..0000000 --- a/net/ipv4/netfilter/ip_conntrack_irc.c +++ /dev/null @@ -1,314 +0,0 @@ -/* IRC extension for IP connection tracking, Version 1.21 - * (C) 2000-2002 by Harald Welte - * based on RR's ip_conntrack_ftp.c - * - * ip_conntrack_irc.c,v 1.21 2002/02/05 14:49:26 laforge Exp - * - * 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. - ** - * Module load syntax: - * insmod ip_conntrack_irc.o ports=port1,port2,...port - * max_dcc_channels=n dcc_timeout=secs - * - * please give the ports of all IRC servers You wish to connect to. - * If You don't specify ports, the default will be port 6667. - * With max_dcc_channels you can define the maximum number of not - * yet answered DCC channels per IRC session (default 8). - * With dcc_timeout you can specify how long the system waits for - * an expected DCC channel (default 300 seconds). - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -static unsigned int max_dcc_channels = 8; -static unsigned int dcc_timeout = 300; -/* This is slow, but it's simple. --RR */ -static char *irc_buffer; -static DEFINE_SPINLOCK(irc_buffer_lock); - -unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); -EXPORT_SYMBOL_GPL(ip_nat_irc_hook); - -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("IRC (DCC) connection tracking helper"); -MODULE_LICENSE("GPL"); -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of IRC servers"); -module_param(max_dcc_channels, uint, 0400); -MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session"); -module_param(dcc_timeout, uint, 0400); -MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels"); - -static const char *dccprotos[] = { "SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT " }; -#define MINMATCHLEN 5 - -#if 0 -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s:" format, \ - __FILE__, __FUNCTION__ , ## args) -#else -#define DEBUGP(format, args...) -#endif - -static int parse_dcc(char *data, char *data_end, u_int32_t *ip, - u_int16_t *port, char **ad_beg_p, char **ad_end_p) -/* tries to get the ip_addr and port out of a dcc command - return value: -1 on failure, 0 on success - data pointer to first byte of DCC command data - data_end pointer to last byte of dcc command data - ip returns parsed ip of dcc command - port returns parsed port of dcc command - ad_beg_p returns pointer to first byte of addr data - ad_end_p returns pointer to last byte of addr data */ -{ - - /* at least 12: "AAAAAAAA P\1\n" */ - while (*data++ != ' ') - if (data > data_end - 12) - return -1; - - *ad_beg_p = data; - *ip = simple_strtoul(data, &data, 10); - - /* skip blanks between ip and port */ - while (*data == ' ') { - if (data >= data_end) - return -1; - data++; - } - - *port = simple_strtoul(data, &data, 10); - *ad_end_p = data; - - return 0; -} - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) -{ - unsigned int dataoff; - struct tcphdr _tcph, *th; - char *data, *data_limit, *ib_ptr; - int dir = CTINFO2DIR(ctinfo); - struct ip_conntrack_expect *exp; - u32 seq; - u_int32_t dcc_ip; - u_int16_t dcc_port; - int i, ret = NF_ACCEPT; - char *addr_beg_p, *addr_end_p; - typeof(ip_nat_irc_hook) ip_nat_irc; - - DEBUGP("entered\n"); - - /* If packet is coming from IRC server */ - if (dir == IP_CT_DIR_REPLY) - return NF_ACCEPT; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { - DEBUGP("Conntrackinfo = %u\n", ctinfo); - return NF_ACCEPT; - } - - /* Not a full tcp header? */ - th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, - sizeof(_tcph), &_tcph); - if (th == NULL) - return NF_ACCEPT; - - /* No data? */ - dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4; - if (dataoff >= (*pskb)->len) - return NF_ACCEPT; - - spin_lock_bh(&irc_buffer_lock); - ib_ptr = skb_header_pointer(*pskb, dataoff, - (*pskb)->len - dataoff, irc_buffer); - BUG_ON(ib_ptr == NULL); - - data = ib_ptr; - data_limit = ib_ptr + (*pskb)->len - dataoff; - - /* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24 - * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */ - while (data < (data_limit - (19 + MINMATCHLEN))) { - if (memcmp(data, "\1DCC ", 5)) { - data++; - continue; - } - - data += 5; - /* we have at least (19+MINMATCHLEN)-5 bytes valid data left */ - - DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n", - NIPQUAD(iph->saddr), ntohs(th->source), - NIPQUAD(iph->daddr), ntohs(th->dest)); - - for (i = 0; i < ARRAY_SIZE(dccprotos); i++) { - if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) { - /* no match */ - continue; - } - - DEBUGP("DCC %s detected\n", dccprotos[i]); - data += strlen(dccprotos[i]); - /* we have at least - * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid - * data left (== 14/13 bytes) */ - if (parse_dcc((char *)data, data_limit, &dcc_ip, - &dcc_port, &addr_beg_p, &addr_end_p)) { - /* unable to parse */ - DEBUGP("unable to parse dcc command\n"); - continue; - } - DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n", - HIPQUAD(dcc_ip), dcc_port); - - /* dcc_ip can be the internal OR external (NAT'ed) IP - * Tiago Sousa */ - if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip) - && ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != htonl(dcc_ip)) { - if (net_ratelimit()) - printk(KERN_WARNING - "Forged DCC command from " - "%u.%u.%u.%u: %u.%u.%u.%u:%u\n", - NIPQUAD(ct->tuplehash[dir].tuple.src.ip), - HIPQUAD(dcc_ip), dcc_port); - - continue; - } - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) { - ret = NF_DROP; - goto out; - } - - /* save position of address in dcc string, - * necessary for NAT */ - DEBUGP("tcph->seq = %u\n", th->seq); - seq = ntohl(th->seq) + (addr_beg_p - ib_ptr); - - /* We refer to the reverse direction ("!dir") - * tuples here, because we're expecting - * something in the other * direction. - * Doesn't matter unless NAT is happening. */ - exp->tuple = ((struct ip_conntrack_tuple) - { { 0, { 0 } }, - { ct->tuplehash[!dir].tuple.dst.ip, - { .tcp = { htons(dcc_port) } }, - IPPROTO_TCP }}); - exp->mask = ((struct ip_conntrack_tuple) - { { 0, { 0 } }, - { htonl(0xFFFFFFFF), - { .tcp = { htons(0xFFFF) } }, 0xFF }}); - exp->expectfn = NULL; - exp->flags = 0; - ip_nat_irc = rcu_dereference(ip_nat_irc_hook); - if (ip_nat_irc) - ret = ip_nat_irc(pskb, ctinfo, - addr_beg_p - ib_ptr, - addr_end_p - addr_beg_p, - exp); - else if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - ip_conntrack_expect_put(exp); - goto out; - } /* for .. NUM_DCCPROTO */ - } /* while data < ... */ - - out: - spin_unlock_bh(&irc_buffer_lock); - return ret; -} - -static struct ip_conntrack_helper irc_helpers[MAX_PORTS]; -static char irc_names[MAX_PORTS][sizeof("irc-65535")]; - -static void ip_conntrack_irc_fini(void); - -static int __init ip_conntrack_irc_init(void) -{ - int i, ret; - struct ip_conntrack_helper *hlpr; - char *tmpname; - - if (max_dcc_channels < 1) { - printk("ip_conntrack_irc: max_dcc_channels must be a positive integer\n"); - return -EBUSY; - } - - irc_buffer = kmalloc(65536, GFP_KERNEL); - if (!irc_buffer) - return -ENOMEM; - - /* If no port given, default to standard irc port */ - if (ports_c == 0) - ports[ports_c++] = IRC_PORT; - - for (i = 0; i < ports_c; i++) { - hlpr = &irc_helpers[i]; - hlpr->tuple.src.u.tcp.port = htons(ports[i]); - hlpr->tuple.dst.protonum = IPPROTO_TCP; - hlpr->mask.src.u.tcp.port = htons(0xFFFF); - hlpr->mask.dst.protonum = 0xFF; - hlpr->max_expected = max_dcc_channels; - hlpr->timeout = dcc_timeout; - hlpr->me = THIS_MODULE; - hlpr->help = help; - - tmpname = &irc_names[i][0]; - if (ports[i] == IRC_PORT) - sprintf(tmpname, "irc"); - else - sprintf(tmpname, "irc-%d", i); - hlpr->name = tmpname; - - DEBUGP("port #%d: %d\n", i, ports[i]); - - ret = ip_conntrack_helper_register(hlpr); - - if (ret) { - printk("ip_conntrack_irc: ERROR registering port %d\n", - ports[i]); - ip_conntrack_irc_fini(); - return -EBUSY; - } - } - return 0; -} - -/* This function is intentionally _NOT_ defined as __exit, because - * it is needed by the init function */ -static void ip_conntrack_irc_fini(void) -{ - int i; - for (i = 0; i < ports_c; i++) { - DEBUGP("unregistering port %d\n", - ports[i]); - ip_conntrack_helper_unregister(&irc_helpers[i]); - } - kfree(irc_buffer); -} - -module_init(ip_conntrack_irc_init); -module_exit(ip_conntrack_irc_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c b/net/ipv4/netfilter/ip_conntrack_netbios_ns.c deleted file mode 100644 index cc6dd49..0000000 --- a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * NetBIOS name service broadcast connection tracking helper - * - * (c) 2005 Patrick McHardy - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -/* - * This helper tracks locally originating NetBIOS name service - * requests by issuing permanent expectations (valid until - * timing out) matching all reply connections from the - * destination network. The only NetBIOS specific thing is - * actually the port number. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define NMBD_PORT 137 - -MODULE_AUTHOR("Patrick McHardy "); -MODULE_DESCRIPTION("NetBIOS name service broadcast connection tracking helper"); -MODULE_LICENSE("GPL"); - -static unsigned int timeout = 3; -module_param(timeout, uint, 0400); -MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds"); - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) -{ - struct ip_conntrack_expect *exp; - struct iphdr *iph = (*pskb)->nh.iph; - struct rtable *rt = (struct rtable *)(*pskb)->dst; - struct in_device *in_dev; - __be32 mask = 0; - - /* we're only interested in locally generated packets */ - if ((*pskb)->sk == NULL) - goto out; - if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST)) - goto out; - if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) - goto out; - - rcu_read_lock(); - in_dev = __in_dev_get_rcu(rt->u.dst.dev); - if (in_dev != NULL) { - for_primary_ifa(in_dev) { - if (ifa->ifa_broadcast == iph->daddr) { - mask = ifa->ifa_mask; - break; - } - } endfor_ifa(in_dev); - } - rcu_read_unlock(); - - if (mask == 0) - goto out; - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) - goto out; - - exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; - exp->tuple.src.u.udp.port = htons(NMBD_PORT); - - exp->mask.src.ip = mask; - exp->mask.src.u.udp.port = htons(0xFFFF); - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.udp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - - exp->expectfn = NULL; - exp->flags = IP_CT_EXPECT_PERMANENT; - - ip_conntrack_expect_related(exp); - ip_conntrack_expect_put(exp); - - ip_ct_refresh(ct, *pskb, timeout * HZ); -out: - return NF_ACCEPT; -} - -static struct ip_conntrack_helper helper = { - .name = "netbios-ns", - .tuple = { - .src = { - .u = { - .udp = { - .port = __constant_htons(NMBD_PORT), - } - } - }, - .dst = { - .protonum = IPPROTO_UDP, - }, - }, - .mask = { - .src = { - .u = { - .udp = { - .port = __constant_htons(0xFFFF), - } - } - }, - .dst = { - .protonum = 0xFF, - }, - }, - .max_expected = 1, - .me = THIS_MODULE, - .help = help, -}; - -static int __init ip_conntrack_netbios_ns_init(void) -{ - helper.timeout = timeout; - return ip_conntrack_helper_register(&helper); -} - -static void __exit ip_conntrack_netbios_ns_fini(void) -{ - ip_conntrack_helper_unregister(&helper); -} - -module_init(ip_conntrack_netbios_ns_init); -module_exit(ip_conntrack_netbios_ns_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_netlink.c b/net/ipv4/netfilter/ip_conntrack_netlink.c deleted file mode 100644 index 9228b76..0000000 --- a/net/ipv4/netfilter/ip_conntrack_netlink.c +++ /dev/null @@ -1,1577 +0,0 @@ -/* Connection tracking via netlink socket. Allows for user space - * protocol helpers and general trouble making from userspace. - * - * (C) 2001 by Jay Schulist - * (C) 2002-2005 by Harald Welte - * (C) 2003 by Patrick Mchardy - * (C) 2005-2006 by Pablo Neira Ayuso - * - * I've reworked this stuff to use attributes instead of conntrack - * structures. 5.44 am. I need more tea. --pablo 05/07/11. - * - * Initial connection tracking via netlink development funded and - * generally made possible by Network Robots, Inc. (www.networkrobots.com) - * - * Further development of this code funded by Astaro AG (http://www.astaro.com) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -MODULE_LICENSE("GPL"); - -static char __initdata version[] = "0.90"; - -static inline int -ctnetlink_dump_tuples_proto(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple, - struct ip_conntrack_protocol *proto) -{ - int ret = 0; - struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO); - - NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum); - - if (likely(proto->tuple_to_nfattr)) - ret = proto->tuple_to_nfattr(skb, tuple); - - NFA_NEST_END(skb, nest_parms); - - return ret; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_tuples_ip(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple) -{ - struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP); - - NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(__be32), &tuple->src.ip); - NFA_PUT(skb, CTA_IP_V4_DST, sizeof(__be32), &tuple->dst.ip); - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_tuples(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple) -{ - int ret; - struct ip_conntrack_protocol *proto; - - ret = ctnetlink_dump_tuples_ip(skb, tuple); - if (unlikely(ret < 0)) - return ret; - - proto = ip_conntrack_proto_find_get(tuple->dst.protonum); - ret = ctnetlink_dump_tuples_proto(skb, tuple, proto); - ip_conntrack_proto_put(proto); - - return ret; -} - -static inline int -ctnetlink_dump_status(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 status = htonl((u_int32_t) ct->status); - NFA_PUT(skb, CTA_STATUS, sizeof(status), &status); - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_timeout(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - long timeout_l = ct->timeout.expires - jiffies; - __be32 timeout; - - if (timeout_l < 0) - timeout = 0; - else - timeout = htonl(timeout_l / HZ); - - NFA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout); - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - struct ip_conntrack_protocol *proto = ip_conntrack_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); - - struct nfattr *nest_proto; - int ret; - - if (!proto->to_nfattr) { - ip_conntrack_proto_put(proto); - return 0; - } - - nest_proto = NFA_NEST(skb, CTA_PROTOINFO); - - ret = proto->to_nfattr(skb, nest_proto, ct); - - ip_conntrack_proto_put(proto); - - NFA_NEST_END(skb, nest_proto); - - return ret; - -nfattr_failure: - ip_conntrack_proto_put(proto); - return -1; -} - -static inline int -ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - struct nfattr *nest_helper; - - if (!ct->helper) - return 0; - - nest_helper = NFA_NEST(skb, CTA_HELP); - NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name); - - if (ct->helper->to_nfattr) - ct->helper->to_nfattr(skb, ct); - - NFA_NEST_END(skb, nest_helper); - - return 0; - -nfattr_failure: - return -1; -} - -#ifdef CONFIG_IP_NF_CT_ACCT -static inline int -ctnetlink_dump_counters(struct sk_buff *skb, const struct ip_conntrack *ct, - enum ip_conntrack_dir dir) -{ - enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; - struct nfattr *nest_count = NFA_NEST(skb, type); - __be32 tmp; - - tmp = htonl(ct->counters[dir].packets); - NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(__be32), &tmp); - - tmp = htonl(ct->counters[dir].bytes); - NFA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(__be32), &tmp); - - NFA_NEST_END(skb, nest_count); - - return 0; - -nfattr_failure: - return -1; -} -#else -#define ctnetlink_dump_counters(a, b, c) (0) -#endif - -#ifdef CONFIG_IP_NF_CONNTRACK_MARK -static inline int -ctnetlink_dump_mark(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 mark = htonl(ct->mark); - - NFA_PUT(skb, CTA_MARK, sizeof(__be32), &mark); - return 0; - -nfattr_failure: - return -1; -} -#else -#define ctnetlink_dump_mark(a, b) (0) -#endif - -static inline int -ctnetlink_dump_id(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 id = htonl(ct->id); - NFA_PUT(skb, CTA_ID, sizeof(__be32), &id); - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_use(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 use = htonl(atomic_read(&ct->ct_general.use)); - - NFA_PUT(skb, CTA_USE, sizeof(__be32), &use); - return 0; - -nfattr_failure: - return -1; -} - -#define tuple(ct, dir) (&(ct)->tuplehash[dir].tuple) - -static int -ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, - int event, int nowait, - const struct ip_conntrack *ct) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct nfattr *nest_parms; - unsigned char *b; - - b = skb->tail; - - event |= NFNL_SUBSYS_CTNETLINK << 8; - nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - if (ctnetlink_dump_status(skb, ct) < 0 || - ctnetlink_dump_timeout(skb, ct) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || - ctnetlink_dump_protoinfo(skb, ct) < 0 || - ctnetlink_dump_helpinfo(skb, ct) < 0 || - ctnetlink_dump_mark(skb, ct) < 0 || - ctnetlink_dump_id(skb, ct) < 0 || - ctnetlink_dump_use(skb, ct) < 0) - goto nfattr_failure; - - nlh->nlmsg_len = skb->tail - b; - return skb->len; - -nlmsg_failure: -nfattr_failure: - skb_trim(skb, b - skb->data); - return -1; -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -static int ctnetlink_conntrack_event(struct notifier_block *this, - unsigned long events, void *ptr) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct nfattr *nest_parms; - struct ip_conntrack *ct = (struct ip_conntrack *)ptr; - struct sk_buff *skb; - unsigned int type; - unsigned char *b; - unsigned int flags = 0, group; - - /* ignore our fake conntrack entry */ - if (ct == &ip_conntrack_untracked) - return NOTIFY_DONE; - - if (events & IPCT_DESTROY) { - type = IPCTNL_MSG_CT_DELETE; - group = NFNLGRP_CONNTRACK_DESTROY; - } else if (events & (IPCT_NEW | IPCT_RELATED)) { - type = IPCTNL_MSG_CT_NEW; - flags = NLM_F_CREATE|NLM_F_EXCL; - group = NFNLGRP_CONNTRACK_NEW; - } else if (events & (IPCT_STATUS | IPCT_PROTOINFO)) { - type = IPCTNL_MSG_CT_NEW; - group = NFNLGRP_CONNTRACK_UPDATE; - } else - return NOTIFY_DONE; - - if (!nfnetlink_has_listeners(group)) - return NOTIFY_DONE; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); - if (!skb) - return NOTIFY_DONE; - - b = skb->tail; - - type |= NFNL_SUBSYS_CTNETLINK << 8; - nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = flags; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - if (events & IPCT_DESTROY) { - if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) - goto nfattr_failure; - } else { - if (ctnetlink_dump_status(skb, ct) < 0) - goto nfattr_failure; - - if (ctnetlink_dump_timeout(skb, ct) < 0) - goto nfattr_failure; - - if (events & IPCT_PROTOINFO - && ctnetlink_dump_protoinfo(skb, ct) < 0) - goto nfattr_failure; - - if ((events & IPCT_HELPER || ct->helper) - && ctnetlink_dump_helpinfo(skb, ct) < 0) - goto nfattr_failure; - -#ifdef CONFIG_IP_NF_CONNTRACK_MARK - if ((events & IPCT_MARK || ct->mark) - && ctnetlink_dump_mark(skb, ct) < 0) - goto nfattr_failure; -#endif - - if (events & IPCT_COUNTER_FILLING && - (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) - goto nfattr_failure; - } - - nlh->nlmsg_len = skb->tail - b; - nfnetlink_send(skb, 0, group, 0); - return NOTIFY_DONE; - -nlmsg_failure: -nfattr_failure: - kfree_skb(skb); - return NOTIFY_DONE; -} -#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ - -static int ctnetlink_done(struct netlink_callback *cb) -{ - if (cb->args[1]) - ip_conntrack_put((struct ip_conntrack *)cb->args[1]); - return 0; -} - -static int -ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) -{ - struct ip_conntrack *ct, *last; - struct ip_conntrack_tuple_hash *h; - struct list_head *i; - - read_lock_bh(&ip_conntrack_lock); - last = (struct ip_conntrack *)cb->args[1]; - for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) { -restart: - list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) { - h = (struct ip_conntrack_tuple_hash *) i; - if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) - continue; - ct = tuplehash_to_ctrack(h); - if (cb->args[1]) { - if (ct != last) - continue; - cb->args[1] = 0; - } - if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - IPCTNL_MSG_CT_NEW, - 1, ct) < 0) { - nf_conntrack_get(&ct->ct_general); - cb->args[1] = (unsigned long)ct; - goto out; - } -#ifdef CONFIG_NF_CT_ACCT - if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == - IPCTNL_MSG_CT_GET_CTRZERO) - memset(&ct->counters, 0, sizeof(ct->counters)); -#endif - } - if (cb->args[1]) { - cb->args[1] = 0; - goto restart; - } - } -out: - read_unlock_bh(&ip_conntrack_lock); - if (last) - ip_conntrack_put(last); - - return skb->len; -} - -static const size_t cta_min_ip[CTA_IP_MAX] = { - [CTA_IP_V4_SRC-1] = sizeof(__be32), - [CTA_IP_V4_DST-1] = sizeof(__be32), -}; - -static inline int -ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple) -{ - struct nfattr *tb[CTA_IP_MAX]; - - nfattr_parse_nested(tb, CTA_IP_MAX, attr); - - if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip)) - return -EINVAL; - - if (!tb[CTA_IP_V4_SRC-1]) - return -EINVAL; - tuple->src.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]); - - if (!tb[CTA_IP_V4_DST-1]) - return -EINVAL; - tuple->dst.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]); - - return 0; -} - -static const size_t cta_min_proto[CTA_PROTO_MAX] = { - [CTA_PROTO_NUM-1] = sizeof(u_int8_t), - [CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t), - [CTA_PROTO_DST_PORT-1] = sizeof(u_int16_t), - [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t), - [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t), - [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t), -}; - -static inline int -ctnetlink_parse_tuple_proto(struct nfattr *attr, - struct ip_conntrack_tuple *tuple) -{ - struct nfattr *tb[CTA_PROTO_MAX]; - struct ip_conntrack_protocol *proto; - int ret = 0; - - nfattr_parse_nested(tb, CTA_PROTO_MAX, attr); - - if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) - return -EINVAL; - - if (!tb[CTA_PROTO_NUM-1]) - return -EINVAL; - tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]); - - proto = ip_conntrack_proto_find_get(tuple->dst.protonum); - - if (likely(proto->nfattr_to_tuple)) - ret = proto->nfattr_to_tuple(tb, tuple); - - ip_conntrack_proto_put(proto); - - return ret; -} - -static inline int -ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple, - enum ctattr_tuple type) -{ - struct nfattr *tb[CTA_TUPLE_MAX]; - int err; - - memset(tuple, 0, sizeof(*tuple)); - - nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]); - - if (!tb[CTA_TUPLE_IP-1]) - return -EINVAL; - - err = ctnetlink_parse_tuple_ip(tb[CTA_TUPLE_IP-1], tuple); - if (err < 0) - return err; - - if (!tb[CTA_TUPLE_PROTO-1]) - return -EINVAL; - - err = ctnetlink_parse_tuple_proto(tb[CTA_TUPLE_PROTO-1], tuple); - if (err < 0) - return err; - - /* orig and expect tuples get DIR_ORIGINAL */ - if (type == CTA_TUPLE_REPLY) - tuple->dst.dir = IP_CT_DIR_REPLY; - else - tuple->dst.dir = IP_CT_DIR_ORIGINAL; - - return 0; -} - -#ifdef CONFIG_IP_NF_NAT_NEEDED -static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = { - [CTA_PROTONAT_PORT_MIN-1] = sizeof(u_int16_t), - [CTA_PROTONAT_PORT_MAX-1] = sizeof(u_int16_t), -}; - -static int ctnetlink_parse_nat_proto(struct nfattr *attr, - const struct ip_conntrack *ct, - struct ip_nat_range *range) -{ - struct nfattr *tb[CTA_PROTONAT_MAX]; - struct ip_nat_protocol *npt; - - nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr); - - if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat)) - return -EINVAL; - - npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); - - if (!npt->nfattr_to_range) { - ip_nat_proto_put(npt); - return 0; - } - - /* nfattr_to_range returns 1 if it parsed, 0 if not, neg. on error */ - if (npt->nfattr_to_range(tb, range) > 0) - range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; - - ip_nat_proto_put(npt); - - return 0; -} - -static const size_t cta_min_nat[CTA_NAT_MAX] = { - [CTA_NAT_MINIP-1] = sizeof(__be32), - [CTA_NAT_MAXIP-1] = sizeof(__be32), -}; - -static inline int -ctnetlink_parse_nat(struct nfattr *nat, - const struct ip_conntrack *ct, struct ip_nat_range *range) -{ - struct nfattr *tb[CTA_NAT_MAX]; - int err; - - memset(range, 0, sizeof(*range)); - - nfattr_parse_nested(tb, CTA_NAT_MAX, nat); - - if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat)) - return -EINVAL; - - if (tb[CTA_NAT_MINIP-1]) - range->min_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MINIP-1]); - - if (!tb[CTA_NAT_MAXIP-1]) - range->max_ip = range->min_ip; - else - range->max_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MAXIP-1]); - - if (range->min_ip) - range->flags |= IP_NAT_RANGE_MAP_IPS; - - if (!tb[CTA_NAT_PROTO-1]) - return 0; - - err = ctnetlink_parse_nat_proto(tb[CTA_NAT_PROTO-1], ct, range); - if (err < 0) - return err; - - return 0; -} -#endif - -static inline int -ctnetlink_parse_help(struct nfattr *attr, char **helper_name) -{ - struct nfattr *tb[CTA_HELP_MAX]; - - nfattr_parse_nested(tb, CTA_HELP_MAX, attr); - - if (!tb[CTA_HELP_NAME-1]) - return -EINVAL; - - *helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]); - - return 0; -} - -static const size_t cta_min[CTA_MAX] = { - [CTA_STATUS-1] = sizeof(__be32), - [CTA_TIMEOUT-1] = sizeof(__be32), - [CTA_MARK-1] = sizeof(__be32), - [CTA_USE-1] = sizeof(__be32), - [CTA_ID-1] = sizeof(__be32) -}; - -static int -ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_tuple tuple; - struct ip_conntrack *ct; - int err = 0; - - if (nfattr_bad_size(cda, CTA_MAX, cta_min)) - return -EINVAL; - - if (cda[CTA_TUPLE_ORIG-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); - else if (cda[CTA_TUPLE_REPLY-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); - else { - /* Flush the whole table */ - ip_conntrack_flush(); - return 0; - } - - if (err < 0) - return err; - - h = ip_conntrack_find_get(&tuple, NULL); - if (!h) - return -ENOENT; - - ct = tuplehash_to_ctrack(h); - - if (cda[CTA_ID-1]) { - u_int32_t id = ntohl(*(__be32 *)NFA_DATA(cda[CTA_ID-1])); - if (ct->id != id) { - ip_conntrack_put(ct); - return -ENOENT; - } - } - if (del_timer(&ct->timeout)) - ct->timeout.function((unsigned long)ct); - - ip_conntrack_put(ct); - - return 0; -} - -static int -ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_tuple tuple; - struct ip_conntrack *ct; - struct sk_buff *skb2 = NULL; - int err = 0; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct nfgenmsg *msg = NLMSG_DATA(nlh); - u32 rlen; - - if (msg->nfgen_family != AF_INET) - return -EAFNOSUPPORT; - -#ifndef CONFIG_IP_NF_CT_ACCT - if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) - return -ENOTSUPP; -#endif - if ((*errp = netlink_dump_start(ctnl, skb, nlh, - ctnetlink_dump_table, - ctnetlink_done)) != 0) - return -EINVAL; - - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; - skb_pull(skb, rlen); - return 0; - } - - if (nfattr_bad_size(cda, CTA_MAX, cta_min)) - return -EINVAL; - - if (cda[CTA_TUPLE_ORIG-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); - else if (cda[CTA_TUPLE_REPLY-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); - else - return -EINVAL; - - if (err < 0) - return err; - - h = ip_conntrack_find_get(&tuple, NULL); - if (!h) - return -ENOENT; - - ct = tuplehash_to_ctrack(h); - - err = -ENOMEM; - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb2) { - ip_conntrack_put(ct); - return -ENOMEM; - } - - err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, - IPCTNL_MSG_CT_NEW, 1, ct); - ip_conntrack_put(ct); - if (err <= 0) - goto free; - - err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); - if (err < 0) - goto out; - - return 0; - -free: - kfree_skb(skb2); -out: - return err; -} - -static inline int -ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - unsigned long d; - unsigned status = ntohl(*(__be32 *)NFA_DATA(cda[CTA_STATUS-1])); - d = ct->status ^ status; - - if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING)) - /* unchangeable */ - return -EINVAL; - - if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY)) - /* SEEN_REPLY bit can only be set */ - return -EINVAL; - - - if (d & IPS_ASSURED && !(status & IPS_ASSURED)) - /* ASSURED bit can only be set */ - return -EINVAL; - - if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { -#ifndef CONFIG_IP_NF_NAT_NEEDED - return -EINVAL; -#else - struct ip_nat_range range; - - if (cda[CTA_NAT_DST-1]) { - if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct, - &range) < 0) - return -EINVAL; - if (ip_nat_initialized(ct, - HOOK2MANIP(NF_IP_PRE_ROUTING))) - return -EEXIST; - ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); - } - if (cda[CTA_NAT_SRC-1]) { - if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct, - &range) < 0) - return -EINVAL; - if (ip_nat_initialized(ct, - HOOK2MANIP(NF_IP_POST_ROUTING))) - return -EEXIST; - ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); - } -#endif - } - - /* Be careful here, modifying NAT bits can screw up things, - * so don't let users modify them directly if they don't pass - * ip_nat_range. */ - ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK); - return 0; -} - - -static inline int -ctnetlink_change_helper(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - struct ip_conntrack_helper *helper; - char *helpname; - int err; - - /* don't change helper of sibling connections */ - if (ct->master) - return -EINVAL; - - err = ctnetlink_parse_help(cda[CTA_HELP-1], &helpname); - if (err < 0) - return err; - - helper = __ip_conntrack_helper_find_byname(helpname); - if (!helper) { - if (!strcmp(helpname, "")) - helper = NULL; - else - return -EINVAL; - } - - if (ct->helper) { - if (!helper) { - /* we had a helper before ... */ - ip_ct_remove_expectations(ct); - ct->helper = NULL; - } else { - /* need to zero data of old helper */ - memset(&ct->help, 0, sizeof(ct->help)); - } - } - - ct->helper = helper; - - return 0; -} - -static inline int -ctnetlink_change_timeout(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - u_int32_t timeout = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); - - if (!del_timer(&ct->timeout)) - return -ETIME; - - ct->timeout.expires = jiffies + timeout * HZ; - add_timer(&ct->timeout); - - return 0; -} - -static inline int -ctnetlink_change_protoinfo(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - struct nfattr *tb[CTA_PROTOINFO_MAX], *attr = cda[CTA_PROTOINFO-1]; - struct ip_conntrack_protocol *proto; - u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; - int err = 0; - - nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr); - - proto = ip_conntrack_proto_find_get(npt); - - if (proto->from_nfattr) - err = proto->from_nfattr(tb, ct); - ip_conntrack_proto_put(proto); - - return err; -} - -static int -ctnetlink_change_conntrack(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - int err; - - if (cda[CTA_HELP-1]) { - err = ctnetlink_change_helper(ct, cda); - if (err < 0) - return err; - } - - if (cda[CTA_TIMEOUT-1]) { - err = ctnetlink_change_timeout(ct, cda); - if (err < 0) - return err; - } - - if (cda[CTA_STATUS-1]) { - err = ctnetlink_change_status(ct, cda); - if (err < 0) - return err; - } - - if (cda[CTA_PROTOINFO-1]) { - err = ctnetlink_change_protoinfo(ct, cda); - if (err < 0) - return err; - } - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - if (cda[CTA_MARK-1]) - ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); -#endif - - return 0; -} - -static int -ctnetlink_create_conntrack(struct nfattr *cda[], - struct ip_conntrack_tuple *otuple, - struct ip_conntrack_tuple *rtuple) -{ - struct ip_conntrack *ct; - int err = -EINVAL; - - ct = ip_conntrack_alloc(otuple, rtuple); - if (ct == NULL || IS_ERR(ct)) - return -ENOMEM; - - if (!cda[CTA_TIMEOUT-1]) - goto err; - ct->timeout.expires = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); - - ct->timeout.expires = jiffies + ct->timeout.expires * HZ; - ct->status |= IPS_CONFIRMED; - - if (cda[CTA_STATUS-1]) { - err = ctnetlink_change_status(ct, cda); - if (err < 0) - goto err; - } - - if (cda[CTA_PROTOINFO-1]) { - err = ctnetlink_change_protoinfo(ct, cda); - if (err < 0) - goto err; - } - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - if (cda[CTA_MARK-1]) - ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); -#endif - - ct->helper = ip_conntrack_helper_find_get(rtuple); - - add_timer(&ct->timeout); - ip_conntrack_hash_insert(ct); - - if (ct->helper) - ip_conntrack_helper_put(ct->helper); - - return 0; - -err: - ip_conntrack_free(ct); - return err; -} - -static int -ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple otuple, rtuple; - struct ip_conntrack_tuple_hash *h = NULL; - int err = 0; - - if (nfattr_bad_size(cda, CTA_MAX, cta_min)) - return -EINVAL; - - if (cda[CTA_TUPLE_ORIG-1]) { - err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG); - if (err < 0) - return err; - } - - if (cda[CTA_TUPLE_REPLY-1]) { - err = ctnetlink_parse_tuple(cda, &rtuple, CTA_TUPLE_REPLY); - if (err < 0) - return err; - } - - write_lock_bh(&ip_conntrack_lock); - if (cda[CTA_TUPLE_ORIG-1]) - h = __ip_conntrack_find(&otuple, NULL); - else if (cda[CTA_TUPLE_REPLY-1]) - h = __ip_conntrack_find(&rtuple, NULL); - - if (h == NULL) { - write_unlock_bh(&ip_conntrack_lock); - err = -ENOENT; - if (nlh->nlmsg_flags & NLM_F_CREATE) - err = ctnetlink_create_conntrack(cda, &otuple, &rtuple); - return err; - } - /* implicit 'else' */ - - /* we only allow nat config for new conntracks */ - if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { - err = -EINVAL; - goto out_unlock; - } - - /* We manipulate the conntrack inside the global conntrack table lock, - * so there's no need to increase the refcount */ - err = -EEXIST; - if (!(nlh->nlmsg_flags & NLM_F_EXCL)) - err = ctnetlink_change_conntrack(tuplehash_to_ctrack(h), cda); - -out_unlock: - write_unlock_bh(&ip_conntrack_lock); - return err; -} - -/*********************************************************************** - * EXPECT - ***********************************************************************/ - -static inline int -ctnetlink_exp_dump_tuple(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple, - enum ctattr_expect type) -{ - struct nfattr *nest_parms = NFA_NEST(skb, type); - - if (ctnetlink_dump_tuples(skb, tuple) < 0) - goto nfattr_failure; - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_exp_dump_mask(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask) -{ - int ret; - struct ip_conntrack_protocol *proto; - struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK); - - ret = ctnetlink_dump_tuples_ip(skb, mask); - if (unlikely(ret < 0)) - goto nfattr_failure; - - proto = ip_conntrack_proto_find_get(tuple->dst.protonum); - ret = ctnetlink_dump_tuples_proto(skb, mask, proto); - ip_conntrack_proto_put(proto); - if (unlikely(ret < 0)) - goto nfattr_failure; - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_exp_dump_expect(struct sk_buff *skb, - const struct ip_conntrack_expect *exp) -{ - struct ip_conntrack *master = exp->master; - __be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ); - __be32 id = htonl(exp->id); - - if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0) - goto nfattr_failure; - if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0) - goto nfattr_failure; - if (ctnetlink_exp_dump_tuple(skb, - &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - CTA_EXPECT_MASTER) < 0) - goto nfattr_failure; - - NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(__be32), &timeout); - NFA_PUT(skb, CTA_EXPECT_ID, sizeof(__be32), &id); - - return 0; - -nfattr_failure: - return -1; -} - -static int -ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq, - int event, - int nowait, - const struct ip_conntrack_expect *exp) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - unsigned char *b; - - b = skb->tail; - - event |= NFNL_SUBSYS_CTNETLINK_EXP << 8; - nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (ctnetlink_exp_dump_expect(skb, exp) < 0) - goto nfattr_failure; - - nlh->nlmsg_len = skb->tail - b; - return skb->len; - -nlmsg_failure: -nfattr_failure: - skb_trim(skb, b - skb->data); - return -1; -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -static int ctnetlink_expect_event(struct notifier_block *this, - unsigned long events, void *ptr) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct ip_conntrack_expect *exp = (struct ip_conntrack_expect *)ptr; - struct sk_buff *skb; - unsigned int type; - unsigned char *b; - int flags = 0; - - if (events & IPEXP_NEW) { - type = IPCTNL_MSG_EXP_NEW; - flags = NLM_F_CREATE|NLM_F_EXCL; - } else - return NOTIFY_DONE; - - if (!nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW)) - return NOTIFY_DONE; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); - if (!skb) - return NOTIFY_DONE; - - b = skb->tail; - - type |= NFNL_SUBSYS_CTNETLINK_EXP << 8; - nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = flags; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (ctnetlink_exp_dump_expect(skb, exp) < 0) - goto nfattr_failure; - - nlh->nlmsg_len = skb->tail - b; - nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0); - return NOTIFY_DONE; - -nlmsg_failure: -nfattr_failure: - kfree_skb(skb); - return NOTIFY_DONE; -} -#endif - -static int -ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) -{ - struct ip_conntrack_expect *exp = NULL; - struct list_head *i; - u_int32_t *id = (u_int32_t *) &cb->args[0]; - - read_lock_bh(&ip_conntrack_lock); - list_for_each_prev(i, &ip_conntrack_expect_list) { - exp = (struct ip_conntrack_expect *) i; - if (exp->id <= *id) - continue; - if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - IPCTNL_MSG_EXP_NEW, - 1, exp) < 0) - goto out; - *id = exp->id; - } -out: - read_unlock_bh(&ip_conntrack_lock); - - return skb->len; -} - -static const size_t cta_min_exp[CTA_EXPECT_MAX] = { - [CTA_EXPECT_TIMEOUT-1] = sizeof(__be32), - [CTA_EXPECT_ID-1] = sizeof(__be32) -}; - -static int -ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_expect *exp; - struct sk_buff *skb2; - int err = 0; - - if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) - return -EINVAL; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct nfgenmsg *msg = NLMSG_DATA(nlh); - u32 rlen; - - if (msg->nfgen_family != AF_INET) - return -EAFNOSUPPORT; - - if ((*errp = netlink_dump_start(ctnl, skb, nlh, - ctnetlink_exp_dump_table, - ctnetlink_done)) != 0) - return -EINVAL; - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; - skb_pull(skb, rlen); - return 0; - } - - if (cda[CTA_EXPECT_MASTER-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER); - else - return -EINVAL; - - if (err < 0) - return err; - - exp = ip_conntrack_expect_find_get(&tuple); - if (!exp) - return -ENOENT; - - if (cda[CTA_EXPECT_ID-1]) { - __be32 id = *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); - if (exp->id != ntohl(id)) { - ip_conntrack_expect_put(exp); - return -ENOENT; - } - } - - err = -ENOMEM; - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb2) - goto out; - - err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid, - nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, - 1, exp); - if (err <= 0) - goto free; - - ip_conntrack_expect_put(exp); - - return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); - -free: - kfree_skb(skb2); -out: - ip_conntrack_expect_put(exp); - return err; -} - -static int -ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_expect *exp, *tmp; - struct ip_conntrack_tuple tuple; - struct ip_conntrack_helper *h; - int err; - - if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) - return -EINVAL; - - if (cda[CTA_EXPECT_TUPLE-1]) { - /* delete a single expect by tuple */ - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); - if (err < 0) - return err; - - /* bump usage count to 2 */ - exp = ip_conntrack_expect_find_get(&tuple); - if (!exp) - return -ENOENT; - - if (cda[CTA_EXPECT_ID-1]) { - __be32 id = - *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); - if (exp->id != ntohl(id)) { - ip_conntrack_expect_put(exp); - return -ENOENT; - } - } - - /* after list removal, usage count == 1 */ - ip_conntrack_unexpect_related(exp); - /* have to put what we 'get' above. - * after this line usage count == 0 */ - ip_conntrack_expect_put(exp); - } else if (cda[CTA_EXPECT_HELP_NAME-1]) { - char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]); - - /* delete all expectations for this helper */ - write_lock_bh(&ip_conntrack_lock); - h = __ip_conntrack_helper_find_byname(name); - if (!h) { - write_unlock_bh(&ip_conntrack_lock); - return -EINVAL; - } - list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, - list) { - if (exp->master->helper == h - && del_timer(&exp->timeout)) { - ip_ct_unlink_expect(exp); - ip_conntrack_expect_put(exp); - } - } - write_unlock_bh(&ip_conntrack_lock); - } else { - /* This basically means we have to flush everything*/ - write_lock_bh(&ip_conntrack_lock); - list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, - list) { - if (del_timer(&exp->timeout)) { - ip_ct_unlink_expect(exp); - ip_conntrack_expect_put(exp); - } - } - write_unlock_bh(&ip_conntrack_lock); - } - - return 0; -} -static int -ctnetlink_change_expect(struct ip_conntrack_expect *x, struct nfattr *cda[]) -{ - return -EOPNOTSUPP; -} - -static int -ctnetlink_create_expect(struct nfattr *cda[]) -{ - struct ip_conntrack_tuple tuple, mask, master_tuple; - struct ip_conntrack_tuple_hash *h = NULL; - struct ip_conntrack_expect *exp; - struct ip_conntrack *ct; - int err = 0; - - /* caller guarantees that those three CTA_EXPECT_* exist */ - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); - if (err < 0) - return err; - err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK); - if (err < 0) - return err; - err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER); - if (err < 0) - return err; - - /* Look for master conntrack of this expectation */ - h = ip_conntrack_find_get(&master_tuple, NULL); - if (!h) - return -ENOENT; - ct = tuplehash_to_ctrack(h); - - if (!ct->helper) { - /* such conntrack hasn't got any helper, abort */ - err = -EINVAL; - goto out; - } - - exp = ip_conntrack_expect_alloc(ct); - if (!exp) { - err = -ENOMEM; - goto out; - } - - exp->expectfn = NULL; - exp->flags = 0; - exp->master = ct; - memcpy(&exp->tuple, &tuple, sizeof(struct ip_conntrack_tuple)); - memcpy(&exp->mask, &mask, sizeof(struct ip_conntrack_tuple)); - - err = ip_conntrack_expect_related(exp); - ip_conntrack_expect_put(exp); - -out: - ip_conntrack_put(tuplehash_to_ctrack(h)); - return err; -} - -static int -ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_expect *exp; - int err = 0; - - if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) - return -EINVAL; - - if (!cda[CTA_EXPECT_TUPLE-1] - || !cda[CTA_EXPECT_MASK-1] - || !cda[CTA_EXPECT_MASTER-1]) - return -EINVAL; - - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); - if (err < 0) - return err; - - write_lock_bh(&ip_conntrack_lock); - exp = __ip_conntrack_expect_find(&tuple); - - if (!exp) { - write_unlock_bh(&ip_conntrack_lock); - err = -ENOENT; - if (nlh->nlmsg_flags & NLM_F_CREATE) - err = ctnetlink_create_expect(cda); - return err; - } - - err = -EEXIST; - if (!(nlh->nlmsg_flags & NLM_F_EXCL)) - err = ctnetlink_change_expect(exp, cda); - write_unlock_bh(&ip_conntrack_lock); - - return err; -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -static struct notifier_block ctnl_notifier = { - .notifier_call = ctnetlink_conntrack_event, -}; - -static struct notifier_block ctnl_notifier_exp = { - .notifier_call = ctnetlink_expect_event, -}; -#endif - -static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = { - [IPCTNL_MSG_CT_NEW] = { .call = ctnetlink_new_conntrack, - .attr_count = CTA_MAX, }, - [IPCTNL_MSG_CT_GET] = { .call = ctnetlink_get_conntrack, - .attr_count = CTA_MAX, }, - [IPCTNL_MSG_CT_DELETE] = { .call = ctnetlink_del_conntrack, - .attr_count = CTA_MAX, }, - [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack, - .attr_count = CTA_MAX, }, -}; - -static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = { - [IPCTNL_MSG_EXP_GET] = { .call = ctnetlink_get_expect, - .attr_count = CTA_EXPECT_MAX, }, - [IPCTNL_MSG_EXP_NEW] = { .call = ctnetlink_new_expect, - .attr_count = CTA_EXPECT_MAX, }, - [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect, - .attr_count = CTA_EXPECT_MAX, }, -}; - -static struct nfnetlink_subsystem ctnl_subsys = { - .name = "conntrack", - .subsys_id = NFNL_SUBSYS_CTNETLINK, - .cb_count = IPCTNL_MSG_MAX, - .cb = ctnl_cb, -}; - -static struct nfnetlink_subsystem ctnl_exp_subsys = { - .name = "conntrack_expect", - .subsys_id = NFNL_SUBSYS_CTNETLINK_EXP, - .cb_count = IPCTNL_MSG_EXP_MAX, - .cb = ctnl_exp_cb, -}; - -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK); -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP); - -static int __init ctnetlink_init(void) -{ - int ret; - - printk("ctnetlink v%s: registering with nfnetlink.\n", version); - ret = nfnetlink_subsys_register(&ctnl_subsys); - if (ret < 0) { - printk("ctnetlink_init: cannot register with nfnetlink.\n"); - goto err_out; - } - - ret = nfnetlink_subsys_register(&ctnl_exp_subsys); - if (ret < 0) { - printk("ctnetlink_init: cannot register exp with nfnetlink.\n"); - goto err_unreg_subsys; - } - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS - ret = ip_conntrack_register_notifier(&ctnl_notifier); - if (ret < 0) { - printk("ctnetlink_init: cannot register notifier.\n"); - goto err_unreg_exp_subsys; - } - - ret = ip_conntrack_expect_register_notifier(&ctnl_notifier_exp); - if (ret < 0) { - printk("ctnetlink_init: cannot expect register notifier.\n"); - goto err_unreg_notifier; - } -#endif - - return 0; - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -err_unreg_notifier: - ip_conntrack_unregister_notifier(&ctnl_notifier); -err_unreg_exp_subsys: - nfnetlink_subsys_unregister(&ctnl_exp_subsys); -#endif -err_unreg_subsys: - nfnetlink_subsys_unregister(&ctnl_subsys); -err_out: - return ret; -} - -static void __exit ctnetlink_exit(void) -{ - printk("ctnetlink: unregistering from nfnetlink.\n"); - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS - ip_conntrack_expect_unregister_notifier(&ctnl_notifier_exp); - ip_conntrack_unregister_notifier(&ctnl_notifier); -#endif - - nfnetlink_subsys_unregister(&ctnl_exp_subsys); - nfnetlink_subsys_unregister(&ctnl_subsys); - return; -} - -module_init(ctnetlink_init); -module_exit(ctnetlink_exit); diff --git a/net/ipv4/netfilter/ip_conntrack_proto_generic.c b/net/ipv4/netfilter/ip_conntrack_proto_generic.c deleted file mode 100644 index 88af82e..0000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_generic.c +++ /dev/null @@ -1,74 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -unsigned int ip_ct_generic_timeout __read_mostly = 600*HZ; - -static int generic_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - tuple->src.u.all = 0; - tuple->dst.u.all = 0; - - return 1; -} - -static int generic_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->src.u.all = 0; - tuple->dst.u.all = 0; - - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int generic_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return 0; -} - -/* Print out the private part of the conntrack. */ -static int generic_print_conntrack(struct seq_file *s, - const struct ip_conntrack *state) -{ - return 0; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_generic_timeout); - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int new(struct ip_conntrack *conntrack, const struct sk_buff *skb) -{ - return 1; -} - -struct ip_conntrack_protocol ip_conntrack_generic_protocol = -{ - .proto = 0, - .name = "unknown", - .pkt_to_tuple = generic_pkt_to_tuple, - .invert_tuple = generic_invert_tuple, - .print_tuple = generic_print_tuple, - .print_conntrack = generic_print_conntrack, - .packet = packet, - .new = new, -}; diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c deleted file mode 100644 index ac1c49e..0000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_gre.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * ip_conntrack_proto_gre.c - Version 3.0 - * - * Connection tracking protocol helper module for GRE. - * - * GRE is a generic encapsulation protocol, which is generally not very - * suited for NAT, as it has no protocol-specific part as port numbers. - * - * It has an optional key field, which may help us distinguishing two - * connections between the same two hosts. - * - * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 - * - * PPTP is built on top of a modified version of GRE, and has a mandatory - * field called "CallID", which serves us for the same purpose as the key - * field in plain GRE. - * - * Documentation about PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static DEFINE_RWLOCK(ip_ct_gre_lock); - -#include -#include -#include - -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); - -/* shamelessly stolen from ip_conntrack_proto_udp.c */ -#define GRE_TIMEOUT (30*HZ) -#define GRE_STREAM_TIMEOUT (180*HZ) - -#if 0 -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) -#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \ - NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \ - NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key)) -#else -#define DEBUGP(x, args...) -#define DUMP_TUPLE_GRE(x) -#endif - -/* GRE KEYMAP HANDLING FUNCTIONS */ -static LIST_HEAD(gre_keymap_list); - -static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, - const struct ip_conntrack_tuple *t) -{ - return ((km->tuple.src.ip == t->src.ip) && - (km->tuple.dst.ip == t->dst.ip) && - (km->tuple.dst.protonum == t->dst.protonum) && - (km->tuple.dst.u.all == t->dst.u.all)); -} - -/* look up the source key for a given tuple */ -static __be16 gre_keymap_lookup(struct ip_conntrack_tuple *t) -{ - struct ip_ct_gre_keymap *km; - __be16 key = 0; - - read_lock_bh(&ip_ct_gre_lock); - list_for_each_entry(km, &gre_keymap_list, list) { - if (gre_key_cmpfn(km, t)) { - key = km->tuple.src.u.gre.key; - break; - } - } - read_unlock_bh(&ip_ct_gre_lock); - - DEBUGP("lookup src key 0x%x up key for ", key); - DUMP_TUPLE_GRE(t); - - return key; -} - -/* add a single keymap entry, associate with specified master ct */ -int -ip_ct_gre_keymap_add(struct ip_conntrack *ct, - struct ip_conntrack_tuple *t, int reply) -{ - struct ip_ct_gre_keymap **exist_km, *km; - - if (!ct->helper || strcmp(ct->helper->name, "pptp")) { - DEBUGP("refusing to add GRE keymap to non-pptp session\n"); - return -1; - } - - if (!reply) - exist_km = &ct->help.ct_pptp_info.keymap_orig; - else - exist_km = &ct->help.ct_pptp_info.keymap_reply; - - if (*exist_km) { - /* check whether it's a retransmission */ - list_for_each_entry(km, &gre_keymap_list, list) { - if (gre_key_cmpfn(km, t) && km == *exist_km) - return 0; - } - DEBUGP("trying to override keymap_%s for ct %p\n", - reply? "reply":"orig", ct); - return -EEXIST; - } - - km = kmalloc(sizeof(*km), GFP_ATOMIC); - if (!km) - return -ENOMEM; - - memcpy(&km->tuple, t, sizeof(*t)); - *exist_km = km; - - DEBUGP("adding new entry %p: ", km); - DUMP_TUPLE_GRE(&km->tuple); - - write_lock_bh(&ip_ct_gre_lock); - list_add_tail(&km->list, &gre_keymap_list); - write_unlock_bh(&ip_ct_gre_lock); - - return 0; -} - -/* destroy the keymap entries associated with specified master ct */ -void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct) -{ - DEBUGP("entering for ct %p\n", ct); - - if (!ct->helper || strcmp(ct->helper->name, "pptp")) { - DEBUGP("refusing to destroy GRE keymap to non-pptp session\n"); - return; - } - - write_lock_bh(&ip_ct_gre_lock); - if (ct->help.ct_pptp_info.keymap_orig) { - DEBUGP("removing %p from list\n", - ct->help.ct_pptp_info.keymap_orig); - list_del(&ct->help.ct_pptp_info.keymap_orig->list); - kfree(ct->help.ct_pptp_info.keymap_orig); - ct->help.ct_pptp_info.keymap_orig = NULL; - } - if (ct->help.ct_pptp_info.keymap_reply) { - DEBUGP("removing %p from list\n", - ct->help.ct_pptp_info.keymap_reply); - list_del(&ct->help.ct_pptp_info.keymap_reply->list); - kfree(ct->help.ct_pptp_info.keymap_reply); - ct->help.ct_pptp_info.keymap_reply = NULL; - } - write_unlock_bh(&ip_ct_gre_lock); -} - - -/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ - -/* invert gre part of tuple */ -static int gre_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->dst.u.gre.key = orig->src.u.gre.key; - tuple->src.u.gre.key = orig->dst.u.gre.key; - - return 1; -} - -/* gre hdr info to tuple */ -static int gre_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct gre_hdr_pptp _pgrehdr, *pgrehdr; - __be16 srckey; - struct gre_hdr _grehdr, *grehdr; - - /* first only delinearize old RFC1701 GRE header */ - grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr); - if (!grehdr || grehdr->version != GRE_VERSION_PPTP) { - /* try to behave like "ip_conntrack_proto_generic" */ - tuple->src.u.all = 0; - tuple->dst.u.all = 0; - return 1; - } - - /* PPTP header is variable length, only need up to the call_id field */ - pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr); - if (!pgrehdr) - return 1; - - if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { - DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); - return 0; - } - - tuple->dst.u.gre.key = pgrehdr->call_id; - srckey = gre_keymap_lookup(tuple); - tuple->src.u.gre.key = srckey; - - return 1; -} - -/* print gre part of tuple */ -static int gre_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "srckey=0x%x dstkey=0x%x ", - ntohs(tuple->src.u.gre.key), - ntohs(tuple->dst.u.gre.key)); -} - -/* print private data for conntrack */ -static int gre_print_conntrack(struct seq_file *s, - const struct ip_conntrack *ct) -{ - return seq_printf(s, "timeout=%u, stream_timeout=%u ", - (ct->proto.gre.timeout / HZ), - (ct->proto.gre.stream_timeout / HZ)); -} - -/* Returns verdict for packet, and may modify conntrack */ -static int gre_packet(struct ip_conntrack *ct, - const struct sk_buff *skb, - enum ip_conntrack_info conntrackinfo) -{ - /* If we've seen traffic both ways, this is a GRE connection. - * Extend timeout. */ - if (ct->status & IPS_SEEN_REPLY) { - ip_ct_refresh_acct(ct, conntrackinfo, skb, - ct->proto.gre.stream_timeout); - /* Also, more likely to be important, and not a probe. */ - set_bit(IPS_ASSURED_BIT, &ct->status); - ip_conntrack_event_cache(IPCT_STATUS, skb); - } else - ip_ct_refresh_acct(ct, conntrackinfo, skb, - ct->proto.gre.timeout); - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int gre_new(struct ip_conntrack *ct, - const struct sk_buff *skb) -{ - DEBUGP(": "); - DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - - /* initialize to sane value. Ideally a conntrack helper - * (e.g. in case of pptp) is increasing them */ - ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; - ct->proto.gre.timeout = GRE_TIMEOUT; - - return 1; -} - -/* Called when a conntrack entry has already been removed from the hashes - * and is about to be deleted from memory */ -static void gre_destroy(struct ip_conntrack *ct) -{ - struct ip_conntrack *master = ct->master; - DEBUGP(" entering\n"); - - if (!master) - DEBUGP("no master !?!\n"); - else - ip_ct_gre_keymap_destroy(master); -} - -/* protocol helper struct */ -static struct ip_conntrack_protocol gre = { - .proto = IPPROTO_GRE, - .name = "gre", - .pkt_to_tuple = gre_pkt_to_tuple, - .invert_tuple = gre_invert_tuple, - .print_tuple = gre_print_tuple, - .print_conntrack = gre_print_conntrack, - .packet = gre_packet, - .new = gre_new, - .destroy = gre_destroy, - .me = THIS_MODULE, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; - -/* ip_conntrack_proto_gre initialization */ -int __init ip_ct_proto_gre_init(void) -{ - return ip_conntrack_protocol_register(&gre); -} - -/* This cannot be __exit, as it is invoked from ip_conntrack_helper_pptp.c's - * init() code on errors. - */ -void ip_ct_proto_gre_fini(void) -{ - struct list_head *pos, *n; - - /* delete all keymap entries */ - write_lock_bh(&ip_ct_gre_lock); - list_for_each_safe(pos, n, &gre_keymap_list) { - DEBUGP("deleting keymap %p at module unload time\n", pos); - list_del(pos); - kfree(pos); - } - write_unlock_bh(&ip_ct_gre_lock); - - ip_conntrack_protocol_unregister(&gre); -} - -EXPORT_SYMBOL(ip_ct_gre_keymap_add); -EXPORT_SYMBOL(ip_ct_gre_keymap_destroy); diff --git a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c deleted file mode 100644 index ad70c81..0000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c +++ /dev/null @@ -1,315 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -unsigned int ip_ct_icmp_timeout __read_mostly = 30*HZ; - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -static int icmp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct icmphdr _hdr, *hp; - - hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); - if (hp == NULL) - return 0; - - tuple->dst.u.icmp.type = hp->type; - tuple->src.u.icmp.id = hp->un.echo.id; - tuple->dst.u.icmp.code = hp->code; - - return 1; -} - -/* Add 1; spaces filled with 0. */ -static const u_int8_t invmap[] = { - [ICMP_ECHO] = ICMP_ECHOREPLY + 1, - [ICMP_ECHOREPLY] = ICMP_ECHO + 1, - [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1, - [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1, - [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1, - [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1, - [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1, - [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1 -}; - -static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - if (orig->dst.u.icmp.type >= sizeof(invmap) - || !invmap[orig->dst.u.icmp.type]) - return 0; - - tuple->src.u.icmp.id = orig->src.u.icmp.id; - tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1; - tuple->dst.u.icmp.code = orig->dst.u.icmp.code; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int icmp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "type=%u code=%u id=%u ", - tuple->dst.u.icmp.type, - tuple->dst.u.icmp.code, - ntohs(tuple->src.u.icmp.id)); -} - -/* Print out the private part of the conntrack. */ -static int icmp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - return 0; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int icmp_packet(struct ip_conntrack *ct, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - /* Try to delete connection immediately after all replies: - won't actually vanish as we still have skb, and del_timer - means this will only run once even if count hits zero twice - (theoretically possible with SMP) */ - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { - if (atomic_dec_and_test(&ct->proto.icmp.count) - && del_timer(&ct->timeout)) - ct->timeout.function((unsigned long)ct); - } else { - atomic_inc(&ct->proto.icmp.count); - ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); - ip_ct_refresh_acct(ct, ctinfo, skb, ip_ct_icmp_timeout); - } - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int icmp_new(struct ip_conntrack *conntrack, - const struct sk_buff *skb) -{ - static const u_int8_t valid_new[] = { - [ICMP_ECHO] = 1, - [ICMP_TIMESTAMP] = 1, - [ICMP_INFO_REQUEST] = 1, - [ICMP_ADDRESS] = 1 - }; - - if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) - || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) { - /* Can't create a new ICMP `conn' with this. */ - DEBUGP("icmp: can't create new conn with type %u\n", - conntrack->tuplehash[0].tuple.dst.u.icmp.type); - DUMP_TUPLE(&conntrack->tuplehash[0].tuple); - return 0; - } - atomic_set(&conntrack->proto.icmp.count, 0); - return 1; -} - -static int -icmp_error_message(struct sk_buff *skb, - enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - struct ip_conntrack_tuple innertuple, origtuple; - struct { - struct icmphdr icmp; - struct iphdr ip; - } _in, *inside; - struct ip_conntrack_protocol *innerproto; - struct ip_conntrack_tuple_hash *h; - int dataoff; - - IP_NF_ASSERT(skb->nfct == NULL); - - /* Not enough header? */ - inside = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_in), &_in); - if (inside == NULL) - return -NF_ACCEPT; - - /* Ignore ICMP's containing fragments (shouldn't happen) */ - if (inside->ip.frag_off & htons(IP_OFFSET)) { - DEBUGP("icmp_error_track: fragment of proto %u\n", - inside->ip.protocol); - return -NF_ACCEPT; - } - - innerproto = ip_conntrack_proto_find_get(inside->ip.protocol); - dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp) + inside->ip.ihl*4; - /* Are they talking about one of our connections? */ - if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto)) { - DEBUGP("icmp_error: ! get_tuple p=%u", inside->ip.protocol); - ip_conntrack_proto_put(innerproto); - return -NF_ACCEPT; - } - - /* Ordinarily, we'd expect the inverted tupleproto, but it's - been preserved inside the ICMP. */ - if (!ip_ct_invert_tuple(&innertuple, &origtuple, innerproto)) { - DEBUGP("icmp_error_track: Can't invert tuple\n"); - ip_conntrack_proto_put(innerproto); - return -NF_ACCEPT; - } - ip_conntrack_proto_put(innerproto); - - *ctinfo = IP_CT_RELATED; - - h = ip_conntrack_find_get(&innertuple, NULL); - if (!h) { - /* Locally generated ICMPs will match inverted if they - haven't been SNAT'ed yet */ - /* FIXME: NAT code has to handle half-done double NAT --RR */ - if (hooknum == NF_IP_LOCAL_OUT) - h = ip_conntrack_find_get(&origtuple, NULL); - - if (!h) { - DEBUGP("icmp_error_track: no match\n"); - return -NF_ACCEPT; - } - /* Reverse direction from that found */ - if (DIRECTION(h) != IP_CT_DIR_REPLY) - *ctinfo += IP_CT_IS_REPLY; - } else { - if (DIRECTION(h) == IP_CT_DIR_REPLY) - *ctinfo += IP_CT_IS_REPLY; - } - - /* Update skb to refer to this connection */ - skb->nfct = &tuplehash_to_ctrack(h)->ct_general; - skb->nfctinfo = *ctinfo; - return -NF_ACCEPT; -} - -/* Small and modified version of icmp_rcv */ -static int -icmp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - struct icmphdr _ih, *icmph; - - /* Not enough header? */ - icmph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_ih), &_ih); - if (icmph == NULL) { - if (LOG_INVALID(IPPROTO_ICMP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_icmp: short packet "); - return -NF_ACCEPT; - } - - /* See ip_conntrack_proto_tcp.c */ - if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && - nf_ip_checksum(skb, hooknum, skb->nh.iph->ihl * 4, 0)) { - if (LOG_INVALID(IPPROTO_ICMP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_icmp: bad ICMP checksum "); - return -NF_ACCEPT; - } - - /* - * 18 is the highest 'known' ICMP type. Anything else is a mystery - * - * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently - * discarded. - */ - if (icmph->type > NR_ICMP_TYPES) { - if (LOG_INVALID(IPPROTO_ICMP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_icmp: invalid ICMP type "); - return -NF_ACCEPT; - } - - /* Need to track icmp error message? */ - if (icmph->type != ICMP_DEST_UNREACH - && icmph->type != ICMP_SOURCE_QUENCH - && icmph->type != ICMP_TIME_EXCEEDED - && icmph->type != ICMP_PARAMETERPROB - && icmph->type != ICMP_REDIRECT) - return NF_ACCEPT; - - return icmp_error_message(skb, ctinfo, hooknum); -} - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -static int icmp_tuple_to_nfattr(struct sk_buff *skb, - const struct ip_conntrack_tuple *t) -{ - NFA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(__be16), - &t->src.u.icmp.id); - NFA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t), - &t->dst.u.icmp.type); - NFA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t), - &t->dst.u.icmp.code); - - return 0; - -nfattr_failure: - return -1; -} - -static int icmp_nfattr_to_tuple(struct nfattr *tb[], - struct ip_conntrack_tuple *tuple) -{ - if (!tb[CTA_PROTO_ICMP_TYPE-1] - || !tb[CTA_PROTO_ICMP_CODE-1] - || !tb[CTA_PROTO_ICMP_ID-1]) - return -EINVAL; - - tuple->dst.u.icmp.type = - *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]); - tuple->dst.u.icmp.code = - *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]); - tuple->src.u.icmp.id = - *(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]); - - if (tuple->dst.u.icmp.type >= sizeof(invmap) - || !invmap[tuple->dst.u.icmp.type]) - return -EINVAL; - - return 0; -} -#endif - -struct ip_conntrack_protocol ip_conntrack_protocol_icmp = -{ - .proto = IPPROTO_ICMP, - .name = "icmp", - .pkt_to_tuple = icmp_pkt_to_tuple, - .invert_tuple = icmp_invert_tuple, - .print_tuple = icmp_print_tuple, - .print_conntrack = icmp_print_conntrack, - .packet = icmp_packet, - .new = icmp_new, - .error = icmp_error, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = icmp_tuple_to_nfattr, - .nfattr_to_tuple = icmp_nfattr_to_tuple, -#endif -}; diff --git a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c b/net/ipv4/netfilter/ip_conntrack_proto_sctp.c deleted file mode 100644 index e694299..0000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c +++ /dev/null @@ -1,659 +0,0 @@ -/* - * Connection tracking protocol helper module for SCTP. - * - * SCTP is defined in RFC 2960. References to various sections in this code - * are to this RFC. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * Added support for proc manipulation of timeouts. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#if 0 -#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__) -#else -#define DEBUGP(format, args...) -#endif - -/* Protects conntrack->proto.sctp */ -static DEFINE_RWLOCK(sctp_lock); - -/* FIXME: Examine ipfilter's timeouts and conntrack transitions more - closely. They're more complex. --RR - - And so for me for SCTP :D -Kiran */ - -static const char *sctp_conntrack_names[] = { - "NONE", - "CLOSED", - "COOKIE_WAIT", - "COOKIE_ECHOED", - "ESTABLISHED", - "SHUTDOWN_SENT", - "SHUTDOWN_RECD", - "SHUTDOWN_ACK_SENT", -}; - -#define SECS * HZ -#define MINS * 60 SECS -#define HOURS * 60 MINS -#define DAYS * 24 HOURS - -static unsigned int ip_ct_sctp_timeout_closed __read_mostly = 10 SECS; -static unsigned int ip_ct_sctp_timeout_cookie_wait __read_mostly = 3 SECS; -static unsigned int ip_ct_sctp_timeout_cookie_echoed __read_mostly = 3 SECS; -static unsigned int ip_ct_sctp_timeout_established __read_mostly = 5 DAYS; -static unsigned int ip_ct_sctp_timeout_shutdown_sent __read_mostly = 300 SECS / 1000; -static unsigned int ip_ct_sctp_timeout_shutdown_recd __read_mostly = 300 SECS / 1000; -static unsigned int ip_ct_sctp_timeout_shutdown_ack_sent __read_mostly = 3 SECS; - -static const unsigned int * sctp_timeouts[] -= { NULL, /* SCTP_CONNTRACK_NONE */ - &ip_ct_sctp_timeout_closed, /* SCTP_CONNTRACK_CLOSED */ - &ip_ct_sctp_timeout_cookie_wait, /* SCTP_CONNTRACK_COOKIE_WAIT */ - &ip_ct_sctp_timeout_cookie_echoed, /* SCTP_CONNTRACK_COOKIE_ECHOED */ - &ip_ct_sctp_timeout_established, /* SCTP_CONNTRACK_ESTABLISHED */ - &ip_ct_sctp_timeout_shutdown_sent, /* SCTP_CONNTRACK_SHUTDOWN_SENT */ - &ip_ct_sctp_timeout_shutdown_recd, /* SCTP_CONNTRACK_SHUTDOWN_RECD */ - &ip_ct_sctp_timeout_shutdown_ack_sent /* SCTP_CONNTRACK_SHUTDOWN_ACK_SENT */ - }; - -#define sNO SCTP_CONNTRACK_NONE -#define sCL SCTP_CONNTRACK_CLOSED -#define sCW SCTP_CONNTRACK_COOKIE_WAIT -#define sCE SCTP_CONNTRACK_COOKIE_ECHOED -#define sES SCTP_CONNTRACK_ESTABLISHED -#define sSS SCTP_CONNTRACK_SHUTDOWN_SENT -#define sSR SCTP_CONNTRACK_SHUTDOWN_RECD -#define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT -#define sIV SCTP_CONNTRACK_MAX - -/* - These are the descriptions of the states: - -NOTE: These state names are tantalizingly similar to the states of an -SCTP endpoint. But the interpretation of the states is a little different, -considering that these are the states of the connection and not of an end -point. Please note the subtleties. -Kiran - -NONE - Nothing so far. -COOKIE WAIT - We have seen an INIT chunk in the original direction, or also - an INIT_ACK chunk in the reply direction. -COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction. -ESTABLISHED - We have seen a COOKIE_ACK in the reply direction. -SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction. -SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply directoin. -SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite - to that of the SHUTDOWN chunk. -CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of - the SHUTDOWN chunk. Connection is closed. -*/ - -/* TODO - - I have assumed that the first INIT is in the original direction. - This messes things when an INIT comes in the reply direction in CLOSED - state. - - Check the error type in the reply dir before transitioning from -cookie echoed to closed. - - Sec 5.2.4 of RFC 2960 - - Multi Homing support. -*/ - -/* SCTP conntrack state transitions */ -static const enum sctp_conntrack sctp_conntracks[2][9][SCTP_CONNTRACK_MAX] = { - { -/* ORIGINAL */ -/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */ -/* init */ {sCW, sCW, sCW, sCE, sES, sSS, sSR, sSA}, -/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA}, -/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, -/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA}, -/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA}, -/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant have Stale cookie*/ -/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA},/* 5.2.4 - Big TODO */ -/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in orig dir */ -/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL} - }, - { -/* REPLY */ -/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */ -/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* INIT in sCL Big TODO */ -/* init_ack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA}, -/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, -/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA}, -/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA}, -/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA}, -/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in reply dir */ -/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA}, -/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL} - } -}; - -static int sctp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - sctp_sctphdr_t _hdr, *hp; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - /* Actually only need first 8 bytes. */ - hp = skb_header_pointer(skb, dataoff, 8, &_hdr); - if (hp == NULL) - return 0; - - tuple->src.u.sctp.port = hp->source; - tuple->dst.u.sctp.port = hp->dest; - return 1; -} - -static int sctp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - tuple->src.u.sctp.port = orig->dst.u.sctp.port; - tuple->dst.u.sctp.port = orig->src.u.sctp.port; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int sctp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.sctp.port), - ntohs(tuple->dst.u.sctp.port)); -} - -/* Print out the private part of the conntrack. */ -static int sctp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - enum sctp_conntrack state; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - read_lock_bh(&sctp_lock); - state = conntrack->proto.sctp.state; - read_unlock_bh(&sctp_lock); - - return seq_printf(s, "%s ", sctp_conntrack_names[state]); -} - -#define for_each_sctp_chunk(skb, sch, _sch, offset, count) \ -for (offset = skb->nh.iph->ihl * 4 + sizeof(sctp_sctphdr_t), count = 0; \ - offset < skb->len && \ - (sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch)); \ - offset += (ntohs(sch->length) + 3) & ~3, count++) - -/* Some validity checks to make sure the chunks are fine */ -static int do_basic_checks(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - char *map) -{ - u_int32_t offset, count; - sctp_chunkhdr_t _sch, *sch; - int flag; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - flag = 0; - - for_each_sctp_chunk (skb, sch, _sch, offset, count) { - DEBUGP("Chunk Num: %d Type: %d\n", count, sch->type); - - if (sch->type == SCTP_CID_INIT - || sch->type == SCTP_CID_INIT_ACK - || sch->type == SCTP_CID_SHUTDOWN_COMPLETE) { - flag = 1; - } - - /* - * Cookie Ack/Echo chunks not the first OR - * Init / Init Ack / Shutdown compl chunks not the only chunks - * OR zero-length. - */ - if (((sch->type == SCTP_CID_COOKIE_ACK - || sch->type == SCTP_CID_COOKIE_ECHO - || flag) - && count !=0) || !sch->length) { - DEBUGP("Basic checks failed\n"); - return 1; - } - - if (map) { - set_bit(sch->type, (void *)map); - } - } - - DEBUGP("Basic checks passed\n"); - return count == 0; -} - -static int new_state(enum ip_conntrack_dir dir, - enum sctp_conntrack cur_state, - int chunk_type) -{ - int i; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - DEBUGP("Chunk type: %d\n", chunk_type); - - switch (chunk_type) { - case SCTP_CID_INIT: - DEBUGP("SCTP_CID_INIT\n"); - i = 0; break; - case SCTP_CID_INIT_ACK: - DEBUGP("SCTP_CID_INIT_ACK\n"); - i = 1; break; - case SCTP_CID_ABORT: - DEBUGP("SCTP_CID_ABORT\n"); - i = 2; break; - case SCTP_CID_SHUTDOWN: - DEBUGP("SCTP_CID_SHUTDOWN\n"); - i = 3; break; - case SCTP_CID_SHUTDOWN_ACK: - DEBUGP("SCTP_CID_SHUTDOWN_ACK\n"); - i = 4; break; - case SCTP_CID_ERROR: - DEBUGP("SCTP_CID_ERROR\n"); - i = 5; break; - case SCTP_CID_COOKIE_ECHO: - DEBUGP("SCTP_CID_COOKIE_ECHO\n"); - i = 6; break; - case SCTP_CID_COOKIE_ACK: - DEBUGP("SCTP_CID_COOKIE_ACK\n"); - i = 7; break; - case SCTP_CID_SHUTDOWN_COMPLETE: - DEBUGP("SCTP_CID_SHUTDOWN_COMPLETE\n"); - i = 8; break; - default: - /* Other chunks like DATA, SACK, HEARTBEAT and - its ACK do not cause a change in state */ - DEBUGP("Unknown chunk type, Will stay in %s\n", - sctp_conntrack_names[cur_state]); - return cur_state; - } - - DEBUGP("dir: %d cur_state: %s chunk_type: %d new_state: %s\n", - dir, sctp_conntrack_names[cur_state], chunk_type, - sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]); - - return sctp_conntracks[dir][i][cur_state]; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int sctp_packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - enum sctp_conntrack newconntrack, oldsctpstate; - struct iphdr *iph = skb->nh.iph; - sctp_sctphdr_t _sctph, *sh; - sctp_chunkhdr_t _sch, *sch; - u_int32_t offset, count; - char map[256 / sizeof (char)] = {0}; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - sh = skb_header_pointer(skb, iph->ihl * 4, sizeof(_sctph), &_sctph); - if (sh == NULL) - return -1; - - if (do_basic_checks(conntrack, skb, map) != 0) - return -1; - - /* Check the verification tag (Sec 8.5) */ - if (!test_bit(SCTP_CID_INIT, (void *)map) - && !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, (void *)map) - && !test_bit(SCTP_CID_COOKIE_ECHO, (void *)map) - && !test_bit(SCTP_CID_ABORT, (void *)map) - && !test_bit(SCTP_CID_SHUTDOWN_ACK, (void *)map) - && (sh->vtag != conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) { - DEBUGP("Verification tag check failed\n"); - return -1; - } - - oldsctpstate = newconntrack = SCTP_CONNTRACK_MAX; - for_each_sctp_chunk (skb, sch, _sch, offset, count) { - write_lock_bh(&sctp_lock); - - /* Special cases of Verification tag check (Sec 8.5.1) */ - if (sch->type == SCTP_CID_INIT) { - /* Sec 8.5.1 (A) */ - if (sh->vtag != 0) { - write_unlock_bh(&sctp_lock); - return -1; - } - } else if (sch->type == SCTP_CID_ABORT) { - /* Sec 8.5.1 (B) */ - if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)]) - && !(sh->vtag == conntrack->proto.sctp.vtag - [1 - CTINFO2DIR(ctinfo)])) { - write_unlock_bh(&sctp_lock); - return -1; - } - } else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) { - /* Sec 8.5.1 (C) */ - if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)]) - && !(sh->vtag == conntrack->proto.sctp.vtag - [1 - CTINFO2DIR(ctinfo)] - && (sch->flags & 1))) { - write_unlock_bh(&sctp_lock); - return -1; - } - } else if (sch->type == SCTP_CID_COOKIE_ECHO) { - /* Sec 8.5.1 (D) */ - if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) { - write_unlock_bh(&sctp_lock); - return -1; - } - } - - oldsctpstate = conntrack->proto.sctp.state; - newconntrack = new_state(CTINFO2DIR(ctinfo), oldsctpstate, sch->type); - - /* Invalid */ - if (newconntrack == SCTP_CONNTRACK_MAX) { - DEBUGP("ip_conntrack_sctp: Invalid dir=%i ctype=%u conntrack=%u\n", - CTINFO2DIR(ctinfo), sch->type, oldsctpstate); - write_unlock_bh(&sctp_lock); - return -1; - } - - /* If it is an INIT or an INIT ACK note down the vtag */ - if (sch->type == SCTP_CID_INIT - || sch->type == SCTP_CID_INIT_ACK) { - sctp_inithdr_t _inithdr, *ih; - - ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t), - sizeof(_inithdr), &_inithdr); - if (ih == NULL) { - write_unlock_bh(&sctp_lock); - return -1; - } - DEBUGP("Setting vtag %x for dir %d\n", - ih->init_tag, !CTINFO2DIR(ctinfo)); - conntrack->proto.sctp.vtag[!CTINFO2DIR(ctinfo)] = ih->init_tag; - } - - conntrack->proto.sctp.state = newconntrack; - if (oldsctpstate != newconntrack) - ip_conntrack_event_cache(IPCT_PROTOINFO, skb); - write_unlock_bh(&sctp_lock); - } - - ip_ct_refresh_acct(conntrack, ctinfo, skb, *sctp_timeouts[newconntrack]); - - if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED - && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY - && newconntrack == SCTP_CONNTRACK_ESTABLISHED) { - DEBUGP("Setting assured bit\n"); - set_bit(IPS_ASSURED_BIT, &conntrack->status); - ip_conntrack_event_cache(IPCT_STATUS, skb); - } - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int sctp_new(struct ip_conntrack *conntrack, - const struct sk_buff *skb) -{ - enum sctp_conntrack newconntrack; - struct iphdr *iph = skb->nh.iph; - sctp_sctphdr_t _sctph, *sh; - sctp_chunkhdr_t _sch, *sch; - u_int32_t offset, count; - char map[256 / sizeof (char)] = {0}; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - sh = skb_header_pointer(skb, iph->ihl * 4, sizeof(_sctph), &_sctph); - if (sh == NULL) - return 0; - - if (do_basic_checks(conntrack, skb, map) != 0) - return 0; - - /* If an OOTB packet has any of these chunks discard (Sec 8.4) */ - if ((test_bit (SCTP_CID_ABORT, (void *)map)) - || (test_bit (SCTP_CID_SHUTDOWN_COMPLETE, (void *)map)) - || (test_bit (SCTP_CID_COOKIE_ACK, (void *)map))) { - return 0; - } - - newconntrack = SCTP_CONNTRACK_MAX; - for_each_sctp_chunk (skb, sch, _sch, offset, count) { - /* Don't need lock here: this conntrack not in circulation yet */ - newconntrack = new_state (IP_CT_DIR_ORIGINAL, - SCTP_CONNTRACK_NONE, sch->type); - - /* Invalid: delete conntrack */ - if (newconntrack == SCTP_CONNTRACK_MAX) { - DEBUGP("ip_conntrack_sctp: invalid new deleting.\n"); - return 0; - } - - /* Copy the vtag into the state info */ - if (sch->type == SCTP_CID_INIT) { - if (sh->vtag == 0) { - sctp_inithdr_t _inithdr, *ih; - - ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t), - sizeof(_inithdr), &_inithdr); - if (ih == NULL) - return 0; - - DEBUGP("Setting vtag %x for new conn\n", - ih->init_tag); - - conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = - ih->init_tag; - } else { - /* Sec 8.5.1 (A) */ - return 0; - } - } - /* If it is a shutdown ack OOTB packet, we expect a return - shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */ - else { - DEBUGP("Setting vtag %x for new conn OOTB\n", - sh->vtag); - conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag; - } - - conntrack->proto.sctp.state = newconntrack; - } - - return 1; -} - -static struct ip_conntrack_protocol ip_conntrack_protocol_sctp = { - .proto = IPPROTO_SCTP, - .name = "sctp", - .pkt_to_tuple = sctp_pkt_to_tuple, - .invert_tuple = sctp_invert_tuple, - .print_tuple = sctp_print_tuple, - .print_conntrack = sctp_print_conntrack, - .packet = sctp_packet, - .new = sctp_new, - .destroy = NULL, - .me = THIS_MODULE, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; - -#ifdef CONFIG_SYSCTL -static ctl_table ip_ct_sysctl_table[] = { - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED, - .procname = "ip_conntrack_sctp_timeout_closed", - .data = &ip_ct_sctp_timeout_closed, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT, - .procname = "ip_conntrack_sctp_timeout_cookie_wait", - .data = &ip_ct_sctp_timeout_cookie_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED, - .procname = "ip_conntrack_sctp_timeout_cookie_echoed", - .data = &ip_ct_sctp_timeout_cookie_echoed, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED, - .procname = "ip_conntrack_sctp_timeout_established", - .data = &ip_ct_sctp_timeout_established, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT, - .procname = "ip_conntrack_sctp_timeout_shutdown_sent", - .data = &ip_ct_sctp_timeout_shutdown_sent, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD, - .procname = "ip_conntrack_sctp_timeout_shutdown_recd", - .data = &ip_ct_sctp_timeout_shutdown_recd, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT, - .procname = "ip_conntrack_sctp_timeout_shutdown_ack_sent", - .data = &ip_ct_sctp_timeout_shutdown_ack_sent, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_netfilter_table[] = { - { - .ctl_name = NET_IPV4_NETFILTER, - .procname = "netfilter", - .mode = 0555, - .child = ip_ct_sysctl_table, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_ipv4_table[] = { - { - .ctl_name = NET_IPV4, - .procname = "ipv4", - .mode = 0555, - .child = ip_ct_netfilter_table, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_net_table[] = { - { - .ctl_name = CTL_NET, - .procname = "net", - .mode = 0555, - .child = ip_ct_ipv4_table, - }, - { .ctl_name = 0 } -}; - -static struct ctl_table_header *ip_ct_sysctl_header; -#endif - -static int __init ip_conntrack_proto_sctp_init(void) -{ - int ret; - - ret = ip_conntrack_protocol_register(&ip_conntrack_protocol_sctp); - if (ret) { - printk("ip_conntrack_proto_sctp: protocol register failed\n"); - goto out; - } - -#ifdef CONFIG_SYSCTL - ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table); - if (ip_ct_sysctl_header == NULL) { - ret = -ENOMEM; - printk("ip_conntrack_proto_sctp: can't register to sysctl.\n"); - goto cleanup; - } -#endif - - return ret; - -#ifdef CONFIG_SYSCTL - cleanup: - ip_conntrack_protocol_unregister(&ip_conntrack_protocol_sctp); -#endif - out: - DEBUGP("SCTP conntrack module loading %s\n", - ret ? "failed": "succeeded"); - return ret; -} - -static void __exit ip_conntrack_proto_sctp_fini(void) -{ - ip_conntrack_protocol_unregister(&ip_conntrack_protocol_sctp); -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(ip_ct_sysctl_header); -#endif - DEBUGP("SCTP conntrack module unloaded\n"); -} - -module_init(ip_conntrack_proto_sctp_init); -module_exit(ip_conntrack_proto_sctp_fini); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kiran Kumar Immidi"); -MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP"); diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c deleted file mode 100644 index 0a72eab..0000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Jozsef Kadlecsik : - * - Real stateful connection tracking - * - Modified state transitions table - * - Window scaling support added - * - SACK support added - * - * Willy Tarreau: - * - State table bugfixes - * - More robust state changes - * - Tuning timer parameters - * - * version 2.2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#if 0 -#define DEBUGP printk -#define DEBUGP_VARS -#else -#define DEBUGP(format, args...) -#endif - -/* Protects conntrack->proto.tcp */ -static DEFINE_RWLOCK(tcp_lock); - -/* "Be conservative in what you do, - be liberal in what you accept from others." - If it's non-zero, we mark only out of window RST segments as INVALID. */ -int ip_ct_tcp_be_liberal __read_mostly = 0; - -/* If it is set to zero, we disable picking up already established - connections. */ -int ip_ct_tcp_loose __read_mostly = 1; - -/* Max number of the retransmitted packets without receiving an (acceptable) - ACK from the destination. If this number is reached, a shorter timer - will be started. */ -int ip_ct_tcp_max_retrans __read_mostly = 3; - - /* FIXME: Examine ipfilter's timeouts and conntrack transitions more - closely. They're more complex. --RR */ - -static const char *tcp_conntrack_names[] = { - "NONE", - "SYN_SENT", - "SYN_RECV", - "ESTABLISHED", - "FIN_WAIT", - "CLOSE_WAIT", - "LAST_ACK", - "TIME_WAIT", - "CLOSE", - "LISTEN" -}; - -#define SECS * HZ -#define MINS * 60 SECS -#define HOURS * 60 MINS -#define DAYS * 24 HOURS - -unsigned int ip_ct_tcp_timeout_syn_sent __read_mostly = 2 MINS; -unsigned int ip_ct_tcp_timeout_syn_recv __read_mostly = 60 SECS; -unsigned int ip_ct_tcp_timeout_established __read_mostly = 5 DAYS; -unsigned int ip_ct_tcp_timeout_fin_wait __read_mostly = 2 MINS; -unsigned int ip_ct_tcp_timeout_close_wait __read_mostly = 60 SECS; -unsigned int ip_ct_tcp_timeout_last_ack __read_mostly = 30 SECS; -unsigned int ip_ct_tcp_timeout_time_wait __read_mostly = 2 MINS; -unsigned int ip_ct_tcp_timeout_close __read_mostly = 10 SECS; - -/* RFC1122 says the R2 limit should be at least 100 seconds. - Linux uses 15 packets as limit, which corresponds - to ~13-30min depending on RTO. */ -unsigned int ip_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS; - -static const unsigned int * tcp_timeouts[] -= { NULL, /* TCP_CONNTRACK_NONE */ - &ip_ct_tcp_timeout_syn_sent, /* TCP_CONNTRACK_SYN_SENT, */ - &ip_ct_tcp_timeout_syn_recv, /* TCP_CONNTRACK_SYN_RECV, */ - &ip_ct_tcp_timeout_established, /* TCP_CONNTRACK_ESTABLISHED, */ - &ip_ct_tcp_timeout_fin_wait, /* TCP_CONNTRACK_FIN_WAIT, */ - &ip_ct_tcp_timeout_close_wait, /* TCP_CONNTRACK_CLOSE_WAIT, */ - &ip_ct_tcp_timeout_last_ack, /* TCP_CONNTRACK_LAST_ACK, */ - &ip_ct_tcp_timeout_time_wait, /* TCP_CONNTRACK_TIME_WAIT, */ - &ip_ct_tcp_timeout_close, /* TCP_CONNTRACK_CLOSE, */ - NULL, /* TCP_CONNTRACK_LISTEN */ - }; - -#define sNO TCP_CONNTRACK_NONE -#define sSS TCP_CONNTRACK_SYN_SENT -#define sSR TCP_CONNTRACK_SYN_RECV -#define sES TCP_CONNTRACK_ESTABLISHED -#define sFW TCP_CONNTRACK_FIN_WAIT -#define sCW TCP_CONNTRACK_CLOSE_WAIT -#define sLA TCP_CONNTRACK_LAST_ACK -#define sTW TCP_CONNTRACK_TIME_WAIT -#define sCL TCP_CONNTRACK_CLOSE -#define sLI TCP_CONNTRACK_LISTEN -#define sIV TCP_CONNTRACK_MAX -#define sIG TCP_CONNTRACK_IGNORE - -/* What TCP flags are set from RST/SYN/FIN/ACK. */ -enum tcp_bit_set { - TCP_SYN_SET, - TCP_SYNACK_SET, - TCP_FIN_SET, - TCP_ACK_SET, - TCP_RST_SET, - TCP_NONE_SET, -}; - -/* - * The TCP state transition table needs a few words... - * - * We are the man in the middle. All the packets go through us - * but might get lost in transit to the destination. - * It is assumed that the destinations can't receive segments - * we haven't seen. - * - * The checked segment is in window, but our windows are *not* - * equivalent with the ones of the sender/receiver. We always - * try to guess the state of the current sender. - * - * The meaning of the states are: - * - * NONE: initial state - * SYN_SENT: SYN-only packet seen - * SYN_RECV: SYN-ACK packet seen - * ESTABLISHED: ACK packet seen - * FIN_WAIT: FIN packet seen - * CLOSE_WAIT: ACK seen (after FIN) - * LAST_ACK: FIN seen (after FIN) - * TIME_WAIT: last ACK seen - * CLOSE: closed connection - * - * LISTEN state is not used. - * - * Packets marked as IGNORED (sIG): - * if they may be either invalid or valid - * and the receiver may send back a connection - * closing RST or a SYN/ACK. - * - * Packets marked as INVALID (sIV): - * if they are invalid - * or we do not support the request (simultaneous open) - */ -static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { - { -/* ORIGINAL */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*syn*/ { sSS, sSS, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sIV }, -/* - * sNO -> sSS Initialize a new connection - * sSS -> sSS Retransmitted SYN - * sSR -> sIG Late retransmitted SYN? - * sES -> sIG Error: SYNs in window outside the SYN_SENT state - * are errors. Receiver will reply with RST - * and close the connection. - * Or we are not in sync and hold a dead connection. - * sFW -> sIG - * sCW -> sIG - * sLA -> sIG - * sTW -> sSS Reopened connection (RFC 1122). - * sCL -> sSS - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }, -/* - * A SYN/ACK from the client is always invalid: - * - either it tries to set up a simultaneous open, which is - * not supported; - * - or the firewall has just been inserted between the two hosts - * during the session set-up. The SYN will be retransmitted - * by the true client (or it'll time out). - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, -/* - * sNO -> sIV Too late and no reason to do anything... - * sSS -> sIV Client migth not send FIN in this state: - * we enforce waiting for a SYN/ACK reply first. - * sSR -> sFW Close started. - * sES -> sFW - * sFW -> sLA FIN seen in both directions, waiting for - * the last ACK. - * Migth be a retransmitted FIN as well... - * sCW -> sLA - * sLA -> sLA Retransmitted FIN. Remain in the same state. - * sTW -> sTW - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*ack*/ { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV }, -/* - * sNO -> sES Assumed. - * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. - * sSR -> sES Established state is reached. - * sES -> sES :-) - * sFW -> sCW Normal close request answered by ACK. - * sCW -> sCW - * sLA -> sTW Last ACK detected. - * sTW -> sTW Retransmitted last ACK. Remain in the same state. - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*rst*/ { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, -/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } - }, - { -/* REPLY */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*syn*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }, -/* - * sNO -> sIV Never reached. - * sSS -> sIV Simultaneous open, not supported - * sSR -> sIV Simultaneous open, not supported. - * sES -> sIV Server may not initiate a connection. - * sFW -> sIV - * sCW -> sIV - * sLA -> sIV - * sTW -> sIV Reopened connection, but server may not do it. - * sCL -> sIV - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIV }, -/* - * sSS -> sSR Standard open. - * sSR -> sSR Retransmitted SYN/ACK. - * sES -> sIG Late retransmitted SYN/ACK? - * sFW -> sIG Might be SYN/ACK answering ignored SYN - * sCW -> sIG - * sLA -> sIG - * sTW -> sIG - * sCL -> sIG - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, -/* - * sSS -> sIV Server might not send FIN in this state. - * sSR -> sFW Close started. - * sES -> sFW - * sFW -> sLA FIN seen in both directions. - * sCW -> sLA - * sLA -> sLA Retransmitted FIN. - * sTW -> sTW - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*ack*/ { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV }, -/* - * sSS -> sIG Might be a half-open connection. - * sSR -> sSR Might answer late resent SYN. - * sES -> sES :-) - * sFW -> sCW Normal close request answered by ACK. - * sCW -> sCW - * sLA -> sTW Last ACK detected. - * sTW -> sTW Retransmitted last ACK. - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*rst*/ { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, -/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } - } -}; - -static int tcp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct tcphdr _hdr, *hp; - - /* Actually only need first 8 bytes. */ - hp = skb_header_pointer(skb, dataoff, 8, &_hdr); - if (hp == NULL) - return 0; - - tuple->src.u.tcp.port = hp->source; - tuple->dst.u.tcp.port = hp->dest; - - return 1; -} - -static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->src.u.tcp.port = orig->dst.u.tcp.port; - tuple->dst.u.tcp.port = orig->src.u.tcp.port; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int tcp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.tcp.port), - ntohs(tuple->dst.u.tcp.port)); -} - -/* Print out the private part of the conntrack. */ -static int tcp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - enum tcp_conntrack state; - - read_lock_bh(&tcp_lock); - state = conntrack->proto.tcp.state; - read_unlock_bh(&tcp_lock); - - return seq_printf(s, "%s ", tcp_conntrack_names[state]); -} - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa, - const struct ip_conntrack *ct) -{ - struct nfattr *nest_parms; - - read_lock_bh(&tcp_lock); - nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP); - NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t), - &ct->proto.tcp.state); - read_unlock_bh(&tcp_lock); - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - read_unlock_bh(&tcp_lock); - return -1; -} - -static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = { - [CTA_PROTOINFO_TCP_STATE-1] = sizeof(u_int8_t), -}; - -static int nfattr_to_tcp(struct nfattr *cda[], struct ip_conntrack *ct) -{ - struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1]; - struct nfattr *tb[CTA_PROTOINFO_TCP_MAX]; - - /* updates could not contain anything about the private - * protocol info, in that case skip the parsing */ - if (!attr) - return 0; - - nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr); - - if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp)) - return -EINVAL; - - if (!tb[CTA_PROTOINFO_TCP_STATE-1]) - return -EINVAL; - - write_lock_bh(&tcp_lock); - ct->proto.tcp.state = - *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]); - write_unlock_bh(&tcp_lock); - - return 0; -} -#endif - -static unsigned int get_conntrack_index(const struct tcphdr *tcph) -{ - if (tcph->rst) return TCP_RST_SET; - else if (tcph->syn) return (tcph->ack ? TCP_SYNACK_SET : TCP_SYN_SET); - else if (tcph->fin) return TCP_FIN_SET; - else if (tcph->ack) return TCP_ACK_SET; - else return TCP_NONE_SET; -} - -/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering - in IP Filter' by Guido van Rooij. - - http://www.nluug.nl/events/sane2000/papers.html - http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz - - The boundaries and the conditions are changed according to RFC793: - the packet must intersect the window (i.e. segments may be - after the right or before the left edge) and thus receivers may ACK - segments after the right edge of the window. - - td_maxend = max(sack + max(win,1)) seen in reply packets - td_maxwin = max(max(win, 1)) + (sack - ack) seen in sent packets - td_maxwin += seq + len - sender.td_maxend - if seq + len > sender.td_maxend - td_end = max(seq + len) seen in sent packets - - I. Upper bound for valid data: seq <= sender.td_maxend - II. Lower bound for valid data: seq + len >= sender.td_end - receiver.td_maxwin - III. Upper bound for valid ack: sack <= receiver.td_end - IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW - - where sack is the highest right edge of sack block found in the packet. - - The upper bound limit for a valid ack is not ignored - - we doesn't have to deal with fragments. -*/ - -static inline __u32 segment_seq_plus_len(__u32 seq, - size_t len, - struct iphdr *iph, - struct tcphdr *tcph) -{ - return (seq + len - (iph->ihl + tcph->doff)*4 - + (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0)); -} - -/* Fixme: what about big packets? */ -#define MAXACKWINCONST 66000 -#define MAXACKWINDOW(sender) \ - ((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin \ - : MAXACKWINCONST) - -/* - * Simplified tcp_parse_options routine from tcp_input.c - */ -static void tcp_options(const struct sk_buff *skb, - struct iphdr *iph, - struct tcphdr *tcph, - struct ip_ct_tcp_state *state) -{ - unsigned char buff[(15 * 4) - sizeof(struct tcphdr)]; - unsigned char *ptr; - int length = (tcph->doff*4) - sizeof(struct tcphdr); - - if (!length) - return; - - ptr = skb_header_pointer(skb, - (iph->ihl * 4) + sizeof(struct tcphdr), - length, buff); - BUG_ON(ptr == NULL); - - state->td_scale = - state->flags = 0; - - while (length > 0) { - int opcode=*ptr++; - int opsize; - - switch (opcode) { - case TCPOPT_EOL: - return; - case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ - length--; - continue; - default: - opsize=*ptr++; - if (opsize < 2) /* "silly options" */ - return; - if (opsize > length) - break; /* don't parse partial options */ - - if (opcode == TCPOPT_SACK_PERM - && opsize == TCPOLEN_SACK_PERM) - state->flags |= IP_CT_TCP_FLAG_SACK_PERM; - else if (opcode == TCPOPT_WINDOW - && opsize == TCPOLEN_WINDOW) { - state->td_scale = *(u_int8_t *)ptr; - - if (state->td_scale > 14) { - /* See RFC1323 */ - state->td_scale = 14; - } - state->flags |= - IP_CT_TCP_FLAG_WINDOW_SCALE; - } - ptr += opsize - 2; - length -= opsize; - } - } -} - -static void tcp_sack(const struct sk_buff *skb, - struct iphdr *iph, - struct tcphdr *tcph, - __u32 *sack) -{ - unsigned char buff[(15 * 4) - sizeof(struct tcphdr)]; - unsigned char *ptr; - int length = (tcph->doff*4) - sizeof(struct tcphdr); - __u32 tmp; - - if (!length) - return; - - ptr = skb_header_pointer(skb, - (iph->ihl * 4) + sizeof(struct tcphdr), - length, buff); - BUG_ON(ptr == NULL); - - /* Fast path for timestamp-only option */ - if (length == TCPOLEN_TSTAMP_ALIGNED*4 - && *(__be32 *)ptr == - __constant_htonl((TCPOPT_NOP << 24) - | (TCPOPT_NOP << 16) - | (TCPOPT_TIMESTAMP << 8) - | TCPOLEN_TIMESTAMP)) - return; - - while (length > 0) { - int opcode=*ptr++; - int opsize, i; - - switch (opcode) { - case TCPOPT_EOL: - return; - case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ - length--; - continue; - default: - opsize=*ptr++; - if (opsize < 2) /* "silly options" */ - return; - if (opsize > length) - break; /* don't parse partial options */ - - if (opcode == TCPOPT_SACK - && opsize >= (TCPOLEN_SACK_BASE - + TCPOLEN_SACK_PERBLOCK) - && !((opsize - TCPOLEN_SACK_BASE) - % TCPOLEN_SACK_PERBLOCK)) { - for (i = 0; - i < (opsize - TCPOLEN_SACK_BASE); - i += TCPOLEN_SACK_PERBLOCK) { - tmp = ntohl(*((__be32 *)(ptr+i)+1)); - - if (after(tmp, *sack)) - *sack = tmp; - } - return; - } - ptr += opsize - 2; - length -= opsize; - } - } -} - -static int tcp_in_window(struct ip_ct_tcp *state, - enum ip_conntrack_dir dir, - unsigned int index, - const struct sk_buff *skb, - struct iphdr *iph, - struct tcphdr *tcph) -{ - struct ip_ct_tcp_state *sender = &state->seen[dir]; - struct ip_ct_tcp_state *receiver = &state->seen[!dir]; - __u32 seq, ack, sack, end, win, swin; - int res; - - /* - * Get the required data from the packet. - */ - seq = ntohl(tcph->seq); - ack = sack = ntohl(tcph->ack_seq); - win = ntohs(tcph->window); - end = segment_seq_plus_len(seq, skb->len, iph, tcph); - - if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM) - tcp_sack(skb, iph, tcph, &sack); - - DEBUGP("tcp_in_window: START\n"); - DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " - "seq=%u ack=%u sack=%u win=%u end=%u\n", - NIPQUAD(iph->saddr), ntohs(tcph->source), - NIPQUAD(iph->daddr), ntohs(tcph->dest), - seq, ack, sack, win, end); - DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); - - if (sender->td_end == 0) { - /* - * Initialize sender data. - */ - if (tcph->syn && tcph->ack) { - /* - * Outgoing SYN-ACK in reply to a SYN. - */ - sender->td_end = - sender->td_maxend = end; - sender->td_maxwin = (win == 0 ? 1 : win); - - tcp_options(skb, iph, tcph, sender); - /* - * RFC 1323: - * Both sides must send the Window Scale option - * to enable window scaling in either direction. - */ - if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE - && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE)) - sender->td_scale = - receiver->td_scale = 0; - } else { - /* - * We are in the middle of a connection, - * its history is lost for us. - * Let's try to use the data from the packet. - */ - sender->td_end = end; - sender->td_maxwin = (win == 0 ? 1 : win); - sender->td_maxend = end + sender->td_maxwin; - } - } else if (((state->state == TCP_CONNTRACK_SYN_SENT - && dir == IP_CT_DIR_ORIGINAL) - || (state->state == TCP_CONNTRACK_SYN_RECV - && dir == IP_CT_DIR_REPLY)) - && after(end, sender->td_end)) { - /* - * RFC 793: "if a TCP is reinitialized ... then it need - * not wait at all; it must only be sure to use sequence - * numbers larger than those recently used." - */ - sender->td_end = - sender->td_maxend = end; - sender->td_maxwin = (win == 0 ? 1 : win); - - tcp_options(skb, iph, tcph, sender); - } - - if (!(tcph->ack)) { - /* - * If there is no ACK, just pretend it was set and OK. - */ - ack = sack = receiver->td_end; - } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) == - (TCP_FLAG_ACK|TCP_FLAG_RST)) - && (ack == 0)) { - /* - * Broken TCP stacks, that set ACK in RST packets as well - * with zero ack value. - */ - ack = sack = receiver->td_end; - } - - if (seq == end - && (!tcph->rst - || (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT))) - /* - * Packets contains no data: we assume it is valid - * and check the ack value only. - * However RST segments are always validated by their - * SEQ number, except when seq == 0 (reset sent answering - * SYN. - */ - seq = end = sender->td_end; - - DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " - "seq=%u ack=%u sack =%u win=%u end=%u\n", - NIPQUAD(iph->saddr), ntohs(tcph->source), - NIPQUAD(iph->daddr), ntohs(tcph->dest), - seq, ack, sack, win, end); - DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); - - DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n", - before(seq, sender->td_maxend + 1), - after(end, sender->td_end - receiver->td_maxwin - 1), - before(sack, receiver->td_end + 1), - after(ack, receiver->td_end - MAXACKWINDOW(sender))); - - if (before(seq, sender->td_maxend + 1) && - after(end, sender->td_end - receiver->td_maxwin - 1) && - before(sack, receiver->td_end + 1) && - after(ack, receiver->td_end - MAXACKWINDOW(sender))) { - /* - * Take into account window scaling (RFC 1323). - */ - if (!tcph->syn) - win <<= sender->td_scale; - - /* - * Update sender data. - */ - swin = win + (sack - ack); - if (sender->td_maxwin < swin) - sender->td_maxwin = swin; - if (after(end, sender->td_end)) - sender->td_end = end; - /* - * Update receiver data. - */ - if (after(end, sender->td_maxend)) - receiver->td_maxwin += end - sender->td_maxend; - if (after(sack + win, receiver->td_maxend - 1)) { - receiver->td_maxend = sack + win; - if (win == 0) - receiver->td_maxend++; - } - - /* - * Check retransmissions. - */ - if (index == TCP_ACK_SET) { - if (state->last_dir == dir - && state->last_seq == seq - && state->last_ack == ack - && state->last_end == end - && state->last_win == win) - state->retrans++; - else { - state->last_dir = dir; - state->last_seq = seq; - state->last_ack = ack; - state->last_end = end; - state->last_win = win; - state->retrans = 0; - } - } - res = 1; - } else { - res = 0; - if (sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || - ip_ct_tcp_be_liberal) - res = 1; - if (!res && LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: %s ", - before(seq, sender->td_maxend + 1) ? - after(end, sender->td_end - receiver->td_maxwin - 1) ? - before(sack, receiver->td_end + 1) ? - after(ack, receiver->td_end - MAXACKWINDOW(sender)) ? "BUG" - : "ACK is under the lower bound (possible overly delayed ACK)" - : "ACK is over the upper bound (ACKed data not seen yet)" - : "SEQ is under the lower bound (already ACKed data retransmitted)" - : "SEQ is over the upper bound (over the window of the receiver)"); - } - - DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u " - "receiver end=%u maxend=%u maxwin=%u\n", - res, sender->td_end, sender->td_maxend, sender->td_maxwin, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin); - - return res; -} - -#ifdef CONFIG_IP_NF_NAT_NEEDED -/* Update sender->td_end after NAT successfully mangled the packet */ -void ip_conntrack_tcp_update(struct sk_buff *skb, - struct ip_conntrack *conntrack, - enum ip_conntrack_dir dir) -{ - struct iphdr *iph = skb->nh.iph; - struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4; - __u32 end; -#ifdef DEBUGP_VARS - struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir]; - struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir]; -#endif - - end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, iph, tcph); - - write_lock_bh(&tcp_lock); - /* - * We have to worry for the ack in the reply packet only... - */ - if (after(end, conntrack->proto.tcp.seen[dir].td_end)) - conntrack->proto.tcp.seen[dir].td_end = end; - conntrack->proto.tcp.last_end = end; - write_unlock_bh(&tcp_lock); - DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); -} - -#endif - -#define TH_FIN 0x01 -#define TH_SYN 0x02 -#define TH_RST 0x04 -#define TH_PUSH 0x08 -#define TH_ACK 0x10 -#define TH_URG 0x20 -#define TH_ECE 0x40 -#define TH_CWR 0x80 - -/* table of valid flag combinations - ECE and CWR are always valid */ -static const u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] = -{ - [TH_SYN] = 1, - [TH_SYN|TH_PUSH] = 1, - [TH_SYN|TH_URG] = 1, - [TH_SYN|TH_PUSH|TH_URG] = 1, - [TH_SYN|TH_ACK] = 1, - [TH_SYN|TH_ACK|TH_PUSH] = 1, - [TH_RST] = 1, - [TH_RST|TH_ACK] = 1, - [TH_RST|TH_ACK|TH_PUSH] = 1, - [TH_FIN|TH_ACK] = 1, - [TH_ACK] = 1, - [TH_ACK|TH_PUSH] = 1, - [TH_ACK|TH_URG] = 1, - [TH_ACK|TH_URG|TH_PUSH] = 1, - [TH_FIN|TH_ACK|TH_PUSH] = 1, - [TH_FIN|TH_ACK|TH_URG] = 1, - [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1, -}; - -/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ -static int tcp_error(struct sk_buff *skb, - enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - struct iphdr *iph = skb->nh.iph; - struct tcphdr _tcph, *th; - unsigned int tcplen = skb->len - iph->ihl * 4; - u_int8_t tcpflags; - - /* Smaller that minimal TCP header? */ - th = skb_header_pointer(skb, iph->ihl * 4, - sizeof(_tcph), &_tcph); - if (th == NULL) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: short packet "); - return -NF_ACCEPT; - } - - /* Not whole TCP header or malformed packet */ - if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: truncated/malformed packet "); - return -NF_ACCEPT; - } - - /* Checksum invalid? Ignore. - * We skip checking packets on the outgoing path - * because it is assumed to be correct. - */ - /* FIXME: Source route IP option packets --RR */ - if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && - nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_TCP)) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: bad TCP checksum "); - return -NF_ACCEPT; - } - - /* Check TCP flags. */ - tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR)); - if (!tcp_valid_flags[tcpflags]) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: invalid TCP flag combination "); - return -NF_ACCEPT; - } - - return NF_ACCEPT; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int tcp_packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - enum tcp_conntrack new_state, old_state; - enum ip_conntrack_dir dir; - struct iphdr *iph = skb->nh.iph; - struct tcphdr *th, _tcph; - unsigned long timeout; - unsigned int index; - - th = skb_header_pointer(skb, iph->ihl * 4, - sizeof(_tcph), &_tcph); - BUG_ON(th == NULL); - - write_lock_bh(&tcp_lock); - old_state = conntrack->proto.tcp.state; - dir = CTINFO2DIR(ctinfo); - index = get_conntrack_index(th); - new_state = tcp_conntracks[dir][index][old_state]; - - switch (new_state) { - case TCP_CONNTRACK_IGNORE: - /* Ignored packets: - * - * a) SYN in ORIGINAL - * b) SYN/ACK in REPLY - * c) ACK in reply direction after initial SYN in original. - */ - if (index == TCP_SYNACK_SET - && conntrack->proto.tcp.last_index == TCP_SYN_SET - && conntrack->proto.tcp.last_dir != dir - && ntohl(th->ack_seq) == - conntrack->proto.tcp.last_end) { - /* This SYN/ACK acknowledges a SYN that we earlier - * ignored as invalid. This means that the client and - * the server are both in sync, while the firewall is - * not. We kill this session and block the SYN/ACK so - * that the client cannot but retransmit its SYN and - * thus initiate a clean new session. - */ - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, - NULL, "ip_ct_tcp: " - "killing out of sync session "); - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long) - conntrack); - return -NF_DROP; - } - conntrack->proto.tcp.last_index = index; - conntrack->proto.tcp.last_dir = dir; - conntrack->proto.tcp.last_seq = ntohl(th->seq); - conntrack->proto.tcp.last_end = - segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th); - - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: invalid packet ignored "); - return NF_ACCEPT; - case TCP_CONNTRACK_MAX: - /* Invalid packet */ - DEBUGP("ip_ct_tcp: Invalid dir=%i index=%u ostate=%u\n", - dir, get_conntrack_index(th), - old_state); - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: invalid state "); - return -NF_ACCEPT; - case TCP_CONNTRACK_SYN_SENT: - if (old_state < TCP_CONNTRACK_TIME_WAIT) - break; - if ((conntrack->proto.tcp.seen[dir].flags & - IP_CT_TCP_FLAG_CLOSE_INIT) - || after(ntohl(th->seq), - conntrack->proto.tcp.seen[dir].td_end)) { - /* Attempt to reopen a closed connection. - * Delete this connection and look up again. */ - write_unlock_bh(&tcp_lock); - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long) - conntrack); - return -NF_REPEAT; - } else { - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, - NULL, "ip_ct_tcp: invalid SYN"); - return -NF_ACCEPT; - } - case TCP_CONNTRACK_CLOSE: - if (index == TCP_RST_SET - && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) - && conntrack->proto.tcp.last_index == TCP_SYN_SET) - || (!test_bit(IPS_ASSURED_BIT, &conntrack->status) - && conntrack->proto.tcp.last_index == TCP_ACK_SET)) - && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) { - /* RST sent to invalid SYN or ACK we had let through - * at a) and c) above: - * - * a) SYN was in window then - * c) we hold a half-open connection. - * - * Delete our connection entry. - * We skip window checking, because packet might ACK - * segments we ignored. */ - goto in_window; - } - /* Just fall through */ - default: - /* Keep compilers happy. */ - break; - } - - if (!tcp_in_window(&conntrack->proto.tcp, dir, index, - skb, iph, th)) { - write_unlock_bh(&tcp_lock); - return -NF_ACCEPT; - } - in_window: - /* From now on we have got in-window packets */ - conntrack->proto.tcp.last_index = index; - - DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " - "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n", - NIPQUAD(iph->saddr), ntohs(th->source), - NIPQUAD(iph->daddr), ntohs(th->dest), - (th->syn ? 1 : 0), (th->ack ? 1 : 0), - (th->fin ? 1 : 0), (th->rst ? 1 : 0), - old_state, new_state); - - conntrack->proto.tcp.state = new_state; - if (old_state != new_state - && (new_state == TCP_CONNTRACK_FIN_WAIT - || new_state == TCP_CONNTRACK_CLOSE)) - conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; - timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans - && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans - ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state]; - write_unlock_bh(&tcp_lock); - - ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); - if (new_state != old_state) - ip_conntrack_event_cache(IPCT_PROTOINFO, skb); - - if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { - /* If only reply is a RST, we can consider ourselves not to - have an established connection: this is a fairly common - problem case, so we can delete the conntrack - immediately. --RR */ - if (th->rst) { - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long) - conntrack); - return NF_ACCEPT; - } - } else if (!test_bit(IPS_ASSURED_BIT, &conntrack->status) - && (old_state == TCP_CONNTRACK_SYN_RECV - || old_state == TCP_CONNTRACK_ESTABLISHED) - && new_state == TCP_CONNTRACK_ESTABLISHED) { - /* Set ASSURED if we see see valid ack in ESTABLISHED - after SYN_RECV or a valid answer for a picked up - connection. */ - set_bit(IPS_ASSURED_BIT, &conntrack->status); - ip_conntrack_event_cache(IPCT_STATUS, skb); - } - ip_ct_refresh_acct(conntrack, ctinfo, skb, timeout); - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int tcp_new(struct ip_conntrack *conntrack, - const struct sk_buff *skb) -{ - enum tcp_conntrack new_state; - struct iphdr *iph = skb->nh.iph; - struct tcphdr *th, _tcph; -#ifdef DEBUGP_VARS - struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[0]; - struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[1]; -#endif - - th = skb_header_pointer(skb, iph->ihl * 4, - sizeof(_tcph), &_tcph); - BUG_ON(th == NULL); - - /* Don't need lock here: this conntrack not in circulation yet */ - new_state - = tcp_conntracks[0][get_conntrack_index(th)] - [TCP_CONNTRACK_NONE]; - - /* Invalid: delete conntrack */ - if (new_state >= TCP_CONNTRACK_MAX) { - DEBUGP("ip_ct_tcp: invalid new deleting.\n"); - return 0; - } - - if (new_state == TCP_CONNTRACK_SYN_SENT) { - /* SYN packet */ - conntrack->proto.tcp.seen[0].td_end = - segment_seq_plus_len(ntohl(th->seq), skb->len, - iph, th); - conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window); - if (conntrack->proto.tcp.seen[0].td_maxwin == 0) - conntrack->proto.tcp.seen[0].td_maxwin = 1; - conntrack->proto.tcp.seen[0].td_maxend = - conntrack->proto.tcp.seen[0].td_end; - - tcp_options(skb, iph, th, &conntrack->proto.tcp.seen[0]); - conntrack->proto.tcp.seen[1].flags = 0; - } else if (ip_ct_tcp_loose == 0) { - /* Don't try to pick up connections. */ - return 0; - } else { - /* - * We are in the middle of a connection, - * its history is lost for us. - * Let's try to use the data from the packet. - */ - conntrack->proto.tcp.seen[0].td_end = - segment_seq_plus_len(ntohl(th->seq), skb->len, - iph, th); - conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window); - if (conntrack->proto.tcp.seen[0].td_maxwin == 0) - conntrack->proto.tcp.seen[0].td_maxwin = 1; - conntrack->proto.tcp.seen[0].td_maxend = - conntrack->proto.tcp.seen[0].td_end + - conntrack->proto.tcp.seen[0].td_maxwin; - conntrack->proto.tcp.seen[0].td_scale = 0; - - /* We assume SACK and liberal window checking to handle - * window scaling */ - conntrack->proto.tcp.seen[0].flags = - conntrack->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM | - IP_CT_TCP_FLAG_BE_LIBERAL; - } - - conntrack->proto.tcp.seen[1].td_end = 0; - conntrack->proto.tcp.seen[1].td_maxend = 0; - conntrack->proto.tcp.seen[1].td_maxwin = 1; - conntrack->proto.tcp.seen[1].td_scale = 0; - - /* tcp_packet will set them */ - conntrack->proto.tcp.state = TCP_CONNTRACK_NONE; - conntrack->proto.tcp.last_index = TCP_NONE_SET; - - DEBUGP("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); - return 1; -} - -struct ip_conntrack_protocol ip_conntrack_protocol_tcp = -{ - .proto = IPPROTO_TCP, - .name = "tcp", - .pkt_to_tuple = tcp_pkt_to_tuple, - .invert_tuple = tcp_invert_tuple, - .print_tuple = tcp_print_tuple, - .print_conntrack = tcp_print_conntrack, - .packet = tcp_packet, - .new = tcp_new, - .error = tcp_error, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .to_nfattr = tcp_to_nfattr, - .from_nfattr = nfattr_to_tcp, - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; diff --git a/net/ipv4/netfilter/ip_conntrack_proto_udp.c b/net/ipv4/netfilter/ip_conntrack_proto_udp.c deleted file mode 100644 index 14c30c6..0000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c +++ /dev/null @@ -1,148 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -unsigned int ip_ct_udp_timeout __read_mostly = 30*HZ; -unsigned int ip_ct_udp_timeout_stream __read_mostly = 180*HZ; - -static int udp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct udphdr _hdr, *hp; - - /* Actually only need first 8 bytes. */ - hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); - if (hp == NULL) - return 0; - - tuple->src.u.udp.port = hp->source; - tuple->dst.u.udp.port = hp->dest; - - return 1; -} - -static int udp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->src.u.udp.port = orig->dst.u.udp.port; - tuple->dst.u.udp.port = orig->src.u.udp.port; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int udp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.udp.port), - ntohs(tuple->dst.u.udp.port)); -} - -/* Print out the private part of the conntrack. */ -static int udp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - return 0; -} - -/* Returns verdict for packet, and may modify conntracktype */ -static int udp_packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - /* If we've seen traffic both ways, this is some kind of UDP - stream. Extend timeout. */ - if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { - ip_ct_refresh_acct(conntrack, ctinfo, skb, - ip_ct_udp_timeout_stream); - /* Also, more likely to be important, and not a probe */ - if (!test_and_set_bit(IPS_ASSURED_BIT, &conntrack->status)) - ip_conntrack_event_cache(IPCT_STATUS, skb); - } else - ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_udp_timeout); - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int udp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb) -{ - return 1; -} - -static int udp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - struct iphdr *iph = skb->nh.iph; - unsigned int udplen = skb->len - iph->ihl * 4; - struct udphdr _hdr, *hdr; - - /* Header is too small? */ - hdr = skb_header_pointer(skb, iph->ihl*4, sizeof(_hdr), &_hdr); - if (hdr == NULL) { - if (LOG_INVALID(IPPROTO_UDP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_udp: short packet "); - return -NF_ACCEPT; - } - - /* Truncated/malformed packets */ - if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) { - if (LOG_INVALID(IPPROTO_UDP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_udp: truncated/malformed packet "); - return -NF_ACCEPT; - } - - /* Packet with no checksum */ - if (!hdr->check) - return NF_ACCEPT; - - /* Checksum invalid? Ignore. - * We skip checking packets on the outgoing path - * because the checksum is assumed to be correct. - * FIXME: Source route IP option packets --RR */ - if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && - nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_UDP)) { - if (LOG_INVALID(IPPROTO_UDP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_udp: bad UDP checksum "); - return -NF_ACCEPT; - } - - return NF_ACCEPT; -} - -struct ip_conntrack_protocol ip_conntrack_protocol_udp = -{ - .proto = IPPROTO_UDP, - .name = "udp", - .pkt_to_tuple = udp_pkt_to_tuple, - .invert_tuple = udp_invert_tuple, - .print_tuple = udp_print_tuple, - .print_conntrack = udp_print_conntrack, - .packet = udp_packet, - .new = udp_new, - .error = udp_error, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; diff --git a/net/ipv4/netfilter/ip_conntrack_sip.c b/net/ipv4/netfilter/ip_conntrack_sip.c deleted file mode 100644 index c59a962..0000000 --- a/net/ipv4/netfilter/ip_conntrack_sip.c +++ /dev/null @@ -1,520 +0,0 @@ -/* SIP extension for IP connection tracking. - * - * (C) 2005 by Christian Hentschel - * based on RR's ip_conntrack_ftp.c and other modules. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Hentschel "); -MODULE_DESCRIPTION("SIP connection tracking helper"); - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of sip servers"); - -static unsigned int sip_timeout = SIP_TIMEOUT; -module_param(sip_timeout, uint, 0600); -MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); - -unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr); -EXPORT_SYMBOL_GPL(ip_nat_sip_hook); - -unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp, - const char *dptr); -EXPORT_SYMBOL_GPL(ip_nat_sdp_hook); - -static int digits_len(const char *dptr, const char *limit, int *shift); -static int epaddr_len(const char *dptr, const char *limit, int *shift); -static int skp_digits_len(const char *dptr, const char *limit, int *shift); -static int skp_epaddr_len(const char *dptr, const char *limit, int *shift); - -struct sip_header_nfo { - const char *lname; - const char *sname; - const char *ln_str; - size_t lnlen; - size_t snlen; - size_t ln_strlen; - int case_sensitive; - int (*match_len)(const char *, const char *, int *); -}; - -static struct sip_header_nfo ct_sip_hdrs[] = { - [POS_REG_REQ_URI] = { /* SIP REGISTER request URI */ - .lname = "sip:", - .lnlen = sizeof("sip:") - 1, - .ln_str = ":", - .ln_strlen = sizeof(":") - 1, - .match_len = epaddr_len - }, - [POS_REQ_URI] = { /* SIP request URI */ - .lname = "sip:", - .lnlen = sizeof("sip:") - 1, - .ln_str = "@", - .ln_strlen = sizeof("@") - 1, - .match_len = epaddr_len - }, - [POS_FROM] = { /* SIP From header */ - .lname = "From:", - .lnlen = sizeof("From:") - 1, - .sname = "\r\nf:", - .snlen = sizeof("\r\nf:") - 1, - .ln_str = "sip:", - .ln_strlen = sizeof("sip:") - 1, - .match_len = skp_epaddr_len, - }, - [POS_TO] = { /* SIP To header */ - .lname = "To:", - .lnlen = sizeof("To:") - 1, - .sname = "\r\nt:", - .snlen = sizeof("\r\nt:") - 1, - .ln_str = "sip:", - .ln_strlen = sizeof("sip:") - 1, - .match_len = skp_epaddr_len, - }, - [POS_VIA] = { /* SIP Via header */ - .lname = "Via:", - .lnlen = sizeof("Via:") - 1, - .sname = "\r\nv:", - .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */ - .ln_str = "UDP ", - .ln_strlen = sizeof("UDP ") - 1, - .match_len = epaddr_len, - }, - [POS_CONTACT] = { /* SIP Contact header */ - .lname = "Contact:", - .lnlen = sizeof("Contact:") - 1, - .sname = "\r\nm:", - .snlen = sizeof("\r\nm:") - 1, - .ln_str = "sip:", - .ln_strlen = sizeof("sip:") - 1, - .match_len = skp_epaddr_len - }, - [POS_CONTENT] = { /* SIP Content length header */ - .lname = "Content-Length:", - .lnlen = sizeof("Content-Length:") - 1, - .sname = "\r\nl:", - .snlen = sizeof("\r\nl:") - 1, - .ln_str = ":", - .ln_strlen = sizeof(":") - 1, - .match_len = skp_digits_len - }, - [POS_MEDIA] = { /* SDP media info */ - .case_sensitive = 1, - .lname = "\nm=", - .lnlen = sizeof("\nm=") - 1, - .sname = "\rm=", - .snlen = sizeof("\rm=") - 1, - .ln_str = "audio ", - .ln_strlen = sizeof("audio ") - 1, - .match_len = digits_len - }, - [POS_OWNER] = { /* SDP owner address*/ - .case_sensitive = 1, - .lname = "\no=", - .lnlen = sizeof("\no=") - 1, - .sname = "\ro=", - .snlen = sizeof("\ro=") - 1, - .ln_str = "IN IP4 ", - .ln_strlen = sizeof("IN IP4 ") - 1, - .match_len = epaddr_len - }, - [POS_CONNECTION] = { /* SDP connection info */ - .case_sensitive = 1, - .lname = "\nc=", - .lnlen = sizeof("\nc=") - 1, - .sname = "\rc=", - .snlen = sizeof("\rc=") - 1, - .ln_str = "IN IP4 ", - .ln_strlen = sizeof("IN IP4 ") - 1, - .match_len = epaddr_len - }, - [POS_SDP_HEADER] = { /* SDP version header */ - .case_sensitive = 1, - .lname = "\nv=", - .lnlen = sizeof("\nv=") - 1, - .sname = "\rv=", - .snlen = sizeof("\rv=") - 1, - .ln_str = "=", - .ln_strlen = sizeof("=") - 1, - .match_len = digits_len - } -}; - -/* get line lenght until first CR or LF seen. */ -int ct_sip_lnlen(const char *line, const char *limit) -{ - const char *k = line; - - while ((line <= limit) && (*line == '\r' || *line == '\n')) - line++; - - while (line <= limit) { - if (*line == '\r' || *line == '\n') - break; - line++; - } - return line - k; -} -EXPORT_SYMBOL_GPL(ct_sip_lnlen); - -/* Linear string search, case sensitive. */ -const char *ct_sip_search(const char *needle, const char *haystack, - size_t needle_len, size_t haystack_len, - int case_sensitive) -{ - const char *limit = haystack + (haystack_len - needle_len); - - while (haystack <= limit) { - if (case_sensitive) { - if (strncmp(haystack, needle, needle_len) == 0) - return haystack; - } else { - if (strnicmp(haystack, needle, needle_len) == 0) - return haystack; - } - haystack++; - } - return NULL; -} -EXPORT_SYMBOL_GPL(ct_sip_search); - -static int digits_len(const char *dptr, const char *limit, int *shift) -{ - int len = 0; - while (dptr <= limit && isdigit(*dptr)) { - dptr++; - len++; - } - return len; -} - -/* get digits lenght, skiping blank spaces. */ -static int skp_digits_len(const char *dptr, const char *limit, int *shift) -{ - for (; dptr <= limit && *dptr == ' '; dptr++) - (*shift)++; - - return digits_len(dptr, limit, shift); -} - -/* Simple ipaddr parser.. */ -static int parse_ipaddr(const char *cp, const char **endp, - __be32 *ipaddr, const char *limit) -{ - unsigned long int val; - int i, digit = 0; - - for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) { - digit = 0; - if (!isdigit(*cp)) - break; - - val = simple_strtoul(cp, (char **)&cp, 10); - if (val > 0xFF) - return -1; - - ((u_int8_t *)ipaddr)[i] = val; - digit = 1; - - if (*cp != '.') - break; - cp++; - } - if (!digit) - return -1; - - if (endp) - *endp = cp; - - return 0; -} - -/* skip ip address. returns it lenght. */ -static int epaddr_len(const char *dptr, const char *limit, int *shift) -{ - const char *aux = dptr; - __be32 ip; - - if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) { - DEBUGP("ip: %s parse failed.!\n", dptr); - return 0; - } - - /* Port number */ - if (*dptr == ':') { - dptr++; - dptr += digits_len(dptr, limit, shift); - } - return dptr - aux; -} - -/* get address length, skiping user info. */ -static int skp_epaddr_len(const char *dptr, const char *limit, int *shift) -{ - int s = *shift; - - /* Search for @, but stop at the end of the line. - * We are inside a sip: URI, so we don't need to worry about - * continuation lines. */ - while (dptr <= limit && - *dptr != '@' && *dptr != '\r' && *dptr != '\n') { - (*shift)++; - dptr++; - } - - if (dptr <= limit && *dptr == '@') { - dptr++; - (*shift)++; - } else - *shift = s; - - return epaddr_len(dptr, limit, shift); -} - -/* Returns 0 if not found, -1 error parsing. */ -int ct_sip_get_info(const char *dptr, size_t dlen, - unsigned int *matchoff, - unsigned int *matchlen, - enum sip_header_pos pos) -{ - struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos]; - const char *limit, *aux, *k = dptr; - int shift = 0; - - limit = dptr + (dlen - hnfo->lnlen); - - while (dptr <= limit) { - if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && - (hnfo->sname == NULL || - strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { - dptr++; - continue; - } - aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, - ct_sip_lnlen(dptr, limit), - hnfo->case_sensitive); - if (!aux) { - DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str, - hnfo->lname); - return -1; - } - aux += hnfo->ln_strlen; - - *matchlen = hnfo->match_len(aux, limit, &shift); - if (!*matchlen) - return -1; - - *matchoff = (aux - k) + shift; - - DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname, - *matchlen); - return 1; - } - DEBUGP("%s header not found.\n", hnfo->lname); - return 0; -} -EXPORT_SYMBOL_GPL(ct_sip_get_info); - -static int set_expected_rtp(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - __be32 ipaddr, u_int16_t port, - const char *dptr) -{ - struct ip_conntrack_expect *exp; - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - int ret; - typeof(ip_nat_sdp_hook) ip_nat_sdp; - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) - return NF_DROP; - - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.udp.port = 0; - exp->tuple.dst.ip = ipaddr; - exp->tuple.dst.u.udp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_UDP; - - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.udp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.udp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - - exp->expectfn = NULL; - exp->flags = 0; - - ip_nat_sdp = rcu_dereference(ip_nat_sdp_hook); - if (ip_nat_sdp) - ret = ip_nat_sdp(pskb, ctinfo, exp, dptr); - else { - if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - else - ret = NF_ACCEPT; - } - ip_conntrack_expect_put(exp); - - return ret; -} - -static int sip_help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dataoff, datalen; - const char *dptr; - int ret = NF_ACCEPT; - int matchoff, matchlen; - __be32 ipaddr; - u_int16_t port; - typeof(ip_nat_sip_hook) ip_nat_sip; - - /* No Data ? */ - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); - if (dataoff >= (*pskb)->len) { - DEBUGP("skb->len = %u\n", (*pskb)->len); - return NF_ACCEPT; - } - - ip_ct_refresh(ct, *pskb, sip_timeout * HZ); - - if (!skb_is_nonlinear(*pskb)) - dptr = (*pskb)->data + dataoff; - else { - DEBUGP("Copy of skbuff not supported yet.\n"); - goto out; - } - - ip_nat_sip = rcu_dereference(ip_nat_sip_hook); - if (ip_nat_sip) { - if (!ip_nat_sip(pskb, ctinfo, ct, &dptr)) { - ret = NF_DROP; - goto out; - } - } - - /* After this point NAT, could have mangled skb, so - we need to recalculate payload lenght. */ - datalen = (*pskb)->len - dataoff; - - if (datalen < (sizeof("SIP/2.0 200") - 1)) - goto out; - - /* RTP info only in some SDP pkts */ - if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 && - memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) { - goto out; - } - /* Get ip and port address from SDP packet. */ - if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, - POS_CONNECTION) > 0) { - - /* We'll drop only if there are parse problems. */ - if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr, - dptr + datalen) < 0) { - ret = NF_DROP; - goto out; - } - if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, - POS_MEDIA) > 0) { - - port = simple_strtoul(dptr + matchoff, NULL, 10); - if (port < 1024) { - ret = NF_DROP; - goto out; - } - ret = set_expected_rtp(pskb, ct, ctinfo, - ipaddr, port, dptr); - } - } -out: - return ret; -} - -static struct ip_conntrack_helper sip[MAX_PORTS]; -static char sip_names[MAX_PORTS][10]; - -static void fini(void) -{ - int i; - for (i = 0; i < ports_c; i++) { - DEBUGP("unregistering helper for port %d\n", ports[i]); - ip_conntrack_helper_unregister(&sip[i]); - } -} - -static int __init init(void) -{ - int i, ret; - char *tmpname; - - if (ports_c == 0) - ports[ports_c++] = SIP_PORT; - - for (i = 0; i < ports_c; i++) { - /* Create helper structure */ - memset(&sip[i], 0, sizeof(struct ip_conntrack_helper)); - - sip[i].tuple.dst.protonum = IPPROTO_UDP; - sip[i].tuple.src.u.udp.port = htons(ports[i]); - sip[i].mask.src.u.udp.port = htons(0xFFFF); - sip[i].mask.dst.protonum = 0xFF; - sip[i].max_expected = 2; - sip[i].timeout = 3 * 60; /* 3 minutes */ - sip[i].me = THIS_MODULE; - sip[i].help = sip_help; - - tmpname = &sip_names[i][0]; - if (ports[i] == SIP_PORT) - sprintf(tmpname, "sip"); - else - sprintf(tmpname, "sip-%d", i); - sip[i].name = tmpname; - - DEBUGP("port #%d: %d\n", i, ports[i]); - - ret = ip_conntrack_helper_register(&sip[i]); - if (ret) { - printk("ERROR registering helper for port %d\n", - ports[i]); - fini(); - return ret; - } - } - return 0; -} - -module_init(init); -module_exit(fini); diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c deleted file mode 100644 index 56b2f75..0000000 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ /dev/null @@ -1,962 +0,0 @@ -/* This file contains all the functions required for the standalone - ip_conntrack module. - - These are not required by the compatibility layer. -*/ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2005 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_SYSCTL -#include -#endif -#include -#include -#include - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -MODULE_LICENSE("GPL"); - -extern atomic_t ip_conntrack_count; -DECLARE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat); - -static int kill_proto(struct ip_conntrack *i, void *data) -{ - return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == - *((u_int8_t *) data)); -} - -#ifdef CONFIG_PROC_FS -static int -print_tuple(struct seq_file *s, const struct ip_conntrack_tuple *tuple, - struct ip_conntrack_protocol *proto) -{ - seq_printf(s, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", - NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip)); - return proto->print_tuple(s, tuple); -} - -#ifdef CONFIG_IP_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif - -struct ct_iter_state { - unsigned int bucket; -}; - -static struct list_head *ct_get_first(struct seq_file *seq) -{ - struct ct_iter_state *st = seq->private; - - for (st->bucket = 0; - st->bucket < ip_conntrack_htable_size; - st->bucket++) { - if (!list_empty(&ip_conntrack_hash[st->bucket])) - return ip_conntrack_hash[st->bucket].next; - } - return NULL; -} - -static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head) -{ - struct ct_iter_state *st = seq->private; - - head = head->next; - while (head == &ip_conntrack_hash[st->bucket]) { - if (++st->bucket >= ip_conntrack_htable_size) - return NULL; - head = ip_conntrack_hash[st->bucket].next; - } - return head; -} - -static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos) -{ - struct list_head *head = ct_get_first(seq); - - if (head) - while (pos && (head = ct_get_next(seq, head))) - pos--; - return pos ? NULL : head; -} - -static void *ct_seq_start(struct seq_file *seq, loff_t *pos) -{ - read_lock_bh(&ip_conntrack_lock); - return ct_get_idx(seq, *pos); -} - -static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - (*pos)++; - return ct_get_next(s, v); -} - -static void ct_seq_stop(struct seq_file *s, void *v) -{ - read_unlock_bh(&ip_conntrack_lock); -} - -static int ct_seq_show(struct seq_file *s, void *v) -{ - const struct ip_conntrack_tuple_hash *hash = v; - const struct ip_conntrack *conntrack = tuplehash_to_ctrack(hash); - struct ip_conntrack_protocol *proto; - - IP_NF_ASSERT(conntrack); - - /* we only want to print DIR_ORIGINAL */ - if (DIRECTION(hash)) - return 0; - - proto = __ip_conntrack_proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); - IP_NF_ASSERT(proto); - - if (seq_printf(s, "%-8s %u %ld ", - proto->name, - conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum, - timer_pending(&conntrack->timeout) - ? (long)(conntrack->timeout.expires - jiffies)/HZ - : 0) != 0) - return -ENOSPC; - - if (proto->print_conntrack(s, conntrack)) - return -ENOSPC; - - if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - proto)) - return -ENOSPC; - - if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL])) - return -ENOSPC; - - if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status))) - if (seq_printf(s, "[UNREPLIED] ")) - return -ENOSPC; - - if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple, - proto)) - return -ENOSPC; - - if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY])) - return -ENOSPC; - - if (test_bit(IPS_ASSURED_BIT, &conntrack->status)) - if (seq_printf(s, "[ASSURED] ")) - return -ENOSPC; - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - if (seq_printf(s, "mark=%u ", conntrack->mark)) - return -ENOSPC; -#endif - -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK - if (seq_printf(s, "secmark=%u ", conntrack->secmark)) - return -ENOSPC; -#endif - - if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) - return -ENOSPC; - - return 0; -} - -static struct seq_operations ct_seq_ops = { - .start = ct_seq_start, - .next = ct_seq_next, - .stop = ct_seq_stop, - .show = ct_seq_show -}; - -static int ct_open(struct inode *inode, struct file *file) -{ - struct seq_file *seq; - struct ct_iter_state *st; - int ret; - - st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL); - if (st == NULL) - return -ENOMEM; - ret = seq_open(file, &ct_seq_ops); - if (ret) - goto out_free; - seq = file->private_data; - seq->private = st; - memset(st, 0, sizeof(struct ct_iter_state)); - return ret; -out_free: - kfree(st); - return ret; -} - -static const struct file_operations ct_file_ops = { - .owner = THIS_MODULE, - .open = ct_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -/* expects */ -static void *exp_seq_start(struct seq_file *s, loff_t *pos) -{ - struct list_head *e = &ip_conntrack_expect_list; - loff_t i; - - /* strange seq_file api calls stop even if we fail, - * thus we need to grab lock since stop unlocks */ - read_lock_bh(&ip_conntrack_lock); - - if (list_empty(e)) - return NULL; - - for (i = 0; i <= *pos; i++) { - e = e->next; - if (e == &ip_conntrack_expect_list) - return NULL; - } - return e; -} - -static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - struct list_head *e = v; - - ++*pos; - e = e->next; - - if (e == &ip_conntrack_expect_list) - return NULL; - - return e; -} - -static void exp_seq_stop(struct seq_file *s, void *v) -{ - read_unlock_bh(&ip_conntrack_lock); -} - -static int exp_seq_show(struct seq_file *s, void *v) -{ - struct ip_conntrack_expect *expect = v; - - if (expect->timeout.function) - seq_printf(s, "%ld ", timer_pending(&expect->timeout) - ? (long)(expect->timeout.expires - jiffies)/HZ : 0); - else - seq_printf(s, "- "); - - seq_printf(s, "proto=%u ", expect->tuple.dst.protonum); - - print_tuple(s, &expect->tuple, - __ip_conntrack_proto_find(expect->tuple.dst.protonum)); - return seq_putc(s, '\n'); -} - -static struct seq_operations exp_seq_ops = { - .start = exp_seq_start, - .next = exp_seq_next, - .stop = exp_seq_stop, - .show = exp_seq_show -}; - -static int exp_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &exp_seq_ops); -} - -static const struct file_operations exp_file_ops = { - .owner = THIS_MODULE, - .open = exp_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release -}; - -static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) -{ - int cpu; - - if (*pos == 0) - return SEQ_START_TOKEN; - - for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { - if (!cpu_possible(cpu)) - continue; - *pos = cpu+1; - return &per_cpu(ip_conntrack_stat, cpu); - } - - return NULL; -} - -static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - int cpu; - - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { - if (!cpu_possible(cpu)) - continue; - *pos = cpu+1; - return &per_cpu(ip_conntrack_stat, cpu); - } - - return NULL; -} - -static void ct_cpu_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int ct_cpu_seq_show(struct seq_file *seq, void *v) -{ - unsigned int nr_conntracks = atomic_read(&ip_conntrack_count); - struct ip_conntrack_stat *st = v; - - if (v == SEQ_START_TOKEN) { - seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n"); - return 0; - } - - seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " - "%08x %08x %08x %08x %08x %08x %08x %08x \n", - nr_conntracks, - st->searched, - st->found, - st->new, - st->invalid, - st->ignore, - st->delete, - st->delete_list, - st->insert, - st->insert_failed, - st->drop, - st->early_drop, - st->error, - - st->expect_new, - st->expect_create, - st->expect_delete - ); - return 0; -} - -static struct seq_operations ct_cpu_seq_ops = { - .start = ct_cpu_seq_start, - .next = ct_cpu_seq_next, - .stop = ct_cpu_seq_stop, - .show = ct_cpu_seq_show, -}; - -static int ct_cpu_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &ct_cpu_seq_ops); -} - -static const struct file_operations ct_cpu_seq_fops = { - .owner = THIS_MODULE, - .open = ct_cpu_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; -#endif - -static unsigned int ip_confirm(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - /* We've seen it coming out the other side: confirm it */ - return ip_conntrack_confirm(pskb); -} - -static unsigned int ip_conntrack_help(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - /* This is where we call the helper: as the packet goes out. */ - ct = ip_conntrack_get(*pskb, &ctinfo); - if (ct && ct->helper && ctinfo != IP_CT_RELATED + IP_CT_IS_REPLY) { - unsigned int ret; - ret = ct->helper->help(pskb, ct, ctinfo); - if (ret != NF_ACCEPT) - return ret; - } - return NF_ACCEPT; -} - -static unsigned int ip_conntrack_defrag(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ -#if !defined(CONFIG_IP_NF_NAT) && !defined(CONFIG_IP_NF_NAT_MODULE) - /* Previously seen (loopback)? Ignore. Do this before - fragment check. */ - if ((*pskb)->nfct) - return NF_ACCEPT; -#endif - - /* Gather fragments. */ - if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { - *pskb = ip_ct_gather_frags(*pskb, - hooknum == NF_IP_PRE_ROUTING ? - IP_DEFRAG_CONNTRACK_IN : - IP_DEFRAG_CONNTRACK_OUT); - if (!*pskb) - return NF_STOLEN; - } - return NF_ACCEPT; -} - -static unsigned int ip_conntrack_local(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - /* root is playing with raw sockets. */ - if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { - if (net_ratelimit()) - printk("ipt_hook: happy cracking.\n"); - return NF_ACCEPT; - } - return ip_conntrack_in(hooknum, pskb, in, out, okfn); -} - -/* Connection tracking may drop packets, but never alters them, so - make it the first hook. */ -static struct nf_hook_ops ip_conntrack_ops[] = { - { - .hook = ip_conntrack_defrag, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_PRE_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_DEFRAG, - }, - { - .hook = ip_conntrack_in, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_PRE_ROUTING, - .priority = NF_IP_PRI_CONNTRACK, - }, - { - .hook = ip_conntrack_defrag, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_OUT, - .priority = NF_IP_PRI_CONNTRACK_DEFRAG, - }, - { - .hook = ip_conntrack_local, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_OUT, - .priority = NF_IP_PRI_CONNTRACK, - }, - { - .hook = ip_conntrack_help, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_HELPER, - }, - { - .hook = ip_conntrack_help, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_CONNTRACK_HELPER, - }, - { - .hook = ip_confirm, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM, - }, - { - .hook = ip_confirm, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM, - }, -}; - -/* Sysctl support */ - -int ip_conntrack_checksum __read_mostly = 1; - -#ifdef CONFIG_SYSCTL - -/* From ip_conntrack_core.c */ -extern int ip_conntrack_max; -extern unsigned int ip_conntrack_htable_size; - -/* From ip_conntrack_proto_tcp.c */ -extern unsigned int ip_ct_tcp_timeout_syn_sent; -extern unsigned int ip_ct_tcp_timeout_syn_recv; -extern unsigned int ip_ct_tcp_timeout_established; -extern unsigned int ip_ct_tcp_timeout_fin_wait; -extern unsigned int ip_ct_tcp_timeout_close_wait; -extern unsigned int ip_ct_tcp_timeout_last_ack; -extern unsigned int ip_ct_tcp_timeout_time_wait; -extern unsigned int ip_ct_tcp_timeout_close; -extern unsigned int ip_ct_tcp_timeout_max_retrans; -extern int ip_ct_tcp_loose; -extern int ip_ct_tcp_be_liberal; -extern int ip_ct_tcp_max_retrans; - -/* From ip_conntrack_proto_udp.c */ -extern unsigned int ip_ct_udp_timeout; -extern unsigned int ip_ct_udp_timeout_stream; - -/* From ip_conntrack_proto_icmp.c */ -extern unsigned int ip_ct_icmp_timeout; - -/* From ip_conntrack_proto_generic.c */ -extern unsigned int ip_ct_generic_timeout; - -/* Log invalid packets of a given protocol */ -static int log_invalid_proto_min = 0; -static int log_invalid_proto_max = 255; - -static struct ctl_table_header *ip_ct_sysctl_header; - -static ctl_table ip_ct_sysctl_table[] = { - { - .ctl_name = NET_IPV4_NF_CONNTRACK_MAX, - .procname = "ip_conntrack_max", - .data = &ip_conntrack_max, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_COUNT, - .procname = "ip_conntrack_count", - .data = &ip_conntrack_count, - .maxlen = sizeof(int), - .mode = 0444, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_BUCKETS, - .procname = "ip_conntrack_buckets", - .data = &ip_conntrack_htable_size, - .maxlen = sizeof(unsigned int), - .mode = 0444, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_CHECKSUM, - .procname = "ip_conntrack_checksum", - .data = &ip_conntrack_checksum, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT, - .procname = "ip_conntrack_tcp_timeout_syn_sent", - .data = &ip_ct_tcp_timeout_syn_sent, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV, - .procname = "ip_conntrack_tcp_timeout_syn_recv", - .data = &ip_ct_tcp_timeout_syn_recv, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED, - .procname = "ip_conntrack_tcp_timeout_established", - .data = &ip_ct_tcp_timeout_established, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT, - .procname = "ip_conntrack_tcp_timeout_fin_wait", - .data = &ip_ct_tcp_timeout_fin_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT, - .procname = "ip_conntrack_tcp_timeout_close_wait", - .data = &ip_ct_tcp_timeout_close_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK, - .procname = "ip_conntrack_tcp_timeout_last_ack", - .data = &ip_ct_tcp_timeout_last_ack, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT, - .procname = "ip_conntrack_tcp_timeout_time_wait", - .data = &ip_ct_tcp_timeout_time_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE, - .procname = "ip_conntrack_tcp_timeout_close", - .data = &ip_ct_tcp_timeout_close, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT, - .procname = "ip_conntrack_udp_timeout", - .data = &ip_ct_udp_timeout, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM, - .procname = "ip_conntrack_udp_timeout_stream", - .data = &ip_ct_udp_timeout_stream, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT, - .procname = "ip_conntrack_icmp_timeout", - .data = &ip_ct_icmp_timeout, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT, - .procname = "ip_conntrack_generic_timeout", - .data = &ip_ct_generic_timeout, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_LOG_INVALID, - .procname = "ip_conntrack_log_invalid", - .data = &ip_ct_log_invalid, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, - .extra1 = &log_invalid_proto_min, - .extra2 = &log_invalid_proto_max, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS, - .procname = "ip_conntrack_tcp_timeout_max_retrans", - .data = &ip_ct_tcp_timeout_max_retrans, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_LOOSE, - .procname = "ip_conntrack_tcp_loose", - .data = &ip_ct_tcp_loose, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL, - .procname = "ip_conntrack_tcp_be_liberal", - .data = &ip_ct_tcp_be_liberal, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_MAX_RETRANS, - .procname = "ip_conntrack_tcp_max_retrans", - .data = &ip_ct_tcp_max_retrans, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { .ctl_name = 0 } -}; - -#define NET_IP_CONNTRACK_MAX 2089 - -static ctl_table ip_ct_netfilter_table[] = { - { - .ctl_name = NET_IPV4_NETFILTER, - .procname = "netfilter", - .mode = 0555, - .child = ip_ct_sysctl_table, - }, - { - .ctl_name = NET_IP_CONNTRACK_MAX, - .procname = "ip_conntrack_max", - .data = &ip_conntrack_max, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_ipv4_table[] = { - { - .ctl_name = NET_IPV4, - .procname = "ipv4", - .mode = 0555, - .child = ip_ct_netfilter_table, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_net_table[] = { - { - .ctl_name = CTL_NET, - .procname = "net", - .mode = 0555, - .child = ip_ct_ipv4_table, - }, - { .ctl_name = 0 } -}; - -EXPORT_SYMBOL(ip_ct_log_invalid); -#endif /* CONFIG_SYSCTL */ - -/* FIXME: Allow NULL functions and sub in pointers to generic for - them. --RR */ -int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto) -{ - int ret = 0; - - write_lock_bh(&ip_conntrack_lock); - if (ip_ct_protos[proto->proto] != &ip_conntrack_generic_protocol) { - ret = -EBUSY; - goto out; - } - rcu_assign_pointer(ip_ct_protos[proto->proto], proto); - out: - write_unlock_bh(&ip_conntrack_lock); - return ret; -} - -void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto) -{ - write_lock_bh(&ip_conntrack_lock); - rcu_assign_pointer(ip_ct_protos[proto->proto], - &ip_conntrack_generic_protocol); - write_unlock_bh(&ip_conntrack_lock); - synchronize_rcu(); - - /* Remove all contrack entries for this protocol */ - ip_ct_iterate_cleanup(kill_proto, &proto->proto); -} - -static int __init ip_conntrack_standalone_init(void) -{ -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *proc, *proc_exp, *proc_stat; -#endif - int ret = 0; - - ret = ip_conntrack_init(); - if (ret < 0) - return ret; - -#ifdef CONFIG_PROC_FS - ret = -ENOMEM; - proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops); - if (!proc) goto cleanup_init; - - proc_exp = proc_net_fops_create("ip_conntrack_expect", 0440, - &exp_file_ops); - if (!proc_exp) goto cleanup_proc; - - proc_stat = create_proc_entry("ip_conntrack", S_IRUGO, proc_net_stat); - if (!proc_stat) - goto cleanup_proc_exp; - - proc_stat->proc_fops = &ct_cpu_seq_fops; - proc_stat->owner = THIS_MODULE; -#endif - - ret = nf_register_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops)); - if (ret < 0) { - printk("ip_conntrack: can't register hooks.\n"); - goto cleanup_proc_stat; - } -#ifdef CONFIG_SYSCTL - ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table); - if (ip_ct_sysctl_header == NULL) { - printk("ip_conntrack: can't register to sysctl.\n"); - ret = -ENOMEM; - goto cleanup_hooks; - } -#endif - return ret; - -#ifdef CONFIG_SYSCTL - cleanup_hooks: - nf_unregister_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops)); -#endif - cleanup_proc_stat: -#ifdef CONFIG_PROC_FS - remove_proc_entry("ip_conntrack", proc_net_stat); - cleanup_proc_exp: - proc_net_remove("ip_conntrack_expect"); - cleanup_proc: - proc_net_remove("ip_conntrack"); - cleanup_init: -#endif /* CONFIG_PROC_FS */ - ip_conntrack_cleanup(); - return ret; -} - -static void __exit ip_conntrack_standalone_fini(void) -{ - synchronize_net(); -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(ip_ct_sysctl_header); -#endif - nf_unregister_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops)); -#ifdef CONFIG_PROC_FS - remove_proc_entry("ip_conntrack", proc_net_stat); - proc_net_remove("ip_conntrack_expect"); - proc_net_remove("ip_conntrack"); -#endif /* CONFIG_PROC_FS */ - ip_conntrack_cleanup(); -} - -module_init(ip_conntrack_standalone_init); -module_exit(ip_conntrack_standalone_fini); - -/* Some modules need us, but don't depend directly on any symbol. - They should call this. */ -void need_conntrack(void) -{ -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -EXPORT_SYMBOL_GPL(ip_conntrack_chain); -EXPORT_SYMBOL_GPL(ip_conntrack_expect_chain); -EXPORT_SYMBOL_GPL(ip_conntrack_register_notifier); -EXPORT_SYMBOL_GPL(ip_conntrack_unregister_notifier); -EXPORT_SYMBOL_GPL(__ip_ct_event_cache_init); -EXPORT_PER_CPU_SYMBOL_GPL(ip_conntrack_ecache); -#endif -EXPORT_SYMBOL(ip_conntrack_protocol_register); -EXPORT_SYMBOL(ip_conntrack_protocol_unregister); -EXPORT_SYMBOL(ip_ct_get_tuple); -EXPORT_SYMBOL(invert_tuplepr); -EXPORT_SYMBOL(ip_conntrack_alter_reply); -EXPORT_SYMBOL(ip_conntrack_destroyed); -EXPORT_SYMBOL(need_conntrack); -EXPORT_SYMBOL(ip_conntrack_helper_register); -EXPORT_SYMBOL(ip_conntrack_helper_unregister); -EXPORT_SYMBOL(ip_ct_iterate_cleanup); -EXPORT_SYMBOL(__ip_ct_refresh_acct); - -EXPORT_SYMBOL(ip_conntrack_expect_alloc); -EXPORT_SYMBOL(ip_conntrack_expect_put); -EXPORT_SYMBOL_GPL(__ip_conntrack_expect_find); -EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get); -EXPORT_SYMBOL(ip_conntrack_expect_related); -EXPORT_SYMBOL(ip_conntrack_unexpect_related); -EXPORT_SYMBOL_GPL(ip_conntrack_expect_list); -EXPORT_SYMBOL_GPL(ip_ct_unlink_expect); - -EXPORT_SYMBOL(ip_conntrack_tuple_taken); -EXPORT_SYMBOL(ip_ct_gather_frags); -EXPORT_SYMBOL(ip_conntrack_htable_size); -EXPORT_SYMBOL(ip_conntrack_lock); -EXPORT_SYMBOL(ip_conntrack_hash); -EXPORT_SYMBOL(ip_conntrack_untracked); -EXPORT_SYMBOL_GPL(ip_conntrack_find_get); -#ifdef CONFIG_IP_NF_NAT_NEEDED -EXPORT_SYMBOL(ip_conntrack_tcp_update); -#endif - -EXPORT_SYMBOL_GPL(ip_conntrack_flush); -EXPORT_SYMBOL_GPL(__ip_conntrack_find); - -EXPORT_SYMBOL_GPL(ip_conntrack_alloc); -EXPORT_SYMBOL_GPL(ip_conntrack_free); -EXPORT_SYMBOL_GPL(ip_conntrack_hash_insert); - -EXPORT_SYMBOL_GPL(ip_ct_remove_expectations); - -EXPORT_SYMBOL_GPL(ip_conntrack_helper_find_get); -EXPORT_SYMBOL_GPL(ip_conntrack_helper_put); -EXPORT_SYMBOL_GPL(__ip_conntrack_helper_find_byname); - -EXPORT_SYMBOL_GPL(ip_conntrack_proto_find_get); -EXPORT_SYMBOL_GPL(ip_conntrack_proto_put); -EXPORT_SYMBOL_GPL(__ip_conntrack_proto_find); -EXPORT_SYMBOL_GPL(ip_conntrack_checksum); -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -EXPORT_SYMBOL_GPL(ip_ct_port_tuple_to_nfattr); -EXPORT_SYMBOL_GPL(ip_ct_port_nfattr_to_tuple); -#endif diff --git a/net/ipv4/netfilter/ip_conntrack_tftp.c b/net/ipv4/netfilter/ip_conntrack_tftp.c deleted file mode 100644 index 76e175e..0000000 --- a/net/ipv4/netfilter/ip_conntrack_tftp.c +++ /dev/null @@ -1,161 +0,0 @@ -/* (C) 2001-2002 Magnus Boden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Version: 0.0.7 - * - * Thu 21 Mar 2002 Harald Welte - * - port to newnat API - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Magnus Boden "); -MODULE_DESCRIPTION("tftp connection tracking helper"); -MODULE_LICENSE("GPL"); - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of tftp servers"); - -#if 0 -#define DEBUGP(format, args...) printk("%s:%s:" format, \ - __FILE__, __FUNCTION__ , ## args) -#else -#define DEBUGP(format, args...) -#endif - -unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp); -EXPORT_SYMBOL_GPL(ip_nat_tftp_hook); - -static int tftp_help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct tftphdr _tftph, *tfh; - struct ip_conntrack_expect *exp; - unsigned int ret = NF_ACCEPT; - typeof(ip_nat_tftp_hook) ip_nat_tftp; - - tfh = skb_header_pointer(*pskb, - (*pskb)->nh.iph->ihl*4+sizeof(struct udphdr), - sizeof(_tftph), &_tftph); - if (tfh == NULL) - return NF_ACCEPT; - - switch (ntohs(tfh->opcode)) { - /* RRQ and WRQ works the same way */ - case TFTP_OPCODE_READ: - case TFTP_OPCODE_WRITE: - DEBUGP(""); - DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) - return NF_DROP; - - exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; - exp->mask.src.ip = htonl(0xffffffff); - exp->mask.src.u.udp.port = 0; - exp->mask.dst.ip = htonl(0xffffffff); - exp->mask.dst.u.udp.port = htons(0xffff); - exp->mask.dst.protonum = 0xff; - exp->expectfn = NULL; - exp->flags = 0; - - DEBUGP("expect: "); - DUMP_TUPLE(&exp->tuple); - DUMP_TUPLE(&exp->mask); - ip_nat_tftp = rcu_dereference(ip_nat_tftp_hook); - if (ip_nat_tftp) - ret = ip_nat_tftp(pskb, ctinfo, exp); - else if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - ip_conntrack_expect_put(exp); - break; - case TFTP_OPCODE_DATA: - case TFTP_OPCODE_ACK: - DEBUGP("Data/ACK opcode\n"); - break; - case TFTP_OPCODE_ERROR: - DEBUGP("Error opcode\n"); - break; - default: - DEBUGP("Unknown opcode\n"); - } - return NF_ACCEPT; -} - -static struct ip_conntrack_helper tftp[MAX_PORTS]; -static char tftp_names[MAX_PORTS][sizeof("tftp-65535")]; - -static void ip_conntrack_tftp_fini(void) -{ - int i; - - for (i = 0 ; i < ports_c; i++) { - DEBUGP("unregistering helper for port %d\n", - ports[i]); - ip_conntrack_helper_unregister(&tftp[i]); - } -} - -static int __init ip_conntrack_tftp_init(void) -{ - int i, ret; - char *tmpname; - - if (ports_c == 0) - ports[ports_c++] = TFTP_PORT; - - for (i = 0; i < ports_c; i++) { - /* Create helper structure */ - memset(&tftp[i], 0, sizeof(struct ip_conntrack_helper)); - - tftp[i].tuple.dst.protonum = IPPROTO_UDP; - tftp[i].tuple.src.u.udp.port = htons(ports[i]); - tftp[i].mask.dst.protonum = 0xFF; - tftp[i].mask.src.u.udp.port = htons(0xFFFF); - tftp[i].max_expected = 1; - tftp[i].timeout = 5 * 60; /* 5 minutes */ - tftp[i].me = THIS_MODULE; - tftp[i].help = tftp_help; - - tmpname = &tftp_names[i][0]; - if (ports[i] == TFTP_PORT) - sprintf(tmpname, "tftp"); - else - sprintf(tmpname, "tftp-%d", i); - tftp[i].name = tmpname; - - DEBUGP("port #%d: %d\n", i, ports[i]); - - ret=ip_conntrack_helper_register(&tftp[i]); - if (ret) { - printk("ERROR registering helper for port %d\n", - ports[i]); - ip_conntrack_tftp_fini(); - return(ret); - } - } - return(0); -} - -module_init(ip_conntrack_tftp_init); -module_exit(ip_conntrack_tftp_fini); diff --git a/net/ipv4/netfilter/ip_nat_amanda.c b/net/ipv4/netfilter/ip_nat_amanda.c deleted file mode 100644 index 85df1a9..0000000 --- a/net/ipv4/netfilter/ip_nat_amanda.c +++ /dev/null @@ -1,85 +0,0 @@ -/* Amanda extension for TCP NAT alteration. - * (C) 2002 by Brian J. Murrell - * based on a copy of HW's ip_nat_irc.c as well as other modules - * - * 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. - * - * Module load syntax: - * insmod ip_nat_amanda.o - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -MODULE_AUTHOR("Brian J. Murrell "); -MODULE_DESCRIPTION("Amanda NAT helper"); -MODULE_LICENSE("GPL"); - -static unsigned int help(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp) -{ - char buffer[sizeof("65535")]; - u_int16_t port; - unsigned int ret; - - /* Connection comes from client. */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->dir = IP_CT_DIR_ORIGINAL; - - /* When you see the packet, we need to NAT it the same as the - * this one (ie. same IP: it will be TCP and master is UDP). */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { - exp->tuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - sprintf(buffer, "%u", port); - ret = ip_nat_mangle_udp_packet(pskb, exp->master, ctinfo, - matchoff, matchlen, - buffer, strlen(buffer)); - if (ret != NF_ACCEPT) - ip_conntrack_unexpect_related(exp); - return ret; -} - -static void __exit ip_nat_amanda_fini(void) -{ - rcu_assign_pointer(ip_nat_amanda_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_amanda_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_amanda_hook)); - rcu_assign_pointer(ip_nat_amanda_hook, help); - return 0; -} - -module_init(ip_nat_amanda_init); -module_exit(ip_nat_amanda_fini); diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c deleted file mode 100644 index 40737fd..0000000 --- a/net/ipv4/netfilter/ip_nat_core.c +++ /dev/null @@ -1,634 +0,0 @@ -/* NAT for netfilter; shared with compatibility layer. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For tcp_prot in getorigdst */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -DEFINE_RWLOCK(ip_nat_lock); - -/* Calculated at init based on memory size */ -static unsigned int ip_nat_htable_size; - -static struct list_head *bysource; - -#define MAX_IP_NAT_PROTO 256 -static struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO]; - -static inline struct ip_nat_protocol * -__ip_nat_proto_find(u_int8_t protonum) -{ - return rcu_dereference(ip_nat_protos[protonum]); -} - -struct ip_nat_protocol * -ip_nat_proto_find_get(u_int8_t protonum) -{ - struct ip_nat_protocol *p; - - rcu_read_lock(); - p = __ip_nat_proto_find(protonum); - if (!try_module_get(p->me)) - p = &ip_nat_unknown_protocol; - rcu_read_unlock(); - - return p; -} -EXPORT_SYMBOL_GPL(ip_nat_proto_find_get); - -void -ip_nat_proto_put(struct ip_nat_protocol *p) -{ - module_put(p->me); -} -EXPORT_SYMBOL_GPL(ip_nat_proto_put); - -/* We keep an extra hash for each conntrack, for fast searching. */ -static inline unsigned int -hash_by_src(const struct ip_conntrack_tuple *tuple) -{ - /* Original src, to ensure we map it consistently if poss. */ - return jhash_3words((__force u32)tuple->src.ip, tuple->src.u.all, - tuple->dst.protonum, 0) % ip_nat_htable_size; -} - -/* Noone using conntrack by the time this called. */ -static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn) -{ - if (!(conn->status & IPS_NAT_DONE_MASK)) - return; - - write_lock_bh(&ip_nat_lock); - list_del(&conn->nat.info.bysource); - write_unlock_bh(&ip_nat_lock); -} - -/* Is this tuple already taken? (not by us) */ -int -ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - /* Conntrack tracking doesn't keep track of outgoing tuples; only - incoming ones. NAT means they don't have a fixed mapping, - so we invert the tuple and look for the incoming reply. - - We could keep a separate hash if this proves too slow. */ - struct ip_conntrack_tuple reply; - - invert_tuplepr(&reply, tuple); - return ip_conntrack_tuple_taken(&reply, ignored_conntrack); -} -EXPORT_SYMBOL(ip_nat_used_tuple); - -/* If we source map this tuple so reply looks like reply_tuple, will - * that meet the constraints of range. */ -static int -in_range(const struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range) -{ - struct ip_nat_protocol *proto; - int ret = 0; - - /* If we are supposed to map IPs, then we must be in the - range specified, otherwise let this drag us onto a new src IP. */ - if (range->flags & IP_NAT_RANGE_MAP_IPS) { - if (ntohl(tuple->src.ip) < ntohl(range->min_ip) - || ntohl(tuple->src.ip) > ntohl(range->max_ip)) - return 0; - } - - rcu_read_lock(); - proto = __ip_nat_proto_find(tuple->dst.protonum); - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) - || proto->in_range(tuple, IP_NAT_MANIP_SRC, - &range->min, &range->max)) - ret = 1; - rcu_read_unlock(); - - return ret; -} - -static inline int -same_src(const struct ip_conntrack *ct, - const struct ip_conntrack_tuple *tuple) -{ - return (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum - == tuple->dst.protonum - && ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip - == tuple->src.ip - && ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all - == tuple->src.u.all); -} - -/* Only called for SRC manip */ -static int -find_appropriate_src(const struct ip_conntrack_tuple *tuple, - struct ip_conntrack_tuple *result, - const struct ip_nat_range *range) -{ - unsigned int h = hash_by_src(tuple); - struct ip_conntrack *ct; - - read_lock_bh(&ip_nat_lock); - list_for_each_entry(ct, &bysource[h], nat.info.bysource) { - if (same_src(ct, tuple)) { - /* Copy source part from reply tuple. */ - invert_tuplepr(result, - &ct->tuplehash[IP_CT_DIR_REPLY].tuple); - result->dst = tuple->dst; - - if (in_range(result, range)) { - read_unlock_bh(&ip_nat_lock); - return 1; - } - } - } - read_unlock_bh(&ip_nat_lock); - return 0; -} - -/* For [FUTURE] fragmentation handling, we want the least-used - src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus - if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports - 1-65535, we don't do pro-rata allocation based on ports; we choose - the ip with the lowest src-ip/dst-ip/proto usage. -*/ -static void -find_best_ips_proto(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - const struct ip_conntrack *conntrack, - enum ip_nat_manip_type maniptype) -{ - __be32 *var_ipp; - /* Host order */ - u_int32_t minip, maxip, j; - - /* No IP mapping? Do nothing. */ - if (!(range->flags & IP_NAT_RANGE_MAP_IPS)) - return; - - if (maniptype == IP_NAT_MANIP_SRC) - var_ipp = &tuple->src.ip; - else - var_ipp = &tuple->dst.ip; - - /* Fast path: only one choice. */ - if (range->min_ip == range->max_ip) { - *var_ipp = range->min_ip; - return; - } - - /* Hashing source and destination IPs gives a fairly even - * spread in practice (if there are a small number of IPs - * involved, there usually aren't that many connections - * anyway). The consistency means that servers see the same - * client coming from the same IP (some Internet Banking sites - * like this), even across reboots. */ - minip = ntohl(range->min_ip); - maxip = ntohl(range->max_ip); - j = jhash_2words((__force u32)tuple->src.ip, (__force u32)tuple->dst.ip, 0); - *var_ipp = htonl(minip + j % (maxip - minip + 1)); -} - -/* Manipulate the tuple into the range given. For NF_IP_POST_ROUTING, - * we change the source to map into the range. For NF_IP_PRE_ROUTING - * and NF_IP_LOCAL_OUT, we change the destination to map into the - * range. It might not be possible to get a unique tuple, but we try. - * At worst (or if we race), we will end up with a final duplicate in - * __ip_conntrack_confirm and drop the packet. */ -static void -get_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig_tuple, - const struct ip_nat_range *range, - struct ip_conntrack *conntrack, - enum ip_nat_manip_type maniptype) -{ - struct ip_nat_protocol *proto; - - /* 1) If this srcip/proto/src-proto-part is currently mapped, - and that same mapping gives a unique tuple within the given - range, use that. - - This is only required for source (ie. NAT/masq) mappings. - So far, we don't do local source mappings, so multiple - manips not an issue. */ - if (maniptype == IP_NAT_MANIP_SRC) { - if (find_appropriate_src(orig_tuple, tuple, range)) { - DEBUGP("get_unique_tuple: Found current src map\n"); - if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) - if (!ip_nat_used_tuple(tuple, conntrack)) - return; - } - } - - /* 2) Select the least-used IP/proto combination in the given - range. */ - *tuple = *orig_tuple; - find_best_ips_proto(tuple, range, conntrack, maniptype); - - /* 3) The per-protocol part of the manip is made to map into - the range to make a unique tuple. */ - - rcu_read_lock(); - proto = __ip_nat_proto_find(orig_tuple->dst.protonum); - - /* Change protocol info to have some randomization */ - if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { - proto->unique_tuple(tuple, range, maniptype, conntrack); - goto out; - } - - /* Only bother mapping if it's not already in range and unique */ - if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) - || proto->in_range(tuple, maniptype, &range->min, &range->max)) - && !ip_nat_used_tuple(tuple, conntrack)) - goto out; - - /* Last change: get protocol to try to obtain unique tuple. */ - proto->unique_tuple(tuple, range, maniptype, conntrack); -out: - rcu_read_unlock(); -} - -unsigned int -ip_nat_setup_info(struct ip_conntrack *conntrack, - const struct ip_nat_range *range, - unsigned int hooknum) -{ - struct ip_conntrack_tuple curr_tuple, new_tuple; - struct ip_nat_info *info = &conntrack->nat.info; - int have_to_hash = !(conntrack->status & IPS_NAT_DONE_MASK); - enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum); - - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING - || hooknum == NF_IP_POST_ROUTING - || hooknum == NF_IP_LOCAL_IN - || hooknum == NF_IP_LOCAL_OUT); - BUG_ON(ip_nat_initialized(conntrack, maniptype)); - - /* What we've got will look like inverse of reply. Normally - this is what is in the conntrack, except for prior - manipulations (future optimization: if num_manips == 0, - orig_tp = - conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ - invert_tuplepr(&curr_tuple, - &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple); - - get_unique_tuple(&new_tuple, &curr_tuple, range, conntrack, maniptype); - - if (!ip_ct_tuple_equal(&new_tuple, &curr_tuple)) { - struct ip_conntrack_tuple reply; - - /* Alter conntrack table so will recognize replies. */ - invert_tuplepr(&reply, &new_tuple); - ip_conntrack_alter_reply(conntrack, &reply); - - /* Non-atomic: we own this at the moment. */ - if (maniptype == IP_NAT_MANIP_SRC) - conntrack->status |= IPS_SRC_NAT; - else - conntrack->status |= IPS_DST_NAT; - } - - /* Place in source hash if this is the first time. */ - if (have_to_hash) { - unsigned int srchash - = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple); - write_lock_bh(&ip_nat_lock); - list_add(&info->bysource, &bysource[srchash]); - write_unlock_bh(&ip_nat_lock); - } - - /* It's done. */ - if (maniptype == IP_NAT_MANIP_DST) - set_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status); - else - set_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status); - - return NF_ACCEPT; -} -EXPORT_SYMBOL(ip_nat_setup_info); - -/* Returns true if succeeded. */ -static int -manip_pkt(u_int16_t proto, - struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *target, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph; - struct ip_nat_protocol *p; - - if (!skb_make_writable(pskb, iphdroff + sizeof(*iph))) - return 0; - - iph = (void *)(*pskb)->data + iphdroff; - - /* Manipulate protcol part. */ - - /* rcu_read_lock()ed by nf_hook_slow */ - p = __ip_nat_proto_find(proto); - if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) - return 0; - - iph = (void *)(*pskb)->data + iphdroff; - - if (maniptype == IP_NAT_MANIP_SRC) { - nf_csum_replace4(&iph->check, iph->saddr, target->src.ip); - iph->saddr = target->src.ip; - } else { - nf_csum_replace4(&iph->check, iph->daddr, target->dst.ip); - iph->daddr = target->dst.ip; - } - return 1; -} - -/* Do packet manipulations according to ip_nat_setup_info. */ -unsigned int ip_nat_packet(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff **pskb) -{ - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned long statusbit; - enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum); - - if (mtype == IP_NAT_MANIP_SRC) - statusbit = IPS_SRC_NAT; - else - statusbit = IPS_DST_NAT; - - /* Invert if this is reply dir. */ - if (dir == IP_CT_DIR_REPLY) - statusbit ^= IPS_NAT_MASK; - - /* Non-atomic: these bits don't change. */ - if (ct->status & statusbit) { - struct ip_conntrack_tuple target; - - /* We are aiming to look like inverse of other direction. */ - invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); - - if (!manip_pkt(target.dst.protonum, pskb, 0, &target, mtype)) - return NF_DROP; - } - return NF_ACCEPT; -} -EXPORT_SYMBOL_GPL(ip_nat_packet); - -/* Dir is direction ICMP is coming from (opposite to packet it contains) */ -int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff **pskb) -{ - struct { - struct icmphdr icmp; - struct iphdr ip; - } *inside; - struct ip_conntrack_protocol *proto; - struct ip_conntrack_tuple inner, target; - int hdrlen = (*pskb)->nh.iph->ihl * 4; - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned long statusbit; - enum ip_nat_manip_type manip = HOOK2MANIP(hooknum); - - if (!skb_make_writable(pskb, hdrlen + sizeof(*inside))) - return 0; - - inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; - - /* We're actually going to mangle it beyond trivial checksum - adjustment, so make sure the current checksum is correct. */ - if (nf_ip_checksum(*pskb, hooknum, hdrlen, 0)) - return 0; - - /* Must be RELATED */ - IP_NF_ASSERT((*pskb)->nfctinfo == IP_CT_RELATED || - (*pskb)->nfctinfo == IP_CT_RELATED+IP_CT_IS_REPLY); - - /* Redirects on non-null nats must be dropped, else they'll - start talking to each other without our translation, and be - confused... --RR */ - if (inside->icmp.type == ICMP_REDIRECT) { - /* If NAT isn't finished, assume it and drop. */ - if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) - return 0; - - if (ct->status & IPS_NAT_MASK) - return 0; - } - - DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n", - *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); - - /* rcu_read_lock()ed by nf_hook_slow */ - proto = __ip_conntrack_proto_find(inside->ip.protocol); - if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 + - sizeof(struct icmphdr) + inside->ip.ihl*4, - &inner, proto)) - return 0; - - /* Change inner back to look like incoming packet. We do the - opposite manip on this hook to normal, because it might not - pass all hooks (locally-generated ICMP). Consider incoming - packet: PREROUTING (DST manip), routing produces ICMP, goes - through POSTROUTING (which must correct the DST manip). */ - if (!manip_pkt(inside->ip.protocol, pskb, - (*pskb)->nh.iph->ihl*4 - + sizeof(inside->icmp), - &ct->tuplehash[!dir].tuple, - !manip)) - return 0; - - if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - /* Reloading "inside" here since manip_pkt inner. */ - inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; - inside->icmp.checksum = 0; - inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen, - (*pskb)->len - hdrlen, - 0)); - } - - /* Change outer to look the reply to an incoming packet - * (proto 0 means don't invert per-proto part). */ - if (manip == IP_NAT_MANIP_SRC) - statusbit = IPS_SRC_NAT; - else - statusbit = IPS_DST_NAT; - - /* Invert if this is reply dir. */ - if (dir == IP_CT_DIR_REPLY) - statusbit ^= IPS_NAT_MASK; - - if (ct->status & statusbit) { - invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); - if (!manip_pkt(0, pskb, 0, &target, manip)) - return 0; - } - - return 1; -} -EXPORT_SYMBOL_GPL(ip_nat_icmp_reply_translation); - -/* Protocol registration. */ -int ip_nat_protocol_register(struct ip_nat_protocol *proto) -{ - int ret = 0; - - write_lock_bh(&ip_nat_lock); - if (ip_nat_protos[proto->protonum] != &ip_nat_unknown_protocol) { - ret = -EBUSY; - goto out; - } - rcu_assign_pointer(ip_nat_protos[proto->protonum], proto); - out: - write_unlock_bh(&ip_nat_lock); - return ret; -} -EXPORT_SYMBOL(ip_nat_protocol_register); - -/* Noone stores the protocol anywhere; simply delete it. */ -void ip_nat_protocol_unregister(struct ip_nat_protocol *proto) -{ - write_lock_bh(&ip_nat_lock); - rcu_assign_pointer(ip_nat_protos[proto->protonum], - &ip_nat_unknown_protocol); - write_unlock_bh(&ip_nat_lock); - synchronize_rcu(); -} -EXPORT_SYMBOL(ip_nat_protocol_unregister); - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -int -ip_nat_port_range_to_nfattr(struct sk_buff *skb, - const struct ip_nat_range *range) -{ - NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(__be16), - &range->min.tcp.port); - NFA_PUT(skb, CTA_PROTONAT_PORT_MAX, sizeof(__be16), - &range->max.tcp.port); - - return 0; - -nfattr_failure: - return -1; -} - -int -ip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range) -{ - int ret = 0; - - /* we have to return whether we actually parsed something or not */ - - if (tb[CTA_PROTONAT_PORT_MIN-1]) { - ret = 1; - range->min.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]); - } - - if (!tb[CTA_PROTONAT_PORT_MAX-1]) { - if (ret) - range->max.tcp.port = range->min.tcp.port; - } else { - ret = 1; - range->max.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ip_nat_port_nfattr_to_range); -EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr); -#endif - -static int __init ip_nat_init(void) -{ - size_t i; - - /* Leave them the same for the moment. */ - ip_nat_htable_size = ip_conntrack_htable_size; - - /* One vmalloc for both hash tables */ - bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size); - if (!bysource) - return -ENOMEM; - - /* Sew in builtin protocols. */ - write_lock_bh(&ip_nat_lock); - for (i = 0; i < MAX_IP_NAT_PROTO; i++) - rcu_assign_pointer(ip_nat_protos[i], &ip_nat_unknown_protocol); - rcu_assign_pointer(ip_nat_protos[IPPROTO_TCP], &ip_nat_protocol_tcp); - rcu_assign_pointer(ip_nat_protos[IPPROTO_UDP], &ip_nat_protocol_udp); - rcu_assign_pointer(ip_nat_protos[IPPROTO_ICMP], &ip_nat_protocol_icmp); - write_unlock_bh(&ip_nat_lock); - - for (i = 0; i < ip_nat_htable_size; i++) { - INIT_LIST_HEAD(&bysource[i]); - } - - /* FIXME: Man, this is a hack. */ - IP_NF_ASSERT(rcu_dereference(ip_conntrack_destroyed) == NULL); - rcu_assign_pointer(ip_conntrack_destroyed, ip_nat_cleanup_conntrack); - - /* Initialize fake conntrack so that NAT will skip it */ - ip_conntrack_untracked.status |= IPS_NAT_DONE_MASK; - return 0; -} - -/* Clear NAT section of all conntracks, in case we're loaded again. */ -static int clean_nat(struct ip_conntrack *i, void *data) -{ - memset(&i->nat, 0, sizeof(i->nat)); - i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST); - return 0; -} - -static void __exit ip_nat_cleanup(void) -{ - ip_ct_iterate_cleanup(&clean_nat, NULL); - rcu_assign_pointer(ip_conntrack_destroyed, NULL); - synchronize_rcu(); - vfree(bysource); -} - -MODULE_LICENSE("GPL"); - -module_init(ip_nat_init); -module_exit(ip_nat_cleanup); diff --git a/net/ipv4/netfilter/ip_nat_ftp.c b/net/ipv4/netfilter/ip_nat_ftp.c deleted file mode 100644 index 32e01d8..0000000 --- a/net/ipv4/netfilter/ip_nat_ftp.c +++ /dev/null @@ -1,180 +0,0 @@ -/* FTP extension for TCP NAT alteration. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Rusty Russell "); -MODULE_DESCRIPTION("ftp NAT helper"); - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -/* FIXME: Time out? --RR */ - -static int -mangle_rfc959_packet(struct sk_buff **pskb, - __be32 newip, - u_int16_t port, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - u32 *seq) -{ - char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")]; - - sprintf(buffer, "%u,%u,%u,%u,%u,%u", - NIPQUAD(newip), port>>8, port&0xFF); - - DEBUGP("calling ip_nat_mangle_tcp_packet\n"); - - *seq += strlen(buffer) - matchlen; - return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, - matchlen, buffer, strlen(buffer)); -} - -/* |1|132.235.1.2|6275| */ -static int -mangle_eprt_packet(struct sk_buff **pskb, - __be32 newip, - u_int16_t port, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - u32 *seq) -{ - char buffer[sizeof("|1|255.255.255.255|65535|")]; - - sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port); - - DEBUGP("calling ip_nat_mangle_tcp_packet\n"); - - *seq += strlen(buffer) - matchlen; - return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, - matchlen, buffer, strlen(buffer)); -} - -/* |1|132.235.1.2|6275| */ -static int -mangle_epsv_packet(struct sk_buff **pskb, - __be32 newip, - u_int16_t port, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - u32 *seq) -{ - char buffer[sizeof("|||65535|")]; - - sprintf(buffer, "|||%u|", port); - - DEBUGP("calling ip_nat_mangle_tcp_packet\n"); - - *seq += strlen(buffer) - matchlen; - return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, - matchlen, buffer, strlen(buffer)); -} - -static int (*mangle[])(struct sk_buff **, __be32, u_int16_t, - unsigned int, - unsigned int, - struct ip_conntrack *, - enum ip_conntrack_info, - u32 *seq) -= { [IP_CT_FTP_PORT] = mangle_rfc959_packet, - [IP_CT_FTP_PASV] = mangle_rfc959_packet, - [IP_CT_FTP_EPRT] = mangle_eprt_packet, - [IP_CT_FTP_EPSV] = mangle_epsv_packet -}; - -/* So, this packet has hit the connection tracking matching code. - Mangle it, and change the expectation to match the new version. */ -static unsigned int ip_nat_ftp(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - enum ip_ct_ftp_type type, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp, - u32 *seq) -{ - __be32 newip; - u_int16_t port; - int dir = CTINFO2DIR(ctinfo); - struct ip_conntrack *ct = exp->master; - - DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); - - /* Connection will come from wherever this packet goes, hence !dir */ - newip = ct->tuplehash[!dir].tuple.dst.ip; - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->dir = !dir; - - /* When you see the packet, we need to NAT it the same as the - * this one. */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { - exp->tuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - if (!mangle[type](pskb, newip, port, matchoff, matchlen, ct, ctinfo, - seq)) { - ip_conntrack_unexpect_related(exp); - return NF_DROP; - } - return NF_ACCEPT; -} - -static void __exit ip_nat_ftp_fini(void) -{ - rcu_assign_pointer(ip_nat_ftp_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_ftp_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_ftp_hook)); - rcu_assign_pointer(ip_nat_ftp_hook, ip_nat_ftp); - return 0; -} - -/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ -static int warn_set(const char *val, struct kernel_param *kp) -{ - printk(KERN_INFO KBUILD_MODNAME - ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); - return 0; -} -module_param_call(ports, warn_set, NULL, NULL, 0); - -module_init(ip_nat_ftp_init); -module_exit(ip_nat_ftp_fini); diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c deleted file mode 100644 index dc778cf..0000000 --- a/net/ipv4/netfilter/ip_nat_helper.c +++ /dev/null @@ -1,436 +0,0 @@ -/* ip_nat_helper.c - generic support functions for NAT helpers - * - * (C) 2000-2002 Harald Welte - * (C) 2003-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 14 Jan 2002 Harald Welte : - * - add support for SACK adjustment - * 14 Mar 2002 Harald Welte : - * - merge SACK support into newnat API - * 16 Aug 2002 Brian J. Murrell : - * - make ip_nat_resize_packet more generic (TCP and UDP) - * - add ip_nat_mangle_udp_packet - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#define DUMP_OFFSET(x) printk("offset_before=%d, offset_after=%d, correction_pos=%u\n", x->offset_before, x->offset_after, x->correction_pos); -#else -#define DEBUGP(format, args...) -#define DUMP_OFFSET(x) -#endif - -static DEFINE_SPINLOCK(ip_nat_seqofs_lock); - -/* Setup TCP sequence correction given this change at this sequence */ -static inline void -adjust_tcp_sequence(u32 seq, - int sizediff, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - int dir; - struct ip_nat_seq *this_way, *other_way; - - DEBUGP("ip_nat_resize_packet: old_size = %u, new_size = %u\n", - (*skb)->len, new_size); - - dir = CTINFO2DIR(ctinfo); - - this_way = &ct->nat.info.seq[dir]; - other_way = &ct->nat.info.seq[!dir]; - - DEBUGP("ip_nat_resize_packet: Seq_offset before: "); - DUMP_OFFSET(this_way); - - spin_lock_bh(&ip_nat_seqofs_lock); - - /* SYN adjust. If it's uninitialized, or this is after last - * correction, record it: we don't handle more than one - * adjustment in the window, but do deal with common case of a - * retransmit */ - if (this_way->offset_before == this_way->offset_after - || before(this_way->correction_pos, seq)) { - this_way->correction_pos = seq; - this_way->offset_before = this_way->offset_after; - this_way->offset_after += sizediff; - } - spin_unlock_bh(&ip_nat_seqofs_lock); - - DEBUGP("ip_nat_resize_packet: Seq_offset after: "); - DUMP_OFFSET(this_way); -} - -/* Frobs data inside this packet, which is linear. */ -static void mangle_contents(struct sk_buff *skb, - unsigned int dataoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - unsigned char *data; - - BUG_ON(skb_is_nonlinear(skb)); - data = (unsigned char *)skb->nh.iph + dataoff; - - /* move post-replacement */ - memmove(data + match_offset + rep_len, - data + match_offset + match_len, - skb->tail - (data + match_offset + match_len)); - - /* insert data from buffer */ - memcpy(data + match_offset, rep_buffer, rep_len); - - /* update skb info */ - if (rep_len > match_len) { - DEBUGP("ip_nat_mangle_packet: Extending packet by " - "%u from %u bytes\n", rep_len - match_len, - skb->len); - skb_put(skb, rep_len - match_len); - } else { - DEBUGP("ip_nat_mangle_packet: Shrinking packet from " - "%u from %u bytes\n", match_len - rep_len, - skb->len); - __skb_trim(skb, skb->len + rep_len - match_len); - } - - /* fix IP hdr checksum information */ - skb->nh.iph->tot_len = htons(skb->len); - ip_send_check(skb->nh.iph); -} - -/* Unusual, but possible case. */ -static int enlarge_skb(struct sk_buff **pskb, unsigned int extra) -{ - struct sk_buff *nskb; - - if ((*pskb)->len + extra > 65535) - return 0; - - nskb = skb_copy_expand(*pskb, skb_headroom(*pskb), extra, GFP_ATOMIC); - if (!nskb) - return 0; - - /* Transfer socket to new skb. */ - if ((*pskb)->sk) - skb_set_owner_w(nskb, (*pskb)->sk); - kfree_skb(*pskb); - *pskb = nskb; - return 1; -} - -/* Generic function for mangling variable-length address changes inside - * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX - * command in FTP). - * - * Takes care about all the nasty sequence number changes, checksumming, - * skb enlargement, ... - * - * */ -int -ip_nat_mangle_tcp_packet(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - struct iphdr *iph; - struct tcphdr *tcph; - int oldlen, datalen; - - if (!skb_make_writable(pskb, (*pskb)->len)) - return 0; - - if (rep_len > match_len - && rep_len - match_len > skb_tailroom(*pskb) - && !enlarge_skb(pskb, rep_len - match_len)) - return 0; - - SKB_LINEAR_ASSERT(*pskb); - - iph = (*pskb)->nh.iph; - tcph = (void *)iph + iph->ihl*4; - - oldlen = (*pskb)->len - iph->ihl*4; - mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4, - match_offset, match_len, rep_buffer, rep_len); - - datalen = (*pskb)->len - iph->ihl*4; - if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - tcph->check = 0; - tcph->check = tcp_v4_check(datalen, - iph->saddr, iph->daddr, - csum_partial((char *)tcph, - datalen, 0)); - } else - nf_proto_csum_replace2(&tcph->check, *pskb, - htons(oldlen), htons(datalen), 1); - - if (rep_len != match_len) { - set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); - adjust_tcp_sequence(ntohl(tcph->seq), - (int)rep_len - (int)match_len, - ct, ctinfo); - /* Tell TCP window tracking about seq change */ - ip_conntrack_tcp_update(*pskb, ct, CTINFO2DIR(ctinfo)); - } - return 1; -} -EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); - -/* Generic function for mangling variable-length address changes inside - * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX - * command in the Amanda protocol) - * - * Takes care about all the nasty sequence number changes, checksumming, - * skb enlargement, ... - * - * XXX - This function could be merged with ip_nat_mangle_tcp_packet which - * should be fairly easy to do. - */ -int -ip_nat_mangle_udp_packet(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - struct iphdr *iph; - struct udphdr *udph; - int datalen, oldlen; - - /* UDP helpers might accidentally mangle the wrong packet */ - iph = (*pskb)->nh.iph; - if ((*pskb)->len < iph->ihl*4 + sizeof(*udph) + - match_offset + match_len) - return 0; - - if (!skb_make_writable(pskb, (*pskb)->len)) - return 0; - - if (rep_len > match_len - && rep_len - match_len > skb_tailroom(*pskb) - && !enlarge_skb(pskb, rep_len - match_len)) - return 0; - - iph = (*pskb)->nh.iph; - udph = (void *)iph + iph->ihl*4; - - oldlen = (*pskb)->len - iph->ihl*4; - mangle_contents(*pskb, iph->ihl*4 + sizeof(*udph), - match_offset, match_len, rep_buffer, rep_len); - - /* update the length of the UDP packet */ - datalen = (*pskb)->len - iph->ihl*4; - udph->len = htons(datalen); - - /* fix udp checksum if udp checksum was previously calculated */ - if (!udph->check && (*pskb)->ip_summed != CHECKSUM_PARTIAL) - return 1; - - if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - udph->check = 0; - udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, - datalen, IPPROTO_UDP, - csum_partial((char *)udph, - datalen, 0)); - if (!udph->check) - udph->check = CSUM_MANGLED_0; - } else - nf_proto_csum_replace2(&udph->check, *pskb, - htons(oldlen), htons(datalen), 1); - return 1; -} -EXPORT_SYMBOL(ip_nat_mangle_udp_packet); - -/* Adjust one found SACK option including checksum correction */ -static void -sack_adjust(struct sk_buff *skb, - struct tcphdr *tcph, - unsigned int sackoff, - unsigned int sackend, - struct ip_nat_seq *natseq) -{ - while (sackoff < sackend) { - struct tcp_sack_block_wire *sack; - __be32 new_start_seq, new_end_seq; - - sack = (void *)skb->data + sackoff; - if (after(ntohl(sack->start_seq) - natseq->offset_before, - natseq->correction_pos)) - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_after); - else - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_before); - - if (after(ntohl(sack->end_seq) - natseq->offset_before, - natseq->correction_pos)) - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_after); - else - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_before); - - DEBUGP("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", - ntohl(sack->start_seq), new_start_seq, - ntohl(sack->end_seq), new_end_seq); - - nf_proto_csum_replace4(&tcph->check, skb, - sack->start_seq, new_start_seq, 0); - nf_proto_csum_replace4(&tcph->check, skb, - sack->end_seq, new_end_seq, 0); - sack->start_seq = new_start_seq; - sack->end_seq = new_end_seq; - sackoff += sizeof(*sack); - } -} - -/* TCP SACK sequence number adjustment */ -static inline unsigned int -ip_nat_sack_adjust(struct sk_buff **pskb, - struct tcphdr *tcph, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dir, optoff, optend; - - optoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr); - optend = (*pskb)->nh.iph->ihl*4 + tcph->doff*4; - - if (!skb_make_writable(pskb, optend)) - return 0; - - dir = CTINFO2DIR(ctinfo); - - while (optoff < optend) { - /* Usually: option, length. */ - unsigned char *op = (*pskb)->data + optoff; - - switch (op[0]) { - case TCPOPT_EOL: - return 1; - case TCPOPT_NOP: - optoff++; - continue; - default: - /* no partial options */ - if (optoff + 1 == optend - || optoff + op[1] > optend - || op[1] < 2) - return 0; - if (op[0] == TCPOPT_SACK - && op[1] >= 2+TCPOLEN_SACK_PERBLOCK - && ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) - sack_adjust(*pskb, tcph, optoff+2, - optoff+op[1], - &ct->nat.info.seq[!dir]); - optoff += op[1]; - } - } - return 1; -} - -/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ -int -ip_nat_seq_adjust(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct tcphdr *tcph; - int dir; - __be32 newseq, newack; - struct ip_nat_seq *this_way, *other_way; - - dir = CTINFO2DIR(ctinfo); - - this_way = &ct->nat.info.seq[dir]; - other_way = &ct->nat.info.seq[!dir]; - - if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph))) - return 0; - - tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; - if (after(ntohl(tcph->seq), this_way->correction_pos)) - newseq = htonl(ntohl(tcph->seq) + this_way->offset_after); - else - newseq = htonl(ntohl(tcph->seq) + this_way->offset_before); - - if (after(ntohl(tcph->ack_seq) - other_way->offset_before, - other_way->correction_pos)) - newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after); - else - newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before); - - nf_proto_csum_replace4(&tcph->check, *pskb, tcph->seq, newseq, 0); - nf_proto_csum_replace4(&tcph->check, *pskb, tcph->ack_seq, newack, 0); - - DEBUGP("Adjusting sequence number from %u->%u, ack from %u->%u\n", - ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), - ntohl(newack)); - - tcph->seq = newseq; - tcph->ack_seq = newack; - - if (!ip_nat_sack_adjust(pskb, tcph, ct, ctinfo)) - return 0; - - ip_conntrack_tcp_update(*pskb, ct, dir); - - return 1; -} -EXPORT_SYMBOL(ip_nat_seq_adjust); - -/* Setup NAT on this expected conntrack so it follows master. */ -/* If we fail to get a free NAT slot, we'll get dropped on confirm */ -void ip_nat_follow_master(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp) -{ - struct ip_nat_range range; - - /* This must be a fresh one. */ - BUG_ON(ct->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.dst.ip; - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = exp->saved_proto; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.src.ip; - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); -} -EXPORT_SYMBOL(ip_nat_follow_master); diff --git a/net/ipv4/netfilter/ip_nat_helper_h323.c b/net/ipv4/netfilter/ip_nat_helper_h323.c deleted file mode 100644 index bdc99ef..0000000 --- a/net/ipv4/netfilter/ip_nat_helper_h323.c +++ /dev/null @@ -1,611 +0,0 @@ -/* - * H.323 extension for NAT alteration. - * - * Copyright (c) 2006 Jing Min Zhao - * - * This source code is licensed under General Public License version 2. - * - * Based on the 'brute force' H.323 NAT module by - * Jozsef Kadlecsik - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -/****************************************************************************/ -static int set_addr(struct sk_buff **pskb, - unsigned char **data, int dataoff, - unsigned int addroff, __be32 ip, u_int16_t port) -{ - enum ip_conntrack_info ctinfo; - struct ip_conntrack *ct = ip_conntrack_get(*pskb, &ctinfo); - struct { - __be32 ip; - __be16 port; - } __attribute__ ((__packed__)) buf; - struct tcphdr _tcph, *th; - - buf.ip = ip; - buf.port = htons(port); - addroff += dataoff; - - if ((*pskb)->nh.iph->protocol == IPPROTO_TCP) { - if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - addroff, sizeof(buf), - (char *) &buf, sizeof(buf))) { - if (net_ratelimit()) - printk("ip_nat_h323: ip_nat_mangle_tcp_packet" - " error\n"); - return -1; - } - - /* Relocate data pointer */ - th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4, - sizeof(_tcph), &_tcph); - if (th == NULL) - return -1; - *data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 + - th->doff * 4 + dataoff; - } else { - if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - addroff, sizeof(buf), - (char *) &buf, sizeof(buf))) { - if (net_ratelimit()) - printk("ip_nat_h323: ip_nat_mangle_udp_packet" - " error\n"); - return -1; - } - /* ip_nat_mangle_udp_packet uses skb_make_writable() to copy - * or pull everything in a linear buffer, so we can safely - * use the skb pointers now */ - *data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 + - sizeof(struct udphdr); - } - - return 0; -} - -/****************************************************************************/ -static int set_h225_addr(struct sk_buff **pskb, - unsigned char **data, int dataoff, - TransportAddress * addr, - __be32 ip, u_int16_t port) -{ - return set_addr(pskb, data, dataoff, addr->ipAddress.ip, ip, port); -} - -/****************************************************************************/ -static int set_h245_addr(struct sk_buff **pskb, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - __be32 ip, u_int16_t port) -{ - return set_addr(pskb, data, dataoff, - addr->unicastAddress.iPAddress.network, ip, port); -} - -/****************************************************************************/ -static int set_sig_addr(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int i; - __be32 ip; - u_int16_t port; - - for (i = 0; i < count; i++) { - if (get_h225_addr(*data, &addr[i], &ip, &port)) { - if (ip == ct->tuplehash[dir].tuple.src.ip && - port == info->sig_port[dir]) { - /* GW->GK */ - - /* Fix for Gnomemeeting */ - if (i > 0 && - get_h225_addr(*data, &addr[0], - &ip, &port) && - (ntohl(ip) & 0xff000000) == 0x7f000000) - i = 0; - - DEBUGP - ("ip_nat_ras: set signal address " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.dst. - ip), info->sig_port[!dir]); - return set_h225_addr(pskb, data, 0, &addr[i], - ct->tuplehash[!dir]. - tuple.dst.ip, - info->sig_port[!dir]); - } else if (ip == ct->tuplehash[dir].tuple.dst.ip && - port == info->sig_port[dir]) { - /* GK->GW */ - DEBUGP - ("ip_nat_ras: set signal address " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.src. - ip), info->sig_port[!dir]); - return set_h225_addr(pskb, data, 0, &addr[i], - ct->tuplehash[!dir]. - tuple.src.ip, - info->sig_port[!dir]); - } - } - } - - return 0; -} - -/****************************************************************************/ -static int set_ras_addr(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count) -{ - int dir = CTINFO2DIR(ctinfo); - int i; - __be32 ip; - u_int16_t port; - - for (i = 0; i < count; i++) { - if (get_h225_addr(*data, &addr[i], &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && - port == ntohs(ct->tuplehash[dir].tuple.src.u.udp.port)) { - DEBUGP("ip_nat_ras: set rasAddress " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.dst.ip), - ntohs(ct->tuplehash[!dir].tuple.dst.u.udp. - port)); - return set_h225_addr(pskb, data, 0, &addr[i], - ct->tuplehash[!dir].tuple.dst.ip, - ntohs(ct->tuplehash[!dir].tuple. - dst.u.udp.port)); - } - } - - return 0; -} - -/****************************************************************************/ -static int nat_rtp_rtcp(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - u_int16_t port, u_int16_t rtp_port, - struct ip_conntrack_expect *rtp_exp, - struct ip_conntrack_expect *rtcp_exp) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int i; - u_int16_t nated_port; - - /* Set expectations for NAT */ - rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; - rtp_exp->expectfn = ip_nat_follow_master; - rtp_exp->dir = !dir; - rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; - rtcp_exp->expectfn = ip_nat_follow_master; - rtcp_exp->dir = !dir; - - /* Lookup existing expects */ - for (i = 0; i < H323_RTP_CHANNEL_MAX; i++) { - if (info->rtp_port[i][dir] == rtp_port) { - /* Expected */ - - /* Use allocated ports first. This will refresh - * the expects */ - rtp_exp->tuple.dst.u.udp.port = - htons(info->rtp_port[i][dir]); - rtcp_exp->tuple.dst.u.udp.port = - htons(info->rtp_port[i][dir] + 1); - break; - } else if (info->rtp_port[i][dir] == 0) { - /* Not expected */ - break; - } - } - - /* Run out of expectations */ - if (i >= H323_RTP_CHANNEL_MAX) { - if (net_ratelimit()) - printk("ip_nat_h323: out of expectations\n"); - return 0; - } - - /* Try to get a pair of ports. */ - for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port); - nated_port != 0; nated_port += 2) { - rtp_exp->tuple.dst.u.udp.port = htons(nated_port); - if (ip_conntrack_expect_related(rtp_exp) == 0) { - rtcp_exp->tuple.dst.u.udp.port = - htons(nated_port + 1); - if (ip_conntrack_expect_related(rtcp_exp) == 0) - break; - ip_conntrack_unexpect_related(rtp_exp); - } - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_h323: out of RTP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h245_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, - (port & 1) ? nated_port + 1 : nated_port) == 0) { - /* Save ports */ - info->rtp_port[i][dir] = rtp_port; - info->rtp_port[i][!dir] = nated_port; - } else { - ip_conntrack_unexpect_related(rtp_exp); - ip_conntrack_unexpect_related(rtcp_exp); - return -1; - } - - /* Success */ - DEBUGP("ip_nat_h323: expect RTP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtp_exp->tuple.src.ip), - ntohs(rtp_exp->tuple.src.u.udp.port), - NIPQUAD(rtp_exp->tuple.dst.ip), - ntohs(rtp_exp->tuple.dst.u.udp.port)); - DEBUGP("ip_nat_h323: expect RTCP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtcp_exp->tuple.src.ip), - ntohs(rtcp_exp->tuple.src.u.udp.port), - NIPQUAD(rtcp_exp->tuple.dst.ip), - ntohs(rtcp_exp->tuple.dst.u.udp.port)); - - return 0; -} - -/****************************************************************************/ -static int nat_t120(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect *exp) -{ - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port = port; - - /* Set expectations for NAT */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_follow_master; - exp->dir = !dir; - - /* Try to get same port: if not, try to change it. */ - for (; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_h323: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h245_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, nated_port) < 0) { - ip_conntrack_unexpect_related(exp); - return -1; - } - - DEBUGP("ip_nat_h323: expect T.120 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/**************************************************************************** - * This conntrack expect function replaces ip_conntrack_h245_expect() - * which was set by ip_conntrack_helper_h323.c. It calls both - * ip_nat_follow_master() and ip_conntrack_h245_expect() - ****************************************************************************/ -static void ip_nat_h245_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - ip_nat_follow_master(new, this); - ip_conntrack_h245_expect(new, this); -} - -/****************************************************************************/ -static int nat_h245(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect *exp) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port = port; - - /* Set expectations for NAT */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_h245_expect; - exp->dir = !dir; - - /* Check existing expects */ - if (info->sig_port[dir] == port) - nated_port = info->sig_port[!dir]; - - /* Try to get same port: if not, try to change it. */ - for (; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_q931: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h225_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, - nated_port) == 0) { - /* Save ports */ - info->sig_port[dir] = port; - info->sig_port[!dir] = nated_port; - } else { - ip_conntrack_unexpect_related(exp); - return -1; - } - - DEBUGP("ip_nat_q931: expect H.245 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/**************************************************************************** - * This conntrack expect function replaces ip_conntrack_q931_expect() - * which was set by ip_conntrack_helper_h323.c. - ****************************************************************************/ -static void ip_nat_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - struct ip_nat_range range; - - if (this->tuple.src.ip != 0) { /* Only accept calls from GK */ - ip_nat_follow_master(new, this); - goto out; - } - - /* This must be a fresh one. */ - BUG_ON(new->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip; - - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = this->saved_proto; - range.min_ip = range.max_ip = - new->master->tuplehash[!this->dir].tuple.src.ip; - - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING); - - out: - ip_conntrack_q931_expect(new, this); -} - -/****************************************************************************/ -static int nat_q931(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, TransportAddress * addr, int idx, - u_int16_t port, struct ip_conntrack_expect *exp) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port = port; - __be32 ip; - - /* Set expectations for NAT */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_q931_expect; - exp->dir = !dir; - - /* Check existing expects */ - if (info->sig_port[dir] == port) - nated_port = info->sig_port[!dir]; - - /* Try to get same port: if not, try to change it. */ - for (; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_ras: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h225_addr(pskb, data, 0, &addr[idx], - ct->tuplehash[!dir].tuple.dst.ip, - nated_port) == 0) { - /* Save ports */ - info->sig_port[dir] = port; - info->sig_port[!dir] = nated_port; - - /* Fix for Gnomemeeting */ - if (idx > 0 && - get_h225_addr(*data, &addr[0], &ip, &port) && - (ntohl(ip) & 0xff000000) == 0x7f000000) { - set_h225_addr_hook(pskb, data, 0, &addr[0], - ct->tuplehash[!dir].tuple.dst.ip, - info->sig_port[!dir]); - } - } else { - ip_conntrack_unexpect_related(exp); - return -1; - } - - /* Success */ - DEBUGP("ip_nat_ras: expect Q.931 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/****************************************************************************/ -static void ip_nat_callforwarding_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - struct ip_nat_range range; - - /* This must be a fresh one. */ - BUG_ON(new->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip; - - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = this->saved_proto; - range.min_ip = range.max_ip = this->saved_ip; - - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING); - - ip_conntrack_q931_expect(new, this); -} - -/****************************************************************************/ -static int nat_callforwarding(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect *exp) -{ - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port; - - /* Set expectations for NAT */ - exp->saved_ip = exp->tuple.dst.ip; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_callforwarding_expect; - exp->dir = !dir; - - /* Try to get same port: if not, try to change it. */ - for (nated_port = port; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_q931: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (!set_h225_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, - nated_port) == 0) { - ip_conntrack_unexpect_related(exp); - return -1; - } - - /* Success */ - DEBUGP("ip_nat_q931: expect Call Forwarding " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/****************************************************************************/ -static int __init init(void) -{ - BUG_ON(rcu_dereference(set_h245_addr_hook) != NULL); - BUG_ON(rcu_dereference(set_h225_addr_hook) != NULL); - BUG_ON(rcu_dereference(set_sig_addr_hook) != NULL); - BUG_ON(rcu_dereference(set_ras_addr_hook) != NULL); - BUG_ON(rcu_dereference(nat_rtp_rtcp_hook) != NULL); - BUG_ON(rcu_dereference(nat_t120_hook) != NULL); - BUG_ON(rcu_dereference(nat_h245_hook) != NULL); - BUG_ON(rcu_dereference(nat_callforwarding_hook) != NULL); - BUG_ON(rcu_dereference(nat_q931_hook) != NULL); - - rcu_assign_pointer(set_h245_addr_hook, set_h245_addr); - rcu_assign_pointer(set_h225_addr_hook, set_h225_addr); - rcu_assign_pointer(set_sig_addr_hook, set_sig_addr); - rcu_assign_pointer(set_ras_addr_hook, set_ras_addr); - rcu_assign_pointer(nat_rtp_rtcp_hook, nat_rtp_rtcp); - rcu_assign_pointer(nat_t120_hook, nat_t120); - rcu_assign_pointer(nat_h245_hook, nat_h245); - rcu_assign_pointer(nat_callforwarding_hook, nat_callforwarding); - rcu_assign_pointer(nat_q931_hook, nat_q931); - - DEBUGP("ip_nat_h323: init success\n"); - return 0; -} - -/****************************************************************************/ -static void __exit fini(void) -{ - rcu_assign_pointer(set_h245_addr_hook, NULL); - rcu_assign_pointer(set_h225_addr_hook, NULL); - rcu_assign_pointer(set_sig_addr_hook, NULL); - rcu_assign_pointer(set_ras_addr_hook, NULL); - rcu_assign_pointer(nat_rtp_rtcp_hook, NULL); - rcu_assign_pointer(nat_t120_hook, NULL); - rcu_assign_pointer(nat_h245_hook, NULL); - rcu_assign_pointer(nat_callforwarding_hook, NULL); - rcu_assign_pointer(nat_q931_hook, NULL); - synchronize_rcu(); -} - -/****************************************************************************/ -module_init(init); -module_exit(fini); - -MODULE_AUTHOR("Jing Min Zhao "); -MODULE_DESCRIPTION("H.323 NAT helper"); -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/ip_nat_helper_pptp.c b/net/ipv4/netfilter/ip_nat_helper_pptp.c deleted file mode 100644 index 24ce4a5..0000000 --- a/net/ipv4/netfilter/ip_nat_helper_pptp.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * ip_nat_pptp.c - Version 3.0 - * - * NAT support for PPTP (Point to Point Tunneling Protocol). - * PPTP is a a protocol for creating virtual private networks. - * It is a specification defined by Microsoft and some vendors - * working with Microsoft. PPTP is built on top of a modified - * version of the Internet Generic Routing Encapsulation Protocol. - * GRE is defined in RFC 1701 and RFC 1702. Documentation of - * PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - * TODO: - NAT to a unique tuple, not to TCP source port - * (needs netfilter tuple reservation) - * - * Changes: - * 2002-02-10 - Version 1.3 - * - Use ip_nat_mangle_tcp_packet() because of cloned skb's - * in local connections (Philip Craig ) - * - add checks for magicCookie and pptp version - * - make argument list of pptp_{out,in}bound_packet() shorter - * - move to C99 style initializers - * - print version number at module loadtime - * 2003-09-22 - Version 1.5 - * - use SNATed tcp sourceport as callid, since we get called before - * TCP header is mangled (Philip Craig ) - * 2004-10-22 - Version 2.0 - * - kernel 2.6.x version - * 2005-06-10 - Version 3.0 - * - kernel >= 2.6.11 version, - * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/) - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IP_NAT_PPTP_VERSION "3.0" - -#define REQ_CID(req, off) (*(__be16 *)((char *)(req) + (off))) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); - - -#if 0 -extern const char *pptp_msg_name[]; -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ - __FUNCTION__, ## args) -#else -#define DEBUGP(format, args...) -#endif - -static void pptp_nat_expected(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp) -{ - struct ip_conntrack *master = ct->master; - struct ip_conntrack_expect *other_exp; - struct ip_conntrack_tuple t; - struct ip_ct_pptp_master *ct_pptp_info; - struct ip_nat_pptp *nat_pptp_info; - struct ip_nat_range range; - - ct_pptp_info = &master->help.ct_pptp_info; - nat_pptp_info = &master->nat.help.nat_pptp_info; - - /* And here goes the grand finale of corrosion... */ - - if (exp->dir == IP_CT_DIR_ORIGINAL) { - DEBUGP("we are PNS->PAC\n"); - /* therefore, build tuple for PAC->PNS */ - t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - t.src.u.gre.key = master->help.ct_pptp_info.pac_call_id; - t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; - t.dst.u.gre.key = master->help.ct_pptp_info.pns_call_id; - t.dst.protonum = IPPROTO_GRE; - } else { - DEBUGP("we are PAC->PNS\n"); - /* build tuple for PNS->PAC */ - t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - t.src.u.gre.key = master->nat.help.nat_pptp_info.pns_call_id; - t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; - t.dst.u.gre.key = master->nat.help.nat_pptp_info.pac_call_id; - t.dst.protonum = IPPROTO_GRE; - } - - DEBUGP("trying to unexpect other dir: "); - DUMP_TUPLE(&t); - other_exp = ip_conntrack_expect_find_get(&t); - if (other_exp) { - ip_conntrack_unexpect_related(other_exp); - ip_conntrack_expect_put(other_exp); - DEBUGP("success\n"); - } else { - DEBUGP("not found!\n"); - } - - /* This must be a fresh one. */ - BUG_ON(ct->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.dst.ip; - if (exp->dir == IP_CT_DIR_ORIGINAL) { - range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; - range.min = range.max = exp->saved_proto; - } - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.src.ip; - if (exp->dir == IP_CT_DIR_REPLY) { - range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; - range.min = range.max = exp->saved_proto; - } - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); -} - -/* outbound packets == from PNS to PAC */ -static int -pptp_outbound_pkt(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq) - -{ - struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; - struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; - u_int16_t msg; - __be16 new_callid; - unsigned int cid_off; - - new_callid = ct_pptp_info->pns_call_id; - - switch (msg = ntohs(ctlh->messageType)) { - case PPTP_OUT_CALL_REQUEST: - cid_off = offsetof(union pptp_ctrl_union, ocreq.callID); - /* FIXME: ideally we would want to reserve a call ID - * here. current netfilter NAT core is not able to do - * this :( For now we use TCP source port. This breaks - * multiple calls within one control session */ - - /* save original call ID in nat_info */ - nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; - - /* don't use tcph->source since we are at a DSTmanip - * hook (e.g. PREROUTING) and pkt is not mangled yet */ - new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; - - /* save new call ID in ct info */ - ct_pptp_info->pns_call_id = new_callid; - break; - case PPTP_IN_CALL_REPLY: - cid_off = offsetof(union pptp_ctrl_union, icack.callID); - break; - case PPTP_CALL_CLEAR_REQUEST: - cid_off = offsetof(union pptp_ctrl_union, clrreq.callID); - break; - default: - DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, - (msg <= PPTP_MSG_MAX)? - pptp_msg_name[msg]:pptp_msg_name[0]); - /* fall through */ - - case PPTP_SET_LINK_INFO: - /* only need to NAT in case PAC is behind NAT box */ - case PPTP_START_SESSION_REQUEST: - case PPTP_START_SESSION_REPLY: - case PPTP_STOP_SESSION_REQUEST: - case PPTP_STOP_SESSION_REPLY: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* no need to alter packet */ - return NF_ACCEPT; - } - - /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass - * down to here */ - DEBUGP("altering call id from 0x%04x to 0x%04x\n", - ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid)); - - /* mangle packet */ - if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - cid_off + sizeof(struct pptp_pkt_hdr) + - sizeof(struct PptpControlHeader), - sizeof(new_callid), (char *)&new_callid, - sizeof(new_callid)) == 0) - return NF_DROP; - - return NF_ACCEPT; -} - -static void -pptp_exp_gre(struct ip_conntrack_expect *expect_orig, - struct ip_conntrack_expect *expect_reply) -{ - struct ip_conntrack *ct = expect_orig->master; - struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; - struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; - - /* save original PAC call ID in nat_info */ - nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; - - /* alter expectation for PNS->PAC direction */ - expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id; - expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id; - expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id; - expect_orig->dir = IP_CT_DIR_ORIGINAL; - - /* alter expectation for PAC->PNS direction */ - expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id; - expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id; - expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id; - expect_reply->dir = IP_CT_DIR_REPLY; -} - -/* inbound packets == from PAC to PNS */ -static int -pptp_inbound_pkt(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq) -{ - struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; - u_int16_t msg; - __be16 new_pcid; - unsigned int pcid_off; - - new_pcid = nat_pptp_info->pns_call_id; - - switch (msg = ntohs(ctlh->messageType)) { - case PPTP_OUT_CALL_REPLY: - pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID); - break; - case PPTP_IN_CALL_CONNECT: - pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID); - break; - case PPTP_IN_CALL_REQUEST: - /* only need to nat in case PAC is behind NAT box */ - return NF_ACCEPT; - case PPTP_WAN_ERROR_NOTIFY: - pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID); - break; - case PPTP_CALL_DISCONNECT_NOTIFY: - pcid_off = offsetof(union pptp_ctrl_union, disc.callID); - break; - case PPTP_SET_LINK_INFO: - pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID); - break; - - default: - DEBUGP("unknown inbound packet %s\n", (msg <= PPTP_MSG_MAX)? - pptp_msg_name[msg]:pptp_msg_name[0]); - /* fall through */ - - case PPTP_START_SESSION_REQUEST: - case PPTP_START_SESSION_REPLY: - case PPTP_STOP_SESSION_REQUEST: - case PPTP_STOP_SESSION_REPLY: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* no need to alter packet */ - return NF_ACCEPT; - } - - /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST, - * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */ - - /* mangle packet */ - DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", - ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid)); - - if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - pcid_off + sizeof(struct pptp_pkt_hdr) + - sizeof(struct PptpControlHeader), - sizeof(new_pcid), (char *)&new_pcid, - sizeof(new_pcid)) == 0) - return NF_DROP; - return NF_ACCEPT; -} - - -extern int __init ip_nat_proto_gre_init(void); -extern void __exit ip_nat_proto_gre_fini(void); - -static int __init ip_nat_helper_pptp_init(void) -{ - int ret; - - DEBUGP("%s: registering NAT helper\n", __FILE__); - - ret = ip_nat_proto_gre_init(); - if (ret < 0) - return ret; - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_outbound)); - rcu_assign_pointer(ip_nat_pptp_hook_outbound, pptp_outbound_pkt); - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_inbound)); - rcu_assign_pointer(ip_nat_pptp_hook_inbound, pptp_inbound_pkt); - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_exp_gre)); - rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, pptp_exp_gre); - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_expectfn)); - rcu_assign_pointer(ip_nat_pptp_hook_expectfn, pptp_nat_expected); - - printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION); - return 0; -} - -static void __exit ip_nat_helper_pptp_fini(void) -{ - DEBUGP("cleanup_module\n" ); - - rcu_assign_pointer(ip_nat_pptp_hook_expectfn, NULL); - rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, NULL); - rcu_assign_pointer(ip_nat_pptp_hook_inbound, NULL); - rcu_assign_pointer(ip_nat_pptp_hook_outbound, NULL); - synchronize_rcu(); - - ip_nat_proto_gre_fini(); - - printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION); -} - -module_init(ip_nat_helper_pptp_init); -module_exit(ip_nat_helper_pptp_fini); diff --git a/net/ipv4/netfilter/ip_nat_irc.c b/net/ipv4/netfilter/ip_nat_irc.c deleted file mode 100644 index cfaeea3..0000000 --- a/net/ipv4/netfilter/ip_nat_irc.c +++ /dev/null @@ -1,122 +0,0 @@ -/* IRC extension for TCP NAT alteration. - * (C) 2000-2001 by Harald Welte - * (C) 2004 Rusty Russell IBM Corporation - * based on a copy of RR's ip_nat_ftp.c - * - * ip_nat_irc.c,v 1.16 2001/12/06 07:42:10 laforge Exp - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("IRC (DCC) NAT helper"); -MODULE_LICENSE("GPL"); - -static unsigned int help(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp) -{ - u_int16_t port; - unsigned int ret; - - /* "4294967296 65635 " */ - char buffer[18]; - - DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n", - expect->seq, exp_irc_info->len, - ntohl(tcph->seq)); - - /* Reply comes from server. */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->dir = IP_CT_DIR_REPLY; - - /* When you see the packet, we need to NAT it the same as the - * this one. */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { - exp->tuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - /* strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27 - * strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28 - * strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26 - * strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26 - * strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27 - * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits, - * 255.255.255.255==4294967296, 10 digits) - * P: bound port (min 1 d, max 5d (65635)) - * F: filename (min 1 d ) - * S: size (min 1 d ) - * 0x01, \n: terminators - */ - - /* AAA = "us", ie. where server normally talks to. */ - sprintf(buffer, "%u %u", - ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), - port); - DEBUGP("ip_nat_irc: Inserting '%s' == %u.%u.%u.%u, port %u\n", - buffer, NIPQUAD(exp->tuple.src.ip), port); - - ret = ip_nat_mangle_tcp_packet(pskb, exp->master, ctinfo, - matchoff, matchlen, buffer, - strlen(buffer)); - if (ret != NF_ACCEPT) - ip_conntrack_unexpect_related(exp); - return ret; -} - -static void __exit ip_nat_irc_fini(void) -{ - rcu_assign_pointer(ip_nat_irc_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_irc_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_irc_hook)); - rcu_assign_pointer(ip_nat_irc_hook, help); - return 0; -} - -/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ -static int warn_set(const char *val, struct kernel_param *kp) -{ - printk(KERN_INFO KBUILD_MODNAME - ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); - return 0; -} -module_param_call(ports, warn_set, NULL, NULL, 0); - -module_init(ip_nat_irc_init); -module_exit(ip_nat_irc_fini); diff --git a/net/ipv4/netfilter/ip_nat_proto_gre.c b/net/ipv4/netfilter/ip_nat_proto_gre.c deleted file mode 100644 index 9581020..0000000 --- a/net/ipv4/netfilter/ip_nat_proto_gre.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * ip_nat_proto_gre.c - Version 2.0 - * - * NAT protocol helper module for GRE. - * - * GRE is a generic encapsulation protocol, which is generally not very - * suited for NAT, as it has no protocol-specific part as port numbers. - * - * It has an optional key field, which may help us distinguishing two - * connections between the same two hosts. - * - * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 - * - * PPTP is built on top of a modified version of GRE, and has a mandatory - * field called "CallID", which serves us for the same purpose as the key - * field in plain GRE. - * - * Documentation about PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - */ - -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); - -#if 0 -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ - __FUNCTION__, ## args) -#else -#define DEBUGP(x, args...) -#endif - -/* is key in given range between min and max */ -static int -gre_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - __be16 key; - - if (maniptype == IP_NAT_MANIP_SRC) - key = tuple->src.u.gre.key; - else - key = tuple->dst.u.gre.key; - - return ntohs(key) >= ntohs(min->gre.key) - && ntohs(key) <= ntohs(max->gre.key); -} - -/* generate unique tuple ... */ -static int -gre_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t key; - __be16 *keyptr; - unsigned int min, i, range_size; - - if (maniptype == IP_NAT_MANIP_SRC) - keyptr = &tuple->src.u.gre.key; - else - keyptr = &tuple->dst.u.gre.key; - - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { - DEBUGP("%p: NATing GRE PPTP\n", conntrack); - min = 1; - range_size = 0xffff; - } else { - min = ntohs(range->min.gre.key); - range_size = ntohs(range->max.gre.key) - min + 1; - } - - DEBUGP("min = %u, range_size = %u\n", min, range_size); - - for (i = 0; i < range_size; i++, key++) { - *keyptr = htons(min + key % range_size); - if (!ip_nat_used_tuple(tuple, conntrack)) - return 1; - } - - DEBUGP("%p: no NAT mapping\n", conntrack); - - return 0; -} - -/* manipulate a GRE packet according to maniptype */ -static int -gre_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct gre_hdr *greh; - struct gre_hdr_pptp *pgreh; - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - unsigned int hdroff = iphdroff + iph->ihl*4; - - /* pgreh includes two optional 32bit fields which are not required - * to be there. That's where the magic '8' comes from */ - if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh)-8)) - return 0; - - greh = (void *)(*pskb)->data + hdroff; - pgreh = (struct gre_hdr_pptp *) greh; - - /* we only have destination manip of a packet, since 'source key' - * is not present in the packet itself */ - if (maniptype == IP_NAT_MANIP_DST) { - /* key manipulation is always dest */ - switch (greh->version) { - case 0: - if (!greh->key) { - DEBUGP("can't nat GRE w/o key\n"); - break; - } - if (greh->csum) { - /* FIXME: Never tested this code... */ - nf_proto_csum_replace4(gre_csum(greh), *pskb, - *(gre_key(greh)), - tuple->dst.u.gre.key, 0); - } - *(gre_key(greh)) = tuple->dst.u.gre.key; - break; - case GRE_VERSION_PPTP: - DEBUGP("call_id -> 0x%04x\n", - ntohs(tuple->dst.u.gre.key)); - pgreh->call_id = tuple->dst.u.gre.key; - break; - default: - DEBUGP("can't nat unknown GRE version\n"); - return 0; - break; - } - } - return 1; -} - -/* nat helper struct */ -static struct ip_nat_protocol gre = { - .name = "GRE", - .protonum = IPPROTO_GRE, - .manip_pkt = gre_manip_pkt, - .in_range = gre_in_range, - .unique_tuple = gre_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; - -int __init ip_nat_proto_gre_init(void) -{ - return ip_nat_protocol_register(&gre); -} - -void __exit ip_nat_proto_gre_fini(void) -{ - ip_nat_protocol_unregister(&gre); -} diff --git a/net/ipv4/netfilter/ip_nat_proto_icmp.c b/net/ipv4/netfilter/ip_nat_proto_icmp.c deleted file mode 100644 index 22a528a..0000000 --- a/net/ipv4/netfilter/ip_nat_proto_icmp.c +++ /dev/null @@ -1,87 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static int -icmp_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - return ntohs(tuple->src.u.icmp.id) >= ntohs(min->icmp.id) && - ntohs(tuple->src.u.icmp.id) <= ntohs(max->icmp.id); -} - -static int -icmp_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t id; - unsigned int range_size; - unsigned int i; - - range_size = ntohs(range->max.icmp.id) - ntohs(range->min.icmp.id) + 1; - /* If no range specified... */ - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) - range_size = 0xFFFF; - - for (i = 0; i < range_size; i++, id++) { - tuple->src.u.icmp.id = htons(ntohs(range->min.icmp.id) + - (id % range_size)); - if (!ip_nat_used_tuple(tuple, conntrack)) - return 1; - } - return 0; -} - -static int -icmp_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - struct icmphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - - if (!skb_make_writable(pskb, hdroff + sizeof(*hdr))) - return 0; - - hdr = (struct icmphdr *)((*pskb)->data + hdroff); - nf_proto_csum_replace2(&hdr->checksum, *pskb, - hdr->un.echo.id, tuple->src.u.icmp.id, 0); - hdr->un.echo.id = tuple->src.u.icmp.id; - return 1; -} - -struct ip_nat_protocol ip_nat_protocol_icmp = { - .name = "ICMP", - .protonum = IPPROTO_ICMP, - .me = THIS_MODULE, - .manip_pkt = icmp_manip_pkt, - .in_range = icmp_in_range, - .unique_tuple = icmp_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c deleted file mode 100644 index 14ff24f..0000000 --- a/net/ipv4/netfilter/ip_nat_proto_tcp.c +++ /dev/null @@ -1,154 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int -tcp_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - __be16 port; - - if (maniptype == IP_NAT_MANIP_SRC) - port = tuple->src.u.tcp.port; - else - port = tuple->dst.u.tcp.port; - - return ntohs(port) >= ntohs(min->tcp.port) - && ntohs(port) <= ntohs(max->tcp.port); -} - -static int -tcp_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t port; - __be16 *portptr; - unsigned int range_size, min, i; - - if (maniptype == IP_NAT_MANIP_SRC) - portptr = &tuple->src.u.tcp.port; - else - portptr = &tuple->dst.u.tcp.port; - - /* If no range specified... */ - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { - /* If it's dst rewrite, can't change port */ - if (maniptype == IP_NAT_MANIP_DST) - return 0; - - /* Map privileged onto privileged. */ - if (ntohs(*portptr) < 1024) { - /* Loose convention: >> 512 is credential passing */ - if (ntohs(*portptr)<512) { - min = 1; - range_size = 511 - min + 1; - } else { - min = 600; - range_size = 1023 - min + 1; - } - } else { - min = 1024; - range_size = 65535 - 1024 + 1; - } - } else { - min = ntohs(range->min.tcp.port); - range_size = ntohs(range->max.tcp.port) - min + 1; - } - - /* Start from random port to avoid prediction */ - if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) - port = net_random(); - - for (i = 0; i < range_size; i++, port++) { - *portptr = htons(min + port % range_size); - if (!ip_nat_used_tuple(tuple, conntrack)) { - return 1; - } - } - return 0; -} - -static int -tcp_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - struct tcphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be16 *portptr, newport, oldport; - int hdrsize = 8; /* TCP connection tracking guarantees this much */ - - /* this could be a inner header returned in icmp packet; in such - cases we cannot update the checksum field since it is outside of - the 8 bytes of transport layer headers we are guaranteed */ - if ((*pskb)->len >= hdroff + sizeof(struct tcphdr)) - hdrsize = sizeof(struct tcphdr); - - if (!skb_make_writable(pskb, hdroff + hdrsize)) - return 0; - - iph = (struct iphdr *)((*pskb)->data + iphdroff); - hdr = (struct tcphdr *)((*pskb)->data + hdroff); - - if (maniptype == IP_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.ip; - newport = tuple->src.u.tcp.port; - portptr = &hdr->source; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.ip; - newport = tuple->dst.u.tcp.port; - portptr = &hdr->dest; - } - - oldport = *portptr; - *portptr = newport; - - if (hdrsize < sizeof(*hdr)) - return 1; - - nf_proto_csum_replace4(&hdr->check, *pskb, oldip, newip, 1); - nf_proto_csum_replace2(&hdr->check, *pskb, oldport, newport, 0); - return 1; -} - -struct ip_nat_protocol ip_nat_protocol_tcp = { - .name = "TCP", - .protonum = IPPROTO_TCP, - .me = THIS_MODULE, - .manip_pkt = tcp_manip_pkt, - .in_range = tcp_in_range, - .unique_tuple = tcp_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c deleted file mode 100644 index dfd5216..0000000 --- a/net/ipv4/netfilter/ip_nat_proto_udp.c +++ /dev/null @@ -1,144 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static int -udp_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - __be16 port; - - if (maniptype == IP_NAT_MANIP_SRC) - port = tuple->src.u.udp.port; - else - port = tuple->dst.u.udp.port; - - return ntohs(port) >= ntohs(min->udp.port) - && ntohs(port) <= ntohs(max->udp.port); -} - -static int -udp_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t port; - __be16 *portptr; - unsigned int range_size, min, i; - - if (maniptype == IP_NAT_MANIP_SRC) - portptr = &tuple->src.u.udp.port; - else - portptr = &tuple->dst.u.udp.port; - - /* If no range specified... */ - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { - /* If it's dst rewrite, can't change port */ - if (maniptype == IP_NAT_MANIP_DST) - return 0; - - if (ntohs(*portptr) < 1024) { - /* Loose convention: >> 512 is credential passing */ - if (ntohs(*portptr)<512) { - min = 1; - range_size = 511 - min + 1; - } else { - min = 600; - range_size = 1023 - min + 1; - } - } else { - min = 1024; - range_size = 65535 - 1024 + 1; - } - } else { - min = ntohs(range->min.udp.port); - range_size = ntohs(range->max.udp.port) - min + 1; - } - - /* Start from random port to avoid prediction */ - if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) - port = net_random(); - - for (i = 0; i < range_size; i++, port++) { - *portptr = htons(min + port % range_size); - if (!ip_nat_used_tuple(tuple, conntrack)) - return 1; - } - return 0; -} - -static int -udp_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - struct udphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be16 *portptr, newport; - - if (!skb_make_writable(pskb, hdroff + sizeof(*hdr))) - return 0; - - iph = (struct iphdr *)((*pskb)->data + iphdroff); - hdr = (struct udphdr *)((*pskb)->data + hdroff); - - if (maniptype == IP_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.ip; - newport = tuple->src.u.udp.port; - portptr = &hdr->source; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.ip; - newport = tuple->dst.u.udp.port; - portptr = &hdr->dest; - } - - if (hdr->check || (*pskb)->ip_summed == CHECKSUM_PARTIAL) { - nf_proto_csum_replace4(&hdr->check, *pskb, oldip, newip, 1); - nf_proto_csum_replace2(&hdr->check, *pskb, *portptr, newport, 0); - if (!hdr->check) - hdr->check = CSUM_MANGLED_0; - } - *portptr = newport; - return 1; -} - -struct ip_nat_protocol ip_nat_protocol_udp = { - .name = "UDP", - .protonum = IPPROTO_UDP, - .me = THIS_MODULE, - .manip_pkt = udp_manip_pkt, - .in_range = udp_in_range, - .unique_tuple = udp_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/ip_nat_proto_unknown.c b/net/ipv4/netfilter/ip_nat_proto_unknown.c deleted file mode 100644 index 3bf0495..0000000 --- a/net/ipv4/netfilter/ip_nat_proto_unknown.c +++ /dev/null @@ -1,55 +0,0 @@ -/* The "unknown" protocol. This is what is used for protocols we - * don't understand. It's returned by ip_ct_find_proto(). - */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include -#include -#include - -static int unknown_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type manip_type, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - return 1; -} - -static int unknown_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - /* Sorry: we can't help you; if it's not unique, we can't frob - anything. */ - return 0; -} - -static int -unknown_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - return 1; -} - -struct ip_nat_protocol ip_nat_unknown_protocol = { - .name = "unknown", - /* .me isn't set: getting a ref to this cannot fail. */ - .manip_pkt = unknown_manip_pkt, - .in_range = unknown_in_range, - .unique_tuple = unknown_unique_tuple, -}; diff --git a/net/ipv4/netfilter/ip_nat_rule.c b/net/ipv4/netfilter/ip_nat_rule.c deleted file mode 100644 index 080eb1d..0000000 --- a/net/ipv4/netfilter/ip_nat_rule.c +++ /dev/null @@ -1,314 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Everything about the rules for NAT. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -#define NAT_VALID_HOOKS ((1<range[0], hooknum); -} - -/* Before 2.6.11 we did implicit source NAT if required. Warn about change. */ -static void warn_if_extra_mangle(__be32 dstip, __be32 srcip) -{ - static int warned = 0; - struct flowi fl = { .nl_u = { .ip4_u = { .daddr = dstip } } }; - struct rtable *rt; - - if (ip_route_output_key(&rt, &fl) != 0) - return; - - if (rt->rt_src != srcip && !warned) { - printk("NAT: no longer support implicit source local NAT\n"); - printk("NAT: packet src %u.%u.%u.%u -> dst %u.%u.%u.%u\n", - NIPQUAD(srcip), NIPQUAD(dstip)); - warned = 1; - } - ip_rt_put(rt); -} - -static unsigned int ipt_dnat_target(struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - const struct ip_nat_multi_range_compat *mr = targinfo; - - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING - || hooknum == NF_IP_LOCAL_OUT); - - ct = ip_conntrack_get(*pskb, &ctinfo); - - /* Connection must be valid and new. */ - IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); - - if (hooknum == NF_IP_LOCAL_OUT - && mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) - warn_if_extra_mangle((*pskb)->nh.iph->daddr, - mr->range[0].min_ip); - - return ip_nat_setup_info(ct, &mr->range[0], hooknum); -} - -static int ipt_snat_checkentry(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) -{ - struct ip_nat_multi_range_compat *mr = targinfo; - - /* Must be a valid range */ - if (mr->rangesize != 1) { - printk("SNAT: multiple ranges no longer supported\n"); - return 0; - } - return 1; -} - -static int ipt_dnat_checkentry(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) -{ - struct ip_nat_multi_range_compat *mr = targinfo; - - /* Must be a valid range */ - if (mr->rangesize != 1) { - printk("DNAT: multiple ranges no longer supported\n"); - return 0; - } - if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) { - printk("DNAT: port randomization not supported\n"); - return 0; - } - return 1; -} - -inline unsigned int -alloc_null_binding(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum) -{ - /* Force range to this IP; let proto decide mapping for - per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). - Use reply in case it's already been mangled (eg local packet). - */ - __be32 ip - = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC - ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip - : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); - struct ip_nat_range range - = { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } }; - - DEBUGP("Allocating NULL binding for %p (%u.%u.%u.%u)\n", conntrack, - NIPQUAD(ip)); - return ip_nat_setup_info(conntrack, &range, hooknum); -} - -unsigned int -alloc_null_binding_confirmed(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum) -{ - __be32 ip - = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC - ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip - : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); - u_int16_t all - = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC - ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all - : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.all); - struct ip_nat_range range - = { IP_NAT_RANGE_MAP_IPS, ip, ip, { all }, { all } }; - - DEBUGP("Allocating NULL binding for confirmed %p (%u.%u.%u.%u)\n", - conntrack, NIPQUAD(ip)); - return ip_nat_setup_info(conntrack, &range, hooknum); -} - -int ip_nat_rule_find(struct sk_buff **pskb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - struct ip_conntrack *ct, - struct ip_nat_info *info) -{ - int ret; - - ret = ipt_do_table(pskb, hooknum, in, out, &nat_table); - - if (ret == NF_ACCEPT) { - if (!ip_nat_initialized(ct, HOOK2MANIP(hooknum))) - /* NUL mapping */ - ret = alloc_null_binding(ct, info, hooknum); - } - return ret; -} - -static struct xt_target ipt_snat_reg = { - .name = "SNAT", - .family = AF_INET, - .target = ipt_snat_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), - .table = "nat", - .hooks = 1 << NF_IP_POST_ROUTING, - .checkentry = ipt_snat_checkentry, -}; - -static struct xt_target ipt_dnat_reg = { - .name = "DNAT", - .family = AF_INET, - .target = ipt_dnat_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), - .table = "nat", - .hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT), - .checkentry = ipt_dnat_checkentry, -}; - -int __init ip_nat_rule_init(void) -{ - int ret; - - ret = ipt_register_table(&nat_table, &nat_initial_table.repl); - if (ret != 0) - return ret; - ret = xt_register_target(&ipt_snat_reg); - if (ret != 0) - goto unregister_table; - - ret = xt_register_target(&ipt_dnat_reg); - if (ret != 0) - goto unregister_snat; - - return ret; - - unregister_snat: - xt_unregister_target(&ipt_snat_reg); - unregister_table: - xt_unregister_table(&nat_table); - - return ret; -} - -void ip_nat_rule_cleanup(void) -{ - xt_unregister_target(&ipt_dnat_reg); - xt_unregister_target(&ipt_snat_reg); - ipt_unregister_table(&nat_table); -} diff --git a/net/ipv4/netfilter/ip_nat_sip.c b/net/ipv4/netfilter/ip_nat_sip.c deleted file mode 100644 index 325c5a9..0000000 --- a/net/ipv4/netfilter/ip_nat_sip.c +++ /dev/null @@ -1,282 +0,0 @@ -/* SIP extension for UDP NAT alteration. - * - * (C) 2005 by Christian Hentschel - * based on RR's ip_nat_ftp.c and other modules. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Hentschel "); -MODULE_DESCRIPTION("SIP NAT helper"); - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -struct addr_map { - struct { - char src[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - char dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - unsigned int srclen, srciplen; - unsigned int dstlen, dstiplen; - } addr[IP_CT_DIR_MAX]; -}; - -static void addr_map_init(struct ip_conntrack *ct, struct addr_map *map) -{ - struct ip_conntrack_tuple *t; - enum ip_conntrack_dir dir; - unsigned int n; - - for (dir = 0; dir < IP_CT_DIR_MAX; dir++) { - t = &ct->tuplehash[dir].tuple; - - n = sprintf(map->addr[dir].src, "%u.%u.%u.%u", - NIPQUAD(t->src.ip)); - map->addr[dir].srciplen = n; - n += sprintf(map->addr[dir].src + n, ":%u", - ntohs(t->src.u.udp.port)); - map->addr[dir].srclen = n; - - n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u", - NIPQUAD(t->dst.ip)); - map->addr[dir].dstiplen = n; - n += sprintf(map->addr[dir].dst + n, ":%u", - ntohs(t->dst.u.udp.port)); - map->addr[dir].dstlen = n; - } -} - -static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, const char **dptr, size_t dlen, - enum sip_header_pos pos, struct addr_map *map) -{ - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned int matchlen, matchoff, addrlen; - char *addr; - - if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0) - return 1; - - if ((matchlen == map->addr[dir].srciplen || - matchlen == map->addr[dir].srclen) && - memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) { - addr = map->addr[!dir].dst; - addrlen = map->addr[!dir].dstlen; - } else if ((matchlen == map->addr[dir].dstiplen || - matchlen == map->addr[dir].dstlen) && - memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) { - addr = map->addr[!dir].src; - addrlen = map->addr[!dir].srclen; - } else - return 1; - - if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - matchoff, matchlen, addr, addrlen)) - return 0; - *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); - return 1; - -} - -static unsigned int ip_nat_sip(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr) -{ - enum sip_header_pos pos; - struct addr_map map; - int dataoff, datalen; - - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); - datalen = (*pskb)->len - dataoff; - if (datalen < sizeof("SIP/2.0") - 1) - return NF_DROP; - - addr_map_init(ct, &map); - - /* Basic rules: requests and responses. */ - if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) { - /* 10.2: Constructing the REGISTER Request: - * - * The "userinfo" and "@" components of the SIP URI MUST NOT - * be present. - */ - if (datalen >= sizeof("REGISTER") - 1 && - strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0) - pos = POS_REG_REQ_URI; - else - pos = POS_REQ_URI; - - if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map)) - return NF_DROP; - } - - if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) || - !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) || - !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) || - !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map)) - return NF_DROP; - return NF_ACCEPT; -} - -static unsigned int mangle_sip_packet(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr, size_t dlen, - char *buffer, int bufflen, - enum sip_header_pos pos) -{ - unsigned int matchlen, matchoff; - - if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0) - return 0; - - if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - matchoff, matchlen, buffer, bufflen)) - return 0; - - /* We need to reload this. Thanks Patrick. */ - *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); - return 1; -} - -static int mangle_content_len(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char *dptr) -{ - unsigned int dataoff, matchoff, matchlen; - char buffer[sizeof("65536")]; - int bufflen; - - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); - - /* Get actual SDP lenght */ - if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, - &matchlen, POS_SDP_HEADER) > 0) { - - /* since ct_sip_get_info() give us a pointer passing 'v=' - we need to add 2 bytes in this count. */ - int c_len = (*pskb)->len - dataoff - matchoff + 2; - - /* Now, update SDP lenght */ - if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, - &matchlen, POS_CONTENT) > 0) { - - bufflen = sprintf(buffer, "%u", c_len); - - return ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - matchoff, matchlen, - buffer, bufflen); - } - } - return 0; -} - -static unsigned int mangle_sdp(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - __be32 newip, u_int16_t port, - const char *dptr) -{ - char buffer[sizeof("nnn.nnn.nnn.nnn")]; - unsigned int dataoff, bufflen; - - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); - - /* Mangle owner and contact info. */ - bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); - if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, - buffer, bufflen, POS_OWNER)) - return 0; - - if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, - buffer, bufflen, POS_CONNECTION)) - return 0; - - /* Mangle media port. */ - bufflen = sprintf(buffer, "%u", port); - if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, - buffer, bufflen, POS_MEDIA)) - return 0; - - return mangle_content_len(pskb, ctinfo, ct, dptr); -} - -/* So, this packet has hit the connection tracking matching code. - Mangle it, and change the expectation to match the new version. */ -static unsigned int ip_nat_sdp(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp, - const char *dptr) -{ - struct ip_conntrack *ct = exp->master; - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - __be32 newip; - u_int16_t port; - - DEBUGP("ip_nat_sdp():\n"); - - /* Connection will come from reply */ - newip = ct->tuplehash[!dir].tuple.dst.ip; - - exp->tuple.dst.ip = newip; - exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; - exp->dir = !dir; - - /* When you see the packet, we need to NAT it the same as the - this one. */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { - exp->tuple.dst.u.udp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { - ip_conntrack_unexpect_related(exp); - return NF_DROP; - } - return NF_ACCEPT; -} - -static void __exit fini(void) -{ - rcu_assign_pointer(ip_nat_sip_hook, NULL); - rcu_assign_pointer(ip_nat_sdp_hook, NULL); - synchronize_rcu(); -} - -static int __init init(void) -{ - BUG_ON(rcu_dereference(ip_nat_sip_hook)); - BUG_ON(rcu_dereference(ip_nat_sdp_hook)); - rcu_assign_pointer(ip_nat_sip_hook, ip_nat_sip); - rcu_assign_pointer(ip_nat_sdp_hook, ip_nat_sdp); - return 0; -} - -module_init(init); -module_exit(fini); diff --git a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c deleted file mode 100644 index e41d0ef..0000000 --- a/net/ipv4/netfilter/ip_nat_snmp_basic.c +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * ip_nat_snmp_basic.c - * - * Basic SNMP Application Layer Gateway - * - * This IP NAT module is intended for use with SNMP network - * discovery and monitoring applications where target networks use - * conflicting private address realms. - * - * Static NAT is used to remap the networks from the view of the network - * management system at the IP layer, and this module remaps some application - * layer addresses to match. - * - * The simplest form of ALG is performed, where only tagged IP addresses - * are modified. The module does not need to be MIB aware and only scans - * messages at the ASN.1/BER level. - * - * Currently, only SNMPv1 and SNMPv2 are supported. - * - * More information on ALG and associated issues can be found in - * RFC 2962 - * - * The ASB.1/BER parsing code is derived from the gxsnmp package by Gregory - * McLean & Jochen Friedrich, stripped down for use in the kernel. - * - * Copyright (c) 2000 RP Internet (www.rpi.net.au). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: James Morris - * - * Updates: - * 2000-08-06: Convert to new helper API (Harald Welte). - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("James Morris "); -MODULE_DESCRIPTION("Basic SNMP Application Layer Gateway"); - -#define SNMP_PORT 161 -#define SNMP_TRAP_PORT 162 -#define NOCT1(n) (*(u8 *)n) - -static int debug; -static DEFINE_SPINLOCK(snmp_lock); - -/* - * Application layer address mapping mimics the NAT mapping, but - * only for the first octet in this case (a more flexible system - * can be implemented if needed). - */ -struct oct1_map -{ - u_int8_t from; - u_int8_t to; -}; - - -/***************************************************************************** - * - * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse) - * - *****************************************************************************/ - -/* Class */ -#define ASN1_UNI 0 /* Universal */ -#define ASN1_APL 1 /* Application */ -#define ASN1_CTX 2 /* Context */ -#define ASN1_PRV 3 /* Private */ - -/* Tag */ -#define ASN1_EOC 0 /* End Of Contents */ -#define ASN1_BOL 1 /* Boolean */ -#define ASN1_INT 2 /* Integer */ -#define ASN1_BTS 3 /* Bit String */ -#define ASN1_OTS 4 /* Octet String */ -#define ASN1_NUL 5 /* Null */ -#define ASN1_OJI 6 /* Object Identifier */ -#define ASN1_OJD 7 /* Object Description */ -#define ASN1_EXT 8 /* External */ -#define ASN1_SEQ 16 /* Sequence */ -#define ASN1_SET 17 /* Set */ -#define ASN1_NUMSTR 18 /* Numerical String */ -#define ASN1_PRNSTR 19 /* Printable String */ -#define ASN1_TEXSTR 20 /* Teletext String */ -#define ASN1_VIDSTR 21 /* Video String */ -#define ASN1_IA5STR 22 /* IA5 String */ -#define ASN1_UNITIM 23 /* Universal Time */ -#define ASN1_GENTIM 24 /* General Time */ -#define ASN1_GRASTR 25 /* Graphical String */ -#define ASN1_VISSTR 26 /* Visible String */ -#define ASN1_GENSTR 27 /* General String */ - -/* Primitive / Constructed methods*/ -#define ASN1_PRI 0 /* Primitive */ -#define ASN1_CON 1 /* Constructed */ - -/* - * Error codes. - */ -#define ASN1_ERR_NOERROR 0 -#define ASN1_ERR_DEC_EMPTY 2 -#define ASN1_ERR_DEC_EOC_MISMATCH 3 -#define ASN1_ERR_DEC_LENGTH_MISMATCH 4 -#define ASN1_ERR_DEC_BADVALUE 5 - -/* - * ASN.1 context. - */ -struct asn1_ctx -{ - int error; /* Error condition */ - unsigned char *pointer; /* Octet just to be decoded */ - unsigned char *begin; /* First octet */ - unsigned char *end; /* Octet after last octet */ -}; - -/* - * Octet string (not null terminated) - */ -struct asn1_octstr -{ - unsigned char *data; - unsigned int len; -}; - -static void asn1_open(struct asn1_ctx *ctx, - unsigned char *buf, - unsigned int len) -{ - ctx->begin = buf; - ctx->end = buf + len; - ctx->pointer = buf; - ctx->error = ASN1_ERR_NOERROR; -} - -static unsigned char asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch) -{ - if (ctx->pointer >= ctx->end) { - ctx->error = ASN1_ERR_DEC_EMPTY; - return 0; - } - *ch = *(ctx->pointer)++; - return 1; -} - -static unsigned char asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag) -{ - unsigned char ch; - - *tag = 0; - - do - { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - *tag <<= 7; - *tag |= ch & 0x7F; - } while ((ch & 0x80) == 0x80); - return 1; -} - -static unsigned char asn1_id_decode(struct asn1_ctx *ctx, - unsigned int *cls, - unsigned int *con, - unsigned int *tag) -{ - unsigned char ch; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *cls = (ch & 0xC0) >> 6; - *con = (ch & 0x20) >> 5; - *tag = (ch & 0x1F); - - if (*tag == 0x1F) { - if (!asn1_tag_decode(ctx, tag)) - return 0; - } - return 1; -} - -static unsigned char asn1_length_decode(struct asn1_ctx *ctx, - unsigned int *def, - unsigned int *len) -{ - unsigned char ch, cnt; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch == 0x80) - *def = 0; - else { - *def = 1; - - if (ch < 0x80) - *len = ch; - else { - cnt = (unsigned char) (ch & 0x7F); - *len = 0; - - while (cnt > 0) { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - *len <<= 8; - *len |= ch; - cnt--; - } - } - } - return 1; -} - -static unsigned char asn1_header_decode(struct asn1_ctx *ctx, - unsigned char **eoc, - unsigned int *cls, - unsigned int *con, - unsigned int *tag) -{ - unsigned int def, len; - - if (!asn1_id_decode(ctx, cls, con, tag)) - return 0; - - def = len = 0; - if (!asn1_length_decode(ctx, &def, &len)) - return 0; - - if (def) - *eoc = ctx->pointer + len; - else - *eoc = NULL; - return 1; -} - -static unsigned char asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc) -{ - unsigned char ch; - - if (eoc == 0) { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch != 0x00) { - ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch != 0x00) { - ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; - return 0; - } - return 1; - } else { - if (ctx->pointer != eoc) { - ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH; - return 0; - } - return 1; - } -} - -static unsigned char asn1_null_decode(struct asn1_ctx *ctx, unsigned char *eoc) -{ - ctx->pointer = eoc; - return 1; -} - -static unsigned char asn1_long_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - long *integer) -{ - unsigned char ch; - unsigned int len; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer = (signed char) ch; - len = 1; - - while (ctx->pointer < eoc) { - if (++len > sizeof (long)) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer <<= 8; - *integer |= ch; - } - return 1; -} - -static unsigned char asn1_uint_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned int *integer) -{ - unsigned char ch; - unsigned int len; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer = ch; - if (ch == 0) len = 0; - else len = 1; - - while (ctx->pointer < eoc) { - if (++len > sizeof (unsigned int)) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer <<= 8; - *integer |= ch; - } - return 1; -} - -static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned long *integer) -{ - unsigned char ch; - unsigned int len; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer = ch; - if (ch == 0) len = 0; - else len = 1; - - while (ctx->pointer < eoc) { - if (++len > sizeof (unsigned long)) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer <<= 8; - *integer |= ch; - } - return 1; -} - -static unsigned char asn1_octets_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned char **octets, - unsigned int *len) -{ - unsigned char *ptr; - - *len = 0; - - *octets = kmalloc(eoc - ctx->pointer, GFP_ATOMIC); - if (*octets == NULL) { - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - - ptr = *octets; - while (ctx->pointer < eoc) { - if (!asn1_octet_decode(ctx, (unsigned char *)ptr++)) { - kfree(*octets); - *octets = NULL; - return 0; - } - (*len)++; - } - return 1; -} - -static unsigned char asn1_subid_decode(struct asn1_ctx *ctx, - unsigned long *subid) -{ - unsigned char ch; - - *subid = 0; - - do { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *subid <<= 7; - *subid |= ch & 0x7F; - } while ((ch & 0x80) == 0x80); - return 1; -} - -static unsigned char asn1_oid_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned long **oid, - unsigned int *len) -{ - unsigned long subid; - unsigned int size; - unsigned long *optr; - - size = eoc - ctx->pointer + 1; - *oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC); - if (*oid == NULL) { - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - - optr = *oid; - - if (!asn1_subid_decode(ctx, &subid)) { - kfree(*oid); - *oid = NULL; - return 0; - } - - if (subid < 40) { - optr [0] = 0; - optr [1] = subid; - } else if (subid < 80) { - optr [0] = 1; - optr [1] = subid - 40; - } else { - optr [0] = 2; - optr [1] = subid - 80; - } - - *len = 2; - optr += 2; - - while (ctx->pointer < eoc) { - if (++(*len) > size) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - kfree(*oid); - *oid = NULL; - return 0; - } - - if (!asn1_subid_decode(ctx, optr++)) { - kfree(*oid); - *oid = NULL; - return 0; - } - } - return 1; -} - -/***************************************************************************** - * - * SNMP decoding routines (gxsnmp author Dirk Wisse) - * - *****************************************************************************/ - -/* SNMP Versions */ -#define SNMP_V1 0 -#define SNMP_V2C 1 -#define SNMP_V2 2 -#define SNMP_V3 3 - -/* Default Sizes */ -#define SNMP_SIZE_COMM 256 -#define SNMP_SIZE_OBJECTID 128 -#define SNMP_SIZE_BUFCHR 256 -#define SNMP_SIZE_BUFINT 128 -#define SNMP_SIZE_SMALLOBJECTID 16 - -/* Requests */ -#define SNMP_PDU_GET 0 -#define SNMP_PDU_NEXT 1 -#define SNMP_PDU_RESPONSE 2 -#define SNMP_PDU_SET 3 -#define SNMP_PDU_TRAP1 4 -#define SNMP_PDU_BULK 5 -#define SNMP_PDU_INFORM 6 -#define SNMP_PDU_TRAP2 7 - -/* Errors */ -#define SNMP_NOERROR 0 -#define SNMP_TOOBIG 1 -#define SNMP_NOSUCHNAME 2 -#define SNMP_BADVALUE 3 -#define SNMP_READONLY 4 -#define SNMP_GENERROR 5 -#define SNMP_NOACCESS 6 -#define SNMP_WRONGTYPE 7 -#define SNMP_WRONGLENGTH 8 -#define SNMP_WRONGENCODING 9 -#define SNMP_WRONGVALUE 10 -#define SNMP_NOCREATION 11 -#define SNMP_INCONSISTENTVALUE 12 -#define SNMP_RESOURCEUNAVAILABLE 13 -#define SNMP_COMMITFAILED 14 -#define SNMP_UNDOFAILED 15 -#define SNMP_AUTHORIZATIONERROR 16 -#define SNMP_NOTWRITABLE 17 -#define SNMP_INCONSISTENTNAME 18 - -/* General SNMP V1 Traps */ -#define SNMP_TRAP_COLDSTART 0 -#define SNMP_TRAP_WARMSTART 1 -#define SNMP_TRAP_LINKDOWN 2 -#define SNMP_TRAP_LINKUP 3 -#define SNMP_TRAP_AUTFAILURE 4 -#define SNMP_TRAP_EQPNEIGHBORLOSS 5 -#define SNMP_TRAP_ENTSPECIFIC 6 - -/* SNMPv1 Types */ -#define SNMP_NULL 0 -#define SNMP_INTEGER 1 /* l */ -#define SNMP_OCTETSTR 2 /* c */ -#define SNMP_DISPLAYSTR 2 /* c */ -#define SNMP_OBJECTID 3 /* ul */ -#define SNMP_IPADDR 4 /* uc */ -#define SNMP_COUNTER 5 /* ul */ -#define SNMP_GAUGE 6 /* ul */ -#define SNMP_TIMETICKS 7 /* ul */ -#define SNMP_OPAQUE 8 /* c */ - -/* Additional SNMPv2 Types */ -#define SNMP_UINTEGER 5 /* ul */ -#define SNMP_BITSTR 9 /* uc */ -#define SNMP_NSAP 10 /* uc */ -#define SNMP_COUNTER64 11 /* ul */ -#define SNMP_NOSUCHOBJECT 12 -#define SNMP_NOSUCHINSTANCE 13 -#define SNMP_ENDOFMIBVIEW 14 - -union snmp_syntax -{ - unsigned char uc[0]; /* 8 bit unsigned */ - char c[0]; /* 8 bit signed */ - unsigned long ul[0]; /* 32 bit unsigned */ - long l[0]; /* 32 bit signed */ -}; - -struct snmp_object -{ - unsigned long *id; - unsigned int id_len; - unsigned short type; - unsigned int syntax_len; - union snmp_syntax syntax; -}; - -struct snmp_request -{ - unsigned long id; - unsigned int error_status; - unsigned int error_index; -}; - -struct snmp_v1_trap -{ - unsigned long *id; - unsigned int id_len; - unsigned long ip_address; /* pointer */ - unsigned int general; - unsigned int specific; - unsigned long time; -}; - -/* SNMP types */ -#define SNMP_IPA 0 -#define SNMP_CNT 1 -#define SNMP_GGE 2 -#define SNMP_TIT 3 -#define SNMP_OPQ 4 -#define SNMP_C64 6 - -/* SNMP errors */ -#define SERR_NSO 0 -#define SERR_NSI 1 -#define SERR_EOM 2 - -static inline void mangle_address(unsigned char *begin, - unsigned char *addr, - const struct oct1_map *map, - __sum16 *check); -struct snmp_cnv -{ - unsigned int class; - unsigned int tag; - int syntax; -}; - -static struct snmp_cnv snmp_conv [] = -{ - {ASN1_UNI, ASN1_NUL, SNMP_NULL}, - {ASN1_UNI, ASN1_INT, SNMP_INTEGER}, - {ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR}, - {ASN1_UNI, ASN1_OTS, SNMP_DISPLAYSTR}, - {ASN1_UNI, ASN1_OJI, SNMP_OBJECTID}, - {ASN1_APL, SNMP_IPA, SNMP_IPADDR}, - {ASN1_APL, SNMP_CNT, SNMP_COUNTER}, /* Counter32 */ - {ASN1_APL, SNMP_GGE, SNMP_GAUGE}, /* Gauge32 == Unsigned32 */ - {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS}, - {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE}, - - /* SNMPv2 data types and errors */ - {ASN1_UNI, ASN1_BTS, SNMP_BITSTR}, - {ASN1_APL, SNMP_C64, SNMP_COUNTER64}, - {ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT}, - {ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE}, - {ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW}, - {0, 0, -1} -}; - -static unsigned char snmp_tag_cls2syntax(unsigned int tag, - unsigned int cls, - unsigned short *syntax) -{ - struct snmp_cnv *cnv; - - cnv = snmp_conv; - - while (cnv->syntax != -1) { - if (cnv->tag == tag && cnv->class == cls) { - *syntax = cnv->syntax; - return 1; - } - cnv++; - } - return 0; -} - -static unsigned char snmp_object_decode(struct asn1_ctx *ctx, - struct snmp_object **obj) -{ - unsigned int cls, con, tag, len, idlen; - unsigned short type; - unsigned char *eoc, *end, *p; - unsigned long *lp, *id; - unsigned long ul; - long l; - - *obj = NULL; - id = NULL; - - if (!asn1_header_decode(ctx, &eoc, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI) - return 0; - - if (!asn1_oid_decode(ctx, end, &id, &idlen)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) { - kfree(id); - return 0; - } - - if (con != ASN1_PRI) { - kfree(id); - return 0; - } - - type = 0; - if (!snmp_tag_cls2syntax(tag, cls, &type)) { - kfree(id); - return 0; - } - - l = 0; - switch (type) { - case SNMP_INTEGER: - len = sizeof(long); - if (!asn1_long_decode(ctx, end, &l)) { - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, - GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - (*obj)->syntax.l[0] = l; - break; - case SNMP_OCTETSTR: - case SNMP_OPAQUE: - if (!asn1_octets_decode(ctx, end, &p, &len)) { - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, - GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - memcpy((*obj)->syntax.c, p, len); - kfree(p); - break; - case SNMP_NULL: - case SNMP_NOSUCHOBJECT: - case SNMP_NOSUCHINSTANCE: - case SNMP_ENDOFMIBVIEW: - len = 0; - *obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - if (!asn1_null_decode(ctx, end)) { - kfree(id); - kfree(*obj); - *obj = NULL; - return 0; - } - break; - case SNMP_OBJECTID: - if (!asn1_oid_decode(ctx, end, (unsigned long **)&lp, &len)) { - kfree(id); - return 0; - } - len *= sizeof(unsigned long); - *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); - if (*obj == NULL) { - kfree(lp); - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - memcpy((*obj)->syntax.ul, lp, len); - kfree(lp); - break; - case SNMP_IPADDR: - if (!asn1_octets_decode(ctx, end, &p, &len)) { - kfree(id); - return 0; - } - if (len != 4) { - kfree(p); - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); - if (*obj == NULL) { - kfree(p); - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - memcpy((*obj)->syntax.uc, p, len); - kfree(p); - break; - case SNMP_COUNTER: - case SNMP_GAUGE: - case SNMP_TIMETICKS: - len = sizeof(unsigned long); - if (!asn1_ulong_decode(ctx, end, &ul)) { - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - (*obj)->syntax.ul[0] = ul; - break; - default: - kfree(id); - return 0; - } - - (*obj)->syntax_len = len; - (*obj)->type = type; - (*obj)->id = id; - (*obj)->id_len = idlen; - - if (!asn1_eoc_decode(ctx, eoc)) { - kfree(id); - kfree(*obj); - *obj = NULL; - return 0; - } - return 1; -} - -static unsigned char snmp_request_decode(struct asn1_ctx *ctx, - struct snmp_request *request) -{ - unsigned int cls, con, tag; - unsigned char *end; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - - if (!asn1_ulong_decode(ctx, end, &request->id)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - - if (!asn1_uint_decode(ctx, end, &request->error_status)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - - if (!asn1_uint_decode(ctx, end, &request->error_index)) - return 0; - - return 1; -} - -/* - * Fast checksum update for possibly oddly-aligned UDP byte, from the - * code example in the draft. - */ -static void fast_csum(__sum16 *csum, - const unsigned char *optr, - const unsigned char *nptr, - int offset) -{ - unsigned char s[4]; - - if (offset & 1) { - s[0] = s[2] = 0; - s[1] = ~*optr; - s[3] = *nptr; - } else { - s[1] = s[3] = 0; - s[0] = ~*optr; - s[2] = *nptr; - } - - *csum = csum_fold(csum_partial(s, 4, ~csum_unfold(*csum))); -} - -/* - * Mangle IP address. - * - begin points to the start of the snmp messgae - * - addr points to the start of the address - */ -static inline void mangle_address(unsigned char *begin, - unsigned char *addr, - const struct oct1_map *map, - __sum16 *check) -{ - if (map->from == NOCT1(addr)) { - u_int32_t old; - - if (debug) - memcpy(&old, (unsigned char *)addr, sizeof(old)); - - *addr = map->to; - - /* Update UDP checksum if being used */ - if (*check) { - fast_csum(check, - &map->from, &map->to, addr - begin); - } - - if (debug) - printk(KERN_DEBUG "bsalg: mapped %u.%u.%u.%u to " - "%u.%u.%u.%u\n", NIPQUAD(old), NIPQUAD(*addr)); - } -} - -static unsigned char snmp_trap_decode(struct asn1_ctx *ctx, - struct snmp_v1_trap *trap, - const struct oct1_map *map, - __sum16 *check) -{ - unsigned int cls, con, tag, len; - unsigned char *end; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI) - return 0; - - if (!asn1_oid_decode(ctx, end, &trap->id, &trap->id_len)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_id_free; - - if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) || - (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS))) - goto err_id_free; - - if (!asn1_octets_decode(ctx, end, (unsigned char **)&trap->ip_address, &len)) - goto err_id_free; - - /* IPv4 only */ - if (len != 4) - goto err_addr_free; - - mangle_address(ctx->begin, ctx->pointer - 4, map, check); - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_addr_free; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - goto err_addr_free; - - if (!asn1_uint_decode(ctx, end, &trap->general)) - goto err_addr_free; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_addr_free; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - goto err_addr_free; - - if (!asn1_uint_decode(ctx, end, &trap->specific)) - goto err_addr_free; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_addr_free; - - if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) || - (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT))) - goto err_addr_free; - - if (!asn1_ulong_decode(ctx, end, &trap->time)) - goto err_addr_free; - - return 1; - -err_addr_free: - kfree((unsigned long *)trap->ip_address); - -err_id_free: - kfree(trap->id); - - return 0; -} - -/***************************************************************************** - * - * Misc. routines - * - *****************************************************************************/ - -static void hex_dump(unsigned char *buf, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (i && !(i % 16)) - printk("\n"); - printk("%02x ", *(buf + i)); - } - printk("\n"); -} - -/* - * Parse and mangle SNMP message according to mapping. - * (And this is the fucking 'basic' method). - */ -static int snmp_parse_mangle(unsigned char *msg, - u_int16_t len, - const struct oct1_map *map, - __sum16 *check) -{ - unsigned char *eoc, *end; - unsigned int cls, con, tag, vers, pdutype; - struct asn1_ctx ctx; - struct asn1_octstr comm; - struct snmp_object **obj; - - if (debug > 1) - hex_dump(msg, len); - - asn1_open(&ctx, msg, len); - - /* - * Start of SNMP message. - */ - if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag)) - return 0; - if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) - return 0; - - /* - * Version 1 or 2 handled. - */ - if (!asn1_header_decode(&ctx, &end, &cls, &con, &tag)) - return 0; - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - if (!asn1_uint_decode (&ctx, end, &vers)) - return 0; - if (debug > 1) - printk(KERN_DEBUG "bsalg: snmp version: %u\n", vers + 1); - if (vers > 1) - return 1; - - /* - * Community. - */ - if (!asn1_header_decode (&ctx, &end, &cls, &con, &tag)) - return 0; - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OTS) - return 0; - if (!asn1_octets_decode(&ctx, end, &comm.data, &comm.len)) - return 0; - if (debug > 1) { - unsigned int i; - - printk(KERN_DEBUG "bsalg: community: "); - for (i = 0; i < comm.len; i++) - printk("%c", comm.data[i]); - printk("\n"); - } - kfree(comm.data); - - /* - * PDU type - */ - if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &pdutype)) - return 0; - if (cls != ASN1_CTX || con != ASN1_CON) - return 0; - if (debug > 1) { - unsigned char *pdus[] = { - [SNMP_PDU_GET] = "get", - [SNMP_PDU_NEXT] = "get-next", - [SNMP_PDU_RESPONSE] = "response", - [SNMP_PDU_SET] = "set", - [SNMP_PDU_TRAP1] = "trapv1", - [SNMP_PDU_BULK] = "bulk", - [SNMP_PDU_INFORM] = "inform", - [SNMP_PDU_TRAP2] = "trapv2" - }; - - if (pdutype > SNMP_PDU_TRAP2) - printk(KERN_DEBUG "bsalg: bad pdu type %u\n", pdutype); - else - printk(KERN_DEBUG "bsalg: pdu: %s\n", pdus[pdutype]); - } - if (pdutype != SNMP_PDU_RESPONSE && - pdutype != SNMP_PDU_TRAP1 && pdutype != SNMP_PDU_TRAP2) - return 1; - - /* - * Request header or v1 trap - */ - if (pdutype == SNMP_PDU_TRAP1) { - struct snmp_v1_trap trap; - unsigned char ret = snmp_trap_decode(&ctx, &trap, map, check); - - if (ret) { - kfree(trap.id); - kfree((unsigned long *)trap.ip_address); - } else - return ret; - - } else { - struct snmp_request req; - - if (!snmp_request_decode(&ctx, &req)) - return 0; - - if (debug > 1) - printk(KERN_DEBUG "bsalg: request: id=0x%lx error_status=%u " - "error_index=%u\n", req.id, req.error_status, - req.error_index); - } - - /* - * Loop through objects, look for IP addresses to mangle. - */ - if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) - return 0; - - obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC); - if (obj == NULL) { - if (net_ratelimit()) - printk(KERN_WARNING "OOM in bsalg(%d)\n", __LINE__); - return 0; - } - - while (!asn1_eoc_decode(&ctx, eoc)) { - unsigned int i; - - if (!snmp_object_decode(&ctx, obj)) { - if (*obj) { - kfree((*obj)->id); - kfree(*obj); - } - kfree(obj); - return 0; - } - - if (debug > 1) { - printk(KERN_DEBUG "bsalg: object: "); - for (i = 0; i < (*obj)->id_len; i++) { - if (i > 0) - printk("."); - printk("%lu", (*obj)->id[i]); - } - printk(": type=%u\n", (*obj)->type); - - } - - if ((*obj)->type == SNMP_IPADDR) - mangle_address(ctx.begin, ctx.pointer - 4 , map, check); - - kfree((*obj)->id); - kfree(*obj); - } - kfree(obj); - - if (!asn1_eoc_decode(&ctx, eoc)) - return 0; - - return 1; -} - -/***************************************************************************** - * - * NAT routines. - * - *****************************************************************************/ - -/* - * SNMP translation routine. - */ -static int snmp_translate(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct sk_buff **pskb) -{ - struct iphdr *iph = (*pskb)->nh.iph; - struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl); - u_int16_t udplen = ntohs(udph->len); - u_int16_t paylen = udplen - sizeof(struct udphdr); - int dir = CTINFO2DIR(ctinfo); - struct oct1_map map; - - /* - * Determine mappping for application layer addresses based - * on NAT manipulations for the packet. - */ - if (dir == IP_CT_DIR_ORIGINAL) { - /* SNAT traps */ - map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip); - map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip); - } else { - /* DNAT replies */ - map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); - map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip); - } - - if (map.from == map.to) - return NF_ACCEPT; - - if (!snmp_parse_mangle((unsigned char *)udph + sizeof(struct udphdr), - paylen, &map, &udph->check)) { - if (net_ratelimit()) - printk(KERN_WARNING "bsalg: parser failed\n"); - return NF_DROP; - } - return NF_ACCEPT; -} - -/* We don't actually set up expectations, just adjust internal IP - * addresses if this is being NATted */ -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - int dir = CTINFO2DIR(ctinfo); - unsigned int ret; - struct iphdr *iph = (*pskb)->nh.iph; - struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); - - /* SNMP replies and originating SNMP traps get mangled */ - if (udph->source == htons(SNMP_PORT) && dir != IP_CT_DIR_REPLY) - return NF_ACCEPT; - if (udph->dest == htons(SNMP_TRAP_PORT) && dir != IP_CT_DIR_ORIGINAL) - return NF_ACCEPT; - - /* No NAT? */ - if (!(ct->status & IPS_NAT_MASK)) - return NF_ACCEPT; - - /* - * Make sure the packet length is ok. So far, we were only guaranteed - * to have a valid length IP header plus 8 bytes, which means we have - * enough room for a UDP header. Just verify the UDP length field so we - * can mess around with the payload. - */ - if (ntohs(udph->len) != (*pskb)->len - (iph->ihl << 2)) { - if (net_ratelimit()) - printk(KERN_WARNING "SNMP: dropping malformed packet " - "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n", - NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); - return NF_DROP; - } - - if (!skb_make_writable(pskb, (*pskb)->len)) - return NF_DROP; - - spin_lock_bh(&snmp_lock); - ret = snmp_translate(ct, ctinfo, pskb); - spin_unlock_bh(&snmp_lock); - return ret; -} - -static struct ip_conntrack_helper snmp_helper = { - .max_expected = 0, - .timeout = 180, - .me = THIS_MODULE, - .help = help, - .name = "snmp", - - .tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_PORT)}}}, - .dst = {.protonum = IPPROTO_UDP}, - }, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}, - }, -}; - -static struct ip_conntrack_helper snmp_trap_helper = { - .max_expected = 0, - .timeout = 180, - .me = THIS_MODULE, - .help = help, - .name = "snmp_trap", - - .tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_TRAP_PORT)}}}, - .dst = {.protonum = IPPROTO_UDP}, - }, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}, - }, -}; - -/***************************************************************************** - * - * Module stuff. - * - *****************************************************************************/ - -static int __init ip_nat_snmp_basic_init(void) -{ - int ret = 0; - - ret = ip_conntrack_helper_register(&snmp_helper); - if (ret < 0) - return ret; - ret = ip_conntrack_helper_register(&snmp_trap_helper); - if (ret < 0) { - ip_conntrack_helper_unregister(&snmp_helper); - return ret; - } - return ret; -} - -static void __exit ip_nat_snmp_basic_fini(void) -{ - ip_conntrack_helper_unregister(&snmp_helper); - ip_conntrack_helper_unregister(&snmp_trap_helper); -} - -module_init(ip_nat_snmp_basic_init); -module_exit(ip_nat_snmp_basic_fini); - -module_param(debug, int, 0600); diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c deleted file mode 100644 index 6bcfdf6..0000000 --- a/net/ipv4/netfilter/ip_nat_standalone.c +++ /dev/null @@ -1,388 +0,0 @@ -/* This file contains all the functions required for the standalone - ip_nat module. - - These are not required by the compatibility layer. -*/ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * 23 Apr 2001: Harald Welte - * - new API and handling of conntrack/nat helpers - * - now capable of multiple expectations for one master - * */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -#ifdef CONFIG_XFRM -static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) -{ - struct ip_conntrack *ct; - struct ip_conntrack_tuple *t; - enum ip_conntrack_info ctinfo; - enum ip_conntrack_dir dir; - unsigned long statusbit; - - ct = ip_conntrack_get(skb, &ctinfo); - if (ct == NULL) - return; - dir = CTINFO2DIR(ctinfo); - t = &ct->tuplehash[dir].tuple; - - if (dir == IP_CT_DIR_ORIGINAL) - statusbit = IPS_DST_NAT; - else - statusbit = IPS_SRC_NAT; - - if (ct->status & statusbit) { - fl->fl4_dst = t->dst.ip; - if (t->dst.protonum == IPPROTO_TCP || - t->dst.protonum == IPPROTO_UDP) - fl->fl_ip_dport = t->dst.u.tcp.port; - } - - statusbit ^= IPS_NAT_MASK; - - if (ct->status & statusbit) { - fl->fl4_src = t->src.ip; - if (t->dst.protonum == IPPROTO_TCP || - t->dst.protonum == IPPROTO_UDP) - fl->fl_ip_sport = t->src.u.tcp.port; - } -} -#endif - -static unsigned int -ip_nat_fn(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - struct ip_nat_info *info; - /* maniptype == SRC for postrouting. */ - enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum); - - /* We never see fragments: conntrack defrags on pre-routing - and local-out, and ip_nat_out protects post-routing. */ - IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off - & htons(IP_MF|IP_OFFSET))); - - ct = ip_conntrack_get(*pskb, &ctinfo); - /* Can't track? It's not due to stress, or conntrack would - have dropped it. Hence it's the user's responsibilty to - packet filter it out, or implement conntrack/NAT for that - protocol. 8) --RR */ - if (!ct) { - /* Exception: ICMP redirect to new connection (not in - hash table yet). We must not let this through, in - case we're doing NAT to the same network. */ - if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { - struct icmphdr _hdr, *hp; - - hp = skb_header_pointer(*pskb, - (*pskb)->nh.iph->ihl*4, - sizeof(_hdr), &_hdr); - if (hp != NULL && - hp->type == ICMP_REDIRECT) - return NF_DROP; - } - return NF_ACCEPT; - } - - /* Don't try to NAT if this packet is not conntracked */ - if (ct == &ip_conntrack_untracked) - return NF_ACCEPT; - - switch (ctinfo) { - case IP_CT_RELATED: - case IP_CT_RELATED+IP_CT_IS_REPLY: - if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { - if (!ip_nat_icmp_reply_translation(ct, ctinfo, - hooknum, pskb)) - return NF_DROP; - else - return NF_ACCEPT; - } - /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ - case IP_CT_NEW: - info = &ct->nat.info; - - /* Seen it before? This can happen for loopback, retrans, - or local packets.. */ - if (!ip_nat_initialized(ct, maniptype)) { - unsigned int ret; - - if (unlikely(is_confirmed(ct))) - /* NAT module was loaded late */ - ret = alloc_null_binding_confirmed(ct, info, - hooknum); - else if (hooknum == NF_IP_LOCAL_IN) - /* LOCAL_IN hook doesn't have a chain! */ - ret = alloc_null_binding(ct, info, hooknum); - else - ret = ip_nat_rule_find(pskb, hooknum, - in, out, ct, - info); - - if (ret != NF_ACCEPT) { - return ret; - } - } else - DEBUGP("Already setup manip %s for ct %p\n", - maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", - ct); - break; - - default: - /* ESTABLISHED */ - IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED - || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY)); - info = &ct->nat.info; - } - - IP_NF_ASSERT(info); - return ip_nat_packet(ct, ctinfo, hooknum, pskb); -} - -static unsigned int -ip_nat_in(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - unsigned int ret; - __be32 daddr = (*pskb)->nh.iph->daddr; - - ret = ip_nat_fn(hooknum, pskb, in, out, okfn); - if (ret != NF_DROP && ret != NF_STOLEN - && daddr != (*pskb)->nh.iph->daddr) { - dst_release((*pskb)->dst); - (*pskb)->dst = NULL; - } - return ret; -} - -static unsigned int -ip_nat_out(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ -#ifdef CONFIG_XFRM - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; -#endif - unsigned int ret; - - /* root is playing with raw sockets. */ - if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) - return NF_ACCEPT; - - ret = ip_nat_fn(hooknum, pskb, in, out, okfn); -#ifdef CONFIG_XFRM - if (ret != NF_DROP && ret != NF_STOLEN - && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) { - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip - || ct->tuplehash[dir].tuple.src.u.all != - ct->tuplehash[!dir].tuple.dst.u.all - ) - return ip_xfrm_me_harder(pskb) == 0 ? ret : NF_DROP; - } -#endif - return ret; -} - -static unsigned int -ip_nat_local_fn(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - unsigned int ret; - - /* root is playing with raw sockets. */ - if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) - return NF_ACCEPT; - - ret = ip_nat_fn(hooknum, pskb, in, out, okfn); - if (ret != NF_DROP && ret != NF_STOLEN - && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) { - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - - if (ct->tuplehash[dir].tuple.dst.ip != - ct->tuplehash[!dir].tuple.src.ip) { - if (ip_route_me_harder(pskb, RTN_UNSPEC)) - ret = NF_DROP; - } -#ifdef CONFIG_XFRM - else if (ct->tuplehash[dir].tuple.dst.u.all != - ct->tuplehash[!dir].tuple.src.u.all) - if (ip_xfrm_me_harder(pskb)) - ret = NF_DROP; -#endif - - } - return ret; -} - -static unsigned int -ip_nat_adjust(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - ct = ip_conntrack_get(*pskb, &ctinfo); - if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { - DEBUGP("ip_nat_standalone: adjusting sequence number\n"); - if (!ip_nat_seq_adjust(pskb, ct, ctinfo)) - return NF_DROP; - } - return NF_ACCEPT; -} - -/* We must be after connection tracking and before packet filtering. */ - -static struct nf_hook_ops ip_nat_ops[] = { - /* Before packet filtering, change destination */ - { - .hook = ip_nat_in, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_PRE_ROUTING, - .priority = NF_IP_PRI_NAT_DST, - }, - /* After packet filtering, change source */ - { - .hook = ip_nat_out, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_NAT_SRC, - }, - /* After conntrack, adjust sequence number */ - { - .hook = ip_nat_adjust, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_NAT_SEQ_ADJUST, - }, - /* Before packet filtering, change destination */ - { - .hook = ip_nat_local_fn, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_OUT, - .priority = NF_IP_PRI_NAT_DST, - }, - /* After packet filtering, change source */ - { - .hook = ip_nat_fn, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_NAT_SRC, - }, - /* After conntrack, adjust sequence number */ - { - .hook = ip_nat_adjust, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_NAT_SEQ_ADJUST, - }, -}; - -static int __init ip_nat_standalone_init(void) -{ - int ret = 0; - - need_conntrack(); - -#ifdef CONFIG_XFRM - BUG_ON(ip_nat_decode_session != NULL); - ip_nat_decode_session = nat_decode_session; -#endif - ret = ip_nat_rule_init(); - if (ret < 0) { - printk("ip_nat_init: can't setup rules.\n"); - goto cleanup_decode_session; - } - ret = nf_register_hooks(ip_nat_ops, ARRAY_SIZE(ip_nat_ops)); - if (ret < 0) { - printk("ip_nat_init: can't register hooks.\n"); - goto cleanup_rule_init; - } - return ret; - - cleanup_rule_init: - ip_nat_rule_cleanup(); - cleanup_decode_session: -#ifdef CONFIG_XFRM - ip_nat_decode_session = NULL; - synchronize_net(); -#endif - return ret; -} - -static void __exit ip_nat_standalone_fini(void) -{ - nf_unregister_hooks(ip_nat_ops, ARRAY_SIZE(ip_nat_ops)); - ip_nat_rule_cleanup(); -#ifdef CONFIG_XFRM - ip_nat_decode_session = NULL; - synchronize_net(); -#endif -} - -module_init(ip_nat_standalone_init); -module_exit(ip_nat_standalone_fini); - -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/ip_nat_tftp.c b/net/ipv4/netfilter/ip_nat_tftp.c deleted file mode 100644 index 6047935..0000000 --- a/net/ipv4/netfilter/ip_nat_tftp.c +++ /dev/null @@ -1,70 +0,0 @@ -/* (C) 2001-2002 Magnus Boden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Version: 0.0.7 - * - * Thu 21 Mar 2002 Harald Welte - * - Port to newnat API - * - * This module currently supports DNAT: - * iptables -t nat -A PREROUTING -d x.x.x.x -j DNAT --to-dest x.x.x.y - * - * and SNAT: - * iptables -t nat -A POSTROUTING { -j MASQUERADE , -j SNAT --to-source x.x.x.x } - * - * It has not been tested with - * -j SNAT --to-source x.x.x.x-x.x.x.y since I only have one external ip - * If you do test this please let me know if it works or not. - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Magnus Boden "); -MODULE_DESCRIPTION("tftp NAT helper"); -MODULE_LICENSE("GPL"); - -static unsigned int help(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp) -{ - struct ip_conntrack *ct = exp->master; - - exp->saved_proto.udp.port - = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; - exp->dir = IP_CT_DIR_REPLY; - exp->expectfn = ip_nat_follow_master; - if (ip_conntrack_expect_related(exp) != 0) - return NF_DROP; - return NF_ACCEPT; -} - -static void __exit ip_nat_tftp_fini(void) -{ - rcu_assign_pointer(ip_nat_tftp_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_tftp_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_tftp_hook)); - rcu_assign_pointer(ip_nat_tftp_hook, help); - return 0; -} - -module_init(ip_nat_tftp_init); -module_exit(ip_nat_tftp_fini); diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c index a14798a..702d94d 100644 --- a/net/ipv4/netfilter/ip_queue.c +++ b/net/ipv4/netfilter/ip_queue.c @@ -8,18 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 2000-03-27: Simplified code (thanks to Andi Kleen for clues). - * 2000-05-20: Fixed notifier problems (following Miguel Freitas' report). - * 2000-06-19: Fixed so nfmark is copied to metadata (reported by Sebastian - * Zander). - * 2000-08-01: Added Nick Williams' MAC support. - * 2002-06-25: Code cleanup. - * 2005-01-10: Added /proc counter for dropped packets; fixed so - * packets aren't delivered to user space if they're going - * to be dropped. - * 2005-05-26: local_bh_{disable,enable} around nf_reinject (Harald Welte) - * */ #include #include @@ -191,12 +179,13 @@ ipq_flush(int verdict) static struct sk_buff * ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) { - unsigned char *old_tail; + sk_buff_data_t old_tail; size_t size = 0; size_t data_len = 0; struct sk_buff *skb; struct ipq_packet_msg *pmsg; struct nlmsghdr *nlh; + struct timeval tv; read_lock_bh(&queue_lock); @@ -234,15 +223,16 @@ ipq_build_packet_message(struct ipq_queu if (!skb) goto nlmsg_failure; - old_tail= skb->tail; + old_tail = skb->tail; nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); pmsg = NLMSG_DATA(nlh); memset(pmsg, 0, sizeof(*pmsg)); pmsg->packet_id = (unsigned long )entry; pmsg->data_len = data_len; - pmsg->timestamp_sec = entry->skb->tstamp.off_sec; - pmsg->timestamp_usec = entry->skb->tstamp.off_usec; + tv = ktime_to_timeval(entry->skb->tstamp); + pmsg->timestamp_sec = tv.tv_sec; + pmsg->timestamp_usec = tv.tv_usec; pmsg->mark = entry->skb->mark; pmsg->hook = entry->info->hook; pmsg->hw_protocol = entry->skb->protocol; @@ -378,7 +368,7 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, st } if (!skb_make_writable(&e->skb, v->data_len)) return -ENOMEM; - memcpy(e->skb->data, v->payload, v->data_len); + skb_copy_to_linear_data(e->skb, v->payload, v->data_len); e->skb->ip_summed = CHECKSUM_NONE; return 0; @@ -495,7 +485,7 @@ ipq_rcv_skb(struct sk_buff *skb) if (skblen < sizeof(*nlh)) return; - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); nlmsglen = nlh->nlmsg_len; if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) return; @@ -678,7 +668,7 @@ static int __init ip_queue_init(void) netlink_register_notifier(&ipq_nl_notifier); ipqnl = netlink_kernel_create(NETLINK_FIREWALL, 0, ipq_rcv_sk, - THIS_MODULE); + NULL, THIS_MODULE); if (ipqnl == NULL) { printk(KERN_ERR "ip_queue: failed to create netlink socket\n"); goto cleanup_netlink_notifier; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 50cc4b9..e3f83bf 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -7,12 +7,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 19 Jan 2002 Harald Welte - * - increase module usage count as soon as we have rules inside - * a table - * 08 Oct 2005 Harald Welte - * - Generalize into "x_tables" layer and "{ip,ip6,arp}_tables" */ #include #include @@ -198,7 +192,7 @@ int do_match(struct ipt_entry_match *m, { /* Stop iteration if it doesn't match */ if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data, - offset, skb->nh.iph->ihl*4, hotdrop)) + offset, ip_hdrlen(skb), hotdrop)) return 1; else return 0; @@ -231,7 +225,7 @@ ipt_do_table(struct sk_buff **pskb, struct xt_table_info *private; /* Initialization */ - ip = (*pskb)->nh.iph; + ip = ip_hdr(*pskb); datalen = (*pskb)->len - ip->ihl * 4; indev = in ? in->name : nulldevname; outdev = out ? out->name : nulldevname; @@ -320,7 +314,7 @@ #ifdef CONFIG_NETFILTER_DEBUG = 0x57acc001; #endif /* Target might have changed stuff. */ - ip = (*pskb)->nh.iph; + ip = ip_hdr(*pskb); datalen = (*pskb)->len - ip->ihl * 4; if (verdict == IPT_CONTINUE) diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 42b0802..40e2734 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -21,15 +21,12 @@ #include #include #include #include - -#include - #include - #include #include #include -#include +#include +#include #define CLUSTERIP_VERSION "0.8" @@ -240,7 +237,7 @@ #endif static inline u_int32_t clusterip_hashfn(struct sk_buff *skb, struct clusterip_config *config) { - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); unsigned long hashval; u_int16_t sport, dport; u_int16_t *ports; @@ -310,15 +307,16 @@ target(struct sk_buff **pskb, const void *targinfo) { const struct ipt_clusterip_tgt_info *cipinfo = targinfo; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; - u_int32_t *mark, hash; + u_int32_t hash; /* don't need to clusterip_config_get() here, since refcount * is only decremented by destroy() - and ip_tables guarantees * that the ->target() function isn't called after ->destroy() */ - mark = nf_ct_get_mark((*pskb), &ctinfo); - if (mark == NULL) { + ct = nf_ct_get(*pskb, &ctinfo); + if (ct == NULL) { printk(KERN_ERR "CLUSTERIP: no conntrack!\n"); /* FIXME: need to drop invalid ones, since replies * to outgoing connections of other nodes will be @@ -328,7 +326,7 @@ target(struct sk_buff **pskb, /* special case: ICMP error handling. conntrack distinguishes between * error messages (RELATED) and information requests (see below) */ - if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP + if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP && (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED+IP_CT_IS_REPLY)) return XT_CONTINUE; @@ -341,7 +339,7 @@ target(struct sk_buff **pskb, switch (ctinfo) { case IP_CT_NEW: - *mark = hash; + ct->mark = hash; break; case IP_CT_RELATED: case IP_CT_RELATED+IP_CT_IS_REPLY: @@ -358,7 +356,7 @@ target(struct sk_buff **pskb, #ifdef DEBUG_CLUSTERP DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); #endif - DEBUGP("hash=%u ct_hash=%u ", hash, *mark); + DEBUGP("hash=%u ct_hash=%u ", hash, ct->mark); if (!clusterip_responsible(cipinfo->config, hash)) { DEBUGP("not responsible\n"); return NF_DROP; @@ -521,7 +519,7 @@ arp_mangle(unsigned int hook, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - struct arphdr *arp = (*pskb)->nh.arph; + struct arphdr *arp = arp_hdr(*pskb); struct arp_payload *payload; struct clusterip_config *c; diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c index 4f56563..918ca92 100644 --- a/net/ipv4/netfilter/ipt_ECN.c +++ b/net/ipv4/netfilter/ipt_ECN.c @@ -5,14 +5,13 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * ipt_ECN.c,v 1.5 2002/08/18 19:36:51 laforge Exp */ #include #include #include #include +#include #include #include @@ -29,13 +28,13 @@ MODULE_DESCRIPTION("iptables ECN modific static inline int set_ect_ip(struct sk_buff **pskb, const struct ipt_ECN_info *einfo) { - struct iphdr *iph = (*pskb)->nh.iph; + struct iphdr *iph = ip_hdr(*pskb); if ((iph->tos & IPT_ECN_IP_MASK) != (einfo->ip_ect & IPT_ECN_IP_MASK)) { __u8 oldtos; if (!skb_make_writable(pskb, sizeof(struct iphdr))) return 0; - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); oldtos = iph->tos; iph->tos &= ~IPT_ECN_IP_MASK; iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK); @@ -52,7 +51,7 @@ set_ect_tcp(struct sk_buff **pskb, const __be16 oldval; /* Not enought header? */ - tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, + tcph = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(_tcph), &_tcph); if (!tcph) return 0; @@ -63,9 +62,9 @@ set_ect_tcp(struct sk_buff **pskb, const tcph->cwr == einfo->proto.tcp.cwr))) return 1; - if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph))) + if (!skb_make_writable(pskb, ip_hdrlen(*pskb) + sizeof(*tcph))) return 0; - tcph = (void *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl*4; + tcph = (void *)ip_hdr(*pskb) + ip_hdrlen(*pskb); oldval = ((__be16 *)tcph)[6]; if (einfo->operation & IPT_ECN_OP_SET_ECE) @@ -93,7 +92,7 @@ target(struct sk_buff **pskb, return NF_DROP; if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR) - && (*pskb)->nh.iph->protocol == IPPROTO_TCP) + && ip_hdr(*pskb)->protocol == IPPROTO_TCP) if (!set_ect_tcp(pskb, einfo)) return NF_DROP; diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c index d9c37fd..a42c5cd 100644 --- a/net/ipv4/netfilter/ipt_LOG.c +++ b/net/ipv4/netfilter/ipt_LOG.c @@ -399,9 +399,9 @@ #endif /* MAC logging for input chain only. */ printk("MAC="); if (skb->dev && skb->dev->hard_header_len - && skb->mac.raw != (void*)skb->nh.iph) { + && skb->mac_header != skb->network_header) { int i; - unsigned char *p = skb->mac.raw; + const unsigned char *p = skb_mac_header(skb); for (i = 0; i < skb->dev->hard_header_len; i++,p++) printk("%02x%c", *p, i==skb->dev->hard_header_len - 1 @@ -477,14 +477,10 @@ static int __init ipt_log_init(void) ret = xt_register_target(&ipt_log_reg); if (ret < 0) return ret; - if (nf_log_register(PF_INET, &ipt_log_logger) < 0) { - printk(KERN_WARNING "ipt_LOG: not logging via system console " - "since somebody else already registered for PF_INET\n"); - /* we cannot make module load fail here, since otherwise - * iptables userspace would abort */ - } - - return 0; + ret = nf_log_register(PF_INET, &ipt_log_logger); + if (ret < 0 && ret != -EEXIST) + xt_unregister_target(&ipt_log_reg); + return ret; } static void __exit ipt_log_fini(void) diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index b5955f3..d4f2d77 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -19,12 +19,8 @@ #include #include #include #include -#include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif +#include #include MODULE_LICENSE("GPL"); @@ -48,7 +44,7 @@ masquerade_check(const char *tablename, void *targinfo, unsigned int hook_mask) { - const struct ip_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = targinfo; if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { DEBUGP("masquerade_check: bad MAP_IPS.\n"); @@ -69,33 +65,26 @@ masquerade_target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { -#ifdef CONFIG_NF_NAT_NEEDED + struct nf_conn *ct; struct nf_conn_nat *nat; -#endif - struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; - struct ip_nat_range newrange; - const struct ip_nat_multi_range_compat *mr; + struct nf_nat_range newrange; + const struct nf_nat_multi_range_compat *mr; struct rtable *rt; __be32 newsrc; - IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING); + NF_CT_ASSERT(hooknum == NF_IP_POST_ROUTING); - ct = ip_conntrack_get(*pskb, &ctinfo); -#ifdef CONFIG_NF_NAT_NEEDED + ct = nf_ct_get(*pskb, &ctinfo); nat = nfct_nat(ct); -#endif - IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED + + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); /* Source address is 0.0.0.0 - locally generated packet that is * probably not supposed to be masqueraded. */ -#ifdef CONFIG_NF_NAT_NEEDED if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) -#else - if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip == 0) -#endif return NF_ACCEPT; mr = targinfo; @@ -107,40 +96,30 @@ #endif } write_lock_bh(&masq_lock); -#ifdef CONFIG_NF_NAT_NEEDED nat->masq_index = out->ifindex; -#else - ct->nat.masq_index = out->ifindex; -#endif write_unlock_bh(&masq_lock); /* Transfer from original range. */ - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, newsrc, newsrc, mr->range[0].min, mr->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static inline int -device_cmp(struct ip_conntrack *i, void *ifindex) +device_cmp(struct nf_conn *i, void *ifindex) { - int ret; -#ifdef CONFIG_NF_NAT_NEEDED struct nf_conn_nat *nat = nfct_nat(i); + int ret; if (!nat) return 0; -#endif read_lock_bh(&masq_lock); -#ifdef CONFIG_NF_NAT_NEEDED ret = (nat->masq_index == (int)(long)ifindex); -#else - ret = (i->nat.masq_index == (int)(long)ifindex); -#endif read_unlock_bh(&masq_lock); return ret; @@ -156,9 +135,9 @@ static int masq_device_event(struct noti /* Device was downed. Search entire table for conntracks which were associated with that device, and forget them. */ - IP_NF_ASSERT(dev->ifindex != 0); + NF_CT_ASSERT(dev->ifindex != 0); - ip_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); + nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); } return NOTIFY_DONE; @@ -174,9 +153,9 @@ static int masq_inet_event(struct notifi /* IP address was deleted. Search entire table for conntracks which were associated with that device, and forget them. */ - IP_NF_ASSERT(dev->ifindex != 0); + NF_CT_ASSERT(dev->ifindex != 0); - ip_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); + nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); } return NOTIFY_DONE; @@ -194,7 +173,7 @@ static struct xt_target masquerade = { .name = "MASQUERADE", .family = AF_INET, .target = masquerade_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), + .targetsize = sizeof(struct nf_nat_multi_range_compat), .table = "nat", .hooks = 1 << NF_IP_POST_ROUTING, .checkentry = masquerade_check, diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index fd7aaa3..068c69b 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -16,11 +16,7 @@ #include #include #include #include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif #define MODULENAME "NETMAP" MODULE_LICENSE("GPL"); @@ -40,7 +36,7 @@ check(const char *tablename, void *targinfo, unsigned int hook_mask) { - const struct ip_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = targinfo; if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) { DEBUGP(MODULENAME":check: bad MAP_IPS.\n"); @@ -61,39 +57,39 @@ target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct ip_conntrack *ct; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; __be32 new_ip, netmask; - const struct ip_nat_multi_range_compat *mr = targinfo; - struct ip_nat_range newrange; + const struct nf_nat_multi_range_compat *mr = targinfo; + struct nf_nat_range newrange; - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING + NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_POST_ROUTING || hooknum == NF_IP_LOCAL_OUT); - ct = ip_conntrack_get(*pskb, &ctinfo); + ct = nf_ct_get(*pskb, &ctinfo); netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); if (hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_LOCAL_OUT) - new_ip = (*pskb)->nh.iph->daddr & ~netmask; + new_ip = ip_hdr(*pskb)->daddr & ~netmask; else - new_ip = (*pskb)->nh.iph->saddr & ~netmask; + new_ip = ip_hdr(*pskb)->saddr & ~netmask; new_ip |= mr->range[0].min_ip & netmask; - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, new_ip, new_ip, mr->range[0].min, mr->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static struct xt_target target_module = { .name = MODULENAME, .family = AF_INET, .target = target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), + .targetsize = sizeof(struct nf_nat_multi_range_compat), .table = "nat", .hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_POST_ROUTING) | (1 << NF_IP_LOCAL_OUT), diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c index c2b6b80..68cc76a 100644 --- a/net/ipv4/netfilter/ipt_REDIRECT.c +++ b/net/ipv4/netfilter/ipt_REDIRECT.c @@ -19,11 +19,7 @@ #include #include #include #include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); @@ -43,7 +39,7 @@ redirect_check(const char *tablename, void *targinfo, unsigned int hook_mask) { - const struct ip_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = targinfo; if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { DEBUGP("redirect_check: bad MAP_IPS.\n"); @@ -64,17 +60,17 @@ redirect_target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct ip_conntrack *ct; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; __be32 newdst; - const struct ip_nat_multi_range_compat *mr = targinfo; - struct ip_nat_range newrange; + const struct nf_nat_multi_range_compat *mr = targinfo; + struct nf_nat_range newrange; - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING + NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_LOCAL_OUT); - ct = ip_conntrack_get(*pskb, &ctinfo); - IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + ct = nf_ct_get(*pskb, &ctinfo); + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); /* Local packets: make them go to loopback */ if (hooknum == NF_IP_LOCAL_OUT) @@ -96,20 +92,20 @@ redirect_target(struct sk_buff **pskb, } /* Transfer from original range. */ - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, newdst, newdst, mr->range[0].min, mr->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static struct xt_target redirect_reg = { .name = "REDIRECT", .family = AF_INET, .target = redirect_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), + .targetsize = sizeof(struct nf_nat_multi_range_compat), .table = "nat", .hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT), .checkentry = redirect_check, diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 80f739e..9041e07 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -1,7 +1,5 @@ /* * This is a module which is used for rejecting packets. - * Added support for customized reject packets (Jozsef Kadlecsik). - * Added support for ICMP type-3-code-13 (Maciej Soltysiak). [RFC 1812] */ /* (C) 1999-2001 Paul `Rusty' Russell @@ -43,7 +41,7 @@ #endif static void send_reset(struct sk_buff *oldskb, int hook) { struct sk_buff *nskb; - struct iphdr *iph = oldskb->nh.iph; + struct iphdr *niph; struct tcphdr _otcph, *oth, *tcph; __be16 tmp_port; __be32 tmp_addr; @@ -51,10 +49,10 @@ static void send_reset(struct sk_buff *o unsigned int addr_type; /* IP header checks: fragment. */ - if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)) + if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) return; - oth = skb_header_pointer(oldskb, oldskb->nh.iph->ihl * 4, + oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), sizeof(_otcph), &_otcph); if (oth == NULL) return; @@ -64,7 +62,7 @@ static void send_reset(struct sk_buff *o return; /* Check checksum */ - if (nf_ip_checksum(oldskb, hook, iph->ihl * 4, IPPROTO_TCP)) + if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) return; /* We need a linear, writeable skb. We also need to expand @@ -84,20 +82,21 @@ static void send_reset(struct sk_buff *o skb_shinfo(nskb)->gso_segs = 0; skb_shinfo(nskb)->gso_type = 0; - tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); + tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); /* Swap source and dest */ - tmp_addr = nskb->nh.iph->saddr; - nskb->nh.iph->saddr = nskb->nh.iph->daddr; - nskb->nh.iph->daddr = tmp_addr; + niph = ip_hdr(nskb); + tmp_addr = niph->saddr; + niph->saddr = niph->daddr; + niph->daddr = tmp_addr; tmp_port = tcph->source; tcph->source = tcph->dest; tcph->dest = tmp_port; /* Truncate to length (no data) */ tcph->doff = sizeof(struct tcphdr)/4; - skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr)); - nskb->nh.iph->tot_len = htons(nskb->len); + skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); + niph->tot_len = htons(nskb->len); if (tcph->ack) { needs_ack = 0; @@ -105,9 +104,9 @@ static void send_reset(struct sk_buff *o tcph->ack_seq = 0; } else { needs_ack = 1; - tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin - + oldskb->len - oldskb->nh.iph->ihl*4 - - (oth->doff<<2)); + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + + oldskb->len - ip_hdrlen(oldskb) - + (oth->doff << 2)); tcph->seq = 0; } @@ -122,14 +121,13 @@ static void send_reset(struct sk_buff *o /* Adjust TCP checksum */ tcph->check = 0; tcph->check = tcp_v4_check(sizeof(struct tcphdr), - nskb->nh.iph->saddr, - nskb->nh.iph->daddr, + niph->saddr, niph->daddr, csum_partial((char *)tcph, sizeof(struct tcphdr), 0)); /* Set DF, id = 0 */ - nskb->nh.iph->frag_off = htons(IP_DF); - nskb->nh.iph->id = 0; + niph->frag_off = htons(IP_DF); + niph->id = 0; addr_type = RTN_UNSPEC; if (hook != NF_IP_FORWARD @@ -145,12 +143,11 @@ #endif nskb->ip_summed = CHECKSUM_NONE; /* Adjust IP TTL */ - nskb->nh.iph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); + niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); /* Adjust IP checksum */ - nskb->nh.iph->check = 0; - nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, - nskb->nh.iph->ihl); + niph->check = 0; + niph->check = ip_fast_csum(skb_network_header(nskb), niph->ihl); /* "Never happens" */ if (nskb->len > dst_mtu(nskb->dst)) @@ -182,7 +179,7 @@ static unsigned int reject(struct sk_buf /* Our naive response construction doesn't deal with IP options, and probably shouldn't try. */ - if ((*pskb)->nh.iph->ihl<<2 != sizeof(struct iphdr)) + if (ip_hdrlen(*pskb) != sizeof(struct iphdr)) return NF_DROP; /* WARNING: This code causes reentry within iptables. diff --git a/net/ipv4/netfilter/ipt_SAME.c b/net/ipv4/netfilter/ipt_SAME.c index bd4404e..511e5ff 100644 --- a/net/ipv4/netfilter/ipt_SAME.c +++ b/net/ipv4/netfilter/ipt_SAME.c @@ -7,21 +7,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 010320 Martin Josefsson - * * copied ipt_BALANCE.c to ipt_SAME.c and changed a few things. - * 010728 Martin Josefsson - * * added --nodst to not include destination-ip in new source - * calculations. - * * added some more sanity-checks. - * 010729 Martin Josefsson - * * fixed a buggy if-statement in same_check(), should have - * used ntohl() but didn't. - * * added support for multiple ranges. IPT_SAME_MAX_RANGE is - * defined in linux/include/linux/netfilter_ipv4/ipt_SAME.h - * and is currently set to 10. - * * added support for 1-address range, nice to have now that - * we have multiple ranges. */ #include #include @@ -35,11 +20,7 @@ #include #include #include #include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif #include MODULE_LICENSE("GPL"); @@ -138,17 +119,17 @@ same_target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct ip_conntrack *ct; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; u_int32_t tmpip, aindex; __be32 new_ip; const struct ipt_same_info *same = targinfo; - struct ip_nat_range newrange; - const struct ip_conntrack_tuple *t; + struct nf_nat_range newrange; + const struct nf_conntrack_tuple *t; - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING || + NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_POST_ROUTING); - ct = ip_conntrack_get(*pskb, &ctinfo); + ct = nf_ct_get(*pskb, &ctinfo); t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; @@ -157,17 +138,10 @@ same_target(struct sk_buff **pskb, Here we calculate the index in same->iparray which holds the ipaddress we should use */ -#ifdef CONFIG_NF_NAT_NEEDED tmpip = ntohl(t->src.u3.ip); if (!(same->info & IPT_SAME_NODST)) tmpip += ntohl(t->dst.u3.ip); -#else - tmpip = ntohl(t->src.ip); - - if (!(same->info & IPT_SAME_NODST)) - tmpip += ntohl(t->dst.ip); -#endif aindex = tmpip % same->ipnum; new_ip = htonl(same->iparray[aindex]); @@ -178,13 +152,13 @@ #endif NIPQUAD(new_ip)); /* Transfer from original range. */ - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { same->range[0].flags, new_ip, new_ip, /* FIXME: Use ports from correct range! */ same->range[0].min, same->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static struct xt_target same_reg = { diff --git a/net/ipv4/netfilter/ipt_TOS.c b/net/ipv4/netfilter/ipt_TOS.c index cedf9f7..0ad02f2 100644 --- a/net/ipv4/netfilter/ipt_TOS.c +++ b/net/ipv4/netfilter/ipt_TOS.c @@ -29,13 +29,13 @@ target(struct sk_buff **pskb, const void *targinfo) { const struct ipt_tos_target_info *tosinfo = targinfo; - struct iphdr *iph = (*pskb)->nh.iph; + struct iphdr *iph = ip_hdr(*pskb); if ((iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) { __u8 oldtos; if (!skb_make_writable(pskb, sizeof(struct iphdr))) return NF_DROP; - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); oldtos = iph->tos; iph->tos = (iph->tos & IPTOS_PREC_MASK) | tosinfo->tos; nf_csum_replace2(&iph->check, htons(oldtos), htons(iph->tos)); diff --git a/net/ipv4/netfilter/ipt_TTL.c b/net/ipv4/netfilter/ipt_TTL.c index 64be31c..a991ec7 100644 --- a/net/ipv4/netfilter/ipt_TTL.c +++ b/net/ipv4/netfilter/ipt_TTL.c @@ -32,7 +32,7 @@ ipt_ttl_target(struct sk_buff **pskb, if (!skb_make_writable(pskb, (*pskb)->len)) return NF_DROP; - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); switch (info->mode) { case IPT_TTL_SET: diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 9acc018..23b607b 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -2,20 +2,6 @@ * netfilter module for userspace packet logging daemons * * (C) 2000-2004 by Harald Welte - * - * 2000/09/22 ulog-cprange feature added - * 2001/01/04 in-kernel queue as proposed by Sebastian Zander - * - * 2001/01/30 per-rule nlgroup conflicts with global queue. - * nlgroup now global (sysctl) - * 2001/04/19 ulog-queue reworked, now fixed buffer size specified at - * module loadtime -HW - * 2002/07/07 remove broken nflog_rcv() function -HW - * 2002/08/29 fix shifted/unshifted nlgroup bug -HW - * 2002/10/30 fix uninitialized mac_len field - - * 2004/10/25 fix erroneous calculation of 'len' parameter to NLMSG_PUT - * resulting in bogus 'error during NLMSG_PUT' messages. - * * (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2004 Netfilter Core Team * @@ -42,8 +28,6 @@ * flushtimeout: * Specify, after how many hundredths of a second the queue should be * flushed even if it is not full yet. - * - * ipt_ULOG.c,v 1.22 2002/10/30 09:07:31 laforge Exp */ #include @@ -187,6 +171,7 @@ static void ipt_ulog_packet(unsigned int ulog_packet_msg_t *pm; size_t size, copy_len; struct nlmsghdr *nlh; + struct timeval tv; /* ffs == find first bit set, necessary because userspace * is already shifting groupnumber, but we need unshifted. @@ -232,13 +217,14 @@ static void ipt_ulog_packet(unsigned int pm = NLMSG_DATA(nlh); /* We might not have a timestamp, get one */ - if (skb->tstamp.off_sec == 0) + if (skb->tstamp.tv64 == 0) __net_timestamp((struct sk_buff *)skb); /* copy hook, prefix, timestamp, payload, etc. */ pm->data_len = copy_len; - put_unaligned(skb->tstamp.off_sec, &pm->timestamp_sec); - put_unaligned(skb->tstamp.off_usec, &pm->timestamp_usec); + tv = ktime_to_timeval(skb->tstamp); + put_unaligned(tv.tv_sec, &pm->timestamp_sec); + put_unaligned(tv.tv_usec, &pm->timestamp_usec); put_unaligned(skb->mark, &pm->mark); pm->hook = hooknum; if (prefix != NULL) @@ -249,9 +235,9 @@ static void ipt_ulog_packet(unsigned int *(pm->prefix) = '\0'; if (in && in->hard_header_len > 0 - && skb->mac.raw != (void *) skb->nh.iph + && skb->mac_header != skb->network_header && in->hard_header_len <= ULOG_MAC_LEN) { - memcpy(pm->mac, skb->mac.raw, in->hard_header_len); + memcpy(pm->mac, skb_mac_header(skb), in->hard_header_len); pm->mac_len = in->hard_header_len; } else pm->mac_len = 0; @@ -363,12 +349,52 @@ static int ipt_ulog_checkentry(const cha return 1; } +#ifdef CONFIG_COMPAT +struct compat_ipt_ulog_info { + compat_uint_t nl_group; + compat_size_t copy_range; + compat_size_t qthreshold; + char prefix[ULOG_PREFIX_LEN]; +}; + +static void compat_from_user(void *dst, void *src) +{ + struct compat_ipt_ulog_info *cl = src; + struct ipt_ulog_info l = { + .nl_group = cl->nl_group, + .copy_range = cl->copy_range, + .qthreshold = cl->qthreshold, + }; + + memcpy(l.prefix, cl->prefix, sizeof(l.prefix)); + memcpy(dst, &l, sizeof(l)); +} + +static int compat_to_user(void __user *dst, void *src) +{ + struct ipt_ulog_info *l = src; + struct compat_ipt_ulog_info cl = { + .nl_group = l->nl_group, + .copy_range = l->copy_range, + .qthreshold = l->qthreshold, + }; + + memcpy(cl.prefix, l->prefix, sizeof(cl.prefix)); + return copy_to_user(dst, &cl, sizeof(cl)) ? -EFAULT : 0; +} +#endif /* CONFIG_COMPAT */ + static struct xt_target ipt_ulog_reg = { .name = "ULOG", .family = AF_INET, .target = ipt_ulog_target, .targetsize = sizeof(struct ipt_ulog_info), .checkentry = ipt_ulog_checkentry, +#ifdef CONFIG_COMPAT + .compatsize = sizeof(struct compat_ipt_ulog_info), + .compat_from_user = compat_from_user, + .compat_to_user = compat_to_user, +#endif .me = THIS_MODULE, }; @@ -390,14 +416,11 @@ static int __init ipt_ulog_init(void) } /* initialize ulog_buffers */ - for (i = 0; i < ULOG_MAXNLGROUPS; i++) { - init_timer(&ulog_buffers[i].timer); - ulog_buffers[i].timer.function = ulog_timer; - ulog_buffers[i].timer.data = i; - } + for (i = 0; i < ULOG_MAXNLGROUPS; i++) + setup_timer(&ulog_buffers[i].timer, ulog_timer, i); nflognl = netlink_kernel_create(NETLINK_NFLOG, ULOG_MAXNLGROUPS, NULL, - THIS_MODULE); + NULL, THIS_MODULE); if (!nflognl) return -ENOMEM; diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c index cfa0472..a652a14 100644 --- a/net/ipv4/netfilter/ipt_addrtype.c +++ b/net/ipv4/netfilter/ipt_addrtype.c @@ -33,7 +33,7 @@ static int match(const struct sk_buff *s int offset, unsigned int protoff, int *hotdrop) { const struct ipt_addrtype_info *info = matchinfo; - const struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); int ret = 1; if (info->source) diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index 37508b2..2621812 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -1,7 +1,5 @@ /* IP tables module for matching the value of the IPv4 and TCP ECN bits * - * ipt_ecn.c,v 1.3 2002/05/29 15:09:00 laforge Exp - * * (C) 2002 by Harald Welte * * This program is free software; you can redistribute it and/or modify @@ -11,6 +9,7 @@ #include #include +#include #include #include #include @@ -26,7 +25,7 @@ MODULE_LICENSE("GPL"); static inline int match_ip(const struct sk_buff *skb, const struct ipt_ecn_info *einfo) { - return ((skb->nh.iph->tos&IPT_ECN_IP_MASK) == einfo->ip_ect); + return (ip_hdr(skb)->tos & IPT_ECN_IP_MASK) == einfo->ip_ect; } static inline int match_tcp(const struct sk_buff *skb, @@ -38,8 +37,7 @@ static inline int match_tcp(const struct /* In practice, TCP match does this, so can't fail. But let's * be good citizens. */ - th = skb_header_pointer(skb, skb->nh.iph->ihl * 4, - sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); if (th == NULL) { *hotdrop = 0; return 0; @@ -80,7 +78,7 @@ static int match(const struct sk_buff *s return 0; if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { - if (skb->nh.iph->protocol != IPPROTO_TCP) + if (ip_hdr(skb)->protocol != IPPROTO_TCP) return 0; if (!match_tcp(skb, info, hotdrop)) return 0; diff --git a/net/ipv4/netfilter/ipt_iprange.c b/net/ipv4/netfilter/ipt_iprange.c index bc5d5e6..33af9e9 100644 --- a/net/ipv4/netfilter/ipt_iprange.c +++ b/net/ipv4/netfilter/ipt_iprange.c @@ -32,7 +32,7 @@ match(const struct sk_buff *skb, int offset, unsigned int protoff, int *hotdrop) { const struct ipt_iprange_info *info = matchinfo; - const struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); if (info->flags & IPRANGE_SRC) { if (((ntohl(iph->saddr) < ntohl(info->src.min_ip)) diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c index aecb9c4..15a9e8b 100644 --- a/net/ipv4/netfilter/ipt_recent.c +++ b/net/ipv4/netfilter/ipt_recent.c @@ -183,11 +183,11 @@ ipt_recent_match(const struct sk_buff *s int ret = info->invert; if (info->side == IPT_RECENT_DEST) - addr = skb->nh.iph->daddr; + addr = ip_hdr(skb)->daddr; else - addr = skb->nh.iph->saddr; + addr = ip_hdr(skb)->saddr; - ttl = skb->nh.iph->ttl; + ttl = ip_hdr(skb)->ttl; /* use TTL as seen before forwarding */ if (out && !skb->sk) ttl++; diff --git a/net/ipv4/netfilter/ipt_tos.c b/net/ipv4/netfilter/ipt_tos.c index 5d33b51..d314844 100644 --- a/net/ipv4/netfilter/ipt_tos.c +++ b/net/ipv4/netfilter/ipt_tos.c @@ -30,7 +30,7 @@ match(const struct sk_buff *skb, { const struct ipt_tos_info *info = matchinfo; - return (skb->nh.iph->tos == info->tos) ^ info->invert; + return (ip_hdr(skb)->tos == info->tos) ^ info->invert; } static struct xt_match tos_match = { diff --git a/net/ipv4/netfilter/ipt_ttl.c b/net/ipv4/netfilter/ipt_ttl.c index 1eca9f4..ab02d9e 100644 --- a/net/ipv4/netfilter/ipt_ttl.c +++ b/net/ipv4/netfilter/ipt_ttl.c @@ -1,7 +1,5 @@ /* IP tables module for matching the value of the TTL * - * ipt_ttl.c,v 1.5 2000/11/13 11:16:08 laforge Exp - * * (C) 2000,2001 by Harald Welte * * This program is free software; you can redistribute it and/or modify @@ -26,19 +24,20 @@ static int match(const struct sk_buff *s int offset, unsigned int protoff, int *hotdrop) { const struct ipt_ttl_info *info = matchinfo; + const u8 ttl = ip_hdr(skb)->ttl; switch (info->mode) { case IPT_TTL_EQ: - return (skb->nh.iph->ttl == info->ttl); + return (ttl == info->ttl); break; case IPT_TTL_NE: - return (!(skb->nh.iph->ttl == info->ttl)); + return (!(ttl == info->ttl)); break; case IPT_TTL_LT: - return (skb->nh.iph->ttl < info->ttl); + return (ttl < info->ttl); break; case IPT_TTL_GT: - return (skb->nh.iph->ttl > info->ttl); + return (ttl > info->ttl); break; default: printk(KERN_WARNING "ipt_ttl: unknown mode %d\n", diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index d1d61e9..4272890 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -13,6 +13,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); @@ -102,7 +103,7 @@ ipt_local_out_hook(unsigned int hook, { /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { + || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { if (net_ratelimit()) printk("ipt_hook: happy cracking.\n"); return NF_ACCEPT; diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 98b66ef..9278802 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -7,8 +7,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * Extended to all five netfilter hooks by Brad Chapman & Harald Welte */ #include #include @@ -17,6 +15,7 @@ #include #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); @@ -130,13 +129,14 @@ ipt_local_hook(unsigned int hook, int (*okfn)(struct sk_buff *)) { unsigned int ret; + const struct iphdr *iph; u_int8_t tos; __be32 saddr, daddr; u_int32_t mark; /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { + || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { if (net_ratelimit()) printk("ipt_hook: happy cracking.\n"); return NF_ACCEPT; @@ -144,19 +144,23 @@ ipt_local_hook(unsigned int hook, /* Save things which could affect route */ mark = (*pskb)->mark; - saddr = (*pskb)->nh.iph->saddr; - daddr = (*pskb)->nh.iph->daddr; - tos = (*pskb)->nh.iph->tos; + iph = ip_hdr(*pskb); + saddr = iph->saddr; + daddr = iph->daddr; + tos = iph->tos; ret = ipt_do_table(pskb, hook, in, out, &packet_mangler); /* Reroute for ANY change. */ - if (ret != NF_DROP && ret != NF_STOLEN && ret != NF_QUEUE - && ((*pskb)->nh.iph->saddr != saddr - || (*pskb)->nh.iph->daddr != daddr - || (*pskb)->mark != mark - || (*pskb)->nh.iph->tos != tos)) - if (ip_route_me_harder(pskb, RTN_UNSPEC)) - ret = NF_DROP; + if (ret != NF_DROP && ret != NF_STOLEN && ret != NF_QUEUE) { + iph = ip_hdr(*pskb); + + if (iph->saddr != saddr || + iph->daddr != daddr || + (*pskb)->mark != mark || + iph->tos != tos) + if (ip_route_me_harder(pskb, RTN_UNSPEC)) + ret = NF_DROP; + } return ret; } diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 8f3e92d..0654eaa 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -4,14 +4,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - move L3 protocol dependent part to this file. - * 23 Mar 2004: Yasuyuki Kozakai @USAGI - * - add get_features() to support various size of conntrack - * structures. - * - * Derived from net/ipv4/netfilter/ip_conntrack_standalone.c */ #include @@ -87,7 +79,7 @@ nf_ct_ipv4_gather_frags(struct sk_buff * local_bh_enable(); if (skb) - ip_send_check(skb->nh.iph); + ip_send_check(ip_hdr(skb)); return skb; } @@ -97,16 +89,16 @@ ipv4_prepare(struct sk_buff **pskb, unsi u_int8_t *protonum) { /* Never happen */ - if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) { + if (ip_hdr(*pskb)->frag_off & htons(IP_OFFSET)) { if (net_ratelimit()) { printk(KERN_ERR "ipv4_prepare: Frag of proto %u (hook=%u)\n", - (*pskb)->nh.iph->protocol, hooknum); + ip_hdr(*pskb)->protocol, hooknum); } return -NF_DROP; } - *dataoff = (*pskb)->nh.raw - (*pskb)->data + (*pskb)->nh.iph->ihl*4; - *protonum = (*pskb)->nh.iph->protocol; + *dataoff = skb_network_offset(*pskb) + ip_hdrlen(*pskb); + *protonum = ip_hdr(*pskb)->protocol; return NF_ACCEPT; } @@ -152,9 +144,8 @@ static unsigned int ipv4_conntrack_help( return NF_ACCEPT; return help->helper->help(pskb, - (*pskb)->nh.raw - (*pskb)->data - + (*pskb)->nh.iph->ihl*4, - ct, ctinfo); + skb_network_offset(*pskb) + ip_hdrlen(*pskb), + ct, ctinfo); } static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, @@ -171,7 +162,7 @@ #if !defined(CONFIG_IP_NF_NAT) && !defin #endif /* Gather fragments. */ - if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { + if (ip_hdr(*pskb)->frag_off & htons(IP_MF | IP_OFFSET)) { *pskb = nf_ct_ipv4_gather_frags(*pskb, hooknum == NF_IP_PRE_ROUTING ? IP_DEFRAG_CONNTRACK_IN : @@ -199,7 +190,7 @@ static unsigned int ipv4_conntrack_local { /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { + || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { if (net_ratelimit()) printk("ipt_hook: happy cracking.\n"); return NF_ACCEPT; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 5fd1e53..f4fc657 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -4,11 +4,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - enable working with Layer 3 protocol independent connection tracking. - * - * Derived from net/ipv4/netfilter/ip_conntrack_proto_icmp.c */ #include @@ -158,7 +153,7 @@ icmp_error_message(struct sk_buff *skb, NF_CT_ASSERT(skb->nfct == NULL); /* Not enough header? */ - inside = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_in), &_in); + inside = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in); if (inside == NULL) return -NF_ACCEPT; @@ -172,7 +167,7 @@ icmp_error_message(struct sk_buff *skb, /* rcu_read_lock()ed by nf_hook_slow */ innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol); - dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp); + dataoff = ip_hdrlen(skb) + sizeof(inside->icmp); /* Are they talking about one of our connections? */ if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET, inside->ip.protocol, &origtuple, @@ -227,7 +222,7 @@ icmp_error(struct sk_buff *skb, unsigned struct icmphdr _ih, *icmph; /* Not enough header? */ - icmph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_ih), &_ih); + icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih); if (icmph == NULL) { if (LOG_INVALID(IPPROTO_ICMP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index 452e9d3..ea02f00 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -431,7 +431,7 @@ int nf_nat_icmp_reply_translation(struct } *inside; struct nf_conntrack_l4proto *l4proto; struct nf_conntrack_tuple inner, target; - int hdrlen = (*pskb)->nh.iph->ihl * 4; + int hdrlen = ip_hdrlen(*pskb); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); unsigned long statusbit; enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); @@ -439,7 +439,7 @@ int nf_nat_icmp_reply_translation(struct if (!skb_make_writable(pskb, hdrlen + sizeof(*inside))) return 0; - inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; + inside = (void *)(*pskb)->data + ip_hdrlen(*pskb); /* We're actually going to mangle it beyond trivial checksum adjustment, so make sure the current checksum is correct. */ @@ -469,9 +469,9 @@ int nf_nat_icmp_reply_translation(struct l4proto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol); if (!nf_ct_get_tuple(*pskb, - (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr), - (*pskb)->nh.iph->ihl*4 + - sizeof(struct icmphdr) + inside->ip.ihl*4, + ip_hdrlen(*pskb) + sizeof(struct icmphdr), + (ip_hdrlen(*pskb) + + sizeof(struct icmphdr) + inside->ip.ihl * 4), (u_int16_t)AF_INET, inside->ip.protocol, &inner, l3proto, l4proto)) @@ -483,14 +483,14 @@ int nf_nat_icmp_reply_translation(struct packet: PREROUTING (DST manip), routing produces ICMP, goes through POSTROUTING (which must correct the DST manip). */ if (!manip_pkt(inside->ip.protocol, pskb, - (*pskb)->nh.iph->ihl*4 + sizeof(inside->icmp), + ip_hdrlen(*pskb) + sizeof(inside->icmp), &ct->tuplehash[!dir].tuple, !manip)) return 0; if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { /* Reloading "inside" here since manip_pkt inner. */ - inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; + inside = (void *)(*pskb)->data + ip_hdrlen(*pskb); inside->icmp.checksum = 0; inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen, diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index 9cbf3f9..fcebc96 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -33,7 +33,7 @@ static int set_addr(struct sk_buff **psk unsigned int addroff, __be32 ip, __be16 port) { enum ip_conntrack_info ctinfo; - struct nf_conn *ct = ip_conntrack_get(*pskb, &ctinfo); + struct nf_conn *ct = nf_ct_get(*pskb, &ctinfo); struct { __be32 ip; __be16 port; @@ -44,7 +44,7 @@ static int set_addr(struct sk_buff **psk buf.port = port; addroff += dataoff; - if ((*pskb)->nh.iph->protocol == IPPROTO_TCP) { + if (ip_hdr(*pskb)->protocol == IPPROTO_TCP) { if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo, addroff, sizeof(buf), (char *) &buf, sizeof(buf))) { @@ -55,11 +55,11 @@ static int set_addr(struct sk_buff **psk } /* Relocate data pointer */ - th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4, + th = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(_tcph), &_tcph); if (th == NULL) return -1; - *data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 + + *data = (*pskb)->data + ip_hdrlen(*pskb) + th->doff * 4 + dataoff; } else { if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo, @@ -73,8 +73,8 @@ static int set_addr(struct sk_buff **psk /* nf_nat_mangle_udp_packet uses skb_make_writable() to copy * or pull everything in a linear buffer, so we can safely * use the skb pointers now */ - *data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 + - sizeof(struct udphdr); + *data = ((*pskb)->data + ip_hdrlen(*pskb) + + sizeof(struct udphdr)); } return 0; @@ -383,7 +383,7 @@ static int nat_h245(struct sk_buff **psk static void ip_nat_q931_expect(struct nf_conn *new, struct nf_conntrack_expect *this) { - struct ip_nat_range range; + struct nf_nat_range range; if (this->tuple.src.u3.ip != 0) { /* Only accept calls from GK */ nf_nat_follow_master(new, this); diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c index 49a90c3..15b6e5c 100644 --- a/net/ipv4/netfilter/nf_nat_helper.c +++ b/net/ipv4/netfilter/nf_nat_helper.c @@ -87,12 +87,13 @@ static void mangle_contents(struct sk_bu unsigned char *data; BUG_ON(skb_is_nonlinear(skb)); - data = (unsigned char *)skb->nh.iph + dataoff; + data = skb_network_header(skb) + dataoff; /* move post-replacement */ memmove(data + match_offset + rep_len, data + match_offset + match_len, - skb->tail - (data + match_offset + match_len)); + skb->tail - (skb->network_header + dataoff + + match_offset + match_len)); /* insert data from buffer */ memcpy(data + match_offset, rep_buffer, rep_len); @@ -111,8 +112,8 @@ static void mangle_contents(struct sk_bu } /* fix IP hdr checksum information */ - skb->nh.iph->tot_len = htons(skb->len); - ip_send_check(skb->nh.iph); + ip_hdr(skb)->tot_len = htons(skb->len); + ip_send_check(ip_hdr(skb)); } /* Unusual, but possible case. */ @@ -152,6 +153,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff const char *rep_buffer, unsigned int rep_len) { + struct rtable *rt = (struct rtable *)(*pskb)->dst; struct iphdr *iph; struct tcphdr *tcph; int oldlen, datalen; @@ -166,7 +168,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff SKB_LINEAR_ASSERT(*pskb); - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); tcph = (void *)iph + iph->ihl*4; oldlen = (*pskb)->len - iph->ihl*4; @@ -175,11 +177,22 @@ nf_nat_mangle_tcp_packet(struct sk_buff datalen = (*pskb)->len - iph->ihl*4; if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - tcph->check = 0; - tcph->check = tcp_v4_check(datalen, - iph->saddr, iph->daddr, - csum_partial((char *)tcph, - datalen, 0)); + if (!(rt->rt_flags & RTCF_LOCAL) && + (*pskb)->dev->features & NETIF_F_ALL_CSUM) { + (*pskb)->ip_summed = CHECKSUM_PARTIAL; + (*pskb)->csum_start = skb_headroom(*pskb) + + skb_network_offset(*pskb) + + iph->ihl * 4; + (*pskb)->csum_offset = offsetof(struct tcphdr, check); + tcph->check = ~tcp_v4_check(datalen, + iph->saddr, iph->daddr, 0); + } else { + tcph->check = 0; + tcph->check = tcp_v4_check(datalen, + iph->saddr, iph->daddr, + csum_partial((char *)tcph, + datalen, 0)); + } } else nf_proto_csum_replace2(&tcph->check, *pskb, htons(oldlen), htons(datalen), 1); @@ -190,7 +203,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff (int)rep_len - (int)match_len, ct, ctinfo); /* Tell TCP window tracking about seq change */ - nf_conntrack_tcp_update(*pskb, (*pskb)->nh.iph->ihl*4, + nf_conntrack_tcp_update(*pskb, ip_hdrlen(*pskb), ct, CTINFO2DIR(ctinfo)); } return 1; @@ -216,12 +229,13 @@ nf_nat_mangle_udp_packet(struct sk_buff const char *rep_buffer, unsigned int rep_len) { + struct rtable *rt = (struct rtable *)(*pskb)->dst; struct iphdr *iph; struct udphdr *udph; int datalen, oldlen; /* UDP helpers might accidentally mangle the wrong packet */ - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); if ((*pskb)->len < iph->ihl*4 + sizeof(*udph) + match_offset + match_len) return 0; @@ -234,7 +248,7 @@ nf_nat_mangle_udp_packet(struct sk_buff !enlarge_skb(pskb, rep_len - match_len)) return 0; - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); udph = (void *)iph + iph->ihl*4; oldlen = (*pskb)->len - iph->ihl*4; @@ -250,13 +264,25 @@ nf_nat_mangle_udp_packet(struct sk_buff return 1; if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - udph->check = 0; - udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, - datalen, IPPROTO_UDP, - csum_partial((char *)udph, - datalen, 0)); - if (!udph->check) - udph->check = CSUM_MANGLED_0; + if (!(rt->rt_flags & RTCF_LOCAL) && + (*pskb)->dev->features & NETIF_F_ALL_CSUM) { + (*pskb)->ip_summed = CHECKSUM_PARTIAL; + (*pskb)->csum_start = skb_headroom(*pskb) + + skb_network_offset(*pskb) + + iph->ihl * 4; + (*pskb)->csum_offset = offsetof(struct udphdr, check); + udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, + datalen, IPPROTO_UDP, + 0); + } else { + udph->check = 0; + udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, + datalen, IPPROTO_UDP, + csum_partial((char *)udph, + datalen, 0)); + if (!udph->check) + udph->check = CSUM_MANGLED_0; + } } else nf_proto_csum_replace2(&udph->check, *pskb, htons(oldlen), htons(datalen), 1); @@ -318,8 +344,8 @@ nf_nat_sack_adjust(struct sk_buff **pskb unsigned int dir, optoff, optend; struct nf_conn_nat *nat = nfct_nat(ct); - optoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr); - optend = (*pskb)->nh.iph->ihl*4 + tcph->doff*4; + optoff = ip_hdrlen(*pskb) + sizeof(struct tcphdr); + optend = ip_hdrlen(*pskb) + tcph->doff * 4; if (!skb_make_writable(pskb, optend)) return 0; @@ -371,10 +397,10 @@ nf_nat_seq_adjust(struct sk_buff **pskb, this_way = &nat->info.seq[dir]; other_way = &nat->info.seq[!dir]; - if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph))) + if (!skb_make_writable(pskb, ip_hdrlen(*pskb) + sizeof(*tcph))) return 0; - tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; + tcph = (void *)(*pskb)->data + ip_hdrlen(*pskb); if (after(ntohl(tcph->seq), this_way->correction_pos)) newseq = htonl(ntohl(tcph->seq) + this_way->offset_after); else @@ -399,7 +425,7 @@ nf_nat_seq_adjust(struct sk_buff **pskb, if (!nf_nat_sack_adjust(pskb, tcph, ct, ctinfo)) return 0; - nf_conntrack_tcp_update(*pskb, (*pskb)->nh.iph->ihl*4, ct, dir); + nf_conntrack_tcp_update(*pskb, ip_hdrlen(*pskb), ct, dir); return 1; } diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index 7ba341c..a668887 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -53,7 +53,7 @@ static void pptp_nat_expected(struct nf_ struct nf_conntrack_tuple t; struct nf_ct_pptp_master *ct_pptp_info; struct nf_nat_pptp *nat_pptp_info; - struct ip_nat_range range; + struct nf_nat_range range; ct_pptp_info = &nfct_help(master)->help.ct_pptp_info; nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 147a437..2a28339 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -191,7 +191,7 @@ static unsigned int ipt_dnat_target(stru if (hooknum == NF_IP_LOCAL_OUT && mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) - warn_if_extra_mangle((*pskb)->nh.iph->daddr, + warn_if_extra_mangle(ip_hdr(*pskb)->daddr, mr->range[0].min_ip); return nf_nat_setup_info(ct, &mr->range[0], hooknum); diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index b12cd7c..bfd88e4 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -92,7 +93,7 @@ static int map_sip_addr(struct sk_buff * if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo, matchoff, matchlen, addr, addrlen)) return 0; - *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + *dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr); return 1; } @@ -106,7 +107,7 @@ static unsigned int ip_nat_sip(struct sk struct addr_map map; int dataoff, datalen; - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); datalen = (*pskb)->len - dataoff; if (datalen < sizeof("SIP/2.0") - 1) return NF_DROP; @@ -155,7 +156,7 @@ static unsigned int mangle_sip_packet(st return 0; /* We need to reload this. Thanks Patrick. */ - *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + *dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr); return 1; } @@ -168,7 +169,7 @@ static int mangle_content_len(struct sk_ char buffer[sizeof("65536")]; int bufflen; - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); /* Get actual SDP lenght */ if (ct_sip_get_info(ct, dptr, (*pskb)->len - dataoff, &matchoff, @@ -200,7 +201,7 @@ static unsigned int mangle_sdp(struct sk char buffer[sizeof("nnn.nnn.nnn.nnn")]; unsigned int dataoff, bufflen; - dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); /* Mangle owner and contact info. */ bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c index ce5c493..6e88505 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c @@ -38,10 +38,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: James Morris - * - * Updates: - * 2000-08-06: Convert to new helper API (Harald Welte). - * */ #include #include @@ -1194,7 +1190,7 @@ static int snmp_translate(struct nf_conn enum ip_conntrack_info ctinfo, struct sk_buff **pskb) { - struct iphdr *iph = (*pskb)->nh.iph; + struct iphdr *iph = ip_hdr(*pskb); struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl); u_int16_t udplen = ntohs(udph->len); u_int16_t paylen = udplen - sizeof(struct udphdr); @@ -1235,7 +1231,7 @@ static int help(struct sk_buff **pskb, u { int dir = CTINFO2DIR(ctinfo); unsigned int ret; - struct iphdr *iph = (*pskb)->nh.iph; + struct iphdr *iph = ip_hdr(*pskb); struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); /* SNMP replies and originating SNMP traps get mangled */ diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c index 15aa3db..64bbed2 100644 --- a/net/ipv4/netfilter/nf_nat_standalone.c +++ b/net/ipv4/netfilter/nf_nat_standalone.c @@ -86,8 +86,7 @@ nf_nat_fn(unsigned int hooknum, /* We never see fragments: conntrack defrags on pre-routing and local-out, and nf_nat_out protects post-routing. */ - NF_CT_ASSERT(!((*pskb)->nh.iph->frag_off - & htons(IP_MF|IP_OFFSET))); + NF_CT_ASSERT(!(ip_hdr(*pskb)->frag_off & htons(IP_MF | IP_OFFSET))); ct = nf_ct_get(*pskb, &ctinfo); /* Can't track? It's not due to stress, or conntrack would @@ -98,11 +97,10 @@ nf_nat_fn(unsigned int hooknum, /* Exception: ICMP redirect to new connection (not in hash table yet). We must not let this through, in case we're doing NAT to the same network. */ - if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { + if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) { struct icmphdr _hdr, *hp; - hp = skb_header_pointer(*pskb, - (*pskb)->nh.iph->ihl*4, + hp = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(_hdr), &_hdr); if (hp != NULL && hp->type == ICMP_REDIRECT) @@ -122,7 +120,7 @@ nf_nat_fn(unsigned int hooknum, switch (ctinfo) { case IP_CT_RELATED: case IP_CT_RELATED+IP_CT_IS_REPLY: - if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { + if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) { if (!nf_nat_icmp_reply_translation(ct, ctinfo, hooknum, pskb)) return NF_DROP; @@ -177,11 +175,11 @@ nf_nat_in(unsigned int hooknum, int (*okfn)(struct sk_buff *)) { unsigned int ret; - __be32 daddr = (*pskb)->nh.iph->daddr; + __be32 daddr = ip_hdr(*pskb)->daddr; ret = nf_nat_fn(hooknum, pskb, in, out, okfn); if (ret != NF_DROP && ret != NF_STOLEN && - daddr != (*pskb)->nh.iph->daddr) { + daddr != ip_hdr(*pskb)->daddr) { dst_release((*pskb)->dst); (*pskb)->dst = NULL; } @@ -203,7 +201,7 @@ #endif /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) || - (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) + ip_hdrlen(*pskb) < sizeof(struct iphdr)) return NF_ACCEPT; ret = nf_nat_fn(hooknum, pskb, in, out, okfn); @@ -236,7 +234,7 @@ nf_nat_local_fn(unsigned int hooknum, /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) || - (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) + ip_hdrlen(*pskb) < sizeof(struct iphdr)) return NF_ACCEPT; ret = nf_nat_fn(hooknum, pskb, in, out, okfn); diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index ae68a69..37ab580 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -87,19 +87,6 @@ static const struct file_operations sock .release = single_release, }; -static unsigned long -fold_field(void *mib[], int offt) -{ - unsigned long res = 0; - int i; - - for_each_possible_cpu(i) { - res += *(((unsigned long *) per_cpu_ptr(mib[0], i)) + offt); - res += *(((unsigned long *) per_cpu_ptr(mib[1], i)) + offt); - } - return res; -} - /* snmp items */ static const struct snmp_mib snmp4_ipstats_list[] = { SNMP_MIB_ITEM("InReceives", IPSTATS_MIB_INRECEIVES), @@ -266,8 +253,8 @@ static int snmp_seq_show(struct seq_file for (i = 0; snmp4_ipstats_list[i].name != NULL; i++) seq_printf(seq, " %lu", - fold_field((void **) ip_statistics, - snmp4_ipstats_list[i].entry)); + snmp_fold_field((void **)ip_statistics, + snmp4_ipstats_list[i].entry)); seq_puts(seq, "\nIcmp:"); for (i = 0; snmp4_icmp_list[i].name != NULL; i++) @@ -276,8 +263,8 @@ static int snmp_seq_show(struct seq_file seq_puts(seq, "\nIcmp:"); for (i = 0; snmp4_icmp_list[i].name != NULL; i++) seq_printf(seq, " %lu", - fold_field((void **) icmp_statistics, - snmp4_icmp_list[i].entry)); + snmp_fold_field((void **)icmp_statistics, + snmp4_icmp_list[i].entry)); seq_puts(seq, "\nTcp:"); for (i = 0; snmp4_tcp_list[i].name != NULL; i++) @@ -288,12 +275,12 @@ static int snmp_seq_show(struct seq_file /* MaxConn field is signed, RFC 2012 */ if (snmp4_tcp_list[i].entry == TCP_MIB_MAXCONN) seq_printf(seq, " %ld", - fold_field((void **) tcp_statistics, - snmp4_tcp_list[i].entry)); + snmp_fold_field((void **)tcp_statistics, + snmp4_tcp_list[i].entry)); else seq_printf(seq, " %lu", - fold_field((void **) tcp_statistics, - snmp4_tcp_list[i].entry)); + snmp_fold_field((void **)tcp_statistics, + snmp4_tcp_list[i].entry)); } seq_puts(seq, "\nUdp:"); @@ -303,8 +290,8 @@ static int snmp_seq_show(struct seq_file seq_puts(seq, "\nUdp:"); for (i = 0; snmp4_udp_list[i].name != NULL; i++) seq_printf(seq, " %lu", - fold_field((void **) udp_statistics, - snmp4_udp_list[i].entry)); + snmp_fold_field((void **)udp_statistics, + snmp4_udp_list[i].entry)); /* the UDP and UDP-Lite MIBs are the same */ seq_puts(seq, "\nUdpLite:"); @@ -314,8 +301,8 @@ static int snmp_seq_show(struct seq_file seq_puts(seq, "\nUdpLite:"); for (i = 0; snmp4_udp_list[i].name != NULL; i++) seq_printf(seq, " %lu", - fold_field((void **) udplite_statistics, - snmp4_udp_list[i].entry) ); + snmp_fold_field((void **)udplite_statistics, + snmp4_udp_list[i].entry)); seq_putc(seq, '\n'); return 0; @@ -348,8 +335,8 @@ static int netstat_seq_show(struct seq_f seq_puts(seq, "\nTcpExt:"); for (i = 0; snmp4_net_list[i].name != NULL; i++) seq_printf(seq, " %lu", - fold_field((void **) net_statistics, - snmp4_net_list[i].entry)); + snmp_fold_field((void **)net_statistics, + snmp4_net_list[i].entry)); seq_putc(seq, '\n'); return 0; diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index da70fef..971ab93 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -45,7 +45,7 @@ #include #include #include -struct net_protocol *inet_protos[MAX_INET_PROTOS]; +struct net_protocol *inet_protos[MAX_INET_PROTOS] ____cacheline_aligned_in_smp; static DEFINE_SPINLOCK(inet_proto_lock); /* diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 87e9c16..24d7c9f 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -132,7 +132,7 @@ static __inline__ int icmp_filter(struct if (!pskb_may_pull(skb, sizeof(struct icmphdr))) return 1; - type = skb->h.icmph->type; + type = icmp_hdr(skb)->type; if (type < 32) { __u32 data = raw_sk(sk)->filter.data; @@ -184,8 +184,8 @@ out: void raw_err (struct sock *sk, struct sk_buff *skb, u32 info) { struct inet_sock *inet = inet_sk(sk); - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; int err = 0; int harderr = 0; @@ -256,7 +256,7 @@ int raw_rcv(struct sock *sk, struct sk_b } nf_reset(skb); - skb_push(skb, skb->data - skb->nh.raw); + skb_push(skb, skb->data - skb_network_header(skb)); raw_rcv_skb(sk, skb); return 0; @@ -291,11 +291,13 @@ static int raw_send_hdrinc(struct sock * skb->priority = sk->sk_priority; skb->dst = dst_clone(&rt->u.dst); - skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length); + skb_reset_network_header(skb); + iph = ip_hdr(skb); + skb_put(skb, length); skb->ip_summed = CHECKSUM_NONE; - skb->h.raw = skb->nh.raw; + skb->transport_header = skb->network_header; err = memcpy_fromiovecend((void *)iph, from, 0, length); if (err) goto error_fault; @@ -613,7 +615,7 @@ static int raw_recvmsg(struct kiocb *ioc /* Copy the address. */ if (sin) { sin->sin_family = AF_INET; - sin->sin_addr.s_addr = skb->nh.iph->saddr; + sin->sin_addr.s_addr = ip_hdr(skb)->saddr; sin->sin_port = 0; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -887,7 +889,7 @@ static int raw_seq_show(struct seq_file return 0; } -static struct seq_operations raw_seq_ops = { +static const struct seq_operations raw_seq_ops = { .start = raw_seq_start, .next = raw_seq_next, .stop = raw_seq_stop, diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 37e0d4d..cb76e3c 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -82,7 +82,6 @@ #include #include #include #include -#include #include #include #include @@ -104,6 +103,7 @@ #include #include #include #include +#include #ifdef CONFIG_SYSCTL #include #endif @@ -364,7 +364,7 @@ static int rt_cache_seq_show(struct seq_ return 0; } -static struct seq_operations rt_cache_seq_ops = { +static const struct seq_operations rt_cache_seq_ops = { .start = rt_cache_seq_start, .next = rt_cache_seq_next, .stop = rt_cache_seq_stop, @@ -470,7 +470,7 @@ static int rt_cpu_seq_show(struct seq_fi return 0; } -static struct seq_operations rt_cpu_seq_ops = { +static const struct seq_operations rt_cpu_seq_ops = { .start = rt_cpu_seq_start, .next = rt_cpu_seq_next, .stop = rt_cpu_seq_stop, @@ -1519,7 +1519,7 @@ static void ipv4_link_failure(struct sk_ static int ip_rt_bug(struct sk_buff *skb) { printk(KERN_DEBUG "ip_rt_bug: %u.%u.%u.%u -> %u.%u.%u.%u, %s\n", - NIPQUAD(skb->nh.iph->saddr), NIPQUAD(skb->nh.iph->daddr), + NIPQUAD(ip_hdr(skb)->saddr), NIPQUAD(ip_hdr(skb)->daddr), skb->dev ? skb->dev->name : "?"); kfree_skb(skb); return 0; @@ -1698,9 +1698,9 @@ #ifdef CONFIG_IP_ROUTE_VERBOSE printk(KERN_WARNING "martian source %u.%u.%u.%u from " "%u.%u.%u.%u, on dev %s\n", NIPQUAD(daddr), NIPQUAD(saddr), dev->name); - if (dev->hard_header_len && skb->mac.raw) { + if (dev->hard_header_len && skb_mac_header_was_set(skb)) { int i; - unsigned char *p = skb->mac.raw; + const unsigned char *p = skb_mac_header(skb); printk(KERN_WARNING "ll header: "); for (i = 0; i < dev->hard_header_len; i++, p++) { printk("%02x", *p); @@ -2134,7 +2134,7 @@ int ip_route_input(struct sk_buff *skb, rcu_read_lock(); if ((in_dev = __in_dev_get_rcu(dev)) != NULL) { int our = ip_check_mc(in_dev, daddr, saddr, - skb->nh.iph->protocol); + ip_hdr(skb)->protocol); if (our #ifdef CONFIG_IP_MROUTE || (!LOCAL_MCAST(daddr) && IN_DEV_MFORWARD(in_dev)) @@ -2396,7 +2396,7 @@ #endif /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ dev_out = ip_dev_find(oldflp->fl4_src); - if (dev_out == NULL) + if ((dev_out == NULL) && !(sysctl_ip_nonlocal_bind)) goto out; /* I removed check for oif == dev_out->oif here. @@ -2407,7 +2407,7 @@ #endif of another iface. --ANK */ - if (oldflp->oif == 0 + if (dev_out && oldflp->oif == 0 && (MULTICAST(oldflp->fl4_dst) || oldflp->fl4_dst == htonl(0xFFFFFFFF))) { /* Special hack: user can direct multicasts and limited broadcast via necessary interface @@ -2683,7 +2683,7 @@ #endif id = rt->peer->ip_id_count; if (rt->peer->tcp_ts_stamp) { ts = rt->peer->tcp_ts; - tsage = xtime.tv_sec - rt->peer->tcp_ts_stamp; + tsage = get_seconds() - rt->peer->tcp_ts_stamp; } } @@ -2721,7 +2721,7 @@ nla_put_failure: return -EMSGSIZE; } -int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) +static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) { struct rtmsg *rtm; struct nlattr *tb[RTA_MAX+1]; @@ -2747,10 +2747,11 @@ int inet_rtm_getroute(struct sk_buff *in /* Reserve room for dummy headers, this skb can pass through good chunk of routing engine. */ - skb->mac.raw = skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); /* Bugfix: need to give ip_route_input enough of an IP header to not gag. */ - skb->nh.iph->protocol = IPPROTO_ICMP; + ip_hdr(skb)->protocol = IPPROTO_ICMP; skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr)); src = tb[RTA_SRC] ? nla_get_be32(tb[RTA_SRC]) : 0; @@ -3193,6 +3194,8 @@ #ifdef CONFIG_XFRM xfrm_init(); xfrm4_init(); #endif + rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL); + return rc; } diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 33016cc..2da1be0 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -125,10 +125,11 @@ #define NUM_MSS (ARRAY_SIZE(msstab) - 1) __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) { struct tcp_sock *tp = tcp_sk(sk); + const struct iphdr *iph = ip_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); int mssind; const __u16 mss = *mssp; - tp->last_synq_overflow = jiffies; /* XXX sort msstab[] by probability? Binary search? */ @@ -138,9 +139,8 @@ __u32 cookie_v4_init_sequence(struct soc NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESSENT); - return secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr, - skb->h.th->source, skb->h.th->dest, - ntohl(skb->h.th->seq), + return secure_tcp_syn_cookie(iph->saddr, iph->daddr, + th->source, th->dest, ntohl(th->seq), jiffies / (HZ * 60), mssind); } @@ -157,14 +157,13 @@ #define COUNTER_TRIES 4 */ static inline int cookie_check(struct sk_buff *skb, __u32 cookie) { - __u32 seq; - __u32 mssind; - - seq = ntohl(skb->h.th->seq)-1; - mssind = check_tcp_syn_cookie(cookie, - skb->nh.iph->saddr, skb->nh.iph->daddr, - skb->h.th->source, skb->h.th->dest, - seq, jiffies / (HZ * 60), COUNTER_TRIES); + const struct iphdr *iph = ip_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); + __u32 seq = ntohl(th->seq) - 1; + __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, + th->source, th->dest, seq, + jiffies / (HZ * 60), + COUNTER_TRIES); return mssind < NUM_MSS ? msstab[mssind] + 1 : 0; } @@ -191,14 +190,15 @@ struct sock *cookie_v4_check(struct sock struct inet_request_sock *ireq; struct tcp_request_sock *treq; struct tcp_sock *tp = tcp_sk(sk); - __u32 cookie = ntohl(skb->h.th->ack_seq) - 1; + const struct tcphdr *th = tcp_hdr(skb); + __u32 cookie = ntohl(th->ack_seq) - 1; struct sock *ret = sk; struct request_sock *req; int mss; struct rtable *rt; __u8 rcv_wscale; - if (!sysctl_tcp_syncookies || !skb->h.th->ack) + if (!sysctl_tcp_syncookies || !th->ack) goto out; if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) || @@ -220,12 +220,12 @@ struct sock *cookie_v4_check(struct sock } ireq = inet_rsk(req); treq = tcp_rsk(req); - treq->rcv_isn = ntohl(skb->h.th->seq) - 1; + treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = cookie; req->mss = mss; - ireq->rmt_port = skb->h.th->source; - ireq->loc_addr = skb->nh.iph->daddr; - ireq->rmt_addr = skb->nh.iph->saddr; + ireq->rmt_port = th->source; + ireq->loc_addr = ip_hdr(skb)->daddr; + ireq->rmt_addr = ip_hdr(skb)->saddr; ireq->opt = NULL; /* We throwed the options of the initial SYN away, so we hope @@ -261,8 +261,8 @@ struct sock *cookie_v4_check(struct sock .tos = RT_CONN_FLAGS(sk) } }, .proto = IPPROTO_TCP, .uli_u = { .ports = - { .sport = skb->h.th->dest, - .dport = skb->h.th->source } } }; + { .sport = th->dest, + .dport = th->source } } }; security_req_classify_flow(req, &fl); if (ip_route_output_key(&rt, &fl)) { reqsk_free(req); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 0aa3047..6817d64 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -647,6 +647,14 @@ #endif .proc_handler = &proc_dointvec }, { + .ctl_name = NET_TCP_FRTO_RESPONSE, + .procname = "tcp_frto_response", + .data = &sysctl_tcp_frto_response, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { .ctl_name = NET_TCP_LOW_LATENCY, .procname = "tcp_low_latency", .data = &sysctl_tcp_low_latency, @@ -803,6 +811,14 @@ #endif /* CONFIG_NETLABEL */ .proc_handler = &proc_allowed_congestion_control, .strategy = &strategy_allowed_congestion_control, }, + { + .ctl_name = NET_TCP_MAX_SSTHRESH, + .procname = "tcp_max_ssthresh", + .data = &sysctl_tcp_max_ssthresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0 } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3834b10..d6e4886 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -297,7 +297,7 @@ EXPORT_SYMBOL(tcp_sockets_allocated); * All the sk_stream_mem_schedule() is of this nature: accounting * is strict, actions are advisory and have some latency. */ -int tcp_memory_pressure; +int tcp_memory_pressure __read_mostly; EXPORT_SYMBOL(tcp_memory_pressure); @@ -425,7 +425,7 @@ int tcp_ioctl(struct sock *sk, int cmd, /* Subtract 1, if FIN is in queue. */ if (answ && !skb_queue_empty(&sk->sk_receive_queue)) answ -= - ((struct sk_buff *)sk->sk_receive_queue.prev)->h.th->fin; + tcp_hdr((struct sk_buff *)sk->sk_receive_queue.prev)->fin; } else answ = tp->urg_seq - tp->copied_seq; release_sock(sk); @@ -444,7 +444,7 @@ int tcp_ioctl(struct sock *sk, int cmd, break; default: return -ENOIOCTLCMD; - }; + } return put_user(answ, (int __user *)arg); } @@ -460,9 +460,9 @@ static inline int forced_push(struct tcp return after(tp->write_seq, tp->pushed_seq + (tp->max_window >> 1)); } -static inline void skb_entail(struct sock *sk, struct tcp_sock *tp, - struct sk_buff *skb) +static inline void skb_entail(struct sock *sk, struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); skb->csum = 0; @@ -470,10 +470,8 @@ static inline void skb_entail(struct soc tcb->flags = TCPCB_FLAG_ACK; tcb->sacked = 0; skb_header_release(skb); - __skb_queue_tail(&sk->sk_write_queue, skb); + tcp_add_write_queue_tail(sk, skb); sk_charge_skb(sk, skb); - if (!sk->sk_send_head) - sk->sk_send_head = skb; if (tp->nonagle & TCP_NAGLE_PUSH) tp->nonagle &= ~TCP_NAGLE_PUSH; } @@ -488,15 +486,17 @@ static inline void tcp_mark_urg(struct t } } -static inline void tcp_push(struct sock *sk, struct tcp_sock *tp, int flags, - int mss_now, int nonagle) +static inline void tcp_push(struct sock *sk, int flags, int mss_now, + int nonagle) { - if (sk->sk_send_head) { - struct sk_buff *skb = sk->sk_write_queue.prev; + struct tcp_sock *tp = tcp_sk(sk); + + if (tcp_send_head(sk)) { + struct sk_buff *skb = tcp_write_queue_tail(sk); if (!(flags & MSG_MORE) || forced_push(tp)) tcp_mark_push(tp, skb); tcp_mark_urg(tp, flags, skb); - __tcp_push_pending_frames(sk, tp, mss_now, + __tcp_push_pending_frames(sk, mss_now, (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle); } } @@ -526,13 +526,13 @@ static ssize_t do_tcp_sendpages(struct s goto do_error; while (psize > 0) { - struct sk_buff *skb = sk->sk_write_queue.prev; + struct sk_buff *skb = tcp_write_queue_tail(sk); struct page *page = pages[poffset / PAGE_SIZE]; int copy, i, can_coalesce; int offset = poffset % PAGE_SIZE; int size = min_t(size_t, psize, PAGE_SIZE - offset); - if (!sk->sk_send_head || (copy = size_goal - skb->len) <= 0) { + if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) { new_segment: if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; @@ -542,7 +542,7 @@ new_segment: if (!skb) goto wait_for_memory; - skb_entail(sk, tp, skb); + skb_entail(sk, skb); copy = size_goal; } @@ -588,8 +588,8 @@ new_segment: if (forced_push(tp)) { tcp_mark_push(tp, skb); - __tcp_push_pending_frames(sk, tp, mss_now, TCP_NAGLE_PUSH); - } else if (skb == sk->sk_send_head) + __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH); + } else if (skb == tcp_send_head(sk)) tcp_push_one(sk, mss_now); continue; @@ -597,7 +597,7 @@ wait_for_sndbuf: set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); wait_for_memory: if (copied) - tcp_push(sk, tp, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); + tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; @@ -608,7 +608,7 @@ wait_for_memory: out: if (copied) - tcp_push(sk, tp, flags, mss_now, tp->nonagle); + tcp_push(sk, flags, mss_now, tp->nonagle); return copied; do_error: @@ -639,8 +639,9 @@ ssize_t tcp_sendpage(struct socket *sock #define TCP_PAGE(sk) (sk->sk_sndmsg_page) #define TCP_OFF(sk) (sk->sk_sndmsg_off) -static inline int select_size(struct sock *sk, struct tcp_sock *tp) +static inline int select_size(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); int tmp = tp->mss_cache; if (sk->sk_route_caps & NETIF_F_SG) { @@ -704,9 +705,9 @@ int tcp_sendmsg(struct kiocb *iocb, stru while (seglen > 0) { int copy; - skb = sk->sk_write_queue.prev; + skb = tcp_write_queue_tail(sk); - if (!sk->sk_send_head || + if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) { new_segment: @@ -716,7 +717,7 @@ new_segment: if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; - skb = sk_stream_alloc_pskb(sk, select_size(sk, tp), + skb = sk_stream_alloc_pskb(sk, select_size(sk), 0, sk->sk_allocation); if (!skb) goto wait_for_memory; @@ -727,7 +728,7 @@ new_segment: if (sk->sk_route_caps & NETIF_F_ALL_CSUM) skb->ip_summed = CHECKSUM_PARTIAL; - skb_entail(sk, tp, skb); + skb_entail(sk, skb); copy = size_goal; } @@ -832,8 +833,8 @@ new_segment: if (forced_push(tp)) { tcp_mark_push(tp, skb); - __tcp_push_pending_frames(sk, tp, mss_now, TCP_NAGLE_PUSH); - } else if (skb == sk->sk_send_head) + __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH); + } else if (skb == tcp_send_head(sk)) tcp_push_one(sk, mss_now); continue; @@ -841,7 +842,7 @@ wait_for_sndbuf: set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); wait_for_memory: if (copied) - tcp_push(sk, tp, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); + tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; @@ -853,16 +854,18 @@ wait_for_memory: out: if (copied) - tcp_push(sk, tp, flags, mss_now, tp->nonagle); + tcp_push(sk, flags, mss_now, tp->nonagle); TCP_CHECK_TIMER(sk); release_sock(sk); return copied; do_fault: if (!skb->len) { - if (sk->sk_send_head == skb) - sk->sk_send_head = NULL; - __skb_unlink(skb, &sk->sk_write_queue); + tcp_unlink_write_queue(skb, sk); + /* It is the one place in all of TCP, except connection + * reset, where we can be unlinking the send_head. + */ + tcp_check_send_head(sk, skb); sk_stream_free_skb(sk, skb); } @@ -1016,9 +1019,9 @@ static inline struct sk_buff *tcp_recv_s skb_queue_walk(&sk->sk_receive_queue, skb) { offset = seq - TCP_SKB_CB(skb)->seq; - if (skb->h.th->syn) + if (tcp_hdr(skb)->syn) offset--; - if (offset < skb->len || skb->h.th->fin) { + if (offset < skb->len || tcp_hdr(skb)->fin) { *off = offset; return skb; } @@ -1070,7 +1073,7 @@ int tcp_read_sock(struct sock *sk, read_ if (offset != skb->len) break; } - if (skb->h.th->fin) { + if (tcp_hdr(skb)->fin) { sk_eat_skb(sk, skb, 0); ++seq; break; @@ -1174,11 +1177,11 @@ #endif break; } offset = *seq - TCP_SKB_CB(skb)->seq; - if (skb->h.th->syn) + if (tcp_hdr(skb)->syn) offset--; if (offset < skb->len) goto found_ok_skb; - if (skb->h.th->fin) + if (tcp_hdr(skb)->fin) goto found_fin_ok; BUG_TRAP(flags & MSG_PEEK); skb = skb->next; @@ -1389,12 +1392,12 @@ #endif skip_copy: if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) { tp->urg_data = 0; - tcp_fast_path_check(sk, tp); + tcp_fast_path_check(sk); } if (used + offset < skb->len) continue; - if (skb->h.th->fin) + if (tcp_hdr(skb)->fin) goto found_fin_ok; if (!(flags & MSG_PEEK)) { sk_eat_skb(sk, skb, copied_early); @@ -1563,21 +1566,19 @@ void tcp_close(struct sock *sk, long tim */ while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - - skb->h.th->fin; + tcp_hdr(skb)->fin; data_was_unread += len; __kfree_skb(skb); } sk_stream_mem_reclaim(sk); - /* As outlined in draft-ietf-tcpimpl-prob-03.txt, section - * 3.10, we send a RST here because data was lost. To - * witness the awful effects of the old behavior of always - * doing a FIN, run an older 2.1.x kernel or 2.0.x, start - * a bulk GET in an FTP client, suspend the process, wait - * for the client to advertise a zero window, then kill -9 - * the FTP client, wheee... Note: timeout is always zero - * in such a case. + /* As outlined in RFC 2525, section 2.17, we send a RST here because + * data was lost. To witness the awful effects of the old behavior of + * always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk + * GET in an FTP client, suspend the process, wait for the client to + * advertise a zero window, then kill -9 the FTP client, wheee... + * Note: timeout is always zero in such a case. */ if (data_was_unread) { /* Unread data was tossed, zap the connection. */ @@ -1732,7 +1733,7 @@ int tcp_disconnect(struct sock *sk, int tcp_clear_xmit_timers(sk); __skb_queue_purge(&sk->sk_receive_queue); - sk_stream_writequeue_purge(sk); + tcp_write_queue_purge(sk); __skb_queue_purge(&tp->out_of_order_queue); #ifdef CONFIG_NET_DMA __skb_queue_purge(&sk->sk_async_wait_queue); @@ -1758,7 +1759,7 @@ #endif tcp_set_ca_state(sk, TCP_CA_Open); tcp_clear_retrans(tp); inet_csk_delack_init(sk); - sk->sk_send_head = NULL; + tcp_init_send_head(sk); tp->rx_opt.saw_tstamp = 0; tcp_sack_reset(&tp->rx_opt); __sk_dst_reset(sk); @@ -1830,7 +1831,7 @@ static int do_tcp_setsockopt(struct sock * for currently queued segments. */ tp->nonagle |= TCP_NAGLE_OFF|TCP_NAGLE_PUSH; - tcp_push_pending_frames(sk, tp); + tcp_push_pending_frames(sk); } else { tp->nonagle &= ~TCP_NAGLE_OFF; } @@ -1854,7 +1855,7 @@ static int do_tcp_setsockopt(struct sock tp->nonagle &= ~TCP_NAGLE_CORK; if (tp->nonagle&TCP_NAGLE_OFF) tp->nonagle |= TCP_NAGLE_PUSH; - tcp_push_pending_frames(sk, tp); + tcp_push_pending_frames(sk); } break; @@ -1954,7 +1955,8 @@ #endif default: err = -ENOPROTOOPT; break; - }; + } + release_sock(sk); return err; } @@ -2124,7 +2126,7 @@ static int do_tcp_getsockopt(struct sock return 0; default: return -ENOPROTOOPT; - }; + } if (put_user(len, optlen)) return -EFAULT; @@ -2170,7 +2172,7 @@ struct sk_buff *tcp_tso_segment(struct s if (!pskb_may_pull(skb, sizeof(*th))) goto out; - th = skb->h.th; + th = tcp_hdr(skb); thlen = th->doff * 4; if (thlen < sizeof(*th)) goto out; @@ -2210,7 +2212,7 @@ struct sk_buff *tcp_tso_segment(struct s delta = htonl(oldlen + (thlen + len)); skb = segs; - th = skb->h.th; + th = tcp_hdr(skb); seq = ntohl(th->seq); do { @@ -2219,23 +2221,25 @@ struct sk_buff *tcp_tso_segment(struct s th->check = ~csum_fold((__force __wsum)((__force u32)th->check + (__force u32)delta)); if (skb->ip_summed != CHECKSUM_PARTIAL) - th->check = csum_fold(csum_partial(skb->h.raw, thlen, - skb->csum)); + th->check = + csum_fold(csum_partial(skb_transport_header(skb), + thlen, skb->csum)); seq += len; skb = skb->next; - th = skb->h.th; + th = tcp_hdr(skb); th->seq = htonl(seq); th->cwr = 0; } while (skb->next); - delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len); + delta = htonl(oldlen + (skb->tail - skb->transport_header) + + skb->data_len); th->check = ~csum_fold((__force __wsum)((__force u32)th->check + (__force u32)delta)); if (skb->ip_summed != CHECKSUM_PARTIAL) - th->check = csum_fold(csum_partial(skb->h.raw, thlen, - skb->csum)); + th->check = csum_fold(csum_partial(skb_transport_header(skb), + thlen, skb->csum)); out: return segs; @@ -2372,6 +2376,23 @@ void __tcp_put_md5sig_pool(void) EXPORT_SYMBOL(__tcp_put_md5sig_pool); #endif +void tcp_done(struct sock *sk) +{ + if(sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) + TCP_INC_STATS_BH(TCP_MIB_ATTEMPTFAILS); + + tcp_set_state(sk, TCP_CLOSE); + tcp_clear_xmit_timers(sk); + + sk->sk_shutdown = SHUTDOWN_MASK; + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_state_change(sk); + else + inet_csk_destroy_sock(sk); +} +EXPORT_SYMBOL_GPL(tcp_done); + extern void __skb_cb_too_small_for_tcp(int, int); extern struct tcp_congestion_ops tcp_reno; diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index 5730333..281c9f9 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -206,7 +206,7 @@ static void bictcp_state(struct sock *sk /* Track delayed acknowledgment ratio using sliding window * ratio = (15*ratio + sample) / 16 */ -static void bictcp_acked(struct sock *sk, u32 cnt) +static void bictcp_acked(struct sock *sk, u32 cnt, ktime_t last) { const struct inet_connection_sock *icsk = inet_csk(sk); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 34ae3f1..86b2653 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -12,6 +12,8 @@ #include #include #include +int sysctl_tcp_max_ssthresh = 0; + static DEFINE_SPINLOCK(tcp_cong_list_lock); static LIST_HEAD(tcp_cong_list); @@ -124,7 +126,7 @@ #ifdef CONFIG_KMOD #endif if (ca) { - ca->non_restricted = 1; /* default is always allowed */ + ca->flags |= TCP_CONG_NON_RESTRICTED; /* default is always allowed */ list_move(&ca->list, &tcp_cong_list); ret = 0; } @@ -179,7 +181,7 @@ void tcp_get_allowed_congestion_control( *buf = '\0'; rcu_read_lock(); list_for_each_entry_rcu(ca, &tcp_cong_list, list) { - if (!ca->non_restricted) + if (!(ca->flags & TCP_CONG_NON_RESTRICTED)) continue; offs += snprintf(buf + offs, maxlen - offs, "%s%s", @@ -210,16 +212,16 @@ int tcp_set_allowed_congestion_control(c } } - /* pass 2 clear */ + /* pass 2 clear old values */ list_for_each_entry_rcu(ca, &tcp_cong_list, list) - ca->non_restricted = 0; + ca->flags &= ~TCP_CONG_NON_RESTRICTED; /* pass 3 mark as allowed */ while ((name = strsep(&val, " ")) && *name) { ca = tcp_ca_find(name); WARN_ON(!ca); if (ca) - ca->non_restricted = 1; + ca->flags |= TCP_CONG_NON_RESTRICTED; } out: spin_unlock(&tcp_cong_list_lock); @@ -254,7 +256,7 @@ #endif if (!ca) err = -ENOENT; - else if (!(ca->non_restricted || capable(CAP_NET_ADMIN))) + else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) || capable(CAP_NET_ADMIN))) err = -EPERM; else if (!try_module_get(ca->owner)) @@ -274,10 +276,13 @@ #endif /* - * Linear increase during slow start + * Slow start (exponential increase) with + * RFC3742 Limited Slow Start (fast linear increase) support. */ void tcp_slow_start(struct tcp_sock *tp) { + int cnt = 0; + if (sysctl_tcp_abc) { /* RFC3465: Slow Start * TCP sender SHOULD increase cwnd by the number of @@ -286,17 +291,25 @@ void tcp_slow_start(struct tcp_sock *tp) */ if (tp->bytes_acked < tp->mss_cache) return; - - /* We MAY increase by 2 if discovered delayed ack */ - if (sysctl_tcp_abc > 1 && tp->bytes_acked >= 2*tp->mss_cache) { - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - } } + + if (sysctl_tcp_max_ssthresh > 0 && + tp->snd_cwnd > sysctl_tcp_max_ssthresh) + cnt += sysctl_tcp_max_ssthresh>>1; + else + cnt += tp->snd_cwnd; + + /* RFC3465: We MAY increase by 2 if discovered delayed ack */ + if (sysctl_tcp_abc > 1 && tp->bytes_acked >= 2*tp->mss_cache) + cnt <<= 1; tp->bytes_acked = 0; - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; + tp->snd_cwnd_cnt += cnt; + while (tp->snd_cwnd_cnt >= tp->snd_cwnd) { + tp->snd_cwnd_cnt -= tp->snd_cwnd; + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + } } EXPORT_SYMBOL_GPL(tcp_slow_start); @@ -358,8 +371,8 @@ u32 tcp_reno_min_cwnd(const struct sock EXPORT_SYMBOL_GPL(tcp_reno_min_cwnd); struct tcp_congestion_ops tcp_reno = { + .flags = TCP_CONG_NON_RESTRICTED, .name = "reno", - .non_restricted = 1, .owner = THIS_MODULE, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 9a582fb..1422448 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -1,5 +1,5 @@ /* - * TCP CUBIC: Binary Increase Congestion control for TCP v2.0 + * TCP CUBIC: Binary Increase Congestion control for TCP v2.1 * * This is from the implementation of CUBIC TCP in * Injong Rhee, Lisong Xu. @@ -51,8 +51,6 @@ MODULE_PARM_DESC(bic_scale, "scale (scal module_param(tcp_friendliness, int, 0644); MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness"); -#include - /* BIC TCP Parameters */ struct bictcp { u32 cnt; /* increase cwnd by 1 after ACKs */ @@ -93,50 +91,51 @@ static void bictcp_init(struct sock *sk) tcp_sk(sk)->snd_ssthresh = initial_ssthresh; } -/* 64bit divisor, dividend and result. dynamic precision */ -static inline u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor) -{ - u_int32_t d = divisor; - - if (divisor > 0xffffffffULL) { - unsigned int shift = fls(divisor >> 32); - - d = divisor >> shift; - dividend >>= shift; - } - - /* avoid 64 bit division if possible */ - if (dividend >> 32) - do_div(dividend, d); - else - dividend = (uint32_t) dividend / d; - - return dividend; -} - -/* - * calculate the cubic root of x using Newton-Raphson +/* calculate the cubic root of x using a table lookup followed by one + * Newton-Raphson iteration. + * Avg err ~= 0.195% */ static u32 cubic_root(u64 a) { - u32 x, x1; - - /* Initial estimate is based on: - * cbrt(x) = exp(log(x) / 3) + u32 x, b, shift; + /* + * cbrt(x) MSB values for x MSB values in [0..63]. + * Precomputed then refined by hand - Willy Tarreau + * + * For x in [0..63], + * v = cbrt(x << 18) - 1 + * cbrt(x) = (v[x] + 10) >> 6 */ - x = 1u << (fls64(a)/3); + static const u8 v[] = { + /* 0x00 */ 0, 54, 54, 54, 118, 118, 118, 118, + /* 0x08 */ 123, 129, 134, 138, 143, 147, 151, 156, + /* 0x10 */ 157, 161, 164, 168, 170, 173, 176, 179, + /* 0x18 */ 181, 185, 187, 190, 192, 194, 197, 199, + /* 0x20 */ 200, 202, 204, 206, 209, 211, 213, 215, + /* 0x28 */ 217, 219, 221, 222, 224, 225, 227, 229, + /* 0x30 */ 231, 232, 234, 236, 237, 239, 240, 242, + /* 0x38 */ 244, 245, 246, 248, 250, 251, 252, 254, + }; + + b = fls64(a); + if (b < 7) { + /* a in [0..63] */ + return ((u32)v[(u32)a] + 35) >> 6; + } + + b = ((b * 84) >> 8) - 1; + shift = (a >> (b * 3)); + + x = ((u32)(((u32)v[shift] + 10) << b)) >> 6; /* - * Iteration based on: + * Newton-Raphson iteration * 2 * x = ( 2 * x + a / x ) / 3 * k+1 k k */ - do { - x1 = x; - x = (2 * x + (uint32_t) div64_64(a, x*x)) / 3; - } while (abs(x1 - x) > 1); - + x = (2 * x + (u32)div64_64(a, (u64)x * (u64)(x - 1))); + x = ((x * 341) >> 10); return x; } @@ -215,7 +214,9 @@ static inline void bictcp_update(struct if (ca->delay_min > 0) { /* max increment = Smax * rtt / 0.1 */ min_cnt = (cwnd * HZ * 8)/(10 * max_increment * ca->delay_min); - if (ca->cnt < min_cnt) + + /* use concave growth when the target is above the origin */ + if (ca->cnt < min_cnt && t >= ca->bic_K) ca->cnt = min_cnt; } @@ -333,7 +334,7 @@ static void bictcp_state(struct sock *sk /* Track delayed acknowledgment ratio using sliding window * ratio = (15*ratio + sample) / 16 */ -static void bictcp_acked(struct sock *sk, u32 cnt) +static void bictcp_acked(struct sock *sk, u32 cnt, ktime_t last) { const struct inet_connection_sock *icsk = inet_csk(sk); @@ -401,4 +402,4 @@ module_exit(cubictcp_unregister); MODULE_AUTHOR("Sangtae Ha, Stephen Hemminger"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("CUBIC TCP"); -MODULE_VERSION("2.0"); +MODULE_VERSION("2.1"); diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c index 1020eb4..4ba4a7a 100644 --- a/net/ipv4/tcp_htcp.c +++ b/net/ipv4/tcp_htcp.c @@ -98,7 +98,7 @@ static inline void measure_rtt(struct so } } -static void measure_achieved_throughput(struct sock *sk, u32 pkts_acked) +static void measure_achieved_throughput(struct sock *sk, u32 pkts_acked, ktime_t last) { const struct inet_connection_sock *icsk = inet_csk(sk); const struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index 59e691d..e5be351 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -144,7 +144,7 @@ static void hybla_cong_avoid(struct sock ca->snd_cwnd_cents += odd; /* check when fractions goes >=128 and increase cwnd by 1. */ - while(ca->snd_cwnd_cents >= 128) { + while (ca->snd_cwnd_cents >= 128) { tp->snd_cwnd++; ca->snd_cwnd_cents -= 128; tp->snd_cwnd_cnt = 0; diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c new file mode 100644 index 0000000..4adc47c --- /dev/null +++ b/net/ipv4/tcp_illinois.c @@ -0,0 +1,356 @@ +/* + * TCP Illinois congestion control. + * Home page: + * http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html + * + * The algorithm is described in: + * "TCP-Illinois: A Loss and Delay-Based Congestion Control Algorithm + * for High-Speed Networks" + * http://www.ews.uiuc.edu/~shaoliu/papersandslides/liubassri06perf.pdf + * + * Implemented from description in paper and ns-2 simulation. + * Copyright (C) 2007 Stephen Hemminger + */ + +#include +#include +#include +#include +#include + +#define ALPHA_SHIFT 7 +#define ALPHA_SCALE (1u<end_seq = tp->snd_nxt; + ca->cnt_rtt = 0; + ca->sum_rtt = 0; + + /* TODO: age max_rtt? */ +} + +static void tcp_illinois_init(struct sock *sk) +{ + struct illinois *ca = inet_csk_ca(sk); + + ca->alpha = ALPHA_MAX; + ca->beta = BETA_BASE; + ca->base_rtt = 0x7fffffff; + ca->max_rtt = 0; + + ca->acked = 0; + ca->rtt_low = 0; + ca->rtt_above = 0; + + rtt_reset(sk); +} + +/* Measure RTT for each ack. */ +static void tcp_illinois_acked(struct sock *sk, u32 pkts_acked, ktime_t last) +{ + struct illinois *ca = inet_csk_ca(sk); + u32 rtt; + + ca->acked = pkts_acked; + + rtt = ktime_to_us(net_timedelta(last)); + + /* ignore bogus values, this prevents wraparound in alpha math */ + if (rtt > RTT_MAX) + rtt = RTT_MAX; + + /* keep track of minimum RTT seen so far */ + if (ca->base_rtt > rtt) + ca->base_rtt = rtt; + + /* and max */ + if (ca->max_rtt < rtt) + ca->max_rtt = rtt; + + ++ca->cnt_rtt; + ca->sum_rtt += rtt; +} + +/* Maximum queuing delay */ +static inline u32 max_delay(const struct illinois *ca) +{ + return ca->max_rtt - ca->base_rtt; +} + +/* Average queuing delay */ +static inline u32 avg_delay(const struct illinois *ca) +{ + u64 t = ca->sum_rtt; + + do_div(t, ca->cnt_rtt); + return t - ca->base_rtt; +} + +/* + * Compute value of alpha used for additive increase. + * If small window then use 1.0, equivalent to Reno. + * + * For larger windows, adjust based on average delay. + * A. If average delay is at minimum (we are uncongested), + * then use large alpha (10.0) to increase faster. + * B. If average delay is at maximum (getting congested) + * then use small alpha (0.3) + * + * The result is a convex window growth curve. + */ +static u32 alpha(struct illinois *ca, u32 da, u32 dm) +{ + u32 d1 = dm / 100; /* Low threshold */ + + if (da <= d1) { + /* If never got out of low delay zone, then use max */ + if (!ca->rtt_above) + return ALPHA_MAX; + + /* Wait for 5 good RTT's before allowing alpha to go alpha max. + * This prevents one good RTT from causing sudden window increase. + */ + if (++ca->rtt_low < theta) + return ca->alpha; + + ca->rtt_low = 0; + ca->rtt_above = 0; + return ALPHA_MAX; + } + + ca->rtt_above = 1; + + /* + * Based on: + * + * (dm - d1) amin amax + * k1 = ------------------- + * amax - amin + * + * (dm - d1) amin + * k2 = ---------------- - d1 + * amax - amin + * + * k1 + * alpha = ---------- + * k2 + da + */ + + dm -= d1; + da -= d1; + return (dm * ALPHA_MAX) / + (dm + (da * (ALPHA_MAX - ALPHA_MIN)) / ALPHA_MIN); +} + +/* + * Beta used for multiplicative decrease. + * For small window sizes returns same value as Reno (0.5) + * + * If delay is small (10% of max) then beta = 1/8 + * If delay is up to 80% of max then beta = 1/2 + * In between is a linear function + */ +static u32 beta(u32 da, u32 dm) +{ + u32 d2, d3; + + d2 = dm / 10; + if (da <= d2) + return BETA_MIN; + + d3 = (8 * dm) / 10; + if (da >= d3 || d3 <= d2) + return BETA_MAX; + + /* + * Based on: + * + * bmin d3 - bmax d2 + * k3 = ------------------- + * d3 - d2 + * + * bmax - bmin + * k4 = ------------- + * d3 - d2 + * + * b = k3 + k4 da + */ + return (BETA_MIN * d3 - BETA_MAX * d2 + (BETA_MAX - BETA_MIN) * da) + / (d3 - d2); +} + +/* Update alpha and beta values once per RTT */ +static void update_params(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct illinois *ca = inet_csk_ca(sk); + + if (tp->snd_cwnd < win_thresh) { + ca->alpha = ALPHA_BASE; + ca->beta = BETA_BASE; + } else if (ca->cnt_rtt > 0) { + u32 dm = max_delay(ca); + u32 da = avg_delay(ca); + + ca->alpha = alpha(ca, da, dm); + ca->beta = beta(da, dm); + } + + rtt_reset(sk); +} + +/* + * In case of loss, reset to default values + */ +static void tcp_illinois_state(struct sock *sk, u8 new_state) +{ + struct illinois *ca = inet_csk_ca(sk); + + if (new_state == TCP_CA_Loss) { + ca->alpha = ALPHA_BASE; + ca->beta = BETA_BASE; + ca->rtt_low = 0; + ca->rtt_above = 0; + rtt_reset(sk); + } +} + +/* + * Increase window in response to successful acknowledgment. + */ +static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 rtt, + u32 in_flight, int flag) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct illinois *ca = inet_csk_ca(sk); + + if (after(ack, ca->end_seq)) + update_params(sk); + + /* RFC2861 only increase cwnd if fully utilized */ + if (!tcp_is_cwnd_limited(sk, in_flight)) + return; + + /* In slow start */ + if (tp->snd_cwnd <= tp->snd_ssthresh) + tcp_slow_start(tp); + + else { + u32 delta; + + /* snd_cwnd_cnt is # of packets since last cwnd increment */ + tp->snd_cwnd_cnt += ca->acked; + ca->acked = 1; + + /* This is close approximation of: + * tp->snd_cwnd += alpha/tp->snd_cwnd + */ + delta = (tp->snd_cwnd_cnt * ca->alpha) >> ALPHA_SHIFT; + if (delta >= tp->snd_cwnd) { + tp->snd_cwnd = min(tp->snd_cwnd + delta / tp->snd_cwnd, + (u32) tp->snd_cwnd_clamp); + tp->snd_cwnd_cnt = 0; + } + } +} + +static u32 tcp_illinois_ssthresh(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct illinois *ca = inet_csk_ca(sk); + + /* Multiplicative decrease */ + return max((tp->snd_cwnd * ca->beta) >> BETA_SHIFT, 2U); +} + + +/* Extract info for Tcp socket info provided via netlink. */ +static void tcp_illinois_info(struct sock *sk, u32 ext, + struct sk_buff *skb) +{ + const struct illinois *ca = inet_csk_ca(sk); + + if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) { + struct tcpvegas_info info = { + .tcpv_enabled = 1, + .tcpv_rttcnt = ca->cnt_rtt, + .tcpv_minrtt = ca->base_rtt, + }; + u64 t = ca->sum_rtt; + + do_div(t, ca->cnt_rtt); + info.tcpv_rtt = t; + + nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info); + } +} + +static struct tcp_congestion_ops tcp_illinois = { + .flags = TCP_CONG_RTT_STAMP, + .init = tcp_illinois_init, + .ssthresh = tcp_illinois_ssthresh, + .min_cwnd = tcp_reno_min_cwnd, + .cong_avoid = tcp_illinois_cong_avoid, + .set_state = tcp_illinois_state, + .get_info = tcp_illinois_info, + .pkts_acked = tcp_illinois_acked, + + .owner = THIS_MODULE, + .name = "illinois", +}; + +static int __init tcp_illinois_register(void) +{ + BUILD_BUG_ON(sizeof(struct illinois) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_illinois); +} + +static void __exit tcp_illinois_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_illinois); +} + +module_init(tcp_illinois_register); +module_exit(tcp_illinois_unregister); + +MODULE_AUTHOR("Stephen Hemminger, Shao Liu"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP Illinois"); +MODULE_VERSION("1.0"); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 1a14191..7641b27 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -86,6 +86,7 @@ int sysctl_tcp_stdurg __read_mostly; int sysctl_tcp_rfc1337 __read_mostly; int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_frto __read_mostly; +int sysctl_tcp_frto_response __read_mostly; int sysctl_tcp_nometrics_save __read_mostly; int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; @@ -100,6 +101,7 @@ #define FLAG_DATA_SACKED 0x20 /* New SAC #define FLAG_ECE 0x40 /* ECE in this ACK */ #define FLAG_DATA_LOST 0x80 /* SACK detected data lossage. */ #define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ +#define FLAG_ONLY_ORIG_SACKED 0x200 /* SACKs only non-rexmit sent before RTO */ #define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) #define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) @@ -110,6 +112,8 @@ #define IsReno(tp) ((tp)->rx_opt.sack_ok #define IsFack(tp) ((tp)->rx_opt.sack_ok & 2) #define IsDSack(tp) ((tp)->rx_opt.sack_ok & 4) +#define IsSackFrto() (sysctl_tcp_frto == 0x2) + #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH) /* Adapt the MSS value used to make delayed ack decision to the @@ -136,7 +140,7 @@ static void tcp_measure_rcv_mss(struct s * * "len" is invariant segment length, including TCP header. */ - len += skb->data - skb->h.raw; + len += skb->data - skb_transport_header(skb); if (len >= TCP_MIN_RCVMSS + sizeof(struct tcphdr) || /* If PSH is not set, packet should be * full sized, provided peer TCP is not badly broken. @@ -144,7 +148,7 @@ static void tcp_measure_rcv_mss(struct s * to handle super-low mtu links fairly. */ (len >= TCP_MIN_MSS + sizeof(struct tcphdr) && - !(tcp_flag_word(skb->h.th)&TCP_REMNANT))) { + !(tcp_flag_word(tcp_hdr(skb)) & TCP_REMNANT))) { /* Subtract also invariant (if peer is RFC compliant), * tcp header plus fixed timestamp option length. * Resulting "len" is MSS free of SACK jitter. @@ -231,9 +235,9 @@ static void tcp_fixup_sndbuf(struct sock */ /* Slow part of check#2. */ -static int __tcp_grow_window(const struct sock *sk, struct tcp_sock *tp, - const struct sk_buff *skb) +static int __tcp_grow_window(const struct sock *sk, const struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); /* Optimize this! */ int truesize = tcp_win_from_space(skb->truesize)/2; int window = tcp_win_from_space(sysctl_tcp_rmem[2])/2; @@ -248,9 +252,11 @@ static int __tcp_grow_window(const struc return 0; } -static void tcp_grow_window(struct sock *sk, struct tcp_sock *tp, +static void tcp_grow_window(struct sock *sk, struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); + /* Check #1 */ if (tp->rcv_ssthresh < tp->window_clamp && (int)tp->rcv_ssthresh < tcp_space(sk) && @@ -263,7 +269,7 @@ static void tcp_grow_window(struct sock if (tcp_win_from_space(skb->truesize) <= skb->len) incr = 2*tp->advmss; else - incr = __tcp_grow_window(sk, tp, skb); + incr = __tcp_grow_window(sk, skb); if (incr) { tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr, tp->window_clamp); @@ -326,8 +332,9 @@ static void tcp_init_buffer_space(struct } /* 5. Recalculate window clamp after socket hit its memory bounds. */ -static void tcp_clamp_window(struct sock *sk, struct tcp_sock *tp) +static void tcp_clamp_window(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); icsk->icsk_ack.quick = 0; @@ -499,8 +506,9 @@ new_measure: * each ACK we send, he increments snd_cwnd and transmits more of his * queue. -DaveM */ -static void tcp_event_data_recv(struct sock *sk, struct tcp_sock *tp, struct sk_buff *skb) +static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); u32 now; @@ -541,7 +549,7 @@ static void tcp_event_data_recv(struct s TCP_ECN_check_ce(tp, skb); if (skb->len >= 128) - tcp_grow_window(sk, tp, skb); + tcp_grow_window(sk, skb); } /* Called to compute a smoothed rtt estimate. The data fed to this @@ -574,7 +582,7 @@ static void tcp_rtt_estimator(struct soc * does not matter how to _calculate_ it. Seems, it was trap * that VJ failed to avoid. 8) */ - if(m == 0) + if (m == 0) m = 1; if (tp->srtt != 0) { m -= (tp->srtt >> 3); /* m is now error in rtt est */ @@ -759,15 +767,17 @@ __u32 tcp_init_cwnd(struct tcp_sock *tp, } /* Set slow start threshold and cwnd not falling to slow start */ -void tcp_enter_cwr(struct sock *sk) +void tcp_enter_cwr(struct sock *sk, const int set_ssthresh) { struct tcp_sock *tp = tcp_sk(sk); + const struct inet_connection_sock *icsk = inet_csk(sk); tp->prior_ssthresh = 0; tp->bytes_acked = 0; - if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { + if (icsk->icsk_ca_state < TCP_CA_CWR) { tp->undo_marker = 0; - tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk); + if (set_ssthresh) + tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp) + 1U); tp->snd_cwnd_cnt = 0; @@ -934,7 +944,8 @@ tcp_sacktag_write_queue(struct sock *sk, { const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); - unsigned char *ptr = ack_skb->h.raw + TCP_SKB_CB(ack_skb)->sacked; + unsigned char *ptr = (skb_transport_header(ack_skb) + + TCP_SKB_CB(ack_skb)->sacked); struct tcp_sack_block_wire *sp = (struct tcp_sack_block_wire *)(ptr+2); struct sk_buff *cached_skb; int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3; @@ -1038,7 +1049,7 @@ tcp_sacktag_write_queue(struct sock *sk, cached_skb = tp->fastpath_skb_hint; cached_fack_count = tp->fastpath_cnt_hint; if (!cached_skb) { - cached_skb = sk->sk_write_queue.next; + cached_skb = tcp_write_queue_head(sk); cached_fack_count = 0; } @@ -1055,10 +1066,13 @@ tcp_sacktag_write_queue(struct sock *sk, if (after(end_seq, tp->high_seq)) flag |= FLAG_DATA_LOST; - sk_stream_for_retrans_queue_from(skb, sk) { + tcp_for_write_queue_from(skb, sk) { int in_sack, pcount; u8 sacked; + if (skb == tcp_send_head(sk)) + break; + cached_skb = skb; cached_fack_count = fack_count; if (i == first_sack_index) { @@ -1159,6 +1173,18 @@ tcp_sacktag_write_queue(struct sock *sk, /* clear lost hint */ tp->retransmit_skb_hint = NULL; } + /* SACK enhanced F-RTO detection. + * Set flag if and only if non-rexmitted + * segments below frto_highmark are + * SACKed (RFC4138; Appendix B). + * Clearing correct due to in-order walk + */ + if (after(end_seq, tp->frto_highmark)) { + flag &= ~FLAG_ONLY_ORIG_SACKED; + } else { + if (!(sacked & TCPCB_RETRANS)) + flag |= FLAG_ONLY_ORIG_SACKED; + } } TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED; @@ -1195,7 +1221,9 @@ tcp_sacktag_write_queue(struct sock *sk, if (lost_retrans && icsk->icsk_ca_state == TCP_CA_Recovery) { struct sk_buff *skb; - sk_stream_for_retrans_queue(skb, sk) { + tcp_for_write_queue(skb, sk) { + if (skb == tcp_send_head(sk)) + break; if (after(TCP_SKB_CB(skb)->seq, lost_retrans)) break; if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) @@ -1224,7 +1252,8 @@ tcp_sacktag_write_queue(struct sock *sk, tp->left_out = tp->sacked_out + tp->lost_out; - if ((reord < tp->fackets_out) && icsk->icsk_ca_state != TCP_CA_Loss) + if ((reord < tp->fackets_out) && icsk->icsk_ca_state != TCP_CA_Loss && + (!tp->frto_highmark || after(tp->snd_una, tp->frto_highmark))) tcp_update_reordering(sk, ((tp->fackets_out + 1) - reord), 0); #if FASTRETRANS_DEBUG > 0 @@ -1236,9 +1265,49 @@ #endif return flag; } -/* RTO occurred, but do not yet enter loss state. Instead, transmit two new - * segments to see from the next ACKs whether any data was really missing. - * If the RTO was spurious, new ACKs should arrive. +/* F-RTO can only be used if TCP has never retransmitted anything other than + * head (SACK enhanced variant from Appendix B of RFC4138 is more robust here) + */ +int tcp_use_frto(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *skb; + + if (!sysctl_tcp_frto) + return 0; + + if (IsSackFrto()) + return 1; + + /* Avoid expensive walking of rexmit queue if possible */ + if (tp->retrans_out > 1) + return 0; + + skb = tcp_write_queue_head(sk); + skb = tcp_write_queue_next(sk, skb); /* Skips head */ + tcp_for_write_queue_from(skb, sk) { + if (skb == tcp_send_head(sk)) + break; + if (TCP_SKB_CB(skb)->sacked&TCPCB_RETRANS) + return 0; + /* Short-circuit when first non-SACKed skb has been checked */ + if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) + break; + } + return 1; +} + +/* RTO occurred, but do not yet enter Loss state. Instead, defer RTO + * recovery a bit and use heuristics in tcp_process_frto() to detect if + * the RTO was spurious. Only clear SACKED_RETRANS of the head here to + * keep retrans_out counting accurate (with SACK F-RTO, other than head + * may still have that bit set); TCPCB_LOST and remaining SACKED_RETRANS + * bits are handled if the Loss state is really to be entered (in + * tcp_enter_frto_loss). + * + * Do like tcp_enter_loss() would; when RTO expires the second time it + * does: + * "Reduce ssthresh if it has not yet been made inside this window." */ void tcp_enter_frto(struct sock *sk) { @@ -1246,39 +1315,69 @@ void tcp_enter_frto(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - tp->frto_counter = 1; - - if (icsk->icsk_ca_state <= TCP_CA_Disorder || + if ((!tp->frto_counter && icsk->icsk_ca_state <= TCP_CA_Disorder) || tp->snd_una == tp->high_seq || - (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) { + ((icsk->icsk_ca_state == TCP_CA_Loss || tp->frto_counter) && + !icsk->icsk_retransmits)) { tp->prior_ssthresh = tcp_current_ssthresh(sk); - tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); + /* Our state is too optimistic in ssthresh() call because cwnd + * is not reduced until tcp_enter_frto_loss() when previous FRTO + * recovery has not yet completed. Pattern would be this: RTO, + * Cumulative ACK, RTO (2xRTO for the same segment does not end + * up here twice). + * RFC4138 should be more specific on what to do, even though + * RTO is quite unlikely to occur after the first Cumulative ACK + * due to back-off and complexity of triggering events ... + */ + if (tp->frto_counter) { + u32 stored_cwnd; + stored_cwnd = tp->snd_cwnd; + tp->snd_cwnd = 2; + tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); + tp->snd_cwnd = stored_cwnd; + } else { + tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); + } + /* ... in theory, cong.control module could do "any tricks" in + * ssthresh(), which means that ca_state, lost bits and lost_out + * counter would have to be faked before the call occurs. We + * consider that too expensive, unlikely and hacky, so modules + * using these in ssthresh() must deal these incompatibility + * issues if they receives CA_EVENT_FRTO and frto_counter != 0 + */ tcp_ca_event(sk, CA_EVENT_FRTO); } - /* Have to clear retransmission markers here to keep the bookkeeping - * in shape, even though we are not yet in Loss state. - * If something was really lost, it is eventually caught up - * in tcp_enter_frto_loss. - */ - tp->retrans_out = 0; tp->undo_marker = tp->snd_una; tp->undo_retrans = 0; - sk_stream_for_retrans_queue(skb, sk) { - TCP_SKB_CB(skb)->sacked &= ~TCPCB_RETRANS; + skb = tcp_write_queue_head(sk); + if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) { + TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; + tp->retrans_out -= tcp_skb_pcount(skb); } tcp_sync_left_out(tp); - tcp_set_ca_state(sk, TCP_CA_Open); - tp->frto_highmark = tp->snd_nxt; + /* Earlier loss recovery underway (see RFC4138; Appendix B). + * The last condition is necessary at least in tp->frto_counter case. + */ + if (IsSackFrto() && (tp->frto_counter || + ((1 << icsk->icsk_ca_state) & (TCPF_CA_Recovery|TCPF_CA_Loss))) && + after(tp->high_seq, tp->snd_una)) { + tp->frto_highmark = tp->high_seq; + } else { + tp->frto_highmark = tp->snd_nxt; + } + tcp_set_ca_state(sk, TCP_CA_Disorder); + tp->high_seq = tp->snd_nxt; + tp->frto_counter = 1; } /* Enter Loss state after F-RTO was applied. Dupack arrived after RTO, * which indicates that we should follow the traditional RTO recovery, * i.e. mark everything lost and do go-back-N retransmission. */ -static void tcp_enter_frto_loss(struct sock *sk) +static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; @@ -1287,10 +1386,23 @@ static void tcp_enter_frto_loss(struct s tp->sacked_out = 0; tp->lost_out = 0; tp->fackets_out = 0; + tp->retrans_out = 0; - sk_stream_for_retrans_queue(skb, sk) { + tcp_for_write_queue(skb, sk) { + if (skb == tcp_send_head(sk)) + break; cnt += tcp_skb_pcount(skb); - TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST; + /* + * Count the retransmission made on RTO correctly (only when + * waiting for the first ACK and did not get it)... + */ + if ((tp->frto_counter == 1) && !(flag&FLAG_DATA_ACKED)) { + tp->retrans_out += tcp_skb_pcount(skb); + /* ...enter this if branch just for the first segment */ + flag |= FLAG_DATA_ACKED; + } else { + TCP_SKB_CB(skb)->sacked &= ~(TCPCB_LOST|TCPCB_SACKED_RETRANS); + } if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) { /* Do not mark those segments lost that were @@ -1308,7 +1420,7 @@ static void tcp_enter_frto_loss(struct s } tcp_sync_left_out(tp); - tp->snd_cwnd = tp->frto_counter + tcp_packets_in_flight(tp)+1; + tp->snd_cwnd = tcp_packets_in_flight(tp) + allowed_segments; tp->snd_cwnd_cnt = 0; tp->snd_cwnd_stamp = tcp_time_stamp; tp->undo_marker = 0; @@ -1366,7 +1478,9 @@ void tcp_enter_loss(struct sock *sk, int if (!how) tp->undo_marker = tp->snd_una; - sk_stream_for_retrans_queue(skb, sk) { + tcp_for_write_queue(skb, sk) { + if (skb == tcp_send_head(sk)) + break; cnt += tcp_skb_pcount(skb); if (TCP_SKB_CB(skb)->sacked&TCPCB_RETRANS) tp->undo_marker = 0; @@ -1401,14 +1515,14 @@ static int tcp_check_sack_reneging(struc * receiver _host_ is heavily congested (or buggy). * Do processing similar to RTO timeout. */ - if ((skb = skb_peek(&sk->sk_write_queue)) != NULL && + if ((skb = tcp_write_queue_head(sk)) != NULL && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) { struct inet_connection_sock *icsk = inet_csk(sk); NET_INC_STATS_BH(LINUX_MIB_TCPSACKRENEGING); tcp_enter_loss(sk, 1); icsk->icsk_retransmits++; - tcp_retransmit_skb(sk, skb_peek(&sk->sk_write_queue)); + tcp_retransmit_skb(sk, tcp_write_queue_head(sk)); inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX); return 1; @@ -1426,10 +1540,12 @@ static inline int tcp_skb_timedout(struc return (tcp_time_stamp - TCP_SKB_CB(skb)->when > inet_csk(sk)->icsk_rto); } -static inline int tcp_head_timedout(struct sock *sk, struct tcp_sock *tp) +static inline int tcp_head_timedout(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + return tp->packets_out && - tcp_skb_timedout(sk, skb_peek(&sk->sk_write_queue)); + tcp_skb_timedout(sk, tcp_write_queue_head(sk)); } /* Linux NewReno/SACK/FACK/ECN state machine. @@ -1525,10 +1641,15 @@ static inline int tcp_head_timedout(stru * Main question: may we further continue forward transmission * with the same cwnd? */ -static int tcp_time_to_recover(struct sock *sk, struct tcp_sock *tp) +static int tcp_time_to_recover(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); __u32 packets_out; + /* Do not perform any recovery during FRTO algorithm */ + if (tp->frto_counter) + return 0; + /* Trick#1: The loss is proven. */ if (tp->lost_out) return 1; @@ -1540,7 +1661,7 @@ static int tcp_time_to_recover(struct so /* Trick#3 : when we use RFC2988 timer restart, fast * retransmit can be triggered by timeout of queue head. */ - if (tcp_head_timedout(sk, tp)) + if (tcp_head_timedout(sk)) return 1; /* Trick#4: It is still not OK... But will it be useful to delay @@ -1549,7 +1670,7 @@ static int tcp_time_to_recover(struct so packets_out = tp->packets_out; if (packets_out <= tp->reordering && tp->sacked_out >= max_t(__u32, packets_out/2, sysctl_tcp_reordering) && - !tcp_may_send_now(sk, tp)) { + !tcp_may_send_now(sk)) { /* We have nothing to send. This connection is limited * either by receiver window or by application. */ @@ -1589,8 +1710,10 @@ static void tcp_add_reno_sack(struct soc /* Account for ACK, ACKing some data in Reno Recovery phase. */ -static void tcp_remove_reno_sacks(struct sock *sk, struct tcp_sock *tp, int acked) +static void tcp_remove_reno_sacks(struct sock *sk, int acked) { + struct tcp_sock *tp = tcp_sk(sk); + if (acked > 0) { /* One ACK acked hole. The rest eat duplicate ACKs. */ if (acked-1 >= tp->sacked_out) @@ -1609,9 +1732,10 @@ static inline void tcp_reset_reno_sack(s } /* Mark head of queue up as lost. */ -static void tcp_mark_head_lost(struct sock *sk, struct tcp_sock *tp, +static void tcp_mark_head_lost(struct sock *sk, int packets, u32 high_seq) { + struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; int cnt; @@ -1620,11 +1744,13 @@ static void tcp_mark_head_lost(struct so skb = tp->lost_skb_hint; cnt = tp->lost_cnt_hint; } else { - skb = sk->sk_write_queue.next; + skb = tcp_write_queue_head(sk); cnt = 0; } - sk_stream_for_retrans_queue_from(skb, sk) { + tcp_for_write_queue_from(skb, sk) { + if (skb == tcp_send_head(sk)) + break; /* TODO: do this better */ /* this is not the most efficient way to do this... */ tp->lost_skb_hint = skb; @@ -1638,12 +1764,11 @@ static void tcp_mark_head_lost(struct so /* clear xmit_retransmit_queue hints * if this is beyond hint */ - if(tp->retransmit_skb_hint != NULL && - before(TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(tp->retransmit_skb_hint)->seq)) { - + if (tp->retransmit_skb_hint != NULL && + before(TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(tp->retransmit_skb_hint)->seq)) tp->retransmit_skb_hint = NULL; - } + } } tcp_sync_left_out(tp); @@ -1651,15 +1776,17 @@ static void tcp_mark_head_lost(struct so /* Account newly detected lost packet(s) */ -static void tcp_update_scoreboard(struct sock *sk, struct tcp_sock *tp) +static void tcp_update_scoreboard(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (IsFack(tp)) { int lost = tp->fackets_out - tp->reordering; if (lost <= 0) lost = 1; - tcp_mark_head_lost(sk, tp, lost, tp->high_seq); + tcp_mark_head_lost(sk, lost, tp->high_seq); } else { - tcp_mark_head_lost(sk, tp, 1, tp->high_seq); + tcp_mark_head_lost(sk, 1, tp->high_seq); } /* New heuristics: it is possible only after we switched @@ -1667,13 +1794,15 @@ static void tcp_update_scoreboard(struct * Hence, we can detect timed out packets during fast * retransmit without falling to slow start. */ - if (!IsReno(tp) && tcp_head_timedout(sk, tp)) { + if (!IsReno(tp) && tcp_head_timedout(sk)) { struct sk_buff *skb; skb = tp->scoreboard_skb_hint ? tp->scoreboard_skb_hint - : sk->sk_write_queue.next; + : tcp_write_queue_head(sk); - sk_stream_for_retrans_queue_from(skb, sk) { + tcp_for_write_queue_from(skb, sk) { + if (skb == tcp_send_head(sk)) + break; if (!tcp_skb_timedout(sk, skb)) break; @@ -1745,9 +1874,11 @@ static inline int tcp_packet_delayed(str /* Undo procedures. */ #if FASTRETRANS_DEBUG > 1 -static void DBGUNDO(struct sock *sk, struct tcp_sock *tp, const char *msg) +static void DBGUNDO(struct sock *sk, const char *msg) { + struct tcp_sock *tp = tcp_sk(sk); struct inet_sock *inet = inet_sk(sk); + printk(KERN_DEBUG "Undo %s %u.%u.%u.%u/%u c%u l%u ss%u/%u p%u\n", msg, NIPQUAD(inet->daddr), ntohs(inet->dport), @@ -1793,13 +1924,15 @@ static inline int tcp_may_undo(struct tc } /* People celebrate: "We love our President!" */ -static int tcp_try_undo_recovery(struct sock *sk, struct tcp_sock *tp) +static int tcp_try_undo_recovery(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (tcp_may_undo(tp)) { /* Happy end! We did not retransmit anything * or our original transmission succeeded. */ - DBGUNDO(sk, tp, inet_csk(sk)->icsk_ca_state == TCP_CA_Loss ? "loss" : "retrans"); + DBGUNDO(sk, inet_csk(sk)->icsk_ca_state == TCP_CA_Loss ? "loss" : "retrans"); tcp_undo_cwr(sk, 1); if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) NET_INC_STATS_BH(LINUX_MIB_TCPLOSSUNDO); @@ -1819,10 +1952,12 @@ static int tcp_try_undo_recovery(struct } /* Try to undo cwnd reduction, because D-SACKs acked all retransmitted data */ -static void tcp_try_undo_dsack(struct sock *sk, struct tcp_sock *tp) +static void tcp_try_undo_dsack(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (tp->undo_marker && !tp->undo_retrans) { - DBGUNDO(sk, tp, "D-SACK"); + DBGUNDO(sk, "D-SACK"); tcp_undo_cwr(sk, 1); tp->undo_marker = 0; NET_INC_STATS_BH(LINUX_MIB_TCPDSACKUNDO); @@ -1831,9 +1966,9 @@ static void tcp_try_undo_dsack(struct so /* Undo during fast recovery after partial ACK. */ -static int tcp_try_undo_partial(struct sock *sk, struct tcp_sock *tp, - int acked) +static int tcp_try_undo_partial(struct sock *sk, int acked) { + struct tcp_sock *tp = tcp_sk(sk); /* Partial ACK arrived. Force Hoe's retransmit. */ int failed = IsReno(tp) || tp->fackets_out>tp->reordering; @@ -1846,7 +1981,7 @@ static int tcp_try_undo_partial(struct s tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1); - DBGUNDO(sk, tp, "Hoe"); + DBGUNDO(sk, "Hoe"); tcp_undo_cwr(sk, 0); NET_INC_STATS_BH(LINUX_MIB_TCPPARTIALUNDO); @@ -1860,17 +1995,21 @@ static int tcp_try_undo_partial(struct s } /* Undo during loss recovery after partial ACK. */ -static int tcp_try_undo_loss(struct sock *sk, struct tcp_sock *tp) +static int tcp_try_undo_loss(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (tcp_may_undo(tp)) { struct sk_buff *skb; - sk_stream_for_retrans_queue(skb, sk) { + tcp_for_write_queue(skb, sk) { + if (skb == tcp_send_head(sk)) + break; TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST; } clear_all_retrans_hints(tp); - DBGUNDO(sk, tp, "partial loss"); + DBGUNDO(sk, "partial loss"); tp->lost_out = 0; tp->left_out = tp->sacked_out; tcp_undo_cwr(sk, 1); @@ -1892,15 +2031,17 @@ static inline void tcp_complete_cwr(stru tcp_ca_event(sk, CA_EVENT_COMPLETE_CWR); } -static void tcp_try_to_open(struct sock *sk, struct tcp_sock *tp, int flag) +static void tcp_try_to_open(struct sock *sk, int flag) { + struct tcp_sock *tp = tcp_sk(sk); + tp->left_out = tp->sacked_out; if (tp->retrans_out == 0) tp->retrans_stamp = 0; if (flag&FLAG_ECE) - tcp_enter_cwr(sk); + tcp_enter_cwr(sk, 1); if (inet_csk(sk)->icsk_ca_state != TCP_CA_CWR) { int state = TCP_CA_Open; @@ -1987,7 +2128,7 @@ tcp_fastretrans_alert(struct sock *sk, u before(tp->snd_una, tp->high_seq) && icsk->icsk_ca_state != TCP_CA_Open && tp->fackets_out > tp->reordering) { - tcp_mark_head_lost(sk, tp, tp->fackets_out-tp->reordering, tp->high_seq); + tcp_mark_head_lost(sk, tp->fackets_out-tp->reordering, tp->high_seq); NET_INC_STATS_BH(LINUX_MIB_TCPLOSS); } @@ -1997,14 +2138,13 @@ tcp_fastretrans_alert(struct sock *sk, u /* E. Check state exit conditions. State can be terminated * when high_seq is ACKed. */ if (icsk->icsk_ca_state == TCP_CA_Open) { - if (!sysctl_tcp_frto) - BUG_TRAP(tp->retrans_out == 0); + BUG_TRAP(tp->retrans_out == 0); tp->retrans_stamp = 0; } else if (!before(tp->snd_una, tp->high_seq)) { switch (icsk->icsk_ca_state) { case TCP_CA_Loss: icsk->icsk_retransmits = 0; - if (tcp_try_undo_recovery(sk, tp)) + if (tcp_try_undo_recovery(sk)) return; break; @@ -2018,7 +2158,7 @@ tcp_fastretrans_alert(struct sock *sk, u break; case TCP_CA_Disorder: - tcp_try_undo_dsack(sk, tp); + tcp_try_undo_dsack(sk); if (!tp->undo_marker || /* For SACK case do not Open to allow to undo * catching for all duplicate ACKs. */ @@ -2031,7 +2171,7 @@ tcp_fastretrans_alert(struct sock *sk, u case TCP_CA_Recovery: if (IsReno(tp)) tcp_reset_reno_sack(tp); - if (tcp_try_undo_recovery(sk, tp)) + if (tcp_try_undo_recovery(sk)) return; tcp_complete_cwr(sk); break; @@ -2047,14 +2187,14 @@ tcp_fastretrans_alert(struct sock *sk, u } else { int acked = prior_packets - tp->packets_out; if (IsReno(tp)) - tcp_remove_reno_sacks(sk, tp, acked); - is_dupack = tcp_try_undo_partial(sk, tp, acked); + tcp_remove_reno_sacks(sk, acked); + is_dupack = tcp_try_undo_partial(sk, acked); } break; case TCP_CA_Loss: if (flag&FLAG_DATA_ACKED) icsk->icsk_retransmits = 0; - if (!tcp_try_undo_loss(sk, tp)) { + if (!tcp_try_undo_loss(sk)) { tcp_moderate_cwnd(tp); tcp_xmit_retransmit_queue(sk); return; @@ -2071,10 +2211,10 @@ tcp_fastretrans_alert(struct sock *sk, u } if (icsk->icsk_ca_state == TCP_CA_Disorder) - tcp_try_undo_dsack(sk, tp); + tcp_try_undo_dsack(sk); - if (!tcp_time_to_recover(sk, tp)) { - tcp_try_to_open(sk, tp, flag); + if (!tcp_time_to_recover(sk)) { + tcp_try_to_open(sk, flag); return; } @@ -2113,8 +2253,8 @@ tcp_fastretrans_alert(struct sock *sk, u tcp_set_ca_state(sk, TCP_CA_Recovery); } - if (is_dupack || tcp_head_timedout(sk, tp)) - tcp_update_scoreboard(sk, tp); + if (is_dupack || tcp_head_timedout(sk)) + tcp_update_scoreboard(sk); tcp_cwnd_down(sk); tcp_xmit_retransmit_queue(sk); } @@ -2190,8 +2330,10 @@ static void tcp_cong_avoid(struct sock * * RFC2988 recommends to restart timer to now+rto. */ -static void tcp_ack_packets_out(struct sock *sk, struct tcp_sock *tp) +static void tcp_ack_packets_out(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (!tp->packets_out) { inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); } else { @@ -2255,14 +2397,6 @@ static int tcp_tso_acked(struct sock *sk return acked; } -static u32 tcp_usrtt(struct timeval *tv) -{ - struct timeval now; - - do_gettimeofday(&now); - return (now.tv_sec - tv->tv_sec) * 1000000 + (now.tv_usec - tv->tv_usec); -} - /* Remove acknowledged frames from the retransmission queue. */ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p) { @@ -2273,12 +2407,10 @@ static int tcp_clean_rtx_queue(struct so int acked = 0; __s32 seq_rtt = -1; u32 pkts_acked = 0; - void (*rtt_sample)(struct sock *sk, u32 usrtt) - = icsk->icsk_ca_ops->rtt_sample; - struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + ktime_t last_ackt = ktime_set(0,0); - while ((skb = skb_peek(&sk->sk_write_queue)) && - skb != sk->sk_send_head) { + while ((skb = tcp_write_queue_head(sk)) && + skb != tcp_send_head(sk)) { struct tcp_skb_cb *scb = TCP_SKB_CB(skb); __u8 sacked = scb->sacked; @@ -2318,13 +2450,13 @@ static int tcp_clean_rtx_queue(struct so if (sacked) { if (sacked & TCPCB_RETRANS) { - if(sacked & TCPCB_SACKED_RETRANS) + if (sacked & TCPCB_SACKED_RETRANS) tp->retrans_out -= tcp_skb_pcount(skb); acked |= FLAG_RETRANS_DATA_ACKED; seq_rtt = -1; } else if (seq_rtt < 0) { seq_rtt = now - scb->when; - skb_get_timestamp(skb, &tv); + last_ackt = skb->tstamp; } if (sacked & TCPCB_SACKED_ACKED) tp->sacked_out -= tcp_skb_pcount(skb); @@ -2337,23 +2469,24 @@ static int tcp_clean_rtx_queue(struct so } } else if (seq_rtt < 0) { seq_rtt = now - scb->when; - skb_get_timestamp(skb, &tv); + last_ackt = skb->tstamp; } tcp_dec_pcount_approx(&tp->fackets_out, skb); tcp_packets_out_dec(tp, skb); - __skb_unlink(skb, &sk->sk_write_queue); + tcp_unlink_write_queue(skb, sk); sk_stream_free_skb(sk, skb); clear_all_retrans_hints(tp); } if (acked&FLAG_ACKED) { + const struct tcp_congestion_ops *ca_ops + = inet_csk(sk)->icsk_ca_ops; + tcp_ack_update_rtt(sk, acked, seq_rtt); - tcp_ack_packets_out(sk, tp); - if (rtt_sample && !(acked & FLAG_RETRANS_DATA_ACKED)) - (*rtt_sample)(sk, tcp_usrtt(&tv)); + tcp_ack_packets_out(sk); - if (icsk->icsk_ca_ops->pkts_acked) - icsk->icsk_ca_ops->pkts_acked(sk, pkts_acked); + if (ca_ops->pkts_acked) + ca_ops->pkts_acked(sk, pkts_acked, last_ackt); } #if FASTRETRANS_DEBUG > 0 @@ -2390,7 +2523,7 @@ static void tcp_ack_probe(struct sock *s /* Was it a usable window open? */ - if (!after(TCP_SKB_CB(sk->sk_send_head)->end_seq, + if (!after(TCP_SKB_CB(tcp_send_head(sk))->end_seq, tp->snd_una + tp->snd_wnd)) { icsk->icsk_backoff = 0; inet_csk_clear_xmit_timer(sk, ICSK_TIME_PROBE0); @@ -2433,13 +2566,14 @@ static inline int tcp_may_update_window( * Window update algorithm, described in RFC793/RFC1122 (used in linux-2.2 * and in FreeBSD. NetBSD's one is even worse.) is wrong. */ -static int tcp_ack_update_window(struct sock *sk, struct tcp_sock *tp, - struct sk_buff *skb, u32 ack, u32 ack_seq) +static int tcp_ack_update_window(struct sock *sk, struct sk_buff *skb, u32 ack, + u32 ack_seq) { + struct tcp_sock *tp = tcp_sk(sk); int flag = 0; - u32 nwin = ntohs(skb->h.th->window); + u32 nwin = ntohs(tcp_hdr(skb)->window); - if (likely(!skb->h.th->syn)) + if (likely(!tcp_hdr(skb)->syn)) nwin <<= tp->rx_opt.snd_wscale; if (tcp_may_update_window(tp, ack, ack_seq, nwin)) { @@ -2453,7 +2587,7 @@ static int tcp_ack_update_window(struct * fast path is recovered for sending TCP. */ tp->pred_flags = 0; - tcp_fast_path_check(sk, tp); + tcp_fast_path_check(sk); if (nwin > tp->max_window) { tp->max_window = nwin; @@ -2467,39 +2601,139 @@ static int tcp_ack_update_window(struct return flag; } -static void tcp_process_frto(struct sock *sk, u32 prior_snd_una) +/* A very conservative spurious RTO response algorithm: reduce cwnd and + * continue in congestion avoidance. + */ +static void tcp_conservative_spur_to_response(struct tcp_sock *tp) +{ + tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); + tp->snd_cwnd_cnt = 0; + tcp_moderate_cwnd(tp); +} + +/* A conservative spurious RTO response algorithm: reduce cwnd using + * rate halving and continue in congestion avoidance. + */ +static void tcp_ratehalving_spur_to_response(struct sock *sk) +{ + tcp_enter_cwr(sk, 0); +} + +static void tcp_undo_spur_to_response(struct sock *sk, int flag) +{ + if (flag&FLAG_ECE) + tcp_ratehalving_spur_to_response(sk); + else + tcp_undo_cwr(sk, 1); +} + +/* F-RTO spurious RTO detection algorithm (RFC4138) + * + * F-RTO affects during two new ACKs following RTO (well, almost, see inline + * comments). State (ACK number) is kept in frto_counter. When ACK advances + * window (but not to or beyond highest sequence sent before RTO): + * On First ACK, send two new segments out. + * On Second ACK, RTO was likely spurious. Do spurious response (response + * algorithm is not part of the F-RTO detection algorithm + * given in RFC4138 but can be selected separately). + * Otherwise (basically on duplicate ACK), RTO was (likely) caused by a loss + * and TCP falls back to conventional RTO recovery. F-RTO allows overriding + * of Nagle, this is done using frto_counter states 2 and 3, when a new data + * segment of any size sent during F-RTO, state 2 is upgraded to 3. + * + * Rationale: if the RTO was spurious, new ACKs should arrive from the + * original window even after we transmit two new data segments. + * + * SACK version: + * on first step, wait until first cumulative ACK arrives, then move to + * the second step. In second step, the next ACK decides. + * + * F-RTO is implemented (mainly) in four functions: + * - tcp_use_frto() is used to determine if TCP is can use F-RTO + * - tcp_enter_frto() prepares TCP state on RTO if F-RTO is used, it is + * called when tcp_use_frto() showed green light + * - tcp_process_frto() handles incoming ACKs during F-RTO algorithm + * - tcp_enter_frto_loss() is called if there is not enough evidence + * to prove that the RTO is indeed spurious. It transfers the control + * from F-RTO to the conventional RTO recovery + */ +static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag) { struct tcp_sock *tp = tcp_sk(sk); tcp_sync_left_out(tp); - if (tp->snd_una == prior_snd_una || - !before(tp->snd_una, tp->frto_highmark)) { - /* RTO was caused by loss, start retransmitting in - * go-back-N slow start + /* Duplicate the behavior from Loss state (fastretrans_alert) */ + if (flag&FLAG_DATA_ACKED) + inet_csk(sk)->icsk_retransmits = 0; + + if (!before(tp->snd_una, tp->frto_highmark)) { + tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3), flag); + return 1; + } + + if (!IsSackFrto() || IsReno(tp)) { + /* RFC4138 shortcoming in step 2; should also have case c): + * ACK isn't duplicate nor advances window, e.g., opposite dir + * data, winupdate */ - tcp_enter_frto_loss(sk); - return; + if ((tp->snd_una == prior_snd_una) && (flag&FLAG_NOT_DUP) && + !(flag&FLAG_FORWARD_PROGRESS)) + return 1; + + if (!(flag&FLAG_DATA_ACKED)) { + tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 0 : 3), + flag); + return 1; + } + } else { + if (!(flag&FLAG_DATA_ACKED) && (tp->frto_counter == 1)) { + /* Prevent sending of new data. */ + tp->snd_cwnd = min(tp->snd_cwnd, + tcp_packets_in_flight(tp)); + return 1; + } + + if ((tp->frto_counter >= 2) && + (!(flag&FLAG_FORWARD_PROGRESS) || + ((flag&FLAG_DATA_SACKED) && !(flag&FLAG_ONLY_ORIG_SACKED)))) { + /* RFC4138 shortcoming (see comment above) */ + if (!(flag&FLAG_FORWARD_PROGRESS) && (flag&FLAG_NOT_DUP)) + return 1; + + tcp_enter_frto_loss(sk, 3, flag); + return 1; + } } if (tp->frto_counter == 1) { - /* First ACK after RTO advances the window: allow two new - * segments out. - */ + /* Sending of the next skb must be allowed or no FRTO */ + if (!tcp_send_head(sk) || + after(TCP_SKB_CB(tcp_send_head(sk))->end_seq, + tp->snd_una + tp->snd_wnd)) { + tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3), + flag); + return 1; + } + tp->snd_cwnd = tcp_packets_in_flight(tp) + 2; + tp->frto_counter = 2; + return 1; } else { - /* Also the second ACK after RTO advances the window. - * The RTO was likely spurious. Reduce cwnd and continue - * in congestion avoidance - */ - tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); - tcp_moderate_cwnd(tp); + switch (sysctl_tcp_frto_response) { + case 2: + tcp_undo_spur_to_response(sk, flag); + break; + case 1: + tcp_conservative_spur_to_response(tp); + break; + default: + tcp_ratehalving_spur_to_response(sk); + break; + } + tp->frto_counter = 0; } - - /* F-RTO affects on two new ACKs following RTO. - * At latest on third ACK the TCP behavior is back to normal. - */ - tp->frto_counter = (tp->frto_counter + 1) % 3; + return 0; } /* This routine deals with incoming acks, but not outgoing ones. */ @@ -2513,6 +2747,7 @@ static int tcp_ack(struct sock *sk, stru u32 prior_in_flight; s32 seq_rtt; int prior_packets; + int frto_cwnd = 0; /* If the ack is newer than sent or older than previous acks * then we can probably ignore it. @@ -2549,12 +2784,12 @@ static int tcp_ack(struct sock *sk, stru else NET_INC_STATS_BH(LINUX_MIB_TCPPUREACKS); - flag |= tcp_ack_update_window(sk, tp, skb, ack, ack_seq); + flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); if (TCP_SKB_CB(skb)->sacked) flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una); - if (TCP_ECN_rcv_ecn_echo(tp, skb->h.th)) + if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb))) flag |= FLAG_ECE; tcp_ca_event(sk, CA_EVENT_SLOW_ACK); @@ -2575,15 +2810,16 @@ static int tcp_ack(struct sock *sk, stru flag |= tcp_clean_rtx_queue(sk, &seq_rtt); if (tp->frto_counter) - tcp_process_frto(sk, prior_snd_una); + frto_cwnd = tcp_process_frto(sk, prior_snd_una, flag); if (tcp_ack_is_dubious(sk, flag)) { /* Advance CWND, if state allows this. */ - if ((flag & FLAG_DATA_ACKED) && tcp_may_raise_cwnd(sk, flag)) + if ((flag & FLAG_DATA_ACKED) && !frto_cwnd && + tcp_may_raise_cwnd(sk, flag)) tcp_cong_avoid(sk, ack, seq_rtt, prior_in_flight, 0); tcp_fastretrans_alert(sk, prior_snd_una, prior_packets, flag); } else { - if ((flag & FLAG_DATA_ACKED)) + if ((flag & FLAG_DATA_ACKED) && !frto_cwnd) tcp_cong_avoid(sk, ack, seq_rtt, prior_in_flight, 1); } @@ -2599,7 +2835,7 @@ no_queue: * being used to time the probes, and is probably far higher than * it needs to be for normal retransmission. */ - if (sk->sk_send_head) + if (tcp_send_head(sk)) tcp_ack_probe(sk); return 1; @@ -2620,13 +2856,13 @@ uninteresting_ack: void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, int estab) { unsigned char *ptr; - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); int length=(th->doff*4)-sizeof(struct tcphdr); ptr = (unsigned char *)(th + 1); opt_rx->saw_tstamp = 0; - while(length>0) { + while (length > 0) { int opcode=*ptr++; int opsize; @@ -2642,9 +2878,9 @@ void tcp_parse_options(struct sk_buff *s return; if (opsize > length) return; /* don't parse partial options */ - switch(opcode) { + switch (opcode) { case TCPOPT_MSS: - if(opsize==TCPOLEN_MSS && th->syn && !estab) { + if (opsize==TCPOLEN_MSS && th->syn && !estab) { u16 in_mss = ntohs(get_unaligned((__be16 *)ptr)); if (in_mss) { if (opt_rx->user_mss && opt_rx->user_mss < in_mss) @@ -2654,12 +2890,12 @@ void tcp_parse_options(struct sk_buff *s } break; case TCPOPT_WINDOW: - if(opsize==TCPOLEN_WINDOW && th->syn && !estab) + if (opsize==TCPOLEN_WINDOW && th->syn && !estab) if (sysctl_tcp_window_scaling) { __u8 snd_wscale = *(__u8 *) ptr; opt_rx->wscale_ok = 1; if (snd_wscale > 14) { - if(net_ratelimit()) + if (net_ratelimit()) printk(KERN_INFO "tcp_parse_options: Illegal window " "scaling value %d >14 received.\n", snd_wscale); @@ -2669,7 +2905,7 @@ void tcp_parse_options(struct sk_buff *s } break; case TCPOPT_TIMESTAMP: - if(opsize==TCPOLEN_TIMESTAMP) { + if (opsize==TCPOLEN_TIMESTAMP) { if ((estab && opt_rx->tstamp_ok) || (!estab && sysctl_tcp_timestamps)) { opt_rx->saw_tstamp = 1; @@ -2679,7 +2915,7 @@ void tcp_parse_options(struct sk_buff *s } break; case TCPOPT_SACK_PERM: - if(opsize==TCPOLEN_SACK_PERM && th->syn && !estab) { + if (opsize==TCPOLEN_SACK_PERM && th->syn && !estab) { if (sysctl_tcp_sack) { opt_rx->sack_ok = 1; tcp_sack_reset(opt_rx); @@ -2688,7 +2924,7 @@ void tcp_parse_options(struct sk_buff *s break; case TCPOPT_SACK: - if((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) && + if ((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) && !((opsize - TCPOLEN_SACK_BASE) % TCPOLEN_SACK_PERBLOCK) && opt_rx->sack_ok) { TCP_SKB_CB(skb)->sacked = (ptr - 2) - (unsigned char *)th; @@ -2701,10 +2937,11 @@ #ifdef CONFIG_TCP_MD5SIG */ break; #endif - }; + } + ptr+=opsize-2; length-=opsize; - }; + } } } @@ -2737,7 +2974,7 @@ static int tcp_fast_parse_options(struct static inline void tcp_store_ts_recent(struct tcp_sock *tp) { tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval; - tp->rx_opt.ts_recent_stamp = xtime.tv_sec; + tp->rx_opt.ts_recent_stamp = get_seconds(); } static inline void tcp_replace_ts_recent(struct tcp_sock *tp, u32 seq) @@ -2750,8 +2987,8 @@ static inline void tcp_replace_ts_recent * Not only, also it occurs for expired timestamps. */ - if((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) >= 0 || - xtime.tv_sec >= tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS) + if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) >= 0 || + get_seconds() >= tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS) tcp_store_ts_recent(tp); } } @@ -2782,7 +3019,7 @@ static inline void tcp_replace_ts_recent static int tcp_disordered_ack(const struct sock *sk, const struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); u32 seq = TCP_SKB_CB(skb)->seq; u32 ack = TCP_SKB_CB(skb)->ack_seq; @@ -2803,7 +3040,7 @@ static inline int tcp_paws_discard(const { const struct tcp_sock *tp = tcp_sk(sk); return ((s32)(tp->rx_opt.ts_recent - tp->rx_opt.rcv_tsval) > TCP_PAWS_WINDOW && - xtime.tv_sec < tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS && + get_seconds() < tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS && !tcp_disordered_ack(sk, skb)); } @@ -2910,7 +3147,7 @@ static void tcp_fin(struct sk_buff *skb, printk(KERN_ERR "%s: Impossible, sk->sk_state=%d\n", __FUNCTION__, sk->sk_state); break; - }; + } /* It _is_ possible, that we have something out-of-order _after_ FIN. * Probably, we should reset in this case. For now drop them. @@ -3009,7 +3246,7 @@ static void tcp_sack_maybe_coalesce(stru */ tp->rx_opt.num_sacks--; tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + tp->rx_opt.dsack, 4 - tp->rx_opt.tstamp_ok); - for(i=this_sack; i < tp->rx_opt.num_sacks; i++) + for (i=this_sack; i < tp->rx_opt.num_sacks; i++) sp[i] = sp[i+1]; continue; } @@ -3062,7 +3299,7 @@ static void tcp_sack_new_ofo_skb(struct tp->rx_opt.num_sacks--; sp--; } - for(; this_sack > 0; this_sack--, sp--) + for (; this_sack > 0; this_sack--, sp--) *sp = *(sp-1); new_sack: @@ -3088,7 +3325,7 @@ static void tcp_sack_remove(struct tcp_s return; } - for(this_sack = 0; this_sack < num_sacks; ) { + for (this_sack = 0; this_sack < num_sacks; ) { /* Check if the start of the sack is covered by RCV.NXT. */ if (!before(tp->rcv_nxt, sp->start_seq)) { int i; @@ -3144,8 +3381,8 @@ static void tcp_ofo_queue(struct sock *s __skb_unlink(skb, &tp->out_of_order_queue); __skb_queue_tail(&sk->sk_receive_queue, skb); tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - if(skb->h.th->fin) - tcp_fin(skb, sk, skb->h.th); + if (tcp_hdr(skb)->fin) + tcp_fin(skb, sk, tcp_hdr(skb)); } } @@ -3153,7 +3390,7 @@ static int tcp_prune_queue(struct sock * static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) { - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); struct tcp_sock *tp = tcp_sk(sk); int eaten = -1; @@ -3210,9 +3447,9 @@ queue_and_out: __skb_queue_tail(&sk->sk_receive_queue, skb); } tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - if(skb->len) - tcp_event_data_recv(sk, tp, skb); - if(th->fin) + if (skb->len) + tcp_event_data_recv(sk, skb); + if (th->fin) tcp_fin(skb, sk, th); if (!skb_queue_empty(&tp->out_of_order_queue)) { @@ -3228,7 +3465,7 @@ queue_and_out: if (tp->rx_opt.num_sacks) tcp_sack_remove(tp); - tcp_fast_path_check(sk, tp); + tcp_fast_path_check(sk); if (eaten > 0) __kfree_skb(skb); @@ -3392,7 +3629,7 @@ tcp_collapse(struct sock *sk, struct sk_ * - bloated or contains data before "start" or * overlaps to the next one. */ - if (!skb->h.th->syn && !skb->h.th->fin && + if (!tcp_hdr(skb)->syn && !tcp_hdr(skb)->fin && (tcp_win_from_space(skb->truesize) > skb->len || before(TCP_SKB_CB(skb)->seq, start) || (skb->next != tail && @@ -3403,7 +3640,7 @@ tcp_collapse(struct sock *sk, struct sk_ start = TCP_SKB_CB(skb)->end_seq; skb = skb->next; } - if (skb == tail || skb->h.th->syn || skb->h.th->fin) + if (skb == tail || tcp_hdr(skb)->syn || tcp_hdr(skb)->fin) return; while (before(start, end)) { @@ -3419,11 +3656,14 @@ tcp_collapse(struct sock *sk, struct sk_ nskb = alloc_skb(copy+header, GFP_ATOMIC); if (!nskb) return; + + skb_set_mac_header(nskb, skb_mac_header(skb) - skb->head); + skb_set_network_header(nskb, (skb_network_header(skb) - + skb->head)); + skb_set_transport_header(nskb, (skb_transport_header(skb) - + skb->head)); skb_reserve(nskb, header); memcpy(nskb->head, skb->head, header); - nskb->nh.raw = nskb->head + (skb->nh.raw-skb->head); - nskb->h.raw = nskb->head + (skb->h.raw-skb->head); - nskb->mac.raw = nskb->head + (skb->mac.raw-skb->head); memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(nskb)->end_seq = start; __skb_insert(nskb, skb->prev, skb, list); @@ -3449,7 +3689,9 @@ tcp_collapse(struct sock *sk, struct sk_ __kfree_skb(skb); NET_INC_STATS_BH(LINUX_MIB_TCPRCVCOLLAPSED); skb = next; - if (skb == tail || skb->h.th->syn || skb->h.th->fin) + if (skb == tail || + tcp_hdr(skb)->syn || + tcp_hdr(skb)->fin) return; } } @@ -3514,7 +3756,7 @@ static int tcp_prune_queue(struct sock * NET_INC_STATS_BH(LINUX_MIB_PRUNECALLED); if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) - tcp_clamp_window(sk, tp); + tcp_clamp_window(sk); else if (tcp_memory_pressure) tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss); @@ -3583,8 +3825,10 @@ void tcp_cwnd_application_limited(struct tp->snd_cwnd_stamp = tcp_time_stamp; } -static int tcp_should_expand_sndbuf(struct sock *sk, struct tcp_sock *tp) +static int tcp_should_expand_sndbuf(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + /* If the user specified a specific send buffer setting, do * not modify it. */ @@ -3616,7 +3860,7 @@ static void tcp_new_space(struct sock *s { struct tcp_sock *tp = tcp_sk(sk); - if (tcp_should_expand_sndbuf(sk, tp)) { + if (tcp_should_expand_sndbuf(sk)) { int sndmem = max_t(u32, tp->rx_opt.mss_clamp, tp->mss_cache) + MAX_TCP_HEADER + 16 + sizeof(struct sk_buff), demanded = max_t(unsigned int, tp->snd_cwnd, @@ -3640,9 +3884,9 @@ static void tcp_check_space(struct sock } } -static inline void tcp_data_snd_check(struct sock *sk, struct tcp_sock *tp) +static inline void tcp_data_snd_check(struct sock *sk) { - tcp_push_pending_frames(sk, tp); + tcp_push_pending_frames(sk); tcp_check_space(sk); } @@ -3790,7 +4034,7 @@ static int tcp_copy_to_iovec(struct sock int err; local_bh_enable(); - if (skb->ip_summed==CHECKSUM_UNNECESSARY) + if (skb_csum_unnecessary(skb)) err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk); else err = skb_copy_and_csum_datagram_iovec(skb, hlen, @@ -3822,7 +4066,7 @@ static __sum16 __tcp_checksum_complete_u static inline int tcp_checksum_complete_user(struct sock *sk, struct sk_buff *skb) { - return skb->ip_summed != CHECKSUM_UNNECESSARY && + return !skb_csum_unnecessary(skb) && __tcp_checksum_complete_user(sk, skb); } @@ -3840,7 +4084,7 @@ static int tcp_dma_try_early_copy(struct if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) tp->ucopy.dma_chan = get_softnet_dma(); - if (tp->ucopy.dma_chan && skb->ip_summed == CHECKSUM_UNNECESSARY) { + if (tp->ucopy.dma_chan && skb_csum_unnecessary(skb)) { dma_cookie = dma_skb_copy_datagram_iovec(tp->ucopy.dma_chan, skb, hlen, tp->ucopy.iov, chunk, tp->ucopy.pinned_list); @@ -3856,7 +4100,7 @@ static int tcp_dma_try_early_copy(struct tcp_rcv_space_adjust(sk); if ((tp->ucopy.len == 0) || - (tcp_flag_word(skb->h.th) & TCP_FLAG_PSH) || + (tcp_flag_word(tcp_hdr(skb)) & TCP_FLAG_PSH) || (atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1))) { tp->ucopy.wakeup = 1; sk->sk_data_ready(sk, 0); @@ -3976,7 +4220,7 @@ int tcp_rcv_established(struct sock *sk, */ tcp_ack(sk, skb, 0); __kfree_skb(skb); - tcp_data_snd_check(sk, tp); + tcp_data_snd_check(sk); return 0; } else { /* Header too small */ TCP_INC_STATS_BH(TCP_MIB_INERRS); @@ -4047,12 +4291,12 @@ #endif tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; } - tcp_event_data_recv(sk, tp, skb); + tcp_event_data_recv(sk, skb); if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) { /* Well, only one small jumplet in fast path... */ tcp_ack(sk, skb, FLAG_DATA); - tcp_data_snd_check(sk, tp); + tcp_data_snd_check(sk); if (!inet_csk_ack_scheduled(sk)) goto no_ack; } @@ -4109,7 +4353,7 @@ slow_path: goto discard; } - if(th->rst) { + if (th->rst) { tcp_reset(sk); goto discard; } @@ -4124,7 +4368,7 @@ slow_path: } step5: - if(th->ack) + if (th->ack) tcp_ack(sk, skb, FLAG_SLOWPATH); tcp_rcv_rtt_measure_ts(sk, skb); @@ -4135,7 +4379,7 @@ step5: /* step 7: process the segment text */ tcp_data_queue(sk, skb); - tcp_data_snd_check(sk, tp); + tcp_data_snd_check(sk); tcp_ack_snd_check(sk); return 0; @@ -4412,13 +4656,13 @@ int tcp_rcv_state_process(struct sock *s goto discard; case TCP_LISTEN: - if(th->ack) + if (th->ack) return 1; - if(th->rst) + if (th->rst) goto discard; - if(th->syn) { + if (th->syn) { if (icsk->icsk_af_ops->conn_request(sk, skb) < 0) return 1; @@ -4452,7 +4696,7 @@ int tcp_rcv_state_process(struct sock *s /* Do step6 onward by hand. */ tcp_urg(sk, skb, th); __kfree_skb(skb); - tcp_data_snd_check(sk, tp); + tcp_data_snd_check(sk); return 0; } @@ -4474,7 +4718,7 @@ int tcp_rcv_state_process(struct sock *s } /* step 2: check RST bit */ - if(th->rst) { + if (th->rst) { tcp_reset(sk); goto discard; } @@ -4497,7 +4741,7 @@ int tcp_rcv_state_process(struct sock *s if (th->ack) { int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH); - switch(sk->sk_state) { + switch (sk->sk_state) { case TCP_SYN_RECV: if (acceptable) { tp->copied_seq = tp->rcv_nxt; @@ -4644,7 +4888,7 @@ int tcp_rcv_state_process(struct sock *s /* tcp_data could move socket to TIME-WAIT */ if (sk->sk_state != TCP_CLOSE) { - tcp_data_snd_check(sk, tp); + tcp_data_snd_check(sk); tcp_ack_snd_check(sk); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 0ba74bb..5a3e7f8 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -88,7 +88,7 @@ int sysctl_tcp_low_latency __read_mostly #define ICMP_MIN_LENGTH 8 /* Socket used for sending RSTs */ -static struct socket *tcp_socket; +static struct socket *tcp_socket __read_mostly; void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb); @@ -125,10 +125,10 @@ void tcp_unhash(struct sock *sk) static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb) { - return secure_tcp_sequence_number(skb->nh.iph->daddr, - skb->nh.iph->saddr, - skb->h.th->dest, - skb->h.th->source); + return secure_tcp_sequence_number(ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, + tcp_hdr(skb)->dest, + tcp_hdr(skb)->source); } int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp) @@ -149,7 +149,7 @@ int tcp_twsk_unique(struct sock *sk, str */ if (tcptw->tw_ts_recent_stamp && (twp == NULL || (sysctl_tcp_tw_reuse && - xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) { + get_seconds() - tcptw->tw_ts_recent_stamp > 1))) { tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2; if (tp->write_seq == 0) tp->write_seq = 1; @@ -224,7 +224,7 @@ int tcp_v4_connect(struct sock *sk, stru * when trying new connection. */ if (peer != NULL && - peer->tcp_ts_stamp + TCP_PAWS_MSL >= xtime.tv_sec) { + peer->tcp_ts_stamp + TCP_PAWS_MSL >= get_seconds()) { tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp; tp->rx_opt.ts_recent = peer->tcp_ts; } @@ -354,8 +354,8 @@ void tcp_v4_err(struct sk_buff *skb, u32 struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); struct tcp_sock *tp; struct inet_sock *inet; - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; struct sock *sk; __u32 seq; int err; @@ -499,11 +499,12 @@ out: void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb) { struct inet_sock *inet = inet_sk(sk); - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { th->check = ~tcp_v4_check(len, inet->saddr, inet->daddr, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); } else { th->check = tcp_v4_check(len, inet->saddr, inet->daddr, @@ -515,17 +516,18 @@ void tcp_v4_send_check(struct sock *sk, int tcp_v4_gso_send_check(struct sk_buff *skb) { - struct iphdr *iph; + const struct iphdr *iph; struct tcphdr *th; if (!pskb_may_pull(skb, sizeof(*th))) return -EINVAL; - iph = skb->nh.iph; - th = skb->h.th; + iph = ip_hdr(skb); + th = tcp_hdr(skb); th->check = 0; th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); skb->ip_summed = CHECKSUM_PARTIAL; return 0; @@ -546,7 +548,7 @@ int tcp_v4_gso_send_check(struct sk_buff static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) { - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); struct { struct tcphdr th; #ifdef CONFIG_TCP_MD5SIG @@ -585,7 +587,7 @@ #endif arg.iov[0].iov_len = sizeof(rep.th); #ifdef CONFIG_TCP_MD5SIG - key = sk ? tcp_v4_md5_do_lookup(sk, skb->nh.iph->daddr) : NULL; + key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL; if (key) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | @@ -597,14 +599,14 @@ #ifdef CONFIG_TCP_MD5SIG tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[1], key, - skb->nh.iph->daddr, - skb->nh.iph->saddr, + ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, &rep.th, IPPROTO_TCP, arg.iov[0].iov_len); } #endif - arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr, - skb->nh.iph->saddr, /* XXX */ + arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, /* XXX */ sizeof(struct tcphdr), IPPROTO_TCP, 0); arg.csumoffset = offsetof(struct tcphdr, check) / 2; @@ -622,7 +624,7 @@ static void tcp_v4_send_ack(struct tcp_t struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts) { - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); struct { struct tcphdr th; __be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2) @@ -670,7 +672,7 @@ #ifdef CONFIG_TCP_MD5SIG * skb->sk) holds true, but we program defensively. */ if (!twsk && skb->sk) { - key = tcp_v4_md5_do_lookup(skb->sk, skb->nh.iph->daddr); + key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr); } else if (twsk && twsk->tw_md5_keylen) { tw_key.key = twsk->tw_md5_key; tw_key.keylen = twsk->tw_md5_keylen; @@ -690,14 +692,14 @@ #ifdef CONFIG_TCP_MD5SIG tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[offset], key, - skb->nh.iph->daddr, - skb->nh.iph->saddr, + ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, &rep.th, IPPROTO_TCP, arg.iov[0].iov_len); } #endif - arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr, - skb->nh.iph->saddr, /* XXX */ + arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, /* XXX */ arg.iov[0].iov_len, IPPROTO_TCP, 0); arg.csumoffset = offsetof(struct tcphdr, check) / 2; @@ -745,7 +747,7 @@ static int tcp_v4_send_synack(struct soc skb = tcp_make_synack(sk, dst, req); if (skb) { - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); th->check = tcp_v4_check(skb->len, ireq->loc_addr, @@ -781,7 +783,7 @@ static void syn_flood_warning(struct sk_ warntime = jiffies; printk(KERN_INFO "possible SYN flooding on port %d. Sending cookies.\n", - ntohs(skb->h.th->dest)); + ntohs(tcp_hdr(skb)->dest)); } } #endif @@ -1133,8 +1135,8 @@ static int tcp_v4_inbound_md5_hash(struc */ __u8 *hash_location = NULL; struct tcp_md5sig_key *hash_expected; - struct iphdr *iph = skb->nh.iph; - struct tcphdr *th = skb->h.th; + const struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); int length = (th->doff << 2) - sizeof(struct tcphdr); int genhash; unsigned char *ptr; @@ -1251,8 +1253,8 @@ int tcp_v4_conn_request(struct sock *sk, struct inet_request_sock *ireq; struct tcp_options_received tmp_opt; struct request_sock *req; - __be32 saddr = skb->nh.iph->saddr; - __be32 daddr = skb->nh.iph->daddr; + __be32 saddr = ip_hdr(skb)->saddr; + __be32 daddr = ip_hdr(skb)->daddr; __u32 isn = TCP_SKB_CB(skb)->when; struct dst_entry *dst = NULL; #ifdef CONFIG_SYN_COOKIES @@ -1327,7 +1329,7 @@ #endif ireq->rmt_addr = saddr; ireq->opt = tcp_v4_save_options(sk, skb); if (!want_cookie) - TCP_ECN_create_request(req, skb->h.th); + TCP_ECN_create_request(req, tcp_hdr(skb)); if (want_cookie) { #ifdef CONFIG_SYN_COOKIES @@ -1351,7 +1353,7 @@ #endif (dst = inet_csk_route_req(sk, req)) != NULL && (peer = rt_get_peer((struct rtable *)dst)) != NULL && peer->v4daddr == saddr) { - if (xtime.tv_sec < peer->tcp_ts_stamp + TCP_PAWS_MSL && + if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL && (s32)(peer->tcp_ts - req->ts_recent) > TCP_PAWS_WINDOW) { NET_INC_STATS_BH(LINUX_MIB_PAWSPASSIVEREJECTED); @@ -1375,7 +1377,7 @@ #endif LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open " "request from %u.%u.%u.%u/%u\n", NIPQUAD(saddr), - ntohs(skb->h.th->source)); + ntohs(tcp_hdr(skb)->source)); dst_release(dst); goto drop_and_free; } @@ -1439,7 +1441,7 @@ #endif newinet->opt = ireq->opt; ireq->opt = NULL; newinet->mc_index = inet_iif(skb); - newinet->mc_ttl = skb->nh.iph->ttl; + newinet->mc_ttl = ip_hdr(skb)->ttl; inet_csk(newsk)->icsk_ext_hdr_len = 0; if (newinet->opt) inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen; @@ -1481,8 +1483,8 @@ exit: static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) { - struct tcphdr *th = skb->h.th; - struct iphdr *iph = skb->nh.iph; + struct tcphdr *th = tcp_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); struct sock *nsk; struct request_sock **prev; /* Find possible connection requests. */ @@ -1491,9 +1493,8 @@ static struct sock *tcp_v4_hnd_req(struc if (req) return tcp_check_req(sk, skb, req, prev); - nsk = inet_lookup_established(&tcp_hashinfo, skb->nh.iph->saddr, - th->source, skb->nh.iph->daddr, - th->dest, inet_iif(skb)); + nsk = inet_lookup_established(&tcp_hashinfo, iph->saddr, th->source, + iph->daddr, th->dest, inet_iif(skb)); if (nsk) { if (nsk->sk_state != TCP_TIME_WAIT) { @@ -1513,15 +1514,17 @@ #endif static __sum16 tcp_v4_checksum_init(struct sk_buff *skb) { + const struct iphdr *iph = ip_hdr(skb); + if (skb->ip_summed == CHECKSUM_COMPLETE) { - if (!tcp_v4_check(skb->len, skb->nh.iph->saddr, - skb->nh.iph->daddr, skb->csum)) { + if (!tcp_v4_check(skb->len, iph->saddr, + iph->daddr, skb->csum)) { skb->ip_summed = CHECKSUM_UNNECESSARY; return 0; } } - skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, skb->nh.iph->daddr, + skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, skb->len, IPPROTO_TCP, 0); if (skb->len <= 76) { @@ -1555,7 +1558,7 @@ #endif if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ TCP_CHECK_TIMER(sk); - if (tcp_rcv_established(sk, skb, skb->h.th, skb->len)) { + if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; } @@ -1563,7 +1566,7 @@ #endif return 0; } - if (skb->len < (skb->h.th->doff << 2) || tcp_checksum_complete(skb)) + if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb)) goto csum_err; if (sk->sk_state == TCP_LISTEN) { @@ -1581,7 +1584,7 @@ #endif } TCP_CHECK_TIMER(sk); - if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) { + if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; } @@ -1610,6 +1613,7 @@ csum_err: int tcp_v4_rcv(struct sk_buff *skb) { + const struct iphdr *iph; struct tcphdr *th; struct sock *sk; int ret; @@ -1623,7 +1627,7 @@ int tcp_v4_rcv(struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct tcphdr))) goto discard_it; - th = skb->h.th; + th = tcp_hdr(skb); if (th->doff < sizeof(struct tcphdr) / 4) goto bad_packet; @@ -1634,23 +1638,21 @@ int tcp_v4_rcv(struct sk_buff *skb) * Packet length and doff are validated by header prediction, * provided case of th->doff==0 is eliminated. * So, we defer the checks. */ - if ((skb->ip_summed != CHECKSUM_UNNECESSARY && - tcp_v4_checksum_init(skb))) + if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb)) goto bad_packet; - th = skb->h.th; + th = tcp_hdr(skb); + iph = ip_hdr(skb); TCP_SKB_CB(skb)->seq = ntohl(th->seq); TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff * 4); TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); TCP_SKB_CB(skb)->when = 0; - TCP_SKB_CB(skb)->flags = skb->nh.iph->tos; + TCP_SKB_CB(skb)->flags = iph->tos; TCP_SKB_CB(skb)->sacked = 0; - sk = __inet_lookup(&tcp_hashinfo, skb->nh.iph->saddr, th->source, - skb->nh.iph->daddr, th->dest, - inet_iif(skb)); - + sk = __inet_lookup(&tcp_hashinfo, iph->saddr, th->source, + iph->daddr, th->dest, inet_iif(skb)); if (!sk) goto no_tcp_socket; @@ -1724,8 +1726,7 @@ do_time_wait: switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) { case TCP_TW_SYN: { struct sock *sk2 = inet_lookup_listener(&tcp_hashinfo, - skb->nh.iph->daddr, - th->dest, + iph->daddr, th->dest, inet_iif(skb)); if (sk2) { inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); @@ -1770,7 +1771,7 @@ int tcp_v4_remember_stamp(struct sock *s if (peer) { if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 || - (peer->tcp_ts_stamp + TCP_PAWS_MSL < xtime.tv_sec && + (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() && peer->tcp_ts_stamp <= tp->rx_opt.ts_recent_stamp)) { peer->tcp_ts_stamp = tp->rx_opt.ts_recent_stamp; peer->tcp_ts = tp->rx_opt.ts_recent; @@ -1791,7 +1792,7 @@ int tcp_v4_tw_remember_stamp(struct inet const struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); if ((s32)(peer->tcp_ts - tcptw->tw_ts_recent) <= 0 || - (peer->tcp_ts_stamp + TCP_PAWS_MSL < xtime.tv_sec && + (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() && peer->tcp_ts_stamp <= tcptw->tw_ts_recent_stamp)) { peer->tcp_ts_stamp = tcptw->tw_ts_recent_stamp; peer->tcp_ts = tcptw->tw_ts_recent; @@ -1890,7 +1891,7 @@ int tcp_v4_destroy_sock(struct sock *sk) tcp_cleanup_congestion_control(sk); /* Cleanup up the write buffer. */ - sk_stream_writequeue_purge(sk); + tcp_write_queue_purge(sk); /* Cleans up our, hopefully empty, out_of_order_queue. */ __skb_queue_purge(&tp->out_of_order_queue); @@ -2293,13 +2294,13 @@ static void get_openreq4(struct sock *sk req); } -static void get_tcp4_sock(struct sock *sp, char *tmpbuf, int i) +static void get_tcp4_sock(struct sock *sk, char *tmpbuf, int i) { int timer_active; unsigned long timer_expires; - struct tcp_sock *tp = tcp_sk(sp); - const struct inet_connection_sock *icsk = inet_csk(sp); - struct inet_sock *inet = inet_sk(sp); + struct tcp_sock *tp = tcp_sk(sk); + const struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_sock *inet = inet_sk(sk); __be32 dest = inet->daddr; __be32 src = inet->rcv_saddr; __u16 destp = ntohs(inet->dport); @@ -2311,9 +2312,9 @@ static void get_tcp4_sock(struct sock *s } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { timer_active = 4; timer_expires = icsk->icsk_timeout; - } else if (timer_pending(&sp->sk_timer)) { + } else if (timer_pending(&sk->sk_timer)) { timer_active = 2; - timer_expires = sp->sk_timer.expires; + timer_expires = sk->sk_timer.expires; } else { timer_active = 0; timer_expires = jiffies; @@ -2321,17 +2322,17 @@ static void get_tcp4_sock(struct sock *s sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX " "%08X %5d %8d %lu %d %p %u %u %u %u %d", - i, src, srcp, dest, destp, sp->sk_state, + i, src, srcp, dest, destp, sk->sk_state, tp->write_seq - tp->snd_una, - sp->sk_state == TCP_LISTEN ? sp->sk_ack_backlog : + sk->sk_state == TCP_LISTEN ? sk->sk_ack_backlog : (tp->rcv_nxt - tp->copied_seq), timer_active, jiffies_to_clock_t(timer_expires - jiffies), icsk->icsk_retransmits, - sock_i_uid(sp), + sock_i_uid(sk), icsk->icsk_probes_out, - sock_i_ino(sp), - atomic_read(&sp->sk_refcnt), sp, + sock_i_ino(sk), + atomic_read(&sk->sk_refcnt), sk, icsk->icsk_rto, icsk->icsk_ack.ato, (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong, diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index f0ebaf0..43294ad 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -218,7 +218,7 @@ static u32 tcp_lp_owd_calculator(struct * 3. calc smoothed OWD (SOWD). * Most ideas come from the original TCP-LP implementation. */ -static void tcp_lp_rtt_sample(struct sock *sk, u32 usrtt) +static void tcp_lp_rtt_sample(struct sock *sk, u32 rtt) { struct lp *lp = inet_csk_ca(sk); s64 mowd = tcp_lp_owd_calculator(sk); @@ -261,11 +261,13 @@ static void tcp_lp_rtt_sample(struct soc * newReno in increase case. * We work it out by following the idea from TCP-LP's paper directly */ -static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked) +static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked, ktime_t last) { struct tcp_sock *tp = tcp_sk(sk); struct lp *lp = inet_csk_ca(sk); + tcp_lp_rtt_sample(sk, ktime_to_us(net_timedelta(last))); + /* calc inference */ if (tcp_time_stamp > tp->rx_opt.rcv_tsecr) lp->inference = 3 * (tcp_time_stamp - tp->rx_opt.rcv_tsecr); @@ -312,11 +314,11 @@ static void tcp_lp_pkts_acked(struct soc } static struct tcp_congestion_ops tcp_lp = { + .flags = TCP_CONG_RTT_STAMP, .init = tcp_lp_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_lp_cong_avoid, .min_cwnd = tcp_reno_min_cwnd, - .rtt_sample = tcp_lp_rtt_sample, .pkts_acked = tcp_lp_pkts_acked, .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 6b5c64f..a12b08f 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -149,7 +149,7 @@ kill_with_rst: tw->tw_substate = TCP_TIME_WAIT; tcptw->tw_rcv_nxt = TCP_SKB_CB(skb)->end_seq; if (tmp_opt.saw_tstamp) { - tcptw->tw_ts_recent_stamp = xtime.tv_sec; + tcptw->tw_ts_recent_stamp = get_seconds(); tcptw->tw_ts_recent = tmp_opt.rcv_tsval; } @@ -208,7 +208,7 @@ kill: if (tmp_opt.saw_tstamp) { tcptw->tw_ts_recent = tmp_opt.rcv_tsval; - tcptw->tw_ts_recent_stamp = xtime.tv_sec; + tcptw->tw_ts_recent_stamp = get_seconds(); } inet_twsk_put(tw); @@ -246,7 +246,7 @@ kill: if (paws_reject) NET_INC_STATS_BH(LINUX_MIB_PAWSESTABREJECTED); - if(!th->rst) { + if (!th->rst) { /* In this case we must reset the TIMEWAIT timer. * * If it is ACKless SYN it may be both old duplicate @@ -324,7 +324,7 @@ #ifdef CONFIG_TCP_MD5SIG if (tcp_alloc_md5sig_pool() == NULL) BUG(); } - } while(0); + } while (0); #endif /* Linkage updates. */ @@ -387,8 +387,8 @@ struct sock *tcp_create_openreq_child(st /* Now setup tcp_sock */ newtp = tcp_sk(newsk); newtp->pred_flags = 0; - newtp->rcv_nxt = treq->rcv_isn + 1; - newtp->snd_nxt = newtp->snd_una = newtp->snd_sml = treq->snt_isn + 1; + newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1; + newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = treq->snt_isn + 1; tcp_prequeue_init(newtp); @@ -422,10 +422,8 @@ struct sock *tcp_create_openreq_child(st tcp_set_ca_state(newsk, TCP_CA_Open); tcp_init_xmit_timers(newsk); skb_queue_head_init(&newtp->out_of_order_queue); - newtp->rcv_wup = treq->rcv_isn + 1; newtp->write_seq = treq->snt_isn + 1; newtp->pushed_seq = newtp->write_seq; - newtp->copied_seq = treq->rcv_isn + 1; newtp->rx_opt.saw_tstamp = 0; @@ -440,7 +438,7 @@ struct sock *tcp_create_openreq_child(st keepalive_time_when(newtp)); newtp->rx_opt.tstamp_ok = ireq->tstamp_ok; - if((newtp->rx_opt.sack_ok = ireq->sack_ok) != 0) { + if ((newtp->rx_opt.sack_ok = ireq->sack_ok) != 0) { if (sysctl_tcp_fack) newtp->rx_opt.sack_ok |= 2; } @@ -455,12 +453,13 @@ struct sock *tcp_create_openreq_child(st newtp->rx_opt.snd_wscale = newtp->rx_opt.rcv_wscale = 0; newtp->window_clamp = min(newtp->window_clamp, 65535U); } - newtp->snd_wnd = ntohs(skb->h.th->window) << newtp->rx_opt.snd_wscale; + newtp->snd_wnd = (ntohs(tcp_hdr(skb)->window) << + newtp->rx_opt.snd_wscale); newtp->max_window = newtp->snd_wnd; if (newtp->rx_opt.tstamp_ok) { newtp->rx_opt.ts_recent = req->ts_recent; - newtp->rx_opt.ts_recent_stamp = xtime.tv_sec; + newtp->rx_opt.ts_recent_stamp = get_seconds(); newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; } else { newtp->rx_opt.ts_recent_stamp = 0; @@ -490,7 +489,7 @@ struct sock *tcp_check_req(struct sock * struct request_sock *req, struct request_sock **prev) { - struct tcphdr *th = skb->h.th; + const struct tcphdr *th = tcp_hdr(skb); __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); int paws_reject = 0; struct tcp_options_received tmp_opt; @@ -506,7 +505,7 @@ struct sock *tcp_check_req(struct sock * * it can be estimated (approximately) * from another data. */ - tmp_opt.ts_recent_stamp = xtime.tv_sec - ((TCP_TIMEOUT_INIT/HZ)<retrans); + tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<retrans); paws_reject = tcp_paws_check(&tmp_opt, th->rst); } } @@ -712,8 +711,8 @@ int tcp_child_process(struct sock *paren int state = child->sk_state; if (!sock_owned_by_user(child)) { - ret = tcp_rcv_state_process(child, skb, skb->h.th, skb->len); - + ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), + skb->len); /* Wakeup parent, send SIGIO */ if (state == TCP_SYN_RECV && child->sk_state != state) parent->sk_data_ready(parent, 0); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3c24881..0faacf9 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -62,14 +62,13 @@ int sysctl_tcp_base_mss __read_mostly = /* By default, RFC2861 behavior. */ int sysctl_tcp_slow_start_after_idle __read_mostly = 1; -static void update_send_head(struct sock *sk, struct tcp_sock *tp, - struct sk_buff *skb) +static void update_send_head(struct sock *sk, struct sk_buff *skb) { - sk->sk_send_head = skb->next; - if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue) - sk->sk_send_head = NULL; + struct tcp_sock *tp = tcp_sk(sk); + + tcp_advance_send_head(sk, skb); tp->snd_nxt = TCP_SKB_CB(skb)->end_seq; - tcp_packets_out_inc(sk, tp, skb); + tcp_packets_out_inc(sk, skb); } /* SND.NXT, if window was not shrunk. @@ -78,8 +77,10 @@ static void update_send_head(struct sock * Anything in between SND.UNA...SND.UNA+SND.WND also can be already * invalid. OK, let's make this for now: */ -static inline __u32 tcp_acceptable_seq(struct sock *sk, struct tcp_sock *tp) +static inline __u32 tcp_acceptable_seq(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (!before(tp->snd_una+tp->snd_wnd, tp->snd_nxt)) return tp->snd_nxt; else @@ -238,7 +239,7 @@ static u16 tcp_select_window(struct sock u32 new_win = __tcp_select_window(sk); /* Never shrink the offered window */ - if(new_win < cur_win) { + if (new_win < cur_win) { /* Danger Will Robinson! * Don't update rcv_wup/rcv_wnd here or else * we will not be able to advertise a zero @@ -289,10 +290,12 @@ static void tcp_build_and_update_options (TCPOPT_SACK << 8) | (TCPOLEN_SACK_BASE + (tp->rx_opt.eff_sacks * TCPOLEN_SACK_PERBLOCK))); - for(this_sack = 0; this_sack < tp->rx_opt.eff_sacks; this_sack++) { + + for (this_sack = 0; this_sack < tp->rx_opt.eff_sacks; this_sack++) { *ptr++ = htonl(sp[this_sack].start_seq); *ptr++ = htonl(sp[this_sack].end_seq); } + if (tp->rx_opt.dsack) { tp->rx_opt.dsack = 0; tp->rx_opt.eff_sacks--; @@ -337,7 +340,7 @@ static void tcp_syn_build_options(__be32 */ *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss); if (ts) { - if(sack) + if (sack) *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) | (TCPOPT_TIMESTAMP << 8) | @@ -349,7 +352,7 @@ static void tcp_syn_build_options(__be32 TCPOLEN_TIMESTAMP); *ptr++ = htonl(tstamp); /* TSVAL */ *ptr++ = htonl(ts_recent); /* TSECR */ - } else if(sack) + } else if (sack) *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_SACK_PERM << 8) | @@ -406,7 +409,7 @@ #endif /* If congestion control is doing timestamping, we must * take such a timestamp before we potentially clone/copy. */ - if (icsk->icsk_ca_ops->rtt_sample) + if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP) __net_timestamp(skb); if (likely(clone_it)) { @@ -430,7 +433,7 @@ #define SYSCTL_FLAG_SACK 0x4 sysctl_flags = 0; if (unlikely(tcb->flags & TCPCB_FLAG_SYN)) { tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS; - if(sysctl_tcp_timestamps) { + if (sysctl_tcp_timestamps) { tcp_header_size += TCPOLEN_TSTAMP_ALIGNED; sysctl_flags |= SYSCTL_FLAG_TSTAMPS; } @@ -465,11 +468,12 @@ #ifdef CONFIG_TCP_MD5SIG tcp_header_size += TCPOLEN_MD5SIG_ALIGNED; #endif - th = (struct tcphdr *) skb_push(skb, tcp_header_size); - skb->h.th = th; + skb_push(skb, tcp_header_size); + skb_reset_transport_header(skb); skb_set_owner_w(skb, sk); /* Build TCP header and checksum it. */ + th = tcp_hdr(skb); th->source = inet->sport; th->dest = inet->dport; th->seq = htonl(tcb->seq); @@ -515,7 +519,7 @@ #ifdef CONFIG_TCP_MD5SIG md5 ? &md5_hash_location : #endif NULL); - TCP_ECN_send(sk, tp, skb, tcp_header_size); + TCP_ECN_send(sk, skb, tcp_header_size); } #ifdef CONFIG_TCP_MD5SIG @@ -524,7 +528,7 @@ #ifdef CONFIG_TCP_MD5SIG tp->af_specific->calc_md5_hash(md5_hash_location, md5, sk, NULL, NULL, - skb->h.th, + tcp_hdr(skb), sk->sk_protocol, skb->len); } @@ -545,7 +549,7 @@ #endif if (likely(err <= 0)) return err; - tcp_enter_cwr(sk); + tcp_enter_cwr(sk, 1); return net_xmit_eval(err); @@ -567,12 +571,8 @@ static void tcp_queue_skb(struct sock *s /* Advance write_seq and place onto the write_queue. */ tp->write_seq = TCP_SKB_CB(skb)->end_seq; skb_header_release(skb); - __skb_queue_tail(&sk->sk_write_queue, skb); + tcp_add_write_queue_tail(sk, skb); sk_charge_skb(sk, skb); - - /* Queue it, remembering where we must start sending. */ - if (sk->sk_send_head == NULL) - sk->sk_send_head = skb; } static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now) @@ -705,7 +705,7 @@ int tcp_fragment(struct sock *sk, struct /* Link BUFF into the send queue. */ skb_header_release(buff); - __skb_append(skb, buff, &sk->sk_write_queue); + tcp_insert_write_queue_after(skb, buff, sk); return 0; } @@ -736,7 +736,7 @@ static void __pskb_trim_head(struct sk_b } skb_shinfo(skb)->nr_frags = k; - skb->tail = skb->data; + skb_reset_tail_pointer(skb); skb->data_len -= len; skb->len = skb->data_len; } @@ -930,8 +930,9 @@ #endif /* Congestion window validation. (RFC2861) */ -static void tcp_cwnd_validate(struct sock *sk, struct tcp_sock *tp) +static void tcp_cwnd_validate(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); __u32 packets_out = tp->packets_out; if (packets_out >= tp->snd_cwnd) { @@ -1034,8 +1035,10 @@ static inline int tcp_nagle_test(struct if (nonagle & TCP_NAGLE_PUSH) return 1; - /* Don't use the nagle rule for urgent data (or for the final FIN). */ - if (tp->urg_mode || + /* Don't use the nagle rule for urgent data (or for the final FIN). + * Nagle can be ignored during F-RTO too (see RFC4138). + */ + if (tp->urg_mode || (tp->frto_counter == 2) || (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)) return 1; @@ -1056,7 +1059,7 @@ static inline int tcp_snd_wnd_test(struc return !after(end_seq, tp->snd_una + tp->snd_wnd); } -/* This checks if the data bearing packet SKB (usually sk->sk_send_head) +/* This checks if the data bearing packet SKB (usually tcp_send_head(sk)) * should be put on the wire right now. If so, it returns the number of * packets allowed by the congestion window. */ @@ -1079,15 +1082,10 @@ static unsigned int tcp_snd_test(struct return cwnd_quota; } -static inline int tcp_skb_is_last(const struct sock *sk, - const struct sk_buff *skb) +int tcp_may_send_now(struct sock *sk) { - return skb->next == (struct sk_buff *)&sk->sk_write_queue; -} - -int tcp_may_send_now(struct sock *sk, struct tcp_sock *tp) -{ - struct sk_buff *skb = sk->sk_send_head; + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *skb = tcp_send_head(sk); return (skb && tcp_snd_test(sk, skb, tcp_current_mss(sk, 1), @@ -1143,7 +1141,7 @@ static int tso_fragment(struct sock *sk, /* Link BUFF into the send queue. */ skb_header_release(buff); - __skb_append(skb, buff, &sk->sk_write_queue); + tcp_insert_write_queue_after(skb, buff, sk); return 0; } @@ -1153,8 +1151,9 @@ static int tso_fragment(struct sock *sk, * * This algorithm is from John Heffner. */ -static int tcp_tso_should_defer(struct sock *sk, struct tcp_sock *tp, struct sk_buff *skb) +static int tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); u32 send_win, cong_win, limit, in_flight; @@ -1249,10 +1248,10 @@ static int tcp_mtu_probe(struct sock *sk /* Have enough data in the send queue to probe? */ len = 0; - if ((skb = sk->sk_send_head) == NULL) + if ((skb = tcp_send_head(sk)) == NULL) return -1; while ((len += skb->len) < probe_size && !tcp_skb_is_last(sk, skb)) - skb = skb->next; + skb = tcp_write_queue_next(sk, skb); if (len < probe_size) return -1; @@ -1279,9 +1278,9 @@ static int tcp_mtu_probe(struct sock *sk return -1; sk_charge_skb(sk, nskb); - skb = sk->sk_send_head; - __skb_insert(nskb, skb->prev, skb, &sk->sk_write_queue); - sk->sk_send_head = nskb; + skb = tcp_send_head(sk); + tcp_insert_write_queue_before(nskb, skb, sk); + tcp_advance_send_head(sk, skb); TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(skb)->seq; TCP_SKB_CB(nskb)->end_seq = TCP_SKB_CB(skb)->seq + probe_size; @@ -1292,7 +1291,7 @@ static int tcp_mtu_probe(struct sock *sk len = 0; while (len < probe_size) { - next = skb->next; + next = tcp_write_queue_next(sk, skb); copy = min_t(int, skb->len, probe_size - len); if (nskb->ip_summed) @@ -1305,7 +1304,7 @@ static int tcp_mtu_probe(struct sock *sk /* We've eaten all the data from this skb. * Throw it away. */ TCP_SKB_CB(nskb)->flags |= TCP_SKB_CB(skb)->flags; - __skb_unlink(skb, &sk->sk_write_queue); + tcp_unlink_write_queue(skb, sk); sk_stream_free_skb(sk, skb); } else { TCP_SKB_CB(nskb)->flags |= TCP_SKB_CB(skb)->flags & @@ -1333,7 +1332,7 @@ static int tcp_mtu_probe(struct sock *sk /* Decrement cwnd here because we are sending * effectively two packets. */ tp->snd_cwnd--; - update_send_head(sk, tp, nskb); + update_send_head(sk, nskb); icsk->icsk_mtup.probe_size = tcp_mss_to_mtu(sk, nskb->len); tp->mtu_probe.probe_seq_start = TCP_SKB_CB(nskb)->seq; @@ -1377,7 +1376,7 @@ static int tcp_write_xmit(struct sock *s sent_pkts = 1; } - while ((skb = sk->sk_send_head)) { + while ((skb = tcp_send_head(sk))) { unsigned int limit; tso_segs = tcp_init_tso_segs(sk, skb, mss_now); @@ -1396,7 +1395,7 @@ static int tcp_write_xmit(struct sock *s nonagle : TCP_NAGLE_PUSH)))) break; } else { - if (tcp_tso_should_defer(sk, tp, skb)) + if (tcp_tso_should_defer(sk, skb)) break; } @@ -1425,31 +1424,31 @@ static int tcp_write_xmit(struct sock *s /* Advance the send_head. This one is sent out. * This call will increment packets_out. */ - update_send_head(sk, tp, skb); + update_send_head(sk, skb); tcp_minshall_update(tp, mss_now, skb); sent_pkts++; } if (likely(sent_pkts)) { - tcp_cwnd_validate(sk, tp); + tcp_cwnd_validate(sk); return 0; } - return !tp->packets_out && sk->sk_send_head; + return !tp->packets_out && tcp_send_head(sk); } /* Push out any pending frames which were held back due to * TCP_CORK or attempt at coalescing tiny packets. * The socket must be locked by the caller. */ -void __tcp_push_pending_frames(struct sock *sk, struct tcp_sock *tp, - unsigned int cur_mss, int nonagle) +void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss, + int nonagle) { - struct sk_buff *skb = sk->sk_send_head; + struct sk_buff *skb = tcp_send_head(sk); if (skb) { if (tcp_write_xmit(sk, cur_mss, nonagle)) - tcp_check_probe_timer(sk, tp); + tcp_check_probe_timer(sk); } } @@ -1459,7 +1458,7 @@ void __tcp_push_pending_frames(struct so void tcp_push_one(struct sock *sk, unsigned int mss_now) { struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb = sk->sk_send_head; + struct sk_buff *skb = tcp_send_head(sk); unsigned int tso_segs, cwnd_quota; BUG_ON(!skb || skb->len < mss_now); @@ -1493,8 +1492,8 @@ void tcp_push_one(struct sock *sk, unsig TCP_SKB_CB(skb)->when = tcp_time_stamp; if (likely(!tcp_transmit_skb(sk, skb, 1, sk->sk_allocation))) { - update_send_head(sk, tp, skb); - tcp_cwnd_validate(sk, tp); + update_send_head(sk, skb); + tcp_cwnd_validate(sk); return; } } @@ -1620,7 +1619,7 @@ u32 __tcp_select_window(struct sock *sk) static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int mss_now) { struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *next_skb = skb->next; + struct sk_buff *next_skb = tcp_write_queue_next(sk, skb); /* The first test we must make is that neither of these two * SKB's are still referenced by someone else. @@ -1630,7 +1629,7 @@ static void tcp_retrans_try_collapse(str u16 flags = TCP_SKB_CB(skb)->flags; /* Also punt if next skb has been SACK'd. */ - if(TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED) + if (TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED) return; /* Next skb is out of window. */ @@ -1652,9 +1651,11 @@ static void tcp_retrans_try_collapse(str clear_all_retrans_hints(tp); /* Ok. We will be able to collapse the packet. */ - __skb_unlink(next_skb, &sk->sk_write_queue); + tcp_unlink_write_queue(next_skb, sk); - memcpy(skb_put(skb, next_skb_size), next_skb->data, next_skb_size); + skb_copy_from_linear_data(next_skb, + skb_put(skb, next_skb_size), + next_skb_size); if (next_skb->ip_summed == CHECKSUM_PARTIAL) skb->ip_summed = CHECKSUM_PARTIAL; @@ -1706,7 +1707,9 @@ void tcp_simple_retransmit(struct sock * unsigned int mss = tcp_current_mss(sk, 0); int lost = 0; - sk_stream_for_retrans_queue(skb, sk) { + tcp_for_write_queue(skb, sk) { + if (skb == tcp_send_head(sk)) + break; if (skb->len > mss && !(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) { if (TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_RETRANS) { @@ -1788,13 +1791,13 @@ int tcp_retransmit_skb(struct sock *sk, } /* Collapse two adjacent packets if worthwhile and we can. */ - if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) && - (skb->len < (cur_mss >> 1)) && - (skb->next != sk->sk_send_head) && - (skb->next != (struct sk_buff *)&sk->sk_write_queue) && - (skb_shinfo(skb)->nr_frags == 0 && skb_shinfo(skb->next)->nr_frags == 0) && - (tcp_skb_pcount(skb) == 1 && tcp_skb_pcount(skb->next) == 1) && - (sysctl_tcp_retrans_collapse != 0)) + if (!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) && + (skb->len < (cur_mss >> 1)) && + (tcp_write_queue_next(sk, skb) != tcp_send_head(sk)) && + (!tcp_skb_is_last(sk, skb)) && + (skb_shinfo(skb)->nr_frags == 0 && skb_shinfo(tcp_write_queue_next(sk, skb))->nr_frags == 0) && + (tcp_skb_pcount(skb) == 1 && tcp_skb_pcount(tcp_write_queue_next(sk, skb)) == 1) && + (sysctl_tcp_retrans_collapse != 0)) tcp_retrans_try_collapse(sk, skb, cur_mss); if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) @@ -1804,9 +1807,9 @@ int tcp_retransmit_skb(struct sock *sk, * retransmit when old data is attached. So strip it off * since it is cheap to do so and saves bytes on the network. */ - if(skb->len > 0 && - (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) && - tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) { + if (skb->len > 0 && + (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) && + tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) { if (!pskb_trim(skb, 0)) { TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1; skb_shinfo(skb)->gso_segs = 1; @@ -1872,15 +1875,17 @@ void tcp_xmit_retransmit_queue(struct so skb = tp->retransmit_skb_hint; packet_cnt = tp->retransmit_cnt_hint; }else{ - skb = sk->sk_write_queue.next; + skb = tcp_write_queue_head(sk); packet_cnt = 0; } /* First pass: retransmit lost packets. */ if (tp->lost_out) { - sk_stream_for_retrans_queue_from(skb, sk) { + tcp_for_write_queue_from(skb, sk) { __u8 sacked = TCP_SKB_CB(skb)->sacked; + if (skb == tcp_send_head(sk)) + break; /* we could do better than to assign each time */ tp->retransmit_skb_hint = skb; tp->retransmit_cnt_hint = packet_cnt; @@ -1906,8 +1911,7 @@ void tcp_xmit_retransmit_queue(struct so else NET_INC_STATS_BH(LINUX_MIB_TCPSLOWSTARTRETRANS); - if (skb == - skb_peek(&sk->sk_write_queue)) + if (skb == tcp_write_queue_head(sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, TCP_RTO_MAX); @@ -1937,18 +1941,20 @@ void tcp_xmit_retransmit_queue(struct so * segments to send. */ - if (tcp_may_send_now(sk, tp)) + if (tcp_may_send_now(sk)) return; if (tp->forward_skb_hint) { skb = tp->forward_skb_hint; packet_cnt = tp->forward_cnt_hint; } else{ - skb = sk->sk_write_queue.next; + skb = tcp_write_queue_head(sk); packet_cnt = 0; } - sk_stream_for_retrans_queue_from(skb, sk) { + tcp_for_write_queue_from(skb, sk) { + if (skb == tcp_send_head(sk)) + break; tp->forward_cnt_hint = packet_cnt; tp->forward_skb_hint = skb; @@ -1973,7 +1979,7 @@ void tcp_xmit_retransmit_queue(struct so break; } - if (skb == skb_peek(&sk->sk_write_queue)) + if (skb == tcp_write_queue_head(sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, TCP_RTO_MAX); @@ -1989,7 +1995,7 @@ void tcp_xmit_retransmit_queue(struct so void tcp_send_fin(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb = skb_peek_tail(&sk->sk_write_queue); + struct sk_buff *skb = tcp_write_queue_tail(sk); int mss_now; /* Optimization, tack on the FIN if we have a queue of @@ -1998,7 +2004,7 @@ void tcp_send_fin(struct sock *sk) */ mss_now = tcp_current_mss(sk, 1); - if (sk->sk_send_head != NULL) { + if (tcp_send_head(sk) != NULL) { TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN; TCP_SKB_CB(skb)->end_seq++; tp->write_seq++; @@ -2025,17 +2031,16 @@ void tcp_send_fin(struct sock *sk) TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1; tcp_queue_skb(sk, skb); } - __tcp_push_pending_frames(sk, tp, mss_now, TCP_NAGLE_OFF); + __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF); } /* We get here when a process closes a file descriptor (either due to * an explicit close() or as a byproduct of exit()'ing) and there * was unread data in the receive queue. This behavior is recommended - * by draft-ietf-tcpimpl-prob-03.txt section 3.10. -DaveM + * by RFC 2525, section 2.17. -DaveM */ void tcp_send_active_reset(struct sock *sk, gfp_t priority) { - struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; /* NOTE: No TCP options attached and we never retransmit this. */ @@ -2055,7 +2060,7 @@ void tcp_send_active_reset(struct sock * skb_shinfo(skb)->gso_type = 0; /* Send it off. */ - TCP_SKB_CB(skb)->seq = tcp_acceptable_seq(sk, tp); + TCP_SKB_CB(skb)->seq = tcp_acceptable_seq(sk); TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq; TCP_SKB_CB(skb)->when = tcp_time_stamp; if (tcp_transmit_skb(sk, skb, 0, priority)) @@ -2071,7 +2076,7 @@ int tcp_send_synack(struct sock *sk) { struct sk_buff* skb; - skb = skb_peek(&sk->sk_write_queue); + skb = tcp_write_queue_head(sk); if (skb == NULL || !(TCP_SKB_CB(skb)->flags&TCPCB_FLAG_SYN)) { printk(KERN_DEBUG "tcp_send_synack: wrong queue state\n"); return -EFAULT; @@ -2081,9 +2086,9 @@ int tcp_send_synack(struct sock *sk) struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); if (nskb == NULL) return -ENOMEM; - __skb_unlink(skb, &sk->sk_write_queue); + tcp_unlink_write_queue(skb, sk); skb_header_release(nskb); - __skb_queue_head(&sk->sk_write_queue, nskb); + __tcp_add_write_queue_head(sk, nskb); sk_stream_free_skb(sk, skb); sk_charge_skb(sk, nskb); skb = nskb; @@ -2133,8 +2138,10 @@ #ifdef CONFIG_TCP_MD5SIG if (md5) tcp_header_size += TCPOLEN_MD5SIG_ALIGNED; #endif - skb->h.th = th = (struct tcphdr *) skb_push(skb, tcp_header_size); + skb_push(skb, tcp_header_size); + skb_reset_transport_header(skb); + th = tcp_hdr(skb); memset(th, 0, sizeof(struct tcphdr)); th->syn = 1; th->ack = 1; @@ -2188,7 +2195,7 @@ #ifdef CONFIG_TCP_MD5SIG tp->af_specific->calc_md5_hash(md5_hash_location, md5, NULL, dst, req, - skb->h.th, sk->sk_protocol, + tcp_hdr(skb), sk->sk_protocol, skb->len); } #endif @@ -2271,7 +2278,7 @@ int tcp_connect(struct sock *sk) skb_reserve(buff, MAX_TCP_HEADER); TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN; - TCP_ECN_send_syn(sk, tp, buff); + TCP_ECN_send_syn(sk, buff); TCP_SKB_CB(buff)->sacked = 0; skb_shinfo(buff)->gso_segs = 1; skb_shinfo(buff)->gso_size = 0; @@ -2285,7 +2292,7 @@ int tcp_connect(struct sock *sk) TCP_SKB_CB(buff)->when = tcp_time_stamp; tp->retrans_stamp = TCP_SKB_CB(buff)->when; skb_header_release(buff); - __skb_queue_tail(&sk->sk_write_queue, buff); + __tcp_add_write_queue_tail(sk, buff); sk_charge_skb(sk, buff); tp->packets_out += tcp_skb_pcount(buff); tcp_transmit_skb(sk, buff, 1, GFP_KERNEL); @@ -2363,7 +2370,6 @@ void tcp_send_ack(struct sock *sk) { /* If we have been reset, we may not send again. */ if (sk->sk_state != TCP_CLOSE) { - struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *buff; /* We are not putting this on the write queue, so @@ -2389,7 +2395,7 @@ void tcp_send_ack(struct sock *sk) skb_shinfo(buff)->gso_type = 0; /* Send it off, this clears delayed acks for us. */ - TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tcp_acceptable_seq(sk, tp); + TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tcp_acceptable_seq(sk); TCP_SKB_CB(buff)->when = tcp_time_stamp; tcp_transmit_skb(sk, buff, 0, GFP_ATOMIC); } @@ -2441,7 +2447,7 @@ int tcp_write_wakeup(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - if ((skb = sk->sk_send_head) != NULL && + if ((skb = tcp_send_head(sk)) != NULL && before(TCP_SKB_CB(skb)->seq, tp->snd_una+tp->snd_wnd)) { int err; unsigned int mss = tcp_current_mss(sk, 0); @@ -2467,7 +2473,7 @@ int tcp_write_wakeup(struct sock *sk) TCP_SKB_CB(skb)->when = tcp_time_stamp; err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); if (!err) { - update_send_head(sk, tp, skb); + update_send_head(sk, skb); } return err; } else { @@ -2491,7 +2497,7 @@ void tcp_send_probe0(struct sock *sk) err = tcp_write_wakeup(sk); - if (tp->packets_out || !sk->sk_send_head) { + if (tp->packets_out || !tcp_send_head(sk)) { /* Cancel probe timer, if it is not required. */ icsk->icsk_probes_out = 0; icsk->icsk_backoff = 0; diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 61f406f..3938d5d 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -26,6 +26,8 @@ #include #include #include #include +#include +#include #include #include @@ -34,43 +36,45 @@ MODULE_AUTHOR("Stephen Hemminger dport) == port || - ntohs(inet->sport) == port) { + /* Only update if port matches */ + if ((port == 0 || ntohs(inet->dport) == port || ntohs(inet->sport) == port) + && (full || tp->snd_cwnd != tcpw.lastcwnd)) { printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n", NIPQUAD(inet->saddr), ntohs(inet->sport), NIPQUAD(inet->daddr), ntohs(inet->dport), - size, tp->snd_nxt, tp->snd_una, + skb->len, tp->snd_nxt, tp->snd_una, tp->snd_cwnd, tcp_current_ssthresh(sk), - tp->snd_wnd); + tp->snd_wnd, tp->srtt >> 3); + tcpw.lastcwnd = tp->snd_cwnd; } jprobe_return(); return 0; } -static struct jprobe tcp_send_probe = { +static struct jprobe tcp_probe = { .kp = { - .symbol_name = "tcp_sendmsg", + .symbol_name = "tcp_rcv_established", }, - .entry = JPROBE_ENTRY(jtcp_sendmsg), + .entry = JPROBE_ENTRY(jtcp_rcv_established), }; static int tcpprobe_open(struct inode * inode, struct file * file) { kfifo_reset(tcpw.fifo); - do_gettimeofday(&tcpw.tstart); + tcpw.start = ktime_get(); return 0; } @@ -162,7 +172,7 @@ static __init int tcpprobe_init(void) if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops)) goto err0; - ret = register_jprobe(&tcp_send_probe); + ret = register_jprobe(&tcp_probe); if (ret) goto err1; @@ -180,7 +190,7 @@ static __exit void tcpprobe_exit(void) { kfifo_free(tcpw.fifo); proc_net_remove(procname); - unregister_jprobe(&tcp_send_probe); + unregister_jprobe(&tcp_probe); } module_exit(tcpprobe_exit); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index a9243cf..2ca97b2 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -233,7 +233,7 @@ static void tcp_probe_timer(struct sock struct tcp_sock *tp = tcp_sk(sk); int max_probes; - if (tp->packets_out || !sk->sk_send_head) { + if (tp->packets_out || !tcp_send_head(sk)) { icsk->icsk_probes_out = 0; return; } @@ -284,7 +284,7 @@ static void tcp_retransmit_timer(struct if (!tp->packets_out) goto out; - BUG_TRAP(!skb_queue_empty(&sk->sk_write_queue)); + BUG_TRAP(!tcp_write_queue_empty(sk)); if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) && !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) { @@ -306,7 +306,7 @@ #endif goto out; } tcp_enter_loss(sk, 0); - tcp_retransmit_skb(sk, skb_peek(&sk->sk_write_queue)); + tcp_retransmit_skb(sk, tcp_write_queue_head(sk)); __sk_dst_reset(sk); goto out_reset_timer; } @@ -341,7 +341,7 @@ #endif tcp_enter_loss(sk, 0); } - if (tcp_retransmit_skb(sk, skb_peek(&sk->sk_write_queue)) > 0) { + if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) { /* Retransmission failed because of local congestion, * do not backoff. */ @@ -482,7 +482,7 @@ static void tcp_keepalive_timer (unsigne elapsed = keepalive_time_when(tp); /* It is alive without keepalive 8) */ - if (tp->packets_out || sk->sk_send_head) + if (tp->packets_out || tcp_send_head(sk)) goto resched; elapsed = tcp_time_stamp - tp->rcv_tstamp; diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index 5c484dc..73e19cf 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -38,6 +38,8 @@ #include #include +#include "tcp_vegas.h" + /* Default values of the Vegas variables, in fixed-point representation * with V_PARAM_SHIFT bits to the right of the binary point. */ @@ -54,17 +56,6 @@ module_param(gamma, int, 0644); MODULE_PARM_DESC(gamma, "limit on increase (scale by 2)"); -/* Vegas variables */ -struct vegas { - u32 beg_snd_nxt; /* right edge during last RTT */ - u32 beg_snd_una; /* left edge during last RTT */ - u32 beg_snd_cwnd; /* saves the size of the cwnd */ - u8 doing_vegas_now;/* if true, do vegas for this RTT */ - u16 cntRTT; /* # of RTTs measured within last RTT */ - u32 minRTT; /* min of RTTs measured within last RTT (in usec) */ - u32 baseRTT; /* the min of all Vegas RTT measurements seen (in usec) */ -}; - /* There are several situations when we must "re-start" Vegas: * * o when a connection is established @@ -81,7 +72,7 @@ struct vegas { * Instead we must wait until the completion of an RTT during * which we actually receive ACKs. */ -static inline void vegas_enable(struct sock *sk) +static void vegas_enable(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); struct vegas *vegas = inet_csk_ca(sk); @@ -104,13 +95,14 @@ static inline void vegas_disable(struct vegas->doing_vegas_now = 0; } -static void tcp_vegas_init(struct sock *sk) +void tcp_vegas_init(struct sock *sk) { struct vegas *vegas = inet_csk_ca(sk); vegas->baseRTT = 0x7fffffff; vegas_enable(sk); } +EXPORT_SYMBOL_GPL(tcp_vegas_init); /* Do RTT sampling needed for Vegas. * Basically we: @@ -120,10 +112,13 @@ static void tcp_vegas_init(struct sock * * o min-filter RTT samples from a much longer window (forever for now) * to find the propagation delay (baseRTT) */ -static void tcp_vegas_rtt_calc(struct sock *sk, u32 usrtt) +void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, ktime_t last) { struct vegas *vegas = inet_csk_ca(sk); - u32 vrtt = usrtt + 1; /* Never allow zero rtt or baseRTT */ + u32 vrtt; + + /* Never allow zero rtt or baseRTT */ + vrtt = ktime_to_us(net_timedelta(last)) + 1; /* Filter to find propagation delay: */ if (vrtt < vegas->baseRTT) @@ -135,8 +130,9 @@ static void tcp_vegas_rtt_calc(struct so vegas->minRTT = min(vegas->minRTT, vrtt); vegas->cntRTT++; } +EXPORT_SYMBOL_GPL(tcp_vegas_pkts_acked); -static void tcp_vegas_state(struct sock *sk, u8 ca_state) +void tcp_vegas_state(struct sock *sk, u8 ca_state) { if (ca_state == TCP_CA_Open) @@ -144,6 +140,7 @@ static void tcp_vegas_state(struct sock else vegas_disable(sk); } +EXPORT_SYMBOL_GPL(tcp_vegas_state); /* * If the connection is idle and we are restarting, @@ -154,12 +151,13 @@ static void tcp_vegas_state(struct sock * packets, _then_ we can make Vegas calculations * again. */ -static void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event) +void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event) { if (event == CA_EVENT_CWND_RESTART || event == CA_EVENT_TX_START) tcp_vegas_init(sk); } +EXPORT_SYMBOL_GPL(tcp_vegas_cwnd_event); static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 seq_rtt, u32 in_flight, int flag) @@ -336,30 +334,29 @@ static void tcp_vegas_cong_avoid(struct } /* Extract info for Tcp socket info provided via netlink. */ -static void tcp_vegas_get_info(struct sock *sk, u32 ext, - struct sk_buff *skb) +void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb) { const struct vegas *ca = inet_csk_ca(sk); if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) { - struct tcpvegas_info *info; - - info = RTA_DATA(__RTA_PUT(skb, INET_DIAG_VEGASINFO, - sizeof(*info))); - - info->tcpv_enabled = ca->doing_vegas_now; - info->tcpv_rttcnt = ca->cntRTT; - info->tcpv_rtt = ca->baseRTT; - info->tcpv_minrtt = ca->minRTT; - rtattr_failure: ; + struct tcpvegas_info info = { + .tcpv_enabled = ca->doing_vegas_now, + .tcpv_rttcnt = ca->cntRTT, + .tcpv_rtt = ca->baseRTT, + .tcpv_minrtt = ca->minRTT, + }; + + nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info); } } +EXPORT_SYMBOL_GPL(tcp_vegas_get_info); static struct tcp_congestion_ops tcp_vegas = { + .flags = TCP_CONG_RTT_STAMP, .init = tcp_vegas_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_vegas_cong_avoid, .min_cwnd = tcp_reno_min_cwnd, - .rtt_sample = tcp_vegas_rtt_calc, + .pkts_acked = tcp_vegas_pkts_acked, .set_state = tcp_vegas_state, .cwnd_event = tcp_vegas_cwnd_event, .get_info = tcp_vegas_get_info, diff --git a/net/ipv4/tcp_vegas.h b/net/ipv4/tcp_vegas.h new file mode 100644 index 0000000..502fa81 --- /dev/null +++ b/net/ipv4/tcp_vegas.h @@ -0,0 +1,24 @@ +/* + * TCP Vegas congestion control interface + */ +#ifndef __TCP_VEGAS_H +#define __TCP_VEGAS_H 1 + +/* Vegas variables */ +struct vegas { + u32 beg_snd_nxt; /* right edge during last RTT */ + u32 beg_snd_una; /* left edge during last RTT */ + u32 beg_snd_cwnd; /* saves the size of the cwnd */ + u8 doing_vegas_now;/* if true, do vegas for this RTT */ + u16 cntRTT; /* # of RTTs measured within last RTT */ + u32 minRTT; /* min of RTTs measured within last RTT (in usec) */ + u32 baseRTT; /* the min of all Vegas RTT measurements seen (in usec) */ +}; + +extern void tcp_vegas_init(struct sock *sk); +extern void tcp_vegas_state(struct sock *sk, u8 ca_state); +extern void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, ktime_t last); +extern void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event); +extern void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb); + +#endif /* __TCP_VEGAS_H */ diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index ce57bf3..9edb340 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -69,10 +69,13 @@ static void tcp_veno_init(struct sock *s } /* Do rtt sampling needed for Veno. */ -static void tcp_veno_rtt_calc(struct sock *sk, u32 usrtt) +static void tcp_veno_pkts_acked(struct sock *sk, u32 cnt, ktime_t last) { struct veno *veno = inet_csk_ca(sk); - u32 vrtt = usrtt + 1; /* Never allow zero rtt or basertt */ + u32 vrtt; + + /* Never allow zero rtt or baseRTT */ + vrtt = ktime_to_us(net_timedelta(last)) + 1; /* Filter to find propagation delay: */ if (vrtt < veno->basertt) @@ -199,10 +202,11 @@ static u32 tcp_veno_ssthresh(struct sock } static struct tcp_congestion_ops tcp_veno = { + .flags = TCP_CONG_RTT_STAMP, .init = tcp_veno_init, .ssthresh = tcp_veno_ssthresh, .cong_avoid = tcp_veno_cong_avoid, - .rtt_sample = tcp_veno_rtt_calc, + .pkts_acked = tcp_veno_pkts_acked, .set_state = tcp_veno_state, .cwnd_event = tcp_veno_cwnd_event, diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index 4e1b610..e61e09d 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -100,7 +100,7 @@ static void westwood_filter(struct westw * Called after processing group of packets. * but all westwood needs is the last sample of srtt. */ -static void tcp_westwood_pkts_acked(struct sock *sk, u32 cnt) +static void tcp_westwood_pkts_acked(struct sock *sk, u32 cnt, ktime_t last) { struct westwood *w = inet_csk_ca(sk); if (cnt > 0) @@ -226,7 +226,7 @@ static void tcp_westwood_event(struct so struct tcp_sock *tp = tcp_sk(sk); struct westwood *w = inet_csk_ca(sk); - switch(event) { + switch (event) { case CA_EVENT_FAST_ACK: westwood_fast_bw(sk); break; @@ -260,16 +260,13 @@ static void tcp_westwood_info(struct soc { const struct westwood *ca = inet_csk_ca(sk); if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) { - struct rtattr *rta; - struct tcpvegas_info *info; - - rta = __RTA_PUT(skb, INET_DIAG_VEGASINFO, sizeof(*info)); - info = RTA_DATA(rta); - info->tcpv_enabled = 1; - info->tcpv_rttcnt = 0; - info->tcpv_rtt = jiffies_to_usecs(ca->rtt); - info->tcpv_minrtt = jiffies_to_usecs(ca->rtt_min); - rtattr_failure: ; + struct tcpvegas_info info = { + .tcpv_enabled = 1, + .tcpv_rtt = jiffies_to_usecs(ca->rtt), + .tcpv_minrtt = jiffies_to_usecs(ca->rtt_min), + }; + + nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info); } } diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c new file mode 100644 index 0000000..545ed23 --- /dev/null +++ b/net/ipv4/tcp_yeah.c @@ -0,0 +1,268 @@ +/* + * + * YeAH TCP + * + * For further details look at: + * http://wil.cs.caltech.edu/pfldnet2007/paper/YeAH_TCP.pdf + * + */ +#include +#include +#include +#include + +#include + +#include "tcp_vegas.h" + +#define TCP_YEAH_ALPHA 80 //lin number of packets queued at the bottleneck +#define TCP_YEAH_GAMMA 1 //lin fraction of queue to be removed per rtt +#define TCP_YEAH_DELTA 3 //log minimum fraction of cwnd to be removed on loss +#define TCP_YEAH_EPSILON 1 //log maximum fraction to be removed on early decongestion +#define TCP_YEAH_PHY 8 //lin maximum delta from base +#define TCP_YEAH_RHO 16 //lin minumum number of consecutive rtt to consider competition on loss +#define TCP_YEAH_ZETA 50 //lin minimum number of state switchs to reset reno_count + +#define TCP_SCALABLE_AI_CNT 100U + +/* YeAH variables */ +struct yeah { + struct vegas vegas; /* must be first */ + + /* YeAH */ + u32 lastQ; + u32 doing_reno_now; + + u32 reno_count; + u32 fast_count; + + u32 pkts_acked; +}; + +static void tcp_yeah_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct yeah *yeah = inet_csk_ca(sk); + + tcp_vegas_init(sk); + + yeah->doing_reno_now = 0; + yeah->lastQ = 0; + + yeah->reno_count = 2; + + /* Ensure the MD arithmetic works. This is somewhat pedantic, + * since I don't think we will see a cwnd this large. :) */ + tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128); + +} + + +static void tcp_yeah_pkts_acked(struct sock *sk, u32 pkts_acked, ktime_t last) +{ + const struct inet_connection_sock *icsk = inet_csk(sk); + struct yeah *yeah = inet_csk_ca(sk); + + if (icsk->icsk_ca_state == TCP_CA_Open) + yeah->pkts_acked = pkts_acked; + + tcp_vegas_pkts_acked(sk, pkts_acked, last); +} + +static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, + u32 seq_rtt, u32 in_flight, int flag) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct yeah *yeah = inet_csk_ca(sk); + + if (!tcp_is_cwnd_limited(sk, in_flight)) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) + tcp_slow_start(tp); + + else if (!yeah->doing_reno_now) { + /* Scalable */ + + tp->snd_cwnd_cnt+=yeah->pkts_acked; + if (tp->snd_cwnd_cnt > min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)){ + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } + + yeah->pkts_acked = 1; + + } else { + /* Reno */ + + if (tp->snd_cwnd_cnt < tp->snd_cwnd) + tp->snd_cwnd_cnt++; + + if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } + } + + /* The key players are v_vegas.beg_snd_una and v_beg_snd_nxt. + * + * These are so named because they represent the approximate values + * of snd_una and snd_nxt at the beginning of the current RTT. More + * precisely, they represent the amount of data sent during the RTT. + * At the end of the RTT, when we receive an ACK for v_beg_snd_nxt, + * we will calculate that (v_beg_snd_nxt - v_vegas.beg_snd_una) outstanding + * bytes of data have been ACKed during the course of the RTT, giving + * an "actual" rate of: + * + * (v_beg_snd_nxt - v_vegas.beg_snd_una) / (rtt duration) + * + * Unfortunately, v_vegas.beg_snd_una is not exactly equal to snd_una, + * because delayed ACKs can cover more than one segment, so they + * don't line up yeahly with the boundaries of RTTs. + * + * Another unfortunate fact of life is that delayed ACKs delay the + * advance of the left edge of our send window, so that the number + * of bytes we send in an RTT is often less than our cwnd will allow. + * So we keep track of our cwnd separately, in v_beg_snd_cwnd. + */ + + if (after(ack, yeah->vegas.beg_snd_nxt)) { + + /* We do the Vegas calculations only if we got enough RTT + * samples that we can be reasonably sure that we got + * at least one RTT sample that wasn't from a delayed ACK. + * If we only had 2 samples total, + * then that means we're getting only 1 ACK per RTT, which + * means they're almost certainly delayed ACKs. + * If we have 3 samples, we should be OK. + */ + + if (yeah->vegas.cntRTT > 2) { + u32 rtt, queue; + u64 bw; + + /* We have enough RTT samples, so, using the Vegas + * algorithm, we determine if we should increase or + * decrease cwnd, and by how much. + */ + + /* Pluck out the RTT we are using for the Vegas + * calculations. This is the min RTT seen during the + * last RTT. Taking the min filters out the effects + * of delayed ACKs, at the cost of noticing congestion + * a bit later. + */ + rtt = yeah->vegas.minRTT; + + /* Compute excess number of packets above bandwidth + * Avoid doing full 64 bit divide. + */ + bw = tp->snd_cwnd; + bw *= rtt - yeah->vegas.baseRTT; + do_div(bw, rtt); + queue = bw; + + if (queue > TCP_YEAH_ALPHA || + rtt - yeah->vegas.baseRTT > (yeah->vegas.baseRTT / TCP_YEAH_PHY)) { + if (queue > TCP_YEAH_ALPHA + && tp->snd_cwnd > yeah->reno_count) { + u32 reduction = min(queue / TCP_YEAH_GAMMA , + tp->snd_cwnd >> TCP_YEAH_EPSILON); + + tp->snd_cwnd -= reduction; + + tp->snd_cwnd = max(tp->snd_cwnd, + yeah->reno_count); + + tp->snd_ssthresh = tp->snd_cwnd; + } + + if (yeah->reno_count <= 2) + yeah->reno_count = max(tp->snd_cwnd>>1, 2U); + else + yeah->reno_count++; + + yeah->doing_reno_now = min(yeah->doing_reno_now + 1, + 0xffffffU); + } else { + yeah->fast_count++; + + if (yeah->fast_count > TCP_YEAH_ZETA) { + yeah->reno_count = 2; + yeah->fast_count = 0; + } + + yeah->doing_reno_now = 0; + } + + yeah->lastQ = queue; + + } + + /* Save the extent of the current window so we can use this + * at the end of the next RTT. + */ + yeah->vegas.beg_snd_una = yeah->vegas.beg_snd_nxt; + yeah->vegas.beg_snd_nxt = tp->snd_nxt; + yeah->vegas.beg_snd_cwnd = tp->snd_cwnd; + + /* Wipe the slate clean for the next RTT. */ + yeah->vegas.cntRTT = 0; + yeah->vegas.minRTT = 0x7fffffff; + } +} + +static u32 tcp_yeah_ssthresh(struct sock *sk) { + const struct tcp_sock *tp = tcp_sk(sk); + struct yeah *yeah = inet_csk_ca(sk); + u32 reduction; + + if (yeah->doing_reno_now < TCP_YEAH_RHO) { + reduction = yeah->lastQ; + + reduction = min( reduction, max(tp->snd_cwnd>>1, 2U) ); + + reduction = max( reduction, tp->snd_cwnd >> TCP_YEAH_DELTA); + } else + reduction = max(tp->snd_cwnd>>1,2U); + + yeah->fast_count = 0; + yeah->reno_count = max(yeah->reno_count>>1, 2U); + + return tp->snd_cwnd - reduction; +} + +static struct tcp_congestion_ops tcp_yeah = { + .flags = TCP_CONG_RTT_STAMP, + .init = tcp_yeah_init, + .ssthresh = tcp_yeah_ssthresh, + .cong_avoid = tcp_yeah_cong_avoid, + .min_cwnd = tcp_reno_min_cwnd, + .set_state = tcp_vegas_state, + .cwnd_event = tcp_vegas_cwnd_event, + .get_info = tcp_vegas_get_info, + .pkts_acked = tcp_yeah_pkts_acked, + + .owner = THIS_MODULE, + .name = "yeah", +}; + +static int __init tcp_yeah_register(void) +{ + BUG_ON(sizeof(struct yeah) > ICSK_CA_PRIV_SIZE); + tcp_register_congestion_control(&tcp_yeah); + return 0; +} + +static void __exit tcp_yeah_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_yeah); +} + +module_init(tcp_yeah_register); +module_exit(tcp_yeah_unregister); + +MODULE_AUTHOR("Angelo P. Castellani"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("YeAH TCP"); diff --git a/net/ipv4/tcp_yeah.h b/net/ipv4/tcp_yeah.h new file mode 100644 index 0000000..ed3b719 --- /dev/null +++ b/net/ipv4/tcp_yeah.h @@ -0,0 +1,7 @@ +#include +#include +#include +#include +#include + +#include diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index fc620a7..113e0c4 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -114,14 +114,33 @@ DEFINE_RWLOCK(udp_hash_lock); static int udp_port_rover; -static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[]) +/* + * Note about this hash function : + * Typical use is probably daddr = 0, only dport is going to vary hash + */ +static inline unsigned int hash_port_and_addr(__u16 port, __be32 addr) +{ + addr ^= addr >> 16; + addr ^= addr >> 8; + return port ^ addr; +} + +static inline int __udp_lib_port_inuse(unsigned int hash, int port, + __be32 daddr, struct hlist_head udptable[]) { struct sock *sk; struct hlist_node *node; + struct inet_sock *inet; - sk_for_each(sk, node, &udptable[num & (UDP_HTABLE_SIZE - 1)]) - if (sk->sk_hash == num) + sk_for_each(sk, node, &udptable[hash & (UDP_HTABLE_SIZE - 1)]) { + if (sk->sk_hash != hash) + continue; + inet = inet_sk(sk); + if (inet->num != port) + continue; + if (inet->rcv_saddr == daddr) return 1; + } return 0; } @@ -142,6 +161,7 @@ int __udp_lib_get_port(struct sock *sk, struct hlist_node *node; struct hlist_head *head; struct sock *sk2; + unsigned int hash; int error = 1; write_lock_bh(&udp_hash_lock); @@ -156,7 +176,9 @@ int __udp_lib_get_port(struct sock *sk, for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { int size; - head = &udptable[result & (UDP_HTABLE_SIZE - 1)]; + hash = hash_port_and_addr(result, + inet_sk(sk)->rcv_saddr); + head = &udptable[hash & (UDP_HTABLE_SIZE - 1)]; if (hlist_empty(head)) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + @@ -175,12 +197,23 @@ int __udp_lib_get_port(struct sock *sk, ; } result = best; - for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { + for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; + i++, result += UDP_HTABLE_SIZE) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); - if (! __udp_lib_lport_inuse(result, udptable)) + hash = hash_port_and_addr(result, 0); + if (__udp_lib_port_inuse(hash, result, + 0, udptable)) + continue; + if (!inet_sk(sk)->rcv_saddr) + break; + + hash = hash_port_and_addr(result, + inet_sk(sk)->rcv_saddr); + if (! __udp_lib_port_inuse(hash, result, + inet_sk(sk)->rcv_saddr, udptable)) break; } if (i >= (1 << 16) / UDP_HTABLE_SIZE) @@ -188,21 +221,41 @@ int __udp_lib_get_port(struct sock *sk, gotit: *port_rover = snum = result; } else { - head = &udptable[snum & (UDP_HTABLE_SIZE - 1)]; + hash = hash_port_and_addr(snum, 0); + head = &udptable[hash & (UDP_HTABLE_SIZE - 1)]; sk_for_each(sk2, node, head) - if (sk2->sk_hash == snum && - sk2 != sk && - (!sk2->sk_reuse || !sk->sk_reuse) && - (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if - || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && - (*saddr_comp)(sk, sk2) ) + if (sk2->sk_hash == hash && + sk2 != sk && + inet_sk(sk2)->num == snum && + (!sk2->sk_reuse || !sk->sk_reuse) && + (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if || + sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && + (*saddr_comp)(sk, sk2)) goto fail; + + if (inet_sk(sk)->rcv_saddr) { + hash = hash_port_and_addr(snum, + inet_sk(sk)->rcv_saddr); + head = &udptable[hash & (UDP_HTABLE_SIZE - 1)]; + + sk_for_each(sk2, node, head) + if (sk2->sk_hash == hash && + sk2 != sk && + inet_sk(sk2)->num == snum && + (!sk2->sk_reuse || !sk->sk_reuse) && + (!sk2->sk_bound_dev_if || + !sk->sk_bound_dev_if || + sk2->sk_bound_dev_if == + sk->sk_bound_dev_if) && + (*saddr_comp)(sk, sk2)) + goto fail; + } } inet_sk(sk)->num = snum; - sk->sk_hash = snum; + sk->sk_hash = hash; if (sk_unhashed(sk)) { - head = &udptable[snum & (UDP_HTABLE_SIZE - 1)]; + head = &udptable[hash & (UDP_HTABLE_SIZE - 1)]; sk_add_node(sk, head); sock_prot_inc_use(sk->sk_prot); } @@ -212,13 +265,13 @@ fail: return error; } -__inline__ int udp_get_port(struct sock *sk, unsigned short snum, +int udp_get_port(struct sock *sk, unsigned short snum, int (*scmp)(const struct sock *, const struct sock *)) { return __udp_lib_get_port(sk, snum, udp_hash, &udp_port_rover, scmp); } -inline int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) +int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) { struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2); @@ -241,63 +294,77 @@ static struct sock *__udp4_lib_lookup(__ { struct sock *sk, *result = NULL; struct hlist_node *node; - unsigned short hnum = ntohs(dport); - int badness = -1; + unsigned int hash, hashwild; + int score, best = -1, hport = ntohs(dport); + + hash = hash_port_and_addr(hport, daddr); + hashwild = hash_port_and_addr(hport, 0); read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) { + +lookup: + + sk_for_each(sk, node, &udptable[hash & (UDP_HTABLE_SIZE - 1)]) { struct inet_sock *inet = inet_sk(sk); - if (sk->sk_hash == hnum && !ipv6_only_sock(sk)) { - int score = (sk->sk_family == PF_INET ? 1 : 0); - if (inet->rcv_saddr) { - if (inet->rcv_saddr != daddr) - continue; - score+=2; - } - if (inet->daddr) { - if (inet->daddr != saddr) - continue; - score+=2; - } - if (inet->dport) { - if (inet->dport != sport) - continue; - score+=2; - } - if (sk->sk_bound_dev_if) { - if (sk->sk_bound_dev_if != dif) - continue; - score+=2; - } - if(score == 9) { - result = sk; - break; - } else if(score > badness) { - result = sk; - badness = score; - } + if (sk->sk_hash != hash || ipv6_only_sock(sk) || + inet->num != hport) + continue; + + score = (sk->sk_family == PF_INET ? 1 : 0); + if (inet->rcv_saddr) { + if (inet->rcv_saddr != daddr) + continue; + score+=2; + } + if (inet->daddr) { + if (inet->daddr != saddr) + continue; + score+=2; } + if (inet->dport) { + if (inet->dport != sport) + continue; + score+=2; + } + if (sk->sk_bound_dev_if) { + if (sk->sk_bound_dev_if != dif) + continue; + score+=2; + } + if (score == 9) { + result = sk; + goto found; + } else if (score > best) { + result = sk; + best = score; + } + } + + if (hash != hashwild) { + hash = hashwild; + goto lookup; } +found: if (result) sock_hold(result); read_unlock(&udp_hash_lock); return result; } -static inline struct sock *udp_v4_mcast_next(struct sock *sk, - __be16 loc_port, __be32 loc_addr, +static inline struct sock *udp_v4_mcast_next(struct sock *sk, unsigned int hnum, + int hport, __be32 loc_addr, __be16 rmt_port, __be32 rmt_addr, int dif) { struct hlist_node *node; struct sock *s = sk; - unsigned short hnum = ntohs(loc_port); sk_for_each_from(s, node) { struct inet_sock *inet = inet_sk(s); if (s->sk_hash != hnum || + inet->num != hport || (inet->daddr && inet->daddr != rmt_addr) || (inet->dport != rmt_port && inet->dport) || (inet->rcv_saddr && inet->rcv_saddr != loc_addr) || @@ -329,8 +396,8 @@ void __udp4_lib_err(struct sk_buff *skb, struct inet_sock *inet; struct iphdr *iph = (struct iphdr*)skb->data; struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2)); - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; struct sock *sk; int harderr; int err; @@ -390,7 +457,7 @@ out: sock_put(sk); } -__inline__ void udp_err(struct sk_buff *skb, u32 info) +void udp_err(struct sk_buff *skb, u32 info) { return __udp4_lib_err(skb, info, udp_hash); } @@ -419,13 +486,14 @@ static void udp4_hwcsum_outgoing(struct __be32 src, __be32 dst, int len ) { unsigned int offset; - struct udphdr *uh = skb->h.uh; + struct udphdr *uh = udp_hdr(skb); __wsum csum = 0; if (skb_queue_len(&sk->sk_write_queue) == 1) { /* * Only one fragment on the socket. */ + skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0); } else { @@ -434,7 +502,7 @@ static void udp4_hwcsum_outgoing(struct * fragments on the socket so that all csums of sk_buffs * should be together */ - offset = skb->h.raw - skb->data; + offset = skb_transport_offset(skb); skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); skb->ip_summed = CHECKSUM_NONE; @@ -469,7 +537,7 @@ static int udp_push_pending_frames(struc /* * Create a UDP header */ - uh = skb->h.uh; + uh = udp_hdr(skb); uh->source = fl->fl_ip_sport; uh->dest = fl->fl_ip_dport; uh->len = htons(up->len); @@ -765,38 +833,38 @@ out: int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) { - switch(cmd) + switch (cmd) { + case SIOCOUTQ: { - case SIOCOUTQ: - { - int amount = atomic_read(&sk->sk_wmem_alloc); - return put_user(amount, (int __user *)arg); - } + int amount = atomic_read(&sk->sk_wmem_alloc); + return put_user(amount, (int __user *)arg); + } - case SIOCINQ: - { - struct sk_buff *skb; - unsigned long amount; - - amount = 0; - spin_lock_bh(&sk->sk_receive_queue.lock); - skb = skb_peek(&sk->sk_receive_queue); - if (skb != NULL) { - /* - * We will only return the amount - * of this packet since that is all - * that will be read. - */ - amount = skb->len - sizeof(struct udphdr); - } - spin_unlock_bh(&sk->sk_receive_queue.lock); - return put_user(amount, (int __user *)arg); + case SIOCINQ: + { + struct sk_buff *skb; + unsigned long amount; + + amount = 0; + spin_lock_bh(&sk->sk_receive_queue.lock); + skb = skb_peek(&sk->sk_receive_queue); + if (skb != NULL) { + /* + * We will only return the amount + * of this packet since that is all + * that will be read. + */ + amount = skb->len - sizeof(struct udphdr); } + spin_unlock_bh(&sk->sk_receive_queue.lock); + return put_user(amount, (int __user *)arg); + } - default: - return -ENOIOCTLCMD; + default: + return -ENOIOCTLCMD; } - return(0); + + return 0; } /* @@ -810,7 +878,9 @@ int udp_recvmsg(struct kiocb *iocb, stru struct inet_sock *inet = inet_sk(sk); struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; struct sk_buff *skb; - int copied, err, copy_only, is_udplite = IS_UDPLITE(sk); + unsigned int ulen, copied; + int err; + int is_udplite = IS_UDPLITE(sk); /* * Check any passed addresses @@ -826,28 +896,25 @@ try_again: if (!skb) goto out; - copied = skb->len - sizeof(struct udphdr); - if (copied > len) { - copied = len; + ulen = skb->len - sizeof(struct udphdr); + copied = len; + if (copied > ulen) + copied = ulen; + else if (copied < ulen) msg->msg_flags |= MSG_TRUNC; - } /* - * Decide whether to checksum and/or copy data. - * - * UDP: checksum may have been computed in HW, - * (re-)compute it if message is truncated. - * UDP-Lite: always needs to checksum, no HW support. + * If checksum is needed at all, try to do it while copying the + * data. If the data is truncated, or if we only want a partial + * coverage checksum (UDP-Lite), do it before the copy. */ - copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY); - if (is_udplite || (!copy_only && msg->msg_flags&MSG_TRUNC)) { - if (__udp_lib_checksum_complete(skb)) + if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { + if (udp_lib_checksum_complete(skb)) goto csum_copy_err; - copy_only = 1; } - if (copy_only) + if (skb_csum_unnecessary(skb)) err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied ); else { @@ -866,8 +933,8 @@ try_again: if (sin) { sin->sin_family = AF_INET; - sin->sin_port = skb->h.uh->source; - sin->sin_addr.s_addr = skb->nh.iph->saddr; + sin->sin_port = udp_hdr(skb)->source; + sin->sin_addr.s_addr = ip_hdr(skb)->saddr; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } if (inet->cmsg_flags) @@ -875,7 +942,7 @@ try_again: err = copied; if (flags & MSG_TRUNC) - err = skb->len - sizeof(struct udphdr); + err = ulen; out_free: skb_free_datagram(sk, skb); @@ -949,7 +1016,7 @@ #else return 1; /* Now we can get the pointers */ - uh = skb->h.uh; + uh = udp_hdr(skb); udpdata = (__u8 *)uh + sizeof(struct udphdr); udpdata32 = (__be32 *)udpdata; @@ -959,7 +1026,7 @@ #else /* Check if this is a keepalive packet. If so, eat it. */ if (len == 1 && udpdata[0] == 0xff) { return 0; - } else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0 ) { + } else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0) { /* ESP Packet without Non-ESP header */ len = sizeof(struct udphdr); } else @@ -990,7 +1057,7 @@ #else return 0; /* Now we can update and verify the packet length... */ - iph = skb->nh.iph; + iph = ip_hdr(skb); iphlen = iph->ihl << 2; iph->tot_len = htons(ntohs(iph->tot_len) - len); if (skb->len < iphlen + len) { @@ -1002,7 +1069,8 @@ #else * transport header to point to ESP. Keep UDP on the stack * for later. */ - skb->h.raw = skb_pull(skb, len); + __skb_pull(skb, len); + skb_reset_transport_header(skb); /* modify the protocol (it's ESP!) */ iph->protocol = IPPROTO_ESP; @@ -1095,10 +1163,9 @@ int udp_queue_rcv_skb(struct sock * sk, } } - if (sk->sk_filter && skb->ip_summed != CHECKSUM_UNNECESSARY) { - if (__udp_lib_checksum_complete(skb)) + if (sk->sk_filter) { + if (udp_lib_checksum_complete(skb)) goto drop; - skb->ip_summed = CHECKSUM_UNNECESSARY; } if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) { @@ -1128,33 +1195,49 @@ static int __udp4_lib_mcast_deliver(stru __be32 saddr, __be32 daddr, struct hlist_head udptable[]) { - struct sock *sk; + struct sock *sk, *skw, *sknext; int dif; + int hport = ntohs(uh->dest); + unsigned int hash = hash_port_and_addr(hport, daddr); + unsigned int hashwild = hash_port_and_addr(hport, 0); - read_lock(&udp_hash_lock); - sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]); dif = skb->dev->ifindex; - sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); - if (sk) { - struct sock *sknext = NULL; + read_lock(&udp_hash_lock); + + sk = sk_head(&udptable[hash & (UDP_HTABLE_SIZE - 1)]); + skw = sk_head(&udptable[hashwild & (UDP_HTABLE_SIZE - 1)]); + + sk = udp_v4_mcast_next(sk, hash, hport, daddr, uh->source, saddr, dif); + if (!sk) { + hash = hashwild; + sk = udp_v4_mcast_next(skw, hash, hport, daddr, uh->source, + saddr, dif); + } + if (sk) { do { struct sk_buff *skb1 = skb; - - sknext = udp_v4_mcast_next(sk_next(sk), uh->dest, daddr, - uh->source, saddr, dif); - if(sknext) + sknext = udp_v4_mcast_next(sk_next(sk), hash, hport, + daddr, uh->source, saddr, dif); + if (!sknext && hash != hashwild) { + hash = hashwild; + sknext = udp_v4_mcast_next(skw, hash, hport, + daddr, uh->source, saddr, dif); + } + if (sknext) skb1 = skb_clone(skb, GFP_ATOMIC); - if(skb1) { + if (skb1) { int ret = udp_queue_rcv_skb(sk, skb1); if (ret > 0) - /* we should probably re-process instead - * of dropping packets here. */ + /* + * we should probably re-process + * instead of dropping packets here. + */ kfree_skb(skb1); } sk = sknext; - } while(sknext); + } while (sknext); } else kfree_skb(skb); read_unlock(&udp_hash_lock); @@ -1166,25 +1249,37 @@ static int __udp4_lib_mcast_deliver(stru * Otherwise, csum completion requires chacksumming packet body, * including udp header and folding it to skb->csum. */ -static inline void udp4_csum_init(struct sk_buff *skb, struct udphdr *uh) +static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, + int proto) { + const struct iphdr *iph; + int err; + + UDP_SKB_CB(skb)->partial_cov = 0; + UDP_SKB_CB(skb)->cscov = skb->len; + + if (proto == IPPROTO_UDPLITE) { + err = udplite_checksum_init(skb, uh); + if (err) + return err; + } + + iph = ip_hdr(skb); if (uh->check == 0) { skb->ip_summed = CHECKSUM_UNNECESSARY; } else if (skb->ip_summed == CHECKSUM_COMPLETE) { - if (!csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr, - skb->len, IPPROTO_UDP, skb->csum )) + if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, + proto, skb->csum)) skb->ip_summed = CHECKSUM_UNNECESSARY; } - if (skb->ip_summed != CHECKSUM_UNNECESSARY) - skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, - skb->nh.iph->daddr, - skb->len, IPPROTO_UDP, 0); + if (!skb_csum_unnecessary(skb)) + skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, + skb->len, proto, 0); /* Probably, we should checksum udp header (it should be in cache * in any case) and data in tiny packets (< rx copybreak). */ - /* UDP = UDP-Lite with a non-partial checksum coverage */ - UDP_SKB_CB(skb)->partial_cov = 0; + return 0; } /* @@ -1192,14 +1287,14 @@ static inline void udp4_csum_init(struct */ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], - int is_udplite) + int proto) { struct sock *sk; - struct udphdr *uh = skb->h.uh; + struct udphdr *uh = udp_hdr(skb); unsigned short ulen; struct rtable *rt = (struct rtable*)skb->dst; - __be32 saddr = skb->nh.iph->saddr; - __be32 daddr = skb->nh.iph->daddr; + __be32 saddr = ip_hdr(skb)->saddr; + __be32 daddr = ip_hdr(skb)->daddr; /* * Validate the packet. @@ -1211,24 +1306,21 @@ int __udp4_lib_rcv(struct sk_buff *skb, if (ulen > skb->len) goto short_packet; - if(! is_udplite ) { /* UDP validates ulen. */ - + if (proto == IPPROTO_UDP) { + /* UDP validates ulen. */ if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen)) goto short_packet; - uh = skb->h.uh; - - udp4_csum_init(skb, uh); - - } else { /* UDP-Lite validates cscov. */ - if (udplite4_csum_init(skb, uh)) - goto csum_error; + uh = udp_hdr(skb); } - if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) + if (udp4_csum_init(skb, uh, proto)) + goto csum_error; + + if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable); sk = __udp4_lib_lookup(saddr, uh->source, daddr, uh->dest, - skb->dev->ifindex, udptable ); + skb->dev->ifindex, udptable); if (sk != NULL) { int ret = udp_queue_rcv_skb(sk, skb); @@ -1250,7 +1342,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, if (udp_lib_checksum_complete(skb)) goto csum_error; - UDP_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite); + UDP_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); /* @@ -1258,11 +1350,11 @@ int __udp4_lib_rcv(struct sk_buff *skb, * don't wanna listen. Ignore it. */ kfree_skb(skb); - return(0); + return 0; short_packet: LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n", - is_udplite? "-Lite" : "", + proto == IPPROTO_UDPLITE ? "-Lite" : "", NIPQUAD(saddr), ntohs(uh->source), ulen, @@ -1277,21 +1369,21 @@ csum_error: * the network is concerned, anyway) as per 4.1.3.4 (MUST). */ LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n", - is_udplite? "-Lite" : "", + proto == IPPROTO_UDPLITE ? "-Lite" : "", NIPQUAD(saddr), ntohs(uh->source), NIPQUAD(daddr), ntohs(uh->dest), ulen); drop: - UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite); + UDP_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); kfree_skb(skb); - return(0); + return 0; } -__inline__ int udp_rcv(struct sk_buff *skb) +int udp_rcv(struct sk_buff *skb) { - return __udp4_lib_rcv(skb, udp_hash, 0); + return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP); } int udp_destroy_sock(struct sock *sk) @@ -1313,13 +1405,13 @@ int udp_lib_setsockopt(struct sock *sk, int val; int err = 0; - if(optlencorkflag = 1; @@ -1373,7 +1465,7 @@ int udp_lib_setsockopt(struct sock *sk, default: err = -ENOPROTOOPT; break; - }; + } return err; } @@ -1404,15 +1496,15 @@ int udp_lib_getsockopt(struct sock *sk, struct udp_sock *up = udp_sk(sk); int val, len; - if(get_user(len,optlen)) + if (get_user(len,optlen)) return -EFAULT; len = min_t(unsigned int, len, sizeof(int)); - if(len < 0) + if (len < 0) return -EINVAL; - switch(optname) { + switch (optname) { case UDP_CORK: val = up->corkflag; break; @@ -1433,11 +1525,11 @@ int udp_lib_getsockopt(struct sock *sk, default: return -ENOPROTOOPT; - }; + } - if(put_user(len, optlen)) + if (put_user(len, optlen)) return -EFAULT; - if(copy_to_user(optval, &val,len)) + if (copy_to_user(optval, &val,len)) return -EFAULT; return 0; } @@ -1486,15 +1578,11 @@ unsigned int udp_poll(struct file *file, struct sk_buff *skb; spin_lock_bh(&rcvq->lock); - while ((skb = skb_peek(rcvq)) != NULL) { - if (udp_lib_checksum_complete(skb)) { - UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite); - __skb_unlink(skb, rcvq); - kfree_skb(skb); - } else { - skb->ip_summed = CHECKSUM_UNNECESSARY; - break; - } + while ((skb = skb_peek(rcvq)) != NULL && + udp_lib_checksum_complete(skb)) { + UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite); + __skb_unlink(skb, rcvq); + kfree_skb(skb); } spin_unlock_bh(&rcvq->lock); @@ -1573,7 +1661,7 @@ static struct sock *udp_get_idx(struct s struct sock *sk = udp_get_first(seq); if (sk) - while(pos && (sk = udp_get_next(seq, sk)) != NULL) + while (pos && (sk = udp_get_next(seq, sk)) != NULL) --pos; return pos ? NULL : sk; } diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index b28fe1e..f34fd68 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -31,7 +31,7 @@ static int udplite_v4_get_port(struct so static int udplite_rcv(struct sk_buff *skb) { - return __udp4_lib_rcv(skb, udplite_hash, 1); + return __udp4_lib_rcv(skb, udplite_hash, IPPROTO_UDPLITE); } static void udplite_err(struct sk_buff *skb, u32 info) diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 78e80de..5ceca95 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -28,7 +28,7 @@ static int xfrm4_parse_spi(struct sk_buf switch (nexthdr) { case IPPROTO_IPIP: case IPPROTO_IPV6: - *spi = skb->nh.iph->saddr; + *spi = ip_hdr(skb)->saddr; *seq = 0; return 0; } @@ -39,9 +39,9 @@ static int xfrm4_parse_spi(struct sk_buf #ifdef CONFIG_NETFILTER static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb) { - struct iphdr *iph = skb->nh.iph; - if (skb->dst == NULL) { + const struct iphdr *iph = ip_hdr(skb); + if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev)) goto drop; @@ -55,18 +55,18 @@ #endif int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) { - int err; __be32 spi, seq; struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; struct xfrm_state *x; int xfrm_nr = 0; int decaps = 0; + int err = xfrm4_parse_spi(skb, ip_hdr(skb)->protocol, &spi, &seq); - if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0) + if (err != 0) goto drop; do { - struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); if (xfrm_nr == XFRM_MAX_DEPTH) goto drop; @@ -113,7 +113,8 @@ int xfrm4_rcv_encap(struct sk_buff *skb, break; } - if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0) + err = xfrm_parse_spi(skb, ip_hdr(skb)->protocol, &spi, &seq); + if (err < 0) goto drop; } while (!err); @@ -146,15 +147,15 @@ int xfrm4_rcv_encap(struct sk_buff *skb, return 0; } else { #ifdef CONFIG_NETFILTER - __skb_push(skb, skb->data - skb->nh.raw); - skb->nh.iph->tot_len = htons(skb->len); - ip_send_check(skb->nh.iph); + __skb_push(skb, skb->data - skb_network_header(skb)); + ip_hdr(skb)->tot_len = htons(skb->len); + ip_send_check(ip_hdr(skb)); NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, xfrm4_rcv_encap_finish); return 0; #else - return -skb->nh.iph->protocol; + return -ip_hdr(skb)->protocol; #endif } diff --git a/net/ipv4/xfrm4_mode_beet.c b/net/ipv4/xfrm4_mode_beet.c index d419e15..a73e710 100644 --- a/net/ipv4/xfrm4_mode_beet.c +++ b/net/ipv4/xfrm4_mode_beet.c @@ -29,20 +29,21 @@ #include */ static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb) { - struct iphdr *iph, *top_iph = NULL; + struct iphdr *iph, *top_iph; int hdrlen, optlen; - iph = skb->nh.iph; - skb->h.ipiph = iph; + iph = ip_hdr(skb); + skb->transport_header = skb->network_header; hdrlen = 0; optlen = iph->ihl * 4 - sizeof(*iph); if (unlikely(optlen)) hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4); - skb->nh.raw = skb_push(skb, x->props.header_len + hdrlen); - top_iph = skb->nh.iph; - skb->h.raw += sizeof(*iph) - hdrlen; + skb_push(skb, x->props.header_len - IPV4_BEET_PHMAXLEN + hdrlen); + skb_reset_network_header(skb); + top_iph = ip_hdr(skb); + skb->transport_header += sizeof(*iph) - hdrlen; memmove(top_iph, iph, sizeof(*iph)); if (unlikely(optlen)) { @@ -50,7 +51,7 @@ static int xfrm4_beet_output(struct xfrm BUG_ON(optlen < 0); - ph = (struct ip_beet_phdr *)skb->h.raw; + ph = (struct ip_beet_phdr *)skb_transport_header(skb); ph->padlen = 4 - (optlen & 4); ph->hdrlen = optlen / 8; ph->nexthdr = top_iph->protocol; @@ -69,20 +70,18 @@ static int xfrm4_beet_output(struct xfrm static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb) { - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); int phlen = 0; int optlen = 0; - __u8 ph_nexthdr = 0, protocol = 0; + u8 ph_nexthdr = 0; int err = -EINVAL; - protocol = iph->protocol; - if (unlikely(iph->protocol == IPPROTO_BEETPH)) { struct ip_beet_phdr *ph; if (!pskb_may_pull(skb, sizeof(*ph))) goto out; - ph = (struct ip_beet_phdr *)(skb->h.ipiph + 1); + ph = (struct ip_beet_phdr *)(ipip_hdr(skb) + 1); phlen = sizeof(*ph) + ph->padlen; optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen); @@ -96,22 +95,20 @@ static int xfrm4_beet_input(struct xfrm_ ph_nexthdr = ph->nexthdr; } - skb->nh.raw = skb->data + (phlen - sizeof(*iph)); - memmove(skb->nh.raw, iph, sizeof(*iph)); - skb->h.raw = skb->data + (phlen + optlen); - skb->data = skb->h.raw; + skb_set_network_header(skb, phlen - sizeof(*iph)); + memmove(skb_network_header(skb), iph, sizeof(*iph)); + skb_set_transport_header(skb, phlen + optlen); + skb->data = skb_transport_header(skb); - iph = skb->nh.iph; + iph = ip_hdr(skb); iph->ihl = (sizeof(*iph) + optlen) / 4; iph->tot_len = htons(skb->len + iph->ihl * 4); iph->daddr = x->sel.daddr.a4; iph->saddr = x->sel.saddr.a4; if (ph_nexthdr) iph->protocol = ph_nexthdr; - else - iph->protocol = protocol; iph->check = 0; - iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); + iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); err = 0; out: return err; diff --git a/net/ipv4/xfrm4_mode_transport.c b/net/ipv4/xfrm4_mode_transport.c index 92676b7..6010471 100644 --- a/net/ipv4/xfrm4_mode_transport.c +++ b/net/ipv4/xfrm4_mode_transport.c @@ -23,16 +23,13 @@ #include */ static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb) { - struct iphdr *iph; - int ihl; + struct iphdr *iph = ip_hdr(skb); + int ihl = iph->ihl * 4; - iph = skb->nh.iph; - skb->h.ipiph = iph; - - ihl = iph->ihl * 4; - skb->h.raw += ihl; - - skb->nh.raw = memmove(skb_push(skb, x->props.header_len), iph, ihl); + skb->transport_header = skb->network_header + ihl; + skb_push(skb, x->props.header_len); + skb_reset_network_header(skb); + memmove(skb_network_header(skb), iph, ihl); return 0; } @@ -46,12 +43,15 @@ static int xfrm4_transport_output(struct */ static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb) { - int ihl = skb->data - skb->h.raw; + int ihl = skb->data - skb_transport_header(skb); - if (skb->h.raw != skb->nh.raw) - skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl); - skb->nh.iph->tot_len = htons(skb->len + ihl); - skb->h.raw = skb->data; + if (skb->transport_header != skb->network_header) { + memmove(skb_transport_header(skb), + skb_network_header(skb), ihl); + skb->network_header = skb->transport_header; + } + ip_hdr(skb)->tot_len = htons(skb->len + ihl); + skb_reset_transport_header(skb); return 0; } diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index ceb4376..a2f2e6a 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -16,8 +16,8 @@ #include static inline void ipip_ecn_decapsulate(struct sk_buff *skb) { - struct iphdr *outer_iph = skb->nh.iph; - struct iphdr *inner_iph = skb->h.ipiph; + struct iphdr *outer_iph = ip_hdr(skb); + struct iphdr *inner_iph = ipip_hdr(skb); if (INET_ECN_is_ce(outer_iph->tos)) IP_ECN_set_ce(inner_iph); @@ -26,7 +26,7 @@ static inline void ipip_ecn_decapsulate( static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) { if (INET_ECN_is_ce(iph->tos)) - IP6_ECN_set_ce(skb->nh.ipv6h); + IP6_ECN_set_ce(ipv6_hdr(skb)); } /* Add encapsulation header. @@ -46,11 +46,12 @@ static int xfrm4_tunnel_output(struct xf struct iphdr *iph, *top_iph; int flags; - iph = skb->nh.iph; - skb->h.ipiph = iph; + iph = ip_hdr(skb); + skb->transport_header = skb->network_header; - skb->nh.raw = skb_push(skb, x->props.header_len); - top_iph = skb->nh.iph; + skb_push(skb, x->props.header_len); + skb_reset_network_header(skb); + top_iph = ip_hdr(skb); top_iph->ihl = 5; top_iph->version = 4; @@ -90,10 +91,11 @@ #endif static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) { - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); + const unsigned char *old_mac; int err = -EINVAL; - switch(iph->protocol){ + switch (iph->protocol){ case IPPROTO_IPIP: break; #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) @@ -111,10 +113,10 @@ #endif (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) goto out; - iph = skb->nh.iph; + iph = ip_hdr(skb); if (iph->protocol == IPPROTO_IPIP) { if (x->props.flags & XFRM_STATE_DECAP_DSCP) - ipv4_copy_dscp(iph, skb->h.ipiph); + ipv4_copy_dscp(iph, ipip_hdr(skb)); if (!(x->props.flags & XFRM_STATE_NOECN)) ipip_ecn_decapsulate(skb); } @@ -125,9 +127,10 @@ #if defined(CONFIG_IPV6) || defined (CON skb->protocol = htons(ETH_P_IPV6); } #endif - skb->mac.raw = memmove(skb->data - skb->mac_len, - skb->mac.raw, skb->mac_len); - skb->nh.raw = skb->data; + old_mac = skb_mac_header(skb); + skb_set_mac_header(skb, -skb->mac_len); + memmove(skb_mac_header(skb), old_mac, skb->mac_len); + skb_reset_network_header(skb); err = 0; out: diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 038ca16..44ef208 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -22,14 +22,13 @@ static int xfrm4_tunnel_check_size(struc { int mtu, ret = 0; struct dst_entry *dst; - struct iphdr *iph = skb->nh.iph; if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE) goto out; IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE; - if (!(iph->frag_off & htons(IP_DF)) || skb->local_df) + if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df) goto out; dst = skb->dst; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 5d51a2a..4ff8ed3 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -119,7 +119,7 @@ __xfrm4_bundle_create(struct xfrm_policy if (xfrm[i]->props.mode == XFRM_MODE_TUNNEL) { unsigned short encap_family = xfrm[i]->props.family; - switch(encap_family) { + switch (encap_family) { case AF_INET: fl_tunnel.fl4_dst = xfrm[i]->id.daddr.a4; fl_tunnel.fl4_src = xfrm[i]->props.saddr.a4; @@ -209,8 +209,8 @@ error: static void _decode_session4(struct sk_buff *skb, struct flowi *fl) { - struct iphdr *iph = skb->nh.iph; - u8 *xprth = skb->nh.raw + iph->ihl*4; + struct iphdr *iph = ip_hdr(skb); + u8 *xprth = skb_network_header(skb) + iph->ihl * 4; memset(fl, 0, sizeof(struct flowi)); if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) { @@ -263,7 +263,7 @@ _decode_session4(struct sk_buff *skb, st default: fl->fl_ipsec_spi = 0; break; - }; + } } fl->proto = iph->protocol; fl->fl4_dst = iph->daddr; diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c index 3eef064..5685103 100644 --- a/net/ipv4/xfrm4_tunnel.c +++ b/net/ipv4/xfrm4_tunnel.c @@ -12,9 +12,8 @@ #include static int ipip_output(struct xfrm_state *x, struct sk_buff *skb) { - struct iphdr *iph; + struct iphdr *iph = ip_hdr(skb); - iph = skb->nh.iph; iph->tot_len = htons(skb->len); ip_send_check(iph); diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 79682ef..8e5d54f 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO If unsure, say N. +config IPV6_OPTIMISTIC_DAD + bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)" + depends on IPV6 && EXPERIMENTAL + ---help--- + This is experimental support for optimistic Duplicate + Address Detection. It allows for autoconfigured addresses + to be used more quickly. + + If unsure, say N. + config INET6_AH tristate "IPv6: AH transformation" depends on IPV6 diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index d460017..bb33309 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -7,14 +7,15 @@ obj-$(CONFIG_IPV6) += ipv6.o ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ - exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ - ip6_flowlabel.o ipv6_syms.o inet6_connection_sock.o + exthdrs.o sysctl_net_ipv6.o datagram.o \ + ip6_flowlabel.o inet6_connection_sock.o ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \ xfrm6_output.o ipv6-$(CONFIG_NETFILTER) += netfilter.o ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o ipv6-$(CONFIG_IPV6_MIP6) += mip6.o +ipv6-$(CONFIG_PROC_FS) += proc.o ipv6-objs += $(ipv6-y) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 452a82c..3452433 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -81,6 +81,7 @@ #include #endif #include +#include #include #include @@ -208,9 +209,7 @@ #endif }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ -#if 0 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; -#endif const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; static void addrconf_del_timer(struct inet6_ifaddr *ifp) @@ -246,6 +245,37 @@ static void addrconf_mod_timer(struct in add_timer(&ifp->timer); } +static int snmp6_alloc_dev(struct inet6_dev *idev) +{ + int err = -ENOMEM; + + if (!idev || !idev->dev) + return -EINVAL; + + if (snmp_mib_init((void **)idev->stats.ipv6, + sizeof(struct ipstats_mib), + __alignof__(struct ipstats_mib)) < 0) + goto err_ip; + if (snmp_mib_init((void **)idev->stats.icmpv6, + sizeof(struct icmpv6_mib), + __alignof__(struct icmpv6_mib)) < 0) + goto err_icmp; + + return 0; + +err_icmp: + snmp_mib_free((void **)idev->stats.ipv6); +err_ip: + return err; +} + +static int snmp6_free_dev(struct inet6_dev *idev) +{ + snmp_mib_free((void **)idev->stats.icmpv6); + snmp_mib_free((void **)idev->stats.ipv6); + return 0; +} + /* Nobody refers to this device, we may destroy it. */ static void in6_dev_finish_destroy_rcu(struct rcu_head *head) @@ -271,6 +301,8 @@ #endif call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu); } +EXPORT_SYMBOL(in6_dev_finish_destroy); + static struct inet6_dev * ipv6_add_dev(struct net_device *dev) { struct inet6_dev *ndev; @@ -528,6 +560,16 @@ ipv6_add_addr(struct inet6_dev *idev, co ifa->rt = rt; + /* + * part one of RFC 4429, section 3.3 + * We should not configure an address as + * optimistic if we do not yet know the link + * layer address of our nexhop router + */ + + if (rt->rt6i_nexthop == NULL) + ifa->flags &= ~IFA_F_OPTIMISTIC; + ifa->idev = idev; in6_dev_hold(idev); /* For caller */ @@ -704,6 +746,7 @@ static int ipv6_create_tempaddr(struct i int tmp_plen; int ret = 0; int max_addresses; + u32 addr_flags; write_lock(&idev->lock); if (ift) { @@ -761,10 +804,17 @@ retry: spin_unlock_bh(&ifp->lock); write_unlock(&idev->lock); + + addr_flags = IFA_F_TEMPORARY; + /* set in addrconf_prefix_rcv() */ + if (ifp->flags & IFA_F_OPTIMISTIC) + addr_flags |= IFA_F_OPTIMISTIC; + ift = !max_addresses || ipv6_count_addresses(idev) < max_addresses ? ipv6_add_addr(idev, &addr, tmp_plen, - ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL; + ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, + addr_flags) : NULL; if (!ift || IS_ERR(ift)) { in6_ifa_put(ifp); in6_dev_put(idev); @@ -896,13 +946,14 @@ int ipv6_dev_get_saddr(struct net_device * - Tentative Address (RFC2462 section 5.4) * - A tentative address is not considered * "assigned to an interface" in the traditional - * sense. + * sense, unless it is also flagged as optimistic. * - Candidate Source Address (section 4) * - In any case, anycast addresses, multicast * addresses, and the unspecified address MUST * NOT be included in a candidate set. */ - if (ifa->flags & IFA_F_TENTATIVE) + if ((ifa->flags & IFA_F_TENTATIVE) && + (!(ifa->flags & IFA_F_OPTIMISTIC))) continue; if (unlikely(score.addr_type == IPV6_ADDR_ANY || score.addr_type & IPV6_ADDR_MULTICAST)) { @@ -961,15 +1012,17 @@ int ipv6_dev_get_saddr(struct net_device } } - /* Rule 3: Avoid deprecated address */ + /* Rule 3: Avoid deprecated and optimistic addresses */ if (hiscore.rule < 3) { if (ipv6_saddr_preferred(hiscore.addr_type) || - !(ifa_result->flags & IFA_F_DEPRECATED)) + (((ifa_result->flags & + (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED; hiscore.rule++; } if (ipv6_saddr_preferred(score.addr_type) || - !(ifa->flags & IFA_F_DEPRECATED)) { + (((ifa_result->flags & + (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) { score.attrs |= IPV6_SADDR_SCORE_PREFERRED; if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) { score.rule = 3; @@ -1107,8 +1160,10 @@ int ipv6_get_saddr(struct dst_entry *dst return ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, daddr, saddr); } +EXPORT_SYMBOL(ipv6_get_saddr); -int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr) +int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, + unsigned char banned_flags) { struct inet6_dev *idev; int err = -EADDRNOTAVAIL; @@ -1119,7 +1174,7 @@ int ipv6_get_lladdr(struct net_device *d read_lock_bh(&idev->lock); for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { - if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) { + if (ifp->scope == IFA_LINK && !(ifp->flags & banned_flags)) { ipv6_addr_copy(addr, &ifp->addr); err = 0; break; @@ -1161,6 +1216,8 @@ int ipv6_chk_addr(struct in6_addr *addr, return ifp != NULL; } +EXPORT_SYMBOL(ipv6_chk_addr); + static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev) { @@ -1669,6 +1726,13 @@ ok: if (ifp == NULL && valid_lft) { int max_addresses = in6_dev->cnf.max_addresses; + u32 addr_flags = 0; + +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + if (in6_dev->cnf.optimistic_dad && + !ipv6_devconf.forwarding) + addr_flags = IFA_F_OPTIMISTIC; +#endif /* Do not allow to create too much of autoconfigured * addresses; this would be too easy way to crash kernel. @@ -1676,7 +1740,8 @@ ok: if (!max_addresses || ipv6_count_addresses(in6_dev) < max_addresses) ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len, - addr_type&IPV6_ADDR_SCOPE_MASK, 0); + addr_type&IPV6_ADDR_SCOPE_MASK, + addr_flags); if (!ifp || IS_ERR(ifp)) { in6_dev_put(in6_dev); @@ -1884,6 +1949,11 @@ static int inet6_addr_add(int ifindex, s addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, jiffies_to_clock_t(valid_lft * HZ), flags); + /* + * Note that section 3.1 of RFC 4429 indicates + * that the Optimistic flag should not be set for + * manually configured addresses + */ addrconf_dad_start(ifp, 0); in6_ifa_put(ifp); addrconf_verify(0); @@ -2060,8 +2130,16 @@ static void init_loopback(struct net_dev static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr) { struct inet6_ifaddr * ifp; + u32 addr_flags = IFA_F_PERMANENT; - ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT); +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + if (idev->cnf.optimistic_dad && + !ipv6_devconf.forwarding) + addr_flags |= IFA_F_OPTIMISTIC; +#endif + + + ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags); if (!IS_ERR(ifp)) { addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0); addrconf_dad_start(ifp, 0); @@ -2129,7 +2207,7 @@ ipv6_inherit_linklocal(struct inet6_dev { struct in6_addr lladdr; - if (!ipv6_get_lladdr(link_dev, &lladdr)) { + if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) { addrconf_add_linklocal(idev, &lladdr); return 0; } @@ -2240,7 +2318,7 @@ #endif default: addrconf_dev_config(dev); break; - }; + } if (idev) { if (run_pending) addrconf_dad_run(idev); @@ -2281,8 +2359,9 @@ #endif break; case NETDEV_CHANGENAME: -#ifdef CONFIG_SYSCTL if (idev) { + snmp6_unregister_dev(idev); +#ifdef CONFIG_SYSCTL addrconf_sysctl_unregister(&idev->cnf); neigh_sysctl_unregister(idev->nd_parms); neigh_sysctl_register(dev, idev->nd_parms, @@ -2290,10 +2369,11 @@ #ifdef CONFIG_SYSCTL &ndisc_ifinfo_sysctl_change, NULL); addrconf_sysctl_register(idev, &idev->cnf); - } #endif + snmp6_register_dev(idev); + } break; - }; + } return NOTIFY_OK; } @@ -2474,7 +2554,11 @@ static void addrconf_dad_kick(struct ine unsigned long rand_num; struct inet6_dev *idev = ifp->idev; - rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); + if (ifp->flags & IFA_F_OPTIMISTIC) + rand_num = 0; + else + rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); + ifp->probes = idev->cnf.dad_transmits; addrconf_mod_timer(ifp, AC_DAD, rand_num); } @@ -2496,7 +2580,7 @@ static void addrconf_dad_start(struct in if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || !(ifp->flags&IFA_F_TENTATIVE) || ifp->flags & IFA_F_NODAD) { - ifp->flags &= ~IFA_F_TENTATIVE; + ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); @@ -2516,6 +2600,14 @@ static void addrconf_dad_start(struct in addrconf_dad_stop(ifp); return; } + + /* + * Optimistic nodes can start receiving + * Frames right away + */ + if(ifp->flags & IFA_F_OPTIMISTIC) + ip6_ins_rt(ifp->rt); + addrconf_dad_kick(ifp); spin_unlock_bh(&ifp->lock); out: @@ -2540,7 +2632,7 @@ static void addrconf_dad_timer(unsigned * DAD was successful */ - ifp->flags &= ~IFA_F_TENTATIVE; + ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); @@ -3164,7 +3256,6 @@ static int inet6_dump_addr(struct sk_buf s_idx = cb->args[0]; s_ip_idx = ip_idx = cb->args[1]; - read_lock(&dev_base_lock); for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) { if (idx < s_idx) @@ -3226,7 +3317,6 @@ done: read_unlock_bh(&idev->lock); in6_dev_put(idev); } - read_unlock(&dev_base_lock); cb->args[0] = idx; cb->args[1] = ip_idx; return skb->len; @@ -3359,6 +3449,9 @@ #endif #endif array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad; +#endif } static inline size_t inet6_if_nlmsg_size(void) @@ -3372,14 +3465,44 @@ static inline size_t inet6_if_nlmsg_size nla_total_size(4) /* IFLA_INET6_FLAGS */ + nla_total_size(sizeof(struct ifla_cacheinfo)) + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */ + + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */ + + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */ ); } +static inline void __snmp6_fill_stats(u64 *stats, void **mib, int items, + int bytes) +{ + int i; + int pad = bytes - sizeof(u64) * items; + BUG_ON(pad < 0); + + /* Use put_unaligned() because stats may not be aligned for u64. */ + put_unaligned(items, &stats[0]); + for (i = 1; i < items; i++) + put_unaligned(snmp_fold_field(mib, i), &stats[i]); + + memset(&stats[items], 0, pad); +} + +static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, + int bytes) +{ + switch(attrtype) { + case IFLA_INET6_STATS: + __snmp6_fill_stats(stats, (void **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes); + break; + case IFLA_INET6_ICMP6STATS: + __snmp6_fill_stats(stats, (void **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes); + break; + } +} + static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, u32 pid, u32 seq, int event, unsigned int flags) { struct net_device *dev = idev->dev; - struct nlattr *conf; + struct nlattr *nla; struct ifinfomsg *hdr; struct nlmsghdr *nlh; void *protoinfo; @@ -3419,12 +3542,22 @@ static int inet6_fill_ifinfo(struct sk_b ci.retrans_time = idev->nd_parms->retrans_time; NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci); - conf = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); - if (conf == NULL) + nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); + if (nla == NULL) goto nla_put_failure; - ipv6_store_devconf(&idev->cnf, nla_data(conf), nla_len(conf)); + ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla)); + + /* XXX - MC not implemented */ - /* XXX - Statistics/MC not implemented */ + nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64)); + if (nla == NULL) + goto nla_put_failure; + snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla)); + + nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64)); + if (nla == NULL) + goto nla_put_failure; + snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla)); nla_nest_end(skb, protoinfo); return nlmsg_end(skb, nlh); @@ -3550,30 +3683,20 @@ errout: rtnl_set_sk_err(RTNLGRP_IPV6_PREFIX, err); } -static struct rtnetlink_link inet6_rtnetlink_table[RTM_NR_MSGTYPES] = { - [RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, }, - [RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, }, - [RTM_DELADDR - RTM_BASE] = { .doit = inet6_rtm_deladdr, }, - [RTM_GETADDR - RTM_BASE] = { .doit = inet6_rtm_getaddr, - .dumpit = inet6_dump_ifaddr, }, - [RTM_GETMULTICAST - RTM_BASE] = { .dumpit = inet6_dump_ifmcaddr, }, - [RTM_GETANYCAST - RTM_BASE] = { .dumpit = inet6_dump_ifacaddr, }, - [RTM_NEWROUTE - RTM_BASE] = { .doit = inet6_rtm_newroute, }, - [RTM_DELROUTE - RTM_BASE] = { .doit = inet6_rtm_delroute, }, - [RTM_GETROUTE - RTM_BASE] = { .doit = inet6_rtm_getroute, - .dumpit = inet6_dump_fib, }, -#ifdef CONFIG_IPV6_MULTIPLE_TABLES - [RTM_GETRULE - RTM_BASE] = { .dumpit = fib6_rules_dump, }, -#endif -}; - static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) { inet6_ifa_notify(event ? : RTM_NEWADDR, ifp); switch (event) { case RTM_NEWADDR: - ip6_ins_rt(ifp->rt); + /* + * If the address was optimistic + * we inserted the route at the start of + * our DAD process, so we don't need + * to do it again + */ + if (!(ifp->rt->rt6i_node)) + ip6_ins_rt(ifp->rt); if (ifp->idev->cnf.forwarding) addrconf_join_anycast(ifp); break; @@ -3894,6 +4017,17 @@ #endif .mode = 0644, .proc_handler = &proc_dointvec, }, +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + { + .ctl_name = CTL_UNNUMBERED, + .procname = "optimistic_dad", + .data = &ipv6_devconf.optimistic_dad, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + + }, +#endif { .ctl_name = 0, /* sentinel */ } @@ -4021,11 +4155,15 @@ int register_inet6addr_notifier(struct n return atomic_notifier_chain_register(&inet6addr_chain, nb); } +EXPORT_SYMBOL(register_inet6addr_notifier); + int unregister_inet6addr_notifier(struct notifier_block *nb) { return atomic_notifier_chain_unregister(&inet6addr_chain,nb); } +EXPORT_SYMBOL(unregister_inet6addr_notifier); + /* * Init / cleanup code */ @@ -4064,7 +4202,18 @@ int __init addrconf_init(void) register_netdevice_notifier(&ipv6_dev_notf); addrconf_verify(0); - rtnetlink_links[PF_INET6] = inet6_rtnetlink_table; + + err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo); + if (err < 0) + goto errout; + + /* Only the first call to __rtnl_register can fail */ + __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL); + __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL); + __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr); + __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr); + __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr); + #ifdef CONFIG_SYSCTL addrconf_sysctl.sysctl_header = register_sysctl_table(addrconf_sysctl.addrconf_root_dir); @@ -4072,6 +4221,10 @@ #ifdef CONFIG_SYSCTL #endif return 0; +errout: + unregister_netdevice_notifier(&ipv6_dev_notf); + + return err; } void __exit addrconf_cleanup(void) @@ -4083,7 +4236,6 @@ void __exit addrconf_cleanup(void) unregister_netdevice_notifier(&ipv6_dev_notf); - rtnetlink_links[PF_INET6] = NULL; #ifdef CONFIG_SYSCTL addrconf_sysctl_unregister(&ipv6_devconf_dflt); addrconf_sysctl_unregister(&ipv6_devconf); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 5cac14a..18cb928 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -98,6 +98,11 @@ static int inet6_create(struct socket *s int try_loading_module = 0; int err; + if (sock->type != SOCK_RAW && + sock->type != SOCK_DGRAM && + !inet_ehash_secret) + build_ehash_secret(); + /* Look for the requested type/protocol pair. */ answer = NULL; lookup_protocol: @@ -349,6 +354,8 @@ out: return err; } +EXPORT_SYMBOL(inet6_bind); + int inet6_release(struct socket *sock) { struct sock *sk = sock->sk; @@ -365,6 +372,8 @@ int inet6_release(struct socket *sock) return inet_release(sock); } +EXPORT_SYMBOL(inet6_release); + int inet6_destroy_sock(struct sock *sk) { struct ipv6_pinfo *np = inet6_sk(sk); @@ -428,6 +437,8 @@ int inet6_getname(struct socket *sock, s return(0); } +EXPORT_SYMBOL(inet6_getname); + int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; @@ -437,6 +448,9 @@ int inet6_ioctl(struct socket *sock, uns case SIOCGSTAMP: return sock_get_timestamp(sk, (struct timeval __user *)arg); + case SIOCGSTAMPNS: + return sock_get_timestampns(sk, (struct timespec __user *)arg); + case SIOCADDRT: case SIOCDELRT: @@ -457,6 +471,8 @@ int inet6_ioctl(struct socket *sock, uns return(0); } +EXPORT_SYMBOL(inet6_ioctl); + const struct proto_ops inet6_stream_ops = { .family = PF_INET6, .owner = THIS_MODULE, @@ -603,6 +619,8 @@ out_illegal: goto out; } +EXPORT_SYMBOL(inet6_register_protosw); + void inet6_unregister_protosw(struct inet_protosw *p) { @@ -619,6 +637,8 @@ inet6_unregister_protosw(struct inet_pro } } +EXPORT_SYMBOL(inet6_unregister_protosw); + int inet6_sk_rebuild_header(struct sock *sk) { int err; @@ -678,7 +698,8 @@ int ipv6_opt_accepted(struct sock *sk, s if (np->rxopt.all) { if ((opt->hop && (np->rxopt.bits.hopopts || np->rxopt.bits.ohopopts)) || - ((IPV6_FLOWINFO_MASK & *(__be32*)skb->nh.raw) && + ((IPV6_FLOWINFO_MASK & + *(__be32 *)skb_network_header(skb)) && np->rxopt.bits.rxflow) || (opt->srcrt && (np->rxopt.bits.srcrt || np->rxopt.bits.osrcrt)) || @@ -691,61 +712,28 @@ int ipv6_opt_accepted(struct sock *sk, s EXPORT_SYMBOL_GPL(ipv6_opt_accepted); -int -snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign) -{ - if (ptr == NULL) - return -EINVAL; - - ptr[0] = __alloc_percpu(mibsize); - if (!ptr[0]) - goto err0; - - ptr[1] = __alloc_percpu(mibsize); - if (!ptr[1]) - goto err1; - - return 0; - -err1: - free_percpu(ptr[0]); - ptr[0] = NULL; -err0: - return -ENOMEM; -} - -void -snmp6_mib_free(void *ptr[2]) -{ - if (ptr == NULL) - return; - free_percpu(ptr[0]); - free_percpu(ptr[1]); - ptr[0] = ptr[1] = NULL; -} - static int __init init_ipv6_mibs(void) { - if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipstats_mib), - __alignof__(struct ipstats_mib)) < 0) + if (snmp_mib_init((void **)ipv6_statistics, sizeof (struct ipstats_mib), + __alignof__(struct ipstats_mib)) < 0) goto err_ip_mib; - if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib), - __alignof__(struct icmpv6_mib)) < 0) + if (snmp_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib), + __alignof__(struct icmpv6_mib)) < 0) goto err_icmp_mib; - if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib), - __alignof__(struct udp_mib)) < 0) + if (snmp_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib), + __alignof__(struct udp_mib)) < 0) goto err_udp_mib; - if (snmp6_mib_init((void **)udplite_stats_in6, sizeof (struct udp_mib), - __alignof__(struct udp_mib)) < 0) + if (snmp_mib_init((void **)udplite_stats_in6, sizeof (struct udp_mib), + __alignof__(struct udp_mib)) < 0) goto err_udplite_mib; return 0; err_udplite_mib: - snmp6_mib_free((void **)udp_stats_in6); + snmp_mib_free((void **)udp_stats_in6); err_udp_mib: - snmp6_mib_free((void **)icmpv6_statistics); + snmp_mib_free((void **)icmpv6_statistics); err_icmp_mib: - snmp6_mib_free((void **)ipv6_statistics); + snmp_mib_free((void **)ipv6_statistics); err_ip_mib: return -ENOMEM; @@ -753,10 +741,10 @@ err_ip_mib: static void cleanup_ipv6_mibs(void) { - snmp6_mib_free((void **)ipv6_statistics); - snmp6_mib_free((void **)icmpv6_statistics); - snmp6_mib_free((void **)udp_stats_in6); - snmp6_mib_free((void **)udplite_stats_in6); + snmp_mib_free((void **)ipv6_statistics); + snmp_mib_free((void **)icmpv6_statistics); + snmp_mib_free((void **)udp_stats_in6); + snmp_mib_free((void **)udplite_stats_in6); } static int __init inet6_init(void) @@ -929,6 +917,8 @@ static void __exit inet6_exit(void) { /* First of all disallow new sockets creation. */ sock_unregister(PF_INET6); + /* Disallow any further netlink messages */ + rtnl_unregister_all(PF_INET6); /* Cleanup code parts. */ ipv6_packet_cleanup(); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index dc68b72..b696c84 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -238,8 +238,8 @@ #endif top_iph = (struct ipv6hdr *)skb->data; top_iph->payload_len = htons(skb->len - sizeof(*top_iph)); - nexthdr = *skb->nh.raw; - *skb->nh.raw = IPPROTO_AH; + nexthdr = *skb_network_header(skb); + *skb_network_header(skb) = IPPROTO_AH; /* When there are no extension headers, we only need to save the first * 8 bytes of the base IP header. @@ -247,7 +247,7 @@ #endif memcpy(tmp_base, top_iph, sizeof(tmp_base)); tmp_ext = NULL; - extlen = skb->h.raw - (unsigned char *)(top_iph + 1); + extlen = skb_transport_offset(skb) + sizeof(struct ipv6hdr); if (extlen) { extlen += sizeof(*tmp_ext); tmp_ext = kmalloc(extlen, GFP_ATOMIC); @@ -268,7 +268,7 @@ #endif goto error_free_iph; } - ah = (struct ip_auth_hdr *)skb->h.raw; + ah = (struct ip_auth_hdr *)skb_transport_header(skb); ah->nexthdr = nexthdr; top_iph->priority = 0; @@ -316,8 +316,8 @@ static int ah6_input(struct xfrm_state * * * To erase AH: * Keeping copy of cleared headers. After AH processing, - * Moving the pointer of skb->nh.raw by using skb_pull as long as AH - * header length. Then copy back the copy as long as hdr_len + * Moving the pointer of skb->network_header by using skb_pull as long + * as AH header length. Then copy back the copy as long as hdr_len * If destination header following AH exists, copy it into after [Ext2]. * * |<>|[IPv6][Ext1][Ext2][Dest][Payload] @@ -325,6 +325,7 @@ static int ah6_input(struct xfrm_state * */ struct ipv6_auth_hdr *ah; + struct ipv6hdr *ip6h; struct ah_data *ahp; unsigned char *tmp_hdr = NULL; u16 hdr_len; @@ -341,7 +342,7 @@ static int ah6_input(struct xfrm_state * pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) goto out; - hdr_len = skb->data - skb->nh.raw; + hdr_len = skb->data - skb_network_header(skb); ah = (struct ipv6_auth_hdr*)skb->data; ahp = x->data; nexthdr = ah->nexthdr; @@ -354,16 +355,17 @@ static int ah6_input(struct xfrm_state * if (!pskb_may_pull(skb, ah_hlen)) goto out; - tmp_hdr = kmemdup(skb->nh.raw, hdr_len, GFP_ATOMIC); + tmp_hdr = kmemdup(skb_network_header(skb), hdr_len, GFP_ATOMIC); if (!tmp_hdr) goto out; - if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len, XFRM_POLICY_IN)) + ip6h = ipv6_hdr(skb); + if (ipv6_clear_mutable_options(ip6h, hdr_len, XFRM_POLICY_IN)) goto free_out; - skb->nh.ipv6h->priority = 0; - skb->nh.ipv6h->flow_lbl[0] = 0; - skb->nh.ipv6h->flow_lbl[1] = 0; - skb->nh.ipv6h->flow_lbl[2] = 0; - skb->nh.ipv6h->hop_limit = 0; + ip6h->priority = 0; + ip6h->flow_lbl[0] = 0; + ip6h->flow_lbl[1] = 0; + ip6h->flow_lbl[2] = 0; + ip6h->hop_limit = 0; { u8 auth_data[MAX_AH_AUTH_LEN]; @@ -382,7 +384,9 @@ static int ah6_input(struct xfrm_state * } } - skb->h.raw = memcpy(skb->nh.raw += ah_hlen, tmp_hdr, hdr_len); + skb->network_header += ah_hlen; + memcpy(skb_network_header(skb), tmp_hdr, hdr_len); + skb->transport_header = skb->network_header; __skb_pull(skb, ah_hlen + hdr_len); kfree(tmp_hdr); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 3b4e8dc..403eee6 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -209,7 +209,7 @@ void ipv6_icmp_error(struct sock *sk, st __be16 port, u32 info, u8 *payload) { struct ipv6_pinfo *np = inet6_sk(sk); - struct icmp6hdr *icmph = (struct icmp6hdr *)skb->h.raw; + struct icmp6hdr *icmph = icmp6_hdr(skb); struct sock_exterr_skb *serr; if (!np->recverr) @@ -227,11 +227,12 @@ void ipv6_icmp_error(struct sock *sk, st serr->ee.ee_pad = 0; serr->ee.ee_info = info; serr->ee.ee_data = 0; - serr->addr_offset = (u8*)&(((struct ipv6hdr*)(icmph+1))->daddr) - skb->nh.raw; + serr->addr_offset = (u8 *)&(((struct ipv6hdr *)(icmph + 1))->daddr) - + skb_network_header(skb); serr->port = port; - skb->h.raw = payload; __skb_pull(skb, payload - skb->data); + skb_reset_transport_header(skb); if (sock_queue_err_skb(sk, skb)) kfree_skb(skb); @@ -251,8 +252,9 @@ void ipv6_local_error(struct sock *sk, i if (!skb) return; - iph = (struct ipv6hdr*)skb_put(skb, sizeof(struct ipv6hdr)); - skb->nh.ipv6h = iph; + skb_put(skb, sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + iph = ipv6_hdr(skb); ipv6_addr_copy(&iph->daddr, &fl->fl6_dst); serr = SKB_EXT_ERR(skb); @@ -263,11 +265,11 @@ void ipv6_local_error(struct sock *sk, i serr->ee.ee_pad = 0; serr->ee.ee_info = info; serr->ee.ee_data = 0; - serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw; + serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb); serr->port = fl->fl_ip_dport; - skb->h.raw = skb->tail; - __skb_pull(skb, skb->tail - skb->data); + __skb_pull(skb, skb_tail_pointer(skb) - skb->data); + skb_reset_transport_header(skb); if (sock_queue_err_skb(sk, skb)) kfree_skb(skb); @@ -309,21 +311,24 @@ int ipv6_recv_error(struct sock *sk, str sin = (struct sockaddr_in6 *)msg->msg_name; if (sin) { + const unsigned char *nh = skb_network_header(skb); sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; sin->sin6_port = serr->port; sin->sin6_scope_id = 0; if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { ipv6_addr_copy(&sin->sin6_addr, - (struct in6_addr *)(skb->nh.raw + serr->addr_offset)); + (struct in6_addr *)(nh + serr->addr_offset)); if (np->sndflow) - sin->sin6_flowinfo = *(__be32*)(skb->nh.raw + serr->addr_offset - 24) & IPV6_FLOWINFO_MASK; + sin->sin6_flowinfo = + (*(__be32 *)(nh + serr->addr_offset - 24) & + IPV6_FLOWINFO_MASK); if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) sin->sin6_scope_id = IP6CB(skb)->iif; } else { ipv6_addr_set(&sin->sin6_addr, 0, 0, htonl(0xffff), - *(__be32*)(skb->nh.raw + serr->addr_offset)); + *(__be32 *)(nh + serr->addr_offset)); } } @@ -335,7 +340,7 @@ int ipv6_recv_error(struct sock *sk, str sin->sin6_flowinfo = 0; sin->sin6_scope_id = 0; if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { - ipv6_addr_copy(&sin->sin6_addr, &skb->nh.ipv6h->saddr); + ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr); if (np->rxopt.all) datagram_recv_ctl(sk, msg, skb); if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) @@ -344,8 +349,7 @@ int ipv6_recv_error(struct sock *sk, str struct inet_sock *inet = inet_sk(sk); ipv6_addr_set(&sin->sin6_addr, 0, 0, - htonl(0xffff), - skb->nh.iph->saddr); + htonl(0xffff), ip_hdr(skb)->saddr); if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); } @@ -381,33 +385,34 @@ int datagram_recv_ctl(struct sock *sk, s { struct ipv6_pinfo *np = inet6_sk(sk); struct inet6_skb_parm *opt = IP6CB(skb); + unsigned char *nh = skb_network_header(skb); if (np->rxopt.bits.rxinfo) { struct in6_pktinfo src_info; src_info.ipi6_ifindex = opt->iif; - ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr); + ipv6_addr_copy(&src_info.ipi6_addr, &ipv6_hdr(skb)->daddr); put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxhlim) { - int hlim = skb->nh.ipv6h->hop_limit; + int hlim = ipv6_hdr(skb)->hop_limit; put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); } if (np->rxopt.bits.rxtclass) { - int tclass = (ntohl(*(__be32 *)skb->nh.ipv6h) >> 20) & 0xff; + int tclass = (ntohl(*(__be32 *)ipv6_hdr(skb)) >> 20) & 0xff; put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass); } - if (np->rxopt.bits.rxflow && (*(__be32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) { - __be32 flowinfo = *(__be32*)skb->nh.raw & IPV6_FLOWINFO_MASK; + if (np->rxopt.bits.rxflow && (*(__be32 *)nh & IPV6_FLOWINFO_MASK)) { + __be32 flowinfo = *(__be32 *)nh & IPV6_FLOWINFO_MASK; put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); } /* HbH is allowed only once */ if (np->rxopt.bits.hopopts && opt->hop) { - u8 *ptr = skb->nh.raw + opt->hop; + u8 *ptr = nh + opt->hop; put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); } @@ -423,11 +428,11 @@ int datagram_recv_ctl(struct sock *sk, s * IPV6_RECVDSTOPTS is more generic. --yoshfuji */ unsigned int off = sizeof(struct ipv6hdr); - u8 nexthdr = skb->nh.ipv6h->nexthdr; + u8 nexthdr = ipv6_hdr(skb)->nexthdr; while (off <= opt->lastopt) { unsigned len; - u8 *ptr = skb->nh.raw + off; + u8 *ptr = nh + off; switch(nexthdr) { case IPPROTO_DSTOPTS: @@ -461,27 +466,27 @@ int datagram_recv_ctl(struct sock *sk, s struct in6_pktinfo src_info; src_info.ipi6_ifindex = opt->iif; - ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr); + ipv6_addr_copy(&src_info.ipi6_addr, &ipv6_hdr(skb)->daddr); put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxohlim) { - int hlim = skb->nh.ipv6h->hop_limit; + int hlim = ipv6_hdr(skb)->hop_limit; put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim); } if (np->rxopt.bits.ohopopts && opt->hop) { - u8 *ptr = skb->nh.raw + opt->hop; + u8 *ptr = nh + opt->hop; put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr); } if (np->rxopt.bits.odstopts && opt->dst0) { - u8 *ptr = skb->nh.raw + opt->dst0; + u8 *ptr = nh + opt->dst0; put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); } if (np->rxopt.bits.osrcrt && opt->srcrt) { - struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); + struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt); put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr); } if (np->rxopt.bits.odstopts && opt->dst1) { - u8 *ptr = skb->nh.raw + opt->dst1; + u8 *ptr = nh + opt->dst1; put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); } return 0; @@ -718,7 +723,7 @@ #endif cmsg->cmsg_type); err = -EINVAL; break; - }; + } } exit_f: diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 363e63f..7107bb7 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -42,21 +42,19 @@ #include static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) { int err; - int hdr_len; struct ipv6hdr *top_iph; struct ipv6_esp_hdr *esph; struct crypto_blkcipher *tfm; struct blkcipher_desc desc; - struct esp_data *esp; struct sk_buff *trailer; int blksize; int clen; int alen; int nfrags; - - esp = x->data; - hdr_len = skb->h.raw - skb->data + - sizeof(*esph) + esp->conf.ivlen; + u8 *tail; + struct esp_data *esp = x->data; + int hdr_len = (skb_transport_offset(skb) + + sizeof(*esph) + esp->conf.ivlen); /* Strip IP+ESP header. */ __skb_pull(skb, hdr_len); @@ -81,19 +79,20 @@ static int esp6_output(struct xfrm_state } /* Fill padding... */ + tail = skb_tail_pointer(trailer); do { int i; for (i=0; ilen - 2; i++) - *(u8*)(trailer->tail + i) = i+1; + tail[i] = i + 1; } while (0); - *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2; + tail[clen-skb->len - 2] = (clen - skb->len) - 2; pskb_put(skb, trailer, clen - skb->len); top_iph = (struct ipv6hdr *)__skb_push(skb, hdr_len); - esph = (struct ipv6_esp_hdr *)skb->h.raw; + esph = (struct ipv6_esp_hdr *)skb_transport_header(skb); top_iph->payload_len = htons(skb->len + alen - sizeof(*top_iph)); - *(u8*)(trailer->tail - 1) = *skb->nh.raw; - *skb->nh.raw = IPPROTO_ESP; + *(skb_tail_pointer(trailer) - 1) = *skb_network_header(skb); + *skb_network_header(skb) = IPPROTO_ESP; esph->spi = x->id.spi; esph->seq_no = htonl(++x->replay.oseq); @@ -150,8 +149,7 @@ static int esp6_input(struct xfrm_state int blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4); int alen = esp->auth.icv_trunc_len; int elen = skb->len - sizeof(struct ipv6_esp_hdr) - esp->conf.ivlen - alen; - - int hdr_len = skb->h.raw - skb->nh.raw; + int hdr_len = skb_network_header_len(skb); int nfrags; int ret = 0; @@ -191,7 +189,7 @@ static int esp6_input(struct xfrm_state skb->ip_summed = CHECKSUM_NONE; esph = (struct ipv6_esp_hdr*)skb->data; - iph = skb->nh.ipv6h; + iph = ipv6_hdr(skb); /* Get ivec. This can be wrong, check against another impls. */ if (esp->conf.ivlen) @@ -231,28 +229,30 @@ static int esp6_input(struct xfrm_state ret = nexthdr[1]; } - skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - hdr_len; - + __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen); + skb_set_transport_header(skb, -hdr_len); out: return ret; } -static u32 esp6_get_max_size(struct xfrm_state *x, int mtu) +static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) { struct esp_data *esp = x->data; u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); + u32 align = max_t(u32, blksize, esp->conf.padlen); + u32 rem; + + mtu -= x->props.header_len + esp->auth.icv_trunc_len; + rem = mtu & (align - 1); + mtu &= ~(align - 1); - if (x->props.mode == XFRM_MODE_TUNNEL) { - mtu = ALIGN(mtu + 2, blksize); - } else { - /* The worst case. */ + if (x->props.mode != XFRM_MODE_TUNNEL) { u32 padsize = ((blksize - 1) & 7) + 1; - mtu = ALIGN(mtu + 2, padsize) + blksize - padsize; + mtu -= blksize - padsize; + mtu += min_t(u32, blksize - padsize, rem); } - if (esp->conf.padlen) - mtu = ALIGN(mtu, esp->conf.padlen); - return mtu + x->props.header_len + esp->auth.icv_trunc_len; + return mtu - 2; } static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, @@ -382,7 +382,7 @@ static struct xfrm_type esp6_type = .proto = IPPROTO_ESP, .init_state = esp6_init_state, .destructor = esp6_destroy, - .get_max_size = esp6_get_max_size, + .get_mtu = esp6_get_mtu, .input = esp6_input, .output = esp6_output, .hdr_offset = xfrm6_find_1stfragopt, diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index fb39604..6d8e4ac 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -50,13 +50,14 @@ #include int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) { - int packet_len = skb->tail - skb->nh.raw; + const unsigned char *nh = skb_network_header(skb); + int packet_len = skb->tail - skb->network_header; struct ipv6_opt_hdr *hdr; int len; if (offset + 2 > packet_len) goto bad; - hdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + hdr = (struct ipv6_opt_hdr *)(nh + offset); len = ((hdr->hdrlen + 1) << 3); if (offset + len > packet_len) @@ -66,7 +67,7 @@ int ipv6_find_tlv(struct sk_buff *skb, i len -= 2; while (len > 0) { - int opttype = skb->nh.raw[offset]; + int opttype = nh[offset]; int optlen; if (opttype == type) @@ -77,7 +78,7 @@ int ipv6_find_tlv(struct sk_buff *skb, i optlen = 1; break; default: - optlen = skb->nh.raw[offset + 1] + 2; + optlen = nh[offset + 1] + 2; if (optlen > len) goto bad; break; @@ -113,7 +114,7 @@ static int ip6_tlvopt_unknown(struct sk_ { struct sk_buff *skb = *skbp; - switch ((skb->nh.raw[optoff] & 0xC0) >> 6) { + switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) { case 0: /* ignore */ return 1; @@ -124,12 +125,12 @@ static int ip6_tlvopt_unknown(struct sk_ /* Actually, it is redundant check. icmp_send will recheck in any case. */ - if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) + if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) break; case 2: /* send ICMP PARM PROB regardless and drop packet */ icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); return 0; - }; + } kfree_skb(skb); return 0; @@ -141,19 +142,20 @@ static int ip6_parse_tlv(struct tlvtype_ { struct sk_buff *skb = *skbp; struct tlvtype_proc *curr; - int off = skb->h.raw - skb->nh.raw; - int len = ((skb->h.raw[1]+1)<<3); + const unsigned char *nh = skb_network_header(skb); + int off = skb_network_header_len(skb); + int len = (skb_transport_header(skb)[1] + 1) << 3; - if ((skb->h.raw + len) - skb->data > skb_headlen(skb)) + if (skb_transport_offset(skb) + len > skb_headlen(skb)) goto bad; off += 2; len -= 2; while (len > 0) { - int optlen = skb->nh.raw[off+1]+2; + int optlen = nh[off + 1] + 2; - switch (skb->nh.raw[off]) { + switch (nh[off]) { case IPV6_TLV_PAD0: optlen = 1; break; @@ -165,7 +167,7 @@ static int ip6_parse_tlv(struct tlvtype_ if (optlen > len) goto bad; for (curr=procs; curr->type >= 0; curr++) { - if (curr->type == skb->nh.raw[off]) { + if (curr->type == nh[off]) { /* type specific length/alignment checks will be performed in the func(). */ @@ -200,7 +202,7 @@ static int ipv6_dest_hao(struct sk_buff struct sk_buff *skb = *skbp; struct ipv6_destopt_hao *hao; struct inet6_skb_parm *opt = IP6CB(skb); - struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->nh.raw; + struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct in6_addr tmp_addr; int ret; @@ -211,7 +213,7 @@ static int ipv6_dest_hao(struct sk_buff opt->dsthao = opt->dst1; opt->dst1 = 0; - hao = (struct ipv6_destopt_hao *)(skb->nh.raw + optoff); + hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff); if (hao->length != 16) { LIMIT_NETDEBUG( @@ -244,8 +246,9 @@ static int ipv6_dest_hao(struct sk_buff /* update all variable using below by copied skbuff */ *skbp = skb = skb2; - hao = (struct ipv6_destopt_hao *)(skb2->nh.raw + optoff); - ipv6h = (struct ipv6hdr *)skb2->nh.raw; + hao = (struct ipv6_destopt_hao *)(skb_network_header(skb2) + + optoff); + ipv6h = ipv6_hdr(skb2); } if (skb->ip_summed == CHECKSUM_COMPLETE) @@ -255,7 +258,7 @@ static int ipv6_dest_hao(struct sk_buff ipv6_addr_copy(&ipv6h->saddr, &hao->addr); ipv6_addr_copy(&hao->addr, &tmp_addr); - if (skb->tstamp.off_sec == 0) + if (skb->tstamp.tv64 == 0) __net_timestamp(skb); return 1; @@ -285,16 +288,16 @@ #ifdef CONFIG_IPV6_MIP6 #endif struct dst_entry *dst; - if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || - !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { + if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || + !pskb_may_pull(skb, (skb_transport_offset(skb) + + ((skb_transport_header(skb)[1] + 1) << 3)))) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); kfree_skb(skb); return -1; } - opt->lastopt = skb->h.raw - skb->nh.raw; - opt->dst1 = skb->h.raw - skb->nh.raw; + opt->lastopt = opt->dst1 = skb_network_header_len(skb); #ifdef CONFIG_IPV6_MIP6 dstbuf = opt->dst1; #endif @@ -303,7 +306,7 @@ #endif if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) { dst_release(dst); skb = *skbp; - skb->h.raw += ((skb->h.raw[1]+1)<<3); + skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; opt = IP6CB(skb); #ifdef CONFIG_IPV6_MIP6 opt->nhoff = dstbuf; @@ -384,18 +387,20 @@ static int ipv6_rthdr_rcv(struct sk_buff in6_dev_put(idev); - if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || - !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { + if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || + !pskb_may_pull(skb, (skb_transport_offset(skb) + + ((skb_transport_header(skb)[1] + 1) << 3)))) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); kfree_skb(skb); return -1; } - hdr = (struct ipv6_rt_hdr *) skb->h.raw; + hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb); switch (hdr->type) { #ifdef CONFIG_IPV6_MIP6 + case IPV6_SRCRT_TYPE_2: break; #endif case IPV6_SRCRT_TYPE_0: @@ -406,11 +411,12 @@ #endif default: IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + (&hdr->type) - skb_network_header(skb)); return -1; } - if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) || + if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) || skb->pkt_type != PACKET_HOST) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS); @@ -438,12 +444,11 @@ #endif break; } - opt->lastopt = skb->h.raw - skb->nh.raw; - opt->srcrt = skb->h.raw - skb->nh.raw; - skb->h.raw += (hdr->hdrlen + 1) << 3; + opt->lastopt = opt->srcrt = skb_network_header_len(skb); + skb->transport_header += (hdr->hdrlen + 1) << 3; opt->dst0 = opt->dst1; opt->dst1 = 0; - opt->nhoff = (&hdr->nexthdr) - skb->nh.raw; + opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); return 1; } @@ -452,7 +457,9 @@ #endif if (hdr->hdrlen & 0x01) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + ((&hdr->hdrlen) - + skb_network_header(skb))); return -1; } break; @@ -479,7 +486,9 @@ #endif if (hdr->segments_left > n) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + ((&hdr->segments_left) - + skb_network_header(skb))); return -1; } @@ -498,7 +507,7 @@ #endif kfree_skb(skb); *skbp = skb = skb2; opt = IP6CB(skb2); - hdr = (struct ipv6_rt_hdr *) skb2->h.raw; + hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb2); } if (skb->ip_summed == CHECKSUM_COMPLETE) @@ -514,7 +523,7 @@ #endif #ifdef CONFIG_IPV6_MIP6 case IPV6_SRCRT_TYPE_2: if (xfrm6_input_addr(skb, (xfrm_address_t *)addr, - (xfrm_address_t *)&skb->nh.ipv6h->saddr, + (xfrm_address_t *)&ipv6_hdr(skb)->saddr, IPPROTO_ROUTING) < 0) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS); @@ -541,19 +550,19 @@ #endif } ipv6_addr_copy(&daddr, addr); - ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr); - ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr); + ipv6_addr_copy(addr, &ipv6_hdr(skb)->daddr); + ipv6_addr_copy(&ipv6_hdr(skb)->daddr, &daddr); dst_release(xchg(&skb->dst, NULL)); ip6_route_input(skb); if (skb->dst->error) { - skb_push(skb, skb->data - skb->nh.raw); + skb_push(skb, skb->data - skb_network_header(skb)); dst_input(skb); return -1; } if (skb->dst->dev->flags&IFF_LOOPBACK) { - if (skb->nh.ipv6h->hop_limit <= 1) { + if (ipv6_hdr(skb)->hop_limit <= 1) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, @@ -561,11 +570,11 @@ #endif kfree_skb(skb); return -1; } - skb->nh.ipv6h->hop_limit--; + ipv6_hdr(skb)->hop_limit--; goto looped_back; } - skb_push(skb, skb->data - skb->nh.raw); + skb_push(skb, skb->data - skb_network_header(skb)); dst_input(skb); return -1; } @@ -656,13 +665,14 @@ EXPORT_SYMBOL_GPL(ipv6_invert_rthdr); static int ipv6_hop_ra(struct sk_buff **skbp, int optoff) { struct sk_buff *skb = *skbp; + const unsigned char *nh = skb_network_header(skb); - if (skb->nh.raw[optoff+1] == 2) { + if (nh[optoff + 1] == 2) { IP6CB(skb)->ra = optoff; return 1; } LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", - skb->nh.raw[optoff+1]); + nh[optoff + 1]); kfree_skb(skb); return 0; } @@ -672,23 +682,24 @@ static int ipv6_hop_ra(struct sk_buff ** static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff) { struct sk_buff *skb = *skbp; + const unsigned char *nh = skb_network_header(skb); u32 pkt_len; - if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) { + if (nh[optoff + 1] != 4 || (optoff & 3) != 2) { LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", - skb->nh.raw[optoff+1]); + nh[optoff+1]); IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); goto drop; } - pkt_len = ntohl(*(__be32*)(skb->nh.raw+optoff+2)); + pkt_len = ntohl(*(__be32 *)(nh + optoff + 2)); if (pkt_len <= IPV6_MAXPLEN) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); return 0; } - if (skb->nh.ipv6h->payload_len) { + if (ipv6_hdr(skb)->payload_len) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); return 0; @@ -727,13 +738,14 @@ int ipv6_parse_hopopts(struct sk_buff ** struct inet6_skb_parm *opt = IP6CB(skb); /* - * skb->nh.raw is equal to skb->data, and - * skb->h.raw - skb->nh.raw is always equal to + * skb_network_header(skb) is equal to skb->data, and + * skb_network_header_len(skb) is always equal to * sizeof(struct ipv6hdr) by definition of * hop-by-hop options. */ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) || - !pskb_may_pull(skb, sizeof(struct ipv6hdr) + ((skb->h.raw[1] + 1) << 3))) { + !pskb_may_pull(skb, (sizeof(struct ipv6hdr) + + ((skb_transport_header(skb)[1] + 1) << 3)))) { kfree_skb(skb); return -1; } @@ -741,7 +753,7 @@ int ipv6_parse_hopopts(struct sk_buff ** opt->hop = sizeof(struct ipv6hdr); if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) { skb = *skbp; - skb->h.raw += (skb->h.raw[1]+1)<<3; + skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; opt = IP6CB(skb); opt->nhoff = sizeof(struct ipv6hdr); return 1; @@ -810,6 +822,8 @@ void ipv6_push_nfrag_opts(struct sk_buff ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); } +EXPORT_SYMBOL(ipv6_push_nfrag_opts); + void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto) { if (opt->dst1opt) diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index ea3035b..fc3882c 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -95,8 +96,27 @@ static int fib6_rule_action(struct fib_r if (table) rt = lookup(table, flp, flags); - if (rt != &ip6_null_entry) + if (rt != &ip6_null_entry) { + struct fib6_rule *r = (struct fib6_rule *)rule; + + /* + * If we need to find a source address for this traffic, + * we check the result if it meets requirement of the rule. + */ + if ((rule->flags & FIB_RULE_FIND_SADDR) && + r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) { + struct in6_addr saddr; + if (ipv6_get_saddr(&rt->u.dst, &flp->fl6_dst, + &saddr)) + goto again; + if (!ipv6_prefix_equal(&saddr, &r->src.addr, + r->src.plen)) + goto again; + ipv6_addr_copy(&flp->fl6_src, &saddr); + } goto out; + } +again: dst_release(&rt->u.dst); rt = NULL; goto out; @@ -117,9 +137,17 @@ static int fib6_rule_match(struct fib_ru !ipv6_prefix_equal(&fl->fl6_dst, &r->dst.addr, r->dst.plen)) return 0; + /* + * If FIB_RULE_FIND_SADDR is set and we do not have a + * source address for the traffic, we defer check for + * source address. + */ if (r->src.plen) { - if (!(flags & RT6_LOOKUP_F_HAS_SADDR) || - !ipv6_prefix_equal(&fl->fl6_src, &r->src.addr, r->src.plen)) + if (flags & RT6_LOOKUP_F_HAS_SADDR) { + if (!ipv6_prefix_equal(&fl->fl6_src, &r->src.addr, + r->src.plen)) + return 0; + } else if (!(r->common.flags & FIB_RULE_FIND_SADDR)) return 0; } @@ -216,11 +244,6 @@ nla_put_failure: return -ENOBUFS; } -int fib6_rules_dump(struct sk_buff *skb, struct netlink_callback *cb) -{ - return fib_rules_dump(skb, cb, AF_INET6); -} - static u32 fib6_rule_default_pref(void) { return 0x3FFF; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index edfe98b..e9bcce9 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -68,6 +68,7 @@ #include #include DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics) __read_mostly; +EXPORT_SYMBOL(icmpv6_statistics); /* * The ICMP socket(s). This is the most convenient way to flow control @@ -128,9 +129,9 @@ void icmpv6_param_prob(struct sk_buff *s static int is_ineligible(struct sk_buff *skb) { - int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data; + int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data; int len = skb->len - ptr; - __u8 nexthdr = skb->nh.ipv6h->nexthdr; + __u8 nexthdr = ipv6_hdr(skb)->nexthdr; if (len < 0) return 1; @@ -205,7 +206,7 @@ static __inline__ int opt_unrec(struct s { u8 _optval, *op; - offset += skb->nh.raw - skb->data; + offset += skb_network_offset(skb); op = skb_header_pointer(skb, offset, sizeof(_optval), &_optval); if (op == NULL) return 1; @@ -221,7 +222,7 @@ static int icmpv6_push_pending_frames(st if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) goto out; - icmp6h = (struct icmp6hdr*) skb->h.raw; + icmp6h = icmp6_hdr(skb); memcpy(icmp6h, thdr, sizeof(struct icmp6hdr)); icmp6h->icmp6_cksum = 0; @@ -274,7 +275,7 @@ static int icmpv6_getfrag(void *from, ch #ifdef CONFIG_IPV6_MIP6 static void mip6_addr_swap(struct sk_buff *skb) { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); struct inet6_skb_parm *opt = IP6CB(skb); struct ipv6_destopt_hao *hao; struct in6_addr tmp; @@ -283,7 +284,8 @@ static void mip6_addr_swap(struct sk_buf if (opt->dsthao) { off = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO); if (likely(off >= 0)) { - hao = (struct ipv6_destopt_hao *)(skb->nh.raw + off); + hao = (struct ipv6_destopt_hao *) + (skb_network_header(skb) + off); ipv6_addr_copy(&tmp, &iph->saddr); ipv6_addr_copy(&iph->saddr, &hao->addr); ipv6_addr_copy(&hao->addr, &tmp); @@ -301,7 +303,7 @@ void icmpv6_send(struct sk_buff *skb, in struct net_device *dev) { struct inet6_dev *idev = NULL; - struct ipv6hdr *hdr = skb->nh.ipv6h; + struct ipv6hdr *hdr = ipv6_hdr(skb); struct sock *sk; struct ipv6_pinfo *np; struct in6_addr *saddr = NULL; @@ -315,7 +317,8 @@ void icmpv6_send(struct sk_buff *skb, in int hlimit, tclass; int err = 0; - if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail) + if ((u8 *)hdr < skb->head || + (skb->network_header + sizeof(*hdr)) > skb->tail) return; /* @@ -430,7 +433,7 @@ void icmpv6_send(struct sk_buff *skb, in tclass = 0; msg.skb = skb; - msg.offset = skb->nh.raw - skb->data; + msg.offset = skb_network_offset(skb); msg.type = type; len = skb->len - msg.offset; @@ -466,13 +469,15 @@ out: icmpv6_xmit_unlock(); } +EXPORT_SYMBOL(icmpv6_send); + static void icmpv6_echo_reply(struct sk_buff *skb) { struct sock *sk; struct inet6_dev *idev; struct ipv6_pinfo *np; struct in6_addr *saddr = NULL; - struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; + struct icmp6hdr *icmph = icmp6_hdr(skb); struct icmp6hdr tmp_hdr; struct flowi fl; struct icmpv6_msg msg; @@ -481,7 +486,7 @@ static void icmpv6_echo_reply(struct sk_ int hlimit; int tclass; - saddr = &skb->nh.ipv6h->daddr; + saddr = &ipv6_hdr(skb)->daddr; if (!ipv6_unicast_destination(skb)) saddr = NULL; @@ -491,7 +496,7 @@ static void icmpv6_echo_reply(struct sk_ memset(&fl, 0, sizeof(fl)); fl.proto = IPPROTO_ICMPV6; - ipv6_addr_copy(&fl.fl6_dst, &skb->nh.ipv6h->saddr); + ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr); if (saddr) ipv6_addr_copy(&fl.fl6_src, saddr); fl.oif = skb->dev->ifindex; @@ -579,8 +584,8 @@ static void icmpv6_notify(struct sk_buff if (!pskb_may_pull(skb, inner_offset+8)) return; - saddr = &skb->nh.ipv6h->saddr; - daddr = &skb->nh.ipv6h->daddr; + saddr = &ipv6_hdr(skb)->saddr; + daddr = &ipv6_hdr(skb)->daddr; /* BUGGG_FUTURE: we should try to parse exthdrs in this packet. Without this we will not able f.e. to make source routed @@ -624,8 +629,8 @@ static int icmpv6_rcv(struct sk_buff **p ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS); - saddr = &skb->nh.ipv6h->saddr; - daddr = &skb->nh.ipv6h->daddr; + saddr = &ipv6_hdr(skb)->saddr; + daddr = &ipv6_hdr(skb)->daddr; /* Perform checksum. */ switch (skb->ip_summed) { @@ -647,7 +652,7 @@ static int icmpv6_rcv(struct sk_buff **p if (!pskb_pull(skb, sizeof(struct icmp6hdr))) goto discard_it; - hdr = (struct icmp6hdr *) skb->h.raw; + hdr = icmp6_hdr(skb); type = hdr->icmp6_type; @@ -673,7 +678,7 @@ static int icmpv6_rcv(struct sk_buff **p */ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto discard_it; - hdr = (struct icmp6hdr *) skb->h.raw; + hdr = icmp6_hdr(skb); orig_hdr = (struct ipv6hdr *) (hdr + 1); rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev, ntohl(hdr->icmp6_mtu)); @@ -727,7 +732,8 @@ static int icmpv6_rcv(struct sk_buff **p */ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); - }; + } + kfree_skb(skb); return 0; @@ -860,11 +866,13 @@ int icmpv6_err_convert(int type, int cod case ICMPV6_TIME_EXCEED: *err = EHOSTUNREACH; break; - }; + } return fatal; } +EXPORT_SYMBOL(icmpv6_err_convert); + #ifdef CONFIG_SYSCTL ctl_table ipv6_icmp_table[] = { { diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 268f476..ca08ee8 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -359,7 +359,7 @@ end: return res; } -int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) +static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) { unsigned int h, s_h; unsigned int e = 0, s_e; @@ -1486,6 +1486,8 @@ void __init fib6_init(void) NULL, NULL); fib6_tables_init(); + + __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib); } void fib6_gc_cleanup(void) diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 61e7a6c..be0ee8a 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -96,12 +96,12 @@ int ipv6_rcv(struct sk_buff *skb, struct if (unlikely(!pskb_may_pull(skb, sizeof(*hdr)))) goto err; - hdr = skb->nh.ipv6h; + hdr = ipv6_hdr(skb); if (hdr->version != 6) goto err; - skb->h.raw = (u8 *)(hdr + 1); + skb->transport_header = skb->network_header + sizeof(*hdr); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); pkt_len = ntohs(hdr->payload_len); @@ -116,7 +116,7 @@ int ipv6_rcv(struct sk_buff *skb, struct IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS); goto drop; } - hdr = skb->nh.ipv6h; + hdr = ipv6_hdr(skb); } if (hdr->nexthdr == NEXTHDR_HOP) { @@ -160,10 +160,10 @@ static inline int ip6_input_finish(struc rcu_read_lock(); resubmit: idev = ip6_dst_idev(skb->dst); - if (!pskb_pull(skb, skb->h.raw - skb->data)) + if (!pskb_pull(skb, skb_transport_offset(skb))) goto discard; nhoff = IP6CB(skb)->nhoff; - nexthdr = skb->nh.raw[nhoff]; + nexthdr = skb_network_header(skb)[nhoff]; raw_sk = sk_head(&raw_v6_htable[nexthdr & (MAX_INET_PROTOS - 1)]); if (raw_sk && !ipv6_raw_deliver(skb, nexthdr)) @@ -181,9 +181,9 @@ resubmit: indefinitely. */ nf_reset(skb); - skb_postpull_rcsum(skb, skb->nh.raw, - skb->h.raw - skb->nh.raw); - hdr = skb->nh.ipv6h; + skb_postpull_rcsum(skb, skb_network_header(skb), + skb_network_header_len(skb)); + hdr = ipv6_hdr(skb); if (ipv6_addr_is_multicast(&hdr->daddr) && !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, &hdr->saddr) && @@ -234,7 +234,7 @@ int ip6_mc_input(struct sk_buff *skb) IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INMCASTPKTS); - hdr = skb->nh.ipv6h; + hdr = ipv6_hdr(skb); deliver = likely(!(skb->dev->flags & (IFF_PROMISC|IFF_ALLMULTI))) || ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 3055169..f508171 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -88,8 +88,8 @@ static inline int ip6_output_finish(stru /* dev_loopback_xmit for use with netfilter. */ static int ip6_dev_loopback_xmit(struct sk_buff *newskb) { - newskb->mac.raw = newskb->data; - __skb_pull(newskb, newskb->nh.raw - newskb->data); + skb_reset_mac_header(newskb); + __skb_pull(newskb, skb_network_offset(newskb)); newskb->pkt_type = PACKET_LOOPBACK; newskb->ip_summed = CHECKSUM_UNNECESSARY; BUG_TRAP(newskb->dst); @@ -107,13 +107,13 @@ static int ip6_output2(struct sk_buff *s skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; - if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) { + if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) { struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL; struct inet6_dev *idev = ip6_dst_idev(skb->dst); if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) && - ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr, - &skb->nh.ipv6h->saddr)) { + ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, + &ipv6_hdr(skb)->saddr)) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); /* Do not check for IFF_ALLMULTI; multicast routing @@ -124,7 +124,7 @@ static int ip6_output2(struct sk_buff *s newskb->dev, ip6_dev_loopback_xmit); - if (skb->nh.ipv6h->hop_limit == 0) { + if (ipv6_hdr(skb)->hop_limit == 0) { IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); return 0; @@ -137,9 +137,17 @@ static int ip6_output2(struct sk_buff *s return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish); } +static inline int ip6_skb_dst_mtu(struct sk_buff *skb) +{ + struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL; + + return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ? + skb->dst->dev->mtu : dst_mtu(skb->dst); +} + int ip6_output(struct sk_buff *skb) { - if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) || + if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || dst_allfrag(skb->dst)) return ip6_fragment(skb, ip6_output2); else @@ -191,7 +199,9 @@ int ip6_xmit(struct sock *sk, struct sk_ ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop); } - hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr)); + skb_push(skb, sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + hdr = ipv6_hdr(skb); /* * Fill in the IPv6 header @@ -239,6 +249,8 @@ int ip6_xmit(struct sock *sk, struct sk_ return -EMSGSIZE; } +EXPORT_SYMBOL(ip6_xmit); + /* * To avoid extra problems ND packets are send through this * routine. It's code duplication but I really want to avoid @@ -259,8 +271,9 @@ int ip6_nd_hdr(struct sock *sk, struct s totlen = len + sizeof(struct ipv6hdr); - hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr)); - skb->nh.ipv6h = hdr; + skb_reset_network_header(skb); + skb_put(skb, sizeof(struct ipv6hdr)); + hdr = ipv6_hdr(skb); *(__be32*)hdr = htonl(0x60000000); @@ -305,7 +318,7 @@ static int ip6_call_ra_chain(struct sk_b static int ip6_forward_proxy_check(struct sk_buff *skb) { - struct ipv6hdr *hdr = skb->nh.ipv6h; + struct ipv6hdr *hdr = ipv6_hdr(skb); u8 nexthdr = hdr->nexthdr; int offset; @@ -319,10 +332,11 @@ static int ip6_forward_proxy_check(struc if (nexthdr == IPPROTO_ICMPV6) { struct icmp6hdr *icmp6; - if (!pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) + if (!pskb_may_pull(skb, (skb_network_header(skb) + + offset + 1 - skb->data))) return 0; - icmp6 = (struct icmp6hdr *)(skb->nh.raw + offset); + icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); switch (icmp6->icmp6_type) { case NDISC_ROUTER_SOLICITATION: @@ -361,7 +375,7 @@ static inline int ip6_forward_finish(str int ip6_forward(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; - struct ipv6hdr *hdr = skb->nh.ipv6h; + struct ipv6hdr *hdr = ipv6_hdr(skb); struct inet6_skb_parm *opt = IP6CB(skb); if (ipv6_devconf.forwarding == 0) @@ -372,7 +386,7 @@ int ip6_forward(struct sk_buff *skb) goto drop; } - skb->ip_summed = CHECKSUM_NONE; + skb_forward_csum(skb); /* * We DO NOT make any processing on @@ -388,7 +402,7 @@ int ip6_forward(struct sk_buff *skb) * that different fragments will go along one path. --ANK */ if (opt->ra) { - u8 *ptr = skb->nh.raw + opt->ra; + u8 *ptr = skb_network_header(skb) + opt->ra; if (ip6_call_ra_chain(skb, (ptr[2]<<8) + ptr[3])) return 0; } @@ -470,7 +484,7 @@ int ip6_forward(struct sk_buff *skb) goto drop; } - hdr = skb->nh.ipv6h; + hdr = ipv6_hdr(skb); /* Mangling hops number delayed to point after skb COW */ @@ -499,33 +513,18 @@ static void ip6_copy_metadata(struct sk_ #ifdef CONFIG_NET_SCHED to->tc_index = from->tc_index; #endif -#ifdef CONFIG_NETFILTER - /* Connection association is same as pre-frag packet */ - nf_conntrack_put(to->nfct); - to->nfct = from->nfct; - nf_conntrack_get(to->nfct); - to->nfctinfo = from->nfctinfo; -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) - nf_conntrack_put_reasm(to->nfct_reasm); - to->nfct_reasm = from->nfct_reasm; - nf_conntrack_get_reasm(to->nfct_reasm); -#endif -#ifdef CONFIG_BRIDGE_NETFILTER - nf_bridge_put(to->nf_bridge); - to->nf_bridge = from->nf_bridge; - nf_bridge_get(to->nf_bridge); -#endif -#endif + nf_copy(to, from); skb_copy_secmark(to, from); } int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) { u16 offset = sizeof(struct ipv6hdr); - struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1); - unsigned int packet_len = skb->tail - skb->nh.raw; + struct ipv6_opt_hdr *exthdr = + (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); + unsigned int packet_len = skb->tail - skb->network_header; int found_rhdr = 0; - *nexthdr = &skb->nh.ipv6h->nexthdr; + *nexthdr = &ipv6_hdr(skb)->nexthdr; while (offset + 1 <= packet_len) { @@ -550,7 +549,8 @@ #endif offset += ipv6_optlen(exthdr); *nexthdr = &exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) + + offset); } return offset; @@ -574,7 +574,20 @@ static int ip6_fragment(struct sk_buff * hlen = ip6_find_1stfragopt(skb, &prevhdr); nexthdr = *prevhdr; - mtu = dst_mtu(&rt->u.dst); + mtu = ip6_skb_dst_mtu(skb); + + /* We must not fragment if the socket is set to force MTU discovery + * or if the skb it not generated by a local socket. (This last + * check should be redundant, but it's free.) + */ + if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) { + skb->dev = skb->dst->dev; + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); + IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS); + kfree_skb(skb); + return -EMSGSIZE; + } + if (np && np->frag_size < mtu) { if (np->frag_size) mtu = np->frag_size; @@ -616,7 +629,7 @@ static int ip6_fragment(struct sk_buff * /* BUILD HEADER */ *prevhdr = NEXTHDR_FRAGMENT; - tmp_hdr = kmemdup(skb->nh.raw, hlen, GFP_ATOMIC); + tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC); if (!tmp_hdr) { IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS); return -ENOMEM; @@ -624,8 +637,9 @@ static int ip6_fragment(struct sk_buff * __skb_pull(skb, hlen); fh = (struct frag_hdr*)__skb_push(skb, sizeof(struct frag_hdr)); - skb->nh.raw = __skb_push(skb, hlen); - memcpy(skb->nh.raw, tmp_hdr, hlen); + __skb_push(skb, hlen); + skb_reset_network_header(skb); + memcpy(skb_network_header(skb), tmp_hdr, hlen); ipv6_select_ident(skb, fh); fh->nexthdr = nexthdr; @@ -636,7 +650,8 @@ static int ip6_fragment(struct sk_buff * first_len = skb_pagelen(skb); skb->data_len = first_len - skb_headlen(skb); skb->len = first_len; - skb->nh.ipv6h->payload_len = htons(first_len - sizeof(struct ipv6hdr)); + ipv6_hdr(skb)->payload_len = htons(first_len - + sizeof(struct ipv6hdr)); dst_hold(&rt->u.dst); @@ -645,10 +660,12 @@ static int ip6_fragment(struct sk_buff * * before previous one went down. */ if (frag) { frag->ip_summed = CHECKSUM_NONE; - frag->h.raw = frag->data; + skb_reset_transport_header(frag); fh = (struct frag_hdr*)__skb_push(frag, sizeof(struct frag_hdr)); - frag->nh.raw = __skb_push(frag, hlen); - memcpy(frag->nh.raw, tmp_hdr, hlen); + __skb_push(frag, hlen); + skb_reset_network_header(frag); + memcpy(skb_network_header(frag), tmp_hdr, + hlen); offset += skb->len - hlen - sizeof(struct frag_hdr); fh->nexthdr = nexthdr; fh->reserved = 0; @@ -656,7 +673,9 @@ static int ip6_fragment(struct sk_buff * if (frag->next != NULL) fh->frag_off |= htons(IP6_MF); fh->identification = frag_id; - frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); + ipv6_hdr(frag)->payload_len = + htons(frag->len - + sizeof(struct ipv6hdr)); ip6_copy_metadata(frag, skb); } @@ -733,9 +752,10 @@ slow_path: ip6_copy_metadata(frag, skb); skb_reserve(frag, LL_RESERVED_SPACE(rt->u.dst.dev)); skb_put(frag, len + hlen + sizeof(struct frag_hdr)); - frag->nh.raw = frag->data; - fh = (struct frag_hdr*)(frag->data + hlen); - frag->h.raw = frag->data + hlen + sizeof(struct frag_hdr); + skb_reset_network_header(frag); + fh = (struct frag_hdr *)(skb_network_header(frag) + hlen); + frag->transport_header = (frag->network_header + hlen + + sizeof(struct frag_hdr)); /* * Charge the memory for the fragment to any owner @@ -747,7 +767,7 @@ slow_path: /* * Copy the packet header into the new buffer. */ - memcpy(frag->nh.raw, skb->data, hlen); + skb_copy_from_linear_data(skb, skb_network_header(frag), hlen); /* * Build fragment header. @@ -763,14 +783,15 @@ slow_path: /* * Copy a block of the IP datagram. */ - if (skb_copy_bits(skb, ptr, frag->h.raw, len)) + if (skb_copy_bits(skb, ptr, skb_transport_header(skb), len)) BUG(); left -= len; fh->frag_off = htons(offset); if (left > 0) fh->frag_off |= htons(IP6_MF); - frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); + ipv6_hdr(frag)->payload_len = htons(frag->len - + sizeof(struct ipv6hdr)); ptr += len; offset += len; @@ -861,6 +882,41 @@ static int ip6_dst_lookup_tail(struct so goto out_err_release; } +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + /* + * Here if the dst entry we've looked up + * has a neighbour entry that is in the INCOMPLETE + * state and the src address from the flow is + * marked as OPTIMISTIC, we release the found + * dst entry and replace it instead with the + * dst entry of the nexthop router + */ + if (!((*dst)->neighbour->nud_state & NUD_VALID)) { + struct inet6_ifaddr *ifp; + struct flowi fl_gw; + int redirect; + + ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1); + + redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC); + if (ifp) + in6_ifa_put(ifp); + + if (redirect) { + /* + * We need to get the dst entry for the + * default router instead + */ + dst_release(*dst); + memcpy(&fl_gw, fl, sizeof(struct flowi)); + memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr)); + *dst = ip6_route_output(sk, &fl_gw); + if ((err = (*dst)->error)) + goto out_err_release; + } + } +#endif + return 0; out_err_release: @@ -939,10 +995,10 @@ static inline int ip6_ufo_append_data(st skb_put(skb,fragheaderlen + transhdrlen); /* initialize network header pointer */ - skb->nh.raw = skb->data; + skb_reset_network_header(skb); /* initialize protocol header pointer */ - skb->h.raw = skb->data + fragheaderlen; + skb->transport_header = skb->network_header + fragheaderlen; skb->ip_summed = CHECKSUM_PARTIAL; skb->csum = 0; @@ -1015,7 +1071,8 @@ int ip6_append_data(struct sock *sk, int inet->cork.fl = *fl; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; - mtu = dst_mtu(rt->u.dst.path); + mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path); if (np->frag_size < mtu) { if (np->frag_size) mtu = np->frag_size; @@ -1162,10 +1219,10 @@ alloc_new_skb: * Find where to start putting bytes */ data = skb_put(skb, fraglen); - skb->nh.raw = data + exthdrlen; + skb_set_network_header(skb, exthdrlen); data += fragheaderlen; - skb->h.raw = data + exthdrlen; - + skb->transport_header = (skb->network_header + + fragheaderlen); if (fraggap) { skb->csum = skb_copy_and_csum_bits( skb_prev, maxfraglen, @@ -1288,10 +1345,10 @@ int ip6_push_pending_frames(struct sock tail_skb = &(skb_shinfo(skb)->frag_list); /* move skb->data to ip header from ext header */ - if (skb->data < skb->nh.raw) - __skb_pull(skb, skb->nh.raw - skb->data); + if (skb->data < skb_network_header(skb)) + __skb_pull(skb, skb_network_offset(skb)); while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { - __skb_pull(tmp_skb, skb->h.raw - skb->nh.raw); + __skb_pull(tmp_skb, skb_network_header_len(skb)); *tail_skb = tmp_skb; tail_skb = &(tmp_skb->next); skb->len += tmp_skb->len; @@ -1303,13 +1360,15 @@ int ip6_push_pending_frames(struct sock } ipv6_addr_copy(final_dst, &fl->fl6_dst); - __skb_pull(skb, skb->h.raw - skb->nh.raw); + __skb_pull(skb, skb_network_header_len(skb)); if (opt && opt->opt_flen) ipv6_push_frag_opts(skb, opt, &proto); if (opt && opt->opt_nflen) ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst); - skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr)); + skb_push(skb, sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + hdr = ipv6_hdr(skb); *(__be32*)hdr = fl->fl6_flowlabel | htonl(0x60000000 | ((int)np->cork.tclass << 20)); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 08d9442..a0902fb 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1,14 +1,15 @@ /* - * IPv6 over IPv6 tunnel device + * IPv6 tunneling device * Linux INET6 implementation * * Authors: * Ville Nuorvala + * Yasuyuki Kozakai * * $Id$ * * Based on: - * linux/net/ipv6/sit.c + * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c * * RFC 2473 * @@ -24,6 +25,7 @@ #include #include #include #include +#include #include #include #include @@ -41,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +54,7 @@ #include #include MODULE_AUTHOR("Ville Nuorvala"); -MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel"); +MODULE_DESCRIPTION("IPv6 tunneling device"); MODULE_LICENSE("GPL"); #define IPV6_TLV_TEL_DST_SIZE 8 @@ -63,6 +66,7 @@ #define IP6_TNL_TRACE(x...) do {;} while #endif #define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) +#define IPV6_TCLASS_SHIFT 20 #define HASH_SIZE 32 @@ -70,12 +74,12 @@ #define HASH(addr) ((__force u32)((addr) (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ (HASH_SIZE - 1)) -static int ip6ip6_fb_tnl_dev_init(struct net_device *dev); -static int ip6ip6_tnl_dev_init(struct net_device *dev); -static void ip6ip6_tnl_dev_setup(struct net_device *dev); +static int ip6_fb_tnl_dev_init(struct net_device *dev); +static int ip6_tnl_dev_init(struct net_device *dev); +static void ip6_tnl_dev_setup(struct net_device *dev); /* the IPv6 tunnel fallback device */ -static struct net_device *ip6ip6_fb_tnl_dev; +static struct net_device *ip6_fb_tnl_dev; /* lists for storing tunnels in use */ @@ -84,7 +88,7 @@ static struct ip6_tnl *tnls_wc[1]; static struct ip6_tnl **tnls[2] = { tnls_wc, tnls_r_l }; /* lock for the tunnel lists */ -static DEFINE_RWLOCK(ip6ip6_lock); +static DEFINE_RWLOCK(ip6_tnl_lock); static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) { @@ -115,7 +119,7 @@ static inline void ip6_tnl_dst_store(str } /** - * ip6ip6_tnl_lookup - fetch tunnel matching the end-point addresses + * ip6_tnl_lookup - fetch tunnel matching the end-point addresses * @remote: the address of the tunnel exit-point * @local: the address of the tunnel entry-point * @@ -126,7 +130,7 @@ static inline void ip6_tnl_dst_store(str **/ static struct ip6_tnl * -ip6ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local) +ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local) { unsigned h0 = HASH(remote); unsigned h1 = HASH(local); @@ -145,18 +149,18 @@ ip6ip6_tnl_lookup(struct in6_addr *remot } /** - * ip6ip6_bucket - get head of list matching given tunnel parameters + * ip6_tnl_bucket - get head of list matching given tunnel parameters * @p: parameters containing tunnel end-points * * Description: - * ip6ip6_bucket() returns the head of the list matching the + * ip6_tnl_bucket() returns the head of the list matching the * &struct in6_addr entries laddr and raddr in @p. * * Return: head of IPv6 tunnel list **/ static struct ip6_tnl ** -ip6ip6_bucket(struct ip6_tnl_parm *p) +ip6_tnl_bucket(struct ip6_tnl_parm *p) { struct in6_addr *remote = &p->raddr; struct in6_addr *local = &p->laddr; @@ -171,36 +175,36 @@ ip6ip6_bucket(struct ip6_tnl_parm *p) } /** - * ip6ip6_tnl_link - add tunnel to hash table + * ip6_tnl_link - add tunnel to hash table * @t: tunnel to be added **/ static void -ip6ip6_tnl_link(struct ip6_tnl *t) +ip6_tnl_link(struct ip6_tnl *t) { - struct ip6_tnl **tp = ip6ip6_bucket(&t->parms); + struct ip6_tnl **tp = ip6_tnl_bucket(&t->parms); t->next = *tp; - write_lock_bh(&ip6ip6_lock); + write_lock_bh(&ip6_tnl_lock); *tp = t; - write_unlock_bh(&ip6ip6_lock); + write_unlock_bh(&ip6_tnl_lock); } /** - * ip6ip6_tnl_unlink - remove tunnel from hash table + * ip6_tnl_unlink - remove tunnel from hash table * @t: tunnel to be removed **/ static void -ip6ip6_tnl_unlink(struct ip6_tnl *t) +ip6_tnl_unlink(struct ip6_tnl *t) { struct ip6_tnl **tp; - for (tp = ip6ip6_bucket(&t->parms); *tp; tp = &(*tp)->next) { + for (tp = ip6_tnl_bucket(&t->parms); *tp; tp = &(*tp)->next) { if (t == *tp) { - write_lock_bh(&ip6ip6_lock); + write_lock_bh(&ip6_tnl_lock); *tp = t->next; - write_unlock_bh(&ip6ip6_lock); + write_unlock_bh(&ip6_tnl_lock); break; } } @@ -237,12 +241,12 @@ static struct ip6_tnl *ip6_tnl_create(st if (i == IP6_TNL_MAX) goto failed; } - dev = alloc_netdev(sizeof (*t), name, ip6ip6_tnl_dev_setup); + dev = alloc_netdev(sizeof (*t), name, ip6_tnl_dev_setup); if (dev == NULL) goto failed; t = netdev_priv(dev); - dev->init = ip6ip6_tnl_dev_init; + dev->init = ip6_tnl_dev_init; t->parms = *p; if ((err = register_netdevice(dev)) < 0) { @@ -250,19 +254,19 @@ static struct ip6_tnl *ip6_tnl_create(st goto failed; } dev_hold(dev); - ip6ip6_tnl_link(t); + ip6_tnl_link(t); return t; failed: return NULL; } /** - * ip6ip6_tnl_locate - find or create tunnel matching given parameters + * ip6_tnl_locate - find or create tunnel matching given parameters * @p: tunnel parameters * @create: != 0 if allowed to create new tunnel if no match found * * Description: - * ip6ip6_tnl_locate() first tries to locate an existing tunnel + * ip6_tnl_locate() first tries to locate an existing tunnel * based on @parms. If this is unsuccessful, but @create is set a new * tunnel device is created and registered for use. * @@ -270,13 +274,13 @@ failed: * matching tunnel or NULL **/ -static struct ip6_tnl *ip6ip6_tnl_locate(struct ip6_tnl_parm *p, int create) +static struct ip6_tnl *ip6_tnl_locate(struct ip6_tnl_parm *p, int create) { struct in6_addr *remote = &p->raddr; struct in6_addr *local = &p->laddr; struct ip6_tnl *t; - for (t = *ip6ip6_bucket(p); t; t = t->next) { + for (t = *ip6_tnl_bucket(p); t; t = t->next) { if (ipv6_addr_equal(local, &t->parms.laddr) && ipv6_addr_equal(remote, &t->parms.raddr)) return t; @@ -287,24 +291,24 @@ static struct ip6_tnl *ip6ip6_tnl_locate } /** - * ip6ip6_tnl_dev_uninit - tunnel device uninitializer + * ip6_tnl_dev_uninit - tunnel device uninitializer * @dev: the device to be destroyed * * Description: - * ip6ip6_tnl_dev_uninit() removes tunnel from its list + * ip6_tnl_dev_uninit() removes tunnel from its list **/ static void -ip6ip6_tnl_dev_uninit(struct net_device *dev) +ip6_tnl_dev_uninit(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - if (dev == ip6ip6_fb_tnl_dev) { - write_lock_bh(&ip6ip6_lock); + if (dev == ip6_fb_tnl_dev) { + write_lock_bh(&ip6_tnl_lock); tnls_wc[0] = NULL; - write_unlock_bh(&ip6ip6_lock); + write_unlock_bh(&ip6_tnl_lock); } else { - ip6ip6_tnl_unlink(t); + ip6_tnl_unlink(t); } ip6_tnl_dst_reset(t); dev_put(dev); @@ -372,16 +376,16 @@ parse_tlv_tnl_enc_lim(struct sk_buff *sk } /** - * ip6ip6_err - tunnel error handler + * ip6_tnl_err - tunnel error handler * * Description: - * ip6ip6_err() should handle errors in the tunnel according + * ip6_tnl_err() should handle errors in the tunnel according * to the specifications in RFC 2473. **/ static int -ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - int type, int code, int offset, __be32 info) +ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, + int *type, int *code, int *msg, __be32 *info, int offset) { struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; struct ip6_tnl *t; @@ -396,13 +400,16 @@ ip6ip6_err(struct sk_buff *skb, struct i in trouble since we might need the source address for further processing of the error. */ - read_lock(&ip6ip6_lock); - if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL) + read_lock(&ip6_tnl_lock); + if ((t = ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL) + goto out; + + if (t->parms.proto != ipproto && t->parms.proto != 0) goto out; err = 0; - switch (type) { + switch (*type) { __u32 teli; struct ipv6_tlv_tnl_enc_lim *tel; __u32 mtu; @@ -414,7 +421,7 @@ ip6ip6_err(struct sk_buff *skb, struct i rel_msg = 1; break; case ICMPV6_TIME_EXCEED: - if (code == ICMPV6_EXC_HOPLIMIT) { + if ((*code) == ICMPV6_EXC_HOPLIMIT) { if (net_ratelimit()) printk(KERN_WARNING "%s: Too small hop limit or " @@ -425,10 +432,10 @@ ip6ip6_err(struct sk_buff *skb, struct i break; case ICMPV6_PARAMPROB: teli = 0; - if (code == ICMPV6_HDR_FIELD) + if ((*code) == ICMPV6_HDR_FIELD) teli = parse_tlv_tnl_enc_lim(skb, skb->data); - if (teli && teli == ntohl(info) - 2) { + if (teli && teli == ntohl(*info) - 2) { tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; if (tel->encap_limit == 0) { if (net_ratelimit()) @@ -445,7 +452,7 @@ ip6ip6_err(struct sk_buff *skb, struct i } break; case ICMPV6_PKT_TOOBIG: - mtu = ntohl(info) - offset; + mtu = ntohl(*info) - offset; if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; t->dev->mtu = mtu; @@ -458,20 +465,144 @@ ip6ip6_err(struct sk_buff *skb, struct i } break; } - if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { + + *type = rel_type; + *code = rel_code; + *info = rel_info; + *msg = rel_msg; + +out: + read_unlock(&ip6_tnl_lock); + return err; +} + +static int +ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) +{ + int rel_msg = 0; + int rel_type = type; + int rel_code = code; + __u32 rel_info = info; + int err; + struct sk_buff *skb2; + struct iphdr *eiph; + struct flowi fl; + struct rtable *rt; + + err = ip6_tnl_err(skb, IPPROTO_IPIP, opt, &rel_type, &rel_code, + &rel_msg, &rel_info, offset); + if (err < 0) + return err; + + if (rel_msg == 0) + return 0; + + switch (rel_type) { + case ICMPV6_DEST_UNREACH: + if (rel_code != ICMPV6_ADDR_UNREACH) + return 0; + rel_type = ICMP_DEST_UNREACH; + rel_code = ICMP_HOST_UNREACH; + break; + case ICMPV6_PKT_TOOBIG: + if (rel_code != 0) + return 0; + rel_type = ICMP_DEST_UNREACH; + rel_code = ICMP_FRAG_NEEDED; + break; + default: + return 0; + } + + if (!pskb_may_pull(skb, offset + sizeof(struct iphdr))) + return 0; + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (!skb2) + return 0; + + dst_release(skb2->dst); + skb2->dst = NULL; + skb_pull(skb2, offset); + skb_reset_network_header(skb2); + eiph = ip_hdr(skb2); + + /* Try to guess incoming interface */ + memset(&fl, 0, sizeof(fl)); + fl.fl4_dst = eiph->saddr; + fl.fl4_tos = RT_TOS(eiph->tos); + fl.proto = IPPROTO_IPIP; + if (ip_route_output_key(&rt, &fl)) + goto out; + + skb2->dev = rt->u.dst.dev; + + /* route "incoming" packet */ + if (rt->rt_flags & RTCF_LOCAL) { + ip_rt_put(rt); + rt = NULL; + fl.fl4_dst = eiph->daddr; + fl.fl4_src = eiph->saddr; + fl.fl4_tos = eiph->tos; + if (ip_route_output_key(&rt, &fl) || + rt->u.dst.dev->type != ARPHRD_TUNNEL) { + ip_rt_put(rt); + goto out; + } + } else { + ip_rt_put(rt); + if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, + skb2->dev) || + skb2->dst->dev->type != ARPHRD_TUNNEL) + goto out; + } + + /* change mtu on this route */ + if (rel_type == ICMP_DEST_UNREACH && rel_code == ICMP_FRAG_NEEDED) { + if (rel_info > dst_mtu(skb2->dst)) + goto out; + + skb2->dst->ops->update_pmtu(skb2->dst, rel_info); + rel_info = htonl(rel_info); + } + + icmp_send(skb2, rel_type, rel_code, rel_info); + +out: + kfree_skb(skb2); + return 0; +} + +static int +ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) +{ + int rel_msg = 0; + int rel_type = type; + int rel_code = code; + __u32 rel_info = info; + int err; + + err = ip6_tnl_err(skb, IPPROTO_IPV6, opt, &rel_type, &rel_code, + &rel_msg, &rel_info, offset); + if (err < 0) + return err; + + if (rel_msg && pskb_may_pull(skb, offset + sizeof(struct ipv6hdr))) { struct rt6_info *rt; struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (!skb2) - goto out; + return 0; dst_release(skb2->dst); skb2->dst = NULL; skb_pull(skb2, offset); - skb2->nh.raw = skb2->data; + skb_reset_network_header(skb2); /* Try to guess incoming interface */ - rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0); + rt = rt6_lookup(&ipv6_hdr(skb2)->saddr, NULL, 0, 0); if (rt && rt->rt6i_dev) skb2->dev = rt->rt6i_dev; @@ -483,19 +614,34 @@ ip6ip6_err(struct sk_buff *skb, struct i kfree_skb(skb2); } -out: - read_unlock(&ip6ip6_lock); - return err; + + return 0; } -static inline void ip6ip6_ecn_decapsulate(struct ipv6hdr *outer_iph, - struct sk_buff *skb) +static void ip4ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, + struct ipv6hdr *ipv6h, + struct sk_buff *skb) { - struct ipv6hdr *inner_iph = skb->nh.ipv6h; + __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK; - if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) - IP6_ECN_set_ce(inner_iph); + if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) + ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield); + + if (INET_ECN_is_ce(dsfield)) + IP_ECN_set_ce(ip_hdr(skb)); +} + +static void ip6ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, + struct ipv6hdr *ipv6h, + struct sk_buff *skb) +{ + if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) + ipv6_copy_dscp(ipv6h, ipv6_hdr(skb)); + + if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h))) + IP6_ECN_set_ce(ipv6_hdr(skb)); } + static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t) { struct ip6_tnl_parm *p = &t->parms; @@ -519,53 +665,61 @@ static inline int ip6_tnl_rcv_ctl(struct } /** - * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally + * ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally * @skb: received socket buffer + * @protocol: ethernet protocol ID + * @dscp_ecn_decapsulate: the function to decapsulate DSCP code and ECN * * Return: 0 **/ -static int -ip6ip6_rcv(struct sk_buff *skb) +static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, + __u8 ipproto, + void (*dscp_ecn_decapsulate)(struct ip6_tnl *t, + struct ipv6hdr *ipv6h, + struct sk_buff *skb)) { - struct ipv6hdr *ipv6h; struct ip6_tnl *t; + struct ipv6hdr *ipv6h = ipv6_hdr(skb); - ipv6h = skb->nh.ipv6h; + read_lock(&ip6_tnl_lock); - read_lock(&ip6ip6_lock); + if ((t = ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { + if (t->parms.proto != ipproto && t->parms.proto != 0) { + read_unlock(&ip6_tnl_lock); + goto discard; + } - if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { - read_unlock(&ip6ip6_lock); + read_unlock(&ip6_tnl_lock); goto discard; } if (!ip6_tnl_rcv_ctl(t)) { t->stat.rx_dropped++; - read_unlock(&ip6ip6_lock); + read_unlock(&ip6_tnl_lock); goto discard; } secpath_reset(skb); - skb->mac.raw = skb->nh.raw; - skb->nh.raw = skb->data; - skb->protocol = htons(ETH_P_IPV6); + skb->mac_header = skb->network_header; + skb_reset_network_header(skb); + skb->protocol = htons(protocol); skb->pkt_type = PACKET_HOST; memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); skb->dev = t->dev; dst_release(skb->dst); skb->dst = NULL; nf_reset(skb); - if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) - ipv6_copy_dscp(ipv6h, skb->nh.ipv6h); - ip6ip6_ecn_decapsulate(ipv6h, skb); + + dscp_ecn_decapsulate(t, ipv6h, skb); + t->stat.rx_packets++; t->stat.rx_bytes += skb->len; netif_rx(skb); - read_unlock(&ip6ip6_lock); + read_unlock(&ip6_tnl_lock); return 0; } - read_unlock(&ip6ip6_lock); + read_unlock(&ip6_tnl_lock); return 1; discard: @@ -573,6 +727,18 @@ discard: return 0; } +static int ip4ip6_rcv(struct sk_buff *skb) +{ + return ip6_tnl_rcv(skb, ETH_P_IP, IPPROTO_IPIP, + ip4ip6_dscp_ecn_decapsulate); +} + +static int ip6ip6_rcv(struct sk_buff *skb) +{ + return ip6_tnl_rcv(skb, ETH_P_IPV6, IPPROTO_IPV6, + ip6ip6_dscp_ecn_decapsulate); +} + struct ipv6_tel_txoption { struct ipv6_txoptions ops; __u8 dst_opt[8]; @@ -593,7 +759,7 @@ static void init_tel_txopt(struct ipv6_t } /** - * ip6ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own * @t: the outgoing tunnel device * @hdr: IPv6 header from the incoming packet * @@ -607,7 +773,7 @@ static void init_tel_txopt(struct ipv6_t **/ static inline int -ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) +ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) { return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); } @@ -641,72 +807,49 @@ static inline int ip6_tnl_xmit_ctl(struc return ret; } /** - * ip6ip6_tnl_xmit - encapsulate packet and send + * ip6_tnl_xmit2 - encapsulate packet and send * @skb: the outgoing socket buffer * @dev: the outgoing tunnel device + * @dsfield: dscp code for outer header + * @fl: flow of tunneled packet + * @encap_limit: encapsulation limit + * @pmtu: Path MTU is stored if packet is too big * * Description: * Build new header and do some sanity checks on the packet before sending * it. * * Return: - * 0 + * 0 on success + * -1 fail + * %-EMSGSIZE message too big. return mtu in this case. **/ -static int -ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) +static int ip6_tnl_xmit2(struct sk_buff *skb, + struct net_device *dev, + __u8 dsfield, + struct flowi *fl, + int encap_limit, + __u32 *pmtu) { struct ip6_tnl *t = netdev_priv(dev); struct net_device_stats *stats = &t->stat; - struct ipv6hdr *ipv6h = skb->nh.ipv6h; - int encap_limit = -1; + struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct ipv6_tel_txoption opt; - __u16 offset; - struct flowi fl; struct dst_entry *dst; struct net_device *tdev; int mtu; int max_headroom = sizeof(struct ipv6hdr); u8 proto; - int err; + int err = -1; int pkt_len; - int dsfield; - - if (t->recursion++) { - stats->collisions++; - goto tx_err; - } - if (skb->protocol != htons(ETH_P_IPV6) || - !ip6_tnl_xmit_ctl(t) || ip6ip6_tnl_addr_conflict(t, ipv6h)) - goto tx_err; - - if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) { - struct ipv6_tlv_tnl_enc_lim *tel; - tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset]; - if (tel->encap_limit == 0) { - icmpv6_send(skb, ICMPV6_PARAMPROB, - ICMPV6_HDR_FIELD, offset + 2, skb->dev); - goto tx_err; - } - encap_limit = tel->encap_limit - 1; - } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) - encap_limit = t->parms.encap_limit; - - memcpy(&fl, &t->fl, sizeof (fl)); - proto = fl.proto; - - dsfield = ipv6_get_dsfield(ipv6h); - if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) - fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK); - if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) - fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); if ((dst = ip6_tnl_dst_check(t)) != NULL) dst_hold(dst); else { - dst = ip6_route_output(NULL, &fl); + dst = ip6_route_output(NULL, fl); - if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0) < 0) + if (dst->error || xfrm_lookup(&dst, fl, NULL, 0) < 0) goto tx_err_link_failure; } @@ -730,7 +873,8 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, str if (skb->dst) skb->dst->ops->update_pmtu(skb->dst, mtu); if (skb->len > mtu) { - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); + *pmtu = mtu; + err = -EMSGSIZE; goto tx_err_dst_release; } @@ -754,22 +898,24 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, str dst_release(skb->dst); skb->dst = dst_clone(dst); - skb->h.raw = skb->nh.raw; + skb->transport_header = skb->network_header; + proto = fl->proto; if (encap_limit >= 0) { init_tel_txopt(&opt, encap_limit); ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL); } - skb->nh.raw = skb_push(skb, sizeof(struct ipv6hdr)); - ipv6h = skb->nh.ipv6h; - *(__be32*)ipv6h = fl.fl6_flowlabel | htonl(0x60000000); + skb_push(skb, sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + ipv6h = ipv6_hdr(skb); + *(__be32*)ipv6h = fl->fl6_flowlabel | htonl(0x60000000); dsfield = INET_ECN_encapsulate(0, dsfield); ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield); ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); ipv6h->hop_limit = t->parms.hop_limit; ipv6h->nexthdr = proto; - ipv6_addr_copy(&ipv6h->saddr, &fl.fl6_src); - ipv6_addr_copy(&ipv6h->daddr, &fl.fl6_dst); + ipv6_addr_copy(&ipv6h->saddr, &fl->fl6_src); + ipv6_addr_copy(&ipv6h->daddr, &fl->fl6_dst); nf_reset(skb); pkt_len = skb->len; err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, @@ -783,13 +929,131 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, str stats->tx_aborted_errors++; } ip6_tnl_dst_store(t, dst); - t->recursion--; return 0; tx_err_link_failure: stats->tx_carrier_errors++; dst_link_failure(skb); tx_err_dst_release: dst_release(dst); + return err; +} + +static inline int +ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct iphdr *iph = ip_hdr(skb); + int encap_limit = -1; + struct flowi fl; + __u8 dsfield; + __u32 mtu; + int err; + + if ((t->parms.proto != IPPROTO_IPIP && t->parms.proto != 0) || + !ip6_tnl_xmit_ctl(t)) + return -1; + + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) + encap_limit = t->parms.encap_limit; + + memcpy(&fl, &t->fl, sizeof (fl)); + fl.proto = IPPROTO_IPIP; + + dsfield = ipv4_get_dsfield(iph); + + if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) + fl.fl6_flowlabel |= ntohl(((__u32)iph->tos << IPV6_TCLASS_SHIFT) + & IPV6_TCLASS_MASK); + + err = ip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu); + if (err != 0) { + /* XXX: send ICMP error even if DF is not set. */ + if (err == -EMSGSIZE) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(mtu)); + return -1; + } + + return 0; +} + +static inline int +ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + int encap_limit = -1; + __u16 offset; + struct flowi fl; + __u8 dsfield; + __u32 mtu; + int err; + + if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || + !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h)) + return -1; + + offset = parse_tlv_tnl_enc_lim(skb, skb_network_header(skb)); + if (offset > 0) { + struct ipv6_tlv_tnl_enc_lim *tel; + tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset]; + if (tel->encap_limit == 0) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, offset + 2, skb->dev); + return -1; + } + encap_limit = tel->encap_limit - 1; + } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) + encap_limit = t->parms.encap_limit; + + memcpy(&fl, &t->fl, sizeof (fl)); + fl.proto = IPPROTO_IPV6; + + dsfield = ipv6_get_dsfield(ipv6h); + if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) + fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK); + if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) + fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); + + err = ip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu); + if (err != 0) { + if (err == -EMSGSIZE) + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); + return -1; + } + + return 0; +} + +static int +ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct net_device_stats *stats = &t->stat; + int ret; + + if (t->recursion++) { + t->stat.collisions++; + goto tx_err; + } + + switch (skb->protocol) { + case __constant_htons(ETH_P_IP): + ret = ip4ip6_tnl_xmit(skb, dev); + break; + case __constant_htons(ETH_P_IPV6): + ret = ip6ip6_tnl_xmit(skb, dev); + break; + default: + goto tx_err; + } + + if (ret < 0) + goto tx_err; + + t->recursion--; + return 0; + tx_err: stats->tx_errors++; stats->tx_dropped++; @@ -817,7 +1081,7 @@ static void ip6_tnl_set_cap(struct ip6_t } } -static void ip6ip6_tnl_link_config(struct ip6_tnl *t) +static void ip6_tnl_link_config(struct ip6_tnl *t) { struct net_device *dev = t->dev; struct ip6_tnl_parm *p = &t->parms; @@ -870,17 +1134,17 @@ static void ip6ip6_tnl_link_config(struc } /** - * ip6ip6_tnl_change - update the tunnel parameters + * ip6_tnl_change - update the tunnel parameters * @t: tunnel to be changed * @p: tunnel configuration parameters * @active: != 0 if tunnel is ready for use * * Description: - * ip6ip6_tnl_change() updates the tunnel parameters + * ip6_tnl_change() updates the tunnel parameters **/ static int -ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) +ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) { ipv6_addr_copy(&t->parms.laddr, &p->laddr); ipv6_addr_copy(&t->parms.raddr, &p->raddr); @@ -889,19 +1153,20 @@ ip6ip6_tnl_change(struct ip6_tnl *t, str t->parms.encap_limit = p->encap_limit; t->parms.flowinfo = p->flowinfo; t->parms.link = p->link; + t->parms.proto = p->proto; ip6_tnl_dst_reset(t); - ip6ip6_tnl_link_config(t); + ip6_tnl_link_config(t); return 0; } /** - * ip6ip6_tnl_ioctl - configure ipv6 tunnels from userspace + * ip6_tnl_ioctl - configure ipv6 tunnels from userspace * @dev: virtual device associated with tunnel * @ifr: parameters passed from userspace * @cmd: command to be performed * * Description: - * ip6ip6_tnl_ioctl() is used for managing IPv6 tunnels + * ip6_tnl_ioctl() is used for managing IPv6 tunnels * from userspace. * * The possible commands are the following: @@ -923,7 +1188,7 @@ ip6ip6_tnl_change(struct ip6_tnl *t, str **/ static int -ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int err = 0; struct ip6_tnl_parm p; @@ -931,12 +1196,12 @@ ip6ip6_tnl_ioctl(struct net_device *dev, switch (cmd) { case SIOCGETTUNNEL: - if (dev == ip6ip6_fb_tnl_dev) { + if (dev == ip6_fb_tnl_dev) { if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { err = -EFAULT; break; } - t = ip6ip6_tnl_locate(&p, 0); + t = ip6_tnl_locate(&p, 0); } if (t == NULL) t = netdev_priv(dev); @@ -954,10 +1219,11 @@ ip6ip6_tnl_ioctl(struct net_device *dev, if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) break; err = -EINVAL; - if (p.proto != IPPROTO_IPV6) + if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP && + p.proto != 0) break; - t = ip6ip6_tnl_locate(&p, cmd == SIOCADDTUNNEL); - if (dev != ip6ip6_fb_tnl_dev && cmd == SIOCCHGTUNNEL) { + t = ip6_tnl_locate(&p, cmd == SIOCADDTUNNEL); + if (dev != ip6_fb_tnl_dev && cmd == SIOCCHGTUNNEL) { if (t != NULL) { if (t->dev != dev) { err = -EEXIST; @@ -966,9 +1232,9 @@ ip6ip6_tnl_ioctl(struct net_device *dev, } else t = netdev_priv(dev); - ip6ip6_tnl_unlink(t); - err = ip6ip6_tnl_change(t, &p); - ip6ip6_tnl_link(t); + ip6_tnl_unlink(t); + err = ip6_tnl_change(t, &p); + ip6_tnl_link(t); netdev_state_change(dev); } if (t) { @@ -984,15 +1250,15 @@ ip6ip6_tnl_ioctl(struct net_device *dev, if (!capable(CAP_NET_ADMIN)) break; - if (dev == ip6ip6_fb_tnl_dev) { + if (dev == ip6_fb_tnl_dev) { err = -EFAULT; if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) break; err = -ENOENT; - if ((t = ip6ip6_tnl_locate(&p, 0)) == NULL) + if ((t = ip6_tnl_locate(&p, 0)) == NULL) break; err = -EPERM; - if (t->dev == ip6ip6_fb_tnl_dev) + if (t->dev == ip6_fb_tnl_dev) break; dev = t->dev; } @@ -1006,20 +1272,20 @@ ip6ip6_tnl_ioctl(struct net_device *dev, } /** - * ip6ip6_tnl_get_stats - return the stats for tunnel device + * ip6_tnl_get_stats - return the stats for tunnel device * @dev: virtual device associated with tunnel * * Return: stats for device **/ static struct net_device_stats * -ip6ip6_tnl_get_stats(struct net_device *dev) +ip6_tnl_get_stats(struct net_device *dev) { return &(((struct ip6_tnl *)netdev_priv(dev))->stat); } /** - * ip6ip6_tnl_change_mtu - change mtu manually for tunnel device + * ip6_tnl_change_mtu - change mtu manually for tunnel device * @dev: virtual device associated with tunnel * @new_mtu: the new mtu * @@ -1029,7 +1295,7 @@ ip6ip6_tnl_get_stats(struct net_device * **/ static int -ip6ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) +ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) { if (new_mtu < IPV6_MIN_MTU) { return -EINVAL; @@ -1039,22 +1305,22 @@ ip6ip6_tnl_change_mtu(struct net_device } /** - * ip6ip6_tnl_dev_setup - setup virtual tunnel device + * ip6_tnl_dev_setup - setup virtual tunnel device * @dev: virtual device associated with tunnel * * Description: * Initialize function pointers and device parameters **/ -static void ip6ip6_tnl_dev_setup(struct net_device *dev) +static void ip6_tnl_dev_setup(struct net_device *dev) { SET_MODULE_OWNER(dev); - dev->uninit = ip6ip6_tnl_dev_uninit; + dev->uninit = ip6_tnl_dev_uninit; dev->destructor = free_netdev; - dev->hard_start_xmit = ip6ip6_tnl_xmit; - dev->get_stats = ip6ip6_tnl_get_stats; - dev->do_ioctl = ip6ip6_tnl_ioctl; - dev->change_mtu = ip6ip6_tnl_change_mtu; + dev->hard_start_xmit = ip6_tnl_xmit; + dev->get_stats = ip6_tnl_get_stats; + dev->do_ioctl = ip6_tnl_ioctl; + dev->change_mtu = ip6_tnl_change_mtu; dev->type = ARPHRD_TUNNEL6; dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr); @@ -1065,50 +1331,56 @@ static void ip6ip6_tnl_dev_setup(struct /** - * ip6ip6_tnl_dev_init_gen - general initializer for all tunnel devices + * ip6_tnl_dev_init_gen - general initializer for all tunnel devices * @dev: virtual device associated with tunnel **/ static inline void -ip6ip6_tnl_dev_init_gen(struct net_device *dev) +ip6_tnl_dev_init_gen(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - t->fl.proto = IPPROTO_IPV6; t->dev = dev; strcpy(t->parms.name, dev->name); } /** - * ip6ip6_tnl_dev_init - initializer for all non fallback tunnel devices + * ip6_tnl_dev_init - initializer for all non fallback tunnel devices * @dev: virtual device associated with tunnel **/ static int -ip6ip6_tnl_dev_init(struct net_device *dev) +ip6_tnl_dev_init(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - ip6ip6_tnl_dev_init_gen(dev); - ip6ip6_tnl_link_config(t); + ip6_tnl_dev_init_gen(dev); + ip6_tnl_link_config(t); return 0; } /** - * ip6ip6_fb_tnl_dev_init - initializer for fallback tunnel device + * ip6_fb_tnl_dev_init - initializer for fallback tunnel device * @dev: fallback device * * Return: 0 **/ static int -ip6ip6_fb_tnl_dev_init(struct net_device *dev) +ip6_fb_tnl_dev_init(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - ip6ip6_tnl_dev_init_gen(dev); + ip6_tnl_dev_init_gen(dev); + t->parms.proto = IPPROTO_IPV6; dev_hold(dev); tnls_wc[0] = t; return 0; } +static struct xfrm6_tunnel ip4ip6_handler = { + .handler = ip4ip6_rcv, + .err_handler = ip4ip6_err, + .priority = 1, +}; + static struct xfrm6_tunnel ip6ip6_handler = { .handler = ip6ip6_rcv, .err_handler = ip6ip6_err, @@ -1125,30 +1397,40 @@ static int __init ip6_tunnel_init(void) { int err; + if (xfrm6_tunnel_register(&ip4ip6_handler, AF_INET)) { + printk(KERN_ERR "ip6_tunnel init: can't register ip4ip6\n"); + err = -EAGAIN; + goto out; + } + if (xfrm6_tunnel_register(&ip6ip6_handler, AF_INET6)) { - printk(KERN_ERR "ip6ip6 init: can't register tunnel\n"); - return -EAGAIN; + printk(KERN_ERR "ip6_tunnel init: can't register ip6ip6\n"); + err = -EAGAIN; + goto unreg_ip4ip6; } - ip6ip6_fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0", - ip6ip6_tnl_dev_setup); + ip6_fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0", + ip6_tnl_dev_setup); - if (!ip6ip6_fb_tnl_dev) { + if (!ip6_fb_tnl_dev) { err = -ENOMEM; goto fail; } - ip6ip6_fb_tnl_dev->init = ip6ip6_fb_tnl_dev_init; + ip6_fb_tnl_dev->init = ip6_fb_tnl_dev_init; - if ((err = register_netdev(ip6ip6_fb_tnl_dev))) { - free_netdev(ip6ip6_fb_tnl_dev); + if ((err = register_netdev(ip6_fb_tnl_dev))) { + free_netdev(ip6_fb_tnl_dev); goto fail; } return 0; fail: xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6); +unreg_ip4ip6: + xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET); +out: return err; } -static void __exit ip6ip6_destroy_tunnels(void) +static void __exit ip6_tnl_destroy_tunnels(void) { int h; struct ip6_tnl *t; @@ -1168,11 +1450,14 @@ static void __exit ip6ip6_destroy_tunnel static void __exit ip6_tunnel_cleanup(void) { + if (xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET)) + printk(KERN_INFO "ip6_tunnel close: can't deregister ip4ip6\n"); + if (xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6)) - printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n"); + printk(KERN_INFO "ip6_tunnel close: can't deregister ip6ip6\n"); rtnl_lock(); - ip6ip6_destroy_tunnels(); + ip6_tnl_destroy_tunnels(); rtnl_unlock(); } diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 5724ba9..1ee50b5 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -79,9 +79,9 @@ static int ipcomp6_input(struct xfrm_sta skb->ip_summed = CHECKSUM_NONE; /* Remove ipcomp header and decompress original payload */ - iph = skb->nh.ipv6h; + iph = ipv6_hdr(skb); ipch = (void *)skb->data; - skb->h.raw = skb->nh.raw + sizeof(*ipch); + skb->transport_header = skb->network_header + sizeof(*ipch); __skb_pull(skb, sizeof(*ipch)); /* decompression */ @@ -111,7 +111,7 @@ static int ipcomp6_input(struct xfrm_sta skb->truesize += dlen - plen; __skb_put(skb, dlen - plen); - memcpy(skb->data, scratch, dlen); + skb_copy_to_linear_data(skb, scratch, dlen); err = ipch->nexthdr; out_put_cpu: @@ -124,15 +124,13 @@ static int ipcomp6_output(struct xfrm_st { int err; struct ipv6hdr *top_iph; - int hdr_len; struct ipv6_comp_hdr *ipch; struct ipcomp_data *ipcd = x->data; int plen, dlen; u8 *start, *scratch; struct crypto_comp *tfm; int cpu; - - hdr_len = skb->h.raw - skb->data; + int hdr_len = skb_transport_offset(skb); /* check whether datagram len is larger than threshold */ if ((skb->len - hdr_len) < ipcd->threshold) { @@ -145,7 +143,7 @@ static int ipcomp6_output(struct xfrm_st /* compression */ plen = skb->len - hdr_len; dlen = IPCOMP_SCRATCH_SIZE; - start = skb->h.raw; + start = skb_transport_header(skb); cpu = get_cpu(); scratch = *per_cpu_ptr(ipcomp6_scratches, cpu); @@ -166,10 +164,10 @@ static int ipcomp6_output(struct xfrm_st top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); ipch = (struct ipv6_comp_hdr *)start; - ipch->nexthdr = *skb->nh.raw; + ipch->nexthdr = *skb_network_header(skb); ipch->flags = 0; ipch->cpi = htons((u16 )ntohl(x->id.spi)); - *skb->nh.raw = IPPROTO_COMP; + *skb_network_header(skb) = IPPROTO_COMP; out_ok: return 0; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index f5f9582..aa3d07c 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -101,14 +101,14 @@ static int ipv6_gso_send_check(struct sk if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) goto out; - ipv6h = skb->nh.ipv6h; + ipv6h = ipv6_hdr(skb); __skb_pull(skb, sizeof(*ipv6h)); err = -EPROTONOSUPPORT; rcu_read_lock(); ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); if (likely(ops && ops->gso_send_check)) { - skb->h.raw = skb->data; + skb_reset_transport_header(skb); err = ops->gso_send_check(skb); } rcu_read_unlock(); @@ -137,14 +137,14 @@ static struct sk_buff *ipv6_gso_segment( if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) goto out; - ipv6h = skb->nh.ipv6h; + ipv6h = ipv6_hdr(skb); __skb_pull(skb, sizeof(*ipv6h)); segs = ERR_PTR(-EPROTONOSUPPORT); rcu_read_lock(); ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); if (likely(ops && ops->gso_segment)) { - skb->h.raw = skb->data; + skb_reset_transport_header(skb); segs = ops->gso_segment(skb, features); } rcu_read_unlock(); @@ -153,7 +153,7 @@ static struct sk_buff *ipv6_gso_segment( goto out; for (skb = segs; skb; skb = skb->next) { - ipv6h = skb->nh.ipv6h; + ipv6h = ipv6_hdr(skb); ipv6h->payload_len = htons(skb->len - skb->mac_len - sizeof(*ipv6h)); } @@ -694,7 +694,7 @@ done: retv = ip6_ra_control(sk, val, NULL); break; case IPV6_MTU_DISCOVER: - if (val<0 || val>2) + if (val<0 || val>3) goto e_inval; np->pmtudisc = val; retv = 0; @@ -761,6 +761,7 @@ #endif return err; } +EXPORT_SYMBOL(ipv6_setsockopt); #ifdef CONFIG_COMPAT int compat_ipv6_setsockopt(struct sock *sk, int level, int optname, @@ -796,18 +797,37 @@ EXPORT_SYMBOL(compat_ipv6_setsockopt); #endif static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt, - char __user *optval, int len) + int optname, char __user *optval, int len) { struct ipv6_opt_hdr *hdr; - if (!opt || !opt->hopopt) + if (!opt) + return 0; + + switch(optname) { + case IPV6_HOPOPTS: + hdr = opt->hopopt; + break; + case IPV6_RTHDRDSTOPTS: + hdr = opt->dst0opt; + break; + case IPV6_RTHDR: + hdr = (struct ipv6_opt_hdr *)opt->srcrt; + break; + case IPV6_DSTOPTS: + hdr = opt->dst1opt; + break; + default: + return -EINVAL; /* should not happen */ + } + + if (!hdr) return 0; - hdr = opt->hopopt; len = min_t(unsigned int, len, ipv6_optlen(hdr)); - if (copy_to_user(optval, hdr, ipv6_optlen(hdr))) + if (copy_to_user(optval, hdr, len)); return -EFAULT; - return len; + return ipv6_optlen(hdr); } static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, @@ -945,7 +965,7 @@ static int do_ipv6_getsockopt(struct soc lock_sock(sk); len = ipv6_getsockopt_sticky(sk, np->opt, - optval, len); + optname, optval, len); release_sock(sk); return put_user(len, optlen); } @@ -1066,6 +1086,8 @@ #endif return err; } +EXPORT_SYMBOL(ipv6_getsockopt); + #ifdef CONFIG_COMPAT int compat_ipv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) diff --git a/net/ipv6/ipv6_syms.c b/net/ipv6/ipv6_syms.c deleted file mode 100644 index e12e3d4..0000000 --- a/net/ipv6/ipv6_syms.c +++ /dev/null @@ -1,36 +0,0 @@ - -#include -#include -#include -#include -#include -#include - -EXPORT_SYMBOL(icmpv6_send); -EXPORT_SYMBOL(icmpv6_statistics); -EXPORT_SYMBOL(icmpv6_err_convert); -EXPORT_SYMBOL(ndisc_mc_map); -EXPORT_SYMBOL(register_inet6addr_notifier); -EXPORT_SYMBOL(unregister_inet6addr_notifier); -EXPORT_SYMBOL(ip6_route_output); -EXPORT_SYMBOL(ipv6_setsockopt); -EXPORT_SYMBOL(ipv6_getsockopt); -EXPORT_SYMBOL(inet6_register_protosw); -EXPORT_SYMBOL(inet6_unregister_protosw); -EXPORT_SYMBOL(inet6_add_protocol); -EXPORT_SYMBOL(inet6_del_protocol); -EXPORT_SYMBOL(ip6_xmit); -EXPORT_SYMBOL(inet6_release); -EXPORT_SYMBOL(inet6_bind); -EXPORT_SYMBOL(inet6_getname); -EXPORT_SYMBOL(inet6_ioctl); -EXPORT_SYMBOL(ipv6_get_saddr); -EXPORT_SYMBOL(ipv6_chk_addr); -EXPORT_SYMBOL(in6_dev_finish_destroy); -#ifdef CONFIG_XFRM -EXPORT_SYMBOL(xfrm6_rcv); -EXPORT_SYMBOL(xfrm6_input_addr); -EXPORT_SYMBOL(xfrm6_find_1stfragopt); -#endif -EXPORT_SYMBOL(rt6_lookup); -EXPORT_SYMBOL(ipv6_push_nfrag_opts); diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index a8d6625..6c27589 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -988,7 +988,7 @@ int ipv6_is_mld(struct sk_buff *skb, int if (!pskb_may_pull(skb, sizeof(struct icmp6hdr))) return 0; - pic = (struct icmp6hdr *)skb->h.raw; + pic = icmp6_hdr(skb); switch (pic->icmp6_type) { case ICMPV6_MGM_QUERY: @@ -1167,11 +1167,11 @@ int igmp6_event_query(struct sk_buff *sk return -EINVAL; /* compute payload length excluding extension headers */ - len = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); - len -= (char *)skb->h.raw - (char *)skb->nh.ipv6h; + len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr); + len -= skb_network_header_len(skb); /* Drop queries with not link local source */ - if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL)) + if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) return -EINVAL; idev = in6_dev_get(skb->dev); @@ -1179,7 +1179,7 @@ int igmp6_event_query(struct sk_buff *sk if (idev == NULL) return 0; - hdr = (struct icmp6hdr *) skb->h.raw; + hdr = icmp6_hdr(skb); group = (struct in6_addr *) (hdr + 1); group_type = ipv6_addr_type(group); @@ -1212,7 +1212,7 @@ int igmp6_event_query(struct sk_buff *sk in6_dev_put(idev); return -EINVAL; } - mlh2 = (struct mld2_query *) skb->h.raw; + mlh2 = (struct mld2_query *)skb_transport_header(skb); max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000; if (!max_delay) max_delay = 1; @@ -1235,7 +1235,7 @@ int igmp6_event_query(struct sk_buff *sk in6_dev_put(idev); return -EINVAL; } - mlh2 = (struct mld2_query *) skb->h.raw; + mlh2 = (struct mld2_query *)skb_transport_header(skb); mark = 1; } } else { @@ -1300,10 +1300,10 @@ int igmp6_event_report(struct sk_buff *s if (!pskb_may_pull(skb, sizeof(struct in6_addr))) return -EINVAL; - hdr = (struct icmp6hdr*) skb->h.raw; + hdr = icmp6_hdr(skb); /* Drop reports with not link local source */ - addr_type = ipv6_addr_type(&skb->nh.ipv6h->saddr); + addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr); if (addr_type != IPV6_ADDR_ANY && !(addr_type&IPV6_ADDR_LINKLOCAL)) return -EINVAL; @@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struc skb_reserve(skb, LL_RESERVED_SPACE(dev)); - if (ipv6_get_lladdr(dev, &addr_buf)) { + if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) { /* : * use unspecified address as the source address * when a valid link-local address is not available. @@ -1423,8 +1423,9 @@ static struct sk_buff *mld_newpack(struc memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); - pmr =(struct mld2_report *)skb_put(skb, sizeof(*pmr)); - skb->h.raw = (unsigned char *)pmr; + skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data); + skb_put(skb, sizeof(*pmr)); + pmr = (struct mld2_report *)skb_transport_header(skb); pmr->type = ICMPV6_MLD2_REPORT; pmr->resv1 = 0; pmr->csum = 0; @@ -1441,7 +1442,7 @@ static inline int mld_dev_queue_xmit2(st unsigned char ha[MAX_ADDR_LEN]; int err; - ndisc_mc_map(&skb->nh.ipv6h->daddr, ha, dev, 1); + ndisc_mc_map(&ipv6_hdr(skb)->daddr, ha, dev, 1); err = dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, skb->len); if (err < 0) { kfree_skb(skb); @@ -1459,20 +1460,21 @@ static inline int mld_dev_queue_xmit(str static void mld_sendpack(struct sk_buff *skb) { - struct ipv6hdr *pip6 = skb->nh.ipv6h; - struct mld2_report *pmr = (struct mld2_report *)skb->h.raw; + struct ipv6hdr *pip6 = ipv6_hdr(skb); + struct mld2_report *pmr = + (struct mld2_report *)skb_transport_header(skb); int payload_len, mldlen; struct inet6_dev *idev = in6_dev_get(skb->dev); int err; IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); - payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h - - sizeof(struct ipv6hdr); - mldlen = skb->tail - skb->h.raw; + payload_len = (skb->tail - skb->network_header) - sizeof(*pip6); + mldlen = skb->tail - skb->transport_header; pip6->payload_len = htons(payload_len); pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, - IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0)); + IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb), + mldlen, 0)); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev, mld_dev_queue_xmit); if (!err) { @@ -1506,7 +1508,7 @@ static struct sk_buff *add_grhead(struct pgr->grec_auxwords = 0; pgr->grec_nsrcs = 0; pgr->grec_mca = pmc->mca_addr; /* structure copy */ - pmr = (struct mld2_report *)skb->h.raw; + pmr = (struct mld2_report *)skb_transport_header(skb); pmr->ngrec = htons(ntohs(pmr->ngrec)+1); *ppgr = pgr; return skb; @@ -1539,7 +1541,7 @@ static struct sk_buff *add_grec(struct s if (!*psf_list) goto empty_source; - pmr = skb ? (struct mld2_report *)skb->h.raw : NULL; + pmr = skb ? (struct mld2_report *)skb_transport_header(skb) : NULL; /* EX and TO_EX get a fresh packet, if needed */ if (truncate) { @@ -1791,7 +1793,7 @@ static void igmp6_send(struct in6_addr * skb_reserve(skb, LL_RESERVED_SPACE(dev)); - if (ipv6_get_lladdr(dev, &addr_buf)) { + if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) { /* : * use unspecified address as the source address * when a valid link-local address is not available. diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index 0afcabd..13b7160 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -90,23 +90,26 @@ int mip6_mh_filter(struct sock *sk, stru { struct ip6_mh *mh; - if (!pskb_may_pull(skb, (skb->h.raw - skb->data) + 8) || - !pskb_may_pull(skb, (skb->h.raw - skb->data) + ((skb->h.raw[1] + 1) << 3))) + if (!pskb_may_pull(skb, (skb_transport_offset(skb)) + 8) || + !pskb_may_pull(skb, (skb_transport_offset(skb) + + ((skb_transport_header(skb)[1] + 1) << 3)))) return -1; - mh = (struct ip6_mh *)skb->h.raw; + mh = (struct ip6_mh *)skb_transport_header(skb); if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) { LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n", mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type)); - mip6_param_prob(skb, 0, (&mh->ip6mh_hdrlen) - skb->nh.raw); + mip6_param_prob(skb, 0, ((&mh->ip6mh_hdrlen) - + skb_network_header(skb))); return -1; } if (mh->ip6mh_proto != IPPROTO_NONE) { LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n", mh->ip6mh_proto); - mip6_param_prob(skb, 0, (&mh->ip6mh_proto) - skb->nh.raw); + mip6_param_prob(skb, 0, ((&mh->ip6mh_proto) - + skb_network_header(skb))); return -1; } @@ -122,12 +125,12 @@ struct mip6_report_rate_limiter { }; static struct mip6_report_rate_limiter mip6_report_rl = { - .lock = SPIN_LOCK_UNLOCKED + .lock = __SPIN_LOCK_UNLOCKED(mip6_report_rl.lock) }; static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb) { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); struct ipv6_destopt_hdr *destopt = (struct ipv6_destopt_hdr *)skb->data; if (!ipv6_addr_equal(&iph->saddr, (struct in6_addr *)x->coaddr) && @@ -152,10 +155,10 @@ static int mip6_destopt_output(struct xf iph = (struct ipv6hdr *)skb->data; iph->payload_len = htons(skb->len - sizeof(*iph)); - nexthdr = *skb->nh.raw; - *skb->nh.raw = IPPROTO_DSTOPTS; + nexthdr = *skb_network_header(skb); + *skb_network_header(skb) = IPPROTO_DSTOPTS; - dstopt = (struct ipv6_destopt_hdr *)skb->h.raw; + dstopt = (struct ipv6_destopt_hdr *)skb_transport_header(skb); dstopt->nexthdr = nexthdr; hao = mip6_padn((char *)(dstopt + 1), @@ -215,21 +218,22 @@ static int mip6_destopt_reject(struct xf if (likely(opt->dsthao)) { offset = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO); if (likely(offset >= 0)) - hao = (struct ipv6_destopt_hao *)(skb->nh.raw + offset); + hao = (struct ipv6_destopt_hao *) + (skb_network_header(skb) + offset); } skb_get_timestamp(skb, &stamp); - if (!mip6_report_rl_allow(&stamp, &skb->nh.ipv6h->daddr, - hao ? &hao->addr : &skb->nh.ipv6h->saddr, + if (!mip6_report_rl_allow(&stamp, &ipv6_hdr(skb)->daddr, + hao ? &hao->addr : &ipv6_hdr(skb)->saddr, opt->iif)) goto out; memset(&sel, 0, sizeof(sel)); - memcpy(&sel.daddr, (xfrm_address_t *)&skb->nh.ipv6h->daddr, + memcpy(&sel.daddr, (xfrm_address_t *)&ipv6_hdr(skb)->daddr, sizeof(sel.daddr)); sel.prefixlen_d = 128; - memcpy(&sel.saddr, (xfrm_address_t *)&skb->nh.ipv6h->saddr, + memcpy(&sel.saddr, (xfrm_address_t *)&ipv6_hdr(skb)->saddr, sizeof(sel.saddr)); sel.prefixlen_s = 128; sel.family = AF_INET6; @@ -253,11 +257,13 @@ static int mip6_destopt_offset(struct xf u8 **nexthdr) { u16 offset = sizeof(struct ipv6hdr); - struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1); - unsigned int packet_len = skb->tail - skb->nh.raw; + struct ipv6_opt_hdr *exthdr = + (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); + const unsigned char *nh = skb_network_header(skb); + unsigned int packet_len = skb->tail - skb->network_header; int found_rhdr = 0; - *nexthdr = &skb->nh.ipv6h->nexthdr; + *nexthdr = &ipv6_hdr(skb)->nexthdr; while (offset + 1 <= packet_len) { @@ -288,7 +294,7 @@ static int mip6_destopt_offset(struct xf offset += ipv6_optlen(exthdr); *nexthdr = &exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + exthdr = (struct ipv6_opt_hdr *)(nh + offset); } return offset; @@ -361,10 +367,10 @@ static int mip6_rthdr_output(struct xfrm iph = (struct ipv6hdr *)skb->data; iph->payload_len = htons(skb->len - sizeof(*iph)); - nexthdr = *skb->nh.raw; - *skb->nh.raw = IPPROTO_ROUTING; + nexthdr = *skb_network_header(skb); + *skb_network_header(skb) = IPPROTO_ROUTING; - rt2 = (struct rt2_hdr *)skb->h.raw; + rt2 = (struct rt2_hdr *)skb_transport_header(skb); rt2->rt_hdr.nexthdr = nexthdr; rt2->rt_hdr.hdrlen = (x->props.header_len >> 3) - 1; rt2->rt_hdr.type = IPV6_SRCRT_TYPE_2; @@ -383,11 +389,13 @@ static int mip6_rthdr_offset(struct xfrm u8 **nexthdr) { u16 offset = sizeof(struct ipv6hdr); - struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1); - unsigned int packet_len = skb->tail - skb->nh.raw; + struct ipv6_opt_hdr *exthdr = + (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); + const unsigned char *nh = skb_network_header(skb); + unsigned int packet_len = skb->tail - skb->network_header; int found_rhdr = 0; - *nexthdr = &skb->nh.ipv6h->nexthdr; + *nexthdr = &ipv6_hdr(skb)->nexthdr; while (offset + 1 <= packet_len) { @@ -397,7 +405,7 @@ static int mip6_rthdr_offset(struct xfrm case NEXTHDR_ROUTING: if (offset + 3 <= packet_len) { struct ipv6_rt_hdr *rt; - rt = (struct ipv6_rt_hdr *)(skb->nh.raw + offset); + rt = (struct ipv6_rt_hdr *)(nh + offset); if (rt->type != 0) return offset; } @@ -417,7 +425,7 @@ static int mip6_rthdr_offset(struct xfrm offset += ipv6_optlen(exthdr); *nexthdr = &exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + exthdr = (struct ipv6_opt_hdr *)(nh + offset); } return offset; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 121f31c..d8b3645 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -319,6 +319,8 @@ int ndisc_mc_map(struct in6_addr *addr, return -EINVAL; } +EXPORT_SYMBOL(ndisc_mc_map); + static u32 ndisc_hash(const void *pkey, const struct net_device *dev) { const u32 *p32 = pkey; @@ -425,36 +427,23 @@ static inline void ndisc_flow_init(struc security_sk_classify_flow(ndisc_socket->sk, fl); } -static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, - struct in6_addr *daddr, struct in6_addr *solicited_addr, - int router, int solicited, int override, int inc_opt) +static void __ndisc_send(struct net_device *dev, + struct neighbour *neigh, + struct in6_addr *daddr, struct in6_addr *saddr, + struct icmp6hdr *icmp6h, struct in6_addr *target, + int llinfo, int icmp6_mib_outnd) { - struct in6_addr tmpaddr; - struct inet6_ifaddr *ifp; - struct inet6_dev *idev; struct flowi fl; - struct dst_entry* dst; + struct dst_entry *dst; struct sock *sk = ndisc_socket->sk; - struct in6_addr *src_addr; - struct nd_msg *msg; - int len; struct sk_buff *skb; + struct icmp6hdr *hdr; + struct inet6_dev *idev; + int len; int err; + u8 *opt; - len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); - - /* for anycast or proxy, solicited_addr != src_addr */ - ifp = ipv6_get_ifaddr(solicited_addr, dev, 1); - if (ifp) { - src_addr = solicited_addr; - in6_ifa_put(ifp); - } else { - if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr)) - return; - src_addr = &tmpaddr; - } - - ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr, + ndisc_flow_init(&fl, icmp6h->icmp6_type, saddr, daddr, dev->ifindex); dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); @@ -465,60 +454,57 @@ static void ndisc_send_na(struct net_dev if (err < 0) return; - if (inc_opt) { - if (dev->addr_len) - len += ndisc_opt_addr_space(dev); - else - inc_opt = 0; - } + if (!dev->addr_len) + llinfo = 0; + + len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0); + if (llinfo) + len += ndisc_opt_addr_space(dev); skb = sock_alloc_send_skb(sk, (MAX_HEADER + sizeof(struct ipv6hdr) + len + LL_RESERVED_SPACE(dev)), 1, &err); - - if (skb == NULL) { + if (!skb) { ND_PRINTK0(KERN_ERR - "ICMPv6 NA: %s() failed to allocate an skb.\n", + "ICMPv6 ND: %s() failed to allocate an skb.\n", __FUNCTION__); dst_release(dst); return; } skb_reserve(skb, LL_RESERVED_SPACE(dev)); - ip6_nd_hdr(sk, skb, dev, src_addr, daddr, IPPROTO_ICMPV6, len); - - msg = (struct nd_msg *)skb_put(skb, len); - skb->h.raw = (unsigned char*)msg; + ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); - msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; - msg->icmph.icmp6_code = 0; - msg->icmph.icmp6_cksum = 0; + skb->transport_header = skb->tail; + skb_put(skb, len); - msg->icmph.icmp6_unused = 0; - msg->icmph.icmp6_router = router; - msg->icmph.icmp6_solicited = solicited; - msg->icmph.icmp6_override = override; + hdr = (struct icmp6hdr *)skb_transport_header(skb); + memcpy(hdr, icmp6h, sizeof(*hdr)); - /* Set the target address. */ - ipv6_addr_copy(&msg->target, solicited_addr); + opt = skb_transport_header(skb) + sizeof(struct icmp6hdr); + if (target) { + ipv6_addr_copy((struct in6_addr *)opt, target); + opt += sizeof(*target); + } - if (inc_opt) - ndisc_fill_addr_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, + if (llinfo) + ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev->addr_len, dev->type); - /* checksum */ - msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, len, - IPPROTO_ICMPV6, - csum_partial((__u8 *) msg, - len, 0)); + hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len, + IPPROTO_ICMPV6, + csum_partial((__u8 *) hdr, + len, 0)); skb->dst = dst; + idev = in6_dev_get(dst->dev); IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); + err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); if (!err) { - ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS); + ICMP6_INC_STATS(idev, icmp6_mib_outnd); ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); } @@ -526,165 +512,95 @@ static void ndisc_send_na(struct net_dev in6_dev_put(idev); } +static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, + struct in6_addr *daddr, struct in6_addr *solicited_addr, + int router, int solicited, int override, int inc_opt) +{ + struct in6_addr tmpaddr; + struct inet6_ifaddr *ifp; + struct in6_addr *src_addr; + struct icmp6hdr icmp6h = { + .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, + }; + + /* for anycast or proxy, solicited_addr != src_addr */ + ifp = ipv6_get_ifaddr(solicited_addr, dev, 1); + if (ifp) { + src_addr = solicited_addr; + if (ifp->flags & IFA_F_OPTIMISTIC) + override = 0; + in6_ifa_put(ifp); + } else { + if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr)) + return; + src_addr = &tmpaddr; + } + + icmp6h.icmp6_router = router; + icmp6h.icmp6_solicited = solicited; + icmp6h.icmp6_override = override; + + __ndisc_send(dev, neigh, daddr, src_addr, + &icmp6h, solicited_addr, + inc_opt ? ND_OPT_TARGET_LL_ADDR : 0, + ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS); +} + void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, struct in6_addr *solicit, struct in6_addr *daddr, struct in6_addr *saddr) { - struct flowi fl; - struct dst_entry* dst; - struct inet6_dev *idev; - struct sock *sk = ndisc_socket->sk; - struct sk_buff *skb; - struct nd_msg *msg; struct in6_addr addr_buf; - int len; - int err; - int send_llinfo; + struct icmp6hdr icmp6h = { + .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, + }; if (saddr == NULL) { - if (ipv6_get_lladdr(dev, &addr_buf)) + if (ipv6_get_lladdr(dev, &addr_buf, + (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))) return; saddr = &addr_buf; } - ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr, - dev->ifindex); - - dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); - if (!dst) - return; - - err = xfrm_lookup(&dst, &fl, NULL, 0); - if (err < 0) - return; - - len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); - send_llinfo = dev->addr_len && !ipv6_addr_any(saddr); - if (send_llinfo) - len += ndisc_opt_addr_space(dev); - - skb = sock_alloc_send_skb(sk, - (MAX_HEADER + sizeof(struct ipv6hdr) + - len + LL_RESERVED_SPACE(dev)), - 1, &err); - if (skb == NULL) { - ND_PRINTK0(KERN_ERR - "ICMPv6 NA: %s() failed to allocate an skb.\n", - __FUNCTION__); - dst_release(dst); - return; - } - - skb_reserve(skb, LL_RESERVED_SPACE(dev)); - ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); - - msg = (struct nd_msg *)skb_put(skb, len); - skb->h.raw = (unsigned char*)msg; - msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION; - msg->icmph.icmp6_code = 0; - msg->icmph.icmp6_cksum = 0; - msg->icmph.icmp6_unused = 0; - - /* Set the target address. */ - ipv6_addr_copy(&msg->target, solicit); - - if (send_llinfo) - ndisc_fill_addr_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, - dev->addr_len, dev->type); - - /* checksum */ - msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, - daddr, len, - IPPROTO_ICMPV6, - csum_partial((__u8 *) msg, - len, 0)); - /* send it! */ - skb->dst = dst; - idev = in6_dev_get(dst->dev); - IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); - err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); - if (!err) { - ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORSOLICITS); - ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); - } - - if (likely(idev != NULL)) - in6_dev_put(idev); + __ndisc_send(dev, neigh, daddr, saddr, + &icmp6h, solicit, + !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0, + ICMP6_MIB_OUTNEIGHBORSOLICITS); } void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr) { - struct flowi fl; - struct dst_entry* dst; - struct inet6_dev *idev; - struct sock *sk = ndisc_socket->sk; - struct sk_buff *skb; - struct icmp6hdr *hdr; - __u8 * opt; - int len; - int err; - - ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr, - dev->ifindex); - - dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output); - if (!dst) - return; - - err = xfrm_lookup(&dst, &fl, NULL, 0); - if (err < 0) - return; - - len = sizeof(struct icmp6hdr); - if (dev->addr_len) - len += ndisc_opt_addr_space(dev); - - skb = sock_alloc_send_skb(sk, - (MAX_HEADER + sizeof(struct ipv6hdr) + - len + LL_RESERVED_SPACE(dev)), - 1, &err); - if (skb == NULL) { - ND_PRINTK0(KERN_ERR - "ICMPv6 RS: %s() failed to allocate an skb.\n", - __FUNCTION__); - dst_release(dst); - return; - } - - skb_reserve(skb, LL_RESERVED_SPACE(dev)); - ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); - - hdr = (struct icmp6hdr *)skb_put(skb, len); - skb->h.raw = (unsigned char*)hdr; - hdr->icmp6_type = NDISC_ROUTER_SOLICITATION; - hdr->icmp6_code = 0; - hdr->icmp6_cksum = 0; - hdr->icmp6_unused = 0; - - opt = (u8*) (hdr + 1); - - if (dev->addr_len) - ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, - dev->addr_len, dev->type); - - /* checksum */ - hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len, - IPPROTO_ICMPV6, - csum_partial((__u8 *) hdr, len, 0)); + struct icmp6hdr icmp6h = { + .icmp6_type = NDISC_ROUTER_SOLICITATION, + }; + int send_sllao = dev->addr_len; - /* send it! */ - skb->dst = dst; - idev = in6_dev_get(dst->dev); - IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); - err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); - if (!err) { - ICMP6_INC_STATS(idev, ICMP6_MIB_OUTROUTERSOLICITS); - ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + /* + * According to section 2.2 of RFC 4429, we must not + * send router solicitations with a sllao from + * optimistic addresses, but we may send the solicitation + * if we don't include the sllao. So here we check + * if our address is optimistic, and if so, we + * supress the inclusion of the sllao. + */ + if (send_sllao) { + struct inet6_ifaddr *ifp = ipv6_get_ifaddr(saddr, dev, 1); + if (ifp) { + if (ifp->flags & IFA_F_OPTIMISTIC) { + send_sllao = 0; + } + in6_ifa_put(ifp); + } else { + send_sllao = 0; + } } - - if (likely(idev != NULL)) - in6_dev_put(idev); +#endif + __ndisc_send(dev, NULL, daddr, saddr, + &icmp6h, NULL, + send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0, + ICMP6_MIB_OUTROUTERSOLICITS); } @@ -708,8 +624,8 @@ static void ndisc_solicit(struct neighbo struct in6_addr *target = (struct in6_addr *)&neigh->primary_key; int probes = atomic_read(&neigh->probes); - if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 1)) - saddr = &skb->nh.ipv6h->saddr; + if (skb && ipv6_chk_addr(&ipv6_hdr(skb)->saddr, dev, 1)) + saddr = &ipv6_hdr(skb)->saddr; if ((probes -= neigh->parms->ucast_probes) < 0) { if (!(neigh->nud_state & NUD_VALID)) { @@ -732,11 +648,12 @@ #endif static void ndisc_recv_ns(struct sk_buff *skb) { - struct nd_msg *msg = (struct nd_msg *)skb->h.raw; - struct in6_addr *saddr = &skb->nh.ipv6h->saddr; - struct in6_addr *daddr = &skb->nh.ipv6h->daddr; + struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); + struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; + struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; - u32 ndoptlen = skb->tail - msg->opt; + u32 ndoptlen = skb->tail - (skb->transport_header + + offsetof(struct nd_msg, opt)); struct ndisc_options ndopts; struct net_device *dev = skb->dev; struct inet6_ifaddr *ifp; @@ -796,28 +713,40 @@ static void ndisc_recv_ns(struct sk_buff inc = ipv6_addr_is_multicast(daddr); if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) { - if (ifp->flags & IFA_F_TENTATIVE) { - /* Address is tentative. If the source - is unspecified address, it is someone - does DAD, otherwise we ignore solicitations - until DAD timer expires. - */ - if (!dad) + + if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) { + if (dad) { + if (dev->type == ARPHRD_IEEE802_TR) { + const unsigned char *sadr; + sadr = skb_mac_header(skb); + if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 && + sadr[9] == dev->dev_addr[1] && + sadr[10] == dev->dev_addr[2] && + sadr[11] == dev->dev_addr[3] && + sadr[12] == dev->dev_addr[4] && + sadr[13] == dev->dev_addr[5]) { + /* looped-back to us */ + goto out; + } + } + + /* + * We are colliding with another node + * who is doing DAD + * so fail our DAD process + */ + addrconf_dad_failure(ifp); goto out; - if (dev->type == ARPHRD_IEEE802_TR) { - unsigned char *sadr = skb->mac.raw; - if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 && - sadr[9] == dev->dev_addr[1] && - sadr[10] == dev->dev_addr[2] && - sadr[11] == dev->dev_addr[3] && - sadr[12] == dev->dev_addr[4] && - sadr[13] == dev->dev_addr[5]) { - /* looped-back to us */ + } else { + /* + * This is not a dad solicitation. + * If we are an optimistic node, + * we should respond. + * Otherwise, we should ignore it. + */ + if (!(ifp->flags & IFA_F_OPTIMISTIC)) goto out; - } } - addrconf_dad_failure(ifp); - return; } idev = ifp->idev; @@ -898,11 +827,12 @@ out: static void ndisc_recv_na(struct sk_buff *skb) { - struct nd_msg *msg = (struct nd_msg *)skb->h.raw; - struct in6_addr *saddr = &skb->nh.ipv6h->saddr; - struct in6_addr *daddr = &skb->nh.ipv6h->daddr; + struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); + struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; + struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; - u32 ndoptlen = skb->tail - msg->opt; + u32 ndoptlen = skb->tail - (skb->transport_header + + offsetof(struct nd_msg, opt)); struct ndisc_options ndopts; struct net_device *dev = skb->dev; struct inet6_ifaddr *ifp; @@ -1000,11 +930,11 @@ out: static void ndisc_recv_rs(struct sk_buff *skb) { - struct rs_msg *rs_msg = (struct rs_msg *) skb->h.raw; + struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb); unsigned long ndoptlen = skb->len - sizeof(*rs_msg); struct neighbour *neigh; struct inet6_dev *idev; - struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; struct ndisc_options ndopts; u8 *lladdr = NULL; @@ -1057,7 +987,7 @@ out: static void ndisc_router_discovery(struct sk_buff *skb) { - struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw; + struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); struct neighbour *neigh = NULL; struct inet6_dev *in6_dev; struct rt6_info *rt = NULL; @@ -1068,9 +998,9 @@ static void ndisc_router_discovery(struc __u8 * opt = (__u8 *)(ra_msg + 1); - optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg); + optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg); - if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) { + if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { ND_PRINTK2(KERN_WARNING "ICMPv6 RA: source address is not link-local.\n"); return; @@ -1136,7 +1066,7 @@ #ifdef CONFIG_IPV6_ROUTER_PREF pref = ICMPV6_ROUTER_PREF_MEDIUM; #endif - rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev); + rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev); if (rt) neigh = rt->rt6i_nexthop; @@ -1151,7 +1081,7 @@ #endif ND_PRINTK3(KERN_DEBUG "ICMPv6 RA: adding default router.\n"); - rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev, pref); + rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref); if (rt == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 RA: %s() failed to add default route.\n", @@ -1223,7 +1153,7 @@ skip_defrtr: */ if (!neigh) - neigh = __neigh_lookup(&nd_tbl, &skb->nh.ipv6h->saddr, + neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr, skb->dev, 1); if (neigh) { u8 *lladdr = NULL; @@ -1252,7 +1182,7 @@ #ifdef CONFIG_IPV6_ROUTE_INFO if (((struct route_info *)p)->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) continue; rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3, - &skb->nh.ipv6h->saddr); + &ipv6_hdr(skb)->saddr); } } #endif @@ -1311,13 +1241,13 @@ static void ndisc_redirect_rcv(struct sk int optlen; u8 *lladdr = NULL; - if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) { + if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { ND_PRINTK2(KERN_WARNING "ICMPv6 Redirect: source address is not link-local.\n"); return; } - optlen = skb->tail - skb->h.raw; + optlen = skb->tail - skb->transport_header; optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); if (optlen < 0) { @@ -1326,7 +1256,7 @@ static void ndisc_redirect_rcv(struct sk return; } - icmph = (struct icmp6hdr *) skb->h.raw; + icmph = icmp6_hdr(skb); target = (struct in6_addr *) (icmph + 1); dest = target + 1; @@ -1376,8 +1306,8 @@ static void ndisc_redirect_rcv(struct sk neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); if (neigh) { - rt6_redirect(dest, &skb->nh.ipv6h->daddr, - &skb->nh.ipv6h->saddr, neigh, lladdr, + rt6_redirect(dest, &ipv6_hdr(skb)->daddr, + &ipv6_hdr(skb)->saddr, neigh, lladdr, on_link); neigh_release(neigh); } @@ -1406,21 +1336,21 @@ void ndisc_send_redirect(struct sk_buff dev = skb->dev; - if (ipv6_get_lladdr(dev, &saddr_buf)) { + if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) { ND_PRINTK2(KERN_WARNING "ICMPv6 Redirect: no link-local address on %s\n", dev->name); return; } - if (!ipv6_addr_equal(&skb->nh.ipv6h->daddr, target) && + if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) && !(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) { ND_PRINTK2(KERN_WARNING "ICMPv6 Redirect: target address is not link-local.\n"); return; } - ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr, + ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex); dst = ip6_route_output(NULL, &fl); @@ -1475,11 +1405,12 @@ void ndisc_send_redirect(struct sk_buff hlen = 0; skb_reserve(buff, LL_RESERVED_SPACE(dev)); - ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr, + ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr, IPPROTO_ICMPV6, len); - icmph = (struct icmp6hdr *)skb_put(buff, len); - buff->h.raw = (unsigned char*)icmph; + skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); + skb_put(buff, len); + icmph = icmp6_hdr(buff); memset(icmph, 0, sizeof(struct icmp6hdr)); icmph->icmp6_type = NDISC_REDIRECT; @@ -1491,7 +1422,7 @@ void ndisc_send_redirect(struct sk_buff addrp = (struct in6_addr *)(icmph + 1); ipv6_addr_copy(addrp, target); addrp++; - ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr); + ipv6_addr_copy(addrp, &ipv6_hdr(skb)->daddr); opt = (u8*) (addrp + 1); @@ -1512,9 +1443,9 @@ void ndisc_send_redirect(struct sk_buff *(opt++) = (rd_len >> 3); opt += 6; - memcpy(opt, skb->nh.ipv6h, rd_len - 8); + memcpy(opt, ipv6_hdr(skb), rd_len - 8); - icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &skb->nh.ipv6h->saddr, + icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, len, IPPROTO_ICMPV6, csum_partial((u8 *) icmph, len, 0)); @@ -1544,14 +1475,14 @@ int ndisc_rcv(struct sk_buff *skb) if (!pskb_may_pull(skb, skb->len)) return 0; - msg = (struct nd_msg *) skb->h.raw; + msg = (struct nd_msg *)skb_transport_header(skb); - __skb_push(skb, skb->data-skb->h.raw); + __skb_push(skb, skb->data - skb_transport_header(skb)); - if (skb->nh.ipv6h->hop_limit != 255) { + if (ipv6_hdr(skb)->hop_limit != 255) { ND_PRINTK2(KERN_WARNING "ICMPv6 NDISC: invalid hop-limit: %d\n", - skb->nh.ipv6h->hop_limit); + ipv6_hdr(skb)->hop_limit); return 0; } @@ -1584,7 +1515,7 @@ int ndisc_rcv(struct sk_buff *skb) case NDISC_REDIRECT: ndisc_redirect_rcv(skb); break; - }; + } return 0; } diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 1c405dd..38b1496 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -11,7 +11,7 @@ #include int ip6_route_me_harder(struct sk_buff *skb) { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); struct dst_entry *dst; struct flowi fl = { .oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, @@ -61,7 +61,7 @@ static void nf_ip6_saveroute(const struc struct ip6_rt_info *rt_info = nf_info_reroute(info); if (info->hook == NF_IP6_LOCAL_OUT) { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); rt_info->daddr = iph->daddr; rt_info->saddr = iph->saddr; @@ -73,7 +73,7 @@ static int nf_ip6_reroute(struct sk_buff struct ip6_rt_info *rt_info = nf_info_reroute(info); if (info->hook == NF_IP6_LOCAL_OUT) { - struct ipv6hdr *iph = (*pskb)->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(*pskb); if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || !ipv6_addr_equal(&iph->saddr, &rt_info->saddr)) return ip6_route_me_harder(*pskb); @@ -84,7 +84,7 @@ static int nf_ip6_reroute(struct sk_buff __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, u_int8_t protocol) { - struct ipv6hdr *ip6h = skb->nh.ipv6h; + struct ipv6hdr *ip6h = ipv6_hdr(skb); __sum16 csum = 0; switch (skb->ip_summed) { diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index fdb30a5..0004db3 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c @@ -11,18 +11,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 2001-11-06: First try. Working with ip_queue.c for IPv4 and trying - * to adapt it to IPv6 - * HEAVILY based in ipqueue.c by James Morris. It's just - * a little modified version of it, so he's nearly the - * real coder of this. - * Few changes needed, mainly the hard_routing code and - * the netlink socket protocol (we're NETLINK_IP6_FW). - * 2002-06-25: Code cleanup. [JM: ported cleanup over from ip_queue.c] - * 2005-02-04: Added /proc counter for dropped packets; fixed so - * packets aren't delivered to user space if they're going - * to be dropped. */ #include #include @@ -189,12 +177,13 @@ ipq_flush(int verdict) static struct sk_buff * ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) { - unsigned char *old_tail; + sk_buff_data_t old_tail; size_t size = 0; size_t data_len = 0; struct sk_buff *skb; struct ipq_packet_msg *pmsg; struct nlmsghdr *nlh; + struct timeval tv; read_lock_bh(&queue_lock); @@ -232,15 +221,16 @@ ipq_build_packet_message(struct ipq_queu if (!skb) goto nlmsg_failure; - old_tail= skb->tail; + old_tail = skb->tail; nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); pmsg = NLMSG_DATA(nlh); memset(pmsg, 0, sizeof(*pmsg)); pmsg->packet_id = (unsigned long )entry; pmsg->data_len = data_len; - pmsg->timestamp_sec = entry->skb->tstamp.off_sec; - pmsg->timestamp_usec = entry->skb->tstamp.off_usec; + tv = ktime_to_timeval(entry->skb->tstamp); + pmsg->timestamp_sec = tv.tv_sec; + pmsg->timestamp_usec = tv.tv_usec; pmsg->mark = entry->skb->mark; pmsg->hook = entry->info->hook; pmsg->hw_protocol = entry->skb->protocol; @@ -376,7 +366,7 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, st } if (!skb_make_writable(&e->skb, v->data_len)) return -ENOMEM; - memcpy(e->skb->data, v->payload, v->data_len); + skb_copy_to_linear_data(e->skb, v->payload, v->data_len); e->skb->ip_summed = CHECKSUM_NONE; return 0; @@ -485,7 +475,7 @@ ipq_rcv_skb(struct sk_buff *skb) if (skblen < sizeof(*nlh)) return; - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); nlmsglen = nlh->nlmsg_len; if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) return; @@ -667,7 +657,7 @@ static int __init ip6_queue_init(void) struct proc_dir_entry *proc; netlink_register_notifier(&ipq_nl_notifier); - ipqnl = netlink_kernel_create(NETLINK_IP6_FW, 0, ipq_rcv_sk, + ipqnl = netlink_kernel_create(NETLINK_IP6_FW, 0, ipq_rcv_sk, NULL, THIS_MODULE); if (ipqnl == NULL) { printk(KERN_ERR "ip6_queue: failed to create netlink socket\n"); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 7c512e1..9aa6240 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -7,15 +7,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 19 Jan 2002 Harald Welte - * - increase module usage count as soon as we have rules inside - * a table - * 06 Jun 2002 Andras Kis-Szabo - * - new extension header parser code - * 15 Oct 2005 Harald Welte - * - Unification of {ip,ip6}_tables into x_tables - * - Removed tcp and udp code, since it's not ipv6 specific */ #include @@ -115,7 +106,7 @@ ip6_packet_match(const struct sk_buff *s { size_t i; unsigned long ret; - const struct ipv6hdr *ipv6 = skb->nh.ipv6h; + const struct ipv6hdr *ipv6 = ipv6_hdr(skb); #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg)) @@ -301,7 +292,7 @@ ip6t_do_table(struct sk_buff **pskb, goto no_match; ADD_COUNTER(e->counters, - ntohs((*pskb)->nh.ipv6h->payload_len) + ntohs(ipv6_hdr(*pskb)->payload_len) + IPV6_HDR_LEN, 1); @@ -1448,8 +1439,8 @@ static void __exit ip6_tables_fini(void) int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff) { - unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data; - u8 nexthdr = skb->nh.ipv6h->nexthdr; + unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); + u8 nexthdr = ipv6_hdr(skb)->nexthdr; unsigned int len = skb->len - start; if (fragoff) diff --git a/net/ipv6/netfilter/ip6t_HL.c b/net/ipv6/netfilter/ip6t_HL.c index ccbab66..4115a57 100644 --- a/net/ipv6/netfilter/ip6t_HL.c +++ b/net/ipv6/netfilter/ip6t_HL.c @@ -32,7 +32,7 @@ static unsigned int ip6t_hl_target(struc if (!skb_make_writable(pskb, (*pskb)->len)) return NF_DROP; - ip6h = (*pskb)->nh.ipv6h; + ip6h = ipv6_hdr(*pskb); switch (info->mode) { case IP6T_HL_SET: diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c index afaa039..5bb9cd3 100644 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ b/net/ipv6/netfilter/ip6t_LOG.c @@ -396,8 +396,8 @@ ip6t_log_packet(unsigned int pf, /* MAC logging for input chain only. */ printk("MAC="); if (skb->dev && (len = skb->dev->hard_header_len) && - skb->mac.raw != skb->nh.raw) { - unsigned char *p = skb->mac.raw; + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); int i; if (skb->dev->type == ARPHRD_SIT && @@ -412,7 +412,8 @@ ip6t_log_packet(unsigned int pf, printk(" "); if (skb->dev->type == ARPHRD_SIT) { - struct iphdr *iph = (struct iphdr *)skb->mac.raw; + const struct iphdr *iph = + (struct iphdr *)skb_mac_header(skb); printk("TUNNEL=%u.%u.%u.%u->%u.%u.%u.%u ", NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); @@ -421,7 +422,7 @@ ip6t_log_packet(unsigned int pf, printk(" "); } - dump_packet(loginfo, skb, (u8*)skb->nh.ipv6h - skb->data, 1); + dump_packet(loginfo, skb, skb_network_offset(skb), 1); printk("\n"); spin_unlock_bh(&log_lock); } @@ -489,14 +490,10 @@ static int __init ip6t_log_init(void) ret = xt_register_target(&ip6t_log_reg); if (ret < 0) return ret; - if (nf_log_register(PF_INET6, &ip6t_logger) < 0) { - printk(KERN_WARNING "ip6t_LOG: not logging via system console " - "since somebody else already registered for PF_INET6\n"); - /* we cannot make module load fail here, since otherwise - * ip6tables userspace would abort */ - } - - return 0; + ret = nf_log_register(PF_INET6, &ip6t_logger); + if (ret < 0 && ret != -EEXIST) + xt_unregister_target(&ip6t_log_reg); + return ret; } static void __exit ip6t_log_fini(void) diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index 6abee94..cb3d241 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -47,7 +47,7 @@ static void send_reset(struct sk_buff *o struct tcphdr otcph, *tcph; unsigned int otcplen, hh_len; int tcphoff, needs_ack; - struct ipv6hdr *oip6h = oldskb->nh.ipv6h, *ip6h; + struct ipv6hdr *oip6h = ipv6_hdr(oldskb), *ip6h; struct dst_entry *dst = NULL; u8 proto; struct flowi fl; @@ -120,8 +120,9 @@ static void send_reset(struct sk_buff *o skb_reserve(nskb, hh_len + dst->header_len); - ip6h = nskb->nh.ipv6h = (struct ipv6hdr *) - skb_put(nskb, sizeof(struct ipv6hdr)); + skb_put(nskb, sizeof(struct ipv6hdr)); + skb_reset_network_header(nskb); + ip6h = ipv6_hdr(nskb); ip6h->version = 6; ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); ip6h->nexthdr = IPPROTO_TCP; @@ -155,8 +156,8 @@ static void send_reset(struct sk_buff *o tcph->check = 0; /* Adjust TCP checksum */ - tcph->check = csum_ipv6_magic(&nskb->nh.ipv6h->saddr, - &nskb->nh.ipv6h->daddr, + tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, + &ipv6_hdr(nskb)->daddr, sizeof(struct tcphdr), IPPROTO_TCP, csum_partial((char *)tcph, sizeof(struct tcphdr), 0)); diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c index 967bed7..0f3dd93 100644 --- a/net/ipv6/netfilter/ip6t_eui64.c +++ b/net/ipv6/netfilter/ip6t_eui64.c @@ -32,8 +32,8 @@ match(const struct sk_buff *skb, unsigned char eui64[8]; int i = 0; - if (!(skb->mac.raw >= skb->head && - (skb->mac.raw + ETH_HLEN) <= skb->data) && + if (!(skb_mac_header(skb) >= skb->head && + (skb_mac_header(skb) + ETH_HLEN) <= skb->data) && offset != 0) { *hotdrop = 1; return 0; @@ -42,7 +42,7 @@ match(const struct sk_buff *skb, memset(eui64, 0, sizeof(eui64)); if (eth_hdr(skb)->h_proto == htons(ETH_P_IPV6)) { - if (skb->nh.ipv6h->version == 0x6) { + if (ipv6_hdr(skb)->version == 0x6) { memcpy(eui64, eth_hdr(skb)->h_source, 3); memcpy(eui64 + 5, eth_hdr(skb)->h_source + 3, 3); eui64[3] = 0xff; @@ -50,7 +50,7 @@ match(const struct sk_buff *skb, eui64[0] |= 0x02; i = 0; - while ((skb->nh.ipv6h->saddr.s6_addr[8+i] == eui64[i]) + while ((ipv6_hdr(skb)->saddr.s6_addr[8 + i] == eui64[i]) && (i < 8)) i++; diff --git a/net/ipv6/netfilter/ip6t_hl.c b/net/ipv6/netfilter/ip6t_hl.c index 37c8a4d..d606c0e 100644 --- a/net/ipv6/netfilter/ip6t_hl.c +++ b/net/ipv6/netfilter/ip6t_hl.c @@ -25,7 +25,7 @@ static int match(const struct sk_buff *s int offset, unsigned int protoff, int *hotdrop) { const struct ip6t_hl_info *info = matchinfo; - const struct ipv6hdr *ip6h = skb->nh.ipv6h; + const struct ipv6hdr *ip6h = ipv6_hdr(skb); switch (info->mode) { case IP6T_HL_EQ: diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c index 700a11d..fd6a086 100644 --- a/net/ipv6/netfilter/ip6t_ipv6header.c +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -45,7 +45,7 @@ ipv6header_match(const struct sk_buff *s /* Make sure this isn't an evil packet */ /* type of the 1st exthdr */ - nexthdr = skb->nh.ipv6h->nexthdr; + nexthdr = ipv6_hdr(skb)->nexthdr; /* pointer to the 1st exthdr */ ptr = sizeof(struct ipv6hdr); /* available length */ diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index 112a21d..76f0cf6 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -102,7 +102,7 @@ ip6t_local_out_hook(unsigned int hook, #if 0 /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { + || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { if (net_ratelimit()) printk("ip6t_hook: happy cracking.\n"); return NF_ACCEPT; diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 0c468d3..a9f10e3 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -7,8 +7,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * Extended to all five netfilter hooks by Brad Chapman & Harald Welte */ #include #include @@ -138,7 +136,7 @@ ip6t_local_hook(unsigned int hook, #if 0 /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) - || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { + || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { if (net_ratelimit()) printk("ip6t_hook: happy cracking.\n"); return NF_ACCEPT; @@ -146,21 +144,21 @@ #if 0 #endif /* save source/dest address, mark, hoplimit, flowlabel, priority, */ - memcpy(&saddr, &(*pskb)->nh.ipv6h->saddr, sizeof(saddr)); - memcpy(&daddr, &(*pskb)->nh.ipv6h->daddr, sizeof(daddr)); + memcpy(&saddr, &ipv6_hdr(*pskb)->saddr, sizeof(saddr)); + memcpy(&daddr, &ipv6_hdr(*pskb)->daddr, sizeof(daddr)); mark = (*pskb)->mark; - hop_limit = (*pskb)->nh.ipv6h->hop_limit; + hop_limit = ipv6_hdr(*pskb)->hop_limit; /* flowlabel and prio (includes version, which shouldn't change either */ - flowlabel = *((u_int32_t *) (*pskb)->nh.ipv6h); + flowlabel = *((u_int32_t *)ipv6_hdr(*pskb)); ret = ip6t_do_table(pskb, hook, in, out, &packet_mangler); if (ret != NF_DROP && ret != NF_STOLEN - && (memcmp(&(*pskb)->nh.ipv6h->saddr, &saddr, sizeof(saddr)) - || memcmp(&(*pskb)->nh.ipv6h->daddr, &daddr, sizeof(daddr)) + && (memcmp(&ipv6_hdr(*pskb)->saddr, &saddr, sizeof(saddr)) + || memcmp(&ipv6_hdr(*pskb)->daddr, &daddr, sizeof(daddr)) || (*pskb)->mark != mark - || (*pskb)->nh.ipv6h->hop_limit != hop_limit)) + || ipv6_hdr(*pskb)->hop_limit != hop_limit)) return ip6_route_me_harder(*pskb) == 0 ? ret : NF_DROP; return ret; diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index d110245..6d2a082 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -7,17 +7,6 @@ * * Author: * Yasuyuki Kozakai @USAGI - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - support Layer 3 protocol independent connection tracking. - * Based on the original ip_conntrack code which had the following - * copyright information: - * (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * 23 Mar 2004: Yasuyuki Kozakai @USAGI - * - add get_features() to support various size of conntrack - * structures. */ #include @@ -138,16 +127,10 @@ static int ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff, u_int8_t *protonum) { - unsigned int extoff; - unsigned char pnum; - int protoff; - - extoff = (u8*)((*pskb)->nh.ipv6h + 1) - (*pskb)->data; - pnum = (*pskb)->nh.ipv6h->nexthdr; - - protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, - (*pskb)->len - extoff); - + unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data; + unsigned char pnum = ipv6_hdr(*pskb)->nexthdr; + int protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, + (*pskb)->len - extoff); /* * (protoff == (*pskb)->len) mean that the packet doesn't have no data * except of IPv6 & ext headers. but it's tracked anyway. - YK @@ -179,9 +162,8 @@ static unsigned int ipv6_confirm(unsigne struct nf_conn_help *help; enum ip_conntrack_info ctinfo; unsigned int ret, protoff; - unsigned int extoff = (u8*)((*pskb)->nh.ipv6h + 1) - - (*pskb)->data; - unsigned char pnum = (*pskb)->nh.ipv6h->nexthdr; + unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data; + unsigned char pnum = ipv6_hdr(*pskb)->nexthdr; /* This is where we call the helper: as the packet goes out. */ diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 075da4f..0be790d 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -7,13 +7,6 @@ * * Author: * Yasuyuki Kozakai @USAGI - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - ICMPv6 tracking support. Derived from the original ip_conntrack code - * net/ipv4/netfilter/ip_conntrack_proto_icmp.c which had the following - * copyright information: - * (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team */ #include diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 15ab1e3..347ab76 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -82,7 +82,7 @@ struct nf_ct_frag6_queue struct sk_buff *fragments; int len; int meat; - struct timeval stamp; + ktime_t stamp; unsigned int csum; __u8 last_in; /* has first/last segment arrived? */ #define COMPLETE 4 @@ -353,9 +353,7 @@ nf_ct_frag6_create(unsigned int hash, __ ipv6_addr_copy(&fq->saddr, src); ipv6_addr_copy(&fq->daddr, dst); - init_timer(&fq->timer); - fq->timer.function = nf_ct_frag6_expire; - fq->timer.data = (long) fq; + setup_timer(&fq->timer, nf_ct_frag6_expire, (unsigned long)fq); spin_lock_init(&fq->lock); atomic_set(&fq->refcnt, 1); @@ -400,19 +398,20 @@ static int nf_ct_frag6_queue(struct nf_c } offset = ntohs(fhdr->frag_off) & ~0x7; - end = offset + (ntohs(skb->nh.ipv6h->payload_len) - - ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1))); + end = offset + (ntohs(ipv6_hdr(skb)->payload_len) - + ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); if ((unsigned int)end > IPV6_MAXPLEN) { DEBUGP("offset is too large.\n"); return -1; } - if (skb->ip_summed == CHECKSUM_COMPLETE) + if (skb->ip_summed == CHECKSUM_COMPLETE) { + const unsigned char *nh = skb_network_header(skb); skb->csum = csum_sub(skb->csum, - csum_partial(skb->nh.raw, - (u8*)(fhdr + 1) - skb->nh.raw, + csum_partial(nh, (u8 *)(fhdr + 1) - nh, 0)); + } /* Is this the final fragment? */ if (!(fhdr->frag_off & htons(IP6_MF))) { @@ -542,7 +541,7 @@ static int nf_ct_frag6_queue(struct nf_c fq->fragments = skb; skb->dev = NULL; - skb_get_timestamp(skb, &fq->stamp); + fq->stamp = skb->tstamp; fq->meat += skb->len; atomic_add(skb->truesize, &nf_ct_frag6_mem); @@ -583,7 +582,9 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_que BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0); /* Unfragmented part is taken from the first segment. */ - payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr); + payload_len = ((head->data - skb_network_header(head)) - + sizeof(struct ipv6hdr) + fq->len - + sizeof(struct frag_hdr)); if (payload_len > IPV6_MAXPLEN) { DEBUGP("payload len is too large.\n"); goto out_oversize; @@ -624,15 +625,15 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_que /* We have to remove fragment header from datagram and to relocate * header in order to calculate ICV correctly. */ - head->nh.raw[fq->nhoffset] = head->h.raw[0]; + skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0]; memmove(head->head + sizeof(struct frag_hdr), head->head, (head->data - head->head) - sizeof(struct frag_hdr)); - head->mac.raw += sizeof(struct frag_hdr); - head->nh.raw += sizeof(struct frag_hdr); + head->mac_header += sizeof(struct frag_hdr); + head->network_header += sizeof(struct frag_hdr); skb_shinfo(head)->frag_list = head->next; - head->h.raw = head->data; - skb_push(head, head->data - head->nh.raw); + skb_reset_transport_header(head); + skb_push(head, head->data - skb_network_header(head)); atomic_sub(head->truesize, &nf_ct_frag6_mem); for (fp=head->next; fp; fp = fp->next) { @@ -648,12 +649,14 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_que head->next = NULL; head->dev = dev; - skb_set_timestamp(head, &fq->stamp); - head->nh.ipv6h->payload_len = htons(payload_len); + head->tstamp = fq->stamp; + ipv6_hdr(head)->payload_len = htons(payload_len); /* Yes, and fold redundant checksum back. 8) */ if (head->ip_summed == CHECKSUM_COMPLETE) - head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum); + head->csum = csum_partial(skb_network_header(head), + skb_network_header_len(head), + head->csum); fq->fragments = NULL; @@ -701,9 +704,10 @@ out_fail: static int find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) { - u8 nexthdr = skb->nh.ipv6h->nexthdr; - u8 prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data; - int start = (u8 *)(skb->nh.ipv6h+1) - skb->data; + u8 nexthdr = ipv6_hdr(skb)->nexthdr; + const int netoff = skb_network_offset(skb); + u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr); + int start = netoff + sizeof(struct ipv6hdr); int len = skb->len - start; u8 prevhdr = NEXTHDR_IPV6; @@ -759,7 +763,7 @@ struct sk_buff *nf_ct_frag6_gather(struc struct sk_buff *ret_skb = NULL; /* Jumbo payload inhibits frag. header */ - if (skb->nh.ipv6h->payload_len == 0) { + if (ipv6_hdr(skb)->payload_len == 0) { DEBUGP("payload len = 0\n"); return skb; } @@ -780,9 +784,9 @@ struct sk_buff *nf_ct_frag6_gather(struc goto ret_orig; } - clone->h.raw = clone->data + fhoff; - hdr = clone->nh.ipv6h; - fhdr = (struct frag_hdr *)clone->h.raw; + skb_set_transport_header(clone, fhoff); + hdr = ipv6_hdr(clone); + fhdr = (struct frag_hdr *)skb_transport_header(clone); if (!(fhdr->frag_off & htons(0xFFF9))) { DEBUGP("Invalid fragment offset\n"); @@ -864,8 +868,7 @@ int nf_ct_frag6_init(void) nf_ct_frag6_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ (jiffies ^ (jiffies >> 6))); - init_timer(&nf_ct_frag6_secret_timer); - nf_ct_frag6_secret_timer.function = nf_ct_frag6_secret_rebuild; + setup_timer(&nf_ct_frag6_secret_timer, nf_ct_frag6_secret_rebuild, 0); nf_ct_frag6_secret_timer.expires = jiffies + nf_ct_frag6_secret_interval; add_timer(&nf_ct_frag6_secret_timer); diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index fa3fb50..920dc9c 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -23,12 +23,12 @@ #include #include #include #include +#include #include #include #include #include -#ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_net_devsnmp6; static int fold_prot_inuse(struct proto *proto) @@ -142,26 +142,13 @@ static struct snmp_mib snmp6_udplite6_li SNMP_MIB_SENTINEL }; -static unsigned long -fold_field(void *mib[], int offt) -{ - unsigned long res = 0; - int i; - - for_each_possible_cpu(i) { - res += *(((unsigned long *)per_cpu_ptr(mib[0], i)) + offt); - res += *(((unsigned long *)per_cpu_ptr(mib[1], i)) + offt); - } - return res; -} - static inline void snmp6_seq_show_item(struct seq_file *seq, void **mib, struct snmp_mib *itemlist) { int i; for (i=0; itemlist[i].name; i++) seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, - fold_field(mib, itemlist[i].entry)); + snmp_fold_field(mib, itemlist[i].entry)); } static int snmp6_seq_show(struct seq_file *seq, void *v) @@ -236,6 +223,7 @@ int snmp6_unregister_dev(struct inet6_de return -EINVAL; remove_proc_entry(idev->stats.proc_dir_entry->name, proc_net_devsnmp6); + idev->stats.proc_dir_entry = NULL; return 0; } @@ -271,47 +259,3 @@ void ipv6_misc_proc_exit(void) proc_net_remove("snmp6"); } -#else /* CONFIG_PROC_FS */ - - -int snmp6_register_dev(struct inet6_dev *idev) -{ - return 0; -} - -int snmp6_unregister_dev(struct inet6_dev *idev) -{ - return 0; -} -#endif /* CONFIG_PROC_FS */ - -int snmp6_alloc_dev(struct inet6_dev *idev) -{ - int err = -ENOMEM; - - if (!idev || !idev->dev) - return -EINVAL; - - if (snmp6_mib_init((void **)idev->stats.ipv6, sizeof(struct ipstats_mib), - __alignof__(struct ipstats_mib)) < 0) - goto err_ip; - if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib), - __alignof__(struct icmpv6_mib)) < 0) - goto err_icmp; - - return 0; - -err_icmp: - snmp6_mib_free((void **)idev->stats.ipv6); -err_ip: - return err; -} - -int snmp6_free_dev(struct inet6_dev *idev) -{ - snmp6_mib_free((void **)idev->stats.icmpv6); - snmp6_mib_free((void **)idev->stats.ipv6); - return 0; -} - - diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index ef43bd5..f929f47 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -60,6 +60,8 @@ int inet6_add_protocol(struct inet6_prot return ret; } +EXPORT_SYMBOL(inet6_add_protocol); + /* * Remove a protocol from the hash tables. */ @@ -83,3 +85,5 @@ int inet6_del_protocol(struct inet6_prot return ret; } + +EXPORT_SYMBOL(inet6_del_protocol); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 203e069..009a104 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -152,7 +152,7 @@ int ipv6_raw_deliver(struct sk_buff *skb int delivered = 0; __u8 hash; - saddr = &skb->nh.ipv6h->saddr; + saddr = &ipv6_hdr(skb)->saddr; daddr = saddr + 1; hash = nexthdr & (MAX_INET_PROTOS - 1); @@ -361,17 +361,18 @@ int rawv6_rcv(struct sock *sk, struct sk skb->ip_summed = CHECKSUM_UNNECESSARY; if (skb->ip_summed == CHECKSUM_COMPLETE) { - skb_postpull_rcsum(skb, skb->nh.raw, - skb->h.raw - skb->nh.raw); - if (!csum_ipv6_magic(&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, + skb_postpull_rcsum(skb, skb_network_header(skb), + skb_network_header_len(skb)); + if (!csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, skb->len, inet->num, skb->csum)) skb->ip_summed = CHECKSUM_UNNECESSARY; } - if (skb->ip_summed != CHECKSUM_UNNECESSARY) - skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, - skb->len, inet->num, 0)); + if (!skb_csum_unnecessary(skb)) + skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + skb->len, + inet->num, 0)); if (inet->hdrincl) { if (skb_checksum_complete(skb)) { @@ -420,7 +421,7 @@ static int rawv6_recvmsg(struct kiocb *i msg->msg_flags |= MSG_TRUNC; } - if (skb->ip_summed==CHECKSUM_UNNECESSARY) { + if (skb_csum_unnecessary(skb)) { err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); } else if (msg->msg_flags&MSG_TRUNC) { if (__skb_checksum_complete(skb)) @@ -438,7 +439,7 @@ static int rawv6_recvmsg(struct kiocb *i if (sin6) { sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; - ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr); + ipv6_addr_copy(&sin6->sin6_addr, &ipv6_hdr(skb)->saddr); sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) @@ -488,7 +489,8 @@ static int rawv6_push_pending_frames(str goto out; offset = rp->offset; - total_len = inet_sk(sk)->cork.length - (skb->nh.raw - skb->data); + total_len = inet_sk(sk)->cork.length - (skb_network_header(skb) - + skb->data); if (offset >= total_len - 1) { err = -EINVAL; ip6_flush_pending_frames(sk); @@ -511,7 +513,7 @@ static int rawv6_push_pending_frames(str if (csum_skb) continue; - len = skb->len - (skb->h.raw - skb->data); + len = skb->len - skb_transport_offset(skb); if (offset >= len) { offset -= len; continue; @@ -523,7 +525,7 @@ static int rawv6_push_pending_frames(str skb = csum_skb; } - offset += skb->h.raw - skb->data; + offset += skb_transport_offset(skb); if (skb_copy_bits(skb, offset, &csum, 2)) BUG(); @@ -575,11 +577,13 @@ static int rawv6_send_hdrinc(struct sock skb->priority = sk->sk_priority; skb->dst = dst_clone(&rt->u.dst); - skb->nh.ipv6h = iph = (struct ipv6hdr *)skb_put(skb, length); + skb_put(skb, length); + skb_reset_network_header(skb); + iph = ipv6_hdr(skb); skb->ip_summed = CHECKSUM_NONE; - skb->h.raw = skb->nh.raw; + skb->transport_header = skb->network_header; err = memcpy_fromiovecend((void *)iph, from, 0, length); if (err) goto error_fault; @@ -878,7 +882,7 @@ static int rawv6_seticmpfilter(struct so return 0; default: return -ENOPROTOOPT; - }; + } return 0; } @@ -903,7 +907,7 @@ static int rawv6_geticmpfilter(struct so return 0; default: return -ENOPROTOOPT; - }; + } return 0; } @@ -957,7 +961,8 @@ static int rawv6_setsockopt(struct sock default: return ipv6_setsockopt(sk, level, optname, optval, optlen); - }; + } + return do_rawv6_setsockopt(sk, level, optname, optval, optlen); } @@ -978,7 +983,7 @@ static int compat_rawv6_setsockopt(struc default: return compat_ipv6_setsockopt(sk, level, optname, optval, optlen); - }; + } return do_rawv6_setsockopt(sk, level, optname, optval, optlen); } #endif @@ -1031,7 +1036,8 @@ static int rawv6_getsockopt(struct sock default: return ipv6_getsockopt(sk, level, optname, optval, optlen); - }; + } + return do_rawv6_getsockopt(sk, level, optname, optval, optlen); } @@ -1052,7 +1058,7 @@ static int compat_rawv6_getsockopt(struc default: return compat_ipv6_getsockopt(sk, level, optname, optval, optlen); - }; + } return do_rawv6_getsockopt(sk, level, optname, optval, optlen); } #endif @@ -1073,7 +1079,7 @@ static int rawv6_ioctl(struct sock *sk, spin_lock_bh(&sk->sk_receive_queue.lock); skb = skb_peek(&sk->sk_receive_queue); if (skb != NULL) - amount = skb->tail - skb->h.raw; + amount = skb->tail - skb->transport_header; spin_unlock_bh(&sk->sk_receive_queue.lock); return put_user(amount, (int __user *)arg); } diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 7034c54..de795c0 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -88,7 +88,7 @@ struct frag_queue int len; int meat; int iif; - struct timeval stamp; + ktime_t stamp; unsigned int csum; __u8 last_in; /* has first/last segment arrived? */ #define COMPLETE 4 @@ -430,19 +430,24 @@ static void ip6_frag_queue(struct frag_q goto err; offset = ntohs(fhdr->frag_off) & ~0x7; - end = offset + (ntohs(skb->nh.ipv6h->payload_len) - - ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1))); + end = offset + (ntohs(ipv6_hdr(skb)->payload_len) - + ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); if ((unsigned int)end > IPV6_MAXPLEN) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off - skb->nh.raw); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + ((u8 *)&fhdr->frag_off - + skb_network_header(skb))); return; } - if (skb->ip_summed == CHECKSUM_COMPLETE) + if (skb->ip_summed == CHECKSUM_COMPLETE) { + const unsigned char *nh = skb_network_header(skb); skb->csum = csum_sub(skb->csum, - csum_partial(skb->nh.raw, (u8*)(fhdr+1)-skb->nh.raw, 0)); + csum_partial(nh, (u8 *)(fhdr + 1) - nh, + 0)); + } /* Is this the final fragment? */ if (!(fhdr->frag_off & htons(IP6_MF))) { @@ -562,7 +567,7 @@ static void ip6_frag_queue(struct frag_q if (skb->dev) fq->iif = skb->dev->ifindex; skb->dev = NULL; - skb_get_timestamp(skb, &fq->stamp); + fq->stamp = skb->tstamp; fq->meat += skb->len; atomic_add(skb->truesize, &ip6_frag_mem); @@ -605,7 +610,9 @@ static int ip6_frag_reasm(struct frag_qu BUG_TRAP(FRAG6_CB(head)->offset == 0); /* Unfragmented part is taken from the first segment. */ - payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr); + payload_len = ((head->data - skb_network_header(head)) - + sizeof(struct ipv6hdr) + fq->len - + sizeof(struct frag_hdr)); if (payload_len > IPV6_MAXPLEN) goto out_oversize; @@ -639,15 +646,15 @@ static int ip6_frag_reasm(struct frag_qu /* We have to remove fragment header from datagram and to relocate * header in order to calculate ICV correctly. */ nhoff = fq->nhoffset; - head->nh.raw[nhoff] = head->h.raw[0]; + skb_network_header(head)[nhoff] = skb_transport_header(head)[0]; memmove(head->head + sizeof(struct frag_hdr), head->head, (head->data - head->head) - sizeof(struct frag_hdr)); - head->mac.raw += sizeof(struct frag_hdr); - head->nh.raw += sizeof(struct frag_hdr); + head->mac_header += sizeof(struct frag_hdr); + head->network_header += sizeof(struct frag_hdr); skb_shinfo(head)->frag_list = head->next; - head->h.raw = head->data; - skb_push(head, head->data - head->nh.raw); + skb_reset_transport_header(head); + skb_push(head, head->data - skb_network_header(head)); atomic_sub(head->truesize, &ip6_frag_mem); for (fp=head->next; fp; fp = fp->next) { @@ -663,15 +670,17 @@ static int ip6_frag_reasm(struct frag_qu head->next = NULL; head->dev = dev; - skb_set_timestamp(head, &fq->stamp); - head->nh.ipv6h->payload_len = htons(payload_len); + head->tstamp = fq->stamp; + ipv6_hdr(head)->payload_len = htons(payload_len); IP6CB(head)->nhoff = nhoff; *skb_in = head; /* Yes, and fold redundant checksum back. 8) */ if (head->ip_summed == CHECKSUM_COMPLETE) - head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum); + head->csum = csum_partial(skb_network_header(head), + skb_network_header_len(head), + head->csum); rcu_read_lock(); IP6_INC_STATS_BH(__in6_dev_get(dev), IPSTATS_MIB_REASMOKS); @@ -699,33 +708,34 @@ static int ipv6_frag_rcv(struct sk_buff struct net_device *dev = skb->dev; struct frag_hdr *fhdr; struct frag_queue *fq; - struct ipv6hdr *hdr; - - hdr = skb->nh.ipv6h; + struct ipv6hdr *hdr = ipv6_hdr(skb); IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMREQDS); /* Jumbo payload inhibits frag. header */ if (hdr->payload_len==0) { IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + skb_network_header_len(skb)); return -1; } - if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) { + if (!pskb_may_pull(skb, (skb_transport_offset(skb) + + sizeof(struct frag_hdr)))) { IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + skb_network_header_len(skb)); return -1; } - hdr = skb->nh.ipv6h; - fhdr = (struct frag_hdr *)skb->h.raw; + hdr = ipv6_hdr(skb); + fhdr = (struct frag_hdr *)skb_transport_header(skb); if (!(fhdr->frag_off & htons(0xFFF9))) { /* It is not a fragmented frame */ - skb->h.raw += sizeof(struct frag_hdr); + skb->transport_header += sizeof(struct frag_hdr); IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMOKS); - IP6CB(skb)->nhoff = (u8*)fhdr - skb->nh.raw; + IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb); return 1; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index aebb4e2..b46ad53 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -575,6 +575,8 @@ struct rt6_info *rt6_lookup(struct in6_a return NULL; } +EXPORT_SYMBOL(rt6_lookup); + /* ip6_ins_rt is called with FREE table->tb6_lock. It takes new route entry, the addition fails by any reason the route is freed. In any case, if caller does not hold it, it may @@ -724,7 +726,7 @@ out2: void ip6_route_input(struct sk_buff *skb) { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); int flags = RT6_LOOKUP_F_HAS_SADDR; struct flowi fl = { .iif = skb->dev->ifindex, @@ -829,6 +831,7 @@ struct dst_entry * ip6_route_output(stru return fib6_rule_lookup(fl, flags, ip6_pol_route_output); } +EXPORT_SYMBOL(ip6_route_output); /* * Destination cache support functions @@ -1757,7 +1760,7 @@ int ipv6_route_ioctl(unsigned int cmd, v rtnl_unlock(); return err; - }; + } return -EINVAL; } @@ -1772,7 +1775,7 @@ static inline int ip6_pkt_drop(struct sk int type; switch (ipstats_mib_noroutes) { case IPSTATS_MIB_INNOROUTES: - type = ipv6_addr_type(&skb->nh.ipv6h->daddr); + type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) { IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS); break; @@ -2012,7 +2015,7 @@ errout: return err; } -int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) +static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct fib6_config cfg; int err; @@ -2024,7 +2027,7 @@ int inet6_rtm_delroute(struct sk_buff *s return ip6_route_del(&cfg); } -int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) +static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct fib6_config cfg; int err; @@ -2161,7 +2164,7 @@ int rt6_dump_route(struct rt6_info *rt, prefix, NLM_F_MULTI); } -int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) +static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) { struct nlattr *tb[RTA_MAX+1]; struct rt6_info *rt; @@ -2215,7 +2218,7 @@ int inet6_rtm_getroute(struct sk_buff *i /* Reserve room for dummy headers, this skb can pass through good chunk of routing engine. */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); rt = (struct rt6_info*) ip6_route_output(NULL, &fl); @@ -2486,8 +2489,9 @@ #endif void __init ip6_route_init(void) { +#ifdef CONFIG_PROC_FS struct proc_dir_entry *p; - +#endif ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); @@ -2505,6 +2509,10 @@ #endif #ifdef CONFIG_IPV6_MULTIPLE_TABLES fib6_rules_init(); #endif + + __rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL); + __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL); + __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL); } void ip6_route_cleanup(void) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 08d6ed3..1efa95a 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -99,10 +99,10 @@ static struct ip_tunnel * ipip6_tunnel_l return NULL; } -static struct ip_tunnel ** ipip6_bucket(struct ip_tunnel *t) +static struct ip_tunnel **__ipip6_bucket(struct ip_tunnel_parm *parms) { - __be32 remote = t->parms.iph.daddr; - __be32 local = t->parms.iph.saddr; + __be32 remote = parms->iph.daddr; + __be32 local = parms->iph.saddr; unsigned h = 0; int prio = 0; @@ -117,6 +117,11 @@ static struct ip_tunnel ** ipip6_bucket( return &tunnels[prio][h]; } +static inline struct ip_tunnel **ipip6_bucket(struct ip_tunnel *t) +{ + return __ipip6_bucket(&t->parms); +} + static void ipip6_tunnel_unlink(struct ip_tunnel *t) { struct ip_tunnel **tp; @@ -147,19 +152,9 @@ static struct ip_tunnel * ipip6_tunnel_l __be32 local = parms->iph.saddr; struct ip_tunnel *t, **tp, *nt; struct net_device *dev; - unsigned h = 0; - int prio = 0; char name[IFNAMSIZ]; - if (remote) { - prio |= 2; - h ^= HASH(remote); - } - if (local) { - prio |= 1; - h ^= HASH(local); - } - for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) { + for (tp = __ipip6_bucket(parms); (t = *tp) != NULL; tp = &t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) return t; } @@ -224,8 +219,8 @@ #ifndef I_WISH_WORLD_WERE_PERFECT ICMP in the real Internet is absolutely infeasible. */ struct iphdr *iph = (struct iphdr*)skb->data; - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; struct ip_tunnel *t; int err; @@ -280,8 +275,8 @@ #else struct iphdr *iph = (struct iphdr*)dp; int hlen = iph->ihl<<2; struct ipv6hdr *iph6; - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; int rel_type = 0; int rel_code = 0; int rel_info = 0; @@ -296,14 +291,14 @@ #else default: return; case ICMP_PARAMETERPROB: - if (skb->h.icmph->un.gateway < hlen) + if (icmp_hdr(skb)->un.gateway < hlen) return; /* So... This guy found something strange INSIDE encapsulated packet. Well, he is fool, but what can we do ? */ rel_type = ICMPV6_PARAMPROB; - rel_info = skb->h.icmph->un.gateway - hlen; + rel_info = icmp_hdr(skb)->un.gateway - hlen; break; case ICMP_DEST_UNREACH: @@ -340,7 +335,7 @@ #else dst_release(skb2->dst); skb2->dst = NULL; skb_pull(skb2, skb->data - (u8*)iph6); - skb2->nh.raw = skb2->data; + skb_reset_network_header(skb2); /* Try to guess incoming interface */ rt6i = rt6_lookup(&iph6->saddr, NULL, NULL, 0); @@ -366,7 +361,7 @@ #endif static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) { if (INET_ECN_is_ce(iph->tos)) - IP6_ECN_set_ce(skb->nh.ipv6h); + IP6_ECN_set_ce(ipv6_hdr(skb)); } static int ipip6_rcv(struct sk_buff *skb) @@ -377,13 +372,13 @@ static int ipip6_rcv(struct sk_buff *skb if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; - iph = skb->nh.iph; + iph = ip_hdr(skb); read_lock(&ipip6_lock); if ((tunnel = ipip6_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) { secpath_reset(skb); - skb->mac.raw = skb->nh.raw; - skb->nh.raw = skb->data; + skb->mac_header = skb->network_header; + skb_reset_network_header(skb); IPCB(skb)->flags = 0; skb->protocol = htons(ETH_P_IPV6); skb->pkt_type = PACKET_HOST; @@ -430,7 +425,7 @@ static int ipip6_tunnel_xmit(struct sk_b struct ip_tunnel *tunnel = netdev_priv(dev); struct net_device_stats *stats = &tunnel->stat; struct iphdr *tiph = &tunnel->parms.iph; - struct ipv6hdr *iph6 = skb->nh.ipv6h; + struct ipv6hdr *iph6 = ipv6_hdr(skb); u8 tos = tunnel->parms.iph.tos; struct rtable *rt; /* Route to the other host */ struct net_device *tdev; /* Device to other host */ @@ -468,7 +463,7 @@ static int ipip6_tunnel_xmit(struct sk_b addr_type = ipv6_addr_type(addr6); if (addr_type == IPV6_ADDR_ANY) { - addr6 = &skb->nh.ipv6h->daddr; + addr6 = &ipv6_hdr(skb)->daddr; addr_type = ipv6_addr_type(addr6); } @@ -550,11 +545,12 @@ static int ipip6_tunnel_xmit(struct sk_b skb_set_owner_w(new_skb, skb->sk); dev_kfree_skb(skb); skb = new_skb; - iph6 = skb->nh.ipv6h; + iph6 = ipv6_hdr(skb); } - skb->h.raw = skb->nh.raw; - skb->nh.raw = skb_push(skb, sizeof(struct iphdr)); + skb->transport_header = skb->network_header; + skb_push(skb, sizeof(struct iphdr)); + skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags = 0; dst_release(skb->dst); @@ -564,7 +560,7 @@ static int ipip6_tunnel_xmit(struct sk_b * Push down and install the IPIP header. */ - iph = skb->nh.iph; + iph = ip_hdr(skb); iph->version = 4; iph->ihl = sizeof(struct iphdr)>>2; if (mtu > IPV6_MIN_MTU) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 92f9992..e2f25ea 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -115,10 +115,10 @@ static __inline__ __sum16 tcp_v6_check(s static __u32 tcp_v6_init_sequence(struct sk_buff *skb) { - return secure_tcpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32, - skb->nh.ipv6h->saddr.s6_addr32, - skb->h.th->dest, - skb->h.th->source); + return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, + ipv6_hdr(skb)->saddr.s6_addr32, + tcp_hdr(skb)->dest, + tcp_hdr(skb)->source); } static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, @@ -486,7 +486,9 @@ static int tcp_v6_send_synack(struct soc struct sk_buff *pktopts = treq->pktopts; struct inet6_skb_parm *rxopt = IP6CB(pktopts); if (rxopt->srcrt) - opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(pktopts->nh.raw + rxopt->srcrt)); + opt = ipv6_invert_rthdr(sk, + (struct ipv6_rt_hdr *)(skb_network_header(pktopts) + + rxopt->srcrt)); } if (opt && opt->srcrt) { @@ -507,7 +509,7 @@ static int tcp_v6_send_synack(struct soc skb = tcp_make_synack(sk, dst, req); if (skb) { - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); th->check = tcp_v6_check(th, skb->len, &treq->loc_addr, &treq->rmt_addr, @@ -835,8 +837,8 @@ static int tcp_v6_inbound_md5_hash (stru { __u8 *hash_location = NULL; struct tcp_md5sig_key *hash_expected; - struct ipv6hdr *ip6h = skb->nh.ipv6h; - struct tcphdr *th = skb->h.th; + struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); int length = (th->doff << 2) - sizeof (*th); int genhash; u8 *ptr; @@ -944,10 +946,11 @@ static struct timewait_sock_ops tcp6_tim static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) { struct ipv6_pinfo *np = inet6_sk(sk); - struct tcphdr *th = skb->h.th; + struct tcphdr *th = tcp_hdr(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); } else { th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, @@ -964,12 +967,13 @@ static int tcp_v6_gso_send_check(struct if (!pskb_may_pull(skb, sizeof(*th))) return -EINVAL; - ipv6h = skb->nh.ipv6h; - th = skb->h.th; + ipv6h = ipv6_hdr(skb); + th = tcp_hdr(skb); th->check = 0; th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, IPPROTO_TCP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); skb->ip_summed = CHECKSUM_PARTIAL; return 0; @@ -977,7 +981,7 @@ static int tcp_v6_gso_send_check(struct static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) { - struct tcphdr *th = skb->h.th, *t1; + struct tcphdr *th = tcp_hdr(skb), *t1; struct sk_buff *buff; struct flowi fl; int tot_len = sizeof(*th); @@ -993,7 +997,7 @@ #endif #ifdef CONFIG_TCP_MD5SIG if (sk) - key = tcp_v6_md5_do_lookup(sk, &skb->nh.ipv6h->daddr); + key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr); else key = NULL; @@ -1037,20 +1041,18 @@ #ifdef CONFIG_TCP_MD5SIG (TCPOPT_NOP << 16) | (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); - tcp_v6_do_calc_md5_hash((__u8*)&opt[1], - key, - &skb->nh.ipv6h->daddr, - &skb->nh.ipv6h->saddr, - t1, IPPROTO_TCP, - tot_len); + tcp_v6_do_calc_md5_hash((__u8 *)&opt[1], key, + &ipv6_hdr(skb)->daddr, + &ipv6_hdr(skb)->saddr, + t1, IPPROTO_TCP, tot_len); } #endif buff->csum = csum_partial((char *)t1, sizeof(*t1), 0); memset(&fl, 0, sizeof(fl)); - ipv6_addr_copy(&fl.fl6_dst, &skb->nh.ipv6h->saddr); - ipv6_addr_copy(&fl.fl6_src, &skb->nh.ipv6h->daddr); + ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr); + ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr); t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst, sizeof(*t1), IPPROTO_TCP, @@ -1079,7 +1081,7 @@ #endif static void tcp_v6_send_ack(struct tcp_timewait_sock *tw, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts) { - struct tcphdr *th = skb->h.th, *t1; + struct tcphdr *th = tcp_hdr(skb), *t1; struct sk_buff *buff; struct flowi fl; int tot_len = sizeof(struct tcphdr); @@ -1091,7 +1093,7 @@ #endif #ifdef CONFIG_TCP_MD5SIG if (!tw && skb->sk) { - key = tcp_v6_md5_do_lookup(skb->sk, &skb->nh.ipv6h->daddr); + key = tcp_v6_md5_do_lookup(skb->sk, &ipv6_hdr(skb)->daddr); } else if (tw && tw->tw_md5_keylen) { tw_key.key = tw->tw_md5_key; tw_key.keylen = tw->tw_md5_keylen; @@ -1140,20 +1142,18 @@ #ifdef CONFIG_TCP_MD5SIG if (key) { *topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); - tcp_v6_do_calc_md5_hash((__u8 *)topt, - key, - &skb->nh.ipv6h->daddr, - &skb->nh.ipv6h->saddr, - t1, IPPROTO_TCP, - tot_len); + tcp_v6_do_calc_md5_hash((__u8 *)topt, key, + &ipv6_hdr(skb)->daddr, + &ipv6_hdr(skb)->saddr, + t1, IPPROTO_TCP, tot_len); } #endif buff->csum = csum_partial((char *)t1, tot_len, 0); memset(&fl, 0, sizeof(fl)); - ipv6_addr_copy(&fl.fl6_dst, &skb->nh.ipv6h->saddr); - ipv6_addr_copy(&fl.fl6_src, &skb->nh.ipv6h->daddr); + ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr); + ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr); t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst, tot_len, IPPROTO_TCP, @@ -1197,18 +1197,18 @@ static void tcp_v6_reqsk_send_ack(struct static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) { struct request_sock *req, **prev; - const struct tcphdr *th = skb->h.th; + const struct tcphdr *th = tcp_hdr(skb); struct sock *nsk; /* Find possible connection requests. */ req = inet6_csk_search_req(sk, &prev, th->source, - &skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, inet6_iif(skb)); + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, inet6_iif(skb)); if (req) return tcp_check_req(sk, skb, req, prev); - nsk = __inet6_lookup_established(&tcp_hashinfo, &skb->nh.ipv6h->saddr, - th->source, &skb->nh.ipv6h->daddr, + nsk = __inet6_lookup_established(&tcp_hashinfo, &ipv6_hdr(skb)->saddr, + th->source, &ipv6_hdr(skb)->daddr, ntohs(th->dest), inet6_iif(skb)); if (nsk) { @@ -1275,9 +1275,9 @@ #endif tcp_openreq_init(req, &tmp_opt, skb); treq = inet6_rsk(req); - ipv6_addr_copy(&treq->rmt_addr, &skb->nh.ipv6h->saddr); - ipv6_addr_copy(&treq->loc_addr, &skb->nh.ipv6h->daddr); - TCP_ECN_create_request(req, skb->h.th); + ipv6_addr_copy(&treq->rmt_addr, &ipv6_hdr(skb)->saddr); + ipv6_addr_copy(&treq->loc_addr, &ipv6_hdr(skb)->daddr); + TCP_ECN_create_request(req, tcp_hdr(skb)); treq->pktopts = NULL; if (ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || @@ -1363,7 +1363,7 @@ #endif newnp->pktoptions = NULL; newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); - newnp->mcast_hops = skb->nh.ipv6h->hop_limit; + newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; /* * No need to charge this sock to the relevant IPv6 refcnt debug socks count @@ -1389,7 +1389,9 @@ #endif opt == NULL && treq->pktopts) { struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts); if (rxopt->srcrt) - opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr *)(treq->pktopts->nh.raw + rxopt->srcrt)); + opt = ipv6_invert_rthdr(sk, + (struct ipv6_rt_hdr *)(skb_network_header(treq->pktopts) + + rxopt->srcrt)); } if (dst == NULL) { @@ -1469,7 +1471,7 @@ #endif } newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); - newnp->mcast_hops = skb->nh.ipv6h->hop_limit; + newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; /* Clone native IPv6 options from listening socket (if any) @@ -1528,15 +1530,16 @@ out: static __sum16 tcp_v6_checksum_init(struct sk_buff *skb) { if (skb->ip_summed == CHECKSUM_COMPLETE) { - if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr,skb->csum)) { + if (!tcp_v6_check(tcp_hdr(skb), skb->len, &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, skb->csum)) { skb->ip_summed = CHECKSUM_UNNECESSARY; return 0; } } - skb->csum = ~csum_unfold(tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, 0)); + skb->csum = ~csum_unfold(tcp_v6_check(tcp_hdr(skb), skb->len, + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, 0)); if (skb->len <= 76) { return __skb_checksum_complete(skb); @@ -1600,7 +1603,7 @@ #endif if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ TCP_CHECK_TIMER(sk); - if (tcp_rcv_established(sk, skb, skb->h.th, skb->len)) + if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) goto reset; TCP_CHECK_TIMER(sk); if (opt_skb) @@ -1608,7 +1611,7 @@ #endif return 0; } - if (skb->len < (skb->h.th->doff<<2) || tcp_checksum_complete(skb)) + if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb)) goto csum_err; if (sk->sk_state == TCP_LISTEN) { @@ -1631,7 +1634,7 @@ #endif } TCP_CHECK_TIMER(sk); - if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) + if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) goto reset; TCP_CHECK_TIMER(sk); if (opt_skb) @@ -1664,7 +1667,7 @@ ipv6_pktoptions: if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo) np->mcast_oif = inet6_iif(opt_skb); if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) - np->mcast_hops = opt_skb->nh.ipv6h->hop_limit; + np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; if (ipv6_opt_accepted(sk, opt_skb)) { skb_set_owner_r(opt_skb, sk); opt_skb = xchg(&np->pktoptions, opt_skb); @@ -1697,28 +1700,27 @@ static int tcp_v6_rcv(struct sk_buff **p if (!pskb_may_pull(skb, sizeof(struct tcphdr))) goto discard_it; - th = skb->h.th; + th = tcp_hdr(skb); if (th->doff < sizeof(struct tcphdr)/4) goto bad_packet; if (!pskb_may_pull(skb, th->doff*4)) goto discard_it; - if ((skb->ip_summed != CHECKSUM_UNNECESSARY && - tcp_v6_checksum_init(skb))) + if (!skb_csum_unnecessary(skb) && tcp_v6_checksum_init(skb)) goto bad_packet; - th = skb->h.th; + th = tcp_hdr(skb); TCP_SKB_CB(skb)->seq = ntohl(th->seq); TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff*4); TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); TCP_SKB_CB(skb)->when = 0; - TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(skb->nh.ipv6h); + TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(ipv6_hdr(skb)); TCP_SKB_CB(skb)->sacked = 0; - sk = __inet6_lookup(&tcp_hashinfo, &skb->nh.ipv6h->saddr, th->source, - &skb->nh.ipv6h->daddr, ntohs(th->dest), + sk = __inet6_lookup(&tcp_hashinfo, &ipv6_hdr(skb)->saddr, th->source, + &ipv6_hdr(skb)->daddr, ntohs(th->dest), inet6_iif(skb)); if (!sk) @@ -1798,7 +1800,7 @@ do_time_wait: struct sock *sk2; sk2 = inet6_lookup_listener(&tcp_hashinfo, - &skb->nh.ipv6h->daddr, + &ipv6_hdr(skb)->daddr, ntohs(th->dest), inet6_iif(skb)); if (sk2 != NULL) { struct inet_timewait_sock *tw = inet_twsk(sk); @@ -1945,6 +1947,7 @@ #endif return inet6_destroy_sock(sk); } +#ifdef CONFIG_PROC_FS /* Proc filesystem TCPv6 sock list dumping. */ static void get_openreq6(struct seq_file *seq, struct sock *sk, struct request_sock *req, int i, int uid) @@ -2061,7 +2064,6 @@ static void get_timewait6_sock(struct se atomic_read(&tw->tw_refcnt), tw); } -#ifdef CONFIG_PROC_FS static int tcp6_seq_show(struct seq_file *seq, void *v) { struct tcp_iter_state *st; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f590db5..b083c09 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -93,10 +93,10 @@ static struct sock *__udp6_lib_lookup(st continue; score++; } - if(score == 4) { + if (score == 4) { result = sk; break; - } else if(score > badness) { + } else if (score > badness) { result = sk; badness = score; } @@ -120,8 +120,9 @@ int udpv6_recvmsg(struct kiocb *iocb, st struct ipv6_pinfo *np = inet6_sk(sk); struct inet_sock *inet = inet_sk(sk); struct sk_buff *skb; - size_t copied; - int err, copy_only, is_udplite = IS_UDPLITE(sk); + unsigned int ulen, copied; + int err; + int is_udplite = IS_UDPLITE(sk); if (addr_len) *addr_len=sizeof(struct sockaddr_in6); @@ -134,24 +135,25 @@ try_again: if (!skb) goto out; - copied = skb->len - sizeof(struct udphdr); - if (copied > len) { - copied = len; + ulen = skb->len - sizeof(struct udphdr); + copied = len; + if (copied > ulen) + copied = ulen; + else if (copied < ulen) msg->msg_flags |= MSG_TRUNC; - } /* - * Decide whether to checksum and/or copy data. + * If checksum is needed at all, try to do it while copying the + * data. If the data is truncated, or if we only want a partial + * coverage checksum (UDP-Lite), do it before the copy. */ - copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY); - if (is_udplite || (!copy_only && msg->msg_flags&MSG_TRUNC)) { - if (__udp_lib_checksum_complete(skb)) + if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { + if (udp_lib_checksum_complete(skb)) goto csum_copy_err; - copy_only = 1; } - if (copy_only) + if (skb_csum_unnecessary(skb)) err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied ); else { @@ -170,15 +172,16 @@ try_again: sin6 = (struct sockaddr_in6 *) msg->msg_name; sin6->sin6_family = AF_INET6; - sin6->sin6_port = skb->h.uh->source; + sin6->sin6_port = udp_hdr(skb)->source; sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; if (skb->protocol == htons(ETH_P_IP)) ipv6_addr_set(&sin6->sin6_addr, 0, 0, - htonl(0xffff), skb->nh.iph->saddr); + htonl(0xffff), ip_hdr(skb)->saddr); else { - ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr); + ipv6_addr_copy(&sin6->sin6_addr, + &ipv6_hdr(skb)->saddr); if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) sin6->sin6_scope_id = IP6CB(skb)->iif; } @@ -194,7 +197,7 @@ try_again: err = copied; if (flags & MSG_TRUNC) - err = skb->len - sizeof(struct udphdr); + err = ulen; out_free: skb_free_datagram(sk, skb); @@ -279,8 +282,10 @@ int udpv6_queue_rcv_skb(struct sock * sk } } - if (udp_lib_checksum_complete(skb)) - goto drop; + if (sk->sk_filter) { + if (udp_lib_checksum_complete(skb)) + goto drop; + } if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) { /* Note that an ENOMEM error is charged twice */ @@ -325,7 +330,7 @@ static struct sock *udp_v6_mcast_next(st if (!ipv6_addr_equal(&np->rcv_saddr, loc_addr)) continue; } - if(!inet6_mc_check(s, loc_addr, rmt_addr)) + if (!inet6_mc_check(s, loc_addr, rmt_addr)) continue; return s; } @@ -341,7 +346,7 @@ static int __udp6_lib_mcast_deliver(stru struct in6_addr *daddr, struct hlist_head udptable[]) { struct sock *sk, *sk2; - const struct udphdr *uh = skb->h.uh; + const struct udphdr *uh = udp_hdr(skb); int dif; read_lock(&udp_hash_lock); @@ -366,9 +371,20 @@ out: return 0; } -static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh) - +static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, + int proto) { + int err; + + UDP_SKB_CB(skb)->partial_cov = 0; + UDP_SKB_CB(skb)->cscov = skb->len; + + if (proto == IPPROTO_UDPLITE) { + err = udplite_checksum_init(skb, uh); + if (err) + return err; + } + if (uh->check == 0) { /* RFC 2460 section 8.1 says that we SHOULD log this error. Well, it is reasonable. @@ -377,21 +393,20 @@ static inline int udp6_csum_init(struct return 1; } if (skb->ip_summed == CHECKSUM_COMPLETE && - !csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, - skb->len, IPPROTO_UDP, skb->csum )) + !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, + skb->len, proto, skb->csum)) skb->ip_summed = CHECKSUM_UNNECESSARY; - if (skb->ip_summed != CHECKSUM_UNNECESSARY) - skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, - skb->len, IPPROTO_UDP, - 0)); + if (!skb_csum_unnecessary(skb)) + skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + skb->len, proto, 0)); - return (UDP_SKB_CB(skb)->partial_cov = 0); + return 0; } int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], - int is_udplite) + int proto) { struct sk_buff *skb = *pskb; struct sock *sk; @@ -403,15 +418,16 @@ int __udp6_lib_rcv(struct sk_buff **pskb if (!pskb_may_pull(skb, sizeof(struct udphdr))) goto short_packet; - saddr = &skb->nh.ipv6h->saddr; - daddr = &skb->nh.ipv6h->daddr; - uh = skb->h.uh; + saddr = &ipv6_hdr(skb)->saddr; + daddr = &ipv6_hdr(skb)->daddr; + uh = udp_hdr(skb); ulen = ntohs(uh->len); if (ulen > skb->len) goto short_packet; - if(! is_udplite ) { /* UDP validates ulen. */ + if (proto == IPPROTO_UDP) { + /* UDP validates ulen. */ /* Check for jumbo payload */ if (ulen == 0) @@ -423,19 +439,15 @@ int __udp6_lib_rcv(struct sk_buff **pskb if (ulen < skb->len) { if (pskb_trim_rcsum(skb, ulen)) goto short_packet; - saddr = &skb->nh.ipv6h->saddr; - daddr = &skb->nh.ipv6h->daddr; - uh = skb->h.uh; + saddr = &ipv6_hdr(skb)->saddr; + daddr = &ipv6_hdr(skb)->daddr; + uh = udp_hdr(skb); } - - if (udp6_csum_init(skb, uh)) - goto discard; - - } else { /* UDP-Lite validates cscov. */ - if (udplite6_csum_init(skb, uh)) - goto discard; } + if (udp6_csum_init(skb, uh, proto)) + goto discard; + /* * Multicast receive code */ @@ -457,33 +469,34 @@ int __udp6_lib_rcv(struct sk_buff **pskb if (udp_lib_checksum_complete(skb)) goto discard; - UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite); + UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev); kfree_skb(skb); - return(0); + return 0; } /* deliver */ udpv6_queue_rcv_skb(sk, skb); sock_put(sk); - return(0); + return 0; short_packet: LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n", - is_udplite? "-Lite" : "", ulen, skb->len); + proto == IPPROTO_UDPLITE ? "-Lite" : "", + ulen, skb->len); discard: - UDP6_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite); + UDP6_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); kfree_skb(skb); - return(0); + return 0; } static __inline__ int udpv6_rcv(struct sk_buff **pskb) { - return __udp6_lib_rcv(pskb, udp_hash, 0); + return __udp6_lib_rcv(pskb, udp_hash, IPPROTO_UDP); } /* @@ -521,7 +534,7 @@ static int udp_v6_push_pending_frames(st /* * Create a UDP header */ - uh = skb->h.uh; + uh = udp_hdr(skb); uh->source = fl->fl_ip_sport; uh->dest = fl->fl_ip_dport; uh->len = htons(up->len); diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 629f971..f54016a 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -19,7 +19,7 @@ DEFINE_SNMP_STAT(struct udp_mib, udplite static int udplitev6_rcv(struct sk_buff **pskb) { - return __udp6_lib_rcv(pskb, udplite_hash, 1); + return __udp6_lib_rcv(pskb, udplite_hash, IPPROTO_UDPLITE); } static void udplitev6_err(struct sk_buff *skb, diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 31f651f..d7ed8aa 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -28,14 +28,14 @@ int xfrm6_rcv_spi(struct sk_buff *skb, _ unsigned int nhoff; nhoff = IP6CB(skb)->nhoff; - nexthdr = skb->nh.raw[nhoff]; + nexthdr = skb_network_header(skb)[nhoff]; seq = 0; if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) goto drop; do { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); if (xfrm_nr == XFRM_MAX_DEPTH) goto drop; @@ -58,7 +58,7 @@ int xfrm6_rcv_spi(struct sk_buff *skb, _ if (nexthdr <= 0) goto drop_unlock; - skb->nh.raw[nhoff] = nexthdr; + skb_network_header(skb)[nhoff] = nexthdr; if (x->props.replay_window) xfrm_replay_advance(x, seq); @@ -112,8 +112,8 @@ int xfrm6_rcv_spi(struct sk_buff *skb, _ return -1; } else { #ifdef CONFIG_NETFILTER - skb->nh.ipv6h->payload_len = htons(skb->len); - __skb_push(skb, skb->data - skb->nh.raw); + ipv6_hdr(skb)->payload_len = htons(skb->len); + __skb_push(skb, skb->data - skb_network_header(skb)); NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, ip6_rcv_finish); @@ -140,19 +140,19 @@ int xfrm6_rcv(struct sk_buff **pskb) return xfrm6_rcv_spi(*pskb, 0); } +EXPORT_SYMBOL(xfrm6_rcv); + int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto) { struct xfrm_state *x = NULL; int wildcard = 0; - struct in6_addr any; xfrm_address_t *xany; struct xfrm_state *xfrm_vec_one = NULL; int nh = 0; int i = 0; - ipv6_addr_set(&any, 0, 0, 0, 0); - xany = (xfrm_address_t *)&any; + xany = (xfrm_address_t *)&in6addr_any; for (i = 0; i < 3; i++) { xfrm_address_t *dst, *src; @@ -247,3 +247,5 @@ drop: xfrm_state_put(xfrm_vec_one); return -1; } + +EXPORT_SYMBOL(xfrm6_input_addr); diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c index edcfffa..2e61d6d 100644 --- a/net/ipv6/xfrm6_mode_beet.c +++ b/net/ipv6/xfrm6_mode_beet.c @@ -38,17 +38,18 @@ static int xfrm6_beet_output(struct xfrm int hdr_len; skb_push(skb, x->props.header_len); - iph = skb->nh.ipv6h; + iph = ipv6_hdr(skb); hdr_len = ip6_find_1stfragopt(skb, &prevhdr); - skb->nh.raw = prevhdr - x->props.header_len; - skb->h.raw = skb->data + hdr_len; + skb_set_network_header(skb, + (prevhdr - x->props.header_len) - skb->data); + skb_set_transport_header(skb, hdr_len); memmove(skb->data, iph, hdr_len); - skb->nh.raw = skb->data; - top_iph = skb->nh.ipv6h; - skb->nh.raw = &top_iph->nexthdr; - skb->h.ipv6h = top_iph + 1; + skb_reset_network_header(skb); + top_iph = ipv6_hdr(skb); + skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); + skb->network_header += offsetof(struct ipv6hdr, nexthdr); ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); @@ -59,6 +60,7 @@ static int xfrm6_beet_output(struct xfrm static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb) { struct ipv6hdr *ip6h; + const unsigned char *old_mac; int size = sizeof(struct ipv6hdr); int err = -EINVAL; @@ -66,13 +68,14 @@ static int xfrm6_beet_input(struct xfrm_ goto out; skb_push(skb, size); - memmove(skb->data, skb->nh.raw, size); - skb->nh.raw = skb->data; + memmove(skb->data, skb_network_header(skb), size); + skb_reset_network_header(skb); - skb->mac.raw = memmove(skb->data - skb->mac_len, - skb->mac.raw, skb->mac_len); + old_mac = skb_mac_header(skb); + skb_set_mac_header(skb, -skb->mac_len); + memmove(skb_mac_header(skb), old_mac, skb->mac_len); - ip6h = skb->nh.ipv6h; + ip6h = ipv6_hdr(skb); ip6h->payload_len = htons(skb->len - size); ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6); ipv6_addr_copy(&ip6h->saddr, (struct in6_addr *) &x->sel.saddr.a6); diff --git a/net/ipv6/xfrm6_mode_ro.c b/net/ipv6/xfrm6_mode_ro.c index 6031c16..6ad6d7a 100644 --- a/net/ipv6/xfrm6_mode_ro.c +++ b/net/ipv6/xfrm6_mode_ro.c @@ -50,11 +50,12 @@ static int xfrm6_ro_output(struct xfrm_s int hdr_len; skb_push(skb, x->props.header_len); - iph = skb->nh.ipv6h; + iph = ipv6_hdr(skb); hdr_len = x->type->hdr_offset(x, skb, &prevhdr); - skb->nh.raw = prevhdr - x->props.header_len; - skb->h.raw = skb->data + hdr_len; + skb_set_network_header(skb, + (prevhdr - x->props.header_len) - skb->data); + skb_set_transport_header(skb, hdr_len); memmove(skb->data, iph, hdr_len); return 0; } diff --git a/net/ipv6/xfrm6_mode_transport.c b/net/ipv6/xfrm6_mode_transport.c index 3a4b39b..c026bfe 100644 --- a/net/ipv6/xfrm6_mode_transport.c +++ b/net/ipv6/xfrm6_mode_transport.c @@ -32,11 +32,12 @@ static int xfrm6_transport_output(struct int hdr_len; skb_push(skb, x->props.header_len); - iph = skb->nh.ipv6h; + iph = ipv6_hdr(skb); hdr_len = x->type->hdr_offset(x, skb, &prevhdr); - skb->nh.raw = prevhdr - x->props.header_len; - skb->h.raw = skb->data + hdr_len; + skb_set_network_header(skb, + (prevhdr - x->props.header_len) - skb->data); + skb_set_transport_header(skb, hdr_len); memmove(skb->data, iph, hdr_len); return 0; } @@ -51,13 +52,16 @@ static int xfrm6_transport_output(struct */ static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb) { - int ihl = skb->data - skb->h.raw; + int ihl = skb->data - skb_transport_header(skb); - if (skb->h.raw != skb->nh.raw) - skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl); - skb->nh.ipv6h->payload_len = htons(skb->len + ihl - + if (skb->transport_header != skb->network_header) { + memmove(skb_transport_header(skb), + skb_network_header(skb), ihl); + skb->network_header = skb->transport_header; + } + ipv6_hdr(skb)->payload_len = htons(skb->len + ihl - sizeof(struct ipv6hdr)); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); return 0; } diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 0bc866c..a6c0cdf 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -18,8 +18,8 @@ #include static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) { - struct ipv6hdr *outer_iph = skb->nh.ipv6h; - struct ipv6hdr *inner_iph = skb->h.ipv6h; + struct ipv6hdr *outer_iph = ipv6_hdr(skb); + struct ipv6hdr *inner_iph = ipipv6_hdr(skb); if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) IP6_ECN_set_ce(inner_iph); @@ -27,8 +27,8 @@ static inline void ipip6_ecn_decapsulate static inline void ip6ip_ecn_decapsulate(struct sk_buff *skb) { - if (INET_ECN_is_ce(ipv6_get_dsfield(skb->nh.ipv6h))) - IP_ECN_set_ce(skb->h.ipiph); + if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6_hdr(skb)))) + IP_ECN_set_ce(ipip_hdr(skb)); } /* Add encapsulation header. @@ -51,12 +51,12 @@ static int xfrm6_tunnel_output(struct xf int dsfield; skb_push(skb, x->props.header_len); - iph = skb->nh.ipv6h; + iph = ipv6_hdr(skb); - skb->nh.raw = skb->data; - top_iph = skb->nh.ipv6h; - skb->nh.raw = &top_iph->nexthdr; - skb->h.ipv6h = top_iph + 1; + skb_reset_network_header(skb); + top_iph = ipv6_hdr(skb); + skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); + skb->network_header += offsetof(struct ipv6hdr, nexthdr); top_iph->version = 6; if (xdst->route->ops->family == AF_INET6) { @@ -86,9 +86,11 @@ static int xfrm6_tunnel_output(struct xf static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) { int err = -EINVAL; + const unsigned char *old_mac; + const unsigned char *nh = skb_network_header(skb); - if (skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPV6 - && skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPIP) + if (nh[IP6CB(skb)->nhoff] != IPPROTO_IPV6 && + nh[IP6CB(skb)->nhoff] != IPPROTO_IPIP) goto out; if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; @@ -97,9 +99,10 @@ static int xfrm6_tunnel_input(struct xfr (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) goto out; - if (skb->nh.raw[IP6CB(skb)->nhoff] == IPPROTO_IPV6) { + nh = skb_network_header(skb); + if (nh[IP6CB(skb)->nhoff] == IPPROTO_IPV6) { if (x->props.flags & XFRM_STATE_DECAP_DSCP) - ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h); + ipv6_copy_dscp(ipv6_hdr(skb), ipipv6_hdr(skb)); if (!(x->props.flags & XFRM_STATE_NOECN)) ipip6_ecn_decapsulate(skb); } else { @@ -107,9 +110,10 @@ static int xfrm6_tunnel_input(struct xfr ip6ip_ecn_decapsulate(skb); skb->protocol = htons(ETH_P_IP); } - skb->mac.raw = memmove(skb->data - skb->mac_len, - skb->mac.raw, skb->mac_len); - skb->nh.raw = skb->data; + old_mac = skb_mac_header(skb); + skb_set_mac_header(skb, -skb->mac_len); + memmove(skb_mac_header(skb), old_mac, skb->mac_len); + skb_reset_network_header(skb); err = 0; out: diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index d6d786b..56364a5 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -23,6 +23,8 @@ int xfrm6_find_1stfragopt(struct xfrm_st return ip6_find_1stfragopt(skb, prevhdr); } +EXPORT_SYMBOL(xfrm6_find_1stfragopt); + static int xfrm6_tunnel_check_size(struct sk_buff *skb) { int mtu, ret = 0; @@ -76,11 +78,11 @@ static int xfrm6_output_one(struct sk_bu x->curlft.bytes += skb->len; x->curlft.packets++; if (x->props.mode == XFRM_MODE_ROUTEOPTIMIZATION) - x->lastused = (u64)xtime.tv_sec; + x->lastused = get_seconds(); spin_unlock_bh(&x->lock); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); if (!(skb->dst = dst_pop(dst))) { err = -EHOSTUNREACH; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index d8a585b..1faa2ea 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -240,7 +240,8 @@ __xfrm6_bundle_create(struct xfrm_policy if (!afinfo) { dst = *dst_p; goto error; - }; + } + dst_prev->output = afinfo->output; xfrm_state_put_afinfo(afinfo); /* Sheit... I remember I did this right. Apparently, @@ -270,17 +271,19 @@ error: static inline void _decode_session6(struct sk_buff *skb, struct flowi *fl) { - u16 offset = skb->h.raw - skb->nh.raw; - struct ipv6hdr *hdr = skb->nh.ipv6h; + u16 offset = skb_network_header_len(skb); + struct ipv6hdr *hdr = ipv6_hdr(skb); struct ipv6_opt_hdr *exthdr; - u8 nexthdr = skb->nh.raw[IP6CB(skb)->nhoff]; + const unsigned char *nh = skb_network_header(skb); + u8 nexthdr = nh[IP6CB(skb)->nhoff]; memset(fl, 0, sizeof(struct flowi)); ipv6_addr_copy(&fl->fl6_dst, &hdr->daddr); ipv6_addr_copy(&fl->fl6_src, &hdr->saddr); - while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) { - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + while (pskb_may_pull(skb, nh + offset + 1 - skb->data)) { + nh = skb_network_header(skb); + exthdr = (struct ipv6_opt_hdr *)(nh + offset); switch (nexthdr) { case NEXTHDR_ROUTING: @@ -288,7 +291,7 @@ _decode_session6(struct sk_buff *skb, st case NEXTHDR_DEST: offset += ipv6_optlen(exthdr); nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + exthdr = (struct ipv6_opt_hdr *)(nh + offset); break; case IPPROTO_UDP: @@ -296,7 +299,7 @@ _decode_session6(struct sk_buff *skb, st case IPPROTO_TCP: case IPPROTO_SCTP: case IPPROTO_DCCP: - if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) { + if (pskb_may_pull(skb, nh + offset + 4 - skb->data)) { __be16 *ports = (__be16 *)exthdr; fl->fl_ip_sport = ports[0]; @@ -306,7 +309,7 @@ _decode_session6(struct sk_buff *skb, st return; case IPPROTO_ICMPV6: - if (pskb_may_pull(skb, skb->nh.raw + offset + 2 - skb->data)) { + if (pskb_may_pull(skb, nh + offset + 2 - skb->data)) { u8 *icmp = (u8 *)exthdr; fl->fl_icmp_type = icmp[0]; @@ -317,7 +320,7 @@ _decode_session6(struct sk_buff *skb, st #ifdef CONFIG_IPV6_MIP6 case IPPROTO_MH: - if (pskb_may_pull(skb, skb->nh.raw + offset + 3 - skb->data)) { + if (pskb_may_pull(skb, nh + offset + 3 - skb->data)) { struct ip6_mh *mh; mh = (struct ip6_mh *)exthdr; @@ -335,7 +338,7 @@ #endif fl->fl_ipsec_spi = 0; fl->proto = nexthdr; return; - }; + } } } diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index 93c4223..5502cc9 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -257,11 +257,11 @@ static int xfrm6_tunnel_input(struct xfr static int xfrm6_tunnel_rcv(struct sk_buff *skb) { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); __be32 spi; spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr); - return xfrm6_rcv_spi(skb, spi); + return xfrm6_rcv_spi(skb, spi) > 0 ? : 0; } static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt, diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index cac35a7..392f8bc 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -576,7 +576,9 @@ static struct sk_buff *ipxitf_adjust_skb skb2 = alloc_skb(len, GFP_ATOMIC); if (skb2) { skb_reserve(skb2, out_offset); - skb2->nh.raw = skb2->h.raw = skb_put(skb2, skb->len); + skb_reset_network_header(skb2); + skb_reset_transport_header(skb2); + skb_put(skb2, skb->len); memcpy(ipx_hdr(skb2), ipx_hdr(skb), skb->len); memcpy(skb2->cb, skb->cb, sizeof(skb->cb)); } @@ -1807,8 +1809,8 @@ #endif /* CONFIG_IPX_INTERN */ copied); if (rc) goto out_free; - if (skb->tstamp.off_sec) - skb_get_timestamp(skb, &sk->sk_stamp); + if (skb->tstamp.tv64) + sk->sk_stamp = skb->tstamp; msg->msg_namelen = sizeof(*sipx); diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c index 8e1cad9..e16c114 100644 --- a/net/ipx/ipx_route.c +++ b/net/ipx/ipx_route.c @@ -203,7 +203,9 @@ int ipxrtr_route_packet(struct sock *sk, skb->sk = sk; /* Fill in IPX header */ - skb->h.raw = skb->nh.raw = skb_put(skb, sizeof(struct ipxhdr)); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(struct ipxhdr)); ipx = ipx_hdr(skb); ipx->ipx_pktsize = htons(len + sizeof(struct ipxhdr)); IPX_SKB_CB(skb)->ipx_tctrl = 0; diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 0eb7d59..06c97c6 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -89,7 +89,6 @@ static int irda_data_indication(void *in self = instance; sk = instance; - IRDA_ASSERT(sk != NULL, return -1;); err = sock_queue_rcv_skb(sk, skb); if (err) { @@ -131,14 +130,12 @@ static void irda_disconnect_indication(v } /* Prevent race conditions with irda_release() and irda_shutdown() */ + bh_lock_sock(sk); if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) { - lock_sock(sk); sk->sk_state = TCP_CLOSE; - sk->sk_err = ECONNRESET; sk->sk_shutdown |= SEND_SHUTDOWN; sk->sk_state_change(sk); - release_sock(sk); /* Close our TSAP. * If we leave it open, IrLMP put it back into the list of @@ -158,6 +155,7 @@ static void irda_disconnect_indication(v self->tsap = NULL; } } + bh_unlock_sock(sk); /* Note : once we are there, there is not much you want to do * with the socket anymore, apart from closing it. @@ -220,7 +218,7 @@ static void irda_connect_confirm(void *i break; default: self->max_data_size = irttp_get_max_seg_size(self->tsap); - }; + } IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __FUNCTION__, self->max_data_size); @@ -283,7 +281,7 @@ static void irda_connect_indication(void break; default: self->max_data_size = irttp_get_max_seg_size(self->tsap); - }; + } IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __FUNCTION__, self->max_data_size); @@ -306,8 +304,6 @@ static void irda_connect_response(struct IRDA_DEBUG(2, "%s()\n", __FUNCTION__); - IRDA_ASSERT(self != NULL, return;); - skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_ATOMIC); if (skb == NULL) { @@ -337,7 +333,7 @@ static void irda_flow_indication(void *i self = instance; sk = instance; - IRDA_ASSERT(sk != NULL, return;); + BUG_ON(sk == NULL); switch (flow) { case FLOW_STOP: @@ -449,7 +445,7 @@ static void irda_discovery_timeout(u_lon IRDA_DEBUG(2, "%s()\n", __FUNCTION__); self = (struct irda_sock *) priv; - IRDA_ASSERT(self != NULL, return;); + BUG_ON(self == NULL); /* Nothing for the caller */ self->cachelog = NULL; @@ -546,8 +542,6 @@ static int irda_find_lsap_sel(struct ird { IRDA_DEBUG(2, "%s(%p, %s)\n", __FUNCTION__, self, name); - IRDA_ASSERT(self != NULL, return -1;); - if (self->iriap) { IRDA_WARNING("%s(): busy with a previous query\n", __FUNCTION__); @@ -635,8 +629,6 @@ static int irda_discover_daddr_and_lsap_ IRDA_DEBUG(2, "%s(), name=%s\n", __FUNCTION__, name); - IRDA_ASSERT(self != NULL, return -1;); - /* Ask lmp for the current discovery log * Note : we have to use irlmp_get_discoveries(), as opposed * to play with the cachelog directly, because while we are @@ -784,8 +776,6 @@ static int irda_bind(struct socket *sock struct irda_sock *self = irda_sk(sk); int err; - IRDA_ASSERT(self != NULL, return -1;); - IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self); if (addr_len != sizeof(struct sockaddr_irda)) @@ -841,8 +831,6 @@ static int irda_accept(struct socket *so IRDA_DEBUG(2, "%s()\n", __FUNCTION__); - IRDA_ASSERT(self != NULL, return -1;); - err = irda_create(newsock, sk->sk_protocol); if (err) return err; @@ -873,44 +861,28 @@ static int irda_accept(struct socket *so * calling us, the data is waiting for us ;-) * Jean II */ - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb == NULL) { - int ret = 0; - DECLARE_WAITQUEUE(waitq, current); + while (1) { + skb = skb_dequeue(&sk->sk_receive_queue); + if (skb) + break; /* Non blocking operation */ if (flags & O_NONBLOCK) return -EWOULDBLOCK; - /* The following code is a cut'n'paste of the - * wait_event_interruptible() macro. - * We don't us the macro because the condition has - * side effects : we want to make sure that only one - * skb get dequeued - Jean II */ - add_wait_queue(sk->sk_sleep, &waitq); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb != NULL) - break; - if (!signal_pending(current)) { - schedule(); - continue; - } - ret = -ERESTARTSYS; - break; - } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &waitq); - if(ret) - return -ERESTARTSYS; + err = wait_event_interruptible(*(sk->sk_sleep), + skb_peek(&sk->sk_receive_queue)); + if (err) + return err; } newsk = newsock->sk; + if (newsk == NULL) + return -EIO; + newsk->sk_state = TCP_ESTABLISHED; new = irda_sk(newsk); - IRDA_ASSERT(new != NULL, return -1;); /* Now attach up the new socket */ new->tsap = irttp_dup(self->tsap, new); @@ -1061,7 +1033,8 @@ static int irda_connect(struct socket *s if (sk->sk_state != TCP_ESTABLISHED) { sock->state = SS_UNCONNECTED; - return sock_error(sk); /* Always set at this point */ + err = sock_error(sk); + return err? err : -ECONNRESET; } sock->state = SS_CONNECTED; @@ -1171,8 +1144,6 @@ static void irda_destroy_socket(struct i { IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self); - IRDA_ASSERT(self != NULL, return;); - /* Unregister with IrLMP */ irlmp_unregister_client(self->ckey); irlmp_unregister_service(self->skey); @@ -1274,7 +1245,6 @@ static int irda_sendmsg(struct kiocb *io struct sock *sk = sock->sk; struct irda_sock *self; struct sk_buff *skb; - unsigned char *asmptr; int err; IRDA_DEBUG(4, "%s(), len=%zd\n", __FUNCTION__, len); @@ -1292,7 +1262,6 @@ static int irda_sendmsg(struct kiocb *io return -ENOTCONN; self = irda_sk(sk); - IRDA_ASSERT(self != NULL, return -1;); /* Check if IrTTP is wants us to slow down */ @@ -1317,9 +1286,9 @@ static int irda_sendmsg(struct kiocb *io return -ENOBUFS; skb_reserve(skb, self->max_header_size + 16); - - asmptr = skb->h.raw = skb_put(skb, len); - err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + skb_reset_transport_header(skb); + skb_put(skb, len); + err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); if (err) { kfree_skb(skb); return err; @@ -1355,16 +1324,16 @@ static int irda_recvmsg_dgram(struct kio IRDA_DEBUG(4, "%s()\n", __FUNCTION__); - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(!sock_error(sk), return -1;); + if ((err = sock_error(sk)) < 0) + return err; skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); if (!skb) return err; - skb->h.raw = skb->data; - copied = skb->len; + skb_reset_transport_header(skb); + copied = skb->len; if (copied > size) { IRDA_DEBUG(2, "%s(), Received truncated frame (%zd < %zd)!\n", @@ -1403,13 +1372,13 @@ static int irda_recvmsg_stream(struct ki struct irda_sock *self = irda_sk(sk); int noblock = flags & MSG_DONTWAIT; size_t copied = 0; - int target = 1; - DECLARE_WAITQUEUE(waitq, current); + int target, err; + long timeo; IRDA_DEBUG(3, "%s()\n", __FUNCTION__); - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(!sock_error(sk), return -1;); + if ((err = sock_error(sk)) < 0) + return err; if (sock->flags & __SO_ACCEPTCON) return(-EINVAL); @@ -1417,8 +1386,8 @@ static int irda_recvmsg_stream(struct ki if (flags & MSG_OOB) return -EOPNOTSUPP; - if (flags & MSG_WAITALL) - target = size; + target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); + timeo = sock_rcvtimeo(sk, noblock); msg->msg_namelen = 0; @@ -1426,19 +1395,14 @@ static int irda_recvmsg_stream(struct ki int chunk; struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); - if (skb==NULL) { + if (skb == NULL) { + DEFINE_WAIT(wait); int ret = 0; if (copied >= target) break; - /* The following code is a cut'n'paste of the - * wait_event_interruptible() macro. - * We don't us the macro because the test condition - * is messy. - Jean II */ - set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); - add_wait_queue(sk->sk_sleep, &waitq); - set_current_state(TASK_INTERRUPTIBLE); + prepare_to_wait_exclusive(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); /* * POSIX 1003.1g mandates this order. @@ -1451,17 +1415,17 @@ static int irda_recvmsg_stream(struct ki else if (noblock) ret = -EAGAIN; else if (signal_pending(current)) - ret = -ERESTARTSYS; + ret = sock_intr_errno(timeo); + else if (sk->sk_state != TCP_ESTABLISHED) + ret = -ENOTCONN; else if (skb_peek(&sk->sk_receive_queue) == NULL) /* Wait process until data arrives */ schedule(); - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &waitq); - clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); + finish_wait(sk->sk_sleep, &wait); - if(ret) - return(ret); + if (ret) + return ret; if (sk->sk_shutdown & RCV_SHUTDOWN) break; @@ -1530,7 +1494,6 @@ static int irda_sendmsg_dgram(struct kio struct sock *sk = sock->sk; struct irda_sock *self; struct sk_buff *skb; - unsigned char *asmptr; int err; IRDA_DEBUG(4, "%s(), len=%zd\n", __FUNCTION__, len); @@ -1547,7 +1510,6 @@ static int irda_sendmsg_dgram(struct kio return -ENOTCONN; self = irda_sk(sk); - IRDA_ASSERT(self != NULL, return -1;); /* * Check that we don't send out too big frames. This is an unreliable @@ -1566,10 +1528,11 @@ static int irda_sendmsg_dgram(struct kio return -ENOBUFS; skb_reserve(skb, self->max_header_size); + skb_reset_transport_header(skb); IRDA_DEBUG(4, "%s(), appending user data\n", __FUNCTION__); - asmptr = skb->h.raw = skb_put(skb, len); - err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + skb_put(skb, len); + err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); if (err) { kfree_skb(skb); return err; @@ -1602,7 +1565,6 @@ static int irda_sendmsg_ultra(struct kio __u8 pid = 0; int bound = 0; struct sk_buff *skb; - unsigned char *asmptr; int err; IRDA_DEBUG(4, "%s(), len=%zd\n", __FUNCTION__, len); @@ -1616,7 +1578,6 @@ static int irda_sendmsg_ultra(struct kio } self = irda_sk(sk); - IRDA_ASSERT(self != NULL, return -1;); /* Check if an address was specified with sendto. Jean II */ if (msg->msg_name) { @@ -1662,10 +1623,11 @@ static int irda_sendmsg_ultra(struct kio return -ENOBUFS; skb_reserve(skb, self->max_header_size); + skb_reset_transport_header(skb); IRDA_DEBUG(4, "%s(), appending user data\n", __FUNCTION__); - asmptr = skb->h.raw = skb_put(skb, len); - err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + skb_put(skb, len); + err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); if (err) { kfree_skb(skb); return err; @@ -1689,8 +1651,6 @@ static int irda_shutdown(struct socket * struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk); - IRDA_ASSERT(self != NULL, return -1;); - IRDA_DEBUG(1, "%s(%p)\n", __FUNCTION__, self); sk->sk_state = TCP_CLOSE; @@ -1863,8 +1823,6 @@ static int irda_setsockopt(struct socket struct ias_attrib * ias_attr; /* Attribute in IAS object */ int opt; - IRDA_ASSERT(self != NULL, return -1;); - IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self); if (level != SOL_IRLMP) diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c index 01d7c9c..e5e4792 100644 --- a/net/irda/ircomm/ircomm_param.c +++ b/net/irda/ircomm/ircomm_param.c @@ -133,8 +133,8 @@ int ircomm_param_request(struct ircomm_t * Inserting is a little bit tricky since we don't know how much * room we will need. But this should hopefully work OK */ - count = irda_param_insert(self, pi, skb->tail, skb_tailroom(skb), - &ircomm_param_info); + count = irda_param_insert(self, pi, skb_tail_pointer(skb), + skb_tailroom(skb), &ircomm_param_info); if (count < 0) { IRDA_WARNING("%s(), no room for parameter!\n", __FUNCTION__); spin_unlock_irqrestore(&self->spinlock, flags); diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index e717801..7b5def1 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -375,7 +375,7 @@ EXPORT_SYMBOL(alloc_irdadev); dongle_t *irda_device_dongle_init(struct net_device *dev, int type) { struct dongle_reg *reg; - dongle_t *dongle = NULL; + dongle_t *dongle = kzalloc(sizeof(dongle_t), GFP_KERNEL); might_sleep(); @@ -397,19 +397,14 @@ #endif if (!reg || !try_module_get(reg->owner) ) { IRDA_ERROR("IrDA: Unable to find requested dongle type %x\n", type); - goto out; + kfree(dongle); + dongle = NULL; + } + if (dongle) { + /* Bind the registration info to this particular instance */ + dongle->issue = reg; + dongle->dev = dev; } - - /* Allocate dongle info for this instance */ - dongle = kzalloc(sizeof(dongle_t), GFP_KERNEL); - if (!dongle) - goto out; - - /* Bind the registration info to this particular instance */ - dongle->issue = reg; - dongle->dev = dev; - - out: spin_unlock(&dongles->hb_spinlock); return dongle; } diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index fcf9d65..ed69773 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -1039,7 +1039,7 @@ static int __irlan_insert_param(struct s } /* Insert at end of sk-buffer */ - frame = skb->tail; + frame = skb_tail_pointer(skb); /* Make space for data */ if (skb_tailroom(skb) < (param_len+value_len+3)) { diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 672ab3f..c421521 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -234,8 +234,7 @@ int irlan_eth_receive(void *instance, vo * might have been previously set by the low level IrDA network * device driver */ - skb->dev = self->dev; - skb->protocol=eth_type_trans(skb, skb->dev); /* Remove eth header */ + skb->protocol = eth_type_trans(skb, self->dev); /* Remove eth header */ self->stats.rx_packets++; self->stats.rx_bytes += skb->len; diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c index 7b6433f..0b02073 100644 --- a/net/irda/irlap_event.c +++ b/net/irda/irlap_event.c @@ -590,7 +590,7 @@ static int irlap_state_query(struct irla if (!self->discovery_log) { IRDA_WARNING("%s: discovery log is gone! " "maybe the discovery timeout has been set" - " to short?\n", __FUNCTION__); + " too short?\n", __FUNCTION__); break; } hashbin_insert(self->discovery_log, diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c index 0b04603..3c5a68e 100644 --- a/net/irda/irlap_frame.c +++ b/net/irda/irlap_frame.c @@ -93,7 +93,9 @@ void irlap_queue_xmit(struct irlap_cb *s { /* Some common init stuff */ skb->dev = self->netdev; - skb->h.raw = skb->nh.raw = skb->mac.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); skb->protocol = htons(ETH_P_IRDA); skb->priority = TC_PRIO_BESTEFFORT; @@ -411,7 +413,7 @@ static void irlap_recv_discovery_xid_rsp IRDA_ASSERT(self->magic == LAP_MAGIC, return;); if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - IRDA_ERROR("%s: frame to short!\n", __FUNCTION__); + IRDA_ERROR("%s: frame too short!\n", __FUNCTION__); return; } @@ -482,7 +484,7 @@ static void irlap_recv_discovery_xid_cmd char *text; if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - IRDA_ERROR("%s: frame to short!\n", __FUNCTION__); + IRDA_ERROR("%s: frame too short!\n", __FUNCTION__); return; } @@ -526,7 +528,7 @@ static void irlap_recv_discovery_xid_cmd /* Check if things are sane at this point... */ if((discovery_info == NULL) || !pskb_may_pull(skb, 3)) { - IRDA_ERROR("%s: discovery frame to short!\n", + IRDA_ERROR("%s: discovery frame too short!\n", __FUNCTION__); return; } @@ -1171,7 +1173,7 @@ static void irlap_recv_frmr_frame(struct IRDA_ASSERT(info != NULL, return;); if (!pskb_may_pull(skb, 4)) { - IRDA_ERROR("%s: frame to short!\n", __FUNCTION__); + IRDA_ERROR("%s: frame too short!\n", __FUNCTION__); return; } @@ -1260,7 +1262,7 @@ static void irlap_recv_test_frame(struct IRDA_DEBUG(2, "%s()\n", __FUNCTION__); if (!pskb_may_pull(skb, sizeof(*frame))) { - IRDA_ERROR("%s: frame to short!\n", __FUNCTION__); + IRDA_ERROR("%s: frame too short!\n", __FUNCTION__); return; } frame = (struct test_frame *) skb->data; @@ -1268,7 +1270,7 @@ static void irlap_recv_test_frame(struct /* Broadcast frames must carry saddr and daddr fields */ if (info->caddr == CBROADCAST) { if (skb->len < sizeof(struct test_frame)) { - IRDA_DEBUG(0, "%s() test frame to short!\n", + IRDA_DEBUG(0, "%s() test frame too short!\n", __FUNCTION__); return; } @@ -1334,7 +1336,7 @@ int irlap_driver_rcv(struct sk_buff *skb /* Check if frame is large enough for parsing */ if (!pskb_may_pull(skb, 2)) { - IRDA_ERROR("%s: frame to short!\n", __FUNCTION__); + IRDA_ERROR("%s: frame too short!\n", __FUNCTION__); dev_kfree_skb(skb); return -1; } diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c index 9266233..d058b46 100644 --- a/net/irda/irqueue.c +++ b/net/irda/irqueue.c @@ -384,6 +384,9 @@ EXPORT_SYMBOL(hashbin_new); * for deallocating this structure if it's complex. If not the user can * just supply kfree, which should take care of the job. */ +#ifdef CONFIG_LOCKDEP +static int hashbin_lock_depth = 0; +#endif int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) { irda_queue_t* queue; @@ -395,7 +398,8 @@ int hashbin_delete( hashbin_t* hashbin, /* Synchronize */ if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); + spin_lock_irqsave_nested(&hashbin->hb_spinlock, flags, + hashbin_lock_depth++); } /* @@ -419,6 +423,9 @@ int hashbin_delete( hashbin_t* hashbin, /* Release lock */ if ( hashbin->hb_type & HB_LOCK) { spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); +#ifdef CONFIG_LOCKDEP + hashbin_lock_depth--; +#endif } /* diff --git a/net/irda/irttp.c b/net/irda/irttp.c index da3f2bc..7069e4a 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -256,7 +256,7 @@ static struct sk_buff *irttp_reassemble_ * Copy all fragments to a new buffer */ while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) { - memcpy(skb->data+n, frag->data, frag->len); + skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len); n += frag->len; dev_kfree_skb(frag); @@ -314,8 +314,8 @@ static inline void irttp_fragment_skb(st skb_reserve(frag, self->max_header_size); /* Copy data from the original skb into this fragment. */ - memcpy(skb_put(frag, self->max_seg_size), skb->data, - self->max_seg_size); + skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size), + self->max_seg_size); /* Insert TTP header, with the more bit set */ frame = skb_push(frag, TTP_HEADER); @@ -551,7 +551,7 @@ int irttp_udata_request(struct tsap_cb * } if (skb->len > self->max_seg_size) { - IRDA_DEBUG(1, "%s(), UData is to large for IrLAP!\n", + IRDA_DEBUG(1, "%s(), UData is too large for IrLAP!\n", __FUNCTION__); goto err; } @@ -598,7 +598,7 @@ int irttp_data_request(struct tsap_cb *s * inside an IrLAP frame */ if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) { - IRDA_ERROR("%s: SAR disabled, and data is to large for IrLAP!\n", + IRDA_ERROR("%s: SAR disabled, and data is too large for IrLAP!\n", __FUNCTION__); ret = -EMSGSIZE; goto err; diff --git a/net/irda/parameters.c b/net/irda/parameters.c index 75a72d2..2627dad 100644 --- a/net/irda/parameters.c +++ b/net/irda/parameters.c @@ -160,7 +160,7 @@ static int irda_insert_integer(void *sel } /* Check if buffer is long enough for insertion */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer to short for insertion!\n", + IRDA_WARNING("%s: buffer too short for insertion!\n", __FUNCTION__); return -1; } @@ -216,7 +216,7 @@ static int irda_extract_integer(void *se /* Check if buffer is long enough for parsing */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer to short for parsing! " + IRDA_WARNING("%s: buffer too short for parsing! " "Need %d bytes, but len is only %d\n", __FUNCTION__, p.pl, len); return -1; @@ -304,7 +304,7 @@ static int irda_extract_string(void *sel /* Check if buffer is long enough for parsing */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer to short for parsing! " + IRDA_WARNING("%s: buffer too short for parsing! " "Need %d bytes, but len is only %d\n", __FUNCTION__, p.pl, len); return -1; @@ -343,7 +343,7 @@ static int irda_extract_octseq(void *sel /* Check if buffer is long enough for parsing */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer to short for parsing! " + IRDA_WARNING("%s: buffer too short for parsing! " "Need %d bytes, but len is only %d\n", __FUNCTION__, p.pl, len); return -1; diff --git a/net/irda/qos.c b/net/irda/qos.c index 349012c..aeb18cf 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -469,49 +469,49 @@ int irlap_insert_qos_negotiation_params( int ret; /* Insert data rate */ - ret = irda_param_insert(self, PI_BAUD_RATE, skb->tail, + ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb), skb_tailroom(skb), &irlap_param_info); if (ret < 0) return ret; skb_put(skb, ret); /* Insert max turnaround time */ - ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb->tail, + ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb), skb_tailroom(skb), &irlap_param_info); if (ret < 0) return ret; skb_put(skb, ret); /* Insert data size */ - ret = irda_param_insert(self, PI_DATA_SIZE, skb->tail, + ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb), skb_tailroom(skb), &irlap_param_info); if (ret < 0) return ret; skb_put(skb, ret); /* Insert window size */ - ret = irda_param_insert(self, PI_WINDOW_SIZE, skb->tail, + ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb), skb_tailroom(skb), &irlap_param_info); if (ret < 0) return ret; skb_put(skb, ret); /* Insert additional BOFs */ - ret = irda_param_insert(self, PI_ADD_BOFS, skb->tail, + ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb), skb_tailroom(skb), &irlap_param_info); if (ret < 0) return ret; skb_put(skb, ret); /* Insert minimum turnaround time */ - ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb->tail, + ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb), skb_tailroom(skb), &irlap_param_info); if (ret < 0) return ret; skb_put(skb, ret); /* Insert link disconnect/threshold time */ - ret = irda_param_insert(self, PI_LINK_DISC, skb->tail, + ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb), skb_tailroom(skb), &irlap_param_info); if (ret < 0) return ret; diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c index 5abfb71..a7a7f19 100644 --- a/net/irda/wrapper.c +++ b/net/irda/wrapper.c @@ -239,7 +239,8 @@ async_bump(struct net_device *dev, if(docopy) { /* Copy data without CRC (lenght already checked) */ - memcpy(newskb->data, rx_buff->data, rx_buff->len - 2); + skb_copy_to_linear_data(newskb, rx_buff->data, + rx_buff->len - 2); /* Deliver this skb */ dataskb = newskb; } else { @@ -256,7 +257,7 @@ async_bump(struct net_device *dev, /* Feed it to IrLAP layer */ dataskb->dev = dev; - dataskb->mac.raw = dataskb->data; + skb_reset_mac_header(dataskb); dataskb->protocol = htons(ETH_P_IRDA); netif_rx(dataskb); diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index acc9421..e84c924 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -181,7 +181,7 @@ static void iucv_sock_close(struct sock default: sock_set_flag(sk, SOCK_ZAPPED); break; - }; + } release_sock(sk); iucv_sock_kill(sk); @@ -953,8 +953,8 @@ static void iucv_callback_rx(struct iucv return; } - skb->h.raw = skb->data; - skb->nh.raw = skb->data; + skb_reset_transport_header(skb); + skb_reset_network_header(skb); skb->len = msg->length; } diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 1b10d57..903bdb6 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -90,20 +90,43 @@ struct iucv_irq_data { u32 res2[8]; }; -struct iucv_work { +struct iucv_irq_list { struct list_head list; struct iucv_irq_data data; }; -static LIST_HEAD(iucv_work_queue); -static DEFINE_SPINLOCK(iucv_work_lock); - static struct iucv_irq_data *iucv_irq_data; static cpumask_t iucv_buffer_cpumask = CPU_MASK_NONE; static cpumask_t iucv_irq_cpumask = CPU_MASK_NONE; -static void iucv_tasklet_handler(unsigned long); -static DECLARE_TASKLET(iucv_tasklet, iucv_tasklet_handler,0); +/* + * Queue of interrupt buffers lock for delivery via the tasklet + * (fast but can't call smp_call_function). + */ +static LIST_HEAD(iucv_task_queue); + +/* + * The tasklet for fast delivery of iucv interrupts. + */ +static void iucv_tasklet_fn(unsigned long); +static DECLARE_TASKLET(iucv_tasklet, iucv_tasklet_fn,0); + +/* + * Queue of interrupt buffers for delivery via a work queue + * (slower but can call smp_call_function). + */ +static LIST_HEAD(iucv_work_queue); + +/* + * The work element to deliver path pending interrupts. + */ +static void iucv_work_fn(struct work_struct *work); +static DECLARE_WORK(iucv_work, iucv_work_fn); + +/* + * Spinlock protecting task and work queue. + */ +static DEFINE_SPINLOCK(iucv_queue_lock); enum iucv_command_codes { IUCV_QUERY = 0, @@ -147,10 +170,10 @@ static unsigned long iucv_max_pathid; static DEFINE_SPINLOCK(iucv_table_lock); /* - * iucv_tasklet_cpu: contains the number of the cpu executing the tasklet. - * Needed for iucv_path_sever called from tasklet. + * iucv_active_cpu: contains the number of the cpu executing the tasklet + * or the work handler. Needed for iucv_path_sever called from tasklet. */ -static int iucv_tasklet_cpu = -1; +static int iucv_active_cpu = -1; /* * Mutex and wait queue for iucv_register/iucv_unregister. @@ -449,17 +472,19 @@ static void iucv_setmask_mp(void) { int cpu; + preempt_disable(); for_each_online_cpu(cpu) /* Enable all cpus with a declared buffer. */ if (cpu_isset(cpu, iucv_buffer_cpumask) && !cpu_isset(cpu, iucv_irq_cpumask)) smp_call_function_on(iucv_allow_cpu, NULL, 0, 1, cpu); + preempt_enable(); } /** * iucv_setmask_up * - * Allow iucv interrupts on a single cpus. + * Allow iucv interrupts on a single cpu. */ static void iucv_setmask_up(void) { @@ -493,8 +518,10 @@ static int iucv_enable(void) goto out; /* Declare per cpu buffers. */ rc = -EIO; + preempt_disable(); for_each_online_cpu(cpu) smp_call_function_on(iucv_declare_cpu, NULL, 0, 1, cpu); + preempt_enable(); if (cpus_empty(iucv_buffer_cpumask)) /* No cpu could declare an iucv buffer. */ goto out_path; @@ -519,7 +546,6 @@ static void iucv_disable(void) kfree(iucv_path_table); } -#ifdef CONFIG_HOTPLUG_CPU static int __cpuinit iucv_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { @@ -565,7 +591,6 @@ static int __cpuinit iucv_cpu_notify(str static struct notifier_block iucv_cpu_notifier = { .notifier_call = iucv_cpu_notify, }; -#endif /** * iucv_sever_pathid @@ -586,48 +611,49 @@ static int iucv_sever_pathid(u16 pathid, return iucv_call_b2f0(IUCV_SEVER, parm); } +#ifdef CONFIG_SMP /** - * __iucv_cleanup_pathid + * __iucv_cleanup_queue * @dummy: unused dummy argument * * Nop function called via smp_call_function to force work items from * pending external iucv interrupts to the work queue. */ -static void __iucv_cleanup_pathid(void *dummy) +static void __iucv_cleanup_queue(void *dummy) { } +#endif /** - * iucv_cleanup_pathid - * @pathid: 16 bit pathid + * iucv_cleanup_queue * * Function called after a path has been severed to find all remaining * work items for the now stale pathid. The caller needs to hold the * iucv_table_lock. */ -static void iucv_cleanup_pathid(u16 pathid) +static void iucv_cleanup_queue(void) { - struct iucv_work *p, *n; + struct iucv_irq_list *p, *n; /* - * Path is severed, the pathid can be reused immediatly on - * a iucv connect or a connection pending interrupt. - * iucv_path_connect and connection pending interrupt will - * wait until the iucv_table_lock is released before the - * recycled pathid enters the system. - * Force remaining interrupts to the work queue, then - * scan the work queue for items of this path. + * When a path is severed, the pathid can be reused immediatly + * on a iucv connect or a connection pending interrupt. Remove + * all entries from the task queue that refer to a stale pathid + * (iucv_path_table[ix] == NULL). Only then do the iucv connect + * or deliver the connection pending interrupt. To get all the + * pending interrupts force them to the work queue by calling + * an empty function on all cpus. */ - smp_call_function(__iucv_cleanup_pathid, NULL, 0, 1); - spin_lock_irq(&iucv_work_lock); - list_for_each_entry_safe(p, n, &iucv_work_queue, list) { - /* Remove work items for pathid except connection pending */ - if (p->data.ippathid == pathid && p->data.iptype != 0x01) { + smp_call_function(__iucv_cleanup_queue, NULL, 0, 1); + spin_lock_irq(&iucv_queue_lock); + list_for_each_entry_safe(p, n, &iucv_task_queue, list) { + /* Remove stale work items from the task queue. */ + if (iucv_path_table[p->data.ippathid] == NULL) { list_del(&p->list); kfree(p); } } - spin_unlock_irq(&iucv_work_lock); + spin_unlock_irq(&iucv_queue_lock); } /** @@ -686,7 +712,6 @@ void iucv_unregister(struct iucv_handler iucv_sever_pathid(p->pathid, NULL); iucv_path_table[p->pathid] = NULL; list_del(&p->list); - iucv_cleanup_pathid(p->pathid); iucv_path_free(p); } spin_unlock_bh(&iucv_table_lock); @@ -759,9 +784,9 @@ int iucv_path_connect(struct iucv_path * union iucv_param *parm; int rc; - preempt_disable(); - if (iucv_tasklet_cpu != smp_processor_id()) - spin_lock_bh(&iucv_table_lock); + BUG_ON(in_atomic()); + spin_lock_bh(&iucv_table_lock); + iucv_cleanup_queue(); parm = percpu_ptr(iucv_param, smp_processor_id()); memset(parm, 0, sizeof(union iucv_param)); parm->ctrl.ipmsglim = path->msglim; @@ -796,9 +821,7 @@ int iucv_path_connect(struct iucv_path * rc = -EIO; } } - if (iucv_tasklet_cpu != smp_processor_id()) - spin_unlock_bh(&iucv_table_lock); - preempt_enable(); + spin_unlock_bh(&iucv_table_lock); return rc; } @@ -869,15 +892,14 @@ int iucv_path_sever(struct iucv_path *pa preempt_disable(); - if (iucv_tasklet_cpu != smp_processor_id()) + if (iucv_active_cpu != smp_processor_id()) spin_lock_bh(&iucv_table_lock); rc = iucv_sever_pathid(path->pathid, userdata); if (!rc) { iucv_path_table[path->pathid] = NULL; list_del_init(&path->list); - iucv_cleanup_pathid(path->pathid); } - if (iucv_tasklet_cpu != smp_processor_id()) + if (iucv_active_cpu != smp_processor_id()) spin_unlock_bh(&iucv_table_lock); preempt_enable(); return rc; @@ -1246,8 +1268,7 @@ static void iucv_path_complete(struct iu struct iucv_path_complete *ipc = (void *) data; struct iucv_path *path = iucv_path_table[ipc->ippathid]; - BUG_ON(!path || !path->handler); - if (path->handler->path_complete) + if (path && path->handler && path->handler->path_complete) path->handler->path_complete(path, ipc->ipuser); } @@ -1275,14 +1296,14 @@ static void iucv_path_severed(struct iuc struct iucv_path_severed *ips = (void *) data; struct iucv_path *path = iucv_path_table[ips->ippathid]; - BUG_ON(!path || !path->handler); + if (!path || !path->handler) /* Already severed */ + return; if (path->handler->path_severed) path->handler->path_severed(path, ips->ipuser); else { iucv_sever_pathid(path->pathid, NULL); iucv_path_table[path->pathid] = NULL; list_del_init(&path->list); - iucv_cleanup_pathid(path->pathid); iucv_path_free(path); } } @@ -1311,8 +1332,7 @@ static void iucv_path_quiesced(struct iu struct iucv_path_quiesced *ipq = (void *) data; struct iucv_path *path = iucv_path_table[ipq->ippathid]; - BUG_ON(!path || !path->handler); - if (path->handler->path_quiesced) + if (path && path->handler && path->handler->path_quiesced) path->handler->path_quiesced(path, ipq->ipuser); } @@ -1340,8 +1360,7 @@ static void iucv_path_resumed(struct iuc struct iucv_path_resumed *ipr = (void *) data; struct iucv_path *path = iucv_path_table[ipr->ippathid]; - BUG_ON(!path || !path->handler); - if (path->handler->path_resumed) + if (path && path->handler && path->handler->path_resumed) path->handler->path_resumed(path, ipr->ipuser); } @@ -1373,8 +1392,7 @@ static void iucv_message_complete(struct struct iucv_path *path = iucv_path_table[imc->ippathid]; struct iucv_message msg; - BUG_ON(!path || !path->handler); - if (path->handler->message_complete) { + if (path && path->handler && path->handler->message_complete) { msg.flags = imc->ipflags1; msg.id = imc->ipmsgid; msg.audit = imc->ipaudit; @@ -1419,8 +1437,7 @@ static void iucv_message_pending(struct struct iucv_path *path = iucv_path_table[imp->ippathid]; struct iucv_message msg; - BUG_ON(!path || !path->handler); - if (path->handler->message_pending) { + if (path && path->handler && path->handler->message_pending) { msg.flags = imp->ipflags1; msg.id = imp->ipmsgid; msg.class = imp->iptrgcls; @@ -1435,17 +1452,16 @@ static void iucv_message_pending(struct } /** - * iucv_tasklet_handler: + * iucv_tasklet_fn: * * This tasklet loops over the queue of irq buffers created by * iucv_external_interrupt, calls the appropriate action handler * and then frees the buffer. */ -static void iucv_tasklet_handler(unsigned long ignored) +static void iucv_tasklet_fn(unsigned long ignored) { typedef void iucv_irq_fn(struct iucv_irq_data *); static iucv_irq_fn *irq_fn[] = { - [0x01] = iucv_path_pending, [0x02] = iucv_path_complete, [0x03] = iucv_path_severed, [0x04] = iucv_path_quiesced, @@ -1455,38 +1471,70 @@ static void iucv_tasklet_handler(unsigne [0x08] = iucv_message_pending, [0x09] = iucv_message_pending, }; - struct iucv_work *p; + struct list_head task_queue = LIST_HEAD_INIT(task_queue); + struct iucv_irq_list *p, *n; /* Serialize tasklet, iucv_path_sever and iucv_path_connect. */ spin_lock(&iucv_table_lock); - iucv_tasklet_cpu = smp_processor_id(); + iucv_active_cpu = smp_processor_id(); - spin_lock_irq(&iucv_work_lock); - while (!list_empty(&iucv_work_queue)) { - p = list_entry(iucv_work_queue.next, struct iucv_work, list); + spin_lock_irq(&iucv_queue_lock); + list_splice_init(&iucv_task_queue, &task_queue); + spin_unlock_irq(&iucv_queue_lock); + + list_for_each_entry_safe(p, n, &task_queue, list) { list_del_init(&p->list); - spin_unlock_irq(&iucv_work_lock); irq_fn[p->data.iptype](&p->data); kfree(p); - spin_lock_irq(&iucv_work_lock); } - spin_unlock_irq(&iucv_work_lock); - iucv_tasklet_cpu = -1; + iucv_active_cpu = -1; spin_unlock(&iucv_table_lock); } /** + * iucv_work_fn: + * + * This work function loops over the queue of path pending irq blocks + * created by iucv_external_interrupt, calls the appropriate action + * handler and then frees the buffer. + */ +static void iucv_work_fn(struct work_struct *work) +{ + typedef void iucv_irq_fn(struct iucv_irq_data *); + struct list_head work_queue = LIST_HEAD_INIT(work_queue); + struct iucv_irq_list *p, *n; + + /* Serialize tasklet, iucv_path_sever and iucv_path_connect. */ + spin_lock_bh(&iucv_table_lock); + iucv_active_cpu = smp_processor_id(); + + spin_lock_irq(&iucv_queue_lock); + list_splice_init(&iucv_work_queue, &work_queue); + spin_unlock_irq(&iucv_queue_lock); + + iucv_cleanup_queue(); + list_for_each_entry_safe(p, n, &work_queue, list) { + list_del_init(&p->list); + iucv_path_pending(&p->data); + kfree(p); + } + + iucv_active_cpu = -1; + spin_unlock_bh(&iucv_table_lock); +} + +/** * iucv_external_interrupt * @code: irq code * * Handles external interrupts coming in from CP. - * Places the interrupt buffer on a queue and schedules iucv_tasklet_handler(). + * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn(). */ static void iucv_external_interrupt(u16 code) { struct iucv_irq_data *p; - struct iucv_work *work; + struct iucv_irq_list *work; p = percpu_ptr(iucv_irq_data, smp_processor_id()); if (p->ippathid >= iucv_max_pathid) { @@ -1500,16 +1548,23 @@ static void iucv_external_interrupt(u16 printk(KERN_ERR "iucv_do_int: unknown iucv interrupt\n"); return; } - work = kmalloc(sizeof(struct iucv_work), GFP_ATOMIC); + work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC); if (!work) { printk(KERN_WARNING "iucv_external_interrupt: out of memory\n"); return; } memcpy(&work->data, p, sizeof(work->data)); - spin_lock(&iucv_work_lock); - list_add_tail(&work->list, &iucv_work_queue); - spin_unlock(&iucv_work_lock); - tasklet_schedule(&iucv_tasklet); + spin_lock(&iucv_queue_lock); + if (p->iptype == 0x01) { + /* Path pending interrupt. */ + list_add_tail(&work->list, &iucv_work_queue); + schedule_work(&iucv_work); + } else { + /* The other interrupts. */ + list_add_tail(&work->list, &iucv_task_queue); + tasklet_schedule(&iucv_tasklet); + } + spin_unlock(&iucv_queue_lock); } /** @@ -1579,12 +1634,14 @@ out: */ static void iucv_exit(void) { - struct iucv_work *p, *n; + struct iucv_irq_list *p, *n; - spin_lock_irq(&iucv_work_lock); + spin_lock_irq(&iucv_queue_lock); + list_for_each_entry_safe(p, n, &iucv_task_queue, list) + kfree(p); list_for_each_entry_safe(p, n, &iucv_work_queue, list) kfree(p); - spin_unlock_irq(&iucv_work_lock); + spin_unlock_irq(&iucv_queue_lock); unregister_hotcpu_notifier(&iucv_cpu_notifier); percpu_free(iucv_param); percpu_free(iucv_irq_data); diff --git a/net/key/af_key.c b/net/key/af_key.c index 3450193..a994441 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -379,7 +379,7 @@ #endif */ return -EINVAL; break; - }; + } return 0; } @@ -3667,7 +3667,7 @@ static int pfkey_recvmsg(struct kiocb *k copied = len; } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (err) goto out_free; diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c index b3f65d1..099ed8f 100644 --- a/net/llc/llc_input.c +++ b/net/llc/llc_input.c @@ -112,7 +112,7 @@ static inline int llc_fixup_skb(struct s if (unlikely(!pskb_may_pull(skb, llc_len))) return 0; - skb->h.raw += llc_len; + skb->transport_header += llc_len; skb_pull(skb, llc_len); if (skb->protocol == htons(ETH_P_802_2)) { __be16 pdulen = eth_hdr(skb)->h_proto; diff --git a/net/llc/llc_output.c b/net/llc/llc_output.c index f4291f3..754f4fe 100644 --- a/net/llc/llc_output.c +++ b/net/llc/llc_output.c @@ -41,7 +41,8 @@ #ifdef CONFIG_TR struct net_device *dev = skb->dev; struct trh_hdr *trh; - skb->mac.raw = skb_push(skb, sizeof(*trh)); + skb_push(skb, sizeof(*trh)); + skb_reset_mac_header(skb); trh = tr_hdr(skb); trh->ac = AC; trh->fc = LLC_FRAME; @@ -52,7 +53,7 @@ #ifdef CONFIG_TR if (da) { memcpy(trh->daddr, da, dev->addr_len); tr_source_route(skb, trh, dev); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); } break; } @@ -62,7 +63,8 @@ #endif unsigned short len = skb->len; struct ethhdr *eth; - skb->mac.raw = skb_push(skb, sizeof(*eth)); + skb_push(skb, sizeof(*eth)); + skb_reset_mac_header(skb); eth = eth_hdr(skb); eth->h_proto = htons(len); memcpy(eth->h_dest, da, ETH_ALEN); diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index 2615dc8..2525165 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c @@ -36,11 +36,12 @@ struct sk_buff *llc_alloc_frame(struct s struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC); if (skb) { + skb_reset_mac_header(skb); skb_reserve(skb, 50); - skb->nh.raw = skb->h.raw = skb->data; + skb_reset_network_header(skb); + skb_reset_transport_header(skb); skb->protocol = htons(ETH_P_802_2); skb->dev = dev; - skb->mac.raw = skb->head; if (sk != NULL) skb_set_owner_w(skb, sk); } diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 54698af..c558f32 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -25,6 +25,7 @@ config NETFILTER_NETLINK_LOG and is also scheduled to replace the old syslog-based ipt_LOG and ip6t_LOG modules. +# Rename this to NF_CONNTRACK in a 2.6.25 config NF_CONNTRACK_ENABLED tristate "Netfilter connection tracking support" help @@ -39,42 +40,9 @@ config NF_CONNTRACK_ENABLED To compile it as a module, choose M here. If unsure, say N. -choice - prompt "Netfilter connection tracking support" - depends on NF_CONNTRACK_ENABLED - -config NF_CONNTRACK_SUPPORT - bool "Layer 3 Independent Connection tracking" - help - Layer 3 independent connection tracking is experimental scheme - which generalize ip_conntrack to support other layer 3 protocols. - - This is required to do Masquerading or other kinds of Network - Address Translation (except for Fast NAT). It can also be used to - enhance packet filtering (see `Connection state match support' - below). - -config IP_NF_CONNTRACK_SUPPORT - bool "Layer 3 Dependent Connection tracking (OBSOLETE)" - help - The old, Layer 3 dependent ip_conntrack subsystem of netfilter. - - This is required to do Masquerading or other kinds of Network - Address Translation (except for Fast NAT). It can also be used to - enhance packet filtering (see `Connection state match support' - below). - -endchoice - config NF_CONNTRACK tristate - default m if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m - default y if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y - -config IP_NF_CONNTRACK - tristate - default m if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m - default y if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y + default NF_CONNTRACK_ENABLED config NF_CT_ACCT bool "Connection tracking flow accounting" @@ -303,9 +271,8 @@ config NETFILTER_XT_TARGET_CONNMARK tristate '"CONNMARK" target support' depends on NETFILTER_XTABLES depends on IP_NF_MANGLE || IP6_NF_MANGLE - depends on IP_NF_CONNTRACK || NF_CONNTRACK - select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK - select NF_CONNTRACK_MARK if NF_CONNTRACK + depends on NF_CONNTRACK + select NF_CONNTRACK_MARK help This option adds a `CONNMARK' target, which allows one to manipulate the connection mark value. Similar to the MARK target, but @@ -366,7 +333,7 @@ config NETFILTER_XT_TARGET_NOTRACK tristate '"NOTRACK" target support' depends on NETFILTER_XTABLES depends on IP_NF_RAW || IP6_NF_RAW - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help The NOTRACK target allows a select rule to specify which packets *not* to enter the conntrack/NAT @@ -387,9 +354,7 @@ config NETFILTER_XT_TARGET_SECMARK config NETFILTER_XT_TARGET_CONNSECMARK tristate '"CONNSECMARK" target support' - depends on NETFILTER_XTABLES && \ - ((NF_CONNTRACK && NF_CONNTRACK_SECMARK) || \ - (IP_NF_CONNTRACK && IP_NF_CONNTRACK_SECMARK)) + depends on NETFILTER_XTABLES && NF_CONNTRACK && NF_CONNTRACK_SECMARK help The CONNSECMARK target copies security markings from packets to connections, and restores security markings from connections @@ -437,9 +402,8 @@ config NETFILTER_XT_MATCH_COMMENT config NETFILTER_XT_MATCH_CONNBYTES tristate '"connbytes" per-connection counter match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK - select IP_NF_CT_ACCT if IP_NF_CONNTRACK - select NF_CT_ACCT if NF_CONNTRACK + depends on NF_CONNTRACK + select NF_CT_ACCT help This option adds a `connbytes' match, which allows you to match the number of bytes and/or packets for each direction within a connection. @@ -450,9 +414,8 @@ config NETFILTER_XT_MATCH_CONNBYTES config NETFILTER_XT_MATCH_CONNMARK tristate '"connmark" connection mark match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK - select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK - select NF_CONNTRACK_MARK if NF_CONNTRACK + depends on NF_CONNTRACK + select NF_CONNTRACK_MARK help This option adds a `connmark' match, which allows you to match the connection mark value previously set for the session by `CONNMARK'. @@ -464,7 +427,7 @@ config NETFILTER_XT_MATCH_CONNMARK config NETFILTER_XT_MATCH_CONNTRACK tristate '"conntrack" connection tracking match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help This is a general conntrack match module, a superset of the state match. @@ -508,7 +471,7 @@ config NETFILTER_XT_MATCH_ESP config NETFILTER_XT_MATCH_HELPER tristate '"helper" match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help Helper matching allows you to match packets in dynamic connections tracked by a conntrack-helper, ie. ip_conntrack_ftp @@ -632,7 +595,7 @@ config NETFILTER_XT_MATCH_SCTP config NETFILTER_XT_MATCH_STATE tristate '"state" match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help Connection state matching allows you to match packets based on their relationship to a tracked connection (ie. previous packets). This diff --git a/net/netfilter/core.c b/net/netfilter/core.c index c3ebdbd..a84478e 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -5,10 +5,6 @@ * way. * * Rusty Russell (C)2000 -- This code is GPL. - * - * February 2000: Modified by James Morris to have 1 queue per protocol. - * 15-Mar-2000: Added NF_REPEAT --RR. - * 08-May-2003: Internal logging interface added by Jozsef Kadlecsik. */ #include #include @@ -244,6 +240,7 @@ void nf_proto_csum_replace4(__sum16 *sum } EXPORT_SYMBOL(nf_proto_csum_replace4); +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) /* This does not belong here, but locally generated errors need it if connection tracking in use: without this, connection may not be in hash table, and hence manufactured ICMP or RST packets will not be associated with it. */ @@ -264,6 +261,22 @@ void nf_ct_attach(struct sk_buff *new, s } EXPORT_SYMBOL(nf_ct_attach); +void (*nf_ct_destroy)(struct nf_conntrack *); +EXPORT_SYMBOL(nf_ct_destroy); + +void nf_conntrack_destroy(struct nf_conntrack *nfct) +{ + void (*destroy)(struct nf_conntrack *); + + rcu_read_lock(); + destroy = rcu_dereference(nf_ct_destroy); + BUG_ON(destroy == NULL); + destroy(nfct); + rcu_read_unlock(); +} +EXPORT_SYMBOL(nf_conntrack_destroy); +#endif /* CONFIG_NF_CONNTRACK */ + #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_net_netfilter; EXPORT_SYMBOL(proc_net_netfilter); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index b3a70eb..e132c8a 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -9,24 +9,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 23 Apr 2001: Harald Welte - * - new API and handling of conntrack/nat helpers - * - now capable of multiple expectations for one master - * 16 Jul 2002: Harald Welte - * - add usage/reference counts to ip_conntrack_expect - * - export ip_conntrack[_expect]_{find_get,put} functions - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - generalize L3 protocol denendent part. - * 23 Mar 2004: Yasuyuki Kozakai @USAGI - * - add support various size of conntrack structures. - * 26 Jan 2006: Harald Welte - * - restructure nf_conn (introduce nf_conn_help) - * - redesign 'features' how they were originally intended - * 26 Feb 2006: Pablo Neira Ayuso - * - add support for L3 protocol module load on demand. - * - * Derived from net/ipv4/netfilter/ip_conntrack_core.c */ #include @@ -128,10 +110,11 @@ static u_int32_t __hash_conntrack(const unsigned int size, unsigned int rnd) { unsigned int a, b; - a = jhash((void *)tuple->src.u3.all, sizeof(tuple->src.u3.all), - ((tuple->src.l3num) << 16) | tuple->dst.protonum); - b = jhash((void *)tuple->dst.u3.all, sizeof(tuple->dst.u3.all), - (tuple->src.u.all << 16) | tuple->dst.u.all); + + a = jhash2(tuple->src.u3.all, ARRAY_SIZE(tuple->src.u3.all), + (tuple->src.l3num << 16) | tuple->dst.protonum); + b = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all), + (tuple->src.u.all << 16) | tuple->dst.u.all); return jhash_2words(a, b, rnd) % size; } @@ -633,13 +616,11 @@ __nf_conntrack_alloc(const struct nf_con memset(conntrack, 0, nf_ct_cache[features].size); conntrack->features = features; atomic_set(&conntrack->ct_general.use, 1); - conntrack->ct_general.destroy = destroy_conntrack; conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig; conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *repl; /* Don't set timer yet: wait for confirmation */ - init_timer(&conntrack->timeout); - conntrack->timeout.data = (unsigned long)conntrack; - conntrack->timeout.function = death_by_timeout; + setup_timer(&conntrack->timeout, death_by_timeout, + (unsigned long)conntrack); read_unlock_bh(&nf_ct_cache_lock); return conntrack; @@ -768,7 +749,7 @@ resolve_normal_ct(struct sk_buff *skb, struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; - if (!nf_ct_get_tuple(skb, (unsigned int)(skb->nh.raw - skb->data), + if (!nf_ct_get_tuple(skb, skb_network_offset(skb), dataoff, l3num, protonum, &tuple, l3proto, l4proto)) { DEBUGP("resolve_normal_ct: Can't get tuple\n"); @@ -960,7 +941,7 @@ #ifdef CONFIG_NF_CT_ACCT if (do_acct) { ct->counters[CTINFO2DIR(ctinfo)].packets++; ct->counters[CTINFO2DIR(ctinfo)].bytes += - skb->len - (unsigned int)(skb->nh.raw - skb->data); + skb->len - skb_network_offset(skb); if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000) || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000)) @@ -1140,6 +1121,8 @@ void nf_conntrack_cleanup(void) while (atomic_read(&nf_conntrack_untracked.ct_general.use) > 1) schedule(); + rcu_assign_pointer(nf_ct_destroy, NULL); + for (i = 0; i < NF_CT_F_NUM; i++) { if (nf_ct_cache[i].use == 0) continue; @@ -1152,14 +1135,7 @@ void nf_conntrack_cleanup(void) free_conntrack_hash(nf_conntrack_hash, nf_conntrack_vmalloc, nf_conntrack_htable_size); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_generic); - - /* free l3proto protocol tables */ - for (i = 0; i < PF_MAX; i++) - if (nf_ct_protos[i]) { - kfree(nf_ct_protos[i]); - nf_ct_protos[i] = NULL; - } + nf_conntrack_proto_fini(); } static struct list_head *alloc_hashtable(int size, int *vmalloced) @@ -1237,7 +1213,6 @@ module_param_call(hashsize, set_hashsize int __init nf_conntrack_init(void) { - unsigned int i; int ret; /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB @@ -1279,18 +1254,13 @@ int __init nf_conntrack_init(void) goto err_free_conntrack_slab; } - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_generic); + ret = nf_conntrack_proto_init(); if (ret < 0) goto out_free_expect_slab; - /* Don't NEED lock here, but good form anyway. */ - write_lock_bh(&nf_conntrack_lock); - for (i = 0; i < AF_MAX; i++) - nf_ct_l3protos[i] = &nf_conntrack_l3proto_generic; - write_unlock_bh(&nf_conntrack_lock); - /* For use by REJECT target */ rcu_assign_pointer(ip_ct_attach, __nf_conntrack_attach); + rcu_assign_pointer(nf_ct_destroy, destroy_conntrack); /* Set up fake conntrack: - to never be deleted, not in any hashes */ diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 1a223e0..6bd421d 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -91,3 +91,26 @@ void nf_ct_event_cache_flush(void) } } +int nf_conntrack_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&nf_conntrack_chain, nb); +} +EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); + +int nf_conntrack_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb); +} +EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); + +int nf_conntrack_expect_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&nf_conntrack_expect_chain, nb); +} +EXPORT_SYMBOL_GPL(nf_conntrack_expect_register_notifier); + +int nf_conntrack_expect_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&nf_conntrack_expect_chain, nb); +} +EXPORT_SYMBOL_GPL(nf_conntrack_expect_unregister_notifier); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index ce70a6f..c31af29 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -290,9 +290,7 @@ static void nf_conntrack_expect_insert(s master_help->expecting++; list_add(&exp->list, &nf_conntrack_expect_list); - init_timer(&exp->timeout); - exp->timeout.data = (unsigned long)exp; - exp->timeout.function = expectation_timed_out; + setup_timer(&exp->timeout, expectation_timed_out, (unsigned long)exp); exp->timeout.expires = jiffies + master_help->helper->timeout * HZ; add_timer(&exp->timeout); diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 3089dfc..a186799 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -7,12 +7,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - enable working with Layer 3 protocol independent connection tracking. - * - track EPRT and EPSV commands with IPv6 address. - * - * Derived from net/ipv4/netfilter/ip_conntrack_ftp.c */ #include diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c index bb26a65..1093478 100644 --- a/net/netfilter/nf_conntrack_netbios_ns.c +++ b/net/netfilter/nf_conntrack_netbios_ns.c @@ -46,7 +46,7 @@ static int help(struct sk_buff **pskb, u struct nf_conn *ct, enum ip_conntrack_info ctinfo) { struct nf_conntrack_expect *exp; - struct iphdr *iph = (*pskb)->nh.iph; + struct iphdr *iph = ip_hdr(*pskb); struct rtable *rt = (struct rtable *)(*pskb)->dst; struct in_device *in_dev; __be32 mask = 0; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 48f0531..aa1a97e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -6,9 +6,6 @@ * (C) 2003 by Patrick Mchardy * (C) 2005-2006 by Pablo Neira Ayuso * - * I've reworked this stuff to use attributes instead of conntrack - * structures. 5.44 am. I need more tea. --pablo 05/07/11. - * * Initial connection tracking via netlink development funded and * generally made possible by Network Robots, Inc. (www.networkrobots.com) * @@ -16,8 +13,6 @@ * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. - * - * Derived from ip_conntrack_netlink.c: Port by Pablo Neira Ayuso (05/11/14) */ #include @@ -33,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -268,9 +264,7 @@ ctnetlink_fill_info(struct sk_buff *skb, struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; struct nfattr *nest_parms; - unsigned char *b; - - b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); event |= NFNL_SUBSYS_CTNETLINK << 8; nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); @@ -303,12 +297,12 @@ ctnetlink_fill_info(struct sk_buff *skb, ctnetlink_dump_use(skb, ct) < 0) goto nfattr_failure; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: nfattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -322,7 +316,7 @@ static int ctnetlink_conntrack_event(str struct nf_conn *ct = (struct nf_conn *)ptr; struct sk_buff *skb; unsigned int type; - unsigned char *b; + sk_buff_data_t b; unsigned int flags = 0, group; /* ignore our fake conntrack entry */ @@ -662,7 +656,7 @@ static const size_t cta_min[CTA_MAX] = { static int ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) + struct nlmsghdr *nlh, struct nfattr *cda[]) { struct nf_conntrack_tuple_hash *h; struct nf_conntrack_tuple tuple; @@ -710,7 +704,7 @@ ctnetlink_del_conntrack(struct sock *ctn static int ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) + struct nlmsghdr *nlh, struct nfattr *cda[]) { struct nf_conntrack_tuple_hash *h; struct nf_conntrack_tuple tuple; @@ -721,22 +715,12 @@ ctnetlink_get_conntrack(struct sock *ctn int err = 0; if (nlh->nlmsg_flags & NLM_F_DUMP) { - u32 rlen; - #ifndef CONFIG_NF_CT_ACCT if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) return -ENOTSUPP; #endif - if ((*errp = netlink_dump_start(ctnl, skb, nlh, - ctnetlink_dump_table, - ctnetlink_done)) != 0) - return -EINVAL; - - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; - skb_pull(skb, rlen); - return 0; + return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, + ctnetlink_done); } if (nfattr_bad_size(cda, CTA_MAX, cta_min)) @@ -1010,7 +994,7 @@ err: static int ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) + struct nlmsghdr *nlh, struct nfattr *cda[]) { struct nf_conntrack_tuple otuple, rtuple; struct nf_conntrack_tuple_hash *h = NULL; @@ -1152,9 +1136,7 @@ ctnetlink_exp_fill_info(struct sk_buff * { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - unsigned char *b; - - b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); event |= NFNL_SUBSYS_CTNETLINK_EXP << 8; nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); @@ -1168,12 +1150,12 @@ ctnetlink_exp_fill_info(struct sk_buff * if (ctnetlink_exp_dump_expect(skb, exp) < 0) goto nfattr_failure; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: nfattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1186,7 +1168,7 @@ static int ctnetlink_expect_event(struct struct nf_conntrack_expect *exp = (struct nf_conntrack_expect *)ptr; struct sk_buff *skb; unsigned int type; - unsigned char *b; + sk_buff_data_t b; int flags = 0; if (events & IPEXP_NEW) { @@ -1263,7 +1245,7 @@ static const size_t cta_min_exp[CTA_EXPE static int ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) + struct nlmsghdr *nlh, struct nfattr *cda[]) { struct nf_conntrack_tuple tuple; struct nf_conntrack_expect *exp; @@ -1276,17 +1258,9 @@ ctnetlink_get_expect(struct sock *ctnl, return -EINVAL; if (nlh->nlmsg_flags & NLM_F_DUMP) { - u32 rlen; - - if ((*errp = netlink_dump_start(ctnl, skb, nlh, - ctnetlink_exp_dump_table, - ctnetlink_done)) != 0) - return -EINVAL; - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; - skb_pull(skb, rlen); - return 0; + return netlink_dump_start(ctnl, skb, nlh, + ctnetlink_exp_dump_table, + ctnetlink_done); } if (cda[CTA_EXPECT_MASTER-1]) @@ -1333,7 +1307,7 @@ out: static int ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) + struct nlmsghdr *nlh, struct nfattr *cda[]) { struct nf_conntrack_expect *exp, *tmp; struct nf_conntrack_tuple tuple; @@ -1467,7 +1441,7 @@ out: static int ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) + struct nlmsghdr *nlh, struct nfattr *cda[]) { struct nf_conntrack_tuple tuple; struct nf_conntrack_expect *exp; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 456155f..6d94706 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -28,13 +28,13 @@ #include #include -struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly; +static struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly; struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_l3protos); -#ifdef CONFIG_SYSCTL -static DEFINE_MUTEX(nf_ct_proto_sysctl_mutex); +static DEFINE_MUTEX(nf_ct_proto_mutex); +#ifdef CONFIG_SYSCTL static int nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_table *path, struct ctl_table *table, unsigned int *users) @@ -164,13 +164,11 @@ static int nf_ct_l3proto_register_sysctl int err = 0; #ifdef CONFIG_SYSCTL - mutex_lock(&nf_ct_proto_sysctl_mutex); if (l3proto->ctl_table != NULL) { err = nf_ct_register_sysctl(&l3proto->ctl_table_header, l3proto->ctl_table_path, l3proto->ctl_table, NULL); } - mutex_unlock(&nf_ct_proto_sysctl_mutex); #endif return err; } @@ -178,11 +176,9 @@ #endif static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto) { #ifdef CONFIG_SYSCTL - mutex_lock(&nf_ct_proto_sysctl_mutex); if (l3proto->ctl_table_header != NULL) nf_ct_unregister_sysctl(&l3proto->ctl_table_header, l3proto->ctl_table, NULL); - mutex_unlock(&nf_ct_proto_sysctl_mutex); #endif } @@ -190,27 +186,23 @@ int nf_conntrack_l3proto_register(struct { int ret = 0; - if (proto->l3proto >= AF_MAX) { - ret = -EBUSY; - goto out; - } + if (proto->l3proto >= AF_MAX) + return -EBUSY; - write_lock_bh(&nf_conntrack_lock); + mutex_lock(&nf_ct_proto_mutex); if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) { ret = -EBUSY; goto out_unlock; } - rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); - write_unlock_bh(&nf_conntrack_lock); ret = nf_ct_l3proto_register_sysctl(proto); if (ret < 0) - nf_conntrack_l3proto_unregister(proto); - return ret; + goto out_unlock; + + rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); out_unlock: - write_unlock_bh(&nf_conntrack_lock); -out: + mutex_unlock(&nf_ct_proto_mutex); return ret; } EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); @@ -219,14 +211,14 @@ void nf_conntrack_l3proto_unregister(str { BUG_ON(proto->l3proto >= AF_MAX); - write_lock_bh(&nf_conntrack_lock); + mutex_lock(&nf_ct_proto_mutex); BUG_ON(nf_ct_l3protos[proto->l3proto] != proto); rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], &nf_conntrack_l3proto_generic); - write_unlock_bh(&nf_conntrack_lock); - synchronize_rcu(); - nf_ct_l3proto_unregister_sysctl(proto); + mutex_unlock(&nf_ct_proto_mutex); + + synchronize_rcu(); /* Remove all contrack entries for this protocol */ nf_ct_iterate_cleanup(kill_l3proto, proto); @@ -238,7 +230,6 @@ static int nf_ct_l4proto_register_sysctl int err = 0; #ifdef CONFIG_SYSCTL - mutex_lock(&nf_ct_proto_sysctl_mutex); if (l4proto->ctl_table != NULL) { err = nf_ct_register_sysctl(l4proto->ctl_table_header, nf_net_netfilter_sysctl_path, @@ -260,7 +251,6 @@ #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT } #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ out: - mutex_unlock(&nf_ct_proto_sysctl_mutex); #endif /* CONFIG_SYSCTL */ return err; } @@ -268,7 +258,6 @@ #endif /* CONFIG_SYSCTL */ static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto) { #ifdef CONFIG_SYSCTL - mutex_lock(&nf_ct_proto_sysctl_mutex); if (l4proto->ctl_table_header != NULL && *l4proto->ctl_table_header != NULL) nf_ct_unregister_sysctl(l4proto->ctl_table_header, @@ -279,7 +268,6 @@ #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header, l4proto->ctl_compat_table, NULL); #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ - mutex_unlock(&nf_ct_proto_sysctl_mutex); #endif /* CONFIG_SYSCTL */ } @@ -289,68 +277,41 @@ int nf_conntrack_l4proto_register(struct { int ret = 0; - if (l4proto->l3proto >= PF_MAX) { - ret = -EBUSY; - goto out; - } - - if (l4proto == &nf_conntrack_l4proto_generic) - return nf_ct_l4proto_register_sysctl(l4proto); + if (l4proto->l3proto >= PF_MAX) + return -EBUSY; -retry: - write_lock_bh(&nf_conntrack_lock); - if (nf_ct_protos[l4proto->l3proto]) { - if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] - != &nf_conntrack_l4proto_generic) { - ret = -EBUSY; - goto out_unlock; - } - } else { + mutex_lock(&nf_ct_proto_mutex); + if (!nf_ct_protos[l4proto->l3proto]) { /* l3proto may be loaded latter. */ struct nf_conntrack_l4proto **proto_array; int i; - write_unlock_bh(&nf_conntrack_lock); - - proto_array = (struct nf_conntrack_l4proto **) - kmalloc(MAX_NF_CT_PROTO * - sizeof(struct nf_conntrack_l4proto *), - GFP_KERNEL); + proto_array = kmalloc(MAX_NF_CT_PROTO * + sizeof(struct nf_conntrack_l4proto *), + GFP_KERNEL); if (proto_array == NULL) { ret = -ENOMEM; - goto out; + goto out_unlock; } + for (i = 0; i < MAX_NF_CT_PROTO; i++) proto_array[i] = &nf_conntrack_l4proto_generic; - - write_lock_bh(&nf_conntrack_lock); - if (nf_ct_protos[l4proto->l3proto]) { - /* bad timing, but no problem */ - write_unlock_bh(&nf_conntrack_lock); - kfree(proto_array); - } else { - nf_ct_protos[l4proto->l3proto] = proto_array; - write_unlock_bh(&nf_conntrack_lock); - } - - /* - * Just once because array is never freed until unloading - * nf_conntrack.ko - */ - goto retry; + nf_ct_protos[l4proto->l3proto] = proto_array; + } else if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != + &nf_conntrack_l4proto_generic) { + ret = -EBUSY; + goto out_unlock; } - rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto); - write_unlock_bh(&nf_conntrack_lock); - ret = nf_ct_l4proto_register_sysctl(l4proto); if (ret < 0) - nf_conntrack_l4proto_unregister(l4proto); - return ret; + goto out_unlock; + + rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], + l4proto); out_unlock: - write_unlock_bh(&nf_conntrack_lock); -out: + mutex_unlock(&nf_ct_proto_mutex); return ret; } EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); @@ -359,21 +320,42 @@ void nf_conntrack_l4proto_unregister(str { BUG_ON(l4proto->l3proto >= PF_MAX); - if (l4proto == &nf_conntrack_l4proto_generic) { - nf_ct_l4proto_unregister_sysctl(l4proto); - return; - } - - write_lock_bh(&nf_conntrack_lock); + mutex_lock(&nf_ct_proto_mutex); BUG_ON(nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto); rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], &nf_conntrack_l4proto_generic); - write_unlock_bh(&nf_conntrack_lock); - synchronize_rcu(); - nf_ct_l4proto_unregister_sysctl(l4proto); + mutex_unlock(&nf_ct_proto_mutex); + + synchronize_rcu(); /* Remove all contrack entries for this protocol */ nf_ct_iterate_cleanup(kill_l4proto, l4proto); } EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); + +int nf_conntrack_proto_init(void) +{ + unsigned int i; + int err; + + err = nf_ct_l4proto_register_sysctl(&nf_conntrack_l4proto_generic); + if (err < 0) + return err; + + for (i = 0; i < AF_MAX; i++) + rcu_assign_pointer(nf_ct_l3protos[i], + &nf_conntrack_l3proto_generic); + return 0; +} + +void nf_conntrack_proto_fini(void) +{ + unsigned int i; + + nf_ct_l4proto_unregister_sysctl(&nf_conntrack_l4proto_generic); + + /* free l3proto protocol tables */ + for (i = 0; i < PF_MAX; i++) + kfree(nf_ct_protos[i]); +} diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 7c06993..6faf1be 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -4,11 +4,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - enable working with L3 protocol independent connection tracking. - * - * Derived from net/ipv4/netfilter/ip_conntrack_proto_generic.c */ #include diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 3c80558..0d3254b 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -7,15 +7,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 17 Oct 2004: Yasuyuki Kozakai @USAGI - * - enable working with L3 protocol independent connection tracking. - * - * Derived from net/ipv4/ip_conntrack_sctp.c - */ - -/* - * Added support for proc manipulation of timeouts. */ #include diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 153d661..ccdd5d2 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -4,24 +4,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * Jozsef Kadlecsik : - * - Real stateful connection tracking - * - Modified state transitions table - * - Window scaling support added - * - SACK support added - * - * Willy Tarreau: - * - State table bugfixes - * - More robust state changes - * - Tuning timer parameters - * - * 27 Oct 2004: Yasuyuki Kozakai @USAGI - * - genelized Layer 3 protocol part. - * - * Derived from net/ipv4/netfilter/ip_conntrack_proto_tcp.c - * - * version 2.2 */ #include @@ -470,11 +452,10 @@ static void tcp_sack(const struct sk_buf /* Fast path for timestamp-only option */ if (length == TCPOLEN_TSTAMP_ALIGNED*4 - && *(__be32 *)ptr == - __constant_htonl((TCPOPT_NOP << 24) - | (TCPOPT_NOP << 16) - | (TCPOPT_TIMESTAMP << 8) - | TCPOLEN_TIMESTAMP)) + && *(__be32 *)ptr == htonl((TCPOPT_NOP << 24) + | (TCPOPT_NOP << 16) + | (TCPOPT_TIMESTAMP << 8) + | TCPOLEN_TIMESTAMP)) return; while (length > 0) { @@ -765,26 +746,18 @@ #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 -/* table of valid flag combinations - ECE and CWR are always valid */ -static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] = +/* table of valid flag combinations - PUSH, ECE and CWR are always valid */ +static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG) + 1] = { [TH_SYN] = 1, - [TH_SYN|TH_PUSH] = 1, [TH_SYN|TH_URG] = 1, - [TH_SYN|TH_PUSH|TH_URG] = 1, [TH_SYN|TH_ACK] = 1, - [TH_SYN|TH_ACK|TH_PUSH] = 1, [TH_RST] = 1, [TH_RST|TH_ACK] = 1, - [TH_RST|TH_ACK|TH_PUSH] = 1, [TH_FIN|TH_ACK] = 1, + [TH_FIN|TH_ACK|TH_URG] = 1, [TH_ACK] = 1, - [TH_ACK|TH_PUSH] = 1, [TH_ACK|TH_URG] = 1, - [TH_ACK|TH_URG|TH_PUSH] = 1, - [TH_FIN|TH_ACK|TH_PUSH] = 1, - [TH_FIN|TH_ACK|TH_URG] = 1, - [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1, }; /* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ @@ -831,7 +804,7 @@ static int tcp_error(struct sk_buff *skb } /* Check TCP flags. */ - tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR)); + tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR|TH_PUSH)); if (!tcp_valid_flags[tcpflags]) { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, @@ -1110,11 +1083,26 @@ static int tcp_to_nfattr(struct sk_buff const struct nf_conn *ct) { struct nfattr *nest_parms; + struct nf_ct_tcp_flags tmp = {}; read_lock_bh(&tcp_lock); nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP); NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t), &ct->proto.tcp.state); + + NFA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, sizeof(u_int8_t), + &ct->proto.tcp.seen[0].td_scale); + + NFA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_REPLY, sizeof(u_int8_t), + &ct->proto.tcp.seen[1].td_scale); + + tmp.flags = ct->proto.tcp.seen[0].flags; + NFA_PUT(skb, CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, + sizeof(struct nf_ct_tcp_flags), &tmp); + + tmp.flags = ct->proto.tcp.seen[1].flags; + NFA_PUT(skb, CTA_PROTOINFO_TCP_FLAGS_REPLY, + sizeof(struct nf_ct_tcp_flags), &tmp); read_unlock_bh(&tcp_lock); NFA_NEST_END(skb, nest_parms); @@ -1127,7 +1115,11 @@ nfattr_failure: } static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = { - [CTA_PROTOINFO_TCP_STATE-1] = sizeof(u_int8_t), + [CTA_PROTOINFO_TCP_STATE-1] = sizeof(u_int8_t), + [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1] = sizeof(u_int8_t), + [CTA_PROTOINFO_TCP_WSCALE_REPLY-1] = sizeof(u_int8_t), + [CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1] = sizeof(struct nf_ct_tcp_flags), + [CTA_PROTOINFO_TCP_FLAGS_REPLY-1] = sizeof(struct nf_ct_tcp_flags) }; static int nfattr_to_tcp(struct nfattr *cda[], struct nf_conn *ct) @@ -1151,6 +1143,30 @@ static int nfattr_to_tcp(struct nfattr * write_lock_bh(&tcp_lock); ct->proto.tcp.state = *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]); + + if (tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1]) { + struct nf_ct_tcp_flags *attr = + NFA_DATA(tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1]); + ct->proto.tcp.seen[0].flags &= ~attr->mask; + ct->proto.tcp.seen[0].flags |= attr->flags & attr->mask; + } + + if (tb[CTA_PROTOINFO_TCP_FLAGS_REPLY-1]) { + struct nf_ct_tcp_flags *attr = + NFA_DATA(tb[CTA_PROTOINFO_TCP_FLAGS_REPLY-1]); + ct->proto.tcp.seen[1].flags &= ~attr->mask; + ct->proto.tcp.seen[1].flags |= attr->flags & attr->mask; + } + + if (tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1] && + tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1] && + ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_WINDOW_SCALE && + ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_WINDOW_SCALE) { + ct->proto.tcp.seen[0].td_scale = *(u_int8_t *) + NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1]); + ct->proto.tcp.seen[1].td_scale = *(u_int8_t *) + NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]); + } write_unlock_bh(&tcp_lock); return 0; diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index a5e5726..3620ecc 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -4,11 +4,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - enable working with Layer 3 protocol independent connection tracking. - * - * Derived from net/ipv4/netfilter/ip_conntrack_proto_udp.c */ #include diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index b858636..45baeb0 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -1,20 +1,9 @@ -/* This file contains all the functions required for the standalone - nf_conntrack module. - - These are not required by the compatibility layer. -*/ - /* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2004 Netfilter Core Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 16 Dec 2003: Yasuyuki Kozakai @USAGI - * - generalize L3 protocol dependent part. - * - * Derived from net/ipv4/netfilter/ip_conntrack_standalone.c */ #include diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index bf23e48..8797e69 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -3,7 +3,7 @@ * * (C) 2001 by Jay Schulist , * (C) 2002-2005 by Harald Welte - * (C) 2005 by Pablo Neira Ayuso + * (C) 2005,2007 by Pablo Neira Ayuso * * Initial netfilter messages via netlink development funded and * generally made possible by Network Robots, Inc. (www.networkrobots.com) @@ -28,10 +28,9 @@ #include #include #include #include +#include #include -#include -#include #include #include @@ -41,32 +40,34 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NE static char __initdata nfversion[] = "0.30"; -#if 0 -#define DEBUGP(format, args...) \ - printk(KERN_DEBUG "%s(%d):%s(): " format, __FILE__, \ - __LINE__, __FUNCTION__, ## args) -#else -#define DEBUGP(format, args...) -#endif - static struct sock *nfnl = NULL; static struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT]; -DECLARE_MUTEX(nfnl_sem); +static DEFINE_MUTEX(nfnl_mutex); -void nfnl_lock(void) +static void nfnl_lock(void) { - nfnl_shlock(); + mutex_lock(&nfnl_mutex); } -void nfnl_unlock(void) +static int nfnl_trylock(void) { - nfnl_shunlock(); + return !mutex_trylock(&nfnl_mutex); } -int nfnetlink_subsys_register(struct nfnetlink_subsystem *n) +static void __nfnl_unlock(void) { - DEBUGP("registering subsystem ID %u\n", n->subsys_id); + mutex_unlock(&nfnl_mutex); +} + +static void nfnl_unlock(void) +{ + mutex_unlock(&nfnl_mutex); + if (nfnl->sk_receive_queue.qlen) + nfnl->sk_data_ready(nfnl, 0); +} +int nfnetlink_subsys_register(struct nfnetlink_subsystem *n) +{ nfnl_lock(); if (subsys_table[n->subsys_id]) { nfnl_unlock(); @@ -77,24 +78,23 @@ int nfnetlink_subsys_register(struct nfn return 0; } +EXPORT_SYMBOL_GPL(nfnetlink_subsys_register); int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n) { - DEBUGP("unregistering subsystem ID %u\n", n->subsys_id); - nfnl_lock(); subsys_table[n->subsys_id] = NULL; nfnl_unlock(); return 0; } +EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister); static inline struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type) { u_int8_t subsys_id = NFNL_SUBSYS_ID(type); - if (subsys_id >= NFNL_SUBSYS_COUNT - || subsys_table[subsys_id] == NULL) + if (subsys_id >= NFNL_SUBSYS_COUNT) return NULL; return subsys_table[subsys_id]; @@ -105,10 +105,8 @@ nfnetlink_find_client(u_int16_t type, st { u_int8_t cb_id = NFNL_MSG_TYPE(type); - if (cb_id >= ss->cb_count) { - DEBUGP("msgtype %u >= %u, returning\n", type, ss->cb_count); + if (cb_id >= ss->cb_count) return NULL; - } return &ss->cb[cb_id]; } @@ -125,6 +123,7 @@ void __nfa_fill(struct sk_buff *skb, int memcpy(NFA_DATA(nfa), data, attrlen); memset(NFA_DATA(nfa) + attrlen, 0, NFA_ALIGN(size) - size); } +EXPORT_SYMBOL_GPL(__nfa_fill); void nfattr_parse(struct nfattr *tb[], int maxattr, struct nfattr *nfa, int len) { @@ -137,6 +136,7 @@ void nfattr_parse(struct nfattr *tb[], i nfa = NFA_NEXT(nfa, len); } } +EXPORT_SYMBOL_GPL(nfattr_parse); /** * nfnetlink_check_attributes - check and parse nfnetlink attributes @@ -150,37 +150,15 @@ static int nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys, struct nlmsghdr *nlh, struct nfattr *cda[]) { - int min_len; - u_int16_t attr_count; + int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); - - if (unlikely(cb_id >= subsys->cb_count)) { - DEBUGP("msgtype %u >= %u, returning\n", - cb_id, subsys->cb_count); - return -EINVAL; - } - - min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); - if (unlikely(nlh->nlmsg_len < min_len)) - return -EINVAL; - - attr_count = subsys->cb[cb_id].attr_count; - memset(cda, 0, sizeof(struct nfattr *) * attr_count); + u_int16_t attr_count = subsys->cb[cb_id].attr_count; /* check attribute lengths. */ if (likely(nlh->nlmsg_len > min_len)) { struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); - - while (NFA_OK(attr, attrlen)) { - unsigned flavor = NFA_TYPE(attr); - if (flavor) { - if (flavor > attr_count) - return -EINVAL; - cda[flavor - 1] = attr; - } - attr = NFA_NEXT(attr, attrlen); - } + nfattr_parse(cda, attr_count, attr, attrlen); } /* implicit: if nlmsg_len == min_len, we return 0, and an empty @@ -208,62 +186,46 @@ int nfnetlink_send(struct sk_buff *skb, return err; } +EXPORT_SYMBOL_GPL(nfnetlink_send); int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags) { return netlink_unicast(nfnl, skb, pid, flags); } +EXPORT_SYMBOL_GPL(nfnetlink_unicast); /* Process one complete nfnetlink message. */ -static int nfnetlink_rcv_msg(struct sk_buff *skb, - struct nlmsghdr *nlh, int *errp) +static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct nfnl_callback *nc; struct nfnetlink_subsystem *ss; - int type, err = 0; - - DEBUGP("entered; subsys=%u, msgtype=%u\n", - NFNL_SUBSYS_ID(nlh->nlmsg_type), - NFNL_MSG_TYPE(nlh->nlmsg_type)); - - if (security_netlink_recv(skb, CAP_NET_ADMIN)) { - DEBUGP("missing CAP_NET_ADMIN\n"); - *errp = -EPERM; - return -1; - } + int type, err; - /* Only requests are handled by kernel now. */ - if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) { - DEBUGP("received non-request message\n"); - return 0; - } + if (security_netlink_recv(skb, CAP_NET_ADMIN)) + return -EPERM; /* All the messages must at least contain nfgenmsg */ - if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) { - DEBUGP("received message was too short\n"); + if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) return 0; - } type = nlh->nlmsg_type; ss = nfnetlink_get_subsys(type); if (!ss) { #ifdef CONFIG_KMOD - /* don't call nfnl_shunlock, since it would reenter + /* don't call nfnl_unlock, since it would reenter * with further packet processing */ - up(&nfnl_sem); + __nfnl_unlock(); request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); - nfnl_shlock(); + nfnl_lock(); ss = nfnetlink_get_subsys(type); if (!ss) #endif - goto err_inval; + return -EINVAL; } nc = nfnetlink_find_client(type, ss); - if (!nc) { - DEBUGP("unable to find client for type %d\n", type); - goto err_inval; - } + if (!nc) + return -EINVAL; { u_int16_t attr_count = @@ -274,73 +236,21 @@ #endif err = nfnetlink_check_attributes(ss, nlh, cda); if (err < 0) - goto err_inval; - - DEBUGP("calling handler\n"); - err = nc->call(nfnl, skb, nlh, cda, errp); - *errp = err; - return err; - } - -err_inval: - DEBUGP("returning -EINVAL\n"); - *errp = -EINVAL; - return -1; -} - -/* Process one packet of messages. */ -static inline int nfnetlink_rcv_skb(struct sk_buff *skb) -{ - int err; - struct nlmsghdr *nlh; - - while (skb->len >= NLMSG_SPACE(0)) { - u32 rlen; - - nlh = (struct nlmsghdr *)skb->data; - if (nlh->nlmsg_len < sizeof(struct nlmsghdr) - || skb->len < nlh->nlmsg_len) - return 0; - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; - if (nfnetlink_rcv_msg(skb, nlh, &err)) { - if (!err) - return -1; - netlink_ack(skb, nlh, err); - } else - if (nlh->nlmsg_flags & NLM_F_ACK) - netlink_ack(skb, nlh, 0); - skb_pull(skb, rlen); + return err; + return nc->call(nfnl, skb, nlh, cda); } - - return 0; } static void nfnetlink_rcv(struct sock *sk, int len) { - do { - struct sk_buff *skb; + unsigned int qlen = 0; - if (nfnl_shlock_nowait()) + do { + if (nfnl_trylock()) return; - - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { - if (nfnetlink_rcv_skb(skb)) { - if (skb->len) - skb_queue_head(&sk->sk_receive_queue, - skb); - else - kfree_skb(skb); - break; - } - kfree_skb(skb); - } - - /* don't call nfnl_shunlock, since it would reenter - * with further packet processing */ - up(&nfnl_sem); - } while(nfnl && nfnl->sk_receive_queue.qlen); + netlink_run_queue(sk, &qlen, nfnetlink_rcv_msg); + __nfnl_unlock(); + } while (qlen); } static void __exit nfnetlink_exit(void) @@ -355,7 +265,7 @@ static int __init nfnetlink_init(void) printk("Netfilter messages via NETLINK v%s.\n", nfversion); nfnl = netlink_kernel_create(NETLINK_NETFILTER, NFNLGRP_MAX, - nfnetlink_rcv, THIS_MODULE); + nfnetlink_rcv, NULL, THIS_MODULE); if (!nfnl) { printk(KERN_ERR "cannot initialize nfnetlink!\n"); return -1; @@ -366,10 +276,3 @@ static int __init nfnetlink_init(void) module_init(nfnetlink_init); module_exit(nfnetlink_exit); - -EXPORT_SYMBOL_GPL(nfnetlink_subsys_register); -EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister); -EXPORT_SYMBOL_GPL(nfnetlink_send); -EXPORT_SYMBOL_GPL(nfnetlink_unicast); -EXPORT_SYMBOL_GPL(nfattr_parse); -EXPORT_SYMBOL_GPL(__nfa_fill); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 5cb30eb..e32e30e 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -10,11 +10,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 2006-01-26 Harald Welte - * - Add optional local and global sequence number to detect lost - * events from userspace - * */ #include #include @@ -163,10 +158,7 @@ instance_create(u_int16_t group_num, int /* needs to be two, since we _put() after creation */ atomic_set(&inst->use, 2); - init_timer(&inst->timer); - inst->timer.function = nfulnl_timer; - inst->timer.data = (unsigned long)inst; - /* don't start timer yet. (re)start it with every packet */ + setup_timer(&inst->timer, nfulnl_timer, (unsigned long)inst); inst->peer_pid = pid; inst->group_num = group_num; @@ -200,20 +192,14 @@ out_unlock: static int __nfulnl_send(struct nfulnl_instance *inst); static void -_instance_destroy2(struct nfulnl_instance *inst, int lock) +__instance_destroy(struct nfulnl_instance *inst) { /* first pull it out of the global list */ - if (lock) - write_lock_bh(&instances_lock); - UDEBUG("removing instance %p (queuenum=%u) from hash\n", inst, inst->group_num); hlist_del(&inst->hlist); - if (lock) - write_unlock_bh(&instances_lock); - /* then flush all pending packets from skb */ spin_lock_bh(&inst->lock); @@ -235,15 +221,11 @@ _instance_destroy2(struct nfulnl_instanc } static inline void -__instance_destroy(struct nfulnl_instance *inst) -{ - _instance_destroy2(inst, 0); -} - -static inline void instance_destroy(struct nfulnl_instance *inst) { - _instance_destroy2(inst, 1); + write_lock_bh(&instances_lock); + __instance_destroy(inst); + write_unlock_bh(&instances_lock); } static int @@ -365,9 +347,6 @@ __nfulnl_send(struct nfulnl_instance *in { int status; - if (!inst->skb) - return 0; - if (inst->qlen > 1) inst->lastnlh->nlmsg_type = NLMSG_DONE; @@ -391,7 +370,8 @@ static void nfulnl_timer(unsigned long d UDEBUG("timer function called, flushing buffer\n"); spin_lock_bh(&inst->lock); - __nfulnl_send(inst); + if (inst->skb) + __nfulnl_send(inst); spin_unlock_bh(&inst->lock); instance_put(inst); } @@ -409,15 +389,14 @@ __build_packet_message(struct nfulnl_ins const struct nf_loginfo *li, const char *prefix, unsigned int plen) { - unsigned char *old_tail; struct nfulnl_msg_packet_hdr pmsg; struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; __be32 tmp_uint; + sk_buff_data_t old_tail = inst->skb->tail; UDEBUG("entered\n"); - old_tail = inst->skb->tail; nlh = NLMSG_PUT(inst->skb, 0, 0, NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET, sizeof(struct nfgenmsg)); @@ -509,11 +488,11 @@ #endif NFA_PUT(inst->skb, NFULA_HWADDR, sizeof(phw), &phw); } - if (skb->tstamp.off_sec) { + if (skb->tstamp.tv64) { struct nfulnl_msg_packet_timestamp ts; - - ts.sec = cpu_to_be64(skb->tstamp.off_sec); - ts.usec = cpu_to_be64(skb->tstamp.off_usec); + struct timeval tv = ktime_to_timeval(skb->tstamp); + ts.sec = cpu_to_be64(tv.tv_sec); + ts.usec = cpu_to_be64(tv.tv_usec); NFA_PUT(inst->skb, NFULA_TIMESTAMP, sizeof(ts), &ts); } @@ -596,7 +575,6 @@ nfulnl_log_packet(unsigned int pf, struct nfulnl_instance *inst; const struct nf_loginfo *li; unsigned int qthreshold; - unsigned int nlbufsiz; unsigned int plen; if (li_user && li_user->type == NF_LOG_TYPE_ULOG) @@ -606,12 +584,7 @@ nfulnl_log_packet(unsigned int pf, inst = instance_lookup_get(li->u.ulog.group); if (!inst) - inst = instance_lookup_get(0); - if (!inst) { - PRINTR("nfnetlink_log: trying to log packet, " - "but no instance for group %u\n", li->u.ulog.group); return; - } plen = 0; if (prefix) @@ -667,24 +640,11 @@ #endif break; default: - spin_unlock_bh(&inst->lock); - instance_put(inst); - return; + goto unlock_and_release; } - if (size > inst->nlbufsiz) - nlbufsiz = size; - else - nlbufsiz = inst->nlbufsiz; - - if (!inst->skb) { - if (!(inst->skb = nfulnl_alloc_skb(nlbufsiz, size))) { - UDEBUG("error in nfulnl_alloc_skb(%u, %u)\n", - inst->nlbufsiz, size); - goto alloc_failure; - } - } else if (inst->qlen >= qthreshold || - size > skb_tailroom(inst->skb)) { + if (inst->qlen >= qthreshold || + (inst->skb && size > skb_tailroom(inst->skb))) { /* either the queue len is too high or we don't have * enough room in the skb left. flush to userspace. */ UDEBUG("flushing old skb\n"); @@ -693,12 +653,12 @@ #endif if (del_timer(&inst->timer)) instance_put(inst); __nfulnl_send(inst); + } - if (!(inst->skb = nfulnl_alloc_skb(nlbufsiz, size))) { - UDEBUG("error in nfulnl_alloc_skb(%u, %u)\n", - inst->nlbufsiz, size); + if (!inst->skb) { + inst->skb = nfulnl_alloc_skb(inst->nlbufsiz, size); + if (!inst->skb) goto alloc_failure; - } } UDEBUG("qlen %d, qthreshold %d\n", inst->qlen, qthreshold); @@ -760,7 +720,7 @@ static struct notifier_block nfulnl_rtnl static int nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) + struct nlmsghdr *nlh, struct nfattr *nfqa[]) { return -ENOTSUPP; } @@ -798,7 +758,7 @@ static const int nfula_cfg_min[NFULA_CFG static int nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *nfula[], int *errp) + struct nlmsghdr *nlh, struct nfattr *nfula[]) { struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t group_num = ntohs(nfmsg->res_id); @@ -830,13 +790,13 @@ nfulnl_recv_config(struct sock *ctnl, st NETLINK_CB(skb).pid); if (!inst) { ret = -EINVAL; - goto out_put; + goto out; } break; case NFULNL_CFG_CMD_UNBIND: if (!inst) { ret = -ENODEV; - goto out_put; + goto out; } if (inst->peer_pid != NETLINK_CB(skb).pid) { @@ -845,7 +805,7 @@ nfulnl_recv_config(struct sock *ctnl, st } instance_destroy(inst); - break; + goto out; case NFULNL_CFG_CMD_PF_BIND: UDEBUG("registering log handler for pf=%u\n", pf); ret = nf_log_register(pf, &nfulnl_logger); @@ -869,7 +829,7 @@ nfulnl_recv_config(struct sock *ctnl, st "group=%u pid=%u =>ENOENT\n", group_num, NETLINK_CB(skb).pid); ret = -ENOENT; - goto out_put; + goto out; } if (inst->peer_pid != NETLINK_CB(skb).pid) { @@ -939,10 +899,8 @@ struct iter_state { unsigned int bucket; }; -static struct hlist_node *get_first(struct seq_file *seq) +static struct hlist_node *get_first(struct iter_state *st) { - struct iter_state *st = seq->private; - if (!st) return NULL; @@ -953,10 +911,8 @@ static struct hlist_node *get_first(stru return NULL; } -static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) +static struct hlist_node *get_next(struct iter_state *st, struct hlist_node *h) { - struct iter_state *st = seq->private; - h = h->next; while (!h) { if (++st->bucket >= INSTANCE_BUCKETS) @@ -967,13 +923,13 @@ static struct hlist_node *get_next(struc return h; } -static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) +static struct hlist_node *get_idx(struct iter_state *st, loff_t pos) { struct hlist_node *head; - head = get_first(seq); + head = get_first(st); if (head) - while (pos && (head = get_next(seq, head))) + while (pos && (head = get_next(st, head))) pos--; return pos ? NULL : head; } @@ -981,13 +937,13 @@ static struct hlist_node *get_idx(struct static void *seq_start(struct seq_file *seq, loff_t *pos) { read_lock_bh(&instances_lock); - return get_idx(seq, *pos); + return get_idx(seq->private, *pos); } static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; - return get_next(s, v); + return get_next(s->private, v); } static void seq_stop(struct seq_file *s, void *v) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index d9ce4a7..7a97bec 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -338,7 +338,7 @@ static struct sk_buff * nfqnl_build_packet_message(struct nfqnl_instance *queue, struct nfqnl_queue_entry *entry, int *errp) { - unsigned char *old_tail; + sk_buff_data_t old_tail; size_t size; size_t data_len = 0; struct sk_buff *skb; @@ -404,7 +404,7 @@ #endif if (!skb) goto nlmsg_failure; - old_tail= skb->tail; + old_tail = skb->tail; nlh = NLMSG_PUT(skb, 0, 0, NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, sizeof(struct nfgenmsg)); @@ -495,11 +495,11 @@ #endif NFA_PUT(skb, NFQA_HWADDR, sizeof(phw), &phw); } - if (entskb->tstamp.off_sec) { + if (entskb->tstamp.tv64) { struct nfqnl_msg_packet_timestamp ts; - - ts.sec = cpu_to_be64(entskb->tstamp.off_sec); - ts.usec = cpu_to_be64(entskb->tstamp.off_usec); + struct timeval tv = ktime_to_timeval(entskb->tstamp); + ts.sec = cpu_to_be64(tv.tv_sec); + ts.usec = cpu_to_be64(tv.tv_usec); NFA_PUT(skb, NFQA_TIMESTAMP, sizeof(ts), &ts); } @@ -648,7 +648,7 @@ nfqnl_mangle(void *data, int data_len, s } if (!skb_make_writable(&e->skb, data_len)) return -ENOMEM; - memcpy(e->skb->data, data, data_len); + skb_copy_to_linear_data(e->skb, data, data_len); e->skb->ip_summed = CHECKSUM_NONE; return 0; } @@ -783,7 +783,7 @@ static const int nfqa_verdict_min[NFQA_M static int nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) + struct nlmsghdr *nlh, struct nfattr *nfqa[]) { struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t queue_num = ntohs(nfmsg->res_id); @@ -848,7 +848,7 @@ err_out_put: static int nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) + struct nlmsghdr *nlh, struct nfattr *nfqa[]) { return -ENOTSUPP; } @@ -865,7 +865,7 @@ static struct nf_queue_handler nfqh = { static int nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) + struct nlmsghdr *nlh, struct nfattr *nfqa[]) { struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t queue_num = ntohs(nfmsg->res_id); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index ec607a4..0eb2504 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -56,8 +56,8 @@ enum { }; static const char *xt_prefix[NPROTO] = { - [AF_INET] = "ip", - [AF_INET6] = "ip6", + [AF_INET] = "ip", + [AF_INET6] = "ip6", [NF_ARP] = "arp", }; @@ -651,12 +651,6 @@ void *xt_unregister_table(struct xt_tabl EXPORT_SYMBOL_GPL(xt_unregister_table); #ifdef CONFIG_PROC_FS -static char *xt_proto_prefix[NPROTO] = { - [AF_INET] = "ip", - [AF_INET6] = "ip6", - [NF_ARP] = "arp", -}; - static struct list_head *xt_get_idx(struct list_head *list, struct seq_file *seq, loff_t pos) { struct list_head *head = list->next; @@ -798,7 +792,7 @@ #endif #ifdef CONFIG_PROC_FS - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TABLES, sizeof(buf)); proc = proc_net_fops_create(buf, 0440, &xt_file_ops); if (!proc) @@ -806,14 +800,14 @@ #ifdef CONFIG_PROC_FS proc->data = (void *) ((unsigned long) af | (TABLE << 16)); - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_MATCHES, sizeof(buf)); proc = proc_net_fops_create(buf, 0440, &xt_file_ops); if (!proc) goto out_remove_tables; proc->data = (void *) ((unsigned long) af | (MATCH << 16)); - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TARGETS, sizeof(buf)); proc = proc_net_fops_create(buf, 0440, &xt_file_ops); if (!proc) @@ -825,12 +819,12 @@ #endif #ifdef CONFIG_PROC_FS out_remove_matches: - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_MATCHES, sizeof(buf)); proc_net_remove(buf); out_remove_tables: - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TABLES, sizeof(buf)); proc_net_remove(buf); out: @@ -844,15 +838,15 @@ void xt_proto_fini(int af) #ifdef CONFIG_PROC_FS char buf[XT_FUNCTION_MAXNAMELEN]; - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TABLES, sizeof(buf)); proc_net_remove(buf); - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TARGETS, sizeof(buf)); proc_net_remove(buf); - strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_MATCHES, sizeof(buf)); proc_net_remove(buf); #endif /*CONFIG_PROC_FS*/ diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c index 795c058..b03ce00 100644 --- a/net/netfilter/xt_CONNMARK.c +++ b/net/netfilter/xt_CONNMARK.c @@ -30,10 +30,7 @@ MODULE_ALIAS("ipt_CONNMARK"); #include #include -#include -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #include -#endif static unsigned int target(struct sk_buff **pskb, @@ -44,40 +41,33 @@ target(struct sk_buff **pskb, const void *targinfo) { const struct xt_connmark_target_info *markinfo = targinfo; + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; u_int32_t diff; u_int32_t mark; u_int32_t newmark; - u_int32_t ctinfo; - u_int32_t *ctmark = nf_ct_get_mark(*pskb, &ctinfo); - if (ctmark) { + ct = nf_ct_get(*pskb, &ctinfo); + if (ct) { switch(markinfo->mode) { case XT_CONNMARK_SET: - newmark = (*ctmark & ~markinfo->mask) | markinfo->mark; - if (newmark != *ctmark) { - *ctmark = newmark; -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - ip_conntrack_event_cache(IPCT_MARK, *pskb); -#else + newmark = (ct->mark & ~markinfo->mask) | markinfo->mark; + if (newmark != ct->mark) { + ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, *pskb); -#endif } break; case XT_CONNMARK_SAVE: - newmark = (*ctmark & ~markinfo->mask) | + newmark = (ct->mark & ~markinfo->mask) | ((*pskb)->mark & markinfo->mask); - if (*ctmark != newmark) { - *ctmark = newmark; -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - ip_conntrack_event_cache(IPCT_MARK, *pskb); -#else + if (ct->mark != newmark) { + ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, *pskb); -#endif } break; case XT_CONNMARK_RESTORE: mark = (*pskb)->mark; - diff = (*ctmark ^ mark) & markinfo->mask; + diff = (ct->mark ^ mark) & markinfo->mask; (*pskb)->mark = mark ^ diff; break; } diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 1ab0db6..81c0c58 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -19,7 +19,7 @@ #include #include #include #include -#include +#include #define PFX "CONNSECMARK: " @@ -36,12 +36,12 @@ MODULE_ALIAS("ip6t_CONNSECMARK"); static void secmark_save(struct sk_buff *skb) { if (skb->secmark) { - u32 *connsecmark; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; - connsecmark = nf_ct_get_secmark(skb, &ctinfo); - if (connsecmark && !*connsecmark) - *connsecmark = skb->secmark; + ct = nf_ct_get(skb, &ctinfo); + if (ct && !ct->secmark) + ct->secmark = skb->secmark; } } @@ -52,12 +52,12 @@ static void secmark_save(struct sk_buff static void secmark_restore(struct sk_buff *skb) { if (!skb->secmark) { - u32 *connsecmark; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; - connsecmark = nf_ct_get_secmark(skb, &ctinfo); - if (connsecmark && *connsecmark) - skb->secmark = *connsecmark; + ct = nf_ct_get(skb, &ctinfo); + if (ct && ct->secmark) + skb->secmark = ct->secmark; } } diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index a7cc75a..9f2f220 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -8,8 +8,6 @@ * published by the Free Software Foundation. * * See RFC2474 for a description of the DSCP field within the IP Header. - * - * xt_DSCP.c,v 1.8 2002/08/06 18:41:57 laforge Exp */ #include @@ -35,13 +33,13 @@ static unsigned int target(struct sk_buf const void *targinfo) { const struct xt_DSCP_info *dinfo = targinfo; - u_int8_t dscp = ipv4_get_dsfield((*pskb)->nh.iph) >> XT_DSCP_SHIFT; + u_int8_t dscp = ipv4_get_dsfield(ip_hdr(*pskb)) >> XT_DSCP_SHIFT; if (dscp != dinfo->dscp) { if (!skb_make_writable(pskb, sizeof(struct iphdr))) return NF_DROP; - ipv4_change_dsfield((*pskb)->nh.iph, (__u8)(~XT_DSCP_MASK), + ipv4_change_dsfield(ip_hdr(*pskb), (__u8)(~XT_DSCP_MASK), dinfo->dscp << XT_DSCP_SHIFT); } @@ -56,13 +54,13 @@ static unsigned int target6(struct sk_bu const void *targinfo) { const struct xt_DSCP_info *dinfo = targinfo; - u_int8_t dscp = ipv6_get_dsfield((*pskb)->nh.ipv6h) >> XT_DSCP_SHIFT; + u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(*pskb)) >> XT_DSCP_SHIFT; if (dscp != dinfo->dscp) { if (!skb_make_writable(pskb, sizeof(struct ipv6hdr))) return NF_DROP; - ipv6_change_dsfield((*pskb)->nh.ipv6h, (__u8)(~XT_DSCP_MASK), + ipv6_change_dsfield(ipv6_hdr(*pskb), (__u8)(~XT_DSCP_MASK), dinfo->dscp << XT_DSCP_SHIFT); } return XT_CONTINUE; diff --git a/net/netfilter/xt_NOTRACK.c b/net/netfilter/xt_NOTRACK.c index b874a20..5085fb3 100644 --- a/net/netfilter/xt_NOTRACK.c +++ b/net/netfilter/xt_NOTRACK.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_NOTRACK"); @@ -26,7 +26,7 @@ target(struct sk_buff **pskb, If there is a real ct entry correspondig to this packet, it'll hang aroun till timing out. We don't deal with it for performance reasons. JK */ - nf_ct_untrack(*pskb); + (*pskb)->nfct = &nf_conntrack_untracked.ct_general; (*pskb)->nfctinfo = IP_CT_NEW; nf_conntrack_get((*pskb)->nfct); diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index db7e38c..15fe8f6 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -54,7 +54,7 @@ tcpmss_mangle_packet(struct sk_buff **ps return -1; tcplen = (*pskb)->len - tcphoff; - tcph = (struct tcphdr *)((*pskb)->nh.raw + tcphoff); + tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff); /* Since it passed flags test in tcp match, we know it is is not a fragment, and has data >= tcp header length. SYN @@ -113,7 +113,7 @@ tcpmss_mangle_packet(struct sk_buff **ps return -1; kfree_skb(*pskb); *pskb = newskb; - tcph = (struct tcphdr *)((*pskb)->nh.raw + tcphoff); + tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff); } skb_put((*pskb), TCPOLEN_MSS); @@ -145,7 +145,7 @@ xt_tcpmss_target4(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct iphdr *iph = (*pskb)->nh.iph; + struct iphdr *iph = ip_hdr(*pskb); __be16 newlen; int ret; @@ -154,7 +154,7 @@ xt_tcpmss_target4(struct sk_buff **pskb, if (ret < 0) return NF_DROP; if (ret > 0) { - iph = (*pskb)->nh.iph; + iph = ip_hdr(*pskb); newlen = htons(ntohs(iph->tot_len) + ret); nf_csum_replace2(&iph->check, iph->tot_len, newlen); iph->tot_len = newlen; @@ -171,7 +171,7 @@ xt_tcpmss_target6(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct ipv6hdr *ipv6h = (*pskb)->nh.ipv6h; + struct ipv6hdr *ipv6h = ipv6_hdr(*pskb); u8 nexthdr; int tcphoff; int ret; @@ -187,7 +187,7 @@ xt_tcpmss_target6(struct sk_buff **pskb, if (ret < 0) return NF_DROP; if (ret > 0) { - ipv6h = (*pskb)->nh.ipv6h; + ipv6h = ipv6_hdr(*pskb); ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); } return XT_CONTINUE; diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index 5e32dfa..804afe5 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -1,20 +1,11 @@ /* Kernel module to match connection tracking byte counter. * GPL (C) 2002 Martin Devera (devik@cdi.cz). - * - * 2004-07-20 Harald Welte - * - reimplemented to use per-connection accounting counters - * - add functionality to match number of packets - * - add functionality to match average packet size - * - add support to match directions seperately - * 2005-10-16 Harald Welte - * - Port to x_tables - * */ #include #include -#include #include #include +#include #include #include @@ -24,22 +15,6 @@ MODULE_AUTHOR("Harald Welte 0xffffffffULL) { - unsigned int shift = fls(divisor >> 32); - - d = divisor >> shift; - dividend >>= shift; - } - - do_div(dividend, d); - return dividend; -} - static int match(const struct sk_buff *skb, const struct net_device *in, @@ -51,13 +26,17 @@ match(const struct sk_buff *skb, int *hotdrop) { const struct xt_connbytes_info *sinfo = matchinfo; + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; u_int64_t what = 0; /* initialize to make gcc happy */ u_int64_t bytes = 0; u_int64_t pkts = 0; const struct ip_conntrack_counter *counters; - if (!(counters = nf_ct_get_counters(skb))) - return 0; /* no match */ + ct = nf_ct_get(skb, &ctinfo); + if (!ct) + return 0; + counters = ct->counters; switch (sinfo->what) { case XT_CONNBYTES_PKTS: diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 36c2def..e180325 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -21,16 +21,15 @@ #include #include +#include +#include +#include MODULE_AUTHOR("Henrik Nordstrom "); MODULE_DESCRIPTION("IP tables connmark match module"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_connmark"); -#include -#include -#include - static int match(const struct sk_buff *skb, const struct net_device *in, @@ -42,12 +41,14 @@ match(const struct sk_buff *skb, int *hotdrop) { const struct xt_connmark_info *info = matchinfo; - u_int32_t ctinfo; - const u_int32_t *ctmark = nf_ct_get_mark(skb, &ctinfo); - if (!ctmark) + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct) return 0; - return (((*ctmark) & info->mask) == info->mark) ^ info->invert; + return (((ct->mark) & info->mask) == info->mark) ^ info->invert; } static int diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 2885c37..f4ea8fe 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -10,121 +10,15 @@ #include #include - -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) -#include -#include -#else -#include -#endif - #include #include -#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); MODULE_DESCRIPTION("iptables connection tracking match module"); MODULE_ALIAS("ipt_conntrack"); -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop) -{ - const struct xt_conntrack_info *sinfo = matchinfo; - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - unsigned int statebit; - - ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); - -#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & invflg)) - - if (ct == &ip_conntrack_untracked) - statebit = XT_CONNTRACK_STATE_UNTRACKED; - else if (ct) - statebit = XT_CONNTRACK_STATE_BIT(ctinfo); - else - statebit = XT_CONNTRACK_STATE_INVALID; - - if (sinfo->flags & XT_CONNTRACK_STATE) { - if (ct) { - if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) - statebit |= XT_CONNTRACK_STATE_SNAT; - if (test_bit(IPS_DST_NAT_BIT, &ct->status)) - statebit |= XT_CONNTRACK_STATE_DNAT; - } - if (FWINV((statebit & sinfo->statemask) == 0, - XT_CONNTRACK_STATE)) - return 0; - } - - if (ct == NULL) { - if (sinfo->flags & ~XT_CONNTRACK_STATE) - return 0; - return 1; - } - - if (sinfo->flags & XT_CONNTRACK_PROTO && - FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != - sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, - XT_CONNTRACK_PROTO)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_ORIGSRC && - FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip & - sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != - sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, - XT_CONNTRACK_ORIGSRC)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_ORIGDST && - FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip & - sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != - sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, - XT_CONNTRACK_ORIGDST)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_REPLSRC && - FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip & - sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != - sinfo->tuple[IP_CT_DIR_REPLY].src.ip, - XT_CONNTRACK_REPLSRC)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_REPLDST && - FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip & - sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != - sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, - XT_CONNTRACK_REPLDST)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_STATUS && - FWINV((ct->status & sinfo->statusmask) == 0, - XT_CONNTRACK_STATUS)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_EXPIRES) { - unsigned long expires = timer_pending(&ct->timeout) ? - (ct->timeout.expires - jiffies)/HZ : 0; - - if (FWINV(!(expires >= sinfo->expires_min && - expires <= sinfo->expires_max), - XT_CONNTRACK_EXPIRES)) - return 0; - } - return 1; -} - -#else /* CONFIG_IP_NF_CONNTRACK */ static int match(const struct sk_buff *skb, const struct net_device *in, @@ -220,8 +114,6 @@ #define FWINV(bool,invflg) ((bool) ^ !!( return 1; } -#endif /* CONFIG_NF_IP_CONNTRACK */ - static int checkentry(const char *tablename, const void *ip, diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 26c7f4a..56b247e 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -1,7 +1,5 @@ /* IP tables module for matching the value of the IPv4/IPv6 DSCP field * - * xt_dscp.c,v 1.3 2002/08/05 19:00:21 laforge Exp - * * (C) 2002 by Harald Welte * * This program is free software; you can redistribute it and/or modify @@ -34,7 +32,7 @@ static int match(const struct sk_buff *s int *hotdrop) { const struct xt_dscp_info *info = matchinfo; - u_int8_t dscp = ipv4_get_dsfield(skb->nh.iph) >> XT_DSCP_SHIFT; + u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; return (dscp == info->dscp) ^ !!info->invert; } @@ -49,7 +47,7 @@ static int match6(const struct sk_buff * int *hotdrop) { const struct xt_dscp_info *info = matchinfo; - u_int8_t dscp = ipv6_get_dsfield(skb->nh.ipv6h) >> XT_DSCP_SHIFT; + u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; return (dscp == info->dscp) ^ !!info->invert; } diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 9f37d59..d3043fa 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -216,10 +216,8 @@ static int htable_create(struct xt_hashl hinfo->pde->proc_fops = &dl_file_ops; hinfo->pde->data = hinfo; - init_timer(&hinfo->timer); + setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo); hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); - hinfo->timer.data = (unsigned long )hinfo; - hinfo->timer.function = htable_gc; add_timer(&hinfo->timer); spin_lock_bh(&hashlimit_lock); @@ -380,22 +378,22 @@ hashlimit_init_dst(struct xt_hashlimit_h switch (hinfo->family) { case AF_INET: if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP) - dst->addr.ip.dst = skb->nh.iph->daddr; + dst->addr.ip.dst = ip_hdr(skb)->daddr; if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP) - dst->addr.ip.src = skb->nh.iph->saddr; + dst->addr.ip.src = ip_hdr(skb)->saddr; if (!(hinfo->cfg.mode & (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT))) return 0; - nexthdr = skb->nh.iph->protocol; + nexthdr = ip_hdr(skb)->protocol; break; #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) case AF_INET6: if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP) - memcpy(&dst->addr.ip6.dst, &skb->nh.ipv6h->daddr, + memcpy(&dst->addr.ip6.dst, &ipv6_hdr(skb)->daddr, sizeof(dst->addr.ip6.dst)); if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP) - memcpy(&dst->addr.ip6.src, &skb->nh.ipv6h->saddr, + memcpy(&dst->addr.ip6.src, &ipv6_hdr(skb)->saddr, sizeof(dst->addr.ip6.src)); if (!(hinfo->cfg.mode & diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index 407d1d5..c139b2f 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -5,26 +5,16 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 19 Mar 2002 Harald Welte : - * - Port to newnat infrastructure */ #include #include #include -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) -#include -#include -#include -#else #include #include #include -#endif #include #include -#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Martin Josefsson "); @@ -38,55 +28,6 @@ #else #define DEBUGP(format, args...) #endif -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop) -{ - const struct xt_helper_info *info = matchinfo; - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - int ret = info->invert; - - ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); - if (!ct) { - DEBUGP("xt_helper: Eek! invalid conntrack?\n"); - return ret; - } - - if (!ct->master) { - DEBUGP("xt_helper: conntrack %p has no master\n", ct); - return ret; - } - - read_lock_bh(&ip_conntrack_lock); - if (!ct->master->helper) { - DEBUGP("xt_helper: master ct %p has no helper\n", - exp->expectant); - goto out_unlock; - } - - DEBUGP("master's name = %s , info->name = %s\n", - ct->master->helper->name, info->name); - - if (info->name[0] == '\0') - ret ^= 1; - else - ret ^= !strncmp(ct->master->helper->name, info->name, - strlen(ct->master->helper->name)); -out_unlock: - read_unlock_bh(&ip_conntrack_lock); - return ret; -} - -#else /* CONFIG_IP_NF_CONNTRACK */ - static int match(const struct sk_buff *skb, const struct net_device *in, @@ -134,7 +75,6 @@ out_unlock: read_unlock_bh(&nf_conntrack_lock); return ret; } -#endif static int check(const char *tablename, const void *inf, diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c index 32fb998..77288c5 100644 --- a/net/netfilter/xt_length.c +++ b/net/netfilter/xt_length.c @@ -31,7 +31,7 @@ match(const struct sk_buff *skb, int *hotdrop) { const struct xt_length_info *info = matchinfo; - u_int16_t pktlen = ntohs(skb->nh.iph->tot_len); + u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; } @@ -47,7 +47,8 @@ match6(const struct sk_buff *skb, int *hotdrop) { const struct xt_length_info *info = matchinfo; - u_int16_t pktlen = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); + const u_int16_t pktlen = (ntohs(ipv6_hdr(skb)->payload_len) + + sizeof(struct ipv6hdr)); return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; } diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index 6fd8347..571a72a 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -1,10 +1,3 @@ -/* Kernel module to control the rate - * - * 2 September 1999: Changed from the target RATE to the match - * `limit', removed logging. Did I mention that - * Alexey is a fucking genius? - * Rusty Russell (rusty@rustcorp.com.au). */ - /* (C) 1999 Jrme de Vivie * (C) 1999 Herv Eychenne * diff --git a/net/netfilter/xt_mac.c b/net/netfilter/xt_mac.c index d430d90..1d3a1d9 100644 --- a/net/netfilter/xt_mac.c +++ b/net/netfilter/xt_mac.c @@ -37,8 +37,8 @@ match(const struct sk_buff *skb, const struct xt_mac_info *info = matchinfo; /* Is mac pointer valid? */ - return (skb->mac.raw >= skb->head - && (skb->mac.raw + ETH_HLEN) <= skb->data + return (skb_mac_header(skb) >= skb->head && + (skb_mac_header(skb) + ETH_HLEN) <= skb->data /* If so, compare... */ && ((!compare_ether_addr(eth_hdr(skb)->h_source, info->srcaddr)) ^ info->invert)); diff --git a/net/netfilter/xt_pkttype.c b/net/netfilter/xt_pkttype.c index 16e7b08..e1409fc 100644 --- a/net/netfilter/xt_pkttype.c +++ b/net/netfilter/xt_pkttype.c @@ -34,7 +34,7 @@ static int match(const struct sk_buff *s const struct xt_pkttype_info *info = matchinfo; if (skb->pkt_type == PACKET_LOOPBACK) - type = (MULTICAST(skb->nh.iph->daddr) + type = (MULTICAST(ip_hdr(skb)->daddr) ? PACKET_MULTICAST : PACKET_BROADCAST); else diff --git a/net/netfilter/xt_realm.c b/net/netfilter/xt_realm.c index 97ffc2f..c2017f8 100644 --- a/net/netfilter/xt_realm.c +++ b/net/netfilter/xt_realm.c @@ -1,7 +1,5 @@ /* IP tables module for matching the routing realm * - * $Id: ipt_realm.c,v 1.3 2004/03/05 13:25:40 laforge Exp $ - * * (C) 2003 by Sampsa Ranta * * This program is free software; you can redistribute it and/or modify diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index df37b91..149294f 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include @@ -36,7 +36,7 @@ match(const struct sk_buff *skb, if (nf_ct_is_untracked(skb)) statebit = XT_STATE_UNTRACKED; - else if (!nf_ct_get_ctinfo(skb, &ctinfo)) + else if (!nf_ct_get(skb, &ctinfo)) statebit = XT_STATE_INVALID; else statebit = XT_STATE_BIT(ctinfo); diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index e03a328..f2535e7 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -263,9 +263,6 @@ int netlbl_socket_setattr(const struct s int ret_val = -ENOENT; struct netlbl_dom_map *dom_entry; - if ((secattr->flags & NETLBL_SECATTR_DOMAIN) == 0) - return -ENOENT; - rcu_read_lock(); dom_entry = netlbl_domhsh_getentry(secattr->domain); if (dom_entry == NULL) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index c48b0f4..42d2fb9 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -56,6 +56,7 @@ #include #include #include #include +#include #include #include @@ -76,7 +77,8 @@ struct netlink_sock { unsigned long state; wait_queue_head_t wait; struct netlink_callback *cb; - spinlock_t cb_lock; + struct mutex *cb_mutex; + struct mutex cb_def_mutex; void (*data_ready)(struct sock *sk, int bytes); struct module *module; }; @@ -108,6 +110,7 @@ struct netlink_table { unsigned long *listeners; unsigned int nl_nonroot; unsigned int groups; + struct mutex *cb_mutex; struct module *module; int registered; }; @@ -118,6 +121,7 @@ static DECLARE_WAIT_QUEUE_HEAD(nl_table_ static int netlink_dump(struct sock *sk); static void netlink_destroy_callback(struct netlink_callback *cb); +static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb); static DEFINE_RWLOCK(nl_table_lock); static atomic_t nl_table_users = ATOMIC_INIT(0); @@ -370,7 +374,8 @@ static struct proto netlink_proto = { .obj_size = sizeof(struct netlink_sock), }; -static int __netlink_create(struct socket *sock, int protocol) +static int __netlink_create(struct socket *sock, struct mutex *cb_mutex, + int protocol) { struct sock *sk; struct netlink_sock *nlk; @@ -384,7 +389,12 @@ static int __netlink_create(struct socke sock_init_data(sock, sk); nlk = nlk_sk(sk); - spin_lock_init(&nlk->cb_lock); + if (cb_mutex) + nlk->cb_mutex = cb_mutex; + else { + nlk->cb_mutex = &nlk->cb_def_mutex; + mutex_init(nlk->cb_mutex); + } init_waitqueue_head(&nlk->wait); sk->sk_destruct = netlink_sock_destruct; @@ -395,8 +405,8 @@ static int __netlink_create(struct socke static int netlink_create(struct socket *sock, int protocol) { struct module *module = NULL; + struct mutex *cb_mutex; struct netlink_sock *nlk; - unsigned int groups; int err = 0; sock->state = SS_UNCONNECTED; @@ -418,10 +428,10 @@ #endif if (nl_table[protocol].registered && try_module_get(nl_table[protocol].module)) module = nl_table[protocol].module; - groups = nl_table[protocol].groups; + cb_mutex = nl_table[protocol].cb_mutex; netlink_unlock_table(); - if ((err = __netlink_create(sock, protocol)) < 0) + if ((err = __netlink_create(sock, cb_mutex, protocol)) < 0) goto out_module; nlk = nlk_sk(sock->sk); @@ -446,14 +456,14 @@ static int netlink_release(struct socket sock_orphan(sk); nlk = nlk_sk(sk); - spin_lock(&nlk->cb_lock); + mutex_lock(nlk->cb_mutex); if (nlk->cb) { if (nlk->cb->done) nlk->cb->done(nlk->cb); netlink_destroy_callback(nlk->cb); nlk->cb = NULL; } - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); /* OK. Socket is unlinked, and, therefore, no new packets will arrive */ @@ -1215,7 +1225,7 @@ static int netlink_recvmsg(struct kiocb copied = len; } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (msg->msg_name) { @@ -1242,6 +1252,9 @@ static int netlink_recvmsg(struct kiocb scm_recv(sock, msg, siocb->scm, flags); + if (flags & MSG_TRUNC) + copied = skb->len; + out: netlink_rcv_wake(sk); return err ? : copied; @@ -1265,7 +1278,7 @@ static void netlink_data_ready(struct so struct sock * netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), - struct module *module) + struct mutex *cb_mutex, struct module *module) { struct socket *sock; struct sock *sk; @@ -1280,7 +1293,7 @@ netlink_kernel_create(int unit, unsigned if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) return NULL; - if (__netlink_create(sock, unit) < 0) + if (__netlink_create(sock, cb_mutex, unit) < 0) goto out_sock_release; if (groups < 32) @@ -1304,6 +1317,7 @@ netlink_kernel_create(int unit, unsigned netlink_table_grab(); nl_table[unit].groups = groups; nl_table[unit].listeners = listeners; + nl_table[unit].cb_mutex = cb_mutex; nl_table[unit].module = module; nl_table[unit].registered = 1; netlink_table_ungrab(); @@ -1346,7 +1360,7 @@ static int netlink_dump(struct sock *sk) if (!skb) goto errout; - spin_lock(&nlk->cb_lock); + mutex_lock(nlk->cb_mutex); cb = nlk->cb; if (cb == NULL) { @@ -1357,7 +1371,7 @@ static int netlink_dump(struct sock *sk) len = cb->dump(skb, cb); if (len > 0) { - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, len); return 0; @@ -1375,13 +1389,13 @@ static int netlink_dump(struct sock *sk) if (cb->done) cb->done(cb); nlk->cb = NULL; - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); netlink_destroy_callback(cb); return 0; errout_skb: - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); kfree_skb(skb); errout: return err; @@ -1413,19 +1427,24 @@ int netlink_dump_start(struct sock *ssk, } nlk = nlk_sk(sk); /* A dump or destruction is in progress... */ - spin_lock(&nlk->cb_lock); + mutex_lock(nlk->cb_mutex); if (nlk->cb || sock_flag(sk, SOCK_DEAD)) { - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); netlink_destroy_callback(cb); sock_put(sk); return -EBUSY; } nlk->cb = cb; - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); netlink_dump(sk); sock_put(sk); - return 0; + + /* We successfully started a dump, by returning -EINTR we + * signal the queue mangement to interrupt processing of + * any netlink messages so userspace gets a chance to read + * the results. */ + return -EINTR; } void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) @@ -1462,27 +1481,35 @@ void netlink_ack(struct sk_buff *in_skb, } static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, - struct nlmsghdr *, int *)) + struct nlmsghdr *)) { struct nlmsghdr *nlh; int err; while (skb->len >= nlmsg_total_size(0)) { - nlh = (struct nlmsghdr *) skb->data; + nlh = nlmsg_hdr(skb); + err = 0; if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) return 0; - if (cb(skb, nlh, &err) < 0) { - /* Not an error, but we have to interrupt processing - * here. Note: that in this case we do not pull - * message from skb, it will be processed later. - */ - if (err == 0) - return -1; + /* Only requests are handled by the kernel */ + if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) + goto skip; + + /* Skip control messages */ + if (nlh->nlmsg_type < NLMSG_MIN_TYPE) + goto skip; + + err = cb(skb, nlh); + if (err == -EINTR) { + /* Not an error, but we interrupt processing */ + netlink_queue_skip(nlh, skb); + return err; + } +skip: + if (nlh->nlmsg_flags & NLM_F_ACK || err) netlink_ack(skb, nlh, err); - } else if (nlh->nlmsg_flags & NLM_F_ACK) - netlink_ack(skb, nlh, 0); netlink_queue_skip(nlh, skb); } @@ -1504,9 +1531,14 @@ static int netlink_rcv_skb(struct sk_buf * * qlen must be initialized to 0 before the initial entry, afterwards * the function may be called repeatedly until qlen reaches 0. + * + * The callback function may return -EINTR to signal that processing + * of netlink messages shall be interrupted. In this case the message + * currently being processed will NOT be requeued onto the receive + * queue. */ void netlink_run_queue(struct sock *sk, unsigned int *qlen, - int (*cb)(struct sk_buff *, struct nlmsghdr *, int *)) + int (*cb)(struct sk_buff *, struct nlmsghdr *)) { struct sk_buff *skb; @@ -1537,7 +1569,7 @@ void netlink_run_queue(struct sock *sk, * Pulls the given netlink message off the socket buffer so the next * call to netlink_queue_run() will not reconsider the message. */ -void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb) +static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb) { int msglen = NLMSG_ALIGN(nlh->nlmsg_len); @@ -1820,12 +1852,10 @@ core_initcall(netlink_proto_init); EXPORT_SYMBOL(netlink_ack); EXPORT_SYMBOL(netlink_run_queue); -EXPORT_SYMBOL(netlink_queue_skip); EXPORT_SYMBOL(netlink_broadcast); EXPORT_SYMBOL(netlink_dump_start); EXPORT_SYMBOL(netlink_kernel_create); EXPORT_SYMBOL(netlink_register_notifier); -EXPORT_SYMBOL(netlink_set_err); EXPORT_SYMBOL(netlink_set_nonroot); EXPORT_SYMBOL(netlink_unicast); EXPORT_SYMBOL(netlink_unregister_notifier); diff --git a/net/netlink/attr.c b/net/netlink/attr.c index 0041395..df5f820 100644 --- a/net/netlink/attr.c +++ b/net/netlink/attr.c @@ -67,6 +67,11 @@ static int validate_nla(struct nlattr *n } break; + case NLA_BINARY: + if (pt->len && attrlen > pt->len) + return -ERANGE; + break; + default: if (pt->len) minlen = pt->len; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index c299679..6e31234 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -295,66 +295,46 @@ int genl_unregister_family(struct genl_f return -ENOENT; } -static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, - int *errp) +static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct genl_ops *ops; struct genl_family *family; struct genl_info info; struct genlmsghdr *hdr = nlmsg_data(nlh); - int hdrlen, err = -EINVAL; - - if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) - goto ignore; - - if (nlh->nlmsg_type < NLMSG_MIN_TYPE) - goto ignore; + int hdrlen, err; family = genl_family_find_byid(nlh->nlmsg_type); - if (family == NULL) { - err = -ENOENT; - goto errout; - } + if (family == NULL) + return -ENOENT; hdrlen = GENL_HDRLEN + family->hdrsize; if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) - goto errout; + return -EINVAL; ops = genl_get_cmd(hdr->cmd, family); - if (ops == NULL) { - err = -EOPNOTSUPP; - goto errout; - } + if (ops == NULL) + return -EOPNOTSUPP; - if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb, CAP_NET_ADMIN)) { - err = -EPERM; - goto errout; - } + if ((ops->flags & GENL_ADMIN_PERM) && + security_netlink_recv(skb, CAP_NET_ADMIN)) + return -EPERM; if (nlh->nlmsg_flags & NLM_F_DUMP) { - if (ops->dumpit == NULL) { - err = -EOPNOTSUPP; - goto errout; - } + if (ops->dumpit == NULL) + return -EOPNOTSUPP; - *errp = err = netlink_dump_start(genl_sock, skb, nlh, - ops->dumpit, ops->done); - if (err == 0) - skb_pull(skb, min(NLMSG_ALIGN(nlh->nlmsg_len), - skb->len)); - return -1; + return netlink_dump_start(genl_sock, skb, nlh, + ops->dumpit, ops->done); } - if (ops->doit == NULL) { - err = -EOPNOTSUPP; - goto errout; - } + if (ops->doit == NULL) + return -EOPNOTSUPP; if (family->attrbuf) { err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr, ops->policy); if (err < 0) - goto errout; + return err; } info.snd_seq = nlh->nlmsg_seq; @@ -364,15 +344,7 @@ static int genl_rcv_msg(struct sk_buff * info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.attrs = family->attrbuf; - *errp = err = ops->doit(skb, &info); - return err; - -ignore: - return 0; - -errout: - *errp = err; - return -1; + return ops->doit(skb, &info); } static void genl_rcv(struct sock *sk, int len) @@ -586,7 +558,7 @@ static int __init genl_init(void) netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID, - genl_rcv, THIS_MODULE); + genl_rcv, NULL, THIS_MODULE); if (genl_sock == NULL) panic("GENL: Cannot initialize generic netlink\n"); diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index bf9837d..5d4a26c 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -625,42 +625,42 @@ static int nr_connect(struct socket *soc ax25_address *source = NULL; ax25_uid_assoc *user; struct net_device *dev; + int err = 0; lock_sock(sk); if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { sock->state = SS_CONNECTED; - release_sock(sk); - return 0; /* Connect completed during a ERESTARTSYS event */ + goto out_release; /* Connect completed during a ERESTARTSYS event */ } if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { sock->state = SS_UNCONNECTED; - release_sock(sk); - return -ECONNREFUSED; + err = -ECONNREFUSED; + goto out_release; } if (sk->sk_state == TCP_ESTABLISHED) { - release_sock(sk); - return -EISCONN; /* No reconnect on a seqpacket socket */ + err = -EISCONN; /* No reconnect on a seqpacket socket */ + goto out_release; } sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) { - release_sock(sk); - return -EINVAL; + err = -EINVAL; + goto out_release; } if (addr->sax25_family != AF_NETROM) { - release_sock(sk); - return -EINVAL; + err = -EINVAL; + goto out_release; } if (sock_flag(sk, SOCK_ZAPPED)) { /* Must bind first - autobinding in this may or may not work */ sock_reset_flag(sk, SOCK_ZAPPED); if ((dev = nr_dev_first()) == NULL) { - release_sock(sk); - return -ENETUNREACH; + err = -ENETUNREACH; + goto out_release; } source = (ax25_address *)dev->dev_addr; @@ -671,8 +671,8 @@ static int nr_connect(struct socket *soc } else { if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) { dev_put(dev); - release_sock(sk); - return -EPERM; + err = -EPERM; + goto out_release; } nr->user_addr = *source; } @@ -707,8 +707,8 @@ static int nr_connect(struct socket *soc /* Now the loop */ if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { - release_sock(sk); - return -EINPROGRESS; + err = -EINPROGRESS; + goto out_release; } /* @@ -716,46 +716,46 @@ static int nr_connect(struct socket *soc * closed. */ if (sk->sk_state == TCP_SYN_SENT) { - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); + DEFINE_WAIT(wait); - add_wait_queue(sk->sk_sleep, &wait); for (;;) { - set_current_state(TASK_INTERRUPTIBLE); + prepare_to_wait(sk->sk_sleep, &wait, + TASK_INTERRUPTIBLE); if (sk->sk_state != TCP_SYN_SENT) break; - release_sock(sk); - if (!signal_pending(tsk)) { + if (!signal_pending(current)) { + release_sock(sk); schedule(); lock_sock(sk); continue; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -ERESTARTSYS; + err = -ERESTARTSYS; + break; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); + finish_wait(sk->sk_sleep, &wait); + if (err) + goto out_release; } if (sk->sk_state != TCP_ESTABLISHED) { sock->state = SS_UNCONNECTED; - release_sock(sk); - return sock_error(sk); /* Always set at this point */ + err = sock_error(sk); /* Always set at this point */ + goto out_release; } sock->state = SS_CONNECTED; + +out_release: release_sock(sk); - return 0; + return err; } static int nr_accept(struct socket *sock, struct socket *newsock, int flags) { - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); struct sk_buff *skb; struct sock *newsk; + DEFINE_WAIT(wait); struct sock *sk; int err = 0; @@ -765,42 +765,40 @@ static int nr_accept(struct socket *sock lock_sock(sk); if (sk->sk_type != SOCK_SEQPACKET) { err = -EOPNOTSUPP; - goto out; + goto out_release; } if (sk->sk_state != TCP_LISTEN) { err = -EINVAL; - goto out; + goto out_release; } /* * The write queue this time is holding sockets ready to use * hooked into the SABM we saved */ - add_wait_queue(sk->sk_sleep, &wait); for (;;) { + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); skb = skb_dequeue(&sk->sk_receive_queue); if (skb) break; - current->state = TASK_INTERRUPTIBLE; - release_sock(sk); if (flags & O_NONBLOCK) { - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -EWOULDBLOCK; + err = -EWOULDBLOCK; + break; } - if (!signal_pending(tsk)) { + if (!signal_pending(current)) { + release_sock(sk); schedule(); lock_sock(sk); continue; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -ERESTARTSYS; + err = -ERESTARTSYS; + break; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); + finish_wait(sk->sk_sleep, &wait); + if (err) + goto out_release; newsk = skb->sk; newsk->sk_socket = newsock; @@ -811,8 +809,9 @@ static int nr_accept(struct socket *sock sk_acceptq_removed(sk); newsock->sk = newsk; -out: +out_release: release_sock(sk); + return err; } @@ -878,7 +877,7 @@ int nr_rx_frame(struct sk_buff *skb, str if (frametype == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) { skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); return nr_rx_ip(skb, dev); } @@ -904,7 +903,7 @@ int nr_rx_frame(struct sk_buff *skb, str } if (sk != NULL) { - skb->h.raw = skb->data; + skb_reset_transport_header(skb); if (frametype == NR_CONNACK && skb->len == 22) nr_sk(sk)->bpqext = 1; @@ -1074,6 +1073,7 @@ static int nr_sendmsg(struct kiocb *iocb goto out; skb_reserve(skb, size - len); + skb_reset_transport_header(skb); /* * Push down the NET/ROM header @@ -1094,14 +1094,12 @@ static int nr_sendmsg(struct kiocb *iocb /* * Put the data on the end */ + skb_put(skb, len); - skb->h.raw = skb_put(skb, len); - - asmptr = skb->h.raw; SOCK_DEBUG(sk, "NET/ROM: Appending user data\n"); /* User data follows immediately after the NET/ROM transport header */ - if (memcpy_fromiovec(asmptr, msg->msg_iov, len)) { + if (memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len)) { kfree_skb(skb); err = -EFAULT; goto out; @@ -1149,7 +1147,7 @@ static int nr_recvmsg(struct kiocb *iocb return er; } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); copied = skb->len; if (copied > size) { @@ -1161,7 +1159,8 @@ static int nr_recvmsg(struct kiocb *iocb if (sax != NULL) { sax->sax25_family = AF_NETROM; - memcpy(sax->sax25_call.ax25_call, skb->data + 7, AX25_ADDR_LEN); + skb_copy_from_linear_data_offset(skb, 7, sax->sax25_call.ax25_call, + AX25_ADDR_LEN); } msg->msg_namelen = sizeof(*sax); @@ -1209,6 +1208,12 @@ static int nr_ioctl(struct socket *sock, release_sock(sk); return ret; + case SIOCGSTAMPNS: + lock_sock(sk); + ret = sock_get_timestampns(sk, argp); + release_sock(sk); + return ret; + case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 9a97ed6..c7b5d93 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -56,8 +56,8 @@ int nr_rx_ip(struct sk_buff *skb, struct /* Spoof incoming device */ skb->dev = dev; - skb->mac.raw = skb->nh.raw; - skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); skb->pkt_type = PACKET_HOST; netif_rx(skb); diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c index 5560acb..6817648 100644 --- a/net/netrom/nr_in.c +++ b/net/netrom/nr_in.c @@ -51,10 +51,12 @@ static int nr_queue_rx_frame(struct sock if ((skbn = alloc_skb(nr->fraglen, GFP_ATOMIC)) == NULL) return 1; - skbn->h.raw = skbn->data; + skb_reset_transport_header(skbn); while ((skbo = skb_dequeue(&nr->frag_queue)) != NULL) { - memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + skb_copy_from_linear_data(skbo, + skb_put(skbn, skbo->len), + skbo->len); kfree_skb(skbo); } diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c index e856ae1..f324d5d 100644 --- a/net/netrom/nr_loopback.c +++ b/net/netrom/nr_loopback.c @@ -34,8 +34,8 @@ int nr_loopback_queue(struct sk_buff *sk struct sk_buff *skbn; if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) { - memcpy(skb_put(skbn, skb->len), skb->data, skb->len); - skbn->h.raw = skbn->data; + skb_copy_from_linear_data(skb, skb_put(skbn, skb->len), skb->len); + skb_reset_transport_header(skbn); skb_queue_tail(&loopback_queue, skbn); diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c index 0cbfb61..e3e6c44 100644 --- a/net/netrom/nr_out.c +++ b/net/netrom/nr_out.c @@ -40,7 +40,7 @@ void nr_output(struct sock *sk, struct s if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) { /* Save a copy of the Transport Header */ - memcpy(transport, skb->data, NR_TRANSPORT_LEN); + skb_copy_from_linear_data(skb, transport, NR_TRANSPORT_LEN); skb_pull(skb, NR_TRANSPORT_LEN); frontlen = skb_headroom(skb); @@ -54,13 +54,13 @@ void nr_output(struct sock *sk, struct s len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE; /* Copy the user data */ - memcpy(skb_put(skbn, len), skb->data, len); + skb_copy_from_linear_data(skb, skb_put(skbn, len), len); skb_pull(skb, len); /* Duplicate the Transport Header */ skb_push(skbn, NR_TRANSPORT_LEN); - memcpy(skbn->data, transport, NR_TRANSPORT_LEN); - + skb_copy_to_linear_data(skbn, transport, + NR_TRANSPORT_LEN); if (skb->len > 0) skbn->data[4] |= NR_MORE_FLAG; diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c index 07b694d..04e7d0d 100644 --- a/net/netrom/nr_subr.c +++ b/net/netrom/nr_subr.c @@ -226,13 +226,13 @@ void __nr_transmit_reply(struct sk_buff dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - memcpy(dptr, skb->data + 7, AX25_ADDR_LEN); + skb_copy_from_linear_data_offset(skb, 7, dptr, AX25_ADDR_LEN); dptr[6] &= ~AX25_CBIT; dptr[6] &= ~AX25_EBIT; dptr[6] |= AX25_SSSID_SPARE; dptr += AX25_ADDR_LEN; - memcpy(dptr, skb->data + 0, AX25_ADDR_LEN); + skb_copy_from_linear_data(skb, dptr, AX25_ADDR_LEN); dptr[6] &= ~AX25_CBIT; dptr[6] |= AX25_EBIT; dptr[6] |= AX25_SSSID_SPARE; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 28d47e8..02e401c 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -114,22 +114,22 @@ On receive: ----------- Incoming, dev->hard_header!=NULL - mac.raw -> ll header - data -> data + mac_header -> ll header + data -> data Outgoing, dev->hard_header!=NULL - mac.raw -> ll header - data -> ll header + mac_header -> ll header + data -> ll header Incoming, dev->hard_header==NULL - mac.raw -> UNKNOWN position. It is very likely, that it points to ll header. - PPP makes it, that is wrong, because introduce assymetry - between rx and tx paths. - data -> data + mac_header -> UNKNOWN position. It is very likely, that it points to ll + header. PPP makes it, that is wrong, because introduce + assymetry between rx and tx paths. + data -> data Outgoing, dev->hard_header==NULL - mac.raw -> data. ll header is still not built! - data -> data + mac_header -> data. ll header is still not built! + data -> data Resume If dev->hard_header==NULL we are unlikely to restore sensible ll header. @@ -139,12 +139,12 @@ On transmit: ------------ dev->hard_header != NULL - mac.raw -> ll header - data -> ll header + mac_header -> ll header + data -> ll header dev->hard_header == NULL (ll header is added by device, we cannot control it) - mac.raw -> data - data -> data + mac_header -> data + data -> data We should set nh.raw on output to correct posistion, packet classifier depends on it. @@ -201,7 +201,8 @@ #endif struct packet_type prot_hook; spinlock_t bind_lock; unsigned int running:1, /* prot_hook is attached*/ - auxdata:1; + auxdata:1, + origdev:1; int ifindex; /* bound device */ __be16 num; #ifdef CONFIG_PACKET_MULTICAST @@ -284,7 +285,7 @@ static int packet_rcv_spkt(struct sk_buf * Incoming packets have ll header pulled, * push it back. * - * For outgoing ones skb->data == skb->mac.raw + * For outgoing ones skb->data == skb_mac_header(skb) * so that this procedure is noop. */ @@ -303,7 +304,7 @@ static int packet_rcv_spkt(struct sk_buf spkt = &PACKET_SKB_CB(skb)->sa.pkt; - skb_push(skb, skb->data-skb->mac.raw); + skb_push(skb, skb->data - skb_mac_header(skb)); /* * The SOCK_PACKET socket receives _all_ frames. @@ -401,14 +402,14 @@ static int packet_sendmsg_spkt(struct ki * notable one here. This should really be fixed at the driver level. */ skb_reserve(skb, LL_RESERVED_SPACE(dev)); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); /* Try to align data part correctly */ if (dev->hard_header) { skb->data -= dev->hard_header_len; skb->tail -= dev->hard_header_len; if (len < dev->hard_header_len) - skb->nh.raw = skb->data; + skb_reset_network_header(skb); } /* Returns -EFAULT on error */ @@ -488,10 +489,10 @@ static int packet_rcv(struct sk_buff *sk never delivered to user. */ if (sk->sk_type != SOCK_DGRAM) - skb_push(skb, skb->data - skb->mac.raw); + skb_push(skb, skb->data - skb_mac_header(skb)); else if (skb->pkt_type == PACKET_OUTGOING) { /* Special case: outgoing packets have ll header at head */ - skb_pull(skb, skb->nh.raw - skb->data); + skb_pull(skb, skb_network_offset(skb)); } } @@ -528,7 +529,10 @@ static int packet_rcv(struct sk_buff *sk sll->sll_hatype = dev->type; sll->sll_protocol = skb->protocol; sll->sll_pkttype = skb->pkt_type; - sll->sll_ifindex = dev->ifindex; + if (unlikely(po->origdev) && skb->pkt_type == PACKET_HOST) + sll->sll_ifindex = orig_dev->ifindex; + else + sll->sll_ifindex = dev->ifindex; sll->sll_halen = 0; if (dev->hard_header_parse) @@ -582,6 +586,7 @@ static int tpacket_rcv(struct sk_buff *s unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER; unsigned short macoff, netoff; struct sk_buff *copy_skb = NULL; + struct timeval tv; if (skb->pkt_type == PACKET_LOOPBACK) goto drop; @@ -591,10 +596,10 @@ static int tpacket_rcv(struct sk_buff *s if (dev->hard_header) { if (sk->sk_type != SOCK_DGRAM) - skb_push(skb, skb->data - skb->mac.raw); + skb_push(skb, skb->data - skb_mac_header(skb)); else if (skb->pkt_type == PACKET_OUTGOING) { /* Special case: outgoing packets have ll header at head */ - skb_pull(skb, skb->nh.raw - skb->data); + skb_pull(skb, skb_network_offset(skb)); } } @@ -612,7 +617,7 @@ static int tpacket_rcv(struct sk_buff *s if (sk->sk_type == SOCK_DGRAM) { macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16; } else { - unsigned maclen = skb->nh.raw - skb->data; + unsigned maclen = skb_network_offset(skb); netoff = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : maclen)); macoff = netoff - maclen; } @@ -656,12 +661,13 @@ static int tpacket_rcv(struct sk_buff *s h->tp_snaplen = snaplen; h->tp_mac = macoff; h->tp_net = netoff; - if (skb->tstamp.off_sec == 0) { + if (skb->tstamp.tv64 == 0) { __net_timestamp(skb); sock_enable_timestamp(sk); } - h->tp_sec = skb->tstamp.off_sec; - h->tp_usec = skb->tstamp.off_usec; + tv = ktime_to_timeval(skb->tstamp); + h->tp_sec = tv.tv_sec; + h->tp_usec = tv.tv_usec; sll = (struct sockaddr_ll*)((u8*)h + TPACKET_ALIGN(sizeof(*h))); sll->sll_halen = 0; @@ -671,7 +677,10 @@ static int tpacket_rcv(struct sk_buff *s sll->sll_hatype = dev->type; sll->sll_protocol = skb->protocol; sll->sll_pkttype = skb->pkt_type; - sll->sll_ifindex = dev->ifindex; + if (unlikely(po->origdev) && skb->pkt_type == PACKET_HOST) + sll->sll_ifindex = orig_dev->ifindex; + else + sll->sll_ifindex = dev->ifindex; h->tp_status = status; smp_mb(); @@ -766,14 +775,14 @@ static int packet_sendmsg(struct kiocb * goto out_unlock; skb_reserve(skb, LL_RESERVED_SPACE(dev)); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); if (dev->hard_header) { int res; err = -EINVAL; res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len); if (sock->type != SOCK_DGRAM) { - skb->tail = skb->data; + skb_reset_tail_pointer(skb); skb->len = 0; } else if (res < 0) goto out_free; @@ -1143,7 +1152,7 @@ #endif aux.tp_len = PACKET_SKB_CB(skb)->origlen; aux.tp_snaplen = skb->len; aux.tp_mac = 0; - aux.tp_net = skb->nh.raw - skb->data; + aux.tp_net = skb_network_offset(skb); put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux); } @@ -1411,6 +1420,18 @@ #endif po->auxdata = !!val; return 0; } + case PACKET_ORIGDEV: + { + int val; + + if (optlen < sizeof(val)) + return -EINVAL; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + + po->origdev = !!val; + return 0; + } default: return -ENOPROTOOPT; } @@ -1454,6 +1475,13 @@ static int packet_getsockopt(struct sock data = &val; break; + case PACKET_ORIGDEV: + if (len > sizeof(int)) + len = sizeof(int); + val = po->origdev; + + data = &val; + break; default: return -ENOPROTOOPT; } @@ -1543,6 +1571,8 @@ static int packet_ioctl(struct socket *s } case SIOCGSTAMP: return sock_get_timestamp(sk, (struct timeval __user *)arg); + case SIOCGSTAMPNS: + return sock_get_timestampns(sk, (struct timespec __user *)arg); #ifdef CONFIG_INET case SIOCADDRT: diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index f92d531..d476c43 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -812,26 +812,26 @@ rose_try_next_neigh: * closed. */ if (sk->sk_state == TCP_SYN_SENT) { - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); + DEFINE_WAIT(wait); - add_wait_queue(sk->sk_sleep, &wait); for (;;) { - set_current_state(TASK_INTERRUPTIBLE); + prepare_to_wait(sk->sk_sleep, &wait, + TASK_INTERRUPTIBLE); if (sk->sk_state != TCP_SYN_SENT) break; - release_sock(sk); - if (!signal_pending(tsk)) { + if (!signal_pending(current)) { + release_sock(sk); schedule(); lock_sock(sk); continue; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -ERESTARTSYS; + err = -ERESTARTSYS; + break; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); + finish_wait(sk->sk_sleep, &wait); + + if (err) + goto out_release; } if (sk->sk_state != TCP_ESTABLISHED) { @@ -856,10 +856,9 @@ out_release: static int rose_accept(struct socket *sock, struct socket *newsock, int flags) { - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); struct sk_buff *skb; struct sock *newsk; + DEFINE_WAIT(wait); struct sock *sk; int err = 0; @@ -869,42 +868,41 @@ static int rose_accept(struct socket *so lock_sock(sk); if (sk->sk_type != SOCK_SEQPACKET) { err = -EOPNOTSUPP; - goto out; + goto out_release; } if (sk->sk_state != TCP_LISTEN) { err = -EINVAL; - goto out; + goto out_release; } /* * The write queue this time is holding sockets ready to use * hooked into the SABM we saved */ - add_wait_queue(sk->sk_sleep, &wait); for (;;) { + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + skb = skb_dequeue(&sk->sk_receive_queue); if (skb) break; - current->state = TASK_INTERRUPTIBLE; - release_sock(sk); if (flags & O_NONBLOCK) { - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -EWOULDBLOCK; + err = -EWOULDBLOCK; + break; } - if (!signal_pending(tsk)) { + if (!signal_pending(current)) { + release_sock(sk); schedule(); lock_sock(sk); continue; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); - return -ERESTARTSYS; + err = -ERESTARTSYS; + break; } - current->state = TASK_RUNNING; - remove_wait_queue(sk->sk_sleep, &wait); + finish_wait(sk->sk_sleep, &wait); + if (err) + goto out_release; newsk = skb->sk; newsk->sk_socket = newsock; @@ -916,7 +914,7 @@ static int rose_accept(struct socket *so sk->sk_ack_backlog--; newsock->sk = newsk; -out: +out_release: release_sock(sk); return err; @@ -1105,9 +1103,10 @@ static int rose_sendmsg(struct kiocb *io */ SOCK_DEBUG(sk, "ROSE: Appending user data\n"); - asmptr = skb->h.raw = skb_put(skb, len); + skb_reset_transport_header(skb); + skb_put(skb, len); - err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); if (err) { kfree_skb(skb); return err; @@ -1155,7 +1154,7 @@ #define ROSE_PACLEN (256-ROSE_MIN_LEN) int lg; /* Save a copy of the Header */ - memcpy(header, skb->data, ROSE_MIN_LEN); + skb_copy_from_linear_data(skb, header, ROSE_MIN_LEN); skb_pull(skb, ROSE_MIN_LEN); frontlen = skb_headroom(skb); @@ -1175,12 +1174,12 @@ #define ROSE_PACLEN (256-ROSE_MIN_LEN) lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN; /* Copy the user data */ - memcpy(skb_put(skbn, lg), skb->data, lg); + skb_copy_from_linear_data(skb, skb_put(skbn, lg), lg); skb_pull(skb, lg); /* Duplicate the Header */ skb_push(skbn, ROSE_MIN_LEN); - memcpy(skbn->data, header, ROSE_MIN_LEN); + skb_copy_to_linear_data(skbn, header, ROSE_MIN_LEN); if (skb->len > 0) skbn->data[2] |= M_BIT; @@ -1234,7 +1233,7 @@ static int rose_recvmsg(struct kiocb *io *asmptr = qbit; } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); copied = skb->len; if (copied > size) { @@ -1296,6 +1295,9 @@ static int rose_ioctl(struct socket *soc case SIOCGSTAMP: return sock_get_timestamp(sk, (struct timeval __user *) argp); + case SIOCGSTAMPNS: + return sock_get_timestampns(sk, (struct timespec __user *) argp); + case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c index 3e41bd9..cd01642 100644 --- a/net/rose/rose_loopback.c +++ b/net/rose/rose_loopback.c @@ -77,7 +77,7 @@ static void rose_loopback_timer(unsigned dest = (rose_address *)(skb->data + 4); lci_o = 0xFFF - lci_i; - skb->h.raw = skb->data; + skb_reset_transport_header(skb); sk = rose_find_socket(lci_o, &rose_loopback_neigh); if (sk) { diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index a1233e1..1f9aefd 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -906,7 +906,7 @@ #endif } } else { - skb->h.raw = skb->data; + skb_reset_transport_header(skb); res = rose_process_rx_frame(sk, skb); goto out; } diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig new file mode 100644 index 0000000..8750f6d --- /dev/null +++ b/net/rxrpc/Kconfig @@ -0,0 +1,42 @@ +# +# RxRPC session sockets +# + +config AF_RXRPC + tristate "RxRPC session sockets" + depends on EXPERIMENTAL + help + Say Y or M here to include support for RxRPC session sockets (just + the transport part, not the presentation part: (un)marshalling is + left to the application). + + These are used for AFS kernel filesystem and userspace utilities. + + This module at the moment only supports client operations and is + currently incomplete. + + See Documentation/networking/rxrpc.txt. + + +config AF_RXRPC_DEBUG + bool "RxRPC dynamic debugging" + depends on AF_RXRPC + help + Say Y here to make runtime controllable debugging messages appear. + + See Documentation/networking/rxrpc.txt. + + +config RXKAD + tristate "RxRPC Kerberos security" + depends on AF_RXRPC && KEYS + select CRYPTO + select CRYPTO_MANAGER + select CRYPTO_BLKCIPHER + select CRYPTO_PCBC + select CRYPTO_FCRYPT + help + Provide kerberos 4 and AFS kaserver security handling for AF_RXRPC + through the use of the key retention service. + + See Documentation/networking/rxrpc.txt. diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 6efcb6f..c46867c 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -1,25 +1,29 @@ # -# Makefile for Linux kernel Rx RPC +# Makefile for Linux kernel RxRPC # -#CFLAGS += -finstrument-functions - -rxrpc-objs := \ - call.o \ - connection.o \ - krxiod.o \ - krxsecd.o \ - krxtimod.o \ - main.o \ - peer.o \ - rxrpc_syms.o \ - transport.o +af-rxrpc-objs := \ + af_rxrpc.o \ + ar-accept.o \ + ar-ack.o \ + ar-call.o \ + ar-connection.o \ + ar-connevent.o \ + ar-error.o \ + ar-input.o \ + ar-key.o \ + ar-local.o \ + ar-output.o \ + ar-peer.o \ + ar-recvmsg.o \ + ar-security.o \ + ar-skbuff.o \ + ar-transport.o ifeq ($(CONFIG_PROC_FS),y) -rxrpc-objs += proc.o -endif -ifeq ($(CONFIG_SYSCTL),y) -rxrpc-objs += sysctl.o +af-rxrpc-objs += ar-proc.o endif -obj-$(CONFIG_RXRPC) := rxrpc.o +obj-$(CONFIG_AF_RXRPC) += af-rxrpc.o + +obj-$(CONFIG_RXKAD) += rxkad.o diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c new file mode 100644 index 0000000..2c57df9 --- /dev/null +++ b/net/rxrpc/af_rxrpc.c @@ -0,0 +1,879 @@ +/* AF_RXRPC implementation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +MODULE_DESCRIPTION("RxRPC network protocol"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_RXRPC); + +unsigned rxrpc_debug; // = RXRPC_DEBUG_KPROTO; +module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(rxrpc_debug, "RxRPC debugging mask"); + +static int sysctl_rxrpc_max_qlen __read_mostly = 10; + +static struct proto rxrpc_proto; +static const struct proto_ops rxrpc_rpc_ops; + +/* local epoch for detecting local-end reset */ +__be32 rxrpc_epoch; + +/* current debugging ID */ +atomic_t rxrpc_debug_id; + +/* count of skbs currently in use */ +atomic_t rxrpc_n_skbs; + +struct workqueue_struct *rxrpc_workqueue; + +static void rxrpc_sock_destructor(struct sock *); + +/* + * see if an RxRPC socket is currently writable + */ +static inline int rxrpc_writable(struct sock *sk) +{ + return atomic_read(&sk->sk_wmem_alloc) < (size_t) sk->sk_sndbuf; +} + +/* + * wait for write bufferage to become available + */ +static void rxrpc_write_space(struct sock *sk) +{ + _enter("%p", sk); + read_lock(&sk->sk_callback_lock); + if (rxrpc_writable(sk)) { + if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + wake_up_interruptible(sk->sk_sleep); + sk_wake_async(sk, 2, POLL_OUT); + } + read_unlock(&sk->sk_callback_lock); +} + +/* + * validate an RxRPC address + */ +static int rxrpc_validate_address(struct rxrpc_sock *rx, + struct sockaddr_rxrpc *srx, + int len) +{ + if (len < sizeof(struct sockaddr_rxrpc)) + return -EINVAL; + + if (srx->srx_family != AF_RXRPC) + return -EAFNOSUPPORT; + + if (srx->transport_type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + len -= offsetof(struct sockaddr_rxrpc, transport); + if (srx->transport_len < sizeof(sa_family_t) || + srx->transport_len > len) + return -EINVAL; + + if (srx->transport.family != rx->proto) + return -EAFNOSUPPORT; + + switch (srx->transport.family) { + case AF_INET: + _debug("INET: %x @ %u.%u.%u.%u", + ntohs(srx->transport.sin.sin_port), + NIPQUAD(srx->transport.sin.sin_addr)); + if (srx->transport_len > 8) + memset((void *)&srx->transport + 8, 0, + srx->transport_len - 8); + break; + + case AF_INET6: + default: + return -EAFNOSUPPORT; + } + + return 0; +} + +/* + * bind a local address to an RxRPC socket + */ +static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len) +{ + struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) saddr; + struct sock *sk = sock->sk; + struct rxrpc_local *local; + struct rxrpc_sock *rx = rxrpc_sk(sk), *prx; + __be16 service_id; + int ret; + + _enter("%p,%p,%d", rx, saddr, len); + + ret = rxrpc_validate_address(rx, srx, len); + if (ret < 0) + goto error; + + lock_sock(&rx->sk); + + if (rx->sk.sk_state != RXRPC_UNCONNECTED) { + ret = -EINVAL; + goto error_unlock; + } + + memcpy(&rx->srx, srx, sizeof(rx->srx)); + + /* find a local transport endpoint if we don't have one already */ + local = rxrpc_lookup_local(&rx->srx); + if (IS_ERR(local)) { + ret = PTR_ERR(local); + goto error_unlock; + } + + rx->local = local; + if (srx->srx_service) { + service_id = htons(srx->srx_service); + write_lock_bh(&local->services_lock); + list_for_each_entry(prx, &local->services, listen_link) { + if (prx->service_id == service_id) + goto service_in_use; + } + + rx->service_id = service_id; + list_add_tail(&rx->listen_link, &local->services); + write_unlock_bh(&local->services_lock); + + rx->sk.sk_state = RXRPC_SERVER_BOUND; + } else { + rx->sk.sk_state = RXRPC_CLIENT_BOUND; + } + + release_sock(&rx->sk); + _leave(" = 0"); + return 0; + +service_in_use: + ret = -EADDRINUSE; + write_unlock_bh(&local->services_lock); +error_unlock: + release_sock(&rx->sk); +error: + _leave(" = %d", ret); + return ret; +} + +/* + * set the number of pending calls permitted on a listening socket + */ +static int rxrpc_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + struct rxrpc_sock *rx = rxrpc_sk(sk); + int ret; + + _enter("%p,%d", rx, backlog); + + lock_sock(&rx->sk); + + switch (rx->sk.sk_state) { + case RXRPC_UNCONNECTED: + ret = -EADDRNOTAVAIL; + break; + case RXRPC_CLIENT_BOUND: + case RXRPC_CLIENT_CONNECTED: + default: + ret = -EBUSY; + break; + case RXRPC_SERVER_BOUND: + ASSERT(rx->local != NULL); + sk->sk_max_ack_backlog = backlog; + rx->sk.sk_state = RXRPC_SERVER_LISTENING; + ret = 0; + break; + } + + release_sock(&rx->sk); + _leave(" = %d", ret); + return ret; +} + +/* + * find a transport by address + */ +static struct rxrpc_transport *rxrpc_name_to_transport(struct socket *sock, + struct sockaddr *addr, + int addr_len, int flags, + gfp_t gfp) +{ + struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr; + struct rxrpc_transport *trans; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + struct rxrpc_peer *peer; + + _enter("%p,%p,%d,%d", rx, addr, addr_len, flags); + + ASSERT(rx->local != NULL); + ASSERT(rx->sk.sk_state > RXRPC_UNCONNECTED); + + if (rx->srx.transport_type != srx->transport_type) + return ERR_PTR(-ESOCKTNOSUPPORT); + if (rx->srx.transport.family != srx->transport.family) + return ERR_PTR(-EAFNOSUPPORT); + + /* find a remote transport endpoint from the local one */ + peer = rxrpc_get_peer(srx, gfp); + if (IS_ERR(peer)) + return ERR_PTR(PTR_ERR(peer)); + + /* find a transport */ + trans = rxrpc_get_transport(rx->local, peer, gfp); + rxrpc_put_peer(peer); + _leave(" = %p", trans); + return trans; +} + +/** + * rxrpc_kernel_begin_call - Allow a kernel service to begin a call + * @sock: The socket on which to make the call + * @srx: The address of the peer to contact (defaults to socket setting) + * @key: The security context to use (defaults to socket setting) + * @user_call_ID: The ID to use + * + * Allow a kernel service to begin a call on the nominated socket. This just + * sets up all the internal tracking structures and allocates connection and + * call IDs as appropriate. The call to be used is returned. + * + * The default socket destination address and security may be overridden by + * supplying @srx and @key. + */ +struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, + struct sockaddr_rxrpc *srx, + struct key *key, + unsigned long user_call_ID, + gfp_t gfp) +{ + struct rxrpc_conn_bundle *bundle; + struct rxrpc_transport *trans; + struct rxrpc_call *call; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + __be16 service_id; + + _enter(",,%x,%lx", key_serial(key), user_call_ID); + + lock_sock(&rx->sk); + + if (srx) { + trans = rxrpc_name_to_transport(sock, (struct sockaddr *) srx, + sizeof(*srx), 0, gfp); + if (IS_ERR(trans)) { + call = ERR_PTR(PTR_ERR(trans)); + trans = NULL; + goto out; + } + } else { + trans = rx->trans; + if (!trans) { + call = ERR_PTR(-ENOTCONN); + goto out; + } + atomic_inc(&trans->usage); + } + + service_id = rx->service_id; + if (srx) + service_id = htons(srx->srx_service); + + if (!key) + key = rx->key; + if (key && !key->payload.data) + key = NULL; /* a no-security key */ + + bundle = rxrpc_get_bundle(rx, trans, key, service_id, gfp); + if (IS_ERR(bundle)) { + call = ERR_PTR(PTR_ERR(bundle)); + goto out; + } + + call = rxrpc_get_client_call(rx, trans, bundle, user_call_ID, true, + gfp); + rxrpc_put_bundle(trans, bundle); +out: + rxrpc_put_transport(trans); + release_sock(&rx->sk); + _leave(" = %p", call); + return call; +} + +EXPORT_SYMBOL(rxrpc_kernel_begin_call); + +/** + * rxrpc_kernel_end_call - Allow a kernel service to end a call it was using + * @call: The call to end + * + * Allow a kernel service to end a call it was using. The call must be + * complete before this is called (the call should be aborted if necessary). + */ +void rxrpc_kernel_end_call(struct rxrpc_call *call) +{ + _enter("%d{%d}", call->debug_id, atomic_read(&call->usage)); + rxrpc_remove_user_ID(call->socket, call); + rxrpc_put_call(call); +} + +EXPORT_SYMBOL(rxrpc_kernel_end_call); + +/** + * rxrpc_kernel_intercept_rx_messages - Intercept received RxRPC messages + * @sock: The socket to intercept received messages on + * @interceptor: The function to pass the messages to + * + * Allow a kernel service to intercept messages heading for the Rx queue on an + * RxRPC socket. They get passed to the specified function instead. + * @interceptor should free the socket buffers it is given. @interceptor is + * called with the socket receive queue spinlock held and softirqs disabled - + * this ensures that the messages will be delivered in the right order. + */ +void rxrpc_kernel_intercept_rx_messages(struct socket *sock, + rxrpc_interceptor_t interceptor) +{ + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + + _enter(""); + rx->interceptor = interceptor; +} + +EXPORT_SYMBOL(rxrpc_kernel_intercept_rx_messages); + +/* + * connect an RxRPC socket + * - this just targets it at a specific destination; no actual connection + * negotiation takes place + */ +static int rxrpc_connect(struct socket *sock, struct sockaddr *addr, + int addr_len, int flags) +{ + struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr; + struct sock *sk = sock->sk; + struct rxrpc_transport *trans; + struct rxrpc_local *local; + struct rxrpc_sock *rx = rxrpc_sk(sk); + int ret; + + _enter("%p,%p,%d,%d", rx, addr, addr_len, flags); + + ret = rxrpc_validate_address(rx, srx, addr_len); + if (ret < 0) { + _leave(" = %d [bad addr]", ret); + return ret; + } + + lock_sock(&rx->sk); + + switch (rx->sk.sk_state) { + case RXRPC_UNCONNECTED: + /* find a local transport endpoint if we don't have one already */ + ASSERTCMP(rx->local, ==, NULL); + rx->srx.srx_family = AF_RXRPC; + rx->srx.srx_service = 0; + rx->srx.transport_type = srx->transport_type; + rx->srx.transport_len = sizeof(sa_family_t); + rx->srx.transport.family = srx->transport.family; + local = rxrpc_lookup_local(&rx->srx); + if (IS_ERR(local)) { + release_sock(&rx->sk); + return PTR_ERR(local); + } + rx->local = local; + rx->sk.sk_state = RXRPC_CLIENT_BOUND; + case RXRPC_CLIENT_BOUND: + break; + case RXRPC_CLIENT_CONNECTED: + release_sock(&rx->sk); + return -EISCONN; + default: + release_sock(&rx->sk); + return -EBUSY; /* server sockets can't connect as well */ + } + + trans = rxrpc_name_to_transport(sock, addr, addr_len, flags, + GFP_KERNEL); + if (IS_ERR(trans)) { + release_sock(&rx->sk); + _leave(" = %ld", PTR_ERR(trans)); + return PTR_ERR(trans); + } + + rx->trans = trans; + rx->service_id = htons(srx->srx_service); + rx->sk.sk_state = RXRPC_CLIENT_CONNECTED; + + release_sock(&rx->sk); + return 0; +} + +/* + * send a message through an RxRPC socket + * - in a client this does a number of things: + * - finds/sets up a connection for the security specified (if any) + * - initiates a call (ID in control data) + * - ends the request phase of a call (if MSG_MORE is not set) + * - sends a call data packet + * - may send an abort (abort code in control data) + */ +static int rxrpc_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t len) +{ + struct rxrpc_transport *trans; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + int ret; + + _enter(",{%d},,%zu", rx->sk.sk_state, len); + + if (m->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + if (m->msg_name) { + ret = rxrpc_validate_address(rx, m->msg_name, m->msg_namelen); + if (ret < 0) { + _leave(" = %d [bad addr]", ret); + return ret; + } + } + + trans = NULL; + lock_sock(&rx->sk); + + if (m->msg_name) { + ret = -EISCONN; + trans = rxrpc_name_to_transport(sock, m->msg_name, + m->msg_namelen, 0, GFP_KERNEL); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + trans = NULL; + goto out; + } + } else { + trans = rx->trans; + if (trans) + atomic_inc(&trans->usage); + } + + switch (rx->sk.sk_state) { + case RXRPC_SERVER_LISTENING: + if (!m->msg_name) { + ret = rxrpc_server_sendmsg(iocb, rx, m, len); + break; + } + case RXRPC_SERVER_BOUND: + case RXRPC_CLIENT_BOUND: + if (!m->msg_name) { + ret = -ENOTCONN; + break; + } + case RXRPC_CLIENT_CONNECTED: + ret = rxrpc_client_sendmsg(iocb, rx, trans, m, len); + break; + default: + ret = -ENOTCONN; + break; + } + +out: + release_sock(&rx->sk); + if (trans) + rxrpc_put_transport(trans); + _leave(" = %d", ret); + return ret; +} + +/* + * set RxRPC socket options + */ +static int rxrpc_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int optlen) +{ + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + unsigned min_sec_level; + int ret; + + _enter(",%d,%d,,%d", level, optname, optlen); + + lock_sock(&rx->sk); + ret = -EOPNOTSUPP; + + if (level == SOL_RXRPC) { + switch (optname) { + case RXRPC_EXCLUSIVE_CONNECTION: + ret = -EINVAL; + if (optlen != 0) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + set_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags); + goto success; + + case RXRPC_SECURITY_KEY: + ret = -EINVAL; + if (rx->key) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + ret = rxrpc_request_key(rx, optval, optlen); + goto error; + + case RXRPC_SECURITY_KEYRING: + ret = -EINVAL; + if (rx->key) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + ret = rxrpc_server_keyring(rx, optval, optlen); + goto error; + + case RXRPC_MIN_SECURITY_LEVEL: + ret = -EINVAL; + if (optlen != sizeof(unsigned)) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + ret = get_user(min_sec_level, + (unsigned __user *) optval); + if (ret < 0) + goto error; + ret = -EINVAL; + if (min_sec_level > RXRPC_SECURITY_MAX) + goto error; + rx->min_sec_level = min_sec_level; + goto success; + + default: + break; + } + } + +success: + ret = 0; +error: + release_sock(&rx->sk); + return ret; +} + +/* + * permit an RxRPC socket to be polled + */ +static unsigned int rxrpc_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + unsigned int mask; + struct sock *sk = sock->sk; + + poll_wait(file, sk->sk_sleep, wait); + mask = 0; + + /* the socket is readable if there are any messages waiting on the Rx + * queue */ + if (!skb_queue_empty(&sk->sk_receive_queue)) + mask |= POLLIN | POLLRDNORM; + + /* the socket is writable if there is space to add new data to the + * socket; there is no guarantee that any particular call in progress + * on the socket may have space in the Tx ACK window */ + if (rxrpc_writable(sk)) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +/* + * create an RxRPC socket + */ +static int rxrpc_create(struct socket *sock, int protocol) +{ + struct rxrpc_sock *rx; + struct sock *sk; + + _enter("%p,%d", sock, protocol); + + /* we support transport protocol UDP only */ + if (protocol != PF_INET) + return -EPROTONOSUPPORT; + + if (sock->type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + sock->ops = &rxrpc_rpc_ops; + sock->state = SS_UNCONNECTED; + + sk = sk_alloc(PF_RXRPC, GFP_KERNEL, &rxrpc_proto, 1); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sk->sk_state = RXRPC_UNCONNECTED; + sk->sk_write_space = rxrpc_write_space; + sk->sk_max_ack_backlog = sysctl_rxrpc_max_qlen; + sk->sk_destruct = rxrpc_sock_destructor; + + rx = rxrpc_sk(sk); + rx->proto = protocol; + rx->calls = RB_ROOT; + + INIT_LIST_HEAD(&rx->listen_link); + INIT_LIST_HEAD(&rx->secureq); + INIT_LIST_HEAD(&rx->acceptq); + rwlock_init(&rx->call_lock); + memset(&rx->srx, 0, sizeof(rx->srx)); + + _leave(" = 0 [%p]", rx); + return 0; +} + +/* + * RxRPC socket destructor + */ +static void rxrpc_sock_destructor(struct sock *sk) +{ + _enter("%p", sk); + + rxrpc_purge_queue(&sk->sk_receive_queue); + + BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc)); + BUG_TRAP(sk_unhashed(sk)); + BUG_TRAP(!sk->sk_socket); + + if (!sock_flag(sk, SOCK_DEAD)) { + printk("Attempt to release alive rxrpc socket: %p\n", sk); + return; + } +} + +/* + * release an RxRPC socket + */ +static int rxrpc_release_sock(struct sock *sk) +{ + struct rxrpc_sock *rx = rxrpc_sk(sk); + + _enter("%p{%d,%d}", sk, sk->sk_state, atomic_read(&sk->sk_refcnt)); + + /* declare the socket closed for business */ + sock_orphan(sk); + sk->sk_shutdown = SHUTDOWN_MASK; + + spin_lock_bh(&sk->sk_receive_queue.lock); + sk->sk_state = RXRPC_CLOSE; + spin_unlock_bh(&sk->sk_receive_queue.lock); + + ASSERTCMP(rx->listen_link.next, !=, LIST_POISON1); + + if (!list_empty(&rx->listen_link)) { + write_lock_bh(&rx->local->services_lock); + list_del(&rx->listen_link); + write_unlock_bh(&rx->local->services_lock); + } + + /* try to flush out this socket */ + rxrpc_release_calls_on_socket(rx); + flush_workqueue(rxrpc_workqueue); + rxrpc_purge_queue(&sk->sk_receive_queue); + + if (rx->conn) { + rxrpc_put_connection(rx->conn); + rx->conn = NULL; + } + + if (rx->bundle) { + rxrpc_put_bundle(rx->trans, rx->bundle); + rx->bundle = NULL; + } + if (rx->trans) { + rxrpc_put_transport(rx->trans); + rx->trans = NULL; + } + if (rx->local) { + rxrpc_put_local(rx->local); + rx->local = NULL; + } + + key_put(rx->key); + rx->key = NULL; + key_put(rx->securities); + rx->securities = NULL; + sock_put(sk); + + _leave(" = 0"); + return 0; +} + +/* + * release an RxRPC BSD socket on close() or equivalent + */ +static int rxrpc_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + _enter("%p{%p}", sock, sk); + + if (!sk) + return 0; + + sock->sk = NULL; + + return rxrpc_release_sock(sk); +} + +/* + * RxRPC network protocol + */ +static const struct proto_ops rxrpc_rpc_ops = { + .family = PF_UNIX, + .owner = THIS_MODULE, + .release = rxrpc_release, + .bind = rxrpc_bind, + .connect = rxrpc_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = rxrpc_poll, + .ioctl = sock_no_ioctl, + .listen = rxrpc_listen, + .shutdown = sock_no_shutdown, + .setsockopt = rxrpc_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = rxrpc_sendmsg, + .recvmsg = rxrpc_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static struct proto rxrpc_proto = { + .name = "RXRPC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct rxrpc_sock), + .max_header = sizeof(struct rxrpc_header), +}; + +static struct net_proto_family rxrpc_family_ops = { + .family = PF_RXRPC, + .create = rxrpc_create, + .owner = THIS_MODULE, +}; + +/* + * initialise and register the RxRPC protocol + */ +static int __init af_rxrpc_init(void) +{ + struct sk_buff *dummy_skb; + int ret = -1; + + BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > sizeof(dummy_skb->cb)); + + rxrpc_epoch = htonl(xtime.tv_sec); + + ret = -ENOMEM; + rxrpc_call_jar = kmem_cache_create( + "rxrpc_call_jar", sizeof(struct rxrpc_call), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!rxrpc_call_jar) { + printk(KERN_NOTICE "RxRPC: Failed to allocate call jar\n"); + goto error_call_jar; + } + + rxrpc_workqueue = create_workqueue("krxrpcd"); + if (!rxrpc_workqueue) { + printk(KERN_NOTICE "RxRPC: Failed to allocate work queue\n"); + goto error_work_queue; + } + + ret = proto_register(&rxrpc_proto, 1); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register protocol\n"); + goto error_proto; + } + + ret = sock_register(&rxrpc_family_ops); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register socket family\n"); + goto error_sock; + } + + ret = register_key_type(&key_type_rxrpc); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register client key type\n"); + goto error_key_type; + } + + ret = register_key_type(&key_type_rxrpc_s); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register server key type\n"); + goto error_key_type_s; + } + +#ifdef CONFIG_PROC_FS + proc_net_fops_create("rxrpc_calls", 0, &rxrpc_call_seq_fops); + proc_net_fops_create("rxrpc_conns", 0, &rxrpc_connection_seq_fops); +#endif + return 0; + +error_key_type_s: + unregister_key_type(&key_type_rxrpc); +error_key_type: + sock_unregister(PF_RXRPC); +error_sock: + proto_unregister(&rxrpc_proto); +error_proto: + destroy_workqueue(rxrpc_workqueue); +error_work_queue: + kmem_cache_destroy(rxrpc_call_jar); +error_call_jar: + return ret; +} + +/* + * unregister the RxRPC protocol + */ +static void __exit af_rxrpc_exit(void) +{ + _enter(""); + unregister_key_type(&key_type_rxrpc_s); + unregister_key_type(&key_type_rxrpc); + sock_unregister(PF_RXRPC); + proto_unregister(&rxrpc_proto); + rxrpc_destroy_all_calls(); + rxrpc_destroy_all_connections(); + rxrpc_destroy_all_transports(); + rxrpc_destroy_all_peers(); + rxrpc_destroy_all_locals(); + + ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); + + _debug("flush scheduled work"); + flush_workqueue(rxrpc_workqueue); + proc_net_remove("rxrpc_conns"); + proc_net_remove("rxrpc_calls"); + destroy_workqueue(rxrpc_workqueue); + kmem_cache_destroy(rxrpc_call_jar); + _leave(""); +} + +module_init(af_rxrpc_init); +module_exit(af_rxrpc_exit); diff --git a/net/rxrpc/ar-accept.c b/net/rxrpc/ar-accept.c new file mode 100644 index 0000000..92a87fd --- /dev/null +++ b/net/rxrpc/ar-accept.c @@ -0,0 +1,504 @@ +/* incoming call handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * generate a connection-level abort + */ +static int rxrpc_busy(struct rxrpc_local *local, struct sockaddr_rxrpc *srx, + struct rxrpc_header *hdr) +{ + struct msghdr msg; + struct kvec iov[1]; + size_t len; + int ret; + + _enter("%d,,", local->debug_id); + + msg.msg_name = &srx->transport.sin; + msg.msg_namelen = sizeof(srx->transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr->seq = 0; + hdr->type = RXRPC_PACKET_TYPE_BUSY; + hdr->flags = 0; + hdr->userStatus = 0; + hdr->_rsvd = 0; + + iov[0].iov_base = hdr; + iov[0].iov_len = sizeof(*hdr); + + len = iov[0].iov_len; + + hdr->serial = htonl(1); + _proto("Tx BUSY %%%u", ntohl(hdr->serial)); + + ret = kernel_sendmsg(local->socket, &msg, iov, 1, len); + if (ret < 0) { + _leave(" = -EAGAIN [sendmsg failed: %d]", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * accept an incoming call that needs peer, transport and/or connection setting + * up + */ +static int rxrpc_accept_incoming_call(struct rxrpc_local *local, + struct rxrpc_sock *rx, + struct sk_buff *skb, + struct sockaddr_rxrpc *srx) +{ + struct rxrpc_connection *conn; + struct rxrpc_transport *trans; + struct rxrpc_skb_priv *sp, *nsp; + struct rxrpc_peer *peer; + struct rxrpc_call *call; + struct sk_buff *notification; + int ret; + + _enter(""); + + sp = rxrpc_skb(skb); + + /* get a notification message to send to the server app */ + notification = alloc_skb(0, GFP_NOFS); + rxrpc_new_skb(notification); + notification->mark = RXRPC_SKB_MARK_NEW_CALL; + + peer = rxrpc_get_peer(srx, GFP_NOIO); + if (IS_ERR(peer)) { + _debug("no peer"); + ret = -EBUSY; + goto error; + } + + trans = rxrpc_get_transport(local, peer, GFP_NOIO); + rxrpc_put_peer(peer); + if (!trans) { + _debug("no trans"); + ret = -EBUSY; + goto error; + } + + conn = rxrpc_incoming_connection(trans, &sp->hdr, GFP_NOIO); + rxrpc_put_transport(trans); + if (IS_ERR(conn)) { + _debug("no conn"); + ret = PTR_ERR(conn); + goto error; + } + + call = rxrpc_incoming_call(rx, conn, &sp->hdr, GFP_NOIO); + rxrpc_put_connection(conn); + if (IS_ERR(call)) { + _debug("no call"); + ret = PTR_ERR(call); + goto error; + } + + /* attach the call to the socket */ + read_lock_bh(&local->services_lock); + if (rx->sk.sk_state == RXRPC_CLOSE) + goto invalid_service; + + write_lock(&rx->call_lock); + if (!test_and_set_bit(RXRPC_CALL_INIT_ACCEPT, &call->flags)) { + rxrpc_get_call(call); + + spin_lock(&call->conn->state_lock); + if (sp->hdr.securityIndex > 0 && + call->conn->state == RXRPC_CONN_SERVER_UNSECURED) { + _debug("await conn sec"); + list_add_tail(&call->accept_link, &rx->secureq); + call->conn->state = RXRPC_CONN_SERVER_CHALLENGING; + atomic_inc(&call->conn->usage); + set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events); + rxrpc_queue_conn(call->conn); + } else { + _debug("conn ready"); + call->state = RXRPC_CALL_SERVER_ACCEPTING; + list_add_tail(&call->accept_link, &rx->acceptq); + rxrpc_get_call(call); + nsp = rxrpc_skb(notification); + nsp->call = call; + + ASSERTCMP(atomic_read(&call->usage), >=, 3); + + _debug("notify"); + spin_lock(&call->lock); + ret = rxrpc_queue_rcv_skb(call, notification, true, + false); + spin_unlock(&call->lock); + notification = NULL; + if (ret < 0) + BUG(); + } + spin_unlock(&call->conn->state_lock); + + _debug("queued"); + } + write_unlock(&rx->call_lock); + + _debug("process"); + rxrpc_fast_process_packet(call, skb); + + _debug("done"); + read_unlock_bh(&local->services_lock); + rxrpc_free_skb(notification); + rxrpc_put_call(call); + _leave(" = 0"); + return 0; + +invalid_service: + _debug("invalid"); + read_unlock_bh(&local->services_lock); + + read_lock_bh(&call->state_lock); + if (!test_bit(RXRPC_CALL_RELEASE, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) { + rxrpc_get_call(call); + rxrpc_queue_call(call); + } + read_unlock_bh(&call->state_lock); + rxrpc_put_call(call); + ret = -ECONNREFUSED; +error: + rxrpc_free_skb(notification); + _leave(" = %d", ret); + return ret; +} + +/* + * accept incoming calls that need peer, transport and/or connection setting up + * - the packets we get are all incoming client DATA packets that have seq == 1 + */ +void rxrpc_accept_incoming_calls(struct work_struct *work) +{ + struct rxrpc_local *local = + container_of(work, struct rxrpc_local, acceptor); + struct rxrpc_skb_priv *sp; + struct sockaddr_rxrpc srx; + struct rxrpc_sock *rx; + struct sk_buff *skb; + __be16 service_id; + int ret; + + _enter("%d", local->debug_id); + + read_lock_bh(&rxrpc_local_lock); + if (atomic_read(&local->usage) > 0) + rxrpc_get_local(local); + else + local = NULL; + read_unlock_bh(&rxrpc_local_lock); + if (!local) { + _leave(" [local dead]"); + return; + } + +process_next_packet: + skb = skb_dequeue(&local->accept_queue); + if (!skb) { + rxrpc_put_local(local); + _leave("\n"); + return; + } + + _net("incoming call skb %p", skb); + + sp = rxrpc_skb(skb); + + /* determine the remote address */ + memset(&srx, 0, sizeof(srx)); + srx.srx_family = AF_RXRPC; + srx.transport.family = local->srx.transport.family; + srx.transport_type = local->srx.transport_type; + switch (srx.transport.family) { + case AF_INET: + srx.transport_len = sizeof(struct sockaddr_in); + srx.transport.sin.sin_port = udp_hdr(skb)->source; + srx.transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + break; + default: + goto busy; + } + + /* get the socket providing the service */ + service_id = sp->hdr.serviceId; + read_lock_bh(&local->services_lock); + list_for_each_entry(rx, &local->services, listen_link) { + if (rx->service_id == service_id && + rx->sk.sk_state != RXRPC_CLOSE) + goto found_service; + } + read_unlock_bh(&local->services_lock); + goto invalid_service; + +found_service: + _debug("found service %hd", ntohs(rx->service_id)); + if (sk_acceptq_is_full(&rx->sk)) + goto backlog_full; + sk_acceptq_added(&rx->sk); + sock_hold(&rx->sk); + read_unlock_bh(&local->services_lock); + + ret = rxrpc_accept_incoming_call(local, rx, skb, &srx); + if (ret < 0) + sk_acceptq_removed(&rx->sk); + sock_put(&rx->sk); + switch (ret) { + case -ECONNRESET: /* old calls are ignored */ + case -ECONNABORTED: /* aborted calls are reaborted or ignored */ + case 0: + goto process_next_packet; + case -ECONNREFUSED: + goto invalid_service; + case -EBUSY: + goto busy; + case -EKEYREJECTED: + goto security_mismatch; + default: + BUG(); + } + +backlog_full: + read_unlock_bh(&local->services_lock); +busy: + rxrpc_busy(local, &srx, &sp->hdr); + rxrpc_free_skb(skb); + goto process_next_packet; + +invalid_service: + skb->priority = RX_INVALID_OPERATION; + rxrpc_reject_packet(local, skb); + goto process_next_packet; + + /* can't change connection security type mid-flow */ +security_mismatch: + skb->priority = RX_PROTOCOL_ERROR; + rxrpc_reject_packet(local, skb); + goto process_next_packet; +} + +/* + * handle acceptance of a call by userspace + * - assign the user call ID to the call at the front of the queue + */ +struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *rx, + unsigned long user_call_ID) +{ + struct rxrpc_call *call; + struct rb_node *parent, **pp; + int ret; + + _enter(",%lx", user_call_ID); + + ASSERT(!irqs_disabled()); + + write_lock(&rx->call_lock); + + ret = -ENODATA; + if (list_empty(&rx->acceptq)) + goto out; + + /* check the user ID isn't already in use */ + ret = -EBADSLT; + pp = &rx->calls.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + call = rb_entry(parent, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + pp = &(*pp)->rb_left; + else if (user_call_ID > call->user_call_ID) + pp = &(*pp)->rb_right; + else + goto out; + } + + /* dequeue the first call and check it's still valid */ + call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link); + list_del_init(&call->accept_link); + sk_acceptq_removed(&rx->sk); + + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_SERVER_ACCEPTING: + call->state = RXRPC_CALL_SERVER_RECV_REQUEST; + break; + case RXRPC_CALL_REMOTELY_ABORTED: + case RXRPC_CALL_LOCALLY_ABORTED: + ret = -ECONNABORTED; + goto out_release; + case RXRPC_CALL_NETWORK_ERROR: + ret = call->conn->error; + goto out_release; + case RXRPC_CALL_DEAD: + ret = -ETIME; + goto out_discard; + default: + BUG(); + } + + /* formalise the acceptance */ + call->user_call_ID = user_call_ID; + rb_link_node(&call->sock_node, parent, pp); + rb_insert_color(&call->sock_node, &rx->calls); + if (test_and_set_bit(RXRPC_CALL_HAS_USERID, &call->flags)) + BUG(); + if (test_and_set_bit(RXRPC_CALL_ACCEPTED, &call->events)) + BUG(); + rxrpc_queue_call(call); + + rxrpc_get_call(call); + write_unlock_bh(&call->state_lock); + write_unlock(&rx->call_lock); + _leave(" = %p{%d}", call, call->debug_id); + return call; + + /* if the call is already dying or dead, then we leave the socket's ref + * on it to be released by rxrpc_dead_call_expired() as induced by + * rxrpc_release_call() */ +out_release: + _debug("release %p", call); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + rxrpc_queue_call(call); +out_discard: + write_unlock_bh(&call->state_lock); + _debug("discard %p", call); +out: + write_unlock(&rx->call_lock); + _leave(" = %d", ret); + return ERR_PTR(ret); +} + +/* + * handle rejectance of a call by userspace + * - reject the call at the front of the queue + */ +int rxrpc_reject_call(struct rxrpc_sock *rx) +{ + struct rxrpc_call *call; + int ret; + + _enter(""); + + ASSERT(!irqs_disabled()); + + write_lock(&rx->call_lock); + + ret = -ENODATA; + if (list_empty(&rx->acceptq)) + goto out; + + /* dequeue the first call and check it's still valid */ + call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link); + list_del_init(&call->accept_link); + sk_acceptq_removed(&rx->sk); + + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_SERVER_ACCEPTING: + call->state = RXRPC_CALL_SERVER_BUSY; + if (test_and_set_bit(RXRPC_CALL_REJECT_BUSY, &call->events)) + rxrpc_queue_call(call); + ret = 0; + goto out_release; + case RXRPC_CALL_REMOTELY_ABORTED: + case RXRPC_CALL_LOCALLY_ABORTED: + ret = -ECONNABORTED; + goto out_release; + case RXRPC_CALL_NETWORK_ERROR: + ret = call->conn->error; + goto out_release; + case RXRPC_CALL_DEAD: + ret = -ETIME; + goto out_discard; + default: + BUG(); + } + + /* if the call is already dying or dead, then we leave the socket's ref + * on it to be released by rxrpc_dead_call_expired() as induced by + * rxrpc_release_call() */ +out_release: + _debug("release %p", call); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + rxrpc_queue_call(call); +out_discard: + write_unlock_bh(&call->state_lock); + _debug("discard %p", call); +out: + write_unlock(&rx->call_lock); + _leave(" = %d", ret); + return ret; +} + +/** + * rxrpc_kernel_accept_call - Allow a kernel service to accept an incoming call + * @sock: The socket on which the impending call is waiting + * @user_call_ID: The tag to attach to the call + * + * Allow a kernel service to accept an incoming call, assuming the incoming + * call is still valid. + */ +struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *sock, + unsigned long user_call_ID) +{ + struct rxrpc_call *call; + + _enter(",%lx", user_call_ID); + call = rxrpc_accept_call(rxrpc_sk(sock->sk), user_call_ID); + _leave(" = %p", call); + return call; +} + +EXPORT_SYMBOL(rxrpc_kernel_accept_call); + +/** + * rxrpc_kernel_reject_call - Allow a kernel service to reject an incoming call + * @sock: The socket on which the impending call is waiting + * + * Allow a kernel service to reject an incoming call with a BUSY message, + * assuming the incoming call is still valid. + */ +int rxrpc_kernel_reject_call(struct socket *sock) +{ + int ret; + + _enter(""); + ret = rxrpc_reject_call(rxrpc_sk(sock->sk)); + _leave(" = %d", ret); + return ret; +} + +EXPORT_SYMBOL(rxrpc_kernel_reject_call); diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c new file mode 100644 index 0000000..fc07a92 --- /dev/null +++ b/net/rxrpc/ar-ack.c @@ -0,0 +1,1250 @@ +/* Management of Tx window, Tx resend, ACKs and out-of-sequence reception + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static unsigned rxrpc_ack_defer = 1; + +static const char *rxrpc_acks[] = { + "---", "REQ", "DUP", "OOS", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL", + "-?-" +}; + +static const s8 rxrpc_ack_priority[] = { + [0] = 0, + [RXRPC_ACK_DELAY] = 1, + [RXRPC_ACK_REQUESTED] = 2, + [RXRPC_ACK_IDLE] = 3, + [RXRPC_ACK_PING_RESPONSE] = 4, + [RXRPC_ACK_DUPLICATE] = 5, + [RXRPC_ACK_OUT_OF_SEQUENCE] = 6, + [RXRPC_ACK_EXCEEDS_WINDOW] = 7, + [RXRPC_ACK_NOSPACE] = 8, +}; + +/* + * propose an ACK be sent + */ +void __rxrpc_propose_ACK(struct rxrpc_call *call, uint8_t ack_reason, + __be32 serial, bool immediate) +{ + unsigned long expiry; + s8 prior = rxrpc_ack_priority[ack_reason]; + + ASSERTCMP(prior, >, 0); + + _enter("{%d},%s,%%%x,%u", + call->debug_id, rxrpc_acks[ack_reason], ntohl(serial), + immediate); + + if (prior < rxrpc_ack_priority[call->ackr_reason]) { + if (immediate) + goto cancel_timer; + return; + } + + /* update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial + * numbers */ + if (prior == rxrpc_ack_priority[call->ackr_reason]) { + if (prior <= 4) + call->ackr_serial = serial; + if (immediate) + goto cancel_timer; + return; + } + + call->ackr_reason = ack_reason; + call->ackr_serial = serial; + + switch (ack_reason) { + case RXRPC_ACK_DELAY: + _debug("run delay timer"); + call->ack_timer.expires = jiffies + rxrpc_ack_timeout * HZ; + add_timer(&call->ack_timer); + return; + + case RXRPC_ACK_IDLE: + if (!immediate) { + _debug("run defer timer"); + expiry = 1; + goto run_timer; + } + goto cancel_timer; + + case RXRPC_ACK_REQUESTED: + if (!rxrpc_ack_defer) + goto cancel_timer; + if (!immediate || serial == cpu_to_be32(1)) { + _debug("run defer timer"); + expiry = rxrpc_ack_defer; + goto run_timer; + } + + default: + _debug("immediate ACK"); + goto cancel_timer; + } + +run_timer: + expiry += jiffies; + if (!timer_pending(&call->ack_timer) || + time_after(call->ack_timer.expires, expiry)) + mod_timer(&call->ack_timer, expiry); + return; + +cancel_timer: + _debug("cancel timer %%%u", ntohl(serial)); + try_to_del_timer_sync(&call->ack_timer); + read_lock_bh(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_ACK, &call->events)) + rxrpc_queue_call(call); + read_unlock_bh(&call->state_lock); +} + +/* + * propose an ACK be sent, locking the call structure + */ +void rxrpc_propose_ACK(struct rxrpc_call *call, uint8_t ack_reason, + __be32 serial, bool immediate) +{ + s8 prior = rxrpc_ack_priority[ack_reason]; + + if (prior > rxrpc_ack_priority[call->ackr_reason]) { + spin_lock_bh(&call->lock); + __rxrpc_propose_ACK(call, ack_reason, serial, immediate); + spin_unlock_bh(&call->lock); + } +} + +/* + * set the resend timer + */ +static void rxrpc_set_resend(struct rxrpc_call *call, u8 resend, + unsigned long resend_at) +{ + read_lock_bh(&call->state_lock); + if (call->state >= RXRPC_CALL_COMPLETE) + resend = 0; + + if (resend & 1) { + _debug("SET RESEND"); + set_bit(RXRPC_CALL_RESEND, &call->events); + } + + if (resend & 2) { + _debug("MODIFY RESEND TIMER"); + set_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + mod_timer(&call->resend_timer, resend_at); + } else { + _debug("KILL RESEND TIMER"); + del_timer_sync(&call->resend_timer); + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + } + read_unlock_bh(&call->state_lock); +} + +/* + * resend packets + */ +static void rxrpc_resend(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct rxrpc_header *hdr; + struct sk_buff *txb; + unsigned long *p_txb, resend_at; + int loop, stop; + u8 resend; + + _enter("{%d,%d,%d,%d},", + call->acks_hard, call->acks_unacked, + atomic_read(&call->sequence), + CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz)); + + stop = 0; + resend = 0; + resend_at = 0; + + for (loop = call->acks_tail; + loop != call->acks_head || stop; + loop = (loop + 1) & (call->acks_winsz - 1) + ) { + p_txb = call->acks_window + loop; + smp_read_barrier_depends(); + if (*p_txb & 1) + continue; + + txb = (struct sk_buff *) *p_txb; + sp = rxrpc_skb(txb); + + if (sp->need_resend) { + sp->need_resend = 0; + + /* each Tx packet has a new serial number */ + sp->hdr.serial = + htonl(atomic_inc_return(&call->conn->serial)); + + hdr = (struct rxrpc_header *) txb->head; + hdr->serial = sp->hdr.serial; + + _proto("Tx DATA %%%u { #%d }", + ntohl(sp->hdr.serial), ntohl(sp->hdr.seq)); + if (rxrpc_send_packet(call->conn->trans, txb) < 0) { + stop = 0; + sp->resend_at = jiffies + 3; + } else { + sp->resend_at = + jiffies + rxrpc_resend_timeout * HZ; + } + } + + if (time_after_eq(jiffies + 1, sp->resend_at)) { + sp->need_resend = 1; + resend |= 1; + } else if (resend & 2) { + if (time_before(sp->resend_at, resend_at)) + resend_at = sp->resend_at; + } else { + resend_at = sp->resend_at; + resend |= 2; + } + } + + rxrpc_set_resend(call, resend, resend_at); + _leave(""); +} + +/* + * handle resend timer expiry + */ +static void rxrpc_resend_timer(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *txb; + unsigned long *p_txb, resend_at; + int loop; + u8 resend; + + _enter("%d,%d,%d", + call->acks_tail, call->acks_unacked, call->acks_head); + + resend = 0; + resend_at = 0; + + for (loop = call->acks_unacked; + loop != call->acks_head; + loop = (loop + 1) & (call->acks_winsz - 1) + ) { + p_txb = call->acks_window + loop; + smp_read_barrier_depends(); + txb = (struct sk_buff *) (*p_txb & ~1); + sp = rxrpc_skb(txb); + + ASSERT(!(*p_txb & 1)); + + if (sp->need_resend) { + ; + } else if (time_after_eq(jiffies + 1, sp->resend_at)) { + sp->need_resend = 1; + resend |= 1; + } else if (resend & 2) { + if (time_before(sp->resend_at, resend_at)) + resend_at = sp->resend_at; + } else { + resend_at = sp->resend_at; + resend |= 2; + } + } + + rxrpc_set_resend(call, resend, resend_at); + _leave(""); +} + +/* + * process soft ACKs of our transmitted packets + * - these indicate packets the peer has or has not received, but hasn't yet + * given to the consumer, and so can still be discarded and re-requested + */ +static int rxrpc_process_soft_ACKs(struct rxrpc_call *call, + struct rxrpc_ackpacket *ack, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *txb; + unsigned long *p_txb, resend_at; + int loop; + u8 sacks[RXRPC_MAXACKS], resend; + + _enter("{%d,%d},{%d},", + call->acks_hard, + CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz), + ack->nAcks); + + if (skb_copy_bits(skb, 0, sacks, ack->nAcks) < 0) + goto protocol_error; + + resend = 0; + resend_at = 0; + for (loop = 0; loop < ack->nAcks; loop++) { + p_txb = call->acks_window; + p_txb += (call->acks_tail + loop) & (call->acks_winsz - 1); + smp_read_barrier_depends(); + txb = (struct sk_buff *) (*p_txb & ~1); + sp = rxrpc_skb(txb); + + switch (sacks[loop]) { + case RXRPC_ACK_TYPE_ACK: + sp->need_resend = 0; + *p_txb |= 1; + break; + case RXRPC_ACK_TYPE_NACK: + sp->need_resend = 1; + *p_txb &= ~1; + resend = 1; + break; + default: + _debug("Unsupported ACK type %d", sacks[loop]); + goto protocol_error; + } + } + + smp_mb(); + call->acks_unacked = (call->acks_tail + loop) & (call->acks_winsz - 1); + + /* anything not explicitly ACK'd is implicitly NACK'd, but may just not + * have been received or processed yet by the far end */ + for (loop = call->acks_unacked; + loop != call->acks_head; + loop = (loop + 1) & (call->acks_winsz - 1) + ) { + p_txb = call->acks_window + loop; + smp_read_barrier_depends(); + txb = (struct sk_buff *) (*p_txb & ~1); + sp = rxrpc_skb(txb); + + if (*p_txb & 1) { + /* packet must have been discarded */ + sp->need_resend = 1; + *p_txb &= ~1; + resend |= 1; + } else if (sp->need_resend) { + ; + } else if (time_after_eq(jiffies + 1, sp->resend_at)) { + sp->need_resend = 1; + resend |= 1; + } else if (resend & 2) { + if (time_before(sp->resend_at, resend_at)) + resend_at = sp->resend_at; + } else { + resend_at = sp->resend_at; + resend |= 2; + } + } + + rxrpc_set_resend(call, resend, resend_at); + _leave(" = 0"); + return 0; + +protocol_error: + _leave(" = -EPROTO"); + return -EPROTO; +} + +/* + * discard hard-ACK'd packets from the Tx window + */ +static void rxrpc_rotate_tx_window(struct rxrpc_call *call, u32 hard) +{ + struct rxrpc_skb_priv *sp; + unsigned long _skb; + int tail = call->acks_tail, old_tail; + int win = CIRC_CNT(call->acks_head, tail, call->acks_winsz); + + _enter("{%u,%u},%u", call->acks_hard, win, hard); + + ASSERTCMP(hard - call->acks_hard, <=, win); + + while (call->acks_hard < hard) { + smp_read_barrier_depends(); + _skb = call->acks_window[tail] & ~1; + sp = rxrpc_skb((struct sk_buff *) _skb); + rxrpc_free_skb((struct sk_buff *) _skb); + old_tail = tail; + tail = (tail + 1) & (call->acks_winsz - 1); + call->acks_tail = tail; + if (call->acks_unacked == old_tail) + call->acks_unacked = tail; + call->acks_hard++; + } + + wake_up(&call->tx_waitq); +} + +/* + * clear the Tx window in the event of a failure + */ +static void rxrpc_clear_tx_window(struct rxrpc_call *call) +{ + rxrpc_rotate_tx_window(call, atomic_read(&call->sequence)); +} + +/* + * drain the out of sequence received packet queue into the packet Rx queue + */ +static int rxrpc_drain_rx_oos_queue(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + bool terminal; + int ret; + + _enter("{%d,%d}", call->rx_data_post, call->rx_first_oos); + + spin_lock_bh(&call->lock); + + ret = -ECONNRESET; + if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) + goto socket_unavailable; + + skb = skb_dequeue(&call->rx_oos_queue); + if (skb) { + sp = rxrpc_skb(skb); + + _debug("drain OOS packet %d [%d]", + ntohl(sp->hdr.seq), call->rx_first_oos); + + if (ntohl(sp->hdr.seq) != call->rx_first_oos) { + skb_queue_head(&call->rx_oos_queue, skb); + call->rx_first_oos = ntohl(rxrpc_skb(skb)->hdr.seq); + _debug("requeue %p {%u}", skb, call->rx_first_oos); + } else { + skb->mark = RXRPC_SKB_MARK_DATA; + terminal = ((sp->hdr.flags & RXRPC_LAST_PACKET) && + !(sp->hdr.flags & RXRPC_CLIENT_INITIATED)); + ret = rxrpc_queue_rcv_skb(call, skb, true, terminal); + BUG_ON(ret < 0); + _debug("drain #%u", call->rx_data_post); + call->rx_data_post++; + + /* find out what the next packet is */ + skb = skb_peek(&call->rx_oos_queue); + if (skb) + call->rx_first_oos = + ntohl(rxrpc_skb(skb)->hdr.seq); + else + call->rx_first_oos = 0; + _debug("peek %p {%u}", skb, call->rx_first_oos); + } + } + + ret = 0; +socket_unavailable: + spin_unlock_bh(&call->lock); + _leave(" = %d", ret); + return ret; +} + +/* + * insert an out of sequence packet into the buffer + */ +static void rxrpc_insert_oos_packet(struct rxrpc_call *call, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp, *psp; + struct sk_buff *p; + u32 seq; + + sp = rxrpc_skb(skb); + seq = ntohl(sp->hdr.seq); + _enter(",,{%u}", seq); + + skb->destructor = rxrpc_packet_destructor; + ASSERTCMP(sp->call, ==, NULL); + sp->call = call; + rxrpc_get_call(call); + + /* insert into the buffer in sequence order */ + spin_lock_bh(&call->lock); + + skb_queue_walk(&call->rx_oos_queue, p) { + psp = rxrpc_skb(p); + if (ntohl(psp->hdr.seq) > seq) { + _debug("insert oos #%u before #%u", + seq, ntohl(psp->hdr.seq)); + skb_insert(p, skb, &call->rx_oos_queue); + goto inserted; + } + } + + _debug("append oos #%u", seq); + skb_queue_tail(&call->rx_oos_queue, skb); +inserted: + + /* we might now have a new front to the queue */ + if (call->rx_first_oos == 0 || seq < call->rx_first_oos) + call->rx_first_oos = seq; + + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + call->rx_data_post == call->rx_first_oos) { + _debug("drain rx oos now"); + set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events); + } + read_unlock(&call->state_lock); + + spin_unlock_bh(&call->lock); + _leave(" [stored #%u]", call->rx_first_oos); +} + +/* + * clear the Tx window on final ACK reception + */ +static void rxrpc_zap_tx_window(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + unsigned long _skb, *acks_window; + uint8_t winsz = call->acks_winsz; + int tail; + + acks_window = call->acks_window; + call->acks_window = NULL; + + while (CIRC_CNT(call->acks_head, call->acks_tail, winsz) > 0) { + tail = call->acks_tail; + smp_read_barrier_depends(); + _skb = acks_window[tail] & ~1; + smp_mb(); + call->acks_tail = (call->acks_tail + 1) & (winsz - 1); + + skb = (struct sk_buff *) _skb; + sp = rxrpc_skb(skb); + _debug("+++ clear Tx %u", ntohl(sp->hdr.seq)); + rxrpc_free_skb(skb); + } + + kfree(acks_window); +} + +/* + * process packets in the reception queue + */ +static int rxrpc_process_rx_queue(struct rxrpc_call *call, + u32 *_abort_code) +{ + struct rxrpc_ackpacket ack; + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + bool post_ACK; + int latest; + u32 hard, tx; + + _enter(""); + +process_further: + skb = skb_dequeue(&call->rx_queue); + if (!skb) + return -EAGAIN; + + _net("deferred skb %p", skb); + + sp = rxrpc_skb(skb); + + _debug("process %s [st %d]", rxrpc_pkts[sp->hdr.type], call->state); + + post_ACK = false; + + switch (sp->hdr.type) { + /* data packets that wind up here have been received out of + * order, need security processing or are jumbo packets */ + case RXRPC_PACKET_TYPE_DATA: + _proto("OOSQ DATA %%%u { #%u }", + ntohl(sp->hdr.serial), ntohl(sp->hdr.seq)); + + /* secured packets must be verified and possibly decrypted */ + if (rxrpc_verify_packet(call, skb, _abort_code) < 0) + goto protocol_error; + + rxrpc_insert_oos_packet(call, skb); + goto process_further; + + /* partial ACK to process */ + case RXRPC_PACKET_TYPE_ACK: + if (skb_copy_bits(skb, 0, &ack, sizeof(ack)) < 0) { + _debug("extraction failure"); + goto protocol_error; + } + if (!skb_pull(skb, sizeof(ack))) + BUG(); + + latest = ntohl(sp->hdr.serial); + hard = ntohl(ack.firstPacket); + tx = atomic_read(&call->sequence); + + _proto("Rx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + latest, + ntohs(ack.maxSkew), + hard, + ntohl(ack.previousPacket), + ntohl(ack.serial), + rxrpc_acks[ack.reason], + ack.nAcks); + + if (ack.reason == RXRPC_ACK_PING) { + _proto("Rx ACK %%%u PING Request", latest); + rxrpc_propose_ACK(call, RXRPC_ACK_PING_RESPONSE, + sp->hdr.serial, true); + } + + /* discard any out-of-order or duplicate ACKs */ + if (latest - call->acks_latest <= 0) { + _debug("discard ACK %d <= %d", + latest, call->acks_latest); + goto discard; + } + call->acks_latest = latest; + + if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST && + call->state != RXRPC_CALL_CLIENT_AWAIT_REPLY && + call->state != RXRPC_CALL_SERVER_SEND_REPLY && + call->state != RXRPC_CALL_SERVER_AWAIT_ACK) + goto discard; + + _debug("Tx=%d H=%u S=%d", tx, call->acks_hard, call->state); + + if (hard > 0) { + if (hard - 1 > tx) { + _debug("hard-ACK'd packet %d not transmitted" + " (%d top)", + hard - 1, tx); + goto protocol_error; + } + + if ((call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY || + call->state == RXRPC_CALL_SERVER_AWAIT_ACK) && + hard > tx) + goto all_acked; + + smp_rmb(); + rxrpc_rotate_tx_window(call, hard - 1); + } + + if (ack.nAcks > 0) { + if (hard - 1 + ack.nAcks > tx) { + _debug("soft-ACK'd packet %d+%d not" + " transmitted (%d top)", + hard - 1, ack.nAcks, tx); + goto protocol_error; + } + + if (rxrpc_process_soft_ACKs(call, &ack, skb) < 0) + goto protocol_error; + } + goto discard; + + /* complete ACK to process */ + case RXRPC_PACKET_TYPE_ACKALL: + goto all_acked; + + /* abort and busy are handled elsewhere */ + case RXRPC_PACKET_TYPE_BUSY: + case RXRPC_PACKET_TYPE_ABORT: + BUG(); + + /* connection level events - also handled elsewhere */ + case RXRPC_PACKET_TYPE_CHALLENGE: + case RXRPC_PACKET_TYPE_RESPONSE: + case RXRPC_PACKET_TYPE_DEBUG: + BUG(); + } + + /* if we've had a hard ACK that covers all the packets we've sent, then + * that ends that phase of the operation */ +all_acked: + write_lock_bh(&call->state_lock); + _debug("ack all %d", call->state); + + switch (call->state) { + case RXRPC_CALL_CLIENT_AWAIT_REPLY: + call->state = RXRPC_CALL_CLIENT_RECV_REPLY; + break; + case RXRPC_CALL_SERVER_AWAIT_ACK: + _debug("srv complete"); + call->state = RXRPC_CALL_COMPLETE; + post_ACK = true; + break; + case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_SERVER_RECV_REQUEST: + goto protocol_error_unlock; /* can't occur yet */ + default: + write_unlock_bh(&call->state_lock); + goto discard; /* assume packet left over from earlier phase */ + } + + write_unlock_bh(&call->state_lock); + + /* if all the packets we sent are hard-ACK'd, then we can discard + * whatever we've got left */ + _debug("clear Tx %d", + CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz)); + + del_timer_sync(&call->resend_timer); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + + if (call->acks_window) + rxrpc_zap_tx_window(call); + + if (post_ACK) { + /* post the final ACK message for userspace to pick up */ + _debug("post ACK"); + skb->mark = RXRPC_SKB_MARK_FINAL_ACK; + sp->call = call; + rxrpc_get_call(call); + spin_lock_bh(&call->lock); + if (rxrpc_queue_rcv_skb(call, skb, true, true) < 0) + BUG(); + spin_unlock_bh(&call->lock); + goto process_further; + } + +discard: + rxrpc_free_skb(skb); + goto process_further; + +protocol_error_unlock: + write_unlock_bh(&call->state_lock); +protocol_error: + rxrpc_free_skb(skb); + _leave(" = -EPROTO"); + return -EPROTO; +} + +/* + * post a message to the socket Rx queue for recvmsg() to pick up + */ +static int rxrpc_post_message(struct rxrpc_call *call, u32 mark, u32 error, + bool fatal) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + int ret; + + _enter("{%d,%lx},%u,%u,%d", + call->debug_id, call->flags, mark, error, fatal); + + /* remove timers and things for fatal messages */ + if (fatal) { + del_timer_sync(&call->resend_timer); + del_timer_sync(&call->ack_timer); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + } + + if (mark != RXRPC_SKB_MARK_NEW_CALL && + !test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) { + _leave("[no userid]"); + return 0; + } + + if (!test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags)) { + skb = alloc_skb(0, GFP_NOFS); + if (!skb) + return -ENOMEM; + + rxrpc_new_skb(skb); + + skb->mark = mark; + + sp = rxrpc_skb(skb); + memset(sp, 0, sizeof(*sp)); + sp->error = error; + sp->call = call; + rxrpc_get_call(call); + + spin_lock_bh(&call->lock); + ret = rxrpc_queue_rcv_skb(call, skb, true, fatal); + spin_unlock_bh(&call->lock); + if (ret < 0) + BUG(); + } + + return 0; +} + +/* + * handle background processing of incoming call packets and ACK / abort + * generation + */ +void rxrpc_process_call(struct work_struct *work) +{ + struct rxrpc_call *call = + container_of(work, struct rxrpc_call, processor); + struct rxrpc_ackpacket ack; + struct rxrpc_ackinfo ackinfo; + struct rxrpc_header hdr; + struct msghdr msg; + struct kvec iov[5]; + unsigned long bits; + __be32 data; + size_t len; + int genbit, loop, nbit, ioc, ret; + u32 abort_code = RX_PROTOCOL_ERROR; + u8 *acks = NULL; + + //printk("\n--------------------\n"); + _enter("{%d,%s,%lx} [%lu]", + call->debug_id, rxrpc_call_states[call->state], call->events, + (jiffies - call->creation_jif) / (HZ / 10)); + + if (test_and_set_bit(RXRPC_CALL_PROC_BUSY, &call->flags)) { + _debug("XXXXXXXXXXXXX RUNNING ON MULTIPLE CPUS XXXXXXXXXXXXX"); + return; + } + + /* there's a good chance we're going to have to send a message, so set + * one up in advance */ + msg.msg_name = &call->conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(call->conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr.epoch = call->conn->epoch; + hdr.cid = call->cid; + hdr.callNumber = call->call_id; + hdr.seq = 0; + hdr.type = RXRPC_PACKET_TYPE_ACK; + hdr.flags = call->conn->out_clientflag; + hdr.userStatus = 0; + hdr.securityIndex = call->conn->security_ix; + hdr._rsvd = 0; + hdr.serviceId = call->conn->service_id; + + memset(iov, 0, sizeof(iov)); + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + + /* deal with events of a final nature */ + if (test_bit(RXRPC_CALL_RELEASE, &call->events)) { + rxrpc_release_call(call); + clear_bit(RXRPC_CALL_RELEASE, &call->events); + } + + if (test_bit(RXRPC_CALL_RCVD_ERROR, &call->events)) { + int error; + + clear_bit(RXRPC_CALL_CONN_ABORT, &call->events); + clear_bit(RXRPC_CALL_REJECT_BUSY, &call->events); + clear_bit(RXRPC_CALL_ABORT, &call->events); + + error = call->conn->trans->peer->net_error; + _debug("post net error %d", error); + + if (rxrpc_post_message(call, RXRPC_SKB_MARK_NET_ERROR, + error, true) < 0) + goto no_mem; + clear_bit(RXRPC_CALL_RCVD_ERROR, &call->events); + goto kill_ACKs; + } + + if (test_bit(RXRPC_CALL_CONN_ABORT, &call->events)) { + ASSERTCMP(call->state, >, RXRPC_CALL_COMPLETE); + + clear_bit(RXRPC_CALL_REJECT_BUSY, &call->events); + clear_bit(RXRPC_CALL_ABORT, &call->events); + + _debug("post conn abort"); + + if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR, + call->conn->error, true) < 0) + goto no_mem; + clear_bit(RXRPC_CALL_CONN_ABORT, &call->events); + goto kill_ACKs; + } + + if (test_bit(RXRPC_CALL_REJECT_BUSY, &call->events)) { + hdr.type = RXRPC_PACKET_TYPE_BUSY; + genbit = RXRPC_CALL_REJECT_BUSY; + goto send_message; + } + + if (test_bit(RXRPC_CALL_ABORT, &call->events)) { + ASSERTCMP(call->state, >, RXRPC_CALL_COMPLETE); + + if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR, + ECONNABORTED, true) < 0) + goto no_mem; + hdr.type = RXRPC_PACKET_TYPE_ABORT; + data = htonl(call->abort_code); + iov[1].iov_base = &data; + iov[1].iov_len = sizeof(data); + genbit = RXRPC_CALL_ABORT; + goto send_message; + } + + if (test_bit(RXRPC_CALL_ACK_FINAL, &call->events)) { + hdr.type = RXRPC_PACKET_TYPE_ACKALL; + genbit = RXRPC_CALL_ACK_FINAL; + goto send_message; + } + + if (call->events & ((1 << RXRPC_CALL_RCVD_BUSY) | + (1 << RXRPC_CALL_RCVD_ABORT)) + ) { + u32 mark; + + if (test_bit(RXRPC_CALL_RCVD_ABORT, &call->events)) + mark = RXRPC_SKB_MARK_REMOTE_ABORT; + else + mark = RXRPC_SKB_MARK_BUSY; + + _debug("post abort/busy"); + rxrpc_clear_tx_window(call); + if (rxrpc_post_message(call, mark, ECONNABORTED, true) < 0) + goto no_mem; + + clear_bit(RXRPC_CALL_RCVD_BUSY, &call->events); + clear_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + goto kill_ACKs; + } + + if (test_and_clear_bit(RXRPC_CALL_RCVD_ACKALL, &call->events)) { + _debug("do implicit ackall"); + rxrpc_clear_tx_window(call); + } + + if (test_bit(RXRPC_CALL_LIFE_TIMER, &call->events)) { + write_lock_bh(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_CALL_TIMEOUT; + set_bit(RXRPC_CALL_ABORT, &call->events); + } + write_unlock_bh(&call->state_lock); + + _debug("post timeout"); + if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR, + ETIME, true) < 0) + goto no_mem; + + clear_bit(RXRPC_CALL_LIFE_TIMER, &call->events); + goto kill_ACKs; + } + + /* deal with assorted inbound messages */ + if (!skb_queue_empty(&call->rx_queue)) { + switch (rxrpc_process_rx_queue(call, &abort_code)) { + case 0: + case -EAGAIN: + break; + case -ENOMEM: + goto no_mem; + case -EKEYEXPIRED: + case -EKEYREJECTED: + case -EPROTO: + rxrpc_abort_call(call, abort_code); + goto kill_ACKs; + } + } + + /* handle resending */ + if (test_and_clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) + rxrpc_resend_timer(call); + if (test_and_clear_bit(RXRPC_CALL_RESEND, &call->events)) + rxrpc_resend(call); + + /* consider sending an ordinary ACK */ + if (test_bit(RXRPC_CALL_ACK, &call->events)) { + __be32 pad; + + _debug("send ACK: window: %d - %d { %lx }", + call->rx_data_eaten, call->ackr_win_top, + call->ackr_window[0]); + + if (call->state > RXRPC_CALL_SERVER_ACK_REQUEST && + call->ackr_reason != RXRPC_ACK_PING_RESPONSE) { + /* ACK by sending reply DATA packet in this state */ + clear_bit(RXRPC_CALL_ACK, &call->events); + goto maybe_reschedule; + } + + genbit = RXRPC_CALL_ACK; + + acks = kzalloc(call->ackr_win_top - call->rx_data_eaten, + GFP_NOFS); + if (!acks) + goto no_mem; + + //hdr.flags = RXRPC_SLOW_START_OK; + ack.bufferSpace = htons(8); + ack.maxSkew = 0; + ack.serial = 0; + ack.reason = 0; + + ackinfo.rxMTU = htonl(5692); +// ackinfo.rxMTU = htonl(call->conn->trans->peer->maxdata); + ackinfo.maxMTU = htonl(call->conn->trans->peer->maxdata); + ackinfo.rwind = htonl(32); + ackinfo.jumbo_max = htonl(4); + + spin_lock_bh(&call->lock); + ack.reason = call->ackr_reason; + ack.serial = call->ackr_serial; + ack.previousPacket = call->ackr_prev_seq; + ack.firstPacket = htonl(call->rx_data_eaten + 1); + + ack.nAcks = 0; + for (loop = 0; loop < RXRPC_ACKR_WINDOW_ASZ; loop++) { + nbit = loop * BITS_PER_LONG; + for (bits = call->ackr_window[loop]; bits; bits >>= 1 + ) { + _debug("- l=%d n=%d b=%lx", loop, nbit, bits); + if (bits & 1) { + acks[nbit] = RXRPC_ACK_TYPE_ACK; + ack.nAcks = nbit + 1; + } + nbit++; + } + } + call->ackr_reason = 0; + spin_unlock_bh(&call->lock); + + pad = 0; + + iov[1].iov_base = &ack; + iov[1].iov_len = sizeof(ack); + iov[2].iov_base = acks; + iov[2].iov_len = ack.nAcks; + iov[3].iov_base = &pad; + iov[3].iov_len = 3; + iov[4].iov_base = &ackinfo; + iov[4].iov_len = sizeof(ackinfo); + + switch (ack.reason) { + case RXRPC_ACK_REQUESTED: + case RXRPC_ACK_DUPLICATE: + case RXRPC_ACK_OUT_OF_SEQUENCE: + case RXRPC_ACK_EXCEEDS_WINDOW: + case RXRPC_ACK_NOSPACE: + case RXRPC_ACK_PING: + case RXRPC_ACK_PING_RESPONSE: + goto send_ACK_with_skew; + case RXRPC_ACK_DELAY: + case RXRPC_ACK_IDLE: + goto send_ACK; + } + } + + /* handle completion of security negotiations on an incoming + * connection */ + if (test_and_clear_bit(RXRPC_CALL_SECURED, &call->events)) { + _debug("secured"); + spin_lock_bh(&call->lock); + + if (call->state == RXRPC_CALL_SERVER_SECURING) { + _debug("securing"); + write_lock(&call->conn->lock); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_bit(RXRPC_CALL_RELEASE, &call->events)) { + _debug("not released"); + call->state = RXRPC_CALL_SERVER_ACCEPTING; + list_move_tail(&call->accept_link, + &call->socket->acceptq); + } + write_unlock(&call->conn->lock); + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) + set_bit(RXRPC_CALL_POST_ACCEPT, &call->events); + read_unlock(&call->state_lock); + } + + spin_unlock_bh(&call->lock); + if (!test_bit(RXRPC_CALL_POST_ACCEPT, &call->events)) + goto maybe_reschedule; + } + + /* post a notification of an acceptable connection to the app */ + if (test_bit(RXRPC_CALL_POST_ACCEPT, &call->events)) { + _debug("post accept"); + if (rxrpc_post_message(call, RXRPC_SKB_MARK_NEW_CALL, + 0, false) < 0) + goto no_mem; + clear_bit(RXRPC_CALL_POST_ACCEPT, &call->events); + goto maybe_reschedule; + } + + /* handle incoming call acceptance */ + if (test_and_clear_bit(RXRPC_CALL_ACCEPTED, &call->events)) { + _debug("accepted"); + ASSERTCMP(call->rx_data_post, ==, 0); + call->rx_data_post = 1; + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) + set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events); + read_unlock_bh(&call->state_lock); + } + + /* drain the out of sequence received packet queue into the packet Rx + * queue */ + if (test_and_clear_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events)) { + while (call->rx_data_post == call->rx_first_oos) + if (rxrpc_drain_rx_oos_queue(call) < 0) + break; + goto maybe_reschedule; + } + + /* other events may have been raised since we started checking */ + goto maybe_reschedule; + +send_ACK_with_skew: + ack.maxSkew = htons(atomic_read(&call->conn->hi_serial) - + ntohl(ack.serial)); +send_ACK: + hdr.serial = htonl(atomic_inc_return(&call->conn->serial)); + _proto("Tx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + ntohl(hdr.serial), + ntohs(ack.maxSkew), + ntohl(ack.firstPacket), + ntohl(ack.previousPacket), + ntohl(ack.serial), + rxrpc_acks[ack.reason], + ack.nAcks); + + del_timer_sync(&call->ack_timer); + if (ack.nAcks > 0) + set_bit(RXRPC_CALL_TX_SOFT_ACK, &call->flags); + goto send_message_2; + +send_message: + _debug("send message"); + + hdr.serial = htonl(atomic_inc_return(&call->conn->serial)); + _proto("Tx %s %%%u", rxrpc_pkts[hdr.type], ntohl(hdr.serial)); +send_message_2: + + len = iov[0].iov_len; + ioc = 1; + if (iov[4].iov_len) { + ioc = 5; + len += iov[4].iov_len; + len += iov[3].iov_len; + len += iov[2].iov_len; + len += iov[1].iov_len; + } else if (iov[3].iov_len) { + ioc = 4; + len += iov[3].iov_len; + len += iov[2].iov_len; + len += iov[1].iov_len; + } else if (iov[2].iov_len) { + ioc = 3; + len += iov[2].iov_len; + len += iov[1].iov_len; + } else if (iov[1].iov_len) { + ioc = 2; + len += iov[1].iov_len; + } + + ret = kernel_sendmsg(call->conn->trans->local->socket, + &msg, iov, ioc, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) + rxrpc_queue_call(call); + read_unlock_bh(&call->state_lock); + goto error; + } + + switch (genbit) { + case RXRPC_CALL_ABORT: + clear_bit(genbit, &call->events); + clear_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + goto kill_ACKs; + + case RXRPC_CALL_ACK_FINAL: + write_lock_bh(&call->state_lock); + if (call->state == RXRPC_CALL_CLIENT_FINAL_ACK) + call->state = RXRPC_CALL_COMPLETE; + write_unlock_bh(&call->state_lock); + goto kill_ACKs; + + default: + clear_bit(genbit, &call->events); + switch (call->state) { + case RXRPC_CALL_CLIENT_AWAIT_REPLY: + case RXRPC_CALL_CLIENT_RECV_REPLY: + case RXRPC_CALL_SERVER_RECV_REQUEST: + case RXRPC_CALL_SERVER_ACK_REQUEST: + _debug("start ACK timer"); + rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, + call->ackr_serial, false); + default: + break; + } + goto maybe_reschedule; + } + +kill_ACKs: + del_timer_sync(&call->ack_timer); + if (test_and_clear_bit(RXRPC_CALL_ACK_FINAL, &call->events)) + rxrpc_put_call(call); + clear_bit(RXRPC_CALL_ACK, &call->events); + +maybe_reschedule: + if (call->events || !skb_queue_empty(&call->rx_queue)) { + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) + rxrpc_queue_call(call); + read_unlock_bh(&call->state_lock); + } + + /* don't leave aborted connections on the accept queue */ + if (call->state >= RXRPC_CALL_COMPLETE && + !list_empty(&call->accept_link)) { + _debug("X unlinking once-pending call %p { e=%lx f=%lx c=%x }", + call, call->events, call->flags, + ntohl(call->conn->cid)); + + read_lock_bh(&call->state_lock); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + rxrpc_queue_call(call); + read_unlock_bh(&call->state_lock); + } + +error: + clear_bit(RXRPC_CALL_PROC_BUSY, &call->flags); + kfree(acks); + + /* because we don't want two CPUs both processing the work item for one + * call at the same time, we use a flag to note when it's busy; however + * this means there's a race between clearing the flag and setting the + * work pending bit and the work item being processed again */ + if (call->events && !work_pending(&call->processor)) { + _debug("jumpstart %x", ntohl(call->conn->cid)); + rxrpc_queue_call(call); + } + + _leave(""); + return; + +no_mem: + _debug("out of memory"); + goto maybe_reschedule; +} diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c new file mode 100644 index 0000000..4d92d88 --- /dev/null +++ b/net/rxrpc/ar-call.c @@ -0,0 +1,804 @@ +/* RxRPC individual remote procedure call handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include "ar-internal.h" + +struct kmem_cache *rxrpc_call_jar; +LIST_HEAD(rxrpc_calls); +DEFINE_RWLOCK(rxrpc_call_lock); +static unsigned rxrpc_call_max_lifetime = 60; +static unsigned rxrpc_dead_call_timeout = 2; + +static void rxrpc_destroy_call(struct work_struct *work); +static void rxrpc_call_life_expired(unsigned long _call); +static void rxrpc_dead_call_expired(unsigned long _call); +static void rxrpc_ack_time_expired(unsigned long _call); +static void rxrpc_resend_time_expired(unsigned long _call); + +/* + * allocate a new call + */ +static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp) +{ + struct rxrpc_call *call; + + call = kmem_cache_zalloc(rxrpc_call_jar, gfp); + if (!call) + return NULL; + + call->acks_winsz = 16; + call->acks_window = kmalloc(call->acks_winsz * sizeof(unsigned long), + gfp); + if (!call->acks_window) { + kmem_cache_free(rxrpc_call_jar, call); + return NULL; + } + + setup_timer(&call->lifetimer, &rxrpc_call_life_expired, + (unsigned long) call); + setup_timer(&call->deadspan, &rxrpc_dead_call_expired, + (unsigned long) call); + setup_timer(&call->ack_timer, &rxrpc_ack_time_expired, + (unsigned long) call); + setup_timer(&call->resend_timer, &rxrpc_resend_time_expired, + (unsigned long) call); + INIT_WORK(&call->destroyer, &rxrpc_destroy_call); + INIT_WORK(&call->processor, &rxrpc_process_call); + INIT_LIST_HEAD(&call->accept_link); + skb_queue_head_init(&call->rx_queue); + skb_queue_head_init(&call->rx_oos_queue); + init_waitqueue_head(&call->tx_waitq); + spin_lock_init(&call->lock); + rwlock_init(&call->state_lock); + atomic_set(&call->usage, 1); + call->debug_id = atomic_inc_return(&rxrpc_debug_id); + call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; + + memset(&call->sock_node, 0xed, sizeof(call->sock_node)); + + call->rx_data_expect = 1; + call->rx_data_eaten = 0; + call->rx_first_oos = 0; + call->ackr_win_top = call->rx_data_eaten + 1 + RXRPC_MAXACKS; + call->creation_jif = jiffies; + return call; +} + +/* + * allocate a new client call and attempt to to get a connection slot for it + */ +static struct rxrpc_call *rxrpc_alloc_client_call( + struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle, + gfp_t gfp) +{ + struct rxrpc_call *call; + int ret; + + _enter(""); + + ASSERT(rx != NULL); + ASSERT(trans != NULL); + ASSERT(bundle != NULL); + + call = rxrpc_alloc_call(gfp); + if (!call) + return ERR_PTR(-ENOMEM); + + sock_hold(&rx->sk); + call->socket = rx; + call->rx_data_post = 1; + + ret = rxrpc_connect_call(rx, trans, bundle, call, gfp); + if (ret < 0) { + kmem_cache_free(rxrpc_call_jar, call); + return ERR_PTR(ret); + } + + spin_lock(&call->conn->trans->peer->lock); + list_add(&call->error_link, &call->conn->trans->peer->error_targets); + spin_unlock(&call->conn->trans->peer->lock); + + call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ; + add_timer(&call->lifetimer); + + _leave(" = %p", call); + return call; +} + +/* + * set up a call for the given data + * - called in process context with IRQs enabled + */ +struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle, + unsigned long user_call_ID, + int create, + gfp_t gfp) +{ + struct rxrpc_call *call, *candidate; + struct rb_node *p, *parent, **pp; + + _enter("%p,%d,%d,%lx,%d", + rx, trans ? trans->debug_id : -1, bundle ? bundle->debug_id : -1, + user_call_ID, create); + + /* search the extant calls first for one that matches the specified + * user ID */ + read_lock(&rx->call_lock); + + p = rx->calls.rb_node; + while (p) { + call = rb_entry(p, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + p = p->rb_left; + else if (user_call_ID > call->user_call_ID) + p = p->rb_right; + else + goto found_extant_call; + } + + read_unlock(&rx->call_lock); + + if (!create || !trans) + return ERR_PTR(-EBADSLT); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_client_call(rx, trans, bundle, gfp); + if (IS_ERR(candidate)) { + _leave(" = %ld", PTR_ERR(candidate)); + return candidate; + } + + candidate->user_call_ID = user_call_ID; + __set_bit(RXRPC_CALL_HAS_USERID, &candidate->flags); + + write_lock(&rx->call_lock); + + pp = &rx->calls.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + call = rb_entry(parent, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + pp = &(*pp)->rb_left; + else if (user_call_ID > call->user_call_ID) + pp = &(*pp)->rb_right; + else + goto found_extant_second; + } + + /* second search also failed; add the new call */ + call = candidate; + candidate = NULL; + rxrpc_get_call(call); + + rb_link_node(&call->sock_node, parent, pp); + rb_insert_color(&call->sock_node, &rx->calls); + write_unlock(&rx->call_lock); + + write_lock_bh(&rxrpc_call_lock); + list_add_tail(&call->link, &rxrpc_calls); + write_unlock_bh(&rxrpc_call_lock); + + _net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id); + + _leave(" = %p [new]", call); + return call; + + /* we found the call in the list immediately */ +found_extant_call: + rxrpc_get_call(call); + read_unlock(&rx->call_lock); + _leave(" = %p [extant %d]", call, atomic_read(&call->usage)); + return call; + + /* we found the call on the second time through the list */ +found_extant_second: + rxrpc_get_call(call); + write_unlock(&rx->call_lock); + rxrpc_put_call(candidate); + _leave(" = %p [second %d]", call, atomic_read(&call->usage)); + return call; +} + +/* + * set up an incoming call + * - called in process context with IRQs enabled + */ +struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, + struct rxrpc_connection *conn, + struct rxrpc_header *hdr, + gfp_t gfp) +{ + struct rxrpc_call *call, *candidate; + struct rb_node **p, *parent; + __be32 call_id; + + _enter(",%d,,%x", conn->debug_id, gfp); + + ASSERT(rx != NULL); + + candidate = rxrpc_alloc_call(gfp); + if (!candidate) + return ERR_PTR(-EBUSY); + + candidate->socket = rx; + candidate->conn = conn; + candidate->cid = hdr->cid; + candidate->call_id = hdr->callNumber; + candidate->channel = ntohl(hdr->cid) & RXRPC_CHANNELMASK; + candidate->rx_data_post = 0; + candidate->state = RXRPC_CALL_SERVER_ACCEPTING; + if (conn->security_ix > 0) + candidate->state = RXRPC_CALL_SERVER_SECURING; + + write_lock_bh(&conn->lock); + + /* set the channel for this call */ + call = conn->channels[candidate->channel]; + _debug("channel[%u] is %p", candidate->channel, call); + if (call && call->call_id == hdr->callNumber) { + /* already set; must've been a duplicate packet */ + _debug("extant call [%d]", call->state); + ASSERTCMP(call->conn, ==, conn); + + read_lock(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_LOCALLY_ABORTED: + if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) + rxrpc_queue_call(call); + case RXRPC_CALL_REMOTELY_ABORTED: + read_unlock(&call->state_lock); + goto aborted_call; + default: + rxrpc_get_call(call); + read_unlock(&call->state_lock); + goto extant_call; + } + } + + if (call) { + /* it seems the channel is still in use from the previous call + * - ditch the old binding if its call is now complete */ + _debug("CALL: %u { %s }", + call->debug_id, rxrpc_call_states[call->state]); + + if (call->state >= RXRPC_CALL_COMPLETE) { + conn->channels[call->channel] = NULL; + } else { + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = -EBUSY"); + return ERR_PTR(-EBUSY); + } + } + + /* check the call number isn't duplicate */ + _debug("check dup"); + call_id = hdr->callNumber; + p = &conn->calls.rb_node; + parent = NULL; + while (*p) { + parent = *p; + call = rb_entry(parent, struct rxrpc_call, conn_node); + + if (call_id < call->call_id) + p = &(*p)->rb_left; + else if (call_id > call->call_id) + p = &(*p)->rb_right; + else + goto old_call; + } + + /* make the call available */ + _debug("new call"); + call = candidate; + candidate = NULL; + rb_link_node(&call->conn_node, parent, p); + rb_insert_color(&call->conn_node, &conn->calls); + conn->channels[call->channel] = call; + sock_hold(&rx->sk); + atomic_inc(&conn->usage); + write_unlock_bh(&conn->lock); + + spin_lock(&conn->trans->peer->lock); + list_add(&call->error_link, &conn->trans->peer->error_targets); + spin_unlock(&conn->trans->peer->lock); + + write_lock_bh(&rxrpc_call_lock); + list_add_tail(&call->link, &rxrpc_calls); + write_unlock_bh(&rxrpc_call_lock); + + _net("CALL incoming %d on CONN %d", call->debug_id, call->conn->debug_id); + + call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ; + add_timer(&call->lifetimer); + _leave(" = %p {%d} [new]", call, call->debug_id); + return call; + +extant_call: + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = %p {%d} [extant]", call, call ? call->debug_id : -1); + return call; + +aborted_call: + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = -ECONNABORTED"); + return ERR_PTR(-ECONNABORTED); + +old_call: + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = -ECONNRESET [old]"); + return ERR_PTR(-ECONNRESET); +} + +/* + * find an extant server call + * - called in process context with IRQs enabled + */ +struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *rx, + unsigned long user_call_ID) +{ + struct rxrpc_call *call; + struct rb_node *p; + + _enter("%p,%lx", rx, user_call_ID); + + /* search the extant calls for one that matches the specified user + * ID */ + read_lock(&rx->call_lock); + + p = rx->calls.rb_node; + while (p) { + call = rb_entry(p, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + p = p->rb_left; + else if (user_call_ID > call->user_call_ID) + p = p->rb_right; + else + goto found_extant_call; + } + + read_unlock(&rx->call_lock); + _leave(" = NULL"); + return NULL; + + /* we found the call in the list immediately */ +found_extant_call: + rxrpc_get_call(call); + read_unlock(&rx->call_lock); + _leave(" = %p [%d]", call, atomic_read(&call->usage)); + return call; +} + +/* + * detach a call from a socket and set up for release + */ +void rxrpc_release_call(struct rxrpc_call *call) +{ + struct rxrpc_connection *conn = call->conn; + struct rxrpc_sock *rx = call->socket; + + _enter("{%d,%d,%d,%d}", + call->debug_id, atomic_read(&call->usage), + atomic_read(&call->ackr_not_idle), + call->rx_first_oos); + + spin_lock_bh(&call->lock); + if (test_and_set_bit(RXRPC_CALL_RELEASED, &call->flags)) + BUG(); + spin_unlock_bh(&call->lock); + + /* dissociate from the socket + * - the socket's ref on the call is passed to the death timer + */ + _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn); + + write_lock_bh(&rx->call_lock); + if (!list_empty(&call->accept_link)) { + _debug("unlinking once-pending call %p { e=%lx f=%lx }", + call, call->events, call->flags); + ASSERT(!test_bit(RXRPC_CALL_HAS_USERID, &call->flags)); + list_del_init(&call->accept_link); + sk_acceptq_removed(&rx->sk); + } else if (test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) { + rb_erase(&call->sock_node, &rx->calls); + memset(&call->sock_node, 0xdd, sizeof(call->sock_node)); + clear_bit(RXRPC_CALL_HAS_USERID, &call->flags); + } + write_unlock_bh(&rx->call_lock); + + /* free up the channel for reuse */ + spin_lock(&conn->trans->client_lock); + write_lock_bh(&conn->lock); + write_lock(&call->state_lock); + + if (conn->channels[call->channel] == call) + conn->channels[call->channel] = NULL; + + if (conn->out_clientflag && conn->bundle) { + conn->avail_calls++; + switch (conn->avail_calls) { + case 1: + list_move_tail(&conn->bundle_link, + &conn->bundle->avail_conns); + case 2 ... RXRPC_MAXCALLS - 1: + ASSERT(conn->channels[0] == NULL || + conn->channels[1] == NULL || + conn->channels[2] == NULL || + conn->channels[3] == NULL); + break; + case RXRPC_MAXCALLS: + list_move_tail(&conn->bundle_link, + &conn->bundle->unused_conns); + ASSERT(conn->channels[0] == NULL && + conn->channels[1] == NULL && + conn->channels[2] == NULL && + conn->channels[3] == NULL); + break; + default: + printk(KERN_ERR "RxRPC: conn->avail_calls=%d\n", + conn->avail_calls); + BUG(); + } + } + + spin_unlock(&conn->trans->client_lock); + + if (call->state < RXRPC_CALL_COMPLETE && + call->state != RXRPC_CALL_CLIENT_FINAL_ACK) { + _debug("+++ ABORTING STATE %d +++\n", call->state); + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_CALL_DEAD; + set_bit(RXRPC_CALL_ABORT, &call->events); + rxrpc_queue_call(call); + } + write_unlock(&call->state_lock); + write_unlock_bh(&conn->lock); + + /* clean up the Rx queue */ + if (!skb_queue_empty(&call->rx_queue) || + !skb_queue_empty(&call->rx_oos_queue)) { + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + + _debug("purge Rx queues"); + + spin_lock_bh(&call->lock); + while ((skb = skb_dequeue(&call->rx_queue)) || + (skb = skb_dequeue(&call->rx_oos_queue))) { + sp = rxrpc_skb(skb); + if (sp->call) { + ASSERTCMP(sp->call, ==, call); + rxrpc_put_call(call); + sp->call = NULL; + } + skb->destructor = NULL; + spin_unlock_bh(&call->lock); + + _debug("- zap %s %%%u #%u", + rxrpc_pkts[sp->hdr.type], + ntohl(sp->hdr.serial), + ntohl(sp->hdr.seq)); + rxrpc_free_skb(skb); + spin_lock_bh(&call->lock); + } + spin_unlock_bh(&call->lock); + + ASSERTCMP(call->state, !=, RXRPC_CALL_COMPLETE); + } + + del_timer_sync(&call->resend_timer); + del_timer_sync(&call->ack_timer); + del_timer_sync(&call->lifetimer); + call->deadspan.expires = jiffies + rxrpc_dead_call_timeout * HZ; + add_timer(&call->deadspan); + + _leave(""); +} + +/* + * handle a dead call being ready for reaping + */ +static void rxrpc_dead_call_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _enter("{%d}", call->debug_id); + + write_lock_bh(&call->state_lock); + call->state = RXRPC_CALL_DEAD; + write_unlock_bh(&call->state_lock); + rxrpc_put_call(call); +} + +/* + * mark a call as to be released, aborting it if it's still in progress + * - called with softirqs disabled + */ +static void rxrpc_mark_call_released(struct rxrpc_call *call) +{ + bool sched; + + write_lock(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) { + sched = false; + if (call->state < RXRPC_CALL_COMPLETE) { + _debug("abort call %p", call); + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_CALL_DEAD; + if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) + sched = true; + } + if (!test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + sched = true; + if (sched) + rxrpc_queue_call(call); + } + write_unlock(&call->state_lock); +} + +/* + * release all the calls associated with a socket + */ +void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx) +{ + struct rxrpc_call *call; + struct rb_node *p; + + _enter("%p", rx); + + read_lock_bh(&rx->call_lock); + + /* mark all the calls as no longer wanting incoming packets */ + for (p = rb_first(&rx->calls); p; p = rb_next(p)) { + call = rb_entry(p, struct rxrpc_call, sock_node); + rxrpc_mark_call_released(call); + } + + /* kill the not-yet-accepted incoming calls */ + list_for_each_entry(call, &rx->secureq, accept_link) { + rxrpc_mark_call_released(call); + } + + list_for_each_entry(call, &rx->acceptq, accept_link) { + rxrpc_mark_call_released(call); + } + + read_unlock_bh(&rx->call_lock); + _leave(""); +} + +/* + * release a call + */ +void __rxrpc_put_call(struct rxrpc_call *call) +{ + ASSERT(call != NULL); + + _enter("%p{u=%d}", call, atomic_read(&call->usage)); + + ASSERTCMP(atomic_read(&call->usage), >, 0); + + if (atomic_dec_and_test(&call->usage)) { + _debug("call %d dead", call->debug_id); + ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD); + rxrpc_queue_work(&call->destroyer); + } + _leave(""); +} + +/* + * clean up a call + */ +static void rxrpc_cleanup_call(struct rxrpc_call *call) +{ + _net("DESTROY CALL %d", call->debug_id); + + ASSERT(call->socket); + + memset(&call->sock_node, 0xcd, sizeof(call->sock_node)); + + del_timer_sync(&call->lifetimer); + del_timer_sync(&call->deadspan); + del_timer_sync(&call->ack_timer); + del_timer_sync(&call->resend_timer); + + ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); + ASSERTCMP(call->events, ==, 0); + if (work_pending(&call->processor)) { + _debug("defer destroy"); + rxrpc_queue_work(&call->destroyer); + return; + } + + if (call->conn) { + spin_lock(&call->conn->trans->peer->lock); + list_del(&call->error_link); + spin_unlock(&call->conn->trans->peer->lock); + + write_lock_bh(&call->conn->lock); + rb_erase(&call->conn_node, &call->conn->calls); + write_unlock_bh(&call->conn->lock); + rxrpc_put_connection(call->conn); + } + + if (call->acks_window) { + _debug("kill Tx window %d", + CIRC_CNT(call->acks_head, call->acks_tail, + call->acks_winsz)); + smp_mb(); + while (CIRC_CNT(call->acks_head, call->acks_tail, + call->acks_winsz) > 0) { + struct rxrpc_skb_priv *sp; + unsigned long _skb; + + _skb = call->acks_window[call->acks_tail] & ~1; + sp = rxrpc_skb((struct sk_buff *) _skb); + _debug("+++ clear Tx %u", ntohl(sp->hdr.seq)); + rxrpc_free_skb((struct sk_buff *) _skb); + call->acks_tail = + (call->acks_tail + 1) & (call->acks_winsz - 1); + } + + kfree(call->acks_window); + } + + rxrpc_free_skb(call->tx_pending); + + rxrpc_purge_queue(&call->rx_queue); + ASSERT(skb_queue_empty(&call->rx_oos_queue)); + sock_put(&call->socket->sk); + kmem_cache_free(rxrpc_call_jar, call); +} + +/* + * destroy a call + */ +static void rxrpc_destroy_call(struct work_struct *work) +{ + struct rxrpc_call *call = + container_of(work, struct rxrpc_call, destroyer); + + _enter("%p{%d,%d,%p}", + call, atomic_read(&call->usage), call->channel, call->conn); + + ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD); + + write_lock_bh(&rxrpc_call_lock); + list_del_init(&call->link); + write_unlock_bh(&rxrpc_call_lock); + + rxrpc_cleanup_call(call); + _leave(""); +} + +/* + * preemptively destroy all the call records from a transport endpoint rather + * than waiting for them to time out + */ +void __exit rxrpc_destroy_all_calls(void) +{ + struct rxrpc_call *call; + + _enter(""); + write_lock_bh(&rxrpc_call_lock); + + while (!list_empty(&rxrpc_calls)) { + call = list_entry(rxrpc_calls.next, struct rxrpc_call, link); + _debug("Zapping call %p", call); + + list_del_init(&call->link); + + switch (atomic_read(&call->usage)) { + case 0: + ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD); + break; + case 1: + if (del_timer_sync(&call->deadspan) != 0 && + call->state != RXRPC_CALL_DEAD) + rxrpc_dead_call_expired((unsigned long) call); + if (call->state != RXRPC_CALL_DEAD) + break; + default: + printk(KERN_ERR "RXRPC:" + " Call %p still in use (%d,%d,%s,%lx,%lx)!\n", + call, atomic_read(&call->usage), + atomic_read(&call->ackr_not_idle), + rxrpc_call_states[call->state], + call->flags, call->events); + if (!skb_queue_empty(&call->rx_queue)) + printk(KERN_ERR"RXRPC: Rx queue occupied\n"); + if (!skb_queue_empty(&call->rx_oos_queue)) + printk(KERN_ERR"RXRPC: OOS queue occupied\n"); + break; + } + + write_unlock_bh(&rxrpc_call_lock); + cond_resched(); + write_lock_bh(&rxrpc_call_lock); + } + + write_unlock_bh(&rxrpc_call_lock); + _leave(""); +} + +/* + * handle call lifetime being exceeded + */ +static void rxrpc_call_life_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + if (call->state >= RXRPC_CALL_COMPLETE) + return; + + _enter("{%d}", call->debug_id); + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) { + set_bit(RXRPC_CALL_LIFE_TIMER, &call->events); + rxrpc_queue_call(call); + } + read_unlock_bh(&call->state_lock); +} + +/* + * handle resend timer expiry + */ +static void rxrpc_resend_time_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _enter("{%d}", call->debug_id); + + if (call->state >= RXRPC_CALL_COMPLETE) + return; + + read_lock_bh(&call->state_lock); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) + rxrpc_queue_call(call); + read_unlock_bh(&call->state_lock); +} + +/* + * handle ACK timer expiry + */ +static void rxrpc_ack_time_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _enter("{%d}", call->debug_id); + + if (call->state >= RXRPC_CALL_COMPLETE) + return; + + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_ACK, &call->events)) + rxrpc_queue_call(call); + read_unlock_bh(&call->state_lock); +} diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c new file mode 100644 index 0000000..43cb3e0 --- /dev/null +++ b/net/rxrpc/ar-connection.c @@ -0,0 +1,911 @@ +/* RxRPC virtual connection handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static void rxrpc_connection_reaper(struct work_struct *work); + +LIST_HEAD(rxrpc_connections); +DEFINE_RWLOCK(rxrpc_connection_lock); +static unsigned long rxrpc_connection_timeout = 10 * 60; +static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper); + +/* + * allocate a new client connection bundle + */ +static struct rxrpc_conn_bundle *rxrpc_alloc_bundle(gfp_t gfp) +{ + struct rxrpc_conn_bundle *bundle; + + _enter(""); + + bundle = kzalloc(sizeof(struct rxrpc_conn_bundle), gfp); + if (bundle) { + INIT_LIST_HEAD(&bundle->unused_conns); + INIT_LIST_HEAD(&bundle->avail_conns); + INIT_LIST_HEAD(&bundle->busy_conns); + init_waitqueue_head(&bundle->chanwait); + atomic_set(&bundle->usage, 1); + } + + _leave(" = %p", bundle); + return bundle; +} + +/* + * compare bundle parameters with what we're looking for + * - return -ve, 0 or +ve + */ +static inline +int rxrpc_cmp_bundle(const struct rxrpc_conn_bundle *bundle, + struct key *key, __be16 service_id) +{ + return (bundle->service_id - service_id) ?: + ((unsigned long) bundle->key - (unsigned long) key); +} + +/* + * get bundle of client connections that a client socket can make use of + */ +struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct key *key, + __be16 service_id, + gfp_t gfp) +{ + struct rxrpc_conn_bundle *bundle, *candidate; + struct rb_node *p, *parent, **pp; + + _enter("%p{%x},%x,%hx,", + rx, key_serial(key), trans->debug_id, ntohl(service_id)); + + if (rx->trans == trans && rx->bundle) { + atomic_inc(&rx->bundle->usage); + return rx->bundle; + } + + /* search the extant bundles first for one that matches the specified + * user ID */ + spin_lock(&trans->client_lock); + + p = trans->bundles.rb_node; + while (p) { + bundle = rb_entry(p, struct rxrpc_conn_bundle, node); + + if (rxrpc_cmp_bundle(bundle, key, service_id) < 0) + p = p->rb_left; + else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0) + p = p->rb_right; + else + goto found_extant_bundle; + } + + spin_unlock(&trans->client_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_bundle(gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + candidate->key = key_get(key); + candidate->service_id = service_id; + + spin_lock(&trans->client_lock); + + pp = &trans->bundles.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + bundle = rb_entry(parent, struct rxrpc_conn_bundle, node); + + if (rxrpc_cmp_bundle(bundle, key, service_id) < 0) + pp = &(*pp)->rb_left; + else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0) + pp = &(*pp)->rb_right; + else + goto found_extant_second; + } + + /* second search also failed; add the new bundle */ + bundle = candidate; + candidate = NULL; + + rb_link_node(&bundle->node, parent, pp); + rb_insert_color(&bundle->node, &trans->bundles); + spin_unlock(&trans->client_lock); + _net("BUNDLE new on trans %d", trans->debug_id); + if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { + atomic_inc(&bundle->usage); + rx->bundle = bundle; + } + _leave(" = %p [new]", bundle); + return bundle; + + /* we found the bundle in the list immediately */ +found_extant_bundle: + atomic_inc(&bundle->usage); + spin_unlock(&trans->client_lock); + _net("BUNDLE old on trans %d", trans->debug_id); + if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { + atomic_inc(&bundle->usage); + rx->bundle = bundle; + } + _leave(" = %p [extant %d]", bundle, atomic_read(&bundle->usage)); + return bundle; + + /* we found the bundle on the second time through the list */ +found_extant_second: + atomic_inc(&bundle->usage); + spin_unlock(&trans->client_lock); + kfree(candidate); + _net("BUNDLE old2 on trans %d", trans->debug_id); + if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { + atomic_inc(&bundle->usage); + rx->bundle = bundle; + } + _leave(" = %p [second %d]", bundle, atomic_read(&bundle->usage)); + return bundle; +} + +/* + * release a bundle + */ +void rxrpc_put_bundle(struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle) +{ + _enter("%p,%p{%d}",trans, bundle, atomic_read(&bundle->usage)); + + if (atomic_dec_and_lock(&bundle->usage, &trans->client_lock)) { + _debug("Destroy bundle"); + rb_erase(&bundle->node, &trans->bundles); + spin_unlock(&trans->client_lock); + ASSERT(list_empty(&bundle->unused_conns)); + ASSERT(list_empty(&bundle->avail_conns)); + ASSERT(list_empty(&bundle->busy_conns)); + ASSERTCMP(bundle->num_conns, ==, 0); + key_put(bundle->key); + kfree(bundle); + } + + _leave(""); +} + +/* + * allocate a new connection + */ +static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) +{ + struct rxrpc_connection *conn; + + _enter(""); + + conn = kzalloc(sizeof(struct rxrpc_connection), gfp); + if (conn) { + INIT_WORK(&conn->processor, &rxrpc_process_connection); + INIT_LIST_HEAD(&conn->bundle_link); + conn->calls = RB_ROOT; + skb_queue_head_init(&conn->rx_queue); + rwlock_init(&conn->lock); + spin_lock_init(&conn->state_lock); + atomic_set(&conn->usage, 1); + conn->debug_id = atomic_inc_return(&rxrpc_debug_id); + conn->avail_calls = RXRPC_MAXCALLS; + conn->size_align = 4; + conn->header_size = sizeof(struct rxrpc_header); + } + + _leave(" = %p{%d}", conn, conn->debug_id); + return conn; +} + +/* + * assign a connection ID to a connection and add it to the transport's + * connection lookup tree + * - called with transport client lock held + */ +static void rxrpc_assign_connection_id(struct rxrpc_connection *conn) +{ + struct rxrpc_connection *xconn; + struct rb_node *parent, **p; + __be32 epoch; + u32 real_conn_id; + + _enter(""); + + epoch = conn->epoch; + + write_lock_bh(&conn->trans->conn_lock); + + conn->trans->conn_idcounter += RXRPC_CID_INC; + if (conn->trans->conn_idcounter < RXRPC_CID_INC) + conn->trans->conn_idcounter = RXRPC_CID_INC; + real_conn_id = conn->trans->conn_idcounter; + +attempt_insertion: + parent = NULL; + p = &conn->trans->client_conns.rb_node; + + while (*p) { + parent = *p; + xconn = rb_entry(parent, struct rxrpc_connection, node); + + if (epoch < xconn->epoch) + p = &(*p)->rb_left; + else if (epoch > xconn->epoch) + p = &(*p)->rb_right; + else if (real_conn_id < xconn->real_conn_id) + p = &(*p)->rb_left; + else if (real_conn_id > xconn->real_conn_id) + p = &(*p)->rb_right; + else + goto id_exists; + } + + /* we've found a suitable hole - arrange for this connection to occupy + * it */ + rb_link_node(&conn->node, parent, p); + rb_insert_color(&conn->node, &conn->trans->client_conns); + + conn->real_conn_id = real_conn_id; + conn->cid = htonl(real_conn_id); + write_unlock_bh(&conn->trans->conn_lock); + _leave(" [CONNID %x CID %x]", real_conn_id, ntohl(conn->cid)); + return; + + /* we found a connection with the proposed ID - walk the tree from that + * point looking for the next unused ID */ +id_exists: + for (;;) { + real_conn_id += RXRPC_CID_INC; + if (real_conn_id < RXRPC_CID_INC) { + real_conn_id = RXRPC_CID_INC; + conn->trans->conn_idcounter = real_conn_id; + goto attempt_insertion; + } + + parent = rb_next(parent); + if (!parent) + goto attempt_insertion; + + xconn = rb_entry(parent, struct rxrpc_connection, node); + if (epoch < xconn->epoch || + real_conn_id < xconn->real_conn_id) + goto attempt_insertion; + } +} + +/* + * add a call to a connection's call-by-ID tree + */ +static void rxrpc_add_call_ID_to_conn(struct rxrpc_connection *conn, + struct rxrpc_call *call) +{ + struct rxrpc_call *xcall; + struct rb_node *parent, **p; + __be32 call_id; + + write_lock_bh(&conn->lock); + + call_id = call->call_id; + p = &conn->calls.rb_node; + parent = NULL; + while (*p) { + parent = *p; + xcall = rb_entry(parent, struct rxrpc_call, conn_node); + + if (call_id < xcall->call_id) + p = &(*p)->rb_left; + else if (call_id > xcall->call_id) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&call->conn_node, parent, p); + rb_insert_color(&call->conn_node, &conn->calls); + + write_unlock_bh(&conn->lock); +} + +/* + * connect a call on an exclusive connection + */ +static int rxrpc_connect_exclusive(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + __be16 service_id, + struct rxrpc_call *call, + gfp_t gfp) +{ + struct rxrpc_connection *conn; + int chan, ret; + + _enter(""); + + conn = rx->conn; + if (!conn) { + /* not yet present - create a candidate for a new connection + * and then redo the check */ + conn = rxrpc_alloc_connection(gfp); + if (IS_ERR(conn)) { + _leave(" = %ld", PTR_ERR(conn)); + return PTR_ERR(conn); + } + + conn->trans = trans; + conn->bundle = NULL; + conn->service_id = service_id; + conn->epoch = rxrpc_epoch; + conn->in_clientflag = 0; + conn->out_clientflag = RXRPC_CLIENT_INITIATED; + conn->cid = 0; + conn->state = RXRPC_CONN_CLIENT; + conn->avail_calls = RXRPC_MAXCALLS - 1; + conn->security_level = rx->min_sec_level; + conn->key = key_get(rx->key); + + ret = rxrpc_init_client_conn_security(conn); + if (ret < 0) { + key_put(conn->key); + kfree(conn); + _leave(" = %d [key]", ret); + return ret; + } + + write_lock_bh(&rxrpc_connection_lock); + list_add_tail(&conn->link, &rxrpc_connections); + write_unlock_bh(&rxrpc_connection_lock); + + spin_lock(&trans->client_lock); + atomic_inc(&trans->usage); + + _net("CONNECT EXCL new %d on TRANS %d", + conn->debug_id, conn->trans->debug_id); + + rxrpc_assign_connection_id(conn); + rx->conn = conn; + } + + /* we've got a connection with a free channel and we can now attach the + * call to it + * - we're holding the transport's client lock + * - we're holding a reference on the connection + */ + for (chan = 0; chan < RXRPC_MAXCALLS; chan++) + if (!conn->channels[chan]) + goto found_channel; + goto no_free_channels; + +found_channel: + atomic_inc(&conn->usage); + conn->channels[chan] = call; + call->conn = conn; + call->channel = chan; + call->cid = conn->cid | htonl(chan); + call->call_id = htonl(++conn->call_counter); + + _net("CONNECT client on conn %d chan %d as call %x", + conn->debug_id, chan, ntohl(call->call_id)); + + spin_unlock(&trans->client_lock); + + rxrpc_add_call_ID_to_conn(conn, call); + _leave(" = 0"); + return 0; + +no_free_channels: + spin_unlock(&trans->client_lock); + _leave(" = -ENOSR"); + return -ENOSR; +} + +/* + * find a connection for a call + * - called in process context with IRQs enabled + */ +int rxrpc_connect_call(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle, + struct rxrpc_call *call, + gfp_t gfp) +{ + struct rxrpc_connection *conn, *candidate; + int chan, ret; + + DECLARE_WAITQUEUE(myself, current); + + _enter("%p,%lx,", rx, call->user_call_ID); + + if (test_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags)) + return rxrpc_connect_exclusive(rx, trans, bundle->service_id, + call, gfp); + + spin_lock(&trans->client_lock); + for (;;) { + /* see if the bundle has a call slot available */ + if (!list_empty(&bundle->avail_conns)) { + _debug("avail"); + conn = list_entry(bundle->avail_conns.next, + struct rxrpc_connection, + bundle_link); + if (--conn->avail_calls == 0) + list_move(&conn->bundle_link, + &bundle->busy_conns); + ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS); + ASSERT(conn->channels[0] == NULL || + conn->channels[1] == NULL || + conn->channels[2] == NULL || + conn->channels[3] == NULL); + atomic_inc(&conn->usage); + break; + } + + if (!list_empty(&bundle->unused_conns)) { + _debug("unused"); + conn = list_entry(bundle->unused_conns.next, + struct rxrpc_connection, + bundle_link); + ASSERTCMP(conn->avail_calls, ==, RXRPC_MAXCALLS); + conn->avail_calls = RXRPC_MAXCALLS - 1; + ASSERT(conn->channels[0] == NULL && + conn->channels[1] == NULL && + conn->channels[2] == NULL && + conn->channels[3] == NULL); + atomic_inc(&conn->usage); + list_move(&conn->bundle_link, &bundle->avail_conns); + break; + } + + /* need to allocate a new connection */ + _debug("get new conn [%d]", bundle->num_conns); + + spin_unlock(&trans->client_lock); + + if (signal_pending(current)) + goto interrupted; + + if (bundle->num_conns >= 20) { + _debug("too many conns"); + + if (!(gfp & __GFP_WAIT)) { + _leave(" = -EAGAIN"); + return -EAGAIN; + } + + add_wait_queue(&bundle->chanwait, &myself); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (bundle->num_conns < 20 || + !list_empty(&bundle->unused_conns) || + !list_empty(&bundle->avail_conns)) + break; + if (signal_pending(current)) + goto interrupted_dequeue; + schedule(); + } + remove_wait_queue(&bundle->chanwait, &myself); + __set_current_state(TASK_RUNNING); + spin_lock(&trans->client_lock); + continue; + } + + /* not yet present - create a candidate for a new connection and then + * redo the check */ + candidate = rxrpc_alloc_connection(gfp); + if (IS_ERR(candidate)) { + _leave(" = %ld", PTR_ERR(candidate)); + return PTR_ERR(candidate); + } + + candidate->trans = trans; + candidate->bundle = bundle; + candidate->service_id = bundle->service_id; + candidate->epoch = rxrpc_epoch; + candidate->in_clientflag = 0; + candidate->out_clientflag = RXRPC_CLIENT_INITIATED; + candidate->cid = 0; + candidate->state = RXRPC_CONN_CLIENT; + candidate->avail_calls = RXRPC_MAXCALLS; + candidate->security_level = rx->min_sec_level; + candidate->key = key_get(bundle->key); + + ret = rxrpc_init_client_conn_security(candidate); + if (ret < 0) { + key_put(candidate->key); + kfree(candidate); + _leave(" = %d [key]", ret); + return ret; + } + + write_lock_bh(&rxrpc_connection_lock); + list_add_tail(&candidate->link, &rxrpc_connections); + write_unlock_bh(&rxrpc_connection_lock); + + spin_lock(&trans->client_lock); + + list_add(&candidate->bundle_link, &bundle->unused_conns); + bundle->num_conns++; + atomic_inc(&bundle->usage); + atomic_inc(&trans->usage); + + _net("CONNECT new %d on TRANS %d", + candidate->debug_id, candidate->trans->debug_id); + + rxrpc_assign_connection_id(candidate); + if (candidate->security) + candidate->security->prime_packet_security(candidate); + + /* leave the candidate lurking in zombie mode attached to the + * bundle until we're ready for it */ + rxrpc_put_connection(candidate); + candidate = NULL; + } + + /* we've got a connection with a free channel and we can now attach the + * call to it + * - we're holding the transport's client lock + * - we're holding a reference on the connection + * - we're holding a reference on the bundle + */ + for (chan = 0; chan < RXRPC_MAXCALLS; chan++) + if (!conn->channels[chan]) + goto found_channel; + ASSERT(conn->channels[0] == NULL || + conn->channels[1] == NULL || + conn->channels[2] == NULL || + conn->channels[3] == NULL); + BUG(); + +found_channel: + conn->channels[chan] = call; + call->conn = conn; + call->channel = chan; + call->cid = conn->cid | htonl(chan); + call->call_id = htonl(++conn->call_counter); + + _net("CONNECT client on conn %d chan %d as call %x", + conn->debug_id, chan, ntohl(call->call_id)); + + ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS); + spin_unlock(&trans->client_lock); + + rxrpc_add_call_ID_to_conn(conn, call); + + _leave(" = 0"); + return 0; + +interrupted_dequeue: + remove_wait_queue(&bundle->chanwait, &myself); + __set_current_state(TASK_RUNNING); +interrupted: + _leave(" = -ERESTARTSYS"); + return -ERESTARTSYS; +} + +/* + * get a record of an incoming connection + */ +struct rxrpc_connection * +rxrpc_incoming_connection(struct rxrpc_transport *trans, + struct rxrpc_header *hdr, + gfp_t gfp) +{ + struct rxrpc_connection *conn, *candidate = NULL; + struct rb_node *p, **pp; + const char *new = "old"; + __be32 epoch; + u32 conn_id; + + _enter(""); + + ASSERT(hdr->flags & RXRPC_CLIENT_INITIATED); + + epoch = hdr->epoch; + conn_id = ntohl(hdr->cid) & RXRPC_CIDMASK; + + /* search the connection list first */ + read_lock_bh(&trans->conn_lock); + + p = trans->server_conns.rb_node; + while (p) { + conn = rb_entry(p, struct rxrpc_connection, node); + + _debug("maybe %x", conn->real_conn_id); + + if (epoch < conn->epoch) + p = p->rb_left; + else if (epoch > conn->epoch) + p = p->rb_right; + else if (conn_id < conn->real_conn_id) + p = p->rb_left; + else if (conn_id > conn->real_conn_id) + p = p->rb_right; + else + goto found_extant_connection; + } + read_unlock_bh(&trans->conn_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_connection(gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + candidate->trans = trans; + candidate->epoch = hdr->epoch; + candidate->cid = hdr->cid & __constant_cpu_to_be32(RXRPC_CIDMASK); + candidate->service_id = hdr->serviceId; + candidate->security_ix = hdr->securityIndex; + candidate->in_clientflag = RXRPC_CLIENT_INITIATED; + candidate->out_clientflag = 0; + candidate->real_conn_id = conn_id; + candidate->state = RXRPC_CONN_SERVER; + if (candidate->service_id) + candidate->state = RXRPC_CONN_SERVER_UNSECURED; + + write_lock_bh(&trans->conn_lock); + + pp = &trans->server_conns.rb_node; + p = NULL; + while (*pp) { + p = *pp; + conn = rb_entry(p, struct rxrpc_connection, node); + + if (epoch < conn->epoch) + pp = &(*pp)->rb_left; + else if (epoch > conn->epoch) + pp = &(*pp)->rb_right; + else if (conn_id < conn->real_conn_id) + pp = &(*pp)->rb_left; + else if (conn_id > conn->real_conn_id) + pp = &(*pp)->rb_right; + else + goto found_extant_second; + } + + /* we can now add the new candidate to the list */ + conn = candidate; + candidate = NULL; + rb_link_node(&conn->node, p, pp); + rb_insert_color(&conn->node, &trans->server_conns); + atomic_inc(&conn->trans->usage); + + write_unlock_bh(&trans->conn_lock); + + write_lock_bh(&rxrpc_connection_lock); + list_add_tail(&conn->link, &rxrpc_connections); + write_unlock_bh(&rxrpc_connection_lock); + + new = "new"; + +success: + _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->real_conn_id); + + _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); + return conn; + + /* we found the connection in the list immediately */ +found_extant_connection: + if (hdr->securityIndex != conn->security_ix) { + read_unlock_bh(&trans->conn_lock); + goto security_mismatch; + } + atomic_inc(&conn->usage); + read_unlock_bh(&trans->conn_lock); + goto success; + + /* we found the connection on the second time through the list */ +found_extant_second: + if (hdr->securityIndex != conn->security_ix) { + write_unlock_bh(&trans->conn_lock); + goto security_mismatch; + } + atomic_inc(&conn->usage); + write_unlock_bh(&trans->conn_lock); + kfree(candidate); + goto success; + +security_mismatch: + kfree(candidate); + _leave(" = -EKEYREJECTED"); + return ERR_PTR(-EKEYREJECTED); +} + +/* + * find a connection based on transport and RxRPC connection ID for an incoming + * packet + */ +struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *trans, + struct rxrpc_header *hdr) +{ + struct rxrpc_connection *conn; + struct rb_node *p; + __be32 epoch; + u32 conn_id; + + _enter(",{%x,%x}", ntohl(hdr->cid), hdr->flags); + + read_lock_bh(&trans->conn_lock); + + conn_id = ntohl(hdr->cid) & RXRPC_CIDMASK; + epoch = hdr->epoch; + + if (hdr->flags & RXRPC_CLIENT_INITIATED) + p = trans->server_conns.rb_node; + else + p = trans->client_conns.rb_node; + + while (p) { + conn = rb_entry(p, struct rxrpc_connection, node); + + _debug("maybe %x", conn->real_conn_id); + + if (epoch < conn->epoch) + p = p->rb_left; + else if (epoch > conn->epoch) + p = p->rb_right; + else if (conn_id < conn->real_conn_id) + p = p->rb_left; + else if (conn_id > conn->real_conn_id) + p = p->rb_right; + else + goto found; + } + + read_unlock_bh(&trans->conn_lock); + _leave(" = NULL"); + return NULL; + +found: + atomic_inc(&conn->usage); + read_unlock_bh(&trans->conn_lock); + _leave(" = %p", conn); + return conn; +} + +/* + * release a virtual connection + */ +void rxrpc_put_connection(struct rxrpc_connection *conn) +{ + _enter("%p{u=%d,d=%d}", + conn, atomic_read(&conn->usage), conn->debug_id); + + ASSERTCMP(atomic_read(&conn->usage), >, 0); + + conn->put_time = xtime.tv_sec; + if (atomic_dec_and_test(&conn->usage)) { + _debug("zombie"); + rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); + } + + _leave(""); +} + +/* + * destroy a virtual connection + */ +static void rxrpc_destroy_connection(struct rxrpc_connection *conn) +{ + _enter("%p{%d}", conn, atomic_read(&conn->usage)); + + ASSERTCMP(atomic_read(&conn->usage), ==, 0); + + _net("DESTROY CONN %d", conn->debug_id); + + if (conn->bundle) + rxrpc_put_bundle(conn->trans, conn->bundle); + + ASSERT(RB_EMPTY_ROOT(&conn->calls)); + rxrpc_purge_queue(&conn->rx_queue); + + rxrpc_clear_conn_security(conn); + rxrpc_put_transport(conn->trans); + kfree(conn); + _leave(""); +} + +/* + * reap dead connections + */ +void rxrpc_connection_reaper(struct work_struct *work) +{ + struct rxrpc_connection *conn, *_p; + unsigned long now, earliest, reap_time; + + LIST_HEAD(graveyard); + + _enter(""); + + now = xtime.tv_sec; + earliest = ULONG_MAX; + + write_lock_bh(&rxrpc_connection_lock); + list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { + _debug("reap CONN %d { u=%d,t=%ld }", + conn->debug_id, atomic_read(&conn->usage), + (long) now - (long) conn->put_time); + + if (likely(atomic_read(&conn->usage) > 0)) + continue; + + spin_lock(&conn->trans->client_lock); + write_lock(&conn->trans->conn_lock); + reap_time = conn->put_time + rxrpc_connection_timeout; + + if (atomic_read(&conn->usage) > 0) { + ; + } else if (reap_time <= now) { + list_move_tail(&conn->link, &graveyard); + if (conn->out_clientflag) + rb_erase(&conn->node, + &conn->trans->client_conns); + else + rb_erase(&conn->node, + &conn->trans->server_conns); + if (conn->bundle) { + list_del_init(&conn->bundle_link); + conn->bundle->num_conns--; + } + + } else if (reap_time < earliest) { + earliest = reap_time; + } + + write_unlock(&conn->trans->conn_lock); + spin_unlock(&conn->trans->client_lock); + } + write_unlock_bh(&rxrpc_connection_lock); + + if (earliest != ULONG_MAX) { + _debug("reschedule reaper %ld", (long) earliest - now); + ASSERTCMP(earliest, >, now); + rxrpc_queue_delayed_work(&rxrpc_connection_reap, + (earliest - now) * HZ); + } + + /* then destroy all those pulled out */ + while (!list_empty(&graveyard)) { + conn = list_entry(graveyard.next, struct rxrpc_connection, + link); + list_del_init(&conn->link); + + ASSERTCMP(atomic_read(&conn->usage), ==, 0); + rxrpc_destroy_connection(conn); + } + + _leave(""); +} + +/* + * preemptively destroy all the connection records rather than waiting for them + * to time out + */ +void __exit rxrpc_destroy_all_connections(void) +{ + _enter(""); + + rxrpc_connection_timeout = 0; + cancel_delayed_work(&rxrpc_connection_reap); + rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); + + _leave(""); +} diff --git a/net/rxrpc/ar-connevent.c b/net/rxrpc/ar-connevent.c new file mode 100644 index 0000000..1ada43d --- /dev/null +++ b/net/rxrpc/ar-connevent.c @@ -0,0 +1,403 @@ +/* connection-level event handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * pass a connection-level abort onto all calls on that connection + */ +static void rxrpc_abort_calls(struct rxrpc_connection *conn, int state, + u32 abort_code) +{ + struct rxrpc_call *call; + struct rb_node *p; + + _enter("{%d},%x", conn->debug_id, abort_code); + + read_lock_bh(&conn->lock); + + for (p = rb_first(&conn->calls); p; p = rb_next(p)) { + call = rb_entry(p, struct rxrpc_call, conn_node); + write_lock(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = state; + call->abort_code = abort_code; + if (state == RXRPC_CALL_LOCALLY_ABORTED) + set_bit(RXRPC_CALL_CONN_ABORT, &call->events); + else + set_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + rxrpc_queue_call(call); + } + write_unlock(&call->state_lock); + } + + read_unlock_bh(&conn->lock); + _leave(""); +} + +/* + * generate a connection-level abort + */ +static int rxrpc_abort_connection(struct rxrpc_connection *conn, + u32 error, u32 abort_code) +{ + struct rxrpc_header hdr; + struct msghdr msg; + struct kvec iov[2]; + __be32 word; + size_t len; + int ret; + + _enter("%d,,%u,%u", conn->debug_id, error, abort_code); + + /* generate a connection-level abort */ + spin_lock_bh(&conn->state_lock); + if (conn->state < RXRPC_CONN_REMOTELY_ABORTED) { + conn->state = RXRPC_CONN_LOCALLY_ABORTED; + conn->error = error; + spin_unlock_bh(&conn->state_lock); + } else { + spin_unlock_bh(&conn->state_lock); + _leave(" = 0 [already dead]"); + return 0; + } + + rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code); + + msg.msg_name = &conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr.epoch = conn->epoch; + hdr.cid = conn->cid; + hdr.callNumber = 0; + hdr.seq = 0; + hdr.type = RXRPC_PACKET_TYPE_ABORT; + hdr.flags = conn->out_clientflag; + hdr.userStatus = 0; + hdr.securityIndex = conn->security_ix; + hdr._rsvd = 0; + hdr.serviceId = conn->service_id; + + word = htonl(abort_code); + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = &word; + iov[1].iov_len = sizeof(word); + + len = iov[0].iov_len + iov[1].iov_len; + + hdr.serial = htonl(atomic_inc_return(&conn->serial)); + _proto("Tx CONN ABORT %%%u { %d }", ntohl(hdr.serial), abort_code); + + ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * mark a call as being on a now-secured channel + * - must be called with softirqs disabled + */ +void rxrpc_call_is_secure(struct rxrpc_call *call) +{ + _enter("%p", call); + if (call) { + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_SECURED, &call->events)) + rxrpc_queue_call(call); + read_unlock(&call->state_lock); + } +} + +/* + * connection-level Rx packet processor + */ +static int rxrpc_process_event(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + __be32 tmp; + u32 serial; + int loop, ret; + + if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) + return -ECONNABORTED; + + serial = ntohl(sp->hdr.serial); + + switch (sp->hdr.type) { + case RXRPC_PACKET_TYPE_ABORT: + if (skb_copy_bits(skb, 0, &tmp, sizeof(tmp)) < 0) + return -EPROTO; + _proto("Rx ABORT %%%u { ac=%d }", serial, ntohl(tmp)); + + conn->state = RXRPC_CONN_REMOTELY_ABORTED; + rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, + ntohl(tmp)); + return -ECONNABORTED; + + case RXRPC_PACKET_TYPE_CHALLENGE: + if (conn->security) + return conn->security->respond_to_challenge( + conn, skb, _abort_code); + return -EPROTO; + + case RXRPC_PACKET_TYPE_RESPONSE: + if (!conn->security) + return -EPROTO; + + ret = conn->security->verify_response(conn, skb, _abort_code); + if (ret < 0) + return ret; + + ret = conn->security->init_connection_security(conn); + if (ret < 0) + return ret; + + conn->security->prime_packet_security(conn); + read_lock_bh(&conn->lock); + spin_lock(&conn->state_lock); + + if (conn->state == RXRPC_CONN_SERVER_CHALLENGING) { + conn->state = RXRPC_CONN_SERVER; + for (loop = 0; loop < RXRPC_MAXCALLS; loop++) + rxrpc_call_is_secure(conn->channels[loop]); + } + + spin_unlock(&conn->state_lock); + read_unlock_bh(&conn->lock); + return 0; + + default: + return -EPROTO; + } +} + +/* + * set up security and issue a challenge + */ +static void rxrpc_secure_connection(struct rxrpc_connection *conn) +{ + u32 abort_code; + int ret; + + _enter("{%d}", conn->debug_id); + + ASSERT(conn->security_ix != 0); + + if (!conn->key) { + _debug("set up security"); + ret = rxrpc_init_server_conn_security(conn); + switch (ret) { + case 0: + break; + case -ENOENT: + abort_code = RX_CALL_DEAD; + goto abort; + default: + abort_code = RXKADNOAUTH; + goto abort; + } + } + + ASSERT(conn->security != NULL); + + if (conn->security->issue_challenge(conn) < 0) { + abort_code = RX_CALL_DEAD; + ret = -ENOMEM; + goto abort; + } + + _leave(""); + return; + +abort: + _debug("abort %d, %d", ret, abort_code); + rxrpc_abort_connection(conn, -ret, abort_code); + _leave(" [aborted]"); +} + +/* + * connection-level event processor + */ +void rxrpc_process_connection(struct work_struct *work) +{ + struct rxrpc_connection *conn = + container_of(work, struct rxrpc_connection, processor); + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + u32 abort_code = RX_PROTOCOL_ERROR; + int ret; + + _enter("{%d}", conn->debug_id); + + atomic_inc(&conn->usage); + + if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) { + rxrpc_secure_connection(conn); + rxrpc_put_connection(conn); + } + + /* go through the conn-level event packets, releasing the ref on this + * connection that each one has when we've finished with it */ + while ((skb = skb_dequeue(&conn->rx_queue))) { + sp = rxrpc_skb(skb); + + ret = rxrpc_process_event(conn, skb, &abort_code); + switch (ret) { + case -EPROTO: + case -EKEYEXPIRED: + case -EKEYREJECTED: + goto protocol_error; + case -EAGAIN: + goto requeue_and_leave; + case -ECONNABORTED: + default: + rxrpc_put_connection(conn); + rxrpc_free_skb(skb); + break; + } + } + +out: + rxrpc_put_connection(conn); + _leave(""); + return; + +requeue_and_leave: + skb_queue_head(&conn->rx_queue, skb); + goto out; + +protocol_error: + if (rxrpc_abort_connection(conn, -ret, abort_code) < 0) + goto requeue_and_leave; + rxrpc_put_connection(conn); + rxrpc_free_skb(skb); + _leave(" [EPROTO]"); + goto out; +} + +/* + * put a packet up for transport-level abort + */ +void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) +{ + CHECK_SLAB_OKAY(&local->usage); + + if (!atomic_inc_not_zero(&local->usage)) { + printk("resurrected on reject\n"); + BUG(); + } + + skb_queue_tail(&local->reject_queue, skb); + rxrpc_queue_work(&local->rejecter); +} + +/* + * reject packets through the local endpoint + */ +void rxrpc_reject_packets(struct work_struct *work) +{ + union { + struct sockaddr sa; + struct sockaddr_in sin; + } sa; + struct rxrpc_skb_priv *sp; + struct rxrpc_header hdr; + struct rxrpc_local *local; + struct sk_buff *skb; + struct msghdr msg; + struct kvec iov[2]; + size_t size; + __be32 code; + + local = container_of(work, struct rxrpc_local, rejecter); + rxrpc_get_local(local); + + _enter("%d", local->debug_id); + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = &code; + iov[1].iov_len = sizeof(code); + size = sizeof(hdr) + sizeof(code); + + msg.msg_name = &sa; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + memset(&sa, 0, sizeof(sa)); + sa.sa.sa_family = local->srx.transport.family; + switch (sa.sa.sa_family) { + case AF_INET: + msg.msg_namelen = sizeof(sa.sin); + break; + default: + msg.msg_namelen = 0; + break; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.type = RXRPC_PACKET_TYPE_ABORT; + + while ((skb = skb_dequeue(&local->reject_queue))) { + sp = rxrpc_skb(skb); + switch (sa.sa.sa_family) { + case AF_INET: + sa.sin.sin_port = udp_hdr(skb)->source; + sa.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + code = htonl(skb->priority); + + hdr.epoch = sp->hdr.epoch; + hdr.cid = sp->hdr.cid; + hdr.callNumber = sp->hdr.callNumber; + hdr.serviceId = sp->hdr.serviceId; + hdr.flags = sp->hdr.flags; + hdr.flags ^= RXRPC_CLIENT_INITIATED; + hdr.flags &= RXRPC_CLIENT_INITIATED; + + kernel_sendmsg(local->socket, &msg, iov, 2, size); + break; + + default: + break; + } + + rxrpc_free_skb(skb); + rxrpc_put_local(local); + } + + rxrpc_put_local(local); + _leave(""); +} diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c new file mode 100644 index 0000000..2c27df1 --- /dev/null +++ b/net/rxrpc/ar-error.c @@ -0,0 +1,253 @@ +/* Error message handling (ICMP) + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * handle an error received on the local endpoint + */ +void rxrpc_UDP_error_report(struct sock *sk) +{ + struct sock_exterr_skb *serr; + struct rxrpc_transport *trans; + struct rxrpc_local *local = sk->sk_user_data; + struct rxrpc_peer *peer; + struct sk_buff *skb; + __be32 addr; + __be16 port; + + _enter("%p{%d}", sk, local->debug_id); + + skb = skb_dequeue(&sk->sk_error_queue); + if (!skb) { + _leave("UDP socket errqueue empty"); + return; + } + + rxrpc_new_skb(skb); + + serr = SKB_EXT_ERR(skb); + addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset); + port = serr->port; + + _net("Rx UDP Error from "NIPQUAD_FMT":%hu", + NIPQUAD(addr), ntohs(port)); + _debug("Msg l:%d d:%d", skb->len, skb->data_len); + + peer = rxrpc_find_peer(local, addr, port); + if (IS_ERR(peer)) { + rxrpc_free_skb(skb); + _leave(" [no peer]"); + return; + } + + trans = rxrpc_find_transport(local, peer); + if (!trans) { + rxrpc_put_peer(peer); + rxrpc_free_skb(skb); + _leave(" [no trans]"); + return; + } + + if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP && + serr->ee.ee_type == ICMP_DEST_UNREACH && + serr->ee.ee_code == ICMP_FRAG_NEEDED + ) { + u32 mtu = serr->ee.ee_info; + + _net("Rx Received ICMP Fragmentation Needed (%d)", mtu); + + /* wind down the local interface MTU */ + if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) { + peer->if_mtu = mtu; + _net("I/F MTU %u", mtu); + } + + /* ip_rt_frag_needed() may have eaten the info */ + if (mtu == 0) + mtu = ntohs(icmp_hdr(skb)->un.frag.mtu); + + if (mtu == 0) { + /* they didn't give us a size, estimate one */ + if (mtu > 1500) { + mtu >>= 1; + if (mtu < 1500) + mtu = 1500; + } else { + mtu -= 100; + if (mtu < peer->hdrsize) + mtu = peer->hdrsize + 4; + } + } + + if (mtu < peer->mtu) { + peer->mtu = mtu; + peer->maxdata = peer->mtu - peer->hdrsize; + _net("Net MTU %u (maxdata %u)", + peer->mtu, peer->maxdata); + } + } + + rxrpc_put_peer(peer); + + /* pass the transport ref to error_handler to release */ + skb_queue_tail(&trans->error_queue, skb); + rxrpc_queue_work(&trans->error_handler); + + /* reset and regenerate socket error */ + spin_lock_bh(&sk->sk_error_queue.lock); + sk->sk_err = 0; + skb = skb_peek(&sk->sk_error_queue); + if (skb) { + sk->sk_err = SKB_EXT_ERR(skb)->ee.ee_errno; + spin_unlock_bh(&sk->sk_error_queue.lock); + sk->sk_error_report(sk); + } else { + spin_unlock_bh(&sk->sk_error_queue.lock); + } + + _leave(""); +} + +/* + * deal with UDP error messages + */ +void rxrpc_UDP_error_handler(struct work_struct *work) +{ + struct sock_extended_err *ee; + struct sock_exterr_skb *serr; + struct rxrpc_transport *trans = + container_of(work, struct rxrpc_transport, error_handler); + struct sk_buff *skb; + int local, err; + + _enter(""); + + skb = skb_dequeue(&trans->error_queue); + if (!skb) + return; + + serr = SKB_EXT_ERR(skb); + ee = &serr->ee; + + _net("Rx Error o=%d t=%d c=%d e=%d", + ee->ee_origin, ee->ee_type, ee->ee_code, ee->ee_errno); + + err = ee->ee_errno; + + switch (ee->ee_origin) { + case SO_EE_ORIGIN_ICMP: + local = 0; + switch (ee->ee_type) { + case ICMP_DEST_UNREACH: + switch (ee->ee_code) { + case ICMP_NET_UNREACH: + _net("Rx Received ICMP Network Unreachable"); + err = ENETUNREACH; + break; + case ICMP_HOST_UNREACH: + _net("Rx Received ICMP Host Unreachable"); + err = EHOSTUNREACH; + break; + case ICMP_PORT_UNREACH: + _net("Rx Received ICMP Port Unreachable"); + err = ECONNREFUSED; + break; + case ICMP_FRAG_NEEDED: + _net("Rx Received ICMP Fragmentation Needed (%d)", + ee->ee_info); + err = 0; /* dealt with elsewhere */ + break; + case ICMP_NET_UNKNOWN: + _net("Rx Received ICMP Unknown Network"); + err = ENETUNREACH; + break; + case ICMP_HOST_UNKNOWN: + _net("Rx Received ICMP Unknown Host"); + err = EHOSTUNREACH; + break; + default: + _net("Rx Received ICMP DestUnreach code=%u", + ee->ee_code); + break; + } + break; + + case ICMP_TIME_EXCEEDED: + _net("Rx Received ICMP TTL Exceeded"); + break; + + default: + _proto("Rx Received ICMP error { type=%u code=%u }", + ee->ee_type, ee->ee_code); + break; + } + break; + + case SO_EE_ORIGIN_LOCAL: + _proto("Rx Received local error { error=%d }", + ee->ee_errno); + local = 1; + break; + + case SO_EE_ORIGIN_NONE: + case SO_EE_ORIGIN_ICMP6: + default: + _proto("Rx Received error report { orig=%u }", + ee->ee_origin); + local = 0; + break; + } + + /* terminate all the affected calls if there's an unrecoverable + * error */ + if (err) { + struct rxrpc_call *call, *_n; + + _debug("ISSUE ERROR %d", err); + + spin_lock_bh(&trans->peer->lock); + trans->peer->net_error = err; + + list_for_each_entry_safe(call, _n, &trans->peer->error_targets, + error_link) { + write_lock(&call->state_lock); + if (call->state != RXRPC_CALL_COMPLETE && + call->state < RXRPC_CALL_NETWORK_ERROR) { + call->state = RXRPC_CALL_NETWORK_ERROR; + set_bit(RXRPC_CALL_RCVD_ERROR, &call->events); + rxrpc_queue_call(call); + } + write_unlock(&call->state_lock); + list_del_init(&call->error_link); + } + + spin_unlock_bh(&trans->peer->lock); + } + + if (!skb_queue_empty(&trans->error_queue)) + rxrpc_queue_work(&trans->error_handler); + + rxrpc_free_skb(skb); + rxrpc_put_transport(trans); + _leave(""); +} diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c new file mode 100644 index 0000000..91b5bbb --- /dev/null +++ b/net/rxrpc/ar-input.c @@ -0,0 +1,797 @@ +/* RxRPC packet reception + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +unsigned long rxrpc_ack_timeout = 1; + +const char *rxrpc_pkts[] = { + "?00", + "DATA", "ACK", "BUSY", "ABORT", "ACKALL", "CHALL", "RESP", "DEBUG", + "?09", "?10", "?11", "?12", "?13", "?14", "?15" +}; + +/* + * queue a packet for recvmsg to pass to userspace + * - the caller must hold a lock on call->lock + * - must not be called with interrupts disabled (sk_filter() disables BH's) + * - eats the packet whether successful or not + * - there must be just one reference to the packet, which the caller passes to + * this function + */ +int rxrpc_queue_rcv_skb(struct rxrpc_call *call, struct sk_buff *skb, + bool force, bool terminal) +{ + struct rxrpc_skb_priv *sp; + struct rxrpc_sock *rx = call->socket; + struct sock *sk; + int skb_len, ret; + + _enter(",,%d,%d", force, terminal); + + ASSERT(!irqs_disabled()); + + sp = rxrpc_skb(skb); + ASSERTCMP(sp->call, ==, call); + + /* if we've already posted the terminal message for a call, then we + * don't post any more */ + if (test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags)) { + _debug("already terminated"); + ASSERTCMP(call->state, >=, RXRPC_CALL_COMPLETE); + skb->destructor = NULL; + sp->call = NULL; + rxrpc_put_call(call); + rxrpc_free_skb(skb); + return 0; + } + + sk = &rx->sk; + + if (!force) { + /* cast skb->rcvbuf to unsigned... It's pointless, but + * reduces number of warnings when compiling with -W + * --ANK */ +// ret = -ENOBUFS; +// if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= +// (unsigned) sk->sk_rcvbuf) +// goto out; + + ret = sk_filter(sk, skb); + if (ret < 0) + goto out; + } + + spin_lock_bh(&sk->sk_receive_queue.lock); + if (!test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags) && + !test_bit(RXRPC_CALL_RELEASED, &call->flags) && + call->socket->sk.sk_state != RXRPC_CLOSE) { + skb->destructor = rxrpc_packet_destructor; + skb->dev = NULL; + skb->sk = sk; + atomic_add(skb->truesize, &sk->sk_rmem_alloc); + + if (terminal) { + _debug("<<<< TERMINAL MESSAGE >>>>"); + set_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags); + } + + /* allow interception by a kernel service */ + if (rx->interceptor) { + rx->interceptor(sk, call->user_call_ID, skb); + spin_unlock_bh(&sk->sk_receive_queue.lock); + } else { + + /* Cache the SKB length before we tack it onto the + * receive queue. Once it is added it no longer + * belongs to us and may be freed by other threads of + * control pulling packets from the queue */ + skb_len = skb->len; + + _net("post skb %p", skb); + __skb_queue_tail(&sk->sk_receive_queue, skb); + spin_unlock_bh(&sk->sk_receive_queue.lock); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, skb_len); + } + skb = NULL; + } else { + spin_unlock_bh(&sk->sk_receive_queue.lock); + } + ret = 0; + +out: + /* release the socket buffer */ + if (skb) { + skb->destructor = NULL; + sp->call = NULL; + rxrpc_put_call(call); + rxrpc_free_skb(skb); + } + + _leave(" = %d", ret); + return ret; +} + +/* + * process a DATA packet, posting the packet to the appropriate queue + * - eats the packet if successful + */ +static int rxrpc_fast_process_data(struct rxrpc_call *call, + struct sk_buff *skb, u32 seq) +{ + struct rxrpc_skb_priv *sp; + bool terminal; + int ret, ackbit, ack; + + _enter("{%u,%u},,{%u}", call->rx_data_post, call->rx_first_oos, seq); + + sp = rxrpc_skb(skb); + ASSERTCMP(sp->call, ==, NULL); + + spin_lock(&call->lock); + + if (call->state > RXRPC_CALL_COMPLETE) + goto discard; + + ASSERTCMP(call->rx_data_expect, >=, call->rx_data_post); + ASSERTCMP(call->rx_data_post, >=, call->rx_data_recv); + ASSERTCMP(call->rx_data_recv, >=, call->rx_data_eaten); + + if (seq < call->rx_data_post) { + _debug("dup #%u [-%u]", seq, call->rx_data_post); + ack = RXRPC_ACK_DUPLICATE; + ret = -ENOBUFS; + goto discard_and_ack; + } + + /* we may already have the packet in the out of sequence queue */ + ackbit = seq - (call->rx_data_eaten + 1); + ASSERTCMP(ackbit, >=, 0); + if (__test_and_set_bit(ackbit, call->ackr_window)) { + _debug("dup oos #%u [%u,%u]", + seq, call->rx_data_eaten, call->rx_data_post); + ack = RXRPC_ACK_DUPLICATE; + goto discard_and_ack; + } + + if (seq >= call->ackr_win_top) { + _debug("exceed #%u [%u]", seq, call->ackr_win_top); + __clear_bit(ackbit, call->ackr_window); + ack = RXRPC_ACK_EXCEEDS_WINDOW; + goto discard_and_ack; + } + + if (seq == call->rx_data_expect) { + clear_bit(RXRPC_CALL_EXPECT_OOS, &call->flags); + call->rx_data_expect++; + } else if (seq > call->rx_data_expect) { + _debug("oos #%u [%u]", seq, call->rx_data_expect); + call->rx_data_expect = seq + 1; + if (test_and_set_bit(RXRPC_CALL_EXPECT_OOS, &call->flags)) { + ack = RXRPC_ACK_OUT_OF_SEQUENCE; + goto enqueue_and_ack; + } + goto enqueue_packet; + } + + if (seq != call->rx_data_post) { + _debug("ahead #%u [%u]", seq, call->rx_data_post); + goto enqueue_packet; + } + + if (test_bit(RXRPC_CALL_RCVD_LAST, &call->flags)) + goto protocol_error; + + /* if the packet need security things doing to it, then it goes down + * the slow path */ + if (call->conn->security) + goto enqueue_packet; + + sp->call = call; + rxrpc_get_call(call); + terminal = ((sp->hdr.flags & RXRPC_LAST_PACKET) && + !(sp->hdr.flags & RXRPC_CLIENT_INITIATED)); + ret = rxrpc_queue_rcv_skb(call, skb, false, terminal); + if (ret < 0) { + if (ret == -ENOMEM || ret == -ENOBUFS) { + __clear_bit(ackbit, call->ackr_window); + ack = RXRPC_ACK_NOSPACE; + goto discard_and_ack; + } + goto out; + } + + skb = NULL; + + _debug("post #%u", seq); + ASSERTCMP(call->rx_data_post, ==, seq); + call->rx_data_post++; + + if (sp->hdr.flags & RXRPC_LAST_PACKET) + set_bit(RXRPC_CALL_RCVD_LAST, &call->flags); + + /* if we've reached an out of sequence packet then we need to drain + * that queue into the socket Rx queue now */ + if (call->rx_data_post == call->rx_first_oos) { + _debug("drain rx oos now"); + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events)) + rxrpc_queue_call(call); + read_unlock(&call->state_lock); + } + + spin_unlock(&call->lock); + atomic_inc(&call->ackr_not_idle); + rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, sp->hdr.serial, false); + _leave(" = 0 [posted]"); + return 0; + +protocol_error: + ret = -EBADMSG; +out: + spin_unlock(&call->lock); + _leave(" = %d", ret); + return ret; + +discard_and_ack: + _debug("discard and ACK packet %p", skb); + __rxrpc_propose_ACK(call, ack, sp->hdr.serial, true); +discard: + spin_unlock(&call->lock); + rxrpc_free_skb(skb); + _leave(" = 0 [discarded]"); + return 0; + +enqueue_and_ack: + __rxrpc_propose_ACK(call, ack, sp->hdr.serial, true); +enqueue_packet: + _net("defer skb %p", skb); + spin_unlock(&call->lock); + skb_queue_tail(&call->rx_queue, skb); + atomic_inc(&call->ackr_not_idle); + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) + rxrpc_queue_call(call); + read_unlock(&call->state_lock); + _leave(" = 0 [queued]"); + return 0; +} + +/* + * assume an implicit ACKALL of the transmission phase of a client socket upon + * reception of the first reply packet + */ +static void rxrpc_assume_implicit_ackall(struct rxrpc_call *call, u32 serial) +{ + write_lock_bh(&call->state_lock); + + switch (call->state) { + case RXRPC_CALL_CLIENT_AWAIT_REPLY: + call->state = RXRPC_CALL_CLIENT_RECV_REPLY; + call->acks_latest = serial; + + _debug("implicit ACKALL %%%u", call->acks_latest); + set_bit(RXRPC_CALL_RCVD_ACKALL, &call->events); + write_unlock_bh(&call->state_lock); + + if (try_to_del_timer_sync(&call->resend_timer) >= 0) { + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + clear_bit(RXRPC_CALL_RESEND, &call->events); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + } + break; + + default: + write_unlock_bh(&call->state_lock); + break; + } +} + +/* + * post an incoming packet to the nominated call to deal with + * - must get rid of the sk_buff, either by freeing it or by queuing it + */ +void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + __be32 _abort_code; + u32 serial, hi_serial, seq, abort_code; + + _enter("%p,%p", call, skb); + + ASSERT(!irqs_disabled()); + +#if 0 // INJECT RX ERROR + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) { + static int skip = 0; + if (++skip == 3) { + printk("DROPPED 3RD PACKET!!!!!!!!!!!!!\n"); + skip = 0; + goto free_packet; + } + } +#endif + + /* track the latest serial number on this connection for ACK packet + * information */ + serial = ntohl(sp->hdr.serial); + hi_serial = atomic_read(&call->conn->hi_serial); + while (serial > hi_serial) + hi_serial = atomic_cmpxchg(&call->conn->hi_serial, hi_serial, + serial); + + /* request ACK generation for any ACK or DATA packet that requests + * it */ + if (sp->hdr.flags & RXRPC_REQUEST_ACK) { + _proto("ACK Requested on %%%u", serial); + rxrpc_propose_ACK(call, RXRPC_ACK_REQUESTED, sp->hdr.serial, + !(sp->hdr.flags & RXRPC_MORE_PACKETS)); + } + + switch (sp->hdr.type) { + case RXRPC_PACKET_TYPE_ABORT: + _debug("abort"); + + if (skb_copy_bits(skb, 0, &_abort_code, + sizeof(_abort_code)) < 0) + goto protocol_error; + + abort_code = ntohl(_abort_code); + _proto("Rx ABORT %%%u { %x }", serial, abort_code); + + write_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_REMOTELY_ABORTED; + call->abort_code = abort_code; + set_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + rxrpc_queue_call(call); + } + goto free_packet_unlock; + + case RXRPC_PACKET_TYPE_BUSY: + _proto("Rx BUSY %%%u", serial); + + if (call->conn->out_clientflag) + goto protocol_error; + + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_CLIENT_SEND_REQUEST: + call->state = RXRPC_CALL_SERVER_BUSY; + set_bit(RXRPC_CALL_RCVD_BUSY, &call->events); + rxrpc_queue_call(call); + case RXRPC_CALL_SERVER_BUSY: + goto free_packet_unlock; + default: + goto protocol_error_locked; + } + + default: + _proto("Rx %s %%%u", rxrpc_pkts[sp->hdr.type], serial); + goto protocol_error; + + case RXRPC_PACKET_TYPE_DATA: + seq = ntohl(sp->hdr.seq); + + _proto("Rx DATA %%%u { #%u }", serial, seq); + + if (seq == 0) + goto protocol_error; + + call->ackr_prev_seq = sp->hdr.seq; + + /* received data implicitly ACKs all of the request packets we + * sent when we're acting as a client */ + if (call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY) + rxrpc_assume_implicit_ackall(call, serial); + + switch (rxrpc_fast_process_data(call, skb, seq)) { + case 0: + skb = NULL; + goto done; + + default: + BUG(); + + /* data packet received beyond the last packet */ + case -EBADMSG: + goto protocol_error; + } + + case RXRPC_PACKET_TYPE_ACK: + /* ACK processing is done in process context */ + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) { + skb_queue_tail(&call->rx_queue, skb); + rxrpc_queue_call(call); + skb = NULL; + } + read_unlock_bh(&call->state_lock); + goto free_packet; + } + +protocol_error: + _debug("protocol error"); + write_lock_bh(&call->state_lock); +protocol_error_locked: + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_PROTOCOL_ERROR; + set_bit(RXRPC_CALL_ABORT, &call->events); + rxrpc_queue_call(call); + } +free_packet_unlock: + write_unlock_bh(&call->state_lock); +free_packet: + rxrpc_free_skb(skb); +done: + _leave(""); +} + +/* + * split up a jumbo data packet + */ +static void rxrpc_process_jumbo_packet(struct rxrpc_call *call, + struct sk_buff *jumbo) +{ + struct rxrpc_jumbo_header jhdr; + struct rxrpc_skb_priv *sp; + struct sk_buff *part; + + _enter(",{%u,%u}", jumbo->data_len, jumbo->len); + + sp = rxrpc_skb(jumbo); + + do { + sp->hdr.flags &= ~RXRPC_JUMBO_PACKET; + + /* make a clone to represent the first subpacket in what's left + * of the jumbo packet */ + part = skb_clone(jumbo, GFP_ATOMIC); + if (!part) { + /* simply ditch the tail in the event of ENOMEM */ + pskb_trim(jumbo, RXRPC_JUMBO_DATALEN); + break; + } + rxrpc_new_skb(part); + + pskb_trim(part, RXRPC_JUMBO_DATALEN); + + if (!pskb_pull(jumbo, RXRPC_JUMBO_DATALEN)) + goto protocol_error; + + if (skb_copy_bits(jumbo, 0, &jhdr, sizeof(jhdr)) < 0) + goto protocol_error; + if (!pskb_pull(jumbo, sizeof(jhdr))) + BUG(); + + sp->hdr.seq = htonl(ntohl(sp->hdr.seq) + 1); + sp->hdr.serial = htonl(ntohl(sp->hdr.serial) + 1); + sp->hdr.flags = jhdr.flags; + sp->hdr._rsvd = jhdr._rsvd; + + _proto("Rx DATA Jumbo %%%u", ntohl(sp->hdr.serial) - 1); + + rxrpc_fast_process_packet(call, part); + part = NULL; + + } while (sp->hdr.flags & RXRPC_JUMBO_PACKET); + + rxrpc_fast_process_packet(call, jumbo); + _leave(""); + return; + +protocol_error: + _debug("protocol error"); + rxrpc_free_skb(part); + rxrpc_free_skb(jumbo); + write_lock_bh(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_PROTOCOL_ERROR; + set_bit(RXRPC_CALL_ABORT, &call->events); + rxrpc_queue_call(call); + } + write_unlock_bh(&call->state_lock); + _leave(""); +} + +/* + * post an incoming packet to the appropriate call/socket to deal with + * - must get rid of the sk_buff, either by freeing it or by queuing it + */ +static void rxrpc_post_packet_to_call(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp; + struct rxrpc_call *call; + struct rb_node *p; + __be32 call_id; + + _enter("%p,%p", conn, skb); + + read_lock_bh(&conn->lock); + + sp = rxrpc_skb(skb); + + /* look at extant calls by channel number first */ + call = conn->channels[ntohl(sp->hdr.cid) & RXRPC_CHANNELMASK]; + if (!call || call->call_id != sp->hdr.callNumber) + goto call_not_extant; + + _debug("extant call [%d]", call->state); + ASSERTCMP(call->conn, ==, conn); + + read_lock(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_LOCALLY_ABORTED: + if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) + rxrpc_queue_call(call); + case RXRPC_CALL_REMOTELY_ABORTED: + case RXRPC_CALL_NETWORK_ERROR: + case RXRPC_CALL_DEAD: + goto free_unlock; + default: + break; + } + + read_unlock(&call->state_lock); + rxrpc_get_call(call); + read_unlock_bh(&conn->lock); + + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && + sp->hdr.flags & RXRPC_JUMBO_PACKET) + rxrpc_process_jumbo_packet(call, skb); + else + rxrpc_fast_process_packet(call, skb); + + rxrpc_put_call(call); + goto done; + +call_not_extant: + /* search the completed calls in case what we're dealing with is + * there */ + _debug("call not extant"); + + call_id = sp->hdr.callNumber; + p = conn->calls.rb_node; + while (p) { + call = rb_entry(p, struct rxrpc_call, conn_node); + + if (call_id < call->call_id) + p = p->rb_left; + else if (call_id > call->call_id) + p = p->rb_right; + else + goto found_completed_call; + } + +dead_call: + /* it's a either a really old call that we no longer remember or its a + * new incoming call */ + read_unlock_bh(&conn->lock); + + if (sp->hdr.flags & RXRPC_CLIENT_INITIATED && + sp->hdr.seq == __constant_cpu_to_be32(1)) { + _debug("incoming call"); + skb_queue_tail(&conn->trans->local->accept_queue, skb); + rxrpc_queue_work(&conn->trans->local->acceptor); + goto done; + } + + _debug("dead call"); + skb->priority = RX_CALL_DEAD; + rxrpc_reject_packet(conn->trans->local, skb); + goto done; + + /* resend last packet of a completed call + * - client calls may have been aborted or ACK'd + * - server calls may have been aborted + */ +found_completed_call: + _debug("completed call"); + + if (atomic_read(&call->usage) == 0) + goto dead_call; + + /* synchronise any state changes */ + read_lock(&call->state_lock); + ASSERTIFCMP(call->state != RXRPC_CALL_CLIENT_FINAL_ACK, + call->state, >=, RXRPC_CALL_COMPLETE); + + if (call->state == RXRPC_CALL_LOCALLY_ABORTED || + call->state == RXRPC_CALL_REMOTELY_ABORTED || + call->state == RXRPC_CALL_DEAD) { + read_unlock(&call->state_lock); + goto dead_call; + } + + if (call->conn->in_clientflag) { + read_unlock(&call->state_lock); + goto dead_call; /* complete server call */ + } + + _debug("final ack again"); + rxrpc_get_call(call); + set_bit(RXRPC_CALL_ACK_FINAL, &call->events); + rxrpc_queue_call(call); + +free_unlock: + read_unlock(&call->state_lock); + read_unlock_bh(&conn->lock); + rxrpc_free_skb(skb); +done: + _leave(""); +} + +/* + * post connection-level events to the connection + * - this includes challenges, responses and some aborts + */ +static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + _enter("%p,%p", conn, skb); + + atomic_inc(&conn->usage); + skb_queue_tail(&conn->rx_queue, skb); + rxrpc_queue_conn(conn); +} + +/* + * handle data received on the local endpoint + * - may be called in interrupt context + */ +void rxrpc_data_ready(struct sock *sk, int count) +{ + struct rxrpc_connection *conn; + struct rxrpc_transport *trans; + struct rxrpc_skb_priv *sp; + struct rxrpc_local *local; + struct rxrpc_peer *peer; + struct sk_buff *skb; + int ret; + + _enter("%p, %d", sk, count); + + ASSERT(!irqs_disabled()); + + read_lock_bh(&rxrpc_local_lock); + local = sk->sk_user_data; + if (local && atomic_read(&local->usage) > 0) + rxrpc_get_local(local); + else + local = NULL; + read_unlock_bh(&rxrpc_local_lock); + if (!local) { + _leave(" [local dead]"); + return; + } + + skb = skb_recv_datagram(sk, 0, 1, &ret); + if (!skb) { + rxrpc_put_local(local); + if (ret == -EAGAIN) + return; + _debug("UDP socket error %d", ret); + return; + } + + rxrpc_new_skb(skb); + + _net("recv skb %p", skb); + + /* we'll probably need to checksum it (didn't call sock_recvmsg) */ + if (skb_checksum_complete(skb)) { + rxrpc_free_skb(skb); + rxrpc_put_local(local); + _leave(" [CSUM failed]"); + return; + } + + /* the socket buffer we have is owned by UDP, with UDP's data all over + * it, but we really want our own */ + skb_orphan(skb); + sp = rxrpc_skb(skb); + memset(sp, 0, sizeof(*sp)); + + _net("Rx UDP packet from %08x:%04hu", + ntohl(ip_hdr(skb)->saddr), ntohs(udp_hdr(skb)->source)); + + /* dig out the RxRPC connection details */ + if (skb_copy_bits(skb, sizeof(struct udphdr), &sp->hdr, + sizeof(sp->hdr)) < 0) + goto bad_message; + if (!pskb_pull(skb, sizeof(struct udphdr) + sizeof(sp->hdr))) + BUG(); + + _net("Rx RxRPC %s ep=%x call=%x:%x", + sp->hdr.flags & RXRPC_CLIENT_INITIATED ? "ToServer" : "ToClient", + ntohl(sp->hdr.epoch), + ntohl(sp->hdr.cid), + ntohl(sp->hdr.callNumber)); + + if (sp->hdr.type == 0 || sp->hdr.type >= RXRPC_N_PACKET_TYPES) { + _proto("Rx Bad Packet Type %u", sp->hdr.type); + goto bad_message; + } + + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && + (sp->hdr.callNumber == 0 || sp->hdr.seq == 0)) + goto bad_message; + + peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr, udp_hdr(skb)->source); + if (IS_ERR(peer)) + goto cant_route_call; + + trans = rxrpc_find_transport(local, peer); + rxrpc_put_peer(peer); + if (!trans) + goto cant_route_call; + + conn = rxrpc_find_connection(trans, &sp->hdr); + rxrpc_put_transport(trans); + if (!conn) + goto cant_route_call; + + _debug("CONN %p {%d}", conn, conn->debug_id); + + if (sp->hdr.callNumber == 0) + rxrpc_post_packet_to_conn(conn, skb); + else + rxrpc_post_packet_to_call(conn, skb); + rxrpc_put_connection(conn); + rxrpc_put_local(local); + return; + +cant_route_call: + _debug("can't route call"); + if (sp->hdr.flags & RXRPC_CLIENT_INITIATED && + sp->hdr.type == RXRPC_PACKET_TYPE_DATA) { + if (sp->hdr.seq == __constant_cpu_to_be32(1)) { + _debug("first packet"); + skb_queue_tail(&local->accept_queue, skb); + rxrpc_queue_work(&local->acceptor); + rxrpc_put_local(local); + _leave(" [incoming]"); + return; + } + skb->priority = RX_INVALID_OPERATION; + } else { + skb->priority = RX_CALL_DEAD; + } + + _debug("reject"); + rxrpc_reject_packet(local, skb); + rxrpc_put_local(local); + _leave(" [no call]"); + return; + +bad_message: + skb->priority = RX_PROTOCOL_ERROR; + rxrpc_reject_packet(local, skb); + rxrpc_put_local(local); + _leave(" [badmsg]"); +} diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h new file mode 100644 index 0000000..58aaf89 --- /dev/null +++ b/net/rxrpc/ar-internal.h @@ -0,0 +1,808 @@ +/* AF_RXRPC internal definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include + +#if 0 +#define CHECK_SLAB_OKAY(X) \ + BUG_ON(atomic_read((X)) >> (sizeof(atomic_t) - 2) == \ + (POISON_FREE << 8 | POISON_FREE)) +#else +#define CHECK_SLAB_OKAY(X) do {} while(0) +#endif + +#define FCRYPT_BSIZE 8 +struct rxrpc_crypt { + union { + u8 x[FCRYPT_BSIZE]; + u32 n[2]; + }; +} __attribute__((aligned(8))); + +#define rxrpc_queue_work(WS) queue_work(rxrpc_workqueue, (WS)) +#define rxrpc_queue_delayed_work(WS,D) \ + queue_delayed_work(rxrpc_workqueue, (WS), (D)) + +#define rxrpc_queue_call(CALL) rxrpc_queue_work(&(CALL)->processor) +#define rxrpc_queue_conn(CONN) rxrpc_queue_work(&(CONN)->processor) + +/* + * sk_state for RxRPC sockets + */ +enum { + RXRPC_UNCONNECTED = 0, + RXRPC_CLIENT_BOUND, /* client local address bound */ + RXRPC_CLIENT_CONNECTED, /* client is connected */ + RXRPC_SERVER_BOUND, /* server local address bound */ + RXRPC_SERVER_LISTENING, /* server listening for connections */ + RXRPC_CLOSE, /* socket is being closed */ +}; + +/* + * RxRPC socket definition + */ +struct rxrpc_sock { + /* WARNING: sk has to be the first member */ + struct sock sk; + rxrpc_interceptor_t interceptor; /* kernel service Rx interceptor function */ + struct rxrpc_local *local; /* local endpoint */ + struct rxrpc_transport *trans; /* transport handler */ + struct rxrpc_conn_bundle *bundle; /* virtual connection bundle */ + struct rxrpc_connection *conn; /* exclusive virtual connection */ + struct list_head listen_link; /* link in the local endpoint's listen list */ + struct list_head secureq; /* calls awaiting connection security clearance */ + struct list_head acceptq; /* calls awaiting acceptance */ + struct key *key; /* security for this socket */ + struct key *securities; /* list of server security descriptors */ + struct rb_root calls; /* outstanding calls on this socket */ + unsigned long flags; +#define RXRPC_SOCK_EXCLUSIVE_CONN 1 /* exclusive connection for a client socket */ + rwlock_t call_lock; /* lock for calls */ + u32 min_sec_level; /* minimum security level */ +#define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT + struct sockaddr_rxrpc srx; /* local address */ + sa_family_t proto; /* protocol created with */ + __be16 service_id; /* service ID of local/remote service */ +}; + +#define rxrpc_sk(__sk) container_of((__sk), struct rxrpc_sock, sk) + +/* + * RxRPC socket buffer private variables + * - max 48 bytes (struct sk_buff::cb) + */ +struct rxrpc_skb_priv { + struct rxrpc_call *call; /* call with which associated */ + unsigned long resend_at; /* time in jiffies at which to resend */ + union { + unsigned offset; /* offset into buffer of next read */ + int remain; /* amount of space remaining for next write */ + u32 error; /* network error code */ + bool need_resend; /* T if needs resending */ + }; + + struct rxrpc_header hdr; /* RxRPC packet header from this packet */ +}; + +#define rxrpc_skb(__skb) ((struct rxrpc_skb_priv *) &(__skb)->cb) + +enum rxrpc_command { + RXRPC_CMD_SEND_DATA, /* send data message */ + RXRPC_CMD_SEND_ABORT, /* request abort generation */ + RXRPC_CMD_ACCEPT, /* [server] accept incoming call */ + RXRPC_CMD_REJECT_BUSY, /* [server] reject a call as busy */ +}; + +/* + * RxRPC security module interface + */ +struct rxrpc_security { + struct module *owner; /* providing module */ + struct list_head link; /* link in master list */ + const char *name; /* name of this service */ + u8 security_index; /* security type provided */ + + /* initialise a connection's security */ + int (*init_connection_security)(struct rxrpc_connection *); + + /* prime a connection's packet security */ + void (*prime_packet_security)(struct rxrpc_connection *); + + /* impose security on a packet */ + int (*secure_packet)(const struct rxrpc_call *, + struct sk_buff *, + size_t, + void *); + + /* verify the security on a received packet */ + int (*verify_packet)(const struct rxrpc_call *, struct sk_buff *, + u32 *); + + /* issue a challenge */ + int (*issue_challenge)(struct rxrpc_connection *); + + /* respond to a challenge */ + int (*respond_to_challenge)(struct rxrpc_connection *, + struct sk_buff *, + u32 *); + + /* verify a response */ + int (*verify_response)(struct rxrpc_connection *, + struct sk_buff *, + u32 *); + + /* clear connection security */ + void (*clear)(struct rxrpc_connection *); +}; + +/* + * RxRPC local transport endpoint definition + * - matched by local port, address and protocol type + */ +struct rxrpc_local { + struct socket *socket; /* my UDP socket */ + struct work_struct destroyer; /* endpoint destroyer */ + struct work_struct acceptor; /* incoming call processor */ + struct work_struct rejecter; /* packet reject writer */ + struct list_head services; /* services listening on this endpoint */ + struct list_head link; /* link in endpoint list */ + struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ + struct sk_buff_head accept_queue; /* incoming calls awaiting acceptance */ + struct sk_buff_head reject_queue; /* packets awaiting rejection */ + spinlock_t lock; /* access lock */ + rwlock_t services_lock; /* lock for services list */ + atomic_t usage; + int debug_id; /* debug ID for printks */ + volatile char error_rcvd; /* T if received ICMP error outstanding */ + struct sockaddr_rxrpc srx; /* local address */ +}; + +/* + * RxRPC remote transport endpoint definition + * - matched by remote port, address and protocol type + * - holds the connection ID counter for connections between the two endpoints + */ +struct rxrpc_peer { + struct work_struct destroyer; /* peer destroyer */ + struct list_head link; /* link in master peer list */ + struct list_head error_targets; /* targets for net error distribution */ + spinlock_t lock; /* access lock */ + atomic_t usage; + unsigned if_mtu; /* interface MTU for this peer */ + unsigned mtu; /* network MTU for this peer */ + unsigned maxdata; /* data size (MTU - hdrsize) */ + unsigned short hdrsize; /* header size (IP + UDP + RxRPC) */ + int debug_id; /* debug ID for printks */ + int net_error; /* network error distributed */ + struct sockaddr_rxrpc srx; /* remote address */ + + /* calculated RTT cache */ +#define RXRPC_RTT_CACHE_SIZE 32 + suseconds_t rtt; /* current RTT estimate (in uS) */ + unsigned rtt_point; /* next entry at which to insert */ + unsigned rtt_usage; /* amount of cache actually used */ + suseconds_t rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */ +}; + +/* + * RxRPC point-to-point transport / connection manager definition + * - handles a bundle of connections between two endpoints + * - matched by { local, peer } + */ +struct rxrpc_transport { + struct rxrpc_local *local; /* local transport endpoint */ + struct rxrpc_peer *peer; /* remote transport endpoint */ + struct work_struct error_handler; /* network error distributor */ + struct rb_root bundles; /* client connection bundles on this transport */ + struct rb_root client_conns; /* client connections on this transport */ + struct rb_root server_conns; /* server connections on this transport */ + struct list_head link; /* link in master session list */ + struct sk_buff_head error_queue; /* error packets awaiting processing */ + time_t put_time; /* time at which to reap */ + spinlock_t client_lock; /* client connection allocation lock */ + rwlock_t conn_lock; /* lock for active/dead connections */ + atomic_t usage; + int debug_id; /* debug ID for printks */ + unsigned int conn_idcounter; /* connection ID counter (client) */ +}; + +/* + * RxRPC client connection bundle + * - matched by { transport, service_id, key } + */ +struct rxrpc_conn_bundle { + struct rb_node node; /* node in transport's lookup tree */ + struct list_head unused_conns; /* unused connections in this bundle */ + struct list_head avail_conns; /* available connections in this bundle */ + struct list_head busy_conns; /* busy connections in this bundle */ + struct key *key; /* security for this bundle */ + wait_queue_head_t chanwait; /* wait for channel to become available */ + atomic_t usage; + int debug_id; /* debug ID for printks */ + unsigned short num_conns; /* number of connections in this bundle */ + __be16 service_id; /* service ID */ + uint8_t security_ix; /* security type */ +}; + +/* + * RxRPC connection definition + * - matched by { transport, service_id, conn_id, direction, key } + * - each connection can only handle four simultaneous calls + */ +struct rxrpc_connection { + struct rxrpc_transport *trans; /* transport session */ + struct rxrpc_conn_bundle *bundle; /* connection bundle (client) */ + struct work_struct processor; /* connection event processor */ + struct rb_node node; /* node in transport's lookup tree */ + struct list_head link; /* link in master connection list */ + struct list_head bundle_link; /* link in bundle */ + struct rb_root calls; /* calls on this connection */ + struct sk_buff_head rx_queue; /* received conn-level packets */ + struct rxrpc_call *channels[RXRPC_MAXCALLS]; /* channels (active calls) */ + struct rxrpc_security *security; /* applied security module */ + struct key *key; /* security for this connection (client) */ + struct key *server_key; /* security for this service */ + struct crypto_blkcipher *cipher; /* encryption handle */ + struct rxrpc_crypt csum_iv; /* packet checksum base */ + unsigned long events; +#define RXRPC_CONN_CHALLENGE 0 /* send challenge packet */ + time_t put_time; /* time at which to reap */ + rwlock_t lock; /* access lock */ + spinlock_t state_lock; /* state-change lock */ + atomic_t usage; + u32 real_conn_id; /* connection ID (host-endian) */ + enum { /* current state of connection */ + RXRPC_CONN_UNUSED, /* - connection not yet attempted */ + RXRPC_CONN_CLIENT, /* - client connection */ + RXRPC_CONN_SERVER_UNSECURED, /* - server unsecured connection */ + RXRPC_CONN_SERVER_CHALLENGING, /* - server challenging for security */ + RXRPC_CONN_SERVER, /* - server secured connection */ + RXRPC_CONN_REMOTELY_ABORTED, /* - conn aborted by peer */ + RXRPC_CONN_LOCALLY_ABORTED, /* - conn aborted locally */ + RXRPC_CONN_NETWORK_ERROR, /* - conn terminated by network error */ + } state; + int error; /* error code for local abort */ + int debug_id; /* debug ID for printks */ + unsigned call_counter; /* call ID counter */ + atomic_t serial; /* packet serial number counter */ + atomic_t hi_serial; /* highest serial number received */ + u8 avail_calls; /* number of calls available */ + u8 size_align; /* data size alignment (for security) */ + u8 header_size; /* rxrpc + security header size */ + u8 security_size; /* security header size */ + u32 security_level; /* security level negotiated */ + u32 security_nonce; /* response re-use preventer */ + + /* the following are all in net order */ + __be32 epoch; /* epoch of this connection */ + __be32 cid; /* connection ID */ + __be16 service_id; /* service ID */ + u8 security_ix; /* security type */ + u8 in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */ + u8 out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */ +}; + +/* + * RxRPC call definition + * - matched by { connection, call_id } + */ +struct rxrpc_call { + struct rxrpc_connection *conn; /* connection carrying call */ + struct rxrpc_sock *socket; /* socket responsible */ + struct timer_list lifetimer; /* lifetime remaining on call */ + struct timer_list deadspan; /* reap timer for re-ACK'ing, etc */ + struct timer_list ack_timer; /* ACK generation timer */ + struct timer_list resend_timer; /* Tx resend timer */ + struct work_struct destroyer; /* call destroyer */ + struct work_struct processor; /* packet processor and ACK generator */ + struct list_head link; /* link in master call list */ + struct list_head error_link; /* link in error distribution list */ + struct list_head accept_link; /* calls awaiting acceptance */ + struct rb_node sock_node; /* node in socket call tree */ + struct rb_node conn_node; /* node in connection call tree */ + struct sk_buff_head rx_queue; /* received packets */ + struct sk_buff_head rx_oos_queue; /* packets received out of sequence */ + struct sk_buff *tx_pending; /* Tx socket buffer being filled */ + wait_queue_head_t tx_waitq; /* wait for Tx window space to become available */ + unsigned long user_call_ID; /* user-defined call ID */ + unsigned long creation_jif; /* time of call creation */ + unsigned long flags; +#define RXRPC_CALL_RELEASED 0 /* call has been released - no more message to userspace */ +#define RXRPC_CALL_TERMINAL_MSG 1 /* call has given the socket its final message */ +#define RXRPC_CALL_RCVD_LAST 2 /* all packets received */ +#define RXRPC_CALL_RUN_RTIMER 3 /* Tx resend timer started */ +#define RXRPC_CALL_TX_SOFT_ACK 4 /* sent some soft ACKs */ +#define RXRPC_CALL_PROC_BUSY 5 /* the processor is busy */ +#define RXRPC_CALL_INIT_ACCEPT 6 /* acceptance was initiated */ +#define RXRPC_CALL_HAS_USERID 7 /* has a user ID attached */ +#define RXRPC_CALL_EXPECT_OOS 8 /* expect out of sequence packets */ + unsigned long events; +#define RXRPC_CALL_RCVD_ACKALL 0 /* ACKALL or reply received */ +#define RXRPC_CALL_RCVD_BUSY 1 /* busy packet received */ +#define RXRPC_CALL_RCVD_ABORT 2 /* abort packet received */ +#define RXRPC_CALL_RCVD_ERROR 3 /* network error received */ +#define RXRPC_CALL_ACK_FINAL 4 /* need to generate final ACK (and release call) */ +#define RXRPC_CALL_ACK 5 /* need to generate ACK */ +#define RXRPC_CALL_REJECT_BUSY 6 /* need to generate busy message */ +#define RXRPC_CALL_ABORT 7 /* need to generate abort */ +#define RXRPC_CALL_CONN_ABORT 8 /* local connection abort generated */ +#define RXRPC_CALL_RESEND_TIMER 9 /* Tx resend timer expired */ +#define RXRPC_CALL_RESEND 10 /* Tx resend required */ +#define RXRPC_CALL_DRAIN_RX_OOS 11 /* drain the Rx out of sequence queue */ +#define RXRPC_CALL_LIFE_TIMER 12 /* call's lifetimer ran out */ +#define RXRPC_CALL_ACCEPTED 13 /* incoming call accepted by userspace app */ +#define RXRPC_CALL_SECURED 14 /* incoming call's connection is now secure */ +#define RXRPC_CALL_POST_ACCEPT 15 /* need to post an "accept?" message to the app */ +#define RXRPC_CALL_RELEASE 16 /* need to release the call's resources */ + + spinlock_t lock; + rwlock_t state_lock; /* lock for state transition */ + atomic_t usage; + atomic_t sequence; /* Tx data packet sequence counter */ + u32 abort_code; /* local/remote abort code */ + enum { /* current state of call */ + RXRPC_CALL_CLIENT_SEND_REQUEST, /* - client sending request phase */ + RXRPC_CALL_CLIENT_AWAIT_REPLY, /* - client awaiting reply */ + RXRPC_CALL_CLIENT_RECV_REPLY, /* - client receiving reply phase */ + RXRPC_CALL_CLIENT_FINAL_ACK, /* - client sending final ACK phase */ + RXRPC_CALL_SERVER_SECURING, /* - server securing request connection */ + RXRPC_CALL_SERVER_ACCEPTING, /* - server accepting request */ + RXRPC_CALL_SERVER_RECV_REQUEST, /* - server receiving request */ + RXRPC_CALL_SERVER_ACK_REQUEST, /* - server pending ACK of request */ + RXRPC_CALL_SERVER_SEND_REPLY, /* - server sending reply */ + RXRPC_CALL_SERVER_AWAIT_ACK, /* - server awaiting final ACK */ + RXRPC_CALL_COMPLETE, /* - call completed */ + RXRPC_CALL_SERVER_BUSY, /* - call rejected by busy server */ + RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */ + RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */ + RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */ + RXRPC_CALL_DEAD, /* - call is dead */ + } state; + int debug_id; /* debug ID for printks */ + u8 channel; /* connection channel occupied by this call */ + + /* transmission-phase ACK management */ + uint8_t acks_head; /* offset into window of first entry */ + uint8_t acks_tail; /* offset into window of last entry */ + uint8_t acks_winsz; /* size of un-ACK'd window */ + uint8_t acks_unacked; /* lowest unacked packet in last ACK received */ + int acks_latest; /* serial number of latest ACK received */ + rxrpc_seq_t acks_hard; /* highest definitively ACK'd msg seq */ + unsigned long *acks_window; /* sent packet window + * - elements are pointers with LSB set if ACK'd + */ + + /* receive-phase ACK management */ + rxrpc_seq_t rx_data_expect; /* next data seq ID expected to be received */ + rxrpc_seq_t rx_data_post; /* next data seq ID expected to be posted */ + rxrpc_seq_t rx_data_recv; /* last data seq ID encountered by recvmsg */ + rxrpc_seq_t rx_data_eaten; /* last data seq ID consumed by recvmsg */ + rxrpc_seq_t rx_first_oos; /* first packet in rx_oos_queue (or 0) */ + rxrpc_seq_t ackr_win_top; /* top of ACK window (rx_data_eaten is bottom) */ + rxrpc_seq_net_t ackr_prev_seq; /* previous sequence number received */ + uint8_t ackr_reason; /* reason to ACK */ + __be32 ackr_serial; /* serial of packet being ACK'd */ + atomic_t ackr_not_idle; /* number of packets in Rx queue */ + + /* received packet records, 1 bit per record */ +#define RXRPC_ACKR_WINDOW_ASZ DIV_ROUND_UP(RXRPC_MAXACKS, BITS_PER_LONG) + unsigned long ackr_window[RXRPC_ACKR_WINDOW_ASZ + 1]; + + /* the following should all be in net order */ + __be32 cid; /* connection ID + channel index */ + __be32 call_id; /* call ID on connection */ +}; + +/* + * RxRPC key for Kerberos (type-2 security) + */ +struct rxkad_key { + u16 security_index; /* RxRPC header security index */ + u16 ticket_len; /* length of ticket[] */ + u32 expiry; /* time at which expires */ + u32 kvno; /* key version number */ + u8 session_key[8]; /* DES session key */ + u8 ticket[0]; /* the encrypted ticket */ +}; + +struct rxrpc_key_payload { + struct rxkad_key k; +}; + +/* + * locally abort an RxRPC call + */ +static inline void rxrpc_abort_call(struct rxrpc_call *call, u32 abort_code) +{ + write_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) { + call->abort_code = abort_code; + call->state = RXRPC_CALL_LOCALLY_ABORTED; + set_bit(RXRPC_CALL_ABORT, &call->events); + } + write_unlock_bh(&call->state_lock); +} + +/* + * af_rxrpc.c + */ +extern atomic_t rxrpc_n_skbs; +extern __be32 rxrpc_epoch; +extern atomic_t rxrpc_debug_id; +extern struct workqueue_struct *rxrpc_workqueue; + +/* + * ar-accept.c + */ +extern void rxrpc_accept_incoming_calls(struct work_struct *); +extern struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *, + unsigned long); +extern int rxrpc_reject_call(struct rxrpc_sock *); + +/* + * ar-ack.c + */ +extern void __rxrpc_propose_ACK(struct rxrpc_call *, uint8_t, __be32, bool); +extern void rxrpc_propose_ACK(struct rxrpc_call *, uint8_t, __be32, bool); +extern void rxrpc_process_call(struct work_struct *); + +/* + * ar-call.c + */ +extern struct kmem_cache *rxrpc_call_jar; +extern struct list_head rxrpc_calls; +extern rwlock_t rxrpc_call_lock; + +extern struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *, + struct rxrpc_transport *, + struct rxrpc_conn_bundle *, + unsigned long, int, gfp_t); +extern struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *, + struct rxrpc_connection *, + struct rxrpc_header *, gfp_t); +extern struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *, + unsigned long); +extern void rxrpc_release_call(struct rxrpc_call *); +extern void rxrpc_release_calls_on_socket(struct rxrpc_sock *); +extern void __rxrpc_put_call(struct rxrpc_call *); +extern void __exit rxrpc_destroy_all_calls(void); + +/* + * ar-connection.c + */ +extern struct list_head rxrpc_connections; +extern rwlock_t rxrpc_connection_lock; + +extern struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *, + struct rxrpc_transport *, + struct key *, + __be16, gfp_t); +extern void rxrpc_put_bundle(struct rxrpc_transport *, + struct rxrpc_conn_bundle *); +extern int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *, + struct rxrpc_conn_bundle *, struct rxrpc_call *, + gfp_t); +extern void rxrpc_put_connection(struct rxrpc_connection *); +extern void __exit rxrpc_destroy_all_connections(void); +extern struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *, + struct rxrpc_header *); +extern struct rxrpc_connection * +rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *, + gfp_t); + +/* + * ar-connevent.c + */ +extern void rxrpc_process_connection(struct work_struct *); +extern void rxrpc_reject_packet(struct rxrpc_local *, struct sk_buff *); +extern void rxrpc_reject_packets(struct work_struct *); + +/* + * ar-error.c + */ +extern void rxrpc_UDP_error_report(struct sock *); +extern void rxrpc_UDP_error_handler(struct work_struct *); + +/* + * ar-input.c + */ +extern unsigned long rxrpc_ack_timeout; +extern const char *rxrpc_pkts[]; + +extern void rxrpc_data_ready(struct sock *, int); +extern int rxrpc_queue_rcv_skb(struct rxrpc_call *, struct sk_buff *, bool, + bool); +extern void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *); + +/* + * ar-local.c + */ +extern rwlock_t rxrpc_local_lock; +extern struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *); +extern void rxrpc_put_local(struct rxrpc_local *); +extern void __exit rxrpc_destroy_all_locals(void); + +/* + * ar-key.c + */ +extern struct key_type key_type_rxrpc; +extern struct key_type key_type_rxrpc_s; + +extern int rxrpc_request_key(struct rxrpc_sock *, char __user *, int); +extern int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int); +extern int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, + time_t, u32); + +/* + * ar-output.c + */ +extern int rxrpc_resend_timeout; + +extern int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *); +extern int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *, + struct rxrpc_transport *, struct msghdr *, + size_t); +extern int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *, + struct msghdr *, size_t); + +/* + * ar-peer.c + */ +extern struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t); +extern void rxrpc_put_peer(struct rxrpc_peer *); +extern struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, + __be32, __be16); +extern void __exit rxrpc_destroy_all_peers(void); + +/* + * ar-proc.c + */ +extern const char *rxrpc_call_states[]; +extern struct file_operations rxrpc_call_seq_fops; +extern struct file_operations rxrpc_connection_seq_fops; + +/* + * ar-recvmsg.c + */ +extern void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *); +extern int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *, + size_t, int); + +/* + * ar-security.c + */ +extern int rxrpc_register_security(struct rxrpc_security *); +extern void rxrpc_unregister_security(struct rxrpc_security *); +extern int rxrpc_init_client_conn_security(struct rxrpc_connection *); +extern int rxrpc_init_server_conn_security(struct rxrpc_connection *); +extern int rxrpc_secure_packet(const struct rxrpc_call *, struct sk_buff *, + size_t, void *); +extern int rxrpc_verify_packet(const struct rxrpc_call *, struct sk_buff *, + u32 *); +extern void rxrpc_clear_conn_security(struct rxrpc_connection *); + +/* + * ar-skbuff.c + */ +extern void rxrpc_packet_destructor(struct sk_buff *); + +/* + * ar-transport.c + */ +extern struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *, + struct rxrpc_peer *, + gfp_t); +extern void rxrpc_put_transport(struct rxrpc_transport *); +extern void __exit rxrpc_destroy_all_transports(void); +extern struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *, + struct rxrpc_peer *); + +/* + * debug tracing + */ +extern unsigned rxrpc_debug; + +#define dbgprintk(FMT,...) \ + printk("[%x%-6.6s] "FMT"\n", smp_processor_id(), current->comm ,##__VA_ARGS__) + +/* make sure we maintain the format strings, even when debugging is disabled */ +static inline __attribute__((format(printf,1,2))) +void _dbprintk(const char *fmt, ...) +{ +} + +#define kenter(FMT,...) dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__) +#define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__) +#define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__) +#define kproto(FMT,...) dbgprintk("### "FMT ,##__VA_ARGS__) +#define knet(FMT,...) dbgprintk("@@@ "FMT ,##__VA_ARGS__) + + +#if defined(__KDEBUG) +#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) +#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) +#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) +#define _proto(FMT,...) kproto(FMT,##__VA_ARGS__) +#define _net(FMT,...) knet(FMT,##__VA_ARGS__) + +#elif defined(CONFIG_AF_RXRPC_DEBUG) +#define RXRPC_DEBUG_KENTER 0x01 +#define RXRPC_DEBUG_KLEAVE 0x02 +#define RXRPC_DEBUG_KDEBUG 0x04 +#define RXRPC_DEBUG_KPROTO 0x08 +#define RXRPC_DEBUG_KNET 0x10 + +#define _enter(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KENTER)) \ + kenter(FMT,##__VA_ARGS__); \ +} while (0) + +#define _leave(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KLEAVE)) \ + kleave(FMT,##__VA_ARGS__); \ +} while (0) + +#define _debug(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KDEBUG)) \ + kdebug(FMT,##__VA_ARGS__); \ +} while (0) + +#define _proto(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KPROTO)) \ + kproto(FMT,##__VA_ARGS__); \ +} while (0) + +#define _net(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KNET)) \ + knet(FMT,##__VA_ARGS__); \ +} while (0) + +#else +#define _enter(FMT,...) _dbprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__) +#define _leave(FMT,...) _dbprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__) +#define _debug(FMT,...) _dbprintk(" "FMT ,##__VA_ARGS__) +#define _proto(FMT,...) _dbprintk("### "FMT ,##__VA_ARGS__) +#define _net(FMT,...) _dbprintk("@@@ "FMT ,##__VA_ARGS__) +#endif + +/* + * debug assertion checking + */ +#if 1 // defined(__KDEBUGALL) + +#define ASSERT(X) \ +do { \ + if (unlikely(!(X))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ + if (unlikely(!((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + printk(KERN_ERR "%lu " #OP " %lu is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTIF(C, X) \ +do { \ + if (unlikely((C) && !(X))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ + if (unlikely((C) && !((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + printk(KERN_ERR "%lu " #OP " %lu is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +#else + +#define ASSERT(X) \ +do { \ +} while(0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ +} while(0) + +#define ASSERTIF(C, X) \ +do { \ +} while(0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ +} while(0) + +#endif /* __KDEBUGALL */ + +/* + * socket buffer accounting / leak finding + */ +static inline void __rxrpc_new_skb(struct sk_buff *skb, const char *fn) +{ + //_net("new skb %p %s [%d]", skb, fn, atomic_read(&rxrpc_n_skbs)); + //atomic_inc(&rxrpc_n_skbs); +} + +#define rxrpc_new_skb(skb) __rxrpc_new_skb((skb), __func__) + +static inline void __rxrpc_kill_skb(struct sk_buff *skb, const char *fn) +{ + //_net("kill skb %p %s [%d]", skb, fn, atomic_read(&rxrpc_n_skbs)); + //atomic_dec(&rxrpc_n_skbs); +} + +#define rxrpc_kill_skb(skb) __rxrpc_kill_skb((skb), __func__) + +static inline void __rxrpc_free_skb(struct sk_buff *skb, const char *fn) +{ + if (skb) { + CHECK_SLAB_OKAY(&skb->users); + //_net("free skb %p %s [%d]", + // skb, fn, atomic_read(&rxrpc_n_skbs)); + //atomic_dec(&rxrpc_n_skbs); + kfree_skb(skb); + } +} + +#define rxrpc_free_skb(skb) __rxrpc_free_skb((skb), __func__) + +static inline void rxrpc_purge_queue(struct sk_buff_head *list) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue((list))) != NULL) + rxrpc_free_skb(skb); +} + +static inline void __rxrpc_get_local(struct rxrpc_local *local, const char *f) +{ + CHECK_SLAB_OKAY(&local->usage); + if (atomic_inc_return(&local->usage) == 1) + printk("resurrected (%s)\n", f); +} + +#define rxrpc_get_local(LOCAL) __rxrpc_get_local((LOCAL), __func__) + +#define rxrpc_get_call(CALL) \ +do { \ + CHECK_SLAB_OKAY(&(CALL)->usage); \ + if (atomic_inc_return(&(CALL)->usage) == 1) \ + BUG(); \ +} while(0) + +#define rxrpc_put_call(CALL) \ +do { \ + __rxrpc_put_call(CALL); \ +} while(0) diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c new file mode 100644 index 0000000..7e049ff --- /dev/null +++ b/net/rxrpc/ar-key.c @@ -0,0 +1,334 @@ +/* RxRPC key management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + * + * RxRPC keys should have a description of describing their purpose: + * "afs@CAMBRIDGE.REDHAT.COM> + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static int rxrpc_instantiate(struct key *, const void *, size_t); +static int rxrpc_instantiate_s(struct key *, const void *, size_t); +static void rxrpc_destroy(struct key *); +static void rxrpc_destroy_s(struct key *); +static void rxrpc_describe(const struct key *, struct seq_file *); + +/* + * rxrpc defined keys take an arbitrary string as the description and an + * arbitrary blob of data as the payload + */ +struct key_type key_type_rxrpc = { + .name = "rxrpc", + .instantiate = rxrpc_instantiate, + .match = user_match, + .destroy = rxrpc_destroy, + .describe = rxrpc_describe, +}; + +EXPORT_SYMBOL(key_type_rxrpc); + +/* + * rxrpc server defined keys take ":" as the + * description and an 8-byte decryption key as the payload + */ +struct key_type key_type_rxrpc_s = { + .name = "rxrpc_s", + .instantiate = rxrpc_instantiate_s, + .match = user_match, + .destroy = rxrpc_destroy_s, + .describe = rxrpc_describe, +}; + +/* + * instantiate an rxrpc defined key + * data should be of the form: + * OFFSET LEN CONTENT + * 0 4 key interface version number + * 4 2 security index (type) + * 6 2 ticket length + * 8 4 key expiry time (time_t) + * 12 4 kvno + * 16 8 session key + * 24 [len] ticket + * + * if no data is provided, then a no-security key is made + */ +static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) +{ + const struct rxkad_key *tsec; + struct rxrpc_key_payload *upayload; + size_t plen; + u32 kver; + int ret; + + _enter("{%x},,%zu", key_serial(key), datalen); + + /* handle a no-security key */ + if (!data && datalen == 0) + return 0; + + /* get the key interface version number */ + ret = -EINVAL; + if (datalen <= 4 || !data) + goto error; + memcpy(&kver, data, sizeof(kver)); + data += sizeof(kver); + datalen -= sizeof(kver); + + _debug("KEY I/F VERSION: %u", kver); + + ret = -EKEYREJECTED; + if (kver != 1) + goto error; + + /* deal with a version 1 key */ + ret = -EINVAL; + if (datalen < sizeof(*tsec)) + goto error; + + tsec = data; + if (datalen != sizeof(*tsec) + tsec->ticket_len) + goto error; + + _debug("SCIX: %u", tsec->security_index); + _debug("TLEN: %u", tsec->ticket_len); + _debug("EXPY: %x", tsec->expiry); + _debug("KVNO: %u", tsec->kvno); + _debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x", + tsec->session_key[0], tsec->session_key[1], + tsec->session_key[2], tsec->session_key[3], + tsec->session_key[4], tsec->session_key[5], + tsec->session_key[6], tsec->session_key[7]); + if (tsec->ticket_len >= 8) + _debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x", + tsec->ticket[0], tsec->ticket[1], + tsec->ticket[2], tsec->ticket[3], + tsec->ticket[4], tsec->ticket[5], + tsec->ticket[6], tsec->ticket[7]); + + ret = -EPROTONOSUPPORT; + if (tsec->security_index != 2) + goto error; + + key->type_data.x[0] = tsec->security_index; + + plen = sizeof(*upayload) + tsec->ticket_len; + ret = key_payload_reserve(key, plen); + if (ret < 0) + goto error; + + ret = -ENOMEM; + upayload = kmalloc(plen, GFP_KERNEL); + if (!upayload) + goto error; + + /* attach the data */ + memcpy(&upayload->k, tsec, sizeof(*tsec)); + memcpy(&upayload->k.ticket, (void *)tsec + sizeof(*tsec), + tsec->ticket_len); + key->payload.data = upayload; + key->expiry = tsec->expiry; + ret = 0; + +error: + return ret; +} + +/* + * instantiate a server secret key + * data should be a pointer to the 8-byte secret key + */ +static int rxrpc_instantiate_s(struct key *key, const void *data, + size_t datalen) +{ + struct crypto_blkcipher *ci; + + _enter("{%x},,%zu", key_serial(key), datalen); + + if (datalen != 8) + return -EINVAL; + + memcpy(&key->type_data, data, 8); + + ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(ci)) { + _leave(" = %ld", PTR_ERR(ci)); + return PTR_ERR(ci); + } + + if (crypto_blkcipher_setkey(ci, data, 8) < 0) + BUG(); + + key->payload.data = ci; + _leave(" = 0"); + return 0; +} + +/* + * dispose of the data dangling from the corpse of a rxrpc key + */ +static void rxrpc_destroy(struct key *key) +{ + kfree(key->payload.data); +} + +/* + * dispose of the data dangling from the corpse of a rxrpc key + */ +static void rxrpc_destroy_s(struct key *key) +{ + if (key->payload.data) { + crypto_free_blkcipher(key->payload.data); + key->payload.data = NULL; + } +} + +/* + * describe the rxrpc key + */ +static void rxrpc_describe(const struct key *key, struct seq_file *m) +{ + seq_puts(m, key->description); +} + +/* + * grab the security key for a socket + */ +int rxrpc_request_key(struct rxrpc_sock *rx, char __user *optval, int optlen) +{ + struct key *key; + char *description; + + _enter(""); + + if (optlen <= 0 || optlen > PAGE_SIZE - 1) + return -EINVAL; + + description = kmalloc(optlen + 1, GFP_KERNEL); + if (!description) + return -ENOMEM; + + if (copy_from_user(description, optval, optlen)) { + kfree(description); + return -EFAULT; + } + description[optlen] = 0; + + key = request_key(&key_type_rxrpc, description, NULL); + if (IS_ERR(key)) { + kfree(description); + _leave(" = %ld", PTR_ERR(key)); + return PTR_ERR(key); + } + + rx->key = key; + kfree(description); + _leave(" = 0 [key %x]", key->serial); + return 0; +} + +/* + * grab the security keyring for a server socket + */ +int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval, + int optlen) +{ + struct key *key; + char *description; + + _enter(""); + + if (optlen <= 0 || optlen > PAGE_SIZE - 1) + return -EINVAL; + + description = kmalloc(optlen + 1, GFP_KERNEL); + if (!description) + return -ENOMEM; + + if (copy_from_user(description, optval, optlen)) { + kfree(description); + return -EFAULT; + } + description[optlen] = 0; + + key = request_key(&key_type_keyring, description, NULL); + if (IS_ERR(key)) { + kfree(description); + _leave(" = %ld", PTR_ERR(key)); + return PTR_ERR(key); + } + + rx->securities = key; + kfree(description); + _leave(" = 0 [key %x]", key->serial); + return 0; +} + +/* + * generate a server data key + */ +int rxrpc_get_server_data_key(struct rxrpc_connection *conn, + const void *session_key, + time_t expiry, + u32 kvno) +{ + struct key *key; + int ret; + + struct { + u32 kver; + struct rxkad_key tsec; + } data; + + _enter(""); + + key = key_alloc(&key_type_rxrpc, "x", 0, 0, current, 0, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(key)) { + _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); + return -ENOMEM; + } + + _debug("key %d", key_serial(key)); + + data.kver = 1; + data.tsec.security_index = 2; + data.tsec.ticket_len = 0; + data.tsec.expiry = expiry; + data.tsec.kvno = 0; + + memcpy(&data.tsec.session_key, session_key, + sizeof(data.tsec.session_key)); + + ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL); + if (ret < 0) + goto error; + + conn->key = key; + _leave(" = 0 [%d]", key_serial(key)); + return 0; + +error: + key_revoke(key); + key_put(key); + _leave(" = -ENOMEM [ins %d]", ret); + return -ENOMEM; +} + +EXPORT_SYMBOL(rxrpc_get_server_data_key); diff --git a/net/rxrpc/ar-local.c b/net/rxrpc/ar-local.c new file mode 100644 index 0000000..fe03f71 --- /dev/null +++ b/net/rxrpc/ar-local.c @@ -0,0 +1,309 @@ +/* AF_RXRPC local endpoint management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static LIST_HEAD(rxrpc_locals); +DEFINE_RWLOCK(rxrpc_local_lock); +static DECLARE_RWSEM(rxrpc_local_sem); +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_local_wq); + +static void rxrpc_destroy_local(struct work_struct *work); + +/* + * allocate a new local + */ +static +struct rxrpc_local *rxrpc_alloc_local(struct sockaddr_rxrpc *srx) +{ + struct rxrpc_local *local; + + local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL); + if (local) { + INIT_WORK(&local->destroyer, &rxrpc_destroy_local); + INIT_WORK(&local->acceptor, &rxrpc_accept_incoming_calls); + INIT_WORK(&local->rejecter, &rxrpc_reject_packets); + INIT_LIST_HEAD(&local->services); + INIT_LIST_HEAD(&local->link); + init_rwsem(&local->defrag_sem); + skb_queue_head_init(&local->accept_queue); + skb_queue_head_init(&local->reject_queue); + spin_lock_init(&local->lock); + rwlock_init(&local->services_lock); + atomic_set(&local->usage, 1); + local->debug_id = atomic_inc_return(&rxrpc_debug_id); + memcpy(&local->srx, srx, sizeof(*srx)); + } + + _leave(" = %p", local); + return local; +} + +/* + * create the local socket + * - must be called with rxrpc_local_sem writelocked + */ +static int rxrpc_create_local(struct rxrpc_local *local) +{ + struct sock *sock; + int ret, opt; + + _enter("%p{%d}", local, local->srx.transport_type); + + /* create a socket to represent the local endpoint */ + ret = sock_create_kern(PF_INET, local->srx.transport_type, IPPROTO_UDP, + &local->socket); + if (ret < 0) { + _leave(" = %d [socket]", ret); + return ret; + } + + /* if a local address was supplied then bind it */ + if (local->srx.transport_len > sizeof(sa_family_t)) { + _debug("bind"); + ret = kernel_bind(local->socket, + (struct sockaddr *) &local->srx.transport, + local->srx.transport_len); + if (ret < 0) { + _debug("bind failed"); + goto error; + } + } + + /* we want to receive ICMP errors */ + opt = 1; + ret = kernel_setsockopt(local->socket, SOL_IP, IP_RECVERR, + (char *) &opt, sizeof(opt)); + if (ret < 0) { + _debug("setsockopt failed"); + goto error; + } + + /* we want to set the don't fragment bit */ + opt = IP_PMTUDISC_DO; + ret = kernel_setsockopt(local->socket, SOL_IP, IP_MTU_DISCOVER, + (char *) &opt, sizeof(opt)); + if (ret < 0) { + _debug("setsockopt failed"); + goto error; + } + + write_lock_bh(&rxrpc_local_lock); + list_add(&local->link, &rxrpc_locals); + write_unlock_bh(&rxrpc_local_lock); + + /* set the socket up */ + sock = local->socket->sk; + sock->sk_user_data = local; + sock->sk_data_ready = rxrpc_data_ready; + sock->sk_error_report = rxrpc_UDP_error_report; + _leave(" = 0"); + return 0; + +error: + local->socket->ops->shutdown(local->socket, 2); + local->socket->sk->sk_user_data = NULL; + sock_release(local->socket); + local->socket = NULL; + + _leave(" = %d", ret); + return ret; +} + +/* + * create a new local endpoint using the specified UDP address + */ +struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx) +{ + struct rxrpc_local *local; + int ret; + + _enter("{%d,%u,%u.%u.%u.%u+%hu}", + srx->transport_type, + srx->transport.family, + NIPQUAD(srx->transport.sin.sin_addr), + ntohs(srx->transport.sin.sin_port)); + + down_write(&rxrpc_local_sem); + + /* see if we have a suitable local local endpoint already */ + read_lock_bh(&rxrpc_local_lock); + + list_for_each_entry(local, &rxrpc_locals, link) { + _debug("CMP {%d,%u,%u.%u.%u.%u+%hu}", + local->srx.transport_type, + local->srx.transport.family, + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port)); + + if (local->srx.transport_type != srx->transport_type || + local->srx.transport.family != srx->transport.family) + continue; + + switch (srx->transport.family) { + case AF_INET: + if (local->srx.transport.sin.sin_port != + srx->transport.sin.sin_port) + continue; + if (memcmp(&local->srx.transport.sin.sin_addr, + &srx->transport.sin.sin_addr, + sizeof(struct in_addr)) != 0) + continue; + goto found_local; + + default: + BUG(); + } + } + + read_unlock_bh(&rxrpc_local_lock); + + /* we didn't find one, so we need to create one */ + local = rxrpc_alloc_local(srx); + if (!local) { + up_write(&rxrpc_local_sem); + return ERR_PTR(-ENOMEM); + } + + ret = rxrpc_create_local(local); + if (ret < 0) { + up_write(&rxrpc_local_sem); + kfree(local); + _leave(" = %d", ret); + return ERR_PTR(ret); + } + + up_write(&rxrpc_local_sem); + + _net("LOCAL new %d {%d,%u,%u.%u.%u.%u+%hu}", + local->debug_id, + local->srx.transport_type, + local->srx.transport.family, + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port)); + + _leave(" = %p [new]", local); + return local; + +found_local: + rxrpc_get_local(local); + read_unlock_bh(&rxrpc_local_lock); + up_write(&rxrpc_local_sem); + + _net("LOCAL old %d {%d,%u,%u.%u.%u.%u+%hu}", + local->debug_id, + local->srx.transport_type, + local->srx.transport.family, + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port)); + + _leave(" = %p [reuse]", local); + return local; +} + +/* + * release a local endpoint + */ +void rxrpc_put_local(struct rxrpc_local *local) +{ + _enter("%p{u=%d}", local, atomic_read(&local->usage)); + + ASSERTCMP(atomic_read(&local->usage), >, 0); + + /* to prevent a race, the decrement and the dequeue must be effectively + * atomic */ + write_lock_bh(&rxrpc_local_lock); + if (unlikely(atomic_dec_and_test(&local->usage))) { + _debug("destroy local"); + rxrpc_queue_work(&local->destroyer); + } + write_unlock_bh(&rxrpc_local_lock); + _leave(""); +} + +/* + * destroy a local endpoint + */ +static void rxrpc_destroy_local(struct work_struct *work) +{ + struct rxrpc_local *local = + container_of(work, struct rxrpc_local, destroyer); + + _enter("%p{%d}", local, atomic_read(&local->usage)); + + down_write(&rxrpc_local_sem); + + write_lock_bh(&rxrpc_local_lock); + if (atomic_read(&local->usage) > 0) { + write_unlock_bh(&rxrpc_local_lock); + up_read(&rxrpc_local_sem); + _leave(" [resurrected]"); + return; + } + + list_del(&local->link); + local->socket->sk->sk_user_data = NULL; + write_unlock_bh(&rxrpc_local_lock); + + downgrade_write(&rxrpc_local_sem); + + ASSERT(list_empty(&local->services)); + ASSERT(!work_pending(&local->acceptor)); + ASSERT(!work_pending(&local->rejecter)); + + /* finish cleaning up the local descriptor */ + rxrpc_purge_queue(&local->accept_queue); + rxrpc_purge_queue(&local->reject_queue); + local->socket->ops->shutdown(local->socket, 2); + sock_release(local->socket); + + up_read(&rxrpc_local_sem); + + _net("DESTROY LOCAL %d", local->debug_id); + kfree(local); + + if (list_empty(&rxrpc_locals)) + wake_up_all(&rxrpc_local_wq); + + _leave(""); +} + +/* + * preemptively destroy all local local endpoint rather than waiting for + * them to be destroyed + */ +void __exit rxrpc_destroy_all_locals(void) +{ + DECLARE_WAITQUEUE(myself,current); + + _enter(""); + + /* we simply have to wait for them to go away */ + if (!list_empty(&rxrpc_locals)) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&rxrpc_local_wq, &myself); + + while (!list_empty(&rxrpc_locals)) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&rxrpc_local_wq, &myself); + set_current_state(TASK_RUNNING); + } + + _leave(""); +} diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c new file mode 100644 index 0000000..5cdde4a --- /dev/null +++ b/net/rxrpc/ar-output.c @@ -0,0 +1,734 @@ +/* RxRPC packet transmission + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +int rxrpc_resend_timeout = 4; + +static int rxrpc_send_data(struct kiocb *iocb, + struct rxrpc_sock *rx, + struct rxrpc_call *call, + struct msghdr *msg, size_t len); + +/* + * extract control messages from the sendmsg() control buffer + */ +static int rxrpc_sendmsg_cmsg(struct rxrpc_sock *rx, struct msghdr *msg, + unsigned long *user_call_ID, + enum rxrpc_command *command, + u32 *abort_code, + bool server) +{ + struct cmsghdr *cmsg; + int len; + + *command = RXRPC_CMD_SEND_DATA; + + if (msg->msg_controllen == 0) + return -EINVAL; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (!CMSG_OK(msg, cmsg)) + return -EINVAL; + + len = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); + _debug("CMSG %d, %d, %d", + cmsg->cmsg_level, cmsg->cmsg_type, len); + + if (cmsg->cmsg_level != SOL_RXRPC) + continue; + + switch (cmsg->cmsg_type) { + case RXRPC_USER_CALL_ID: + if (msg->msg_flags & MSG_CMSG_COMPAT) { + if (len != sizeof(u32)) + return -EINVAL; + *user_call_ID = *(u32 *) CMSG_DATA(cmsg); + } else { + if (len != sizeof(unsigned long)) + return -EINVAL; + *user_call_ID = *(unsigned long *) + CMSG_DATA(cmsg); + } + _debug("User Call ID %lx", *user_call_ID); + break; + + case RXRPC_ABORT: + if (*command != RXRPC_CMD_SEND_DATA) + return -EINVAL; + *command = RXRPC_CMD_SEND_ABORT; + if (len != sizeof(*abort_code)) + return -EINVAL; + *abort_code = *(unsigned int *) CMSG_DATA(cmsg); + _debug("Abort %x", *abort_code); + if (*abort_code == 0) + return -EINVAL; + break; + + case RXRPC_ACCEPT: + if (*command != RXRPC_CMD_SEND_DATA) + return -EINVAL; + *command = RXRPC_CMD_ACCEPT; + if (len != 0) + return -EINVAL; + if (!server) + return -EISCONN; + break; + + default: + return -EINVAL; + } + } + + _leave(" = 0"); + return 0; +} + +/* + * abort a call, sending an ABORT packet to the peer + */ +static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code) +{ + write_lock_bh(&call->state_lock); + + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = abort_code; + set_bit(RXRPC_CALL_ABORT, &call->events); + del_timer_sync(&call->resend_timer); + del_timer_sync(&call->ack_timer); + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + clear_bit(RXRPC_CALL_ACK, &call->events); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + rxrpc_queue_call(call); + } + + write_unlock_bh(&call->state_lock); +} + +/* + * send a message forming part of a client call through an RxRPC socket + * - caller holds the socket locked + * - the socket may be either a client socket or a server socket + */ +int rxrpc_client_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, + struct rxrpc_transport *trans, struct msghdr *msg, + size_t len) +{ + struct rxrpc_conn_bundle *bundle; + enum rxrpc_command cmd; + struct rxrpc_call *call; + unsigned long user_call_ID = 0; + struct key *key; + __be16 service_id; + u32 abort_code = 0; + int ret; + + _enter(""); + + ASSERT(trans != NULL); + + ret = rxrpc_sendmsg_cmsg(rx, msg, &user_call_ID, &cmd, &abort_code, + false); + if (ret < 0) + return ret; + + bundle = NULL; + if (trans) { + service_id = rx->service_id; + if (msg->msg_name) { + struct sockaddr_rxrpc *srx = + (struct sockaddr_rxrpc *) msg->msg_name; + service_id = htons(srx->srx_service); + } + key = rx->key; + if (key && !rx->key->payload.data) + key = NULL; + bundle = rxrpc_get_bundle(rx, trans, key, service_id, + GFP_KERNEL); + if (IS_ERR(bundle)) + return PTR_ERR(bundle); + } + + call = rxrpc_get_client_call(rx, trans, bundle, user_call_ID, + abort_code == 0, GFP_KERNEL); + if (trans) + rxrpc_put_bundle(trans, bundle); + if (IS_ERR(call)) { + _leave(" = %ld", PTR_ERR(call)); + return PTR_ERR(call); + } + + _debug("CALL %d USR %lx ST %d on CONN %p", + call->debug_id, call->user_call_ID, call->state, call->conn); + + if (call->state >= RXRPC_CALL_COMPLETE) { + /* it's too late for this call */ + ret = -ESHUTDOWN; + } else if (cmd == RXRPC_CMD_SEND_ABORT) { + rxrpc_send_abort(call, abort_code); + } else if (cmd != RXRPC_CMD_SEND_DATA) { + ret = -EINVAL; + } else if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST) { + /* request phase complete for this client call */ + ret = -EPROTO; + } else { + ret = rxrpc_send_data(iocb, rx, call, msg, len); + } + + rxrpc_put_call(call); + _leave(" = %d", ret); + return ret; +} + +/** + * rxrpc_kernel_send_data - Allow a kernel service to send data on a call + * @call: The call to send data through + * @msg: The data to send + * @len: The amount of data to send + * + * Allow a kernel service to send data on a call. The call must be in an state + * appropriate to sending data. No control data should be supplied in @msg, + * nor should an address be supplied. MSG_MORE should be flagged if there's + * more data to come, otherwise this data will end the transmission phase. + */ +int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg, + size_t len) +{ + int ret; + + _enter("{%d,%s},", call->debug_id, rxrpc_call_states[call->state]); + + ASSERTCMP(msg->msg_name, ==, NULL); + ASSERTCMP(msg->msg_control, ==, NULL); + + lock_sock(&call->socket->sk); + + _debug("CALL %d USR %lx ST %d on CONN %p", + call->debug_id, call->user_call_ID, call->state, call->conn); + + if (call->state >= RXRPC_CALL_COMPLETE) { + ret = -ESHUTDOWN; /* it's too late for this call */ + } else if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST && + call->state != RXRPC_CALL_SERVER_ACK_REQUEST && + call->state != RXRPC_CALL_SERVER_SEND_REPLY) { + ret = -EPROTO; /* request phase complete for this client call */ + } else { + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = rxrpc_send_data(NULL, call->socket, call, msg, len); + set_fs(oldfs); + } + + release_sock(&call->socket->sk); + _leave(" = %d", ret); + return ret; +} + +EXPORT_SYMBOL(rxrpc_kernel_send_data); + +/* + * rxrpc_kernel_abort_call - Allow a kernel service to abort a call + * @call: The call to be aborted + * @abort_code: The abort code to stick into the ABORT packet + * + * Allow a kernel service to abort a call, if it's still in an abortable state. + */ +void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code) +{ + _enter("{%d},%d", call->debug_id, abort_code); + + lock_sock(&call->socket->sk); + + _debug("CALL %d USR %lx ST %d on CONN %p", + call->debug_id, call->user_call_ID, call->state, call->conn); + + if (call->state < RXRPC_CALL_COMPLETE) + rxrpc_send_abort(call, abort_code); + + release_sock(&call->socket->sk); + _leave(""); +} + +EXPORT_SYMBOL(rxrpc_kernel_abort_call); + +/* + * send a message through a server socket + * - caller holds the socket locked + */ +int rxrpc_server_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, + struct msghdr *msg, size_t len) +{ + enum rxrpc_command cmd; + struct rxrpc_call *call; + unsigned long user_call_ID = 0; + u32 abort_code = 0; + int ret; + + _enter(""); + + ret = rxrpc_sendmsg_cmsg(rx, msg, &user_call_ID, &cmd, &abort_code, + true); + if (ret < 0) + return ret; + + if (cmd == RXRPC_CMD_ACCEPT) { + call = rxrpc_accept_call(rx, user_call_ID); + if (IS_ERR(call)) + return PTR_ERR(call); + rxrpc_put_call(call); + return 0; + } + + call = rxrpc_find_server_call(rx, user_call_ID); + if (!call) + return -EBADSLT; + if (call->state >= RXRPC_CALL_COMPLETE) { + ret = -ESHUTDOWN; + goto out; + } + + switch (cmd) { + case RXRPC_CMD_SEND_DATA: + if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST && + call->state != RXRPC_CALL_SERVER_ACK_REQUEST && + call->state != RXRPC_CALL_SERVER_SEND_REPLY) { + /* Tx phase not yet begun for this call */ + ret = -EPROTO; + break; + } + + ret = rxrpc_send_data(iocb, rx, call, msg, len); + break; + + case RXRPC_CMD_SEND_ABORT: + rxrpc_send_abort(call, abort_code); + break; + default: + BUG(); + } + + out: + rxrpc_put_call(call); + _leave(" = %d", ret); + return ret; +} + +/* + * send a packet through the transport endpoint + */ +int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb) +{ + struct kvec iov[1]; + struct msghdr msg; + int ret, opt; + + _enter(",{%d}", skb->len); + + iov[0].iov_base = skb->head; + iov[0].iov_len = skb->len; + + msg.msg_name = &trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + /* send the packet with the don't fragment bit set if we currently + * think it's small enough */ + if (skb->len - sizeof(struct rxrpc_header) < trans->peer->maxdata) { + down_read(&trans->local->defrag_sem); + /* send the packet by UDP + * - returns -EMSGSIZE if UDP would have to fragment the packet + * to go out of the interface + * - in which case, we'll have processed the ICMP error + * message and update the peer record + */ + ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1, + iov[0].iov_len); + + up_read(&trans->local->defrag_sem); + if (ret == -EMSGSIZE) + goto send_fragmentable; + + _leave(" = %d [%u]", ret, trans->peer->maxdata); + return ret; + } + +send_fragmentable: + /* attempt to send this message with fragmentation enabled */ + _debug("send fragment"); + + down_write(&trans->local->defrag_sem); + opt = IP_PMTUDISC_DONT; + ret = kernel_setsockopt(trans->local->socket, SOL_IP, IP_MTU_DISCOVER, + (char *) &opt, sizeof(opt)); + if (ret == 0) { + ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1, + iov[0].iov_len); + + opt = IP_PMTUDISC_DO; + kernel_setsockopt(trans->local->socket, SOL_IP, + IP_MTU_DISCOVER, (char *) &opt, sizeof(opt)); + } + + up_write(&trans->local->defrag_sem); + _leave(" = %d [frag %u]", ret, trans->peer->maxdata); + return ret; +} + +/* + * wait for space to appear in the transmit/ACK window + * - caller holds the socket locked + */ +static int rxrpc_wait_for_tx_window(struct rxrpc_sock *rx, + struct rxrpc_call *call, + long *timeo) +{ + DECLARE_WAITQUEUE(myself, current); + int ret; + + _enter(",{%d},%ld", + CIRC_SPACE(call->acks_head, call->acks_tail, call->acks_winsz), + *timeo); + + add_wait_queue(&call->tx_waitq, &myself); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + ret = 0; + if (CIRC_SPACE(call->acks_head, call->acks_tail, + call->acks_winsz) > 0) + break; + if (signal_pending(current)) { + ret = sock_intr_errno(*timeo); + break; + } + + release_sock(&rx->sk); + *timeo = schedule_timeout(*timeo); + lock_sock(&rx->sk); + } + + remove_wait_queue(&call->tx_waitq, &myself); + set_current_state(TASK_RUNNING); + _leave(" = %d", ret); + return ret; +} + +/* + * attempt to schedule an instant Tx resend + */ +static inline void rxrpc_instant_resend(struct rxrpc_call *call) +{ + read_lock_bh(&call->state_lock); + if (try_to_del_timer_sync(&call->resend_timer) >= 0) { + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) + rxrpc_queue_call(call); + } + read_unlock_bh(&call->state_lock); +} + +/* + * queue a packet for transmission, set the resend timer and attempt + * to send the packet immediately + */ +static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, + bool last) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + int ret; + + _net("queue skb %p [%d]", skb, call->acks_head); + + ASSERT(call->acks_window != NULL); + call->acks_window[call->acks_head] = (unsigned long) skb; + smp_wmb(); + call->acks_head = (call->acks_head + 1) & (call->acks_winsz - 1); + + if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) { + _debug("________awaiting reply/ACK__________"); + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_CLIENT_SEND_REQUEST: + call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY; + break; + case RXRPC_CALL_SERVER_ACK_REQUEST: + call->state = RXRPC_CALL_SERVER_SEND_REPLY; + if (!last) + break; + case RXRPC_CALL_SERVER_SEND_REPLY: + call->state = RXRPC_CALL_SERVER_AWAIT_ACK; + break; + default: + break; + } + write_unlock_bh(&call->state_lock); + } + + _proto("Tx DATA %%%u { #%u }", + ntohl(sp->hdr.serial), ntohl(sp->hdr.seq)); + + sp->need_resend = 0; + sp->resend_at = jiffies + rxrpc_resend_timeout * HZ; + if (!test_and_set_bit(RXRPC_CALL_RUN_RTIMER, &call->flags)) { + _debug("run timer"); + call->resend_timer.expires = sp->resend_at; + add_timer(&call->resend_timer); + } + + /* attempt to cancel the rx-ACK timer, deferring reply transmission if + * we're ACK'ing the request phase of an incoming call */ + ret = -EAGAIN; + if (try_to_del_timer_sync(&call->ack_timer) >= 0) { + /* the packet may be freed by rxrpc_process_call() before this + * returns */ + ret = rxrpc_send_packet(call->conn->trans, skb); + _net("sent skb %p", skb); + } else { + _debug("failed to delete ACK timer"); + } + + if (ret < 0) { + _debug("need instant resend %d", ret); + sp->need_resend = 1; + rxrpc_instant_resend(call); + } + + _leave(""); +} + +/* + * send data through a socket + * - must be called in process context + * - caller holds the socket locked + */ +static int rxrpc_send_data(struct kiocb *iocb, + struct rxrpc_sock *rx, + struct rxrpc_call *call, + struct msghdr *msg, size_t len) +{ + struct rxrpc_skb_priv *sp; + unsigned char __user *from; + struct sk_buff *skb; + struct iovec *iov; + struct sock *sk = &rx->sk; + long timeo; + bool more; + int ret, ioc, segment, copied; + + _enter(",,,{%zu},%zu", msg->msg_iovlen, len); + + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + + /* this should be in poll */ + clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); + + if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) + return -EPIPE; + + iov = msg->msg_iov; + ioc = msg->msg_iovlen - 1; + from = iov->iov_base; + segment = iov->iov_len; + iov++; + more = msg->msg_flags & MSG_MORE; + + skb = call->tx_pending; + call->tx_pending = NULL; + + copied = 0; + do { + int copy; + + if (segment > len) + segment = len; + + _debug("SEGMENT %d @%p", segment, from); + + if (!skb) { + size_t size, chunk, max, space; + + _debug("alloc"); + + if (CIRC_SPACE(call->acks_head, call->acks_tail, + call->acks_winsz) <= 0) { + ret = -EAGAIN; + if (msg->msg_flags & MSG_DONTWAIT) + goto maybe_error; + ret = rxrpc_wait_for_tx_window(rx, call, + &timeo); + if (ret < 0) + goto maybe_error; + } + + max = call->conn->trans->peer->maxdata; + max -= call->conn->security_size; + max &= ~(call->conn->size_align - 1UL); + + chunk = max; + if (chunk > len) + chunk = len; + + space = chunk + call->conn->size_align; + space &= ~(call->conn->size_align - 1UL); + + size = space + call->conn->header_size; + + _debug("SIZE: %zu/%zu/%zu", chunk, space, size); + + /* create a buffer that we can retain until it's ACK'd */ + skb = sock_alloc_send_skb( + sk, size, msg->msg_flags & MSG_DONTWAIT, &ret); + if (!skb) + goto maybe_error; + + rxrpc_new_skb(skb); + + _debug("ALLOC SEND %p", skb); + + ASSERTCMP(skb->mark, ==, 0); + + _debug("HS: %u", call->conn->header_size); + skb_reserve(skb, call->conn->header_size); + skb->len += call->conn->header_size; + + sp = rxrpc_skb(skb); + sp->remain = chunk; + if (sp->remain > skb_tailroom(skb)) + sp->remain = skb_tailroom(skb); + + _net("skb: hr %d, tr %d, hl %d, rm %d", + skb_headroom(skb), + skb_tailroom(skb), + skb_headlen(skb), + sp->remain); + + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + _debug("append"); + sp = rxrpc_skb(skb); + + /* append next segment of data to the current buffer */ + copy = skb_tailroom(skb); + ASSERTCMP(copy, >, 0); + if (copy > segment) + copy = segment; + if (copy > sp->remain) + copy = sp->remain; + + _debug("add"); + ret = skb_add_data(skb, from, copy); + _debug("added"); + if (ret < 0) + goto efault; + sp->remain -= copy; + skb->mark += copy; + + len -= copy; + segment -= copy; + from += copy; + while (segment == 0 && ioc > 0) { + from = iov->iov_base; + segment = iov->iov_len; + iov++; + ioc--; + } + if (len == 0) { + segment = 0; + ioc = 0; + } + + /* check for the far side aborting the call or a network error + * occurring */ + if (call->state > RXRPC_CALL_COMPLETE) + goto call_aborted; + + /* add the packet to the send queue if it's now full */ + if (sp->remain <= 0 || (segment == 0 && !more)) { + struct rxrpc_connection *conn = call->conn; + size_t pad; + + /* pad out if we're using security */ + if (conn->security) { + pad = conn->security_size + skb->mark; + pad = conn->size_align - pad; + pad &= conn->size_align - 1; + _debug("pad %zu", pad); + if (pad) + memset(skb_put(skb, pad), 0, pad); + } + + sp->hdr.epoch = conn->epoch; + sp->hdr.cid = call->cid; + sp->hdr.callNumber = call->call_id; + sp->hdr.seq = + htonl(atomic_inc_return(&call->sequence)); + sp->hdr.serial = + htonl(atomic_inc_return(&conn->serial)); + sp->hdr.type = RXRPC_PACKET_TYPE_DATA; + sp->hdr.userStatus = 0; + sp->hdr.securityIndex = conn->security_ix; + sp->hdr._rsvd = 0; + sp->hdr.serviceId = conn->service_id; + + sp->hdr.flags = conn->out_clientflag; + if (len == 0 && !more) + sp->hdr.flags |= RXRPC_LAST_PACKET; + else if (CIRC_SPACE(call->acks_head, call->acks_tail, + call->acks_winsz) > 1) + sp->hdr.flags |= RXRPC_MORE_PACKETS; + + ret = rxrpc_secure_packet( + call, skb, skb->mark, + skb->head + sizeof(struct rxrpc_header)); + if (ret < 0) + goto out; + + memcpy(skb->head, &sp->hdr, + sizeof(struct rxrpc_header)); + rxrpc_queue_packet(call, skb, segment == 0 && !more); + skb = NULL; + } + + } while (segment > 0); + +out: + call->tx_pending = skb; + _leave(" = %d", ret); + return ret; + +call_aborted: + rxrpc_free_skb(skb); + if (call->state == RXRPC_CALL_NETWORK_ERROR) + ret = call->conn->trans->peer->net_error; + else + ret = -ECONNABORTED; + _leave(" = %d", ret); + return ret; + +maybe_error: + if (copied) + ret = copied; + goto out; + +efault: + ret = -EFAULT; + goto out; +} diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c new file mode 100644 index 0000000..d399de4 --- /dev/null +++ b/net/rxrpc/ar-peer.c @@ -0,0 +1,273 @@ +/* RxRPC remote transport endpoint management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static LIST_HEAD(rxrpc_peers); +static DEFINE_RWLOCK(rxrpc_peer_lock); +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_peer_wq); + +static void rxrpc_destroy_peer(struct work_struct *work); + +/* + * allocate a new peer + */ +static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx, + gfp_t gfp) +{ + struct rxrpc_peer *peer; + + _enter(""); + + peer = kzalloc(sizeof(struct rxrpc_peer), gfp); + if (peer) { + INIT_WORK(&peer->destroyer, &rxrpc_destroy_peer); + INIT_LIST_HEAD(&peer->link); + INIT_LIST_HEAD(&peer->error_targets); + spin_lock_init(&peer->lock); + atomic_set(&peer->usage, 1); + peer->debug_id = atomic_inc_return(&rxrpc_debug_id); + memcpy(&peer->srx, srx, sizeof(*srx)); + + peer->mtu = peer->if_mtu = 65535; + + if (srx->transport.family == AF_INET) { + peer->hdrsize = sizeof(struct iphdr); + switch (srx->transport_type) { + case SOCK_DGRAM: + peer->hdrsize += sizeof(struct udphdr); + break; + default: + BUG(); + break; + } + } else { + BUG(); + } + + peer->hdrsize += sizeof(struct rxrpc_header); + peer->maxdata = peer->mtu - peer->hdrsize; + } + + _leave(" = %p", peer); + return peer; +} + +/* + * obtain a remote transport endpoint for the specified address + */ +struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp) +{ + struct rxrpc_peer *peer, *candidate; + const char *new = "old"; + int usage; + + _enter("{%d,%d,%u.%u.%u.%u+%hu}", + srx->transport_type, + srx->transport_len, + NIPQUAD(srx->transport.sin.sin_addr), + ntohs(srx->transport.sin.sin_port)); + + /* search the peer list first */ + read_lock_bh(&rxrpc_peer_lock); + list_for_each_entry(peer, &rxrpc_peers, link) { + _debug("check PEER %d { u=%d t=%d l=%d }", + peer->debug_id, + atomic_read(&peer->usage), + peer->srx.transport_type, + peer->srx.transport_len); + + if (atomic_read(&peer->usage) > 0 && + peer->srx.transport_type == srx->transport_type && + peer->srx.transport_len == srx->transport_len && + memcmp(&peer->srx.transport, + &srx->transport, + srx->transport_len) == 0) + goto found_extant_peer; + } + read_unlock_bh(&rxrpc_peer_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_peer(srx, gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + write_lock_bh(&rxrpc_peer_lock); + + list_for_each_entry(peer, &rxrpc_peers, link) { + if (atomic_read(&peer->usage) > 0 && + peer->srx.transport_type == srx->transport_type && + peer->srx.transport_len == srx->transport_len && + memcmp(&peer->srx.transport, + &srx->transport, + srx->transport_len) == 0) + goto found_extant_second; + } + + /* we can now add the new candidate to the list */ + peer = candidate; + candidate = NULL; + + list_add_tail(&peer->link, &rxrpc_peers); + write_unlock_bh(&rxrpc_peer_lock); + new = "new"; + +success: + _net("PEER %s %d {%d,%u,%u.%u.%u.%u+%hu}", + new, + peer->debug_id, + peer->srx.transport_type, + peer->srx.transport.family, + NIPQUAD(peer->srx.transport.sin.sin_addr), + ntohs(peer->srx.transport.sin.sin_port)); + + _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage)); + return peer; + + /* we found the peer in the list immediately */ +found_extant_peer: + usage = atomic_inc_return(&peer->usage); + read_unlock_bh(&rxrpc_peer_lock); + goto success; + + /* we found the peer on the second time through the list */ +found_extant_second: + usage = atomic_inc_return(&peer->usage); + write_unlock_bh(&rxrpc_peer_lock); + kfree(candidate); + goto success; +} + +/* + * find the peer associated with a packet + */ +struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *local, + __be32 addr, __be16 port) +{ + struct rxrpc_peer *peer; + + _enter(""); + + /* search the peer list */ + read_lock_bh(&rxrpc_peer_lock); + + if (local->srx.transport.family == AF_INET && + local->srx.transport_type == SOCK_DGRAM + ) { + list_for_each_entry(peer, &rxrpc_peers, link) { + if (atomic_read(&peer->usage) > 0 && + peer->srx.transport_type == SOCK_DGRAM && + peer->srx.transport.family == AF_INET && + peer->srx.transport.sin.sin_port == port && + peer->srx.transport.sin.sin_addr.s_addr == addr) + goto found_UDP_peer; + } + + goto new_UDP_peer; + } + + read_unlock_bh(&rxrpc_peer_lock); + _leave(" = -EAFNOSUPPORT"); + return ERR_PTR(-EAFNOSUPPORT); + +found_UDP_peer: + _net("Rx UDP DGRAM from peer %d", peer->debug_id); + atomic_inc(&peer->usage); + read_unlock_bh(&rxrpc_peer_lock); + _leave(" = %p", peer); + return peer; + +new_UDP_peer: + _net("Rx UDP DGRAM from NEW peer %d", peer->debug_id); + read_unlock_bh(&rxrpc_peer_lock); + _leave(" = -EBUSY [new]"); + return ERR_PTR(-EBUSY); +} + +/* + * release a remote transport endpoint + */ +void rxrpc_put_peer(struct rxrpc_peer *peer) +{ + _enter("%p{u=%d}", peer, atomic_read(&peer->usage)); + + ASSERTCMP(atomic_read(&peer->usage), >, 0); + + if (likely(!atomic_dec_and_test(&peer->usage))) { + _leave(" [in use]"); + return; + } + + rxrpc_queue_work(&peer->destroyer); + _leave(""); +} + +/* + * destroy a remote transport endpoint + */ +static void rxrpc_destroy_peer(struct work_struct *work) +{ + struct rxrpc_peer *peer = + container_of(work, struct rxrpc_peer, destroyer); + + _enter("%p{%d}", peer, atomic_read(&peer->usage)); + + write_lock_bh(&rxrpc_peer_lock); + list_del(&peer->link); + write_unlock_bh(&rxrpc_peer_lock); + + _net("DESTROY PEER %d", peer->debug_id); + kfree(peer); + + if (list_empty(&rxrpc_peers)) + wake_up_all(&rxrpc_peer_wq); + _leave(""); +} + +/* + * preemptively destroy all the peer records from a transport endpoint rather + * than waiting for them to time out + */ +void __exit rxrpc_destroy_all_peers(void) +{ + DECLARE_WAITQUEUE(myself,current); + + _enter(""); + + /* we simply have to wait for them to go away */ + if (!list_empty(&rxrpc_peers)) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&rxrpc_peer_wq, &myself); + + while (!list_empty(&rxrpc_peers)) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&rxrpc_peer_wq, &myself); + set_current_state(TASK_RUNNING); + } + + _leave(""); +} diff --git a/net/rxrpc/ar-proc.c b/net/rxrpc/ar-proc.c new file mode 100644 index 0000000..58f4b4e --- /dev/null +++ b/net/rxrpc/ar-proc.c @@ -0,0 +1,247 @@ +/* /proc/net/ support for AF_RXRPC + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include "ar-internal.h" + +static const char *rxrpc_conn_states[] = { + [RXRPC_CONN_UNUSED] = "Unused ", + [RXRPC_CONN_CLIENT] = "Client ", + [RXRPC_CONN_SERVER_UNSECURED] = "SvUnsec ", + [RXRPC_CONN_SERVER_CHALLENGING] = "SvChall ", + [RXRPC_CONN_SERVER] = "SvSecure", + [RXRPC_CONN_REMOTELY_ABORTED] = "RmtAbort", + [RXRPC_CONN_LOCALLY_ABORTED] = "LocAbort", + [RXRPC_CONN_NETWORK_ERROR] = "NetError", +}; + +const char *rxrpc_call_states[] = { + [RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq", + [RXRPC_CALL_CLIENT_AWAIT_REPLY] = "ClAwtRpl", + [RXRPC_CALL_CLIENT_RECV_REPLY] = "ClRcvRpl", + [RXRPC_CALL_CLIENT_FINAL_ACK] = "ClFnlACK", + [RXRPC_CALL_SERVER_SECURING] = "SvSecure", + [RXRPC_CALL_SERVER_ACCEPTING] = "SvAccept", + [RXRPC_CALL_SERVER_RECV_REQUEST] = "SvRcvReq", + [RXRPC_CALL_SERVER_ACK_REQUEST] = "SvAckReq", + [RXRPC_CALL_SERVER_SEND_REPLY] = "SvSndRpl", + [RXRPC_CALL_SERVER_AWAIT_ACK] = "SvAwtACK", + [RXRPC_CALL_COMPLETE] = "Complete", + [RXRPC_CALL_SERVER_BUSY] = "SvBusy ", + [RXRPC_CALL_REMOTELY_ABORTED] = "RmtAbort", + [RXRPC_CALL_LOCALLY_ABORTED] = "LocAbort", + [RXRPC_CALL_NETWORK_ERROR] = "NetError", + [RXRPC_CALL_DEAD] = "Dead ", +}; + +/* + * generate a list of extant and dead calls in /proc/net/rxrpc_calls + */ +static void *rxrpc_call_seq_start(struct seq_file *seq, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + read_lock(&rxrpc_call_lock); + if (!pos) + return SEQ_START_TOKEN; + pos--; + + list_for_each(_p, &rxrpc_calls) + if (!pos--) + break; + + return _p != &rxrpc_calls ? _p : NULL; +} + +static void *rxrpc_call_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = (v == SEQ_START_TOKEN) ? rxrpc_calls.next : _p->next; + + return _p != &rxrpc_calls ? _p : NULL; +} + +static void rxrpc_call_seq_stop(struct seq_file *seq, void *v) +{ + read_unlock(&rxrpc_call_lock); +} + +static int rxrpc_call_seq_show(struct seq_file *seq, void *v) +{ + struct rxrpc_transport *trans; + struct rxrpc_call *call; + char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, + "Proto Local Remote " + " SvID ConnID CallID End Use State Abort " + " UserID\n"); + return 0; + } + + call = list_entry(v, struct rxrpc_call, link); + trans = call->conn->trans; + + sprintf(lbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->local->srx.transport.sin.sin_addr), + ntohs(trans->local->srx.transport.sin.sin_port)); + + sprintf(rbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->peer->srx.transport.sin.sin_addr), + ntohs(trans->peer->srx.transport.sin.sin_port)); + + seq_printf(seq, + "UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" + " %-8.8s %08x %lx\n", + lbuff, + rbuff, + ntohs(call->conn->service_id), + ntohl(call->conn->cid), + ntohl(call->call_id), + call->conn->in_clientflag ? "Svc" : "Clt", + atomic_read(&call->usage), + rxrpc_call_states[call->state], + call->abort_code, + call->user_call_ID); + + return 0; +} + +static struct seq_operations rxrpc_call_seq_ops = { + .start = rxrpc_call_seq_start, + .next = rxrpc_call_seq_next, + .stop = rxrpc_call_seq_stop, + .show = rxrpc_call_seq_show, +}; + +static int rxrpc_call_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &rxrpc_call_seq_ops); +} + +struct file_operations rxrpc_call_seq_fops = { + .owner = THIS_MODULE, + .open = rxrpc_call_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +/* + * generate a list of extant virtual connections in /proc/net/rxrpc_conns + */ +static void *rxrpc_connection_seq_start(struct seq_file *seq, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + read_lock(&rxrpc_connection_lock); + if (!pos) + return SEQ_START_TOKEN; + pos--; + + list_for_each(_p, &rxrpc_connections) + if (!pos--) + break; + + return _p != &rxrpc_connections ? _p : NULL; +} + +static void *rxrpc_connection_seq_next(struct seq_file *seq, void *v, + loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = (v == SEQ_START_TOKEN) ? rxrpc_connections.next : _p->next; + + return _p != &rxrpc_connections ? _p : NULL; +} + +static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v) +{ + read_unlock(&rxrpc_connection_lock); +} + +static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) +{ + struct rxrpc_connection *conn; + struct rxrpc_transport *trans; + char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, + "Proto Local Remote " + " SvID ConnID Calls End Use State Key " + " Serial ISerial\n" + ); + return 0; + } + + conn = list_entry(v, struct rxrpc_connection, link); + trans = conn->trans; + + sprintf(lbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->local->srx.transport.sin.sin_addr), + ntohs(trans->local->srx.transport.sin.sin_port)); + + sprintf(rbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->peer->srx.transport.sin.sin_addr), + ntohs(trans->peer->srx.transport.sin.sin_port)); + + seq_printf(seq, + "UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" + " %s %08x %08x %08x\n", + lbuff, + rbuff, + ntohs(conn->service_id), + ntohl(conn->cid), + conn->call_counter, + conn->in_clientflag ? "Svc" : "Clt", + atomic_read(&conn->usage), + rxrpc_conn_states[conn->state], + key_serial(conn->key), + atomic_read(&conn->serial), + atomic_read(&conn->hi_serial)); + + return 0; +} + +static struct seq_operations rxrpc_connection_seq_ops = { + .start = rxrpc_connection_seq_start, + .next = rxrpc_connection_seq_next, + .stop = rxrpc_connection_seq_stop, + .show = rxrpc_connection_seq_show, +}; + + +static int rxrpc_connection_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &rxrpc_connection_seq_ops); +} + +struct file_operations rxrpc_connection_seq_fops = { + .owner = THIS_MODULE, + .open = rxrpc_connection_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c new file mode 100644 index 0000000..f19121d --- /dev/null +++ b/net/rxrpc/ar-recvmsg.c @@ -0,0 +1,437 @@ +/* RxRPC recvmsg() implementation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * removal a call's user ID from the socket tree to make the user ID available + * again and so that it won't be seen again in association with that call + */ +void rxrpc_remove_user_ID(struct rxrpc_sock *rx, struct rxrpc_call *call) +{ + _debug("RELEASE CALL %d", call->debug_id); + + if (test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) { + write_lock_bh(&rx->call_lock); + rb_erase(&call->sock_node, &call->socket->calls); + clear_bit(RXRPC_CALL_HAS_USERID, &call->flags); + write_unlock_bh(&rx->call_lock); + } + + read_lock_bh(&call->state_lock); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + rxrpc_queue_call(call); + read_unlock_bh(&call->state_lock); +} + +/* + * receive a message from an RxRPC socket + * - we need to be careful about two or more threads calling recvmsg + * simultaneously + */ +int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct rxrpc_skb_priv *sp; + struct rxrpc_call *call = NULL, *continue_call = NULL; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + struct sk_buff *skb; + long timeo; + int copy, ret, ullen, offset, copied = 0; + u32 abort_code; + + DEFINE_WAIT(wait); + + _enter(",,,%zu,%d", len, flags); + + if (flags & (MSG_OOB | MSG_TRUNC)) + return -EOPNOTSUPP; + + ullen = msg->msg_flags & MSG_CMSG_COMPAT ? 4 : sizeof(unsigned long); + + timeo = sock_rcvtimeo(&rx->sk, flags & MSG_DONTWAIT); + msg->msg_flags |= MSG_MORE; + + lock_sock(&rx->sk); + + for (;;) { + /* return immediately if a client socket has no outstanding + * calls */ + if (RB_EMPTY_ROOT(&rx->calls)) { + if (copied) + goto out; + if (rx->sk.sk_state != RXRPC_SERVER_LISTENING) { + release_sock(&rx->sk); + if (continue_call) + rxrpc_put_call(continue_call); + return -ENODATA; + } + } + + /* get the next message on the Rx queue */ + skb = skb_peek(&rx->sk.sk_receive_queue); + if (!skb) { + /* nothing remains on the queue */ + if (copied && + (msg->msg_flags & MSG_PEEK || timeo == 0)) + goto out; + + /* wait for a message to turn up */ + release_sock(&rx->sk); + prepare_to_wait_exclusive(rx->sk.sk_sleep, &wait, + TASK_INTERRUPTIBLE); + ret = sock_error(&rx->sk); + if (ret) + goto wait_error; + + if (skb_queue_empty(&rx->sk.sk_receive_queue)) { + if (signal_pending(current)) + goto wait_interrupted; + timeo = schedule_timeout(timeo); + } + finish_wait(rx->sk.sk_sleep, &wait); + lock_sock(&rx->sk); + continue; + } + + peek_next_packet: + sp = rxrpc_skb(skb); + call = sp->call; + ASSERT(call != NULL); + + _debug("next pkt %s", rxrpc_pkts[sp->hdr.type]); + + /* make sure we wait for the state to be updated in this call */ + spin_lock_bh(&call->lock); + spin_unlock_bh(&call->lock); + + if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { + _debug("packet from released call"); + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + continue; + } + + /* determine whether to continue last data receive */ + if (continue_call) { + _debug("maybe cont"); + if (call != continue_call || + skb->mark != RXRPC_SKB_MARK_DATA) { + release_sock(&rx->sk); + rxrpc_put_call(continue_call); + _leave(" = %d [noncont]", copied); + return copied; + } + } + + rxrpc_get_call(call); + + /* copy the peer address and timestamp */ + if (!continue_call) { + if (msg->msg_name && msg->msg_namelen > 0) + memcpy(&msg->msg_name, &call->conn->trans->peer->srx, + sizeof(call->conn->trans->peer->srx)); + sock_recv_timestamp(msg, &rx->sk, skb); + } + + /* receive the message */ + if (skb->mark != RXRPC_SKB_MARK_DATA) + goto receive_non_data_message; + + _debug("recvmsg DATA #%u { %d, %d }", + ntohl(sp->hdr.seq), skb->len, sp->offset); + + if (!continue_call) { + /* only set the control data once per recvmsg() */ + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID, + ullen, &call->user_call_ID); + if (ret < 0) + goto copy_error; + ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags)); + } + + ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv); + ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1); + call->rx_data_recv = ntohl(sp->hdr.seq); + + ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten); + + offset = sp->offset; + copy = skb->len - offset; + if (copy > len - copied) + copy = len - copied; + + if (skb->ip_summed == CHECKSUM_UNNECESSARY) { + ret = skb_copy_datagram_iovec(skb, offset, + msg->msg_iov, copy); + } else { + ret = skb_copy_and_csum_datagram_iovec(skb, offset, + msg->msg_iov); + if (ret == -EINVAL) + goto csum_copy_error; + } + + if (ret < 0) + goto copy_error; + + /* handle piecemeal consumption of data packets */ + _debug("copied %d+%d", copy, copied); + + offset += copy; + copied += copy; + + if (!(flags & MSG_PEEK)) + sp->offset = offset; + + if (sp->offset < skb->len) { + _debug("buffer full"); + ASSERTCMP(copied, ==, len); + break; + } + + /* we transferred the whole data packet */ + if (sp->hdr.flags & RXRPC_LAST_PACKET) { + _debug("last"); + if (call->conn->out_clientflag) { + /* last byte of reply received */ + ret = copied; + goto terminal_message; + } + + /* last bit of request received */ + if (!(flags & MSG_PEEK)) { + _debug("eat packet"); + if (skb_dequeue(&rx->sk.sk_receive_queue) != + skb) + BUG(); + rxrpc_free_skb(skb); + } + msg->msg_flags &= ~MSG_MORE; + break; + } + + /* move on to the next data message */ + _debug("next"); + if (!continue_call) + continue_call = sp->call; + else + rxrpc_put_call(call); + call = NULL; + + if (flags & MSG_PEEK) { + _debug("peek next"); + skb = skb->next; + if (skb == (struct sk_buff *) &rx->sk.sk_receive_queue) + break; + goto peek_next_packet; + } + + _debug("eat packet"); + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + } + + /* end of non-terminal data packet reception for the moment */ + _debug("end rcv data"); +out: + release_sock(&rx->sk); + if (call) + rxrpc_put_call(call); + if (continue_call) + rxrpc_put_call(continue_call); + _leave(" = %d [data]", copied); + return copied; + + /* handle non-DATA messages such as aborts, incoming connections and + * final ACKs */ +receive_non_data_message: + _debug("non-data"); + + if (skb->mark == RXRPC_SKB_MARK_NEW_CALL) { + _debug("RECV NEW CALL"); + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_NEW_CALL, 0, &abort_code); + if (ret < 0) + goto copy_error; + if (!(flags & MSG_PEEK)) { + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + } + goto out; + } + + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID, + ullen, &call->user_call_ID); + if (ret < 0) + goto copy_error; + ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags)); + + switch (skb->mark) { + case RXRPC_SKB_MARK_DATA: + BUG(); + case RXRPC_SKB_MARK_FINAL_ACK: + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_ACK, 0, &abort_code); + break; + case RXRPC_SKB_MARK_BUSY: + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_BUSY, 0, &abort_code); + break; + case RXRPC_SKB_MARK_REMOTE_ABORT: + abort_code = call->abort_code; + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_ABORT, 4, &abort_code); + break; + case RXRPC_SKB_MARK_NET_ERROR: + _debug("RECV NET ERROR %d", sp->error); + abort_code = sp->error; + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_NET_ERROR, 4, &abort_code); + break; + case RXRPC_SKB_MARK_LOCAL_ERROR: + _debug("RECV LOCAL ERROR %d", sp->error); + abort_code = sp->error; + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_LOCAL_ERROR, 4, + &abort_code); + break; + default: + BUG(); + break; + } + + if (ret < 0) + goto copy_error; + +terminal_message: + _debug("terminal"); + msg->msg_flags &= ~MSG_MORE; + msg->msg_flags |= MSG_EOR; + + if (!(flags & MSG_PEEK)) { + _net("free terminal skb %p", skb); + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + rxrpc_remove_user_ID(rx, call); + } + + release_sock(&rx->sk); + rxrpc_put_call(call); + if (continue_call) + rxrpc_put_call(continue_call); + _leave(" = %d", ret); + return ret; + +copy_error: + _debug("copy error"); + release_sock(&rx->sk); + rxrpc_put_call(call); + if (continue_call) + rxrpc_put_call(continue_call); + _leave(" = %d", ret); + return ret; + +csum_copy_error: + _debug("csum error"); + release_sock(&rx->sk); + if (continue_call) + rxrpc_put_call(continue_call); + rxrpc_kill_skb(skb); + skb_kill_datagram(&rx->sk, skb, flags); + rxrpc_put_call(call); + return -EAGAIN; + +wait_interrupted: + ret = sock_intr_errno(timeo); +wait_error: + finish_wait(rx->sk.sk_sleep, &wait); + if (continue_call) + rxrpc_put_call(continue_call); + if (copied) + copied = ret; + _leave(" = %d [waitfail %d]", copied, ret); + return copied; + +} + +/** + * rxrpc_kernel_data_delivered - Record delivery of data message + * @skb: Message holding data + * + * Record the delivery of a data message. This permits RxRPC to keep its + * tracking correct. The socket buffer will be deleted. + */ +void rxrpc_kernel_data_delivered(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_call *call = sp->call; + + ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv); + ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1); + call->rx_data_recv = ntohl(sp->hdr.seq); + + ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten); + rxrpc_free_skb(skb); +} + +EXPORT_SYMBOL(rxrpc_kernel_data_delivered); + +/** + * rxrpc_kernel_is_data_last - Determine if data message is last one + * @skb: Message holding data + * + * Determine if data message is last one for the parent call. + */ +bool rxrpc_kernel_is_data_last(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + ASSERTCMP(skb->mark, ==, RXRPC_SKB_MARK_DATA); + + return sp->hdr.flags & RXRPC_LAST_PACKET; +} + +EXPORT_SYMBOL(rxrpc_kernel_is_data_last); + +/** + * rxrpc_kernel_get_abort_code - Get the abort code from an RxRPC abort message + * @skb: Message indicating an abort + * + * Get the abort code from an RxRPC abort message. + */ +u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + ASSERTCMP(skb->mark, ==, RXRPC_SKB_MARK_REMOTE_ABORT); + + return sp->call->abort_code; +} + +EXPORT_SYMBOL(rxrpc_kernel_get_abort_code); + +/** + * rxrpc_kernel_get_error - Get the error number from an RxRPC error message + * @skb: Message indicating an error + * + * Get the error number from an RxRPC error message. + */ +int rxrpc_kernel_get_error_number(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + return sp->error; +} + +EXPORT_SYMBOL(rxrpc_kernel_get_error_number); diff --git a/net/rxrpc/ar-security.c b/net/rxrpc/ar-security.c new file mode 100644 index 0000000..60d1d36 --- /dev/null +++ b/net/rxrpc/ar-security.c @@ -0,0 +1,258 @@ +/* RxRPC security handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static LIST_HEAD(rxrpc_security_methods); +static DECLARE_RWSEM(rxrpc_security_sem); + +/* + * get an RxRPC security module + */ +static struct rxrpc_security *rxrpc_security_get(struct rxrpc_security *sec) +{ + return try_module_get(sec->owner) ? sec : NULL; +} + +/* + * release an RxRPC security module + */ +static void rxrpc_security_put(struct rxrpc_security *sec) +{ + module_put(sec->owner); +} + +/* + * look up an rxrpc security module + */ +struct rxrpc_security *rxrpc_security_lookup(u8 security_index) +{ + struct rxrpc_security *sec = NULL; + + _enter(""); + + down_read(&rxrpc_security_sem); + + list_for_each_entry(sec, &rxrpc_security_methods, link) { + if (sec->security_index == security_index) { + if (unlikely(!rxrpc_security_get(sec))) + break; + goto out; + } + } + + sec = NULL; +out: + up_read(&rxrpc_security_sem); + _leave(" = %p [%s]", sec, sec ? sec->name : ""); + return sec; +} + +/** + * rxrpc_register_security - register an RxRPC security handler + * @sec: security module + * + * register an RxRPC security handler for use by RxRPC + */ +int rxrpc_register_security(struct rxrpc_security *sec) +{ + struct rxrpc_security *psec; + int ret; + + _enter(""); + down_write(&rxrpc_security_sem); + + ret = -EEXIST; + list_for_each_entry(psec, &rxrpc_security_methods, link) { + if (psec->security_index == sec->security_index) + goto out; + } + + list_add(&sec->link, &rxrpc_security_methods); + + printk(KERN_NOTICE "RxRPC: Registered security type %d '%s'\n", + sec->security_index, sec->name); + ret = 0; + +out: + up_write(&rxrpc_security_sem); + _leave(" = %d", ret); + return ret; +} + +EXPORT_SYMBOL_GPL(rxrpc_register_security); + +/** + * rxrpc_unregister_security - unregister an RxRPC security handler + * @sec: security module + * + * unregister an RxRPC security handler + */ +void rxrpc_unregister_security(struct rxrpc_security *sec) +{ + + _enter(""); + down_write(&rxrpc_security_sem); + list_del_init(&sec->link); + up_write(&rxrpc_security_sem); + + printk(KERN_NOTICE "RxRPC: Unregistered security type %d '%s'\n", + sec->security_index, sec->name); +} + +EXPORT_SYMBOL_GPL(rxrpc_unregister_security); + +/* + * initialise the security on a client connection + */ +int rxrpc_init_client_conn_security(struct rxrpc_connection *conn) +{ + struct rxrpc_security *sec; + struct key *key = conn->key; + int ret; + + _enter("{%d},{%x}", conn->debug_id, key_serial(key)); + + if (!key) + return 0; + + ret = key_validate(key); + if (ret < 0) + return ret; + + sec = rxrpc_security_lookup(key->type_data.x[0]); + if (!sec) + return -EKEYREJECTED; + conn->security = sec; + + ret = conn->security->init_connection_security(conn); + if (ret < 0) { + rxrpc_security_put(conn->security); + conn->security = NULL; + return ret; + } + + _leave(" = 0"); + return 0; +} + +/* + * initialise the security on a server connection + */ +int rxrpc_init_server_conn_security(struct rxrpc_connection *conn) +{ + struct rxrpc_security *sec; + struct rxrpc_local *local = conn->trans->local; + struct rxrpc_sock *rx; + struct key *key; + key_ref_t kref; + char kdesc[5+1+3+1]; + + _enter(""); + + sprintf(kdesc, "%u:%u", ntohs(conn->service_id), conn->security_ix); + + sec = rxrpc_security_lookup(conn->security_ix); + if (!sec) { + _leave(" = -ENOKEY [lookup]"); + return -ENOKEY; + } + + /* find the service */ + read_lock_bh(&local->services_lock); + list_for_each_entry(rx, &local->services, listen_link) { + if (rx->service_id == conn->service_id) + goto found_service; + } + + /* the service appears to have died */ + read_unlock_bh(&local->services_lock); + rxrpc_security_put(sec); + _leave(" = -ENOENT"); + return -ENOENT; + +found_service: + if (!rx->securities) { + read_unlock_bh(&local->services_lock); + rxrpc_security_put(sec); + _leave(" = -ENOKEY"); + return -ENOKEY; + } + + /* look through the service's keyring */ + kref = keyring_search(make_key_ref(rx->securities, 1UL), + &key_type_rxrpc_s, kdesc); + if (IS_ERR(kref)) { + read_unlock_bh(&local->services_lock); + rxrpc_security_put(sec); + _leave(" = %ld [search]", PTR_ERR(kref)); + return PTR_ERR(kref); + } + + key = key_ref_to_ptr(kref); + read_unlock_bh(&local->services_lock); + + conn->server_key = key; + conn->security = sec; + + _leave(" = 0"); + return 0; +} + +/* + * secure a packet prior to transmission + */ +int rxrpc_secure_packet(const struct rxrpc_call *call, + struct sk_buff *skb, + size_t data_size, + void *sechdr) +{ + if (call->conn->security) + return call->conn->security->secure_packet( + call, skb, data_size, sechdr); + return 0; +} + +/* + * secure a packet prior to transmission + */ +int rxrpc_verify_packet(const struct rxrpc_call *call, struct sk_buff *skb, + u32 *_abort_code) +{ + if (call->conn->security) + return call->conn->security->verify_packet( + call, skb, _abort_code); + return 0; +} + +/* + * clear connection security + */ +void rxrpc_clear_conn_security(struct rxrpc_connection *conn) +{ + _enter("{%d}", conn->debug_id); + + if (conn->security) { + conn->security->clear(conn); + rxrpc_security_put(conn->security); + conn->security = NULL; + } + + key_put(conn->key); + key_put(conn->server_key); +} diff --git a/net/rxrpc/ar-skbuff.c b/net/rxrpc/ar-skbuff.c new file mode 100644 index 0000000..de755e0 --- /dev/null +++ b/net/rxrpc/ar-skbuff.c @@ -0,0 +1,132 @@ +/* ar-skbuff.c: socket buffer destruction handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * set up for the ACK at the end of the receive phase when we discard the final + * receive phase data packet + * - called with softirqs disabled + */ +static void rxrpc_request_final_ACK(struct rxrpc_call *call) +{ + /* the call may be aborted before we have a chance to ACK it */ + write_lock(&call->state_lock); + + switch (call->state) { + case RXRPC_CALL_CLIENT_RECV_REPLY: + call->state = RXRPC_CALL_CLIENT_FINAL_ACK; + _debug("request final ACK"); + + /* get an extra ref on the call for the final-ACK generator to + * release */ + rxrpc_get_call(call); + set_bit(RXRPC_CALL_ACK_FINAL, &call->events); + if (try_to_del_timer_sync(&call->ack_timer) >= 0) + rxrpc_queue_call(call); + break; + + case RXRPC_CALL_SERVER_RECV_REQUEST: + call->state = RXRPC_CALL_SERVER_ACK_REQUEST; + default: + break; + } + + write_unlock(&call->state_lock); +} + +/* + * drop the bottom ACK off of the call ACK window and advance the window + */ +static void rxrpc_hard_ACK_data(struct rxrpc_call *call, + struct rxrpc_skb_priv *sp) +{ + int loop; + u32 seq; + + spin_lock_bh(&call->lock); + + _debug("hard ACK #%u", ntohl(sp->hdr.seq)); + + for (loop = 0; loop < RXRPC_ACKR_WINDOW_ASZ; loop++) { + call->ackr_window[loop] >>= 1; + call->ackr_window[loop] |= + call->ackr_window[loop + 1] << (BITS_PER_LONG - 1); + } + + seq = ntohl(sp->hdr.seq); + ASSERTCMP(seq, ==, call->rx_data_eaten + 1); + call->rx_data_eaten = seq; + + if (call->ackr_win_top < UINT_MAX) + call->ackr_win_top++; + + ASSERTIFCMP(call->state <= RXRPC_CALL_COMPLETE, + call->rx_data_post, >=, call->rx_data_recv); + ASSERTIFCMP(call->state <= RXRPC_CALL_COMPLETE, + call->rx_data_recv, >=, call->rx_data_eaten); + + if (sp->hdr.flags & RXRPC_LAST_PACKET) { + rxrpc_request_final_ACK(call); + } else if (atomic_dec_and_test(&call->ackr_not_idle) && + test_and_clear_bit(RXRPC_CALL_TX_SOFT_ACK, &call->flags)) { + _debug("send Rx idle ACK"); + __rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, sp->hdr.serial, + true); + } + + spin_unlock_bh(&call->lock); +} + +/* + * destroy a packet that has an RxRPC control buffer + * - advance the hard-ACK state of the parent call (done here in case something + * in the kernel bypasses recvmsg() and steals the packet directly off of the + * socket receive queue) + */ +void rxrpc_packet_destructor(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_call *call = sp->call; + + _enter("%p{%p}", skb, call); + + if (call) { + /* send the final ACK on a client call */ + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) + rxrpc_hard_ACK_data(call, sp); + rxrpc_put_call(call); + sp->call = NULL; + } + + if (skb->sk) + sock_rfree(skb); + _leave(""); +} + +/** + * rxrpc_kernel_free_skb - Free an RxRPC socket buffer + * @skb: The socket buffer to be freed + * + * Let RxRPC free its own socket buffer, permitting it to maintain debug + * accounting. + */ +void rxrpc_kernel_free_skb(struct sk_buff *skb) +{ + rxrpc_free_skb(skb); +} + +EXPORT_SYMBOL(rxrpc_kernel_free_skb); diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c new file mode 100644 index 0000000..d43d78f --- /dev/null +++ b/net/rxrpc/ar-transport.c @@ -0,0 +1,276 @@ +/* RxRPC point-to-point transport session management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static void rxrpc_transport_reaper(struct work_struct *work); + +static LIST_HEAD(rxrpc_transports); +static DEFINE_RWLOCK(rxrpc_transport_lock); +static unsigned long rxrpc_transport_timeout = 3600 * 24; +static DECLARE_DELAYED_WORK(rxrpc_transport_reap, rxrpc_transport_reaper); + +/* + * allocate a new transport session manager + */ +static struct rxrpc_transport *rxrpc_alloc_transport(struct rxrpc_local *local, + struct rxrpc_peer *peer, + gfp_t gfp) +{ + struct rxrpc_transport *trans; + + _enter(""); + + trans = kzalloc(sizeof(struct rxrpc_transport), gfp); + if (trans) { + trans->local = local; + trans->peer = peer; + INIT_LIST_HEAD(&trans->link); + trans->bundles = RB_ROOT; + trans->client_conns = RB_ROOT; + trans->server_conns = RB_ROOT; + skb_queue_head_init(&trans->error_queue); + spin_lock_init(&trans->client_lock); + rwlock_init(&trans->conn_lock); + atomic_set(&trans->usage, 1); + trans->debug_id = atomic_inc_return(&rxrpc_debug_id); + + if (peer->srx.transport.family == AF_INET) { + switch (peer->srx.transport_type) { + case SOCK_DGRAM: + INIT_WORK(&trans->error_handler, + rxrpc_UDP_error_handler); + break; + default: + BUG(); + break; + } + } else { + BUG(); + } + } + + _leave(" = %p", trans); + return trans; +} + +/* + * obtain a transport session for the nominated endpoints + */ +struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local, + struct rxrpc_peer *peer, + gfp_t gfp) +{ + struct rxrpc_transport *trans, *candidate; + const char *new = "old"; + int usage; + + _enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},", + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port), + NIPQUAD(peer->srx.transport.sin.sin_addr), + ntohs(peer->srx.transport.sin.sin_port)); + + /* search the transport list first */ + read_lock_bh(&rxrpc_transport_lock); + list_for_each_entry(trans, &rxrpc_transports, link) { + if (trans->local == local && trans->peer == peer) + goto found_extant_transport; + } + read_unlock_bh(&rxrpc_transport_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_transport(local, peer, gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + write_lock_bh(&rxrpc_transport_lock); + + list_for_each_entry(trans, &rxrpc_transports, link) { + if (trans->local == local && trans->peer == peer) + goto found_extant_second; + } + + /* we can now add the new candidate to the list */ + trans = candidate; + candidate = NULL; + + rxrpc_get_local(trans->local); + atomic_inc(&trans->peer->usage); + list_add_tail(&trans->link, &rxrpc_transports); + write_unlock_bh(&rxrpc_transport_lock); + new = "new"; + +success: + _net("TRANSPORT %s %d local %d -> peer %d", + new, + trans->debug_id, + trans->local->debug_id, + trans->peer->debug_id); + + _leave(" = %p {u=%d}", trans, atomic_read(&trans->usage)); + return trans; + + /* we found the transport in the list immediately */ +found_extant_transport: + usage = atomic_inc_return(&trans->usage); + read_unlock_bh(&rxrpc_transport_lock); + goto success; + + /* we found the transport on the second time through the list */ +found_extant_second: + usage = atomic_inc_return(&trans->usage); + write_unlock_bh(&rxrpc_transport_lock); + kfree(candidate); + goto success; +} + +/* + * find the transport connecting two endpoints + */ +struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *local, + struct rxrpc_peer *peer) +{ + struct rxrpc_transport *trans; + + _enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},", + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port), + NIPQUAD(peer->srx.transport.sin.sin_addr), + ntohs(peer->srx.transport.sin.sin_port)); + + /* search the transport list */ + read_lock_bh(&rxrpc_transport_lock); + + list_for_each_entry(trans, &rxrpc_transports, link) { + if (trans->local == local && trans->peer == peer) + goto found_extant_transport; + } + + read_unlock_bh(&rxrpc_transport_lock); + _leave(" = NULL"); + return NULL; + +found_extant_transport: + atomic_inc(&trans->usage); + read_unlock_bh(&rxrpc_transport_lock); + _leave(" = %p", trans); + return trans; +} + +/* + * release a transport session + */ +void rxrpc_put_transport(struct rxrpc_transport *trans) +{ + _enter("%p{u=%d}", trans, atomic_read(&trans->usage)); + + ASSERTCMP(atomic_read(&trans->usage), >, 0); + + trans->put_time = xtime.tv_sec; + if (unlikely(atomic_dec_and_test(&trans->usage))) + _debug("zombie"); + /* let the reaper determine the timeout to avoid a race with + * overextending the timeout if the reaper is running at the + * same time */ + rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0); + _leave(""); +} + +/* + * clean up a transport session + */ +static void rxrpc_cleanup_transport(struct rxrpc_transport *trans) +{ + _net("DESTROY TRANS %d", trans->debug_id); + + rxrpc_purge_queue(&trans->error_queue); + + rxrpc_put_local(trans->local); + rxrpc_put_peer(trans->peer); + kfree(trans); +} + +/* + * reap dead transports that have passed their expiry date + */ +static void rxrpc_transport_reaper(struct work_struct *work) +{ + struct rxrpc_transport *trans, *_p; + unsigned long now, earliest, reap_time; + + LIST_HEAD(graveyard); + + _enter(""); + + now = xtime.tv_sec; + earliest = ULONG_MAX; + + /* extract all the transports that have been dead too long */ + write_lock_bh(&rxrpc_transport_lock); + list_for_each_entry_safe(trans, _p, &rxrpc_transports, link) { + _debug("reap TRANS %d { u=%d t=%ld }", + trans->debug_id, atomic_read(&trans->usage), + (long) now - (long) trans->put_time); + + if (likely(atomic_read(&trans->usage) > 0)) + continue; + + reap_time = trans->put_time + rxrpc_transport_timeout; + if (reap_time <= now) + list_move_tail(&trans->link, &graveyard); + else if (reap_time < earliest) + earliest = reap_time; + } + write_unlock_bh(&rxrpc_transport_lock); + + if (earliest != ULONG_MAX) { + _debug("reschedule reaper %ld", (long) earliest - now); + ASSERTCMP(earliest, >, now); + rxrpc_queue_delayed_work(&rxrpc_transport_reap, + (earliest - now) * HZ); + } + + /* then destroy all those pulled out */ + while (!list_empty(&graveyard)) { + trans = list_entry(graveyard.next, struct rxrpc_transport, + link); + list_del_init(&trans->link); + + ASSERTCMP(atomic_read(&trans->usage), ==, 0); + rxrpc_cleanup_transport(trans); + } + + _leave(""); +} + +/* + * preemptively destroy all the transport session records rather than waiting + * for them to time out + */ +void __exit rxrpc_destroy_all_transports(void) +{ + _enter(""); + + rxrpc_transport_timeout = 0; + cancel_delayed_work(&rxrpc_transport_reap); + rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0); + + _leave(""); +} diff --git a/net/rxrpc/call.c b/net/rxrpc/call.c deleted file mode 100644 index d07122b..0000000 --- a/net/rxrpc/call.c +++ /dev/null @@ -1,2277 +0,0 @@ -/* call.c: Rx call routines - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -__RXACCT_DECL(atomic_t rxrpc_call_count); -__RXACCT_DECL(atomic_t rxrpc_message_count); - -LIST_HEAD(rxrpc_calls); -DECLARE_RWSEM(rxrpc_calls_sem); - -unsigned rxrpc_call_rcv_timeout = HZ/3; -static unsigned rxrpc_call_acks_timeout = HZ/3; -static unsigned rxrpc_call_dfr_ack_timeout = HZ/20; -static unsigned short rxrpc_call_max_resend = HZ/10; - -const char *rxrpc_call_states[] = { - "COMPLETE", - "ERROR", - "SRVR_RCV_OPID", - "SRVR_RCV_ARGS", - "SRVR_GOT_ARGS", - "SRVR_SND_REPLY", - "SRVR_RCV_FINAL_ACK", - "CLNT_SND_ARGS", - "CLNT_RCV_REPLY", - "CLNT_GOT_REPLY" -}; - -const char *rxrpc_call_error_states[] = { - "NO_ERROR", - "LOCAL_ABORT", - "PEER_ABORT", - "LOCAL_ERROR", - "REMOTE_ERROR" -}; - -const char *rxrpc_pkts[] = { - "?00", - "data", "ack", "busy", "abort", "ackall", "chall", "resp", "debug", - "?09", "?10", "?11", "?12", "?13", "?14", "?15" -}; - -static const char *rxrpc_acks[] = { - "---", "REQ", "DUP", "SEQ", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL", - "-?-" -}; - -static const char _acktype[] = "NA-"; - -static void rxrpc_call_receive_packet(struct rxrpc_call *call); -static void rxrpc_call_receive_data_packet(struct rxrpc_call *call, - struct rxrpc_message *msg); -static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call, - struct rxrpc_message *msg); -static void rxrpc_call_definitively_ACK(struct rxrpc_call *call, - rxrpc_seq_t higest); -static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest); -static int __rxrpc_call_read_data(struct rxrpc_call *call); - -static int rxrpc_call_record_ACK(struct rxrpc_call *call, - struct rxrpc_message *msg, - rxrpc_seq_t seq, - size_t count); - -static int rxrpc_call_flush(struct rxrpc_call *call); - -#define _state(call) \ - _debug("[[[ state %s ]]]", rxrpc_call_states[call->app_call_state]); - -static void rxrpc_call_default_attn_func(struct rxrpc_call *call) -{ - wake_up(&call->waitq); -} - -static void rxrpc_call_default_error_func(struct rxrpc_call *call) -{ - wake_up(&call->waitq); -} - -static void rxrpc_call_default_aemap_func(struct rxrpc_call *call) -{ - switch (call->app_err_state) { - case RXRPC_ESTATE_LOCAL_ABORT: - call->app_abort_code = -call->app_errno; - case RXRPC_ESTATE_PEER_ABORT: - call->app_errno = -ECONNABORTED; - default: - break; - } -} - -static void __rxrpc_call_acks_timeout(unsigned long _call) -{ - struct rxrpc_call *call = (struct rxrpc_call *) _call; - - _debug("ACKS TIMEOUT %05lu", jiffies - call->cjif); - - call->flags |= RXRPC_CALL_ACKS_TIMO; - rxrpc_krxiod_queue_call(call); -} - -static void __rxrpc_call_rcv_timeout(unsigned long _call) -{ - struct rxrpc_call *call = (struct rxrpc_call *) _call; - - _debug("RCV TIMEOUT %05lu", jiffies - call->cjif); - - call->flags |= RXRPC_CALL_RCV_TIMO; - rxrpc_krxiod_queue_call(call); -} - -static void __rxrpc_call_ackr_timeout(unsigned long _call) -{ - struct rxrpc_call *call = (struct rxrpc_call *) _call; - - _debug("ACKR TIMEOUT %05lu",jiffies - call->cjif); - - call->flags |= RXRPC_CALL_ACKR_TIMO; - rxrpc_krxiod_queue_call(call); -} - -/*****************************************************************************/ -/* - * calculate a timeout based on an RTT value - */ -static inline unsigned long __rxrpc_rtt_based_timeout(struct rxrpc_call *call, - unsigned long val) -{ - unsigned long expiry = call->conn->peer->rtt / (1000000 / HZ); - - expiry += 10; - if (expiry < HZ / 25) - expiry = HZ / 25; - if (expiry > HZ) - expiry = HZ; - - _leave(" = %lu jiffies", expiry); - return jiffies + expiry; -} /* end __rxrpc_rtt_based_timeout() */ - -/*****************************************************************************/ -/* - * create a new call record - */ -static inline int __rxrpc_create_call(struct rxrpc_connection *conn, - struct rxrpc_call **_call) -{ - struct rxrpc_call *call; - - _enter("%p", conn); - - /* allocate and initialise a call record */ - call = (struct rxrpc_call *) get_zeroed_page(GFP_KERNEL); - if (!call) { - _leave(" ENOMEM"); - return -ENOMEM; - } - - atomic_set(&call->usage, 1); - - init_waitqueue_head(&call->waitq); - spin_lock_init(&call->lock); - INIT_LIST_HEAD(&call->link); - INIT_LIST_HEAD(&call->acks_pendq); - INIT_LIST_HEAD(&call->rcv_receiveq); - INIT_LIST_HEAD(&call->rcv_krxiodq_lk); - INIT_LIST_HEAD(&call->app_readyq); - INIT_LIST_HEAD(&call->app_unreadyq); - INIT_LIST_HEAD(&call->app_link); - INIT_LIST_HEAD(&call->app_attn_link); - - init_timer(&call->acks_timeout); - call->acks_timeout.data = (unsigned long) call; - call->acks_timeout.function = __rxrpc_call_acks_timeout; - - init_timer(&call->rcv_timeout); - call->rcv_timeout.data = (unsigned long) call; - call->rcv_timeout.function = __rxrpc_call_rcv_timeout; - - init_timer(&call->ackr_dfr_timo); - call->ackr_dfr_timo.data = (unsigned long) call; - call->ackr_dfr_timo.function = __rxrpc_call_ackr_timeout; - - call->conn = conn; - call->ackr_win_bot = 1; - call->ackr_win_top = call->ackr_win_bot + RXRPC_CALL_ACK_WINDOW_SIZE - 1; - call->ackr_prev_seq = 0; - call->app_mark = RXRPC_APP_MARK_EOF; - call->app_attn_func = rxrpc_call_default_attn_func; - call->app_error_func = rxrpc_call_default_error_func; - call->app_aemap_func = rxrpc_call_default_aemap_func; - call->app_scr_alloc = call->app_scratch; - - call->cjif = jiffies; - - _leave(" = 0 (%p)", call); - - *_call = call; - - return 0; -} /* end __rxrpc_create_call() */ - -/*****************************************************************************/ -/* - * create a new call record for outgoing calls - */ -int rxrpc_create_call(struct rxrpc_connection *conn, - rxrpc_call_attn_func_t attn, - rxrpc_call_error_func_t error, - rxrpc_call_aemap_func_t aemap, - struct rxrpc_call **_call) -{ - DECLARE_WAITQUEUE(myself, current); - - struct rxrpc_call *call; - int ret, cix, loop; - - _enter("%p", conn); - - /* allocate and initialise a call record */ - ret = __rxrpc_create_call(conn, &call); - if (ret < 0) { - _leave(" = %d", ret); - return ret; - } - - call->app_call_state = RXRPC_CSTATE_CLNT_SND_ARGS; - if (attn) - call->app_attn_func = attn; - if (error) - call->app_error_func = error; - if (aemap) - call->app_aemap_func = aemap; - - _state(call); - - spin_lock(&conn->lock); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&conn->chanwait, &myself); - - try_again: - /* try to find an unused channel */ - for (cix = 0; cix < 4; cix++) - if (!conn->channels[cix]) - goto obtained_chan; - - /* no free channels - wait for one to become available */ - ret = -EINTR; - if (signal_pending(current)) - goto error_unwait; - - spin_unlock(&conn->lock); - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock(&conn->lock); - goto try_again; - - /* got a channel - now attach to the connection */ - obtained_chan: - remove_wait_queue(&conn->chanwait, &myself); - set_current_state(TASK_RUNNING); - - /* concoct a unique call number */ - next_callid: - call->call_id = htonl(++conn->call_counter); - for (loop = 0; loop < 4; loop++) - if (conn->channels[loop] && - conn->channels[loop]->call_id == call->call_id) - goto next_callid; - - rxrpc_get_connection(conn); - conn->channels[cix] = call; /* assign _after_ done callid check loop */ - do_gettimeofday(&conn->atime); - call->chan_ix = htonl(cix); - - spin_unlock(&conn->lock); - - down_write(&rxrpc_calls_sem); - list_add_tail(&call->call_link, &rxrpc_calls); - up_write(&rxrpc_calls_sem); - - __RXACCT(atomic_inc(&rxrpc_call_count)); - *_call = call; - - _leave(" = 0 (call=%p cix=%u)", call, cix); - return 0; - - error_unwait: - remove_wait_queue(&conn->chanwait, &myself); - set_current_state(TASK_RUNNING); - spin_unlock(&conn->lock); - - free_page((unsigned long) call); - _leave(" = %d", ret); - return ret; -} /* end rxrpc_create_call() */ - -/*****************************************************************************/ -/* - * create a new call record for incoming calls - */ -int rxrpc_incoming_call(struct rxrpc_connection *conn, - struct rxrpc_message *msg, - struct rxrpc_call **_call) -{ - struct rxrpc_call *call; - unsigned cix; - int ret; - - cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK; - - _enter("%p,%u,%u", conn, ntohl(msg->hdr.callNumber), cix); - - /* allocate and initialise a call record */ - ret = __rxrpc_create_call(conn, &call); - if (ret < 0) { - _leave(" = %d", ret); - return ret; - } - - call->pkt_rcv_count = 1; - call->app_call_state = RXRPC_CSTATE_SRVR_RCV_OPID; - call->app_mark = sizeof(uint32_t); - - _state(call); - - /* attach to the connection */ - ret = -EBUSY; - call->chan_ix = htonl(cix); - call->call_id = msg->hdr.callNumber; - - spin_lock(&conn->lock); - - if (!conn->channels[cix] || - conn->channels[cix]->app_call_state == RXRPC_CSTATE_COMPLETE || - conn->channels[cix]->app_call_state == RXRPC_CSTATE_ERROR - ) { - conn->channels[cix] = call; - rxrpc_get_connection(conn); - ret = 0; - } - - spin_unlock(&conn->lock); - - if (ret < 0) { - free_page((unsigned long) call); - call = NULL; - } - - if (ret == 0) { - down_write(&rxrpc_calls_sem); - list_add_tail(&call->call_link, &rxrpc_calls); - up_write(&rxrpc_calls_sem); - __RXACCT(atomic_inc(&rxrpc_call_count)); - *_call = call; - } - - _leave(" = %d [%p]", ret, call); - return ret; -} /* end rxrpc_incoming_call() */ - -/*****************************************************************************/ -/* - * free a call record - */ -void rxrpc_put_call(struct rxrpc_call *call) -{ - struct rxrpc_connection *conn = call->conn; - struct rxrpc_message *msg; - - _enter("%p{u=%d}",call,atomic_read(&call->usage)); - - /* sanity check */ - if (atomic_read(&call->usage) <= 0) - BUG(); - - /* to prevent a race, the decrement and the de-list must be effectively - * atomic */ - spin_lock(&conn->lock); - if (likely(!atomic_dec_and_test(&call->usage))) { - spin_unlock(&conn->lock); - _leave(""); - return; - } - - if (conn->channels[ntohl(call->chan_ix)] == call) - conn->channels[ntohl(call->chan_ix)] = NULL; - - spin_unlock(&conn->lock); - - wake_up(&conn->chanwait); - - rxrpc_put_connection(conn); - - /* clear the timers and dequeue from krxiod */ - del_timer_sync(&call->acks_timeout); - del_timer_sync(&call->rcv_timeout); - del_timer_sync(&call->ackr_dfr_timo); - - rxrpc_krxiod_dequeue_call(call); - - /* clean up the contents of the struct */ - if (call->snd_nextmsg) - rxrpc_put_message(call->snd_nextmsg); - - if (call->snd_ping) - rxrpc_put_message(call->snd_ping); - - while (!list_empty(&call->acks_pendq)) { - msg = list_entry(call->acks_pendq.next, - struct rxrpc_message, link); - list_del(&msg->link); - rxrpc_put_message(msg); - } - - while (!list_empty(&call->rcv_receiveq)) { - msg = list_entry(call->rcv_receiveq.next, - struct rxrpc_message, link); - list_del(&msg->link); - rxrpc_put_message(msg); - } - - while (!list_empty(&call->app_readyq)) { - msg = list_entry(call->app_readyq.next, - struct rxrpc_message, link); - list_del(&msg->link); - rxrpc_put_message(msg); - } - - while (!list_empty(&call->app_unreadyq)) { - msg = list_entry(call->app_unreadyq.next, - struct rxrpc_message, link); - list_del(&msg->link); - rxrpc_put_message(msg); - } - - module_put(call->owner); - - down_write(&rxrpc_calls_sem); - list_del(&call->call_link); - up_write(&rxrpc_calls_sem); - - __RXACCT(atomic_dec(&rxrpc_call_count)); - free_page((unsigned long) call); - - _leave(" [destroyed]"); -} /* end rxrpc_put_call() */ - -/*****************************************************************************/ -/* - * actually generate a normal ACK - */ -static inline int __rxrpc_call_gen_normal_ACK(struct rxrpc_call *call, - rxrpc_seq_t seq) -{ - struct rxrpc_message *msg; - struct kvec diov[3]; - __be32 aux[4]; - int delta, ret; - - /* ACKs default to DELAY */ - if (!call->ackr.reason) - call->ackr.reason = RXRPC_ACK_DELAY; - - _proto("Rx %05lu Sending ACK { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", - jiffies - call->cjif, - ntohs(call->ackr.maxSkew), - ntohl(call->ackr.firstPacket), - ntohl(call->ackr.previousPacket), - ntohl(call->ackr.serial), - rxrpc_acks[call->ackr.reason], - call->ackr.nAcks); - - aux[0] = htonl(call->conn->peer->if_mtu); /* interface MTU */ - aux[1] = htonl(1444); /* max MTU */ - aux[2] = htonl(16); /* rwind */ - aux[3] = htonl(4); /* max packets */ - - diov[0].iov_len = sizeof(struct rxrpc_ackpacket); - diov[0].iov_base = &call->ackr; - diov[1].iov_len = call->ackr_pend_cnt + 3; - diov[1].iov_base = call->ackr_array; - diov[2].iov_len = sizeof(aux); - diov[2].iov_base = &aux; - - /* build and send the message */ - ret = rxrpc_conn_newmsg(call->conn,call, RXRPC_PACKET_TYPE_ACK, - 3, diov, GFP_KERNEL, &msg); - if (ret < 0) - goto out; - - msg->seq = seq; - msg->hdr.seq = htonl(seq); - msg->hdr.flags |= RXRPC_SLOW_START_OK; - - ret = rxrpc_conn_sendmsg(call->conn, msg); - rxrpc_put_message(msg); - if (ret < 0) - goto out; - call->pkt_snd_count++; - - /* count how many actual ACKs there were at the front */ - for (delta = 0; delta < call->ackr_pend_cnt; delta++) - if (call->ackr_array[delta] != RXRPC_ACK_TYPE_ACK) - break; - - call->ackr_pend_cnt -= delta; /* all ACK'd to this point */ - - /* crank the ACK window around */ - if (delta == 0) { - /* un-ACK'd window */ - } - else if (delta < RXRPC_CALL_ACK_WINDOW_SIZE) { - /* partially ACK'd window - * - shuffle down to avoid losing out-of-sequence packets - */ - call->ackr_win_bot += delta; - call->ackr_win_top += delta; - - memmove(&call->ackr_array[0], - &call->ackr_array[delta], - call->ackr_pend_cnt); - - memset(&call->ackr_array[call->ackr_pend_cnt], - RXRPC_ACK_TYPE_NACK, - sizeof(call->ackr_array) - call->ackr_pend_cnt); - } - else { - /* fully ACK'd window - * - just clear the whole thing - */ - memset(&call->ackr_array, - RXRPC_ACK_TYPE_NACK, - sizeof(call->ackr_array)); - } - - /* clear this ACK */ - memset(&call->ackr, 0, sizeof(call->ackr)); - - out: - if (!call->app_call_state) - printk("___ STATE 0 ___\n"); - return ret; -} /* end __rxrpc_call_gen_normal_ACK() */ - -/*****************************************************************************/ -/* - * note the reception of a packet in the call's ACK records and generate an - * appropriate ACK packet if necessary - * - returns 0 if packet should be processed, 1 if packet should be ignored - * and -ve on an error - */ -static int rxrpc_call_generate_ACK(struct rxrpc_call *call, - struct rxrpc_header *hdr, - struct rxrpc_ackpacket *ack) -{ - struct rxrpc_message *msg; - rxrpc_seq_t seq; - unsigned offset; - int ret = 0, err; - u8 special_ACK, do_ACK, force; - - _enter("%p,%p { seq=%d tp=%d fl=%02x }", - call, hdr, ntohl(hdr->seq), hdr->type, hdr->flags); - - seq = ntohl(hdr->seq); - offset = seq - call->ackr_win_bot; - do_ACK = RXRPC_ACK_DELAY; - special_ACK = 0; - force = (seq == 1); - - if (call->ackr_high_seq < seq) - call->ackr_high_seq = seq; - - /* deal with generation of obvious special ACKs first */ - if (ack && ack->reason == RXRPC_ACK_PING) { - special_ACK = RXRPC_ACK_PING_RESPONSE; - ret = 1; - goto gen_ACK; - } - - if (seq < call->ackr_win_bot) { - special_ACK = RXRPC_ACK_DUPLICATE; - ret = 1; - goto gen_ACK; - } - - if (seq >= call->ackr_win_top) { - special_ACK = RXRPC_ACK_EXCEEDS_WINDOW; - ret = 1; - goto gen_ACK; - } - - if (call->ackr_array[offset] != RXRPC_ACK_TYPE_NACK) { - special_ACK = RXRPC_ACK_DUPLICATE; - ret = 1; - goto gen_ACK; - } - - /* okay... it's a normal data packet inside the ACK window */ - call->ackr_array[offset] = RXRPC_ACK_TYPE_ACK; - - if (offset < call->ackr_pend_cnt) { - } - else if (offset > call->ackr_pend_cnt) { - do_ACK = RXRPC_ACK_OUT_OF_SEQUENCE; - call->ackr_pend_cnt = offset; - goto gen_ACK; - } - - if (hdr->flags & RXRPC_REQUEST_ACK) { - do_ACK = RXRPC_ACK_REQUESTED; - } - - /* generate an ACK on the final packet of a reply just received */ - if (hdr->flags & RXRPC_LAST_PACKET) { - if (call->conn->out_clientflag) - force = 1; - } - else if (!(hdr->flags & RXRPC_MORE_PACKETS)) { - do_ACK = RXRPC_ACK_REQUESTED; - } - - /* re-ACK packets previously received out-of-order */ - for (offset++; offset < RXRPC_CALL_ACK_WINDOW_SIZE; offset++) - if (call->ackr_array[offset] != RXRPC_ACK_TYPE_ACK) - break; - - call->ackr_pend_cnt = offset; - - /* generate an ACK if we fill up the window */ - if (call->ackr_pend_cnt >= RXRPC_CALL_ACK_WINDOW_SIZE) - force = 1; - - gen_ACK: - _debug("%05lu ACKs pend=%u norm=%s special=%s%s", - jiffies - call->cjif, - call->ackr_pend_cnt, - rxrpc_acks[do_ACK], - rxrpc_acks[special_ACK], - force ? " immediate" : - do_ACK == RXRPC_ACK_REQUESTED ? " merge-req" : - hdr->flags & RXRPC_LAST_PACKET ? " finalise" : - " defer" - ); - - /* send any pending normal ACKs if need be */ - if (call->ackr_pend_cnt > 0) { - /* fill out the appropriate form */ - call->ackr.bufferSpace = htons(RXRPC_CALL_ACK_WINDOW_SIZE); - call->ackr.maxSkew = htons(min(call->ackr_high_seq - seq, - 65535U)); - call->ackr.firstPacket = htonl(call->ackr_win_bot); - call->ackr.previousPacket = call->ackr_prev_seq; - call->ackr.serial = hdr->serial; - call->ackr.nAcks = call->ackr_pend_cnt; - - if (do_ACK == RXRPC_ACK_REQUESTED) - call->ackr.reason = do_ACK; - - /* generate the ACK immediately if necessary */ - if (special_ACK || force) { - err = __rxrpc_call_gen_normal_ACK( - call, do_ACK == RXRPC_ACK_DELAY ? 0 : seq); - if (err < 0) { - ret = err; - goto out; - } - } - } - - if (call->ackr.reason == RXRPC_ACK_REQUESTED) - call->ackr_dfr_seq = seq; - - /* start the ACK timer if not running if there are any pending deferred - * ACKs */ - if (call->ackr_pend_cnt > 0 && - call->ackr.reason != RXRPC_ACK_REQUESTED && - !timer_pending(&call->ackr_dfr_timo) - ) { - unsigned long timo; - - timo = rxrpc_call_dfr_ack_timeout + jiffies; - - _debug("START ACKR TIMER for cj=%lu", timo - call->cjif); - - spin_lock(&call->lock); - mod_timer(&call->ackr_dfr_timo, timo); - spin_unlock(&call->lock); - } - else if ((call->ackr_pend_cnt == 0 || - call->ackr.reason == RXRPC_ACK_REQUESTED) && - timer_pending(&call->ackr_dfr_timo) - ) { - /* stop timer if no pending ACKs */ - _debug("CLEAR ACKR TIMER"); - del_timer_sync(&call->ackr_dfr_timo); - } - - /* send a special ACK if one is required */ - if (special_ACK) { - struct rxrpc_ackpacket ack; - struct kvec diov[2]; - uint8_t acks[1] = { RXRPC_ACK_TYPE_ACK }; - - /* fill out the appropriate form */ - ack.bufferSpace = htons(RXRPC_CALL_ACK_WINDOW_SIZE); - ack.maxSkew = htons(min(call->ackr_high_seq - seq, - 65535U)); - ack.firstPacket = htonl(call->ackr_win_bot); - ack.previousPacket = call->ackr_prev_seq; - ack.serial = hdr->serial; - ack.reason = special_ACK; - ack.nAcks = 0; - - _proto("Rx Sending s-ACK" - " { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", - ntohs(ack.maxSkew), - ntohl(ack.firstPacket), - ntohl(ack.previousPacket), - ntohl(ack.serial), - rxrpc_acks[ack.reason], - ack.nAcks); - - diov[0].iov_len = sizeof(struct rxrpc_ackpacket); - diov[0].iov_base = &ack; - diov[1].iov_len = sizeof(acks); - diov[1].iov_base = acks; - - /* build and send the message */ - err = rxrpc_conn_newmsg(call->conn,call, RXRPC_PACKET_TYPE_ACK, - hdr->seq ? 2 : 1, diov, - GFP_KERNEL, - &msg); - if (err < 0) { - ret = err; - goto out; - } - - msg->seq = seq; - msg->hdr.seq = htonl(seq); - msg->hdr.flags |= RXRPC_SLOW_START_OK; - - err = rxrpc_conn_sendmsg(call->conn, msg); - rxrpc_put_message(msg); - if (err < 0) { - ret = err; - goto out; - } - call->pkt_snd_count++; - } - - out: - if (hdr->seq) - call->ackr_prev_seq = hdr->seq; - - _leave(" = %d", ret); - return ret; -} /* end rxrpc_call_generate_ACK() */ - -/*****************************************************************************/ -/* - * handle work to be done on a call - * - includes packet reception and timeout processing - */ -void rxrpc_call_do_stuff(struct rxrpc_call *call) -{ - _enter("%p{flags=%lx}", call, call->flags); - - /* handle packet reception */ - if (call->flags & RXRPC_CALL_RCV_PKT) { - _debug("- receive packet"); - call->flags &= ~RXRPC_CALL_RCV_PKT; - rxrpc_call_receive_packet(call); - } - - /* handle overdue ACKs */ - if (call->flags & RXRPC_CALL_ACKS_TIMO) { - _debug("- overdue ACK timeout"); - call->flags &= ~RXRPC_CALL_ACKS_TIMO; - rxrpc_call_resend(call, call->snd_seq_count); - } - - /* handle lack of reception */ - if (call->flags & RXRPC_CALL_RCV_TIMO) { - _debug("- reception timeout"); - call->flags &= ~RXRPC_CALL_RCV_TIMO; - rxrpc_call_abort(call, -EIO); - } - - /* handle deferred ACKs */ - if (call->flags & RXRPC_CALL_ACKR_TIMO || - (call->ackr.nAcks > 0 && call->ackr.reason == RXRPC_ACK_REQUESTED) - ) { - _debug("- deferred ACK timeout: cj=%05lu r=%s n=%u", - jiffies - call->cjif, - rxrpc_acks[call->ackr.reason], - call->ackr.nAcks); - - call->flags &= ~RXRPC_CALL_ACKR_TIMO; - - if (call->ackr.nAcks > 0 && - call->app_call_state != RXRPC_CSTATE_ERROR) { - /* generate ACK */ - __rxrpc_call_gen_normal_ACK(call, call->ackr_dfr_seq); - call->ackr_dfr_seq = 0; - } - } - - _leave(""); - -} /* end rxrpc_call_do_stuff() */ - -/*****************************************************************************/ -/* - * send an abort message at call or connection level - * - must be called with call->lock held - * - the supplied error code is sent as the packet data - */ -static int __rxrpc_call_abort(struct rxrpc_call *call, int errno) -{ - struct rxrpc_connection *conn = call->conn; - struct rxrpc_message *msg; - struct kvec diov[1]; - int ret; - __be32 _error; - - _enter("%p{%08x},%p{%d},%d", - conn, ntohl(conn->conn_id), call, ntohl(call->call_id), errno); - - /* if this call is already aborted, then just wake up any waiters */ - if (call->app_call_state == RXRPC_CSTATE_ERROR) { - spin_unlock(&call->lock); - call->app_error_func(call); - _leave(" = 0"); - return 0; - } - - rxrpc_get_call(call); - - /* change the state _with_ the lock still held */ - call->app_call_state = RXRPC_CSTATE_ERROR; - call->app_err_state = RXRPC_ESTATE_LOCAL_ABORT; - call->app_errno = errno; - call->app_mark = RXRPC_APP_MARK_EOF; - call->app_read_buf = NULL; - call->app_async_read = 0; - - _state(call); - - /* ask the app to translate the error code */ - call->app_aemap_func(call); - - spin_unlock(&call->lock); - - /* flush any outstanding ACKs */ - del_timer_sync(&call->acks_timeout); - del_timer_sync(&call->rcv_timeout); - del_timer_sync(&call->ackr_dfr_timo); - - if (rxrpc_call_is_ack_pending(call)) - __rxrpc_call_gen_normal_ACK(call, 0); - - /* send the abort packet only if we actually traded some other - * packets */ - ret = 0; - if (call->pkt_snd_count || call->pkt_rcv_count) { - /* actually send the abort */ - _proto("Rx Sending Call ABORT { data=%d }", - call->app_abort_code); - - _error = htonl(call->app_abort_code); - - diov[0].iov_len = sizeof(_error); - diov[0].iov_base = &_error; - - ret = rxrpc_conn_newmsg(conn, call, RXRPC_PACKET_TYPE_ABORT, - 1, diov, GFP_KERNEL, &msg); - if (ret == 0) { - ret = rxrpc_conn_sendmsg(conn, msg); - rxrpc_put_message(msg); - } - } - - /* tell the app layer to let go */ - call->app_error_func(call); - - rxrpc_put_call(call); - - _leave(" = %d", ret); - return ret; -} /* end __rxrpc_call_abort() */ - -/*****************************************************************************/ -/* - * send an abort message at call or connection level - * - the supplied error code is sent as the packet data - */ -int rxrpc_call_abort(struct rxrpc_call *call, int error) -{ - spin_lock(&call->lock); - - return __rxrpc_call_abort(call, error); - -} /* end rxrpc_call_abort() */ - -/*****************************************************************************/ -/* - * process packets waiting for this call - */ -static void rxrpc_call_receive_packet(struct rxrpc_call *call) -{ - struct rxrpc_message *msg; - struct list_head *_p; - - _enter("%p", call); - - rxrpc_get_call(call); /* must not go away too soon if aborted by - * app-layer */ - - while (!list_empty(&call->rcv_receiveq)) { - /* try to get next packet */ - _p = NULL; - spin_lock(&call->lock); - if (!list_empty(&call->rcv_receiveq)) { - _p = call->rcv_receiveq.next; - list_del_init(_p); - } - spin_unlock(&call->lock); - - if (!_p) - break; - - msg = list_entry(_p, struct rxrpc_message, link); - - _proto("Rx %05lu Received %s packet (%%%u,#%u,%c%c%c%c%c)", - jiffies - call->cjif, - rxrpc_pkts[msg->hdr.type], - ntohl(msg->hdr.serial), - msg->seq, - msg->hdr.flags & RXRPC_JUMBO_PACKET ? 'j' : '-', - msg->hdr.flags & RXRPC_MORE_PACKETS ? 'm' : '-', - msg->hdr.flags & RXRPC_LAST_PACKET ? 'l' : '-', - msg->hdr.flags & RXRPC_REQUEST_ACK ? 'r' : '-', - msg->hdr.flags & RXRPC_CLIENT_INITIATED ? 'C' : 'S' - ); - - switch (msg->hdr.type) { - /* deal with data packets */ - case RXRPC_PACKET_TYPE_DATA: - /* ACK the packet if necessary */ - switch (rxrpc_call_generate_ACK(call, &msg->hdr, - NULL)) { - case 0: /* useful packet */ - rxrpc_call_receive_data_packet(call, msg); - break; - case 1: /* duplicate or out-of-window packet */ - break; - default: - rxrpc_put_message(msg); - goto out; - } - break; - - /* deal with ACK packets */ - case RXRPC_PACKET_TYPE_ACK: - rxrpc_call_receive_ack_packet(call, msg); - break; - - /* deal with abort packets */ - case RXRPC_PACKET_TYPE_ABORT: { - __be32 _dbuf, *dp; - - dp = skb_header_pointer(msg->pkt, msg->offset, - sizeof(_dbuf), &_dbuf); - if (dp == NULL) - printk("Rx Received short ABORT packet\n"); - - _proto("Rx Received Call ABORT { data=%d }", - (dp ? ntohl(*dp) : 0)); - - spin_lock(&call->lock); - call->app_call_state = RXRPC_CSTATE_ERROR; - call->app_err_state = RXRPC_ESTATE_PEER_ABORT; - call->app_abort_code = (dp ? ntohl(*dp) : 0); - call->app_errno = -ECONNABORTED; - call->app_mark = RXRPC_APP_MARK_EOF; - call->app_read_buf = NULL; - call->app_async_read = 0; - - /* ask the app to translate the error code */ - call->app_aemap_func(call); - _state(call); - spin_unlock(&call->lock); - call->app_error_func(call); - break; - } - default: - /* deal with other packet types */ - _proto("Rx Unsupported packet type %u (#%u)", - msg->hdr.type, msg->seq); - break; - } - - rxrpc_put_message(msg); - } - - out: - rxrpc_put_call(call); - _leave(""); -} /* end rxrpc_call_receive_packet() */ - -/*****************************************************************************/ -/* - * process next data packet - * - as the next data packet arrives: - * - it is queued on app_readyq _if_ it is the next one expected - * (app_ready_seq+1) - * - it is queued on app_unreadyq _if_ it is not the next one expected - * - if a packet placed on app_readyq completely fills a hole leading up to - * the first packet on app_unreadyq, then packets now in sequence are - * tranferred to app_readyq - * - the application layer can only see packets on app_readyq - * (app_ready_qty bytes) - * - the application layer is prodded every time a new packet arrives - */ -static void rxrpc_call_receive_data_packet(struct rxrpc_call *call, - struct rxrpc_message *msg) -{ - const struct rxrpc_operation *optbl, *op; - struct rxrpc_message *pmsg; - struct list_head *_p; - int ret, lo, hi, rmtimo; - __be32 opid; - - _enter("%p{%u},%p{%u}", call, ntohl(call->call_id), msg, msg->seq); - - rxrpc_get_message(msg); - - /* add to the unready queue if we'd have to create a hole in the ready - * queue otherwise */ - if (msg->seq != call->app_ready_seq + 1) { - _debug("Call add packet %d to unreadyq", msg->seq); - - /* insert in seq order */ - list_for_each(_p, &call->app_unreadyq) { - pmsg = list_entry(_p, struct rxrpc_message, link); - if (pmsg->seq > msg->seq) - break; - } - - list_add_tail(&msg->link, _p); - - _leave(" [unreadyq]"); - return; - } - - /* next in sequence - simply append into the call's ready queue */ - _debug("Call add packet %d to readyq (+%Zd => %Zd bytes)", - msg->seq, msg->dsize, call->app_ready_qty); - - spin_lock(&call->lock); - call->app_ready_seq = msg->seq; - call->app_ready_qty += msg->dsize; - list_add_tail(&msg->link, &call->app_readyq); - - /* move unready packets to the readyq if we got rid of a hole */ - while (!list_empty(&call->app_unreadyq)) { - pmsg = list_entry(call->app_unreadyq.next, - struct rxrpc_message, link); - - if (pmsg->seq != call->app_ready_seq + 1) - break; - - /* next in sequence - just move list-to-list */ - _debug("Call transfer packet %d to readyq (+%Zd => %Zd bytes)", - pmsg->seq, pmsg->dsize, call->app_ready_qty); - - call->app_ready_seq = pmsg->seq; - call->app_ready_qty += pmsg->dsize; - list_move_tail(&pmsg->link, &call->app_readyq); - } - - /* see if we've got the last packet yet */ - if (!list_empty(&call->app_readyq)) { - pmsg = list_entry(call->app_readyq.prev, - struct rxrpc_message, link); - if (pmsg->hdr.flags & RXRPC_LAST_PACKET) { - call->app_last_rcv = 1; - _debug("Last packet on readyq"); - } - } - - switch (call->app_call_state) { - /* do nothing if call already aborted */ - case RXRPC_CSTATE_ERROR: - spin_unlock(&call->lock); - _leave(" [error]"); - return; - - /* extract the operation ID from an incoming call if that's not - * yet been done */ - case RXRPC_CSTATE_SRVR_RCV_OPID: - spin_unlock(&call->lock); - - /* handle as yet insufficient data for the operation ID */ - if (call->app_ready_qty < 4) { - if (call->app_last_rcv) - /* trouble - last packet seen */ - rxrpc_call_abort(call, -EINVAL); - - _leave(""); - return; - } - - /* pull the operation ID out of the buffer */ - ret = rxrpc_call_read_data(call, &opid, sizeof(opid), 0); - if (ret < 0) { - printk("Unexpected error from read-data: %d\n", ret); - if (call->app_call_state != RXRPC_CSTATE_ERROR) - rxrpc_call_abort(call, ret); - _leave(""); - return; - } - call->app_opcode = ntohl(opid); - - /* locate the operation in the available ops table */ - optbl = call->conn->service->ops_begin; - lo = 0; - hi = call->conn->service->ops_end - optbl; - - while (lo < hi) { - int mid = (hi + lo) / 2; - op = &optbl[mid]; - if (call->app_opcode == op->id) - goto found_op; - if (call->app_opcode > op->id) - lo = mid + 1; - else - hi = mid; - } - - /* search failed */ - kproto("Rx Client requested operation %d from %s service", - call->app_opcode, call->conn->service->name); - rxrpc_call_abort(call, -EINVAL); - _leave(" [inval]"); - return; - - found_op: - _proto("Rx Client requested operation %s from %s service", - op->name, call->conn->service->name); - - /* we're now waiting for the argument block (unless the call - * was aborted) */ - spin_lock(&call->lock); - if (call->app_call_state == RXRPC_CSTATE_SRVR_RCV_OPID || - call->app_call_state == RXRPC_CSTATE_SRVR_SND_REPLY) { - if (!call->app_last_rcv) - call->app_call_state = - RXRPC_CSTATE_SRVR_RCV_ARGS; - else if (call->app_ready_qty > 0) - call->app_call_state = - RXRPC_CSTATE_SRVR_GOT_ARGS; - else - call->app_call_state = - RXRPC_CSTATE_SRVR_SND_REPLY; - call->app_mark = op->asize; - call->app_user = op->user; - } - spin_unlock(&call->lock); - - _state(call); - break; - - case RXRPC_CSTATE_SRVR_RCV_ARGS: - /* change state if just received last packet of arg block */ - if (call->app_last_rcv) - call->app_call_state = RXRPC_CSTATE_SRVR_GOT_ARGS; - spin_unlock(&call->lock); - - _state(call); - break; - - case RXRPC_CSTATE_CLNT_RCV_REPLY: - /* change state if just received last packet of reply block */ - rmtimo = 0; - if (call->app_last_rcv) { - call->app_call_state = RXRPC_CSTATE_CLNT_GOT_REPLY; - rmtimo = 1; - } - spin_unlock(&call->lock); - - if (rmtimo) { - del_timer_sync(&call->acks_timeout); - del_timer_sync(&call->rcv_timeout); - del_timer_sync(&call->ackr_dfr_timo); - } - - _state(call); - break; - - default: - /* deal with data reception in an unexpected state */ - printk("Unexpected state [[[ %u ]]]\n", call->app_call_state); - __rxrpc_call_abort(call, -EBADMSG); - _leave(""); - return; - } - - if (call->app_call_state == RXRPC_CSTATE_CLNT_RCV_REPLY && - call->app_last_rcv) - BUG(); - - /* otherwise just invoke the data function whenever we can satisfy its desire for more - * data - */ - _proto("Rx Received Op Data: st=%u qty=%Zu mk=%Zu%s", - call->app_call_state, call->app_ready_qty, call->app_mark, - call->app_last_rcv ? " last-rcvd" : ""); - - spin_lock(&call->lock); - - ret = __rxrpc_call_read_data(call); - switch (ret) { - case 0: - spin_unlock(&call->lock); - call->app_attn_func(call); - break; - case -EAGAIN: - spin_unlock(&call->lock); - break; - case -ECONNABORTED: - spin_unlock(&call->lock); - break; - default: - __rxrpc_call_abort(call, ret); - break; - } - - _state(call); - - _leave(""); - -} /* end rxrpc_call_receive_data_packet() */ - -/*****************************************************************************/ -/* - * received an ACK packet - */ -static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call, - struct rxrpc_message *msg) -{ - struct rxrpc_ackpacket _ack, *ap; - rxrpc_serial_net_t serial; - rxrpc_seq_t seq; - int ret; - - _enter("%p{%u},%p{%u}", call, ntohl(call->call_id), msg, msg->seq); - - /* extract the basic ACK record */ - ap = skb_header_pointer(msg->pkt, msg->offset, sizeof(_ack), &_ack); - if (ap == NULL) { - printk("Rx Received short ACK packet\n"); - return; - } - msg->offset += sizeof(_ack); - - serial = ap->serial; - seq = ntohl(ap->firstPacket); - - _proto("Rx Received ACK %%%d { b=%hu m=%hu f=%u p=%u s=%u r=%s n=%u }", - ntohl(msg->hdr.serial), - ntohs(ap->bufferSpace), - ntohs(ap->maxSkew), - seq, - ntohl(ap->previousPacket), - ntohl(serial), - rxrpc_acks[ap->reason], - call->ackr.nAcks - ); - - /* check the other side isn't ACK'ing a sequence number I haven't sent - * yet */ - if (ap->nAcks > 0 && - (seq > call->snd_seq_count || - seq + ap->nAcks - 1 > call->snd_seq_count)) { - printk("Received ACK (#%u-#%u) for unsent packet\n", - seq, seq + ap->nAcks - 1); - rxrpc_call_abort(call, -EINVAL); - _leave(""); - return; - } - - /* deal with RTT calculation */ - if (serial) { - struct rxrpc_message *rttmsg; - - /* find the prompting packet */ - spin_lock(&call->lock); - if (call->snd_ping && call->snd_ping->hdr.serial == serial) { - /* it was a ping packet */ - rttmsg = call->snd_ping; - call->snd_ping = NULL; - spin_unlock(&call->lock); - - if (rttmsg) { - rttmsg->rttdone = 1; - rxrpc_peer_calculate_rtt(call->conn->peer, - rttmsg, msg); - rxrpc_put_message(rttmsg); - } - } - else { - struct list_head *_p; - - /* it ought to be a data packet - look in the pending - * ACK list */ - list_for_each(_p, &call->acks_pendq) { - rttmsg = list_entry(_p, struct rxrpc_message, - link); - if (rttmsg->hdr.serial == serial) { - if (rttmsg->rttdone) - /* never do RTT twice without - * resending */ - break; - - rttmsg->rttdone = 1; - rxrpc_peer_calculate_rtt( - call->conn->peer, rttmsg, msg); - break; - } - } - spin_unlock(&call->lock); - } - } - - switch (ap->reason) { - /* deal with negative/positive acknowledgement of data - * packets */ - case RXRPC_ACK_REQUESTED: - case RXRPC_ACK_DELAY: - case RXRPC_ACK_IDLE: - rxrpc_call_definitively_ACK(call, seq - 1); - - case RXRPC_ACK_DUPLICATE: - case RXRPC_ACK_OUT_OF_SEQUENCE: - case RXRPC_ACK_EXCEEDS_WINDOW: - call->snd_resend_cnt = 0; - ret = rxrpc_call_record_ACK(call, msg, seq, ap->nAcks); - if (ret < 0) - rxrpc_call_abort(call, ret); - break; - - /* respond to ping packets immediately */ - case RXRPC_ACK_PING: - rxrpc_call_generate_ACK(call, &msg->hdr, ap); - break; - - /* only record RTT on ping response packets */ - case RXRPC_ACK_PING_RESPONSE: - if (call->snd_ping) { - struct rxrpc_message *rttmsg; - - /* only do RTT stuff if the response matches the - * retained ping */ - rttmsg = NULL; - spin_lock(&call->lock); - if (call->snd_ping && - call->snd_ping->hdr.serial == ap->serial) { - rttmsg = call->snd_ping; - call->snd_ping = NULL; - } - spin_unlock(&call->lock); - - if (rttmsg) { - rttmsg->rttdone = 1; - rxrpc_peer_calculate_rtt(call->conn->peer, - rttmsg, msg); - rxrpc_put_message(rttmsg); - } - } - break; - - default: - printk("Unsupported ACK reason %u\n", ap->reason); - break; - } - - _leave(""); -} /* end rxrpc_call_receive_ack_packet() */ - -/*****************************************************************************/ -/* - * record definitive ACKs for all messages up to and including the one with the - * 'highest' seq - */ -static void rxrpc_call_definitively_ACK(struct rxrpc_call *call, - rxrpc_seq_t highest) -{ - struct rxrpc_message *msg; - int now_complete; - - _enter("%p{ads=%u},%u", call, call->acks_dftv_seq, highest); - - while (call->acks_dftv_seq < highest) { - call->acks_dftv_seq++; - - _proto("Definitive ACK on packet #%u", call->acks_dftv_seq); - - /* discard those at front of queue until message with highest - * ACK is found */ - spin_lock(&call->lock); - msg = NULL; - if (!list_empty(&call->acks_pendq)) { - msg = list_entry(call->acks_pendq.next, - struct rxrpc_message, link); - list_del_init(&msg->link); /* dequeue */ - if (msg->state == RXRPC_MSG_SENT) - call->acks_pend_cnt--; - } - spin_unlock(&call->lock); - - /* insanity check */ - if (!msg) - panic("%s(): acks_pendq unexpectedly empty\n", - __FUNCTION__); - - if (msg->seq != call->acks_dftv_seq) - panic("%s(): Packet #%u expected at front of acks_pendq" - " (#%u found)\n", - __FUNCTION__, call->acks_dftv_seq, msg->seq); - - /* discard the message */ - msg->state = RXRPC_MSG_DONE; - rxrpc_put_message(msg); - } - - /* if all sent packets are definitively ACK'd then prod any sleepers just in case */ - now_complete = 0; - spin_lock(&call->lock); - if (call->acks_dftv_seq == call->snd_seq_count) { - if (call->app_call_state != RXRPC_CSTATE_COMPLETE) { - call->app_call_state = RXRPC_CSTATE_COMPLETE; - _state(call); - now_complete = 1; - } - } - spin_unlock(&call->lock); - - if (now_complete) { - del_timer_sync(&call->acks_timeout); - del_timer_sync(&call->rcv_timeout); - del_timer_sync(&call->ackr_dfr_timo); - call->app_attn_func(call); - } - - _leave(""); -} /* end rxrpc_call_definitively_ACK() */ - -/*****************************************************************************/ -/* - * record the specified amount of ACKs/NAKs - */ -static int rxrpc_call_record_ACK(struct rxrpc_call *call, - struct rxrpc_message *msg, - rxrpc_seq_t seq, - size_t count) -{ - struct rxrpc_message *dmsg; - struct list_head *_p; - rxrpc_seq_t highest; - unsigned ix; - size_t chunk; - char resend, now_complete; - u8 acks[16]; - - _enter("%p{apc=%u ads=%u},%p,%u,%Zu", - call, call->acks_pend_cnt, call->acks_dftv_seq, - msg, seq, count); - - /* handle re-ACK'ing of definitively ACK'd packets (may be out-of-order - * ACKs) */ - if (seq <= call->acks_dftv_seq) { - unsigned delta = call->acks_dftv_seq - seq; - - if (count <= delta) { - _leave(" = 0 [all definitively ACK'd]"); - return 0; - } - - seq += delta; - count -= delta; - msg->offset += delta; - } - - highest = seq + count - 1; - resend = 0; - while (count > 0) { - /* extract up to 16 ACK slots at a time */ - chunk = min(count, sizeof(acks)); - count -= chunk; - - memset(acks, 2, sizeof(acks)); - - if (skb_copy_bits(msg->pkt, msg->offset, &acks, chunk) < 0) { - printk("Rx Received short ACK packet\n"); - _leave(" = -EINVAL"); - return -EINVAL; - } - msg->offset += chunk; - - /* check that the ACK set is valid */ - for (ix = 0; ix < chunk; ix++) { - switch (acks[ix]) { - case RXRPC_ACK_TYPE_ACK: - break; - case RXRPC_ACK_TYPE_NACK: - resend = 1; - break; - default: - printk("Rx Received unsupported ACK state" - " %u\n", acks[ix]); - _leave(" = -EINVAL"); - return -EINVAL; - } - } - - _proto("Rx ACK of packets #%u-#%u " - "[%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c] (pend=%u)", - seq, (unsigned) (seq + chunk - 1), - _acktype[acks[0x0]], - _acktype[acks[0x1]], - _acktype[acks[0x2]], - _acktype[acks[0x3]], - _acktype[acks[0x4]], - _acktype[acks[0x5]], - _acktype[acks[0x6]], - _acktype[acks[0x7]], - _acktype[acks[0x8]], - _acktype[acks[0x9]], - _acktype[acks[0xA]], - _acktype[acks[0xB]], - _acktype[acks[0xC]], - _acktype[acks[0xD]], - _acktype[acks[0xE]], - _acktype[acks[0xF]], - call->acks_pend_cnt - ); - - /* mark the packets in the ACK queue as being provisionally - * ACK'd */ - ix = 0; - spin_lock(&call->lock); - - /* find the first packet ACK'd/NAK'd here */ - list_for_each(_p, &call->acks_pendq) { - dmsg = list_entry(_p, struct rxrpc_message, link); - if (dmsg->seq == seq) - goto found_first; - _debug("- %u: skipping #%u", ix, dmsg->seq); - } - goto bad_queue; - - found_first: - do { - _debug("- %u: processing #%u (%c) apc=%u", - ix, dmsg->seq, _acktype[acks[ix]], - call->acks_pend_cnt); - - if (acks[ix] == RXRPC_ACK_TYPE_ACK) { - if (dmsg->state == RXRPC_MSG_SENT) - call->acks_pend_cnt--; - dmsg->state = RXRPC_MSG_ACKED; - } - else { - if (dmsg->state == RXRPC_MSG_ACKED) - call->acks_pend_cnt++; - dmsg->state = RXRPC_MSG_SENT; - } - ix++; - seq++; - - _p = dmsg->link.next; - dmsg = list_entry(_p, struct rxrpc_message, link); - } while(ix < chunk && - _p != &call->acks_pendq && - dmsg->seq == seq); - - if (ix < chunk) - goto bad_queue; - - spin_unlock(&call->lock); - } - - if (resend) - rxrpc_call_resend(call, highest); - - /* if all packets are provisionally ACK'd, then wake up anyone who's - * waiting for that */ - now_complete = 0; - spin_lock(&call->lock); - if (call->acks_pend_cnt == 0) { - if (call->app_call_state == RXRPC_CSTATE_SRVR_RCV_FINAL_ACK) { - call->app_call_state = RXRPC_CSTATE_COMPLETE; - _state(call); - } - now_complete = 1; - } - spin_unlock(&call->lock); - - if (now_complete) { - _debug("- wake up waiters"); - del_timer_sync(&call->acks_timeout); - del_timer_sync(&call->rcv_timeout); - del_timer_sync(&call->ackr_dfr_timo); - call->app_attn_func(call); - } - - _leave(" = 0 (apc=%u)", call->acks_pend_cnt); - return 0; - - bad_queue: - panic("%s(): acks_pendq in bad state (packet #%u absent)\n", - __FUNCTION__, seq); - -} /* end rxrpc_call_record_ACK() */ - -/*****************************************************************************/ -/* - * transfer data from the ready packet queue to the asynchronous read buffer - * - since this func is the only one going to look at packets queued on - * app_readyq, we don't need a lock to modify or access them, only to modify - * the queue pointers - * - called with call->lock held - * - the buffer must be in kernel space - * - returns: - * 0 if buffer filled - * -EAGAIN if buffer not filled and more data to come - * -EBADMSG if last packet received and insufficient data left - * -ECONNABORTED if the call has in an error state - */ -static int __rxrpc_call_read_data(struct rxrpc_call *call) -{ - struct rxrpc_message *msg; - size_t qty; - int ret; - - _enter("%p{as=%d buf=%p qty=%Zu/%Zu}", - call, - call->app_async_read, call->app_read_buf, - call->app_ready_qty, call->app_mark); - - /* check the state */ - switch (call->app_call_state) { - case RXRPC_CSTATE_SRVR_RCV_ARGS: - case RXRPC_CSTATE_CLNT_RCV_REPLY: - if (call->app_last_rcv) { - printk("%s(%p,%p,%Zd):" - " Inconsistent call state (%s, last pkt)", - __FUNCTION__, - call, call->app_read_buf, call->app_mark, - rxrpc_call_states[call->app_call_state]); - BUG(); - } - break; - - case RXRPC_CSTATE_SRVR_RCV_OPID: - case RXRPC_CSTATE_SRVR_GOT_ARGS: - case RXRPC_CSTATE_CLNT_GOT_REPLY: - break; - - case RXRPC_CSTATE_SRVR_SND_REPLY: - if (!call->app_last_rcv) { - printk("%s(%p,%p,%Zd):" - " Inconsistent call state (%s, not last pkt)", - __FUNCTION__, - call, call->app_read_buf, call->app_mark, - rxrpc_call_states[call->app_call_state]); - BUG(); - } - _debug("Trying to read data from call in SND_REPLY state"); - break; - - case RXRPC_CSTATE_ERROR: - _leave(" = -ECONNABORTED"); - return -ECONNABORTED; - - default: - printk("reading in unexpected state [[[ %u ]]]\n", - call->app_call_state); - BUG(); - } - - /* handle the case of not having an async buffer */ - if (!call->app_async_read) { - if (call->app_mark == RXRPC_APP_MARK_EOF) { - ret = call->app_last_rcv ? 0 : -EAGAIN; - } - else { - if (call->app_mark >= call->app_ready_qty) { - call->app_mark = RXRPC_APP_MARK_EOF; - ret = 0; - } - else { - ret = call->app_last_rcv ? -EBADMSG : -EAGAIN; - } - } - - _leave(" = %d [no buf]", ret); - return 0; - } - - while (!list_empty(&call->app_readyq) && call->app_mark > 0) { - msg = list_entry(call->app_readyq.next, - struct rxrpc_message, link); - - /* drag as much data as we need out of this packet */ - qty = min(call->app_mark, msg->dsize); - - _debug("reading %Zu from skb=%p off=%lu", - qty, msg->pkt, msg->offset); - - if (call->app_read_buf) - if (skb_copy_bits(msg->pkt, msg->offset, - call->app_read_buf, qty) < 0) - panic("%s: Failed to copy data from packet:" - " (%p,%p,%Zd)", - __FUNCTION__, - call, call->app_read_buf, qty); - - /* if that packet is now empty, discard it */ - call->app_ready_qty -= qty; - msg->dsize -= qty; - - if (msg->dsize == 0) { - list_del_init(&msg->link); - rxrpc_put_message(msg); - } - else { - msg->offset += qty; - } - - call->app_mark -= qty; - if (call->app_read_buf) - call->app_read_buf += qty; - } - - if (call->app_mark == 0) { - call->app_async_read = 0; - call->app_mark = RXRPC_APP_MARK_EOF; - call->app_read_buf = NULL; - - /* adjust the state if used up all packets */ - if (list_empty(&call->app_readyq) && call->app_last_rcv) { - switch (call->app_call_state) { - case RXRPC_CSTATE_SRVR_RCV_OPID: - call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; - call->app_mark = RXRPC_APP_MARK_EOF; - _state(call); - del_timer_sync(&call->rcv_timeout); - break; - case RXRPC_CSTATE_SRVR_GOT_ARGS: - call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; - _state(call); - del_timer_sync(&call->rcv_timeout); - break; - default: - call->app_call_state = RXRPC_CSTATE_COMPLETE; - _state(call); - del_timer_sync(&call->acks_timeout); - del_timer_sync(&call->ackr_dfr_timo); - del_timer_sync(&call->rcv_timeout); - break; - } - } - - _leave(" = 0"); - return 0; - } - - if (call->app_last_rcv) { - _debug("Insufficient data (%Zu/%Zu)", - call->app_ready_qty, call->app_mark); - call->app_async_read = 0; - call->app_mark = RXRPC_APP_MARK_EOF; - call->app_read_buf = NULL; - - _leave(" = -EBADMSG"); - return -EBADMSG; - } - - _leave(" = -EAGAIN"); - return -EAGAIN; -} /* end __rxrpc_call_read_data() */ - -/*****************************************************************************/ -/* - * attempt to read the specified amount of data from the call's ready queue - * into the buffer provided - * - since this func is the only one going to look at packets queued on - * app_readyq, we don't need a lock to modify or access them, only to modify - * the queue pointers - * - if the buffer pointer is NULL, then data is merely drained, not copied - * - if flags&RXRPC_CALL_READ_BLOCK, then the function will wait until there is - * enough data or an error will be generated - * - note that the caller must have added the calling task to the call's wait - * queue beforehand - * - if flags&RXRPC_CALL_READ_ALL, then an error will be generated if this - * function doesn't read all available data - */ -int rxrpc_call_read_data(struct rxrpc_call *call, - void *buffer, size_t size, int flags) -{ - int ret; - - _enter("%p{arq=%Zu},%p,%Zd,%x", - call, call->app_ready_qty, buffer, size, flags); - - spin_lock(&call->lock); - - if (unlikely(!!call->app_read_buf)) { - spin_unlock(&call->lock); - _leave(" = -EBUSY"); - return -EBUSY; - } - - call->app_mark = size; - call->app_read_buf = buffer; - call->app_async_read = 1; - call->app_read_count++; - - /* read as much data as possible */ - ret = __rxrpc_call_read_data(call); - switch (ret) { - case 0: - if (flags & RXRPC_CALL_READ_ALL && - (!call->app_last_rcv || call->app_ready_qty > 0)) { - _leave(" = -EBADMSG"); - __rxrpc_call_abort(call, -EBADMSG); - return -EBADMSG; - } - - spin_unlock(&call->lock); - call->app_attn_func(call); - _leave(" = 0"); - return ret; - - case -ECONNABORTED: - spin_unlock(&call->lock); - _leave(" = %d [aborted]", ret); - return ret; - - default: - __rxrpc_call_abort(call, ret); - _leave(" = %d", ret); - return ret; - - case -EAGAIN: - spin_unlock(&call->lock); - - if (!(flags & RXRPC_CALL_READ_BLOCK)) { - _leave(" = -EAGAIN"); - return -EAGAIN; - } - - /* wait for the data to arrive */ - _debug("blocking for data arrival"); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (!call->app_async_read || signal_pending(current)) - break; - schedule(); - } - set_current_state(TASK_RUNNING); - - if (signal_pending(current)) { - _leave(" = -EINTR"); - return -EINTR; - } - - if (call->app_call_state == RXRPC_CSTATE_ERROR) { - _leave(" = -ECONNABORTED"); - return -ECONNABORTED; - } - - _leave(" = 0"); - return 0; - } - -} /* end rxrpc_call_read_data() */ - -/*****************************************************************************/ -/* - * write data to a call - * - the data may not be sent immediately if it doesn't fill a buffer - * - if we can't queue all the data for buffering now, siov[] will have been - * adjusted to take account of what has been sent - */ -int rxrpc_call_write_data(struct rxrpc_call *call, - size_t sioc, - struct kvec *siov, - u8 rxhdr_flags, - gfp_t alloc_flags, - int dup_data, - size_t *size_sent) -{ - struct rxrpc_message *msg; - struct kvec *sptr; - size_t space, size, chunk, tmp; - char *buf; - int ret; - - _enter("%p,%Zu,%p,%02x,%x,%d,%p", - call, sioc, siov, rxhdr_flags, alloc_flags, dup_data, - size_sent); - - *size_sent = 0; - size = 0; - ret = -EINVAL; - - /* can't send more if we've sent last packet from this end */ - switch (call->app_call_state) { - case RXRPC_CSTATE_SRVR_SND_REPLY: - case RXRPC_CSTATE_CLNT_SND_ARGS: - break; - case RXRPC_CSTATE_ERROR: - ret = call->app_errno; - default: - goto out; - } - - /* calculate how much data we've been given */ - sptr = siov; - for (; sioc > 0; sptr++, sioc--) { - if (!sptr->iov_len) - continue; - - if (!sptr->iov_base) - goto out; - - size += sptr->iov_len; - } - - _debug("- size=%Zu mtu=%Zu", size, call->conn->mtu_size); - - do { - /* make sure there's a message under construction */ - if (!call->snd_nextmsg) { - /* no - allocate a message with no data yet attached */ - ret = rxrpc_conn_newmsg(call->conn, call, - RXRPC_PACKET_TYPE_DATA, - 0, NULL, alloc_flags, - &call->snd_nextmsg); - if (ret < 0) - goto out; - _debug("- allocated new message [ds=%Zu]", - call->snd_nextmsg->dsize); - } - - msg = call->snd_nextmsg; - msg->hdr.flags |= rxhdr_flags; - - /* deal with zero-length terminal packet */ - if (size == 0) { - if (rxhdr_flags & RXRPC_LAST_PACKET) { - ret = rxrpc_call_flush(call); - if (ret < 0) - goto out; - } - break; - } - - /* work out how much space current packet has available */ - space = call->conn->mtu_size - msg->dsize; - chunk = min(space, size); - - _debug("- [before] space=%Zu chunk=%Zu", space, chunk); - - while (!siov->iov_len) - siov++; - - /* if we are going to have to duplicate the data then coalesce - * it too */ - if (dup_data) { - /* don't allocate more that 1 page at a time */ - if (chunk > PAGE_SIZE) - chunk = PAGE_SIZE; - - /* allocate a data buffer and attach to the message */ - buf = kmalloc(chunk, alloc_flags); - if (unlikely(!buf)) { - if (msg->dsize == - sizeof(struct rxrpc_header)) { - /* discard an empty msg and wind back - * the seq counter */ - rxrpc_put_message(msg); - call->snd_nextmsg = NULL; - call->snd_seq_count--; - } - - ret = -ENOMEM; - goto out; - } - - tmp = msg->dcount++; - set_bit(tmp, &msg->dfree); - msg->data[tmp].iov_base = buf; - msg->data[tmp].iov_len = chunk; - msg->dsize += chunk; - *size_sent += chunk; - size -= chunk; - - /* load the buffer with data */ - while (chunk > 0) { - tmp = min(chunk, siov->iov_len); - memcpy(buf, siov->iov_base, tmp); - buf += tmp; - siov->iov_base += tmp; - siov->iov_len -= tmp; - if (!siov->iov_len) - siov++; - chunk -= tmp; - } - } - else { - /* we want to attach the supplied buffers directly */ - while (chunk > 0 && - msg->dcount < RXRPC_MSG_MAX_IOCS) { - tmp = msg->dcount++; - msg->data[tmp].iov_base = siov->iov_base; - msg->data[tmp].iov_len = siov->iov_len; - msg->dsize += siov->iov_len; - *size_sent += siov->iov_len; - size -= siov->iov_len; - chunk -= siov->iov_len; - siov++; - } - } - - _debug("- [loaded] chunk=%Zu size=%Zu", chunk, size); - - /* dispatch the message when full, final or requesting ACK */ - if (msg->dsize >= call->conn->mtu_size || rxhdr_flags) { - ret = rxrpc_call_flush(call); - if (ret < 0) - goto out; - } - - } while(size > 0); - - ret = 0; - out: - _leave(" = %d (%Zd queued, %Zd rem)", ret, *size_sent, size); - return ret; - -} /* end rxrpc_call_write_data() */ - -/*****************************************************************************/ -/* - * flush outstanding packets to the network - */ -static int rxrpc_call_flush(struct rxrpc_call *call) -{ - struct rxrpc_message *msg; - int ret = 0; - - _enter("%p", call); - - rxrpc_get_call(call); - - /* if there's a packet under construction, then dispatch it now */ - if (call->snd_nextmsg) { - msg = call->snd_nextmsg; - call->snd_nextmsg = NULL; - - if (msg->hdr.flags & RXRPC_LAST_PACKET) { - msg->hdr.flags &= ~RXRPC_MORE_PACKETS; - if (call->app_call_state != RXRPC_CSTATE_CLNT_SND_ARGS) - msg->hdr.flags |= RXRPC_REQUEST_ACK; - } - else { - msg->hdr.flags |= RXRPC_MORE_PACKETS; - } - - _proto("Sending DATA message { ds=%Zu dc=%u df=%02lu }", - msg->dsize, msg->dcount, msg->dfree); - - /* queue and adjust call state */ - spin_lock(&call->lock); - list_add_tail(&msg->link, &call->acks_pendq); - - /* decide what to do depending on current state and if this is - * the last packet */ - ret = -EINVAL; - switch (call->app_call_state) { - case RXRPC_CSTATE_SRVR_SND_REPLY: - if (msg->hdr.flags & RXRPC_LAST_PACKET) { - call->app_call_state = - RXRPC_CSTATE_SRVR_RCV_FINAL_ACK; - _state(call); - } - break; - - case RXRPC_CSTATE_CLNT_SND_ARGS: - if (msg->hdr.flags & RXRPC_LAST_PACKET) { - call->app_call_state = - RXRPC_CSTATE_CLNT_RCV_REPLY; - _state(call); - } - break; - - case RXRPC_CSTATE_ERROR: - ret = call->app_errno; - default: - spin_unlock(&call->lock); - goto out; - } - - call->acks_pend_cnt++; - - mod_timer(&call->acks_timeout, - __rxrpc_rtt_based_timeout(call, - rxrpc_call_acks_timeout)); - - spin_unlock(&call->lock); - - ret = rxrpc_conn_sendmsg(call->conn, msg); - if (ret == 0) - call->pkt_snd_count++; - } - - out: - rxrpc_put_call(call); - - _leave(" = %d", ret); - return ret; - -} /* end rxrpc_call_flush() */ - -/*****************************************************************************/ -/* - * resend NAK'd or unacknowledged packets up to the highest one specified - */ -static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest) -{ - struct rxrpc_message *msg; - struct list_head *_p; - rxrpc_seq_t seq = 0; - - _enter("%p,%u", call, highest); - - _proto("Rx Resend required"); - - /* handle too many resends */ - if (call->snd_resend_cnt >= rxrpc_call_max_resend) { - _debug("Aborting due to too many resends (rcv=%d)", - call->pkt_rcv_count); - rxrpc_call_abort(call, - call->pkt_rcv_count > 0 ? -EIO : -ETIMEDOUT); - _leave(""); - return; - } - - spin_lock(&call->lock); - call->snd_resend_cnt++; - for (;;) { - /* determine which the next packet we might need to ACK is */ - if (seq <= call->acks_dftv_seq) - seq = call->acks_dftv_seq; - seq++; - - if (seq > highest) - break; - - /* look for the packet in the pending-ACK queue */ - list_for_each(_p, &call->acks_pendq) { - msg = list_entry(_p, struct rxrpc_message, link); - if (msg->seq == seq) - goto found_msg; - } - - panic("%s(%p,%d):" - " Inconsistent pending-ACK queue (ds=%u sc=%u sq=%u)\n", - __FUNCTION__, call, highest, - call->acks_dftv_seq, call->snd_seq_count, seq); - - found_msg: - if (msg->state != RXRPC_MSG_SENT) - continue; /* only un-ACK'd packets */ - - rxrpc_get_message(msg); - spin_unlock(&call->lock); - - /* send each message again (and ignore any errors we might - * incur) */ - _proto("Resending DATA message { ds=%Zu dc=%u df=%02lu }", - msg->dsize, msg->dcount, msg->dfree); - - if (rxrpc_conn_sendmsg(call->conn, msg) == 0) - call->pkt_snd_count++; - - rxrpc_put_message(msg); - - spin_lock(&call->lock); - } - - /* reset the timeout */ - mod_timer(&call->acks_timeout, - __rxrpc_rtt_based_timeout(call, rxrpc_call_acks_timeout)); - - spin_unlock(&call->lock); - - _leave(""); -} /* end rxrpc_call_resend() */ - -/*****************************************************************************/ -/* - * handle an ICMP error being applied to a call - */ -void rxrpc_call_handle_error(struct rxrpc_call *call, int local, int errno) -{ - _enter("%p{%u},%d", call, ntohl(call->call_id), errno); - - /* if this call is already aborted, then just wake up any waiters */ - if (call->app_call_state == RXRPC_CSTATE_ERROR) { - call->app_error_func(call); - } - else { - /* tell the app layer what happened */ - spin_lock(&call->lock); - call->app_call_state = RXRPC_CSTATE_ERROR; - _state(call); - if (local) - call->app_err_state = RXRPC_ESTATE_LOCAL_ERROR; - else - call->app_err_state = RXRPC_ESTATE_REMOTE_ERROR; - call->app_errno = errno; - call->app_mark = RXRPC_APP_MARK_EOF; - call->app_read_buf = NULL; - call->app_async_read = 0; - - /* map the error */ - call->app_aemap_func(call); - - del_timer_sync(&call->acks_timeout); - del_timer_sync(&call->rcv_timeout); - del_timer_sync(&call->ackr_dfr_timo); - - spin_unlock(&call->lock); - - call->app_error_func(call); - } - - _leave(""); -} /* end rxrpc_call_handle_error() */ diff --git a/net/rxrpc/connection.c b/net/rxrpc/connection.c deleted file mode 100644 index a7c929a..0000000 --- a/net/rxrpc/connection.c +++ /dev/null @@ -1,777 +0,0 @@ -/* connection.c: Rx connection routines - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -__RXACCT_DECL(atomic_t rxrpc_connection_count); - -LIST_HEAD(rxrpc_conns); -DECLARE_RWSEM(rxrpc_conns_sem); -unsigned long rxrpc_conn_timeout = 60 * 60; - -static void rxrpc_conn_do_timeout(struct rxrpc_connection *conn); - -static void __rxrpc_conn_timeout(rxrpc_timer_t *timer) -{ - struct rxrpc_connection *conn = - list_entry(timer, struct rxrpc_connection, timeout); - - _debug("Rx CONN TIMEOUT [%p{u=%d}]", conn, atomic_read(&conn->usage)); - - rxrpc_conn_do_timeout(conn); -} - -static const struct rxrpc_timer_ops rxrpc_conn_timer_ops = { - .timed_out = __rxrpc_conn_timeout, -}; - -/*****************************************************************************/ -/* - * create a new connection record - */ -static inline int __rxrpc_create_connection(struct rxrpc_peer *peer, - struct rxrpc_connection **_conn) -{ - struct rxrpc_connection *conn; - - _enter("%p",peer); - - /* allocate and initialise a connection record */ - conn = kzalloc(sizeof(struct rxrpc_connection), GFP_KERNEL); - if (!conn) { - _leave(" = -ENOMEM"); - return -ENOMEM; - } - - atomic_set(&conn->usage, 1); - - INIT_LIST_HEAD(&conn->link); - INIT_LIST_HEAD(&conn->id_link); - init_waitqueue_head(&conn->chanwait); - spin_lock_init(&conn->lock); - rxrpc_timer_init(&conn->timeout, &rxrpc_conn_timer_ops); - - do_gettimeofday(&conn->atime); - conn->mtu_size = 1024; - conn->peer = peer; - conn->trans = peer->trans; - - __RXACCT(atomic_inc(&rxrpc_connection_count)); - *_conn = conn; - _leave(" = 0 (%p)", conn); - - return 0; -} /* end __rxrpc_create_connection() */ - -/*****************************************************************************/ -/* - * create a new connection record for outgoing connections - */ -int rxrpc_create_connection(struct rxrpc_transport *trans, - __be16 port, - __be32 addr, - uint16_t service_id, - void *security, - struct rxrpc_connection **_conn) -{ - struct rxrpc_connection *candidate, *conn; - struct rxrpc_peer *peer; - struct list_head *_p; - __be32 connid; - int ret; - - _enter("%p{%hu},%u,%hu", trans, trans->port, ntohs(port), service_id); - - /* get a peer record */ - ret = rxrpc_peer_lookup(trans, addr, &peer); - if (ret < 0) { - _leave(" = %d", ret); - return ret; - } - - /* allocate and initialise a connection record */ - ret = __rxrpc_create_connection(peer, &candidate); - if (ret < 0) { - rxrpc_put_peer(peer); - _leave(" = %d", ret); - return ret; - } - - /* fill in the specific bits */ - candidate->addr.sin_family = AF_INET; - candidate->addr.sin_port = port; - candidate->addr.sin_addr.s_addr = addr; - - candidate->in_epoch = rxrpc_epoch; - candidate->out_epoch = rxrpc_epoch; - candidate->in_clientflag = 0; - candidate->out_clientflag = RXRPC_CLIENT_INITIATED; - candidate->service_id = htons(service_id); - - /* invent a unique connection ID */ - write_lock(&peer->conn_idlock); - - try_next_id: - connid = htonl(peer->conn_idcounter & RXRPC_CIDMASK); - peer->conn_idcounter += RXRPC_MAXCALLS; - - list_for_each(_p, &peer->conn_idlist) { - conn = list_entry(_p, struct rxrpc_connection, id_link); - if (connid == conn->conn_id) - goto try_next_id; - if (connid > conn->conn_id) - break; - } - - _debug("selected candidate conn ID %x.%u", - ntohl(peer->addr.s_addr), ntohl(connid)); - - candidate->conn_id = connid; - list_add_tail(&candidate->id_link, _p); - - write_unlock(&peer->conn_idlock); - - /* attach to peer */ - candidate->peer = peer; - - write_lock(&peer->conn_lock); - - /* search the peer's transport graveyard list */ - spin_lock(&peer->conn_gylock); - list_for_each(_p, &peer->conn_graveyard) { - conn = list_entry(_p, struct rxrpc_connection, link); - if (conn->addr.sin_port == candidate->addr.sin_port && - conn->security_ix == candidate->security_ix && - conn->service_id == candidate->service_id && - conn->in_clientflag == 0) - goto found_in_graveyard; - } - spin_unlock(&peer->conn_gylock); - - /* pick the new candidate */ - _debug("created connection: {%08x} [out]", ntohl(candidate->conn_id)); - atomic_inc(&peer->conn_count); - conn = candidate; - candidate = NULL; - - make_active: - list_add_tail(&conn->link, &peer->conn_active); - write_unlock(&peer->conn_lock); - - if (candidate) { - write_lock(&peer->conn_idlock); - list_del(&candidate->id_link); - write_unlock(&peer->conn_idlock); - - __RXACCT(atomic_dec(&rxrpc_connection_count)); - kfree(candidate); - } - else { - down_write(&rxrpc_conns_sem); - list_add_tail(&conn->proc_link, &rxrpc_conns); - up_write(&rxrpc_conns_sem); - } - - *_conn = conn; - _leave(" = 0 (%p)", conn); - - return 0; - - /* handle resurrecting a connection from the graveyard */ - found_in_graveyard: - _debug("resurrecting connection: {%08x} [out]", ntohl(conn->conn_id)); - rxrpc_get_connection(conn); - rxrpc_krxtimod_del_timer(&conn->timeout); - list_del_init(&conn->link); - spin_unlock(&peer->conn_gylock); - goto make_active; -} /* end rxrpc_create_connection() */ - -/*****************************************************************************/ -/* - * lookup the connection for an incoming packet - * - create a new connection record for unrecorded incoming connections - */ -int rxrpc_connection_lookup(struct rxrpc_peer *peer, - struct rxrpc_message *msg, - struct rxrpc_connection **_conn) -{ - struct rxrpc_connection *conn, *candidate = NULL; - struct list_head *_p; - struct sk_buff *pkt = msg->pkt; - int ret, fresh = 0; - __be32 x_epoch, x_connid; - __be16 x_port, x_servid; - __u32 x_secix; - u8 x_clflag; - - _enter("%p{{%hu}},%u,%hu", - peer, - peer->trans->port, - ntohs(pkt->h.uh->source), - ntohs(msg->hdr.serviceId)); - - x_port = pkt->h.uh->source; - x_epoch = msg->hdr.epoch; - x_clflag = msg->hdr.flags & RXRPC_CLIENT_INITIATED; - x_connid = htonl(ntohl(msg->hdr.cid) & RXRPC_CIDMASK); - x_servid = msg->hdr.serviceId; - x_secix = msg->hdr.securityIndex; - - /* [common case] search the transport's active list first */ - read_lock(&peer->conn_lock); - list_for_each(_p, &peer->conn_active) { - conn = list_entry(_p, struct rxrpc_connection, link); - if (conn->addr.sin_port == x_port && - conn->in_epoch == x_epoch && - conn->conn_id == x_connid && - conn->security_ix == x_secix && - conn->service_id == x_servid && - conn->in_clientflag == x_clflag) - goto found_active; - } - read_unlock(&peer->conn_lock); - - /* [uncommon case] not active - * - create a candidate for a new record if an inbound connection - * - only examine the graveyard for an outbound connection - */ - if (x_clflag) { - ret = __rxrpc_create_connection(peer, &candidate); - if (ret < 0) { - _leave(" = %d", ret); - return ret; - } - - /* fill in the specifics */ - candidate->addr.sin_family = AF_INET; - candidate->addr.sin_port = x_port; - candidate->addr.sin_addr.s_addr = pkt->nh.iph->saddr; - candidate->in_epoch = x_epoch; - candidate->out_epoch = x_epoch; - candidate->in_clientflag = RXRPC_CLIENT_INITIATED; - candidate->out_clientflag = 0; - candidate->conn_id = x_connid; - candidate->service_id = x_servid; - candidate->security_ix = x_secix; - } - - /* search the active list again, just in case it appeared whilst we - * were busy */ - write_lock(&peer->conn_lock); - list_for_each(_p, &peer->conn_active) { - conn = list_entry(_p, struct rxrpc_connection, link); - if (conn->addr.sin_port == x_port && - conn->in_epoch == x_epoch && - conn->conn_id == x_connid && - conn->security_ix == x_secix && - conn->service_id == x_servid && - conn->in_clientflag == x_clflag) - goto found_active_second_chance; - } - - /* search the transport's graveyard list */ - spin_lock(&peer->conn_gylock); - list_for_each(_p, &peer->conn_graveyard) { - conn = list_entry(_p, struct rxrpc_connection, link); - if (conn->addr.sin_port == x_port && - conn->in_epoch == x_epoch && - conn->conn_id == x_connid && - conn->security_ix == x_secix && - conn->service_id == x_servid && - conn->in_clientflag == x_clflag) - goto found_in_graveyard; - } - spin_unlock(&peer->conn_gylock); - - /* outbound connections aren't created here */ - if (!x_clflag) { - write_unlock(&peer->conn_lock); - _leave(" = -ENOENT"); - return -ENOENT; - } - - /* we can now add the new candidate to the list */ - _debug("created connection: {%08x} [in]", ntohl(candidate->conn_id)); - rxrpc_get_peer(peer); - conn = candidate; - candidate = NULL; - atomic_inc(&peer->conn_count); - fresh = 1; - - make_active: - list_add_tail(&conn->link, &peer->conn_active); - - success_uwfree: - write_unlock(&peer->conn_lock); - - if (candidate) { - write_lock(&peer->conn_idlock); - list_del(&candidate->id_link); - write_unlock(&peer->conn_idlock); - - __RXACCT(atomic_dec(&rxrpc_connection_count)); - kfree(candidate); - } - - if (fresh) { - down_write(&rxrpc_conns_sem); - list_add_tail(&conn->proc_link, &rxrpc_conns); - up_write(&rxrpc_conns_sem); - } - - success: - *_conn = conn; - _leave(" = 0 (%p)", conn); - return 0; - - /* handle the connection being found in the active list straight off */ - found_active: - rxrpc_get_connection(conn); - read_unlock(&peer->conn_lock); - goto success; - - /* handle resurrecting a connection from the graveyard */ - found_in_graveyard: - _debug("resurrecting connection: {%08x} [in]", ntohl(conn->conn_id)); - rxrpc_get_peer(peer); - rxrpc_get_connection(conn); - rxrpc_krxtimod_del_timer(&conn->timeout); - list_del_init(&conn->link); - spin_unlock(&peer->conn_gylock); - goto make_active; - - /* handle finding the connection on the second time through the active - * list */ - found_active_second_chance: - rxrpc_get_connection(conn); - goto success_uwfree; - -} /* end rxrpc_connection_lookup() */ - -/*****************************************************************************/ -/* - * finish using a connection record - * - it will be transferred to the peer's connection graveyard when refcount - * reaches 0 - */ -void rxrpc_put_connection(struct rxrpc_connection *conn) -{ - struct rxrpc_peer *peer; - - if (!conn) - return; - - _enter("%p{u=%d p=%hu}", - conn, atomic_read(&conn->usage), ntohs(conn->addr.sin_port)); - - peer = conn->peer; - spin_lock(&peer->conn_gylock); - - /* sanity check */ - if (atomic_read(&conn->usage) <= 0) - BUG(); - - if (likely(!atomic_dec_and_test(&conn->usage))) { - spin_unlock(&peer->conn_gylock); - _leave(""); - return; - } - - /* move to graveyard queue */ - _debug("burying connection: {%08x}", ntohl(conn->conn_id)); - list_move_tail(&conn->link, &peer->conn_graveyard); - - rxrpc_krxtimod_add_timer(&conn->timeout, rxrpc_conn_timeout * HZ); - - spin_unlock(&peer->conn_gylock); - - rxrpc_put_peer(conn->peer); - - _leave(" [killed]"); -} /* end rxrpc_put_connection() */ - -/*****************************************************************************/ -/* - * free a connection record - */ -static void rxrpc_conn_do_timeout(struct rxrpc_connection *conn) -{ - struct rxrpc_peer *peer; - - _enter("%p{u=%d p=%hu}", - conn, atomic_read(&conn->usage), ntohs(conn->addr.sin_port)); - - peer = conn->peer; - - if (atomic_read(&conn->usage) < 0) - BUG(); - - /* remove from graveyard if still dead */ - spin_lock(&peer->conn_gylock); - if (atomic_read(&conn->usage) == 0) { - list_del_init(&conn->link); - } - else { - conn = NULL; - } - spin_unlock(&peer->conn_gylock); - - if (!conn) { - _leave(""); - return; /* resurrected */ - } - - _debug("--- Destroying Connection %p{%08x} ---", - conn, ntohl(conn->conn_id)); - - down_write(&rxrpc_conns_sem); - list_del(&conn->proc_link); - up_write(&rxrpc_conns_sem); - - write_lock(&peer->conn_idlock); - list_del(&conn->id_link); - write_unlock(&peer->conn_idlock); - - __RXACCT(atomic_dec(&rxrpc_connection_count)); - kfree(conn); - - /* if the graveyard is now empty, wake up anyone waiting for that */ - if (atomic_dec_and_test(&peer->conn_count)) - wake_up(&peer->conn_gy_waitq); - - _leave(" [destroyed]"); -} /* end rxrpc_conn_do_timeout() */ - -/*****************************************************************************/ -/* - * clear all connection records from a peer endpoint - */ -void rxrpc_conn_clearall(struct rxrpc_peer *peer) -{ - DECLARE_WAITQUEUE(myself, current); - - struct rxrpc_connection *conn; - int err; - - _enter("%p", peer); - - /* there shouldn't be any active conns remaining */ - if (!list_empty(&peer->conn_active)) - BUG(); - - /* manually timeout all conns in the graveyard */ - spin_lock(&peer->conn_gylock); - while (!list_empty(&peer->conn_graveyard)) { - conn = list_entry(peer->conn_graveyard.next, - struct rxrpc_connection, link); - err = rxrpc_krxtimod_del_timer(&conn->timeout); - spin_unlock(&peer->conn_gylock); - - if (err == 0) - rxrpc_conn_do_timeout(conn); - - spin_lock(&peer->conn_gylock); - } - spin_unlock(&peer->conn_gylock); - - /* wait for the the conn graveyard to be completely cleared */ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&peer->conn_gy_waitq, &myself); - - while (atomic_read(&peer->conn_count) != 0) { - schedule(); - set_current_state(TASK_UNINTERRUPTIBLE); - } - - remove_wait_queue(&peer->conn_gy_waitq, &myself); - set_current_state(TASK_RUNNING); - - _leave(""); -} /* end rxrpc_conn_clearall() */ - -/*****************************************************************************/ -/* - * allocate and prepare a message for sending out through the transport - * endpoint - */ -int rxrpc_conn_newmsg(struct rxrpc_connection *conn, - struct rxrpc_call *call, - uint8_t type, - int dcount, - struct kvec diov[], - gfp_t alloc_flags, - struct rxrpc_message **_msg) -{ - struct rxrpc_message *msg; - int loop; - - _enter("%p{%d},%p,%u", conn, ntohs(conn->addr.sin_port), call, type); - - if (dcount > 3) { - _leave(" = -EINVAL"); - return -EINVAL; - } - - msg = kzalloc(sizeof(struct rxrpc_message), alloc_flags); - if (!msg) { - _leave(" = -ENOMEM"); - return -ENOMEM; - } - - atomic_set(&msg->usage, 1); - - INIT_LIST_HEAD(&msg->link); - - msg->state = RXRPC_MSG_PREPARED; - - msg->hdr.epoch = conn->out_epoch; - msg->hdr.cid = conn->conn_id | (call ? call->chan_ix : 0); - msg->hdr.callNumber = call ? call->call_id : 0; - msg->hdr.type = type; - msg->hdr.flags = conn->out_clientflag; - msg->hdr.securityIndex = conn->security_ix; - msg->hdr.serviceId = conn->service_id; - - /* generate sequence numbers for data packets */ - if (call) { - switch (type) { - case RXRPC_PACKET_TYPE_DATA: - msg->seq = ++call->snd_seq_count; - msg->hdr.seq = htonl(msg->seq); - break; - case RXRPC_PACKET_TYPE_ACK: - /* ACK sequence numbers are complicated. The following - * may be wrong: - * - jumbo packet ACKs should have a seq number - * - normal ACKs should not - */ - default: - break; - } - } - - msg->dcount = dcount + 1; - msg->dsize = sizeof(msg->hdr); - msg->data[0].iov_len = sizeof(msg->hdr); - msg->data[0].iov_base = &msg->hdr; - - for (loop=0; loop < dcount; loop++) { - msg->dsize += diov[loop].iov_len; - msg->data[loop+1].iov_len = diov[loop].iov_len; - msg->data[loop+1].iov_base = diov[loop].iov_base; - } - - __RXACCT(atomic_inc(&rxrpc_message_count)); - *_msg = msg; - _leave(" = 0 (%p) #%d", msg, atomic_read(&rxrpc_message_count)); - return 0; -} /* end rxrpc_conn_newmsg() */ - -/*****************************************************************************/ -/* - * free a message - */ -void __rxrpc_put_message(struct rxrpc_message *msg) -{ - int loop; - - _enter("%p #%d", msg, atomic_read(&rxrpc_message_count)); - - if (msg->pkt) - kfree_skb(msg->pkt); - rxrpc_put_connection(msg->conn); - - for (loop = 0; loop < 8; loop++) - if (test_bit(loop, &msg->dfree)) - kfree(msg->data[loop].iov_base); - - __RXACCT(atomic_dec(&rxrpc_message_count)); - kfree(msg); - - _leave(""); -} /* end __rxrpc_put_message() */ - -/*****************************************************************************/ -/* - * send a message out through the transport endpoint - */ -int rxrpc_conn_sendmsg(struct rxrpc_connection *conn, - struct rxrpc_message *msg) -{ - struct msghdr msghdr; - int ret; - - _enter("%p{%d}", conn, ntohs(conn->addr.sin_port)); - - /* fill in some fields in the header */ - spin_lock(&conn->lock); - msg->hdr.serial = htonl(++conn->serial_counter); - msg->rttdone = 0; - spin_unlock(&conn->lock); - - /* set up the message to be transmitted */ - msghdr.msg_name = &conn->addr; - msghdr.msg_namelen = sizeof(conn->addr); - msghdr.msg_control = NULL; - msghdr.msg_controllen = 0; - msghdr.msg_flags = MSG_CONFIRM | MSG_DONTWAIT; - - _net("Sending message type %d of %Zd bytes to %08x:%d", - msg->hdr.type, - msg->dsize, - ntohl(conn->addr.sin_addr.s_addr), - ntohs(conn->addr.sin_port)); - - /* send the message */ - ret = kernel_sendmsg(conn->trans->socket, &msghdr, - msg->data, msg->dcount, msg->dsize); - if (ret < 0) { - msg->state = RXRPC_MSG_ERROR; - } else { - msg->state = RXRPC_MSG_SENT; - ret = 0; - - spin_lock(&conn->lock); - do_gettimeofday(&conn->atime); - msg->stamp = conn->atime; - spin_unlock(&conn->lock); - } - - _leave(" = %d", ret); - - return ret; -} /* end rxrpc_conn_sendmsg() */ - -/*****************************************************************************/ -/* - * deal with a subsequent call packet - */ -int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn, - struct rxrpc_call *call, - struct rxrpc_message *msg) -{ - struct rxrpc_message *pmsg; - struct dst_entry *dst; - struct list_head *_p; - unsigned cix, seq; - int ret = 0; - - _enter("%p,%p,%p", conn, call, msg); - - if (!call) { - cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK; - - spin_lock(&conn->lock); - call = conn->channels[cix]; - - if (!call || call->call_id != msg->hdr.callNumber) { - spin_unlock(&conn->lock); - rxrpc_trans_immediate_abort(conn->trans, msg, -ENOENT); - goto out; - } - else { - rxrpc_get_call(call); - spin_unlock(&conn->lock); - } - } - else { - rxrpc_get_call(call); - } - - _proto("Received packet %%%u [%u] on call %hu:%u:%u", - ntohl(msg->hdr.serial), - ntohl(msg->hdr.seq), - ntohs(msg->hdr.serviceId), - ntohl(conn->conn_id), - ntohl(call->call_id)); - - call->pkt_rcv_count++; - - dst = msg->pkt->dst; - if (dst && dst->dev) - conn->peer->if_mtu = - dst->dev->mtu - dst->dev->hard_header_len; - - /* queue on the call in seq order */ - rxrpc_get_message(msg); - seq = msg->seq; - - spin_lock(&call->lock); - list_for_each(_p, &call->rcv_receiveq) { - pmsg = list_entry(_p, struct rxrpc_message, link); - if (pmsg->seq > seq) - break; - } - list_add_tail(&msg->link, _p); - - /* reset the activity timeout */ - call->flags |= RXRPC_CALL_RCV_PKT; - mod_timer(&call->rcv_timeout,jiffies + rxrpc_call_rcv_timeout * HZ); - - spin_unlock(&call->lock); - - rxrpc_krxiod_queue_call(call); - - rxrpc_put_call(call); - out: - _leave(" = %d", ret); - return ret; -} /* end rxrpc_conn_receive_call_packet() */ - -/*****************************************************************************/ -/* - * handle an ICMP error being applied to a connection - */ -void rxrpc_conn_handle_error(struct rxrpc_connection *conn, - int local, int errno) -{ - struct rxrpc_call *calls[4]; - int loop; - - _enter("%p{%d},%d", conn, ntohs(conn->addr.sin_port), errno); - - /* get a ref to all my calls in one go */ - memset(calls, 0, sizeof(calls)); - spin_lock(&conn->lock); - - for (loop = 3; loop >= 0; loop--) { - if (conn->channels[loop]) { - calls[loop] = conn->channels[loop]; - rxrpc_get_call(calls[loop]); - } - } - - spin_unlock(&conn->lock); - - /* now kick them all */ - for (loop = 3; loop >= 0; loop--) { - if (calls[loop]) { - rxrpc_call_handle_error(calls[loop], local, errno); - rxrpc_put_call(calls[loop]); - } - } - - _leave(""); -} /* end rxrpc_conn_handle_error() */ diff --git a/net/rxrpc/internal.h b/net/rxrpc/internal.h deleted file mode 100644 index cc0c579..0000000 --- a/net/rxrpc/internal.h +++ /dev/null @@ -1,106 +0,0 @@ -/* internal.h: internal Rx RPC stuff - * - * Copyright (c) 2002 David Howells (dhowells@redhat.com). - */ - -#ifndef RXRPC_INTERNAL_H -#define RXRPC_INTERNAL_H - -#include -#include - -/* - * debug accounting - */ -#if 1 -#define __RXACCT_DECL(X) X -#define __RXACCT(X) do { X; } while(0) -#else -#define __RXACCT_DECL(X) -#define __RXACCT(X) do { } while(0) -#endif - -__RXACCT_DECL(extern atomic_t rxrpc_transport_count); -__RXACCT_DECL(extern atomic_t rxrpc_peer_count); -__RXACCT_DECL(extern atomic_t rxrpc_connection_count); -__RXACCT_DECL(extern atomic_t rxrpc_call_count); -__RXACCT_DECL(extern atomic_t rxrpc_message_count); - -/* - * debug tracing - */ -#define kenter(FMT, a...) printk("==> %s("FMT")\n",__FUNCTION__ , ##a) -#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ##a) -#define kdebug(FMT, a...) printk(" "FMT"\n" , ##a) -#define kproto(FMT, a...) printk("### "FMT"\n" , ##a) -#define knet(FMT, a...) printk(" "FMT"\n" , ##a) - -#if 0 -#define _enter(FMT, a...) kenter(FMT , ##a) -#define _leave(FMT, a...) kleave(FMT , ##a) -#define _debug(FMT, a...) kdebug(FMT , ##a) -#define _proto(FMT, a...) kproto(FMT , ##a) -#define _net(FMT, a...) knet(FMT , ##a) -#else -#define _enter(FMT, a...) do { if (rxrpc_ktrace) kenter(FMT , ##a); } while(0) -#define _leave(FMT, a...) do { if (rxrpc_ktrace) kleave(FMT , ##a); } while(0) -#define _debug(FMT, a...) do { if (rxrpc_kdebug) kdebug(FMT , ##a); } while(0) -#define _proto(FMT, a...) do { if (rxrpc_kproto) kproto(FMT , ##a); } while(0) -#define _net(FMT, a...) do { if (rxrpc_knet) knet (FMT , ##a); } while(0) -#endif - -static inline void rxrpc_discard_my_signals(void) -{ - while (signal_pending(current)) { - siginfo_t sinfo; - - spin_lock_irq(¤t->sighand->siglock); - dequeue_signal(current, ¤t->blocked, &sinfo); - spin_unlock_irq(¤t->sighand->siglock); - } -} - -/* - * call.c - */ -extern struct list_head rxrpc_calls; -extern struct rw_semaphore rxrpc_calls_sem; - -/* - * connection.c - */ -extern struct list_head rxrpc_conns; -extern struct rw_semaphore rxrpc_conns_sem; -extern unsigned long rxrpc_conn_timeout; - -extern void rxrpc_conn_clearall(struct rxrpc_peer *peer); - -/* - * peer.c - */ -extern struct list_head rxrpc_peers; -extern struct rw_semaphore rxrpc_peers_sem; -extern unsigned long rxrpc_peer_timeout; - -extern void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer, - struct rxrpc_message *msg, - struct rxrpc_message *resp); - -extern void rxrpc_peer_clearall(struct rxrpc_transport *trans); - - -/* - * proc.c - */ -#ifdef CONFIG_PROC_FS -extern int rxrpc_proc_init(void); -extern void rxrpc_proc_cleanup(void); -#endif - -/* - * transport.c - */ -extern struct list_head rxrpc_proc_transports; -extern struct rw_semaphore rxrpc_proc_transports_sem; - -#endif /* RXRPC_INTERNAL_H */ diff --git a/net/rxrpc/krxiod.c b/net/rxrpc/krxiod.c deleted file mode 100644 index bbbcd6c..0000000 --- a/net/rxrpc/krxiod.c +++ /dev/null @@ -1,262 +0,0 @@ -/* krxiod.c: Rx I/O daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxiod_sleepq); -static DECLARE_COMPLETION(rxrpc_krxiod_dead); - -static atomic_t rxrpc_krxiod_qcount = ATOMIC_INIT(0); - -static LIST_HEAD(rxrpc_krxiod_transportq); -static DEFINE_SPINLOCK(rxrpc_krxiod_transportq_lock); - -static LIST_HEAD(rxrpc_krxiod_callq); -static DEFINE_SPINLOCK(rxrpc_krxiod_callq_lock); - -static volatile int rxrpc_krxiod_die; - -/*****************************************************************************/ -/* - * Rx I/O daemon - */ -static int rxrpc_krxiod(void *arg) -{ - DECLARE_WAITQUEUE(krxiod,current); - - printk("Started krxiod %d\n",current->pid); - - daemonize("krxiod"); - - /* loop around waiting for work to do */ - do { - /* wait for work or to be told to exit */ - _debug("### Begin Wait"); - if (!atomic_read(&rxrpc_krxiod_qcount)) { - set_current_state(TASK_INTERRUPTIBLE); - - add_wait_queue(&rxrpc_krxiod_sleepq, &krxiod); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (atomic_read(&rxrpc_krxiod_qcount) || - rxrpc_krxiod_die || - signal_pending(current)) - break; - - schedule(); - } - - remove_wait_queue(&rxrpc_krxiod_sleepq, &krxiod); - set_current_state(TASK_RUNNING); - } - _debug("### End Wait"); - - /* do work if been given some to do */ - _debug("### Begin Work"); - - /* see if there's a transport in need of attention */ - if (!list_empty(&rxrpc_krxiod_transportq)) { - struct rxrpc_transport *trans = NULL; - - spin_lock_irq(&rxrpc_krxiod_transportq_lock); - - if (!list_empty(&rxrpc_krxiod_transportq)) { - trans = list_entry( - rxrpc_krxiod_transportq.next, - struct rxrpc_transport, - krxiodq_link); - - list_del_init(&trans->krxiodq_link); - atomic_dec(&rxrpc_krxiod_qcount); - - /* make sure it hasn't gone away and doesn't go - * away */ - if (atomic_read(&trans->usage)>0) - rxrpc_get_transport(trans); - else - trans = NULL; - } - - spin_unlock_irq(&rxrpc_krxiod_transportq_lock); - - if (trans) { - rxrpc_trans_receive_packet(trans); - rxrpc_put_transport(trans); - } - } - - /* see if there's a call in need of attention */ - if (!list_empty(&rxrpc_krxiod_callq)) { - struct rxrpc_call *call = NULL; - - spin_lock_irq(&rxrpc_krxiod_callq_lock); - - if (!list_empty(&rxrpc_krxiod_callq)) { - call = list_entry(rxrpc_krxiod_callq.next, - struct rxrpc_call, - rcv_krxiodq_lk); - list_del_init(&call->rcv_krxiodq_lk); - atomic_dec(&rxrpc_krxiod_qcount); - - /* make sure it hasn't gone away and doesn't go - * away */ - if (atomic_read(&call->usage) > 0) { - _debug("@@@ KRXIOD" - " Begin Attend Call %p", call); - rxrpc_get_call(call); - } - else { - call = NULL; - } - } - - spin_unlock_irq(&rxrpc_krxiod_callq_lock); - - if (call) { - rxrpc_call_do_stuff(call); - rxrpc_put_call(call); - _debug("@@@ KRXIOD End Attend Call %p", call); - } - } - - _debug("### End Work"); - - try_to_freeze(); - - /* discard pending signals */ - rxrpc_discard_my_signals(); - - } while (!rxrpc_krxiod_die); - - /* and that's all */ - complete_and_exit(&rxrpc_krxiod_dead, 0); - -} /* end rxrpc_krxiod() */ - -/*****************************************************************************/ -/* - * start up a krxiod daemon - */ -int __init rxrpc_krxiod_init(void) -{ - return kernel_thread(rxrpc_krxiod, NULL, 0); - -} /* end rxrpc_krxiod_init() */ - -/*****************************************************************************/ -/* - * kill the krxiod daemon and wait for it to complete - */ -void rxrpc_krxiod_kill(void) -{ - rxrpc_krxiod_die = 1; - wake_up_all(&rxrpc_krxiod_sleepq); - wait_for_completion(&rxrpc_krxiod_dead); - -} /* end rxrpc_krxiod_kill() */ - -/*****************************************************************************/ -/* - * queue a transport for attention by krxiod - */ -void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans) -{ - unsigned long flags; - - _enter(""); - - if (list_empty(&trans->krxiodq_link)) { - spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags); - - if (list_empty(&trans->krxiodq_link)) { - if (atomic_read(&trans->usage) > 0) { - list_add_tail(&trans->krxiodq_link, - &rxrpc_krxiod_transportq); - atomic_inc(&rxrpc_krxiod_qcount); - } - } - - spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags); - wake_up_all(&rxrpc_krxiod_sleepq); - } - - _leave(""); - -} /* end rxrpc_krxiod_queue_transport() */ - -/*****************************************************************************/ -/* - * dequeue a transport from krxiod's attention queue - */ -void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans) -{ - unsigned long flags; - - _enter(""); - - spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags); - if (!list_empty(&trans->krxiodq_link)) { - list_del_init(&trans->krxiodq_link); - atomic_dec(&rxrpc_krxiod_qcount); - } - spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags); - - _leave(""); - -} /* end rxrpc_krxiod_dequeue_transport() */ - -/*****************************************************************************/ -/* - * queue a call for attention by krxiod - */ -void rxrpc_krxiod_queue_call(struct rxrpc_call *call) -{ - unsigned long flags; - - if (list_empty(&call->rcv_krxiodq_lk)) { - spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags); - if (atomic_read(&call->usage) > 0) { - list_add_tail(&call->rcv_krxiodq_lk, - &rxrpc_krxiod_callq); - atomic_inc(&rxrpc_krxiod_qcount); - } - spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags); - } - wake_up_all(&rxrpc_krxiod_sleepq); - -} /* end rxrpc_krxiod_queue_call() */ - -/*****************************************************************************/ -/* - * dequeue a call from krxiod's attention queue - */ -void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call) -{ - unsigned long flags; - - spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags); - if (!list_empty(&call->rcv_krxiodq_lk)) { - list_del_init(&call->rcv_krxiodq_lk); - atomic_dec(&rxrpc_krxiod_qcount); - } - spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags); - -} /* end rxrpc_krxiod_dequeue_call() */ diff --git a/net/rxrpc/krxsecd.c b/net/rxrpc/krxsecd.c deleted file mode 100644 index 9a1e7f5..0000000 --- a/net/rxrpc/krxsecd.c +++ /dev/null @@ -1,270 +0,0 @@ -/* krxsecd.c: Rx security daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - * - * This daemon deals with: - * - consulting the application as to whether inbound peers and calls should be authorised - * - generating security challenges for inbound connections - * - responding to security challenges on outbound connections - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxsecd_sleepq); -static DECLARE_COMPLETION(rxrpc_krxsecd_dead); -static volatile int rxrpc_krxsecd_die; - -static atomic_t rxrpc_krxsecd_qcount; - -/* queue of unprocessed inbound messages with seqno #1 and - * RXRPC_CLIENT_INITIATED flag set */ -static LIST_HEAD(rxrpc_krxsecd_initmsgq); -static DEFINE_SPINLOCK(rxrpc_krxsecd_initmsgq_lock); - -static void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg); - -/*****************************************************************************/ -/* - * Rx security daemon - */ -static int rxrpc_krxsecd(void *arg) -{ - DECLARE_WAITQUEUE(krxsecd, current); - - int die; - - printk("Started krxsecd %d\n", current->pid); - - daemonize("krxsecd"); - - /* loop around waiting for work to do */ - do { - /* wait for work or to be told to exit */ - _debug("### Begin Wait"); - if (!atomic_read(&rxrpc_krxsecd_qcount)) { - set_current_state(TASK_INTERRUPTIBLE); - - add_wait_queue(&rxrpc_krxsecd_sleepq, &krxsecd); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (atomic_read(&rxrpc_krxsecd_qcount) || - rxrpc_krxsecd_die || - signal_pending(current)) - break; - - schedule(); - } - - remove_wait_queue(&rxrpc_krxsecd_sleepq, &krxsecd); - set_current_state(TASK_RUNNING); - } - die = rxrpc_krxsecd_die; - _debug("### End Wait"); - - /* see if there're incoming calls in need of authenticating */ - _debug("### Begin Inbound Calls"); - - if (!list_empty(&rxrpc_krxsecd_initmsgq)) { - struct rxrpc_message *msg = NULL; - - spin_lock(&rxrpc_krxsecd_initmsgq_lock); - - if (!list_empty(&rxrpc_krxsecd_initmsgq)) { - msg = list_entry(rxrpc_krxsecd_initmsgq.next, - struct rxrpc_message, link); - list_del_init(&msg->link); - atomic_dec(&rxrpc_krxsecd_qcount); - } - - spin_unlock(&rxrpc_krxsecd_initmsgq_lock); - - if (msg) { - rxrpc_krxsecd_process_incoming_call(msg); - rxrpc_put_message(msg); - } - } - - _debug("### End Inbound Calls"); - - try_to_freeze(); - - /* discard pending signals */ - rxrpc_discard_my_signals(); - - } while (!die); - - /* and that's all */ - complete_and_exit(&rxrpc_krxsecd_dead, 0); - -} /* end rxrpc_krxsecd() */ - -/*****************************************************************************/ -/* - * start up a krxsecd daemon - */ -int __init rxrpc_krxsecd_init(void) -{ - return kernel_thread(rxrpc_krxsecd, NULL, 0); - -} /* end rxrpc_krxsecd_init() */ - -/*****************************************************************************/ -/* - * kill the krxsecd daemon and wait for it to complete - */ -void rxrpc_krxsecd_kill(void) -{ - rxrpc_krxsecd_die = 1; - wake_up_all(&rxrpc_krxsecd_sleepq); - wait_for_completion(&rxrpc_krxsecd_dead); - -} /* end rxrpc_krxsecd_kill() */ - -/*****************************************************************************/ -/* - * clear all pending incoming calls for the specified transport - */ -void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans) -{ - LIST_HEAD(tmp); - - struct rxrpc_message *msg; - struct list_head *_p, *_n; - - _enter("%p",trans); - - /* move all the messages for this transport onto a temp list */ - spin_lock(&rxrpc_krxsecd_initmsgq_lock); - - list_for_each_safe(_p, _n, &rxrpc_krxsecd_initmsgq) { - msg = list_entry(_p, struct rxrpc_message, link); - if (msg->trans == trans) { - list_move_tail(&msg->link, &tmp); - atomic_dec(&rxrpc_krxsecd_qcount); - } - } - - spin_unlock(&rxrpc_krxsecd_initmsgq_lock); - - /* zap all messages on the temp list */ - while (!list_empty(&tmp)) { - msg = list_entry(tmp.next, struct rxrpc_message, link); - list_del_init(&msg->link); - rxrpc_put_message(msg); - } - - _leave(""); -} /* end rxrpc_krxsecd_clear_transport() */ - -/*****************************************************************************/ -/* - * queue a message on the incoming calls list - */ -void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg) -{ - _enter("%p", msg); - - /* queue for processing by krxsecd */ - spin_lock(&rxrpc_krxsecd_initmsgq_lock); - - if (!rxrpc_krxsecd_die) { - rxrpc_get_message(msg); - list_add_tail(&msg->link, &rxrpc_krxsecd_initmsgq); - atomic_inc(&rxrpc_krxsecd_qcount); - } - - spin_unlock(&rxrpc_krxsecd_initmsgq_lock); - - wake_up(&rxrpc_krxsecd_sleepq); - - _leave(""); -} /* end rxrpc_krxsecd_queue_incoming_call() */ - -/*****************************************************************************/ -/* - * process the initial message of an incoming call - */ -void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg) -{ - struct rxrpc_transport *trans = msg->trans; - struct rxrpc_service *srv; - struct rxrpc_call *call; - struct list_head *_p; - unsigned short sid; - int ret; - - _enter("%p{tr=%p}", msg, trans); - - ret = rxrpc_incoming_call(msg->conn, msg, &call); - if (ret < 0) - goto out; - - /* find the matching service on the transport */ - sid = ntohs(msg->hdr.serviceId); - srv = NULL; - - spin_lock(&trans->lock); - list_for_each(_p, &trans->services) { - srv = list_entry(_p, struct rxrpc_service, link); - if (srv->service_id == sid && try_module_get(srv->owner)) { - /* found a match (made sure it won't vanish) */ - _debug("found service '%s'", srv->name); - call->owner = srv->owner; - break; - } - } - spin_unlock(&trans->lock); - - /* report the new connection - * - the func must inc the call's usage count to keep it - */ - ret = -ENOENT; - if (_p != &trans->services) { - /* attempt to accept the call */ - call->conn->service = srv; - call->app_attn_func = srv->attn_func; - call->app_error_func = srv->error_func; - call->app_aemap_func = srv->aemap_func; - - ret = srv->new_call(call); - - /* send an abort if an error occurred */ - if (ret < 0) { - rxrpc_call_abort(call, ret); - } - else { - /* formally receive and ACK the new packet */ - ret = rxrpc_conn_receive_call_packet(call->conn, - call, msg); - } - } - - rxrpc_put_call(call); - out: - if (ret < 0) - rxrpc_trans_immediate_abort(trans, msg, ret); - - _leave(" (%d)", ret); -} /* end rxrpc_krxsecd_process_incoming_call() */ diff --git a/net/rxrpc/krxtimod.c b/net/rxrpc/krxtimod.c deleted file mode 100644 index 9a9b613..0000000 --- a/net/rxrpc/krxtimod.c +++ /dev/null @@ -1,204 +0,0 @@ -/* krxtimod.c: RXRPC timeout daemon - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static DECLARE_COMPLETION(krxtimod_alive); -static DECLARE_COMPLETION(krxtimod_dead); -static DECLARE_WAIT_QUEUE_HEAD(krxtimod_sleepq); -static int krxtimod_die; - -static LIST_HEAD(krxtimod_list); -static DEFINE_SPINLOCK(krxtimod_lock); - -static int krxtimod(void *arg); - -/*****************************************************************************/ -/* - * start the timeout daemon - */ -int rxrpc_krxtimod_start(void) -{ - int ret; - - ret = kernel_thread(krxtimod, NULL, 0); - if (ret < 0) - return ret; - - wait_for_completion(&krxtimod_alive); - - return ret; -} /* end rxrpc_krxtimod_start() */ - -/*****************************************************************************/ -/* - * stop the timeout daemon - */ -void rxrpc_krxtimod_kill(void) -{ - /* get rid of my daemon */ - krxtimod_die = 1; - wake_up(&krxtimod_sleepq); - wait_for_completion(&krxtimod_dead); - -} /* end rxrpc_krxtimod_kill() */ - -/*****************************************************************************/ -/* - * timeout processing daemon - */ -static int krxtimod(void *arg) -{ - DECLARE_WAITQUEUE(myself, current); - - rxrpc_timer_t *timer; - - printk("Started krxtimod %d\n", current->pid); - - daemonize("krxtimod"); - - complete(&krxtimod_alive); - - /* loop around looking for things to attend to */ - loop: - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&krxtimod_sleepq, &myself); - - for (;;) { - unsigned long jif; - long timeout; - - /* deal with the server being asked to die */ - if (krxtimod_die) { - remove_wait_queue(&krxtimod_sleepq, &myself); - _leave(""); - complete_and_exit(&krxtimod_dead, 0); - } - - try_to_freeze(); - - /* discard pending signals */ - rxrpc_discard_my_signals(); - - /* work out the time to elapse before the next event */ - spin_lock(&krxtimod_lock); - if (list_empty(&krxtimod_list)) { - timeout = MAX_SCHEDULE_TIMEOUT; - } - else { - timer = list_entry(krxtimod_list.next, - rxrpc_timer_t, link); - timeout = timer->timo_jif; - jif = jiffies; - - if (time_before_eq((unsigned long) timeout, jif)) - goto immediate; - - else { - timeout = (long) timeout - (long) jiffies; - } - } - spin_unlock(&krxtimod_lock); - - schedule_timeout(timeout); - - set_current_state(TASK_INTERRUPTIBLE); - } - - /* the thing on the front of the queue needs processing - * - we come here with the lock held and timer pointing to the expired - * entry - */ - immediate: - remove_wait_queue(&krxtimod_sleepq, &myself); - set_current_state(TASK_RUNNING); - - _debug("@@@ Begin Timeout of %p", timer); - - /* dequeue the timer */ - list_del_init(&timer->link); - spin_unlock(&krxtimod_lock); - - /* call the timeout function */ - timer->ops->timed_out(timer); - - _debug("@@@ End Timeout"); - goto loop; - -} /* end krxtimod() */ - -/*****************************************************************************/ -/* - * (re-)queue a timer - */ -void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout) -{ - struct list_head *_p; - rxrpc_timer_t *ptimer; - - _enter("%p,%lu", timer, timeout); - - spin_lock(&krxtimod_lock); - - list_del(&timer->link); - - /* the timer was deferred or reset - put it back in the queue at the - * right place */ - timer->timo_jif = jiffies + timeout; - - list_for_each(_p, &krxtimod_list) { - ptimer = list_entry(_p, rxrpc_timer_t, link); - if (time_before(timer->timo_jif, ptimer->timo_jif)) - break; - } - - list_add_tail(&timer->link, _p); /* insert before stopping point */ - - spin_unlock(&krxtimod_lock); - - wake_up(&krxtimod_sleepq); - - _leave(""); -} /* end rxrpc_krxtimod_add_timer() */ - -/*****************************************************************************/ -/* - * dequeue a timer - * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued - */ -int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer) -{ - int ret = 0; - - _enter("%p", timer); - - spin_lock(&krxtimod_lock); - - if (list_empty(&timer->link)) - ret = -ENOENT; - else - list_del_init(&timer->link); - - spin_unlock(&krxtimod_lock); - - wake_up(&krxtimod_sleepq); - - _leave(" = %d", ret); - return ret; -} /* end rxrpc_krxtimod_del_timer() */ diff --git a/net/rxrpc/main.c b/net/rxrpc/main.c deleted file mode 100644 index baec1f7..0000000 --- a/net/rxrpc/main.c +++ /dev/null @@ -1,180 +0,0 @@ -/* main.c: Rx RPC interface - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -MODULE_DESCRIPTION("Rx RPC implementation"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - -__be32 rxrpc_epoch; - -/*****************************************************************************/ -/* - * initialise the Rx module - */ -static int __init rxrpc_initialise(void) -{ - int ret; - - /* my epoch value */ - rxrpc_epoch = htonl(xtime.tv_sec); - - /* register the /proc interface */ -#ifdef CONFIG_PROC_FS - ret = rxrpc_proc_init(); - if (ret<0) - return ret; -#endif - - /* register the sysctl files */ -#ifdef CONFIG_SYSCTL - ret = rxrpc_sysctl_init(); - if (ret<0) - goto error_proc; -#endif - - /* start the krxtimod daemon */ - ret = rxrpc_krxtimod_start(); - if (ret<0) - goto error_sysctl; - - /* start the krxiod daemon */ - ret = rxrpc_krxiod_init(); - if (ret<0) - goto error_krxtimod; - - /* start the krxsecd daemon */ - ret = rxrpc_krxsecd_init(); - if (ret<0) - goto error_krxiod; - - kdebug("\n\n"); - - return 0; - - error_krxiod: - rxrpc_krxiod_kill(); - error_krxtimod: - rxrpc_krxtimod_kill(); - error_sysctl: -#ifdef CONFIG_SYSCTL - rxrpc_sysctl_cleanup(); - error_proc: -#endif -#ifdef CONFIG_PROC_FS - rxrpc_proc_cleanup(); -#endif - return ret; -} /* end rxrpc_initialise() */ - -module_init(rxrpc_initialise); - -/*****************************************************************************/ -/* - * clean up the Rx module - */ -static void __exit rxrpc_cleanup(void) -{ - kenter(""); - - __RXACCT(printk("Outstanding Messages : %d\n", - atomic_read(&rxrpc_message_count))); - __RXACCT(printk("Outstanding Calls : %d\n", - atomic_read(&rxrpc_call_count))); - __RXACCT(printk("Outstanding Connections: %d\n", - atomic_read(&rxrpc_connection_count))); - __RXACCT(printk("Outstanding Peers : %d\n", - atomic_read(&rxrpc_peer_count))); - __RXACCT(printk("Outstanding Transports : %d\n", - atomic_read(&rxrpc_transport_count))); - - rxrpc_krxsecd_kill(); - rxrpc_krxiod_kill(); - rxrpc_krxtimod_kill(); -#ifdef CONFIG_SYSCTL - rxrpc_sysctl_cleanup(); -#endif -#ifdef CONFIG_PROC_FS - rxrpc_proc_cleanup(); -#endif - - __RXACCT(printk("Outstanding Messages : %d\n", - atomic_read(&rxrpc_message_count))); - __RXACCT(printk("Outstanding Calls : %d\n", - atomic_read(&rxrpc_call_count))); - __RXACCT(printk("Outstanding Connections: %d\n", - atomic_read(&rxrpc_connection_count))); - __RXACCT(printk("Outstanding Peers : %d\n", - atomic_read(&rxrpc_peer_count))); - __RXACCT(printk("Outstanding Transports : %d\n", - atomic_read(&rxrpc_transport_count))); - - kleave(""); -} /* end rxrpc_cleanup() */ - -module_exit(rxrpc_cleanup); - -/*****************************************************************************/ -/* - * clear the dead space between task_struct and kernel stack - * - called by supplying -finstrument-functions to gcc - */ -#if 0 -void __cyg_profile_func_enter (void *this_fn, void *call_site) -__attribute__((no_instrument_function)); - -void __cyg_profile_func_enter (void *this_fn, void *call_site) -{ - asm volatile(" movl %%esp,%%edi \n" - " andl %0,%%edi \n" - " addl %1,%%edi \n" - " movl %%esp,%%ecx \n" - " subl %%edi,%%ecx \n" - " shrl $2,%%ecx \n" - " movl $0xedededed,%%eax \n" - " rep stosl \n" - : - : "i"(~(THREAD_SIZE-1)), "i"(sizeof(struct thread_info)) - : "eax", "ecx", "edi", "memory", "cc" - ); -} - -void __cyg_profile_func_exit(void *this_fn, void *call_site) -__attribute__((no_instrument_function)); - -void __cyg_profile_func_exit(void *this_fn, void *call_site) -{ - asm volatile(" movl %%esp,%%edi \n" - " andl %0,%%edi \n" - " addl %1,%%edi \n" - " movl %%esp,%%ecx \n" - " subl %%edi,%%ecx \n" - " shrl $2,%%ecx \n" - " movl $0xdadadada,%%eax \n" - " rep stosl \n" - : - : "i"(~(THREAD_SIZE-1)), "i"(sizeof(struct thread_info)) - : "eax", "ecx", "edi", "memory", "cc" - ); -} -#endif diff --git a/net/rxrpc/peer.c b/net/rxrpc/peer.c deleted file mode 100644 index 8a27515..0000000 --- a/net/rxrpc/peer.c +++ /dev/null @@ -1,398 +0,0 @@ -/* peer.c: Rx RPC peer management - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -__RXACCT_DECL(atomic_t rxrpc_peer_count); -LIST_HEAD(rxrpc_peers); -DECLARE_RWSEM(rxrpc_peers_sem); -unsigned long rxrpc_peer_timeout = 12 * 60 * 60; - -static void rxrpc_peer_do_timeout(struct rxrpc_peer *peer); - -static void __rxrpc_peer_timeout(rxrpc_timer_t *timer) -{ - struct rxrpc_peer *peer = - list_entry(timer, struct rxrpc_peer, timeout); - - _debug("Rx PEER TIMEOUT [%p{u=%d}]", peer, atomic_read(&peer->usage)); - - rxrpc_peer_do_timeout(peer); -} - -static const struct rxrpc_timer_ops rxrpc_peer_timer_ops = { - .timed_out = __rxrpc_peer_timeout, -}; - -/*****************************************************************************/ -/* - * create a peer record - */ -static int __rxrpc_create_peer(struct rxrpc_transport *trans, __be32 addr, - struct rxrpc_peer **_peer) -{ - struct rxrpc_peer *peer; - - _enter("%p,%08x", trans, ntohl(addr)); - - /* allocate and initialise a peer record */ - peer = kzalloc(sizeof(struct rxrpc_peer), GFP_KERNEL); - if (!peer) { - _leave(" = -ENOMEM"); - return -ENOMEM; - } - - atomic_set(&peer->usage, 1); - - INIT_LIST_HEAD(&peer->link); - INIT_LIST_HEAD(&peer->proc_link); - INIT_LIST_HEAD(&peer->conn_idlist); - INIT_LIST_HEAD(&peer->conn_active); - INIT_LIST_HEAD(&peer->conn_graveyard); - spin_lock_init(&peer->conn_gylock); - init_waitqueue_head(&peer->conn_gy_waitq); - rwlock_init(&peer->conn_idlock); - rwlock_init(&peer->conn_lock); - atomic_set(&peer->conn_count, 0); - spin_lock_init(&peer->lock); - rxrpc_timer_init(&peer->timeout, &rxrpc_peer_timer_ops); - - peer->addr.s_addr = addr; - - peer->trans = trans; - peer->ops = trans->peer_ops; - - __RXACCT(atomic_inc(&rxrpc_peer_count)); - *_peer = peer; - _leave(" = 0 (%p)", peer); - - return 0; -} /* end __rxrpc_create_peer() */ - -/*****************************************************************************/ -/* - * find a peer record on the specified transport - * - returns (if successful) with peer record usage incremented - * - resurrects it from the graveyard if found there - */ -int rxrpc_peer_lookup(struct rxrpc_transport *trans, __be32 addr, - struct rxrpc_peer **_peer) -{ - struct rxrpc_peer *peer, *candidate = NULL; - struct list_head *_p; - int ret; - - _enter("%p{%hu},%08x", trans, trans->port, ntohl(addr)); - - /* [common case] search the transport's active list first */ - read_lock(&trans->peer_lock); - list_for_each(_p, &trans->peer_active) { - peer = list_entry(_p, struct rxrpc_peer, link); - if (peer->addr.s_addr == addr) - goto found_active; - } - read_unlock(&trans->peer_lock); - - /* [uncommon case] not active - create a candidate for a new record */ - ret = __rxrpc_create_peer(trans, addr, &candidate); - if (ret < 0) { - _leave(" = %d", ret); - return ret; - } - - /* search the active list again, just in case it appeared whilst we - * were busy */ - write_lock(&trans->peer_lock); - list_for_each(_p, &trans->peer_active) { - peer = list_entry(_p, struct rxrpc_peer, link); - if (peer->addr.s_addr == addr) - goto found_active_second_chance; - } - - /* search the transport's graveyard list */ - spin_lock(&trans->peer_gylock); - list_for_each(_p, &trans->peer_graveyard) { - peer = list_entry(_p, struct rxrpc_peer, link); - if (peer->addr.s_addr == addr) - goto found_in_graveyard; - } - spin_unlock(&trans->peer_gylock); - - /* we can now add the new candidate to the list - * - tell the application layer that this peer has been added - */ - rxrpc_get_transport(trans); - peer = candidate; - candidate = NULL; - - if (peer->ops && peer->ops->adding) { - ret = peer->ops->adding(peer); - if (ret < 0) { - write_unlock(&trans->peer_lock); - __RXACCT(atomic_dec(&rxrpc_peer_count)); - kfree(peer); - rxrpc_put_transport(trans); - _leave(" = %d", ret); - return ret; - } - } - - atomic_inc(&trans->peer_count); - - make_active: - list_add_tail(&peer->link, &trans->peer_active); - - success_uwfree: - write_unlock(&trans->peer_lock); - - if (candidate) { - __RXACCT(atomic_dec(&rxrpc_peer_count)); - kfree(candidate); - } - - if (list_empty(&peer->proc_link)) { - down_write(&rxrpc_peers_sem); - list_add_tail(&peer->proc_link, &rxrpc_peers); - up_write(&rxrpc_peers_sem); - } - - success: - *_peer = peer; - - _leave(" = 0 (%p{u=%d cc=%d})", - peer, - atomic_read(&peer->usage), - atomic_read(&peer->conn_count)); - return 0; - - /* handle the peer being found in the active list straight off */ - found_active: - rxrpc_get_peer(peer); - read_unlock(&trans->peer_lock); - goto success; - - /* handle resurrecting a peer from the graveyard */ - found_in_graveyard: - rxrpc_get_peer(peer); - rxrpc_get_transport(peer->trans); - rxrpc_krxtimod_del_timer(&peer->timeout); - list_del_init(&peer->link); - spin_unlock(&trans->peer_gylock); - goto make_active; - - /* handle finding the peer on the second time through the active - * list */ - found_active_second_chance: - rxrpc_get_peer(peer); - goto success_uwfree; - -} /* end rxrpc_peer_lookup() */ - -/*****************************************************************************/ -/* - * finish with a peer record - * - it gets sent to the graveyard from where it can be resurrected or timed - * out - */ -void rxrpc_put_peer(struct rxrpc_peer *peer) -{ - struct rxrpc_transport *trans = peer->trans; - - _enter("%p{cc=%d a=%08x}", - peer, - atomic_read(&peer->conn_count), - ntohl(peer->addr.s_addr)); - - /* sanity check */ - if (atomic_read(&peer->usage) <= 0) - BUG(); - - write_lock(&trans->peer_lock); - spin_lock(&trans->peer_gylock); - if (likely(!atomic_dec_and_test(&peer->usage))) { - spin_unlock(&trans->peer_gylock); - write_unlock(&trans->peer_lock); - _leave(""); - return; - } - - /* move to graveyard queue */ - list_del(&peer->link); - write_unlock(&trans->peer_lock); - - list_add_tail(&peer->link, &trans->peer_graveyard); - - BUG_ON(!list_empty(&peer->conn_active)); - - rxrpc_krxtimod_add_timer(&peer->timeout, rxrpc_peer_timeout * HZ); - - spin_unlock(&trans->peer_gylock); - - rxrpc_put_transport(trans); - - _leave(" [killed]"); -} /* end rxrpc_put_peer() */ - -/*****************************************************************************/ -/* - * handle a peer timing out in the graveyard - * - called from krxtimod - */ -static void rxrpc_peer_do_timeout(struct rxrpc_peer *peer) -{ - struct rxrpc_transport *trans = peer->trans; - - _enter("%p{u=%d cc=%d a=%08x}", - peer, - atomic_read(&peer->usage), - atomic_read(&peer->conn_count), - ntohl(peer->addr.s_addr)); - - BUG_ON(atomic_read(&peer->usage) < 0); - - /* remove from graveyard if still dead */ - spin_lock(&trans->peer_gylock); - if (atomic_read(&peer->usage) == 0) - list_del_init(&peer->link); - else - peer = NULL; - spin_unlock(&trans->peer_gylock); - - if (!peer) { - _leave(""); - return; /* resurrected */ - } - - /* clear all connections on this peer */ - rxrpc_conn_clearall(peer); - - BUG_ON(!list_empty(&peer->conn_active)); - BUG_ON(!list_empty(&peer->conn_graveyard)); - - /* inform the application layer */ - if (peer->ops && peer->ops->discarding) - peer->ops->discarding(peer); - - if (!list_empty(&peer->proc_link)) { - down_write(&rxrpc_peers_sem); - list_del(&peer->proc_link); - up_write(&rxrpc_peers_sem); - } - - __RXACCT(atomic_dec(&rxrpc_peer_count)); - kfree(peer); - - /* if the graveyard is now empty, wake up anyone waiting for that */ - if (atomic_dec_and_test(&trans->peer_count)) - wake_up(&trans->peer_gy_waitq); - - _leave(" [destroyed]"); -} /* end rxrpc_peer_do_timeout() */ - -/*****************************************************************************/ -/* - * clear all peer records from a transport endpoint - */ -void rxrpc_peer_clearall(struct rxrpc_transport *trans) -{ - DECLARE_WAITQUEUE(myself,current); - - struct rxrpc_peer *peer; - int err; - - _enter("%p",trans); - - /* there shouldn't be any active peers remaining */ - BUG_ON(!list_empty(&trans->peer_active)); - - /* manually timeout all peers in the graveyard */ - spin_lock(&trans->peer_gylock); - while (!list_empty(&trans->peer_graveyard)) { - peer = list_entry(trans->peer_graveyard.next, - struct rxrpc_peer, link); - _debug("Clearing peer %p\n", peer); - err = rxrpc_krxtimod_del_timer(&peer->timeout); - spin_unlock(&trans->peer_gylock); - - if (err == 0) - rxrpc_peer_do_timeout(peer); - - spin_lock(&trans->peer_gylock); - } - spin_unlock(&trans->peer_gylock); - - /* wait for the the peer graveyard to be completely cleared */ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&trans->peer_gy_waitq, &myself); - - while (atomic_read(&trans->peer_count) != 0) { - schedule(); - set_current_state(TASK_UNINTERRUPTIBLE); - } - - remove_wait_queue(&trans->peer_gy_waitq, &myself); - set_current_state(TASK_RUNNING); - - _leave(""); -} /* end rxrpc_peer_clearall() */ - -/*****************************************************************************/ -/* - * calculate and cache the Round-Trip-Time for a message and its response - */ -void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer, - struct rxrpc_message *msg, - struct rxrpc_message *resp) -{ - unsigned long long rtt; - int loop; - - _enter("%p,%p,%p", peer, msg, resp); - - /* calculate the latest RTT */ - rtt = resp->stamp.tv_sec - msg->stamp.tv_sec; - rtt *= 1000000UL; - rtt += resp->stamp.tv_usec - msg->stamp.tv_usec; - - /* add to cache */ - peer->rtt_cache[peer->rtt_point] = rtt; - peer->rtt_point++; - peer->rtt_point %= RXRPC_RTT_CACHE_SIZE; - - if (peer->rtt_usage < RXRPC_RTT_CACHE_SIZE) - peer->rtt_usage++; - - /* recalculate RTT */ - rtt = 0; - for (loop = peer->rtt_usage - 1; loop >= 0; loop--) - rtt += peer->rtt_cache[loop]; - - do_div(rtt, peer->rtt_usage); - peer->rtt = rtt; - - _leave(" RTT=%lu.%lums", - (long) (peer->rtt / 1000), (long) (peer->rtt % 1000)); - -} /* end rxrpc_peer_calculate_rtt() */ diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c deleted file mode 100644 index 8551c87..0000000 --- a/net/rxrpc/proc.c +++ /dev/null @@ -1,617 +0,0 @@ -/* proc.c: /proc interface for RxRPC - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static struct proc_dir_entry *proc_rxrpc; - -static int rxrpc_proc_transports_open(struct inode *inode, struct file *file); -static void *rxrpc_proc_transports_start(struct seq_file *p, loff_t *pos); -static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos); -static void rxrpc_proc_transports_stop(struct seq_file *p, void *v); -static int rxrpc_proc_transports_show(struct seq_file *m, void *v); - -static struct seq_operations rxrpc_proc_transports_ops = { - .start = rxrpc_proc_transports_start, - .next = rxrpc_proc_transports_next, - .stop = rxrpc_proc_transports_stop, - .show = rxrpc_proc_transports_show, -}; - -static const struct file_operations rxrpc_proc_transports_fops = { - .open = rxrpc_proc_transports_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static int rxrpc_proc_peers_open(struct inode *inode, struct file *file); -static void *rxrpc_proc_peers_start(struct seq_file *p, loff_t *pos); -static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos); -static void rxrpc_proc_peers_stop(struct seq_file *p, void *v); -static int rxrpc_proc_peers_show(struct seq_file *m, void *v); - -static struct seq_operations rxrpc_proc_peers_ops = { - .start = rxrpc_proc_peers_start, - .next = rxrpc_proc_peers_next, - .stop = rxrpc_proc_peers_stop, - .show = rxrpc_proc_peers_show, -}; - -static const struct file_operations rxrpc_proc_peers_fops = { - .open = rxrpc_proc_peers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static int rxrpc_proc_conns_open(struct inode *inode, struct file *file); -static void *rxrpc_proc_conns_start(struct seq_file *p, loff_t *pos); -static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos); -static void rxrpc_proc_conns_stop(struct seq_file *p, void *v); -static int rxrpc_proc_conns_show(struct seq_file *m, void *v); - -static struct seq_operations rxrpc_proc_conns_ops = { - .start = rxrpc_proc_conns_start, - .next = rxrpc_proc_conns_next, - .stop = rxrpc_proc_conns_stop, - .show = rxrpc_proc_conns_show, -}; - -static const struct file_operations rxrpc_proc_conns_fops = { - .open = rxrpc_proc_conns_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static int rxrpc_proc_calls_open(struct inode *inode, struct file *file); -static void *rxrpc_proc_calls_start(struct seq_file *p, loff_t *pos); -static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos); -static void rxrpc_proc_calls_stop(struct seq_file *p, void *v); -static int rxrpc_proc_calls_show(struct seq_file *m, void *v); - -static struct seq_operations rxrpc_proc_calls_ops = { - .start = rxrpc_proc_calls_start, - .next = rxrpc_proc_calls_next, - .stop = rxrpc_proc_calls_stop, - .show = rxrpc_proc_calls_show, -}; - -static const struct file_operations rxrpc_proc_calls_fops = { - .open = rxrpc_proc_calls_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static const char *rxrpc_call_states7[] = { - "complet", - "error ", - "rcv_op ", - "rcv_arg", - "got_arg", - "snd_rpl", - "fin_ack", - "snd_arg", - "rcv_rpl", - "got_rpl" -}; - -static const char *rxrpc_call_error_states7[] = { - "no_err ", - "loc_abt", - "rmt_abt", - "loc_err", - "rmt_err" -}; - -/*****************************************************************************/ -/* - * initialise the /proc/net/rxrpc/ directory - */ -int rxrpc_proc_init(void) -{ - struct proc_dir_entry *p; - - proc_rxrpc = proc_mkdir("rxrpc", proc_net); - if (!proc_rxrpc) - goto error; - proc_rxrpc->owner = THIS_MODULE; - - p = create_proc_entry("calls", 0, proc_rxrpc); - if (!p) - goto error_proc; - p->proc_fops = &rxrpc_proc_calls_fops; - p->owner = THIS_MODULE; - - p = create_proc_entry("connections", 0, proc_rxrpc); - if (!p) - goto error_calls; - p->proc_fops = &rxrpc_proc_conns_fops; - p->owner = THIS_MODULE; - - p = create_proc_entry("peers", 0, proc_rxrpc); - if (!p) - goto error_calls; - p->proc_fops = &rxrpc_proc_peers_fops; - p->owner = THIS_MODULE; - - p = create_proc_entry("transports", 0, proc_rxrpc); - if (!p) - goto error_conns; - p->proc_fops = &rxrpc_proc_transports_fops; - p->owner = THIS_MODULE; - - return 0; - - error_conns: - remove_proc_entry("connections", proc_rxrpc); - error_calls: - remove_proc_entry("calls", proc_rxrpc); - error_proc: - remove_proc_entry("rxrpc", proc_net); - error: - return -ENOMEM; -} /* end rxrpc_proc_init() */ - -/*****************************************************************************/ -/* - * clean up the /proc/net/rxrpc/ directory - */ -void rxrpc_proc_cleanup(void) -{ - remove_proc_entry("transports", proc_rxrpc); - remove_proc_entry("peers", proc_rxrpc); - remove_proc_entry("connections", proc_rxrpc); - remove_proc_entry("calls", proc_rxrpc); - - remove_proc_entry("rxrpc", proc_net); - -} /* end rxrpc_proc_cleanup() */ - -/*****************************************************************************/ -/* - * open "/proc/net/rxrpc/transports" which provides a summary of extant transports - */ -static int rxrpc_proc_transports_open(struct inode *inode, struct file *file) -{ - struct seq_file *m; - int ret; - - ret = seq_open(file, &rxrpc_proc_transports_ops); - if (ret < 0) - return ret; - - m = file->private_data; - m->private = PDE(inode)->data; - - return 0; -} /* end rxrpc_proc_transports_open() */ - -/*****************************************************************************/ -/* - * set up the iterator to start reading from the transports list and return the first item - */ -static void *rxrpc_proc_transports_start(struct seq_file *m, loff_t *_pos) -{ - struct list_head *_p; - loff_t pos = *_pos; - - /* lock the list against modification */ - down_read(&rxrpc_proc_transports_sem); - - /* allow for the header line */ - if (!pos) - return SEQ_START_TOKEN; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &rxrpc_proc_transports) - if (!pos--) - break; - - return _p != &rxrpc_proc_transports ? _p : NULL; -} /* end rxrpc_proc_transports_start() */ - -/*****************************************************************************/ -/* - * move to next call in transports list - */ -static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos) -{ - struct list_head *_p; - - (*pos)++; - - _p = v; - _p = (v == SEQ_START_TOKEN) ? rxrpc_proc_transports.next : _p->next; - - return _p != &rxrpc_proc_transports ? _p : NULL; -} /* end rxrpc_proc_transports_next() */ - -/*****************************************************************************/ -/* - * clean up after reading from the transports list - */ -static void rxrpc_proc_transports_stop(struct seq_file *p, void *v) -{ - up_read(&rxrpc_proc_transports_sem); - -} /* end rxrpc_proc_transports_stop() */ - -/*****************************************************************************/ -/* - * display a header line followed by a load of call lines - */ -static int rxrpc_proc_transports_show(struct seq_file *m, void *v) -{ - struct rxrpc_transport *trans = - list_entry(v, struct rxrpc_transport, proc_link); - - /* display header on line 1 */ - if (v == SEQ_START_TOKEN) { - seq_puts(m, "LOCAL USE\n"); - return 0; - } - - /* display one transport per line on subsequent lines */ - seq_printf(m, "%5hu %3d\n", - trans->port, - atomic_read(&trans->usage) - ); - - return 0; -} /* end rxrpc_proc_transports_show() */ - -/*****************************************************************************/ -/* - * open "/proc/net/rxrpc/peers" which provides a summary of extant peers - */ -static int rxrpc_proc_peers_open(struct inode *inode, struct file *file) -{ - struct seq_file *m; - int ret; - - ret = seq_open(file, &rxrpc_proc_peers_ops); - if (ret < 0) - return ret; - - m = file->private_data; - m->private = PDE(inode)->data; - - return 0; -} /* end rxrpc_proc_peers_open() */ - -/*****************************************************************************/ -/* - * set up the iterator to start reading from the peers list and return the - * first item - */ -static void *rxrpc_proc_peers_start(struct seq_file *m, loff_t *_pos) -{ - struct list_head *_p; - loff_t pos = *_pos; - - /* lock the list against modification */ - down_read(&rxrpc_peers_sem); - - /* allow for the header line */ - if (!pos) - return SEQ_START_TOKEN; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &rxrpc_peers) - if (!pos--) - break; - - return _p != &rxrpc_peers ? _p : NULL; -} /* end rxrpc_proc_peers_start() */ - -/*****************************************************************************/ -/* - * move to next conn in peers list - */ -static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos) -{ - struct list_head *_p; - - (*pos)++; - - _p = v; - _p = (v == SEQ_START_TOKEN) ? rxrpc_peers.next : _p->next; - - return _p != &rxrpc_peers ? _p : NULL; -} /* end rxrpc_proc_peers_next() */ - -/*****************************************************************************/ -/* - * clean up after reading from the peers list - */ -static void rxrpc_proc_peers_stop(struct seq_file *p, void *v) -{ - up_read(&rxrpc_peers_sem); - -} /* end rxrpc_proc_peers_stop() */ - -/*****************************************************************************/ -/* - * display a header line followed by a load of conn lines - */ -static int rxrpc_proc_peers_show(struct seq_file *m, void *v) -{ - struct rxrpc_peer *peer = list_entry(v, struct rxrpc_peer, proc_link); - long timeout; - - /* display header on line 1 */ - if (v == SEQ_START_TOKEN) { - seq_puts(m, "LOCAL REMOTE USAGE CONNS TIMEOUT" - " MTU RTT(uS)\n"); - return 0; - } - - /* display one peer per line on subsequent lines */ - timeout = 0; - if (!list_empty(&peer->timeout.link)) - timeout = (long) peer->timeout.timo_jif - - (long) jiffies; - - seq_printf(m, "%5hu %08x %5d %5d %8ld %5Zu %7lu\n", - peer->trans->port, - ntohl(peer->addr.s_addr), - atomic_read(&peer->usage), - atomic_read(&peer->conn_count), - timeout, - peer->if_mtu, - (long) peer->rtt - ); - - return 0; -} /* end rxrpc_proc_peers_show() */ - -/*****************************************************************************/ -/* - * open "/proc/net/rxrpc/connections" which provides a summary of extant - * connections - */ -static int rxrpc_proc_conns_open(struct inode *inode, struct file *file) -{ - struct seq_file *m; - int ret; - - ret = seq_open(file, &rxrpc_proc_conns_ops); - if (ret < 0) - return ret; - - m = file->private_data; - m->private = PDE(inode)->data; - - return 0; -} /* end rxrpc_proc_conns_open() */ - -/*****************************************************************************/ -/* - * set up the iterator to start reading from the conns list and return the - * first item - */ -static void *rxrpc_proc_conns_start(struct seq_file *m, loff_t *_pos) -{ - struct list_head *_p; - loff_t pos = *_pos; - - /* lock the list against modification */ - down_read(&rxrpc_conns_sem); - - /* allow for the header line */ - if (!pos) - return SEQ_START_TOKEN; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &rxrpc_conns) - if (!pos--) - break; - - return _p != &rxrpc_conns ? _p : NULL; -} /* end rxrpc_proc_conns_start() */ - -/*****************************************************************************/ -/* - * move to next conn in conns list - */ -static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos) -{ - struct list_head *_p; - - (*pos)++; - - _p = v; - _p = (v == SEQ_START_TOKEN) ? rxrpc_conns.next : _p->next; - - return _p != &rxrpc_conns ? _p : NULL; -} /* end rxrpc_proc_conns_next() */ - -/*****************************************************************************/ -/* - * clean up after reading from the conns list - */ -static void rxrpc_proc_conns_stop(struct seq_file *p, void *v) -{ - up_read(&rxrpc_conns_sem); - -} /* end rxrpc_proc_conns_stop() */ - -/*****************************************************************************/ -/* - * display a header line followed by a load of conn lines - */ -static int rxrpc_proc_conns_show(struct seq_file *m, void *v) -{ - struct rxrpc_connection *conn; - long timeout; - - conn = list_entry(v, struct rxrpc_connection, proc_link); - - /* display header on line 1 */ - if (v == SEQ_START_TOKEN) { - seq_puts(m, - "LOCAL REMOTE RPORT SRVC CONN END SERIALNO " - "CALLNO MTU TIMEOUT" - "\n"); - return 0; - } - - /* display one conn per line on subsequent lines */ - timeout = 0; - if (!list_empty(&conn->timeout.link)) - timeout = (long) conn->timeout.timo_jif - - (long) jiffies; - - seq_printf(m, - "%5hu %08x %5hu %04hx %08x %-3.3s %08x %08x %5Zu %8ld\n", - conn->trans->port, - ntohl(conn->addr.sin_addr.s_addr), - ntohs(conn->addr.sin_port), - ntohs(conn->service_id), - ntohl(conn->conn_id), - conn->out_clientflag ? "CLT" : "SRV", - conn->serial_counter, - conn->call_counter, - conn->mtu_size, - timeout - ); - - return 0; -} /* end rxrpc_proc_conns_show() */ - -/*****************************************************************************/ -/* - * open "/proc/net/rxrpc/calls" which provides a summary of extant calls - */ -static int rxrpc_proc_calls_open(struct inode *inode, struct file *file) -{ - struct seq_file *m; - int ret; - - ret = seq_open(file, &rxrpc_proc_calls_ops); - if (ret < 0) - return ret; - - m = file->private_data; - m->private = PDE(inode)->data; - - return 0; -} /* end rxrpc_proc_calls_open() */ - -/*****************************************************************************/ -/* - * set up the iterator to start reading from the calls list and return the - * first item - */ -static void *rxrpc_proc_calls_start(struct seq_file *m, loff_t *_pos) -{ - struct list_head *_p; - loff_t pos = *_pos; - - /* lock the list against modification */ - down_read(&rxrpc_calls_sem); - - /* allow for the header line */ - if (!pos) - return SEQ_START_TOKEN; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &rxrpc_calls) - if (!pos--) - break; - - return _p != &rxrpc_calls ? _p : NULL; -} /* end rxrpc_proc_calls_start() */ - -/*****************************************************************************/ -/* - * move to next call in calls list - */ -static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos) -{ - struct list_head *_p; - - (*pos)++; - - _p = v; - _p = (v == SEQ_START_TOKEN) ? rxrpc_calls.next : _p->next; - - return _p != &rxrpc_calls ? _p : NULL; -} /* end rxrpc_proc_calls_next() */ - -/*****************************************************************************/ -/* - * clean up after reading from the calls list - */ -static void rxrpc_proc_calls_stop(struct seq_file *p, void *v) -{ - up_read(&rxrpc_calls_sem); - -} /* end rxrpc_proc_calls_stop() */ - -/*****************************************************************************/ -/* - * display a header line followed by a load of call lines - */ -static int rxrpc_proc_calls_show(struct seq_file *m, void *v) -{ - struct rxrpc_call *call = list_entry(v, struct rxrpc_call, call_link); - - /* display header on line 1 */ - if (v == SEQ_START_TOKEN) { - seq_puts(m, - "LOCAL REMOT SRVC CONN CALL DIR USE " - " L STATE OPCODE ABORT ERRNO\n" - ); - return 0; - } - - /* display one call per line on subsequent lines */ - seq_printf(m, - "%5hu %5hu %04hx %08x %08x %s %3u%c" - " %c %-7.7s %6d %08x %5d\n", - call->conn->trans->port, - ntohs(call->conn->addr.sin_port), - ntohs(call->conn->service_id), - ntohl(call->conn->conn_id), - ntohl(call->call_id), - call->conn->service ? "SVC" : "CLT", - atomic_read(&call->usage), - waitqueue_active(&call->waitq) ? 'w' : ' ', - call->app_last_rcv ? 'Y' : '-', - (call->app_call_state!=RXRPC_CSTATE_ERROR ? - rxrpc_call_states7[call->app_call_state] : - rxrpc_call_error_states7[call->app_err_state]), - call->app_opcode, - call->app_abort_code, - call->app_errno - ); - - return 0; -} /* end rxrpc_proc_calls_show() */ diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c new file mode 100644 index 0000000..5ec7051 --- /dev/null +++ b/net/rxrpc/rxkad.c @@ -0,0 +1,1154 @@ +/* Kerberos-based RxRPC security + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define rxrpc_debug rxkad_debug +#include "ar-internal.h" + +#define RXKAD_VERSION 2 +#define MAXKRB5TICKETLEN 1024 +#define RXKAD_TKT_TYPE_KERBEROS_V5 256 +#define ANAME_SZ 40 /* size of authentication name */ +#define INST_SZ 40 /* size of principal's instance */ +#define REALM_SZ 40 /* size of principal's auth domain */ +#define SNAME_SZ 40 /* size of service name */ + +unsigned rxrpc_debug; +module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(rxrpc_debug, "rxkad debugging mask"); + +struct rxkad_level1_hdr { + __be32 data_size; /* true data size (excluding padding) */ +}; + +struct rxkad_level2_hdr { + __be32 data_size; /* true data size (excluding padding) */ + __be32 checksum; /* decrypted data checksum */ +}; + +MODULE_DESCRIPTION("RxRPC network protocol type-2 security (Kerberos)"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +/* + * this holds a pinned cipher so that keventd doesn't get called by the cipher + * alloc routine, but since we have it to hand, we use it to decrypt RESPONSE + * packets + */ +static struct crypto_blkcipher *rxkad_ci; +static DEFINE_MUTEX(rxkad_ci_mutex); + +/* + * initialise connection security + */ +static int rxkad_init_connection_security(struct rxrpc_connection *conn) +{ + struct rxrpc_key_payload *payload; + struct crypto_blkcipher *ci; + int ret; + + _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); + + payload = conn->key->payload.data; + conn->security_ix = payload->k.security_index; + + ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(ci)) { + _debug("no cipher"); + ret = PTR_ERR(ci); + goto error; + } + + if (crypto_blkcipher_setkey(ci, payload->k.session_key, + sizeof(payload->k.session_key)) < 0) + BUG(); + + switch (conn->security_level) { + case RXRPC_SECURITY_PLAIN: + break; + case RXRPC_SECURITY_AUTH: + conn->size_align = 8; + conn->security_size = sizeof(struct rxkad_level1_hdr); + conn->header_size += sizeof(struct rxkad_level1_hdr); + break; + case RXRPC_SECURITY_ENCRYPT: + conn->size_align = 8; + conn->security_size = sizeof(struct rxkad_level2_hdr); + conn->header_size += sizeof(struct rxkad_level2_hdr); + break; + default: + ret = -EKEYREJECTED; + goto error; + } + + conn->cipher = ci; + ret = 0; +error: + _leave(" = %d", ret); + return ret; +} + +/* + * prime the encryption state with the invariant parts of a connection's + * description + */ +static void rxkad_prime_packet_security(struct rxrpc_connection *conn) +{ + struct rxrpc_key_payload *payload; + struct blkcipher_desc desc; + struct scatterlist sg[2]; + struct rxrpc_crypt iv; + struct { + __be32 x[4]; + } tmpbuf __attribute__((aligned(16))); /* must all be in same page */ + + _enter(""); + + if (!conn->key) + return; + + payload = conn->key->payload.data; + memcpy(&iv, payload->k.session_key, sizeof(iv)); + + desc.tfm = conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + tmpbuf.x[0] = conn->epoch; + tmpbuf.x[1] = conn->cid; + tmpbuf.x[2] = 0; + tmpbuf.x[3] = htonl(conn->security_ix); + + memset(sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + memcpy(&conn->csum_iv, &tmpbuf.x[2], sizeof(conn->csum_iv)); + ASSERTCMP(conn->csum_iv.n[0], ==, tmpbuf.x[2]); + + _leave(""); +} + +/* + * partially encrypt a packet (level 1 security) + */ +static int rxkad_secure_packet_auth(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 data_size, + void *sechdr) +{ + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct { + struct rxkad_level1_hdr hdr; + __be32 first; /* first four bytes of data and padding */ + } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + u16 check; + + sp = rxrpc_skb(skb); + + _enter(""); + + check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + data_size |= (u32) check << 16; + + tmpbuf.hdr.data_size = htonl(data_size); + memcpy(&tmpbuf.first, sechdr + 4, sizeof(tmpbuf.first)); + + /* start the encryption afresh */ + memset(&iv, 0, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + memset(sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + memcpy(sechdr, &tmpbuf, sizeof(tmpbuf)); + + _leave(" = 0"); + return 0; +} + +/* + * wholly encrypt a packet (level 2 security) + */ +static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 data_size, + void *sechdr) +{ + const struct rxrpc_key_payload *payload; + struct rxkad_level2_hdr rxkhdr + __attribute__((aligned(8))); /* must be all on one page */ + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[16]; + struct sk_buff *trailer; + unsigned len; + u16 check; + int nsg; + + sp = rxrpc_skb(skb); + + _enter(""); + + check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + + rxkhdr.data_size = htonl(data_size | (u32) check << 16); + rxkhdr.checksum = 0; + + /* encrypt from the session key */ + payload = call->conn->key->payload.data; + memcpy(&iv, payload->k.session_key, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + memset(sg, 0, sizeof(sg[0]) * 2); + sg_set_buf(&sg[0], sechdr, sizeof(rxkhdr)); + sg_set_buf(&sg[1], &rxkhdr, sizeof(rxkhdr)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(rxkhdr)); + + /* we want to encrypt the skbuff in-place */ + nsg = skb_cow_data(skb, 0, &trailer); + if (nsg < 0 || nsg > 16) + return -ENOMEM; + + len = data_size + call->conn->size_align - 1; + len &= ~(call->conn->size_align - 1); + + skb_to_sgvec(skb, sg, 0, len); + crypto_blkcipher_encrypt_iv(&desc, sg, sg, len); + + _leave(" = 0"); + return 0; +} + +/* + * checksum an RxRPC packet header + */ +static int rxkad_secure_packet(const struct rxrpc_call *call, + struct sk_buff *skb, + size_t data_size, + void *sechdr) +{ + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct { + __be32 x[2]; + } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + __be32 x; + int ret; + + sp = rxrpc_skb(skb); + + _enter("{%d{%x}},{#%u},%zu,", + call->debug_id, key_serial(call->conn->key), ntohl(sp->hdr.seq), + data_size); + + if (!call->conn->cipher) + return 0; + + ret = key_validate(call->conn->key); + if (ret < 0) + return ret; + + /* continue encrypting from where we left off */ + memcpy(&iv, call->conn->csum_iv.x, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + /* calculate the security checksum */ + x = htonl(call->channel << (32 - RXRPC_CIDSHIFT)); + x |= sp->hdr.seq & __constant_cpu_to_be32(0x3fffffff); + tmpbuf.x[0] = sp->hdr.callNumber; + tmpbuf.x[1] = x; + + memset(&sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + x = ntohl(tmpbuf.x[1]); + x = (x >> 16) & 0xffff; + if (x == 0) + x = 1; /* zero checksums are not permitted */ + sp->hdr.cksum = htons(x); + + switch (call->conn->security_level) { + case RXRPC_SECURITY_PLAIN: + ret = 0; + break; + case RXRPC_SECURITY_AUTH: + ret = rxkad_secure_packet_auth(call, skb, data_size, sechdr); + break; + case RXRPC_SECURITY_ENCRYPT: + ret = rxkad_secure_packet_encrypt(call, skb, data_size, + sechdr); + break; + default: + ret = -EPERM; + break; + } + + _leave(" = %d [set %hx]", ret, x); + return ret; +} + +/* + * decrypt partial encryption on a packet (level 1 security) + */ +static int rxkad_verify_packet_auth(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct rxkad_level1_hdr sechdr; + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct sk_buff *trailer; + u32 data_size, buf; + u16 check; + + _enter(""); + + sp = rxrpc_skb(skb); + + /* we want to decrypt the skbuff in-place */ + if (skb_cow_data(skb, 0, &trailer) < 0) + goto nomem; + + skb_to_sgvec(skb, sg, 0, 8); + + /* start the decryption afresh */ + memset(&iv, 0, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + crypto_blkcipher_decrypt_iv(&desc, sg, sg, 8); + + /* remove the decrypted packet length */ + if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0) + goto datalen_error; + if (!skb_pull(skb, sizeof(sechdr))) + BUG(); + + buf = ntohl(sechdr.data_size); + data_size = buf & 0xffff; + + check = buf >> 16; + check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + check &= 0xffff; + if (check != 0) { + *_abort_code = RXKADSEALEDINCON; + goto protocol_error; + } + + /* shorten the packet to remove the padding */ + if (data_size > skb->len) + goto datalen_error; + else if (data_size < skb->len) + skb->len = data_size; + + _leave(" = 0 [dlen=%x]", data_size); + return 0; + +datalen_error: + *_abort_code = RXKADDATALEN; +protocol_error: + _leave(" = -EPROTO"); + return -EPROTO; + +nomem: + _leave(" = -ENOMEM"); + return -ENOMEM; +} + +/* + * wholly decrypt a packet (level 2 security) + */ +static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 *_abort_code) +{ + const struct rxrpc_key_payload *payload; + struct rxkad_level2_hdr sechdr; + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist _sg[4], *sg; + struct sk_buff *trailer; + u32 data_size, buf; + u16 check; + int nsg; + + _enter(",{%d}", skb->len); + + sp = rxrpc_skb(skb); + + /* we want to decrypt the skbuff in-place */ + nsg = skb_cow_data(skb, 0, &trailer); + if (nsg < 0) + goto nomem; + + sg = _sg; + if (unlikely(nsg > 4)) { + sg = kmalloc(sizeof(*sg) * nsg, GFP_NOIO); + if (!sg) + goto nomem; + } + + skb_to_sgvec(skb, sg, 0, skb->len); + + /* decrypt from the session key */ + payload = call->conn->key->payload.data; + memcpy(&iv, payload->k.session_key, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + crypto_blkcipher_decrypt_iv(&desc, sg, sg, skb->len); + if (sg != _sg) + kfree(sg); + + /* remove the decrypted packet length */ + if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0) + goto datalen_error; + if (!skb_pull(skb, sizeof(sechdr))) + BUG(); + + buf = ntohl(sechdr.data_size); + data_size = buf & 0xffff; + + check = buf >> 16; + check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + check &= 0xffff; + if (check != 0) { + *_abort_code = RXKADSEALEDINCON; + goto protocol_error; + } + + /* shorten the packet to remove the padding */ + if (data_size > skb->len) + goto datalen_error; + else if (data_size < skb->len) + skb->len = data_size; + + _leave(" = 0 [dlen=%x]", data_size); + return 0; + +datalen_error: + *_abort_code = RXKADDATALEN; +protocol_error: + _leave(" = -EPROTO"); + return -EPROTO; + +nomem: + _leave(" = -ENOMEM"); + return -ENOMEM; +} + +/* + * verify the security on a received packet + */ +static int rxkad_verify_packet(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct blkcipher_desc desc; + struct rxrpc_skb_priv *sp; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct { + __be32 x[2]; + } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + __be32 x; + __be16 cksum; + int ret; + + sp = rxrpc_skb(skb); + + _enter("{%d{%x}},{#%u}", + call->debug_id, key_serial(call->conn->key), + ntohl(sp->hdr.seq)); + + if (!call->conn->cipher) + return 0; + + if (sp->hdr.securityIndex != 2) { + *_abort_code = RXKADINCONSISTENCY; + _leave(" = -EPROTO [not rxkad]"); + return -EPROTO; + } + + /* continue encrypting from where we left off */ + memcpy(&iv, call->conn->csum_iv.x, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + /* validate the security checksum */ + x = htonl(call->channel << (32 - RXRPC_CIDSHIFT)); + x |= sp->hdr.seq & __constant_cpu_to_be32(0x3fffffff); + tmpbuf.x[0] = call->call_id; + tmpbuf.x[1] = x; + + memset(&sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + x = ntohl(tmpbuf.x[1]); + x = (x >> 16) & 0xffff; + if (x == 0) + x = 1; /* zero checksums are not permitted */ + + cksum = htons(x); + if (sp->hdr.cksum != cksum) { + *_abort_code = RXKADSEALEDINCON; + _leave(" = -EPROTO [csum failed]"); + return -EPROTO; + } + + switch (call->conn->security_level) { + case RXRPC_SECURITY_PLAIN: + ret = 0; + break; + case RXRPC_SECURITY_AUTH: + ret = rxkad_verify_packet_auth(call, skb, _abort_code); + break; + case RXRPC_SECURITY_ENCRYPT: + ret = rxkad_verify_packet_encrypt(call, skb, _abort_code); + break; + default: + ret = -ENOANO; + break; + } + + _leave(" = %d", ret); + return ret; +} + +/* + * issue a challenge + */ +static int rxkad_issue_challenge(struct rxrpc_connection *conn) +{ + struct rxkad_challenge challenge; + struct rxrpc_header hdr; + struct msghdr msg; + struct kvec iov[2]; + size_t len; + int ret; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); + + ret = key_validate(conn->key); + if (ret < 0) + return ret; + + get_random_bytes(&conn->security_nonce, sizeof(conn->security_nonce)); + + challenge.version = htonl(2); + challenge.nonce = htonl(conn->security_nonce); + challenge.min_level = htonl(0); + challenge.__padding = 0; + + msg.msg_name = &conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr.epoch = conn->epoch; + hdr.cid = conn->cid; + hdr.callNumber = 0; + hdr.seq = 0; + hdr.type = RXRPC_PACKET_TYPE_CHALLENGE; + hdr.flags = conn->out_clientflag; + hdr.userStatus = 0; + hdr.securityIndex = conn->security_ix; + hdr._rsvd = 0; + hdr.serviceId = conn->service_id; + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = &challenge; + iov[1].iov_len = sizeof(challenge); + + len = iov[0].iov_len + iov[1].iov_len; + + hdr.serial = htonl(atomic_inc_return(&conn->serial)); + _proto("Tx CHALLENGE %%%u", ntohl(hdr.serial)); + + ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * send a Kerberos security response + */ +static int rxkad_send_response(struct rxrpc_connection *conn, + struct rxrpc_header *hdr, + struct rxkad_response *resp, + const struct rxkad_key *s2) +{ + struct msghdr msg; + struct kvec iov[3]; + size_t len; + int ret; + + _enter(""); + + msg.msg_name = &conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr->epoch = conn->epoch; + hdr->seq = 0; + hdr->type = RXRPC_PACKET_TYPE_RESPONSE; + hdr->flags = conn->out_clientflag; + hdr->userStatus = 0; + hdr->_rsvd = 0; + + iov[0].iov_base = hdr; + iov[0].iov_len = sizeof(*hdr); + iov[1].iov_base = resp; + iov[1].iov_len = sizeof(*resp); + iov[2].iov_base = (void *) s2->ticket; + iov[2].iov_len = s2->ticket_len; + + len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; + + hdr->serial = htonl(atomic_inc_return(&conn->serial)); + _proto("Tx RESPONSE %%%u", ntohl(hdr->serial)); + + ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 3, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * calculate the response checksum + */ +static void rxkad_calc_response_checksum(struct rxkad_response *response) +{ + u32 csum = 1000003; + int loop; + u8 *p = (u8 *) response; + + for (loop = sizeof(*response); loop > 0; loop--) + csum = csum * 0x10204081 + *p++; + + response->encrypted.checksum = htonl(csum); +} + +/* + * load a scatterlist with a potentially split-page buffer + */ +static void rxkad_sg_set_buf2(struct scatterlist sg[2], + void *buf, size_t buflen) +{ + + memset(sg, 0, sizeof(sg)); + + sg_set_buf(&sg[0], buf, buflen); + if (sg[0].offset + buflen > PAGE_SIZE) { + /* the buffer was split over two pages */ + sg[0].length = PAGE_SIZE - sg[0].offset; + sg_set_buf(&sg[1], buf + sg[0].length, buflen - sg[0].length); + } + + ASSERTCMP(sg[0].length + sg[1].length, ==, buflen); +} + +/* + * encrypt the response packet + */ +static void rxkad_encrypt_response(struct rxrpc_connection *conn, + struct rxkad_response *resp, + const struct rxkad_key *s2) +{ + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist ssg[2], dsg[2]; + + /* continue encrypting from where we left off */ + memcpy(&iv, s2->session_key, sizeof(iv)); + desc.tfm = conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted)); + memcpy(dsg, ssg, sizeof(dsg)); + crypto_blkcipher_encrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted)); +} + +/* + * respond to a challenge packet + */ +static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + const struct rxrpc_key_payload *payload; + struct rxkad_challenge challenge; + struct rxkad_response resp + __attribute__((aligned(8))); /* must be aligned for crypto */ + struct rxrpc_skb_priv *sp; + u32 version, nonce, min_level, abort_code; + int ret; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); + + if (!conn->key) { + _leave(" = -EPROTO [no key]"); + return -EPROTO; + } + + ret = key_validate(conn->key); + if (ret < 0) { + *_abort_code = RXKADEXPIRED; + return ret; + } + + abort_code = RXKADPACKETSHORT; + sp = rxrpc_skb(skb); + if (skb_copy_bits(skb, 0, &challenge, sizeof(challenge)) < 0) + goto protocol_error; + + version = ntohl(challenge.version); + nonce = ntohl(challenge.nonce); + min_level = ntohl(challenge.min_level); + + _proto("Rx CHALLENGE %%%u { v=%u n=%u ml=%u }", + ntohl(sp->hdr.serial), version, nonce, min_level); + + abort_code = RXKADINCONSISTENCY; + if (version != RXKAD_VERSION) + goto protocol_error; + + abort_code = RXKADLEVELFAIL; + if (conn->security_level < min_level) + goto protocol_error; + + payload = conn->key->payload.data; + + /* build the response packet */ + memset(&resp, 0, sizeof(resp)); + + resp.version = RXKAD_VERSION; + resp.encrypted.epoch = conn->epoch; + resp.encrypted.cid = conn->cid; + resp.encrypted.securityIndex = htonl(conn->security_ix); + resp.encrypted.call_id[0] = + (conn->channels[0] ? conn->channels[0]->call_id : 0); + resp.encrypted.call_id[1] = + (conn->channels[1] ? conn->channels[1]->call_id : 0); + resp.encrypted.call_id[2] = + (conn->channels[2] ? conn->channels[2]->call_id : 0); + resp.encrypted.call_id[3] = + (conn->channels[3] ? conn->channels[3]->call_id : 0); + resp.encrypted.inc_nonce = htonl(nonce + 1); + resp.encrypted.level = htonl(conn->security_level); + resp.kvno = htonl(payload->k.kvno); + resp.ticket_len = htonl(payload->k.ticket_len); + + /* calculate the response checksum and then do the encryption */ + rxkad_calc_response_checksum(&resp); + rxkad_encrypt_response(conn, &resp, &payload->k); + return rxkad_send_response(conn, &sp->hdr, &resp, &payload->k); + +protocol_error: + *_abort_code = abort_code; + _leave(" = -EPROTO [%d]", abort_code); + return -EPROTO; +} + +/* + * decrypt the kerberos IV ticket in the response + */ +static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, + void *ticket, size_t ticket_len, + struct rxrpc_crypt *_session_key, + time_t *_expiry, + u32 *_abort_code) +{ + struct blkcipher_desc desc; + struct rxrpc_crypt iv, key; + struct scatterlist ssg[1], dsg[1]; + struct in_addr addr; + unsigned life; + time_t issue, now; + bool little_endian; + int ret; + u8 *p, *q, *name, *end; + + _enter("{%d},{%x}", conn->debug_id, key_serial(conn->server_key)); + + *_expiry = 0; + + ret = key_validate(conn->server_key); + if (ret < 0) { + switch (ret) { + case -EKEYEXPIRED: + *_abort_code = RXKADEXPIRED; + goto error; + default: + *_abort_code = RXKADNOAUTH; + goto error; + } + } + + ASSERT(conn->server_key->payload.data != NULL); + ASSERTCMP((unsigned long) ticket & 7UL, ==, 0); + + memcpy(&iv, &conn->server_key->type_data, sizeof(iv)); + + desc.tfm = conn->server_key->payload.data; + desc.info = iv.x; + desc.flags = 0; + + sg_init_one(&ssg[0], ticket, ticket_len); + memcpy(dsg, ssg, sizeof(dsg)); + crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, ticket_len); + + p = ticket; + end = p + ticket_len; + +#define Z(size) \ + ({ \ + u8 *__str = p; \ + q = memchr(p, 0, end - p); \ + if (!q || q - p > (size)) \ + goto bad_ticket; \ + for (; p < q; p++) \ + if (!isprint(*p)) \ + goto bad_ticket; \ + p++; \ + __str; \ + }) + + /* extract the ticket flags */ + _debug("KIV FLAGS: %x", *p); + little_endian = *p & 1; + p++; + + /* extract the authentication name */ + name = Z(ANAME_SZ); + _debug("KIV ANAME: %s", name); + + /* extract the principal's instance */ + name = Z(INST_SZ); + _debug("KIV INST : %s", name); + + /* extract the principal's authentication domain */ + name = Z(REALM_SZ); + _debug("KIV REALM: %s", name); + + if (end - p < 4 + 8 + 4 + 2) + goto bad_ticket; + + /* get the IPv4 address of the entity that requested the ticket */ + memcpy(&addr, p, sizeof(addr)); + p += 4; + _debug("KIV ADDR : "NIPQUAD_FMT, NIPQUAD(addr)); + + /* get the session key from the ticket */ + memcpy(&key, p, sizeof(key)); + p += 8; + _debug("KIV KEY : %08x %08x", ntohl(key.n[0]), ntohl(key.n[1])); + memcpy(_session_key, &key, sizeof(key)); + + /* get the ticket's lifetime */ + life = *p++ * 5 * 60; + _debug("KIV LIFE : %u", life); + + /* get the issue time of the ticket */ + if (little_endian) { + __le32 stamp; + memcpy(&stamp, p, 4); + issue = le32_to_cpu(stamp); + } else { + __be32 stamp; + memcpy(&stamp, p, 4); + issue = be32_to_cpu(stamp); + } + p += 4; + now = xtime.tv_sec; + _debug("KIV ISSUE: %lx [%lx]", issue, now); + + /* check the ticket is in date */ + if (issue > now) { + *_abort_code = RXKADNOAUTH; + ret = -EKEYREJECTED; + goto error; + } + + if (issue < now - life) { + *_abort_code = RXKADEXPIRED; + ret = -EKEYEXPIRED; + goto error; + } + + *_expiry = issue + life; + + /* get the service name */ + name = Z(SNAME_SZ); + _debug("KIV SNAME: %s", name); + + /* get the service instance name */ + name = Z(INST_SZ); + _debug("KIV SINST: %s", name); + + ret = 0; +error: + _leave(" = %d", ret); + return ret; + +bad_ticket: + *_abort_code = RXKADBADTICKET; + ret = -EBADMSG; + goto error; +} + +/* + * decrypt the response packet + */ +static void rxkad_decrypt_response(struct rxrpc_connection *conn, + struct rxkad_response *resp, + const struct rxrpc_crypt *session_key) +{ + struct blkcipher_desc desc; + struct scatterlist ssg[2], dsg[2]; + struct rxrpc_crypt iv; + + _enter(",,%08x%08x", + ntohl(session_key->n[0]), ntohl(session_key->n[1])); + + ASSERT(rxkad_ci != NULL); + + mutex_lock(&rxkad_ci_mutex); + if (crypto_blkcipher_setkey(rxkad_ci, session_key->x, + sizeof(*session_key)) < 0) + BUG(); + + memcpy(&iv, session_key, sizeof(iv)); + desc.tfm = rxkad_ci; + desc.info = iv.x; + desc.flags = 0; + + rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted)); + memcpy(dsg, ssg, sizeof(dsg)); + crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted)); + mutex_unlock(&rxkad_ci_mutex); + + _leave(""); +} + +/* + * verify a response + */ +static int rxkad_verify_response(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct rxkad_response response + __attribute__((aligned(8))); /* must be aligned for crypto */ + struct rxrpc_skb_priv *sp; + struct rxrpc_crypt session_key; + time_t expiry; + void *ticket; + u32 abort_code, version, kvno, ticket_len, csum, level; + int ret; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key)); + + abort_code = RXKADPACKETSHORT; + if (skb_copy_bits(skb, 0, &response, sizeof(response)) < 0) + goto protocol_error; + if (!pskb_pull(skb, sizeof(response))) + BUG(); + + version = ntohl(response.version); + ticket_len = ntohl(response.ticket_len); + kvno = ntohl(response.kvno); + sp = rxrpc_skb(skb); + _proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }", + ntohl(sp->hdr.serial), version, kvno, ticket_len); + + abort_code = RXKADINCONSISTENCY; + if (version != RXKAD_VERSION) + + abort_code = RXKADTICKETLEN; + if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) + goto protocol_error; + + abort_code = RXKADUNKNOWNKEY; + if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) + goto protocol_error; + + /* extract the kerberos ticket and decrypt and decode it */ + ticket = kmalloc(ticket_len, GFP_NOFS); + if (!ticket) + return -ENOMEM; + + abort_code = RXKADPACKETSHORT; + if (skb_copy_bits(skb, 0, ticket, ticket_len) < 0) + goto protocol_error_free; + + ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key, + &expiry, &abort_code); + if (ret < 0) { + *_abort_code = abort_code; + kfree(ticket); + return ret; + } + + /* use the session key from inside the ticket to decrypt the + * response */ + rxkad_decrypt_response(conn, &response, &session_key); + + abort_code = RXKADSEALEDINCON; + if (response.encrypted.epoch != conn->epoch) + goto protocol_error_free; + if (response.encrypted.cid != conn->cid) + goto protocol_error_free; + if (ntohl(response.encrypted.securityIndex) != conn->security_ix) + goto protocol_error_free; + csum = response.encrypted.checksum; + response.encrypted.checksum = 0; + rxkad_calc_response_checksum(&response); + if (response.encrypted.checksum != csum) + goto protocol_error_free; + + if (ntohl(response.encrypted.call_id[0]) > INT_MAX || + ntohl(response.encrypted.call_id[1]) > INT_MAX || + ntohl(response.encrypted.call_id[2]) > INT_MAX || + ntohl(response.encrypted.call_id[3]) > INT_MAX) + goto protocol_error_free; + + abort_code = RXKADOUTOFSEQUENCE; + if (response.encrypted.inc_nonce != htonl(conn->security_nonce + 1)) + goto protocol_error_free; + + abort_code = RXKADLEVELFAIL; + level = ntohl(response.encrypted.level); + if (level > RXRPC_SECURITY_ENCRYPT) + goto protocol_error_free; + conn->security_level = level; + + /* create a key to hold the security data and expiration time - after + * this the connection security can be handled in exactly the same way + * as for a client connection */ + ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno); + if (ret < 0) { + kfree(ticket); + return ret; + } + + kfree(ticket); + _leave(" = 0"); + return 0; + +protocol_error_free: + kfree(ticket); +protocol_error: + *_abort_code = abort_code; + _leave(" = -EPROTO [%d]", abort_code); + return -EPROTO; +} + +/* + * clear the connection security + */ +static void rxkad_clear(struct rxrpc_connection *conn) +{ + _enter(""); + + if (conn->cipher) + crypto_free_blkcipher(conn->cipher); +} + +/* + * RxRPC Kerberos-based security + */ +static struct rxrpc_security rxkad = { + .owner = THIS_MODULE, + .name = "rxkad", + .security_index = RXKAD_VERSION, + .init_connection_security = rxkad_init_connection_security, + .prime_packet_security = rxkad_prime_packet_security, + .secure_packet = rxkad_secure_packet, + .verify_packet = rxkad_verify_packet, + .issue_challenge = rxkad_issue_challenge, + .respond_to_challenge = rxkad_respond_to_challenge, + .verify_response = rxkad_verify_response, + .clear = rxkad_clear, +}; + +static __init int rxkad_init(void) +{ + _enter(""); + + /* pin the cipher we need so that the crypto layer doesn't invoke + * keventd to go get it */ + rxkad_ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(rxkad_ci)) + return PTR_ERR(rxkad_ci); + + return rxrpc_register_security(&rxkad); +} + +module_init(rxkad_init); + +static __exit void rxkad_exit(void) +{ + _enter(""); + + rxrpc_unregister_security(&rxkad); + crypto_free_blkcipher(rxkad_ci); +} + +module_exit(rxkad_exit); diff --git a/net/rxrpc/rxrpc_syms.c b/net/rxrpc/rxrpc_syms.c deleted file mode 100644 index 9896fd8..0000000 --- a/net/rxrpc/rxrpc_syms.c +++ /dev/null @@ -1,34 +0,0 @@ -/* rxrpc_syms.c: exported Rx RPC layer interface symbols - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include - -#include -#include -#include -#include - -/* call.c */ -EXPORT_SYMBOL(rxrpc_create_call); -EXPORT_SYMBOL(rxrpc_put_call); -EXPORT_SYMBOL(rxrpc_call_abort); -EXPORT_SYMBOL(rxrpc_call_read_data); -EXPORT_SYMBOL(rxrpc_call_write_data); - -/* connection.c */ -EXPORT_SYMBOL(rxrpc_create_connection); -EXPORT_SYMBOL(rxrpc_put_connection); - -/* transport.c */ -EXPORT_SYMBOL(rxrpc_create_transport); -EXPORT_SYMBOL(rxrpc_put_transport); -EXPORT_SYMBOL(rxrpc_add_service); -EXPORT_SYMBOL(rxrpc_del_service); diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c deleted file mode 100644 index 8842907..0000000 --- a/net/rxrpc/sysctl.c +++ /dev/null @@ -1,121 +0,0 @@ -/* sysctl.c: Rx RPC control - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -int rxrpc_ktrace; -int rxrpc_kdebug; -int rxrpc_kproto; -int rxrpc_knet; - -#ifdef CONFIG_SYSCTL -static struct ctl_table_header *rxrpc_sysctl = NULL; - -static ctl_table rxrpc_sysctl_table[] = { - { - .ctl_name = 1, - .procname = "kdebug", - .data = &rxrpc_kdebug, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = 2, - .procname = "ktrace", - .data = &rxrpc_ktrace, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = 3, - .procname = "kproto", - .data = &rxrpc_kproto, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = 4, - .procname = "knet", - .data = &rxrpc_knet, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = 5, - .procname = "peertimo", - .data = &rxrpc_peer_timeout, - .maxlen = sizeof(unsigned long), - .mode = 0644, - .proc_handler = &proc_doulongvec_minmax - }, - { - .ctl_name = 6, - .procname = "conntimo", - .data = &rxrpc_conn_timeout, - .maxlen = sizeof(unsigned long), - .mode = 0644, - .proc_handler = &proc_doulongvec_minmax - }, - { .ctl_name = 0 } -}; - -static ctl_table rxrpc_dir_sysctl_table[] = { - { - .ctl_name = 1, - .procname = "rxrpc", - .maxlen = 0, - .mode = 0555, - .child = rxrpc_sysctl_table - }, - { .ctl_name = 0 } -}; -#endif /* CONFIG_SYSCTL */ - -/*****************************************************************************/ -/* - * initialise the sysctl stuff for Rx RPC - */ -int rxrpc_sysctl_init(void) -{ -#ifdef CONFIG_SYSCTL - rxrpc_sysctl = register_sysctl_table(rxrpc_dir_sysctl_table); - if (!rxrpc_sysctl) - return -ENOMEM; -#endif /* CONFIG_SYSCTL */ - - return 0; -} /* end rxrpc_sysctl_init() */ - -/*****************************************************************************/ -/* - * clean up the sysctl stuff for Rx RPC - */ -void rxrpc_sysctl_cleanup(void) -{ -#ifdef CONFIG_SYSCTL - if (rxrpc_sysctl) { - unregister_sysctl_table(rxrpc_sysctl); - rxrpc_sysctl = NULL; - } -#endif /* CONFIG_SYSCTL */ - -} /* end rxrpc_sysctl_cleanup() */ diff --git a/net/rxrpc/transport.c b/net/rxrpc/transport.c deleted file mode 100644 index 8e57be2..0000000 --- a/net/rxrpc/transport.c +++ /dev/null @@ -1,846 +0,0 @@ -/* transport.c: Rx Transport routines - * - * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -#include /* this should _really_ be in errqueue.h.. */ -#endif -#include -#include -#include "internal.h" - -struct errormsg { - struct cmsghdr cmsg; /* control message header */ - struct sock_extended_err ee; /* extended error information */ - struct sockaddr_in icmp_src; /* ICMP packet source address */ -}; - -static DEFINE_SPINLOCK(rxrpc_transports_lock); -static struct list_head rxrpc_transports = LIST_HEAD_INIT(rxrpc_transports); - -__RXACCT_DECL(atomic_t rxrpc_transport_count); -LIST_HEAD(rxrpc_proc_transports); -DECLARE_RWSEM(rxrpc_proc_transports_sem); - -static void rxrpc_data_ready(struct sock *sk, int count); -static void rxrpc_error_report(struct sock *sk); -static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans, - struct list_head *msgq); -static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans); - -/*****************************************************************************/ -/* - * create a new transport endpoint using the specified UDP port - */ -int rxrpc_create_transport(unsigned short port, - struct rxrpc_transport **_trans) -{ - struct rxrpc_transport *trans; - struct sockaddr_in sin; - mm_segment_t oldfs; - struct sock *sock; - int ret, opt; - - _enter("%hu", port); - - trans = kzalloc(sizeof(struct rxrpc_transport), GFP_KERNEL); - if (!trans) - return -ENOMEM; - - atomic_set(&trans->usage, 1); - INIT_LIST_HEAD(&trans->services); - INIT_LIST_HEAD(&trans->link); - INIT_LIST_HEAD(&trans->krxiodq_link); - spin_lock_init(&trans->lock); - INIT_LIST_HEAD(&trans->peer_active); - INIT_LIST_HEAD(&trans->peer_graveyard); - spin_lock_init(&trans->peer_gylock); - init_waitqueue_head(&trans->peer_gy_waitq); - rwlock_init(&trans->peer_lock); - atomic_set(&trans->peer_count, 0); - trans->port = port; - - /* create a UDP socket to be my actual transport endpoint */ - ret = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &trans->socket); - if (ret < 0) - goto error; - - /* use the specified port */ - if (port) { - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - ret = trans->socket->ops->bind(trans->socket, - (struct sockaddr *) &sin, - sizeof(sin)); - if (ret < 0) - goto error; - } - - opt = 1; - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = trans->socket->ops->setsockopt(trans->socket, SOL_IP, IP_RECVERR, - (char *) &opt, sizeof(opt)); - set_fs(oldfs); - - spin_lock(&rxrpc_transports_lock); - list_add(&trans->link, &rxrpc_transports); - spin_unlock(&rxrpc_transports_lock); - - /* set the socket up */ - sock = trans->socket->sk; - sock->sk_user_data = trans; - sock->sk_data_ready = rxrpc_data_ready; - sock->sk_error_report = rxrpc_error_report; - - down_write(&rxrpc_proc_transports_sem); - list_add_tail(&trans->proc_link, &rxrpc_proc_transports); - up_write(&rxrpc_proc_transports_sem); - - __RXACCT(atomic_inc(&rxrpc_transport_count)); - - *_trans = trans; - _leave(" = 0 (%p)", trans); - return 0; - - error: - /* finish cleaning up the transport (not really needed here, but...) */ - if (trans->socket) - trans->socket->ops->shutdown(trans->socket, 2); - - /* close the socket */ - if (trans->socket) { - trans->socket->sk->sk_user_data = NULL; - sock_release(trans->socket); - trans->socket = NULL; - } - - kfree(trans); - - - _leave(" = %d", ret); - return ret; -} /* end rxrpc_create_transport() */ - -/*****************************************************************************/ -/* - * destroy a transport endpoint - */ -void rxrpc_put_transport(struct rxrpc_transport *trans) -{ - _enter("%p{u=%d p=%hu}", - trans, atomic_read(&trans->usage), trans->port); - - BUG_ON(atomic_read(&trans->usage) <= 0); - - /* to prevent a race, the decrement and the dequeue must be - * effectively atomic */ - spin_lock(&rxrpc_transports_lock); - if (likely(!atomic_dec_and_test(&trans->usage))) { - spin_unlock(&rxrpc_transports_lock); - _leave(""); - return; - } - - list_del(&trans->link); - spin_unlock(&rxrpc_transports_lock); - - /* finish cleaning up the transport */ - if (trans->socket) - trans->socket->ops->shutdown(trans->socket, 2); - - rxrpc_krxsecd_clear_transport(trans); - rxrpc_krxiod_dequeue_transport(trans); - - /* discard all peer information */ - rxrpc_peer_clearall(trans); - - down_write(&rxrpc_proc_transports_sem); - list_del(&trans->proc_link); - up_write(&rxrpc_proc_transports_sem); - __RXACCT(atomic_dec(&rxrpc_transport_count)); - - /* close the socket */ - if (trans->socket) { - trans->socket->sk->sk_user_data = NULL; - sock_release(trans->socket); - trans->socket = NULL; - } - - kfree(trans); - - _leave(""); -} /* end rxrpc_put_transport() */ - -/*****************************************************************************/ -/* - * add a service to a transport to be listened upon - */ -int rxrpc_add_service(struct rxrpc_transport *trans, - struct rxrpc_service *newsrv) -{ - struct rxrpc_service *srv; - struct list_head *_p; - int ret = -EEXIST; - - _enter("%p{%hu},%p{%hu}", - trans, trans->port, newsrv, newsrv->service_id); - - /* verify that the service ID is not already present */ - spin_lock(&trans->lock); - - list_for_each(_p, &trans->services) { - srv = list_entry(_p, struct rxrpc_service, link); - if (srv->service_id == newsrv->service_id) - goto out; - } - - /* okay - add the transport to the list */ - list_add_tail(&newsrv->link, &trans->services); - rxrpc_get_transport(trans); - ret = 0; - - out: - spin_unlock(&trans->lock); - - _leave("= %d", ret); - return ret; -} /* end rxrpc_add_service() */ - -/*****************************************************************************/ -/* - * remove a service from a transport - */ -void rxrpc_del_service(struct rxrpc_transport *trans, struct rxrpc_service *srv) -{ - _enter("%p{%hu},%p{%hu}", trans, trans->port, srv, srv->service_id); - - spin_lock(&trans->lock); - list_del(&srv->link); - spin_unlock(&trans->lock); - - rxrpc_put_transport(trans); - - _leave(""); -} /* end rxrpc_del_service() */ - -/*****************************************************************************/ -/* - * INET callback when data has been received on the socket. - */ -static void rxrpc_data_ready(struct sock *sk, int count) -{ - struct rxrpc_transport *trans; - - _enter("%p{t=%p},%d", sk, sk->sk_user_data, count); - - /* queue the transport for attention by krxiod */ - trans = (struct rxrpc_transport *) sk->sk_user_data; - if (trans) - rxrpc_krxiod_queue_transport(trans); - - /* wake up anyone waiting on the socket */ - if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) - wake_up_interruptible(sk->sk_sleep); - - _leave(""); -} /* end rxrpc_data_ready() */ - -/*****************************************************************************/ -/* - * INET callback when an ICMP error packet is received - * - sk->err is error (EHOSTUNREACH, EPROTO or EMSGSIZE) - */ -static void rxrpc_error_report(struct sock *sk) -{ - struct rxrpc_transport *trans; - - _enter("%p{t=%p}", sk, sk->sk_user_data); - - /* queue the transport for attention by krxiod */ - trans = (struct rxrpc_transport *) sk->sk_user_data; - if (trans) { - trans->error_rcvd = 1; - rxrpc_krxiod_queue_transport(trans); - } - - /* wake up anyone waiting on the socket */ - if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) - wake_up_interruptible(sk->sk_sleep); - - _leave(""); -} /* end rxrpc_error_report() */ - -/*****************************************************************************/ -/* - * split a message up, allocating message records and filling them in - * from the contents of a socket buffer - */ -static int rxrpc_incoming_msg(struct rxrpc_transport *trans, - struct sk_buff *pkt, - struct list_head *msgq) -{ - struct rxrpc_message *msg; - int ret; - - _enter(""); - - msg = kzalloc(sizeof(struct rxrpc_message), GFP_KERNEL); - if (!msg) { - _leave(" = -ENOMEM"); - return -ENOMEM; - } - - atomic_set(&msg->usage, 1); - list_add_tail(&msg->link,msgq); - - /* dig out the Rx routing parameters */ - if (skb_copy_bits(pkt, sizeof(struct udphdr), - &msg->hdr, sizeof(msg->hdr)) < 0) { - ret = -EBADMSG; - goto error; - } - - msg->trans = trans; - msg->state = RXRPC_MSG_RECEIVED; - skb_get_timestamp(pkt, &msg->stamp); - if (msg->stamp.tv_sec == 0) { - do_gettimeofday(&msg->stamp); - if (pkt->sk) - sock_enable_timestamp(pkt->sk); - } - msg->seq = ntohl(msg->hdr.seq); - - /* attach the packet */ - skb_get(pkt); - msg->pkt = pkt; - - msg->offset = sizeof(struct udphdr) + sizeof(struct rxrpc_header); - msg->dsize = msg->pkt->len - msg->offset; - - _net("Rx Received packet from %s (%08x;%08x,%1x,%d,%s,%02x,%d,%d)", - msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server", - ntohl(msg->hdr.epoch), - (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT, - ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK, - ntohl(msg->hdr.callNumber), - rxrpc_pkts[msg->hdr.type], - msg->hdr.flags, - ntohs(msg->hdr.serviceId), - msg->hdr.securityIndex); - - __RXACCT(atomic_inc(&rxrpc_message_count)); - - /* split off jumbo packets */ - while (msg->hdr.type == RXRPC_PACKET_TYPE_DATA && - msg->hdr.flags & RXRPC_JUMBO_PACKET - ) { - struct rxrpc_jumbo_header jumbo; - struct rxrpc_message *jumbomsg = msg; - - _debug("split jumbo packet"); - - /* quick sanity check */ - ret = -EBADMSG; - if (msg->dsize < - RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header)) - goto error; - if (msg->hdr.flags & RXRPC_LAST_PACKET) - goto error; - - /* dig out the secondary header */ - if (skb_copy_bits(pkt, msg->offset + RXRPC_JUMBO_DATALEN, - &jumbo, sizeof(jumbo)) < 0) - goto error; - - /* allocate a new message record */ - ret = -ENOMEM; - msg = kmemdup(jumbomsg, sizeof(struct rxrpc_message), GFP_KERNEL); - if (!msg) - goto error; - - list_add_tail(&msg->link, msgq); - - /* adjust the jumbo packet */ - jumbomsg->dsize = RXRPC_JUMBO_DATALEN; - - /* attach the packet here too */ - skb_get(pkt); - - /* adjust the parameters */ - msg->seq++; - msg->hdr.seq = htonl(msg->seq); - msg->hdr.serial = htonl(ntohl(msg->hdr.serial) + 1); - msg->offset += RXRPC_JUMBO_DATALEN + - sizeof(struct rxrpc_jumbo_header); - msg->dsize -= RXRPC_JUMBO_DATALEN + - sizeof(struct rxrpc_jumbo_header); - msg->hdr.flags = jumbo.flags; - msg->hdr._rsvd = jumbo._rsvd; - - _net("Rx Split jumbo packet from %s" - " (%08x;%08x,%1x,%d,%s,%02x,%d,%d)", - msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server", - ntohl(msg->hdr.epoch), - (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT, - ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK, - ntohl(msg->hdr.callNumber), - rxrpc_pkts[msg->hdr.type], - msg->hdr.flags, - ntohs(msg->hdr.serviceId), - msg->hdr.securityIndex); - - __RXACCT(atomic_inc(&rxrpc_message_count)); - } - - _leave(" = 0 #%d", atomic_read(&rxrpc_message_count)); - return 0; - - error: - while (!list_empty(msgq)) { - msg = list_entry(msgq->next, struct rxrpc_message, link); - list_del_init(&msg->link); - - rxrpc_put_message(msg); - } - - _leave(" = %d", ret); - return ret; -} /* end rxrpc_incoming_msg() */ - -/*****************************************************************************/ -/* - * accept a new call - * - called from krxiod in process context - */ -void rxrpc_trans_receive_packet(struct rxrpc_transport *trans) -{ - struct rxrpc_message *msg; - struct rxrpc_peer *peer; - struct sk_buff *pkt; - int ret; - __be32 addr; - __be16 port; - - LIST_HEAD(msgq); - - _enter("%p{%d}", trans, trans->port); - - for (;;) { - /* deal with outstanting errors first */ - if (trans->error_rcvd) - rxrpc_trans_receive_error_report(trans); - - /* attempt to receive a packet */ - pkt = skb_recv_datagram(trans->socket->sk, 0, 1, &ret); - if (!pkt) { - if (ret == -EAGAIN) { - _leave(" EAGAIN"); - return; - } - - /* an icmp error may have occurred */ - rxrpc_krxiod_queue_transport(trans); - _leave(" error %d\n", ret); - return; - } - - /* we'll probably need to checksum it (didn't call - * sock_recvmsg) */ - if (skb_checksum_complete(pkt)) { - kfree_skb(pkt); - rxrpc_krxiod_queue_transport(trans); - _leave(" CSUM failed"); - return; - } - - addr = pkt->nh.iph->saddr; - port = pkt->h.uh->source; - - _net("Rx Received UDP packet from %08x:%04hu", - ntohl(addr), ntohs(port)); - - /* unmarshall the Rx parameters and split jumbo packets */ - ret = rxrpc_incoming_msg(trans, pkt, &msgq); - if (ret < 0) { - kfree_skb(pkt); - rxrpc_krxiod_queue_transport(trans); - _leave(" bad packet"); - return; - } - - BUG_ON(list_empty(&msgq)); - - msg = list_entry(msgq.next, struct rxrpc_message, link); - - /* locate the record for the peer from which it - * originated */ - ret = rxrpc_peer_lookup(trans, addr, &peer); - if (ret < 0) { - kdebug("Rx No connections from that peer"); - rxrpc_trans_immediate_abort(trans, msg, -EINVAL); - goto finished_msg; - } - - /* try and find a matching connection */ - ret = rxrpc_connection_lookup(peer, msg, &msg->conn); - if (ret < 0) { - kdebug("Rx Unknown Connection"); - rxrpc_trans_immediate_abort(trans, msg, -EINVAL); - rxrpc_put_peer(peer); - goto finished_msg; - } - rxrpc_put_peer(peer); - - /* deal with the first packet of a new call */ - if (msg->hdr.flags & RXRPC_CLIENT_INITIATED && - msg->hdr.type == RXRPC_PACKET_TYPE_DATA && - ntohl(msg->hdr.seq) == 1 - ) { - _debug("Rx New server call"); - rxrpc_trans_receive_new_call(trans, &msgq); - goto finished_msg; - } - - /* deal with subsequent packet(s) of call */ - _debug("Rx Call packet"); - while (!list_empty(&msgq)) { - msg = list_entry(msgq.next, struct rxrpc_message, link); - list_del_init(&msg->link); - - ret = rxrpc_conn_receive_call_packet(msg->conn, NULL, msg); - if (ret < 0) { - rxrpc_trans_immediate_abort(trans, msg, ret); - rxrpc_put_message(msg); - goto finished_msg; - } - - rxrpc_put_message(msg); - } - - goto finished_msg; - - /* dispose of the packets */ - finished_msg: - while (!list_empty(&msgq)) { - msg = list_entry(msgq.next, struct rxrpc_message, link); - list_del_init(&msg->link); - - rxrpc_put_message(msg); - } - kfree_skb(pkt); - } - - _leave(""); - -} /* end rxrpc_trans_receive_packet() */ - -/*****************************************************************************/ -/* - * accept a new call from a client trying to connect to one of my services - * - called in process context - */ -static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans, - struct list_head *msgq) -{ - struct rxrpc_message *msg; - - _enter(""); - - /* only bother with the first packet */ - msg = list_entry(msgq->next, struct rxrpc_message, link); - list_del_init(&msg->link); - rxrpc_krxsecd_queue_incoming_call(msg); - rxrpc_put_message(msg); - - _leave(" = 0"); - - return 0; -} /* end rxrpc_trans_receive_new_call() */ - -/*****************************************************************************/ -/* - * perform an immediate abort without connection or call structures - */ -int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans, - struct rxrpc_message *msg, - int error) -{ - struct rxrpc_header ahdr; - struct sockaddr_in sin; - struct msghdr msghdr; - struct kvec iov[2]; - __be32 _error; - int len, ret; - - _enter("%p,%p,%d", trans, msg, error); - - /* don't abort an abort packet */ - if (msg->hdr.type == RXRPC_PACKET_TYPE_ABORT) { - _leave(" = 0"); - return 0; - } - - _error = htonl(-error); - - /* set up the message to be transmitted */ - memcpy(&ahdr, &msg->hdr, sizeof(ahdr)); - ahdr.epoch = msg->hdr.epoch; - ahdr.serial = htonl(1); - ahdr.seq = 0; - ahdr.type = RXRPC_PACKET_TYPE_ABORT; - ahdr.flags = RXRPC_LAST_PACKET; - ahdr.flags |= ~msg->hdr.flags & RXRPC_CLIENT_INITIATED; - - iov[0].iov_len = sizeof(ahdr); - iov[0].iov_base = &ahdr; - iov[1].iov_len = sizeof(_error); - iov[1].iov_base = &_error; - - len = sizeof(ahdr) + sizeof(_error); - - memset(&sin,0,sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = msg->pkt->h.uh->source; - sin.sin_addr.s_addr = msg->pkt->nh.iph->saddr; - - msghdr.msg_name = &sin; - msghdr.msg_namelen = sizeof(sin); - msghdr.msg_control = NULL; - msghdr.msg_controllen = 0; - msghdr.msg_flags = MSG_DONTWAIT; - - _net("Sending message type %d of %d bytes to %08x:%d", - ahdr.type, - len, - ntohl(sin.sin_addr.s_addr), - ntohs(sin.sin_port)); - - /* send the message */ - ret = kernel_sendmsg(trans->socket, &msghdr, iov, 2, len); - - _leave(" = %d", ret); - return ret; -} /* end rxrpc_trans_immediate_abort() */ - -/*****************************************************************************/ -/* - * receive an ICMP error report and percolate it to all connections - * heading to the affected host or port - */ -static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans) -{ - struct rxrpc_connection *conn; - struct sockaddr_in sin; - struct rxrpc_peer *peer; - struct list_head connq, *_p; - struct errormsg emsg; - struct msghdr msg; - __be16 port; - int local, err; - - _enter("%p", trans); - - for (;;) { - trans->error_rcvd = 0; - - /* try and receive an error message */ - msg.msg_name = &sin; - msg.msg_namelen = sizeof(sin); - msg.msg_control = &emsg; - msg.msg_controllen = sizeof(emsg); - msg.msg_flags = 0; - - err = kernel_recvmsg(trans->socket, &msg, NULL, 0, 0, - MSG_ERRQUEUE | MSG_DONTWAIT | MSG_TRUNC); - - if (err == -EAGAIN) { - _leave(""); - return; - } - - if (err < 0) { - printk("%s: unable to recv an error report: %d\n", - __FUNCTION__, err); - _leave(""); - return; - } - - msg.msg_controllen = (char *) msg.msg_control - (char *) &emsg; - - if (msg.msg_controllen < sizeof(emsg.cmsg) || - msg.msg_namelen < sizeof(sin)) { - printk("%s: short control message" - " (nlen=%u clen=%Zu fl=%x)\n", - __FUNCTION__, - msg.msg_namelen, - msg.msg_controllen, - msg.msg_flags); - continue; - } - - _net("Rx Received control message" - " { len=%Zu level=%u type=%u }", - emsg.cmsg.cmsg_len, - emsg.cmsg.cmsg_level, - emsg.cmsg.cmsg_type); - - if (sin.sin_family != AF_INET) { - printk("Rx Ignoring error report with non-INET address" - " (fam=%u)", - sin.sin_family); - continue; - } - - _net("Rx Received message pertaining to host addr=%x port=%hu", - ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port)); - - if (emsg.cmsg.cmsg_level != SOL_IP || - emsg.cmsg.cmsg_type != IP_RECVERR) { - printk("Rx Ignoring unknown error report" - " { level=%u type=%u }", - emsg.cmsg.cmsg_level, - emsg.cmsg.cmsg_type); - continue; - } - - if (msg.msg_controllen < sizeof(emsg.cmsg) + sizeof(emsg.ee)) { - printk("%s: short error message (%Zu)\n", - __FUNCTION__, msg.msg_controllen); - _leave(""); - return; - } - - port = sin.sin_port; - - switch (emsg.ee.ee_origin) { - case SO_EE_ORIGIN_ICMP: - local = 0; - switch (emsg.ee.ee_type) { - case ICMP_DEST_UNREACH: - switch (emsg.ee.ee_code) { - case ICMP_NET_UNREACH: - _net("Rx Received ICMP Network Unreachable"); - port = 0; - err = -ENETUNREACH; - break; - case ICMP_HOST_UNREACH: - _net("Rx Received ICMP Host Unreachable"); - port = 0; - err = -EHOSTUNREACH; - break; - case ICMP_PORT_UNREACH: - _net("Rx Received ICMP Port Unreachable"); - err = -ECONNREFUSED; - break; - case ICMP_NET_UNKNOWN: - _net("Rx Received ICMP Unknown Network"); - port = 0; - err = -ENETUNREACH; - break; - case ICMP_HOST_UNKNOWN: - _net("Rx Received ICMP Unknown Host"); - port = 0; - err = -EHOSTUNREACH; - break; - default: - _net("Rx Received ICMP DestUnreach { code=%u }", - emsg.ee.ee_code); - err = emsg.ee.ee_errno; - break; - } - break; - - case ICMP_TIME_EXCEEDED: - _net("Rx Received ICMP TTL Exceeded"); - err = emsg.ee.ee_errno; - break; - - default: - _proto("Rx Received ICMP error { type=%u code=%u }", - emsg.ee.ee_type, emsg.ee.ee_code); - err = emsg.ee.ee_errno; - break; - } - break; - - case SO_EE_ORIGIN_LOCAL: - _proto("Rx Received local error { error=%d }", - emsg.ee.ee_errno); - local = 1; - err = emsg.ee.ee_errno; - break; - - case SO_EE_ORIGIN_NONE: - case SO_EE_ORIGIN_ICMP6: - default: - _proto("Rx Received error report { orig=%u }", - emsg.ee.ee_origin); - local = 0; - err = emsg.ee.ee_errno; - break; - } - - /* find all the connections between this transport and the - * affected destination */ - INIT_LIST_HEAD(&connq); - - if (rxrpc_peer_lookup(trans, sin.sin_addr.s_addr, - &peer) == 0) { - read_lock(&peer->conn_lock); - list_for_each(_p, &peer->conn_active) { - conn = list_entry(_p, struct rxrpc_connection, - link); - if (port && conn->addr.sin_port != port) - continue; - if (!list_empty(&conn->err_link)) - continue; - - rxrpc_get_connection(conn); - list_add_tail(&conn->err_link, &connq); - } - read_unlock(&peer->conn_lock); - - /* service all those connections */ - while (!list_empty(&connq)) { - conn = list_entry(connq.next, - struct rxrpc_connection, - err_link); - list_del(&conn->err_link); - - rxrpc_conn_handle_error(conn, local, err); - - rxrpc_put_connection(conn); - } - - rxrpc_put_peer(peer); - } - } - - _leave(""); - return; -} /* end rxrpc_trans_receive_error_report() */ diff --git a/net/sched/Kconfig b/net/sched/Kconfig index f4544dd..475df84 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -46,62 +46,6 @@ config NET_SCH_FIFO if NET_SCHED -choice - prompt "Packet scheduler clock source" - default NET_SCH_CLK_GETTIMEOFDAY - ---help--- - Packet schedulers need a monotonic clock that increments at a static - rate. The kernel provides several suitable interfaces, each with - different properties: - - - high resolution (us or better) - - fast to read (minimal locking, no i/o access) - - synchronized on all processors - - handles cpu clock frequency changes - - but nothing provides all of the above. - -config NET_SCH_CLK_JIFFIES - bool "Timer interrupt" - ---help--- - Say Y here if you want to use the timer interrupt (jiffies) as clock - source. This clock source is fast, synchronized on all processors and - handles cpu clock frequency changes, but its resolution is too low - for accurate shaping except at very low speed. - -config NET_SCH_CLK_GETTIMEOFDAY - bool "gettimeofday" - ---help--- - Say Y here if you want to use gettimeofday as clock source. This clock - source has high resolution, is synchronized on all processors and - handles cpu clock frequency changes, but it is slow. - - Choose this if you need a high resolution clock source but can't use - the CPU's cycle counter. - -# don't allow on SMP x86 because they can have unsynchronized TSCs. -# gettimeofday is a good alternative -config NET_SCH_CLK_CPU - bool "CPU cycle counter" - depends on ((X86_TSC || X86_64) && !SMP) || ALPHA || SPARC64 || PPC64 || IA64 - ---help--- - Say Y here if you want to use the CPU's cycle counter as clock source. - This is a cheap and high resolution clock source, but on some - architectures it is not synchronized on all processors and doesn't - handle cpu clock frequency changes. - - The useable cycle counters are: - - x86/x86_64 - Timestamp Counter - alpha - Cycle Counter - sparc64 - %ticks register - ppc64 - Time base - ia64 - Interval Time Counter - - Choose this if your CPU's cycle counter is working properly. - -endchoice - comment "Queueing/Scheduling" config NET_SCH_CBQ diff --git a/net/sched/act_api.c b/net/sched/act_api.c index cb21617..711dd26 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -25,12 +25,12 @@ #include #include #include #include -#include #include #include #include #include #include +#include void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo) { @@ -93,15 +93,15 @@ static int tcf_dump_walker(struct sk_buf continue; a->priv = p; a->order = n_i; - r = (struct rtattr*) skb->tail; + r = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, a->order, 0, NULL); err = tcf_action_dump_1(skb, a, 0, 0); if (err < 0) { index--; - skb_trim(skb, (u8*)r - skb->data); + nlmsg_trim(skb, r); goto done; } - r->rta_len = skb->tail - (u8*)r; + r->rta_len = skb_tail_pointer(skb) - (u8 *)r; n_i++; if (n_i >= TCA_ACT_MAX_PRIO) goto done; @@ -114,7 +114,7 @@ done: return n_i; rtattr_failure: - skb_trim(skb, (u8*)r - skb->data); + nlmsg_trim(skb, r); goto done; } @@ -125,7 +125,7 @@ static int tcf_del_walker(struct sk_buff struct rtattr *r ; int i= 0, n_i = 0; - r = (struct rtattr*) skb->tail; + r = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, a->order, 0, NULL); RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind); for (i = 0; i < (hinfo->hmask + 1); i++) { @@ -140,11 +140,11 @@ static int tcf_del_walker(struct sk_buff } } RTA_PUT(skb, TCA_FCNT, 4, &n_i); - r->rta_len = skb->tail - (u8*)r; + r->rta_len = skb_tail_pointer(skb) - (u8 *)r; return n_i; rtattr_failure: - skb_trim(skb, (u8*)r - skb->data); + nlmsg_trim(skb, r); return -EINVAL; } @@ -423,7 +423,7 @@ int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { int err = -EINVAL; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *r; if (a->ops == NULL || a->ops->dump == NULL) @@ -432,15 +432,15 @@ tcf_action_dump_1(struct sk_buff *skb, s RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind); if (tcf_action_copy_stats(skb, a, 0)) goto rtattr_failure; - r = (struct rtattr*) skb->tail; + r = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, TCA_OPTIONS, 0, NULL); if ((err = tcf_action_dump_old(skb, a, bind, ref)) > 0) { - r->rta_len = skb->tail - (u8*)r; + r->rta_len = skb_tail_pointer(skb) - (u8 *)r; return err; } rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -449,17 +449,17 @@ tcf_action_dump(struct sk_buff *skb, str { struct tc_action *a; int err = -EINVAL; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *r ; while ((a = act) != NULL) { - r = (struct rtattr*) skb->tail; + r = (struct rtattr *)skb_tail_pointer(skb); act = a->next; RTA_PUT(skb, a->order, 0, NULL); err = tcf_action_dump_1(skb, a, bind, ref); if (err < 0) goto errout; - r->rta_len = skb->tail - (u8*)r; + r->rta_len = skb_tail_pointer(skb) - (u8 *)r; } return 0; @@ -467,7 +467,7 @@ tcf_action_dump(struct sk_buff *skb, str rtattr_failure: err = -EINVAL; errout: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return err; } @@ -635,7 +635,7 @@ tca_get_fill(struct sk_buff *skb, struct { struct tcamsg *t; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *x; nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags); @@ -645,20 +645,20 @@ tca_get_fill(struct sk_buff *skb, struct t->tca__pad1 = 0; t->tca__pad2 = 0; - x = (struct rtattr*) skb->tail; + x = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, TCA_ACT_TAB, 0, NULL); if (tcf_action_dump(skb, a, bind, ref) < 0) goto rtattr_failure; - x->rta_len = skb->tail - (u8*)x; + x->rta_len = skb_tail_pointer(skb) - (u8 *)x; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -767,7 +767,7 @@ static int tca_action_flush(struct rtatt return -ENOBUFS; } - b = (unsigned char *)skb->tail; + b = skb_tail_pointer(skb); if (rtattr_parse_nested(tb, TCA_ACT_MAX, rta) < 0) goto err_out; @@ -783,16 +783,16 @@ static int tca_action_flush(struct rtatt t->tca__pad1 = 0; t->tca__pad2 = 0; - x = (struct rtattr *) skb->tail; + x = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, TCA_ACT_TAB, 0, NULL); err = a->ops->walk(skb, &dcb, RTM_DELACTION, a); if (err < 0) goto rtattr_failure; - x->rta_len = skb->tail - (u8 *) x; + x->rta_len = skb_tail_pointer(skb) - (u8 *)x; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; nlh->nlmsg_flags |= NLM_F_ROOT; module_put(a->ops->owner); kfree(a); @@ -884,7 +884,7 @@ static int tcf_add_notify(struct tc_acti if (!skb) return -ENOBUFS; - b = (unsigned char *)skb->tail; + b = skb_tail_pointer(skb); nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags); t = NLMSG_DATA(nlh); @@ -892,15 +892,15 @@ static int tcf_add_notify(struct tc_acti t->tca__pad1 = 0; t->tca__pad2 = 0; - x = (struct rtattr*) skb->tail; + x = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, TCA_ACT_TAB, 0, NULL); if (tcf_action_dump(skb, a, 0, 0) < 0) goto rtattr_failure; - x->rta_len = skb->tail - (u8*)x; + x->rta_len = skb_tail_pointer(skb) - (u8 *)x; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; NETLINK_CB(skb).dst_group = RTNLGRP_TC; err = rtnetlink_send(skb, pid, RTNLGRP_TC, flags&NLM_F_ECHO); @@ -1015,7 +1015,7 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) { struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *x; struct tc_action_ops *a_o; struct tc_action a; @@ -1048,7 +1048,7 @@ tc_dump_action(struct sk_buff *skb, stru t->tca__pad1 = 0; t->tca__pad2 = 0; - x = (struct rtattr *) skb->tail; + x = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, TCA_ACT_TAB, 0, NULL); ret = a_o->walk(skb, cb, RTM_GETACTION, &a); @@ -1056,12 +1056,12 @@ tc_dump_action(struct sk_buff *skb, stru goto rtattr_failure; if (ret > 0) { - x->rta_len = skb->tail - (u8 *) x; + x->rta_len = skb_tail_pointer(skb) - (u8 *)x; ret = skb->len; } else - skb_trim(skb, (u8*)x - skb->data); + nlmsg_trim(skb, x); - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; if (NETLINK_CB(cb->skb).pid && ret) nlh->nlmsg_flags |= NLM_F_MULTI; module_put(a_o->owner); @@ -1070,20 +1070,15 @@ tc_dump_action(struct sk_buff *skb, stru rtattr_failure: nlmsg_failure: module_put(a_o->owner); - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return skb->len; } static int __init tc_action_init(void) { - struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC]; - - if (link_p) { - link_p[RTM_NEWACTION-RTM_BASE].doit = tc_ctl_action; - link_p[RTM_DELACTION-RTM_BASE].doit = tc_ctl_action; - link_p[RTM_GETACTION-RTM_BASE].doit = tc_ctl_action; - link_p[RTM_GETACTION-RTM_BASE].dumpit = tc_dump_action; - } + rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL); + rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL); + rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action); return 0; } diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 87d0faf..7517f37 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -28,6 +28,7 @@ #include #include #include #include +#include #include #include #include @@ -155,7 +156,7 @@ #endif static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_gact opt; struct tcf_gact *gact = a->priv; struct tcf_t t; @@ -181,7 +182,7 @@ #endif return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 47f0b13..00b05f4 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -30,6 +30,7 @@ #include #include #include #include +#include #include #include #include @@ -245,7 +246,7 @@ static int tcf_ipt(struct sk_buff *skb, static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tcf_ipt *ipt = a->priv; struct ipt_entry_target *t; struct tcf_t tm; @@ -277,7 +278,7 @@ static int tcf_ipt_dump(struct sk_buff * return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); kfree(t); return -1; } diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 3e93683..de21c92 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -30,6 +30,7 @@ #include #include #include #include +#include #include #include #include @@ -206,7 +207,7 @@ bad_mirred: static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tcf_mirred *m = a->priv; struct tc_mirred opt; struct tcf_t t; @@ -225,7 +226,7 @@ static int tcf_mirred_dump(struct sk_buf return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 3d6a2fc..45b3cda 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -27,6 +27,7 @@ #include #include #include #include +#include #include #include #include @@ -136,7 +137,7 @@ static int tcf_pedit(struct sk_buff *skb } } - pptr = skb->nh.raw; + pptr = skb_network_header(skb); spin_lock(&p->tcf_lock); @@ -195,7 +196,7 @@ done: static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tcf_pedit *p = a->priv; struct tc_pedit *opt; struct tcf_t t; @@ -226,7 +227,7 @@ static int tcf_pedit_dump(struct sk_buff return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); kfree(opt); return -1; } diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 10a5a5c..616f465 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -30,6 +30,7 @@ #include #include #include #include +#include #define L2T(p,L) ((p)->tcfp_R_tab->data[(L)>>(p)->tcfp_R_tab->rate.cell_log]) #define L2T_P(p,L) ((p)->tcfp_P_tab->data[(L)>>(p)->tcfp_P_tab->rate.cell_log]) @@ -80,7 +81,7 @@ static int tcf_act_police_walker(struct continue; a->priv = p; a->order = index; - r = (struct rtattr*) skb->tail; + r = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, a->order, 0, NULL); if (type == RTM_DELACTION) err = tcf_action_dump_1(skb, a, 0, 1); @@ -88,10 +89,10 @@ static int tcf_act_police_walker(struct err = tcf_action_dump_1(skb, a, 0, 0); if (err < 0) { index--; - skb_trim(skb, (u8*)r - skb->data); + nlmsg_trim(skb, r); goto done; } - r->rta_len = skb->tail - (u8*)r; + r->rta_len = skb_tail_pointer(skb) - (u8 *)r; n_i++; } } @@ -102,7 +103,7 @@ done: return n_i; rtattr_failure: - skb_trim(skb, (u8*)r - skb->data); + nlmsg_trim(skb, r); goto done; } #endif @@ -240,7 +241,7 @@ #endif if (ret != ACT_P_CREATED) return ret; - PSCHED_GET_TIME(police->tcfp_t_c); + police->tcfp_t_c = psched_get_time(); police->tcf_index = parm->index ? parm->index : tcf_hash_new_index(&police_idx_gen, &police_hash_info); h = tcf_hash(police->tcf_index, POL_TAB_MASK); @@ -295,10 +296,9 @@ #endif return police->tcfp_result; } - PSCHED_GET_TIME(now); - - toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c, - police->tcfp_burst); + now = psched_get_time(); + toks = psched_tdiff_bounded(now, police->tcfp_t_c, + police->tcfp_burst); if (police->tcfp_P_tab) { ptoks = toks + police->tcfp_ptoks; if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) @@ -326,7 +326,7 @@ #endif static int tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tcf_police *police = a->priv; struct tc_police opt; @@ -355,7 +355,7 @@ #endif return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -494,7 +494,7 @@ #endif } if (police->tcfp_P_tab) police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); - PSCHED_GET_TIME(police->tcfp_t_c); + police->tcfp_t_c = psched_get_time(); police->tcf_index = parm->index ? parm->index : tcf_police_new_index(); police->tcf_action = parm->action; @@ -542,9 +542,9 @@ #endif return police->tcfp_result; } - PSCHED_GET_TIME(now); - toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c, - police->tcfp_burst); + now = psched_get_time(); + toks = psched_tdiff_bounded(now, police->tcfp_t_c, + police->tcfp_burst); if (police->tcfp_P_tab) { ptoks = toks + police->tcfp_ptoks; if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) @@ -572,7 +572,7 @@ EXPORT_SYMBOL(tcf_police); int tcf_police_dump(struct sk_buff *skb, struct tcf_police *police) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_police opt; opt.index = police->tcf_index; @@ -598,7 +598,7 @@ #endif return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index c797118..36e1eda 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -16,6 +16,7 @@ #include #include #include #include +#include #include #define TCA_ACT_SIMP 22 @@ -155,7 +156,7 @@ static inline int tcf_simp_cleanup(struc static inline int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tcf_defact *d = a->priv; struct tc_defact opt; struct tcf_t t; @@ -173,7 +174,7 @@ static inline int tcf_simp_dump(struct s return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 5c6ffdb..ebf94ed 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -29,9 +29,10 @@ #include #include #include #include -#include #include #include +#include +#include #include #include #include @@ -323,7 +324,7 @@ tcf_fill_node(struct sk_buff *skb, struc { struct tcmsg *tcm; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags); tcm = NLMSG_DATA(nlh); @@ -340,12 +341,12 @@ tcf_fill_node(struct sk_buff *skb, struc if (tp->ops->dump && tp->ops->dump(tp, fh, skb, tcm) < 0) goto rtattr_failure; } - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -399,7 +400,6 @@ static int tc_dump_tfilter(struct sk_buf if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL) return skb->len; - read_lock(&qdisc_tree_lock); if (!tcm->tcm_parent) q = dev->qdisc_sleeping; else @@ -456,7 +456,6 @@ errout: if (cl) cops->put(q, cl); out: - read_unlock(&qdisc_tree_lock); dev_put(dev); return skb->len; } @@ -563,30 +562,30 @@ #ifdef CONFIG_NET_CLS_ACT * to work with both old and new modes of entering * tc data even if iproute2 was newer - jhs */ - struct rtattr * p_rta = (struct rtattr*) skb->tail; + struct rtattr *p_rta = (struct rtattr *)skb_tail_pointer(skb); if (exts->action->type != TCA_OLD_COMPAT) { RTA_PUT(skb, map->action, 0, NULL); if (tcf_action_dump(skb, exts->action, 0, 0) < 0) goto rtattr_failure; - p_rta->rta_len = skb->tail - (u8*)p_rta; + p_rta->rta_len = skb_tail_pointer(skb) - (u8 *)p_rta; } else if (map->police) { RTA_PUT(skb, map->police, 0, NULL); if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0) goto rtattr_failure; - p_rta->rta_len = skb->tail - (u8*)p_rta; + p_rta->rta_len = skb_tail_pointer(skb) - (u8 *)p_rta; } } #elif defined CONFIG_NET_CLS_POLICE if (map->police && exts->police) { - struct rtattr * p_rta = (struct rtattr*) skb->tail; + struct rtattr *p_rta = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, map->police, 0, NULL); if (tcf_police_dump(skb, exts->police) < 0) goto rtattr_failure; - p_rta->rta_len = skb->tail - (u8*)p_rta; + p_rta->rta_len = skb_tail_pointer(skb) - (u8 *)p_rta; } #endif return 0; @@ -614,18 +613,11 @@ rtattr_failure: __attribute__ ((unused)) static int __init tc_filter_init(void) { - struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC]; + rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL); + rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL); + rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter, + tc_dump_tfilter); - /* Setup rtnetlink links. It is made here to avoid - exporting large number of public symbols. - */ - - if (link_p) { - link_p[RTM_NEWTFILTER-RTM_BASE].doit = tc_ctl_tfilter; - link_p[RTM_DELTFILTER-RTM_BASE].doit = tc_ctl_tfilter; - link_p[RTM_GETTFILTER-RTM_BASE].doit = tc_ctl_tfilter; - link_p[RTM_GETTFILTER-RTM_BASE].dumpit = tc_dump_tfilter; - } return 0; } diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 4a91f08..c885412 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -17,6 +17,7 @@ #include #include #include #include +#include #include #include @@ -245,7 +246,7 @@ static int basic_dump(struct tcf_proto * struct sk_buff *skb, struct tcmsg *t) { struct basic_filter *f = (struct basic_filter *) fh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; if (f == NULL) @@ -263,11 +264,11 @@ static int basic_dump(struct tcf_proto * tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0) goto rtattr_failure; - rta->rta_len = (skb->tail - b); + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 5dbb9d4..bbec4a0 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -38,6 +38,7 @@ #include #include #include #include +#include #include #include #include @@ -348,7 +349,7 @@ static int fw_dump(struct tcf_proto *tp, { struct fw_head *head = (struct fw_head *)tp->root; struct fw_filter *f = (struct fw_filter*)fh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; if (f == NULL) @@ -374,7 +375,7 @@ #endif /* CONFIG_NET_CLS_IND */ if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0) goto rtattr_failure; - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0) goto rtattr_failure; @@ -382,7 +383,7 @@ #endif /* CONFIG_NET_CLS_IND */ return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index abc47cc..cc941d0 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -28,6 +28,7 @@ #include #include #include #include +#include #include #include #include @@ -88,9 +89,9 @@ static __inline__ int route4_fastmap_has static inline void route4_reset_fastmap(struct net_device *dev, struct route4_head *head, u32 id) { - spin_lock_bh(&dev->queue_lock); + qdisc_lock_tree(dev); memset(head->fastmap, 0, sizeof(head->fastmap)); - spin_unlock_bh(&dev->queue_lock); + qdisc_unlock_tree(dev); } static inline void @@ -562,7 +563,7 @@ static int route4_dump(struct tcf_proto struct sk_buff *skb, struct tcmsg *t) { struct route4_filter *f = (struct route4_filter*)fh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; u32 id; @@ -591,7 +592,7 @@ static int route4_dump(struct tcf_proto if (tcf_exts_dump(skb, &f->exts, &route_ext_map) < 0) goto rtattr_failure; - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; if (tcf_exts_dump_stats(skb, &f->exts, &route_ext_map) < 0) goto rtattr_failure; @@ -599,7 +600,7 @@ static int route4_dump(struct tcf_proto return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/cls_rsvp.c b/net/sched/cls_rsvp.c index 1d4a1fb..0a683c0 100644 --- a/net/sched/cls_rsvp.c +++ b/net/sched/cls_rsvp.c @@ -31,6 +31,7 @@ #include #include #include #include +#include #include #include diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 7853621..22f9ede 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -143,9 +143,9 @@ static int rsvp_classify(struct sk_buff u8 tunnelid = 0; u8 *xprt; #if RSVP_DST_LEN == 4 - struct ipv6hdr *nhptr = skb->nh.ipv6h; + struct ipv6hdr *nhptr = ipv6_hdr(skb); #else - struct iphdr *nhptr = skb->nh.iph; + struct iphdr *nhptr = ip_hdr(skb); #endif restart: @@ -160,7 +160,7 @@ #else dst = &nhptr->daddr; protocol = nhptr->protocol; xprt = ((u8*)nhptr) + (nhptr->ihl<<2); - if (nhptr->frag_off&__constant_htons(IP_MF|IP_OFFSET)) + if (nhptr->frag_off & htons(IP_MF|IP_OFFSET)) return -1; #endif @@ -593,7 +593,7 @@ static int rsvp_dump(struct tcf_proto *t { struct rsvp_filter *f = (struct rsvp_filter*)fh; struct rsvp_session *s; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; struct tc_rsvp_pinfo pinfo; @@ -623,14 +623,14 @@ static int rsvp_dump(struct tcf_proto *t if (tcf_exts_dump(skb, &f->exts, &rsvp_ext_map) < 0) goto rtattr_failure; - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; if (tcf_exts_dump_stats(skb, &f->exts, &rsvp_ext_map) < 0) goto rtattr_failure; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/cls_rsvp6.c b/net/sched/cls_rsvp6.c index a2979d8..93b6abe 100644 --- a/net/sched/cls_rsvp6.c +++ b/net/sched/cls_rsvp6.c @@ -34,6 +34,7 @@ #include #include #include #include +#include #define RSVP_DST_LEN 4 #define RSVP_ID "rsvp6" diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 7563fdc..47ac0c5 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -12,6 +12,7 @@ #include #include #include #include +#include #include #include @@ -448,7 +449,7 @@ static int tcindex_dump(struct tcf_proto { struct tcindex_data *p = PRIV(tp); struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; DPRINTK("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p,b %p\n", @@ -463,7 +464,7 @@ static int tcindex_dump(struct tcf_proto RTA_PUT(skb,TCA_TCINDEX_SHIFT,sizeof(p->shift),&p->shift); RTA_PUT(skb,TCA_TCINDEX_FALL_THROUGH,sizeof(p->fall_through), &p->fall_through); - rta->rta_len = skb->tail-b; + rta->rta_len = skb_tail_pointer(skb) - b; } else { if (p->perfect) { t->tcm_handle = r-p->perfect; @@ -486,7 +487,7 @@ static int tcindex_dump(struct tcf_proto if (tcf_exts_dump(skb, &r->exts, &tcindex_ext_map) < 0) goto rtattr_failure; - rta->rta_len = skb->tail-b; + rta->rta_len = skb_tail_pointer(skb) - b; if (tcf_exts_dump_stats(skb, &r->exts, &tcindex_ext_map) < 0) goto rtattr_failure; @@ -495,7 +496,7 @@ static int tcindex_dump(struct tcf_proto return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 0bcb169..c7a347b 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -50,6 +50,7 @@ #include #include #include #include +#include #include #include #include @@ -119,7 +120,7 @@ static int u32_classify(struct sk_buff * } stack[TC_U32_MAXDEPTH]; struct tc_u_hnode *ht = (struct tc_u_hnode*)tp->root; - u8 *ptr = skb->nh.raw; + u8 *ptr = skb_network_header(skb); struct tc_u_knode *n; int sdepth = 0; int off2 = 0; @@ -213,7 +214,7 @@ #endif off2 = 0; } - if (ptr < skb->tail) + if (ptr < skb_tail_pointer(skb)) goto next_ht; } @@ -435,7 +436,7 @@ static void u32_destroy(struct tcf_proto BUG_TRAP(ht->refcnt == 0); kfree(ht); - }; + } kfree(tp_c); } @@ -718,7 +719,7 @@ static int u32_dump(struct tcf_proto *tp struct sk_buff *skb, struct tcmsg *t) { struct tc_u_knode *n = (struct tc_u_knode*)fh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; if (n == NULL) @@ -765,14 +766,14 @@ #ifdef CONFIG_CLS_U32_PERF #endif } - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; if (TC_U32_KEY(n->handle)) if (tcf_exts_dump_stats(skb, &n->exts, &u32_ext_map) < 0) goto rtattr_failure; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/em_u32.c b/net/sched/em_u32.c index cd0600c..0a2a7fe 100644 --- a/net/sched/em_u32.c +++ b/net/sched/em_u32.c @@ -22,7 +22,7 @@ static int em_u32_match(struct sk_buff * struct tcf_pkt_info *info) { struct tc_u32_key *key = (struct tc_u32_key *) em->data; - unsigned char *ptr = skb->nh.raw; + const unsigned char *ptr = skb_network_header(skb); if (info) { if (info->ptr) diff --git a/net/sched/ematch.c b/net/sched/ematch.c index 959c306..63146d3 100644 --- a/net/sched/ematch.c +++ b/net/sched/ematch.c @@ -418,17 +418,19 @@ void tcf_em_tree_destroy(struct tcf_prot int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *tree, int tlv) { int i; - struct rtattr * top_start = (struct rtattr*) skb->tail; - struct rtattr * list_start; + u8 *tail; + struct rtattr *top_start = (struct rtattr *)skb_tail_pointer(skb); + struct rtattr *list_start; RTA_PUT(skb, tlv, 0, NULL); RTA_PUT(skb, TCA_EMATCH_TREE_HDR, sizeof(tree->hdr), &tree->hdr); - list_start = (struct rtattr *) skb->tail; + list_start = (struct rtattr *)skb_tail_pointer(skb); RTA_PUT(skb, TCA_EMATCH_TREE_LIST, 0, NULL); + tail = skb_tail_pointer(skb); for (i = 0; i < tree->hdr.nmatches; i++) { - struct rtattr *match_start = (struct rtattr*) skb->tail; + struct rtattr *match_start = (struct rtattr *)tail; struct tcf_ematch *em = tcf_em_get_match(tree, i); struct tcf_ematch_hdr em_hdr = { .kind = em->ops ? em->ops->kind : TCF_EM_CONTAINER, @@ -447,11 +449,12 @@ int tcf_em_tree_dump(struct sk_buff *skb } else if (em->datalen > 0) RTA_PUT_NOHDR(skb, em->datalen, (void *) em->data); - match_start->rta_len = skb->tail - (u8*) match_start; + tail = skb_tail_pointer(skb); + match_start->rta_len = tail - (u8 *)match_start; } - list_start->rta_len = skb->tail - (u8 *) list_start; - top_start->rta_len = skb->tail - (u8 *) top_start; + list_start->rta_len = tail - (u8 *)list_start; + top_start->rta_len = tail - (u8 *)top_start; return 0; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index ecc988a..8699e70 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -27,14 +27,15 @@ #include #include #include #include -#include #include #include #include #include #include #include +#include +#include #include #include @@ -190,7 +191,7 @@ int unregister_qdisc(struct Qdisc_ops *q (root qdisc, all its children, children of children etc.) */ -static struct Qdisc *__qdisc_lookup(struct net_device *dev, u32 handle) +struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { struct Qdisc *q; @@ -201,16 +202,6 @@ static struct Qdisc *__qdisc_lookup(stru return NULL; } -struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) -{ - struct Qdisc *q; - - read_lock(&qdisc_tree_lock); - q = __qdisc_lookup(dev, handle); - read_unlock(&qdisc_tree_lock); - return q; -} - static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) { unsigned long cl; @@ -291,6 +282,48 @@ void qdisc_put_rtab(struct qdisc_rate_ta } } +static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) +{ + struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, + timer); + struct net_device *dev = wd->qdisc->dev; + + wd->qdisc->flags &= ~TCQ_F_THROTTLED; + smp_wmb(); + if (spin_trylock(&dev->queue_lock)) { + qdisc_run(dev); + spin_unlock(&dev->queue_lock); + } else + netif_schedule(dev); + + return HRTIMER_NORESTART; +} + +void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc) +{ + hrtimer_init(&wd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + wd->timer.function = qdisc_watchdog; + wd->qdisc = qdisc; +} +EXPORT_SYMBOL(qdisc_watchdog_init); + +void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires) +{ + ktime_t time; + + wd->qdisc->flags |= TCQ_F_THROTTLED; + time = ktime_set(0, 0); + time = ktime_add_ns(time, PSCHED_US2NS(expires)); + hrtimer_start(&wd->timer, time, HRTIMER_MODE_ABS); +} +EXPORT_SYMBOL(qdisc_watchdog_schedule); + +void qdisc_watchdog_cancel(struct qdisc_watchdog *wd) +{ + hrtimer_cancel(&wd->timer); + wd->qdisc->flags &= ~TCQ_F_THROTTLED; +} +EXPORT_SYMBOL(qdisc_watchdog_cancel); /* Allocate an unique handle from space managed by kernel */ @@ -362,7 +395,7 @@ void qdisc_tree_decrease_qlen(struct Qdi if (n == 0) return; while ((parentid = sch->parent)) { - sch = __qdisc_lookup(sch->dev, TC_H_MAJ(parentid)); + sch = qdisc_lookup(sch->dev, TC_H_MAJ(parentid)); cops = sch->ops->cl_ops; if (cops->qlen_notify) { cl = cops->get(sch, parentid); @@ -467,12 +500,16 @@ #endif if (handle == TC_H_INGRESS) { sch->flags |= TCQ_F_INGRESS; + sch->stats_lock = &dev->ingress_lock; handle = TC_H_MAKE(TC_H_INGRESS, 0); - } else if (handle == 0) { - handle = qdisc_alloc_handle(dev); - err = -ENOMEM; - if (handle == 0) - goto err_out3; + } else { + sch->stats_lock = &dev->queue_lock; + if (handle == 0) { + handle = qdisc_alloc_handle(dev); + err = -ENOMEM; + if (handle == 0) + goto err_out3; + } } sch->handle = handle; @@ -621,9 +658,9 @@ static int tc_get_qdisc(struct sk_buff * return err; if (q) { qdisc_notify(skb, n, clid, q, NULL); - spin_lock_bh(&dev->queue_lock); + qdisc_lock_tree(dev); qdisc_destroy(q); - spin_unlock_bh(&dev->queue_lock); + qdisc_unlock_tree(dev); } } else { qdisc_notify(skb, n, clid, NULL, q); @@ -756,17 +793,17 @@ graft: err = qdisc_graft(dev, p, clid, q, &old_q); if (err) { if (q) { - spin_lock_bh(&dev->queue_lock); + qdisc_lock_tree(dev); qdisc_destroy(q); - spin_unlock_bh(&dev->queue_lock); + qdisc_unlock_tree(dev); } return err; } qdisc_notify(skb, n, clid, old_q, q); if (old_q) { - spin_lock_bh(&dev->queue_lock); + qdisc_lock_tree(dev); qdisc_destroy(old_q); - spin_unlock_bh(&dev->queue_lock); + qdisc_unlock_tree(dev); } } return 0; @@ -777,7 +814,7 @@ static int tc_fill_qdisc(struct sk_buff { struct tcmsg *tcm; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct gnet_dump d; nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags); @@ -811,12 +848,12 @@ #endif if (gnet_stats_finish_copy(&d) < 0) goto rtattr_failure; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -862,7 +899,6 @@ static int tc_dump_qdisc(struct sk_buff continue; if (idx > s_idx) s_q_idx = 0; - read_lock(&qdisc_tree_lock); q_idx = 0; list_for_each_entry(q, &dev->qdisc_list, list) { if (q_idx < s_q_idx) { @@ -870,13 +906,10 @@ static int tc_dump_qdisc(struct sk_buff continue; } if (tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) { - read_unlock(&qdisc_tree_lock); + cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) goto done; - } q_idx++; } - read_unlock(&qdisc_tree_lock); } done: @@ -1015,7 +1048,7 @@ static int tc_fill_tclass(struct sk_buff { struct tcmsg *tcm; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct gnet_dump d; struct Qdisc_class_ops *cl_ops = q->ops->cl_ops; @@ -1040,12 +1073,12 @@ static int tc_fill_tclass(struct sk_buff if (gnet_stats_finish_copy(&d) < 0) goto rtattr_failure; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1099,7 +1132,6 @@ static int tc_dump_tclass(struct sk_buff s_t = cb->args[0]; t = 0; - read_lock(&qdisc_tree_lock); list_for_each_entry(q, &dev->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && @@ -1121,7 +1153,6 @@ static int tc_dump_tclass(struct sk_buff break; t++; } - read_unlock(&qdisc_tree_lock); cb->args[0] = t; @@ -1146,7 +1177,7 @@ #endif for ( ; tp; tp = tp->next) { if ((tp->protocol == protocol || - tp->protocol == __constant_htons(ETH_P_ALL)) && + tp->protocol == htons(ETH_P_ALL)) && (err = tp->classify(skb, tp, res)) >= 0) { #ifdef CONFIG_NET_CLS_ACT if ( TC_ACT_RECLASSIFY == err) { @@ -1175,15 +1206,31 @@ #endif return -1; } -static int psched_us_per_tick = 1; -static int psched_tick_per_us = 1; +void tcf_destroy(struct tcf_proto *tp) +{ + tp->ops->destroy(tp); + module_put(tp->ops->owner); + kfree(tp); +} + +void tcf_destroy_chain(struct tcf_proto *fl) +{ + struct tcf_proto *tp; + + while ((tp = fl) != NULL) { + fl = tp->next; + tcf_destroy(tp); + } +} +EXPORT_SYMBOL(tcf_destroy_chain); #ifdef CONFIG_PROC_FS static int psched_show(struct seq_file *seq, void *v) { seq_printf(seq, "%08x %08x %08x %08x\n", - psched_tick_per_us, psched_us_per_tick, - 1000000, HZ); + (u32)NSEC_PER_USEC, (u32)PSCHED_US2NS(1), + 1000000, + (u32)NSEC_PER_SEC/(u32)ktime_to_ns(KTIME_MONOTONIC_RES)); return 0; } @@ -1202,101 +1249,19 @@ static const struct file_operations psch }; #endif -#ifdef CONFIG_NET_SCH_CLK_CPU -psched_tdiff_t psched_clock_per_hz; -int psched_clock_scale; -EXPORT_SYMBOL(psched_clock_per_hz); -EXPORT_SYMBOL(psched_clock_scale); - -psched_time_t psched_time_base; -cycles_t psched_time_mark; -EXPORT_SYMBOL(psched_time_mark); -EXPORT_SYMBOL(psched_time_base); - -/* - * Periodically adjust psched_time_base to avoid overflow - * with 32-bit get_cycles(). Safe up to 4GHz CPU. - */ -static void psched_tick(unsigned long); -static DEFINE_TIMER(psched_timer, psched_tick, 0, 0); - -static void psched_tick(unsigned long dummy) -{ - if (sizeof(cycles_t) == sizeof(u32)) { - psched_time_t dummy_stamp; - PSCHED_GET_TIME(dummy_stamp); - psched_timer.expires = jiffies + 1*HZ; - add_timer(&psched_timer); - } -} - -int __init psched_calibrate_clock(void) -{ - psched_time_t stamp, stamp1; - struct timeval tv, tv1; - psched_tdiff_t delay; - long rdelay; - unsigned long stop; - - psched_tick(0); - stop = jiffies + HZ/10; - PSCHED_GET_TIME(stamp); - do_gettimeofday(&tv); - while (time_before(jiffies, stop)) { - barrier(); - cpu_relax(); - } - PSCHED_GET_TIME(stamp1); - do_gettimeofday(&tv1); - - delay = PSCHED_TDIFF(stamp1, stamp); - rdelay = tv1.tv_usec - tv.tv_usec; - rdelay += (tv1.tv_sec - tv.tv_sec)*1000000; - if (rdelay > delay) - return -1; - delay /= rdelay; - psched_tick_per_us = delay; - while ((delay>>=1) != 0) - psched_clock_scale++; - psched_us_per_tick = 1<>psched_clock_scale; - return 0; -} -#endif - static int __init pktsched_init(void) { - struct rtnetlink_link *link_p; - -#ifdef CONFIG_NET_SCH_CLK_CPU - if (psched_calibrate_clock() < 0) - return -1; -#elif defined(CONFIG_NET_SCH_CLK_JIFFIES) - psched_tick_per_us = HZ< #include #include #include /* for fput */ +#include #include #include @@ -157,19 +158,6 @@ static unsigned long atm_tc_bind_filter( return atm_tc_get(sch,classid); } - -static void destroy_filters(struct atm_flow_data *flow) -{ - struct tcf_proto *filter; - - while ((filter = flow->filter_list)) { - DPRINTK("destroy_filters: destroying filter %p\n",filter); - flow->filter_list = filter->next; - tcf_destroy(filter); - } -} - - /* * atm_tc_put handles all destructions, including the ones that are explicitly * requested (atm_tc_destroy, etc.). The assumption here is that we never drop @@ -194,7 +182,7 @@ static void atm_tc_put(struct Qdisc *sch *prev = flow->next; DPRINTK("atm_tc_put: qdisc %p\n",flow->q); qdisc_destroy(flow->q); - destroy_filters(flow); + tcf_destroy_chain(flow->filter_list); if (flow->sock) { DPRINTK("atm_tc_put: f_count %d\n", file_count(flow->sock->file)); @@ -503,7 +491,7 @@ static void sch_atm_dequeue(unsigned lon } D2PRINTK("atm_tc_dequeue: sending on class %p\n",flow); /* remove any LL header somebody else has attached */ - skb_pull(skb,(char *) skb->nh.iph-(char *) skb->data); + skb_pull(skb, skb_network_offset(skb)); if (skb_headroom(skb) < flow->hdr_len) { struct sk_buff *new; @@ -513,7 +501,7 @@ static void sch_atm_dequeue(unsigned lon skb = new; } D2PRINTK("sch_atm_dequeue: ip %p, data %p\n", - skb->nh.iph,skb->data); + skb_network_header(skb), skb->data); ATM_SKB(skb)->vcc = flow->vcc; memcpy(skb_push(skb,flow->hdr_len),flow->hdr, flow->hdr_len); @@ -610,7 +598,7 @@ static void atm_tc_destroy(struct Qdisc DPRINTK("atm_tc_destroy(sch %p,[qdisc %p])\n",sch,p); /* races ? */ while ((flow = p->flows)) { - destroy_filters(flow); + tcf_destroy_chain(flow->filter_list); if (flow->ref > 1) printk(KERN_ERR "atm_destroy: %p->ref = %d\n",flow, flow->ref); @@ -631,7 +619,7 @@ static int atm_tc_dump_class(struct Qdis { struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow = (struct atm_flow_data *) cl; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; DPRINTK("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n", @@ -661,11 +649,11 @@ static int atm_tc_dump_class(struct Qdis RTA_PUT(skb,TCA_ATM_EXCESS,sizeof(zero),&zero); } - rta->rta_len = skb->tail-b; + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb,b-skb->data); + nlmsg_trim(skb, b); return -1; } static int diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 76c92e7..a294542 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -29,6 +29,7 @@ #include #include #include #include +#include #include #include #include @@ -112,7 +113,7 @@ #endif /* Overlimit strategy parameters */ void (*overlimit)(struct cbq_class *cl); - long penalty; + psched_tdiff_t penalty; /* General scheduler (WRR) parameters */ long allot; @@ -143,7 +144,7 @@ #endif psched_time_t undertime; long avgidle; long deficit; /* Saved deficit for WRR */ - unsigned long penalized; + psched_time_t penalized; struct gnet_stats_basic bstats; struct gnet_stats_queue qstats; struct gnet_stats_rate_est rate_est; @@ -180,12 +181,12 @@ #endif psched_time_t now_rt; /* Cached real time */ unsigned pmask; - struct timer_list delay_timer; - struct timer_list wd_timer; /* Watchdog timer, + struct hrtimer delay_timer; + struct qdisc_watchdog watchdog; /* Watchdog timer, started when CBQ has backlog, but cannot transmit just now */ - long wd_expires; + psched_tdiff_t wd_expires; int toplevel; u32 hgenerator; }; @@ -384,12 +385,12 @@ cbq_mark_toplevel(struct cbq_sched_data psched_time_t now; psched_tdiff_t incr; - PSCHED_GET_TIME(now); - incr = PSCHED_TDIFF(now, q->now_rt); - PSCHED_TADD2(q->now, incr, now); + now = psched_get_time(); + incr = now - q->now_rt; + now = q->now + incr; do { - if (PSCHED_TLESS(cl->undertime, now)) { + if (cl->undertime < now) { q->toplevel = cl->level; return; } @@ -473,7 +474,7 @@ #endif static void cbq_ovl_classic(struct cbq_class *cl) { struct cbq_sched_data *q = qdisc_priv(cl->qdisc); - psched_tdiff_t delay = PSCHED_TDIFF(cl->undertime, q->now); + psched_tdiff_t delay = cl->undertime - q->now; if (!cl->delayed) { delay += cl->offtime; @@ -491,7 +492,7 @@ static void cbq_ovl_classic(struct cbq_c cl->avgidle = cl->minidle; if (delay <= 0) delay = 1; - PSCHED_TADD2(q->now, delay, cl->undertime); + cl->undertime = q->now + delay; cl->xstats.overactions++; cl->delayed = 1; @@ -508,7 +509,7 @@ static void cbq_ovl_classic(struct cbq_c psched_tdiff_t base_delay = q->wd_expires; for (b = cl->borrow; b; b = b->borrow) { - delay = PSCHED_TDIFF(b->undertime, q->now); + delay = b->undertime - q->now; if (delay < base_delay) { if (delay <= 0) delay = 1; @@ -546,27 +547,32 @@ static void cbq_ovl_rclassic(struct cbq_ static void cbq_ovl_delay(struct cbq_class *cl) { struct cbq_sched_data *q = qdisc_priv(cl->qdisc); - psched_tdiff_t delay = PSCHED_TDIFF(cl->undertime, q->now); + psched_tdiff_t delay = cl->undertime - q->now; if (!cl->delayed) { - unsigned long sched = jiffies; + psched_time_t sched = q->now; + ktime_t expires; delay += cl->offtime; if (cl->avgidle < 0) delay -= (-cl->avgidle) - ((-cl->avgidle) >> cl->ewma_log); if (cl->avgidle < cl->minidle) cl->avgidle = cl->minidle; - PSCHED_TADD2(q->now, delay, cl->undertime); + cl->undertime = q->now + delay; if (delay > 0) { - sched += PSCHED_US2JIFFIE(delay) + cl->penalty; + sched += delay + cl->penalty; cl->penalized = sched; cl->cpriority = TC_CBQ_MAXPRIO; q->pmask |= (1<delay_timer) && - (long)(q->delay_timer.expires - sched) > 0) - q->delay_timer.expires = sched; - add_timer(&q->delay_timer); + + expires = ktime_set(0, 0); + expires = ktime_add_ns(expires, PSCHED_US2NS(sched)); + if (hrtimer_try_to_cancel(&q->delay_timer) && + ktime_to_ns(ktime_sub(q->delay_timer.expires, + expires)) > 0) + q->delay_timer.expires = expires; + hrtimer_restart(&q->delay_timer); cl->delayed = 1; cl->xstats.overactions++; return; @@ -583,7 +589,7 @@ static void cbq_ovl_lowprio(struct cbq_c { struct cbq_sched_data *q = qdisc_priv(cl->qdisc); - cl->penalized = jiffies + cl->penalty; + cl->penalized = q->now + cl->penalty; if (cl->cpriority != cl->priority2) { cl->cpriority = cl->priority2; @@ -604,27 +610,19 @@ static void cbq_ovl_drop(struct cbq_clas cbq_ovl_classic(cl); } -static void cbq_watchdog(unsigned long arg) -{ - struct Qdisc *sch = (struct Qdisc*)arg; - - sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule(sch->dev); -} - -static unsigned long cbq_undelay_prio(struct cbq_sched_data *q, int prio) +static psched_tdiff_t cbq_undelay_prio(struct cbq_sched_data *q, int prio, + psched_time_t now) { struct cbq_class *cl; struct cbq_class *cl_prev = q->active[prio]; - unsigned long now = jiffies; - unsigned long sched = now; + psched_time_t sched = now; if (cl_prev == NULL) - return now; + return 0; do { cl = cl_prev->next_alive; - if ((long)(now - cl->penalized) > 0) { + if (now - cl->penalized > 0) { cl_prev->next_alive = cl->next_alive; cl->next_alive = NULL; cl->cpriority = cl->priority; @@ -640,30 +638,34 @@ static unsigned long cbq_undelay_prio(st } cl = cl_prev->next_alive; - } else if ((long)(sched - cl->penalized) > 0) + } else if (sched - cl->penalized > 0) sched = cl->penalized; } while ((cl_prev = cl) != q->active[prio]); - return (long)(sched - now); + return sched - now; } -static void cbq_undelay(unsigned long arg) +static enum hrtimer_restart cbq_undelay(struct hrtimer *timer) { - struct Qdisc *sch = (struct Qdisc*)arg; - struct cbq_sched_data *q = qdisc_priv(sch); - long delay = 0; + struct cbq_sched_data *q = container_of(timer, struct cbq_sched_data, + delay_timer); + struct Qdisc *sch = q->watchdog.qdisc; + psched_time_t now; + psched_tdiff_t delay = 0; unsigned pmask; + now = psched_get_time(); + pmask = q->pmask; q->pmask = 0; while (pmask) { int prio = ffz(~pmask); - long tmp; + psched_tdiff_t tmp; pmask &= ~(1< 0) { q->pmask |= 1<delay_timer.expires = jiffies + delay; - add_timer(&q->delay_timer); + ktime_t time; + + time = ktime_set(0, 0); + time = ktime_add_ns(time, PSCHED_US2NS(now + delay)); + hrtimer_start(&q->delay_timer, time, HRTIMER_MODE_ABS); } sch->flags &= ~TCQ_F_THROTTLED; netif_schedule(sch->dev); + return HRTIMER_NORESTART; } @@ -732,7 +738,7 @@ cbq_update_toplevel(struct cbq_sched_dat if (cl && q->toplevel >= borrowed->level) { if (cl->q->q.qlen > 1) { do { - if (PSCHED_IS_PASTPERFECT(borrowed->undertime)) { + if (borrowed->undertime == PSCHED_PASTPERFECT) { q->toplevel = borrowed->level; return; } @@ -770,7 +776,7 @@ cbq_update(struct cbq_sched_data *q) idle = (now - last) - last_pktlen/rate */ - idle = PSCHED_TDIFF(q->now, cl->last); + idle = q->now - cl->last; if ((unsigned long)idle > 128*1024*1024) { avgidle = cl->maxidle; } else { @@ -814,13 +820,11 @@ cbq_update(struct cbq_sched_data *q) idle -= L2T(&q->link, len); idle += L2T(cl, len); - PSCHED_AUDIT_TDIFF(idle); - - PSCHED_TADD2(q->now, idle, cl->undertime); + cl->undertime = q->now + idle; } else { /* Underlimit */ - PSCHED_SET_PASTPERFECT(cl->undertime); + cl->undertime = PSCHED_PASTPERFECT; if (avgidle > cl->maxidle) cl->avgidle = cl->maxidle; else @@ -841,8 +845,7 @@ cbq_under_limit(struct cbq_class *cl) if (cl->tparent == NULL) return cl; - if (PSCHED_IS_PASTPERFECT(cl->undertime) || - !PSCHED_TLESS(q->now, cl->undertime)) { + if (cl->undertime == PSCHED_PASTPERFECT || q->now >= cl->undertime) { cl->delayed = 0; return cl; } @@ -865,8 +868,7 @@ cbq_under_limit(struct cbq_class *cl) } if (cl->level > q->toplevel) return NULL; - } while (!PSCHED_IS_PASTPERFECT(cl->undertime) && - PSCHED_TLESS(q->now, cl->undertime)); + } while (cl->undertime != PSCHED_PASTPERFECT && q->now < cl->undertime); cl->delayed = 0; return cl; @@ -1001,8 +1003,8 @@ cbq_dequeue(struct Qdisc *sch) psched_time_t now; psched_tdiff_t incr; - PSCHED_GET_TIME(now); - incr = PSCHED_TDIFF(now, q->now_rt); + now = psched_get_time(); + incr = now - q->now_rt; if (q->tx_class) { psched_tdiff_t incr2; @@ -1014,12 +1016,12 @@ cbq_dequeue(struct Qdisc *sch) cbq_time = max(real_time, work); */ incr2 = L2T(&q->link, q->tx_len); - PSCHED_TADD(q->now, incr2); + q->now += incr2; cbq_update(q); if ((incr -= incr2) < 0) incr = 0; } - PSCHED_TADD(q->now, incr); + q->now += incr; q->now_rt = now; for (;;) { @@ -1051,11 +1053,11 @@ cbq_dequeue(struct Qdisc *sch) */ if (q->toplevel == TC_CBQ_MAXLEVEL && - PSCHED_IS_PASTPERFECT(q->link.undertime)) + q->link.undertime == PSCHED_PASTPERFECT) break; q->toplevel = TC_CBQ_MAXLEVEL; - PSCHED_SET_PASTPERFECT(q->link.undertime); + q->link.undertime = PSCHED_PASTPERFECT; } /* No packets in scheduler or nobody wants to give them to us :-( @@ -1063,13 +1065,9 @@ cbq_dequeue(struct Qdisc *sch) if (sch->q.qlen) { sch->qstats.overlimits++; - if (q->wd_expires) { - long delay = PSCHED_US2JIFFIE(q->wd_expires); - if (delay <= 0) - delay = 1; - mod_timer(&q->wd_timer, jiffies + delay); - sch->flags |= TCQ_F_THROTTLED; - } + if (q->wd_expires) + qdisc_watchdog_schedule(&q->watchdog, + now + q->wd_expires); } return NULL; } @@ -1276,10 +1274,10 @@ cbq_reset(struct Qdisc* sch) q->pmask = 0; q->tx_class = NULL; q->tx_borrowed = NULL; - del_timer(&q->wd_timer); - del_timer(&q->delay_timer); + qdisc_watchdog_cancel(&q->watchdog); + hrtimer_cancel(&q->delay_timer); q->toplevel = TC_CBQ_MAXLEVEL; - PSCHED_GET_TIME(q->now); + q->now = psched_get_time(); q->now_rt = q->now; for (prio = 0; prio <= TC_CBQ_MAXPRIO; prio++) @@ -1290,7 +1288,7 @@ cbq_reset(struct Qdisc* sch) qdisc_reset(cl->q); cl->next_alive = NULL; - PSCHED_SET_PASTPERFECT(cl->undertime); + cl->undertime = PSCHED_PASTPERFECT; cl->avgidle = cl->maxidle; cl->deficit = cl->quantum; cl->cpriority = cl->priority; @@ -1379,7 +1377,7 @@ static int cbq_set_overlimit(struct cbq_ default: return -EINVAL; } - cl->penalty = (ovl->penalty*HZ)/1000; + cl->penalty = ovl->penalty; return 0; } @@ -1446,14 +1444,11 @@ static int cbq_init(struct Qdisc *sch, s q->link.minidle = -0x7FFFFFFF; q->link.stats_lock = &sch->dev->queue_lock; - init_timer(&q->wd_timer); - q->wd_timer.data = (unsigned long)sch; - q->wd_timer.function = cbq_watchdog; - init_timer(&q->delay_timer); - q->delay_timer.data = (unsigned long)sch; + qdisc_watchdog_init(&q->watchdog, sch); + hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); q->delay_timer.function = cbq_undelay; q->toplevel = TC_CBQ_MAXLEVEL; - PSCHED_GET_TIME(q->now); + q->now = psched_get_time(); q->now_rt = q->now; cbq_link_class(&q->link); @@ -1467,19 +1462,19 @@ static int cbq_init(struct Qdisc *sch, s static __inline__ int cbq_dump_rate(struct sk_buff *skb, struct cbq_class *cl) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); RTA_PUT(skb, TCA_CBQ_RATE, sizeof(cl->R_tab->rate), &cl->R_tab->rate); return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } static __inline__ int cbq_dump_lss(struct sk_buff *skb, struct cbq_class *cl) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_cbq_lssopt opt; opt.flags = 0; @@ -1498,13 +1493,13 @@ static __inline__ int cbq_dump_lss(struc return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } static __inline__ int cbq_dump_wrr(struct sk_buff *skb, struct cbq_class *cl) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_cbq_wrropt opt; opt.flags = 0; @@ -1516,30 +1511,30 @@ static __inline__ int cbq_dump_wrr(struc return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } static __inline__ int cbq_dump_ovl(struct sk_buff *skb, struct cbq_class *cl) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_cbq_ovl opt; opt.strategy = cl->ovl_strategy; opt.priority2 = cl->priority2+1; opt.pad = 0; - opt.penalty = (cl->penalty*1000)/HZ; + opt.penalty = cl->penalty; RTA_PUT(skb, TCA_CBQ_OVL_STRATEGY, sizeof(opt), &opt); return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } static __inline__ int cbq_dump_fopt(struct sk_buff *skb, struct cbq_class *cl) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_cbq_fopt opt; if (cl->split || cl->defmap) { @@ -1551,14 +1546,14 @@ static __inline__ int cbq_dump_fopt(stru return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } #ifdef CONFIG_NET_CLS_POLICE static __inline__ int cbq_dump_police(struct sk_buff *skb, struct cbq_class *cl) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_cbq_police opt; if (cl->police) { @@ -1570,7 +1565,7 @@ static __inline__ int cbq_dump_police(st return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } #endif @@ -1592,18 +1587,18 @@ #endif static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb) { struct cbq_sched_data *q = qdisc_priv(sch); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; rta = (struct rtattr*)b; RTA_PUT(skb, TCA_OPTIONS, 0, NULL); if (cbq_dump_attr(skb, &q->link) < 0) goto rtattr_failure; - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1621,7 +1616,7 @@ cbq_dump_class(struct Qdisc *sch, unsign struct sk_buff *skb, struct tcmsg *tcm) { struct cbq_class *cl = (struct cbq_class*)arg; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; if (cl->tparent) @@ -1635,11 +1630,11 @@ cbq_dump_class(struct Qdisc *sch, unsign RTA_PUT(skb, TCA_OPTIONS, 0, NULL); if (cbq_dump_attr(skb, cl) < 0) goto rtattr_failure; - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1654,8 +1649,8 @@ cbq_dump_class_stats(struct Qdisc *sch, cl->xstats.avgidle = cl->avgidle; cl->xstats.undertime = 0; - if (!PSCHED_IS_PASTPERFECT(cl->undertime)) - cl->xstats.undertime = PSCHED_TDIFF(cl->undertime, q->now); + if (cl->undertime != PSCHED_PASTPERFECT) + cl->xstats.undertime = cl->undertime - q->now; if (gnet_stats_copy_basic(d, &cl->bstats) < 0 || #ifdef CONFIG_NET_ESTIMATOR @@ -1722,23 +1717,13 @@ static unsigned long cbq_get(struct Qdis return 0; } -static void cbq_destroy_filters(struct cbq_class *cl) -{ - struct tcf_proto *tp; - - while ((tp = cl->filter_list) != NULL) { - cl->filter_list = tp->next; - tcf_destroy(tp); - } -} - static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl) { struct cbq_sched_data *q = qdisc_priv(sch); BUG_TRAP(!cl->filters); - cbq_destroy_filters(cl); + tcf_destroy_chain(cl->filter_list); qdisc_destroy(cl->q); qdisc_put_rtab(cl->R_tab); #ifdef CONFIG_NET_ESTIMATOR @@ -1765,7 +1750,7 @@ #endif */ for (h = 0; h < 16; h++) for (cl = q->classes[h]; cl; cl = cl->next) - cbq_destroy_filters(cl); + tcf_destroy_chain(cl->filter_list); for (h = 0; h < 16; h++) { struct cbq_class *next; diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 96324cf..3c6fd18 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -216,17 +216,17 @@ static int dsmark_enqueue(struct sk_buff /* FIXME: Safe with non-linear skbs? --RR */ switch (skb->protocol) { case __constant_htons(ETH_P_IP): - skb->tc_index = ipv4_get_dsfield(skb->nh.iph) + skb->tc_index = ipv4_get_dsfield(ip_hdr(skb)) & ~INET_ECN_MASK; break; case __constant_htons(ETH_P_IPV6): - skb->tc_index = ipv6_get_dsfield(skb->nh.ipv6h) + skb->tc_index = ipv6_get_dsfield(ipv6_hdr(skb)) & ~INET_ECN_MASK; break; default: skb->tc_index = 0; break; - }; + } } if (TC_H_MAJ(skb->priority) == sch->handle) @@ -257,7 +257,7 @@ #endif if (p->default_index != NO_DEFAULT_INDEX) skb->tc_index = p->default_index; break; - }; + } } err = p->q->enqueue(skb,p->q); @@ -292,11 +292,11 @@ static struct sk_buff *dsmark_dequeue(st switch (skb->protocol) { case __constant_htons(ETH_P_IP): - ipv4_change_dsfield(skb->nh.iph, p->mask[index], + ipv4_change_dsfield(ip_hdr(skb), p->mask[index], p->value[index]); break; case __constant_htons(ETH_P_IPV6): - ipv6_change_dsfield(skb->nh.ipv6h, p->mask[index], + ipv6_change_dsfield(ipv6_hdr(skb), p->mask[index], p->value[index]); break; default: @@ -310,7 +310,7 @@ static struct sk_buff *dsmark_dequeue(st "unsupported protocol %d\n", ntohs(skb->protocol)); break; - }; + } return skb; } @@ -412,16 +412,10 @@ static void dsmark_reset(struct Qdisc *s static void dsmark_destroy(struct Qdisc *sch) { struct dsmark_qdisc_data *p = PRIV(sch); - struct tcf_proto *tp; DPRINTK("dsmark_destroy(sch %p,[qdisc %p])\n", sch, p); - while (p->filter_list) { - tp = p->filter_list; - p->filter_list = tp->next; - tcf_destroy(tp); - } - + tcf_destroy_chain(p->filter_list); qdisc_destroy(p->q); kfree(p->mask); } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 52eb343..3385ee5 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -36,34 +36,27 @@ #include /* Main transmission queue. */ -/* Main qdisc structure lock. - - However, modifications - to data, participating in scheduling must be additionally - protected with dev->queue_lock spinlock. - - The idea is the following: - - enqueue, dequeue are serialized via top level device - spinlock dev->queue_lock. - - tree walking is protected by read_lock(qdisc_tree_lock) - and this lock is used only in process context. - - updates to tree are made only under rtnl semaphore, - hence this lock may be made without local bh disabling. - - qdisc_tree_lock must be grabbed BEFORE dev->queue_lock! +/* Modifications to data participating in scheduling must be protected with + * dev->queue_lock spinlock. + * + * The idea is the following: + * - enqueue, dequeue are serialized via top level device + * spinlock dev->queue_lock. + * - ingress filtering is serialized via top level device + * spinlock dev->ingress_lock. + * - updates to tree and tree walking are only done under the rtnl mutex. */ -DEFINE_RWLOCK(qdisc_tree_lock); void qdisc_lock_tree(struct net_device *dev) { - write_lock(&qdisc_tree_lock); spin_lock_bh(&dev->queue_lock); + spin_lock(&dev->ingress_lock); } void qdisc_unlock_tree(struct net_device *dev) { + spin_unlock(&dev->ingress_lock); spin_unlock_bh(&dev->queue_lock); - write_unlock(&qdisc_tree_lock); } /* @@ -442,7 +435,6 @@ struct Qdisc *qdisc_alloc(struct net_dev sch->dequeue = ops->dequeue; sch->dev = dev; dev_hold(dev); - sch->stats_lock = &dev->queue_lock; atomic_set(&sch->refcnt, 1); return sch; @@ -458,6 +450,7 @@ struct Qdisc * qdisc_create_dflt(struct sch = qdisc_alloc(dev, ops); if (IS_ERR(sch)) goto errout; + sch->stats_lock = &dev->queue_lock; sch->parent = parentid; if (!ops->init || ops->init(sch, NULL) == 0) @@ -528,15 +521,11 @@ void dev_activate(struct net_device *dev printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - write_lock(&qdisc_tree_lock); list_add_tail(&qdisc->list, &dev->qdisc_list); - write_unlock(&qdisc_tree_lock); } else { qdisc = &noqueue_qdisc; } - write_lock(&qdisc_tree_lock); dev->qdisc_sleeping = qdisc; - write_unlock(&qdisc_tree_lock); } if (!netif_carrier_ok(dev)) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 407c6fb..9d124c4 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -59,13 +59,13 @@ #include #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -192,23 +192,9 @@ struct hfsc_sched struct list_head droplist; /* active leaf class list (for dropping) */ struct sk_buff_head requeue; /* requeued packet */ - struct timer_list wd_timer; /* watchdog timer */ + struct qdisc_watchdog watchdog; /* watchdog timer */ }; -/* - * macros - */ -#ifdef CONFIG_NET_SCH_CLK_GETTIMEOFDAY -#include -#undef PSCHED_GET_TIME -#define PSCHED_GET_TIME(stamp) \ -do { \ - struct timeval tv; \ - do_gettimeofday(&tv); \ - (stamp) = 1ULL * USEC_PER_SEC * tv.tv_sec + tv.tv_usec; \ -} while (0) -#endif - #define HT_INFINITY 0xffffffffffffffffULL /* infinite time value */ @@ -394,28 +380,17 @@ cftree_update(struct hfsc_class *cl) * ism: (psched_us/byte) << ISM_SHIFT * dx: psched_us * - * Clock source resolution (CONFIG_NET_SCH_CLK_*) - * JIFFIES: for 48<=HZ<=1534 resolution is between 0.63us and 1.27us. - * CPU: resolution is between 0.5us and 1us. - * GETTIMEOFDAY: resolution is exactly 1us. + * The clock source resolution with ktime is 1.024us. * * sm and ism are scaled in order to keep effective digits. * SM_SHIFT and ISM_SHIFT are selected to keep at least 4 effective * digits in decimal using the following table. * - * Note: We can afford the additional accuracy (altq hfsc keeps at most - * 3 effective digits) thanks to the fact that linux clock is bounded - * much more tightly. - * * bits/sec 100Kbps 1Mbps 10Mbps 100Mbps 1Gbps * ------------+------------------------------------------------------- - * bytes/0.5us 6.25e-3 62.5e-3 625e-3 6250e-e 62500e-3 - * bytes/us 12.5e-3 125e-3 1250e-3 12500e-3 125000e-3 - * bytes/1.27us 15.875e-3 158.75e-3 1587.5e-3 15875e-3 158750e-3 + * bytes/1.024us 12.8e-3 128e-3 1280e-3 12800e-3 128000e-3 * - * 0.5us/byte 160 16 1.6 0.16 0.016 - * us/byte 80 8 0.8 0.08 0.008 - * 1.27us/byte 63 6.3 0.63 0.063 0.0063 + * 1.024us/byte 78.125 7.8125 0.78125 0.078125 0.0078125 */ #define SM_SHIFT 20 #define ISM_SHIFT 18 @@ -460,8 +435,8 @@ m2sm(u32 m) u64 sm; sm = ((u64)m << SM_SHIFT); - sm += PSCHED_JIFFIE2US(HZ) - 1; - do_div(sm, PSCHED_JIFFIE2US(HZ)); + sm += PSCHED_TICKS_PER_SEC - 1; + do_div(sm, PSCHED_TICKS_PER_SEC); return sm; } @@ -474,7 +449,7 @@ m2ism(u32 m) if (m == 0) ism = HT_INFINITY; else { - ism = ((u64)PSCHED_JIFFIE2US(HZ) << ISM_SHIFT); + ism = ((u64)PSCHED_TICKS_PER_SEC << ISM_SHIFT); ism += m - 1; do_div(ism, m); } @@ -487,7 +462,7 @@ d2dx(u32 d) { u64 dx; - dx = ((u64)d * PSCHED_JIFFIE2US(HZ)); + dx = ((u64)d * PSCHED_TICKS_PER_SEC); dx += USEC_PER_SEC - 1; do_div(dx, USEC_PER_SEC); return dx; @@ -499,7 +474,7 @@ sm2m(u64 sm) { u64 m; - m = (sm * PSCHED_JIFFIE2US(HZ)) >> SM_SHIFT; + m = (sm * PSCHED_TICKS_PER_SEC) >> SM_SHIFT; return (u32)m; } @@ -510,7 +485,7 @@ dx2d(u64 dx) u64 d; d = dx * USEC_PER_SEC; - do_div(d, PSCHED_JIFFIE2US(HZ)); + do_div(d, PSCHED_TICKS_PER_SEC); return (u32)d; } @@ -654,9 +629,7 @@ rtsc_min(struct runtime_sc *rtsc, struct static void init_ed(struct hfsc_class *cl, unsigned int next_len) { - u64 cur_time; - - PSCHED_GET_TIME(cur_time); + u64 cur_time = psched_get_time(); /* update the deadline curve */ rtsc_min(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul); @@ -779,7 +752,7 @@ init_vf(struct hfsc_class *cl, unsigned if (cl->cl_flags & HFSC_USC) { /* class has upper limit curve */ if (cur_time == 0) - PSCHED_GET_TIME(cur_time); + cur_time = psched_get_time(); /* update the ulimit curve */ rtsc_min(&cl->cl_ulimit, &cl->cl_usc, cur_time, @@ -1063,7 +1036,7 @@ hfsc_change_class(struct Qdisc *sch, u32 if (cl->cl_parent == NULL && parentid != TC_H_ROOT) return -EINVAL; } - PSCHED_GET_TIME(cur_time); + cur_time = psched_get_time(); sch_tree_lock(sch); if (rsc != NULL) @@ -1149,22 +1122,11 @@ #endif } static void -hfsc_destroy_filters(struct tcf_proto **fl) -{ - struct tcf_proto *tp; - - while ((tp = *fl) != NULL) { - *fl = tp->next; - tcf_destroy(tp); - } -} - -static void hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl) { struct hfsc_sched *q = qdisc_priv(sch); - hfsc_destroy_filters(&cl->filter_list); + tcf_destroy_chain(cl->filter_list); qdisc_destroy(cl->qdisc); #ifdef CONFIG_NET_ESTIMATOR gen_kill_estimator(&cl->bstats, &cl->rate_est); @@ -1389,7 +1351,7 @@ hfsc_dump_class(struct Qdisc *sch, unsig struct tcmsg *tcm) { struct hfsc_class *cl = (struct hfsc_class *)arg; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta = (struct rtattr *)b; tcm->tcm_parent = cl->cl_parent ? cl->cl_parent->classid : TC_H_ROOT; @@ -1400,11 +1362,11 @@ hfsc_dump_class(struct Qdisc *sch, unsig RTA_PUT(skb, TCA_OPTIONS, 0, NULL); if (hfsc_dump_curves(skb, cl) < 0) goto rtattr_failure; - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1459,21 +1421,11 @@ hfsc_walk(struct Qdisc *sch, struct qdis } static void -hfsc_watchdog(unsigned long arg) -{ - struct Qdisc *sch = (struct Qdisc *)arg; - - sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule(sch->dev); -} - -static void -hfsc_schedule_watchdog(struct Qdisc *sch, u64 cur_time) +hfsc_schedule_watchdog(struct Qdisc *sch) { struct hfsc_sched *q = qdisc_priv(sch); struct hfsc_class *cl; u64 next_time = 0; - long delay; if ((cl = eltree_get_minel(q)) != NULL) next_time = cl->cl_e; @@ -1482,11 +1434,7 @@ hfsc_schedule_watchdog(struct Qdisc *sch next_time = q->root.cl_cfmin; } WARN_ON(next_time == 0); - delay = next_time - cur_time; - delay = PSCHED_US2JIFFIE(delay); - - sch->flags |= TCQ_F_THROTTLED; - mod_timer(&q->wd_timer, jiffies + delay); + qdisc_watchdog_schedule(&q->watchdog, next_time); } static int @@ -1523,9 +1471,7 @@ hfsc_init_qdisc(struct Qdisc *sch, struc list_add(&q->root.hlist, &q->clhash[hfsc_hash(q->root.classid)]); - init_timer(&q->wd_timer); - q->wd_timer.function = hfsc_watchdog; - q->wd_timer.data = (unsigned long)sch; + qdisc_watchdog_init(&q->watchdog, sch); return 0; } @@ -1595,8 +1541,7 @@ hfsc_reset_qdisc(struct Qdisc *sch) __skb_queue_purge(&q->requeue); q->eligible = RB_ROOT; INIT_LIST_HEAD(&q->droplist); - del_timer(&q->wd_timer); - sch->flags &= ~TCQ_F_THROTTLED; + qdisc_watchdog_cancel(&q->watchdog); sch->q.qlen = 0; } @@ -1612,14 +1557,14 @@ hfsc_destroy_qdisc(struct Qdisc *sch) hfsc_destroy_class(sch, cl); } __skb_queue_purge(&q->requeue); - del_timer(&q->wd_timer); + qdisc_watchdog_cancel(&q->watchdog); } static int hfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb) { struct hfsc_sched *q = qdisc_priv(sch); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_hfsc_qopt qopt; qopt.defcls = q->defcls; @@ -1627,7 +1572,7 @@ hfsc_dump_qdisc(struct Qdisc *sch, struc return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1681,7 +1626,7 @@ hfsc_dequeue(struct Qdisc *sch) if ((skb = __skb_dequeue(&q->requeue))) goto out; - PSCHED_GET_TIME(cur_time); + cur_time = psched_get_time(); /* * if there are eligible classes, use real-time criteria. @@ -1698,7 +1643,7 @@ hfsc_dequeue(struct Qdisc *sch) cl = vttree_get_minvt(&q->root, cur_time); if (cl == NULL) { sch->qstats.overlimits++; - hfsc_schedule_watchdog(sch, cur_time); + hfsc_schedule_watchdog(sch); return NULL; } } diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 3c3294d..99bcec8 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -50,6 +50,7 @@ #include #include #include #include +#include #include #include #include @@ -128,7 +129,7 @@ #endif } un; struct rb_node node[TC_HTB_NUMPRIO]; /* node for self or feed tree */ struct rb_node pq_node; /* node for event queue */ - unsigned long pq_key; /* the same type as jiffies global */ + psched_time_t pq_key; int prio_activity; /* for which prios are we active */ enum htb_cmode cmode; /* current mode of the class */ @@ -179,10 +180,7 @@ struct htb_sched { struct rb_root wait_pq[TC_HTB_MAXDEPTH]; /* time of nearest event per level (row) */ - unsigned long near_ev_cache[TC_HTB_MAXDEPTH]; - - /* cached value of jiffies in dequeue */ - unsigned long jiffies; + psched_time_t near_ev_cache[TC_HTB_MAXDEPTH]; /* whether we hit non-work conserving class during this dequeue; we use */ int nwc_hit; /* this to disable mindelay complaint in dequeue */ @@ -195,7 +193,7 @@ struct htb_sched { int rate2quantum; /* quant = rate / rate2quantum */ psched_time_t now; /* cached dequeue time */ - struct timer_list timer; /* send delay timer */ + struct qdisc_watchdog watchdog; #ifdef HTB_RATECM struct timer_list rttim; /* rate computer timer */ int recmp_bucket; /* which hash bucket to recompute next */ @@ -342,19 +340,19 @@ static void htb_add_to_wait_tree(struct { struct rb_node **p = &q->wait_pq[cl->level].rb_node, *parent = NULL; - cl->pq_key = q->jiffies + PSCHED_US2JIFFIE(delay); - if (cl->pq_key == q->jiffies) + cl->pq_key = q->now + delay; + if (cl->pq_key == q->now) cl->pq_key++; /* update the nearest event cache */ - if (time_after(q->near_ev_cache[cl->level], cl->pq_key)) + if (q->near_ev_cache[cl->level] > cl->pq_key) q->near_ev_cache[cl->level] = cl->pq_key; while (*p) { struct htb_class *c; parent = *p; c = rb_entry(parent, struct htb_class, pq_node); - if (time_after_eq(cl->pq_key, c->pq_key)) + if (cl->pq_key >= c->pq_key) p = &parent->rb_right; else p = &parent->rb_left; @@ -679,14 +677,6 @@ static int htb_requeue(struct sk_buff *s return NET_XMIT_SUCCESS; } -static void htb_timer(unsigned long arg) -{ - struct Qdisc *sch = (struct Qdisc *)arg; - sch->flags &= ~TCQ_F_THROTTLED; - wmb(); - netif_schedule(sch->dev); -} - #ifdef HTB_RATECM #define RT_GEN(D,R) R+=D-(R/HTB_EWMAC);D=0 static void htb_rate_timer(unsigned long arg) @@ -739,7 +729,7 @@ #define HTB_ACCNT(T,B,R) toks = diff + c cl->T = toks while (cl) { - diff = PSCHED_TDIFF_SAFE(q->now, cl->t_c, (u32) cl->mbuffer); + diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer); if (cl->level >= level) { if (cl->level == level) cl->xstats.lends++; @@ -778,11 +768,11 @@ #endif /** * htb_do_events - make mode changes to classes at the level * - * Scans event queue for pending events and applies them. Returns jiffies to + * Scans event queue for pending events and applies them. Returns time of * next pending event (0 for no event in pq). - * Note: Aplied are events whose have cl->pq_key <= jiffies. + * Note: Applied are events whose have cl->pq_key <= q->now. */ -static long htb_do_events(struct htb_sched *q, int level) +static psched_time_t htb_do_events(struct htb_sched *q, int level) { int i; @@ -795,18 +785,18 @@ static long htb_do_events(struct htb_sch return 0; cl = rb_entry(p, struct htb_class, pq_node); - if (time_after(cl->pq_key, q->jiffies)) { - return cl->pq_key - q->jiffies; - } + if (cl->pq_key > q->now) + return cl->pq_key; + htb_safe_rb_erase(p, q->wait_pq + level); - diff = PSCHED_TDIFF_SAFE(q->now, cl->t_c, (u32) cl->mbuffer); + diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer); htb_change_class_mode(q, cl, &diff); if (cl->cmode != HTB_CAN_SEND) htb_add_to_wait_tree(q, cl, diff); } if (net_ratelimit()) printk(KERN_WARNING "htb: too many events !\n"); - return HZ / 10; + return q->now + PSCHED_TICKS_PER_SEC / 10; } /* Returns class->node+prio from id-tree where classe's id is >= id. NULL @@ -958,30 +948,12 @@ next: return skb; } -static void htb_delay_by(struct Qdisc *sch, long delay) -{ - struct htb_sched *q = qdisc_priv(sch); - if (delay <= 0) - delay = 1; - if (unlikely(delay > 5 * HZ)) { - if (net_ratelimit()) - printk(KERN_INFO "HTB delay %ld > 5sec\n", delay); - delay = 5 * HZ; - } - /* why don't use jiffies here ? because expires can be in past */ - mod_timer(&q->timer, q->jiffies + delay); - sch->flags |= TCQ_F_THROTTLED; - sch->qstats.overlimits++; -} - static struct sk_buff *htb_dequeue(struct Qdisc *sch) { struct sk_buff *skb = NULL; struct htb_sched *q = qdisc_priv(sch); int level; - long min_delay; - - q->jiffies = jiffies; + psched_time_t next_event; /* try to dequeue direct packets as high prio (!) to minimize cpu work */ skb = __skb_dequeue(&q->direct_queue); @@ -993,23 +965,25 @@ static struct sk_buff *htb_dequeue(struc if (!sch->q.qlen) goto fin; - PSCHED_GET_TIME(q->now); + q->now = psched_get_time(); - min_delay = LONG_MAX; + next_event = q->now + 5 * PSCHED_TICKS_PER_SEC; q->nwc_hit = 0; for (level = 0; level < TC_HTB_MAXDEPTH; level++) { /* common case optimization - skip event handler quickly */ int m; - long delay; - if (time_after_eq(q->jiffies, q->near_ev_cache[level])) { - delay = htb_do_events(q, level); - q->near_ev_cache[level] = - q->jiffies + (delay ? delay : HZ); + psched_time_t event; + + if (q->now >= q->near_ev_cache[level]) { + event = htb_do_events(q, level); + q->near_ev_cache[level] = event ? event : + PSCHED_TICKS_PER_SEC; } else - delay = q->near_ev_cache[level] - q->jiffies; + event = q->near_ev_cache[level]; + + if (event && next_event > event) + next_event = event; - if (delay && min_delay > delay) - min_delay = delay; m = ~q->row_mask[level]; while (m != (int)(-1)) { int prio = ffz(m); @@ -1022,7 +996,8 @@ static struct sk_buff *htb_dequeue(struc } } } - htb_delay_by(sch, min_delay > 5 * HZ ? 5 * HZ : min_delay); + sch->qstats.overlimits++; + qdisc_watchdog_schedule(&q->watchdog, next_event); fin: return skb; } @@ -1075,8 +1050,7 @@ static void htb_reset(struct Qdisc *sch) } } - sch->flags &= ~TCQ_F_THROTTLED; - del_timer(&q->timer); + qdisc_watchdog_cancel(&q->watchdog); __skb_queue_purge(&q->direct_queue); sch->q.qlen = 0; memset(q->row, 0, sizeof(q->row)); @@ -1113,14 +1087,12 @@ static int htb_init(struct Qdisc *sch, s for (i = 0; i < TC_HTB_NUMPRIO; i++) INIT_LIST_HEAD(q->drops + i); - init_timer(&q->timer); + qdisc_watchdog_init(&q->watchdog, sch); skb_queue_head_init(&q->direct_queue); q->direct_qlen = sch->dev->tx_queue_len; if (q->direct_qlen < 2) /* some devices have zero tx_queue_len */ q->direct_qlen = 2; - q->timer.function = htb_timer; - q->timer.data = (unsigned long)sch; #ifdef HTB_RATECM init_timer(&q->rttim); @@ -1139,7 +1111,7 @@ #endif static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) { struct htb_sched *q = qdisc_priv(sch); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; struct tc_htb_glob gopt; spin_lock_bh(&sch->dev->queue_lock); @@ -1152,12 +1124,12 @@ static int htb_dump(struct Qdisc *sch, s rta = (struct rtattr *)b; RTA_PUT(skb, TCA_OPTIONS, 0, NULL); RTA_PUT(skb, TCA_HTB_INIT, sizeof(gopt), &gopt); - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; spin_unlock_bh(&sch->dev->queue_lock); return skb->len; rtattr_failure: spin_unlock_bh(&sch->dev->queue_lock); - skb_trim(skb, skb->tail - skb->data); + nlmsg_trim(skb, skb_tail_pointer(skb)); return -1; } @@ -1165,7 +1137,7 @@ static int htb_dump_class(struct Qdisc * struct sk_buff *skb, struct tcmsg *tcm) { struct htb_class *cl = (struct htb_class *)arg; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; struct tc_htb_opt opt; @@ -1188,12 +1160,12 @@ static int htb_dump_class(struct Qdisc * opt.prio = cl->un.leaf.prio; opt.level = cl->level; RTA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt); - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; spin_unlock_bh(&sch->dev->queue_lock); return skb->len; rtattr_failure: spin_unlock_bh(&sch->dev->queue_lock); - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1264,16 +1236,6 @@ static unsigned long htb_get(struct Qdis return (unsigned long)cl; } -static void htb_destroy_filters(struct tcf_proto **fl) -{ - struct tcf_proto *tp; - - while ((tp = *fl) != NULL) { - *fl = tp->next; - tcf_destroy(tp); - } -} - static inline int htb_parent_last_child(struct htb_class *cl) { if (!cl->parent) @@ -1302,7 +1264,7 @@ static void htb_parent_to_leaf(struct ht parent->un.leaf.prio = parent->prio; parent->tokens = parent->buffer; parent->ctokens = parent->cbuffer; - PSCHED_GET_TIME(parent->t_c); + parent->t_c = psched_get_time(); parent->cmode = HTB_CAN_SEND; } @@ -1317,7 +1279,7 @@ static void htb_destroy_class(struct Qdi qdisc_put_rtab(cl->rate); qdisc_put_rtab(cl->ceil); - htb_destroy_filters(&cl->filter_list); + tcf_destroy_chain(cl->filter_list); while (!list_empty(&cl->children)) htb_destroy_class(sch, list_entry(cl->children.next, @@ -1341,7 +1303,7 @@ static void htb_destroy(struct Qdisc *sc { struct htb_sched *q = qdisc_priv(sch); - del_timer_sync(&q->timer); + qdisc_watchdog_cancel(&q->watchdog); #ifdef HTB_RATECM del_timer_sync(&q->rttim); #endif @@ -1349,7 +1311,7 @@ #endif and surprisingly it worked in 2.4. But it must precede it because filter need its target class alive to be able to call unbind_filter on it (without Oops). */ - htb_destroy_filters(&q->filter_list); + tcf_destroy_chain(q->filter_list); while (!list_empty(&q->root)) htb_destroy_class(sch, list_entry(q->root.next, @@ -1498,8 +1460,8 @@ static int htb_change_class(struct Qdisc /* set class to be in HTB_CAN_SEND state */ cl->tokens = hopt->buffer; cl->ctokens = hopt->cbuffer; - cl->mbuffer = PSCHED_JIFFIE2US(HZ * 60); /* 1min */ - PSCHED_GET_TIME(cl->t_c); + cl->mbuffer = 60 * PSCHED_TICKS_PER_SEC; /* 1min */ + cl->t_c = psched_get_time(); cl->cmode = HTB_CAN_SEND; /* attach to the hash list and parent's family */ diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index cfe070e..f8b9f1c 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -16,6 +16,7 @@ #include #include #include #include +#include #include #include #include @@ -169,7 +170,7 @@ #ifdef CONFIG_NET_CLS_ACT skb->tc_index = TC_H_MIN(res.classid); result = TC_ACT_OK; break; - }; + } /* backward compat */ #else #ifdef CONFIG_NET_CLS_POLICE @@ -186,7 +187,7 @@ #ifdef CONFIG_NET_CLS_POLICE sch->bstats.bytes += skb->len; result = NF_ACCEPT; break; - }; + } #else D2PRINTK("Overriding result to ACCEPT\n"); @@ -247,16 +248,11 @@ ing_hook(unsigned int hook, struct sk_bu skb->dev ? (*pskb)->dev->name : "(no dev)", skb->len); -/* -revisit later: Use a private since lock dev->queue_lock is also -used on the egress (might slow things for an iota) -*/ - if (dev->qdisc_ingress) { - spin_lock(&dev->queue_lock); + spin_lock(&dev->ingress_lock); if ((q = dev->qdisc_ingress) != NULL) fwres = q->enqueue(skb, q); - spin_unlock(&dev->queue_lock); + spin_unlock(&dev->ingress_lock); } return fwres; @@ -345,14 +341,9 @@ #endif static void ingress_destroy(struct Qdisc *sch) { struct ingress_qdisc_data *p = PRIV(sch); - struct tcf_proto *tp; DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p); - while (p->filter_list) { - tp = p->filter_list; - p->filter_list = tp->next; - tcf_destroy(tp); - } + tcf_destroy_chain(p->filter_list); #if 0 /* for future use */ qdisc_destroy(p->q); @@ -362,16 +353,16 @@ #endif static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb) { - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; rta = (struct rtattr *) b; RTA_PUT(skb, TCA_OPTIONS, 0, NULL); - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 1ccbfb5..5d9d8bc 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #define VERSION "1.2" @@ -54,21 +55,22 @@ #define VERSION "1.2" struct netem_sched_data { struct Qdisc *qdisc; - struct timer_list timer; + struct qdisc_watchdog watchdog; + + psched_tdiff_t latency; + psched_tdiff_t jitter; - u32 latency; u32 loss; u32 limit; u32 counter; u32 gap; - u32 jitter; u32 duplicate; u32 reorder; u32 corrupt; struct crndstate { - unsigned long last; - unsigned long rho; + u32 last; + u32 rho; } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor; struct disttable { @@ -95,12 +97,12 @@ static void init_crandom(struct crndstat * Next number depends on last value. * rho is scaled to avoid floating point. */ -static unsigned long get_crandom(struct crndstate *state) +static u32 get_crandom(struct crndstate *state) { u64 value, rho; unsigned long answer; - if (state->rho == 0) /* no correllation */ + if (state->rho == 0) /* no correlation */ return net_random(); value = net_random(); @@ -114,11 +116,13 @@ static unsigned long get_crandom(struct * std deviation sigma. Uses table lookup to approximate the desired * distribution, and a uniformly-distributed pseudo-random source. */ -static long tabledist(unsigned long mu, long sigma, - struct crndstate *state, const struct disttable *dist) +static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma, + struct crndstate *state, + const struct disttable *dist) { - long t, x; - unsigned long rnd; + psched_tdiff_t x; + long t; + u32 rnd; if (sigma == 0) return mu; @@ -213,8 +217,8 @@ static int netem_enqueue(struct sk_buff delay = tabledist(q->latency, q->jitter, &q->delay_cor, q->delay_dist); - PSCHED_GET_TIME(now); - PSCHED_TADD2(now, delay, cb->time_to_send); + now = psched_get_time(); + cb->time_to_send = now + delay; ++q->counter; ret = q->qdisc->enqueue(skb, q->qdisc); } else { @@ -222,7 +226,7 @@ static int netem_enqueue(struct sk_buff * Do re-ordering by putting one out of N packets at the front * of the queue. */ - PSCHED_GET_TIME(cb->time_to_send); + cb->time_to_send = psched_get_time(); q->counter = 0; ret = q->qdisc->ops->requeue(skb, q->qdisc); } @@ -269,55 +273,43 @@ static struct sk_buff *netem_dequeue(str struct netem_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; + smp_mb(); + if (sch->flags & TCQ_F_THROTTLED) + return NULL; + skb = q->qdisc->dequeue(q->qdisc); if (skb) { const struct netem_skb_cb *cb = (const struct netem_skb_cb *)skb->cb; - psched_time_t now; + psched_time_t now = psched_get_time(); /* if more time remaining? */ - PSCHED_GET_TIME(now); - - if (PSCHED_TLESS(cb->time_to_send, now)) { + if (cb->time_to_send <= now) { pr_debug("netem_dequeue: return skb=%p\n", skb); sch->q.qlen--; - sch->flags &= ~TCQ_F_THROTTLED; return skb; - } else { - psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now); - - if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) { - qdisc_tree_decrease_qlen(q->qdisc, 1); - sch->qstats.drops++; - printk(KERN_ERR "netem: queue discpline %s could not requeue\n", - q->qdisc->ops->id); - } + } - mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay)); - sch->flags |= TCQ_F_THROTTLED; + if (unlikely(q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS)) { + qdisc_tree_decrease_qlen(q->qdisc, 1); + sch->qstats.drops++; + printk(KERN_ERR "netem: %s could not requeue\n", + q->qdisc->ops->id); } + + qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send); } return NULL; } -static void netem_watchdog(unsigned long arg) -{ - struct Qdisc *sch = (struct Qdisc *)arg; - - pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen); - sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule(sch->dev); -} - static void netem_reset(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); qdisc_reset(q->qdisc); sch->q.qlen = 0; - sch->flags &= ~TCQ_F_THROTTLED; - del_timer_sync(&q->timer); + qdisc_watchdog_cancel(&q->watchdog); } /* Pass size change message down to embedded FIFO */ @@ -438,10 +430,11 @@ static int netem_change(struct Qdisc *sc q->loss = qopt->loss; q->duplicate = qopt->duplicate; - /* for compatiablity with earlier versions. - * if gap is set, need to assume 100% probablity + /* for compatibility with earlier versions. + * if gap is set, need to assume 100% probability */ - q->reorder = ~0; + if (q->gap) + q->reorder = ~0; /* Handle nested options after initial queue options. * Should have put all options in nested format but too late now. @@ -487,22 +480,28 @@ static int netem_change(struct Qdisc *sc */ struct fifo_sched_data { u32 limit; + psched_time_t oldest; }; static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) { struct fifo_sched_data *q = qdisc_priv(sch); struct sk_buff_head *list = &sch->q; - const struct netem_skb_cb *ncb - = (const struct netem_skb_cb *)nskb->cb; + psched_time_t tnext = ((struct netem_skb_cb *)nskb->cb)->time_to_send; struct sk_buff *skb; if (likely(skb_queue_len(list) < q->limit)) { + /* Optimize for add at tail */ + if (likely(skb_queue_empty(list) || tnext >= q->oldest)) { + q->oldest = tnext; + return qdisc_enqueue_tail(nskb, sch); + } + skb_queue_reverse_walk(list, skb) { const struct netem_skb_cb *cb = (const struct netem_skb_cb *)skb->cb; - if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send)) + if (tnext >= cb->time_to_send) break; } @@ -515,7 +514,7 @@ static int tfifo_enqueue(struct sk_buff return NET_XMIT_SUCCESS; } - return qdisc_drop(nskb, sch); + return qdisc_reshape_fail(nskb, sch); } static int tfifo_init(struct Qdisc *sch, struct rtattr *opt) @@ -531,6 +530,7 @@ static int tfifo_init(struct Qdisc *sch, } else q->limit = max_t(u32, sch->dev->tx_queue_len, 1); + q->oldest = PSCHED_PASTPERFECT; return 0; } @@ -567,9 +567,7 @@ static int netem_init(struct Qdisc *sch, if (!opt) return -EINVAL; - init_timer(&q->timer); - q->timer.function = netem_watchdog; - q->timer.data = (unsigned long) sch; + qdisc_watchdog_init(&q->watchdog, sch); q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops, TC_H_MAKE(sch->handle, 1)); @@ -590,7 +588,7 @@ static void netem_destroy(struct Qdisc * { struct netem_sched_data *q = qdisc_priv(sch); - del_timer_sync(&q->timer); + qdisc_watchdog_cancel(&q->watchdog); qdisc_destroy(q->qdisc); kfree(q->delay_dist); } @@ -598,7 +596,7 @@ static void netem_destroy(struct Qdisc * static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) { const struct netem_sched_data *q = qdisc_priv(sch); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta = (struct rtattr *) b; struct tc_netem_qopt qopt; struct tc_netem_corr cor; @@ -626,12 +624,12 @@ static int netem_dump(struct Qdisc *sch, corrupt.correlation = q->corrupt_cor.rho; RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index de889f2..269a6e1 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -32,6 +32,7 @@ #include #include #include #include +#include #include #include @@ -61,7 +62,7 @@ #ifdef CONFIG_NET_CLS_ACT *qerr = NET_XMIT_SUCCESS; case TC_ACT_SHOT: return NULL; - }; + } if (!q->filter_list ) { #else @@ -188,13 +189,8 @@ prio_destroy(struct Qdisc* sch) { int prio; struct prio_sched_data *q = qdisc_priv(sch); - struct tcf_proto *tp; - - while ((tp = q->filter_list) != NULL) { - q->filter_list = tp->next; - tcf_destroy(tp); - } + tcf_destroy_chain(q->filter_list); for (prio=0; priobands; prio++) qdisc_destroy(q->queues[prio]); } @@ -271,7 +267,7 @@ static int prio_init(struct Qdisc *sch, static int prio_dump(struct Qdisc *sch, struct sk_buff *skb) { struct prio_sched_data *q = qdisc_priv(sch); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_prio_qopt opt; opt.bands = q->bands; @@ -280,7 +276,7 @@ static int prio_dump(struct Qdisc *sch, return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 66f3205..96dfdf7 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -30,6 +30,7 @@ #include #include #include #include +#include #include #include #include @@ -137,7 +138,7 @@ static unsigned sfq_hash(struct sfq_sche switch (skb->protocol) { case __constant_htons(ETH_P_IP): { - struct iphdr *iph = skb->nh.iph; + const struct iphdr *iph = ip_hdr(skb); h = iph->daddr; h2 = iph->saddr^iph->protocol; if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && @@ -152,7 +153,7 @@ static unsigned sfq_hash(struct sfq_sche } case __constant_htons(ETH_P_IPV6): { - struct ipv6hdr *iph = skb->nh.ipv6h; + struct ipv6hdr *iph = ipv6_hdr(skb); h = iph->daddr.s6_addr32[3]; h2 = iph->saddr.s6_addr32[3]^iph->nexthdr; if (iph->nexthdr == IPPROTO_TCP || @@ -461,7 +462,7 @@ static void sfq_destroy(struct Qdisc *sc static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb) { struct sfq_sched_data *q = qdisc_priv(sch); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct tc_sfq_qopt opt; opt.quantum = q->quantum; @@ -476,7 +477,7 @@ static int sfq_dump(struct Qdisc *sch, s return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 85da8da..5386295 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -32,6 +32,7 @@ #include #include #include #include +#include #include #include #include @@ -127,8 +128,8 @@ struct tbf_sched_data long tokens; /* Current number of B tokens */ long ptokens; /* Current number of P tokens */ psched_time_t t_c; /* Time check-point */ - struct timer_list wd_timer; /* Watchdog timer */ struct Qdisc *qdisc; /* Inner qdisc, default - bfifo queue */ + struct qdisc_watchdog watchdog; /* Watchdog timer */ }; #define L2T(q,L) ((q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log]) @@ -185,14 +186,6 @@ static unsigned int tbf_drop(struct Qdis return len; } -static void tbf_watchdog(unsigned long arg) -{ - struct Qdisc *sch = (struct Qdisc*)arg; - - sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule(sch->dev); -} - static struct sk_buff *tbf_dequeue(struct Qdisc* sch) { struct tbf_sched_data *q = qdisc_priv(sch); @@ -202,13 +195,12 @@ static struct sk_buff *tbf_dequeue(struc if (skb) { psched_time_t now; - long toks, delay; + long toks; long ptoks = 0; unsigned int len = skb->len; - PSCHED_GET_TIME(now); - - toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer); + now = psched_get_time(); + toks = psched_tdiff_bounded(now, q->t_c, q->buffer); if (q->P_tab) { ptoks = toks + q->ptokens; @@ -230,12 +222,8 @@ static struct sk_buff *tbf_dequeue(struc return skb; } - delay = PSCHED_US2JIFFIE(max_t(long, -toks, -ptoks)); - - if (delay == 0) - delay = 1; - - mod_timer(&q->wd_timer, jiffies+delay); + qdisc_watchdog_schedule(&q->watchdog, + now + max_t(long, -toks, -ptoks)); /* Maybe we have a shorter packet in the queue, which can be sent now. It sounds cool, @@ -254,7 +242,6 @@ static struct sk_buff *tbf_dequeue(struc sch->qstats.drops++; } - sch->flags |= TCQ_F_THROTTLED; sch->qstats.overlimits++; } return NULL; @@ -266,11 +253,10 @@ static void tbf_reset(struct Qdisc* sch) qdisc_reset(q->qdisc); sch->q.qlen = 0; - PSCHED_GET_TIME(q->t_c); + q->t_c = psched_get_time(); q->tokens = q->buffer; q->ptokens = q->mtu; - sch->flags &= ~TCQ_F_THROTTLED; - del_timer(&q->wd_timer); + qdisc_watchdog_cancel(&q->watchdog); } static struct Qdisc *tbf_create_dflt_qdisc(struct Qdisc *sch, u32 limit) @@ -377,11 +363,8 @@ static int tbf_init(struct Qdisc* sch, s if (opt == NULL) return -EINVAL; - PSCHED_GET_TIME(q->t_c); - init_timer(&q->wd_timer); - q->wd_timer.function = tbf_watchdog; - q->wd_timer.data = (unsigned long)sch; - + q->t_c = psched_get_time(); + qdisc_watchdog_init(&q->watchdog, sch); q->qdisc = &noop_qdisc; return tbf_change(sch, opt); @@ -391,7 +374,7 @@ static void tbf_destroy(struct Qdisc *sc { struct tbf_sched_data *q = qdisc_priv(sch); - del_timer(&q->wd_timer); + qdisc_watchdog_cancel(&q->watchdog); if (q->P_tab) qdisc_put_rtab(q->P_tab); @@ -404,7 +387,7 @@ static void tbf_destroy(struct Qdisc *sc static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb) { struct tbf_sched_data *q = qdisc_priv(sch); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta; struct tc_tbf_qopt opt; @@ -420,12 +403,12 @@ static int tbf_dump(struct Qdisc *sch, s opt.mtu = q->mtu; opt.buffer = q->buffer; RTA_PUT(skb, TCA_TBF_PARMS, sizeof(opt), &opt); - rta->rta_len = skb->tail - b; + rta->rta_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 587123c..d24914d 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -323,7 +323,7 @@ restart: nores = 1; break; } - __skb_pull(skb, skb->nh.raw - skb->data); + __skb_pull(skb, skb_network_offset(skb)); } while ((q = NEXT_SLAVE(q)) != start); if (nores && skb_res == NULL) { diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 78d2ddb..db73ef9 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -143,7 +143,7 @@ static struct sctp_association *sctp_ass /* Initialize the maximum mumber of new data packets that can be sent * in a burst. */ - asoc->max_burst = sctp_max_burst; + asoc->max_burst = sp->max_burst; /* initialize association timers */ asoc->timeouts[SCTP_EVENT_TIMEOUT_NONE] = 0; @@ -714,8 +714,16 @@ void sctp_assoc_control_transport(struct /* Record the transition on the transport. */ switch (command) { case SCTP_TRANSPORT_UP: + /* If we are moving from UNCONFIRMED state due + * to heartbeat success, report the SCTP_ADDR_CONFIRMED + * state to the user, otherwise report SCTP_ADDR_AVAILABLE. + */ + if (SCTP_UNCONFIRMED == transport->state && + SCTP_HEARTBEAT_SUCCESS == error) + spc_state = SCTP_ADDR_CONFIRMED; + else + spc_state = SCTP_ADDR_AVAILABLE; transport->state = SCTP_ACTIVE; - spc_state = SCTP_ADDR_AVAILABLE; break; case SCTP_TRANSPORT_DOWN: @@ -725,7 +733,7 @@ void sctp_assoc_control_transport(struct default: return; - }; + } /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the * user. diff --git a/net/sctp/debug.c b/net/sctp/debug.c index 5f5ab28..e8c0f74 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -93,8 +93,9 @@ const char *sctp_cname(const sctp_subtyp return "FWD_TSN"; default: - return "unknown chunk"; - }; + break; + } + return "unknown chunk"; } diff --git a/net/sctp/input.c b/net/sctp/input.c index 71db668..885109f 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -79,14 +79,10 @@ static void sctp_add_backlog(struct sock /* Calculate the SCTP checksum of an SCTP packet. */ static inline int sctp_rcv_checksum(struct sk_buff *skb) { - struct sctphdr *sh; - __u32 cmp, val; struct sk_buff *list = skb_shinfo(skb)->frag_list; - - sh = (struct sctphdr *) skb->h.raw; - cmp = ntohl(sh->checksum); - - val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); + struct sctphdr *sh = sctp_hdr(skb); + __u32 cmp = ntohl(sh->checksum); + __u32 val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); for (; list; list = list->next) val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list), @@ -138,14 +134,13 @@ int sctp_rcv(struct sk_buff *skb) if (skb_linearize(skb)) goto discard_it; - sh = (struct sctphdr *) skb->h.raw; + sh = sctp_hdr(skb); /* Pull up the IP and SCTP headers. */ - __skb_pull(skb, skb->h.raw - skb->data); + __skb_pull(skb, skb_transport_offset(skb)); if (skb->len < sizeof(struct sctphdr)) goto discard_it; - if ((skb->ip_summed != CHECKSUM_UNNECESSARY) && - (sctp_rcv_checksum(skb) < 0)) + if (!skb_csum_unnecessary(skb) && sctp_rcv_checksum(skb) < 0) goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); @@ -154,7 +149,7 @@ int sctp_rcv(struct sk_buff *skb) if (skb->len < sizeof(struct sctp_chunkhdr)) goto discard_it; - family = ipver2af(skb->nh.iph->version); + family = ipver2af(ip_hdr(skb)->version); af = sctp_get_af_specific(family); if (unlikely(!af)) goto discard_it; @@ -510,30 +505,30 @@ void sctp_err_finish(struct sock *sk, st void sctp_v4_err(struct sk_buff *skb, __u32 info) { struct iphdr *iph = (struct iphdr *)skb->data; - struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2)); - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; + const int ihlen = iph->ihl * 4; + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; struct sock *sk; struct sctp_association *asoc = NULL; struct sctp_transport *transport; struct inet_sock *inet; - char *saveip, *savesctp; + sk_buff_data_t saveip, savesctp; int err; - if (skb->len < ((iph->ihl << 2) + 8)) { + if (skb->len < ihlen + 8) { ICMP_INC_STATS_BH(ICMP_MIB_INERRORS); return; } /* Fix up skb to look at the embedded net header. */ - saveip = skb->nh.raw; - savesctp = skb->h.raw; - skb->nh.iph = iph; - skb->h.raw = (char *)sh; - sk = sctp_err_lookup(AF_INET, skb, sh, &asoc, &transport); - /* Put back, the original pointers. */ - skb->nh.raw = saveip; - skb->h.raw = savesctp; + saveip = skb->network_header; + savesctp = skb->transport_header; + skb_reset_network_header(skb); + skb_set_transport_header(skb, ihlen); + sk = sctp_err_lookup(AF_INET, skb, sctp_hdr(skb), &asoc, &transport); + /* Put back, the original values. */ + skb->network_header = saveip; + skb->transport_header = savesctp; if (!sk) { ICMP_INC_STATS_BH(ICMP_MIB_INERRORS); return; @@ -616,7 +611,7 @@ int sctp_rcv_ootb(struct sk_buff *skb) break; ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); - if (ch_end > skb->tail) + if (ch_end > skb_tail_pointer(skb)) break; /* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the @@ -648,7 +643,7 @@ int sctp_rcv_ootb(struct sk_buff *skb) } ch = (sctp_chunkhdr_t *) ch_end; - } while (ch_end < skb->tail); + } while (ch_end < skb_tail_pointer(skb)); return 0; @@ -905,7 +900,7 @@ static struct sctp_association *__sctp_r struct sctp_association *asoc; union sctp_addr addr; union sctp_addr *paddr = &addr; - struct sctphdr *sh = (struct sctphdr *) skb->h.raw; + struct sctphdr *sh = sctp_hdr(skb); sctp_chunkhdr_t *ch; union sctp_params params; sctp_init_chunk_t *init; diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index c30629e..88aa224 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -159,16 +159,16 @@ struct sctp_chunk *sctp_inq_pop(struct s * the skb->tail. */ if (unlikely(skb_is_nonlinear(chunk->skb))) { - if (chunk->chunk_end > chunk->skb->tail) - chunk->chunk_end = chunk->skb->tail; + if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) + chunk->chunk_end = skb_tail_pointer(chunk->skb); } skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); chunk->subh.v = NULL; /* Subheader is no longer valid. */ - if (chunk->chunk_end < chunk->skb->tail) { + if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) { /* This is not a singleton */ chunk->singleton = 0; - } else if (chunk->chunk_end > chunk->skb->tail) { + } else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) { /* RFC 2960, Section 6.10 Bundling * * Partial chunks MUST NOT be placed in an SCTP packet. diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 0b9c49b..ca527a2 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -122,26 +122,24 @@ SCTP_STATIC void sctp_v6_err(struct sk_b int type, int code, int offset, __be32 info) { struct inet6_dev *idev; - struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; - struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); struct sock *sk; struct sctp_association *asoc; struct sctp_transport *transport; struct ipv6_pinfo *np; - char *saveip, *savesctp; + sk_buff_data_t saveip, savesctp; int err; idev = in6_dev_get(skb->dev); /* Fix up skb to look at the embedded net header. */ - saveip = skb->nh.raw; - savesctp = skb->h.raw; - skb->nh.ipv6h = iph; - skb->h.raw = (char *)sh; - sk = sctp_err_lookup(AF_INET6, skb, sh, &asoc, &transport); + saveip = skb->network_header; + savesctp = skb->transport_header; + skb_reset_network_header(skb); + skb_set_transport_header(skb, offset); + sk = sctp_err_lookup(AF_INET6, skb, sctp_hdr(skb), &asoc, &transport); /* Put back, the original pointers. */ - skb->nh.raw = saveip; - skb->h.raw = savesctp; + skb->network_header = saveip; + skb->transport_header = savesctp; if (!sk) { ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); goto out; @@ -391,13 +389,13 @@ static void sctp_v6_from_skb(union sctp_ addr->v6.sin6_flowinfo = 0; /* FIXME */ addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif; - sh = (struct sctphdr *) skb->h.raw; + sh = sctp_hdr(skb); if (is_saddr) { *port = sh->source; - from = &skb->nh.ipv6h->saddr; + from = &ipv6_hdr(skb)->saddr; } else { *port = sh->dest; - from = &skb->nh.ipv6h->daddr; + from = &ipv6_hdr(skb)->daddr; } ipv6_addr_copy(&addr->v6.sin6_addr, from); } @@ -606,7 +604,7 @@ static sctp_scope_t sctp_v6_scope(union default: retval = SCTP_SCOPE_GLOBAL; break; - }; + } return retval; } @@ -699,7 +697,7 @@ static int sctp_v6_skb_iif(const struct /* Was this packet marked by Explicit Congestion Notification? */ static int sctp_v6_is_ce(const struct sk_buff *skb) { - return *((__u32 *)(skb->nh.ipv6h)) & htonl(1<<20); + return *((__u32 *)(ipv6_hdr(skb))) & htonl(1 << 20); } /* Dump the v6 addr to the seq file. */ @@ -766,19 +764,19 @@ static void sctp_inet6_skb_msgname(struc if (msgname) { sctp_inet6_msgname(msgname, addr_len); sin6 = (struct sockaddr_in6 *)msgname; - sh = (struct sctphdr *)skb->h.raw; + sh = sctp_hdr(skb); sin6->sin6_port = sh->source; /* Map ipv4 address into v4-mapped-on-v6 address. */ if (sctp_sk(skb->sk)->v4mapped && - skb->nh.iph->version == 4) { + ip_hdr(skb)->version == 4) { sctp_v4_map_v6((union sctp_addr *)sin6); - sin6->sin6_addr.s6_addr32[3] = skb->nh.iph->saddr; + sin6->sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr; return; } /* Otherwise, just copy the v6 address. */ - ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr); + ipv6_addr_copy(&sin6->sin6_addr, &ipv6_hdr(skb)->saddr); if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct sctp_ulpevent *ev = sctp_skb2event(skb); sin6->sin6_scope_id = ev->iif; diff --git a/net/sctp/output.c b/net/sctp/output.c index f875fc3..d85543d 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -176,7 +176,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(s case SCTP_XMIT_OK: case SCTP_XMIT_NAGLE_DELAY: break; - }; + } return retval; } diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 41abfd1..992f361 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -338,7 +338,7 @@ int sctp_outq_tail(struct sctp_outq *q, SCTP_INC_STATS(SCTP_MIB_OUTORDERCHUNKS); q->empty = 0; break; - }; + } } else { list_add_tail(&chunk->list, &q->control_chunk_list); SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); @@ -630,7 +630,7 @@ static int sctp_outq_flush_rtx(struct sc /* Retrieve a new chunk to bundle. */ lchunk = sctp_list_dequeue(lqueue); break; - }; + } /* If we are here due to a retransmit timeout or a fast * retransmit and if there are any chunks left in the retransmit @@ -779,7 +779,7 @@ int sctp_outq_flush(struct sctp_outq *q, default: /* We built a chunk with an illegal type! */ BUG(); - }; + } } /* Is it OK to send data chunks? */ @@ -1397,7 +1397,7 @@ #if SCTP_DEBUG SCTP_DEBUG_PRINTK("ACKed: %08x", tsn); dbg_prt_state = 0; dbg_ack_tsn = tsn; - }; + } dbg_last_ack_tsn = tsn; #endif /* SCTP_DEBUG */ @@ -1452,7 +1452,7 @@ #if SCTP_DEBUG SCTP_DEBUG_PRINTK("KEPT: %08x",tsn); dbg_prt_state = 1; dbg_kept_tsn = tsn; - }; + } dbg_last_kept_tsn = tsn; #endif /* SCTP_DEBUG */ @@ -1476,7 +1476,7 @@ #if SCTP_DEBUG } else { SCTP_DEBUG_PRINTK("\n"); } - }; + } #endif /* SCTP_DEBUG */ if (transport) { if (bytes_acked) { diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index e17a823..c361deb 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -235,13 +235,13 @@ static void sctp_v4_from_skb(union sctp_ port = &addr->v4.sin_port; addr->v4.sin_family = AF_INET; - sh = (struct sctphdr *) skb->h.raw; + sh = sctp_hdr(skb); if (is_saddr) { *port = sh->source; - from = &skb->nh.iph->saddr; + from = &ip_hdr(skb)->saddr; } else { *port = sh->dest; - from = &skb->nh.iph->daddr; + from = &ip_hdr(skb)->daddr; } memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr)); } @@ -530,7 +530,7 @@ static int sctp_v4_skb_iif(const struct /* Was this packet marked by Explicit Congestion Notification? */ static int sctp_v4_is_ce(const struct sk_buff *skb) { - return INET_ECN_is_ce(skb->nh.iph->tos); + return INET_ECN_is_ce(ip_hdr(skb)->tos); } /* Create and initialize a new sk for the socket returned by accept(). */ @@ -731,15 +731,13 @@ static void sctp_inet_event_msgname(stru /* Initialize and copy out a msgname from an inbound skb. */ static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *len) { - struct sctphdr *sh; - struct sockaddr_in *sin; - if (msgname) { + struct sctphdr *sh = sctp_hdr(skb); + struct sockaddr_in *sin = (struct sockaddr_in *)msgname; + sctp_inet_msgname(msgname, len); - sin = (struct sockaddr_in *)msgname; - sh = (struct sctphdr *)skb->h.raw; sin->sin_port = sh->source; - sin->sin_addr.s_addr = skb->nh.iph->saddr; + sin->sin_addr.s_addr = ip_hdr(skb)->saddr; } } @@ -1044,7 +1042,7 @@ SCTP_STATIC __init int sctp_init(void) sctp_cookie_preserve_enable = 1; /* Max.Burst - 4 */ - sctp_max_burst = SCTP_MAX_BURST; + sctp_max_burst = SCTP_DEFAULT_MAX_BURST; /* Association.Max.Retrans - 10 attempts * Path.Max.Retrans - 5 attempts (per destination address) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index f7fb29d..be783a3 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -86,7 +86,7 @@ int sctp_chunk_iif(const struct sctp_chu struct sctp_af *af; int iif = 0; - af = sctp_get_af_specific(ipver2af(chunk->skb->nh.iph->version)); + af = sctp_get_af_specific(ipver2af(ip_hdr(chunk->skb)->version)); if (af) iif = af->skb_iif(chunk->skb); @@ -1143,7 +1143,7 @@ void *sctp_addto_chunk(struct sctp_chunk /* Adjust the chunk length field. */ chunk->chunk_hdr->length = htons(chunklen + padlen + len); - chunk->chunk_end = chunk->skb->tail; + chunk->chunk_end = skb_tail_pointer(chunk->skb); return target; } @@ -1168,7 +1168,7 @@ int sctp_user_addto_chunk(struct sctp_ch /* Adjust the chunk length field. */ chunk->chunk_hdr->length = htons(ntohs(chunk->chunk_hdr->length) + len); - chunk->chunk_end = chunk->skb->tail; + chunk->chunk_end = skb_tail_pointer(chunk->skb); out: return err; @@ -1233,7 +1233,7 @@ struct sctp_association *sctp_make_temp_ asoc->temp = 1; skb = chunk->skb; /* Create an entry for the source address of the packet. */ - af = sctp_get_af_specific(ipver2af(skb->nh.iph->version)); + af = sctp_get_af_specific(ipver2af(ip_hdr(skb)->version)); if (unlikely(!af)) goto fail; af->from_skb(&asoc->c.peer_addr, skb, 1); @@ -2077,7 +2077,7 @@ static int sctp_process_param(struct sct default: /* Just ignore anything else. */ break; - }; + } } break; @@ -2118,7 +2118,7 @@ static int sctp_process_param(struct sct SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n", ntohs(param.p->type), asoc); break; - }; + } return retval; } diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 1355674..b37a7ad 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -464,7 +464,7 @@ static void sctp_cmd_init_failed(sctp_cm struct sctp_ulpevent *event; event = sctp_ulpevent_make_assoc_change(asoc,0, SCTP_CANT_STR_ASSOC, - (__u16)error, 0, 0, + (__u16)error, 0, 0, NULL, GFP_ATOMIC); if (event) @@ -492,8 +492,13 @@ static void sctp_cmd_assoc_failed(sctp_c /* Cancel any partial delivery in progress. */ sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); - event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST, - (__u16)error, 0, 0, + if (event_type == SCTP_EVENT_T_CHUNK && subtype.chunk == SCTP_CID_ABORT) + event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST, + (__u16)error, 0, 0, chunk, + GFP_ATOMIC); + else + event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST, + (__u16)error, 0, 0, NULL, GFP_ATOMIC); if (event) sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, @@ -1004,7 +1009,7 @@ static int sctp_side_effects(sctp_event_ status, state, event_type, subtype.chunk); BUG(); break; - }; + } bail: return error; @@ -1484,7 +1489,8 @@ static int sctp_cmd_interpreter(sctp_eve printk(KERN_WARNING "Impossible command: %u, %p\n", cmd->verb, cmd->obj.ptr); break; - }; + } + if (error) break; } diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index e9097cf..9e28a5d 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -186,7 +186,7 @@ sctp_disposition_t sctp_sf_do_4_C(const * notification is passed to the upper layer. */ ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP, - 0, 0, 0, GFP_ATOMIC); + 0, 0, 0, NULL, GFP_ATOMIC); if (ev) sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); @@ -629,7 +629,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(co case -SCTP_IERROR_BAD_SIG: default: return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - }; + } } @@ -661,7 +661,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(co ev = sctp_ulpevent_make_assoc_change(new_asoc, 0, SCTP_COMM_UP, 0, new_asoc->c.sinit_num_ostreams, new_asoc->c.sinit_max_instreams, - GFP_ATOMIC); + NULL, GFP_ATOMIC); if (!ev) goto nomem_ev; @@ -790,7 +790,7 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(co ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0, asoc->c.sinit_num_ostreams, asoc->c.sinit_max_instreams, - GFP_ATOMIC); + NULL, GFP_ATOMIC); if (!ev) goto nomem; @@ -1195,7 +1195,7 @@ static void sctp_tietags_populate(struct new_asoc->c.my_ttag = asoc->c.my_vtag; new_asoc->c.peer_ttag = asoc->c.peer_vtag; break; - }; + } /* Other parameters for the endpoint SHOULD be copied from the * existing parameters of the association (e.g. number of @@ -1625,7 +1625,7 @@ static sctp_disposition_t sctp_sf_do_dup ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_RESTART, 0, new_asoc->c.sinit_num_ostreams, new_asoc->c.sinit_max_instreams, - GFP_ATOMIC); + NULL, GFP_ATOMIC); if (!ev) goto nomem_ev; @@ -1691,7 +1691,7 @@ static sctp_disposition_t sctp_sf_do_dup ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0, new_asoc->c.sinit_num_ostreams, new_asoc->c.sinit_max_instreams, - GFP_ATOMIC); + NULL, GFP_ATOMIC); if (!ev) goto nomem_ev; @@ -1786,7 +1786,7 @@ static sctp_disposition_t sctp_sf_do_dup SCTP_COMM_UP, 0, asoc->c.sinit_num_ostreams, asoc->c.sinit_max_instreams, - GFP_ATOMIC); + NULL, GFP_ATOMIC); if (!ev) goto nomem; @@ -1904,7 +1904,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupc case -SCTP_IERROR_BAD_SIG: default: return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - }; + } } /* Compare the tie_tag in cookie with the verification tag of @@ -1936,7 +1936,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupc default: /* Discard packet for all others. */ retval = sctp_sf_pdiscard(ep, asoc, type, arg, commands); break; - }; + } /* Delete the tempory new association. */ sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); @@ -3035,7 +3035,7 @@ sctp_disposition_t sctp_sf_do_9_2_final( * notification is passed to the upper layer. */ ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP, - 0, 0, 0, GFP_ATOMIC); + 0, 0, 0, NULL, GFP_ATOMIC); if (!ev) goto nomem; @@ -3115,7 +3115,7 @@ sctp_disposition_t sctp_sf_ootb(const st break; ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); - if (ch_end > skb->tail) + if (ch_end > skb_tail_pointer(skb)) break; if (SCTP_CID_SHUTDOWN_ACK == ch->type) @@ -3130,7 +3130,7 @@ sctp_disposition_t sctp_sf_ootb(const st return sctp_sf_pdiscard(ep, asoc, type, arg, commands); ch = (sctp_chunkhdr_t *) ch_end; - } while (ch_end < skb->tail); + } while (ch_end < skb_tail_pointer(skb)); if (ootb_shut_ack) sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); @@ -4816,7 +4816,7 @@ sctp_disposition_t sctp_sf_t2_timer_expi default: BUG(); break; - }; + } if (!reply) goto nomem; @@ -5286,7 +5286,7 @@ static int sctp_eat_data(const struct sc chunk->ecn_ce_done = 1; af = sctp_get_af_specific( - ipver2af(chunk->skb->nh.iph->version)); + ipver2af(ip_hdr(chunk->skb)->version)); if (af && af->is_ce(chunk->skb) && asoc->peer.ecn_capable) { /* Do real work as sideffect. */ diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 5e54b17..523071c 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -101,7 +101,7 @@ const sctp_sm_table_entry_t *sctp_sm_loo default: /* Yikes! We got an illegal event type. */ return &bug; - }; + } } #define TYPE_SCTP_FUNC(func) {.fn = func, .name = #func} diff --git a/net/sctp/socket.c b/net/sctp/socket.c index a1d026f..2fc0a92 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -941,7 +941,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(st default: err = -EINVAL; break; - }; + } out: kfree(kaddrs); @@ -2039,6 +2039,10 @@ static int sctp_setsockopt_autoclose(str * SPP_HB_DEMAND - Request a user initiated heartbeat * to be made immediately. * + * SPP_HB_TIME_IS_ZERO - Specify's that the time for + * heartbeat delayis to be set to the value of 0 + * milliseconds. + * * SPP_PMTUD_ENABLE - This field will enable PMTU * discovery upon the specified address. Note that * if the address feild is empty then all addresses @@ -2081,13 +2085,30 @@ static int sctp_apply_peer_addr_params(s return error; } - if (params->spp_hbinterval) { - if (trans) { - trans->hbinterval = msecs_to_jiffies(params->spp_hbinterval); - } else if (asoc) { - asoc->hbinterval = msecs_to_jiffies(params->spp_hbinterval); - } else { - sp->hbinterval = params->spp_hbinterval; + /* Note that unless the spp_flag is set to SPP_HB_ENABLE the value of + * this field is ignored. Note also that a value of zero indicates + * the current setting should be left unchanged. + */ + if (params->spp_flags & SPP_HB_ENABLE) { + + /* Re-zero the interval if the SPP_HB_TIME_IS_ZERO is + * set. This lets us use 0 value when this flag + * is set. + */ + if (params->spp_flags & SPP_HB_TIME_IS_ZERO) + params->spp_hbinterval = 0; + + if (params->spp_hbinterval || + (params->spp_flags & SPP_HB_TIME_IS_ZERO)) { + if (trans) { + trans->hbinterval = + msecs_to_jiffies(params->spp_hbinterval); + } else if (asoc) { + asoc->hbinterval = + msecs_to_jiffies(params->spp_hbinterval); + } else { + sp->hbinterval = params->spp_hbinterval; + } } } @@ -2104,7 +2125,12 @@ static int sctp_apply_peer_addr_params(s } } - if (params->spp_pathmtu) { + /* When Path MTU discovery is disabled the value specified here will + * be the "fixed" path mtu (i.e. the value of the spp_flags field must + * include the flag SPP_PMTUD_DISABLE for this field to have any + * effect). + */ + if ((params->spp_flags & SPP_PMTUD_DISABLE) && params->spp_pathmtu) { if (trans) { trans->pathmtu = params->spp_pathmtu; sctp_assoc_sync_pmtu(asoc); @@ -2135,7 +2161,11 @@ static int sctp_apply_peer_addr_params(s } } - if (params->spp_sackdelay) { + /* Note that unless the spp_flag is set to SPP_SACKDELAY_ENABLE the + * value of this field is ignored. Note also that a value of zero + * indicates the current setting should be left unchanged. + */ + if ((params->spp_flags & SPP_SACKDELAY_ENABLE) && params->spp_sackdelay) { if (trans) { trans->sackdelay = msecs_to_jiffies(params->spp_sackdelay); @@ -2163,7 +2193,11 @@ static int sctp_apply_peer_addr_params(s } } - if (params->spp_pathmaxrxt) { + /* Note that unless the spp_flag is set to SPP_PMTUD_ENABLE the value + * of this field is ignored. Note also that a value of zero + * indicates the current setting should be left unchanged. + */ + if ((params->spp_flags & SPP_PMTUD_ENABLE) && params->spp_pathmaxrxt) { if (trans) { trans->pathmaxrxt = params->spp_pathmaxrxt; } else if (asoc) { @@ -2255,7 +2289,7 @@ static int sctp_setsockopt_peer_addr_par return 0; } -/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) +/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) * * This options will get or set the delayed ack timer. The time is set * in milliseconds. If the assoc_id is 0, then this sets or gets the @@ -2792,6 +2826,102 @@ static int sctp_setsockopt_context(struc return 0; } +/* + * 7.1.24. Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE) + * + * This options will at a minimum specify if the implementation is doing + * fragmented interleave. Fragmented interleave, for a one to many + * socket, is when subsequent calls to receive a message may return + * parts of messages from different associations. Some implementations + * may allow you to turn this value on or off. If so, when turned off, + * no fragment interleave will occur (which will cause a head of line + * blocking amongst multiple associations sharing the same one to many + * socket). When this option is turned on, then each receive call may + * come from a different association (thus the user must receive data + * with the extended calls (e.g. sctp_recvmsg) to keep track of which + * association each receive belongs to. + * + * This option takes a boolean value. A non-zero value indicates that + * fragmented interleave is on. A value of zero indicates that + * fragmented interleave is off. + * + * Note that it is important that an implementation that allows this + * option to be turned on, have it off by default. Otherwise an unaware + * application using the one to many model may become confused and act + * incorrectly. + */ +static int sctp_setsockopt_fragment_interleave(struct sock *sk, + char __user *optval, + int optlen) +{ + int val; + + if (optlen != sizeof(int)) + return -EINVAL; + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + sctp_sk(sk)->frag_interleave = (val == 0) ? 0 : 1; + + return 0; +} + +/* + * 7.1.25. Set or Get the sctp partial delivery point + * (SCTP_PARTIAL_DELIVERY_POINT) + * This option will set or get the SCTP partial delivery point. This + * point is the size of a message where the partial delivery API will be + * invoked to help free up rwnd space for the peer. Setting this to a + * lower value will cause partial delivery's to happen more often. The + * calls argument is an integer that sets or gets the partial delivery + * point. + */ +static int sctp_setsockopt_partial_delivery_point(struct sock *sk, + char __user *optval, + int optlen) +{ + u32 val; + + if (optlen != sizeof(u32)) + return -EINVAL; + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + sctp_sk(sk)->pd_point = val; + + return 0; /* is this the right error code? */ +} + +/* + * 7.1.28. Set or Get the maximum burst (SCTP_MAX_BURST) + * + * This option will allow a user to change the maximum burst of packets + * that can be emitted by this association. Note that the default value + * is 4, and some implementations may restrict this setting so that it + * can only be lowered. + * + * NOTE: This text doesn't seem right. Do this on a socket basis with + * future associations inheriting the socket value. + */ +static int sctp_setsockopt_maxburst(struct sock *sk, + char __user *optval, + int optlen) +{ + int val; + + if (optlen != sizeof(int)) + return -EINVAL; + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + if (val < 0) + return -EINVAL; + + sctp_sk(sk)->max_burst = val; + + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -2871,6 +3001,9 @@ SCTP_STATIC int sctp_setsockopt(struct s case SCTP_DELAYED_ACK_TIME: retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen); break; + case SCTP_PARTIAL_DELIVERY_POINT: + retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen); + break; case SCTP_INITMSG: retval = sctp_setsockopt_initmsg(sk, optval, optlen); @@ -2906,11 +3039,16 @@ SCTP_STATIC int sctp_setsockopt(struct s case SCTP_CONTEXT: retval = sctp_setsockopt_context(sk, optval, optlen); break; - + case SCTP_FRAGMENT_INTERLEAVE: + retval = sctp_setsockopt_fragment_interleave(sk, optval, optlen); + break; + case SCTP_MAX_BURST: + retval = sctp_setsockopt_maxburst(sk, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; - }; + } sctp_release_sock(sk); @@ -3066,6 +3204,7 @@ SCTP_STATIC int sctp_init_sock(struct so sp->default_timetolive = 0; sp->default_rcv_context = 0; + sp->max_burst = sctp_max_burst; /* Initialize default setup parameters. These parameters * can be modified with the SCTP_INITMSG socket option or @@ -3134,8 +3273,9 @@ SCTP_STATIC int sctp_init_sock(struct so sp->pf = sctp_get_pf_specific(sk->sk_family); /* Control variables for partial data delivery. */ - sp->pd_mode = 0; + atomic_set(&sp->pd_mode, 0); skb_queue_head_init(&sp->pd_lobby); + sp->frag_interleave = 0; /* Create a per socket endpoint structure. Even if we * change the data structure relationships, this may still @@ -3642,7 +3782,7 @@ static int sctp_getsockopt_peer_addr_par return 0; } -/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) +/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) * * This options will get or set the delayed ack timer. The time is set * in milliseconds. If the assoc_id is 0, then this sets or gets the @@ -3847,7 +3987,7 @@ static int sctp_getsockopt_peer_addrs(st memcpy(&temp, &from->ipaddr, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len; - if(space_left < addrlen) + if (space_left < addrlen) return -ENOMEM; if (copy_to_user(to, &temp, addrlen)) return -EFAULT; @@ -3936,8 +4076,9 @@ done: /* Helper function that copies local addresses to user and returns the number * of addresses copied. */ -static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, - void __user *to) +static int sctp_copy_laddrs_old(struct sock *sk, __u16 port, + int max_addrs, void *to, + int *bytes_copied) { struct list_head *pos, *next; struct sctp_sockaddr_entry *addr; @@ -3954,10 +4095,10 @@ static int sctp_copy_laddrs_to_user_old( sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if (copy_to_user(to, &temp, addrlen)) - return -EFAULT; + memcpy(to, &temp, addrlen); to += addrlen; + *bytes_copied += addrlen; cnt ++; if (cnt >= max_addrs) break; } @@ -3965,8 +4106,8 @@ static int sctp_copy_laddrs_to_user_old( return cnt; } -static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, - void __user **to, size_t space_left) +static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, + size_t space_left, int *bytes_copied) { struct list_head *pos, *next; struct sctp_sockaddr_entry *addr; @@ -3983,14 +4124,14 @@ static int sctp_copy_laddrs_to_user(stru sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if(space_leftaddress_list.next, struct sctp_sockaddr_entry, list); if (sctp_is_any(&addr->a)) { - cnt = sctp_copy_laddrs_to_user_old(sk, bp->port, - getaddrs.addr_num, - to); - if (cnt < 0) { - err = cnt; - goto unlock; - } + cnt = sctp_copy_laddrs_old(sk, bp->port, + getaddrs.addr_num, + addrs, &bytes_copied); goto copy_getaddrs; } } @@ -4066,22 +4214,29 @@ static int sctp_getsockopt_local_addrs_o memcpy(&temp, &addr->a, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if (copy_to_user(to, &temp, addrlen)) { - err = -EFAULT; - goto unlock; - } + memcpy(addrs, &temp, addrlen); to += addrlen; + bytes_copied += addrlen; cnt ++; if (cnt >= getaddrs.addr_num) break; } copy_getaddrs: + sctp_read_unlock(addr_lock); + + /* copy the entire address list into the user provided space */ + if (copy_to_user(to, addrs, bytes_copied)) { + err = -EFAULT; + goto error; + } + + /* copy the leading structure back to user */ getaddrs.addr_num = cnt; if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) err = -EFAULT; -unlock: - sctp_read_unlock(addr_lock); +error: + kfree(addrs); return err; } @@ -4101,7 +4256,8 @@ static int sctp_getsockopt_local_addrs(s rwlock_t *addr_lock; int err = 0; size_t space_left; - int bytes_copied; + int bytes_copied = 0; + void *addrs; if (len <= sizeof(struct sctp_getaddrs)) return -EINVAL; @@ -4129,6 +4285,9 @@ static int sctp_getsockopt_local_addrs(s to = optval + offsetof(struct sctp_getaddrs,addrs); space_left = len - sizeof(struct sctp_getaddrs) - offsetof(struct sctp_getaddrs,addrs); + addrs = kmalloc(space_left, GFP_KERNEL); + if (!addrs) + return -ENOMEM; sctp_read_lock(addr_lock); @@ -4139,11 +4298,11 @@ static int sctp_getsockopt_local_addrs(s addr = list_entry(bp->address_list.next, struct sctp_sockaddr_entry, list); if (sctp_is_any(&addr->a)) { - cnt = sctp_copy_laddrs_to_user(sk, bp->port, - &to, space_left); + cnt = sctp_copy_laddrs(sk, bp->port, addrs, + space_left, &bytes_copied); if (cnt < 0) { err = cnt; - goto unlock; + goto error; } goto copy_getaddrs; } @@ -4154,26 +4313,31 @@ static int sctp_getsockopt_local_addrs(s memcpy(&temp, &addr->a, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if(space_left < addrlen) - return -ENOMEM; /*fixme: right error?*/ - if (copy_to_user(to, &temp, addrlen)) { - err = -EFAULT; - goto unlock; + if (space_left < addrlen) { + err = -ENOMEM; /*fixme: right error?*/ + goto error; } + memcpy(addrs, &temp, addrlen); to += addrlen; + bytes_copied += addrlen; cnt ++; space_left -= addrlen; } copy_getaddrs: + sctp_read_unlock(addr_lock); + + if (copy_to_user(to, addrs, bytes_copied)) { + err = -EFAULT; + goto error; + } if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) return -EFAULT; - bytes_copied = ((char __user *)to) - optval; if (put_user(bytes_copied, optlen)) return -EFAULT; -unlock: - sctp_read_unlock(addr_lock); +error: + kfree(addrs); return err; } @@ -4536,6 +4700,77 @@ static int sctp_getsockopt_maxseg(struct return 0; } +/* + * 7.1.24. Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE) + * (chapter and verse is quoted at sctp_setsockopt_fragment_interleave()) + */ +static int sctp_getsockopt_fragment_interleave(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + int val; + + if (len < sizeof(int)) + return -EINVAL; + + len = sizeof(int); + + val = sctp_sk(sk)->frag_interleave; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + + return 0; +} + +/* + * 7.1.25. Set or Get the sctp partial delivery point + * (chapter and verse is quoted at sctp_setsockopt_partial_delivery_point()) + */ +static int sctp_getsockopt_partial_delivery_point(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + u32 val; + + if (len < sizeof(u32)) + return -EINVAL; + + len = sizeof(u32); + + val = sctp_sk(sk)->pd_point; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + + return -ENOTSUPP; +} + +/* + * 7.1.28. Set or Get the maximum burst (SCTP_MAX_BURST) + * (chapter and verse is quoted at sctp_setsockopt_maxburst()) + */ +static int sctp_getsockopt_maxburst(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + int val; + + if (len < sizeof(int)) + return -EINVAL; + + len = sizeof(int); + + val = sctp_sk(sk)->max_burst; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + + return -ENOTSUPP; +} + SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -4648,10 +4883,21 @@ SCTP_STATIC int sctp_getsockopt(struct s case SCTP_CONTEXT: retval = sctp_getsockopt_context(sk, len, optval, optlen); break; + case SCTP_FRAGMENT_INTERLEAVE: + retval = sctp_getsockopt_fragment_interleave(sk, len, optval, + optlen); + break; + case SCTP_PARTIAL_DELIVERY_POINT: + retval = sctp_getsockopt_partial_delivery_point(sk, len, optval, + optlen); + break; + case SCTP_MAX_BURST: + retval = sctp_getsockopt_maxburst(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; - }; + } sctp_release_sock(sk); return retval; @@ -4976,7 +5222,8 @@ int sctp_inet_listen(struct socket *sock break; default: break; - }; + } + if (err) goto cleanup; @@ -5239,7 +5486,7 @@ SCTP_STATIC int sctp_msghdr_parse(const default: return -EINVAL; - }; + } } return 0; } @@ -5742,9 +5989,9 @@ static void sctp_sock_migrate(struct soc * 3) Peeling off non-partial delivery; move pd_lobby to receive_queue. */ skb_queue_head_init(&newsp->pd_lobby); - sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode; + atomic_set(&sctp_sk(newsk)->pd_mode, assoc->ulpq.pd_mode); - if (sctp_sk(oldsk)->pd_mode) { + if (atomic_read(&sctp_sk(oldsk)->pd_mode)) { struct sk_buff_head *queue; /* Decide which queue to move pd_lobby skbs to. */ @@ -5770,7 +6017,7 @@ static void sctp_sock_migrate(struct soc * delivery to finish. */ if (assoc->ulpq.pd_mode) - sctp_clear_pd(oldsk); + sctp_clear_pd(oldsk, NULL); } diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 4d8c2ab..961df27 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -507,7 +507,7 @@ void sctp_transport_lower_cwnd(struct sc transport->cwnd = max(transport->cwnd/2, 4*transport->asoc->pathmtu); break; - }; + } transport->partial_bytes_acked = 0; SCTP_DEBUG_PRINTK("%s: transport: %p reason: %d cwnd: " diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 2e11bc8..661ea2d 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -131,19 +131,54 @@ static inline void sctp_ulpevent_release struct sctp_ulpevent *sctp_ulpevent_make_assoc_change( const struct sctp_association *asoc, __u16 flags, __u16 state, __u16 error, __u16 outbound, - __u16 inbound, gfp_t gfp) + __u16 inbound, struct sctp_chunk *chunk, gfp_t gfp) { struct sctp_ulpevent *event; struct sctp_assoc_change *sac; struct sk_buff *skb; - event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change), + /* If the lower layer passed in the chunk, it will be + * an ABORT, so we need to include it in the sac_info. + */ + if (chunk) { + /* sctp_inqu_pop() has allready pulled off the chunk + * header. We need to put it back temporarily + */ + skb_push(chunk->skb, sizeof(sctp_chunkhdr_t)); + + /* Copy the chunk data to a new skb and reserve enough + * head room to use as notification. + */ + skb = skb_copy_expand(chunk->skb, + sizeof(struct sctp_assoc_change), 0, gfp); + + if (!skb) + goto fail; + + /* put back the chunk header now that we have a copy */ + skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); + + /* Embed the event fields inside the cloned skb. */ + event = sctp_skb2event(skb); + sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize); + + /* Include the notification structure */ + sac = (struct sctp_assoc_change *) + skb_push(skb, sizeof(struct sctp_assoc_change)); + + /* Trim the buffer to the right length. */ + skb_trim(skb, sizeof(struct sctp_assoc_change) + + ntohs(chunk->chunk_hdr->length)); + } else { + event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change), MSG_NOTIFICATION, gfp); - if (!event) - goto fail; - skb = sctp_event2skb(event); - sac = (struct sctp_assoc_change *) - skb_put(skb, sizeof(struct sctp_assoc_change)); + if (!event) + goto fail; + + skb = sctp_event2skb(event); + sac = (struct sctp_assoc_change *) skb_put(skb, + sizeof(struct sctp_assoc_change)); + } /* Socket Extensions for SCTP * 5.3.1.1 SCTP_ASSOC_CHANGE diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index b29e3e4..34eb977 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -138,26 +138,59 @@ int sctp_ulpq_tail_data(struct sctp_ulpq /* Clear the partial delivery mode for this socket. Note: This * assumes that no association is currently in partial delivery mode. */ -int sctp_clear_pd(struct sock *sk) +int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc) { struct sctp_sock *sp = sctp_sk(sk); - sp->pd_mode = 0; - if (!skb_queue_empty(&sp->pd_lobby)) { - struct list_head *list; - sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue); - list = (struct list_head *)&sctp_sk(sk)->pd_lobby; - INIT_LIST_HEAD(list); - return 1; + if (atomic_dec_and_test(&sp->pd_mode)) { + /* This means there are no other associations in PD, so + * we can go ahead and clear out the lobby in one shot + */ + if (!skb_queue_empty(&sp->pd_lobby)) { + struct list_head *list; + sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue); + list = (struct list_head *)&sctp_sk(sk)->pd_lobby; + INIT_LIST_HEAD(list); + return 1; + } + } else { + /* There are other associations in PD, so we only need to + * pull stuff out of the lobby that belongs to the + * associations that is exiting PD (all of its notifications + * are posted here). + */ + if (!skb_queue_empty(&sp->pd_lobby) && asoc) { + struct sk_buff *skb, *tmp; + struct sctp_ulpevent *event; + + sctp_skb_for_each(skb, &sp->pd_lobby, tmp) { + event = sctp_skb2event(skb); + if (event->asoc == asoc) { + __skb_unlink(skb, &sp->pd_lobby); + __skb_queue_tail(&sk->sk_receive_queue, + skb); + } + } + } } + return 0; } +/* Set the pd_mode on the socket and ulpq */ +static void sctp_ulpq_set_pd(struct sctp_ulpq *ulpq) +{ + struct sctp_sock *sp = sctp_sk(ulpq->asoc->base.sk); + + atomic_inc(&sp->pd_mode); + ulpq->pd_mode = 1; +} + /* Clear the pd_mode and restart any pending messages waiting for delivery. */ static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq) { ulpq->pd_mode = 0; - return sctp_clear_pd(ulpq->asoc->base.sk); + return sctp_clear_pd(ulpq->asoc->base.sk, ulpq->asoc); } /* If the SKB of 'event' is on a list, it is the first such member @@ -187,25 +220,35 @@ int sctp_ulpq_tail_event(struct sctp_ulp * the association the cause of the partial delivery. */ - if (!sctp_sk(sk)->pd_mode) { + if (atomic_read(&sctp_sk(sk)->pd_mode) == 0) { queue = &sk->sk_receive_queue; - } else if (ulpq->pd_mode) { - /* If the association is in partial delivery, we - * need to finish delivering the partially processed - * packet before passing any other data. This is - * because we don't truly support stream interleaving. - */ - if ((event->msg_flags & MSG_NOTIFICATION) || - (SCTP_DATA_NOT_FRAG == - (event->msg_flags & SCTP_DATA_FRAG_MASK))) - queue = &sctp_sk(sk)->pd_lobby; - else { - clear_pd = event->msg_flags & MSG_EOR; - queue = &sk->sk_receive_queue; + } else { + if (ulpq->pd_mode) { + /* If the association is in partial delivery, we + * need to finish delivering the partially processed + * packet before passing any other data. This is + * because we don't truly support stream interleaving. + */ + if ((event->msg_flags & MSG_NOTIFICATION) || + (SCTP_DATA_NOT_FRAG == + (event->msg_flags & SCTP_DATA_FRAG_MASK))) + queue = &sctp_sk(sk)->pd_lobby; + else { + clear_pd = event->msg_flags & MSG_EOR; + queue = &sk->sk_receive_queue; + } + } else { + /* + * If fragment interleave is enabled, we + * can queue this to the recieve queue instead + * of the lobby. + */ + if (sctp_sk(sk)->frag_interleave) + queue = &sk->sk_receive_queue; + else + queue = &sctp_sk(sk)->pd_lobby; } - } else - queue = &sctp_sk(sk)->pd_lobby; - + } /* If we are harvesting multiple skbs they will be * collected on a list. @@ -348,7 +391,7 @@ static struct sctp_ulpevent *sctp_make_r break; pos->next = pnext; pos = pnext; - }; + } event = sctp_skb2event(f_frag); SCTP_INC_STATS(SCTP_MIB_REASMUSRMSGS); @@ -367,6 +410,11 @@ static inline struct sctp_ulpevent *sctp struct sk_buff *first_frag = NULL; __u32 ctsn, next_tsn; struct sctp_ulpevent *retval = NULL; + struct sk_buff *pd_first = NULL; + struct sk_buff *pd_last = NULL; + size_t pd_len = 0; + struct sctp_association *asoc; + u32 pd_point; /* Initialized to 0 just to avoid compiler warning message. Will * never be used with this value. It is referenced only after it @@ -382,6 +430,10 @@ static inline struct sctp_ulpevent *sctp * we expect to find the remaining middle fragments and the last * fragment in order. If not, first_frag is reset to NULL and we * start the next pass when we find another first fragment. + * + * There is a potential to do partial delivery if user sets + * SCTP_PARTIAL_DELIVERY_POINT option. Lets count some things here + * to see if can do PD. */ skb_queue_walk(&ulpq->reasm, pos) { cevent = sctp_skb2event(pos); @@ -389,14 +441,32 @@ static inline struct sctp_ulpevent *sctp switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) { case SCTP_DATA_FIRST_FRAG: + /* If this "FIRST_FRAG" is the first + * element in the queue, then count it towards + * possible PD. + */ + if (pos == ulpq->reasm.next) { + pd_first = pos; + pd_last = pos; + pd_len = pos->len; + } else { + pd_first = NULL; + pd_last = NULL; + pd_len = 0; + } + first_frag = pos; next_tsn = ctsn + 1; break; case SCTP_DATA_MIDDLE_FRAG: - if ((first_frag) && (ctsn == next_tsn)) + if ((first_frag) && (ctsn == next_tsn)) { next_tsn++; - else + if (pd_first) { + pd_last = pos; + pd_len += pos->len; + } + } else first_frag = NULL; break; @@ -406,8 +476,29 @@ static inline struct sctp_ulpevent *sctp else first_frag = NULL; break; - }; + } + } + asoc = ulpq->asoc; + if (pd_first) { + /* Make sure we can enter partial deliver. + * We can trigger partial delivery only if framgent + * interleave is set, or the socket is not already + * in partial delivery. + */ + if (!sctp_sk(asoc->base.sk)->frag_interleave && + atomic_read(&sctp_sk(asoc->base.sk)->pd_mode)) + goto done; + + cevent = sctp_skb2event(pd_first); + pd_point = sctp_sk(asoc->base.sk)->pd_point; + if (pd_point && pd_point <= pd_len) { + retval = sctp_make_reassembled_event(&ulpq->reasm, + pd_first, + pd_last); + if (retval) + sctp_ulpq_set_pd(ulpq); + } } done: return retval; @@ -465,7 +556,7 @@ static inline struct sctp_ulpevent *sctp goto done; default: return NULL; - }; + } } /* We have the reassembled event. There is no need to look @@ -557,7 +648,7 @@ static inline struct sctp_ulpevent *sctp break; default: return NULL; - }; + } } /* We have the reassembled event. There is no need to look @@ -826,19 +917,29 @@ void sctp_ulpq_partial_delivery(struct s { struct sctp_ulpevent *event; struct sctp_association *asoc; + struct sctp_sock *sp; asoc = ulpq->asoc; + sp = sctp_sk(asoc->base.sk); - /* Are we already in partial delivery mode? */ - if (!sctp_sk(asoc->base.sk)->pd_mode) { + /* If the association is already in Partial Delivery mode + * we have noting to do. + */ + if (ulpq->pd_mode) + return; + /* If the user enabled fragment interleave socket option, + * multiple associations can enter partial delivery. + * Otherwise, we can only enter partial delivery if the + * socket is not in partial deliver mode. + */ + if (sp->frag_interleave || atomic_read(&sp->pd_mode) == 0) { /* Is partial delivery possible? */ event = sctp_ulpq_retrieve_first(ulpq); /* Send event to the ULP. */ if (event) { sctp_ulpq_tail_event(ulpq, event); - sctp_sk(asoc->base.sk)->pd_mode = 1; - ulpq->pd_mode = 1; + sctp_ulpq_set_pd(ulpq); return; } } diff --git a/net/socket.c b/net/socket.c index ea8f81a..1ad62c0 100644 --- a/net/socket.c +++ b/net/socket.c @@ -585,6 +585,37 @@ int kernel_sendmsg(struct socket *sock, return result; } +/* + * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP) + */ +void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb) +{ + ktime_t kt = skb->tstamp; + + if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) { + struct timeval tv; + /* Race occurred between timestamp enabling and packet + receiving. Fill in the current time for now. */ + if (kt.tv64 == 0) + kt = ktime_get_real(); + skb->tstamp = kt; + tv = ktime_to_timeval(kt); + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, sizeof(tv), &tv); + } else { + struct timespec ts; + /* Race occurred between timestamp enabling and packet + receiving. Fill in the current time for now. */ + if (kt.tv64 == 0) + kt = ktime_get_real(); + skb->tstamp = kt; + ts = ktime_to_timespec(kt); + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof(ts), &ts); + } +} + +EXPORT_SYMBOL_GPL(__sock_recv_timestamp); + static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { @@ -1292,7 +1323,7 @@ asmlinkage long sys_bind(int fd, struct int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); - if(sock) { + if (sock) { err = move_addr_to_kernel(umyaddr, addrlen, address); if (err >= 0) { err = security_socket_bind(sock, diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index f02f24a..543b085 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1237,20 +1237,12 @@ static int content_open(struct inode *in return res; } -static int content_release(struct inode *inode, struct file *file) -{ - struct seq_file *m = (struct seq_file *)file->private_data; - struct handle *han = m->private; - kfree(han); - m->private = NULL; - return seq_release(inode, file); -} static const struct file_operations content_file_operations = { .open = content_open, .read = seq_read, .llseek = seq_lseek, - .release = content_release, + .release = seq_release_private, }; static ssize_t read_flush(struct file *file, char __user *buf, diff --git a/net/sunrpc/socklib.c b/net/sunrpc/socklib.c index 634885b..1d377d1 100644 --- a/net/sunrpc/socklib.c +++ b/net/sunrpc/socklib.c @@ -154,7 +154,7 @@ int csum_partial_copy_to_xdr(struct xdr_ desc.offset = sizeof(struct udphdr); desc.count = skb->len - desc.offset; - if (skb->ip_summed == CHECKSUM_UNNECESSARY) + if (skb_csum_unnecessary(skb)) goto no_checksum; desc.csum = csum_partial(skb->data, desc.offset, skb->csum); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 2772fee..22f61ae 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -798,16 +798,12 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) dprintk("svc: recvfrom returned error %d\n", -err); } rqstp->rq_addrlen = sizeof(rqstp->rq_addr); - if (skb->tstamp.off_sec == 0) { - struct timeval tv; - - tv.tv_sec = xtime.tv_sec; - tv.tv_usec = xtime.tv_nsec / NSEC_PER_USEC; - skb_set_timestamp(skb, &tv); + if (skb->tstamp.tv64 == 0) { + skb->tstamp = ktime_get_real(); /* Don't enable netstamp, sunrpc doesn't need that much accuracy */ } - skb_get_timestamp(skb, &svsk->sk_sk->sk_stamp); + svsk->sk_sk->sk_stamp = skb->tstamp; set_bit(SK_DATA, &svsk->sk_flags); /* there may be more data... */ /* diff --git a/net/tipc/config.c b/net/tipc/config.c index 14789a8..c71337a 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -89,7 +89,7 @@ struct sk_buff *tipc_cfg_reply_alloc(int int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type, void *tlv_data, int tlv_data_size) { - struct tlv_desc *tlv = (struct tlv_desc *)buf->tail; + struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(buf); int new_tlv_space = TLV_SPACE(tlv_data_size); if (skb_tailroom(buf) < new_tlv_space) { diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c index 9be4839..67bb29b 100644 --- a/net/tipc/eth_media.c +++ b/net/tipc/eth_media.c @@ -73,7 +73,7 @@ static int send_msg(struct sk_buff *buf, clone = skb_clone(buf, GFP_ATOMIC); if (clone) { - clone->nh.raw = clone->data; + skb_reset_network_header(clone); dev = ((struct eth_bearer *)(tb_ptr->usr_handle))->dev; clone->dev = dev; dev->hard_header(clone, dev, ETH_P_TIPC, @@ -99,8 +99,8 @@ static int recv_msg(struct sk_buff *buf, if (likely(eb_ptr->bearer)) { if (likely(!dev->promiscuity) || - !memcmp(buf->mac.raw,dev->dev_addr,ETH_ALEN) || - !memcmp(buf->mac.raw,dev->broadcast,ETH_ALEN)) { + !memcmp(skb_mac_header(buf), dev->dev_addr, ETH_ALEN) || + !memcmp(skb_mac_header(buf), dev->broadcast, ETH_ALEN)) { size = msg_size((struct tipc_msg *)buf->data); skb_trim(buf, size); if (likely(buf->len == size)) { @@ -140,7 +140,7 @@ static int enable_bearer(struct tipc_bea return -EDQUOT; if (!eb_ptr->dev) { eb_ptr->dev = dev; - eb_ptr->tipc_packet_type.type = __constant_htons(ETH_P_TIPC); + eb_ptr->tipc_packet_type.type = htons(ETH_P_TIPC); eb_ptr->tipc_packet_type.dev = dev; eb_ptr->tipc_packet_type.func = recv_msg; eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr; diff --git a/net/tipc/link.c b/net/tipc/link.c index 71c2f2f..2124f32 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1001,7 +1001,7 @@ static int link_bundle_buf(struct link * return 0; skb_put(bundler, pad + size); - memcpy(bundler->data + to_pos, buf->data, size); + skb_copy_to_linear_data_offset(bundler, to_pos, buf->data, size); msg_set_size(bundler_msg, to_pos + size); msg_set_msgcnt(bundler_msg, msg_msgcnt(bundler_msg) + 1); dbg("Packed msg # %u(%u octets) into pos %u in buf(#%u)\n", @@ -1109,8 +1109,8 @@ int tipc_link_send_buf(struct link *l_pt if (bundler) { msg_init(&bundler_hdr, MSG_BUNDLER, OPEN_MSG, TIPC_OK, INT_H_SIZE, l_ptr->addr); - memcpy(bundler->data, (unchar *)&bundler_hdr, - INT_H_SIZE); + skb_copy_to_linear_data(bundler, &bundler_hdr, + INT_H_SIZE); skb_trim(bundler, INT_H_SIZE); link_bundle_buf(l_ptr, bundler, buf); buf = bundler; @@ -1383,9 +1383,9 @@ again: if (!buf) return -ENOMEM; buf->next = NULL; - memcpy(buf->data, (unchar *)&fragm_hdr, INT_H_SIZE); + skb_copy_to_linear_data(buf, &fragm_hdr, INT_H_SIZE); hsz = msg_hdr_sz(hdr); - memcpy(buf->data + INT_H_SIZE, (unchar *)hdr, hsz); + skb_copy_to_linear_data_offset(buf, INT_H_SIZE, hdr, hsz); msg_dbg(buf_msg(buf), ">BUILD>"); /* Chop up message: */ @@ -1416,8 +1416,8 @@ error: return -EFAULT; } } else - memcpy(buf->data + fragm_crs, sect_crs, sz); - + skb_copy_to_linear_data_offset(buf, fragm_crs, + sect_crs, sz); sect_crs += sz; sect_rest -= sz; fragm_crs += sz; @@ -1442,7 +1442,7 @@ error: buf->next = NULL; prev->next = buf; - memcpy(buf->data, (unchar *)&fragm_hdr, INT_H_SIZE); + skb_copy_to_linear_data(buf, &fragm_hdr, INT_H_SIZE); fragm_crs = INT_H_SIZE; fragm_rest = fragm_sz; msg_dbg(buf_msg(buf)," >BUILD>"); @@ -2130,7 +2130,7 @@ void tipc_link_send_proto_msg(struct lin buf = l_ptr->proto_msg_queue; if (!buf) return; - memcpy(buf->data, (unchar *)msg, sizeof(l_ptr->proto_msg)); + skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg)); return; } msg_set_timestamp(msg, jiffies_to_msecs(jiffies)); @@ -2143,7 +2143,7 @@ void tipc_link_send_proto_msg(struct lin if (!buf) return; - memcpy(buf->data, (unchar *)msg, sizeof(l_ptr->proto_msg)); + skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg)); msg_set_size(buf_msg(buf), msg_size); if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) { @@ -2319,8 +2319,8 @@ void tipc_link_tunnel(struct link *l_ptr "unable to send tunnel msg\n"); return; } - memcpy(buf->data, (unchar *)tunnel_hdr, INT_H_SIZE); - memcpy(buf->data + INT_H_SIZE, (unchar *)msg, length); + skb_copy_to_linear_data(buf, tunnel_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(buf, INT_H_SIZE, msg, length); dbg("%c->%c:", l_ptr->b_ptr->net_plane, tunnel->b_ptr->net_plane); msg_dbg(buf_msg(buf), ">SEND>"); tipc_link_send_buf(tunnel, buf); @@ -2361,7 +2361,7 @@ void tipc_link_changeover(struct link *l buf = buf_acquire(INT_H_SIZE); if (buf) { - memcpy(buf->data, (unchar *)&tunnel_hdr, INT_H_SIZE); + skb_copy_to_linear_data(buf, &tunnel_hdr, INT_H_SIZE); msg_set_size(&tunnel_hdr, INT_H_SIZE); dbg("%c->%c:", l_ptr->b_ptr->net_plane, tunnel->b_ptr->net_plane); @@ -2426,8 +2426,9 @@ void tipc_link_send_duplicate(struct lin "unable to send duplicate msg\n"); return; } - memcpy(outbuf->data, (unchar *)&tunnel_hdr, INT_H_SIZE); - memcpy(outbuf->data + INT_H_SIZE, iter->data, length); + skb_copy_to_linear_data(outbuf, &tunnel_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(outbuf, INT_H_SIZE, iter->data, + length); dbg("%c->%c:", l_ptr->b_ptr->net_plane, tunnel->b_ptr->net_plane); msg_dbg(buf_msg(outbuf), ">SEND>"); @@ -2457,7 +2458,7 @@ static struct sk_buff *buf_extract(struc eb = buf_acquire(size); if (eb) - memcpy(eb->data, (unchar *)msg, size); + skb_copy_to_linear_data(eb, msg, size); return eb; } @@ -2569,7 +2570,7 @@ void tipc_link_recv_bundle(struct sk_buf if (obuf == NULL) { warn("Link unable to unbundle message(s)\n"); break; - }; + } pos += align(msg_size(buf_msg(obuf))); msg_dbg(buf_msg(obuf), " /"); tipc_net_route_msg(obuf); @@ -2631,9 +2632,9 @@ int tipc_link_send_long_buf(struct link goto exit; } msg_set_size(&fragm_hdr, fragm_sz + INT_H_SIZE); - memcpy(fragm->data, (unchar *)&fragm_hdr, INT_H_SIZE); - memcpy(fragm->data + INT_H_SIZE, crs, fragm_sz); - + skb_copy_to_linear_data(fragm, &fragm_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(fragm, INT_H_SIZE, crs, + fragm_sz); /* Send queued messages first, if any: */ l_ptr->stats.sent_fragments++; @@ -2733,8 +2734,8 @@ int tipc_link_recv_fragment(struct sk_bu if (pbuf != NULL) { pbuf->next = *pending; *pending = pbuf; - memcpy(pbuf->data, (unchar *)imsg, msg_data_sz(fragm)); - + skb_copy_to_linear_data(pbuf, imsg, + msg_data_sz(fragm)); /* Prepare buffer for subsequent fragments. */ set_long_msg_seqno(pbuf, long_msg_seq_no); @@ -2750,7 +2751,8 @@ int tipc_link_recv_fragment(struct sk_bu u32 fsz = get_fragm_size(pbuf); u32 crs = ((msg_fragm_no(fragm) - 1) * fsz); u32 exp_frags = get_expected_frags(pbuf) - 1; - memcpy(pbuf->data + crs, msg_data(fragm), dsz); + skb_copy_to_linear_data_offset(pbuf, crs, + msg_data(fragm), dsz); buf_discard(fbuf); /* Is message complete? */ diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 62d5490..35d5ba1 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -1,8 +1,8 @@ /* * net/tipc/msg.h: Include file for TIPC message header routines * - * Copyright (c) 2000-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2000-2007, Ericsson AB + * Copyright (c) 2005-2007, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -71,8 +71,11 @@ static inline void msg_set_word(struct t static inline void msg_set_bits(struct tipc_msg *m, u32 w, u32 pos, u32 mask, u32 val) { - u32 word = msg_word(m,w) & ~(mask << pos); - msg_set_word(m, w, (word |= (val << pos))); + val = (val & mask) << pos; + val = htonl(val); + mask = htonl(mask << pos); + m->hdr[w] &= ~mask; + m->hdr[w] |= val; } /* @@ -786,15 +789,16 @@ static inline int msg_build(struct tipc_ *buf = buf_acquire(sz); if (!(*buf)) return -ENOMEM; - memcpy((*buf)->data, (unchar *)hdr, hsz); + skb_copy_to_linear_data(*buf, hdr, hsz); for (res = 1, cnt = 0; res && (cnt < num_sect); cnt++) { if (likely(usrmem)) res = !copy_from_user((*buf)->data + pos, msg_sect[cnt].iov_base, msg_sect[cnt].iov_len); else - memcpy((*buf)->data + pos, msg_sect[cnt].iov_base, - msg_sect[cnt].iov_len); + skb_copy_to_linear_data_offset(*buf, pos, + msg_sect[cnt].iov_base, + msg_sect[cnt].iov_len); pos += msg_sect[cnt].iov_len; } if (likely(res)) diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index b8e1edc..4cdafa2 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -57,7 +57,7 @@ static int handle_cmd(struct sk_buff *sk if (rep_buf) { skb_push(rep_buf, hdr_space); - rep_nlh = (struct nlmsghdr *)rep_buf->data; + rep_nlh = nlmsg_hdr(rep_buf); memcpy(rep_nlh, req_nlh, hdr_space); rep_nlh->nlmsg_len = rep_buf->len; genlmsg_unicast(rep_buf, req_nlh->nlmsg_pid); diff --git a/net/tipc/port.c b/net/tipc/port.c index 5f8217d..bcd5da0 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -464,7 +464,7 @@ int tipc_reject_msg(struct sk_buff *buf, msg_set_size(rmsg, data_sz + hdr_sz); msg_set_nametype(rmsg, msg_nametype(msg)); msg_set_nameinst(rmsg, msg_nameinst(msg)); - memcpy(rbuf->data + hdr_sz, msg_data(msg), data_sz); + skb_copy_to_linear_data_offset(rbuf, hdr_sz, msg_data(msg), data_sz); /* send self-abort message when rejecting on a connected port */ if (msg_connected(msg)) { @@ -1419,7 +1419,7 @@ int tipc_send_buf(u32 ref, struct sk_buf return -ENOMEM; skb_push(buf, hsz); - memcpy(buf->data, (unchar *)msg, hsz); + skb_copy_to_linear_data(buf, msg, hsz); destnode = msg_destnode(msg); p_ptr->publ.congested = 1; if (!tipc_port_congested(p_ptr)) { @@ -1555,7 +1555,7 @@ int tipc_forward_buf2name(u32 ref, if (skb_cow(buf, LONG_H_SIZE)) return -ENOMEM; skb_push(buf, LONG_H_SIZE); - memcpy(buf->data, (unchar *)msg, LONG_H_SIZE); + skb_copy_to_linear_data(buf, msg, LONG_H_SIZE); msg_dbg(buf_msg(buf),"PREP:"); if (likely(destport || destnode)) { p_ptr->sent++; @@ -1679,7 +1679,7 @@ int tipc_forward_buf2port(u32 ref, return -ENOMEM; skb_push(buf, DIR_MSG_H_SIZE); - memcpy(buf->data, (unchar *)msg, DIR_MSG_H_SIZE); + skb_copy_to_linear_data(buf, msg, DIR_MSG_H_SIZE); msg_dbg(msg, "buf2port: "); p_ptr->sent++; if (dest->node == tipc_own_addr) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index b71739f..45832fb 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1020,7 +1020,7 @@ restart: if (!err) { buf_crs = (unsigned char *)(TIPC_SKB_CB(buf)->handle); - sz = buf->tail - buf_crs; + sz = skb_tail_pointer(buf) - buf_crs; needed = (buf_len - sz_copied); sz_to_copy = (sz <= needed) ? sz : needed; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 6069716..aec8cf1 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1319,7 +1319,7 @@ static int unix_dgram_sendmsg(struct kio unix_attach_fds(siocb->scm, skb); unix_get_secdata(siocb->scm, skb); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); if (err) goto out_free; diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c index 5d2d93d..7a19e0e 100644 --- a/net/wanrouter/wanmain.c +++ b/net/wanrouter/wanmain.c @@ -277,8 +277,8 @@ int wanrouter_encapsulate(struct sk_buff skb_push(skb, 7); skb->data[0] = 0; skb->data[1] = NLPID_SNAP; - memcpy(&skb->data[2], wanrouter_oui_ether, - sizeof(wanrouter_oui_ether)); + skb_copy_to_linear_data_offset(skb, 2, wanrouter_oui_ether, + sizeof(wanrouter_oui_ether)); *((unsigned short*)&skb->data[5]) = htons(type); break; @@ -339,7 +339,7 @@ __be16 wanrouter_type_trans(struct sk_bu skb->protocol = ethertype; skb->pkt_type = PACKET_HOST; /* Physically point to point */ skb_pull(skb, cnt); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); return ethertype; } diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig new file mode 100644 index 0000000..a228d56 --- /dev/null +++ b/net/wireless/Kconfig @@ -0,0 +1,16 @@ +config CFG80211 + tristate "Improved wireless configuration API" + +config WIRELESS_EXT + bool "Wireless extensions" + default n + ---help--- + This option enables the legacy wireless extensions + (wireless network interface configuration via ioctls.) + + Wireless extensions will be replaced by cfg80211 and + will be required only by legacy drivers that implement + wireless extension handlers. + + Say N (if you can) unless you know you need wireless + extensions for external modules. diff --git a/net/wireless/Makefile b/net/wireless/Makefile new file mode 100644 index 0000000..3a96ae6 --- /dev/null +++ b/net/wireless/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_WIRELESS_EXT) += wext.o +obj-$(CONFIG_CFG80211) += cfg80211.o + +cfg80211-y += core.o sysfs.o diff --git a/net/wireless/core.c b/net/wireless/core.c new file mode 100644 index 0000000..7eabd55 --- /dev/null +++ b/net/wireless/core.c @@ -0,0 +1,224 @@ +/* + * This is the linux wireless configuration interface. + * + * Copyright 2006, 2007 Johannes Berg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "sysfs.h" + +/* name for sysfs, %d is appended */ +#define PHY_NAME "phy" + +MODULE_AUTHOR("Johannes Berg"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("wireless configuration support"); + +/* RCU might be appropriate here since we usually + * only read the list, and that can happen quite + * often because we need to do it for each command */ +LIST_HEAD(cfg80211_drv_list); +DEFINE_MUTEX(cfg80211_drv_mutex); +static int wiphy_counter; + +/* for debugfs */ +static struct dentry *ieee80211_debugfs_dir; + +/* exported functions */ + +struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) +{ + struct cfg80211_registered_device *drv; + int alloc_size; + + alloc_size = sizeof(*drv) + sizeof_priv; + + drv = kzalloc(alloc_size, GFP_KERNEL); + if (!drv) + return NULL; + + drv->ops = ops; + + mutex_lock(&cfg80211_drv_mutex); + + drv->idx = wiphy_counter; + + /* now increase counter for the next device unless + * it has wrapped previously */ + if (wiphy_counter >= 0) + wiphy_counter++; + + mutex_unlock(&cfg80211_drv_mutex); + + if (unlikely(drv->idx < 0)) { + /* ugh, wrapped! */ + kfree(drv); + return NULL; + } + + /* give it a proper name */ + snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE, + PHY_NAME "%d", drv->idx); + + mutex_init(&drv->mtx); + mutex_init(&drv->devlist_mtx); + INIT_LIST_HEAD(&drv->netdev_list); + + device_initialize(&drv->wiphy.dev); + drv->wiphy.dev.class = &ieee80211_class; + drv->wiphy.dev.platform_data = drv; + + return &drv->wiphy; +} +EXPORT_SYMBOL(wiphy_new); + +int wiphy_register(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + int res; + + mutex_lock(&cfg80211_drv_mutex); + + res = device_add(&drv->wiphy.dev); + if (res) + goto out_unlock; + + list_add(&drv->list, &cfg80211_drv_list); + + /* add to debugfs */ + drv->wiphy.debugfsdir = + debugfs_create_dir(wiphy_name(&drv->wiphy), + ieee80211_debugfs_dir); + + res = 0; +out_unlock: + mutex_unlock(&cfg80211_drv_mutex); + return res; +} +EXPORT_SYMBOL(wiphy_register); + +void wiphy_unregister(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + + /* protect the device list */ + mutex_lock(&cfg80211_drv_mutex); + + BUG_ON(!list_empty(&drv->netdev_list)); + + /* + * Try to grab drv->mtx. If a command is still in progress, + * hopefully the driver will refuse it since it's tearing + * down the device already. We wait for this command to complete + * before unlinking the item from the list. + * Note: as codified by the BUG_ON above we cannot get here if + * a virtual interface is still associated. Hence, we can only + * get to lock contention here if userspace issues a command + * that identified the hardware by wiphy index. + */ + mutex_lock(&drv->mtx); + /* unlock again before freeing */ + mutex_unlock(&drv->mtx); + + list_del(&drv->list); + device_del(&drv->wiphy.dev); + debugfs_remove(drv->wiphy.debugfsdir); + + mutex_unlock(&cfg80211_drv_mutex); +} +EXPORT_SYMBOL(wiphy_unregister); + +void cfg80211_dev_free(struct cfg80211_registered_device *drv) +{ + mutex_destroy(&drv->mtx); + mutex_destroy(&drv->devlist_mtx); + kfree(drv); +} + +void wiphy_free(struct wiphy *wiphy) +{ + put_device(&wiphy->dev); +} +EXPORT_SYMBOL(wiphy_free); + +static int cfg80211_netdev_notifier_call(struct notifier_block * nb, + unsigned long state, + void *ndev) +{ + struct net_device *dev = ndev; + struct cfg80211_registered_device *rdev; + + if (!dev->ieee80211_ptr) + return 0; + + rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + + switch (state) { + case NETDEV_REGISTER: + mutex_lock(&rdev->devlist_mtx); + list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list); + if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, + "phy80211")) { + printk(KERN_ERR "wireless: failed to add phy80211 " + "symlink to netdev!\n"); + } + dev->ieee80211_ptr->netdev = dev; + mutex_unlock(&rdev->devlist_mtx); + break; + case NETDEV_UNREGISTER: + mutex_lock(&rdev->devlist_mtx); + if (!list_empty(&dev->ieee80211_ptr->list)) { + sysfs_remove_link(&dev->dev.kobj, "phy80211"); + list_del_init(&dev->ieee80211_ptr->list); + } + mutex_unlock(&rdev->devlist_mtx); + break; + } + + return 0; +} + +static struct notifier_block cfg80211_netdev_notifier = { + .notifier_call = cfg80211_netdev_notifier_call, +}; + +static int cfg80211_init(void) +{ + int err = wiphy_sysfs_init(); + if (err) + goto out_fail_sysfs; + + err = register_netdevice_notifier(&cfg80211_netdev_notifier); + if (err) + goto out_fail_notifier; + + ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); + + return 0; + +out_fail_notifier: + wiphy_sysfs_exit(); +out_fail_sysfs: + return err; +} +module_init(cfg80211_init); + +static void cfg80211_exit(void) +{ + debugfs_remove(ieee80211_debugfs_dir); + unregister_netdevice_notifier(&cfg80211_netdev_notifier); + wiphy_sysfs_exit(); +} +module_exit(cfg80211_exit); diff --git a/net/wireless/core.h b/net/wireless/core.h new file mode 100644 index 0000000..158db1e --- /dev/null +++ b/net/wireless/core.h @@ -0,0 +1,49 @@ +/* + * Wireless configuration interface internals. + * + * Copyright 2006, 2007 Johannes Berg + */ +#ifndef __NET_WIRELESS_CORE_H +#define __NET_WIRELESS_CORE_H +#include +#include +#include +#include +#include +#include + +struct cfg80211_registered_device { + struct cfg80211_ops *ops; + struct list_head list; + /* we hold this mutex during any call so that + * we cannot do multiple calls at once, and also + * to avoid the deregister call to proceed while + * any call is in progress */ + struct mutex mtx; + + /* wiphy index, internal only */ + int idx; + + /* associate netdev list */ + struct mutex devlist_mtx; + struct list_head netdev_list; + + /* must be last because of the way we do wiphy_priv(), + * and it should at least be aligned to NETDEV_ALIGN */ + struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); +}; + +static inline +struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) +{ + BUG_ON(!wiphy); + return container_of(wiphy, struct cfg80211_registered_device, wiphy); +} + +extern struct mutex cfg80211_drv_mutex; +extern struct list_head cfg80211_drv_list; + +/* free object */ +extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); + +#endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c new file mode 100644 index 0000000..3ebae14 --- /dev/null +++ b/net/wireless/sysfs.c @@ -0,0 +1,80 @@ +/* + * This file provides /sys/class/ieee80211// + * and some default attributes. + * + * Copyright 2005-2006 Jiri Benc + * Copyright 2006 Johannes Berg + * + * This file is GPLv2 as found in COPYING. + */ + +#include +#include +#include +#include +#include +#include +#include "sysfs.h" +#include "core.h" + +static inline struct cfg80211_registered_device *dev_to_rdev( + struct device *dev) +{ + return container_of(dev, struct cfg80211_registered_device, wiphy.dev); +} + +static ssize_t _show_index(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx); +} + +static ssize_t _show_permaddr(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char *addr = dev_to_rdev(dev)->wiphy.perm_addr; + + return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); +} + +static struct device_attribute ieee80211_dev_attrs[] = { + __ATTR(index, S_IRUGO, _show_index, NULL), + __ATTR(macaddress, S_IRUGO, _show_permaddr, NULL), + {} +}; + +static void wiphy_dev_release(struct device *dev) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + + cfg80211_dev_free(rdev); +} + +static int wiphy_uevent(struct device *dev, char **envp, + int num_envp, char *buf, int size) +{ + /* TODO, we probably need stuff here */ + return 0; +} + +struct class ieee80211_class = { + .name = "ieee80211", + .owner = THIS_MODULE, + .dev_release = wiphy_dev_release, + .dev_attrs = ieee80211_dev_attrs, +#ifdef CONFIG_HOTPLUG + .dev_uevent = wiphy_uevent, +#endif +}; + +int wiphy_sysfs_init(void) +{ + return class_register(&ieee80211_class); +} + +void wiphy_sysfs_exit(void) +{ + class_unregister(&ieee80211_class); +} diff --git a/net/wireless/sysfs.h b/net/wireless/sysfs.h new file mode 100644 index 0000000..65acbeb --- /dev/null +++ b/net/wireless/sysfs.h @@ -0,0 +1,9 @@ +#ifndef __WIRELESS_SYSFS_H +#define __WIRELESS_SYSFS_H + +extern int wiphy_sysfs_init(void); +extern void wiphy_sysfs_exit(void); + +extern struct class ieee80211_class; + +#endif /* __WIRELESS_SYSFS_H */ diff --git a/net/wireless/wext.c b/net/wireless/wext.c new file mode 100644 index 0000000..d6aaf65 --- /dev/null +++ b/net/wireless/wext.c @@ -0,0 +1,1509 @@ +/* + * This file implement the Wireless Extensions APIs. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ + +/************************** DOCUMENTATION **************************/ +/* + * API definition : + * -------------- + * See for details of the APIs and the rest. + * + * History : + * ------- + * + * v1 - 5.12.01 - Jean II + * o Created this file. + * + * v2 - 13.12.01 - Jean II + * o Move /proc/net/wireless stuff from net/core/dev.c to here + * o Make Wireless Extension IOCTLs go through here + * o Added iw_handler handling ;-) + * o Added standard ioctl description + * o Initial dumb commit strategy based on orinoco.c + * + * v3 - 19.12.01 - Jean II + * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call + * o Add event dispatcher function + * o Add event description + * o Propagate events as rtnetlink IFLA_WIRELESS option + * o Generate event on selected SET requests + * + * v4 - 18.04.02 - Jean II + * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 + * + * v5 - 21.06.02 - Jean II + * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup) + * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes + * o Add IWEVCUSTOM for driver specific event/scanning token + * o Turn on WE_STRICT_WRITE by default + kernel warning + * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) + * o Fix off-by-one in test (extra_size <= IFNAMSIZ) + * + * v6 - 9.01.03 - Jean II + * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() + * o Add enhanced spy support : iw_handler_set_thrspy() and event. + * o Add WIRELESS_EXT version display in /proc/net/wireless + * + * v6 - 18.06.04 - Jean II + * o Change get_spydata() method for added safety + * o Remove spy #ifdef, they are always on -> cleaner code + * o Allow any size GET request if user specifies length > max + * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV + * o Start migrating get_wireless_stats to struct iw_handler_def + * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus + * Based on patch from Pavel Roskin : + * o Fix kernel data leak to user space in private handler handling + * + * v7 - 18.3.05 - Jean II + * o Remove (struct iw_point *)->pointer from events and streams + * o Remove spy_offset from struct iw_handler_def + * o Start deprecating dev->get_wireless_stats, output a warning + * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless + * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats) + * + * v8 - 17.02.06 - Jean II + * o RtNetlink requests support (SET/GET) + * + * v8b - 03.08.06 - Herbert Xu + * o Fix Wireless Event locking issues. + * + * v9 - 14.3.06 - Jean II + * o Change length in ESSID and NICK to strlen() instead of strlen()+1 + * o Make standard_ioctl_num and standard_event_num unsigned + * o Remove (struct net_device *)->get_wireless_stats() + * + * v10 - 16.3.07 - Jean II + * o Prevent leaking of kernel space in stream on 64 bits. + */ + +/***************************** INCLUDES *****************************/ + +#include +#include /* off_t */ +#include /* struct ifreq, dev_get_by_name() */ +#include +#include /* rtnetlink stuff */ +#include +#include /* for __init */ +#include /* ARPHRD_ETHER */ +#include /* compare_ether_addr */ +#include + +#include /* Pretty obvious */ +#include /* New driver API */ +#include +#include + +#include /* copy_to_user() */ + +/************************* GLOBAL VARIABLES *************************/ +/* + * You should not use global variables, because of re-entrancy. + * On our case, it's only const, so it's OK... + */ +/* + * Meta-data about all the standard Wireless Extension request we + * know about. + */ +static const struct iw_ioctl_description standard_ioctl[] = { + [SIOCSIWCOMMIT - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWNAME - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_CHAR, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWNWID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWNWID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWFREQ - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWFREQ - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWMODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWMODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWSENS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWSENS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRANGE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWRANGE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_range), + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWPRIV - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_priv_args), + .max_tokens = 16, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [SIOCSIWSTATS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_statistics), + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr), + .max_tokens = IW_MAX_SPY, + }, + [SIOCGIWSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr) + + sizeof(struct iw_quality), + .max_tokens = IW_MAX_SPY, + }, + [SIOCSIWTHRSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [SIOCGIWTHRSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [SIOCSIWAP - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [SIOCGIWAP - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWMLME - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_mlme), + .max_tokens = sizeof(struct iw_mlme), + }, + [SIOCGIWAPLIST - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr) + + sizeof(struct iw_quality), + .max_tokens = IW_MAX_AP, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [SIOCSIWSCAN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = 0, + .max_tokens = sizeof(struct iw_scan_req), + }, + [SIOCGIWSCAN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_SCAN_MAX_DATA, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [SIOCSIWESSID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWESSID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWNICKN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + }, + [SIOCGIWNICKN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + }, + [SIOCSIWRATE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRATE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRTS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRTS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWFRAG - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWFRAG - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWTXPOW - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWTXPOW - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRETRY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRETRY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWENCODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, + }, + [SIOCGIWENCODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, + }, + [SIOCSIWPOWER - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWPOWER - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWGENIE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [SIOCGIWGENIE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [SIOCSIWAUTH - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWAUTH - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWENCODEEXT - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_encode_ext), + .max_tokens = sizeof(struct iw_encode_ext) + + IW_ENCODING_TOKEN_MAX, + }, + [SIOCGIWENCODEEXT - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_encode_ext), + .max_tokens = sizeof(struct iw_encode_ext) + + IW_ENCODING_TOKEN_MAX, + }, + [SIOCSIWPMKSA - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_pmksa), + .max_tokens = sizeof(struct iw_pmksa), + }, +}; +static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl); + +/* + * Meta-data about all the additional standard Wireless Extension events + * we know about. + */ +static const struct iw_ioctl_description standard_event[] = { + [IWEVTXDROP - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IWEVQUAL - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_QUAL, + }, + [IWEVCUSTOM - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_CUSTOM_MAX, + }, + [IWEVREGISTERED - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IWEVEXPIRED - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IWEVGENIE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IWEVMICHAELMICFAILURE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_michaelmicfailure), + }, + [IWEVASSOCREQIE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IWEVASSOCRESPIE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IWEVPMKIDCAND - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_pmkid_cand), + }, +}; +static const unsigned standard_event_num = ARRAY_SIZE(standard_event); + +/* Size (in bytes) of the various private data types */ +static const char iw_priv_type_size[] = { + 0, /* IW_PRIV_TYPE_NONE */ + 1, /* IW_PRIV_TYPE_BYTE */ + 1, /* IW_PRIV_TYPE_CHAR */ + 0, /* Not defined */ + sizeof(__u32), /* IW_PRIV_TYPE_INT */ + sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ + sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ + 0, /* Not defined */ +}; + +/* Size (in bytes) of various events */ +static const int event_type_size[] = { + IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_POINT_LEN, /* Without variable payload */ + IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ +}; + +/* Size (in bytes) of various events, as packed */ +static const int event_type_pk_size[] = { + IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_POINT_PK_LEN, /* Without variable payload */ + IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */ +}; + +/************************ COMMON SUBROUTINES ************************/ +/* + * Stuff that may be used in various place or doesn't fit in one + * of the section below. + */ + +/* ---------------------------------------------------------------- */ +/* + * Return the driver handler associated with a specific Wireless Extension. + */ +static iw_handler get_handler(struct net_device *dev, unsigned int cmd) +{ + /* Don't "optimise" the following variable, it will crash */ + unsigned int index; /* *MUST* be unsigned */ + + /* Check if we have some wireless handlers defined */ + if (dev->wireless_handlers == NULL) + return NULL; + + /* Try as a standard command */ + index = cmd - SIOCIWFIRST; + if (index < dev->wireless_handlers->num_standard) + return dev->wireless_handlers->standard[index]; + + /* Try as a private command */ + index = cmd - SIOCIWFIRSTPRIV; + if (index < dev->wireless_handlers->num_private) + return dev->wireless_handlers->private[index]; + + /* Not found */ + return NULL; +} + +/* ---------------------------------------------------------------- */ +/* + * Get statistics out of the driver + */ +static struct iw_statistics *get_wireless_stats(struct net_device *dev) +{ + /* New location */ + if ((dev->wireless_handlers != NULL) && + (dev->wireless_handlers->get_wireless_stats != NULL)) + return dev->wireless_handlers->get_wireless_stats(dev); + + /* Not found */ + return NULL; +} + +/* ---------------------------------------------------------------- */ +/* + * Call the commit handler in the driver + * (if exist and if conditions are right) + * + * Note : our current commit strategy is currently pretty dumb, + * but we will be able to improve on that... + * The goal is to try to agreagate as many changes as possible + * before doing the commit. Drivers that will define a commit handler + * are usually those that need a reset after changing parameters, so + * we want to minimise the number of reset. + * A cool idea is to use a timer : at each "set" command, we re-set the + * timer, when the timer eventually fires, we call the driver. + * Hopefully, more on that later. + * + * Also, I'm waiting to see how many people will complain about the + * netif_running(dev) test. I'm open on that one... + * Hopefully, the driver will remember to do a commit in "open()" ;-) + */ +static int call_commit_handler(struct net_device *dev) +{ + if ((netif_running(dev)) && + (dev->wireless_handlers->standard[0] != NULL)) + /* Call the commit handler on the driver */ + return dev->wireless_handlers->standard[0](dev, NULL, + NULL, NULL); + else + return 0; /* Command completed successfully */ +} + +/* ---------------------------------------------------------------- */ +/* + * Calculate size of private arguments + */ +static inline int get_priv_size(__u16 args) +{ + int num = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + return num * iw_priv_type_size[type]; +} + +/* ---------------------------------------------------------------- */ +/* + * Re-calculate the size of private arguments + */ +static inline int adjust_priv_size(__u16 args, + union iwreq_data * wrqu) +{ + int num = wrqu->data.length; + int max = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + /* Make sure the driver doesn't goof up */ + if (max < num) + num = max; + + return num * iw_priv_type_size[type]; +} + +/* ---------------------------------------------------------------- */ +/* + * Standard Wireless Handler : get wireless stats + * Allow programatic access to /proc/net/wireless even if /proc + * doesn't exist... Also more efficient... + */ +static int iw_handler_get_iwstats(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + /* Get stats from the driver */ + struct iw_statistics *stats; + + stats = get_wireless_stats(dev); + if (stats) { + /* Copy statistics to extra */ + memcpy(extra, stats, sizeof(struct iw_statistics)); + wrqu->data.length = sizeof(struct iw_statistics); + + /* Check if we need to clear the updated flag */ + if (wrqu->data.flags != 0) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + return 0; + } else + return -EOPNOTSUPP; +} + +/* ---------------------------------------------------------------- */ +/* + * Standard Wireless Handler : get iwpriv definitions + * Export the driver private handler definition + * They will be picked up by tools like iwpriv... + */ +static int iw_handler_get_private(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + /* Check if the driver has something to export */ + if ((dev->wireless_handlers->num_private_args == 0) || + (dev->wireless_handlers->private_args == NULL)) + return -EOPNOTSUPP; + + /* Check if there is enough buffer up there */ + if (wrqu->data.length < dev->wireless_handlers->num_private_args) { + /* User space can't know in advance how large the buffer + * needs to be. Give it a hint, so that we can support + * any size buffer we want somewhat efficiently... */ + wrqu->data.length = dev->wireless_handlers->num_private_args; + return -E2BIG; + } + + /* Set the number of available ioctls. */ + wrqu->data.length = dev->wireless_handlers->num_private_args; + + /* Copy structure to the user buffer. */ + memcpy(extra, dev->wireless_handlers->private_args, + sizeof(struct iw_priv_args) * wrqu->data.length); + + return 0; +} + + +/******************** /proc/net/wireless SUPPORT ********************/ +/* + * The /proc/net/wireless file is a human readable user-space interface + * exporting various wireless specific statistics from the wireless devices. + * This is the most popular part of the Wireless Extensions ;-) + * + * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). + * The content of the file is basically the content of "struct iw_statistics". + */ + +#ifdef CONFIG_PROC_FS + +/* ---------------------------------------------------------------- */ +/* + * Print one entry (line) of /proc/net/wireless + */ +static void wireless_seq_printf_stats(struct seq_file *seq, + struct net_device *dev) +{ + /* Get stats from the driver */ + struct iw_statistics *stats = get_wireless_stats(dev); + + if (stats) { + seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " + "%6d %6d %6d\n", + dev->name, stats->status, stats->qual.qual, + stats->qual.updated & IW_QUAL_QUAL_UPDATED + ? '.' : ' ', + ((__s32) stats->qual.level) - + ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), + stats->qual.updated & IW_QUAL_LEVEL_UPDATED + ? '.' : ' ', + ((__s32) stats->qual.noise) - + ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), + stats->qual.updated & IW_QUAL_NOISE_UPDATED + ? '.' : ' ', + stats->discard.nwid, stats->discard.code, + stats->discard.fragment, stats->discard.retries, + stats->discard.misc, stats->miss.beacon); + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + } +} + +/* ---------------------------------------------------------------- */ +/* + * Print info for /proc/net/wireless (print all entries) + */ +static int wireless_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "Inter-| sta-| Quality | Discarded " + "packets | Missed | WE\n" + " face | tus | link level noise | nwid " + "crypt frag retry misc | beacon | %d\n", + WIRELESS_EXT); + else + wireless_seq_printf_stats(seq, v); + return 0; +} + +static const struct seq_operations wireless_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = wireless_seq_show, +}; + +static int wireless_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &wireless_seq_ops); +} + +static const struct file_operations wireless_seq_fops = { + .owner = THIS_MODULE, + .open = wireless_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +int __init wext_proc_init(void) +{ + /* Create /proc/net/wireless entry */ + if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops)) + return -ENOMEM; + + return 0; +} +#endif /* CONFIG_PROC_FS */ + +/************************** IOCTL SUPPORT **************************/ +/* + * The original user space API to configure all those Wireless Extensions + * is through IOCTLs. + * In there, we check if we need to call the new driver API (iw_handler) + * or just call the driver ioctl handler. + */ + +/* ---------------------------------------------------------------- */ +/* + * Wrapper to call a standard Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + */ +static int ioctl_standard_call(struct net_device * dev, + struct ifreq * ifr, + unsigned int cmd, + iw_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + const struct iw_ioctl_description * descr; + struct iw_request_info info; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) + return -EOPNOTSUPP; + descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not */ + if (descr->header_type != IW_HEADER_TYPE_POINT) { + + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), NULL); + + /* Generate an event to notify listeners of the change */ + if ((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) + wireless_send_event(dev, cmd, &(iwr->u), NULL); + } else { + char * extra; + int extra_size; + int user_length = 0; + int err; + int essid_compat = 0; + + /* Calculate space needed by arguments. Always allocate + * for max space. Easier, and won't last long... */ + extra_size = descr->max_tokens * descr->token_size; + + /* Check need for ESSID compatibility for WE < 21 */ + switch (cmd) { + case SIOCSIWESSID: + case SIOCGIWESSID: + case SIOCSIWNICKN: + case SIOCGIWNICKN: + if (iwr->u.data.length == descr->max_tokens + 1) + essid_compat = 1; + else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + char essid[IW_ESSID_MAX_SIZE + 1]; + + err = copy_from_user(essid, iwr->u.data.pointer, + iwr->u.data.length * + descr->token_size); + if (err) + return -EFAULT; + + if (essid[iwr->u.data.length - 1] == '\0') + essid_compat = 1; + } + break; + default: + break; + } + + iwr->u.data.length -= essid_compat; + + /* Check what user space is giving us */ + if (IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if ((iwr->u.data.pointer == NULL) && + (iwr->u.data.length != 0)) + return -EFAULT; + /* Check if number of token fits within bounds */ + if (iwr->u.data.length > descr->max_tokens) + return -E2BIG; + if (iwr->u.data.length < descr->min_tokens) + return -EINVAL; + } else { + /* Check NULL pointer */ + if (iwr->u.data.pointer == NULL) + return -EFAULT; + /* Save user space buffer size for checking */ + user_length = iwr->u.data.length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. */ + + /* Support for very large requests */ + if ((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. */ + extra_size = user_length * descr->token_size; + /* Note : user_length is originally a __u16, + * and token_size is controlled by us, + * so extra_size won't get negative and + * won't overflow... */ + } + } + + /* Create the kernel buffer */ + /* kzalloc ensures NULL-termination for essid_compat */ + extra = kzalloc(extra_size, GFP_KERNEL); + if (extra == NULL) + return -ENOMEM; + + /* If it is a SET, get all the extra data in here */ + if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + err = copy_from_user(extra, iwr->u.data.pointer, + iwr->u.data.length * + descr->token_size); + if (err) { + kfree(extra); + return -EFAULT; + } + } + + /* Call the handler */ + ret = handler(dev, &info, &(iwr->u), extra); + + iwr->u.data.length += essid_compat; + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { + /* Check if there is enough buffer up there */ + if (user_length < iwr->u.data.length) { + kfree(extra); + return -E2BIG; + } + + err = copy_to_user(iwr->u.data.pointer, extra, + iwr->u.data.length * + descr->token_size); + if (err) + ret = -EFAULT; + } + + /* Generate an event to notify listeners of the change */ + if ((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) { + if (descr->flags & IW_DESCR_FLAG_RESTRICT) + /* If the event is restricted, don't + * export the payload */ + wireless_send_event(dev, cmd, &(iwr->u), NULL); + else + wireless_send_event(dev, cmd, &(iwr->u), + extra); + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + /* Here, we will generate the appropriate event if needed */ + + return ret; +} + +/* ---------------------------------------------------------------- */ +/* + * Wrapper to call a private Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + * It's not as nice and slimline as the standard wrapper. The cause + * is struct iw_priv_args, which was not really designed for the + * job we are going here. + * + * IMPORTANT : This function prevent to set and get data on the same + * IOCTL and enforce the SET/GET convention. Not doing it would be + * far too hairy... + * If you need to set and get data at the same time, please don't use + * a iw_handler but process it in your ioctl handler (i.e. use the + * old driver API). + */ +static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, + unsigned int cmd, iw_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + const struct iw_priv_args * descr = NULL; + struct iw_request_info info; + int extra_size = 0; + int i; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + for (i = 0; i < dev->wireless_handlers->num_private_args; i++) + if (cmd == dev->wireless_handlers->private_args[i].cmd) { + descr = &(dev->wireless_handlers->private_args[i]); + break; + } + + /* Compute the size of the set/get arguments */ + if (descr != NULL) { + if (IW_IS_SET(cmd)) { + int offset = 0; /* For sub-ioctls */ + /* Check for sub-ioctl handler */ + if (descr->name[0] == '\0') + /* Reserve one int for sub-ioctl index */ + offset = sizeof(__u32); + + /* Size of set arguments */ + extra_size = get_priv_size(descr->set_args); + + /* Does it fits in iwr ? */ + if ((descr->set_args & IW_PRIV_SIZE_FIXED) && + ((extra_size + offset) <= IFNAMSIZ)) + extra_size = 0; + } else { + /* Size of get arguments */ + extra_size = get_priv_size(descr->get_args); + + /* Does it fits in iwr ? */ + if ((descr->get_args & IW_PRIV_SIZE_FIXED) && + (extra_size <= IFNAMSIZ)) + extra_size = 0; + } + } + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not. */ + if (extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + } else { + char * extra; + int err; + + /* Check what user space is giving us */ + if (IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if ((iwr->u.data.pointer == NULL) && + (iwr->u.data.length != 0)) + return -EFAULT; + + /* Does it fits within bounds ? */ + if (iwr->u.data.length > (descr->set_args & + IW_PRIV_SIZE_MASK)) + return -E2BIG; + } else if (iwr->u.data.pointer == NULL) + return -EFAULT; + + /* Always allocate for max space. Easier, and won't last + * long... */ + extra = kmalloc(extra_size, GFP_KERNEL); + if (extra == NULL) + return -ENOMEM; + + /* If it is a SET, get all the extra data in here */ + if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + err = copy_from_user(extra, iwr->u.data.pointer, + extra_size); + if (err) { + kfree(extra); + return -EFAULT; + } + } + + /* Call the handler */ + ret = handler(dev, &info, &(iwr->u), extra); + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { + + /* Adjust for the actual length if it's variable, + * avoid leaking kernel bits outside. */ + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { + extra_size = adjust_priv_size(descr->get_args, + &(iwr->u)); + } + + err = copy_to_user(iwr->u.data.pointer, extra, + extra_size); + if (err) + ret = -EFAULT; + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} + +/* ---------------------------------------------------------------- */ +/* + * Main IOCTl dispatcher. + * Check the type of IOCTL and call the appropriate wrapper... + */ +static int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd) +{ + struct net_device *dev; + iw_handler handler; + + /* Permissions are already checked in dev_ioctl() before calling us. + * The copy_to/from_user() of ifr is also dealt with in there */ + + /* Make sure the device exist */ + if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) + return -ENODEV; + + /* A bunch of special cases, then the generic case... + * Note that 'cmd' is already filtered in dev_ioctl() with + * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ + if (cmd == SIOCGIWSTATS) + return ioctl_standard_call(dev, ifr, cmd, + &iw_handler_get_iwstats); + + if (cmd == SIOCGIWPRIV && dev->wireless_handlers) + return ioctl_standard_call(dev, ifr, cmd, + &iw_handler_get_private); + + /* Basic check */ + if (!netif_device_present(dev)) + return -ENODEV; + + /* New driver API : try to find the handler */ + handler = get_handler(dev, cmd); + if (handler) { + /* Standard and private are not the same */ + if (cmd < SIOCIWFIRSTPRIV) + return ioctl_standard_call(dev, ifr, cmd, handler); + else + return ioctl_private_call(dev, ifr, cmd, handler); + } + /* Old driver API : call driver ioctl handler */ + if (dev->do_ioctl) + return dev->do_ioctl(dev, ifr, cmd); + return -EOPNOTSUPP; +} + +/* entry point from dev ioctl */ +int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd, + void __user *arg) +{ + int ret; + + /* If command is `set a parameter', or + * `get the encoding parameters', check if + * the user has the right to do it */ + if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) + && !capable(CAP_NET_ADMIN)) + return -EPERM; + + dev_load(ifr->ifr_name); + rtnl_lock(); + ret = wireless_process_ioctl(ifr, cmd); + rtnl_unlock(); + if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct ifreq))) + return -EFAULT; + return ret; +} + +/************************* EVENT PROCESSING *************************/ +/* + * Process events generated by the wireless layer or the driver. + * Most often, the event will be propagated through rtnetlink + */ + +/* ---------------------------------------------------------------- */ +/* + * Locking... + * ---------- + * + * Thanks to Herbert Xu for fixing + * the locking issue in here and implementing this code ! + * + * The issue : wireless_send_event() is often called in interrupt context, + * while the Netlink layer can never be called in interrupt context. + * The fully formed RtNetlink events are queued, and then a tasklet is run + * to feed those to Netlink. + * The skb_queue is interrupt safe, and its lock is not held while calling + * Netlink, so there is no possibility of dealock. + * Jean II + */ + +static struct sk_buff_head wireless_nlevent_queue; + +static int __init wireless_nlevent_init(void) +{ + skb_queue_head_init(&wireless_nlevent_queue); + return 0; +} + +subsys_initcall(wireless_nlevent_init); + +static void wireless_nlevent_process(unsigned long data) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&wireless_nlevent_queue))) + rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); +} + +static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); + +/* ---------------------------------------------------------------- */ +/* + * Fill a rtnetlink message with our event data. + * Note that we propage only the specified event and don't dump the + * current wireless config. Dumping the wireless config is far too + * expensive (for each parameter, the driver need to query the hardware). + */ +static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, + int type, char *event, int event_len) +{ + struct ifinfomsg *r; + struct nlmsghdr *nlh; + unsigned char *b = skb_tail_pointer(skb); + + nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r)); + r = NLMSG_DATA(nlh); + r->ifi_family = AF_UNSPEC; + r->__ifi_pad = 0; + r->ifi_type = dev->type; + r->ifi_index = dev->ifindex; + r->ifi_flags = dev_get_flags(dev); + r->ifi_change = 0; /* Wireless changes don't affect those flags */ + + /* Add the wireless events in the netlink packet */ + RTA_PUT(skb, IFLA_WIRELESS, event_len, event); + + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; + +nlmsg_failure: +rtattr_failure: + nlmsg_trim(skb, b); + return -1; +} + +/* ---------------------------------------------------------------- */ +/* + * Create and broadcast and send it on the standard rtnetlink socket + * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c + * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field + * within a RTM_NEWLINK event. + */ +static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) +{ + struct sk_buff *skb; + int size = NLMSG_GOODSIZE; + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + return; + + if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, + event, event_len) < 0) { + kfree_skb(skb); + return; + } + NETLINK_CB(skb).dst_group = RTNLGRP_LINK; + skb_queue_tail(&wireless_nlevent_queue, skb); + tasklet_schedule(&wireless_nlevent_tasklet); +} + +/* ---------------------------------------------------------------- */ +/* + * Main event dispatcher. Called from other parts and drivers. + * Send the event on the appropriate channels. + * May be called from interrupt context. + */ +void wireless_send_event(struct net_device * dev, + unsigned int cmd, + union iwreq_data * wrqu, + char * extra) +{ + const struct iw_ioctl_description * descr = NULL; + int extra_len = 0; + struct iw_event *event; /* Mallocated whole event */ + int event_len; /* Its size */ + int hdr_len; /* Size of the event header */ + int wrqu_off = 0; /* Offset in wrqu */ + /* Don't "optimise" the following variable, it will crash */ + unsigned cmd_index; /* *MUST* be unsigned */ + + /* Get the description of the Event */ + if (cmd <= SIOCIWLAST) { + cmd_index = cmd - SIOCIWFIRST; + if (cmd_index < standard_ioctl_num) + descr = &(standard_ioctl[cmd_index]); + } else { + cmd_index = cmd - IWEVFIRST; + if (cmd_index < standard_event_num) + descr = &(standard_event[cmd_index]); + } + /* Don't accept unknown events */ + if (descr == NULL) { + /* Note : we don't return an error to the driver, because + * the driver would not know what to do about it. It can't + * return an error to the user, because the event is not + * initiated by a user request. + * The best the driver could do is to log an error message. + * We will do it ourselves instead... + */ + printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", + dev->name, cmd); + return; + } + + /* Check extra parameters and set extra_len */ + if (descr->header_type == IW_HEADER_TYPE_POINT) { + /* Check if number of token fits within bounds */ + if (wrqu->data.length > descr->max_tokens) { + printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); + return; + } + if (wrqu->data.length < descr->min_tokens) { + printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); + return; + } + /* Calculate extra_len - extra is NULL for restricted events */ + if (extra != NULL) + extra_len = wrqu->data.length * descr->token_size; + /* Always at an offset in wrqu */ + wrqu_off = IW_EV_POINT_OFF; + } + + /* Total length of the event */ + hdr_len = event_type_size[descr->header_type]; + event_len = hdr_len + extra_len; + + /* Create temporary buffer to hold the event */ + event = kmalloc(event_len, GFP_ATOMIC); + if (event == NULL) + return; + + /* Fill event */ + event->len = event_len; + event->cmd = cmd; + memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); + if (extra) + memcpy(((char *) event) + hdr_len, extra, extra_len); + + /* Send via the RtNetlink event channel */ + rtmsg_iwinfo(dev, (char *) event, event_len); + + /* Cleanup */ + kfree(event); + + return; /* Always success, I guess ;-) */ +} +EXPORT_SYMBOL(wireless_send_event); + +/********************** ENHANCED IWSPY SUPPORT **********************/ +/* + * In the old days, the driver was handling spy support all by itself. + * Now, the driver can delegate this task to Wireless Extensions. + * It needs to use those standard spy iw_handler in struct iw_handler_def, + * push data to us via wireless_spy_update() and include struct iw_spy_data + * in its private part (and export it in net_device->wireless_data->spy_data). + * One of the main advantage of centralising spy support here is that + * it becomes much easier to improve and extend it without having to touch + * the drivers. One example is the addition of the Spy-Threshold events. + */ + +/* ---------------------------------------------------------------- */ +/* + * Return the pointer to the spy data in the driver. + * Because this is called on the Rx path via wireless_spy_update(), + * we want it to be efficient... + */ +static inline struct iw_spy_data *get_spydata(struct net_device *dev) +{ + /* This is the new way */ + if (dev->wireless_data) + return dev->wireless_data->spy_data; + return NULL; +} + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : set Spy List + */ +int iw_handler_set_spy(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct sockaddr * address = (struct sockaddr *) extra; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + /* Disable spy collection while we copy the addresses. + * While we copy addresses, any call to wireless_spy_update() + * will NOP. This is OK, as anyway the addresses are changing. */ + spydata->spy_number = 0; + + /* We want to operate without locking, because wireless_spy_update() + * most likely will happen in the interrupt handler, and therefore + * have its own locking constraints and needs performance. + * The rtnl_lock() make sure we don't race with the other iw_handlers. + * This make sure wireless_spy_update() "see" that the spy list + * is temporarily disabled. */ + smp_wmb(); + + /* Are there are addresses to copy? */ + if (wrqu->data.length > 0) { + int i; + + /* Copy addresses */ + for (i = 0; i < wrqu->data.length; i++) + memcpy(spydata->spy_address[i], address[i].sa_data, + ETH_ALEN); + /* Reset stats */ + memset(spydata->spy_stat, 0, + sizeof(struct iw_quality) * IW_MAX_SPY); + } + + /* Make sure above is updated before re-enabling */ + smp_wmb(); + + /* Enable addresses */ + spydata->spy_number = wrqu->data.length; + + return 0; +} +EXPORT_SYMBOL(iw_handler_set_spy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : get Spy List + */ +int iw_handler_get_spy(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct sockaddr * address = (struct sockaddr *) extra; + int i; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + wrqu->data.length = spydata->spy_number; + + /* Copy addresses. */ + for (i = 0; i < spydata->spy_number; i++) { + memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); + address[i].sa_family = AF_UNIX; + } + /* Copy stats to the user buffer (just after). */ + if (spydata->spy_number > 0) + memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), + spydata->spy_stat, + sizeof(struct iw_quality) * spydata->spy_number); + /* Reset updated flags. */ + for (i = 0; i < spydata->spy_number; i++) + spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; + return 0; +} +EXPORT_SYMBOL(iw_handler_get_spy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : set spy threshold + */ +int iw_handler_set_thrspy(struct net_device * dev, + struct iw_request_info *info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + /* Just do it */ + memcpy(&(spydata->spy_thr_low), &(threshold->low), + 2 * sizeof(struct iw_quality)); + + /* Clear flag */ + memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); + + return 0; +} +EXPORT_SYMBOL(iw_handler_set_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : get spy threshold + */ +int iw_handler_get_thrspy(struct net_device * dev, + struct iw_request_info *info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + /* Just do it */ + memcpy(&(threshold->low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); + + return 0; +} +EXPORT_SYMBOL(iw_handler_get_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Prepare and send a Spy Threshold event + */ +static void iw_send_thrspy_event(struct net_device * dev, + struct iw_spy_data * spydata, + unsigned char * address, + struct iw_quality * wstats) +{ + union iwreq_data wrqu; + struct iw_thrspy threshold; + + /* Init */ + wrqu.data.length = 1; + wrqu.data.flags = 0; + /* Copy address */ + memcpy(threshold.addr.sa_data, address, ETH_ALEN); + threshold.addr.sa_family = ARPHRD_ETHER; + /* Copy stats */ + memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); + /* Copy also thresholds */ + memcpy(&(threshold.low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); +} + +/* ---------------------------------------------------------------- */ +/* + * Call for the driver to update the spy data. + * For now, the spy data is a simple array. As the size of the array is + * small, this is good enough. If we wanted to support larger number of + * spy addresses, we should use something more efficient... + */ +void wireless_spy_update(struct net_device * dev, + unsigned char * address, + struct iw_quality * wstats) +{ + struct iw_spy_data * spydata = get_spydata(dev); + int i; + int match = -1; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return; + + /* Update all records that match */ + for (i = 0; i < spydata->spy_number; i++) + if (!compare_ether_addr(address, spydata->spy_address[i])) { + memcpy(&(spydata->spy_stat[i]), wstats, + sizeof(struct iw_quality)); + match = i; + } + + /* Generate an event if we cross the spy threshold. + * To avoid event storms, we have a simple hysteresis : we generate + * event only when we go under the low threshold or above the + * high threshold. */ + if (match >= 0) { + if (spydata->spy_thr_under[match]) { + if (wstats->level > spydata->spy_thr_high.level) { + spydata->spy_thr_under[match] = 0; + iw_send_thrspy_event(dev, spydata, + address, wstats); + } + } else { + if (wstats->level < spydata->spy_thr_low.level) { + spydata->spy_thr_under[match] = 1; + iw_send_thrspy_event(dev, spydata, + address, wstats); + } + } + } +} +EXPORT_SYMBOL(wireless_spy_update); diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index e62ba41..0d6002f 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -951,7 +951,7 @@ int x25_rx_call_request(struct sk_buff * * Incoming Call User Data. */ if (skb->len >= 0) { - memcpy(makex25->calluserdata.cuddata, skb->data, skb->len); + skb_copy_from_linear_data(skb, makex25->calluserdata.cuddata, skb->len); makex25->calluserdata.cudlength = skb->len; } @@ -1058,9 +1058,10 @@ static int x25_sendmsg(struct kiocb *ioc */ SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n"); - asmptr = skb->h.raw = skb_put(skb, len); + skb_reset_transport_header(skb); + skb_put(skb, len); - rc = memcpy_fromiovec(asmptr, msg->msg_iov, len); + rc = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); if (rc) goto out_kfree_skb; @@ -1210,8 +1211,7 @@ static int x25_recvmsg(struct kiocb *ioc } } - skb->h.raw = skb->data; - + skb_reset_transport_header(skb); copied = skb->len; if (copied > size) { @@ -1280,6 +1280,12 @@ static int x25_ioctl(struct socket *sock rc = sock_get_timestamp(sk, (struct timeval __user *)argp); break; + case SIOCGSTAMPNS: + rc = -EINVAL; + if (sk) + rc = sock_get_timestampns(sk, + (struct timespec __user *)argp); + break; case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: @@ -1521,6 +1527,12 @@ static int compat_x25_ioctl(struct socke rc = compat_sock_get_timestamp(sk, (struct timeval __user*)argp); break; + case SIOCGSTAMPNS: + rc = -EINVAL; + if (sk) + rc = compat_sock_get_timestampns(sk, + (struct timespec __user*)argp); + break; case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index c7221de..848a6b6 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -48,7 +48,7 @@ static int x25_receive_data(struct sk_bu if ((sk = x25_find_socket(lci, nb)) != NULL) { int queued = 1; - skb->h.raw = skb->data; + skb_reset_transport_header(skb); bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { queued = x25_process_rx_frame(sk, skb); @@ -191,7 +191,7 @@ void x25_send_frame(struct sk_buff *skb, { unsigned char *dptr; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); switch (nb->dev->type) { case ARPHRD_X25: diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index c5239fc..1c88762 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -53,17 +53,20 @@ static int x25_queue_rx_frame(struct soc skb_queue_tail(&x25->fragment_queue, skb); - skbn->h.raw = skbn->data; + skb_reset_transport_header(skbn); skbo = skb_dequeue(&x25->fragment_queue); - memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + skb_copy_from_linear_data(skbo, skb_put(skbn, skbo->len), + skbo->len); kfree_skb(skbo); while ((skbo = skb_dequeue(&x25->fragment_queue)) != NULL) { skb_pull(skbo, (x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN); - memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + skb_copy_from_linear_data(skbo, + skb_put(skbn, skbo->len), + skbo->len); kfree_skb(skbo); } @@ -112,8 +115,9 @@ static int x25_state1_machine(struct soc * Copy any Call User Data. */ if (skb->len >= 0) { - memcpy(x25->calluserdata.cuddata, skb->data, - skb->len); + skb_copy_from_linear_data(skb, + x25->calluserdata.cuddata, + skb->len); x25->calluserdata.cudlength = skb->len; } if (!sock_flag(sk, SOCK_DEAD)) diff --git a/net/x25/x25_out.c b/net/x25/x25_out.c index 6f57378..2b96b52 100644 --- a/net/x25/x25_out.c +++ b/net/x25/x25_out.c @@ -61,7 +61,7 @@ int x25_output(struct sock *sk, struct s if (skb->len - header_len > max_len) { /* Save a copy of the Header */ - memcpy(header, skb->data, header_len); + skb_copy_from_linear_data(skb, header, header_len); skb_pull(skb, header_len); frontlen = skb_headroom(skb); @@ -84,12 +84,12 @@ int x25_output(struct sock *sk, struct s len = max_len > skb->len ? skb->len : max_len; /* Copy the user data */ - memcpy(skb_put(skbn, len), skb->data, len); + skb_copy_from_linear_data(skb, skb_put(skbn, len), len); skb_pull(skb, len); /* Duplicate the Header */ skb_push(skbn, header_len); - memcpy(skbn->data, header, header_len); + skb_copy_to_linear_data(skbn, header, header_len); if (skb->len > 0) { if (x25->neighbour->extended) diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index f373a8a..6249a94 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -612,175 +612,6 @@ EXPORT_SYMBOL_GPL(skb_icv_walk); #if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE) -/* Looking generic it is not used in another places. */ - -int -skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) -{ - int start = skb_headlen(skb); - int i, copy = start - offset; - int elt = 0; - - if (copy > 0) { - if (copy > len) - copy = len; - sg[elt].page = virt_to_page(skb->data + offset); - sg[elt].offset = (unsigned long)(skb->data + offset) % PAGE_SIZE; - sg[elt].length = copy; - elt++; - if ((len -= copy) == 0) - return elt; - offset += copy; - } - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - int end; - - BUG_TRAP(start <= offset + len); - - end = start + skb_shinfo(skb)->frags[i].size; - if ((copy = end - offset) > 0) { - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - - if (copy > len) - copy = len; - sg[elt].page = frag->page; - sg[elt].offset = frag->page_offset+offset-start; - sg[elt].length = copy; - elt++; - if (!(len -= copy)) - return elt; - offset += copy; - } - start = end; - } - - if (skb_shinfo(skb)->frag_list) { - struct sk_buff *list = skb_shinfo(skb)->frag_list; - - for (; list; list = list->next) { - int end; - - BUG_TRAP(start <= offset + len); - - end = start + list->len; - if ((copy = end - offset) > 0) { - if (copy > len) - copy = len; - elt += skb_to_sgvec(list, sg+elt, offset - start, copy); - if ((len -= copy) == 0) - return elt; - offset += copy; - } - start = end; - } - } - BUG_ON(len); - return elt; -} -EXPORT_SYMBOL_GPL(skb_to_sgvec); - -/* Check that skb data bits are writable. If they are not, copy data - * to newly created private area. If "tailbits" is given, make sure that - * tailbits bytes beyond current end of skb are writable. - * - * Returns amount of elements of scatterlist to load for subsequent - * transformations and pointer to writable trailer skb. - */ - -int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer) -{ - int copyflag; - int elt; - struct sk_buff *skb1, **skb_p; - - /* If skb is cloned or its head is paged, reallocate - * head pulling out all the pages (pages are considered not writable - * at the moment even if they are anonymous). - */ - if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) && - __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL) - return -ENOMEM; - - /* Easy case. Most of packets will go this way. */ - if (!skb_shinfo(skb)->frag_list) { - /* A little of trouble, not enough of space for trailer. - * This should not happen, when stack is tuned to generate - * good frames. OK, on miss we reallocate and reserve even more - * space, 128 bytes is fair. */ - - if (skb_tailroom(skb) < tailbits && - pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC)) - return -ENOMEM; - - /* Voila! */ - *trailer = skb; - return 1; - } - - /* Misery. We are in troubles, going to mincer fragments... */ - - elt = 1; - skb_p = &skb_shinfo(skb)->frag_list; - copyflag = 0; - - while ((skb1 = *skb_p) != NULL) { - int ntail = 0; - - /* The fragment is partially pulled by someone, - * this can happen on input. Copy it and everything - * after it. */ - - if (skb_shared(skb1)) - copyflag = 1; - - /* If the skb is the last, worry about trailer. */ - - if (skb1->next == NULL && tailbits) { - if (skb_shinfo(skb1)->nr_frags || - skb_shinfo(skb1)->frag_list || - skb_tailroom(skb1) < tailbits) - ntail = tailbits + 128; - } - - if (copyflag || - skb_cloned(skb1) || - ntail || - skb_shinfo(skb1)->nr_frags || - skb_shinfo(skb1)->frag_list) { - struct sk_buff *skb2; - - /* Fuck, we are miserable poor guys... */ - if (ntail == 0) - skb2 = skb_copy(skb1, GFP_ATOMIC); - else - skb2 = skb_copy_expand(skb1, - skb_headroom(skb1), - ntail, - GFP_ATOMIC); - if (unlikely(skb2 == NULL)) - return -ENOMEM; - - if (skb1->sk) - skb_set_owner_w(skb2, skb1->sk); - - /* Looking around. Are we still alive? - * OK, link new skb, drop old one */ - - skb2->next = skb1->next; - *skb_p = skb2; - kfree_skb(skb1); - skb1 = skb2; - } - elt++; - *trailer = skb1; - skb_p = &skb1->next; - } - - return elt; -} -EXPORT_SYMBOL_GPL(skb_cow_data); - void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len) { if (tail != skb) { diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index ee15bda..5c46958 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -62,7 +62,7 @@ int xfrm_parse_spi(struct sk_buff *skb, case IPPROTO_COMP: if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr))) return -EINVAL; - *spi = htonl(ntohs(*(__be16*)(skb->h.raw + 2))); + *spi = htonl(ntohs(*(__be16*)(skb_transport_header(skb) + 2))); *seq = 0; return 0; default: @@ -72,8 +72,8 @@ int xfrm_parse_spi(struct sk_buff *skb, if (!pskb_may_pull(skb, 16)) return -EINVAL; - *spi = *(__be32*)(skb->h.raw + offset); - *seq = *(__be32*)(skb->h.raw + offset_seq); + *spi = *(__be32*)(skb_transport_header(skb) + offset); + *seq = *(__be32*)(skb_transport_header(skb) + offset_seq); return 0; } EXPORT_SYMBOL(xfrm_parse_spi); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 785c3e3..263e34e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -268,7 +268,7 @@ static inline unsigned long make_jiffies static void xfrm_policy_timer(unsigned long data) { struct xfrm_policy *xp = (struct xfrm_policy*)data; - unsigned long now = (unsigned long)xtime.tv_sec; + unsigned long now = get_seconds(); long next = LONG_MAX; int warn = 0; int dir; @@ -579,8 +579,22 @@ static inline int xfrm_byidx_should_resi return 0; } -static DEFINE_MUTEX(hash_resize_mutex); +void xfrm_spd_getinfo(struct xfrm_spdinfo *si) +{ + read_lock_bh(&xfrm_policy_lock); + si->incnt = xfrm_policy_count[XFRM_POLICY_IN]; + si->outcnt = xfrm_policy_count[XFRM_POLICY_OUT]; + si->fwdcnt = xfrm_policy_count[XFRM_POLICY_FWD]; + si->inscnt = xfrm_policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]; + si->outscnt = xfrm_policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]; + si->fwdscnt = xfrm_policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]; + si->spdhcnt = xfrm_idx_hmask; + si->spdhmcnt = xfrm_policy_hashmax; + read_unlock_bh(&xfrm_policy_lock); +} +EXPORT_SYMBOL(xfrm_spd_getinfo); +static DEFINE_MUTEX(hash_resize_mutex); static void xfrm_hash_resize(struct work_struct *__unused) { int dir, total; @@ -690,7 +704,7 @@ int xfrm_policy_insert(int dir, struct x } policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir); hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index)); - policy->curlft.add_time = (unsigned long)xtime.tv_sec; + policy->curlft.add_time = get_seconds(); policy->curlft.use_time = 0; if (!mod_timer(&policy->timer, jiffies + HZ)) xfrm_pol_hold(policy); @@ -1049,7 +1063,7 @@ static inline int policy_to_flow_dir(int return FLOW_DIR_OUT; case XFRM_POLICY_FWD: return FLOW_DIR_FWD; - }; + } } static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl) @@ -1133,7 +1147,7 @@ #endif old_pol = sk->sk_policy[dir]; sk->sk_policy[dir] = pol; if (pol) { - pol->curlft.add_time = (unsigned long)xtime.tv_sec; + pol->curlft.add_time = get_seconds(); pol->index = xfrm_gen_index(pol->type, XFRM_POLICY_MAX+dir); __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir); } @@ -1330,6 +1344,40 @@ xfrm_bundle_create(struct xfrm_policy *p return err; } +static int inline +xfrm_dst_alloc_copy(void **target, void *src, int size) +{ + if (!*target) { + *target = kmalloc(size, GFP_ATOMIC); + if (!*target) + return -ENOMEM; + } + memcpy(*target, src, size); + return 0; +} + +static int inline +xfrm_dst_update_parent(struct dst_entry *dst, struct xfrm_selector *sel) +{ +#ifdef CONFIG_XFRM_SUB_POLICY + struct xfrm_dst *xdst = (struct xfrm_dst *)dst; + return xfrm_dst_alloc_copy((void **)&(xdst->partner), + sel, sizeof(*sel)); +#else + return 0; +#endif +} + +static int inline +xfrm_dst_update_origin(struct dst_entry *dst, struct flowi *fl) +{ +#ifdef CONFIG_XFRM_SUB_POLICY + struct xfrm_dst *xdst = (struct xfrm_dst *)dst; + return xfrm_dst_alloc_copy((void **)&(xdst->origin), fl, sizeof(*fl)); +#else + return 0; +#endif +} static int stale_bundle(struct dst_entry *dst); @@ -1386,7 +1434,7 @@ restart: return 0; family = dst_orig->ops->family; - policy->curlft.use_time = (unsigned long)xtime.tv_sec; + policy->curlft.use_time = get_seconds(); pols[0] = policy; npols ++; xfrm_nr += pols[0]->xfrm_nr; @@ -1518,6 +1566,18 @@ #endif err = -EHOSTUNREACH; goto error; } + + if (npols > 1) + err = xfrm_dst_update_parent(dst, &pols[1]->selector); + else + err = xfrm_dst_update_origin(dst, fl); + if (unlikely(err)) { + write_unlock_bh(&policy->lock); + if (dst) + dst_free(dst); + goto error; + } + dst->next = policy->bundles; policy->bundles = dst; dst_hold(dst); @@ -1682,7 +1742,7 @@ int __xfrm_policy_check(struct sock *sk, return 1; } - pol->curlft.use_time = (unsigned long)xtime.tv_sec; + pol->curlft.use_time = get_seconds(); pols[0] = pol; npols ++; @@ -1694,7 +1754,7 @@ #ifdef CONFIG_XFRM_SUB_POLICY if (pols[1]) { if (IS_ERR(pols[1])) return 0; - pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec; + pols[1]->curlft.use_time = get_seconds(); npols ++; } } @@ -1933,6 +1993,15 @@ int xfrm_bundle_ok(struct xfrm_policy *p if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) || (dst->dev && !netif_running(dst->dev))) return 0; +#ifdef CONFIG_XFRM_SUB_POLICY + if (fl) { + if (first->origin && !flow_cache_uli_match(first->origin, fl)) + return 0; + if (first->partner && + !xfrm_selector_match(first->partner, fl, family)) + return 0; + } +#endif last = NULL; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index e3a0bcf..f3a61eb 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -233,7 +233,7 @@ static inline unsigned long make_jiffies static void xfrm_timer_handler(unsigned long data) { struct xfrm_state *x = (struct xfrm_state*)data; - unsigned long now = (unsigned long)xtime.tv_sec; + unsigned long now = get_seconds(); long next = LONG_MAX; int warn = 0; int err = 0; @@ -326,7 +326,7 @@ struct xfrm_state *xfrm_state_alloc(void init_timer(&x->rtimer); x->rtimer.function = xfrm_replay_timer_handler; x->rtimer.data = (unsigned long)x; - x->curlft.add_time = (unsigned long)xtime.tv_sec; + x->curlft.add_time = get_seconds(); x->lft.soft_byte_limit = XFRM_INF; x->lft.soft_packet_limit = XFRM_INF; x->lft.hard_byte_limit = XFRM_INF; @@ -421,6 +421,16 @@ restart: } EXPORT_SYMBOL(xfrm_state_flush); +void xfrm_sad_getinfo(struct xfrm_sadinfo *si) +{ + spin_lock_bh(&xfrm_state_lock); + si->sadcnt = xfrm_state_num; + si->sadhcnt = xfrm_state_hmask; + si->sadhmcnt = xfrm_state_hashmax; + spin_unlock_bh(&xfrm_state_lock); +} +EXPORT_SYMBOL(xfrm_sad_getinfo); + static int xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, struct xfrm_tmpl *tmpl, @@ -458,7 +468,7 @@ static struct xfrm_state *__xfrm_state_l x->id.daddr.a6)) continue; break; - }; + } xfrm_state_hold(x); return x; @@ -493,7 +503,7 @@ static struct xfrm_state *__xfrm_state_l x->props.saddr.a6)) continue; break; - }; + } xfrm_state_hold(x); return x; @@ -722,7 +732,7 @@ static struct xfrm_state *__find_acq_cor (struct in6_addr *)saddr)) continue; break; - }; + } xfrm_state_hold(x); return x; @@ -755,7 +765,7 @@ static struct xfrm_state *__find_acq_cor ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6, (struct in6_addr *)daddr); break; - }; + } x->km.state = XFRM_STATE_ACQ; x->id.proto = proto; @@ -1051,7 +1061,7 @@ EXPORT_SYMBOL(xfrm_state_update); int xfrm_state_check_expire(struct xfrm_state *x) { if (!x->curlft.use_time) - x->curlft.use_time = (unsigned long)xtime.tv_sec; + x->curlft.use_time = get_seconds(); if (x->km.state != XFRM_STATE_VALID) return -EINVAL; @@ -1667,37 +1677,17 @@ void xfrm_state_delete_tunnel(struct xfr } EXPORT_SYMBOL(xfrm_state_delete_tunnel); -/* - * This function is NOT optimal. For example, with ESP it will give an - * MTU that's usually two bytes short of being optimal. However, it will - * usually give an answer that's a multiple of 4 provided the input is - * also a multiple of 4. - */ int xfrm_state_mtu(struct xfrm_state *x, int mtu) { - int res = mtu; - - res -= x->props.header_len; - - for (;;) { - int m = res; - - if (m < 68) - return 68; - - spin_lock_bh(&x->lock); - if (x->km.state == XFRM_STATE_VALID && - x->type && x->type->get_max_size) - m = x->type->get_max_size(x, m); - else - m += x->props.header_len; - spin_unlock_bh(&x->lock); - - if (m <= mtu) - break; - res -= (m - mtu); - } + int res; + spin_lock_bh(&x->lock); + if (x->km.state == XFRM_STATE_VALID && + x->type && x->type->get_mtu) + res = x->type->get_mtu(x, mtu); + else + res = mtu; + spin_unlock_bh(&x->lock); return res; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 816e369..4210d91 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -71,7 +71,7 @@ static int verify_one_alg(struct rtattr default: return -EINVAL; - }; + } algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; return 0; @@ -152,7 +152,7 @@ #endif default: goto out; - }; + } err = -EINVAL; switch (p->id.proto) { @@ -192,7 +192,7 @@ #endif default: goto out; - }; + } if ((err = verify_one_alg(xfrma, XFRMA_ALG_AUTH))) goto out; @@ -217,7 +217,7 @@ #endif default: goto out; - }; + } err = 0; @@ -576,7 +576,7 @@ static int dump_one_state(struct xfrm_st struct sk_buff *skb = sp->out_skb; struct xfrm_usersa_info *p; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); if (sp->this_idx < sp->start_idx) goto out; @@ -621,14 +621,14 @@ static int dump_one_state(struct xfrm_st if (x->lastused) RTA_PUT(skb, XFRMA_LASTUSED, sizeof(x->lastused), &x->lastused); - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; out: sp->this_idx++; return 0; nlmsg_failure: rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -672,6 +672,136 @@ static struct sk_buff *xfrm_state_netlin return skb; } +static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) +{ + struct xfrm_spdinfo si; + struct nlmsghdr *nlh; + u32 *f; + + nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); + if (nlh == NULL) /* shouldnt really happen ... */ + return -EMSGSIZE; + + f = nlmsg_data(nlh); + *f = flags; + xfrm_spd_getinfo(&si); + + if (flags & XFRM_SPD_HMASK) + NLA_PUT_U32(skb, XFRMA_SPDHMASK, si.spdhcnt); + if (flags & XFRM_SPD_HMAX) + NLA_PUT_U32(skb, XFRMA_SPDHMAX, si.spdhmcnt); + if (flags & XFRM_SPD_ICNT) + NLA_PUT_U32(skb, XFRMA_SPDICNT, si.incnt); + if (flags & XFRM_SPD_OCNT) + NLA_PUT_U32(skb, XFRMA_SPDOCNT, si.outcnt); + if (flags & XFRM_SPD_FCNT) + NLA_PUT_U32(skb, XFRMA_SPDFCNT, si.fwdcnt); + if (flags & XFRM_SPD_ISCNT) + NLA_PUT_U32(skb, XFRMA_SPDISCNT, si.inscnt); + if (flags & XFRM_SPD_OSCNT) + NLA_PUT_U32(skb, XFRMA_SPDOSCNT, si.inscnt); + if (flags & XFRM_SPD_FSCNT) + NLA_PUT_U32(skb, XFRMA_SPDFSCNT, si.inscnt); + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, + struct rtattr **xfrma) +{ + struct sk_buff *r_skb; + u32 *flags = NLMSG_DATA(nlh); + u32 spid = NETLINK_CB(skb).pid; + u32 seq = nlh->nlmsg_seq; + int len = NLMSG_LENGTH(sizeof(u32)); + + + if (*flags & XFRM_SPD_HMASK) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SPD_HMAX) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SPD_ICNT) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SPD_OCNT) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SPD_FCNT) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SPD_ISCNT) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SPD_OSCNT) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SPD_FSCNT) + len += RTA_SPACE(sizeof(u32)); + + r_skb = alloc_skb(len, GFP_ATOMIC); + if (r_skb == NULL) + return -ENOMEM; + + if (build_spdinfo(r_skb, spid, seq, *flags) < 0) + BUG(); + + return nlmsg_unicast(xfrm_nl, r_skb, spid); +} + +static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) +{ + struct xfrm_sadinfo si; + struct nlmsghdr *nlh; + u32 *f; + + nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); + if (nlh == NULL) /* shouldnt really happen ... */ + return -EMSGSIZE; + + f = nlmsg_data(nlh); + *f = flags; + xfrm_sad_getinfo(&si); + + if (flags & XFRM_SAD_HMASK) + NLA_PUT_U32(skb, XFRMA_SADHMASK, si.sadhcnt); + if (flags & XFRM_SAD_HMAX) + NLA_PUT_U32(skb, XFRMA_SADHMAX, si.sadhmcnt); + if (flags & XFRM_SAD_CNT) + NLA_PUT_U32(skb, XFRMA_SADCNT, si.sadcnt); + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, + struct rtattr **xfrma) +{ + struct sk_buff *r_skb; + u32 *flags = NLMSG_DATA(nlh); + u32 spid = NETLINK_CB(skb).pid; + u32 seq = nlh->nlmsg_seq; + int len = NLMSG_LENGTH(sizeof(u32)); + + if (*flags & XFRM_SAD_HMASK) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SAD_HMAX) + len += RTA_SPACE(sizeof(u32)); + if (*flags & XFRM_SAD_CNT) + len += RTA_SPACE(sizeof(u32)); + + r_skb = alloc_skb(len, GFP_ATOMIC); + + if (r_skb == NULL) + return -ENOMEM; + + if (build_sadinfo(r_skb, spid, seq, *flags) < 0) + BUG(); + + return nlmsg_unicast(xfrm_nl, r_skb, spid); +} + static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct rtattr **xfrma) { @@ -711,7 +841,7 @@ static int verify_userspi_info(struct xf default: return -EINVAL; - }; + } if (p->min > p->max) return -EINVAL; @@ -789,7 +919,7 @@ static int verify_policy_dir(u8 dir) default: return -EINVAL; - }; + } return 0; } @@ -805,7 +935,7 @@ #endif default: return -EINVAL; - }; + } return 0; } @@ -821,7 +951,7 @@ static int verify_newpolicy_info(struct default: return -EINVAL; - }; + } switch (p->action) { case XFRM_POLICY_ALLOW: @@ -830,7 +960,7 @@ static int verify_newpolicy_info(struct default: return -EINVAL; - }; + } switch (p->sel.family) { case AF_INET: @@ -845,7 +975,7 @@ #endif default: return -EINVAL; - }; + } return verify_policy_dir(p->dir); } @@ -912,7 +1042,7 @@ #if defined(CONFIG_IPV6) || defined(CONF #endif default: return -EINVAL; - }; + } } return 0; @@ -1157,7 +1287,7 @@ static int dump_one_policy(struct xfrm_p struct sk_buff *in_skb = sp->in_skb; struct sk_buff *skb = sp->out_skb; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); if (sp->this_idx < sp->start_idx) goto out; @@ -1176,13 +1306,13 @@ static int dump_one_policy(struct xfrm_p if (copy_to_user_policy_type(xp->type, skb) < 0) goto nlmsg_failure; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; out: sp->this_idx++; return 0; nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1330,7 +1460,7 @@ static int build_aevent(struct sk_buff * struct xfrm_aevent_id *id; struct nlmsghdr *nlh; struct xfrm_lifetime_cur ltime; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); nlh = NLMSG_PUT(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id)); id = NLMSG_DATA(nlh); @@ -1362,12 +1492,12 @@ static int build_aevent(struct sk_buff * RTA_PUT(skb,XFRMA_ETIMER_THRESH,sizeof(u32),&etimer); } - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1744,7 +1874,7 @@ static int build_migrate(struct sk_buff struct xfrm_migrate *mp; struct xfrm_userpolicy_id *pol_id; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); int i; nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id)); @@ -1764,10 +1894,10 @@ static int build_migrate(struct sk_buff goto nlmsg_failure; } - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1823,6 +1953,8 @@ static const int xfrm_msg_min[XFRM_NR_MS [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), + [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)), + [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)), }; #undef XMSGSIZE @@ -1850,55 +1982,40 @@ static struct xfrm_link { [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, + [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, + [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, }; -static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) +static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct rtattr *xfrma[XFRMA_MAX]; struct xfrm_link *link; int type, min_len; - if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) - return 0; - type = nlh->nlmsg_type; - - /* A control message: ignore them */ - if (type < XFRM_MSG_BASE) - return 0; - - /* Unknown message: reply with EINVAL */ if (type > XFRM_MSG_MAX) - goto err_einval; + return -EINVAL; type -= XFRM_MSG_BASE; link = &xfrm_dispatch[type]; /* All operations require privileges, even GET */ - if (security_netlink_recv(skb, CAP_NET_ADMIN)) { - *errp = -EPERM; - return -1; - } + if (security_netlink_recv(skb, CAP_NET_ADMIN)) + return -EPERM; if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && (nlh->nlmsg_flags & NLM_F_DUMP)) { if (link->dump == NULL) - goto err_einval; - - if ((*errp = netlink_dump_start(xfrm_nl, skb, nlh, - link->dump, NULL)) != 0) { - return -1; - } + return -EINVAL; - netlink_queue_skip(nlh, skb); - return -1; + return netlink_dump_start(xfrm_nl, skb, nlh, link->dump, NULL); } memset(xfrma, 0, sizeof(xfrma)); if (nlh->nlmsg_len < (min_len = xfrm_msg_min[type])) - goto err_einval; + return -EINVAL; if (nlh->nlmsg_len > min_len) { int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); @@ -1908,7 +2025,7 @@ static int xfrm_user_rcv_msg(struct sk_b unsigned short flavor = attr->rta_type; if (flavor) { if (flavor > XFRMA_MAX) - goto err_einval; + return -EINVAL; xfrma[flavor - 1] = attr; } attr = RTA_NEXT(attr, attrlen); @@ -1916,14 +2033,9 @@ static int xfrm_user_rcv_msg(struct sk_b } if (link->doit == NULL) - goto err_einval; - *errp = link->doit(skb, nlh, xfrma); - - return *errp; + return -EINVAL; -err_einval: - *errp = -EINVAL; - return -1; + return link->doit(skb, nlh, xfrma); } static void xfrm_netlink_rcv(struct sock *sk, int len) @@ -1942,7 +2054,7 @@ static int build_expire(struct sk_buff * { struct xfrm_user_expire *ue; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue)); @@ -1952,11 +2064,11 @@ static int build_expire(struct sk_buff * copy_to_user_state(x, &ue->state); ue->hard = (c->data.hard != 0) ? 1 : 0; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -1999,7 +2111,7 @@ static int xfrm_notify_sa_flush(struct k struct xfrm_usersa_flush *p; struct nlmsghdr *nlh; struct sk_buff *skb; - unsigned char *b; + sk_buff_data_t b; int len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush)); skb = alloc_skb(len, GFP_ATOMIC); @@ -2045,7 +2157,7 @@ static int xfrm_notify_sa(struct xfrm_st struct xfrm_usersa_id *id; struct nlmsghdr *nlh; struct sk_buff *skb; - unsigned char *b; + sk_buff_data_t b; int len = xfrm_sa_len(x); int headlen; @@ -2129,7 +2241,7 @@ static int build_acquire(struct sk_buff { struct xfrm_user_acquire *ua; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); __u32 seq = xfrm_get_acqseq(); nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_ACQUIRE, @@ -2153,11 +2265,11 @@ static int build_acquire(struct sk_buff if (copy_to_user_policy_type(xp->type, skb) < 0) goto nlmsg_failure; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -2249,7 +2361,7 @@ static int build_polexpire(struct sk_buf struct xfrm_user_polexpire *upe; struct nlmsghdr *nlh; int hard = c->data.hard; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe)); upe = NLMSG_DATA(nlh); @@ -2264,11 +2376,11 @@ static int build_polexpire(struct sk_buf goto nlmsg_failure; upe->hard = !!hard; - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -2300,7 +2412,7 @@ static int xfrm_notify_policy(struct xfr struct xfrm_userpolicy_id *id; struct nlmsghdr *nlh; struct sk_buff *skb; - unsigned char *b; + sk_buff_data_t b; int len = RTA_SPACE(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); int headlen; @@ -2357,7 +2469,7 @@ static int xfrm_notify_policy_flush(stru { struct nlmsghdr *nlh; struct sk_buff *skb; - unsigned char *b; + sk_buff_data_t b; int len = 0; #ifdef CONFIG_XFRM_SUB_POLICY len += RTA_SPACE(sizeof(struct xfrm_userpolicy_type)); @@ -2410,7 +2522,7 @@ static int build_report(struct sk_buff * { struct xfrm_user_report *ur; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur)); ur = NLMSG_DATA(nlh); @@ -2422,12 +2534,12 @@ static int build_report(struct sk_buff * if (addr) RTA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr); - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: rtattr_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -2466,7 +2578,7 @@ static int __init xfrm_user_init(void) printk(KERN_INFO "Initializing XFRM netlink socket\n"); nlsk = netlink_kernel_create(NETLINK_XFRM, XFRMNLGRP_MAX, - xfrm_netlink_rcv, THIS_MODULE); + xfrm_netlink_rcv, NULL, THIS_MODULE); if (nlsk == NULL) return -ENOMEM; rcu_assign_pointer(xfrm_nl, nlsk); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index ad45ce7..88292e3 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -66,6 +66,8 @@ struct key_type key_type_keyring = { .read = keyring_read, }; +EXPORT_SYMBOL(key_type_keyring); + /* * semaphore to serialise link/link calls to prevent two link calls in parallel * introducing a cycle diff --git a/security/selinux/Makefile b/security/selinux/Makefile index faf2e02..dc3502e 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -8,5 +8,7 @@ selinux-y := avc.o hooks.o selinuxfs.o n selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o +selinux-$(CONFIG_NETLABEL) += netlabel.o + EXTRA_CFLAGS += -Isecurity/selinux/include diff --git a/security/selinux/avc.c b/security/selinux/avc.c index da8caf1..e4396a8 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -217,6 +217,8 @@ static void avc_dump_query(struct audit_ audit_log_format(ab, " tcontext=%s", scontext); kfree(scontext); } + + BUG_ON(tclass >= ARRAY_SIZE(class_to_string) || !class_to_string[tclass]); audit_log_format(ab, " tclass=%s", class_to_string[tclass]); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d41e24d..885a9a9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -77,7 +77,7 @@ #include "avc.h" #include "objsec.h" #include "netif.h" #include "xfrm.h" -#include "selinux_netlabel.h" +#include "netlabel.h" #define XATTR_SELINUX_SUFFIX "selinux" #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX @@ -2944,7 +2944,7 @@ static int selinux_parse_skb_ipv4(struct int offset, ihlen, ret = -EINVAL; struct iphdr _iph, *ih; - offset = skb->nh.raw - skb->data; + offset = skb_network_offset(skb); ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); if (ih == NULL) goto out; @@ -3026,7 +3026,7 @@ static int selinux_parse_skb_ipv6(struct int ret = -EINVAL, offset; struct ipv6hdr _ipv6h, *ip6; - offset = skb->nh.raw - skb->data; + offset = skb_network_offset(skb); ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h); if (ip6 == NULL) goto out; @@ -3123,6 +3123,34 @@ #endif /* IPV6 */ return ret; } +/** + * selinux_skb_extlbl_sid - Determine the external label of a packet + * @skb: the packet + * @base_sid: the SELinux SID to use as a context for MLS only external labels + * @sid: the packet's SID + * + * Description: + * Check the various different forms of external packet labeling and determine + * the external SID for the packet. + * + */ +static void selinux_skb_extlbl_sid(struct sk_buff *skb, + u32 base_sid, + u32 *sid) +{ + u32 xfrm_sid; + u32 nlbl_sid; + + selinux_skb_xfrm_sid(skb, &xfrm_sid); + if (selinux_netlbl_skbuff_getsid(skb, + (xfrm_sid == SECSID_NULL ? + base_sid : xfrm_sid), + &nlbl_sid) != 0) + nlbl_sid = SECSID_NULL; + + *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); +} + /* socket security operations */ static int socket_has_perm(struct task_struct *task, struct socket *sock, u32 perms) @@ -3664,9 +3692,7 @@ static int selinux_socket_getpeersec_dgr if (sock && sock->sk->sk_family == PF_UNIX) selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); else if (skb) - security_skb_extlbl_sid(skb, - SECINITSID_UNLABELED, - &peer_secid); + selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peer_secid); if (peer_secid == SECSID_NULL) err = -EINVAL; @@ -3727,7 +3753,7 @@ static int selinux_inet_conn_request(str u32 newsid; u32 peersid; - security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid); + selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid); if (peersid == SECSID_NULL) { req->secid = sksec->sid; req->peer_secid = SECSID_NULL; @@ -3765,7 +3791,7 @@ static void selinux_inet_conn_establishe { struct sk_security_struct *sksec = sk->sk_security; - security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid); + selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid); } static void selinux_req_classify_flow(const struct request_sock *req, @@ -3786,7 +3812,7 @@ static int selinux_nlmsg_perm(struct soc err = -EINVAL; goto out; } - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); if (err) { diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index ad9fb2d..b83e740 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -128,96 +128,6 @@ S_(SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease") S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_WRITE, "audit_write") S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_CONTROL, "audit_control") - S_(SECCLASS_PASSWD, PASSWD__PASSWD, "passwd") - S_(SECCLASS_PASSWD, PASSWD__CHFN, "chfn") - S_(SECCLASS_PASSWD, PASSWD__CHSH, "chsh") - S_(SECCLASS_PASSWD, PASSWD__ROOTOK, "rootok") - S_(SECCLASS_PASSWD, PASSWD__CRONTAB, "crontab") - S_(SECCLASS_DRAWABLE, DRAWABLE__CREATE, "create") - S_(SECCLASS_DRAWABLE, DRAWABLE__DESTROY, "destroy") - S_(SECCLASS_DRAWABLE, DRAWABLE__DRAW, "draw") - S_(SECCLASS_DRAWABLE, DRAWABLE__COPY, "copy") - S_(SECCLASS_DRAWABLE, DRAWABLE__GETATTR, "getattr") - S_(SECCLASS_GC, GC__CREATE, "create") - S_(SECCLASS_GC, GC__FREE, "free") - S_(SECCLASS_GC, GC__GETATTR, "getattr") - S_(SECCLASS_GC, GC__SETATTR, "setattr") - S_(SECCLASS_WINDOW, WINDOW__ADDCHILD, "addchild") - S_(SECCLASS_WINDOW, WINDOW__CREATE, "create") - S_(SECCLASS_WINDOW, WINDOW__DESTROY, "destroy") - S_(SECCLASS_WINDOW, WINDOW__MAP, "map") - S_(SECCLASS_WINDOW, WINDOW__UNMAP, "unmap") - S_(SECCLASS_WINDOW, WINDOW__CHSTACK, "chstack") - S_(SECCLASS_WINDOW, WINDOW__CHPROPLIST, "chproplist") - S_(SECCLASS_WINDOW, WINDOW__CHPROP, "chprop") - S_(SECCLASS_WINDOW, WINDOW__LISTPROP, "listprop") - S_(SECCLASS_WINDOW, WINDOW__GETATTR, "getattr") - S_(SECCLASS_WINDOW, WINDOW__SETATTR, "setattr") - S_(SECCLASS_WINDOW, WINDOW__SETFOCUS, "setfocus") - S_(SECCLASS_WINDOW, WINDOW__MOVE, "move") - S_(SECCLASS_WINDOW, WINDOW__CHSELECTION, "chselection") - S_(SECCLASS_WINDOW, WINDOW__CHPARENT, "chparent") - S_(SECCLASS_WINDOW, WINDOW__CTRLLIFE, "ctrllife") - S_(SECCLASS_WINDOW, WINDOW__ENUMERATE, "enumerate") - S_(SECCLASS_WINDOW, WINDOW__TRANSPARENT, "transparent") - S_(SECCLASS_WINDOW, WINDOW__MOUSEMOTION, "mousemotion") - S_(SECCLASS_WINDOW, WINDOW__CLIENTCOMEVENT, "clientcomevent") - S_(SECCLASS_WINDOW, WINDOW__INPUTEVENT, "inputevent") - S_(SECCLASS_WINDOW, WINDOW__DRAWEVENT, "drawevent") - S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEEVENT, "windowchangeevent") - S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEREQUEST, "windowchangerequest") - S_(SECCLASS_WINDOW, WINDOW__SERVERCHANGEEVENT, "serverchangeevent") - S_(SECCLASS_WINDOW, WINDOW__EXTENSIONEVENT, "extensionevent") - S_(SECCLASS_FONT, FONT__LOAD, "load") - S_(SECCLASS_FONT, FONT__FREE, "free") - S_(SECCLASS_FONT, FONT__GETATTR, "getattr") - S_(SECCLASS_FONT, FONT__USE, "use") - S_(SECCLASS_COLORMAP, COLORMAP__CREATE, "create") - S_(SECCLASS_COLORMAP, COLORMAP__FREE, "free") - S_(SECCLASS_COLORMAP, COLORMAP__INSTALL, "install") - S_(SECCLASS_COLORMAP, COLORMAP__UNINSTALL, "uninstall") - S_(SECCLASS_COLORMAP, COLORMAP__LIST, "list") - S_(SECCLASS_COLORMAP, COLORMAP__READ, "read") - S_(SECCLASS_COLORMAP, COLORMAP__STORE, "store") - S_(SECCLASS_COLORMAP, COLORMAP__GETATTR, "getattr") - S_(SECCLASS_COLORMAP, COLORMAP__SETATTR, "setattr") - S_(SECCLASS_PROPERTY, PROPERTY__CREATE, "create") - S_(SECCLASS_PROPERTY, PROPERTY__FREE, "free") - S_(SECCLASS_PROPERTY, PROPERTY__READ, "read") - S_(SECCLASS_PROPERTY, PROPERTY__WRITE, "write") - S_(SECCLASS_CURSOR, CURSOR__CREATE, "create") - S_(SECCLASS_CURSOR, CURSOR__CREATEGLYPH, "createglyph") - S_(SECCLASS_CURSOR, CURSOR__FREE, "free") - S_(SECCLASS_CURSOR, CURSOR__ASSIGN, "assign") - S_(SECCLASS_CURSOR, CURSOR__SETATTR, "setattr") - S_(SECCLASS_XCLIENT, XCLIENT__KILL, "kill") - S_(SECCLASS_XINPUT, XINPUT__LOOKUP, "lookup") - S_(SECCLASS_XINPUT, XINPUT__GETATTR, "getattr") - S_(SECCLASS_XINPUT, XINPUT__SETATTR, "setattr") - S_(SECCLASS_XINPUT, XINPUT__SETFOCUS, "setfocus") - S_(SECCLASS_XINPUT, XINPUT__WARPPOINTER, "warppointer") - S_(SECCLASS_XINPUT, XINPUT__ACTIVEGRAB, "activegrab") - S_(SECCLASS_XINPUT, XINPUT__PASSIVEGRAB, "passivegrab") - S_(SECCLASS_XINPUT, XINPUT__UNGRAB, "ungrab") - S_(SECCLASS_XINPUT, XINPUT__BELL, "bell") - S_(SECCLASS_XINPUT, XINPUT__MOUSEMOTION, "mousemotion") - S_(SECCLASS_XINPUT, XINPUT__RELABELINPUT, "relabelinput") - S_(SECCLASS_XSERVER, XSERVER__SCREENSAVER, "screensaver") - S_(SECCLASS_XSERVER, XSERVER__GETHOSTLIST, "gethostlist") - S_(SECCLASS_XSERVER, XSERVER__SETHOSTLIST, "sethostlist") - S_(SECCLASS_XSERVER, XSERVER__GETFONTPATH, "getfontpath") - S_(SECCLASS_XSERVER, XSERVER__SETFONTPATH, "setfontpath") - S_(SECCLASS_XSERVER, XSERVER__GETATTR, "getattr") - S_(SECCLASS_XSERVER, XSERVER__GRAB, "grab") - S_(SECCLASS_XSERVER, XSERVER__UNGRAB, "ungrab") - S_(SECCLASS_XEXTENSION, XEXTENSION__QUERY, "query") - S_(SECCLASS_XEXTENSION, XEXTENSION__USE, "use") - S_(SECCLASS_PAX, PAX__PAGEEXEC, "pageexec") - S_(SECCLASS_PAX, PAX__EMUTRAMP, "emutramp") - S_(SECCLASS_PAX, PAX__MPROTECT, "mprotect") - S_(SECCLASS_PAX, PAX__RANDMMAP, "randmmap") - S_(SECCLASS_PAX, PAX__RANDEXEC, "randexec") - S_(SECCLASS_PAX, PAX__SEGMEXEC, "segmexec") S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ, "nlmsg_read") S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE, "nlmsg_write") S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_READ, "nlmsg_read") @@ -232,16 +142,6 @@ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv") S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read") S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write") - S_(SECCLASS_DBUS, DBUS__ACQUIRE_SVC, "acquire_svc") - S_(SECCLASS_DBUS, DBUS__SEND_MSG, "send_msg") - S_(SECCLASS_NSCD, NSCD__GETPWD, "getpwd") - S_(SECCLASS_NSCD, NSCD__GETGRP, "getgrp") - S_(SECCLASS_NSCD, NSCD__GETHOST, "gethost") - S_(SECCLASS_NSCD, NSCD__GETSTAT, "getstat") - S_(SECCLASS_NSCD, NSCD__ADMIN, "admin") - S_(SECCLASS_NSCD, NSCD__SHMEMPWD, "shmempwd") - S_(SECCLASS_NSCD, NSCD__SHMEMGRP, "shmemgrp") - S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost") S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto") S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom") S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext") @@ -256,7 +156,5 @@ S_(SECCLASS_KEY, KEY__LINK, "link") S_(SECCLASS_KEY, KEY__SETATTR, "setattr") S_(SECCLASS_KEY, KEY__CREATE, "create") - S_(SECCLASS_CONTEXT, CONTEXT__TRANSLATE, "translate") - S_(SECCLASS_CONTEXT, CONTEXT__CONTAINS, "contains") S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind") S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index 2de4b5f..5fee173 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -16,7 +16,6 @@ #define COMMON_FILE__EXECUTE #define COMMON_FILE__SWAPON 0x00004000UL #define COMMON_FILE__QUOTAON 0x00008000UL #define COMMON_FILE__MOUNTON 0x00010000UL - #define COMMON_SOCKET__IOCTL 0x00000001UL #define COMMON_SOCKET__READ 0x00000002UL #define COMMON_SOCKET__WRITE 0x00000004UL @@ -39,7 +38,6 @@ #define COMMON_SOCKET__SENDTO #define COMMON_SOCKET__RECV_MSG 0x00080000UL #define COMMON_SOCKET__SEND_MSG 0x00100000UL #define COMMON_SOCKET__NAME_BIND 0x00200000UL - #define COMMON_IPC__CREATE 0x00000001UL #define COMMON_IPC__DESTROY 0x00000002UL #define COMMON_IPC__GETATTR 0x00000004UL @@ -49,7 +47,6 @@ #define COMMON_IPC__WRITE #define COMMON_IPC__ASSOCIATE 0x00000040UL #define COMMON_IPC__UNIX_READ 0x00000080UL #define COMMON_IPC__UNIX_WRITE 0x00000100UL - #define FILESYSTEM__MOUNT 0x00000001UL #define FILESYSTEM__REMOUNT 0x00000002UL #define FILESYSTEM__UNMOUNT 0x00000004UL @@ -60,7 +57,6 @@ #define FILESYSTEM__TRANSITION #define FILESYSTEM__ASSOCIATE 0x00000080UL #define FILESYSTEM__QUOTAMOD 0x00000100UL #define FILESYSTEM__QUOTAGET 0x00000200UL - #define DIR__IOCTL 0x00000001UL #define DIR__READ 0x00000002UL #define DIR__WRITE 0x00000004UL @@ -78,13 +74,11 @@ #define DIR__EXECUTE #define DIR__SWAPON 0x00004000UL #define DIR__QUOTAON 0x00008000UL #define DIR__MOUNTON 0x00010000UL - #define DIR__ADD_NAME 0x00020000UL #define DIR__REMOVE_NAME 0x00040000UL #define DIR__REPARENT 0x00080000UL #define DIR__SEARCH 0x00100000UL #define DIR__RMDIR 0x00200000UL - #define FILE__IOCTL 0x00000001UL #define FILE__READ 0x00000002UL #define FILE__WRITE 0x00000004UL @@ -102,11 +96,9 @@ #define FILE__EXECUTE #define FILE__SWAPON 0x00004000UL #define FILE__QUOTAON 0x00008000UL #define FILE__MOUNTON 0x00010000UL - #define FILE__EXECUTE_NO_TRANS 0x00020000UL #define FILE__ENTRYPOINT 0x00040000UL #define FILE__EXECMOD 0x00080000UL - #define LNK_FILE__IOCTL 0x00000001UL #define LNK_FILE__READ 0x00000002UL #define LNK_FILE__WRITE 0x00000004UL @@ -124,7 +116,6 @@ #define LNK_FILE__EXECUTE #define LNK_FILE__SWAPON 0x00004000UL #define LNK_FILE__QUOTAON 0x00008000UL #define LNK_FILE__MOUNTON 0x00010000UL - #define CHR_FILE__IOCTL 0x00000001UL #define CHR_FILE__READ 0x00000002UL #define CHR_FILE__WRITE 0x00000004UL @@ -142,11 +133,9 @@ #define CHR_FILE__EXECUTE #define CHR_FILE__SWAPON 0x00004000UL #define CHR_FILE__QUOTAON 0x00008000UL #define CHR_FILE__MOUNTON 0x00010000UL - #define CHR_FILE__EXECUTE_NO_TRANS 0x00020000UL #define CHR_FILE__ENTRYPOINT 0x00040000UL #define CHR_FILE__EXECMOD 0x00080000UL - #define BLK_FILE__IOCTL 0x00000001UL #define BLK_FILE__READ 0x00000002UL #define BLK_FILE__WRITE 0x00000004UL @@ -164,7 +153,6 @@ #define BLK_FILE__EXECUTE #define BLK_FILE__SWAPON 0x00004000UL #define BLK_FILE__QUOTAON 0x00008000UL #define BLK_FILE__MOUNTON 0x00010000UL - #define SOCK_FILE__IOCTL 0x00000001UL #define SOCK_FILE__READ 0x00000002UL #define SOCK_FILE__WRITE 0x00000004UL @@ -182,7 +170,6 @@ #define SOCK_FILE__EXECUTE #define SOCK_FILE__SWAPON 0x00004000UL #define SOCK_FILE__QUOTAON 0x00008000UL #define SOCK_FILE__MOUNTON 0x00010000UL - #define FIFO_FILE__IOCTL 0x00000001UL #define FIFO_FILE__READ 0x00000002UL #define FIFO_FILE__WRITE 0x00000004UL @@ -200,9 +187,7 @@ #define FIFO_FILE__EXECUTE #define FIFO_FILE__SWAPON 0x00004000UL #define FIFO_FILE__QUOTAON 0x00008000UL #define FIFO_FILE__MOUNTON 0x00010000UL - #define FD__USE 0x00000001UL - #define SOCKET__IOCTL 0x00000001UL #define SOCKET__READ 0x00000002UL #define SOCKET__WRITE 0x00000004UL @@ -225,7 +210,6 @@ #define SOCKET__SENDTO #define SOCKET__RECV_MSG 0x00080000UL #define SOCKET__SEND_MSG 0x00100000UL #define SOCKET__NAME_BIND 0x00200000UL - #define TCP_SOCKET__IOCTL 0x00000001UL #define TCP_SOCKET__READ 0x00000002UL #define TCP_SOCKET__WRITE 0x00000004UL @@ -248,13 +232,11 @@ #define TCP_SOCKET__SENDTO #define TCP_SOCKET__RECV_MSG 0x00080000UL #define TCP_SOCKET__SEND_MSG 0x00100000UL #define TCP_SOCKET__NAME_BIND 0x00200000UL - #define TCP_SOCKET__CONNECTTO 0x00400000UL #define TCP_SOCKET__NEWCONN 0x00800000UL #define TCP_SOCKET__ACCEPTFROM 0x01000000UL #define TCP_SOCKET__NODE_BIND 0x02000000UL #define TCP_SOCKET__NAME_CONNECT 0x04000000UL - #define UDP_SOCKET__IOCTL 0x00000001UL #define UDP_SOCKET__READ 0x00000002UL #define UDP_SOCKET__WRITE 0x00000004UL @@ -277,9 +259,7 @@ #define UDP_SOCKET__SENDTO #define UDP_SOCKET__RECV_MSG 0x00080000UL #define UDP_SOCKET__SEND_MSG 0x00100000UL #define UDP_SOCKET__NAME_BIND 0x00200000UL - #define UDP_SOCKET__NODE_BIND 0x00400000UL - #define RAWIP_SOCKET__IOCTL 0x00000001UL #define RAWIP_SOCKET__READ 0x00000002UL #define RAWIP_SOCKET__WRITE 0x00000004UL @@ -302,9 +282,7 @@ #define RAWIP_SOCKET__SENDTO #define RAWIP_SOCKET__RECV_MSG 0x00080000UL #define RAWIP_SOCKET__SEND_MSG 0x00100000UL #define RAWIP_SOCKET__NAME_BIND 0x00200000UL - #define RAWIP_SOCKET__NODE_BIND 0x00400000UL - #define NODE__TCP_RECV 0x00000001UL #define NODE__TCP_SEND 0x00000002UL #define NODE__UDP_RECV 0x00000004UL @@ -314,7 +292,6 @@ #define NODE__RAWIP_SEND #define NODE__ENFORCE_DEST 0x00000040UL #define NODE__DCCP_RECV 0x00000080UL #define NODE__DCCP_SEND 0x00000100UL - #define NETIF__TCP_RECV 0x00000001UL #define NETIF__TCP_SEND 0x00000002UL #define NETIF__UDP_RECV 0x00000004UL @@ -323,7 +300,6 @@ #define NETIF__RAWIP_RECV #define NETIF__RAWIP_SEND 0x00000020UL #define NETIF__DCCP_RECV 0x00000040UL #define NETIF__DCCP_SEND 0x00000080UL - #define NETLINK_SOCKET__IOCTL 0x00000001UL #define NETLINK_SOCKET__READ 0x00000002UL #define NETLINK_SOCKET__WRITE 0x00000004UL @@ -346,7 +322,6 @@ #define NETLINK_SOCKET__SENDTO #define NETLINK_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_SOCKET__NAME_BIND 0x00200000UL - #define PACKET_SOCKET__IOCTL 0x00000001UL #define PACKET_SOCKET__READ 0x00000002UL #define PACKET_SOCKET__WRITE 0x00000004UL @@ -369,7 +344,6 @@ #define PACKET_SOCKET__SENDTO #define PACKET_SOCKET__RECV_MSG 0x00080000UL #define PACKET_SOCKET__SEND_MSG 0x00100000UL #define PACKET_SOCKET__NAME_BIND 0x00200000UL - #define KEY_SOCKET__IOCTL 0x00000001UL #define KEY_SOCKET__READ 0x00000002UL #define KEY_SOCKET__WRITE 0x00000004UL @@ -392,7 +366,6 @@ #define KEY_SOCKET__SENDTO #define KEY_SOCKET__RECV_MSG 0x00080000UL #define KEY_SOCKET__SEND_MSG 0x00100000UL #define KEY_SOCKET__NAME_BIND 0x00200000UL - #define UNIX_STREAM_SOCKET__IOCTL 0x00000001UL #define UNIX_STREAM_SOCKET__READ 0x00000002UL #define UNIX_STREAM_SOCKET__WRITE 0x00000004UL @@ -415,11 +388,9 @@ #define UNIX_STREAM_SOCKET__SENDTO #define UNIX_STREAM_SOCKET__RECV_MSG 0x00080000UL #define UNIX_STREAM_SOCKET__SEND_MSG 0x00100000UL #define UNIX_STREAM_SOCKET__NAME_BIND 0x00200000UL - #define UNIX_STREAM_SOCKET__CONNECTTO 0x00400000UL #define UNIX_STREAM_SOCKET__NEWCONN 0x00800000UL #define UNIX_STREAM_SOCKET__ACCEPTFROM 0x01000000UL - #define UNIX_DGRAM_SOCKET__IOCTL 0x00000001UL #define UNIX_DGRAM_SOCKET__READ 0x00000002UL #define UNIX_DGRAM_SOCKET__WRITE 0x00000004UL @@ -442,7 +413,6 @@ #define UNIX_DGRAM_SOCKET__SENDTO #define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL #define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL #define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL - #define PROCESS__FORK 0x00000001UL #define PROCESS__TRANSITION 0x00000002UL #define PROCESS__SIGCHLD 0x00000004UL @@ -473,7 +443,6 @@ #define PROCESS__EXECSTACK #define PROCESS__EXECHEAP 0x08000000UL #define PROCESS__SETKEYCREATE 0x10000000UL #define PROCESS__SETSOCKCREATE 0x20000000UL - #define IPC__CREATE 0x00000001UL #define IPC__DESTROY 0x00000002UL #define IPC__GETATTR 0x00000004UL @@ -483,7 +452,6 @@ #define IPC__WRITE #define IPC__ASSOCIATE 0x00000040UL #define IPC__UNIX_READ 0x00000080UL #define IPC__UNIX_WRITE 0x00000100UL - #define SEM__CREATE 0x00000001UL #define SEM__DESTROY 0x00000002UL #define SEM__GETATTR 0x00000004UL @@ -493,7 +461,6 @@ #define SEM__WRITE #define SEM__ASSOCIATE 0x00000040UL #define SEM__UNIX_READ 0x00000080UL #define SEM__UNIX_WRITE 0x00000100UL - #define MSGQ__CREATE 0x00000001UL #define MSGQ__DESTROY 0x00000002UL #define MSGQ__GETATTR 0x00000004UL @@ -503,12 +470,9 @@ #define MSGQ__WRITE #define MSGQ__ASSOCIATE 0x00000040UL #define MSGQ__UNIX_READ 0x00000080UL #define MSGQ__UNIX_WRITE 0x00000100UL - #define MSGQ__ENQUEUE 0x00000200UL - #define MSG__SEND 0x00000001UL #define MSG__RECEIVE 0x00000002UL - #define SHM__CREATE 0x00000001UL #define SHM__DESTROY 0x00000002UL #define SHM__GETATTR 0x00000004UL @@ -518,9 +482,7 @@ #define SHM__WRITE #define SHM__ASSOCIATE 0x00000040UL #define SHM__UNIX_READ 0x00000080UL #define SHM__UNIX_WRITE 0x00000100UL - #define SHM__LOCK 0x00000200UL - #define SECURITY__COMPUTE_AV 0x00000001UL #define SECURITY__COMPUTE_CREATE 0x00000002UL #define SECURITY__COMPUTE_MEMBER 0x00000004UL @@ -532,12 +494,10 @@ #define SECURITY__SETENFORCE #define SECURITY__SETBOOL 0x00000100UL #define SECURITY__SETSECPARAM 0x00000200UL #define SECURITY__SETCHECKREQPROT 0x00000400UL - #define SYSTEM__IPC_INFO 0x00000001UL #define SYSTEM__SYSLOG_READ 0x00000002UL #define SYSTEM__SYSLOG_MOD 0x00000004UL #define SYSTEM__SYSLOG_CONSOLE 0x00000008UL - #define CAPABILITY__CHOWN 0x00000001UL #define CAPABILITY__DAC_OVERRIDE 0x00000002UL #define CAPABILITY__DAC_READ_SEARCH 0x00000004UL @@ -569,110 +529,6 @@ #define CAPABILITY__MKNOD #define CAPABILITY__LEASE 0x10000000UL #define CAPABILITY__AUDIT_WRITE 0x20000000UL #define CAPABILITY__AUDIT_CONTROL 0x40000000UL - -#define PASSWD__PASSWD 0x00000001UL -#define PASSWD__CHFN 0x00000002UL -#define PASSWD__CHSH 0x00000004UL -#define PASSWD__ROOTOK 0x00000008UL -#define PASSWD__CRONTAB 0x00000010UL - -#define DRAWABLE__CREATE 0x00000001UL -#define DRAWABLE__DESTROY 0x00000002UL -#define DRAWABLE__DRAW 0x00000004UL -#define DRAWABLE__COPY 0x00000008UL -#define DRAWABLE__GETATTR 0x00000010UL - -#define GC__CREATE 0x00000001UL -#define GC__FREE 0x00000002UL -#define GC__GETATTR 0x00000004UL -#define GC__SETATTR 0x00000008UL - -#define WINDOW__ADDCHILD 0x00000001UL -#define WINDOW__CREATE 0x00000002UL -#define WINDOW__DESTROY 0x00000004UL -#define WINDOW__MAP 0x00000008UL -#define WINDOW__UNMAP 0x00000010UL -#define WINDOW__CHSTACK 0x00000020UL -#define WINDOW__CHPROPLIST 0x00000040UL -#define WINDOW__CHPROP 0x00000080UL -#define WINDOW__LISTPROP 0x00000100UL -#define WINDOW__GETATTR 0x00000200UL -#define WINDOW__SETATTR 0x00000400UL -#define WINDOW__SETFOCUS 0x00000800UL -#define WINDOW__MOVE 0x00001000UL -#define WINDOW__CHSELECTION 0x00002000UL -#define WINDOW__CHPARENT 0x00004000UL -#define WINDOW__CTRLLIFE 0x00008000UL -#define WINDOW__ENUMERATE 0x00010000UL -#define WINDOW__TRANSPARENT 0x00020000UL -#define WINDOW__MOUSEMOTION 0x00040000UL -#define WINDOW__CLIENTCOMEVENT 0x00080000UL -#define WINDOW__INPUTEVENT 0x00100000UL -#define WINDOW__DRAWEVENT 0x00200000UL -#define WINDOW__WINDOWCHANGEEVENT 0x00400000UL -#define WINDOW__WINDOWCHANGEREQUEST 0x00800000UL -#define WINDOW__SERVERCHANGEEVENT 0x01000000UL -#define WINDOW__EXTENSIONEVENT 0x02000000UL - -#define FONT__LOAD 0x00000001UL -#define FONT__FREE 0x00000002UL -#define FONT__GETATTR 0x00000004UL -#define FONT__USE 0x00000008UL - -#define COLORMAP__CREATE 0x00000001UL -#define COLORMAP__FREE 0x00000002UL -#define COLORMAP__INSTALL 0x00000004UL -#define COLORMAP__UNINSTALL 0x00000008UL -#define COLORMAP__LIST 0x00000010UL -#define COLORMAP__READ 0x00000020UL -#define COLORMAP__STORE 0x00000040UL -#define COLORMAP__GETATTR 0x00000080UL -#define COLORMAP__SETATTR 0x00000100UL - -#define PROPERTY__CREATE 0x00000001UL -#define PROPERTY__FREE 0x00000002UL -#define PROPERTY__READ 0x00000004UL -#define PROPERTY__WRITE 0x00000008UL - -#define CURSOR__CREATE 0x00000001UL -#define CURSOR__CREATEGLYPH 0x00000002UL -#define CURSOR__FREE 0x00000004UL -#define CURSOR__ASSIGN 0x00000008UL -#define CURSOR__SETATTR 0x00000010UL - -#define XCLIENT__KILL 0x00000001UL - -#define XINPUT__LOOKUP 0x00000001UL -#define XINPUT__GETATTR 0x00000002UL -#define XINPUT__SETATTR 0x00000004UL -#define XINPUT__SETFOCUS 0x00000008UL -#define XINPUT__WARPPOINTER 0x00000010UL -#define XINPUT__ACTIVEGRAB 0x00000020UL -#define XINPUT__PASSIVEGRAB 0x00000040UL -#define XINPUT__UNGRAB 0x00000080UL -#define XINPUT__BELL 0x00000100UL -#define XINPUT__MOUSEMOTION 0x00000200UL -#define XINPUT__RELABELINPUT 0x00000400UL - -#define XSERVER__SCREENSAVER 0x00000001UL -#define XSERVER__GETHOSTLIST 0x00000002UL -#define XSERVER__SETHOSTLIST 0x00000004UL -#define XSERVER__GETFONTPATH 0x00000008UL -#define XSERVER__SETFONTPATH 0x00000010UL -#define XSERVER__GETATTR 0x00000020UL -#define XSERVER__GRAB 0x00000040UL -#define XSERVER__UNGRAB 0x00000080UL - -#define XEXTENSION__QUERY 0x00000001UL -#define XEXTENSION__USE 0x00000002UL - -#define PAX__PAGEEXEC 0x00000001UL -#define PAX__EMUTRAMP 0x00000002UL -#define PAX__MPROTECT 0x00000004UL -#define PAX__RANDMMAP 0x00000008UL -#define PAX__RANDEXEC 0x00000010UL -#define PAX__SEGMEXEC 0x00000020UL - #define NETLINK_ROUTE_SOCKET__IOCTL 0x00000001UL #define NETLINK_ROUTE_SOCKET__READ 0x00000002UL #define NETLINK_ROUTE_SOCKET__WRITE 0x00000004UL @@ -695,10 +551,8 @@ #define NETLINK_ROUTE_SOCKET__SENDTO #define NETLINK_ROUTE_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_ROUTE_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_ROUTE_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_ROUTE_SOCKET__NLMSG_READ 0x00400000UL #define NETLINK_ROUTE_SOCKET__NLMSG_WRITE 0x00800000UL - #define NETLINK_FIREWALL_SOCKET__IOCTL 0x00000001UL #define NETLINK_FIREWALL_SOCKET__READ 0x00000002UL #define NETLINK_FIREWALL_SOCKET__WRITE 0x00000004UL @@ -721,10 +575,8 @@ #define NETLINK_FIREWALL_SOCKET__SENDTO #define NETLINK_FIREWALL_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_FIREWALL_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_FIREWALL_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_FIREWALL_SOCKET__NLMSG_READ 0x00400000UL #define NETLINK_FIREWALL_SOCKET__NLMSG_WRITE 0x00800000UL - #define NETLINK_TCPDIAG_SOCKET__IOCTL 0x00000001UL #define NETLINK_TCPDIAG_SOCKET__READ 0x00000002UL #define NETLINK_TCPDIAG_SOCKET__WRITE 0x00000004UL @@ -747,10 +599,8 @@ #define NETLINK_TCPDIAG_SOCKET__SENDTO #define NETLINK_TCPDIAG_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_TCPDIAG_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_TCPDIAG_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_TCPDIAG_SOCKET__NLMSG_READ 0x00400000UL #define NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE 0x00800000UL - #define NETLINK_NFLOG_SOCKET__IOCTL 0x00000001UL #define NETLINK_NFLOG_SOCKET__READ 0x00000002UL #define NETLINK_NFLOG_SOCKET__WRITE 0x00000004UL @@ -773,7 +623,6 @@ #define NETLINK_NFLOG_SOCKET__SENDTO #define NETLINK_NFLOG_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_NFLOG_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_NFLOG_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_XFRM_SOCKET__IOCTL 0x00000001UL #define NETLINK_XFRM_SOCKET__READ 0x00000002UL #define NETLINK_XFRM_SOCKET__WRITE 0x00000004UL @@ -796,10 +645,8 @@ #define NETLINK_XFRM_SOCKET__SENDTO #define NETLINK_XFRM_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_XFRM_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_XFRM_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_XFRM_SOCKET__NLMSG_READ 0x00400000UL #define NETLINK_XFRM_SOCKET__NLMSG_WRITE 0x00800000UL - #define NETLINK_SELINUX_SOCKET__IOCTL 0x00000001UL #define NETLINK_SELINUX_SOCKET__READ 0x00000002UL #define NETLINK_SELINUX_SOCKET__WRITE 0x00000004UL @@ -822,7 +669,6 @@ #define NETLINK_SELINUX_SOCKET__SENDTO #define NETLINK_SELINUX_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_SELINUX_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_SELINUX_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_AUDIT_SOCKET__IOCTL 0x00000001UL #define NETLINK_AUDIT_SOCKET__READ 0x00000002UL #define NETLINK_AUDIT_SOCKET__WRITE 0x00000004UL @@ -845,12 +691,10 @@ #define NETLINK_AUDIT_SOCKET__SENDTO #define NETLINK_AUDIT_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_AUDIT_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_AUDIT_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_AUDIT_SOCKET__NLMSG_READ 0x00400000UL #define NETLINK_AUDIT_SOCKET__NLMSG_WRITE 0x00800000UL #define NETLINK_AUDIT_SOCKET__NLMSG_RELAY 0x01000000UL #define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV 0x02000000UL - #define NETLINK_IP6FW_SOCKET__IOCTL 0x00000001UL #define NETLINK_IP6FW_SOCKET__READ 0x00000002UL #define NETLINK_IP6FW_SOCKET__WRITE 0x00000004UL @@ -873,10 +717,8 @@ #define NETLINK_IP6FW_SOCKET__SENDTO #define NETLINK_IP6FW_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_IP6FW_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_IP6FW_SOCKET__NAME_BIND 0x00200000UL - #define NETLINK_IP6FW_SOCKET__NLMSG_READ 0x00400000UL #define NETLINK_IP6FW_SOCKET__NLMSG_WRITE 0x00800000UL - #define NETLINK_DNRT_SOCKET__IOCTL 0x00000001UL #define NETLINK_DNRT_SOCKET__READ 0x00000002UL #define NETLINK_DNRT_SOCKET__WRITE 0x00000004UL @@ -899,24 +741,10 @@ #define NETLINK_DNRT_SOCKET__SENDTO #define NETLINK_DNRT_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_DNRT_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_DNRT_SOCKET__NAME_BIND 0x00200000UL - -#define DBUS__ACQUIRE_SVC 0x00000001UL -#define DBUS__SEND_MSG 0x00000002UL - -#define NSCD__GETPWD 0x00000001UL -#define NSCD__GETGRP 0x00000002UL -#define NSCD__GETHOST 0x00000004UL -#define NSCD__GETSTAT 0x00000008UL -#define NSCD__ADMIN 0x00000010UL -#define NSCD__SHMEMPWD 0x00000020UL -#define NSCD__SHMEMGRP 0x00000040UL -#define NSCD__SHMEMHOST 0x00000080UL - #define ASSOCIATION__SENDTO 0x00000001UL #define ASSOCIATION__RECVFROM 0x00000002UL #define ASSOCIATION__SETCONTEXT 0x00000004UL #define ASSOCIATION__POLMATCH 0x00000008UL - #define NETLINK_KOBJECT_UEVENT_SOCKET__IOCTL 0x00000001UL #define NETLINK_KOBJECT_UEVENT_SOCKET__READ 0x00000002UL #define NETLINK_KOBJECT_UEVENT_SOCKET__WRITE 0x00000004UL @@ -939,7 +767,6 @@ #define NETLINK_KOBJECT_UEVENT_SOCKET__S #define NETLINK_KOBJECT_UEVENT_SOCKET__RECV_MSG 0x00080000UL #define NETLINK_KOBJECT_UEVENT_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_KOBJECT_UEVENT_SOCKET__NAME_BIND 0x00200000UL - #define APPLETALK_SOCKET__IOCTL 0x00000001UL #define APPLETALK_SOCKET__READ 0x00000002UL #define APPLETALK_SOCKET__WRITE 0x00000004UL @@ -962,11 +789,9 @@ #define APPLETALK_SOCKET__SENDTO #define APPLETALK_SOCKET__RECV_MSG 0x00080000UL #define APPLETALK_SOCKET__SEND_MSG 0x00100000UL #define APPLETALK_SOCKET__NAME_BIND 0x00200000UL - #define PACKET__SEND 0x00000001UL #define PACKET__RECV 0x00000002UL #define PACKET__RELABELTO 0x00000004UL - #define KEY__VIEW 0x00000001UL #define KEY__READ 0x00000002UL #define KEY__WRITE 0x00000004UL @@ -974,10 +799,6 @@ #define KEY__SEARCH #define KEY__LINK 0x00000010UL #define KEY__SETATTR 0x00000020UL #define KEY__CREATE 0x00000040UL - -#define CONTEXT__TRANSLATE 0x00000001UL -#define CONTEXT__CONTAINS 0x00000002UL - #define DCCP_SOCKET__IOCTL 0x00000001UL #define DCCP_SOCKET__READ 0x00000002UL #define DCCP_SOCKET__WRITE 0x00000004UL diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h index 9f3ebb1..3787990 100644 --- a/security/selinux/include/class_to_string.h +++ b/security/selinux/include/class_to_string.h @@ -2,7 +2,7 @@ /* * Security object class definitions */ - S_("null") + S_(NULL) S_("security") S_("process") S_("system") @@ -32,19 +32,19 @@ S_("msgq") S_("shm") S_("ipc") - S_("passwd") - S_("drawable") - S_("window") - S_("gc") - S_("font") - S_("colormap") - S_("property") - S_("cursor") - S_("xclient") - S_("xinput") - S_("xserver") - S_("xextension") - S_("pax") + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) S_("netlink_route_socket") S_("netlink_firewall_socket") S_("netlink_tcpdiag_socket") @@ -54,12 +54,12 @@ S_("netlink_audit_socket") S_("netlink_ip6fw_socket") S_("netlink_dnrt_socket") - S_("dbus") - S_("nscd") + S_(NULL) + S_(NULL) S_("association") S_("netlink_kobject_uevent_socket") S_("appletalk_socket") S_("packet") S_("key") - S_("context") + S_(NULL) S_("dccp_socket") diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h index 67cef37..35f309f 100644 --- a/security/selinux/include/flask.h +++ b/security/selinux/include/flask.h @@ -34,19 +34,6 @@ #define SECCLASS_MSG #define SECCLASS_MSGQ 27 #define SECCLASS_SHM 28 #define SECCLASS_IPC 29 -#define SECCLASS_PASSWD 30 -#define SECCLASS_DRAWABLE 31 -#define SECCLASS_WINDOW 32 -#define SECCLASS_GC 33 -#define SECCLASS_FONT 34 -#define SECCLASS_COLORMAP 35 -#define SECCLASS_PROPERTY 36 -#define SECCLASS_CURSOR 37 -#define SECCLASS_XCLIENT 38 -#define SECCLASS_XINPUT 39 -#define SECCLASS_XSERVER 40 -#define SECCLASS_XEXTENSION 41 -#define SECCLASS_PAX 42 #define SECCLASS_NETLINK_ROUTE_SOCKET 43 #define SECCLASS_NETLINK_FIREWALL_SOCKET 44 #define SECCLASS_NETLINK_TCPDIAG_SOCKET 45 @@ -56,14 +43,11 @@ #define SECCLASS_NETLINK_SELINUX_SOCKET #define SECCLASS_NETLINK_AUDIT_SOCKET 49 #define SECCLASS_NETLINK_IP6FW_SOCKET 50 #define SECCLASS_NETLINK_DNRT_SOCKET 51 -#define SECCLASS_DBUS 52 -#define SECCLASS_NSCD 53 #define SECCLASS_ASSOCIATION 54 #define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55 #define SECCLASS_APPLETALK_SOCKET 56 #define SECCLASS_PACKET 57 #define SECCLASS_KEY 58 -#define SECCLASS_CONTEXT 59 #define SECCLASS_DCCP_SOCKET 60 /* diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h new file mode 100644 index 0000000..218e3f7 --- /dev/null +++ b/security/selinux/include/netlabel.h @@ -0,0 +1,121 @@ +/* + * SELinux interface to the NetLabel subsystem + * + * Author : Paul Moore + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _SELINUX_NETLABEL_H_ +#define _SELINUX_NETLABEL_H_ + +#include +#include +#include +#include +#include + +#include "avc.h" +#include "objsec.h" + +#ifdef CONFIG_NETLABEL +void selinux_netlbl_cache_invalidate(void); + +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, + int family); +void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, + int family); +void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, + struct sk_security_struct *newssec); + +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid); + +void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); +int selinux_netlbl_socket_post_create(struct socket *sock); +int selinux_netlbl_inode_permission(struct inode *inode, int mask); +int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, + struct sk_buff *skb, + struct avc_audit_data *ad); +int selinux_netlbl_socket_setsockopt(struct socket *sock, + int level, + int optname); +#else +static inline void selinux_netlbl_cache_invalidate(void) +{ + return; +} + +static inline void selinux_netlbl_sk_security_reset( + struct sk_security_struct *ssec, + int family) +{ + return; +} +static inline void selinux_netlbl_sk_security_init( + struct sk_security_struct *ssec, + int family) +{ + return; +} +static inline void selinux_netlbl_sk_security_clone( + struct sk_security_struct *ssec, + struct sk_security_struct *newssec) +{ + return; +} + +static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u32 base_sid, + u32 *sid) +{ + *sid = SECSID_NULL; + return 0; +} + +static inline void selinux_netlbl_sock_graft(struct sock *sk, + struct socket *sock) +{ + return; +} +static inline int selinux_netlbl_socket_post_create(struct socket *sock) +{ + return 0; +} +static inline int selinux_netlbl_inode_permission(struct inode *inode, + int mask) +{ + return 0; +} +static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, + struct sk_buff *skb, + struct avc_audit_data *ad) +{ + return 0; +} +static inline int selinux_netlbl_socket_setsockopt(struct socket *sock, + int level, + int optname) +{ + return 0; +} +#endif /* CONFIG_NETLABEL */ + +#endif diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 210eec7..b94378a 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -34,7 +34,7 @@ #else #define POLICYDB_VERSION_MAX POLICYDB_VERSION_RANGETRANS #endif -struct sk_buff; +struct netlbl_lsm_secattr; extern int selinux_enabled; extern int selinux_mls_enabled; @@ -82,8 +82,6 @@ int security_netif_sid(char *name, u32 * int security_node_sid(u16 domain, void *addr, u32 addrlen, u32 *out_sid); -void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid); - int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); @@ -102,5 +100,30 @@ int security_fs_use(const char *fstype, int security_genfs_sid(const char *fstype, char *name, u16 sclass, u32 *sid); +#ifdef CONFIG_NETLABEL +int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, + u32 base_sid, + u32 *sid); + +int security_netlbl_sid_to_secattr(u32 sid, + struct netlbl_lsm_secattr *secattr); +#else +static inline int security_netlbl_secattr_to_sid( + struct netlbl_lsm_secattr *secattr, + u32 base_sid, + u32 *sid) +{ + return -EIDRM; +} + +static inline int security_netlbl_sid_to_secattr(u32 sid, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOENT; +} +#endif /* CONFIG_NETLABEL */ + +const char *security_get_initial_sid_context(u32 sid); + #endif /* _SELINUX_SECURITY_H_ */ diff --git a/security/selinux/include/selinux_netlabel.h b/security/selinux/include/selinux_netlabel.h deleted file mode 100644 index 2a732c9..0000000 --- a/security/selinux/include/selinux_netlabel.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * SELinux interface to the NetLabel subsystem - * - * Author : Paul Moore - * - */ - -/* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef _SELINUX_NETLABEL_H_ -#define _SELINUX_NETLABEL_H_ - -#include -#include -#include -#include -#include - -#include "avc.h" -#include "objsec.h" - -#ifdef CONFIG_NETLABEL -void selinux_netlbl_cache_invalidate(void); -int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid); -int selinux_netlbl_socket_post_create(struct socket *sock); -void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); -int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, - struct sk_buff *skb, - struct avc_audit_data *ad); -void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, - int family); -void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, - int family); -void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, - struct sk_security_struct *newssec); -int selinux_netlbl_inode_permission(struct inode *inode, int mask); -int selinux_netlbl_socket_setsockopt(struct socket *sock, - int level, - int optname); -#else -static inline void selinux_netlbl_cache_invalidate(void) -{ - return; -} - -static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, - u32 base_sid, - u32 *sid) -{ - *sid = SECSID_NULL; - return 0; -} - -static inline int selinux_netlbl_socket_post_create(struct socket *sock) -{ - return 0; -} - -static inline void selinux_netlbl_sock_graft(struct sock *sk, - struct socket *sock) -{ - return; -} - -static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, - struct sk_buff *skb, - struct avc_audit_data *ad) -{ - return 0; -} - -static inline void selinux_netlbl_sk_security_reset( - struct sk_security_struct *ssec, - int family) -{ - return; -} - -static inline void selinux_netlbl_sk_security_init( - struct sk_security_struct *ssec, - int family) -{ - return; -} - -static inline void selinux_netlbl_sk_security_clone( - struct sk_security_struct *ssec, - struct sk_security_struct *newssec) -{ - return; -} - -static inline int selinux_netlbl_inode_permission(struct inode *inode, - int mask) -{ - return 0; -} - -static inline int selinux_netlbl_socket_setsockopt(struct socket *sock, - int level, - int optname) -{ - return 0; -} -#endif /* CONFIG_NETLABEL */ - -#endif diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c new file mode 100644 index 0000000..bf87507 --- /dev/null +++ b/security/selinux/netlabel.c @@ -0,0 +1,363 @@ +/* + * SELinux NetLabel Support + * + * This file provides the necessary glue to tie NetLabel into the SELinux + * subsystem. + * + * Author: Paul Moore + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include "objsec.h" +#include "security.h" + +/** + * selinux_netlbl_socket_setsid - Label a socket using the NetLabel mechanism + * @sock: the socket to label + * @sid: the SID to use + * + * Description: + * Attempt to label a socket using the NetLabel mechanism using the given + * SID. Returns zero values on success, negative values on failure. The + * caller is responsibile for calling rcu_read_lock() before calling this + * this function and rcu_read_unlock() after this function returns. + * + */ +static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) +{ + int rc; + struct sk_security_struct *sksec = sock->sk->sk_security; + struct netlbl_lsm_secattr secattr; + + rc = security_netlbl_sid_to_secattr(sid, &secattr); + if (rc != 0) + return rc; + + rc = netlbl_socket_setattr(sock, &secattr); + if (rc == 0) { + spin_lock_bh(&sksec->nlbl_lock); + sksec->nlbl_state = NLBL_LABELED; + spin_unlock_bh(&sksec->nlbl_lock); + } + + return rc; +} + +/** + * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache + * + * Description: + * Invalidate the NetLabel security attribute mapping cache. + * + */ +void selinux_netlbl_cache_invalidate(void) +{ + netlbl_cache_invalidate(); +} + +/** + * selinux_netlbl_sk_security_reset - Reset the NetLabel fields + * @ssec: the sk_security_struct + * @family: the socket family + * + * Description: + * Called when the NetLabel state of a sk_security_struct needs to be reset. + * The caller is responsibile for all the NetLabel sk_security_struct locking. + * + */ +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, + int family) +{ + if (family == PF_INET) + ssec->nlbl_state = NLBL_REQUIRE; + else + ssec->nlbl_state = NLBL_UNSET; +} + +/** + * selinux_netlbl_sk_security_init - Setup the NetLabel fields + * @ssec: the sk_security_struct + * @family: the socket family + * + * Description: + * Called when a new sk_security_struct is allocated to initialize the NetLabel + * fields. + * + */ +void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, + int family) +{ + /* No locking needed, we are the only one who has access to ssec */ + selinux_netlbl_sk_security_reset(ssec, family); + spin_lock_init(&ssec->nlbl_lock); +} + +/** + * selinux_netlbl_sk_security_clone - Copy the NetLabel fields + * @ssec: the original sk_security_struct + * @newssec: the cloned sk_security_struct + * + * Description: + * Clone the NetLabel specific sk_security_struct fields from @ssec to + * @newssec. + * + */ +void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, + struct sk_security_struct *newssec) +{ + /* We don't need to take newssec->nlbl_lock because we are the only + * thread with access to newssec, but we do need to take the RCU read + * lock as other threads could have access to ssec */ + rcu_read_lock(); + selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); + newssec->sclass = ssec->sclass; + rcu_read_unlock(); +} + +/** + * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel + * @skb: the packet + * @base_sid: the SELinux SID to use as a context for MLS only attributes + * @sid: the SID + * + * Description: + * Call the NetLabel mechanism to get the security attributes of the given + * packet and use those attributes to determine the correct context/SID to + * assign to the packet. Returns zero on success, negative values on failure. + * + */ +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) +{ + int rc; + struct netlbl_lsm_secattr secattr; + + netlbl_secattr_init(&secattr); + rc = netlbl_skbuff_getattr(skb, &secattr); + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) + rc = security_netlbl_secattr_to_sid(&secattr, + base_sid, + sid); + else + *sid = SECSID_NULL; + netlbl_secattr_destroy(&secattr); + + return rc; +} + +/** + * selinux_netlbl_sock_graft - Netlabel the new socket + * @sk: the new connection + * @sock: the new socket + * + * Description: + * The connection represented by @sk is being grafted onto @sock so set the + * socket's NetLabel to match the SID of @sk. + * + */ +void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) +{ + struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr secattr; + u32 nlbl_peer_sid; + + sksec->sclass = isec->sclass; + + rcu_read_lock(); + + if (sksec->nlbl_state != NLBL_REQUIRE) { + rcu_read_unlock(); + return; + } + + netlbl_secattr_init(&secattr); + if (netlbl_sock_getattr(sk, &secattr) == 0 && + secattr.flags != NETLBL_SECATTR_NONE && + security_netlbl_secattr_to_sid(&secattr, + SECINITSID_UNLABELED, + &nlbl_peer_sid) == 0) + sksec->peer_sid = nlbl_peer_sid; + netlbl_secattr_destroy(&secattr); + + /* Try to set the NetLabel on the socket to save time later, if we fail + * here we will pick up the pieces in later calls to + * selinux_netlbl_inode_permission(). */ + selinux_netlbl_socket_setsid(sock, sksec->sid); + + rcu_read_unlock(); +} + +/** + * selinux_netlbl_socket_post_create - Label a socket using NetLabel + * @sock: the socket to label + * + * Description: + * Attempt to label a socket using the NetLabel mechanism using the given + * SID. Returns zero values on success, negative values on failure. + * + */ +int selinux_netlbl_socket_post_create(struct socket *sock) +{ + int rc = 0; + struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; + struct sk_security_struct *sksec = sock->sk->sk_security; + + sksec->sclass = isec->sclass; + + rcu_read_lock(); + if (sksec->nlbl_state == NLBL_REQUIRE) + rc = selinux_netlbl_socket_setsid(sock, sksec->sid); + rcu_read_unlock(); + + return rc; +} + +/** + * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled + * @inode: the file descriptor's inode + * @mask: the permission mask + * + * Description: + * Looks at a file's inode and if it is marked as a socket protected by + * NetLabel then verify that the socket has been labeled, if not try to label + * the socket now with the inode's SID. Returns zero on success, negative + * values on failure. + * + */ +int selinux_netlbl_inode_permission(struct inode *inode, int mask) +{ + int rc; + struct sk_security_struct *sksec; + struct socket *sock; + + if (!S_ISSOCK(inode->i_mode) || + ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) + return 0; + sock = SOCKET_I(inode); + sksec = sock->sk->sk_security; + + rcu_read_lock(); + if (sksec->nlbl_state != NLBL_REQUIRE) { + rcu_read_unlock(); + return 0; + } + local_bh_disable(); + bh_lock_sock_nested(sock->sk); + rc = selinux_netlbl_socket_setsid(sock, sksec->sid); + bh_unlock_sock(sock->sk); + local_bh_enable(); + rcu_read_unlock(); + + return rc; +} + +/** + * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel + * @sksec: the sock's sk_security_struct + * @skb: the packet + * @ad: the audit data + * + * Description: + * Fetch the NetLabel security attributes from @skb and perform an access check + * against the receiving socket. Returns zero on success, negative values on + * error. + * + */ +int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, + struct sk_buff *skb, + struct avc_audit_data *ad) +{ + int rc; + u32 netlbl_sid; + u32 recv_perm; + + rc = selinux_netlbl_skbuff_getsid(skb, + SECINITSID_UNLABELED, + &netlbl_sid); + if (rc != 0) + return rc; + + if (netlbl_sid == SECSID_NULL) + return 0; + + switch (sksec->sclass) { + case SECCLASS_UDP_SOCKET: + recv_perm = UDP_SOCKET__RECVFROM; + break; + case SECCLASS_TCP_SOCKET: + recv_perm = TCP_SOCKET__RECVFROM; + break; + default: + recv_perm = RAWIP_SOCKET__RECVFROM; + } + + rc = avc_has_perm(sksec->sid, + netlbl_sid, + sksec->sclass, + recv_perm, + ad); + if (rc == 0) + return 0; + + netlbl_skbuff_err(skb, rc); + return rc; +} + +/** + * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel + * @sock: the socket + * @level: the socket level or protocol + * @optname: the socket option name + * + * Description: + * Check the setsockopt() call and if the user is trying to replace the IP + * options on a socket and a NetLabel is in place for the socket deny the + * access; otherwise allow the access. Returns zero when the access is + * allowed, -EACCES when denied, and other negative values on error. + * + */ +int selinux_netlbl_socket_setsockopt(struct socket *sock, + int level, + int optname) +{ + int rc = 0; + struct sk_security_struct *sksec = sock->sk->sk_security; + struct netlbl_lsm_secattr secattr; + + rcu_read_lock(); + if (level == IPPROTO_IP && optname == IP_OPTIONS && + sksec->nlbl_state == NLBL_LABELED) { + netlbl_secattr_init(&secattr); + rc = netlbl_socket_getattr(sock, &secattr); + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) + rc = -EACCES; + netlbl_secattr_destroy(&secattr); + } + rcu_read_unlock(); + + return rc; +} diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c index e203883..f49046d 100644 --- a/security/selinux/netlink.c +++ b/security/selinux/netlink.c @@ -66,7 +66,7 @@ static void selnl_add_payload(struct nlm static void selnl_notify(int msgtype, void *data) { int len; - unsigned char *tmp; + sk_buff_data_t tmp; struct sk_buff *skb; struct nlmsghdr *nlh; @@ -104,7 +104,7 @@ void selnl_notify_policyload(u32 seqno) static int __init selnl_init(void) { - selnl = netlink_kernel_create(NETLINK_SELINUX, SELNLGRP_MAX, NULL, + selnl = netlink_kernel_create(NETLINK_SELINUX, SELNLGRP_MAX, NULL, NULL, THIS_MODULE); if (selnl == NULL) panic("SELinux: Cannot create netlink socket."); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 93b3177..aca099a 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -96,12 +96,18 @@ enum sel_inos { SEL_COMMIT_BOOLS, /* commit new boolean values */ SEL_MLS, /* return if MLS policy is enabled */ SEL_DISABLE, /* disable SELinux until next reboot */ - SEL_AVC, /* AVC management directory */ SEL_MEMBER, /* compute polyinstantiation membership decision */ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ SEL_COMPAT_NET, /* whether to use old compat network packet controls */ + SEL_INO_NEXT, /* The next inode number to use */ }; +static unsigned long sel_last_ino = SEL_INO_NEXT - 1; + +#define SEL_INITCON_INO_OFFSET 0x01000000 +#define SEL_BOOL_INO_OFFSET 0x02000000 +#define SEL_INO_MASK 0x00ffffff + #define TMPBUFLEN 12 static ssize_t sel_read_enforce(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -777,8 +783,6 @@ static struct inode *sel_make_inode(stru return ret; } -#define BOOL_INO_OFFSET 30 - static ssize_t sel_read_bool(struct file *filep, char __user *buf, size_t count, loff_t *ppos) { @@ -806,14 +810,14 @@ static ssize_t sel_read_bool(struct file } inode = filep->f_path.dentry->d_inode; - cur_enforcing = security_get_bool_value(inode->i_ino - BOOL_INO_OFFSET); + cur_enforcing = security_get_bool_value(inode->i_ino&SEL_INO_MASK); if (cur_enforcing < 0) { ret = cur_enforcing; goto out; } length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, - bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]); + bool_pending_values[inode->i_ino&SEL_INO_MASK]); ret = simple_read_from_buffer(buf, count, ppos, page, length); out: mutex_unlock(&sel_mutex); @@ -865,7 +869,7 @@ static ssize_t sel_write_bool(struct fil new_value = 1; inode = filep->f_path.dentry->d_inode; - bool_pending_values[inode->i_ino - BOOL_INO_OFFSET] = new_value; + bool_pending_values[inode->i_ino&SEL_INO_MASK] = new_value; length = count; out: @@ -1029,7 +1033,7 @@ static int sel_make_bools(void) isec->sid = sid; isec->initialized = 1; inode->i_fop = &sel_bool_ops; - inode->i_ino = i + BOOL_INO_OFFSET; + inode->i_ino = i|SEL_BOOL_INO_OFFSET; d_add(dentry, inode); } bool_num = num; @@ -1234,6 +1238,56 @@ #endif goto out; } inode->i_fop = files[i].ops; + inode->i_ino = ++sel_last_ino; + d_add(dentry, inode); + } +out: + return ret; +} + +static ssize_t sel_read_initcon(struct file * file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode; + char *con; + u32 sid, len; + ssize_t ret; + + inode = file->f_path.dentry->d_inode; + sid = inode->i_ino&SEL_INO_MASK; + ret = security_sid_to_context(sid, &con, &len); + if (ret < 0) + return ret; + + ret = simple_read_from_buffer(buf, count, ppos, con, len); + kfree(con); + return ret; +} + +static const struct file_operations sel_initcon_ops = { + .read = sel_read_initcon, +}; + +static int sel_make_initcon_files(struct dentry *dir) +{ + int i, ret = 0; + + for (i = 1; i <= SECINITSID_NUM; i++) { + struct inode *inode; + struct dentry *dentry; + dentry = d_alloc_name(dir, security_get_initial_sid_context(i)); + if (!dentry) { + ret = -ENOMEM; + goto out; + } + + inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); + if (!inode) { + ret = -ENOMEM; + goto out; + } + inode->i_fop = &sel_initcon_ops; + inode->i_ino = i|SEL_INITCON_INO_OFFSET; d_add(dentry, inode); } out: @@ -1252,6 +1306,7 @@ static int sel_make_dir(struct inode *di } inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; + inode->i_ino = ++sel_last_ino; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inc_nlink(inode); d_add(dentry, inode); @@ -1314,6 +1369,7 @@ static int sel_fill_super(struct super_b ret = -ENOMEM; goto err; } + inode->i_ino = ++sel_last_ino; isec = (struct inode_security_struct*)inode->i_security; isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; @@ -1336,6 +1392,21 @@ static int sel_fill_super(struct super_b ret = sel_make_avc_files(dentry); if (ret) goto err; + + dentry = d_alloc_name(sb->s_root, "initial_contexts"); + if (!dentry) { + ret = -ENOMEM; + goto err; + } + + ret = sel_make_dir(root_inode, dentry); + if (ret) + goto err; + + ret = sel_make_initcon_files(dentry); + if (ret) + goto err; + out: return ret; err: diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1e52356..40660ff 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -39,7 +39,6 @@ #include #include #include #include -#include #include #include "flask.h" @@ -53,7 +52,7 @@ #include "services.h" #include "conditional.h" #include "mls.h" #include "objsec.h" -#include "selinux_netlabel.h" +#include "netlabel.h" #include "xfrm.h" #include "ebitmap.h" @@ -594,6 +593,13 @@ static int context_struct_to_string(stru #include "initial_sid_to_string.h" +const char *security_get_initial_sid_context(u32 sid) +{ + if (unlikely(sid > SECINITSID_NUM)) + return NULL; + return initial_sid_to_string[sid]; +} + /** * security_sid_to_context - Obtain a context for a given SID. * @sid: security identifier, SID @@ -1050,6 +1056,8 @@ static int validate_classes(struct polic for (i = 1; i < kdefs->cts_len; i++) { def_class = kdefs->class_to_string[i]; + if (!def_class) + continue; if (i > p->p_classes.nprim) { printk(KERN_INFO "security: class %s not defined in policy\n", @@ -1249,6 +1257,7 @@ bad: } extern void selinux_complete_init(void); +static int security_preserve_bools(struct policydb *p); /** * security_load_policy - Load a security policy configuration. @@ -1325,6 +1334,12 @@ #endif goto err; } + rc = security_preserve_bools(&newpolicydb); + if (rc) { + printk(KERN_ERR "security: unable to preserve booleans\n"); + goto err; + } + /* Clone the SID table. */ sidtab_shutdown(&sidtab); if (sidtab_map(&sidtab, clone_sid, &newsidtab)) { @@ -1882,6 +1897,37 @@ out: return rc; } +static int security_preserve_bools(struct policydb *p) +{ + int rc, nbools = 0, *bvalues = NULL, i; + char **bnames = NULL; + struct cond_bool_datum *booldatum; + struct cond_node *cur; + + rc = security_get_bools(&nbools, &bnames, &bvalues); + if (rc) + goto out; + for (i = 0; i < nbools; i++) { + booldatum = hashtab_search(p->p_bools.table, bnames[i]); + if (booldatum) + booldatum->state = bvalues[i]; + } + for (cur = p->cond_list; cur != NULL; cur = cur->next) { + rc = evaluate_cond_node(p, cur); + if (rc) + goto out; + } + +out: + if (bnames) { + for (i = 0; i < nbools; i++) + kfree(bnames[i]); + } + kfree(bnames); + kfree(bvalues); + return rc; +} + /* * security_sid_mls_copy() - computes a new sid based on the given * sid and the mls portion of mls_sid. @@ -2198,41 +2244,15 @@ void selinux_audit_set_callback(int (*ca aurule_callback = callback; } -/** - * security_skb_extlbl_sid - Determine the external label of a packet - * @skb: the packet - * @base_sid: the SELinux SID to use as a context for MLS only external labels - * @sid: the packet's SID - * - * Description: - * Check the various different forms of external packet labeling and determine - * the external SID for the packet. - * - */ -void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid) -{ - u32 xfrm_sid; - u32 nlbl_sid; - - selinux_skb_xfrm_sid(skb, &xfrm_sid); - if (selinux_netlbl_skbuff_getsid(skb, - (xfrm_sid == SECSID_NULL ? - base_sid : xfrm_sid), - &nlbl_sid) != 0) - nlbl_sid = SECSID_NULL; - - *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); -} - #ifdef CONFIG_NETLABEL /* - * This is the structure we store inside the NetLabel cache block. + * NetLabel cache structure */ -#define NETLBL_CACHE(x) ((struct netlbl_cache *)(x)) +#define NETLBL_CACHE(x) ((struct selinux_netlbl_cache *)(x)) #define NETLBL_CACHE_T_NONE 0 #define NETLBL_CACHE_T_SID 1 #define NETLBL_CACHE_T_MLS 2 -struct netlbl_cache { +struct selinux_netlbl_cache { u32 type; union { u32 sid; @@ -2241,7 +2261,7 @@ struct netlbl_cache { }; /** - * selinux_netlbl_cache_free - Free the NetLabel cached data + * security_netlbl_cache_free - Free the NetLabel cached data * @data: the data to free * * Description: @@ -2249,9 +2269,9 @@ struct netlbl_cache { * netlbl_lsm_cache structure. * */ -static void selinux_netlbl_cache_free(const void *data) +static void security_netlbl_cache_free(const void *data) { - struct netlbl_cache *cache; + struct selinux_netlbl_cache *cache; if (data == NULL) return; @@ -2266,33 +2286,33 @@ static void selinux_netlbl_cache_free(co } /** - * selinux_netlbl_cache_add - Add an entry to the NetLabel cache - * @skb: the packet + * security_netlbl_cache_add - Add an entry to the NetLabel cache + * @secattr: the NetLabel packet security attributes * @ctx: the SELinux context * * Description: * Attempt to cache the context in @ctx, which was derived from the packet in - * @skb, in the NetLabel subsystem cache. + * @skb, in the NetLabel subsystem cache. This function assumes @secattr has + * already been initialized. * */ -static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx) +static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr, + struct context *ctx) { - struct netlbl_cache *cache = NULL; - struct netlbl_lsm_secattr secattr; + struct selinux_netlbl_cache *cache = NULL; - netlbl_secattr_init(&secattr); - secattr.cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); - if (secattr.cache == NULL) - goto netlbl_cache_add_return; + secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); + if (secattr->cache == NULL) + return; cache = kzalloc(sizeof(*cache), GFP_ATOMIC); if (cache == NULL) - goto netlbl_cache_add_return; + return; cache->type = NETLBL_CACHE_T_MLS; if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, &ctx->range.level[0].cat) != 0) - goto netlbl_cache_add_return; + return; cache->data.mls_label.level[1].cat.highbit = cache->data.mls_label.level[0].cat.highbit; cache->data.mls_label.level[1].cat.node = @@ -2300,52 +2320,40 @@ static void selinux_netlbl_cache_add(str cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; - secattr.cache->free = selinux_netlbl_cache_free; - secattr.cache->data = (void *)cache; - secattr.flags = NETLBL_SECATTR_CACHE; - - netlbl_cache_add(skb, &secattr); - -netlbl_cache_add_return: - netlbl_secattr_destroy(&secattr); + secattr->cache->free = security_netlbl_cache_free; + secattr->cache->data = (void *)cache; + secattr->flags |= NETLBL_SECATTR_CACHE; } /** - * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache - * - * Description: - * Invalidate the NetLabel security attribute mapping cache. - * - */ -void selinux_netlbl_cache_invalidate(void) -{ - netlbl_cache_invalidate(); -} - -/** - * selinux_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID - * @skb: the network packet + * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID * @secattr: the NetLabel packet security attributes * @base_sid: the SELinux SID to use as a context for MLS only attributes * @sid: the SELinux SID * * Description: - * Convert the given NetLabel packet security attributes in @secattr into a + * Convert the given NetLabel security attributes in @secattr into a * SELinux SID. If the @secattr field does not contain a full SELinux - * SID/context then use the context in @base_sid as the foundation. If @skb - * is not NULL attempt to cache as much data as possibile. Returns zero on - * success, negative values on failure. + * SID/context then use the context in @base_sid as the foundation. If + * possibile the 'cache' field of @secattr is set and the CACHE flag is set; + * this is to allow the @secattr to be used by NetLabel to cache the secattr to + * SID conversion for future lookups. Returns zero on success, negative + * values on failure. * */ -static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, - struct netlbl_lsm_secattr *secattr, - u32 base_sid, - u32 *sid) +int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, + u32 base_sid, + u32 *sid) { int rc = -EIDRM; struct context *ctx; struct context ctx_new; - struct netlbl_cache *cache; + struct selinux_netlbl_cache *cache; + + if (!ss_initialized) { + *sid = SECSID_NULL; + return 0; + } POLICY_RDLOCK; @@ -2410,8 +2418,8 @@ static int selinux_netlbl_secattr_to_sid if (rc != 0) goto netlbl_secattr_to_sid_return_cleanup; - if (skb != NULL) - selinux_netlbl_cache_add(skb, &ctx_new); + security_netlbl_cache_add(secattr, &ctx_new); + ebitmap_destroy(&ctx_new.range.level[0].cat); } else { *sid = SECSID_NULL; @@ -2427,338 +2435,43 @@ netlbl_secattr_to_sid_return_cleanup: } /** - * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel - * @skb: the packet - * @base_sid: the SELinux SID to use as a context for MLS only attributes - * @sid: the SID - * - * Description: - * Call the NetLabel mechanism to get the security attributes of the given - * packet and use those attributes to determine the correct context/SID to - * assign to the packet. Returns zero on success, negative values on failure. - * - */ -int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) -{ - int rc; - struct netlbl_lsm_secattr secattr; - - netlbl_secattr_init(&secattr); - rc = netlbl_skbuff_getattr(skb, &secattr); - if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) - rc = selinux_netlbl_secattr_to_sid(skb, - &secattr, - base_sid, - sid); - else - *sid = SECSID_NULL; - netlbl_secattr_destroy(&secattr); - - return rc; -} - -/** - * selinux_netlbl_socket_setsid - Label a socket using the NetLabel mechanism - * @sock: the socket to label - * @sid: the SID to use + * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr + * @sid: the SELinux SID + * @secattr: the NetLabel packet security attributes * * Description: - * Attempt to label a socket using the NetLabel mechanism using the given - * SID. Returns zero values on success, negative values on failure. The - * caller is responsibile for calling rcu_read_lock() before calling this - * this function and rcu_read_unlock() after this function returns. + * Convert the given SELinux SID in @sid into a NetLabel security attribute. + * Returns zero on success, negative values on failure. * */ -static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) +int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) { int rc = -ENOENT; - struct sk_security_struct *sksec = sock->sk->sk_security; - struct netlbl_lsm_secattr secattr; struct context *ctx; + netlbl_secattr_init(secattr); + if (!ss_initialized) return 0; - netlbl_secattr_init(&secattr); - POLICY_RDLOCK; - ctx = sidtab_search(&sidtab, sid); if (ctx == NULL) - goto netlbl_socket_setsid_return; - - secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], - GFP_ATOMIC); - secattr.flags |= NETLBL_SECATTR_DOMAIN; - mls_export_netlbl_lvl(ctx, &secattr); - rc = mls_export_netlbl_cat(ctx, &secattr); + goto netlbl_sid_to_secattr_failure; + secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], + GFP_ATOMIC); + secattr->flags |= NETLBL_SECATTR_DOMAIN; + mls_export_netlbl_lvl(ctx, secattr); + rc = mls_export_netlbl_cat(ctx, secattr); if (rc != 0) - goto netlbl_socket_setsid_return; - - rc = netlbl_socket_setattr(sock, &secattr); - if (rc == 0) { - spin_lock_bh(&sksec->nlbl_lock); - sksec->nlbl_state = NLBL_LABELED; - spin_unlock_bh(&sksec->nlbl_lock); - } - -netlbl_socket_setsid_return: + goto netlbl_sid_to_secattr_failure; POLICY_RDUNLOCK; - netlbl_secattr_destroy(&secattr); - return rc; -} - -/** - * selinux_netlbl_sk_security_reset - Reset the NetLabel fields - * @ssec: the sk_security_struct - * @family: the socket family - * - * Description: - * Called when the NetLabel state of a sk_security_struct needs to be reset. - * The caller is responsibile for all the NetLabel sk_security_struct locking. - * - */ -void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, - int family) -{ - if (family == PF_INET) - ssec->nlbl_state = NLBL_REQUIRE; - else - ssec->nlbl_state = NLBL_UNSET; -} -/** - * selinux_netlbl_sk_security_init - Setup the NetLabel fields - * @ssec: the sk_security_struct - * @family: the socket family - * - * Description: - * Called when a new sk_security_struct is allocated to initialize the NetLabel - * fields. - * - */ -void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, - int family) -{ - /* No locking needed, we are the only one who has access to ssec */ - selinux_netlbl_sk_security_reset(ssec, family); - spin_lock_init(&ssec->nlbl_lock); -} - -/** - * selinux_netlbl_sk_security_clone - Copy the NetLabel fields - * @ssec: the original sk_security_struct - * @newssec: the cloned sk_security_struct - * - * Description: - * Clone the NetLabel specific sk_security_struct fields from @ssec to - * @newssec. - * - */ -void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, - struct sk_security_struct *newssec) -{ - /* We don't need to take newssec->nlbl_lock because we are the only - * thread with access to newssec, but we do need to take the RCU read - * lock as other threads could have access to ssec */ - rcu_read_lock(); - selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); - newssec->sclass = ssec->sclass; - rcu_read_unlock(); -} - -/** - * selinux_netlbl_socket_post_create - Label a socket using NetLabel - * @sock: the socket to label - * - * Description: - * Attempt to label a socket using the NetLabel mechanism using the given - * SID. Returns zero values on success, negative values on failure. - * - */ -int selinux_netlbl_socket_post_create(struct socket *sock) -{ - int rc = 0; - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; - struct sk_security_struct *sksec = sock->sk->sk_security; - - sksec->sclass = isec->sclass; - - rcu_read_lock(); - if (sksec->nlbl_state == NLBL_REQUIRE) - rc = selinux_netlbl_socket_setsid(sock, sksec->sid); - rcu_read_unlock(); - - return rc; -} - -/** - * selinux_netlbl_sock_graft - Netlabel the new socket - * @sk: the new connection - * @sock: the new socket - * - * Description: - * The connection represented by @sk is being grafted onto @sock so set the - * socket's NetLabel to match the SID of @sk. - * - */ -void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) -{ - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; - struct sk_security_struct *sksec = sk->sk_security; - struct netlbl_lsm_secattr secattr; - u32 nlbl_peer_sid; - - sksec->sclass = isec->sclass; - - rcu_read_lock(); - - if (sksec->nlbl_state != NLBL_REQUIRE) { - rcu_read_unlock(); - return; - } - - netlbl_secattr_init(&secattr); - if (netlbl_sock_getattr(sk, &secattr) == 0 && - secattr.flags != NETLBL_SECATTR_NONE && - selinux_netlbl_secattr_to_sid(NULL, - &secattr, - SECINITSID_UNLABELED, - &nlbl_peer_sid) == 0) - sksec->peer_sid = nlbl_peer_sid; - netlbl_secattr_destroy(&secattr); - - /* Try to set the NetLabel on the socket to save time later, if we fail - * here we will pick up the pieces in later calls to - * selinux_netlbl_inode_permission(). */ - selinux_netlbl_socket_setsid(sock, sksec->sid); - - rcu_read_unlock(); -} - -/** - * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled - * @inode: the file descriptor's inode - * @mask: the permission mask - * - * Description: - * Looks at a file's inode and if it is marked as a socket protected by - * NetLabel then verify that the socket has been labeled, if not try to label - * the socket now with the inode's SID. Returns zero on success, negative - * values on failure. - * - */ -int selinux_netlbl_inode_permission(struct inode *inode, int mask) -{ - int rc; - struct sk_security_struct *sksec; - struct socket *sock; - - if (!S_ISSOCK(inode->i_mode) || - ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) - return 0; - sock = SOCKET_I(inode); - sksec = sock->sk->sk_security; - - rcu_read_lock(); - if (sksec->nlbl_state != NLBL_REQUIRE) { - rcu_read_unlock(); - return 0; - } - local_bh_disable(); - bh_lock_sock_nested(sock->sk); - rc = selinux_netlbl_socket_setsid(sock, sksec->sid); - bh_unlock_sock(sock->sk); - local_bh_enable(); - rcu_read_unlock(); - - return rc; -} - -/** - * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel - * @sksec: the sock's sk_security_struct - * @skb: the packet - * @ad: the audit data - * - * Description: - * Fetch the NetLabel security attributes from @skb and perform an access check - * against the receiving socket. Returns zero on success, negative values on - * error. - * - */ -int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, - struct sk_buff *skb, - struct avc_audit_data *ad) -{ - int rc; - u32 netlbl_sid; - u32 recv_perm; - - rc = selinux_netlbl_skbuff_getsid(skb, - SECINITSID_UNLABELED, - &netlbl_sid); - if (rc != 0) - return rc; - - if (netlbl_sid == SECSID_NULL) - return 0; - - switch (sksec->sclass) { - case SECCLASS_UDP_SOCKET: - recv_perm = UDP_SOCKET__RECVFROM; - break; - case SECCLASS_TCP_SOCKET: - recv_perm = TCP_SOCKET__RECVFROM; - break; - default: - recv_perm = RAWIP_SOCKET__RECVFROM; - } - - rc = avc_has_perm(sksec->sid, - netlbl_sid, - sksec->sclass, - recv_perm, - ad); - if (rc == 0) - return 0; - - netlbl_skbuff_err(skb, rc); - return rc; -} - -/** - * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel - * @sock: the socket - * @level: the socket level or protocol - * @optname: the socket option name - * - * Description: - * Check the setsockopt() call and if the user is trying to replace the IP - * options on a socket and a NetLabel is in place for the socket deny the - * access; otherwise allow the access. Returns zero when the access is - * allowed, -EACCES when denied, and other negative values on error. - * - */ -int selinux_netlbl_socket_setsockopt(struct socket *sock, - int level, - int optname) -{ - int rc = 0; - struct sk_security_struct *sksec = sock->sk->sk_security; - struct netlbl_lsm_secattr secattr; - - rcu_read_lock(); - if (level == IPPROTO_IP && optname == IP_OPTIONS && - sksec->nlbl_state == NLBL_LABELED) { - netlbl_secattr_init(&secattr); - rc = netlbl_socket_getattr(sock, &secattr); - if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) - rc = -EACCES; - netlbl_secattr_destroy(&secattr); - } - rcu_read_unlock(); + return 0; +netlbl_sid_to_secattr_failure: + POLICY_RDUNLOCK; + netlbl_secattr_destroy(secattr); return rc; } #endif /* CONFIG_NETLABEL */ diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c index b00fc48..7f980be 100644 --- a/sound/aoa/codecs/snd-aoa-codec-onyx.c +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c @@ -1062,9 +1062,9 @@ static int onyx_i2c_attach(struct i2c_ad while ((dev = of_get_next_child(busnode, dev)) != NULL) { if (device_is_compatible(dev, "pcm3052")) { - u32 *addr; + const u32 *addr; printk(KERN_DEBUG PFX "found pcm3052\n"); - addr = (u32 *) get_property(dev, "reg", NULL); + addr = of_get_property(dev, "reg", NULL); if (!addr) return -ENODEV; return onyx_create(adapter, dev, (*addr)>>1); diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c index 2cd81fa..ceca384 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.c +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c @@ -939,9 +939,9 @@ static int tas_i2c_attach(struct i2c_ada while ((dev = of_get_next_child(busnode, dev)) != NULL) { if (device_is_compatible(dev, "tas3004")) { - u32 *addr; + const u32 *addr; printk(KERN_DEBUG PFX "found tas3004\n"); - addr = (u32 *) get_property(dev, "reg", NULL); + addr = of_get_property(dev, "reg", NULL); if (!addr) continue; return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f); @@ -950,9 +950,10 @@ static int tas_i2c_attach(struct i2c_ada * property that says 'tas3004', they just have a 'deq' * node without any such property... */ if (strcmp(dev->name, "deq") == 0) { - u32 *_addr, addr; + const u32 *_addr; + u32 addr; printk(KERN_DEBUG PFX "found 'deq' node\n"); - _addr = (u32 *) get_property(dev, "i2c-address", NULL); + _addr = of_get_property(dev, "i2c-address", NULL); if (!_addr) continue; addr = ((*_addr) >> 1) & 0x7f; diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c index 2b03bc7..805dcbf 100644 --- a/sound/aoa/core/snd-aoa-gpio-feature.c +++ b/sound/aoa/core/snd-aoa-gpio-feature.c @@ -55,7 +55,7 @@ static struct device_node *get_gpio(char int *gpioactiveptr) { struct device_node *np, *gpio; - u32 *reg; + const u32 *reg; const char *audio_gpio; *gpioptr = -1; @@ -71,7 +71,7 @@ static struct device_node *get_gpio(char if (!gpio) return NULL; while ((np = of_get_next_child(gpio, np))) { - audio_gpio = get_property(np, "audio-gpio", NULL); + audio_gpio = of_get_property(np, "audio-gpio", NULL); if (!audio_gpio) continue; if (strcmp(audio_gpio, name) == 0) @@ -84,7 +84,7 @@ static struct device_node *get_gpio(char return NULL; } - reg = (u32 *)get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); if (!reg) return NULL; @@ -96,7 +96,7 @@ static struct device_node *get_gpio(char if (*gpioptr < 0x50) *gpioptr += 0x50; - reg = (u32 *)get_property(np, "audio-gpio-active-state", NULL); + reg = of_get_property(np, "audio-gpio-active-state", NULL); if (!reg) /* Apple seems to default to 1, but * that doesn't seem right at least on most diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c index 1b94ba6..9880628 100644 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c @@ -724,7 +724,7 @@ static int check_codec(struct aoa_codec struct layout_dev *ldev, struct codec_connect_info *cci) { - u32 *ref; + const u32 *ref; char propname[32]; struct codec_connection *cc; @@ -732,7 +732,7 @@ static int check_codec(struct aoa_codec if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { snprintf(propname, sizeof(propname), "platform-%s-codec-ref", codec->name); - ref = (u32*)get_property(ldev->sound, propname, NULL); + ref = of_get_property(ldev->sound, propname, NULL); if (!ref) { printk(KERN_INFO "snd-aoa-fabric-layout: " "required property %s not present\n", propname); @@ -946,7 +946,7 @@ static struct aoa_fabric layout_fabric = static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) { struct device_node *sound = NULL; - unsigned int *layout_id; + const unsigned int *layout_id; struct layout *layout; struct layout_dev *ldev = NULL; int err; @@ -962,7 +962,7 @@ static int aoa_fabric_layout_probe(struc } if (!sound) return -ENODEV; - layout_id = (unsigned int *) get_property(sound, "layout-id", NULL); + layout_id = of_get_property(sound, "layout-id", NULL); if (!layout_id) goto outnodev; printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n", diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 47b3e37..8b2e9b9 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -61,9 +61,9 @@ static int soundbus_uevent(struct device { struct soundbus_dev * soundbus_dev; struct of_device * of; - char *scratch, *compat, *compat2; - int i = 0; - int length, cplen, cplen2, seen = 0; + const char *compat; + int retval = 0, i = 0, length = 0; + int cplen, seen = 0; if (!dev) return -ENODEV; @@ -75,63 +75,47 @@ static int soundbus_uevent(struct device of = &soundbus_dev->ofdev; /* stuff we want to pass to /sbin/hotplug */ - envp[i++] = scratch = buffer; - length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_NAME=%s", of->node->name); + if (retval) + return retval; + + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_TYPE=%s", of->node->type); + if (retval) + return retval; /* Since the compatible field can contain pretty much anything * it's not really legal to split it out with commas. We split it * up using a number of environment variables instead. */ - compat = (char *) get_property(of->node, "compatible", &cplen); - compat2 = compat; - cplen2= cplen; + compat = of_get_property(of->node, "compatible", &cplen); while (compat && cplen > 0) { - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, - "OF_COMPATIBLE_%d=%s", seen, compat); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - length = strlen (compat) + 1; - compat += length; - cplen -= length; - seen++; + int tmp = length; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_COMPATIBLE_%d=%s", seen, compat); + if (retval) + return retval; + compat += length - tmp; + cplen -= length - tmp; + seen += 1; } - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "MODALIAS=%s", - soundbus_dev->modalias); - - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_COMPATIBLE_N=%d", seen); + if (retval) + return retval; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=%s", soundbus_dev->modalias); envp[i] = NULL; - return 0; + return retval; } static int soundbus_device_remove(struct device *dev) diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c index e36f6aa..79fc4bc 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c @@ -122,7 +122,7 @@ static int i2sbus_get_and_fixup_rsrc(str { struct device_node *parent; int pindex, rc = -ENXIO; - u32 *reg; + const u32 *reg; /* Machines with layout 76 and 36 (K2 based) have a weird device * tree what we need to special case. @@ -141,7 +141,7 @@ static int i2sbus_get_and_fixup_rsrc(str rc = of_address_to_resource(parent, pindex, res); if (rc) goto bail; - reg = (u32 *)get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); if (reg == NULL) { rc = -ENXIO; goto bail; @@ -188,8 +188,8 @@ static int i2sbus_add_dev(struct macio_d } } if (i == 1) { - u32 *layout_id; - layout_id = (u32*) get_property(sound, "layout-id", NULL); + const u32 *layout_id = + of_get_property(sound, "layout-id", NULL); if (layout_id) { layout = *layout_id; snprintf(dev->sound.modalias, 32, diff --git a/sound/oss/dmasound/dmasound_awacs.c b/sound/oss/dmasound/dmasound_awacs.c index 37773b1..730fa1d 100644 --- a/sound/oss/dmasound/dmasound_awacs.c +++ b/sound/oss/dmasound/dmasound_awacs.c @@ -257,7 +257,7 @@ #ifdef CONFIG_PM /* * Stuff for restoring after a sleep. */ -static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); +static void awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); struct pmu_sleep_notifier awacs_sleep_notifier = { awacs_sleep_notify, SLEEP_LEVEL_SOUND, }; @@ -346,36 +346,42 @@ int gpio_headphone_irq; int setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int* gpio_pol) { + struct device_node *gpiop; struct device_node *np; const u32* pp; + int ret = -ENODEV; - np = find_devices("gpio"); - if (!np) - return -ENODEV; + gpiop = of_find_node_by_name(NULL, "gpio"); + if (!gpiop) + goto done; - np = np->child; + np = of_get_next_child(gpiop, NULL); while(np != 0) { if (name) { const char *property = - get_property(np,"audio-gpio",NULL); + of_get_property(np,"audio-gpio",NULL); if (property != 0 && strcmp(property,name) == 0) break; } else if (compatible && device_is_compatible(np, compatible)) break; - np = np->sibling; + np = of_get_next_child(gpiop, np); } if (!np) - return -ENODEV; - pp = get_property(np, "AAPL,address", NULL); + goto done; + pp = of_get_property(np, "AAPL,address", NULL); if (!pp) - return -ENODEV; + goto done; *gpio_addr = (*pp) & 0x0000ffff; - pp = get_property(np, "audio-gpio-active-state", NULL); + pp = of_get_property(np, "audio-gpio-active-state", NULL); if (pp) *gpio_pol = *pp; else *gpio_pol = 1; - return irq_of_parse_and_map(np, 0); + ret = irq_of_parse_and_map(np, 0); +done: + of_node_put(np); + of_node_put(gpiop); + return ret; } static inline void @@ -578,7 +584,7 @@ tas_mixer_ioctl(u_int cmd, u_long arg) } static void __init -tas_init_frame_rates(unsigned int *prop, unsigned int l) +tas_init_frame_rates(const unsigned int *prop, unsigned int l) { int i ; if (prop) { @@ -1419,7 +1425,7 @@ #ifdef CONFIG_PM * Save state when going to sleep, restore it afterwards. */ /* FIXME: sort out disabling/re-enabling of read stuff as well */ -static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) +static void awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) { unsigned long flags; @@ -1548,7 +1554,6 @@ static int awacs_sleep_notify(struct pmu spin_unlock_irqrestore(&dmasound.lock, flags); UNLOCK(); } - return PBOOK_SLEEP_OK; } #endif /* CONFIG_PM */ @@ -2553,32 +2558,33 @@ set_model(void) static struct device_node* __init get_snd_io_node(void) { - struct device_node *np = NULL; + struct device_node *np; /* set up awacs_node for early OF which doesn't have a full set of * properties on davbus - */ - - awacs_node = find_devices("awacs"); + */ + awacs_node = of_find_node_by_name(NULL, "awacs"); if (awacs_node) awacs_revision = AWACS_AWACS; /* powermac models after 9500 (other than those which use DACA or * Tumbler) have a node called "davbus". */ - np = find_devices("davbus"); + np = of_find_node_by_name(NULL, "davbus"); /* * if we didn't find a davbus device, try 'i2s-a' since * this seems to be what iBooks (& Tumbler) have. */ - if (np == NULL) - np = i2s_node = find_devices("i2s-a"); + if (np == NULL) { + i2s_node = of_find_node_by_name(NULL, "i2s-a"); + np = of_node_get(i2s_node); + } /* if we didn't find this - perhaps we are on an early model * which _only_ has an 'awacs' node */ if (np == NULL && awacs_node) - np = awacs_node ; + np = of_node_get(awacs_node); /* if we failed all these return null - this will cause the * driver to give up... @@ -2597,9 +2603,9 @@ get_snd_info_node(struct device_node *io { struct device_node *info; - info = find_devices("sound"); - while (info && info->parent != io) - info = info->next; + for_each_node_by_name(info, "sound") + if (info->parent == io) + break; return info; } @@ -2635,11 +2641,17 @@ get_codec_type(struct device_node *info) static void __init get_expansion_type(void) { - if (find_devices("perch") != NULL) + struct device_node *dn; + + dn = of_find_node_by_name(NULL, "perch"); + if (dn != NULL) has_perch = 1; + of_node_put(dn); - if (find_devices("pb-ziva-pc") != NULL) + dn = of_find_node_by_name(NULL, "pb-ziva-pc"); + if (dn != NULL) has_ziva = 1; + of_node_put(dn); /* need to work out how we deal with iMac SRS module */ } @@ -2652,7 +2664,7 @@ get_expansion_type(void) */ static void __init -awacs_init_frame_rates(unsigned int *prop, unsigned int l) +awacs_init_frame_rates(const unsigned int *prop, unsigned int l) { int i ; if (prop) { @@ -2675,7 +2687,7 @@ awacs_init_frame_rates(unsigned int *pro } static void __init -burgundy_init_frame_rates(unsigned int *prop, unsigned int l) +burgundy_init_frame_rates(const unsigned int *prop, unsigned int l) { int temp[9] ; int i = 0 ; @@ -2701,7 +2713,7 @@ #endif } static void __init -daca_init_frame_rates(unsigned int *prop, unsigned int l) +daca_init_frame_rates(const unsigned int *prop, unsigned int l) { int temp[9] ; int i = 0 ; @@ -2728,7 +2740,7 @@ #endif } static void __init -init_frame_rates(unsigned int *prop, unsigned int l) +init_frame_rates(const unsigned int *prop, unsigned int l) { switch (awacs_revision) { case AWACS_TUMBLER: @@ -2828,7 +2840,7 @@ int __init dmasound_awacs_init(void) #ifdef DEBUG_DMASOUND printk("dmasound_pmac: couldn't find sound io OF node\n"); #endif - return -ENODEV ; + goto no_device; } /* find the OF node that tells us about the sound sub-system @@ -2840,7 +2852,7 @@ #endif #ifdef DEBUG_DMASOUND printk("dmasound_pmac: couldn't find 'sound' OF node\n"); #endif - return -ENODEV ; + goto no_device; } } @@ -2849,7 +2861,7 @@ #endif #ifdef DEBUG_DMASOUND printk("dmasound_pmac: couldn't find a Codec we can handle\n"); #endif - return -ENODEV ; /* we don't know this type of h/w */ + goto no_device; /* we don't know this type of h/w */ } /* set up perch, ziva, SRS or whatever else we have as sound @@ -2867,11 +2879,12 @@ #endif * machines). */ if (awacs_node) { - io = awacs_node ; + of_node_put(io); + io = of_node_get(awacs_node); if (of_get_address(io, 2, NULL, NULL) == NULL) { printk("dmasound_pmac: can't use %s\n", io->full_name); - return -ENODEV; + goto no_device; } } else printk("dmasound_pmac: can't use %s\n", io->full_name); @@ -2882,7 +2895,7 @@ #endif awacs_rsrc[0].end - awacs_rsrc[0].start + 1, " (IO)") == NULL) { printk(KERN_ERR "dmasound: can't request IO resource !\n"); - return -ENODEV; + goto no_device; } if (of_address_to_resource(io, 1, &awacs_rsrc[1]) || request_mem_region(awacs_rsrc[1].start, @@ -2891,7 +2904,7 @@ #endif release_mem_region(awacs_rsrc[0].start, awacs_rsrc[0].end - awacs_rsrc[0].start + 1); printk(KERN_ERR "dmasound: can't request Tx DMA resource !\n"); - return -ENODEV; + goto no_device; } if (of_address_to_resource(io, 2, &awacs_rsrc[2]) || request_mem_region(awacs_rsrc[2].start, @@ -2902,7 +2915,7 @@ #endif release_mem_region(awacs_rsrc[1].start, awacs_rsrc[1].end - awacs_rsrc[1].start + 1); printk(KERN_ERR "dmasound: can't request Rx DMA resource !\n"); - return -ENODEV; + goto no_device; } awacs_beep_dev = input_allocate_device(); @@ -2914,7 +2927,7 @@ #endif release_mem_region(awacs_rsrc[2].start, awacs_rsrc[2].end - awacs_rsrc[2].start + 1); printk(KERN_ERR "dmasound: can't allocate input device !\n"); - return -ENOMEM; + goto no_device; } awacs_beep_dev->name = "dmasound beeper"; @@ -2942,7 +2955,8 @@ #endif awacs_rx_irq = irq_of_parse_and_map(io, 2); /* Hack for legacy crap that will be killed someday */ - awacs_node = io; + of_node_put(awacs_node); + awacs_node = of_node_get(io); /* if we have an awacs or screamer - probe the chip to make * sure we have the right revision. @@ -2973,24 +2987,26 @@ #endif */ if (info) { - unsigned int *prop, l; + const unsigned int *prop; + unsigned int l; sound_device_id = 0; /* device ID appears post g3 b&w */ - prop = (unsigned int *)get_property(info, "device-id", NULL); + prop = of_get_property(info, "device-id", NULL); if (prop != 0) sound_device_id = *prop; /* look for a property saying what sample rates are available */ - prop = (unsigned int *)get_property(info, "sample-rates", &l); + prop = of_get_property(info, "sample-rates", &l); if (prop == 0) - prop = (unsigned int *) get_property - (info, "output-frame-rates", &l); + prop = of_get_property(info, "output-frame-rates", &l); /* if it's there use it to set up frame rates */ init_frame_rates(prop, l) ; + of_node_put(info); + info = NULL; } if (awacs) @@ -3160,7 +3176,16 @@ #endif /* CONFIG_PM */ */ input_register_device(awacs_beep_dev); + of_node_put(io); + return dmasound_init(); + +no_device: + of_node_put(info); + of_node_put(awacs_node); + of_node_put(i2s_node); + of_node_put(io); + return -ENODEV ; } static void __exit dmasound_awacs_cleanup(void) @@ -3179,6 +3204,8 @@ static void __exit dmasound_awacs_cleanu } dmasound_deinit(); + of_node_put(awacs_node); + of_node_put(i2s_node); } MODULE_DESCRIPTION("PowerMac built-in audio driver."); diff --git a/sound/oss/dmasound/tas_common.c b/sound/oss/dmasound/tas_common.c index 665e85b..b295ef6 100644 --- a/sound/oss/dmasound/tas_common.c +++ b/sound/oss/dmasound/tas_common.c @@ -41,7 +41,6 @@ #define CALL(proc,arg...) \ static u8 tas_i2c_address = 0x34; static struct i2c_client *tas_client; -static struct device_node* tas_node; static int tas_attach_adapter(struct i2c_adapter *); static int tas_detach_client(struct i2c_client *); @@ -190,17 +189,18 @@ tas_cleanup(void) int __init tas_init(int driver_id, const char *driver_name) { - u32* paddr; + const u32* paddr; + struct device_node *tas_node; printk(KERN_INFO "tas driver [%s])\n", driver_name); #ifndef CONFIG_I2C_POWERMAC request_module("i2c-powermac"); #endif - tas_node = find_devices("deq"); + tas_node = of_find_node_by_name("deq"); if (tas_node == NULL) return -ENODEV; - paddr = (u32 *)get_property(tas_node, "i2c-address", NULL); + paddr = of_get_property(tas_node, "i2c-address", NULL); if (paddr) { tas_i2c_address = (*paddr) >> 1; printk(KERN_INFO "using i2c address: 0x%x from device-tree\n", @@ -208,6 +208,7 @@ #endif } else printk(KERN_INFO "using i2c address: 0x%x (default)\n", tas_i2c_address); + of_node_put(tas_node); return i2c_add_driver(&tas_driver); } diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index c64af55..2bae9c1 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -816,6 +816,7 @@ static int snd_pmac_free(struct snd_pmac if (chip->pdev) pci_dev_put(chip->pdev); + of_node_put(chip->node); kfree(chip); return 0; } @@ -863,8 +864,10 @@ static void __init detect_byte_swap(stru */ static int __init snd_pmac_detect(struct snd_pmac *chip) { - struct device_node *sound = NULL; - unsigned int *prop, l; + struct device_node *sound; + struct device_node *dn; + const unsigned int *prop; + unsigned int l; struct macio_chip* macio; if (!machine_is(powermac)) @@ -890,22 +893,21 @@ static int __init snd_pmac_detect(struct else if (machine_is_compatible("PowerBook1,1") || machine_is_compatible("AAPL,PowerBook1998")) chip->is_pbook_G3 = 1; - chip->node = find_devices("awacs"); - if (chip->node) - sound = chip->node; + chip->node = of_find_node_by_name(NULL, "awacs"); + sound = of_node_get(chip->node); /* * powermac G3 models have a node called "davbus" * with a child called "sound". */ if (!chip->node) - chip->node = find_devices("davbus"); + chip->node = of_find_node_by_name(NULL, "davbus"); /* * if we didn't find a davbus device, try 'i2s-a' since * this seems to be what iBooks have */ if (! chip->node) { - chip->node = find_devices("i2s-a"); + chip->node = of_find_node_by_name(NULL, "i2s-a"); if (chip->node && chip->node->parent && chip->node->parent->parent) { if (device_is_compatible(chip->node->parent->parent, @@ -917,22 +919,25 @@ static int __init snd_pmac_detect(struct return -ENODEV; if (!sound) { - sound = find_devices("sound"); + sound = of_find_node_by_name(NULL, "sound"); while (sound && sound->parent != chip->node) - sound = sound->next; + sound = of_find_node_by_name(sound, "sound"); } - if (! sound) + if (! sound) { + of_node_put(chip->node); return -ENODEV; - prop = (unsigned int *) get_property(sound, "sub-frame", NULL); + } + prop = of_get_property(sound, "sub-frame", NULL); if (prop && *prop < 16) chip->subframe = *prop; - prop = (unsigned int *) get_property(sound, "layout-id", NULL); + prop = of_get_property(sound, "layout-id", NULL); if (prop) { /* partly deprecate snd-powermac, for those machines * that have a layout-id property for now */ printk(KERN_INFO "snd-powermac no longer handles any " "machines with a layout-id property " "in the device-tree, use snd-aoa.\n"); + of_node_put(chip->node); return -ENODEV; } /* This should be verified on older screamers */ @@ -967,10 +972,12 @@ static int __init snd_pmac_detect(struct chip->freq_table = tumbler_freqs; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } - prop = (unsigned int *)get_property(sound, "device-id", NULL); + prop = of_get_property(sound, "device-id", NULL); if (prop) chip->device_id = *prop; - chip->has_iic = (find_devices("perch") != NULL); + dn = of_find_node_by_name(NULL, "perch"); + chip->has_iic = (dn != NULL); + of_node_put(dn); /* We need the PCI device for DMA allocations, let's use a crude method * for now ... @@ -997,10 +1004,9 @@ static int __init snd_pmac_detect(struct /* look for a property saying what sample rates are available */ - prop = (unsigned int *) get_property(sound, "sample-rates", &l); + prop = of_get_property(sound, "sample-rates", &l); if (! prop) - prop = (unsigned int *) get_property(sound, - "output-frame-rates", &l); + prop = of_get_property(sound, "output-frame-rates", &l); if (prop) { int i; chip->freqs_ok = 0; @@ -1021,6 +1027,7 @@ static int __init snd_pmac_detect(struct chip->freqs_ok = 1; } + of_node_put(sound); return 0; } diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 8f074c7..54e333f 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1031,32 +1031,40 @@ static irqreturn_t headphone_intr(int ir /* look for audio-gpio device */ static struct device_node *find_audio_device(const char *name) { + struct device_node *gpiop; struct device_node *np; - if (! (np = find_devices("gpio"))) + gpiop = of_find_node_by_name(NULL, "gpio"); + if (! gpiop) return NULL; - for (np = np->child; np; np = np->sibling) { - const char *property = get_property(np, "audio-gpio", NULL); + for (np = of_get_next_child(gpiop, NULL); np; + np = of_get_next_child(gpiop, np)) { + const char *property = of_get_property(np, "audio-gpio", NULL); if (property && strcmp(property, name) == 0) - return np; + break; } - return NULL; + of_node_put(gpiop); + return np; } /* look for audio-gpio device */ static struct device_node *find_compatible_audio_device(const char *name) { + struct device_node *gpiop; struct device_node *np; - if (! (np = find_devices("gpio"))) + gpiop = of_find_node_by_name(NULL, "gpio"); + if (!gpiop) return NULL; - for (np = np->child; np; np = np->sibling) { + for (np = of_get_next_child(gpiop, NULL); np; + np = of_get_next_child(gpiop, np)) { if (device_is_compatible(np, name)) - return np; + break; } - return NULL; + of_node_put(gpiop); + return np; } /* find an audio device and get its address */ @@ -1066,6 +1074,7 @@ static long tumbler_find_device(const ch struct device_node *node; const u32 *base; u32 addr; + long ret; if (is_compatible) node = find_compatible_audio_device(device); @@ -1077,12 +1086,13 @@ static long tumbler_find_device(const ch return -ENODEV; } - base = get_property(node, "AAPL,address", NULL); + base = of_get_property(node, "AAPL,address", NULL); if (! base) { - base = get_property(node, "reg", NULL); + base = of_get_property(node, "reg", NULL); if (!base) { DBG("(E) cannot find address for device %s !\n", device); snd_printd("cannot find address for device %s\n", device); + of_node_put(node); return -ENODEV; } addr = *base; @@ -1093,7 +1103,7 @@ static long tumbler_find_device(const ch gp->addr = addr & 0x0000ffff; /* Try to find the active state, default to 0 ! */ - base = get_property(node, "audio-gpio-active-state", NULL); + base = of_get_property(node, "audio-gpio-active-state", NULL); if (base) { gp->active_state = *base; gp->active_val = (*base) ? 0x5 : 0x4; @@ -1108,7 +1118,7 @@ static long tumbler_find_device(const ch * as we don't yet have an interpreter for these things */ if (platform) - prop = get_property(node, platform, NULL); + prop = of_get_property(node, platform, NULL); if (prop) { if (prop[3] == 0x9 && prop[4] == 0x9) { gp->active_val = 0xd; @@ -1124,7 +1134,9 @@ static long tumbler_find_device(const ch DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n", device, gp->addr, gp->active_state); - return irq_of_parse_and_map(node, 0); + ret = irq_of_parse_and_map(node, 0); + of_node_put(node); + return ret; } /* reset audio */ @@ -1310,7 +1322,7 @@ int __init snd_pmac_tumbler_init(struct { int i, err; struct pmac_tumbler *mix; - u32 *paddr; + const u32 *paddr; struct device_node *tas_node, *np; char *chipname; @@ -1331,9 +1343,9 @@ #endif /* CONFIG_KMOD */ for (np = chip->node->child; np; np = np->sibling) { if (!strcmp(np->name, "sound")) { - if (get_property(np, "has-anded-reset", NULL)) + if (of_get_property(np, "has-anded-reset", NULL)) mix->anded_reset = 1; - if (get_property(np, "layout-id", NULL)) + if (of_get_property(np, "layout-id", NULL)) mix->reset_on_sleep = 0; break; } @@ -1342,19 +1354,20 @@ #endif /* CONFIG_KMOD */ return err; /* set up TAS */ - tas_node = find_devices("deq"); + tas_node = of_find_node_by_name(NULL, "deq"); if (tas_node == NULL) - tas_node = find_devices("codec"); + tas_node = of_find_node_by_name(NULL, "codec"); if (tas_node == NULL) return -ENODEV; - paddr = (u32 *)get_property(tas_node, "i2c-address", NULL); + paddr = of_get_property(tas_node, "i2c-address", NULL); if (paddr == NULL) - paddr = (u32 *)get_property(tas_node, "reg", NULL); + paddr = of_get_property(tas_node, "reg", NULL); if (paddr) mix->i2c.addr = (*paddr) >> 1; else mix->i2c.addr = TAS_I2C_ADDR; + of_node_put(tas_node); DBG("(I) TAS i2c address is: %x\n", mix->i2c.addr); diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index c899786..07962a3 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -1067,8 +1067,8 @@ out_err: static int __devinit amd7930_obio_attach(struct device_node *dp) { - struct linux_prom_registers *regs; - struct linux_prom_irqs *irqp; + const struct linux_prom_registers *regs; + const struct linux_prom_irqs *irqp; struct resource res, *rp; int len; diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index f5956d5..900a00d 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -2284,7 +2284,7 @@ #ifdef EBUS_SUPPORT if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) { match = 1; } else if (!strcmp(edev->prom_node->name, "audio")) { - char *compat; + const char *compat; compat = of_get_property(edev->prom_node, "compatible", NULL);